aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/llvm14/tools
diff options
context:
space:
mode:
authorvitalyisaev <vitalyisaev@yandex-team.com>2023-06-29 10:00:50 +0300
committervitalyisaev <vitalyisaev@yandex-team.com>2023-06-29 10:00:50 +0300
commit6ffe9e53658409f212834330e13564e4952558f6 (patch)
tree85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/llvm14/tools
parent726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff)
downloadydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/llvm14/tools')
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/BugDriver.cpp260
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/BugDriver.h306
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/CrashDebugger.cpp1430
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/ExecutionDriver.cpp457
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/ExtractFunction.cpp420
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/FindBugs.cpp99
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/ListReducer.h208
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/Miscompilation.cpp1104
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/OptimizerDriver.cpp287
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/ToolRunner.cpp865
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/ToolRunner.h190
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/bugpoint.cpp244
-rw-r--r--contrib/libs/llvm14/tools/bugpoint/ya.make100
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/BinaryHolder.cpp285
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/BinaryHolder.h172
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/CFBundle.cpp185
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/CFBundle.h30
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/DebugMap.cpp294
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/DebugMap.h270
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/DwarfLinkerForBinary.cpp885
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/DwarfLinkerForBinary.h228
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/LinkUtils.h108
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/MachODebugMapParser.cpp622
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/MachOUtils.cpp624
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/MachOUtils.h52
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/Options.td178
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/Reproducer.cpp85
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/Reproducer.h77
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/SymbolMap.cpp161
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/SymbolMap.h53
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/dsymutil.cpp708
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/dsymutil.h56
-rw-r--r--contrib/libs/llvm14/tools/dsymutil/ya.make98
-rw-r--r--contrib/libs/llvm14/tools/gold/README.txt13
-rw-r--r--contrib/libs/llvm14/tools/gold/gold-plugin.cpp1184
-rw-r--r--contrib/libs/llvm14/tools/gold/gold.exports1
-rw-r--r--contrib/libs/llvm14/tools/gold/ya.make67
-rw-r--r--contrib/libs/llvm14/tools/llc/llc.cpp753
-rw-r--r--contrib/libs/llvm14/tools/llc/ya.make86
-rw-r--r--contrib/libs/llvm14/tools/lli/ChildTarget/ChildTarget.cpp76
-rw-r--r--contrib/libs/llvm14/tools/lli/ChildTarget/ya.make30
-rw-r--r--contrib/libs/llvm14/tools/lli/ExecutionUtils.cpp146
-rw-r--r--contrib/libs/llvm14/tools/lli/ExecutionUtils.h60
-rw-r--r--contrib/libs/llvm14/tools/lli/ForwardingMemoryManager.h131
-rw-r--r--contrib/libs/llvm14/tools/lli/lli.cpp1155
-rw-r--r--contrib/libs/llvm14/tools/lli/ya.make83
-rw-r--r--contrib/libs/llvm14/tools/llvm-ar/llvm-ar.cpp1307
-rw-r--r--contrib/libs/llvm14/tools/llvm-ar/ya.make59
-rw-r--r--contrib/libs/llvm14/tools/llvm-as/llvm-as.cpp166
-rw-r--r--contrib/libs/llvm14/tools/llvm-as/ya.make42
-rw-r--r--contrib/libs/llvm14/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp134
-rw-r--r--contrib/libs/llvm14/tools/llvm-bcanalyzer/ya.make32
-rw-r--r--contrib/libs/llvm14/tools/llvm-cat/llvm-cat.cpp96
-rw-r--r--contrib/libs/llvm14/tools/llvm-cat/ya.make43
-rw-r--r--contrib/libs/llvm14/tools/llvm-cfi-verify/lib/FileAnalysis.cpp599
-rw-r--r--contrib/libs/llvm14/tools/llvm-cfi-verify/lib/FileAnalysis.h248
-rw-r--r--contrib/libs/llvm14/tools/llvm-cfi-verify/lib/GraphBuilder.cpp339
-rw-r--r--contrib/libs/llvm14/tools/llvm-cfi-verify/lib/GraphBuilder.h136
-rw-r--r--contrib/libs/llvm14/tools/llvm-cfi-verify/lib/ya.make32
-rw-r--r--contrib/libs/llvm14/tools/llvm-cfi-verify/llvm-cfi-verify.cpp282
-rw-r--r--contrib/libs/llvm14/tools/llvm-cfi-verify/ya.make67
-rw-r--r--contrib/libs/llvm14/tools/llvm-config/BuildVariables.inc39
-rw-r--r--contrib/libs/llvm14/tools/llvm-config/ExtensionDependencies.inc8
-rw-r--r--contrib/libs/llvm14/tools/llvm-config/LibraryDependencies.inc126
-rw-r--r--contrib/libs/llvm14/tools/llvm-config/llvm-config.cpp755
-rw-r--r--contrib/libs/llvm14/tools/llvm-config/ya.make31
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CodeCoverage.cpp1218
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageExporter.h51
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageExporterJson.cpp309
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageExporterJson.h35
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageExporterLcov.cpp222
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageExporterLcov.h35
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageFilters.cpp93
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageFilters.h173
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageReport.cpp490
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageReport.h69
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageSummaryInfo.cpp106
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageSummaryInfo.h263
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/CoverageViewOptions.h81
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/RenderingSupport.h60
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/SourceCoverageView.cpp281
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/SourceCoverageView.h294
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewHTML.cpp768
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewHTML.h100
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewText.cpp299
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewText.h91
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/TestingSupport.cpp123
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/gcov.cpp180
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/llvm-cov.cpp95
-rw-r--r--contrib/libs/llvm14/tools/llvm-cov/ya.make50
-rw-r--r--contrib/libs/llvm14/tools/llvm-cvtres/Opts.td19
-rw-r--r--contrib/libs/llvm14/tools/llvm-cvtres/llvm-cvtres.cpp234
-rw-r--r--contrib/libs/llvm14/tools/llvm-cvtres/ya.make39
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxdump/Error.cpp46
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxdump/Error.h38
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxdump/llvm-cxxdump.cpp563
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxdump/llvm-cxxdump.h22
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxdump/ya.make43
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxfilt/Opts.td28
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxfilt/llvm-cxxfilt.cpp184
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxfilt/ya.make30
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxmap/llvm-cxxmap.cpp166
-rw-r--r--contrib/libs/llvm14/tools/llvm-cxxmap/ya.make28
-rw-r--r--contrib/libs/llvm14/tools/llvm-diff/lib/DiffConsumer.cpp218
-rw-r--r--contrib/libs/llvm14/tools/llvm-diff/lib/DiffConsumer.h91
-rw-r--r--contrib/libs/llvm14/tools/llvm-diff/lib/DiffLog.cpp54
-rw-r--r--contrib/libs/llvm14/tools/llvm-diff/lib/DiffLog.h83
-rw-r--r--contrib/libs/llvm14/tools/llvm-diff/lib/DifferenceEngine.cpp874
-rw-r--r--contrib/libs/llvm14/tools/llvm-diff/lib/DifferenceEngine.h90
-rw-r--r--contrib/libs/llvm14/tools/llvm-diff/lib/ya.make30
-rw-r--r--contrib/libs/llvm14/tools/llvm-diff/llvm-diff.cpp96
-rw-r--r--contrib/libs/llvm14/tools/llvm-diff/ya.make36
-rw-r--r--contrib/libs/llvm14/tools/llvm-dis/llvm-dis.cpp258
-rw-r--r--contrib/libs/llvm14/tools/llvm-dis/ya.make33
-rw-r--r--contrib/libs/llvm14/tools/llvm-dwarfdump/SectionSizes.cpp122
-rw-r--r--contrib/libs/llvm14/tools/llvm-dwarfdump/Statistics.cpp1056
-rw-r--r--contrib/libs/llvm14/tools/llvm-dwarfdump/llvm-dwarfdump.cpp686
-rw-r--r--contrib/libs/llvm14/tools/llvm-dwarfdump/llvm-dwarfdump.h45
-rw-r--r--contrib/libs/llvm14/tools/llvm-dwarfdump/ya.make54
-rw-r--r--contrib/libs/llvm14/tools/llvm-dwp/llvm-dwp.cpp199
-rw-r--r--contrib/libs/llvm14/tools/llvm-dwp/ya.make80
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/Target.cpp76
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/ya.make37
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/Analysis.cpp608
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/Analysis.h130
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/Assembler.cpp326
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/Assembler.h132
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkCode.h35
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkResult.cpp433
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkResult.h124
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkRunner.cpp281
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkRunner.h94
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/Clustering.cpp408
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/Clustering.h171
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/CodeTemplate.cpp115
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/CodeTemplate.h140
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/Error.cpp31
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/Error.h57
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.cpp143
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.h38
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/LlvmState.cpp82
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/LlvmState.h79
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/MCInstrDescView.cpp400
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/MCInstrDescView.h239
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp257
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h65
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/PerfHelper.cpp168
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/PerfHelper.h112
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/Target.cpp140
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/ya.make36
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterAliasing.cpp92
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterAliasing.h119
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterValue.cpp51
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterValue.h52
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/SchedClassResolution.cpp321
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/SchedClassResolution.h61
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp181
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/SerialSnippetGenerator.h37
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetFile.cpp181
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetFile.h35
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetGenerator.cpp275
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetGenerator.h109
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetRepetitor.cpp133
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetRepetitor.h54
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/Target.cpp177
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/Target.h208
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/TargetSelect.h40
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/UopsBenchmarkRunner.cpp46
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/UopsBenchmarkRunner.h38
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/Target.cpp970
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/X86Counter.cpp261
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/X86Counter.h60
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/ya.make38
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/lib/ya.make59
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/llvm-exegesis.cpp478
-rw-r--r--contrib/libs/llvm14/tools/llvm-exegesis/ya.make69
-rw-r--r--contrib/libs/llvm14/tools/llvm-extract/llvm-extract.cpp394
-rw-r--r--contrib/libs/llvm14/tools/llvm-extract/ya.make52
-rw-r--r--contrib/libs/llvm14/tools/llvm-gsymutil/llvm-gsymutil.cpp535
-rw-r--r--contrib/libs/llvm14/tools/llvm-gsymutil/ya.make90
-rw-r--r--contrib/libs/llvm14/tools/llvm-ifs/ErrorCollector.cpp65
-rw-r--r--contrib/libs/llvm14/tools/llvm-ifs/ErrorCollector.h74
-rw-r--r--contrib/libs/llvm14/tools/llvm-ifs/llvm-ifs.cpp552
-rw-r--r--contrib/libs/llvm14/tools/llvm-ifs/ya.make39
-rw-r--r--contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-elf.cpp175
-rw-r--r--contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp179
-rw-r--r--contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/ya.make29
-rw-r--r--contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-macho.cpp170
-rw-r--r--contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink.cpp2007
-rw-r--r--contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink.h93
-rw-r--r--contrib/libs/llvm14/tools/llvm-jitlink/ya.make84
-rw-r--r--contrib/libs/llvm14/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp694
-rw-r--r--contrib/libs/llvm14/tools/llvm-libtool-darwin/ya.make36
-rw-r--r--contrib/libs/llvm14/tools/llvm-link/llvm-link.cpp505
-rw-r--r--contrib/libs/llvm14/tools/llvm-link/ya.make52
-rw-r--r--contrib/libs/llvm14/tools/llvm-lipo/LipoOpts.td60
-rw-r--r--contrib/libs/llvm14/tools/llvm-lipo/llvm-lipo.cpp755
-rw-r--r--contrib/libs/llvm14/tools/llvm-lipo/ya.make69
-rw-r--r--contrib/libs/llvm14/tools/llvm-lto/llvm-lto.cpp1134
-rw-r--r--contrib/libs/llvm14/tools/llvm-lto/ya.make92
-rw-r--r--contrib/libs/llvm14/tools/llvm-lto2/llvm-lto2.cpp517
-rw-r--r--contrib/libs/llvm14/tools/llvm-lto2/ya.make92
-rw-r--r--contrib/libs/llvm14/tools/llvm-mc/Disassembler.cpp207
-rw-r--r--contrib/libs/llvm14/tools/llvm-mc/Disassembler.h40
-rw-r--r--contrib/libs/llvm14/tools/llvm-mc/llvm-mc.cpp610
-rw-r--r--contrib/libs/llvm14/tools/llvm-mc/ya.make56
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/CodeRegion.cpp116
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/CodeRegion.h130
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/CodeRegionGenerator.cpp150
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/CodeRegionGenerator.h71
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/PipelinePrinter.cpp129
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/PipelinePrinter.h69
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/BottleneckAnalysis.cpp644
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/BottleneckAnalysis.h348
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/DispatchStatistics.cpp98
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/DispatchStatistics.h87
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/InstructionInfoView.cpp177
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/InstructionInfoView.h93
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/InstructionView.cpp43
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/InstructionView.h59
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/RegisterFileStatistics.cpp170
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/RegisterFileStatistics.h84
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/ResourcePressureView.cpp200
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/ResourcePressureView.h103
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp91
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/RetireControlUnitStatistics.h64
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/SchedulerStatistics.cpp178
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/SchedulerStatistics.h97
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/SummaryView.cpp113
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/SummaryView.h90
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/TimelineView.cpp328
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/Views/TimelineView.h188
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/llvm-mca.cpp697
-rw-r--r--contrib/libs/llvm14/tools/llvm-mca/ya.make76
-rw-r--r--contrib/libs/llvm14/tools/llvm-ml/Disassembler.cpp203
-rw-r--r--contrib/libs/llvm14/tools/llvm-ml/Disassembler.h37
-rw-r--r--contrib/libs/llvm14/tools/llvm-ml/Opts.td120
-rw-r--r--contrib/libs/llvm14/tools/llvm-ml/llvm-ml.cpp441
-rw-r--r--contrib/libs/llvm14/tools/llvm-ml/ya.make59
-rw-r--r--contrib/libs/llvm14/tools/llvm-modextract/llvm-modextract.cpp85
-rw-r--r--contrib/libs/llvm14/tools/llvm-modextract/ya.make42
-rw-r--r--contrib/libs/llvm14/tools/llvm-mt/Opts.td30
-rw-r--r--contrib/libs/llvm14/tools/llvm-mt/llvm-mt.cpp176
-rw-r--r--contrib/libs/llvm14/tools/llvm-mt/ya.make31
-rw-r--r--contrib/libs/llvm14/tools/llvm-nm/Opts.td76
-rw-r--r--contrib/libs/llvm14/tools/llvm-nm/llvm-nm.cpp2256
-rw-r--r--contrib/libs/llvm14/tools/llvm-nm/ya.make59
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/BitcodeStripOpts.td24
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFConfig.h27
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFObjcopy.cpp297
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFObjcopy.h33
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/COFF/Object.cpp132
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/COFF/Object.h211
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/COFF/Reader.cpp226
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/COFF/Reader.h41
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/COFF/Writer.cpp466
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/COFF/Writer.h63
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/CommonConfig.h260
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/CommonOpts.td129
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/ConfigManager.cpp1432
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/ConfigManager.h80
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFConfig.h38
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFObjcopy.cpp833
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFObjcopy.h40
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/ELF/Object.cpp2826
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/ELF/Object.h1113
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/InstallNameToolOpts.td44
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOConfig.h43
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp441
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h97
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOObjcopy.cpp549
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOObjcopy.h39
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOReader.cpp374
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOReader.h57
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOWriter.cpp748
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOWriter.h71
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MachO/Object.cpp214
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MachO/Object.h374
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/MultiFormatConfig.h37
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/ObjcopyOpts.td226
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/StripOpts.td20
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/llvm-objcopy.cpp438
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/llvm-objcopy.h34
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/wasm/Object.cpp34
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/wasm/Object.h47
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/wasm/Reader.cpp33
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/wasm/Reader.h31
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmConfig.h21
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmObjcopy.cpp162
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmObjcopy.h32
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/wasm/Writer.cpp79
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/wasm/Writer.h49
-rw-r--r--contrib/libs/llvm14/tools/llvm-objcopy/ya.make55
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/COFFDump.cpp920
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/COFFDump.h36
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/ELFDump.cpp395
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/ELFDump.h39
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/MachODump.cpp10521
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/MachODump.h78
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/ObjdumpOptID.h13
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/ObjdumpOpts.td337
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/OtoolOpts.td68
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/SourcePrinter.cpp483
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/SourcePrinter.h166
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/WasmDump.cpp53
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/WasmDump.h35
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/XCOFFDump.cpp114
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/XCOFFDump.h37
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/llvm-objdump.cpp2776
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/llvm-objdump.h157
-rw-r--r--contrib/libs/llvm14/tools/llvm-objdump/ya.make70
-rw-r--r--contrib/libs/llvm14/tools/llvm-opt-report/OptReport.cpp478
-rw-r--r--contrib/libs/llvm14/tools/llvm-opt-report/ya.make31
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/BytesOutputStyle.cpp491
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/BytesOutputStyle.h68
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/DumpOutputStyle.cpp1989
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/DumpOutputStyle.h116
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/ExplainOutputStyle.cpp468
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/ExplainOutputStyle.h67
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/FormatUtil.cpp258
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/FormatUtil.h141
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/InputFile.cpp510
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/InputFile.h154
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/LinePrinter.cpp335
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/LinePrinter.h167
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/MinimalSymbolDumper.cpp902
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/MinimalSymbolDumper.h68
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/MinimalTypeDumper.cpp592
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/MinimalTypeDumper.h70
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/OutputStyle.h26
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PdbYaml.cpp189
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PdbYaml.h126
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp97
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyBuiltinDumper.h34
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp114
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h46
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp212
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h57
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyCompilandDumper.cpp228
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyCompilandDumper.h44
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyEnumDumper.cpp68
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyEnumDumper.h30
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp41
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h33
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyFunctionDumper.cpp267
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyFunctionDumper.h42
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypeDumper.cpp359
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypeDumper.h41
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypedefDumper.cpp82
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypedefDumper.h38
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyVariableDumper.cpp225
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/PrettyVariableDumper.h49
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/StreamUtil.cpp194
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/StreamUtil.h63
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/TypeReferenceTracker.cpp160
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/TypeReferenceTracker.h69
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/YAMLOutputStyle.cpp370
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/YAMLOutputStyle.h48
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/llvm-pdbutil.cpp1576
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/llvm-pdbutil.h220
-rw-r--r--contrib/libs/llvm14/tools/llvm-pdbutil/ya.make62
-rw-r--r--contrib/libs/llvm14/tools/llvm-profdata/llvm-profdata.cpp2669
-rw-r--r--contrib/libs/llvm14/tools/llvm-profdata/ya.make39
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/CSPreInliner.cpp285
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/CSPreInliner.h95
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/CallContext.h59
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/ErrorHandling.h56
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/PerfReader.cpp1222
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/PerfReader.h728
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/ProfileGenerator.cpp979
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/ProfileGenerator.h312
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/ProfiledBinary.cpp790
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/ProfiledBinary.h541
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/llvm-profgen.cpp164
-rw-r--r--contrib/libs/llvm14/tools/llvm-profgen/ya.make80
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/Opts.td65
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.cpp1567
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.h217
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.cpp111
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.h34
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.cpp856
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.h189
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.cpp294
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.h954
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.cpp367
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.h81
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptTokenList.def39
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceVisitor.h61
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/WindresOpts.td62
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/llvm-rc.cpp755
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ya.make44
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/ARMEHABIPrinter.h647
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/ARMWinEHPrinter.cpp1303
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/ARMWinEHPrinter.h188
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/COFFDumper.cpp2079
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/COFFImportDumper.cpp58
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/DwarfCFIEHPrinter.h241
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/ELFDumper.cpp7362
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/MachODumper.cpp938
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/ObjDumper.cpp216
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/ObjDumper.h167
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/Opts.td133
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/StackMapPrinter.h81
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/WasmDumper.cpp251
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/Win64EHDumper.cpp426
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/Win64EHDumper.h62
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/WindowsResourceDumper.cpp84
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/WindowsResourceDumper.h36
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/XCOFFDumper.cpp955
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/llvm-readobj.cpp632
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/llvm-readobj.h52
-rw-r--r--contrib/libs/llvm14/tools/llvm-readobj/ya.make53
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/DeltaManager.cpp134
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/DeltaManager.h25
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/ReducerWorkItem.cpp175
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/ReducerWorkItem.h37
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/TestRunner.cpp45
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/TestRunner.h51
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/Delta.cpp404
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/Delta.h114
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAliases.cpp36
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAliases.h23
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceArguments.cpp121
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceArguments.h26
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAttributes.cpp186
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAttributes.h22
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp138
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceBasicBlocks.h24
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctionBodies.cpp36
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctionBodies.h23
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctions.cpp55
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctions.h24
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalObjects.cpp32
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalObjects.h18
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalValues.cpp63
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalValues.h23
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.cpp34
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.h25
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp63
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVars.h25
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructions.cpp55
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructions.h25
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructionsMIR.cpp125
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructionsMIR.h23
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceMetadata.cpp72
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceMetadata.h23
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceModuleData.cpp34
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceModuleData.h18
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandBundles.cpp109
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandBundles.h22
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperands.cpp98
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperands.h20
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsSkip.cpp223
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsSkip.h18
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsToArgs.cpp198
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsToArgs.h18
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceSpecialGlobals.cpp42
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceSpecialGlobals.h26
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/llvm-reduce.cpp180
-rw-r--r--contrib/libs/llvm14/tools/llvm-reduce/ya.make109
-rw-r--r--contrib/libs/llvm14/tools/llvm-rtdyld/llvm-rtdyld.cpp1049
-rw-r--r--contrib/libs/llvm14/tools/llvm-rtdyld/ya.make59
-rw-r--r--contrib/libs/llvm14/tools/llvm-size/Opts.td32
-rw-r--r--contrib/libs/llvm14/tools/llvm-size/llvm-size.cpp937
-rw-r--r--contrib/libs/llvm14/tools/llvm-size/ya.make39
-rw-r--r--contrib/libs/llvm14/tools/llvm-split/llvm-split.cpp88
-rw-r--r--contrib/libs/llvm14/tools/llvm-split/ya.make44
-rw-r--r--contrib/libs/llvm14/tools/llvm-stress/llvm-stress.cpp778
-rw-r--r--contrib/libs/llvm14/tools/llvm-stress/ya.make33
-rw-r--r--contrib/libs/llvm14/tools/llvm-strings/Opts.td23
-rw-r--r--contrib/libs/llvm14/tools/llvm-strings/llvm-strings.cpp186
-rw-r--r--contrib/libs/llvm14/tools/llvm-strings/ya.make32
-rw-r--r--contrib/libs/llvm14/tools/llvm-symbolizer/Opts.td80
-rw-r--r--contrib/libs/llvm14/tools/llvm-symbolizer/llvm-symbolizer.cpp369
-rw-r--r--contrib/libs/llvm14/tools/llvm-symbolizer/ya.make50
-rw-r--r--contrib/libs/llvm14/tools/llvm-undname/llvm-undname.cpp146
-rw-r--r--contrib/libs/llvm14/tools/llvm-undname/ya.make27
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/func-id-helper.cpp75
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/func-id-helper.h50
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/llvm-xray.cpp48
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/trie-node.h91
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-account.cpp519
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-account.h115
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-color-helper.cpp222
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-color-helper.h87
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-converter.cpp425
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-converter.h43
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-extract.cpp101
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-fdr-dump.cpp119
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-graph-diff.cpp469
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-graph-diff.h73
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-graph.cpp534
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-graph.h231
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-registry.cpp40
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-registry.h40
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/xray-stacks.cpp792
-rw-r--r--contrib/libs/llvm14/tools/llvm-xray/ya.make52
-rw-r--r--contrib/libs/llvm14/tools/lto/LTODisassembler.cpp25
-rw-r--r--contrib/libs/llvm14/tools/lto/lto.cpp707
-rw-r--r--contrib/libs/llvm14/tools/lto/ya.make63
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/archive2yaml.cpp114
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/coff2yaml.cpp366
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/dwarf2yaml.cpp463
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/elf2yaml.cpp1596
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/macho2yaml.cpp672
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/minidump2yaml.cpp23
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/obj2yaml.cpp95
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/obj2yaml.h57
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/wasm2yaml.cpp405
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/xcoff2yaml.cpp152
-rw-r--r--contrib/libs/llvm14/tools/obj2yaml/ya.make47
-rw-r--r--contrib/libs/llvm14/tools/opt/AnalysisWrappers.cpp71
-rw-r--r--contrib/libs/llvm14/tools/opt/BreakpointPrinter.cpp71
-rw-r--r--contrib/libs/llvm14/tools/opt/BreakpointPrinter.h24
-rw-r--r--contrib/libs/llvm14/tools/opt/GraphPrinters.cpp45
-rw-r--r--contrib/libs/llvm14/tools/opt/NewPMDriver.cpp495
-rw-r--r--contrib/libs/llvm14/tools/opt/NewPMDriver.h79
-rw-r--r--contrib/libs/llvm14/tools/opt/PassPrinters.cpp212
-rw-r--r--contrib/libs/llvm14/tools/opt/PassPrinters.h40
-rw-r--r--contrib/libs/llvm14/tools/opt/PrintSCC.cpp111
-rw-r--r--contrib/libs/llvm14/tools/opt/opt.cpp1090
-rw-r--r--contrib/libs/llvm14/tools/opt/ya.make98
-rw-r--r--contrib/libs/llvm14/tools/polly/CREDITS.txt42
-rw-r--r--contrib/libs/llvm14/tools/polly/LICENSE.TXT273
-rw-r--r--contrib/libs/llvm14/tools/polly/README12
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Canonicalization.h37
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/BlockGenerators.h996
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/CodeGeneration.h39
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/CodegenCleanup.h17
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IRBuilder.h144
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslAst.h212
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslExprBuilder.h269
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslNodeBuilder.h425
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGenerators.h228
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGeneratorsGOMP.h74
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGeneratorsKMP.h145
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/PPCGCodeGeneration.h33
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/PerfMonitor.h142
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/RuntimeDebugBuilder.h169
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodeGen/Utils.h72
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/CodePreparation.h25
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Config/config.h18
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/DeLICM.h67
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/DeadCodeElimination.h40
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/DependenceInfo.h311
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/FlattenAlgo.h37
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/FlattenSchedule.h31
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/ForwardOpTree.h47
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/JSONExporter.h31
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/LinkAllPasses.h132
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/ManualOptimizer.h43
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/MatmulOptimizer.h74
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Options.h19
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/PolyhedralInfo.h101
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/PruneUnprofitable.h38
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/RegisterPasses.h32
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/ScheduleOptimizer.h46
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/ScheduleTreeTransform.h286
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/ScopBuilder.h782
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/ScopDetection.h681
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/ScopDetectionDiagnostic.h890
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/ScopInfo.h2869
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/ScopPass.h293
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Simplify.h81
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Support/DumpFunctionPass.h44
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Support/DumpModulePass.h54
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Support/GICHelper.h492
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Support/ISLOStream.h48
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Support/ISLTools.h640
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Support/SCEVAffinator.h124
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Support/SCEVValidator.h90
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Support/ScopHelper.h602
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Support/ScopLocation.h34
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/Support/VirtualInstruction.h346
-rw-r--r--contrib/libs/llvm14/tools/polly/include/polly/ZoneAlgo.h419
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Analysis/DependenceInfo.cpp983
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Analysis/PolyhedralInfo.cpp167
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Analysis/PruneUnprofitable.cpp123
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Analysis/ScopBuilder.cpp3650
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Analysis/ScopDetection.cpp2044
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Analysis/ScopDetectionDiagnostic.cpp834
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Analysis/ScopGraphPrinter.cpp263
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Analysis/ScopInfo.cpp2775
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Analysis/ScopPass.cpp169
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/BlockGenerators.cpp1795
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/CodeGeneration.cpp386
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/CodegenCleanup.cpp139
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/IRBuilder.cpp270
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/IslAst.cpp825
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/IslExprBuilder.cpp788
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/IslNodeBuilder.cpp1559
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGenerators.cpp253
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGeneratorsGOMP.cpp223
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGeneratorsKMP.cpp558
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/ManagedMemoryRewrite.cpp428
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/PPCGCodeGeneration.cpp3666
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/PerfMonitor.cpp303
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/RuntimeDebugBuilder.cpp290
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/CodeGen/Utils.cpp219
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Exchange/JSONExporter.cpp835
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/README.txt18
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/AUTHORS64
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/ChangeLog250
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/LICENSE19
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/README53
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/basis_reduction_tab.c293
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/basis_reduction_templ.c356
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/bset_from_bmap.c8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/bset_to_bmap.c10
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/check_single_reference_templ.c19
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/check_type_range_templ.c20
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/extract_key.c62
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/gitversion.h1
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/has_single_reference_templ.c12
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/imath/ChangeLog563
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/imath/LICENSE20
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/imath/README.md107
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/imath/gmp_compat.c823
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/imath/gmp_compat.h229
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imath.c2780
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imath.h421
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imrat.c940
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imrat.h270
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/aff.h1476
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/aff_type.h52
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/arg.h325
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast.h242
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast_build.h131
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast_type.h109
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/constraint.h148
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ctx.h265
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/fixed_box.h43
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/flow.h156
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hash.h78
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hmap.h55
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hmap_templ.c425
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id.h54
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_ast_expr.h18
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_id.h17
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_pw_aff.h18
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_type.h22
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ilp.h67
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/isl-noexceptions.h22973
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/list.h127
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/local_space.h98
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/lp.h37
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map.h786
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map_to_basic_set.h18
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map_type.h38
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/mat.h119
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe.h7
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_ast_expr.h8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_basic_set.h8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_id.h8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_pw_aff.h8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_templ.h12
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/multi.h278
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/obj.h57
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/options.h56
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/point.h46
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/polynomial.h833
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/polynomial_type.h38
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/printer.h84
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/printer_type.h15
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule.h201
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule_node.h300
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule_type.h33
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/set.h600
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/set_type.h6
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/space.h217
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/space_type.h27
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stdint.h1
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stream.h98
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stride_info.h30
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_map.h372
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_map_type.h24
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_set.h199
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_set_type.h6
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/val.h179
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/val_type.h22
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/vec.h80
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/version.h14
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/vertices.h47
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff.c10096
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_lex_templ.c55
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_map.c596
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_private.h237
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_affine_hull.c1252
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_align_params_bin_templ.c8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_align_params_templ.c40
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_arg.c1312
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast.c2972
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build.c2459
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_expr.c2611
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_expr.h22
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_private.h327
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_codegen.c5952
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_graft.c1466
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_graft_private.h105
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_private.h122
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_basis_reduction.h27
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bernstein.c583
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bernstein.h5
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bind_domain_templ.c168
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_blk.c134
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_blk.h40
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bound.c427
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bound.h29
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_box.c504
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_check_named_params_templ.c10
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_coalesce.c4262
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-linux.h56
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-osx.h1
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-win.h55
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config.h9
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config_post.h38
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_constraint.c1371
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_constraint_private.h32
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_convex_hull.c3148
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_copy_tuple_id_templ.c33
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ctx.c409
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ctx_private.h47
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_deprecated.c15
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_dim_map.c252
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_dim_map.h37
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_domain_factor_templ.c67
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_equalities.c897
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_equalities.h36
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_factorization.c389
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_factorization.h42
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_farkas.c966
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ffs.c24
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_flow.c3340
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_fold.c2183
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_hash.c241
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id.c307
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_private.h48
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_ast_expr.c16
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_id.c16
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_pw_aff.c16
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp.c912
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_opt_multi_val_templ.c75
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_opt_val_templ.c42
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_private.h11
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_imath.c83
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_imath.h10
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_input.c4491
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_insert_domain_templ.c39
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int.h52
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int_sioimath.c223
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int_sioimath.h1253
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_macro.h8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_private.h10
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_read_templ.c65
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_templ.c691
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_templ.h16
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local.c318
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local.h23
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_private.h8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_space.c1707
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_space_private.h100
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_lp.c371
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_lp_private.h21
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map.c14451
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_bound_templ.c57
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_lexopt_templ.c229
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_list.c33
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_private.h598
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_simplify.c5544
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_subtract.c944
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_to_basic_set.c14
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_mat.c2110
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_mat_private.h70
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_maybe_ast_graft_list.h10
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_maybe_map.h10
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_morph.c804
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_morph.h92
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_add_constant_templ.c93
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_set.c7
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_templ.c45
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_union_set.c7
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_set.c7
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_templ.c84
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_union_set.c7
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_arith_templ.c234
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_bind_domain_templ.c14
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_bind_templ.c65
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_cmp.c40
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_coalesce.c35
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_dim_id_templ.c92
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_dims.c119
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_domain_templ.c42
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_explicit_domain.c205
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_floor.c29
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_from_base_templ.c37
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_gist.c29
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_hash.c30
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_identity_templ.c96
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_insert_domain_templ.c14
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_intersect.c151
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_locals_templ.c17
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_macro.h8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_min_max_templ.c24
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_move_dims_templ.c71
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_nan_templ.c17
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_no_domain_templ.c118
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_no_explicit_domain.c172
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_param_templ.c60
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_product_templ.c68
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_pw_aff_explicit_domain.c134
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_read_no_explicit_domain_templ.c94
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_splice_templ.c63
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_templ.c906
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_templ.h34
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_tuple_id_templ.c145
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_unbind_params_templ.c14
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_union_add_templ.c81
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_union_pw_aff_explicit_domain.c51
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_zero_space_templ.c21
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_zero_templ.c53
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_obj.c365
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_opt_mpa_templ.c51
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_options.c392
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_options_private.h75
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_output.c3646
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_output_private.h27
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_point.c818
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_point_private.h27
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_polynomial.c5228
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_polynomial_private.h299
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_power_templ.c81
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_printer.c851
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_printer_private.h52
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_project_out_all_params_templ.c21
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_multi_val_templ.c13
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_templ.c47
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_val_templ.c13
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_bind_domain_templ.c14
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_eval.c86
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_hash.c33
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_insert_dims_templ.c59
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_insert_domain_templ.c14
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_lift_templ.c76
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_locals_templ.c35
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_macro.h8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_morph_templ.c49
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_move_dims_templ.c49
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_neg_templ.c34
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_opt_templ.c54
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_pullback_templ.c121
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_range_tuple_id_templ.c46
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_sub_templ.c16
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_templ.c2079
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_templ.h5
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_union_opt.c359
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_range.c562
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_range.h6
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_reordering.c313
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_reordering.h36
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sample.c1335
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sample.h36
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scan.c327
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scan.h26
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule.c684
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_band.c1310
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_band.h125
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_constraints.c768
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_constraints.h31
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_node.c4906
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_node_private.h68
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_private.h36
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_read.c779
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_tree.c2906
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_tree.h266
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scheduler.c7661
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_seq.c363
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_seq.h63
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_list.c34
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_to_ast_graft_list.c17
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_to_ast_graft_list.h18
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sort.c157
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sort.h9
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_space.c3366
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_space_private.h100
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stream.c1190
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stream_private.h69
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stride.c393
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab.c4259
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab.h338
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab_lexopt_templ.c235
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab_pip.c6011
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tarjan.c159
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tarjan.h42
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_transitive_closure.c2947
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_check_equal_space_templ.c25
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_equal_space_bin_templ.c8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_equal_space_templ.c28
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_space_templ.c18
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_unbind_params_templ.c35
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_eval.c82
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_locals_templ.c27
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_macro.h10
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map.c4564
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map_lex_templ.c23
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map_private.h27
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_multi.c549
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_neg.c25
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_pw_templ.c22
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_set_private.h11
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_single.c231
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_templ.c1419
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val.c1645
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val_private.h61
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val_sioimath.c68
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vec.c655
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vec_private.h30
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_version.c17
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vertices.c1624
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vertices_private.h71
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/isl_yaml.h18
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/opt_type.h16
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/print.c105
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/print_templ.c42
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/print_templ_yaml.c39
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/print_yaml_field_templ.c22
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/read_in_string_templ.c38
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/set_from_map.c8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/set_list_from_map_list_inl.c9
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/set_to_map.c10
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/uset_from_umap.c8
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/uset_to_umap.c10
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/isl/ya.make113
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/pet/include/pet.h622
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/ChangeLog29
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/README246
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/cpu.h15
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda.c730
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda.h13
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda_common.c50
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda_common.h15
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/external.c192
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu.c5849
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu.h459
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_array_tile.c71
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_array_tile.h59
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_group.c1828
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_group.h65
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_hybrid.c146
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_hybrid.h13
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_print.c310
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_print.h28
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_tree.c640
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_tree.h33
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/grouping.c684
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/hybrid.c2242
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/hybrid.h41
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/opencl.h11
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg.c1067
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg.h128
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg_options.c136
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg_options.h100
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/print.c461
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/print.h40
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/schedule.c165
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/schedule.h21
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/util.c105
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/util.h22
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/External/ppcg/ya.make49
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Support/DumpFunctionPass.cpp130
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Support/DumpModulePass.cpp102
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Support/GICHelper.cpp259
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Support/ISLTools.cpp908
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Support/PollyPasses.def42
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Support/RegisterPasses.cpp840
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Support/SCEVAffinator.cpp576
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Support/SCEVValidator.cpp770
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Support/ScopHelper.cpp817
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Support/ScopLocation.cpp43
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Support/VirtualInstruction.cpp420
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/Canonicalization.cpp178
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/CodePreparation.cpp120
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/DeLICM.cpp1496
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/DeadCodeElimination.cpp200
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/FlattenAlgo.cpp342
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/FlattenSchedule.cpp106
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/ForwardOpTree.cpp1169
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/ManualOptimizer.cpp304
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/MatmulOptimizer.cpp1039
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/MaximalStaticExpansion.cpp487
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/ScheduleOptimizer.cpp1010
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/ScheduleTreeTransform.cpp1243
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/ScopInliner.cpp127
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/Simplify.cpp852
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/Transform/ZoneAlgo.cpp1172
-rw-r--r--contrib/libs/llvm14/tools/polly/lib/ya.make104
-rw-r--r--contrib/libs/llvm14/tools/remarks-shlib/libremarks.cpp17
-rw-r--r--contrib/libs/llvm14/tools/remarks-shlib/ya.make26
-rw-r--r--contrib/libs/llvm14/tools/sancov/sancov.cpp1194
-rw-r--r--contrib/libs/llvm14/tools/sancov/ya.make61
-rw-r--r--contrib/libs/llvm14/tools/sanstats/sanstats.cpp150
-rw-r--r--contrib/libs/llvm14/tools/sanstats/ya.make42
-rw-r--r--contrib/libs/llvm14/tools/split-file/split-file.cpp178
-rw-r--r--contrib/libs/llvm14/tools/split-file/ya.make27
-rw-r--r--contrib/libs/llvm14/tools/verify-uselistorder/verify-uselistorder.cpp574
-rw-r--r--contrib/libs/llvm14/tools/verify-uselistorder/ya.make43
-rw-r--r--contrib/libs/llvm14/tools/yaml2obj/ya.make38
-rw-r--r--contrib/libs/llvm14/tools/yaml2obj/yaml2obj.cpp145
1007 files changed, 414489 insertions, 0 deletions
diff --git a/contrib/libs/llvm14/tools/bugpoint/BugDriver.cpp b/contrib/libs/llvm14/tools/bugpoint/BugDriver.cpp
new file mode 100644
index 00000000000..942028cad80
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/BugDriver.cpp
@@ -0,0 +1,260 @@
+//===- BugDriver.cpp - Top-Level BugPoint class implementation ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This class contains all of the shared state and information that is used by
+// the BugPoint tool to track down errors in optimizations. This class is the
+// main driver class that invokes all sub-functionality.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BugDriver.h"
+#include "ToolRunner.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Linker/Linker.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+using namespace llvm;
+
+namespace llvm {
+Triple TargetTriple;
+}
+
+DiscardTemp::~DiscardTemp() {
+ if (SaveTemps) {
+ if (Error E = File.keep())
+ errs() << "Failed to keep temp file " << toString(std::move(E)) << '\n';
+ return;
+ }
+ if (Error E = File.discard())
+ errs() << "Failed to delete temp file " << toString(std::move(E)) << '\n';
+}
+
+// Anonymous namespace to define command line options for debugging.
+//
+namespace {
+// Output - The user can specify a file containing the expected output of the
+// program. If this filename is set, it is used as the reference diff source,
+// otherwise the raw input run through an interpreter is used as the reference
+// source.
+//
+cl::opt<std::string> OutputFile("output",
+ cl::desc("Specify a reference program output "
+ "(for miscompilation detection)"));
+}
+
+/// If we reduce or update the program somehow, call this method to update
+/// bugdriver with it. This deletes the old module and sets the specified one
+/// as the current program.
+void BugDriver::setNewProgram(std::unique_ptr<Module> M) {
+ Program = std::move(M);
+}
+
+/// getPassesString - Turn a list of passes into a string which indicates the
+/// command line options that must be passed to add the passes.
+///
+std::string llvm::getPassesString(const std::vector<std::string> &Passes) {
+ std::string Result;
+ for (unsigned i = 0, e = Passes.size(); i != e; ++i) {
+ if (i)
+ Result += " ";
+ Result += "-";
+ Result += Passes[i];
+ }
+ return Result;
+}
+
+BugDriver::BugDriver(const char *toolname, bool find_bugs, unsigned timeout,
+ unsigned memlimit, bool use_valgrind, LLVMContext &ctxt)
+ : Context(ctxt), ToolName(toolname), ReferenceOutputFile(OutputFile),
+ Program(nullptr), Interpreter(nullptr), SafeInterpreter(nullptr),
+ cc(nullptr), run_find_bugs(find_bugs), Timeout(timeout),
+ MemoryLimit(memlimit), UseValgrind(use_valgrind) {}
+
+BugDriver::~BugDriver() {
+ if (Interpreter != SafeInterpreter)
+ delete Interpreter;
+ delete SafeInterpreter;
+ delete cc;
+}
+
+std::unique_ptr<Module> llvm::parseInputFile(StringRef Filename,
+ LLVMContext &Ctxt) {
+ SMDiagnostic Err;
+ std::unique_ptr<Module> Result = parseIRFile(Filename, Err, Ctxt);
+ if (!Result) {
+ Err.print("bugpoint", errs());
+ return Result;
+ }
+
+ if (verifyModule(*Result, &errs())) {
+ errs() << "bugpoint: " << Filename << ": error: input module is broken!\n";
+ return std::unique_ptr<Module>();
+ }
+
+ // If we don't have an override triple, use the first one to configure
+ // bugpoint, or use the host triple if none provided.
+ if (TargetTriple.getTriple().empty()) {
+ Triple TheTriple(Result->getTargetTriple());
+
+ if (TheTriple.getTriple().empty())
+ TheTriple.setTriple(sys::getDefaultTargetTriple());
+
+ TargetTriple.setTriple(TheTriple.getTriple());
+ }
+
+ Result->setTargetTriple(TargetTriple.getTriple()); // override the triple
+ return Result;
+}
+
+std::unique_ptr<Module> BugDriver::swapProgramIn(std::unique_ptr<Module> M) {
+ std::unique_ptr<Module> OldProgram = std::move(Program);
+ Program = std::move(M);
+ return OldProgram;
+}
+
+// This method takes the specified list of LLVM input files, attempts to load
+// them, either as assembly or bitcode, then link them together. It returns
+// true on failure (if, for example, an input bitcode file could not be
+// parsed), and false on success.
+//
+bool BugDriver::addSources(const std::vector<std::string> &Filenames) {
+ assert(!Program && "Cannot call addSources multiple times!");
+ assert(!Filenames.empty() && "Must specify at least on input filename!");
+
+ // Load the first input file.
+ Program = parseInputFile(Filenames[0], Context);
+ if (!Program)
+ return true;
+
+ outs() << "Read input file : '" << Filenames[0] << "'\n";
+
+ for (unsigned i = 1, e = Filenames.size(); i != e; ++i) {
+ std::unique_ptr<Module> M = parseInputFile(Filenames[i], Context);
+ if (!M.get())
+ return true;
+
+ outs() << "Linking in input file: '" << Filenames[i] << "'\n";
+ if (Linker::linkModules(*Program, std::move(M)))
+ return true;
+ }
+
+ outs() << "*** All input ok\n";
+
+ // All input files read successfully!
+ return false;
+}
+
+/// run - The top level method that is invoked after all of the instance
+/// variables are set up from command line arguments.
+///
+Error BugDriver::run() {
+ if (run_find_bugs) {
+ // Rearrange the passes and apply them to the program. Repeat this process
+ // until the user kills the program or we find a bug.
+ return runManyPasses(PassesToRun);
+ }
+
+ // If we're not running as a child, the first thing that we must do is
+ // determine what the problem is. Does the optimization series crash the
+ // compiler, or does it produce illegal code? We make the top-level
+ // decision by trying to run all of the passes on the input program,
+ // which should generate a bitcode file. If it does generate a bitcode
+ // file, then we know the compiler didn't crash, so try to diagnose a
+ // miscompilation.
+ if (!PassesToRun.empty()) {
+ outs() << "Running selected passes on program to test for crash: ";
+ if (runPasses(*Program, PassesToRun))
+ return debugOptimizerCrash();
+ }
+
+ // Set up the execution environment, selecting a method to run LLVM bitcode.
+ if (Error E = initializeExecutionEnvironment())
+ return E;
+
+ // Test to see if we have a code generator crash.
+ outs() << "Running the code generator to test for a crash: ";
+ if (Error E = compileProgram(*Program)) {
+ outs() << toString(std::move(E));
+ return debugCodeGeneratorCrash();
+ }
+ outs() << '\n';
+
+ // Run the raw input to see where we are coming from. If a reference output
+ // was specified, make sure that the raw output matches it. If not, it's a
+ // problem in the front-end or the code generator.
+ //
+ bool CreatedOutput = false;
+ if (ReferenceOutputFile.empty()) {
+ outs() << "Generating reference output from raw program: ";
+ if (Error E = createReferenceFile(*Program)) {
+ errs() << toString(std::move(E));
+ return debugCodeGeneratorCrash();
+ }
+ CreatedOutput = true;
+ }
+
+ // Make sure the reference output file gets deleted on exit from this
+ // function, if appropriate.
+ std::string ROF(ReferenceOutputFile);
+ FileRemover RemoverInstance(ROF, CreatedOutput && !SaveTemps);
+
+ // Diff the output of the raw program against the reference output. If it
+ // matches, then we assume there is a miscompilation bug and try to
+ // diagnose it.
+ outs() << "*** Checking the code generator...\n";
+ Expected<bool> Diff = diffProgram(*Program, "", "", false);
+ if (Error E = Diff.takeError()) {
+ errs() << toString(std::move(E));
+ return debugCodeGeneratorCrash();
+ }
+ if (!*Diff) {
+ outs() << "\n*** Output matches: Debugging miscompilation!\n";
+ if (Error E = debugMiscompilation()) {
+ errs() << toString(std::move(E));
+ return debugCodeGeneratorCrash();
+ }
+ return Error::success();
+ }
+
+ outs() << "\n*** Input program does not match reference diff!\n";
+ outs() << "Debugging code generator problem!\n";
+ if (Error E = debugCodeGenerator()) {
+ errs() << toString(std::move(E));
+ return debugCodeGeneratorCrash();
+ }
+ return Error::success();
+}
+
+void llvm::PrintFunctionList(const std::vector<Function *> &Funcs) {
+ unsigned NumPrint = Funcs.size();
+ if (NumPrint > 10)
+ NumPrint = 10;
+ for (unsigned i = 0; i != NumPrint; ++i)
+ outs() << " " << Funcs[i]->getName();
+ if (NumPrint < Funcs.size())
+ outs() << "... <" << Funcs.size() << " total>";
+ outs().flush();
+}
+
+void llvm::PrintGlobalVariableList(const std::vector<GlobalVariable *> &GVs) {
+ unsigned NumPrint = GVs.size();
+ if (NumPrint > 10)
+ NumPrint = 10;
+ for (unsigned i = 0; i != NumPrint; ++i)
+ outs() << " " << GVs[i]->getName();
+ if (NumPrint < GVs.size())
+ outs() << "... <" << GVs.size() << " total>";
+ outs().flush();
+}
diff --git a/contrib/libs/llvm14/tools/bugpoint/BugDriver.h b/contrib/libs/llvm14/tools/bugpoint/BugDriver.h
new file mode 100644
index 00000000000..b7c9edc5b81
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/BugDriver.h
@@ -0,0 +1,306 @@
+//===- BugDriver.h - Top-Level BugPoint class -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This class contains all of the shared state and information that is used by
+// the BugPoint tool to track down errors in optimizations. This class is the
+// main driver class that invokes all sub-functionality.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_BUGPOINT_BUGDRIVER_H
+#define LLVM_TOOLS_BUGPOINT_BUGDRIVER_H
+
+#include "llvm/IR/ValueMap.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Transforms/Utils/ValueMapper.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace llvm {
+
+class PassInfo;
+class Module;
+class GlobalVariable;
+class Function;
+class BasicBlock;
+class AbstractInterpreter;
+class Instruction;
+class LLVMContext;
+
+class CC;
+
+extern bool DisableSimplifyCFG;
+
+/// BugpointIsInterrupted - Set to true when the user presses ctrl-c.
+///
+extern bool BugpointIsInterrupted;
+
+class BugDriver {
+ LLVMContext &Context;
+ const char *ToolName; // argv[0] of bugpoint
+ std::string ReferenceOutputFile; // Name of `good' output file
+ std::unique_ptr<Module> Program; // The raw program, linked together
+ std::vector<std::string> PassesToRun;
+ AbstractInterpreter *Interpreter; // How to run the program
+ AbstractInterpreter *SafeInterpreter; // To generate reference output, etc.
+ CC *cc;
+ bool run_find_bugs;
+ unsigned Timeout;
+ unsigned MemoryLimit;
+ bool UseValgrind;
+
+ // FIXME: sort out public/private distinctions...
+ friend class ReducePassList;
+ friend class ReduceMisCodegenFunctions;
+
+public:
+ BugDriver(const char *toolname, bool find_bugs, unsigned timeout,
+ unsigned memlimit, bool use_valgrind, LLVMContext &ctxt);
+ ~BugDriver();
+
+ const char *getToolName() const { return ToolName; }
+
+ LLVMContext &getContext() const { return Context; }
+
+ // Set up methods... these methods are used to copy information about the
+ // command line arguments into instance variables of BugDriver.
+ //
+ bool addSources(const std::vector<std::string> &FileNames);
+ void addPass(std::string p) { PassesToRun.push_back(std::move(p)); }
+ void setPassesToRun(const std::vector<std::string> &PTR) {
+ PassesToRun = PTR;
+ }
+ const std::vector<std::string> &getPassesToRun() const { return PassesToRun; }
+
+ /// run - The top level method that is invoked after all of the instance
+ /// variables are set up from command line arguments. The \p as_child argument
+ /// indicates whether the driver is to run in parent mode or child mode.
+ ///
+ Error run();
+
+ /// debugOptimizerCrash - This method is called when some optimizer pass
+ /// crashes on input. It attempts to prune down the testcase to something
+ /// reasonable, and figure out exactly which pass is crashing.
+ ///
+ Error debugOptimizerCrash(const std::string &ID = "passes");
+
+ /// debugCodeGeneratorCrash - This method is called when the code generator
+ /// crashes on an input. It attempts to reduce the input as much as possible
+ /// while still causing the code generator to crash.
+ Error debugCodeGeneratorCrash();
+
+ /// debugMiscompilation - This method is used when the passes selected are not
+ /// crashing, but the generated output is semantically different from the
+ /// input.
+ Error debugMiscompilation();
+
+ /// debugPassMiscompilation - This method is called when the specified pass
+ /// miscompiles Program as input. It tries to reduce the testcase to
+ /// something that smaller that still miscompiles the program.
+ /// ReferenceOutput contains the filename of the file containing the output we
+ /// are to match.
+ ///
+ bool debugPassMiscompilation(const PassInfo *ThePass,
+ const std::string &ReferenceOutput);
+
+ /// compileSharedObject - This method creates a SharedObject from a given
+ /// BitcodeFile for debugging a code generator.
+ ///
+ Expected<std::string> compileSharedObject(const std::string &BitcodeFile);
+
+ /// debugCodeGenerator - This method narrows down a module to a function or
+ /// set of functions, using the CBE as a ``safe'' code generator for other
+ /// functions that are not under consideration.
+ Error debugCodeGenerator();
+
+ /// isExecutingJIT - Returns true if bugpoint is currently testing the JIT
+ ///
+ bool isExecutingJIT();
+
+ Module &getProgram() const { return *Program; }
+
+ /// Set the current module to the specified module, returning the old one.
+ std::unique_ptr<Module> swapProgramIn(std::unique_ptr<Module> M);
+
+ AbstractInterpreter *switchToSafeInterpreter() {
+ AbstractInterpreter *Old = Interpreter;
+ Interpreter = (AbstractInterpreter *)SafeInterpreter;
+ return Old;
+ }
+
+ void switchToInterpreter(AbstractInterpreter *AI) { Interpreter = AI; }
+
+ /// If we reduce or update the program somehow, call this method to update
+ /// bugdriver with it. This deletes the old module and sets the specified one
+ /// as the current program.
+ void setNewProgram(std::unique_ptr<Module> M);
+
+ /// Try to compile the specified module. This is used for code generation
+ /// crash testing.
+ Error compileProgram(Module &M) const;
+
+ /// This method runs "Program", capturing the output of the program to a file.
+ /// A recommended filename may be optionally specified.
+ Expected<std::string> executeProgram(const Module &Program,
+ std::string OutputFilename,
+ std::string Bitcode,
+ const std::string &SharedObjects,
+ AbstractInterpreter *AI) const;
+
+ /// Used to create reference output with the "safe" backend, if reference
+ /// output is not provided. If there is a problem with the code generator
+ /// (e.g., llc crashes), this will return false and set Error.
+ Expected<std::string>
+ executeProgramSafely(const Module &Program,
+ const std::string &OutputFile) const;
+
+ /// Calls compileProgram and then records the output into ReferenceOutputFile.
+ /// Returns true if reference file created, false otherwise. Note:
+ /// initializeExecutionEnvironment should be called BEFORE this function.
+ Error createReferenceFile(Module &M, const std::string &Filename =
+ "bugpoint.reference.out-%%%%%%%");
+
+ /// This method executes the specified module and diffs the output against the
+ /// file specified by ReferenceOutputFile. If the output is different, 1 is
+ /// returned. If there is a problem with the code generator (e.g., llc
+ /// crashes), this will return -1 and set Error.
+ Expected<bool> diffProgram(const Module &Program,
+ const std::string &BitcodeFile = "",
+ const std::string &SharedObj = "",
+ bool RemoveBitcode = false) const;
+
+ /// This function is used to output M to a file named "bugpoint-ID.bc".
+ void EmitProgressBitcode(const Module &M, const std::string &ID,
+ bool NoFlyer = false) const;
+
+ /// This method clones the current Program and deletes the specified
+ /// instruction from the cloned module. It then runs a series of cleanup
+ /// passes (ADCE and SimplifyCFG) to eliminate any code which depends on the
+ /// value. The modified module is then returned.
+ ///
+ std::unique_ptr<Module> deleteInstructionFromProgram(const Instruction *I,
+ unsigned Simp);
+
+ /// This method clones the current Program and performs a series of cleanups
+ /// intended to get rid of extra cruft on the module. If the
+ /// MayModifySemantics argument is true, then the cleanups is allowed to
+ /// modify how the code behaves.
+ ///
+ std::unique_ptr<Module> performFinalCleanups(std::unique_ptr<Module> M,
+ bool MayModifySemantics = false);
+
+ /// Given a module, extract up to one loop from it into a new function. This
+ /// returns null if there are no extractable loops in the program or if the
+ /// loop extractor crashes.
+ std::unique_ptr<Module> extractLoop(Module *M);
+
+ /// Extract all but the specified basic blocks into their own functions. The
+ /// only detail is that M is actually a module cloned from the one the BBs are
+ /// in, so some mapping needs to be performed. If this operation fails for
+ /// some reason (ie the implementation is buggy), this function should return
+ /// null, otherwise it returns a new Module.
+ std::unique_ptr<Module>
+ extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs,
+ Module *M);
+
+ /// Carefully run the specified set of pass on the specified/ module,
+ /// returning the transformed module on success, or a null pointer on failure.
+ std::unique_ptr<Module> runPassesOn(Module *M,
+ const std::vector<std::string> &Passes,
+ ArrayRef<std::string> ExtraArgs = {});
+
+ /// runPasses - Run the specified passes on Program, outputting a bitcode
+ /// file and writting the filename into OutputFile if successful. If the
+ /// optimizations fail for some reason (optimizer crashes), return true,
+ /// otherwise return false. If DeleteOutput is set to true, the bitcode is
+ /// deleted on success, and the filename string is undefined. This prints to
+ /// outs() a single line message indicating whether compilation was successful
+ /// or failed, unless Quiet is set. ExtraArgs specifies additional arguments
+ /// to pass to the child bugpoint instance.
+ ///
+ bool runPasses(Module &Program, const std::vector<std::string> &PassesToRun,
+ std::string &OutputFilename, bool DeleteOutput = false,
+ bool Quiet = false,
+ ArrayRef<std::string> ExtraArgs = {}) const;
+
+ /// runPasses - Just like the method above, but this just returns true or
+ /// false indicating whether or not the optimizer crashed on the specified
+ /// input (true = crashed). Does not produce any output.
+ ///
+ bool runPasses(Module &M, const std::vector<std::string> &PassesToRun) const {
+ std::string Filename;
+ return runPasses(M, PassesToRun, Filename, true);
+ }
+
+ /// Take the specified pass list and create different combinations of passes
+ /// to compile the program with. Compile the program with each set and mark
+ /// test to see if it compiled correctly. If the passes compiled correctly
+ /// output nothing and rearrange the passes into a new order. If the passes
+ /// did not compile correctly, output the command required to recreate the
+ /// failure.
+ Error runManyPasses(const std::vector<std::string> &AllPasses);
+
+ /// This writes the current "Program" to the named bitcode file. If an error
+ /// occurs, true is returned.
+ bool writeProgramToFile(const std::string &Filename, const Module &M) const;
+ bool writeProgramToFile(const std::string &Filename, int FD,
+ const Module &M) const;
+ bool writeProgramToFile(int FD, const Module &M) const;
+
+private:
+ /// initializeExecutionEnvironment - This method is used to set up the
+ /// environment for executing LLVM programs.
+ ///
+ Error initializeExecutionEnvironment();
+};
+
+struct DiscardTemp {
+ sys::fs::TempFile &File;
+ ~DiscardTemp();
+};
+
+/// Given a bitcode or assembly input filename, parse and return it, or return
+/// null if not possible.
+///
+std::unique_ptr<Module> parseInputFile(StringRef InputFilename,
+ LLVMContext &ctxt);
+
+/// getPassesString - Turn a list of passes into a string which indicates the
+/// command line options that must be passed to add the passes.
+///
+std::string getPassesString(const std::vector<std::string> &Passes);
+
+/// PrintFunctionList - prints out list of problematic functions
+///
+void PrintFunctionList(const std::vector<Function *> &Funcs);
+
+/// PrintGlobalVariableList - prints out list of problematic global variables
+///
+void PrintGlobalVariableList(const std::vector<GlobalVariable *> &GVs);
+
+// DeleteGlobalInitializer - "Remove" the global variable by deleting its
+// initializer, making it external.
+//
+void DeleteGlobalInitializer(GlobalVariable *GV);
+
+// DeleteFunctionBody - "Remove" the function by deleting all of it's basic
+// blocks, making it external.
+//
+void DeleteFunctionBody(Function *F);
+
+/// Given a module and a list of functions in the module, split the functions
+/// OUT of the specified module, and place them in the new module.
+std::unique_ptr<Module>
+SplitFunctionsOutOfModule(Module *M, const std::vector<Function *> &F,
+ ValueToValueMapTy &VMap);
+
+} // End llvm namespace
+
+#endif
diff --git a/contrib/libs/llvm14/tools/bugpoint/CrashDebugger.cpp b/contrib/libs/llvm14/tools/bugpoint/CrashDebugger.cpp
new file mode 100644
index 00000000000..d127ea0945f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/CrashDebugger.cpp
@@ -0,0 +1,1430 @@
+//===- CrashDebugger.cpp - Debug compilation crashes ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the bugpoint internals that narrow down compilation crashes
+//
+//===----------------------------------------------------------------------===//
+
+#include "BugDriver.h"
+#include "ListReducer.h"
+#include "ToolRunner.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/ValueSymbolTable.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/Local.h"
+#include <set>
+using namespace llvm;
+
+namespace {
+cl::opt<bool> KeepMain("keep-main",
+ cl::desc("Force function reduction to keep main"),
+ cl::init(false));
+cl::opt<bool> NoGlobalRM("disable-global-remove",
+ cl::desc("Do not remove global variables"),
+ cl::init(false));
+
+cl::opt<bool> NoAttributeRM("disable-attribute-remove",
+ cl::desc("Do not remove function attributes"),
+ cl::init(false));
+
+cl::opt<bool> ReplaceFuncsWithNull(
+ "replace-funcs-with-null",
+ cl::desc("When stubbing functions, replace all uses will null"),
+ cl::init(false));
+cl::opt<bool> DontReducePassList("disable-pass-list-reduction",
+ cl::desc("Skip pass list reduction steps"),
+ cl::init(false));
+
+cl::opt<bool> NoNamedMDRM("disable-namedmd-remove",
+ cl::desc("Do not remove global named metadata"),
+ cl::init(false));
+cl::opt<bool> NoStripDebugInfo("disable-strip-debuginfo",
+ cl::desc("Do not strip debug info metadata"),
+ cl::init(false));
+cl::opt<bool> NoStripDebugTypeInfo("disable-strip-debug-types",
+ cl::desc("Do not strip debug type info metadata"),
+ cl::init(false));
+cl::opt<bool> VerboseErrors("verbose-errors",
+ cl::desc("Print the output of crashing program"),
+ cl::init(false));
+}
+
+namespace llvm {
+class ReducePassList : public ListReducer<std::string> {
+ BugDriver &BD;
+
+public:
+ ReducePassList(BugDriver &bd) : BD(bd) {}
+
+ // Return true iff running the "removed" passes succeeds, and running the
+ // "Kept" passes fail when run on the output of the "removed" passes. If we
+ // return true, we update the current module of bugpoint.
+ Expected<TestResult> doTest(std::vector<std::string> &Removed,
+ std::vector<std::string> &Kept) override;
+};
+}
+
+Expected<ReducePassList::TestResult>
+ReducePassList::doTest(std::vector<std::string> &Prefix,
+ std::vector<std::string> &Suffix) {
+ std::string PrefixOutput;
+ std::unique_ptr<Module> OrigProgram;
+ if (!Prefix.empty()) {
+ outs() << "Checking to see if these passes crash: "
+ << getPassesString(Prefix) << ": ";
+ if (BD.runPasses(BD.getProgram(), Prefix, PrefixOutput))
+ return KeepPrefix;
+
+ OrigProgram = std::move(BD.Program);
+
+ BD.Program = parseInputFile(PrefixOutput, BD.getContext());
+ if (BD.Program == nullptr) {
+ errs() << BD.getToolName() << ": Error reading bitcode file '"
+ << PrefixOutput << "'!\n";
+ exit(1);
+ }
+ sys::fs::remove(PrefixOutput);
+ }
+
+ outs() << "Checking to see if these passes crash: " << getPassesString(Suffix)
+ << ": ";
+
+ if (BD.runPasses(BD.getProgram(), Suffix))
+ return KeepSuffix; // The suffix crashes alone...
+
+ // Nothing failed, restore state...
+ if (OrigProgram)
+ BD.Program = std::move(OrigProgram);
+ return NoFailure;
+}
+
+using BugTester = bool (*)(const BugDriver &, Module *);
+
+namespace {
+/// ReduceCrashingGlobalInitializers - This works by removing global variable
+/// initializers and seeing if the program still crashes. If it does, then we
+/// keep that program and try again.
+class ReduceCrashingGlobalInitializers : public ListReducer<GlobalVariable *> {
+ BugDriver &BD;
+ BugTester TestFn;
+
+public:
+ ReduceCrashingGlobalInitializers(BugDriver &bd, BugTester testFn)
+ : BD(bd), TestFn(testFn) {}
+
+ Expected<TestResult> doTest(std::vector<GlobalVariable *> &Prefix,
+ std::vector<GlobalVariable *> &Kept) override {
+ if (!Kept.empty() && TestGlobalVariables(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestGlobalVariables(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestGlobalVariables(std::vector<GlobalVariable *> &GVs);
+};
+}
+
+bool ReduceCrashingGlobalInitializers::TestGlobalVariables(
+ std::vector<GlobalVariable *> &GVs) {
+ // Clone the program to try hacking it apart...
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
+
+ // Convert list to set for fast lookup...
+ std::set<GlobalVariable *> GVSet;
+
+ for (unsigned i = 0, e = GVs.size(); i != e; ++i) {
+ GlobalVariable *CMGV = cast<GlobalVariable>(VMap[GVs[i]]);
+ assert(CMGV && "Global Variable not in module?!");
+ GVSet.insert(CMGV);
+ }
+
+ outs() << "Checking for crash with only these global variables: ";
+ PrintGlobalVariableList(GVs);
+ outs() << ": ";
+
+ // Loop over and delete any global variables which we aren't supposed to be
+ // playing with...
+ for (GlobalVariable &I : M->globals())
+ if (I.hasInitializer() && !GVSet.count(&I)) {
+ DeleteGlobalInitializer(&I);
+ I.setLinkage(GlobalValue::ExternalLinkage);
+ I.setComdat(nullptr);
+ }
+
+ // Try running the hacked up program...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
+
+ // Make sure to use global variable pointers that point into the now-current
+ // module.
+ GVs.assign(GVSet.begin(), GVSet.end());
+ return true;
+ }
+
+ return false;
+}
+
+namespace {
+/// ReduceCrashingFunctions reducer - This works by removing functions and
+/// seeing if the program still crashes. If it does, then keep the newer,
+/// smaller program.
+///
+class ReduceCrashingFunctions : public ListReducer<Function *> {
+ BugDriver &BD;
+ BugTester TestFn;
+
+public:
+ ReduceCrashingFunctions(BugDriver &bd, BugTester testFn)
+ : BD(bd), TestFn(testFn) {}
+
+ Expected<TestResult> doTest(std::vector<Function *> &Prefix,
+ std::vector<Function *> &Kept) override {
+ if (!Kept.empty() && TestFuncs(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestFuncs(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestFuncs(std::vector<Function *> &Prefix);
+};
+}
+
+static void RemoveFunctionReferences(Module *M, const char *Name) {
+ auto *UsedVar = M->getGlobalVariable(Name, true);
+ if (!UsedVar || !UsedVar->hasInitializer())
+ return;
+ if (isa<ConstantAggregateZero>(UsedVar->getInitializer())) {
+ assert(UsedVar->use_empty());
+ UsedVar->eraseFromParent();
+ return;
+ }
+ auto *OldUsedVal = cast<ConstantArray>(UsedVar->getInitializer());
+ std::vector<Constant *> Used;
+ for (Value *V : OldUsedVal->operand_values()) {
+ Constant *Op = cast<Constant>(V->stripPointerCasts());
+ if (!Op->isNullValue()) {
+ Used.push_back(cast<Constant>(V));
+ }
+ }
+ auto *NewValElemTy = OldUsedVal->getType()->getElementType();
+ auto *NewValTy = ArrayType::get(NewValElemTy, Used.size());
+ auto *NewUsedVal = ConstantArray::get(NewValTy, Used);
+ UsedVar->mutateType(NewUsedVal->getType()->getPointerTo());
+ UsedVar->setInitializer(NewUsedVal);
+}
+
+bool ReduceCrashingFunctions::TestFuncs(std::vector<Function *> &Funcs) {
+ // If main isn't present, claim there is no problem.
+ if (KeepMain && !is_contained(Funcs, BD.getProgram().getFunction("main")))
+ return false;
+
+ // Clone the program to try hacking it apart...
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
+
+ // Convert list to set for fast lookup...
+ std::set<Function *> Functions;
+ for (unsigned i = 0, e = Funcs.size(); i != e; ++i) {
+ Function *CMF = cast<Function>(VMap[Funcs[i]]);
+ assert(CMF && "Function not in module?!");
+ assert(CMF->getFunctionType() == Funcs[i]->getFunctionType() && "wrong ty");
+ assert(CMF->getName() == Funcs[i]->getName() && "wrong name");
+ Functions.insert(CMF);
+ }
+
+ outs() << "Checking for crash with only these functions: ";
+ PrintFunctionList(Funcs);
+ outs() << ": ";
+ if (!ReplaceFuncsWithNull) {
+ // Loop over and delete any functions which we aren't supposed to be playing
+ // with...
+ for (Function &I : *M)
+ if (!I.isDeclaration() && !Functions.count(&I))
+ DeleteFunctionBody(&I);
+ } else {
+ std::vector<GlobalValue *> ToRemove;
+ // First, remove aliases to functions we're about to purge.
+ for (GlobalAlias &Alias : M->aliases()) {
+ GlobalObject *Root = Alias.getAliaseeObject();
+ Function *F = dyn_cast_or_null<Function>(Root);
+ if (F) {
+ if (Functions.count(F))
+ // We're keeping this function.
+ continue;
+ } else if (Root->isNullValue()) {
+ // This referenced a globalalias that we've already replaced,
+ // so we still need to replace this alias.
+ } else if (!F) {
+ // Not a function, therefore not something we mess with.
+ continue;
+ }
+
+ PointerType *Ty = cast<PointerType>(Alias.getType());
+ Constant *Replacement = ConstantPointerNull::get(Ty);
+ Alias.replaceAllUsesWith(Replacement);
+ ToRemove.push_back(&Alias);
+ }
+
+ for (Function &I : *M) {
+ if (!I.isDeclaration() && !Functions.count(&I)) {
+ PointerType *Ty = cast<PointerType>(I.getType());
+ Constant *Replacement = ConstantPointerNull::get(Ty);
+ I.replaceAllUsesWith(Replacement);
+ ToRemove.push_back(&I);
+ }
+ }
+
+ for (auto *F : ToRemove) {
+ F->eraseFromParent();
+ }
+
+ // Finally, remove any null members from any global intrinsic.
+ RemoveFunctionReferences(M.get(), "llvm.used");
+ RemoveFunctionReferences(M.get(), "llvm.compiler.used");
+ }
+ // Try running the hacked up program...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
+
+ // Make sure to use function pointers that point into the now-current
+ // module.
+ Funcs.assign(Functions.begin(), Functions.end());
+ return true;
+ }
+ return false;
+}
+
+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(M->getContext());
+ for (auto A : Attrs)
+ AB.addAttribute(A);
+ AttributeList NewAttrs;
+ NewAttrs = NewAttrs.addFnAttributes(BD.getContext(), AB);
+
+ // Set this new list of attributes on the function.
+ F->setAttributes(NewAttrs);
+
+ // If the attribute list includes "optnone" we need to make sure it also
+ // includes "noinline" otherwise we will get a verifier failure.
+ if (F->hasFnAttribute(Attribute::OptimizeNone))
+ F->addFnAttr(Attribute::NoInline);
+
+ // 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.getFnAttrs()) {
+ 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
+/// certain undefined behavior cuts off the program at the legs".
+void simpleSimplifyCfg(Function &F, SmallVectorImpl<BasicBlock *> &BBs) {
+ if (F.empty())
+ return;
+
+ for (auto *BB : BBs) {
+ ConstantFoldTerminator(BB);
+ MergeBlockIntoPredecessor(BB);
+ }
+
+ // Remove unreachable blocks
+ // removeUnreachableBlocks can't be used here, it will turn various
+ // undefined behavior into unreachables, but bugpoint was the thing that
+ // generated the undefined behavior, and we don't want it to kill the entire
+ // program.
+ SmallPtrSet<BasicBlock *, 16> Visited;
+ for (auto *BB : depth_first(&F.getEntryBlock()))
+ Visited.insert(BB);
+
+ SmallVector<BasicBlock *, 16> Unreachable;
+ for (auto &BB : F)
+ if (!Visited.count(&BB))
+ Unreachable.push_back(&BB);
+
+ // The dead BB's may be in a dead cycle or otherwise have references to each
+ // other. Because of this, we have to drop all references first, then delete
+ // them all at once.
+ for (auto *BB : Unreachable) {
+ for (BasicBlock *Successor : successors(&*BB))
+ if (Visited.count(Successor))
+ Successor->removePredecessor(&*BB);
+ BB->dropAllReferences();
+ }
+ for (auto *BB : Unreachable)
+ BB->eraseFromParent();
+}
+/// ReduceCrashingBlocks reducer - This works by setting the terminators of
+/// all terminators except the specified basic blocks to a 'ret' instruction,
+/// then running the simplifycfg pass. This has the effect of chopping up
+/// the CFG really fast which can reduce large functions quickly.
+///
+class ReduceCrashingBlocks : public ListReducer<const BasicBlock *> {
+ BugDriver &BD;
+ BugTester TestFn;
+
+public:
+ ReduceCrashingBlocks(BugDriver &BD, BugTester testFn)
+ : BD(BD), TestFn(testFn) {}
+
+ Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix,
+ std::vector<const BasicBlock *> &Kept) override {
+ if (!Kept.empty() && TestBlocks(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestBlocks(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestBlocks(std::vector<const BasicBlock *> &Prefix);
+};
+}
+
+bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock *> &BBs) {
+ // Clone the program to try hacking it apart...
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
+
+ // Convert list to set for fast lookup...
+ SmallPtrSet<BasicBlock *, 8> Blocks;
+ for (unsigned i = 0, e = BBs.size(); i != e; ++i)
+ Blocks.insert(cast<BasicBlock>(VMap[BBs[i]]));
+
+ outs() << "Checking for crash with only these blocks:";
+ unsigned NumPrint = Blocks.size();
+ if (NumPrint > 10)
+ NumPrint = 10;
+ for (unsigned i = 0, e = NumPrint; i != e; ++i)
+ outs() << " " << BBs[i]->getName();
+ if (NumPrint < Blocks.size())
+ outs() << "... <" << Blocks.size() << " total>";
+ outs() << ": ";
+
+ // Loop over and delete any hack up any blocks that are not listed...
+ for (Function &F : M->functions()) {
+ for (BasicBlock &BB : F) {
+ if (!Blocks.count(&BB) && BB.getTerminator()->getNumSuccessors()) {
+ // Loop over all of the successors of this block, deleting any PHI nodes
+ // that might include it.
+ for (BasicBlock *Succ : successors(&BB))
+ Succ->removePredecessor(&BB);
+
+ Instruction *BBTerm = BB.getTerminator();
+ if (BBTerm->isEHPad() || BBTerm->getType()->isTokenTy())
+ continue;
+ if (!BBTerm->getType()->isVoidTy())
+ BBTerm->replaceAllUsesWith(Constant::getNullValue(BBTerm->getType()));
+
+ // Replace the old terminator instruction.
+ BB.getInstList().pop_back();
+ new UnreachableInst(BB.getContext(), &BB);
+ }
+ }
+ }
+
+ // The CFG Simplifier pass may delete one of the basic blocks we are
+ // interested in. If it does we need to take the block out of the list. Make
+ // a "persistent mapping" by turning basic blocks into <function, name> pairs.
+ // This won't work well if blocks are unnamed, but that is just the risk we
+ // have to take. FIXME: Can we just name the blocks?
+ std::vector<std::pair<std::string, std::string>> BlockInfo;
+
+ for (BasicBlock *BB : Blocks)
+ BlockInfo.emplace_back(std::string(BB->getParent()->getName()),
+ std::string(BB->getName()));
+
+ SmallVector<BasicBlock *, 16> ToProcess;
+ for (auto &F : *M) {
+ for (auto &BB : F)
+ if (!Blocks.count(&BB))
+ ToProcess.push_back(&BB);
+ simpleSimplifyCfg(F, ToProcess);
+ ToProcess.clear();
+ }
+ // Verify we didn't break anything
+ std::vector<std::string> Passes;
+ Passes.push_back("verify");
+ std::unique_ptr<Module> New = BD.runPassesOn(M.get(), Passes);
+ if (!New) {
+ errs() << "verify failed!\n";
+ exit(1);
+ }
+ M = std::move(New);
+
+ // Try running on the hacked up program...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
+
+ // Make sure to use basic block pointers that point into the now-current
+ // module, and that they don't include any deleted blocks.
+ BBs.clear();
+ const ValueSymbolTable &GST = BD.getProgram().getValueSymbolTable();
+ for (const auto &BI : BlockInfo) {
+ Function *F = cast<Function>(GST.lookup(BI.first));
+ Value *V = F->getValueSymbolTable()->lookup(BI.second);
+ if (V && V->getType() == Type::getLabelTy(V->getContext()))
+ BBs.push_back(cast<BasicBlock>(V));
+ }
+ return true;
+ }
+ // It didn't crash, try something else.
+ return false;
+}
+
+namespace {
+/// ReduceCrashingConditionals reducer - This works by changing
+/// conditional branches to unconditional ones, then simplifying the CFG
+/// This has the effect of chopping up the CFG really fast which can reduce
+/// large functions quickly.
+///
+class ReduceCrashingConditionals : public ListReducer<const BasicBlock *> {
+ BugDriver &BD;
+ BugTester TestFn;
+ bool Direction;
+
+public:
+ ReduceCrashingConditionals(BugDriver &bd, BugTester testFn, bool Direction)
+ : BD(bd), TestFn(testFn), Direction(Direction) {}
+
+ Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix,
+ std::vector<const BasicBlock *> &Kept) override {
+ if (!Kept.empty() && TestBlocks(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestBlocks(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestBlocks(std::vector<const BasicBlock *> &Prefix);
+};
+}
+
+bool ReduceCrashingConditionals::TestBlocks(
+ std::vector<const BasicBlock *> &BBs) {
+ // Clone the program to try hacking it apart...
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
+
+ // Convert list to set for fast lookup...
+ SmallPtrSet<const BasicBlock *, 8> Blocks;
+ for (const auto *BB : BBs)
+ Blocks.insert(cast<BasicBlock>(VMap[BB]));
+
+ outs() << "Checking for crash with changing conditionals to always jump to "
+ << (Direction ? "true" : "false") << ":";
+ unsigned NumPrint = Blocks.size();
+ if (NumPrint > 10)
+ NumPrint = 10;
+ for (unsigned i = 0, e = NumPrint; i != e; ++i)
+ outs() << " " << BBs[i]->getName();
+ if (NumPrint < Blocks.size())
+ outs() << "... <" << Blocks.size() << " total>";
+ outs() << ": ";
+
+ // Loop over and delete any hack up any blocks that are not listed...
+ for (auto &F : *M)
+ for (auto &BB : F)
+ if (!Blocks.count(&BB)) {
+ auto *BR = dyn_cast<BranchInst>(BB.getTerminator());
+ if (!BR || !BR->isConditional())
+ continue;
+ if (Direction)
+ BR->setCondition(ConstantInt::getTrue(BR->getContext()));
+ else
+ BR->setCondition(ConstantInt::getFalse(BR->getContext()));
+ }
+
+ // The following may destroy some blocks, so we save them first
+ std::vector<std::pair<std::string, std::string>> BlockInfo;
+
+ for (const BasicBlock *BB : Blocks)
+ BlockInfo.emplace_back(std::string(BB->getParent()->getName()),
+ std::string(BB->getName()));
+
+ SmallVector<BasicBlock *, 16> ToProcess;
+ for (auto &F : *M) {
+ for (auto &BB : F)
+ if (!Blocks.count(&BB))
+ ToProcess.push_back(&BB);
+ simpleSimplifyCfg(F, ToProcess);
+ ToProcess.clear();
+ }
+ // Verify we didn't break anything
+ std::vector<std::string> Passes;
+ Passes.push_back("verify");
+ std::unique_ptr<Module> New = BD.runPassesOn(M.get(), Passes);
+ if (!New) {
+ errs() << "verify failed!\n";
+ exit(1);
+ }
+ M = std::move(New);
+
+ // Try running on the hacked up program...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
+
+ // Make sure to use basic block pointers that point into the now-current
+ // module, and that they don't include any deleted blocks.
+ BBs.clear();
+ const ValueSymbolTable &GST = BD.getProgram().getValueSymbolTable();
+ for (auto &BI : BlockInfo) {
+ auto *F = cast<Function>(GST.lookup(BI.first));
+ Value *V = F->getValueSymbolTable()->lookup(BI.second);
+ if (V && V->getType() == Type::getLabelTy(V->getContext()))
+ BBs.push_back(cast<BasicBlock>(V));
+ }
+ return true;
+ }
+ // It didn't crash, try something else.
+ return false;
+}
+
+namespace {
+/// SimplifyCFG reducer - This works by calling SimplifyCFG on each basic block
+/// in the program.
+
+class ReduceSimplifyCFG : public ListReducer<const BasicBlock *> {
+ BugDriver &BD;
+ BugTester TestFn;
+ TargetTransformInfo TTI;
+
+public:
+ ReduceSimplifyCFG(BugDriver &bd, BugTester testFn)
+ : BD(bd), TestFn(testFn), TTI(bd.getProgram().getDataLayout()) {}
+
+ Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix,
+ std::vector<const BasicBlock *> &Kept) override {
+ if (!Kept.empty() && TestBlocks(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestBlocks(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestBlocks(std::vector<const BasicBlock *> &Prefix);
+};
+}
+
+bool ReduceSimplifyCFG::TestBlocks(std::vector<const BasicBlock *> &BBs) {
+ // Clone the program to try hacking it apart...
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
+
+ // Convert list to set for fast lookup...
+ SmallPtrSet<const BasicBlock *, 8> Blocks;
+ for (const auto *BB : BBs)
+ Blocks.insert(cast<BasicBlock>(VMap[BB]));
+
+ outs() << "Checking for crash with CFG simplifying:";
+ unsigned NumPrint = Blocks.size();
+ if (NumPrint > 10)
+ NumPrint = 10;
+ for (unsigned i = 0, e = NumPrint; i != e; ++i)
+ outs() << " " << BBs[i]->getName();
+ if (NumPrint < Blocks.size())
+ outs() << "... <" << Blocks.size() << " total>";
+ outs() << ": ";
+
+ // The following may destroy some blocks, so we save them first
+ std::vector<std::pair<std::string, std::string>> BlockInfo;
+
+ for (const BasicBlock *BB : Blocks)
+ BlockInfo.emplace_back(std::string(BB->getParent()->getName()),
+ std::string(BB->getName()));
+
+ // Loop over and delete any hack up any blocks that are not listed...
+ for (auto &F : *M)
+ // Loop over all of the basic blocks and remove them if they are unneeded.
+ for (Function::iterator BBIt = F.begin(); BBIt != F.end();) {
+ if (!Blocks.count(&*BBIt)) {
+ ++BBIt;
+ continue;
+ }
+ simplifyCFG(&*BBIt++, TTI);
+ }
+ // Verify we didn't break anything
+ std::vector<std::string> Passes;
+ Passes.push_back("verify");
+ std::unique_ptr<Module> New = BD.runPassesOn(M.get(), Passes);
+ if (!New) {
+ errs() << "verify failed!\n";
+ exit(1);
+ }
+ M = std::move(New);
+
+ // Try running on the hacked up program...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
+
+ // Make sure to use basic block pointers that point into the now-current
+ // module, and that they don't include any deleted blocks.
+ BBs.clear();
+ const ValueSymbolTable &GST = BD.getProgram().getValueSymbolTable();
+ for (auto &BI : BlockInfo) {
+ auto *F = cast<Function>(GST.lookup(BI.first));
+ Value *V = F->getValueSymbolTable()->lookup(BI.second);
+ if (V && V->getType() == Type::getLabelTy(V->getContext()))
+ BBs.push_back(cast<BasicBlock>(V));
+ }
+ return true;
+ }
+ // It didn't crash, try something else.
+ return false;
+}
+
+namespace {
+/// ReduceCrashingInstructions reducer - This works by removing the specified
+/// non-terminator instructions and replacing them with undef.
+///
+class ReduceCrashingInstructions : public ListReducer<const Instruction *> {
+ BugDriver &BD;
+ BugTester TestFn;
+
+public:
+ ReduceCrashingInstructions(BugDriver &bd, BugTester testFn)
+ : BD(bd), TestFn(testFn) {}
+
+ Expected<TestResult> doTest(std::vector<const Instruction *> &Prefix,
+ std::vector<const Instruction *> &Kept) override {
+ if (!Kept.empty() && TestInsts(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestInsts(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestInsts(std::vector<const Instruction *> &Prefix);
+};
+}
+
+bool ReduceCrashingInstructions::TestInsts(
+ std::vector<const Instruction *> &Insts) {
+ // Clone the program to try hacking it apart...
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
+
+ // Convert list to set for fast lookup...
+ SmallPtrSet<Instruction *, 32> Instructions;
+ for (unsigned i = 0, e = Insts.size(); i != e; ++i) {
+ assert(!Insts[i]->isTerminator());
+ Instructions.insert(cast<Instruction>(VMap[Insts[i]]));
+ }
+
+ outs() << "Checking for crash with only " << Instructions.size();
+ if (Instructions.size() == 1)
+ outs() << " instruction: ";
+ else
+ outs() << " instructions: ";
+
+ for (Module::iterator MI = M->begin(), ME = M->end(); MI != ME; ++MI)
+ for (Function::iterator FI = MI->begin(), FE = MI->end(); FI != FE; ++FI)
+ for (Instruction &Inst : llvm::make_early_inc_range(*FI)) {
+ if (!Instructions.count(&Inst) && !Inst.isTerminator() &&
+ !Inst.isEHPad() && !Inst.getType()->isTokenTy() &&
+ !Inst.isSwiftError()) {
+ if (!Inst.getType()->isVoidTy())
+ Inst.replaceAllUsesWith(UndefValue::get(Inst.getType()));
+ Inst.eraseFromParent();
+ }
+ }
+
+ // Verify that this is still valid.
+ legacy::PassManager Passes;
+ Passes.add(createVerifierPass(/*FatalErrors=*/false));
+ Passes.run(*M);
+
+ // Try running on the hacked up program...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
+
+ // Make sure to use instruction pointers that point into the now-current
+ // module, and that they don't include any deleted blocks.
+ Insts.clear();
+ for (Instruction *Inst : Instructions)
+ Insts.push_back(Inst);
+ return true;
+ }
+ // It didn't crash, try something else.
+ return false;
+}
+
+namespace {
+/// ReduceCrashingMetadata reducer - This works by removing all metadata from
+/// the specified instructions.
+///
+class ReduceCrashingMetadata : public ListReducer<Instruction *> {
+ BugDriver &BD;
+ BugTester TestFn;
+
+public:
+ ReduceCrashingMetadata(BugDriver &bd, BugTester testFn)
+ : BD(bd), TestFn(testFn) {}
+
+ Expected<TestResult> doTest(std::vector<Instruction *> &Prefix,
+ std::vector<Instruction *> &Kept) override {
+ if (!Kept.empty() && TestInsts(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestInsts(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestInsts(std::vector<Instruction *> &Prefix);
+};
+} // namespace
+
+bool ReduceCrashingMetadata::TestInsts(std::vector<Instruction *> &Insts) {
+ // Clone the program to try hacking it apart...
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
+
+ // Convert list to set for fast lookup...
+ SmallPtrSet<Instruction *, 32> Instructions;
+ for (Instruction *I : Insts)
+ Instructions.insert(cast<Instruction>(VMap[I]));
+
+ outs() << "Checking for crash with metadata retained from "
+ << Instructions.size();
+ if (Instructions.size() == 1)
+ outs() << " instruction: ";
+ else
+ outs() << " instructions: ";
+
+ // Try to drop instruction metadata from all instructions, except the ones
+ // selected in Instructions.
+ for (Function &F : *M)
+ for (Instruction &Inst : instructions(F)) {
+ if (!Instructions.count(&Inst)) {
+ Inst.dropUnknownNonDebugMetadata();
+ Inst.setDebugLoc({});
+ }
+ }
+
+ // Verify that this is still valid.
+ legacy::PassManager Passes;
+ Passes.add(createVerifierPass(/*FatalErrors=*/false));
+ Passes.run(*M);
+
+ // Try running on the hacked up program...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
+
+ // Make sure to use instruction pointers that point into the now-current
+ // module, and that they don't include any deleted blocks.
+ Insts.clear();
+ for (Instruction *I : Instructions)
+ Insts.push_back(I);
+ return true;
+ }
+ // It didn't crash, try something else.
+ return false;
+}
+
+namespace {
+// Reduce the list of Named Metadata nodes. We keep this as a list of
+// names to avoid having to convert back and forth every time.
+class ReduceCrashingNamedMD : public ListReducer<std::string> {
+ BugDriver &BD;
+ BugTester TestFn;
+
+public:
+ ReduceCrashingNamedMD(BugDriver &bd, BugTester testFn)
+ : BD(bd), TestFn(testFn) {}
+
+ Expected<TestResult> doTest(std::vector<std::string> &Prefix,
+ std::vector<std::string> &Kept) override {
+ if (!Kept.empty() && TestNamedMDs(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestNamedMDs(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestNamedMDs(std::vector<std::string> &NamedMDs);
+};
+}
+
+bool ReduceCrashingNamedMD::TestNamedMDs(std::vector<std::string> &NamedMDs) {
+
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
+
+ outs() << "Checking for crash with only these named metadata nodes:";
+ unsigned NumPrint = std::min<size_t>(NamedMDs.size(), 10);
+ for (unsigned i = 0, e = NumPrint; i != e; ++i)
+ outs() << " " << NamedMDs[i];
+ if (NumPrint < NamedMDs.size())
+ outs() << "... <" << NamedMDs.size() << " total>";
+ outs() << ": ";
+
+ // Make a StringMap for faster lookup
+ StringSet<> Names;
+ for (const std::string &Name : NamedMDs)
+ Names.insert(Name);
+
+ // First collect all the metadata to delete in a vector, then
+ // delete them all at once to avoid invalidating the iterator
+ std::vector<NamedMDNode *> ToDelete;
+ ToDelete.reserve(M->named_metadata_size() - Names.size());
+ for (auto &NamedMD : M->named_metadata())
+ // Always keep a nonempty llvm.dbg.cu because the Verifier would complain.
+ if (!Names.count(NamedMD.getName()) &&
+ (!(NamedMD.getName() == "llvm.dbg.cu" && NamedMD.getNumOperands() > 0)))
+ ToDelete.push_back(&NamedMD);
+
+ for (auto *NamedMD : ToDelete)
+ NamedMD->eraseFromParent();
+
+ // Verify that this is still valid.
+ legacy::PassManager Passes;
+ Passes.add(createVerifierPass(/*FatalErrors=*/false));
+ Passes.run(*M);
+
+ // Try running on the hacked up program...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
+ return true;
+ }
+ return false;
+}
+
+namespace {
+// Reduce the list of operands to named metadata nodes
+class ReduceCrashingNamedMDOps : public ListReducer<const MDNode *> {
+ BugDriver &BD;
+ BugTester TestFn;
+
+public:
+ ReduceCrashingNamedMDOps(BugDriver &bd, BugTester testFn)
+ : BD(bd), TestFn(testFn) {}
+
+ Expected<TestResult> doTest(std::vector<const MDNode *> &Prefix,
+ std::vector<const MDNode *> &Kept) override {
+ if (!Kept.empty() && TestNamedMDOps(Kept))
+ return KeepSuffix;
+ if (!Prefix.empty() && TestNamedMDOps(Prefix))
+ return KeepPrefix;
+ return NoFailure;
+ }
+
+ bool TestNamedMDOps(std::vector<const MDNode *> &NamedMDOps);
+};
+}
+
+bool ReduceCrashingNamedMDOps::TestNamedMDOps(
+ std::vector<const MDNode *> &NamedMDOps) {
+ // Convert list to set for fast lookup...
+ SmallPtrSet<const MDNode *, 32> OldMDNodeOps;
+ for (unsigned i = 0, e = NamedMDOps.size(); i != e; ++i) {
+ OldMDNodeOps.insert(NamedMDOps[i]);
+ }
+
+ outs() << "Checking for crash with only " << OldMDNodeOps.size();
+ if (OldMDNodeOps.size() == 1)
+ outs() << " named metadata operand: ";
+ else
+ outs() << " named metadata operands: ";
+
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
+
+ // This is a little wasteful. In the future it might be good if we could have
+ // these dropped during cloning.
+ for (auto &NamedMD : BD.getProgram().named_metadata()) {
+ // Drop the old one and create a new one
+ M->eraseNamedMetadata(M->getNamedMetadata(NamedMD.getName()));
+ NamedMDNode *NewNamedMDNode =
+ M->getOrInsertNamedMetadata(NamedMD.getName());
+ for (MDNode *op : NamedMD.operands())
+ if (OldMDNodeOps.count(op))
+ NewNamedMDNode->addOperand(cast<MDNode>(MapMetadata(op, VMap)));
+ }
+
+ // Verify that this is still valid.
+ legacy::PassManager Passes;
+ Passes.add(createVerifierPass(/*FatalErrors=*/false));
+ Passes.run(*M);
+
+ // Try running on the hacked up program...
+ if (TestFn(BD, M.get())) {
+ // Make sure to use instruction pointers that point into the now-current
+ // module, and that they don't include any deleted blocks.
+ NamedMDOps.clear();
+ for (const MDNode *Node : OldMDNodeOps)
+ NamedMDOps.push_back(cast<MDNode>(*VMap.getMappedMD(Node)));
+
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
+ return true;
+ }
+ // It didn't crash, try something else.
+ return false;
+}
+
+/// Attempt to eliminate as many global initializers as possible.
+static Error ReduceGlobalInitializers(BugDriver &BD, BugTester TestFn) {
+ Module &OrigM = BD.getProgram();
+ if (OrigM.global_empty())
+ return Error::success();
+
+ // Now try to reduce the number of global variable initializers in the
+ // module to something small.
+ std::unique_ptr<Module> M = CloneModule(OrigM);
+ bool DeletedInit = false;
+
+ for (GlobalVariable &GV : M->globals()) {
+ if (GV.hasInitializer()) {
+ DeleteGlobalInitializer(&GV);
+ GV.setLinkage(GlobalValue::ExternalLinkage);
+ GV.setComdat(nullptr);
+ DeletedInit = true;
+ }
+ }
+
+ if (!DeletedInit)
+ return Error::success();
+
+ // See if the program still causes a crash...
+ outs() << "\nChecking to see if we can delete global inits: ";
+
+ if (TestFn(BD, M.get())) { // Still crashes?
+ BD.setNewProgram(std::move(M));
+ outs() << "\n*** Able to remove all global initializers!\n";
+ return Error::success();
+ }
+
+ // No longer crashes.
+ outs() << " - Removing all global inits hides problem!\n";
+
+ std::vector<GlobalVariable *> GVs;
+ for (GlobalVariable &GV : OrigM.globals())
+ if (GV.hasInitializer())
+ GVs.push_back(&GV);
+
+ if (GVs.size() > 1 && !BugpointIsInterrupted) {
+ outs() << "\n*** Attempting to reduce the number of global initializers "
+ << "in the testcase\n";
+
+ unsigned OldSize = GVs.size();
+ Expected<bool> Result =
+ ReduceCrashingGlobalInitializers(BD, TestFn).reduceList(GVs);
+ if (Error E = Result.takeError())
+ return E;
+
+ if (GVs.size() < OldSize)
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-global-variables");
+ }
+ return Error::success();
+}
+
+static Error ReduceInsts(BugDriver &BD, BugTester TestFn) {
+ // Attempt to delete instructions using bisection. This should help out nasty
+ // cases with large basic blocks where the problem is at one end.
+ if (!BugpointIsInterrupted) {
+ std::vector<const Instruction *> Insts;
+ for (const Function &F : BD.getProgram())
+ for (const BasicBlock &BB : F)
+ for (const Instruction &I : BB)
+ if (!I.isTerminator())
+ Insts.push_back(&I);
+
+ Expected<bool> Result =
+ ReduceCrashingInstructions(BD, TestFn).reduceList(Insts);
+ if (Error E = Result.takeError())
+ return E;
+ }
+
+ unsigned Simplification = 2;
+ do {
+ if (BugpointIsInterrupted)
+ // TODO: Should we distinguish this with an "interrupted error"?
+ return Error::success();
+ --Simplification;
+ outs() << "\n*** Attempting to reduce testcase by deleting instruc"
+ << "tions: Simplification Level #" << Simplification << '\n';
+
+ // Now that we have deleted the functions that are unnecessary for the
+ // program, try to remove instructions that are not necessary to cause the
+ // crash. To do this, we loop through all of the instructions in the
+ // remaining functions, deleting them (replacing any values produced with
+ // nulls), and then running ADCE and SimplifyCFG. If the transformed input
+ // still triggers failure, keep deleting until we cannot trigger failure
+ // anymore.
+ //
+ unsigned InstructionsToSkipBeforeDeleting = 0;
+ TryAgain:
+
+ // Loop over all of the (non-terminator) instructions remaining in the
+ // function, attempting to delete them.
+ unsigned CurInstructionNum = 0;
+ for (Module::const_iterator FI = BD.getProgram().begin(),
+ E = BD.getProgram().end();
+ FI != E; ++FI)
+ if (!FI->isDeclaration())
+ for (Function::const_iterator BI = FI->begin(), E = FI->end(); BI != E;
+ ++BI)
+ for (BasicBlock::const_iterator I = BI->begin(), E = --BI->end();
+ I != E; ++I, ++CurInstructionNum) {
+ if (InstructionsToSkipBeforeDeleting) {
+ --InstructionsToSkipBeforeDeleting;
+ } else {
+ if (BugpointIsInterrupted)
+ // TODO: Should this be some kind of interrupted error?
+ return Error::success();
+
+ if (I->isEHPad() || I->getType()->isTokenTy() ||
+ I->isSwiftError())
+ continue;
+
+ outs() << "Checking instruction: " << *I;
+ std::unique_ptr<Module> M =
+ BD.deleteInstructionFromProgram(&*I, Simplification);
+
+ // Find out if the pass still crashes on this pass...
+ if (TestFn(BD, M.get())) {
+ // Yup, it does, we delete the old module, and continue trying
+ // to reduce the testcase...
+ BD.setNewProgram(std::move(M));
+ InstructionsToSkipBeforeDeleting = CurInstructionNum;
+ goto TryAgain; // I wish I had a multi-level break here!
+ }
+ }
+ }
+
+ if (InstructionsToSkipBeforeDeleting) {
+ InstructionsToSkipBeforeDeleting = 0;
+ goto TryAgain;
+ }
+
+ } while (Simplification);
+
+ // Attempt to drop metadata from instructions that does not contribute to the
+ // crash.
+ if (!BugpointIsInterrupted) {
+ std::vector<Instruction *> Insts;
+ for (Function &F : BD.getProgram())
+ for (Instruction &I : instructions(F))
+ Insts.push_back(&I);
+
+ Expected<bool> Result =
+ ReduceCrashingMetadata(BD, TestFn).reduceList(Insts);
+ if (Error E = Result.takeError())
+ return E;
+ }
+
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-instructions");
+ return Error::success();
+}
+
+/// DebugACrash - Given a predicate that determines whether a component crashes
+/// on a program, try to destructively reduce the program while still keeping
+/// the predicate true.
+static Error DebugACrash(BugDriver &BD, BugTester TestFn) {
+ // See if we can get away with nuking some of the global variable initializers
+ // in the program...
+ if (!NoGlobalRM)
+ if (Error E = ReduceGlobalInitializers(BD, TestFn))
+ return E;
+
+ // Now try to reduce the number of functions in the module to something small.
+ std::vector<Function *> Functions;
+ for (Function &F : BD.getProgram())
+ if (!F.isDeclaration())
+ Functions.push_back(&F);
+
+ if (Functions.size() > 1 && !BugpointIsInterrupted) {
+ outs() << "\n*** Attempting to reduce the number of functions "
+ "in the testcase\n";
+
+ unsigned OldSize = Functions.size();
+ Expected<bool> Result =
+ ReduceCrashingFunctions(BD, TestFn).reduceList(Functions);
+ if (Error E = Result.takeError())
+ return E;
+
+ if (Functions.size() < OldSize)
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-function");
+ }
+
+ if (!NoAttributeRM) {
+ // For each remaining function, try to reduce that function's attributes.
+ std::vector<std::string> FunctionNames;
+ for (Function &F : BD.getProgram())
+ FunctionNames.push_back(std::string(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 function?");
+
+ std::vector<Attribute> Attrs;
+ for (Attribute A : Fn->getAttributes().getFnAttrs())
+ 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) {
+ std::vector<const BasicBlock *> Blocks;
+ for (Function &F : BD.getProgram())
+ for (BasicBlock &BB : F)
+ Blocks.push_back(&BB);
+ unsigned OldSize = Blocks.size();
+ Expected<bool> Result =
+ ReduceCrashingConditionals(BD, TestFn, true).reduceList(Blocks);
+ if (Error E = Result.takeError())
+ return E;
+ Result = ReduceCrashingConditionals(BD, TestFn, false).reduceList(Blocks);
+ if (Error E = Result.takeError())
+ return E;
+ if (Blocks.size() < OldSize)
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-conditionals");
+ }
+
+ // Attempt to delete entire basic blocks at a time to speed up
+ // convergence... this actually works by setting the terminator of the blocks
+ // to a return instruction then running simplifycfg, which can potentially
+ // shrinks the code dramatically quickly
+ //
+ if (!DisableSimplifyCFG && !BugpointIsInterrupted) {
+ std::vector<const BasicBlock *> Blocks;
+ for (Function &F : BD.getProgram())
+ for (BasicBlock &BB : F)
+ Blocks.push_back(&BB);
+ unsigned OldSize = Blocks.size();
+ Expected<bool> Result = ReduceCrashingBlocks(BD, TestFn).reduceList(Blocks);
+ if (Error E = Result.takeError())
+ return E;
+ if (Blocks.size() < OldSize)
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-blocks");
+ }
+
+ if (!DisableSimplifyCFG && !BugpointIsInterrupted) {
+ std::vector<const BasicBlock *> Blocks;
+ for (Function &F : BD.getProgram())
+ for (BasicBlock &BB : F)
+ Blocks.push_back(&BB);
+ unsigned OldSize = Blocks.size();
+ Expected<bool> Result = ReduceSimplifyCFG(BD, TestFn).reduceList(Blocks);
+ if (Error E = Result.takeError())
+ return E;
+ if (Blocks.size() < OldSize)
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplifycfg");
+ }
+
+ // Attempt to delete instructions using bisection. This should help out nasty
+ // cases with large basic blocks where the problem is at one end.
+ if (!BugpointIsInterrupted)
+ if (Error E = ReduceInsts(BD, TestFn))
+ return E;
+
+ // Attempt to strip debug info metadata.
+ auto stripMetadata = [&](std::function<bool(Module &)> strip) {
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram());
+ strip(*M);
+ if (TestFn(BD, M.get()))
+ BD.setNewProgram(std::move(M));
+ };
+ if (!NoStripDebugInfo && !BugpointIsInterrupted) {
+ outs() << "\n*** Attempting to strip the debug info: ";
+ stripMetadata(StripDebugInfo);
+ }
+ if (!NoStripDebugTypeInfo && !BugpointIsInterrupted) {
+ outs() << "\n*** Attempting to strip the debug type info: ";
+ stripMetadata(stripNonLineTableDebugInfo);
+ }
+
+ if (!NoNamedMDRM) {
+ if (!BugpointIsInterrupted) {
+ // Try to reduce the amount of global metadata (particularly debug info),
+ // by dropping global named metadata that anchors them
+ outs() << "\n*** Attempting to remove named metadata: ";
+ std::vector<std::string> NamedMDNames;
+ for (auto &NamedMD : BD.getProgram().named_metadata())
+ NamedMDNames.push_back(NamedMD.getName().str());
+ Expected<bool> Result =
+ ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames);
+ if (Error E = Result.takeError())
+ return E;
+ }
+
+ if (!BugpointIsInterrupted) {
+ // Now that we quickly dropped all the named metadata that doesn't
+ // contribute to the crash, bisect the operands of the remaining ones
+ std::vector<const MDNode *> NamedMDOps;
+ for (auto &NamedMD : BD.getProgram().named_metadata())
+ for (auto op : NamedMD.operands())
+ NamedMDOps.push_back(op);
+ Expected<bool> Result =
+ ReduceCrashingNamedMDOps(BD, TestFn).reduceList(NamedMDOps);
+ if (Error E = Result.takeError())
+ return E;
+ }
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-named-md");
+ }
+
+ // Try to clean up the testcase by running funcresolve and globaldce...
+ if (!BugpointIsInterrupted) {
+ outs() << "\n*** Attempting to perform final cleanups: ";
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram());
+ M = BD.performFinalCleanups(std::move(M), true);
+
+ // Find out if the pass still crashes on the cleaned up program...
+ if (M && TestFn(BD, M.get()))
+ BD.setNewProgram(
+ std::move(M)); // Yup, it does, keep the reduced version...
+ }
+
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplified");
+
+ return Error::success();
+}
+
+static bool TestForOptimizerCrash(const BugDriver &BD, Module *M) {
+ return BD.runPasses(*M, BD.getPassesToRun());
+}
+
+/// debugOptimizerCrash - This method is called when some pass crashes on input.
+/// It attempts to prune down the testcase to something reasonable, and figure
+/// out exactly which pass is crashing.
+///
+Error BugDriver::debugOptimizerCrash(const std::string &ID) {
+ outs() << "\n*** Debugging optimizer crash!\n";
+
+ // Reduce the list of passes which causes the optimizer to crash...
+ if (!BugpointIsInterrupted && !DontReducePassList) {
+ Expected<bool> Result = ReducePassList(*this).reduceList(PassesToRun);
+ if (Error E = Result.takeError())
+ return E;
+ }
+
+ outs() << "\n*** Found crashing pass"
+ << (PassesToRun.size() == 1 ? ": " : "es: ")
+ << getPassesString(PassesToRun) << '\n';
+
+ EmitProgressBitcode(*Program, ID);
+
+ auto Res = DebugACrash(*this, TestForOptimizerCrash);
+ if (Res || DontReducePassList)
+ return Res;
+ // Try to reduce the pass list again. This covers additional cases
+ // we failed to reduce earlier, because of more complex pass dependencies
+ // triggering the crash.
+ auto SecondRes = ReducePassList(*this).reduceList(PassesToRun);
+ if (Error E = SecondRes.takeError())
+ return E;
+ outs() << "\n*** Found crashing pass"
+ << (PassesToRun.size() == 1 ? ": " : "es: ")
+ << getPassesString(PassesToRun) << '\n';
+
+ EmitProgressBitcode(getProgram(), "reduced-simplified");
+ return Res;
+}
+
+static bool TestForCodeGenCrash(const BugDriver &BD, Module *M) {
+ if (Error E = BD.compileProgram(*M)) {
+ if (VerboseErrors)
+ errs() << toString(std::move(E)) << "\n";
+ else {
+ consumeError(std::move(E));
+ errs() << "<crash>\n";
+ }
+ return true; // Tool is still crashing.
+ }
+ errs() << '\n';
+ return false;
+}
+
+/// debugCodeGeneratorCrash - This method is called when the code generator
+/// crashes on an input. It attempts to reduce the input as much as possible
+/// while still causing the code generator to crash.
+Error BugDriver::debugCodeGeneratorCrash() {
+ errs() << "*** Debugging code generator crash!\n";
+
+ return DebugACrash(*this, TestForCodeGenCrash);
+}
diff --git a/contrib/libs/llvm14/tools/bugpoint/ExecutionDriver.cpp b/contrib/libs/llvm14/tools/bugpoint/ExecutionDriver.cpp
new file mode 100644
index 00000000000..f06f378962d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/ExecutionDriver.cpp
@@ -0,0 +1,457 @@
+//===- ExecutionDriver.cpp - Allow execution of LLVM program --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains code used to execute the program utilizing one of the
+// various ways of running LLVM bitcode.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BugDriver.h"
+#include "ToolRunner.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/SystemUtils.h"
+#include "llvm/Support/raw_ostream.h"
+#include <fstream>
+
+using namespace llvm;
+
+namespace {
+// OutputType - Allow the user to specify the way code should be run, to test
+// for miscompilation.
+//
+enum OutputType {
+ AutoPick,
+ RunLLI,
+ RunJIT,
+ RunLLC,
+ RunLLCIA,
+ CompileCustom,
+ Custom
+};
+
+cl::opt<double> AbsTolerance("abs-tolerance",
+ cl::desc("Absolute error tolerated"),
+ cl::init(0.0));
+cl::opt<double> RelTolerance("rel-tolerance",
+ cl::desc("Relative error tolerated"),
+ cl::init(0.0));
+
+cl::opt<OutputType> InterpreterSel(
+ cl::desc("Specify the \"test\" i.e. suspect back-end:"),
+ cl::values(clEnumValN(AutoPick, "auto", "Use best guess"),
+ clEnumValN(RunLLI, "run-int", "Execute with the interpreter"),
+ clEnumValN(RunJIT, "run-jit", "Execute with JIT"),
+ clEnumValN(RunLLC, "run-llc", "Compile with LLC"),
+ clEnumValN(RunLLCIA, "run-llc-ia",
+ "Compile with LLC with integrated assembler"),
+ clEnumValN(CompileCustom, "compile-custom",
+ "Use -compile-command to define a command to "
+ "compile the bitcode. Useful to avoid linking."),
+ clEnumValN(Custom, "run-custom",
+ "Use -exec-command to define a command to execute "
+ "the bitcode. Useful for cross-compilation.")),
+ cl::init(AutoPick));
+
+cl::opt<OutputType> SafeInterpreterSel(
+ cl::desc("Specify \"safe\" i.e. known-good backend:"),
+ cl::values(clEnumValN(AutoPick, "safe-auto", "Use best guess"),
+ clEnumValN(RunLLC, "safe-run-llc", "Compile with LLC"),
+ clEnumValN(Custom, "safe-run-custom",
+ "Use -exec-command to define a command to execute "
+ "the bitcode. Useful for cross-compilation.")),
+ cl::init(AutoPick));
+
+cl::opt<std::string> SafeInterpreterPath(
+ "safe-path", cl::desc("Specify the path to the \"safe\" backend program"),
+ cl::init(""));
+
+cl::opt<bool> AppendProgramExitCode(
+ "append-exit-code",
+ cl::desc("Append the exit code to the output so it gets diff'd too"),
+ cl::init(false));
+
+cl::opt<std::string>
+ InputFile("input", cl::init("/dev/null"),
+ cl::desc("Filename to pipe in as stdin (default: /dev/null)"));
+
+cl::list<std::string>
+ AdditionalSOs("additional-so", cl::desc("Additional shared objects to load "
+ "into executing programs"));
+
+cl::list<std::string> AdditionalLinkerArgs(
+ "Xlinker", cl::desc("Additional arguments to pass to the linker"));
+
+cl::opt<std::string> CustomCompileCommand(
+ "compile-command", cl::init("llc"),
+ cl::desc("Command to compile the bitcode (use with -compile-custom) "
+ "(default: llc)"));
+
+cl::opt<std::string> CustomExecCommand(
+ "exec-command", cl::init("simulate"),
+ cl::desc("Command to execute the bitcode (use with -run-custom) "
+ "(default: simulate)"));
+}
+
+namespace llvm {
+// Anything specified after the --args option are taken as arguments to the
+// program being debugged.
+cl::list<std::string> InputArgv("args", cl::Positional,
+ cl::desc("<program arguments>..."),
+ cl::ZeroOrMore, cl::PositionalEatsArgs);
+
+cl::opt<std::string>
+ OutputPrefix("output-prefix", cl::init("bugpoint"),
+ cl::desc("Prefix to use for outputs (default: 'bugpoint')"));
+}
+
+namespace {
+cl::list<std::string> ToolArgv("tool-args", cl::Positional,
+ cl::desc("<tool arguments>..."), cl::ZeroOrMore,
+ cl::PositionalEatsArgs);
+
+cl::list<std::string> SafeToolArgv("safe-tool-args", cl::Positional,
+ cl::desc("<safe-tool arguments>..."),
+ cl::ZeroOrMore, cl::PositionalEatsArgs);
+
+cl::opt<std::string> CCBinary("gcc", cl::init(""),
+ cl::desc("The gcc binary to use."));
+
+cl::list<std::string> CCToolArgv("gcc-tool-args", cl::Positional,
+ cl::desc("<gcc-tool arguments>..."),
+ cl::ZeroOrMore, cl::PositionalEatsArgs);
+}
+
+//===----------------------------------------------------------------------===//
+// BugDriver method implementation
+//
+
+/// initializeExecutionEnvironment - This method is used to set up the
+/// environment for executing LLVM programs.
+///
+Error BugDriver::initializeExecutionEnvironment() {
+ outs() << "Initializing execution environment: ";
+
+ // Create an instance of the AbstractInterpreter interface as specified on
+ // the command line
+ SafeInterpreter = nullptr;
+ std::string Message;
+
+ if (CCBinary.empty()) {
+ if (ErrorOr<std::string> ClangPath =
+ FindProgramByName("clang", getToolName(), &AbsTolerance))
+ CCBinary = *ClangPath;
+ else
+ CCBinary = "gcc";
+ }
+
+ switch (InterpreterSel) {
+ case AutoPick:
+ if (!Interpreter) {
+ InterpreterSel = RunJIT;
+ Interpreter =
+ AbstractInterpreter::createJIT(getToolName(), Message, &ToolArgv);
+ }
+ if (!Interpreter) {
+ InterpreterSel = RunLLC;
+ Interpreter = AbstractInterpreter::createLLC(
+ getToolName(), Message, CCBinary, &ToolArgv, &CCToolArgv);
+ }
+ if (!Interpreter) {
+ InterpreterSel = RunLLI;
+ Interpreter =
+ AbstractInterpreter::createLLI(getToolName(), Message, &ToolArgv);
+ }
+ if (!Interpreter) {
+ InterpreterSel = AutoPick;
+ Message = "Sorry, I can't automatically select an interpreter!\n";
+ }
+ break;
+ case RunLLI:
+ Interpreter =
+ AbstractInterpreter::createLLI(getToolName(), Message, &ToolArgv);
+ break;
+ case RunLLC:
+ case RunLLCIA:
+ Interpreter = AbstractInterpreter::createLLC(
+ getToolName(), Message, CCBinary, &ToolArgv, &CCToolArgv,
+ InterpreterSel == RunLLCIA);
+ break;
+ case RunJIT:
+ Interpreter =
+ AbstractInterpreter::createJIT(getToolName(), Message, &ToolArgv);
+ break;
+ case CompileCustom:
+ Interpreter = AbstractInterpreter::createCustomCompiler(
+ getToolName(), Message, CustomCompileCommand);
+ break;
+ case Custom:
+ Interpreter = AbstractInterpreter::createCustomExecutor(
+ getToolName(), Message, CustomExecCommand);
+ break;
+ }
+ if (!Interpreter)
+ errs() << Message;
+ else // Display informational messages on stdout instead of stderr
+ outs() << Message;
+
+ std::string Path = SafeInterpreterPath;
+ if (Path.empty())
+ Path = getToolName();
+ std::vector<std::string> SafeToolArgs = SafeToolArgv;
+ switch (SafeInterpreterSel) {
+ case AutoPick:
+ // In "llc-safe" mode, default to using LLC as the "safe" backend.
+ if (InterpreterSel == RunLLC) {
+ SafeInterpreterSel = RunLLC;
+ SafeToolArgs.push_back("--relocation-model=pic");
+ SafeInterpreter = AbstractInterpreter::createLLC(
+ Path.c_str(), Message, CCBinary, &SafeToolArgs, &CCToolArgv);
+ } else if (InterpreterSel != CompileCustom) {
+ SafeInterpreterSel = AutoPick;
+ Message = "Sorry, I can't automatically select a safe interpreter!\n";
+ }
+ break;
+ case RunLLC:
+ case RunLLCIA:
+ SafeToolArgs.push_back("--relocation-model=pic");
+ SafeInterpreter = AbstractInterpreter::createLLC(
+ Path.c_str(), Message, CCBinary, &SafeToolArgs, &CCToolArgv,
+ SafeInterpreterSel == RunLLCIA);
+ break;
+ case Custom:
+ SafeInterpreter = AbstractInterpreter::createCustomExecutor(
+ getToolName(), Message, CustomExecCommand);
+ break;
+ default:
+ Message = "Sorry, this back-end is not supported by bugpoint as the "
+ "\"safe\" backend right now!\n";
+ break;
+ }
+ if (!SafeInterpreter && InterpreterSel != CompileCustom) {
+ outs() << Message << "\nExiting.\n";
+ exit(1);
+ }
+
+ cc = CC::create(getToolName(), Message, CCBinary, &CCToolArgv);
+ if (!cc) {
+ outs() << Message << "\nExiting.\n";
+ exit(1);
+ }
+
+ // If there was an error creating the selected interpreter, quit with error.
+ if (Interpreter == nullptr)
+ return make_error<StringError>("Failed to init execution environment",
+ inconvertibleErrorCode());
+ return Error::success();
+}
+
+/// Try to compile the specified module, returning false and setting Error if an
+/// error occurs. This is used for code generation crash testing.
+Error BugDriver::compileProgram(Module &M) const {
+ // Emit the program to a bitcode file...
+ auto Temp =
+ sys::fs::TempFile::create(OutputPrefix + "-test-program-%%%%%%%.bc");
+ if (!Temp) {
+ errs() << ToolName
+ << ": Error making unique filename: " << toString(Temp.takeError())
+ << "\n";
+ exit(1);
+ }
+ DiscardTemp Discard{*Temp};
+ if (writeProgramToFile(Temp->FD, M)) {
+ errs() << ToolName << ": Error emitting bitcode to file '" << Temp->TmpName
+ << "'!\n";
+ exit(1);
+ }
+
+ // Actually compile the program!
+ return Interpreter->compileProgram(Temp->TmpName, Timeout, MemoryLimit);
+}
+
+/// This method runs "Program", capturing the output of the program to a file,
+/// returning the filename of the file. A recommended filename may be
+/// optionally specified.
+Expected<std::string> BugDriver::executeProgram(const Module &Program,
+ std::string OutputFile,
+ std::string BitcodeFile,
+ const std::string &SharedObj,
+ AbstractInterpreter *AI) const {
+ 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...
+ 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);
+ }
+ BitcodeFile = std::string(UniqueFilename.str());
+
+ 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-%%%%%%%";
+
+ // Check to see if this is a valid output filename...
+ SmallString<128> UniqueFile;
+ std::error_code EC = sys::fs::createUniqueFile(OutputFile, UniqueFile);
+ if (EC) {
+ errs() << ToolName << ": Error making unique filename: " << EC.message()
+ << "\n";
+ exit(1);
+ }
+ OutputFile = std::string(UniqueFile.str());
+
+ // Figure out which shared objects to run, if any.
+ std::vector<std::string> SharedObjs(AdditionalSOs);
+ if (!SharedObj.empty())
+ SharedObjs.push_back(SharedObj);
+
+ Expected<int> RetVal = AI->ExecuteProgram(BitcodeFile, InputArgv, InputFile,
+ OutputFile, AdditionalLinkerArgs,
+ SharedObjs, Timeout, MemoryLimit);
+ if (Error E = RetVal.takeError())
+ return std::move(E);
+
+ if (*RetVal == -1) {
+ errs() << "<timeout>";
+ static bool FirstTimeout = true;
+ if (FirstTimeout) {
+ outs()
+ << "\n"
+ "*** Program execution timed out! This mechanism is designed to "
+ "handle\n"
+ " programs stuck in infinite loops gracefully. The -timeout "
+ "option\n"
+ " can be used to change the timeout threshold or disable it "
+ "completely\n"
+ " (with -timeout=0). This message is only displayed once.\n";
+ FirstTimeout = false;
+ }
+ }
+
+ if (AppendProgramExitCode) {
+ std::ofstream outFile(OutputFile.c_str(), std::ios_base::app);
+ outFile << "exit " << *RetVal << '\n';
+ outFile.close();
+ }
+
+ // Return the filename we captured the output to.
+ return OutputFile;
+}
+
+/// Used to create reference output with the "safe" backend, if reference output
+/// is not provided.
+Expected<std::string>
+BugDriver::executeProgramSafely(const Module &Program,
+ const std::string &OutputFile) const {
+ return executeProgram(Program, OutputFile, "", "", SafeInterpreter);
+}
+
+Expected<std::string>
+BugDriver::compileSharedObject(const std::string &BitcodeFile) {
+ assert(Interpreter && "Interpreter should have been created already!");
+ std::string OutputFile;
+
+ // Using the known-good backend.
+ Expected<CC::FileType> FT =
+ SafeInterpreter->OutputCode(BitcodeFile, OutputFile);
+ if (Error E = FT.takeError())
+ return std::move(E);
+
+ std::string SharedObjectFile;
+ if (Error E = cc->MakeSharedObject(OutputFile, *FT, SharedObjectFile,
+ AdditionalLinkerArgs))
+ return std::move(E);
+
+ // Remove the intermediate C file
+ sys::fs::remove(OutputFile);
+
+ return SharedObjectFile;
+}
+
+/// Calls compileProgram and then records the output into ReferenceOutputFile.
+/// Returns true if reference file created, false otherwise. Note:
+/// initializeExecutionEnvironment should be called BEFORE this function.
+Error BugDriver::createReferenceFile(Module &M, const std::string &Filename) {
+ if (Error E = compileProgram(*Program))
+ return E;
+
+ Expected<std::string> Result = executeProgramSafely(*Program, Filename);
+ if (Error E = Result.takeError()) {
+ if (Interpreter != SafeInterpreter) {
+ E = joinErrors(
+ std::move(E),
+ make_error<StringError>(
+ "*** There is a bug running the \"safe\" backend. Either"
+ " debug it (for example with the -run-jit bugpoint option,"
+ " if JIT is being used as the \"safe\" backend), or fix the"
+ " error some other way.\n",
+ inconvertibleErrorCode()));
+ }
+ return E;
+ }
+ ReferenceOutputFile = *Result;
+ outs() << "\nReference output is: " << ReferenceOutputFile << "\n\n";
+ return Error::success();
+}
+
+/// This method executes the specified module and diffs the output against the
+/// file specified by ReferenceOutputFile. If the output is different, 1 is
+/// returned. If there is a problem with the code generator (e.g., llc
+/// crashes), this will set ErrMsg.
+Expected<bool> BugDriver::diffProgram(const Module &Program,
+ const std::string &BitcodeFile,
+ const std::string &SharedObject,
+ bool RemoveBitcode) const {
+ // Execute the program, generating an output file...
+ Expected<std::string> Output =
+ executeProgram(Program, "", BitcodeFile, SharedObject, nullptr);
+ if (Error E = Output.takeError())
+ return std::move(E);
+
+ std::string Error;
+ bool FilesDifferent = false;
+ if (int Diff = DiffFilesWithTolerance(ReferenceOutputFile, *Output,
+ AbsTolerance, RelTolerance, &Error)) {
+ if (Diff == 2) {
+ errs() << "While diffing output: " << Error << '\n';
+ exit(1);
+ }
+ FilesDifferent = true;
+ } else {
+ // Remove the generated output if there are no differences.
+ sys::fs::remove(*Output);
+ }
+
+ // Remove the bitcode file if we are supposed to.
+ if (RemoveBitcode)
+ sys::fs::remove(BitcodeFile);
+ return FilesDifferent;
+}
+
+bool BugDriver::isExecutingJIT() { return InterpreterSel == RunJIT; }
diff --git a/contrib/libs/llvm14/tools/bugpoint/ExtractFunction.cpp b/contrib/libs/llvm14/tools/bugpoint/ExtractFunction.cpp
new file mode 100644
index 00000000000..7a75cb90edc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/ExtractFunction.cpp
@@ -0,0 +1,420 @@
+//===- ExtractFunction.cpp - Extract a function from Program --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements several methods that are used to extract functions,
+// loops, or portions of a module from the rest of the module.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BugDriver.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/CodeExtractor.h"
+#include <set>
+using namespace llvm;
+
+#define DEBUG_TYPE "bugpoint"
+
+namespace llvm {
+bool DisableSimplifyCFG = false;
+extern cl::opt<std::string> OutputPrefix;
+} // End llvm namespace
+
+namespace {
+cl::opt<bool> NoDCE("disable-dce",
+ cl::desc("Do not use the -dce pass to reduce testcases"));
+cl::opt<bool, true>
+ NoSCFG("disable-simplifycfg", cl::location(DisableSimplifyCFG),
+ cl::desc("Do not use the -simplifycfg pass to reduce testcases"));
+
+Function *globalInitUsesExternalBA(GlobalVariable *GV) {
+ if (!GV->hasInitializer())
+ return nullptr;
+
+ Constant *I = GV->getInitializer();
+
+ // walk the values used by the initializer
+ // (and recurse into things like ConstantExpr)
+ std::vector<Constant *> Todo;
+ std::set<Constant *> Done;
+ Todo.push_back(I);
+
+ while (!Todo.empty()) {
+ Constant *V = Todo.back();
+ Todo.pop_back();
+ Done.insert(V);
+
+ if (BlockAddress *BA = dyn_cast<BlockAddress>(V)) {
+ Function *F = BA->getFunction();
+ if (F->isDeclaration())
+ return F;
+ }
+
+ for (User::op_iterator i = V->op_begin(), e = V->op_end(); i != e; ++i) {
+ Constant *C = dyn_cast<Constant>(*i);
+ if (C && !isa<GlobalValue>(C) && !Done.count(C))
+ Todo.push_back(C);
+ }
+ }
+ return nullptr;
+}
+} // end anonymous namespace
+
+std::unique_ptr<Module>
+BugDriver::deleteInstructionFromProgram(const Instruction *I,
+ unsigned Simplification) {
+ // FIXME, use vmap?
+ std::unique_ptr<Module> Clone = CloneModule(*Program);
+
+ const BasicBlock *PBB = I->getParent();
+ const Function *PF = PBB->getParent();
+
+ Module::iterator RFI = Clone->begin(); // Get iterator to corresponding fn
+ std::advance(
+ RFI, std::distance(PF->getParent()->begin(), Module::const_iterator(PF)));
+
+ Function::iterator RBI = RFI->begin(); // Get iterator to corresponding BB
+ std::advance(RBI, std::distance(PF->begin(), Function::const_iterator(PBB)));
+
+ BasicBlock::iterator RI = RBI->begin(); // Get iterator to corresponding inst
+ std::advance(RI, std::distance(PBB->begin(), BasicBlock::const_iterator(I)));
+ Instruction *TheInst = &*RI; // Got the corresponding instruction!
+
+ // If this instruction produces a value, replace any users with null values
+ if (!TheInst->getType()->isVoidTy())
+ TheInst->replaceAllUsesWith(Constant::getNullValue(TheInst->getType()));
+
+ // Remove the instruction from the program.
+ TheInst->getParent()->getInstList().erase(TheInst);
+
+ // Spiff up the output a little bit.
+ std::vector<std::string> Passes;
+
+ /// Can we get rid of the -disable-* options?
+ if (Simplification > 1 && !NoDCE)
+ Passes.push_back("dce");
+ if (Simplification && !DisableSimplifyCFG)
+ Passes.push_back("simplifycfg"); // Delete dead control flow
+
+ Passes.push_back("verify");
+ std::unique_ptr<Module> New = runPassesOn(Clone.get(), Passes);
+ if (!New) {
+ errs() << "Instruction removal failed. Sorry. :( Please report a bug!\n";
+ exit(1);
+ }
+ return New;
+}
+
+std::unique_ptr<Module>
+BugDriver::performFinalCleanups(std::unique_ptr<Module> M,
+ bool MayModifySemantics) {
+ // Make all functions external, so GlobalDCE doesn't delete them...
+ for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
+ I->setLinkage(GlobalValue::ExternalLinkage);
+
+ std::vector<std::string> CleanupPasses;
+ CleanupPasses.push_back("globaldce");
+
+ if (MayModifySemantics)
+ CleanupPasses.push_back("deadarghaX0r");
+ else
+ CleanupPasses.push_back("deadargelim");
+
+ std::unique_ptr<Module> New = runPassesOn(M.get(), CleanupPasses);
+ if (!New) {
+ errs() << "Final cleanups failed. Sorry. :( Please report a bug!\n";
+ return nullptr;
+ }
+ return New;
+}
+
+std::unique_ptr<Module> BugDriver::extractLoop(Module *M) {
+ std::vector<std::string> LoopExtractPasses;
+ LoopExtractPasses.push_back("loop-extract-single");
+
+ std::unique_ptr<Module> NewM = runPassesOn(M, LoopExtractPasses);
+ if (!NewM) {
+ outs() << "*** Loop extraction failed: ";
+ EmitProgressBitcode(*M, "loopextraction", true);
+ outs() << "*** Sorry. :( Please report a bug!\n";
+ return nullptr;
+ }
+
+ // Check to see if we created any new functions. If not, no loops were
+ // extracted and we should return null. Limit the number of loops we extract
+ // to avoid taking forever.
+ static unsigned NumExtracted = 32;
+ if (M->size() == NewM->size() || --NumExtracted == 0) {
+ return nullptr;
+ } else {
+ assert(M->size() < NewM->size() && "Loop extract removed functions?");
+ Module::iterator MI = NewM->begin();
+ for (unsigned i = 0, e = M->size(); i != e; ++i)
+ ++MI;
+ }
+
+ return NewM;
+}
+
+static void eliminateAliases(GlobalValue *GV) {
+ // First, check whether a GlobalAlias references this definition.
+ // GlobalAlias MAY NOT reference declarations.
+ for (;;) {
+ // 1. Find aliases
+ SmallVector<GlobalAlias *, 1> aliases;
+ Module *M = GV->getParent();
+ for (Module::alias_iterator I = M->alias_begin(), E = M->alias_end();
+ I != E; ++I)
+ if (I->getAliasee()->stripPointerCasts() == GV)
+ aliases.push_back(&*I);
+ if (aliases.empty())
+ break;
+ // 2. Resolve aliases
+ for (unsigned i = 0, e = aliases.size(); i < e; ++i) {
+ aliases[i]->replaceAllUsesWith(aliases[i]->getAliasee());
+ aliases[i]->eraseFromParent();
+ }
+ // 3. Repeat until no more aliases found; there might
+ // be an alias to an alias...
+ }
+}
+
+//
+// DeleteGlobalInitializer - "Remove" the global variable by deleting its
+// initializer,
+// making it external.
+//
+void llvm::DeleteGlobalInitializer(GlobalVariable *GV) {
+ eliminateAliases(GV);
+ GV->setInitializer(nullptr);
+ GV->setComdat(nullptr);
+}
+
+// DeleteFunctionBody - "Remove" the function by deleting all of its basic
+// blocks, making it external.
+//
+void llvm::DeleteFunctionBody(Function *F) {
+ eliminateAliases(F);
+ // Function declarations can't have comdats.
+ F->setComdat(nullptr);
+
+ // delete the body of the function...
+ F->deleteBody();
+ assert(F->isDeclaration() && "This didn't make the function external!");
+}
+
+/// GetTorInit - Given a list of entries for static ctors/dtors, return them
+/// as a constant array.
+static Constant *GetTorInit(std::vector<std::pair<Function *, int>> &TorList) {
+ assert(!TorList.empty() && "Don't create empty tor list!");
+ std::vector<Constant *> ArrayElts;
+ Type *Int32Ty = Type::getInt32Ty(TorList[0].first->getContext());
+
+ StructType *STy = StructType::get(Int32Ty, TorList[0].first->getType());
+ for (unsigned i = 0, e = TorList.size(); i != e; ++i) {
+ Constant *Elts[] = {ConstantInt::get(Int32Ty, TorList[i].second),
+ TorList[i].first};
+ ArrayElts.push_back(ConstantStruct::get(STy, Elts));
+ }
+ return ConstantArray::get(
+ ArrayType::get(ArrayElts[0]->getType(), ArrayElts.size()), ArrayElts);
+}
+
+/// SplitStaticCtorDtor - A module was recently split into two parts, M1/M2, and
+/// M1 has all of the global variables. If M2 contains any functions that are
+/// static ctors/dtors, we need to add an llvm.global_[cd]tors global to M2, and
+/// prune appropriate entries out of M1s list.
+static void SplitStaticCtorDtor(const char *GlobalName, Module *M1, Module *M2,
+ ValueToValueMapTy &VMap) {
+ GlobalVariable *GV = M1->getNamedGlobal(GlobalName);
+ if (!GV || GV->isDeclaration() || GV->hasLocalLinkage() || !GV->use_empty())
+ return;
+
+ std::vector<std::pair<Function *, int>> M1Tors, M2Tors;
+ ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer());
+ if (!InitList)
+ return;
+
+ for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) {
+ if (ConstantStruct *CS =
+ dyn_cast<ConstantStruct>(InitList->getOperand(i))) {
+ if (CS->getNumOperands() != 2)
+ return; // Not array of 2-element structs.
+
+ if (CS->getOperand(1)->isNullValue())
+ break; // Found a null terminator, stop here.
+
+ ConstantInt *CI = dyn_cast<ConstantInt>(CS->getOperand(0));
+ int Priority = CI ? CI->getSExtValue() : 0;
+
+ Constant *FP = CS->getOperand(1);
+ if (ConstantExpr *CE = dyn_cast<ConstantExpr>(FP))
+ if (CE->isCast())
+ FP = CE->getOperand(0);
+ if (Function *F = dyn_cast<Function>(FP)) {
+ if (!F->isDeclaration())
+ M1Tors.push_back(std::make_pair(F, Priority));
+ else {
+ // Map to M2's version of the function.
+ F = cast<Function>(VMap[F]);
+ M2Tors.push_back(std::make_pair(F, Priority));
+ }
+ }
+ }
+ }
+
+ GV->eraseFromParent();
+ if (!M1Tors.empty()) {
+ Constant *M1Init = GetTorInit(M1Tors);
+ new GlobalVariable(*M1, M1Init->getType(), false,
+ GlobalValue::AppendingLinkage, M1Init, GlobalName);
+ }
+
+ GV = M2->getNamedGlobal(GlobalName);
+ assert(GV && "Not a clone of M1?");
+ assert(GV->use_empty() && "llvm.ctors shouldn't have uses!");
+
+ GV->eraseFromParent();
+ if (!M2Tors.empty()) {
+ Constant *M2Init = GetTorInit(M2Tors);
+ new GlobalVariable(*M2, M2Init->getType(), false,
+ GlobalValue::AppendingLinkage, M2Init, GlobalName);
+ }
+}
+
+std::unique_ptr<Module>
+llvm::SplitFunctionsOutOfModule(Module *M, const std::vector<Function *> &F,
+ ValueToValueMapTy &VMap) {
+ // Make sure functions & globals are all external so that linkage
+ // between the two modules will work.
+ for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
+ I->setLinkage(GlobalValue::ExternalLinkage);
+ for (Module::global_iterator I = M->global_begin(), E = M->global_end();
+ I != E; ++I) {
+ if (I->hasName() && I->getName()[0] == '\01')
+ I->setName(I->getName().substr(1));
+ I->setLinkage(GlobalValue::ExternalLinkage);
+ }
+
+ ValueToValueMapTy NewVMap;
+ std::unique_ptr<Module> New = CloneModule(*M, NewVMap);
+
+ // Remove the Test functions from the Safe module
+ std::set<Function *> TestFunctions;
+ for (unsigned i = 0, e = F.size(); i != e; ++i) {
+ Function *TNOF = cast<Function>(VMap[F[i]]);
+ LLVM_DEBUG(errs() << "Removing function ");
+ LLVM_DEBUG(TNOF->printAsOperand(errs(), false));
+ LLVM_DEBUG(errs() << "\n");
+ TestFunctions.insert(cast<Function>(NewVMap[TNOF]));
+ DeleteFunctionBody(TNOF); // Function is now external in this module!
+ }
+
+ // Remove the Safe functions from the Test module
+ for (Function &I : *New)
+ if (!TestFunctions.count(&I))
+ DeleteFunctionBody(&I);
+
+ // Try to split the global initializers evenly
+ for (GlobalVariable &I : M->globals()) {
+ GlobalVariable *GV = cast<GlobalVariable>(NewVMap[&I]);
+ if (Function *TestFn = globalInitUsesExternalBA(&I)) {
+ if (Function *SafeFn = globalInitUsesExternalBA(GV)) {
+ errs() << "*** Error: when reducing functions, encountered "
+ "the global '";
+ GV->printAsOperand(errs(), false);
+ errs() << "' with an initializer that references blockaddresses "
+ "from safe function '"
+ << SafeFn->getName() << "' and from test function '"
+ << TestFn->getName() << "'.\n";
+ exit(1);
+ }
+ DeleteGlobalInitializer(&I); // Delete the initializer to make it external
+ } else {
+ // If we keep it in the safe module, then delete it in the test module
+ DeleteGlobalInitializer(GV);
+ }
+ }
+
+ // Make sure that there is a global ctor/dtor array in both halves of the
+ // module if they both have static ctor/dtor functions.
+ SplitStaticCtorDtor("llvm.global_ctors", M, New.get(), NewVMap);
+ SplitStaticCtorDtor("llvm.global_dtors", M, New.get(), NewVMap);
+
+ return New;
+}
+
+//===----------------------------------------------------------------------===//
+// Basic Block Extraction Code
+//===----------------------------------------------------------------------===//
+
+std::unique_ptr<Module>
+BugDriver::extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs,
+ Module *M) {
+ auto Temp = sys::fs::TempFile::create(OutputPrefix + "-extractblocks%%%%%%%");
+ if (!Temp) {
+ outs() << "*** Basic Block extraction failed!\n";
+ errs() << "Error creating temporary file: " << toString(Temp.takeError())
+ << "\n";
+ EmitProgressBitcode(*M, "basicblockextractfail", true);
+ return nullptr;
+ }
+ DiscardTemp Discard{*Temp};
+
+ // Extract all of the blocks except the ones in BBs.
+ SmallVector<BasicBlock *, 32> BlocksToExtract;
+ for (Function &F : *M)
+ for (BasicBlock &BB : F)
+ // Check if this block is going to be extracted.
+ if (!llvm::is_contained(BBs, &BB))
+ BlocksToExtract.push_back(&BB);
+
+ raw_fd_ostream OS(Temp->FD, /*shouldClose*/ false);
+ for (BasicBlock *BB : BBs) {
+ // If the BB doesn't have a name, give it one so we have something to key
+ // off of.
+ if (!BB->hasName())
+ BB->setName("tmpbb");
+ OS << BB->getParent()->getName() << " " << BB->getName() << "\n";
+ }
+ OS.flush();
+ if (OS.has_error()) {
+ errs() << "Error writing list of blocks to not extract\n";
+ EmitProgressBitcode(*M, "basicblockextractfail", true);
+ OS.clear_error();
+ return nullptr;
+ }
+
+ std::string uniqueFN = "--extract-blocks-file=";
+ uniqueFN += Temp->TmpName;
+
+ std::vector<std::string> PI;
+ PI.push_back("extract-blocks");
+ std::unique_ptr<Module> Ret = runPassesOn(M, PI, {uniqueFN});
+
+ if (!Ret) {
+ outs() << "*** Basic Block extraction failed, please report a bug!\n";
+ EmitProgressBitcode(*M, "basicblockextractfail", true);
+ }
+ return Ret;
+}
diff --git a/contrib/libs/llvm14/tools/bugpoint/FindBugs.cpp b/contrib/libs/llvm14/tools/bugpoint/FindBugs.cpp
new file mode 100644
index 00000000000..31945fadd95
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/FindBugs.cpp
@@ -0,0 +1,99 @@
+//===-- FindBugs.cpp - Run Many Different Optimizations -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines an interface that allows bugpoint to choose different
+// combinations of optimizations to run on the selected input. Bugpoint will
+// run these optimizations and record the success/failure of each. This way
+// we can hopefully spot bugs in the optimizations.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BugDriver.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include <random>
+using namespace llvm;
+
+Error
+BugDriver::runManyPasses(const std::vector<std::string> &AllPasses) {
+ setPassesToRun(AllPasses);
+ outs() << "Starting bug finding procedure...\n\n";
+
+ // Creating a reference output if necessary
+ if (Error E = initializeExecutionEnvironment())
+ return E;
+
+ outs() << "\n";
+ if (ReferenceOutputFile.empty()) {
+ outs() << "Generating reference output from raw program: \n";
+ if (Error E = createReferenceFile(*Program))
+ return E;
+ }
+
+ std::mt19937 randomness(std::random_device{}());
+ unsigned num = 1;
+ while (true) {
+ //
+ // Step 1: Randomize the order of the optimizer passes.
+ //
+ llvm::shuffle(PassesToRun.begin(), PassesToRun.end(), randomness);
+
+ //
+ // Step 2: Run optimizer passes on the program and check for success.
+ //
+ outs() << "Running selected passes on program to test for crash: ";
+ for (int i = 0, e = PassesToRun.size(); i != e; i++) {
+ outs() << "-" << PassesToRun[i] << " ";
+ }
+
+ std::string Filename;
+ if (runPasses(*Program, PassesToRun, Filename, false)) {
+ outs() << "\n";
+ outs() << "Optimizer passes caused failure!\n\n";
+ return debugOptimizerCrash();
+ } else {
+ outs() << "Combination " << num << " optimized successfully!\n";
+ }
+
+ //
+ // Step 3: Compile the optimized code.
+ //
+ outs() << "Running the code generator to test for a crash: ";
+ if (Error E = compileProgram(*Program)) {
+ outs() << "\n*** compileProgram threw an exception: ";
+ outs() << toString(std::move(E));
+ return debugCodeGeneratorCrash();
+ }
+ outs() << '\n';
+
+ //
+ // Step 4: Run the program and compare its output to the reference
+ // output (created above).
+ //
+ outs() << "*** Checking if passes caused miscompliation:\n";
+ Expected<bool> Diff = diffProgram(*Program, Filename, "", false);
+ if (Error E = Diff.takeError()) {
+ errs() << toString(std::move(E));
+ return debugCodeGeneratorCrash();
+ }
+ if (*Diff) {
+ outs() << "\n*** diffProgram returned true!\n";
+ Error E = debugMiscompilation();
+ if (!E)
+ return Error::success();
+ }
+ outs() << "\n*** diff'd output matches!\n";
+
+ sys::fs::remove(Filename);
+
+ outs() << "\n\n";
+ num++;
+ } // end while
+
+ // Unreachable.
+}
diff --git a/contrib/libs/llvm14/tools/bugpoint/ListReducer.h b/contrib/libs/llvm14/tools/bugpoint/ListReducer.h
new file mode 100644
index 00000000000..06f8ddb2553
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/ListReducer.h
@@ -0,0 +1,208 @@
+//===- ListReducer.h - Trim down list while retaining property --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This class is to be used as a base class for operations that want to zero in
+// on a subset of the input which still causes the bug we are tracking.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
+#define LLVM_TOOLS_BUGPOINT_LISTREDUCER_H
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cstdlib>
+#include <random>
+#include <vector>
+
+namespace llvm {
+
+extern bool BugpointIsInterrupted;
+
+template <typename ElTy> struct ListReducer {
+ enum TestResult {
+ NoFailure, // No failure of the predicate was detected
+ KeepSuffix, // The suffix alone satisfies the predicate
+ KeepPrefix // The prefix alone satisfies the predicate
+ };
+
+ virtual ~ListReducer() {}
+
+ /// This virtual function should be overriden by subclasses to implement the
+ /// test desired. The testcase is only required to test to see if the Kept
+ /// list still satisfies the property, but if it is going to check the prefix
+ /// anyway, it can.
+ virtual Expected<TestResult> doTest(std::vector<ElTy> &Prefix,
+ std::vector<ElTy> &Kept) = 0;
+
+ /// This function attempts to reduce the length of the specified list while
+ /// still maintaining the "test" property. This is the core of the "work"
+ /// that bugpoint does.
+ Expected<bool> reduceList(std::vector<ElTy> &TheList) {
+ std::vector<ElTy> empty;
+ std::mt19937 randomness(0x6e5ea738); // Seed the random number generator
+ Expected<TestResult> Result = doTest(TheList, empty);
+ if (Error E = Result.takeError())
+ return std::move(E);
+ switch (*Result) {
+ case KeepPrefix:
+ if (TheList.size() == 1) // we are done, it's the base case and it fails
+ return true;
+ else
+ break; // there's definitely an error, but we need to narrow it down
+
+ case KeepSuffix:
+ // cannot be reached!
+ llvm_unreachable("bugpoint ListReducer internal error: "
+ "selected empty set.");
+
+ case NoFailure:
+ return false; // there is no failure with the full set of passes/funcs!
+ }
+
+ // Maximal number of allowed splitting iterations,
+ // before the elements are randomly shuffled.
+ const unsigned MaxIterationsWithoutProgress = 3;
+
+ // Maximal number of allowed single-element trim iterations. We add a
+ // threshold here as single-element reductions may otherwise take a
+ // very long time to complete.
+ const unsigned MaxTrimIterationsWithoutBackJump = 3;
+ bool ShufflingEnabled = true;
+
+ Backjump:
+ unsigned MidTop = TheList.size();
+ unsigned MaxIterations = MaxIterationsWithoutProgress;
+ unsigned NumOfIterationsWithoutProgress = 0;
+ while (MidTop > 1) { // Binary split reduction loop
+ // Halt if the user presses ctrl-c.
+ if (BugpointIsInterrupted) {
+ errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
+ return true;
+ }
+
+ // If the loop doesn't make satisfying progress, try shuffling.
+ // The purpose of shuffling is to avoid the heavy tails of the
+ // distribution (improving the speed of convergence).
+ if (ShufflingEnabled && NumOfIterationsWithoutProgress > MaxIterations) {
+ std::vector<ElTy> ShuffledList(TheList);
+ llvm::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness);
+ errs() << "\n\n*** Testing shuffled set...\n\n";
+ // Check that random shuffle doesn't lose the bug
+ Expected<TestResult> Result = doTest(ShuffledList, empty);
+ // TODO: Previously, this error was ignored and we treated it as if
+ // shuffling hid the bug. This should really either be consumeError if
+ // that behaviour was sensible, or we should propagate the error.
+ assert(!Result.takeError() && "Shuffling caused internal error?");
+
+ if (*Result == KeepPrefix) {
+ // If the bug is still here, use the shuffled list.
+ TheList.swap(ShuffledList);
+ MidTop = TheList.size();
+ // Must increase the shuffling treshold to avoid the small
+ // probability of infinite looping without making progress.
+ MaxIterations += 2;
+ errs() << "\n\n*** Shuffling does not hide the bug...\n\n";
+ } else {
+ ShufflingEnabled = false; // Disable shuffling further on
+ errs() << "\n\n*** Shuffling hides the bug...\n\n";
+ }
+ NumOfIterationsWithoutProgress = 0;
+ }
+
+ unsigned Mid = MidTop / 2;
+ std::vector<ElTy> Prefix(TheList.begin(), TheList.begin() + Mid);
+ std::vector<ElTy> Suffix(TheList.begin() + Mid, TheList.end());
+
+ Expected<TestResult> Result = doTest(Prefix, Suffix);
+ if (Error E = Result.takeError())
+ return std::move(E);
+ switch (*Result) {
+ case KeepSuffix:
+ // The property still holds. We can just drop the prefix elements, and
+ // shorten the list to the "kept" elements.
+ TheList.swap(Suffix);
+ MidTop = TheList.size();
+ // Reset progress treshold and progress counter
+ MaxIterations = MaxIterationsWithoutProgress;
+ NumOfIterationsWithoutProgress = 0;
+ break;
+ case KeepPrefix:
+ // The predicate still holds, shorten the list to the prefix elements.
+ TheList.swap(Prefix);
+ MidTop = TheList.size();
+ // Reset progress treshold and progress counter
+ MaxIterations = MaxIterationsWithoutProgress;
+ NumOfIterationsWithoutProgress = 0;
+ break;
+ case NoFailure:
+ // Otherwise the property doesn't hold. Some of the elements we removed
+ // must be necessary to maintain the property.
+ MidTop = Mid;
+ NumOfIterationsWithoutProgress++;
+ break;
+ }
+ }
+
+ // Probability of backjumping from the trimming loop back to the binary
+ // split reduction loop.
+ const int BackjumpProbability = 10;
+
+ // Okay, we trimmed as much off the top and the bottom of the list as we
+ // could. If there is more than two elements in the list, try deleting
+ // interior elements and testing that.
+ //
+ if (TheList.size() > 2) {
+ bool Changed = true;
+ std::vector<ElTy> EmptyList;
+ unsigned TrimIterations = 0;
+ while (Changed) { // Trimming loop.
+ Changed = false;
+
+ // If the binary split reduction loop made an unfortunate sequence of
+ // splits, the trimming loop might be left off with a huge number of
+ // remaining elements (large search space). Backjumping out of that
+ // search space and attempting a different split can significantly
+ // improve the convergence speed.
+ if (std::rand() % 100 < BackjumpProbability)
+ goto Backjump;
+
+ for (unsigned i = 1; i < TheList.size() - 1; ++i) {
+ // Check interior elts
+ if (BugpointIsInterrupted) {
+ errs() << "\n\n*** Reduction Interrupted, cleaning up...\n\n";
+ return true;
+ }
+
+ std::vector<ElTy> TestList(TheList);
+ TestList.erase(TestList.begin() + i);
+
+ Expected<TestResult> Result = doTest(EmptyList, TestList);
+ if (Error E = Result.takeError())
+ return std::move(E);
+ if (*Result == KeepSuffix) {
+ // We can trim down the list!
+ TheList.swap(TestList);
+ --i; // Don't skip an element of the list
+ Changed = true;
+ }
+ }
+ if (TrimIterations >= MaxTrimIterationsWithoutBackJump)
+ break;
+ TrimIterations++;
+ }
+ }
+
+ return true; // there are some failure and we've narrowed them down
+ }
+};
+
+} // End llvm namespace
+
+#endif
diff --git a/contrib/libs/llvm14/tools/bugpoint/Miscompilation.cpp b/contrib/libs/llvm14/tools/bugpoint/Miscompilation.cpp
new file mode 100644
index 00000000000..38821949d0f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/Miscompilation.cpp
@@ -0,0 +1,1104 @@
+//===- Miscompilation.cpp - Debug program miscompilations -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements optimizer and code generation miscompilation debugging
+// support.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BugDriver.h"
+#include "ListReducer.h"
+#include "ToolRunner.h"
+#include "llvm/Config/config.h" // for HAVE_LINK_R
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Linker/Linker.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+using namespace llvm;
+
+namespace llvm {
+extern cl::opt<std::string> OutputPrefix;
+extern cl::list<std::string> InputArgv;
+} // end namespace llvm
+
+namespace {
+static llvm::cl::opt<bool> DisableLoopExtraction(
+ "disable-loop-extraction",
+ cl::desc("Don't extract loops when searching for miscompilations"),
+ cl::init(false));
+static llvm::cl::opt<bool> DisableBlockExtraction(
+ "disable-block-extraction",
+ cl::desc("Don't extract blocks when searching for miscompilations"),
+ cl::init(false));
+
+class ReduceMiscompilingPasses : public ListReducer<std::string> {
+ BugDriver &BD;
+
+public:
+ ReduceMiscompilingPasses(BugDriver &bd) : BD(bd) {}
+
+ Expected<TestResult> doTest(std::vector<std::string> &Prefix,
+ std::vector<std::string> &Suffix) override;
+};
+} // end anonymous namespace
+
+/// TestResult - After passes have been split into a test group and a control
+/// group, see if they still break the program.
+///
+Expected<ReduceMiscompilingPasses::TestResult>
+ReduceMiscompilingPasses::doTest(std::vector<std::string> &Prefix,
+ std::vector<std::string> &Suffix) {
+ // First, run the program with just the Suffix passes. If it is still broken
+ // with JUST the kept passes, discard the prefix passes.
+ outs() << "Checking to see if '" << getPassesString(Suffix)
+ << "' compiles correctly: ";
+
+ std::string BitcodeResult;
+ if (BD.runPasses(BD.getProgram(), Suffix, BitcodeResult, false /*delete*/,
+ true /*quiet*/)) {
+ errs() << " Error running this sequence of passes"
+ << " on the input program!\n";
+ BD.setPassesToRun(Suffix);
+ BD.EmitProgressBitcode(BD.getProgram(), "pass-error", false);
+ // TODO: This should propagate the error instead of exiting.
+ if (Error E = BD.debugOptimizerCrash())
+ exit(1);
+ exit(0);
+ }
+
+ // Check to see if the finished program matches the reference output...
+ Expected<bool> Diff = BD.diffProgram(BD.getProgram(), BitcodeResult, "",
+ true /*delete bitcode*/);
+ if (Error E = Diff.takeError())
+ return std::move(E);
+ if (*Diff) {
+ outs() << " nope.\n";
+ if (Suffix.empty()) {
+ errs() << BD.getToolName() << ": I'm confused: the test fails when "
+ << "no passes are run, nondeterministic program?\n";
+ exit(1);
+ }
+ return KeepSuffix; // Miscompilation detected!
+ }
+ outs() << " yup.\n"; // No miscompilation!
+
+ if (Prefix.empty())
+ return NoFailure;
+
+ // Next, see if the program is broken if we run the "prefix" passes first,
+ // then separately run the "kept" passes.
+ outs() << "Checking to see if '" << getPassesString(Prefix)
+ << "' compiles correctly: ";
+
+ // If it is not broken with the kept passes, it's possible that the prefix
+ // passes must be run before the kept passes to break it. If the program
+ // WORKS after the prefix passes, but then fails if running the prefix AND
+ // kept passes, we can update our bitcode file to include the result of the
+ // prefix passes, then discard the prefix passes.
+ //
+ if (BD.runPasses(BD.getProgram(), Prefix, BitcodeResult, false /*delete*/,
+ true /*quiet*/)) {
+ errs() << " Error running this sequence of passes"
+ << " on the input program!\n";
+ BD.setPassesToRun(Prefix);
+ BD.EmitProgressBitcode(BD.getProgram(), "pass-error", false);
+ // TODO: This should propagate the error instead of exiting.
+ if (Error E = BD.debugOptimizerCrash())
+ exit(1);
+ exit(0);
+ }
+
+ // If the prefix maintains the predicate by itself, only keep the prefix!
+ Diff = BD.diffProgram(BD.getProgram(), BitcodeResult, "", false);
+ if (Error E = Diff.takeError())
+ return std::move(E);
+ if (*Diff) {
+ outs() << " nope.\n";
+ sys::fs::remove(BitcodeResult);
+ return KeepPrefix;
+ }
+ outs() << " yup.\n"; // No miscompilation!
+
+ // Ok, so now we know that the prefix passes work, try running the suffix
+ // passes on the result of the prefix passes.
+ //
+ std::unique_ptr<Module> PrefixOutput =
+ parseInputFile(BitcodeResult, BD.getContext());
+ if (!PrefixOutput) {
+ errs() << BD.getToolName() << ": Error reading bitcode file '"
+ << BitcodeResult << "'!\n";
+ exit(1);
+ }
+ sys::fs::remove(BitcodeResult);
+
+ // Don't check if there are no passes in the suffix.
+ if (Suffix.empty())
+ return NoFailure;
+
+ outs() << "Checking to see if '" << getPassesString(Suffix)
+ << "' passes compile correctly after the '" << getPassesString(Prefix)
+ << "' passes: ";
+
+ std::unique_ptr<Module> OriginalInput =
+ BD.swapProgramIn(std::move(PrefixOutput));
+ if (BD.runPasses(BD.getProgram(), Suffix, BitcodeResult, false /*delete*/,
+ true /*quiet*/)) {
+ errs() << " Error running this sequence of passes"
+ << " on the input program!\n";
+ BD.setPassesToRun(Suffix);
+ BD.EmitProgressBitcode(BD.getProgram(), "pass-error", false);
+ // TODO: This should propagate the error instead of exiting.
+ if (Error E = BD.debugOptimizerCrash())
+ exit(1);
+ exit(0);
+ }
+
+ // Run the result...
+ Diff = BD.diffProgram(BD.getProgram(), BitcodeResult, "",
+ true /*delete bitcode*/);
+ if (Error E = Diff.takeError())
+ return std::move(E);
+ if (*Diff) {
+ outs() << " nope.\n";
+ return KeepSuffix;
+ }
+
+ // Otherwise, we must not be running the bad pass anymore.
+ outs() << " yup.\n"; // No miscompilation!
+ // Restore orig program & free test.
+ BD.setNewProgram(std::move(OriginalInput));
+ return NoFailure;
+}
+
+namespace {
+class ReduceMiscompilingFunctions : public ListReducer<Function *> {
+ BugDriver &BD;
+ Expected<bool> (*TestFn)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>);
+
+public:
+ ReduceMiscompilingFunctions(BugDriver &bd,
+ Expected<bool> (*F)(BugDriver &,
+ std::unique_ptr<Module>,
+ std::unique_ptr<Module>))
+ : BD(bd), TestFn(F) {}
+
+ Expected<TestResult> doTest(std::vector<Function *> &Prefix,
+ std::vector<Function *> &Suffix) override {
+ if (!Suffix.empty()) {
+ Expected<bool> Ret = TestFuncs(Suffix);
+ if (Error E = Ret.takeError())
+ return std::move(E);
+ if (*Ret)
+ return KeepSuffix;
+ }
+ if (!Prefix.empty()) {
+ Expected<bool> Ret = TestFuncs(Prefix);
+ if (Error E = Ret.takeError())
+ return std::move(E);
+ if (*Ret)
+ return KeepPrefix;
+ }
+ return NoFailure;
+ }
+
+ Expected<bool> TestFuncs(const std::vector<Function *> &Prefix);
+};
+} // end anonymous namespace
+
+/// Given two modules, link them together and run the program, checking to see
+/// if the program matches the diff. If there is an error, return NULL. If not,
+/// return the merged module. The Broken argument will be set to true if the
+/// output is different. If the DeleteInputs argument is set to true then this
+/// function deletes both input modules before it returns.
+///
+static Expected<std::unique_ptr<Module>> testMergedProgram(const BugDriver &BD,
+ const Module &M1,
+ const Module &M2,
+ bool &Broken) {
+ // Resulting merge of M1 and M2.
+ auto Merged = CloneModule(M1);
+ if (Linker::linkModules(*Merged, CloneModule(M2)))
+ // TODO: Shouldn't we thread the error up instead of exiting?
+ exit(1);
+
+ // Execute the program.
+ Expected<bool> Diff = BD.diffProgram(*Merged, "", "", false);
+ if (Error E = Diff.takeError())
+ return std::move(E);
+ Broken = *Diff;
+ return std::move(Merged);
+}
+
+/// split functions in a Module into two groups: those that are under
+/// consideration for miscompilation vs. those that are not, and test
+/// accordingly. Each group of functions becomes a separate Module.
+Expected<bool>
+ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function *> &Funcs) {
+ // Test to see if the function is misoptimized if we ONLY run it on the
+ // functions listed in Funcs.
+ outs() << "Checking to see if the program is misoptimized when "
+ << (Funcs.size() == 1 ? "this function is" : "these functions are")
+ << " run through the pass"
+ << (BD.getPassesToRun().size() == 1 ? "" : "es") << ":";
+ PrintFunctionList(Funcs);
+ outs() << '\n';
+
+ // Create a clone for two reasons:
+ // * If the optimization passes delete any function, the deleted function
+ // will be in the clone and Funcs will still point to valid memory
+ // * If the optimization passes use interprocedural information to break
+ // a function, we want to continue with the original function. Otherwise
+ // we can conclude that a function triggers the bug when in fact one
+ // needs a larger set of original functions to do so.
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> Clone = CloneModule(BD.getProgram(), VMap);
+ std::unique_ptr<Module> Orig = BD.swapProgramIn(std::move(Clone));
+
+ std::vector<Function *> FuncsOnClone;
+ for (unsigned i = 0, e = Funcs.size(); i != e; ++i) {
+ Function *F = cast<Function>(VMap[Funcs[i]]);
+ FuncsOnClone.push_back(F);
+ }
+
+ // Split the module into the two halves of the program we want.
+ VMap.clear();
+ std::unique_ptr<Module> ToNotOptimize = CloneModule(BD.getProgram(), VMap);
+ std::unique_ptr<Module> ToOptimize =
+ SplitFunctionsOutOfModule(ToNotOptimize.get(), FuncsOnClone, VMap);
+
+ Expected<bool> Broken =
+ TestFn(BD, std::move(ToOptimize), std::move(ToNotOptimize));
+
+ BD.setNewProgram(std::move(Orig));
+
+ return Broken;
+}
+
+/// Give anonymous global values names.
+static void DisambiguateGlobalSymbols(Module &M) {
+ for (Module::global_iterator I = M.global_begin(), E = M.global_end(); I != E;
+ ++I)
+ if (!I->hasName())
+ I->setName("anon_global");
+ for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
+ if (!I->hasName())
+ I->setName("anon_fn");
+}
+
+/// Given a reduced list of functions that still exposed the bug, check to see
+/// if we can extract the loops in the region without obscuring the bug. If so,
+/// it reduces the amount of code identified.
+///
+static Expected<bool>
+ExtractLoops(BugDriver &BD,
+ Expected<bool> (*TestFn)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>),
+ std::vector<Function *> &MiscompiledFunctions) {
+ bool MadeChange = false;
+ while (true) {
+ if (BugpointIsInterrupted)
+ return MadeChange;
+
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> ToNotOptimize = CloneModule(BD.getProgram(), VMap);
+ std::unique_ptr<Module> ToOptimize = SplitFunctionsOutOfModule(
+ ToNotOptimize.get(), MiscompiledFunctions, VMap);
+ std::unique_ptr<Module> ToOptimizeLoopExtracted =
+ BD.extractLoop(ToOptimize.get());
+ if (!ToOptimizeLoopExtracted)
+ // If the loop extractor crashed or if there were no extractible loops,
+ // then this chapter of our odyssey is over with.
+ return MadeChange;
+
+ errs() << "Extracted a loop from the breaking portion of the program.\n";
+
+ // Bugpoint is intentionally not very trusting of LLVM transformations. In
+ // particular, we're not going to assume that the loop extractor works, so
+ // we're going to test the newly loop extracted program to make sure nothing
+ // has broken. If something broke, then we'll inform the user and stop
+ // extraction.
+ AbstractInterpreter *AI = BD.switchToSafeInterpreter();
+ bool Failure;
+ Expected<std::unique_ptr<Module>> New = testMergedProgram(
+ BD, *ToOptimizeLoopExtracted, *ToNotOptimize, Failure);
+ if (Error E = New.takeError())
+ return std::move(E);
+ if (!*New)
+ return false;
+
+ // Delete the original and set the new program.
+ std::unique_ptr<Module> Old = BD.swapProgramIn(std::move(*New));
+ for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i)
+ MiscompiledFunctions[i] = cast<Function>(VMap[MiscompiledFunctions[i]]);
+
+ if (Failure) {
+ BD.switchToInterpreter(AI);
+
+ // Merged program doesn't work anymore!
+ errs() << " *** ERROR: Loop extraction broke the program. :("
+ << " Please report a bug!\n";
+ errs() << " Continuing on with un-loop-extracted version.\n";
+
+ BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-tno.bc",
+ *ToNotOptimize);
+ BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-to.bc",
+ *ToOptimize);
+ BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-to-le.bc",
+ *ToOptimizeLoopExtracted);
+
+ errs() << "Please submit the " << OutputPrefix
+ << "-loop-extract-fail-*.bc files.\n";
+ return MadeChange;
+ }
+ BD.switchToInterpreter(AI);
+
+ outs() << " Testing after loop extraction:\n";
+ // Clone modules, the tester function will free them.
+ std::unique_ptr<Module> TOLEBackup =
+ CloneModule(*ToOptimizeLoopExtracted, VMap);
+ std::unique_ptr<Module> TNOBackup = CloneModule(*ToNotOptimize, VMap);
+
+ for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i)
+ MiscompiledFunctions[i] = cast<Function>(VMap[MiscompiledFunctions[i]]);
+
+ Expected<bool> Result = TestFn(BD, std::move(ToOptimizeLoopExtracted),
+ std::move(ToNotOptimize));
+ if (Error E = Result.takeError())
+ return std::move(E);
+
+ ToOptimizeLoopExtracted = std::move(TOLEBackup);
+ ToNotOptimize = std::move(TNOBackup);
+
+ if (!*Result) {
+ outs() << "*** Loop extraction masked the problem. Undoing.\n";
+ // If the program is not still broken, then loop extraction did something
+ // that masked the error. Stop loop extraction now.
+
+ std::vector<std::pair<std::string, FunctionType *>> MisCompFunctions;
+ for (Function *F : MiscompiledFunctions) {
+ MisCompFunctions.emplace_back(std::string(F->getName()),
+ F->getFunctionType());
+ }
+
+ if (Linker::linkModules(*ToNotOptimize,
+ std::move(ToOptimizeLoopExtracted)))
+ exit(1);
+
+ MiscompiledFunctions.clear();
+ for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
+ Function *NewF = ToNotOptimize->getFunction(MisCompFunctions[i].first);
+
+ assert(NewF && "Function not found??");
+ MiscompiledFunctions.push_back(NewF);
+ }
+
+ BD.setNewProgram(std::move(ToNotOptimize));
+ return MadeChange;
+ }
+
+ outs() << "*** Loop extraction successful!\n";
+
+ std::vector<std::pair<std::string, FunctionType *>> MisCompFunctions;
+ for (Module::iterator I = ToOptimizeLoopExtracted->begin(),
+ E = ToOptimizeLoopExtracted->end();
+ I != E; ++I)
+ if (!I->isDeclaration())
+ MisCompFunctions.emplace_back(std::string(I->getName()),
+ I->getFunctionType());
+
+ // Okay, great! Now we know that we extracted a loop and that loop
+ // extraction both didn't break the program, and didn't mask the problem.
+ // Replace the current program with the loop extracted version, and try to
+ // extract another loop.
+ if (Linker::linkModules(*ToNotOptimize, std::move(ToOptimizeLoopExtracted)))
+ exit(1);
+
+ // All of the Function*'s in the MiscompiledFunctions list are in the old
+ // module. Update this list to include all of the functions in the
+ // optimized and loop extracted module.
+ MiscompiledFunctions.clear();
+ for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
+ Function *NewF = ToNotOptimize->getFunction(MisCompFunctions[i].first);
+
+ assert(NewF && "Function not found??");
+ MiscompiledFunctions.push_back(NewF);
+ }
+
+ BD.setNewProgram(std::move(ToNotOptimize));
+ MadeChange = true;
+ }
+}
+
+namespace {
+class ReduceMiscompiledBlocks : public ListReducer<BasicBlock *> {
+ BugDriver &BD;
+ Expected<bool> (*TestFn)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>);
+ std::vector<Function *> FunctionsBeingTested;
+
+public:
+ ReduceMiscompiledBlocks(BugDriver &bd,
+ Expected<bool> (*F)(BugDriver &,
+ std::unique_ptr<Module>,
+ std::unique_ptr<Module>),
+ const std::vector<Function *> &Fns)
+ : BD(bd), TestFn(F), FunctionsBeingTested(Fns) {}
+
+ Expected<TestResult> doTest(std::vector<BasicBlock *> &Prefix,
+ std::vector<BasicBlock *> &Suffix) override {
+ if (!Suffix.empty()) {
+ Expected<bool> Ret = TestFuncs(Suffix);
+ if (Error E = Ret.takeError())
+ return std::move(E);
+ if (*Ret)
+ return KeepSuffix;
+ }
+ if (!Prefix.empty()) {
+ Expected<bool> Ret = TestFuncs(Prefix);
+ if (Error E = Ret.takeError())
+ return std::move(E);
+ if (*Ret)
+ return KeepPrefix;
+ }
+ return NoFailure;
+ }
+
+ Expected<bool> TestFuncs(const std::vector<BasicBlock *> &BBs);
+};
+} // end anonymous namespace
+
+/// TestFuncs - Extract all blocks for the miscompiled functions except for the
+/// specified blocks. If the problem still exists, return true.
+///
+Expected<bool>
+ReduceMiscompiledBlocks::TestFuncs(const std::vector<BasicBlock *> &BBs) {
+ // Test to see if the function is misoptimized if we ONLY run it on the
+ // functions listed in Funcs.
+ outs() << "Checking to see if the program is misoptimized when all ";
+ if (!BBs.empty()) {
+ outs() << "but these " << BBs.size() << " blocks are extracted: ";
+ for (unsigned i = 0, e = BBs.size() < 10 ? BBs.size() : 10; i != e; ++i)
+ outs() << BBs[i]->getName() << " ";
+ if (BBs.size() > 10)
+ outs() << "...";
+ } else {
+ outs() << "blocks are extracted.";
+ }
+ outs() << '\n';
+
+ // Split the module into the two halves of the program we want.
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> Clone = CloneModule(BD.getProgram(), VMap);
+ std::unique_ptr<Module> Orig = BD.swapProgramIn(std::move(Clone));
+ std::vector<Function *> FuncsOnClone;
+ std::vector<BasicBlock *> BBsOnClone;
+ for (unsigned i = 0, e = FunctionsBeingTested.size(); i != e; ++i) {
+ Function *F = cast<Function>(VMap[FunctionsBeingTested[i]]);
+ FuncsOnClone.push_back(F);
+ }
+ for (unsigned i = 0, e = BBs.size(); i != e; ++i) {
+ BasicBlock *BB = cast<BasicBlock>(VMap[BBs[i]]);
+ BBsOnClone.push_back(BB);
+ }
+ VMap.clear();
+
+ std::unique_ptr<Module> ToNotOptimize = CloneModule(BD.getProgram(), VMap);
+ std::unique_ptr<Module> ToOptimize =
+ SplitFunctionsOutOfModule(ToNotOptimize.get(), FuncsOnClone, VMap);
+
+ // Try the extraction. If it doesn't work, then the block extractor crashed
+ // or something, in which case bugpoint can't chase down this possibility.
+ if (std::unique_ptr<Module> New =
+ BD.extractMappedBlocksFromModule(BBsOnClone, ToOptimize.get())) {
+ Expected<bool> Ret = TestFn(BD, std::move(New), std::move(ToNotOptimize));
+ BD.setNewProgram(std::move(Orig));
+ return Ret;
+ }
+ BD.setNewProgram(std::move(Orig));
+ return false;
+}
+
+/// Given a reduced list of functions that still expose the bug, extract as many
+/// basic blocks from the region as possible without obscuring the bug.
+///
+static Expected<bool>
+ExtractBlocks(BugDriver &BD,
+ Expected<bool> (*TestFn)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>),
+ std::vector<Function *> &MiscompiledFunctions) {
+ if (BugpointIsInterrupted)
+ return false;
+
+ std::vector<BasicBlock *> Blocks;
+ for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i)
+ for (BasicBlock &BB : *MiscompiledFunctions[i])
+ Blocks.push_back(&BB);
+
+ // Use the list reducer to identify blocks that can be extracted without
+ // obscuring the bug. The Blocks list will end up containing blocks that must
+ // be retained from the original program.
+ unsigned OldSize = Blocks.size();
+
+ // Check to see if all blocks are extractible first.
+ Expected<bool> Ret = ReduceMiscompiledBlocks(BD, TestFn, MiscompiledFunctions)
+ .TestFuncs(std::vector<BasicBlock *>());
+ if (Error E = Ret.takeError())
+ return std::move(E);
+ if (*Ret) {
+ Blocks.clear();
+ } else {
+ Expected<bool> Ret =
+ ReduceMiscompiledBlocks(BD, TestFn, MiscompiledFunctions)
+ .reduceList(Blocks);
+ if (Error E = Ret.takeError())
+ return std::move(E);
+ if (Blocks.size() == OldSize)
+ return false;
+ }
+
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> ProgClone = CloneModule(BD.getProgram(), VMap);
+ std::unique_ptr<Module> ToExtract =
+ SplitFunctionsOutOfModule(ProgClone.get(), MiscompiledFunctions, VMap);
+ std::unique_ptr<Module> Extracted =
+ BD.extractMappedBlocksFromModule(Blocks, ToExtract.get());
+ if (!Extracted) {
+ // Weird, extraction should have worked.
+ errs() << "Nondeterministic problem extracting blocks??\n";
+ return false;
+ }
+
+ // Otherwise, block extraction succeeded. Link the two program fragments back
+ // together.
+
+ std::vector<std::pair<std::string, FunctionType *>> MisCompFunctions;
+ for (Module::iterator I = Extracted->begin(), E = Extracted->end(); I != E;
+ ++I)
+ if (!I->isDeclaration())
+ MisCompFunctions.emplace_back(std::string(I->getName()),
+ I->getFunctionType());
+
+ if (Linker::linkModules(*ProgClone, std::move(Extracted)))
+ exit(1);
+
+ // Update the list of miscompiled functions.
+ MiscompiledFunctions.clear();
+
+ for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) {
+ Function *NewF = ProgClone->getFunction(MisCompFunctions[i].first);
+ assert(NewF && "Function not found??");
+ MiscompiledFunctions.push_back(NewF);
+ }
+
+ // Set the new program and delete the old one.
+ BD.setNewProgram(std::move(ProgClone));
+
+ return true;
+}
+
+/// This is a generic driver to narrow down miscompilations, either in an
+/// optimization or a code generator.
+///
+static Expected<std::vector<Function *>> DebugAMiscompilation(
+ BugDriver &BD,
+ Expected<bool> (*TestFn)(BugDriver &, std::unique_ptr<Module>,
+ std::unique_ptr<Module>)) {
+ // Okay, now that we have reduced the list of passes which are causing the
+ // failure, see if we can pin down which functions are being
+ // miscompiled... first build a list of all of the non-external functions in
+ // the program.
+ std::vector<Function *> MiscompiledFunctions;
+ Module &Prog = BD.getProgram();
+ for (Function &F : Prog)
+ if (!F.isDeclaration())
+ MiscompiledFunctions.push_back(&F);
+
+ // Do the reduction...
+ if (!BugpointIsInterrupted) {
+ Expected<bool> Ret = ReduceMiscompilingFunctions(BD, TestFn)
+ .reduceList(MiscompiledFunctions);
+ if (Error E = Ret.takeError()) {
+ errs() << "\n***Cannot reduce functions: ";
+ return std::move(E);
+ }
+ }
+ outs() << "\n*** The following function"
+ << (MiscompiledFunctions.size() == 1 ? " is" : "s are")
+ << " being miscompiled: ";
+ PrintFunctionList(MiscompiledFunctions);
+ outs() << '\n';
+
+ // See if we can rip any loops out of the miscompiled functions and still
+ // trigger the problem.
+
+ if (!BugpointIsInterrupted && !DisableLoopExtraction) {
+ Expected<bool> Ret = ExtractLoops(BD, TestFn, MiscompiledFunctions);
+ if (Error E = Ret.takeError())
+ return std::move(E);
+ if (*Ret) {
+ // Okay, we extracted some loops and the problem still appears. See if
+ // we can eliminate some of the created functions from being candidates.
+ DisambiguateGlobalSymbols(BD.getProgram());
+
+ // Do the reduction...
+ if (!BugpointIsInterrupted)
+ Ret = ReduceMiscompilingFunctions(BD, TestFn)
+ .reduceList(MiscompiledFunctions);
+ if (Error E = Ret.takeError())
+ return std::move(E);
+
+ outs() << "\n*** The following function"
+ << (MiscompiledFunctions.size() == 1 ? " is" : "s are")
+ << " being miscompiled: ";
+ PrintFunctionList(MiscompiledFunctions);
+ outs() << '\n';
+ }
+ }
+
+ if (!BugpointIsInterrupted && !DisableBlockExtraction) {
+ Expected<bool> Ret = ExtractBlocks(BD, TestFn, MiscompiledFunctions);
+ if (Error E = Ret.takeError())
+ return std::move(E);
+ if (*Ret) {
+ // Okay, we extracted some blocks and the problem still appears. See if
+ // we can eliminate some of the created functions from being candidates.
+ DisambiguateGlobalSymbols(BD.getProgram());
+
+ // Do the reduction...
+ Ret = ReduceMiscompilingFunctions(BD, TestFn)
+ .reduceList(MiscompiledFunctions);
+ if (Error E = Ret.takeError())
+ return std::move(E);
+
+ outs() << "\n*** The following function"
+ << (MiscompiledFunctions.size() == 1 ? " is" : "s are")
+ << " being miscompiled: ";
+ PrintFunctionList(MiscompiledFunctions);
+ outs() << '\n';
+ }
+ }
+
+ return MiscompiledFunctions;
+}
+
+/// This is the predicate function used to check to see if the "Test" portion of
+/// the program is misoptimized. If so, return true. In any case, both module
+/// arguments are deleted.
+///
+static Expected<bool> TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test,
+ std::unique_ptr<Module> Safe) {
+ // Run the optimization passes on ToOptimize, producing a transformed version
+ // of the functions being tested.
+ outs() << " Optimizing functions being tested: ";
+ std::unique_ptr<Module> Optimized =
+ BD.runPassesOn(Test.get(), BD.getPassesToRun());
+ if (!Optimized) {
+ errs() << " Error running this sequence of passes"
+ << " on the input program!\n";
+ BD.EmitProgressBitcode(*Test, "pass-error", false);
+ BD.setNewProgram(std::move(Test));
+ if (Error E = BD.debugOptimizerCrash())
+ return std::move(E);
+ return false;
+ }
+ outs() << "done.\n";
+
+ outs() << " Checking to see if the merged program executes correctly: ";
+ bool Broken;
+ auto Result = testMergedProgram(BD, *Optimized, *Safe, Broken);
+ if (Error E = Result.takeError())
+ return std::move(E);
+ if (auto New = std::move(*Result)) {
+ outs() << (Broken ? " nope.\n" : " yup.\n");
+ // Delete the original and set the new program.
+ BD.setNewProgram(std::move(New));
+ }
+ return Broken;
+}
+
+/// debugMiscompilation - This method is used when the passes selected are not
+/// crashing, but the generated output is semantically different from the
+/// input.
+///
+Error BugDriver::debugMiscompilation() {
+ // Make sure something was miscompiled...
+ if (!BugpointIsInterrupted) {
+ Expected<bool> Result =
+ ReduceMiscompilingPasses(*this).reduceList(PassesToRun);
+ if (Error E = Result.takeError())
+ return E;
+ if (!*Result)
+ return make_error<StringError>(
+ "*** Optimized program matches reference output! No problem"
+ " detected...\nbugpoint can't help you with your problem!\n",
+ inconvertibleErrorCode());
+ }
+
+ outs() << "\n*** Found miscompiling pass"
+ << (getPassesToRun().size() == 1 ? "" : "es") << ": "
+ << getPassesString(getPassesToRun()) << '\n';
+ EmitProgressBitcode(*Program, "passinput");
+
+ Expected<std::vector<Function *>> MiscompiledFunctions =
+ DebugAMiscompilation(*this, TestOptimizer);
+ if (Error E = MiscompiledFunctions.takeError())
+ return E;
+
+ // Output a bunch of bitcode files for the user...
+ outs() << "Outputting reduced bitcode files which expose the problem:\n";
+ ValueToValueMapTy VMap;
+ Module *ToNotOptimize = CloneModule(getProgram(), VMap).release();
+ Module *ToOptimize =
+ SplitFunctionsOutOfModule(ToNotOptimize, *MiscompiledFunctions, VMap)
+ .release();
+
+ outs() << " Non-optimized portion: ";
+ EmitProgressBitcode(*ToNotOptimize, "tonotoptimize", true);
+ delete ToNotOptimize; // Delete hacked module.
+
+ outs() << " Portion that is input to optimizer: ";
+ EmitProgressBitcode(*ToOptimize, "tooptimize");
+ delete ToOptimize; // Delete hacked module.
+
+ return Error::success();
+}
+
+/// Get the specified modules ready for code generator testing.
+///
+static std::unique_ptr<Module>
+CleanupAndPrepareModules(BugDriver &BD, std::unique_ptr<Module> Test,
+ Module *Safe) {
+ // Clean up the modules, removing extra cruft that we don't need anymore...
+ Test = BD.performFinalCleanups(std::move(Test));
+
+ // If we are executing the JIT, we have several nasty issues to take care of.
+ if (!BD.isExecutingJIT())
+ return Test;
+
+ // First, if the main function is in the Safe module, we must add a stub to
+ // the Test module to call into it. Thus, we create a new function `main'
+ // which just calls the old one.
+ if (Function *oldMain = Safe->getFunction("main"))
+ if (!oldMain->isDeclaration()) {
+ // Rename it
+ oldMain->setName("llvm_bugpoint_old_main");
+ // Create a NEW `main' function with same type in the test module.
+ Function *newMain =
+ Function::Create(oldMain->getFunctionType(),
+ GlobalValue::ExternalLinkage, "main", Test.get());
+ // Create an `oldmain' prototype in the test module, which will
+ // corresponds to the real main function in the same module.
+ Function *oldMainProto = Function::Create(oldMain->getFunctionType(),
+ GlobalValue::ExternalLinkage,
+ oldMain->getName(), Test.get());
+ // Set up and remember the argument list for the main function.
+ std::vector<Value *> args;
+ for (Function::arg_iterator I = newMain->arg_begin(),
+ E = newMain->arg_end(),
+ OI = oldMain->arg_begin();
+ I != E; ++I, ++OI) {
+ I->setName(OI->getName()); // Copy argument names from oldMain
+ args.push_back(&*I);
+ }
+
+ // Call the old main function and return its result
+ BasicBlock *BB = BasicBlock::Create(Safe->getContext(), "entry", newMain);
+ CallInst *call = CallInst::Create(oldMainProto, args, "", BB);
+
+ // If the type of old function wasn't void, return value of call
+ ReturnInst::Create(Safe->getContext(), call, BB);
+ }
+
+ // The second nasty issue we must deal with in the JIT is that the Safe
+ // module cannot directly reference any functions defined in the test
+ // module. Instead, we use a JIT API call to dynamically resolve the
+ // symbol.
+
+ // Add the resolver to the Safe module.
+ // Prototype: void *getPointerToNamedFunction(const char* Name)
+ FunctionCallee resolverFunc = Safe->getOrInsertFunction(
+ "getPointerToNamedFunction", Type::getInt8PtrTy(Safe->getContext()),
+ Type::getInt8PtrTy(Safe->getContext()));
+
+ // Use the function we just added to get addresses of functions we need.
+ for (Module::iterator F = Safe->begin(), E = Safe->end(); F != E; ++F) {
+ if (F->isDeclaration() && !F->use_empty() &&
+ &*F != resolverFunc.getCallee() &&
+ !F->isIntrinsic() /* ignore intrinsics */) {
+ Function *TestFn = Test->getFunction(F->getName());
+
+ // Don't forward functions which are external in the test module too.
+ if (TestFn && !TestFn->isDeclaration()) {
+ // 1. Add a string constant with its name to the global file
+ Constant *InitArray =
+ ConstantDataArray::getString(F->getContext(), F->getName());
+ GlobalVariable *funcName = new GlobalVariable(
+ *Safe, InitArray->getType(), true /*isConstant*/,
+ GlobalValue::InternalLinkage, InitArray, F->getName() + "_name");
+
+ // 2. Use `GetElementPtr *funcName, 0, 0' to convert the string to an
+ // sbyte* so it matches the signature of the resolver function.
+
+ // GetElementPtr *funcName, ulong 0, ulong 0
+ std::vector<Constant *> GEPargs(
+ 2, Constant::getNullValue(Type::getInt32Ty(F->getContext())));
+ Value *GEP = ConstantExpr::getGetElementPtr(InitArray->getType(),
+ funcName, GEPargs);
+ std::vector<Value *> ResolverArgs;
+ ResolverArgs.push_back(GEP);
+
+ // Rewrite uses of F in global initializers, etc. to uses of a wrapper
+ // function that dynamically resolves the calls to F via our JIT API
+ if (!F->use_empty()) {
+ // Create a new global to hold the cached function pointer.
+ Constant *NullPtr = ConstantPointerNull::get(F->getType());
+ GlobalVariable *Cache = new GlobalVariable(
+ *F->getParent(), F->getType(), false,
+ GlobalValue::InternalLinkage, NullPtr, F->getName() + ".fpcache");
+
+ // Construct a new stub function that will re-route calls to F
+ FunctionType *FuncTy = F->getFunctionType();
+ Function *FuncWrapper =
+ Function::Create(FuncTy, GlobalValue::InternalLinkage,
+ F->getName() + "_wrapper", F->getParent());
+ BasicBlock *EntryBB =
+ BasicBlock::Create(F->getContext(), "entry", FuncWrapper);
+ BasicBlock *DoCallBB =
+ BasicBlock::Create(F->getContext(), "usecache", FuncWrapper);
+ BasicBlock *LookupBB =
+ BasicBlock::Create(F->getContext(), "lookupfp", FuncWrapper);
+
+ // Check to see if we already looked up the value.
+ Value *CachedVal =
+ new LoadInst(F->getType(), Cache, "fpcache", EntryBB);
+ Value *IsNull = new ICmpInst(*EntryBB, ICmpInst::ICMP_EQ, CachedVal,
+ NullPtr, "isNull");
+ BranchInst::Create(LookupBB, DoCallBB, IsNull, EntryBB);
+
+ // Resolve the call to function F via the JIT API:
+ //
+ // call resolver(GetElementPtr...)
+ CallInst *Resolver = CallInst::Create(resolverFunc, ResolverArgs,
+ "resolver", LookupBB);
+
+ // Cast the result from the resolver to correctly-typed function.
+ CastInst *CastedResolver = new BitCastInst(
+ Resolver, PointerType::getUnqual(F->getFunctionType()),
+ "resolverCast", LookupBB);
+
+ // Save the value in our cache.
+ new StoreInst(CastedResolver, Cache, LookupBB);
+ BranchInst::Create(DoCallBB, LookupBB);
+
+ PHINode *FuncPtr =
+ PHINode::Create(NullPtr->getType(), 2, "fp", DoCallBB);
+ FuncPtr->addIncoming(CastedResolver, LookupBB);
+ FuncPtr->addIncoming(CachedVal, EntryBB);
+
+ // Save the argument list.
+ std::vector<Value *> Args;
+ for (Argument &A : FuncWrapper->args())
+ Args.push_back(&A);
+
+ // Pass on the arguments to the real function, return its result
+ if (F->getReturnType()->isVoidTy()) {
+ CallInst::Create(FuncTy, FuncPtr, Args, "", DoCallBB);
+ ReturnInst::Create(F->getContext(), DoCallBB);
+ } else {
+ CallInst *Call =
+ CallInst::Create(FuncTy, FuncPtr, Args, "retval", DoCallBB);
+ ReturnInst::Create(F->getContext(), Call, DoCallBB);
+ }
+
+ // Use the wrapper function instead of the old function
+ F->replaceAllUsesWith(FuncWrapper);
+ }
+ }
+ }
+ }
+
+ if (verifyModule(*Test) || verifyModule(*Safe)) {
+ errs() << "Bugpoint has a bug, which corrupted a module!!\n";
+ abort();
+ }
+
+ return Test;
+}
+
+/// This is the predicate function used to check to see if the "Test" portion of
+/// the program is miscompiled by the code generator under test. If so, return
+/// true. In any case, both module arguments are deleted.
+///
+static Expected<bool> TestCodeGenerator(BugDriver &BD,
+ std::unique_ptr<Module> Test,
+ std::unique_ptr<Module> Safe) {
+ Test = CleanupAndPrepareModules(BD, std::move(Test), Safe.get());
+
+ SmallString<128> TestModuleBC;
+ int TestModuleFD;
+ std::error_code EC = sys::fs::createTemporaryFile("bugpoint.test", "bc",
+ TestModuleFD, TestModuleBC);
+ if (EC) {
+ errs() << BD.getToolName()
+ << "Error making unique filename: " << EC.message() << "\n";
+ exit(1);
+ }
+ if (BD.writeProgramToFile(std::string(TestModuleBC.str()), TestModuleFD,
+ *Test)) {
+ errs() << "Error writing bitcode to `" << TestModuleBC.str()
+ << "'\nExiting.";
+ exit(1);
+ }
+
+ FileRemover TestModuleBCRemover(TestModuleBC.str(), !SaveTemps);
+
+ // Make the shared library
+ SmallString<128> SafeModuleBC;
+ int SafeModuleFD;
+ EC = sys::fs::createTemporaryFile("bugpoint.safe", "bc", SafeModuleFD,
+ SafeModuleBC);
+ if (EC) {
+ errs() << BD.getToolName()
+ << "Error making unique filename: " << EC.message() << "\n";
+ exit(1);
+ }
+
+ if (BD.writeProgramToFile(std::string(SafeModuleBC.str()), SafeModuleFD,
+ *Safe)) {
+ errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting.";
+ exit(1);
+ }
+
+ FileRemover SafeModuleBCRemover(SafeModuleBC.str(), !SaveTemps);
+
+ Expected<std::string> SharedObject =
+ BD.compileSharedObject(std::string(SafeModuleBC.str()));
+ if (Error E = SharedObject.takeError())
+ return std::move(E);
+
+ FileRemover SharedObjectRemover(*SharedObject, !SaveTemps);
+
+ // Run the code generator on the `Test' code, loading the shared library.
+ // The function returns whether or not the new output differs from reference.
+ Expected<bool> Result = BD.diffProgram(
+ BD.getProgram(), std::string(TestModuleBC.str()), *SharedObject, false);
+ if (Error E = Result.takeError())
+ return std::move(E);
+
+ if (*Result)
+ errs() << ": still failing!\n";
+ else
+ errs() << ": didn't fail.\n";
+
+ return Result;
+}
+
+/// debugCodeGenerator - debug errors in LLC, LLI, or CBE.
+///
+Error BugDriver::debugCodeGenerator() {
+ if ((void *)SafeInterpreter == (void *)Interpreter) {
+ Expected<std::string> Result =
+ executeProgramSafely(*Program, "bugpoint.safe.out");
+ if (Result) {
+ outs() << "\n*** The \"safe\" i.e. 'known good' backend cannot match "
+ << "the reference diff. This may be due to a\n front-end "
+ << "bug or a bug in the original program, but this can also "
+ << "happen if bugpoint isn't running the program with the "
+ << "right flags or input.\n I left the result of executing "
+ << "the program with the \"safe\" backend in this file for "
+ << "you: '" << *Result << "'.\n";
+ }
+ return Error::success();
+ }
+
+ DisambiguateGlobalSymbols(*Program);
+
+ Expected<std::vector<Function *>> Funcs =
+ DebugAMiscompilation(*this, TestCodeGenerator);
+ if (Error E = Funcs.takeError())
+ return E;
+
+ // Split the module into the two halves of the program we want.
+ ValueToValueMapTy VMap;
+ std::unique_ptr<Module> ToNotCodeGen = CloneModule(getProgram(), VMap);
+ std::unique_ptr<Module> ToCodeGen =
+ SplitFunctionsOutOfModule(ToNotCodeGen.get(), *Funcs, VMap);
+
+ // Condition the modules
+ ToCodeGen =
+ CleanupAndPrepareModules(*this, std::move(ToCodeGen), ToNotCodeGen.get());
+
+ SmallString<128> TestModuleBC;
+ int TestModuleFD;
+ std::error_code EC = sys::fs::createTemporaryFile("bugpoint.test", "bc",
+ TestModuleFD, TestModuleBC);
+ if (EC) {
+ errs() << getToolName() << "Error making unique filename: " << EC.message()
+ << "\n";
+ exit(1);
+ }
+
+ if (writeProgramToFile(std::string(TestModuleBC.str()), TestModuleFD,
+ *ToCodeGen)) {
+ errs() << "Error writing bitcode to `" << TestModuleBC << "'\nExiting.";
+ exit(1);
+ }
+
+ // Make the shared library
+ SmallString<128> SafeModuleBC;
+ int SafeModuleFD;
+ EC = sys::fs::createTemporaryFile("bugpoint.safe", "bc", SafeModuleFD,
+ SafeModuleBC);
+ if (EC) {
+ errs() << getToolName() << "Error making unique filename: " << EC.message()
+ << "\n";
+ exit(1);
+ }
+
+ if (writeProgramToFile(std::string(SafeModuleBC.str()), SafeModuleFD,
+ *ToNotCodeGen)) {
+ errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting.";
+ exit(1);
+ }
+ Expected<std::string> SharedObject =
+ compileSharedObject(std::string(SafeModuleBC.str()));
+ if (Error E = SharedObject.takeError())
+ return E;
+
+ outs() << "You can reproduce the problem with the command line: \n";
+ if (isExecutingJIT()) {
+ outs() << " lli -load " << *SharedObject << " " << TestModuleBC;
+ } else {
+ outs() << " llc " << TestModuleBC << " -o " << TestModuleBC << ".s\n";
+ outs() << " cc " << *SharedObject << " " << TestModuleBC.str() << ".s -o "
+ << TestModuleBC << ".exe\n";
+ outs() << " ./" << TestModuleBC << ".exe";
+ }
+ for (unsigned i = 0, e = InputArgv.size(); i != e; ++i)
+ outs() << " " << InputArgv[i];
+ outs() << '\n';
+ outs() << "The shared object was created with:\n llc -march=c "
+ << SafeModuleBC.str() << " -o temporary.c\n"
+ << " cc -xc temporary.c -O2 -o " << *SharedObject;
+ if (TargetTriple.getArch() == Triple::sparc)
+ outs() << " -G"; // Compile a shared library, `-G' for Sparc
+ else
+ outs() << " -fPIC -shared"; // `-shared' for Linux/X86, maybe others
+
+ outs() << " -fno-strict-aliasing\n";
+
+ return Error::success();
+}
diff --git a/contrib/libs/llvm14/tools/bugpoint/OptimizerDriver.cpp b/contrib/libs/llvm14/tools/bugpoint/OptimizerDriver.cpp
new file mode 100644
index 00000000000..e67e877c13a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/OptimizerDriver.cpp
@@ -0,0 +1,287 @@
+//===- OptimizerDriver.cpp - Allow BugPoint to run passes safely ----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines an interface that allows bugpoint to run various passes
+// without the threat of a buggy pass corrupting bugpoint (of course, bugpoint
+// may have its own bugs, but that's another story...). It achieves this by
+// forking a copy of itself and having the child process do the optimizations.
+// If this client dies, we can always fork a new one. :)
+//
+//===----------------------------------------------------------------------===//
+
+#include "BugDriver.h"
+#include "ToolRunner.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+#define DONT_GET_PLUGIN_LOADER_OPTION
+#include "llvm/Support/PluginLoader.h"
+
+
+using namespace llvm;
+
+#define DEBUG_TYPE "bugpoint"
+
+namespace llvm {
+extern cl::opt<std::string> OutputPrefix;
+}
+
+static cl::opt<bool> PreserveBitcodeUseListOrder(
+ "preserve-bc-uselistorder",
+ cl::desc("Preserve use-list order when writing LLVM bitcode."),
+ cl::init(true), cl::Hidden);
+
+static cl::opt<std::string>
+ OptCmd("opt-command", cl::init(""),
+ cl::desc("Path to opt. (default: search path "
+ "for 'opt'.)"));
+
+/// This writes the current "Program" to the named bitcode file. If an error
+/// occurs, true is returned.
+static bool writeProgramToFileAux(ToolOutputFile &Out, const Module &M) {
+ WriteBitcodeToFile(M, Out.os(), PreserveBitcodeUseListOrder);
+ Out.os().close();
+ if (!Out.os().has_error()) {
+ Out.keep();
+ return false;
+ }
+ return true;
+}
+
+bool BugDriver::writeProgramToFile(const std::string &Filename, int FD,
+ const Module &M) const {
+ ToolOutputFile Out(Filename, FD);
+ return writeProgramToFileAux(Out, M);
+}
+
+bool BugDriver::writeProgramToFile(int FD, const Module &M) const {
+ raw_fd_ostream OS(FD, /*shouldClose*/ false);
+ WriteBitcodeToFile(M, OS, PreserveBitcodeUseListOrder);
+ OS.flush();
+ if (!OS.has_error())
+ return false;
+ OS.clear_error();
+ return true;
+}
+
+bool BugDriver::writeProgramToFile(const std::string &Filename,
+ const Module &M) const {
+ std::error_code EC;
+ ToolOutputFile Out(Filename, EC, sys::fs::OF_None);
+ if (!EC)
+ return writeProgramToFileAux(Out, M);
+ return true;
+}
+
+/// This function is used to output the current Program to a file named
+/// "bugpoint-ID.bc".
+void BugDriver::EmitProgressBitcode(const Module &M, const std::string &ID,
+ bool NoFlyer) const {
+ // Output the input to the current pass to a bitcode file, emit a message
+ // telling the user how to reproduce it: opt -foo blah.bc
+ //
+ std::string Filename = OutputPrefix + "-" + ID + ".bc";
+ if (writeProgramToFile(Filename, M)) {
+ errs() << "Error opening file '" << Filename << "' for writing!\n";
+ return;
+ }
+
+ outs() << "Emitted bitcode to '" << Filename << "'\n";
+ if (NoFlyer || PassesToRun.empty())
+ return;
+ outs() << "\n*** You can reproduce the problem with: ";
+ if (UseValgrind)
+ outs() << "valgrind ";
+ outs() << "opt " << Filename;
+ for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {
+ outs() << " -load " << PluginLoader::getPlugin(i);
+ }
+ outs() << " " << getPassesString(PassesToRun) << "\n";
+}
+
+cl::opt<bool> SilencePasses(
+ "silence-passes",
+ cl::desc("Suppress output of running passes (both stdout and stderr)"));
+
+static cl::list<std::string> OptArgs("opt-args", cl::Positional,
+ cl::desc("<opt arguments>..."),
+ cl::ZeroOrMore, cl::PositionalEatsArgs);
+
+/// runPasses - Run the specified passes on Program, outputting a bitcode file
+/// and writing the filename into OutputFile if successful. If the
+/// optimizations fail for some reason (optimizer crashes), return true,
+/// otherwise return false. If DeleteOutput is set to true, the bitcode is
+/// deleted on success, and the filename string is undefined. This prints to
+/// outs() a single line message indicating whether compilation was successful
+/// or failed.
+///
+bool BugDriver::runPasses(Module &Program,
+ const std::vector<std::string> &Passes,
+ std::string &OutputFilename, bool DeleteOutput,
+ bool Quiet, ArrayRef<std::string> ExtraArgs) const {
+ // setup the output file name
+ outs().flush();
+ SmallString<128> UniqueFilename;
+ std::error_code EC = sys::fs::createUniqueFile(
+ OutputPrefix + "-output-%%%%%%%.bc", UniqueFilename);
+ if (EC) {
+ errs() << getToolName()
+ << ": Error making unique filename: " << EC.message() << "\n";
+ return true;
+ }
+ OutputFilename = std::string(UniqueFilename.str());
+
+ // set up the input file name
+ Expected<sys::fs::TempFile> Temp =
+ sys::fs::TempFile::create(OutputPrefix + "-input-%%%%%%%.bc");
+ if (!Temp) {
+ errs() << getToolName()
+ << ": Error making unique filename: " << toString(Temp.takeError())
+ << "\n";
+ return true;
+ }
+ DiscardTemp Discard{*Temp};
+ raw_fd_ostream OS(Temp->FD, /*shouldClose*/ false);
+
+ WriteBitcodeToFile(Program, OS, PreserveBitcodeUseListOrder);
+ OS.flush();
+ if (OS.has_error()) {
+ errs() << "Error writing bitcode file: " << Temp->TmpName << "\n";
+ OS.clear_error();
+ return true;
+ }
+
+ std::string tool = OptCmd;
+ if (OptCmd.empty()) {
+ if (ErrorOr<std::string> Path =
+ FindProgramByName("opt", getToolName(), &OutputPrefix))
+ tool = *Path;
+ else
+ errs() << Path.getError().message() << "\n";
+ }
+ if (tool.empty()) {
+ errs() << "Cannot find `opt' in PATH!\n";
+ return true;
+ }
+ if (!sys::fs::exists(tool)) {
+ errs() << "Specified `opt' binary does not exist: " << tool << "\n";
+ return true;
+ }
+
+ std::string Prog;
+ if (UseValgrind) {
+ if (ErrorOr<std::string> Path = sys::findProgramByName("valgrind"))
+ Prog = *Path;
+ else
+ errs() << Path.getError().message() << "\n";
+ } else
+ Prog = tool;
+ if (Prog.empty()) {
+ errs() << "Cannot find `valgrind' in PATH!\n";
+ return true;
+ }
+
+ // setup the child process' arguments
+ SmallVector<StringRef, 8> Args;
+ if (UseValgrind) {
+ Args.push_back("valgrind");
+ Args.push_back("--error-exitcode=1");
+ Args.push_back("-q");
+ Args.push_back(tool);
+ } else
+ Args.push_back(tool);
+
+ for (unsigned i = 0, e = OptArgs.size(); i != e; ++i)
+ Args.push_back(OptArgs[i]);
+ // Pin to legacy PM since bugpoint has lots of infra and hacks revolving
+ // around the legacy PM.
+ Args.push_back("-enable-new-pm=0");
+ Args.push_back("-disable-symbolication");
+ Args.push_back("-o");
+ Args.push_back(OutputFilename);
+ std::vector<std::string> pass_args;
+ for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {
+ pass_args.push_back(std::string("-load"));
+ pass_args.push_back(PluginLoader::getPlugin(i));
+ }
+ for (std::vector<std::string>::const_iterator I = Passes.begin(),
+ E = Passes.end();
+ I != E; ++I)
+ pass_args.push_back(std::string("-") + (*I));
+ for (std::vector<std::string>::const_iterator I = pass_args.begin(),
+ E = pass_args.end();
+ I != E; ++I)
+ Args.push_back(*I);
+ Args.push_back(Temp->TmpName);
+ Args.append(ExtraArgs.begin(), ExtraArgs.end());
+
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) errs()
+ << " " << Args[i];
+ errs() << "\n";);
+
+ Optional<StringRef> Redirects[3] = {None, None, None};
+ // Redirect stdout and stderr to nowhere if SilencePasses is given.
+ if (SilencePasses) {
+ Redirects[1] = "";
+ Redirects[2] = "";
+ }
+
+ std::string ErrMsg;
+ int result = sys::ExecuteAndWait(Prog, Args, None, Redirects, Timeout,
+ MemoryLimit, &ErrMsg);
+
+ // If we are supposed to delete the bitcode file or if the passes crashed,
+ // remove it now. This may fail if the file was never created, but that's ok.
+ if (DeleteOutput || result != 0)
+ sys::fs::remove(OutputFilename);
+
+ if (!Quiet) {
+ if (result == 0)
+ outs() << "Success!\n";
+ else if (result > 0)
+ outs() << "Exited with error code '" << result << "'\n";
+ else if (result < 0) {
+ if (result == -1)
+ outs() << "Execute failed: " << ErrMsg << "\n";
+ else
+ outs() << "Crashed: " << ErrMsg << "\n";
+ }
+ if (result & 0x01000000)
+ outs() << "Dumped core\n";
+ }
+
+ // Was the child successful?
+ return result != 0;
+}
+
+std::unique_ptr<Module>
+BugDriver::runPassesOn(Module *M, const std::vector<std::string> &Passes,
+ ArrayRef<std::string> ExtraArgs) {
+ std::string BitcodeResult;
+ if (runPasses(*M, Passes, BitcodeResult, false /*delete*/, true /*quiet*/,
+ ExtraArgs)) {
+ return nullptr;
+ }
+
+ std::unique_ptr<Module> Ret = parseInputFile(BitcodeResult, Context);
+ if (!Ret) {
+ errs() << getToolName() << ": Error reading bitcode file '" << BitcodeResult
+ << "'!\n";
+ exit(1);
+ }
+ sys::fs::remove(BitcodeResult);
+ return Ret;
+}
diff --git a/contrib/libs/llvm14/tools/bugpoint/ToolRunner.cpp b/contrib/libs/llvm14/tools/bugpoint/ToolRunner.cpp
new file mode 100644
index 00000000000..d3111e574e7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/ToolRunner.cpp
@@ -0,0 +1,865 @@
+//===-- ToolRunner.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the interfaces described in the ToolRunner.h file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ToolRunner.h"
+#include "llvm/Config/config.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/raw_ostream.h"
+#include <fstream>
+#include <sstream>
+#include <utility>
+using namespace llvm;
+
+#define DEBUG_TYPE "toolrunner"
+
+namespace llvm {
+cl::opt<bool> SaveTemps("save-temps", cl::init(false),
+ cl::desc("Save temporary files"));
+}
+
+namespace {
+cl::opt<std::string>
+ RemoteClient("remote-client",
+ cl::desc("Remote execution client (rsh/ssh)"));
+
+cl::opt<std::string> RemoteHost("remote-host",
+ cl::desc("Remote execution (rsh/ssh) host"));
+
+cl::opt<std::string> RemotePort("remote-port",
+ cl::desc("Remote execution (rsh/ssh) port"));
+
+cl::opt<std::string> RemoteUser("remote-user",
+ cl::desc("Remote execution (rsh/ssh) user id"));
+
+cl::opt<std::string>
+ RemoteExtra("remote-extra-options",
+ cl::desc("Remote execution (rsh/ssh) extra options"));
+}
+
+/// RunProgramWithTimeout - This function provides an alternate interface
+/// to the sys::Program::ExecuteAndWait interface.
+/// @see sys::Program::ExecuteAndWait
+static int RunProgramWithTimeout(StringRef ProgramPath,
+ ArrayRef<StringRef> Args, StringRef StdInFile,
+ StringRef StdOutFile, StringRef StdErrFile,
+ unsigned NumSeconds = 0,
+ unsigned MemoryLimit = 0,
+ std::string *ErrMsg = nullptr) {
+ Optional<StringRef> Redirects[3] = {StdInFile, StdOutFile, StdErrFile};
+ return sys::ExecuteAndWait(ProgramPath, Args, None, Redirects, NumSeconds,
+ MemoryLimit, ErrMsg);
+}
+
+/// RunProgramRemotelyWithTimeout - This function runs the given program
+/// remotely using the given remote client and the sys::Program::ExecuteAndWait.
+/// Returns the remote program exit code or reports a remote client error if it
+/// fails. Remote client is required to return 255 if it failed or program exit
+/// code otherwise.
+/// @see sys::Program::ExecuteAndWait
+static int RunProgramRemotelyWithTimeout(
+ StringRef RemoteClientPath, ArrayRef<StringRef> Args, StringRef StdInFile,
+ StringRef StdOutFile, StringRef StdErrFile, unsigned NumSeconds = 0,
+ unsigned MemoryLimit = 0) {
+ Optional<StringRef> Redirects[3] = {StdInFile, StdOutFile, StdErrFile};
+
+ // Run the program remotely with the remote client
+ int ReturnCode = sys::ExecuteAndWait(RemoteClientPath, Args, None, Redirects,
+ NumSeconds, MemoryLimit);
+
+ // Has the remote client fail?
+ if (255 == ReturnCode) {
+ std::ostringstream OS;
+ OS << "\nError running remote client:\n ";
+ for (StringRef Arg : Args)
+ OS << " " << Arg.str();
+ OS << "\n";
+
+ // The error message is in the output file, let's print it out from there.
+ std::string StdOutFileName = StdOutFile.str();
+ std::ifstream ErrorFile(StdOutFileName.c_str());
+ if (ErrorFile) {
+ std::copy(std::istreambuf_iterator<char>(ErrorFile),
+ std::istreambuf_iterator<char>(),
+ std::ostreambuf_iterator<char>(OS));
+ ErrorFile.close();
+ }
+
+ errs() << OS.str();
+ }
+
+ return ReturnCode;
+}
+
+static Error ProcessFailure(StringRef ProgPath, ArrayRef<StringRef> Args,
+ unsigned Timeout = 0, unsigned MemoryLimit = 0) {
+ std::ostringstream OS;
+ OS << "\nError running tool:\n ";
+ for (StringRef Arg : Args)
+ OS << " " << Arg.str();
+ OS << "\n";
+
+ // Rerun the compiler, capturing any error messages to print them.
+ SmallString<128> ErrorFilename;
+ std::error_code EC = sys::fs::createTemporaryFile(
+ "bugpoint.program_error_messages", "", ErrorFilename);
+ if (EC) {
+ errs() << "Error making unique filename: " << EC.message() << "\n";
+ exit(1);
+ }
+
+ RunProgramWithTimeout(ProgPath, Args, "", ErrorFilename.str(),
+ ErrorFilename.str(), Timeout, MemoryLimit);
+ // FIXME: check return code ?
+
+ // Print out the error messages generated by CC if possible...
+ std::ifstream ErrorFile(ErrorFilename.c_str());
+ if (ErrorFile) {
+ std::copy(std::istreambuf_iterator<char>(ErrorFile),
+ std::istreambuf_iterator<char>(),
+ std::ostreambuf_iterator<char>(OS));
+ ErrorFile.close();
+ }
+
+ sys::fs::remove(ErrorFilename.c_str());
+ return make_error<StringError>(OS.str(), inconvertibleErrorCode());
+}
+
+//===---------------------------------------------------------------------===//
+// LLI Implementation of AbstractIntepreter interface
+//
+namespace {
+class LLI : public AbstractInterpreter {
+ std::string LLIPath; // The path to the LLI executable
+ std::vector<std::string> ToolArgs; // Args to pass to LLI
+public:
+ LLI(const std::string &Path, const std::vector<std::string> *Args)
+ : LLIPath(Path) {
+ ToolArgs.clear();
+ if (Args) {
+ ToolArgs = *Args;
+ }
+ }
+
+ Expected<int> ExecuteProgram(
+ const std::string &Bitcode, const std::vector<std::string> &Args,
+ const std::string &InputFile, const std::string &OutputFile,
+ const std::vector<std::string> &CCArgs,
+ const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
+ unsigned Timeout = 0, unsigned MemoryLimit = 0) override;
+};
+}
+
+Expected<int> LLI::ExecuteProgram(const std::string &Bitcode,
+ const std::vector<std::string> &Args,
+ const std::string &InputFile,
+ const std::string &OutputFile,
+ const std::vector<std::string> &CCArgs,
+ const std::vector<std::string> &SharedLibs,
+ unsigned Timeout, unsigned MemoryLimit) {
+ std::vector<StringRef> LLIArgs;
+ LLIArgs.push_back(LLIPath);
+ LLIArgs.push_back("-force-interpreter=true");
+
+ for (std::vector<std::string>::const_iterator i = SharedLibs.begin(),
+ e = SharedLibs.end();
+ i != e; ++i) {
+ LLIArgs.push_back("-load");
+ LLIArgs.push_back(*i);
+ }
+
+ // Add any extra LLI args.
+ for (unsigned i = 0, e = ToolArgs.size(); i != e; ++i)
+ LLIArgs.push_back(ToolArgs[i]);
+
+ LLIArgs.push_back(Bitcode);
+ // Add optional parameters to the running program from Argv
+ for (unsigned i = 0, e = Args.size(); i != e; ++i)
+ LLIArgs.push_back(Args[i]);
+
+ outs() << "<lli>";
+ outs().flush();
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = LLIArgs.size(); i != e; ++i) errs()
+ << " " << LLIArgs[i];
+ errs() << "\n";);
+ return RunProgramWithTimeout(LLIPath, LLIArgs, InputFile, OutputFile,
+ OutputFile, Timeout, MemoryLimit);
+}
+
+void AbstractInterpreter::anchor() {}
+
+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
+ // this if ProgramPath contains at least one / character, indicating that it
+ // 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;
+
+ // 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) {
+ 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;
+ }
+}
+
+//===---------------------------------------------------------------------===//
+// Custom compiler command implementation of AbstractIntepreter interface
+//
+// Allows using a custom command for compiling the bitcode, thus allows, for
+// example, to compile a bitcode fragment without linking or executing, then
+// using a custom wrapper script to check for compiler errors.
+namespace {
+class CustomCompiler : public AbstractInterpreter {
+ std::string CompilerCommand;
+ std::vector<std::string> CompilerArgs;
+
+public:
+ CustomCompiler(const std::string &CompilerCmd,
+ std::vector<std::string> CompArgs)
+ : CompilerCommand(CompilerCmd), CompilerArgs(std::move(CompArgs)) {}
+
+ Error compileProgram(const std::string &Bitcode, unsigned Timeout = 0,
+ unsigned MemoryLimit = 0) override;
+
+ Expected<int> ExecuteProgram(
+ const std::string &Bitcode, const std::vector<std::string> &Args,
+ const std::string &InputFile, const std::string &OutputFile,
+ const std::vector<std::string> &CCArgs = std::vector<std::string>(),
+ const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
+ unsigned Timeout = 0, unsigned MemoryLimit = 0) override {
+ return make_error<StringError>(
+ "Execution not supported with -compile-custom",
+ inconvertibleErrorCode());
+ }
+};
+}
+
+Error CustomCompiler::compileProgram(const std::string &Bitcode,
+ unsigned Timeout, unsigned MemoryLimit) {
+
+ std::vector<StringRef> ProgramArgs;
+ ProgramArgs.push_back(CompilerCommand);
+
+ for (const auto &Arg : CompilerArgs)
+ ProgramArgs.push_back(Arg);
+ ProgramArgs.push_back(Bitcode);
+
+ // Add optional parameters to the running program from Argv
+ for (const auto &Arg : CompilerArgs)
+ ProgramArgs.push_back(Arg);
+
+ if (RunProgramWithTimeout(CompilerCommand, ProgramArgs, "", "", "", Timeout,
+ MemoryLimit))
+ return ProcessFailure(CompilerCommand, ProgramArgs, Timeout, MemoryLimit);
+ return Error::success();
+}
+
+//===---------------------------------------------------------------------===//
+// Custom execution command implementation of AbstractIntepreter interface
+//
+// Allows using a custom command for executing the bitcode, thus allows,
+// for example, to invoke a cross compiler for code generation followed by
+// a simulator that executes the generated binary.
+namespace {
+class CustomExecutor : public AbstractInterpreter {
+ std::string ExecutionCommand;
+ std::vector<std::string> ExecutorArgs;
+
+public:
+ CustomExecutor(const std::string &ExecutionCmd,
+ std::vector<std::string> ExecArgs)
+ : ExecutionCommand(ExecutionCmd), ExecutorArgs(std::move(ExecArgs)) {}
+
+ Expected<int> ExecuteProgram(
+ const std::string &Bitcode, const std::vector<std::string> &Args,
+ const std::string &InputFile, const std::string &OutputFile,
+ const std::vector<std::string> &CCArgs,
+ const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
+ unsigned Timeout = 0, unsigned MemoryLimit = 0) override;
+};
+}
+
+Expected<int> CustomExecutor::ExecuteProgram(
+ const std::string &Bitcode, const std::vector<std::string> &Args,
+ const std::string &InputFile, const std::string &OutputFile,
+ const std::vector<std::string> &CCArgs,
+ const std::vector<std::string> &SharedLibs, unsigned Timeout,
+ unsigned MemoryLimit) {
+
+ std::vector<StringRef> ProgramArgs;
+ ProgramArgs.push_back(ExecutionCommand);
+
+ for (std::size_t i = 0; i < ExecutorArgs.size(); ++i)
+ ProgramArgs.push_back(ExecutorArgs[i]);
+ ProgramArgs.push_back(Bitcode);
+
+ // Add optional parameters to the running program from Argv
+ for (unsigned i = 0, e = Args.size(); i != e; ++i)
+ ProgramArgs.push_back(Args[i]);
+
+ return RunProgramWithTimeout(ExecutionCommand, ProgramArgs, InputFile,
+ OutputFile, OutputFile, Timeout, MemoryLimit);
+}
+
+// Tokenize the CommandLine to the command and the args to allow
+// defining a full command line as the command instead of just the
+// executed program. We cannot just pass the whole string after the command
+// as a single argument because then the program sees only a single
+// command line argument (with spaces in it: "foo bar" instead
+// of "foo" and "bar").
+//
+// Spaces are used as a delimiter; however repeated, leading, and trailing
+// whitespace are ignored. Simple escaping is allowed via the '\'
+// character, as seen below:
+//
+// Two consecutive '\' evaluate to a single '\'.
+// A space after a '\' evaluates to a space that is not interpreted as a
+// delimiter.
+// Any other instances of the '\' character are removed.
+//
+// Example:
+// '\\' -> '\'
+// '\ ' -> ' '
+// 'exa\mple' -> 'example'
+//
+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;
+ bool FoundPath = false;
+
+ // first argument is the PATH.
+ // Skip repeated whitespace, leading whitespace and trailing whitespace.
+ for (std::size_t Pos = 0u; Pos <= CommandLine.size(); ++Pos) {
+ if ('\\' == CommandLine[Pos]) {
+ if (Pos + 1 < CommandLine.size())
+ Token.push_back(CommandLine[++Pos]);
+
+ continue;
+ }
+ if (' ' == CommandLine[Pos] || CommandLine.size() == Pos) {
+ if (Token.empty())
+ continue;
+
+ if (!FoundPath) {
+ Command = Token;
+ FoundPath = true;
+ Token.clear();
+ continue;
+ }
+
+ Args.push_back(Token);
+ Token.clear();
+ continue;
+ }
+ Token.push_back(CommandLine[Pos]);
+ }
+
+ auto Path = FindProgramByName(Command, Argv0, (void *)(intptr_t)&lexCommand);
+ if (!Path) {
+ Message = std::string("Cannot find '") + Command +
+ "' in PATH: " + Path.getError().message() + "\n";
+ return;
+ }
+ CmdPath = *Path;
+
+ Message = "Found command in: " + CmdPath + "\n";
+}
+
+// Custom execution environment create method, takes the execution command
+// as arguments
+AbstractInterpreter *AbstractInterpreter::createCustomCompiler(
+ const char *Argv0, std::string &Message,
+ const std::string &CompileCommandLine) {
+
+ std::string CmdPath;
+ std::vector<std::string> Args;
+ lexCommand(Argv0, Message, CompileCommandLine, CmdPath, Args);
+ if (CmdPath.empty())
+ return nullptr;
+
+ return new CustomCompiler(CmdPath, Args);
+}
+
+// Custom execution environment create method, takes the execution command
+// as arguments
+AbstractInterpreter *
+AbstractInterpreter::createCustomExecutor(const char *Argv0,
+ std::string &Message,
+ const std::string &ExecCommandLine) {
+
+ std::string CmdPath;
+ std::vector<std::string> Args;
+ lexCommand(Argv0, Message, ExecCommandLine, CmdPath, Args);
+ if (CmdPath.empty())
+ return nullptr;
+
+ return new CustomExecutor(CmdPath, Args);
+}
+
+//===----------------------------------------------------------------------===//
+// LLC Implementation of AbstractIntepreter interface
+//
+Expected<CC::FileType> LLC::OutputCode(const std::string &Bitcode,
+ std::string &OutputAsmFile,
+ unsigned Timeout, unsigned MemoryLimit) {
+ const char *Suffix = (UseIntegratedAssembler ? ".llc.o" : ".llc.s");
+
+ SmallString<128> UniqueFile;
+ std::error_code EC =
+ sys::fs::createUniqueFile(Bitcode + "-%%%%%%%" + Suffix, UniqueFile);
+ if (EC) {
+ errs() << "Error making unique filename: " << EC.message() << "\n";
+ exit(1);
+ }
+ OutputAsmFile = std::string(UniqueFile.str());
+ std::vector<StringRef> LLCArgs;
+ LLCArgs.push_back(LLCPath);
+
+ // Add any extra LLC args.
+ for (unsigned i = 0, e = ToolArgs.size(); i != e; ++i)
+ LLCArgs.push_back(ToolArgs[i]);
+
+ LLCArgs.push_back("-o");
+ LLCArgs.push_back(OutputAsmFile); // Output to the Asm file
+ LLCArgs.push_back(Bitcode); // This is the input bitcode
+
+ if (UseIntegratedAssembler)
+ LLCArgs.push_back("-filetype=obj");
+
+ outs() << (UseIntegratedAssembler ? "<llc-ia>" : "<llc>");
+ outs().flush();
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = LLCArgs.size(); i != e; ++i) errs()
+ << " " << LLCArgs[i];
+ errs() << "\n";);
+ if (RunProgramWithTimeout(LLCPath, LLCArgs, "", "", "", Timeout, MemoryLimit))
+ return ProcessFailure(LLCPath, LLCArgs, Timeout, MemoryLimit);
+ return UseIntegratedAssembler ? CC::ObjectFile : CC::AsmFile;
+}
+
+Error LLC::compileProgram(const std::string &Bitcode, unsigned Timeout,
+ unsigned MemoryLimit) {
+ std::string OutputAsmFile;
+ Expected<CC::FileType> Result =
+ OutputCode(Bitcode, OutputAsmFile, Timeout, MemoryLimit);
+ sys::fs::remove(OutputAsmFile);
+ if (Error E = Result.takeError())
+ return E;
+ return Error::success();
+}
+
+Expected<int> LLC::ExecuteProgram(const std::string &Bitcode,
+ const std::vector<std::string> &Args,
+ const std::string &InputFile,
+ const std::string &OutputFile,
+ const std::vector<std::string> &ArgsForCC,
+ const std::vector<std::string> &SharedLibs,
+ unsigned Timeout, unsigned MemoryLimit) {
+
+ std::string OutputAsmFile;
+ Expected<CC::FileType> FileKind =
+ OutputCode(Bitcode, OutputAsmFile, Timeout, MemoryLimit);
+ FileRemover OutFileRemover(OutputAsmFile, !SaveTemps);
+ if (Error E = FileKind.takeError())
+ return std::move(E);
+
+ std::vector<std::string> CCArgs(ArgsForCC);
+ llvm::append_range(CCArgs, SharedLibs);
+
+ // Assuming LLC worked, compile the result with CC and run it.
+ return cc->ExecuteProgram(OutputAsmFile, Args, *FileKind, InputFile,
+ OutputFile, CCArgs, Timeout, MemoryLimit);
+}
+
+/// createLLC - Try to find the LLC executable
+///
+LLC *AbstractInterpreter::createLLC(const char *Argv0, std::string &Message,
+ const std::string &CCBinary,
+ const std::vector<std::string> *Args,
+ const std::vector<std::string> *CCArgs,
+ bool UseIntegratedAssembler) {
+ ErrorOr<std::string> LLCPath =
+ FindProgramByName("llc", Argv0, (void *)(intptr_t)&createLLC);
+ if (!LLCPath) {
+ Message = LLCPath.getError().message() + "\n";
+ return nullptr;
+ }
+
+ 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);
+}
+
+//===---------------------------------------------------------------------===//
+// JIT Implementation of AbstractIntepreter interface
+//
+namespace {
+class JIT : public AbstractInterpreter {
+ std::string LLIPath; // The path to the LLI executable
+ std::vector<std::string> ToolArgs; // Args to pass to LLI
+public:
+ JIT(const std::string &Path, const std::vector<std::string> *Args)
+ : LLIPath(Path) {
+ ToolArgs.clear();
+ if (Args) {
+ ToolArgs = *Args;
+ }
+ }
+
+ Expected<int> ExecuteProgram(
+ const std::string &Bitcode, const std::vector<std::string> &Args,
+ const std::string &InputFile, const std::string &OutputFile,
+ const std::vector<std::string> &CCArgs = std::vector<std::string>(),
+ const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
+ unsigned Timeout = 0, unsigned MemoryLimit = 0) override;
+};
+}
+
+Expected<int> JIT::ExecuteProgram(const std::string &Bitcode,
+ const std::vector<std::string> &Args,
+ const std::string &InputFile,
+ const std::string &OutputFile,
+ const std::vector<std::string> &CCArgs,
+ const std::vector<std::string> &SharedLibs,
+ unsigned Timeout, unsigned MemoryLimit) {
+ // Construct a vector of parameters, incorporating those from the command-line
+ std::vector<StringRef> JITArgs;
+ JITArgs.push_back(LLIPath);
+ JITArgs.push_back("-force-interpreter=false");
+
+ // Add any extra LLI args.
+ for (unsigned i = 0, e = ToolArgs.size(); i != e; ++i)
+ JITArgs.push_back(ToolArgs[i]);
+
+ for (unsigned i = 0, e = SharedLibs.size(); i != e; ++i) {
+ JITArgs.push_back("-load");
+ JITArgs.push_back(SharedLibs[i]);
+ }
+ JITArgs.push_back(Bitcode);
+ // Add optional parameters to the running program from Argv
+ for (unsigned i = 0, e = Args.size(); i != e; ++i)
+ JITArgs.push_back(Args[i]);
+
+ outs() << "<jit>";
+ outs().flush();
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = JITArgs.size(); i != e; ++i) errs()
+ << " " << JITArgs[i];
+ errs() << "\n";);
+ LLVM_DEBUG(errs() << "\nSending output to " << OutputFile << "\n");
+ return RunProgramWithTimeout(LLIPath, JITArgs, InputFile, OutputFile,
+ OutputFile, Timeout, MemoryLimit);
+}
+
+/// createJIT - Try to find the LLI executable
+///
+AbstractInterpreter *
+AbstractInterpreter::createJIT(const char *Argv0, std::string &Message,
+ const std::vector<std::string> *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;
+ }
+}
+
+//===---------------------------------------------------------------------===//
+// CC abstraction
+//
+
+static bool IsARMArchitecture(std::vector<StringRef> Args) {
+ for (size_t I = 0; I < Args.size(); ++I) {
+ if (!Args[I].equals_insensitive("-arch"))
+ continue;
+ ++I;
+ if (I == Args.size())
+ break;
+ if (Args[I].startswith_insensitive("arm"))
+ return true;
+ }
+
+ return false;
+}
+
+Expected<int> CC::ExecuteProgram(const std::string &ProgramFile,
+ const std::vector<std::string> &Args,
+ FileType fileType,
+ const std::string &InputFile,
+ const std::string &OutputFile,
+ const std::vector<std::string> &ArgsForCC,
+ unsigned Timeout, unsigned MemoryLimit) {
+ std::vector<StringRef> CCArgs;
+
+ CCArgs.push_back(CCPath);
+
+ if (TargetTriple.getArch() == Triple::x86)
+ CCArgs.push_back("-m32");
+
+ for (std::vector<std::string>::const_iterator I = ccArgs.begin(),
+ E = ccArgs.end();
+ I != E; ++I)
+ CCArgs.push_back(*I);
+
+ // Specify -x explicitly in case the extension is wonky
+ if (fileType != ObjectFile) {
+ CCArgs.push_back("-x");
+ if (fileType == CFile) {
+ CCArgs.push_back("c");
+ CCArgs.push_back("-fno-strict-aliasing");
+ } else {
+ CCArgs.push_back("assembler");
+
+ // For ARM architectures we don't want this flag. bugpoint isn't
+ // explicitly told what architecture it is working on, so we get
+ // it from cc flags
+ if (TargetTriple.isOSDarwin() && !IsARMArchitecture(CCArgs))
+ CCArgs.push_back("-force_cpusubtype_ALL");
+ }
+ }
+
+ CCArgs.push_back(ProgramFile); // Specify the input filename.
+
+ CCArgs.push_back("-x");
+ CCArgs.push_back("none");
+ CCArgs.push_back("-o");
+
+ SmallString<128> OutputBinary;
+ std::error_code EC =
+ sys::fs::createUniqueFile(ProgramFile + "-%%%%%%%.cc.exe", OutputBinary);
+ if (EC) {
+ errs() << "Error making unique filename: " << EC.message() << "\n";
+ exit(1);
+ }
+ CCArgs.push_back(OutputBinary); // Output to the right file...
+
+ // Add any arguments intended for CC. We locate them here because this is
+ // most likely -L and -l options that need to come before other libraries but
+ // after the source. Other options won't be sensitive to placement on the
+ // command line, so this should be safe.
+ for (unsigned i = 0, e = ArgsForCC.size(); i != e; ++i)
+ CCArgs.push_back(ArgsForCC[i]);
+
+ CCArgs.push_back("-lm"); // Hard-code the math library...
+ CCArgs.push_back("-O2"); // Optimize the program a bit...
+ if (TargetTriple.getArch() == Triple::sparc)
+ CCArgs.push_back("-mcpu=v9");
+
+ outs() << "<CC>";
+ outs().flush();
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = CCArgs.size(); i != e; ++i) errs()
+ << " " << CCArgs[i];
+ errs() << "\n";);
+ if (RunProgramWithTimeout(CCPath, CCArgs, "", "", ""))
+ return ProcessFailure(CCPath, CCArgs);
+
+ std::vector<StringRef> ProgramArgs;
+
+ // Declared here so that the destructor only runs after
+ // ProgramArgs is used.
+ std::string Exec;
+
+ if (RemoteClientPath.empty())
+ ProgramArgs.push_back(OutputBinary);
+ else {
+ ProgramArgs.push_back(RemoteClientPath);
+ ProgramArgs.push_back(RemoteHost);
+ if (!RemoteUser.empty()) {
+ ProgramArgs.push_back("-l");
+ ProgramArgs.push_back(RemoteUser);
+ }
+ if (!RemotePort.empty()) {
+ ProgramArgs.push_back("-p");
+ ProgramArgs.push_back(RemotePort);
+ }
+ if (!RemoteExtra.empty()) {
+ ProgramArgs.push_back(RemoteExtra);
+ }
+
+ // Full path to the binary. We need to cd to the exec directory because
+ // there is a dylib there that the exec expects to find in the CWD
+ char *env_pwd = getenv("PWD");
+ Exec = "cd ";
+ Exec += env_pwd;
+ Exec += "; ./";
+ Exec += OutputBinary.c_str();
+ ProgramArgs.push_back(Exec);
+ }
+
+ // Add optional parameters to the running program from Argv
+ for (unsigned i = 0, e = Args.size(); i != e; ++i)
+ ProgramArgs.push_back(Args[i]);
+
+ // Now that we have a binary, run it!
+ outs() << "<program>";
+ outs().flush();
+ LLVM_DEBUG(
+ errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = ProgramArgs.size(); i != e; ++i) errs()
+ << " " << ProgramArgs[i];
+ errs() << "\n";);
+
+ FileRemover OutputBinaryRemover(OutputBinary.str(), !SaveTemps);
+
+ if (RemoteClientPath.empty()) {
+ LLVM_DEBUG(errs() << "<run locally>");
+ std::string Error;
+ int ExitCode = RunProgramWithTimeout(OutputBinary.str(), ProgramArgs,
+ InputFile, OutputFile, OutputFile,
+ Timeout, MemoryLimit, &Error);
+ // Treat a signal (usually SIGSEGV) or timeout as part of the program output
+ // so that crash-causing miscompilation is handled seamlessly.
+ if (ExitCode < -1) {
+ std::ofstream outFile(OutputFile.c_str(), std::ios_base::app);
+ outFile << Error << '\n';
+ outFile.close();
+ }
+ return ExitCode;
+ } else {
+ outs() << "<run remotely>";
+ outs().flush();
+ return RunProgramRemotelyWithTimeout(RemoteClientPath, ProgramArgs,
+ InputFile, OutputFile, OutputFile,
+ Timeout, MemoryLimit);
+ }
+}
+
+Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType,
+ std::string &OutputFile,
+ const std::vector<std::string> &ArgsForCC) {
+ SmallString<128> UniqueFilename;
+ std::error_code EC = sys::fs::createUniqueFile(
+ InputFile + "-%%%%%%%" + LTDL_SHLIB_EXT, UniqueFilename);
+ if (EC) {
+ errs() << "Error making unique filename: " << EC.message() << "\n";
+ exit(1);
+ }
+ OutputFile = std::string(UniqueFilename.str());
+
+ std::vector<StringRef> CCArgs;
+
+ CCArgs.push_back(CCPath);
+
+ if (TargetTriple.getArch() == Triple::x86)
+ CCArgs.push_back("-m32");
+
+ for (std::vector<std::string>::const_iterator I = ccArgs.begin(),
+ E = ccArgs.end();
+ I != E; ++I)
+ CCArgs.push_back(*I);
+
+ // Compile the C/asm file into a shared object
+ if (fileType != ObjectFile) {
+ CCArgs.push_back("-x");
+ CCArgs.push_back(fileType == AsmFile ? "assembler" : "c");
+ }
+ CCArgs.push_back("-fno-strict-aliasing");
+ CCArgs.push_back(InputFile); // Specify the input filename.
+ CCArgs.push_back("-x");
+ CCArgs.push_back("none");
+ if (TargetTriple.getArch() == Triple::sparc)
+ CCArgs.push_back("-G"); // Compile a shared library, `-G' for Sparc
+ else if (TargetTriple.isOSDarwin()) {
+ // link all source files into a single module in data segment, rather than
+ // generating blocks. dynamic_lookup requires that you set
+ // MACOSX_DEPLOYMENT_TARGET=10.3 in your env. FIXME: it would be better for
+ // bugpoint to just pass that in the environment of CC.
+ CCArgs.push_back("-single_module");
+ CCArgs.push_back("-dynamiclib"); // `-dynamiclib' for MacOS X/PowerPC
+ CCArgs.push_back("-undefined");
+ CCArgs.push_back("dynamic_lookup");
+ } else
+ CCArgs.push_back("-shared"); // `-shared' for Linux/X86, maybe others
+
+ if (TargetTriple.getArch() == Triple::x86_64)
+ CCArgs.push_back("-fPIC"); // Requires shared objs to contain PIC
+
+ if (TargetTriple.getArch() == Triple::sparc)
+ CCArgs.push_back("-mcpu=v9");
+
+ CCArgs.push_back("-o");
+ CCArgs.push_back(OutputFile); // Output to the right filename.
+ CCArgs.push_back("-O2"); // Optimize the program a bit.
+
+ // Add any arguments intended for CC. We locate them here because this is
+ // most likely -L and -l options that need to come before other libraries but
+ // after the source. Other options won't be sensitive to placement on the
+ // command line, so this should be safe.
+ for (unsigned i = 0, e = ArgsForCC.size(); i != e; ++i)
+ CCArgs.push_back(ArgsForCC[i]);
+
+ outs() << "<CC>";
+ outs().flush();
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = CCArgs.size(); i != e; ++i) errs()
+ << " " << CCArgs[i];
+ errs() << "\n";);
+ if (RunProgramWithTimeout(CCPath, CCArgs, "", "", ""))
+ return ProcessFailure(CCPath, CCArgs);
+ return Error::success();
+}
+
+/// create - Try to find the CC executable
+///
+CC *CC::create(const char *Argv0, std::string &Message,
+ const std::string &CCBinary,
+ const std::vector<std::string> *Args) {
+ auto CCPath = FindProgramByName(CCBinary, Argv0, (void *)(intptr_t)&create);
+ if (!CCPath) {
+ Message = "Cannot find `" + CCBinary + "' in PATH: " +
+ CCPath.getError().message() + "\n";
+ return nullptr;
+ }
+
+ std::string RemoteClientPath;
+ if (!RemoteClient.empty()) {
+ auto Path = sys::findProgramByName(RemoteClient);
+ if (!Path) {
+ Message = "Cannot find `" + RemoteClient + "' in PATH: " +
+ Path.getError().message() + "\n";
+ return nullptr;
+ }
+ RemoteClientPath = *Path;
+ }
+
+ Message = "Found CC: " + *CCPath + "\n";
+ return new CC(*CCPath, RemoteClientPath, Args);
+}
diff --git a/contrib/libs/llvm14/tools/bugpoint/ToolRunner.h b/contrib/libs/llvm14/tools/bugpoint/ToolRunner.h
new file mode 100644
index 00000000000..f6b5f26c7a6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/ToolRunner.h
@@ -0,0 +1,190 @@
+//===-- tools/bugpoint/ToolRunner.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file exposes an abstraction around a platform C compiler, used to
+// compile C and assembly code. It also exposes an "AbstractIntepreter"
+// interface, which is used to execute code using one of the LLVM execution
+// engines.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_BUGPOINT_TOOLRUNNER_H
+#define LLVM_TOOLS_BUGPOINT_TOOLRUNNER_H
+
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/SystemUtils.h"
+#include <exception>
+#include <vector>
+
+namespace llvm {
+
+extern cl::opt<bool> SaveTemps;
+extern Triple TargetTriple;
+
+class LLC;
+
+//===---------------------------------------------------------------------===//
+// CC abstraction
+//
+class CC {
+ std::string CCPath; // The path to the cc executable.
+ std::string RemoteClientPath; // The path to the rsh / ssh executable.
+ std::vector<std::string> ccArgs; // CC-specific arguments.
+ CC(StringRef ccPath, StringRef RemotePath,
+ const std::vector<std::string> *CCArgs)
+ : CCPath(std::string(ccPath)), RemoteClientPath(std::string(RemotePath)) {
+ if (CCArgs)
+ ccArgs = *CCArgs;
+ }
+
+public:
+ enum FileType { AsmFile, ObjectFile, CFile };
+
+ 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
+ /// either a .s file, or a .c file, specified by FileType), with the specified
+ /// arguments. Standard input is specified with InputFile, and standard
+ /// Output is captured to the specified OutputFile location. The SharedLibs
+ /// option specifies optional native shared objects that can be loaded into
+ /// the program for execution.
+ ///
+ Expected<int> ExecuteProgram(
+ const std::string &ProgramFile, const std::vector<std::string> &Args,
+ FileType fileType, const std::string &InputFile,
+ const std::string &OutputFile,
+ const std::vector<std::string> &CCArgs = std::vector<std::string>(),
+ unsigned Timeout = 0, unsigned MemoryLimit = 0);
+
+ /// MakeSharedObject - This compiles the specified file (which is either a .c
+ /// file or a .s file) into a shared object.
+ ///
+ Error MakeSharedObject(const std::string &InputFile, FileType fileType,
+ std::string &OutputFile,
+ const std::vector<std::string> &ArgsForCC);
+};
+
+//===---------------------------------------------------------------------===//
+/// AbstractInterpreter Class - Subclasses of this class are used to execute
+/// LLVM bitcode in a variety of ways. This abstract interface hides this
+/// complexity behind a simple interface.
+///
+class AbstractInterpreter {
+ virtual void anchor();
+
+public:
+ static LLC *createLLC(const char *Argv0, std::string &Message,
+ const std::string &CCBinary,
+ const std::vector<std::string> *Args = nullptr,
+ const std::vector<std::string> *CCArgs = nullptr,
+ bool UseIntegratedAssembler = false);
+
+ static AbstractInterpreter *
+ createLLI(const char *Argv0, std::string &Message,
+ const std::vector<std::string> *Args = nullptr);
+
+ static AbstractInterpreter *
+ createJIT(const char *Argv0, std::string &Message,
+ const std::vector<std::string> *Args = nullptr);
+
+ static AbstractInterpreter *
+ createCustomCompiler(const char *Argv0, std::string &Message,
+ const std::string &CompileCommandLine);
+
+ static AbstractInterpreter *
+ createCustomExecutor(const char *Argv0, std::string &Message,
+ const std::string &ExecCommandLine);
+
+ virtual ~AbstractInterpreter() {}
+
+ /// compileProgram - Compile the specified program from bitcode to executable
+ /// code. This does not produce any output, it is only used when debugging
+ /// the code generator. It returns false if the code generator fails.
+ virtual Error compileProgram(const std::string &Bitcode, unsigned Timeout = 0,
+ unsigned MemoryLimit = 0) {
+ return Error::success();
+ }
+
+ /// Compile the specified program from bitcode to code understood by the CC
+ /// driver (either C or asm). Returns an error if the code generator fails,,
+ /// otherwise, the type of code emitted.
+ virtual Expected<CC::FileType> OutputCode(const std::string &Bitcode,
+ std::string &OutFile,
+ unsigned Timeout = 0,
+ unsigned MemoryLimit = 0) {
+ return make_error<StringError>(
+ "OutputCode not supported by this AbstractInterpreter!",
+ inconvertibleErrorCode());
+ }
+
+ /// ExecuteProgram - Run the specified bitcode file, emitting output to the
+ /// specified filename. This sets RetVal to the exit code of the program or
+ /// returns an Error if a problem was encountered that prevented execution of
+ /// the program.
+ ///
+ virtual Expected<int> ExecuteProgram(
+ const std::string &Bitcode, const std::vector<std::string> &Args,
+ const std::string &InputFile, const std::string &OutputFile,
+ const std::vector<std::string> &CCArgs = std::vector<std::string>(),
+ const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
+ unsigned Timeout = 0, unsigned MemoryLimit = 0) = 0;
+};
+
+//===---------------------------------------------------------------------===//
+// LLC Implementation of AbstractIntepreter interface
+//
+class LLC : public AbstractInterpreter {
+ std::string LLCPath; // The path to the LLC executable.
+ std::vector<std::string> ToolArgs; // Extra args to pass to LLC.
+ CC *cc;
+ bool UseIntegratedAssembler;
+
+public:
+ LLC(const std::string &llcPath, CC *cc, const std::vector<std::string> *Args,
+ bool useIntegratedAssembler)
+ : LLCPath(llcPath), cc(cc),
+ UseIntegratedAssembler(useIntegratedAssembler) {
+ ToolArgs.clear();
+ if (Args)
+ ToolArgs = *Args;
+ }
+ ~LLC() override { delete cc; }
+
+ /// compileProgram - Compile the specified program from bitcode to executable
+ /// code. This does not produce any output, it is only used when debugging
+ /// the code generator. Returns false if the code generator fails.
+ Error compileProgram(const std::string &Bitcode, unsigned Timeout = 0,
+ unsigned MemoryLimit = 0) override;
+
+ Expected<int> ExecuteProgram(
+ const std::string &Bitcode, const std::vector<std::string> &Args,
+ const std::string &InputFile, const std::string &OutputFile,
+ const std::vector<std::string> &CCArgs = std::vector<std::string>(),
+ const std::vector<std::string> &SharedLibs = std::vector<std::string>(),
+ unsigned Timeout = 0, unsigned MemoryLimit = 0) override;
+
+ Expected<CC::FileType> OutputCode(const std::string &Bitcode,
+ std::string &OutFile, unsigned Timeout = 0,
+ 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/contrib/libs/llvm14/tools/bugpoint/bugpoint.cpp b/contrib/libs/llvm14/tools/bugpoint/bugpoint.cpp
new file mode 100644
index 00000000000..937ec23231b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/bugpoint.cpp
@@ -0,0 +1,244 @@
+//===- bugpoint.cpp - The LLVM Bugpoint utility ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is an automated compiler debugger tool. It is used to narrow
+// down miscompilations and crash problems to a specific pass in the compiler,
+// and the specific Module or Function input that is causing the problem.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BugDriver.h"
+#include "ToolRunner.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/LegacyPassNameParser.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/LinkAllIR.h"
+#include "llvm/LinkAllPasses.h"
+#include "llvm/Passes/PassPlugin.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/PluginLoader.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Valgrind.h"
+#include "llvm/Transforms/IPO/AlwaysInliner.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+
+// Enable this macro to debug bugpoint itself.
+//#define DEBUG_BUGPOINT 1
+
+using namespace llvm;
+
+static cl::opt<bool>
+ FindBugs("find-bugs", cl::desc("Run many different optimization sequences "
+ "on program to find bugs"),
+ cl::init(false));
+
+static cl::list<std::string>
+ InputFilenames(cl::Positional, cl::OneOrMore,
+ cl::desc("<input llvm ll/bc files>"));
+
+static cl::opt<unsigned> TimeoutValue(
+ "timeout", cl::init(300), cl::value_desc("seconds"),
+ cl::desc("Number of seconds program is allowed to run before it "
+ "is killed (default is 300s), 0 disables timeout"));
+
+static cl::opt<int> MemoryLimit(
+ "mlimit", cl::init(-1), cl::value_desc("MBytes"),
+ cl::desc("Maximum amount of memory to use. 0 disables check. Defaults to "
+ "400MB (800MB under valgrind, 0 with sanitizers)."));
+
+static cl::opt<bool>
+ UseValgrind("enable-valgrind",
+ cl::desc("Run optimizations through valgrind"));
+
+// The AnalysesList is automatically populated with registered Passes by the
+// PassNameParser.
+//
+static cl::list<const PassInfo *, bool, PassNameParser>
+ PassList(cl::desc("Passes available:"), cl::ZeroOrMore);
+
+static cl::opt<bool>
+ StandardLinkOpts("std-link-opts",
+ cl::desc("Include the standard link time optimizations"));
+
+static cl::opt<bool>
+ OptLevelO1("O1", cl::desc("Optimization level 1. Identical to 'opt -O1'"));
+
+static cl::opt<bool>
+ OptLevelO2("O2", cl::desc("Optimization level 2. Identical to 'opt -O2'"));
+
+static cl::opt<bool> OptLevelOs(
+ "Os",
+ cl::desc(
+ "Like -O2 with extra optimizations for size. Similar to clang -Os"));
+
+static cl::opt<bool>
+OptLevelOz("Oz",
+ cl::desc("Like -Os but reduces code size further. Similar to clang -Oz"));
+
+static cl::opt<bool>
+ OptLevelO3("O3", cl::desc("Optimization level 3. Identical to 'opt -O3'"));
+
+static cl::opt<std::string>
+ OverrideTriple("mtriple", cl::desc("Override target triple for module"));
+
+/// BugpointIsInterrupted - Set to true when the user presses ctrl-c.
+bool llvm::BugpointIsInterrupted = false;
+
+#ifndef DEBUG_BUGPOINT
+static void BugpointInterruptFunction() { BugpointIsInterrupted = true; }
+#endif
+
+// Hack to capture a pass list.
+namespace {
+class AddToDriver : public legacy::FunctionPassManager {
+ BugDriver &D;
+
+public:
+ AddToDriver(BugDriver &_D) : FunctionPassManager(nullptr), D(_D) {}
+
+ void add(Pass *P) override {
+ const void *ID = P->getPassID();
+ const PassInfo *PI = PassRegistry::getPassRegistry()->getPassInfo(ID);
+ D.addPass(std::string(PI->getPassArgument()));
+ }
+};
+}
+
+// This routine adds optimization passes based on selected optimization level,
+// OptLevel.
+//
+// OptLevel - Optimization Level
+static void AddOptimizationPasses(legacy::FunctionPassManager &FPM,
+ unsigned OptLevel,
+ unsigned SizeLevel) {
+ PassManagerBuilder Builder;
+ Builder.OptLevel = OptLevel;
+ Builder.SizeLevel = SizeLevel;
+
+ if (OptLevel > 1)
+ Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel, false);
+ else
+ Builder.Inliner = createAlwaysInlinerLegacyPass();
+
+ Builder.populateFunctionPassManager(FPM);
+ Builder.populateModulePassManager(FPM);
+}
+
+#define HANDLE_EXTENSION(Ext) \
+ llvm::PassPluginLibraryInfo get##Ext##PluginInfo();
+#include "llvm/Support/Extension.def"
+
+int main(int argc, char **argv) {
+#ifndef DEBUG_BUGPOINT
+ InitLLVM X(argc, argv);
+#endif
+
+ // Initialize passes
+ PassRegistry &Registry = *PassRegistry::getPassRegistry();
+ initializeCore(Registry);
+ initializeScalarOpts(Registry);
+ initializeObjCARCOpts(Registry);
+ initializeVectorization(Registry);
+ initializeIPO(Registry);
+ initializeAnalysis(Registry);
+ initializeTransformUtils(Registry);
+ initializeInstCombine(Registry);
+ initializeAggressiveInstCombine(Registry);
+ initializeInstrumentation(Registry);
+ initializeTarget(Registry);
+
+ if (std::getenv("bar") == (char*) -1) {
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmPrinters();
+ InitializeAllAsmParsers();
+ }
+
+ cl::ParseCommandLineOptions(argc, argv,
+ "LLVM automatic testcase reducer. See\nhttp://"
+ "llvm.org/cmds/bugpoint.html"
+ " for more information.\n");
+#ifndef DEBUG_BUGPOINT
+ sys::SetInterruptFunction(BugpointInterruptFunction);
+#endif
+
+ LLVMContext Context;
+ // If we have an override, set it and then track the triple we want Modules
+ // to use.
+ if (!OverrideTriple.empty()) {
+ TargetTriple.setTriple(Triple::normalize(OverrideTriple));
+ outs() << "Override triple set to '" << TargetTriple.getTriple() << "'\n";
+ }
+
+ if (MemoryLimit < 0) {
+ // Set the default MemoryLimit. Be sure to update the flag's description if
+ // you change this.
+ if (sys::RunningOnValgrind() || UseValgrind)
+ MemoryLimit = 800;
+ else
+ MemoryLimit = 400;
+#if (LLVM_ADDRESS_SANITIZER_BUILD || LLVM_MEMORY_SANITIZER_BUILD || \
+ LLVM_THREAD_SANITIZER_BUILD)
+ // Starting from kernel 4.9 memory allocated with mmap is counted against
+ // RLIMIT_DATA. Sanitizers need to allocate tens of terabytes for shadow.
+ MemoryLimit = 0;
+#endif
+ }
+
+ BugDriver D(argv[0], FindBugs, TimeoutValue, MemoryLimit, UseValgrind,
+ Context);
+ if (D.addSources(InputFilenames))
+ return 1;
+
+ AddToDriver PM(D);
+
+ if (StandardLinkOpts) {
+ PassManagerBuilder Builder;
+ Builder.Inliner = createFunctionInliningPass();
+ Builder.populateLTOPassManager(PM);
+ }
+
+ if (OptLevelO1)
+ AddOptimizationPasses(PM, 1, 0);
+ else if (OptLevelO2)
+ AddOptimizationPasses(PM, 2, 0);
+ else if (OptLevelO3)
+ AddOptimizationPasses(PM, 3, 0);
+ else if (OptLevelOs)
+ AddOptimizationPasses(PM, 2, 1);
+ else if (OptLevelOz)
+ AddOptimizationPasses(PM, 2, 2);
+
+ for (const PassInfo *PI : PassList)
+ D.addPass(std::string(PI->getPassArgument()));
+
+// Bugpoint has the ability of generating a plethora of core files, so to
+// avoid filling up the disk, we prevent it
+#ifndef DEBUG_BUGPOINT
+ sys::Process::PreventCoreFiles();
+#endif
+
+// Needed to pull in symbols from statically linked extensions, including static
+// registration. It is unused otherwise because bugpoint has no support for
+// NewPM.
+#define HANDLE_EXTENSION(Ext) \
+ (void)get##Ext##PluginInfo();
+#include "llvm/Support/Extension.def"
+
+ if (Error E = D.run()) {
+ errs() << toString(std::move(E));
+ return 1;
+ }
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/bugpoint/ya.make b/contrib/libs/llvm14/tools/bugpoint/ya.make
new file mode 100644
index 00000000000..f6e035e20bc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/bugpoint/ya.make
@@ -0,0 +1,100 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/AsmPrinter
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/CodeGen/SelectionDAG
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Extensions
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Passes
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/CFGuard
+ contrib/libs/llvm14/lib/Transforms/Coroutines
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/ObjCARC
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+ contrib/libs/llvm14/tools/polly/lib
+ contrib/libs/llvm14/tools/polly/lib/External/isl
+ contrib/libs/llvm14/tools/polly/lib/External/ppcg
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/bugpoint
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ BugDriver.cpp
+ CrashDebugger.cpp
+ ExecutionDriver.cpp
+ ExtractFunction.cpp
+ FindBugs.cpp
+ Miscompilation.cpp
+ OptimizerDriver.cpp
+ ToolRunner.cpp
+ bugpoint.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/dsymutil/BinaryHolder.cpp b/contrib/libs/llvm14/tools/dsymutil/BinaryHolder.cpp
new file mode 100644
index 00000000000..f83521346c5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/BinaryHolder.cpp
@@ -0,0 +1,285 @@
+//===-- BinaryHolder.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is a utility that aims to be a dropin replacement for
+// Darwin's dsymutil.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BinaryHolder.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace dsymutil {
+
+static std::pair<StringRef, StringRef>
+getArchiveAndObjectName(StringRef Filename) {
+ StringRef Archive = Filename.substr(0, Filename.rfind('('));
+ StringRef Object = Filename.substr(Archive.size() + 1).drop_back();
+ return {Archive, Object};
+}
+
+static bool isArchive(StringRef Filename) { return Filename.endswith(")"); }
+
+static std::vector<MemoryBufferRef>
+getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem,
+ object::MachOUniversalBinary &Fat) {
+ std::vector<MemoryBufferRef> Buffers;
+ StringRef FatData = Fat.getData();
+ for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End;
+ ++It) {
+ StringRef ObjData = FatData.substr(It->getOffset(), It->getSize());
+ Buffers.emplace_back(ObjData, Filename);
+ }
+ return Buffers;
+}
+
+Error BinaryHolder::ArchiveEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
+ StringRef Filename,
+ TimestampTy Timestamp, bool Verbose) {
+ StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
+
+ // Try to load archive and force it to be memory mapped.
+ auto ErrOrBuff = (ArchiveFilename == "-")
+ ? MemoryBuffer::getSTDIN()
+ : VFS->getBufferForFile(ArchiveFilename, -1, false);
+ if (auto Err = ErrOrBuff.getError())
+ return errorCodeToError(Err);
+
+ MemBuffer = std::move(*ErrOrBuff);
+
+ if (Verbose)
+ WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n";
+
+ // Load one or more archive buffers, depending on whether we're dealing with
+ // a fat binary.
+ std::vector<MemoryBufferRef> ArchiveBuffers;
+
+ auto ErrOrFat =
+ object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
+ if (!ErrOrFat) {
+ consumeError(ErrOrFat.takeError());
+ ArchiveBuffers.push_back(MemBuffer->getMemBufferRef());
+ } else {
+ FatBinary = std::move(*ErrOrFat);
+ FatBinaryName = std::string(ArchiveFilename);
+ ArchiveBuffers =
+ getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
+ }
+
+ // Finally, try to load the archives.
+ Archives.reserve(ArchiveBuffers.size());
+ for (auto MemRef : ArchiveBuffers) {
+ auto ErrOrArchive = object::Archive::create(MemRef);
+ if (!ErrOrArchive)
+ return ErrOrArchive.takeError();
+ Archives.push_back(std::move(*ErrOrArchive));
+ }
+
+ return Error::success();
+}
+
+Error BinaryHolder::ObjectEntry::load(IntrusiveRefCntPtr<vfs::FileSystem> VFS,
+ StringRef Filename, TimestampTy Timestamp,
+ bool Verbose) {
+ // Try to load regular binary and force it to be memory mapped.
+ auto ErrOrBuff = (Filename == "-")
+ ? MemoryBuffer::getSTDIN()
+ : VFS->getBufferForFile(Filename, -1, false);
+ if (auto Err = ErrOrBuff.getError())
+ return errorCodeToError(Err);
+
+ if (Filename != "-" && Timestamp != sys::TimePoint<>()) {
+ llvm::ErrorOr<vfs::Status> Stat = VFS->status(Filename);
+ if (!Stat)
+ return errorCodeToError(Stat.getError());
+ if (Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(
+ Stat->getLastModificationTime()))
+ WithColor::warning() << Filename
+ << ": timestamp mismatch between object file ("
+ << Stat->getLastModificationTime()
+ << ") and debug map (" << Timestamp << ")\n";
+ }
+
+ MemBuffer = std::move(*ErrOrBuff);
+
+ if (Verbose)
+ WithColor::note() << "loaded object.\n";
+
+ // Load one or more object buffers, depending on whether we're dealing with a
+ // fat binary.
+ std::vector<MemoryBufferRef> ObjectBuffers;
+
+ auto ErrOrFat =
+ object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef());
+ if (!ErrOrFat) {
+ consumeError(ErrOrFat.takeError());
+ ObjectBuffers.push_back(MemBuffer->getMemBufferRef());
+ } else {
+ FatBinary = std::move(*ErrOrFat);
+ FatBinaryName = std::string(Filename);
+ ObjectBuffers =
+ getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary);
+ }
+
+ Objects.reserve(ObjectBuffers.size());
+ for (auto MemRef : ObjectBuffers) {
+ auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef);
+ if (!ErrOrObjectFile)
+ return ErrOrObjectFile.takeError();
+ Objects.push_back(std::move(*ErrOrObjectFile));
+ }
+
+ return Error::success();
+}
+
+std::vector<const object::ObjectFile *>
+BinaryHolder::ObjectEntry::getObjects() const {
+ std::vector<const object::ObjectFile *> Result;
+ Result.reserve(Objects.size());
+ for (auto &Object : Objects) {
+ Result.push_back(Object.get());
+ }
+ return Result;
+}
+Expected<const object::ObjectFile &>
+BinaryHolder::ObjectEntry::getObject(const Triple &T) const {
+ for (const auto &Obj : Objects) {
+ if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) {
+ if (MachO->getArchTriple().str() == T.str())
+ return *MachO;
+ } else if (Obj->getArch() == T.getArch())
+ return *Obj;
+ }
+ return errorCodeToError(object::object_error::arch_not_found);
+}
+
+Expected<const BinaryHolder::ObjectEntry &>
+BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename,
+ TimestampTy Timestamp,
+ bool Verbose) {
+ StringRef ArchiveFilename;
+ StringRef ObjectFilename;
+ std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename);
+
+ // Try the cache first.
+ KeyTy Key = {ObjectFilename, Timestamp};
+
+ {
+ std::lock_guard<std::mutex> Lock(MemberCacheMutex);
+ if (MemberCache.count(Key))
+ return MemberCache[Key];
+ }
+
+ // Create a new ObjectEntry, but don't add it to the cache yet. Loading of
+ // the archive members might fail and we don't want to lock the whole archive
+ // during this operation.
+ ObjectEntry OE;
+
+ for (const auto &Archive : Archives) {
+ Error Err = Error::success();
+ for (auto Child : Archive->children(Err)) {
+ if (auto NameOrErr = Child.getName()) {
+ if (*NameOrErr == ObjectFilename) {
+ auto ModTimeOrErr = Child.getLastModified();
+ if (!ModTimeOrErr)
+ return ModTimeOrErr.takeError();
+
+ if (Timestamp != sys::TimePoint<>() &&
+ Timestamp != std::chrono::time_point_cast<std::chrono::seconds>(
+ ModTimeOrErr.get())) {
+ if (Verbose)
+ WithColor::warning()
+ << *NameOrErr
+ << ": timestamp mismatch between archive member ("
+ << ModTimeOrErr.get() << ") and debug map (" << Timestamp
+ << ")\n";
+ continue;
+ }
+
+ if (Verbose)
+ WithColor::note() << "found member in archive.\n";
+
+ auto ErrOrMem = Child.getMemoryBufferRef();
+ if (!ErrOrMem)
+ return ErrOrMem.takeError();
+
+ auto ErrOrObjectFile =
+ object::ObjectFile::createObjectFile(*ErrOrMem);
+ if (!ErrOrObjectFile)
+ return ErrOrObjectFile.takeError();
+
+ OE.Objects.push_back(std::move(*ErrOrObjectFile));
+ }
+ }
+ }
+ if (Err)
+ return std::move(Err);
+ }
+
+ if (OE.Objects.empty())
+ return errorCodeToError(errc::no_such_file_or_directory);
+
+ std::lock_guard<std::mutex> Lock(MemberCacheMutex);
+ MemberCache.try_emplace(Key, std::move(OE));
+ return MemberCache[Key];
+}
+
+Expected<const BinaryHolder::ObjectEntry &>
+BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) {
+ if (Verbose)
+ WithColor::note() << "trying to open '" << Filename << "'\n";
+
+ // If this is an archive, we might have either the object or the archive
+ // cached. In this case we can load it without accessing the file system.
+ if (isArchive(Filename)) {
+ StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
+ std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
+ if (ArchiveCache.count(ArchiveFilename)) {
+ return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp,
+ Verbose);
+ } else {
+ ArchiveEntry &AE = ArchiveCache[ArchiveFilename];
+ auto Err = AE.load(VFS, Filename, Timestamp, Verbose);
+ if (Err) {
+ ArchiveCache.erase(ArchiveFilename);
+ // Don't return the error here: maybe the file wasn't an archive.
+ llvm::consumeError(std::move(Err));
+ } else {
+ return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp,
+ Verbose);
+ }
+ }
+ }
+
+ // If this is an object, we might have it cached. If not we'll have to load
+ // it from the file system and cache it now.
+ std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
+ if (!ObjectCache.count(Filename)) {
+ ObjectEntry &OE = ObjectCache[Filename];
+ auto Err = OE.load(VFS, Filename, Timestamp, Verbose);
+ if (Err) {
+ ObjectCache.erase(Filename);
+ return std::move(Err);
+ }
+ }
+
+ return ObjectCache[Filename];
+}
+
+void BinaryHolder::clear() {
+ std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex);
+ std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex);
+ ArchiveCache.clear();
+ ObjectCache.clear();
+}
+
+} // namespace dsymutil
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/dsymutil/BinaryHolder.h b/contrib/libs/llvm14/tools/dsymutil/BinaryHolder.h
new file mode 100644
index 00000000000..6245e492473
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/BinaryHolder.h
@@ -0,0 +1,172 @@
+//===-- BinaryHolder.h - Utility class for accessing binaries -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is a utility that aims to be a dropin replacement for
+// Darwin's dsymutil.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H
+#define LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/Error.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Chrono.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/VirtualFileSystem.h"
+
+#include <mutex>
+
+namespace llvm {
+namespace dsymutil {
+
+/// The BinaryHolder class is responsible for creating and owning
+/// ObjectFiles and their underlying MemoryBuffers. It differs from a simple
+/// OwningBinary in that it handles accessing and caching of archives and its
+/// members.
+class BinaryHolder {
+public:
+ using TimestampTy = sys::TimePoint<std::chrono::seconds>;
+
+ BinaryHolder(IntrusiveRefCntPtr<vfs::FileSystem> VFS, bool Verbose = false)
+ : VFS(VFS), Verbose(Verbose) {}
+
+ // Forward declarations for friend declaration.
+ class ObjectEntry;
+ class ArchiveEntry;
+
+ /// Base class shared by cached entries, representing objects and archives.
+ class EntryBase {
+ protected:
+ std::unique_ptr<MemoryBuffer> MemBuffer;
+ std::unique_ptr<object::MachOUniversalBinary> FatBinary;
+ std::string FatBinaryName;
+ };
+
+ /// Cached entry holding one or more (in case of a fat binary) object files.
+ class ObjectEntry : public EntryBase {
+ public:
+ /// Load the given object binary in memory.
+ Error load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, StringRef Filename,
+ TimestampTy Timestamp, bool Verbose = false);
+
+ /// Access all owned ObjectFiles.
+ std::vector<const object::ObjectFile *> getObjects() const;
+
+ /// Access to a derived version of all the currently owned ObjectFiles. The
+ /// conversion might be invalid, in which case an Error is returned.
+ template <typename ObjectFileType>
+ Expected<std::vector<const ObjectFileType *>> getObjectsAs() const {
+ std::vector<const ObjectFileType *> Result;
+ Result.reserve(Objects.size());
+ for (auto &Object : Objects) {
+ const auto *Derived = dyn_cast<ObjectFileType>(Object.get());
+ if (!Derived)
+ return errorCodeToError(object::object_error::invalid_file_type);
+ Result.push_back(Derived);
+ }
+ return Result;
+ }
+
+ /// Access the owned ObjectFile with architecture \p T.
+ Expected<const object::ObjectFile &> getObject(const Triple &T) const;
+
+ /// Access to a derived version of the currently owned ObjectFile with
+ /// architecture \p T. The conversion must be known to be valid.
+ template <typename ObjectFileType>
+ Expected<const ObjectFileType &> getObjectAs(const Triple &T) const {
+ auto Object = getObject(T);
+ if (!Object)
+ return Object.takeError();
+ return cast<ObjectFileType>(*Object);
+ }
+
+ private:
+ std::vector<std::unique_ptr<object::ObjectFile>> Objects;
+ friend ArchiveEntry;
+ };
+
+ /// Cached entry holding one or more (in the of a fat binary) archive files.
+ class ArchiveEntry : public EntryBase {
+ public:
+ struct KeyTy {
+ std::string Filename;
+ TimestampTy Timestamp;
+
+ KeyTy() {}
+ KeyTy(StringRef Filename, TimestampTy Timestamp)
+ : Filename(Filename.str()), Timestamp(Timestamp) {}
+ };
+
+ /// Load the given object binary in memory.
+ Error load(IntrusiveRefCntPtr<vfs::FileSystem> VFS, StringRef Filename,
+ TimestampTy Timestamp, bool Verbose = false);
+
+ Expected<const ObjectEntry &> getObjectEntry(StringRef Filename,
+ TimestampTy Timestamp,
+ bool Verbose = false);
+
+ private:
+ std::vector<std::unique_ptr<object::Archive>> Archives;
+ DenseMap<KeyTy, ObjectEntry> MemberCache;
+ std::mutex MemberCacheMutex;
+ };
+
+ Expected<const ObjectEntry &>
+ getObjectEntry(StringRef Filename, TimestampTy Timestamp = TimestampTy());
+
+ void clear();
+
+private:
+ /// Cache of static archives. Objects that are part of a static archive are
+ /// stored under this object, rather than in the map below.
+ StringMap<ArchiveEntry> ArchiveCache;
+ std::mutex ArchiveCacheMutex;
+
+ /// Object entries for objects that are not in a static archive.
+ StringMap<ObjectEntry> ObjectCache;
+ std::mutex ObjectCacheMutex;
+
+ /// Virtual File System instance.
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS;
+
+ bool Verbose;
+};
+
+} // namespace dsymutil
+
+template <> struct DenseMapInfo<dsymutil::BinaryHolder::ArchiveEntry::KeyTy> {
+
+ static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getEmptyKey() {
+ return dsymutil::BinaryHolder::ArchiveEntry::KeyTy();
+ }
+
+ static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getTombstoneKey() {
+ return dsymutil::BinaryHolder::ArchiveEntry::KeyTy("/", {});
+ }
+
+ static unsigned
+ getHashValue(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &K) {
+ return hash_combine(DenseMapInfo<StringRef>::getHashValue(K.Filename),
+ DenseMapInfo<unsigned>::getHashValue(
+ K.Timestamp.time_since_epoch().count()));
+ }
+
+ static bool isEqual(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &LHS,
+ const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &RHS) {
+ return LHS.Filename == RHS.Filename && LHS.Timestamp == RHS.Timestamp;
+ }
+};
+
+} // namespace llvm
+#endif
diff --git a/contrib/libs/llvm14/tools/dsymutil/CFBundle.cpp b/contrib/libs/llvm14/tools/dsymutil/CFBundle.cpp
new file mode 100644
index 00000000000..0625afb18ab
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/CFBundle.cpp
@@ -0,0 +1,185 @@
+//===- tools/dsymutil/CFBundle.cpp - CFBundle helper ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CFBundle.h"
+
+#ifdef __APPLE__
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <assert.h>
+#include <glob.h>
+#include <memory>
+#endif
+
+namespace llvm {
+namespace dsymutil {
+
+#ifdef __APPLE__
+/// Deleter that calls CFRelease rather than deleting the pointer.
+template <typename T> struct CFDeleter {
+ void operator()(T *P) {
+ if (P)
+ ::CFRelease(P);
+ }
+};
+
+/// This helper owns any CoreFoundation pointer and will call CFRelease() on
+/// any valid pointer it owns unless that pointer is explicitly released using
+/// the release() member function.
+template <typename T>
+using CFReleaser = std::unique_ptr<std::remove_pointer_t<T>,
+ CFDeleter<std::remove_pointer_t<T>>>;
+
+/// RAII wrapper around CFBundleRef.
+class CFString : public CFReleaser<CFStringRef> {
+public:
+ CFString(CFStringRef CFStr = nullptr) : CFReleaser<CFStringRef>(CFStr) {}
+
+ const char *UTF8(std::string &Str) const {
+ return CFString::UTF8(get(), Str);
+ }
+
+ CFIndex GetLength() const {
+ if (CFStringRef Str = get())
+ return CFStringGetLength(Str);
+ return 0;
+ }
+
+ static const char *UTF8(CFStringRef CFStr, std::string &Str);
+};
+
+/// Static function that puts a copy of the UTF-8 contents of CFStringRef into
+/// std::string and returns the C string pointer that is contained in the
+/// std::string when successful, nullptr otherwise.
+///
+/// This allows the std::string parameter to own the extracted string, and also
+/// allows that string to be returned as a C string pointer that can be used.
+const char *CFString::UTF8(CFStringRef CFStr, std::string &Str) {
+ if (!CFStr)
+ return nullptr;
+
+ const CFStringEncoding Encoding = kCFStringEncodingUTF8;
+ CFIndex MaxUTF8StrLength = CFStringGetLength(CFStr);
+ MaxUTF8StrLength =
+ CFStringGetMaximumSizeForEncoding(MaxUTF8StrLength, Encoding);
+ if (MaxUTF8StrLength > 0) {
+ Str.resize(MaxUTF8StrLength);
+ if (!Str.empty() &&
+ CFStringGetCString(CFStr, &Str[0], Str.size(), Encoding)) {
+ Str.resize(strlen(Str.c_str()));
+ return Str.c_str();
+ }
+ }
+
+ return nullptr;
+}
+
+/// RAII wrapper around CFBundleRef.
+class CFBundle : public CFReleaser<CFBundleRef> {
+public:
+ CFBundle(StringRef Path) : CFReleaser<CFBundleRef>() { SetFromPath(Path); }
+
+ CFBundle(CFURLRef Url)
+ : CFReleaser<CFBundleRef>(Url ? ::CFBundleCreate(nullptr, Url)
+ : nullptr) {}
+
+ /// Return the bundle identifier.
+ CFStringRef GetIdentifier() const {
+ if (CFBundleRef bundle = get())
+ return ::CFBundleGetIdentifier(bundle);
+ return nullptr;
+ }
+
+ /// Return value for key.
+ CFTypeRef GetValueForInfoDictionaryKey(CFStringRef key) const {
+ if (CFBundleRef bundle = get())
+ return ::CFBundleGetValueForInfoDictionaryKey(bundle, key);
+ return nullptr;
+ }
+
+private:
+ /// Helper to initialize this instance with a new bundle created from the
+ /// given path. This function will recursively remove components from the
+ /// path in its search for the nearest Info.plist.
+ void SetFromPath(StringRef Path);
+};
+
+void CFBundle::SetFromPath(StringRef Path) {
+ // Start from an empty/invalid CFBundle.
+ reset();
+
+ if (Path.empty() || !sys::fs::exists(Path))
+ return;
+
+ SmallString<256> RealPath;
+ sys::fs::real_path(Path, RealPath, /*expand_tilde*/ true);
+
+ do {
+ // Create a CFURL from the current path and use it to create a CFBundle.
+ CFReleaser<CFURLRef> BundleURL(::CFURLCreateFromFileSystemRepresentation(
+ kCFAllocatorDefault, (const UInt8 *)RealPath.data(), RealPath.size(),
+ false));
+ reset(::CFBundleCreate(kCFAllocatorDefault, BundleURL.get()));
+
+ // If we have a valid bundle and find its identifier we are done.
+ if (get() != nullptr) {
+ if (GetIdentifier() != nullptr)
+ return;
+ reset();
+ }
+
+ // Remove the last component of the path and try again until there's
+ // nothing left but the root.
+ sys::path::remove_filename(RealPath);
+ } while (RealPath != sys::path::root_name(RealPath));
+}
+#endif
+
+/// On Darwin, try and find the original executable's Info.plist to extract
+/// information about the bundle. Return default values on other platforms.
+CFBundleInfo getBundleInfo(StringRef ExePath) {
+ CFBundleInfo BundleInfo;
+
+#ifdef __APPLE__
+ auto PrintError = [&](CFTypeID TypeID) {
+ CFString TypeIDCFStr(::CFCopyTypeIDDescription(TypeID));
+ std::string TypeIDStr;
+ errs() << "The Info.plist key \"CFBundleShortVersionString\" is"
+ << "a " << TypeIDCFStr.UTF8(TypeIDStr)
+ << ", but it should be a string in: " << ExePath << ".\n";
+ };
+
+ CFBundle Bundle(ExePath);
+ if (CFStringRef BundleID = Bundle.GetIdentifier()) {
+ CFString::UTF8(BundleID, BundleInfo.IDStr);
+ if (CFTypeRef TypeRef =
+ Bundle.GetValueForInfoDictionaryKey(CFSTR("CFBundleVersion"))) {
+ CFTypeID TypeID = ::CFGetTypeID(TypeRef);
+ if (TypeID == ::CFStringGetTypeID())
+ CFString::UTF8((CFStringRef)TypeRef, BundleInfo.VersionStr);
+ else
+ PrintError(TypeID);
+ }
+ if (CFTypeRef TypeRef = Bundle.GetValueForInfoDictionaryKey(
+ CFSTR("CFBundleShortVersionString"))) {
+ CFTypeID TypeID = ::CFGetTypeID(TypeRef);
+ if (TypeID == ::CFStringGetTypeID())
+ CFString::UTF8((CFStringRef)TypeRef, BundleInfo.ShortVersionStr);
+ else
+ PrintError(TypeID);
+ }
+ }
+#endif
+
+ return BundleInfo;
+}
+
+} // end namespace dsymutil
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/dsymutil/CFBundle.h b/contrib/libs/llvm14/tools/dsymutil/CFBundle.h
new file mode 100644
index 00000000000..103db035164
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/CFBundle.h
@@ -0,0 +1,30 @@
+//===- tools/dsymutil/CFBundle.h - CFBundle helper --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_DSYMUTIL_CFBUNDLE_H
+#define LLVM_TOOLS_DSYMUTIL_CFBUNDLE_H
+
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace llvm {
+namespace dsymutil {
+
+struct CFBundleInfo {
+ std::string VersionStr = "1";
+ std::string ShortVersionStr = "1.0";
+ std::string IDStr;
+ bool OmitShortVersion() const { return ShortVersionStr.empty(); }
+};
+
+CFBundleInfo getBundleInfo(llvm::StringRef ExePath);
+
+} // end namespace dsymutil
+} // end namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/dsymutil/DebugMap.cpp b/contrib/libs/llvm14/tools/dsymutil/DebugMap.cpp
new file mode 100644
index 00000000000..605c1317b9c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/DebugMap.cpp
@@ -0,0 +1,294 @@
+//===- tools/dsymutil/DebugMap.cpp - Generic debug map representation -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DebugMap.h"
+#include "BinaryHolder.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Chrono.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cinttypes>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace llvm {
+
+namespace dsymutil {
+
+using namespace llvm::object;
+
+DebugMapObject::DebugMapObject(StringRef ObjectFilename,
+ sys::TimePoint<std::chrono::seconds> Timestamp,
+ uint8_t Type)
+ : Filename(std::string(ObjectFilename)), Timestamp(Timestamp), Type(Type) {}
+
+bool DebugMapObject::addSymbol(StringRef Name, Optional<uint64_t> ObjectAddress,
+ uint64_t LinkedAddress, uint32_t Size) {
+ auto InsertResult = Symbols.insert(
+ std::make_pair(Name, SymbolMapping(ObjectAddress, LinkedAddress, Size)));
+
+ if (ObjectAddress && InsertResult.second)
+ AddressToMapping[*ObjectAddress] = &*InsertResult.first;
+ return InsertResult.second;
+}
+
+void DebugMapObject::print(raw_ostream &OS) const {
+ OS << getObjectFilename() << ":\n";
+ // Sort the symbols in alphabetical order, like llvm-nm (and to get
+ // deterministic output for testing).
+ using Entry = std::pair<StringRef, SymbolMapping>;
+ std::vector<Entry> Entries;
+ Entries.reserve(Symbols.getNumItems());
+ for (const auto &Sym : Symbols)
+ Entries.push_back(std::make_pair(Sym.getKey(), Sym.getValue()));
+ 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));
+ else
+ OS << "\t????????????????";
+ OS << format(" => %016" PRIx64 "+0x%x\t%s\n",
+ uint64_t(Sym.second.BinaryAddress), uint32_t(Sym.second.Size),
+ Sym.first.data());
+ }
+ OS << '\n';
+}
+
+#ifndef NDEBUG
+void DebugMapObject::dump() const { print(errs()); }
+#endif
+
+DebugMapObject &
+DebugMap::addDebugMapObject(StringRef ObjectFilePath,
+ sys::TimePoint<std::chrono::seconds> Timestamp,
+ uint8_t Type) {
+ Objects.emplace_back(new DebugMapObject(ObjectFilePath, Timestamp, Type));
+ return *Objects.back();
+}
+
+const DebugMapObject::DebugMapEntry *
+DebugMapObject::lookupSymbol(StringRef SymbolName) const {
+ StringMap<SymbolMapping>::const_iterator Sym = Symbols.find(SymbolName);
+ if (Sym == Symbols.end())
+ return nullptr;
+ return &*Sym;
+}
+
+const DebugMapObject::DebugMapEntry *
+DebugMapObject::lookupObjectAddress(uint64_t Address) const {
+ auto Mapping = AddressToMapping.find(Address);
+ if (Mapping == AddressToMapping.end())
+ return nullptr;
+ return Mapping->getSecond();
+}
+
+void DebugMap::print(raw_ostream &OS) const {
+ yaml::Output yout(OS, /* Ctxt = */ nullptr, /* WrapColumn = */ 0);
+ yout << const_cast<DebugMap &>(*this);
+}
+
+#ifndef NDEBUG
+void DebugMap::dump() const { print(errs()); }
+#endif
+
+namespace {
+
+struct YAMLContext {
+ StringRef PrependPath;
+ Triple BinaryTriple;
+};
+
+} // end anonymous namespace
+
+ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
+DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath,
+ bool Verbose) {
+ auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile);
+ if (auto Err = ErrOrFile.getError())
+ return Err;
+
+ YAMLContext Ctxt;
+
+ Ctxt.PrependPath = PrependPath;
+
+ std::unique_ptr<DebugMap> Res;
+ yaml::Input yin((*ErrOrFile)->getBuffer(), &Ctxt);
+ yin >> Res;
+
+ if (auto EC = yin.error())
+ return EC;
+ std::vector<std::unique_ptr<DebugMap>> Result;
+ Result.push_back(std::move(Res));
+ return std::move(Result);
+}
+
+} // end namespace dsymutil
+
+namespace yaml {
+
+// Normalize/Denormalize between YAML and a DebugMapObject.
+struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO {
+ YamlDMO(IO &io) { Timestamp = 0; }
+ YamlDMO(IO &io, dsymutil::DebugMapObject &Obj);
+ dsymutil::DebugMapObject denormalize(IO &IO);
+
+ std::string Filename;
+ int64_t Timestamp;
+ std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries;
+};
+
+void MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>>::
+ mapping(IO &io, std::pair<std::string, DebugMapObject::SymbolMapping> &s) {
+ io.mapRequired("sym", s.first);
+ io.mapOptional("objAddr", s.second.ObjectAddress);
+ io.mapRequired("binAddr", s.second.BinaryAddress);
+ io.mapOptional("size", s.second.Size);
+}
+
+void MappingTraits<dsymutil::DebugMapObject>::mapping(
+ IO &io, dsymutil::DebugMapObject &DMO) {
+ MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO);
+ io.mapRequired("filename", Norm->Filename);
+ io.mapOptional("timestamp", Norm->Timestamp);
+ io.mapRequired("symbols", Norm->Entries);
+}
+
+void ScalarTraits<Triple>::output(const Triple &val, void *, raw_ostream &out) {
+ out << val.str();
+}
+
+StringRef ScalarTraits<Triple>::input(StringRef scalar, void *, Triple &value) {
+ value = Triple(scalar);
+ return StringRef();
+}
+
+size_t
+SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::size(
+ IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq) {
+ return seq.size();
+}
+
+dsymutil::DebugMapObject &
+SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::element(
+ IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq,
+ size_t index) {
+ if (index >= seq.size()) {
+ seq.resize(index + 1);
+ seq[index].reset(new dsymutil::DebugMapObject);
+ }
+ return *seq[index];
+}
+
+void MappingTraits<dsymutil::DebugMap>::mapping(IO &io,
+ dsymutil::DebugMap &DM) {
+ io.mapRequired("triple", DM.BinaryTriple);
+ io.mapOptional("binary-path", DM.BinaryPath);
+ if (void *Ctxt = io.getContext())
+ reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM.BinaryTriple;
+ io.mapOptional("objects", DM.Objects);
+}
+
+void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping(
+ IO &io, std::unique_ptr<dsymutil::DebugMap> &DM) {
+ if (!DM)
+ DM.reset(new DebugMap());
+ io.mapRequired("triple", DM->BinaryTriple);
+ io.mapOptional("binary-path", DM->BinaryPath);
+ if (void *Ctxt = io.getContext())
+ reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM->BinaryTriple;
+ io.mapOptional("objects", DM->Objects);
+}
+
+MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO(
+ IO &io, dsymutil::DebugMapObject &Obj) {
+ Filename = Obj.Filename;
+ Timestamp = sys::toTimeT(Obj.getTimestamp());
+ Entries.reserve(Obj.Symbols.size());
+ for (auto &Entry : Obj.Symbols)
+ Entries.push_back(
+ std::make_pair(std::string(Entry.getKey()), Entry.getValue()));
+}
+
+dsymutil::DebugMapObject
+MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) {
+ BinaryHolder BinHolder(vfs::getRealFileSystem(), /* Verbose =*/false);
+ const auto &Ctxt = *reinterpret_cast<YAMLContext *>(IO.getContext());
+ SmallString<80> Path(Ctxt.PrependPath);
+ StringMap<uint64_t> SymbolAddresses;
+
+ sys::path::append(Path, Filename);
+
+ auto ObjectEntry = BinHolder.getObjectEntry(Path);
+ if (!ObjectEntry) {
+ auto Err = ObjectEntry.takeError();
+ WithColor::warning() << "Unable to open " << Path << " "
+ << toString(std::move(Err)) << '\n';
+ } else {
+ auto Object = ObjectEntry->getObject(Ctxt.BinaryTriple);
+ if (!Object) {
+ auto Err = Object.takeError();
+ WithColor::warning() << "Unable to open " << Path << " "
+ << toString(std::move(Err)) << '\n';
+ } else {
+ for (const auto &Sym : Object->symbols()) {
+ Expected<uint64_t> AddressOrErr = Sym.getValue();
+ if (!AddressOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(AddressOrErr.takeError());
+ continue;
+ }
+ Expected<StringRef> Name = Sym.getName();
+ Expected<uint32_t> FlagsOrErr = Sym.getFlags();
+ if (!Name || !FlagsOrErr ||
+ (*FlagsOrErr & (SymbolRef::SF_Absolute | SymbolRef::SF_Common))) {
+ // TODO: Actually report errors helpfully.
+ if (!FlagsOrErr)
+ consumeError(FlagsOrErr.takeError());
+ if (!Name)
+ consumeError(Name.takeError());
+ continue;
+ }
+ SymbolAddresses[*Name] = *AddressOrErr;
+ }
+ }
+ }
+
+ dsymutil::DebugMapObject Res(Path, sys::toTimePoint(Timestamp), MachO::N_OSO);
+ for (auto &Entry : Entries) {
+ auto &Mapping = Entry.second;
+ Optional<uint64_t> ObjAddress;
+ if (Mapping.ObjectAddress)
+ ObjAddress = *Mapping.ObjectAddress;
+ auto AddressIt = SymbolAddresses.find(Entry.first);
+ if (AddressIt != SymbolAddresses.end())
+ ObjAddress = AddressIt->getValue();
+ Res.addSymbol(Entry.first, ObjAddress, Mapping.BinaryAddress, Mapping.Size);
+ }
+ return Res;
+}
+
+} // end namespace yaml
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/dsymutil/DebugMap.h b/contrib/libs/llvm14/tools/dsymutil/DebugMap.h
new file mode 100644
index 00000000000..e4fbaa81174
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/DebugMap.h
@@ -0,0 +1,270 @@
+//=- tools/dsymutil/DebugMap.h - Generic debug map representation -*- C++ -*-=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+///
+/// This file contains the class declaration of the DebugMap
+/// entity. A DebugMap lists all the object files linked together to
+/// produce an executable along with the linked address of all the
+/// atoms used in these object files.
+/// The DebugMap is an input to the DwarfLinker class that will
+/// extract the Dwarf debug information from the referenced object
+/// files and link their usefull debug info together.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H
+#define LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Support/Chrono.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace llvm {
+
+class raw_ostream;
+
+namespace dsymutil {
+
+class DebugMapObject;
+
+/// The DebugMap object stores the list of object files to query for debug
+/// information along with the mapping between the symbols' addresses in the
+/// object file to their linked address in the linked binary.
+///
+/// A DebugMap producer could look like this:
+/// DebugMap *DM = new DebugMap();
+/// for (const auto &Obj: LinkedObjects) {
+/// DebugMapObject &DMO = DM->addDebugMapObject(Obj.getPath());
+/// for (const auto &Sym: Obj.getLinkedSymbols())
+/// DMO.addSymbol(Sym.getName(), Sym.getObjectFileAddress(),
+/// Sym.getBinaryAddress());
+/// }
+///
+/// A DebugMap consumer can then use the map to link the debug
+/// information. For example something along the lines of:
+/// for (const auto &DMO: DM->objects()) {
+/// auto Obj = createBinary(DMO.getObjectFilename());
+/// for (auto &DIE: Obj.getDwarfDIEs()) {
+/// if (SymbolMapping *Sym = DMO.lookup(DIE.getName()))
+/// DIE.relocate(Sym->ObjectAddress, Sym->BinaryAddress);
+/// else
+/// DIE.discardSubtree();
+/// }
+/// }
+class DebugMap {
+ Triple BinaryTriple;
+ std::string BinaryPath;
+ std::vector<uint8_t> BinaryUUID;
+ using ObjectContainer = std::vector<std::unique_ptr<DebugMapObject>>;
+
+ ObjectContainer Objects;
+
+ /// For YAML IO support.
+ ///@{
+ friend yaml::MappingTraits<std::unique_ptr<DebugMap>>;
+ friend yaml::MappingTraits<DebugMap>;
+
+ DebugMap() = default;
+ ///@}
+
+public:
+ DebugMap(const Triple &BinaryTriple, StringRef BinaryPath,
+ ArrayRef<uint8_t> BinaryUUID = ArrayRef<uint8_t>())
+ : BinaryTriple(BinaryTriple), BinaryPath(std::string(BinaryPath)),
+ BinaryUUID(BinaryUUID.begin(), BinaryUUID.end()) {}
+
+ using const_iterator = ObjectContainer::const_iterator;
+
+ iterator_range<const_iterator> objects() const {
+ return make_range(begin(), end());
+ }
+
+ const_iterator begin() const { return Objects.begin(); }
+
+ const_iterator end() const { return Objects.end(); }
+
+ unsigned getNumberOfObjects() const { return Objects.size(); }
+
+ /// This function adds an DebugMapObject to the list owned by this
+ /// debug map.
+ DebugMapObject &
+ addDebugMapObject(StringRef ObjectFilePath,
+ sys::TimePoint<std::chrono::seconds> Timestamp,
+ uint8_t Type = llvm::MachO::N_OSO);
+
+ const Triple &getTriple() const { return BinaryTriple; }
+
+ ArrayRef<uint8_t> getUUID() const { return ArrayRef<uint8_t>(BinaryUUID); }
+
+ StringRef getBinaryPath() const { return BinaryPath; }
+
+ void print(raw_ostream &OS) const;
+
+#ifndef NDEBUG
+ void dump() const;
+#endif
+
+ /// Read a debug map for \a InputFile.
+ static ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
+ parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose);
+};
+
+/// The DebugMapObject represents one object file described by the DebugMap. It
+/// contains a list of mappings between addresses in the object file and in the
+/// linked binary for all the linked atoms in this object file.
+class DebugMapObject {
+public:
+ struct SymbolMapping {
+ Optional<yaml::Hex64> ObjectAddress;
+ yaml::Hex64 BinaryAddress;
+ yaml::Hex32 Size;
+
+ SymbolMapping(Optional<uint64_t> ObjectAddr, uint64_t BinaryAddress,
+ uint32_t Size)
+ : BinaryAddress(BinaryAddress), Size(Size) {
+ if (ObjectAddr)
+ ObjectAddress = *ObjectAddr;
+ }
+
+ /// For YAML IO support
+ SymbolMapping() = default;
+ };
+
+ using YAMLSymbolMapping = std::pair<std::string, SymbolMapping>;
+ using DebugMapEntry = StringMapEntry<SymbolMapping>;
+
+ /// Adds a symbol mapping to this DebugMapObject.
+ /// \returns false if the symbol was already registered. The request
+ /// is discarded in this case.
+ bool addSymbol(StringRef SymName, Optional<uint64_t> ObjectAddress,
+ uint64_t LinkedAddress, uint32_t Size);
+
+ /// Lookup a symbol mapping.
+ /// \returns null if the symbol isn't found.
+ const DebugMapEntry *lookupSymbol(StringRef SymbolName) const;
+
+ /// Lookup an object file address.
+ /// \returns null if the address isn't found.
+ const DebugMapEntry *lookupObjectAddress(uint64_t Address) const;
+
+ StringRef getObjectFilename() const { return Filename; }
+
+ sys::TimePoint<std::chrono::seconds> getTimestamp() const {
+ return Timestamp;
+ }
+
+ uint8_t getType() const { return Type; }
+
+ iterator_range<StringMap<SymbolMapping>::const_iterator> symbols() const {
+ return make_range(Symbols.begin(), Symbols.end());
+ }
+
+ bool empty() const { return Symbols.empty(); }
+
+ void addWarning(StringRef Warning) {
+ Warnings.push_back(std::string(Warning));
+ }
+ const std::vector<std::string> &getWarnings() const { return Warnings; }
+
+ void print(raw_ostream &OS) const;
+#ifndef NDEBUG
+ void dump() const;
+#endif
+
+private:
+ friend class DebugMap;
+
+ /// DebugMapObjects can only be constructed by the owning DebugMap.
+ DebugMapObject(StringRef ObjectFilename,
+ sys::TimePoint<std::chrono::seconds> Timestamp, uint8_t Type);
+
+ std::string Filename;
+ sys::TimePoint<std::chrono::seconds> Timestamp;
+ StringMap<SymbolMapping> Symbols;
+ DenseMap<uint64_t, DebugMapEntry *> AddressToMapping;
+ uint8_t Type;
+
+ std::vector<std::string> Warnings;
+
+ /// For YAMLIO support.
+ ///@{
+ friend yaml::MappingTraits<dsymutil::DebugMapObject>;
+ friend yaml::SequenceTraits<std::vector<std::unique_ptr<DebugMapObject>>>;
+
+ DebugMapObject() = default;
+
+public:
+ DebugMapObject(DebugMapObject &&) = default;
+ DebugMapObject &operator=(DebugMapObject &&) = default;
+ ///@}
+};
+
+} // end namespace dsymutil
+} // end namespace llvm
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::dsymutil::DebugMapObject::YAMLSymbolMapping)
+
+namespace llvm {
+namespace yaml {
+
+using namespace llvm::dsymutil;
+
+template <>
+struct MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>> {
+ static void mapping(IO &io,
+ std::pair<std::string, DebugMapObject::SymbolMapping> &s);
+ static const bool flow = true;
+};
+
+template <> struct MappingTraits<dsymutil::DebugMapObject> {
+ struct YamlDMO;
+ static void mapping(IO &io, dsymutil::DebugMapObject &DMO);
+};
+
+template <> struct ScalarTraits<Triple> {
+ static void output(const Triple &val, void *, raw_ostream &out);
+ static StringRef input(StringRef scalar, void *, Triple &value);
+ static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
+};
+
+template <>
+struct SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>> {
+ static size_t
+ size(IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq);
+ static dsymutil::DebugMapObject &
+ element(IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq,
+ size_t index);
+};
+
+template <> struct MappingTraits<dsymutil::DebugMap> {
+ static void mapping(IO &io, dsymutil::DebugMap &DM);
+};
+
+template <> struct MappingTraits<std::unique_ptr<dsymutil::DebugMap>> {
+ static void mapping(IO &io, std::unique_ptr<dsymutil::DebugMap> &DM);
+};
+
+} // end namespace yaml
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H
diff --git a/contrib/libs/llvm14/tools/dsymutil/DwarfLinkerForBinary.cpp b/contrib/libs/llvm14/tools/dsymutil/DwarfLinkerForBinary.cpp
new file mode 100644
index 00000000000..a7df034f1c5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/DwarfLinkerForBinary.cpp
@@ -0,0 +1,885 @@
+//===- tools/dsymutil/DwarfLinkerForBinary.cpp ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DwarfLinkerForBinary.h"
+#include "BinaryHolder.h"
+#include "DebugMap.h"
+#include "MachOUtils.h"
+#include "dsymutil.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/IntervalMap.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerIntPair.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/BinaryFormat/Swift.h"
+#include "llvm/CodeGen/AccelTable.h"
+#include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/CodeGen/DIE.h"
+#include "llvm/CodeGen/NonRelocatableStringpool.h"
+#include "llvm/Config/config.h"
+#include "llvm/DWARFLinker/DWARFLinkerDeclContext.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h"
+#include "llvm/DebugInfo/DWARF/DWARFDie.h"
+#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
+#include "llvm/DebugInfo/DWARF/DWARFSection.h"
+#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
+#include "llvm/MC/MCAsmBackend.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDwarf.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCObjectWriter.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSection.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/SymbolicFile.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/DJB.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+#include <algorithm>
+#include <cassert>
+#include <cinttypes>
+#include <climits>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <map>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace llvm {
+
+static mc::RegisterMCTargetOptionsFlags MOF;
+
+namespace dsymutil {
+
+static Error copySwiftInterfaces(
+ const std::map<std::string, std::string> &ParseableSwiftInterfaces,
+ StringRef Architecture, const LinkOptions &Options) {
+ std::error_code EC;
+ SmallString<128> InputPath;
+ SmallString<128> Path;
+ sys::path::append(Path, *Options.ResourceDir, "Swift", Architecture);
+ if ((EC = sys::fs::create_directories(Path.str(), true,
+ sys::fs::perms::all_all)))
+ return make_error<StringError>(
+ "cannot create directory: " + toString(errorCodeToError(EC)), EC);
+ unsigned BaseLength = Path.size();
+
+ for (auto &I : ParseableSwiftInterfaces) {
+ StringRef ModuleName = I.first;
+ StringRef InterfaceFile = I.second;
+ if (!Options.PrependPath.empty()) {
+ InputPath.clear();
+ sys::path::append(InputPath, Options.PrependPath, InterfaceFile);
+ InterfaceFile = InputPath;
+ }
+ sys::path::append(Path, ModuleName);
+ Path.append(".swiftinterface");
+ if (Options.Verbose)
+ outs() << "copy parseable Swift interface " << InterfaceFile << " -> "
+ << Path.str() << '\n';
+
+ // copy_file attempts an APFS clone first, so this should be cheap.
+ if ((EC = sys::fs::copy_file(InterfaceFile, Path.str())))
+ warn(Twine("cannot copy parseable Swift interface ") + InterfaceFile +
+ ": " + toString(errorCodeToError(EC)));
+ Path.resize(BaseLength);
+ }
+ return Error::success();
+}
+
+/// Report a warning to the user, optionally including information about a
+/// specific \p DIE related to the warning.
+void DwarfLinkerForBinary::reportWarning(const Twine &Warning,
+ StringRef Context,
+ const DWARFDie *DIE) const {
+
+ warn(Warning, Context);
+
+ if (!Options.Verbose || !DIE)
+ return;
+
+ DIDumpOptions DumpOpts;
+ DumpOpts.ChildRecurseDepth = 0;
+ DumpOpts.Verbose = Options.Verbose;
+
+ WithColor::note() << " in DIE:\n";
+ DIE->dump(errs(), 6 /* Indent */, DumpOpts);
+}
+
+bool DwarfLinkerForBinary::createStreamer(const Triple &TheTriple,
+ raw_fd_ostream &OutFile) {
+ if (Options.NoOutput)
+ return true;
+
+ Streamer = std::make_unique<DwarfStreamer>(
+ Options.FileType, OutFile, Options.Translator,
+ [&](const Twine &Error, StringRef Context, const DWARFDie *) {
+ error(Error, Context);
+ },
+ [&](const Twine &Warning, StringRef Context, const DWARFDie *) {
+ warn(Warning, Context);
+ });
+ return Streamer->init(TheTriple, "__DWARF");
+}
+
+ErrorOr<const object::ObjectFile &>
+DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj,
+ const Triple &Triple) {
+ auto ObjectEntry =
+ BinHolder.getObjectEntry(Obj.getObjectFilename(), Obj.getTimestamp());
+ if (!ObjectEntry) {
+ auto Err = ObjectEntry.takeError();
+ reportWarning(Twine(Obj.getObjectFilename()) + ": " +
+ toString(std::move(Err)),
+ Obj.getObjectFilename());
+ return errorToErrorCode(std::move(Err));
+ }
+
+ auto Object = ObjectEntry->getObject(Triple);
+ if (!Object) {
+ auto Err = Object.takeError();
+ reportWarning(Twine(Obj.getObjectFilename()) + ": " +
+ toString(std::move(Err)),
+ Obj.getObjectFilename());
+ return errorToErrorCode(std::move(Err));
+ }
+
+ return *Object;
+}
+
+static Error remarksErrorHandler(const DebugMapObject &DMO,
+ DwarfLinkerForBinary &Linker,
+ std::unique_ptr<FileError> FE) {
+ bool IsArchive = DMO.getObjectFilename().endswith(")");
+ // Don't report errors for missing remark files from static
+ // archives.
+ if (!IsArchive)
+ return Error(std::move(FE));
+
+ std::string Message = FE->message();
+ Error E = FE->takeError();
+ Error NewE = handleErrors(std::move(E), [&](std::unique_ptr<ECError> EC) {
+ if (EC->convertToErrorCode() != std::errc::no_such_file_or_directory)
+ return Error(std::move(EC));
+
+ Linker.reportWarning(Message, DMO.getObjectFilename());
+ return Error(Error::success());
+ });
+
+ if (!NewE)
+ return Error::success();
+
+ return createFileError(FE->getFileName(), std::move(NewE));
+}
+
+static Error emitRemarks(const LinkOptions &Options, StringRef BinaryPath,
+ StringRef ArchName, const remarks::RemarkLinker &RL) {
+ // Make sure we don't create the directories and the file if there is nothing
+ // to serialize.
+ if (RL.empty())
+ return Error::success();
+
+ SmallString<128> InputPath;
+ SmallString<128> Path;
+ // Create the "Remarks" directory in the "Resources" directory.
+ sys::path::append(Path, *Options.ResourceDir, "Remarks");
+ if (std::error_code EC = sys::fs::create_directories(Path.str(), true,
+ sys::fs::perms::all_all))
+ return errorCodeToError(EC);
+
+ // Append the file name.
+ // For fat binaries, also append a dash and the architecture name.
+ sys::path::append(Path, sys::path::filename(BinaryPath));
+ if (Options.NumDebugMaps > 1) {
+ // More than one debug map means we have a fat binary.
+ Path += '-';
+ Path += ArchName;
+ }
+
+ std::error_code EC;
+ raw_fd_ostream OS(Options.NoOutput ? "-" : Path.str(), EC,
+ Options.RemarksFormat == remarks::Format::Bitstream
+ ? sys::fs::OF_None
+ : sys::fs::OF_Text);
+ if (EC)
+ return errorCodeToError(EC);
+
+ if (Error E = RL.serialize(OS, Options.RemarksFormat))
+ return E;
+
+ return Error::success();
+}
+
+ErrorOr<DWARFFile &>
+DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj,
+ const DebugMap &DebugMap,
+ remarks::RemarkLinker &RL) {
+ auto ErrorOrObj = loadObject(Obj, DebugMap.getTriple());
+
+ if (ErrorOrObj) {
+ ContextForLinking.push_back(
+ std::unique_ptr<DWARFContext>(DWARFContext::create(*ErrorOrObj)));
+ AddressMapForLinking.push_back(
+ std::make_unique<AddressManager>(*this, *ErrorOrObj, Obj));
+
+ ObjectsForLinking.push_back(std::make_unique<DWARFFile>(
+ Obj.getObjectFilename(), ContextForLinking.back().get(),
+ AddressMapForLinking.back().get(),
+ Obj.empty() ? Obj.getWarnings() : EmptyWarnings));
+
+ Error E = RL.link(*ErrorOrObj);
+ if (Error NewE = handleErrors(
+ std::move(E), [&](std::unique_ptr<FileError> EC) -> Error {
+ return remarksErrorHandler(Obj, *this, std::move(EC));
+ }))
+ return errorToErrorCode(std::move(NewE));
+
+ return *ObjectsForLinking.back();
+ }
+
+ return ErrorOrObj.getError();
+}
+
+static bool binaryHasSwiftReflectionSections(const DebugMap &Map,
+ const LinkOptions &Options,
+ BinaryHolder &BinHolder) {
+ // If the input binary has swift5 reflection sections, there is no need to
+ // copy them to the .dSYM. Only copy them for binaries where the linker
+ // omitted the reflection metadata.
+ if (!Map.getBinaryPath().empty() &&
+ Options.FileType == OutputFileType::Object) {
+
+ auto ObjectEntry = BinHolder.getObjectEntry(Map.getBinaryPath());
+ // If ObjectEntry or Object has an error, no binary exists, therefore no
+ // reflection sections exist.
+ if (!ObjectEntry) {
+ // Any errors will be diagnosed later in the main loop, ignore them here.
+ llvm::consumeError(ObjectEntry.takeError());
+ return false;
+ }
+
+ auto Object =
+ ObjectEntry->getObjectAs<object::MachOObjectFile>(Map.getTriple());
+ if (!Object) {
+ // Any errors will be diagnosed later in the main loop, ignore them here.
+ llvm::consumeError(Object.takeError());
+ return false;
+ }
+
+ for (auto &Section : Object->sections()) {
+ llvm::Expected<llvm::StringRef> NameOrErr =
+ Object->getSectionName(Section.getRawDataRefImpl());
+ if (!NameOrErr) {
+ llvm::consumeError(NameOrErr.takeError());
+ continue;
+ }
+ NameOrErr->consume_back("__TEXT");
+ if (Object->mapReflectionSectionNameToEnumValue(*NameOrErr) !=
+ llvm::binaryformat::Swift5ReflectionSectionKind::unknown) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void
+copySwiftReflectionMetadata(const llvm::dsymutil::DebugMapObject *Obj,
+ DwarfStreamer *Streamer) {
+ auto OF =
+ llvm::object::ObjectFile::createObjectFile(Obj->getObjectFilename());
+ if (!OF) {
+ llvm::consumeError(OF.takeError());
+ return;
+ } else if (auto *MO =
+ dyn_cast<llvm::object::MachOObjectFile>(OF->getBinary())) {
+ for (auto &Section : OF->getBinary()->sections()) {
+ llvm::Expected<llvm::StringRef> NameOrErr =
+ MO->getSectionName(Section.getRawDataRefImpl());
+ if (!NameOrErr) {
+ llvm::consumeError(NameOrErr.takeError());
+ continue;
+ }
+ llvm::Expected<llvm::StringRef> SectionContents = Section.getContents();
+ if (SectionContents) {
+ NameOrErr->consume_back("__TEXT");
+ Streamer->emitSwiftReflectionSection(
+ MO->mapReflectionSectionNameToEnumValue(*NameOrErr),
+ *SectionContents, Section.getAlignment(), Section.getSize());
+ }
+ }
+ }
+}
+
+bool DwarfLinkerForBinary::link(const DebugMap &Map) {
+ if (!createStreamer(Map.getTriple(), OutFile))
+ return false;
+
+ ObjectsForLinking.clear();
+ ContextForLinking.clear();
+ AddressMapForLinking.clear();
+
+ DebugMap DebugMap(Map.getTriple(), Map.getBinaryPath());
+
+ DWARFLinker GeneralLinker(Streamer.get(), DwarfLinkerClient::Dsymutil);
+
+ remarks::RemarkLinker RL;
+ if (!Options.RemarksPrependPath.empty())
+ RL.setExternalFilePrependPath(Options.RemarksPrependPath);
+ GeneralLinker.setObjectPrefixMap(&Options.ObjectPrefixMap);
+
+ std::function<StringRef(StringRef)> TranslationLambda = [&](StringRef Input) {
+ assert(Options.Translator);
+ return Options.Translator(Input);
+ };
+
+ GeneralLinker.setVerbosity(Options.Verbose);
+ GeneralLinker.setStatistics(Options.Statistics);
+ GeneralLinker.setNoOutput(Options.NoOutput);
+ GeneralLinker.setNoODR(Options.NoODR);
+ GeneralLinker.setUpdate(Options.Update);
+ GeneralLinker.setNumThreads(Options.Threads);
+ GeneralLinker.setAccelTableKind(Options.TheAccelTableKind);
+ GeneralLinker.setPrependPath(Options.PrependPath);
+ GeneralLinker.setKeepFunctionForStatic(Options.KeepFunctionForStatic);
+ if (Options.Translator)
+ GeneralLinker.setStringsTranslator(TranslationLambda);
+ GeneralLinker.setWarningHandler(
+ [&](const Twine &Warning, StringRef Context, const DWARFDie *DIE) {
+ reportWarning(Warning, Context, DIE);
+ });
+ GeneralLinker.setErrorHandler(
+ [&](const Twine &Error, StringRef Context, const DWARFDie *) {
+ error(Error, Context);
+ });
+ GeneralLinker.setObjFileLoader(
+ [&DebugMap, &RL, this](StringRef ContainerName,
+ StringRef Path) -> ErrorOr<DWARFFile &> {
+ auto &Obj = DebugMap.addDebugMapObject(
+ Path, sys::TimePoint<std::chrono::seconds>(), MachO::N_OSO);
+
+ if (auto ErrorOrObj = loadObject(Obj, DebugMap, RL)) {
+ return *ErrorOrObj;
+ } else {
+ // Try and emit more helpful warnings by applying some heuristics.
+ StringRef ObjFile = ContainerName;
+ bool IsClangModule = sys::path::extension(Path).equals(".pcm");
+ bool IsArchive = ObjFile.endswith(")");
+
+ if (IsClangModule) {
+ StringRef ModuleCacheDir = sys::path::parent_path(Path);
+ if (sys::fs::exists(ModuleCacheDir)) {
+ // If the module's parent directory exists, we assume that the
+ // module cache has expired and was pruned by clang. A more
+ // adventurous dsymutil would invoke clang to rebuild the module
+ // now.
+ if (!ModuleCacheHintDisplayed) {
+ WithColor::note()
+ << "The clang module cache may have expired since "
+ "this object file was built. Rebuilding the "
+ "object file will rebuild the module cache.\n";
+ ModuleCacheHintDisplayed = true;
+ }
+ } else if (IsArchive) {
+ // If the module cache directory doesn't exist at all and the
+ // object file is inside a static library, we assume that the
+ // static library was built on a different machine. We don't want
+ // to discourage module debugging for convenience libraries within
+ // a project though.
+ if (!ArchiveHintDisplayed) {
+ WithColor::note()
+ << "Linking a static library that was built with "
+ "-gmodules, but the module cache was not found. "
+ "Redistributable static libraries should never be "
+ "built with module debugging enabled. The debug "
+ "experience will be degraded due to incomplete "
+ "debug information.\n";
+ ArchiveHintDisplayed = true;
+ }
+ }
+ }
+
+ return ErrorOrObj.getError();
+ }
+
+ llvm_unreachable("Unhandled DebugMap object");
+ });
+ GeneralLinker.setSwiftInterfacesMap(&ParseableSwiftInterfaces);
+ bool ReflectionSectionsPresentInBinary = false;
+ // If there is no output specified, no point in checking the binary for swift5
+ // reflection sections.
+ if (!Options.NoOutput) {
+ ReflectionSectionsPresentInBinary =
+ binaryHasSwiftReflectionSections(Map, Options, BinHolder);
+ }
+
+ for (const auto &Obj : Map.objects()) {
+ // If there is no output specified or the reflection sections are present in
+ // the Input binary, there is no need to copy the Swift Reflection Metadata
+ if (!Options.NoOutput && !ReflectionSectionsPresentInBinary)
+ copySwiftReflectionMetadata(Obj.get(), Streamer.get());
+
+ // N_AST objects (swiftmodule files) should get dumped directly into the
+ // appropriate DWARF section.
+ if (Obj->getType() == MachO::N_AST) {
+ if (Options.Verbose)
+ outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n";
+
+ StringRef File = Obj->getObjectFilename();
+ auto ErrorOrMem = MemoryBuffer::getFile(File);
+ if (!ErrorOrMem) {
+ warn("Could not open '" + File + "'\n");
+ continue;
+ }
+ sys::fs::file_status Stat;
+ if (auto Err = sys::fs::status(File, Stat)) {
+ warn(Err.message());
+ 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 (Obj->getTimestamp() != sys::TimePoint<>() &&
+ ModificationTime != Obj->getTimestamp()) {
+ // Not using the helper here as we can easily stream TimePoint<>.
+ WithColor::warning()
+ << File << ": timestamp mismatch between swift interface file ("
+ << sys::TimePoint<>(ModificationTime) << ") and debug map ("
+ << sys::TimePoint<>(Obj->getTimestamp()) << ")\n";
+ continue;
+ }
+ }
+
+ // Copy the module into the .swift_ast section.
+ if (!Options.NoOutput)
+ Streamer->emitSwiftAST((*ErrorOrMem)->getBuffer());
+
+ continue;
+ }
+ if (auto ErrorOrObj = loadObject(*Obj, Map, RL))
+ GeneralLinker.addObjectFile(*ErrorOrObj);
+ else {
+ ObjectsForLinking.push_back(std::make_unique<DWARFFile>(
+ Obj->getObjectFilename(), nullptr, nullptr,
+ Obj->empty() ? Obj->getWarnings() : EmptyWarnings));
+ GeneralLinker.addObjectFile(*ObjectsForLinking.back());
+ }
+ }
+
+ // link debug info for loaded object files.
+ GeneralLinker.link();
+
+ StringRef ArchName = Map.getTriple().getArchName();
+ if (Error E = emitRemarks(Options, Map.getBinaryPath(), ArchName, RL))
+ return error(toString(std::move(E)));
+
+ if (Options.NoOutput)
+ return true;
+
+ if (Options.ResourceDir && !ParseableSwiftInterfaces.empty()) {
+ StringRef ArchName = Triple::getArchTypeName(Map.getTriple().getArch());
+ if (auto E =
+ copySwiftInterfaces(ParseableSwiftInterfaces, ArchName, Options))
+ return error(toString(std::move(E)));
+ }
+
+ if (Map.getTriple().isOSDarwin() && !Map.getBinaryPath().empty() &&
+ Options.FileType == OutputFileType::Object)
+ return MachOUtils::generateDsymCompanion(
+ Options.VFS, Map, Options.Translator,
+ *Streamer->getAsmPrinter().OutStreamer, OutFile);
+
+ Streamer->finish();
+ return true;
+}
+
+static bool isMachOPairedReloc(uint64_t RelocType, uint64_t Arch) {
+ switch (Arch) {
+ case Triple::x86:
+ return RelocType == MachO::GENERIC_RELOC_SECTDIFF ||
+ RelocType == MachO::GENERIC_RELOC_LOCAL_SECTDIFF;
+ case Triple::x86_64:
+ return RelocType == MachO::X86_64_RELOC_SUBTRACTOR;
+ case Triple::arm:
+ case Triple::thumb:
+ return RelocType == MachO::ARM_RELOC_SECTDIFF ||
+ RelocType == MachO::ARM_RELOC_LOCAL_SECTDIFF ||
+ RelocType == MachO::ARM_RELOC_HALF ||
+ RelocType == MachO::ARM_RELOC_HALF_SECTDIFF;
+ case Triple::aarch64:
+ return RelocType == MachO::ARM64_RELOC_SUBTRACTOR;
+ default:
+ return false;
+ }
+}
+
+/// Iterate over the relocations of the given \p Section and
+/// store the ones that correspond to debug map entries into the
+/// ValidRelocs array.
+void DwarfLinkerForBinary::AddressManager::findValidRelocsMachO(
+ const object::SectionRef &Section, const object::MachOObjectFile &Obj,
+ const DebugMapObject &DMO, std::vector<ValidReloc> &ValidRelocs) {
+ Expected<StringRef> ContentsOrErr = Section.getContents();
+ if (!ContentsOrErr) {
+ consumeError(ContentsOrErr.takeError());
+ Linker.reportWarning("error reading section", DMO.getObjectFilename());
+ return;
+ }
+ DataExtractor Data(*ContentsOrErr, Obj.isLittleEndian(), 0);
+ bool SkipNext = false;
+
+ for (const object::RelocationRef &Reloc : Section.relocations()) {
+ if (SkipNext) {
+ SkipNext = false;
+ continue;
+ }
+
+ object::DataRefImpl RelocDataRef = Reloc.getRawDataRefImpl();
+ MachO::any_relocation_info MachOReloc = Obj.getRelocation(RelocDataRef);
+
+ if (isMachOPairedReloc(Obj.getAnyRelocationType(MachOReloc),
+ Obj.getArch())) {
+ SkipNext = true;
+ Linker.reportWarning("unsupported relocation in " + *Section.getName() +
+ " section.",
+ DMO.getObjectFilename());
+ continue;
+ }
+
+ unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc);
+ uint64_t Offset64 = Reloc.getOffset();
+ if ((RelocSize != 4 && RelocSize != 8)) {
+ Linker.reportWarning("unsupported relocation in " + *Section.getName() +
+ " section.",
+ DMO.getObjectFilename());
+ continue;
+ }
+ uint64_t OffsetCopy = Offset64;
+ // Mach-o uses REL relocations, the addend is at the relocation offset.
+ uint64_t Addend = Data.getUnsigned(&OffsetCopy, RelocSize);
+ uint64_t SymAddress;
+ int64_t SymOffset;
+
+ if (Obj.isRelocationScattered(MachOReloc)) {
+ // The address of the base symbol for scattered relocations is
+ // stored in the reloc itself. The actual addend will store the
+ // base address plus the offset.
+ SymAddress = Obj.getScatteredRelocationValue(MachOReloc);
+ SymOffset = int64_t(Addend) - SymAddress;
+ } else {
+ SymAddress = Addend;
+ SymOffset = 0;
+ }
+
+ auto Sym = Reloc.getSymbol();
+ if (Sym != Obj.symbol_end()) {
+ Expected<StringRef> SymbolName = Sym->getName();
+ if (!SymbolName) {
+ consumeError(SymbolName.takeError());
+ Linker.reportWarning("error getting relocation symbol name.",
+ DMO.getObjectFilename());
+ continue;
+ }
+ if (const auto *Mapping = DMO.lookupSymbol(*SymbolName))
+ ValidRelocs.emplace_back(Offset64, RelocSize, Addend, Mapping);
+ } else if (const auto *Mapping = DMO.lookupObjectAddress(SymAddress)) {
+ // Do not store the addend. The addend was the address of the symbol in
+ // the object file, the address in the binary that is stored in the debug
+ // map doesn't need to be offset.
+ ValidRelocs.emplace_back(Offset64, RelocSize, SymOffset, Mapping);
+ }
+ }
+}
+
+/// Dispatch the valid relocation finding logic to the
+/// appropriate handler depending on the object file format.
+bool DwarfLinkerForBinary::AddressManager::findValidRelocs(
+ const object::SectionRef &Section, const object::ObjectFile &Obj,
+ const DebugMapObject &DMO, std::vector<ValidReloc> &Relocs) {
+ // Dispatch to the right handler depending on the file type.
+ if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Obj))
+ findValidRelocsMachO(Section, *MachOObj, DMO, Relocs);
+ else
+ Linker.reportWarning(Twine("unsupported object file type: ") +
+ Obj.getFileName(),
+ DMO.getObjectFilename());
+ if (Relocs.empty())
+ return false;
+
+ // Sort the relocations by offset. We will walk the DIEs linearly in
+ // 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 DwarfLinkerForBinary::NextValidReloc.
+ llvm::sort(Relocs);
+ return true;
+}
+
+/// Look for relocations in the debug_info and debug_addr section that match
+/// entries in the debug map. These relocations will drive the Dwarf link by
+/// indicating which DIEs refer to symbols present in the linked binary.
+/// \returns whether there are any valid relocations in the debug info.
+bool DwarfLinkerForBinary::AddressManager::findValidRelocsInDebugSections(
+ const object::ObjectFile &Obj, const DebugMapObject &DMO) {
+ // Find the debug_info section.
+ bool FoundValidRelocs = false;
+ for (const object::SectionRef &Section : Obj.sections()) {
+ StringRef SectionName;
+ if (Expected<StringRef> NameOrErr = Section.getName())
+ SectionName = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ SectionName = SectionName.substr(SectionName.find_first_not_of("._"));
+ if (SectionName == "debug_info")
+ FoundValidRelocs |=
+ findValidRelocs(Section, Obj, DMO, ValidDebugInfoRelocs);
+ if (SectionName == "debug_addr")
+ FoundValidRelocs |=
+ findValidRelocs(Section, Obj, DMO, ValidDebugAddrRelocs);
+ }
+ return FoundValidRelocs;
+}
+
+std::vector<DwarfLinkerForBinary::AddressManager::ValidReloc>
+DwarfLinkerForBinary::AddressManager::getRelocations(
+ const std::vector<ValidReloc> &Relocs, uint64_t StartPos, uint64_t EndPos) {
+ std::vector<DwarfLinkerForBinary::AddressManager::ValidReloc> Res;
+
+ auto CurReloc = partition_point(Relocs, [StartPos](const ValidReloc &Reloc) {
+ return Reloc.Offset < StartPos;
+ });
+
+ while (CurReloc != Relocs.end() && CurReloc->Offset >= StartPos &&
+ CurReloc->Offset < EndPos) {
+ Res.push_back(*CurReloc);
+ CurReloc++;
+ }
+
+ return Res;
+}
+
+void DwarfLinkerForBinary::AddressManager::printReloc(const ValidReloc &Reloc) {
+ const auto &Mapping = Reloc.Mapping->getValue();
+ const uint64_t ObjectAddress = Mapping.ObjectAddress
+ ? uint64_t(*Mapping.ObjectAddress)
+ : std::numeric_limits<uint64_t>::max();
+
+ outs() << "Found valid debug map entry: " << Reloc.Mapping->getKey() << "\t"
+ << format("0x%016" PRIx64 " => 0x%016" PRIx64 "\n", ObjectAddress,
+ uint64_t(Mapping.BinaryAddress));
+}
+
+void DwarfLinkerForBinary::AddressManager::fillDieInfo(
+ const ValidReloc &Reloc, CompileUnit::DIEInfo &Info) {
+ Info.AddrAdjust = relocate(Reloc);
+ if (Reloc.Mapping->getValue().ObjectAddress)
+ Info.AddrAdjust -= uint64_t(*Reloc.Mapping->getValue().ObjectAddress);
+ Info.InDebugMap = true;
+}
+
+bool DwarfLinkerForBinary::AddressManager::hasValidRelocationAt(
+ const std::vector<ValidReloc> &AllRelocs, uint64_t StartOffset,
+ uint64_t EndOffset, CompileUnit::DIEInfo &Info) {
+ std::vector<ValidReloc> Relocs =
+ getRelocations(AllRelocs, StartOffset, EndOffset);
+
+ if (Relocs.size() == 0)
+ return false;
+
+ if (Linker.Options.Verbose)
+ printReloc(Relocs[0]);
+ fillDieInfo(Relocs[0], Info);
+
+ return true;
+}
+
+/// Get the starting and ending (exclusive) offset for the
+/// attribute with index \p Idx descibed by \p Abbrev. \p Offset is
+/// supposed to point to the position of the first attribute described
+/// by \p Abbrev.
+/// \return [StartOffset, EndOffset) as a pair.
+static std::pair<uint64_t, uint64_t>
+getAttributeOffsets(const DWARFAbbreviationDeclaration *Abbrev, unsigned Idx,
+ uint64_t Offset, const DWARFUnit &Unit) {
+ DataExtractor Data = Unit.getDebugInfoExtractor();
+
+ for (unsigned I = 0; I < Idx; ++I)
+ DWARFFormValue::skipValue(Abbrev->getFormByIndex(I), Data, &Offset,
+ Unit.getFormParams());
+
+ uint64_t End = Offset;
+ DWARFFormValue::skipValue(Abbrev->getFormByIndex(Idx), Data, &End,
+ Unit.getFormParams());
+
+ return std::make_pair(Offset, End);
+}
+
+bool DwarfLinkerForBinary::AddressManager::hasLiveMemoryLocation(
+ const DWARFDie &DIE, CompileUnit::DIEInfo &MyInfo) {
+ const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
+
+ Optional<uint32_t> LocationIdx =
+ Abbrev->findAttributeIndex(dwarf::DW_AT_location);
+ if (!LocationIdx)
+ return false;
+
+ uint64_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode());
+ uint64_t LocationOffset, LocationEndOffset;
+ std::tie(LocationOffset, LocationEndOffset) =
+ getAttributeOffsets(Abbrev, *LocationIdx, Offset, *DIE.getDwarfUnit());
+
+ // FIXME: Support relocations debug_addr.
+ return hasValidRelocationAt(ValidDebugInfoRelocs, LocationOffset,
+ LocationEndOffset, MyInfo);
+}
+
+bool DwarfLinkerForBinary::AddressManager::hasLiveAddressRange(
+ const DWARFDie &DIE, CompileUnit::DIEInfo &MyInfo) {
+ const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
+
+ Optional<uint32_t> LowPcIdx = Abbrev->findAttributeIndex(dwarf::DW_AT_low_pc);
+ if (!LowPcIdx)
+ return false;
+
+ dwarf::Form Form = Abbrev->getFormByIndex(*LowPcIdx);
+
+ if (Form == dwarf::DW_FORM_addr) {
+ uint64_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode());
+ uint64_t LowPcOffset, LowPcEndOffset;
+ std::tie(LowPcOffset, LowPcEndOffset) =
+ getAttributeOffsets(Abbrev, *LowPcIdx, Offset, *DIE.getDwarfUnit());
+ return hasValidRelocationAt(ValidDebugInfoRelocs, LowPcOffset,
+ LowPcEndOffset, MyInfo);
+ }
+
+ if (Form == dwarf::DW_FORM_addrx) {
+ Optional<DWARFFormValue> AddrValue = DIE.find(dwarf::DW_AT_low_pc);
+ if (Optional<uint64_t> AddrOffsetSectionBase =
+ DIE.getDwarfUnit()->getAddrOffsetSectionBase()) {
+ uint64_t StartOffset = *AddrOffsetSectionBase + AddrValue->getRawUValue();
+ uint64_t EndOffset =
+ StartOffset + DIE.getDwarfUnit()->getAddressByteSize();
+ return hasValidRelocationAt(ValidDebugAddrRelocs, StartOffset, EndOffset,
+ MyInfo);
+ } else
+ Linker.reportWarning("no base offset for address table", SrcFileName);
+ }
+
+ return false;
+}
+
+uint64_t
+DwarfLinkerForBinary::AddressManager::relocate(const ValidReloc &Reloc) const {
+ return Reloc.Mapping->getValue().BinaryAddress + Reloc.Addend;
+}
+
+/// Apply the valid relocations found by findValidRelocs() to
+/// the buffer \p Data, taking into account that Data is at \p BaseOffset
+/// in the debug_info section.
+///
+/// Like for findValidRelocs(), this function must be called with
+/// monotonic \p BaseOffset values.
+///
+/// \returns whether any reloc has been applied.
+bool DwarfLinkerForBinary::AddressManager::applyValidRelocs(
+ MutableArrayRef<char> Data, uint64_t BaseOffset, bool IsLittleEndian) {
+ assert(areRelocationsResolved());
+ std::vector<ValidReloc> Relocs = getRelocations(
+ ValidDebugInfoRelocs, BaseOffset, BaseOffset + Data.size());
+
+ for (const ValidReloc &CurReloc : Relocs) {
+ assert(CurReloc.Offset - BaseOffset < Data.size());
+ assert(CurReloc.Offset - BaseOffset + CurReloc.Size <= Data.size());
+ char Buf[8];
+ uint64_t Value = relocate(CurReloc);
+ for (unsigned I = 0; I != CurReloc.Size; ++I) {
+ unsigned Index = IsLittleEndian ? I : (CurReloc.Size - I - 1);
+ Buf[I] = uint8_t(Value >> (Index * 8));
+ }
+ assert(CurReloc.Size <= sizeof(Buf));
+ memcpy(&Data[CurReloc.Offset - BaseOffset], Buf, CurReloc.Size);
+ }
+
+ return Relocs.size() > 0;
+}
+
+llvm::Expected<uint64_t>
+DwarfLinkerForBinary::AddressManager::relocateIndexedAddr(uint64_t StartOffset,
+ uint64_t EndOffset) {
+ std::vector<ValidReloc> Relocs =
+ getRelocations(ValidDebugAddrRelocs, StartOffset, EndOffset);
+ if (Relocs.size() == 0)
+ return createStringError(
+ std::make_error_code(std::errc::invalid_argument),
+ "no relocation for offset %llu in debug_addr section", StartOffset);
+
+ return relocate(Relocs[0]);
+}
+
+bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder,
+ const DebugMap &DM, LinkOptions Options) {
+ DwarfLinkerForBinary Linker(OutFile, BinHolder, std::move(Options));
+ return Linker.link(DM);
+}
+
+} // namespace dsymutil
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/dsymutil/DwarfLinkerForBinary.h b/contrib/libs/llvm14/tools/dsymutil/DwarfLinkerForBinary.h
new file mode 100644
index 00000000000..dc4691b69c5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/DwarfLinkerForBinary.h
@@ -0,0 +1,228 @@
+//===- tools/dsymutil/DwarfLinkerForBinary.h --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
+#define LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
+
+#include "BinaryHolder.h"
+#include "DebugMap.h"
+#include "LinkUtils.h"
+#include "llvm/DWARFLinker/DWARFLinker.h"
+#include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h"
+#include "llvm/DWARFLinker/DWARFLinkerDeclContext.h"
+#include "llvm/DWARFLinker/DWARFStreamer.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Remarks/RemarkFormat.h"
+#include "llvm/Remarks/RemarkLinker.h"
+
+namespace llvm {
+namespace dsymutil {
+
+/// The core of the Dsymutil Dwarf linking logic.
+///
+/// The link of the dwarf information from the object files will be
+/// driven by DWARFLinker. DwarfLinkerForBinary reads DebugMap objects
+/// and pass information to the DWARFLinker. DWARFLinker
+/// optimizes DWARF taking into account valid relocations.
+/// Finally, optimized DWARF is passed to DwarfLinkerForBinary through
+/// DWARFEmitter interface.
+class DwarfLinkerForBinary {
+public:
+ DwarfLinkerForBinary(raw_fd_ostream &OutFile, BinaryHolder &BinHolder,
+ LinkOptions Options)
+ : OutFile(OutFile), BinHolder(BinHolder), Options(std::move(Options)) {}
+
+ /// Link the contents of the DebugMap.
+ bool link(const DebugMap &);
+
+ void reportWarning(const Twine &Warning, StringRef Context,
+ const DWARFDie *DIE = nullptr) const;
+
+ /// Flags passed to DwarfLinker::lookForDIEsToKeep
+ enum TraversalFlags {
+ TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept.
+ TF_InFunctionScope = 1 << 1, ///< Current scope is a function scope.
+ TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE.
+ TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE.
+ TF_ODR = 1 << 4, ///< Use the ODR while keeping dependents.
+ TF_SkipPC = 1 << 5, ///< Skip all location attributes.
+ };
+
+private:
+
+ /// Keeps track of relocations.
+ class AddressManager : public AddressesMap {
+ struct ValidReloc {
+ uint64_t Offset;
+ uint32_t Size;
+ uint64_t Addend;
+ const DebugMapObject::DebugMapEntry *Mapping;
+
+ ValidReloc(uint64_t Offset, uint32_t Size, uint64_t Addend,
+ const DebugMapObject::DebugMapEntry *Mapping)
+ : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {}
+
+ bool operator<(const ValidReloc &RHS) const {
+ return Offset < RHS.Offset;
+ }
+ bool operator<(uint64_t RHS) const { return Offset < RHS; }
+ };
+
+ const DwarfLinkerForBinary &Linker;
+
+ /// The valid relocations for the current DebugMapObject.
+ /// This vector is sorted by relocation offset.
+ /// {
+ std::vector<ValidReloc> ValidDebugInfoRelocs;
+ std::vector<ValidReloc> ValidDebugAddrRelocs;
+ /// }
+
+ RangesTy AddressRanges;
+
+ StringRef SrcFileName;
+
+ /// Returns list of valid relocations from \p Relocs,
+ /// between \p StartOffset and \p NextOffset.
+ ///
+ /// \returns true if any relocation is found.
+ std::vector<ValidReloc>
+ getRelocations(const std::vector<ValidReloc> &Relocs, uint64_t StartPos,
+ uint64_t EndPos);
+
+ /// Resolve specified relocation \p Reloc.
+ ///
+ /// \returns resolved value.
+ uint64_t relocate(const ValidReloc &Reloc) const;
+
+ /// Fill \p Info with address information for the specified \p Reloc.
+ void fillDieInfo(const ValidReloc &Reloc, CompileUnit::DIEInfo &Info);
+
+ /// Print contents of debug map entry for the specified \p Reloc.
+ void printReloc(const ValidReloc &Reloc);
+
+ public:
+ AddressManager(DwarfLinkerForBinary &Linker, const object::ObjectFile &Obj,
+ const DebugMapObject &DMO)
+ : Linker(Linker), SrcFileName(DMO.getObjectFilename()) {
+ findValidRelocsInDebugSections(Obj, DMO);
+
+ // Iterate over the debug map entries and put all the ones that are
+ // functions (because they have a size) into the Ranges map. This map is
+ // very similar to the FunctionRanges that are stored in each unit, with 2
+ // notable differences:
+ //
+ // 1. Obviously this one is global, while the other ones are per-unit.
+ //
+ // 2. This one contains not only the functions described in the DIE
+ // tree, but also the ones that are only in the debug map.
+ //
+ // The latter information is required to reproduce dsymutil's logic while
+ // linking line tables. The cases where this information matters look like
+ // bugs that need to be investigated, but for now we need to reproduce
+ // dsymutil's behavior.
+ // FIXME: Once we understood exactly if that information is needed,
+ // maybe totally remove this (or try to use it to do a real
+ // -gline-tables-only on Darwin.
+ for (const auto &Entry : DMO.symbols()) {
+ const auto &Mapping = Entry.getValue();
+ if (Mapping.Size && Mapping.ObjectAddress)
+ AddressRanges[*Mapping.ObjectAddress] = ObjFileAddressRange(
+ *Mapping.ObjectAddress + Mapping.Size,
+ int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress);
+ }
+ }
+ virtual ~AddressManager() override { clear(); }
+
+ virtual bool areRelocationsResolved() const override { return true; }
+
+ bool hasValidRelocs() override {
+ return !ValidDebugInfoRelocs.empty() || !ValidDebugAddrRelocs.empty();
+ }
+
+ /// \defgroup FindValidRelocations Translate debug map into a list
+ /// of relevant relocations
+ ///
+ /// @{
+ bool findValidRelocsInDebugSections(const object::ObjectFile &Obj,
+ const DebugMapObject &DMO);
+
+ bool findValidRelocs(const object::SectionRef &Section,
+ const object::ObjectFile &Obj,
+ const DebugMapObject &DMO,
+ std::vector<ValidReloc> &ValidRelocs);
+
+ void findValidRelocsMachO(const object::SectionRef &Section,
+ const object::MachOObjectFile &Obj,
+ const DebugMapObject &DMO,
+ std::vector<ValidReloc> &ValidRelocs);
+ /// @}
+
+ /// Checks that there is a relocation in the \p Relocs array against a
+ /// debug map entry between \p StartOffset and \p NextOffset.
+ ///
+ /// \returns true and sets Info.InDebugMap if it is the case.
+ bool hasValidRelocationAt(const std::vector<ValidReloc> &Relocs,
+ uint64_t StartOffset, uint64_t EndOffset,
+ CompileUnit::DIEInfo &Info);
+
+ bool hasLiveMemoryLocation(const DWARFDie &DIE,
+ CompileUnit::DIEInfo &Info) override;
+ bool hasLiveAddressRange(const DWARFDie &DIE,
+ CompileUnit::DIEInfo &Info) override;
+
+ bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset,
+ bool IsLittleEndian) override;
+
+ llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t StartOffset,
+ uint64_t EndOffset) override;
+
+ RangesTy &getValidAddressRanges() override { return AddressRanges; }
+
+ void clear() override {
+ AddressRanges.clear();
+ ValidDebugInfoRelocs.clear();
+ ValidDebugAddrRelocs.clear();
+ }
+ };
+
+private:
+ /// \defgroup Helpers Various helper methods.
+ ///
+ /// @{
+ bool createStreamer(const Triple &TheTriple, raw_fd_ostream &OutFile);
+
+ /// Attempt to load a debug object from disk.
+ ErrorOr<const object::ObjectFile &> loadObject(const DebugMapObject &Obj,
+ const Triple &triple);
+ ErrorOr<DWARFFile &> loadObject(const DebugMapObject &Obj,
+ const DebugMap &DebugMap,
+ remarks::RemarkLinker &RL);
+
+ raw_fd_ostream &OutFile;
+ BinaryHolder &BinHolder;
+ LinkOptions Options;
+ std::unique_ptr<DwarfStreamer> Streamer;
+ std::vector<std::unique_ptr<DWARFFile>> ObjectsForLinking;
+ std::vector<std::unique_ptr<DWARFContext>> ContextForLinking;
+ std::vector<std::unique_ptr<AddressManager>> AddressMapForLinking;
+ std::vector<std::string> EmptyWarnings;
+
+ /// A list of all .swiftinterface files referenced by the debug
+ /// info, mapping Module name to path on disk. The entries need to
+ /// be uniqued and sorted and there are only few entries expected
+ /// per compile unit, which is why this is a std::map.
+ std::map<std::string, std::string> ParseableSwiftInterfaces;
+
+ bool ModuleCacheHintDisplayed = false;
+ bool ArchiveHintDisplayed = false;
+};
+
+} // end namespace dsymutil
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
diff --git a/contrib/libs/llvm14/tools/dsymutil/LinkUtils.h b/contrib/libs/llvm14/tools/dsymutil/LinkUtils.h
new file mode 100644
index 00000000000..872a65deb4d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/LinkUtils.h
@@ -0,0 +1,108 @@
+//===- tools/dsymutil/LinkUtils.h - Dwarf linker utilities ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H
+#define LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H
+
+#include "SymbolMap.h"
+
+#include "llvm/ADT/Twine.h"
+#include "llvm/Remarks/RemarkFormat.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include "llvm/Support/WithColor.h"
+
+#include "llvm/DWARFLinker/DWARFLinker.h"
+#include "llvm/DWARFLinker/DWARFStreamer.h"
+#include <string>
+
+namespace llvm {
+namespace dsymutil {
+
+struct LinkOptions {
+ /// Verbosity
+ bool Verbose = false;
+
+ /// Statistics
+ bool Statistics = false;
+
+ /// Skip emitting output
+ bool NoOutput = false;
+
+ /// Do not unique types according to ODR
+ bool NoODR = false;
+
+ /// Update
+ bool Update = false;
+
+ /// Do not check swiftmodule timestamp
+ bool NoTimestamp = false;
+
+ /// Whether we want a static variable to force us to keep its enclosing
+ /// function.
+ bool KeepFunctionForStatic = false;
+
+ /// Number of threads.
+ unsigned Threads = 1;
+
+ // Output file type.
+ OutputFileType FileType = OutputFileType::Object;
+
+ /// The accelerator table kind
+ AccelTableKind TheAccelTableKind;
+
+ /// -oso-prepend-path
+ std::string PrependPath;
+
+ /// The -object-prefix-map.
+ std::map<std::string, std::string> ObjectPrefixMap;
+
+ /// The Resources directory in the .dSYM bundle.
+ Optional<std::string> ResourceDir;
+
+ /// Symbol map translator.
+ SymbolMapTranslator Translator;
+
+ /// Virtual File System.
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
+ vfs::getRealFileSystem();
+
+ /// Fields used for linking and placing remarks into the .dSYM bundle.
+ /// @{
+
+ /// Number of debug maps processed in total.
+ unsigned NumDebugMaps = 0;
+
+ /// -remarks-prepend-path: prepend a path to all the external remark file
+ /// paths found in remark metadata.
+ std::string RemarksPrependPath;
+
+ /// The output format of the remarks.
+ remarks::Format RemarksFormat = remarks::Format::Bitstream;
+
+ /// @}
+
+ LinkOptions() = default;
+};
+
+inline void warn(Twine Warning, Twine Context = {}) {
+ WithColor::warning() << Warning + "\n";
+ if (!Context.isTriviallyEmpty())
+ WithColor::note() << Twine("while processing ") + Context + "\n";
+}
+
+inline bool error(Twine Error, Twine Context = {}) {
+ WithColor::error() << Error + "\n";
+ if (!Context.isTriviallyEmpty())
+ WithColor::note() << Twine("while processing ") + Context + "\n";
+ return false;
+}
+
+} // end namespace dsymutil
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H
diff --git a/contrib/libs/llvm14/tools/dsymutil/MachODebugMapParser.cpp b/contrib/libs/llvm14/tools/dsymutil/MachODebugMapParser.cpp
new file mode 100644
index 00000000000..45774c6c069
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/MachODebugMapParser.cpp
@@ -0,0 +1,622 @@
+//===- tools/dsymutil/MachODebugMapParser.cpp - Parse STABS debug maps ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "BinaryHolder.h"
+#include "DebugMap.h"
+#include "MachOUtils.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <vector>
+
+namespace {
+using namespace llvm;
+using namespace llvm::dsymutil;
+using namespace llvm::object;
+
+class MachODebugMapParser {
+public:
+ MachODebugMapParser(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
+ StringRef BinaryPath, ArrayRef<std::string> Archs,
+ StringRef PathPrefix = "",
+ bool PaperTrailWarnings = false, bool Verbose = false)
+ : BinaryPath(std::string(BinaryPath)), Archs(Archs.begin(), Archs.end()),
+ PathPrefix(std::string(PathPrefix)),
+ PaperTrailWarnings(PaperTrailWarnings), BinHolder(VFS, Verbose),
+ CurrentDebugMapObject(nullptr) {}
+
+ /// Parses and returns the DebugMaps of the input binary. The binary contains
+ /// multiple maps in case it is a universal binary.
+ /// \returns an error in case the provided BinaryPath doesn't exist
+ /// or isn't of a supported type.
+ ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parse();
+
+ /// Walk the symbol table and dump it.
+ bool dumpStab();
+
+private:
+ std::string BinaryPath;
+ SmallVector<StringRef, 1> Archs;
+ std::string PathPrefix;
+ bool PaperTrailWarnings;
+
+ /// Owns the MemoryBuffer for the main binary.
+ BinaryHolder BinHolder;
+ /// Map of the binary symbol addresses.
+ StringMap<uint64_t> MainBinarySymbolAddresses;
+ StringRef MainBinaryStrings;
+ /// The constructed DebugMap.
+ std::unique_ptr<DebugMap> Result;
+ /// List of common symbols that need to be added to the debug map.
+ std::vector<std::string> CommonSymbols;
+
+ /// Map of the currently processed object file symbol addresses.
+ StringMap<Optional<uint64_t>> CurrentObjectAddresses;
+ /// Element of the debug map corresponding to the current object file.
+ DebugMapObject *CurrentDebugMapObject;
+
+ /// Holds function info while function scope processing.
+ const char *CurrentFunctionName;
+ uint64_t CurrentFunctionAddress;
+
+ std::unique_ptr<DebugMap> parseOneBinary(const MachOObjectFile &MainBinary,
+ StringRef BinaryPath);
+
+ void
+ switchToNewDebugMapObject(StringRef Filename,
+ sys::TimePoint<std::chrono::seconds> Timestamp);
+ void resetParserState();
+ uint64_t getMainBinarySymbolAddress(StringRef Name);
+ std::vector<StringRef> getMainBinarySymbolNames(uint64_t Value);
+ void loadMainBinarySymbols(const MachOObjectFile &MainBinary);
+ void loadCurrentObjectFileSymbols(const object::MachOObjectFile &Obj);
+ void handleStabSymbolTableEntry(uint32_t StringIndex, uint8_t Type,
+ uint8_t SectionIndex, uint16_t Flags,
+ uint64_t Value);
+
+ template <typename STEType> void handleStabDebugMapEntry(const STEType &STE) {
+ handleStabSymbolTableEntry(STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc,
+ STE.n_value);
+ }
+
+ void addCommonSymbols();
+
+ /// Dump the symbol table output header.
+ void dumpSymTabHeader(raw_ostream &OS, StringRef Arch);
+
+ /// Dump the contents of nlist entries.
+ void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, uint32_t StringIndex,
+ uint8_t Type, uint8_t SectionIndex, uint16_t Flags,
+ uint64_t Value);
+
+ template <typename STEType>
+ void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, const STEType &STE) {
+ dumpSymTabEntry(OS, Index, STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc,
+ STE.n_value);
+ }
+ void dumpOneBinaryStab(const MachOObjectFile &MainBinary,
+ StringRef BinaryPath);
+
+ void Warning(const Twine &Msg, StringRef File = StringRef()) {
+ WithColor::warning() << "("
+ << MachOUtils::getArchName(
+ Result->getTriple().getArchName())
+ << ") " << File << " " << Msg << "\n";
+
+ if (PaperTrailWarnings) {
+ if (!File.empty())
+ Result->addDebugMapObject(File, sys::TimePoint<std::chrono::seconds>());
+ if (Result->end() != Result->begin()) {
+ auto it = Result->end();
+ (*--it)->addWarning(Msg.str());
+ }
+ }
+ }
+};
+
+} // anonymous namespace
+
+/// Reset the parser state corresponding to the current object
+/// file. This is to be called after an object file is finished
+/// processing.
+void MachODebugMapParser::resetParserState() {
+ CommonSymbols.clear();
+ CurrentObjectAddresses.clear();
+ CurrentDebugMapObject = nullptr;
+}
+
+/// Commons symbols won't show up in the symbol map but might need to be
+/// relocated. We can add them to the symbol table ourselves by combining the
+/// information in the object file (the symbol name) and the main binary (the
+/// address).
+void MachODebugMapParser::addCommonSymbols() {
+ for (auto &CommonSymbol : CommonSymbols) {
+ uint64_t CommonAddr = getMainBinarySymbolAddress(CommonSymbol);
+ if (CommonAddr == 0) {
+ // The main binary doesn't have an address for the given symbol.
+ continue;
+ }
+ if (!CurrentDebugMapObject->addSymbol(CommonSymbol, None /*ObjectAddress*/,
+ CommonAddr, 0 /*size*/)) {
+ // The symbol is already present.
+ continue;
+ }
+ }
+}
+
+/// Create a new DebugMapObject. This function resets the state of the
+/// parser that was referring to the last object file and sets
+/// everything up to add symbols to the new one.
+void MachODebugMapParser::switchToNewDebugMapObject(
+ StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) {
+ addCommonSymbols();
+ resetParserState();
+
+ SmallString<80> Path(PathPrefix);
+ sys::path::append(Path, Filename);
+
+ auto ObjectEntry = BinHolder.getObjectEntry(Path, Timestamp);
+ if (!ObjectEntry) {
+ auto Err = ObjectEntry.takeError();
+ Warning("unable to open object file: " + toString(std::move(Err)),
+ Path.str());
+ return;
+ }
+
+ auto Object = ObjectEntry->getObjectAs<MachOObjectFile>(Result->getTriple());
+ if (!Object) {
+ auto Err = Object.takeError();
+ Warning("unable to open object file: " + toString(std::move(Err)),
+ Path.str());
+ return;
+ }
+
+ CurrentDebugMapObject =
+ &Result->addDebugMapObject(Path, Timestamp, MachO::N_OSO);
+ loadCurrentObjectFileSymbols(*Object);
+}
+
+static std::string getArchName(const object::MachOObjectFile &Obj) {
+ Triple T = Obj.getArchTriple();
+ return std::string(T.getArchName());
+}
+
+std::unique_ptr<DebugMap>
+MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary,
+ StringRef BinaryPath) {
+ loadMainBinarySymbols(MainBinary);
+ ArrayRef<uint8_t> UUID = MainBinary.getUuid();
+ Result =
+ std::make_unique<DebugMap>(MainBinary.getArchTriple(), BinaryPath, UUID);
+ MainBinaryStrings = MainBinary.getStringTableData();
+ for (const SymbolRef &Symbol : MainBinary.symbols()) {
+ const DataRefImpl &DRI = Symbol.getRawDataRefImpl();
+ if (MainBinary.is64Bit())
+ handleStabDebugMapEntry(MainBinary.getSymbol64TableEntry(DRI));
+ else
+ handleStabDebugMapEntry(MainBinary.getSymbolTableEntry(DRI));
+ }
+
+ resetParserState();
+ return std::move(Result);
+}
+
+// Table that maps Darwin's Mach-O stab constants to strings to allow printing.
+// llvm-nm has very similar code, the strings used here are however slightly
+// different and part of the interface of dsymutil (some project's build-systems
+// parse the ouptut of dsymutil -s), thus they shouldn't be changed.
+struct DarwinStabName {
+ uint8_t NType;
+ const char *Name;
+};
+
+const struct DarwinStabName DarwinStabNames[] = {
+ {MachO::N_GSYM, "N_GSYM"}, {MachO::N_FNAME, "N_FNAME"},
+ {MachO::N_FUN, "N_FUN"}, {MachO::N_STSYM, "N_STSYM"},
+ {MachO::N_LCSYM, "N_LCSYM"}, {MachO::N_BNSYM, "N_BNSYM"},
+ {MachO::N_PC, "N_PC"}, {MachO::N_AST, "N_AST"},
+ {MachO::N_OPT, "N_OPT"}, {MachO::N_RSYM, "N_RSYM"},
+ {MachO::N_SLINE, "N_SLINE"}, {MachO::N_ENSYM, "N_ENSYM"},
+ {MachO::N_SSYM, "N_SSYM"}, {MachO::N_SO, "N_SO"},
+ {MachO::N_OSO, "N_OSO"}, {MachO::N_LSYM, "N_LSYM"},
+ {MachO::N_BINCL, "N_BINCL"}, {MachO::N_SOL, "N_SOL"},
+ {MachO::N_PARAMS, "N_PARAM"}, {MachO::N_VERSION, "N_VERS"},
+ {MachO::N_OLEVEL, "N_OLEV"}, {MachO::N_PSYM, "N_PSYM"},
+ {MachO::N_EINCL, "N_EINCL"}, {MachO::N_ENTRY, "N_ENTRY"},
+ {MachO::N_LBRAC, "N_LBRAC"}, {MachO::N_EXCL, "N_EXCL"},
+ {MachO::N_RBRAC, "N_RBRAC"}, {MachO::N_BCOMM, "N_BCOMM"},
+ {MachO::N_ECOMM, "N_ECOMM"}, {MachO::N_ECOML, "N_ECOML"},
+ {MachO::N_LENG, "N_LENG"}, {0, nullptr}};
+
+static const char *getDarwinStabString(uint8_t NType) {
+ for (unsigned i = 0; DarwinStabNames[i].Name; i++) {
+ if (DarwinStabNames[i].NType == NType)
+ return DarwinStabNames[i].Name;
+ }
+ return nullptr;
+}
+
+void MachODebugMapParser::dumpSymTabHeader(raw_ostream &OS, StringRef Arch) {
+ OS << "-----------------------------------"
+ "-----------------------------------\n";
+ OS << "Symbol table for: '" << BinaryPath << "' (" << Arch.data() << ")\n";
+ OS << "-----------------------------------"
+ "-----------------------------------\n";
+ OS << "Index n_strx n_type n_sect n_desc n_value\n";
+ OS << "======== -------- ------------------ ------ ------ ----------------\n";
+}
+
+void MachODebugMapParser::dumpSymTabEntry(raw_ostream &OS, uint64_t Index,
+ uint32_t StringIndex, uint8_t Type,
+ uint8_t SectionIndex, uint16_t Flags,
+ uint64_t Value) {
+ // Index
+ OS << '[' << format_decimal(Index, 6)
+ << "] "
+ // n_strx
+ << format_hex_no_prefix(StringIndex, 8)
+ << ' '
+ // n_type...
+ << format_hex_no_prefix(Type, 2) << " (";
+
+ if (Type & MachO::N_STAB)
+ OS << left_justify(getDarwinStabString(Type), 13);
+ else {
+ if (Type & MachO::N_PEXT)
+ OS << "PEXT ";
+ else
+ OS << " ";
+ switch (Type & MachO::N_TYPE) {
+ case MachO::N_UNDF: // 0x0 undefined, n_sect == NO_SECT
+ OS << "UNDF";
+ break;
+ case MachO::N_ABS: // 0x2 absolute, n_sect == NO_SECT
+ OS << "ABS ";
+ break;
+ case MachO::N_SECT: // 0xe defined in section number n_sect
+ OS << "SECT";
+ break;
+ case MachO::N_PBUD: // 0xc prebound undefined (defined in a dylib)
+ OS << "PBUD";
+ break;
+ case MachO::N_INDR: // 0xa indirect
+ OS << "INDR";
+ break;
+ default:
+ OS << format_hex_no_prefix(Type, 2) << " ";
+ break;
+ }
+ if (Type & MachO::N_EXT)
+ OS << " EXT";
+ else
+ OS << " ";
+ }
+
+ OS << ") "
+ // n_sect
+ << format_hex_no_prefix(SectionIndex, 2)
+ << " "
+ // n_desc
+ << format_hex_no_prefix(Flags, 4)
+ << " "
+ // n_value
+ << format_hex_no_prefix(Value, 16);
+
+ const char *Name = &MainBinaryStrings.data()[StringIndex];
+ if (Name && Name[0])
+ OS << " '" << Name << "'";
+
+ OS << "\n";
+}
+
+void MachODebugMapParser::dumpOneBinaryStab(const MachOObjectFile &MainBinary,
+ StringRef BinaryPath) {
+ loadMainBinarySymbols(MainBinary);
+ MainBinaryStrings = MainBinary.getStringTableData();
+ raw_ostream &OS(llvm::outs());
+
+ dumpSymTabHeader(OS, getArchName(MainBinary));
+ uint64_t Idx = 0;
+ for (const SymbolRef &Symbol : MainBinary.symbols()) {
+ const DataRefImpl &DRI = Symbol.getRawDataRefImpl();
+ if (MainBinary.is64Bit())
+ dumpSymTabEntry(OS, Idx, MainBinary.getSymbol64TableEntry(DRI));
+ else
+ dumpSymTabEntry(OS, Idx, MainBinary.getSymbolTableEntry(DRI));
+ Idx++;
+ }
+
+ OS << "\n\n";
+ resetParserState();
+}
+
+static bool shouldLinkArch(SmallVectorImpl<StringRef> &Archs, StringRef Arch) {
+ if (Archs.empty() || is_contained(Archs, "all") || is_contained(Archs, "*"))
+ return true;
+
+ if (Arch.startswith("arm") && Arch != "arm64" && is_contained(Archs, "arm"))
+ return true;
+
+ SmallString<16> ArchName = Arch;
+ if (Arch.startswith("thumb"))
+ ArchName = ("arm" + Arch.substr(5)).str();
+
+ return is_contained(Archs, ArchName);
+}
+
+bool MachODebugMapParser::dumpStab() {
+ auto ObjectEntry = BinHolder.getObjectEntry(BinaryPath);
+ if (!ObjectEntry) {
+ auto Err = ObjectEntry.takeError();
+ WithColor::error() << "cannot load '" << BinaryPath
+ << "': " << toString(std::move(Err)) << '\n';
+ return false;
+ }
+
+ auto Objects = ObjectEntry->getObjectsAs<MachOObjectFile>();
+ if (!Objects) {
+ auto Err = Objects.takeError();
+ WithColor::error() << "cannot get '" << BinaryPath
+ << "' as MachO file: " << toString(std::move(Err))
+ << "\n";
+ return false;
+ }
+
+ for (const auto *Object : *Objects)
+ if (shouldLinkArch(Archs, Object->getArchTriple().getArchName()))
+ dumpOneBinaryStab(*Object, BinaryPath);
+
+ return true;
+}
+
+/// This main parsing routine tries to open the main binary and if
+/// successful iterates over the STAB entries. The real parsing is
+/// done in handleStabSymbolTableEntry.
+ErrorOr<std::vector<std::unique_ptr<DebugMap>>> MachODebugMapParser::parse() {
+ auto ObjectEntry = BinHolder.getObjectEntry(BinaryPath);
+ if (!ObjectEntry) {
+ return errorToErrorCode(ObjectEntry.takeError());
+ }
+
+ auto Objects = ObjectEntry->getObjectsAs<MachOObjectFile>();
+ if (!Objects) {
+ return errorToErrorCode(Objects.takeError());
+ }
+
+ std::vector<std::unique_ptr<DebugMap>> Results;
+ for (const auto *Object : *Objects)
+ if (shouldLinkArch(Archs, Object->getArchTriple().getArchName()))
+ Results.push_back(parseOneBinary(*Object, BinaryPath));
+
+ return std::move(Results);
+}
+
+/// Interpret the STAB entries to fill the DebugMap.
+void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
+ uint8_t Type,
+ uint8_t SectionIndex,
+ uint16_t Flags,
+ uint64_t Value) {
+ if (!(Type & MachO::N_STAB))
+ return;
+
+ const char *Name = &MainBinaryStrings.data()[StringIndex];
+
+ // An N_OSO entry represents the start of a new object file description.
+ if (Type == MachO::N_OSO)
+ return switchToNewDebugMapObject(Name, sys::toTimePoint(Value));
+
+ if (Type == MachO::N_AST) {
+ SmallString<80> Path(PathPrefix);
+ sys::path::append(Path, Name);
+ Result->addDebugMapObject(Path, sys::toTimePoint(Value), Type);
+ return;
+ }
+
+ // If the last N_OSO object file wasn't found, CurrentDebugMapObject will be
+ // null. Do not update anything until we find the next valid N_OSO entry.
+ if (!CurrentDebugMapObject)
+ return;
+
+ uint32_t Size = 0;
+ switch (Type) {
+ case MachO::N_GSYM:
+ // This is a global variable. We need to query the main binary
+ // symbol table to find its address as it might not be in the
+ // debug map (for common symbols).
+ Value = getMainBinarySymbolAddress(Name);
+ break;
+ case MachO::N_FUN:
+ // Functions are scopes in STABS. They have an end marker that
+ // contains the function size.
+ if (Name[0] == '\0') {
+ Size = Value;
+ Value = CurrentFunctionAddress;
+ Name = CurrentFunctionName;
+ break;
+ } else {
+ CurrentFunctionName = Name;
+ CurrentFunctionAddress = Value;
+ return;
+ }
+ case MachO::N_STSYM:
+ break;
+ default:
+ return;
+ }
+
+ auto ObjectSymIt = CurrentObjectAddresses.find(Name);
+
+ // If the name of a (non-static) symbol is not in the current object, we
+ // check all its aliases from the main binary.
+ if (ObjectSymIt == CurrentObjectAddresses.end() && Type != MachO::N_STSYM) {
+ for (const auto &Alias : getMainBinarySymbolNames(Value)) {
+ ObjectSymIt = CurrentObjectAddresses.find(Alias);
+ if (ObjectSymIt != CurrentObjectAddresses.end())
+ break;
+ }
+ }
+
+ // ThinLTO adds a unique suffix to exported private symbols.
+ if (ObjectSymIt == CurrentObjectAddresses.end()) {
+ for (auto Iter = CurrentObjectAddresses.begin();
+ Iter != CurrentObjectAddresses.end(); ++Iter) {
+ llvm::StringRef SymbolName = Iter->getKey();
+ auto Pos = SymbolName.rfind(".llvm.");
+ if (Pos != llvm::StringRef::npos && SymbolName.substr(0, Pos) == Name) {
+ ObjectSymIt = Iter;
+ break;
+ }
+ }
+ }
+
+ if (ObjectSymIt == CurrentObjectAddresses.end()) {
+ Warning("could not find object file symbol for symbol " + Twine(Name));
+ return;
+ }
+
+ if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value,
+ Size)) {
+ Warning(Twine("failed to insert symbol '") + Name + "' in the debug map.");
+ return;
+ }
+}
+
+/// Load the current object file symbols into CurrentObjectAddresses.
+void MachODebugMapParser::loadCurrentObjectFileSymbols(
+ const object::MachOObjectFile &Obj) {
+ CurrentObjectAddresses.clear();
+
+ for (auto Sym : Obj.symbols()) {
+ uint64_t Addr = cantFail(Sym.getValue());
+ Expected<StringRef> Name = Sym.getName();
+ if (!Name) {
+ // TODO: Actually report errors helpfully.
+ consumeError(Name.takeError());
+ continue;
+ }
+ // The value of some categories of symbols isn't meaningful. For
+ // example common symbols store their size in the value field, not
+ // their address. Absolute symbols have a fixed address that can
+ // conflict with standard symbols. These symbols (especially the
+ // common ones), might still be referenced by relocations. These
+ // relocations will use the symbol itself, and won't need an
+ // object file address. The object file address field is optional
+ // in the DebugMap, leave it unassigned for these symbols.
+ uint32_t Flags = cantFail(Sym.getFlags());
+ if (Flags & SymbolRef::SF_Absolute) {
+ CurrentObjectAddresses[*Name] = None;
+ } else if (Flags & SymbolRef::SF_Common) {
+ CurrentObjectAddresses[*Name] = None;
+ CommonSymbols.push_back(std::string(*Name));
+ } else {
+ CurrentObjectAddresses[*Name] = Addr;
+ }
+ }
+}
+
+/// Lookup a symbol address in the main binary symbol table. The
+/// parser only needs to query common symbols, thus not every symbol's
+/// address is available through this function.
+uint64_t MachODebugMapParser::getMainBinarySymbolAddress(StringRef Name) {
+ auto Sym = MainBinarySymbolAddresses.find(Name);
+ if (Sym == MainBinarySymbolAddresses.end())
+ return 0;
+ return Sym->second;
+}
+
+/// Get all symbol names in the main binary for the given value.
+std::vector<StringRef>
+MachODebugMapParser::getMainBinarySymbolNames(uint64_t Value) {
+ std::vector<StringRef> Names;
+ for (const auto &Entry : MainBinarySymbolAddresses) {
+ if (Entry.second == Value)
+ Names.push_back(Entry.first());
+ }
+ return Names;
+}
+
+/// Load the interesting main binary symbols' addresses into
+/// MainBinarySymbolAddresses.
+void MachODebugMapParser::loadMainBinarySymbols(
+ const MachOObjectFile &MainBinary) {
+ section_iterator Section = MainBinary.section_end();
+ MainBinarySymbolAddresses.clear();
+ for (const auto &Sym : MainBinary.symbols()) {
+ Expected<SymbolRef::Type> TypeOrErr = Sym.getType();
+ if (!TypeOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(TypeOrErr.takeError());
+ continue;
+ }
+ SymbolRef::Type Type = *TypeOrErr;
+ // Skip undefined and STAB entries.
+ if ((Type == SymbolRef::ST_Debug) || (Type == SymbolRef::ST_Unknown))
+ continue;
+ // 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;
+ bool Extern = SymType & (MachO::N_EXT | MachO::N_PEXT);
+ Expected<section_iterator> SectionOrErr = Sym.getSection();
+ if (!SectionOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(SectionOrErr.takeError());
+ continue;
+ }
+ Section = *SectionOrErr;
+ if ((Section == MainBinary.section_end() || Section->isText()) && !Extern)
+ continue;
+ uint64_t Addr = cantFail(Sym.getValue());
+ Expected<StringRef> NameOrErr = Sym.getName();
+ if (!NameOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(NameOrErr.takeError());
+ continue;
+ }
+ StringRef Name = *NameOrErr;
+ if (Name.size() == 0 || Name[0] == '\0')
+ continue;
+ // Override only if the new key is global.
+ if (Extern)
+ MainBinarySymbolAddresses[Name] = Addr;
+ else
+ MainBinarySymbolAddresses.try_emplace(Name, Addr);
+ }
+}
+
+namespace llvm {
+namespace dsymutil {
+llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
+parseDebugMap(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
+ StringRef InputFile, ArrayRef<std::string> Archs,
+ StringRef PrependPath, bool PaperTrailWarnings, bool Verbose,
+ bool InputIsYAML) {
+ if (InputIsYAML)
+ return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose);
+
+ MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath,
+ PaperTrailWarnings, Verbose);
+ return Parser.parse();
+}
+
+bool dumpStab(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
+ StringRef InputFile, ArrayRef<std::string> Archs,
+ StringRef PrependPath) {
+ MachODebugMapParser Parser(VFS, InputFile, Archs, PrependPath, false);
+ return Parser.dumpStab();
+}
+} // namespace dsymutil
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/dsymutil/MachOUtils.cpp b/contrib/libs/llvm14/tools/dsymutil/MachOUtils.cpp
new file mode 100644
index 00000000000..943af430584
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/MachOUtils.cpp
@@ -0,0 +1,624 @@
+//===-- MachOUtils.cpp - Mach-o specific helpers for dsymutil ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachOUtils.h"
+#include "BinaryHolder.h"
+#include "DebugMap.h"
+#include "LinkUtils.h"
+#include "llvm/CodeGen/NonRelocatableStringpool.h"
+#include "llvm/MC/MCAsmLayout.h"
+#include "llvm/MC/MCMachObjectWriter.h"
+#include "llvm/MC/MCObjectStreamer.h"
+#include "llvm/MC/MCSectionMachO.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace dsymutil {
+namespace MachOUtils {
+
+llvm::Error ArchAndFile::createTempFile() {
+ llvm::SmallString<128> TmpModel;
+ llvm::sys::path::system_temp_directory(true, TmpModel);
+ llvm::sys::path::append(TmpModel, "dsym.tmp%%%%%.dwarf");
+ Expected<sys::fs::TempFile> T = sys::fs::TempFile::create(TmpModel);
+
+ if (!T)
+ return T.takeError();
+
+ File = std::make_unique<sys::fs::TempFile>(std::move(*T));
+ return Error::success();
+}
+
+llvm::StringRef ArchAndFile::path() const { return File->TmpName; }
+
+ArchAndFile::~ArchAndFile() {
+ if (File)
+ if (auto E = File->discard())
+ llvm::consumeError(std::move(E));
+}
+
+std::string getArchName(StringRef Arch) {
+ if (Arch.startswith("thumb"))
+ return (llvm::Twine("arm") + Arch.drop_front(5)).str();
+ return std::string(Arch);
+}
+
+static bool runLipo(StringRef SDKPath, SmallVectorImpl<StringRef> &Args) {
+ auto Path = sys::findProgramByName("lipo", makeArrayRef(SDKPath));
+ if (!Path)
+ Path = sys::findProgramByName("lipo");
+
+ if (!Path) {
+ WithColor::error() << "lipo: " << Path.getError().message() << "\n";
+ return false;
+ }
+
+ std::string ErrMsg;
+ int result = sys::ExecuteAndWait(*Path, Args, None, {}, 0, 0, &ErrMsg);
+ if (result) {
+ WithColor::error() << "lipo: " << ErrMsg << "\n";
+ return false;
+ }
+
+ return true;
+}
+
+bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles,
+ StringRef OutputFileName,
+ const LinkOptions &Options, StringRef SDKPath) {
+ // No need to merge one file into a universal fat binary.
+ if (ArchFiles.size() == 1) {
+ if (auto E = ArchFiles.front().File->keep(OutputFileName)) {
+ WithColor::error() << "while keeping " << ArchFiles.front().path()
+ << " as " << OutputFileName << ": "
+ << toString(std::move(E)) << "\n";
+ return false;
+ }
+ return true;
+ }
+
+ SmallVector<StringRef, 8> Args;
+ Args.push_back("lipo");
+ Args.push_back("-create");
+
+ for (auto &Thin : ArchFiles)
+ Args.push_back(Thin.path());
+
+ // Align segments to match dsymutil-classic alignment
+ for (auto &Thin : ArchFiles) {
+ Thin.Arch = getArchName(Thin.Arch);
+ Args.push_back("-segalign");
+ Args.push_back(Thin.Arch);
+ Args.push_back("20");
+ }
+
+ Args.push_back("-output");
+ Args.push_back(OutputFileName.data());
+
+ if (Options.Verbose) {
+ outs() << "Running lipo\n";
+ for (auto Arg : Args)
+ outs() << ' ' << Arg;
+ outs() << "\n";
+ }
+
+ return Options.NoOutput ? true : runLipo(SDKPath, Args);
+}
+
+// Return a MachO::segment_command_64 that holds the same values as the passed
+// MachO::segment_command. We do that to avoid having to duplicate the logic
+// for 32bits and 64bits segments.
+struct MachO::segment_command_64 adaptFrom32bits(MachO::segment_command Seg) {
+ MachO::segment_command_64 Seg64;
+ Seg64.cmd = Seg.cmd;
+ Seg64.cmdsize = Seg.cmdsize;
+ memcpy(Seg64.segname, Seg.segname, sizeof(Seg.segname));
+ Seg64.vmaddr = Seg.vmaddr;
+ Seg64.vmsize = Seg.vmsize;
+ Seg64.fileoff = Seg.fileoff;
+ Seg64.filesize = Seg.filesize;
+ Seg64.maxprot = Seg.maxprot;
+ Seg64.initprot = Seg.initprot;
+ Seg64.nsects = Seg.nsects;
+ Seg64.flags = Seg.flags;
+ return Seg64;
+}
+
+// Iterate on all \a Obj segments, and apply \a Handler to them.
+template <typename FunctionTy>
+static void iterateOnSegments(const object::MachOObjectFile &Obj,
+ FunctionTy Handler) {
+ for (const auto &LCI : Obj.load_commands()) {
+ MachO::segment_command_64 Segment;
+ if (LCI.C.cmd == MachO::LC_SEGMENT)
+ Segment = adaptFrom32bits(Obj.getSegmentLoadCommand(LCI));
+ else if (LCI.C.cmd == MachO::LC_SEGMENT_64)
+ Segment = Obj.getSegment64LoadCommand(LCI);
+ else
+ continue;
+
+ Handler(Segment);
+ }
+}
+
+// Transfer the symbols described by \a NList to \a NewSymtab which is just the
+// raw contents of the symbol table for the dSYM companion file. \returns
+// whether the symbol was transferred or not.
+template <typename NListTy>
+static bool transferSymbol(NListTy NList, bool IsLittleEndian,
+ StringRef Strings, SmallVectorImpl<char> &NewSymtab,
+ NonRelocatableStringpool &NewStrings,
+ bool &InDebugNote) {
+ // Do not transfer undefined symbols, we want real addresses.
+ if ((NList.n_type & MachO::N_TYPE) == MachO::N_UNDF)
+ return false;
+
+ // Do not transfer N_AST symbols as their content is copied into a section of
+ // the Mach-O companion file.
+ if (NList.n_type == MachO::N_AST)
+ return false;
+
+ StringRef Name = StringRef(Strings.begin() + NList.n_strx);
+
+ // An N_SO with a filename opens a debugging scope and another one without a
+ // name closes it. Don't transfer anything in the debugging scope.
+ if (InDebugNote) {
+ InDebugNote =
+ (NList.n_type != MachO::N_SO) || (!Name.empty() && Name[0] != '\0');
+ return false;
+ } else if (NList.n_type == MachO::N_SO) {
+ InDebugNote = true;
+ return false;
+ }
+
+ // FIXME: The + 1 is here to mimic dsymutil-classic that has 2 empty
+ // strings at the start of the generated string table (There is
+ // corresponding code in the string table emission).
+ NList.n_strx = NewStrings.getStringOffset(Name) + 1;
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(NList);
+
+ NewSymtab.append(reinterpret_cast<char *>(&NList),
+ reinterpret_cast<char *>(&NList + 1));
+ return true;
+}
+
+// Wrapper around transferSymbol to transfer all of \a Obj symbols
+// to \a NewSymtab. This function does not write in the output file.
+// \returns the number of symbols in \a NewSymtab.
+static unsigned transferSymbols(const object::MachOObjectFile &Obj,
+ SmallVectorImpl<char> &NewSymtab,
+ NonRelocatableStringpool &NewStrings) {
+ unsigned Syms = 0;
+ StringRef Strings = Obj.getStringTableData();
+ bool IsLittleEndian = Obj.isLittleEndian();
+ bool InDebugNote = false;
+
+ if (Obj.is64Bit()) {
+ for (const object::SymbolRef &Symbol : Obj.symbols()) {
+ object::DataRefImpl DRI = Symbol.getRawDataRefImpl();
+ if (transferSymbol(Obj.getSymbol64TableEntry(DRI), IsLittleEndian,
+ Strings, NewSymtab, NewStrings, InDebugNote))
+ ++Syms;
+ }
+ } else {
+ for (const object::SymbolRef &Symbol : Obj.symbols()) {
+ object::DataRefImpl DRI = Symbol.getRawDataRefImpl();
+ if (transferSymbol(Obj.getSymbolTableEntry(DRI), IsLittleEndian, Strings,
+ NewSymtab, NewStrings, InDebugNote))
+ ++Syms;
+ }
+ }
+ return Syms;
+}
+
+static MachO::section
+getSection(const object::MachOObjectFile &Obj,
+ const MachO::segment_command &Seg,
+ const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) {
+ return Obj.getSection(LCI, Idx);
+}
+
+static MachO::section_64
+getSection(const object::MachOObjectFile &Obj,
+ const MachO::segment_command_64 &Seg,
+ const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) {
+ return Obj.getSection64(LCI, Idx);
+}
+
+// Transfer \a Segment from \a Obj to the output file. This calls into \a Writer
+// to write these load commands directly in the output file at the current
+// position.
+//
+// The function also tries to find a hole in the address map to fit the __DWARF
+// segment of \a DwarfSegmentSize size. \a EndAddress is updated to point at the
+// highest segment address.
+//
+// When the __LINKEDIT segment is transferred, its offset and size are set resp.
+// to \a LinkeditOffset and \a LinkeditSize.
+//
+// When the eh_frame section is transferred, its offset and size are set resp.
+// to \a EHFrameOffset and \a EHFrameSize.
+template <typename SegmentTy>
+static void transferSegmentAndSections(
+ const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment,
+ const object::MachOObjectFile &Obj, MachObjectWriter &Writer,
+ uint64_t LinkeditOffset, uint64_t LinkeditSize, uint64_t EHFrameOffset,
+ uint64_t EHFrameSize, uint64_t DwarfSegmentSize, uint64_t &GapForDwarf,
+ uint64_t &EndAddress) {
+ if (StringRef("__DWARF") == Segment.segname)
+ return;
+
+ if (StringRef("__TEXT") == Segment.segname && EHFrameSize > 0) {
+ Segment.fileoff = EHFrameOffset;
+ Segment.filesize = EHFrameSize;
+ } else if (StringRef("__LINKEDIT") == Segment.segname) {
+ Segment.fileoff = LinkeditOffset;
+ Segment.filesize = LinkeditSize;
+ // Resize vmsize by rounding to the page size.
+ Segment.vmsize = alignTo(LinkeditSize, 0x1000);
+ } else {
+ Segment.fileoff = Segment.filesize = 0;
+ }
+
+ // Check if the end address of the last segment and our current
+ // start address leave a sufficient gap to store the __DWARF
+ // segment.
+ uint64_t PrevEndAddress = EndAddress;
+ EndAddress = alignTo(EndAddress, 0x1000);
+ if (GapForDwarf == UINT64_MAX && Segment.vmaddr > EndAddress &&
+ Segment.vmaddr - EndAddress >= DwarfSegmentSize)
+ GapForDwarf = EndAddress;
+
+ // The segments are not necessarily sorted by their vmaddr.
+ EndAddress =
+ std::max<uint64_t>(PrevEndAddress, Segment.vmaddr + Segment.vmsize);
+ unsigned nsects = Segment.nsects;
+ if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
+ MachO::swapStruct(Segment);
+ Writer.W.OS.write(reinterpret_cast<char *>(&Segment), sizeof(Segment));
+ for (unsigned i = 0; i < nsects; ++i) {
+ auto Sect = getSection(Obj, Segment, LCI, i);
+ if (StringRef("__eh_frame") == Sect.sectname) {
+ Sect.offset = EHFrameOffset;
+ Sect.reloff = Sect.nreloc = 0;
+ } else {
+ Sect.offset = Sect.reloff = Sect.nreloc = 0;
+ }
+ if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
+ MachO::swapStruct(Sect);
+ Writer.W.OS.write(reinterpret_cast<char *>(&Sect), sizeof(Sect));
+ }
+}
+
+// Write the __DWARF segment load command to the output file.
+static void createDwarfSegment(uint64_t VMAddr, uint64_t FileOffset,
+ uint64_t FileSize, unsigned NumSections,
+ MCAsmLayout &Layout, MachObjectWriter &Writer) {
+ Writer.writeSegmentLoadCommand("__DWARF", NumSections, VMAddr,
+ alignTo(FileSize, 0x1000), FileOffset,
+ FileSize, /* MaxProt */ 7,
+ /* InitProt =*/3);
+
+ for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) {
+ MCSection *Sec = Layout.getSectionOrder()[i];
+ if (Sec->begin() == Sec->end() || !Layout.getSectionFileSize(Sec))
+ continue;
+
+ unsigned Align = Sec->getAlignment();
+ if (Align > 1) {
+ VMAddr = alignTo(VMAddr, Align);
+ FileOffset = alignTo(FileOffset, Align);
+ }
+ Writer.writeSection(Layout, *Sec, VMAddr, FileOffset, 0, 0, 0);
+
+ FileOffset += Layout.getSectionAddressSize(Sec);
+ VMAddr += Layout.getSectionAddressSize(Sec);
+ }
+}
+
+static bool isExecutable(const object::MachOObjectFile &Obj) {
+ if (Obj.is64Bit())
+ return Obj.getHeader64().filetype != MachO::MH_OBJECT;
+ else
+ return Obj.getHeader().filetype != MachO::MH_OBJECT;
+}
+
+static bool hasLinkEditSegment(const object::MachOObjectFile &Obj) {
+ bool HasLinkEditSegment = false;
+ iterateOnSegments(Obj, [&](const MachO::segment_command_64 &Segment) {
+ if (StringRef("__LINKEDIT") == Segment.segname)
+ HasLinkEditSegment = true;
+ });
+ return HasLinkEditSegment;
+}
+
+static unsigned segmentLoadCommandSize(bool Is64Bit, unsigned NumSections) {
+ if (Is64Bit)
+ return sizeof(MachO::segment_command_64) +
+ NumSections * sizeof(MachO::section_64);
+
+ return sizeof(MachO::segment_command) + NumSections * sizeof(MachO::section);
+}
+
+// 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(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
+ 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());
+
+ // Layout but don't emit.
+ ObjectStreamer.flushPendingLabels();
+ MCAsmLayout Layout(MCAsm);
+ MCAsm.layout(Layout);
+
+ BinaryHolder InputBinaryHolder(VFS, false);
+
+ auto ObjectEntry = InputBinaryHolder.getObjectEntry(DM.getBinaryPath());
+ if (!ObjectEntry) {
+ auto Err = ObjectEntry.takeError();
+ return error(Twine("opening ") + DM.getBinaryPath() + ": " +
+ toString(std::move(Err)),
+ "output file streaming");
+ }
+
+ auto Object =
+ ObjectEntry->getObjectAs<object::MachOObjectFile>(DM.getTriple());
+ if (!Object) {
+ auto Err = Object.takeError();
+ return error(Twine("opening ") + DM.getBinaryPath() + ": " +
+ toString(std::move(Err)),
+ "output file streaming");
+ }
+
+ auto &InputBinary = *Object;
+
+ bool Is64Bit = Writer.is64Bit();
+ MachO::symtab_command SymtabCmd = InputBinary.getSymtabLoadCommand();
+
+ // 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));
+ for (auto &LCI : InputBinary.load_commands()) {
+ 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;
+ }
+ }
+
+ // If we have a valid symtab to copy, do it.
+ bool ShouldEmitSymtab =
+ isExecutable(InputBinary) && hasLinkEditSegment(InputBinary);
+ if (ShouldEmitSymtab) {
+ LoadCommandSize += sizeof(MachO::symtab_command);
+ ++NumLoadCommands;
+ }
+
+ // If we have a valid eh_frame to copy, do it.
+ uint64_t EHFrameSize = 0;
+ StringRef EHFrameData;
+ for (const object::SectionRef &Section : InputBinary.sections()) {
+ Expected<StringRef> NameOrErr = Section.getName();
+ if (!NameOrErr) {
+ consumeError(NameOrErr.takeError());
+ continue;
+ }
+ StringRef SectionName = *NameOrErr;
+ SectionName = SectionName.substr(SectionName.find_first_not_of("._"));
+ if (SectionName == "eh_frame") {
+ if (Expected<StringRef> ContentsOrErr = Section.getContents()) {
+ EHFrameData = *ContentsOrErr;
+ EHFrameSize = Section.getSize();
+ } else {
+ consumeError(ContentsOrErr.takeError());
+ }
+ }
+ }
+
+ unsigned HeaderSize =
+ Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
+ // We will copy every segment that isn't __DWARF.
+ iterateOnSegments(InputBinary, [&](const MachO::segment_command_64 &Segment) {
+ if (StringRef("__DWARF") == Segment.segname)
+ return;
+
+ ++NumLoadCommands;
+ LoadCommandSize += segmentLoadCommandSize(Is64Bit, Segment.nsects);
+ });
+
+ // We will add our own brand new __DWARF segment if we have debug
+ // info.
+ unsigned NumDwarfSections = 0;
+ uint64_t DwarfSegmentSize = 0;
+
+ for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) {
+ MCSection *Sec = Layout.getSectionOrder()[i];
+ if (Sec->begin() == Sec->end())
+ continue;
+
+ if (uint64_t Size = Layout.getSectionFileSize(Sec)) {
+ DwarfSegmentSize = alignTo(DwarfSegmentSize, Sec->getAlignment());
+ DwarfSegmentSize += Size;
+ ++NumDwarfSections;
+ }
+ }
+
+ if (NumDwarfSections) {
+ ++NumLoadCommands;
+ LoadCommandSize += segmentLoadCommandSize(Is64Bit, NumDwarfSections);
+ }
+
+ SmallString<0> NewSymtab;
+ std::function<StringRef(StringRef)> TranslationLambda =
+ Translator ? [&](StringRef Input) { return Translator(Input); }
+ : static_cast<std::function<StringRef(StringRef)>>(nullptr);
+ // Legacy dsymutil puts an empty string at the start of the line table.
+ // thus we set NonRelocatableStringpool(,PutEmptyString=true)
+ NonRelocatableStringpool NewStrings(TranslationLambda, true);
+ unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);
+ unsigned NumSyms = 0;
+ uint64_t NewStringsSize = 0;
+ if (ShouldEmitSymtab) {
+ NewSymtab.reserve(SymtabCmd.nsyms * NListSize / 2);
+ NumSyms = transferSymbols(InputBinary, NewSymtab, NewStrings);
+ NewStringsSize = NewStrings.getSize() + 1;
+ }
+
+ uint64_t SymtabStart = LoadCommandSize;
+ SymtabStart += HeaderSize;
+ SymtabStart = alignTo(SymtabStart, 0x1000);
+
+ // We gathered all the information we need, start emitting the output file.
+ Writer.writeHeader(MachO::MH_DSYM, NumLoadCommands, LoadCommandSize, false);
+
+ // Write the load commands.
+ assert(OutFile.tell() == HeaderSize);
+ if (UUIDCmd.cmd != 0) {
+ Writer.W.write<uint32_t>(UUIDCmd.cmd);
+ 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;
+ if (ShouldEmitSymtab)
+ Writer.writeSymtabLoadCommand(SymtabStart, NumSyms, StringStart,
+ NewStringsSize);
+
+ uint64_t EHFrameStart = StringStart + NewStringsSize;
+ EHFrameStart = alignTo(EHFrameStart, 0x1000);
+
+ uint64_t DwarfSegmentStart = EHFrameStart + EHFrameSize;
+ DwarfSegmentStart = alignTo(DwarfSegmentStart, 0x1000);
+
+ // Write the load commands for the segments and sections we 'import' from
+ // the original binary.
+ uint64_t EndAddress = 0;
+ uint64_t GapForDwarf = UINT64_MAX;
+ for (auto &LCI : InputBinary.load_commands()) {
+ if (LCI.C.cmd == MachO::LC_SEGMENT)
+ transferSegmentAndSections(
+ LCI, InputBinary.getSegmentLoadCommand(LCI), InputBinary, Writer,
+ SymtabStart, StringStart + NewStringsSize - SymtabStart, EHFrameStart,
+ EHFrameSize, DwarfSegmentSize, GapForDwarf, EndAddress);
+ else if (LCI.C.cmd == MachO::LC_SEGMENT_64)
+ transferSegmentAndSections(
+ LCI, InputBinary.getSegment64LoadCommand(LCI), InputBinary, Writer,
+ SymtabStart, StringStart + NewStringsSize - SymtabStart, EHFrameStart,
+ EHFrameSize, DwarfSegmentSize, GapForDwarf, EndAddress);
+ }
+
+ uint64_t DwarfVMAddr = alignTo(EndAddress, 0x1000);
+ uint64_t DwarfVMMax = Is64Bit ? UINT64_MAX : UINT32_MAX;
+ if (DwarfVMAddr + DwarfSegmentSize > DwarfVMMax ||
+ DwarfVMAddr + DwarfSegmentSize < DwarfVMAddr /* Overflow */) {
+ // There is no room for the __DWARF segment at the end of the
+ // address space. Look through segments to find a gap.
+ DwarfVMAddr = GapForDwarf;
+ if (DwarfVMAddr == UINT64_MAX)
+ warn("not enough VM space for the __DWARF segment.",
+ "output file streaming");
+ }
+
+ // Write the load command for the __DWARF segment.
+ createDwarfSegment(DwarfVMAddr, DwarfSegmentStart, DwarfSegmentSize,
+ NumDwarfSections, Layout, Writer);
+
+ assert(OutFile.tell() == LoadCommandSize + HeaderSize);
+ OutFile.write_zeros(SymtabStart - (LoadCommandSize + HeaderSize));
+ assert(OutFile.tell() == SymtabStart);
+
+ // Transfer symbols.
+ if (ShouldEmitSymtab) {
+ OutFile << NewSymtab.str();
+ assert(OutFile.tell() == StringStart);
+
+ // Transfer string table.
+ // FIXME: The NonRelocatableStringpool starts with an empty string, but
+ // dsymutil-classic starts the reconstructed string table with 2 of these.
+ // Reproduce that behavior for now (there is corresponding code in
+ // transferSymbol).
+ OutFile << '\0';
+ std::vector<DwarfStringPoolEntryRef> Strings =
+ NewStrings.getEntriesForEmission();
+ for (auto EntryRef : Strings) {
+ OutFile.write(EntryRef.getString().data(),
+ EntryRef.getString().size() + 1);
+ }
+ }
+ assert(OutFile.tell() == StringStart + NewStringsSize);
+
+ // Pad till the EH frame start.
+ OutFile.write_zeros(EHFrameStart - (StringStart + NewStringsSize));
+ assert(OutFile.tell() == EHFrameStart);
+
+ // Transfer eh_frame.
+ if (EHFrameSize > 0)
+ OutFile << EHFrameData;
+ assert(OutFile.tell() == EHFrameStart + EHFrameSize);
+
+ // Pad till the Dwarf segment start.
+ OutFile.write_zeros(DwarfSegmentStart - (EHFrameStart + EHFrameSize));
+ assert(OutFile.tell() == DwarfSegmentStart);
+
+ // Emit the Dwarf sections contents.
+ for (const MCSection &Sec : MCAsm) {
+ if (Sec.begin() == Sec.end())
+ continue;
+
+ uint64_t Pos = OutFile.tell();
+ OutFile.write_zeros(alignTo(Pos, Sec.getAlignment()) - Pos);
+ MCAsm.writeSectionData(OutFile, &Sec, Layout);
+ }
+
+ return true;
+}
+} // namespace MachOUtils
+} // namespace dsymutil
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/dsymutil/MachOUtils.h b/contrib/libs/llvm14/tools/dsymutil/MachOUtils.h
new file mode 100644
index 00000000000..b1cdd44d38e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/MachOUtils.h
@@ -0,0 +1,52 @@
+//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#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 "llvm/Support/VirtualFileSystem.h"
+
+#include <string>
+
+namespace llvm {
+class MCStreamer;
+class raw_fd_ostream;
+namespace dsymutil {
+class DebugMap;
+struct LinkOptions;
+namespace MachOUtils {
+
+struct ArchAndFile {
+ std::string Arch;
+ std::unique_ptr<llvm::sys::fs::TempFile> File;
+
+ llvm::Error createTempFile();
+ llvm::StringRef path() const;
+
+ ArchAndFile(StringRef Arch) : Arch(std::string(Arch)) {}
+ ArchAndFile(ArchAndFile &&A) = default;
+ ArchAndFile &operator=(ArchAndFile &&A) = default;
+ ~ArchAndFile();
+};
+
+bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles,
+ StringRef OutputFileName, const LinkOptions &,
+ StringRef SDKPath);
+
+bool generateDsymCompanion(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
+ const DebugMap &DM, SymbolMapTranslator &Translator,
+ MCStreamer &MS, raw_fd_ostream &OutFile);
+
+std::string getArchName(StringRef Arch);
+} // namespace MachOUtils
+} // namespace dsymutil
+} // namespace llvm
+#endif // LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H
diff --git a/contrib/libs/llvm14/tools/dsymutil/Options.td b/contrib/libs/llvm14/tools/dsymutil/Options.td
new file mode 100644
index 00000000000..ff11298f4ae
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/Options.td
@@ -0,0 +1,178 @@
+include "llvm/Option/OptParser.td"
+
+class F<string name>: Flag<["--", "-"], name>;
+
+def grp_general : OptionGroup<"Dsymutil">, HelpText<"Dsymutil Options">;
+
+def help: F<"help">,
+ HelpText<"Prints this help output.">,
+ Group<grp_general>;
+def: Flag<["-"], "h">,
+ Alias<help>,
+ HelpText<"Alias for --help">,
+ Group<grp_general>;
+
+def version: F<"version">,
+ HelpText<"Prints the dsymutil version.">,
+ Group<grp_general>;
+def: Flag<["-"], "v">,
+ Alias<version>,
+ HelpText<"Alias for --version">,
+ Group<grp_general>;
+
+def verbose: F<"verbose">,
+ HelpText<"Enable verbose mode.">,
+ Group<grp_general>;
+
+def keep_func_for_static: F<"keep-function-for-static">,
+ HelpText<"Make a static variable keep the enclosing function even if it would have been omitted otherwise.">,
+ Group<grp_general>;
+
+def statistics: F<"statistics">,
+ HelpText<"Print statistics about the contribution of each object file to "
+ "the linked debug info. This prints a table after linking with the "
+ "object file name, the size of the debug info in the object file "
+ "(in bytes) and the size contributed (in bytes) to the linked dSYM. "
+ "The table is sorted by the output size listing the object files "
+ "with the largest contribution first.">,
+ Group<grp_general>;
+
+def verify: F<"verify">,
+ HelpText<"Run the DWARF verifier on the linked DWARF debug info.">,
+ Group<grp_general>;
+
+def no_output: F<"no-output">,
+ HelpText<"Do the link in memory, but do not emit the result file.">,
+ Group<grp_general>;
+
+def no_swiftmodule_timestamp: F<"no-swiftmodule-timestamp">,
+ HelpText<"Don't check timestamp for swiftmodule files.">,
+ Group<grp_general>;
+
+def no_odr: F<"no-odr">,
+ HelpText<"Do not use ODR (One Definition Rule) for type uniquing.">,
+ Group<grp_general>;
+
+def dump_debug_map: F<"dump-debug-map">,
+ HelpText<"Parse and dump the debug map to standard output. No DWARF link will take place.">,
+ Group<grp_general>;
+
+def yaml_input: Flag<["-", "--"], "y">,
+ HelpText<"Treat the input file is a YAML debug map rather than a binary.">,
+ Group<grp_general>;
+
+def papertrail: F<"papertrail">,
+ HelpText<"Embed warnings in the linked DWARF debug info.">,
+ Group<grp_general>;
+
+def assembly: Flag<["-", "--"], "S">,
+ HelpText<"Output textual assembly instead of a binary dSYM companion file.">,
+ Group<grp_general>;
+
+def symtab: F<"symtab">,
+ HelpText<"Dumps the symbol table found in executable or object file(s) and exits.">,
+ Group<grp_general>;
+def: Flag<["-"], "s">,
+ Alias<symtab>,
+ HelpText<"Alias for --symtab">,
+ Group<grp_general>;
+
+def flat: F<"flat">,
+ HelpText<"Produce a flat dSYM file (not a bundle).">,
+ Group<grp_general>;
+def: Flag<["-"], "f">,
+ Alias<flat>,
+ HelpText<"Alias for --flat">,
+ Group<grp_general>;
+
+def update: F<"update">,
+ HelpText<"Updates existing dSYM files to contain the latest accelerator tables and other DWARF optimizations.">,
+ Group<grp_general>;
+def: Flag<["-"], "u">,
+ Alias<update>,
+ HelpText<"Alias for --update">,
+ Group<grp_general>;
+
+def output: Separate<["-", "--"], "o">,
+ MetaVarName<"<filename>">,
+ HelpText<"Specify the output file. Defaults to <input file>.dwarf">,
+ Group<grp_general>;
+def: Separate<["--", "-"], "out">,
+ MetaVarName<"<filename>">,
+ Alias<output>,
+ HelpText<"Alias for -o">,
+ Group<grp_general>;
+def: Joined<["--", "-"], "out=">, Alias<output>;
+def: Joined<["-", "--"], "o=">, Alias<output>;
+
+def oso_prepend_path: Separate<["--", "-"], "oso-prepend-path">,
+ MetaVarName<"<path>">,
+ HelpText<"Specify a directory to prepend to the paths of object files.">,
+ Group<grp_general>;
+def: Joined<["--", "-"], "oso-prepend-path=">, Alias<oso_prepend_path>;
+
+def object_prefix_map: Separate<["--", "-"], "object-prefix-map">,
+ MetaVarName<"<prefix=remapped>">,
+ HelpText<"Remap object file paths (but no source paths) before processing."
+ "Use this for Clang objects where the module cache location was"
+ "remapped using -fdebug-prefix-map; to help dsymutil"
+ "find the Clang module cache.">,
+ Group<grp_general>;
+def: Joined<["--", "-"], "object-prefix-map=">, Alias<object_prefix_map>;
+
+def symbolmap: Separate<["--", "-"], "symbol-map">,
+ MetaVarName<"<bcsymbolmap>">,
+ HelpText<"Updates the existing dSYMs inplace using symbol map specified.">,
+ Group<grp_general>;
+def: Joined<["--", "-"], "symbol-map=">, Alias<symbolmap>;
+
+def arch: Separate<["--", "-"], "arch">,
+ MetaVarName<"<arch>">,
+ HelpText<"Link DWARF debug information only for specified CPU architecture"
+ "types. This option can be specified multiple times, once for each"
+ "desired architecture. All CPU architectures will be linked by"
+ "default.">,
+ Group<grp_general>;
+def: Joined<["--", "-"], "arch=">, Alias<arch>;
+
+def accelerator: Separate<["--", "-"], "accelerator">,
+ MetaVarName<"<accelerator type>">,
+ HelpText<"Specify the desired type of accelerator table. Valid options are 'Apple' (.apple_names, .apple_namespaces, .apple_types, .apple_objc), 'Dwarf' (.debug_names), 'Pub' (.debug_pubnames, .debug_pubtypes) and 'Default'">,
+ Group<grp_general>;
+def: Joined<["--", "-"], "accelerator=">, Alias<accelerator>;
+
+def toolchain: Separate<["--", "-"], "toolchain">,
+ MetaVarName<"<toolchain>">,
+ HelpText<"Embed toolchain information in dSYM bundle.">,
+ Group<grp_general>;
+
+def threads: Separate<["--", "-"], "num-threads">,
+ MetaVarName<"<threads>">,
+ HelpText<"Specifies the maximum number of simultaneous threads to use when linking multiple architectures.">,
+ Group<grp_general>;
+def: Separate<["-"], "j">,
+ MetaVarName<"<threads>">,
+ HelpText<"Alias for --num-threads">,
+ Group<grp_general>;
+
+def gen_reproducer: F<"gen-reproducer">,
+ HelpText<"Generate a reproducer consisting of the input object files.">,
+ Group<grp_general>;
+
+def use_reproducer: Separate<["--", "-"], "use-reproducer">,
+ MetaVarName<"<path>">,
+ HelpText<"Use the object files from the given reproducer path.">,
+ Group<grp_general>;
+def: Joined<["--", "-"], "use-reproducer=">, Alias<use_reproducer>;
+
+def remarks_prepend_path: Separate<["--", "-"], "remarks-prepend-path">,
+ MetaVarName<"<path>">,
+ HelpText<"Specify a directory to prepend to the paths of the external remark files.">,
+ Group<grp_general>;
+def: Joined<["--", "-"], "remarks-prepend-path=">, Alias<remarks_prepend_path>;
+
+def remarks_output_format: Separate<["--", "-"], "remarks-output-format">,
+ MetaVarName<"<format>">,
+ HelpText<"Specify the format to be used when serializing the linked remarks.">,
+ Group<grp_general>;
+def: Joined<["--", "-"], "remarks-output-format=">, Alias<remarks_output_format>;
diff --git a/contrib/libs/llvm14/tools/dsymutil/Reproducer.cpp b/contrib/libs/llvm14/tools/dsymutil/Reproducer.cpp
new file mode 100644
index 00000000000..4f2e0db297e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/Reproducer.cpp
@@ -0,0 +1,85 @@
+//===- Reproducer.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Reproducer.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace llvm::dsymutil;
+
+static std::string createReproducerDir(std::error_code &EC) {
+ SmallString<128> Root;
+ if (const char *Path = getenv("DSYMUTIL_REPRODUCER_PATH")) {
+ Root.assign(Path);
+ EC = sys::fs::create_directory(Root);
+ } else {
+ EC = sys::fs::createUniqueDirectory("dsymutil", Root);
+ }
+ return EC ? "" : std::string(Root);
+}
+
+Reproducer::Reproducer() : VFS(vfs::getRealFileSystem()) {}
+Reproducer::~Reproducer() = default;
+
+ReproducerGenerate::ReproducerGenerate(std::error_code &EC)
+ : Root(createReproducerDir(EC)) {
+ if (!Root.empty())
+ FC = std::make_shared<FileCollector>(Root, Root);
+ VFS = FileCollector::createCollectorVFS(vfs::getRealFileSystem(), FC);
+}
+
+ReproducerGenerate::~ReproducerGenerate() {
+ if (!FC)
+ return;
+ FC->copyFiles(false);
+ SmallString<128> Mapping(Root);
+ sys::path::append(Mapping, "mapping.yaml");
+ FC->writeMapping(Mapping.str());
+ outs() << "reproducer written to " << Root << '\n';
+}
+
+ReproducerUse::~ReproducerUse() = default;
+
+ReproducerUse::ReproducerUse(StringRef Root, std::error_code &EC) {
+ SmallString<128> Mapping(Root);
+ sys::path::append(Mapping, "mapping.yaml");
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
+ vfs::getRealFileSystem()->getBufferForFile(Mapping.str());
+
+ if (!Buffer) {
+ EC = Buffer.getError();
+ return;
+ }
+
+ VFS = llvm::vfs::getVFSFromYAML(std::move(Buffer.get()), nullptr, Mapping);
+}
+
+llvm::Expected<std::unique_ptr<Reproducer>>
+Reproducer::createReproducer(ReproducerMode Mode, StringRef Root) {
+ switch (Mode) {
+ case ReproducerMode::Generate: {
+ std::error_code EC;
+ std::unique_ptr<Reproducer> Repro =
+ std::make_unique<ReproducerGenerate>(EC);
+ if (EC)
+ return errorCodeToError(EC);
+ return std::move(Repro);
+ }
+ case ReproducerMode::Use: {
+ std::error_code EC;
+ std::unique_ptr<Reproducer> Repro =
+ std::make_unique<ReproducerUse>(Root, EC);
+ if (EC)
+ return errorCodeToError(EC);
+ return std::move(Repro);
+ }
+ case ReproducerMode::Off:
+ return std::make_unique<Reproducer>();
+ }
+ llvm_unreachable("All cases handled above.");
+}
diff --git a/contrib/libs/llvm14/tools/dsymutil/Reproducer.h b/contrib/libs/llvm14/tools/dsymutil/Reproducer.h
new file mode 100644
index 00000000000..e965e1ceda2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/Reproducer.h
@@ -0,0 +1,77 @@
+//===- tools/dsymutil/Reproducer.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_DSYMUTIL_REPRODUCER_H
+#define LLVM_TOOLS_DSYMUTIL_REPRODUCER_H
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileCollector.h"
+#include "llvm/Support/VirtualFileSystem.h"
+
+namespace llvm {
+namespace dsymutil {
+
+/// The reproducer mode.
+enum class ReproducerMode {
+ Generate,
+ Use,
+ Off,
+};
+
+/// The reproducer class manages the sate related to reproducers in dsymutil.
+/// Instances should be created with Reproducer::createReproducer. An instance
+/// of this class is returned when reproducers are off. The VFS returned by
+/// this instance is the real file system.
+class Reproducer {
+public:
+ Reproducer();
+ virtual ~Reproducer();
+
+ IntrusiveRefCntPtr<vfs::FileSystem> getVFS() const { return VFS; }
+
+ /// Create a Reproducer instance based on the given mode.
+ static llvm::Expected<std::unique_ptr<Reproducer>>
+ createReproducer(ReproducerMode Mode, StringRef Root);
+
+protected:
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS;
+};
+
+/// Reproducer instance used to generate a new reproducer. The VFS returned by
+/// this instance is a FileCollectorFileSystem that tracks every file used by
+/// dsymutil.
+class ReproducerGenerate : public Reproducer {
+public:
+ ReproducerGenerate(std::error_code &EC);
+ ~ReproducerGenerate() override;
+
+private:
+ /// The path to the reproducer.
+ std::string Root;
+
+ /// The FileCollector used by the FileCollectorFileSystem.
+ std::shared_ptr<FileCollector> FC;
+};
+
+/// Reproducer instance used to use an existing reproducer. The VFS returned by
+/// this instance is a RedirectingFileSystem that remaps paths to their
+/// counterpart in the reproducer.
+class ReproducerUse : public Reproducer {
+public:
+ ReproducerUse(StringRef Root, std::error_code &EC);
+ ~ReproducerUse() override;
+
+private:
+ /// The path to the reproducer.
+ std::string Root;
+};
+
+} // end namespace dsymutil
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_DSYMUTIL_REPRODUCER_H
diff --git a/contrib/libs/llvm14/tools/dsymutil/SymbolMap.cpp b/contrib/libs/llvm14/tools/dsymutil/SymbolMap.cpp
new file mode 100644
index 00000000000..07a54795a84
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/SymbolMap.cpp
@@ -0,0 +1,161 @@
+//===- tools/dsymutil/SymbolMap.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#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
+ // `MangleContext::mangleObjCMethodName` 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 = std::string(BCSymbolMapPath);
+ }
+ 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/contrib/libs/llvm14/tools/dsymutil/SymbolMap.h b/contrib/libs/llvm14/tools/dsymutil/SymbolMap.h
new file mode 100644
index 00000000000..977de31a5a1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/SymbolMap.h
@@ -0,0 +1,53 @@
+//=- tools/dsymutil/SymbolMap.h -----------------------------------*- C++ -*-=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#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/contrib/libs/llvm14/tools/dsymutil/dsymutil.cpp b/contrib/libs/llvm14/tools/dsymutil/dsymutil.cpp
new file mode 100644
index 00000000000..2a8e317bef6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/dsymutil.cpp
@@ -0,0 +1,708 @@
+//===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is a utility that aims to be a dropin replacement for Darwin's
+// dsymutil.
+//===----------------------------------------------------------------------===//
+
+#include "dsymutil.h"
+#include "BinaryHolder.h"
+#include "CFBundle.h"
+#include "DebugMap.h"
+#include "LinkUtils.h"
+#include "MachOUtils.h"
+#include "Reproducer.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileCollector.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/thread.h"
+#include <algorithm>
+#include <cstdint>
+#include <cstdlib>
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+using namespace llvm::dsymutil;
+using namespace object;
+
+namespace {
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Options.inc"
+#undef OPTION
+};
+
+class DsymutilOptTable : public opt::OptTable {
+public:
+ DsymutilOptTable() : OptTable(InfoTable) {}
+};
+} // namespace
+
+struct DsymutilOptions {
+ bool DumpDebugMap = false;
+ bool DumpStab = false;
+ bool Flat = false;
+ bool InputIsYAMLDebugMap = false;
+ bool PaperTrailWarnings = false;
+ bool Verify = false;
+ bool ForceKeepFunctionForStatic = false;
+ std::string SymbolMap;
+ std::string OutputFile;
+ std::string Toolchain;
+ std::string ReproducerPath;
+ std::vector<std::string> Archs;
+ std::vector<std::string> InputFiles;
+ unsigned NumThreads;
+ ReproducerMode ReproMode = ReproducerMode::Off;
+ dsymutil::LinkOptions LinkOpts;
+};
+
+/// Return a list of input files. This function has logic for dealing with the
+/// special case where we might have dSYM bundles as input. The function
+/// returns an error when the directory structure doesn't match that of a dSYM
+/// bundle.
+static Expected<std::vector<std::string>> getInputs(opt::InputArgList &Args,
+ bool DsymAsInput) {
+ std::vector<std::string> InputFiles;
+ for (auto *File : Args.filtered(OPT_INPUT))
+ InputFiles.push_back(File->getValue());
+
+ if (!DsymAsInput)
+ return InputFiles;
+
+ // If we are updating, we might get dSYM bundles as input.
+ std::vector<std::string> Inputs;
+ for (const auto &Input : InputFiles) {
+ if (!sys::fs::is_directory(Input)) {
+ Inputs.push_back(Input);
+ continue;
+ }
+
+ // Make sure that we're dealing with a dSYM bundle.
+ SmallString<256> BundlePath(Input);
+ sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
+ if (!sys::fs::is_directory(BundlePath))
+ return make_error<StringError>(
+ Input + " is a directory, but doesn't look like a dSYM bundle.",
+ inconvertibleErrorCode());
+
+ // Create a directory iterator to iterate over all the entries in the
+ // bundle.
+ std::error_code EC;
+ sys::fs::directory_iterator DirIt(BundlePath, EC);
+ sys::fs::directory_iterator DirEnd;
+ if (EC)
+ return errorCodeToError(EC);
+
+ // Add each entry to the list of inputs.
+ while (DirIt != DirEnd) {
+ Inputs.push_back(DirIt->path());
+ DirIt.increment(EC);
+ if (EC)
+ return errorCodeToError(EC);
+ }
+ }
+ return Inputs;
+}
+
+// Verify that the given combination of options makes sense.
+static Error verifyOptions(const DsymutilOptions &Options) {
+ if (Options.InputFiles.empty()) {
+ return make_error<StringError>("no input files specified",
+ errc::invalid_argument);
+ }
+
+ if (Options.LinkOpts.Update && llvm::is_contained(Options.InputFiles, "-")) {
+ // FIXME: We cannot use stdin for an update because stdin will be
+ // consumed by the BinaryHolder during the debugmap parsing, and
+ // then we will want to consume it again in DwarfLinker. If we
+ // used a unique BinaryHolder object that could cache multiple
+ // binaries this restriction would go away.
+ return make_error<StringError>(
+ "standard input cannot be used as input for a dSYM update.",
+ errc::invalid_argument);
+ }
+
+ if (!Options.Flat && Options.OutputFile == "-")
+ return make_error<StringError>(
+ "cannot emit to standard output without --flat.",
+ errc::invalid_argument);
+
+ if (Options.InputFiles.size() > 1 && Options.Flat &&
+ !Options.OutputFile.empty())
+ return make_error<StringError>(
+ "cannot use -o with multiple inputs in flat mode.",
+ errc::invalid_argument);
+
+ if (Options.PaperTrailWarnings && Options.InputIsYAMLDebugMap)
+ return make_error<StringError>(
+ "paper trail warnings are not supported for YAML input.",
+ errc::invalid_argument);
+
+ if (!Options.ReproducerPath.empty() &&
+ Options.ReproMode != ReproducerMode::Use)
+ return make_error<StringError>(
+ "cannot combine --gen-reproducer and --use-reproducer.",
+ errc::invalid_argument);
+
+ return Error::success();
+}
+
+static Expected<AccelTableKind> getAccelTableKind(opt::InputArgList &Args) {
+ if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) {
+ StringRef S = Accelerator->getValue();
+ if (S == "Apple")
+ return AccelTableKind::Apple;
+ if (S == "Dwarf")
+ return AccelTableKind::Dwarf;
+ if (S == "Pub")
+ return AccelTableKind::Pub;
+ if (S == "Default")
+ return AccelTableKind::Default;
+ return make_error<StringError>(
+ "invalid accelerator type specified: '" + S +
+ "'. Support values are 'Apple', 'Dwarf', 'Pub' and 'Default'.",
+ inconvertibleErrorCode());
+ }
+ return AccelTableKind::Default;
+}
+
+/// Parses the command line options into the LinkOptions struct and performs
+/// some sanity checking. Returns an error in case the latter fails.
+static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {
+ DsymutilOptions Options;
+
+ Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map);
+ Options.DumpStab = Args.hasArg(OPT_symtab);
+ Options.Flat = Args.hasArg(OPT_flat);
+ Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input);
+ Options.PaperTrailWarnings = Args.hasArg(OPT_papertrail);
+ Options.Verify = Args.hasArg(OPT_verify);
+
+ Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr);
+ Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output);
+ Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp);
+ Options.LinkOpts.Update = Args.hasArg(OPT_update);
+ Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose);
+ Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics);
+ Options.LinkOpts.KeepFunctionForStatic =
+ Args.hasArg(OPT_keep_func_for_static);
+
+ if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) {
+ Options.ReproMode = ReproducerMode::Use;
+ Options.ReproducerPath = ReproducerPath->getValue();
+ }
+
+ if (Args.hasArg(OPT_gen_reproducer))
+ Options.ReproMode = ReproducerMode::Generate;
+
+ if (Expected<AccelTableKind> AccelKind = getAccelTableKind(Args)) {
+ Options.LinkOpts.TheAccelTableKind = *AccelKind;
+ } else {
+ return AccelKind.takeError();
+ }
+
+ if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap))
+ Options.SymbolMap = SymbolMap->getValue();
+
+ if (Args.hasArg(OPT_symbolmap))
+ Options.LinkOpts.Update = true;
+
+ if (Expected<std::vector<std::string>> InputFiles =
+ getInputs(Args, Options.LinkOpts.Update)) {
+ Options.InputFiles = std::move(*InputFiles);
+ } else {
+ return InputFiles.takeError();
+ }
+
+ for (auto *Arch : Args.filtered(OPT_arch))
+ Options.Archs.push_back(Arch->getValue());
+
+ if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path))
+ Options.LinkOpts.PrependPath = OsoPrependPath->getValue();
+
+ for (const auto &Arg : Args.getAllArgValues(OPT_object_prefix_map)) {
+ auto Split = StringRef(Arg).split('=');
+ Options.LinkOpts.ObjectPrefixMap.insert(
+ {std::string(Split.first), std::string(Split.second)});
+ }
+
+ if (opt::Arg *OutputFile = Args.getLastArg(OPT_output))
+ Options.OutputFile = OutputFile->getValue();
+
+ if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain))
+ Options.Toolchain = Toolchain->getValue();
+
+ if (Args.hasArg(OPT_assembly))
+ Options.LinkOpts.FileType = OutputFileType::Assembly;
+
+ if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads))
+ Options.LinkOpts.Threads = atoi(NumThreads->getValue());
+ else
+ Options.LinkOpts.Threads = 0; // Use all available hardware threads
+
+ if (Options.DumpDebugMap || Options.LinkOpts.Verbose)
+ Options.LinkOpts.Threads = 1;
+
+ if (getenv("RC_DEBUG_OPTIONS"))
+ Options.PaperTrailWarnings = true;
+
+ if (opt::Arg *RemarksPrependPath = Args.getLastArg(OPT_remarks_prepend_path))
+ Options.LinkOpts.RemarksPrependPath = RemarksPrependPath->getValue();
+
+ if (opt::Arg *RemarksOutputFormat =
+ Args.getLastArg(OPT_remarks_output_format)) {
+ if (Expected<remarks::Format> FormatOrErr =
+ remarks::parseFormat(RemarksOutputFormat->getValue()))
+ Options.LinkOpts.RemarksFormat = *FormatOrErr;
+ else
+ return FormatOrErr.takeError();
+ }
+
+ if (Error E = verifyOptions(Options))
+ return std::move(E);
+ return Options;
+}
+
+static Error createPlistFile(StringRef Bin, StringRef BundleRoot,
+ StringRef Toolchain) {
+ // Create plist file to write to.
+ SmallString<128> InfoPlist(BundleRoot);
+ sys::path::append(InfoPlist, "Contents/Info.plist");
+ std::error_code EC;
+ raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_TextWithCRLF);
+ if (EC)
+ return make_error<StringError>(
+ "cannot create Plist: " + toString(errorCodeToError(EC)), EC);
+
+ CFBundleInfo BI = getBundleInfo(Bin);
+
+ if (BI.IDStr.empty()) {
+ StringRef BundleID = *sys::path::rbegin(BundleRoot);
+ if (sys::path::extension(BundleRoot) == ".dSYM")
+ BI.IDStr = std::string(sys::path::stem(BundleID));
+ else
+ BI.IDStr = std::string(BundleID);
+ }
+
+ // Print out information to the plist file.
+ PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
+ << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
+ << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ << "<plist version=\"1.0\">\n"
+ << "\t<dict>\n"
+ << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
+ << "\t\t<string>English</string>\n"
+ << "\t\t<key>CFBundleIdentifier</key>\n"
+ << "\t\t<string>com.apple.xcode.dsym.";
+ printHTMLEscaped(BI.IDStr, PL);
+ PL << "</string>\n"
+ << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
+ << "\t\t<string>6.0</string>\n"
+ << "\t\t<key>CFBundlePackageType</key>\n"
+ << "\t\t<string>dSYM</string>\n"
+ << "\t\t<key>CFBundleSignature</key>\n"
+ << "\t\t<string>\?\?\?\?</string>\n";
+
+ if (!BI.OmitShortVersion()) {
+ PL << "\t\t<key>CFBundleShortVersionString</key>\n";
+ PL << "\t\t<string>";
+ printHTMLEscaped(BI.ShortVersionStr, PL);
+ PL << "</string>\n";
+ }
+
+ PL << "\t\t<key>CFBundleVersion</key>\n";
+ PL << "\t\t<string>";
+ printHTMLEscaped(BI.VersionStr, PL);
+ PL << "</string>\n";
+
+ if (!Toolchain.empty()) {
+ PL << "\t\t<key>Toolchain</key>\n";
+ PL << "\t\t<string>";
+ printHTMLEscaped(Toolchain, PL);
+ PL << "</string>\n";
+ }
+
+ PL << "\t</dict>\n"
+ << "</plist>\n";
+
+ PL.close();
+ return Error::success();
+}
+
+static Error createBundleDir(StringRef BundleBase) {
+ SmallString<128> Bundle(BundleBase);
+ sys::path::append(Bundle, "Contents", "Resources", "DWARF");
+ if (std::error_code EC =
+ create_directories(Bundle.str(), true, sys::fs::perms::all_all))
+ return make_error<StringError>(
+ "cannot create bundle: " + toString(errorCodeToError(EC)), EC);
+
+ return Error::success();
+}
+
+static bool verify(StringRef OutputFile, StringRef Arch, bool Verbose) {
+ if (OutputFile == "-") {
+ WithColor::warning() << "verification skipped for " << Arch
+ << "because writing to stdout.\n";
+ return true;
+ }
+
+ Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
+ if (!BinOrErr) {
+ WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());
+ return false;
+ }
+
+ Binary &Binary = *BinOrErr.get().getBinary();
+ if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
+ raw_ostream &os = Verbose ? errs() : nulls();
+ os << "Verifying DWARF for architecture: " << Arch << "\n";
+ std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
+ DIDumpOptions DumpOpts;
+ bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion());
+ if (!success)
+ WithColor::error() << "verification failed for " << Arch << '\n';
+ return success;
+ }
+
+ return false;
+}
+
+namespace {
+struct OutputLocation {
+ OutputLocation(std::string DWARFFile, Optional<std::string> ResourceDir = {})
+ : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {}
+ /// This method is a workaround for older compilers.
+ Optional<std::string> getResourceDir() const { return ResourceDir; }
+ std::string DWARFFile;
+ Optional<std::string> ResourceDir;
+};
+} // namespace
+
+static Expected<OutputLocation>
+getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) {
+ if (Options.OutputFile == "-")
+ return OutputLocation(Options.OutputFile);
+
+ // When updating, do in place replacement.
+ if (Options.OutputFile.empty() &&
+ (Options.LinkOpts.Update || !Options.SymbolMap.empty()))
+ return OutputLocation(std::string(InputFile));
+
+ // When dumping the debug map, just return an empty output location. This
+ // allows us to compute the output location once.
+ if (Options.DumpDebugMap)
+ return OutputLocation("");
+
+ // If a flat dSYM has been requested, things are pretty simple.
+ if (Options.Flat) {
+ if (Options.OutputFile.empty()) {
+ if (InputFile == "-")
+ return OutputLocation{"a.out.dwarf", {}};
+ return OutputLocation((InputFile + ".dwarf").str());
+ }
+
+ return OutputLocation(Options.OutputFile);
+ }
+
+ // We need to create/update a dSYM bundle.
+ // A bundle hierarchy looks like this:
+ // <bundle name>.dSYM/
+ // Contents/
+ // Info.plist
+ // Resources/
+ // DWARF/
+ // <DWARF file(s)>
+ std::string DwarfFile =
+ std::string(InputFile == "-" ? StringRef("a.out") : InputFile);
+ SmallString<128> Path(Options.OutputFile);
+ if (Path.empty())
+ Path = DwarfFile + ".dSYM";
+ if (!Options.LinkOpts.NoOutput) {
+ if (auto E = createBundleDir(Path))
+ return std::move(E);
+ if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain))
+ return std::move(E);
+ }
+
+ sys::path::append(Path, "Contents", "Resources");
+ std::string ResourceDir = std::string(Path.str());
+ sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile));
+ return OutputLocation(std::string(Path.str()), ResourceDir);
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ // Parse arguments.
+ DsymutilOptTable T;
+ unsigned MAI;
+ unsigned MAC;
+ ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc - 1);
+ opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC);
+
+ void *P = (void *)(intptr_t)getOutputFileName;
+ std::string SDKPath = sys::fs::getMainExecutable(argv[0], P);
+ SDKPath = std::string(sys::path::parent_path(SDKPath));
+
+ for (auto *Arg : Args.filtered(OPT_UNKNOWN)) {
+ WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling()
+ << '\n';
+ }
+
+ if (Args.hasArg(OPT_help)) {
+ T.printHelp(
+ outs(), (std::string(argv[0]) + " [options] <input files>").c_str(),
+ "manipulate archived DWARF debug symbol files.\n\n"
+ "dsymutil links the DWARF debug information found in the object files\n"
+ "for the executable <input file> by using debug symbols information\n"
+ "contained in its symbol table.\n",
+ false);
+ return EXIT_SUCCESS;
+ }
+
+ if (Args.hasArg(OPT_version)) {
+ cl::PrintVersionMessage();
+ return EXIT_SUCCESS;
+ }
+
+ auto OptionsOrErr = getOptions(Args);
+ if (!OptionsOrErr) {
+ WithColor::error() << toString(OptionsOrErr.takeError());
+ return EXIT_FAILURE;
+ }
+
+ auto &Options = *OptionsOrErr;
+
+ InitializeAllTargetInfos();
+ InitializeAllTargetMCs();
+ InitializeAllTargets();
+ InitializeAllAsmPrinters();
+
+ auto Repro =
+ Reproducer::createReproducer(Options.ReproMode, Options.ReproducerPath);
+ if (!Repro) {
+ WithColor::error() << toString(Repro.takeError());
+ return EXIT_FAILURE;
+ }
+
+ Options.LinkOpts.VFS = (*Repro)->getVFS();
+
+ for (const auto &Arch : Options.Archs)
+ if (Arch != "*" && Arch != "all" &&
+ !object::MachOObjectFile::isValidArch(Arch)) {
+ WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
+ return EXIT_FAILURE;
+ }
+
+ SymbolMapLoader SymMapLoader(Options.SymbolMap);
+
+ for (auto &InputFile : Options.InputFiles) {
+ // Dump the symbol table for each input file and requested arch
+ if (Options.DumpStab) {
+ if (!dumpStab(Options.LinkOpts.VFS, InputFile, Options.Archs,
+ Options.LinkOpts.PrependPath))
+ return EXIT_FAILURE;
+ continue;
+ }
+
+ auto DebugMapPtrsOrErr =
+ parseDebugMap(Options.LinkOpts.VFS, InputFile, Options.Archs,
+ Options.LinkOpts.PrependPath, Options.PaperTrailWarnings,
+ Options.LinkOpts.Verbose, Options.InputIsYAMLDebugMap);
+
+ if (auto EC = DebugMapPtrsOrErr.getError()) {
+ WithColor::error() << "cannot parse the debug map for '" << InputFile
+ << "': " << EC.message() << '\n';
+ return EXIT_FAILURE;
+ }
+
+ // Remember the number of debug maps that are being processed to decide how
+ // to name the remark files.
+ Options.LinkOpts.NumDebugMaps = DebugMapPtrsOrErr->size();
+
+ if (Options.LinkOpts.Update) {
+ // The debug map should be empty. Add one object file corresponding to
+ // the input file.
+ for (auto &Map : *DebugMapPtrsOrErr)
+ Map->addDebugMapObject(InputFile,
+ sys::TimePoint<std::chrono::seconds>());
+ }
+
+ // Ensure that the debug map is not empty (anymore).
+ if (DebugMapPtrsOrErr->empty()) {
+ WithColor::error() << "no architecture to link\n";
+ return EXIT_FAILURE;
+ }
+
+ // Shared a single binary holder for all the link steps.
+ BinaryHolder BinHolder(Options.LinkOpts.VFS);
+
+ // Compute the output location and update the resource directory.
+ Expected<OutputLocation> OutputLocationOrErr =
+ getOutputFileName(InputFile, Options);
+ if (!OutputLocationOrErr) {
+ WithColor::error() << toString(OutputLocationOrErr.takeError());
+ return EXIT_FAILURE;
+ }
+ Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir();
+
+ // Statistics only require different architectures to be processed
+ // sequentially, the link itself can still happen in parallel. Change the
+ // thread pool strategy here instead of modifying LinkOpts.Threads.
+ ThreadPoolStrategy S = hardware_concurrency(
+ Options.LinkOpts.Statistics ? 1 : Options.LinkOpts.Threads);
+ if (Options.LinkOpts.Threads == 0) {
+ // If NumThreads is not specified, create one thread for each input, up to
+ // the number of hardware threads.
+ S.ThreadsRequested = DebugMapPtrsOrErr->size();
+ S.Limit = true;
+ }
+ ThreadPool Threads(S);
+
+ // If there is more than one link to execute, we need to generate
+ // temporary files.
+ const bool NeedsTempFiles =
+ !Options.DumpDebugMap && (Options.OutputFile != "-") &&
+ (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update);
+ const bool Verify = Options.Verify && !Options.LinkOpts.NoOutput;
+
+ SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
+ std::atomic_char AllOK(1);
+ for (auto &Map : *DebugMapPtrsOrErr) {
+ if (Options.LinkOpts.Verbose || Options.DumpDebugMap)
+ Map->print(outs());
+
+ if (Options.DumpDebugMap)
+ continue;
+
+ if (!Options.SymbolMap.empty())
+ Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map);
+
+ if (Map->begin() == Map->end())
+ WithColor::warning()
+ << "no debug symbols in executable (-arch "
+ << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n";
+
+ // 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 = OutputLocationOrErr->DWARFFile;
+ if (NeedsTempFiles) {
+ TempFiles.emplace_back(Map->getTriple().getArchName().str());
+
+ auto E = TempFiles.back().createTempFile();
+ if (E) {
+ WithColor::error() << toString(std::move(E));
+ return EXIT_FAILURE;
+ }
+
+ auto &TempFile = *(TempFiles.back().File);
+ OS = std::make_shared<raw_fd_ostream>(TempFile.FD,
+ /*shouldClose*/ false);
+ OutputFile = TempFile.TmpName;
+ } else {
+ std::error_code EC;
+ OS = std::make_shared<raw_fd_ostream>(
+ Options.LinkOpts.NoOutput ? "-" : OutputFile, EC, sys::fs::OF_None);
+ if (EC) {
+ WithColor::error() << OutputFile << ": " << EC.message();
+ return EXIT_FAILURE;
+ }
+ }
+
+ auto LinkLambda = [&, OutputFile](std::shared_ptr<raw_fd_ostream> Stream,
+ LinkOptions Options) {
+ AllOK.fetch_and(
+ linkDwarf(*Stream, BinHolder, *Map, std::move(Options)));
+ Stream->flush();
+ if (Verify)
+ AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName(),
+ Options.Verbose));
+ };
+
+ // 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.
+ if (S.ThreadsRequested == 1)
+ LinkLambda(OS, Options.LinkOpts);
+ else
+ Threads.async(LinkLambda, OS, Options.LinkOpts);
+ }
+
+ Threads.wait();
+
+ if (!AllOK)
+ return EXIT_FAILURE;
+
+ if (NeedsTempFiles) {
+ if (!MachOUtils::generateUniversalBinary(TempFiles,
+ OutputLocationOrErr->DWARFFile,
+ Options.LinkOpts, SDKPath))
+ return EXIT_FAILURE;
+ }
+
+ // The Mach-O object file format is limited to 4GB. Make sure that we print
+ // an error when we emit an invalid Mach-O companion file. Leave the
+ // invalid object file around on disk for inspection.
+ ErrorOr<vfs::Status> stat =
+ Options.LinkOpts.VFS->status(OutputLocationOrErr->DWARFFile);
+ if (stat) {
+ if (stat->getSize() > std::numeric_limits<uint32_t>::max()) {
+ WithColor::error() << "the linked debug info exceeds the 4GB Mach-O "
+ "object file format.";
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/contrib/libs/llvm14/tools/dsymutil/dsymutil.h b/contrib/libs/llvm14/tools/dsymutil/dsymutil.h
new file mode 100644
index 00000000000..f88f57bb20a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/dsymutil.h
@@ -0,0 +1,56 @@
+//===- tools/dsymutil/dsymutil.h - dsymutil high-level functionality ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+///
+/// This file contains the class declaration for the code that parses STABS
+/// debug maps that are embedded in the binaries symbol tables.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H
+#define LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H
+
+#include "DebugMap.h"
+#include "LinkUtils.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorOr.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace dsymutil {
+
+class BinaryHolder;
+
+/// Extract the DebugMaps from the given file.
+/// The file has to be a MachO object file. Multiple debug maps can be
+/// returned when the file is universal (aka fat) binary.
+ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
+parseDebugMap(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
+ StringRef InputFile, ArrayRef<std::string> Archs,
+ StringRef PrependPath, bool PaperTrailWarnings, bool Verbose,
+ bool InputIsYAML);
+
+/// Dump the symbol table.
+bool dumpStab(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
+ StringRef InputFile, ArrayRef<std::string> Archs,
+ StringRef PrependPath = "");
+
+/// Link the Dwarf debug info as directed by the passed DebugMap \p DM into a
+/// DwarfFile named \p OutputFilename. \returns false if the link failed.
+bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder,
+ const DebugMap &DM, LinkOptions Options);
+
+} // end namespace dsymutil
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H
diff --git a/contrib/libs/llvm14/tools/dsymutil/ya.make b/contrib/libs/llvm14/tools/dsymutil/ya.make
new file mode 100644
index 00000000000..5f18d9531b9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/dsymutil/ya.make
@@ -0,0 +1,98 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/AsmPrinter
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/CodeGen/SelectionDAG
+ contrib/libs/llvm14/lib/DWARFLinker
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/CFGuard
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/dsymutil
+ contrib/libs/llvm14/tools/dsymutil
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ BinaryHolder.cpp
+ CFBundle.cpp
+ DebugMap.cpp
+ DwarfLinkerForBinary.cpp
+ MachODebugMapParser.cpp
+ MachOUtils.cpp
+ Reproducer.cpp
+ SymbolMap.cpp
+ dsymutil.cpp
+)
+
+IF (OS_DARWIN)
+ LDFLAGS(
+ -framework
+ CoreFoundation
+ )
+ENDIF()
+
+END()
diff --git a/contrib/libs/llvm14/tools/gold/README.txt b/contrib/libs/llvm14/tools/gold/README.txt
new file mode 100644
index 00000000000..286d9af13b5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/gold/README.txt
@@ -0,0 +1,13 @@
+The LLVM Gold LTO Plugin
+========================
+
+This directory contains a plugin that is designed to work with binutils
+gold linker. At present time, this is not the default linker in
+binutils, and the default build of gold does not support plugins.
+
+See docs/GoldPlugin.html for complete build and usage instructions.
+
+NOTE: libLTO and LLVMgold aren't built without PIC because they would fail
+to link on x86-64 with a relocation error: PIC and non-PIC can't be combined.
+As an alternative to passing --enable-pic, you can use 'make ENABLE_PIC=1' in
+your entire LLVM build.
diff --git a/contrib/libs/llvm14/tools/gold/gold-plugin.cpp b/contrib/libs/llvm14/tools/gold/gold-plugin.cpp
new file mode 100644
index 00000000000..180c181368e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/gold/gold-plugin.cpp
@@ -0,0 +1,1184 @@
+//===-- gold-plugin.cpp - Plugin to gold for Link Time Optimization ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a gold plugin for LLVM. It provides an LLVM implementation of the
+// interface described in http://gcc.gnu.org/wiki/whopr/driver .
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/Config/config.h" // plugin-api.h requires HAVE_STDINT_H
+#include "llvm/Config/llvm-config.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/Object/Error.h"
+#include "llvm/Remarks/HotnessThresholdParser.h"
+#include "llvm/Support/CachePruning.h"
+#include "llvm/Support/Caching.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+#include <list>
+#include <map>
+#include <plugin-api.h>
+#include <string>
+#include <system_error>
+#include <utility>
+#include <vector>
+
+// FIXME: remove this declaration when we stop maintaining Ubuntu Quantal and
+// Precise and Debian Wheezy (binutils 2.23 is required)
+#define LDPO_PIE 3
+
+#define LDPT_GET_SYMBOLS_V3 28
+
+// FIXME: Remove when binutils 2.31 (containing gold 1.16) is the minimum
+// required version.
+#define LDPT_GET_WRAP_SYMBOLS 32
+
+using namespace llvm;
+using namespace lto;
+
+static codegen::RegisterCodeGenFlags CodeGenFlags;
+
+// FIXME: Remove when binutils 2.31 (containing gold 1.16) is the minimum
+// required version.
+typedef enum ld_plugin_status (*ld_plugin_get_wrap_symbols)(
+ uint64_t *num_symbols, const char ***wrap_symbol_list);
+
+static ld_plugin_status discard_message(int level, const char *format, ...) {
+ // Die loudly. Recent versions of Gold pass ld_plugin_message as the first
+ // callback in the transfer vector. This should never be called.
+ abort();
+}
+
+static ld_plugin_release_input_file release_input_file = nullptr;
+static ld_plugin_get_input_file get_input_file = nullptr;
+static ld_plugin_message message = discard_message;
+static ld_plugin_get_wrap_symbols get_wrap_symbols = nullptr;
+
+namespace {
+struct claimed_file {
+ void *handle;
+ void *leader_handle;
+ std::vector<ld_plugin_symbol> syms;
+ off_t filesize;
+ std::string name;
+};
+
+/// RAII wrapper to manage opening and releasing of a ld_plugin_input_file.
+struct PluginInputFile {
+ void *Handle;
+ std::unique_ptr<ld_plugin_input_file> File;
+
+ PluginInputFile(void *Handle) : Handle(Handle) {
+ File = std::make_unique<ld_plugin_input_file>();
+ if (get_input_file(Handle, File.get()) != LDPS_OK)
+ message(LDPL_FATAL, "Failed to get file information");
+ }
+ ~PluginInputFile() {
+ // File would have been reset to nullptr if we moved this object
+ // to a new owner.
+ if (File)
+ if (release_input_file(Handle) != LDPS_OK)
+ message(LDPL_FATAL, "Failed to release file information");
+ }
+
+ ld_plugin_input_file &file() { return *File; }
+
+ PluginInputFile(PluginInputFile &&RHS) = default;
+ PluginInputFile &operator=(PluginInputFile &&RHS) = default;
+};
+
+struct ResolutionInfo {
+ bool CanOmitFromDynSym = true;
+ bool DefaultVisibility = true;
+ bool CanInline = true;
+ bool IsUsedInRegularObj = false;
+};
+
+}
+
+static ld_plugin_add_symbols add_symbols = nullptr;
+static ld_plugin_get_symbols get_symbols = nullptr;
+static ld_plugin_add_input_file add_input_file = nullptr;
+static ld_plugin_set_extra_library_path set_extra_library_path = nullptr;
+static ld_plugin_get_view get_view = nullptr;
+static bool IsExecutable = false;
+static bool SplitSections = true;
+static Optional<Reloc::Model> RelocationModel = None;
+static std::string output_name = "";
+static std::list<claimed_file> Modules;
+static DenseMap<int, void *> FDToLeaderHandle;
+static StringMap<ResolutionInfo> ResInfo;
+static std::vector<std::string> Cleanup;
+
+namespace options {
+ enum OutputType {
+ OT_NORMAL,
+ OT_DISABLE,
+ OT_BC_ONLY,
+ OT_ASM_ONLY,
+ OT_SAVE_TEMPS
+ };
+ static OutputType TheOutputType = OT_NORMAL;
+ static unsigned OptLevel = 2;
+ // Currently only affects ThinLTO, where the default is the max cores in the
+ // system. See llvm::get_threadpool_strategy() for acceptable values.
+ static std::string Parallelism;
+ // Default regular LTO codegen parallelism (number of partitions).
+ static unsigned ParallelCodeGenParallelismLevel = 1;
+#ifdef NDEBUG
+ static bool DisableVerify = true;
+#else
+ static bool DisableVerify = false;
+#endif
+ static std::string obj_path;
+ static std::string extra_library_path;
+ static std::string triple;
+ static std::string mcpu;
+ // When the thinlto plugin option is specified, only read the function
+ // the information from intermediate files and write a combined
+ // global index for the ThinLTO backends.
+ static bool thinlto = false;
+ // If false, all ThinLTO backend compilations through code gen are performed
+ // using multiple threads in the gold-plugin, before handing control back to
+ // gold. If true, write individual backend index files which reflect
+ // the import decisions, and exit afterwards. The assumption is
+ // that the build system will launch the backend processes.
+ static bool thinlto_index_only = false;
+ // If non-empty, holds the name of a file in which to write the list of
+ // oject files gold selected for inclusion in the link after symbol
+ // resolution (i.e. they had selected symbols). This will only be non-empty
+ // in the thinlto_index_only case. It is used to identify files, which may
+ // have originally been within archive libraries specified via
+ // --start-lib/--end-lib pairs, that should be included in the final
+ // native link process (since intervening function importing and inlining
+ // may change the symbol resolution detected in the final link and which
+ // files to include out of --start-lib/--end-lib libraries as a result).
+ static std::string thinlto_linked_objects_file;
+ // If true, when generating individual index files for distributed backends,
+ // also generate a "${bitcodefile}.imports" file at the same location for each
+ // bitcode file, listing the files it imports from in plain text. This is to
+ // support distributed build file staging.
+ static bool thinlto_emit_imports_files = false;
+ // Option to control where files for a distributed backend (the individual
+ // index files and optional imports files) are created.
+ // If specified, expects a string of the form "oldprefix:newprefix", and
+ // instead of generating these files in the same directory path as the
+ // corresponding bitcode file, will use a path formed by replacing the
+ // bitcode file's path prefix matching oldprefix with newprefix.
+ static std::string thinlto_prefix_replace;
+ // Option to control the name of modules encoded in the individual index
+ // files for a distributed backend. This enables the use of minimized
+ // bitcode files for the thin link, assuming the name of the full bitcode
+ // file used in the backend differs just in some part of the file suffix.
+ // If specified, expects a string of the form "oldsuffix:newsuffix".
+ static std::string thinlto_object_suffix_replace;
+ // Optional path to a directory for caching ThinLTO objects.
+ static std::string cache_dir;
+ // Optional pruning policy for ThinLTO caches.
+ static std::string cache_policy;
+ // Additional options to pass into the code generator.
+ // Note: This array will contain all plugin options which are not claimed
+ // as plugin exclusive to pass to the code generator.
+ static std::vector<const char *> extra;
+ // Sample profile file path
+ static std::string sample_profile;
+ // New pass manager
+ static bool new_pass_manager = LLVM_ENABLE_NEW_PASS_MANAGER;
+ // Debug new pass manager
+ static bool debug_pass_manager = false;
+ // Directory to store the .dwo files.
+ static std::string dwo_dir;
+ /// Statistics output filename.
+ static std::string stats_file;
+ // Asserts that LTO link has whole program visibility
+ static bool whole_program_visibility = false;
+
+ // Optimization remarks filename, accepted passes and hotness options
+ static std::string RemarksFilename;
+ static std::string RemarksPasses;
+ static bool RemarksWithHotness = false;
+ static Optional<uint64_t> RemarksHotnessThreshold = 0;
+ static std::string RemarksFormat;
+
+ // Context sensitive PGO options.
+ static std::string cs_profile_path;
+ static bool cs_pgo_gen = false;
+
+ static void process_plugin_option(const char *opt_)
+ {
+ if (opt_ == nullptr)
+ return;
+ llvm::StringRef opt = opt_;
+
+ if (opt.consume_front("mcpu=")) {
+ mcpu = std::string(opt);
+ } else if (opt.consume_front("extra-library-path=")) {
+ extra_library_path = std::string(opt);
+ } else if (opt.consume_front("mtriple=")) {
+ triple = std::string(opt);
+ } else if (opt.consume_front("obj-path=")) {
+ obj_path = std::string(opt);
+ } else if (opt == "emit-llvm") {
+ TheOutputType = OT_BC_ONLY;
+ } else if (opt == "save-temps") {
+ 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") {
+ thinlto_index_only = true;
+ } else if (opt.consume_front("thinlto-index-only=")) {
+ thinlto_index_only = true;
+ thinlto_linked_objects_file = std::string(opt);
+ } else if (opt == "thinlto-emit-imports-files") {
+ thinlto_emit_imports_files = true;
+ } else if (opt.consume_front("thinlto-prefix-replace=")) {
+ thinlto_prefix_replace = std::string(opt);
+ if (thinlto_prefix_replace.find(';') == std::string::npos)
+ message(LDPL_FATAL, "thinlto-prefix-replace expects 'old;new' format");
+ } else if (opt.consume_front("thinlto-object-suffix-replace=")) {
+ thinlto_object_suffix_replace = std::string(opt);
+ if (thinlto_object_suffix_replace.find(';') == std::string::npos)
+ message(LDPL_FATAL,
+ "thinlto-object-suffix-replace expects 'old;new' format");
+ } else if (opt.consume_front("cache-dir=")) {
+ cache_dir = std::string(opt);
+ } else if (opt.consume_front("cache-policy=")) {
+ cache_policy = std::string(opt);
+ } else if (opt.size() == 2 && opt[0] == 'O') {
+ if (opt[1] < '0' || opt[1] > '3')
+ message(LDPL_FATAL, "Optimization level must be between 0 and 3");
+ OptLevel = opt[1] - '0';
+ } else if (opt.consume_front("jobs=")) {
+ Parallelism = std::string(opt);
+ if (!get_threadpool_strategy(opt))
+ message(LDPL_FATAL, "Invalid parallelism level: %s",
+ Parallelism.c_str());
+ } else if (opt.consume_front("lto-partitions=")) {
+ if (opt.getAsInteger(10, ParallelCodeGenParallelismLevel))
+ message(LDPL_FATAL, "Invalid codegen partition level: %s", opt_ + 5);
+ } else if (opt == "disable-verify") {
+ DisableVerify = true;
+ } else if (opt.consume_front("sample-profile=")) {
+ sample_profile = std::string(opt);
+ } else if (opt == "cs-profile-generate") {
+ cs_pgo_gen = true;
+ } else if (opt.consume_front("cs-profile-path=")) {
+ cs_profile_path = std::string(opt);
+ } else if (opt == "new-pass-manager") {
+ new_pass_manager = true;
+ } else if (opt == "legacy-pass-manager") {
+ new_pass_manager = false;
+ } else if (opt == "debug-pass-manager") {
+ debug_pass_manager = true;
+ } else if (opt == "whole-program-visibility") {
+ whole_program_visibility = true;
+ } else if (opt.consume_front("dwo_dir=")) {
+ dwo_dir = std::string(opt);
+ } else if (opt.consume_front("opt-remarks-filename=")) {
+ RemarksFilename = std::string(opt);
+ } else if (opt.consume_front("opt-remarks-passes=")) {
+ RemarksPasses = std::string(opt);
+ } else if (opt == "opt-remarks-with-hotness") {
+ RemarksWithHotness = true;
+ } else if (opt.consume_front("opt-remarks-hotness-threshold=")) {
+ auto ResultOrErr = remarks::parseHotnessThresholdOption(opt);
+ if (!ResultOrErr)
+ message(LDPL_FATAL, "Invalid remarks hotness threshold: %s", opt);
+ else
+ RemarksHotnessThreshold = *ResultOrErr;
+ } else if (opt.consume_front("opt-remarks-format=")) {
+ RemarksFormat = std::string(opt);
+ } else if (opt.consume_front("stats-file=")) {
+ stats_file = std::string(opt);
+ } else {
+ // Save this option to pass to the code generator.
+ // ParseCommandLineOptions() expects argv[0] to be program name. Lazily
+ // add that.
+ if (extra.empty())
+ extra.push_back("LLVMgold");
+
+ extra.push_back(opt_);
+ }
+ }
+}
+
+static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
+ int *claimed);
+static ld_plugin_status all_symbols_read_hook(void);
+static ld_plugin_status cleanup_hook(void);
+
+extern "C" ld_plugin_status onload(ld_plugin_tv *tv);
+ld_plugin_status onload(ld_plugin_tv *tv) {
+ InitializeAllTargetInfos();
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmParsers();
+ InitializeAllAsmPrinters();
+
+ // We're given a pointer to the first transfer vector. We read through them
+ // until we find one where tv_tag == LDPT_NULL. The REGISTER_* tagged values
+ // contain pointers to functions that we need to call to register our own
+ // hooks. The others are addresses of functions we can use to call into gold
+ // for services.
+
+ bool registeredClaimFile = false;
+ bool RegisteredAllSymbolsRead = false;
+
+ for (; tv->tv_tag != LDPT_NULL; ++tv) {
+ // Cast tv_tag to int to allow values not in "enum ld_plugin_tag", like, for
+ // example, LDPT_GET_SYMBOLS_V3 when building against an older plugin-api.h
+ // header.
+ switch (static_cast<int>(tv->tv_tag)) {
+ case LDPT_OUTPUT_NAME:
+ output_name = tv->tv_u.tv_string;
+ break;
+ case LDPT_LINKER_OUTPUT:
+ switch (tv->tv_u.tv_val) {
+ case LDPO_REL: // .o
+ IsExecutable = false;
+ SplitSections = false;
+ break;
+ case LDPO_DYN: // .so
+ IsExecutable = false;
+ RelocationModel = Reloc::PIC_;
+ break;
+ case LDPO_PIE: // position independent executable
+ IsExecutable = true;
+ RelocationModel = Reloc::PIC_;
+ break;
+ case LDPO_EXEC: // .exe
+ IsExecutable = true;
+ RelocationModel = Reloc::Static;
+ break;
+ default:
+ message(LDPL_ERROR, "Unknown output file type %d", tv->tv_u.tv_val);
+ return LDPS_ERR;
+ }
+ break;
+ case LDPT_OPTION:
+ options::process_plugin_option(tv->tv_u.tv_string);
+ break;
+ case LDPT_REGISTER_CLAIM_FILE_HOOK: {
+ ld_plugin_register_claim_file callback;
+ callback = tv->tv_u.tv_register_claim_file;
+
+ if (callback(claim_file_hook) != LDPS_OK)
+ return LDPS_ERR;
+
+ registeredClaimFile = true;
+ } break;
+ case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: {
+ ld_plugin_register_all_symbols_read callback;
+ callback = tv->tv_u.tv_register_all_symbols_read;
+
+ if (callback(all_symbols_read_hook) != LDPS_OK)
+ return LDPS_ERR;
+
+ RegisteredAllSymbolsRead = true;
+ } break;
+ case LDPT_REGISTER_CLEANUP_HOOK: {
+ ld_plugin_register_cleanup callback;
+ callback = tv->tv_u.tv_register_cleanup;
+
+ if (callback(cleanup_hook) != LDPS_OK)
+ return LDPS_ERR;
+ } break;
+ case LDPT_GET_INPUT_FILE:
+ get_input_file = tv->tv_u.tv_get_input_file;
+ break;
+ case LDPT_RELEASE_INPUT_FILE:
+ release_input_file = tv->tv_u.tv_release_input_file;
+ break;
+ case LDPT_ADD_SYMBOLS:
+ add_symbols = tv->tv_u.tv_add_symbols;
+ break;
+ case LDPT_GET_SYMBOLS_V2:
+ // Do not override get_symbols_v3 with get_symbols_v2.
+ if (!get_symbols)
+ get_symbols = tv->tv_u.tv_get_symbols;
+ break;
+ case LDPT_GET_SYMBOLS_V3:
+ get_symbols = tv->tv_u.tv_get_symbols;
+ break;
+ case LDPT_ADD_INPUT_FILE:
+ add_input_file = tv->tv_u.tv_add_input_file;
+ break;
+ case LDPT_SET_EXTRA_LIBRARY_PATH:
+ set_extra_library_path = tv->tv_u.tv_set_extra_library_path;
+ break;
+ case LDPT_GET_VIEW:
+ get_view = tv->tv_u.tv_get_view;
+ break;
+ case LDPT_MESSAGE:
+ message = tv->tv_u.tv_message;
+ break;
+ case LDPT_GET_WRAP_SYMBOLS:
+ // FIXME: When binutils 2.31 (containing gold 1.16) is the minimum
+ // required version, this should be changed to:
+ // get_wrap_symbols = tv->tv_u.tv_get_wrap_symbols;
+ get_wrap_symbols =
+ (ld_plugin_get_wrap_symbols)tv->tv_u.tv_message;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!registeredClaimFile) {
+ message(LDPL_ERROR, "register_claim_file not passed to LLVMgold.");
+ return LDPS_ERR;
+ }
+ if (!add_symbols) {
+ message(LDPL_ERROR, "add_symbols not passed to LLVMgold.");
+ return LDPS_ERR;
+ }
+
+ if (!RegisteredAllSymbolsRead)
+ return LDPS_OK;
+
+ if (!get_input_file) {
+ message(LDPL_ERROR, "get_input_file not passed to LLVMgold.");
+ return LDPS_ERR;
+ }
+ if (!release_input_file) {
+ message(LDPL_ERROR, "release_input_file not passed to LLVMgold.");
+ return LDPS_ERR;
+ }
+
+ return LDPS_OK;
+}
+
+static void diagnosticHandler(const DiagnosticInfo &DI) {
+ std::string ErrStorage;
+ {
+ raw_string_ostream OS(ErrStorage);
+ DiagnosticPrinterRawOStream DP(OS);
+ DI.print(DP);
+ }
+ ld_plugin_level Level;
+ switch (DI.getSeverity()) {
+ case DS_Error:
+ Level = LDPL_FATAL;
+ break;
+ case DS_Warning:
+ Level = LDPL_WARNING;
+ break;
+ case DS_Note:
+ case DS_Remark:
+ Level = LDPL_INFO;
+ break;
+ }
+ message(Level, "LLVM gold plugin: %s", ErrStorage.c_str());
+}
+
+static void check(Error E, std::string Msg = "LLVM gold plugin") {
+ handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error {
+ message(LDPL_FATAL, "%s: %s", Msg.c_str(), EIB.message().c_str());
+ return Error::success();
+ });
+}
+
+template <typename T> static T check(Expected<T> E) {
+ if (E)
+ return std::move(*E);
+ check(E.takeError());
+ return T();
+}
+
+/// Called by gold to see whether this file is one that our plugin can handle.
+/// We'll try to open it and register all the symbols with add_symbol if
+/// possible.
+static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
+ int *claimed) {
+ MemoryBufferRef BufferRef;
+ std::unique_ptr<MemoryBuffer> Buffer;
+ if (get_view) {
+ const void *view;
+ if (get_view(file->handle, &view) != LDPS_OK) {
+ message(LDPL_ERROR, "Failed to get a view of %s", file->name);
+ return LDPS_ERR;
+ }
+ BufferRef =
+ MemoryBufferRef(StringRef((const char *)view, file->filesize), "");
+ } else {
+ int64_t offset = 0;
+ // Gold has found what might be IR part-way inside of a file, such as
+ // an .a archive.
+ if (file->offset) {
+ offset = file->offset;
+ }
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+ MemoryBuffer::getOpenFileSlice(sys::fs::convertFDToNativeFile(file->fd),
+ file->name, file->filesize, offset);
+ if (std::error_code EC = BufferOrErr.getError()) {
+ message(LDPL_ERROR, EC.message().c_str());
+ return LDPS_ERR;
+ }
+ Buffer = std::move(BufferOrErr.get());
+ BufferRef = Buffer->getMemBufferRef();
+ }
+
+ *claimed = 1;
+
+ Expected<std::unique_ptr<InputFile>> ObjOrErr = InputFile::create(BufferRef);
+ if (!ObjOrErr) {
+ handleAllErrors(ObjOrErr.takeError(), [&](const ErrorInfoBase &EI) {
+ std::error_code EC = EI.convertToErrorCode();
+ if (EC == object::object_error::invalid_file_type ||
+ EC == object::object_error::bitcode_section_not_found)
+ *claimed = 0;
+ else
+ message(LDPL_FATAL,
+ "LLVM gold plugin has failed to create LTO module: %s",
+ EI.message().c_str());
+ });
+
+ return *claimed ? LDPS_ERR : LDPS_OK;
+ }
+
+ std::unique_ptr<InputFile> Obj = std::move(*ObjOrErr);
+
+ Modules.emplace_back();
+ claimed_file &cf = Modules.back();
+
+ cf.handle = file->handle;
+ // Keep track of the first handle for each file descriptor, since there are
+ // multiple in the case of an archive. This is used later in the case of
+ // ThinLTO parallel backends to ensure that each file is only opened and
+ // released once.
+ auto LeaderHandle =
+ FDToLeaderHandle.insert(std::make_pair(file->fd, file->handle)).first;
+ cf.leader_handle = LeaderHandle->second;
+ // Save the filesize since for parallel ThinLTO backends we can only
+ // invoke get_input_file once per archive (only for the leader handle).
+ cf.filesize = file->filesize;
+ // In the case of an archive library, all but the first member must have a
+ // non-zero offset, which we can append to the file name to obtain a
+ // unique name.
+ cf.name = file->name;
+ if (file->offset)
+ cf.name += ".llvm." + std::to_string(file->offset) + "." +
+ sys::path::filename(Obj->getSourceFileName()).str();
+
+ for (auto &Sym : Obj->symbols()) {
+ cf.syms.push_back(ld_plugin_symbol());
+ ld_plugin_symbol &sym = cf.syms.back();
+ sym.version = nullptr;
+ StringRef Name = Sym.getName();
+ sym.name = strdup(Name.str().c_str());
+
+ ResolutionInfo &Res = ResInfo[Name];
+
+ Res.CanOmitFromDynSym &= Sym.canBeOmittedFromSymbolTable();
+
+ sym.visibility = LDPV_DEFAULT;
+ GlobalValue::VisibilityTypes Vis = Sym.getVisibility();
+ if (Vis != GlobalValue::DefaultVisibility)
+ Res.DefaultVisibility = false;
+ switch (Vis) {
+ case GlobalValue::DefaultVisibility:
+ break;
+ case GlobalValue::HiddenVisibility:
+ sym.visibility = LDPV_HIDDEN;
+ break;
+ case GlobalValue::ProtectedVisibility:
+ sym.visibility = LDPV_PROTECTED;
+ break;
+ }
+
+ if (Sym.isUndefined()) {
+ sym.def = LDPK_UNDEF;
+ if (Sym.isWeak())
+ sym.def = LDPK_WEAKUNDEF;
+ } else if (Sym.isCommon())
+ sym.def = LDPK_COMMON;
+ else if (Sym.isWeak())
+ sym.def = LDPK_WEAKDEF;
+ else
+ sym.def = LDPK_DEF;
+
+ sym.size = 0;
+ sym.comdat_key = nullptr;
+ int CI = Sym.getComdatIndex();
+ if (CI != -1) {
+ // Not setting comdat_key for nodeduplicate ensuress we don't deduplicate.
+ std::pair<StringRef, Comdat::SelectionKind> C = Obj->getComdatTable()[CI];
+ if (C.second != Comdat::NoDeduplicate)
+ sym.comdat_key = strdup(C.first.str().c_str());
+ }
+
+ sym.resolution = LDPR_UNKNOWN;
+ }
+
+ if (!cf.syms.empty()) {
+ if (add_symbols(cf.handle, cf.syms.size(), cf.syms.data()) != LDPS_OK) {
+ message(LDPL_ERROR, "Unable to add symbols!");
+ return LDPS_ERR;
+ }
+ }
+
+ // Handle any --wrap options passed to gold, which are than passed
+ // along to the plugin.
+ if (get_wrap_symbols) {
+ const char **wrap_symbols;
+ uint64_t count = 0;
+ if (get_wrap_symbols(&count, &wrap_symbols) != LDPS_OK) {
+ message(LDPL_ERROR, "Unable to get wrap symbols!");
+ return LDPS_ERR;
+ }
+ for (uint64_t i = 0; i < count; i++) {
+ StringRef Name = wrap_symbols[i];
+ ResolutionInfo &Res = ResInfo[Name];
+ ResolutionInfo &WrapRes = ResInfo["__wrap_" + Name.str()];
+ ResolutionInfo &RealRes = ResInfo["__real_" + Name.str()];
+ // Tell LTO not to inline symbols that will be overwritten.
+ Res.CanInline = false;
+ RealRes.CanInline = false;
+ // Tell LTO not to eliminate symbols that will be used after renaming.
+ Res.IsUsedInRegularObj = true;
+ WrapRes.IsUsedInRegularObj = true;
+ }
+ }
+
+ return LDPS_OK;
+}
+
+static void freeSymName(ld_plugin_symbol &Sym) {
+ free(Sym.name);
+ free(Sym.comdat_key);
+ Sym.name = nullptr;
+ Sym.comdat_key = nullptr;
+}
+
+/// Helper to get a file's symbols and a view into it via gold callbacks.
+static const void *getSymbolsAndView(claimed_file &F) {
+ ld_plugin_status status = get_symbols(F.handle, F.syms.size(), F.syms.data());
+ if (status == LDPS_NO_SYMS)
+ return nullptr;
+
+ if (status != LDPS_OK)
+ message(LDPL_FATAL, "Failed to get symbol information");
+
+ const void *View;
+ if (get_view(F.handle, &View) != LDPS_OK)
+ message(LDPL_FATAL, "Failed to get a view of file");
+
+ return View;
+}
+
+/// Parse the thinlto-object-suffix-replace option into the \p OldSuffix and
+/// \p NewSuffix strings, if it was specified.
+static void getThinLTOOldAndNewSuffix(std::string &OldSuffix,
+ std::string &NewSuffix) {
+ assert(options::thinlto_object_suffix_replace.empty() ||
+ options::thinlto_object_suffix_replace.find(';') != StringRef::npos);
+ StringRef SuffixReplace = options::thinlto_object_suffix_replace;
+ auto Split = SuffixReplace.split(';');
+ OldSuffix = std::string(Split.first);
+ NewSuffix = std::string(Split.second);
+}
+
+/// Given the original \p Path to an output file, replace any filename
+/// suffix matching \p OldSuffix with \p NewSuffix.
+static std::string getThinLTOObjectFileName(StringRef Path, StringRef OldSuffix,
+ StringRef NewSuffix) {
+ if (Path.consume_back(OldSuffix))
+ return (Path + NewSuffix).str();
+ return std::string(Path);
+}
+
+// Returns true if S is valid as a C language identifier.
+static bool isValidCIdentifier(StringRef S) {
+ return !S.empty() && (isAlpha(S[0]) || S[0] == '_') &&
+ std::all_of(S.begin() + 1, S.end(),
+ [](char C) { return C == '_' || isAlnum(C); });
+}
+
+static bool isUndefined(ld_plugin_symbol &Sym) {
+ return Sym.def == LDPK_UNDEF || Sym.def == LDPK_WEAKUNDEF;
+}
+
+static void addModule(LTO &Lto, claimed_file &F, const void *View,
+ StringRef Filename) {
+ MemoryBufferRef BufferRef(StringRef((const char *)View, F.filesize),
+ Filename);
+ Expected<std::unique_ptr<InputFile>> ObjOrErr = InputFile::create(BufferRef);
+
+ if (!ObjOrErr)
+ message(LDPL_FATAL, "Could not read bitcode from file : %s",
+ toString(ObjOrErr.takeError()).c_str());
+
+ unsigned SymNum = 0;
+ std::unique_ptr<InputFile> Input = std::move(ObjOrErr.get());
+ auto InputFileSyms = Input->symbols();
+ assert(InputFileSyms.size() == F.syms.size());
+ std::vector<SymbolResolution> Resols(F.syms.size());
+ for (ld_plugin_symbol &Sym : F.syms) {
+ const InputFile::Symbol &InpSym = InputFileSyms[SymNum];
+ SymbolResolution &R = Resols[SymNum++];
+
+ ld_plugin_symbol_resolution Resolution =
+ (ld_plugin_symbol_resolution)Sym.resolution;
+
+ ResolutionInfo &Res = ResInfo[Sym.name];
+
+ switch (Resolution) {
+ case LDPR_UNKNOWN:
+ llvm_unreachable("Unexpected resolution");
+
+ case LDPR_RESOLVED_IR:
+ case LDPR_RESOLVED_EXEC:
+ case LDPR_PREEMPTED_IR:
+ case LDPR_PREEMPTED_REG:
+ case LDPR_UNDEF:
+ break;
+
+ case LDPR_RESOLVED_DYN:
+ R.ExportDynamic = true;
+ break;
+
+ case LDPR_PREVAILING_DEF_IRONLY:
+ R.Prevailing = !isUndefined(Sym);
+ break;
+
+ case LDPR_PREVAILING_DEF:
+ R.Prevailing = !isUndefined(Sym);
+ R.VisibleToRegularObj = true;
+ break;
+
+ case LDPR_PREVAILING_DEF_IRONLY_EXP:
+ R.Prevailing = !isUndefined(Sym);
+ // Identify symbols exported dynamically, and that therefore could be
+ // referenced by a shared library not visible to the linker.
+ R.ExportDynamic = true;
+ if (!Res.CanOmitFromDynSym)
+ R.VisibleToRegularObj = true;
+ break;
+ }
+
+ // If the symbol has a C identifier section name, we need to mark
+ // it as visible to a regular object so that LTO will keep it around
+ // to ensure the linker generates special __start_<secname> and
+ // __stop_<secname> symbols which may be used elsewhere.
+ if (isValidCIdentifier(InpSym.getSectionName()))
+ R.VisibleToRegularObj = true;
+
+ if (Resolution != LDPR_RESOLVED_DYN && Resolution != LDPR_UNDEF &&
+ (IsExecutable || !Res.DefaultVisibility))
+ R.FinalDefinitionInLinkageUnit = true;
+
+ if (!Res.CanInline)
+ R.LinkerRedefined = true;
+
+ if (Res.IsUsedInRegularObj)
+ R.VisibleToRegularObj = true;
+
+ freeSymName(Sym);
+ }
+
+ check(Lto.add(std::move(Input), Resols),
+ std::string("Failed to link module ") + F.name);
+}
+
+static void recordFile(const std::string &Filename, bool TempOutFile) {
+ if (add_input_file(Filename.c_str()) != LDPS_OK)
+ message(LDPL_FATAL,
+ "Unable to add .o file to the link. File left behind in: %s",
+ Filename.c_str());
+ if (TempOutFile)
+ Cleanup.push_back(Filename);
+}
+
+/// Return the desired output filename given a base input name, a flag
+/// indicating whether a temp file should be generated, and an optional task id.
+/// The new filename generated is returned in \p NewFilename.
+static int getOutputFileName(StringRef InFilename, bool TempOutFile,
+ SmallString<128> &NewFilename, int TaskID) {
+ int FD = -1;
+ if (TempOutFile) {
+ std::error_code EC =
+ sys::fs::createTemporaryFile("lto-llvm", "o", FD, NewFilename);
+ if (EC)
+ message(LDPL_FATAL, "Could not create temporary file: %s",
+ EC.message().c_str());
+ } else {
+ NewFilename = InFilename;
+ if (TaskID > 0)
+ NewFilename += utostr(TaskID);
+ std::error_code EC =
+ sys::fs::openFileForWrite(NewFilename, FD, sys::fs::CD_CreateAlways);
+ if (EC)
+ message(LDPL_FATAL, "Could not open file %s: %s", NewFilename.c_str(),
+ EC.message().c_str());
+ }
+ return FD;
+}
+
+static CodeGenOpt::Level getCGOptLevel() {
+ switch (options::OptLevel) {
+ case 0:
+ return CodeGenOpt::None;
+ case 1:
+ return CodeGenOpt::Less;
+ case 2:
+ return CodeGenOpt::Default;
+ case 3:
+ return CodeGenOpt::Aggressive;
+ }
+ llvm_unreachable("Invalid optimization level");
+}
+
+/// Parse the thinlto_prefix_replace option into the \p OldPrefix and
+/// \p NewPrefix strings, if it was specified.
+static void getThinLTOOldAndNewPrefix(std::string &OldPrefix,
+ std::string &NewPrefix) {
+ StringRef PrefixReplace = options::thinlto_prefix_replace;
+ assert(PrefixReplace.empty() || PrefixReplace.find(';') != StringRef::npos);
+ auto Split = PrefixReplace.split(';');
+ OldPrefix = std::string(Split.first);
+ NewPrefix = std::string(Split.second);
+}
+
+/// Creates instance of LTO.
+/// OnIndexWrite is callback to let caller know when LTO writes index files.
+/// LinkedObjectsFile is an output stream to write the list of object files for
+/// the final ThinLTO linking. Can be nullptr.
+static std::unique_ptr<LTO> createLTO(IndexWriteCallback OnIndexWrite,
+ raw_fd_ostream *LinkedObjectsFile) {
+ Config Conf;
+ ThinBackend Backend;
+
+ Conf.CPU = options::mcpu;
+ Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+
+ // Disable the new X86 relax relocations since gold might not support them.
+ // FIXME: Check the gold version or add a new option to enable them.
+ Conf.Options.RelaxELFRelocations = false;
+
+ // Toggle function/data sections.
+ if (!codegen::getExplicitFunctionSections())
+ Conf.Options.FunctionSections = SplitSections;
+ if (!codegen::getExplicitDataSections())
+ Conf.Options.DataSections = SplitSections;
+
+ Conf.MAttrs = codegen::getMAttrs();
+ Conf.RelocModel = RelocationModel;
+ Conf.CodeModel = codegen::getExplicitCodeModel();
+ Conf.CGOptLevel = getCGOptLevel();
+ Conf.DisableVerify = options::DisableVerify;
+ Conf.OptLevel = options::OptLevel;
+ Conf.PTO.LoopVectorization = options::OptLevel > 1;
+ Conf.PTO.SLPVectorization = options::OptLevel > 1;
+ Conf.AlwaysEmitRegularLTOObj = !options::obj_path.empty();
+
+ if (options::thinlto_index_only) {
+ std::string OldPrefix, NewPrefix;
+ getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix);
+ Backend = createWriteIndexesThinBackend(OldPrefix, NewPrefix,
+ options::thinlto_emit_imports_files,
+ LinkedObjectsFile, OnIndexWrite);
+ } else {
+ Backend = createInProcessThinBackend(
+ llvm::heavyweight_hardware_concurrency(options::Parallelism));
+ }
+
+ Conf.OverrideTriple = options::triple;
+ Conf.DefaultTriple = sys::getDefaultTargetTriple();
+
+ Conf.DiagHandler = diagnosticHandler;
+
+ switch (options::TheOutputType) {
+ case options::OT_NORMAL:
+ break;
+
+ case options::OT_DISABLE:
+ Conf.PreOptModuleHook = [](size_t Task, const Module &M) { return false; };
+ break;
+
+ case options::OT_BC_ONLY:
+ Conf.PostInternalizeModuleHook = [](size_t Task, const Module &M) {
+ std::error_code EC;
+ SmallString<128> TaskFilename;
+ getOutputFileName(output_name, /* TempOutFile */ false, TaskFilename,
+ Task);
+ raw_fd_ostream OS(TaskFilename, EC, sys::fs::OpenFlags::OF_None);
+ if (EC)
+ message(LDPL_FATAL, "Failed to write the output file.");
+ WriteBitcodeToFile(M, OS, /* ShouldPreserveUseListOrder */ false);
+ return false;
+ };
+ break;
+
+ case options::OT_SAVE_TEMPS:
+ check(Conf.addSaveTemps(output_name + ".",
+ /* UseInputModulePath */ true));
+ break;
+ case options::OT_ASM_ONLY:
+ Conf.CGFileType = CGFT_AssemblyFile;
+ break;
+ }
+
+ if (!options::sample_profile.empty())
+ Conf.SampleProfile = options::sample_profile;
+
+ if (!options::cs_profile_path.empty())
+ Conf.CSIRProfile = options::cs_profile_path;
+ Conf.RunCSIRInstr = options::cs_pgo_gen;
+
+ Conf.DwoDir = options::dwo_dir;
+
+ // Set up optimization remarks handling.
+ Conf.RemarksFilename = options::RemarksFilename;
+ Conf.RemarksPasses = options::RemarksPasses;
+ Conf.RemarksWithHotness = options::RemarksWithHotness;
+ Conf.RemarksHotnessThreshold = options::RemarksHotnessThreshold;
+ Conf.RemarksFormat = options::RemarksFormat;
+
+ // Use new pass manager if set in driver
+ Conf.UseNewPM = options::new_pass_manager;
+ // Debug new pass manager if requested
+ Conf.DebugPassManager = options::debug_pass_manager;
+
+ Conf.HasWholeProgramVisibility = options::whole_program_visibility;
+
+ Conf.StatsFile = options::stats_file;
+ return std::make_unique<LTO>(std::move(Conf), Backend,
+ options::ParallelCodeGenParallelismLevel);
+}
+
+// Write empty files that may be expected by a distributed build
+// system when invoked with thinlto_index_only. This is invoked when
+// the linker has decided not to include the given module in the
+// final link. Frequently the distributed build system will want to
+// confirm that all expected outputs are created based on all of the
+// modules provided to the linker.
+// If SkipModule is true then .thinlto.bc should contain just
+// SkipModuleByDistributedBackend flag which requests distributed backend
+// to skip the compilation of the corresponding module and produce an empty
+// object file.
+static void writeEmptyDistributedBuildOutputs(const std::string &ModulePath,
+ const std::string &OldPrefix,
+ const std::string &NewPrefix,
+ bool SkipModule) {
+ std::string NewModulePath =
+ getThinLTOOutputFile(ModulePath, OldPrefix, NewPrefix);
+ std::error_code EC;
+ {
+ raw_fd_ostream OS(NewModulePath + ".thinlto.bc", EC,
+ sys::fs::OpenFlags::OF_None);
+ if (EC)
+ message(LDPL_FATAL, "Failed to write '%s': %s",
+ (NewModulePath + ".thinlto.bc").c_str(), EC.message().c_str());
+
+ if (SkipModule) {
+ ModuleSummaryIndex Index(/*HaveGVs*/ false);
+ Index.setSkipModuleByDistributedBackend();
+ writeIndexToFile(Index, OS, nullptr);
+ }
+ }
+ if (options::thinlto_emit_imports_files) {
+ raw_fd_ostream OS(NewModulePath + ".imports", EC,
+ sys::fs::OpenFlags::OF_None);
+ if (EC)
+ message(LDPL_FATAL, "Failed to write '%s': %s",
+ (NewModulePath + ".imports").c_str(), EC.message().c_str());
+ }
+}
+
+// Creates and returns output stream with a list of object files for final
+// linking of distributed ThinLTO.
+static std::unique_ptr<raw_fd_ostream> CreateLinkedObjectsFile() {
+ if (options::thinlto_linked_objects_file.empty())
+ return nullptr;
+ assert(options::thinlto_index_only);
+ std::error_code EC;
+ auto LinkedObjectsFile = std::make_unique<raw_fd_ostream>(
+ options::thinlto_linked_objects_file, EC, sys::fs::OpenFlags::OF_None);
+ if (EC)
+ message(LDPL_FATAL, "Failed to create '%s': %s",
+ options::thinlto_linked_objects_file.c_str(), EC.message().c_str());
+ return LinkedObjectsFile;
+}
+
+/// Runs LTO and return a list of pairs <FileName, IsTemporary>.
+static std::vector<std::pair<SmallString<128>, bool>> runLTO() {
+ // Map to own RAII objects that manage the file opening and releasing
+ // interfaces with gold. This is needed only for ThinLTO mode, since
+ // unlike regular LTO, where addModule will result in the opened file
+ // being merged into a new combined module, we need to keep these files open
+ // through Lto->run().
+ DenseMap<void *, std::unique_ptr<PluginInputFile>> HandleToInputFile;
+
+ // Owns string objects and tells if index file was already created.
+ StringMap<bool> ObjectToIndexFileState;
+
+ std::unique_ptr<raw_fd_ostream> LinkedObjects = CreateLinkedObjectsFile();
+ std::unique_ptr<LTO> Lto = createLTO(
+ [&ObjectToIndexFileState](const std::string &Identifier) {
+ ObjectToIndexFileState[Identifier] = true;
+ },
+ LinkedObjects.get());
+
+ std::string OldPrefix, NewPrefix;
+ if (options::thinlto_index_only)
+ getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix);
+
+ std::string OldSuffix, NewSuffix;
+ getThinLTOOldAndNewSuffix(OldSuffix, NewSuffix);
+
+ for (claimed_file &F : Modules) {
+ if (options::thinlto && !HandleToInputFile.count(F.leader_handle))
+ HandleToInputFile.insert(std::make_pair(
+ F.leader_handle, std::make_unique<PluginInputFile>(F.handle)));
+ // In case we are thin linking with a minimized bitcode file, ensure
+ // the module paths encoded in the index reflect where the backends
+ // will locate the full bitcode files for compiling/importing.
+ std::string Identifier =
+ getThinLTOObjectFileName(F.name, OldSuffix, NewSuffix);
+ auto ObjFilename = ObjectToIndexFileState.insert({Identifier, false});
+ assert(ObjFilename.second);
+ if (const void *View = getSymbolsAndView(F))
+ addModule(*Lto, F, View, ObjFilename.first->first());
+ else if (options::thinlto_index_only) {
+ ObjFilename.first->second = true;
+ writeEmptyDistributedBuildOutputs(Identifier, OldPrefix, NewPrefix,
+ /* SkipModule */ true);
+ }
+ }
+
+ SmallString<128> Filename;
+ // Note that getOutputFileName will append a unique ID for each task
+ if (!options::obj_path.empty())
+ Filename = options::obj_path;
+ else if (options::TheOutputType == options::OT_SAVE_TEMPS)
+ Filename = output_name + ".lto.o";
+ else if (options::TheOutputType == options::OT_ASM_ONLY)
+ Filename = output_name;
+ bool SaveTemps = !Filename.empty();
+
+ size_t MaxTasks = Lto->getMaxTasks();
+ std::vector<std::pair<SmallString<128>, bool>> Files(MaxTasks);
+
+ auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
+ Files[Task].second = !SaveTemps;
+ int FD = getOutputFileName(Filename, /* TempOutFile */ !SaveTemps,
+ Files[Task].first, Task);
+ return std::make_unique<CachedFileStream>(
+ std::make_unique<llvm::raw_fd_ostream>(FD, true));
+ };
+
+ auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
+ *AddStream(Task)->OS << MB->getBuffer();
+ };
+
+ FileCache Cache;
+ if (!options::cache_dir.empty())
+ Cache = check(localCache("ThinLTO", "Thin", options::cache_dir, AddBuffer));
+
+ check(Lto->run(AddStream, Cache));
+
+ // Write empty output files that may be expected by the distributed build
+ // system.
+ if (options::thinlto_index_only)
+ for (auto &Identifier : ObjectToIndexFileState)
+ if (!Identifier.getValue())
+ writeEmptyDistributedBuildOutputs(std::string(Identifier.getKey()),
+ OldPrefix, NewPrefix,
+ /* SkipModule */ false);
+
+ return Files;
+}
+
+/// gold informs us that all symbols have been read. At this point, we use
+/// get_symbols to see if any of our definitions have been overridden by a
+/// native object file. Then, perform optimization and codegen.
+static ld_plugin_status allSymbolsReadHook() {
+ if (Modules.empty())
+ return LDPS_OK;
+
+ if (unsigned NumOpts = options::extra.size())
+ cl::ParseCommandLineOptions(NumOpts, &options::extra[0]);
+
+ 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_ASM_ONLY)
+ return LDPS_OK;
+
+ if (options::thinlto_index_only) {
+ llvm_shutdown();
+ cleanup_hook();
+ exit(0);
+ }
+
+ for (const auto &F : Files)
+ if (!F.first.empty())
+ recordFile(std::string(F.first.str()), F.second);
+
+ if (!options::extra_library_path.empty() &&
+ set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK)
+ message(LDPL_FATAL, "Unable to set the extra library path.");
+
+ return LDPS_OK;
+}
+
+static ld_plugin_status all_symbols_read_hook(void) {
+ ld_plugin_status Ret = allSymbolsReadHook();
+ 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
+ // early.
+ std::error_code EC = sys::fs::remove(output_name);
+ if (EC)
+ message(LDPL_ERROR, "Failed to delete '%s': %s", output_name.c_str(),
+ EC.message().c_str());
+ }
+ exit(0);
+ }
+
+ return Ret;
+}
+
+static ld_plugin_status cleanup_hook(void) {
+ for (std::string &Name : Cleanup) {
+ std::error_code EC = sys::fs::remove(Name);
+ if (EC)
+ message(LDPL_ERROR, "Failed to delete '%s': %s", Name.c_str(),
+ EC.message().c_str());
+ }
+
+ // Prune cache
+ if (!options::cache_dir.empty()) {
+ CachePruningPolicy policy = check(parseCachePruningPolicy(options::cache_policy));
+ pruneCache(options::cache_dir, policy);
+ }
+
+ return LDPS_OK;
+}
diff --git a/contrib/libs/llvm14/tools/gold/gold.exports b/contrib/libs/llvm14/tools/gold/gold.exports
new file mode 100644
index 00000000000..277a33a1ec3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/gold/gold.exports
@@ -0,0 +1 @@
+onload
diff --git a/contrib/libs/llvm14/tools/gold/ya.make b/contrib/libs/llvm14/tools/gold/ya.make
new file mode 100644
index 00000000000..40c4bf5825c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/gold/ya.make
@@ -0,0 +1,67 @@
+# Generated by devtools/yamaker.
+
+DLL(LLVMgold PREFIX "")
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ build/platform/binutils
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/LTO
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/Transforms/IPO
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/gold
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+EXPORTS_SCRIPT(gold.exports)
+
+CFLAGS(
+ -I$BINUTILS_ROOT_RESOURCE_GLOBAL/include
+)
+
+SRCS(
+ gold-plugin.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llc/llc.cpp b/contrib/libs/llvm14/tools/llc/llc.cpp
new file mode 100644
index 00000000000..c07f4e66486
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llc/llc.cpp
@@ -0,0 +1,753 @@
+//===-- llc.cpp - Implement the LLVM Native Code Generator ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is the llc code generator driver. It provides a convenient
+// command-line interface for generating native assembly-language code
+// or C code, given LLVM bitcode.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/CodeGen/LinkAllAsmWriterComponents.h"
+#include "llvm/CodeGen/LinkAllCodegenComponents.h"
+#include "llvm/CodeGen/MIRParser/MIRParser.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/IR/AutoUpgrade.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/IRPrintingPasses.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LLVMRemarkStreamer.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Pass.h"
+#include "llvm/Remarks/HotnessThresholdParser.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/PluginLoader.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/TimeProfiler.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Target/TargetLoweringObjectFile.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include <memory>
+using namespace llvm;
+
+static codegen::RegisterCodeGenFlags CGF;
+
+// General options for llc. Other pass-specific options are specified
+// within the corresponding llc passes, and target-specific options
+// and back-end code generation options are specified with the target machine.
+//
+static cl::opt<std::string>
+InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-"));
+
+static cl::opt<std::string>
+InputLanguage("x", cl::desc("Input language ('ir' or 'mir')"));
+
+static cl::opt<std::string>
+OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"));
+
+static cl::opt<std::string>
+ SplitDwarfOutputFile("split-dwarf-output",
+ cl::desc(".dwo output filename"),
+ cl::value_desc("filename"));
+
+static cl::opt<unsigned>
+TimeCompilations("time-compilations", cl::Hidden, cl::init(1u),
+ cl::value_desc("N"),
+ cl::desc("Repeat compilation N times for timing"));
+
+static cl::opt<bool> TimeTrace("time-trace", cl::desc("Record time trace"));
+
+static cl::opt<unsigned> TimeTraceGranularity(
+ "time-trace-granularity",
+ cl::desc(
+ "Minimum time granularity (in microseconds) traced by time profiler"),
+ cl::init(500), cl::Hidden);
+
+static cl::opt<std::string>
+ TimeTraceFile("time-trace-file",
+ cl::desc("Specify time trace file destination"),
+ cl::value_desc("filename"));
+
+static cl::opt<std::string>
+ BinutilsVersion("binutils-version", cl::Hidden,
+ cl::desc("Produced object files can use all ELF features "
+ "supported by this binutils version and newer."
+ "If -no-integrated-as is specified, the generated "
+ "assembly will consider GNU as support."
+ "'none' means that all ELF features can be used, "
+ "regardless of binutils support"));
+
+static cl::opt<bool>
+NoIntegratedAssembler("no-integrated-as", cl::Hidden,
+ cl::desc("Disable integrated assembler"));
+
+static cl::opt<bool>
+ PreserveComments("preserve-as-comments", cl::Hidden,
+ cl::desc("Preserve Comments in outputted assembly"),
+ cl::init(true));
+
+// Determine optimization level.
+static cl::opt<char>
+OptLevel("O",
+ cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
+ "(default = '-O2')"),
+ cl::Prefix,
+ cl::ZeroOrMore,
+ cl::init(' '));
+
+static cl::opt<std::string>
+TargetTriple("mtriple", cl::desc("Override target triple for module"));
+
+static cl::opt<std::string> SplitDwarfFile(
+ "split-dwarf-file",
+ cl::desc(
+ "Specify the name of the .dwo file to encode in the DWARF output"));
+
+static cl::opt<bool> NoVerify("disable-verify", cl::Hidden,
+ cl::desc("Do not verify input module"));
+
+static cl::opt<bool> DisableSimplifyLibCalls("disable-simplify-libcalls",
+ cl::desc("Disable simplify-libcalls"));
+
+static cl::opt<bool> ShowMCEncoding("show-mc-encoding", cl::Hidden,
+ cl::desc("Show encoding in .s output"));
+
+static cl::opt<bool>
+ DwarfDirectory("dwarf-directory", cl::Hidden,
+ cl::desc("Use .file directives with an explicit directory"),
+ cl::init(true));
+
+static cl::opt<bool> AsmVerbose("asm-verbose",
+ cl::desc("Add comments to directives."),
+ cl::init(true));
+
+static cl::opt<bool>
+ CompileTwice("compile-twice", cl::Hidden,
+ cl::desc("Run everything twice, re-using the same pass "
+ "manager and verify the result is the same."),
+ cl::init(false));
+
+static cl::opt<bool> DiscardValueNames(
+ "discard-value-names",
+ cl::desc("Discard names from Value (other than GlobalValue)."),
+ cl::init(false), cl::Hidden);
+
+static cl::list<std::string> IncludeDirs("I", cl::desc("include search path"));
+
+static cl::opt<bool> RemarksWithHotness(
+ "pass-remarks-with-hotness",
+ cl::desc("With PGO, include profile count in optimization remarks"),
+ cl::Hidden);
+
+static cl::opt<Optional<uint64_t>, false, remarks::HotnessThresholdParser>
+ RemarksHotnessThreshold(
+ "pass-remarks-hotness-threshold",
+ cl::desc("Minimum profile count required for "
+ "an optimization remark to be output. "
+ "Use 'auto' to apply the threshold from profile summary."),
+ cl::value_desc("N or 'auto'"), cl::init(0), cl::Hidden);
+
+static cl::opt<std::string>
+ RemarksFilename("pass-remarks-output",
+ cl::desc("Output filename for pass remarks"),
+ cl::value_desc("filename"));
+
+static cl::opt<std::string>
+ RemarksPasses("pass-remarks-filter",
+ cl::desc("Only record optimization remarks from passes whose "
+ "names match the given regular expression"),
+ cl::value_desc("regex"));
+
+static cl::opt<std::string> RemarksFormat(
+ "pass-remarks-format",
+ cl::desc("The format used for serializing remarks (default: YAML)"),
+ cl::value_desc("format"), cl::init("yaml"));
+
+namespace {
+static ManagedStatic<std::vector<std::string>> RunPassNames;
+
+struct RunPassOption {
+ void operator=(const std::string &Val) const {
+ if (Val.empty())
+ return;
+ SmallVector<StringRef, 8> PassNames;
+ StringRef(Val).split(PassNames, ',', -1, false);
+ for (auto PassName : PassNames)
+ RunPassNames->push_back(std::string(PassName));
+ }
+};
+}
+
+static RunPassOption RunPassOpt;
+
+static cl::opt<RunPassOption, true, cl::parser<std::string>> RunPass(
+ "run-pass",
+ cl::desc("Run compiler only for specified passes (comma separated list)"),
+ cl::value_desc("pass-name"), cl::ZeroOrMore, cl::location(RunPassOpt));
+
+static int compileModule(char **, LLVMContext &);
+
+[[noreturn]] static void reportError(Twine Msg, StringRef Filename = "") {
+ SmallString<256> Prefix;
+ if (!Filename.empty()) {
+ if (Filename == "-")
+ Filename = "<stdin>";
+ ("'" + Twine(Filename) + "': ").toStringRef(Prefix);
+ }
+ WithColor::error(errs(), "llc") << Prefix << Msg << "\n";
+ exit(1);
+}
+
+[[noreturn]] static void reportError(Error Err, StringRef Filename) {
+ assert(Err);
+ handleAllErrors(createFileError(Filename, std::move(Err)),
+ [&](const ErrorInfoBase &EI) { reportError(EI.message()); });
+ llvm_unreachable("reportError() should not return");
+}
+
+static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName,
+ Triple::OSType OS,
+ const char *ProgName) {
+ // If we don't yet have an output filename, make one.
+ if (OutputFilename.empty()) {
+ if (InputFilename == "-")
+ OutputFilename = "-";
+ else {
+ // If InputFilename ends in .bc or .ll, remove it.
+ StringRef IFN = InputFilename;
+ if (IFN.endswith(".bc") || IFN.endswith(".ll"))
+ OutputFilename = std::string(IFN.drop_back(3));
+ else if (IFN.endswith(".mir"))
+ OutputFilename = std::string(IFN.drop_back(4));
+ else
+ OutputFilename = std::string(IFN);
+
+ switch (codegen::getFileType()) {
+ case CGFT_AssemblyFile:
+ if (TargetName[0] == 'c') {
+ if (TargetName[1] == 0)
+ OutputFilename += ".cbe.c";
+ else if (TargetName[1] == 'p' && TargetName[2] == 'p')
+ OutputFilename += ".cpp";
+ else
+ OutputFilename += ".s";
+ } else
+ OutputFilename += ".s";
+ break;
+ case CGFT_ObjectFile:
+ if (OS == Triple::Win32)
+ OutputFilename += ".obj";
+ else
+ OutputFilename += ".o";
+ break;
+ case CGFT_Null:
+ OutputFilename = "-";
+ break;
+ }
+ }
+ }
+
+ // Decide if we need "binary" output.
+ bool Binary = false;
+ switch (codegen::getFileType()) {
+ case CGFT_AssemblyFile:
+ break;
+ case CGFT_ObjectFile:
+ case CGFT_Null:
+ Binary = true;
+ break;
+ }
+
+ // Open the file.
+ std::error_code EC;
+ sys::fs::OpenFlags OpenFlags = sys::fs::OF_None;
+ if (!Binary)
+ OpenFlags |= sys::fs::OF_TextWithCRLF;
+ auto FDOut = std::make_unique<ToolOutputFile>(OutputFilename, EC, OpenFlags);
+ if (EC) {
+ reportError(EC.message());
+ return nullptr;
+ }
+
+ return FDOut;
+}
+
+struct LLCDiagnosticHandler : public DiagnosticHandler {
+ bool *HasError;
+ LLCDiagnosticHandler(bool *HasErrorPtr) : HasError(HasErrorPtr) {}
+ bool handleDiagnostics(const DiagnosticInfo &DI) override {
+ if (DI.getKind() == llvm::DK_SrcMgr) {
+ const auto &DISM = cast<DiagnosticInfoSrcMgr>(DI);
+ const SMDiagnostic &SMD = DISM.getSMDiag();
+
+ if (SMD.getKind() == SourceMgr::DK_Error)
+ *HasError = true;
+
+ SMD.print(nullptr, errs());
+
+ // For testing purposes, we print the LocCookie here.
+ if (DISM.isInlineAsmDiag() && DISM.getLocCookie())
+ WithColor::note() << "!srcloc = " << DISM.getLocCookie() << "\n";
+
+ return true;
+ }
+
+ if (DI.getSeverity() == DS_Error)
+ *HasError = true;
+
+ if (auto *Remark = dyn_cast<DiagnosticInfoOptimizationBase>(&DI))
+ if (!Remark->isEnabled())
+ return true;
+
+ DiagnosticPrinterRawOStream DP(errs());
+ errs() << LLVMContext::getDiagnosticMessagePrefix(DI.getSeverity()) << ": ";
+ DI.print(DP);
+ errs() << "\n";
+ return true;
+ }
+};
+
+// main - Entry point for the llc compiler.
+//
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ // Enable debug stream buffering.
+ EnableDebugBuffering = true;
+
+ // Initialize targets first, so that --version shows registered targets.
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmPrinters();
+ InitializeAllAsmParsers();
+
+ // Initialize codegen and IR passes used by llc so that the -print-after,
+ // -print-before, and -stop-after options work.
+ PassRegistry *Registry = PassRegistry::getPassRegistry();
+ initializeCore(*Registry);
+ initializeCodeGen(*Registry);
+ initializeLoopStrengthReducePass(*Registry);
+ initializeLowerIntrinsicsPass(*Registry);
+ initializeEntryExitInstrumenterPass(*Registry);
+ initializePostInlineEntryExitInstrumenterPass(*Registry);
+ initializeUnreachableBlockElimLegacyPassPass(*Registry);
+ initializeConstantHoistingLegacyPassPass(*Registry);
+ initializeScalarOpts(*Registry);
+ initializeVectorization(*Registry);
+ initializeScalarizeMaskedMemIntrinLegacyPassPass(*Registry);
+ initializeExpandReductionsPass(*Registry);
+ initializeExpandVectorPredicationPass(*Registry);
+ initializeHardwareLoopsPass(*Registry);
+ initializeTransformUtils(*Registry);
+ initializeReplaceWithVeclibLegacyPass(*Registry);
+
+ // Initialize debugging passes.
+ initializeScavengerTestPass(*Registry);
+
+ // Register the target printer for --version.
+ cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
+
+ cl::ParseCommandLineOptions(argc, argv, "llvm system compiler\n");
+
+ if (TimeTrace)
+ timeTraceProfilerInitialize(TimeTraceGranularity, argv[0]);
+ auto TimeTraceScopeExit = make_scope_exit([]() {
+ if (TimeTrace) {
+ if (auto E = timeTraceProfilerWrite(TimeTraceFile, OutputFilename)) {
+ handleAllErrors(std::move(E), [&](const StringError &SE) {
+ errs() << SE.getMessage() << "\n";
+ });
+ return;
+ }
+ timeTraceProfilerCleanup();
+ }
+ });
+
+ LLVMContext Context;
+ Context.setDiscardValueNames(DiscardValueNames);
+
+ // Set a diagnostic handler that doesn't exit on the first error
+ bool HasError = false;
+ Context.setDiagnosticHandler(
+ std::make_unique<LLCDiagnosticHandler>(&HasError));
+
+ Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr =
+ setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses,
+ RemarksFormat, RemarksWithHotness,
+ RemarksHotnessThreshold);
+ if (Error E = RemarksFileOrErr.takeError())
+ reportError(std::move(E), RemarksFilename);
+ std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr);
+
+ if (InputLanguage != "" && InputLanguage != "ir" && InputLanguage != "mir")
+ reportError("input language must be '', 'IR' or 'MIR'");
+
+ // Compile the module TimeCompilations times to give better compile time
+ // metrics.
+ for (unsigned I = TimeCompilations; I; --I)
+ if (int RetVal = compileModule(argv, Context))
+ return RetVal;
+
+ if (RemarksFile)
+ RemarksFile->keep();
+ return 0;
+}
+
+static bool addPass(PassManagerBase &PM, const char *argv0,
+ StringRef PassName, TargetPassConfig &TPC) {
+ if (PassName == "none")
+ return false;
+
+ const PassRegistry *PR = PassRegistry::getPassRegistry();
+ const PassInfo *PI = PR->getPassInfo(PassName);
+ if (!PI) {
+ WithColor::error(errs(), argv0)
+ << "run-pass " << PassName << " is not registered.\n";
+ return true;
+ }
+
+ Pass *P;
+ if (PI->getNormalCtor())
+ P = PI->getNormalCtor()();
+ else {
+ WithColor::error(errs(), argv0)
+ << "cannot create pass: " << PI->getPassName() << "\n";
+ return true;
+ }
+ std::string Banner = std::string("After ") + std::string(P->getPassName());
+ TPC.addMachinePrePasses();
+ PM.add(P);
+ TPC.addMachinePostPasses(Banner);
+
+ return false;
+}
+
+static int compileModule(char **argv, LLVMContext &Context) {
+ // Load the module to be compiled...
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M;
+ std::unique_ptr<MIRParser> MIR;
+ Triple TheTriple;
+ std::string CPUStr = codegen::getCPUStr(),
+ FeaturesStr = codegen::getFeaturesStr();
+
+ // Set attributes on functions as loaded from MIR from command line arguments.
+ auto setMIRFunctionAttributes = [&CPUStr, &FeaturesStr](Function &F) {
+ codegen::setFunctionAttributes(CPUStr, FeaturesStr, F);
+ };
+
+ auto MAttrs = codegen::getMAttrs();
+ bool SkipModule = codegen::getMCPU() == "help" ||
+ (!MAttrs.empty() && MAttrs.front() == "help");
+
+ CodeGenOpt::Level OLvl = CodeGenOpt::Default;
+ switch (OptLevel) {
+ default:
+ WithColor::error(errs(), argv[0]) << "invalid optimization level.\n";
+ return 1;
+ case ' ': break;
+ case '0': OLvl = CodeGenOpt::None; break;
+ case '1': OLvl = CodeGenOpt::Less; break;
+ case '2': OLvl = CodeGenOpt::Default; break;
+ case '3': OLvl = CodeGenOpt::Aggressive; break;
+ }
+
+ // Parse 'none' or '$major.$minor'. Disallow -binutils-version=0 because we
+ // use that to indicate the MC default.
+ if (!BinutilsVersion.empty() && BinutilsVersion != "none") {
+ StringRef V = BinutilsVersion.getValue();
+ unsigned Num;
+ if (V.consumeInteger(10, Num) || Num == 0 ||
+ !(V.empty() ||
+ (V.consume_front(".") && !V.consumeInteger(10, Num) && V.empty()))) {
+ WithColor::error(errs(), argv[0])
+ << "invalid -binutils-version, accepting 'none' or major.minor\n";
+ return 1;
+ }
+ }
+ TargetOptions Options;
+ auto InitializeOptions = [&](const Triple &TheTriple) {
+ Options = codegen::InitTargetOptionsFromCodeGenFlags(TheTriple);
+ Options.BinutilsVersion =
+ TargetMachine::parseBinutilsVersion(BinutilsVersion);
+ Options.DisableIntegratedAS = NoIntegratedAssembler;
+ Options.MCOptions.ShowMCEncoding = ShowMCEncoding;
+ Options.MCOptions.MCUseDwarfDirectory = DwarfDirectory;
+ Options.MCOptions.AsmVerbose = AsmVerbose;
+ Options.MCOptions.PreserveAsmComments = PreserveComments;
+ Options.MCOptions.IASSearchPaths = IncludeDirs;
+ Options.MCOptions.SplitDwarfFile = SplitDwarfFile;
+ };
+
+ Optional<Reloc::Model> RM = codegen::getExplicitRelocModel();
+
+ const Target *TheTarget = nullptr;
+ std::unique_ptr<TargetMachine> Target;
+
+ // If user just wants to list available options, skip module loading
+ if (!SkipModule) {
+ auto SetDataLayout =
+ [&](StringRef DataLayoutTargetTriple) -> Optional<std::string> {
+ // If we are supposed to override the target triple, do so now.
+ std::string IRTargetTriple = DataLayoutTargetTriple.str();
+ if (!TargetTriple.empty())
+ IRTargetTriple = Triple::normalize(TargetTriple);
+ TheTriple = Triple(IRTargetTriple);
+ if (TheTriple.getTriple().empty())
+ TheTriple.setTriple(sys::getDefaultTargetTriple());
+
+ std::string Error;
+ TheTarget =
+ TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error);
+ if (!TheTarget) {
+ WithColor::error(errs(), argv[0]) << Error;
+ exit(1);
+ }
+
+ // On AIX, setting the relocation model to anything other than PIC is
+ // considered a user error.
+ if (TheTriple.isOSAIX() && RM.hasValue() && *RM != Reloc::PIC_)
+ reportError("invalid relocation model, AIX only supports PIC",
+ InputFilename);
+
+ InitializeOptions(TheTriple);
+ Target = std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
+ TheTriple.getTriple(), CPUStr, FeaturesStr, Options, RM,
+ codegen::getExplicitCodeModel(), OLvl));
+ assert(Target && "Could not allocate target machine!");
+
+ return Target->createDataLayout().getStringRepresentation();
+ };
+ if (InputLanguage == "mir" ||
+ (InputLanguage == "" && StringRef(InputFilename).endswith(".mir"))) {
+ MIR = createMIRParserFromFile(InputFilename, Err, Context,
+ setMIRFunctionAttributes);
+ if (MIR)
+ M = MIR->parseIRModule(SetDataLayout);
+ } else {
+ M = parseIRFile(InputFilename, Err, Context, SetDataLayout);
+ }
+ if (!M) {
+ Err.print(argv[0], WithColor::error(errs(), argv[0]));
+ return 1;
+ }
+ if (!TargetTriple.empty())
+ M->setTargetTriple(Triple::normalize(TargetTriple));
+ } else {
+ TheTriple = Triple(Triple::normalize(TargetTriple));
+ if (TheTriple.getTriple().empty())
+ TheTriple.setTriple(sys::getDefaultTargetTriple());
+
+ // Get the target specific parser.
+ std::string Error;
+ TheTarget =
+ TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error);
+ if (!TheTarget) {
+ WithColor::error(errs(), argv[0]) << Error;
+ return 1;
+ }
+
+ // On AIX, setting the relocation model to anything other than PIC is
+ // considered a user error.
+ if (TheTriple.isOSAIX() && RM.hasValue() && *RM != Reloc::PIC_) {
+ WithColor::error(errs(), argv[0])
+ << "invalid relocation model, AIX only supports PIC.\n";
+ return 1;
+ }
+
+ InitializeOptions(TheTriple);
+ Target = std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
+ TheTriple.getTriple(), CPUStr, FeaturesStr, Options, RM,
+ codegen::getExplicitCodeModel(), OLvl));
+ assert(Target && "Could not allocate target machine!");
+
+ // If we don't have a module then just exit now. We do this down
+ // here since the CPU/Feature help is underneath the target machine
+ // creation.
+ return 0;
+ }
+
+ assert(M && "Should have exited if we didn't have a module!");
+ if (codegen::getFloatABIForCalls() != FloatABI::Default)
+ Options.FloatABIType = codegen::getFloatABIForCalls();
+
+ // Figure out where we are going to send the output.
+ std::unique_ptr<ToolOutputFile> Out =
+ GetOutputStream(TheTarget->getName(), TheTriple.getOS(), argv[0]);
+ if (!Out) return 1;
+
+ // Ensure the filename is passed down to CodeViewDebug.
+ Target->Options.ObjectFilenameForDebug = Out->outputFilename();
+
+ std::unique_ptr<ToolOutputFile> DwoOut;
+ if (!SplitDwarfOutputFile.empty()) {
+ std::error_code EC;
+ DwoOut = std::make_unique<ToolOutputFile>(SplitDwarfOutputFile, EC,
+ sys::fs::OF_None);
+ if (EC)
+ reportError(EC.message(), SplitDwarfOutputFile);
+ }
+
+ // Build up all of the passes that we want to do to the module.
+ legacy::PassManager PM;
+
+ // Add an appropriate TargetLibraryInfo pass for the module's triple.
+ TargetLibraryInfoImpl TLII(Triple(M->getTargetTriple()));
+
+ // The -disable-simplify-libcalls flag actually disables all builtin optzns.
+ if (DisableSimplifyLibCalls)
+ TLII.disableAllFunctions();
+ PM.add(new TargetLibraryInfoWrapperPass(TLII));
+
+ // Verify module immediately to catch problems before doInitialization() is
+ // called on any passes.
+ if (!NoVerify && verifyModule(*M, &errs()))
+ reportError("input module cannot be verified", InputFilename);
+
+ // Override function attributes based on CPUStr, FeaturesStr, and command line
+ // flags.
+ codegen::setFunctionAttributes(CPUStr, FeaturesStr, *M);
+
+ if (mc::getExplicitRelaxAll() && codegen::getFileType() != CGFT_ObjectFile)
+ WithColor::warning(errs(), argv[0])
+ << ": warning: ignoring -mc-relax-all because filetype != obj";
+
+ {
+ raw_pwrite_stream *OS = &Out->os();
+
+ // Manually do the buffering rather than using buffer_ostream,
+ // so we can memcmp the contents in CompileTwice mode
+ SmallVector<char, 0> Buffer;
+ std::unique_ptr<raw_svector_ostream> BOS;
+ if ((codegen::getFileType() != CGFT_AssemblyFile &&
+ !Out->os().supportsSeeking()) ||
+ CompileTwice) {
+ BOS = std::make_unique<raw_svector_ostream>(Buffer);
+ OS = BOS.get();
+ }
+
+ const char *argv0 = argv[0];
+ LLVMTargetMachine &LLVMTM = static_cast<LLVMTargetMachine &>(*Target);
+ MachineModuleInfoWrapperPass *MMIWP =
+ new MachineModuleInfoWrapperPass(&LLVMTM);
+
+ // Construct a custom pass pipeline that starts after instruction
+ // selection.
+ if (!RunPassNames->empty()) {
+ if (!MIR) {
+ WithColor::warning(errs(), argv[0])
+ << "run-pass is for .mir file only.\n";
+ return 1;
+ }
+ TargetPassConfig &TPC = *LLVMTM.createPassConfig(PM);
+ if (TPC.hasLimitedCodeGenPipeline()) {
+ WithColor::warning(errs(), argv[0])
+ << "run-pass cannot be used with "
+ << TPC.getLimitedCodeGenPipelineReason(" and ") << ".\n";
+ return 1;
+ }
+
+ TPC.setDisableVerify(NoVerify);
+ PM.add(&TPC);
+ PM.add(MMIWP);
+ TPC.printAndVerify("");
+ for (const std::string &RunPassName : *RunPassNames) {
+ if (addPass(PM, argv0, RunPassName, TPC))
+ return 1;
+ }
+ TPC.setInitialized();
+ PM.add(createPrintMIRPass(*OS));
+ PM.add(createFreeMachineFunctionPass());
+ } else if (Target->addPassesToEmitFile(
+ PM, *OS, DwoOut ? &DwoOut->os() : nullptr,
+ codegen::getFileType(), NoVerify, MMIWP)) {
+ reportError("target does not support generation of this file type");
+ }
+
+ const_cast<TargetLoweringObjectFile *>(LLVMTM.getObjFileLowering())
+ ->Initialize(MMIWP->getMMI().getContext(), *Target);
+ if (MIR) {
+ assert(MMIWP && "Forgot to create MMIWP?");
+ if (MIR->parseMachineFunctions(*M, MMIWP->getMMI()))
+ return 1;
+ }
+
+ // Before executing passes, print the final values of the LLVM options.
+ cl::PrintOptionValues();
+
+ // If requested, run the pass manager over the same module again,
+ // to catch any bugs due to persistent state in the passes. Note that
+ // opt has the same functionality, so it may be worth abstracting this out
+ // in the future.
+ SmallVector<char, 0> CompileTwiceBuffer;
+ if (CompileTwice) {
+ std::unique_ptr<Module> M2(llvm::CloneModule(*M));
+ PM.run(*M2);
+ CompileTwiceBuffer = Buffer;
+ Buffer.clear();
+ }
+
+ PM.run(*M);
+
+ auto HasError =
+ ((const LLCDiagnosticHandler *)(Context.getDiagHandlerPtr()))->HasError;
+ if (*HasError)
+ return 1;
+
+ // Compare the two outputs and make sure they're the same
+ if (CompileTwice) {
+ if (Buffer.size() != CompileTwiceBuffer.size() ||
+ (memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) !=
+ 0)) {
+ errs()
+ << "Running the pass manager twice changed the output.\n"
+ "Writing the result of the second run to the specified output\n"
+ "To generate the one-run comparison binary, just run without\n"
+ "the compile-twice option\n";
+ Out->os() << Buffer;
+ Out->keep();
+ return 1;
+ }
+ }
+
+ if (BOS) {
+ Out->os() << Buffer;
+ }
+ }
+
+ // Declare success.
+ Out->keep();
+ if (DwoOut)
+ DwoOut->keep();
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llc/ya.make b/contrib/libs/llvm14/tools/llc/ya.make
new file mode 100644
index 00000000000..2d52c3aa0ee
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llc/ya.make
@@ -0,0 +1,86 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/AsmPrinter
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/CodeGen/MIRParser
+ contrib/libs/llvm14/lib/CodeGen/SelectionDAG
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/CFGuard
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llc
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llc.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/lli/ChildTarget/ChildTarget.cpp b/contrib/libs/llvm14/tools/lli/ChildTarget/ChildTarget.cpp
new file mode 100644
index 00000000000..cf1b03a141c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/lli/ChildTarget/ChildTarget.cpp
@@ -0,0 +1,76 @@
+//===----------- ChildTarget.cpp - Out-of-proc executor for lli -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Simple out-of-process executor for lli.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstring>
+#include <sstream>
+
+using namespace llvm;
+using namespace llvm::orc;
+
+ExitOnError ExitOnErr;
+
+int main(int argc, char *argv[]) {
+#if LLVM_ENABLE_THREADS
+
+ if (argc != 3) {
+ errs() << "Usage: " << argv[0] << " <input fd> <output fd>\n";
+ return 1;
+ }
+
+ if (sys::DynamicLibrary::LoadLibraryPermanently(nullptr)) {
+ errs() << "Error loading program symbols.\n";
+ return 1;
+ }
+
+ ExitOnErr.setBanner(std::string(argv[0]) + ": ");
+
+ int InFD = 0;
+ int OutFD = 0;
+ {
+ std::istringstream InFDStream(argv[1]), OutFDStream(argv[2]);
+ InFDStream >> InFD;
+ OutFDStream >> OutFD;
+ }
+
+ auto Server =
+ ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>(
+ [](SimpleRemoteEPCServer::Setup &S) -> Error {
+ S.setDispatcher(
+ std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>());
+ S.bootstrapSymbols() =
+ SimpleRemoteEPCServer::defaultBootstrapSymbols();
+ S.services().push_back(
+ std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>());
+ return Error::success();
+ },
+ InFD, OutFD));
+
+ ExitOnErr(Server->waitForDisconnect());
+
+ return 0;
+
+#else
+ errs() << argv[0]
+ << " error: this tool requires threads, but LLVM was "
+ "built with LLVM_ENABLE_THREADS=Off\n";
+ return 1;
+#endif
+}
diff --git a/contrib/libs/llvm14/tools/lli/ChildTarget/ya.make b/contrib/libs/llvm14/tools/lli/ChildTarget/ya.make
new file mode 100644
index 00000000000..383b33fd38c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/lli/ChildTarget/ya.make
@@ -0,0 +1,30 @@
+# Generated by devtools/yamaker.
+
+PROGRAM(lli-child-target)
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc/Shared
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc/TargetProcess
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/lli/ChildTarget
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ ChildTarget.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/lli/ExecutionUtils.cpp b/contrib/libs/llvm14/tools/lli/ExecutionUtils.cpp
new file mode 100644
index 00000000000..55370ed40f2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/lli/ExecutionUtils.cpp
@@ -0,0 +1,146 @@
+//===---- ExecutionUtils.cpp - Utilities for executing functions in lli ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExecutionUtils.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <cstdint>
+#include <vector>
+
+// Declarations follow the GDB JIT interface (version 1, 2009) and must match
+// those of the DYLD used for testing. See:
+//
+// llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp
+// llvm/lib/ExecutionEngine/GDBRegistrationListener.cpp
+//
+typedef enum {
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+} jit_actions_t;
+
+struct jit_code_entry {
+ struct jit_code_entry *next_entry;
+ struct jit_code_entry *prev_entry;
+ const char *symfile_addr;
+ uint64_t symfile_size;
+};
+
+struct jit_descriptor {
+ uint32_t version;
+ // This should be jit_actions_t, but we want to be specific about the
+ // bit-width.
+ uint32_t action_flag;
+ struct jit_code_entry *relevant_entry;
+ struct jit_code_entry *first_entry;
+};
+
+namespace llvm {
+
+template <typename... Ts> static void outsv(const char *Fmt, Ts &&...Vals) {
+ outs() << formatv(Fmt, Vals...);
+}
+
+static const char *actionFlagToStr(uint32_t ActionFlag) {
+ switch (ActionFlag) {
+ case JIT_NOACTION:
+ return "JIT_NOACTION";
+ case JIT_REGISTER_FN:
+ return "JIT_REGISTER_FN";
+ case JIT_UNREGISTER_FN:
+ return "JIT_UNREGISTER_FN";
+ }
+ return "<invalid action_flag>";
+}
+
+// Sample output:
+//
+// Reading __jit_debug_descriptor at 0x0000000000404048
+//
+// Version: 0
+// Action: JIT_REGISTER_FN
+//
+// Entry Symbol File Size Previous Entry
+// [ 0] 0x0000000000451290 0x0000000000002000 200 0x0000000000000000
+// [ 1] 0x0000000000451260 0x0000000000001000 100 0x0000000000451290
+// ...
+//
+static void dumpDebugDescriptor(void *Addr) {
+ outsv("Reading __jit_debug_descriptor at {0}\n\n", Addr);
+
+ jit_descriptor *Descriptor = reinterpret_cast<jit_descriptor *>(Addr);
+ outsv("Version: {0}\n", Descriptor->version);
+ outsv("Action: {0}\n\n", actionFlagToStr(Descriptor->action_flag));
+ outsv("{0,11} {1,24} {2,15} {3,14}\n", "Entry", "Symbol File", "Size",
+ "Previous Entry");
+
+ unsigned Idx = 0;
+ for (auto *Entry = Descriptor->first_entry; Entry; Entry = Entry->next_entry)
+ outsv("[{0,2}] {1:X16} {2:X16} {3,8:D} {4}\n", Idx++, Entry,
+ reinterpret_cast<const void *>(Entry->symfile_addr),
+ Entry->symfile_size, Entry->prev_entry);
+}
+
+static LLIBuiltinFunctionGenerator *Generator = nullptr;
+
+static void dumpDebugObjects(void *Addr) {
+ jit_descriptor *Descriptor = reinterpret_cast<jit_descriptor *>(Addr);
+ for (auto *Entry = Descriptor->first_entry; Entry; Entry = Entry->next_entry)
+ Generator->appendDebugObject(Entry->symfile_addr, Entry->symfile_size);
+}
+
+LLIBuiltinFunctionGenerator::LLIBuiltinFunctionGenerator(
+ std::vector<BuiltinFunctionKind> Enabled, orc::MangleAndInterner &Mangle)
+ : TestOut(nullptr) {
+ Generator = this;
+ for (BuiltinFunctionKind F : Enabled) {
+ switch (F) {
+ case BuiltinFunctionKind::DumpDebugDescriptor:
+ expose(Mangle("__dump_jit_debug_descriptor"), &dumpDebugDescriptor);
+ break;
+ case BuiltinFunctionKind::DumpDebugObjects:
+ expose(Mangle("__dump_jit_debug_objects"), &dumpDebugObjects);
+ TestOut = createToolOutput();
+ break;
+ }
+ }
+}
+
+Error LLIBuiltinFunctionGenerator::tryToGenerate(
+ orc::LookupState &LS, orc::LookupKind K, orc::JITDylib &JD,
+ orc::JITDylibLookupFlags JDLookupFlags,
+ const orc::SymbolLookupSet &Symbols) {
+ orc::SymbolMap NewSymbols;
+ for (const auto &NameFlags : Symbols) {
+ auto It = BuiltinFunctions.find(NameFlags.first);
+ if (It != BuiltinFunctions.end())
+ NewSymbols.insert(*It);
+ }
+
+ if (NewSymbols.empty())
+ return Error::success();
+
+ return JD.define(absoluteSymbols(std::move(NewSymbols)));
+}
+
+// static
+std::unique_ptr<ToolOutputFile>
+LLIBuiltinFunctionGenerator::createToolOutput() {
+ std::error_code EC;
+ auto TestOut = std::make_unique<ToolOutputFile>("-", EC, sys::fs::OF_None);
+ if (EC) {
+ errs() << "Error creating tool output file: " << EC.message() << '\n';
+ exit(1);
+ }
+ return TestOut;
+}
+
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/lli/ExecutionUtils.h b/contrib/libs/llvm14/tools/lli/ExecutionUtils.h
new file mode 100644
index 00000000000..fcd1db05cca
--- /dev/null
+++ b/contrib/libs/llvm14/tools/lli/ExecutionUtils.h
@@ -0,0 +1,60 @@
+//===- ExecutionUtils.h - Utilities for executing code in lli ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Contains utilities for executing code in lli.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLI_EXECUTIONUTILS_H
+#define LLVM_TOOLS_LLI_EXECUTIONUTILS_H
+
+#include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/Mangling.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+#include <memory>
+#include <utility>
+
+namespace llvm {
+
+enum class BuiltinFunctionKind {
+ DumpDebugDescriptor,
+ DumpDebugObjects,
+};
+
+// Utility class to expose symbols for special-purpose functions to the JIT.
+class LLIBuiltinFunctionGenerator : public orc::DefinitionGenerator {
+public:
+ LLIBuiltinFunctionGenerator(std::vector<BuiltinFunctionKind> Enabled,
+ orc::MangleAndInterner &Mangle);
+
+ Error tryToGenerate(orc::LookupState &LS, orc::LookupKind K,
+ orc::JITDylib &JD, orc::JITDylibLookupFlags JDLookupFlags,
+ const orc::SymbolLookupSet &Symbols) override;
+
+ void appendDebugObject(const char *Addr, size_t Size) {
+ TestOut->os().write(Addr, Size);
+ }
+
+private:
+ orc::SymbolMap BuiltinFunctions;
+ std::unique_ptr<ToolOutputFile> TestOut;
+
+ template <typename T> void expose(orc::SymbolStringPtr Name, T *Handler) {
+ BuiltinFunctions[Name] = JITEvaluatedSymbol(
+ pointerToJITTargetAddress(Handler), JITSymbolFlags::Exported);
+ }
+
+ static std::unique_ptr<ToolOutputFile> createToolOutput();
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLI_EXECUTIONUTILS_H
diff --git a/contrib/libs/llvm14/tools/lli/ForwardingMemoryManager.h b/contrib/libs/llvm14/tools/lli/ForwardingMemoryManager.h
new file mode 100644
index 00000000000..99a545e60de
--- /dev/null
+++ b/contrib/libs/llvm14/tools/lli/ForwardingMemoryManager.h
@@ -0,0 +1,131 @@
+//===-- RemoteJITUtils.h - Utilities for remote-JITing with LLI -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Utilities for remote-JITing with LLI.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLI_FORWARDINGMEMORYMANAGER_H
+#define LLVM_TOOLS_LLI_FORWARDINGMEMORYMANAGER_H
+
+#include "llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h"
+#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
+
+namespace llvm {
+
+// ForwardingMM - Adapter to connect MCJIT to Orc's Remote
+// memory manager.
+class ForwardingMemoryManager : public llvm::RTDyldMemoryManager {
+public:
+ void setMemMgr(std::unique_ptr<RuntimeDyld::MemoryManager> MemMgr) {
+ this->MemMgr = std::move(MemMgr);
+ }
+
+ void setResolver(std::shared_ptr<LegacyJITSymbolResolver> Resolver) {
+ this->Resolver = std::move(Resolver);
+ }
+
+ uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
+ unsigned SectionID,
+ StringRef SectionName) override {
+ return MemMgr->allocateCodeSection(Size, Alignment, SectionID, SectionName);
+ }
+
+ uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
+ unsigned SectionID, StringRef SectionName,
+ bool IsReadOnly) override {
+ return MemMgr->allocateDataSection(Size, Alignment, SectionID, SectionName,
+ IsReadOnly);
+ }
+
+ void reserveAllocationSpace(uintptr_t CodeSize, uint32_t CodeAlign,
+ uintptr_t RODataSize, uint32_t RODataAlign,
+ uintptr_t RWDataSize,
+ uint32_t RWDataAlign) override {
+ MemMgr->reserveAllocationSpace(CodeSize, CodeAlign, RODataSize, RODataAlign,
+ RWDataSize, RWDataAlign);
+ }
+
+ bool needsToReserveAllocationSpace() override {
+ return MemMgr->needsToReserveAllocationSpace();
+ }
+
+ void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr,
+ size_t Size) override {
+ MemMgr->registerEHFrames(Addr, LoadAddr, Size);
+ }
+
+ void deregisterEHFrames() override { MemMgr->deregisterEHFrames(); }
+
+ bool finalizeMemory(std::string *ErrMsg = nullptr) override {
+ return MemMgr->finalizeMemory(ErrMsg);
+ }
+
+ void notifyObjectLoaded(RuntimeDyld &RTDyld,
+ const object::ObjectFile &Obj) override {
+ MemMgr->notifyObjectLoaded(RTDyld, Obj);
+ }
+
+ // Don't hide the sibling notifyObjectLoaded from RTDyldMemoryManager.
+ using RTDyldMemoryManager::notifyObjectLoaded;
+
+ JITSymbol findSymbol(const std::string &Name) override {
+ return Resolver->findSymbol(Name);
+ }
+
+ JITSymbol findSymbolInLogicalDylib(const std::string &Name) override {
+ return Resolver->findSymbolInLogicalDylib(Name);
+ }
+
+private:
+ std::unique_ptr<RuntimeDyld::MemoryManager> MemMgr;
+ std::shared_ptr<LegacyJITSymbolResolver> Resolver;
+};
+
+class RemoteResolver : public LegacyJITSymbolResolver {
+public:
+ static Expected<std::unique_ptr<RemoteResolver>>
+ Create(orc::ExecutorProcessControl &EPC) {
+ auto DylibMgr =
+ orc::EPCGenericDylibManager::CreateWithDefaultBootstrapSymbols(EPC);
+ if (!DylibMgr)
+ return DylibMgr.takeError();
+ auto H = DylibMgr->open("", 0);
+ if (!H)
+ return H.takeError();
+ return std::unique_ptr<RemoteResolver>(
+ new RemoteResolver(std::move(*DylibMgr), std::move(*H)));
+ }
+
+ JITSymbol findSymbol(const std::string &Name) override {
+ orc::RemoteSymbolLookupSet R;
+ R.push_back({std::move(Name), false});
+ if (auto Addrs = DylibMgr.lookup(H, R)) {
+ if (Addrs->size() != 1)
+ return make_error<StringError>("Unexpected remote lookup result",
+ inconvertibleErrorCode());
+ return JITSymbol(Addrs->front().getValue(), JITSymbolFlags::Exported);
+ } else
+ return Addrs.takeError();
+ }
+
+ JITSymbol findSymbolInLogicalDylib(const std::string &Name) override {
+ return nullptr;
+ }
+
+public:
+ RemoteResolver(orc::EPCGenericDylibManager DylibMgr,
+ orc::tpctypes::DylibHandle H)
+ : DylibMgr(std::move(DylibMgr)), H(std::move(H)) {}
+
+ orc::EPCGenericDylibManager DylibMgr;
+ orc::tpctypes::DylibHandle H;
+};
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLI_FORWARDINGMEMORYMANAGER_H
diff --git a/contrib/libs/llvm14/tools/lli/lli.cpp b/contrib/libs/llvm14/tools/lli/lli.cpp
new file mode 100644
index 00000000000..d20daa07196
--- /dev/null
+++ b/contrib/libs/llvm14/tools/lli/lli.cpp
@@ -0,0 +1,1155 @@
+//===- lli.cpp - LLVM Interpreter / Dynamic compiler ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This utility provides a simple wrapper around the LLVM Execution Engines,
+// which allow the direct execution of LLVM programs through a Just-In-Time
+// compiler, or through an interpreter if no JIT is available for this platform.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExecutionUtils.h"
+#include "ForwardingMemoryManager.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/CodeGen/LinkAllCodegenComponents.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/ExecutionEngine/GenericValue.h"
+#include "llvm/ExecutionEngine/Interpreter.h"
+#include "llvm/ExecutionEngine/JITEventListener.h"
+#include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/ExecutionEngine/MCJIT.h"
+#include "llvm/ExecutionEngine/ObjectCache.h"
+#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
+#include "llvm/ExecutionEngine/Orc/DebugUtils.h"
+#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
+#include "llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h"
+#include "llvm/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.h"
+#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
+#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
+#include "llvm/ExecutionEngine/Orc/LLJIT.h"
+#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
+#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
+#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h"
+#include "llvm/ExecutionEngine/SectionMemoryManager.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/Memory.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PluginLoader.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Instrumentation.h"
+#include <cerrno>
+
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
+#ifdef __CYGWIN__
+#include <cygwin/version.h>
+#if defined(CYGWIN_VERSION_DLL_MAJOR) && CYGWIN_VERSION_DLL_MAJOR<1007
+#define DO_NOTHING_ATEXIT 1
+#endif
+#endif
+
+using namespace llvm;
+
+static codegen::RegisterCodeGenFlags CGF;
+
+#define DEBUG_TYPE "lli"
+
+namespace {
+
+ enum class JITKind { MCJIT, Orc, OrcLazy };
+ enum class JITLinkerKind { Default, RuntimeDyld, JITLink };
+
+ cl::opt<std::string>
+ InputFile(cl::desc("<input bitcode>"), cl::Positional, cl::init("-"));
+
+ cl::list<std::string>
+ InputArgv(cl::ConsumeAfter, cl::desc("<program arguments>..."));
+
+ cl::opt<bool> ForceInterpreter("force-interpreter",
+ cl::desc("Force interpretation: disable JIT"),
+ cl::init(false));
+
+ cl::opt<JITKind> UseJITKind(
+ "jit-kind", cl::desc("Choose underlying JIT kind."),
+ cl::init(JITKind::Orc),
+ cl::values(clEnumValN(JITKind::MCJIT, "mcjit", "MCJIT"),
+ clEnumValN(JITKind::Orc, "orc", "Orc JIT"),
+ clEnumValN(JITKind::OrcLazy, "orc-lazy",
+ "Orc-based lazy JIT.")));
+
+ cl::opt<JITLinkerKind>
+ JITLinker("jit-linker", cl::desc("Choose the dynamic linker/loader."),
+ cl::init(JITLinkerKind::Default),
+ cl::values(clEnumValN(JITLinkerKind::Default, "default",
+ "Default for platform and JIT-kind"),
+ clEnumValN(JITLinkerKind::RuntimeDyld, "rtdyld",
+ "RuntimeDyld"),
+ clEnumValN(JITLinkerKind::JITLink, "jitlink",
+ "Orc-specific linker")));
+
+ 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."));
+
+ cl::list<std::string>
+ Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"),
+ cl::ZeroOrMore);
+
+ // 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.
+ cl::opt<bool> RemoteMCJIT("remote-mcjit",
+ cl::desc("Execute MCJIT'ed code in a separate process."),
+ cl::init(false));
+
+ // Manually specify the child process for remote execution. This overrides
+ // the simulated remote execution that allocates address space for child
+ // execution. The child process will be executed and will communicate with
+ // lli via stdin/stdout pipes.
+ cl::opt<std::string>
+ ChildExecPath("mcjit-remote-process",
+ cl::desc("Specify the filename of the process to launch "
+ "for remote MCJIT execution. If none is specified,"
+ "\n\tremote execution will be simulated in-process."),
+ cl::value_desc("filename"), cl::init(""));
+
+ // Determine optimization level.
+ cl::opt<char>
+ OptLevel("O",
+ cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
+ "(default = '-O2')"),
+ cl::Prefix,
+ cl::ZeroOrMore,
+ cl::init(' '));
+
+ cl::opt<std::string>
+ TargetTriple("mtriple", cl::desc("Override target triple for module"));
+
+ cl::opt<std::string>
+ EntryFunc("entry-function",
+ cl::desc("Specify the entry function (default = 'main') "
+ "of the executable"),
+ cl::value_desc("function"),
+ cl::init("main"));
+
+ cl::list<std::string>
+ ExtraModules("extra-module",
+ cl::desc("Extra modules to be loaded"),
+ cl::value_desc("input bitcode"));
+
+ cl::list<std::string>
+ ExtraObjects("extra-object",
+ cl::desc("Extra object files to be loaded"),
+ cl::value_desc("input object"));
+
+ cl::list<std::string>
+ ExtraArchives("extra-archive",
+ cl::desc("Extra archive files to be loaded"),
+ cl::value_desc("input archive"));
+
+ cl::opt<bool>
+ EnableCacheManager("enable-cache-manager",
+ cl::desc("Use cache manager to save/load modules"),
+ cl::init(false));
+
+ cl::opt<std::string>
+ ObjectCacheDir("object-cache-dir",
+ cl::desc("Directory to store cached object files "
+ "(must be user writable)"),
+ cl::init(""));
+
+ cl::opt<std::string>
+ FakeArgv0("fake-argv0",
+ cl::desc("Override the 'argv[0]' value passed into the executing"
+ " program"), cl::value_desc("executable"));
+
+ cl::opt<bool>
+ DisableCoreFiles("disable-core-files", cl::Hidden,
+ cl::desc("Disable emission of core files if possible"));
+
+ cl::opt<bool>
+ NoLazyCompilation("disable-lazy-compilation",
+ cl::desc("Disable JIT lazy compilation"),
+ cl::init(false));
+
+ cl::opt<bool>
+ GenerateSoftFloatCalls("soft-float",
+ cl::desc("Generate software floating point library calls"),
+ cl::init(false));
+
+ cl::opt<bool> NoProcessSymbols(
+ "no-process-syms",
+ cl::desc("Do not resolve lli process symbols in JIT'd code"),
+ cl::init(false));
+
+ enum class LLJITPlatform { Inactive, DetectHost, GenericIR };
+
+ cl::opt<LLJITPlatform>
+ Platform("lljit-platform", cl::desc("Platform to use with LLJIT"),
+ cl::init(LLJITPlatform::DetectHost),
+ cl::values(clEnumValN(LLJITPlatform::DetectHost, "DetectHost",
+ "Select based on JIT target triple"),
+ clEnumValN(LLJITPlatform::GenericIR, "GenericIR",
+ "Use LLJITGenericIRPlatform"),
+ clEnumValN(LLJITPlatform::Inactive, "Inactive",
+ "Disable platform support explicitly")),
+ cl::Hidden);
+
+ enum class DumpKind {
+ NoDump,
+ DumpFuncsToStdOut,
+ DumpModsToStdOut,
+ DumpModsToDisk
+ };
+
+ cl::opt<DumpKind> OrcDumpKind(
+ "orc-lazy-debug", cl::desc("Debug dumping for the orc-lazy JIT."),
+ cl::init(DumpKind::NoDump),
+ cl::values(clEnumValN(DumpKind::NoDump, "no-dump",
+ "Don't dump anything."),
+ clEnumValN(DumpKind::DumpFuncsToStdOut, "funcs-to-stdout",
+ "Dump function names to stdout."),
+ clEnumValN(DumpKind::DumpModsToStdOut, "mods-to-stdout",
+ "Dump modules to stdout."),
+ clEnumValN(DumpKind::DumpModsToDisk, "mods-to-disk",
+ "Dump modules to the current "
+ "working directory. (WARNING: "
+ "will overwrite existing files).")),
+ cl::Hidden);
+
+ cl::list<BuiltinFunctionKind> GenerateBuiltinFunctions(
+ "generate",
+ cl::desc("Provide built-in functions for access by JITed code "
+ "(jit-kind=orc-lazy only)"),
+ cl::values(clEnumValN(BuiltinFunctionKind::DumpDebugDescriptor,
+ "__dump_jit_debug_descriptor",
+ "Dump __jit_debug_descriptor contents to stdout"),
+ clEnumValN(BuiltinFunctionKind::DumpDebugObjects,
+ "__dump_jit_debug_objects",
+ "Dump __jit_debug_descriptor in-memory debug "
+ "objects as tool output")),
+ cl::Hidden);
+
+ ExitOnError ExitOnErr;
+}
+
+LLVM_ATTRIBUTE_USED void linkComponents() {
+ errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper
+ << (void *)&llvm_orc_deregisterEHFrameSectionWrapper
+ << (void *)&llvm_orc_registerJITLoaderGDBWrapper;
+}
+
+//===----------------------------------------------------------------------===//
+// Object cache
+//
+// This object cache implementation writes cached objects to disk to the
+// directory specified by CacheDir, using a filename provided in the module
+// descriptor. The cache tries to load a saved object using that path if the
+// file exists. CacheDir defaults to "", in which case objects are cached
+// alongside their originating bitcodes.
+//
+class LLIObjectCache : public ObjectCache {
+public:
+ LLIObjectCache(const std::string& CacheDir) : CacheDir(CacheDir) {
+ // Add trailing '/' to cache dir if necessary.
+ if (!this->CacheDir.empty() &&
+ this->CacheDir[this->CacheDir.size() - 1] != '/')
+ this->CacheDir += '/';
+ }
+ ~LLIObjectCache() override {}
+
+ void notifyObjectCompiled(const Module *M, MemoryBufferRef Obj) override {
+ const std::string &ModuleID = M->getModuleIdentifier();
+ std::string CacheName;
+ if (!getCacheFilename(ModuleID, CacheName))
+ return;
+ if (!CacheDir.empty()) { // Create user-defined cache dir.
+ SmallString<128> dir(sys::path::parent_path(CacheName));
+ sys::fs::create_directories(Twine(dir));
+ }
+
+ std::error_code EC;
+ raw_fd_ostream outfile(CacheName, EC, sys::fs::OF_None);
+ outfile.write(Obj.getBufferStart(), Obj.getBufferSize());
+ outfile.close();
+ }
+
+ std::unique_ptr<MemoryBuffer> getObject(const Module* M) override {
+ const std::string &ModuleID = M->getModuleIdentifier();
+ std::string CacheName;
+ if (!getCacheFilename(ModuleID, CacheName))
+ return nullptr;
+ // Load the object from the cache filename
+ ErrorOr<std::unique_ptr<MemoryBuffer>> IRObjectBuffer =
+ MemoryBuffer::getFile(CacheName, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
+ // If the file isn't there, that's OK.
+ if (!IRObjectBuffer)
+ return nullptr;
+ // MCJIT will want to write into this buffer, and we don't want that
+ // because the file has probably just been mmapped. Instead we make
+ // a copy. The filed-based buffer will be released when it goes
+ // out of scope.
+ return MemoryBuffer::getMemBufferCopy(IRObjectBuffer.get()->getBuffer());
+ }
+
+private:
+ std::string CacheDir;
+
+ bool getCacheFilename(const std::string &ModID, std::string &CacheName) {
+ std::string Prefix("file:");
+ size_t PrefixLength = Prefix.length();
+ if (ModID.substr(0, PrefixLength) != Prefix)
+ return false;
+
+ std::string CacheSubdir = ModID.substr(PrefixLength);
+ // Transform "X:\foo" => "/X\foo" for convenience on Windows.
+ if (is_style_windows(llvm::sys::path::Style::native) &&
+ isalpha(CacheSubdir[0]) && CacheSubdir[1] == ':') {
+ CacheSubdir[1] = CacheSubdir[0];
+ CacheSubdir[0] = '/';
+ }
+
+ CacheName = CacheDir + CacheSubdir;
+ size_t pos = CacheName.rfind('.');
+ CacheName.replace(pos, CacheName.length() - pos, ".o");
+ return true;
+ }
+};
+
+// On Mingw and Cygwin, an external symbol named '__main' is called from the
+// generated 'main' function to allow static initialization. To avoid linking
+// problems with remote targets (because lli's remote target support does not
+// currently handle external linking) we add a secondary module which defines
+// an empty '__main' function.
+static void addCygMingExtraModule(ExecutionEngine &EE, LLVMContext &Context,
+ StringRef TargetTripleStr) {
+ IRBuilder<> Builder(Context);
+ Triple TargetTriple(TargetTripleStr);
+
+ // Create a new module.
+ std::unique_ptr<Module> M = std::make_unique<Module>("CygMingHelper", Context);
+ M->setTargetTriple(TargetTripleStr);
+
+ // Create an empty function named "__main".
+ Type *ReturnTy;
+ if (TargetTriple.isArch64Bit())
+ ReturnTy = Type::getInt64Ty(Context);
+ else
+ 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.
+ EE.addModule(std::move(M));
+}
+
+CodeGenOpt::Level getOptLevel() {
+ switch (OptLevel) {
+ default:
+ WithColor::error(errs(), "lli") << "invalid optimization level.\n";
+ exit(1);
+ case '0': return CodeGenOpt::None;
+ case '1': return CodeGenOpt::Less;
+ case ' ':
+ case '2': return CodeGenOpt::Default;
+ case '3': return CodeGenOpt::Aggressive;
+ }
+ llvm_unreachable("Unrecognized opt level.");
+}
+
+[[noreturn]] static void reportError(SMDiagnostic Err, const char *ProgName) {
+ Err.print(ProgName, errs());
+ exit(1);
+}
+
+Error loadDylibs();
+int runOrcJIT(const char *ProgName);
+void disallowOrcOptions();
+Expected<std::unique_ptr<orc::ExecutorProcessControl>> launchRemote();
+
+//===----------------------------------------------------------------------===//
+// main Driver function
+//
+int main(int argc, char **argv, char * const *envp) {
+ InitLLVM X(argc, argv);
+
+ if (argc > 1)
+ ExitOnErr.setBanner(std::string(argv[0]) + ": ");
+
+ // If we have a native target, initialize it to ensure it is linked in and
+ // usable by the JIT.
+ InitializeNativeTarget();
+ InitializeNativeTargetAsmPrinter();
+ InitializeNativeTargetAsmParser();
+
+ cl::ParseCommandLineOptions(argc, argv,
+ "llvm interpreter & dynamic compiler\n");
+
+ // If the user doesn't want core files, disable them.
+ if (DisableCoreFiles)
+ sys::Process::PreventCoreFiles();
+
+ ExitOnErr(loadDylibs());
+
+ if (UseJITKind == JITKind::MCJIT)
+ disallowOrcOptions();
+ else
+ return runOrcJIT(argv[0]);
+
+ // Old lli implementation based on ExecutionEngine and MCJIT.
+ LLVMContext Context;
+
+ // Load the bitcode...
+ SMDiagnostic Err;
+ std::unique_ptr<Module> Owner = parseIRFile(InputFile, Err, Context);
+ Module *Mod = Owner.get();
+ if (!Mod)
+ reportError(Err, argv[0]);
+
+ if (EnableCacheManager) {
+ std::string CacheName("file:");
+ CacheName.append(InputFile);
+ Mod->setModuleIdentifier(CacheName);
+ }
+
+ // If not jitting lazily, load the whole bitcode file eagerly too.
+ if (NoLazyCompilation) {
+ // Use *argv instead of argv[0] to work around a wrong GCC warning.
+ ExitOnError ExitOnErr(std::string(*argv) +
+ ": bitcode didn't read correctly: ");
+ ExitOnErr(Mod->materializeAll());
+ }
+
+ std::string ErrorMsg;
+ EngineBuilder builder(std::move(Owner));
+ builder.setMArch(codegen::getMArch());
+ builder.setMCPU(codegen::getCPUStr());
+ builder.setMAttrs(codegen::getFeatureList());
+ if (auto RM = codegen::getExplicitRelocModel())
+ builder.setRelocationModel(RM.getValue());
+ if (auto CM = codegen::getExplicitCodeModel())
+ builder.setCodeModel(CM.getValue());
+ builder.setErrorStr(&ErrorMsg);
+ builder.setEngineKind(ForceInterpreter
+ ? EngineKind::Interpreter
+ : EngineKind::JIT);
+
+ // If we are supposed to override the target triple, do so now.
+ if (!TargetTriple.empty())
+ Mod->setTargetTriple(Triple::normalize(TargetTriple));
+
+ // Enable MCJIT if desired.
+ RTDyldMemoryManager *RTDyldMM = nullptr;
+ if (!ForceInterpreter) {
+ if (RemoteMCJIT)
+ RTDyldMM = new ForwardingMemoryManager();
+ else
+ RTDyldMM = new SectionMemoryManager();
+
+ // Deliberately construct a temp std::unique_ptr to pass in. Do not null out
+ // RTDyldMM: We still use it below, even though we don't own it.
+ builder.setMCJITMemoryManager(
+ std::unique_ptr<RTDyldMemoryManager>(RTDyldMM));
+ } else if (RemoteMCJIT) {
+ WithColor::error(errs(), argv[0])
+ << "remote process execution does not work with the interpreter.\n";
+ exit(1);
+ }
+
+ builder.setOptLevel(getOptLevel());
+
+ TargetOptions Options =
+ codegen::InitTargetOptionsFromCodeGenFlags(Triple(TargetTriple));
+ if (codegen::getFloatABIForCalls() != FloatABI::Default)
+ Options.FloatABIType = codegen::getFloatABIForCalls();
+
+ builder.setTargetOptions(Options);
+
+ std::unique_ptr<ExecutionEngine> EE(builder.create());
+ if (!EE) {
+ if (!ErrorMsg.empty())
+ WithColor::error(errs(), argv[0])
+ << "error creating EE: " << ErrorMsg << "\n";
+ else
+ WithColor::error(errs(), argv[0]) << "unknown error creating EE!\n";
+ exit(1);
+ }
+
+ std::unique_ptr<LLIObjectCache> CacheManager;
+ if (EnableCacheManager) {
+ CacheManager.reset(new LLIObjectCache(ObjectCacheDir));
+ EE->setObjectCache(CacheManager.get());
+ }
+
+ // Load any additional modules specified on the command line.
+ for (unsigned i = 0, e = ExtraModules.size(); i != e; ++i) {
+ std::unique_ptr<Module> XMod = parseIRFile(ExtraModules[i], Err, Context);
+ if (!XMod)
+ reportError(Err, argv[0]);
+ if (EnableCacheManager) {
+ std::string CacheName("file:");
+ CacheName.append(ExtraModules[i]);
+ XMod->setModuleIdentifier(CacheName);
+ }
+ EE->addModule(std::move(XMod));
+ }
+
+ for (unsigned i = 0, e = ExtraObjects.size(); i != e; ++i) {
+ Expected<object::OwningBinary<object::ObjectFile>> Obj =
+ object::ObjectFile::createObjectFile(ExtraObjects[i]);
+ if (!Obj) {
+ // TODO: Actually report errors helpfully.
+ consumeError(Obj.takeError());
+ reportError(Err, argv[0]);
+ }
+ object::OwningBinary<object::ObjectFile> &O = Obj.get();
+ EE->addObjectFile(std::move(O));
+ }
+
+ for (unsigned i = 0, e = ExtraArchives.size(); i != e; ++i) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> ArBufOrErr =
+ MemoryBuffer::getFileOrSTDIN(ExtraArchives[i]);
+ if (!ArBufOrErr)
+ reportError(Err, argv[0]);
+ std::unique_ptr<MemoryBuffer> &ArBuf = ArBufOrErr.get();
+
+ Expected<std::unique_ptr<object::Archive>> ArOrErr =
+ object::Archive::create(ArBuf->getMemBufferRef());
+ if (!ArOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(ArOrErr.takeError(), OS);
+ OS.flush();
+ errs() << Buf;
+ exit(1);
+ }
+ std::unique_ptr<object::Archive> &Ar = ArOrErr.get();
+
+ object::OwningBinary<object::Archive> OB(std::move(Ar), std::move(ArBuf));
+
+ EE->addArchive(std::move(OB));
+ }
+
+ // If the target is Cygwin/MingW and we are generating remote code, we
+ // need an extra module to help out with linking.
+ if (RemoteMCJIT && Triple(Mod->getTargetTriple()).isOSCygMing()) {
+ addCygMingExtraModule(*EE, Context, Mod->getTargetTriple());
+ }
+
+ // The following functions have no effect if their respective profiling
+ // support wasn't enabled in the build configuration.
+ EE->RegisterJITEventListener(
+ JITEventListener::createOProfileJITEventListener());
+ EE->RegisterJITEventListener(
+ JITEventListener::createIntelJITEventListener());
+ if (!RemoteMCJIT)
+ EE->RegisterJITEventListener(
+ JITEventListener::createPerfJITEventListener());
+
+ if (!NoLazyCompilation && RemoteMCJIT) {
+ WithColor::warning(errs(), argv[0])
+ << "remote mcjit does not support lazy compilation\n";
+ NoLazyCompilation = true;
+ }
+ EE->DisableLazyCompilation(NoLazyCompilation);
+
+ // If the user specifically requested an argv[0] to pass into the program,
+ // do it now.
+ if (!FakeArgv0.empty()) {
+ InputFile = static_cast<std::string>(FakeArgv0);
+ } else {
+ // Otherwise, if there is a .bc suffix on the executable strip it off, it
+ // might confuse the program.
+ if (StringRef(InputFile).endswith(".bc"))
+ InputFile.erase(InputFile.length() - 3);
+ }
+
+ // Add the module's name to the start of the vector of arguments to main().
+ InputArgv.insert(InputArgv.begin(), InputFile);
+
+ // Call the main function from M as if its signature were:
+ // int main (int argc, char **argv, const char **envp)
+ // using the contents of Args to determine argc & argv, and the contents of
+ // EnvVars to determine envp.
+ //
+ Function *EntryFn = Mod->getFunction(EntryFunc);
+ if (!EntryFn) {
+ WithColor::error(errs(), argv[0])
+ << '\'' << EntryFunc << "\' function not found in module.\n";
+ return -1;
+ }
+
+ // Reset errno to zero on entry to main.
+ errno = 0;
+
+ int Result = -1;
+
+ // Sanity check use of remote-jit: LLI currently only supports use of the
+ // remote JIT on Unix platforms.
+ if (RemoteMCJIT) {
+#ifndef LLVM_ON_UNIX
+ WithColor::warning(errs(), argv[0])
+ << "host does not support external remote targets.\n";
+ WithColor::note() << "defaulting to local execution\n";
+ return -1;
+#else
+ if (ChildExecPath.empty()) {
+ WithColor::error(errs(), argv[0])
+ << "-remote-mcjit requires -mcjit-remote-process.\n";
+ exit(1);
+ } else if (!sys::fs::can_execute(ChildExecPath)) {
+ WithColor::error(errs(), argv[0])
+ << "unable to find usable child executable: '" << ChildExecPath
+ << "'\n";
+ return -1;
+ }
+#endif
+ }
+
+ std::unique_ptr<orc::ExecutorProcessControl> EPC =
+ RemoteMCJIT ? ExitOnErr(launchRemote())
+ : ExitOnErr(orc::SelfExecutorProcessControl::Create());
+
+ if (!RemoteMCJIT) {
+ // If the program doesn't explicitly call exit, we will need the Exit
+ // function later on to make an explicit call, so get the function now.
+ FunctionCallee Exit = Mod->getOrInsertFunction(
+ "exit", Type::getVoidTy(Context), Type::getInt32Ty(Context));
+
+ // Run static constructors.
+ if (!ForceInterpreter) {
+ // Give MCJIT a chance to apply relocations and set page permissions.
+ EE->finalizeObject();
+ }
+ EE->runStaticConstructorsDestructors(false);
+
+ // Trigger compilation separately so code regions that need to be
+ // invalidated will be known.
+ (void)EE->getPointerToFunction(EntryFn);
+ // Clear instruction cache before code will be executed.
+ if (RTDyldMM)
+ static_cast<SectionMemoryManager*>(RTDyldMM)->invalidateInstructionCache();
+
+ // Run main.
+ Result = EE->runFunctionAsMain(EntryFn, InputArgv, envp);
+
+ // Run static destructors.
+ EE->runStaticConstructorsDestructors(true);
+
+ // If the program didn't call exit explicitly, we should call it now.
+ // This ensures that any atexit handlers get called correctly.
+ if (Function *ExitF =
+ dyn_cast<Function>(Exit.getCallee()->stripPointerCasts())) {
+ if (ExitF->getFunctionType() == Exit.getFunctionType()) {
+ std::vector<GenericValue> Args;
+ GenericValue ResultGV;
+ ResultGV.IntVal = APInt(32, Result);
+ Args.push_back(ResultGV);
+ EE->runFunction(ExitF, Args);
+ WithColor::error(errs(), argv[0])
+ << "exit(" << Result << ") returned!\n";
+ abort();
+ }
+ }
+ WithColor::error(errs(), argv[0]) << "exit defined with wrong prototype!\n";
+ abort();
+ } else {
+ // else == "if (RemoteMCJIT)"
+
+ // Remote target MCJIT doesn't (yet) support static constructors. No reason
+ // it couldn't. This is a limitation of the LLI implementation, not the
+ // MCJIT itself. FIXME.
+
+ // Create a remote memory manager.
+ auto RemoteMM = ExitOnErr(
+ orc::EPCGenericRTDyldMemoryManager::CreateWithDefaultBootstrapSymbols(
+ *EPC));
+
+ // Forward MCJIT's memory manager calls to the remote memory manager.
+ static_cast<ForwardingMemoryManager*>(RTDyldMM)->setMemMgr(
+ std::move(RemoteMM));
+
+ // Forward MCJIT's symbol resolution calls to the remote.
+ static_cast<ForwardingMemoryManager *>(RTDyldMM)->setResolver(
+ ExitOnErr(RemoteResolver::Create(*EPC)));
+ // Grab the target address of the JIT'd main function on the remote and call
+ // it.
+ // FIXME: argv and envp handling.
+ auto Entry =
+ orc::ExecutorAddr(EE->getFunctionAddress(EntryFn->getName().str()));
+ EE->finalizeObject();
+ LLVM_DEBUG(dbgs() << "Executing '" << EntryFn->getName() << "' at 0x"
+ << format("%llx", Entry.getValue()) << "\n");
+ Result = ExitOnErr(EPC->runAsMain(Entry, {}));
+
+ // Like static constructors, the remote target MCJIT support doesn't handle
+ // this yet. It could. FIXME.
+
+ // Delete the EE - we need to tear it down *before* we terminate the session
+ // with the remote, otherwise it'll crash when it tries to release resources
+ // on a remote that has already been disconnected.
+ EE.reset();
+
+ // Signal the remote target that we're done JITing.
+ ExitOnErr(EPC->disconnect());
+ }
+
+ return Result;
+}
+
+static std::function<void(Module &)> createDebugDumper() {
+ switch (OrcDumpKind) {
+ case DumpKind::NoDump:
+ return [](Module &M) {};
+
+ case DumpKind::DumpFuncsToStdOut:
+ return [](Module &M) {
+ printf("[ ");
+
+ for (const auto &F : M) {
+ if (F.isDeclaration())
+ continue;
+
+ if (F.hasName()) {
+ std::string Name(std::string(F.getName()));
+ printf("%s ", Name.c_str());
+ } else
+ printf("<anon> ");
+ }
+
+ printf("]\n");
+ };
+
+ case DumpKind::DumpModsToStdOut:
+ return [](Module &M) {
+ outs() << "----- Module Start -----\n" << M << "----- Module End -----\n";
+ };
+
+ case DumpKind::DumpModsToDisk:
+ return [](Module &M) {
+ std::error_code EC;
+ raw_fd_ostream Out(M.getModuleIdentifier() + ".ll", EC,
+ sys::fs::OF_TextWithCRLF);
+ if (EC) {
+ errs() << "Couldn't open " << M.getModuleIdentifier()
+ << " for dumping.\nError:" << EC.message() << "\n";
+ exit(1);
+ }
+ Out << M;
+ };
+ }
+ llvm_unreachable("Unknown DumpKind");
+}
+
+Error loadDylibs() {
+ for (const auto &Dylib : Dylibs) {
+ std::string ErrMsg;
+ if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg))
+ return make_error<StringError>(ErrMsg, inconvertibleErrorCode());
+ }
+
+ return Error::success();
+}
+
+static void exitOnLazyCallThroughFailure() { exit(1); }
+
+Expected<orc::ThreadSafeModule>
+loadModule(StringRef Path, orc::ThreadSafeContext TSCtx) {
+ SMDiagnostic Err;
+ auto M = parseIRFile(Path, Err, *TSCtx.getContext());
+ if (!M) {
+ std::string ErrMsg;
+ {
+ raw_string_ostream ErrMsgStream(ErrMsg);
+ Err.print("lli", ErrMsgStream);
+ }
+ return make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode());
+ }
+
+ if (EnableCacheManager)
+ M->setModuleIdentifier("file:" + M->getModuleIdentifier());
+
+ return orc::ThreadSafeModule(std::move(M), std::move(TSCtx));
+}
+
+int runOrcJIT(const char *ProgName) {
+ // Start setting up the JIT environment.
+
+ // Parse the main module.
+ orc::ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());
+ auto MainModule = ExitOnErr(loadModule(InputFile, TSCtx));
+
+ // Get TargetTriple and DataLayout from the main module if they're explicitly
+ // set.
+ Optional<Triple> TT;
+ Optional<DataLayout> DL;
+ MainModule.withModuleDo([&](Module &M) {
+ if (!M.getTargetTriple().empty())
+ TT = Triple(M.getTargetTriple());
+ if (!M.getDataLayout().isDefault())
+ DL = M.getDataLayout();
+ });
+
+ orc::LLLazyJITBuilder Builder;
+
+ Builder.setJITTargetMachineBuilder(
+ TT ? orc::JITTargetMachineBuilder(*TT)
+ : ExitOnErr(orc::JITTargetMachineBuilder::detectHost()));
+
+ TT = Builder.getJITTargetMachineBuilder()->getTargetTriple();
+ if (DL)
+ Builder.setDataLayout(DL);
+
+ if (!codegen::getMArch().empty())
+ Builder.getJITTargetMachineBuilder()->getTargetTriple().setArchName(
+ codegen::getMArch());
+
+ Builder.getJITTargetMachineBuilder()
+ ->setCPU(codegen::getCPUStr())
+ .addFeatures(codegen::getFeatureList())
+ .setRelocationModel(codegen::getExplicitRelocModel())
+ .setCodeModel(codegen::getExplicitCodeModel());
+
+ // FIXME: Setting a dummy call-through manager in non-lazy mode prevents the
+ // JIT builder to instantiate a default (which would fail with an error for
+ // unsupported architectures).
+ if (UseJITKind != JITKind::OrcLazy) {
+ auto ES = std::make_unique<orc::ExecutionSession>(
+ ExitOnErr(orc::SelfExecutorProcessControl::Create()));
+ Builder.setLazyCallthroughManager(
+ std::make_unique<orc::LazyCallThroughManager>(*ES, 0, nullptr));
+ Builder.setExecutionSession(std::move(ES));
+ }
+
+ Builder.setLazyCompileFailureAddr(
+ pointerToJITTargetAddress(exitOnLazyCallThroughFailure));
+ Builder.setNumCompileThreads(LazyJITCompileThreads);
+
+ // If the object cache is enabled then set a custom compile function
+ // creator to use the cache.
+ std::unique_ptr<LLIObjectCache> CacheManager;
+ if (EnableCacheManager) {
+
+ CacheManager = std::make_unique<LLIObjectCache>(ObjectCacheDir);
+
+ Builder.setCompileFunctionCreator(
+ [&](orc::JITTargetMachineBuilder JTMB)
+ -> Expected<std::unique_ptr<orc::IRCompileLayer::IRCompiler>> {
+ if (LazyJITCompileThreads > 0)
+ return std::make_unique<orc::ConcurrentIRCompiler>(std::move(JTMB),
+ CacheManager.get());
+
+ auto TM = JTMB.createTargetMachine();
+ if (!TM)
+ return TM.takeError();
+
+ return std::make_unique<orc::TMOwningSimpleCompiler>(std::move(*TM),
+ CacheManager.get());
+ });
+ }
+
+ // Set up LLJIT platform.
+ {
+ LLJITPlatform P = Platform;
+ if (P == LLJITPlatform::DetectHost)
+ P = LLJITPlatform::GenericIR;
+
+ switch (P) {
+ case LLJITPlatform::GenericIR:
+ // Nothing to do: LLJITBuilder will use this by default.
+ break;
+ case LLJITPlatform::Inactive:
+ Builder.setPlatformSetUp(orc::setUpInactivePlatform);
+ break;
+ default:
+ llvm_unreachable("Unrecognized platform value");
+ }
+ }
+
+ std::unique_ptr<orc::ExecutorProcessControl> EPC = nullptr;
+ if (JITLinker == JITLinkerKind::JITLink) {
+ EPC = ExitOnErr(orc::SelfExecutorProcessControl::Create(
+ std::make_shared<orc::SymbolStringPool>()));
+
+ Builder.setObjectLinkingLayerCreator([&EPC](orc::ExecutionSession &ES,
+ const Triple &) {
+ auto L = std::make_unique<orc::ObjectLinkingLayer>(ES, EPC->getMemMgr());
+ L->addPlugin(std::make_unique<orc::EHFrameRegistrationPlugin>(
+ ES, ExitOnErr(orc::EPCEHFrameRegistrar::Create(ES))));
+ L->addPlugin(std::make_unique<orc::DebugObjectManagerPlugin>(
+ ES, ExitOnErr(orc::createJITLoaderGDBRegistrar(ES))));
+ return L;
+ });
+ }
+
+ auto J = ExitOnErr(Builder.create());
+
+ auto *ObjLayer = &J->getObjLinkingLayer();
+ if (auto *RTDyldObjLayer = dyn_cast<orc::RTDyldObjectLinkingLayer>(ObjLayer))
+ RTDyldObjLayer->registerJITEventListener(
+ *JITEventListener::createGDBRegistrationListener());
+
+ if (PerModuleLazy)
+ J->setPartitionFunction(orc::CompileOnDemandLayer::compileWholeModule);
+
+ auto Dump = createDebugDumper();
+
+ J->getIRTransformLayer().setTransform(
+ [&](orc::ThreadSafeModule TSM,
+ const orc::MaterializationResponsibility &R) {
+ TSM.withModuleDo([&](Module &M) {
+ if (verifyModule(M, &dbgs())) {
+ dbgs() << "Bad module: " << &M << "\n";
+ exit(1);
+ }
+ Dump(M);
+ });
+ return TSM;
+ });
+
+ orc::MangleAndInterner Mangle(J->getExecutionSession(), J->getDataLayout());
+
+ // Unless they've been explicitly disabled, make process symbols available to
+ // JIT'd code.
+ if (!NoProcessSymbols)
+ J->getMainJITDylib().addGenerator(
+ ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
+ J->getDataLayout().getGlobalPrefix(),
+ [MainName = Mangle("main")](const orc::SymbolStringPtr &Name) {
+ return Name != MainName;
+ })));
+
+ if (GenerateBuiltinFunctions.size() > 0)
+ J->getMainJITDylib().addGenerator(
+ std::make_unique<LLIBuiltinFunctionGenerator>(GenerateBuiltinFunctions,
+ Mangle));
+
+ // Regular modules are greedy: They materialize as a whole and trigger
+ // materialization for all required symbols recursively. Lazy modules go
+ // through partitioning and they replace outgoing calls with reexport stubs
+ // that resolve on call-through.
+ auto AddModule = [&](orc::JITDylib &JD, orc::ThreadSafeModule M) {
+ return UseJITKind == JITKind::OrcLazy ? J->addLazyIRModule(JD, std::move(M))
+ : J->addIRModule(JD, std::move(M));
+ };
+
+ // Add the main module.
+ ExitOnErr(AddModule(J->getMainJITDylib(), 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) {
+ orc::JITDylib *JD = J->getJITDylibByName(*JDItr);
+ if (!JD) {
+ JD = &ExitOnErr(J->createJITDylib(*JDItr));
+ J->getMainJITDylib().addToLinkOrder(*JD);
+ JD->addToLinkOrder(J->getMainJITDylib());
+ }
+ IdxToDylib[JITDylibs.getPosition(JDItr - JITDylibs.begin())] = JD;
+ }
+
+ for (auto EMItr = ExtraModules.begin(), EMEnd = ExtraModules.end();
+ EMItr != EMEnd; ++EMItr) {
+ auto M = ExitOnErr(loadModule(*EMItr, TSCtx));
+
+ 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(AddModule(JD, std::move(M)));
+ }
+
+ for (auto EAItr = ExtraArchives.begin(), EAEnd = ExtraArchives.end();
+ EAItr != EAEnd; ++EAItr) {
+ auto EAIdx = ExtraArchives.getPosition(EAItr - ExtraArchives.begin());
+ assert(EAIdx != 0 && "ExtraArchive should have index > 0");
+ auto JDItr = std::prev(IdxToDylib.lower_bound(EAIdx));
+ auto &JD = *JDItr->second;
+ JD.addGenerator(ExitOnErr(orc::StaticLibraryDefinitionGenerator::Load(
+ J->getObjLinkingLayer(), EAItr->c_str(), *TT)));
+ }
+ }
+
+ // Add the objects.
+ for (auto &ObjPath : ExtraObjects) {
+ auto Obj = ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(ObjPath)));
+ ExitOnErr(J->addObjectFile(std::move(Obj)));
+ }
+
+ // Run any static constructors.
+ ExitOnErr(J->initialize(J->getMainJITDylib()));
+
+ // 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(); }));
+ }
+
+ // Resolve and run the main function.
+ JITEvaluatedSymbol MainSym = ExitOnErr(J->lookup(EntryFunc));
+ int Result;
+
+ if (EPC) {
+ // ExecutorProcessControl-based execution with JITLink.
+ Result = ExitOnErr(
+ EPC->runAsMain(orc::ExecutorAddr(MainSym.getAddress()), InputArgv));
+ } else {
+ // Manual in-process execution with RuntimeDyld.
+ using MainFnTy = int(int, char *[]);
+ auto MainFn = jitTargetAddressToFunction<MainFnTy *>(MainSym.getAddress());
+ Result = orc::runAsMain(MainFn, InputArgv, StringRef(InputFile));
+ }
+
+ // Wait for -entry-point threads.
+ for (auto &AltEntryThread : AltEntryThreads)
+ AltEntryThread.join();
+
+ // Run destructors.
+ ExitOnErr(J->deinitialize(J->getMainJITDylib()));
+
+ 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);
+ }
+}
+
+Expected<std::unique_ptr<orc::ExecutorProcessControl>> launchRemote() {
+#ifndef LLVM_ON_UNIX
+ llvm_unreachable("launchRemote not supported on non-Unix platforms");
+#else
+ int PipeFD[2][2];
+ pid_t ChildPID;
+
+ // Create two pipes.
+ if (pipe(PipeFD[0]) != 0 || pipe(PipeFD[1]) != 0)
+ perror("Error creating pipe: ");
+
+ ChildPID = fork();
+
+ if (ChildPID == 0) {
+ // In the child...
+
+ // Close the parent ends of the pipes
+ close(PipeFD[0][1]);
+ close(PipeFD[1][0]);
+
+
+ // Execute the child process.
+ std::unique_ptr<char[]> ChildPath, ChildIn, ChildOut;
+ {
+ ChildPath.reset(new char[ChildExecPath.size() + 1]);
+ std::copy(ChildExecPath.begin(), ChildExecPath.end(), &ChildPath[0]);
+ ChildPath[ChildExecPath.size()] = '\0';
+ std::string ChildInStr = utostr(PipeFD[0][0]);
+ ChildIn.reset(new char[ChildInStr.size() + 1]);
+ std::copy(ChildInStr.begin(), ChildInStr.end(), &ChildIn[0]);
+ ChildIn[ChildInStr.size()] = '\0';
+ std::string ChildOutStr = utostr(PipeFD[1][1]);
+ ChildOut.reset(new char[ChildOutStr.size() + 1]);
+ std::copy(ChildOutStr.begin(), ChildOutStr.end(), &ChildOut[0]);
+ ChildOut[ChildOutStr.size()] = '\0';
+ }
+
+ char * const args[] = { &ChildPath[0], &ChildIn[0], &ChildOut[0], nullptr };
+ int rc = execv(ChildExecPath.c_str(), args);
+ if (rc != 0)
+ perror("Error executing child process: ");
+ llvm_unreachable("Error executing child process");
+ }
+ // else we're the parent...
+
+ // Close the child ends of the pipes
+ close(PipeFD[0][0]);
+ close(PipeFD[1][1]);
+
+ // Return a SimpleRemoteEPC instance connected to our end of the pipes.
+ return orc::SimpleRemoteEPC::Create<orc::FDSimpleRemoteEPCTransport>(
+ std::make_unique<llvm::orc::InPlaceTaskDispatcher>(),
+ llvm::orc::SimpleRemoteEPC::Setup(), PipeFD[1][0], PipeFD[0][1]);
+#endif
+}
diff --git a/contrib/libs/llvm14/tools/lli/ya.make b/contrib/libs/llvm14/tools/lli/ya.make
new file mode 100644
index 00000000000..6da39fa4c90
--- /dev/null
+++ b/contrib/libs/llvm14/tools/lli/ya.make
@@ -0,0 +1,83 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/AsmPrinter
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/CodeGen/SelectionDAG
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/ExecutionEngine
+ contrib/libs/llvm14/lib/ExecutionEngine/Interpreter
+ contrib/libs/llvm14/lib/ExecutionEngine/JITLink
+ contrib/libs/llvm14/lib/ExecutionEngine/MCJIT
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc/Shared
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc/TargetProcess
+ contrib/libs/llvm14/lib/ExecutionEngine/RuntimeDyld
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Passes
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/CFGuard
+ contrib/libs/llvm14/lib/Transforms/Coroutines
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/ObjCARC
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+)
+
+IF (OS_LINUX)
+ PEERDIR(
+ contrib/libs/llvm14/lib/ExecutionEngine/PerfJITEvents
+ )
+ENDIF()
+
+ADDINCL(
+ contrib/libs/llvm14/tools/lli
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ ExecutionUtils.cpp
+ lli.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-ar/llvm-ar.cpp b/contrib/libs/llvm14/tools/llvm-ar/llvm-ar.cpp
new file mode 100644
index 00000000000..8842162f521
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-ar/llvm-ar.cpp
@@ -0,0 +1,1307 @@
+//===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Builds up (relatively) standard unix archive files (.a) containing LLVM
+// bitcode or other files.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ArchiveWriter.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/SymbolicFile.h"
+#include "llvm/Support/Chrono.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#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"
+
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
+#ifdef _WIN32
+#include "llvm/Support/Windows/WindowsSupport.h"
+#endif
+
+using namespace llvm;
+
+// The name this program was invoked as.
+static StringRef ToolName;
+
+// The basename of this program.
+static StringRef Stem;
+
+const char RanlibHelp[] = R"(OVERVIEW: LLVM Ranlib (llvm-ranlib)
+
+ This program generates an index to speed access to archives
+
+USAGE: llvm-ranlib <archive-file>
+
+OPTIONS:
+ -h --help - Display available options
+ -v --version - Display the version of this program
+ -D - Use zero for timestamps and uids/gids (default)
+ -U - Use actual timestamps and uids/gids
+)";
+
+const char ArHelp[] = R"(OVERVIEW: LLVM Archiver
+
+USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] [count] <archive> [files]
+ llvm-ar -M [<mri-script]
+
+OPTIONS:
+ --format - archive format to create
+ =default - default
+ =gnu - gnu
+ =darwin - darwin
+ =bsd - bsd
+ --plugin=<string> - ignored for compatibility
+ -h --help - display this help and exit
+ --rsp-quoting - quoting style for response files
+ =posix - posix
+ =windows - windows
+ --thin - create a thin archive
+ --version - print the version and exit
+ @<file> - read options from <file>
+
+OPERATIONS:
+ d - delete [files] from the archive
+ m - move [files] in the archive
+ p - print contents of [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 list of files in 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)
+ [h] - display this help and exit
+ [i] - put [files] before [relpos] (same as [b])
+ [l] - ignored for compatibility
+ [L] - add archive's contents
+ [N] - use instance [count] of name
+ [o] - preserve original dates
+ [O] - display member offsets
+ [P] - use full names when matching (implied for thin archives)
+ [s] - create an archive index (cf. ranlib)
+ [S] - do not build a symbol table
+ [T] - deprecated, use --thin instead
+ [u] - update only [files] newer than archive contents
+ [U] - use actual timestamps and uids/gids
+ [v] - be verbose about actions taken
+ [V] - display the version and exit
+)";
+
+static void printHelpMessage() {
+ if (Stem.contains_insensitive("ranlib"))
+ outs() << RanlibHelp;
+ else if (Stem.contains_insensitive("ar"))
+ outs() << ArHelp;
+}
+
+static unsigned MRILineNumber;
+static bool ParsingMRIScript;
+
+// Show the error plus the usage message, and exit.
+[[noreturn]] static void badUsage(Twine Error) {
+ WithColor::error(errs(), ToolName) << Error << "\n";
+ printHelpMessage();
+ exit(1);
+}
+
+// Show the error message and exit.
+[[noreturn]] static void fail(Twine Error) {
+ if (ParsingMRIScript) {
+ WithColor::error(errs(), ToolName)
+ << "script line " << MRILineNumber << ": " << Error << "\n";
+ } else {
+ WithColor::error(errs(), ToolName) << Error << "\n";
+ }
+ exit(1);
+}
+
+static void failIfError(std::error_code EC, Twine Context = "") {
+ if (!EC)
+ return;
+
+ std::string ContextStr = Context.str();
+ if (ContextStr.empty())
+ fail(EC.message());
+ fail(Context + ": " + EC.message());
+}
+
+static void failIfError(Error E, Twine Context = "") {
+ if (!E)
+ return;
+
+ handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {
+ std::string ContextStr = Context.str();
+ if (ContextStr.empty())
+ fail(EIB.message());
+ fail(Context + ": " + EIB.message());
+ });
+}
+
+static SmallVector<const char *, 256> PositionalArgs;
+
+static bool MRI;
+
+namespace {
+enum Format { Default, GNU, BSD, DARWIN, Unknown };
+}
+
+static Format FormatType = Default;
+
+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
+};
+
+// Modifiers to follow operation to vary behavior
+static bool AddAfter = false; ///< 'a' modifier
+static bool AddBefore = false; ///< 'b' modifier
+static bool Create = false; ///< 'c' modifier
+static bool OriginalDates = false; ///< 'o' modifier
+static bool DisplayMemberOffsets = false; ///< 'O' modifier
+static bool CompareFullPath = false; ///< 'P' modifier
+static bool OnlyUpdate = false; ///< 'u' modifier
+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
+// refers. Only one of 'a', 'b' or 'i' can be specified so we only need
+// one variable.
+static std::string RelPos;
+
+// Count parameter for 'N' modifier. This variable specifies which file should
+// match for extract/delete operations when there are multiple matches. This is
+// 1-indexed. A value of 0 is invalid, and implies 'N' is not used.
+static int CountParam = 0;
+
+// This variable holds the name of the archive file as given on the
+// command line.
+static std::string ArchiveName;
+
+static std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
+static std::vector<std::unique_ptr<object::Archive>> Archives;
+
+// This variable holds the list of member files to proecess, as given
+// on the command line.
+static std::vector<StringRef> Members;
+
+// Static buffer to hold StringRefs.
+static BumpPtrAllocator Alloc;
+
+// Extract the member filename from the command line for the [relpos] argument
+// associated with a, b, and i modifiers
+static void getRelPos() {
+ if (PositionalArgs.empty())
+ fail("expected [relpos] for 'a', 'b', or 'i' modifier");
+ RelPos = PositionalArgs[0];
+ PositionalArgs.erase(PositionalArgs.begin());
+}
+
+// Extract the parameter from the command line for the [count] argument
+// associated with the N modifier
+static void getCountParam() {
+ if (PositionalArgs.empty())
+ badUsage("expected [count] for 'N' modifier");
+ auto CountParamArg = StringRef(PositionalArgs[0]);
+ if (CountParamArg.getAsInteger(10, CountParam))
+ badUsage("value for [count] must be numeric, got: " + CountParamArg);
+ if (CountParam < 1)
+ badUsage("value for [count] must be positive, got: " + CountParamArg);
+ PositionalArgs.erase(PositionalArgs.begin());
+}
+
+// Get the archive file name from the command line
+static void getArchive() {
+ if (PositionalArgs.empty())
+ badUsage("an archive name must be specified");
+ ArchiveName = PositionalArgs[0];
+ PositionalArgs.erase(PositionalArgs.begin());
+}
+
+static object::Archive &readLibrary(const Twine &Library) {
+ auto BufOrErr = MemoryBuffer::getFile(Library, /*IsText=*/false,
+ /*RequiresNullTerminator=*/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
+// specified. Process all modifiers and check to make sure that constraints on
+// modifier/operation pairs have not been violated.
+static ArchiveOperation parseCommandLine() {
+ if (MRI) {
+ if (!PositionalArgs.empty() || !Options.empty())
+ badUsage("cannot mix -M and other options");
+ runMRIScript();
+ }
+
+ // Keep track of number of operations. We can only specify one
+ // per execution.
+ unsigned NumOperations = 0;
+
+ // Keep track of the number of positional modifiers (a,b,i). Only
+ // one can be specified.
+ unsigned NumPositional = 0;
+
+ // Keep track of which operation was requested
+ ArchiveOperation Operation;
+
+ 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;
+ case 'O':
+ DisplayMemberOffsets = true;
+ break;
+ case 'P':
+ CompareFullPath = true;
+ break;
+ case 's':
+ Symtab = true;
+ MaybeJustCreateSymTab = true;
+ break;
+ case 'S':
+ Symtab = false;
+ break;
+ case 'u':
+ OnlyUpdate = true;
+ break;
+ case 'v':
+ Verbose = true;
+ break;
+ case 'a':
+ getRelPos();
+ AddAfter = true;
+ NumPositional++;
+ break;
+ case 'b':
+ getRelPos();
+ AddBefore = true;
+ NumPositional++;
+ break;
+ case 'i':
+ getRelPos();
+ AddBefore = true;
+ NumPositional++;
+ break;
+ case 'D':
+ Deterministic = true;
+ break;
+ case 'U':
+ Deterministic = false;
+ break;
+ case 'N':
+ getCountParam();
+ break;
+ case 'T':
+ Thin = true;
+ break;
+ case 'L':
+ AddLibrary = true;
+ break;
+ case 'V':
+ cl::PrintVersionMessage();
+ exit(0);
+ case 'h':
+ printHelpMessage();
+ exit(0);
+ default:
+ badUsage(std::string("unknown option ") + Options[i]);
+ }
+ }
+
+ // Thin archives store path names, so P should be forced.
+ if (Thin)
+ CompareFullPath = true;
+
+ // At this point, the next thing on the command line must be
+ // the archive name.
+ getArchive();
+
+ // Everything on the command line at this point is a member.
+ Members.assign(PositionalArgs.begin(), PositionalArgs.end());
+
+ if (NumOperations == 0 && MaybeJustCreateSymTab) {
+ NumOperations = 1;
+ Operation = CreateSymTab;
+ if (!Members.empty())
+ badUsage("the 's' operation takes only an archive as argument");
+ }
+
+ // Perform various checks on the operation/modifier specification
+ // to make sure we are dealing with a legal request.
+ if (NumOperations == 0)
+ badUsage("you must specify at least one of the operations");
+ if (NumOperations > 1)
+ badUsage("only one operation may be specified");
+ if (NumPositional > 1)
+ badUsage("you may only specify one of 'a', 'b', and 'i' modifiers");
+ if (AddAfter || AddBefore)
+ if (Operation != Move && Operation != ReplaceOrInsert)
+ badUsage("the 'a', 'b' and 'i' modifiers can only be specified with "
+ "the 'm' or 'r' operations");
+ if (CountParam)
+ if (Operation != Extract && Operation != Delete)
+ badUsage("the 'N' modifier can only be specified with the 'x' or 'd' "
+ "operations");
+ if (OriginalDates && Operation != Extract)
+ badUsage("the 'o' modifier is only applicable to the 'x' operation");
+ if (OnlyUpdate && Operation != ReplaceOrInsert)
+ badUsage("the 'u' modifier is only applicable to the 'r' operation");
+ if (AddLibrary && Operation != QuickAppend)
+ badUsage("the 'L' modifier is only applicable to the 'q' operation");
+
+ // Return the parsed operation to the caller
+ return Operation;
+}
+
+// Implements the 'p' operation. This function traverses the archive
+// looking for members that match the path list.
+static void doPrint(StringRef Name, const object::Archive::Child &C) {
+ if (Verbose)
+ outs() << "Printing " << Name << "\n";
+
+ Expected<StringRef> DataOrErr = C.getBuffer();
+ failIfError(DataOrErr.takeError());
+ StringRef Data = *DataOrErr;
+ outs().write(Data.data(), Data.size());
+}
+
+// Utility function for printing out the file mode when the 't' operation is in
+// verbose mode.
+static void printMode(unsigned mode) {
+ outs() << ((mode & 004) ? "r" : "-");
+ outs() << ((mode & 002) ? "w" : "-");
+ outs() << ((mode & 001) ? "x" : "-");
+}
+
+// Implement the 't' operation. This function prints out just
+// the file names of each of the members. However, if verbose mode is requested
+// ('v' modifier) then the file type, permission mode, user, group, size, and
+// modification time are also printed.
+static void doDisplayTable(StringRef Name, const object::Archive::Child &C) {
+ if (Verbose) {
+ Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();
+ failIfError(ModeOrErr.takeError());
+ sys::fs::perms Mode = ModeOrErr.get();
+ printMode((Mode >> 6) & 007);
+ printMode((Mode >> 3) & 007);
+ printMode(Mode & 007);
+ Expected<unsigned> UIDOrErr = C.getUID();
+ failIfError(UIDOrErr.takeError());
+ outs() << ' ' << UIDOrErr.get();
+ Expected<unsigned> GIDOrErr = C.getGID();
+ failIfError(GIDOrErr.takeError());
+ outs() << '/' << GIDOrErr.get();
+ Expected<uint64_t> Size = C.getSize();
+ failIfError(Size.takeError());
+ outs() << ' ' << format("%6llu", Size.get());
+ auto ModTimeOrErr = C.getLastModified();
+ failIfError(ModTimeOrErr.takeError());
+ // 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() << ' ';
+ }
+
+ if (C.getParent()->isThin()) {
+ if (!sys::path::is_absolute(Name)) {
+ StringRef ParentDir = sys::path::parent_path(ArchiveName);
+ if (!ParentDir.empty())
+ outs() << sys::path::convert_to_slash(ParentDir) << '/';
+ }
+ outs() << Name;
+ } else {
+ outs() << Name;
+ if (DisplayMemberOffsets)
+ outs() << " 0x" << utohexstr(C.getDataOffset(), true);
+ }
+ outs() << '\n';
+}
+
+static std::string normalizePath(StringRef Path) {
+ return CompareFullPath ? sys::path::convert_to_slash(Path)
+ : std::string(sys::path::filename(Path));
+}
+
+static bool comparePaths(StringRef Path1, StringRef Path2) {
+// When on Windows this function calls CompareStringOrdinal
+// as Windows file paths are case-insensitive.
+// CompareStringOrdinal compares two Unicode strings for
+// binary equivalence and allows for case insensitivity.
+#ifdef _WIN32
+ SmallVector<wchar_t, 128> WPath1, WPath2;
+ failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path1), WPath1));
+ failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path2), WPath2));
+
+ return CompareStringOrdinal(WPath1.data(), WPath1.size(), WPath2.data(),
+ WPath2.size(), true) == CSTR_EQUAL;
+#else
+ return normalizePath(Path1) == normalizePath(Path2);
+#endif
+}
+
+// Implement the 'x' operation. This function extracts files back to the file
+// system.
+static void doExtract(StringRef Name, const object::Archive::Child &C) {
+ // Retain the original mode.
+ Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();
+ failIfError(ModeOrErr.takeError());
+ sys::fs::perms Mode = ModeOrErr.get();
+
+ llvm::StringRef outputFilePath = sys::path::filename(Name);
+ if (Verbose)
+ outs() << "x - " << outputFilePath << '\n';
+
+ int FD;
+ failIfError(sys::fs::openFileForWrite(outputFilePath, FD,
+ sys::fs::CD_CreateAlways,
+ sys::fs::OF_None, Mode),
+ Name);
+
+ {
+ raw_fd_ostream file(FD, false);
+
+ // Get the data and its length
+ Expected<StringRef> BufOrErr = C.getBuffer();
+ failIfError(BufOrErr.takeError());
+ StringRef Data = BufOrErr.get();
+
+ // Write the data.
+ file.write(Data.data(), Data.size());
+ }
+
+ // If we're supposed to retain the original modification times, etc. do so
+ // now.
+ if (OriginalDates) {
+ auto ModTimeOrErr = C.getLastModified();
+ failIfError(ModTimeOrErr.takeError());
+ failIfError(
+ sys::fs::setLastAccessAndModificationTime(FD, ModTimeOrErr.get()));
+ }
+
+ if (close(FD))
+ fail("Could not close the file");
+}
+
+static bool shouldCreateArchive(ArchiveOperation Op) {
+ switch (Op) {
+ case Print:
+ case Delete:
+ case Move:
+ case DisplayTable:
+ case Extract:
+ case CreateSymTab:
+ return false;
+
+ case QuickAppend:
+ case ReplaceOrInsert:
+ return true;
+ }
+
+ llvm_unreachable("Missing entry in covered switch.");
+}
+
+static void performReadOperation(ArchiveOperation Operation,
+ object::Archive *OldArchive) {
+ if (Operation == Extract && OldArchive->isThin())
+ fail("extracting from a thin archive is not supported");
+
+ bool Filter = !Members.empty();
+ StringMap<int> MemberCount;
+ {
+ Error Err = Error::success();
+ for (auto &C : OldArchive->children(Err)) {
+ Expected<StringRef> NameOrErr = C.getName();
+ failIfError(NameOrErr.takeError());
+ StringRef Name = NameOrErr.get();
+
+ if (Filter) {
+ auto I = find_if(Members, [Name](StringRef Path) {
+ return comparePaths(Name, Path);
+ });
+ if (I == Members.end())
+ continue;
+ if (CountParam && ++MemberCount[Name] != CountParam)
+ continue;
+ Members.erase(I);
+ }
+
+ switch (Operation) {
+ default:
+ llvm_unreachable("Not a read operation");
+ case Print:
+ doPrint(Name, C);
+ break;
+ case DisplayTable:
+ doDisplayTable(Name, C);
+ break;
+ case Extract:
+ doExtract(Name, C);
+ break;
+ }
+ }
+ failIfError(std::move(Err));
+ }
+
+ if (Members.empty())
+ return;
+ for (StringRef Name : Members)
+ 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 the child member we're trying to add is thin, use the path relative to
+ // the archive it's in, so the file resolves correctly.
+ if (Thin && FlattenArchive) {
+ StringSaver Saver(Alloc);
+ Expected<std::string> FileNameOrErr(M.getName());
+ failIfError(FileNameOrErr.takeError());
+ if (sys::path::is_absolute(*FileNameOrErr)) {
+ NMOrErr->MemberName = Saver.save(sys::path::convert_to_slash(*FileNameOrErr));
+ } else {
+ FileNameOrErr = M.getFullName();
+ failIfError(FileNameOrErr.takeError());
+ Expected<std::string> PathOrErr =
+ computeArchiveRelativePath(ArchiveName, *FileNameOrErr);
+ NMOrErr->MemberName = Saver.save(
+ PathOrErr ? *PathOrErr : sys::path::convert_to_slash(*FileNameOrErr));
+ }
+ }
+ 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, bool FlattenArchive = false) {
+ Expected<NewArchiveMember> NMOrErr =
+ NewArchiveMember::getFile(FileName, Deterministic);
+ failIfError(NMOrErr.takeError(), FileName);
+ StringSaver Saver(Alloc);
+ // For regular archives, use the basename of the object path for the member
+ // name. For thin archives, use the full relative paths so the file resolves
+ // correctly.
+ if (!Thin) {
+ NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName);
+ } else {
+ if (sys::path::is_absolute(FileName))
+ NMOrErr->MemberName = Saver.save(sys::path::convert_to_slash(FileName));
+ else {
+ Expected<std::string> PathOrErr =
+ computeArchiveRelativePath(ArchiveName, FileName);
+ NMOrErr->MemberName = Saver.save(
+ PathOrErr ? *PathOrErr : sys::path::convert_to_slash(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;
+ }
+ }
+ Members.push_back(std::move(*NMOrErr));
+}
+
+enum InsertAction {
+ IA_AddOldMember,
+ IA_AddNewMember,
+ IA_Delete,
+ IA_MoveOldMember,
+ IA_MoveNewMember
+};
+
+static InsertAction computeInsertAction(ArchiveOperation Operation,
+ const object::Archive::Child &Member,
+ StringRef Name,
+ std::vector<StringRef>::iterator &Pos,
+ StringMap<int> &MemberCount) {
+ if (Operation == QuickAppend || Members.empty())
+ return IA_AddOldMember;
+ auto MI = find_if(
+ Members, [Name](StringRef Path) { return comparePaths(Name, Path); });
+
+ if (MI == Members.end())
+ return IA_AddOldMember;
+
+ Pos = MI;
+
+ if (Operation == Delete) {
+ if (CountParam && ++MemberCount[Name] != CountParam)
+ return IA_AddOldMember;
+ return IA_Delete;
+ }
+
+ if (Operation == Move)
+ return IA_MoveOldMember;
+
+ if (Operation == ReplaceOrInsert) {
+ if (!OnlyUpdate) {
+ if (RelPos.empty())
+ return IA_AddNewMember;
+ return IA_MoveNewMember;
+ }
+
+ // We could try to optimize this to a fstat, but it is not a common
+ // operation.
+ sys::fs::file_status Status;
+ failIfError(sys::fs::status(*MI, Status), *MI);
+ auto ModTimeOrErr = Member.getLastModified();
+ failIfError(ModTimeOrErr.takeError());
+ if (Status.getLastModificationTime() < ModTimeOrErr.get()) {
+ if (RelPos.empty())
+ return IA_AddOldMember;
+ return IA_MoveOldMember;
+ }
+
+ if (RelPos.empty())
+ return IA_AddNewMember;
+ return IA_MoveNewMember;
+ }
+ llvm_unreachable("No such operation");
+}
+
+// We have to walk this twice and computing it is not trivial, so creating an
+// explicit std::vector is actually fairly efficient.
+static std::vector<NewArchiveMember>
+computeNewArchiveMembers(ArchiveOperation Operation,
+ object::Archive *OldArchive) {
+ std::vector<NewArchiveMember> Ret;
+ std::vector<NewArchiveMember> Moved;
+ int InsertPos = -1;
+ if (OldArchive) {
+ Error Err = Error::success();
+ StringMap<int> MemberCount;
+ for (auto &Child : OldArchive->children(Err)) {
+ int Pos = Ret.size();
+ Expected<StringRef> NameOrErr = Child.getName();
+ failIfError(NameOrErr.takeError());
+ std::string Name = std::string(NameOrErr.get());
+ if (comparePaths(Name, RelPos)) {
+ assert(AddAfter || AddBefore);
+ if (AddBefore)
+ InsertPos = Pos;
+ else
+ InsertPos = Pos + 1;
+ }
+
+ std::vector<StringRef>::iterator MemberI = Members.end();
+ InsertAction Action =
+ computeInsertAction(Operation, Child, Name, MemberI, MemberCount);
+ switch (Action) {
+ case IA_AddOldMember:
+ addChildMember(Ret, Child, /*FlattenArchive=*/Thin);
+ break;
+ case IA_AddNewMember:
+ addMember(Ret, *MemberI);
+ break;
+ case IA_Delete:
+ break;
+ case IA_MoveOldMember:
+ addChildMember(Moved, Child, /*FlattenArchive=*/Thin);
+ break;
+ case IA_MoveNewMember:
+ addMember(Moved, *MemberI);
+ break;
+ }
+ // When processing elements with the count param, we need to preserve the
+ // full members list when iterating over all archive members. For
+ // instance, "llvm-ar dN 2 archive.a member.o" should delete the second
+ // file named member.o it sees; we are not done with member.o the first
+ // time we see it in the archive.
+ if (MemberI != Members.end() && !CountParam)
+ Members.erase(MemberI);
+ }
+ failIfError(std::move(Err));
+ }
+
+ if (Operation == Delete)
+ return Ret;
+
+ if (!RelPos.empty() && InsertPos == -1)
+ fail("insertion point not found");
+
+ if (RelPos.empty())
+ InsertPos = Ret.size();
+
+ assert(unsigned(InsertPos) <= Ret.size());
+ int Pos = InsertPos;
+ for (auto &M : Moved) {
+ Ret.insert(Ret.begin() + Pos, std::move(M));
+ ++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;
+}
+
+static object::Archive::Kind getDefaultForHost() {
+ return Triple(sys::getProcessTriple()).isOSDarwin()
+ ? object::Archive::K_DARWIN
+ : object::Archive::K_GNU;
+}
+
+static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) {
+ auto MemBufferRef = Member.Buf->getMemBufferRef();
+ Expected<std::unique_ptr<object::ObjectFile>> OptionalObject =
+ object::ObjectFile::createObjectFile(MemBufferRef);
+
+ if (OptionalObject)
+ return isa<object::MachOObjectFile>(**OptionalObject)
+ ? object::Archive::K_DARWIN
+ : object::Archive::K_GNU;
+
+ // squelch the error in case we had a non-object file
+ consumeError(OptionalObject.takeError());
+
+ // If we're adding a bitcode file to the archive, detect the Archive kind
+ // based on the target triple.
+ LLVMContext Context;
+ if (identify_magic(MemBufferRef.getBuffer()) == file_magic::bitcode) {
+ if (auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
+ MemBufferRef, file_magic::bitcode, &Context)) {
+ auto &IRObject = cast<object::IRObjectFile>(**ObjOrErr);
+ return Triple(IRObject.getTargetTriple()).isOSDarwin()
+ ? object::Archive::K_DARWIN
+ : object::Archive::K_GNU;
+ } else {
+ // Squelch the error in case this was not a SymbolicFile.
+ consumeError(ObjOrErr.takeError());
+ }
+ }
+
+ return getDefaultForHost();
+}
+
+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);
+
+ object::Archive::Kind Kind;
+ switch (FormatType) {
+ case Default:
+ if (Thin)
+ Kind = object::Archive::K_GNU;
+ else if (OldArchive)
+ Kind = OldArchive->kind();
+ else if (NewMembersP)
+ Kind = !NewMembersP->empty() ? getKindFromMember(NewMembersP->front())
+ : getDefaultForHost();
+ else
+ Kind = !NewMembers.empty() ? getKindFromMember(NewMembers.front())
+ : getDefaultForHost();
+ break;
+ case GNU:
+ Kind = object::Archive::K_GNU;
+ break;
+ case BSD:
+ if (Thin)
+ fail("only the gnu format has a thin mode");
+ Kind = object::Archive::K_BSD;
+ break;
+ case DARWIN:
+ if (Thin)
+ fail("only the gnu format has a thin mode");
+ Kind = object::Archive::K_DARWIN;
+ break;
+ case Unknown:
+ llvm_unreachable("");
+ }
+
+ Error E =
+ writeArchive(ArchiveName, NewMembersP ? *NewMembersP : NewMembers, Symtab,
+ Kind, Deterministic, Thin, std::move(OldArchiveBuf));
+ failIfError(std::move(E), ArchiveName);
+}
+
+static void createSymbolTable(object::Archive *OldArchive) {
+ // When an archive is created or modified, if the s option is given, the
+ // resulting archive will have a current symbol table. If the S option
+ // is given, it will have no symbol table.
+ // In summary, we only need to update the symbol table if we have none.
+ // This is actually very common because of broken build systems that think
+ // they have to run ranlib.
+ if (OldArchive->hasSymbolTable())
+ return;
+
+ if (OldArchive->isThin())
+ Thin = true;
+ performWriteOperation(CreateSymTab, OldArchive, nullptr, nullptr);
+}
+
+static void performOperation(ArchiveOperation Operation,
+ object::Archive *OldArchive,
+ std::unique_ptr<MemoryBuffer> OldArchiveBuf,
+ std::vector<NewArchiveMember> *NewMembers) {
+ switch (Operation) {
+ case Print:
+ case DisplayTable:
+ case Extract:
+ performReadOperation(Operation, OldArchive);
+ return;
+
+ case Delete:
+ case Move:
+ case QuickAppend:
+ case ReplaceOrInsert:
+ performWriteOperation(Operation, OldArchive, std::move(OldArchiveBuf),
+ NewMembers);
+ return;
+ case CreateSymTab:
+ createSymbolTable(OldArchive);
+ return;
+ }
+ llvm_unreachable("Unknown operation.");
+}
+
+static int performOperation(ArchiveOperation Operation,
+ std::vector<NewArchiveMember> *NewMembers) {
+ // Create or open the archive object.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(
+ ArchiveName, /*IsText=*/false, /*RequiresNullTerminator=*/false);
+ std::error_code EC = Buf.getError();
+ if (EC && EC != errc::no_such_file_or_directory)
+ fail("unable to open '" + ArchiveName + "': " + EC.message());
+
+ if (!EC) {
+ Expected<std::unique_ptr<object::Archive>> ArchiveOrError =
+ object::Archive::create(Buf.get()->getMemBufferRef());
+ if (!ArchiveOrError)
+ failIfError(ArchiveOrError.takeError(),
+ "unable to load '" + ArchiveName + "'");
+
+ std::unique_ptr<object::Archive> Archive = std::move(ArchiveOrError.get());
+ if (Archive->isThin())
+ CompareFullPath = true;
+ performOperation(Operation, Archive.get(), std::move(Buf.get()),
+ NewMembers);
+ return 0;
+ }
+
+ assert(EC == errc::no_such_file_or_directory);
+
+ if (!shouldCreateArchive(Operation)) {
+ failIfError(EC, Twine("unable to load '") + ArchiveName + "'");
+ } else {
+ if (!Create) {
+ // Produce a warning if we should and we're creating the archive
+ WithColor::warning(errs(), ToolName)
+ << "creating " << ArchiveName << "\n";
+ }
+ }
+
+ performOperation(Operation, nullptr, nullptr, NewMembers);
+ return 0;
+}
+
+static void runMRIScript() {
+ enum class MRICommand { AddLib, AddMod, Create, CreateThin, Delete, Save, End, Invalid };
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getSTDIN();
+ failIfError(Buf.getError());
+ const MemoryBuffer &Ref = *Buf.get();
+ bool Saved = false;
+ std::vector<NewArchiveMember> NewMembers;
+ ParsingMRIScript = true;
+
+ for (line_iterator I(Ref, /*SkipBlanks*/ false), E; I != E; ++I) {
+ ++MRILineNumber;
+ 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();
+ if (!Rest.empty() && Rest.front() == '"' && Rest.back() == '"')
+ Rest = Rest.drop_front().drop_back();
+ auto Command = StringSwitch<MRICommand>(CommandStr.lower())
+ .Case("addlib", MRICommand::AddLib)
+ .Case("addmod", MRICommand::AddMod)
+ .Case("create", MRICommand::Create)
+ .Case("createthin", MRICommand::CreateThin)
+ .Case("delete", MRICommand::Delete)
+ .Case("save", MRICommand::Save)
+ .Case("end", MRICommand::End)
+ .Default(MRICommand::Invalid);
+
+ switch (Command) {
+ case MRICommand::AddLib: {
+ object::Archive &Lib = readLibrary(Rest);
+ {
+ Error Err = Error::success();
+ for (auto &Member : Lib.children(Err))
+ addChildMember(NewMembers, Member, /*FlattenArchive=*/Thin);
+ failIfError(std::move(Err));
+ }
+ break;
+ }
+ case MRICommand::AddMod:
+ addMember(NewMembers, Rest);
+ break;
+ case MRICommand::CreateThin:
+ Thin = true;
+ LLVM_FALLTHROUGH;
+ case MRICommand::Create:
+ Create = true;
+ if (!ArchiveName.empty())
+ fail("editing multiple archives not supported");
+ if (Saved)
+ fail("file already saved");
+ ArchiveName = std::string(Rest);
+ break;
+ case MRICommand::Delete: {
+ llvm::erase_if(NewMembers, [=](NewArchiveMember &M) {
+ return comparePaths(M.MemberName, Rest);
+ });
+ break;
+ }
+ case MRICommand::Save:
+ Saved = true;
+ break;
+ case MRICommand::End:
+ break;
+ case MRICommand::Invalid:
+ fail("unknown command: " + CommandStr);
+ }
+ }
+
+ ParsingMRIScript = false;
+
+ // Nothing to do if not saved.
+ if (Saved)
+ performOperation(ReplaceOrInsert, &NewMembers);
+ exit(0);
+}
+
+static bool handleGenericOption(StringRef arg) {
+ if (arg == "--help" || arg == "-h") {
+ printHelpMessage();
+ return true;
+ }
+ if (arg == "--version") {
+ cl::PrintVersionMessage();
+ return true;
+ }
+ return false;
+}
+
+static const char *matchFlagWithArg(StringRef Expected,
+ ArrayRef<const char *>::iterator &ArgIt,
+ ArrayRef<const char *> Args) {
+ StringRef Arg = *ArgIt;
+
+ if (Arg.startswith("--"))
+ Arg = Arg.substr(2);
+
+ size_t len = Expected.size();
+ if (Arg == Expected) {
+ if (++ArgIt == Args.end())
+ fail(std::string(Expected) + " requires an argument");
+
+ return *ArgIt;
+ }
+ if (Arg.startswith(Expected) && Arg.size() > len && Arg[len] == '=')
+ return Arg.data() + len + 1;
+
+ return nullptr;
+}
+
+static cl::TokenizerCallback getRspQuoting(ArrayRef<const char *> ArgsArr) {
+ cl::TokenizerCallback Ret =
+ Triple(sys::getProcessTriple()).getOS() == Triple::Win32
+ ? cl::TokenizeWindowsCommandLine
+ : cl::TokenizeGNUCommandLine;
+
+ for (ArrayRef<const char *>::iterator ArgIt = ArgsArr.begin();
+ ArgIt != ArgsArr.end(); ++ArgIt) {
+ if (const char *Match = matchFlagWithArg("rsp-quoting", ArgIt, ArgsArr)) {
+ StringRef MatchRef = Match;
+ if (MatchRef == "posix")
+ Ret = cl::TokenizeGNUCommandLine;
+ else if (MatchRef == "windows")
+ Ret = cl::TokenizeWindowsCommandLine;
+ else
+ fail(std::string("Invalid response file quoting style ") + Match);
+ }
+ }
+
+ return Ret;
+}
+
+static int ar_main(int argc, char **argv) {
+ SmallVector<const char *, 0> Argv(argv + 1, argv + argc);
+ StringSaver Saver(Alloc);
+
+ cl::ExpandResponseFiles(Saver, getRspQuoting(makeArrayRef(argv, argc)), Argv);
+
+ for (ArrayRef<const char *>::iterator ArgIt = Argv.begin();
+ ArgIt != Argv.end(); ++ArgIt) {
+ const char *Match = nullptr;
+
+ if (handleGenericOption(*ArgIt))
+ return 0;
+ if (strcmp(*ArgIt, "--") == 0) {
+ ++ArgIt;
+ for (; ArgIt != Argv.end(); ++ArgIt)
+ PositionalArgs.push_back(*ArgIt);
+ break;
+ }
+
+ if (*ArgIt[0] != '-') {
+ if (Options.empty())
+ Options += *ArgIt;
+ else
+ PositionalArgs.push_back(*ArgIt);
+ continue;
+ }
+
+ if (strcmp(*ArgIt, "-M") == 0) {
+ MRI = true;
+ continue;
+ }
+
+ if (strcmp(*ArgIt, "--thin") == 0) {
+ Thin = true;
+ continue;
+ }
+
+ Match = matchFlagWithArg("format", ArgIt, Argv);
+ if (Match) {
+ FormatType = StringSwitch<Format>(Match)
+ .Case("default", Default)
+ .Case("gnu", GNU)
+ .Case("darwin", DARWIN)
+ .Case("bsd", BSD)
+ .Default(Unknown);
+ if (FormatType == Unknown)
+ fail(std::string("Invalid format ") + Match);
+ continue;
+ }
+
+ if (matchFlagWithArg("plugin", ArgIt, Argv) ||
+ matchFlagWithArg("rsp-quoting", ArgIt, Argv))
+ continue;
+
+ Options += *ArgIt + 1;
+ }
+
+ ArchiveOperation Operation = parseCommandLine();
+ return performOperation(Operation, nullptr);
+}
+
+static int ranlib_main(int argc, char **argv) {
+ bool ArchiveSpecified = false;
+ for (int i = 1; i < argc; ++i) {
+ StringRef arg(argv[i]);
+ if (handleGenericOption(arg)) {
+ return 0;
+ } else if (arg.consume_front("-")) {
+ // Handle the -D/-U flag
+ while (!arg.empty()) {
+ if (arg.front() == 'D') {
+ Deterministic = true;
+ } else if (arg.front() == 'U') {
+ Deterministic = false;
+ } else if (arg.front() == 'h') {
+ printHelpMessage();
+ return 0;
+ } else if (arg.front() == 'v') {
+ cl::PrintVersionMessage();
+ return 0;
+ } else {
+ // TODO: GNU ranlib also supports a -t flag
+ fail("Invalid option: '-" + arg + "'");
+ }
+ arg = arg.drop_front(1);
+ }
+ } else {
+ if (ArchiveSpecified)
+ fail("exactly one archive should be specified");
+ ArchiveSpecified = true;
+ ArchiveName = arg.str();
+ }
+ }
+ if (!ArchiveSpecified) {
+ badUsage("an archive name must be specified");
+ }
+ return performOperation(CreateSymTab, nullptr);
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ ToolName = argv[0];
+
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmParsers();
+
+ Stem = sys::path::stem(ToolName);
+ auto Is = [](StringRef Tool) {
+ // We need to recognize the following filenames.
+ //
+ // Lib.exe -> lib (see D44808, MSBuild runs Lib.exe)
+ // dlltool.exe -> dlltool
+ // arm-pokymllib32-linux-gnueabi-llvm-ar-10 -> ar
+ auto I = Stem.rfind_insensitive(Tool);
+ return I != StringRef::npos &&
+ (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()]));
+ };
+
+ if (Is("dlltool"))
+ return dlltoolDriverMain(makeArrayRef(argv, argc));
+ if (Is("ranlib"))
+ return ranlib_main(argc, argv);
+ if (Is("lib"))
+ return libDriverMain(makeArrayRef(argv, argc));
+ if (Is("ar"))
+ return ar_main(argc, argv);
+
+ fail("not ranlib, ar, lib or dlltool");
+}
diff --git a/contrib/libs/llvm14/tools/llvm-ar/ya.make b/contrib/libs/llvm14/tools/llvm-ar/ya.make
new file mode 100644
index 00000000000..9277ad75f25
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-ar/ya.make
@@ -0,0 +1,59 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/ToolDrivers/llvm-dlltool
+ contrib/libs/llvm14/lib/ToolDrivers/llvm-lib
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-ar
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-ar.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-as/llvm-as.cpp b/contrib/libs/llvm14/tools/llvm-as/llvm-as.cpp
new file mode 100644
index 00000000000..11dad0d9c36
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-as/llvm-as.cpp
@@ -0,0 +1,166 @@
+//===--- llvm-as.cpp - The low-level LLVM assembler -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This utility may be invoked in the following manner:
+// llvm-as --help - Output information about command line switches
+// llvm-as [options] - Read LLVM asm from stdin, write bitcode to stdout
+// llvm-as [options] x.ll - Read LLVM asm from the x.ll file, write bitcode
+// to the x.bc file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/ModuleSummaryIndex.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/SystemUtils.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include <memory>
+using namespace llvm;
+
+cl::OptionCategory AsCat("llvm-as Options");
+
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input .llvm file>"),
+ cl::init("-"));
+
+static cl::opt<std::string> OutputFilename("o",
+ cl::desc("Override output filename"),
+ cl::value_desc("filename"),
+ cl::cat(AsCat));
+
+static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"),
+ cl::cat(AsCat));
+
+static cl::opt<bool> DisableOutput("disable-output", cl::desc("Disable output"),
+ cl::init(false), cl::cat(AsCat));
+
+static cl::opt<bool> EmitModuleHash("module-hash", cl::desc("Emit module hash"),
+ cl::init(false), cl::cat(AsCat));
+
+static cl::opt<bool> DumpAsm("d", cl::desc("Print assembly as parsed"),
+ cl::Hidden, cl::cat(AsCat));
+
+static cl::opt<bool>
+ DisableVerify("disable-verify", cl::Hidden,
+ cl::desc("Do not run verifier on input LLVM (dangerous!)"),
+ cl::cat(AsCat));
+
+static cl::opt<bool> PreserveBitcodeUseListOrder(
+ "preserve-bc-uselistorder",
+ cl::desc("Preserve use-list order when writing LLVM bitcode."),
+ cl::init(true), cl::Hidden, cl::cat(AsCat));
+
+static cl::opt<std::string> ClDataLayout("data-layout",
+ cl::desc("data layout string to use"),
+ cl::value_desc("layout-string"),
+ cl::init(""), cl::cat(AsCat));
+
+static void WriteOutputFile(const Module *M, const ModuleSummaryIndex *Index) {
+ // Infer the output filename if needed.
+ if (OutputFilename.empty()) {
+ if (InputFilename == "-") {
+ OutputFilename = "-";
+ } else {
+ StringRef IFN = InputFilename;
+ OutputFilename = (IFN.endswith(".ll") ? IFN.drop_back(3) : IFN).str();
+ OutputFilename += ".bc";
+ }
+ }
+
+ std::error_code EC;
+ std::unique_ptr<ToolOutputFile> Out(
+ new ToolOutputFile(OutputFilename, EC, sys::fs::OF_None));
+ if (EC) {
+ errs() << EC.message() << '\n';
+ exit(1);
+ }
+
+ if (Force || !CheckBitcodeOutputToConsole(Out->os())) {
+ const ModuleSummaryIndex *IndexToWrite = nullptr;
+ // Don't attempt to write a summary index unless it contains any entries or
+ // has non-zero flags. The latter is used to assemble dummy index files for
+ // skipping modules by distributed ThinLTO backends. Otherwise we get an empty
+ // summary section.
+ if (Index && (Index->begin() != Index->end() || Index->getFlags()))
+ IndexToWrite = Index;
+ if (!IndexToWrite || (M && (!M->empty() || !M->global_empty())))
+ // If we have a non-empty Module, then we write the Module plus
+ // any non-null Index along with it as a per-module Index.
+ // If both are empty, this will give an empty module block, which is
+ // the expected behavior.
+ WriteBitcodeToFile(*M, Out->os(), PreserveBitcodeUseListOrder,
+ IndexToWrite, EmitModuleHash);
+ else
+ // Otherwise, with an empty Module but non-empty Index, we write a
+ // combined index.
+ writeIndexToFile(*IndexToWrite, Out->os());
+ }
+
+ // Declare success.
+ Out->keep();
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ cl::HideUnrelatedOptions(AsCat);
+ cl::ParseCommandLineOptions(argc, argv, "llvm .ll -> .bc assembler\n");
+ LLVMContext Context;
+
+ // Parse the file now...
+ SMDiagnostic Err;
+ auto SetDataLayout = [](StringRef) -> Optional<std::string> {
+ if (ClDataLayout.empty())
+ return None;
+ return ClDataLayout;
+ };
+ ParsedModuleAndIndex ModuleAndIndex;
+ if (DisableVerify) {
+ ModuleAndIndex = parseAssemblyFileWithIndexNoUpgradeDebugInfo(
+ InputFilename, Err, Context, nullptr, SetDataLayout);
+ } else {
+ ModuleAndIndex = parseAssemblyFileWithIndex(InputFilename, Err, Context,
+ nullptr, SetDataLayout);
+ }
+ std::unique_ptr<Module> M = std::move(ModuleAndIndex.Mod);
+ if (!M.get()) {
+ Err.print(argv[0], errs());
+ return 1;
+ }
+ std::unique_ptr<ModuleSummaryIndex> Index = std::move(ModuleAndIndex.Index);
+
+ if (!DisableVerify) {
+ std::string ErrorStr;
+ raw_string_ostream OS(ErrorStr);
+ if (verifyModule(*M.get(), &OS)) {
+ errs() << argv[0]
+ << ": assembly parsed, but does not verify as correct!\n";
+ errs() << OS.str();
+ return 1;
+ }
+ // TODO: Implement and call summary index verifier.
+ }
+
+ if (DumpAsm) {
+ errs() << "Here's the assembly:\n" << *M.get();
+ if (Index.get() && Index->begin() != Index->end())
+ Index->print(errs());
+ }
+
+ if (!DisableOutput)
+ WriteOutputFile(M.get(), Index.get());
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-as/ya.make b/contrib/libs/llvm14/tools/llvm-as/ya.make
new file mode 100644
index 00000000000..e072abfab9b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-as/ya.make
@@ -0,0 +1,42 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-as
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-as.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/contrib/libs/llvm14/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
new file mode 100644
index 00000000000..a238b0cf592
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
@@ -0,0 +1,134 @@
+//===-- llvm-bcanalyzer.cpp - Bitcode Analyzer --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This tool may be invoked in the following manner:
+// llvm-bcanalyzer [options] - Read LLVM bitcode from stdin
+// llvm-bcanalyzer [options] x.bc - Read LLVM bitcode from the x.bc file
+//
+// Options:
+// --help - Output information about command line switches
+// --dump - Dump low-level bitcode structure in readable format
+// --dump-blockinfo - Dump the BLOCKINFO_BLOCK, when used with --dump
+//
+// This tool provides analytical information about a bitcode file. It is
+// intended as an aid to developers of bitcode reading and writing software. It
+// produces on std::out a summary of the bitcode file that shows various
+// statistics about the contents of the file. By default this information is
+// detailed and contains information about individual bitcode blocks and the
+// functions in the module.
+// The tool is also able to print a bitcode file in a straight forward text
+// format that shows the containment and relationships of the information in
+// the bitcode file (-dump option).
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/Bitcode/BitcodeAnalyzer.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+using namespace llvm;
+
+static cl::OptionCategory BCAnalyzerCategory("BC Analyzer Options");
+
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input bitcode>"),
+ cl::init("-"),
+ cl::cat(BCAnalyzerCategory));
+
+static cl::opt<bool> Dump("dump", cl::desc("Dump low level bitcode trace"),
+ cl::cat(BCAnalyzerCategory));
+
+static cl::opt<bool> DumpBlockinfo("dump-blockinfo",
+ cl::desc("Include BLOCKINFO details in low"
+ " level dump"),
+ cl::cat(BCAnalyzerCategory));
+
+//===----------------------------------------------------------------------===//
+// Bitcode specific analysis.
+//===----------------------------------------------------------------------===//
+
+static cl::opt<bool> NoHistogram("disable-histogram",
+ cl::desc("Do not print per-code histogram"),
+ cl::cat(BCAnalyzerCategory));
+
+static cl::opt<bool> NonSymbolic("non-symbolic",
+ cl::desc("Emit numeric info in dump even if"
+ " symbolic info is available"),
+ cl::cat(BCAnalyzerCategory));
+
+static cl::opt<std::string>
+ BlockInfoFilename("block-info",
+ cl::desc("Use the BLOCK_INFO from the given file"),
+ cl::cat(BCAnalyzerCategory));
+
+static cl::opt<bool>
+ ShowBinaryBlobs("show-binary-blobs",
+ cl::desc("Print binary blobs using hex escapes"),
+ cl::cat(BCAnalyzerCategory));
+
+static cl::opt<std::string> CheckHash(
+ "check-hash",
+ cl::desc("Check module hash using the argument as a string table"),
+ cl::cat(BCAnalyzerCategory));
+
+static Error reportError(StringRef Message) {
+ return createStringError(std::errc::illegal_byte_sequence, Message.data());
+}
+
+static Expected<std::unique_ptr<MemoryBuffer>> openBitcodeFile(StringRef Path) {
+ // Read the input file.
+ Expected<std::unique_ptr<MemoryBuffer>> MemBufOrErr =
+ errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Path));
+ if (Error E = MemBufOrErr.takeError())
+ return std::move(E);
+
+ std::unique_ptr<MemoryBuffer> MemBuf = std::move(*MemBufOrErr);
+
+ if (MemBuf->getBufferSize() & 3)
+ return reportError(
+ "Bitcode stream should be a multiple of 4 bytes in length");
+ return std::move(MemBuf);
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ cl::HideUnrelatedOptions({&BCAnalyzerCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "llvm-bcanalyzer file analyzer\n");
+ ExitOnError ExitOnErr("llvm-bcanalyzer: ");
+
+ std::unique_ptr<MemoryBuffer> MB = ExitOnErr(openBitcodeFile(InputFilename));
+ std::unique_ptr<MemoryBuffer> BlockInfoMB = nullptr;
+ if (!BlockInfoFilename.empty())
+ BlockInfoMB = ExitOnErr(openBitcodeFile(BlockInfoFilename));
+
+ BitcodeAnalyzer BA(MB->getBuffer(),
+ BlockInfoMB ? Optional<StringRef>(BlockInfoMB->getBuffer())
+ : None);
+
+ BCDumpOptions O(outs());
+ O.Histogram = !NoHistogram;
+ O.Symbolic = !NonSymbolic;
+ O.ShowBinaryBlobs = ShowBinaryBlobs;
+ O.DumpBlockinfo = DumpBlockinfo;
+
+ ExitOnErr(BA.analyze(
+ Dump ? Optional<BCDumpOptions>(O) : Optional<BCDumpOptions>(None),
+ CheckHash.empty() ? None : Optional<StringRef>(CheckHash)));
+
+ if (Dump)
+ outs() << "\n\n";
+
+ BA.printStats(O, StringRef(InputFilename.getValue()));
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-bcanalyzer/ya.make b/contrib/libs/llvm14/tools/llvm-bcanalyzer/ya.make
new file mode 100644
index 00000000000..6639a21322e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-bcanalyzer/ya.make
@@ -0,0 +1,32 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-bcanalyzer
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-bcanalyzer.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-cat/llvm-cat.cpp b/contrib/libs/llvm14/tools/llvm-cat/llvm-cat.cpp
new file mode 100644
index 00000000000..39848e5d336
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cat/llvm-cat.cpp
@@ -0,0 +1,96 @@
+//===- llvm-cat.cpp - LLVM module concatenation utility -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is for testing features that rely on multi-module bitcode files.
+// It takes a list of input modules and uses them to create a multi-module
+// bitcode file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <vector>
+
+using namespace llvm;
+
+cl::OptionCategory CatCategory("llvm-cat Options");
+
+static cl::opt<bool>
+ BinaryCat("b", cl::desc("Whether to perform binary concatenation"),
+ cl::cat(CatCategory));
+
+static cl::opt<std::string> OutputFilename("o", cl::Required,
+ cl::desc("Output filename"),
+ cl::value_desc("filename"),
+ cl::cat(CatCategory));
+
+static cl::list<std::string> InputFilenames(cl::Positional, cl::ZeroOrMore,
+ cl::desc("<input files>"),
+ cl::cat(CatCategory));
+
+int main(int argc, char **argv) {
+ cl::HideUnrelatedOptions(CatCategory);
+ cl::ParseCommandLineOptions(argc, argv, "Module concatenation");
+
+ ExitOnError ExitOnErr("llvm-cat: ");
+ LLVMContext Context;
+
+ SmallVector<char, 0> Buffer;
+ BitcodeWriter Writer(Buffer);
+ if (BinaryCat) {
+ for (const auto &InputFilename : InputFilenames) {
+ std::unique_ptr<MemoryBuffer> MB = ExitOnErr(
+ errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename)));
+ std::vector<BitcodeModule> Mods = ExitOnErr(getBitcodeModuleList(*MB));
+ for (auto &BitcodeMod : Mods) {
+ llvm::append_range(Buffer, BitcodeMod.getBuffer());
+ Writer.copyStrtab(BitcodeMod.getStrtab());
+ }
+ }
+ } else {
+ // The string table does not own strings added to it, some of which are
+ // owned by the modules; keep them alive until we write the string table.
+ std::vector<std::unique_ptr<Module>> OwnedMods;
+ for (const auto &InputFilename : InputFilenames) {
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context);
+ if (!M) {
+ Err.print(argv[0], errs());
+ return 1;
+ }
+ Writer.writeModule(*M);
+ OwnedMods.push_back(std::move(M));
+ }
+ Writer.writeStrtab();
+ }
+
+ std::error_code EC;
+ raw_fd_ostream OS(OutputFilename, EC, sys::fs::OpenFlags::OF_None);
+ if (EC) {
+ errs() << argv[0] << ": cannot open " << OutputFilename << " for writing: "
+ << EC.message();
+ return 1;
+ }
+
+ OS.write(Buffer.data(), Buffer.size());
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cat/ya.make b/contrib/libs/llvm14/tools/llvm-cat/ya.make
new file mode 100644
index 00000000000..d39a54c54d9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cat/ya.make
@@ -0,0 +1,43 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-cat
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-cat.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
new file mode 100644
index 00000000000..dac2bdab04e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
@@ -0,0 +1,599 @@
+//===- FileAnalysis.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FileAnalysis.h"
+#include "GraphBuilder.h"
+
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrAnalysis.h"
+#include "llvm/MC/MCInstrDesc.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
+
+using Instr = llvm::cfi_verify::FileAnalysis::Instr;
+using LLVMSymbolizer = llvm::symbolize::LLVMSymbolizer;
+
+namespace llvm {
+namespace cfi_verify {
+
+bool IgnoreDWARFFlag;
+
+static cl::opt<bool, true> IgnoreDWARFArg(
+ "ignore-dwarf",
+ cl::desc(
+ "Ignore all DWARF data. This relaxes the requirements for all "
+ "statically linked libraries to have been compiled with '-g', but "
+ "will result in false positives for 'CFI unprotected' instructions."),
+ cl::location(IgnoreDWARFFlag), cl::init(false));
+
+StringRef stringCFIProtectionStatus(CFIProtectionStatus Status) {
+ switch (Status) {
+ case CFIProtectionStatus::PROTECTED:
+ return "PROTECTED";
+ case CFIProtectionStatus::FAIL_NOT_INDIRECT_CF:
+ return "FAIL_NOT_INDIRECT_CF";
+ case CFIProtectionStatus::FAIL_ORPHANS:
+ return "FAIL_ORPHANS";
+ case CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH:
+ return "FAIL_BAD_CONDITIONAL_BRANCH";
+ case CFIProtectionStatus::FAIL_REGISTER_CLOBBERED:
+ return "FAIL_REGISTER_CLOBBERED";
+ case CFIProtectionStatus::FAIL_INVALID_INSTRUCTION:
+ return "FAIL_INVALID_INSTRUCTION";
+ }
+ llvm_unreachable("Attempted to stringify an unknown enum value.");
+}
+
+Expected<FileAnalysis> FileAnalysis::Create(StringRef Filename) {
+ // Open the filename provided.
+ Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
+ object::createBinary(Filename);
+ if (!BinaryOrErr)
+ return BinaryOrErr.takeError();
+
+ // Construct the object and allow it to take ownership of the binary.
+ object::OwningBinary<object::Binary> Binary = std::move(BinaryOrErr.get());
+ FileAnalysis Analysis(std::move(Binary));
+
+ Analysis.Object = dyn_cast<object::ObjectFile>(Analysis.Binary.getBinary());
+ if (!Analysis.Object)
+ return make_error<UnsupportedDisassembly>("Failed to cast object");
+
+ switch (Analysis.Object->getArch()) {
+ case Triple::x86:
+ case Triple::x86_64:
+ case Triple::aarch64:
+ case Triple::aarch64_be:
+ break;
+ default:
+ return make_error<UnsupportedDisassembly>("Unsupported architecture.");
+ }
+
+ Analysis.ObjectTriple = Analysis.Object->makeTriple();
+ Analysis.Features = Analysis.Object->getFeatures();
+
+ // Init the rest of the object.
+ if (auto InitResponse = Analysis.initialiseDisassemblyMembers())
+ return std::move(InitResponse);
+
+ if (auto SectionParseResponse = Analysis.parseCodeSections())
+ return std::move(SectionParseResponse);
+
+ if (auto SymbolTableParseResponse = Analysis.parseSymbolTable())
+ return std::move(SymbolTableParseResponse);
+
+ return std::move(Analysis);
+}
+
+FileAnalysis::FileAnalysis(object::OwningBinary<object::Binary> Binary)
+ : Binary(std::move(Binary)) {}
+
+FileAnalysis::FileAnalysis(const Triple &ObjectTriple,
+ const SubtargetFeatures &Features)
+ : ObjectTriple(ObjectTriple), Features(Features) {}
+
+const Instr *
+FileAnalysis::getPrevInstructionSequential(const Instr &InstrMeta) const {
+ std::map<uint64_t, Instr>::const_iterator KV =
+ Instructions.find(InstrMeta.VMAddress);
+ if (KV == Instructions.end() || KV == Instructions.begin())
+ return nullptr;
+
+ if (!(--KV)->second.Valid)
+ return nullptr;
+
+ return &KV->second;
+}
+
+const Instr *
+FileAnalysis::getNextInstructionSequential(const Instr &InstrMeta) const {
+ std::map<uint64_t, Instr>::const_iterator KV =
+ Instructions.find(InstrMeta.VMAddress);
+ if (KV == Instructions.end() || ++KV == Instructions.end())
+ return nullptr;
+
+ if (!KV->second.Valid)
+ return nullptr;
+
+ return &KV->second;
+}
+
+bool FileAnalysis::usesRegisterOperand(const Instr &InstrMeta) const {
+ for (const auto &Operand : InstrMeta.Instruction) {
+ if (Operand.isReg())
+ return true;
+ }
+ return false;
+}
+
+const Instr *FileAnalysis::getInstruction(uint64_t Address) const {
+ const auto &InstrKV = Instructions.find(Address);
+ if (InstrKV == Instructions.end())
+ return nullptr;
+
+ return &InstrKV->second;
+}
+
+const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const {
+ const auto &InstrKV = Instructions.find(Address);
+ assert(InstrKV != Instructions.end() && "Address doesn't exist.");
+ return InstrKV->second;
+}
+
+bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const {
+ const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
+ 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.contains(Target);
+}
+
+bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const {
+ if (!InstrMeta.Valid)
+ return false;
+
+ if (isCFITrap(InstrMeta))
+ return false;
+
+ const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
+ if (InstrDesc.mayAffectControlFlow(InstrMeta.Instruction, *RegisterInfo))
+ return InstrDesc.isConditionalBranch();
+
+ return true;
+}
+
+const Instr *
+FileAnalysis::getDefiniteNextInstruction(const Instr &InstrMeta) const {
+ if (!InstrMeta.Valid)
+ return nullptr;
+
+ if (isCFITrap(InstrMeta))
+ return nullptr;
+
+ const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
+ const Instr *NextMetaPtr;
+ if (InstrDesc.mayAffectControlFlow(InstrMeta.Instruction, *RegisterInfo)) {
+ if (InstrDesc.isConditionalBranch())
+ return nullptr;
+
+ uint64_t Target;
+ if (!MIA->evaluateBranch(InstrMeta.Instruction, InstrMeta.VMAddress,
+ InstrMeta.InstructionSize, Target))
+ return nullptr;
+
+ NextMetaPtr = getInstruction(Target);
+ } else {
+ NextMetaPtr =
+ getInstruction(InstrMeta.VMAddress + InstrMeta.InstructionSize);
+ }
+
+ if (!NextMetaPtr || !NextMetaPtr->Valid)
+ return nullptr;
+
+ return NextMetaPtr;
+}
+
+std::set<const Instr *>
+FileAnalysis::getDirectControlFlowXRefs(const Instr &InstrMeta) const {
+ std::set<const Instr *> CFCrossReferences;
+ const Instr *PrevInstruction = getPrevInstructionSequential(InstrMeta);
+
+ if (PrevInstruction && canFallThrough(*PrevInstruction))
+ CFCrossReferences.insert(PrevInstruction);
+
+ const auto &TargetRefsKV = StaticBranchTargetings.find(InstrMeta.VMAddress);
+ if (TargetRefsKV == StaticBranchTargetings.end())
+ return CFCrossReferences;
+
+ for (uint64_t SourceInstrAddress : TargetRefsKV->second) {
+ const auto &SourceInstrKV = Instructions.find(SourceInstrAddress);
+ if (SourceInstrKV == Instructions.end()) {
+ errs() << "Failed to find source instruction at address "
+ << format_hex(SourceInstrAddress, 2)
+ << " for the cross-reference to instruction at address "
+ << format_hex(InstrMeta.VMAddress, 2) << ".\n";
+ continue;
+ }
+
+ CFCrossReferences.insert(&SourceInstrKV->second);
+ }
+
+ return CFCrossReferences;
+}
+
+const std::set<object::SectionedAddress> &
+FileAnalysis::getIndirectInstructions() const {
+ return IndirectInstructions;
+}
+
+const MCRegisterInfo *FileAnalysis::getRegisterInfo() const {
+ return RegisterInfo.get();
+}
+
+const MCInstrInfo *FileAnalysis::getMCInstrInfo() const { return MII.get(); }
+
+const MCInstrAnalysis *FileAnalysis::getMCInstrAnalysis() const {
+ return MIA.get();
+}
+
+Expected<DIInliningInfo>
+FileAnalysis::symbolizeInlinedCode(object::SectionedAddress Address) {
+ assert(Symbolizer != nullptr && "Symbolizer is invalid.");
+
+ return Symbolizer->symbolizeInlinedCode(std::string(Object->getFileName()),
+ Address);
+}
+
+CFIProtectionStatus
+FileAnalysis::validateCFIProtection(const GraphResult &Graph) const {
+ const Instr *InstrMetaPtr = getInstruction(Graph.BaseAddress);
+ if (!InstrMetaPtr)
+ return CFIProtectionStatus::FAIL_INVALID_INSTRUCTION;
+
+ const auto &InstrDesc = MII->get(InstrMetaPtr->Instruction.getOpcode());
+ if (!InstrDesc.mayAffectControlFlow(InstrMetaPtr->Instruction, *RegisterInfo))
+ return CFIProtectionStatus::FAIL_NOT_INDIRECT_CF;
+
+ if (!usesRegisterOperand(*InstrMetaPtr))
+ return CFIProtectionStatus::FAIL_NOT_INDIRECT_CF;
+
+ if (!Graph.OrphanedNodes.empty())
+ return CFIProtectionStatus::FAIL_ORPHANS;
+
+ for (const auto &BranchNode : Graph.ConditionalBranchNodes) {
+ if (!BranchNode.CFIProtection)
+ return CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH;
+ }
+
+ if (indirectCFOperandClobber(Graph) != Graph.BaseAddress)
+ return CFIProtectionStatus::FAIL_REGISTER_CLOBBERED;
+
+ return CFIProtectionStatus::PROTECTED;
+}
+
+uint64_t FileAnalysis::indirectCFOperandClobber(const GraphResult &Graph) const {
+ assert(Graph.OrphanedNodes.empty() && "Orphaned nodes should be empty.");
+
+ // Get the set of registers we must check to ensure they're not clobbered.
+ const Instr &IndirectCF = getInstructionOrDie(Graph.BaseAddress);
+ DenseSet<unsigned> RegisterNumbers;
+ for (const auto &Operand : IndirectCF.Instruction) {
+ if (Operand.isReg())
+ RegisterNumbers.insert(Operand.getReg());
+ }
+ assert(RegisterNumbers.size() && "Zero register operands on indirect CF.");
+
+ // Now check all branches to indirect CFs and ensure no clobbering happens.
+ for (const auto &Branch : Graph.ConditionalBranchNodes) {
+ uint64_t Node;
+ if (Branch.IndirectCFIsOnTargetPath)
+ Node = Branch.Target;
+ else
+ Node = Branch.Fallthrough;
+
+ // Some architectures (e.g., AArch64) cannot load in an indirect branch, so
+ // we allow them one load.
+ bool canLoad = !MII->get(IndirectCF.Instruction.getOpcode()).mayLoad();
+
+ // We walk backwards from the indirect CF. It is the last node returned by
+ // Graph.flattenAddress, so we skip it since we already handled it.
+ DenseSet<unsigned> CurRegisterNumbers = RegisterNumbers;
+ std::vector<uint64_t> Nodes = Graph.flattenAddress(Node);
+ for (auto I = Nodes.rbegin() + 1, E = Nodes.rend(); I != E; ++I) {
+ Node = *I;
+ const Instr &NodeInstr = getInstructionOrDie(Node);
+ const auto &InstrDesc = MII->get(NodeInstr.Instruction.getOpcode());
+
+ for (auto RI = CurRegisterNumbers.begin(), RE = CurRegisterNumbers.end();
+ RI != RE; ++RI) {
+ unsigned RegNum = *RI;
+ if (InstrDesc.hasDefOfPhysReg(NodeInstr.Instruction, RegNum,
+ *RegisterInfo)) {
+ if (!canLoad || !InstrDesc.mayLoad())
+ return Node;
+ canLoad = false;
+ CurRegisterNumbers.erase(RI);
+ // Add the registers this load reads to those we check for clobbers.
+ for (unsigned i = InstrDesc.getNumDefs(),
+ e = InstrDesc.getNumOperands(); i != e; i++) {
+ const auto &Operand = NodeInstr.Instruction.getOperand(i);
+ if (Operand.isReg())
+ CurRegisterNumbers.insert(Operand.getReg());
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return Graph.BaseAddress;
+}
+
+void FileAnalysis::printInstruction(const Instr &InstrMeta,
+ raw_ostream &OS) const {
+ Printer->printInst(&InstrMeta.Instruction, 0, "", *SubtargetInfo.get(), OS);
+}
+
+Error FileAnalysis::initialiseDisassemblyMembers() {
+ std::string TripleName = ObjectTriple.getTriple();
+ ArchName = "";
+ MCPU = "";
+ std::string ErrorString;
+
+ LLVMSymbolizer::Options Opt;
+ Opt.UseSymbolTable = false;
+ Symbolizer.reset(new LLVMSymbolizer(Opt));
+
+ ObjectTarget =
+ TargetRegistry::lookupTarget(ArchName, ObjectTriple, ErrorString);
+ if (!ObjectTarget)
+ return make_error<UnsupportedDisassembly>(
+ (Twine("Couldn't find target \"") + ObjectTriple.getTriple() +
+ "\", failed with error: " + ErrorString)
+ .str());
+
+ RegisterInfo.reset(ObjectTarget->createMCRegInfo(TripleName));
+ if (!RegisterInfo)
+ return make_error<UnsupportedDisassembly>(
+ "Failed to initialise RegisterInfo.");
+
+ MCTargetOptions MCOptions;
+ AsmInfo.reset(
+ ObjectTarget->createMCAsmInfo(*RegisterInfo, TripleName, MCOptions));
+ if (!AsmInfo)
+ return make_error<UnsupportedDisassembly>("Failed to initialise AsmInfo.");
+
+ SubtargetInfo.reset(ObjectTarget->createMCSubtargetInfo(
+ TripleName, MCPU, Features.getString()));
+ if (!SubtargetInfo)
+ return make_error<UnsupportedDisassembly>(
+ "Failed to initialise SubtargetInfo.");
+
+ MII.reset(ObjectTarget->createMCInstrInfo());
+ if (!MII)
+ return make_error<UnsupportedDisassembly>("Failed to initialise MII.");
+
+ Context.reset(new MCContext(Triple(TripleName), AsmInfo.get(),
+ RegisterInfo.get(), SubtargetInfo.get()));
+
+ Disassembler.reset(
+ ObjectTarget->createMCDisassembler(*SubtargetInfo, *Context));
+
+ if (!Disassembler)
+ return make_error<UnsupportedDisassembly>(
+ "No disassembler available for target");
+
+ MIA.reset(ObjectTarget->createMCInstrAnalysis(MII.get()));
+
+ Printer.reset(ObjectTarget->createMCInstPrinter(
+ ObjectTriple, AsmInfo->getAssemblerDialect(), *AsmInfo, *MII,
+ *RegisterInfo));
+
+ return Error::success();
+}
+
+Error FileAnalysis::parseCodeSections() {
+ if (!IgnoreDWARFFlag) {
+ std::unique_ptr<DWARFContext> DWARF = DWARFContext::create(*Object);
+ if (!DWARF)
+ return make_error<StringError>("Could not create DWARF information.",
+ inconvertibleErrorCode());
+
+ bool LineInfoValid = false;
+
+ for (auto &Unit : DWARF->compile_units()) {
+ const auto &LineTable = DWARF->getLineTableForUnit(Unit.get());
+ if (LineTable && !LineTable->Rows.empty()) {
+ LineInfoValid = true;
+ break;
+ }
+ }
+
+ if (!LineInfoValid)
+ return make_error<StringError>(
+ "DWARF line information missing. Did you compile with '-g'?",
+ inconvertibleErrorCode());
+ }
+
+ for (const object::SectionRef &Section : Object->sections()) {
+ // Ensure only executable sections get analysed.
+ if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR))
+ continue;
+
+ // Avoid checking the PLT since it produces spurious failures on AArch64
+ // when ignoring DWARF data.
+ Expected<StringRef> NameOrErr = Section.getName();
+ if (NameOrErr && *NameOrErr == ".plt")
+ continue;
+ consumeError(NameOrErr.takeError());
+
+ Expected<StringRef> Contents = Section.getContents();
+ if (!Contents)
+ return Contents.takeError();
+ ArrayRef<uint8_t> SectionBytes = arrayRefFromStringRef(*Contents);
+
+ parseSectionContents(SectionBytes,
+ {Section.getAddress(), Section.getIndex()});
+ }
+ return Error::success();
+}
+
+void FileAnalysis::parseSectionContents(ArrayRef<uint8_t> SectionBytes,
+ object::SectionedAddress Address) {
+ assert(Symbolizer && "Symbolizer is uninitialised.");
+ MCInst Instruction;
+ Instr InstrMeta;
+ uint64_t InstructionSize;
+
+ for (uint64_t Byte = 0; Byte < SectionBytes.size();) {
+ bool ValidInstruction =
+ Disassembler->getInstruction(Instruction, InstructionSize,
+ SectionBytes.drop_front(Byte), 0,
+ outs()) == MCDisassembler::Success;
+
+ Byte += InstructionSize;
+
+ uint64_t VMAddress = Address.Address + Byte - InstructionSize;
+ InstrMeta.Instruction = Instruction;
+ InstrMeta.VMAddress = VMAddress;
+ InstrMeta.InstructionSize = InstructionSize;
+ InstrMeta.Valid = ValidInstruction;
+
+ addInstruction(InstrMeta);
+
+ if (!ValidInstruction)
+ continue;
+
+ // Skip additional parsing for instructions that do not affect the control
+ // flow.
+ const auto &InstrDesc = MII->get(Instruction.getOpcode());
+ if (!InstrDesc.mayAffectControlFlow(Instruction, *RegisterInfo))
+ continue;
+
+ uint64_t Target;
+ if (MIA->evaluateBranch(Instruction, VMAddress, InstructionSize, Target)) {
+ // If the target can be evaluated, it's not indirect.
+ StaticBranchTargetings[Target].push_back(VMAddress);
+ continue;
+ }
+
+ if (!usesRegisterOperand(InstrMeta))
+ continue;
+
+ if (InstrDesc.isReturn())
+ continue;
+
+ // Check if this instruction exists in the range of the DWARF metadata.
+ if (!IgnoreDWARFFlag) {
+ auto LineInfo =
+ Symbolizer->symbolizeCode(std::string(Object->getFileName()),
+ {VMAddress, Address.SectionIndex});
+ if (!LineInfo) {
+ handleAllErrors(LineInfo.takeError(), [](const ErrorInfoBase &E) {
+ errs() << "Symbolizer failed to get line: " << E.message() << "\n";
+ });
+ continue;
+ }
+
+ if (LineInfo->FileName == DILineInfo::BadString)
+ continue;
+ }
+
+ IndirectInstructions.insert({VMAddress, Address.SectionIndex});
+ }
+}
+
+void FileAnalysis::addInstruction(const Instr &Instruction) {
+ const auto &KV =
+ Instructions.insert(std::make_pair(Instruction.VMAddress, Instruction));
+ if (!KV.second) {
+ errs() << "Failed to add instruction at address "
+ << format_hex(Instruction.VMAddress, 2)
+ << ": Instruction at this address already exists.\n";
+ exit(EXIT_FAILURE);
+ }
+}
+
+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.contains(*SymNameOrErr)) {
+ 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()) {
+ if (!Addr.first)
+ continue;
+ object::SymbolRef Sym(*Addr.first, Object);
+ auto SymNameOrErr = Sym.getName();
+ if (!SymNameOrErr)
+ consumeError(SymNameOrErr.takeError());
+ else if (TrapOnFailFunctions.contains(*SymNameOrErr))
+ TrapOnFailFunctionAddresses.insert(Addr.second);
+ }
+ }
+ return Error::success();
+}
+
+UnsupportedDisassembly::UnsupportedDisassembly(StringRef Text)
+ : Text(std::string(Text)) {}
+
+char UnsupportedDisassembly::ID;
+void UnsupportedDisassembly::log(raw_ostream &OS) const {
+ OS << "Could not initialise disassembler: " << Text;
+}
+
+std::error_code UnsupportedDisassembly::convertToErrorCode() const {
+ return std::error_code();
+}
+
+} // namespace cfi_verify
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/FileAnalysis.h b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/FileAnalysis.h
new file mode 100644
index 00000000000..8fd687d1b16
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/FileAnalysis.h
@@ -0,0 +1,248 @@
+//===- FileAnalysis.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CFI_VERIFY_FILE_ANALYSIS_H
+#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"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrAnalysis.h"
+#include "llvm/MC/MCInstrDesc.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <functional>
+#include <set>
+#include <string>
+#include <unordered_map>
+
+namespace llvm {
+namespace cfi_verify {
+
+struct GraphResult;
+
+extern bool IgnoreDWARFFlag;
+
+enum class CFIProtectionStatus {
+ // This instruction is protected by CFI.
+ PROTECTED,
+ // The instruction is not an indirect control flow instruction, and thus
+ // shouldn't be protected.
+ FAIL_NOT_INDIRECT_CF,
+ // There is a path to the instruction that was unexpected.
+ FAIL_ORPHANS,
+ // There is a path to the instruction from a conditional branch that does not
+ // properly check the destination for this vcall/icall.
+ FAIL_BAD_CONDITIONAL_BRANCH,
+ // One of the operands of the indirect CF instruction is modified between the
+ // CFI-check and execution.
+ FAIL_REGISTER_CLOBBERED,
+ // The instruction referenced does not exist. This normally indicates an
+ // error in the program, where you try and validate a graph that was created
+ // in a different FileAnalysis object.
+ FAIL_INVALID_INSTRUCTION,
+};
+
+StringRef stringCFIProtectionStatus(CFIProtectionStatus Status);
+
+// Disassembler and analysis tool for machine code files. Keeps track of non-
+// sequential control flows, including indirect control flow instructions.
+class FileAnalysis {
+public:
+ // A metadata struct for an instruction.
+ struct Instr {
+ uint64_t VMAddress; // Virtual memory address of this instruction.
+ MCInst Instruction; // Instruction.
+ uint64_t InstructionSize; // Size of this instruction.
+ bool Valid; // Is this a valid instruction? If false, Instr::Instruction is
+ // undefined.
+ };
+
+ // Construct a FileAnalysis from a file path.
+ static Expected<FileAnalysis> Create(StringRef Filename);
+
+ // Construct and take ownership of the supplied object. Do not use this
+ // constructor, prefer to use FileAnalysis::Create instead.
+ FileAnalysis(object::OwningBinary<object::Binary> Binary);
+ FileAnalysis() = delete;
+ FileAnalysis(const FileAnalysis &) = delete;
+ FileAnalysis(FileAnalysis &&Other) = default;
+
+ // Returns the instruction at the provided address. Returns nullptr if there
+ // is no instruction at the provided address.
+ const Instr *getInstruction(uint64_t Address) const;
+
+ // Returns the instruction at the provided adress, dying if the instruction is
+ // not found.
+ const Instr &getInstructionOrDie(uint64_t Address) const;
+
+ // Returns a pointer to the previous/next instruction in sequence,
+ // respectively. Returns nullptr if the next/prev instruction doesn't exist,
+ // or if the provided instruction doesn't exist.
+ const Instr *getPrevInstructionSequential(const Instr &InstrMeta) const;
+ const Instr *getNextInstructionSequential(const Instr &InstrMeta) const;
+
+ // 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
+ // branches or calls.
+ bool canFallThrough(const Instr &InstrMeta) const;
+
+ // Returns the definitive next instruction. This is different from the next
+ // instruction sequentially as it will follow unconditional branches (assuming
+ // they can be resolved at compile time, i.e. not indirect). This method
+ // returns nullptr if the provided instruction does not transfer control flow
+ // to exactly one instruction that is known deterministically at compile time.
+ // Also returns nullptr if the deterministic target does not exist in this
+ // file.
+ const Instr *getDefiniteNextInstruction(const Instr &InstrMeta) const;
+
+ // Get a list of deterministic control flows that lead to the provided
+ // instruction. This list includes all static control flow cross-references as
+ // well as the previous instruction if it can fall through.
+ std::set<const Instr *>
+ getDirectControlFlowXRefs(const Instr &InstrMeta) const;
+
+ // Returns whether this instruction uses a register operand.
+ bool usesRegisterOperand(const Instr &InstrMeta) const;
+
+ // Returns the list of indirect instructions.
+ const std::set<object::SectionedAddress> &getIndirectInstructions() const;
+
+ const MCRegisterInfo *getRegisterInfo() const;
+ const MCInstrInfo *getMCInstrInfo() const;
+ const MCInstrAnalysis *getMCInstrAnalysis() const;
+
+ // Returns the inlining information for the provided address.
+ Expected<DIInliningInfo>
+ symbolizeInlinedCode(object::SectionedAddress Address);
+
+ // Returns whether the provided Graph represents a protected indirect control
+ // flow instruction in this file.
+ CFIProtectionStatus validateCFIProtection(const GraphResult &Graph) const;
+
+ // Returns the first place the operand register is clobbered between the CFI-
+ // check and the indirect CF instruction execution. We do this by walking
+ // backwards from the indirect CF and ensuring there is at most one load
+ // involving the operand register (which is the indirect CF itself on x86).
+ // If the register is not modified, returns the address of the indirect CF
+ // instruction. The result is undefined if the provided graph does not fall
+ // under either the FAIL_REGISTER_CLOBBERED or PROTECTED status (see
+ // CFIProtectionStatus).
+ uint64_t indirectCFOperandClobber(const GraphResult& Graph) const;
+
+ // Prints an instruction to the provided stream using this object's pretty-
+ // printers.
+ void printInstruction(const Instr &InstrMeta, raw_ostream &OS) const;
+
+protected:
+ // Construct a blank object with the provided triple and features. Used in
+ // testing, where a sub class will dependency inject protected methods to
+ // allow analysis of raw binary, without requiring a fully valid ELF file.
+ FileAnalysis(const Triple &ObjectTriple, const SubtargetFeatures &Features);
+
+ // Add an instruction to this object.
+ void addInstruction(const Instr &Instruction);
+
+ // Disassemble and parse the provided bytes into this object. Instruction
+ // address calculation is done relative to the provided SectionAddress.
+ void parseSectionContents(ArrayRef<uint8_t> SectionBytes,
+ object::SectionedAddress Address);
+
+ // Constructs and initialises members required for disassembly.
+ Error initialiseDisassemblyMembers();
+
+ // Parses code sections from the internal object file. Saves them into the
+ // 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;
+ const object::ObjectFile *Object = nullptr;
+ Triple ObjectTriple;
+ std::string ArchName;
+ std::string MCPU;
+ const Target *ObjectTarget = nullptr;
+ SubtargetFeatures Features;
+
+ // Members required for disassembly.
+ std::unique_ptr<const MCRegisterInfo> RegisterInfo;
+ std::unique_ptr<const MCAsmInfo> AsmInfo;
+ std::unique_ptr<MCSubtargetInfo> SubtargetInfo;
+ std::unique_ptr<const MCInstrInfo> MII;
+ std::unique_ptr<MCContext> Context;
+ std::unique_ptr<const MCDisassembler> Disassembler;
+ std::unique_ptr<const MCInstrAnalysis> MIA;
+ std::unique_ptr<MCInstPrinter> Printer;
+
+ // Symbolizer used for debug information parsing.
+ std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer;
+
+ // A mapping between the virtual memory address to the instruction metadata
+ // struct. TODO(hctim): Reimplement this as a sorted vector to avoid per-
+ // insertion allocation.
+ std::map<uint64_t, Instr> Instructions;
+
+ // Contains a mapping between a specific address, and a list of instructions
+ // that use this address as a branch target (including call instructions).
+ DenseMap<uint64_t, std::vector<uint64_t>> StaticBranchTargetings;
+
+ // A list of addresses of indirect control flow instructions.
+ std::set<object::SectionedAddress> IndirectInstructions;
+
+ // The addresses of functions that will trap on CFI violations.
+ SmallSet<uint64_t, 4> TrapOnFailFunctionAddresses;
+};
+
+class UnsupportedDisassembly : public ErrorInfo<UnsupportedDisassembly> {
+public:
+ static char ID;
+ std::string Text;
+
+ UnsupportedDisassembly(StringRef Text);
+
+ void log(raw_ostream &OS) const override;
+ std::error_code convertToErrorCode() const override;
+};
+
+} // namespace cfi_verify
+} // namespace llvm
+
+#endif // LLVM_CFI_VERIFY_FILE_ANALYSIS_H
diff --git a/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/GraphBuilder.cpp b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
new file mode 100644
index 00000000000..88fbbdf6b2b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
@@ -0,0 +1,339 @@
+//===- GraphBuilder.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "GraphBuilder.h"
+
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrAnalysis.h"
+#include "llvm/MC/MCInstrDesc.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
+
+using Instr = llvm::cfi_verify::FileAnalysis::Instr;
+
+namespace llvm {
+namespace cfi_verify {
+
+uint64_t SearchLengthForUndef;
+uint64_t SearchLengthForConditionalBranch;
+
+static cl::opt<uint64_t, true> SearchLengthForUndefArg(
+ "search-length-undef",
+ cl::desc("Specify the maximum amount of instructions "
+ "to inspect when searching for an undefined "
+ "instruction from a conditional branch."),
+ cl::location(SearchLengthForUndef), cl::init(2));
+
+static cl::opt<uint64_t, true> SearchLengthForConditionalBranchArg(
+ "search-length-cb",
+ cl::desc("Specify the maximum amount of instructions "
+ "to inspect when searching for a conditional "
+ "branch from an indirect control flow."),
+ cl::location(SearchLengthForConditionalBranch), cl::init(20));
+
+std::vector<uint64_t> GraphResult::flattenAddress(uint64_t Address) const {
+ std::vector<uint64_t> Addresses;
+
+ auto It = IntermediateNodes.find(Address);
+ Addresses.push_back(Address);
+
+ while (It != IntermediateNodes.end()) {
+ Addresses.push_back(It->second);
+ It = IntermediateNodes.find(It->second);
+ }
+ return Addresses;
+}
+
+void printPairToDOT(const FileAnalysis &Analysis, raw_ostream &OS,
+ uint64_t From, uint64_t To) {
+ OS << " \"" << format_hex(From, 2) << ": ";
+ Analysis.printInstruction(Analysis.getInstructionOrDie(From), OS);
+ OS << "\" -> \"" << format_hex(To, 2) << ": ";
+ Analysis.printInstruction(Analysis.getInstructionOrDie(To), OS);
+ OS << "\"\n";
+}
+
+void GraphResult::printToDOT(const FileAnalysis &Analysis,
+ raw_ostream &OS) const {
+ std::map<uint64_t, uint64_t> SortedIntermediateNodes(
+ IntermediateNodes.begin(), IntermediateNodes.end());
+ OS << "digraph graph_" << format_hex(BaseAddress, 2) << " {\n";
+ for (const auto &KV : SortedIntermediateNodes)
+ printPairToDOT(Analysis, OS, KV.first, KV.second);
+
+ for (auto &BranchNode : ConditionalBranchNodes) {
+ for (auto &V : {BranchNode.Target, BranchNode.Fallthrough})
+ printPairToDOT(Analysis, OS, BranchNode.Address, V);
+ }
+ OS << "}\n";
+}
+
+GraphResult GraphBuilder::buildFlowGraph(const FileAnalysis &Analysis,
+ object::SectionedAddress Address) {
+ GraphResult Result;
+ Result.BaseAddress = Address.Address;
+ DenseSet<uint64_t> OpenedNodes;
+
+ const auto &IndirectInstructions = Analysis.getIndirectInstructions();
+
+ // check that IndirectInstructions contains specified Address
+ if (IndirectInstructions.find(Address) == IndirectInstructions.end()) {
+ return Result;
+ }
+
+ buildFlowGraphImpl(Analysis, OpenedNodes, Result, Address.Address, 0);
+ return Result;
+}
+
+void GraphBuilder::buildFlowsToUndefined(const FileAnalysis &Analysis,
+ GraphResult &Result,
+ ConditionalBranchNode &BranchNode,
+ const Instr &BranchInstrMeta) {
+ assert(SearchLengthForUndef > 0 &&
+ "Search length for undefined flow must be greater than zero.");
+
+ // Start setting up the next node in the block.
+ uint64_t NextAddress = 0;
+ const Instr *NextMetaPtr;
+
+ // Find out the next instruction in the block and add it to the new
+ // node.
+ if (BranchNode.Target && !BranchNode.Fallthrough) {
+ // We know the target of the branch, find the fallthrough.
+ NextMetaPtr = Analysis.getNextInstructionSequential(BranchInstrMeta);
+ if (!NextMetaPtr) {
+ errs() << "Failed to get next instruction from "
+ << format_hex(BranchNode.Address, 2) << ".\n";
+ return;
+ }
+
+ NextAddress = NextMetaPtr->VMAddress;
+ BranchNode.Fallthrough =
+ NextMetaPtr->VMAddress; // Add the new node to the branch head.
+ } else if (BranchNode.Fallthrough && !BranchNode.Target) {
+ // We already know the fallthrough, evaluate the target.
+ uint64_t Target;
+ if (!Analysis.getMCInstrAnalysis()->evaluateBranch(
+ BranchInstrMeta.Instruction, BranchInstrMeta.VMAddress,
+ BranchInstrMeta.InstructionSize, Target)) {
+ errs() << "Failed to get branch target for conditional branch at address "
+ << format_hex(BranchInstrMeta.VMAddress, 2) << ".\n";
+ return;
+ }
+
+ // Resolve the meta pointer for the target of this branch.
+ NextMetaPtr = Analysis.getInstruction(Target);
+ if (!NextMetaPtr) {
+ errs() << "Failed to find instruction at address "
+ << format_hex(Target, 2) << ".\n";
+ return;
+ }
+
+ NextAddress = Target;
+ BranchNode.Target =
+ NextMetaPtr->VMAddress; // Add the new node to the branch head.
+ } else {
+ errs() << "ControlBranchNode supplied to buildFlowsToUndefined should "
+ "provide Target xor Fallthrough.\n";
+ return;
+ }
+
+ uint64_t CurrentAddress = NextAddress;
+ const Instr *CurrentMetaPtr = NextMetaPtr;
+
+ // Now the branch head has been set properly, complete the rest of the block.
+ for (uint64_t i = 1; i < SearchLengthForUndef; ++i) {
+ // Check to see whether the block should die.
+ if (Analysis.isCFITrap(*CurrentMetaPtr)) {
+ BranchNode.CFIProtection = true;
+ return;
+ }
+
+ // Find the metadata of the next instruction.
+ NextMetaPtr = Analysis.getDefiniteNextInstruction(*CurrentMetaPtr);
+ if (!NextMetaPtr)
+ return;
+
+ // Setup the next node.
+ NextAddress = NextMetaPtr->VMAddress;
+
+ // Add this as an intermediate.
+ Result.IntermediateNodes[CurrentAddress] = NextAddress;
+
+ // Move the 'current' pointers to the new tail of the block.
+ CurrentMetaPtr = NextMetaPtr;
+ CurrentAddress = NextAddress;
+ }
+
+ // Final check of the last thing we added to the block.
+ if (Analysis.isCFITrap(*CurrentMetaPtr))
+ BranchNode.CFIProtection = true;
+}
+
+void GraphBuilder::buildFlowGraphImpl(const FileAnalysis &Analysis,
+ DenseSet<uint64_t> &OpenedNodes,
+ GraphResult &Result, uint64_t Address,
+ uint64_t Depth) {
+ // If we've exceeded the flow length, terminate.
+ if (Depth >= SearchLengthForConditionalBranch) {
+ Result.OrphanedNodes.push_back(Address);
+ return;
+ }
+
+ // Ensure this flow is acyclic.
+ if (OpenedNodes.count(Address))
+ Result.OrphanedNodes.push_back(Address);
+
+ // If this flow is already explored, stop here.
+ if (Result.IntermediateNodes.count(Address))
+ return;
+
+ // Get the metadata for the node instruction.
+ const auto &InstrMetaPtr = Analysis.getInstruction(Address);
+ if (!InstrMetaPtr) {
+ errs() << "Failed to build flow graph for instruction at address "
+ << format_hex(Address, 2) << ".\n";
+ Result.OrphanedNodes.push_back(Address);
+ return;
+ }
+ const auto &ChildMeta = *InstrMetaPtr;
+
+ OpenedNodes.insert(Address);
+ std::set<const Instr *> CFCrossRefs =
+ Analysis.getDirectControlFlowXRefs(ChildMeta);
+
+ bool HasValidCrossRef = false;
+
+ for (const auto *ParentMetaPtr : CFCrossRefs) {
+ assert(ParentMetaPtr && "CFCrossRefs returned nullptr.");
+ const auto &ParentMeta = *ParentMetaPtr;
+ const auto &ParentDesc =
+ Analysis.getMCInstrInfo()->get(ParentMeta.Instruction.getOpcode());
+
+ if (!ParentDesc.mayAffectControlFlow(ParentMeta.Instruction,
+ *Analysis.getRegisterInfo())) {
+ // If this cross reference doesn't affect CF, continue the graph.
+ buildFlowGraphImpl(Analysis, OpenedNodes, Result, ParentMeta.VMAddress,
+ Depth + 1);
+ Result.IntermediateNodes[ParentMeta.VMAddress] = Address;
+ HasValidCrossRef = true;
+ continue;
+ }
+
+ // Call instructions are not valid in the upwards traversal.
+ if (ParentDesc.isCall()) {
+ Result.IntermediateNodes[ParentMeta.VMAddress] = Address;
+ Result.OrphanedNodes.push_back(ParentMeta.VMAddress);
+ continue;
+ }
+
+ // Evaluate the branch target to ascertain whether this XRef is the result
+ // of a fallthrough or the target of a branch.
+ uint64_t BranchTarget;
+ if (!Analysis.getMCInstrAnalysis()->evaluateBranch(
+ ParentMeta.Instruction, ParentMeta.VMAddress,
+ ParentMeta.InstructionSize, BranchTarget)) {
+ errs() << "Failed to evaluate branch target for instruction at address "
+ << format_hex(ParentMeta.VMAddress, 2) << ".\n";
+ Result.IntermediateNodes[ParentMeta.VMAddress] = Address;
+ Result.OrphanedNodes.push_back(ParentMeta.VMAddress);
+ continue;
+ }
+
+ // Allow unconditional branches to be part of the upwards traversal.
+ if (ParentDesc.isUnconditionalBranch()) {
+ // Ensures that the unconditional branch is actually an XRef to the child.
+ if (BranchTarget != Address) {
+ errs() << "Control flow to " << format_hex(Address, 2)
+ << ", but target resolution of "
+ << format_hex(ParentMeta.VMAddress, 2)
+ << " is not this address?\n";
+ Result.IntermediateNodes[ParentMeta.VMAddress] = Address;
+ Result.OrphanedNodes.push_back(ParentMeta.VMAddress);
+ continue;
+ }
+
+ buildFlowGraphImpl(Analysis, OpenedNodes, Result, ParentMeta.VMAddress,
+ Depth + 1);
+ Result.IntermediateNodes[ParentMeta.VMAddress] = Address;
+ HasValidCrossRef = true;
+ continue;
+ }
+
+ // Ensure that any unknown CFs are caught.
+ if (!ParentDesc.isConditionalBranch()) {
+ errs() << "Unknown control flow encountered when building graph at "
+ << format_hex(Address, 2) << "\n.";
+ Result.IntermediateNodes[ParentMeta.VMAddress] = Address;
+ Result.OrphanedNodes.push_back(ParentMeta.VMAddress);
+ continue;
+ }
+
+ // Only direct conditional branches should be present at this point. Setup
+ // a conditional branch node and build flows to the ud2.
+ ConditionalBranchNode BranchNode;
+ BranchNode.Address = ParentMeta.VMAddress;
+ BranchNode.Target = 0;
+ BranchNode.Fallthrough = 0;
+ BranchNode.CFIProtection = false;
+ BranchNode.IndirectCFIsOnTargetPath = (BranchTarget == Address);
+
+ if (BranchTarget == Address)
+ BranchNode.Target = Address;
+ else
+ BranchNode.Fallthrough = Address;
+
+ HasValidCrossRef = true;
+ buildFlowsToUndefined(Analysis, Result, BranchNode, ParentMeta);
+ 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);
+
+ OpenedNodes.erase(Address);
+}
+
+} // namespace cfi_verify
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/GraphBuilder.h b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/GraphBuilder.h
new file mode 100644
index 00000000000..89724c04f76
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/GraphBuilder.h
@@ -0,0 +1,136 @@
+//===- GraphBuilder.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CFI_VERIFY_GRAPH_BUILDER_H
+#define LLVM_CFI_VERIFY_GRAPH_BUILDER_H
+
+#include "FileAnalysis.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrAnalysis.h"
+#include "llvm/MC/MCInstrDesc.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <functional>
+#include <set>
+#include <string>
+#include <unordered_map>
+
+using Instr = llvm::cfi_verify::FileAnalysis::Instr;
+
+namespace llvm {
+namespace cfi_verify {
+
+extern uint64_t SearchLengthForUndef;
+extern uint64_t SearchLengthForConditionalBranch;
+
+struct ConditionalBranchNode {
+ uint64_t Address;
+ uint64_t Target;
+ uint64_t Fallthrough;
+ // Does this conditional branch look like it's used for CFI protection? i.e.
+ // - The exit point of a basic block whos entry point is {target|fallthrough}
+ // is a CFI trap, and...
+ // - The exit point of the other basic block is an undirect CF instruction.
+ bool CFIProtection;
+ bool IndirectCFIsOnTargetPath;
+};
+
+// The canonical graph result structure returned by GraphBuilder. The members
+// in this structure encapsulate all possible code paths to the instruction
+// located at `BaseAddress`.
+struct GraphResult {
+ uint64_t BaseAddress;
+
+ // Map between an instruction address, and the address of the next instruction
+ // that will be executed. This map will contain all keys in the range:
+ // - [orphaned node, base address)
+ // - [conditional branch node {target|fallthrough}, base address)
+ DenseMap<uint64_t, uint64_t> IntermediateNodes;
+
+ // A list of orphaned nodes. A node is an 'orphan' if it meets any of the
+ // following criteria:
+ // - The length of the path from the base to this node has exceeded
+ // `SearchLengthForConditionalBranch`.
+ // - The node has no cross references to it.
+ // - The path from the base to this node is cyclic.
+ std::vector<uint64_t> OrphanedNodes;
+
+ // A list of top-level conditional branches that exist at the top of any
+ // non-orphan paths from the base.
+ std::vector<ConditionalBranchNode> ConditionalBranchNodes;
+
+ // Returns an in-order list of the path between the address provided and the
+ // base. The provided address must be part of this graph, and must not be a
+ // conditional branch.
+ std::vector<uint64_t> flattenAddress(uint64_t Address) const;
+
+ // Print the DOT representation of this result.
+ void printToDOT(const FileAnalysis &Analysis, raw_ostream &OS) const;
+};
+
+class GraphBuilder {
+public:
+ // Build the control flow graph for a provided control flow node. This method
+ // will enumerate all branch nodes that can lead to this node, and place them
+ // into GraphResult::ConditionalBranchNodes. It will also provide any orphaned
+ // (i.e. the upwards traversal did not make it to a branch node) flows to the
+ // provided node in GraphResult::OrphanedNodes.
+ static GraphResult buildFlowGraph(const FileAnalysis &Analysis,
+ object::SectionedAddress Address);
+
+private:
+ // Implementation function that actually builds the flow graph. Retrieves a
+ // list of cross references to instruction referenced in `Address`. If any of
+ // these XRefs are conditional branches, it will build the other potential
+ // path (fallthrough or target) using `buildFlowsToUndefined`. Otherwise, this
+ // function will recursively call itself where `Address` in the recursive call
+ // is now the XRef. If any XRef is an orphan, it is added to
+ // `Result.OrphanedNodes`. `OpenedNodes` keeps track of the list of nodes
+ // in the current path and is used for cycle-checking. If the path is found
+ // to be cyclic, it will be added to `Result.OrphanedNodes`.
+ static void buildFlowGraphImpl(const FileAnalysis &Analysis,
+ DenseSet<uint64_t> &OpenedNodes,
+ GraphResult &Result, uint64_t Address,
+ uint64_t Depth);
+
+ // Utilised by buildFlowGraphImpl to build the tree out from the provided
+ // conditional branch node to an undefined instruction. The provided
+ // conditional branch node must have exactly one of its subtrees set, and will
+ // update the node's CFIProtection field if a deterministic flow can be found
+ // to an undefined instruction.
+ static void buildFlowsToUndefined(const FileAnalysis &Analysis,
+ GraphResult &Result,
+ ConditionalBranchNode &BranchNode,
+ const Instr &BranchInstrMeta);
+};
+
+} // end namespace cfi_verify
+} // end namespace llvm
+
+#endif // LLVM_CFI_VERIFY_GRAPH_BUILDER_H
diff --git a/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/ya.make b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/ya.make
new file mode 100644
index 00000000000..3b9ac103f7e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cfi-verify/lib/ya.make
@@ -0,0 +1,32 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/DebugInfo/Symbolize
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-cfi-verify/lib
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ FileAnalysis.cpp
+ GraphBuilder.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-cfi-verify/llvm-cfi-verify.cpp b/contrib/libs/llvm14/tools/llvm-cfi-verify/llvm-cfi-verify.cpp
new file mode 100644
index 00000000000..8c43ea83902
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cfi-verify/llvm-cfi-verify.cpp
@@ -0,0 +1,282 @@
+//===-- llvm-cfi-verify.cpp - CFI Verification tool for LLVM --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This tool verifies Control Flow Integrity (CFI) instrumentation by static
+// binary anaylsis. See the design document in /docs/CFIVerify.rst for more
+// information.
+//
+// This tool is currently incomplete. It currently only does disassembly for
+// object files, and searches through the code for indirect control flow
+// instructions, printing them once found.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lib/FileAnalysis.h"
+#include "lib/GraphBuilder.h"
+
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/Support/VirtualFileSystem.h"
+
+#include <cstdlib>
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::cfi_verify;
+
+static cl::OptionCategory CFIVerifyCategory("CFI Verify Options");
+
+cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"),
+ cl::Required, cl::cat(CFIVerifyCategory));
+cl::opt<std::string> IgnorelistFilename(cl::Positional,
+ cl::desc("[ignorelist file]"),
+ cl::init("-"),
+ cl::cat(CFIVerifyCategory));
+cl::opt<bool> PrintGraphs(
+ "print-graphs",
+ cl::desc("Print graphs around indirect CF instructions in DOT format."),
+ cl::init(false), cl::cat(CFIVerifyCategory));
+cl::opt<unsigned> PrintBlameContext(
+ "blame-context",
+ cl::desc("Print the blame context (if possible) for BAD instructions. This "
+ "specifies the number of lines of context to include, where zero "
+ "disables this feature."),
+ cl::init(0), cl::cat(CFIVerifyCategory));
+cl::opt<unsigned> PrintBlameContextAll(
+ "blame-context-all",
+ cl::desc("Prints the blame context (if possible) for ALL instructions. "
+ "This specifies the number of lines of context for non-BAD "
+ "instructions (see --blame-context). If --blame-context is "
+ "unspecified, it prints this number of contextual lines for BAD "
+ "instructions as well."),
+ cl::init(0), cl::cat(CFIVerifyCategory));
+cl::opt<bool> Summarize("summarize", cl::desc("Print the summary only."),
+ cl::init(false), cl::cat(CFIVerifyCategory));
+
+ExitOnError ExitOnErr;
+
+static void printBlameContext(const DILineInfo &LineInfo, unsigned Context) {
+ auto FileOrErr = MemoryBuffer::getFile(LineInfo.FileName);
+ if (!FileOrErr) {
+ errs() << "Could not open file: " << LineInfo.FileName << "\n";
+ return;
+ }
+
+ std::unique_ptr<MemoryBuffer> File = std::move(FileOrErr.get());
+ SmallVector<StringRef, 100> Lines;
+ File->getBuffer().split(Lines, '\n');
+
+ for (unsigned i = std::max<size_t>(1, LineInfo.Line - Context);
+ i <
+ std::min<size_t>(Lines.size() + 1, LineInfo.Line + Context + 1);
+ ++i) {
+ if (i == LineInfo.Line)
+ outs() << ">";
+ else
+ outs() << " ";
+
+ outs() << i << ": " << Lines[i - 1] << "\n";
+ }
+}
+
+static void printInstructionInformation(const FileAnalysis &Analysis,
+ const Instr &InstrMeta,
+ const GraphResult &Graph,
+ CFIProtectionStatus ProtectionStatus) {
+ outs() << "Instruction: " << format_hex(InstrMeta.VMAddress, 2) << " ("
+ << stringCFIProtectionStatus(ProtectionStatus) << "): ";
+ Analysis.printInstruction(InstrMeta, outs());
+ outs() << " \n";
+
+ if (PrintGraphs)
+ Graph.printToDOT(Analysis, outs());
+}
+
+static void printInstructionStatus(unsigned BlameLine, bool CFIProtected,
+ const DILineInfo &LineInfo) {
+ if (BlameLine) {
+ outs() << "Ignorelist Match: " << IgnorelistFilename << ":" << BlameLine
+ << "\n";
+ if (CFIProtected)
+ outs() << "====> Unexpected Protected\n";
+ else
+ outs() << "====> Expected Unprotected\n";
+
+ if (PrintBlameContextAll)
+ printBlameContext(LineInfo, PrintBlameContextAll);
+ } else {
+ if (CFIProtected) {
+ outs() << "====> Expected Protected\n";
+ if (PrintBlameContextAll)
+ printBlameContext(LineInfo, PrintBlameContextAll);
+ } else {
+ outs() << "====> Unexpected Unprotected (BAD)\n";
+ if (PrintBlameContext)
+ printBlameContext(LineInfo, PrintBlameContext);
+ }
+ }
+}
+
+static void
+printIndirectCFInstructions(FileAnalysis &Analysis,
+ const SpecialCaseList *SpecialCaseList) {
+ uint64_t ExpectedProtected = 0;
+ uint64_t UnexpectedProtected = 0;
+ uint64_t ExpectedUnprotected = 0;
+ uint64_t UnexpectedUnprotected = 0;
+
+ std::map<unsigned, uint64_t> BlameCounter;
+
+ for (object::SectionedAddress Address : Analysis.getIndirectInstructions()) {
+ const auto &InstrMeta = Analysis.getInstructionOrDie(Address.Address);
+ GraphResult Graph = GraphBuilder::buildFlowGraph(Analysis, Address);
+
+ CFIProtectionStatus ProtectionStatus =
+ Analysis.validateCFIProtection(Graph);
+ bool CFIProtected = (ProtectionStatus == CFIProtectionStatus::PROTECTED);
+
+ if (!Summarize) {
+ outs() << "-----------------------------------------------------\n";
+ printInstructionInformation(Analysis, InstrMeta, Graph, ProtectionStatus);
+ }
+
+ if (IgnoreDWARFFlag) {
+ if (CFIProtected)
+ ExpectedProtected++;
+ else
+ UnexpectedUnprotected++;
+ continue;
+ }
+
+ auto InliningInfo = Analysis.symbolizeInlinedCode(Address);
+ if (!InliningInfo || InliningInfo->getNumberOfFrames() == 0) {
+ errs() << "Failed to symbolise " << format_hex(Address.Address, 2)
+ << " with line tables from " << InputFilename << "\n";
+ exit(EXIT_FAILURE);
+ }
+
+ const auto &LineInfo = InliningInfo->getFrame(0);
+
+ // Print the inlining symbolisation of this instruction.
+ if (!Summarize) {
+ for (uint32_t i = 0; i < InliningInfo->getNumberOfFrames(); ++i) {
+ const auto &Line = InliningInfo->getFrame(i);
+ outs() << " " << format_hex(Address.Address, 2) << " = "
+ << Line.FileName << ":" << Line.Line << ":" << Line.Column
+ << " (" << Line.FunctionName << ")\n";
+ }
+ }
+
+ if (!SpecialCaseList) {
+ if (CFIProtected) {
+ if (PrintBlameContextAll && !Summarize)
+ printBlameContext(LineInfo, PrintBlameContextAll);
+ ExpectedProtected++;
+ } else {
+ if (PrintBlameContext && !Summarize)
+ printBlameContext(LineInfo, PrintBlameContext);
+ UnexpectedUnprotected++;
+ }
+ continue;
+ }
+
+ unsigned BlameLine = 0;
+ for (auto &K : {"cfi-icall", "cfi-vcall"}) {
+ if (!BlameLine)
+ BlameLine =
+ SpecialCaseList->inSectionBlame(K, "src", LineInfo.FileName);
+ if (!BlameLine)
+ BlameLine =
+ SpecialCaseList->inSectionBlame(K, "fun", LineInfo.FunctionName);
+ }
+
+ if (BlameLine) {
+ BlameCounter[BlameLine]++;
+ if (CFIProtected)
+ UnexpectedProtected++;
+ else
+ ExpectedUnprotected++;
+ } else {
+ if (CFIProtected)
+ ExpectedProtected++;
+ else
+ UnexpectedUnprotected++;
+ }
+
+ if (!Summarize)
+ printInstructionStatus(BlameLine, CFIProtected, LineInfo);
+ }
+
+ uint64_t IndirectCFInstructions = ExpectedProtected + UnexpectedProtected +
+ ExpectedUnprotected + UnexpectedUnprotected;
+
+ if (IndirectCFInstructions == 0) {
+ outs() << "No indirect CF instructions found.\n";
+ return;
+ }
+
+ outs() << formatv("\nTotal Indirect CF Instructions: {0}\n"
+ "Expected Protected: {1} ({2:P})\n"
+ "Unexpected Protected: {3} ({4:P})\n"
+ "Expected Unprotected: {5} ({6:P})\n"
+ "Unexpected Unprotected (BAD): {7} ({8:P})\n",
+ IndirectCFInstructions, ExpectedProtected,
+ ((double)ExpectedProtected) / IndirectCFInstructions,
+ UnexpectedProtected,
+ ((double)UnexpectedProtected) / IndirectCFInstructions,
+ ExpectedUnprotected,
+ ((double)ExpectedUnprotected) / IndirectCFInstructions,
+ UnexpectedUnprotected,
+ ((double)UnexpectedUnprotected) / IndirectCFInstructions);
+
+ if (!SpecialCaseList)
+ return;
+
+ outs() << "\nIgnorelist Results:\n";
+ for (const auto &KV : BlameCounter) {
+ outs() << " " << IgnorelistFilename << ":" << KV.first << " affects "
+ << KV.second << " indirect CF instructions.\n";
+ }
+}
+
+int main(int argc, char **argv) {
+ cl::HideUnrelatedOptions({&CFIVerifyCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(
+ argc, argv,
+ "Identifies whether Control Flow Integrity protects all indirect control "
+ "flow instructions in the provided object file, DSO or binary.\nNote: "
+ "Anything statically linked into the provided file *must* be compiled "
+ "with '-g'. This can be relaxed through the '--ignore-dwarf' flag.");
+
+ InitializeAllTargetInfos();
+ InitializeAllTargetMCs();
+ InitializeAllAsmParsers();
+ InitializeAllDisassemblers();
+
+ if (PrintBlameContextAll && !PrintBlameContext)
+ PrintBlameContext.setValue(PrintBlameContextAll);
+
+ std::unique_ptr<SpecialCaseList> SpecialCaseList;
+ if (IgnorelistFilename != "-") {
+ std::string Error;
+ SpecialCaseList = SpecialCaseList::create({IgnorelistFilename},
+ *vfs::getRealFileSystem(), Error);
+ if (!SpecialCaseList) {
+ errs() << "Failed to get ignorelist: " << Error << "\n";
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ FileAnalysis Analysis = ExitOnErr(FileAnalysis::Create(InputFilename));
+ printIndirectCFInstructions(Analysis, SpecialCaseList.get());
+
+ return EXIT_SUCCESS;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cfi-verify/ya.make b/contrib/libs/llvm14/tools/llvm-cfi-verify/ya.make
new file mode 100644
index 00000000000..b56a323e2a1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cfi-verify/ya.make
@@ -0,0 +1,67 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/DebugInfo/MSF
+ contrib/libs/llvm14/lib/DebugInfo/PDB
+ contrib/libs/llvm14/lib/DebugInfo/Symbolize
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/tools/llvm-cfi-verify/lib
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-cfi-verify
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-cfi-verify.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-config/BuildVariables.inc b/contrib/libs/llvm14/tools/llvm-config/BuildVariables.inc
new file mode 100644
index 00000000000..8e34c8ebafc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-config/BuildVariables.inc
@@ -0,0 +1,39 @@
+//===-- BuildVariables.inc.in - llvm-config build variables -*- C++ -*-----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is configured by the build system to define the variables
+// llvm-config wants to report to the user, but which can only be determined at
+// build time.
+//
+// The variant of this file not ending with .in has been autogenerated by the
+// LLVM build. Do not edit!
+//
+//===----------------------------------------------------------------------===//
+
+#define LLVM_SRC_ROOT "/var/empty/tmp/llvm-src-14.0.6/llvm"
+#define LLVM_OBJ_ROOT "/var/empty/tmp/llvm-src-14.0.6/llvm/build"
+#define LLVM_CPPFLAGS "-D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS"
+#define LLVM_CFLAGS " -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS"
+#define LLVM_LDFLAGS ""
+#define LLVM_CXXFLAGS "-std=c++14 -fno-exceptions -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS"
+#define LLVM_BUILDMODE "MinSizeRel"
+#define LLVM_LIBDIR_SUFFIX ""
+#define LLVM_INSTALL_BINDIR "/var/empty/tmp/out/bin"
+#define LLVM_INSTALL_LIBDIR "/var/empty/tmp/out/lib"
+#define LLVM_INSTALL_INCLUDEDIR "/var/empty/tmp/out/include"
+#define LLVM_INSTALL_CMAKEDIR "/02qcpld1y6xhs5gz9bchpxaw0xdhmsp5dv88lh25r2ss44kh8dxz/lib/cmake/llvm/"
+#define LLVM_TARGETS_BUILT "AArch64 ARM BPF PowerPC X86 NVPTX"
+#define LLVM_SYSTEM_LIBS "-lrt -ldl -lm -lz"
+#define LLVM_BUILD_SYSTEM "cmake"
+#define LLVM_HAS_RTTI 1
+#define LLVM_ENABLE_DYLIB 0
+#define LLVM_LINK_DYLIB 0
+#define LLVM_ENABLE_SHARED 1
+#define LLVM_DYLIB_COMPONENTS "all"
+#define LLVM_DYLIB_VERSION "14"
+#define LLVM_TOOLS_INSTALL_DIR "/var/empty/tmp/out/bin"
diff --git a/contrib/libs/llvm14/tools/llvm-config/ExtensionDependencies.inc b/contrib/libs/llvm14/tools/llvm-config/ExtensionDependencies.inc
new file mode 100644
index 00000000000..d57a2d98fc5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-config/ExtensionDependencies.inc
@@ -0,0 +1,8 @@
+#include <array>
+ struct ExtensionDescriptor {
+ const char* Name;
+ const char* RequiredLibraries[1 + 1 + 23];
+ };
+ std::array<ExtensionDescriptor, 1> AvailableExtensions{
+ExtensionDescriptor{"Polly", {"support", "core", "scalaropts", "instcombine", "transformutils", "analysis", "ipo", "mc", "passes", "linker", "irreader", "analysis", "bitreader", "mcparser", "object", "profiledata", "target", "vectorize", "nvptxcodegen", "nvptxdesc", "nvptxinfo", "PollyISL", "PollyPPCG", "Polly",nullptr}},
+};
diff --git a/contrib/libs/llvm14/tools/llvm-config/LibraryDependencies.inc b/contrib/libs/llvm14/tools/llvm-config/LibraryDependencies.inc
new file mode 100644
index 00000000000..a3d5ce884b6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-config/LibraryDependencies.inc
@@ -0,0 +1,126 @@
+
+ struct AvailableComponent {
+ /// The name of the component.
+ const char *Name;
+
+ /// The name of the library for this component (or NULL).
+ const char *Library;
+
+ /// Whether the component is installed.
+ bool IsInstalled;
+
+ /// The list of libraries required when linking this component.
+ const char *RequiredLibraries[111];
+ } AvailableComponents[111] = {
+ { "aarch64", nullptr, true, {"aarch64codegen", "aarch64asmparser", "aarch64disassembler", "aarch64desc", "aarch64info", "aarch64utils"} },
+{ "aarch64asmparser", "LLVMAArch64AsmParser", true, {"aarch64desc", "aarch64info", "aarch64utils", "mc", "mcparser", "support"} },
+{ "aarch64codegen", "LLVMAArch64CodeGen", true, {"aarch64desc", "aarch64info", "aarch64utils", "analysis", "asmprinter", "codegen", "core", "mc", "scalaropts", "selectiondag", "support", "target", "transformutils", "globalisel", "cfguard"} },
+{ "aarch64desc", "LLVMAArch64Desc", true, {"aarch64info", "aarch64utils", "mc", "binaryformat", "support"} },
+{ "aarch64disassembler", "LLVMAArch64Disassembler", true, {"aarch64desc", "aarch64info", "aarch64utils", "mc", "mcdisassembler", "support"} },
+{ "aarch64info", "LLVMAArch64Info", true, {"mc", "support"} },
+{ "aarch64utils", "LLVMAArch64Utils", true, {"support"} },
+{ "aggressiveinstcombine", "LLVMAggressiveInstCombine", true, {"analysis", "core", "support", "transformutils"} },
+{ "all", nullptr, true, {"demangle", "support", "tablegen", "core", "fuzzmutate", "filecheck", "interfacestub", "irreader", "codegen", "selectiondag", "asmprinter", "mirparser", "globalisel", "binaryformat", "bitreader", "bitwriter", "bitstreamreader", "dwarflinker", "extensions", "frontendopenacc", "frontendopenmp", "transformutils", "instrumentation", "aggressiveinstcombine", "instcombine", "scalaropts", "ipo", "vectorize", "objcarcopts", "coroutines", "cfguard", "linker", "analysis", "lto", "mc", "mcparser", "mcdisassembler", "mca", "object", "objectyaml", "option", "remarks", "debuginfodwarf", "debuginfogsym", "debuginfomsf", "debuginfocodeview", "debuginfopdb", "symbolize", "dwp", "executionengine", "interpreter", "jitlink", "mcjit", "orcjit", "orcshared", "orctargetprocess", "runtimedyld", "perfjitevents", "target", "aarch64codegen", "aarch64asmparser", "aarch64disassembler", "aarch64desc", "aarch64info", "aarch64utils", "armcodegen", "armasmparser", "armdisassembler", "armdesc", "arminfo", "armutils", "bpfcodegen", "bpfasmparser", "bpfdisassembler", "bpfdesc", "bpfinfo", "powerpccodegen", "powerpcasmparser", "powerpcdisassembler", "powerpcdesc", "powerpcinfo", "x86codegen", "x86asmparser", "x86disassembler", "x86targetmca", "x86desc", "x86info", "nvptxcodegen", "nvptxdesc", "nvptxinfo", "asmparser", "lineeditor", "profiledata", "coverage", "passes", "textapi", "dlltooldriver", "libdriver", "xray", "windowsmanifest", "all-targets", "engine", "native", "nativecodegen", "aarch64", "arm", "bpf", "powerpc", "x86", "nvptx"} },
+{ "all-targets", nullptr, true, {"aarch64", "arm", "bpf", "powerpc", "x86", "nvptx"} },
+{ "analysis", "LLVMAnalysis", true, {"binaryformat", "core", "object", "profiledata", "support"} },
+{ "arm", nullptr, true, {"armcodegen", "armasmparser", "armdisassembler", "armdesc", "arminfo", "armutils"} },
+{ "armasmparser", "LLVMARMAsmParser", true, {"armdesc", "arminfo", "mc", "mcparser", "support", "armutils"} },
+{ "armcodegen", "LLVMARMCodeGen", true, {"armdesc", "arminfo", "analysis", "asmprinter", "codegen", "core", "ipo", "mc", "scalaropts", "selectiondag", "support", "target", "globalisel", "armutils", "transformutils", "cfguard"} },
+{ "armdesc", "LLVMARMDesc", true, {"arminfo", "armutils", "mc", "mcdisassembler", "support", "binaryformat"} },
+{ "armdisassembler", "LLVMARMDisassembler", true, {"armdesc", "arminfo", "mcdisassembler", "support", "armutils"} },
+{ "arminfo", "LLVMARMInfo", true, {"mc", "support"} },
+{ "armutils", "LLVMARMUtils", true, {"support"} },
+{ "asmparser", "LLVMAsmParser", true, {"binaryformat", "core", "support"} },
+{ "asmprinter", "LLVMAsmPrinter", true, {"analysis", "binaryformat", "codegen", "core", "debuginfocodeview", "debuginfodwarf", "debuginfomsf", "mc", "mcparser", "remarks", "support", "target"} },
+{ "binaryformat", "LLVMBinaryFormat", true, {"support"} },
+{ "bitreader", "LLVMBitReader", true, {"bitstreamreader", "core", "support"} },
+{ "bitstreamreader", "LLVMBitstreamReader", true, {"support"} },
+{ "bitwriter", "LLVMBitWriter", true, {"analysis", "core", "mc", "object", "support"} },
+{ "bpf", nullptr, true, {"bpfcodegen", "bpfasmparser", "bpfdisassembler", "bpfdesc", "bpfinfo"} },
+{ "bpfasmparser", "LLVMBPFAsmParser", true, {"mc", "mcparser", "bpfdesc", "bpfinfo", "support"} },
+{ "bpfcodegen", "LLVMBPFCodeGen", true, {"analysis", "asmprinter", "codegen", "core", "mc", "bpfdesc", "bpfinfo", "ipo", "scalaropts", "selectiondag", "support", "target", "transformutils"} },
+{ "bpfdesc", "LLVMBPFDesc", true, {"mc", "bpfinfo", "support"} },
+{ "bpfdisassembler", "LLVMBPFDisassembler", true, {"mcdisassembler", "bpfinfo", "support"} },
+{ "bpfinfo", "LLVMBPFInfo", true, {"mc", "support"} },
+{ "cfguard", "LLVMCFGuard", true, {"core", "support"} },
+{ "codegen", "LLVMCodeGen", true, {"analysis", "bitreader", "bitwriter", "core", "mc", "profiledata", "scalaropts", "support", "target", "transformutils"} },
+{ "core", "LLVMCore", true, {"binaryformat", "remarks", "support"} },
+{ "coroutines", "LLVMCoroutines", true, {"analysis", "core", "ipo", "scalaropts", "support", "transformutils"} },
+{ "coverage", "LLVMCoverage", true, {"core", "object", "profiledata", "support"} },
+{ "debuginfocodeview", "LLVMDebugInfoCodeView", true, {"support"} },
+{ "debuginfodwarf", "LLVMDebugInfoDWARF", true, {"binaryformat", "object", "mc", "support"} },
+{ "debuginfogsym", "LLVMDebugInfoGSYM", true, {"mc", "object", "support", "debuginfodwarf"} },
+{ "debuginfomsf", "LLVMDebugInfoMSF", true, {"support"} },
+{ "debuginfopdb", "LLVMDebugInfoPDB", true, {"binaryformat", "object", "support", "debuginfocodeview", "debuginfomsf"} },
+{ "demangle", "LLVMDemangle", true, {} },
+{ "dlltooldriver", "LLVMDlltoolDriver", true, {"object", "option", "support"} },
+{ "dwarflinker", "LLVMDWARFLinker", true, {"binaryformat", "debuginfodwarf", "asmprinter", "codegen", "mc", "object", "support"} },
+{ "dwp", "LLVMDWP", true, {"debuginfodwarf", "mc", "object", "support", "target"} },
+{ "engine", nullptr, true, {"interpreter"} },
+{ "executionengine", "LLVMExecutionEngine", true, {"core", "mc", "object", "orctargetprocess", "runtimedyld", "support", "target"} },
+{ "extensions", "LLVMExtensions", true, {"support"} },
+{ "filecheck", "LLVMFileCheck", true, {} },
+{ "frontendopenacc", "LLVMFrontendOpenACC", true, {} },
+{ "frontendopenmp", "LLVMFrontendOpenMP", true, {"core", "support", "transformutils", "analysis", "mc", "scalaropts"} },
+{ "fuzzmutate", "LLVMFuzzMutate", true, {"analysis", "bitreader", "bitwriter", "core", "scalaropts", "support", "target"} },
+{ "globalisel", "LLVMGlobalISel", true, {"analysis", "codegen", "core", "mc", "selectiondag", "support", "target", "transformutils"} },
+{ "instcombine", "LLVMInstCombine", true, {"analysis", "core", "support", "transformutils"} },
+{ "instrumentation", "LLVMInstrumentation", true, {"analysis", "core", "mc", "support", "transformutils", "profiledata"} },
+{ "interfacestub", "LLVMInterfaceStub", true, {"binaryformat", "mc", "object", "support"} },
+{ "interpreter", "LLVMInterpreter", true, {"codegen", "core", "executionengine", "support"} },
+{ "ipo", "LLVMipo", true, {"aggressiveinstcombine", "analysis", "bitreader", "bitwriter", "core", "frontendopenmp", "instcombine", "irreader", "linker", "object", "profiledata", "scalaropts", "support", "transformutils", "vectorize", "instrumentation", "scalaropts"} },
+{ "irreader", "LLVMIRReader", true, {"asmparser", "bitreader", "core", "support"} },
+{ "jitlink", "LLVMJITLink", true, {"binaryformat", "object", "orctargetprocess", "support"} },
+{ "libdriver", "LLVMLibDriver", true, {"binaryformat", "bitreader", "object", "option", "support", "binaryformat", "bitreader", "object", "option", "support"} },
+{ "lineeditor", "LLVMLineEditor", true, {"support"} },
+{ "linker", "LLVMLinker", true, {"core", "object", "support", "transformutils"} },
+{ "lto", "LLVMLTO", true, {"aggressiveinstcombine", "analysis", "binaryformat", "bitreader", "bitwriter", "codegen", "core", "extensions", "ipo", "instcombine", "instrumentation", "linker", "mc", "objcarcopts", "object", "passes", "remarks", "scalaropts", "support", "target", "transformutils"} },
+{ "mc", "LLVMMC", true, {"support", "binaryformat", "debuginfocodeview"} },
+{ "mca", "LLVMMCA", true, {"mc", "support"} },
+{ "mcdisassembler", "LLVMMCDisassembler", true, {"mc", "support"} },
+{ "mcjit", "LLVMMCJIT", true, {"core", "executionengine", "object", "runtimedyld", "support", "target"} },
+{ "mcparser", "LLVMMCParser", true, {"mc", "support"} },
+{ "mirparser", "LLVMMIRParser", true, {"asmparser", "binaryformat", "codegen", "core", "mc", "support", "target"} },
+{ "native", nullptr, true, {"x86"} },
+{ "nativecodegen", nullptr, true, {"x86codegen"} },
+{ "nvptx", nullptr, true, {"nvptxcodegen", "nvptxdesc", "nvptxinfo"} },
+{ "nvptxcodegen", "LLVMNVPTXCodeGen", true, {"analysis", "asmprinter", "codegen", "core", "ipo", "mc", "nvptxdesc", "nvptxinfo", "scalaropts", "selectiondag", "support", "target", "transformutils", "vectorize"} },
+{ "nvptxdesc", "LLVMNVPTXDesc", true, {"mc", "nvptxinfo", "support"} },
+{ "nvptxinfo", "LLVMNVPTXInfo", true, {"mc", "support"} },
+{ "objcarcopts", "LLVMObjCARCOpts", true, {"analysis", "core", "support", "transformutils"} },
+{ "object", "LLVMObject", true, {"bitreader", "core", "mc", "binaryformat", "mcparser", "support", "textapi"} },
+{ "objectyaml", "LLVMObjectYAML", true, {"binaryformat", "object", "support", "debuginfocodeview", "mc"} },
+{ "option", "LLVMOption", true, {"support"} },
+{ "orcjit", "LLVMOrcJIT", true, {"core", "executionengine", "jitlink", "object", "orcshared", "orctargetprocess", "mc", "mcdisassembler", "passes", "runtimedyld", "support", "target", "transformutils"} },
+{ "orcshared", "LLVMOrcShared", true, {"support"} },
+{ "orctargetprocess", "LLVMOrcTargetProcess", true, {"orcshared", "support"} },
+{ "passes", "LLVMPasses", true, {"aggressiveinstcombine", "analysis", "core", "coroutines", "ipo", "instcombine", "objcarcopts", "scalaropts", "support", "target", "transformutils", "vectorize", "instrumentation"} },
+{ "perfjitevents", "LLVMPerfJITEvents", true, {"codegen", "core", "debuginfodwarf", "executionengine", "object", "support"} },
+{ "powerpc", nullptr, true, {"powerpccodegen", "powerpcasmparser", "powerpcdisassembler", "powerpcdesc", "powerpcinfo"} },
+{ "powerpcasmparser", "LLVMPowerPCAsmParser", true, {"mc", "mcparser", "powerpcdesc", "powerpcinfo", "support"} },
+{ "powerpccodegen", "LLVMPowerPCCodeGen", true, {"analysis", "asmprinter", "binaryformat", "codegen", "core", "mc", "powerpcdesc", "powerpcinfo", "scalaropts", "selectiondag", "support", "target", "transformutils", "globalisel"} },
+{ "powerpcdesc", "LLVMPowerPCDesc", true, {"mc", "powerpcinfo", "support", "binaryformat"} },
+{ "powerpcdisassembler", "LLVMPowerPCDisassembler", true, {"mcdisassembler", "powerpcinfo", "support"} },
+{ "powerpcinfo", "LLVMPowerPCInfo", true, {"mc", "support"} },
+{ "profiledata", "LLVMProfileData", true, {"core", "support", "demangle", "object", "debuginfodwarf"} },
+{ "remarks", "LLVMRemarks", true, {"bitstreamreader", "support"} },
+{ "runtimedyld", "LLVMRuntimeDyld", true, {"core", "mc", "object", "support"} },
+{ "scalaropts", "LLVMScalarOpts", true, {"aggressiveinstcombine", "analysis", "core", "instcombine", "support", "transformutils"} },
+{ "selectiondag", "LLVMSelectionDAG", true, {"analysis", "codegen", "core", "mc", "support", "target", "transformutils"} },
+{ "support", "LLVMSupport", true, {"demangle"} },
+{ "symbolize", "LLVMSymbolize", true, {"debuginfodwarf", "debuginfopdb", "object", "support", "demangle"} },
+{ "tablegen", "LLVMTableGen", true, {"support"} },
+{ "target", "LLVMTarget", true, {"analysis", "core", "mc", "support"} },
+{ "textapi", "LLVMTextAPI", true, {"support", "binaryformat"} },
+{ "transformutils", "LLVMTransformUtils", true, {"analysis", "core", "support"} },
+{ "vectorize", "LLVMVectorize", true, {"analysis", "core", "support", "transformutils"} },
+{ "windowsmanifest", "LLVMWindowsManifest", true, {"support"} },
+{ "x86", nullptr, true, {"x86codegen", "x86asmparser", "x86disassembler", "x86targetmca", "x86desc", "x86info"} },
+{ "x86asmparser", "LLVMX86AsmParser", true, {"mc", "mcparser", "support", "x86desc", "x86info"} },
+{ "x86codegen", "LLVMX86CodeGen", true, {"analysis", "asmprinter", "codegen", "core", "instrumentation", "mc", "selectiondag", "support", "target", "transformutils", "x86desc", "x86info", "globalisel", "profiledata", "cfguard"} },
+{ "x86desc", "LLVMX86Desc", true, {"mc", "mcdisassembler", "support", "x86info", "binaryformat"} },
+{ "x86disassembler", "LLVMX86Disassembler", true, {"mcdisassembler", "support", "x86info"} },
+{ "x86info", "LLVMX86Info", true, {"mc", "support"} },
+{ "x86targetmca", "LLVMX86TargetMCA", true, {"mc", "mcparser", "x86desc", "x86info", "support", "mca"} },
+{ "xray", "LLVMXRay", true, {"support", "object"} },
+}; \ No newline at end of file
diff --git a/contrib/libs/llvm14/tools/llvm-config/llvm-config.cpp b/contrib/libs/llvm14/tools/llvm-config/llvm-config.cpp
new file mode 100644
index 00000000000..5e7184bab90
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-config/llvm-config.cpp
@@ -0,0 +1,755 @@
+//===-- llvm-config.cpp - LLVM project configuration utility --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This tool encapsulates information about an LLVM project configuration for
+// use by other project's build environments (to determine installed path,
+// available features, required libraries, etc.).
+//
+// Note that although this tool *may* be used by some parts of LLVM's build
+// itself (i.e., the Makefiles use it to compute required libraries when linking
+// tools), this tool is primarily designed to support external projects.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Config/llvm-config.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Config/config.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstdlib>
+#include <set>
+#include <unordered_set>
+#include <vector>
+
+using namespace llvm;
+
+// Include the build time variables we can report to the user. This is generated
+// at build time from the BuildVariables.inc.in file by the build system.
+#include "BuildVariables.inc"
+
+// Include the component table. This creates an array of struct
+// AvailableComponent entries, which record the component name, library name,
+// and required components for all of the available libraries.
+//
+// Not all components define a library, we also use "library groups" as a way to
+// create entries for pseudo groups like x86 or all-targets.
+#include "LibraryDependencies.inc"
+
+// Built-in extensions also register their dependencies, but in a separate file,
+// later in the process.
+#include "ExtensionDependencies.inc"
+
+// LinkMode determines what libraries and flags are returned by llvm-config.
+enum LinkMode {
+ // LinkModeAuto will link with the default link mode for the installation,
+ // which is dependent on the value of LLVM_LINK_LLVM_DYLIB, and fall back
+ // to the alternative if the required libraries are not available.
+ LinkModeAuto = 0,
+
+ // LinkModeShared will link with the dynamic component libraries if they
+ // exist, and return an error otherwise.
+ LinkModeShared = 1,
+
+ // LinkModeStatic will link with the static component libraries if they
+ // exist, and return an error otherwise.
+ LinkModeStatic = 2,
+};
+
+/// Traverse a single component adding to the topological ordering in
+/// \arg RequiredLibs.
+///
+/// \param Name - The component to traverse.
+/// \param ComponentMap - A prebuilt map of component names to descriptors.
+/// \param VisitedComponents [in] [out] - The set of already visited components.
+/// \param RequiredLibs [out] - The ordered list of required
+/// libraries.
+/// \param GetComponentNames - Get the component names instead of the
+/// library name.
+static void VisitComponent(const std::string &Name,
+ const StringMap<AvailableComponent *> &ComponentMap,
+ std::set<AvailableComponent *> &VisitedComponents,
+ std::vector<std::string> &RequiredLibs,
+ bool IncludeNonInstalled, bool GetComponentNames,
+ const std::function<std::string(const StringRef &)>
+ *GetComponentLibraryPath,
+ std::vector<std::string> *Missing,
+ const std::string &DirSep) {
+ // Lookup the component.
+ AvailableComponent *AC = ComponentMap.lookup(Name);
+ if (!AC) {
+ errs() << "Can't find component: '" << Name << "' in the map. Available components are: ";
+ for (const auto &Component : ComponentMap) {
+ errs() << "'" << Component.first() << "' ";
+ }
+ errs() << "\n";
+ report_fatal_error("abort");
+ }
+ assert(AC && "Invalid component name!");
+
+ // Add to the visited table.
+ if (!VisitedComponents.insert(AC).second) {
+ // We are done if the component has already been visited.
+ return;
+ }
+
+ // Only include non-installed components if requested.
+ if (!AC->IsInstalled && !IncludeNonInstalled)
+ return;
+
+ // Otherwise, visit all the dependencies.
+ for (unsigned i = 0; AC->RequiredLibraries[i]; ++i) {
+ VisitComponent(AC->RequiredLibraries[i], ComponentMap, VisitedComponents,
+ RequiredLibs, IncludeNonInstalled, GetComponentNames,
+ GetComponentLibraryPath, Missing, DirSep);
+ }
+
+ // Special handling for the special 'extensions' component. Its content is
+ // not populated by llvm-build, but later in the process and loaded from
+ // ExtensionDependencies.inc.
+ if (Name == "extensions") {
+ for (auto const &AvailableExtension : AvailableExtensions) {
+ for (const char *const *Iter = &AvailableExtension.RequiredLibraries[0];
+ *Iter; ++Iter) {
+ AvailableComponent *AC = ComponentMap.lookup(*Iter);
+ if (!AC) {
+ RequiredLibs.push_back(*Iter);
+ } else {
+ VisitComponent(*Iter, ComponentMap, VisitedComponents, RequiredLibs,
+ IncludeNonInstalled, GetComponentNames,
+ GetComponentLibraryPath, Missing, DirSep);
+ }
+ }
+ }
+ }
+
+ if (GetComponentNames) {
+ RequiredLibs.push_back(Name);
+ return;
+ }
+
+ // Add to the required library list.
+ if (AC->Library) {
+ if (Missing && GetComponentLibraryPath) {
+ std::string path = (*GetComponentLibraryPath)(AC->Library);
+ if (DirSep == "\\") {
+ std::replace(path.begin(), path.end(), '/', '\\');
+ }
+ if (!sys::fs::exists(path))
+ Missing->push_back(path);
+ }
+ RequiredLibs.push_back(AC->Library);
+ }
+}
+
+/// Compute the list of required libraries for a given list of
+/// components, in an order suitable for passing to a linker (that is, libraries
+/// appear prior to their dependencies).
+///
+/// \param Components - The names of the components to find libraries for.
+/// \param IncludeNonInstalled - Whether non-installed components should be
+/// reported.
+/// \param GetComponentNames - True if one would prefer the component names.
+static std::vector<std::string> ComputeLibsForComponents(
+ const std::vector<StringRef> &Components, bool IncludeNonInstalled,
+ bool GetComponentNames, const std::function<std::string(const StringRef &)>
+ *GetComponentLibraryPath,
+ std::vector<std::string> *Missing, const std::string &DirSep) {
+ std::vector<std::string> RequiredLibs;
+ std::set<AvailableComponent *> VisitedComponents;
+
+ // Build a map of component names to information.
+ StringMap<AvailableComponent *> ComponentMap;
+ for (unsigned i = 0; i != array_lengthof(AvailableComponents); ++i) {
+ AvailableComponent *AC = &AvailableComponents[i];
+ ComponentMap[AC->Name] = AC;
+ }
+
+ // Visit the components.
+ for (unsigned i = 0, e = Components.size(); i != e; ++i) {
+ // Users are allowed to provide mixed case component names.
+ std::string ComponentLower = Components[i].lower();
+
+ // Validate that the user supplied a valid component name.
+ if (!ComponentMap.count(ComponentLower)) {
+ llvm::errs() << "llvm-config: unknown component name: " << Components[i]
+ << "\n";
+ exit(1);
+ }
+
+ VisitComponent(ComponentLower, ComponentMap, VisitedComponents,
+ RequiredLibs, IncludeNonInstalled, GetComponentNames,
+ GetComponentLibraryPath, Missing, DirSep);
+ }
+
+ // The list is now ordered with leafs first, we want the libraries to printed
+ // in the reverse order of dependency.
+ std::reverse(RequiredLibs.begin(), RequiredLibs.end());
+
+ return RequiredLibs;
+}
+
+/* *** */
+
+static void usage() {
+ errs() << "\
+usage: llvm-config <OPTION>... [<COMPONENT>...]\n\
+\n\
+Get various configuration information needed to compile programs which use\n\
+LLVM. Typically called from 'configure' scripts. Examples:\n\
+ llvm-config --cxxflags\n\
+ llvm-config --ldflags\n\
+ llvm-config --libs engine bcreader scalaropts\n\
+\n\
+Options:\n\
+ --version Print LLVM version.\n\
+ --prefix Print the installation prefix.\n\
+ --src-root Print the source root LLVM was built from.\n\
+ --obj-root Print the object root used to build LLVM.\n\
+ --bindir Directory containing LLVM executables.\n\
+ --includedir Directory containing LLVM headers.\n\
+ --libdir Directory containing LLVM libraries.\n\
+ --cmakedir Directory containing LLVM cmake modules.\n\
+ --cppflags C preprocessor flags for files that include LLVM headers.\n\
+ --cflags C compiler flags for files that include LLVM headers.\n\
+ --cxxflags C++ compiler flags for files that include LLVM headers.\n\
+ --ldflags Print Linker flags.\n\
+ --system-libs System Libraries needed to link against LLVM components.\n\
+ --libs Libraries needed to link against LLVM components.\n\
+ --libnames Bare library names for in-tree builds.\n\
+ --libfiles Fully qualified library filenames for makefile depends.\n\
+ --components List of all possible components.\n\
+ --targets-built List of all targets currently built.\n\
+ --host-target Target triple used to configure LLVM.\n\
+ --build-mode Print build mode of LLVM tree (e.g. Debug or Release).\n\
+ --assertion-mode Print assertion mode of LLVM tree (ON or OFF).\n\
+ --build-system Print the build system used to build LLVM (always cmake).\n\
+ --has-rtti Print whether or not LLVM was built with rtti (YES or NO).\n\
+ --shared-mode Print how the provided components can be collectively linked (`shared` or `static`).\n\
+ --link-shared Link the components as shared libraries.\n\
+ --link-static Link the component libraries statically.\n\
+ --ignore-libllvm Ignore libLLVM and link component libraries instead.\n\
+Typical components:\n\
+ all All LLVM libraries (default).\n\
+ engine Either a native JIT or a bitcode interpreter.\n";
+ exit(1);
+}
+
+/// Compute the path to the main executable.
+std::string GetExecutablePath(const char *Argv0) {
+ // This just needs to be some symbol in the binary; C++ doesn't
+ // allow taking the address of ::main however.
+ void *P = (void *)(intptr_t)GetExecutablePath;
+ return llvm::sys::fs::getMainExecutable(Argv0, P);
+}
+
+/// Expand the semi-colon delimited LLVM_DYLIB_COMPONENTS into
+/// the full list of components.
+std::vector<std::string> GetAllDyLibComponents(const bool IsInDevelopmentTree,
+ const bool GetComponentNames,
+ const std::string &DirSep) {
+ std::vector<StringRef> DyLibComponents;
+
+ StringRef DyLibComponentsStr(LLVM_DYLIB_COMPONENTS);
+ size_t Offset = 0;
+ while (true) {
+ const size_t NextOffset = DyLibComponentsStr.find(';', Offset);
+ DyLibComponents.push_back(DyLibComponentsStr.substr(Offset, NextOffset-Offset));
+ if (NextOffset == std::string::npos) {
+ break;
+ }
+ Offset = NextOffset + 1;
+ }
+
+ assert(!DyLibComponents.empty());
+
+ return ComputeLibsForComponents(DyLibComponents,
+ /*IncludeNonInstalled=*/IsInDevelopmentTree,
+ GetComponentNames, nullptr, nullptr, DirSep);
+}
+
+int main(int argc, char **argv) {
+ std::vector<StringRef> Components;
+ bool PrintLibs = false, PrintLibNames = false, PrintLibFiles = false;
+ bool PrintSystemLibs = false, PrintSharedMode = false;
+ bool HasAnyOption = false;
+
+ // llvm-config is designed to support being run both from a development tree
+ // and from an installed path. We try and auto-detect which case we are in so
+ // that we can report the correct information when run from a development
+ // tree.
+ bool IsInDevelopmentTree;
+ enum { CMakeStyle, CMakeBuildModeStyle } DevelopmentTreeLayout;
+ llvm::SmallString<256> CurrentPath(GetExecutablePath(argv[0]));
+ std::string CurrentExecPrefix;
+ std::string ActiveObjRoot;
+
+ // If CMAKE_CFG_INTDIR is given, honor it as build mode.
+ char const *build_mode = LLVM_BUILDMODE;
+#if defined(CMAKE_CFG_INTDIR)
+ if (!(CMAKE_CFG_INTDIR[0] == '.' && CMAKE_CFG_INTDIR[1] == '\0'))
+ build_mode = CMAKE_CFG_INTDIR;
+#endif
+
+ // Create an absolute path, and pop up one directory (we expect to be inside a
+ // bin dir).
+ sys::fs::make_absolute(CurrentPath);
+ CurrentExecPrefix =
+ sys::path::parent_path(sys::path::parent_path(CurrentPath)).str();
+
+ // Check to see if we are inside a development tree by comparing to possible
+ // locations (prefix style or CMake style).
+ if (sys::fs::equivalent(CurrentExecPrefix, LLVM_OBJ_ROOT)) {
+ IsInDevelopmentTree = true;
+ DevelopmentTreeLayout = CMakeStyle;
+ ActiveObjRoot = LLVM_OBJ_ROOT;
+ } else if (sys::fs::equivalent(sys::path::parent_path(CurrentExecPrefix),
+ LLVM_OBJ_ROOT)) {
+ IsInDevelopmentTree = true;
+ DevelopmentTreeLayout = CMakeBuildModeStyle;
+ ActiveObjRoot = LLVM_OBJ_ROOT;
+ } else {
+ IsInDevelopmentTree = false;
+ DevelopmentTreeLayout = CMakeStyle; // Initialized to avoid warnings.
+ }
+
+ // Compute various directory locations based on the derived location
+ // information.
+ std::string ActivePrefix, ActiveBinDir, ActiveIncludeDir, ActiveLibDir,
+ ActiveCMakeDir;
+ std::string ActiveIncludeOption;
+ if (IsInDevelopmentTree) {
+ ActiveIncludeDir = std::string(LLVM_SRC_ROOT) + "/include";
+ ActivePrefix = CurrentExecPrefix;
+
+ // CMake organizes the products differently than a normal prefix style
+ // layout.
+ switch (DevelopmentTreeLayout) {
+ case CMakeStyle:
+ ActiveBinDir = ActiveObjRoot + "/bin";
+ ActiveLibDir = ActiveObjRoot + "/lib" + LLVM_LIBDIR_SUFFIX;
+ ActiveCMakeDir = ActiveLibDir + "/cmake/llvm";
+ break;
+ case CMakeBuildModeStyle:
+ // FIXME: Should we consider the build-mode-specific path as the prefix?
+ ActivePrefix = ActiveObjRoot;
+ ActiveBinDir = ActiveObjRoot + "/" + build_mode + "/bin";
+ ActiveLibDir =
+ ActiveObjRoot + "/" + build_mode + "/lib" + LLVM_LIBDIR_SUFFIX;
+ // The CMake directory isn't separated by build mode.
+ ActiveCMakeDir =
+ ActivePrefix + "/lib" + LLVM_LIBDIR_SUFFIX + "/cmake/llvm";
+ break;
+ }
+
+ // We need to include files from both the source and object trees.
+ ActiveIncludeOption =
+ ("-I" + ActiveIncludeDir + " " + "-I" + ActiveObjRoot + "/include");
+ } else {
+ ActivePrefix = CurrentExecPrefix;
+ {
+ SmallString<256> Path(LLVM_INSTALL_INCLUDEDIR);
+ sys::fs::make_absolute(ActivePrefix, Path);
+ ActiveIncludeDir = std::string(Path.str());
+ }
+ {
+ SmallString<256> Path(LLVM_INSTALL_BINDIR);
+ sys::fs::make_absolute(ActivePrefix, Path);
+ ActiveBinDir = std::string(Path.str());
+ }
+ {
+ SmallString<256> Path(LLVM_INSTALL_LIBDIR LLVM_LIBDIR_SUFFIX);
+ sys::fs::make_absolute(ActivePrefix, Path);
+ ActiveLibDir = std::string(Path.str());
+ }
+ {
+ SmallString<256> Path(LLVM_INSTALL_CMAKEDIR);
+ sys::fs::make_absolute(ActivePrefix, Path);
+ ActiveCMakeDir = std::string(Path.str());
+ }
+ ActiveIncludeOption = "-I" + ActiveIncludeDir;
+ }
+
+ /// We only use `shared library` mode in cases where the static library form
+ /// of the components provided are not available; note however that this is
+ /// skipped if we're run from within the build dir. However, once installed,
+ /// we still need to provide correct output when the static archives are
+ /// removed or, as in the case of CMake's `BUILD_SHARED_LIBS`, never present
+ /// in the first place. This can't be done at configure/build time.
+
+ StringRef SharedExt, SharedVersionedExt, SharedDir, SharedPrefix, StaticExt,
+ StaticPrefix, StaticDir = "lib";
+ std::string DirSep = "/";
+ const Triple HostTriple(Triple::normalize(LLVM_HOST_TRIPLE));
+ if (HostTriple.isOSWindows()) {
+ SharedExt = "dll";
+ SharedVersionedExt = LLVM_DYLIB_VERSION ".dll";
+ if (HostTriple.isOSCygMing()) {
+ SharedPrefix = "lib";
+ StaticExt = "a";
+ StaticPrefix = "lib";
+ } else {
+ StaticExt = "lib";
+ DirSep = "\\";
+ std::replace(ActiveObjRoot.begin(), ActiveObjRoot.end(), '/', '\\');
+ std::replace(ActivePrefix.begin(), ActivePrefix.end(), '/', '\\');
+ std::replace(ActiveBinDir.begin(), ActiveBinDir.end(), '/', '\\');
+ std::replace(ActiveLibDir.begin(), ActiveLibDir.end(), '/', '\\');
+ std::replace(ActiveCMakeDir.begin(), ActiveCMakeDir.end(), '/', '\\');
+ std::replace(ActiveIncludeOption.begin(), ActiveIncludeOption.end(), '/',
+ '\\');
+ }
+ SharedDir = ActiveBinDir;
+ StaticDir = ActiveLibDir;
+ } else if (HostTriple.isOSDarwin()) {
+ SharedExt = "dylib";
+ SharedVersionedExt = LLVM_DYLIB_VERSION ".dylib";
+ StaticExt = "a";
+ StaticDir = SharedDir = ActiveLibDir;
+ StaticPrefix = SharedPrefix = "lib";
+ } else {
+ // default to the unix values:
+ SharedExt = "so";
+ SharedVersionedExt = LLVM_DYLIB_VERSION ".so";
+ StaticExt = "a";
+ StaticDir = SharedDir = ActiveLibDir;
+ StaticPrefix = SharedPrefix = "lib";
+ }
+
+ const bool BuiltDyLib = !!LLVM_ENABLE_DYLIB;
+
+ /// CMake style shared libs, ie each component is in a shared library.
+ const bool BuiltSharedLibs = !!LLVM_ENABLE_SHARED;
+
+ bool DyLibExists = false;
+ const std::string DyLibName =
+ (SharedPrefix + "LLVM-" + SharedVersionedExt).str();
+
+ // If LLVM_LINK_DYLIB is ON, the single shared library will be returned
+ // for "--libs", etc, if they exist. This behaviour can be overridden with
+ // --link-static or --link-shared.
+ bool LinkDyLib = !!LLVM_LINK_DYLIB;
+
+ if (BuiltDyLib) {
+ std::string path((SharedDir + DirSep + DyLibName).str());
+ if (DirSep == "\\") {
+ std::replace(path.begin(), path.end(), '/', '\\');
+ }
+ DyLibExists = sys::fs::exists(path);
+ if (!DyLibExists) {
+ // The shared library does not exist: don't error unless the user
+ // explicitly passes --link-shared.
+ LinkDyLib = false;
+ }
+ }
+ LinkMode LinkMode =
+ (LinkDyLib || BuiltSharedLibs) ? LinkModeShared : LinkModeAuto;
+
+ /// Get the component's library name without the lib prefix and the
+ /// extension. Returns true if Lib is in a recognized format.
+ auto GetComponentLibraryNameSlice = [&](const StringRef &Lib,
+ StringRef &Out) {
+ if (Lib.startswith("lib")) {
+ unsigned FromEnd;
+ if (Lib.endswith(StaticExt)) {
+ FromEnd = StaticExt.size() + 1;
+ } else if (Lib.endswith(SharedExt)) {
+ FromEnd = SharedExt.size() + 1;
+ } else {
+ FromEnd = 0;
+ }
+
+ if (FromEnd != 0) {
+ Out = Lib.slice(3, Lib.size() - FromEnd);
+ return true;
+ }
+ }
+
+ return false;
+ };
+ /// Maps Unixizms to the host platform.
+ auto GetComponentLibraryFileName = [&](const StringRef &Lib,
+ const bool Shared) {
+ std::string LibFileName;
+ if (Shared) {
+ if (Lib == DyLibName) {
+ // Treat the DyLibName specially. It is not a component library and
+ // already has the necessary prefix and suffix (e.g. `.so`) added so
+ // just return it unmodified.
+ assert(Lib.endswith(SharedExt) && "DyLib is missing suffix");
+ LibFileName = std::string(Lib);
+ } else {
+ LibFileName = (SharedPrefix + Lib + "." + SharedExt).str();
+ }
+ } else {
+ // default to static
+ LibFileName = (StaticPrefix + Lib + "." + StaticExt).str();
+ }
+
+ return LibFileName;
+ };
+ /// Get the full path for a possibly shared component library.
+ auto GetComponentLibraryPath = [&](const StringRef &Name, const bool Shared) {
+ auto LibFileName = GetComponentLibraryFileName(Name, Shared);
+ if (Shared) {
+ return (SharedDir + DirSep + LibFileName).str();
+ } else {
+ return (StaticDir + DirSep + LibFileName).str();
+ }
+ };
+
+ raw_ostream &OS = outs();
+ for (int i = 1; i != argc; ++i) {
+ StringRef Arg = argv[i];
+
+ if (Arg.startswith("-")) {
+ HasAnyOption = true;
+ if (Arg == "--version") {
+ OS << PACKAGE_VERSION << '\n';
+ } else if (Arg == "--prefix") {
+ OS << ActivePrefix << '\n';
+ } else if (Arg == "--bindir") {
+ OS << ActiveBinDir << '\n';
+ } else if (Arg == "--includedir") {
+ OS << ActiveIncludeDir << '\n';
+ } else if (Arg == "--libdir") {
+ OS << ActiveLibDir << '\n';
+ } else if (Arg == "--cmakedir") {
+ OS << ActiveCMakeDir << '\n';
+ } else if (Arg == "--cppflags") {
+ OS << ActiveIncludeOption << ' ' << LLVM_CPPFLAGS << '\n';
+ } else if (Arg == "--cflags") {
+ OS << ActiveIncludeOption << ' ' << LLVM_CFLAGS << '\n';
+ } else if (Arg == "--cxxflags") {
+ OS << ActiveIncludeOption << ' ' << LLVM_CXXFLAGS << '\n';
+ } else if (Arg == "--ldflags") {
+ OS << ((HostTriple.isWindowsMSVCEnvironment()) ? "-LIBPATH:" : "-L")
+ << ActiveLibDir << ' ' << LLVM_LDFLAGS << '\n';
+ } else if (Arg == "--system-libs") {
+ PrintSystemLibs = true;
+ } else if (Arg == "--libs") {
+ PrintLibs = true;
+ } else if (Arg == "--libnames") {
+ PrintLibNames = true;
+ } else if (Arg == "--libfiles") {
+ PrintLibFiles = true;
+ } else if (Arg == "--components") {
+ /// If there are missing static archives and a dylib was
+ /// built, print LLVM_DYLIB_COMPONENTS instead of everything
+ /// in the manifest.
+ std::vector<std::string> Components;
+ for (unsigned j = 0; j != array_lengthof(AvailableComponents); ++j) {
+ // Only include non-installed components when in a development tree.
+ if (!AvailableComponents[j].IsInstalled && !IsInDevelopmentTree)
+ continue;
+
+ Components.push_back(AvailableComponents[j].Name);
+ if (AvailableComponents[j].Library && !IsInDevelopmentTree) {
+ std::string path(
+ GetComponentLibraryPath(AvailableComponents[j].Library, false));
+ if (DirSep == "\\") {
+ std::replace(path.begin(), path.end(), '/', '\\');
+ }
+ if (DyLibExists && !sys::fs::exists(path)) {
+ Components =
+ GetAllDyLibComponents(IsInDevelopmentTree, true, DirSep);
+ llvm::sort(Components);
+ break;
+ }
+ }
+ }
+
+ for (unsigned I = 0; I < Components.size(); ++I) {
+ if (I) {
+ OS << ' ';
+ }
+
+ OS << Components[I];
+ }
+ OS << '\n';
+ } else if (Arg == "--targets-built") {
+ OS << LLVM_TARGETS_BUILT << '\n';
+ } else if (Arg == "--host-target") {
+ OS << Triple::normalize(LLVM_DEFAULT_TARGET_TRIPLE) << '\n';
+ } else if (Arg == "--build-mode") {
+ OS << build_mode << '\n';
+ } else if (Arg == "--assertion-mode") {
+#if defined(NDEBUG)
+ OS << "OFF\n";
+#else
+ OS << "ON\n";
+#endif
+ } else if (Arg == "--build-system") {
+ OS << LLVM_BUILD_SYSTEM << '\n';
+ } else if (Arg == "--has-rtti") {
+ OS << (LLVM_HAS_RTTI ? "YES" : "NO") << '\n';
+ } else if (Arg == "--shared-mode") {
+ PrintSharedMode = true;
+ } else if (Arg == "--obj-root") {
+ OS << ActivePrefix << '\n';
+ } else if (Arg == "--src-root") {
+ OS << LLVM_SRC_ROOT << '\n';
+ } else if (Arg == "--ignore-libllvm") {
+ LinkDyLib = false;
+ LinkMode = BuiltSharedLibs ? LinkModeShared : LinkModeAuto;
+ } else if (Arg == "--link-shared") {
+ LinkMode = LinkModeShared;
+ } else if (Arg == "--link-static") {
+ LinkMode = LinkModeStatic;
+ } else {
+ usage();
+ }
+ } else {
+ Components.push_back(Arg);
+ }
+ }
+
+ if (!HasAnyOption)
+ usage();
+
+ if (LinkMode == LinkModeShared && !DyLibExists && !BuiltSharedLibs) {
+ WithColor::error(errs(), "llvm-config") << DyLibName << " is missing\n";
+ return 1;
+ }
+
+ if (PrintLibs || PrintLibNames || PrintLibFiles || PrintSystemLibs ||
+ PrintSharedMode) {
+
+ if (PrintSharedMode && BuiltSharedLibs) {
+ OS << "shared\n";
+ return 0;
+ }
+
+ // If no components were specified, default to "all".
+ if (Components.empty())
+ Components.push_back("all");
+
+ // Construct the list of all the required libraries.
+ std::function<std::string(const StringRef &)>
+ GetComponentLibraryPathFunction = [&](const StringRef &Name) {
+ return GetComponentLibraryPath(Name, LinkMode == LinkModeShared);
+ };
+ std::vector<std::string> MissingLibs;
+ std::vector<std::string> RequiredLibs = ComputeLibsForComponents(
+ Components,
+ /*IncludeNonInstalled=*/IsInDevelopmentTree, false,
+ &GetComponentLibraryPathFunction, &MissingLibs, DirSep);
+ if (!MissingLibs.empty()) {
+ switch (LinkMode) {
+ case LinkModeShared:
+ if (LinkDyLib && !BuiltSharedLibs)
+ break;
+ // Using component shared libraries.
+ for (auto &Lib : MissingLibs)
+ WithColor::error(errs(), "llvm-config") << "missing: " << Lib << "\n";
+ return 1;
+ case LinkModeAuto:
+ if (DyLibExists) {
+ LinkMode = LinkModeShared;
+ break;
+ }
+ WithColor::error(errs(), "llvm-config")
+ << "component libraries and shared library\n\n";
+ LLVM_FALLTHROUGH;
+ case LinkModeStatic:
+ for (auto &Lib : MissingLibs)
+ WithColor::error(errs(), "llvm-config") << "missing: " << Lib << "\n";
+ return 1;
+ }
+ } else if (LinkMode == LinkModeAuto) {
+ LinkMode = LinkModeStatic;
+ }
+
+ if (PrintSharedMode) {
+ std::unordered_set<std::string> FullDyLibComponents;
+ std::vector<std::string> DyLibComponents =
+ GetAllDyLibComponents(IsInDevelopmentTree, false, DirSep);
+
+ for (auto &Component : DyLibComponents) {
+ FullDyLibComponents.insert(Component);
+ }
+ DyLibComponents.clear();
+
+ for (auto &Lib : RequiredLibs) {
+ if (!FullDyLibComponents.count(Lib)) {
+ OS << "static\n";
+ return 0;
+ }
+ }
+ FullDyLibComponents.clear();
+
+ if (LinkMode == LinkModeShared) {
+ OS << "shared\n";
+ return 0;
+ } else {
+ OS << "static\n";
+ return 0;
+ }
+ }
+
+ if (PrintLibs || PrintLibNames || PrintLibFiles) {
+
+ auto PrintForLib = [&](const StringRef &Lib) {
+ const bool Shared = LinkMode == LinkModeShared;
+ if (PrintLibNames) {
+ OS << GetComponentLibraryFileName(Lib, Shared);
+ } else if (PrintLibFiles) {
+ OS << GetComponentLibraryPath(Lib, Shared);
+ } else if (PrintLibs) {
+ // On Windows, output full path to library without parameters.
+ // Elsewhere, if this is a typical library name, include it using -l.
+ if (HostTriple.isWindowsMSVCEnvironment()) {
+ OS << GetComponentLibraryPath(Lib, Shared);
+ } else {
+ StringRef LibName;
+ if (GetComponentLibraryNameSlice(Lib, LibName)) {
+ // Extract library name (remove prefix and suffix).
+ OS << "-l" << LibName;
+ } else {
+ // Lib is already a library name without prefix and suffix.
+ OS << "-l" << Lib;
+ }
+ }
+ }
+ };
+
+ if (LinkMode == LinkModeShared && LinkDyLib) {
+ PrintForLib(DyLibName);
+ } else {
+ for (unsigned i = 0, e = RequiredLibs.size(); i != e; ++i) {
+ auto Lib = RequiredLibs[i];
+ if (i)
+ OS << ' ';
+
+ PrintForLib(Lib);
+ }
+ }
+ OS << '\n';
+ }
+
+ // Print SYSTEM_LIBS after --libs.
+ // FIXME: Each LLVM component may have its dependent system libs.
+ if (PrintSystemLibs) {
+ // Output system libraries only if linking against a static
+ // library (since the shared library links to all system libs
+ // already)
+ OS << (LinkMode == LinkModeStatic ? LLVM_SYSTEM_LIBS : "") << '\n';
+ }
+ } else if (!Components.empty()) {
+ WithColor::error(errs(), "llvm-config")
+ << "components given, but unused\n\n";
+ usage();
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-config/ya.make b/contrib/libs/llvm14/tools/llvm-config/ya.make
new file mode 100644
index 00000000000..621cad12d06
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-config/ya.make
@@ -0,0 +1,31 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-config
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+CFLAGS(
+ -DCMAKE_CFG_INTDIR=\".\"
+)
+
+SRCS(
+ llvm-config.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CodeCoverage.cpp b/contrib/libs/llvm14/tools/llvm-cov/CodeCoverage.cpp
new file mode 100644
index 00000000000..ef801287c1b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CodeCoverage.cpp
@@ -0,0 +1,1218 @@
+//===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The 'CodeCoverageTool' class implements a command line tool to analyze and
+// report coverage information using the profiling instrumentation and code
+// coverage mapping.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoverageExporterJson.h"
+#include "CoverageExporterLcov.h"
+#include "CoverageFilters.h"
+#include "CoverageReport.h"
+#include "CoverageSummaryInfo.h"
+#include "CoverageViewOptions.h"
+#include "RenderingSupport.h"
+#include "SourceCoverageView.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
+#include "llvm/ProfileData/InstrProfReader.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/VirtualFileSystem.h"
+
+#include <functional>
+#include <map>
+#include <system_error>
+
+using namespace llvm;
+using namespace coverage;
+
+void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping,
+ const CoverageViewOptions &Options,
+ raw_ostream &OS);
+
+namespace {
+/// The implementation of the coverage tool.
+class CodeCoverageTool {
+public:
+ enum Command {
+ /// The show command.
+ Show,
+ /// The report command.
+ Report,
+ /// The export command.
+ Export
+ };
+
+ int run(Command Cmd, int argc, const char **argv);
+
+private:
+ /// Print the error message to the error output stream.
+ void error(const Twine &Message, StringRef Whence = "");
+
+ /// Print the warning message to the error output stream.
+ void warning(const Twine &Message, StringRef Whence = "");
+
+ /// Convert \p Path into an absolute path and append it to the list
+ /// of collected paths.
+ void addCollectedPath(const std::string &Path);
+
+ /// If \p Path is a regular file, collect the path. If it's a
+ /// directory, recursively collect all of the paths within the directory.
+ void collectPaths(const std::string &Path);
+
+ /// Check if the two given files are the same file.
+ bool isEquivalentFile(StringRef FilePath1, StringRef FilePath2);
+
+ /// Retrieve a file status with a cache.
+ Optional<sys::fs::file_status> getFileStatus(StringRef FilePath);
+
+ /// Return a memory buffer for the given source file.
+ ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
+
+ /// Create source views for the expansions of the view.
+ void attachExpansionSubViews(SourceCoverageView &View,
+ ArrayRef<ExpansionRecord> Expansions,
+ const CoverageMapping &Coverage);
+
+ /// Create source views for the branches of the view.
+ void attachBranchSubViews(SourceCoverageView &View, StringRef SourceName,
+ ArrayRef<CountedRegion> Branches,
+ const MemoryBuffer &File,
+ CoverageData &CoverageInfo);
+
+ /// Create the source view of a particular function.
+ std::unique_ptr<SourceCoverageView>
+ createFunctionView(const FunctionRecord &Function,
+ const CoverageMapping &Coverage);
+
+ /// Create the main source view of a particular source file.
+ std::unique_ptr<SourceCoverageView>
+ createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
+
+ /// Load the coverage mapping data. Return nullptr if an error occurred.
+ std::unique_ptr<CoverageMapping> load();
+
+ /// Create a mapping from files in the Coverage data to local copies
+ /// (path-equivalence).
+ void remapPathNames(const CoverageMapping &Coverage);
+
+ /// Remove input source files which aren't mapped by \p Coverage.
+ void removeUnmappedInputs(const CoverageMapping &Coverage);
+
+ /// If a demangler is available, demangle all symbol names.
+ void demangleSymbols(const CoverageMapping &Coverage);
+
+ /// Write out a source file view to the filesystem.
+ void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage,
+ CoveragePrinter *Printer, bool ShowFilenames);
+
+ typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
+
+ int doShow(int argc, const char **argv,
+ CommandLineParserType commandLineParser);
+
+ int doReport(int argc, const char **argv,
+ CommandLineParserType commandLineParser);
+
+ int doExport(int argc, const char **argv,
+ CommandLineParserType commandLineParser);
+
+ std::vector<StringRef> ObjectFilenames;
+ CoverageViewOptions ViewOpts;
+ CoverageFiltersMatchAll Filters;
+ CoverageFilters IgnoreFilenameFilters;
+
+ /// True if InputSourceFiles are provided.
+ bool HadSourceFiles = false;
+
+ /// The path to the indexed profile.
+ std::string PGOFilename;
+
+ /// A list of input source files.
+ std::vector<std::string> SourceFiles;
+
+ /// In -path-equivalence mode, this maps the absolute paths from the coverage
+ /// mapping data to the input source files.
+ StringMap<std::string> RemappedFilenames;
+
+ /// The coverage data path to be remapped from, and the source path to be
+ /// remapped to, when using -path-equivalence.
+ Optional<std::pair<std::string, std::string>> PathRemapping;
+
+ /// File status cache used when finding the same file.
+ StringMap<Optional<sys::fs::file_status>> FileStatusCache;
+
+ /// The architecture the coverage mapping data targets.
+ std::vector<StringRef> CoverageArches;
+
+ /// A cache for demangled symbols.
+ DemangleCache DC;
+
+ /// A lock which guards printing to stderr.
+ std::mutex ErrsLock;
+
+ /// A container for input source file buffers.
+ std::mutex LoadedSourceFilesLock;
+ std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
+ LoadedSourceFiles;
+
+ /// Allowlist from -name-allowlist to be used for filtering.
+ std::unique_ptr<SpecialCaseList> NameAllowlist;
+};
+}
+
+static std::string getErrorString(const Twine &Message, StringRef Whence,
+ bool Warning) {
+ std::string Str = (Warning ? "warning" : "error");
+ Str += ": ";
+ if (!Whence.empty())
+ Str += Whence.str() + ": ";
+ Str += Message.str() + "\n";
+ return Str;
+}
+
+void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
+ std::unique_lock<std::mutex> Guard{ErrsLock};
+ ViewOpts.colored_ostream(errs(), raw_ostream::RED)
+ << getErrorString(Message, Whence, false);
+}
+
+void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) {
+ std::unique_lock<std::mutex> Guard{ErrsLock};
+ ViewOpts.colored_ostream(errs(), raw_ostream::RED)
+ << getErrorString(Message, Whence, true);
+}
+
+void CodeCoverageTool::addCollectedPath(const std::string &Path) {
+ SmallString<128> EffectivePath(Path);
+ if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) {
+ error(EC.message(), Path);
+ return;
+ }
+ sys::path::remove_dots(EffectivePath, /*remove_dot_dot=*/true);
+ if (!IgnoreFilenameFilters.matchesFilename(EffectivePath))
+ SourceFiles.emplace_back(EffectivePath.str());
+ HadSourceFiles = !SourceFiles.empty();
+}
+
+void CodeCoverageTool::collectPaths(const std::string &Path) {
+ llvm::sys::fs::file_status Status;
+ llvm::sys::fs::status(Path, Status);
+ if (!llvm::sys::fs::exists(Status)) {
+ if (PathRemapping)
+ addCollectedPath(Path);
+ else
+ warning("Source file doesn't exist, proceeded by ignoring it.", Path);
+ return;
+ }
+
+ if (llvm::sys::fs::is_regular_file(Status)) {
+ addCollectedPath(Path);
+ return;
+ }
+
+ if (llvm::sys::fs::is_directory(Status)) {
+ std::error_code EC;
+ for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E;
+ F != E; F.increment(EC)) {
+
+ auto Status = F->status();
+ if (!Status) {
+ warning(Status.getError().message(), F->path());
+ continue;
+ }
+
+ if (Status->type() == llvm::sys::fs::file_type::regular_file)
+ addCollectedPath(F->path());
+ }
+ }
+}
+
+Optional<sys::fs::file_status>
+CodeCoverageTool::getFileStatus(StringRef FilePath) {
+ auto It = FileStatusCache.try_emplace(FilePath);
+ auto &CachedStatus = It.first->getValue();
+ if (!It.second)
+ return CachedStatus;
+
+ sys::fs::file_status Status;
+ if (!sys::fs::status(FilePath, Status))
+ CachedStatus = Status;
+ return CachedStatus;
+}
+
+bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1,
+ StringRef FilePath2) {
+ auto Status1 = getFileStatus(FilePath1);
+ auto Status2 = getFileStatus(FilePath2);
+ return Status1.hasValue() && Status2.hasValue() &&
+ sys::fs::equivalent(Status1.getValue(), Status2.getValue());
+}
+
+ErrorOr<const MemoryBuffer &>
+CodeCoverageTool::getSourceFile(StringRef SourceFile) {
+ // If we've remapped filenames, look up the real location for this file.
+ std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock};
+ if (!RemappedFilenames.empty()) {
+ auto Loc = RemappedFilenames.find(SourceFile);
+ if (Loc != RemappedFilenames.end())
+ SourceFile = Loc->second;
+ }
+ for (const auto &Files : LoadedSourceFiles)
+ if (isEquivalentFile(SourceFile, Files.first))
+ return *Files.second;
+ auto Buffer = MemoryBuffer::getFile(SourceFile);
+ if (auto EC = Buffer.getError()) {
+ error(EC.message(), SourceFile);
+ return EC;
+ }
+ LoadedSourceFiles.emplace_back(std::string(SourceFile),
+ std::move(Buffer.get()));
+ return *LoadedSourceFiles.back().second;
+}
+
+void CodeCoverageTool::attachExpansionSubViews(
+ SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions,
+ const CoverageMapping &Coverage) {
+ if (!ViewOpts.ShowExpandedRegions)
+ return;
+ for (const auto &Expansion : Expansions) {
+ auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
+ if (ExpansionCoverage.empty())
+ continue;
+ auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
+ if (!SourceBuffer)
+ continue;
+
+ auto SubViewBranches = ExpansionCoverage.getBranches();
+ auto SubViewExpansions = ExpansionCoverage.getExpansions();
+ auto SubView =
+ SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
+ ViewOpts, std::move(ExpansionCoverage));
+ attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
+ attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches,
+ SourceBuffer.get(), ExpansionCoverage);
+ View.addExpansion(Expansion.Region, std::move(SubView));
+ }
+}
+
+void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View,
+ StringRef SourceName,
+ ArrayRef<CountedRegion> Branches,
+ const MemoryBuffer &File,
+ CoverageData &CoverageInfo) {
+ if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents)
+ return;
+
+ const auto *NextBranch = Branches.begin();
+ const auto *EndBranch = Branches.end();
+
+ // Group branches that have the same line number into the same subview.
+ while (NextBranch != EndBranch) {
+ std::vector<CountedRegion> ViewBranches;
+ unsigned CurrentLine = NextBranch->LineStart;
+
+ while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart)
+ ViewBranches.push_back(*NextBranch++);
+
+ if (!ViewBranches.empty()) {
+ auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts,
+ std::move(CoverageInfo));
+ View.addBranch(CurrentLine, ViewBranches, std::move(SubView));
+ }
+ }
+}
+
+std::unique_ptr<SourceCoverageView>
+CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
+ const CoverageMapping &Coverage) {
+ auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
+ if (FunctionCoverage.empty())
+ return nullptr;
+ auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
+ if (!SourceBuffer)
+ return nullptr;
+
+ auto Branches = FunctionCoverage.getBranches();
+ auto Expansions = FunctionCoverage.getExpansions();
+ auto View = SourceCoverageView::create(DC.demangle(Function.Name),
+ SourceBuffer.get(), ViewOpts,
+ std::move(FunctionCoverage));
+ attachExpansionSubViews(*View, Expansions, Coverage);
+ attachBranchSubViews(*View, DC.demangle(Function.Name), Branches,
+ SourceBuffer.get(), FunctionCoverage);
+
+ return View;
+}
+
+std::unique_ptr<SourceCoverageView>
+CodeCoverageTool::createSourceFileView(StringRef SourceFile,
+ const CoverageMapping &Coverage) {
+ auto SourceBuffer = getSourceFile(SourceFile);
+ if (!SourceBuffer)
+ return nullptr;
+ auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
+ if (FileCoverage.empty())
+ return nullptr;
+
+ auto Branches = FileCoverage.getBranches();
+ auto Expansions = FileCoverage.getExpansions();
+ auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
+ ViewOpts, std::move(FileCoverage));
+ attachExpansionSubViews(*View, Expansions, Coverage);
+ attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(),
+ FileCoverage);
+ if (!ViewOpts.ShowFunctionInstantiations)
+ return View;
+
+ for (const auto &Group : Coverage.getInstantiationGroups(SourceFile)) {
+ // Skip functions which have a single instantiation.
+ if (Group.size() < 2)
+ continue;
+
+ for (const FunctionRecord *Function : Group.getInstantiations()) {
+ std::unique_ptr<SourceCoverageView> SubView{nullptr};
+
+ StringRef Funcname = DC.demangle(Function->Name);
+
+ if (Function->ExecutionCount > 0) {
+ auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
+ auto SubViewExpansions = SubViewCoverage.getExpansions();
+ auto SubViewBranches = SubViewCoverage.getBranches();
+ SubView = SourceCoverageView::create(
+ Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
+ attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
+ attachBranchSubViews(*SubView, SourceFile, SubViewBranches,
+ SourceBuffer.get(), SubViewCoverage);
+ }
+
+ unsigned FileID = Function->CountedRegions.front().FileID;
+ unsigned Line = 0;
+ for (const auto &CR : Function->CountedRegions)
+ if (CR.FileID == FileID)
+ Line = std::max(CR.LineEnd, Line);
+ View->addInstantiation(Funcname, Line, std::move(SubView));
+ }
+ }
+ return View;
+}
+
+static bool modifiedTimeGT(StringRef LHS, StringRef RHS) {
+ sys::fs::file_status Status;
+ if (sys::fs::status(LHS, Status))
+ return false;
+ auto LHSTime = Status.getLastModificationTime();
+ if (sys::fs::status(RHS, Status))
+ return false;
+ auto RHSTime = Status.getLastModificationTime();
+ return LHSTime > RHSTime;
+}
+
+std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
+ for (StringRef ObjectFilename : ObjectFilenames)
+ if (modifiedTimeGT(ObjectFilename, PGOFilename))
+ warning("profile data may be out of date - object is newer",
+ ObjectFilename);
+ auto CoverageOrErr =
+ CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches,
+ ViewOpts.CompilationDirectory);
+ if (Error E = CoverageOrErr.takeError()) {
+ error("Failed to load coverage: " + toString(std::move(E)),
+ join(ObjectFilenames.begin(), ObjectFilenames.end(), ", "));
+ return nullptr;
+ }
+ auto Coverage = std::move(CoverageOrErr.get());
+ unsigned Mismatched = Coverage->getMismatchedCount();
+ if (Mismatched) {
+ warning(Twine(Mismatched) + " functions have mismatched data");
+
+ if (ViewOpts.Debug) {
+ for (const auto &HashMismatch : Coverage->getHashMismatches())
+ errs() << "hash-mismatch: "
+ << "No profile record found for '" << HashMismatch.first << "'"
+ << " with hash = 0x" << Twine::utohexstr(HashMismatch.second)
+ << '\n';
+ }
+ }
+
+ remapPathNames(*Coverage);
+
+ if (!SourceFiles.empty())
+ removeUnmappedInputs(*Coverage);
+
+ demangleSymbols(*Coverage);
+
+ return Coverage;
+}
+
+void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
+ if (!PathRemapping)
+ return;
+
+ // Convert remapping paths to native paths with trailing seperators.
+ auto nativeWithTrailing = [](StringRef Path) -> std::string {
+ if (Path.empty())
+ return "";
+ SmallString<128> NativePath;
+ sys::path::native(Path, NativePath);
+ sys::path::remove_dots(NativePath, true);
+ if (!NativePath.empty() && !sys::path::is_separator(NativePath.back()))
+ NativePath += sys::path::get_separator();
+ return NativePath.c_str();
+ };
+ std::string RemapFrom = nativeWithTrailing(PathRemapping->first);
+ std::string RemapTo = nativeWithTrailing(PathRemapping->second);
+
+ // Create a mapping from coverage data file paths to local paths.
+ for (StringRef Filename : Coverage.getUniqueSourceFiles()) {
+ SmallString<128> NativeFilename;
+ sys::path::native(Filename, NativeFilename);
+ sys::path::remove_dots(NativeFilename, true);
+ if (NativeFilename.startswith(RemapFrom)) {
+ RemappedFilenames[Filename] =
+ RemapTo + NativeFilename.substr(RemapFrom.size()).str();
+ }
+ }
+
+ // Convert input files from local paths to coverage data file paths.
+ StringMap<std::string> InvRemappedFilenames;
+ for (const auto &RemappedFilename : RemappedFilenames)
+ InvRemappedFilenames[RemappedFilename.getValue()] =
+ std::string(RemappedFilename.getKey());
+
+ for (std::string &Filename : SourceFiles) {
+ SmallString<128> NativeFilename;
+ sys::path::native(Filename, NativeFilename);
+ auto CovFileName = InvRemappedFilenames.find(NativeFilename);
+ if (CovFileName != InvRemappedFilenames.end())
+ Filename = CovFileName->second;
+ }
+}
+
+void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) {
+ std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles();
+
+ // The user may have specified source files which aren't in the coverage
+ // mapping. Filter these files away.
+ llvm::erase_if(SourceFiles, [&](const std::string &SF) {
+ return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), SF);
+ });
+}
+
+void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
+ if (!ViewOpts.hasDemangler())
+ return;
+
+ // Pass function names to the demangler in a temporary file.
+ int InputFD;
+ SmallString<256> InputPath;
+ std::error_code EC =
+ sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath);
+ if (EC) {
+ error(InputPath, EC.message());
+ return;
+ }
+ ToolOutputFile InputTOF{InputPath, InputFD};
+
+ unsigned NumSymbols = 0;
+ for (const auto &Function : Coverage.getCoveredFunctions()) {
+ InputTOF.os() << Function.Name << '\n';
+ ++NumSymbols;
+ }
+ InputTOF.os().close();
+
+ // Use another temporary file to store the demangler's output.
+ int OutputFD;
+ SmallString<256> OutputPath;
+ EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD,
+ OutputPath);
+ if (EC) {
+ error(OutputPath, EC.message());
+ return;
+ }
+ ToolOutputFile OutputTOF{OutputPath, OutputFD};
+ OutputTOF.os().close();
+
+ // Invoke the demangler.
+ std::vector<StringRef> ArgsV;
+ for (StringRef Arg : ViewOpts.DemanglerOpts)
+ ArgsV.push_back(Arg);
+ Optional<StringRef> Redirects[] = {InputPath.str(), OutputPath.str(), {""}};
+ std::string ErrMsg;
+ int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV,
+ /*env=*/None, Redirects, /*secondsToWait=*/0,
+ /*memoryLimit=*/0, &ErrMsg);
+ if (RC) {
+ error(ErrMsg, ViewOpts.DemanglerOpts[0]);
+ return;
+ }
+
+ // Parse the demangler's output.
+ auto BufOrError = MemoryBuffer::getFile(OutputPath);
+ if (!BufOrError) {
+ error(OutputPath, BufOrError.getError().message());
+ return;
+ }
+
+ std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError);
+
+ SmallVector<StringRef, 8> Symbols;
+ StringRef DemanglerData = DemanglerBuf->getBuffer();
+ DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols,
+ /*KeepEmpty=*/false);
+ if (Symbols.size() != NumSymbols) {
+ error("Demangler did not provide expected number of symbols");
+ return;
+ }
+
+ // Cache the demangled names.
+ unsigned I = 0;
+ for (const auto &Function : Coverage.getCoveredFunctions())
+ // On Windows, lines in the demangler's output file end with "\r\n".
+ // Splitting by '\n' keeps '\r's, so cut them now.
+ DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim());
+}
+
+void CodeCoverageTool::writeSourceFileView(StringRef SourceFile,
+ CoverageMapping *Coverage,
+ CoveragePrinter *Printer,
+ bool ShowFilenames) {
+ auto View = createSourceFileView(SourceFile, *Coverage);
+ if (!View) {
+ warning("The file '" + SourceFile + "' isn't covered.");
+ return;
+ }
+
+ auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false);
+ if (Error E = OSOrErr.takeError()) {
+ error("Could not create view file!", toString(std::move(E)));
+ return;
+ }
+ auto OS = std::move(OSOrErr.get());
+
+ View->print(*OS.get(), /*Wholefile=*/true,
+ /*ShowSourceName=*/ShowFilenames,
+ /*ShowTitle=*/ViewOpts.hasOutputDirectory());
+ Printer->closeViewFile(std::move(OS));
+}
+
+int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
+ cl::opt<std::string> CovFilename(
+ cl::Positional, cl::desc("Covered executable or object file."));
+
+ cl::list<std::string> CovFilenames(
+ "object", cl::desc("Coverage executable or object file"), cl::ZeroOrMore);
+
+ cl::opt<bool> DebugDumpCollectedObjects(
+ "dump-collected-objects", cl::Optional, cl::Hidden,
+ cl::desc("Show the collected coverage object files"));
+
+ cl::list<std::string> InputSourceFiles(
+ cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
+
+ cl::opt<bool> DebugDumpCollectedPaths(
+ "dump-collected-paths", cl::Optional, cl::Hidden,
+ cl::desc("Show the collected paths to source files"));
+
+ cl::opt<std::string, true> PGOFilename(
+ "instr-profile", cl::Required, cl::location(this->PGOFilename),
+ cl::desc(
+ "File with the profile data obtained after an instrumented run"));
+
+ cl::list<std::string> Arches(
+ "arch", cl::desc("architectures of the coverage mapping binaries"));
+
+ cl::opt<bool> DebugDump("dump", cl::Optional,
+ cl::desc("Show internal debug dump"));
+
+ cl::opt<CoverageViewOptions::OutputFormat> Format(
+ "format", cl::desc("Output format for line-based coverage reports"),
+ cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
+ "Text output"),
+ clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
+ "HTML output"),
+ clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov",
+ "lcov tracefile output")),
+ cl::init(CoverageViewOptions::OutputFormat::Text));
+
+ cl::opt<std::string> PathRemap(
+ "path-equivalence", cl::Optional,
+ cl::desc("<from>,<to> Map coverage data paths to local source file "
+ "paths"));
+
+ cl::OptionCategory FilteringCategory("Function filtering options");
+
+ cl::list<std::string> NameFilters(
+ "name", cl::Optional,
+ cl::desc("Show code coverage only for functions with the given name"),
+ cl::ZeroOrMore, cl::cat(FilteringCategory));
+
+ cl::list<std::string> NameFilterFiles(
+ "name-allowlist", cl::Optional,
+ cl::desc("Show code coverage only for functions listed in the given "
+ "file"),
+ cl::ZeroOrMore, cl::cat(FilteringCategory));
+
+ // Allow for accepting previous option name.
+ cl::list<std::string> NameFilterFilesDeprecated(
+ "name-whitelist", cl::Optional, cl::Hidden,
+ cl::desc("Show code coverage only for functions listed in the given "
+ "file. Deprecated, use -name-allowlist instead"),
+ cl::ZeroOrMore, cl::cat(FilteringCategory));
+
+ cl::list<std::string> NameRegexFilters(
+ "name-regex", cl::Optional,
+ cl::desc("Show code coverage only for functions that match the given "
+ "regular expression"),
+ cl::ZeroOrMore, cl::cat(FilteringCategory));
+
+ cl::list<std::string> IgnoreFilenameRegexFilters(
+ "ignore-filename-regex", cl::Optional,
+ cl::desc("Skip source code files with file paths that match the given "
+ "regular expression"),
+ cl::ZeroOrMore, cl::cat(FilteringCategory));
+
+ cl::opt<double> RegionCoverageLtFilter(
+ "region-coverage-lt", cl::Optional,
+ cl::desc("Show code coverage only for functions with region coverage "
+ "less than the given threshold"),
+ cl::cat(FilteringCategory));
+
+ cl::opt<double> RegionCoverageGtFilter(
+ "region-coverage-gt", cl::Optional,
+ cl::desc("Show code coverage only for functions with region coverage "
+ "greater than the given threshold"),
+ cl::cat(FilteringCategory));
+
+ cl::opt<double> LineCoverageLtFilter(
+ "line-coverage-lt", cl::Optional,
+ cl::desc("Show code coverage only for functions with line coverage less "
+ "than the given threshold"),
+ cl::cat(FilteringCategory));
+
+ cl::opt<double> LineCoverageGtFilter(
+ "line-coverage-gt", cl::Optional,
+ cl::desc("Show code coverage only for functions with line coverage "
+ "greater than the given threshold"),
+ cl::cat(FilteringCategory));
+
+ cl::opt<cl::boolOrDefault> UseColor(
+ "use-color", cl::desc("Emit colored output (default=autodetect)"),
+ cl::init(cl::BOU_UNSET));
+
+ cl::list<std::string> DemanglerOpts(
+ "Xdemangler", cl::desc("<demangler-path>|<demangler-option>"));
+
+ cl::opt<bool> RegionSummary(
+ "show-region-summary", cl::Optional,
+ cl::desc("Show region statistics in summary table"),
+ cl::init(true));
+
+ cl::opt<bool> BranchSummary(
+ "show-branch-summary", cl::Optional,
+ cl::desc("Show branch condition statistics in summary table"),
+ cl::init(true));
+
+ cl::opt<bool> InstantiationSummary(
+ "show-instantiation-summary", cl::Optional,
+ cl::desc("Show instantiation statistics in summary table"));
+
+ cl::opt<bool> SummaryOnly(
+ "summary-only", cl::Optional,
+ cl::desc("Export only summary information for each source file"));
+
+ cl::opt<unsigned> NumThreads(
+ "num-threads", cl::init(0),
+ cl::desc("Number of merge threads to use (default: autodetect)"));
+ cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
+ cl::aliasopt(NumThreads));
+
+ cl::opt<std::string> CompilationDirectory(
+ "compilation-dir", cl::init(""),
+ cl::desc("Directory used as a base for relative coverage mapping paths"));
+
+ auto commandLineParser = [&, this](int argc, const char **argv) -> int {
+ cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
+ ViewOpts.Debug = DebugDump;
+
+ if (!CovFilename.empty())
+ ObjectFilenames.emplace_back(CovFilename);
+ for (const std::string &Filename : CovFilenames)
+ ObjectFilenames.emplace_back(Filename);
+ if (ObjectFilenames.empty()) {
+ errs() << "No filenames specified!\n";
+ ::exit(1);
+ }
+
+ if (DebugDumpCollectedObjects) {
+ for (StringRef OF : ObjectFilenames)
+ outs() << OF << '\n';
+ ::exit(0);
+ }
+
+ ViewOpts.Format = Format;
+ switch (ViewOpts.Format) {
+ case CoverageViewOptions::OutputFormat::Text:
+ ViewOpts.Colors = UseColor == cl::BOU_UNSET
+ ? sys::Process::StandardOutHasColors()
+ : UseColor == cl::BOU_TRUE;
+ break;
+ case CoverageViewOptions::OutputFormat::HTML:
+ if (UseColor == cl::BOU_FALSE)
+ 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
+ // PathRemapping.
+ if (!PathRemap.empty()) {
+ auto EquivPair = StringRef(PathRemap).split(',');
+ if (EquivPair.first.empty() || EquivPair.second.empty()) {
+ error("invalid argument '" + PathRemap +
+ "', must be in format 'from,to'",
+ "-path-equivalence");
+ return 1;
+ }
+
+ PathRemapping = {std::string(EquivPair.first),
+ std::string(EquivPair.second)};
+ }
+
+ // If a demangler is supplied, check if it exists and register it.
+ if (!DemanglerOpts.empty()) {
+ auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);
+ if (!DemanglerPathOrErr) {
+ error("Could not find the demangler!",
+ DemanglerPathOrErr.getError().message());
+ return 1;
+ }
+ DemanglerOpts[0] = *DemanglerPathOrErr;
+ ViewOpts.DemanglerOpts.swap(DemanglerOpts);
+ }
+
+ // Read in -name-allowlist files.
+ if (!NameFilterFiles.empty() || !NameFilterFilesDeprecated.empty()) {
+ std::string SpecialCaseListErr;
+ if (!NameFilterFiles.empty())
+ NameAllowlist = SpecialCaseList::create(
+ NameFilterFiles, *vfs::getRealFileSystem(), SpecialCaseListErr);
+ if (!NameFilterFilesDeprecated.empty())
+ NameAllowlist = SpecialCaseList::create(NameFilterFilesDeprecated,
+ *vfs::getRealFileSystem(),
+ SpecialCaseListErr);
+
+ if (!NameAllowlist)
+ error(SpecialCaseListErr);
+ }
+
+ // Create the function filters
+ if (!NameFilters.empty() || NameAllowlist || !NameRegexFilters.empty()) {
+ auto NameFilterer = std::make_unique<CoverageFilters>();
+ for (const auto &Name : NameFilters)
+ NameFilterer->push_back(std::make_unique<NameCoverageFilter>(Name));
+ if (NameAllowlist) {
+ if (!NameFilterFiles.empty())
+ NameFilterer->push_back(
+ std::make_unique<NameAllowlistCoverageFilter>(*NameAllowlist));
+ if (!NameFilterFilesDeprecated.empty())
+ NameFilterer->push_back(
+ std::make_unique<NameWhitelistCoverageFilter>(*NameAllowlist));
+ }
+ for (const auto &Regex : NameRegexFilters)
+ NameFilterer->push_back(
+ std::make_unique<NameRegexCoverageFilter>(Regex));
+ Filters.push_back(std::move(NameFilterer));
+ }
+
+ if (RegionCoverageLtFilter.getNumOccurrences() ||
+ RegionCoverageGtFilter.getNumOccurrences() ||
+ LineCoverageLtFilter.getNumOccurrences() ||
+ LineCoverageGtFilter.getNumOccurrences()) {
+ auto StatFilterer = std::make_unique<CoverageFilters>();
+ if (RegionCoverageLtFilter.getNumOccurrences())
+ StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
+ RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
+ if (RegionCoverageGtFilter.getNumOccurrences())
+ StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
+ RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
+ if (LineCoverageLtFilter.getNumOccurrences())
+ StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
+ LineCoverageFilter::LessThan, LineCoverageLtFilter));
+ if (LineCoverageGtFilter.getNumOccurrences())
+ StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
+ RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
+ Filters.push_back(std::move(StatFilterer));
+ }
+
+ // Create the ignore filename filters.
+ for (const auto &RE : IgnoreFilenameRegexFilters)
+ IgnoreFilenameFilters.push_back(
+ std::make_unique<NameRegexCoverageFilter>(RE));
+
+ if (!Arches.empty()) {
+ for (const std::string &Arch : Arches) {
+ if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
+ error("Unknown architecture: " + Arch);
+ return 1;
+ }
+ CoverageArches.emplace_back(Arch);
+ }
+ if (CoverageArches.size() != ObjectFilenames.size()) {
+ error("Number of architectures doesn't match the number of objects");
+ return 1;
+ }
+ }
+
+ // IgnoreFilenameFilters are applied even when InputSourceFiles specified.
+ for (const std::string &File : InputSourceFiles)
+ collectPaths(File);
+
+ if (DebugDumpCollectedPaths) {
+ for (const std::string &SF : SourceFiles)
+ outs() << SF << '\n';
+ ::exit(0);
+ }
+
+ ViewOpts.ShowBranchSummary = BranchSummary;
+ ViewOpts.ShowRegionSummary = RegionSummary;
+ ViewOpts.ShowInstantiationSummary = InstantiationSummary;
+ ViewOpts.ExportSummaryOnly = SummaryOnly;
+ ViewOpts.NumThreads = NumThreads;
+ ViewOpts.CompilationDirectory = CompilationDirectory;
+
+ return 0;
+ };
+
+ switch (Cmd) {
+ case Show:
+ return doShow(argc, argv, commandLineParser);
+ case Report:
+ return doReport(argc, argv, commandLineParser);
+ case Export:
+ return doExport(argc, argv, commandLineParser);
+ }
+ return 0;
+}
+
+int CodeCoverageTool::doShow(int argc, const char **argv,
+ CommandLineParserType commandLineParser) {
+
+ cl::OptionCategory ViewCategory("Viewing options");
+
+ cl::opt<bool> ShowLineExecutionCounts(
+ "show-line-counts", cl::Optional,
+ cl::desc("Show the execution counts for each line"), cl::init(true),
+ cl::cat(ViewCategory));
+
+ cl::opt<bool> ShowRegions(
+ "show-regions", cl::Optional,
+ cl::desc("Show the execution counts for each region"),
+ cl::cat(ViewCategory));
+
+ cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches(
+ "show-branches", cl::Optional,
+ cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory),
+ cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count,
+ "count", "Show True/False counts"),
+ clEnumValN(CoverageViewOptions::BranchOutputType::Percent,
+ "percent", "Show True/False percent")),
+ cl::init(CoverageViewOptions::BranchOutputType::Off));
+
+ cl::opt<bool> ShowBestLineRegionsCounts(
+ "show-line-counts-or-regions", cl::Optional,
+ cl::desc("Show the execution counts for each line, or the execution "
+ "counts for each region on lines that have multiple regions"),
+ cl::cat(ViewCategory));
+
+ cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
+ cl::desc("Show expanded source regions"),
+ cl::cat(ViewCategory));
+
+ cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
+ cl::desc("Show function instantiations"),
+ cl::init(true), cl::cat(ViewCategory));
+
+ cl::opt<std::string> ShowOutputDirectory(
+ "output-dir", cl::init(""),
+ cl::desc("Directory in which coverage information is written out"));
+ cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"),
+ cl::aliasopt(ShowOutputDirectory));
+
+ cl::opt<uint32_t> TabSize(
+ "tab-size", cl::init(2),
+ cl::desc(
+ "Set tab expansion size for html coverage reports (default = 2)"));
+
+ cl::opt<std::string> ProjectTitle(
+ "project-title", cl::Optional,
+ cl::desc("Set project title for the coverage report"));
+
+ auto Err = commandLineParser(argc, 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;
+ ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
+ ViewOpts.ShowExpandedRegions = ShowExpansions;
+ ViewOpts.ShowBranchCounts =
+ ShowBranches == CoverageViewOptions::BranchOutputType::Count;
+ ViewOpts.ShowBranchPercents =
+ ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
+ ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
+ ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
+ ViewOpts.TabSize = TabSize;
+ ViewOpts.ProjectTitle = ProjectTitle;
+
+ if (ViewOpts.hasOutputDirectory()) {
+ if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) {
+ error("Could not create output directory!", E.message());
+ return 1;
+ }
+ }
+
+ sys::fs::file_status Status;
+ if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
+ error("Could not read profile data!", EC.message());
+ return 1;
+ }
+
+ auto ModifiedTime = Status.getLastModificationTime();
+ std::string ModifiedTimeStr = to_string(ModifiedTime);
+ size_t found = ModifiedTimeStr.rfind(':');
+ ViewOpts.CreatedTimeStr = (found != std::string::npos)
+ ? "Created: " + ModifiedTimeStr.substr(0, found)
+ : "Created: " + ModifiedTimeStr;
+
+ auto Coverage = load();
+ if (!Coverage)
+ return 1;
+
+ auto Printer = CoveragePrinter::create(ViewOpts);
+
+ if (SourceFiles.empty() && !HadSourceFiles)
+ // Get the source files from the function coverage mapping.
+ for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
+ if (!IgnoreFilenameFilters.matchesFilename(Filename))
+ SourceFiles.push_back(std::string(Filename));
+ }
+
+ // Create an index out of the source files.
+ if (ViewOpts.hasOutputDirectory()) {
+ if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) {
+ error("Could not create index file!", toString(std::move(E)));
+ return 1;
+ }
+ }
+
+ if (!Filters.empty()) {
+ // Build the map of filenames to functions.
+ std::map<llvm::StringRef, std::vector<const FunctionRecord *>>
+ FilenameFunctionMap;
+ for (const auto &SourceFile : SourceFiles)
+ for (const auto &Function : Coverage->getCoveredFunctions(SourceFile))
+ if (Filters.matches(*Coverage.get(), Function))
+ FilenameFunctionMap[SourceFile].push_back(&Function);
+
+ // Only print filter matching functions for each file.
+ for (const auto &FileFunc : FilenameFunctionMap) {
+ StringRef File = FileFunc.first;
+ const auto &Functions = FileFunc.second;
+
+ auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false);
+ if (Error E = OSOrErr.takeError()) {
+ error("Could not create view file!", toString(std::move(E)));
+ return 1;
+ }
+ auto OS = std::move(OSOrErr.get());
+
+ bool ShowTitle = ViewOpts.hasOutputDirectory();
+ for (const auto *Function : Functions) {
+ auto FunctionView = createFunctionView(*Function, *Coverage);
+ if (!FunctionView) {
+ warning("Could not read coverage for '" + Function->Name + "'.");
+ continue;
+ }
+ FunctionView->print(*OS.get(), /*WholeFile=*/false,
+ /*ShowSourceName=*/true, ShowTitle);
+ ShowTitle = false;
+ }
+
+ Printer->closeViewFile(std::move(OS));
+ }
+ return 0;
+ }
+
+ // Show files
+ bool ShowFilenames =
+ (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() ||
+ (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML);
+
+ ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads);
+ if (ViewOpts.NumThreads == 0) {
+ // If NumThreads is not specified, create one thread for each input, up to
+ // the number of hardware cores.
+ S = heavyweight_hardware_concurrency(SourceFiles.size());
+ S.Limit = true;
+ }
+
+ if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) {
+ for (const std::string &SourceFile : SourceFiles)
+ writeSourceFileView(SourceFile, Coverage.get(), Printer.get(),
+ ShowFilenames);
+ } else {
+ // In -output-dir mode, it's safe to use multiple threads to print files.
+ ThreadPool Pool(S);
+ for (const std::string &SourceFile : SourceFiles)
+ Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile,
+ Coverage.get(), Printer.get(), ShowFilenames);
+ Pool.wait();
+ }
+
+ return 0;
+}
+
+int CodeCoverageTool::doReport(int argc, const char **argv,
+ CommandLineParserType commandLineParser) {
+ cl::opt<bool> ShowFunctionSummaries(
+ "show-functions", cl::Optional, cl::init(false),
+ cl::desc("Show coverage summaries for each function"));
+
+ auto Err = commandLineParser(argc, argv);
+ if (Err)
+ return Err;
+
+ 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();
+ if (!Coverage)
+ return 1;
+
+ CoverageReport Report(ViewOpts, *Coverage.get());
+ if (!ShowFunctionSummaries) {
+ if (SourceFiles.empty())
+ Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters);
+ else
+ Report.renderFileReports(llvm::outs(), SourceFiles);
+ } else {
+ if (SourceFiles.empty()) {
+ error("Source files must be specified when -show-functions=true is "
+ "specified");
+ return 1;
+ }
+
+ Report.renderFunctionReports(SourceFiles, DC, llvm::outs());
+ }
+ return 0;
+}
+
+int CodeCoverageTool::doExport(int argc, const char **argv,
+ CommandLineParserType commandLineParser) {
+
+ cl::OptionCategory ExportCategory("Exporting options");
+
+ cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional,
+ cl::desc("Don't export expanded source regions"),
+ cl::cat(ExportCategory));
+
+ cl::opt<bool> SkipFunctions("skip-functions", cl::Optional,
+ cl::desc("Don't export per-function data"),
+ cl::cat(ExportCategory));
+
+ auto Err = commandLineParser(argc, argv);
+ if (Err)
+ return Err;
+
+ ViewOpts.SkipExpansions = SkipExpansions;
+ ViewOpts.SkipFunctions = SkipFunctions;
+
+ 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;
+ }
+
+ auto Coverage = load();
+ if (!Coverage) {
+ error("Could not load coverage information");
+ return 1;
+ }
+
+ std::unique_ptr<CoverageExporter> Exporter;
+
+ switch (ViewOpts.Format) {
+ case CoverageViewOptions::OutputFormat::Text:
+ Exporter = std::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 = std::make_unique<CoverageExporterLcov>(*Coverage.get(),
+ ViewOpts, outs());
+ break;
+ }
+
+ if (SourceFiles.empty())
+ Exporter->renderRoot(IgnoreFilenameFilters);
+ else
+ Exporter->renderRoot(SourceFiles);
+
+ return 0;
+}
+
+int showMain(int argc, const char *argv[]) {
+ CodeCoverageTool Tool;
+ return Tool.run(CodeCoverageTool::Show, argc, argv);
+}
+
+int reportMain(int argc, const char *argv[]) {
+ CodeCoverageTool Tool;
+ return Tool.run(CodeCoverageTool::Report, argc, argv);
+}
+
+int exportMain(int argc, const char *argv[]) {
+ CodeCoverageTool Tool;
+ return Tool.run(CodeCoverageTool::Export, argc, argv);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CoverageExporter.h b/contrib/libs/llvm14/tools/llvm-cov/CoverageExporter.h
new file mode 100644
index 00000000000..751e55dc091
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageExporter.h
@@ -0,0 +1,51 @@
+//===- CoverageExporter.h - Code coverage exporter ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This class defines a code coverage exporter interface.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGEEXPORTER_H
+#define LLVM_COV_COVERAGEEXPORTER_H
+
+#include "CoverageFilters.h"
+#include "CoverageSummaryInfo.h"
+#include "CoverageViewOptions.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
+
+namespace llvm {
+
+/// Exports the code coverage information.
+class CoverageExporter {
+protected:
+ /// The full CoverageMapping object to export.
+ const coverage::CoverageMapping &Coverage;
+
+ /// The options passed to the tool.
+ const CoverageViewOptions &Options;
+
+ /// Output stream to print to.
+ raw_ostream &OS;
+
+ CoverageExporter(const coverage::CoverageMapping &CoverageMapping,
+ const CoverageViewOptions &Options, raw_ostream &OS)
+ : Coverage(CoverageMapping), Options(Options), OS(OS) {}
+
+public:
+ virtual ~CoverageExporter(){};
+
+ /// Render the CoverageMapping object.
+ virtual void renderRoot(const CoverageFilters &IgnoreFilters) = 0;
+
+ /// Render the CoverageMapping object for specified source files.
+ virtual void renderRoot(ArrayRef<std::string> SourceFiles) = 0;
+};
+
+} // end namespace llvm
+
+#endif // LLVM_COV_COVERAGEEXPORTER_H
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterJson.cpp b/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterJson.cpp
new file mode 100644
index 00000000000..d341abe8dfc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterJson.cpp
@@ -0,0 +1,309 @@
+//===- CoverageExporterJson.cpp - Code coverage export --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements export of code coverage data to JSON.
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+//
+// 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
+// -- Branches: array => List of Branches in the file
+// -- Branch: dict => Describes a branch of the file with counters
+// -- 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
+// -- Branches: array => List of Branches in the expansion
+// -- Branch: dict => Describes a branch in expansion and counters
+// -- 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
+// -- BranchCoverage: dict => Object summarizing branch 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
+// -- BranchCoverage: dict => Object summarizing branch coverage
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoverageExporterJson.h"
+#include "CoverageReport.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
+#include <algorithm>
+#include <limits>
+#include <mutex>
+#include <utility>
+
+/// The semantic version combined as a string.
+#define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.1"
+
+/// Unique type identifier for JSON coverage export.
+#define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export"
+
+using namespace llvm;
+
+namespace {
+
+// The JSON library accepts int64_t, but profiling counts are stored as uint64_t.
+// Therefore we need to explicitly convert from unsigned to signed, since a naive
+// cast is implementation-defined behavior when the unsigned value cannot be
+// represented as a signed value. We choose to clamp the values to preserve the
+// invariant that counts are always >= 0.
+int64_t clamp_uint64_to_int64(uint64_t u) {
+ return std::min(u, static_cast<uint64_t>(std::numeric_limits<int64_t>::max()));
+}
+
+json::Array renderSegment(const coverage::CoverageSegment &Segment) {
+ return json::Array({Segment.Line, Segment.Col,
+ clamp_uint64_to_int64(Segment.Count), Segment.HasCount,
+ Segment.IsRegionEntry, Segment.IsGapRegion});
+}
+
+json::Array renderRegion(const coverage::CountedRegion &Region) {
+ return json::Array({Region.LineStart, Region.ColumnStart, Region.LineEnd,
+ Region.ColumnEnd, clamp_uint64_to_int64(Region.ExecutionCount),
+ Region.FileID, Region.ExpandedFileID,
+ int64_t(Region.Kind)});
+}
+
+json::Array renderBranch(const coverage::CountedRegion &Region) {
+ return json::Array(
+ {Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
+ clamp_uint64_to_int64(Region.ExecutionCount),
+ clamp_uint64_to_int64(Region.FalseExecutionCount), Region.FileID,
+ Region.ExpandedFileID, int64_t(Region.Kind)});
+}
+
+json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
+ json::Array RegionArray;
+ for (const auto &Region : Regions)
+ RegionArray.push_back(renderRegion(Region));
+ return RegionArray;
+}
+
+json::Array renderBranchRegions(ArrayRef<coverage::CountedRegion> Regions) {
+ json::Array RegionArray;
+ for (const auto &Region : Regions)
+ if (!Region.Folded)
+ RegionArray.push_back(renderBranch(Region));
+ return RegionArray;
+}
+
+std::vector<llvm::coverage::CountedRegion>
+collectNestedBranches(const coverage::CoverageMapping &Coverage,
+ ArrayRef<llvm::coverage::ExpansionRecord> Expansions) {
+ std::vector<llvm::coverage::CountedRegion> Branches;
+ for (const auto &Expansion : Expansions) {
+ auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
+
+ // Recursively collect branches from nested expansions.
+ auto NestedExpansions = ExpansionCoverage.getExpansions();
+ auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions);
+ append_range(Branches, NestedExBranches);
+
+ // Add branches from this level of expansion.
+ auto ExBranches = ExpansionCoverage.getBranches();
+ for (auto B : ExBranches)
+ if (B.FileID == Expansion.FileID)
+ Branches.push_back(B);
+ }
+
+ return Branches;
+}
+
+json::Object renderExpansion(const coverage::CoverageMapping &Coverage,
+ const coverage::ExpansionRecord &Expansion) {
+ std::vector<llvm::coverage::ExpansionRecord> Expansions = {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)},
+ // Enumerate the branch coverage information for the expansion.
+ {"branches",
+ renderBranchRegions(collectNestedBranches(Coverage, Expansions))}});
+}
+
+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()}})},
+ {"branches",
+ json::Object(
+ {{"count", int64_t(Summary.BranchCoverage.getNumBranches())},
+ {"covered", int64_t(Summary.BranchCoverage.getCovered())},
+ {"notcovered", int64_t(Summary.BranchCoverage.getNumBranches() -
+ Summary.BranchCoverage.getCovered())},
+ {"percent", Summary.BranchCoverage.getPercentCovered()}})}});
+}
+
+json::Array renderFileExpansions(const coverage::CoverageMapping &Coverage,
+ const coverage::CoverageData &FileCoverage,
+ const FileCoverageSummary &FileReport) {
+ json::Array ExpansionArray;
+ for (const auto &Expansion : FileCoverage.getExpansions())
+ ExpansionArray.push_back(renderExpansion(Coverage, Expansion));
+ return ExpansionArray;
+}
+
+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;
+}
+
+json::Array renderFileBranches(const coverage::CoverageData &FileCoverage,
+ const FileCoverageSummary &FileReport) {
+ json::Array BranchArray;
+ for (const auto &Branch : FileCoverage.getBranches())
+ BranchArray.push_back(renderBranch(Branch));
+ return BranchArray;
+}
+
+json::Object renderFile(const coverage::CoverageMapping &Coverage,
+ const std::string &Filename,
+ const FileCoverageSummary &FileReport,
+ const CoverageViewOptions &Options) {
+ json::Object File({{"filename", Filename}});
+ if (!Options.ExportSummaryOnly) {
+ // Calculate and render detailed coverage information for given file.
+ auto FileCoverage = Coverage.getCoverageForFile(Filename);
+ File["segments"] = renderFileSegments(FileCoverage, FileReport);
+ File["branches"] = renderFileBranches(FileCoverage, FileReport);
+ if (!Options.SkipExpansions) {
+ File["expansions"] =
+ renderFileExpansions(Coverage, FileCoverage, FileReport);
+ }
+ }
+ File["summary"] = renderSummary(FileReport);
+ return File;
+}
+
+json::Array renderFiles(const coverage::CoverageMapping &Coverage,
+ ArrayRef<std::string> SourceFiles,
+ ArrayRef<FileCoverageSummary> FileReports,
+ const CoverageViewOptions &Options) {
+ ThreadPoolStrategy S = hardware_concurrency(Options.NumThreads);
+ if (Options.NumThreads == 0) {
+ // If NumThreads is not specified, create one thread for each input, up to
+ // the number of hardware cores.
+ S = heavyweight_hardware_concurrency(SourceFiles.size());
+ S.Limit = true;
+ }
+ ThreadPool Pool(S);
+ json::Array FileArray;
+ std::mutex FileArrayMutex;
+
+ for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) {
+ auto &SourceFile = SourceFiles[I];
+ auto &FileReport = FileReports[I];
+ Pool.async([&] {
+ auto File = renderFile(Coverage, SourceFile, FileReport, Options);
+ {
+ std::lock_guard<std::mutex> Lock(FileArrayMutex);
+ FileArray.push_back(std::move(File));
+ }
+ });
+ }
+ Pool.wait();
+ return FileArray;
+}
+
+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", clamp_uint64_to_int64(F.ExecutionCount)},
+ {"regions", renderRegions(F.CountedRegions)},
+ {"branches", renderBranchRegions(F.CountedBranchRegions)},
+ {"filenames", json::Array(F.Filenames)}}));
+ return FunctionArray;
+}
+
+} // end anonymous namespace
+
+void CoverageExporterJson::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 CoverageExporterJson::renderRoot(ArrayRef<std::string> SourceFiles) {
+ FileCoverageSummary Totals = FileCoverageSummary("Totals");
+ auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
+ SourceFiles, Options);
+ auto Files = renderFiles(Coverage, SourceFiles, FileReports, Options);
+ // Sort files in order of their names.
+ llvm::sort(Files, [](const json::Value &A, const json::Value &B) {
+ const json::Object *ObjA = A.getAsObject();
+ const json::Object *ObjB = B.getAsObject();
+ assert(ObjA != nullptr && "Value A was not an Object");
+ assert(ObjB != nullptr && "Value B was not an Object");
+ const StringRef FilenameA = ObjA->getString("filename").getValue();
+ const StringRef FilenameB = ObjB->getString("filename").getValue();
+ return FilenameA.compare(FilenameB) < 0;
+ });
+ auto Export = json::Object(
+ {{"files", std::move(Files)}, {"totals", renderSummary(Totals)}});
+ // Skip functions-level information if necessary.
+ if (!Options.ExportSummaryOnly && !Options.SkipFunctions)
+ Export["functions"] = renderFunctions(Coverage.getCoveredFunctions());
+
+ auto ExportArray = json::Array({std::move(Export)});
+
+ OS << json::Object({{"version", LLVM_COVERAGE_EXPORT_JSON_STR},
+ {"type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR},
+ {"data", std::move(ExportArray)}});
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterJson.h b/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterJson.h
new file mode 100644
index 00000000000..c1947500555
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterJson.h
@@ -0,0 +1,35 @@
+//===- CoverageExporterJson.h - Code coverage JSON exporter ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements a code coverage exporter for JSON format.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGEEXPORTERJSON_H
+#define LLVM_COV_COVERAGEEXPORTERJSON_H
+
+#include "CoverageExporter.h"
+
+namespace llvm {
+
+class CoverageExporterJson : public CoverageExporter {
+public:
+ CoverageExporterJson(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_COVERAGEEXPORTERJSON_H
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterLcov.cpp b/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterLcov.cpp
new file mode 100644
index 00000000000..0096a3d44d8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterLcov.cpp
@@ -0,0 +1,222 @@
+//===- CoverageExporterLcov.cpp - Code coverage export --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// 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>]
+// - for each branch:
+// - "BRDA:<line number>,<branch pair id>,<branch id>,<count>"
+// - "BRF:<number of branches found>"
+// - "BRH:<number of branches hit>"
+// - "LH:<number of lines with non-zero execution count>"
+// - "LF:<number 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';
+ }
+ }
+}
+
+std::vector<llvm::coverage::CountedRegion>
+collectNestedBranches(const coverage::CoverageMapping &Coverage,
+ ArrayRef<llvm::coverage::ExpansionRecord> Expansions,
+ int ViewDepth = 0, int SrcLine = 0) {
+ std::vector<llvm::coverage::CountedRegion> Branches;
+ for (const auto &Expansion : Expansions) {
+ auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
+
+ // If we're at the top level, set the corresponding source line.
+ if (ViewDepth == 0)
+ SrcLine = Expansion.Region.LineStart;
+
+ // Recursively collect branches from nested expansions.
+ auto NestedExpansions = ExpansionCoverage.getExpansions();
+ auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions,
+ ViewDepth + 1, SrcLine);
+ append_range(Branches, NestedExBranches);
+
+ // Add branches from this level of expansion.
+ auto ExBranches = ExpansionCoverage.getBranches();
+ for (auto B : ExBranches)
+ if (B.FileID == Expansion.FileID) {
+ B.LineStart = SrcLine;
+ Branches.push_back(B);
+ }
+ }
+
+ return Branches;
+}
+
+bool sortLine(llvm::coverage::CountedRegion I,
+ llvm::coverage::CountedRegion J) {
+ return (I.LineStart < J.LineStart) ||
+ ((I.LineStart == J.LineStart) && (I.ColumnStart < J.ColumnStart));
+}
+
+void renderBranchExecutionCounts(raw_ostream &OS,
+ const coverage::CoverageMapping &Coverage,
+ const coverage::CoverageData &FileCoverage) {
+ std::vector<llvm::coverage::CountedRegion> Branches =
+ FileCoverage.getBranches();
+
+ // Recursively collect branches for all file expansions.
+ std::vector<llvm::coverage::CountedRegion> ExBranches =
+ collectNestedBranches(Coverage, FileCoverage.getExpansions());
+
+ // Append Expansion Branches to Source Branches.
+ append_range(Branches, ExBranches);
+
+ // Sort branches based on line number to ensure branches corresponding to the
+ // same source line are counted together.
+ llvm::sort(Branches, sortLine);
+
+ auto NextBranch = Branches.begin();
+ auto EndBranch = Branches.end();
+
+ // Branches with the same source line are enumerated individually
+ // (BranchIndex) as well as based on True/False pairs (PairIndex).
+ while (NextBranch != EndBranch) {
+ unsigned CurrentLine = NextBranch->LineStart;
+ unsigned PairIndex = 0;
+ unsigned BranchIndex = 0;
+
+ while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) {
+ if (!NextBranch->Folded) {
+ unsigned BC1 = NextBranch->ExecutionCount;
+ unsigned BC2 = NextBranch->FalseExecutionCount;
+ bool BranchNotExecuted = (BC1 == 0 && BC2 == 0);
+
+ for (int I = 0; I < 2; I++, BranchIndex++) {
+ OS << "BRDA:" << CurrentLine << ',' << PairIndex << ','
+ << BranchIndex;
+ if (BranchNotExecuted)
+ OS << ',' << '-' << '\n';
+ else
+ OS << ',' << (I == 0 ? BC1 : BC2) << '\n';
+ }
+
+ PairIndex++;
+ }
+ NextBranch++;
+ }
+ }
+}
+
+void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
+ OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n'
+ << "LH:" << Summary.LineCoverage.getCovered() << '\n';
+}
+
+void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
+ OS << "BRF:" << Summary.BranchCoverage.getNumBranches() << '\n'
+ << "BRH:" << Summary.BranchCoverage.getCovered() << '\n';
+}
+
+void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
+ const std::string &Filename,
+ const FileCoverageSummary &FileReport, bool ExportSummaryOnly,
+ bool SkipFunctions) {
+ OS << "SF:" << Filename << '\n';
+
+ if (!ExportSummaryOnly && !SkipFunctions) {
+ renderFunctions(OS, Coverage.getCoveredFunctions(Filename));
+ }
+ renderFunctionSummary(OS, FileReport);
+
+ if (!ExportSummaryOnly) {
+ // Calculate and render detailed coverage information for given file.
+ auto FileCoverage = Coverage.getCoverageForFile(Filename);
+ renderLineExecutionCounts(OS, FileCoverage);
+ renderBranchExecutionCounts(OS, Coverage, FileCoverage);
+ }
+ renderBranchSummary(OS, FileReport);
+ 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, bool SkipFunctions) {
+ for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I)
+ renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly,
+ SkipFunctions);
+}
+
+} // 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,
+ Options.SkipFunctions);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterLcov.h b/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterLcov.h
new file mode 100644
index 00000000000..e8a260bf493
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageExporterLcov.h
@@ -0,0 +1,35 @@
+//===- CoverageExporterLcov.h - Code coverage lcov exporter ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// 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/contrib/libs/llvm14/tools/llvm-cov/CoverageFilters.cpp b/contrib/libs/llvm14/tools/llvm-cov/CoverageFilters.cpp
new file mode 100644
index 00000000000..b7998647cc5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageFilters.cpp
@@ -0,0 +1,93 @@
+//===- CoverageFilters.cpp - Function coverage mapping filters ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// These classes provide filtering for function coverage mapping records.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoverageFilters.h"
+#include "CoverageSummaryInfo.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/SpecialCaseList.h"
+
+using namespace llvm;
+
+bool NameCoverageFilter::matches(
+ const coverage::CoverageMapping &,
+ const coverage::FunctionRecord &Function) const {
+ StringRef FuncName = Function.Name;
+ return FuncName.contains(Name);
+}
+
+bool NameRegexCoverageFilter::matches(
+ const coverage::CoverageMapping &,
+ const coverage::FunctionRecord &Function) const {
+ return llvm::Regex(Regex).match(Function.Name);
+}
+
+bool NameRegexCoverageFilter::matchesFilename(StringRef Filename) const {
+ return llvm::Regex(Regex).match(Filename);
+}
+
+bool NameAllowlistCoverageFilter::matches(
+ const coverage::CoverageMapping &,
+ const coverage::FunctionRecord &Function) const {
+ return Allowlist.inSection("llvmcov", "allowlist_fun", Function.Name);
+}
+
+// TODO: remove this when -name-whitelist option is removed.
+bool NameWhitelistCoverageFilter::matches(
+ const coverage::CoverageMapping &,
+ const coverage::FunctionRecord &Function) const {
+ return Whitelist.inSection("llvmcov", "whitelist_fun", Function.Name);
+}
+
+bool RegionCoverageFilter::matches(
+ const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const {
+ return PassesThreshold(FunctionCoverageSummary::get(CM, Function)
+ .RegionCoverage.getPercentCovered());
+}
+
+bool LineCoverageFilter::matches(
+ const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const {
+ return PassesThreshold(FunctionCoverageSummary::get(CM, Function)
+ .LineCoverage.getPercentCovered());
+}
+
+void CoverageFilters::push_back(std::unique_ptr<CoverageFilter> Filter) {
+ Filters.push_back(std::move(Filter));
+}
+
+bool CoverageFilters::matches(const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const {
+ for (const auto &Filter : Filters) {
+ if (Filter->matches(CM, Function))
+ return true;
+ }
+ return false;
+}
+
+bool CoverageFilters::matchesFilename(StringRef Filename) const {
+ for (const auto &Filter : Filters) {
+ if (Filter->matchesFilename(Filename))
+ return true;
+ }
+ return false;
+}
+
+bool CoverageFiltersMatchAll::matches(
+ const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const {
+ for (const auto &Filter : Filters) {
+ if (!Filter->matches(CM, Function))
+ return false;
+ }
+ return true;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CoverageFilters.h b/contrib/libs/llvm14/tools/llvm-cov/CoverageFilters.h
new file mode 100644
index 00000000000..3040fe74f7c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageFilters.h
@@ -0,0 +1,173 @@
+//===- CoverageFilters.h - Function coverage mapping filters --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// These classes provide filtering for function coverage mapping records.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGEFILTERS_H
+#define LLVM_COV_COVERAGEFILTERS_H
+
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+#include <vector>
+
+namespace llvm {
+class SpecialCaseList;
+
+namespace coverage {
+class CoverageMapping;
+struct FunctionRecord;
+} // namespace coverage
+
+/// Matches specific functions that pass the requirement of this filter.
+class CoverageFilter {
+public:
+ virtual ~CoverageFilter() {}
+
+ /// Return true if the function passes the requirements of this filter.
+ virtual bool matches(const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const {
+ return true;
+ }
+
+ /// Return true if the filename passes the requirements of this filter.
+ virtual bool matchesFilename(StringRef Filename) const {
+ return true;
+ }
+};
+
+/// Matches functions that contain a specific string in their name.
+class NameCoverageFilter : public CoverageFilter {
+ StringRef Name;
+
+public:
+ NameCoverageFilter(StringRef Name) : Name(Name) {}
+
+ bool matches(const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const override;
+};
+
+/// Matches functions whose name matches a certain regular expression.
+class NameRegexCoverageFilter : public CoverageFilter {
+ StringRef Regex;
+
+public:
+ NameRegexCoverageFilter(StringRef Regex) : Regex(Regex) {}
+
+ bool matches(const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const override;
+
+ bool matchesFilename(StringRef Filename) const override;
+};
+
+/// Matches functions whose name appears in a SpecialCaseList in the
+/// allowlist_fun section.
+class NameAllowlistCoverageFilter : public CoverageFilter {
+ const SpecialCaseList &Allowlist;
+
+public:
+ NameAllowlistCoverageFilter(const SpecialCaseList &Allowlist)
+ : Allowlist(Allowlist) {}
+
+ bool matches(const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const override;
+};
+
+// TODO: Remove this class when -name-whitelist option is removed.
+class NameWhitelistCoverageFilter : public CoverageFilter {
+ const SpecialCaseList &Whitelist;
+
+public:
+ NameWhitelistCoverageFilter(const SpecialCaseList &Whitelist)
+ : Whitelist(Whitelist) {}
+
+ bool matches(const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const override;
+};
+
+/// Matches numbers that pass a certain threshold.
+template <typename T> class StatisticThresholdFilter {
+public:
+ enum Operation { LessThan, GreaterThan };
+
+protected:
+ Operation Op;
+ T Threshold;
+
+ StatisticThresholdFilter(Operation Op, T Threshold)
+ : Op(Op), Threshold(Threshold) {}
+
+ /// Return true if the given number is less than
+ /// or greater than the certain threshold.
+ bool PassesThreshold(T Value) const {
+ switch (Op) {
+ case LessThan:
+ return Value < Threshold;
+ case GreaterThan:
+ return Value > Threshold;
+ }
+ return false;
+ }
+};
+
+/// Matches functions whose region coverage percentage
+/// is above/below a certain percentage.
+class RegionCoverageFilter : public CoverageFilter,
+ public StatisticThresholdFilter<double> {
+public:
+ RegionCoverageFilter(Operation Op, double Threshold)
+ : StatisticThresholdFilter(Op, Threshold) {}
+
+ bool matches(const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const override;
+};
+
+/// Matches functions whose line coverage percentage
+/// is above/below a certain percentage.
+class LineCoverageFilter : public CoverageFilter,
+ public StatisticThresholdFilter<double> {
+public:
+ LineCoverageFilter(Operation Op, double Threshold)
+ : StatisticThresholdFilter(Op, Threshold) {}
+
+ bool matches(const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const override;
+};
+
+/// A collection of filters.
+/// Matches functions that match any filters contained
+/// in an instance of this class.
+class CoverageFilters : public CoverageFilter {
+protected:
+ std::vector<std::unique_ptr<CoverageFilter>> Filters;
+
+public:
+ /// Append a filter to this collection.
+ void push_back(std::unique_ptr<CoverageFilter> Filter);
+
+ bool empty() const { return Filters.empty(); }
+
+ bool matches(const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const override;
+
+ bool matchesFilename(StringRef Filename) const override;
+};
+
+/// A collection of filters.
+/// Matches functions that match all of the filters contained
+/// in an instance of this class.
+class CoverageFiltersMatchAll : public CoverageFilters {
+public:
+ bool matches(const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) const override;
+};
+
+} // namespace llvm
+
+#endif // LLVM_COV_COVERAGEFILTERS_H
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CoverageReport.cpp b/contrib/libs/llvm14/tools/llvm-cov/CoverageReport.cpp
new file mode 100644
index 00000000000..c91edc2b0dc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageReport.cpp
@@ -0,0 +1,490 @@
+//===- CoverageReport.cpp - Code coverage report -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements rendering of a code coverage report.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoverageReport.h"
+#include "RenderingSupport.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
+#include <numeric>
+
+using namespace llvm;
+
+namespace {
+
+/// Helper struct which prints trimmed and aligned columns.
+struct Column {
+ enum TrimKind { NoTrim, WidthTrim, RightTrim };
+
+ enum AlignmentKind { LeftAlignment, RightAlignment };
+
+ StringRef Str;
+ unsigned Width;
+ TrimKind Trim;
+ AlignmentKind Alignment;
+
+ Column(StringRef Str, unsigned Width)
+ : Str(Str), Width(Width), Trim(WidthTrim), Alignment(LeftAlignment) {}
+
+ Column &set(TrimKind Value) {
+ Trim = Value;
+ return *this;
+ }
+
+ Column &set(AlignmentKind Value) {
+ Alignment = Value;
+ return *this;
+ }
+
+ void render(raw_ostream &OS) const {
+ if (Str.size() <= Width) {
+ if (Alignment == RightAlignment) {
+ OS.indent(Width - Str.size());
+ OS << Str;
+ return;
+ }
+ OS << Str;
+ OS.indent(Width - Str.size());
+ return;
+ }
+
+ switch (Trim) {
+ case NoTrim:
+ OS << Str;
+ break;
+ case WidthTrim:
+ OS << Str.substr(0, Width);
+ break;
+ case RightTrim:
+ OS << Str.substr(0, Width - 3) << "...";
+ break;
+ }
+ }
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const Column &Value) {
+ Value.render(OS);
+ return OS;
+}
+
+Column column(StringRef Str, unsigned Width) { return Column(Str, Width); }
+
+template <typename T>
+Column column(StringRef Str, unsigned Width, const T &Value) {
+ return Column(Str, Width).set(Value);
+}
+
+// Specify the default column widths.
+size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, 16,
+ 16, 10, 12, 18, 10, 12, 18, 10};
+size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8, 10, 8, 8};
+
+/// Adjust column widths to fit long file paths and function names.
+void adjustColumnWidths(ArrayRef<StringRef> Files,
+ ArrayRef<StringRef> Functions) {
+ for (StringRef Filename : Files)
+ FileReportColumns[0] = std::max(FileReportColumns[0], Filename.size());
+ for (StringRef Funcname : Functions)
+ FunctionReportColumns[0] =
+ std::max(FunctionReportColumns[0], Funcname.size());
+}
+
+/// Prints a horizontal divider long enough to cover the given column
+/// widths.
+void renderDivider(ArrayRef<size_t> ColumnWidths, raw_ostream &OS) {
+ size_t Length = std::accumulate(ColumnWidths.begin(), ColumnWidths.end(), 0);
+ for (size_t I = 0; I < Length; ++I)
+ OS << '-';
+}
+
+/// Return the color which correponds to the coverage percentage of a
+/// certain metric.
+template <typename T>
+raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {
+ if (Info.isFullyCovered())
+ return raw_ostream::GREEN;
+ return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW
+ : raw_ostream::RED;
+}
+
+/// Get the number of redundant path components in each path in \p Paths.
+unsigned getNumRedundantPathComponents(ArrayRef<std::string> Paths) {
+ // To start, set the number of redundant path components to the maximum
+ // possible value.
+ SmallVector<StringRef, 8> FirstPathComponents{sys::path::begin(Paths[0]),
+ sys::path::end(Paths[0])};
+ unsigned NumRedundant = FirstPathComponents.size();
+
+ for (unsigned I = 1, E = Paths.size(); NumRedundant > 0 && I < E; ++I) {
+ StringRef Path = Paths[I];
+ for (const auto &Component :
+ enumerate(make_range(sys::path::begin(Path), sys::path::end(Path)))) {
+ // Do not increase the number of redundant components: that would remove
+ // useful parts of already-visited paths.
+ if (Component.index() >= NumRedundant)
+ break;
+
+ // Lower the number of redundant components when there's a mismatch
+ // between the first path, and the path under consideration.
+ if (FirstPathComponents[Component.index()] != Component.value()) {
+ NumRedundant = Component.index();
+ break;
+ }
+ }
+ }
+
+ return NumRedundant;
+}
+
+/// Determine the length of the longest redundant prefix of the paths in
+/// \p Paths.
+unsigned getRedundantPrefixLen(ArrayRef<std::string> Paths) {
+ // If there's at most one path, no path components are redundant.
+ if (Paths.size() <= 1)
+ return 0;
+
+ unsigned PrefixLen = 0;
+ unsigned NumRedundant = getNumRedundantPathComponents(Paths);
+ auto Component = sys::path::begin(Paths[0]);
+ for (unsigned I = 0; I < NumRedundant; ++I) {
+ auto LastComponent = Component;
+ ++Component;
+ PrefixLen += Component - LastComponent;
+ }
+ return PrefixLen;
+}
+
+} // end anonymous namespace
+
+namespace llvm {
+
+void CoverageReport::render(const FileCoverageSummary &File,
+ raw_ostream &OS) const {
+ auto FileCoverageColor =
+ determineCoveragePercentageColor(File.RegionCoverage);
+ auto FuncCoverageColor =
+ determineCoveragePercentageColor(File.FunctionCoverage);
+ auto InstantiationCoverageColor =
+ determineCoveragePercentageColor(File.InstantiationCoverage);
+ auto LineCoverageColor = determineCoveragePercentageColor(File.LineCoverage);
+ SmallString<256> FileName = File.Name;
+ sys::path::remove_dots(FileName, /*remove_dot_dot=*/true);
+ sys::path::native(FileName);
+ OS << column(FileName, FileReportColumns[0], Column::NoTrim);
+
+ if (Options.ShowRegionSummary) {
+ OS << format("%*u", FileReportColumns[1],
+ (unsigned)File.RegionCoverage.getNumRegions());
+ Options.colored_ostream(OS, FileCoverageColor)
+ << format("%*u", FileReportColumns[2],
+ (unsigned)(File.RegionCoverage.getNumRegions() -
+ File.RegionCoverage.getCovered()));
+ if (File.RegionCoverage.getNumRegions())
+ Options.colored_ostream(OS, FileCoverageColor)
+ << format("%*.2f", FileReportColumns[3] - 1,
+ File.RegionCoverage.getPercentCovered())
+ << '%';
+ else
+ OS << column("-", FileReportColumns[3], Column::RightAlignment);
+ }
+
+ OS << format("%*u", FileReportColumns[4],
+ (unsigned)File.FunctionCoverage.getNumFunctions());
+ OS << format("%*u", FileReportColumns[5],
+ (unsigned)(File.FunctionCoverage.getNumFunctions() -
+ File.FunctionCoverage.getExecuted()));
+ if (File.FunctionCoverage.getNumFunctions())
+ Options.colored_ostream(OS, FuncCoverageColor)
+ << format("%*.2f", FileReportColumns[6] - 1,
+ File.FunctionCoverage.getPercentCovered())
+ << '%';
+ else
+ OS << column("-", FileReportColumns[6], Column::RightAlignment);
+
+ if (Options.ShowInstantiationSummary) {
+ OS << format("%*u", FileReportColumns[7],
+ (unsigned)File.InstantiationCoverage.getNumFunctions());
+ OS << format("%*u", FileReportColumns[8],
+ (unsigned)(File.InstantiationCoverage.getNumFunctions() -
+ File.InstantiationCoverage.getExecuted()));
+ if (File.InstantiationCoverage.getNumFunctions())
+ Options.colored_ostream(OS, InstantiationCoverageColor)
+ << format("%*.2f", FileReportColumns[9] - 1,
+ File.InstantiationCoverage.getPercentCovered())
+ << '%';
+ else
+ OS << column("-", FileReportColumns[9], Column::RightAlignment);
+ }
+
+ OS << format("%*u", FileReportColumns[10],
+ (unsigned)File.LineCoverage.getNumLines());
+ Options.colored_ostream(OS, LineCoverageColor) << format(
+ "%*u", FileReportColumns[11], (unsigned)(File.LineCoverage.getNumLines() -
+ File.LineCoverage.getCovered()));
+ if (File.LineCoverage.getNumLines())
+ Options.colored_ostream(OS, LineCoverageColor)
+ << format("%*.2f", FileReportColumns[12] - 1,
+ File.LineCoverage.getPercentCovered())
+ << '%';
+ else
+ OS << column("-", FileReportColumns[12], Column::RightAlignment);
+
+ if (Options.ShowBranchSummary) {
+ OS << format("%*u", FileReportColumns[13],
+ (unsigned)File.BranchCoverage.getNumBranches());
+ Options.colored_ostream(OS, LineCoverageColor)
+ << format("%*u", FileReportColumns[14],
+ (unsigned)(File.BranchCoverage.getNumBranches() -
+ File.BranchCoverage.getCovered()));
+ if (File.BranchCoverage.getNumBranches())
+ Options.colored_ostream(OS, LineCoverageColor)
+ << format("%*.2f", FileReportColumns[15] - 1,
+ File.BranchCoverage.getPercentCovered())
+ << '%';
+ else
+ OS << column("-", FileReportColumns[15], Column::RightAlignment);
+ }
+
+ OS << "\n";
+}
+
+void CoverageReport::render(const FunctionCoverageSummary &Function,
+ const DemangleCache &DC,
+ raw_ostream &OS) const {
+ auto FuncCoverageColor =
+ determineCoveragePercentageColor(Function.RegionCoverage);
+ auto LineCoverageColor =
+ determineCoveragePercentageColor(Function.LineCoverage);
+ OS << column(DC.demangle(Function.Name), FunctionReportColumns[0],
+ Column::RightTrim)
+ << format("%*u", FunctionReportColumns[1],
+ (unsigned)Function.RegionCoverage.getNumRegions());
+ Options.colored_ostream(OS, FuncCoverageColor)
+ << format("%*u", FunctionReportColumns[2],
+ (unsigned)(Function.RegionCoverage.getNumRegions() -
+ Function.RegionCoverage.getCovered()));
+ Options.colored_ostream(
+ OS, determineCoveragePercentageColor(Function.RegionCoverage))
+ << format("%*.2f", FunctionReportColumns[3] - 1,
+ Function.RegionCoverage.getPercentCovered())
+ << '%';
+ OS << format("%*u", FunctionReportColumns[4],
+ (unsigned)Function.LineCoverage.getNumLines());
+ Options.colored_ostream(OS, LineCoverageColor)
+ << format("%*u", FunctionReportColumns[5],
+ (unsigned)(Function.LineCoverage.getNumLines() -
+ Function.LineCoverage.getCovered()));
+ Options.colored_ostream(
+ OS, determineCoveragePercentageColor(Function.LineCoverage))
+ << format("%*.2f", FunctionReportColumns[6] - 1,
+ Function.LineCoverage.getPercentCovered())
+ << '%';
+ if (Options.ShowBranchSummary) {
+ OS << format("%*u", FunctionReportColumns[7],
+ (unsigned)Function.BranchCoverage.getNumBranches());
+ Options.colored_ostream(OS, LineCoverageColor)
+ << format("%*u", FunctionReportColumns[8],
+ (unsigned)(Function.BranchCoverage.getNumBranches() -
+ Function.BranchCoverage.getCovered()));
+ Options.colored_ostream(
+ OS, determineCoveragePercentageColor(Function.BranchCoverage))
+ << format("%*.2f", FunctionReportColumns[9] - 1,
+ Function.BranchCoverage.getPercentCovered())
+ << '%';
+ }
+ OS << "\n";
+}
+
+void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
+ const DemangleCache &DC,
+ raw_ostream &OS) {
+ bool isFirst = true;
+ for (StringRef Filename : Files) {
+ auto Functions = Coverage.getCoveredFunctions(Filename);
+
+ if (isFirst)
+ isFirst = false;
+ else
+ OS << "\n";
+
+ std::vector<StringRef> Funcnames;
+ for (const auto &F : Functions)
+ Funcnames.emplace_back(DC.demangle(F.Name));
+ adjustColumnWidths({}, Funcnames);
+
+ OS << "File '" << Filename << "':\n";
+ OS << column("Name", FunctionReportColumns[0])
+ << column("Regions", FunctionReportColumns[1], Column::RightAlignment)
+ << column("Miss", FunctionReportColumns[2], Column::RightAlignment)
+ << column("Cover", FunctionReportColumns[3], Column::RightAlignment)
+ << column("Lines", FunctionReportColumns[4], Column::RightAlignment)
+ << column("Miss", FunctionReportColumns[5], Column::RightAlignment)
+ << column("Cover", FunctionReportColumns[6], Column::RightAlignment);
+ if (Options.ShowBranchSummary)
+ OS << column("Branches", FunctionReportColumns[7], Column::RightAlignment)
+ << column("Miss", FunctionReportColumns[8], Column::RightAlignment)
+ << column("Cover", FunctionReportColumns[9], Column::RightAlignment);
+ OS << "\n";
+ renderDivider(FunctionReportColumns, OS);
+ OS << "\n";
+ FunctionCoverageSummary Totals("TOTAL");
+ for (const auto &F : Functions) {
+ auto Function = FunctionCoverageSummary::get(Coverage, F);
+ ++Totals.ExecutionCount;
+ Totals.RegionCoverage += Function.RegionCoverage;
+ Totals.LineCoverage += Function.LineCoverage;
+ Totals.BranchCoverage += Function.BranchCoverage;
+ render(Function, DC, OS);
+ }
+ if (Totals.ExecutionCount) {
+ renderDivider(FunctionReportColumns, OS);
+ OS << "\n";
+ render(Totals, DC, OS);
+ }
+ }
+}
+
+void CoverageReport::prepareSingleFileReport(const StringRef Filename,
+ const coverage::CoverageMapping *Coverage,
+ const CoverageViewOptions &Options, const unsigned LCP,
+ FileCoverageSummary *FileReport, const CoverageFilter *Filters) {
+ for (const auto &Group : Coverage->getInstantiationGroups(Filename)) {
+ std::vector<FunctionCoverageSummary> InstantiationSummaries;
+ for (const coverage::FunctionRecord *F : Group.getInstantiations()) {
+ if (!Filters->matches(*Coverage, *F))
+ continue;
+ auto InstantiationSummary = FunctionCoverageSummary::get(*Coverage, *F);
+ FileReport->addInstantiation(InstantiationSummary);
+ InstantiationSummaries.push_back(InstantiationSummary);
+ }
+ if (InstantiationSummaries.empty())
+ continue;
+
+ auto GroupSummary =
+ FunctionCoverageSummary::get(Group, InstantiationSummaries);
+
+ if (Options.Debug)
+ outs() << "InstantiationGroup: " << GroupSummary.Name << " with "
+ << "size = " << Group.size() << "\n";
+
+ FileReport->addFunction(GroupSummary);
+ }
+}
+
+std::vector<FileCoverageSummary> CoverageReport::prepareFileReports(
+ const coverage::CoverageMapping &Coverage, FileCoverageSummary &Totals,
+ ArrayRef<std::string> Files, const CoverageViewOptions &Options,
+ const CoverageFilter &Filters) {
+ unsigned LCP = getRedundantPrefixLen(Files);
+
+ ThreadPoolStrategy S = hardware_concurrency(Options.NumThreads);
+ if (Options.NumThreads == 0) {
+ // If NumThreads is not specified, create one thread for each input, up to
+ // the number of hardware cores.
+ S = heavyweight_hardware_concurrency(Files.size());
+ S.Limit = true;
+ }
+ ThreadPool Pool(S);
+
+ std::vector<FileCoverageSummary> FileReports;
+ FileReports.reserve(Files.size());
+
+ for (StringRef Filename : Files) {
+ FileReports.emplace_back(Filename.drop_front(LCP));
+ Pool.async(&CoverageReport::prepareSingleFileReport, Filename,
+ &Coverage, Options, LCP, &FileReports.back(), &Filters);
+ }
+ Pool.wait();
+
+ for (const auto &FileReport : FileReports)
+ Totals += FileReport;
+
+ return FileReports;
+}
+
+void CoverageReport::renderFileReports(
+ raw_ostream &OS, const CoverageFilters &IgnoreFilenameFilters) const {
+ std::vector<std::string> UniqueSourceFiles;
+ for (StringRef SF : Coverage.getUniqueSourceFiles()) {
+ // Apply ignore source files filters.
+ if (!IgnoreFilenameFilters.matchesFilename(SF))
+ UniqueSourceFiles.emplace_back(SF.str());
+ }
+ renderFileReports(OS, UniqueSourceFiles);
+}
+
+void CoverageReport::renderFileReports(
+ raw_ostream &OS, ArrayRef<std::string> Files) const {
+ renderFileReports(OS, Files, CoverageFiltersMatchAll());
+}
+
+void CoverageReport::renderFileReports(
+ raw_ostream &OS, ArrayRef<std::string> Files,
+ const CoverageFiltersMatchAll &Filters) const {
+ FileCoverageSummary Totals("TOTAL");
+ auto FileReports =
+ prepareFileReports(Coverage, Totals, Files, Options, Filters);
+
+ std::vector<StringRef> Filenames;
+ for (const FileCoverageSummary &FCS : FileReports)
+ Filenames.emplace_back(FCS.Name);
+ adjustColumnWidths(Filenames, {});
+
+ OS << column("Filename", FileReportColumns[0]);
+ if (Options.ShowRegionSummary)
+ OS << column("Regions", FileReportColumns[1], Column::RightAlignment)
+ << column("Missed Regions", FileReportColumns[2], Column::RightAlignment)
+ << column("Cover", FileReportColumns[3], Column::RightAlignment);
+ OS << column("Functions", FileReportColumns[4], Column::RightAlignment)
+ << column("Missed Functions", FileReportColumns[5], Column::RightAlignment)
+ << column("Executed", FileReportColumns[6], Column::RightAlignment);
+ if (Options.ShowInstantiationSummary)
+ OS << column("Instantiations", FileReportColumns[7], Column::RightAlignment)
+ << column("Missed Insts.", FileReportColumns[8], Column::RightAlignment)
+ << column("Executed", FileReportColumns[9], Column::RightAlignment);
+ OS << column("Lines", FileReportColumns[10], Column::RightAlignment)
+ << column("Missed Lines", FileReportColumns[11], Column::RightAlignment)
+ << column("Cover", FileReportColumns[12], Column::RightAlignment);
+ if (Options.ShowBranchSummary)
+ OS << column("Branches", FileReportColumns[13], Column::RightAlignment)
+ << column("Missed Branches", FileReportColumns[14],
+ Column::RightAlignment)
+ << column("Cover", FileReportColumns[15], Column::RightAlignment);
+ OS << "\n";
+ renderDivider(FileReportColumns, OS);
+ OS << "\n";
+
+ bool EmptyFiles = false;
+ for (const FileCoverageSummary &FCS : FileReports) {
+ if (FCS.FunctionCoverage.getNumFunctions())
+ render(FCS, OS);
+ else
+ EmptyFiles = true;
+ }
+
+ if (EmptyFiles && Filters.empty()) {
+ OS << "\n"
+ << "Files which contain no functions:\n";
+
+ for (const FileCoverageSummary &FCS : FileReports)
+ if (!FCS.FunctionCoverage.getNumFunctions())
+ render(FCS, OS);
+ }
+
+ renderDivider(FileReportColumns, OS);
+ OS << "\n";
+ render(Totals, OS);
+}
+
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CoverageReport.h b/contrib/libs/llvm14/tools/llvm-cov/CoverageReport.h
new file mode 100644
index 00000000000..f9a092f510b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageReport.h
@@ -0,0 +1,69 @@
+//===- CoverageReport.h - Code coverage report ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements rendering of a code coverage report.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGEREPORT_H
+#define LLVM_COV_COVERAGEREPORT_H
+
+#include "CoverageFilters.h"
+#include "CoverageSummaryInfo.h"
+#include "CoverageViewOptions.h"
+
+namespace llvm {
+
+/// Displays the code coverage report.
+class CoverageReport {
+ const CoverageViewOptions &Options;
+ const coverage::CoverageMapping &Coverage;
+
+ void render(const FileCoverageSummary &File, raw_ostream &OS) const;
+ void render(const FunctionCoverageSummary &Function, const DemangleCache &DC,
+ raw_ostream &OS) const;
+
+public:
+ CoverageReport(const CoverageViewOptions &Options,
+ const coverage::CoverageMapping &Coverage)
+ : Options(Options), Coverage(Coverage) {}
+
+ void renderFunctionReports(ArrayRef<std::string> Files,
+ const DemangleCache &DC, raw_ostream &OS);
+
+ /// Prepare file reports for the files specified in \p Files.
+ static std::vector<FileCoverageSummary>
+ prepareFileReports(const coverage::CoverageMapping &Coverage,
+ FileCoverageSummary &Totals, ArrayRef<std::string> Files,
+ const CoverageViewOptions &Options,
+ const CoverageFilter &Filters = CoverageFiltersMatchAll());
+
+ static void
+ prepareSingleFileReport(const StringRef Filename,
+ const coverage::CoverageMapping *Coverage,
+ const CoverageViewOptions &Options,
+ const unsigned LCP,
+ FileCoverageSummary *FileReport,
+ const CoverageFilter *Filters);
+
+ /// Render file reports for every unique file in the coverage mapping.
+ void renderFileReports(raw_ostream &OS,
+ const CoverageFilters &IgnoreFilenameFilters) const;
+
+ /// Render file reports for the files specified in \p Files.
+ void renderFileReports(raw_ostream &OS, ArrayRef<std::string> Files) const;
+
+ /// Render file reports for the files specified in \p Files and the functions
+ /// in \p Filters.
+ void renderFileReports(raw_ostream &OS, ArrayRef<std::string> Files,
+ const CoverageFiltersMatchAll &Filters) const;
+};
+
+} // end namespace llvm
+
+#endif // LLVM_COV_COVERAGEREPORT_H
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CoverageSummaryInfo.cpp b/contrib/libs/llvm14/tools/llvm-cov/CoverageSummaryInfo.cpp
new file mode 100644
index 00000000000..10e059adeb7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageSummaryInfo.cpp
@@ -0,0 +1,106 @@
+//===- CoverageSummaryInfo.cpp - Coverage summary for function/file -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// These structures are used to represent code coverage metrics
+// for functions/files.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoverageSummaryInfo.h"
+
+using namespace llvm;
+using namespace coverage;
+
+static void sumBranches(size_t &NumBranches, size_t &CoveredBranches,
+ const ArrayRef<CountedRegion> &Branches) {
+ for (const auto &BR : Branches) {
+ // Skip folded branches.
+ if (BR.Folded)
+ continue;
+
+ // "True" Condition Branches.
+ ++NumBranches;
+ if (BR.ExecutionCount > 0)
+ ++CoveredBranches;
+ // "False" Condition Branches.
+ ++NumBranches;
+ if (BR.FalseExecutionCount > 0)
+ ++CoveredBranches;
+ }
+}
+
+static void sumBranchExpansions(size_t &NumBranches, size_t &CoveredBranches,
+ const CoverageMapping &CM,
+ ArrayRef<ExpansionRecord> Expansions) {
+ for (const auto &Expansion : Expansions) {
+ auto CE = CM.getCoverageForExpansion(Expansion);
+ sumBranches(NumBranches, CoveredBranches, CE.getBranches());
+ sumBranchExpansions(NumBranches, CoveredBranches, CM, CE.getExpansions());
+ }
+}
+
+FunctionCoverageSummary
+FunctionCoverageSummary::get(const CoverageMapping &CM,
+ const coverage::FunctionRecord &Function) {
+ // Compute the region coverage.
+ size_t NumCodeRegions = 0, CoveredRegions = 0;
+ for (auto &CR : Function.CountedRegions) {
+ if (CR.Kind != CounterMappingRegion::CodeRegion)
+ continue;
+ ++NumCodeRegions;
+ if (CR.ExecutionCount != 0)
+ ++CoveredRegions;
+ }
+
+ // Compute the line coverage
+ size_t NumLines = 0, CoveredLines = 0;
+ CoverageData CD = CM.getCoverageForFunction(Function);
+ for (const auto &LCS : getLineCoverageStats(CD)) {
+ if (!LCS.isMapped())
+ continue;
+ ++NumLines;
+ if (LCS.getExecutionCount())
+ ++CoveredLines;
+ }
+
+ // Compute the branch coverage, including branches from expansions.
+ size_t NumBranches = 0, CoveredBranches = 0;
+ sumBranches(NumBranches, CoveredBranches, CD.getBranches());
+ sumBranchExpansions(NumBranches, CoveredBranches, CM, CD.getExpansions());
+
+ return FunctionCoverageSummary(
+ Function.Name, Function.ExecutionCount,
+ RegionCoverageInfo(CoveredRegions, NumCodeRegions),
+ LineCoverageInfo(CoveredLines, NumLines),
+ BranchCoverageInfo(CoveredBranches, NumBranches));
+}
+
+FunctionCoverageSummary
+FunctionCoverageSummary::get(const InstantiationGroup &Group,
+ ArrayRef<FunctionCoverageSummary> Summaries) {
+ std::string Name;
+ if (Group.hasName()) {
+ Name = std::string(Group.getName());
+ } else {
+ llvm::raw_string_ostream OS(Name);
+ OS << "Definition at line " << Group.getLine() << ", column "
+ << Group.getColumn();
+ }
+
+ FunctionCoverageSummary Summary(Name);
+ Summary.ExecutionCount = Group.getTotalExecutionCount();
+ Summary.RegionCoverage = Summaries[0].RegionCoverage;
+ Summary.LineCoverage = Summaries[0].LineCoverage;
+ Summary.BranchCoverage = Summaries[0].BranchCoverage;
+ for (const auto &FCS : Summaries.drop_front()) {
+ Summary.RegionCoverage.merge(FCS.RegionCoverage);
+ Summary.LineCoverage.merge(FCS.LineCoverage);
+ Summary.BranchCoverage.merge(FCS.BranchCoverage);
+ }
+ return Summary;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CoverageSummaryInfo.h b/contrib/libs/llvm14/tools/llvm-cov/CoverageSummaryInfo.h
new file mode 100644
index 00000000000..84a3228f22b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageSummaryInfo.h
@@ -0,0 +1,263 @@
+//===- CoverageSummaryInfo.h - Coverage summary for function/file ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// These structures are used to represent code coverage metrics
+// for functions/files.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGESUMMARYINFO_H
+#define LLVM_COV_COVERAGESUMMARYINFO_H
+
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+
+/// Provides information about region coverage for a function/file.
+class RegionCoverageInfo {
+ /// The number of regions that were executed at least once.
+ size_t Covered;
+
+ /// The total number of regions in a function/file.
+ size_t NumRegions;
+
+public:
+ RegionCoverageInfo() : Covered(0), NumRegions(0) {}
+
+ RegionCoverageInfo(size_t Covered, size_t NumRegions)
+ : Covered(Covered), NumRegions(NumRegions) {
+ assert(Covered <= NumRegions && "Covered regions over-counted");
+ }
+
+ RegionCoverageInfo &operator+=(const RegionCoverageInfo &RHS) {
+ Covered += RHS.Covered;
+ NumRegions += RHS.NumRegions;
+ return *this;
+ }
+
+ void merge(const RegionCoverageInfo &RHS) {
+ Covered = std::max(Covered, RHS.Covered);
+ NumRegions = std::max(NumRegions, RHS.NumRegions);
+ }
+
+ size_t getCovered() const { return Covered; }
+
+ size_t getNumRegions() const { return NumRegions; }
+
+ bool isFullyCovered() const { return Covered == NumRegions; }
+
+ double getPercentCovered() const {
+ assert(Covered <= NumRegions && "Covered regions over-counted");
+ if (NumRegions == 0)
+ return 0.0;
+ return double(Covered) / double(NumRegions) * 100.0;
+ }
+};
+
+/// Provides information about line coverage for a function/file.
+class LineCoverageInfo {
+ /// The number of lines that were executed at least once.
+ size_t Covered;
+
+ /// The total number of lines in a function/file.
+ size_t NumLines;
+
+public:
+ LineCoverageInfo() : Covered(0), NumLines(0) {}
+
+ LineCoverageInfo(size_t Covered, size_t NumLines)
+ : Covered(Covered), NumLines(NumLines) {
+ assert(Covered <= NumLines && "Covered lines over-counted");
+ }
+
+ LineCoverageInfo &operator+=(const LineCoverageInfo &RHS) {
+ Covered += RHS.Covered;
+ NumLines += RHS.NumLines;
+ return *this;
+ }
+
+ void merge(const LineCoverageInfo &RHS) {
+ Covered = std::max(Covered, RHS.Covered);
+ NumLines = std::max(NumLines, RHS.NumLines);
+ }
+
+ size_t getCovered() const { return Covered; }
+
+ size_t getNumLines() const { return NumLines; }
+
+ bool isFullyCovered() const { return Covered == NumLines; }
+
+ double getPercentCovered() const {
+ assert(Covered <= NumLines && "Covered lines over-counted");
+ if (NumLines == 0)
+ return 0.0;
+ return double(Covered) / double(NumLines) * 100.0;
+ }
+};
+
+/// Provides information about branches coverage for a function/file.
+class BranchCoverageInfo {
+ /// The number of branches that were executed at least once.
+ size_t Covered;
+
+ /// The total number of branches in a function/file.
+ size_t NumBranches;
+
+public:
+ BranchCoverageInfo() : Covered(0), NumBranches(0) {}
+
+ BranchCoverageInfo(size_t Covered, size_t NumBranches)
+ : Covered(Covered), NumBranches(NumBranches) {
+ assert(Covered <= NumBranches && "Covered branches over-counted");
+ }
+
+ BranchCoverageInfo &operator+=(const BranchCoverageInfo &RHS) {
+ Covered += RHS.Covered;
+ NumBranches += RHS.NumBranches;
+ return *this;
+ }
+
+ void merge(const BranchCoverageInfo &RHS) {
+ Covered = std::max(Covered, RHS.Covered);
+ NumBranches = std::max(NumBranches, RHS.NumBranches);
+ }
+
+ size_t getCovered() const { return Covered; }
+
+ size_t getNumBranches() const { return NumBranches; }
+
+ bool isFullyCovered() const { return Covered == NumBranches; }
+
+ double getPercentCovered() const {
+ assert(Covered <= NumBranches && "Covered branches over-counted");
+ if (NumBranches == 0)
+ return 0.0;
+ return double(Covered) / double(NumBranches) * 100.0;
+ }
+};
+
+/// Provides information about function coverage for a file.
+class FunctionCoverageInfo {
+ /// The number of functions that were executed.
+ size_t Executed;
+
+ /// The total number of functions in this file.
+ size_t NumFunctions;
+
+public:
+ FunctionCoverageInfo() : Executed(0), NumFunctions(0) {}
+
+ FunctionCoverageInfo(size_t Executed, size_t NumFunctions)
+ : Executed(Executed), NumFunctions(NumFunctions) {}
+
+ FunctionCoverageInfo &operator+=(const FunctionCoverageInfo &RHS) {
+ Executed += RHS.Executed;
+ NumFunctions += RHS.NumFunctions;
+ return *this;
+ }
+
+ void addFunction(bool Covered) {
+ if (Covered)
+ ++Executed;
+ ++NumFunctions;
+ }
+
+ size_t getExecuted() const { return Executed; }
+
+ size_t getNumFunctions() const { return NumFunctions; }
+
+ bool isFullyCovered() const { return Executed == NumFunctions; }
+
+ double getPercentCovered() const {
+ assert(Executed <= NumFunctions && "Covered functions over-counted");
+ if (NumFunctions == 0)
+ return 0.0;
+ return double(Executed) / double(NumFunctions) * 100.0;
+ }
+};
+
+/// A summary of function's code coverage.
+struct FunctionCoverageSummary {
+ std::string Name;
+ uint64_t ExecutionCount;
+ RegionCoverageInfo RegionCoverage;
+ LineCoverageInfo LineCoverage;
+ BranchCoverageInfo BranchCoverage;
+
+ FunctionCoverageSummary(const std::string &Name)
+ : Name(Name), ExecutionCount(0) {}
+
+ FunctionCoverageSummary(const std::string &Name, uint64_t ExecutionCount,
+ const RegionCoverageInfo &RegionCoverage,
+ const LineCoverageInfo &LineCoverage,
+ const BranchCoverageInfo &BranchCoverage)
+ : Name(Name), ExecutionCount(ExecutionCount),
+ RegionCoverage(RegionCoverage), LineCoverage(LineCoverage),
+ BranchCoverage(BranchCoverage) {}
+
+ /// Compute the code coverage summary for the given function coverage
+ /// mapping record.
+ static FunctionCoverageSummary get(const coverage::CoverageMapping &CM,
+ const coverage::FunctionRecord &Function);
+
+ /// Compute the code coverage summary for an instantiation group \p Group,
+ /// given a list of summaries for each instantiation in \p Summaries.
+ static FunctionCoverageSummary
+ get(const coverage::InstantiationGroup &Group,
+ ArrayRef<FunctionCoverageSummary> Summaries);
+};
+
+/// A summary of file's code coverage.
+struct FileCoverageSummary {
+ StringRef Name;
+ RegionCoverageInfo RegionCoverage;
+ LineCoverageInfo LineCoverage;
+ BranchCoverageInfo BranchCoverage;
+ FunctionCoverageInfo FunctionCoverage;
+ FunctionCoverageInfo InstantiationCoverage;
+
+ FileCoverageSummary(StringRef Name) : Name(Name) {}
+
+ FileCoverageSummary &operator+=(const FileCoverageSummary &RHS) {
+ RegionCoverage += RHS.RegionCoverage;
+ LineCoverage += RHS.LineCoverage;
+ FunctionCoverage += RHS.FunctionCoverage;
+ BranchCoverage += RHS.BranchCoverage;
+ InstantiationCoverage += RHS.InstantiationCoverage;
+ return *this;
+ }
+
+ void addFunction(const FunctionCoverageSummary &Function) {
+ RegionCoverage += Function.RegionCoverage;
+ LineCoverage += Function.LineCoverage;
+ BranchCoverage += Function.BranchCoverage;
+ FunctionCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0);
+ }
+
+ void addInstantiation(const FunctionCoverageSummary &Function) {
+ InstantiationCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0);
+ }
+};
+
+/// A cache for demangled symbols.
+struct DemangleCache {
+ StringMap<std::string> DemangledNames;
+
+ /// Demangle \p Sym if possible. Otherwise, just return \p Sym.
+ StringRef demangle(StringRef Sym) const {
+ const auto DemangledName = DemangledNames.find(Sym);
+ if (DemangledName == DemangledNames.end())
+ return Sym;
+ return DemangledName->getValue();
+ }
+};
+
+} // namespace llvm
+
+#endif // LLVM_COV_COVERAGESUMMARYINFO_H
diff --git a/contrib/libs/llvm14/tools/llvm-cov/CoverageViewOptions.h b/contrib/libs/llvm14/tools/llvm-cov/CoverageViewOptions.h
new file mode 100644
index 00000000000..045fb1787bc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/CoverageViewOptions.h
@@ -0,0 +1,81 @@
+//===- CoverageViewOptions.h - Code coverage display options -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGEVIEWOPTIONS_H
+#define LLVM_COV_COVERAGEVIEWOPTIONS_H
+
+#include "llvm/Config/llvm-config.h"
+#include "RenderingSupport.h"
+#include <vector>
+
+namespace llvm {
+
+/// The options for displaying the code coverage information.
+struct CoverageViewOptions {
+ enum class OutputFormat {
+ Text,
+ HTML,
+ Lcov
+ };
+
+ enum class BranchOutputType { Count, Percent, Off };
+
+ bool Debug;
+ bool Colors;
+ bool ShowLineNumbers;
+ bool ShowLineStats;
+ bool ShowRegionMarkers;
+ bool ShowBranchCounts;
+ bool ShowBranchPercents;
+ bool ShowExpandedRegions;
+ bool ShowFunctionInstantiations;
+ bool ShowFullFilenames;
+ bool ShowBranchSummary;
+ bool ShowRegionSummary;
+ bool ShowInstantiationSummary;
+ bool ExportSummaryOnly;
+ bool SkipExpansions;
+ bool SkipFunctions;
+ OutputFormat Format;
+ BranchOutputType ShowBranches;
+ std::string ShowOutputDirectory;
+ std::vector<std::string> DemanglerOpts;
+ uint32_t TabSize;
+ std::string ProjectTitle;
+ std::string CreatedTimeStr;
+ unsigned NumThreads;
+ std::string CompilationDirectory;
+
+ /// Change the output's stream color if the colors are enabled.
+ ColoredRawOstream colored_ostream(raw_ostream &OS,
+ raw_ostream::Colors Color) const {
+ return llvm::colored_ostream(OS, Color, Colors);
+ }
+
+ /// Check if an output directory has been specified.
+ bool hasOutputDirectory() const { return !ShowOutputDirectory.empty(); }
+
+ /// Check if a demangler has been specified.
+ bool hasDemangler() const { return !DemanglerOpts.empty(); }
+
+ /// Check if a project title has been specified.
+ bool hasProjectTitle() const { return !ProjectTitle.empty(); }
+
+ /// Check if the created time of the profile data file is available.
+ bool hasCreatedTime() const { return !CreatedTimeStr.empty(); }
+
+ /// Get the LLVM version string.
+ std::string getLLVMVersionString() const {
+ std::string VersionString = "Generated by llvm-cov -- llvm version ";
+ VersionString += LLVM_VERSION_STRING;
+ return VersionString;
+ }
+};
+}
+
+#endif // LLVM_COV_COVERAGEVIEWOPTIONS_H
diff --git a/contrib/libs/llvm14/tools/llvm-cov/RenderingSupport.h b/contrib/libs/llvm14/tools/llvm-cov/RenderingSupport.h
new file mode 100644
index 00000000000..0674fbac9a3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/RenderingSupport.h
@@ -0,0 +1,60 @@
+//===- RenderingSupport.h - output stream rendering support functions ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_RENDERINGSUPPORT_H
+#define LLVM_COV_RENDERINGSUPPORT_H
+
+#include "llvm/Support/raw_ostream.h"
+#include <utility>
+
+namespace llvm {
+
+/// A helper class that resets the output stream's color if needed
+/// when destroyed.
+class ColoredRawOstream {
+ ColoredRawOstream(const ColoredRawOstream &OS) = delete;
+
+public:
+ raw_ostream &OS;
+ bool IsColorUsed;
+
+ ColoredRawOstream(raw_ostream &OS, bool IsColorUsed)
+ : OS(OS), IsColorUsed(IsColorUsed) {}
+
+ ColoredRawOstream(ColoredRawOstream &&Other)
+ : OS(Other.OS), IsColorUsed(Other.IsColorUsed) {
+ // Reset the other IsColorUsed so that the other object won't reset the
+ // color when destroyed.
+ Other.IsColorUsed = false;
+ }
+
+ ~ColoredRawOstream() {
+ if (IsColorUsed)
+ OS.resetColor();
+ }
+};
+
+template <typename T>
+inline raw_ostream &operator<<(const ColoredRawOstream &OS, T &&Value) {
+ return OS.OS << std::forward<T>(Value);
+}
+
+/// Change the color of the output stream if the `IsColorUsed` flag
+/// is true. Returns an object that resets the color when destroyed.
+inline ColoredRawOstream colored_ostream(raw_ostream &OS,
+ raw_ostream::Colors Color,
+ bool IsColorUsed = true,
+ bool Bold = false, bool BG = false) {
+ if (IsColorUsed)
+ OS.changeColor(Color, Bold, BG);
+ return ColoredRawOstream(OS, IsColorUsed);
+}
+
+} // namespace llvm
+
+#endif // LLVM_COV_RENDERINGSUPPORT_H
diff --git a/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageView.cpp b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageView.cpp
new file mode 100644
index 00000000000..ea86acadf00
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageView.cpp
@@ -0,0 +1,281 @@
+//===- SourceCoverageView.cpp - Code coverage view for source code --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This class implements rendering for code coverage of source code.
+///
+//===----------------------------------------------------------------------===//
+
+#include "SourceCoverageView.h"
+#include "SourceCoverageViewHTML.h"
+#include "SourceCoverageViewText.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+
+void CoveragePrinter::StreamDestructor::operator()(raw_ostream *OS) const {
+ if (OS == &outs())
+ return;
+ delete OS;
+}
+
+std::string CoveragePrinter::getOutputPath(StringRef Path, StringRef Extension,
+ bool InToplevel,
+ bool Relative) const {
+ assert(!Extension.empty() && "The file extension may not be empty");
+
+ SmallString<256> FullPath;
+
+ if (!Relative)
+ FullPath.append(Opts.ShowOutputDirectory);
+
+ if (!InToplevel)
+ sys::path::append(FullPath, getCoverageDir());
+
+ SmallString<256> ParentPath = sys::path::parent_path(Path);
+ sys::path::remove_dots(ParentPath, /*remove_dot_dot=*/true);
+ sys::path::append(FullPath, sys::path::relative_path(ParentPath));
+
+ auto PathFilename = (sys::path::filename(Path) + "." + Extension).str();
+ sys::path::append(FullPath, PathFilename);
+ sys::path::native(FullPath);
+
+ return std::string(FullPath.str());
+}
+
+Expected<CoveragePrinter::OwnedStream>
+CoveragePrinter::createOutputStream(StringRef Path, StringRef Extension,
+ bool InToplevel) const {
+ if (!Opts.hasOutputDirectory())
+ return OwnedStream(&outs());
+
+ std::string FullPath = getOutputPath(Path, Extension, InToplevel, false);
+
+ auto ParentDir = sys::path::parent_path(FullPath);
+ if (auto E = sys::fs::create_directories(ParentDir))
+ return errorCodeToError(E);
+
+ std::error_code E;
+ raw_ostream *RawStream =
+ new raw_fd_ostream(FullPath, E, sys::fs::FA_Read | sys::fs::FA_Write);
+ auto OS = CoveragePrinter::OwnedStream(RawStream);
+ if (E)
+ return errorCodeToError(E);
+ return std::move(OS);
+}
+
+std::unique_ptr<CoveragePrinter>
+CoveragePrinter::create(const CoverageViewOptions &Opts) {
+ switch (Opts.Format) {
+ case CoverageViewOptions::OutputFormat::Text:
+ return std::make_unique<CoveragePrinterText>(Opts);
+ case CoverageViewOptions::OutputFormat::HTML:
+ return std::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!");
+}
+
+unsigned SourceCoverageView::getFirstUncoveredLineNo() {
+ const auto MinSegIt = find_if(CoverageInfo, [](const CoverageSegment &S) {
+ return S.HasCount && S.Count == 0;
+ });
+
+ // There is no uncovered line, return zero.
+ if (MinSegIt == CoverageInfo.end())
+ return 0;
+
+ return (*MinSegIt).Line;
+}
+
+std::string SourceCoverageView::formatCount(uint64_t N) {
+ std::string Number = utostr(N);
+ int Len = Number.size();
+ if (Len <= 3)
+ return Number;
+ int IntLen = Len % 3 == 0 ? 3 : Len % 3;
+ std::string Result(Number.data(), IntLen);
+ if (IntLen != 3) {
+ Result.push_back('.');
+ Result += Number.substr(IntLen, 3 - IntLen);
+ }
+ Result.push_back(" kMGTPEZY"[(Len - 1) / 3]);
+ return Result;
+}
+
+bool SourceCoverageView::shouldRenderRegionMarkers(
+ const LineCoverageStats &LCS) const {
+ if (!getOptions().ShowRegionMarkers)
+ return false;
+
+ CoverageSegmentArray Segments = LCS.getLineSegments();
+ if (Segments.empty())
+ return false;
+ for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) {
+ const auto *CurSeg = Segments[I];
+ if (!CurSeg->IsRegionEntry || CurSeg->Count == LCS.getExecutionCount())
+ continue;
+ return true;
+ }
+ return false;
+}
+
+bool SourceCoverageView::hasSubViews() const {
+ return !ExpansionSubViews.empty() || !InstantiationSubViews.empty() ||
+ !BranchSubViews.empty();
+}
+
+std::unique_ptr<SourceCoverageView>
+SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File,
+ const CoverageViewOptions &Options,
+ CoverageData &&CoverageInfo) {
+ switch (Options.Format) {
+ case CoverageViewOptions::OutputFormat::Text:
+ return std::make_unique<SourceCoverageViewText>(
+ SourceName, File, Options, std::move(CoverageInfo));
+ case CoverageViewOptions::OutputFormat::HTML:
+ return std::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!");
+}
+
+std::string SourceCoverageView::getSourceName() const {
+ SmallString<128> SourceText(SourceName);
+ sys::path::remove_dots(SourceText, /*remove_dot_dot=*/true);
+ sys::path::native(SourceText);
+ return std::string(SourceText.str());
+}
+
+void SourceCoverageView::addExpansion(
+ const CounterMappingRegion &Region,
+ std::unique_ptr<SourceCoverageView> View) {
+ ExpansionSubViews.emplace_back(Region, std::move(View));
+}
+
+void SourceCoverageView::addBranch(unsigned Line,
+ ArrayRef<CountedRegion> Regions,
+ std::unique_ptr<SourceCoverageView> View) {
+ BranchSubViews.emplace_back(Line, Regions, std::move(View));
+}
+
+void SourceCoverageView::addInstantiation(
+ StringRef FunctionName, unsigned Line,
+ std::unique_ptr<SourceCoverageView> View) {
+ InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View));
+}
+
+void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
+ bool ShowSourceName, bool ShowTitle,
+ unsigned ViewDepth) {
+ if (ShowTitle)
+ renderTitle(OS, "Coverage Report");
+
+ renderViewHeader(OS);
+
+ if (ShowSourceName)
+ renderSourceName(OS, WholeFile);
+
+ renderTableHeader(OS, (ViewDepth > 0) ? 0 : getFirstUncoveredLineNo(),
+ ViewDepth);
+
+ // We need the expansions, instantiations, and branches sorted so we can go
+ // through them while we iterate lines.
+ llvm::stable_sort(ExpansionSubViews);
+ llvm::stable_sort(InstantiationSubViews);
+ llvm::stable_sort(BranchSubViews);
+ auto NextESV = ExpansionSubViews.begin();
+ auto EndESV = ExpansionSubViews.end();
+ auto NextISV = InstantiationSubViews.begin();
+ auto EndISV = InstantiationSubViews.end();
+ auto NextBRV = BranchSubViews.begin();
+ auto EndBRV = BranchSubViews.end();
+
+ // Get the coverage information for the file.
+ auto StartSegment = CoverageInfo.begin();
+ auto EndSegment = CoverageInfo.end();
+ LineCoverageIterator LCI{CoverageInfo, 1};
+ LineCoverageIterator LCIEnd = LCI.getEnd();
+
+ unsigned FirstLine = StartSegment != EndSegment ? StartSegment->Line : 0;
+ for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof();
+ ++LI, ++LCI) {
+ // If we aren't rendering the whole file, we need to filter out the prologue
+ // and epilogue.
+ if (!WholeFile) {
+ if (LCI == LCIEnd)
+ break;
+ else if (LI.line_number() < FirstLine)
+ continue;
+ }
+
+ renderLinePrefix(OS, ViewDepth);
+ if (getOptions().ShowLineNumbers)
+ renderLineNumberColumn(OS, LI.line_number());
+
+ if (getOptions().ShowLineStats)
+ renderLineCoverageColumn(OS, *LCI);
+
+ // If there are expansion subviews, we want to highlight the first one.
+ unsigned ExpansionColumn = 0;
+ if (NextESV != EndESV && NextESV->getLine() == LI.line_number() &&
+ getOptions().Colors)
+ ExpansionColumn = NextESV->getStartCol();
+
+ // Display the source code for the current line.
+ renderLine(OS, {*LI, LI.line_number()}, *LCI, ExpansionColumn, ViewDepth);
+
+ // Show the region markers.
+ if (shouldRenderRegionMarkers(*LCI))
+ renderRegionMarkers(OS, *LCI, ViewDepth);
+
+ // Show the expansions, instantiations, and branches for this line.
+ bool RenderedSubView = false;
+ for (; NextESV != EndESV && NextESV->getLine() == LI.line_number();
+ ++NextESV) {
+ renderViewDivider(OS, ViewDepth + 1);
+
+ // Re-render the current line and highlight the expansion range for
+ // this subview.
+ if (RenderedSubView) {
+ ExpansionColumn = NextESV->getStartCol();
+ renderExpansionSite(OS, {*LI, LI.line_number()}, *LCI, ExpansionColumn,
+ ViewDepth);
+ renderViewDivider(OS, ViewDepth + 1);
+ }
+
+ renderExpansionView(OS, *NextESV, ViewDepth + 1);
+ RenderedSubView = true;
+ }
+ for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) {
+ renderViewDivider(OS, ViewDepth + 1);
+ renderInstantiationView(OS, *NextISV, ViewDepth + 1);
+ RenderedSubView = true;
+ }
+ for (; NextBRV != EndBRV && NextBRV->Line == LI.line_number(); ++NextBRV) {
+ renderViewDivider(OS, ViewDepth + 1);
+ renderBranchView(OS, *NextBRV, ViewDepth + 1);
+ RenderedSubView = true;
+ }
+ if (RenderedSubView)
+ renderViewDivider(OS, ViewDepth + 1);
+ renderLineSuffix(OS, ViewDepth);
+ }
+
+ renderViewFooter(OS);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageView.h b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageView.h
new file mode 100644
index 00000000000..5a9fcdd1576
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageView.h
@@ -0,0 +1,294 @@
+//===- SourceCoverageView.h - Code coverage view for source code ----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This class implements rendering for code coverage of source code.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_SOURCECOVERAGEVIEW_H
+#define LLVM_COV_SOURCECOVERAGEVIEW_H
+
+#include "CoverageViewOptions.h"
+#include "CoverageSummaryInfo.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <vector>
+
+namespace llvm {
+
+using namespace coverage;
+
+class CoverageFiltersMatchAll;
+class SourceCoverageView;
+
+/// A view that represents a macro or include expansion.
+struct ExpansionView {
+ CounterMappingRegion Region;
+ std::unique_ptr<SourceCoverageView> View;
+
+ ExpansionView(const CounterMappingRegion &Region,
+ std::unique_ptr<SourceCoverageView> View)
+ : Region(Region), View(std::move(View)) {}
+ ExpansionView(ExpansionView &&RHS)
+ : Region(std::move(RHS.Region)), View(std::move(RHS.View)) {}
+ ExpansionView &operator=(ExpansionView &&RHS) {
+ Region = std::move(RHS.Region);
+ View = std::move(RHS.View);
+ return *this;
+ }
+
+ unsigned getLine() const { return Region.LineStart; }
+ unsigned getStartCol() const { return Region.ColumnStart; }
+ unsigned getEndCol() const { return Region.ColumnEnd; }
+
+ friend bool operator<(const ExpansionView &LHS, const ExpansionView &RHS) {
+ return LHS.Region.startLoc() < RHS.Region.startLoc();
+ }
+};
+
+/// A view that represents a function instantiation.
+struct InstantiationView {
+ StringRef FunctionName;
+ unsigned Line;
+ std::unique_ptr<SourceCoverageView> View;
+
+ InstantiationView(StringRef FunctionName, unsigned Line,
+ std::unique_ptr<SourceCoverageView> View)
+ : FunctionName(FunctionName), Line(Line), View(std::move(View)) {}
+
+ friend bool operator<(const InstantiationView &LHS,
+ const InstantiationView &RHS) {
+ return LHS.Line < RHS.Line;
+ }
+};
+
+/// A view that represents one or more branch regions on a given source line.
+struct BranchView {
+ std::vector<CountedRegion> Regions;
+ std::unique_ptr<SourceCoverageView> View;
+ unsigned Line;
+
+ BranchView(unsigned Line, ArrayRef<CountedRegion> Regions,
+ std::unique_ptr<SourceCoverageView> View)
+ : Regions(Regions), View(std::move(View)), Line(Line) {}
+
+ unsigned getLine() const { return Line; }
+
+ friend bool operator<(const BranchView &LHS, const BranchView &RHS) {
+ return LHS.Line < RHS.Line;
+ }
+};
+
+/// A file manager that handles format-aware file creation.
+class CoveragePrinter {
+public:
+ struct StreamDestructor {
+ void operator()(raw_ostream *OS) const;
+ };
+
+ using OwnedStream = std::unique_ptr<raw_ostream, StreamDestructor>;
+
+protected:
+ const CoverageViewOptions &Opts;
+
+ CoveragePrinter(const CoverageViewOptions &Opts) : Opts(Opts) {}
+
+ /// Return `OutputDir/ToplevelDir/Path.Extension`. If \p InToplevel is
+ /// false, skip the ToplevelDir component. If \p Relative is false, skip the
+ /// OutputDir component.
+ std::string getOutputPath(StringRef Path, StringRef Extension,
+ bool InToplevel, bool Relative = true) const;
+
+ /// If directory output is enabled, create a file in that directory
+ /// at the path given by getOutputPath(). Otherwise, return stdout.
+ Expected<OwnedStream> createOutputStream(StringRef Path, StringRef Extension,
+ bool InToplevel) const;
+
+ /// Return the sub-directory name for file coverage reports.
+ static StringRef getCoverageDir() { return "coverage"; }
+
+public:
+ static std::unique_ptr<CoveragePrinter>
+ create(const CoverageViewOptions &Opts);
+
+ virtual ~CoveragePrinter() {}
+
+ /// @name File Creation Interface
+ /// @{
+
+ /// Create a file to print a coverage view into.
+ virtual Expected<OwnedStream> createViewFile(StringRef Path,
+ bool InToplevel) = 0;
+
+ /// Close a file which has been used to print a coverage view.
+ virtual void closeViewFile(OwnedStream OS) = 0;
+
+ /// Create an index which lists reports for the given source files.
+ virtual Error createIndexFile(ArrayRef<std::string> SourceFiles,
+ const CoverageMapping &Coverage,
+ const CoverageFiltersMatchAll &Filters) = 0;
+
+ /// @}
+};
+
+/// A code coverage view of a source file or function.
+///
+/// A source coverage view and its nested sub-views form a file-oriented
+/// representation of code coverage data. This view can be printed out by a
+/// renderer which implements the Rendering Interface.
+class SourceCoverageView {
+ /// A function or file name.
+ StringRef SourceName;
+
+ /// A memory buffer backing the source on display.
+ const MemoryBuffer &File;
+
+ /// Various options to guide the coverage renderer.
+ const CoverageViewOptions &Options;
+
+ /// Complete coverage information about the source on display.
+ CoverageData CoverageInfo;
+
+ /// A container for all expansions (e.g macros) in the source on display.
+ std::vector<ExpansionView> ExpansionSubViews;
+
+ /// A container for all branches in the source on display.
+ std::vector<BranchView> BranchSubViews;
+
+ /// A container for all instantiations (e.g template functions) in the source
+ /// on display.
+ std::vector<InstantiationView> InstantiationSubViews;
+
+ /// Get the first uncovered line number for the source file.
+ unsigned getFirstUncoveredLineNo();
+
+protected:
+ struct LineRef {
+ StringRef Line;
+ int64_t LineNo;
+
+ LineRef(StringRef Line, int64_t LineNo) : Line(Line), LineNo(LineNo) {}
+ };
+
+ using CoverageSegmentArray = ArrayRef<const CoverageSegment *>;
+
+ /// @name Rendering Interface
+ /// @{
+
+ /// Render a header for the view.
+ virtual void renderViewHeader(raw_ostream &OS) = 0;
+
+ /// Render a footer for the view.
+ virtual void renderViewFooter(raw_ostream &OS) = 0;
+
+ /// Render the source name for the view.
+ virtual void renderSourceName(raw_ostream &OS, bool WholeFile) = 0;
+
+ /// Render the line prefix at the given \p ViewDepth.
+ virtual void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) = 0;
+
+ /// Render the line suffix at the given \p ViewDepth.
+ virtual void renderLineSuffix(raw_ostream &OS, unsigned ViewDepth) = 0;
+
+ /// Render a view divider at the given \p ViewDepth.
+ virtual void renderViewDivider(raw_ostream &OS, unsigned ViewDepth) = 0;
+
+ /// Render a source line with highlighting.
+ virtual void renderLine(raw_ostream &OS, LineRef L,
+ const LineCoverageStats &LCS, unsigned ExpansionCol,
+ unsigned ViewDepth) = 0;
+
+ /// Render the line's execution count column.
+ virtual void renderLineCoverageColumn(raw_ostream &OS,
+ const LineCoverageStats &Line) = 0;
+
+ /// Render the line number column.
+ virtual void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) = 0;
+
+ /// Render all the region's execution counts on a line.
+ virtual void renderRegionMarkers(raw_ostream &OS,
+ const LineCoverageStats &Line,
+ unsigned ViewDepth) = 0;
+
+ /// Render the site of an expansion.
+ virtual void renderExpansionSite(raw_ostream &OS, LineRef L,
+ const LineCoverageStats &LCS,
+ unsigned ExpansionCol,
+ unsigned ViewDepth) = 0;
+
+ /// Render an expansion view and any nested views.
+ virtual void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
+ unsigned ViewDepth) = 0;
+
+ /// Render an instantiation view and any nested views.
+ virtual void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
+ unsigned ViewDepth) = 0;
+
+ /// Render a branch view and any nested views.
+ virtual void renderBranchView(raw_ostream &OS, BranchView &BRV,
+ unsigned ViewDepth) = 0;
+
+ /// Render \p Title, a project title if one is available, and the
+ /// created time.
+ virtual void renderTitle(raw_ostream &OS, StringRef CellText) = 0;
+
+ /// Render the table header for a given source file.
+ virtual void renderTableHeader(raw_ostream &OS, unsigned FirstUncoveredLineNo,
+ unsigned IndentLevel) = 0;
+
+ /// @}
+
+ /// Format a count using engineering notation with 3 significant
+ /// digits.
+ static std::string formatCount(uint64_t N);
+
+ /// Check if region marker output is expected for a line.
+ bool shouldRenderRegionMarkers(const LineCoverageStats &LCS) const;
+
+ /// Check if there are any sub-views attached to this view.
+ bool hasSubViews() const;
+
+ SourceCoverageView(StringRef SourceName, const MemoryBuffer &File,
+ const CoverageViewOptions &Options,
+ CoverageData &&CoverageInfo)
+ : SourceName(SourceName), File(File), Options(Options),
+ CoverageInfo(std::move(CoverageInfo)) {}
+
+public:
+ static std::unique_ptr<SourceCoverageView>
+ create(StringRef SourceName, const MemoryBuffer &File,
+ const CoverageViewOptions &Options, CoverageData &&CoverageInfo);
+
+ virtual ~SourceCoverageView() {}
+
+ /// Return the source name formatted for the host OS.
+ std::string getSourceName() const;
+
+ const CoverageViewOptions &getOptions() const { return Options; }
+
+ /// Add an expansion subview to this view.
+ void addExpansion(const CounterMappingRegion &Region,
+ std::unique_ptr<SourceCoverageView> View);
+
+ /// Add a function instantiation subview to this view.
+ void addInstantiation(StringRef FunctionName, unsigned Line,
+ std::unique_ptr<SourceCoverageView> View);
+
+ /// Add a branch subview to this view.
+ void addBranch(unsigned Line, ArrayRef<CountedRegion> Regions,
+ std::unique_ptr<SourceCoverageView> View);
+
+ /// Print the code coverage information for a specific portion of a
+ /// source file to the output stream.
+ void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName,
+ bool ShowTitle, unsigned ViewDepth = 0);
+};
+
+} // namespace llvm
+
+#endif // LLVM_COV_SOURCECOVERAGEVIEW_H
diff --git a/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewHTML.cpp b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewHTML.cpp
new file mode 100644
index 00000000000..56efc40b934
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewHTML.cpp
@@ -0,0 +1,768 @@
+//===- SourceCoverageViewHTML.cpp - A html code coverage view -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file implements the html coverage renderer.
+///
+//===----------------------------------------------------------------------===//
+
+#include "CoverageReport.h"
+#include "SourceCoverageViewHTML.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+
+namespace {
+
+// Return a string with the special characters in \p Str escaped.
+std::string escape(StringRef Str, const CoverageViewOptions &Opts) {
+ std::string TabExpandedResult;
+ unsigned ColNum = 0; // Record the column number.
+ for (char C : Str) {
+ if (C == '\t') {
+ // Replace '\t' with up to TabSize spaces.
+ unsigned NumSpaces = Opts.TabSize - (ColNum % Opts.TabSize);
+ TabExpandedResult.append(NumSpaces, ' ');
+ ColNum += NumSpaces;
+ } else {
+ TabExpandedResult += C;
+ if (C == '\n' || C == '\r')
+ ColNum = 0;
+ else
+ ++ColNum;
+ }
+ }
+ std::string EscapedHTML;
+ {
+ raw_string_ostream OS{EscapedHTML};
+ printHTMLEscaped(TabExpandedResult, OS);
+ }
+ return EscapedHTML;
+}
+
+// Create a \p Name tag around \p Str, and optionally set its \p ClassName.
+std::string tag(const std::string &Name, const std::string &Str,
+ const std::string &ClassName = "") {
+ std::string Tag = "<" + Name;
+ if (!ClassName.empty())
+ Tag += " class='" + ClassName + "'";
+ return Tag + ">" + Str + "</" + Name + ">";
+}
+
+// Create an anchor to \p Link with the label \p Str.
+std::string a(const std::string &Link, const std::string &Str,
+ const std::string &TargetName = "") {
+ std::string Name = TargetName.empty() ? "" : ("name='" + TargetName + "' ");
+ return "<a " + Name + "href='" + Link + "'>" + Str + "</a>";
+}
+
+const char *BeginHeader =
+ "<head>"
+ "<meta name='viewport' content='width=device-width,initial-scale=1'>"
+ "<meta charset='UTF-8'>";
+
+const char *CSSForCoverage =
+ R"(.red {
+ background-color: #ffd0d0;
+}
+.cyan {
+ background-color: cyan;
+}
+body {
+ font-family: -apple-system, sans-serif;
+}
+pre {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+}
+.source-name-title {
+ padding: 5px 10px;
+ border-bottom: 1px solid #dbdbdb;
+ background-color: #eee;
+ line-height: 35px;
+}
+.centered {
+ display: table;
+ margin-left: left;
+ margin-right: auto;
+ border: 1px solid #dbdbdb;
+ border-radius: 3px;
+}
+.expansion-view {
+ background-color: rgba(0, 0, 0, 0);
+ margin-left: 0px;
+ margin-top: 5px;
+ margin-right: 5px;
+ margin-bottom: 5px;
+ border: 1px solid #dbdbdb;
+ border-radius: 3px;
+}
+table {
+ border-collapse: collapse;
+}
+.light-row {
+ background: #ffffff;
+ border: 1px solid #dbdbdb;
+}
+.light-row-bold {
+ background: #ffffff;
+ border: 1px solid #dbdbdb;
+ font-weight: bold;
+}
+.column-entry {
+ text-align: left;
+}
+.column-entry-bold {
+ font-weight: bold;
+ text-align: left;
+}
+.column-entry-yellow {
+ text-align: left;
+ background-color: #ffffd0;
+}
+.column-entry-yellow:hover {
+ background-color: #fffff0;
+}
+.column-entry-red {
+ text-align: left;
+ background-color: #ffd0d0;
+}
+.column-entry-red:hover {
+ background-color: #fff0f0;
+}
+.column-entry-green {
+ text-align: left;
+ background-color: #d0ffd0;
+}
+.column-entry-green:hover {
+ background-color: #f0fff0;
+}
+.line-number {
+ text-align: right;
+ color: #aaa;
+}
+.covered-line {
+ text-align: right;
+ color: #0080ff;
+}
+.uncovered-line {
+ text-align: right;
+ color: #ff3300;
+}
+.tooltip {
+ position: relative;
+ display: inline;
+ background-color: #b3e6ff;
+ text-decoration: none;
+}
+.tooltip span.tooltip-content {
+ position: absolute;
+ width: 100px;
+ margin-left: -50px;
+ color: #FFFFFF;
+ background: #000000;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ visibility: hidden;
+ border-radius: 6px;
+}
+.tooltip span.tooltip-content:after {
+ content: '';
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -8px;
+ width: 0; height: 0;
+ border-top: 8px solid #000000;
+ border-right: 8px solid transparent;
+ border-left: 8px solid transparent;
+}
+:hover.tooltip span.tooltip-content {
+ visibility: visible;
+ opacity: 0.8;
+ bottom: 30px;
+ left: 50%;
+ z-index: 999;
+}
+th, td {
+ vertical-align: top;
+ padding: 2px 8px;
+ border-collapse: collapse;
+ border-right: solid 1px #eee;
+ border-left: solid 1px #eee;
+ text-align: left;
+}
+td pre {
+ display: inline-block;
+}
+td:first-child {
+ border-left: none;
+}
+td:last-child {
+ border-right: none;
+}
+tr:hover {
+ background-color: #f0f0f0;
+}
+)";
+
+const char *EndHeader = "</head>";
+
+const char *BeginCenteredDiv = "<div class='centered'>";
+
+const char *EndCenteredDiv = "</div>";
+
+const char *BeginSourceNameDiv = "<div class='source-name-title'>";
+
+const char *EndSourceNameDiv = "</div>";
+
+const char *BeginCodeTD = "<td class='code'>";
+
+const char *EndCodeTD = "</td>";
+
+const char *BeginPre = "<pre>";
+
+const char *EndPre = "</pre>";
+
+const char *BeginExpansionDiv = "<div class='expansion-view'>";
+
+const char *EndExpansionDiv = "</div>";
+
+const char *BeginTable = "<table>";
+
+const char *EndTable = "</table>";
+
+const char *ProjectTitleTag = "h1";
+
+const char *ReportTitleTag = "h2";
+
+const char *CreatedTimeTag = "h4";
+
+std::string getPathToStyle(StringRef ViewPath) {
+ std::string PathToStyle;
+ std::string PathSep = std::string(sys::path::get_separator());
+ unsigned NumSeps = ViewPath.count(PathSep);
+ for (unsigned I = 0, E = NumSeps; I < E; ++I)
+ PathToStyle += ".." + PathSep;
+ return PathToStyle + "style.css";
+}
+
+void emitPrelude(raw_ostream &OS, const CoverageViewOptions &Opts,
+ const std::string &PathToStyle = "") {
+ OS << "<!doctype html>"
+ "<html>"
+ << BeginHeader;
+
+ // Link to a stylesheet if one is available. Otherwise, use the default style.
+ if (PathToStyle.empty())
+ OS << "<style>" << CSSForCoverage << "</style>";
+ else
+ OS << "<link rel='stylesheet' type='text/css' href='"
+ << escape(PathToStyle, Opts) << "'>";
+
+ OS << EndHeader << "<body>";
+}
+
+void emitEpilog(raw_ostream &OS) {
+ OS << "</body>"
+ << "</html>";
+}
+
+} // anonymous namespace
+
+Expected<CoveragePrinter::OwnedStream>
+CoveragePrinterHTML::createViewFile(StringRef Path, bool InToplevel) {
+ auto OSOrErr = createOutputStream(Path, "html", InToplevel);
+ if (!OSOrErr)
+ return OSOrErr;
+
+ OwnedStream OS = std::move(OSOrErr.get());
+
+ if (!Opts.hasOutputDirectory()) {
+ emitPrelude(*OS.get(), Opts);
+ } else {
+ std::string ViewPath = getOutputPath(Path, "html", InToplevel);
+ emitPrelude(*OS.get(), Opts, getPathToStyle(ViewPath));
+ }
+
+ return std::move(OS);
+}
+
+void CoveragePrinterHTML::closeViewFile(OwnedStream OS) {
+ emitEpilog(*OS.get());
+}
+
+/// Emit column labels for the table in the index.
+static void emitColumnLabelsForIndex(raw_ostream &OS,
+ const CoverageViewOptions &Opts) {
+ SmallVector<std::string, 4> Columns;
+ Columns.emplace_back(tag("td", "Filename", "column-entry-bold"));
+ Columns.emplace_back(tag("td", "Function Coverage", "column-entry-bold"));
+ if (Opts.ShowInstantiationSummary)
+ Columns.emplace_back(
+ tag("td", "Instantiation Coverage", "column-entry-bold"));
+ Columns.emplace_back(tag("td", "Line Coverage", "column-entry-bold"));
+ if (Opts.ShowRegionSummary)
+ Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold"));
+ if (Opts.ShowBranchSummary)
+ Columns.emplace_back(tag("td", "Branch Coverage", "column-entry-bold"));
+ OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
+}
+
+std::string
+CoveragePrinterHTML::buildLinkToFile(StringRef SF,
+ const FileCoverageSummary &FCS) const {
+ SmallString<128> LinkTextStr(sys::path::relative_path(FCS.Name));
+ sys::path::remove_dots(LinkTextStr, /*remove_dot_dot=*/true);
+ sys::path::native(LinkTextStr);
+ std::string LinkText = escape(LinkTextStr, Opts);
+ std::string LinkTarget =
+ escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts);
+ return a(LinkTarget, LinkText);
+}
+
+/// Render a file coverage summary (\p FCS) in a table row. If \p IsTotals is
+/// false, link the summary to \p SF.
+void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF,
+ const FileCoverageSummary &FCS,
+ bool IsTotals) const {
+ SmallVector<std::string, 8> Columns;
+
+ // Format a coverage triple and add the result to the list of columns.
+ auto AddCoverageTripleToColumn = [&Columns](unsigned Hit, unsigned Total,
+ float Pctg) {
+ std::string S;
+ {
+ raw_string_ostream RSO{S};
+ if (Total)
+ RSO << format("%*.2f", 7, Pctg) << "% ";
+ else
+ RSO << "- ";
+ RSO << '(' << Hit << '/' << Total << ')';
+ }
+ const char *CellClass = "column-entry-yellow";
+ if (Hit == Total)
+ CellClass = "column-entry-green";
+ else if (Pctg < 80.0)
+ CellClass = "column-entry-red";
+ Columns.emplace_back(tag("td", tag("pre", S), CellClass));
+ };
+
+ // Simplify the display file path, and wrap it in a link if requested.
+ std::string Filename;
+ if (IsTotals) {
+ Filename = std::string(SF);
+ } else {
+ Filename = buildLinkToFile(SF, FCS);
+ }
+
+ Columns.emplace_back(tag("td", tag("pre", Filename)));
+ AddCoverageTripleToColumn(FCS.FunctionCoverage.getExecuted(),
+ FCS.FunctionCoverage.getNumFunctions(),
+ FCS.FunctionCoverage.getPercentCovered());
+ if (Opts.ShowInstantiationSummary)
+ AddCoverageTripleToColumn(FCS.InstantiationCoverage.getExecuted(),
+ FCS.InstantiationCoverage.getNumFunctions(),
+ FCS.InstantiationCoverage.getPercentCovered());
+ AddCoverageTripleToColumn(FCS.LineCoverage.getCovered(),
+ FCS.LineCoverage.getNumLines(),
+ FCS.LineCoverage.getPercentCovered());
+ if (Opts.ShowRegionSummary)
+ AddCoverageTripleToColumn(FCS.RegionCoverage.getCovered(),
+ FCS.RegionCoverage.getNumRegions(),
+ FCS.RegionCoverage.getPercentCovered());
+ if (Opts.ShowBranchSummary)
+ AddCoverageTripleToColumn(FCS.BranchCoverage.getCovered(),
+ FCS.BranchCoverage.getNumBranches(),
+ FCS.BranchCoverage.getPercentCovered());
+
+ if (IsTotals)
+ OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold");
+ else
+ OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row");
+}
+
+Error CoveragePrinterHTML::createIndexFile(
+ ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage,
+ const CoverageFiltersMatchAll &Filters) {
+ // Emit the default stylesheet.
+ auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true);
+ if (Error E = CSSOrErr.takeError())
+ return E;
+
+ OwnedStream CSS = std::move(CSSOrErr.get());
+ CSS->operator<<(CSSForCoverage);
+
+ // Emit a file index along with some coverage statistics.
+ auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true);
+ if (Error E = OSOrErr.takeError())
+ return E;
+ auto OS = std::move(OSOrErr.get());
+ raw_ostream &OSRef = *OS.get();
+
+ assert(Opts.hasOutputDirectory() && "No output directory for index file");
+ emitPrelude(OSRef, Opts, getPathToStyle(""));
+
+ // Emit some basic information about the coverage report.
+ if (Opts.hasProjectTitle())
+ OSRef << tag(ProjectTitleTag, escape(Opts.ProjectTitle, Opts));
+ OSRef << tag(ReportTitleTag, "Coverage Report");
+ if (Opts.hasCreatedTime())
+ OSRef << tag(CreatedTimeTag, escape(Opts.CreatedTimeStr, Opts));
+
+ // Emit a link to some documentation.
+ OSRef << tag("p", "Click " +
+ a("http://clang.llvm.org/docs/"
+ "SourceBasedCodeCoverage.html#interpreting-reports",
+ "here") +
+ " for information about interpreting this report.");
+
+ // Emit a table containing links to reports for each file in the covmapping.
+ // Exclude files which don't contain any regions.
+ OSRef << BeginCenteredDiv << BeginTable;
+ emitColumnLabelsForIndex(OSRef, Opts);
+ FileCoverageSummary Totals("TOTALS");
+ auto FileReports = CoverageReport::prepareFileReports(
+ Coverage, Totals, SourceFiles, Opts, Filters);
+ bool EmptyFiles = false;
+ for (unsigned I = 0, E = FileReports.size(); I < E; ++I) {
+ if (FileReports[I].FunctionCoverage.getNumFunctions())
+ emitFileSummary(OSRef, SourceFiles[I], FileReports[I]);
+ else
+ EmptyFiles = true;
+ }
+ emitFileSummary(OSRef, "Totals", Totals, /*IsTotals=*/true);
+ OSRef << EndTable << EndCenteredDiv;
+
+ // Emit links to files which don't contain any functions. These are normally
+ // not very useful, but could be relevant for code which abuses the
+ // preprocessor.
+ if (EmptyFiles && Filters.empty()) {
+ OSRef << tag("p", "Files which contain no functions. (These "
+ "files contain code pulled into other files "
+ "by the preprocessor.)\n");
+ OSRef << BeginCenteredDiv << BeginTable;
+ for (unsigned I = 0, E = FileReports.size(); I < E; ++I)
+ if (!FileReports[I].FunctionCoverage.getNumFunctions()) {
+ std::string Link = buildLinkToFile(SourceFiles[I], FileReports[I]);
+ OSRef << tag("tr", tag("td", tag("pre", Link)), "light-row") << '\n';
+ }
+ OSRef << EndTable << EndCenteredDiv;
+ }
+
+ OSRef << tag("h5", escape(Opts.getLLVMVersionString(), Opts));
+ emitEpilog(OSRef);
+
+ return Error::success();
+}
+
+void SourceCoverageViewHTML::renderViewHeader(raw_ostream &OS) {
+ OS << BeginCenteredDiv << BeginTable;
+}
+
+void SourceCoverageViewHTML::renderViewFooter(raw_ostream &OS) {
+ OS << EndTable << EndCenteredDiv;
+}
+
+void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS, bool WholeFile) {
+ OS << BeginSourceNameDiv << tag("pre", escape(getSourceName(), getOptions()))
+ << EndSourceNameDiv;
+}
+
+void SourceCoverageViewHTML::renderLinePrefix(raw_ostream &OS, unsigned) {
+ OS << "<tr>";
+}
+
+void SourceCoverageViewHTML::renderLineSuffix(raw_ostream &OS, unsigned) {
+ // If this view has sub-views, renderLine() cannot close the view's cell.
+ // Take care of it here, after all sub-views have been rendered.
+ if (hasSubViews())
+ OS << EndCodeTD;
+ OS << "</tr>";
+}
+
+void SourceCoverageViewHTML::renderViewDivider(raw_ostream &, unsigned) {
+ // The table-based output makes view dividers unnecessary.
+}
+
+void SourceCoverageViewHTML::renderLine(raw_ostream &OS, LineRef L,
+ const LineCoverageStats &LCS,
+ unsigned ExpansionCol, unsigned) {
+ StringRef Line = L.Line;
+ unsigned LineNo = L.LineNo;
+
+ // Steps for handling text-escaping, highlighting, and tooltip creation:
+ //
+ // 1. Split the line into N+1 snippets, where N = |Segments|. The first
+ // snippet starts from Col=1 and ends at the start of the first segment.
+ // The last snippet starts at the last mapped column in the line and ends
+ // at the end of the line. Both are required but may be empty.
+
+ SmallVector<std::string, 8> Snippets;
+ CoverageSegmentArray Segments = LCS.getLineSegments();
+
+ unsigned LCol = 1;
+ auto Snip = [&](unsigned Start, unsigned Len) {
+ Snippets.push_back(std::string(Line.substr(Start, Len)));
+ LCol += Len;
+ };
+
+ Snip(LCol - 1, Segments.empty() ? 0 : (Segments.front()->Col - 1));
+
+ for (unsigned I = 1, E = Segments.size(); I < E; ++I)
+ Snip(LCol - 1, Segments[I]->Col - LCol);
+
+ // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1.
+ Snip(LCol - 1, Line.size() + 1 - LCol);
+
+ // 2. Escape all of the snippets.
+
+ for (unsigned I = 0, E = Snippets.size(); I < E; ++I)
+ Snippets[I] = escape(Snippets[I], getOptions());
+
+ // 3. Use \p WrappedSegment to set the highlight for snippet 0. Use segment
+ // 1 to set the highlight for snippet 2, segment 2 to set the highlight for
+ // snippet 3, and so on.
+
+ Optional<StringRef> Color;
+ SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
+ auto Highlight = [&](const std::string &Snippet, unsigned LC, unsigned RC) {
+ if (getOptions().Debug)
+ HighlightedRanges.emplace_back(LC, RC);
+ return tag("span", Snippet, std::string(Color.getValue()));
+ };
+
+ auto CheckIfUncovered = [&](const CoverageSegment *S) {
+ return S && (!S->IsGapRegion || (Color && *Color == "red")) &&
+ S->HasCount && S->Count == 0;
+ };
+
+ if (CheckIfUncovered(LCS.getWrappedSegment())) {
+ Color = "red";
+ if (!Snippets[0].empty())
+ Snippets[0] = Highlight(Snippets[0], 1, 1 + Snippets[0].size());
+ }
+
+ for (unsigned I = 0, E = Segments.size(); I < E; ++I) {
+ const auto *CurSeg = Segments[I];
+ if (CheckIfUncovered(CurSeg))
+ Color = "red";
+ else if (CurSeg->Col == ExpansionCol)
+ Color = "cyan";
+ else
+ Color = None;
+
+ if (Color.hasValue())
+ Snippets[I + 1] = Highlight(Snippets[I + 1], CurSeg->Col,
+ CurSeg->Col + Snippets[I + 1].size());
+ }
+
+ if (Color.hasValue() && Segments.empty())
+ Snippets.back() = Highlight(Snippets.back(), 1, 1 + Snippets.back().size());
+
+ if (getOptions().Debug) {
+ for (const auto &Range : HighlightedRanges) {
+ errs() << "Highlighted line " << LineNo << ", " << Range.first << " -> ";
+ if (Range.second == 0)
+ errs() << "?";
+ else
+ errs() << Range.second;
+ errs() << "\n";
+ }
+ }
+
+ // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate
+ // sub-line region count tooltips if needed.
+
+ if (shouldRenderRegionMarkers(LCS)) {
+ // Just consider the segments which start *and* end on this line.
+ for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) {
+ const auto *CurSeg = Segments[I];
+ if (!CurSeg->IsRegionEntry)
+ continue;
+ if (CurSeg->Count == LCS.getExecutionCount())
+ continue;
+
+ Snippets[I + 1] =
+ tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count),
+ "tooltip-content"),
+ "tooltip");
+
+ if (getOptions().Debug)
+ errs() << "Marker at " << CurSeg->Line << ":" << CurSeg->Col << " = "
+ << formatCount(CurSeg->Count) << "\n";
+ }
+ }
+
+ OS << BeginCodeTD;
+ OS << BeginPre;
+ for (const auto &Snippet : Snippets)
+ OS << Snippet;
+ OS << EndPre;
+
+ // If there are no sub-views left to attach to this cell, end the cell.
+ // Otherwise, end it after the sub-views are rendered (renderLineSuffix()).
+ if (!hasSubViews())
+ OS << EndCodeTD;
+}
+
+void SourceCoverageViewHTML::renderLineCoverageColumn(
+ raw_ostream &OS, const LineCoverageStats &Line) {
+ std::string Count;
+ if (Line.isMapped())
+ Count = tag("pre", formatCount(Line.getExecutionCount()));
+ std::string CoverageClass =
+ (Line.getExecutionCount() > 0) ? "covered-line" : "uncovered-line";
+ OS << tag("td", Count, CoverageClass);
+}
+
+void SourceCoverageViewHTML::renderLineNumberColumn(raw_ostream &OS,
+ unsigned LineNo) {
+ std::string LineNoStr = utostr(uint64_t(LineNo));
+ std::string TargetName = "L" + LineNoStr;
+ OS << tag("td", a("#" + TargetName, tag("pre", LineNoStr), TargetName),
+ "line-number");
+}
+
+void SourceCoverageViewHTML::renderRegionMarkers(raw_ostream &,
+ const LineCoverageStats &Line,
+ unsigned) {
+ // Region markers are rendered in-line using tooltips.
+}
+
+void SourceCoverageViewHTML::renderExpansionSite(raw_ostream &OS, LineRef L,
+ const LineCoverageStats &LCS,
+ unsigned ExpansionCol,
+ unsigned ViewDepth) {
+ // Render the line containing the expansion site. No extra formatting needed.
+ renderLine(OS, L, LCS, ExpansionCol, ViewDepth);
+}
+
+void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS,
+ ExpansionView &ESV,
+ unsigned ViewDepth) {
+ OS << BeginExpansionDiv;
+ ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
+ /*ShowTitle=*/false, ViewDepth + 1);
+ OS << EndExpansionDiv;
+}
+
+void SourceCoverageViewHTML::renderBranchView(raw_ostream &OS, BranchView &BRV,
+ unsigned ViewDepth) {
+ // Render the child subview.
+ if (getOptions().Debug)
+ errs() << "Branch at line " << BRV.getLine() << '\n';
+
+ OS << BeginExpansionDiv;
+ OS << BeginPre;
+ for (const auto &R : BRV.Regions) {
+ // Calculate TruePercent and False Percent.
+ double TruePercent = 0.0;
+ double FalsePercent = 0.0;
+ unsigned Total = R.ExecutionCount + R.FalseExecutionCount;
+
+ if (!getOptions().ShowBranchCounts && Total != 0) {
+ TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0;
+ FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0;
+ }
+
+ // Display Line + Column.
+ std::string LineNoStr = utostr(uint64_t(R.LineStart));
+ std::string ColNoStr = utostr(uint64_t(R.ColumnStart));
+ std::string TargetName = "L" + LineNoStr;
+
+ OS << " Branch (";
+ OS << tag("span",
+ a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr),
+ TargetName),
+ "line-number") +
+ "): [";
+
+ if (R.Folded) {
+ OS << "Folded - Ignored]\n";
+ continue;
+ }
+
+ // Display TrueCount or TruePercent.
+ std::string TrueColor = R.ExecutionCount ? "None" : "red";
+ std::string TrueCovClass =
+ (R.ExecutionCount > 0) ? "covered-line" : "uncovered-line";
+
+ OS << tag("span", "True", TrueColor);
+ OS << ": ";
+ if (getOptions().ShowBranchCounts)
+ OS << tag("span", formatCount(R.ExecutionCount), TrueCovClass) << ", ";
+ else
+ OS << format("%0.2f", TruePercent) << "%, ";
+
+ // Display FalseCount or FalsePercent.
+ std::string FalseColor = R.FalseExecutionCount ? "None" : "red";
+ std::string FalseCovClass =
+ (R.FalseExecutionCount > 0) ? "covered-line" : "uncovered-line";
+
+ OS << tag("span", "False", FalseColor);
+ OS << ": ";
+ if (getOptions().ShowBranchCounts)
+ OS << tag("span", formatCount(R.FalseExecutionCount), FalseCovClass);
+ else
+ OS << format("%0.2f", FalsePercent) << "%";
+
+ OS << "]\n";
+ }
+ OS << EndPre;
+ OS << EndExpansionDiv;
+}
+
+void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
+ InstantiationView &ISV,
+ unsigned ViewDepth) {
+ OS << BeginExpansionDiv;
+ if (!ISV.View)
+ OS << BeginSourceNameDiv
+ << tag("pre",
+ escape("Unexecuted instantiation: " + ISV.FunctionName.str(),
+ getOptions()))
+ << EndSourceNameDiv;
+ else
+ ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true,
+ /*ShowTitle=*/false, ViewDepth);
+ OS << EndExpansionDiv;
+}
+
+void SourceCoverageViewHTML::renderTitle(raw_ostream &OS, StringRef Title) {
+ if (getOptions().hasProjectTitle())
+ OS << tag(ProjectTitleTag, escape(getOptions().ProjectTitle, getOptions()));
+ OS << tag(ReportTitleTag, escape(Title, getOptions()));
+ if (getOptions().hasCreatedTime())
+ OS << tag(CreatedTimeTag,
+ escape(getOptions().CreatedTimeStr, getOptions()));
+}
+
+void SourceCoverageViewHTML::renderTableHeader(raw_ostream &OS,
+ unsigned FirstUncoveredLineNo,
+ unsigned ViewDepth) {
+ std::string SourceLabel;
+ if (FirstUncoveredLineNo == 0) {
+ SourceLabel = tag("td", tag("pre", "Source"));
+ } else {
+ std::string LinkTarget = "#L" + utostr(uint64_t(FirstUncoveredLineNo));
+ SourceLabel =
+ tag("td", tag("pre", "Source (" +
+ a(LinkTarget, "jump to first uncovered line") +
+ ")"));
+ }
+
+ renderLinePrefix(OS, ViewDepth);
+ OS << tag("td", tag("pre", "Line")) << tag("td", tag("pre", "Count"))
+ << SourceLabel;
+ renderLineSuffix(OS, ViewDepth);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewHTML.h b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewHTML.h
new file mode 100644
index 00000000000..7d94675f4b0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewHTML.h
@@ -0,0 +1,100 @@
+//===- SourceCoverageViewHTML.h - A html code coverage view ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file defines the interface to the html coverage renderer.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_SOURCECOVERAGEVIEWHTML_H
+#define LLVM_COV_SOURCECOVERAGEVIEWHTML_H
+
+#include "SourceCoverageView.h"
+
+namespace llvm {
+
+using namespace coverage;
+
+struct FileCoverageSummary;
+
+/// A coverage printer for html output.
+class CoveragePrinterHTML : public CoveragePrinter {
+public:
+ Expected<OwnedStream> createViewFile(StringRef Path,
+ bool InToplevel) override;
+
+ void closeViewFile(OwnedStream OS) override;
+
+ Error createIndexFile(ArrayRef<std::string> SourceFiles,
+ const coverage::CoverageMapping &Coverage,
+ const CoverageFiltersMatchAll &Filters) override;
+
+ CoveragePrinterHTML(const CoverageViewOptions &Opts)
+ : CoveragePrinter(Opts) {}
+
+private:
+ void emitFileSummary(raw_ostream &OS, StringRef SF,
+ const FileCoverageSummary &FCS,
+ bool IsTotals = false) const;
+ std::string buildLinkToFile(StringRef SF,
+ const FileCoverageSummary &FCS) const;
+};
+
+/// A code coverage view which supports html-based rendering.
+class SourceCoverageViewHTML : public SourceCoverageView {
+ void renderViewHeader(raw_ostream &OS) override;
+
+ void renderViewFooter(raw_ostream &OS) override;
+
+ void renderSourceName(raw_ostream &OS, bool WholeFile) override;
+
+ void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderLineSuffix(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderViewDivider(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderLine(raw_ostream &OS, LineRef L, const LineCoverageStats &LCS,
+ unsigned ExpansionCol, unsigned ViewDepth) override;
+
+ void renderExpansionSite(raw_ostream &OS, LineRef L,
+ const LineCoverageStats &LCS, unsigned ExpansionCol,
+ unsigned ViewDepth) override;
+
+ void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
+ unsigned ViewDepth) override;
+
+ void renderBranchView(raw_ostream &OS, BranchView &BRV,
+ unsigned ViewDepth) override;
+
+ void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
+ unsigned ViewDepth) override;
+
+ void renderLineCoverageColumn(raw_ostream &OS,
+ const LineCoverageStats &Line) override;
+
+ void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) override;
+
+ void renderRegionMarkers(raw_ostream &OS, const LineCoverageStats &Line,
+ unsigned ViewDepth) override;
+
+ void renderTitle(raw_ostream &OS, StringRef Title) override;
+
+ void renderTableHeader(raw_ostream &OS, unsigned FirstUncoveredLineNo,
+ unsigned IndentLevel) override;
+
+public:
+ SourceCoverageViewHTML(StringRef SourceName, const MemoryBuffer &File,
+ const CoverageViewOptions &Options,
+ coverage::CoverageData &&CoverageInfo)
+ : SourceCoverageView(SourceName, File, Options, std::move(CoverageInfo)) {
+ }
+};
+
+} // namespace llvm
+
+#endif // LLVM_COV_SOURCECOVERAGEVIEWHTML_H
diff --git a/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewText.cpp b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewText.cpp
new file mode 100644
index 00000000000..948414a4f99
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewText.cpp
@@ -0,0 +1,299 @@
+//===- SourceCoverageViewText.cpp - A text-based code coverage view -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file implements the text-based coverage renderer.
+///
+//===----------------------------------------------------------------------===//
+
+#include "SourceCoverageViewText.h"
+#include "CoverageReport.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+
+Expected<CoveragePrinter::OwnedStream>
+CoveragePrinterText::createViewFile(StringRef Path, bool InToplevel) {
+ return createOutputStream(Path, "txt", InToplevel);
+}
+
+void CoveragePrinterText::closeViewFile(OwnedStream OS) {
+ OS->operator<<('\n');
+}
+
+Error CoveragePrinterText::createIndexFile(
+ ArrayRef<std::string> SourceFiles, const CoverageMapping &Coverage,
+ const CoverageFiltersMatchAll &Filters) {
+ auto OSOrErr = createOutputStream("index", "txt", /*InToplevel=*/true);
+ if (Error E = OSOrErr.takeError())
+ return E;
+ auto OS = std::move(OSOrErr.get());
+ raw_ostream &OSRef = *OS.get();
+
+ CoverageReport Report(Opts, Coverage);
+ Report.renderFileReports(OSRef, SourceFiles, Filters);
+
+ Opts.colored_ostream(OSRef, raw_ostream::CYAN) << "\n"
+ << Opts.getLLVMVersionString();
+
+ return Error::success();
+}
+
+namespace {
+
+static const unsigned LineCoverageColumnWidth = 7;
+static const unsigned LineNumberColumnWidth = 5;
+
+/// Get the width of the leading columns.
+unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) {
+ return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
+ (Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
+}
+
+/// The width of the line that is used to divide between the view and
+/// the subviews.
+unsigned getDividerWidth(const CoverageViewOptions &Opts) {
+ return getCombinedColumnWidth(Opts) + 4;
+}
+
+} // anonymous namespace
+
+void SourceCoverageViewText::renderViewHeader(raw_ostream &) {}
+
+void SourceCoverageViewText::renderViewFooter(raw_ostream &) {}
+
+void SourceCoverageViewText::renderSourceName(raw_ostream &OS, bool WholeFile) {
+ getOptions().colored_ostream(OS, raw_ostream::CYAN) << getSourceName()
+ << ":\n";
+}
+
+void SourceCoverageViewText::renderLinePrefix(raw_ostream &OS,
+ unsigned ViewDepth) {
+ for (unsigned I = 0; I < ViewDepth; ++I)
+ OS << " |";
+}
+
+void SourceCoverageViewText::renderLineSuffix(raw_ostream &, unsigned) {}
+
+void SourceCoverageViewText::renderViewDivider(raw_ostream &OS,
+ unsigned ViewDepth) {
+ assert(ViewDepth != 0 && "Cannot render divider at top level");
+ renderLinePrefix(OS, ViewDepth - 1);
+ OS.indent(2);
+ unsigned Length = getDividerWidth(getOptions());
+ for (unsigned I = 0; I < Length; ++I)
+ OS << '-';
+ OS << '\n';
+}
+
+void SourceCoverageViewText::renderLine(raw_ostream &OS, LineRef L,
+ const LineCoverageStats &LCS,
+ unsigned ExpansionCol,
+ unsigned ViewDepth) {
+ StringRef Line = L.Line;
+ unsigned LineNumber = L.LineNo;
+ auto *WrappedSegment = LCS.getWrappedSegment();
+ CoverageSegmentArray Segments = LCS.getLineSegments();
+
+ Optional<raw_ostream::Colors> Highlight;
+ SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
+
+ // The first segment overlaps from a previous line, so we treat it specially.
+ if (WrappedSegment && !WrappedSegment->IsGapRegion &&
+ WrappedSegment->HasCount && WrappedSegment->Count == 0)
+ Highlight = raw_ostream::RED;
+
+ // Output each segment of the line, possibly highlighted.
+ unsigned Col = 1;
+ for (const auto *S : Segments) {
+ unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1);
+ colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
+ getOptions().Colors && Highlight, /*Bold=*/false,
+ /*BG=*/true)
+ << Line.substr(Col - 1, End - Col);
+ if (getOptions().Debug && Highlight)
+ HighlightedRanges.push_back(std::make_pair(Col, End));
+ Col = End;
+ if ((!S->IsGapRegion || (Highlight && *Highlight == raw_ostream::RED)) &&
+ S->HasCount && S->Count == 0)
+ Highlight = raw_ostream::RED;
+ else if (Col == ExpansionCol)
+ Highlight = raw_ostream::CYAN;
+ else
+ Highlight = None;
+ }
+
+ // Show the rest of the line.
+ colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR,
+ getOptions().Colors && Highlight, /*Bold=*/false, /*BG=*/true)
+ << Line.substr(Col - 1, Line.size() - Col + 1);
+ OS << '\n';
+
+ if (getOptions().Debug) {
+ for (const auto &Range : HighlightedRanges)
+ errs() << "Highlighted line " << LineNumber << ", " << Range.first
+ << " -> " << Range.second << '\n';
+ if (Highlight)
+ errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n";
+ }
+}
+
+void SourceCoverageViewText::renderLineCoverageColumn(
+ raw_ostream &OS, const LineCoverageStats &Line) {
+ if (!Line.isMapped()) {
+ OS.indent(LineCoverageColumnWidth) << '|';
+ return;
+ }
+ std::string C = formatCount(Line.getExecutionCount());
+ OS.indent(LineCoverageColumnWidth - C.size());
+ colored_ostream(OS, raw_ostream::MAGENTA,
+ Line.hasMultipleRegions() && getOptions().Colors)
+ << C;
+ OS << '|';
+}
+
+void SourceCoverageViewText::renderLineNumberColumn(raw_ostream &OS,
+ unsigned LineNo) {
+ SmallString<32> Buffer;
+ raw_svector_ostream BufferOS(Buffer);
+ BufferOS << LineNo;
+ auto Str = BufferOS.str();
+ // Trim and align to the right.
+ Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
+ OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
+}
+
+void SourceCoverageViewText::renderRegionMarkers(raw_ostream &OS,
+ const LineCoverageStats &Line,
+ unsigned ViewDepth) {
+ renderLinePrefix(OS, ViewDepth);
+ OS.indent(getCombinedColumnWidth(getOptions()));
+
+ CoverageSegmentArray Segments = Line.getLineSegments();
+
+ // Just consider the segments which start *and* end on this line.
+ if (Segments.size() > 1)
+ Segments = Segments.drop_back();
+
+ unsigned PrevColumn = 1;
+ for (const auto *S : Segments) {
+ if (!S->IsRegionEntry)
+ continue;
+ if (S->Count == Line.getExecutionCount())
+ continue;
+ // Skip to the new region.
+ if (S->Col > PrevColumn)
+ OS.indent(S->Col - PrevColumn);
+ PrevColumn = S->Col + 1;
+ std::string C = formatCount(S->Count);
+ PrevColumn += C.size();
+ OS << '^' << C;
+
+ if (getOptions().Debug)
+ errs() << "Marker at " << S->Line << ":" << S->Col << " = "
+ << formatCount(S->Count) << "\n";
+ }
+ OS << '\n';
+}
+
+void SourceCoverageViewText::renderExpansionSite(raw_ostream &OS, LineRef L,
+ const LineCoverageStats &LCS,
+ unsigned ExpansionCol,
+ unsigned ViewDepth) {
+ renderLinePrefix(OS, ViewDepth);
+ OS.indent(getCombinedColumnWidth(getOptions()) + (ViewDepth == 0 ? 0 : 1));
+ renderLine(OS, L, LCS, ExpansionCol, ViewDepth);
+}
+
+void SourceCoverageViewText::renderExpansionView(raw_ostream &OS,
+ ExpansionView &ESV,
+ unsigned ViewDepth) {
+ // Render the child subview.
+ if (getOptions().Debug)
+ errs() << "Expansion at line " << ESV.getLine() << ", " << ESV.getStartCol()
+ << " -> " << ESV.getEndCol() << '\n';
+ ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
+ /*ShowTitle=*/false, ViewDepth + 1);
+}
+
+void SourceCoverageViewText::renderBranchView(raw_ostream &OS, BranchView &BRV,
+ unsigned ViewDepth) {
+ // Render the child subview.
+ if (getOptions().Debug)
+ errs() << "Branch at line " << BRV.getLine() << '\n';
+
+ for (const auto &R : BRV.Regions) {
+ double TruePercent = 0.0;
+ double FalsePercent = 0.0;
+ unsigned Total = R.ExecutionCount + R.FalseExecutionCount;
+
+ if (!getOptions().ShowBranchCounts && Total != 0) {
+ TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0;
+ FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0;
+ }
+
+ renderLinePrefix(OS, ViewDepth);
+ OS << " Branch (" << R.LineStart << ":" << R.ColumnStart << "): [";
+
+ if (R.Folded) {
+ OS << "Folded - Ignored]\n";
+ continue;
+ }
+
+ colored_ostream(OS, raw_ostream::RED,
+ getOptions().Colors && !R.ExecutionCount,
+ /*Bold=*/false, /*BG=*/true)
+ << "True";
+
+ if (getOptions().ShowBranchCounts)
+ OS << ": " << formatCount(R.ExecutionCount) << ", ";
+ else
+ OS << ": " << format("%0.2f", TruePercent) << "%, ";
+
+ colored_ostream(OS, raw_ostream::RED,
+ getOptions().Colors && !R.FalseExecutionCount,
+ /*Bold=*/false, /*BG=*/true)
+ << "False";
+
+ if (getOptions().ShowBranchCounts)
+ OS << ": " << formatCount(R.FalseExecutionCount);
+ else
+ OS << ": " << format("%0.2f", FalsePercent) << "%";
+ OS << "]\n";
+ }
+}
+
+void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
+ InstantiationView &ISV,
+ unsigned ViewDepth) {
+ renderLinePrefix(OS, ViewDepth);
+ OS << ' ';
+ if (!ISV.View)
+ getOptions().colored_ostream(OS, raw_ostream::RED)
+ << "Unexecuted instantiation: " << ISV.FunctionName << "\n";
+ else
+ ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true,
+ /*ShowTitle=*/false, ViewDepth);
+}
+
+void SourceCoverageViewText::renderTitle(raw_ostream &OS, StringRef Title) {
+ if (getOptions().hasProjectTitle())
+ getOptions().colored_ostream(OS, raw_ostream::CYAN)
+ << getOptions().ProjectTitle << "\n";
+
+ getOptions().colored_ostream(OS, raw_ostream::CYAN) << Title << "\n";
+
+ if (getOptions().hasCreatedTime())
+ getOptions().colored_ostream(OS, raw_ostream::CYAN)
+ << getOptions().CreatedTimeStr << "\n";
+}
+
+void SourceCoverageViewText::renderTableHeader(raw_ostream &, unsigned,
+ unsigned) {}
diff --git a/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewText.h b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewText.h
new file mode 100644
index 00000000000..b2be06039f9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/SourceCoverageViewText.h
@@ -0,0 +1,91 @@
+//===- SourceCoverageViewText.h - A text-based code coverage view ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file defines the interface to the text-based coverage renderer.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_SOURCECOVERAGEVIEWTEXT_H
+#define LLVM_COV_SOURCECOVERAGEVIEWTEXT_H
+
+#include "SourceCoverageView.h"
+
+namespace llvm {
+
+using namespace coverage;
+
+/// A coverage printer for text output.
+class CoveragePrinterText : public CoveragePrinter {
+public:
+ Expected<OwnedStream> createViewFile(StringRef Path,
+ bool InToplevel) override;
+
+ void closeViewFile(OwnedStream OS) override;
+
+ Error createIndexFile(ArrayRef<std::string> SourceFiles,
+ const CoverageMapping &Coverage,
+ const CoverageFiltersMatchAll &Filters) override;
+
+ CoveragePrinterText(const CoverageViewOptions &Opts)
+ : CoveragePrinter(Opts) {}
+};
+
+/// A code coverage view which supports text-based rendering.
+class SourceCoverageViewText : public SourceCoverageView {
+ void renderViewHeader(raw_ostream &OS) override;
+
+ void renderViewFooter(raw_ostream &OS) override;
+
+ void renderSourceName(raw_ostream &OS, bool WholeFile) override;
+
+ void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderLineSuffix(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderViewDivider(raw_ostream &OS, unsigned ViewDepth) override;
+
+ void renderLine(raw_ostream &OS, LineRef L, const LineCoverageStats &LCS,
+ unsigned ExpansionCol, unsigned ViewDepth) override;
+
+ void renderExpansionSite(raw_ostream &OS, LineRef L,
+ const LineCoverageStats &LCS, unsigned ExpansionCol,
+ unsigned ViewDepth) override;
+
+ void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
+ unsigned ViewDepth) override;
+
+ void renderBranchView(raw_ostream &OS, BranchView &BRV,
+ unsigned ViewDepth) override;
+
+ void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
+ unsigned ViewDepth) override;
+
+ void renderLineCoverageColumn(raw_ostream &OS,
+ const LineCoverageStats &Line) override;
+
+ void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) override;
+
+ void renderRegionMarkers(raw_ostream &OS, const LineCoverageStats &Line,
+ unsigned ViewDepth) override;
+
+ void renderTitle(raw_ostream &OS, StringRef Title) override;
+
+ void renderTableHeader(raw_ostream &OS, unsigned FirstUncoveredLineNo,
+ unsigned IndentLevel) override;
+
+public:
+ SourceCoverageViewText(StringRef SourceName, const MemoryBuffer &File,
+ const CoverageViewOptions &Options,
+ CoverageData &&CoverageInfo)
+ : SourceCoverageView(SourceName, File, Options, std::move(CoverageInfo)) {
+ }
+};
+
+} // namespace llvm
+
+#endif // LLVM_COV_SOURCECOVERAGEVIEWTEXT_H
diff --git a/contrib/libs/llvm14/tools/llvm-cov/TestingSupport.cpp b/contrib/libs/llvm14/tools/llvm-cov/TestingSupport.cpp
new file mode 100644
index 00000000000..9c6b25f2f58
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/TestingSupport.cpp
@@ -0,0 +1,123 @@
+//===- TestingSupport.cpp - Convert objects files into test files --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/ProfileData/InstrProf.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/raw_ostream.h"
+#include <functional>
+#include <system_error>
+
+using namespace llvm;
+using namespace object;
+
+int convertForTestingMain(int argc, const char *argv[]) {
+ cl::opt<std::string> InputSourceFile(cl::Positional, cl::Required,
+ cl::desc("<Source file>"));
+
+ cl::opt<std::string> OutputFilename(
+ "o", cl::Required,
+ cl::desc(
+ "File with the profile data obtained after an instrumented run"));
+
+ cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
+
+ auto ObjErr = llvm::object::ObjectFile::createObjectFile(InputSourceFile);
+ if (!ObjErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(ObjErr.takeError(), OS);
+ OS.flush();
+ errs() << "error: " << Buf;
+ return 1;
+ }
+ ObjectFile *OF = ObjErr.get().getBinary();
+ auto BytesInAddress = OF->getBytesInAddress();
+ if (BytesInAddress != 8) {
+ errs() << "error: 64 bit binary expected\n";
+ return 1;
+ }
+
+ // Look for the sections that we are interested in.
+ int FoundSectionCount = 0;
+ SectionRef ProfileNames, CoverageMapping, CoverageRecords;
+ auto ObjFormat = OF->getTripleObjectFormat();
+ for (const auto &Section : OF->sections()) {
+ StringRef Name;
+ if (Expected<StringRef> NameOrErr = Section.getName()) {
+ Name = *NameOrErr;
+ } else {
+ consumeError(NameOrErr.takeError());
+ return 1;
+ }
+
+ if (Name == llvm::getInstrProfSectionName(IPSK_name, ObjFormat,
+ /*AddSegmentInfo=*/false)) {
+ ProfileNames = Section;
+ } else if (Name == llvm::getInstrProfSectionName(
+ IPSK_covmap, ObjFormat, /*AddSegmentInfo=*/false)) {
+ CoverageMapping = Section;
+ } else if (Name == llvm::getInstrProfSectionName(
+ IPSK_covfun, ObjFormat, /*AddSegmentInfo=*/false)) {
+ CoverageRecords = Section;
+ } else
+ continue;
+ ++FoundSectionCount;
+ }
+ if (FoundSectionCount != 3)
+ return 1;
+
+ // Get the contents of the given sections.
+ uint64_t ProfileNamesAddress = ProfileNames.getAddress();
+ StringRef CoverageMappingData;
+ StringRef CoverageRecordsData;
+ StringRef ProfileNamesData;
+ if (Expected<StringRef> E = CoverageMapping.getContents())
+ CoverageMappingData = *E;
+ else {
+ consumeError(E.takeError());
+ return 1;
+ }
+ if (Expected<StringRef> E = CoverageRecords.getContents())
+ CoverageRecordsData = *E;
+ else {
+ consumeError(E.takeError());
+ return 1;
+ }
+ if (Expected<StringRef> E = ProfileNames.getContents())
+ ProfileNamesData = *E;
+ else {
+ consumeError(E.takeError());
+ return 1;
+ }
+
+ int FD;
+ if (auto Err = sys::fs::openFileForWrite(OutputFilename, FD)) {
+ errs() << "error: " << Err.message() << "\n";
+ return 1;
+ }
+
+ raw_fd_ostream OS(FD, true);
+ OS << "llvmcovmtestdata";
+ encodeULEB128(ProfileNamesData.size(), OS);
+ encodeULEB128(ProfileNamesAddress, OS);
+ OS << ProfileNamesData;
+ // Coverage mapping data is expected to have an alignment of 8.
+ for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad)
+ OS.write(uint8_t(0));
+ OS << CoverageMappingData;
+ // Coverage records data is expected to have an alignment of 8.
+ for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad)
+ OS.write(uint8_t(0));
+ OS << CoverageRecordsData;
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cov/gcov.cpp b/contrib/libs/llvm14/tools/llvm-cov/gcov.cpp
new file mode 100644
index 00000000000..9a1ebebc87f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/gcov.cpp
@@ -0,0 +1,180 @@
+//===- gcov.cpp - GCOV compatible LLVM coverage tool ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// llvm-cov is a command line tools to analyze and report coverage information.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ProfileData/GCOV.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include <system_error>
+using namespace llvm;
+
+static void reportCoverage(StringRef SourceFile, StringRef ObjectDir,
+ const std::string &InputGCNO,
+ const std::string &InputGCDA, bool DumpGCOV,
+ const GCOV::Options &Options) {
+ SmallString<128> CoverageFileStem(ObjectDir);
+ if (CoverageFileStem.empty()) {
+ // If no directory was specified with -o, look next to the source file.
+ CoverageFileStem = sys::path::parent_path(SourceFile);
+ sys::path::append(CoverageFileStem, sys::path::stem(SourceFile));
+ } else if (sys::fs::is_directory(ObjectDir))
+ // A directory name was given. Use it and the source file name.
+ sys::path::append(CoverageFileStem, sys::path::stem(SourceFile));
+ else
+ // A file was given. Ignore the source file and look next to this file.
+ sys::path::replace_extension(CoverageFileStem, "");
+
+ std::string GCNO = InputGCNO.empty()
+ ? std::string(CoverageFileStem.str()) + ".gcno"
+ : InputGCNO;
+ std::string GCDA = InputGCDA.empty()
+ ? std::string(CoverageFileStem.str()) + ".gcda"
+ : InputGCDA;
+ GCOVFile GF;
+
+ // Open .gcda and .gcda without requiring a NUL terminator. The concurrent
+ // modification may nullify the NUL terminator condition.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> GCNO_Buff =
+ MemoryBuffer::getFileOrSTDIN(GCNO, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
+ if (std::error_code EC = GCNO_Buff.getError()) {
+ errs() << GCNO << ": " << EC.message() << "\n";
+ return;
+ }
+ GCOVBuffer GCNO_GB(GCNO_Buff.get().get());
+ if (!GF.readGCNO(GCNO_GB)) {
+ errs() << "Invalid .gcno File!\n";
+ return;
+ }
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> GCDA_Buff =
+ MemoryBuffer::getFileOrSTDIN(GCDA, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
+ if (std::error_code EC = GCDA_Buff.getError()) {
+ if (EC != errc::no_such_file_or_directory) {
+ errs() << GCDA << ": " << EC.message() << "\n";
+ return;
+ }
+ // Clear the filename to make it clear we didn't read anything.
+ GCDA = "-";
+ } else {
+ GCOVBuffer gcda_buf(GCDA_Buff.get().get());
+ if (!gcda_buf.readGCDAFormat())
+ errs() << GCDA << ":not a gcov data file\n";
+ else if (!GF.readGCDA(gcda_buf))
+ errs() << "Invalid .gcda File!\n";
+ }
+
+ if (DumpGCOV)
+ GF.print(errs());
+
+ gcovOneInput(Options, SourceFile, GCNO, GCDA, GF);
+}
+
+int gcovMain(int argc, const char *argv[]) {
+ cl::list<std::string> SourceFiles(cl::Positional, cl::OneOrMore,
+ cl::desc("SOURCEFILE"));
+
+ cl::opt<bool> AllBlocks("a", cl::Grouping, cl::init(false),
+ cl::desc("Display all basic blocks"));
+ cl::alias AllBlocksA("all-blocks", cl::aliasopt(AllBlocks));
+
+ cl::opt<bool> BranchProb("b", cl::Grouping, cl::init(false),
+ cl::desc("Display branch probabilities"));
+ cl::alias BranchProbA("branch-probabilities", cl::aliasopt(BranchProb));
+
+ cl::opt<bool> BranchCount("c", cl::Grouping, cl::init(false),
+ cl::desc("Display branch counts instead "
+ "of percentages (requires -b)"));
+ cl::alias BranchCountA("branch-counts", cl::aliasopt(BranchCount));
+
+ cl::opt<bool> LongNames("l", cl::Grouping, cl::init(false),
+ cl::desc("Prefix filenames with the main file"));
+ cl::alias LongNamesA("long-file-names", cl::aliasopt(LongNames));
+
+ cl::opt<bool> FuncSummary("f", cl::Grouping, cl::init(false),
+ cl::desc("Show coverage for each function"));
+ cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary));
+
+ // Supported by gcov 4.9~8. gcov 9 (GCC r265587) removed --intermediate-format
+ // and -i was changed to mean --json-format. We consider this format still
+ // useful and support -i.
+ cl::opt<bool> Intermediate(
+ "intermediate-format", cl::init(false),
+ cl::desc("Output .gcov in intermediate text format"));
+ cl::alias IntermediateA("i", cl::desc("Alias for --intermediate-format"),
+ cl::Grouping, cl::NotHidden,
+ cl::aliasopt(Intermediate));
+
+ cl::opt<bool> Demangle("demangled-names", cl::init(false),
+ cl::desc("Demangle function names"));
+ cl::alias DemangleA("m", cl::desc("Alias for --demangled-names"),
+ cl::Grouping, cl::NotHidden, cl::aliasopt(Demangle));
+
+ cl::opt<bool> NoOutput("n", cl::Grouping, cl::init(false),
+ cl::desc("Do not output any .gcov files"));
+ cl::alias NoOutputA("no-output", cl::aliasopt(NoOutput));
+
+ cl::opt<std::string> ObjectDir(
+ "o", cl::value_desc("DIR|FILE"), cl::init(""),
+ cl::desc("Find objects in DIR or based on FILE's path"));
+ cl::alias ObjectDirA("object-directory", cl::aliasopt(ObjectDir));
+ cl::alias ObjectDirB("object-file", cl::aliasopt(ObjectDir));
+
+ cl::opt<bool> PreservePaths("p", cl::Grouping, cl::init(false),
+ cl::desc("Preserve path components"));
+ cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths));
+
+ cl::opt<bool> RelativeOnly(
+ "r", cl::Grouping,
+ cl::desc("Only dump files with relative paths or absolute paths with the "
+ "prefix specified by -s"));
+ cl::alias RelativeOnlyA("relative-only", cl::aliasopt(RelativeOnly));
+ cl::opt<std::string> SourcePrefix("s", cl::desc("Source prefix to elide"));
+ cl::alias SourcePrefixA("source-prefix", cl::aliasopt(SourcePrefix));
+
+ cl::opt<bool> UseStdout("t", cl::Grouping, cl::init(false),
+ cl::desc("Print to stdout"));
+ cl::alias UseStdoutA("stdout", cl::aliasopt(UseStdout));
+
+ cl::opt<bool> UncondBranch("u", cl::Grouping, cl::init(false),
+ cl::desc("Display unconditional branch info "
+ "(requires -b)"));
+ cl::alias UncondBranchA("unconditional-branches", cl::aliasopt(UncondBranch));
+
+ cl::opt<bool> HashFilenames("x", cl::Grouping, cl::init(false),
+ cl::desc("Hash long pathnames"));
+ cl::alias HashFilenamesA("hash-filenames", cl::aliasopt(HashFilenames));
+
+
+ cl::OptionCategory DebugCat("Internal and debugging options");
+ cl::opt<bool> DumpGCOV("dump", cl::init(false), cl::cat(DebugCat),
+ cl::desc("Dump the gcov file to stderr"));
+ cl::opt<std::string> InputGCNO("gcno", cl::cat(DebugCat), cl::init(""),
+ cl::desc("Override inferred gcno file"));
+ cl::opt<std::string> InputGCDA("gcda", cl::cat(DebugCat), cl::init(""),
+ cl::desc("Override inferred gcda file"));
+
+ cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
+
+ GCOV::Options Options(AllBlocks, BranchProb, BranchCount, FuncSummary,
+ PreservePaths, UncondBranch, Intermediate, LongNames,
+ Demangle, NoOutput, RelativeOnly, UseStdout,
+ HashFilenames, SourcePrefix);
+
+ for (const auto &SourceFile : SourceFiles)
+ reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV,
+ Options);
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cov/llvm-cov.cpp b/contrib/libs/llvm14/tools/llvm-cov/llvm-cov.cpp
new file mode 100644
index 00000000000..0e320c0965f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/llvm-cov.cpp
@@ -0,0 +1,95 @@
+//===- llvm-cov.cpp - LLVM coverage tool ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// llvm-cov is a command line tools to analyze and report coverage information.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+
+using namespace llvm;
+
+/// The main entry point for the 'show' subcommand.
+int showMain(int argc, const char *argv[]);
+
+/// The main entry point for the 'report' subcommand.
+int reportMain(int argc, const char *argv[]);
+
+/// The main entry point for the 'export' subcommand.
+int exportMain(int argc, const char *argv[]);
+
+/// The main entry point for the 'convert-for-testing' subcommand.
+int convertForTestingMain(int argc, const char *argv[]);
+
+/// The main entry point for the gcov compatible coverage tool.
+int gcovMain(int argc, const char *argv[]);
+
+/// Top level help.
+static int helpMain(int argc, const char *argv[]) {
+ errs() << "Usage: llvm-cov {export|gcov|report|show} [OPTION]...\n\n"
+ << "Shows code coverage information.\n\n"
+ << "Subcommands:\n"
+ << " export: Export instrprof file to structured format.\n"
+ << " gcov: Work with the gcov format.\n"
+ << " report: Summarize instrprof style coverage information.\n"
+ << " show: Annotate source files using instrprof style coverage.\n";
+
+ return 0;
+}
+
+/// Top level version information.
+static int versionMain(int argc, const char *argv[]) {
+ cl::PrintVersionMessage();
+ return 0;
+}
+
+int main(int argc, const char **argv) {
+ InitLLVM X(argc, argv);
+
+ // If argv[0] is or ends with 'gcov', always be gcov compatible
+ if (sys::path::stem(argv[0]).endswith_insensitive("gcov"))
+ return gcovMain(argc, argv);
+
+ // Check if we are invoking a specific tool command.
+ if (argc > 1) {
+ typedef int (*MainFunction)(int, const char *[]);
+ MainFunction Func = StringSwitch<MainFunction>(argv[1])
+ .Case("convert-for-testing", convertForTestingMain)
+ .Case("export", exportMain)
+ .Case("gcov", gcovMain)
+ .Case("report", reportMain)
+ .Case("show", showMain)
+ .Cases("-h", "-help", "--help", helpMain)
+ .Cases("-version", "--version", versionMain)
+ .Default(nullptr);
+
+ if (Func) {
+ std::string Invocation = std::string(argv[0]) + " " + argv[1];
+ argv[1] = Invocation.c_str();
+ return Func(argc - 1, argv + 1);
+ }
+ }
+
+ if (argc > 1) {
+ if (sys::Process::StandardErrHasColors())
+ errs().changeColor(raw_ostream::RED);
+ errs() << "Unrecognized command: " << argv[1] << ".\n\n";
+ if (sys::Process::StandardErrHasColors())
+ errs().resetColor();
+ }
+ helpMain(argc, argv);
+ return 1;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cov/ya.make b/contrib/libs/llvm14/tools/llvm-cov/ya.make
new file mode 100644
index 00000000000..f5b0b9ebcf8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cov/ya.make
@@ -0,0 +1,50 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/ProfileData/Coverage
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-cov
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ CodeCoverage.cpp
+ CoverageExporterJson.cpp
+ CoverageExporterLcov.cpp
+ CoverageFilters.cpp
+ CoverageReport.cpp
+ CoverageSummaryInfo.cpp
+ SourceCoverageView.cpp
+ SourceCoverageViewHTML.cpp
+ SourceCoverageViewText.cpp
+ TestingSupport.cpp
+ gcov.cpp
+ llvm-cov.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-cvtres/Opts.td b/contrib/libs/llvm14/tools/llvm-cvtres/Opts.td
new file mode 100644
index 00000000000..21d583787cd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cvtres/Opts.td
@@ -0,0 +1,19 @@
+include "llvm/Option/OptParser.td"
+
+// All the switches can be preceded by either '/' or '-'.
+
+def DEFINE : Joined<["/", "-"], "DEFINE:">, HelpText<"Not implemented">, MetaVarName<"symbol">;
+def FOLDDUPS : Flag<["/", "-"], "FOLDDUPS:">, HelpText<"Not implemented">;
+def MACHINE : Joined<["/", "-"], "MACHINE:">, HelpText<"Machine architecture">, MetaVarName<"{ARM|ARM64|EBC|IA64|X64|X86}">;
+def NOLOGO : Flag<["/", "-"], "NOLOGO">, HelpText<"Not implemented">;
+def OUT : Joined<["/", "-"], "OUT:">, HelpText<"Output file">, MetaVarName<"filename">;
+def READONLY : Flag<["/", "-"], "READONLY">, HelpText<"Not implemented">;
+def VERBOSE : Flag<["/", "-"], "VERBOSE">, HelpText<"Use verbose output">;
+def HELP : Flag<["/", "-"], "HELP">, HelpText<"Display available options">;
+def H : Flag<["/", "-"], "H">, Alias<HELP>;
+def HELP_Q : Flag<["/?", "-?"], "">, Alias<HELP>;
+
+// Extensions.
+
+def TIMESTAMP : Joined<["/", "-"], "TIMESTAMP:">,
+ HelpText<"Timestamp for coff header, defaults to current time">;
diff --git a/contrib/libs/llvm14/tools/llvm-cvtres/llvm-cvtres.cpp b/contrib/libs/llvm14/tools/llvm-cvtres/llvm-cvtres.cpp
new file mode 100644
index 00000000000..086b337c425
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cvtres/llvm-cvtres.cpp
@@ -0,0 +1,234 @@
+//===- llvm-cvtres.cpp - Serialize .res files into .obj ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Serialize .res files into .obj files. This is intended to be a
+// platform-independent port of Microsoft's cvtres.exe.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/WindowsMachineFlag.h"
+#include "llvm/Object/WindowsResource.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/BinaryStreamError.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <system_error>
+
+using namespace llvm;
+using namespace object;
+
+namespace {
+
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class CvtResOptTable : public opt::OptTable {
+public:
+ CvtResOptTable() : OptTable(InfoTable, true) {}
+};
+}
+
+[[noreturn]] static void reportError(Twine Msg) {
+ errs() << Msg;
+ exit(1);
+}
+
+static void reportError(StringRef Input, std::error_code EC) {
+ reportError(Twine(Input) + ": " + EC.message() + ".\n");
+}
+
+static void error(StringRef Input, Error EC) {
+ if (!EC)
+ return;
+ handleAllErrors(std::move(EC), [&](const ErrorInfoBase &EI) {
+ reportError(Twine(Input) + ": " + EI.message() + ".\n");
+ });
+}
+
+static void error(Error EC) {
+ if (!EC)
+ return;
+ handleAllErrors(std::move(EC),
+ [&](const ErrorInfoBase &EI) { reportError(EI.message()); });
+}
+
+static uint32_t getTime() {
+ std::time_t Now = time(nullptr);
+ if (Now < 0 || !isUInt<32>(Now))
+ return UINT32_MAX;
+ return static_cast<uint32_t>(Now);
+}
+
+template <typename T> T error(Expected<T> EC) {
+ if (!EC)
+ error(EC.takeError());
+ return std::move(EC.get());
+}
+
+template <typename T> T error(StringRef Input, Expected<T> EC) {
+ if (!EC)
+ error(Input, EC.takeError());
+ return std::move(EC.get());
+}
+
+template <typename T> T error(StringRef Input, ErrorOr<T> &&EC) {
+ return error(Input, errorOrToExpected(std::move(EC)));
+}
+
+int main(int Argc, const char **Argv) {
+ InitLLVM X(Argc, Argv);
+
+ CvtResOptTable T;
+ unsigned MAI, MAC;
+ ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1);
+ opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
+
+ if (InputArgs.hasArg(OPT_HELP)) {
+ T.printHelp(outs(), "llvm-cvtres [options] file...", "Resource Converter");
+ return 0;
+ }
+
+ bool Verbose = InputArgs.hasArg(OPT_VERBOSE);
+
+ COFF::MachineTypes MachineType;
+
+ if (opt::Arg *Arg = InputArgs.getLastArg(OPT_MACHINE)) {
+ MachineType = getMachineType(Arg->getValue());
+ if (MachineType == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
+ reportError(Twine("Unsupported machine architecture ") + Arg->getValue() +
+ "\n");
+ }
+ } else {
+ if (Verbose)
+ outs() << "Machine architecture not specified; assumed X64.\n";
+ MachineType = COFF::IMAGE_FILE_MACHINE_AMD64;
+ }
+
+ std::vector<std::string> InputFiles = InputArgs.getAllArgValues(OPT_INPUT);
+
+ if (InputFiles.size() == 0) {
+ reportError("No input file specified.\n");
+ }
+
+ SmallString<128> OutputFile;
+
+ if (opt::Arg *Arg = InputArgs.getLastArg(OPT_OUT)) {
+ OutputFile = Arg->getValue();
+ } else {
+ OutputFile = sys::path::filename(StringRef(InputFiles[0]));
+ sys::path::replace_extension(OutputFile, ".obj");
+ }
+
+ uint32_t DateTimeStamp;
+ if (llvm::opt::Arg *Arg = InputArgs.getLastArg(OPT_TIMESTAMP)) {
+ StringRef Value(Arg->getValue());
+ if (Value.getAsInteger(0, DateTimeStamp))
+ reportError(Twine("invalid timestamp: ") + Value +
+ ". Expected 32-bit integer\n");
+ } else {
+ DateTimeStamp = getTime();
+ }
+
+ if (Verbose)
+ outs() << "Machine: " << machineToStr(MachineType) << '\n';
+
+ WindowsResourceParser Parser;
+
+ for (const auto &File : InputFiles) {
+ std::unique_ptr<MemoryBuffer> Buffer = error(
+ File, MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false));
+ file_magic Type = identify_magic(Buffer->getMemBufferRef().getBuffer());
+ if (Type != file_magic::windows_resource)
+ reportError(File + ": unrecognized file format.\n");
+ std::unique_ptr<WindowsResource> Binary = error(
+ File,
+ WindowsResource::createWindowsResource(Buffer->getMemBufferRef()));
+
+ WindowsResource *RF = Binary.get();
+
+ if (Verbose) {
+ int EntryNumber = 0;
+ ResourceEntryRef Entry = error(RF->getHeadEntry());
+ bool End = false;
+ while (!End) {
+ error(Entry.moveNext(End));
+ EntryNumber++;
+ }
+ outs() << "Number of resources: " << EntryNumber << "\n";
+ }
+
+ std::vector<std::string> Duplicates;
+ error(Parser.parse(RF, Duplicates));
+ for (const auto& DupeDiag : Duplicates)
+ reportError(DupeDiag);
+ }
+
+ if (Verbose) {
+ Parser.printTree(outs());
+ }
+
+ std::unique_ptr<MemoryBuffer> OutputBuffer =
+ error(llvm::object::writeWindowsResourceCOFF(MachineType, Parser,
+ DateTimeStamp));
+ auto FileOrErr =
+ FileOutputBuffer::create(OutputFile, OutputBuffer->getBufferSize());
+ if (!FileOrErr)
+ reportError(OutputFile, errorToErrorCode(FileOrErr.takeError()));
+ std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr);
+ std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(),
+ FileBuffer->getBufferStart());
+ error(FileBuffer->commit());
+
+ if (Verbose) {
+ std::unique_ptr<MemoryBuffer> Buffer =
+ error(OutputFile,
+ MemoryBuffer::getFileOrSTDIN(OutputFile, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false));
+
+ ScopedPrinter W(errs());
+ W.printBinaryBlock("Output File Raw Data",
+ Buffer->getMemBufferRef().getBuffer());
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cvtres/ya.make b/contrib/libs/llvm14/tools/llvm-cvtres/ya.make
new file mode 100644
index 00000000000..55a962f3499
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cvtres/ya.make
@@ -0,0 +1,39 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-cvtres
+ contrib/libs/llvm14/tools/llvm-cvtres
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-cvtres.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-cxxdump/Error.cpp b/contrib/libs/llvm14/tools/llvm-cxxdump/Error.cpp
new file mode 100644
index 00000000000..053d0e0764b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cxxdump/Error.cpp
@@ -0,0 +1,46 @@
+//===- Error.cpp - system_error extensions for llvm-cxxdump -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines a new error_category for the llvm-cxxdump tool.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <string>
+
+using namespace llvm;
+
+namespace {
+// FIXME: This class is only here to support the transition to llvm::Error. It
+// will be removed once this transition is complete. Clients should prefer to
+// deal with the Error value directly, rather than converting to error_code.
+class cxxdump_error_category : public std::error_category {
+public:
+ const char *name() const noexcept override { return "llvm.cxxdump"; }
+ std::string message(int ev) const override {
+ switch (static_cast<cxxdump_error>(ev)) {
+ case cxxdump_error::success:
+ return "Success";
+ case cxxdump_error::file_not_found:
+ return "No such file.";
+ case cxxdump_error::unrecognized_file_format:
+ return "Unrecognized file type.";
+ }
+ llvm_unreachable(
+ "An enumerator of cxxdump_error does not have a message defined.");
+ }
+};
+} // namespace
+
+namespace llvm {
+const std::error_category &cxxdump_category() {
+ static cxxdump_error_category o;
+ return o;
+}
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-cxxdump/Error.h b/contrib/libs/llvm14/tools/llvm-cxxdump/Error.h
new file mode 100644
index 00000000000..439902fa380
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cxxdump/Error.h
@@ -0,0 +1,38 @@
+//===- Error.h - system_error extensions for llvm-cxxdump -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This declares a new error_category for the llvm-cxxdump tool.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_CXXDUMP_ERROR_H
+#define LLVM_TOOLS_LLVM_CXXDUMP_ERROR_H
+
+#include <system_error>
+
+namespace llvm {
+const std::error_category &cxxdump_category();
+
+enum class cxxdump_error {
+ success = 0,
+ file_not_found,
+ unrecognized_file_format,
+};
+
+inline std::error_code make_error_code(cxxdump_error e) {
+ return std::error_code(static_cast<int>(e), cxxdump_category());
+}
+
+} // namespace llvm
+
+namespace std {
+template <>
+struct is_error_code_enum<llvm::cxxdump_error> : std::true_type {};
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-cxxdump/llvm-cxxdump.cpp b/contrib/libs/llvm14/tools/llvm-cxxdump/llvm-cxxdump.cpp
new file mode 100644
index 00000000000..1430674dbad
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cxxdump/llvm-cxxdump.cpp
@@ -0,0 +1,563 @@
+//===- llvm-cxxdump.cpp - Dump C++ data in an Object File -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Dumps C++ data resident in object files and archives.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-cxxdump.h"
+#include "Error.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/SymbolSize.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <map>
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::support;
+
+namespace opts {
+cl::OptionCategory CXXDumpCategory("CXX Dump Options");
+cl::list<std::string> InputFilenames(cl::Positional,
+ cl::desc("<input object files>"),
+ cl::ZeroOrMore, cl::cat(CXXDumpCategory));
+} // namespace opts
+
+namespace llvm {
+
+static void error(std::error_code EC) {
+ if (!EC)
+ return;
+ WithColor::error(outs(), "") << "reading file: " << EC.message() << ".\n";
+ outs().flush();
+ exit(1);
+}
+
+[[noreturn]] static void error(Error Err) {
+ logAllUnhandledErrors(std::move(Err), WithColor::error(outs()),
+ "reading file: ");
+ outs().flush();
+ exit(1);
+}
+
+template <typename T>
+T unwrapOrError(Expected<T> EO) {
+ if (!EO)
+ error(EO.takeError());
+ return std::move(*EO);
+}
+
+} // namespace llvm
+
+static void reportError(StringRef Input, StringRef Message) {
+ if (Input == "-")
+ Input = "<stdin>";
+ WithColor::error(errs(), Input) << Message << "\n";
+ errs().flush();
+ exit(1);
+}
+
+static void reportError(StringRef Input, std::error_code EC) {
+ reportError(Input, EC.message());
+}
+
+static std::map<SectionRef, SmallVector<SectionRef, 1>> SectionRelocMap;
+
+static void collectRelocatedSymbols(const ObjectFile *Obj,
+ const SectionRef &Sec, uint64_t SecAddress,
+ uint64_t SymAddress, uint64_t SymSize,
+ StringRef *I, StringRef *E) {
+ uint64_t SymOffset = SymAddress - SecAddress;
+ uint64_t SymEnd = SymOffset + SymSize;
+ for (const SectionRef &SR : SectionRelocMap[Sec]) {
+ for (const object::RelocationRef &Reloc : SR.relocations()) {
+ if (I == E)
+ break;
+ const object::symbol_iterator RelocSymI = Reloc.getSymbol();
+ if (RelocSymI == Obj->symbol_end())
+ continue;
+ Expected<StringRef> RelocSymName = RelocSymI->getName();
+ error(errorToErrorCode(RelocSymName.takeError()));
+ uint64_t Offset = Reloc.getOffset();
+ if (Offset >= SymOffset && Offset < SymEnd) {
+ *I = *RelocSymName;
+ ++I;
+ }
+ }
+ }
+}
+
+static void collectRelocationOffsets(
+ const ObjectFile *Obj, const SectionRef &Sec, uint64_t SecAddress,
+ uint64_t SymAddress, uint64_t SymSize, StringRef SymName,
+ std::map<std::pair<StringRef, uint64_t>, StringRef> &Collection) {
+ uint64_t SymOffset = SymAddress - SecAddress;
+ uint64_t SymEnd = SymOffset + SymSize;
+ for (const SectionRef &SR : SectionRelocMap[Sec]) {
+ for (const object::RelocationRef &Reloc : SR.relocations()) {
+ const object::symbol_iterator RelocSymI = Reloc.getSymbol();
+ if (RelocSymI == Obj->symbol_end())
+ continue;
+ Expected<StringRef> RelocSymName = RelocSymI->getName();
+ error(errorToErrorCode(RelocSymName.takeError()));
+ uint64_t Offset = Reloc.getOffset();
+ if (Offset >= SymOffset && Offset < SymEnd)
+ Collection[std::make_pair(SymName, Offset - SymOffset)] = *RelocSymName;
+ }
+ }
+}
+
+static void dumpCXXData(const ObjectFile *Obj) {
+ struct CompleteObjectLocator {
+ StringRef Symbols[2];
+ ArrayRef<little32_t> Data;
+ };
+ struct ClassHierarchyDescriptor {
+ StringRef Symbols[1];
+ ArrayRef<little32_t> Data;
+ };
+ struct BaseClassDescriptor {
+ StringRef Symbols[2];
+ ArrayRef<little32_t> Data;
+ };
+ struct TypeDescriptor {
+ StringRef Symbols[1];
+ uint64_t AlwaysZero;
+ StringRef MangledName;
+ };
+ struct ThrowInfo {
+ uint32_t Flags;
+ };
+ struct CatchableTypeArray {
+ uint32_t NumEntries;
+ };
+ struct CatchableType {
+ uint32_t Flags;
+ uint32_t NonVirtualBaseAdjustmentOffset;
+ int32_t VirtualBasePointerOffset;
+ uint32_t VirtualBaseAdjustmentOffset;
+ uint32_t Size;
+ StringRef Symbols[2];
+ };
+ std::map<std::pair<StringRef, uint64_t>, StringRef> VFTableEntries;
+ std::map<std::pair<StringRef, uint64_t>, StringRef> TIEntries;
+ std::map<std::pair<StringRef, uint64_t>, StringRef> CTAEntries;
+ std::map<StringRef, ArrayRef<little32_t>> VBTables;
+ std::map<StringRef, CompleteObjectLocator> COLs;
+ std::map<StringRef, ClassHierarchyDescriptor> CHDs;
+ std::map<std::pair<StringRef, uint64_t>, StringRef> BCAEntries;
+ std::map<StringRef, BaseClassDescriptor> BCDs;
+ std::map<StringRef, TypeDescriptor> TDs;
+ std::map<StringRef, ThrowInfo> TIs;
+ std::map<StringRef, CatchableTypeArray> CTAs;
+ std::map<StringRef, CatchableType> CTs;
+
+ std::map<std::pair<StringRef, uint64_t>, StringRef> VTableSymEntries;
+ std::map<std::pair<StringRef, uint64_t>, int64_t> VTableDataEntries;
+ std::map<std::pair<StringRef, uint64_t>, StringRef> VTTEntries;
+ std::map<StringRef, StringRef> TINames;
+
+ SectionRelocMap.clear();
+ for (const SectionRef &Section : Obj->sections()) {
+ Expected<section_iterator> ErrOrSec = Section.getRelocatedSection();
+ if (!ErrOrSec)
+ error(ErrOrSec.takeError());
+
+ section_iterator Sec2 = *ErrOrSec;
+ if (Sec2 != Obj->section_end())
+ SectionRelocMap[*Sec2].push_back(Section);
+ }
+
+ uint8_t BytesInAddress = Obj->getBytesInAddress();
+
+ std::vector<std::pair<SymbolRef, uint64_t>> SymAddr =
+ object::computeSymbolSizes(*Obj);
+
+ for (auto &P : SymAddr) {
+ object::SymbolRef Sym = P.first;
+ uint64_t SymSize = P.second;
+ Expected<StringRef> SymNameOrErr = Sym.getName();
+ error(errorToErrorCode(SymNameOrErr.takeError()));
+ StringRef SymName = *SymNameOrErr;
+ Expected<object::section_iterator> SecIOrErr = Sym.getSection();
+ error(errorToErrorCode(SecIOrErr.takeError()));
+ object::section_iterator SecI = *SecIOrErr;
+ // Skip external symbols.
+ if (SecI == Obj->section_end())
+ continue;
+ const SectionRef &Sec = *SecI;
+ // Skip virtual or BSS sections.
+ if (Sec.isBSS() || Sec.isVirtual())
+ continue;
+ StringRef SecContents = unwrapOrError(Sec.getContents());
+ Expected<uint64_t> SymAddressOrErr = Sym.getAddress();
+ error(errorToErrorCode(SymAddressOrErr.takeError()));
+ uint64_t SymAddress = *SymAddressOrErr;
+ uint64_t SecAddress = Sec.getAddress();
+ uint64_t SecSize = Sec.getSize();
+ uint64_t SymOffset = SymAddress - SecAddress;
+ StringRef SymContents = SecContents.substr(SymOffset, SymSize);
+
+ // VFTables in the MS-ABI start with '??_7' and are contained within their
+ // own COMDAT section. We then determine the contents of the VFTable by
+ // looking at each relocation in the section.
+ if (SymName.startswith("??_7")) {
+ // Each relocation either names a virtual method or a thunk. We note the
+ // offset into the section and the symbol used for the relocation.
+ collectRelocationOffsets(Obj, Sec, SecAddress, SecAddress, SecSize,
+ SymName, VFTableEntries);
+ }
+ // VBTables in the MS-ABI start with '??_8' and are filled with 32-bit
+ // offsets of virtual bases.
+ else if (SymName.startswith("??_8")) {
+ ArrayRef<little32_t> VBTableData(
+ reinterpret_cast<const little32_t *>(SymContents.data()),
+ SymContents.size() / sizeof(little32_t));
+ VBTables[SymName] = VBTableData;
+ }
+ // Complete object locators in the MS-ABI start with '??_R4'
+ else if (SymName.startswith("??_R4")) {
+ CompleteObjectLocator COL;
+ COL.Data = makeArrayRef(
+ reinterpret_cast<const little32_t *>(SymContents.data()), 3);
+ StringRef *I = std::begin(COL.Symbols), *E = std::end(COL.Symbols);
+ collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);
+ COLs[SymName] = COL;
+ }
+ // Class hierarchy descriptors in the MS-ABI start with '??_R3'
+ else if (SymName.startswith("??_R3")) {
+ ClassHierarchyDescriptor CHD;
+ CHD.Data = makeArrayRef(
+ reinterpret_cast<const little32_t *>(SymContents.data()), 3);
+ StringRef *I = std::begin(CHD.Symbols), *E = std::end(CHD.Symbols);
+ collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);
+ CHDs[SymName] = CHD;
+ }
+ // Class hierarchy descriptors in the MS-ABI start with '??_R2'
+ else if (SymName.startswith("??_R2")) {
+ // Each relocation names a base class descriptor. We note the offset into
+ // the section and the symbol used for the relocation.
+ collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize,
+ SymName, BCAEntries);
+ }
+ // Base class descriptors in the MS-ABI start with '??_R1'
+ else if (SymName.startswith("??_R1")) {
+ BaseClassDescriptor BCD;
+ BCD.Data = makeArrayRef(
+ reinterpret_cast<const little32_t *>(SymContents.data()) + 1, 5);
+ StringRef *I = std::begin(BCD.Symbols), *E = std::end(BCD.Symbols);
+ collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);
+ BCDs[SymName] = BCD;
+ }
+ // Type descriptors in the MS-ABI start with '??_R0'
+ else if (SymName.startswith("??_R0")) {
+ const char *DataPtr = SymContents.drop_front(BytesInAddress).data();
+ TypeDescriptor TD;
+ if (BytesInAddress == 8)
+ TD.AlwaysZero = *reinterpret_cast<const little64_t *>(DataPtr);
+ else
+ TD.AlwaysZero = *reinterpret_cast<const little32_t *>(DataPtr);
+ TD.MangledName = SymContents.drop_front(BytesInAddress * 2);
+ StringRef *I = std::begin(TD.Symbols), *E = std::end(TD.Symbols);
+ collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);
+ TDs[SymName] = TD;
+ }
+ // Throw descriptors in the MS-ABI start with '_TI'
+ else if (SymName.startswith("_TI") || SymName.startswith("__TI")) {
+ ThrowInfo TI;
+ TI.Flags = *reinterpret_cast<const little32_t *>(SymContents.data());
+ collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize,
+ SymName, TIEntries);
+ TIs[SymName] = TI;
+ }
+ // Catchable type arrays in the MS-ABI start with _CTA or __CTA.
+ else if (SymName.startswith("_CTA") || SymName.startswith("__CTA")) {
+ CatchableTypeArray CTA;
+ CTA.NumEntries =
+ *reinterpret_cast<const little32_t *>(SymContents.data());
+ collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize,
+ SymName, CTAEntries);
+ CTAs[SymName] = CTA;
+ }
+ // Catchable types in the MS-ABI start with _CT or __CT.
+ else if (SymName.startswith("_CT") || SymName.startswith("__CT")) {
+ const little32_t *DataPtr =
+ reinterpret_cast<const little32_t *>(SymContents.data());
+ CatchableType CT;
+ CT.Flags = DataPtr[0];
+ CT.NonVirtualBaseAdjustmentOffset = DataPtr[2];
+ CT.VirtualBasePointerOffset = DataPtr[3];
+ CT.VirtualBaseAdjustmentOffset = DataPtr[4];
+ CT.Size = DataPtr[5];
+ StringRef *I = std::begin(CT.Symbols), *E = std::end(CT.Symbols);
+ collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E);
+ CTs[SymName] = CT;
+ }
+ // Construction vtables in the Itanium ABI start with '_ZTT' or '__ZTT'.
+ else if (SymName.startswith("_ZTT") || SymName.startswith("__ZTT")) {
+ collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize,
+ SymName, VTTEntries);
+ }
+ // Typeinfo names in the Itanium ABI start with '_ZTS' or '__ZTS'.
+ else if (SymName.startswith("_ZTS") || SymName.startswith("__ZTS")) {
+ TINames[SymName] = SymContents.slice(0, SymContents.find('\0'));
+ }
+ // Vtables in the Itanium ABI start with '_ZTV' or '__ZTV'.
+ else if (SymName.startswith("_ZTV") || SymName.startswith("__ZTV")) {
+ collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize,
+ SymName, VTableSymEntries);
+ for (uint64_t SymOffI = 0; SymOffI < SymSize; SymOffI += BytesInAddress) {
+ auto Key = std::make_pair(SymName, SymOffI);
+ if (VTableSymEntries.count(Key))
+ continue;
+ const char *DataPtr =
+ SymContents.substr(SymOffI, BytesInAddress).data();
+ int64_t VData;
+ if (BytesInAddress == 8)
+ VData = *reinterpret_cast<const little64_t *>(DataPtr);
+ else
+ VData = *reinterpret_cast<const little32_t *>(DataPtr);
+ VTableDataEntries[Key] = VData;
+ }
+ }
+ // Typeinfo structures in the Itanium ABI start with '_ZTI' or '__ZTI'.
+ else if (SymName.startswith("_ZTI") || SymName.startswith("__ZTI")) {
+ // FIXME: Do something with these!
+ }
+ }
+ for (const auto &VFTableEntry : VFTableEntries) {
+ StringRef VFTableName = VFTableEntry.first.first;
+ uint64_t Offset = VFTableEntry.first.second;
+ StringRef SymName = VFTableEntry.second;
+ outs() << VFTableName << '[' << Offset << "]: " << SymName << '\n';
+ }
+ for (const auto &VBTable : VBTables) {
+ StringRef VBTableName = VBTable.first;
+ uint32_t Idx = 0;
+ for (little32_t Offset : VBTable.second) {
+ outs() << VBTableName << '[' << Idx << "]: " << Offset << '\n';
+ Idx += sizeof(Offset);
+ }
+ }
+ for (const auto &COLPair : COLs) {
+ StringRef COLName = COLPair.first;
+ const CompleteObjectLocator &COL = COLPair.second;
+ outs() << COLName << "[IsImageRelative]: " << COL.Data[0] << '\n';
+ outs() << COLName << "[OffsetToTop]: " << COL.Data[1] << '\n';
+ outs() << COLName << "[VFPtrOffset]: " << COL.Data[2] << '\n';
+ outs() << COLName << "[TypeDescriptor]: " << COL.Symbols[0] << '\n';
+ outs() << COLName << "[ClassHierarchyDescriptor]: " << COL.Symbols[1]
+ << '\n';
+ }
+ for (const auto &CHDPair : CHDs) {
+ StringRef CHDName = CHDPair.first;
+ const ClassHierarchyDescriptor &CHD = CHDPair.second;
+ outs() << CHDName << "[AlwaysZero]: " << CHD.Data[0] << '\n';
+ outs() << CHDName << "[Flags]: " << CHD.Data[1] << '\n';
+ outs() << CHDName << "[NumClasses]: " << CHD.Data[2] << '\n';
+ outs() << CHDName << "[BaseClassArray]: " << CHD.Symbols[0] << '\n';
+ }
+ for (const auto &BCAEntry : BCAEntries) {
+ StringRef BCAName = BCAEntry.first.first;
+ uint64_t Offset = BCAEntry.first.second;
+ StringRef SymName = BCAEntry.second;
+ outs() << BCAName << '[' << Offset << "]: " << SymName << '\n';
+ }
+ for (const auto &BCDPair : BCDs) {
+ StringRef BCDName = BCDPair.first;
+ const BaseClassDescriptor &BCD = BCDPair.second;
+ outs() << BCDName << "[TypeDescriptor]: " << BCD.Symbols[0] << '\n';
+ outs() << BCDName << "[NumBases]: " << BCD.Data[0] << '\n';
+ outs() << BCDName << "[OffsetInVBase]: " << BCD.Data[1] << '\n';
+ outs() << BCDName << "[VBPtrOffset]: " << BCD.Data[2] << '\n';
+ outs() << BCDName << "[OffsetInVBTable]: " << BCD.Data[3] << '\n';
+ outs() << BCDName << "[Flags]: " << BCD.Data[4] << '\n';
+ outs() << BCDName << "[ClassHierarchyDescriptor]: " << BCD.Symbols[1]
+ << '\n';
+ }
+ for (const auto &TDPair : TDs) {
+ StringRef TDName = TDPair.first;
+ const TypeDescriptor &TD = TDPair.second;
+ outs() << TDName << "[VFPtr]: " << TD.Symbols[0] << '\n';
+ outs() << TDName << "[AlwaysZero]: " << TD.AlwaysZero << '\n';
+ outs() << TDName << "[MangledName]: ";
+ outs().write_escaped(TD.MangledName.rtrim(StringRef("\0", 1)),
+ /*UseHexEscapes=*/true)
+ << '\n';
+ }
+ for (const auto &TIPair : TIs) {
+ StringRef TIName = TIPair.first;
+ const ThrowInfo &TI = TIPair.second;
+ auto dumpThrowInfoFlag = [&](const char *Name, uint32_t Flag) {
+ outs() << TIName << "[Flags." << Name
+ << "]: " << (TI.Flags & Flag ? "true" : "false") << '\n';
+ };
+ auto dumpThrowInfoSymbol = [&](const char *Name, int Offset) {
+ outs() << TIName << '[' << Name << "]: ";
+ auto Entry = TIEntries.find(std::make_pair(TIName, Offset));
+ outs() << (Entry == TIEntries.end() ? "null" : Entry->second) << '\n';
+ };
+ outs() << TIName << "[Flags]: " << TI.Flags << '\n';
+ dumpThrowInfoFlag("Const", 1);
+ dumpThrowInfoFlag("Volatile", 2);
+ dumpThrowInfoSymbol("CleanupFn", 4);
+ dumpThrowInfoSymbol("ForwardCompat", 8);
+ dumpThrowInfoSymbol("CatchableTypeArray", 12);
+ }
+ for (const auto &CTAPair : CTAs) {
+ StringRef CTAName = CTAPair.first;
+ const CatchableTypeArray &CTA = CTAPair.second;
+
+ outs() << CTAName << "[NumEntries]: " << CTA.NumEntries << '\n';
+
+ unsigned Idx = 0;
+ for (auto I = CTAEntries.lower_bound(std::make_pair(CTAName, 0)),
+ E = CTAEntries.upper_bound(std::make_pair(CTAName, UINT64_MAX));
+ I != E; ++I)
+ outs() << CTAName << '[' << Idx++ << "]: " << I->second << '\n';
+ }
+ for (const auto &CTPair : CTs) {
+ StringRef CTName = CTPair.first;
+ const CatchableType &CT = CTPair.second;
+ auto dumpCatchableTypeFlag = [&](const char *Name, uint32_t Flag) {
+ outs() << CTName << "[Flags." << Name
+ << "]: " << (CT.Flags & Flag ? "true" : "false") << '\n';
+ };
+ outs() << CTName << "[Flags]: " << CT.Flags << '\n';
+ dumpCatchableTypeFlag("ScalarType", 1);
+ dumpCatchableTypeFlag("VirtualInheritance", 4);
+ outs() << CTName << "[TypeDescriptor]: " << CT.Symbols[0] << '\n';
+ outs() << CTName << "[NonVirtualBaseAdjustmentOffset]: "
+ << CT.NonVirtualBaseAdjustmentOffset << '\n';
+ outs() << CTName
+ << "[VirtualBasePointerOffset]: " << CT.VirtualBasePointerOffset
+ << '\n';
+ outs() << CTName << "[VirtualBaseAdjustmentOffset]: "
+ << CT.VirtualBaseAdjustmentOffset << '\n';
+ outs() << CTName << "[Size]: " << CT.Size << '\n';
+ outs() << CTName
+ << "[CopyCtor]: " << (CT.Symbols[1].empty() ? "null" : CT.Symbols[1])
+ << '\n';
+ }
+ for (const auto &VTTPair : VTTEntries) {
+ StringRef VTTName = VTTPair.first.first;
+ uint64_t VTTOffset = VTTPair.first.second;
+ StringRef VTTEntry = VTTPair.second;
+ outs() << VTTName << '[' << VTTOffset << "]: " << VTTEntry << '\n';
+ }
+ for (const auto &TIPair : TINames) {
+ StringRef TIName = TIPair.first;
+ outs() << TIName << ": " << TIPair.second << '\n';
+ }
+ auto VTableSymI = VTableSymEntries.begin();
+ auto VTableSymE = VTableSymEntries.end();
+ auto VTableDataI = VTableDataEntries.begin();
+ auto VTableDataE = VTableDataEntries.end();
+ for (;;) {
+ bool SymDone = VTableSymI == VTableSymE;
+ bool DataDone = VTableDataI == VTableDataE;
+ if (SymDone && DataDone)
+ break;
+ if (!SymDone && (DataDone || VTableSymI->first < VTableDataI->first)) {
+ StringRef VTableName = VTableSymI->first.first;
+ uint64_t Offset = VTableSymI->first.second;
+ StringRef VTableEntry = VTableSymI->second;
+ outs() << VTableName << '[' << Offset << "]: ";
+ outs() << VTableEntry;
+ outs() << '\n';
+ ++VTableSymI;
+ continue;
+ }
+ if (!DataDone && (SymDone || VTableDataI->first < VTableSymI->first)) {
+ StringRef VTableName = VTableDataI->first.first;
+ uint64_t Offset = VTableDataI->first.second;
+ int64_t VTableEntry = VTableDataI->second;
+ outs() << VTableName << '[' << Offset << "]: ";
+ outs() << VTableEntry;
+ outs() << '\n';
+ ++VTableDataI;
+ continue;
+ }
+ }
+}
+
+static void dumpArchive(const Archive *Arc) {
+ Error Err = Error::success();
+ for (auto &ArcC : Arc->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = ArcC.getAsBinary();
+ if (!ChildOrErr) {
+ // Ignore non-object files.
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS);
+ OS.flush();
+ reportError(Arc->getFileName(), Buf);
+ }
+ consumeError(ChildOrErr.takeError());
+ continue;
+ }
+
+ if (ObjectFile *Obj = dyn_cast<ObjectFile>(&*ChildOrErr.get()))
+ dumpCXXData(Obj);
+ else
+ reportError(Arc->getFileName(), cxxdump_error::unrecognized_file_format);
+ }
+ if (Err)
+ error(std::move(Err));
+}
+
+static void dumpInput(StringRef File) {
+ // Attempt to open the binary.
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
+ if (!BinaryOrErr) {
+ auto EC = errorToErrorCode(BinaryOrErr.takeError());
+ reportError(File, EC);
+ return;
+ }
+ Binary &Binary = *BinaryOrErr.get().getBinary();
+
+ if (Archive *Arc = dyn_cast<Archive>(&Binary))
+ dumpArchive(Arc);
+ else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary))
+ dumpCXXData(Obj);
+ else
+ reportError(File, cxxdump_error::unrecognized_file_format);
+}
+
+int main(int argc, const char *argv[]) {
+ InitLLVM X(argc, argv);
+
+ // Initialize targets.
+ llvm::InitializeAllTargetInfos();
+
+ // Register the target printer for --version.
+ cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
+
+ cl::HideUnrelatedOptions({&opts::CXXDumpCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "LLVM C++ ABI Data Dumper\n");
+
+ // Default to stdin if no filename is specified.
+ if (opts::InputFilenames.size() == 0)
+ opts::InputFilenames.push_back("-");
+
+ llvm::for_each(opts::InputFilenames, dumpInput);
+
+ return EXIT_SUCCESS;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cxxdump/llvm-cxxdump.h b/contrib/libs/llvm14/tools/llvm-cxxdump/llvm-cxxdump.h
new file mode 100644
index 00000000000..739cfe481a4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cxxdump/llvm-cxxdump.h
@@ -0,0 +1,22 @@
+//===-- llvm-cxxdump.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_CXXDUMP_LLVM_CXXDUMP_H
+#define LLVM_TOOLS_LLVM_CXXDUMP_LLVM_CXXDUMP_H
+
+#include "llvm/Support/CommandLine.h"
+#include <string>
+
+namespace opts {
+extern llvm::cl::list<std::string> InputFilenames;
+} // namespace opts
+
+#define LLVM_CXXDUMP_ENUM_ENT(ns, enum) \
+ { #enum, ns::enum }
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-cxxdump/ya.make b/contrib/libs/llvm14/tools/llvm-cxxdump/ya.make
new file mode 100644
index 00000000000..ec7047118ad
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cxxdump/ya.make
@@ -0,0 +1,43 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-cxxdump
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ Error.cpp
+ llvm-cxxdump.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-cxxfilt/Opts.td b/contrib/libs/llvm14/tools/llvm-cxxfilt/Opts.td
new file mode 100644
index 00000000000..93f865245fe
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cxxfilt/Opts.td
@@ -0,0 +1,28 @@
+include "llvm/Option/OptParser.td"
+
+class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>;
+class FF<string name, string help> : Flag<["--"], name>, HelpText<help>;
+
+multiclass BB<string name, string help1, string help2> {
+ def NAME: Flag<["--"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
+}
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["--"], name #"=">,
+ HelpText<help>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def help : FF<"help", "Display this help">;
+defm strip_underscore : BB<"strip-underscore", "Strip the leading underscore", "Don't strip the leading underscore">;
+def types : FF<"types", "">;
+def version : FF<"version", "Display the version">;
+
+defm : Eq<"format", "Specify mangling format. Currently ignored because only 'gnu' is supported">;
+def : F<"s", "Alias for --format">;
+
+def : F<"_", "Alias for --strip-underscore">, Alias<strip_underscore>;
+def : F<"h", "Alias for --help">, Alias<help>;
+def : F<"n", "Alias for --no-strip-underscore">, Alias<no_strip_underscore>;
+def : F<"t", "Alias for --types">, Alias<types>;
diff --git a/contrib/libs/llvm14/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/contrib/libs/llvm14/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
new file mode 100644
index 00000000000..ccfaaa96deb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
@@ -0,0 +1,184 @@
+//===-- llvm-c++filt.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstdlib>
+#include <iostream>
+
+using namespace llvm;
+
+namespace {
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class CxxfiltOptTable : public opt::OptTable {
+public:
+ CxxfiltOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
+};
+} // namespace
+
+static bool StripUnderscore;
+static bool Types;
+
+static StringRef ToolName;
+
+static void error(const Twine &Message) {
+ WithColor::error(errs(), ToolName) << Message << '\n';
+ exit(1);
+}
+
+static std::string demangle(const std::string &Mangled) {
+ const char *DecoratedStr = Mangled.c_str();
+ if (StripUnderscore)
+ if (DecoratedStr[0] == '_')
+ ++DecoratedStr;
+
+ std::string Result;
+ if (nonMicrosoftDemangle(DecoratedStr, Result))
+ return Result;
+
+ std::string Prefix;
+ char *Undecorated = nullptr;
+
+ if (Types)
+ Undecorated = itaniumDemangle(DecoratedStr, nullptr, nullptr, nullptr);
+
+ if (!Undecorated && strncmp(DecoratedStr, "__imp_", 6) == 0) {
+ Prefix = "import thunk for ";
+ Undecorated = itaniumDemangle(DecoratedStr + 6, nullptr, nullptr, nullptr);
+ }
+
+ Result = Undecorated ? Prefix + Undecorated : Mangled;
+ free(Undecorated);
+ return Result;
+}
+
+// Split 'Source' on any character that fails to pass 'IsLegalChar'. The
+// returned vector consists of pairs where 'first' is the delimited word, and
+// 'second' are the delimiters following that word.
+static void SplitStringDelims(
+ StringRef Source,
+ SmallVectorImpl<std::pair<StringRef, StringRef>> &OutFragments,
+ function_ref<bool(char)> IsLegalChar) {
+ // The beginning of the input string.
+ const auto Head = Source.begin();
+
+ // Obtain any leading delimiters.
+ auto Start = std::find_if(Head, Source.end(), IsLegalChar);
+ if (Start != Head)
+ OutFragments.push_back({"", Source.slice(0, Start - Head)});
+
+ // Capture each word and the delimiters following that word.
+ while (Start != Source.end()) {
+ Start = std::find_if(Start, Source.end(), IsLegalChar);
+ auto End = std::find_if_not(Start, Source.end(), IsLegalChar);
+ auto DEnd = std::find_if(End, Source.end(), IsLegalChar);
+ OutFragments.push_back({Source.slice(Start - Head, End - Head),
+ Source.slice(End - Head, DEnd - Head)});
+ Start = DEnd;
+ }
+}
+
+// This returns true if 'C' is a character that can show up in an
+// Itanium-mangled string.
+static bool IsLegalItaniumChar(char C) {
+ // Itanium CXX ABI [External Names]p5.1.1:
+ // '$' and '.' in mangled names are reserved for private implementations.
+ return isAlnum(C) || C == '.' || C == '$' || C == '_';
+}
+
+// If 'Split' is true, then 'Mangled' is broken into individual words and each
+// word is demangled. Otherwise, the entire string is treated as a single
+// mangled item. The result is output to 'OS'.
+static void demangleLine(llvm::raw_ostream &OS, StringRef Mangled, bool Split) {
+ std::string Result;
+ if (Split) {
+ SmallVector<std::pair<StringRef, StringRef>, 16> Words;
+ SplitStringDelims(Mangled, Words, IsLegalItaniumChar);
+ for (const auto &Word : Words)
+ Result += ::demangle(std::string(Word.first)) + Word.second.str();
+ } else
+ Result = ::demangle(std::string(Mangled));
+ OS << Result << '\n';
+ OS.flush();
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ CxxfiltOptTable Tbl;
+ ToolName = argv[0];
+ opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver,
+ [&](StringRef Msg) { error(Msg); });
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(outs(),
+ (Twine(ToolName) + " [options] <mangled>").str().c_str(),
+ "LLVM symbol undecoration tool");
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ return 0;
+ }
+ if (Args.hasArg(OPT_version)) {
+ outs() << ToolName << '\n';
+ cl::PrintVersionMessage();
+ return 0;
+ }
+
+ // The default value depends on the default triple. Mach-O has symbols
+ // prefixed with "_", so strip by default.
+ if (opt::Arg *A =
+ Args.getLastArg(OPT_strip_underscore, OPT_no_strip_underscore))
+ StripUnderscore = A->getOption().matches(OPT_strip_underscore);
+ else
+ StripUnderscore = Triple(sys::getProcessTriple()).isOSBinFormatMachO();
+
+ Types = Args.hasArg(OPT_types);
+
+ std::vector<std::string> Decorated = Args.getAllArgValues(OPT_INPUT);
+ if (Decorated.empty())
+ for (std::string Mangled; std::getline(std::cin, Mangled);)
+ demangleLine(llvm::outs(), Mangled, true);
+ else
+ for (const auto &Symbol : Decorated)
+ demangleLine(llvm::outs(), Symbol, false);
+
+ return EXIT_SUCCESS;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cxxfilt/ya.make b/contrib/libs/llvm14/tools/llvm-cxxfilt/ya.make
new file mode 100644
index 00000000000..349199f76c5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cxxfilt/ya.make
@@ -0,0 +1,30 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-cxxfilt
+ contrib/libs/llvm14/tools/llvm-cxxfilt
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-cxxfilt.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-cxxmap/llvm-cxxmap.cpp b/contrib/libs/llvm14/tools/llvm-cxxmap/llvm-cxxmap.cpp
new file mode 100644
index 00000000000..1e18e379f23
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cxxmap/llvm-cxxmap.cpp
@@ -0,0 +1,166 @@
+//===- llvm-cxxmap.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// llvm-cxxmap computes a correspondence between old symbol names and new
+// symbol names based on a symbol equivalence file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.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::OptionCategory CXXMapCategory("CXX Map Options");
+
+cl::opt<std::string> OldSymbolFile(cl::Positional, cl::Required,
+ cl::desc("<symbol-file>"),
+ cl::cat(CXXMapCategory));
+cl::opt<std::string> NewSymbolFile(cl::Positional, cl::Required,
+ cl::desc("<symbol-file>"),
+ cl::cat(CXXMapCategory));
+cl::opt<std::string> RemappingFile("remapping-file", cl::Required,
+ cl::desc("Remapping file"),
+ cl::cat(CXXMapCategory));
+cl::alias RemappingFileA("r", cl::aliasopt(RemappingFile),
+ cl::cat(CXXMapCategory));
+cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
+ cl::init("-"), cl::desc("Output file"),
+ cl::cat(CXXMapCategory));
+cl::alias OutputFilenameA("o", cl::aliasopt(OutputFilename),
+ cl::cat(CXXMapCategory));
+
+cl::opt<bool> WarnAmbiguous(
+ "Wambiguous",
+ cl::desc("Warn on equivalent symbols in the output symbol list"),
+ cl::cat(CXXMapCategory));
+cl::opt<bool> WarnIncomplete(
+ "Wincomplete",
+ cl::desc("Warn on input symbols missing from output symbol list"),
+ cl::cat(CXXMapCategory));
+
+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::HideUnrelatedOptions({&CXXMapCategory, &getColorCategory()});
+ 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::OF_TextWithCRLF);
+ if (EC)
+ exitWithErrorCode(EC, OutputFilename);
+
+ remapSymbols(*OldSymbolBufOrError.get(), *NewSymbolBufOrError.get(),
+ *RemappingBufOrError.get(), OS);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-cxxmap/ya.make b/contrib/libs/llvm14/tools/llvm-cxxmap/ya.make
new file mode 100644
index 00000000000..057b4f1ab71
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-cxxmap/ya.make
@@ -0,0 +1,28 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-cxxmap
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-cxxmap.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-diff/lib/DiffConsumer.cpp b/contrib/libs/llvm14/tools/llvm-diff/lib/DiffConsumer.cpp
new file mode 100644
index 00000000000..b6eb71916ac
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-diff/lib/DiffConsumer.cpp
@@ -0,0 +1,218 @@
+//===-- DiffConsumer.cpp - Difference Consumer ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This files implements the LLVM difference Consumer
+//
+//===----------------------------------------------------------------------===//
+
+#include "DiffConsumer.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm;
+
+static void ComputeNumbering(const Function *F,
+ DenseMap<const Value *, unsigned> &Numbering) {
+ unsigned IN = 0;
+
+ // Arguments get the first numbers.
+ for (const auto &Arg : F->args())
+ if (!Arg.hasName())
+ Numbering[&Arg] = IN++;
+
+ // Walk the basic blocks in order.
+ for (const auto &Func : *F) {
+ if (!Func.hasName())
+ Numbering[&Func] = IN++;
+
+ // Walk the instructions in order.
+ for (const auto &BB : Func)
+ // void instructions don't get numbers.
+ if (!BB.hasName() && !BB.getType()->isVoidTy())
+ Numbering[&BB] = IN++;
+ }
+
+ assert(!Numbering.empty() && "asked for numbering but numbering was no-op");
+}
+
+void Consumer::anchor() { }
+
+void DiffConsumer::printValue(const Value *V, bool isL) {
+ if (V->hasName()) {
+ out << (isa<GlobalValue>(V) ? '@' : '%') << V->getName();
+ return;
+ }
+ if (V->getType()->isVoidTy()) {
+ if (auto *SI = dyn_cast<StoreInst>(V)) {
+ out << "store to ";
+ printValue(SI->getPointerOperand(), isL);
+ } else if (auto *CI = dyn_cast<CallInst>(V)) {
+ out << "call to ";
+ printValue(CI->getCalledOperand(), isL);
+ } else if (auto *II = dyn_cast<InvokeInst>(V)) {
+ out << "invoke to ";
+ printValue(II->getCalledOperand(), isL);
+ } else {
+ out << *V;
+ }
+ return;
+ }
+ if (isa<Constant>(V)) {
+ out << *V;
+ return;
+ }
+
+ unsigned N = contexts.size();
+ while (N > 0) {
+ --N;
+ DiffContext &ctxt = contexts[N];
+ if (!ctxt.IsFunction) continue;
+ if (isL) {
+ if (ctxt.LNumbering.empty())
+ ComputeNumbering(cast<Function>(ctxt.L), ctxt.LNumbering);
+ out << '%' << ctxt.LNumbering[V];
+ return;
+ } else {
+ if (ctxt.RNumbering.empty())
+ ComputeNumbering(cast<Function>(ctxt.R), ctxt.RNumbering);
+ out << '%' << ctxt.RNumbering[V];
+ return;
+ }
+ }
+
+ out << "<anonymous>";
+}
+
+void DiffConsumer::header() {
+ if (contexts.empty()) return;
+ for (SmallVectorImpl<DiffContext>::iterator
+ I = contexts.begin(), E = contexts.end(); I != E; ++I) {
+ if (I->Differences) continue;
+ if (isa<Function>(I->L)) {
+ // Extra newline between functions.
+ if (Differences) out << "\n";
+
+ const Function *L = cast<Function>(I->L);
+ const Function *R = cast<Function>(I->R);
+ if (L->getName() != R->getName())
+ out << "in function " << L->getName()
+ << " / " << R->getName() << ":\n";
+ else
+ out << "in function " << L->getName() << ":\n";
+ } else if (isa<BasicBlock>(I->L)) {
+ const BasicBlock *L = cast<BasicBlock>(I->L);
+ const BasicBlock *R = cast<BasicBlock>(I->R);
+ if (L->hasName() && R->hasName() && L->getName() == R->getName())
+ out << " in block %" << L->getName() << ":\n";
+ else {
+ out << " in block ";
+ printValue(L, true);
+ out << " / ";
+ printValue(R, false);
+ out << ":\n";
+ }
+ } else if (isa<Instruction>(I->L)) {
+ out << " in instruction ";
+ printValue(I->L, true);
+ out << " / ";
+ printValue(I->R, false);
+ out << ":\n";
+ }
+
+ I->Differences = true;
+ }
+}
+
+void DiffConsumer::indent() {
+ unsigned N = Indent;
+ while (N--) out << ' ';
+}
+
+void DiffConsumer::reset() {
+ contexts.clear();
+ Differences = false;
+ Indent = 0;
+}
+
+bool DiffConsumer::hadDifferences() const {
+ return Differences;
+}
+
+void DiffConsumer::enterContext(const Value *L, const Value *R) {
+ contexts.push_back(DiffContext(L, R));
+ Indent += 2;
+}
+
+void DiffConsumer::exitContext() {
+ Differences |= contexts.back().Differences;
+ contexts.pop_back();
+ Indent -= 2;
+}
+
+void DiffConsumer::log(StringRef text) {
+ header();
+ indent();
+ out << text << '\n';
+}
+
+void DiffConsumer::logf(const LogBuilder &Log) {
+ header();
+ indent();
+
+ unsigned arg = 0;
+
+ StringRef format = Log.getFormat();
+ while (true) {
+ size_t percent = format.find('%');
+ if (percent == StringRef::npos) {
+ out << format;
+ break;
+ }
+ assert(format[percent] == '%');
+
+ if (percent > 0) out << format.substr(0, percent);
+
+ switch (format[percent+1]) {
+ case '%': out << '%'; break;
+ case 'l': printValue(Log.getArgument(arg++), true); break;
+ case 'r': printValue(Log.getArgument(arg++), false); break;
+ default: llvm_unreachable("unknown format character");
+ }
+
+ format = format.substr(percent+2);
+ }
+
+ out << '\n';
+}
+
+void DiffConsumer::logd(const DiffLogBuilder &Log) {
+ header();
+
+ for (unsigned I = 0, E = Log.getNumLines(); I != E; ++I) {
+ indent();
+ switch (Log.getLineKind(I)) {
+ case DC_match:
+ out << " ";
+ Log.getLeft(I)->print(dbgs()); dbgs() << '\n';
+ //printValue(Log.getLeft(I), true);
+ break;
+ case DC_left:
+ out << "< ";
+ Log.getLeft(I)->print(dbgs()); dbgs() << '\n';
+ //printValue(Log.getLeft(I), true);
+ break;
+ case DC_right:
+ out << "> ";
+ Log.getRight(I)->print(dbgs()); dbgs() << '\n';
+ //printValue(Log.getRight(I), false);
+ break;
+ }
+ //out << "\n";
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-diff/lib/DiffConsumer.h b/contrib/libs/llvm14/tools/llvm-diff/lib/DiffConsumer.h
new file mode 100644
index 00000000000..08c3afcbe11
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-diff/lib/DiffConsumer.h
@@ -0,0 +1,91 @@
+//===-- DiffConsumer.h - Difference Consumer --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This header defines the interface to the LLVM difference Consumer
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_DIFF_DIFFCONSUMER_H
+#define LLVM_TOOLS_LLVM_DIFF_DIFFCONSUMER_H
+
+#include "DiffLog.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+class StringRef;
+ class Module;
+ class Value;
+ class Function;
+
+ /// The interface for consumers of difference data.
+ class Consumer {
+ virtual void anchor();
+ public:
+ /// Record that a local context has been entered. Left and
+ /// Right are IR "containers" of some sort which are being
+ /// considered for structural equivalence: global variables,
+ /// functions, blocks, instructions, etc.
+ virtual void enterContext(const Value *Left, const Value *Right) = 0;
+
+ /// Record that a local context has been exited.
+ virtual void exitContext() = 0;
+
+ /// Record a difference within the current context.
+ virtual void log(StringRef Text) = 0;
+
+ /// Record a formatted difference within the current context.
+ virtual void logf(const LogBuilder &Log) = 0;
+
+ /// Record a line-by-line instruction diff.
+ virtual void logd(const DiffLogBuilder &Log) = 0;
+
+ protected:
+ virtual ~Consumer() {}
+ };
+
+ class DiffConsumer : public Consumer {
+ private:
+ struct DiffContext {
+ DiffContext(const Value *L, const Value *R)
+ : L(L), R(R), Differences(false), IsFunction(isa<Function>(L)) {}
+ const Value *L;
+ const Value *R;
+ bool Differences;
+ bool IsFunction;
+ DenseMap<const Value *, unsigned> LNumbering;
+ DenseMap<const Value *, unsigned> RNumbering;
+ };
+
+ raw_ostream &out;
+ SmallVector<DiffContext, 5> contexts;
+ bool Differences;
+ unsigned Indent;
+
+ void printValue(const Value *V, bool isL);
+ void header();
+ void indent();
+
+ public:
+ DiffConsumer()
+ : out(errs()), Differences(false), Indent(0) {}
+
+ void reset();
+ bool hadDifferences() const;
+ void enterContext(const Value *L, const Value *R) override;
+ void exitContext() override;
+ void log(StringRef text) override;
+ void logf(const LogBuilder &Log) override;
+ void logd(const DiffLogBuilder &Log) override;
+ };
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-diff/lib/DiffLog.cpp b/contrib/libs/llvm14/tools/llvm-diff/lib/DiffLog.cpp
new file mode 100644
index 00000000000..d31a345d255
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-diff/lib/DiffLog.cpp
@@ -0,0 +1,54 @@
+//===-- DiffLog.h - Difference Log Builder and accessories ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This header defines the interface to the LLVM difference log builder.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DiffLog.h"
+#include "DiffConsumer.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace llvm;
+
+LogBuilder::~LogBuilder() {
+ if (consumer)
+ consumer->logf(*this);
+}
+
+StringRef LogBuilder::getFormat() const { return Format; }
+
+unsigned LogBuilder::getNumArguments() const { return Arguments.size(); }
+const Value *LogBuilder::getArgument(unsigned I) const { return Arguments[I]; }
+
+DiffLogBuilder::~DiffLogBuilder() { consumer.logd(*this); }
+
+void DiffLogBuilder::addMatch(const Instruction *L, const Instruction *R) {
+ Diff.push_back(DiffRecord(L, R));
+}
+void DiffLogBuilder::addLeft(const Instruction *L) {
+ // HACK: VS 2010 has a bug in the stdlib that requires this.
+ Diff.push_back(DiffRecord(L, DiffRecord::second_type(nullptr)));
+}
+void DiffLogBuilder::addRight(const Instruction *R) {
+ // HACK: VS 2010 has a bug in the stdlib that requires this.
+ Diff.push_back(DiffRecord(DiffRecord::first_type(nullptr), R));
+}
+
+unsigned DiffLogBuilder::getNumLines() const { return Diff.size(); }
+
+DiffChange DiffLogBuilder::getLineKind(unsigned I) const {
+ return (Diff[I].first ? (Diff[I].second ? DC_match : DC_left)
+ : DC_right);
+}
+const Instruction *DiffLogBuilder::getLeft(unsigned I) const {
+ return Diff[I].first;
+}
+const Instruction *DiffLogBuilder::getRight(unsigned I) const {
+ return Diff[I].second;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-diff/lib/DiffLog.h b/contrib/libs/llvm14/tools/llvm-diff/lib/DiffLog.h
new file mode 100644
index 00000000000..d8b07b97119
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-diff/lib/DiffLog.h
@@ -0,0 +1,83 @@
+//===-- DiffLog.h - Difference Log Builder and accessories ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This header defines the interface to the LLVM difference log builder.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_DIFF_DIFFLOG_H
+#define LLVM_TOOLS_LLVM_DIFF_DIFFLOG_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace llvm {
+ class Instruction;
+ class Value;
+ class Consumer;
+
+ /// Trichotomy assumption
+ enum DiffChange { DC_match, DC_left, DC_right };
+
+ /// A temporary-object class for building up log messages.
+ class LogBuilder {
+ Consumer *consumer;
+
+ /// The use of a stored StringRef here is okay because
+ /// LogBuilder should be used only as a temporary, and as a
+ /// temporary it will be destructed before whatever temporary
+ /// might be initializing this format.
+ StringRef Format;
+
+ SmallVector<const Value *, 4> Arguments;
+
+ public:
+ LogBuilder(Consumer &c, StringRef Format) : consumer(&c), Format(Format) {}
+ LogBuilder(LogBuilder &&L)
+ : consumer(L.consumer), Format(L.Format),
+ Arguments(std::move(L.Arguments)) {
+ L.consumer = nullptr;
+ }
+
+ LogBuilder &operator<<(const Value *V) {
+ Arguments.push_back(V);
+ return *this;
+ }
+
+ ~LogBuilder();
+
+ StringRef getFormat() const;
+ unsigned getNumArguments() const;
+ const Value *getArgument(unsigned I) const;
+ };
+
+ /// A temporary-object class for building up diff messages.
+ class DiffLogBuilder {
+ typedef std::pair<const Instruction *, const Instruction *> DiffRecord;
+ SmallVector<DiffRecord, 20> Diff;
+
+ Consumer &consumer;
+
+ public:
+ DiffLogBuilder(Consumer &c) : consumer(c) {}
+ ~DiffLogBuilder();
+
+ void addMatch(const Instruction *L, const Instruction *R);
+ // HACK: VS 2010 has a bug in the stdlib that requires this.
+ void addLeft(const Instruction *L);
+ void addRight(const Instruction *R);
+
+ unsigned getNumLines() const;
+ DiffChange getLineKind(unsigned I) const;
+ const Instruction *getLeft(unsigned I) const;
+ const Instruction *getRight(unsigned I) const;
+ };
+
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-diff/lib/DifferenceEngine.cpp b/contrib/libs/llvm14/tools/llvm-diff/lib/DifferenceEngine.cpp
new file mode 100644
index 00000000000..4bdefcdc175
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-diff/lib/DifferenceEngine.cpp
@@ -0,0 +1,874 @@
+//===-- DifferenceEngine.cpp - Structural function/module comparison ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This header defines the implementation of the LLVM difference
+// engine, which structurally compares global values within a module.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DifferenceEngine.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/type_traits.h"
+#include <utility>
+
+using namespace llvm;
+
+namespace {
+
+/// A priority queue, implemented as a heap.
+template <class T, class Sorter, unsigned InlineCapacity>
+class PriorityQueue {
+ Sorter Precedes;
+ llvm::SmallVector<T, InlineCapacity> Storage;
+
+public:
+ PriorityQueue(const Sorter &Precedes) : Precedes(Precedes) {}
+
+ /// Checks whether the heap is empty.
+ bool empty() const { return Storage.empty(); }
+
+ /// Insert a new value on the heap.
+ void insert(const T &V) {
+ unsigned Index = Storage.size();
+ Storage.push_back(V);
+ if (Index == 0) return;
+
+ T *data = Storage.data();
+ while (true) {
+ unsigned Target = (Index + 1) / 2 - 1;
+ if (!Precedes(data[Index], data[Target])) return;
+ std::swap(data[Index], data[Target]);
+ if (Target == 0) return;
+ Index = Target;
+ }
+ }
+
+ /// Remove the minimum value in the heap. Only valid on a non-empty heap.
+ T remove_min() {
+ assert(!empty());
+ T tmp = Storage[0];
+
+ unsigned NewSize = Storage.size() - 1;
+ if (NewSize) {
+ // Move the slot at the end to the beginning.
+ if (std::is_trivially_copyable<T>::value)
+ Storage[0] = Storage[NewSize];
+ else
+ std::swap(Storage[0], Storage[NewSize]);
+
+ // Bubble the root up as necessary.
+ unsigned Index = 0;
+ while (true) {
+ // With a 1-based index, the children would be Index*2 and Index*2+1.
+ unsigned R = (Index + 1) * 2;
+ unsigned L = R - 1;
+
+ // If R is out of bounds, we're done after this in any case.
+ if (R >= NewSize) {
+ // If L is also out of bounds, we're done immediately.
+ if (L >= NewSize) break;
+
+ // Otherwise, test whether we should swap L and Index.
+ if (Precedes(Storage[L], Storage[Index]))
+ std::swap(Storage[L], Storage[Index]);
+ break;
+ }
+
+ // Otherwise, we need to compare with the smaller of L and R.
+ // Prefer R because it's closer to the end of the array.
+ unsigned IndexToTest = (Precedes(Storage[L], Storage[R]) ? L : R);
+
+ // If Index is >= the min of L and R, then heap ordering is restored.
+ if (!Precedes(Storage[IndexToTest], Storage[Index]))
+ break;
+
+ // Otherwise, keep bubbling up.
+ std::swap(Storage[IndexToTest], Storage[Index]);
+ Index = IndexToTest;
+ }
+ }
+ Storage.pop_back();
+
+ return tmp;
+ }
+};
+
+/// A function-scope difference engine.
+class FunctionDifferenceEngine {
+ DifferenceEngine &Engine;
+
+ // Some initializers may reference the variable we're currently checking. This
+ // can cause an infinite loop. The Saved[LR]HS ivars can be checked to prevent
+ // recursing.
+ const Value *SavedLHS;
+ const Value *SavedRHS;
+
+ /// The current mapping from old local values to new local values.
+ DenseMap<const Value *, const Value *> Values;
+
+ /// The current mapping from old blocks to new blocks.
+ DenseMap<const BasicBlock *, const BasicBlock *> Blocks;
+
+ DenseSet<std::pair<const Value *, const Value *>> TentativeValues;
+
+ unsigned getUnprocPredCount(const BasicBlock *Block) const {
+ unsigned Count = 0;
+ for (const_pred_iterator I = pred_begin(Block), E = pred_end(Block); I != E;
+ ++I)
+ if (!Blocks.count(*I)) Count++;
+ return Count;
+ }
+
+ typedef std::pair<const BasicBlock *, const BasicBlock *> BlockPair;
+
+ /// A type which sorts a priority queue by the number of unprocessed
+ /// predecessor blocks it has remaining.
+ ///
+ /// This is actually really expensive to calculate.
+ struct QueueSorter {
+ const FunctionDifferenceEngine &fde;
+ explicit QueueSorter(const FunctionDifferenceEngine &fde) : fde(fde) {}
+
+ bool operator()(BlockPair &Old, BlockPair &New) {
+ return fde.getUnprocPredCount(Old.first)
+ < fde.getUnprocPredCount(New.first);
+ }
+ };
+
+ /// A queue of unified blocks to process.
+ PriorityQueue<BlockPair, QueueSorter, 20> Queue;
+
+ /// Try to unify the given two blocks. Enqueues them for processing
+ /// if they haven't already been processed.
+ ///
+ /// Returns true if there was a problem unifying them.
+ bool tryUnify(const BasicBlock *L, const BasicBlock *R) {
+ const BasicBlock *&Ref = Blocks[L];
+
+ if (Ref) {
+ if (Ref == R) return false;
+
+ Engine.logf("successor %l cannot be equivalent to %r; "
+ "it's already equivalent to %r")
+ << L << R << Ref;
+ return true;
+ }
+
+ Ref = R;
+ Queue.insert(BlockPair(L, R));
+ return false;
+ }
+
+ /// Unifies two instructions, given that they're known not to have
+ /// structural differences.
+ void unify(const Instruction *L, const Instruction *R) {
+ DifferenceEngine::Context C(Engine, L, R);
+
+ bool Result = diff(L, R, true, true);
+ assert(!Result && "structural differences second time around?");
+ (void) Result;
+ if (!L->use_empty())
+ Values[L] = R;
+ }
+
+ void processQueue() {
+ while (!Queue.empty()) {
+ BlockPair Pair = Queue.remove_min();
+ diff(Pair.first, Pair.second);
+ }
+ }
+
+ void diff(const BasicBlock *L, const BasicBlock *R) {
+ DifferenceEngine::Context C(Engine, L, R);
+
+ BasicBlock::const_iterator LI = L->begin(), LE = L->end();
+ BasicBlock::const_iterator RI = R->begin();
+
+ do {
+ assert(LI != LE && RI != R->end());
+ const Instruction *LeftI = &*LI, *RightI = &*RI;
+
+ // If the instructions differ, start the more sophisticated diff
+ // algorithm at the start of the block.
+ if (diff(LeftI, RightI, false, false)) {
+ TentativeValues.clear();
+ return runBlockDiff(L->begin(), R->begin());
+ }
+
+ // Otherwise, tentatively unify them.
+ if (!LeftI->use_empty())
+ TentativeValues.insert(std::make_pair(LeftI, RightI));
+
+ ++LI;
+ ++RI;
+ } while (LI != LE); // This is sufficient: we can't get equality of
+ // terminators if there are residual instructions.
+
+ // Unify everything in the block, non-tentatively this time.
+ TentativeValues.clear();
+ for (LI = L->begin(), RI = R->begin(); LI != LE; ++LI, ++RI)
+ unify(&*LI, &*RI);
+ }
+
+ bool matchForBlockDiff(const Instruction *L, const Instruction *R);
+ void runBlockDiff(BasicBlock::const_iterator LI,
+ BasicBlock::const_iterator RI);
+
+ bool diffCallSites(const CallBase &L, const CallBase &R, bool Complain) {
+ // FIXME: call attributes
+ if (!equivalentAsOperands(L.getCalledOperand(), R.getCalledOperand())) {
+ if (Complain) Engine.log("called functions differ");
+ return true;
+ }
+ if (L.arg_size() != R.arg_size()) {
+ if (Complain) Engine.log("argument counts differ");
+ return true;
+ }
+ for (unsigned I = 0, E = L.arg_size(); I != E; ++I)
+ if (!equivalentAsOperands(L.getArgOperand(I), R.getArgOperand(I))) {
+ if (Complain)
+ Engine.logf("arguments %l and %r differ")
+ << L.getArgOperand(I) << R.getArgOperand(I);
+ return true;
+ }
+ return false;
+ }
+
+ bool diff(const Instruction *L, const Instruction *R, bool Complain,
+ bool TryUnify) {
+ // FIXME: metadata (if Complain is set)
+
+ // Different opcodes always imply different operations.
+ if (L->getOpcode() != R->getOpcode()) {
+ if (Complain) Engine.log("different instruction types");
+ return true;
+ }
+
+ if (isa<CmpInst>(L)) {
+ if (cast<CmpInst>(L)->getPredicate()
+ != cast<CmpInst>(R)->getPredicate()) {
+ if (Complain) Engine.log("different predicates");
+ return true;
+ }
+ } else if (isa<CallInst>(L)) {
+ return diffCallSites(cast<CallInst>(*L), cast<CallInst>(*R), Complain);
+ } else if (isa<PHINode>(L)) {
+ const PHINode &LI = cast<PHINode>(*L);
+ const PHINode &RI = cast<PHINode>(*R);
+
+ // This is really weird; type uniquing is broken?
+ if (LI.getType() != RI.getType()) {
+ if (!LI.getType()->isPointerTy() || !RI.getType()->isPointerTy()) {
+ if (Complain) Engine.log("different phi types");
+ return true;
+ }
+ }
+
+ if (LI.getNumIncomingValues() != RI.getNumIncomingValues()) {
+ if (Complain)
+ Engine.log("PHI node # of incoming values differ");
+ return true;
+ }
+
+ for (unsigned I = 0; I < LI.getNumIncomingValues(); ++I) {
+ if (TryUnify)
+ tryUnify(LI.getIncomingBlock(I), RI.getIncomingBlock(I));
+
+ if (!equivalentAsOperands(LI.getIncomingValue(I),
+ RI.getIncomingValue(I))) {
+ if (Complain)
+ Engine.log("PHI node incoming values differ");
+ return true;
+ }
+ }
+
+ return false;
+
+ // Terminators.
+ } else if (isa<InvokeInst>(L)) {
+ const InvokeInst &LI = cast<InvokeInst>(*L);
+ const InvokeInst &RI = cast<InvokeInst>(*R);
+ if (diffCallSites(LI, RI, Complain))
+ return true;
+
+ if (TryUnify) {
+ tryUnify(LI.getNormalDest(), RI.getNormalDest());
+ tryUnify(LI.getUnwindDest(), RI.getUnwindDest());
+ }
+ return false;
+
+ } else if (isa<CallBrInst>(L)) {
+ const CallBrInst &LI = cast<CallBrInst>(*L);
+ const CallBrInst &RI = cast<CallBrInst>(*R);
+ if (LI.getNumIndirectDests() != RI.getNumIndirectDests()) {
+ if (Complain)
+ Engine.log("callbr # of indirect destinations differ");
+ return true;
+ }
+
+ // Perform the "try unify" step so that we can equate the indirect
+ // destinations before checking the call site.
+ for (unsigned I = 0; I < LI.getNumIndirectDests(); I++)
+ tryUnify(LI.getIndirectDest(I), RI.getIndirectDest(I));
+
+ if (diffCallSites(LI, RI, Complain))
+ return true;
+
+ if (TryUnify)
+ tryUnify(LI.getDefaultDest(), RI.getDefaultDest());
+ return false;
+
+ } else if (isa<BranchInst>(L)) {
+ const BranchInst *LI = cast<BranchInst>(L);
+ const BranchInst *RI = cast<BranchInst>(R);
+ if (LI->isConditional() != RI->isConditional()) {
+ if (Complain) Engine.log("branch conditionality differs");
+ return true;
+ }
+
+ if (LI->isConditional()) {
+ if (!equivalentAsOperands(LI->getCondition(), RI->getCondition())) {
+ if (Complain) Engine.log("branch conditions differ");
+ return true;
+ }
+ if (TryUnify) tryUnify(LI->getSuccessor(1), RI->getSuccessor(1));
+ }
+ if (TryUnify) tryUnify(LI->getSuccessor(0), RI->getSuccessor(0));
+ return false;
+
+ } else if (isa<IndirectBrInst>(L)) {
+ const IndirectBrInst *LI = cast<IndirectBrInst>(L);
+ const IndirectBrInst *RI = cast<IndirectBrInst>(R);
+ if (LI->getNumDestinations() != RI->getNumDestinations()) {
+ if (Complain) Engine.log("indirectbr # of destinations differ");
+ return true;
+ }
+
+ if (!equivalentAsOperands(LI->getAddress(), RI->getAddress())) {
+ if (Complain) Engine.log("indirectbr addresses differ");
+ return true;
+ }
+
+ if (TryUnify) {
+ for (unsigned i = 0; i < LI->getNumDestinations(); i++) {
+ tryUnify(LI->getDestination(i), RI->getDestination(i));
+ }
+ }
+ return false;
+
+ } else if (isa<SwitchInst>(L)) {
+ const SwitchInst *LI = cast<SwitchInst>(L);
+ const SwitchInst *RI = cast<SwitchInst>(R);
+ if (!equivalentAsOperands(LI->getCondition(), RI->getCondition())) {
+ if (Complain) Engine.log("switch conditions differ");
+ return true;
+ }
+ if (TryUnify) tryUnify(LI->getDefaultDest(), RI->getDefaultDest());
+
+ bool Difference = false;
+
+ DenseMap<const ConstantInt *, const BasicBlock *> LCases;
+ for (auto Case : LI->cases())
+ LCases[Case.getCaseValue()] = Case.getCaseSuccessor();
+
+ for (auto Case : RI->cases()) {
+ const ConstantInt *CaseValue = Case.getCaseValue();
+ const BasicBlock *LCase = LCases[CaseValue];
+ if (LCase) {
+ if (TryUnify)
+ tryUnify(LCase, Case.getCaseSuccessor());
+ LCases.erase(CaseValue);
+ } else if (Complain || !Difference) {
+ if (Complain)
+ Engine.logf("right switch has extra case %r") << CaseValue;
+ Difference = true;
+ }
+ }
+ if (!Difference)
+ for (DenseMap<const ConstantInt *, const BasicBlock *>::iterator
+ I = LCases.begin(),
+ E = LCases.end();
+ I != E; ++I) {
+ if (Complain)
+ Engine.logf("left switch has extra case %l") << I->first;
+ Difference = true;
+ }
+ return Difference;
+ } else if (isa<UnreachableInst>(L)) {
+ return false;
+ }
+
+ if (L->getNumOperands() != R->getNumOperands()) {
+ if (Complain) Engine.log("instructions have different operand counts");
+ return true;
+ }
+
+ for (unsigned I = 0, E = L->getNumOperands(); I != E; ++I) {
+ Value *LO = L->getOperand(I), *RO = R->getOperand(I);
+ if (!equivalentAsOperands(LO, RO)) {
+ if (Complain) Engine.logf("operands %l and %r differ") << LO << RO;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+public:
+ bool equivalentAsOperands(const Constant *L, const Constant *R) {
+ // Use equality as a preliminary filter.
+ if (L == R)
+ return true;
+
+ if (L->getValueID() != R->getValueID())
+ return false;
+
+ // Ask the engine about global values.
+ if (isa<GlobalValue>(L))
+ return Engine.equivalentAsOperands(cast<GlobalValue>(L),
+ cast<GlobalValue>(R));
+
+ // Compare constant expressions structurally.
+ if (isa<ConstantExpr>(L))
+ return equivalentAsOperands(cast<ConstantExpr>(L),
+ cast<ConstantExpr>(R));
+
+ // Constants of the "same type" don't always actually have the same
+ // type; I don't know why. Just white-list them.
+ if (isa<ConstantPointerNull>(L) || isa<UndefValue>(L) || isa<ConstantAggregateZero>(L))
+ return true;
+
+ // Block addresses only match if we've already encountered the
+ // block. FIXME: tentative matches?
+ if (isa<BlockAddress>(L))
+ return Blocks[cast<BlockAddress>(L)->getBasicBlock()]
+ == cast<BlockAddress>(R)->getBasicBlock();
+
+ // If L and R are ConstantVectors, compare each element
+ if (isa<ConstantVector>(L)) {
+ const ConstantVector *CVL = cast<ConstantVector>(L);
+ const ConstantVector *CVR = cast<ConstantVector>(R);
+ if (CVL->getType()->getNumElements() != CVR->getType()->getNumElements())
+ return false;
+ for (unsigned i = 0; i < CVL->getType()->getNumElements(); i++) {
+ if (!equivalentAsOperands(CVL->getOperand(i), CVR->getOperand(i)))
+ return false;
+ }
+ return true;
+ }
+
+ // If L and R are ConstantArrays, compare the element count and types.
+ if (isa<ConstantArray>(L)) {
+ const ConstantArray *CAL = cast<ConstantArray>(L);
+ const ConstantArray *CAR = cast<ConstantArray>(R);
+ // Sometimes a type may be equivalent, but not uniquified---e.g. it may
+ // contain a GEP instruction. Do a deeper comparison of the types.
+ if (CAL->getType()->getNumElements() != CAR->getType()->getNumElements())
+ return false;
+
+ for (unsigned I = 0; I < CAL->getType()->getNumElements(); ++I) {
+ if (!equivalentAsOperands(CAL->getAggregateElement(I),
+ CAR->getAggregateElement(I)))
+ return false;
+ }
+
+ return true;
+ }
+
+ // If L and R are ConstantStructs, compare each field and type.
+ if (isa<ConstantStruct>(L)) {
+ const ConstantStruct *CSL = cast<ConstantStruct>(L);
+ const ConstantStruct *CSR = cast<ConstantStruct>(R);
+
+ const StructType *LTy = cast<StructType>(CSL->getType());
+ const StructType *RTy = cast<StructType>(CSR->getType());
+
+ // The StructTypes should have the same attributes. Don't use
+ // isLayoutIdentical(), because that just checks the element pointers,
+ // which may not work here.
+ if (LTy->getNumElements() != RTy->getNumElements() ||
+ LTy->isPacked() != RTy->isPacked())
+ return false;
+
+ for (unsigned I = 0; I < LTy->getNumElements(); I++) {
+ const Value *LAgg = CSL->getAggregateElement(I);
+ const Value *RAgg = CSR->getAggregateElement(I);
+
+ if (LAgg == SavedLHS || RAgg == SavedRHS) {
+ if (LAgg != SavedLHS || RAgg != SavedRHS)
+ // If the left and right operands aren't both re-analyzing the
+ // variable, then the initialiers don't match, so report "false".
+ // Otherwise, we skip these operands..
+ return false;
+
+ continue;
+ }
+
+ if (!equivalentAsOperands(LAgg, RAgg)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ bool equivalentAsOperands(const ConstantExpr *L, const ConstantExpr *R) {
+ if (L == R)
+ return true;
+
+ if (L->getOpcode() != R->getOpcode())
+ return false;
+
+ switch (L->getOpcode()) {
+ case Instruction::ICmp:
+ case Instruction::FCmp:
+ if (L->getPredicate() != R->getPredicate())
+ return false;
+ break;
+
+ case Instruction::GetElementPtr:
+ // FIXME: inbounds?
+ break;
+
+ default:
+ break;
+ }
+
+ if (L->getNumOperands() != R->getNumOperands())
+ return false;
+
+ for (unsigned I = 0, E = L->getNumOperands(); I != E; ++I) {
+ const auto *LOp = L->getOperand(I);
+ const auto *ROp = R->getOperand(I);
+
+ if (LOp == SavedLHS || ROp == SavedRHS) {
+ if (LOp != SavedLHS || ROp != SavedRHS)
+ // If the left and right operands aren't both re-analyzing the
+ // variable, then the initialiers don't match, so report "false".
+ // Otherwise, we skip these operands..
+ return false;
+
+ continue;
+ }
+
+ if (!equivalentAsOperands(LOp, ROp))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool equivalentAsOperands(const Value *L, const Value *R) {
+ // Fall out if the values have different kind.
+ // This possibly shouldn't take priority over oracles.
+ if (L->getValueID() != R->getValueID())
+ return false;
+
+ // Value subtypes: Argument, Constant, Instruction, BasicBlock,
+ // InlineAsm, MDNode, MDString, PseudoSourceValue
+
+ if (isa<Constant>(L))
+ return equivalentAsOperands(cast<Constant>(L), cast<Constant>(R));
+
+ if (isa<Instruction>(L))
+ return Values[L] == R || TentativeValues.count(std::make_pair(L, R));
+
+ if (isa<Argument>(L))
+ return Values[L] == R;
+
+ if (isa<BasicBlock>(L))
+ return Blocks[cast<BasicBlock>(L)] != R;
+
+ // Pretend everything else is identical.
+ return true;
+ }
+
+ // Avoid a gcc warning about accessing 'this' in an initializer.
+ FunctionDifferenceEngine *this_() { return this; }
+
+public:
+ FunctionDifferenceEngine(DifferenceEngine &Engine,
+ const Value *SavedLHS = nullptr,
+ const Value *SavedRHS = nullptr)
+ : Engine(Engine), SavedLHS(SavedLHS), SavedRHS(SavedRHS),
+ Queue(QueueSorter(*this_())) {}
+
+ void diff(const Function *L, const Function *R) {
+ if (L->arg_size() != R->arg_size())
+ Engine.log("different argument counts");
+
+ // Map the arguments.
+ for (Function::const_arg_iterator LI = L->arg_begin(), LE = L->arg_end(),
+ RI = R->arg_begin(), RE = R->arg_end();
+ LI != LE && RI != RE; ++LI, ++RI)
+ Values[&*LI] = &*RI;
+
+ tryUnify(&*L->begin(), &*R->begin());
+ processQueue();
+ }
+};
+
+struct DiffEntry {
+ DiffEntry() : Cost(0) {}
+
+ unsigned Cost;
+ llvm::SmallVector<char, 8> Path; // actually of DifferenceEngine::DiffChange
+};
+
+bool FunctionDifferenceEngine::matchForBlockDiff(const Instruction *L,
+ const Instruction *R) {
+ return !diff(L, R, false, false);
+}
+
+void FunctionDifferenceEngine::runBlockDiff(BasicBlock::const_iterator LStart,
+ BasicBlock::const_iterator RStart) {
+ BasicBlock::const_iterator LE = LStart->getParent()->end();
+ BasicBlock::const_iterator RE = RStart->getParent()->end();
+
+ unsigned NL = std::distance(LStart, LE);
+
+ SmallVector<DiffEntry, 20> Paths1(NL+1);
+ SmallVector<DiffEntry, 20> Paths2(NL+1);
+
+ DiffEntry *Cur = Paths1.data();
+ DiffEntry *Next = Paths2.data();
+
+ const unsigned LeftCost = 2;
+ const unsigned RightCost = 2;
+ const unsigned MatchCost = 0;
+
+ assert(TentativeValues.empty());
+
+ // Initialize the first column.
+ for (unsigned I = 0; I != NL+1; ++I) {
+ Cur[I].Cost = I * LeftCost;
+ for (unsigned J = 0; J != I; ++J)
+ Cur[I].Path.push_back(DC_left);
+ }
+
+ for (BasicBlock::const_iterator RI = RStart; RI != RE; ++RI) {
+ // Initialize the first row.
+ Next[0] = Cur[0];
+ Next[0].Cost += RightCost;
+ Next[0].Path.push_back(DC_right);
+
+ unsigned Index = 1;
+ for (BasicBlock::const_iterator LI = LStart; LI != LE; ++LI, ++Index) {
+ if (matchForBlockDiff(&*LI, &*RI)) {
+ Next[Index] = Cur[Index-1];
+ Next[Index].Cost += MatchCost;
+ Next[Index].Path.push_back(DC_match);
+ TentativeValues.insert(std::make_pair(&*LI, &*RI));
+ } else if (Next[Index-1].Cost <= Cur[Index].Cost) {
+ Next[Index] = Next[Index-1];
+ Next[Index].Cost += LeftCost;
+ Next[Index].Path.push_back(DC_left);
+ } else {
+ Next[Index] = Cur[Index];
+ Next[Index].Cost += RightCost;
+ Next[Index].Path.push_back(DC_right);
+ }
+ }
+
+ std::swap(Cur, Next);
+ }
+
+ // We don't need the tentative values anymore; everything from here
+ // on out should be non-tentative.
+ TentativeValues.clear();
+
+ SmallVectorImpl<char> &Path = Cur[NL].Path;
+ BasicBlock::const_iterator LI = LStart, RI = RStart;
+
+ DiffLogBuilder Diff(Engine.getConsumer());
+
+ // Drop trailing matches.
+ while (Path.size() && Path.back() == DC_match)
+ Path.pop_back();
+
+ // Skip leading matches.
+ SmallVectorImpl<char>::iterator
+ PI = Path.begin(), PE = Path.end();
+ while (PI != PE && *PI == DC_match) {
+ unify(&*LI, &*RI);
+ ++PI;
+ ++LI;
+ ++RI;
+ }
+
+ for (; PI != PE; ++PI) {
+ switch (static_cast<DiffChange>(*PI)) {
+ case DC_match:
+ assert(LI != LE && RI != RE);
+ {
+ const Instruction *L = &*LI, *R = &*RI;
+ unify(L, R);
+ Diff.addMatch(L, R);
+ }
+ ++LI; ++RI;
+ break;
+
+ case DC_left:
+ assert(LI != LE);
+ Diff.addLeft(&*LI);
+ ++LI;
+ break;
+
+ case DC_right:
+ assert(RI != RE);
+ Diff.addRight(&*RI);
+ ++RI;
+ break;
+ }
+ }
+
+ // Finishing unifying and complaining about the tails of the block,
+ // which should be matches all the way through.
+ while (LI != LE) {
+ assert(RI != RE);
+ unify(&*LI, &*RI);
+ ++LI;
+ ++RI;
+ }
+
+ // 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.
+ const Instruction *LTerm = LStart->getParent()->getTerminator();
+ const Instruction *RTerm = RStart->getParent()->getTerminator();
+ if (isa<BranchInst>(LTerm) && isa<InvokeInst>(RTerm)) {
+ if (cast<BranchInst>(LTerm)->isConditional()) return;
+ BasicBlock::const_iterator I = LTerm->getIterator();
+ if (I == LStart->getParent()->begin()) return;
+ --I;
+ if (!isa<CallInst>(*I)) return;
+ const CallInst *LCall = cast<CallInst>(&*I);
+ const InvokeInst *RInvoke = cast<InvokeInst>(RTerm);
+ if (!equivalentAsOperands(LCall->getCalledOperand(),
+ RInvoke->getCalledOperand()))
+ return;
+ if (!LCall->use_empty())
+ Values[LCall] = RInvoke;
+ tryUnify(LTerm->getSuccessor(0), RInvoke->getNormalDest());
+ } else if (isa<InvokeInst>(LTerm) && isa<BranchInst>(RTerm)) {
+ if (cast<BranchInst>(RTerm)->isConditional()) return;
+ BasicBlock::const_iterator I = RTerm->getIterator();
+ if (I == RStart->getParent()->begin()) return;
+ --I;
+ if (!isa<CallInst>(*I)) return;
+ const CallInst *RCall = cast<CallInst>(I);
+ const InvokeInst *LInvoke = cast<InvokeInst>(LTerm);
+ if (!equivalentAsOperands(LInvoke->getCalledOperand(),
+ RCall->getCalledOperand()))
+ return;
+ if (!LInvoke->use_empty())
+ Values[LInvoke] = RCall;
+ tryUnify(LInvoke->getNormalDest(), RTerm->getSuccessor(0));
+ }
+}
+}
+
+void DifferenceEngine::Oracle::anchor() { }
+
+void DifferenceEngine::diff(const Function *L, const Function *R) {
+ Context C(*this, L, R);
+
+ // FIXME: types
+ // FIXME: attributes and CC
+ // FIXME: parameter attributes
+
+ // If both are declarations, we're done.
+ if (L->empty() && R->empty())
+ return;
+ else if (L->empty())
+ log("left function is declaration, right function is definition");
+ else if (R->empty())
+ log("right function is declaration, left function is definition");
+ else
+ FunctionDifferenceEngine(*this).diff(L, R);
+}
+
+void DifferenceEngine::diff(const Module *L, const Module *R) {
+ StringSet<> LNames;
+ SmallVector<std::pair<const Function *, const Function *>, 20> Queue;
+
+ unsigned LeftAnonCount = 0;
+ unsigned RightAnonCount = 0;
+
+ for (Module::const_iterator I = L->begin(), E = L->end(); I != E; ++I) {
+ const Function *LFn = &*I;
+ 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));
+ else
+ logf("function %l exists only in left module") << LFn;
+ }
+
+ for (Module::const_iterator I = R->begin(), E = R->end(); I != E; ++I) {
+ const Function *RFn = &*I;
+ 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<const Function *, const Function *>>::iterator
+ I = Queue.begin(),
+ E = Queue.end();
+ I != E; ++I)
+ diff(I->first, I->second);
+}
+
+bool DifferenceEngine::equivalentAsOperands(const GlobalValue *L,
+ const GlobalValue *R) {
+ if (globalValueOracle) return (*globalValueOracle)(L, R);
+
+ if (isa<GlobalVariable>(L) && isa<GlobalVariable>(R)) {
+ const GlobalVariable *GVL = cast<GlobalVariable>(L);
+ const GlobalVariable *GVR = cast<GlobalVariable>(R);
+ if (GVL->hasLocalLinkage() && GVL->hasUniqueInitializer() &&
+ GVR->hasLocalLinkage() && GVR->hasUniqueInitializer())
+ return FunctionDifferenceEngine(*this, GVL, GVR)
+ .equivalentAsOperands(GVL->getInitializer(), GVR->getInitializer());
+ }
+
+ return L->getName() == R->getName();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-diff/lib/DifferenceEngine.h b/contrib/libs/llvm14/tools/llvm-diff/lib/DifferenceEngine.h
new file mode 100644
index 00000000000..436a3556636
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-diff/lib/DifferenceEngine.h
@@ -0,0 +1,90 @@
+//===-- DifferenceEngine.h - Module comparator ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This header defines the interface to the LLVM difference engine,
+// which structurally compares functions within a module.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_DIFF_DIFFERENCEENGINE_H
+#define LLVM_TOOLS_LLVM_DIFF_DIFFERENCEENGINE_H
+
+#include "DiffConsumer.h"
+#include "DiffLog.h"
+#include "llvm/ADT/StringRef.h"
+#include <utility>
+
+namespace llvm {
+ class Function;
+ class GlobalValue;
+ class Instruction;
+ class LLVMContext;
+ class Module;
+ class Twine;
+ class Value;
+
+ /// A class for performing structural comparisons of LLVM assembly.
+ class DifferenceEngine {
+ public:
+ /// A RAII object for recording the current context.
+ struct Context {
+ Context(DifferenceEngine &Engine, const Value *L, const Value *R)
+ : Engine(Engine) {
+ Engine.consumer.enterContext(L, R);
+ }
+
+ ~Context() {
+ Engine.consumer.exitContext();
+ }
+
+ private:
+ DifferenceEngine &Engine;
+ };
+
+ /// An oracle for answering whether two values are equivalent as
+ /// operands.
+ class Oracle {
+ virtual void anchor();
+ public:
+ virtual bool operator()(const Value *L, const Value *R) = 0;
+
+ protected:
+ virtual ~Oracle() {}
+ };
+
+ DifferenceEngine(Consumer &consumer)
+ : consumer(consumer), globalValueOracle(nullptr) {}
+
+ void diff(const Module *L, const Module *R);
+ void diff(const Function *L, const Function *R);
+ void log(StringRef text) {
+ consumer.log(text);
+ }
+ LogBuilder logf(StringRef text) {
+ return LogBuilder(consumer, text);
+ }
+ Consumer& getConsumer() const { return consumer; }
+
+ /// Installs an oracle to decide whether two global values are
+ /// equivalent as operands. Without an oracle, global values are
+ /// considered equivalent as operands precisely when they have the
+ /// same name.
+ void setGlobalValueOracle(Oracle *oracle) {
+ globalValueOracle = oracle;
+ }
+
+ /// Determines whether two global values are equivalent.
+ bool equivalentAsOperands(const GlobalValue *L, const GlobalValue *R);
+
+ private:
+ Consumer &consumer;
+ Oracle *globalValueOracle;
+ };
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-diff/lib/ya.make b/contrib/libs/llvm14/tools/llvm-diff/lib/ya.make
new file mode 100644
index 00000000000..2cfa6d9a48c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-diff/lib/ya.make
@@ -0,0 +1,30 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-diff/lib
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ DiffConsumer.cpp
+ DiffLog.cpp
+ DifferenceEngine.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-diff/llvm-diff.cpp b/contrib/libs/llvm14/tools/llvm-diff/llvm-diff.cpp
new file mode 100644
index 00000000000..7349469c80d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-diff/llvm-diff.cpp
@@ -0,0 +1,96 @@
+//===-- llvm-diff.cpp - Module comparator command-line driver ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the command-line driver for the difference engine.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lib/DiffLog.h"
+#include "lib/DifferenceEngine.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/WithColor.h"
+#include <string>
+#include <utility>
+
+
+using namespace llvm;
+
+/// Reads a module from a file. On error, messages are written to stderr
+/// and null is returned.
+static std::unique_ptr<Module> readModule(LLVMContext &Context,
+ StringRef Name) {
+ SMDiagnostic Diag;
+ std::unique_ptr<Module> M = parseIRFile(Name, Diag, Context);
+ if (!M)
+ Diag.print("llvm-diff", errs());
+ return M;
+}
+
+static void diffGlobal(DifferenceEngine &Engine, Module &L, Module &R,
+ StringRef Name) {
+ // Drop leading sigils from the global name.
+ if (Name.startswith("@")) Name = Name.substr(1);
+
+ Function *LFn = L.getFunction(Name);
+ Function *RFn = R.getFunction(Name);
+ if (LFn && RFn)
+ Engine.diff(LFn, RFn);
+ else if (!LFn && !RFn)
+ errs() << "No function named @" << Name << " in either module\n";
+ else if (!LFn)
+ errs() << "No function named @" << Name << " in left module\n";
+ else
+ errs() << "No function named @" << Name << " in right module\n";
+}
+
+cl::OptionCategory DiffCategory("Diff Options");
+
+static cl::opt<std::string> LeftFilename(cl::Positional,
+ cl::desc("<first file>"), cl::Required,
+ cl::cat(DiffCategory));
+static cl::opt<std::string> RightFilename(cl::Positional,
+ cl::desc("<second file>"),
+ cl::Required, cl::cat(DiffCategory));
+static cl::list<std::string> GlobalsToCompare(cl::Positional,
+ cl::desc("<globals to compare>"),
+ cl::cat(DiffCategory));
+
+int main(int argc, char **argv) {
+ cl::HideUnrelatedOptions({&DiffCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv);
+
+ LLVMContext Context;
+
+ // Load both modules. Die if that fails.
+ std::unique_ptr<Module> LModule = readModule(Context, LeftFilename);
+ std::unique_ptr<Module> RModule = readModule(Context, RightFilename);
+ if (!LModule || !RModule) return 1;
+
+ DiffConsumer Consumer;
+ DifferenceEngine Engine(Consumer);
+
+ // If any global names were given, just diff those.
+ if (!GlobalsToCompare.empty()) {
+ for (unsigned I = 0, E = GlobalsToCompare.size(); I != E; ++I)
+ diffGlobal(Engine, *LModule, *RModule, GlobalsToCompare[I]);
+
+ // Otherwise, diff everything in the module.
+ } else {
+ Engine.diff(LModule.get(), RModule.get());
+ }
+
+ return Consumer.hadDifferences();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-diff/ya.make b/contrib/libs/llvm14/tools/llvm-diff/ya.make
new file mode 100644
index 00000000000..589ecf55b94
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-diff/ya.make
@@ -0,0 +1,36 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/tools/llvm-diff/lib
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-diff
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-diff.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-dis/llvm-dis.cpp b/contrib/libs/llvm14/tools/llvm-dis/llvm-dis.cpp
new file mode 100644
index 00000000000..7b3c3e7706a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-dis/llvm-dis.cpp
@@ -0,0 +1,258 @@
+//===-- llvm-dis.cpp - The low-level LLVM disassembler --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This utility may be invoked in the following manner:
+// llvm-dis [options] - Read LLVM bitcode from stdin, write asm to stdout
+// llvm-dis [options] x.bc - Read LLVM bitcode from the x.bc file, write asm
+// to the x.ll file.
+// Options:
+// --help - Output information about command line switches
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/IR/AssemblyAnnotationWriter.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+#include <system_error>
+using namespace llvm;
+
+static cl::OptionCategory DisCategory("Disassembler Options");
+
+static cl::list<std::string> InputFilenames(cl::Positional, cl::ZeroOrMore,
+ cl::desc("[input bitcode]..."),
+ cl::cat(DisCategory));
+
+static cl::opt<std::string> OutputFilename("o",
+ cl::desc("Override output filename"),
+ cl::value_desc("filename"),
+ cl::cat(DisCategory));
+
+static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"),
+ cl::cat(DisCategory));
+
+static cl::opt<bool> DontPrint("disable-output",
+ cl::desc("Don't output the .ll file"),
+ cl::Hidden, cl::cat(DisCategory));
+
+static cl::opt<bool>
+ SetImporting("set-importing",
+ cl::desc("Set lazy loading to pretend to import a module"),
+ cl::Hidden, cl::cat(DisCategory));
+
+static cl::opt<bool>
+ ShowAnnotations("show-annotations",
+ cl::desc("Add informational comments to the .ll file"),
+ cl::cat(DisCategory));
+
+static cl::opt<bool> PreserveAssemblyUseListOrder(
+ "preserve-ll-uselistorder",
+ cl::desc("Preserve use-list order when writing LLVM assembly."),
+ cl::init(false), cl::Hidden, cl::cat(DisCategory));
+
+static cl::opt<bool>
+ MaterializeMetadata("materialize-metadata",
+ cl::desc("Load module without materializing metadata, "
+ "then materialize only the metadata"),
+ cl::cat(DisCategory));
+
+static cl::opt<bool> PrintThinLTOIndexOnly(
+ "print-thinlto-index-only",
+ cl::desc("Only read thinlto index and print the index as LLVM assembly."),
+ cl::init(false), cl::Hidden, cl::cat(DisCategory));
+
+namespace {
+
+static void printDebugLoc(const DebugLoc &DL, formatted_raw_ostream &OS) {
+ OS << DL.getLine() << ":" << DL.getCol();
+ if (DILocation *IDL = DL.getInlinedAt()) {
+ OS << "@";
+ printDebugLoc(IDL, OS);
+ }
+}
+class CommentWriter : public AssemblyAnnotationWriter {
+public:
+ void emitFunctionAnnot(const Function *F,
+ formatted_raw_ostream &OS) override {
+ OS << "; [#uses=" << F->getNumUses() << ']'; // Output # uses
+ OS << '\n';
+ }
+ void printInfoComment(const Value &V, formatted_raw_ostream &OS) override {
+ bool Padded = false;
+ if (!V.getType()->isVoidTy()) {
+ OS.PadToColumn(50);
+ Padded = true;
+ // Output # uses and type
+ OS << "; [#uses=" << V.getNumUses() << " type=" << *V.getType() << "]";
+ }
+ if (const Instruction *I = dyn_cast<Instruction>(&V)) {
+ if (const DebugLoc &DL = I->getDebugLoc()) {
+ if (!Padded) {
+ OS.PadToColumn(50);
+ Padded = true;
+ OS << ";";
+ }
+ OS << " [debug line = ";
+ printDebugLoc(DL,OS);
+ OS << "]";
+ }
+ if (const DbgDeclareInst *DDI = dyn_cast<DbgDeclareInst>(I)) {
+ if (!Padded) {
+ OS.PadToColumn(50);
+ OS << ";";
+ }
+ OS << " [debug variable = " << DDI->getVariable()->getName() << "]";
+ }
+ else if (const DbgValueInst *DVI = dyn_cast<DbgValueInst>(I)) {
+ if (!Padded) {
+ OS.PadToColumn(50);
+ OS << ";";
+ }
+ OS << " [debug variable = " << DVI->getVariable()->getName() << "]";
+ }
+ }
+ }
+};
+
+struct LLVMDisDiagnosticHandler : public DiagnosticHandler {
+ char *Prefix;
+ LLVMDisDiagnosticHandler(char *PrefixPtr) : Prefix(PrefixPtr) {}
+ bool handleDiagnostics(const DiagnosticInfo &DI) override {
+ raw_ostream &OS = errs();
+ OS << Prefix << ": ";
+ switch (DI.getSeverity()) {
+ case DS_Error: WithColor::error(OS); break;
+ case DS_Warning: WithColor::warning(OS); break;
+ case DS_Remark: OS << "remark: "; break;
+ case DS_Note: WithColor::note(OS); break;
+ }
+
+ DiagnosticPrinterRawOStream DP(OS);
+ DI.print(DP);
+ OS << '\n';
+
+ if (DI.getSeverity() == DS_Error)
+ exit(1);
+ return true;
+ }
+};
+} // end anon namespace
+
+static ExitOnError ExitOnErr;
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ ExitOnErr.setBanner(std::string(argv[0]) + ": error: ");
+
+ cl::HideUnrelatedOptions({&DisCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n");
+
+ LLVMContext Context;
+ Context.setDiagnosticHandler(
+ std::make_unique<LLVMDisDiagnosticHandler>(argv[0]));
+
+ if (InputFilenames.size() < 1) {
+ InputFilenames.push_back("-");
+ } else if (InputFilenames.size() > 1 && !OutputFilename.empty()) {
+ errs()
+ << "error: output file name cannot be set for multiple input files\n";
+ return 1;
+ }
+
+ for (std::string InputFilename : InputFilenames) {
+ std::unique_ptr<MemoryBuffer> MB = ExitOnErr(
+ errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename)));
+
+ BitcodeFileContents IF = ExitOnErr(llvm::getBitcodeFileContents(*MB));
+
+ const size_t N = IF.Mods.size();
+
+ if (OutputFilename == "-" && N > 1)
+ errs() << "only single module bitcode files can be written to stdout\n";
+
+ for (size_t I = 0; I < N; ++I) {
+ BitcodeModule MB = IF.Mods[I];
+
+ std::unique_ptr<Module> M;
+
+ if (!PrintThinLTOIndexOnly) {
+ M = ExitOnErr(
+ MB.getLazyModule(Context, MaterializeMetadata, SetImporting));
+ if (MaterializeMetadata)
+ ExitOnErr(M->materializeMetadata());
+ else
+ ExitOnErr(M->materializeAll());
+ }
+
+ BitcodeLTOInfo LTOInfo = ExitOnErr(MB.getLTOInfo());
+ std::unique_ptr<ModuleSummaryIndex> Index;
+ if (LTOInfo.HasSummary)
+ Index = ExitOnErr(MB.getSummary());
+
+ std::string FinalFilename(OutputFilename);
+
+ // Just use stdout. We won't actually print anything on it.
+ if (DontPrint)
+ FinalFilename = "-";
+
+ if (FinalFilename.empty()) { // Unspecified output, infer it.
+ if (InputFilename == "-") {
+ FinalFilename = "-";
+ } else {
+ StringRef IFN = InputFilename;
+ FinalFilename = (IFN.endswith(".bc") ? IFN.drop_back(3) : IFN).str();
+ if (N > 1)
+ FinalFilename += std::string(".") + std::to_string(I);
+ FinalFilename += ".ll";
+ }
+ } else {
+ if (N > 1)
+ FinalFilename += std::string(".") + std::to_string(I);
+ }
+
+ std::error_code EC;
+ std::unique_ptr<ToolOutputFile> Out(
+ new ToolOutputFile(FinalFilename, EC, sys::fs::OF_TextWithCRLF));
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return 1;
+ }
+
+ std::unique_ptr<AssemblyAnnotationWriter> Annotator;
+ if (ShowAnnotations)
+ Annotator.reset(new CommentWriter());
+
+ // All that llvm-dis does is write the assembly to a file.
+ if (!DontPrint) {
+ if (M)
+ M->print(Out->os(), Annotator.get(), PreserveAssemblyUseListOrder);
+ if (Index)
+ Index->print(Out->os());
+ }
+
+ // Declare success.
+ Out->keep();
+ }
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-dis/ya.make b/contrib/libs/llvm14/tools/llvm-dis/ya.make
new file mode 100644
index 00000000000..49edeef0bdd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-dis/ya.make
@@ -0,0 +1,33 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-dis
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-dis.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-dwarfdump/SectionSizes.cpp b/contrib/libs/llvm14/tools/llvm-dwarfdump/SectionSizes.cpp
new file mode 100644
index 00000000000..731bf05fd75
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-dwarfdump/SectionSizes.cpp
@@ -0,0 +1,122 @@
+//===-- SectionSizes.cpp - Debug section sizes ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-dwarfdump.h"
+
+#define DEBUG_TYPE "dwarfdump"
+
+using namespace llvm;
+using namespace llvm::dwarfdump;
+using namespace llvm::object;
+
+static size_t getNameColumnWidth(const SectionSizes &Sizes,
+ const StringRef SectionNameTitle) {
+ // The minimum column width should be the size of "SECTION".
+ size_t Width = SectionNameTitle.size();
+ for (const auto &It : Sizes.DebugSectionSizes)
+ Width = std::max(Width, It.first.size());
+ return Width;
+}
+
+static size_t getSizeColumnWidth(const SectionSizes &Sizes,
+ const StringRef SectionSizeTitle) {
+ // The minimum column width should be the size of the column title.
+ size_t Width = SectionSizeTitle.size();
+ for (const auto &It : Sizes.DebugSectionSizes) {
+ size_t NumWidth = std::to_string(It.second).size();
+ Width = std::max(Width, NumWidth);
+ }
+ return Width;
+}
+
+static void prettyPrintSectionSizes(const ObjectFile &Obj,
+ const SectionSizes &Sizes,
+ raw_ostream &OS) {
+ const StringRef SectionNameTitle = "SECTION";
+ const StringRef SectionSizeTitle = "SIZE (b)";
+
+ size_t NameColWidth = getNameColumnWidth(Sizes, SectionNameTitle);
+ size_t SizeColWidth = getSizeColumnWidth(Sizes, SectionSizeTitle);
+
+ OS << "----------------------------------------------------" << '\n';
+ OS << SectionNameTitle;
+ size_t SectionNameTitleWidth = SectionNameTitle.size();
+ for (unsigned i = 0; i < (NameColWidth - SectionNameTitleWidth) + 2; i++)
+ OS << " ";
+ OS << SectionSizeTitle << '\n';
+ for (unsigned i = 0; i < NameColWidth; i++)
+ OS << "-";
+ OS << " ";
+
+ for (unsigned i = 0; i < SizeColWidth; i++)
+ OS << "-";
+ OS << '\n';
+
+ for (const auto &It : Sizes.DebugSectionSizes) {
+ OS << left_justify(It.first, NameColWidth) << " ";
+
+ std::string NumBytes = std::to_string(It.second);
+ OS << right_justify(NumBytes, SizeColWidth) << " ("
+ << format("%0.2f",
+ It.second / static_cast<double>(Sizes.TotalObjectSize) * 100)
+ << "%)\n";
+ }
+
+ OS << '\n';
+ OS << " Total Size: " << Sizes.TotalDebugSectionsSize << " ("
+ << format("%0.2f", Sizes.TotalDebugSectionsSize /
+ static_cast<double>(Sizes.TotalObjectSize) * 100)
+ << "%)\n";
+ OS << " Total File Size: " << Sizes.TotalObjectSize << '\n';
+ OS << "----------------------------------------------------" << '\n';
+}
+
+void dwarfdump::calculateSectionSizes(const ObjectFile &Obj,
+ SectionSizes &Sizes,
+ const Twine &Filename) {
+ // Get total size.
+ Sizes.TotalObjectSize = Obj.getData().size();
+
+ for (const SectionRef &Section : Obj.sections()) {
+ StringRef SectionName;
+ if (Expected<StringRef> NameOrErr = Section.getName())
+ SectionName = *NameOrErr;
+ else
+ WithColor::defaultWarningHandler(
+ createFileError(Filename, NameOrErr.takeError()));
+
+ LLVM_DEBUG(dbgs() << SectionName.str() << ": " << Section.getSize()
+ << '\n');
+
+ if (!Section.isDebugSection())
+ continue;
+
+ Sizes.TotalDebugSectionsSize += Section.getSize();
+ Sizes.DebugSectionSizes[std::string(SectionName)] += Section.getSize();
+ }
+}
+
+bool dwarfdump::collectObjectSectionSizes(ObjectFile &Obj,
+ DWARFContext & /*DICtx*/,
+ const Twine &Filename,
+ raw_ostream &OS) {
+ SectionSizes Sizes;
+
+ // Get the section sizes.
+ calculateSectionSizes(Obj, Sizes, Filename);
+
+ OS << "----------------------------------------------------\n";
+ OS << "file: " << Filename.str() << '\n';
+
+ prettyPrintSectionSizes(Obj, Sizes, OS);
+
+ // TODO: If the input file is an archive, print the cumulative summary of all
+ // files from the archive.
+
+ return true;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-dwarfdump/Statistics.cpp b/contrib/libs/llvm14/tools/llvm-dwarfdump/Statistics.cpp
new file mode 100644
index 00000000000..5c08e43b4b0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-dwarfdump/Statistics.cpp
@@ -0,0 +1,1056 @@
+//===-- Statistics.cpp - Debug Info quality metrics -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-dwarfdump.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/JSON.h"
+
+#define DEBUG_TYPE "dwarfdump"
+using namespace llvm;
+using namespace llvm::dwarfdump;
+using namespace llvm::object;
+
+namespace {
+/// This represents the number of categories of debug location coverage being
+/// calculated. The first category is the number of variables with 0% location
+/// coverage, but the last category is the number of variables with 100%
+/// location coverage.
+constexpr int NumOfCoverageCategories = 12;
+
+/// This is used for zero location coverage bucket.
+constexpr unsigned ZeroCoverageBucket = 0;
+
+/// The UINT64_MAX is used as an indication of the overflow.
+constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
+
+/// This represents variables DIE offsets.
+using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
+/// This maps function DIE offset to its variables.
+using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
+/// This represents function DIE offsets containing an abstract_origin.
+using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;
+
+/// This represents a data type for the stats and it helps us to
+/// detect an overflow.
+/// NOTE: This can be implemented as a template if there is an another type
+/// needing this.
+struct SaturatingUINT64 {
+ /// Number that represents the stats.
+ uint64_t Value;
+
+ SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
+
+ void operator++(int) { return *this += 1; }
+ void operator+=(uint64_t Value_) {
+ if (Value != OverflowValue) {
+ if (Value < OverflowValue - Value_)
+ Value += Value_;
+ else
+ Value = OverflowValue;
+ }
+ }
+};
+
+/// Utility struct to store the full location of a DIE - its CU and offset.
+struct DIELocation {
+ DWARFUnit *DwUnit;
+ uint64_t DIEOffset;
+ DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
+ : DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
+};
+/// This represents DWARF locations of CrossCU referencing DIEs.
+using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
+
+/// This maps function DIE offset to its DWARF CU.
+using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;
+
+/// Holds statistics for one function (or other entity that has a PC range and
+/// contains variables, such as a compile unit).
+struct PerFunctionStats {
+ /// Number of inlined instances of this function.
+ uint64_t NumFnInlined = 0;
+ /// Number of out-of-line instances of this function.
+ uint64_t NumFnOutOfLine = 0;
+ /// Number of inlined instances that have abstract origins.
+ uint64_t NumAbstractOrigins = 0;
+ /// Number of variables and parameters with location across all inlined
+ /// instances.
+ uint64_t TotalVarWithLoc = 0;
+ /// Number of constants with location across all inlined instances.
+ uint64_t ConstantMembers = 0;
+ /// Number of arificial variables, parameters or members across all instances.
+ uint64_t NumArtificial = 0;
+ /// List of all Variables and parameters in this function.
+ StringSet<> VarsInFunction;
+ /// Compile units also cover a PC range, but have this flag set to false.
+ bool IsFunction = false;
+ /// Function has source location information.
+ bool HasSourceLocation = false;
+ /// Number of function parameters.
+ uint64_t NumParams = 0;
+ /// Number of function parameters with source location.
+ uint64_t NumParamSourceLocations = 0;
+ /// Number of function parameters with type.
+ uint64_t NumParamTypes = 0;
+ /// Number of function parameters with a DW_AT_location.
+ uint64_t NumParamLocations = 0;
+ /// Number of local variables.
+ uint64_t NumLocalVars = 0;
+ /// Number of local variables with source location.
+ uint64_t NumLocalVarSourceLocations = 0;
+ /// Number of local variables with type.
+ uint64_t NumLocalVarTypes = 0;
+ /// Number of local variables with DW_AT_location.
+ uint64_t NumLocalVarLocations = 0;
+};
+
+/// Holds accumulated global statistics about DIEs.
+struct GlobalStats {
+ /// Total number of PC range bytes covered by DW_AT_locations.
+ SaturatingUINT64 TotalBytesCovered = 0;
+ /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
+ SaturatingUINT64 ScopeBytesCovered = 0;
+ /// Total number of PC range bytes in each variable's enclosing scope.
+ SaturatingUINT64 ScopeBytes = 0;
+ /// Total number of PC range bytes covered by DW_AT_locations with
+ /// the debug entry values (DW_OP_entry_value).
+ SaturatingUINT64 ScopeEntryValueBytesCovered = 0;
+ /// Total number of PC range bytes covered by DW_AT_locations of
+ /// formal parameters.
+ SaturatingUINT64 ParamScopeBytesCovered = 0;
+ /// Total number of PC range bytes in each parameter's enclosing scope.
+ SaturatingUINT64 ParamScopeBytes = 0;
+ /// Total number of PC range bytes covered by DW_AT_locations with
+ /// the debug entry values (DW_OP_entry_value) (only for parameters).
+ SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;
+ /// Total number of PC range bytes covered by DW_AT_locations (only for local
+ /// variables).
+ SaturatingUINT64 LocalVarScopeBytesCovered = 0;
+ /// Total number of PC range bytes in each local variable's enclosing scope.
+ SaturatingUINT64 LocalVarScopeBytes = 0;
+ /// Total number of PC range bytes covered by DW_AT_locations with
+ /// the debug entry values (DW_OP_entry_value) (only for local variables).
+ SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;
+ /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
+ SaturatingUINT64 CallSiteEntries = 0;
+ /// Total number of call site DIEs (DW_TAG_call_site).
+ SaturatingUINT64 CallSiteDIEs = 0;
+ /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
+ SaturatingUINT64 CallSiteParamDIEs = 0;
+ /// Total byte size of concrete functions. This byte size includes
+ /// inline functions contained in the concrete functions.
+ SaturatingUINT64 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.
+ SaturatingUINT64 InlineFunctionSize = 0;
+};
+
+/// Holds accumulated debug location statistics about local variables and
+/// formal parameters.
+struct LocationStats {
+ /// Map the scope coverage decile to the number of variables in the decile.
+ /// The first element of the array (at the index zero) represents the number
+ /// of variables with the no debug location at all, but the last element
+ /// in the vector represents the number of fully covered variables within
+ /// its scope.
+ std::vector<SaturatingUINT64> VarParamLocStats{
+ std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
+ /// Map non debug entry values coverage.
+ std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{
+ std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
+ /// The debug location statistics for formal parameters.
+ std::vector<SaturatingUINT64> ParamLocStats{
+ std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
+ /// Map non debug entry values coverage for formal parameters.
+ std::vector<SaturatingUINT64> ParamNonEntryValLocStats{
+ std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
+ /// The debug location statistics for local variables.
+ std::vector<SaturatingUINT64> LocalVarLocStats{
+ std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
+ /// Map non debug entry values coverage for local variables.
+ std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{
+ std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
+ /// Total number of local variables and function parameters processed.
+ SaturatingUINT64 NumVarParam = 0;
+ /// Total number of formal parameters processed.
+ SaturatingUINT64 NumParam = 0;
+ /// Total number of local variables processed.
+ SaturatingUINT64 NumVar = 0;
+};
+} // namespace
+
+/// Collect debug location statistics for one DIE.
+static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
+ std::vector<SaturatingUINT64> &VarParamLocStats,
+ std::vector<SaturatingUINT64> &ParamLocStats,
+ std::vector<SaturatingUINT64> &LocalVarLocStats,
+ bool IsParam, bool IsLocalVar) {
+ auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
+ // No debug location at all for the variable.
+ if (ScopeBytesCovered == 0)
+ return 0;
+ // Fully covered variable within its scope.
+ if (ScopeBytesCovered >= BytesInScope)
+ return NumOfCoverageCategories - 1;
+ // Get covered range (e.g. 20%-29%).
+ unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
+ LocBucket /= 10;
+ return LocBucket + 1;
+ };
+
+ unsigned CoverageBucket = getCoverageBucket();
+
+ VarParamLocStats[CoverageBucket].Value++;
+ if (IsParam)
+ ParamLocStats[CoverageBucket].Value++;
+ else if (IsLocalVar)
+ LocalVarLocStats[CoverageBucket].Value++;
+}
+
+/// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
+/// and DeclLine. The identifier aims to be unique for any unique entities,
+/// but keeping the same among different instances of the same entity.
+static std::string constructDieID(DWARFDie Die,
+ StringRef Prefix = StringRef()) {
+ std::string IDStr;
+ llvm::raw_string_ostream ID(IDStr);
+ ID << Prefix
+ << Die.getName(DINameKind::LinkageName);
+
+ // Prefix + Name is enough for local variables and parameters.
+ if (!Prefix.empty() && !Prefix.equals("g"))
+ return ID.str();
+
+ auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
+ std::string File;
+ if (DeclFile) {
+ DWARFUnit *U = Die.getDwarfUnit();
+ if (const auto *LT = U->getContext().getLineTableForUnit(U))
+ if (LT->getFileNameByIndex(
+ dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
+ File = std::string(sys::path::filename(File));
+ }
+ ID << ":" << (File.empty() ? "/" : File);
+ ID << ":"
+ << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
+ return ID.str();
+}
+
+/// Return the number of bytes in the overlap of ranges A and B.
+static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
+ uint64_t Lower = std::max(A.LowPC, B.LowPC);
+ uint64_t Upper = std::min(A.HighPC, B.HighPC);
+ if (Lower >= Upper)
+ return 0;
+ return Upper - Lower;
+}
+
+/// Collect debug info quality metrics for one DIE.
+static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
+ const std::string &VarPrefix,
+ uint64_t BytesInScope, uint32_t InlineDepth,
+ StringMap<PerFunctionStats> &FnStatMap,
+ GlobalStats &GlobalStats,
+ LocationStats &LocStats,
+ AbstractOriginVarsTy *AbstractOriginVariables) {
+ const dwarf::Tag Tag = Die.getTag();
+ // Skip CU node.
+ if (Tag == dwarf::DW_TAG_compile_unit)
+ return;
+
+ bool HasLoc = false;
+ bool HasSrcLoc = false;
+ bool HasType = false;
+ uint64_t TotalBytesCovered = 0;
+ uint64_t ScopeBytesCovered = 0;
+ uint64_t BytesEntryValuesCovered = 0;
+ auto &FnStats = FnStatMap[FnPrefix];
+ bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
+ bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
+ bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
+ Die.find(dwarf::DW_AT_const_value);
+
+ // For zero covered inlined variables the locstats will be
+ // calculated later.
+ bool DeferLocStats = false;
+
+ if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
+ GlobalStats.CallSiteDIEs++;
+ return;
+ }
+
+ if (Tag == dwarf::DW_TAG_call_site_parameter ||
+ Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
+ GlobalStats.CallSiteParamDIEs++;
+ return;
+ }
+
+ if (!IsParam && !IsLocalVar && !IsConstantMember) {
+ // Not a variable or constant member.
+ return;
+ }
+
+ // Ignore declarations of global variables.
+ if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
+ return;
+
+ if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
+ Die.findRecursively(dwarf::DW_AT_decl_line))
+ HasSrcLoc = true;
+
+ if (Die.findRecursively(dwarf::DW_AT_type))
+ HasType = true;
+
+ if (Die.find(dwarf::DW_AT_abstract_origin)) {
+ if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) {
+ if (AbstractOriginVariables) {
+ auto Offset = Die.find(dwarf::DW_AT_abstract_origin);
+ // Do not track this variable any more, since it has location
+ // coverage.
+ llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue());
+ }
+ } else {
+ // The locstats will be handled at the end of
+ // the collectStatsRecursive().
+ DeferLocStats = true;
+ }
+ }
+
+ auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
+ DWARFUnit *U = Die.getDwarfUnit();
+ DataExtractor Data(toStringRef(D),
+ Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
+ DWARFExpression Expression(Data, U->getAddressByteSize(),
+ U->getFormParams().Format);
+ // Consider the expression containing the DW_OP_entry_value as
+ // an entry value.
+ return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) {
+ return Op.getCode() == dwarf::DW_OP_entry_value ||
+ Op.getCode() == dwarf::DW_OP_GNU_entry_value;
+ });
+ };
+
+ if (Die.find(dwarf::DW_AT_const_value)) {
+ // This catches constant members *and* variables.
+ HasLoc = true;
+ ScopeBytesCovered = BytesInScope;
+ TotalBytesCovered = BytesInScope;
+ } else {
+ // Handle variables and function arguments.
+ Expected<std::vector<DWARFLocationExpression>> Loc =
+ Die.getLocations(dwarf::DW_AT_location);
+ if (!Loc) {
+ consumeError(Loc.takeError());
+ } else {
+ HasLoc = true;
+ // Get PC coverage.
+ auto Default = find_if(
+ *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
+ if (Default != Loc->end()) {
+ // Assume the entire range is covered by a single location.
+ ScopeBytesCovered = BytesInScope;
+ TotalBytesCovered = BytesInScope;
+ } else {
+ // Caller checks this Expected result already, it cannot fail.
+ auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
+ for (auto Entry : *Loc) {
+ TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
+ uint64_t ScopeBytesCoveredByEntry = 0;
+ // Calculate how many bytes of the parent scope this entry covers.
+ // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
+ // address ranges defined by the bounded location descriptions of a
+ // location list may overlap". So in theory a variable can have
+ // multiple simultaneous locations, which would make this calculation
+ // misleading because we will count the overlapped areas
+ // twice. However, clang does not currently emit DWARF like this.
+ for (DWARFAddressRange R : ScopeRanges) {
+ ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
+ }
+ ScopeBytesCovered += ScopeBytesCoveredByEntry;
+ if (IsEntryValue(Entry.Expr))
+ BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
+ }
+ }
+ }
+ }
+
+ // Calculate the debug location statistics.
+ if (BytesInScope && !DeferLocStats) {
+ LocStats.NumVarParam.Value++;
+ if (IsParam)
+ LocStats.NumParam.Value++;
+ else if (IsLocalVar)
+ LocStats.NumVar.Value++;
+
+ collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
+ LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
+ IsLocalVar);
+ // Non debug entry values coverage statistics.
+ collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
+ LocStats.VarParamNonEntryValLocStats,
+ LocStats.ParamNonEntryValLocStats,
+ LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
+ }
+
+ // Collect PC range coverage data.
+ if (DWARFDie D =
+ Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
+ Die = D;
+
+ std::string VarID = constructDieID(Die, VarPrefix);
+ FnStats.VarsInFunction.insert(VarID);
+
+ GlobalStats.TotalBytesCovered += TotalBytesCovered;
+ if (BytesInScope) {
+ GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
+ GlobalStats.ScopeBytes += BytesInScope;
+ GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
+ if (IsParam) {
+ GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
+ GlobalStats.ParamScopeBytes += BytesInScope;
+ GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
+ } else if (IsLocalVar) {
+ GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
+ GlobalStats.LocalVarScopeBytes += BytesInScope;
+ GlobalStats.LocalVarScopeEntryValueBytesCovered +=
+ BytesEntryValuesCovered;
+ }
+ assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
+ }
+
+ if (IsConstantMember) {
+ FnStats.ConstantMembers++;
+ return;
+ }
+
+ FnStats.TotalVarWithLoc += (unsigned)HasLoc;
+
+ if (Die.find(dwarf::DW_AT_artificial)) {
+ FnStats.NumArtificial++;
+ return;
+ }
+
+ if (IsParam) {
+ FnStats.NumParams++;
+ if (HasType)
+ FnStats.NumParamTypes++;
+ if (HasSrcLoc)
+ FnStats.NumParamSourceLocations++;
+ if (HasLoc)
+ FnStats.NumParamLocations++;
+ } else if (IsLocalVar) {
+ FnStats.NumLocalVars++;
+ if (HasType)
+ FnStats.NumLocalVarTypes++;
+ if (HasSrcLoc)
+ FnStats.NumLocalVarSourceLocations++;
+ if (HasLoc)
+ FnStats.NumLocalVarLocations++;
+ }
+}
+
+/// Recursively collect variables from subprogram with DW_AT_inline attribute.
+static void collectAbstractOriginFnInfo(
+ DWARFDie Die, uint64_t SPOffset,
+ AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
+ AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
+ DWARFDie Child = Die.getFirstChild();
+ while (Child) {
+ const dwarf::Tag ChildTag = Child.getTag();
+ if (ChildTag == dwarf::DW_TAG_formal_parameter ||
+ ChildTag == dwarf::DW_TAG_variable) {
+ GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
+ LocalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
+ } else if (ChildTag == dwarf::DW_TAG_lexical_block)
+ collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo,
+ LocalAbstractOriginFnInfo);
+ Child = Child.getSibling();
+ }
+}
+
+/// Recursively collect debug info quality metrics.
+static void collectStatsRecursive(
+ DWARFDie Die, std::string FnPrefix, std::string VarPrefix,
+ uint64_t BytesInScope, uint32_t InlineDepth,
+ StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,
+ LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs,
+ AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
+ AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
+ FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,
+ AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {
+ // Skip NULL nodes.
+ if (Die.isNULL())
+ return;
+
+ const dwarf::Tag Tag = Die.getTag();
+ // Skip function types.
+ if (Tag == dwarf::DW_TAG_subroutine_type)
+ return;
+
+ // Handle any kind of lexical scope.
+ const bool HasAbstractOrigin = Die.find(dwarf::DW_AT_abstract_origin) != None;
+ 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;
+ // We want to know how many variables (with abstract_origin) don't have
+ // location info.
+ const bool IsCandidateForZeroLocCovTracking =
+ (IsInlinedFunction || (IsFunction && HasAbstractOrigin));
+
+ AbstractOriginVarsTy AbstractOriginVars;
+
+ // Get the vars of the inlined fn, so the locstats
+ // reports the missing vars (with coverage 0%).
+ if (IsCandidateForZeroLocCovTracking) {
+ auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
+ if (OffsetFn) {
+ uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
+ if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {
+ AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy];
+ AbstractOriginVarsPtr = &AbstractOriginVars;
+ } else {
+ // This means that the DW_AT_inline fn copy is out of order
+ // or that the abstract_origin references another CU,
+ // so this abstract origin instance will be processed later.
+ FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
+ AbstractOriginVarsPtr = nullptr;
+ }
+ }
+ }
+
+ if (IsFunction || IsInlinedFunction || IsBlock) {
+ // Reset VarPrefix when entering a new function.
+ if (IsFunction || IsInlinedFunction)
+ VarPrefix = "v";
+
+ // Ignore forward declarations.
+ if (Die.find(dwarf::DW_AT_declaration))
+ return;
+
+ // Check for call sites.
+ if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
+ GlobalStats.CallSiteEntries++;
+
+ // PC Ranges.
+ auto RangesOrError = Die.getAddressRanges();
+ if (!RangesOrError) {
+ llvm::consumeError(RangesOrError.takeError());
+ return;
+ }
+
+ auto Ranges = RangesOrError.get();
+ uint64_t BytesInThisScope = 0;
+ for (auto Range : Ranges)
+ BytesInThisScope += Range.HighPC - Range.LowPC;
+
+ // Count the function.
+ if (!IsBlock) {
+ // Skip over abstract origins, but collect variables
+ // from it so it can be used for location statistics
+ // for inlined instancies.
+ if (Die.find(dwarf::DW_AT_inline)) {
+ uint64_t SPOffset = Die.getOffset();
+ AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
+ collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
+ LocalAbstractOriginFnInfo);
+ return;
+ }
+
+ std::string FnID = constructDieID(Die);
+ // We've seen an instance of this function.
+ auto &FnStats = FnStatMap[FnID];
+ FnStats.IsFunction = true;
+ if (IsInlinedFunction) {
+ FnStats.NumFnInlined++;
+ if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
+ FnStats.NumAbstractOrigins++;
+ } else {
+ FnStats.NumFnOutOfLine++;
+ }
+ if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
+ Die.findRecursively(dwarf::DW_AT_decl_line))
+ FnStats.HasSourceLocation = true;
+ // Update function prefix.
+ FnPrefix = FnID;
+ }
+
+ 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, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
+ FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
+ }
+
+ // Set InlineDepth correctly for child recursion
+ if (IsFunction)
+ InlineDepth = 0;
+ else if (IsInlinedFunction)
+ ++InlineDepth;
+
+ // Traverse children.
+ unsigned LexicalBlockIndex = 0;
+ unsigned FormalParameterIndex = 0;
+ DWARFDie Child = Die.getFirstChild();
+ while (Child) {
+ std::string ChildVarPrefix = VarPrefix;
+ if (Child.getTag() == dwarf::DW_TAG_lexical_block)
+ ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
+ if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
+ ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
+
+ collectStatsRecursive(
+ Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
+ GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
+ LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
+ AbstractOriginVarsPtr);
+ Child = Child.getSibling();
+ }
+
+ if (!IsCandidateForZeroLocCovTracking)
+ return;
+
+ // After we have processed all vars of the inlined function (or function with
+ // an abstract_origin), we want to know how many variables have no location.
+ for (auto Offset : AbstractOriginVars) {
+ LocStats.NumVarParam++;
+ LocStats.VarParamLocStats[ZeroCoverageBucket]++;
+ auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
+ if (!FnDie)
+ continue;
+ auto Tag = FnDie.getTag();
+ if (Tag == dwarf::DW_TAG_formal_parameter) {
+ LocStats.NumParam++;
+ LocStats.ParamLocStats[ZeroCoverageBucket]++;
+ } else if (Tag == dwarf::DW_TAG_variable) {
+ LocStats.NumVar++;
+ LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
+ }
+ }
+}
+
+/// Print human-readable output.
+/// \{
+static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
+ if (Value == OverflowValue)
+ J.attribute(Key, "overflowed");
+ else
+ J.attribute(Key, Value);
+
+ LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
+}
+
+static void printLocationStats(json::OStream &J, const char *Key,
+ std::vector<SaturatingUINT64> &LocationStats) {
+ if (LocationStats[0].Value == OverflowValue)
+ J.attribute((Twine(Key) +
+ " with (0%,10%) of parent scope covered by DW_AT_location")
+ .str(),
+ "overflowed");
+ else
+ J.attribute(
+ (Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
+ .str(),
+ LocationStats[0].Value);
+ LLVM_DEBUG(
+ llvm::dbgs() << Key
+ << " with 0% of parent scope covered by DW_AT_location: \\"
+ << LocationStats[0].Value << '\n');
+
+ if (LocationStats[1].Value == OverflowValue)
+ J.attribute((Twine(Key) +
+ " with (0%,10%) of parent scope covered by DW_AT_location")
+ .str(),
+ "overflowed");
+ else
+ J.attribute((Twine(Key) +
+ " with (0%,10%) of parent scope covered by DW_AT_location")
+ .str(),
+ LocationStats[1].Value);
+ LLVM_DEBUG(llvm::dbgs()
+ << Key
+ << " with (0%,10%) of parent scope covered by DW_AT_location: "
+ << LocationStats[1].Value << '\n');
+
+ for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
+ if (LocationStats[i].Value == OverflowValue)
+ J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
+ Twine(i * 10) +
+ "%) of parent scope covered by DW_AT_location")
+ .str(),
+ "overflowed");
+ else
+ J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
+ Twine(i * 10) +
+ "%) of parent scope covered by DW_AT_location")
+ .str(),
+ LocationStats[i].Value);
+ LLVM_DEBUG(llvm::dbgs()
+ << Key << " with [" << (i - 1) * 10 << "%," << i * 10
+ << "%) of parent scope covered by DW_AT_location: "
+ << LocationStats[i].Value);
+ }
+ if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
+ J.attribute(
+ (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
+ .str(),
+ "overflowed");
+ else
+ J.attribute(
+ (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
+ .str(),
+ LocationStats[NumOfCoverageCategories - 1].Value);
+ LLVM_DEBUG(
+ llvm::dbgs() << Key
+ << " with 100% of parent scope covered by DW_AT_location: "
+ << LocationStats[NumOfCoverageCategories - 1].Value);
+}
+
+static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
+ for (const auto &It : Sizes.DebugSectionSizes)
+ J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
+}
+
+/// Stop tracking variables that contain abstract_origin with a location.
+/// This is used for out-of-order DW_AT_inline subprograms only.
+static void updateVarsWithAbstractOriginLocCovInfo(
+ DWARFDie FnDieWithAbstractOrigin,
+ AbstractOriginVarsTy &AbstractOriginVars) {
+ DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
+ while (Child) {
+ const dwarf::Tag ChildTag = Child.getTag();
+ if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
+ ChildTag == dwarf::DW_TAG_variable) &&
+ (Child.find(dwarf::DW_AT_location) ||
+ Child.find(dwarf::DW_AT_const_value))) {
+ auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
+ if (OffsetVar)
+ llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue());
+ } else if (ChildTag == dwarf::DW_TAG_lexical_block)
+ updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
+ Child = Child.getSibling();
+ }
+}
+
+/// Collect zero location coverage for inlined variables which refer to
+/// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
+/// Also cover the variables of a concrete function (represented with
+/// the DW_TAG_subprogram) with an abstract_origin attribute.
+static void collectZeroLocCovForVarsWithAbstractOrigin(
+ DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
+ AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
+ FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
+ // The next variable is used to filter out functions that have been processed,
+ // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
+ FunctionsWithAbstractOriginTy ProcessedFns;
+ for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
+ DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);
+ auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);
+ AbstractOriginVarsTy AbstractOriginVars;
+ if (!FnCopy)
+ continue;
+ uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
+ // If there is no entry within LocalAbstractOriginFnInfo for the given
+ // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
+ // CrossCU referencing.
+ if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue))
+ continue;
+ AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue];
+ updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
+ AbstractOriginVars);
+
+ for (auto Offset : AbstractOriginVars) {
+ LocStats.NumVarParam++;
+ LocStats.VarParamLocStats[ZeroCoverageBucket]++;
+ auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
+ if (Tag == dwarf::DW_TAG_formal_parameter) {
+ LocStats.NumParam++;
+ LocStats.ParamLocStats[ZeroCoverageBucket]++;
+ } else if (Tag == dwarf::DW_TAG_variable) {
+ LocStats.NumVar++;
+ LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
+ }
+ }
+ ProcessedFns.push_back(FnOffset);
+ }
+ for (auto ProcessedFn : ProcessedFns)
+ llvm::erase_value(FnsWithAbstractOriginToBeProcessed, ProcessedFn);
+}
+
+/// Collect zero location coverage for inlined variables which refer to
+/// a DW_AT_inline copy of subprogram that is in a different CU.
+static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
+ LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
+ AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
+ CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
+ for (const auto &CrossCUReferenceToBeResolved :
+ CrossCUReferencesToBeResolved) {
+ DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
+ DWARFDie FnDIEWithCrossCUReferencing =
+ DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset);
+ auto FnCopy =
+ FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);
+ if (!FnCopy)
+ continue;
+ uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
+ AbstractOriginVarsTy AbstractOriginVars =
+ GlobalAbstractOriginFnInfo[FnCopyRawUValue];
+ updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,
+ AbstractOriginVars);
+ for (auto Offset : AbstractOriginVars) {
+ LocStats.NumVarParam++;
+ LocStats.VarParamLocStats[ZeroCoverageBucket]++;
+ auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
+ ->getDIEForOffset(Offset)
+ .getTag();
+ if (Tag == dwarf::DW_TAG_formal_parameter) {
+ LocStats.NumParam++;
+ LocStats.ParamLocStats[ZeroCoverageBucket]++;
+ } else if (Tag == dwarf::DW_TAG_variable) {
+ LocStats.NumVar++;
+ LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
+ }
+ }
+ }
+}
+
+/// \}
+
+/// Collect debug info quality metrics for an entire DIContext.
+///
+/// Do the impossible and reduce the quality of the debug info down to a few
+/// numbers. The idea is to condense the data into numbers that can be tracked
+/// over time to identify trends in newer compiler versions and gauge the effect
+/// of particular optimizations. The raw numbers themselves are not particularly
+/// useful, only the delta between compiling the same program with different
+/// compilers is.
+bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
+ const Twine &Filename,
+ raw_ostream &OS) {
+ StringRef FormatName = Obj.getFileFormatName();
+ GlobalStats GlobalStats;
+ LocationStats LocStats;
+ StringMap<PerFunctionStats> Statistics;
+ // This variable holds variable information for functions with
+ // abstract_origin globally, across all CUs.
+ AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
+ // This variable holds information about the CU of a function with
+ // abstract_origin.
+ FunctionDIECUTyMap AbstractOriginFnCUs;
+ CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
+ for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
+ if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
+ // This variable holds variable information for functions with
+ // abstract_origin, but just for the current CU.
+ AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
+ FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
+
+ collectStatsRecursive(
+ CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,
+ AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
+ LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
+
+ // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
+ // out-of-order DWARF functions that have been processed within it,
+ // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
+ // references.
+ collectZeroLocCovForVarsWithAbstractOrigin(
+ CUDie.getDwarfUnit(), GlobalStats, LocStats,
+ LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
+
+ // Collect all CrossCU references into CrossCUReferencesToBeResolved.
+ for (auto CrossCUReferencingDIEOffset :
+ FnsWithAbstractOriginToBeProcessed)
+ CrossCUReferencesToBeResolved.push_back(
+ DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
+ }
+ }
+
+ /// Resolve CrossCU references.
+ collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
+ LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
+ CrossCUReferencesToBeResolved);
+
+ /// Collect the sizes of debug sections.
+ SectionSizes Sizes;
+ calculateSectionSizes(Obj, Sizes, Filename);
+
+ /// The version number should be increased every time the algorithm is changed
+ /// (including bug fixes). New metrics may be added without increasing the
+ /// version.
+ unsigned Version = 9;
+ SaturatingUINT64 VarParamTotal = 0;
+ SaturatingUINT64 VarParamUnique = 0;
+ SaturatingUINT64 VarParamWithLoc = 0;
+ SaturatingUINT64 NumFunctions = 0;
+ SaturatingUINT64 NumInlinedFunctions = 0;
+ SaturatingUINT64 NumFuncsWithSrcLoc = 0;
+ SaturatingUINT64 NumAbstractOrigins = 0;
+ SaturatingUINT64 ParamTotal = 0;
+ SaturatingUINT64 ParamWithType = 0;
+ SaturatingUINT64 ParamWithLoc = 0;
+ SaturatingUINT64 ParamWithSrcLoc = 0;
+ SaturatingUINT64 LocalVarTotal = 0;
+ SaturatingUINT64 LocalVarWithType = 0;
+ SaturatingUINT64 LocalVarWithSrcLoc = 0;
+ SaturatingUINT64 LocalVarWithLoc = 0;
+ for (auto &Entry : Statistics) {
+ PerFunctionStats &Stats = Entry.getValue();
+ uint64_t TotalVars = Stats.VarsInFunction.size() *
+ (Stats.NumFnInlined + Stats.NumFnOutOfLine);
+ // Count variables in global scope.
+ if (!Stats.IsFunction)
+ TotalVars =
+ Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
+ uint64_t Constants = Stats.ConstantMembers;
+ VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
+ VarParamTotal += TotalVars;
+ VarParamUnique += Stats.VarsInFunction.size();
+ LLVM_DEBUG(for (auto &V
+ : Stats.VarsInFunction) llvm::dbgs()
+ << Entry.getKey() << ": " << V.getKey() << "\n");
+ NumFunctions += Stats.IsFunction;
+ NumFuncsWithSrcLoc += Stats.HasSourceLocation;
+ NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
+ NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
+ ParamTotal += Stats.NumParams;
+ ParamWithType += Stats.NumParamTypes;
+ ParamWithLoc += Stats.NumParamLocations;
+ ParamWithSrcLoc += Stats.NumParamSourceLocations;
+ LocalVarTotal += Stats.NumLocalVars;
+ LocalVarWithType += Stats.NumLocalVarTypes;
+ LocalVarWithLoc += Stats.NumLocalVarLocations;
+ LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
+ }
+
+ // Print summary.
+ OS.SetBufferSize(1024);
+ json::OStream J(OS, 2);
+ J.objectBegin();
+ J.attribute("version", Version);
+ LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
+ llvm::dbgs() << "---------------------------------\n");
+
+ printDatum(J, "file", Filename.str());
+ printDatum(J, "format", FormatName);
+
+ printDatum(J, "#functions", NumFunctions.Value);
+ printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);
+ printDatum(J, "#inlined functions", NumInlinedFunctions.Value);
+ printDatum(J, "#inlined functions with abstract origins",
+ NumAbstractOrigins.Value);
+
+ // This includes local variables and formal parameters.
+ printDatum(J, "#unique source variables", VarParamUnique.Value);
+ printDatum(J, "#source variables", VarParamTotal.Value);
+ printDatum(J, "#source variables with location", VarParamWithLoc.Value);
+
+ printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);
+ printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);
+ printDatum(J, "#call site parameter DIEs",
+ GlobalStats.CallSiteParamDIEs.Value);
+
+ printDatum(J, "sum_all_variables(#bytes in parent scope)",
+ GlobalStats.ScopeBytes.Value);
+ printDatum(J,
+ "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
+ GlobalStats.TotalBytesCovered.Value);
+ printDatum(J,
+ "sum_all_variables(#bytes in parent scope covered by "
+ "DW_AT_location)",
+ GlobalStats.ScopeBytesCovered.Value);
+ printDatum(J,
+ "sum_all_variables(#bytes in parent scope covered by "
+ "DW_OP_entry_value)",
+ GlobalStats.ScopeEntryValueBytesCovered.Value);
+
+ printDatum(J, "sum_all_params(#bytes in parent scope)",
+ GlobalStats.ParamScopeBytes.Value);
+ printDatum(J,
+ "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
+ GlobalStats.ParamScopeBytesCovered.Value);
+ printDatum(J,
+ "sum_all_params(#bytes in parent scope covered by "
+ "DW_OP_entry_value)",
+ GlobalStats.ParamScopeEntryValueBytesCovered.Value);
+
+ printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
+ GlobalStats.LocalVarScopeBytes.Value);
+ printDatum(J,
+ "sum_all_local_vars(#bytes in parent scope covered by "
+ "DW_AT_location)",
+ GlobalStats.LocalVarScopeBytesCovered.Value);
+ printDatum(J,
+ "sum_all_local_vars(#bytes in parent scope covered by "
+ "DW_OP_entry_value)",
+ GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
+
+ printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
+ printDatum(J, "#bytes within inlined functions",
+ GlobalStats.InlineFunctionSize.Value);
+
+ // Print the summary for formal parameters.
+ printDatum(J, "#params", ParamTotal.Value);
+ printDatum(J, "#params with source location", ParamWithSrcLoc.Value);
+ printDatum(J, "#params with type", ParamWithType.Value);
+ printDatum(J, "#params with binary location", ParamWithLoc.Value);
+
+ // Print the summary for local variables.
+ printDatum(J, "#local vars", LocalVarTotal.Value);
+ printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);
+ printDatum(J, "#local vars with type", LocalVarWithType.Value);
+ printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);
+
+ // Print the debug section sizes.
+ printSectionSizes(J, Sizes);
+
+ // Print the location statistics for variables (includes local variables
+ // and formal parameters).
+ printDatum(J, "#variables processed by location statistics",
+ LocStats.NumVarParam.Value);
+ printLocationStats(J, "#variables", LocStats.VarParamLocStats);
+ printLocationStats(J, "#variables - entry values",
+ LocStats.VarParamNonEntryValLocStats);
+
+ // Print the location statistics for formal parameters.
+ printDatum(J, "#params processed by location statistics",
+ LocStats.NumParam.Value);
+ printLocationStats(J, "#params", LocStats.ParamLocStats);
+ printLocationStats(J, "#params - entry values",
+ LocStats.ParamNonEntryValLocStats);
+
+ // Print the location statistics for local variables.
+ printDatum(J, "#local vars processed by location statistics",
+ LocStats.NumVar.Value);
+ printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
+ printLocationStats(J, "#local vars - entry values",
+ LocStats.LocalVarNonEntryValLocStats);
+ J.objectEnd();
+ OS << '\n';
+ LLVM_DEBUG(llvm::dbgs() << "Total Availability: "
+ << (int)std::round((VarParamWithLoc.Value * 100.0) /
+ VarParamTotal.Value)
+ << "%\n";
+ llvm::dbgs() << "PC Ranges covered: "
+ << (int)std::round(
+ (GlobalStats.ScopeBytesCovered.Value * 100.0) /
+ GlobalStats.ScopeBytes.Value)
+ << "%\n");
+ return true;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/contrib/libs/llvm14/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
new file mode 100644
index 00000000000..9c2ddc3867a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -0,0 +1,686 @@
+//===-- llvm-dwarfdump.cpp - Debug info dumping utility for llvm ----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is a utility that works like "dwarfdump".
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-dwarfdump.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstdlib>
+
+using namespace llvm;
+using namespace llvm::dwarfdump;
+using namespace llvm::object;
+
+namespace {
+/// Parser for options that take an optional offest argument.
+/// @{
+struct OffsetOption {
+ uint64_t Val = 0;
+ bool HasValue = false;
+ bool IsRequested = false;
+};
+struct BoolOption : public OffsetOption {};
+} // namespace
+
+namespace llvm {
+namespace cl {
+template <>
+class parser<OffsetOption> final : public basic_parser<OffsetOption> {
+public:
+ parser(Option &O) : basic_parser(O) {}
+
+ /// Return true on error.
+ bool parse(Option &O, StringRef ArgName, StringRef Arg, OffsetOption &Val) {
+ if (Arg == "") {
+ Val.Val = 0;
+ Val.HasValue = false;
+ Val.IsRequested = true;
+ return false;
+ }
+ if (Arg.getAsInteger(0, Val.Val))
+ return O.error("'" + Arg + "' value invalid for integer argument");
+ Val.HasValue = true;
+ Val.IsRequested = true;
+ return false;
+ }
+
+ enum ValueExpected getValueExpectedFlagDefault() const {
+ return ValueOptional;
+ }
+
+ StringRef getValueName() const override { return StringRef("offset"); }
+
+ void printOptionDiff(const Option &O, OffsetOption V, OptVal Default,
+ size_t GlobalWidth) const {
+ printOptionName(O, GlobalWidth);
+ outs() << "[=offset]";
+ }
+};
+
+template <> class parser<BoolOption> final : public basic_parser<BoolOption> {
+public:
+ parser(Option &O) : basic_parser(O) {}
+
+ /// Return true on error.
+ bool parse(Option &O, StringRef ArgName, StringRef Arg, BoolOption &Val) {
+ if (Arg != "")
+ return O.error("this is a flag and does not take a value");
+ Val.Val = 0;
+ Val.HasValue = false;
+ Val.IsRequested = true;
+ return false;
+ }
+
+ enum ValueExpected getValueExpectedFlagDefault() const {
+ return ValueOptional;
+ }
+
+ StringRef getValueName() const override { return StringRef(); }
+
+ void printOptionDiff(const Option &O, OffsetOption V, OptVal Default,
+ size_t GlobalWidth) const {
+ printOptionName(O, GlobalWidth);
+ }
+};
+} // namespace cl
+} // namespace llvm
+
+/// @}
+/// Command line options.
+/// @{
+
+namespace {
+using namespace cl;
+
+OptionCategory DwarfDumpCategory("Specific Options");
+static list<std::string>
+ InputFilenames(Positional, desc("<input object files or .dSYM bundles>"),
+ ZeroOrMore, cat(DwarfDumpCategory));
+
+cl::OptionCategory SectionCategory("Section-specific Dump Options",
+ "These control which sections are dumped. "
+ "Where applicable these parameters take an "
+ "optional =<offset> argument to dump only "
+ "the entry at the specified offset.");
+
+static opt<bool> DumpAll("all", desc("Dump all debug info sections"),
+ cat(SectionCategory));
+static alias DumpAllAlias("a", desc("Alias for --all"), aliasopt(DumpAll),
+ cl::NotHidden);
+
+// Options for dumping specific sections.
+static unsigned DumpType = DIDT_Null;
+static std::array<llvm::Optional<uint64_t>, (unsigned)DIDT_ID_Count>
+ DumpOffsets;
+#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
+ static opt<OPTION> Dump##ENUM_NAME(CMDLINE_NAME, \
+ desc("Dump the " ELF_NAME " section"), \
+ cat(SectionCategory));
+#include "llvm/BinaryFormat/Dwarf.def"
+#undef HANDLE_DWARF_SECTION
+
+// The aliased DumpDebugFrame is created by the Dwarf.def x-macro just above.
+static alias DumpDebugFrameAlias("eh-frame", desc("Alias for --debug-frame"),
+ NotHidden, cat(SectionCategory),
+ aliasopt(DumpDebugFrame));
+static list<std::string>
+ ArchFilters("arch",
+ desc("Dump debug information for the specified CPU "
+ "architecture only. Architectures may be specified by "
+ "name or by number. This option can be specified "
+ "multiple times, once for each desired architecture."),
+ cat(DwarfDumpCategory));
+static opt<bool>
+ Diff("diff",
+ desc("Emit diff-friendly output by omitting offsets and addresses."),
+ cat(DwarfDumpCategory));
+static list<std::string>
+ Find("find",
+ desc("Search for the exact match for <name> in the accelerator tables "
+ "and print the matching debug information entries. When no "
+ "accelerator tables are available, the slower but more complete "
+ "-name option can be used instead."),
+ value_desc("name"), cat(DwarfDumpCategory));
+static alias FindAlias("f", desc("Alias for --find."), aliasopt(Find),
+ cl::NotHidden);
+static opt<bool> IgnoreCase("ignore-case",
+ desc("Ignore case distinctions when using --name."),
+ value_desc("i"), cat(DwarfDumpCategory));
+static alias IgnoreCaseAlias("i", desc("Alias for --ignore-case."),
+ aliasopt(IgnoreCase), cl::NotHidden);
+static list<std::string> Name(
+ "name",
+ desc("Find and print all debug info entries whose name (DW_AT_name "
+ "attribute) matches the exact text in <pattern>. When used with the "
+ "the -regex option <pattern> is interpreted as a regular expression."),
+ value_desc("pattern"), cat(DwarfDumpCategory));
+static alias NameAlias("n", desc("Alias for --name"), aliasopt(Name),
+ cl::NotHidden);
+static opt<uint64_t>
+ Lookup("lookup",
+ 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>
+ OutputFilename("o", cl::init("-"),
+ cl::desc("Redirect output to the specified file."),
+ cl::value_desc("filename"), cat(DwarfDumpCategory));
+static alias OutputFilenameAlias("out-file", desc("Alias for -o."),
+ aliasopt(OutputFilename));
+static opt<bool> UseRegex(
+ "regex",
+ desc("Treat any <pattern> strings as regular "
+ "expressions when searching with --name. If --ignore-case is also "
+ "specified, the regular expression becomes case-insensitive."),
+ cat(DwarfDumpCategory));
+static alias RegexAlias("x", desc("Alias for --regex"), aliasopt(UseRegex),
+ cl::NotHidden);
+static opt<bool>
+ ShowChildren("show-children",
+ desc("Show a debug info entry's children when selectively "
+ "printing entries."),
+ cat(DwarfDumpCategory));
+static alias ShowChildrenAlias("c", desc("Alias for --show-children."),
+ aliasopt(ShowChildren), cl::NotHidden);
+static opt<bool>
+ ShowParents("show-parents",
+ desc("Show a debug info entry's parents when selectively "
+ "printing entries."),
+ cat(DwarfDumpCategory));
+static alias ShowParentsAlias("p", desc("Alias for --show-parents."),
+ aliasopt(ShowParents), cl::NotHidden);
+static opt<bool>
+ ShowForm("show-form",
+ desc("Show DWARF form types after the DWARF attribute types."),
+ cat(DwarfDumpCategory));
+static alias ShowFormAlias("F", desc("Alias for --show-form."),
+ aliasopt(ShowForm), cat(DwarfDumpCategory),
+ cl::NotHidden);
+static opt<unsigned>
+ ChildRecurseDepth("recurse-depth",
+ desc("Only recurse to a depth of N when displaying "
+ "children of debug info entries."),
+ cat(DwarfDumpCategory), init(-1U), value_desc("N"));
+static alias ChildRecurseDepthAlias("r", desc("Alias for --recurse-depth."),
+ aliasopt(ChildRecurseDepth), cl::NotHidden);
+static opt<unsigned>
+ ParentRecurseDepth("parent-recurse-depth",
+ desc("Only recurse to a depth of N when displaying "
+ "parents of debug info entries."),
+ cat(DwarfDumpCategory), init(-1U), value_desc("N"));
+static opt<bool>
+ SummarizeTypes("summarize-types",
+ desc("Abbreviate the description of type unit entries."),
+ cat(DwarfDumpCategory));
+static cl::opt<bool>
+ Statistics("statistics",
+ cl::desc("Emit JSON-formatted debug info quality metrics."),
+ cat(DwarfDumpCategory));
+static cl::opt<bool>
+ ShowSectionSizes("show-section-sizes",
+ cl::desc("Show the sizes of all debug sections, "
+ "expressed in bytes."),
+ cat(DwarfDumpCategory));
+static opt<bool> Verify("verify", desc("Verify the DWARF debug info."),
+ cat(DwarfDumpCategory));
+static opt<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."),
+ cat(DwarfDumpCategory));
+static opt<bool> DumpUUID("uuid", desc("Show the UUID for each architecture."),
+ cat(DwarfDumpCategory));
+static alias DumpUUIDAlias("u", desc("Alias for --uuid."), aliasopt(DumpUUID),
+ cl::NotHidden);
+static opt<bool> Verbose("verbose",
+ desc("Print more low-level encoding details."),
+ cat(DwarfDumpCategory));
+static alias VerboseAlias("v", desc("Alias for --verbose."), aliasopt(Verbose),
+ cat(DwarfDumpCategory), cl::NotHidden);
+static cl::extrahelp
+ HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
+} // namespace
+/// @}
+//===----------------------------------------------------------------------===//
+
+static void error(Error Err) {
+ if (!Err)
+ return;
+ WithColor::error() << toString(std::move(Err)) << "\n";
+ exit(1);
+}
+
+static void error(StringRef Prefix, Error Err) {
+ if (!Err)
+ return;
+ WithColor::error() << Prefix << ": " << toString(std::move(Err)) << "\n";
+ exit(1);
+}
+
+static void error(StringRef Prefix, std::error_code EC) {
+ error(Prefix, errorCodeToError(EC));
+}
+
+static DIDumpOptions getDumpOpts(DWARFContext &C) {
+ DIDumpOptions DumpOpts;
+ DumpOpts.DumpType = DumpType;
+ DumpOpts.ChildRecurseDepth = ChildRecurseDepth;
+ DumpOpts.ParentRecurseDepth = ParentRecurseDepth;
+ DumpOpts.ShowAddresses = !Diff;
+ DumpOpts.ShowChildren = ShowChildren;
+ DumpOpts.ShowParents = ShowParents;
+ DumpOpts.ShowForm = ShowForm;
+ DumpOpts.SummarizeTypes = SummarizeTypes;
+ DumpOpts.Verbose = Verbose;
+ DumpOpts.RecoverableErrorHandler = C.getRecoverableErrorHandler();
+ // In -verify mode, print DIEs without children in error messages.
+ if (Verify) {
+ DumpOpts.Verbose = true;
+ return DumpOpts.noImplicitRecursion();
+ }
+ return DumpOpts;
+}
+
+static uint32_t getCPUType(MachOObjectFile &MachO) {
+ if (MachO.is64Bit())
+ return MachO.getHeader64().cputype;
+ else
+ return MachO.getHeader().cputype;
+}
+
+/// Return true if the object file has not been filtered by an --arch option.
+static bool filterArch(ObjectFile &Obj) {
+ if (ArchFilters.empty())
+ return true;
+
+ if (auto *MachO = dyn_cast<MachOObjectFile>(&Obj)) {
+ for (auto Arch : ArchFilters) {
+ // Match architecture number.
+ unsigned Value;
+ if (!StringRef(Arch).getAsInteger(0, Value))
+ if (Value == getCPUType(*MachO))
+ return true;
+
+ // Match as name.
+ if (MachO->getArchTriple().getArchName() == Triple(Arch).getArchName())
+ return true;
+ }
+ }
+ return false;
+}
+
+using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx,
+ const Twine &, raw_ostream &)>;
+
+/// Print only DIEs that have a certain name.
+static bool filterByName(const StringSet<> &Names, DWARFDie Die,
+ StringRef NameRef, raw_ostream &OS) {
+ DIDumpOptions DumpOpts = getDumpOpts(Die.getDwarfUnit()->getContext());
+ 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, DumpOpts);
+ return true;
+ }
+ }
+ } else if (Names.count(Name)) {
+ // Match full text.
+ Die.dump(OS, 0, DumpOpts);
+ return true;
+ }
+ return false;
+}
+
+/// Print only DIEs that have a certain name.
+static void filterByName(const StringSet<> &Names,
+ 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 *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,
+ StringRef Name, SmallVectorImpl<DWARFDie> &Dies) {
+ for (const auto &Entry : Accel.equal_range(Name)) {
+ if (llvm::Optional<uint64_t> Off = Entry.getDIESectionOffset()) {
+ if (DWARFDie Die = DICtx.getDIEForOffset(*Off))
+ Dies.push_back(Die);
+ }
+ }
+}
+
+static DWARFDie toDie(const DWARFDebugNames::Entry &Entry,
+ DWARFContext &DICtx) {
+ llvm::Optional<uint64_t> CUOff = Entry.getCUOffset();
+ llvm::Optional<uint64_t> Off = Entry.getDIEUnitOffset();
+ if (!CUOff || !Off)
+ return DWARFDie();
+
+ DWARFCompileUnit *CU = DICtx.getCompileUnitForOffset(*CUOff);
+ if (!CU)
+ return DWARFDie();
+
+ if (llvm::Optional<uint64_t> DWOId = CU->getDWOId()) {
+ // This is a skeleton unit. Look up the DIE in the DWO unit.
+ CU = DICtx.getDWOCompileUnitForHash(*DWOId);
+ if (!CU)
+ return DWARFDie();
+ }
+
+ return CU->getDIEForOffset(CU->getOffset() + *Off);
+}
+
+static void getDies(DWARFContext &DICtx, const DWARFDebugNames &Accel,
+ StringRef Name, SmallVectorImpl<DWARFDie> &Dies) {
+ for (const auto &Entry : Accel.equal_range(Name)) {
+ if (DWARFDie Die = toDie(Entry, DICtx))
+ Dies.push_back(Die);
+ }
+}
+
+/// Print only DIEs that have a certain name.
+static void filterByAccelName(ArrayRef<std::string> Names, DWARFContext &DICtx,
+ raw_ostream &OS) {
+ SmallVector<DWARFDie, 4> Dies;
+ for (const auto &Name : Names) {
+ getDies(DICtx, DICtx.getAppleNames(), Name, Dies);
+ getDies(DICtx, DICtx.getAppleTypes(), Name, Dies);
+ getDies(DICtx, DICtx.getAppleNamespaces(), Name, Dies);
+ getDies(DICtx, DICtx.getDebugNames(), Name, Dies);
+ }
+ llvm::sort(Dies);
+ Dies.erase(std::unique(Dies.begin(), Dies.end()), Dies.end());
+
+ DIDumpOptions DumpOpts = getDumpOpts(DICtx);
+ for (DWARFDie Die : Dies)
+ Die.dump(OS, 0, DumpOpts);
+}
+
+/// Handle the --lookup option and dump the DIEs and line info for the given
+/// address.
+/// TODO: specified Address for --lookup option could relate for several
+/// different sections(in case not-linked object file). llvm-dwarfdump
+/// need to do something with this: extend lookup option with section
+/// information or probably display all matched entries, or something else...
+static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address,
+ raw_ostream &OS) {
+ auto DIEsForAddr = DICtx.getDIEsForAddress(Lookup);
+
+ if (!DIEsForAddr)
+ return false;
+
+ DIDumpOptions DumpOpts = getDumpOpts(DICtx);
+ DumpOpts.ChildRecurseDepth = 0;
+ DIEsForAddr.CompileUnit->dump(OS, DumpOpts);
+ if (DIEsForAddr.FunctionDIE) {
+ DIEsForAddr.FunctionDIE.dump(OS, 2, DumpOpts);
+ if (DIEsForAddr.BlockDIE)
+ DIEsForAddr.BlockDIE.dump(OS, 4, DumpOpts);
+ }
+
+ // TODO: it is neccessary to set proper SectionIndex here.
+ // object::SectionedAddress::UndefSection works for only absolute addresses.
+ if (DILineInfo LineInfo = DICtx.getLineInfoForAddress(
+ {Lookup, object::SectionedAddress::UndefSection}))
+ LineInfo.dump(OS);
+
+ return true;
+}
+
+static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
+ const Twine &Filename, raw_ostream &OS) {
+ logAllUnhandledErrors(DICtx.loadRegisterInfo(Obj), errs(),
+ Filename.str() + ": ");
+ // The UUID dump already contains all the same information.
+ if (!(DumpType & DIDT_UUID) || DumpType == DIDT_All)
+ OS << Filename << ":\tfile format " << Obj.getFileFormatName() << '\n';
+
+ // Handle the --lookup option.
+ if (Lookup)
+ return lookup(Obj, DICtx, Lookup, OS);
+
+ // Handle the --name option.
+ if (!Name.empty()) {
+ StringSet<> Names;
+ for (auto name : Name)
+ Names.insert((IgnoreCase && !UseRegex) ? StringRef(name).lower() : name);
+
+ filterByName(Names, DICtx.normal_units(), OS);
+ filterByName(Names, DICtx.dwo_units(), OS);
+ return true;
+ }
+
+ // Handle the --find option and lower it to --debug-info=<offset>.
+ if (!Find.empty()) {
+ filterByAccelName(Find, DICtx, OS);
+ return true;
+ }
+
+ // Dump the complete DWARF structure.
+ DICtx.dump(OS, getDumpOpts(DICtx), DumpOffsets);
+ return true;
+}
+
+static bool verifyObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
+ const Twine &Filename, raw_ostream &OS) {
+ // Verify the DWARF and exit with non-zero exit status if verification
+ // fails.
+ raw_ostream &stream = Quiet ? nulls() : OS;
+ stream << "Verifying " << Filename.str() << ":\tfile format "
+ << Obj.getFileFormatName() << "\n";
+ bool Result = DICtx.verify(stream, getDumpOpts(DICtx));
+ if (Result)
+ stream << "No errors.\n";
+ else
+ stream << "Errors detected.\n";
+ return Result;
+}
+
+static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
+ HandlerFn HandleObj, raw_ostream &OS);
+
+static bool handleArchive(StringRef Filename, Archive &Arch,
+ HandlerFn HandleObj, raw_ostream &OS) {
+ bool Result = true;
+ Error Err = Error::success();
+ for (auto Child : Arch.children(Err)) {
+ auto BuffOrErr = Child.getMemoryBufferRef();
+ error(Filename, BuffOrErr.takeError());
+ auto NameOrErr = Child.getName();
+ error(Filename, NameOrErr.takeError());
+ std::string Name = (Filename + "(" + NameOrErr.get() + ")").str();
+ Result &= handleBuffer(Name, BuffOrErr.get(), HandleObj, OS);
+ }
+ error(Filename, std::move(Err));
+
+ return Result;
+}
+
+static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
+ HandlerFn HandleObj, raw_ostream &OS) {
+ Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Buffer);
+ error(Filename, BinOrErr.takeError());
+
+ bool Result = true;
+ auto RecoverableErrorHandler = [&](Error E) {
+ Result = false;
+ WithColor::defaultErrorHandler(std::move(E));
+ };
+ if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) {
+ if (filterArch(*Obj)) {
+ std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(
+ *Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",
+ RecoverableErrorHandler);
+ if (!HandleObj(*Obj, *DICtx, Filename, OS))
+ Result = false;
+ }
+ } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))
+ for (auto &ObjForArch : Fat->objects()) {
+ std::string ObjName =
+ (Filename + "(" + ObjForArch.getArchFlagName() + ")").str();
+ if (auto MachOOrErr = ObjForArch.getAsObjectFile()) {
+ auto &Obj = **MachOOrErr;
+ if (filterArch(Obj)) {
+ std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(
+ Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",
+ RecoverableErrorHandler);
+ if (!HandleObj(Obj, *DICtx, ObjName, OS))
+ Result = false;
+ }
+ continue;
+ } else
+ consumeError(MachOOrErr.takeError());
+ if (auto ArchiveOrErr = ObjForArch.getAsArchive()) {
+ error(ObjName, ArchiveOrErr.takeError());
+ if (!handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS))
+ Result = false;
+ continue;
+ } else
+ consumeError(ArchiveOrErr.takeError());
+ }
+ else if (auto *Arch = dyn_cast<Archive>(BinOrErr->get()))
+ Result = handleArchive(Filename, *Arch, HandleObj, OS);
+ return Result;
+}
+
+static bool handleFile(StringRef Filename, HandlerFn HandleObj,
+ raw_ostream &OS) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
+ MemoryBuffer::getFileOrSTDIN(Filename);
+ error(Filename, BuffOrErr.getError());
+ std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get());
+ return handleBuffer(Filename, *Buffer, HandleObj, OS);
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ // Flush outs() when printing to errs(). This avoids interleaving output
+ // between the two.
+ errs().tie(&outs());
+
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+
+ HideUnrelatedOptions(
+ {&DwarfDumpCategory, &SectionCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(
+ argc, argv,
+ "pretty-print DWARF debug information in object files"
+ " and debug info archives.\n");
+
+ // 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 1;
+ }
+
+ std::error_code EC;
+ ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_TextWithCRLF);
+ error("unable to open output file " + OutputFilename, EC);
+ // Don't remove output file if we exit with an error.
+ OutputFile.keep();
+
+ bool OffsetRequested = false;
+
+ // Defaults to dumping all sections, unless brief mode is specified in which
+ // case only the .debug_info section in dumped.
+#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
+ if (Dump##ENUM_NAME.IsRequested) { \
+ DumpType |= DIDT_##ENUM_NAME; \
+ if (Dump##ENUM_NAME.HasValue) { \
+ DumpOffsets[DIDT_ID_##ENUM_NAME] = Dump##ENUM_NAME.Val; \
+ OffsetRequested = true; \
+ } \
+ }
+#include "llvm/BinaryFormat/Dwarf.def"
+#undef HANDLE_DWARF_SECTION
+ if (DumpUUID)
+ DumpType |= DIDT_UUID;
+ if (DumpAll)
+ DumpType = DIDT_All;
+ if (DumpType == DIDT_Null) {
+ if (Verbose)
+ DumpType = DIDT_All;
+ else
+ DumpType = DIDT_DebugInfo;
+ }
+
+ // Unless dumping a specific DIE, default to --show-children.
+ if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() &&
+ Find.empty())
+ ShowChildren = true;
+
+ // Defaults to a.out if no filenames specified.
+ if (InputFilenames.empty())
+ InputFilenames.push_back("a.out");
+
+ // Expand any .dSYM bundles to the individual object files contained therein.
+ std::vector<std::string> Objects;
+ for (const auto &F : InputFilenames) {
+ if (auto DsymObjectsOrErr = MachOObjectFile::findDsymObjectMembers(F)) {
+ if (DsymObjectsOrErr->empty())
+ Objects.push_back(F);
+ else
+ llvm::append_range(Objects, *DsymObjectsOrErr);
+ } else {
+ error(DsymObjectsOrErr.takeError());
+ }
+ }
+
+ bool Success = true;
+ if (Verify) {
+ for (auto Object : Objects)
+ Success &= handleFile(Object, verifyObjectFile, OutputFile.os());
+ } else if (Statistics) {
+ for (auto Object : Objects)
+ Success &= handleFile(Object, collectStatsForObjectFile, OutputFile.os());
+ } else if (ShowSectionSizes) {
+ for (auto Object : Objects)
+ Success &= handleFile(Object, collectObjectSectionSizes, OutputFile.os());
+ } else {
+ for (auto Object : Objects)
+ Success &= handleFile(Object, dumpObjectFile, OutputFile.os());
+ }
+
+ return Success ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-dwarfdump/llvm-dwarfdump.h b/contrib/libs/llvm14/tools/llvm-dwarfdump/llvm-dwarfdump.h
new file mode 100644
index 00000000000..cf7da56c91f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-dwarfdump/llvm-dwarfdump.h
@@ -0,0 +1,45 @@
+//===-- llvm-dwarfdump - Debug info dumping utility -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H
+#define LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H
+
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace dwarfdump {
+
+/// Holds cumulative section sizes for an object file.
+struct SectionSizes {
+ /// Map of .debug section names and their sizes across all such-named
+ /// sections.
+ MapVector<std::string, uint64_t, StringMap<uint64_t>> DebugSectionSizes;
+ /// Total number of bytes of all sections.
+ uint64_t TotalObjectSize = 0;
+ /// Total number of bytes of all debug sections.
+ uint64_t TotalDebugSectionsSize = 0;
+};
+
+/// Calculate the section sizes.
+void calculateSectionSizes(const object::ObjectFile &Obj, SectionSizes &Sizes,
+ const Twine &Filename);
+
+bool collectStatsForObjectFile(object::ObjectFile &Obj, DWARFContext &DICtx,
+ const Twine &Filename, raw_ostream &OS);
+bool collectObjectSectionSizes(object::ObjectFile &Obj, DWARFContext &DICtx,
+ const Twine &Filename, raw_ostream &OS);
+
+} // namespace dwarfdump
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-dwarfdump/ya.make b/contrib/libs/llvm14/tools/llvm-dwarfdump/ya.make
new file mode 100644
index 00000000000..d4771720c8f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-dwarfdump/ya.make
@@ -0,0 +1,54 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-dwarfdump
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ SectionSizes.cpp
+ Statistics.cpp
+ llvm-dwarfdump.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-dwp/llvm-dwp.cpp b/contrib/libs/llvm14/tools/llvm-dwp/llvm-dwp.cpp
new file mode 100644
index 00000000000..4b6f7bc8dd3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-dwp/llvm-dwp.cpp
@@ -0,0 +1,199 @@
+//===-- llvm-dwp.cpp - Split DWARF merging tool for llvm ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A utility for merging DWARF 5 Split DWARF .dwo files into .dwp (DWARF
+// package files).
+//
+//===----------------------------------------------------------------------===//
+#include "llvm/DWP/DWP.h"
+#include "llvm/DWP/DWPError.h"
+#include "llvm/DWP/DWPStringPool.h"
+#include "llvm/MC/MCAsmBackend.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectWriter.h"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+static mc::RegisterMCTargetOptionsFlags MCTargetOptionsFlags;
+
+cl::OptionCategory DwpCategory("Specific Options");
+static cl::list<std::string> InputFiles(cl::Positional, cl::ZeroOrMore,
+ cl::desc("<input files>"),
+ cl::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 cl::opt<std::string> OutputFilename(cl::Required, "o",
+ cl::desc("Specify the output file."),
+ cl::value_desc("filename"),
+ cl::cat(DwpCategory));
+
+static Expected<SmallVector<std::string, 16>>
+getDWOFilenames(StringRef ExecFilename) {
+ auto ErrOrObj = object::ObjectFile::createObjectFile(ExecFilename);
+ if (!ErrOrObj)
+ return ErrOrObj.takeError();
+
+ const ObjectFile &Obj = *ErrOrObj.get().getBinary();
+ std::unique_ptr<DWARFContext> DWARFCtx = DWARFContext::create(Obj);
+
+ SmallVector<std::string, 16> DWOPaths;
+ for (const auto &CU : DWARFCtx->compile_units()) {
+ const DWARFDie &Die = CU->getUnitDIE();
+ std::string DWOName = dwarf::toString(
+ Die.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), "");
+ if (DWOName.empty())
+ continue;
+ std::string DWOCompDir =
+ dwarf::toString(Die.find(dwarf::DW_AT_comp_dir), "");
+ if (!DWOCompDir.empty()) {
+ SmallString<16> DWOPath(std::move(DWOName));
+ sys::fs::make_absolute(DWOCompDir, DWOPath);
+ DWOPaths.emplace_back(DWOPath.data(), DWOPath.size());
+ } else {
+ DWOPaths.push_back(std::move(DWOName));
+ }
+ }
+ return std::move(DWOPaths);
+}
+
+static int error(const Twine &Error, const Twine &Context) {
+ errs() << Twine("while processing ") + Context + ":\n";
+ errs() << Twine("error: ") + Error + "\n";
+ return 1;
+}
+
+static Expected<Triple> readTargetTriple(StringRef FileName) {
+ auto ErrOrObj = object::ObjectFile::createObjectFile(FileName);
+ if (!ErrOrObj)
+ return ErrOrObj.takeError();
+
+ return ErrOrObj->getBinary()->makeTriple();
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ cl::HideUnrelatedOptions({&DwpCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files\n");
+
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllAsmPrinters();
+
+ std::vector<std::string> DWOFilenames = InputFiles;
+ for (const auto &ExecFilename : ExecFilenames) {
+ auto DWOs = getDWOFilenames(ExecFilename);
+ if (!DWOs) {
+ logAllUnhandledErrors(DWOs.takeError(), WithColor::error());
+ return 1;
+ }
+ DWOFilenames.insert(DWOFilenames.end(),
+ std::make_move_iterator(DWOs->begin()),
+ std::make_move_iterator(DWOs->end()));
+ }
+
+ if (DWOFilenames.empty())
+ return 0;
+
+ std::string ErrorStr;
+ StringRef Context = "dwarf streamer init";
+
+ auto ErrOrTriple = readTargetTriple(DWOFilenames.front());
+ if (!ErrOrTriple) {
+ logAllUnhandledErrors(ErrOrTriple.takeError(), WithColor::error());
+ return 1;
+ }
+
+ // Get the target.
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget("", *ErrOrTriple, ErrorStr);
+ if (!TheTarget)
+ return error(ErrorStr, Context);
+ std::string TripleName = ErrOrTriple->getTriple();
+
+ // Create all the MC Objects.
+ std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
+ if (!MRI)
+ return error(Twine("no register info for target ") + TripleName, Context);
+
+ MCTargetOptions MCOptions = llvm::mc::InitMCTargetOptionsFromFlags();
+ std::unique_ptr<MCAsmInfo> MAI(
+ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ if (!MAI)
+ return error("no asm info for target " + TripleName, Context);
+
+ std::unique_ptr<MCSubtargetInfo> MSTI(
+ TheTarget->createMCSubtargetInfo(TripleName, "", ""));
+ if (!MSTI)
+ return error("no subtarget info for target " + TripleName, Context);
+
+ MCContext MC(*ErrOrTriple, MAI.get(), MRI.get(), MSTI.get());
+ std::unique_ptr<MCObjectFileInfo> MOFI(
+ TheTarget->createMCObjectFileInfo(MC, /*PIC=*/false));
+ MC.setObjectFileInfo(MOFI.get());
+
+ MCTargetOptions Options;
+ auto MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, Options);
+ if (!MAB)
+ return error("no asm backend for target " + TripleName, Context);
+
+ std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
+ if (!MII)
+ return error("no instr info info for target " + TripleName, Context);
+
+ MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, MC);
+ if (!MCE)
+ return error("no code emitter for target " + TripleName, Context);
+
+ // Create the output file.
+ std::error_code EC;
+ ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None);
+ Optional<buffer_ostream> BOS;
+ raw_pwrite_stream *OS;
+ if (EC)
+ return error(Twine(OutputFilename) + ": " + EC.message(), Context);
+ if (OutFile.os().supportsSeeking()) {
+ OS = &OutFile.os();
+ } else {
+ BOS.emplace(OutFile.os());
+ OS = BOS.getPointer();
+ }
+
+ std::unique_ptr<MCStreamer> MS(TheTarget->createMCObjectStreamer(
+ *ErrOrTriple, MC, std::unique_ptr<MCAsmBackend>(MAB),
+ MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(MCE), *MSTI,
+ MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible,
+ /*DWARFMustBeAtTheEnd*/ false));
+ if (!MS)
+ return error("no object streamer for target " + TripleName, Context);
+
+ if (auto Err = write(*MS, DWOFilenames)) {
+ logAllUnhandledErrors(std::move(Err), WithColor::error());
+ return 1;
+ }
+
+ MS->Finish();
+ OutFile.keep();
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-dwp/ya.make b/contrib/libs/llvm14/tools/llvm-dwp/ya.make
new file mode 100644
index 00000000000..522aee0c0c2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-dwp/ya.make
@@ -0,0 +1,80 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/AsmPrinter
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/CodeGen/SelectionDAG
+ contrib/libs/llvm14/lib/DWP
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/CFGuard
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-dwp
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-dwp.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/Target.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/Target.cpp
new file mode 100644
index 00000000000..c778b89032c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/Target.cpp
@@ -0,0 +1,76 @@
+//===-- Target.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "../Target.h"
+#include "AArch64.h"
+#include "AArch64RegisterInfo.h"
+
+namespace llvm {
+namespace exegesis {
+
+static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) {
+ switch (RegBitWidth) {
+ case 32:
+ return AArch64::MOVi32imm;
+ case 64:
+ return AArch64::MOVi64imm;
+ }
+ llvm_unreachable("Invalid Value Width");
+}
+
+// Generates instruction to load an immediate value into a register.
+static MCInst loadImmediate(unsigned Reg, unsigned RegBitWidth,
+ const APInt &Value) {
+ if (Value.getBitWidth() > RegBitWidth)
+ llvm_unreachable("Value must fit in the Register");
+ return MCInstBuilder(getLoadImmediateOpcode(RegBitWidth))
+ .addReg(Reg)
+ .addImm(Value.getZExtValue());
+}
+
+#include "AArch64GenExegesis.inc"
+
+namespace {
+
+class ExegesisAArch64Target : public ExegesisTarget {
+public:
+ ExegesisAArch64Target() : ExegesisTarget(AArch64CpuPfmCounters) {}
+
+private:
+ std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, unsigned Reg,
+ const APInt &Value) const override {
+ if (AArch64::GPR32RegClass.contains(Reg))
+ return {loadImmediate(Reg, 32, Value)};
+ if (AArch64::GPR64RegClass.contains(Reg))
+ return {loadImmediate(Reg, 64, Value)};
+ errs() << "setRegTo is not implemented, results will be unreliable\n";
+ return {};
+ }
+
+ bool matchesArch(Triple::ArchType Arch) const override {
+ return Arch == Triple::aarch64 || Arch == Triple::aarch64_be;
+ }
+
+ void addTargetSpecificPasses(PassManagerBase &PM) const override {
+ // Function return is a pseudo-instruction that needs to be expanded
+ PM.add(createAArch64ExpandPseudoPass());
+ }
+};
+
+} // namespace
+
+static ExegesisTarget *getTheExegesisAArch64Target() {
+ static ExegesisAArch64Target Target;
+ return &Target;
+}
+
+void InitializeAArch64ExegesisTarget() {
+ ExegesisTarget::registerTarget(getTheExegesisAArch64Target());
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/ya.make b/contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/ya.make
new file mode 100644
index 00000000000..084a26238ea
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64/ya.make
@@ -0,0 +1,37 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/tools/llvm-exegesis/lib
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/tools/llvm-exegesis/lib/AArch64
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ Target.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/Analysis.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Analysis.cpp
new file mode 100644
index 00000000000..b12f872a28d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Analysis.cpp
@@ -0,0 +1,608 @@
+//===-- Analysis.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Analysis.h"
+#include "BenchmarkResult.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <limits>
+#include <unordered_set>
+#include <vector>
+
+namespace llvm {
+namespace exegesis {
+
+static const char kCsvSep = ',';
+
+namespace {
+
+enum EscapeTag { kEscapeCsv, kEscapeHtml, kEscapeHtmlString };
+
+template <EscapeTag Tag> void writeEscaped(raw_ostream &OS, const StringRef S);
+
+template <> void writeEscaped<kEscapeCsv>(raw_ostream &OS, const StringRef S) {
+ if (!llvm::is_contained(S, kCsvSep)) {
+ OS << S;
+ } else {
+ // Needs escaping.
+ OS << '"';
+ for (const char C : S) {
+ if (C == '"')
+ OS << "\"\"";
+ else
+ OS << C;
+ }
+ OS << '"';
+ }
+}
+
+template <> void writeEscaped<kEscapeHtml>(raw_ostream &OS, const StringRef S) {
+ for (const char C : S) {
+ if (C == '<')
+ OS << "&lt;";
+ else if (C == '>')
+ OS << "&gt;";
+ else if (C == '&')
+ OS << "&amp;";
+ else
+ OS << C;
+ }
+}
+
+template <>
+void writeEscaped<kEscapeHtmlString>(raw_ostream &OS, const StringRef S) {
+ for (const char C : S) {
+ if (C == '"')
+ OS << "\\\"";
+ else
+ OS << C;
+ }
+}
+
+} // namespace
+
+template <EscapeTag Tag>
+static void
+writeClusterId(raw_ostream &OS,
+ const InstructionBenchmarkClustering::ClusterId &CID) {
+ if (CID.isNoise())
+ writeEscaped<Tag>(OS, "[noise]");
+ else if (CID.isError())
+ writeEscaped<Tag>(OS, "[error]");
+ else
+ OS << CID.getId();
+}
+
+template <EscapeTag Tag>
+static void writeMeasurementValue(raw_ostream &OS, const double Value) {
+ // 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, formatv(SimpleFloatFormat.data(), Value).sstr<SerializationLen>());
+}
+
+template <typename EscapeTag, EscapeTag Tag>
+void Analysis::writeSnippet(raw_ostream &OS, ArrayRef<uint8_t> Bytes,
+ const char *Separator) const {
+ SmallVector<std::string, 3> Lines;
+ // Parse the asm snippet and print it.
+ while (!Bytes.empty()) {
+ MCInst MI;
+ uint64_t MISize = 0;
+ if (!Disasm_->getInstruction(MI, MISize, Bytes, 0, nulls())) {
+ writeEscaped<Tag>(OS, join(Lines, Separator));
+ writeEscaped<Tag>(OS, Separator);
+ writeEscaped<Tag>(OS, "[error decoding asm snippet]");
+ return;
+ }
+ SmallString<128> InstPrinterStr; // FIXME: magic number.
+ raw_svector_ostream OSS(InstPrinterStr);
+ InstPrinter_->printInst(&MI, 0, "", *SubtargetInfo_, OSS);
+ Bytes = Bytes.drop_front(MISize);
+ Lines.emplace_back(InstPrinterStr.str().trim());
+ }
+ writeEscaped<Tag>(OS, join(Lines, Separator));
+}
+
+// Prints a row representing an instruction, along with scheduling info and
+// point coordinates (measurements).
+void Analysis::printInstructionRowCsv(const size_t PointId,
+ raw_ostream &OS) const {
+ const InstructionBenchmark &Point = Clustering_.getPoints()[PointId];
+ writeClusterId<kEscapeCsv>(OS, Clustering_.getClusterIdForPoint(PointId));
+ OS << kCsvSep;
+ writeSnippet<EscapeTag, kEscapeCsv>(OS, Point.AssembledSnippet, "; ");
+ OS << kCsvSep;
+ writeEscaped<kEscapeCsv>(OS, Point.Key.Config);
+ OS << kCsvSep;
+ assert(!Point.Key.Instructions.empty());
+ const MCInst &MCI = Point.keyInstruction();
+ unsigned SchedClassId;
+ std::tie(SchedClassId, std::ignore) = ResolvedSchedClass::resolveSchedClassId(
+ *SubtargetInfo_, *InstrInfo_, MCI);
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ const MCSchedClassDesc *const SCDesc =
+ 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.PerInstructionValue);
+ }
+ OS << "\n";
+}
+
+Analysis::Analysis(const Target &Target,
+ std::unique_ptr<MCSubtargetInfo> SubtargetInfo,
+ std::unique_ptr<MCInstrInfo> InstrInfo,
+ const InstructionBenchmarkClustering &Clustering,
+ double AnalysisInconsistencyEpsilon,
+ bool AnalysisDisplayUnstableOpcodes,
+ const std::string &ForceCpuName)
+ : Clustering_(Clustering), SubtargetInfo_(std::move(SubtargetInfo)),
+ InstrInfo_(std::move(InstrInfo)),
+ AnalysisInconsistencyEpsilonSquared_(AnalysisInconsistencyEpsilon *
+ AnalysisInconsistencyEpsilon),
+ AnalysisDisplayUnstableOpcodes_(AnalysisDisplayUnstableOpcodes) {
+ if (Clustering.getPoints().empty())
+ return;
+
+ const InstructionBenchmark &FirstPoint = Clustering.getPoints().front();
+ const std::string CpuName =
+ ForceCpuName.empty() ? FirstPoint.CpuName : ForceCpuName;
+ RegInfo_.reset(Target.createMCRegInfo(FirstPoint.LLVMTriple));
+ MCTargetOptions MCOptions;
+ AsmInfo_.reset(
+ Target.createMCAsmInfo(*RegInfo_, FirstPoint.LLVMTriple, MCOptions));
+ SubtargetInfo_.reset(
+ Target.createMCSubtargetInfo(FirstPoint.LLVMTriple, CpuName, ""));
+ InstPrinter_.reset(Target.createMCInstPrinter(
+ Triple(FirstPoint.LLVMTriple), 0 /*default variant*/, *AsmInfo_,
+ *InstrInfo_, *RegInfo_));
+
+ Context_ =
+ std::make_unique<MCContext>(Triple(FirstPoint.LLVMTriple), AsmInfo_.get(),
+ RegInfo_.get(), SubtargetInfo_.get());
+ Disasm_.reset(Target.createMCDisassembler(*SubtargetInfo_, *Context_));
+ assert(Disasm_ && "cannot create MCDisassembler. missing call to "
+ "InitializeXXXTargetDisassembler ?");
+}
+
+template <>
+Error Analysis::run<Analysis::PrintClusters>(raw_ostream &OS) const {
+ if (Clustering_.getPoints().empty())
+ return Error::success();
+
+ // Write the header.
+ OS << "cluster_id" << kCsvSep << "opcode_name" << kCsvSep << "config"
+ << kCsvSep << "sched_class";
+ for (const auto &Measurement : Clustering_.getPoints().front().Measurements) {
+ OS << kCsvSep;
+ writeEscaped<kEscapeCsv>(OS, Measurement.Key);
+ }
+ OS << "\n";
+
+ // Write the points.
+ for (const auto &ClusterIt : Clustering_.getValidClusters()) {
+ for (const size_t PointId : ClusterIt.PointIndices) {
+ printInstructionRowCsv(PointId, OS);
+ }
+ OS << "\n\n";
+ }
+ return Error::success();
+}
+
+Analysis::ResolvedSchedClassAndPoints::ResolvedSchedClassAndPoints(
+ ResolvedSchedClass &&RSC)
+ : RSC(std::move(RSC)) {}
+
+std::vector<Analysis::ResolvedSchedClassAndPoints>
+Analysis::makePointsPerSchedClass() const {
+ 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());
+ // FIXME: we should be using the tuple of classes for instructions in the
+ // snippet as key.
+ const MCInst &MCI = Point.keyInstruction();
+ unsigned SchedClassId;
+ bool WasVariant;
+ std::tie(SchedClassId, WasVariant) =
+ ResolvedSchedClass::resolveSchedClassId(*SubtargetInfo_, *InstrInfo_,
+ 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 Entries;
+}
+
+// Parallel benchmarks repeat the same opcode multiple times. Just show this
+// opcode and show the whole snippet only on hover.
+static void writeParallelSnippetHtml(raw_ostream &OS,
+ const std::vector<MCInst> &Instructions,
+ const MCInstrInfo &InstrInfo) {
+ if (Instructions.empty())
+ return;
+ writeEscaped<kEscapeHtml>(OS, InstrInfo.getName(Instructions[0].getOpcode()));
+ if (Instructions.size() > 1)
+ OS << " (x" << Instructions.size() << ")";
+}
+
+// Latency tries to find a serial path. Just show the opcode path and show the
+// whole snippet only on hover.
+static void writeLatencySnippetHtml(raw_ostream &OS,
+ const std::vector<MCInst> &Instructions,
+ const MCInstrInfo &InstrInfo) {
+ bool First = true;
+ for (const MCInst &Instr : Instructions) {
+ if (First)
+ First = false;
+ else
+ OS << " &rarr; ";
+ writeEscaped<kEscapeHtml>(OS, InstrInfo.getName(Instr.getOpcode()));
+ }
+}
+
+void Analysis::printPointHtml(const InstructionBenchmark &Point,
+ llvm::raw_ostream &OS) const {
+ OS << "<li><span class=\"mono\" title=\"";
+ writeSnippet<EscapeTag, kEscapeHtmlString>(OS, Point.AssembledSnippet, "\n");
+ OS << "\">";
+ switch (Point.Mode) {
+ case InstructionBenchmark::Latency:
+ writeLatencySnippetHtml(OS, Point.Key.Instructions, *InstrInfo_);
+ break;
+ case InstructionBenchmark::Uops:
+ case InstructionBenchmark::InverseThroughput:
+ writeParallelSnippetHtml(OS, Point.Key.Instructions, *InstrInfo_);
+ break;
+ default:
+ llvm_unreachable("invalid mode");
+ }
+ OS << "</span> <span class=\"mono\">";
+ writeEscaped<kEscapeHtml>(OS, Point.Key.Config);
+ OS << "</span></li>";
+}
+
+void Analysis::printSchedClassClustersHtml(
+ const std::vector<SchedClassCluster> &Clusters,
+ const ResolvedSchedClass &RSC, raw_ostream &OS) const {
+ const auto &Points = Clustering_.getPoints();
+ OS << "<table class=\"sched-class-clusters\">";
+ OS << "<tr><th>ClusterId</th><th>Opcode/Config</th>";
+ assert(!Clusters.empty());
+ for (const auto &Measurement :
+ Points[Clusters[0].getPointIds()[0]].Measurements) {
+ OS << "<th>";
+ writeEscaped<kEscapeHtml>(OS, Measurement.Key);
+ OS << "</th>";
+ }
+ OS << "</tr>";
+ for (const SchedClassCluster &Cluster : Clusters) {
+ OS << "<tr class=\""
+ << (Cluster.measurementsMatch(*SubtargetInfo_, RSC, Clustering_,
+ AnalysisInconsistencyEpsilonSquared_)
+ ? "good-cluster"
+ : "bad-cluster")
+ << "\"><td>";
+ writeClusterId<kEscapeHtml>(OS, Cluster.id());
+ OS << "</td><td><ul>";
+ for (const size_t PointId : Cluster.getPointIds()) {
+ printPointHtml(Points[PointId], OS);
+ }
+ OS << "</ul></td>";
+ for (const auto &Stats : Cluster.getCentroid().getStats()) {
+ OS << "<td class=\"measurement\">";
+ writeMeasurementValue<kEscapeHtml>(OS, Stats.avg());
+ OS << "<br><span class=\"minmax\">[";
+ writeMeasurementValue<kEscapeHtml>(OS, Stats.min());
+ OS << ";";
+ writeMeasurementValue<kEscapeHtml>(OS, Stats.max());
+ OS << "]</span></td>";
+ }
+ OS << "</tr>";
+ }
+ OS << "</table>";
+}
+
+void Analysis::SchedClassCluster::addPoint(
+ size_t PointId, const InstructionBenchmarkClustering &Clustering) {
+ PointIds.push_back(PointId);
+ const auto &Point = Clustering.getPoints()[PointId];
+ if (ClusterId.isUndef())
+ ClusterId = Clustering.getClusterIdForPoint(PointId);
+ assert(ClusterId == Clustering.getClusterIdForPoint(PointId));
+
+ Centroid.addPoint(Point.Measurements);
+}
+
+bool Analysis::SchedClassCluster::measurementsMatch(
+ const MCSubtargetInfo &STI, const ResolvedSchedClass &RSC,
+ const InstructionBenchmarkClustering &Clustering,
+ const double AnalysisInconsistencyEpsilonSquared_) const {
+ assert(!Clustering.getPoints().empty());
+ const InstructionBenchmark::ModeE Mode = Clustering.getPoints()[0].Mode;
+
+ if (!Centroid.validate(Mode))
+ return false;
+
+ const std::vector<BenchmarkMeasure> ClusterCenterPoint =
+ Centroid.getAsPoint();
+
+ const std::vector<BenchmarkMeasure> SchedClassPoint =
+ RSC.getAsPoint(Mode, STI, Centroid.getStats());
+ if (SchedClassPoint.empty())
+ return false; // In Uops mode validate() may not be enough.
+
+ assert(ClusterCenterPoint.size() == SchedClassPoint.size() &&
+ "Expected measured/sched data dimensions to match.");
+
+ return Clustering.isNeighbour(ClusterCenterPoint, SchedClassPoint,
+ AnalysisInconsistencyEpsilonSquared_);
+}
+
+void Analysis::printSchedClassDescHtml(const ResolvedSchedClass &RSC,
+ raw_ostream &OS) const {
+ OS << "<table class=\"sched-class-desc\">";
+ OS << "<tr><th>Valid</th><th>Variant</th><th>NumMicroOps</th><th>Latency</"
+ "th><th>RThroughput</th><th>WriteProcRes</th><th title=\"This is the "
+ "idealized unit resource (port) pressure assuming ideal "
+ "distribution\">Idealized Resource Pressure</th></tr>";
+ if (RSC.SCDesc->isValid()) {
+ const auto &SM = SubtargetInfo_->getSchedModel();
+ OS << "<tr><td>&#10004;</td>";
+ OS << "<td>" << (RSC.WasVariant ? "&#10004;" : "&#10005;") << "</td>";
+ OS << "<td>" << RSC.SCDesc->NumMicroOps << "</td>";
+ // Latencies.
+ OS << "<td><ul>";
+ for (int I = 0, E = RSC.SCDesc->NumWriteLatencyEntries; I < E; ++I) {
+ const auto *const Entry =
+ SubtargetInfo_->getWriteLatencyEntry(RSC.SCDesc, I);
+ OS << "<li>" << Entry->Cycles;
+ if (RSC.SCDesc->NumWriteLatencyEntries > 1) {
+ // Dismabiguate if more than 1 latency.
+ OS << " (WriteResourceID " << Entry->WriteResourceID << ")";
+ }
+ OS << "</li>";
+ }
+ OS << "</ul></td>";
+ // inverse throughput.
+ OS << "<td>";
+ writeMeasurementValue<kEscapeHtml>(
+ OS,
+ MCSchedModel::getReciprocalThroughput(*SubtargetInfo_, *RSC.SCDesc));
+ OS << "</td>";
+ // WriteProcRes.
+ OS << "<td><ul>";
+ for (const auto &WPR : RSC.NonRedundantWriteProcRes) {
+ OS << "<li><span class=\"mono\">";
+ writeEscaped<kEscapeHtml>(OS,
+ SM.getProcResource(WPR.ProcResourceIdx)->Name);
+ OS << "</span>: " << WPR.Cycles << "</li>";
+ }
+ OS << "</ul></td>";
+ // Idealized port pressure.
+ OS << "<td><ul>";
+ for (const auto &Pressure : RSC.IdealizedProcResPressure) {
+ OS << "<li><span class=\"mono\">";
+ writeEscaped<kEscapeHtml>(OS, SubtargetInfo_->getSchedModel()
+ .getProcResource(Pressure.first)
+ ->Name);
+ OS << "</span>: ";
+ writeMeasurementValue<kEscapeHtml>(OS, Pressure.second);
+ OS << "</li>";
+ }
+ OS << "</ul></td>";
+ OS << "</tr>";
+ } else {
+ OS << "<tr><td>&#10005;</td><td></td><td></td></tr>";
+ }
+ OS << "</table>";
+}
+
+void Analysis::printClusterRawHtml(
+ const InstructionBenchmarkClustering::ClusterId &Id, StringRef display_name,
+ llvm::raw_ostream &OS) const {
+ const auto &Points = Clustering_.getPoints();
+ const auto &Cluster = Clustering_.getCluster(Id);
+ if (Cluster.PointIndices.empty())
+ return;
+
+ OS << "<div class=\"inconsistency\"><p>" << display_name << " Cluster ("
+ << Cluster.PointIndices.size() << " points)</p>";
+ OS << "<table class=\"sched-class-clusters\">";
+ // Table Header.
+ OS << "<tr><th>ClusterId</th><th>Opcode/Config</th>";
+ for (const auto &Measurement : Points[Cluster.PointIndices[0]].Measurements) {
+ OS << "<th>";
+ writeEscaped<kEscapeHtml>(OS, Measurement.Key);
+ OS << "</th>";
+ }
+ OS << "</tr>";
+
+ // Point data.
+ for (const auto &PointId : Cluster.PointIndices) {
+ OS << "<tr class=\"bad-cluster\"><td>" << display_name << "</td><td><ul>";
+ printPointHtml(Points[PointId], OS);
+ OS << "</ul></td>";
+ for (const auto &Measurement : Points[PointId].Measurements) {
+ OS << "<td class=\"measurement\">";
+ writeMeasurementValue<kEscapeHtml>(OS, Measurement.PerInstructionValue);
+ }
+ OS << "</tr>";
+ }
+ OS << "</table>";
+
+ OS << "</div>";
+
+} // namespace exegesis
+
+static constexpr const char kHtmlHead[] = R"(
+<head>
+<title>llvm-exegesis Analysis Results</title>
+<style>
+body {
+ font-family: sans-serif
+}
+span.sched-class-name {
+ font-weight: bold;
+ font-family: monospace;
+}
+span.opcode {
+ font-family: monospace;
+}
+span.config {
+ font-family: monospace;
+}
+div.inconsistency {
+ margin-top: 50px;
+}
+table {
+ margin-left: 50px;
+ border-collapse: collapse;
+}
+table, table tr,td,th {
+ border: 1px solid #444;
+}
+table ul {
+ padding-left: 0px;
+ margin: 0px;
+ list-style-type: none;
+}
+table.sched-class-clusters td {
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+table.sched-class-desc td {
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+}
+span.mono {
+ font-family: monospace;
+}
+td.measurement {
+ text-align: center;
+}
+tr.good-cluster td.measurement {
+ color: #292
+}
+tr.bad-cluster td.measurement {
+ color: #922
+}
+tr.good-cluster td.measurement span.minmax {
+ color: #888;
+}
+tr.bad-cluster td.measurement span.minmax {
+ color: #888;
+}
+</style>
+</head>
+)";
+
+template <>
+Error Analysis::run<Analysis::PrintSchedClassInconsistencies>(
+ raw_ostream &OS) const {
+ const auto &FirstPoint = Clustering_.getPoints()[0];
+ // Print the header.
+ OS << "<!DOCTYPE html><html>" << kHtmlHead << "<body>";
+ OS << "<h1><span class=\"mono\">llvm-exegesis</span> Analysis Results</h1>";
+ OS << "<h3>Triple: <span class=\"mono\">";
+ writeEscaped<kEscapeHtml>(OS, FirstPoint.LLVMTriple);
+ OS << "</span></h3><h3>Cpu: <span class=\"mono\">";
+ writeEscaped<kEscapeHtml>(OS, FirstPoint.CpuName);
+ OS << "</span></h3>";
+
+ for (const auto &RSCAndPoints : makePointsPerSchedClass()) {
+ if (!RSCAndPoints.RSC.SCDesc)
+ continue;
+ // Bucket sched class points into sched class clusters.
+ std::vector<SchedClassCluster> SchedClassClusters;
+ 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 ?
+ if (ClusterId.isUnstable() ^ AnalysisDisplayUnstableOpcodes_)
+ continue; // Either display stable or unstable clusters only.
+ auto SchedClassClusterIt = llvm::find_if(
+ SchedClassClusters, [ClusterId](const SchedClassCluster &C) {
+ return C.id() == ClusterId;
+ });
+ if (SchedClassClusterIt == SchedClassClusters.end()) {
+ SchedClassClusters.emplace_back();
+ SchedClassClusterIt = std::prev(SchedClassClusters.end());
+ }
+ SchedClassClusterIt->addPoint(PointId, Clustering_);
+ }
+
+ // Print any scheduling class that has at least one cluster that does not
+ // match the checked-in data.
+ if (all_of(SchedClassClusters, [this,
+ &RSCAndPoints](const SchedClassCluster &C) {
+ return C.measurementsMatch(*SubtargetInfo_, RSCAndPoints.RSC,
+ Clustering_,
+ AnalysisInconsistencyEpsilonSquared_);
+ }))
+ 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, RSCAndPoints.RSC.SCDesc->Name);
+#else
+ OS << RSCAndPoints.RSC.SchedClassId;
+#endif
+ OS << "</span> contains instructions whose performance characteristics do"
+ " not match that of LLVM:</p>";
+ printSchedClassClustersHtml(SchedClassClusters, RSCAndPoints.RSC, OS);
+ OS << "<p>llvm SchedModel data:</p>";
+ printSchedClassDescHtml(RSCAndPoints.RSC, OS);
+ OS << "</div>";
+ }
+
+ printClusterRawHtml(InstructionBenchmarkClustering::ClusterId::noise(),
+ "[noise]", OS);
+
+ OS << "</body></html>";
+ return Error::success();
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/Analysis.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Analysis.h
new file mode 100644
index 00000000000..b6746bed808
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Analysis.h
@@ -0,0 +1,130 @@
+//===-- Analysis.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Analysis output for benchmark results.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_ANALYSIS_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_ANALYSIS_H
+
+#include "Clustering.h"
+#include "SchedClassResolution.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+#include <set>
+#include <string>
+#include <unordered_map>
+
+namespace llvm {
+namespace exegesis {
+
+// A helper class to analyze benchmark results for a target.
+class Analysis {
+public:
+ Analysis(const Target &Target, std::unique_ptr<MCSubtargetInfo> SubtargetInfo,
+ std::unique_ptr<MCInstrInfo> InstrInfo,
+ const InstructionBenchmarkClustering &Clustering,
+ double AnalysisInconsistencyEpsilon,
+ bool AnalysisDisplayUnstableOpcodes,
+ const std::string &ForceCpuName = "");
+
+ // Prints a csv of instructions for each cluster.
+ struct PrintClusters {};
+ // Find potential errors in the scheduling information given measurements.
+ struct PrintSchedClassInconsistencies {};
+
+ template <typename Pass> Error run(raw_ostream &OS) const;
+
+private:
+ using ClusterId = InstructionBenchmarkClustering::ClusterId;
+
+ // Represents the intersection of a sched class and a cluster.
+ class SchedClassCluster {
+ public:
+ const InstructionBenchmarkClustering::ClusterId &id() const {
+ return ClusterId;
+ }
+
+ const std::vector<size_t> &getPointIds() const { return PointIds; }
+
+ void addPoint(size_t PointId,
+ const InstructionBenchmarkClustering &Clustering);
+
+ // Return the cluster centroid.
+ const SchedClassClusterCentroid &getCentroid() const { return Centroid; }
+
+ // Returns true if the cluster representative measurements match that of SC.
+ bool
+ measurementsMatch(const MCSubtargetInfo &STI, const ResolvedSchedClass &SC,
+ const InstructionBenchmarkClustering &Clustering,
+ const double AnalysisInconsistencyEpsilonSquared_) const;
+
+ private:
+ InstructionBenchmarkClustering::ClusterId ClusterId;
+ std::vector<size_t> PointIds;
+ // Measurement stats for the points in the SchedClassCluster.
+ SchedClassClusterCentroid Centroid;
+ };
+
+ void printInstructionRowCsv(size_t PointId, raw_ostream &OS) const;
+
+ void printClusterRawHtml(const InstructionBenchmarkClustering::ClusterId &Id,
+ StringRef display_name, llvm::raw_ostream &OS) const;
+
+ void printPointHtml(const InstructionBenchmark &Point,
+ llvm::raw_ostream &OS) const;
+
+ void
+ printSchedClassClustersHtml(const std::vector<SchedClassCluster> &Clusters,
+ const ResolvedSchedClass &SC,
+ raw_ostream &OS) const;
+ void printSchedClassDescHtml(const ResolvedSchedClass &SC,
+ raw_ostream &OS) 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(raw_ostream &OS, ArrayRef<uint8_t> Bytes,
+ const char *Separator) const;
+
+ const InstructionBenchmarkClustering &Clustering_;
+ std::unique_ptr<MCContext> Context_;
+ std::unique_ptr<MCSubtargetInfo> SubtargetInfo_;
+ std::unique_ptr<MCInstrInfo> InstrInfo_;
+ std::unique_ptr<MCRegisterInfo> RegInfo_;
+ std::unique_ptr<MCAsmInfo> AsmInfo_;
+ std::unique_ptr<MCInstPrinter> InstPrinter_;
+ std::unique_ptr<MCDisassembler> Disasm_;
+ const double AnalysisInconsistencyEpsilonSquared_;
+ const bool AnalysisDisplayUnstableOpcodes_;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/Assembler.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Assembler.cpp
new file mode 100644
index 00000000000..84fd9295c76
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Assembler.cpp
@@ -0,0 +1,326 @@
+//===-- Assembler.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Assembler.h"
+
+#include "SnippetRepetitor.h"
+#include "Target.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/CodeGen/FunctionLoweringInfo.h"
+#include "llvm/CodeGen/GlobalISel/CallLowering.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/ExecutionEngine/SectionMemoryManager.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+namespace llvm {
+namespace exegesis {
+
+static constexpr const char ModuleID[] = "ExegesisInfoTest";
+static constexpr const char FunctionID[] = "foo";
+static const Align kFunctionAlignment(4096);
+
+// Fills the given basic block with register setup code, and returns true if
+// all registers could be setup correctly.
+static bool generateSnippetSetupCode(
+ const ExegesisTarget &ET, const MCSubtargetInfo *const MSI,
+ ArrayRef<RegisterValue> RegisterInitialValues, BasicBlockFiller &BBF) {
+ bool IsSnippetSetupComplete = true;
+ for (const RegisterValue &RV : RegisterInitialValues) {
+ // Load a constant in the register.
+ const auto SetRegisterCode = ET.setRegTo(*MSI, RV.Register, RV.Value);
+ if (SetRegisterCode.empty())
+ IsSnippetSetupComplete = false;
+ BBF.addInstructions(SetRegisterCode);
+ }
+ return IsSnippetSetupComplete;
+}
+
+// Small utility function to add named passes.
+static bool addPass(PassManagerBase &PM, StringRef PassName,
+ TargetPassConfig &TPC) {
+ const PassRegistry *PR = PassRegistry::getPassRegistry();
+ const PassInfo *PI = PR->getPassInfo(PassName);
+ if (!PI) {
+ errs() << " run-pass " << PassName << " is not registered.\n";
+ return true;
+ }
+
+ if (!PI->getNormalCtor()) {
+ errs() << " cannot create pass: " << PI->getPassName() << "\n";
+ return true;
+ }
+ Pass *P = PI->getNormalCtor()();
+ std::string Banner = std::string("After ") + std::string(P->getPassName());
+ PM.add(P);
+ TPC.printAndVerify(Banner);
+
+ return false;
+}
+
+MachineFunction &createVoidVoidPtrMachineFunction(StringRef FunctionName,
+ Module *Module,
+ MachineModuleInfo *MMI) {
+ Type *const ReturnType = Type::getInt32Ty(Module->getContext());
+ Type *const MemParamType = PointerType::get(
+ Type::getInt8Ty(Module->getContext()), 0 /*default address space*/);
+ FunctionType *FunctionType =
+ FunctionType::get(ReturnType, {MemParamType}, false);
+ Function *const F = Function::Create(
+ FunctionType, GlobalValue::InternalLinkage, FunctionName, Module);
+ // Making sure we can create a MachineFunction out of this Function even if it
+ // contains no IR.
+ F->setIsMaterializable(true);
+ return MMI->getOrCreateMachineFunction(*F);
+}
+
+BasicBlockFiller::BasicBlockFiller(MachineFunction &MF, MachineBasicBlock *MBB,
+ const MCInstrInfo *MCII)
+ : MF(MF), MBB(MBB), MCII(MCII) {}
+
+void BasicBlockFiller::addInstruction(const MCInst &Inst, const DebugLoc &DL) {
+ const unsigned Opcode = Inst.getOpcode();
+ const MCInstrDesc &MCID = MCII->get(Opcode);
+ MachineInstrBuilder Builder = BuildMI(MBB, DL, MCID);
+ for (unsigned OpIndex = 0, E = Inst.getNumOperands(); OpIndex < E;
+ ++OpIndex) {
+ const MCOperand &Op = Inst.getOperand(OpIndex);
+ if (Op.isReg()) {
+ const bool IsDef = OpIndex < MCID.getNumDefs();
+ unsigned Flags = 0;
+ const MCOperandInfo &OpInfo = MCID.operands().begin()[OpIndex];
+ if (IsDef && !OpInfo.isOptionalDef())
+ Flags |= RegState::Define;
+ 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");
+ }
+ }
+}
+
+void BasicBlockFiller::addInstructions(ArrayRef<MCInst> Insts,
+ const DebugLoc &DL) {
+ for (const MCInst &Inst : Insts)
+ addInstruction(Inst, DL);
+}
+
+void BasicBlockFiller::addReturn(const DebugLoc &DL) {
+ // Insert the return code.
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ if (TII->getReturnOpcode() < TII->getNumOpcodes()) {
+ BuildMI(MBB, DL, TII->get(TII->getReturnOpcode()));
+ } else {
+ MachineIRBuilder MIB(MF);
+ MIB.setMBB(*MBB);
+
+ FunctionLoweringInfo FuncInfo;
+ FuncInfo.CanLowerReturn = true;
+ MF.getSubtarget().getCallLowering()->lowerReturn(MIB, nullptr, {},
+ FuncInfo);
+ }
+}
+
+FunctionFiller::FunctionFiller(MachineFunction &MF,
+ std::vector<unsigned> RegistersSetUp)
+ : MF(MF), MCII(MF.getTarget().getMCInstrInfo()), Entry(addBasicBlock()),
+ RegistersSetUp(std::move(RegistersSetUp)) {}
+
+BasicBlockFiller FunctionFiller::addBasicBlock() {
+ MachineBasicBlock *MBB = MF.CreateMachineBasicBlock();
+ MF.push_back(MBB);
+ return BasicBlockFiller(MF, MBB, MCII);
+}
+
+ArrayRef<unsigned> FunctionFiller::getRegistersSetUp() const {
+ return RegistersSetUp;
+}
+
+static std::unique_ptr<Module>
+createModule(const std::unique_ptr<LLVMContext> &Context, const DataLayout &DL) {
+ auto Mod = std::make_unique<Module>(ModuleID, *Context);
+ Mod->setDataLayout(DL);
+ return Mod;
+}
+
+BitVector getFunctionReservedRegs(const TargetMachine &TM) {
+ std::unique_ptr<LLVMContext> Context = std::make_unique<LLVMContext>();
+ std::unique_ptr<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<MachineModuleInfoWrapperPass> MMIWP =
+ std::make_unique<MachineModuleInfoWrapperPass>(&LLVMTM);
+ MachineFunction &MF = createVoidVoidPtrMachineFunction(
+ FunctionID, Module.get(), &MMIWP.get()->getMMI());
+ // Saving reserved registers for client.
+ return MF.getSubtarget().getRegisterInfo()->getReservedRegs(MF);
+}
+
+Error assembleToStream(const ExegesisTarget &ET,
+ std::unique_ptr<LLVMTargetMachine> TM,
+ ArrayRef<unsigned> LiveIns,
+ ArrayRef<RegisterValue> RegisterInitialValues,
+ const FillFunction &Fill, raw_pwrite_stream &AsmStream) {
+ auto Context = std::make_unique<LLVMContext>();
+ std::unique_ptr<Module> Module =
+ createModule(Context, TM->createDataLayout());
+ auto MMIWP = std::make_unique<MachineModuleInfoWrapperPass>(TM.get());
+ MachineFunction &MF = createVoidVoidPtrMachineFunction(
+ FunctionID, Module.get(), &MMIWP.get()->getMMI());
+ MF.ensureAlignment(kFunctionAlignment);
+
+ // We need to instruct the passes that we're done with SSA and virtual
+ // registers.
+ auto &Properties = MF.getProperties();
+ Properties.set(MachineFunctionProperties::Property::NoVRegs);
+ Properties.reset(MachineFunctionProperties::Property::IsSSA);
+ Properties.set(MachineFunctionProperties::Property::NoPHIs);
+
+ for (const unsigned Reg : LiveIns)
+ MF.getRegInfo().addLiveIn(Reg);
+
+ std::vector<unsigned> RegistersSetUp;
+ for (const auto &InitValue : RegisterInitialValues) {
+ RegistersSetUp.push_back(InitValue.Register);
+ }
+ FunctionFiller Sink(MF, std::move(RegistersSetUp));
+ auto Entry = Sink.getEntry();
+ for (const unsigned Reg : LiveIns)
+ Entry.MBB->addLiveIn(Reg);
+
+ const bool IsSnippetSetupComplete = generateSnippetSetupCode(
+ ET, TM->getMCSubtargetInfo(), RegisterInitialValues, Entry);
+
+ // 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)
+ Properties.reset(MachineFunctionProperties::Property::TracksLiveness);
+
+ Fill(Sink);
+
+ // prologue/epilogue pass needs the reserved registers to be frozen, this
+ // is usually done by the SelectionDAGISel pass.
+ MF.getRegInfo().freezeReservedRegs(MF);
+
+ // We create the pass manager, run the passes to populate AsmBuffer.
+ MCContext &MCContext = MMIWP->getMMI().getContext();
+ legacy::PassManager PM;
+
+ TargetLibraryInfoImpl TLII(Triple(Module->getTargetTriple()));
+ PM.add(new TargetLibraryInfoWrapperPass(TLII));
+
+ TargetPassConfig *TPC = TM->createPassConfig(PM);
+ PM.add(TPC);
+ PM.add(MMIWP.release());
+ TPC->printAndVerify("MachineFunctionGenerator::assemble");
+ // Add target-specific passes.
+ ET.addTargetSpecificPasses(PM);
+ TPC->printAndVerify("After ExegesisTarget::addTargetSpecificPasses");
+ // Adding the following passes:
+ // - postrapseudos: expands pseudo return instructions used on some targets.
+ // - machineverifier: checks that the MachineFunction is well formed.
+ // - prologepilog: saves and restore callee saved registers.
+ for (const char *PassName :
+ {"postrapseudos", "machineverifier", "prologepilog"})
+ if (addPass(PM, PassName, *TPC))
+ return make_error<Failure>("Unable to add a mandatory pass");
+ TPC->setInitialized();
+
+ // AsmPrinter is responsible for generating the assembly into AsmBuffer.
+ if (TM->addAsmPrinter(PM, AsmStream, nullptr, CGFT_ObjectFile, MCContext))
+ return make_error<Failure>("Cannot add AsmPrinter passes");
+
+ PM.run(*Module); // Run all the passes
+ return Error::success();
+}
+
+object::OwningBinary<object::ObjectFile>
+getObjectFromBuffer(StringRef InputData) {
+ // Storing the generated assembly into a MemoryBuffer that owns the memory.
+ std::unique_ptr<MemoryBuffer> Buffer =
+ MemoryBuffer::getMemBufferCopy(InputData);
+ // Create the ObjectFile from the MemoryBuffer.
+ std::unique_ptr<object::ObjectFile> Obj =
+ cantFail(object::ObjectFile::createObjectFile(Buffer->getMemBufferRef()));
+ // Returning both the MemoryBuffer and the ObjectFile.
+ return object::OwningBinary<object::ObjectFile>(std::move(Obj),
+ std::move(Buffer));
+}
+
+object::OwningBinary<object::ObjectFile> getObjectFromFile(StringRef Filename) {
+ return cantFail(object::ObjectFile::createObjectFile(Filename));
+}
+
+namespace {
+
+// Implementation of this class relies on the fact that a single object with a
+// single function will be loaded into memory.
+class TrackingSectionMemoryManager : public SectionMemoryManager {
+public:
+ explicit TrackingSectionMemoryManager(uintptr_t *CodeSize)
+ : CodeSize(CodeSize) {}
+
+ uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
+ unsigned SectionID,
+ StringRef SectionName) override {
+ *CodeSize = Size;
+ return SectionMemoryManager::allocateCodeSection(Size, Alignment, SectionID,
+ SectionName);
+ }
+
+private:
+ uintptr_t *const CodeSize = nullptr;
+};
+
+} // namespace
+
+ExecutableFunction::ExecutableFunction(
+ std::unique_ptr<LLVMTargetMachine> TM,
+ object::OwningBinary<object::ObjectFile> &&ObjectFileHolder)
+ : Context(std::make_unique<LLVMContext>()) {
+ assert(ObjectFileHolder.getBinary() && "cannot create object file");
+ // Initializing the execution engine.
+ // We need to use the JIT EngineKind to be able to add an object file.
+ LLVMLinkInMCJIT();
+ uintptr_t CodeSize = 0;
+ std::string Error;
+ ExecEngine.reset(
+ EngineBuilder(createModule(Context, TM->createDataLayout()))
+ .setErrorStr(&Error)
+ .setMCPU(TM->getTargetCPU())
+ .setEngineKind(EngineKind::JIT)
+ .setMCJITMemoryManager(
+ std::make_unique<TrackingSectionMemoryManager>(&CodeSize))
+ .create(TM.release()));
+ if (!ExecEngine)
+ report_fatal_error(Twine(Error));
+ // Adding the generated object file containing the assembled function.
+ // The ExecutionEngine makes sure the object file is copied into an
+ // executable page.
+ ExecEngine->addObjectFile(std::move(ObjectFileHolder));
+ // Fetching function bytes.
+ const uint64_t FunctionAddress = ExecEngine->getFunctionAddress(FunctionID);
+ assert(isAligned(kFunctionAlignment, FunctionAddress) &&
+ "function is not properly aligned");
+ FunctionBytes =
+ StringRef(reinterpret_cast<const char *>(FunctionAddress), CodeSize);
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/Assembler.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Assembler.h
new file mode 100644
index 00000000000..2a83344b751
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Assembler.h
@@ -0,0 +1,132 @@
+//===-- Assembler.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines classes to assemble functions composed of a single basic block of
+/// MCInsts.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_ASSEMBLER_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_ASSEMBLER_H
+
+#include <memory>
+
+#include "BenchmarkCode.h"
+#include "Error.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/ExecutionEngine/ExecutionEngine.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+
+namespace llvm {
+namespace exegesis {
+
+class ExegesisTarget;
+
+// Gather the set of reserved registers (depends on function's calling
+// convention and target machine).
+BitVector getFunctionReservedRegs(const TargetMachine &TM);
+
+// Helper to fill in a basic block.
+class BasicBlockFiller {
+public:
+ BasicBlockFiller(MachineFunction &MF, MachineBasicBlock *MBB,
+ const MCInstrInfo *MCII);
+
+ void addInstruction(const MCInst &Inst, const DebugLoc &DL = DebugLoc());
+ void addInstructions(ArrayRef<MCInst> Insts, const DebugLoc &DL = DebugLoc());
+
+ void addReturn(const DebugLoc &DL = DebugLoc());
+
+ MachineFunction &MF;
+ MachineBasicBlock *const MBB;
+ const MCInstrInfo *const MCII;
+};
+
+// Helper to fill in a function.
+class FunctionFiller {
+public:
+ FunctionFiller(MachineFunction &MF, std::vector<unsigned> RegistersSetUp);
+
+ // Adds a basic block to the function.
+ BasicBlockFiller addBasicBlock();
+
+ // Returns the function entry point.
+ BasicBlockFiller getEntry() { return Entry; }
+
+ MachineFunction &MF;
+ const MCInstrInfo *const MCII;
+
+ // Returns the set of registers in the snippet setup code.
+ ArrayRef<unsigned> getRegistersSetUp() const;
+
+private:
+ BasicBlockFiller Entry;
+ // The set of registers that are set up in the basic block.
+ std::vector<unsigned> RegistersSetUp;
+};
+
+// A callback that fills a function.
+using FillFunction = std::function<void(FunctionFiller &)>;
+
+// 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.
+Error assembleToStream(const ExegesisTarget &ET,
+ std::unique_ptr<LLVMTargetMachine> TM,
+ ArrayRef<unsigned> LiveIns,
+ ArrayRef<RegisterValue> RegisterInitialValues,
+ const FillFunction &Fill, raw_pwrite_stream &AsmStream);
+
+// Creates an ObjectFile in the format understood by the host.
+// Note: the resulting object keeps a copy of Buffer so it can be discarded once
+// this function returns.
+object::OwningBinary<object::ObjectFile> getObjectFromBuffer(StringRef Buffer);
+
+// Loads the content of Filename as on ObjectFile and returns it.
+object::OwningBinary<object::ObjectFile> getObjectFromFile(StringRef Filename);
+
+// Consumes an ObjectFile containing a `void foo(char*)` function and make it
+// executable.
+struct ExecutableFunction {
+ explicit ExecutableFunction(
+ std::unique_ptr<LLVMTargetMachine> TM,
+ object::OwningBinary<object::ObjectFile> &&ObjectFileHolder);
+
+ // Retrieves the function as an array of bytes.
+ StringRef getFunctionBytes() const { return FunctionBytes; }
+
+ // Executes the function.
+ void operator()(char *Memory) const {
+ ((void (*)(char *))(intptr_t)FunctionBytes.data())(Memory);
+ }
+
+ std::unique_ptr<LLVMContext> Context;
+ std::unique_ptr<ExecutionEngine> ExecEngine;
+ StringRef FunctionBytes;
+};
+
+// Creates a void(int8*) MachineFunction.
+MachineFunction &createVoidVoidPtrMachineFunction(StringRef FunctionID,
+ Module *Module,
+ MachineModuleInfo *MMI);
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_ASSEMBLER_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkCode.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkCode.h
new file mode 100644
index 00000000000..7dceb25b507
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkCode.h
@@ -0,0 +1,35 @@
+//===-- BenchmarkCode.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKCODE_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKCODE_H
+
+#include "BenchmarkResult.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 {
+ InstructionBenchmarkKey Key;
+
+ // 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/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkResult.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkResult.cpp
new file mode 100644
index 00000000000..dbf07699bbe
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkResult.cpp
@@ -0,0 +1,433 @@
+//===-- BenchmarkResult.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "BenchmarkResult.h"
+#include "BenchmarkRunner.h"
+#include "Error.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/bit.h"
+#include "llvm/ObjectYAML/YAML.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+
+static constexpr const char kIntegerPrefix[] = "i_0x";
+static constexpr const char kDoublePrefix[] = "f_";
+static constexpr const char kInvalidOperand[] = "INVALID";
+static constexpr llvm::StringLiteral kNoRegister("%noreg");
+
+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),
+ OpcodeNameToOpcodeIdx(
+ generateOpcodeNameToOpcodeIdxMapping(State.getInstrInfo())),
+ RegNameToRegNo(generateRegNameToRegNoMapping(State.getRegInfo())) {}
+
+ static StringMap<unsigned>
+ generateOpcodeNameToOpcodeIdxMapping(const MCInstrInfo &InstrInfo) {
+ StringMap<unsigned> Map(InstrInfo.getNumOpcodes());
+ for (unsigned I = 0, E = InstrInfo.getNumOpcodes(); I < E; ++I)
+ Map[InstrInfo.getName(I)] = I;
+ assert(Map.size() == InstrInfo.getNumOpcodes() && "Size prediction failed");
+ return Map;
+ };
+
+ StringMap<unsigned>
+ generateRegNameToRegNoMapping(const MCRegisterInfo &RegInfo) {
+ StringMap<unsigned> Map(RegInfo.getNumRegs());
+ // Special-case RegNo 0, which would otherwise be spelled as ''.
+ Map[kNoRegister] = 0;
+ for (unsigned I = 1, E = RegInfo.getNumRegs(); I < E; ++I)
+ Map[RegInfo.getName(I)] = I;
+ assert(Map.size() == RegInfo.getNumRegs() && "Size prediction failed");
+ return Map;
+ };
+
+ void serializeMCInst(const MCInst &MCInst, raw_ostream &OS) {
+ OS << getInstrName(MCInst.getOpcode());
+ for (const auto &Op : MCInst) {
+ OS << ' ';
+ serializeMCOperand(Op, OS);
+ }
+ }
+
+ void deserializeMCInst(StringRef String, MCInst &Value) {
+ SmallVector<StringRef, 16> Pieces;
+ String.split(Pieces, " ", /* MaxSplit */ -1, /* KeepEmpty */ false);
+ if (Pieces.empty()) {
+ ErrorStream << "Unknown Instruction: '" << String << "'\n";
+ return;
+ }
+ bool ProcessOpcode = true;
+ for (StringRef Piece : Pieces) {
+ if (ProcessOpcode)
+ Value.setOpcode(getInstrOpcode(Piece));
+ else
+ Value.addOperand(deserializeMCOperand(Piece));
+ ProcessOpcode = false;
+ }
+ }
+
+ std::string &getLastError() { return ErrorStream.str(); }
+
+ raw_string_ostream &getErrorStream() { return ErrorStream; }
+
+ StringRef getRegName(unsigned RegNo) {
+ // Special case: RegNo 0 is NoRegister. We have to deal with it explicitly.
+ if (RegNo == 0)
+ return kNoRegister;
+ const StringRef RegName = State->getRegInfo().getName(RegNo);
+ if (RegName.empty())
+ ErrorStream << "No register with enum value '" << RegNo << "'\n";
+ return RegName;
+ }
+
+ Optional<unsigned> getRegNo(StringRef RegName) {
+ auto Iter = RegNameToRegNo.find(RegName);
+ if (Iter != RegNameToRegNo.end())
+ return Iter->second;
+ ErrorStream << "No register with name '" << RegName << "'\n";
+ return None;
+ }
+
+private:
+ void serializeIntegerOperand(raw_ostream &OS, int64_t Value) {
+ OS << kIntegerPrefix;
+ OS.write_hex(bit_cast<uint64_t>(Value));
+ }
+
+ bool tryDeserializeIntegerOperand(StringRef String, int64_t &Value) {
+ if (!String.consume_front(kIntegerPrefix))
+ return false;
+ return !String.consumeInteger(16, Value);
+ }
+
+ void serializeFPOperand(raw_ostream &OS, double Value) {
+ OS << kDoublePrefix << format("%la", Value);
+ }
+
+ bool tryDeserializeFPOperand(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 MCOperand &MCOperand, raw_ostream &OS) {
+ if (MCOperand.isReg()) {
+ OS << getRegName(MCOperand.getReg());
+ } else if (MCOperand.isImm()) {
+ serializeIntegerOperand(OS, MCOperand.getImm());
+ } else if (MCOperand.isDFPImm()) {
+ serializeFPOperand(OS, bit_cast<double>(MCOperand.getDFPImm()));
+ } else {
+ OS << kInvalidOperand;
+ }
+ }
+
+ MCOperand deserializeMCOperand(StringRef String) {
+ assert(!String.empty());
+ int64_t IntValue = 0;
+ double DoubleValue = 0;
+ if (tryDeserializeIntegerOperand(String, IntValue))
+ return MCOperand::createImm(IntValue);
+ if (tryDeserializeFPOperand(String, DoubleValue))
+ return MCOperand::createDFPImm(bit_cast<uint64_t>(DoubleValue));
+ if (auto RegNo = getRegNo(String))
+ return MCOperand::createReg(*RegNo);
+ if (String != kInvalidOperand)
+ ErrorStream << "Unknown Operand: '" << String << "'\n";
+ return {};
+ }
+
+ StringRef getInstrName(unsigned InstrNo) {
+ const StringRef InstrName = State->getInstrInfo().getName(InstrNo);
+ if (InstrName.empty())
+ ErrorStream << "No opcode with enum value '" << InstrNo << "'\n";
+ return InstrName;
+ }
+
+ unsigned getInstrOpcode(StringRef InstrName) {
+ auto Iter = OpcodeNameToOpcodeIdx.find(InstrName);
+ if (Iter != OpcodeNameToOpcodeIdx.end())
+ return Iter->second;
+ ErrorStream << "No opcode with name '" << InstrName << "'\n";
+ return 0;
+ }
+
+ const exegesis::LLVMState *State;
+ std::string LastError;
+ raw_string_ostream ErrorStream;
+ const StringMap<unsigned> OpcodeNameToOpcodeIdx;
+ const StringMap<unsigned> RegNameToRegNo;
+};
+} // namespace
+
+// Defining YAML traits for IO.
+namespace yaml {
+
+static YamlContext &getTypedContext(void *Ctx) {
+ return *reinterpret_cast<YamlContext *>(Ctx);
+}
+
+// std::vector<MCInst> will be rendered as a list.
+template <> struct SequenceElementTraits<MCInst> {
+ static const bool flow = false;
+};
+
+template <> struct ScalarTraits<MCInst> {
+
+ static void output(const MCInst &Value, void *Ctx, raw_ostream &Out) {
+ getTypedContext(Ctx).serializeMCInst(Value, Out);
+ }
+
+ static StringRef input(StringRef Scalar, void *Ctx, MCInst &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;
+};
+
+// std::vector<exegesis::Measure> will be rendered as a list.
+template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> {
+ static const bool flow = false;
+};
+
+// exegesis::Measure is rendererd as a flow instead of a list.
+// e.g. { "key": "the key", "value": 0123 }
+template <> struct MappingTraits<exegesis::BenchmarkMeasure> {
+ static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) {
+ Io.mapRequired("key", Obj.Key);
+ 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;
+};
+
+template <>
+struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> {
+ static void enumeration(IO &Io,
+ exegesis::InstructionBenchmark::ModeE &Value) {
+ Io.enumCase(Value, "", exegesis::InstructionBenchmark::Unknown);
+ Io.enumCase(Value, "latency", exegesis::InstructionBenchmark::Latency);
+ Io.enumCase(Value, "uops", exegesis::InstructionBenchmark::Uops);
+ Io.enumCase(Value, "inverse_throughput",
+ exegesis::InstructionBenchmark::InverseThroughput);
+ }
+};
+
+// 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,
+ raw_ostream &Out) {
+ YamlContext &Context = getTypedContext(Ctx);
+ Out << Context.getRegName(RV.Register) << "=0x"
+ << toString(RV.Value, kRadix, kSigned);
+ }
+
+ static StringRef input(StringRef String, void *Ctx,
+ exegesis::RegisterValue &RV) {
+ SmallVector<StringRef, 2> Pieces;
+ String.split(Pieces, "=0x", /* MaxSplit */ -1,
+ /* KeepEmpty */ false);
+ YamlContext &Context = getTypedContext(Ctx);
+ Optional<unsigned> RegNo;
+ if (Pieces.size() == 2 && (RegNo = Context.getRegNo(Pieces[0]))) {
+ RV.Register = *RegNo;
+ const unsigned BitsNeeded = APInt::getBitsNeeded(Pieces[1], kRadix);
+ RV.Value = 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 MappingContextTraits<exegesis::InstructionBenchmark, YamlContext> {
+ struct NormalizedBinary {
+ NormalizedBinary(IO &io) {}
+ NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {}
+ std::vector<uint8_t> denormalize(IO &) {
+ std::vector<uint8_t> Data;
+ std::string Str;
+ raw_string_ostream OSS(Str);
+ Binary.writeAsBinary(OSS);
+ OSS.flush();
+ Data.assign(Str.begin(), Str.end());
+ return Data;
+ }
+
+ BinaryRef Binary;
+ };
+
+ static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj,
+ YamlContext &Context) {
+ Io.mapRequired("mode", Obj.Mode);
+ Io.mapRequired("key", Obj.Key, Context);
+ Io.mapRequired("cpu_name", Obj.CpuName);
+ Io.mapRequired("llvm_triple", Obj.LLVMTriple);
+ Io.mapRequired("num_repetitions", Obj.NumRepetitions);
+ Io.mapRequired("measurements", Obj.Measurements);
+ Io.mapRequired("error", Obj.Error);
+ Io.mapOptional("info", Obj.Info);
+ // AssembledSnippet
+ MappingNormalization<NormalizedBinary, std::vector<uint8_t>> BinaryString(
+ Io, Obj.AssembledSnippet);
+ Io.mapOptional("assembled_snippet", BinaryString->Binary);
+ }
+};
+
+} // namespace yaml
+
+namespace exegesis {
+
+Expected<InstructionBenchmark>
+InstructionBenchmark::readYaml(const LLVMState &State, StringRef Filename) {
+ if (auto ExpectedMemoryBuffer =
+ errorOrToExpected(MemoryBuffer::getFile(Filename, /*IsText=*/true))) {
+ yaml::Input Yin(*ExpectedMemoryBuffer.get());
+ YamlContext Context(State);
+ InstructionBenchmark Benchmark;
+ if (Yin.setCurrentDocument())
+ yaml::yamlize(Yin, Benchmark, /*unused*/ true, Context);
+ if (!Context.getLastError().empty())
+ return make_error<Failure>(Context.getLastError());
+ return Benchmark;
+ } else {
+ return ExpectedMemoryBuffer.takeError();
+ }
+}
+
+Expected<std::vector<InstructionBenchmark>>
+InstructionBenchmark::readYamls(const LLVMState &State, StringRef Filename) {
+ if (auto ExpectedMemoryBuffer =
+ errorOrToExpected(MemoryBuffer::getFile(Filename, /*IsText=*/true))) {
+ 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 errorCodeToError(Yin.error());
+ if (!Context.getLastError().empty())
+ return make_error<Failure>(Context.getLastError());
+ Yin.nextDocument();
+ }
+ return Benchmarks;
+ } else {
+ return ExpectedMemoryBuffer.takeError();
+ }
+}
+
+Error InstructionBenchmark::writeYamlTo(const LLVMState &State,
+ raw_ostream &OS) {
+ auto Cleanup = make_scope_exit([&] { OS.flush(); });
+ yaml::Output Yout(OS, nullptr /*Ctx*/, 200 /*WrapColumn*/);
+ YamlContext Context(State);
+ Yout.beginDocuments();
+ yaml::yamlize(Yout, *this, /*unused*/ true, Context);
+ if (!Context.getLastError().empty())
+ return make_error<Failure>(Context.getLastError());
+ Yout.endDocuments();
+ return Error::success();
+}
+
+Error InstructionBenchmark::readYamlFrom(const LLVMState &State,
+ StringRef InputContent) {
+ yaml::Input Yin(InputContent);
+ YamlContext Context(State);
+ if (Yin.setCurrentDocument())
+ yaml::yamlize(Yin, *this, /*unused*/ true, Context);
+ if (!Context.getLastError().empty())
+ return make_error<Failure>(Context.getLastError());
+ return Error::success();
+}
+
+Error InstructionBenchmark::writeYaml(const LLVMState &State,
+ const StringRef Filename) {
+ if (Filename == "-") {
+ if (auto Err = writeYamlTo(State, outs()))
+ return Err;
+ } else {
+ int ResultFD = 0;
+ if (auto E = errorCodeToError(openFileForWrite(Filename, ResultFD,
+ sys::fs::CD_CreateAlways,
+ sys::fs::OF_TextWithCRLF))) {
+ return E;
+ }
+ raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/);
+ if (auto Err = writeYamlTo(State, Ostr))
+ return Err;
+ }
+ return Error::success();
+}
+
+void PerInstructionStats::push(const BenchmarkMeasure &BM) {
+ if (Key.empty())
+ Key = BM.Key;
+ assert(Key == BM.Key);
+ ++NumValues;
+ SumValues += BM.PerInstructionValue;
+ MaxValue = std::max(MaxValue, BM.PerInstructionValue);
+ MinValue = std::min(MinValue, BM.PerInstructionValue);
+}
+
+bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B) {
+ return std::tie(A.Key, A.PerInstructionValue, A.PerSnippetValue) ==
+ std::tie(B.Key, B.PerInstructionValue, B.PerSnippetValue);
+}
+
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkResult.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkResult.h
new file mode 100644
index 00000000000..436bd00ac43
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkResult.h
@@ -0,0 +1,124 @@
+//===-- BenchmarkResult.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines classes to represent measurements and serialize/deserialize them to
+// Yaml.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H
+
+#include "LlvmState.h"
+#include "RegisterValue.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstBuilder.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <limits>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace llvm {
+class Error;
+
+namespace exegesis {
+
+struct InstructionBenchmarkKey {
+ // The LLVM opcode name.
+ std::vector<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;
+ // 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.
+struct InstructionBenchmark {
+ InstructionBenchmarkKey Key;
+ enum ModeE { Unknown, Latency, Uops, InverseThroughput };
+ ModeE Mode;
+ std::string CpuName;
+ std::string LLVMTriple;
+ // Which instruction is being benchmarked here?
+ const MCInst &keyInstruction() const { return Key.Instructions[0]; }
+ // The number of instructions inside the repeated snippet. For example, if a
+ // snippet of 3 instructions is repeated 4 times, this is 12.
+ unsigned NumRepetitions = 0;
+ enum RepetitionModeE { Duplicate, Loop, AggregateMin };
+ // Note that measurements are per instruction.
+ std::vector<BenchmarkMeasure> Measurements;
+ std::string Error;
+ std::string Info;
+ std::vector<uint8_t> AssembledSnippet;
+ // How to aggregate measurements.
+ enum ResultAggregationModeE { Min, Max, Mean, MinVariance };
+ // Read functions.
+ static Expected<InstructionBenchmark> readYaml(const LLVMState &State,
+ StringRef Filename);
+
+ static Expected<std::vector<InstructionBenchmark>>
+ readYamls(const LLVMState &State, StringRef Filename);
+
+ class Error readYamlFrom(const LLVMState &State, StringRef InputContent);
+
+ // Write functions, non-const because of YAML traits.
+ class Error writeYamlTo(const LLVMState &State, raw_ostream &S);
+
+ class Error writeYaml(const LLVMState &State, const StringRef Filename);
+};
+
+bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B);
+
+//------------------------------------------------------------------------------
+// Utilities to work with Benchmark measures.
+
+// A class that measures stats over benchmark measures.
+class PerInstructionStats {
+public:
+ void push(const BenchmarkMeasure &BM);
+
+ double avg() const {
+ assert(NumValues);
+ return SumValues / NumValues;
+ }
+ double min() const { return MinValue; }
+ double max() const { return MaxValue; }
+
+ const std::string &key() const { return Key; }
+
+private:
+ std::string Key;
+ double SumValues = 0.0;
+ int NumValues = 0;
+ double MaxValue = std::numeric_limits<double>::min();
+ double MinValue = std::numeric_limits<double>::max();
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
new file mode 100644
index 00000000000..03e7ccc26f4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkRunner.cpp
@@ -0,0 +1,281 @@
+//===-- BenchmarkRunner.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <array>
+#include <memory>
+#include <string>
+
+#include "Assembler.h"
+#include "BenchmarkRunner.h"
+#include "Error.h"
+#include "MCInstrDescView.h"
+#include "PerfHelper.h"
+#include "Target.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Program.h"
+
+namespace llvm {
+namespace exegesis {
+
+BenchmarkRunner::BenchmarkRunner(const LLVMState &State,
+ InstructionBenchmark::ModeE Mode)
+ : State(State), Mode(Mode), Scratch(std::make_unique<ScratchSpace>()) {}
+
+BenchmarkRunner::~BenchmarkRunner() = default;
+
+namespace {
+class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor {
+public:
+ FunctionExecutorImpl(const LLVMState &State,
+ object::OwningBinary<object::ObjectFile> Obj,
+ BenchmarkRunner::ScratchSpace *Scratch)
+ : State(State), Function(State.createTargetMachine(), std::move(Obj)),
+ Scratch(Scratch) {}
+
+private:
+ Expected<int64_t> runAndMeasure(const char *Counters) const override {
+ auto ResultOrError = runAndSample(Counters);
+ if (ResultOrError)
+ return ResultOrError.get()[0];
+ return ResultOrError.takeError();
+ }
+
+ static void
+ accumulateCounterValues(const llvm::SmallVector<int64_t, 4> &NewValues,
+ llvm::SmallVector<int64_t, 4> *Result) {
+ const size_t NumValues = std::max(NewValues.size(), Result->size());
+ if (NumValues > Result->size())
+ Result->resize(NumValues, 0);
+ for (size_t I = 0, End = NewValues.size(); I < End; ++I)
+ (*Result)[I] += NewValues[I];
+ }
+
+ Expected<llvm::SmallVector<int64_t, 4>>
+ runAndSample(const char *Counters) const override {
+ // We sum counts when there are several counters for a single ProcRes
+ // (e.g. P23 on SandyBridge).
+ llvm::SmallVector<int64_t, 4> CounterValues;
+ int Reserved = 0;
+ SmallVector<StringRef, 2> CounterNames;
+ StringRef(Counters).split(CounterNames, '+');
+ char *const ScratchPtr = Scratch->ptr();
+ const ExegesisTarget &ET = State.getExegesisTarget();
+ for (auto &CounterName : CounterNames) {
+ CounterName = CounterName.trim();
+ auto CounterOrError = ET.createCounter(CounterName, State);
+
+ if (!CounterOrError)
+ return CounterOrError.takeError();
+
+ pfm::Counter *Counter = CounterOrError.get().get();
+ if (Reserved == 0) {
+ Reserved = Counter->numValues();
+ CounterValues.reserve(Reserved);
+ } else if (Reserved != Counter->numValues())
+ // It'd be wrong to accumulate vectors of different sizes.
+ return make_error<Failure>(
+ llvm::Twine("Inconsistent number of values for counter ")
+ .concat(CounterName)
+ .concat(std::to_string(Counter->numValues()))
+ .concat(" vs expected of ")
+ .concat(std::to_string(Reserved)));
+ Scratch->clear();
+ {
+ auto PS = ET.withSavedState();
+ CrashRecoveryContext CRC;
+ CrashRecoveryContext::Enable();
+ const bool Crashed = !CRC.RunSafely([this, Counter, ScratchPtr]() {
+ Counter->start();
+ this->Function(ScratchPtr);
+ Counter->stop();
+ });
+ CrashRecoveryContext::Disable();
+ PS.reset();
+ if (Crashed) {
+ std::string Msg = "snippet crashed while running";
+#ifdef LLVM_ON_UNIX
+ // See "Exit Status for Commands":
+ // https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html
+ constexpr const int kSigOffset = 128;
+ if (const char *const SigName = strsignal(CRC.RetCode - kSigOffset)) {
+ Msg += ": ";
+ Msg += SigName;
+ }
+#endif
+ return make_error<SnippetCrash>(std::move(Msg));
+ }
+ }
+
+ auto ValueOrError = Counter->readOrError(Function.getFunctionBytes());
+ if (!ValueOrError)
+ return ValueOrError.takeError();
+ accumulateCounterValues(ValueOrError.get(), &CounterValues);
+ }
+ return CounterValues;
+ }
+
+ const LLVMState &State;
+ const ExecutableFunction Function;
+ BenchmarkRunner::ScratchSpace *const Scratch;
+};
+} // namespace
+
+Expected<InstructionBenchmark> BenchmarkRunner::runConfiguration(
+ const BenchmarkCode &BC, unsigned NumRepetitions, unsigned LoopBodySize,
+ ArrayRef<std::unique_ptr<const SnippetRepetitor>> Repetitors,
+ bool DumpObjectToDisk) const {
+ InstructionBenchmark InstrBenchmark;
+ InstrBenchmark.Mode = Mode;
+ InstrBenchmark.CpuName = std::string(State.getTargetMachine().getTargetCPU());
+ InstrBenchmark.LLVMTriple =
+ State.getTargetMachine().getTargetTriple().normalize();
+ InstrBenchmark.NumRepetitions = NumRepetitions;
+ InstrBenchmark.Info = BC.Info;
+
+ const std::vector<MCInst> &Instructions = BC.Key.Instructions;
+
+ InstrBenchmark.Key = BC.Key;
+
+ // If we end up having an error, and we've previously succeeded with
+ // some other Repetitor, we want to discard the previous measurements.
+ struct ClearBenchmarkOnReturn {
+ ClearBenchmarkOnReturn(InstructionBenchmark *IB) : IB(IB) {}
+ ~ClearBenchmarkOnReturn() {
+ if (Clear)
+ IB->Measurements.clear();
+ }
+ void disarm() { Clear = false; }
+
+ private:
+ InstructionBenchmark *const IB;
+ bool Clear = true;
+ };
+ ClearBenchmarkOnReturn CBOR(&InstrBenchmark);
+
+ for (const std::unique_ptr<const SnippetRepetitor> &Repetitor : Repetitors) {
+ // 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.
+ const int MinInstructionsForSnippet = 4 * Instructions.size();
+ const int LoopBodySizeForSnippet = 2 * Instructions.size();
+ {
+ SmallString<0> Buffer;
+ raw_svector_ostream OS(Buffer);
+ if (Error E = assembleToStream(
+ State.getExegesisTarget(), State.createTargetMachine(),
+ BC.LiveIns, BC.Key.RegisterInitialValues,
+ Repetitor->Repeat(Instructions, MinInstructionsForSnippet,
+ LoopBodySizeForSnippet),
+ OS)) {
+ return std::move(E);
+ }
+ const ExecutableFunction EF(State.createTargetMachine(),
+ getObjectFromBuffer(OS.str()));
+ const auto FnBytes = EF.getFunctionBytes();
+ llvm::append_range(InstrBenchmark.AssembledSnippet, FnBytes);
+ }
+
+ // Assemble NumRepetitions instructions repetitions of the snippet for
+ // measurements.
+ const auto Filler = Repetitor->Repeat(
+ Instructions, InstrBenchmark.NumRepetitions, LoopBodySize);
+
+ object::OwningBinary<object::ObjectFile> ObjectFile;
+ if (DumpObjectToDisk) {
+ auto ObjectFilePath = writeObjectFile(BC, Filler);
+ if (Error E = ObjectFilePath.takeError()) {
+ InstrBenchmark.Error = toString(std::move(E));
+ return InstrBenchmark;
+ }
+ outs() << "Check generated assembly with: /usr/bin/objdump -d "
+ << *ObjectFilePath << "\n";
+ ObjectFile = getObjectFromFile(*ObjectFilePath);
+ } else {
+ SmallString<0> Buffer;
+ raw_svector_ostream OS(Buffer);
+ if (Error E = assembleToStream(
+ State.getExegesisTarget(), State.createTargetMachine(),
+ BC.LiveIns, BC.Key.RegisterInitialValues, Filler, OS)) {
+ return std::move(E);
+ }
+ ObjectFile = getObjectFromBuffer(OS.str());
+ }
+
+ const FunctionExecutorImpl Executor(State, std::move(ObjectFile),
+ Scratch.get());
+ auto NewMeasurements = runMeasurements(Executor);
+ if (Error E = NewMeasurements.takeError()) {
+ if (!E.isA<SnippetCrash>())
+ return std::move(E);
+ InstrBenchmark.Error = toString(std::move(E));
+ return InstrBenchmark;
+ }
+ assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions");
+ for (BenchmarkMeasure &BM : *NewMeasurements) {
+ // Scale the measurements by instruction.
+ BM.PerInstructionValue /= InstrBenchmark.NumRepetitions;
+ // Scale the measurements by snippet.
+ BM.PerSnippetValue *= static_cast<double>(Instructions.size()) /
+ InstrBenchmark.NumRepetitions;
+ }
+ if (InstrBenchmark.Measurements.empty()) {
+ InstrBenchmark.Measurements = std::move(*NewMeasurements);
+ continue;
+ }
+
+ assert(Repetitors.size() > 1 && !InstrBenchmark.Measurements.empty() &&
+ "We're in an 'min' repetition mode, and need to aggregate new "
+ "result to the existing result.");
+ assert(InstrBenchmark.Measurements.size() == NewMeasurements->size() &&
+ "Expected to have identical number of measurements.");
+ for (auto I : zip(InstrBenchmark.Measurements, *NewMeasurements)) {
+ BenchmarkMeasure &Measurement = std::get<0>(I);
+ BenchmarkMeasure &NewMeasurement = std::get<1>(I);
+ assert(Measurement.Key == NewMeasurement.Key &&
+ "Expected measurements to be symmetric");
+
+ Measurement.PerInstructionValue = std::min(
+ Measurement.PerInstructionValue, NewMeasurement.PerInstructionValue);
+ Measurement.PerSnippetValue =
+ std::min(Measurement.PerSnippetValue, NewMeasurement.PerSnippetValue);
+ }
+ }
+
+ // We successfully measured everything, so don't discard the results.
+ CBOR.disarm();
+ return InstrBenchmark;
+}
+
+Expected<std::string>
+BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC,
+ const FillFunction &FillFunction) const {
+ int ResultFD = 0;
+ SmallString<256> ResultPath;
+ if (Error E = errorCodeToError(
+ sys::fs::createTemporaryFile("snippet", "o", ResultFD, ResultPath)))
+ return std::move(E);
+ raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/);
+ if (Error E = assembleToStream(
+ State.getExegesisTarget(), State.createTargetMachine(), BC.LiveIns,
+ BC.Key.RegisterInitialValues, FillFunction, OFS)) {
+ return std::move(E);
+ }
+ return std::string(ResultPath.str());
+}
+
+BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkRunner.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkRunner.h
new file mode 100644
index 00000000000..b66902e6c0d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/BenchmarkRunner.h
@@ -0,0 +1,94 @@
+//===-- BenchmarkRunner.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines the abstract BenchmarkRunner class for measuring a certain execution
+/// property of instructions (e.g. latency).
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H
+
+#include "Assembler.h"
+#include "BenchmarkCode.h"
+#include "BenchmarkResult.h"
+#include "LlvmState.h"
+#include "MCInstrDescView.h"
+#include "SnippetRepetitor.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/Support/Error.h"
+#include <cstdlib>
+#include <memory>
+#include <vector>
+
+namespace llvm {
+namespace exegesis {
+
+// Common code for all benchmark modes.
+class BenchmarkRunner {
+public:
+ explicit BenchmarkRunner(const LLVMState &State,
+ InstructionBenchmark::ModeE Mode);
+
+ virtual ~BenchmarkRunner();
+
+ Expected<InstructionBenchmark>
+ runConfiguration(const BenchmarkCode &Configuration, unsigned NumRepetitions,
+ unsigned LoopUnrollFactor,
+ ArrayRef<std::unique_ptr<const SnippetRepetitor>> Repetitors,
+ bool DumpObjectToDisk) 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(std::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;
+ };
+
+ // A helper to measure counters while executing a function in a sandboxed
+ // context.
+ class FunctionExecutor {
+ public:
+ virtual ~FunctionExecutor();
+ // FIXME deprecate this.
+ virtual Expected<int64_t> runAndMeasure(const char *Counters) const = 0;
+
+ virtual Expected<llvm::SmallVector<int64_t, 4>>
+ runAndSample(const char *Counters) const = 0;
+ };
+
+protected:
+ const LLVMState &State;
+ const InstructionBenchmark::ModeE Mode;
+
+private:
+ virtual Expected<std::vector<BenchmarkMeasure>>
+ runMeasurements(const FunctionExecutor &Executor) const = 0;
+
+ Expected<std::string> writeObjectFile(const BenchmarkCode &Configuration,
+ const FillFunction &Fill) const;
+
+ const std::unique_ptr<ScratchSpace> Scratch;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/Clustering.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Clustering.cpp
new file mode 100644
index 00000000000..08646aac52e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Clustering.cpp
@@ -0,0 +1,408 @@
+//===-- Clustering.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Clustering.h"
+#include "Error.h"
+#include "SchedClassResolution.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include <algorithm>
+#include <deque>
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace exegesis {
+
+// The clustering problem has the following characteristics:
+// (A) - Low dimension (dimensions are typically proc resource units,
+// typically < 10).
+// (B) - Number of points : ~thousands (points are measurements of an MCInst)
+// (C) - Number of clusters: ~tens.
+// (D) - The number of clusters is not known /a priory/.
+// (E) - The amount of noise is relatively small.
+// The problem is rather small. In terms of algorithms, (D) disqualifies
+// k-means and makes algorithms such as DBSCAN[1] or OPTICS[2] more applicable.
+//
+// We've used DBSCAN here because it's simple to implement. This is a pretty
+// straightforward and inefficient implementation of the pseudocode in [2].
+//
+// [1] https://en.wikipedia.org/wiki/DBSCAN
+// [2] https://en.wikipedia.org/wiki/OPTICS_algorithm
+
+// Finds the points at distance less than sqrt(EpsilonSquared) of Q (not
+// including Q).
+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)
+ continue;
+ const auto &PMeasurements = Points_[P].Measurements;
+ if (PMeasurements.empty()) // Error point.
+ continue;
+ if (isNeighbour(PMeasurements, QMeasurements,
+ AnalysisClusteringEpsilonSquared_)) {
+ Neighbors.push_back(P);
+ }
+ }
+}
+
+// Given a set of points, checks that all the points are neighbours
+// up to AnalysisClusteringEpsilon. This is O(2*N).
+bool InstructionBenchmarkClustering::areAllNeighbours(
+ ArrayRef<size_t> Pts) const {
+ // First, get the centroid of this group of points. This is O(N).
+ SchedClassClusterCentroid G;
+ for_each(Pts, [this, &G](size_t P) {
+ assert(P < Points_.size());
+ ArrayRef<BenchmarkMeasure> Measurements = Points_[P].Measurements;
+ if (Measurements.empty()) // Error point.
+ return;
+ G.addPoint(Measurements);
+ });
+ const std::vector<BenchmarkMeasure> Centroid = G.getAsPoint();
+
+ // Since we will be comparing with the centroid, we need to halve the epsilon.
+ double AnalysisClusteringEpsilonHalvedSquared =
+ AnalysisClusteringEpsilonSquared_ / 4.0;
+
+ // And now check that every point is a neighbour of the centroid. Also O(N).
+ return all_of(
+ Pts, [this, &Centroid, AnalysisClusteringEpsilonHalvedSquared](size_t P) {
+ assert(P < Points_.size());
+ const auto &PMeasurements = Points_[P].Measurements;
+ if (PMeasurements.empty()) // Error point.
+ return true; // Pretend that error point is a neighbour.
+ return isNeighbour(PMeasurements, Centroid,
+ AnalysisClusteringEpsilonHalvedSquared);
+ });
+}
+
+InstructionBenchmarkClustering::InstructionBenchmarkClustering(
+ const std::vector<InstructionBenchmark> &Points,
+ const double AnalysisClusteringEpsilonSquared)
+ : Points_(Points),
+ AnalysisClusteringEpsilonSquared_(AnalysisClusteringEpsilonSquared),
+ NoiseCluster_(ClusterId::noise()), ErrorCluster_(ClusterId::error()) {}
+
+Error InstructionBenchmarkClustering::validateAndSetup() {
+ ClusterIdForPoint_.resize(Points_.size());
+ // Mark erroneous measurements out.
+ // All points must have the same number of dimensions, in the same order.
+ const std::vector<BenchmarkMeasure> *LastMeasurement = nullptr;
+ for (size_t P = 0, NumPoints = Points_.size(); P < NumPoints; ++P) {
+ const auto &Point = Points_[P];
+ if (!Point.Error.empty()) {
+ ClusterIdForPoint_[P] = ClusterId::error();
+ ErrorCluster_.PointIndices.push_back(P);
+ continue;
+ }
+ const auto *CurMeasurement = &Point.Measurements;
+ if (LastMeasurement) {
+ if (LastMeasurement->size() != CurMeasurement->size()) {
+ return make_error<ClusteringError>(
+ "inconsistent measurement dimensions");
+ }
+ for (size_t I = 0, E = LastMeasurement->size(); I < E; ++I) {
+ if (LastMeasurement->at(I).Key != CurMeasurement->at(I).Key) {
+ return make_error<ClusteringError>(
+ "inconsistent measurement dimensions keys");
+ }
+ }
+ }
+ LastMeasurement = CurMeasurement;
+ }
+ if (LastMeasurement) {
+ NumDimensions_ = LastMeasurement->size();
+ }
+ return Error::success();
+}
+
+void InstructionBenchmarkClustering::clusterizeDbScan(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.
+ 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.
+ ClusterIdForPoint_[P] = ClusterId::noise();
+ continue;
+ }
+
+ // Create a new cluster, add P.
+ Clusters_.emplace_back(ClusterId::makeValid(Clusters_.size()));
+ Cluster &CurrentCluster = Clusters_.back();
+ ClusterIdForPoint_[P] = CurrentCluster.Id; /* Label initial point */
+ CurrentCluster.PointIndices.push_back(P);
+
+ // Process P's neighbors.
+ 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(ToProcess.begin());
+
+ if (ClusterIdForPoint_[Q].isNoise()) {
+ // Change noise point to border point.
+ ClusterIdForPoint_[Q] = CurrentCluster.Id;
+ CurrentCluster.PointIndices.push_back(Q);
+ continue;
+ }
+ if (!ClusterIdForPoint_[Q].isUndef()) {
+ continue; // Previously processed.
+ }
+ // Add Q to the current custer.
+ ClusterIdForPoint_[Q] = CurrentCluster.Id;
+ CurrentCluster.PointIndices.push_back(Q);
+ // And extend to the neighbors of Q if the region is dense enough.
+ 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) {
+ if (ClusterIdForPoint_[P].isNoise()) {
+ NoiseCluster_.PointIndices.push_back(P);
+ }
+ }
+}
+
+void InstructionBenchmarkClustering::clusterizeNaive(
+ const MCSubtargetInfo &SubtargetInfo, const MCInstrInfo &InstrInfo) {
+ // Given an instruction Opcode, which sched class id's are represented,
+ // and which are the benchmarks for each sched class?
+ std::vector<SmallMapVector<unsigned, SmallVector<size_t, 1>, 1>>
+ OpcodeToSchedClassesToPoints;
+ const unsigned NumOpcodes = InstrInfo.getNumOpcodes();
+ OpcodeToSchedClassesToPoints.resize(NumOpcodes);
+ size_t NumClusters = 0;
+ for (size_t P = 0, NumPoints = Points_.size(); P < NumPoints; ++P) {
+ const InstructionBenchmark &Point = Points_[P];
+ const MCInst &MCI = Point.keyInstruction();
+ unsigned SchedClassId;
+ std::tie(SchedClassId, std::ignore) =
+ ResolvedSchedClass::resolveSchedClassId(SubtargetInfo, InstrInfo, MCI);
+ const unsigned Opcode = MCI.getOpcode();
+ assert(Opcode < NumOpcodes && "NumOpcodes is incorrect (too small)");
+ auto &Points = OpcodeToSchedClassesToPoints[Opcode][SchedClassId];
+ if (Points.empty()) // If we previously have not seen any points of
+ ++NumClusters; // this opcode's sched class, then new cluster begins.
+ Points.emplace_back(P);
+ }
+ assert(NumClusters <= NumOpcodes &&
+ "can't see more opcodes than there are total opcodes");
+ assert(NumClusters <= Points_.size() &&
+ "can't see more opcodes than there are total points");
+
+ Clusters_.reserve(NumClusters); // We already know how many clusters there is.
+ for (const auto &SchedClassesOfOpcode : OpcodeToSchedClassesToPoints) {
+ if (SchedClassesOfOpcode.empty())
+ continue;
+ for (ArrayRef<size_t> PointsOfSchedClass :
+ make_second_range(SchedClassesOfOpcode)) {
+ if (PointsOfSchedClass.empty())
+ continue;
+ // Create a new cluster.
+ Clusters_.emplace_back(ClusterId::makeValid(
+ Clusters_.size(),
+ /*IsUnstable=*/!areAllNeighbours(PointsOfSchedClass)));
+ Cluster &CurrentCluster = Clusters_.back();
+ // Mark points as belonging to the new cluster.
+ for_each(PointsOfSchedClass, [this, &CurrentCluster](size_t P) {
+ ClusterIdForPoint_[P] = CurrentCluster.Id;
+ });
+ // And add all the points of this opcode's sched class to the new cluster.
+ CurrentCluster.PointIndices.reserve(PointsOfSchedClass.size());
+ CurrentCluster.PointIndices.assign(PointsOfSchedClass.begin(),
+ PointsOfSchedClass.end());
+ assert(CurrentCluster.PointIndices.size() == PointsOfSchedClass.size());
+ }
+ }
+ assert(Clusters_.size() == NumClusters);
+}
+
+// Given an instruction Opcode, we can make benchmarks (measurements) of the
+// instruction characteristics/performance. Then, to facilitate further analysis
+// we group the benchmarks with *similar* characteristics into clusters.
+// Now, this is all not entirely deterministic. Some instructions have variable
+// characteristics, depending on their arguments. And thus, if we do several
+// benchmarks of the same instruction Opcode, we may end up with *different*
+// performance characteristics measurements. And when we then do clustering,
+// these several benchmarks of the same instruction Opcode may end up being
+// clustered into *different* clusters. This is not great for further analysis.
+// We shall find every opcode with benchmarks not in just one cluster, and move
+// *all* the benchmarks of said Opcode into one new unstable cluster per Opcode.
+void InstructionBenchmarkClustering::stabilize(unsigned NumOpcodes) {
+ // Given an instruction Opcode and Config, in which clusters do benchmarks of
+ // this instruction lie? Normally, they all should be in the same cluster.
+ struct OpcodeAndConfig {
+ explicit OpcodeAndConfig(const InstructionBenchmark &IB)
+ : Opcode(IB.keyInstruction().getOpcode()), Config(&IB.Key.Config) {}
+ unsigned Opcode;
+ const std::string *Config;
+
+ auto Tie() const -> auto { return std::tie(Opcode, *Config); }
+
+ bool operator<(const OpcodeAndConfig &O) const { return Tie() < O.Tie(); }
+ bool operator!=(const OpcodeAndConfig &O) const { return Tie() != O.Tie(); }
+ };
+ std::map<OpcodeAndConfig, SmallSet<ClusterId, 1>> OpcodeConfigToClusterIDs;
+ // Populate OpcodeConfigToClusterIDs and UnstableOpcodes data structures.
+ assert(ClusterIdForPoint_.size() == Points_.size() && "size mismatch");
+ for (auto Point : zip(Points_, ClusterIdForPoint_)) {
+ const ClusterId &ClusterIdOfPoint = std::get<1>(Point);
+ if (!ClusterIdOfPoint.isValid())
+ continue; // Only process fully valid clusters.
+ const OpcodeAndConfig Key(std::get<0>(Point));
+ SmallSet<ClusterId, 1> &ClusterIDsOfOpcode = OpcodeConfigToClusterIDs[Key];
+ ClusterIDsOfOpcode.insert(ClusterIdOfPoint);
+ }
+
+ for (const auto &OpcodeConfigToClusterID : OpcodeConfigToClusterIDs) {
+ const SmallSet<ClusterId, 1> &ClusterIDs = OpcodeConfigToClusterID.second;
+ const OpcodeAndConfig &Key = OpcodeConfigToClusterID.first;
+ // We only care about unstable instructions.
+ if (ClusterIDs.size() < 2)
+ continue;
+
+ // Create a new unstable cluster, one per Opcode.
+ Clusters_.emplace_back(ClusterId::makeValidUnstable(Clusters_.size()));
+ Cluster &UnstableCluster = Clusters_.back();
+ // We will find *at least* one point in each of these clusters.
+ UnstableCluster.PointIndices.reserve(ClusterIDs.size());
+
+ // Go through every cluster which we recorded as containing benchmarks
+ // of this UnstableOpcode. NOTE: we only recorded valid clusters.
+ for (const ClusterId &CID : ClusterIDs) {
+ assert(CID.isValid() &&
+ "We only recorded valid clusters, not noise/error clusters.");
+ Cluster &OldCluster = Clusters_[CID.getId()]; // Valid clusters storage.
+ // Within each cluster, go through each point, and either move it to the
+ // new unstable cluster, or 'keep' it.
+ // In this case, we'll reshuffle OldCluster.PointIndices vector
+ // so that all the points that are *not* for UnstableOpcode are first,
+ // and the rest of the points is for the UnstableOpcode.
+ const auto it = std::stable_partition(
+ OldCluster.PointIndices.begin(), OldCluster.PointIndices.end(),
+ [this, &Key](size_t P) {
+ return OpcodeAndConfig(Points_[P]) != Key;
+ });
+ assert(std::distance(it, OldCluster.PointIndices.end()) > 0 &&
+ "Should have found at least one bad point");
+ // Mark to-be-moved points as belonging to the new cluster.
+ std::for_each(it, OldCluster.PointIndices.end(),
+ [this, &UnstableCluster](size_t P) {
+ ClusterIdForPoint_[P] = UnstableCluster.Id;
+ });
+ // Actually append to-be-moved points to the new cluster.
+ UnstableCluster.PointIndices.insert(UnstableCluster.PointIndices.end(),
+ it, OldCluster.PointIndices.end());
+ // And finally, remove "to-be-moved" points form the old cluster.
+ OldCluster.PointIndices.erase(it, OldCluster.PointIndices.end());
+ // Now, the old cluster may end up being empty, but let's just keep it
+ // in whatever state it ended up. Purging empty clusters isn't worth it.
+ };
+ assert(UnstableCluster.PointIndices.size() > 1 &&
+ "New unstable cluster should end up with more than one point.");
+ assert(UnstableCluster.PointIndices.size() >= ClusterIDs.size() &&
+ "New unstable cluster should end up with no less points than there "
+ "was clusters");
+ }
+}
+
+Expected<InstructionBenchmarkClustering> InstructionBenchmarkClustering::create(
+ const std::vector<InstructionBenchmark> &Points, const ModeE Mode,
+ const size_t DbscanMinPts, const double AnalysisClusteringEpsilon,
+ const MCSubtargetInfo *SubtargetInfo, const MCInstrInfo *InstrInfo) {
+ InstructionBenchmarkClustering Clustering(
+ Points, AnalysisClusteringEpsilon * AnalysisClusteringEpsilon);
+ if (auto Error = Clustering.validateAndSetup()) {
+ return std::move(Error);
+ }
+ if (Clustering.ErrorCluster_.PointIndices.size() == Points.size()) {
+ return Clustering; // Nothing to cluster.
+ }
+
+ if (Mode == ModeE::Dbscan) {
+ Clustering.clusterizeDbScan(DbscanMinPts);
+
+ if (InstrInfo)
+ Clustering.stabilize(InstrInfo->getNumOpcodes());
+ } else /*if(Mode == ModeE::Naive)*/ {
+ if (!SubtargetInfo || !InstrInfo)
+ return make_error<Failure>("'naive' clustering mode requires "
+ "SubtargetInfo and InstrInfo to be present");
+ Clustering.clusterizeNaive(*SubtargetInfo, *InstrInfo);
+ }
+
+ return Clustering;
+}
+
+void SchedClassClusterCentroid::addPoint(ArrayRef<BenchmarkMeasure> Point) {
+ if (Representative.empty())
+ Representative.resize(Point.size());
+ assert(Representative.size() == Point.size() &&
+ "All points should have identical dimensions.");
+
+ for (auto I : zip(Representative, Point))
+ std::get<0>(I).push(std::get<1>(I));
+}
+
+std::vector<BenchmarkMeasure> SchedClassClusterCentroid::getAsPoint() const {
+ std::vector<BenchmarkMeasure> ClusterCenterPoint(Representative.size());
+ for (auto I : zip(ClusterCenterPoint, Representative))
+ std::get<0>(I).PerInstructionValue = std::get<1>(I).avg();
+ return ClusterCenterPoint;
+}
+
+bool SchedClassClusterCentroid::validate(
+ InstructionBenchmark::ModeE Mode) const {
+ size_t NumMeasurements = Representative.size();
+ switch (Mode) {
+ case InstructionBenchmark::Latency:
+ if (NumMeasurements != 1) {
+ errs()
+ << "invalid number of measurements in latency mode: expected 1, got "
+ << NumMeasurements << "\n";
+ return false;
+ }
+ break;
+ case InstructionBenchmark::Uops:
+ // Can have many measurements.
+ break;
+ case InstructionBenchmark::InverseThroughput:
+ if (NumMeasurements != 1) {
+ errs() << "invalid number of measurements in inverse throughput "
+ "mode: expected 1, got "
+ << NumMeasurements << "\n";
+ return false;
+ }
+ break;
+ default:
+ llvm_unreachable("unimplemented measurement matching mode");
+ return false;
+ }
+
+ return true; // All good.
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/Clustering.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Clustering.h
new file mode 100644
index 00000000000..a4da3af7749
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Clustering.h
@@ -0,0 +1,171 @@
+//===-- Clustering.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Utilities to compute benchmark result clusters.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H
+
+#include "BenchmarkResult.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Error.h"
+#include <limits>
+#include <vector>
+
+namespace llvm {
+namespace exegesis {
+
+class InstructionBenchmarkClustering {
+public:
+ enum ModeE { Dbscan, Naive };
+
+ // Clusters `Points` using DBSCAN with the given parameters. See the cc file
+ // for more explanations on the algorithm.
+ static Expected<InstructionBenchmarkClustering>
+ create(const std::vector<InstructionBenchmark> &Points, ModeE Mode,
+ size_t DbscanMinPts, double AnalysisClusteringEpsilon,
+ const MCSubtargetInfo *SubtargetInfo = nullptr,
+ const MCInstrInfo *InstrInfo = nullptr);
+
+ class ClusterId {
+ public:
+ static ClusterId noise() { return ClusterId(kNoise); }
+ static ClusterId error() { return ClusterId(kError); }
+ static ClusterId makeValid(size_t Id, bool IsUnstable = false) {
+ return ClusterId(Id, IsUnstable);
+ }
+ static ClusterId makeValidUnstable(size_t Id) {
+ return makeValid(Id, /*IsUnstable=*/true);
+ }
+
+ ClusterId() : Id_(kUndef), IsUnstable_(false) {}
+
+ // Compare id's, ignoring the 'unstability' bit.
+ bool operator==(const ClusterId &O) const { return Id_ == O.Id_; }
+ bool operator<(const ClusterId &O) const { return Id_ < O.Id_; }
+
+ bool isValid() const { return Id_ <= kMaxValid; }
+ bool isUnstable() const { return IsUnstable_; }
+ bool isNoise() const { return Id_ == kNoise; }
+ bool isError() const { return Id_ == kError; }
+ bool isUndef() const { return Id_ == kUndef; }
+
+ // Precondition: isValid().
+ size_t getId() const {
+ assert(isValid());
+ return Id_;
+ }
+
+ private:
+ ClusterId(size_t Id, bool IsUnstable = false)
+ : Id_(Id), IsUnstable_(IsUnstable) {}
+
+ static constexpr const size_t kMaxValid =
+ (std::numeric_limits<size_t>::max() >> 1) - 4;
+ static constexpr const size_t kNoise = kMaxValid + 1;
+ static constexpr const size_t kError = kMaxValid + 2;
+ static constexpr const size_t kUndef = kMaxValid + 3;
+
+ size_t Id_ : (std::numeric_limits<size_t>::digits - 1);
+ size_t IsUnstable_ : 1;
+ };
+ static_assert(sizeof(ClusterId) == sizeof(size_t), "should be a bit field.");
+
+ struct Cluster {
+ Cluster() = delete;
+ explicit Cluster(const ClusterId &Id) : Id(Id) {}
+
+ const ClusterId Id;
+ // Indices of benchmarks within the cluster.
+ std::vector<int> PointIndices;
+ };
+
+ ClusterId getClusterIdForPoint(size_t P) const {
+ return ClusterIdForPoint_[P];
+ }
+
+ const std::vector<InstructionBenchmark> &getPoints() const { return Points_; }
+
+ const Cluster &getCluster(ClusterId Id) const {
+ assert(!Id.isUndef() && "unlabeled cluster");
+ if (Id.isNoise()) {
+ return NoiseCluster_;
+ }
+ if (Id.isError()) {
+ return ErrorCluster_;
+ }
+ return Clusters_[Id.getId()];
+ }
+
+ const std::vector<Cluster> &getValidClusters() const { return Clusters_; }
+
+ // 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 double EpsilonSquared_) 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 AnalysisClusteringEpsilonSquared);
+
+ Error validateAndSetup();
+
+ void clusterizeDbScan(size_t MinPts);
+ void clusterizeNaive(const MCSubtargetInfo &SubtargetInfo,
+ const MCInstrInfo &InstrInfo);
+
+ // Stabilization is only needed if dbscan was used to clusterize.
+ void stabilize(unsigned NumOpcodes);
+
+ void rangeQuery(size_t Q, std::vector<size_t> &Scratchpad) const;
+
+ bool areAllNeighbours(ArrayRef<size_t> Pts) const;
+
+ const std::vector<InstructionBenchmark> &Points_;
+ const double AnalysisClusteringEpsilonSquared_;
+
+ int NumDimensions_ = 0;
+ // ClusterForPoint_[P] is the cluster id for Points[P].
+ std::vector<ClusterId> ClusterIdForPoint_;
+ std::vector<Cluster> Clusters_;
+ Cluster NoiseCluster_;
+ Cluster ErrorCluster_;
+};
+
+class SchedClassClusterCentroid {
+public:
+ const std::vector<PerInstructionStats> &getStats() const {
+ return Representative;
+ }
+
+ std::vector<BenchmarkMeasure> getAsPoint() const;
+
+ void addPoint(ArrayRef<BenchmarkMeasure> Point);
+
+ bool validate(InstructionBenchmark::ModeE Mode) const;
+
+private:
+ // Measurement stats for the points in the SchedClassCluster.
+ std::vector<PerInstructionStats> Representative;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/CodeTemplate.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/CodeTemplate.cpp
new file mode 100644
index 00000000000..9840a08c253
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/CodeTemplate.cpp
@@ -0,0 +1,115 @@
+//===-- CodeTemplate.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#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();
+}
+
+MCOperand &InstructionTemplate::getValueFor(const Variable &Var) {
+ return VariableValues[Var.getIndex()];
+}
+
+const MCOperand &InstructionTemplate::getValueFor(const Variable &Var) const {
+ return VariableValues[Var.getIndex()];
+}
+
+MCOperand &InstructionTemplate::getValueFor(const Operand &Op) {
+ return getValueFor(Instr->Variables[Op.getVariableIndex()]);
+}
+
+const MCOperand &InstructionTemplate::getValueFor(const Operand &Op) const {
+ return getValueFor(Instr->Variables[Op.getVariableIndex()]);
+}
+
+bool InstructionTemplate::hasImmediateVariables() const {
+ return any_of(Instr->Variables, [this](const Variable &Var) {
+ return Instr->getPrimaryOperand(Var).isImmediate();
+ });
+}
+
+MCInst InstructionTemplate::build() const {
+ 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 isPowerOf2_32(static_cast<uint32_t>(Execution));
+}
+
+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");
+}
+
+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 makeArrayRef(kAllExecutionModeBits);
+}
+
+SmallVector<ExecutionMode, 4> getExecutionModeBits(ExecutionMode Execution) {
+ 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/contrib/libs/llvm14/tools/llvm-exegesis/lib/CodeTemplate.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/CodeTemplate.h
new file mode 100644
index 00000000000..bea10304cba
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/CodeTemplate.h
@@ -0,0 +1,140 @@
+//===-- CodeTemplate.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \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;
+ MCOperand &getValueFor(const Variable &Var);
+ const MCOperand &getValueFor(const Variable &Var) const;
+ MCOperand &getValueFor(const Operand &Op);
+ const MCOperand &getValueFor(const Operand &Op) const;
+ bool hasImmediateVariables() const;
+ const Instruction &getInstr() const { return *Instr; }
+ ArrayRef<MCOperand> getVariableValues() const { return VariableValues; }
+ void setVariableValues(ArrayRef<MCOperand> NewVariableValues) {
+ assert(VariableValues.size() == NewVariableValues.size() &&
+ "Value count mismatch");
+ VariableValues.assign(NewVariableValues.begin(), NewVariableValues.end());
+ }
+
+ // Builds an MCInst from this InstructionTemplate setting its operands
+ // to the corresponding variable values. Precondition: All VariableValues must
+ // be set.
+ MCInst build() const;
+
+private:
+ const Instruction *Instr;
+ SmallVector<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.
+StringRef getName(ExecutionMode Execution);
+
+// Returns a sequence of increasing powers of two corresponding to all the
+// Execution flags.
+ArrayRef<ExecutionMode> getAllExecutionBits();
+
+// Decomposes Execution into individual set bits.
+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;
+ // See InstructionBenchmarkKey.::Config.
+ std::string Config;
+ // 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/contrib/libs/llvm14/tools/llvm-exegesis/lib/Error.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Error.cpp
new file mode 100644
index 00000000000..51ce41bf00b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Error.cpp
@@ -0,0 +1,31 @@
+//===-- Error.cpp -----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Error.h"
+
+namespace llvm {
+namespace exegesis {
+
+char ClusteringError::ID;
+
+void ClusteringError::log(raw_ostream &OS) const { OS << Msg; }
+
+std::error_code ClusteringError::convertToErrorCode() const {
+ return inconvertibleErrorCode();
+}
+
+char SnippetCrash::ID;
+
+void SnippetCrash::log(raw_ostream &OS) const { OS << Msg; }
+
+std::error_code SnippetCrash::convertToErrorCode() const {
+ return inconvertibleErrorCode();
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/Error.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Error.h
new file mode 100644
index 00000000000..e5fa093e6e1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Error.h
@@ -0,0 +1,57 @@
+//===-- Error.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_ERROR_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_ERROR_H
+
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace exegesis {
+
+// A class representing failures that happened within llvm-exegesis, they are
+// used to report informations to the user.
+class Failure : public StringError {
+public:
+ Failure(const Twine &S) : StringError(S, inconvertibleErrorCode()) {}
+};
+
+// A class representing failures that happened during clustering calculations.
+class ClusteringError : public ErrorInfo<ClusteringError> {
+public:
+ static char ID;
+ ClusteringError(const Twine &S) : Msg(S.str()) {}
+
+ void log(raw_ostream &OS) const override;
+
+ std::error_code convertToErrorCode() const override;
+
+private:
+ std::string Msg;
+};
+
+// A class representing failures that happened during snippet execution.
+// Instead of terminating the program crashes are logged into the output.
+class SnippetCrash : public ErrorInfo<SnippetCrash> {
+public:
+ static char ID;
+ SnippetCrash(const Twine &S) : Msg(S.str()) {}
+
+ void log(raw_ostream &OS) const override;
+
+ std::error_code convertToErrorCode() const override;
+
+private:
+ std::string Msg;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.cpp
new file mode 100644
index 00000000000..6cdefb8b067
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.cpp
@@ -0,0 +1,143 @@
+//===-- LatencyBenchmarkRunner.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LatencyBenchmarkRunner.h"
+
+#include "BenchmarkRunner.h"
+#include "Target.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Error.h"
+#include <algorithm>
+#include <cmath>
+
+namespace llvm {
+namespace exegesis {
+
+LatencyBenchmarkRunner::LatencyBenchmarkRunner(
+ const LLVMState &State, InstructionBenchmark::ModeE Mode,
+ InstructionBenchmark::ResultAggregationModeE ResultAgg)
+ : BenchmarkRunner(State, Mode) {
+ assert((Mode == InstructionBenchmark::Latency ||
+ Mode == InstructionBenchmark::InverseThroughput) &&
+ "invalid mode");
+ ResultAggMode = ResultAgg;
+}
+
+LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default;
+
+static double computeVariance(const llvm::SmallVector<int64_t, 4> &Values) {
+ if (Values.empty())
+ return 0.0;
+ double Sum = std::accumulate(Values.begin(), Values.end(), 0.0);
+
+ const double Mean = Sum / Values.size();
+ double Ret = 0;
+ for (const auto &V : Values) {
+ double Delta = V - Mean;
+ Ret += Delta * Delta;
+ }
+ return Ret / Values.size();
+}
+
+static int64_t findMin(const llvm::SmallVector<int64_t, 4> &Values) {
+ if (Values.empty())
+ return 0;
+ return *std::min_element(Values.begin(), Values.end());
+}
+
+static int64_t findMax(const llvm::SmallVector<int64_t, 4> &Values) {
+ if (Values.empty())
+ return 0;
+ return *std::max_element(Values.begin(), Values.end());
+}
+
+static int64_t findMean(const llvm::SmallVector<int64_t, 4> &Values) {
+ if (Values.empty())
+ return 0;
+ return std::accumulate(Values.begin(), Values.end(), 0.0) /
+ static_cast<double>(Values.size());
+}
+
+Expected<std::vector<BenchmarkMeasure>> LatencyBenchmarkRunner::runMeasurements(
+ const FunctionExecutor &Executor) const {
+ // Cycle measurements include some overhead from the kernel. Repeat the
+ // measure several times and return the aggregated value, as specified by
+ // ResultAggMode.
+ constexpr const int NumMeasurements = 30;
+ llvm::SmallVector<int64_t, 4> AccumulatedValues;
+ double MinVariance = std::numeric_limits<double>::infinity();
+ const char *CounterName = State.getPfmCounters().CycleCounter;
+ // Values count for each run.
+ int ValuesCount = 0;
+ for (size_t I = 0; I < NumMeasurements; ++I) {
+ auto ExpectedCounterValues = Executor.runAndSample(CounterName);
+ if (!ExpectedCounterValues)
+ return ExpectedCounterValues.takeError();
+ ValuesCount = ExpectedCounterValues.get().size();
+ if (ValuesCount == 1)
+ AccumulatedValues.push_back(ExpectedCounterValues.get()[0]);
+ else {
+ // We'll keep the reading with lowest variance (ie., most stable)
+ double Variance = computeVariance(*ExpectedCounterValues);
+ if (MinVariance > Variance) {
+ AccumulatedValues = std::move(ExpectedCounterValues.get());
+ MinVariance = Variance;
+ }
+ }
+ }
+
+ std::string ModeName;
+ switch (Mode) {
+ case InstructionBenchmark::Latency:
+ ModeName = "latency";
+ break;
+ case InstructionBenchmark::InverseThroughput:
+ ModeName = "inverse_throughput";
+ break;
+ default:
+ break;
+ }
+
+ switch (ResultAggMode) {
+ case InstructionBenchmark::MinVariance: {
+ if (ValuesCount == 1)
+ llvm::errs() << "Each sample only has one value. result-aggregation-mode "
+ "of min-variance is probably non-sensical\n";
+ std::vector<BenchmarkMeasure> Result;
+ Result.reserve(AccumulatedValues.size());
+ for (const int64_t Value : AccumulatedValues)
+ Result.push_back(BenchmarkMeasure::Create(ModeName, Value));
+ return std::move(Result);
+ }
+ case InstructionBenchmark::Min: {
+ std::vector<BenchmarkMeasure> Result;
+ Result.push_back(
+ BenchmarkMeasure::Create(ModeName, findMin(AccumulatedValues)));
+ return std::move(Result);
+ }
+ case InstructionBenchmark::Max: {
+ std::vector<BenchmarkMeasure> Result;
+ Result.push_back(
+ BenchmarkMeasure::Create(ModeName, findMax(AccumulatedValues)));
+ return std::move(Result);
+ }
+ case InstructionBenchmark::Mean: {
+ std::vector<BenchmarkMeasure> Result;
+ Result.push_back(
+ BenchmarkMeasure::Create(ModeName, findMean(AccumulatedValues)));
+ return std::move(Result);
+ }
+ }
+ return llvm::make_error<Failure>(llvm::Twine("Unexpected benchmark mode(")
+ .concat(std::to_string(Mode))
+ .concat(" and unexpected ResultAggMode ")
+ .concat(std::to_string(ResultAggMode)));
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.h
new file mode 100644
index 00000000000..b9b9efc25d1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/LatencyBenchmarkRunner.h
@@ -0,0 +1,38 @@
+//===-- LatencyBenchmarkRunner.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// A BenchmarkRunner implementation to measure instruction latencies.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H
+
+#include "BenchmarkRunner.h"
+
+namespace llvm {
+namespace exegesis {
+
+class LatencyBenchmarkRunner : public BenchmarkRunner {
+public:
+ LatencyBenchmarkRunner(
+ const LLVMState &State, InstructionBenchmark::ModeE Mode,
+ InstructionBenchmark::ResultAggregationModeE ResultAggMode);
+ ~LatencyBenchmarkRunner() override;
+
+private:
+ Expected<std::vector<BenchmarkMeasure>>
+ runMeasurements(const FunctionExecutor &Executor) const override;
+
+ InstructionBenchmark::ResultAggregationModeE ResultAggMode;
+};
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/LlvmState.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/LlvmState.cpp
new file mode 100644
index 00000000000..4797ceb3307
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/LlvmState.cpp
@@ -0,0 +1,82 @@
+//===-- LlvmState.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LlvmState.h"
+#include "Target.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCFixup.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+
+namespace llvm {
+namespace exegesis {
+
+LLVMState::LLVMState(const std::string &Triple, const std::string &CpuName,
+ const std::string &Features) {
+ std::string Error;
+ const Target *const TheTarget = TargetRegistry::lookupTarget(Triple, Error);
+ assert(TheTarget && "unknown target for host");
+ const TargetOptions Options;
+ TheTargetMachine.reset(
+ static_cast<LLVMTargetMachine *>(TheTarget->createTargetMachine(
+ Triple, CpuName, Features, Options, Reloc::Model::Static)));
+ assert(TheTargetMachine && "unable to create target machine");
+ TheExegesisTarget = ExegesisTarget::lookup(TheTargetMachine->getTargetTriple());
+ if (!TheExegesisTarget) {
+ errs() << "no exegesis target for " << Triple << ", using default\n";
+ TheExegesisTarget = &ExegesisTarget::getDefault();
+ }
+ PfmCounters = &TheExegesisTarget->getPfmCounters(CpuName);
+
+ BitVector ReservedRegs = getFunctionReservedRegs(getTargetMachine());
+ for (const unsigned Reg : TheExegesisTarget->getUnavailableRegisters())
+ ReservedRegs.set(Reg);
+ RATC.reset(
+ new RegisterAliasingTrackerCache(getRegInfo(), std::move(ReservedRegs)));
+ IC.reset(new InstructionsCache(getInstrInfo(), getRATC()));
+}
+
+LLVMState::LLVMState(const std::string &CpuName)
+ : LLVMState(sys::getProcessTriple(),
+ CpuName.empty() ? sys::getHostCPUName().str() : CpuName, "") {}
+
+std::unique_ptr<LLVMTargetMachine> LLVMState::createTargetMachine() const {
+ return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(
+ TheTargetMachine->getTarget().createTargetMachine(
+ TheTargetMachine->getTargetTriple().normalize(),
+ TheTargetMachine->getTargetCPU(),
+ TheTargetMachine->getTargetFeatureString(), TheTargetMachine->Options,
+ Reloc::Model::Static)));
+}
+
+bool LLVMState::canAssemble(const MCInst &Inst) const {
+ MCContext Context(TheTargetMachine->getTargetTriple(),
+ TheTargetMachine->getMCAsmInfo(),
+ TheTargetMachine->getMCRegisterInfo(),
+ TheTargetMachine->getMCSubtargetInfo());
+ std::unique_ptr<const MCCodeEmitter> CodeEmitter(
+ TheTargetMachine->getTarget().createMCCodeEmitter(
+ *TheTargetMachine->getMCInstrInfo(), *TheTargetMachine->getMCRegisterInfo(),
+ Context));
+ assert(CodeEmitter && "unable to create code emitter");
+ SmallVector<char, 16> Tmp;
+ raw_svector_ostream OS(Tmp);
+ SmallVector<MCFixup, 4> Fixups;
+ CodeEmitter->encodeInstruction(Inst, OS, Fixups,
+ *TheTargetMachine->getMCSubtargetInfo());
+ return Tmp.size() > 0;
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/LlvmState.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/LlvmState.h
new file mode 100644
index 00000000000..e660a9f56b4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/LlvmState.h
@@ -0,0 +1,79 @@
+//===-- LlvmState.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// A class to set up and access common LLVM objects.
+///
+//===----------------------------------------------------------------------===//
+
+#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"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Target/TargetMachine.h"
+#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:
+ // 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,
+ const std::string &Features = ""); // For tests.
+
+ const TargetMachine &getTargetMachine() const { return *TheTargetMachine; }
+ std::unique_ptr<LLVMTargetMachine> createTargetMachine() const;
+
+ const ExegesisTarget &getExegesisTarget() const { return *TheExegesisTarget; }
+
+ bool canAssemble(const MCInst &mc_inst) const;
+
+ // For convenience:
+ const MCInstrInfo &getInstrInfo() const {
+ return *TheTargetMachine->getMCInstrInfo();
+ }
+ const MCRegisterInfo &getRegInfo() const {
+ return *TheTargetMachine->getMCRegisterInfo();
+ }
+ const MCSubtargetInfo &getSubtargetInfo() const {
+ return *TheTargetMachine->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 TargetMachine> TheTargetMachine;
+ 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/contrib/libs/llvm14/tools/llvm-exegesis/lib/MCInstrDescView.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/MCInstrDescView.cpp
new file mode 100644
index 00000000000..049cc68b4fb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/MCInstrDescView.cpp
@@ -0,0 +1,400 @@
+//===-- MCInstrDescView.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MCInstrDescView.h"
+
+#include <iterator>
+#include <map>
+#include <tuple>
+
+#include "llvm/ADT/STLExtras.h"
+
+namespace llvm {
+namespace exegesis {
+
+unsigned Variable::getIndex() const { 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 { 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.hasValue(); }
+
+bool Operand::isVariable() const { return VariableIndex.hasValue(); }
+
+bool Operand::isMemory() const {
+ return isExplicit() &&
+ getExplicitOperandInfo().OperandType == MCOI::OPERAND_MEMORY;
+}
+
+bool Operand::isImmediate() const {
+ return isExplicit() &&
+ getExplicitOperandInfo().OperandType == MCOI::OPERAND_IMMEDIATE;
+}
+
+unsigned Operand::getTiedToIndex() const { return *TiedToIndex; }
+
+unsigned Operand::getVariableIndex() const { return *VariableIndex; }
+
+unsigned Operand::getImplicitReg() const {
+ assert(ImplicitReg);
+ return *ImplicitReg;
+}
+
+const RegisterAliasingTracker &Operand::getRegisterAliasing() const {
+ assert(Tracker);
+ return *Tracker;
+}
+
+const MCOperandInfo &Operand::getExplicitOperandInfo() const {
+ assert(Info);
+ return *Info;
+}
+
+const BitVector *BitVectorCache::getUnique(BitVector &&BV) const {
+ for (const auto &Entry : Cache)
+ if (*Entry == BV)
+ return Entry.get();
+ Cache.push_back(std::make_unique<BitVector>());
+ auto &Entry = Cache.back();
+ Entry->swap(BV);
+ return Entry.get();
+}
+
+Instruction::Instruction(const MCInstrDesc *Description, StringRef Name,
+ SmallVector<Operand, 8> Operands,
+ SmallVector<Variable, 4> Variables,
+ const BitVector *ImplDefRegs,
+ const BitVector *ImplUseRegs,
+ const BitVector *AllDefRegs,
+ const BitVector *AllUseRegs)
+ : Description(*Description), Name(Name), Operands(std::move(Operands)),
+ Variables(std::move(Variables)), ImplDefRegs(*ImplDefRegs),
+ ImplUseRegs(*ImplUseRegs), AllDefRegs(*AllDefRegs),
+ AllUseRegs(*AllUseRegs) {}
+
+std::unique_ptr<Instruction>
+Instruction::create(const MCInstrInfo &InstrInfo,
+ const RegisterAliasingTrackerCache &RATC,
+ const BitVectorCache &BVC, unsigned Opcode) {
+ const llvm::MCInstrDesc *const Description = &InstrInfo.get(Opcode);
+ unsigned OpIndex = 0;
+ SmallVector<Operand, 8> Operands;
+ SmallVector<Variable, 4> Variables;
+ for (; OpIndex < Description->getNumOperands(); ++OpIndex) {
+ const auto &OpInfo = Description->opInfo_begin()[OpIndex];
+ Operand Operand;
+ Operand.Index = OpIndex;
+ Operand.IsDef = (OpIndex < Description->getNumDefs());
+ // TODO(gchatelet): Handle isLookupPtrRegClass.
+ if (OpInfo.RegClass >= 0)
+ Operand.Tracker = &RATC.getRegisterClass(OpInfo.RegClass);
+ int TiedToIndex = Description->getOperandConstraint(OpIndex, MCOI::TIED_TO);
+ assert((TiedToIndex == -1 ||
+ (0 <= TiedToIndex &&
+ TiedToIndex < std::numeric_limits<uint8_t>::max())) &&
+ "Unknown Operand Constraint");
+ if (TiedToIndex >= 0)
+ Operand.TiedToIndex = TiedToIndex;
+ Operand.Info = &OpInfo;
+ Operands.push_back(Operand);
+ }
+ for (const MCPhysReg *MCPhysReg = Description->getImplicitDefs();
+ MCPhysReg && *MCPhysReg; ++MCPhysReg, ++OpIndex) {
+ Operand Operand;
+ Operand.Index = OpIndex;
+ Operand.IsDef = true;
+ Operand.Tracker = &RATC.getRegister(*MCPhysReg);
+ Operand.ImplicitReg = MCPhysReg;
+ Operands.push_back(Operand);
+ }
+ for (const MCPhysReg *MCPhysReg = Description->getImplicitUses();
+ MCPhysReg && *MCPhysReg; ++MCPhysReg, ++OpIndex) {
+ Operand Operand;
+ Operand.Index = OpIndex;
+ Operand.IsDef = false;
+ Operand.Tracker = &RATC.getRegister(*MCPhysReg);
+ Operand.ImplicitReg = MCPhysReg;
+ Operands.push_back(Operand);
+ }
+ Variables.reserve(Operands.size()); // Variables.size() <= Operands.size()
+ // Assigning Variables to non tied explicit operands.
+ for (auto &Op : Operands)
+ if (Op.isExplicit() && !Op.isTied()) {
+ const size_t VariableIndex = Variables.size();
+ assert(VariableIndex < std::numeric_limits<uint8_t>::max());
+ Op.VariableIndex = VariableIndex;
+ Variables.emplace_back();
+ Variables.back().Index = VariableIndex;
+ }
+ // Assigning Variables to tied operands.
+ for (auto &Op : Operands)
+ if (Op.isExplicit() && Op.isTied())
+ Op.VariableIndex = Operands[Op.getTiedToIndex()].getVariableIndex();
+ // Assigning Operands to Variables.
+ for (auto &Op : Operands)
+ if (Op.isVariable())
+ Variables[Op.getVariableIndex()].TiedOperands.push_back(Op.getIndex());
+ // Processing Aliasing.
+ BitVector ImplDefRegs = RATC.emptyRegisters();
+ BitVector ImplUseRegs = RATC.emptyRegisters();
+ BitVector AllDefRegs = RATC.emptyRegisters();
+ BitVector AllUseRegs = RATC.emptyRegisters();
+ for (const auto &Op : Operands) {
+ 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;
+ }
+ }
+ // Can't use make_unique because constructor is private.
+ return std::unique_ptr<Instruction>(new Instruction(
+ Description, InstrInfo.getName(Opcode), std::move(Operands),
+ std::move(Variables), BVC.getUnique(std::move(ImplDefRegs)),
+ BVC.getUnique(std::move(ImplUseRegs)),
+ BVC.getUnique(std::move(AllDefRegs)),
+ BVC.getUnique(std::move(AllUseRegs))));
+}
+
+const Operand &Instruction::getPrimaryOperand(const Variable &Var) const {
+ const auto PrimaryOperandIndex = Var.getPrimaryOperandIndex();
+ assert(PrimaryOperandIndex < Operands.size());
+ return Operands[PrimaryOperandIndex];
+}
+
+bool Instruction::hasMemoryOperands() const {
+ return any_of(Operands, [](const Operand &Op) {
+ return Op.isReg() && Op.isExplicit() && Op.isMemory();
+ });
+}
+
+bool Instruction::hasAliasingImplicitRegisters() const {
+ return ImplDefRegs.anyCommon(ImplUseRegs);
+}
+
+// Returns true if there are registers that are both in `A` and `B` but not in
+// `Forbidden`.
+static bool anyCommonExcludingForbidden(const BitVector &A, const BitVector &B,
+ const BitVector &Forbidden) {
+ assert(A.size() == B.size() && B.size() == Forbidden.size());
+ const auto Size = A.size();
+ for (int AIndex = A.find_first(); AIndex != -1;) {
+ const int BIndex = B.find_first_in(AIndex, Size);
+ if (BIndex == -1)
+ return false;
+ if (AIndex == BIndex && !Forbidden.test(AIndex))
+ return true;
+ AIndex = A.find_first_in(BIndex + 1, Size);
+ }
+ return false;
+}
+
+bool Instruction::hasAliasingRegistersThrough(
+ const Instruction &OtherInstr, const BitVector &ForbiddenRegisters) const {
+ return anyCommonExcludingForbidden(AllDefRegs, OtherInstr.AllUseRegs,
+ ForbiddenRegisters) &&
+ anyCommonExcludingForbidden(OtherInstr.AllDefRegs, AllUseRegs,
+ ForbiddenRegisters);
+}
+
+bool Instruction::hasTiedRegisters() const {
+ return any_of(Variables,
+ [](const Variable &Var) { return Var.hasTiedOperands(); });
+}
+
+bool Instruction::hasAliasingRegisters(
+ const BitVector &ForbiddenRegisters) const {
+ return anyCommonExcludingForbidden(AllDefRegs, AllUseRegs,
+ ForbiddenRegisters);
+}
+
+bool Instruction::hasOneUseOrOneDef() const {
+ return AllDefRegs.count() || AllUseRegs.count();
+}
+
+void Instruction::dump(const MCRegisterInfo &RegInfo,
+ const RegisterAliasingTrackerCache &RATC,
+ 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(RATC.emptyRegisters()))
+ Stream << "- hasAliasingRegisters\n";
+}
+
+InstructionsCache::InstructionsCache(const MCInstrInfo &InstrInfo,
+ const RegisterAliasingTrackerCache &RATC)
+ : InstrInfo(InstrInfo), RATC(RATC), BVC() {}
+
+const Instruction &InstructionsCache::getInstr(unsigned Opcode) const {
+ auto &Found = Instructions[Opcode];
+ if (!Found)
+ Found = Instruction::create(InstrInfo, RATC, BVC, Opcode);
+ return *Found;
+}
+
+bool RegisterOperandAssignment::
+operator==(const RegisterOperandAssignment &Other) const {
+ return std::tie(Op, Reg) == std::tie(Other.Op, Other.Reg);
+}
+
+bool AliasingRegisterOperands::
+operator==(const AliasingRegisterOperands &Other) const {
+ return std::tie(Defs, Uses) == std::tie(Other.Defs, Other.Uses);
+}
+
+static void
+addOperandIfAlias(const MCPhysReg Reg, bool SelectDef,
+ ArrayRef<Operand> Operands,
+ SmallVectorImpl<RegisterOperandAssignment> &OperandValues) {
+ for (const auto &Op : Operands) {
+ if (Op.isReg() && Op.isDef() == SelectDef) {
+ const int SourceReg = Op.getRegisterAliasing().getOrigin(Reg);
+ if (SourceReg >= 0)
+ OperandValues.emplace_back(&Op, SourceReg);
+ }
+ }
+}
+
+bool AliasingRegisterOperands::hasImplicitAliasing() const {
+ const auto HasImplicit = [](const RegisterOperandAssignment &ROV) {
+ return ROV.Op->isImplicit();
+ };
+ return any_of(Defs, HasImplicit) && any_of(Uses, HasImplicit);
+}
+
+bool AliasingConfigurations::empty() const { return Configurations.empty(); }
+
+bool AliasingConfigurations::hasImplicitAliasing() const {
+ return any_of(Configurations, [](const AliasingRegisterOperands &ARO) {
+ return ARO.hasImplicitAliasing();
+ });
+}
+
+AliasingConfigurations::AliasingConfigurations(
+ const Instruction &DefInstruction, const Instruction &UseInstruction) {
+ if (UseInstruction.AllUseRegs.anyCommon(DefInstruction.AllDefRegs)) {
+ auto CommonRegisters = UseInstruction.AllUseRegs;
+ CommonRegisters &= DefInstruction.AllDefRegs;
+ for (const MCPhysReg Reg : CommonRegisters.set_bits()) {
+ AliasingRegisterOperands ARO;
+ addOperandIfAlias(Reg, true, DefInstruction.Operands, ARO.Defs);
+ addOperandIfAlias(Reg, false, UseInstruction.Operands, ARO.Uses);
+ if (!ARO.Defs.empty() && !ARO.Uses.empty() &&
+ !is_contained(Configurations, ARO))
+ Configurations.push_back(std::move(ARO));
+ }
+ }
+}
+
+void DumpMCOperand(const MCRegisterInfo &MCRegisterInfo, const MCOperand &Op,
+ raw_ostream &OS) {
+ if (!Op.isValid())
+ OS << "Invalid";
+ else if (Op.isReg())
+ OS << MCRegisterInfo.getName(Op.getReg());
+ else if (Op.isImm())
+ OS << Op.getImm();
+ else if (Op.isDFPImm())
+ OS << bit_cast<double>(Op.getDFPImm());
+ else if (Op.isSFPImm())
+ OS << bit_cast<float>(Op.getSFPImm());
+ else if (Op.isExpr())
+ OS << "Expr";
+ else if (Op.isInst())
+ OS << "SubInst";
+}
+
+void DumpMCInst(const MCRegisterInfo &MCRegisterInfo,
+ const MCInstrInfo &MCInstrInfo, const MCInst &MCInst,
+ raw_ostream &OS) {
+ OS << MCInstrInfo.getName(MCInst.getOpcode());
+ for (unsigned I = 0, E = MCInst.getNumOperands(); I < E; ++I) {
+ if (I > 0)
+ OS << ',';
+ OS << ' ';
+ DumpMCOperand(MCRegisterInfo, MCInst.getOperand(I), OS);
+ }
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/MCInstrDescView.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/MCInstrDescView.h
new file mode 100644
index 00000000000..8c7e0b2e01d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/MCInstrDescView.h
@@ -0,0 +1,239 @@
+//===-- MCInstrDescView.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Provide views around LLVM structures to represents an instruction instance,
+/// as well as its implicit and explicit arguments in a uniform way.
+/// Arguments that are explicit and independant (non tied) also have a Variable
+/// associated to them so the instruction can be fully defined by reading its
+/// Variables.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H
+
+#include <memory>
+#include <random>
+#include <unordered_map>
+
+#include "RegisterAliasing.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstrDesc.h"
+#include "llvm/MC/MCInstrInfo.h"
+
+namespace llvm {
+namespace exegesis {
+
+// 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.
+ SmallVector<unsigned, 2> TiedOperands;
+
+ // The index of this Variable in Instruction.Variables and its associated
+ // Value in InstructionBuilder.VariableValues.
+ Optional<uint8_t> Index;
+};
+
+// 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).
+// - Info: a shortcut for MCInstrDesc::operands()[Index].
+// - TiedToIndex: the index of the Operand holding the value or -1.
+// - ImplicitReg: a pointer to the register value when Operand is Implicit,
+// nullptr otherwise.
+// - VariableIndex: the index of the Variable holding the value for this Operand
+// or -1 if this operand is implicit.
+struct Operand {
+ 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 MCOperandInfo &getExplicitOperandInfo() const;
+
+ // Please use the accessors above and not the following fields.
+ Optional<uint8_t> Index;
+ bool IsDef = false;
+ const RegisterAliasingTracker *Tracker = nullptr; // Set for Register Op.
+ const MCOperandInfo *Info = nullptr; // Set for Explicit Op.
+ Optional<uint8_t> TiedToIndex; // Set for Reg&Explicit Op.
+ const MCPhysReg *ImplicitReg = nullptr; // Set for Implicit Op.
+ Optional<uint8_t> VariableIndex; // Set for Explicit Op.
+};
+
+/// A cache of BitVector to reuse between Instructions.
+/// The cache will only be exercised during Instruction initialization.
+/// For X86, this is ~160 unique vectors for all of the ~15K Instructions.
+struct BitVectorCache {
+ // Finds or allocates the provided BitVector in the cache and retrieves it's
+ // unique instance.
+ const BitVector *getUnique(BitVector &&BV) const;
+
+private:
+ mutable std::vector<std::unique_ptr<BitVector>> Cache;
+};
+
+// A view over an MCInstrDesc offering a convenient interface to compute
+// Register aliasing.
+struct Instruction {
+ // Create an instruction for a particular Opcode.
+ static std::unique_ptr<Instruction>
+ create(const MCInstrInfo &InstrInfo, const RegisterAliasingTrackerCache &RATC,
+ const BitVectorCache &BVC, unsigned Opcode);
+
+ // Prevent copy or move, instructions are allocated once and cached.
+ Instruction(const Instruction &) = delete;
+ Instruction(Instruction &&) = delete;
+ Instruction &operator=(const Instruction &) = delete;
+ Instruction &operator=(Instruction &&) = delete;
+
+ // 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;
+
+ // Whether this instruction is self aliasing through its tied registers.
+ // Repeating this instruction is guaranteed to executes sequentially.
+ bool hasTiedRegisters() const;
+
+ // Whether this instruction is self aliasing through its implicit registers.
+ // Repeating this instruction is guaranteed to executes sequentially.
+ bool hasAliasingImplicitRegisters() const;
+
+ // 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 BitVector &ForbiddenRegisters) const;
+
+ // Whether this instruction's registers alias with OtherInstr's registers.
+ bool hasAliasingRegistersThrough(const Instruction &OtherInstr,
+ const BitVector &ForbiddenRegisters) 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;
+
+ // 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;
+
+ // Convenient function to help with debugging.
+ void dump(const MCRegisterInfo &RegInfo,
+ const RegisterAliasingTrackerCache &RATC,
+ raw_ostream &Stream) const;
+
+ const MCInstrDesc &Description;
+ const StringRef Name; // The name of this instruction.
+ const SmallVector<Operand, 8> Operands;
+ const SmallVector<Variable, 4> Variables;
+ const BitVector &ImplDefRegs; // The set of aliased implicit def registers.
+ const BitVector &ImplUseRegs; // The set of aliased implicit use registers.
+ const BitVector &AllDefRegs; // The set of all aliased def registers.
+ const BitVector &AllUseRegs; // The set of all aliased use registers.
+private:
+ Instruction(const MCInstrDesc *Description, StringRef Name,
+ SmallVector<Operand, 8> Operands,
+ SmallVector<Variable, 4> Variables, const BitVector *ImplDefRegs,
+ const BitVector *ImplUseRegs, const BitVector *AllDefRegs,
+ const BitVector *AllUseRegs);
+};
+
+// Instructions are expensive to instantiate. This class provides a cache of
+// Instructions with lazy construction.
+struct InstructionsCache {
+ InstructionsCache(const MCInstrInfo &InstrInfo,
+ const RegisterAliasingTrackerCache &RATC);
+
+ // Returns the Instruction object corresponding to this Opcode.
+ const Instruction &getInstr(unsigned Opcode) const;
+
+private:
+ const MCInstrInfo &InstrInfo;
+ const RegisterAliasingTrackerCache &RATC;
+ mutable std::unordered_map<unsigned, std::unique_ptr<Instruction>>
+ Instructions;
+ const BitVectorCache BVC;
+};
+
+// Represents the assignment of a Register to an Operand.
+struct RegisterOperandAssignment {
+ RegisterOperandAssignment(const Operand *Operand, MCPhysReg Reg)
+ : Op(Operand), Reg(Reg) {}
+
+ const Operand *Op; // Pointer to an Explicit Register Operand.
+ MCPhysReg Reg;
+
+ bool operator==(const RegisterOperandAssignment &other) const;
+};
+
+// Represents a set of Operands that would alias through the use of some
+// Registers.
+// There are two reasons why operands would alias:
+// - The registers assigned to each of the operands are the same or alias each
+// other (e.g. AX/AL)
+// - The operands are tied.
+struct AliasingRegisterOperands {
+ SmallVector<RegisterOperandAssignment, 1> Defs; // Unlikely size() > 1.
+ SmallVector<RegisterOperandAssignment, 2> Uses;
+
+ // True is Defs and Use contain an Implicit Operand.
+ bool hasImplicitAliasing() const;
+
+ bool operator==(const AliasingRegisterOperands &other) const;
+};
+
+// Returns all possible configurations leading Def registers of DefInstruction
+// to alias with Use registers of UseInstruction.
+struct AliasingConfigurations {
+ AliasingConfigurations(const Instruction &DefInstruction,
+ const Instruction &UseInstruction);
+
+ bool empty() const; // True if no aliasing configuration is found.
+ bool hasImplicitAliasing() const;
+
+ SmallVector<AliasingRegisterOperands, 32> Configurations;
+};
+
+// Writes MCInst to OS.
+// This is not assembly but the internal LLVM's name for instructions and
+// registers.
+void DumpMCInst(const MCRegisterInfo &MCRegisterInfo,
+ const MCInstrInfo &MCInstrInfo, const MCInst &MCInst,
+ raw_ostream &OS);
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp
new file mode 100644
index 00000000000..7728fcb5d62
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp
@@ -0,0 +1,257 @@
+//===-- ParallelSnippetGenerator.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ParallelSnippetGenerator.h"
+
+#include "BenchmarkRunner.h"
+#include "MCInstrDescView.h"
+#include "Target.h"
+
+// FIXME: Load constants into registers (e.g. with fld1) to not break
+// instructions like x87.
+
+// Ideally we would like the only limitation on executing instructions to be the
+// availability of the CPU resources (e.g. execution ports) needed to execute
+// them, instead of the availability of their data dependencies.
+
+// To achieve that, one approach is to generate instructions that do not have
+// data dependencies between them.
+//
+// For some instructions, this is trivial:
+// mov rax, qword ptr [rsi]
+// mov rax, qword ptr [rsi]
+// mov rax, qword ptr [rsi]
+// mov rax, qword ptr [rsi]
+// For the above snippet, haswell just renames rax four times and executes the
+// four instructions two at a time on P23 and P0126.
+//
+// For some instructions, we just need to make sure that the source is
+// different from the destination. For example, IDIV8r reads from GPR and
+// writes to AX. We just need to ensure that the Var is assigned a
+// register which is different from AX:
+// idiv bx
+// idiv bx
+// idiv bx
+// idiv bx
+// The above snippet will be able to fully saturate the ports, while the same
+// with ax would issue one uop every `latency(IDIV8r)` cycles.
+//
+// Some instructions make this harder because they both read and write from
+// the same register:
+// inc rax
+// inc rax
+// inc rax
+// inc rax
+// This has a data dependency from each instruction to the next, limit the
+// number of instructions that can be issued in parallel.
+// It turns out that this is not a big issue on recent Intel CPUs because they
+// have heuristics to balance port pressure. In the snippet above, subsequent
+// instructions will end up evenly distributed on {P0,P1,P5,P6}, but some CPUs
+// might end up executing them all on P0 (just because they can), or try
+// avoiding P5 because it's usually under high pressure from vector
+// instructions.
+// This issue is even more important for high-latency instructions because
+// they increase the idle time of the CPU, e.g. :
+// imul rax, rbx
+// imul rax, rbx
+// imul rax, rbx
+// imul rax, rbx
+//
+// To avoid that, we do the renaming statically by generating as many
+// independent exclusive assignments as possible (until all possible registers
+// are exhausted) e.g.:
+// imul rax, rbx
+// imul rcx, rbx
+// imul rdx, rbx
+// imul r8, rbx
+//
+// Some instruction even make the above static renaming impossible because
+// they implicitly read and write from the same operand, e.g. ADC16rr reads
+// and writes from EFLAGS.
+// In that case we just use a greedy register assignment and hope for the
+// best.
+
+namespace llvm {
+namespace exegesis {
+
+static SmallVector<const Variable *, 8>
+getVariablesWithTiedOperands(const Instruction &Instr) {
+ SmallVector<const Variable *, 8> Result;
+ for (const auto &Var : Instr.Variables)
+ if (Var.hasTiedOperands())
+ Result.push_back(&Var);
+ return Result;
+}
+
+ParallelSnippetGenerator::~ParallelSnippetGenerator() = default;
+
+void ParallelSnippetGenerator::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");
+}
+
+static std::vector<InstructionTemplate> generateSnippetUsingStaticRenaming(
+ const LLVMState &State, const InstructionTemplate &IT,
+ const ArrayRef<const Variable *> TiedVariables,
+ const BitVector &ForbiddenRegisters) {
+ std::vector<InstructionTemplate> Instructions;
+ // Assign registers to variables in a round-robin manner. This is simple but
+ // ensures that the most register-constrained variable does not get starved.
+ std::vector<BitVector> PossibleRegsForVar;
+ for (const Variable *Var : TiedVariables) {
+ assert(Var);
+ const Operand &Op = IT.getInstr().getPrimaryOperand(*Var);
+ assert(Op.isReg());
+ BitVector PossibleRegs = Op.getRegisterAliasing().sourceBits();
+ remove(PossibleRegs, ForbiddenRegisters);
+ PossibleRegsForVar.push_back(std::move(PossibleRegs));
+ }
+ SmallVector<int, 2> Iterators(TiedVariables.size(), 0);
+ while (true) {
+ InstructionTemplate TmpIT = IT;
+ // Find a possible register for each variable in turn, marking the
+ // register as taken.
+ for (size_t VarId = 0; VarId < TiedVariables.size(); ++VarId) {
+ const int NextPossibleReg =
+ PossibleRegsForVar[VarId].find_next(Iterators[VarId]);
+ if (NextPossibleReg <= 0) {
+ return Instructions;
+ }
+ TmpIT.getValueFor(*TiedVariables[VarId]) =
+ MCOperand::createReg(NextPossibleReg);
+ // Bump iterator.
+ Iterators[VarId] = NextPossibleReg;
+ // Prevent other variables from using the register.
+ for (BitVector &OtherPossibleRegs : PossibleRegsForVar) {
+ OtherPossibleRegs.reset(NextPossibleReg);
+ }
+ }
+ Instructions.push_back(std::move(TmpIT));
+ }
+}
+
+Expected<std::vector<CodeTemplate>>
+ParallelSnippetGenerator::generateCodeTemplates(
+ InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
+ const Instruction &Instr = Variant.getInstr();
+ CodeTemplate CT;
+ CT.ScratchSpacePointerInReg =
+ Instr.hasMemoryOperands()
+ ? State.getExegesisTarget().getScratchMemoryRegister(
+ State.getTargetMachine().getTargetTriple())
+ : 0;
+ const AliasingConfigurations SelfAliasing(Instr, Instr);
+ if (SelfAliasing.empty()) {
+ CT.Info = "instruction is parallel, repeating a random one.";
+ CT.Instructions.push_back(std::move(Variant));
+ instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
+ return getSingleton(std::move(CT));
+ }
+ if (SelfAliasing.hasImplicitAliasing()) {
+ CT.Info = "instruction is serial, repeating a random one.";
+ CT.Instructions.push_back(std::move(Variant));
+ instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
+ return getSingleton(std::move(CT));
+ }
+ const auto TiedVariables = getVariablesWithTiedOperands(Instr);
+ if (!TiedVariables.empty()) {
+ CT.Info = "instruction has tied variables, using static renaming.";
+ CT.Instructions = generateSnippetUsingStaticRenaming(
+ State, Variant, TiedVariables, ForbiddenRegisters);
+ instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
+ return getSingleton(std::move(CT));
+ }
+ // No tied variables, we pick random values for defs.
+
+ // We don't want to accidentally serialize the instruction,
+ // so we must be sure that we don't pick a def that is an implicit use,
+ // or a use that is an implicit def, so record implicit regs now.
+ BitVector ImplicitUses(State.getRegInfo().getNumRegs());
+ BitVector ImplicitDefs(State.getRegInfo().getNumRegs());
+ for (const auto &Op : Instr.Operands) {
+ if (Op.isReg() && Op.isImplicit() && !Op.isMemory()) {
+ assert(Op.isImplicitReg() && "Not an implicit register operand?");
+ if (Op.isUse())
+ ImplicitUses.set(Op.getImplicitReg());
+ else {
+ assert(Op.isDef() && "Not a use and not a def?");
+ ImplicitDefs.set(Op.getImplicitReg());
+ }
+ }
+ }
+ const auto ImplicitUseAliases =
+ getAliasedBits(State.getRegInfo(), ImplicitUses);
+ const auto ImplicitDefAliases =
+ getAliasedBits(State.getRegInfo(), ImplicitDefs);
+ BitVector Defs(State.getRegInfo().getNumRegs());
+ for (const auto &Op : Instr.Operands) {
+ if (Op.isReg() && Op.isExplicit() && Op.isDef() && !Op.isMemory()) {
+ auto PossibleRegisters = Op.getRegisterAliasing().sourceBits();
+ // Do not use forbidden registers and regs that are implicitly used.
+ // Note that we don't try to avoid using implicit defs explicitly.
+ remove(PossibleRegisters, ForbiddenRegisters);
+ remove(PossibleRegisters, ImplicitUseAliases);
+ if (!PossibleRegisters.any())
+ return make_error<StringError>(
+ Twine("no available registers:\ncandidates:\n")
+ .concat(debugString(State.getRegInfo(),
+ Op.getRegisterAliasing().sourceBits()))
+ .concat("\nforbidden:\n")
+ .concat(debugString(State.getRegInfo(), ForbiddenRegisters))
+ .concat("\nimplicit use:\n")
+ .concat(debugString(State.getRegInfo(), ImplicitUseAliases)),
+ inconvertibleErrorCode());
+ const auto RandomReg = randomBit(PossibleRegisters);
+ Defs.set(RandomReg);
+ Variant.getValueFor(Op) = MCOperand::createReg(RandomReg);
+ }
+ }
+ // And pick random use values that are not reserved and don't alias with defs.
+ // Note that we don't try to avoid using implicit uses explicitly.
+ const auto DefAliases = getAliasedBits(State.getRegInfo(), Defs);
+ for (const auto &Op : Instr.Operands) {
+ if (Op.isReg() && Op.isExplicit() && Op.isUse() && !Op.isMemory()) {
+ auto PossibleRegisters = Op.getRegisterAliasing().sourceBits();
+ remove(PossibleRegisters, ForbiddenRegisters);
+ remove(PossibleRegisters, DefAliases);
+ remove(PossibleRegisters, ImplicitDefAliases);
+ assert(PossibleRegisters.any() && "No register left to choose from");
+ const auto RandomReg = randomBit(PossibleRegisters);
+ Variant.getValueFor(Op) = MCOperand::createReg(RandomReg);
+ }
+ }
+ CT.Info =
+ "instruction has no tied variables picking Uses different from defs";
+ CT.Instructions.push_back(std::move(Variant));
+ instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions);
+ return getSingleton(std::move(CT));
+}
+
+constexpr const size_t ParallelSnippetGenerator::kMinNumDifferentAddresses;
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h
new file mode 100644
index 00000000000..94eb4e26eb5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h
@@ -0,0 +1,65 @@
+//===-- ParallelSnippetGenerator.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// A SnippetGenerator implementation to create parallel instruction snippets.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_PARALLELSNIPPETGENERATOR_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_PARALLELSNIPPETGENERATOR_H
+
+#include "SnippetGenerator.h"
+
+namespace llvm {
+namespace exegesis {
+
+class ParallelSnippetGenerator : public SnippetGenerator {
+public:
+ using SnippetGenerator::SnippetGenerator;
+ ~ParallelSnippetGenerator() override;
+
+ Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(InstructionTemplate Variant,
+ const BitVector &ForbiddenRegisters) 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;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_PARALLELSNIPPETGENERATOR_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/PerfHelper.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/PerfHelper.cpp
new file mode 100644
index 00000000000..e77980022d5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/PerfHelper.cpp
@@ -0,0 +1,168 @@
+//===-- PerfHelper.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PerfHelper.h"
+#include "llvm/Config/config.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+#ifdef HAVE_LIBPFM
+#error #include <perfmon/perf_event.h>
+#error #include <perfmon/pfmlib.h>
+#error #include <perfmon/pfmlib_perf_event.h>
+#endif
+
+#include <cassert>
+#include <cstddef>
+#include <errno.h> // for erno
+#include <string.h> // for strerror()
+
+namespace llvm {
+namespace exegesis {
+namespace pfm {
+
+#ifdef HAVE_LIBPFM
+static bool isPfmError(int Code) { return Code != PFM_SUCCESS; }
+#endif
+
+bool pfmInitialize() {
+#ifdef HAVE_LIBPFM
+ return isPfmError(pfm_initialize());
+#else
+ return true;
+#endif
+}
+
+void pfmTerminate() {
+#ifdef HAVE_LIBPFM
+ pfm_terminate();
+#endif
+}
+
+PerfEvent::~PerfEvent() {
+#ifdef HAVE_LIBPFM
+ delete Attr;
+ ;
+#endif
+}
+
+PerfEvent::PerfEvent(PerfEvent &&Other)
+ : EventString(std::move(Other.EventString)),
+ FullQualifiedEventString(std::move(Other.FullQualifiedEventString)),
+ Attr(Other.Attr) {
+ Other.Attr = nullptr;
+}
+
+PerfEvent::PerfEvent(StringRef PfmEventString)
+ : EventString(PfmEventString.str()), Attr(nullptr) {
+#ifdef HAVE_LIBPFM
+ char *Fstr = nullptr;
+ pfm_perf_encode_arg_t Arg = {};
+ Attr = new perf_event_attr();
+ Arg.attr = Attr;
+ Arg.fstr = &Fstr;
+ Arg.size = sizeof(pfm_perf_encode_arg_t);
+ const int Result = pfm_get_os_event_encoding(EventString.c_str(), PFM_PLM3,
+ PFM_OS_PERF_EVENT, &Arg);
+ if (isPfmError(Result)) {
+ // We don't know beforehand which counters are available (e.g. 6 uops ports
+ // on Sandybridge but 8 on Haswell) so we report the missing counter without
+ // crashing.
+ errs() << pfm_strerror(Result) << " - cannot create event " << EventString
+ << "\n";
+ }
+ if (Fstr) {
+ FullQualifiedEventString = Fstr;
+ free(Fstr);
+ }
+#endif
+}
+
+StringRef PerfEvent::name() const { return EventString; }
+
+bool PerfEvent::valid() const { return !FullQualifiedEventString.empty(); }
+
+const perf_event_attr *PerfEvent::attribute() const { return Attr; }
+
+StringRef PerfEvent::getPfmEventString() const {
+ return FullQualifiedEventString;
+}
+
+#ifdef HAVE_LIBPFM
+Counter::Counter(PerfEvent &&E) : Event(std::move(E)){
+ assert(Event.valid());
+ const pid_t Pid = 0; // measure current process/thread.
+ const int Cpu = -1; // measure any processor.
+ const int GroupFd = -1; // no grouping of counters.
+ const uint32_t Flags = 0;
+ perf_event_attr AttrCopy = *Event.attribute();
+ FileDescriptor = perf_event_open(&AttrCopy, Pid, Cpu, GroupFd, Flags);
+ if (FileDescriptor == -1) {
+ errs() << "Unable to open event. ERRNO: " << strerror(errno)
+ << ". Make sure your kernel allows user "
+ "space perf monitoring.\nYou may want to try:\n$ sudo sh "
+ "-c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'\n";
+ }
+ assert(FileDescriptor != -1 && "Unable to open event");
+}
+
+Counter::~Counter() { close(FileDescriptor); }
+
+void Counter::start() { ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0); }
+
+void Counter::stop() { ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0); }
+
+int64_t Counter::read() const {
+ auto ValueOrError = readOrError();
+ if (ValueOrError) {
+ if (!ValueOrError.get().empty())
+ return ValueOrError.get()[0];
+ errs() << "Counter has no reading\n";
+ } else
+ errs() << ValueOrError.takeError() << "\n";
+ return -1;
+}
+
+llvm::Expected<llvm::SmallVector<int64_t, 4>>
+Counter::readOrError(StringRef /*unused*/) const {
+ int64_t Count = 0;
+ ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count));
+ if (ReadSize != sizeof(Count))
+ return llvm::make_error<llvm::StringError>("Failed to read event counter",
+ llvm::errc::io_error);
+ llvm::SmallVector<int64_t, 4> Result;
+ Result.push_back(Count);
+ return Result;
+}
+
+int Counter::numValues() const { return 1; }
+#else
+
+Counter::Counter(PerfEvent &&Event) : Event(std::move(Event)) {}
+
+Counter::~Counter() = default;
+
+void Counter::start() {}
+
+void Counter::stop() {}
+
+int64_t Counter::read() const { return 42; }
+
+llvm::Expected<llvm::SmallVector<int64_t, 4>>
+Counter::readOrError(StringRef /*unused*/) const {
+ return llvm::make_error<llvm::StringError>("Not implemented",
+ llvm::errc::io_error);
+}
+
+int Counter::numValues() const { return 1; }
+
+#endif
+
+} // namespace pfm
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/PerfHelper.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/PerfHelper.h
new file mode 100644
index 00000000000..19a35595c9a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/PerfHelper.h
@@ -0,0 +1,112 @@
+//===-- PerfHelper.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Helpers for measuring perf events.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_PERFHELPER_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_PERFHELPER_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/config.h"
+#include "llvm/Support/Error.h"
+
+#include <cstdint>
+#include <functional>
+#include <memory>
+
+struct perf_event_attr;
+
+namespace llvm {
+namespace exegesis {
+namespace pfm {
+
+// Returns true on error.
+bool pfmInitialize();
+void pfmTerminate();
+
+// Retrieves the encoding for the event described by pfm_event_string.
+// NOTE: pfm_initialize() must be called before creating PerfEvent objects.
+class PerfEvent {
+public:
+ // http://perfmon2.sourceforge.net/manv4/libpfm.html
+ // Events are expressed as strings. e.g. "INSTRUCTION_RETIRED"
+ explicit PerfEvent(StringRef PfmEventString);
+
+ PerfEvent(const PerfEvent &) = delete;
+ PerfEvent(PerfEvent &&other);
+ ~PerfEvent();
+
+ // The pfm_event_string passed at construction time.
+ StringRef name() const;
+
+ // Whether the event was successfully created.
+ bool valid() const;
+
+ // The encoded event to be passed to the Kernel.
+ const perf_event_attr *attribute() const;
+
+ // The fully qualified name for the event.
+ // e.g. "snb_ep::INSTRUCTION_RETIRED:e=0:i=0:c=0:t=0:u=1:k=0:mg=0:mh=1"
+ StringRef getPfmEventString() const;
+
+protected:
+ PerfEvent() = default;
+ std::string EventString;
+ std::string FullQualifiedEventString;
+ perf_event_attr *Attr;
+};
+
+// Uses a valid PerfEvent to configure the Kernel so we can measure the
+// underlying event.
+class Counter {
+public:
+ // event: the PerfEvent to measure.
+ explicit Counter(PerfEvent &&event);
+
+ Counter(const Counter &) = delete;
+ Counter(Counter &&other) = default;
+
+ virtual ~Counter();
+
+ /// Starts the measurement of the event.
+ virtual void start();
+
+ /// Stops the measurement of the event.
+ void stop();
+
+ /// Returns the current value of the counter or -1 if it cannot be read.
+ int64_t read() const;
+
+ /// Returns the current value of the counter or error if it cannot be read.
+ /// FunctionBytes: The benchmark function being executed.
+ /// This is used to filter out the measurements to ensure they are only
+ /// within the benchmarked code.
+ /// If empty (or not specified), then no filtering will be done.
+ /// Not all counters choose to use this.
+ virtual llvm::Expected<llvm::SmallVector<int64_t, 4>>
+ readOrError(StringRef FunctionBytes = StringRef()) const;
+
+ virtual int numValues() const;
+
+protected:
+ PerfEvent Event;
+#ifdef HAVE_LIBPFM
+ int FileDescriptor = -1;
+#endif
+};
+
+} // namespace pfm
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_PERFHELPER_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/Target.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/Target.cpp
new file mode 100644
index 00000000000..54d42dfd22e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/Target.cpp
@@ -0,0 +1,140 @@
+//===-- Target.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// The PowerPC ExegesisTarget.
+//===----------------------------------------------------------------------===//
+#include "../Target.h"
+#include "PPC.h"
+#include "PPCRegisterInfo.h"
+
+namespace llvm {
+namespace exegesis {
+
+// Helper to fill a memory operand with a value.
+static void setMemOp(InstructionTemplate &IT, int OpIdx,
+ const MCOperand &OpVal) {
+ const auto Op = IT.getInstr().Operands[OpIdx];
+ assert(Op.isExplicit() && "invalid memory pattern");
+ IT.getValueFor(Op) = OpVal;
+}
+
+#include "PPCGenExegesis.inc"
+
+namespace {
+class ExegesisPowerPCTarget : public ExegesisTarget {
+public:
+ ExegesisPowerPCTarget() : ExegesisTarget(PPCCpuPfmCounters) {}
+
+private:
+ std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, unsigned Reg,
+ const APInt &Value) const override;
+ bool matchesArch(Triple::ArchType Arch) const override {
+ return Arch == Triple::ppc64le;
+ }
+ unsigned getScratchMemoryRegister(const Triple &) const override;
+ void fillMemoryOperands(InstructionTemplate &IT, unsigned Reg,
+ unsigned Offset) const override;
+};
+} // end anonymous namespace
+
+static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) {
+ switch (RegBitWidth) {
+ case 32:
+ return PPC::LI;
+ case 64:
+ return PPC::LI8;
+ }
+ llvm_unreachable("Invalid Value Width");
+}
+
+// Generates instruction to load an immediate value into a register.
+static MCInst loadImmediate(unsigned Reg, unsigned RegBitWidth,
+ const APInt &Value) {
+ if (Value.getBitWidth() > RegBitWidth)
+ llvm_unreachable("Value must fit in the Register");
+ // We don't really care the value in reg, ignore the 16 bit
+ // restriction for now.
+ // TODO: make sure we get the exact value in reg if needed.
+ return MCInstBuilder(getLoadImmediateOpcode(RegBitWidth))
+ .addReg(Reg)
+ .addImm(Value.getZExtValue());
+}
+
+unsigned
+ExegesisPowerPCTarget::getScratchMemoryRegister(const Triple &TT) const {
+ // R13 is reserved as Thread Pointer, we won't use threading in benchmark, so
+ // use it as scratch memory register
+ return TT.isArch64Bit() ? PPC::X13 : PPC::R13;
+}
+
+void ExegesisPowerPCTarget::fillMemoryOperands(InstructionTemplate &IT,
+ unsigned Reg,
+ unsigned Offset) const {
+ int MemOpIdx = 0;
+ if (IT.getInstr().hasTiedRegisters())
+ MemOpIdx = 1;
+ int DispOpIdx = MemOpIdx + 1;
+ const auto DispOp = IT.getInstr().Operands[DispOpIdx];
+ if (DispOp.isReg())
+ // We don't really care about the real address in snippets,
+ // So hardcode X1 for X-form Memory Operations for simplicity.
+ // TODO: materialize the offset into a reggister
+ setMemOp(IT, DispOpIdx, MCOperand::createReg(PPC::X1));
+ else
+ setMemOp(IT, DispOpIdx, MCOperand::createImm(Offset)); // Disp
+ setMemOp(IT, MemOpIdx + 2, MCOperand::createReg(Reg)); // BaseReg
+}
+
+std::vector<MCInst> ExegesisPowerPCTarget::setRegTo(const MCSubtargetInfo &STI,
+ unsigned Reg,
+ const APInt &Value) const {
+ // X11 is optional use in function linkage, should be the least used one
+ // Use it as scratch reg to load immediate.
+ unsigned ScratchImmReg = PPC::X11;
+
+ if (PPC::GPRCRegClass.contains(Reg))
+ return {loadImmediate(Reg, 32, Value)};
+ if (PPC::G8RCRegClass.contains(Reg))
+ return {loadImmediate(Reg, 64, Value)};
+ if (PPC::F4RCRegClass.contains(Reg))
+ return {loadImmediate(ScratchImmReg, 64, Value),
+ MCInstBuilder(PPC::MTVSRD).addReg(Reg).addReg(ScratchImmReg)};
+ // We don't care the real value in reg, so set 64 bits or duplicate 64 bits
+ // for simplicity.
+ // TODO: update these if we need a accurate 128 values in registers.
+ if (PPC::VRRCRegClass.contains(Reg))
+ return {loadImmediate(ScratchImmReg, 64, Value),
+ MCInstBuilder(PPC::MTVRD).addReg(Reg).addReg(ScratchImmReg)};
+ if (PPC::VSRCRegClass.contains(Reg))
+ return {loadImmediate(ScratchImmReg, 64, Value),
+ MCInstBuilder(PPC::MTVSRDD)
+ .addReg(Reg)
+ .addReg(ScratchImmReg)
+ .addReg(ScratchImmReg)};
+ if (PPC::VFRCRegClass.contains(Reg))
+ return {loadImmediate(ScratchImmReg, 64, Value),
+ MCInstBuilder(PPC::MTVSRD).addReg(Reg).addReg(ScratchImmReg)};
+ // SPE not supported yet
+ if (PPC::SPERCRegClass.contains(Reg)) {
+ errs() << "Unsupported SPE Reg:" << Reg << "\n";
+ return {};
+ }
+ errs() << "setRegTo is not implemented, results will be unreliable:" << Reg
+ << "\n";
+ return {};
+}
+
+static ExegesisTarget *getTheExegesisPowerPCTarget() {
+ static ExegesisPowerPCTarget Target;
+ return &Target;
+}
+
+void InitializePowerPCExegesisTarget() {
+ ExegesisTarget::registerTarget(getTheExegesisPowerPCTarget());
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/ya.make b/contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/ya.make
new file mode 100644
index 00000000000..7f2d6ea594d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC/ya.make
@@ -0,0 +1,36 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/tools/llvm-exegesis/lib
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/tools/llvm-exegesis/lib/PowerPC
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ Target.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterAliasing.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterAliasing.cpp
new file mode 100644
index 00000000000..ee612fb0dd6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterAliasing.cpp
@@ -0,0 +1,92 @@
+//===-- RegisterAliasing.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterAliasing.h"
+
+namespace llvm {
+namespace exegesis {
+
+BitVector getAliasedBits(const MCRegisterInfo &RegInfo,
+ const BitVector &SourceBits) {
+ BitVector AliasedBits(RegInfo.getNumRegs());
+ for (const size_t PhysReg : SourceBits.set_bits()) {
+ using RegAliasItr = MCRegAliasIterator;
+ for (auto Itr = RegAliasItr(PhysReg, &RegInfo, true); Itr.isValid();
+ ++Itr) {
+ AliasedBits.set(*Itr);
+ }
+ }
+ return AliasedBits;
+}
+
+RegisterAliasingTracker::RegisterAliasingTracker(const MCRegisterInfo &RegInfo)
+ : SourceBits(RegInfo.getNumRegs()), AliasedBits(RegInfo.getNumRegs()),
+ Origins(RegInfo.getNumRegs()) {}
+
+RegisterAliasingTracker::RegisterAliasingTracker(
+ const MCRegisterInfo &RegInfo, const BitVector &ReservedReg,
+ const MCRegisterClass &RegClass)
+ : RegisterAliasingTracker(RegInfo) {
+ for (MCPhysReg PhysReg : RegClass)
+ if (!ReservedReg[PhysReg]) // Removing reserved registers.
+ SourceBits.set(PhysReg);
+ FillOriginAndAliasedBits(RegInfo, SourceBits);
+}
+
+RegisterAliasingTracker::RegisterAliasingTracker(const MCRegisterInfo &RegInfo,
+ const MCPhysReg PhysReg)
+ : RegisterAliasingTracker(RegInfo) {
+ SourceBits.set(PhysReg);
+ FillOriginAndAliasedBits(RegInfo, SourceBits);
+}
+
+void RegisterAliasingTracker::FillOriginAndAliasedBits(
+ const MCRegisterInfo &RegInfo, const BitVector &SourceBits) {
+ using RegAliasItr = MCRegAliasIterator;
+ for (const size_t PhysReg : SourceBits.set_bits()) {
+ for (auto Itr = RegAliasItr(PhysReg, &RegInfo, true); Itr.isValid();
+ ++Itr) {
+ AliasedBits.set(*Itr);
+ Origins[*Itr] = PhysReg;
+ }
+ }
+}
+
+RegisterAliasingTrackerCache::RegisterAliasingTrackerCache(
+ const MCRegisterInfo &RegInfo, const BitVector &ReservedReg)
+ : RegInfo(RegInfo), ReservedReg(ReservedReg),
+ EmptyRegisters(RegInfo.getNumRegs()) {}
+
+const RegisterAliasingTracker &
+RegisterAliasingTrackerCache::getRegister(MCPhysReg PhysReg) const {
+ auto &Found = Registers[PhysReg];
+ if (!Found)
+ Found.reset(new RegisterAliasingTracker(RegInfo, PhysReg));
+ return *Found;
+}
+
+const RegisterAliasingTracker &
+RegisterAliasingTrackerCache::getRegisterClass(unsigned RegClassIndex) const {
+ auto &Found = RegisterClasses[RegClassIndex];
+ const auto &RegClass = RegInfo.getRegClass(RegClassIndex);
+ if (!Found)
+ Found.reset(new RegisterAliasingTracker(RegInfo, ReservedReg, RegClass));
+ return *Found;
+}
+
+std::string debugString(const MCRegisterInfo &RegInfo, const BitVector &Regs) {
+ std::string Result;
+ for (const unsigned Reg : Regs.set_bits()) {
+ Result.append(RegInfo.getName(Reg));
+ Result.push_back(' ');
+ }
+ return Result;
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterAliasing.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterAliasing.h
new file mode 100644
index 00000000000..b2980854ba2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterAliasing.h
@@ -0,0 +1,119 @@
+//===-- RegisterAliasingTracker.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines classes to keep track of register aliasing.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_ALIASINGTRACKER_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_ALIASINGTRACKER_H
+
+#include <memory>
+#include <unordered_map>
+
+#include "llvm/ADT/BitVector.h"
+#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.
+BitVector getAliasedBits(const MCRegisterInfo &RegInfo,
+ const BitVector &SourceBits);
+
+// Keeps track of a mapping from one register (or a register class) to its
+// aliased registers.
+//
+// e.g.
+// RegisterAliasingTracker Tracker(RegInfo, X86::EAX);
+// Tracker.sourceBits() == { X86::EAX }
+// Tracker.aliasedBits() == { X86::AL, X86::AH, X86::AX,
+// X86::EAX,X86::HAX, X86::RAX }
+// Tracker.getOrigin(X86::AL) == X86::EAX;
+// Tracker.getOrigin(X86::BX) == -1;
+struct RegisterAliasingTracker {
+ // Construct a tracker from an MCRegisterClass.
+ RegisterAliasingTracker(const MCRegisterInfo &RegInfo,
+ const BitVector &ReservedReg,
+ const MCRegisterClass &RegClass);
+
+ // Construct a tracker from an MCPhysReg.
+ RegisterAliasingTracker(const MCRegisterInfo &RegInfo,
+ const MCPhysReg Register);
+
+ const BitVector &sourceBits() const { return SourceBits; }
+
+ // Retrieves all the touched registers as a BitVector.
+ const BitVector &aliasedBits() const { return AliasedBits; }
+
+ // Returns the origin of this register or -1.
+ int getOrigin(MCPhysReg Aliased) const {
+ if (!AliasedBits[Aliased])
+ return -1;
+ return Origins[Aliased];
+ }
+
+private:
+ RegisterAliasingTracker(const MCRegisterInfo &RegInfo);
+ RegisterAliasingTracker(const RegisterAliasingTracker &) = delete;
+
+ void FillOriginAndAliasedBits(const MCRegisterInfo &RegInfo,
+ const BitVector &OriginalBits);
+
+ BitVector SourceBits;
+ BitVector AliasedBits;
+ PackedVector<size_t, 10> Origins; // Max 1024 physical registers.
+};
+
+// A cache of existing trackers.
+struct RegisterAliasingTrackerCache {
+ // RegInfo must outlive the cache.
+ RegisterAliasingTrackerCache(const MCRegisterInfo &RegInfo,
+ const BitVector &ReservedReg);
+
+ // Convenient function to retrieve a BitVector of the right size.
+ const BitVector &emptyRegisters() const { return EmptyRegisters; }
+
+ // Convenient function to retrieve the registers the function body can't use.
+ const BitVector &reservedRegisters() const { return ReservedReg; }
+
+ // Convenient function to retrieve the underlying MCRegInfo.
+ const MCRegisterInfo &regInfo() const { return RegInfo; }
+
+ // Retrieves the RegisterAliasingTracker for this particular register.
+ const RegisterAliasingTracker &getRegister(MCPhysReg Reg) const;
+
+ // Retrieves the RegisterAliasingTracker for this particular register class.
+ const RegisterAliasingTracker &getRegisterClass(unsigned RegClassIndex) const;
+
+private:
+ const MCRegisterInfo &RegInfo;
+ const BitVector ReservedReg;
+ const BitVector EmptyRegisters;
+ mutable std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>>
+ Registers;
+ mutable std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>>
+ RegisterClasses;
+};
+
+// `a = a & ~b`, optimized for few bit sets in B and no allocation.
+inline void remove(BitVector &A, const BitVector &B) {
+ assert(A.size() == B.size());
+ for (auto I : B.set_bits())
+ A.reset(I);
+}
+
+// Returns a debug string for the list of registers.
+std::string debugString(const MCRegisterInfo &RegInfo, const BitVector &Regs);
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_ALIASINGTRACKER_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterValue.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterValue.cpp
new file mode 100644
index 00000000000..f881aa6d538
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterValue.cpp
@@ -0,0 +1,51 @@
+//===-- RegisterValue.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterValue.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace llvm {
+namespace exegesis {
+
+static APFloat getFloatValue(const fltSemantics &FltSemantics,
+ PredefinedValues Value) {
+ switch (Value) {
+ case PredefinedValues::POS_ZERO:
+ return APFloat::getZero(FltSemantics);
+ case PredefinedValues::NEG_ZERO:
+ return APFloat::getZero(FltSemantics, true);
+ case PredefinedValues::ONE:
+ return APFloat(FltSemantics, "1");
+ case PredefinedValues::TWO:
+ return APFloat(FltSemantics, "2");
+ case PredefinedValues::INF:
+ return APFloat::getInf(FltSemantics);
+ case PredefinedValues::QNAN:
+ return APFloat::getQNaN(FltSemantics);
+ case PredefinedValues::SMALLEST_NORM:
+ return APFloat::getSmallestNormalized(FltSemantics);
+ case PredefinedValues::LARGEST:
+ return APFloat::getLargest(FltSemantics);
+ case PredefinedValues::ULP:
+ return APFloat::getSmallest(FltSemantics);
+ case PredefinedValues::ONE_PLUS_ULP:
+ auto Output = getFloatValue(FltSemantics, PredefinedValues::ONE);
+ Output.next(false);
+ return Output;
+ }
+ llvm_unreachable("Unhandled exegesis::PredefinedValues");
+}
+
+APInt bitcastFloatValue(const fltSemantics &FltSemantics,
+ PredefinedValues Value) {
+ return getFloatValue(FltSemantics, Value).bitcastToAPInt();
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterValue.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterValue.h
new file mode 100644
index 00000000000..3429783a48a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/RegisterValue.h
@@ -0,0 +1,52 @@
+//===-- RegisterValue.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \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, APInt()}; }
+ unsigned Register;
+ 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
+};
+
+APInt bitcastFloatValue(const fltSemantics &FltSemantics,
+ PredefinedValues Value);
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_REGISTERVALUE_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/SchedClassResolution.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SchedClassResolution.cpp
new file mode 100644
index 00000000000..03386cf2384
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SchedClassResolution.cpp
@@ -0,0 +1,321 @@
+//===-- SchedClassResolution.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SchedClassResolution.h"
+#include "BenchmarkResult.h"
+#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 {
+
+// Return the non-redundant list of WriteProcRes used by the given sched class.
+// The scheduling model for LLVM is such that each instruction has a certain
+// number of uops which consume resources which are described by WriteProcRes
+// entries. Each entry describe how many cycles are spent on a specific ProcRes
+// kind.
+// For example, an instruction might have 3 uOps, one dispatching on P0
+// (ProcResIdx=1) and two on P06 (ProcResIdx = 7).
+// Note that LLVM additionally denormalizes resource consumption to include
+// usage of super resources by subresources. So in practice if there exists a
+// P016 (ProcResIdx=10), then the cycles consumed by P0 are also consumed by
+// P06 (ProcResIdx = 7) and P016 (ProcResIdx = 10), and the resources consumed
+// by P06 are also consumed by P016. In the figure below, parenthesized cycles
+// denote implied usage of superresources by subresources:
+// P0 P06 P016
+// uOp1 1 (1) (1)
+// uOp2 1 (1)
+// uOp3 1 (1)
+// =============================
+// 1 3 3
+// Eventually we end up with three entries for the WriteProcRes of the
+// instruction:
+// {ProcResIdx=1, Cycles=1} // P0
+// {ProcResIdx=7, Cycles=3} // P06
+// {ProcResIdx=10, Cycles=3} // P016
+//
+// Note that in this case, P016 does not contribute any cycles, so it would
+// be removed by this function.
+// FIXME: Move this to MCSubtargetInfo and use it in llvm-mca.
+static SmallVector<MCWriteProcResEntry, 8>
+getNonRedundantWriteProcRes(const MCSchedClassDesc &SCDesc,
+ const MCSubtargetInfo &STI) {
+ SmallVector<MCWriteProcResEntry, 8> Result;
+ const auto &SM = STI.getSchedModel();
+ const unsigned NumProcRes = SM.getNumProcResourceKinds();
+
+ // This assumes that the ProcResDescs are sorted in topological order, which
+ // is guaranteed by the tablegen backend.
+ SmallVector<float, 32> ProcResUnitUsage(NumProcRes);
+ for (const auto *WPR = STI.getWriteProcResBegin(&SCDesc),
+ *const WPREnd = STI.getWriteProcResEnd(&SCDesc);
+ WPR != WPREnd; ++WPR) {
+ const MCProcResourceDesc *const ProcResDesc =
+ SM.getProcResource(WPR->ProcResourceIdx);
+ if (ProcResDesc->SubUnitsIdxBegin == nullptr) {
+ // This is a ProcResUnit.
+ Result.push_back({WPR->ProcResourceIdx, WPR->Cycles});
+ ProcResUnitUsage[WPR->ProcResourceIdx] += WPR->Cycles;
+ } else {
+ // This is a ProcResGroup. First see if it contributes any cycles or if
+ // it has cycles just from subunits.
+ float RemainingCycles = WPR->Cycles;
+ for (const auto *SubResIdx = ProcResDesc->SubUnitsIdxBegin;
+ SubResIdx != ProcResDesc->SubUnitsIdxBegin + ProcResDesc->NumUnits;
+ ++SubResIdx) {
+ RemainingCycles -= ProcResUnitUsage[*SubResIdx];
+ }
+ if (RemainingCycles < 0.01f) {
+ // The ProcResGroup contributes no cycles of its own.
+ continue;
+ }
+ // The ProcResGroup contributes `RemainingCycles` cycles of its own.
+ Result.push_back({WPR->ProcResourceIdx,
+ static_cast<uint16_t>(std::round(RemainingCycles))});
+ // Spread the remaining cycles over all subunits.
+ for (const auto *SubResIdx = ProcResDesc->SubUnitsIdxBegin;
+ SubResIdx != ProcResDesc->SubUnitsIdxBegin + ProcResDesc->NumUnits;
+ ++SubResIdx) {
+ ProcResUnitUsage[*SubResIdx] += RemainingCycles / ProcResDesc->NumUnits;
+ }
+ }
+ }
+ return Result;
+}
+
+// Distributes a pressure budget as evenly as possible on the provided subunits
+// given the already existing port pressure distribution.
+//
+// The algorithm is as follows: while there is remaining pressure to
+// distribute, find the subunits with minimal pressure, and distribute
+// remaining pressure equally up to the pressure of the unit with
+// second-to-minimal pressure.
+// For example, let's assume we want to distribute 2*P1256
+// (Subunits = [P1,P2,P5,P6]), and the starting DensePressure is:
+// DensePressure = P0 P1 P2 P3 P4 P5 P6 P7
+// 0.1 0.3 0.2 0.0 0.0 0.5 0.5 0.5
+// RemainingPressure = 2.0
+// We sort the subunits by pressure:
+// Subunits = [(P2,p=0.2), (P1,p=0.3), (P5,p=0.5), (P6, p=0.5)]
+// We'll first start by the subunits with minimal pressure, which are at
+// the beginning of the sorted array. In this example there is one (P2).
+// The subunit with second-to-minimal pressure is the next one in the
+// array (P1). So we distribute 0.1 pressure to P2, and remove 0.1 cycles
+// from the budget.
+// Subunits = [(P2,p=0.3), (P1,p=0.3), (P5,p=0.5), (P5,p=0.5)]
+// RemainingPressure = 1.9
+// We repeat this process: distribute 0.2 pressure on each of the minimal
+// P2 and P1, decrease budget by 2*0.2:
+// Subunits = [(P2,p=0.5), (P1,p=0.5), (P5,p=0.5), (P5,p=0.5)]
+// RemainingPressure = 1.5
+// There are no second-to-minimal subunits so we just share the remaining
+// budget (1.5 cycles) equally:
+// Subunits = [(P2,p=0.875), (P1,p=0.875), (P5,p=0.875), (P5,p=0.875)]
+// RemainingPressure = 0.0
+// We stop as there is no remaining budget to distribute.
+static void distributePressure(float RemainingPressure,
+ SmallVector<uint16_t, 32> Subunits,
+ SmallVector<float, 32> &DensePressure) {
+ // Find the number of subunits with minimal pressure (they are at the
+ // front).
+ 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]];
+ };
+ size_t NumMinimalSU = 1;
+ while (NumMinimalSU < Subunits.size() &&
+ getPressureForSubunit(NumMinimalSU) == getPressureForSubunit(0)) {
+ ++NumMinimalSU;
+ }
+ while (RemainingPressure > 0.0f) {
+ if (NumMinimalSU == Subunits.size()) {
+ // All units are minimal, just distribute evenly and be done.
+ for (size_t I = 0; I < NumMinimalSU; ++I) {
+ getPressureForSubunit(I) += RemainingPressure / NumMinimalSU;
+ }
+ return;
+ }
+ // Distribute the remaining pressure equally.
+ const float MinimalPressure = getPressureForSubunit(NumMinimalSU - 1);
+ const float SecondToMinimalPressure = getPressureForSubunit(NumMinimalSU);
+ assert(MinimalPressure < SecondToMinimalPressure);
+ const float Increment = SecondToMinimalPressure - MinimalPressure;
+ if (RemainingPressure <= NumMinimalSU * Increment) {
+ // There is not enough remaining pressure.
+ for (size_t I = 0; I < NumMinimalSU; ++I) {
+ getPressureForSubunit(I) += RemainingPressure / NumMinimalSU;
+ }
+ return;
+ }
+ // Bump all minimal pressure subunits to `SecondToMinimalPressure`.
+ for (size_t I = 0; I < NumMinimalSU; ++I) {
+ getPressureForSubunit(I) = SecondToMinimalPressure;
+ RemainingPressure -= SecondToMinimalPressure;
+ }
+ while (NumMinimalSU < Subunits.size() &&
+ getPressureForSubunit(NumMinimalSU) == SecondToMinimalPressure) {
+ ++NumMinimalSU;
+ }
+ }
+}
+
+std::vector<std::pair<uint16_t, float>>
+computeIdealizedProcResPressure(const MCSchedModel &SM,
+ SmallVector<MCWriteProcResEntry, 8> WPRS) {
+ // DensePressure[I] is the port pressure for Proc Resource I.
+ SmallVector<float, 32> DensePressure(SM.getNumProcResourceKinds());
+ sort(WPRS, [](const MCWriteProcResEntry &A, const MCWriteProcResEntry &B) {
+ return A.ProcResourceIdx < B.ProcResourceIdx;
+ });
+ for (const MCWriteProcResEntry &WPR : WPRS) {
+ // Get units for the entry.
+ const MCProcResourceDesc *const ProcResDesc =
+ SM.getProcResource(WPR.ProcResourceIdx);
+ if (ProcResDesc->SubUnitsIdxBegin == nullptr) {
+ // This is a ProcResUnit.
+ DensePressure[WPR.ProcResourceIdx] += WPR.Cycles;
+ } else {
+ // This is a ProcResGroup.
+ SmallVector<uint16_t, 32> Subunits(ProcResDesc->SubUnitsIdxBegin,
+ ProcResDesc->SubUnitsIdxBegin +
+ ProcResDesc->NumUnits);
+ distributePressure(WPR.Cycles, Subunits, DensePressure);
+ }
+ }
+ // Turn dense pressure into sparse pressure by removing zero entries.
+ std::vector<std::pair<uint16_t, float>> Pressure;
+ for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ if (DensePressure[I] > 0.0f)
+ Pressure.emplace_back(I, DensePressure[I]);
+ }
+ return Pressure;
+}
+
+ResolvedSchedClass::ResolvedSchedClass(const MCSubtargetInfo &STI,
+ unsigned ResolvedSchedClassId,
+ bool WasVariant)
+ : SchedClassId(ResolvedSchedClassId),
+ SCDesc(STI.getSchedModel().getSchedClassDesc(ResolvedSchedClassId)),
+ WasVariant(WasVariant),
+ NonRedundantWriteProcRes(getNonRedundantWriteProcRes(*SCDesc, STI)),
+ IdealizedProcResPressure(computeIdealizedProcResPressure(
+ STI.getSchedModel(), NonRedundantWriteProcRes)) {
+ assert((SCDesc == nullptr || !SCDesc->isVariant()) &&
+ "ResolvedSchedClass should never be variant");
+}
+
+static unsigned ResolveVariantSchedClassId(const MCSubtargetInfo &STI,
+ const MCInstrInfo &InstrInfo,
+ unsigned SchedClassId,
+ const MCInst &MCI) {
+ const auto &SM = STI.getSchedModel();
+ while (SchedClassId && SM.getSchedClassDesc(SchedClassId)->isVariant()) {
+ SchedClassId = STI.resolveVariantSchedClass(SchedClassId, &MCI, &InstrInfo,
+ SM.getProcessorID());
+ }
+ return SchedClassId;
+}
+
+std::pair<unsigned /*SchedClassId*/, bool /*WasVariant*/>
+ResolvedSchedClass::resolveSchedClassId(const MCSubtargetInfo &SubtargetInfo,
+ const MCInstrInfo &InstrInfo,
+ const MCInst &MCI) {
+ unsigned SchedClassId = InstrInfo.get(MCI.getOpcode()).getSchedClass();
+ const bool WasVariant = SchedClassId && SubtargetInfo.getSchedModel()
+ .getSchedClassDesc(SchedClassId)
+ ->isVariant();
+ SchedClassId =
+ ResolveVariantSchedClassId(SubtargetInfo, InstrInfo, SchedClassId, MCI);
+ return std::make_pair(SchedClassId, WasVariant);
+}
+
+// Returns a ProxResIdx by id or name.
+static unsigned findProcResIdx(const MCSubtargetInfo &STI,
+ const StringRef NameOrId) {
+ // Interpret the key as an ProcResIdx.
+ unsigned ProcResIdx = 0;
+ if (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;
+}
+
+std::vector<BenchmarkMeasure> ResolvedSchedClass::getAsPoint(
+ InstructionBenchmark::ModeE Mode, const MCSubtargetInfo &STI,
+ ArrayRef<PerInstructionStats> Representative) const {
+ const size_t NumMeasurements = Representative.size();
+
+ std::vector<BenchmarkMeasure> SchedClassPoint(NumMeasurements);
+
+ if (Mode == InstructionBenchmark::Latency) {
+ assert(NumMeasurements == 1 && "Latency is a single measure.");
+ BenchmarkMeasure &LatencyMeasure = SchedClassPoint[0];
+
+ // Find the latency.
+ LatencyMeasure.PerInstructionValue = 0.0;
+
+ for (unsigned I = 0; I < SCDesc->NumWriteLatencyEntries; ++I) {
+ const MCWriteLatencyEntry *const WLE =
+ STI.getWriteLatencyEntry(SCDesc, I);
+ LatencyMeasure.PerInstructionValue =
+ std::max<double>(LatencyMeasure.PerInstructionValue, WLE->Cycles);
+ }
+ } else if (Mode == InstructionBenchmark::Uops) {
+ for (auto I : zip(SchedClassPoint, Representative)) {
+ BenchmarkMeasure &Measure = std::get<0>(I);
+ const PerInstructionStats &Stats = std::get<1>(I);
+
+ StringRef Key = Stats.key();
+ uint16_t ProcResIdx = findProcResIdx(STI, Key);
+ if (ProcResIdx > 0) {
+ // Find the pressure on ProcResIdx `Key`.
+ const auto ProcResPressureIt =
+ llvm::find_if(IdealizedProcResPressure,
+ [ProcResIdx](const std::pair<uint16_t, float> &WPR) {
+ return WPR.first == ProcResIdx;
+ });
+ Measure.PerInstructionValue =
+ ProcResPressureIt == IdealizedProcResPressure.end()
+ ? 0.0
+ : ProcResPressureIt->second;
+ } else if (Key == "NumMicroOps") {
+ Measure.PerInstructionValue = SCDesc->NumMicroOps;
+ } else {
+ errs() << "expected `key` to be either a ProcResIdx or a ProcRes "
+ "name, got "
+ << Key << "\n";
+ return {};
+ }
+ }
+ } else if (Mode == InstructionBenchmark::InverseThroughput) {
+ assert(NumMeasurements == 1 && "Inverse Throughput is a single measure.");
+ BenchmarkMeasure &RThroughputMeasure = SchedClassPoint[0];
+
+ RThroughputMeasure.PerInstructionValue =
+ MCSchedModel::getReciprocalThroughput(STI, *SCDesc);
+ } else {
+ llvm_unreachable("unimplemented measurement matching mode");
+ }
+
+ return SchedClassPoint;
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/SchedClassResolution.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SchedClassResolution.h
new file mode 100644
index 00000000000..3c7d8b3190b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SchedClassResolution.h
@@ -0,0 +1,61 @@
+//===-- SchedClassResolution.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Resolution of MCInst sched class into expanded form for further analysis.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_SCHEDCLASSRESOLUTION_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_SCHEDCLASSRESOLUTION_H
+
+#include "BenchmarkResult.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace exegesis {
+
+// Computes the idealized ProcRes Unit pressure. This is the expected
+// distribution if the CPU scheduler can distribute the load as evenly as
+// possible.
+std::vector<std::pair<uint16_t, float>>
+computeIdealizedProcResPressure(const MCSchedModel &SM,
+ SmallVector<MCWriteProcResEntry, 8> WPRS);
+
+// An MCSchedClassDesc augmented with some additional data.
+struct ResolvedSchedClass {
+ ResolvedSchedClass(const MCSubtargetInfo &STI, unsigned ResolvedSchedClassId,
+ bool WasVariant);
+
+ static std::pair<unsigned /*SchedClassId*/, bool /*WasVariant*/>
+ resolveSchedClassId(const MCSubtargetInfo &SubtargetInfo,
+ const MCInstrInfo &InstrInfo, const MCInst &MCI);
+
+ std::vector<BenchmarkMeasure>
+ getAsPoint(InstructionBenchmark::ModeE Mode, const MCSubtargetInfo &STI,
+ ArrayRef<PerInstructionStats> Representative) const;
+
+ const unsigned SchedClassId;
+ const MCSchedClassDesc *const SCDesc;
+ const bool WasVariant; // Whether the original class was variant.
+ const SmallVector<MCWriteProcResEntry, 8> NonRedundantWriteProcRes;
+ const std::vector<std::pair<uint16_t, float>> IdealizedProcResPressure;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_SCHEDCLASSRESOLUTION_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
new file mode 100644
index 00000000000..962136a1f87
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
@@ -0,0 +1,181 @@
+//===-- SerialSnippetGenerator.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SerialSnippetGenerator.h"
+
+#include "CodeTemplate.h"
+#include "MCInstrDescView.h"
+#include "Target.h"
+#include <algorithm>
+#include <numeric>
+#include <vector>
+
+namespace llvm {
+namespace exegesis {
+
+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"},
+};
+
+static constexpr size_t kMaxAliasingInstructions = 10;
+
+static std::vector<const Instruction *>
+computeAliasingInstructions(const LLVMState &State, const Instruction *Instr,
+ size_t MaxAliasingInstructions,
+ const BitVector &ForbiddenRegisters) {
+ // Randomly iterate the set of instructions.
+ std::vector<unsigned> Opcodes;
+ Opcodes.resize(State.getInstrInfo().getNumOpcodes());
+ std::iota(Opcodes.begin(), Opcodes.end(), 0U);
+ llvm::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator());
+
+ std::vector<const Instruction *> AliasingInstructions;
+ for (const unsigned OtherOpcode : Opcodes) {
+ if (OtherOpcode == Instr->Description.getOpcode())
+ continue;
+ const Instruction &OtherInstr = State.getIC().getInstr(OtherOpcode);
+ const MCInstrDesc &OtherInstrDesc = OtherInstr.Description;
+ // Ignore instructions that we cannot run.
+ if (OtherInstrDesc.isPseudo() || OtherInstrDesc.usesCustomInsertionHook() ||
+ OtherInstrDesc.isBranch() || OtherInstrDesc.isIndirectBranch() ||
+ OtherInstrDesc.isCall() || OtherInstrDesc.isReturn()) {
+ continue;
+ }
+ if (OtherInstr.hasMemoryOperands())
+ continue;
+ if (!State.getExegesisTarget().allowAsBackToBack(OtherInstr))
+ continue;
+ if (Instr->hasAliasingRegistersThrough(OtherInstr, ForbiddenRegisters))
+ AliasingInstructions.push_back(&OtherInstr);
+ if (AliasingInstructions.size() >= MaxAliasingInstructions)
+ break;
+ }
+ return AliasingInstructions;
+}
+
+static ExecutionMode getExecutionModes(const Instruction &Instr,
+ const BitVector &ForbiddenRegisters) {
+ 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(ForbiddenRegisters))
+ EM |= ExecutionMode::SERIAL_VIA_EXPLICIT_REGS;
+ if (Instr.hasOneUseOrOneDef())
+ EM |= ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR;
+ }
+ return EM;
+}
+
+static void appendCodeTemplates(const LLVMState &State,
+ InstructionTemplate Variant,
+ const BitVector &ForbiddenRegisters,
+ ExecutionMode ExecutionModeBit,
+ 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 = std::string(ExecutionClassDescription);
+ CT.Instructions.push_back(std::move(Variant));
+ 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(Variant.getInstr(),
+ Variant.getInstr());
+ assert(!SelfAliasing.empty() && !SelfAliasing.hasImplicitAliasing() &&
+ "Instr must alias itself explicitly");
+ // This is a self aliasing instruction so defs and uses are from the same
+ // instance, hence twice Variant in the following call.
+ setRandomAliasing(SelfAliasing, Variant, Variant);
+ CodeTemplate CT;
+ CT.Execution = ExecutionModeBit;
+ CT.Info = std::string(ExecutionClassDescription);
+ CT.Instructions.push_back(std::move(Variant));
+ CodeTemplates.push_back(std::move(CT));
+ return;
+ }
+ case ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR: {
+ const Instruction &Instr = Variant.getInstr();
+ // Select back-to-back non-memory instruction.
+ for (const auto *OtherInstr : computeAliasingInstructions(
+ State, &Instr, kMaxAliasingInstructions, ForbiddenRegisters)) {
+ const AliasingConfigurations Forward(Instr, *OtherInstr);
+ const AliasingConfigurations Back(*OtherInstr, Instr);
+ InstructionTemplate ThisIT(Variant);
+ InstructionTemplate OtherIT(OtherInstr);
+ if (!Forward.hasImplicitAliasing())
+ setRandomAliasing(Forward, ThisIT, OtherIT);
+ else if (!Back.hasImplicitAliasing())
+ setRandomAliasing(Back, OtherIT, ThisIT);
+ CodeTemplate CT;
+ CT.Execution = ExecutionModeBit;
+ CT.Info = std::string(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");
+ }
+}
+
+SerialSnippetGenerator::~SerialSnippetGenerator() = default;
+
+Expected<std::vector<CodeTemplate>>
+SerialSnippetGenerator::generateCodeTemplates(
+ InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
+ std::vector<CodeTemplate> Results;
+ const ExecutionMode EM =
+ getExecutionModes(Variant.getInstr(), ForbiddenRegisters);
+ for (const auto EC : kExecutionClasses) {
+ for (const auto ExecutionModeBit : getExecutionModeBits(EM & EC.Mask))
+ appendCodeTemplates(State, Variant, ForbiddenRegisters, ExecutionModeBit,
+ EC.Description, Results);
+ if (!Results.empty())
+ break;
+ }
+ if (Results.empty())
+ return make_error<Failure>(
+ "No strategy found to make the execution serial");
+ return std::move(Results);
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/SerialSnippetGenerator.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SerialSnippetGenerator.h
new file mode 100644
index 00000000000..42a1ed38b50
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SerialSnippetGenerator.h
@@ -0,0 +1,37 @@
+//===-- SerialSnippetGenerator.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// A SnippetGenerator implementation to create serial instruction snippets.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_SERIALSNIPPETGENERATOR_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_SERIALSNIPPETGENERATOR_H
+
+#include "Error.h"
+#include "MCInstrDescView.h"
+#include "SnippetGenerator.h"
+
+namespace llvm {
+namespace exegesis {
+
+class SerialSnippetGenerator : public SnippetGenerator {
+public:
+ using SnippetGenerator::SnippetGenerator;
+ ~SerialSnippetGenerator() override;
+
+ Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(InstructionTemplate Variant,
+ const BitVector &ForbiddenRegisters) const override;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_SERIALSNIPPETGENERATOR_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetFile.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetFile.cpp
new file mode 100644
index 00000000000..9c316d60c45
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetFile.cpp
@@ -0,0 +1,181 @@
+//===-- SnippetFile.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SnippetFile.h"
+#include "Error.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCInstPrinter.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/TargetRegistry.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/SourceMgr.h"
+#include <string>
+
+namespace llvm {
+namespace exegesis {
+namespace {
+
+// An MCStreamer that reads a BenchmarkCode definition from a file.
+class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer {
+public:
+ explicit BenchmarkCodeStreamer(MCContext *Context,
+ const MCRegisterInfo *TheRegInfo,
+ BenchmarkCode *Result)
+ : MCStreamer(*Context), RegInfo(TheRegInfo), Result(Result) {}
+
+ // Implementation of the MCStreamer interface. We only care about
+ // instructions.
+ void emitInstruction(const MCInst &Instruction,
+ const MCSubtargetInfo &STI) override {
+ Result->Key.Instructions.push_back(Instruction);
+ }
+
+ // Implementation of the AsmCommentConsumer.
+ void HandleComment(SMLoc Loc, 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;
+ SmallVector<StringRef, 2> Parts;
+ CommentText.split(Parts, ' ', /*unlimited splits*/ -1,
+ /*do not keep empty strings*/ false);
+ if (Parts.size() != 2) {
+ errs() << "invalid comment 'LLVM-EXEGESIS-DEFREG " << CommentText
+ << "', expected two parameters <REG> <HEX_VALUE>\n";
+ ++InvalidComments;
+ return;
+ }
+ if (!(RegVal.Register = findRegisterByName(Parts[0].trim()))) {
+ errs() << "unknown register '" << Parts[0]
+ << "' in 'LLVM-EXEGESIS-DEFREG " << CommentText << "'\n";
+ ++InvalidComments;
+ return;
+ }
+ const StringRef HexValue = Parts[1].trim();
+ RegVal.Value = APInt(
+ /* each hex digit is 4 bits */ HexValue.size() * 4, HexValue, 16);
+ Result->Key.RegisterInitialValues.push_back(std::move(RegVal));
+ return;
+ }
+ if (CommentText.consume_front("LIVEIN")) {
+ // LLVM-EXEGESIS-LIVEIN <reg>
+ const auto RegName = CommentText.ltrim();
+ if (unsigned Reg = findRegisterByName(RegName))
+ Result->LiveIns.push_back(Reg);
+ else {
+ errs() << "unknown register '" << RegName
+ << "' in 'LLVM-EXEGESIS-LIVEIN " << CommentText << "'\n";
+ ++InvalidComments;
+ }
+ return;
+ }
+ }
+
+ unsigned numInvalidComments() const { return InvalidComments; }
+
+private:
+ // We only care about instructions, we don't implement this part of the API.
+ void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
+ unsigned ByteAlignment) override {}
+ bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
+ return false;
+ }
+ void emitValueToAlignment(unsigned ByteAlignment, int64_t Value,
+ unsigned ValueSize,
+ unsigned MaxBytesToEmit) override {}
+ void emitZerofill(MCSection *Section, MCSymbol *Symbol, uint64_t Size,
+ unsigned ByteAlignment, SMLoc Loc) override {}
+
+ unsigned findRegisterByName(const 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;
+ }
+ errs() << "'" << RegName
+ << "' is not a valid register name for the target\n";
+ return 0;
+ }
+
+ const MCRegisterInfo *const RegInfo;
+ BenchmarkCode *const Result;
+ unsigned InvalidComments = 0;
+};
+
+} // namespace
+
+// Reads code snippets from file `Filename`.
+Expected<std::vector<BenchmarkCode>> readSnippets(const LLVMState &State,
+ StringRef Filename) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
+ MemoryBuffer::getFileOrSTDIN(Filename);
+ if (std::error_code EC = BufferPtr.getError()) {
+ return make_error<Failure>("cannot read snippet: " + Filename + ": " +
+ EC.message());
+ }
+ SourceMgr SM;
+ SM.AddNewSourceBuffer(std::move(BufferPtr.get()), SMLoc());
+
+ BenchmarkCode Result;
+
+ const TargetMachine &TM = State.getTargetMachine();
+ MCContext Context(TM.getTargetTriple(), TM.getMCAsmInfo(),
+ TM.getMCRegisterInfo(), TM.getMCSubtargetInfo());
+ std::unique_ptr<MCObjectFileInfo> ObjectFileInfo(
+ TM.getTarget().createMCObjectFileInfo(Context, /*PIC=*/false));
+ Context.setObjectFileInfo(ObjectFileInfo.get());
+ Context.initInlineSourceManager();
+ BenchmarkCodeStreamer Streamer(&Context, TM.getMCRegisterInfo(), &Result);
+
+ std::string Error;
+ raw_string_ostream ErrorStream(Error);
+ formatted_raw_ostream InstPrinterOStream(ErrorStream);
+ const std::unique_ptr<MCInstPrinter> InstPrinter(
+ TM.getTarget().createMCInstPrinter(
+ TM.getTargetTriple(), TM.getMCAsmInfo()->getAssemblerDialect(),
+ *TM.getMCAsmInfo(), *TM.getMCInstrInfo(), *TM.getMCRegisterInfo()));
+ // The following call will take care of calling Streamer.setTargetStreamer.
+ TM.getTarget().createAsmTargetStreamer(Streamer, InstPrinterOStream,
+ InstPrinter.get(),
+ TM.Options.MCOptions.AsmVerbose);
+ if (!Streamer.getTargetStreamer())
+ return make_error<Failure>("cannot create target asm streamer");
+
+ const std::unique_ptr<MCAsmParser> AsmParser(
+ createMCAsmParser(SM, Context, Streamer, *TM.getMCAsmInfo()));
+ if (!AsmParser)
+ return make_error<Failure>("cannot create asm parser");
+ AsmParser->getLexer().setCommentConsumer(&Streamer);
+
+ const std::unique_ptr<MCTargetAsmParser> TargetAsmParser(
+ TM.getTarget().createMCAsmParser(*TM.getMCSubtargetInfo(), *AsmParser,
+ *TM.getMCInstrInfo(),
+ MCTargetOptions()));
+
+ if (!TargetAsmParser)
+ return make_error<Failure>("cannot create target asm parser");
+ AsmParser->setTargetParser(*TargetAsmParser);
+
+ if (AsmParser->Run(false))
+ return make_error<Failure>("cannot parse asm file");
+ if (Streamer.numInvalidComments())
+ return make_error<Failure>(Twine("found ")
+ .concat(Twine(Streamer.numInvalidComments()))
+ .concat(" invalid LLVM-EXEGESIS comments"));
+ return std::vector<BenchmarkCode>{std::move(Result)};
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetFile.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetFile.h
new file mode 100644
index 00000000000..c346a047bf4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetFile.h
@@ -0,0 +1,35 @@
+//===-- SnippetFile.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Utilities to read a snippet file.
+/// Snippet files are just asm files with additional comments to specify which
+/// registers should be defined or are live on entry.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETFILE_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETFILE_H
+
+#include "BenchmarkCode.h"
+#include "LlvmState.h"
+#include "llvm/Support/Error.h"
+
+#include <vector>
+
+namespace llvm {
+namespace exegesis {
+
+// Reads code snippets from file `Filename`.
+Expected<std::vector<BenchmarkCode>> readSnippets(const LLVMState &State,
+ StringRef Filename);
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetGenerator.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetGenerator.cpp
new file mode 100644
index 00000000000..b3a7118115c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetGenerator.cpp
@@ -0,0 +1,275 @@
+//===-- SnippetGenerator.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <array>
+#include <string>
+
+#include "Assembler.h"
+#include "Error.h"
+#include "MCInstrDescView.h"
+#include "SnippetGenerator.h"
+#include "Target.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 Twine &S)
+ : StringError(S, inconvertibleErrorCode()) {}
+
+SnippetGenerator::SnippetGenerator(const LLVMState &State, const Options &Opts)
+ : State(State), Opts(Opts) {}
+
+SnippetGenerator::~SnippetGenerator() = default;
+
+Error SnippetGenerator::generateConfigurations(
+ const InstructionTemplate &Variant, std::vector<BenchmarkCode> &Benchmarks,
+ const BitVector &ExtraForbiddenRegs) const {
+ BitVector ForbiddenRegs = State.getRATC().reservedRegisters();
+ ForbiddenRegs |= ExtraForbiddenRegs;
+ // If the instruction has memory registers, prevent the generator from
+ // using the scratch register and its aliasing registers.
+ if (Variant.getInstr().hasMemoryOperands()) {
+ const auto &ET = State.getExegesisTarget();
+ unsigned ScratchSpacePointerInReg =
+ ET.getScratchMemoryRegister(State.getTargetMachine().getTargetTriple());
+ if (ScratchSpacePointerInReg == 0)
+ return make_error<Failure>(
+ "Infeasible : target does not support memory instructions");
+ const auto &ScratchRegAliases =
+ State.getRATC().getRegister(ScratchSpacePointerInReg).aliasedBits();
+ // If the instruction implicitly writes to ScratchSpacePointerInReg , abort.
+ // FIXME: We could make a copy of the scratch register.
+ for (const auto &Op : Variant.getInstr().Operands) {
+ if (Op.isDef() && Op.isImplicitReg() &&
+ ScratchRegAliases.test(Op.getImplicitReg()))
+ return make_error<Failure>(
+ "Infeasible : memory instruction uses scratch memory register");
+ }
+ ForbiddenRegs |= ScratchRegAliases;
+ }
+
+ if (auto E = generateCodeTemplates(Variant, ForbiddenRegs)) {
+ MutableArrayRef<CodeTemplate> Templates = E.get();
+
+ // Avoid reallocations in the loop.
+ Benchmarks.reserve(Benchmarks.size() + Templates.size());
+ for (CodeTemplate &CT : Templates) {
+ // TODO: Generate as many BenchmarkCode as needed.
+ {
+ BenchmarkCode BC;
+ BC.Info = CT.Info;
+ for (InstructionTemplate &IT : CT.Instructions) {
+ if (auto error = randomizeUnsetVariables(State, ForbiddenRegs, IT))
+ return error;
+ BC.Key.Instructions.push_back(IT.build());
+ }
+ if (CT.ScratchSpacePointerInReg)
+ BC.LiveIns.push_back(CT.ScratchSpacePointerInReg);
+ BC.Key.RegisterInitialValues =
+ computeRegisterInitialValues(CT.Instructions);
+ BC.Key.Config = CT.Config;
+ Benchmarks.emplace_back(std::move(BC));
+ if (Benchmarks.size() >= Opts.MaxConfigsPerOpcode) {
+ // We reached the number of allowed configs and return early.
+ return Error::success();
+ }
+ }
+ }
+ return Error::success();
+ } 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.
+ 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.getInstr().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.getInstr().Operands) {
+ if (Op.isDef()) {
+ const unsigned Reg = GetOpReg(Op);
+ if (Reg > 0)
+ DefinedRegs.set(Reg);
+ }
+ }
+ }
+ return RIV;
+}
+
+Expected<std::vector<CodeTemplate>>
+generateSelfAliasingCodeTemplates(InstructionTemplate Variant) {
+ const AliasingConfigurations SelfAliasing(Variant.getInstr(),
+ Variant.getInstr());
+ if (SelfAliasing.empty())
+ return make_error<SnippetGeneratorFailure>("empty self aliasing");
+ std::vector<CodeTemplate> Result;
+ Result.emplace_back();
+ CodeTemplate &CT = Result.back();
+ 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 Variant in the following call.
+ setRandomAliasing(SelfAliasing, Variant, Variant);
+ }
+ CT.Instructions.push_back(std::move(Variant));
+ return std::move(Result);
+}
+
+Expected<std::vector<CodeTemplate>>
+generateUnconstrainedCodeTemplates(const InstructionTemplate &Variant,
+ StringRef Msg) {
+ std::vector<CodeTemplate> Result;
+ Result.emplace_back();
+ CodeTemplate &CT = Result.back();
+ CT.Info =
+ std::string(formatv("{0}, repeating an unconstrained assignment", Msg));
+ CT.Instructions.push_back(std::move(Variant));
+ return std::move(Result);
+}
+
+std::mt19937 &randomGenerator() {
+ static std::random_device RandomDevice;
+ static std::mt19937 RandomGenerator(RandomDevice());
+ return RandomGenerator;
+}
+
+size_t randomIndex(size_t Max) {
+ std::uniform_int_distribution<> Distribution(0, Max);
+ return Distribution(randomGenerator());
+}
+
+template <typename C> static decltype(auto) randomElement(const C &Container) {
+ assert(!Container.empty() &&
+ "Can't pick a random element from an empty container)");
+ return Container[randomIndex(Container.size() - 1)];
+}
+
+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 = MCOperand::createReg(ROV.Reg);
+ } else {
+ assert(ROV.Op->isImplicitReg());
+ assert(ROV.Reg == ROV.Op->getImplicitReg());
+ }
+}
+
+size_t randomBit(const BitVector &Vector) {
+ assert(Vector.any());
+ auto Itr = Vector.set_bits_begin();
+ for (size_t I = randomIndex(Vector.count() - 1); 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);
+}
+
+static Error randomizeMCOperand(const LLVMState &State,
+ const Instruction &Instr, const Variable &Var,
+ MCOperand &AssignedValue,
+ const BitVector &ForbiddenRegs) {
+ const Operand &Op = Instr.getPrimaryOperand(Var);
+ if (Op.getExplicitOperandInfo().OperandType >=
+ MCOI::OperandType::OPERAND_FIRST_TARGET)
+ return State.getExegesisTarget().randomizeTargetMCOperand(
+ Instr, Var, AssignedValue, ForbiddenRegs);
+ switch (Op.getExplicitOperandInfo().OperandType) {
+ case MCOI::OperandType::OPERAND_IMMEDIATE:
+ // FIXME: explore immediate values too.
+ AssignedValue = MCOperand::createImm(1);
+ break;
+ case 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);
+ if (!AllowedRegs.any())
+ return make_error<Failure>(
+ Twine("no available registers:\ncandidates:\n")
+ .concat(debugString(State.getRegInfo(),
+ Op.getRegisterAliasing().sourceBits()))
+ .concat("\nforbidden:\n")
+ .concat(debugString(State.getRegInfo(), ForbiddenRegs)));
+ AssignedValue = MCOperand::createReg(randomBit(AllowedRegs));
+ break;
+ }
+ default:
+ break;
+ }
+ return Error::success();
+}
+
+Error randomizeUnsetVariables(const LLVMState &State,
+ const BitVector &ForbiddenRegs,
+ InstructionTemplate &IT) {
+ for (const Variable &Var : IT.getInstr().Variables) {
+ MCOperand &AssignedValue = IT.getValueFor(Var);
+ if (!AssignedValue.isValid())
+ if (auto Err = randomizeMCOperand(State, IT.getInstr(), Var,
+ AssignedValue, ForbiddenRegs))
+ return Err;
+ }
+ return Error::success();
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetGenerator.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetGenerator.h
new file mode 100644
index 00000000000..7a53c035470
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetGenerator.h
@@ -0,0 +1,109 @@
+//===-- SnippetGenerator.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \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/ADT/CombinationGenerator.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.
+Expected<std::vector<CodeTemplate>>
+generateSelfAliasingCodeTemplates(InstructionTemplate Variant);
+
+// Generates code templates without assignment constraints.
+Expected<std::vector<CodeTemplate>>
+generateUnconstrainedCodeTemplates(const InstructionTemplate &Variant,
+ StringRef Msg);
+
+// A class representing failures that happened during Benchmark, they are used
+// to report informations to the user.
+class SnippetGeneratorFailure : public StringError {
+public:
+ SnippetGeneratorFailure(const Twine &S);
+};
+
+// Common code for all benchmark modes.
+class SnippetGenerator {
+public:
+ struct Options {
+ unsigned MaxConfigsPerOpcode = 1;
+ };
+
+ explicit SnippetGenerator(const LLVMState &State, const Options &Opts);
+
+ virtual ~SnippetGenerator();
+
+ // Calls generateCodeTemplate and expands it into one or more BenchmarkCode.
+ Error generateConfigurations(const InstructionTemplate &Variant,
+ std::vector<BenchmarkCode> &Benchmarks,
+ const BitVector &ExtraForbiddenRegs) 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;
+ const Options Opts;
+
+private:
+ // API to be implemented by subclasses.
+ virtual Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(InstructionTemplate Variant,
+ const BitVector &ForbiddenRegisters) 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 unsigned integer from 0 to Max (inclusive).
+size_t randomIndex(size_t Max);
+
+// 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 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`.
+Error randomizeUnsetVariables(const LLVMState &State,
+ const BitVector &ForbiddenRegs,
+ InstructionTemplate &IT);
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetRepetitor.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetRepetitor.cpp
new file mode 100644
index 00000000000..1851cb46743
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetRepetitor.cpp
@@ -0,0 +1,133 @@
+//===-- SnippetRepetitor.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <array>
+#include <string>
+
+#include "SnippetRepetitor.h"
+#include "Target.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+
+namespace llvm {
+namespace exegesis {
+namespace {
+
+class DuplicateSnippetRepetitor : public SnippetRepetitor {
+public:
+ using SnippetRepetitor::SnippetRepetitor;
+
+ // Repeats the snippet until there are at least MinInstructions in the
+ // resulting code.
+ FillFunction Repeat(ArrayRef<MCInst> Instructions, unsigned MinInstructions,
+ unsigned LoopBodySize) const override {
+ return [Instructions, MinInstructions](FunctionFiller &Filler) {
+ auto Entry = Filler.getEntry();
+ if (!Instructions.empty()) {
+ // Add the whole snippet at least once.
+ Entry.addInstructions(Instructions);
+ for (unsigned I = Instructions.size(); I < MinInstructions; ++I) {
+ Entry.addInstruction(Instructions[I % Instructions.size()]);
+ }
+ }
+ Entry.addReturn();
+ };
+ }
+
+ BitVector getReservedRegs() const override {
+ // We're using no additional registers.
+ return State.getRATC().emptyRegisters();
+ }
+};
+
+class LoopSnippetRepetitor : public SnippetRepetitor {
+public:
+ explicit LoopSnippetRepetitor(const LLVMState &State)
+ : SnippetRepetitor(State),
+ LoopCounter(State.getExegesisTarget().getLoopCounterRegister(
+ State.getTargetMachine().getTargetTriple())) {}
+
+ // Loop over the snippet ceil(MinInstructions / Instructions.Size()) times.
+ FillFunction Repeat(ArrayRef<MCInst> Instructions, unsigned MinInstructions,
+ unsigned LoopBodySize) const override {
+ return [this, Instructions, MinInstructions,
+ LoopBodySize](FunctionFiller &Filler) {
+ const auto &ET = State.getExegesisTarget();
+ auto Entry = Filler.getEntry();
+ auto Loop = Filler.addBasicBlock();
+ auto Exit = Filler.addBasicBlock();
+
+ const unsigned LoopUnrollFactor =
+ LoopBodySize <= Instructions.size()
+ ? 1
+ : divideCeil(LoopBodySize, Instructions.size());
+ assert(LoopUnrollFactor >= 1 && "Should end up with at least 1 snippet.");
+
+ // Set loop counter to the right value:
+ const APInt LoopCount(
+ 32,
+ divideCeil(MinInstructions, LoopUnrollFactor * Instructions.size()));
+ assert(LoopCount.uge(1) && "Trip count should be at least 1.");
+ for (const MCInst &Inst :
+ ET.setRegTo(State.getSubtargetInfo(), LoopCounter, LoopCount))
+ Entry.addInstruction(Inst);
+
+ // Set up the loop basic block.
+ Entry.MBB->addSuccessor(Loop.MBB, BranchProbability::getOne());
+ Loop.MBB->addSuccessor(Loop.MBB, BranchProbability::getOne());
+ // The live ins are: the loop counter, the registers that were setup by
+ // the entry block, and entry block live ins.
+ Loop.MBB->addLiveIn(LoopCounter);
+ for (unsigned Reg : Filler.getRegistersSetUp())
+ Loop.MBB->addLiveIn(Reg);
+ for (const auto &LiveIn : Entry.MBB->liveins())
+ Loop.MBB->addLiveIn(LiveIn);
+ for (auto _ : seq(0U, LoopUnrollFactor)) {
+ (void)_;
+ Loop.addInstructions(Instructions);
+ }
+ ET.decrementLoopCounterAndJump(*Loop.MBB, *Loop.MBB,
+ State.getInstrInfo());
+
+ // Set up the exit basic block.
+ Loop.MBB->addSuccessor(Exit.MBB, BranchProbability::getZero());
+ Exit.addReturn();
+ };
+ }
+
+ BitVector getReservedRegs() const override {
+ // We're using a single loop counter, but we have to reserve all aliasing
+ // registers.
+ return State.getRATC().getRegister(LoopCounter).aliasedBits();
+ }
+
+private:
+ const unsigned LoopCounter;
+};
+
+} // namespace
+
+SnippetRepetitor::~SnippetRepetitor() {}
+
+std::unique_ptr<const SnippetRepetitor>
+SnippetRepetitor::Create(InstructionBenchmark::RepetitionModeE Mode,
+ const LLVMState &State) {
+ switch (Mode) {
+ case InstructionBenchmark::Duplicate:
+ return std::make_unique<DuplicateSnippetRepetitor>(State);
+ case InstructionBenchmark::Loop:
+ return std::make_unique<LoopSnippetRepetitor>(State);
+ case InstructionBenchmark::AggregateMin:
+ break;
+ }
+ llvm_unreachable("Unknown RepetitionModeE enum");
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetRepetitor.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetRepetitor.h
new file mode 100644
index 00000000000..239fa25408d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/SnippetRepetitor.h
@@ -0,0 +1,54 @@
+//===-- SnippetRepetitor.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines helpers to fill functions with repetitions of a snippet.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_FUNCTIONFILLER_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_FUNCTIONFILLER_H
+
+#include "Assembler.h"
+#include "BenchmarkResult.h"
+#include "LlvmState.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/Object/Binary.h"
+
+namespace llvm {
+namespace exegesis {
+
+class SnippetRepetitor {
+public:
+ static std::unique_ptr<const SnippetRepetitor>
+ Create(InstructionBenchmark::RepetitionModeE Mode, const LLVMState &State);
+
+ virtual ~SnippetRepetitor();
+
+ // Returns the set of registers that are reserved by the repetitor.
+ virtual BitVector getReservedRegs() const = 0;
+
+ // Returns a functor that repeats `Instructions` so that the function executes
+ // at least `MinInstructions` instructions.
+ virtual FillFunction Repeat(ArrayRef<MCInst> Instructions,
+ unsigned MinInstructions,
+ unsigned LoopBodySize) const = 0;
+
+ explicit SnippetRepetitor(const LLVMState &State) : State(State) {}
+
+protected:
+ const LLVMState &State;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/Target.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Target.cpp
new file mode 100644
index 00000000000..9ff19d57a87
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Target.cpp
@@ -0,0 +1,177 @@
+//===-- Target.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "Target.h"
+
+#include "LatencyBenchmarkRunner.h"
+#include "ParallelSnippetGenerator.h"
+#include "SerialSnippetGenerator.h"
+#include "UopsBenchmarkRunner.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace exegesis {
+
+ExegesisTarget::~ExegesisTarget() {} // anchor.
+
+static ExegesisTarget *FirstTarget = nullptr;
+
+const ExegesisTarget *ExegesisTarget::lookup(Triple TT) {
+ for (const ExegesisTarget *T = FirstTarget; T != nullptr; T = T->Next) {
+ if (T->matchesArch(TT.getArch()))
+ return T;
+ }
+ return nullptr;
+}
+
+Expected<std::unique_ptr<pfm::Counter>>
+ExegesisTarget::createCounter(StringRef CounterName, const LLVMState &) const {
+ pfm::PerfEvent Event(CounterName);
+ if (!Event.valid())
+ return llvm::make_error<Failure>(
+ llvm::Twine("Unable to create counter with name '")
+ .concat(CounterName)
+ .concat("'"));
+
+ return std::make_unique<pfm::Counter>(std::move(Event));
+}
+
+void ExegesisTarget::registerTarget(ExegesisTarget *Target) {
+ if (FirstTarget == nullptr) {
+ FirstTarget = Target;
+ return;
+ }
+ if (Target->Next != nullptr)
+ return; // Already registered.
+ Target->Next = FirstTarget;
+ FirstTarget = Target;
+}
+
+std::unique_ptr<SnippetGenerator> ExegesisTarget::createSnippetGenerator(
+ InstructionBenchmark::ModeE Mode, const LLVMState &State,
+ const SnippetGenerator::Options &Opts) const {
+ switch (Mode) {
+ case InstructionBenchmark::Unknown:
+ return nullptr;
+ case InstructionBenchmark::Latency:
+ return createSerialSnippetGenerator(State, Opts);
+ case InstructionBenchmark::Uops:
+ case InstructionBenchmark::InverseThroughput:
+ return createParallelSnippetGenerator(State, Opts);
+ }
+ return nullptr;
+}
+
+Expected<std::unique_ptr<BenchmarkRunner>>
+ExegesisTarget::createBenchmarkRunner(
+ InstructionBenchmark::ModeE Mode, const LLVMState &State,
+ InstructionBenchmark::ResultAggregationModeE ResultAggMode) const {
+ PfmCountersInfo PfmCounters = State.getPfmCounters();
+ switch (Mode) {
+ case InstructionBenchmark::Unknown:
+ return nullptr;
+ case InstructionBenchmark::Latency:
+ case InstructionBenchmark::InverseThroughput:
+ if (!PfmCounters.CycleCounter) {
+ const char *ModeName = Mode == InstructionBenchmark::Latency
+ ? "latency"
+ : "inverse_throughput";
+ return make_error<Failure>(
+ Twine("can't run '")
+ .concat(ModeName)
+ .concat("' mode, sched model does not define a cycle counter."));
+ }
+ return createLatencyBenchmarkRunner(State, Mode, ResultAggMode);
+ case InstructionBenchmark::Uops:
+ if (!PfmCounters.UopsCounter && !PfmCounters.IssueCounters)
+ return make_error<Failure>("can't run 'uops' mode, sched model does not "
+ "define uops or issue counters.");
+ return createUopsBenchmarkRunner(State, ResultAggMode);
+ }
+ return nullptr;
+}
+
+std::unique_ptr<SnippetGenerator> ExegesisTarget::createSerialSnippetGenerator(
+ const LLVMState &State, const SnippetGenerator::Options &Opts) const {
+ return std::make_unique<SerialSnippetGenerator>(State, Opts);
+}
+
+std::unique_ptr<SnippetGenerator> ExegesisTarget::createParallelSnippetGenerator(
+ const LLVMState &State, const SnippetGenerator::Options &Opts) const {
+ return std::make_unique<ParallelSnippetGenerator>(State, Opts);
+}
+
+std::unique_ptr<BenchmarkRunner> ExegesisTarget::createLatencyBenchmarkRunner(
+ const LLVMState &State, InstructionBenchmark::ModeE Mode,
+ InstructionBenchmark::ResultAggregationModeE ResultAggMode) const {
+ return std::make_unique<LatencyBenchmarkRunner>(State, Mode, ResultAggMode);
+}
+
+std::unique_ptr<BenchmarkRunner> ExegesisTarget::createUopsBenchmarkRunner(
+ const LLVMState &State,
+ InstructionBenchmark::ResultAggregationModeE /*unused*/) const {
+ return std::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(StringRef CpuName) const {
+ assert(llvm::is_sorted(
+ CpuPfmCounters,
+ [](const CpuAndPfmCounters &LHS, const CpuAndPfmCounters &RHS) {
+ return strcmp(LHS.CpuName, RHS.CpuName) < 0;
+ }) &&
+ "CpuPfmCounters table is not sorted");
+
+ // Find entry
+ auto Found = llvm::lower_bound(CpuPfmCounters, CpuName);
+ if (Found == CpuPfmCounters.end() || StringRef(Found->CpuName) != CpuName) {
+ // Use the default.
+ if (!CpuPfmCounters.empty() && 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;
+}
+
+ExegesisTarget::SavedState::~SavedState() {} // anchor.
+
+namespace {
+
+// Default implementation.
+class ExegesisDefaultTarget : public ExegesisTarget {
+public:
+ ExegesisDefaultTarget() : ExegesisTarget({}) {}
+
+private:
+ std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, unsigned Reg,
+ const APInt &Value) const override {
+ llvm_unreachable("Not yet implemented");
+ }
+
+ bool matchesArch(Triple::ArchType Arch) const override {
+ llvm_unreachable("never called");
+ return false;
+ }
+};
+
+} // namespace
+
+const ExegesisTarget &ExegesisTarget::getDefault() {
+ static ExegesisDefaultTarget Target;
+ return Target;
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/Target.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Target.h
new file mode 100644
index 00000000000..28c103aa194
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/Target.h
@@ -0,0 +1,208 @@
+//===-- Target.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+///
+/// Classes that handle the creation of target-specific objects. This is
+/// similar to Target/TargetRegistry.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_TARGET_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_TARGET_H
+
+#include "BenchmarkResult.h"
+#include "BenchmarkRunner.h"
+#include "Error.h"
+#include "LlvmState.h"
+#include "PerfHelper.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"
+#include "llvm/Support/Error.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<(StringRef S) const { return StringRef(CpuName) < S; }
+};
+
+class ExegesisTarget {
+public:
+ explicit ExegesisTarget(ArrayRef<CpuAndPfmCounters> CpuPfmCounters)
+ : CpuPfmCounters(CpuPfmCounters) {}
+
+ // Targets can use this to create target-specific perf counters.
+ virtual Expected<std::unique_ptr<pfm::Counter>>
+ createCounter(StringRef CounterName, const LLVMState &State) const;
+
+ // Targets can use this to add target-specific passes in assembleToStream();
+ virtual void addTargetSpecificPasses(PassManagerBase &PM) const {}
+
+ // Generates code to move a constant into a the given register.
+ // Precondition: Value must fit into Reg.
+ virtual std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, unsigned Reg,
+ const 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 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 a counter usable as a loop counter.
+ virtual unsigned getLoopCounterRegister(const Triple &) const { return 0; }
+
+ // Adds the code to decrement the loop counter and
+ virtual void decrementLoopCounterAndJump(MachineBasicBlock &MBB,
+ MachineBasicBlock &TargetMBB,
+ const MCInstrInfo &MII) const {
+ llvm_unreachable("decrementLoopCounterAndBranch() requires "
+ "getLoopCounterRegister() > 0");
+ }
+
+ // Returns a list of unavailable registers.
+ // Targets can use this to prevent some registers to be automatically selected
+ // for use in snippets.
+ virtual ArrayRef<unsigned> getUnavailableRegisters() const { return {}; }
+
+ // 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; }
+
+ // Assigns a random operand of the right type to variable Var.
+ // The target is responsible for handling any operand starting from
+ // OPERAND_FIRST_TARGET.
+ virtual Error randomizeTargetMCOperand(const Instruction &Instr,
+ const Variable &Var,
+ MCOperand &AssignedValue,
+ const BitVector &ForbiddenRegs) const {
+ return make_error<Failure>(
+ "targets with target-specific operands should implement this");
+ }
+
+ // Returns true if this instruction is supported as a back-to-back
+ // instructions.
+ // FIXME: Eventually we should discover this dynamically.
+ virtual bool allowAsBackToBack(const Instruction &Instr) const {
+ return true;
+ }
+
+ // For some instructions, it is interesting to measure how it's performance
+ // characteristics differ depending on it's operands.
+ // This allows us to produce all the interesting variants.
+ virtual std::vector<InstructionTemplate>
+ generateInstructionVariants(const Instruction &Instr,
+ unsigned MaxConfigsPerOpcode) const {
+ // By default, we're happy with whatever randomizer will give us.
+ return {&Instr};
+ }
+
+ // Checks hardware and software support for current benchmark mode.
+ // Returns an error if the target host does not have support to run the
+ // benchmark.
+ virtual Error checkFeatureSupport() const { return Error::success(); }
+
+ // Creates a snippet generator for the given mode.
+ std::unique_ptr<SnippetGenerator>
+ createSnippetGenerator(InstructionBenchmark::ModeE Mode,
+ const LLVMState &State,
+ const SnippetGenerator::Options &Opts) const;
+ // Creates a benchmark runner for the given mode.
+ Expected<std::unique_ptr<BenchmarkRunner>> createBenchmarkRunner(
+ InstructionBenchmark::ModeE Mode, const LLVMState &State,
+ InstructionBenchmark::ResultAggregationModeE ResultAggMode =
+ InstructionBenchmark::Min) const;
+
+ // Returns the ExegesisTarget for the given triple or nullptr if the target
+ // does not exist.
+ static const ExegesisTarget *lookup(Triple TT);
+ // Returns the default (unspecialized) ExegesisTarget.
+ static const ExegesisTarget &getDefault();
+ // Registers a target. Not thread safe.
+ static void registerTarget(ExegesisTarget *T);
+
+ 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(StringRef CpuName) const;
+
+ // Saves the CPU state that needs to be preserved when running a benchmark,
+ // and returns and RAII object that restores the state on destruction.
+ // By default no state is preserved.
+ struct SavedState {
+ virtual ~SavedState();
+ };
+ virtual std::unique_ptr<SavedState> withSavedState() const {
+ return std::make_unique<SavedState>();
+ }
+
+private:
+ virtual bool matchesArch(Triple::ArchType Arch) const = 0;
+
+ // Targets can implement their own snippet generators/benchmarks runners by
+ // implementing these.
+ std::unique_ptr<SnippetGenerator> virtual createSerialSnippetGenerator(
+ const LLVMState &State, const SnippetGenerator::Options &Opts) const;
+ std::unique_ptr<SnippetGenerator> virtual createParallelSnippetGenerator(
+ const LLVMState &State, const SnippetGenerator::Options &Opts) const;
+ std::unique_ptr<BenchmarkRunner> virtual createLatencyBenchmarkRunner(
+ const LLVMState &State, InstructionBenchmark::ModeE Mode,
+ InstructionBenchmark::ResultAggregationModeE ResultAggMode) const;
+ std::unique_ptr<BenchmarkRunner> virtual createUopsBenchmarkRunner(
+ const LLVMState &State,
+ InstructionBenchmark::ResultAggregationModeE ResultAggMode) const;
+
+ const ExegesisTarget *Next = nullptr;
+ const ArrayRef<CpuAndPfmCounters> CpuPfmCounters;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_TARGET_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/TargetSelect.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/TargetSelect.h
new file mode 100644
index 00000000000..003d12e6e7b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/TargetSelect.h
@@ -0,0 +1,40 @@
+//===-- TargetSelect.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+///
+/// Utilities to handle the creation of the native exegesis target.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_TARGET_SELECT_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_TARGET_SELECT_H
+
+namespace llvm {
+namespace exegesis {
+
+#ifdef LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET
+void LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET();
+#endif
+
+// Initializes the native exegesis target, or returns false if there is no
+// native target (either because llvm-exegesis does not support the target or
+// because it's not linked in).
+inline bool InitializeNativeExegesisTarget() {
+#ifdef LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET
+ LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET();
+ return true;
+#else
+ return false;
+#endif
+}
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_TARGET_SELECT_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/UopsBenchmarkRunner.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/UopsBenchmarkRunner.cpp
new file mode 100644
index 00000000000..b99b1c5e711
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/UopsBenchmarkRunner.cpp
@@ -0,0 +1,46 @@
+//===-- UopsBenchmarkRunner.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UopsBenchmarkRunner.h"
+
+#include "Target.h"
+
+namespace llvm {
+namespace exegesis {
+
+UopsBenchmarkRunner::~UopsBenchmarkRunner() = default;
+
+Expected<std::vector<BenchmarkMeasure>>
+UopsBenchmarkRunner::runMeasurements(const FunctionExecutor &Executor) const {
+ std::vector<BenchmarkMeasure> Result;
+ 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;
+ auto ExpectedCounterValue = Executor.runAndMeasure(IssueCounter->Counter);
+ if (!ExpectedCounterValue)
+ return ExpectedCounterValue.takeError();
+ Result.push_back(BenchmarkMeasure::Create(IssueCounter->ProcResName,
+ *ExpectedCounterValue));
+ }
+ // 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);
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/UopsBenchmarkRunner.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/UopsBenchmarkRunner.h
new file mode 100644
index 00000000000..cda74eb453d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/UopsBenchmarkRunner.h
@@ -0,0 +1,38 @@
+//===-- UopsBenchmarkRunner.h -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// A BenchmarkRunner implementation to measure uop decomposition.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_UOPSBENCHMARKRUNNER_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_UOPSBENCHMARKRUNNER_H
+
+#include "BenchmarkRunner.h"
+
+namespace llvm {
+namespace exegesis {
+
+class UopsBenchmarkRunner : public BenchmarkRunner {
+public:
+ UopsBenchmarkRunner(const LLVMState &State)
+ : BenchmarkRunner(State, InstructionBenchmark::Uops) {}
+ ~UopsBenchmarkRunner() override;
+
+ static constexpr const size_t kMinNumDifferentAddresses = 6;
+
+private:
+ Expected<std::vector<BenchmarkMeasure>>
+ runMeasurements(const FunctionExecutor &Executor) const override;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_UOPSBENCHMARKRUNNER_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/Target.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/Target.cpp
new file mode 100644
index 00000000000..7188d8fafe8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/Target.cpp
@@ -0,0 +1,970 @@
+//===-- Target.cpp ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "../Target.h"
+
+#include "../Error.h"
+#include "../ParallelSnippetGenerator.h"
+#include "../SerialSnippetGenerator.h"
+#include "../SnippetGenerator.h"
+#include "MCTargetDesc/X86BaseInfo.h"
+#include "MCTargetDesc/X86MCTargetDesc.h"
+#include "X86.h"
+#include "X86Counter.h"
+#include "X86RegisterInfo.h"
+#include "X86Subtarget.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/MC/MCInstBuilder.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Host.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+#include <float.h>
+#include <immintrin.h>
+#include <intrin.h>
+#endif
+
+namespace llvm {
+namespace exegesis {
+
+static cl::OptionCategory
+ BenchmarkOptions("llvm-exegesis benchmark x86-options");
+
+// If a positive value is specified, we are going to use the LBR in
+// latency-mode.
+//
+// Note:
+// - A small value is preferred, but too low a value could result in
+// throttling.
+// - A prime number is preferred to avoid always skipping certain blocks.
+//
+static cl::opt<unsigned> LbrSamplingPeriod(
+ "x86-lbr-sample-period",
+ cl::desc("The sample period (nbranches/sample), used for LBR sampling"),
+ cl::cat(BenchmarkOptions), cl::init(0));
+
+// FIXME: Validates that repetition-mode is loop if LBR is requested.
+
+// Returns a non-null reason if we cannot handle the memory references in this
+// instruction.
+static const char *isInvalidMemoryInstr(const Instruction &Instr) {
+ switch (Instr.Description.TSFlags & X86II::FormMask) {
+ default:
+ return "Unknown FormMask value";
+ // These have no memory access.
+ case X86II::Pseudo:
+ case X86II::RawFrm:
+ case X86II::AddCCFrm:
+ case X86II::PrefixByte:
+ case X86II::MRMDestReg:
+ case X86II::MRMSrcReg:
+ case X86II::MRMSrcReg4VOp3:
+ case X86II::MRMSrcRegOp4:
+ case X86II::MRMSrcRegCC:
+ case X86II::MRMXrCC:
+ case X86II::MRMr0:
+ 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::MRM0X:
+ case X86II::MRM1X:
+ case X86II::MRM2X:
+ case X86II::MRM3X:
+ case X86II::MRM4X:
+ case X86II::MRM5X:
+ case X86II::MRM6X:
+ case X86II::MRM7X:
+ 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 nullptr;
+ case X86II::AddRegFrm:
+ return (Instr.Description.Opcode == X86::POP16r ||
+ Instr.Description.Opcode == X86::POP32r ||
+ Instr.Description.Opcode == X86::PUSH16r ||
+ Instr.Description.Opcode == X86::PUSH32r)
+ ? "unsupported opcode: unsupported memory access"
+ : nullptr;
+ // These access memory and are handled.
+ case X86II::MRMDestMem:
+ case X86II::MRMSrcMem:
+ case X86II::MRMSrcMem4VOp3:
+ case X86II::MRMSrcMemOp4:
+ case X86II::MRMSrcMemCC:
+ case X86II::MRMXmCC:
+ 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 nullptr;
+ // These access memory and are not handled yet.
+ case X86II::RawFrmImm16:
+ case X86II::RawFrmMemOffs:
+ case X86II::RawFrmSrc:
+ case X86II::RawFrmDst:
+ case X86II::RawFrmDstSrc:
+ return "unsupported opcode: non uniform memory access";
+ }
+}
+
+// If the opcode is invalid, returns a pointer to a character literal indicating
+// the reason. nullptr indicates a valid opcode.
+static const char *isInvalidOpcode(const Instruction &Instr) {
+ const auto OpcodeName = Instr.Name;
+ if ((Instr.Description.TSFlags & X86II::FormMask) == X86II::Pseudo)
+ return "unsupported opcode: pseudo instruction";
+ if ((OpcodeName.startswith("POP") && !OpcodeName.startswith("POPCNT")) ||
+ OpcodeName.startswith("PUSH") || OpcodeName.startswith("ADJCALLSTACK") ||
+ OpcodeName.startswith("LEAVE"))
+ return "unsupported opcode: Push/Pop/AdjCallStack/Leave";
+ switch (Instr.Description.Opcode) {
+ case X86::LFS16rm:
+ case X86::LFS32rm:
+ case X86::LFS64rm:
+ case X86::LGS16rm:
+ case X86::LGS32rm:
+ case X86::LGS64rm:
+ case X86::LSS16rm:
+ case X86::LSS32rm:
+ case X86::LSS64rm:
+ case X86::SYSENTER:
+ return "unsupported opcode";
+ default:
+ break;
+ }
+ if (const auto reason = isInvalidMemoryInstr(Instr))
+ return reason;
+ // We do not handle instructions with OPERAND_PCREL.
+ for (const Operand &Op : Instr.Operands)
+ if (Op.isExplicit() &&
+ Op.getExplicitOperandInfo().OperandType == MCOI::OPERAND_PCREL)
+ return "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 == X86::RSTRegClassID)
+ return "unsupported second-form X87 instruction";
+ return nullptr;
+}
+
+static unsigned getX86FPFlags(const Instruction &Instr) {
+ return Instr.Description.TSFlags & X86II::FPTypeMask;
+}
+
+// Helper to fill a memory operand with a value.
+static void setMemOp(InstructionTemplate &IT, int OpIdx,
+ const MCOperand &OpVal) {
+ const auto Op = IT.getInstr().Operands[OpIdx];
+ assert(Op.isExplicit() && "invalid memory pattern");
+ IT.getValueFor(Op) = OpVal;
+}
+
+// Common (latency, uops) code for LEA templates. `GetDestReg` takes the
+// addressing base and index registers and returns the LEA destination register.
+static Expected<std::vector<CodeTemplate>> generateLEATemplatesCommon(
+ const Instruction &Instr, const BitVector &ForbiddenRegisters,
+ const LLVMState &State, const SnippetGenerator::Options &Opts,
+ std::function<void(unsigned, unsigned, BitVector &CandidateDestRegs)>
+ RestrictDestRegs) {
+ assert(Instr.Operands.size() == 6 && "invalid LEA");
+ assert(X86II::getMemoryOperandNo(Instr.Description.TSFlags) == 1 &&
+ "invalid LEA");
+
+ constexpr const int kDestOp = 0;
+ constexpr const int kBaseOp = 1;
+ constexpr const int kIndexOp = 3;
+ auto PossibleDestRegs =
+ Instr.Operands[kDestOp].getRegisterAliasing().sourceBits();
+ remove(PossibleDestRegs, ForbiddenRegisters);
+ auto PossibleBaseRegs =
+ Instr.Operands[kBaseOp].getRegisterAliasing().sourceBits();
+ remove(PossibleBaseRegs, ForbiddenRegisters);
+ auto PossibleIndexRegs =
+ Instr.Operands[kIndexOp].getRegisterAliasing().sourceBits();
+ remove(PossibleIndexRegs, ForbiddenRegisters);
+
+ const auto &RegInfo = State.getRegInfo();
+ std::vector<CodeTemplate> Result;
+ for (const unsigned BaseReg : PossibleBaseRegs.set_bits()) {
+ for (const unsigned IndexReg : PossibleIndexRegs.set_bits()) {
+ for (int LogScale = 0; LogScale <= 3; ++LogScale) {
+ // FIXME: Add an option for controlling how we explore immediates.
+ for (const int Disp : {0, 42}) {
+ InstructionTemplate IT(&Instr);
+ const int64_t Scale = 1ull << LogScale;
+ setMemOp(IT, 1, MCOperand::createReg(BaseReg));
+ setMemOp(IT, 2, MCOperand::createImm(Scale));
+ setMemOp(IT, 3, MCOperand::createReg(IndexReg));
+ setMemOp(IT, 4, MCOperand::createImm(Disp));
+ // SegmentReg must be 0 for LEA.
+ setMemOp(IT, 5, MCOperand::createReg(0));
+
+ // Output reg candidates are selected by the caller.
+ auto PossibleDestRegsNow = PossibleDestRegs;
+ RestrictDestRegs(BaseReg, IndexReg, PossibleDestRegsNow);
+ assert(PossibleDestRegsNow.set_bits().begin() !=
+ PossibleDestRegsNow.set_bits().end() &&
+ "no remaining registers");
+ setMemOp(
+ IT, 0,
+ MCOperand::createReg(*PossibleDestRegsNow.set_bits().begin()));
+
+ CodeTemplate CT;
+ CT.Instructions.push_back(std::move(IT));
+ CT.Config = formatv("{3}(%{0}, %{1}, {2})", RegInfo.getName(BaseReg),
+ RegInfo.getName(IndexReg), Scale, Disp)
+ .str();
+ Result.push_back(std::move(CT));
+ if (Result.size() >= Opts.MaxConfigsPerOpcode)
+ return std::move(Result);
+ }
+ }
+ }
+ }
+
+ return std::move(Result);
+}
+
+namespace {
+class X86SerialSnippetGenerator : public SerialSnippetGenerator {
+public:
+ using SerialSnippetGenerator::SerialSnippetGenerator;
+
+ Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(InstructionTemplate Variant,
+ const BitVector &ForbiddenRegisters) const override;
+};
+} // namespace
+
+Expected<std::vector<CodeTemplate>>
+X86SerialSnippetGenerator::generateCodeTemplates(
+ InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
+ const Instruction &Instr = Variant.getInstr();
+
+ if (const auto reason = isInvalidOpcode(Instr))
+ return make_error<Failure>(reason);
+
+ // LEA gets special attention.
+ const auto Opcode = Instr.Description.getOpcode();
+ if (Opcode == X86::LEA64r || Opcode == X86::LEA64_32r) {
+ return generateLEATemplatesCommon(
+ Instr, ForbiddenRegisters, State, Opts,
+ [this](unsigned BaseReg, unsigned IndexReg,
+ BitVector &CandidateDestRegs) {
+ // We just select a destination register that aliases the base
+ // register.
+ CandidateDestRegs &=
+ State.getRATC().getRegister(BaseReg).aliasedBits();
+ });
+ }
+
+ if (Instr.hasMemoryOperands())
+ return make_error<Failure>(
+ "unsupported memory operand in latency measurements");
+
+ switch (getX86FPFlags(Instr)) {
+ case X86II::NotFP:
+ return SerialSnippetGenerator::generateCodeTemplates(Variant,
+ ForbiddenRegisters);
+ case X86II::ZeroArgFP:
+ case X86II::OneArgFP:
+ case X86II::SpecialFP:
+ case X86II::CompareFP:
+ case X86II::CondMovFP:
+ return make_error<Failure>("Unsupported x87 Instruction");
+ case X86II::OneArgFPRW:
+ case 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(Variant);
+ default:
+ llvm_unreachable("Unknown FP Type!");
+ }
+}
+
+namespace {
+class X86ParallelSnippetGenerator : public ParallelSnippetGenerator {
+public:
+ using ParallelSnippetGenerator::ParallelSnippetGenerator;
+
+ Expected<std::vector<CodeTemplate>>
+ generateCodeTemplates(InstructionTemplate Variant,
+ const BitVector &ForbiddenRegisters) const override;
+};
+
+} // namespace
+
+Expected<std::vector<CodeTemplate>>
+X86ParallelSnippetGenerator::generateCodeTemplates(
+ InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const {
+ const Instruction &Instr = Variant.getInstr();
+
+ if (const auto reason = isInvalidOpcode(Instr))
+ return make_error<Failure>(reason);
+
+ // LEA gets special attention.
+ const auto Opcode = Instr.Description.getOpcode();
+ if (Opcode == X86::LEA64r || Opcode == X86::LEA64_32r) {
+ return generateLEATemplatesCommon(
+ Instr, ForbiddenRegisters, State, Opts,
+ [this](unsigned BaseReg, unsigned IndexReg,
+ BitVector &CandidateDestRegs) {
+ // Any destination register that is not used for addressing is fine.
+ remove(CandidateDestRegs,
+ State.getRATC().getRegister(BaseReg).aliasedBits());
+ remove(CandidateDestRegs,
+ State.getRATC().getRegister(IndexReg).aliasedBits());
+ });
+ }
+
+ switch (getX86FPFlags(Instr)) {
+ case X86II::NotFP:
+ return ParallelSnippetGenerator::generateCodeTemplates(Variant,
+ ForbiddenRegisters);
+ case X86II::ZeroArgFP:
+ case X86II::OneArgFP:
+ case X86II::SpecialFP:
+ return make_error<Failure>("Unsupported x87 Instruction");
+ case X86II::OneArgFPRW:
+ case 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(Variant);
+ case X86II::CompareFP:
+ case 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(
+ Variant, "instruction does not grow/shrink the FP stack");
+ default:
+ llvm_unreachable("Unknown FP Type!");
+ }
+}
+
+static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) {
+ switch (RegBitWidth) {
+ case 8:
+ return X86::MOV8ri;
+ case 16:
+ return X86::MOV16ri;
+ case 32:
+ return X86::MOV32ri;
+ case 64:
+ return X86::MOV64ri;
+ }
+ llvm_unreachable("Invalid Value Width");
+}
+
+// Generates instruction to load an immediate value into a register.
+static MCInst loadImmediate(unsigned Reg, unsigned RegBitWidth,
+ const APInt &Value) {
+ if (Value.getBitWidth() > RegBitWidth)
+ llvm_unreachable("Value must fit in the Register");
+ return MCInstBuilder(getLoadImmediateOpcode(RegBitWidth))
+ .addReg(Reg)
+ .addImm(Value.getZExtValue());
+}
+
+// Allocates scratch memory on the stack.
+static MCInst allocateStackSpace(unsigned Bytes) {
+ return MCInstBuilder(X86::SUB64ri8)
+ .addReg(X86::RSP)
+ .addReg(X86::RSP)
+ .addImm(Bytes);
+}
+
+// Fills scratch memory at offset `OffsetBytes` with value `Imm`.
+static MCInst fillStackSpace(unsigned MovOpcode, unsigned OffsetBytes,
+ uint64_t Imm) {
+ return MCInstBuilder(MovOpcode)
+ // Address = ESP
+ .addReg(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 MCInst loadToReg(unsigned Reg, unsigned RMOpcode) {
+ return MCInstBuilder(RMOpcode)
+ .addReg(Reg)
+ // Address = ESP
+ .addReg(X86::RSP) // BaseReg
+ .addImm(1) // ScaleAmt
+ .addReg(0) // IndexReg
+ .addImm(0) // Disp
+ .addReg(0); // Segment
+}
+
+// Releases scratch memory.
+static MCInst releaseStackSpace(unsigned Bytes) {
+ return MCInstBuilder(X86::ADD64ri8)
+ .addReg(X86::RSP)
+ .addReg(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 APInt &Constant) : Constant_(Constant) {}
+
+ std::vector<MCInst> loadAndFinalize(unsigned Reg, unsigned RegBitWidth,
+ unsigned Opcode);
+
+ std::vector<MCInst> loadX87STAndFinalize(unsigned Reg);
+
+ std::vector<MCInst> loadX87FPAndFinalize(unsigned Reg);
+
+ std::vector<MCInst> popFlagAndFinalize();
+
+ std::vector<MCInst> loadImplicitRegAndFinalize(unsigned Opcode,
+ unsigned Value);
+
+private:
+ ConstantInliner &add(const MCInst &Inst) {
+ Instructions.push_back(Inst);
+ return *this;
+ }
+
+ void initStack(unsigned Bytes);
+
+ static constexpr const unsigned kF80Bytes = 10; // 80 bits.
+
+ APInt Constant_;
+ std::vector<MCInst> Instructions;
+};
+} // namespace
+
+std::vector<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<MCInst> ConstantInliner::loadX87STAndFinalize(unsigned Reg) {
+ initStack(kF80Bytes);
+ add(MCInstBuilder(X86::LD_F80m)
+ // Address = ESP
+ .addReg(X86::RSP) // BaseReg
+ .addImm(1) // ScaleAmt
+ .addReg(0) // IndexReg
+ .addImm(0) // Disp
+ .addReg(0)); // Segment
+ if (Reg != X86::ST0)
+ add(MCInstBuilder(X86::ST_Frr).addReg(Reg));
+ add(releaseStackSpace(kF80Bytes));
+ return std::move(Instructions);
+}
+
+std::vector<MCInst> ConstantInliner::loadX87FPAndFinalize(unsigned Reg) {
+ initStack(kF80Bytes);
+ add(MCInstBuilder(X86::LD_Fp80m)
+ .addReg(Reg)
+ // Address = ESP
+ .addReg(X86::RSP) // BaseReg
+ .addImm(1) // ScaleAmt
+ .addReg(0) // IndexReg
+ .addImm(0) // Disp
+ .addReg(0)); // Segment
+ add(releaseStackSpace(kF80Bytes));
+ return std::move(Instructions);
+}
+
+std::vector<MCInst> ConstantInliner::popFlagAndFinalize() {
+ initStack(8);
+ add(MCInstBuilder(X86::POPF64));
+ return std::move(Instructions);
+}
+
+std::vector<MCInst>
+ConstantInliner::loadImplicitRegAndFinalize(unsigned Opcode, unsigned Value) {
+ add(allocateStackSpace(4));
+ add(fillStackSpace(X86::MOV32mi, 0, Value)); // Mask all FP exceptions
+ add(MCInstBuilder(Opcode)
+ // Address = ESP
+ .addReg(X86::RSP) // BaseReg
+ .addImm(1) // ScaleAmt
+ .addReg(0) // IndexReg
+ .addImm(0) // Disp
+ .addReg(0)); // Segment
+ add(releaseStackSpace(4));
+ return std::move(Instructions);
+}
+
+void ConstantInliner::initStack(unsigned Bytes) {
+ assert(Constant_.getBitWidth() <= Bytes * 8 &&
+ "Value does not have the correct size");
+ const 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(
+ X86::MOV32mi, ByteOffset,
+ WideConstant.extractBits(32, ByteOffset * 8).getZExtValue()));
+ if (Bytes - ByteOffset >= 2) {
+ add(fillStackSpace(
+ X86::MOV16mi, ByteOffset,
+ WideConstant.extractBits(16, ByteOffset * 8).getZExtValue()));
+ ByteOffset += 2;
+ }
+ if (Bytes - ByteOffset >= 1)
+ add(fillStackSpace(
+ X86::MOV8mi, ByteOffset,
+ WideConstant.extractBits(8, ByteOffset * 8).getZExtValue()));
+}
+
+#include "X86GenExegesis.inc"
+
+namespace {
+
+class X86SavedState : public ExegesisTarget::SavedState {
+public:
+ X86SavedState() {
+#ifdef __x86_64__
+# if defined(_MSC_VER)
+ _fxsave64(FPState);
+ Eflags = __readeflags();
+# elif defined(__GNUC__)
+ __builtin_ia32_fxsave64(FPState);
+ Eflags = __builtin_ia32_readeflags_u64();
+# endif
+#else
+ llvm_unreachable("X86 exegesis running on non-X86 target");
+#endif
+ }
+
+ ~X86SavedState() {
+ // Restoring the X87 state does not flush pending exceptions, make sure
+ // these exceptions are flushed now.
+#ifdef __x86_64__
+# if defined(_MSC_VER)
+ _clearfp();
+ _fxrstor64(FPState);
+ __writeeflags(Eflags);
+# elif defined(__GNUC__)
+ asm volatile("fwait");
+ __builtin_ia32_fxrstor64(FPState);
+ __builtin_ia32_writeeflags_u64(Eflags);
+# endif
+#else
+ llvm_unreachable("X86 exegesis running on non-X86 target");
+#endif
+ }
+
+private:
+#ifdef __x86_64__
+ alignas(16) char FPState[512];
+ uint64_t Eflags;
+#endif
+};
+
+class ExegesisX86Target : public ExegesisTarget {
+public:
+ ExegesisX86Target() : ExegesisTarget(X86CpuPfmCounters) {}
+
+ Expected<std::unique_ptr<pfm::Counter>>
+ createCounter(StringRef CounterName, const LLVMState &State) const override {
+ // If LbrSamplingPeriod was provided, then ignore the
+ // CounterName because we only have one for LBR.
+ if (LbrSamplingPeriod > 0) {
+ // Can't use LBR without HAVE_LIBPFM, LIBPFM_HAS_FIELD_CYCLES, or without
+ // __linux__ (for now)
+#if defined(HAVE_LIBPFM) && defined(LIBPFM_HAS_FIELD_CYCLES) && \
+ defined(__linux__)
+ return std::make_unique<X86LbrCounter>(
+ X86LbrPerfEvent(LbrSamplingPeriod));
+#else
+ return llvm::make_error<llvm::StringError>(
+ "LBR counter requested without HAVE_LIBPFM, LIBPFM_HAS_FIELD_CYCLES, "
+ "or running on Linux.",
+ llvm::errc::invalid_argument);
+#endif
+ }
+ return ExegesisTarget::createCounter(CounterName, State);
+ }
+
+private:
+ void addTargetSpecificPasses(PassManagerBase &PM) const override;
+
+ unsigned getScratchMemoryRegister(const Triple &TT) const override;
+
+ unsigned getLoopCounterRegister(const Triple &) const override;
+
+ unsigned getMaxMemoryAccessSize() const override { return 64; }
+
+ Error randomizeTargetMCOperand(const Instruction &Instr, const Variable &Var,
+ MCOperand &AssignedValue,
+ const BitVector &ForbiddenRegs) const override;
+
+ void fillMemoryOperands(InstructionTemplate &IT, unsigned Reg,
+ unsigned Offset) const override;
+
+ void decrementLoopCounterAndJump(MachineBasicBlock &MBB,
+ MachineBasicBlock &TargetMBB,
+ const MCInstrInfo &MII) const override;
+
+ std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, unsigned Reg,
+ const APInt &Value) const override;
+
+ ArrayRef<unsigned> getUnavailableRegisters() const override {
+ return makeArrayRef(kUnavailableRegisters,
+ sizeof(kUnavailableRegisters) /
+ sizeof(kUnavailableRegisters[0]));
+ }
+
+ bool allowAsBackToBack(const Instruction &Instr) const override {
+ const unsigned Opcode = Instr.Description.Opcode;
+ return !isInvalidOpcode(Instr) && Opcode != X86::LEA64r &&
+ Opcode != X86::LEA64_32r && Opcode != X86::LEA16r;
+ }
+
+ std::vector<InstructionTemplate>
+ generateInstructionVariants(const Instruction &Instr,
+ unsigned MaxConfigsPerOpcode) const override;
+
+ std::unique_ptr<SnippetGenerator> createSerialSnippetGenerator(
+ const LLVMState &State,
+ const SnippetGenerator::Options &Opts) const override {
+ return std::make_unique<X86SerialSnippetGenerator>(State, Opts);
+ }
+
+ std::unique_ptr<SnippetGenerator> createParallelSnippetGenerator(
+ const LLVMState &State,
+ const SnippetGenerator::Options &Opts) const override {
+ return std::make_unique<X86ParallelSnippetGenerator>(State, Opts);
+ }
+
+ bool matchesArch(Triple::ArchType Arch) const override {
+ return Arch == Triple::x86_64 || Arch == Triple::x86;
+ }
+
+ Error checkFeatureSupport() const override {
+ // LBR is the only feature we conditionally support now.
+ // So if LBR is not requested, then we should be able to run the benchmarks.
+ if (LbrSamplingPeriod == 0)
+ return Error::success();
+
+#if defined(__linux__) && defined(HAVE_LIBPFM) && \
+ defined(LIBPFM_HAS_FIELD_CYCLES)
+ // FIXME: Fix this.
+ // https://bugs.llvm.org/show_bug.cgi?id=48918
+ // For now, only do the check if we see an Intel machine because
+ // the counter uses some intel-specific magic and it could
+ // be confuse and think an AMD machine actually has LBR support.
+#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || \
+ defined(_M_X64)
+ using namespace sys::detail::x86;
+
+ if (getVendorSignature() == VendorSignatures::GENUINE_INTEL)
+ // If the kernel supports it, the hardware still may not have it.
+ return X86LbrCounter::checkLbrSupport();
+#else
+ llvm_unreachable("Running X86 exegesis on non-X86 target");
+#endif
+#endif
+ return llvm::make_error<llvm::StringError>(
+ "LBR not supported on this kernel and/or platform",
+ llvm::errc::not_supported);
+ }
+
+ std::unique_ptr<SavedState> withSavedState() const override {
+ return std::make_unique<X86SavedState>();
+ }
+
+ static const unsigned kUnavailableRegisters[4];
+};
+
+// We disable a few registers that cannot be encoded on instructions with a REX
+// prefix.
+const unsigned ExegesisX86Target::kUnavailableRegisters[4] = {X86::AH, X86::BH,
+ X86::CH, X86::DH};
+
+// We're using one of R8-R15 because these registers are never hardcoded in
+// instructions (e.g. MOVS writes to EDI, ESI, EDX), so they have less
+// conflicts.
+constexpr const unsigned kLoopCounterReg = X86::R8;
+
+} // namespace
+
+void ExegesisX86Target::addTargetSpecificPasses(PassManagerBase &PM) const {
+ // Lowers FP pseudo-instructions, e.g. ABS_Fp32 -> ABS_F.
+ PM.add(createX86FloatingPointStackifierPass());
+}
+
+unsigned ExegesisX86Target::getScratchMemoryRegister(const 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() ? X86::RCX : X86::RDI;
+}
+
+unsigned ExegesisX86Target::getLoopCounterRegister(const Triple &TT) const {
+ if (!TT.isArch64Bit()) {
+ return 0;
+ }
+ return kLoopCounterReg;
+}
+
+Error ExegesisX86Target::randomizeTargetMCOperand(
+ const Instruction &Instr, const Variable &Var, MCOperand &AssignedValue,
+ const BitVector &ForbiddenRegs) const {
+ const Operand &Op = Instr.getPrimaryOperand(Var);
+ switch (Op.getExplicitOperandInfo().OperandType) {
+ case X86::OperandType::OPERAND_ROUNDING_CONTROL:
+ AssignedValue =
+ MCOperand::createImm(randomIndex(X86::STATIC_ROUNDING::TO_ZERO));
+ return Error::success();
+ default:
+ break;
+ }
+ return make_error<Failure>(
+ Twine("unimplemented operand type ")
+ .concat(Twine(Op.getExplicitOperandInfo().OperandType)));
+}
+
+void ExegesisX86Target::fillMemoryOperands(InstructionTemplate &IT,
+ unsigned Reg,
+ unsigned Offset) const {
+ assert(!isInvalidMemoryInstr(IT.getInstr()) &&
+ "fillMemoryOperands requires a valid memory instruction");
+ int MemOpIdx = X86II::getMemoryOperandNo(IT.getInstr().Description.TSFlags);
+ assert(MemOpIdx >= 0 && "invalid memory operand index");
+ // getMemoryOperandNo() ignores tied operands, so we have to add them back.
+ MemOpIdx += X86II::getOperandBias(IT.getInstr().Description);
+ setMemOp(IT, MemOpIdx + 0, MCOperand::createReg(Reg)); // BaseReg
+ setMemOp(IT, MemOpIdx + 1, MCOperand::createImm(1)); // ScaleAmt
+ setMemOp(IT, MemOpIdx + 2, MCOperand::createReg(0)); // IndexReg
+ setMemOp(IT, MemOpIdx + 3, MCOperand::createImm(Offset)); // Disp
+ setMemOp(IT, MemOpIdx + 4, MCOperand::createReg(0)); // Segment
+}
+
+void ExegesisX86Target::decrementLoopCounterAndJump(
+ MachineBasicBlock &MBB, MachineBasicBlock &TargetMBB,
+ const MCInstrInfo &MII) const {
+ BuildMI(&MBB, DebugLoc(), MII.get(X86::ADD64ri8))
+ .addDef(kLoopCounterReg)
+ .addUse(kLoopCounterReg)
+ .addImm(-1);
+ BuildMI(&MBB, DebugLoc(), MII.get(X86::JCC_1))
+ .addMBB(&TargetMBB)
+ .addImm(X86::COND_NE);
+}
+
+std::vector<MCInst> ExegesisX86Target::setRegTo(const MCSubtargetInfo &STI,
+ unsigned Reg,
+ const APInt &Value) const {
+ if (X86::GR8RegClass.contains(Reg))
+ return {loadImmediate(Reg, 8, Value)};
+ if (X86::GR16RegClass.contains(Reg))
+ return {loadImmediate(Reg, 16, Value)};
+ if (X86::GR32RegClass.contains(Reg))
+ return {loadImmediate(Reg, 32, Value)};
+ if (X86::GR64RegClass.contains(Reg))
+ return {loadImmediate(Reg, 64, Value)};
+ ConstantInliner CI(Value);
+ if (X86::VR64RegClass.contains(Reg))
+ return CI.loadAndFinalize(Reg, 64, X86::MMX_MOVQ64rm);
+ if (X86::VR128XRegClass.contains(Reg)) {
+ if (STI.getFeatureBits()[X86::FeatureAVX512])
+ return CI.loadAndFinalize(Reg, 128, X86::VMOVDQU32Z128rm);
+ if (STI.getFeatureBits()[X86::FeatureAVX])
+ return CI.loadAndFinalize(Reg, 128, X86::VMOVDQUrm);
+ return CI.loadAndFinalize(Reg, 128, X86::MOVDQUrm);
+ }
+ if (X86::VR256XRegClass.contains(Reg)) {
+ if (STI.getFeatureBits()[X86::FeatureAVX512])
+ return CI.loadAndFinalize(Reg, 256, X86::VMOVDQU32Z256rm);
+ if (STI.getFeatureBits()[X86::FeatureAVX])
+ return CI.loadAndFinalize(Reg, 256, X86::VMOVDQUYrm);
+ }
+ if (X86::VR512RegClass.contains(Reg))
+ if (STI.getFeatureBits()[X86::FeatureAVX512])
+ return CI.loadAndFinalize(Reg, 512, X86::VMOVDQU32Zrm);
+ if (X86::RSTRegClass.contains(Reg)) {
+ return CI.loadX87STAndFinalize(Reg);
+ }
+ if (X86::RFP32RegClass.contains(Reg) || X86::RFP64RegClass.contains(Reg) ||
+ X86::RFP80RegClass.contains(Reg)) {
+ return CI.loadX87FPAndFinalize(Reg);
+ }
+ if (Reg == X86::EFLAGS)
+ return CI.popFlagAndFinalize();
+ if (Reg == X86::MXCSR)
+ return CI.loadImplicitRegAndFinalize(
+ STI.getFeatureBits()[X86::FeatureAVX] ? X86::VLDMXCSR : X86::LDMXCSR,
+ 0x1f80);
+ if (Reg == X86::FPCW)
+ return CI.loadImplicitRegAndFinalize(X86::FLDCW16m, 0x37f);
+ return {}; // Not yet implemented.
+}
+
+// Instruction can have some variable operands, and we may want to see how
+// different operands affect performance. So for each operand position,
+// precompute all the possible choices we might care about,
+// and greedily generate all the possible combinations of choices.
+std::vector<InstructionTemplate> ExegesisX86Target::generateInstructionVariants(
+ const Instruction &Instr, unsigned MaxConfigsPerOpcode) const {
+ bool Exploration = false;
+ SmallVector<SmallVector<MCOperand, 1>, 4> VariableChoices;
+ VariableChoices.resize(Instr.Variables.size());
+ for (auto I : llvm::zip(Instr.Variables, VariableChoices)) {
+ const Variable &Var = std::get<0>(I);
+ SmallVectorImpl<MCOperand> &Choices = std::get<1>(I);
+
+ switch (Instr.getPrimaryOperand(Var).getExplicitOperandInfo().OperandType) {
+ default:
+ // We don't wish to explicitly explore this variable.
+ Choices.emplace_back(); // But add invalid MCOperand to simplify logic.
+ continue;
+ case X86::OperandType::OPERAND_COND_CODE: {
+ Exploration = true;
+ auto CondCodes = enum_seq_inclusive(X86::CondCode::COND_O,
+ X86::CondCode::LAST_VALID_COND,
+ force_iteration_on_noniterable_enum);
+ Choices.reserve(CondCodes.size());
+ for (int CondCode : CondCodes)
+ Choices.emplace_back(MCOperand::createImm(CondCode));
+ break;
+ }
+ }
+ }
+
+ // If we don't wish to explore any variables, defer to the baseline method.
+ if (!Exploration)
+ return ExegesisTarget::generateInstructionVariants(Instr,
+ MaxConfigsPerOpcode);
+
+ std::vector<InstructionTemplate> Variants;
+ size_t NumVariants;
+ CombinationGenerator<MCOperand, decltype(VariableChoices)::value_type, 4> G(
+ VariableChoices);
+
+ // How many operand combinations can we produce, within the limit?
+ NumVariants = std::min(G.numCombinations(), (size_t)MaxConfigsPerOpcode);
+ // And actually produce all the wanted operand combinations.
+ Variants.reserve(NumVariants);
+ G.generate([&](ArrayRef<MCOperand> State) -> bool {
+ Variants.emplace_back(&Instr);
+ Variants.back().setVariableValues(State);
+ // Did we run out of space for variants?
+ return Variants.size() >= NumVariants;
+ });
+
+ assert(Variants.size() == NumVariants &&
+ Variants.size() <= MaxConfigsPerOpcode &&
+ "Should not produce too many variants");
+ return Variants;
+}
+
+static ExegesisTarget *getTheExegesisX86Target() {
+ static ExegesisX86Target Target;
+ return &Target;
+}
+
+void InitializeX86ExegesisTarget() {
+ ExegesisTarget::registerTarget(getTheExegesisX86Target());
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/X86Counter.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/X86Counter.cpp
new file mode 100644
index 00000000000..a91a2e8ac8c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/X86Counter.cpp
@@ -0,0 +1,261 @@
+//===-- X86Counter.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "X86Counter.h"
+
+#if defined(__linux__) && defined(HAVE_LIBPFM) && \
+ defined(LIBPFM_HAS_FIELD_CYCLES)
+
+// FIXME: Use appropriate wrappers for poll.h and mman.h
+// to support Windows and remove this linux-only guard.
+
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Errc.h"
+
+#error #include <perfmon/perf_event.h>
+#error #include <perfmon/pfmlib.h>
+#error #include <perfmon/pfmlib_perf_event.h>
+
+#include <atomic>
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <vector>
+
+#include <poll.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+namespace llvm {
+namespace exegesis {
+
+// Number of entries in the LBR.
+static constexpr int kLbrEntries = 16;
+static constexpr size_t kBufferPages = 8;
+static const size_t kDataBufferSize = kBufferPages * getpagesize();
+
+// First page is reserved for perf_event_mmap_page. Data buffer starts on
+// the next page, so we allocate one more page.
+static const size_t kMappedBufferSize = (kBufferPages + 1) * getpagesize();
+
+// Waits for the LBR perf events.
+static int pollLbrPerfEvent(const int FileDescriptor) {
+ struct pollfd PollFd;
+ PollFd.fd = FileDescriptor;
+ PollFd.events = POLLIN;
+ PollFd.revents = 0;
+ return poll(&PollFd, 1 /* num of fds */, 10000 /* timeout in ms */);
+}
+
+// Copies the data-buffer into Buf, given the pointer to MMapped.
+static void copyDataBuffer(void *MMappedBuffer, char *Buf, uint64_t Tail,
+ size_t DataSize) {
+ // First page is reserved for perf_event_mmap_page. Data buffer starts on
+ // the next page.
+ char *Start = reinterpret_cast<char *>(MMappedBuffer) + getpagesize();
+ // The LBR buffer is a cyclic buffer, we copy data to another buffer.
+ uint64_t Offset = Tail % kDataBufferSize;
+ size_t CopySize = kDataBufferSize - Offset;
+ memcpy(Buf, Start + Offset, CopySize);
+ if (CopySize >= DataSize)
+ return;
+
+ memcpy(Buf + CopySize, Start, Offset);
+ return;
+}
+
+// Parses the given data-buffer for stats and fill the CycleArray.
+// If data has been extracted successfully, also modifies the code to jump
+// out the benchmark loop.
+static llvm::Error parseDataBuffer(const char *DataBuf, size_t DataSize,
+ const void *From, const void *To,
+ llvm::SmallVector<int64_t, 4> *CycleArray) {
+ const char *DataPtr = DataBuf;
+ while (DataPtr < DataBuf + DataSize) {
+ struct perf_event_header Header;
+ memcpy(&Header, DataPtr, sizeof(struct perf_event_header));
+ if (Header.type != PERF_RECORD_SAMPLE) {
+ // Ignores non-sample records.
+ DataPtr += Header.size;
+ continue;
+ }
+ DataPtr += sizeof(Header);
+ uint64_t Count = llvm::support::endian::read64(DataPtr, support::native);
+ DataPtr += sizeof(Count);
+
+ struct perf_branch_entry Entry;
+ memcpy(&Entry, DataPtr, sizeof(struct perf_branch_entry));
+
+ // Read the perf_branch_entry array.
+ for (uint64_t i = 0; i < Count; ++i) {
+ const uint64_t BlockStart = From == nullptr
+ ? std::numeric_limits<uint64_t>::min()
+ : reinterpret_cast<uint64_t>(From);
+ const uint64_t BlockEnd = To == nullptr
+ ? std::numeric_limits<uint64_t>::max()
+ : reinterpret_cast<uint64_t>(To);
+
+ if (BlockStart <= Entry.from && BlockEnd >= Entry.to)
+ CycleArray->push_back(Entry.cycles);
+
+ if (i == Count - 1)
+ // We've reached the last entry.
+ return llvm::Error::success();
+
+ // Advance to next entry
+ DataPtr += sizeof(Entry);
+ memcpy(&Entry, DataPtr, sizeof(struct perf_branch_entry));
+ }
+ }
+ return llvm::make_error<llvm::StringError>("Unable to parse databuffer.",
+ llvm::errc::io_error);
+}
+
+X86LbrPerfEvent::X86LbrPerfEvent(unsigned SamplingPeriod) {
+ assert(SamplingPeriod > 0 && "SamplingPeriod must be positive");
+ EventString = "BR_INST_RETIRED.NEAR_TAKEN";
+ Attr = new perf_event_attr();
+ Attr->size = sizeof(*Attr);
+ Attr->type = PERF_TYPE_RAW;
+ // FIXME This is SKL's encoding. Not sure if it'll change.
+ Attr->config = 0x20c4; // BR_INST_RETIRED.NEAR_TAKEN
+ Attr->sample_type = PERF_SAMPLE_BRANCH_STACK;
+ // Don't need to specify "USER" because we've already excluded HV and Kernel.
+ Attr->branch_sample_type = PERF_SAMPLE_BRANCH_ANY;
+ Attr->sample_period = SamplingPeriod;
+ Attr->wakeup_events = 1; // We need this even when using ioctl REFRESH.
+ Attr->disabled = 1;
+ Attr->exclude_kernel = 1;
+ Attr->exclude_hv = 1;
+ Attr->read_format = PERF_FORMAT_GROUP;
+
+ FullQualifiedEventString = EventString;
+}
+
+X86LbrCounter::X86LbrCounter(pfm::PerfEvent &&NewEvent)
+ : Counter(std::move(NewEvent)) {
+ MMappedBuffer = mmap(nullptr, kMappedBufferSize, PROT_READ | PROT_WRITE,
+ MAP_SHARED, FileDescriptor, 0);
+ if (MMappedBuffer == MAP_FAILED)
+ llvm::errs() << "Failed to mmap buffer.";
+}
+
+X86LbrCounter::~X86LbrCounter() {
+ if (0 != munmap(MMappedBuffer, kMappedBufferSize))
+ llvm::errs() << "Failed to munmap buffer.";
+}
+
+void X86LbrCounter::start() {
+ ioctl(FileDescriptor, PERF_EVENT_IOC_REFRESH, 1024 /* kMaxPollsPerFd */);
+}
+
+llvm::Error X86LbrCounter::checkLbrSupport() {
+ // Do a sample read and check if the results contain non-zero values.
+
+ X86LbrCounter counter(X86LbrPerfEvent(123));
+ counter.start();
+
+ // Prevent the compiler from unrolling the loop and get rid of all the
+ // branches. We need at least 16 iterations.
+ int Sum = 0;
+ int V = 1;
+
+ volatile int *P = &V;
+ auto TimeLimit =
+ std::chrono::high_resolution_clock::now() + std::chrono::microseconds(5);
+
+ for (int I = 0;
+ I < kLbrEntries || std::chrono::high_resolution_clock::now() < TimeLimit;
+ ++I) {
+ Sum += *P;
+ }
+
+ counter.stop();
+ (void)Sum;
+
+ auto ResultOrError = counter.doReadCounter(nullptr, nullptr);
+ if (ResultOrError)
+ if (!ResultOrError.get().empty())
+ // If there is at least one non-zero entry, then LBR is supported.
+ for (const int64_t &Value : ResultOrError.get())
+ if (Value != 0)
+ return Error::success();
+
+ return llvm::make_error<llvm::StringError>(
+ "LBR format with cycles is not suppported on the host.",
+ llvm::errc::not_supported);
+}
+
+llvm::Expected<llvm::SmallVector<int64_t, 4>>
+X86LbrCounter::readOrError(StringRef FunctionBytes) const {
+ // Disable the event before reading
+ ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0);
+
+ // Find the boundary of the function so that we could filter the LBRs
+ // to keep only the relevant records.
+ if (FunctionBytes.empty())
+ return llvm::make_error<llvm::StringError>("Empty function bytes",
+ llvm::errc::invalid_argument);
+ const void *From = reinterpret_cast<const void *>(FunctionBytes.data());
+ const void *To = reinterpret_cast<const void *>(FunctionBytes.data() +
+ FunctionBytes.size());
+ return doReadCounter(From, To);
+}
+
+llvm::Expected<llvm::SmallVector<int64_t, 4>>
+X86LbrCounter::doReadCounter(const void *From, const void *To) const {
+ // The max number of time-outs/retries before we give up.
+ static constexpr int kMaxTimeouts = 160;
+
+ // Parses the LBR buffer and fills CycleArray with the sequence of cycle
+ // counts from the buffer.
+ llvm::SmallVector<int64_t, 4> CycleArray;
+ auto DataBuf = std::make_unique<char[]>(kDataBufferSize);
+ int NumTimeouts = 0;
+ int PollResult = 0;
+
+ while (PollResult <= 0) {
+ PollResult = pollLbrPerfEvent(FileDescriptor);
+ if (PollResult > 0)
+ break;
+ if (PollResult == -1)
+ return llvm::make_error<llvm::StringError>("Cannot poll LBR perf event.",
+ llvm::errc::io_error);
+ if (NumTimeouts++ >= kMaxTimeouts)
+ return llvm::make_error<llvm::StringError>(
+ "LBR polling still timed out after max number of attempts.",
+ llvm::errc::device_or_resource_busy);
+ }
+
+ struct perf_event_mmap_page Page;
+ memcpy(&Page, MMappedBuffer, sizeof(struct perf_event_mmap_page));
+
+ const uint64_t DataTail = Page.data_tail;
+ const uint64_t DataHead = Page.data_head;
+ // We're supposed to use a barrier after reading data_head.
+ std::atomic_thread_fence(std::memory_order_acq_rel);
+ const size_t DataSize = DataHead - DataTail;
+ if (DataSize > kDataBufferSize)
+ return llvm::make_error<llvm::StringError>(
+ "DataSize larger than buffer size.", llvm::errc::invalid_argument);
+
+ copyDataBuffer(MMappedBuffer, DataBuf.get(), DataTail, DataSize);
+ llvm::Error error =
+ parseDataBuffer(DataBuf.get(), DataSize, From, To, &CycleArray);
+ if (!error)
+ return CycleArray;
+ return std::move(error);
+}
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // defined(__linux__) && defined(HAVE_LIBPFM) &&
+ // defined(LIBPFM_HAS_FIELD_CYCLES)
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/X86Counter.h b/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/X86Counter.h
new file mode 100644
index 00000000000..73e4dc5b990
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/X86Counter.h
@@ -0,0 +1,60 @@
+//===-- X86Counter.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Perf counter that reads the LBRs for measuring the benchmarked block's
+/// throughput.
+///
+/// More info at: https://lwn.net/Articles/680985
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_LIB_X86_X86COUNTER_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_LIB_X86_X86COUNTER_H
+
+#include "../PerfHelper.h"
+#include "llvm/Support/Error.h"
+
+// FIXME: Use appropriate wrappers for poll.h and mman.h
+// to support Windows and remove this linux-only guard.
+#if defined(__linux__) && defined(HAVE_LIBPFM) && \
+ defined(LIBPFM_HAS_FIELD_CYCLES)
+
+namespace llvm {
+namespace exegesis {
+
+class X86LbrPerfEvent : public pfm::PerfEvent {
+public:
+ X86LbrPerfEvent(unsigned SamplingPeriod);
+};
+
+class X86LbrCounter : public pfm::Counter {
+public:
+ static llvm::Error checkLbrSupport();
+
+ explicit X86LbrCounter(pfm::PerfEvent &&Event);
+
+ virtual ~X86LbrCounter();
+
+ void start() override;
+
+ llvm::Expected<llvm::SmallVector<int64_t, 4>>
+ readOrError(StringRef FunctionBytes) const override;
+
+private:
+ llvm::Expected<llvm::SmallVector<int64_t, 4>>
+ doReadCounter(const void *From, const void *To) const;
+
+ void *MMappedBuffer = nullptr;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif // defined(__linux__) && defined(HAVE_LIBPFM) &&
+ // defined(LIBPFM_HAS_FIELD_CYCLES)
+
+#endif // LLVM_TOOLS_LLVM_EXEGESIS_LIB_X86_X86COUNTER_H
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/ya.make b/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/ya.make
new file mode 100644
index 00000000000..de6d676424d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/X86/ya.make
@@ -0,0 +1,38 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/tools/llvm-exegesis/lib
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/tools/llvm-exegesis/lib/X86
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ Target.cpp
+ X86Counter.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/lib/ya.make b/contrib/libs/llvm14/tools/llvm-exegesis/lib/ya.make
new file mode 100644
index 00000000000..18733ffb032
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/lib/ya.make
@@ -0,0 +1,59 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/ExecutionEngine
+ contrib/libs/llvm14/lib/ExecutionEngine/MCJIT
+ contrib/libs/llvm14/lib/ExecutionEngine/RuntimeDyld
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ObjectYAML
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-exegesis/lib
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ Analysis.cpp
+ Assembler.cpp
+ BenchmarkResult.cpp
+ BenchmarkRunner.cpp
+ Clustering.cpp
+ CodeTemplate.cpp
+ Error.cpp
+ LatencyBenchmarkRunner.cpp
+ LlvmState.cpp
+ MCInstrDescView.cpp
+ ParallelSnippetGenerator.cpp
+ PerfHelper.cpp
+ RegisterAliasing.cpp
+ RegisterValue.cpp
+ SchedClassResolution.cpp
+ SerialSnippetGenerator.cpp
+ SnippetFile.cpp
+ SnippetGenerator.cpp
+ SnippetRepetitor.cpp
+ Target.cpp
+ UopsBenchmarkRunner.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/llvm-exegesis.cpp b/contrib/libs/llvm14/tools/llvm-exegesis/llvm-exegesis.cpp
new file mode 100644
index 00000000000..aecae5c949e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/llvm-exegesis.cpp
@@ -0,0 +1,478 @@
+//===-- llvm-exegesis.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Measures execution properties (latencies/uops) of an instruction.
+///
+//===----------------------------------------------------------------------===//
+
+#include "lib/Analysis.h"
+#include "lib/BenchmarkResult.h"
+#include "lib/BenchmarkRunner.h"
+#include "lib/Clustering.h"
+#include "lib/Error.h"
+#include "lib/LlvmState.h"
+#include "lib/PerfHelper.h"
+#include "lib/SnippetFile.h"
+#include "lib/SnippetRepetitor.h"
+#include "lib/Target.h"
+#include "lib/TargetSelect.h"
+#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/MCSubtargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
+#include <algorithm>
+#include <string>
+
+namespace llvm {
+namespace exegesis {
+
+static cl::OptionCategory Options("llvm-exegesis options");
+static cl::OptionCategory BenchmarkOptions("llvm-exegesis benchmark options");
+static cl::OptionCategory AnalysisOptions("llvm-exegesis analysis options");
+
+static cl::opt<int> OpcodeIndex(
+ "opcode-index",
+ cl::desc("opcode to measure, by index, or -1 to measure all opcodes"),
+ cl::cat(BenchmarkOptions), cl::init(0));
+
+static cl::opt<std::string>
+ OpcodeNames("opcode-name",
+ cl::desc("comma-separated list of opcodes to measure, by name"),
+ cl::cat(BenchmarkOptions), cl::init(""));
+
+static cl::opt<std::string> SnippetsFile("snippets-file",
+ cl::desc("code snippets to measure"),
+ cl::cat(BenchmarkOptions),
+ cl::init(""));
+
+static cl::opt<std::string>
+ BenchmarkFile("benchmarks-file",
+ cl::desc("File to read (analysis mode) or write "
+ "(latency/uops/inverse_throughput modes) benchmark "
+ "results. “-” uses stdin/stdout."),
+ cl::cat(Options), cl::init(""));
+
+static cl::opt<exegesis::InstructionBenchmark::ModeE> BenchmarkMode(
+ "mode", cl::desc("the mode to run"), cl::cat(Options),
+ cl::values(clEnumValN(exegesis::InstructionBenchmark::Latency, "latency",
+ "Instruction Latency"),
+ clEnumValN(exegesis::InstructionBenchmark::InverseThroughput,
+ "inverse_throughput",
+ "Instruction Inverse Throughput"),
+ 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<exegesis::InstructionBenchmark::ResultAggregationModeE>
+ ResultAggMode(
+ "result-aggregation-mode",
+ cl::desc("How to aggregate multi-values result"), cl::cat(Options),
+ cl::values(clEnumValN(exegesis::InstructionBenchmark::Min, "min",
+ "Keep min reading"),
+ clEnumValN(exegesis::InstructionBenchmark::Max, "max",
+ "Keep max reading"),
+ clEnumValN(exegesis::InstructionBenchmark::Mean, "mean",
+ "Compute mean of all readings"),
+ clEnumValN(exegesis::InstructionBenchmark::MinVariance,
+ "min-variance",
+ "Keep readings set with min-variance")),
+ cl::init(exegesis::InstructionBenchmark::Min));
+
+static cl::opt<exegesis::InstructionBenchmark::RepetitionModeE> RepetitionMode(
+ "repetition-mode", cl::desc("how to repeat the instruction snippet"),
+ cl::cat(BenchmarkOptions),
+ cl::values(
+ clEnumValN(exegesis::InstructionBenchmark::Duplicate, "duplicate",
+ "Duplicate the snippet"),
+ clEnumValN(exegesis::InstructionBenchmark::Loop, "loop",
+ "Loop over the snippet"),
+ clEnumValN(exegesis::InstructionBenchmark::AggregateMin, "min",
+ "All of the above and take the minimum of measurements")),
+ cl::init(exegesis::InstructionBenchmark::Duplicate));
+
+static cl::opt<unsigned>
+ NumRepetitions("num-repetitions",
+ cl::desc("number of time to repeat the asm snippet"),
+ cl::cat(BenchmarkOptions), cl::init(10000));
+
+static cl::opt<unsigned>
+ LoopBodySize("loop-body-size",
+ cl::desc("when repeating the instruction snippet by looping "
+ "over it, duplicate the snippet until the loop body "
+ "contains at least this many instruction"),
+ cl::cat(BenchmarkOptions), cl::init(0));
+
+static cl::opt<unsigned> MaxConfigsPerOpcode(
+ "max-configs-per-opcode",
+ cl::desc(
+ "allow to snippet generator to generate at most that many configs"),
+ cl::cat(BenchmarkOptions), cl::init(1));
+
+static cl::opt<bool> IgnoreInvalidSchedClass(
+ "ignore-invalid-sched-class",
+ cl::desc("ignore instructions that do not define a sched class"),
+ cl::cat(BenchmarkOptions), cl::init(false));
+
+static cl::opt<exegesis::InstructionBenchmarkClustering::ModeE>
+ AnalysisClusteringAlgorithm(
+ "analysis-clustering", cl::desc("the clustering algorithm to use"),
+ cl::cat(AnalysisOptions),
+ cl::values(clEnumValN(exegesis::InstructionBenchmarkClustering::Dbscan,
+ "dbscan", "use DBSCAN/OPTICS algorithm"),
+ clEnumValN(exegesis::InstructionBenchmarkClustering::Naive,
+ "naive", "one cluster per opcode")),
+ cl::init(exegesis::InstructionBenchmarkClustering::Dbscan));
+
+static cl::opt<unsigned> AnalysisDbscanNumPoints(
+ "analysis-numpoints",
+ cl::desc("minimum number of points in an analysis cluster (dbscan only)"),
+ cl::cat(AnalysisOptions), cl::init(3));
+
+static cl::opt<float> AnalysisClusteringEpsilon(
+ "analysis-clustering-epsilon",
+ cl::desc("epsilon for benchmark point clustering"),
+ cl::cat(AnalysisOptions), cl::init(0.1));
+
+static cl::opt<float> AnalysisInconsistencyEpsilon(
+ "analysis-inconsistency-epsilon",
+ cl::desc("epsilon for detection of when the cluster is different from the "
+ "LLVM schedule profile values"),
+ cl::cat(AnalysisOptions), cl::init(0.1));
+
+static cl::opt<std::string>
+ AnalysisClustersOutputFile("analysis-clusters-output-file", cl::desc(""),
+ cl::cat(AnalysisOptions), cl::init(""));
+static cl::opt<std::string>
+ AnalysisInconsistenciesOutputFile("analysis-inconsistencies-output-file",
+ cl::desc(""), cl::cat(AnalysisOptions),
+ cl::init(""));
+
+static cl::opt<bool> AnalysisDisplayUnstableOpcodes(
+ "analysis-display-unstable-clusters",
+ cl::desc("if there is more than one benchmark for an opcode, said "
+ "benchmarks may end up not being clustered into the same cluster "
+ "if the measured performance characteristics are different. by "
+ "default all such opcodes are filtered out. this flag will "
+ "instead show only such unstable opcodes"),
+ cl::cat(AnalysisOptions), cl::init(false));
+
+static cl::opt<std::string> CpuName(
+ "mcpu",
+ cl::desc("cpu name to use for pfm counters, leave empty to autodetect"),
+ cl::cat(Options), cl::init(""));
+
+static cl::opt<bool>
+ DumpObjectToDisk("dump-object-to-disk",
+ cl::desc("dumps the generated benchmark object to disk "
+ "and prints a message to access it"),
+ cl::cat(BenchmarkOptions), cl::init(true));
+
+static ExitOnError ExitOnErr("llvm-exegesis error: ");
+
+// Helper function that logs the error(s) and exits.
+template <typename... ArgTs> static void ExitWithError(ArgTs &&... Args) {
+ ExitOnErr(make_error<Failure>(std::forward<ArgTs>(Args)...));
+}
+
+// Check Err. If it's in a failure state log the file error(s) and exit.
+static void ExitOnFileError(const Twine &FileName, Error Err) {
+ if (Err) {
+ ExitOnErr(createFileError(FileName, std::move(Err)));
+ }
+}
+
+// Check E. If it's in a success state then return the contained value.
+// If it's in a failure state log the file error(s) and exit.
+template <typename T>
+T ExitOnFileError(const Twine &FileName, Expected<T> &&E) {
+ ExitOnFileError(FileName, E.takeError());
+ return std::move(*E);
+}
+
+// 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 MCInstrInfo &MCInstrInfo) {
+ const size_t NumSetFlags = (OpcodeNames.empty() ? 0 : 1) +
+ (OpcodeIndex == 0 ? 0 : 1) +
+ (SnippetsFile.empty() ? 0 : 1);
+ if (NumSetFlags != 1) {
+ ExitOnErr.setBanner("llvm-exegesis: ");
+ ExitWithError("please provide one and only one of 'opcode-index', "
+ "'opcode-name' or 'snippets-file'");
+ }
+ if (!SnippetsFile.empty())
+ return {};
+ if (OpcodeIndex > 0)
+ 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.
+ const auto ResolveName = [&MCInstrInfo](StringRef OpcodeName) -> unsigned {
+ for (unsigned I = 1, E = MCInstrInfo.getNumOpcodes(); I < E; ++I)
+ if (MCInstrInfo.getName(I) == OpcodeName)
+ return I;
+ return 0u;
+ };
+ SmallVector<StringRef, 2> Pieces;
+ StringRef(OpcodeNames.getValue())
+ .split(Pieces, ",", /* MaxSplit */ -1, /* KeepEmpty */ false);
+ std::vector<unsigned> Result;
+ for (const StringRef &OpcodeName : Pieces) {
+ if (unsigned Opcode = ResolveName(OpcodeName))
+ Result.push_back(Opcode);
+ else
+ ExitWithError(Twine("unknown opcode ").concat(OpcodeName));
+ }
+ return Result;
+}
+
+// Generates code snippets for opcode `Opcode`.
+static Expected<std::vector<BenchmarkCode>>
+generateSnippets(const LLVMState &State, unsigned Opcode,
+ const BitVector &ForbiddenRegs) {
+ const Instruction &Instr = State.getIC().getInstr(Opcode);
+ const MCInstrDesc &InstrDesc = Instr.Description;
+ // Ignore instructions that we cannot run.
+ if (InstrDesc.isPseudo() || InstrDesc.usesCustomInsertionHook())
+ return make_error<Failure>(
+ "Unsupported opcode: isPseudo/usesCustomInserter");
+ if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch())
+ return make_error<Failure>("Unsupported opcode: isBranch/isIndirectBranch");
+ if (InstrDesc.isCall() || InstrDesc.isReturn())
+ return make_error<Failure>("Unsupported opcode: isCall/isReturn");
+
+ const std::vector<InstructionTemplate> InstructionVariants =
+ State.getExegesisTarget().generateInstructionVariants(
+ Instr, MaxConfigsPerOpcode);
+
+ SnippetGenerator::Options SnippetOptions;
+ SnippetOptions.MaxConfigsPerOpcode = MaxConfigsPerOpcode;
+ const std::unique_ptr<SnippetGenerator> Generator =
+ State.getExegesisTarget().createSnippetGenerator(BenchmarkMode, State,
+ SnippetOptions);
+ if (!Generator)
+ ExitWithError("cannot create snippet generator");
+
+ std::vector<BenchmarkCode> Benchmarks;
+ for (const InstructionTemplate &Variant : InstructionVariants) {
+ if (Benchmarks.size() >= MaxConfigsPerOpcode)
+ break;
+ if (auto Err = Generator->generateConfigurations(Variant, Benchmarks,
+ ForbiddenRegs))
+ return std::move(Err);
+ }
+ return Benchmarks;
+}
+
+void benchmarkMain() {
+#ifndef HAVE_LIBPFM
+ ExitWithError("benchmarking unavailable, LLVM was built without libpfm.");
+#endif
+
+ if (exegesis::pfm::pfmInitialize())
+ ExitWithError("cannot initialize libpfm");
+
+ InitializeNativeTarget();
+ InitializeNativeTargetAsmPrinter();
+ InitializeNativeTargetAsmParser();
+ InitializeNativeExegesisTarget();
+
+ const LLVMState State(CpuName);
+
+ // Preliminary check to ensure features needed for requested
+ // benchmark mode are present on target CPU and/or OS.
+ ExitOnErr(State.getExegesisTarget().checkFeatureSupport());
+
+ const std::unique_ptr<BenchmarkRunner> Runner =
+ ExitOnErr(State.getExegesisTarget().createBenchmarkRunner(
+ BenchmarkMode, State, ResultAggMode));
+ if (!Runner) {
+ ExitWithError("cannot create benchmark runner");
+ }
+
+ const auto Opcodes = getOpcodesOrDie(State.getInstrInfo());
+
+ SmallVector<std::unique_ptr<const SnippetRepetitor>, 2> Repetitors;
+ if (RepetitionMode != InstructionBenchmark::RepetitionModeE::AggregateMin)
+ Repetitors.emplace_back(SnippetRepetitor::Create(RepetitionMode, State));
+ else {
+ for (InstructionBenchmark::RepetitionModeE RepMode :
+ {InstructionBenchmark::RepetitionModeE::Duplicate,
+ InstructionBenchmark::RepetitionModeE::Loop})
+ Repetitors.emplace_back(SnippetRepetitor::Create(RepMode, State));
+ }
+
+ BitVector AllReservedRegs;
+ llvm::for_each(Repetitors,
+ [&AllReservedRegs](
+ const std::unique_ptr<const SnippetRepetitor> &Repetitor) {
+ AllReservedRegs |= Repetitor->getReservedRegs();
+ });
+
+ 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) {
+ errs() << State.getInstrInfo().getName(Opcode)
+ << ": ignoring instruction without sched class\n";
+ continue;
+ }
+
+ auto ConfigsForInstr = generateSnippets(State, Opcode, AllReservedRegs);
+ if (!ConfigsForInstr) {
+ logAllUnhandledErrors(
+ ConfigsForInstr.takeError(), errs(),
+ Twine(State.getInstrInfo().getName(Opcode)).concat(": "));
+ continue;
+ }
+ std::move(ConfigsForInstr->begin(), ConfigsForInstr->end(),
+ std::back_inserter(Configurations));
+ }
+ } else {
+ Configurations = ExitOnErr(readSnippets(State, SnippetsFile));
+ }
+
+ if (NumRepetitions == 0) {
+ ExitOnErr.setBanner("llvm-exegesis: ");
+ ExitWithError("--num-repetitions must be greater than zero");
+ }
+
+ // Write to standard output if file is not set.
+ if (BenchmarkFile.empty())
+ BenchmarkFile = "-";
+
+ for (const BenchmarkCode &Conf : Configurations) {
+ InstructionBenchmark Result = ExitOnErr(Runner->runConfiguration(
+ Conf, NumRepetitions, LoopBodySize, Repetitors, DumpObjectToDisk));
+ ExitOnFileError(BenchmarkFile, Result.writeYaml(State, BenchmarkFile));
+ }
+ exegesis::pfm::pfmTerminate();
+}
+
+// Prints the results of running analysis pass `Pass` to file `OutputFilename`
+// if OutputFilename is non-empty.
+template <typename Pass>
+static void maybeRunAnalysis(const Analysis &Analyzer, const std::string &Name,
+ const std::string &OutputFilename) {
+ if (OutputFilename.empty())
+ return;
+ if (OutputFilename != "-") {
+ errs() << "Printing " << Name << " results to file '" << OutputFilename
+ << "'\n";
+ }
+ std::error_code ErrorCode;
+ raw_fd_ostream ClustersOS(OutputFilename, ErrorCode,
+ sys::fs::FA_Read | sys::fs::FA_Write);
+ if (ErrorCode)
+ ExitOnFileError(OutputFilename, errorCodeToError(ErrorCode));
+ if (auto Err = Analyzer.run<Pass>(ClustersOS))
+ ExitOnFileError(OutputFilename, std::move(Err));
+}
+
+static void analysisMain() {
+ ExitOnErr.setBanner("llvm-exegesis: ");
+ if (BenchmarkFile.empty())
+ ExitWithError("--benchmarks-file must be set");
+
+ if (AnalysisClustersOutputFile.empty() &&
+ AnalysisInconsistenciesOutputFile.empty()) {
+ ExitWithError(
+ "for --mode=analysis: At least one of --analysis-clusters-output-file "
+ "and --analysis-inconsistencies-output-file must be specified");
+ }
+
+ InitializeNativeTarget();
+ InitializeNativeTargetAsmPrinter();
+ InitializeNativeTargetDisassembler();
+
+ // Read benchmarks.
+ const LLVMState State("");
+ const std::vector<InstructionBenchmark> Points = ExitOnFileError(
+ BenchmarkFile, InstructionBenchmark::readYamls(State, BenchmarkFile));
+
+ outs() << "Parsed " << Points.size() << " benchmark points\n";
+ if (Points.empty()) {
+ errs() << "no benchmarks to analyze\n";
+ return;
+ }
+ // FIXME: Check that all points have the same triple/cpu.
+ // FIXME: Merge points from several runs (latency and uops).
+
+ std::string Error;
+ const auto *TheTarget =
+ TargetRegistry::lookupTarget(Points[0].LLVMTriple, Error);
+ if (!TheTarget) {
+ errs() << "unknown target '" << Points[0].LLVMTriple << "'\n";
+ return;
+ }
+
+ std::unique_ptr<MCSubtargetInfo> SubtargetInfo(
+ TheTarget->createMCSubtargetInfo(Points[0].LLVMTriple, CpuName, ""));
+
+ std::unique_ptr<MCInstrInfo> InstrInfo(TheTarget->createMCInstrInfo());
+ assert(InstrInfo && "Unable to create instruction info!");
+
+ const auto Clustering = ExitOnErr(InstructionBenchmarkClustering::create(
+ Points, AnalysisClusteringAlgorithm, AnalysisDbscanNumPoints,
+ AnalysisClusteringEpsilon, SubtargetInfo.get(), InstrInfo.get()));
+
+ const Analysis Analyzer(
+ *TheTarget, std::move(SubtargetInfo), std::move(InstrInfo), Clustering,
+ AnalysisInconsistencyEpsilon, AnalysisDisplayUnstableOpcodes, CpuName);
+
+ maybeRunAnalysis<Analysis::PrintClusters>(Analyzer, "analysis clusters",
+ AnalysisClustersOutputFile);
+ maybeRunAnalysis<Analysis::PrintSchedClassInconsistencies>(
+ Analyzer, "sched class consistency analysis",
+ AnalysisInconsistenciesOutputFile);
+}
+
+} // namespace exegesis
+} // namespace llvm
+
+int main(int Argc, char **Argv) {
+ using namespace llvm;
+ cl::ParseCommandLineOptions(Argc, Argv, "");
+
+ exegesis::ExitOnErr.setExitCodeMapper([](const Error &Err) {
+ if (Err.isA<exegesis::ClusteringError>())
+ return EXIT_SUCCESS;
+ return EXIT_FAILURE;
+ });
+
+ if (exegesis::BenchmarkMode == exegesis::InstructionBenchmark::Unknown) {
+ exegesis::analysisMain();
+ } else {
+ exegesis::benchmarkMain();
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-exegesis/ya.make b/contrib/libs/llvm14/tools/llvm-exegesis/ya.make
new file mode 100644
index 00000000000..e3fe22aaf61
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-exegesis/ya.make
@@ -0,0 +1,69 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/AsmPrinter
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/CodeGen/SelectionDAG
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/ExecutionEngine
+ contrib/libs/llvm14/lib/ExecutionEngine/MCJIT
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc/Shared
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc/TargetProcess
+ contrib/libs/llvm14/lib/ExecutionEngine/RuntimeDyld
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ObjectYAML
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/CFGuard
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/tools/llvm-exegesis/lib
+ contrib/libs/llvm14/tools/llvm-exegesis/lib/X86
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-exegesis
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+CFLAGS(
+ -DLLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET=InitializeX86ExegesisTarget
+)
+
+SRCS(
+ llvm-exegesis.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-extract/llvm-extract.cpp b/contrib/libs/llvm14/tools/llvm-extract/llvm-extract.cpp
new file mode 100644
index 00000000000..3cdef529504
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-extract/llvm-extract.cpp
@@ -0,0 +1,394 @@
+//===- llvm-extract.cpp - LLVM function extraction utility ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This utility changes the input module to only contain a single function,
+// which is primarily used for debugging transformations.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Bitcode/BitcodeWriterPass.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/IRPrintingPasses.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/SystemUtils.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Transforms/IPO.h"
+#include <memory>
+#include <utility>
+using namespace llvm;
+
+cl::OptionCategory ExtractCat("llvm-extract Options");
+
+// InputFilename - The filename to read from.
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input bitcode file>"),
+ cl::init("-"),
+ cl::value_desc("filename"));
+
+static cl::opt<std::string> OutputFilename("o",
+ cl::desc("Specify output filename"),
+ cl::value_desc("filename"),
+ cl::init("-"), cl::cat(ExtractCat));
+
+static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"),
+ cl::cat(ExtractCat));
+
+static cl::opt<bool> DeleteFn("delete",
+ cl::desc("Delete specified Globals from Module"),
+ cl::cat(ExtractCat));
+
+static cl::opt<bool> KeepConstInit("keep-const-init",
+ cl::desc("Keep initializers of constants"),
+ cl::cat(ExtractCat));
+
+static cl::opt<bool>
+ Recursive("recursive", cl::desc("Recursively extract all called functions"),
+ cl::cat(ExtractCat));
+
+// ExtractFuncs - The functions to extract from the module.
+static cl::list<std::string>
+ ExtractFuncs("func", cl::desc("Specify function to extract"),
+ cl::ZeroOrMore, cl::value_desc("function"),
+ cl::cat(ExtractCat));
+
+// ExtractRegExpFuncs - The functions, matched via regular expression, to
+// extract from the module.
+static cl::list<std::string>
+ ExtractRegExpFuncs("rfunc",
+ cl::desc("Specify function(s) to extract using a "
+ "regular expression"),
+ cl::ZeroOrMore, cl::value_desc("rfunction"),
+ cl::cat(ExtractCat));
+
+// ExtractBlocks - The blocks to extract from the module.
+static cl::list<std::string> ExtractBlocks(
+ "bb",
+ cl::desc(
+ "Specify <function, basic block1[;basic block2...]> pairs to extract.\n"
+ "Each pair will create a function.\n"
+ "If multiple basic blocks are specified in one pair,\n"
+ "the first block in the sequence should dominate the rest.\n"
+ "eg:\n"
+ " --bb=f:bb1;bb2 will extract one function with both bb1 and bb2;\n"
+ " --bb=f:bb1 --bb=f:bb2 will extract two functions, one with bb1, one "
+ "with bb2."),
+ cl::ZeroOrMore, cl::value_desc("function:bb1[;bb2...]"),
+ cl::cat(ExtractCat));
+
+// ExtractAlias - The alias to extract from the module.
+static cl::list<std::string>
+ ExtractAliases("alias", cl::desc("Specify alias to extract"),
+ cl::ZeroOrMore, cl::value_desc("alias"),
+ cl::cat(ExtractCat));
+
+// ExtractRegExpAliases - The aliases, matched via regular expression, to
+// extract from the module.
+static cl::list<std::string>
+ ExtractRegExpAliases("ralias",
+ cl::desc("Specify alias(es) to extract using a "
+ "regular expression"),
+ cl::ZeroOrMore, cl::value_desc("ralias"),
+ cl::cat(ExtractCat));
+
+// ExtractGlobals - The globals to extract from the module.
+static cl::list<std::string>
+ ExtractGlobals("glob", cl::desc("Specify global to extract"),
+ cl::ZeroOrMore, cl::value_desc("global"),
+ cl::cat(ExtractCat));
+
+// ExtractRegExpGlobals - The globals, matched via regular expression, to
+// extract from the module...
+static cl::list<std::string>
+ ExtractRegExpGlobals("rglob",
+ cl::desc("Specify global(s) to extract using a "
+ "regular expression"),
+ cl::ZeroOrMore, cl::value_desc("rglobal"),
+ cl::cat(ExtractCat));
+
+static cl::opt<bool> OutputAssembly("S",
+ cl::desc("Write output as LLVM assembly"),
+ cl::Hidden, cl::cat(ExtractCat));
+
+static cl::opt<bool> PreserveBitcodeUseListOrder(
+ "preserve-bc-uselistorder",
+ cl::desc("Preserve use-list order when writing LLVM bitcode."),
+ cl::init(true), cl::Hidden, cl::cat(ExtractCat));
+
+static cl::opt<bool> PreserveAssemblyUseListOrder(
+ "preserve-ll-uselistorder",
+ cl::desc("Preserve use-list order when writing LLVM assembly."),
+ cl::init(false), cl::Hidden, cl::cat(ExtractCat));
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ LLVMContext Context;
+ cl::HideUnrelatedOptions(ExtractCat);
+ cl::ParseCommandLineOptions(argc, argv, "llvm extractor\n");
+
+ // Use lazy loading, since we only care about selected global values.
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M = getLazyIRFileModule(InputFilename, Err, Context);
+
+ if (!M.get()) {
+ Err.print(argv[0], errs());
+ return 1;
+ }
+
+ // Use SetVector to avoid duplicates.
+ SetVector<GlobalValue *> GVs;
+
+ // Figure out which aliases we should extract.
+ for (size_t i = 0, e = ExtractAliases.size(); i != e; ++i) {
+ GlobalAlias *GA = M->getNamedAlias(ExtractAliases[i]);
+ if (!GA) {
+ errs() << argv[0] << ": program doesn't contain alias named '"
+ << ExtractAliases[i] << "'!\n";
+ return 1;
+ }
+ GVs.insert(GA);
+ }
+
+ // Extract aliases via regular expression matching.
+ for (size_t i = 0, e = ExtractRegExpAliases.size(); i != e; ++i) {
+ std::string Error;
+ Regex RegEx(ExtractRegExpAliases[i]);
+ if (!RegEx.isValid(Error)) {
+ errs() << argv[0] << ": '" << ExtractRegExpAliases[i] << "' "
+ "invalid regex: " << Error;
+ }
+ bool match = false;
+ for (Module::alias_iterator GA = M->alias_begin(), E = M->alias_end();
+ GA != E; GA++) {
+ if (RegEx.match(GA->getName())) {
+ GVs.insert(&*GA);
+ match = true;
+ }
+ }
+ if (!match) {
+ errs() << argv[0] << ": program doesn't contain global named '"
+ << ExtractRegExpAliases[i] << "'!\n";
+ return 1;
+ }
+ }
+
+ // Figure out which globals we should extract.
+ for (size_t i = 0, e = ExtractGlobals.size(); i != e; ++i) {
+ GlobalValue *GV = M->getNamedGlobal(ExtractGlobals[i]);
+ if (!GV) {
+ errs() << argv[0] << ": program doesn't contain global named '"
+ << ExtractGlobals[i] << "'!\n";
+ return 1;
+ }
+ GVs.insert(GV);
+ }
+
+ // Extract globals via regular expression matching.
+ for (size_t i = 0, e = ExtractRegExpGlobals.size(); i != e; ++i) {
+ std::string Error;
+ Regex RegEx(ExtractRegExpGlobals[i]);
+ if (!RegEx.isValid(Error)) {
+ errs() << argv[0] << ": '" << ExtractRegExpGlobals[i] << "' "
+ "invalid regex: " << Error;
+ }
+ bool match = false;
+ for (auto &GV : M->globals()) {
+ if (RegEx.match(GV.getName())) {
+ GVs.insert(&GV);
+ match = true;
+ }
+ }
+ if (!match) {
+ errs() << argv[0] << ": program doesn't contain global named '"
+ << ExtractRegExpGlobals[i] << "'!\n";
+ return 1;
+ }
+ }
+
+ // Figure out which functions we should extract.
+ for (size_t i = 0, e = ExtractFuncs.size(); i != e; ++i) {
+ GlobalValue *GV = M->getFunction(ExtractFuncs[i]);
+ if (!GV) {
+ errs() << argv[0] << ": program doesn't contain function named '"
+ << ExtractFuncs[i] << "'!\n";
+ return 1;
+ }
+ GVs.insert(GV);
+ }
+ // Extract functions via regular expression matching.
+ for (size_t i = 0, e = ExtractRegExpFuncs.size(); i != e; ++i) {
+ std::string Error;
+ StringRef RegExStr = ExtractRegExpFuncs[i];
+ Regex RegEx(RegExStr);
+ if (!RegEx.isValid(Error)) {
+ errs() << argv[0] << ": '" << ExtractRegExpFuncs[i] << "' "
+ "invalid regex: " << Error;
+ }
+ bool match = false;
+ for (Module::iterator F = M->begin(), E = M->end(); F != E;
+ F++) {
+ if (RegEx.match(F->getName())) {
+ GVs.insert(&*F);
+ match = true;
+ }
+ }
+ if (!match) {
+ errs() << argv[0] << ": program doesn't contain global named '"
+ << ExtractRegExpFuncs[i] << "'!\n";
+ return 1;
+ }
+ }
+
+ // Figure out which BasicBlocks we should extract.
+ SmallVector<std::pair<Function *, SmallVector<StringRef, 16>>, 2> BBMap;
+ for (StringRef StrPair : ExtractBlocks) {
+ SmallVector<StringRef, 16> BBNames;
+ auto BBInfo = StrPair.split(':');
+ // Get the function.
+ Function *F = M->getFunction(BBInfo.first);
+ if (!F) {
+ errs() << argv[0] << ": program doesn't contain a function named '"
+ << BBInfo.first << "'!\n";
+ return 1;
+ }
+ // Add the function to the materialize list, and store the basic block names
+ // to check after materialization.
+ GVs.insert(F);
+ BBInfo.second.split(BBNames, ';', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+ BBMap.push_back({F, std::move(BBNames)});
+ }
+
+ // Use *argv instead of argv[0] to work around a wrong GCC warning.
+ ExitOnError ExitOnErr(std::string(*argv) + ": error reading input: ");
+
+ if (Recursive) {
+ std::vector<llvm::Function *> Workqueue;
+ for (GlobalValue *GV : GVs) {
+ if (auto *F = dyn_cast<Function>(GV)) {
+ Workqueue.push_back(F);
+ }
+ }
+ while (!Workqueue.empty()) {
+ Function *F = &*Workqueue.back();
+ Workqueue.pop_back();
+ ExitOnErr(F->materialize());
+ for (auto &BB : *F) {
+ for (auto &I : BB) {
+ CallBase *CB = dyn_cast<CallBase>(&I);
+ if (!CB)
+ continue;
+ Function *CF = CB->getCalledFunction();
+ if (!CF)
+ continue;
+ if (CF->isDeclaration() || GVs.count(CF))
+ continue;
+ GVs.insert(CF);
+ Workqueue.push_back(CF);
+ }
+ }
+ }
+ }
+
+ auto Materialize = [&](GlobalValue &GV) { ExitOnErr(GV.materialize()); };
+
+ // Materialize requisite global values.
+ if (!DeleteFn) {
+ for (size_t i = 0, e = GVs.size(); i != e; ++i)
+ Materialize(*GVs[i]);
+ } else {
+ // Deleting. Materialize every GV that's *not* in GVs.
+ SmallPtrSet<GlobalValue *, 8> GVSet(GVs.begin(), GVs.end());
+ for (auto &F : *M) {
+ if (!GVSet.count(&F))
+ Materialize(F);
+ }
+ }
+
+ {
+ std::vector<GlobalValue *> Gvs(GVs.begin(), GVs.end());
+ legacy::PassManager Extract;
+ Extract.add(createGVExtractionPass(Gvs, DeleteFn, KeepConstInit));
+ Extract.run(*M);
+
+ // Now that we have all the GVs we want, mark the module as fully
+ // materialized.
+ // FIXME: should the GVExtractionPass handle this?
+ ExitOnErr(M->materializeAll());
+ }
+
+ // Extract the specified basic blocks from the module and erase the existing
+ // functions.
+ if (!ExtractBlocks.empty()) {
+ // Figure out which BasicBlocks we should extract.
+ SmallVector<SmallVector<BasicBlock *, 16>, 4> GroupOfBBs;
+ for (auto &P : BBMap) {
+ SmallVector<BasicBlock *, 16> BBs;
+ for (StringRef BBName : P.second) {
+ // The function has been materialized, so add its matching basic blocks
+ // to the block extractor list, or fail if a name is not found.
+ auto Res = llvm::find_if(*P.first, [&](const BasicBlock &BB) {
+ return BB.getName().equals(BBName);
+ });
+ if (Res == P.first->end()) {
+ errs() << argv[0] << ": function " << P.first->getName()
+ << " doesn't contain a basic block named '" << BBName
+ << "'!\n";
+ return 1;
+ }
+ BBs.push_back(&*Res);
+ }
+ GroupOfBBs.push_back(BBs);
+ }
+
+ legacy::PassManager PM;
+ PM.add(createBlockExtractorPass(GroupOfBBs, true));
+ PM.run(*M);
+ }
+
+ // In addition to deleting all other functions, we also want to spiff it
+ // up a little bit. Do this now.
+ legacy::PassManager Passes;
+
+ if (!DeleteFn)
+ Passes.add(createGlobalDCEPass()); // Delete unreachable globals
+ Passes.add(createStripDeadDebugInfoPass()); // Remove dead debug info
+ Passes.add(createStripDeadPrototypesPass()); // Remove dead func decls
+
+ std::error_code EC;
+ ToolOutputFile Out(OutputFilename, EC, sys::fs::OF_None);
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return 1;
+ }
+
+ if (OutputAssembly)
+ Passes.add(
+ createPrintModulePass(Out.os(), "", PreserveAssemblyUseListOrder));
+ else if (Force || !CheckBitcodeOutputToConsole(Out.os()))
+ Passes.add(createBitcodeWriterPass(Out.os(), PreserveBitcodeUseListOrder));
+
+ Passes.run(*M.get());
+
+ // Declare success.
+ Out.keep();
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-extract/ya.make b/contrib/libs/llvm14/tools/llvm-extract/ya.make
new file mode 100644
index 00000000000..20b60c2e369
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-extract/ya.make
@@ -0,0 +1,52 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-extract
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-extract.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-gsymutil/llvm-gsymutil.cpp b/contrib/libs/llvm14/tools/llvm-gsymutil/llvm-gsymutil.cpp
new file mode 100644
index 00000000000..4e3c06e1e5f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-gsymutil/llvm-gsymutil.cpp
@@ -0,0 +1,535 @@
+//===-- gsymutil.cpp - GSYM dumping and creation utility for llvm ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cstring>
+#include <inttypes.h>
+#include <iostream>
+#include <map>
+#include <string>
+#include <system_error>
+#include <vector>
+
+#include "llvm/DebugInfo/GSYM/DwarfTransformer.h"
+#include "llvm/DebugInfo/GSYM/FunctionInfo.h"
+#include "llvm/DebugInfo/GSYM/GsymCreator.h"
+#include "llvm/DebugInfo/GSYM/GsymReader.h"
+#include "llvm/DebugInfo/GSYM/InlineInfo.h"
+#include "llvm/DebugInfo/GSYM/LookupResult.h"
+#include "llvm/DebugInfo/GSYM/ObjectFileTransformer.h"
+
+using namespace llvm;
+using namespace gsym;
+using namespace object;
+
+/// @}
+/// Command line options.
+/// @{
+
+namespace {
+using namespace cl;
+
+OptionCategory GeneralOptions("Options");
+OptionCategory ConversionOptions("Conversion Options");
+OptionCategory LookupOptions("Lookup Options");
+
+static opt<bool> Help("h", desc("Alias for -help"), Hidden,
+ cat(GeneralOptions));
+
+static opt<bool> Verbose("verbose",
+ desc("Enable verbose logging and encoding details."),
+ cat(GeneralOptions));
+
+static list<std::string> InputFilenames(Positional, desc("<input GSYM files>"),
+ ZeroOrMore, cat(GeneralOptions));
+
+static opt<std::string>
+ ConvertFilename("convert", cl::init(""),
+ cl::desc("Convert the specified file to the GSYM format.\n"
+ "Supported files include ELF and mach-o files "
+ "that will have their debug info (DWARF) and "
+ "symbol table converted."),
+ cl::value_desc("path"), cat(ConversionOptions));
+
+static list<std::string>
+ ArchFilters("arch",
+ desc("Process debug information for the specified CPU "
+ "architecture only.\nArchitectures may be specified by "
+ "name or by number.\nThis option can be specified "
+ "multiple times, once for each desired architecture."),
+ cl::value_desc("arch"), cat(ConversionOptions));
+
+static opt<std::string>
+ OutputFilename("out-file", cl::init(""),
+ cl::desc("Specify the path where the converted GSYM file "
+ "will be saved.\nWhen not specified, a '.gsym' "
+ "extension will be appended to the file name "
+ "specified in the --convert option."),
+ cl::value_desc("path"), cat(ConversionOptions));
+static alias OutputFilenameAlias("o", desc("Alias for -out-file."),
+ aliasopt(OutputFilename),
+ cat(ConversionOptions));
+
+static opt<bool> Verify("verify",
+ desc("Verify the generated GSYM file against the "
+ "information in the file that was converted."),
+ cat(ConversionOptions));
+
+static opt<unsigned>
+ NumThreads("num-threads",
+ desc("Specify the maximum number (n) of simultaneous threads "
+ "to use when converting files to GSYM.\nDefaults to the "
+ "number of cores on the current machine."),
+ cl::value_desc("n"), cat(ConversionOptions));
+
+static opt<bool>
+ Quiet("quiet", desc("Do not output warnings about the debug information"),
+ cat(ConversionOptions));
+
+static list<uint64_t> LookupAddresses("address",
+ desc("Lookup an address in a GSYM file"),
+ cl::value_desc("addr"),
+ cat(LookupOptions));
+
+static opt<bool> LookupAddressesFromStdin(
+ "addresses-from-stdin",
+ desc("Lookup addresses in a GSYM file that are read from stdin\nEach input "
+ "line is expected to be of the following format: <addr> <gsym-path>"),
+ cat(LookupOptions));
+
+} // namespace
+/// @}
+//===----------------------------------------------------------------------===//
+
+static void error(Error Err) {
+ if (!Err)
+ return;
+ WithColor::error() << toString(std::move(Err)) << "\n";
+ exit(1);
+}
+
+static void error(StringRef Prefix, llvm::Error Err) {
+ if (!Err)
+ return;
+ errs() << Prefix << ": " << Err << "\n";
+ consumeError(std::move(Err));
+ exit(1);
+}
+
+static void error(StringRef Prefix, std::error_code EC) {
+ if (!EC)
+ return;
+ errs() << Prefix << ": " << EC.message() << "\n";
+ exit(1);
+}
+
+static uint32_t getCPUType(MachOObjectFile &MachO) {
+ if (MachO.is64Bit())
+ return MachO.getHeader64().cputype;
+ else
+ return MachO.getHeader().cputype;
+}
+
+/// Return true if the object file has not been filtered by an --arch option.
+static bool filterArch(MachOObjectFile &Obj) {
+ if (ArchFilters.empty())
+ return true;
+
+ Triple ObjTriple(Obj.getArchTriple());
+ StringRef ObjArch = ObjTriple.getArchName();
+
+ for (auto Arch : ArchFilters) {
+ // Match name.
+ if (Arch == ObjArch)
+ return true;
+
+ // Match architecture number.
+ unsigned Value;
+ if (!StringRef(Arch).getAsInteger(0, Value))
+ if (Value == getCPUType(Obj))
+ return true;
+ }
+ return false;
+}
+
+/// Determine the virtual address that is considered the base address of an ELF
+/// object file.
+///
+/// The base address of an ELF file is the the "p_vaddr" of the first program
+/// header whose "p_type" is PT_LOAD.
+///
+/// \param ELFFile An ELF object file we will search.
+///
+/// \returns A valid image base address if we are able to extract one.
+template <class ELFT>
+static llvm::Optional<uint64_t>
+getImageBaseAddress(const object::ELFFile<ELFT> &ELFFile) {
+ auto PhdrRangeOrErr = ELFFile.program_headers();
+ if (!PhdrRangeOrErr) {
+ consumeError(PhdrRangeOrErr.takeError());
+ return llvm::None;
+ }
+ for (const typename ELFT::Phdr &Phdr : *PhdrRangeOrErr)
+ if (Phdr.p_type == ELF::PT_LOAD)
+ return (uint64_t)Phdr.p_vaddr;
+ return llvm::None;
+}
+
+/// Determine the virtual address that is considered the base address of mach-o
+/// object file.
+///
+/// The base address of a mach-o file is the vmaddr of the "__TEXT" segment.
+///
+/// \param MachO A mach-o object file we will search.
+///
+/// \returns A valid image base address if we are able to extract one.
+static llvm::Optional<uint64_t>
+getImageBaseAddress(const object::MachOObjectFile *MachO) {
+ for (const auto &Command : MachO->load_commands()) {
+ if (Command.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command SLC = MachO->getSegmentLoadCommand(Command);
+ StringRef SegName = SLC.segname;
+ if (SegName == "__TEXT")
+ return SLC.vmaddr;
+ } else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 SLC = MachO->getSegment64LoadCommand(Command);
+ StringRef SegName = SLC.segname;
+ if (SegName == "__TEXT")
+ return SLC.vmaddr;
+ }
+ }
+ return llvm::None;
+}
+
+/// Determine the virtual address that is considered the base address of an
+/// object file.
+///
+/// Since GSYM files are used for symbolication, many clients will need to
+/// easily adjust addresses they find in stack traces so the lookups happen
+/// on unslid addresses from the original object file. If the base address of
+/// a GSYM file is set to the base address of the image, then this address
+/// adjusting is much easier.
+///
+/// \param Obj An object file we will search.
+///
+/// \returns A valid image base address if we are able to extract one.
+static llvm::Optional<uint64_t> getImageBaseAddress(object::ObjectFile &Obj) {
+ if (const auto *MachO = dyn_cast<object::MachOObjectFile>(&Obj))
+ return getImageBaseAddress(MachO);
+ else if (const auto *ELFObj = dyn_cast<object::ELF32LEObjectFile>(&Obj))
+ return getImageBaseAddress(ELFObj->getELFFile());
+ else if (const auto *ELFObj = dyn_cast<object::ELF32BEObjectFile>(&Obj))
+ return getImageBaseAddress(ELFObj->getELFFile());
+ else if (const auto *ELFObj = dyn_cast<object::ELF64LEObjectFile>(&Obj))
+ return getImageBaseAddress(ELFObj->getELFFile());
+ else if (const auto *ELFObj = dyn_cast<object::ELF64BEObjectFile>(&Obj))
+ return getImageBaseAddress(ELFObj->getELFFile());
+ return llvm::None;
+}
+
+static llvm::Error handleObjectFile(ObjectFile &Obj,
+ const std::string &OutFile) {
+ auto ThreadCount =
+ NumThreads > 0 ? NumThreads : std::thread::hardware_concurrency();
+ auto &OS = outs();
+
+ GsymCreator Gsym(Quiet);
+
+ // See if we can figure out the base address for a given object file, and if
+ // we can, then set the base address to use to this value. This will ease
+ // symbolication since clients can slide the GSYM lookup addresses by using
+ // the load bias of the shared library.
+ if (auto ImageBaseAddr = getImageBaseAddress(Obj))
+ Gsym.setBaseAddress(*ImageBaseAddr);
+
+ // We need to know where the valid sections are that contain instructions.
+ // See header documentation for DWARFTransformer::SetValidTextRanges() for
+ // defails.
+ AddressRanges TextRanges;
+ for (const object::SectionRef &Sect : Obj.sections()) {
+ if (!Sect.isText())
+ continue;
+ const uint64_t Size = Sect.getSize();
+ if (Size == 0)
+ continue;
+ const uint64_t StartAddr = Sect.getAddress();
+ TextRanges.insert(AddressRange(StartAddr, StartAddr + Size));
+ }
+
+ // Make sure there is DWARF to convert first.
+ std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(Obj);
+ if (!DICtx)
+ return createStringError(std::errc::invalid_argument,
+ "unable to create DWARF context");
+ logAllUnhandledErrors(DICtx->loadRegisterInfo(Obj), OS, "DwarfTransformer: ");
+
+ // Make a DWARF transformer object and populate the ranges of the code
+ // so we don't end up adding invalid functions to GSYM data.
+ DwarfTransformer DT(*DICtx, OS, Gsym);
+ if (!TextRanges.empty())
+ Gsym.SetValidTextRanges(TextRanges);
+
+ // Convert all DWARF to GSYM.
+ if (auto Err = DT.convert(ThreadCount))
+ return Err;
+
+ // Get the UUID and convert symbol table to GSYM.
+ if (auto Err = ObjectFileTransformer::convert(Obj, OS, Gsym))
+ return Err;
+
+ // Finalize the GSYM to make it ready to save to disk. This will remove
+ // duplicate FunctionInfo entries where we might have found an entry from
+ // debug info and also a symbol table entry from the object file.
+ if (auto Err = Gsym.finalize(OS))
+ return Err;
+
+ // Save the GSYM file to disk.
+ support::endianness Endian =
+ Obj.makeTriple().isLittleEndian() ? support::little : support::big;
+ if (auto Err = Gsym.save(OutFile, Endian))
+ return Err;
+
+ // Verify the DWARF if requested. This will ensure all the info in the DWARF
+ // can be looked up in the GSYM and that all lookups get matching data.
+ if (Verify) {
+ if (auto Err = DT.verify(OutFile))
+ return Err;
+ }
+
+ return Error::success();
+}
+
+static llvm::Error handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
+ const std::string &OutFile) {
+ Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Buffer);
+ error(Filename, errorToErrorCode(BinOrErr.takeError()));
+
+ if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) {
+ Triple ObjTriple(Obj->makeTriple());
+ auto ArchName = ObjTriple.getArchName();
+ outs() << "Output file (" << ArchName << "): " << OutFile << "\n";
+ if (auto Err = handleObjectFile(*Obj, OutFile))
+ return Err;
+ } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) {
+ // Iterate over all contained architectures and filter out any that were
+ // not specified with the "--arch <arch>" option. If the --arch option was
+ // not specified on the command line, we will process all architectures.
+ std::vector<std::unique_ptr<MachOObjectFile>> FilterObjs;
+ for (auto &ObjForArch : Fat->objects()) {
+ if (auto MachOOrErr = ObjForArch.getAsObjectFile()) {
+ auto &Obj = **MachOOrErr;
+ if (filterArch(Obj))
+ FilterObjs.emplace_back(MachOOrErr->release());
+ } else {
+ error(Filename, MachOOrErr.takeError());
+ }
+ }
+ if (FilterObjs.empty())
+ error(Filename, createStringError(std::errc::invalid_argument,
+ "no matching architectures found"));
+
+ // Now handle each architecture we need to convert.
+ for (auto &Obj : FilterObjs) {
+ Triple ObjTriple(Obj->getArchTriple());
+ auto ArchName = ObjTriple.getArchName();
+ std::string ArchOutFile(OutFile);
+ // If we are only handling a single architecture, then we will use the
+ // normal output file. If we are handling multiple architectures append
+ // the architecture name to the end of the out file path so that we
+ // don't overwrite the previous architecture's gsym file.
+ if (FilterObjs.size() > 1) {
+ ArchOutFile.append(1, '.');
+ ArchOutFile.append(ArchName.str());
+ }
+ outs() << "Output file (" << ArchName << "): " << ArchOutFile << "\n";
+ if (auto Err = handleObjectFile(*Obj, ArchOutFile))
+ return Err;
+ }
+ }
+ return Error::success();
+}
+
+static llvm::Error handleFileConversionToGSYM(StringRef Filename,
+ const std::string &OutFile) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
+ MemoryBuffer::getFileOrSTDIN(Filename);
+ error(Filename, BuffOrErr.getError());
+ std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get());
+ return handleBuffer(Filename, *Buffer, OutFile);
+}
+
+static llvm::Error convertFileToGSYM(raw_ostream &OS) {
+ // Expand any .dSYM bundles to the individual object files contained therein.
+ std::vector<std::string> Objects;
+ std::string OutFile = OutputFilename;
+ if (OutFile.empty()) {
+ OutFile = ConvertFilename;
+ OutFile += ".gsym";
+ }
+
+ OS << "Input file: " << ConvertFilename << "\n";
+
+ if (auto DsymObjectsOrErr =
+ MachOObjectFile::findDsymObjectMembers(ConvertFilename)) {
+ if (DsymObjectsOrErr->empty())
+ Objects.push_back(ConvertFilename);
+ else
+ llvm::append_range(Objects, *DsymObjectsOrErr);
+ } else {
+ error(DsymObjectsOrErr.takeError());
+ }
+
+ for (auto Object : Objects) {
+ if (auto Err = handleFileConversionToGSYM(Object, OutFile))
+ return Err;
+ }
+ return Error::success();
+}
+
+static void doLookup(GsymReader &Gsym, uint64_t Addr, raw_ostream &OS) {
+ if (auto Result = Gsym.lookup(Addr)) {
+ // If verbose is enabled dump the full function info for the address.
+ if (Verbose) {
+ if (auto FI = Gsym.getFunctionInfo(Addr)) {
+ OS << "FunctionInfo for " << HEX64(Addr) << ":\n";
+ Gsym.dump(OS, *FI);
+ OS << "\nLookupResult for " << HEX64(Addr) << ":\n";
+ }
+ }
+ OS << Result.get();
+ } else {
+ if (Verbose)
+ OS << "\nLookupResult for " << HEX64(Addr) << ":\n";
+ OS << HEX64(Addr) << ": ";
+ logAllUnhandledErrors(Result.takeError(), OS, "error: ");
+ }
+ if (Verbose)
+ OS << "\n";
+}
+
+int main(int argc, char const *argv[]) {
+ // Print a stack trace if we signal out.
+ sys::PrintStackTraceOnErrorSignal(argv[0]);
+ PrettyStackTraceProgram X(argc, argv);
+ llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+
+ llvm::InitializeAllTargets();
+
+ const char *Overview =
+ "A tool for dumping, searching and creating GSYM files.\n\n"
+ "Specify one or more GSYM paths as arguments to dump all of the "
+ "information in each GSYM file.\n"
+ "Specify a single GSYM file along with one or more --lookup options to "
+ "lookup addresses within that GSYM file.\n"
+ "Use the --convert option to specify a file with option --out-file "
+ "option to convert to GSYM format.\n";
+ HideUnrelatedOptions({&GeneralOptions, &ConversionOptions, &LookupOptions});
+ cl::ParseCommandLineOptions(argc, argv, Overview);
+
+ if (Help) {
+ PrintHelpMessage(/*Hidden =*/false, /*Categorized =*/true);
+ return 0;
+ }
+
+ raw_ostream &OS = outs();
+
+ if (!ConvertFilename.empty()) {
+ // Convert DWARF to GSYM
+ if (!InputFilenames.empty()) {
+ OS << "error: no input files can be specified when using the --convert "
+ "option.\n";
+ return 1;
+ }
+ // Call error() if we have an error and it will exit with a status of 1
+ if (auto Err = convertFileToGSYM(OS))
+ error("DWARF conversion failed: ", std::move(Err));
+ return 0;
+ }
+
+ if (LookupAddressesFromStdin) {
+ if (!LookupAddresses.empty() || !InputFilenames.empty()) {
+ OS << "error: no input files or addresses can be specified when using "
+ "the --addresses-from-stdin "
+ "option.\n";
+ return 1;
+ }
+
+ std::string InputLine;
+ std::string CurrentGSYMPath;
+ llvm::Optional<Expected<GsymReader>> CurrentGsym;
+
+ while (std::getline(std::cin, InputLine)) {
+ // Strip newline characters.
+ std::string StrippedInputLine(InputLine);
+ llvm::erase_if(StrippedInputLine,
+ [](char c) { return c == '\r' || c == '\n'; });
+
+ StringRef AddrStr, GSYMPath;
+ std::tie(AddrStr, GSYMPath) =
+ llvm::StringRef{StrippedInputLine}.split(' ');
+
+ if (GSYMPath != CurrentGSYMPath) {
+ CurrentGsym = GsymReader::openFile(GSYMPath);
+ if (!*CurrentGsym)
+ error(GSYMPath, CurrentGsym->takeError());
+ }
+
+ uint64_t Addr;
+ if (AddrStr.getAsInteger(0, Addr)) {
+ OS << "error: invalid address " << AddrStr
+ << ", expected: Address GsymFile.\n";
+ return 1;
+ }
+
+ doLookup(**CurrentGsym, Addr, OS);
+
+ OS << "\n";
+ OS.flush();
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+ // Dump or access data inside GSYM files
+ for (const auto &GSYMPath : InputFilenames) {
+ auto Gsym = GsymReader::openFile(GSYMPath);
+ if (!Gsym)
+ error(GSYMPath, Gsym.takeError());
+
+ if (LookupAddresses.empty()) {
+ Gsym->dump(outs());
+ continue;
+ }
+
+ // Lookup an address in a GSYM file and print any matches.
+ OS << "Looking up addresses in \"" << GSYMPath << "\":\n";
+ for (auto Addr : LookupAddresses) {
+ doLookup(*Gsym, Addr, OS);
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-gsymutil/ya.make b/contrib/libs/llvm14/tools/llvm-gsymutil/ya.make
new file mode 100644
index 00000000000..4b7ab63d10d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-gsymutil/ya.make
@@ -0,0 +1,90 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/AsmPrinter
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/CodeGen/SelectionDAG
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/DebugInfo/GSYM
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/CFGuard
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-gsymutil
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-gsymutil.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-ifs/ErrorCollector.cpp b/contrib/libs/llvm14/tools/llvm-ifs/ErrorCollector.cpp
new file mode 100644
index 00000000000..04daa848548
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-ifs/ErrorCollector.cpp
@@ -0,0 +1,65 @@
+//===- ErrorCollector.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===-----------------------------------------------------------------------===/
+
+#include "ErrorCollector.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::ifs;
+
+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));
+ }
+}
+
+[[noreturn]] void ErrorCollector::fatalUnhandledError() {
+ errs() << "Program aborted due to unhandled Error(s):\n";
+ log(errs());
+ errs() << "\n";
+ abort();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-ifs/ErrorCollector.h b/contrib/libs/llvm14/tools/llvm-ifs/ErrorCollector.h
new file mode 100644
index 00000000000..7217ca3b859
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-ifs/ErrorCollector.h
@@ -0,0 +1,74 @@
+//===- ErrorCollector.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===-----------------------------------------------------------------------===/
+///
+/// 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_LLVM_IFS_ERRORCOLLECTOR_H
+#define LLVM_TOOLS_LLVM_IFS_ERRORCOLLECTOR_H
+
+#include "llvm/Support/Error.h"
+#include <vector>
+
+namespace llvm {
+namespace ifs {
+
+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.
+ [[noreturn]] void fatalUnhandledError();
+
+ bool ErrorsAreFatal;
+ std::vector<Error> Errors;
+ std::vector<std::string> Tags;
+};
+
+} // end namespace ifs
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_IFS_ERRORCOLLECTOR_H
diff --git a/contrib/libs/llvm14/tools/llvm-ifs/llvm-ifs.cpp b/contrib/libs/llvm14/tools/llvm-ifs/llvm-ifs.cpp
new file mode 100644
index 00000000000..2dcd0c5ca9e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-ifs/llvm-ifs.cpp
@@ -0,0 +1,552 @@
+//===- llvm-ifs.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===-----------------------------------------------------------------------===/
+
+#include "ErrorCollector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/InterfaceStub/ELFObjHandler.h"
+#include "llvm/InterfaceStub/IFSHandler.h"
+#include "llvm/InterfaceStub/IFSStub.h"
+#include "llvm/ObjectYAML/yaml2obj.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/VersionTuple.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TextAPI/InterfaceFile.h"
+#include "llvm/TextAPI/TextAPIReader.h"
+#include "llvm/TextAPI/TextAPIWriter.h"
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::yaml;
+using namespace llvm::MachO;
+using namespace llvm::ifs;
+
+#define DEBUG_TYPE "llvm-ifs"
+
+namespace {
+const VersionTuple IfsVersionCurrent(3, 0);
+
+enum class FileFormat { IFS, ELF, TBD };
+} // end anonymous namespace
+
+cl::OptionCategory IfsCategory("Ifs Options");
+
+// TODO: Use OptTable for option parsing in the future.
+// Command line flags:
+cl::list<std::string> InputFilePaths(cl::Positional, cl::desc("input"),
+ cl::ZeroOrMore, cl::cat(IfsCategory));
+cl::opt<FileFormat> InputFormat(
+ "input-format", cl::desc("Specify the input file format"),
+ cl::values(clEnumValN(FileFormat::IFS, "IFS", "Text based ELF stub file"),
+ clEnumValN(FileFormat::ELF, "ELF", "ELF object file")),
+ cl::cat(IfsCategory));
+cl::opt<FileFormat> OutputFormat(
+ "output-format", cl::desc("Specify the output file format **DEPRECATED**"),
+ cl::values(clEnumValN(FileFormat::IFS, "IFS", "Text based ELF stub file"),
+ clEnumValN(FileFormat::ELF, "ELF", "ELF stub file"),
+ clEnumValN(FileFormat::TBD, "TBD", "Apple TBD text stub file")),
+ cl::cat(IfsCategory));
+cl::opt<std::string> OptArch("arch",
+ cl::desc("Specify the architecture, e.g. x86_64"),
+ cl::cat(IfsCategory));
+cl::opt<IFSBitWidthType>
+ OptBitWidth("bitwidth", cl::desc("Specify the bit width"),
+ cl::values(clEnumValN(IFSBitWidthType::IFS32, "32", "32 bits"),
+ clEnumValN(IFSBitWidthType::IFS64, "64", "64 bits")),
+ cl::cat(IfsCategory));
+cl::opt<IFSEndiannessType> OptEndianness(
+ "endianness", cl::desc("Specify the endianness"),
+ cl::values(clEnumValN(IFSEndiannessType::Little, "little", "Little Endian"),
+ clEnumValN(IFSEndiannessType::Big, "big", "Big Endian")),
+ cl::cat(IfsCategory));
+cl::opt<std::string> OptTargetTriple(
+ "target", cl::desc("Specify the target triple, e.g. x86_64-linux-gnu"),
+ cl::cat(IfsCategory));
+cl::opt<std::string> OptTargetTripleHint(
+ "hint-ifs-target",
+ cl::desc("When --output-format is 'IFS', this flag will hint the expected "
+ "target triple for IFS output"),
+ cl::cat(IfsCategory));
+cl::opt<bool> StripIFSArch(
+ "strip-ifs-arch",
+ cl::desc("Strip target architecture information away from IFS output"),
+ cl::cat(IfsCategory));
+cl::opt<bool> StripIFSBitWidth(
+ "strip-ifs-bitwidth",
+ cl::desc("Strip target bit width information away from IFS output"),
+ cl::cat(IfsCategory));
+cl::opt<bool> StripIFSEndiannessWidth(
+ "strip-ifs-endianness",
+ cl::desc("Strip target endianness information away from IFS output"),
+ cl::cat(IfsCategory));
+cl::opt<bool> StripIFSTarget(
+ "strip-ifs-target",
+ cl::desc("Strip all target information away from IFS output"),
+ cl::cat(IfsCategory));
+cl::opt<bool>
+ StripUndefined("strip-undefined",
+ cl::desc("Strip undefined symbols from IFS output"),
+ cl::cat(IfsCategory));
+
+cl::opt<std::string>
+ SoName("soname",
+ cl::desc("Manually set the DT_SONAME entry of any emitted files"),
+ cl::value_desc("name"), cl::cat(IfsCategory));
+cl::opt<std::string> OutputFilePath("output",
+ cl::desc("Output file **DEPRECATED**"),
+ cl::cat(IfsCategory));
+cl::alias OutputFilePathA("o", cl::desc("Alias for --output"),
+ cl::aliasopt(OutputFilePath), cl::cat(IfsCategory));
+cl::opt<std::string> OutputELFFilePath("output-elf",
+ cl::desc("Output path for ELF file"),
+ cl::cat(IfsCategory));
+cl::opt<std::string> OutputIFSFilePath("output-ifs",
+ cl::desc("Output path for IFS file"),
+ cl::cat(IfsCategory));
+cl::opt<std::string> OutputTBDFilePath("output-tbd",
+ cl::desc("Output path for TBD file"),
+ cl::cat(IfsCategory));
+
+cl::opt<bool> WriteIfChanged(
+ "write-if-changed",
+ cl::desc("Write the output file only if it is new or has changed."),
+ cl::cat(IfsCategory));
+
+static std::string getTypeName(IFSSymbolType Type) {
+ switch (Type) {
+ case IFSSymbolType::NoType:
+ return "NoType";
+ case IFSSymbolType::Func:
+ return "Func";
+ case IFSSymbolType::Object:
+ return "Object";
+ case IFSSymbolType::TLS:
+ return "TLS";
+ case IFSSymbolType::Unknown:
+ return "Unknown";
+ }
+ llvm_unreachable("Unexpected ifs symbol type.");
+}
+
+static Expected<std::unique_ptr<IFSStub>> readInputFile(StringRef FilePath) {
+ // Read in file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
+ MemoryBuffer::getFileOrSTDIN(FilePath, /*IsText=*/true);
+ 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 (InputFormat.getNumOccurrences() == 0 || InputFormat == FileFormat::ELF) {
+ Expected<std::unique_ptr<IFSStub>> StubFromELF =
+ readELFFile(FileReadBuffer->getMemBufferRef());
+ if (StubFromELF) {
+ (*StubFromELF)->IfsVersion = IfsVersionCurrent;
+ return std::move(*StubFromELF);
+ }
+ EC.addError(StubFromELF.takeError(), "BinaryRead");
+ }
+
+ // Fall back to reading as a ifs.
+ if (InputFormat.getNumOccurrences() == 0 || InputFormat == FileFormat::IFS) {
+ Expected<std::unique_ptr<IFSStub>> StubFromIFS =
+ readIFSFromBuffer(FileReadBuffer->getBuffer());
+ if (StubFromIFS) {
+ if ((*StubFromIFS)->IfsVersion > IfsVersionCurrent)
+ EC.addError(
+ createStringError(errc::not_supported,
+ "IFS version " +
+ (*StubFromIFS)->IfsVersion.getAsString() +
+ " is unsupported."),
+ "ReadInputFile");
+ else
+ return std::move(*StubFromIFS);
+ } else {
+ EC.addError(StubFromIFS.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();
+}
+
+static int writeTbdStub(const Triple &T, const std::vector<IFSSymbol> &Symbols,
+ const StringRef Format, raw_ostream &Out) {
+
+ auto PlatformTypeOrError =
+ [](const llvm::Triple &T) -> llvm::Expected<llvm::MachO::PlatformType> {
+ if (T.isMacOSX())
+ return llvm::MachO::PLATFORM_MACOS;
+ if (T.isTvOS())
+ return llvm::MachO::PLATFORM_TVOS;
+ if (T.isWatchOS())
+ return llvm::MachO::PLATFORM_WATCHOS;
+ // Note: put isiOS last because tvOS and watchOS are also iOS according
+ // to the Triple.
+ if (T.isiOS())
+ return llvm::MachO::PLATFORM_IOS;
+
+ return createStringError(errc::not_supported, "Invalid Platform.\n");
+ }(T);
+
+ if (!PlatformTypeOrError)
+ return -1;
+
+ PlatformType Plat = PlatformTypeOrError.get();
+ TargetList Targets({Target(llvm::MachO::mapToArchitecture(T), Plat)});
+
+ InterfaceFile File;
+ File.setFileType(FileType::TBD_V3); // Only supporting v3 for now.
+ File.addTargets(Targets);
+
+ for (const auto &Symbol : Symbols) {
+ auto Name = Symbol.Name;
+ auto Kind = SymbolKind::GlobalSymbol;
+ switch (Symbol.Type) {
+ default:
+ case IFSSymbolType::NoType:
+ Kind = SymbolKind::GlobalSymbol;
+ break;
+ case IFSSymbolType::Object:
+ Kind = SymbolKind::GlobalSymbol;
+ break;
+ case IFSSymbolType::Func:
+ Kind = SymbolKind::GlobalSymbol;
+ break;
+ }
+ if (Symbol.Weak)
+ File.addSymbol(Kind, Name, Targets, SymbolFlags::WeakDefined);
+ else
+ File.addSymbol(Kind, Name, Targets);
+ }
+
+ SmallString<4096> Buffer;
+ raw_svector_ostream OS(Buffer);
+ if (Error Result = TextAPIWriter::writeToStream(OS, File))
+ return -1;
+ Out << OS.str();
+ return 0;
+}
+
+static void fatalError(Error Err) {
+ WithColor::defaultErrorHandler(std::move(Err));
+ exit(1);
+}
+
+/// writeIFS() writes a Text-Based ELF stub to a file using the latest version
+/// of the YAML parser.
+static Error writeIFS(StringRef FilePath, IFSStub &Stub) {
+ // Write IFS to memory first.
+ std::string IFSStr;
+ raw_string_ostream OutStr(IFSStr);
+ Error YAMLErr = writeIFSToOutputStream(OutStr, Stub);
+ if (YAMLErr)
+ return YAMLErr;
+ OutStr.flush();
+
+ if (WriteIfChanged) {
+ if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
+ MemoryBuffer::getFile(FilePath)) {
+ // Compare IFS output with the existing IFS file. If unchanged, avoid changing the file.
+ if ((*BufOrError)->getBuffer() == IFSStr)
+ return Error::success();
+ }
+ }
+ // Open IFS file for writing.
+ std::error_code SysErr;
+ raw_fd_ostream Out(FilePath, SysErr);
+ if (SysErr)
+ return createStringError(SysErr, "Couldn't open `%s` for writing",
+ FilePath.data());
+ Out << IFSStr;
+ return Error::success();
+}
+
+int main(int argc, char *argv[]) {
+ // Parse arguments.
+ cl::HideUnrelatedOptions({&IfsCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv);
+
+ if (InputFilePaths.empty())
+ InputFilePaths.push_back("-");
+
+ // If input files are more than one, they can only be IFS files.
+ if (InputFilePaths.size() > 1)
+ InputFormat.setValue(FileFormat::IFS);
+
+ // Attempt to merge input.
+ IFSStub Stub;
+ std::map<std::string, IFSSymbol> SymbolMap;
+ std::string PreviousInputFilePath;
+ for (const std::string &InputFilePath : InputFilePaths) {
+ Expected<std::unique_ptr<IFSStub>> StubOrErr = readInputFile(InputFilePath);
+ if (!StubOrErr)
+ fatalError(StubOrErr.takeError());
+
+ std::unique_ptr<IFSStub> TargetStub = std::move(StubOrErr.get());
+ if (PreviousInputFilePath.empty()) {
+ Stub.IfsVersion = TargetStub->IfsVersion;
+ Stub.Target = TargetStub->Target;
+ Stub.SoName = TargetStub->SoName;
+ Stub.NeededLibs = TargetStub->NeededLibs;
+ } else {
+ if (Stub.IfsVersion != TargetStub->IfsVersion) {
+ if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {
+ WithColor::error()
+ << "Interface Stub: IfsVersion Mismatch."
+ << "\nFilenames: " << PreviousInputFilePath << " "
+ << InputFilePath << "\nIfsVersion Values: " << Stub.IfsVersion
+ << " " << TargetStub->IfsVersion << "\n";
+ return -1;
+ }
+ if (TargetStub->IfsVersion > Stub.IfsVersion)
+ Stub.IfsVersion = TargetStub->IfsVersion;
+ }
+ if (Stub.Target != TargetStub->Target && !TargetStub->Target.empty()) {
+ WithColor::error() << "Interface Stub: Target Mismatch."
+ << "\nFilenames: " << PreviousInputFilePath << " "
+ << InputFilePath;
+ return -1;
+ }
+ if (Stub.SoName != TargetStub->SoName) {
+ WithColor::error() << "Interface Stub: SoName Mismatch."
+ << "\nFilenames: " << PreviousInputFilePath << " "
+ << InputFilePath
+ << "\nSoName Values: " << Stub.SoName << " "
+ << TargetStub->SoName << "\n";
+ return -1;
+ }
+ if (Stub.NeededLibs != TargetStub->NeededLibs) {
+ WithColor::error() << "Interface Stub: NeededLibs Mismatch."
+ << "\nFilenames: " << PreviousInputFilePath << " "
+ << InputFilePath << "\n";
+ return -1;
+ }
+ }
+
+ for (auto Symbol : TargetStub->Symbols) {
+ auto SI = SymbolMap.find(Symbol.Name);
+ if (SI == SymbolMap.end()) {
+ SymbolMap.insert(
+ std::pair<std::string, IFSSymbol>(Symbol.Name, Symbol));
+ continue;
+ }
+
+ assert(Symbol.Name == SI->second.Name && "Symbol Names Must Match.");
+
+ // Check conflicts:
+ if (Symbol.Type != SI->second.Type) {
+ WithColor::error() << "Interface Stub: Type Mismatch for "
+ << Symbol.Name << ".\nFilename: " << InputFilePath
+ << "\nType Values: " << getTypeName(SI->second.Type)
+ << " " << getTypeName(Symbol.Type) << "\n";
+
+ return -1;
+ }
+ if (Symbol.Size != SI->second.Size) {
+ WithColor::error() << "Interface Stub: Size Mismatch for "
+ << Symbol.Name << ".\nFilename: " << InputFilePath
+ << "\nSize Values: " << SI->second.Size << " "
+ << Symbol.Size << "\n";
+
+ return -1;
+ }
+ if (Symbol.Weak != SI->second.Weak) {
+ Symbol.Weak = false;
+ continue;
+ }
+ // TODO: Not checking Warning. Will be dropped.
+ }
+
+ PreviousInputFilePath = InputFilePath;
+ }
+
+ if (Stub.IfsVersion != IfsVersionCurrent)
+ if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {
+ WithColor::error() << "Interface Stub: Bad IfsVersion: "
+ << Stub.IfsVersion << ", llvm-ifs supported version: "
+ << IfsVersionCurrent << ".\n";
+ return -1;
+ }
+
+ for (auto &Entry : SymbolMap)
+ Stub.Symbols.push_back(Entry.second);
+
+ // Change SoName before emitting stubs.
+ if (SoName.getNumOccurrences() == 1)
+ Stub.SoName = SoName;
+ Optional<IFSArch> OverrideArch;
+ Optional<IFSEndiannessType> OverrideEndianness;
+ Optional<IFSBitWidthType> OverrideBitWidth;
+ Optional<std::string> OverrideTriple;
+ if (OptArch.getNumOccurrences() == 1)
+ OverrideArch = ELF::convertArchNameToEMachine(OptArch.getValue());
+ if (OptEndianness.getNumOccurrences() == 1)
+ OverrideEndianness = OptEndianness.getValue();
+ if (OptBitWidth.getNumOccurrences() == 1)
+ OverrideBitWidth = OptBitWidth.getValue();
+ if (OptTargetTriple.getNumOccurrences() == 1)
+ OverrideTriple = OptTargetTriple.getValue();
+ Error OverrideError = overrideIFSTarget(
+ Stub, OverrideArch, OverrideEndianness, OverrideBitWidth, OverrideTriple);
+ if (OverrideError)
+ fatalError(std::move(OverrideError));
+
+ if (OutputELFFilePath.getNumOccurrences() == 0 &&
+ OutputIFSFilePath.getNumOccurrences() == 0 &&
+ OutputTBDFilePath.getNumOccurrences() == 0) {
+ if (OutputFormat.getNumOccurrences() == 0) {
+ WithColor::error() << "at least one output should be specified.";
+ return -1;
+ }
+ } else if (OutputFormat.getNumOccurrences() == 1) {
+ WithColor::error() << "'--output-format' cannot be used with "
+ "'--output-{FILE_FORMAT}' options at the same time";
+ return -1;
+ }
+ if (OutputFormat.getNumOccurrences() == 1) {
+ // TODO: Remove OutputFormat flag in the next revision.
+ WithColor::warning() << "--output-format option is deprecated, please use "
+ "--output-{FILE_FORMAT} options instead\n";
+ switch (OutputFormat.getValue()) {
+ case FileFormat::TBD: {
+ std::error_code SysErr;
+ raw_fd_ostream Out(OutputFilePath, SysErr);
+ if (SysErr) {
+ WithColor::error() << "Couldn't open " << OutputFilePath
+ << " for writing.\n";
+ return -1;
+ }
+ if (!Stub.Target.Triple) {
+ WithColor::error()
+ << "Triple should be defined when output format is TBD";
+ return -1;
+ }
+ return writeTbdStub(llvm::Triple(Stub.Target.Triple.getValue()),
+ Stub.Symbols, "TBD", Out);
+ }
+ case FileFormat::IFS: {
+ Stub.IfsVersion = IfsVersionCurrent;
+ if (InputFormat.getValue() == FileFormat::ELF &&
+ OptTargetTripleHint.getNumOccurrences() == 1) {
+ std::error_code HintEC(1, std::generic_category());
+ IFSTarget HintTarget = parseTriple(OptTargetTripleHint);
+ if (Stub.Target.Arch.getValue() != HintTarget.Arch.getValue())
+ fatalError(make_error<StringError>(
+ "Triple hint does not match the actual architecture", HintEC));
+ if (Stub.Target.Endianness.getValue() !=
+ HintTarget.Endianness.getValue())
+ fatalError(make_error<StringError>(
+ "Triple hint does not match the actual endianness", HintEC));
+ if (Stub.Target.BitWidth.getValue() != HintTarget.BitWidth.getValue())
+ fatalError(make_error<StringError>(
+ "Triple hint does not match the actual bit width", HintEC));
+
+ stripIFSTarget(Stub, true, false, false, false);
+ Stub.Target.Triple = OptTargetTripleHint.getValue();
+ } else {
+ stripIFSTarget(Stub, StripIFSTarget, StripIFSArch,
+ StripIFSEndiannessWidth, StripIFSBitWidth);
+ }
+ if (StripUndefined)
+ stripIFSUndefinedSymbols(Stub);
+ Error IFSWriteError = writeIFS(OutputFilePath.getValue(), Stub);
+ if (IFSWriteError)
+ fatalError(std::move(IFSWriteError));
+ break;
+ }
+ case FileFormat::ELF: {
+ Error TargetError = validateIFSTarget(Stub, true);
+ if (TargetError)
+ fatalError(std::move(TargetError));
+ Error BinaryWriteError =
+ writeBinaryStub(OutputFilePath, Stub, WriteIfChanged);
+ if (BinaryWriteError)
+ fatalError(std::move(BinaryWriteError));
+ break;
+ }
+ }
+ } else {
+ // Check if output path for individual format.
+ if (OutputELFFilePath.getNumOccurrences() == 1) {
+ Error TargetError = validateIFSTarget(Stub, true);
+ if (TargetError)
+ fatalError(std::move(TargetError));
+ Error BinaryWriteError =
+ writeBinaryStub(OutputELFFilePath, Stub, WriteIfChanged);
+ if (BinaryWriteError)
+ fatalError(std::move(BinaryWriteError));
+ }
+ if (OutputIFSFilePath.getNumOccurrences() == 1) {
+ Stub.IfsVersion = IfsVersionCurrent;
+ if (InputFormat.getValue() == FileFormat::ELF &&
+ OptTargetTripleHint.getNumOccurrences() == 1) {
+ std::error_code HintEC(1, std::generic_category());
+ IFSTarget HintTarget = parseTriple(OptTargetTripleHint);
+ if (Stub.Target.Arch.getValue() != HintTarget.Arch.getValue())
+ fatalError(make_error<StringError>(
+ "Triple hint does not match the actual architecture", HintEC));
+ if (Stub.Target.Endianness.getValue() !=
+ HintTarget.Endianness.getValue())
+ fatalError(make_error<StringError>(
+ "Triple hint does not match the actual endianness", HintEC));
+ if (Stub.Target.BitWidth.getValue() != HintTarget.BitWidth.getValue())
+ fatalError(make_error<StringError>(
+ "Triple hint does not match the actual bit width", HintEC));
+
+ stripIFSTarget(Stub, true, false, false, false);
+ Stub.Target.Triple = OptTargetTripleHint.getValue();
+ } else {
+ stripIFSTarget(Stub, StripIFSTarget, StripIFSArch,
+ StripIFSEndiannessWidth, StripIFSBitWidth);
+ }
+ if (StripUndefined)
+ stripIFSUndefinedSymbols(Stub);
+ Error IFSWriteError = writeIFS(OutputIFSFilePath.getValue(), Stub);
+ if (IFSWriteError)
+ fatalError(std::move(IFSWriteError));
+ }
+ if (OutputTBDFilePath.getNumOccurrences() == 1) {
+ std::error_code SysErr;
+ raw_fd_ostream Out(OutputTBDFilePath, SysErr);
+ if (SysErr) {
+ WithColor::error() << "Couldn't open " << OutputTBDFilePath
+ << " for writing.\n";
+ return -1;
+ }
+ if (!Stub.Target.Triple) {
+ WithColor::error()
+ << "Triple should be defined when output format is TBD";
+ return -1;
+ }
+ return writeTbdStub(llvm::Triple(Stub.Target.Triple.getValue()),
+ Stub.Symbols, "TBD", Out);
+ }
+ }
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-ifs/ya.make b/contrib/libs/llvm14/tools/llvm-ifs/ya.make
new file mode 100644
index 00000000000..72a2b68ede5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-ifs/ya.make
@@ -0,0 +1,39 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/InterfaceStub
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ObjectYAML
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-ifs
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ ErrorCollector.cpp
+ llvm-ifs.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-elf.cpp b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-elf.cpp
new file mode 100644
index 00000000000..d79dbc410e8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-elf.cpp
@@ -0,0 +1,175 @@
+//===---- llvm-jitlink-elf.cpp -- ELF parsing support for llvm-jitlink ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// ELF parsing support for llvm-jitlink.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-jitlink.h"
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Path.h"
+
+#define DEBUG_TYPE "llvm_jitlink"
+
+using namespace llvm;
+using namespace llvm::jitlink;
+
+static bool isELFGOTSection(Section &S) { return S.getName() == "$__GOT"; }
+
+static bool isELFStubsSection(Section &S) { return S.getName() == "$__STUBS"; }
+
+static Expected<Edge &> getFirstRelocationEdge(LinkGraph &G, Block &B) {
+ auto EItr = std::find_if(B.edges().begin(), B.edges().end(),
+ [](Edge &E) { return E.isRelocation(); });
+ if (EItr == B.edges().end())
+ return make_error<StringError>("GOT entry in " + G.getName() + ", \"" +
+ B.getSection().getName() +
+ "\" has no relocations",
+ inconvertibleErrorCode());
+ return *EItr;
+}
+
+static Expected<Symbol &> getELFGOTTarget(LinkGraph &G, Block &B) {
+ auto E = getFirstRelocationEdge(G, B);
+ if (!E)
+ return E.takeError();
+ auto &TargetSym = E->getTarget();
+ if (!TargetSym.hasName())
+ return make_error<StringError>(
+ "GOT entry in " + G.getName() + ", \"" +
+ TargetSym.getBlock().getSection().getName() +
+ "\" points to anonymous "
+ "symbol",
+ inconvertibleErrorCode());
+ return TargetSym;
+}
+
+static Expected<Symbol &> getELFStubTarget(LinkGraph &G, Block &B) {
+ auto E = getFirstRelocationEdge(G, B);
+ if (!E)
+ return E.takeError();
+ auto &GOTSym = E->getTarget();
+ if (!GOTSym.isDefined() || !isELFGOTSection(GOTSym.getBlock().getSection()))
+ return make_error<StringError>(
+ "Stubs entry in " + G.getName() + ", \"" +
+ GOTSym.getBlock().getSection().getName() +
+ "\" does not point to GOT entry",
+ inconvertibleErrorCode());
+ return getELFGOTTarget(G, GOTSym.getBlock());
+}
+
+namespace llvm {
+
+Error registerELFGraphInfo(Session &S, LinkGraph &G) {
+ auto FileName = sys::path::filename(G.getName());
+ if (S.FileInfos.count(FileName)) {
+ return make_error<StringError>("When -check is passed, file names must be "
+ "distinct (duplicate: \"" +
+ FileName + "\")",
+ inconvertibleErrorCode());
+ }
+
+ auto &FileInfo = S.FileInfos[FileName];
+ LLVM_DEBUG({
+ dbgs() << "Registering ELF file info for \"" << FileName << "\"\n";
+ });
+ for (auto &Sec : G.sections()) {
+ LLVM_DEBUG({
+ dbgs() << " Section \"" << Sec.getName() << "\": "
+ << (llvm::empty(Sec.symbols()) ? "empty. skipping."
+ : "processing...")
+ << "\n";
+ });
+
+ // Skip empty sections.
+ if (llvm::empty(Sec.symbols()))
+ continue;
+
+ if (FileInfo.SectionInfos.count(Sec.getName()))
+ return make_error<StringError>("Encountered duplicate section name \"" +
+ Sec.getName() + "\" in \"" + FileName +
+ "\"",
+ inconvertibleErrorCode());
+
+ bool isGOTSection = isELFGOTSection(Sec);
+ bool isStubsSection = isELFStubsSection(Sec);
+
+ bool SectionContainsContent = false;
+ bool SectionContainsZeroFill = false;
+
+ auto *FirstSym = *Sec.symbols().begin();
+ auto *LastSym = FirstSym;
+ for (auto *Sym : Sec.symbols()) {
+ if (Sym->getAddress() < FirstSym->getAddress())
+ FirstSym = Sym;
+ if (Sym->getAddress() > LastSym->getAddress())
+ LastSym = Sym;
+
+ if (isGOTSection) {
+ if (Sym->isSymbolZeroFill())
+ return make_error<StringError>("zero-fill atom in GOT section",
+ inconvertibleErrorCode());
+
+ // If this is a GOT symbol with size (i.e. not the GOT start symbol)
+ // then add it to the GOT entry info table.
+ if (Sym->getSize() != 0) {
+ if (auto TS = getELFGOTTarget(G, Sym->getBlock()))
+ FileInfo.GOTEntryInfos[TS->getName()] = {
+ Sym->getSymbolContent(), Sym->getAddress().getValue()};
+ else
+ return TS.takeError();
+ }
+ SectionContainsContent = true;
+ } else if (isStubsSection) {
+ if (Sym->isSymbolZeroFill())
+ return make_error<StringError>("zero-fill atom in Stub section",
+ inconvertibleErrorCode());
+
+ if (auto TS = getELFStubTarget(G, Sym->getBlock()))
+ FileInfo.StubInfos[TS->getName()] = {Sym->getSymbolContent(),
+ Sym->getAddress().getValue()};
+ else
+ return TS.takeError();
+ SectionContainsContent = true;
+ }
+
+ if (Sym->hasName()) {
+ if (Sym->isSymbolZeroFill()) {
+ S.SymbolInfos[Sym->getName()] = {Sym->getSize(),
+ Sym->getAddress().getValue()};
+ SectionContainsZeroFill = true;
+ } else {
+ S.SymbolInfos[Sym->getName()] = {Sym->getSymbolContent(),
+ Sym->getAddress().getValue()};
+ SectionContainsContent = true;
+ }
+ }
+ }
+
+ auto SecAddr = FirstSym->getAddress();
+ auto SecSize =
+ (LastSym->getBlock().getAddress() + LastSym->getBlock().getSize()) -
+ SecAddr;
+
+ if (SectionContainsZeroFill && SectionContainsContent)
+ return make_error<StringError>("Mixed zero-fill and content sections not "
+ "supported yet",
+ inconvertibleErrorCode());
+ if (SectionContainsZeroFill)
+ FileInfo.SectionInfos[Sec.getName()] = {SecSize, SecAddr.getValue()};
+ else
+ FileInfo.SectionInfos[Sec.getName()] = {
+ ArrayRef<char>(FirstSym->getBlock().getContent().data(), SecSize),
+ SecAddr.getValue()};
+ }
+
+ return Error::success();
+}
+
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp
new file mode 100644
index 00000000000..7100c274b98
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp
@@ -0,0 +1,179 @@
+//===- llvm-jitlink-executor.cpp - Out-of-proc executor for llvm-jitlink -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Simple out-of-process executor for llvm-jitlink.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstring>
+#include <sstream>
+
+#ifdef LLVM_ON_UNIX
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#endif
+
+using namespace llvm;
+using namespace llvm::orc;
+
+ExitOnError ExitOnErr;
+
+LLVM_ATTRIBUTE_USED void linkComponents() {
+ errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper
+ << (void *)&llvm_orc_deregisterEHFrameSectionWrapper
+ << (void *)&llvm_orc_registerJITLoaderGDBWrapper;
+}
+
+void printErrorAndExit(Twine ErrMsg) {
+#ifndef NDEBUG
+ const char *DebugOption = "[debug] ";
+#else
+ const char *DebugOption = "";
+#endif
+
+ errs() << "error: " << ErrMsg.str() << "\n\n"
+ << "Usage:\n"
+ << " llvm-jitlink-executor " << DebugOption
+ << "filedescs=<infd>,<outfd> [args...]\n"
+ << " llvm-jitlink-executor " << DebugOption
+ << "listen=<host>:<port> [args...]\n";
+ exit(1);
+}
+
+int openListener(std::string Host, std::string PortStr) {
+#ifndef LLVM_ON_UNIX
+ // FIXME: Add TCP support for Windows.
+ printErrorAndExit("listen option not supported");
+ return 0;
+#else
+ addrinfo Hints{};
+ Hints.ai_family = AF_INET;
+ Hints.ai_socktype = SOCK_STREAM;
+ Hints.ai_flags = AI_PASSIVE;
+
+ addrinfo *AI;
+ if (int EC = getaddrinfo(nullptr, PortStr.c_str(), &Hints, &AI)) {
+ errs() << "Error setting up bind address: " << gai_strerror(EC) << "\n";
+ exit(1);
+ }
+
+ // Create a socket from first addrinfo structure returned by getaddrinfo.
+ int SockFD;
+ if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) {
+ errs() << "Error creating socket: " << std::strerror(errno) << "\n";
+ exit(1);
+ }
+
+ // Avoid "Address already in use" errors.
+ const int Yes = 1;
+ if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) {
+ errs() << "Error calling setsockopt: " << std::strerror(errno) << "\n";
+ exit(1);
+ }
+
+ // Bind the socket to the desired port.
+ if (bind(SockFD, AI->ai_addr, AI->ai_addrlen) < 0) {
+ errs() << "Error on binding: " << std::strerror(errno) << "\n";
+ exit(1);
+ }
+
+ // Listen for incomming connections.
+ static constexpr int ConnectionQueueLen = 1;
+ listen(SockFD, ConnectionQueueLen);
+
+#if defined(_AIX)
+ assert(Hi_32(AI->ai_addrlen) == 0 && "Field is a size_t on 64-bit AIX");
+ socklen_t AddrLen = Lo_32(AI->ai_addrlen);
+ return accept(SockFD, AI->ai_addr, &AddrLen);
+#else
+ return accept(SockFD, AI->ai_addr, &AI->ai_addrlen);
+#endif
+
+#endif // LLVM_ON_UNIX
+}
+
+int main(int argc, char *argv[]) {
+#if LLVM_ENABLE_THREADS
+
+ ExitOnErr.setBanner(std::string(argv[0]) + ": ");
+
+ unsigned FirstProgramArg = 1;
+ int InFD = 0;
+ int OutFD = 0;
+
+ if (argc < 2)
+ printErrorAndExit("insufficient arguments");
+ else {
+
+ StringRef ConnectArg = argv[FirstProgramArg++];
+#ifndef NDEBUG
+ if (ConnectArg == "debug") {
+ DebugFlag = true;
+ ConnectArg = argv[FirstProgramArg++];
+ }
+#endif
+
+ StringRef SpecifierType, Specifier;
+ std::tie(SpecifierType, Specifier) = ConnectArg.split('=');
+ if (SpecifierType == "filedescs") {
+ StringRef FD1Str, FD2Str;
+ std::tie(FD1Str, FD2Str) = Specifier.split(',');
+ if (FD1Str.getAsInteger(10, InFD))
+ printErrorAndExit(FD1Str + " is not a valid file descriptor");
+ if (FD2Str.getAsInteger(10, OutFD))
+ printErrorAndExit(FD2Str + " is not a valid file descriptor");
+ } else if (SpecifierType == "listen") {
+ StringRef Host, PortStr;
+ std::tie(Host, PortStr) = Specifier.split(':');
+
+ int Port = 0;
+ if (PortStr.getAsInteger(10, Port))
+ printErrorAndExit("port number '" + PortStr +
+ "' is not a valid integer");
+
+ InFD = OutFD = openListener(Host.str(), PortStr.str());
+ } else
+ printErrorAndExit("invalid specifier type \"" + SpecifierType + "\"");
+ }
+
+ auto Server =
+ ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>(
+ [](SimpleRemoteEPCServer::Setup &S) -> Error {
+ S.setDispatcher(
+ std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>());
+ S.bootstrapSymbols() =
+ SimpleRemoteEPCServer::defaultBootstrapSymbols();
+ S.services().push_back(
+ std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>());
+ return Error::success();
+ },
+ InFD, OutFD));
+
+ ExitOnErr(Server->waitForDisconnect());
+ return 0;
+
+#else
+ errs() << argv[0]
+ << " error: this tool requires threads, but LLVM was "
+ "built with LLVM_ENABLE_THREADS=Off\n";
+ return 1;
+#endif
+}
diff --git a/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/ya.make b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/ya.make
new file mode 100644
index 00000000000..dfc915f68b2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor/ya.make
@@ -0,0 +1,29 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc/Shared
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc/TargetProcess
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-executor
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-jitlink-executor.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-macho.cpp b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-macho.cpp
new file mode 100644
index 00000000000..ed7fd1a57a7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink-macho.cpp
@@ -0,0 +1,170 @@
+//===-- llvm-jitlink-macho.cpp -- MachO parsing support for llvm-jitlink --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// MachO parsing support for llvm-jitlink.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-jitlink.h"
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Path.h"
+
+#define DEBUG_TYPE "llvm_jitlink"
+
+using namespace llvm;
+using namespace llvm::jitlink;
+
+static bool isMachOGOTSection(Section &S) { return S.getName() == "$__GOT"; }
+
+static bool isMachOStubsSection(Section &S) {
+ return S.getName() == "$__STUBS";
+}
+
+static Expected<Edge &> getFirstRelocationEdge(LinkGraph &G, Block &B) {
+ auto EItr = std::find_if(B.edges().begin(), B.edges().end(),
+ [](Edge &E) { return E.isRelocation(); });
+ if (EItr == B.edges().end())
+ return make_error<StringError>("GOT entry in " + G.getName() + ", \"" +
+ B.getSection().getName() +
+ "\" has no relocations",
+ inconvertibleErrorCode());
+ return *EItr;
+}
+
+static Expected<Symbol &> getMachOGOTTarget(LinkGraph &G, Block &B) {
+ auto E = getFirstRelocationEdge(G, B);
+ if (!E)
+ return E.takeError();
+ auto &TargetSym = E->getTarget();
+ if (!TargetSym.hasName())
+ return make_error<StringError>(
+ "GOT entry in " + G.getName() + ", \"" +
+ TargetSym.getBlock().getSection().getName() +
+ "\" points to anonymous "
+ "symbol",
+ inconvertibleErrorCode());
+ return TargetSym;
+}
+
+static Expected<Symbol &> getMachOStubTarget(LinkGraph &G, Block &B) {
+ auto E = getFirstRelocationEdge(G, B);
+ if (!E)
+ return E.takeError();
+ auto &GOTSym = E->getTarget();
+ if (!GOTSym.isDefined() || !isMachOGOTSection(GOTSym.getBlock().getSection()))
+ return make_error<StringError>(
+ "Stubs entry in " + G.getName() + ", \"" +
+ GOTSym.getBlock().getSection().getName() +
+ "\" does not point to GOT entry",
+ inconvertibleErrorCode());
+ return getMachOGOTTarget(G, GOTSym.getBlock());
+}
+
+namespace llvm {
+
+Error registerMachOGraphInfo(Session &S, LinkGraph &G) {
+ auto FileName = sys::path::filename(G.getName());
+ if (S.FileInfos.count(FileName)) {
+ return make_error<StringError>("When -check is passed, file names must be "
+ "distinct (duplicate: \"" +
+ FileName + "\")",
+ inconvertibleErrorCode());
+ }
+
+ auto &FileInfo = S.FileInfos[FileName];
+ LLVM_DEBUG({
+ dbgs() << "Registering MachO file info for \"" << FileName << "\"\n";
+ });
+ for (auto &Sec : G.sections()) {
+ LLVM_DEBUG({
+ dbgs() << " Section \"" << Sec.getName() << "\": "
+ << (llvm::empty(Sec.symbols()) ? "empty. skipping."
+ : "processing...")
+ << "\n";
+ });
+
+ // Skip empty sections.
+ if (llvm::empty(Sec.symbols()))
+ continue;
+
+ if (FileInfo.SectionInfos.count(Sec.getName()))
+ return make_error<StringError>("Encountered duplicate section name \"" +
+ Sec.getName() + "\" in \"" + FileName +
+ "\"",
+ inconvertibleErrorCode());
+
+ bool isGOTSection = isMachOGOTSection(Sec);
+ bool isStubsSection = isMachOStubsSection(Sec);
+
+ bool SectionContainsContent = false;
+ bool SectionContainsZeroFill = false;
+
+ auto *FirstSym = *Sec.symbols().begin();
+ auto *LastSym = FirstSym;
+ for (auto *Sym : Sec.symbols()) {
+ if (Sym->getAddress() < FirstSym->getAddress())
+ FirstSym = Sym;
+ if (Sym->getAddress() > LastSym->getAddress())
+ LastSym = Sym;
+ if (isGOTSection) {
+ if (Sym->isSymbolZeroFill())
+ return make_error<StringError>("zero-fill atom in GOT section",
+ inconvertibleErrorCode());
+
+ if (auto TS = getMachOGOTTarget(G, Sym->getBlock()))
+ FileInfo.GOTEntryInfos[TS->getName()] = {
+ Sym->getSymbolContent(), Sym->getAddress().getValue()};
+ else
+ return TS.takeError();
+ SectionContainsContent = true;
+ } else if (isStubsSection) {
+ if (Sym->isSymbolZeroFill())
+ return make_error<StringError>("zero-fill atom in Stub section",
+ inconvertibleErrorCode());
+
+ if (auto TS = getMachOStubTarget(G, Sym->getBlock()))
+ FileInfo.StubInfos[TS->getName()] = {Sym->getSymbolContent(),
+ Sym->getAddress().getValue()};
+ else
+ return TS.takeError();
+ SectionContainsContent = true;
+ } else if (Sym->hasName()) {
+ if (Sym->isSymbolZeroFill()) {
+ S.SymbolInfos[Sym->getName()] = {Sym->getSize(),
+ Sym->getAddress().getValue()};
+ SectionContainsZeroFill = true;
+ } else {
+ S.SymbolInfos[Sym->getName()] = {Sym->getSymbolContent(),
+ Sym->getAddress().getValue()};
+ SectionContainsContent = true;
+ }
+ }
+ }
+
+ auto SecAddr = FirstSym->getAddress();
+ auto SecSize =
+ (LastSym->getBlock().getAddress() + LastSym->getBlock().getSize()) -
+ SecAddr;
+
+ if (SectionContainsZeroFill && SectionContainsContent)
+ return make_error<StringError>("Mixed zero-fill and content sections not "
+ "supported yet",
+ inconvertibleErrorCode());
+ if (SectionContainsZeroFill)
+ FileInfo.SectionInfos[Sec.getName()] = {SecSize, SecAddr.getValue()};
+ else
+ FileInfo.SectionInfos[Sec.getName()] = {
+ ArrayRef<char>(FirstSym->getBlock().getContent().data(), SecSize),
+ SecAddr.getValue()};
+ }
+
+ return Error::success();
+}
+
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink.cpp b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink.cpp
new file mode 100644
index 00000000000..aacac7279b5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -0,0 +1,2007 @@
+//===- llvm-jitlink.cpp -- Command line interface/tester for llvm-jitlink -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This utility provides a simple command line interface to the llvm jitlink
+// library, which makes relocatable object files executable in memory. Its
+// primary function is as a testing utility for the jitlink library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-jitlink.h"
+
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
+#include "llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h"
+#include "llvm/ExecutionEngine/Orc/ELFNixPlatform.h"
+#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
+#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
+#include "llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h"
+#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
+#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
+#include "llvm/ExecutionEngine/Orc/MachOPlatform.h"
+#include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.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"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Timer.h"
+
+#include <cstring>
+#include <list>
+#include <string>
+
+#ifdef LLVM_ON_UNIX
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#endif // LLVM_ON_UNIX
+
+#define DEBUG_TYPE "llvm_jitlink"
+
+using namespace llvm;
+using namespace llvm::jitlink;
+using namespace llvm::orc;
+
+static cl::OptionCategory JITLinkCategory("JITLink Options");
+
+static cl::list<std::string> InputFiles(cl::Positional, cl::OneOrMore,
+ cl::desc("input files"),
+ cl::cat(JITLinkCategory));
+
+static cl::list<std::string>
+ LibrarySearchPaths("L",
+ cl::desc("Add dir to the list of library search paths"),
+ cl::Prefix, cl::cat(JITLinkCategory));
+
+static cl::list<std::string>
+ Libraries("l",
+ cl::desc("Link against library X in the library search paths"),
+ cl::Prefix, cl::cat(JITLinkCategory));
+
+static cl::list<std::string>
+ LibrariesHidden("hidden-l",
+ cl::desc("Link against library X in the library search "
+ "paths with hidden visibility"),
+ cl::Prefix, cl::cat(JITLinkCategory));
+
+static cl::list<std::string>
+ LoadHidden("load_hidden",
+ cl::desc("Link against library X with hidden visibility"),
+ cl::cat(JITLinkCategory));
+
+static cl::opt<bool> NoExec("noexec", cl::desc("Do not execute loaded code"),
+ cl::init(false), cl::cat(JITLinkCategory));
+
+static cl::list<std::string>
+ CheckFiles("check", cl::desc("File containing verifier checks"),
+ cl::ZeroOrMore, cl::cat(JITLinkCategory));
+
+static cl::opt<std::string>
+ CheckName("check-name", cl::desc("Name of checks to match against"),
+ cl::init("jitlink-check"), cl::cat(JITLinkCategory));
+
+static cl::opt<std::string>
+ EntryPointName("entry", cl::desc("Symbol to call as main entry point"),
+ cl::init(""), cl::cat(JITLinkCategory));
+
+static cl::list<std::string> JITDylibs(
+ "jd",
+ cl::desc("Specifies the JITDylib to be used for any subsequent "
+ "input file, -L<seacrh-path>, and -l<library> arguments"),
+ cl::cat(JITLinkCategory));
+
+static cl::list<std::string>
+ Dylibs("preload",
+ cl::desc("Pre-load dynamic libraries (e.g. language runtimes "
+ "required by the ORC runtime)"),
+ cl::ZeroOrMore, cl::cat(JITLinkCategory));
+
+static cl::list<std::string> InputArgv("args", cl::Positional,
+ cl::desc("<program arguments>..."),
+ cl::ZeroOrMore, cl::PositionalEatsArgs,
+ cl::cat(JITLinkCategory));
+
+static cl::opt<bool>
+ DebuggerSupport("debugger-support",
+ cl::desc("Enable debugger suppport (default = !-noexec)"),
+ cl::init(true), cl::Hidden, cl::cat(JITLinkCategory));
+
+static cl::opt<bool>
+ NoProcessSymbols("no-process-syms",
+ cl::desc("Do not resolve to llvm-jitlink process symbols"),
+ cl::init(false), cl::cat(JITLinkCategory));
+
+static cl::list<std::string> AbsoluteDefs(
+ "define-abs",
+ cl::desc("Inject absolute symbol definitions (syntax: <name>=<addr>)"),
+ cl::ZeroOrMore, cl::cat(JITLinkCategory));
+
+static cl::list<std::string> TestHarnesses("harness", cl::Positional,
+ cl::desc("Test harness files"),
+ cl::ZeroOrMore,
+ cl::PositionalEatsArgs,
+ cl::cat(JITLinkCategory));
+
+static cl::opt<bool> ShowInitialExecutionSessionState(
+ "show-init-es",
+ cl::desc("Print ExecutionSession state before resolving entry point"),
+ cl::init(false), cl::cat(JITLinkCategory));
+
+static cl::opt<bool> ShowEntryExecutionSessionState(
+ "show-entry-es",
+ cl::desc("Print ExecutionSession state after resolving entry point"),
+ cl::init(false), cl::cat(JITLinkCategory));
+
+static cl::opt<bool> ShowAddrs(
+ "show-addrs",
+ cl::desc("Print registered symbol, section, got and stub addresses"),
+ cl::init(false), cl::cat(JITLinkCategory));
+
+static cl::opt<bool> ShowLinkGraph(
+ "show-graph",
+ cl::desc("Print the link graph after fixups have been applied"),
+ cl::init(false), cl::cat(JITLinkCategory));
+
+static cl::opt<bool> ShowSizes(
+ "show-sizes",
+ cl::desc("Show sizes pre- and post-dead stripping, and allocations"),
+ cl::init(false), cl::cat(JITLinkCategory));
+
+static cl::opt<bool> ShowTimes("show-times",
+ cl::desc("Show times for llvm-jitlink phases"),
+ cl::init(false), cl::cat(JITLinkCategory));
+
+static cl::opt<std::string> SlabAllocateSizeString(
+ "slab-allocate",
+ cl::desc("Allocate from a slab of the given size "
+ "(allowable suffixes: Kb, Mb, Gb. default = "
+ "Kb)"),
+ cl::init(""), cl::cat(JITLinkCategory));
+
+static cl::opt<uint64_t> SlabAddress(
+ "slab-address",
+ cl::desc("Set slab target address (requires -slab-allocate and -noexec)"),
+ cl::init(~0ULL), cl::cat(JITLinkCategory));
+
+static cl::opt<uint64_t> SlabPageSize(
+ "slab-page-size",
+ cl::desc("Set page size for slab (requires -slab-allocate and -noexec)"),
+ cl::init(0), cl::cat(JITLinkCategory));
+
+static cl::opt<bool> ShowRelocatedSectionContents(
+ "show-relocated-section-contents",
+ cl::desc("show section contents after fixups have been applied"),
+ cl::init(false), cl::cat(JITLinkCategory));
+
+static cl::opt<bool> PhonyExternals(
+ "phony-externals",
+ cl::desc("resolve all otherwise unresolved externals to null"),
+ cl::init(false), cl::cat(JITLinkCategory));
+
+static cl::opt<std::string> OutOfProcessExecutor(
+ "oop-executor", cl::desc("Launch an out-of-process executor to run code"),
+ cl::ValueOptional, cl::cat(JITLinkCategory));
+
+static cl::opt<std::string> OutOfProcessExecutorConnect(
+ "oop-executor-connect",
+ cl::desc("Connect to an out-of-process executor via TCP"),
+ cl::cat(JITLinkCategory));
+
+static cl::opt<std::string>
+ OrcRuntime("orc-runtime", cl::desc("Use ORC runtime from given path"),
+ cl::init(""), cl::cat(JITLinkCategory));
+
+static cl::opt<bool> AddSelfRelocations(
+ "add-self-relocations",
+ cl::desc("Add relocations to function pointers to the current function"),
+ cl::init(false), cl::cat(JITLinkCategory));
+
+ExitOnError ExitOnErr;
+
+LLVM_ATTRIBUTE_USED void linkComponents() {
+ errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper
+ << (void *)&llvm_orc_deregisterEHFrameSectionWrapper
+ << (void *)&llvm_orc_registerJITLoaderGDBWrapper;
+}
+
+static bool UseTestResultOverride = false;
+static int64_t TestResultOverride = 0;
+
+extern "C" LLVM_ATTRIBUTE_USED void
+llvm_jitlink_setTestResultOverride(int64_t Value) {
+ TestResultOverride = Value;
+ UseTestResultOverride = true;
+}
+
+static Error addSelfRelocations(LinkGraph &G);
+
+namespace llvm {
+
+static raw_ostream &
+operator<<(raw_ostream &OS, const Session::MemoryRegionInfo &MRI) {
+ return OS << "target addr = "
+ << format("0x%016" PRIx64, MRI.getTargetAddress())
+ << ", content: " << (const void *)MRI.getContent().data() << " -- "
+ << (const void *)(MRI.getContent().data() + MRI.getContent().size())
+ << " (" << MRI.getContent().size() << " bytes)";
+}
+
+static raw_ostream &
+operator<<(raw_ostream &OS, const Session::SymbolInfoMap &SIM) {
+ OS << "Symbols:\n";
+ for (auto &SKV : SIM)
+ OS << " \"" << SKV.first() << "\" " << SKV.second << "\n";
+ return OS;
+}
+
+static raw_ostream &
+operator<<(raw_ostream &OS, const Session::FileInfo &FI) {
+ for (auto &SIKV : FI.SectionInfos)
+ OS << " Section \"" << SIKV.first() << "\": " << SIKV.second << "\n";
+ for (auto &GOTKV : FI.GOTEntryInfos)
+ OS << " GOT \"" << GOTKV.first() << "\": " << GOTKV.second << "\n";
+ for (auto &StubKV : FI.StubInfos)
+ OS << " Stub \"" << StubKV.first() << "\": " << StubKV.second << "\n";
+ return OS;
+}
+
+static raw_ostream &
+operator<<(raw_ostream &OS, const Session::FileInfoMap &FIM) {
+ for (auto &FIKV : FIM)
+ OS << "File \"" << FIKV.first() << "\":\n" << FIKV.second;
+ return OS;
+}
+
+static Error applyHarnessPromotions(Session &S, LinkGraph &G) {
+
+ // If this graph is part of the test harness there's nothing to do.
+ if (S.HarnessFiles.empty() || S.HarnessFiles.count(G.getName()))
+ return Error::success();
+
+ LLVM_DEBUG(dbgs() << "Applying promotions to graph " << G.getName() << "\n");
+
+ // If this graph is part of the test then promote any symbols referenced by
+ // the harness to default scope, remove all symbols that clash with harness
+ // definitions.
+ std::vector<Symbol *> DefinitionsToRemove;
+ for (auto *Sym : G.defined_symbols()) {
+
+ if (!Sym->hasName())
+ continue;
+
+ if (Sym->getLinkage() == Linkage::Weak) {
+ if (!S.CanonicalWeakDefs.count(Sym->getName()) ||
+ S.CanonicalWeakDefs[Sym->getName()] != G.getName()) {
+ LLVM_DEBUG({
+ dbgs() << " Externalizing weak symbol " << Sym->getName() << "\n";
+ });
+ DefinitionsToRemove.push_back(Sym);
+ } else {
+ LLVM_DEBUG({
+ dbgs() << " Making weak symbol " << Sym->getName() << " strong\n";
+ });
+ if (S.HarnessExternals.count(Sym->getName()))
+ Sym->setScope(Scope::Default);
+ else
+ Sym->setScope(Scope::Hidden);
+ Sym->setLinkage(Linkage::Strong);
+ }
+ } else if (S.HarnessExternals.count(Sym->getName())) {
+ LLVM_DEBUG(dbgs() << " Promoting " << Sym->getName() << "\n");
+ Sym->setScope(Scope::Default);
+ Sym->setLive(true);
+ continue;
+ } else if (S.HarnessDefinitions.count(Sym->getName())) {
+ LLVM_DEBUG(dbgs() << " Externalizing " << Sym->getName() << "\n");
+ DefinitionsToRemove.push_back(Sym);
+ }
+ }
+
+ for (auto *Sym : DefinitionsToRemove)
+ G.makeExternal(*Sym);
+
+ return Error::success();
+}
+
+static uint64_t computeTotalBlockSizes(LinkGraph &G) {
+ uint64_t TotalSize = 0;
+ for (auto *B : G.blocks())
+ TotalSize += B->getSize();
+ return TotalSize;
+}
+
+static void dumpSectionContents(raw_ostream &OS, LinkGraph &G) {
+ constexpr orc::ExecutorAddrDiff DumpWidth = 16;
+ static_assert(isPowerOf2_64(DumpWidth), "DumpWidth must be a power of two");
+
+ // Put sections in address order.
+ std::vector<Section *> Sections;
+ for (auto &S : G.sections())
+ Sections.push_back(&S);
+
+ llvm::sort(Sections, [](const Section *LHS, const Section *RHS) {
+ if (llvm::empty(LHS->symbols()) && llvm::empty(RHS->symbols()))
+ return false;
+ if (llvm::empty(LHS->symbols()))
+ return false;
+ if (llvm::empty(RHS->symbols()))
+ return true;
+ SectionRange LHSRange(*LHS);
+ SectionRange RHSRange(*RHS);
+ return LHSRange.getStart() < RHSRange.getStart();
+ });
+
+ for (auto *S : Sections) {
+ OS << S->getName() << " content:";
+ if (llvm::empty(S->symbols())) {
+ OS << "\n section empty\n";
+ continue;
+ }
+
+ // Sort symbols into order, then render.
+ std::vector<Symbol *> Syms(S->symbols().begin(), S->symbols().end());
+ llvm::sort(Syms, [](const Symbol *LHS, const Symbol *RHS) {
+ return LHS->getAddress() < RHS->getAddress();
+ });
+
+ orc::ExecutorAddr NextAddr(Syms.front()->getAddress().getValue() &
+ ~(DumpWidth - 1));
+ for (auto *Sym : Syms) {
+ bool IsZeroFill = Sym->getBlock().isZeroFill();
+ auto SymStart = Sym->getAddress();
+ auto SymSize = Sym->getSize();
+ auto SymEnd = SymStart + SymSize;
+ const uint8_t *SymData = IsZeroFill ? nullptr
+ : reinterpret_cast<const uint8_t *>(
+ Sym->getSymbolContent().data());
+
+ // Pad any space before the symbol starts.
+ while (NextAddr != SymStart) {
+ if (NextAddr % DumpWidth == 0)
+ OS << formatv("\n{0:x16}:", NextAddr);
+ OS << " ";
+ ++NextAddr;
+ }
+
+ // Render the symbol content.
+ while (NextAddr != SymEnd) {
+ if (NextAddr % DumpWidth == 0)
+ OS << formatv("\n{0:x16}:", NextAddr);
+ if (IsZeroFill)
+ OS << " 00";
+ else
+ OS << formatv(" {0:x-2}", SymData[NextAddr - SymStart]);
+ ++NextAddr;
+ }
+ }
+ OS << "\n";
+ }
+}
+
+class JITLinkSlabAllocator final : public JITLinkMemoryManager {
+private:
+ struct FinalizedAllocInfo {
+ FinalizedAllocInfo(sys::MemoryBlock Mem,
+ std::vector<shared::WrapperFunctionCall> DeallocActions)
+ : Mem(Mem), DeallocActions(std::move(DeallocActions)) {}
+ sys::MemoryBlock Mem;
+ std::vector<shared::WrapperFunctionCall> DeallocActions;
+ };
+
+public:
+ static Expected<std::unique_ptr<JITLinkSlabAllocator>>
+ Create(uint64_t SlabSize) {
+ Error Err = Error::success();
+ std::unique_ptr<JITLinkSlabAllocator> Allocator(
+ new JITLinkSlabAllocator(SlabSize, Err));
+ if (Err)
+ return std::move(Err);
+ return std::move(Allocator);
+ }
+
+ void allocate(const JITLinkDylib *JD, LinkGraph &G,
+ OnAllocatedFunction OnAllocated) override {
+
+ // Local class for allocation.
+ class IPMMAlloc : public InFlightAlloc {
+ public:
+ IPMMAlloc(JITLinkSlabAllocator &Parent, BasicLayout BL,
+ sys::MemoryBlock StandardSegs, sys::MemoryBlock FinalizeSegs)
+ : Parent(Parent), BL(std::move(BL)),
+ StandardSegs(std::move(StandardSegs)),
+ FinalizeSegs(std::move(FinalizeSegs)) {}
+
+ void finalize(OnFinalizedFunction OnFinalized) override {
+ if (auto Err = applyProtections()) {
+ OnFinalized(std::move(Err));
+ return;
+ }
+
+ auto DeallocActions = runFinalizeActions(BL.graphAllocActions());
+ if (!DeallocActions) {
+ OnFinalized(DeallocActions.takeError());
+ return;
+ }
+
+ if (auto Err = Parent.freeBlock(FinalizeSegs)) {
+ OnFinalized(
+ joinErrors(std::move(Err), runDeallocActions(*DeallocActions)));
+ return;
+ }
+
+ OnFinalized(FinalizedAlloc(ExecutorAddr::fromPtr(
+ new FinalizedAllocInfo(StandardSegs, std::move(*DeallocActions)))));
+ }
+
+ void abandon(OnAbandonedFunction OnAbandoned) override {
+ OnAbandoned(joinErrors(Parent.freeBlock(StandardSegs),
+ Parent.freeBlock(FinalizeSegs)));
+ }
+
+ private:
+ Error applyProtections() {
+ for (auto &KV : BL.segments()) {
+ const auto &Group = KV.first;
+ auto &Seg = KV.second;
+
+ auto Prot = toSysMemoryProtectionFlags(Group.getMemProt());
+
+ uint64_t SegSize =
+ alignTo(Seg.ContentSize + Seg.ZeroFillSize, Parent.PageSize);
+ sys::MemoryBlock MB(Seg.WorkingMem, SegSize);
+ if (auto EC = sys::Memory::protectMappedMemory(MB, Prot))
+ return errorCodeToError(EC);
+ if (Prot & sys::Memory::MF_EXEC)
+ sys::Memory::InvalidateInstructionCache(MB.base(),
+ MB.allocatedSize());
+ }
+ return Error::success();
+ }
+
+ JITLinkSlabAllocator &Parent;
+ BasicLayout BL;
+ sys::MemoryBlock StandardSegs;
+ sys::MemoryBlock FinalizeSegs;
+ };
+
+ BasicLayout BL(G);
+ auto SegsSizes = BL.getContiguousPageBasedLayoutSizes(PageSize);
+
+ if (!SegsSizes) {
+ OnAllocated(SegsSizes.takeError());
+ return;
+ }
+
+ char *AllocBase = nullptr;
+ {
+ std::lock_guard<std::mutex> Lock(SlabMutex);
+
+ if (SegsSizes->total() > SlabRemaining.allocatedSize()) {
+ OnAllocated(make_error<StringError>(
+ "Slab allocator out of memory: request for " +
+ formatv("{0:x}", SegsSizes->total()) +
+ " bytes exceeds remaining capacity of " +
+ formatv("{0:x}", SlabRemaining.allocatedSize()) + " bytes",
+ inconvertibleErrorCode()));
+ return;
+ }
+
+ AllocBase = reinterpret_cast<char *>(SlabRemaining.base());
+ SlabRemaining =
+ sys::MemoryBlock(AllocBase + SegsSizes->total(),
+ SlabRemaining.allocatedSize() - SegsSizes->total());
+ }
+
+ sys::MemoryBlock StandardSegs(AllocBase, SegsSizes->StandardSegs);
+ sys::MemoryBlock FinalizeSegs(AllocBase + SegsSizes->StandardSegs,
+ SegsSizes->FinalizeSegs);
+
+ auto NextStandardSegAddr = ExecutorAddr::fromPtr(StandardSegs.base());
+ auto NextFinalizeSegAddr = ExecutorAddr::fromPtr(FinalizeSegs.base());
+
+ LLVM_DEBUG({
+ dbgs() << "JITLinkSlabAllocator allocated:\n";
+ if (SegsSizes->StandardSegs)
+ dbgs() << formatv(" [ {0:x16} -- {1:x16} ]", NextStandardSegAddr,
+ NextStandardSegAddr + StandardSegs.allocatedSize())
+ << " to stardard segs\n";
+ else
+ dbgs() << " no standard segs\n";
+ if (SegsSizes->FinalizeSegs)
+ dbgs() << formatv(" [ {0:x16} -- {1:x16} ]", NextFinalizeSegAddr,
+ NextFinalizeSegAddr + FinalizeSegs.allocatedSize())
+ << " to finalize segs\n";
+ else
+ dbgs() << " no finalize segs\n";
+ });
+
+ for (auto &KV : BL.segments()) {
+ auto &Group = KV.first;
+ auto &Seg = KV.second;
+
+ auto &SegAddr =
+ (Group.getMemDeallocPolicy() == MemDeallocPolicy::Standard)
+ ? NextStandardSegAddr
+ : NextFinalizeSegAddr;
+
+ LLVM_DEBUG({
+ dbgs() << " " << Group << " -> " << formatv("{0:x16}", SegAddr)
+ << "\n";
+ });
+ Seg.WorkingMem = SegAddr.toPtr<char *>();
+ Seg.Addr = SegAddr + NextSlabDelta;
+
+ SegAddr += alignTo(Seg.ContentSize + Seg.ZeroFillSize, PageSize);
+
+ // Zero out the zero-fill memory.
+ if (Seg.ZeroFillSize != 0)
+ memset(Seg.WorkingMem + Seg.ContentSize, 0, Seg.ZeroFillSize);
+ }
+
+ NextSlabDelta += SegsSizes->total();
+
+ if (auto Err = BL.apply()) {
+ OnAllocated(std::move(Err));
+ return;
+ }
+
+ OnAllocated(std::unique_ptr<InProcessMemoryManager::InFlightAlloc>(
+ new IPMMAlloc(*this, std::move(BL), std::move(StandardSegs),
+ std::move(FinalizeSegs))));
+ }
+
+ void deallocate(std::vector<FinalizedAlloc> FinalizedAllocs,
+ OnDeallocatedFunction OnDeallocated) override {
+ Error Err = Error::success();
+ for (auto &FA : FinalizedAllocs) {
+ std::unique_ptr<FinalizedAllocInfo> FAI(
+ FA.release().toPtr<FinalizedAllocInfo *>());
+
+ // FIXME: Run dealloc actions.
+
+ Err = joinErrors(std::move(Err), freeBlock(FAI->Mem));
+ }
+ OnDeallocated(std::move(Err));
+ }
+
+private:
+ JITLinkSlabAllocator(uint64_t SlabSize, Error &Err) {
+ ErrorAsOutParameter _(&Err);
+
+ if (!SlabPageSize) {
+ if (auto PageSizeOrErr = sys::Process::getPageSize())
+ PageSize = *PageSizeOrErr;
+ else {
+ Err = PageSizeOrErr.takeError();
+ return;
+ }
+
+ if (PageSize == 0) {
+ Err = make_error<StringError>("Page size is zero",
+ inconvertibleErrorCode());
+ return;
+ }
+ } else
+ PageSize = SlabPageSize;
+
+ if (!isPowerOf2_64(PageSize)) {
+ Err = make_error<StringError>("Page size is not a power of 2",
+ inconvertibleErrorCode());
+ return;
+ }
+
+ // Round slab request up to page size.
+ SlabSize = (SlabSize + PageSize - 1) & ~(PageSize - 1);
+
+ const sys::Memory::ProtectionFlags ReadWrite =
+ static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
+ sys::Memory::MF_WRITE);
+
+ std::error_code EC;
+ SlabRemaining =
+ sys::Memory::allocateMappedMemory(SlabSize, nullptr, ReadWrite, EC);
+
+ if (EC) {
+ Err = errorCodeToError(EC);
+ return;
+ }
+
+ // Calculate the target address delta to link as-if slab were at
+ // SlabAddress.
+ if (SlabAddress != ~0ULL)
+ NextSlabDelta = ExecutorAddr(SlabAddress) -
+ ExecutorAddr::fromPtr(SlabRemaining.base());
+ }
+
+ Error freeBlock(sys::MemoryBlock MB) {
+ // FIXME: Return memory to slab.
+ return Error::success();
+ }
+
+ std::mutex SlabMutex;
+ sys::MemoryBlock SlabRemaining;
+ uint64_t PageSize = 0;
+ int64_t NextSlabDelta = 0;
+};
+
+Expected<uint64_t> getSlabAllocSize(StringRef SizeString) {
+ SizeString = SizeString.trim();
+
+ uint64_t Units = 1024;
+
+ if (SizeString.endswith_insensitive("kb"))
+ SizeString = SizeString.drop_back(2).rtrim();
+ else if (SizeString.endswith_insensitive("mb")) {
+ Units = 1024 * 1024;
+ SizeString = SizeString.drop_back(2).rtrim();
+ } else if (SizeString.endswith_insensitive("gb")) {
+ Units = 1024 * 1024 * 1024;
+ SizeString = SizeString.drop_back(2).rtrim();
+ }
+
+ uint64_t SlabSize = 0;
+ if (SizeString.getAsInteger(10, SlabSize))
+ return make_error<StringError>("Invalid numeric format for slab size",
+ inconvertibleErrorCode());
+
+ return SlabSize * Units;
+}
+
+static std::unique_ptr<JITLinkMemoryManager> createMemoryManager() {
+ if (!SlabAllocateSizeString.empty()) {
+ auto SlabSize = ExitOnErr(getSlabAllocSize(SlabAllocateSizeString));
+ return ExitOnErr(JITLinkSlabAllocator::Create(SlabSize));
+ }
+ return ExitOnErr(InProcessMemoryManager::Create());
+}
+
+static Expected<MaterializationUnit::Interface>
+getTestObjectFileInterface(Session &S, MemoryBufferRef O) {
+
+ // Get the standard interface for this object, but ignore the symbols field.
+ // We'll handle that manually to include promotion.
+ auto I = getObjectFileInterface(S.ES, O);
+ if (!I)
+ return I.takeError();
+ I->SymbolFlags.clear();
+
+ // If creating an object file was going to fail it would have happened above,
+ // so we can 'cantFail' this.
+ auto Obj = cantFail(object::ObjectFile::createObjectFile(O));
+
+ // The init symbol must be included in the SymbolFlags map if present.
+ if (I->InitSymbol)
+ I->SymbolFlags[I->InitSymbol] =
+ JITSymbolFlags::MaterializationSideEffectsOnly;
+
+ for (auto &Sym : Obj->symbols()) {
+ Expected<uint32_t> SymFlagsOrErr = Sym.getFlags();
+ if (!SymFlagsOrErr)
+ // TODO: Test this error.
+ return SymFlagsOrErr.takeError();
+
+ // Skip symbols not defined in this object file.
+ if ((*SymFlagsOrErr & object::BasicSymbolRef::SF_Undefined))
+ continue;
+
+ auto Name = Sym.getName();
+ if (!Name)
+ return Name.takeError();
+
+ // Skip symbols that have type SF_File.
+ if (auto SymType = Sym.getType()) {
+ if (*SymType == object::SymbolRef::ST_File)
+ continue;
+ } else
+ return SymType.takeError();
+
+ auto SymFlags = JITSymbolFlags::fromObjectSymbol(Sym);
+ if (!SymFlags)
+ return SymFlags.takeError();
+
+ if (SymFlags->isWeak()) {
+ // If this is a weak symbol that's not defined in the harness then we
+ // need to either mark it as strong (if this is the first definition
+ // that we've seen) or discard it.
+ if (S.HarnessDefinitions.count(*Name) || S.CanonicalWeakDefs.count(*Name))
+ continue;
+ S.CanonicalWeakDefs[*Name] = O.getBufferIdentifier();
+ *SymFlags &= ~JITSymbolFlags::Weak;
+ if (!S.HarnessExternals.count(*Name))
+ *SymFlags &= ~JITSymbolFlags::Exported;
+ } else if (S.HarnessExternals.count(*Name)) {
+ *SymFlags |= JITSymbolFlags::Exported;
+ } else if (S.HarnessDefinitions.count(*Name) ||
+ !(*SymFlagsOrErr & object::BasicSymbolRef::SF_Global))
+ continue;
+
+ auto InternedName = S.ES.intern(*Name);
+ I->SymbolFlags[InternedName] = std::move(*SymFlags);
+ }
+
+ return I;
+}
+
+static Error loadProcessSymbols(Session &S) {
+ auto FilterMainEntryPoint =
+ [EPName = S.ES.intern(EntryPointName)](SymbolStringPtr Name) {
+ return Name != EPName;
+ };
+ S.MainJD->addGenerator(
+ ExitOnErr(orc::EPCDynamicLibrarySearchGenerator::GetForTargetProcess(
+ S.ES, std::move(FilterMainEntryPoint))));
+
+ return Error::success();
+}
+
+static Error loadDylibs(Session &S) {
+ LLVM_DEBUG(dbgs() << "Loading dylibs...\n");
+ for (const auto &Dylib : Dylibs) {
+ LLVM_DEBUG(dbgs() << " " << Dylib << "\n");
+ auto G = orc::EPCDynamicLibrarySearchGenerator::Load(S.ES, Dylib.c_str());
+ if (!G)
+ return G.takeError();
+ S.MainJD->addGenerator(std::move(*G));
+ }
+
+ return Error::success();
+}
+
+static Expected<std::unique_ptr<ExecutorProcessControl>> launchExecutor() {
+#ifndef LLVM_ON_UNIX
+ // FIXME: Add support for Windows.
+ return make_error<StringError>("-" + OutOfProcessExecutor.ArgStr +
+ " not supported on non-unix platforms",
+ inconvertibleErrorCode());
+#elif !LLVM_ENABLE_THREADS
+ // Out of process mode using SimpleRemoteEPC depends on threads.
+ return make_error<StringError>(
+ "-" + OutOfProcessExecutor.ArgStr +
+ " requires threads, but LLVM was built with "
+ "LLVM_ENABLE_THREADS=Off",
+ inconvertibleErrorCode());
+#else
+
+ constexpr int ReadEnd = 0;
+ constexpr int WriteEnd = 1;
+
+ // Pipe FDs.
+ int ToExecutor[2];
+ int FromExecutor[2];
+
+ pid_t ChildPID;
+
+ // Create pipes to/from the executor..
+ if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
+ return make_error<StringError>("Unable to create pipe for executor",
+ inconvertibleErrorCode());
+
+ ChildPID = fork();
+
+ if (ChildPID == 0) {
+ // In the child...
+
+ // Close the parent ends of the pipes
+ close(ToExecutor[WriteEnd]);
+ close(FromExecutor[ReadEnd]);
+
+ // Execute the child process.
+ std::unique_ptr<char[]> ExecutorPath, FDSpecifier;
+ {
+ ExecutorPath = std::make_unique<char[]>(OutOfProcessExecutor.size() + 1);
+ strcpy(ExecutorPath.get(), OutOfProcessExecutor.data());
+
+ std::string FDSpecifierStr("filedescs=");
+ FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
+ FDSpecifierStr += ',';
+ FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
+ FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
+ strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
+ }
+
+ char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr};
+ int RC = execvp(ExecutorPath.get(), Args);
+ if (RC != 0) {
+ errs() << "unable to launch out-of-process executor \""
+ << ExecutorPath.get() << "\"\n";
+ exit(1);
+ }
+ }
+ // else we're the parent...
+
+ // Close the child ends of the pipes
+ close(ToExecutor[ReadEnd]);
+ close(FromExecutor[WriteEnd]);
+
+ return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
+ std::make_unique<DynamicThreadPoolTaskDispatcher>(),
+ SimpleRemoteEPC::Setup(), FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
+#endif
+}
+
+#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
+static Error createTCPSocketError(Twine Details) {
+ return make_error<StringError>(
+ formatv("Failed to connect TCP socket '{0}': {1}",
+ OutOfProcessExecutorConnect, Details),
+ inconvertibleErrorCode());
+}
+
+static Expected<int> connectTCPSocket(std::string Host, std::string PortStr) {
+ addrinfo *AI;
+ addrinfo Hints{};
+ Hints.ai_family = AF_INET;
+ Hints.ai_socktype = SOCK_STREAM;
+ Hints.ai_flags = AI_NUMERICSERV;
+
+ if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
+ return createTCPSocketError("Address resolution failed (" +
+ StringRef(gai_strerror(EC)) + ")");
+
+ // Cycle through the returned addrinfo structures and connect to the first
+ // reachable endpoint.
+ int SockFD;
+ addrinfo *Server;
+ for (Server = AI; Server != nullptr; Server = Server->ai_next) {
+ // socket might fail, e.g. if the address family is not supported. Skip to
+ // the next addrinfo structure in such a case.
+ if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
+ continue;
+
+ // If connect returns null, we exit the loop with a working socket.
+ if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
+ break;
+
+ close(SockFD);
+ }
+ freeaddrinfo(AI);
+
+ // If we reached the end of the loop without connecting to a valid endpoint,
+ // dump the last error that was logged in socket() or connect().
+ if (Server == nullptr)
+ return createTCPSocketError(std::strerror(errno));
+
+ return SockFD;
+}
+#endif
+
+static Expected<std::unique_ptr<ExecutorProcessControl>> connectToExecutor() {
+#ifndef LLVM_ON_UNIX
+ // FIXME: Add TCP support for Windows.
+ return make_error<StringError>("-" + OutOfProcessExecutorConnect.ArgStr +
+ " not supported on non-unix platforms",
+ inconvertibleErrorCode());
+#elif !LLVM_ENABLE_THREADS
+ // Out of process mode using SimpleRemoteEPC depends on threads.
+ return make_error<StringError>(
+ "-" + OutOfProcessExecutorConnect.ArgStr +
+ " requires threads, but LLVM was built with "
+ "LLVM_ENABLE_THREADS=Off",
+ inconvertibleErrorCode());
+#else
+
+ StringRef Host, PortStr;
+ std::tie(Host, PortStr) = StringRef(OutOfProcessExecutorConnect).split(':');
+ if (Host.empty())
+ return createTCPSocketError("Host name for -" +
+ OutOfProcessExecutorConnect.ArgStr +
+ " can not be empty");
+ if (PortStr.empty())
+ return createTCPSocketError("Port number in -" +
+ OutOfProcessExecutorConnect.ArgStr +
+ " can not be empty");
+ int Port = 0;
+ if (PortStr.getAsInteger(10, Port))
+ return createTCPSocketError("Port number '" + PortStr +
+ "' is not a valid integer");
+
+ Expected<int> SockFD = connectTCPSocket(Host.str(), PortStr.str());
+ if (!SockFD)
+ return SockFD.takeError();
+
+ return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
+ std::make_unique<DynamicThreadPoolTaskDispatcher>(),
+ SimpleRemoteEPC::Setup(), *SockFD, *SockFD);
+#endif
+}
+
+class PhonyExternalsGenerator : public DefinitionGenerator {
+public:
+ Error tryToGenerate(LookupState &LS, LookupKind K, JITDylib &JD,
+ JITDylibLookupFlags JDLookupFlags,
+ const SymbolLookupSet &LookupSet) override {
+ SymbolMap PhonySymbols;
+ for (auto &KV : LookupSet)
+ PhonySymbols[KV.first] = JITEvaluatedSymbol(0, JITSymbolFlags::Exported);
+ return JD.define(absoluteSymbols(std::move(PhonySymbols)));
+ }
+};
+
+Expected<std::unique_ptr<Session>> Session::Create(Triple TT) {
+
+ std::unique_ptr<ExecutorProcessControl> EPC;
+ if (OutOfProcessExecutor.getNumOccurrences()) {
+ /// If -oop-executor is passed then launch the executor.
+ if (auto REPC = launchExecutor())
+ EPC = std::move(*REPC);
+ else
+ return REPC.takeError();
+ } else if (OutOfProcessExecutorConnect.getNumOccurrences()) {
+ /// If -oop-executor-connect is passed then connect to the executor.
+ if (auto REPC = connectToExecutor())
+ EPC = std::move(*REPC);
+ else
+ return REPC.takeError();
+ } else {
+ /// Otherwise use SelfExecutorProcessControl to target the current process.
+ auto PageSize = sys::Process::getPageSize();
+ if (!PageSize)
+ return PageSize.takeError();
+ EPC = std::make_unique<SelfExecutorProcessControl>(
+ std::make_shared<SymbolStringPool>(),
+ std::make_unique<InPlaceTaskDispatcher>(), std::move(TT), *PageSize,
+ createMemoryManager());
+ }
+
+ Error Err = Error::success();
+ std::unique_ptr<Session> S(new Session(std::move(EPC), Err));
+ if (Err)
+ return std::move(Err);
+ return std::move(S);
+}
+
+Session::~Session() {
+ if (auto Err = ES.endSession())
+ ES.reportError(std::move(Err));
+}
+
+Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)
+ : ES(std::move(EPC)),
+ ObjLayer(ES, ES.getExecutorProcessControl().getMemMgr()) {
+
+ /// Local ObjectLinkingLayer::Plugin class to forward modifyPassConfig to the
+ /// Session.
+ class JITLinkSessionPlugin : public ObjectLinkingLayer::Plugin {
+ public:
+ JITLinkSessionPlugin(Session &S) : S(S) {}
+ void modifyPassConfig(MaterializationResponsibility &MR, LinkGraph &G,
+ PassConfiguration &PassConfig) override {
+ S.modifyPassConfig(G.getTargetTriple(), PassConfig);
+ }
+
+ Error notifyFailed(MaterializationResponsibility &MR) override {
+ return Error::success();
+ }
+ Error notifyRemovingResources(ResourceKey K) override {
+ return Error::success();
+ }
+ void notifyTransferringResources(ResourceKey DstKey,
+ ResourceKey SrcKey) override {}
+
+ private:
+ Session &S;
+ };
+
+ ErrorAsOutParameter _(&Err);
+
+ if (auto MainJDOrErr = ES.createJITDylib("main"))
+ MainJD = &*MainJDOrErr;
+ else {
+ Err = MainJDOrErr.takeError();
+ return;
+ }
+
+ if (!NoProcessSymbols)
+ ExitOnErr(loadProcessSymbols(*this));
+ ExitOnErr(loadDylibs(*this));
+
+ auto &TT = ES.getExecutorProcessControl().getTargetTriple();
+
+ if (DebuggerSupport && TT.isOSBinFormatMachO())
+ ObjLayer.addPlugin(ExitOnErr(
+ GDBJITDebugInfoRegistrationPlugin::Create(this->ES, *MainJD, TT)));
+
+ // Set up the platform.
+ if (TT.isOSBinFormatMachO() && !OrcRuntime.empty()) {
+ if (auto P =
+ MachOPlatform::Create(ES, ObjLayer, *MainJD, OrcRuntime.c_str()))
+ ES.setPlatform(std::move(*P));
+ else {
+ Err = P.takeError();
+ return;
+ }
+ } else if (TT.isOSBinFormatELF() && !OrcRuntime.empty()) {
+ if (auto P =
+ ELFNixPlatform::Create(ES, ObjLayer, *MainJD, OrcRuntime.c_str()))
+ ES.setPlatform(std::move(*P));
+ else {
+ Err = P.takeError();
+ return;
+ }
+ } else if (!TT.isOSWindows() && !TT.isOSBinFormatMachO()) {
+ if (!NoExec)
+ ObjLayer.addPlugin(std::make_unique<EHFrameRegistrationPlugin>(
+ ES, ExitOnErr(EPCEHFrameRegistrar::Create(this->ES))));
+ if (DebuggerSupport)
+ ObjLayer.addPlugin(std::make_unique<DebugObjectManagerPlugin>(
+ ES, ExitOnErr(createJITLoaderGDBRegistrar(this->ES))));
+ }
+
+ ObjLayer.addPlugin(std::make_unique<JITLinkSessionPlugin>(*this));
+
+ // Process any harness files.
+ for (auto &HarnessFile : TestHarnesses) {
+ HarnessFiles.insert(HarnessFile);
+
+ auto ObjBuffer =
+ ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(HarnessFile)));
+
+ auto ObjInterface =
+ ExitOnErr(getObjectFileInterface(ES, ObjBuffer->getMemBufferRef()));
+
+ for (auto &KV : ObjInterface.SymbolFlags)
+ HarnessDefinitions.insert(*KV.first);
+
+ auto Obj = ExitOnErr(
+ object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef()));
+
+ for (auto &Sym : Obj->symbols()) {
+ uint32_t SymFlags = ExitOnErr(Sym.getFlags());
+ auto Name = ExitOnErr(Sym.getName());
+
+ if (Name.empty())
+ continue;
+
+ if (SymFlags & object::BasicSymbolRef::SF_Undefined)
+ HarnessExternals.insert(Name);
+ }
+ }
+
+ // If a name is defined by some harness file then it's a definition, not an
+ // external.
+ for (auto &DefName : HarnessDefinitions)
+ HarnessExternals.erase(DefName.getKey());
+}
+
+void Session::dumpSessionInfo(raw_ostream &OS) {
+ OS << "Registered addresses:\n" << SymbolInfos << FileInfos;
+}
+
+void Session::modifyPassConfig(const Triple &TT,
+ PassConfiguration &PassConfig) {
+ if (!CheckFiles.empty())
+ PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) {
+ auto &EPC = ES.getExecutorProcessControl();
+ if (EPC.getTargetTriple().getObjectFormat() == Triple::ELF)
+ return registerELFGraphInfo(*this, G);
+
+ if (EPC.getTargetTriple().getObjectFormat() == Triple::MachO)
+ return registerMachOGraphInfo(*this, G);
+
+ return make_error<StringError>("Unsupported object format for GOT/stub "
+ "registration",
+ inconvertibleErrorCode());
+ });
+
+ if (ShowLinkGraph)
+ PassConfig.PostFixupPasses.push_back([](LinkGraph &G) -> Error {
+ outs() << "Link graph \"" << G.getName() << "\" post-fixup:\n";
+ G.dump(outs());
+ return Error::success();
+ });
+
+ PassConfig.PrePrunePasses.push_back(
+ [this](LinkGraph &G) { return applyHarnessPromotions(*this, G); });
+
+ if (ShowSizes) {
+ PassConfig.PrePrunePasses.push_back([this](LinkGraph &G) -> Error {
+ SizeBeforePruning += computeTotalBlockSizes(G);
+ return Error::success();
+ });
+ PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) -> Error {
+ SizeAfterFixups += computeTotalBlockSizes(G);
+ return Error::success();
+ });
+ }
+
+ if (ShowRelocatedSectionContents)
+ PassConfig.PostFixupPasses.push_back([](LinkGraph &G) -> Error {
+ outs() << "Relocated section contents for " << G.getName() << ":\n";
+ dumpSectionContents(outs(), G);
+ return Error::success();
+ });
+
+ if (AddSelfRelocations)
+ PassConfig.PostPrunePasses.push_back(addSelfRelocations);
+}
+
+Expected<Session::FileInfo &> Session::findFileInfo(StringRef FileName) {
+ auto FileInfoItr = FileInfos.find(FileName);
+ if (FileInfoItr == FileInfos.end())
+ return make_error<StringError>("file \"" + FileName + "\" not recognized",
+ inconvertibleErrorCode());
+ return FileInfoItr->second;
+}
+
+Expected<Session::MemoryRegionInfo &>
+Session::findSectionInfo(StringRef FileName, StringRef SectionName) {
+ auto FI = findFileInfo(FileName);
+ if (!FI)
+ return FI.takeError();
+ auto SecInfoItr = FI->SectionInfos.find(SectionName);
+ if (SecInfoItr == FI->SectionInfos.end())
+ return make_error<StringError>("no section \"" + SectionName +
+ "\" registered for file \"" + FileName +
+ "\"",
+ inconvertibleErrorCode());
+ return SecInfoItr->second;
+}
+
+Expected<Session::MemoryRegionInfo &>
+Session::findStubInfo(StringRef FileName, StringRef TargetName) {
+ auto FI = findFileInfo(FileName);
+ if (!FI)
+ return FI.takeError();
+ auto StubInfoItr = FI->StubInfos.find(TargetName);
+ if (StubInfoItr == FI->StubInfos.end())
+ return make_error<StringError>("no stub for \"" + TargetName +
+ "\" registered for file \"" + FileName +
+ "\"",
+ inconvertibleErrorCode());
+ return StubInfoItr->second;
+}
+
+Expected<Session::MemoryRegionInfo &>
+Session::findGOTEntryInfo(StringRef FileName, StringRef TargetName) {
+ auto FI = findFileInfo(FileName);
+ if (!FI)
+ return FI.takeError();
+ auto GOTInfoItr = FI->GOTEntryInfos.find(TargetName);
+ if (GOTInfoItr == FI->GOTEntryInfos.end())
+ return make_error<StringError>("no GOT entry for \"" + TargetName +
+ "\" registered for file \"" + FileName +
+ "\"",
+ inconvertibleErrorCode());
+ return GOTInfoItr->second;
+}
+
+bool Session::isSymbolRegistered(StringRef SymbolName) {
+ return SymbolInfos.count(SymbolName);
+}
+
+Expected<Session::MemoryRegionInfo &>
+Session::findSymbolInfo(StringRef SymbolName, Twine ErrorMsgStem) {
+ auto SymInfoItr = SymbolInfos.find(SymbolName);
+ if (SymInfoItr == SymbolInfos.end())
+ return make_error<StringError>(ErrorMsgStem + ": symbol " + SymbolName +
+ " not found",
+ inconvertibleErrorCode());
+ return SymInfoItr->second;
+}
+
+} // end namespace llvm
+
+static Triple getFirstFileTriple() {
+ static Triple FirstTT = []() {
+ assert(!InputFiles.empty() && "InputFiles can not be empty");
+ for (auto InputFile : InputFiles) {
+ auto ObjBuffer =
+ ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFile)));
+ switch (identify_magic(ObjBuffer->getBuffer())) {
+ case file_magic::elf_relocatable:
+ case file_magic::macho_object:
+ case file_magic::coff_object: {
+ auto Obj = ExitOnErr(
+ object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef()));
+ return Obj->makeTriple();
+ }
+ default:
+ break;
+ }
+ }
+ return Triple();
+ }();
+
+ return FirstTT;
+}
+
+static Error sanitizeArguments(const Triple &TT, const char *ArgV0) {
+
+ // -noexec and --args should not be used together.
+ if (NoExec && !InputArgv.empty())
+ errs() << "Warning: --args passed to -noexec run will be ignored.\n";
+
+ // Set the entry point name if not specified.
+ if (EntryPointName.empty())
+ EntryPointName = TT.getObjectFormat() == Triple::MachO ? "_main" : "main";
+
+ // Disable debugger support by default in noexec tests.
+ if (DebuggerSupport.getNumOccurrences() == 0 && NoExec)
+ DebuggerSupport = false;
+
+ // If -slab-allocate is passed, check that we're not trying to use it in
+ // -oop-executor or -oop-executor-connect mode.
+ //
+ // FIXME: Remove once we enable remote slab allocation.
+ if (SlabAllocateSizeString != "") {
+ if (OutOfProcessExecutor.getNumOccurrences() ||
+ OutOfProcessExecutorConnect.getNumOccurrences())
+ return make_error<StringError>(
+ "-slab-allocate cannot be used with -oop-executor or "
+ "-oop-executor-connect",
+ inconvertibleErrorCode());
+ }
+
+ // If -slab-address is passed, require -slab-allocate and -noexec
+ if (SlabAddress != ~0ULL) {
+ if (SlabAllocateSizeString == "" || !NoExec)
+ return make_error<StringError>(
+ "-slab-address requires -slab-allocate and -noexec",
+ inconvertibleErrorCode());
+
+ if (SlabPageSize == 0)
+ errs() << "Warning: -slab-address used without -slab-page-size.\n";
+ }
+
+ if (SlabPageSize != 0) {
+ // -slab-page-size requires slab alloc.
+ if (SlabAllocateSizeString == "")
+ return make_error<StringError>("-slab-page-size requires -slab-allocate",
+ inconvertibleErrorCode());
+
+ // Check -slab-page-size / -noexec interactions.
+ if (!NoExec) {
+ if (auto RealPageSize = sys::Process::getPageSize()) {
+ if (SlabPageSize % *RealPageSize)
+ return make_error<StringError>(
+ "-slab-page-size must be a multiple of real page size for exec "
+ "tests (did you mean to use -noexec ?)\n",
+ inconvertibleErrorCode());
+ } else {
+ errs() << "Could not retrieve process page size:\n";
+ logAllUnhandledErrors(RealPageSize.takeError(), errs(), "");
+ errs() << "Executing with slab page size = "
+ << formatv("{0:x}", SlabPageSize) << ".\n"
+ << "Tool may crash if " << formatv("{0:x}", SlabPageSize)
+ << " is not a multiple of the real process page size.\n"
+ << "(did you mean to use -noexec ?)";
+ }
+ }
+ }
+
+ // Only one of -oop-executor and -oop-executor-connect can be used.
+ if (!!OutOfProcessExecutor.getNumOccurrences() &&
+ !!OutOfProcessExecutorConnect.getNumOccurrences())
+ return make_error<StringError>(
+ "Only one of -" + OutOfProcessExecutor.ArgStr + " and -" +
+ OutOfProcessExecutorConnect.ArgStr + " can be specified",
+ inconvertibleErrorCode());
+
+ // If -oop-executor was used but no value was specified then use a sensible
+ // default.
+ if (!!OutOfProcessExecutor.getNumOccurrences() &&
+ OutOfProcessExecutor.empty()) {
+ SmallString<256> OOPExecutorPath(sys::fs::getMainExecutable(
+ ArgV0, reinterpret_cast<void *>(&sanitizeArguments)));
+ sys::path::remove_filename(OOPExecutorPath);
+ sys::path::append(OOPExecutorPath, "llvm-jitlink-executor");
+ OutOfProcessExecutor = OOPExecutorPath.str().str();
+ }
+
+ return Error::success();
+}
+
+static void addPhonyExternalsGenerator(Session &S) {
+ S.MainJD->addGenerator(std::make_unique<PhonyExternalsGenerator>());
+}
+
+static Error createJITDylibs(Session &S,
+ std::map<unsigned, JITDylib *> &IdxToJD) {
+ // First, set up JITDylibs.
+ LLVM_DEBUG(dbgs() << "Creating JITDylibs...\n");
+ {
+ // Create a "main" JITLinkDylib.
+ IdxToJD[0] = S.MainJD;
+ S.JDSearchOrder.push_back({S.MainJD, JITDylibLookupFlags::MatchAllSymbols});
+ LLVM_DEBUG(dbgs() << " 0: " << S.MainJD->getName() << "\n");
+
+ // Add any extra JITDylibs from the command line.
+ for (auto JDItr = JITDylibs.begin(), JDEnd = JITDylibs.end();
+ JDItr != JDEnd; ++JDItr) {
+ auto JD = S.ES.createJITDylib(*JDItr);
+ if (!JD)
+ return JD.takeError();
+ unsigned JDIdx = JITDylibs.getPosition(JDItr - JITDylibs.begin());
+ IdxToJD[JDIdx] = &*JD;
+ S.JDSearchOrder.push_back({&*JD, JITDylibLookupFlags::MatchAllSymbols});
+ LLVM_DEBUG(dbgs() << " " << JDIdx << ": " << JD->getName() << "\n");
+ }
+ }
+
+ LLVM_DEBUG({
+ dbgs() << "Dylib search order is [ ";
+ for (auto &KV : S.JDSearchOrder)
+ dbgs() << KV.first->getName() << " ";
+ dbgs() << "]\n";
+ });
+
+ return Error::success();
+}
+
+static Error addAbsoluteSymbols(Session &S,
+ const std::map<unsigned, JITDylib *> &IdxToJD) {
+ // Define absolute symbols.
+ LLVM_DEBUG(dbgs() << "Defining absolute symbols...\n");
+ for (auto AbsDefItr = AbsoluteDefs.begin(), AbsDefEnd = AbsoluteDefs.end();
+ AbsDefItr != AbsDefEnd; ++AbsDefItr) {
+ unsigned AbsDefArgIdx =
+ AbsoluteDefs.getPosition(AbsDefItr - AbsoluteDefs.begin());
+ auto &JD = *std::prev(IdxToJD.lower_bound(AbsDefArgIdx))->second;
+
+ StringRef AbsDefStmt = *AbsDefItr;
+ size_t EqIdx = AbsDefStmt.find_first_of('=');
+ if (EqIdx == StringRef::npos)
+ return make_error<StringError>("Invalid absolute define \"" + AbsDefStmt +
+ "\". Syntax: <name>=<addr>",
+ inconvertibleErrorCode());
+ StringRef Name = AbsDefStmt.substr(0, EqIdx).trim();
+ StringRef AddrStr = AbsDefStmt.substr(EqIdx + 1).trim();
+
+ uint64_t Addr;
+ if (AddrStr.getAsInteger(0, Addr))
+ return make_error<StringError>("Invalid address expression \"" + AddrStr +
+ "\" in absolute define \"" + AbsDefStmt +
+ "\"",
+ inconvertibleErrorCode());
+ JITEvaluatedSymbol AbsDef(Addr, JITSymbolFlags::Exported);
+ if (auto Err = JD.define(absoluteSymbols({{S.ES.intern(Name), AbsDef}})))
+ return Err;
+
+ // Register the absolute symbol with the session symbol infos.
+ S.SymbolInfos[Name] = {ArrayRef<char>(), Addr};
+ }
+
+ return Error::success();
+}
+
+static Error addTestHarnesses(Session &S) {
+ LLVM_DEBUG(dbgs() << "Adding test harness objects...\n");
+ for (auto HarnessFile : TestHarnesses) {
+ LLVM_DEBUG(dbgs() << " " << HarnessFile << "\n");
+ auto ObjBuffer = errorOrToExpected(MemoryBuffer::getFile(HarnessFile));
+ if (!ObjBuffer)
+ return ObjBuffer.takeError();
+ if (auto Err = S.ObjLayer.add(*S.MainJD, std::move(*ObjBuffer)))
+ return Err;
+ }
+ return Error::success();
+}
+
+static Error addObjects(Session &S,
+ const std::map<unsigned, JITDylib *> &IdxToJD) {
+
+ // Load each object into the corresponding JITDylib..
+ LLVM_DEBUG(dbgs() << "Adding objects...\n");
+ for (auto InputFileItr = InputFiles.begin(), InputFileEnd = InputFiles.end();
+ InputFileItr != InputFileEnd; ++InputFileItr) {
+ unsigned InputFileArgIdx =
+ InputFiles.getPosition(InputFileItr - InputFiles.begin());
+ const std::string &InputFile = *InputFileItr;
+ if (StringRef(InputFile).endswith(".a"))
+ continue;
+ auto &JD = *std::prev(IdxToJD.lower_bound(InputFileArgIdx))->second;
+ LLVM_DEBUG(dbgs() << " " << InputFileArgIdx << ": \"" << InputFile
+ << "\" to " << JD.getName() << "\n";);
+ auto ObjBuffer = errorOrToExpected(MemoryBuffer::getFile(InputFile));
+ if (!ObjBuffer)
+ return ObjBuffer.takeError();
+
+ if (S.HarnessFiles.empty()) {
+ if (auto Err = S.ObjLayer.add(JD, std::move(*ObjBuffer)))
+ return Err;
+ } else {
+ // We're in -harness mode. Use a custom interface for this
+ // test object.
+ auto ObjInterface =
+ getTestObjectFileInterface(S, (*ObjBuffer)->getMemBufferRef());
+ if (!ObjInterface)
+ return ObjInterface.takeError();
+ if (auto Err = S.ObjLayer.add(JD, std::move(*ObjBuffer),
+ std::move(*ObjInterface)))
+ return Err;
+ }
+ }
+
+ return Error::success();
+}
+
+static Expected<MaterializationUnit::Interface>
+getObjectFileInterfaceHidden(ExecutionSession &ES, MemoryBufferRef ObjBuffer) {
+ auto I = getObjectFileInterface(ES, ObjBuffer);
+ if (I) {
+ for (auto &KV : I->SymbolFlags)
+ KV.second &= ~JITSymbolFlags::Exported;
+ }
+ return I;
+}
+
+static Error addLibraries(Session &S,
+ const std::map<unsigned, JITDylib *> &IdxToJD) {
+
+ // 1. Collect search paths for each JITDylib.
+ DenseMap<const JITDylib *, SmallVector<StringRef, 2>> JDSearchPaths;
+
+ for (auto LSPItr = LibrarySearchPaths.begin(),
+ LSPEnd = LibrarySearchPaths.end();
+ LSPItr != LSPEnd; ++LSPItr) {
+ unsigned LibrarySearchPathIdx =
+ LibrarySearchPaths.getPosition(LSPItr - LibrarySearchPaths.begin());
+ auto &JD = *std::prev(IdxToJD.lower_bound(LibrarySearchPathIdx))->second;
+
+ StringRef LibrarySearchPath = *LSPItr;
+ if (sys::fs::get_file_type(LibrarySearchPath) !=
+ sys::fs::file_type::directory_file)
+ return make_error<StringError>("While linking " + JD.getName() + ", -L" +
+ LibrarySearchPath +
+ " does not point to a directory",
+ inconvertibleErrorCode());
+
+ JDSearchPaths[&JD].push_back(*LSPItr);
+ }
+
+ LLVM_DEBUG({
+ if (!JDSearchPaths.empty())
+ dbgs() << "Search paths:\n";
+ for (auto &KV : JDSearchPaths) {
+ dbgs() << " " << KV.first->getName() << ": [";
+ for (auto &LibSearchPath : KV.second)
+ dbgs() << " \"" << LibSearchPath << "\"";
+ dbgs() << " ]\n";
+ }
+ });
+
+ // 2. Collect library loads
+ struct LibraryLoad {
+ StringRef LibName;
+ bool IsPath = false;
+ unsigned Position;
+ StringRef *CandidateExtensions;
+ enum { Standard, Hidden } Modifier;
+ };
+ std::vector<LibraryLoad> LibraryLoads;
+ // Add archive files from the inputs to LibraryLoads.
+ for (auto InputFileItr = InputFiles.begin(), InputFileEnd = InputFiles.end();
+ InputFileItr != InputFileEnd; ++InputFileItr) {
+ StringRef InputFile = *InputFileItr;
+ if (!InputFile.endswith(".a"))
+ continue;
+ LibraryLoad LL;
+ LL.LibName = InputFile;
+ LL.IsPath = true;
+ LL.Position = InputFiles.getPosition(InputFileItr - InputFiles.begin());
+ LL.CandidateExtensions = nullptr;
+ LL.Modifier = LibraryLoad::Standard;
+ LibraryLoads.push_back(std::move(LL));
+ }
+
+ // Add -load_hidden arguments to LibraryLoads.
+ for (auto LibItr = LoadHidden.begin(), LibEnd = LoadHidden.end();
+ LibItr != LibEnd; ++LibItr) {
+ LibraryLoad LL;
+ LL.LibName = *LibItr;
+ LL.IsPath = true;
+ LL.Position = LoadHidden.getPosition(LibItr - LoadHidden.begin());
+ LL.CandidateExtensions = nullptr;
+ LL.Modifier = LibraryLoad::Hidden;
+ LibraryLoads.push_back(std::move(LL));
+ }
+ StringRef StandardExtensions[] = {".so", ".dylib", ".a"};
+ StringRef ArchiveExtensionsOnly[] = {".a"};
+
+ // Add -lx arguments to LibraryLoads.
+ for (auto LibItr = Libraries.begin(), LibEnd = Libraries.end();
+ LibItr != LibEnd; ++LibItr) {
+ LibraryLoad LL;
+ LL.LibName = *LibItr;
+ LL.Position = Libraries.getPosition(LibItr - Libraries.begin());
+ LL.CandidateExtensions = StandardExtensions;
+ LL.Modifier = LibraryLoad::Standard;
+ LibraryLoads.push_back(std::move(LL));
+ }
+
+ // Add -hidden-lx arguments to LibraryLoads.
+ for (auto LibHiddenItr = LibrariesHidden.begin(),
+ LibHiddenEnd = LibrariesHidden.end();
+ LibHiddenItr != LibHiddenEnd; ++LibHiddenItr) {
+ LibraryLoad LL;
+ LL.LibName = *LibHiddenItr;
+ LL.Position =
+ LibrariesHidden.getPosition(LibHiddenItr - LibrariesHidden.begin());
+ LL.CandidateExtensions = ArchiveExtensionsOnly;
+ LL.Modifier = LibraryLoad::Hidden;
+ LibraryLoads.push_back(std::move(LL));
+ }
+
+ // If there are any load-<modified> options then turn on flag overrides
+ // to avoid flag mismatch errors.
+ if (!LibrariesHidden.empty() || !LoadHidden.empty())
+ S.ObjLayer.setOverrideObjectFlagsWithResponsibilityFlags(true);
+
+ // Sort library loads by position in the argument list.
+ llvm::sort(LibraryLoads, [](const LibraryLoad &LHS, const LibraryLoad &RHS) {
+ return LHS.Position < RHS.Position;
+ });
+
+ // 3. Process library loads.
+ auto AddArchive = [&](const char *Path, const LibraryLoad &LL)
+ -> Expected<std::unique_ptr<StaticLibraryDefinitionGenerator>> {
+ unique_function<Expected<MaterializationUnit::Interface>(
+ ExecutionSession & ES, MemoryBufferRef ObjBuffer)>
+ GetObjFileInterface;
+ switch (LL.Modifier) {
+ case LibraryLoad::Standard:
+ GetObjFileInterface = getObjectFileInterface;
+ break;
+ case LibraryLoad::Hidden:
+ GetObjFileInterface = getObjectFileInterfaceHidden;
+ break;
+ }
+ return StaticLibraryDefinitionGenerator::Load(
+ S.ObjLayer, Path, S.ES.getExecutorProcessControl().getTargetTriple(),
+ std::move(GetObjFileInterface));
+ };
+
+ for (auto &LL : LibraryLoads) {
+ bool LibFound = false;
+ auto &JD = *std::prev(IdxToJD.lower_bound(LL.Position))->second;
+
+ // If this is the name of a JITDylib then link against that.
+ if (auto *LJD = S.ES.getJITDylibByName(LL.LibName)) {
+ JD.addToLinkOrder(*LJD);
+ continue;
+ }
+
+ if (LL.IsPath) {
+ auto G = AddArchive(LL.LibName.str().c_str(), LL);
+ if (!G)
+ return createFileError(LL.LibName, G.takeError());
+ JD.addGenerator(std::move(*G));
+ LLVM_DEBUG({
+ dbgs() << "Adding generator for static library " << LL.LibName << " to "
+ << JD.getName() << "\n";
+ });
+ continue;
+ }
+
+ // Otherwise look through the search paths.
+ auto JDSearchPathsItr = JDSearchPaths.find(&JD);
+ if (JDSearchPathsItr != JDSearchPaths.end()) {
+ for (StringRef SearchPath : JDSearchPathsItr->second) {
+ for (const char *LibExt : {".dylib", ".so", ".a"}) {
+ SmallVector<char, 256> LibPath;
+ LibPath.reserve(SearchPath.size() + strlen("lib") +
+ LL.LibName.size() + strlen(LibExt) +
+ 2); // +2 for pathsep, null term.
+ llvm::copy(SearchPath, std::back_inserter(LibPath));
+ sys::path::append(LibPath, "lib" + LL.LibName + LibExt);
+ LibPath.push_back('\0');
+
+ // Skip missing or non-regular paths.
+ if (sys::fs::get_file_type(LibPath.data()) !=
+ sys::fs::file_type::regular_file) {
+ continue;
+ }
+
+ file_magic Magic;
+ if (auto EC = identify_magic(LibPath, Magic)) {
+ // If there was an error loading the file then skip it.
+ LLVM_DEBUG({
+ dbgs() << "Library search found \"" << LibPath
+ << "\", but could not identify file type (" << EC.message()
+ << "). Skipping.\n";
+ });
+ continue;
+ }
+
+ // We identified the magic. Assume that we can load it -- we'll reset
+ // in the default case.
+ LibFound = true;
+ switch (Magic) {
+ case file_magic::elf_shared_object:
+ case file_magic::macho_dynamically_linked_shared_lib: {
+ // TODO: On first reference to LibPath this should create a JITDylib
+ // with a generator and add it to JD's links-against list. Subsquent
+ // references should use the JITDylib created on the first
+ // reference.
+ auto G =
+ EPCDynamicLibrarySearchGenerator::Load(S.ES, LibPath.data());
+ if (!G)
+ return G.takeError();
+ LLVM_DEBUG({
+ dbgs() << "Adding generator for dynamic library "
+ << LibPath.data() << " to " << JD.getName() << "\n";
+ });
+ JD.addGenerator(std::move(*G));
+ break;
+ }
+ case file_magic::archive:
+ case file_magic::macho_universal_binary: {
+ auto G = AddArchive(LibPath.data(), LL);
+ if (!G)
+ return G.takeError();
+ JD.addGenerator(std::move(*G));
+ LLVM_DEBUG({
+ dbgs() << "Adding generator for static library " << LibPath.data()
+ << " to " << JD.getName() << "\n";
+ });
+ break;
+ }
+ default:
+ // This file isn't a recognized library kind.
+ LLVM_DEBUG({
+ dbgs() << "Library search found \"" << LibPath
+ << "\", but file type is not supported. Skipping.\n";
+ });
+ LibFound = false;
+ break;
+ }
+ if (LibFound)
+ break;
+ }
+ if (LibFound)
+ break;
+ }
+ }
+
+ if (!LibFound)
+ return make_error<StringError>("While linking " + JD.getName() +
+ ", could not find library for -l" +
+ LL.LibName,
+ inconvertibleErrorCode());
+ }
+
+ return Error::success();
+}
+
+static Error addProcessSymbols(Session &S,
+ const std::map<unsigned, JITDylib *> &IdxToJD) {
+
+ if (NoProcessSymbols)
+ return Error::success();
+
+ for (auto &KV : IdxToJD) {
+ auto &JD = *KV.second;
+ JD.addGenerator(ExitOnErr(
+ orc::EPCDynamicLibrarySearchGenerator::GetForTargetProcess(S.ES)));
+ }
+
+ return Error::success();
+}
+
+static Error addSessionInputs(Session &S) {
+ std::map<unsigned, JITDylib *> IdxToJD;
+
+ if (auto Err = createJITDylibs(S, IdxToJD))
+ return Err;
+
+ if (auto Err = addAbsoluteSymbols(S, IdxToJD))
+ return Err;
+
+ if (!TestHarnesses.empty())
+ if (auto Err = addTestHarnesses(S))
+ return Err;
+
+ if (auto Err = addObjects(S, IdxToJD))
+ return Err;
+
+ if (auto Err = addLibraries(S, IdxToJD))
+ return Err;
+
+ if (auto Err = addProcessSymbols(S, IdxToJD))
+ return Err;
+
+ return Error::success();
+}
+
+namespace {
+struct TargetInfo {
+ const Target *TheTarget;
+ std::unique_ptr<MCSubtargetInfo> STI;
+ std::unique_ptr<MCRegisterInfo> MRI;
+ std::unique_ptr<MCAsmInfo> MAI;
+ std::unique_ptr<MCContext> Ctx;
+ std::unique_ptr<MCDisassembler> Disassembler;
+ std::unique_ptr<MCInstrInfo> MII;
+ std::unique_ptr<MCInstrAnalysis> MIA;
+ std::unique_ptr<MCInstPrinter> InstPrinter;
+};
+} // anonymous namespace
+
+static TargetInfo getTargetInfo(const Triple &TT) {
+ auto TripleName = TT.str();
+ std::string ErrorStr;
+ const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, ErrorStr);
+ if (!TheTarget)
+ ExitOnErr(make_error<StringError>("Error accessing target '" + TripleName +
+ "': " + ErrorStr,
+ inconvertibleErrorCode()));
+
+ std::unique_ptr<MCSubtargetInfo> STI(
+ TheTarget->createMCSubtargetInfo(TripleName, "", ""));
+ if (!STI)
+ ExitOnErr(
+ make_error<StringError>("Unable to create subtarget for " + TripleName,
+ inconvertibleErrorCode()));
+
+ std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
+ if (!MRI)
+ ExitOnErr(make_error<StringError>("Unable to create target register info "
+ "for " +
+ TripleName,
+ inconvertibleErrorCode()));
+
+ MCTargetOptions MCOptions;
+ std::unique_ptr<MCAsmInfo> MAI(
+ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ if (!MAI)
+ ExitOnErr(make_error<StringError>("Unable to create target asm info " +
+ TripleName,
+ inconvertibleErrorCode()));
+
+ auto Ctx = std::make_unique<MCContext>(Triple(TripleName), MAI.get(),
+ MRI.get(), STI.get());
+
+ std::unique_ptr<MCDisassembler> Disassembler(
+ TheTarget->createMCDisassembler(*STI, *Ctx));
+ if (!Disassembler)
+ ExitOnErr(make_error<StringError>("Unable to create disassembler for " +
+ TripleName,
+ inconvertibleErrorCode()));
+
+ std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
+ if (!MII)
+ ExitOnErr(make_error<StringError>("Unable to create instruction info for" +
+ TripleName,
+ inconvertibleErrorCode()));
+
+ std::unique_ptr<MCInstrAnalysis> MIA(
+ TheTarget->createMCInstrAnalysis(MII.get()));
+ if (!MIA)
+ ExitOnErr(make_error<StringError>(
+ "Unable to create instruction analysis for" + TripleName,
+ inconvertibleErrorCode()));
+
+ std::unique_ptr<MCInstPrinter> InstPrinter(
+ TheTarget->createMCInstPrinter(Triple(TripleName), 0, *MAI, *MII, *MRI));
+ if (!InstPrinter)
+ ExitOnErr(make_error<StringError>(
+ "Unable to create instruction printer for" + TripleName,
+ inconvertibleErrorCode()));
+ return {TheTarget, std::move(STI), std::move(MRI),
+ std::move(MAI), std::move(Ctx), std::move(Disassembler),
+ std::move(MII), std::move(MIA), std::move(InstPrinter)};
+}
+
+static Error runChecks(Session &S) {
+ const auto &TT = S.ES.getExecutorProcessControl().getTargetTriple();
+
+ if (CheckFiles.empty())
+ return Error::success();
+
+ LLVM_DEBUG(dbgs() << "Running checks...\n");
+
+ auto TI = getTargetInfo(TT);
+
+ auto IsSymbolValid = [&S](StringRef Symbol) {
+ return S.isSymbolRegistered(Symbol);
+ };
+
+ auto GetSymbolInfo = [&S](StringRef Symbol) {
+ return S.findSymbolInfo(Symbol, "Can not get symbol info");
+ };
+
+ auto GetSectionInfo = [&S](StringRef FileName, StringRef SectionName) {
+ return S.findSectionInfo(FileName, SectionName);
+ };
+
+ auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName) {
+ return S.findStubInfo(FileName, SectionName);
+ };
+
+ auto GetGOTInfo = [&S](StringRef FileName, StringRef SectionName) {
+ return S.findGOTEntryInfo(FileName, SectionName);
+ };
+
+ RuntimeDyldChecker Checker(
+ IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo, GetGOTInfo,
+ TT.isLittleEndian() ? support::little : support::big,
+ TI.Disassembler.get(), TI.InstPrinter.get(), dbgs());
+
+ std::string CheckLineStart = "# " + CheckName + ":";
+ for (auto &CheckFile : CheckFiles) {
+ auto CheckerFileBuf =
+ ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(CheckFile)));
+ if (!Checker.checkAllRulesInBuffer(CheckLineStart, &*CheckerFileBuf))
+ ExitOnErr(make_error<StringError>(
+ "Some checks in " + CheckFile + " failed", inconvertibleErrorCode()));
+ }
+
+ return Error::success();
+}
+
+static Error addSelfRelocations(LinkGraph &G) {
+ auto TI = getTargetInfo(G.getTargetTriple());
+ for (auto *Sym : G.defined_symbols())
+ if (Sym->isCallable())
+ if (auto Err = addFunctionPointerRelocationsToCurrentSymbol(
+ *Sym, G, *TI.Disassembler, *TI.MIA))
+ return Err;
+ return Error::success();
+}
+
+static void dumpSessionStats(Session &S) {
+ if (!ShowSizes)
+ return;
+ if (!OrcRuntime.empty())
+ outs() << "Note: Session stats include runtime and entry point lookup, but "
+ "not JITDylib initialization/deinitialization.\n";
+ if (ShowSizes)
+ outs() << " Total size of all blocks before pruning: "
+ << S.SizeBeforePruning
+ << "\n Total size of all blocks after fixups: " << S.SizeAfterFixups
+ << "\n";
+}
+
+static Expected<JITEvaluatedSymbol> getMainEntryPoint(Session &S) {
+ return S.ES.lookup(S.JDSearchOrder, S.ES.intern(EntryPointName));
+}
+
+static Expected<JITEvaluatedSymbol> getOrcRuntimeEntryPoint(Session &S) {
+ std::string RuntimeEntryPoint = "__orc_rt_run_program_wrapper";
+ const auto &TT = S.ES.getExecutorProcessControl().getTargetTriple();
+ if (TT.getObjectFormat() == Triple::MachO)
+ RuntimeEntryPoint = '_' + RuntimeEntryPoint;
+ return S.ES.lookup(S.JDSearchOrder, S.ES.intern(RuntimeEntryPoint));
+}
+
+static Expected<int> runWithRuntime(Session &S, ExecutorAddr EntryPointAddr) {
+ StringRef DemangledEntryPoint = EntryPointName;
+ const auto &TT = S.ES.getExecutorProcessControl().getTargetTriple();
+ if (TT.getObjectFormat() == Triple::MachO &&
+ DemangledEntryPoint.front() == '_')
+ DemangledEntryPoint = DemangledEntryPoint.drop_front();
+ using SPSRunProgramSig =
+ int64_t(SPSString, SPSString, SPSSequence<SPSString>);
+ int64_t Result;
+ if (auto Err = S.ES.callSPSWrapper<SPSRunProgramSig>(
+ EntryPointAddr, Result, S.MainJD->getName(), DemangledEntryPoint,
+ static_cast<std::vector<std::string> &>(InputArgv)))
+ return std::move(Err);
+ return Result;
+}
+
+static Expected<int> runWithoutRuntime(Session &S,
+ ExecutorAddr EntryPointAddr) {
+ return S.ES.getExecutorProcessControl().runAsMain(EntryPointAddr, InputArgv);
+}
+
+namespace {
+struct JITLinkTimers {
+ TimerGroup JITLinkTG{"llvm-jitlink timers", "timers for llvm-jitlink phases"};
+ Timer LoadObjectsTimer{"load", "time to load/add object files", JITLinkTG};
+ Timer LinkTimer{"link", "time to link object files", JITLinkTG};
+ Timer RunTimer{"run", "time to execute jitlink'd code", JITLinkTG};
+};
+} // namespace
+
+int main(int argc, char *argv[]) {
+ InitLLVM X(argc, argv);
+
+ InitializeAllTargetInfos();
+ InitializeAllTargetMCs();
+ InitializeAllDisassemblers();
+
+ cl::HideUnrelatedOptions({&JITLinkCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "llvm jitlink tool");
+ ExitOnErr.setBanner(std::string(argv[0]) + ": ");
+
+ /// If timers are enabled, create a JITLinkTimers instance.
+ std::unique_ptr<JITLinkTimers> Timers =
+ ShowTimes ? std::make_unique<JITLinkTimers>() : nullptr;
+
+ ExitOnErr(sanitizeArguments(getFirstFileTriple(), argv[0]));
+
+ auto S = ExitOnErr(Session::Create(getFirstFileTriple()));
+
+ {
+ TimeRegion TR(Timers ? &Timers->LoadObjectsTimer : nullptr);
+ ExitOnErr(addSessionInputs(*S));
+ }
+
+ if (PhonyExternals)
+ addPhonyExternalsGenerator(*S);
+
+ if (ShowInitialExecutionSessionState)
+ S->ES.dump(outs());
+
+ JITEvaluatedSymbol EntryPoint = nullptr;
+ {
+ TimeRegion TR(Timers ? &Timers->LinkTimer : nullptr);
+ // Find the entry-point function unconditionally, since we want to force
+ // it to be materialized to collect stats.
+ EntryPoint = ExitOnErr(getMainEntryPoint(*S));
+ LLVM_DEBUG({
+ dbgs() << "Using entry point \"" << EntryPointName
+ << "\": " << formatv("{0:x16}", EntryPoint.getAddress()) << "\n";
+ });
+
+ // If we're running with the ORC runtime then replace the entry-point
+ // with the __orc_rt_run_program symbol.
+ if (!OrcRuntime.empty()) {
+ EntryPoint = ExitOnErr(getOrcRuntimeEntryPoint(*S));
+ LLVM_DEBUG({
+ dbgs() << "(called via __orc_rt_run_program_wrapper at "
+ << formatv("{0:x16}", EntryPoint.getAddress()) << ")\n";
+ });
+ }
+ }
+
+ if (ShowEntryExecutionSessionState)
+ S->ES.dump(outs());
+
+ if (ShowAddrs)
+ S->dumpSessionInfo(outs());
+
+ ExitOnErr(runChecks(*S));
+
+ dumpSessionStats(*S);
+
+ if (NoExec)
+ return 0;
+
+ int Result = 0;
+ {
+ LLVM_DEBUG(dbgs() << "Running \"" << EntryPointName << "\"...\n");
+ TimeRegion TR(Timers ? &Timers->RunTimer : nullptr);
+ if (!OrcRuntime.empty())
+ Result =
+ ExitOnErr(runWithRuntime(*S, ExecutorAddr(EntryPoint.getAddress())));
+ else
+ Result = ExitOnErr(
+ runWithoutRuntime(*S, ExecutorAddr(EntryPoint.getAddress())));
+ }
+
+ // Destroy the session.
+ ExitOnErr(S->ES.endSession());
+ S.reset();
+
+ // If the executing code set a test result override then use that.
+ if (UseTestResultOverride)
+ Result = TestResultOverride;
+
+ return Result;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink.h b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink.h
new file mode 100644
index 00000000000..71bdab7862c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-jitlink/llvm-jitlink.h
@@ -0,0 +1,93 @@
+//===---- llvm-jitlink.h - Session and format-specific decls ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// llvm-jitlink Session class and tool utilities.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_JITLINK_LLVM_JITLINK_H
+#define LLVM_TOOLS_LLVM_JITLINK_LLVM_JITLINK_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
+#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
+#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
+#include "llvm/ExecutionEngine/RuntimeDyldChecker.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <vector>
+
+namespace llvm {
+
+struct Session;
+
+struct Session {
+
+ orc::ExecutionSession ES;
+ orc::JITDylib *MainJD = nullptr;
+ orc::ObjectLinkingLayer ObjLayer;
+ orc::JITDylibSearchOrder JDSearchOrder;
+
+ ~Session();
+
+ static Expected<std::unique_ptr<Session>> Create(Triple TT);
+ void dumpSessionInfo(raw_ostream &OS);
+ void modifyPassConfig(const Triple &FTT,
+ jitlink::PassConfiguration &PassConfig);
+
+ using MemoryRegionInfo = RuntimeDyldChecker::MemoryRegionInfo;
+
+ struct FileInfo {
+ StringMap<MemoryRegionInfo> SectionInfos;
+ StringMap<MemoryRegionInfo> StubInfos;
+ StringMap<MemoryRegionInfo> GOTEntryInfos;
+ };
+
+ using SymbolInfoMap = StringMap<MemoryRegionInfo>;
+ using FileInfoMap = StringMap<FileInfo>;
+
+ Expected<FileInfo &> findFileInfo(StringRef FileName);
+ Expected<MemoryRegionInfo &> findSectionInfo(StringRef FileName,
+ StringRef SectionName);
+ Expected<MemoryRegionInfo &> findStubInfo(StringRef FileName,
+ StringRef TargetName);
+ Expected<MemoryRegionInfo &> findGOTEntryInfo(StringRef FileName,
+ StringRef TargetName);
+
+ bool isSymbolRegistered(StringRef Name);
+ Expected<MemoryRegionInfo &> findSymbolInfo(StringRef SymbolName,
+ Twine ErrorMsgStem);
+
+ SymbolInfoMap SymbolInfos;
+ FileInfoMap FileInfos;
+ uint64_t SizeBeforePruning = 0;
+ uint64_t SizeAfterFixups = 0;
+
+ StringSet<> HarnessFiles;
+ StringSet<> HarnessExternals;
+ StringSet<> HarnessDefinitions;
+ DenseMap<StringRef, StringRef> CanonicalWeakDefs;
+
+private:
+ Session(std::unique_ptr<orc::ExecutorProcessControl> EPC, Error &Err);
+};
+
+/// Record symbols, GOT entries, stubs, and sections for ELF file.
+Error registerELFGraphInfo(Session &S, jitlink::LinkGraph &G);
+
+/// Record symbols, GOT entries, stubs, and sections for MachO file.
+Error registerMachOGraphInfo(Session &S, jitlink::LinkGraph &G);
+
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_JITLINK_LLVM_JITLINK_H
diff --git a/contrib/libs/llvm14/tools/llvm-jitlink/ya.make b/contrib/libs/llvm14/tools/llvm-jitlink/ya.make
new file mode 100644
index 00000000000..b0338450285
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-jitlink/ya.make
@@ -0,0 +1,84 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/ExecutionEngine
+ contrib/libs/llvm14/lib/ExecutionEngine/JITLink
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc/Shared
+ contrib/libs/llvm14/lib/ExecutionEngine/Orc/TargetProcess
+ contrib/libs/llvm14/lib/ExecutionEngine/RuntimeDyld
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Passes
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/Coroutines
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/ObjCARC
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-jitlink
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-jitlink-elf.cpp
+ llvm-jitlink-macho.cpp
+ llvm-jitlink.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp b/contrib/libs/llvm14/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp
new file mode 100644
index 00000000000..9355d385d6f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp
@@ -0,0 +1,694 @@
+//===-- llvm-libtool-darwin.cpp - a tool for creating libraries -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A utility for creating static and dynamic libraries for Darwin.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/Object/ArchiveWriter.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/MachOUniversalWriter.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TextAPI/Architecture.h"
+#include <map>
+#include <type_traits>
+
+using namespace llvm;
+using namespace llvm::object;
+
+static LLVMContext LLVMCtx;
+
+class NewArchiveMemberList;
+typedef std::map<uint64_t, NewArchiveMemberList> MembersPerArchitectureMap;
+
+cl::OptionCategory LibtoolCategory("llvm-libtool-darwin Options");
+
+static cl::opt<std::string> OutputFile("o", cl::desc("Specify output filename"),
+ cl::value_desc("filename"),
+ cl::cat(LibtoolCategory));
+
+static cl::list<std::string> InputFiles(cl::Positional,
+ cl::desc("<input files>"),
+ cl::ZeroOrMore,
+ cl::cat(LibtoolCategory));
+
+static cl::opt<std::string> ArchType(
+ "arch_only", cl::desc("Specify architecture type for output library"),
+ cl::value_desc("arch_type"), cl::ZeroOrMore, cl::cat(LibtoolCategory));
+
+enum class Operation { None, Static };
+
+static cl::opt<Operation> LibraryOperation(
+ cl::desc("Library Type: "),
+ cl::values(
+ clEnumValN(Operation::Static, "static",
+ "Produce a statically linked library from the input files")),
+ cl::init(Operation::None), cl::cat(LibtoolCategory));
+
+static cl::opt<bool> DeterministicOption(
+ "D", cl::desc("Use zero for timestamps and UIDs/GIDs (Default)"),
+ cl::init(false), cl::cat(LibtoolCategory));
+
+static cl::opt<bool>
+ NonDeterministicOption("U", cl::desc("Use actual timestamps and UIDs/GIDs"),
+ cl::init(false), cl::cat(LibtoolCategory));
+
+static cl::opt<std::string>
+ FileList("filelist",
+ cl::desc("Pass in file containing a list of filenames"),
+ cl::value_desc("listfile[,dirname]"), cl::cat(LibtoolCategory));
+
+static cl::list<std::string> Libraries(
+ "l",
+ cl::desc(
+ "l<x> searches for the library libx.a in the library search path. If"
+ " the string 'x' ends with '.o', then the library 'x' is searched for"
+ " without prepending 'lib' or appending '.a'"),
+ cl::ZeroOrMore, cl::Prefix, cl::cat(LibtoolCategory));
+
+static cl::list<std::string> LibrarySearchDirs(
+ "L",
+ cl::desc(
+ "L<dir> adds <dir> to the list of directories in which to search for"
+ " libraries"),
+ cl::ZeroOrMore, cl::Prefix, cl::cat(LibtoolCategory));
+
+static cl::opt<bool>
+ VersionOption("V", cl::desc("Print the version number and exit"),
+ cl::cat(LibtoolCategory));
+
+static cl::opt<bool> NoWarningForNoSymbols(
+ "no_warning_for_no_symbols",
+ cl::desc("Do not warn about files that have no symbols"),
+ cl::cat(LibtoolCategory), cl::init(false));
+
+static const std::array<std::string, 3> StandardSearchDirs{
+ "/lib",
+ "/usr/lib",
+ "/usr/local/lib",
+};
+
+struct Config {
+ bool Deterministic = true; // Updated by 'D' and 'U' modifiers.
+ uint32_t ArchCPUType;
+ uint32_t ArchCPUSubtype;
+};
+
+static Expected<std::string> searchForFile(const Twine &FileName) {
+
+ auto FindLib =
+ [FileName](ArrayRef<std::string> SearchDirs) -> Optional<std::string> {
+ for (StringRef Dir : SearchDirs) {
+ SmallString<128> Path;
+ sys::path::append(Path, Dir, FileName);
+
+ if (sys::fs::exists(Path))
+ return std::string(Path);
+ }
+ return None;
+ };
+
+ Optional<std::string> Found = FindLib(LibrarySearchDirs);
+ if (!Found)
+ Found = FindLib(StandardSearchDirs);
+ if (Found)
+ return *Found;
+
+ return createStringError(std::errc::invalid_argument,
+ "cannot locate file '%s'", FileName.str().c_str());
+}
+
+static Error processCommandLineLibraries() {
+ for (StringRef BaseName : Libraries) {
+ Expected<std::string> FullPath = searchForFile(
+ BaseName.endswith(".o") ? BaseName.str() : "lib" + BaseName + ".a");
+ if (!FullPath)
+ return FullPath.takeError();
+ InputFiles.push_back(FullPath.get());
+ }
+
+ return Error::success();
+}
+
+static Error processFileList() {
+ StringRef FileName, DirName;
+ std::tie(FileName, DirName) = StringRef(FileList).rsplit(",");
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
+ MemoryBuffer::getFileOrSTDIN(FileName, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
+ if (std::error_code EC = FileOrErr.getError())
+ return createFileError(FileName, errorCodeToError(EC));
+ const MemoryBuffer &Ref = *FileOrErr.get();
+
+ line_iterator I(Ref, /*SkipBlanks=*/false);
+ if (I.is_at_eof())
+ return createStringError(std::errc::invalid_argument,
+ "file list file: '%s' is empty",
+ FileName.str().c_str());
+ for (; !I.is_at_eof(); ++I) {
+ StringRef Line = *I;
+ if (Line.empty())
+ return createStringError(std::errc::invalid_argument,
+ "file list file: '%s': filename cannot be empty",
+ FileName.str().c_str());
+
+ SmallString<128> Path;
+ if (!DirName.empty())
+ sys::path::append(Path, DirName, Line);
+ else
+ sys::path::append(Path, Line);
+ InputFiles.push_back(static_cast<std::string>(Path));
+ }
+ return Error::success();
+}
+
+static Error validateArchitectureName(StringRef ArchitectureName) {
+ if (!MachOObjectFile::isValidArch(ArchitectureName)) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ for (StringRef Arch : MachOObjectFile::getValidArchs())
+ OS << Arch << " ";
+
+ return createStringError(
+ std::errc::invalid_argument,
+ "invalid architecture '%s': valid architecture names are %s",
+ ArchitectureName.str().c_str(), OS.str().c_str());
+ }
+ return Error::success();
+}
+
+static uint64_t getCPUID(uint32_t CPUType, uint32_t CPUSubtype) {
+ switch (CPUType) {
+ case MachO::CPU_TYPE_ARM:
+ case MachO::CPU_TYPE_ARM64:
+ case MachO::CPU_TYPE_ARM64_32:
+ case MachO::CPU_TYPE_X86_64:
+ // We consider CPUSubtype only for the above 4 CPUTypes to match cctools'
+ // libtool behavior.
+ return static_cast<uint64_t>(CPUType) << 32 | CPUSubtype;
+ default:
+ return CPUType;
+ }
+}
+
+// MembersData is an organized collection of members.
+struct MembersData {
+ // MembersPerArchitectureMap is a mapping from CPU architecture to a list of
+ // members.
+ MembersPerArchitectureMap MembersPerArchitecture;
+ std::vector<std::unique_ptr<MemoryBuffer>> FileBuffers;
+};
+
+// NewArchiveMemberList instances serve as collections of archive members and
+// information about those members.
+class NewArchiveMemberList {
+ std::vector<NewArchiveMember> Members;
+ // This vector contains the file that each NewArchiveMember from Members came
+ // from. Therefore, it has the same size as Members.
+ std::vector<StringRef> Files;
+
+public:
+ // Add a NewArchiveMember and the file it came from to the list.
+ void push_back(NewArchiveMember &&Member, StringRef File) {
+ Members.push_back(std::move(Member));
+ Files.push_back(File);
+ }
+
+ ArrayRef<NewArchiveMember> getMembers() const { return Members; }
+
+ ArrayRef<StringRef> getFiles() const { return Files; }
+
+ static_assert(
+ std::is_same<decltype(MembersData::MembersPerArchitecture)::mapped_type,
+ NewArchiveMemberList>(),
+ "This test makes sure NewArchiveMemberList is used by MembersData since "
+ "the following asserts test invariants required for MembersData.");
+ static_assert(
+ !std::is_copy_constructible<
+ decltype(NewArchiveMemberList::Members)::value_type>::value,
+ "MembersData::MembersPerArchitecture has a dependency on "
+ "MembersData::FileBuffers so it should not be able to "
+ "be copied on its own without FileBuffers. Unfortunately, "
+ "is_copy_constructible does not detect whether the container (ie vector) "
+ "of a non-copyable type is itself non-copyable so we have to test the "
+ "actual type of the stored data (ie, value_type).");
+ static_assert(
+ !std::is_copy_assignable<
+ decltype(NewArchiveMemberList::Members)::value_type>::value,
+ "MembersData::MembersPerArchitecture has a dependency on "
+ "MembersData::FileBuffers so it should not be able to "
+ "be copied on its own without FileBuffers. Unfortunately, "
+ "is_copy_constructible does not detect whether the container (ie vector) "
+ "of a non-copyable type is itself non-copyable so we have to test the "
+ "actual type of the stored data (ie, value_type).");
+};
+
+// MembersBuilder collects and organizes all members from the files provided by
+// the user.
+class MembersBuilder {
+public:
+ MembersBuilder(const Config &C) : C(C) {}
+
+ Expected<MembersData> build() {
+ for (StringRef FileName : InputFiles)
+ if (Error E = AddMember(*this, FileName)())
+ return std::move(E);
+
+ if (!ArchType.empty()) {
+ uint64_t ArchCPUID = getCPUID(C.ArchCPUType, C.ArchCPUSubtype);
+ if (Data.MembersPerArchitecture.find(ArchCPUID) ==
+ Data.MembersPerArchitecture.end())
+ return createStringError(std::errc::invalid_argument,
+ "no library created (no object files in input "
+ "files matching -arch_only %s)",
+ ArchType.c_str());
+ }
+ return std::move(Data);
+ }
+
+private:
+ class AddMember {
+ MembersBuilder &Builder;
+ StringRef FileName;
+
+ public:
+ AddMember(MembersBuilder &Builder, StringRef FileName)
+ : Builder(Builder), FileName(FileName) {}
+
+ Error operator()() {
+ Expected<NewArchiveMember> NewMemberOrErr =
+ NewArchiveMember::getFile(FileName, Builder.C.Deterministic);
+ if (!NewMemberOrErr)
+ return createFileError(FileName, NewMemberOrErr.takeError());
+ auto &NewMember = *NewMemberOrErr;
+
+ // For regular archives, use the basename of the object path for the
+ // member name.
+ NewMember.MemberName = sys::path::filename(NewMember.MemberName);
+ file_magic Magic = identify_magic(NewMember.Buf->getBuffer());
+
+ // Flatten archives.
+ if (Magic == file_magic::archive)
+ return addArchiveMembers(std::move(NewMember));
+
+ // Flatten universal files.
+ if (Magic == file_magic::macho_universal_binary)
+ return addUniversalMembers(std::move(NewMember));
+
+ // Bitcode files.
+ if (Magic == file_magic::bitcode)
+ return verifyAndAddIRObject(std::move(NewMember));
+
+ return verifyAndAddMachOObject(std::move(NewMember));
+ }
+
+ private:
+ // Check that a file's architecture [FileCPUType, FileCPUSubtype]
+ // matches the architecture specified under -arch_only flag.
+ bool acceptFileArch(uint32_t FileCPUType, uint32_t FileCPUSubtype) {
+ if (Builder.C.ArchCPUType != FileCPUType)
+ return false;
+
+ switch (Builder.C.ArchCPUType) {
+ case MachO::CPU_TYPE_ARM:
+ case MachO::CPU_TYPE_ARM64_32:
+ case MachO::CPU_TYPE_X86_64:
+ return Builder.C.ArchCPUSubtype == FileCPUSubtype;
+
+ case MachO::CPU_TYPE_ARM64:
+ if (Builder.C.ArchCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL)
+ return FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL ||
+ FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_V8;
+ else
+ return Builder.C.ArchCPUSubtype == FileCPUSubtype;
+
+ default:
+ return true;
+ }
+ }
+
+ Error verifyAndAddMachOObject(NewArchiveMember Member) {
+ auto MBRef = Member.Buf->getMemBufferRef();
+ Expected<std::unique_ptr<object::ObjectFile>> ObjOrErr =
+ object::ObjectFile::createObjectFile(MBRef);
+
+ // Throw error if not a valid object file.
+ if (!ObjOrErr)
+ return createFileError(Member.MemberName, ObjOrErr.takeError());
+
+ // Throw error if not in Mach-O format.
+ if (!isa<object::MachOObjectFile>(**ObjOrErr))
+ return createStringError(std::errc::invalid_argument,
+ "'%s': format not supported",
+ Member.MemberName.data());
+
+ auto *O = dyn_cast<MachOObjectFile>(ObjOrErr->get());
+ uint32_t FileCPUType, FileCPUSubtype;
+ std::tie(FileCPUType, FileCPUSubtype) = MachO::getCPUTypeFromArchitecture(
+ MachO::getArchitectureFromName(O->getArchTriple().getArchName()));
+
+ // If -arch_only is specified then skip this file if it doesn't match
+ // the architecture specified.
+ if (!ArchType.empty() && !acceptFileArch(FileCPUType, FileCPUSubtype)) {
+ return Error::success();
+ }
+
+ if (!NoWarningForNoSymbols && O->symbols().empty())
+ WithColor::warning() << Member.MemberName + " has no symbols\n";
+
+ uint64_t FileCPUID = getCPUID(FileCPUType, FileCPUSubtype);
+ Builder.Data.MembersPerArchitecture[FileCPUID].push_back(
+ std::move(Member), FileName);
+ return Error::success();
+ }
+
+ Error verifyAndAddIRObject(NewArchiveMember Member) {
+ auto MBRef = Member.Buf->getMemBufferRef();
+ Expected<std::unique_ptr<object::IRObjectFile>> IROrErr =
+ object::IRObjectFile::create(MBRef, LLVMCtx);
+
+ // Throw error if not a valid IR object file.
+ if (!IROrErr)
+ return createFileError(Member.MemberName, IROrErr.takeError());
+
+ Triple TT = Triple(IROrErr->get()->getTargetTriple());
+
+ Expected<uint32_t> FileCPUTypeOrErr = MachO::getCPUType(TT);
+ if (!FileCPUTypeOrErr)
+ return FileCPUTypeOrErr.takeError();
+
+ Expected<uint32_t> FileCPUSubTypeOrErr = MachO::getCPUSubType(TT);
+ if (!FileCPUSubTypeOrErr)
+ return FileCPUSubTypeOrErr.takeError();
+
+ // If -arch_only is specified then skip this file if it doesn't match
+ // the architecture specified.
+ if (!ArchType.empty() &&
+ !acceptFileArch(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr)) {
+ return Error::success();
+ }
+
+ uint64_t FileCPUID = getCPUID(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr);
+ Builder.Data.MembersPerArchitecture[FileCPUID].push_back(
+ std::move(Member), FileName);
+ return Error::success();
+ }
+
+ Error addChildMember(const object::Archive::Child &M) {
+ Expected<NewArchiveMember> NewMemberOrErr =
+ NewArchiveMember::getOldMember(M, Builder.C.Deterministic);
+ if (!NewMemberOrErr)
+ return NewMemberOrErr.takeError();
+ auto &NewMember = *NewMemberOrErr;
+
+ file_magic Magic = identify_magic(NewMember.Buf->getBuffer());
+
+ if (Magic == file_magic::bitcode)
+ return verifyAndAddIRObject(std::move(NewMember));
+
+ return verifyAndAddMachOObject(std::move(NewMember));
+ }
+
+ Error processArchive(object::Archive &Lib) {
+ Error Err = Error::success();
+ for (const object::Archive::Child &Child : Lib.children(Err))
+ if (Error E = addChildMember(Child))
+ return createFileError(FileName, std::move(E));
+ if (Err)
+ return createFileError(FileName, std::move(Err));
+
+ return Error::success();
+ }
+
+ Error addArchiveMembers(NewArchiveMember NewMember) {
+ Expected<std::unique_ptr<Archive>> LibOrErr =
+ object::Archive::create(NewMember.Buf->getMemBufferRef());
+ if (!LibOrErr)
+ return createFileError(FileName, LibOrErr.takeError());
+
+ if (Error E = processArchive(**LibOrErr))
+ return E;
+
+ // Update vector FileBuffers with the MemoryBuffers to transfer
+ // ownership.
+ Builder.Data.FileBuffers.push_back(std::move(NewMember.Buf));
+ return Error::success();
+ }
+
+ Error addUniversalMembers(NewArchiveMember NewMember) {
+ Expected<std::unique_ptr<MachOUniversalBinary>> BinaryOrErr =
+ MachOUniversalBinary::create(NewMember.Buf->getMemBufferRef());
+ if (!BinaryOrErr)
+ return createFileError(FileName, BinaryOrErr.takeError());
+
+ auto *UO = BinaryOrErr->get();
+ for (const MachOUniversalBinary::ObjectForArch &O : UO->objects()) {
+
+ Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrErr =
+ O.getAsObjectFile();
+ if (MachOObjOrErr) {
+ NewArchiveMember NewMember =
+ NewArchiveMember(MachOObjOrErr->get()->getMemoryBufferRef());
+ NewMember.MemberName = sys::path::filename(NewMember.MemberName);
+
+ if (Error E = verifyAndAddMachOObject(std::move(NewMember)))
+ return E;
+ continue;
+ }
+
+ Expected<std::unique_ptr<IRObjectFile>> IRObjectOrError =
+ O.getAsIRObject(LLVMCtx);
+ if (IRObjectOrError) {
+ // A universal file member can be a MachOObjectFile, an IRObject or an
+ // Archive. In case we can successfully cast the member as an
+ // IRObject, it is safe to throw away the error generated due to
+ // casting the object as a MachOObjectFile.
+ consumeError(MachOObjOrErr.takeError());
+
+ NewArchiveMember NewMember =
+ NewArchiveMember(IRObjectOrError->get()->getMemoryBufferRef());
+ NewMember.MemberName = sys::path::filename(NewMember.MemberName);
+
+ if (Error E = verifyAndAddIRObject(std::move(NewMember)))
+ return E;
+ continue;
+ }
+
+ Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive();
+ if (ArchiveOrError) {
+ // A universal file member can be a MachOObjectFile, an IRObject or an
+ // Archive. In case we can successfully cast the member as an Archive,
+ // it is safe to throw away the error generated due to casting the
+ // object as a MachOObjectFile.
+ consumeError(MachOObjOrErr.takeError());
+ consumeError(IRObjectOrError.takeError());
+
+ if (Error E = processArchive(**ArchiveOrError))
+ return E;
+ continue;
+ }
+
+ Error CombinedError = joinErrors(
+ ArchiveOrError.takeError(),
+ joinErrors(IRObjectOrError.takeError(), MachOObjOrErr.takeError()));
+ return createFileError(FileName, std::move(CombinedError));
+ }
+
+ // Update vector FileBuffers with the MemoryBuffers to transfer
+ // ownership.
+ Builder.Data.FileBuffers.push_back(std::move(NewMember.Buf));
+ return Error::success();
+ }
+ };
+
+ MembersData Data;
+ const Config &C;
+};
+
+static Expected<SmallVector<Slice, 2>>
+buildSlices(ArrayRef<OwningBinary<Archive>> OutputBinaries) {
+ SmallVector<Slice, 2> Slices;
+
+ for (const auto &OB : OutputBinaries) {
+ const Archive &A = *OB.getBinary();
+ Expected<Slice> ArchiveSlice = Slice::create(A, &LLVMCtx);
+ if (!ArchiveSlice)
+ return ArchiveSlice.takeError();
+ Slices.push_back(*ArchiveSlice);
+ }
+ return Slices;
+}
+
+static Error
+checkForDuplicates(const MembersPerArchitectureMap &MembersPerArch) {
+ for (const auto &M : MembersPerArch) {
+ ArrayRef<NewArchiveMember> Members = M.second.getMembers();
+ ArrayRef<StringRef> Files = M.second.getFiles();
+ StringMap<std::vector<StringRef>> MembersToFiles;
+ for (auto Iterators = std::make_pair(Members.begin(), Files.begin());
+ Iterators.first != Members.end();
+ ++Iterators.first, ++Iterators.second) {
+ assert(Iterators.second != Files.end() &&
+ "Files should be the same size as Members.");
+ MembersToFiles[Iterators.first->MemberName].push_back(*Iterators.second);
+ }
+
+ std::string ErrorData;
+ raw_string_ostream ErrorStream(ErrorData);
+ for (const auto &MemberToFile : MembersToFiles) {
+ if (MemberToFile.getValue().size() > 1) {
+ ErrorStream << "file '" << MemberToFile.getKey().str()
+ << "' was specified multiple times.\n";
+
+ for (StringRef OriginalFile : MemberToFile.getValue())
+ ErrorStream << "in: " << OriginalFile.str() << '\n';
+
+ ErrorStream << '\n';
+ }
+ }
+
+ ErrorStream.flush();
+ if (ErrorData.size() > 0)
+ return createStringError(std::errc::invalid_argument, ErrorData.c_str());
+ }
+ return Error::success();
+}
+
+static Error createStaticLibrary(const Config &C) {
+ MembersBuilder Builder(C);
+ auto DataOrError = Builder.build();
+ if (auto Error = DataOrError.takeError())
+ return Error;
+
+ const auto &NewMembers = DataOrError->MembersPerArchitecture;
+
+ if (Error E = checkForDuplicates(NewMembers))
+ WithColor::defaultWarningHandler(std::move(E));
+
+ if (NewMembers.size() == 1)
+ return writeArchive(OutputFile, NewMembers.begin()->second.getMembers(),
+ /*WriteSymtab=*/true,
+ /*Kind=*/object::Archive::K_DARWIN, C.Deterministic,
+ /*Thin=*/false);
+
+ SmallVector<OwningBinary<Archive>, 2> OutputBinaries;
+ for (const std::pair<const uint64_t, NewArchiveMemberList> &M : NewMembers) {
+ Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr =
+ writeArchiveToBuffer(M.second.getMembers(),
+ /*WriteSymtab=*/true,
+ /*Kind=*/object::Archive::K_DARWIN,
+ C.Deterministic,
+ /*Thin=*/false);
+ if (!OutputBufferOrErr)
+ return OutputBufferOrErr.takeError();
+ std::unique_ptr<MemoryBuffer> &OutputBuffer = OutputBufferOrErr.get();
+
+ Expected<std::unique_ptr<Archive>> ArchiveOrError =
+ Archive::create(OutputBuffer->getMemBufferRef());
+ if (!ArchiveOrError)
+ return ArchiveOrError.takeError();
+ std::unique_ptr<Archive> &A = ArchiveOrError.get();
+
+ OutputBinaries.push_back(
+ OwningBinary<Archive>(std::move(A), std::move(OutputBuffer)));
+ }
+
+ Expected<SmallVector<Slice, 2>> Slices = buildSlices(OutputBinaries);
+ if (!Slices)
+ return Slices.takeError();
+
+ llvm::stable_sort(*Slices);
+ return writeUniversalBinary(*Slices, OutputFile);
+}
+
+static Expected<Config> parseCommandLine(int Argc, char **Argv) {
+ Config C;
+ cl::ParseCommandLineOptions(Argc, Argv, "llvm-libtool-darwin\n");
+
+ if (LibraryOperation == Operation::None) {
+ if (!VersionOption) {
+ std::string Error;
+ raw_string_ostream Stream(Error);
+ LibraryOperation.error("must be specified", "", Stream);
+ return createStringError(std::errc::invalid_argument, Error.c_str());
+ }
+ return C;
+ }
+
+ if (OutputFile.empty()) {
+ std::string Error;
+ raw_string_ostream Stream(Error);
+ OutputFile.error("must be specified", "o", Stream);
+ return createStringError(std::errc::invalid_argument, Error.c_str());
+ }
+
+ if (DeterministicOption && NonDeterministicOption)
+ return createStringError(std::errc::invalid_argument,
+ "cannot specify both -D and -U flags");
+ else if (NonDeterministicOption)
+ C.Deterministic = false;
+
+ if (!Libraries.empty())
+ if (Error E = processCommandLineLibraries())
+ return std::move(E);
+
+ if (!FileList.empty())
+ if (Error E = processFileList())
+ return std::move(E);
+
+ if (InputFiles.empty())
+ return createStringError(std::errc::invalid_argument,
+ "no input files specified");
+
+ if (ArchType.getNumOccurrences()) {
+ if (Error E = validateArchitectureName(ArchType))
+ return std::move(E);
+
+ std::tie(C.ArchCPUType, C.ArchCPUSubtype) =
+ MachO::getCPUTypeFromArchitecture(
+ MachO::getArchitectureFromName(ArchType));
+ }
+
+ return C;
+}
+
+int main(int Argc, char **Argv) {
+ InitLLVM X(Argc, Argv);
+ cl::HideUnrelatedOptions({&LibtoolCategory, &getColorCategory()});
+ Expected<Config> ConfigOrErr = parseCommandLine(Argc, Argv);
+ if (!ConfigOrErr) {
+ WithColor::defaultErrorHandler(ConfigOrErr.takeError());
+ return EXIT_FAILURE;
+ }
+
+ if (VersionOption)
+ cl::PrintVersionMessage();
+
+ Config C = *ConfigOrErr;
+ switch (LibraryOperation) {
+ case Operation::None:
+ break;
+ case Operation::Static:
+ if (Error E = createStaticLibrary(C)) {
+ WithColor::defaultErrorHandler(std::move(E));
+ return EXIT_FAILURE;
+ }
+ break;
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-libtool-darwin/ya.make b/contrib/libs/llvm14/tools/llvm-libtool-darwin/ya.make
new file mode 100644
index 00000000000..517eca772e9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-libtool-darwin/ya.make
@@ -0,0 +1,36 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-libtool-darwin
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-libtool-darwin.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-link/llvm-link.cpp b/contrib/libs/llvm14/tools/llvm-link/llvm-link.cpp
new file mode 100644
index 00000000000..9abe8efaa4e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-link/llvm-link.cpp
@@ -0,0 +1,505 @@
+//===- llvm-link.cpp - Low-level LLVM linker ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This utility may be invoked in the following manner:
+// llvm-link a.bc b.bc c.bc -o x.bc
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/IR/AutoUpgrade.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/ModuleSummaryIndex.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Linker/Linker.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/SystemUtils.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Transforms/IPO/FunctionImport.h"
+#include "llvm/Transforms/IPO/Internalize.h"
+#include "llvm/Transforms/Utils/FunctionImportUtils.h"
+
+#include <memory>
+#include <utility>
+using namespace llvm;
+
+static cl::OptionCategory LinkCategory("Link Options");
+
+static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
+ cl::desc("<input bitcode files>"),
+ cl::cat(LinkCategory));
+
+static cl::list<std::string> OverridingInputs(
+ "override", cl::ZeroOrMore, cl::value_desc("filename"),
+ cl::desc(
+ "input bitcode file which can override previously defined symbol(s)"),
+ cl::cat(LinkCategory));
+
+// Option to simulate function importing for testing. This enables using
+// llvm-link to simulate ThinLTO backend processes.
+static cl::list<std::string> Imports(
+ "import", cl::ZeroOrMore, cl::value_desc("function:filename"),
+ cl::desc("Pair of function name and filename, where function should be "
+ "imported from bitcode in filename"),
+ cl::cat(LinkCategory));
+
+// Option to support testing of function importing. The module summary
+// must be specified in the case were we request imports via the -import
+// option, as well as when compiling any module with functions that may be
+// exported (imported by a different llvm-link -import invocation), to ensure
+// consistent promotion and renaming of locals.
+static cl::opt<std::string>
+ SummaryIndex("summary-index", cl::desc("Module summary index filename"),
+ cl::init(""), cl::value_desc("filename"),
+ cl::cat(LinkCategory));
+
+static cl::opt<std::string>
+ OutputFilename("o", cl::desc("Override output filename"), cl::init("-"),
+ cl::value_desc("filename"), cl::cat(LinkCategory));
+
+static cl::opt<bool> Internalize("internalize",
+ cl::desc("Internalize linked symbols"),
+ cl::cat(LinkCategory));
+
+static cl::opt<bool>
+ DisableDITypeMap("disable-debug-info-type-map",
+ cl::desc("Don't use a uniquing type map for debug info"),
+ cl::cat(LinkCategory));
+
+static cl::opt<bool> OnlyNeeded("only-needed",
+ cl::desc("Link only needed symbols"),
+ cl::cat(LinkCategory));
+
+static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"),
+ cl::cat(LinkCategory));
+
+static cl::opt<bool> DisableLazyLoad("disable-lazy-loading",
+ cl::desc("Disable lazy module loading"),
+ cl::cat(LinkCategory));
+
+static cl::opt<bool> OutputAssembly("S",
+ cl::desc("Write output as LLVM assembly"),
+ cl::Hidden, cl::cat(LinkCategory));
+
+static cl::opt<bool> Verbose("v",
+ cl::desc("Print information about actions taken"),
+ cl::cat(LinkCategory));
+
+static cl::opt<bool> DumpAsm("d", cl::desc("Print assembly as linked"),
+ cl::Hidden, cl::cat(LinkCategory));
+
+static cl::opt<bool> SuppressWarnings("suppress-warnings",
+ cl::desc("Suppress all linking warnings"),
+ cl::init(false), cl::cat(LinkCategory));
+
+static cl::opt<bool> PreserveBitcodeUseListOrder(
+ "preserve-bc-uselistorder",
+ cl::desc("Preserve use-list order when writing LLVM bitcode."),
+ cl::init(true), cl::Hidden, cl::cat(LinkCategory));
+
+static cl::opt<bool> PreserveAssemblyUseListOrder(
+ "preserve-ll-uselistorder",
+ cl::desc("Preserve use-list order when writing LLVM assembly."),
+ cl::init(false), cl::Hidden, cl::cat(LinkCategory));
+
+static cl::opt<bool> NoVerify("disable-verify",
+ cl::desc("Do not run the verifier"), cl::Hidden,
+ cl::cat(LinkCategory));
+
+static ExitOnError ExitOnErr;
+
+// Read the specified bitcode file in and return it. This routine searches the
+// link path for the specified file to try to find it...
+//
+static std::unique_ptr<Module> loadFile(const char *argv0,
+ std::unique_ptr<MemoryBuffer> Buffer,
+ LLVMContext &Context,
+ bool MaterializeMetadata = true) {
+ SMDiagnostic Err;
+ if (Verbose)
+ errs() << "Loading '" << Buffer->getBufferIdentifier() << "'\n";
+ std::unique_ptr<Module> Result;
+ if (DisableLazyLoad)
+ Result = parseIR(*Buffer, Err, Context);
+ else
+ Result =
+ getLazyIRModule(std::move(Buffer), Err, Context, !MaterializeMetadata);
+
+ if (!Result) {
+ Err.print(argv0, errs());
+ return nullptr;
+ }
+
+ if (MaterializeMetadata) {
+ ExitOnErr(Result->materializeMetadata());
+ UpgradeDebugInfo(*Result);
+ }
+
+ return Result;
+}
+
+static std::unique_ptr<Module> loadArFile(const char *Argv0,
+ std::unique_ptr<MemoryBuffer> Buffer,
+ LLVMContext &Context) {
+ std::unique_ptr<Module> Result(new Module("ArchiveModule", Context));
+ StringRef ArchiveName = Buffer->getBufferIdentifier();
+ if (Verbose)
+ errs() << "Reading library archive file '" << ArchiveName
+ << "' to memory\n";
+ Error Err = Error::success();
+ object::Archive Archive(*Buffer, Err);
+ ExitOnErr(std::move(Err));
+ Linker L(*Result);
+ for (const object::Archive::Child &C : Archive.children(Err)) {
+ Expected<StringRef> Ename = C.getName();
+ if (Error E = Ename.takeError()) {
+ errs() << Argv0 << ": ";
+ WithColor::error() << " failed to read name of archive member"
+ << ArchiveName << "'\n";
+ return nullptr;
+ }
+ std::string ChildName = Ename.get().str();
+ if (Verbose)
+ errs() << "Parsing member '" << ChildName
+ << "' of archive library to module.\n";
+ SMDiagnostic ParseErr;
+ Expected<MemoryBufferRef> MemBuf = C.getMemoryBufferRef();
+ if (Error E = MemBuf.takeError()) {
+ errs() << Argv0 << ": ";
+ WithColor::error() << " loading memory for member '" << ChildName
+ << "' of archive library failed'" << ArchiveName
+ << "'\n";
+ return nullptr;
+ };
+
+ if (!isBitcode(reinterpret_cast<const unsigned char *>(
+ MemBuf.get().getBufferStart()),
+ reinterpret_cast<const unsigned char *>(
+ MemBuf.get().getBufferEnd()))) {
+ errs() << Argv0 << ": ";
+ WithColor::error() << " member of archive is not a bitcode file: '"
+ << ChildName << "'\n";
+ return nullptr;
+ }
+
+ std::unique_ptr<Module> M;
+ if (DisableLazyLoad)
+ M = parseIR(MemBuf.get(), ParseErr, Context);
+ else
+ M = getLazyIRModule(MemoryBuffer::getMemBuffer(MemBuf.get(), false),
+ ParseErr, Context);
+
+ if (!M.get()) {
+ errs() << Argv0 << ": ";
+ WithColor::error() << " parsing member '" << ChildName
+ << "' of archive library failed'" << ArchiveName
+ << "'\n";
+ return nullptr;
+ }
+ if (Verbose)
+ errs() << "Linking member '" << ChildName << "' of archive library.\n";
+ if (L.linkInModule(std::move(M)))
+ return nullptr;
+ } // end for each child
+ ExitOnErr(std::move(Err));
+ return Result;
+}
+
+namespace {
+
+/// Helper to load on demand a Module from file and cache it for subsequent
+/// queries during function importing.
+class ModuleLazyLoaderCache {
+ /// Cache of lazily loaded module for import.
+ StringMap<std::unique_ptr<Module>> ModuleMap;
+
+ /// Retrieve a Module from the cache or lazily load it on demand.
+ std::function<std::unique_ptr<Module>(const char *argv0,
+ const std::string &FileName)>
+ createLazyModule;
+
+public:
+ /// Create the loader, Module will be initialized in \p Context.
+ ModuleLazyLoaderCache(std::function<std::unique_ptr<Module>(
+ const char *argv0, const std::string &FileName)>
+ createLazyModule)
+ : createLazyModule(std::move(createLazyModule)) {}
+
+ /// Retrieve a Module from the cache or lazily load it on demand.
+ Module &operator()(const char *argv0, const std::string &FileName);
+
+ std::unique_ptr<Module> takeModule(const std::string &FileName) {
+ auto I = ModuleMap.find(FileName);
+ assert(I != ModuleMap.end());
+ std::unique_ptr<Module> Ret = std::move(I->second);
+ ModuleMap.erase(I);
+ return Ret;
+ }
+};
+
+// Get a Module for \p FileName from the cache, or load it lazily.
+Module &ModuleLazyLoaderCache::operator()(const char *argv0,
+ const std::string &Identifier) {
+ auto &Module = ModuleMap[Identifier];
+ if (!Module) {
+ Module = createLazyModule(argv0, Identifier);
+ assert(Module && "Failed to create lazy module!");
+ }
+ return *Module;
+}
+} // anonymous namespace
+
+namespace {
+struct LLVMLinkDiagnosticHandler : public DiagnosticHandler {
+ bool handleDiagnostics(const DiagnosticInfo &DI) override {
+ unsigned Severity = DI.getSeverity();
+ switch (Severity) {
+ case DS_Error:
+ WithColor::error();
+ break;
+ case DS_Warning:
+ if (SuppressWarnings)
+ return true;
+ WithColor::warning();
+ break;
+ case DS_Remark:
+ case DS_Note:
+ llvm_unreachable("Only expecting warnings and errors");
+ }
+
+ DiagnosticPrinterRawOStream DP(errs());
+ DI.print(DP);
+ errs() << '\n';
+ return true;
+ }
+};
+} // namespace
+
+/// Import any functions requested via the -import option.
+static bool importFunctions(const char *argv0, Module &DestModule) {
+ if (SummaryIndex.empty())
+ return true;
+ std::unique_ptr<ModuleSummaryIndex> Index =
+ ExitOnErr(llvm::getModuleSummaryIndexForFile(SummaryIndex));
+
+ // Map of Module -> List of globals to import from the Module
+ FunctionImporter::ImportMapTy ImportList;
+
+ auto ModuleLoader = [&DestModule](const char *argv0,
+ const std::string &Identifier) {
+ std::unique_ptr<MemoryBuffer> Buffer =
+ ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Identifier)));
+ return loadFile(argv0, std::move(Buffer), DestModule.getContext(), false);
+ };
+
+ ModuleLazyLoaderCache ModuleLoaderCache(ModuleLoader);
+ for (const auto &Import : Imports) {
+ // Identify the requested function and its bitcode source file.
+ size_t Idx = Import.find(':');
+ if (Idx == std::string::npos) {
+ errs() << "Import parameter bad format: " << Import << "\n";
+ return false;
+ }
+ std::string FunctionName = Import.substr(0, Idx);
+ std::string FileName = Import.substr(Idx + 1, std::string::npos);
+
+ // Load the specified source module.
+ auto &SrcModule = ModuleLoaderCache(argv0, FileName);
+
+ if (!NoVerify && verifyModule(SrcModule, &errs())) {
+ errs() << argv0 << ": " << FileName;
+ WithColor::error() << "input module is broken!\n";
+ return false;
+ }
+
+ Function *F = SrcModule.getFunction(FunctionName);
+ if (!F) {
+ errs() << "Ignoring import request for non-existent function "
+ << FunctionName << " from " << FileName << "\n";
+ continue;
+ }
+ // We cannot import weak_any functions without possibly affecting the
+ // order they are seen and selected by the linker, changing program
+ // semantics.
+ if (F->hasWeakAnyLinkage()) {
+ errs() << "Ignoring import request for weak-any function " << FunctionName
+ << " from " << FileName << "\n";
+ continue;
+ }
+
+ if (Verbose)
+ errs() << "Importing " << FunctionName << " from " << FileName << "\n";
+
+ auto &Entry = ImportList[FileName];
+ Entry.insert(F->getGUID());
+ }
+ auto CachedModuleLoader = [&](StringRef Identifier) {
+ return ModuleLoaderCache.takeModule(std::string(Identifier));
+ };
+ FunctionImporter Importer(*Index, CachedModuleLoader,
+ /*ClearDSOLocalOnDeclarations=*/false);
+ ExitOnErr(Importer.importFunctions(DestModule, ImportList));
+
+ return true;
+}
+
+static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
+ const cl::list<std::string> &Files, unsigned Flags) {
+ // Filter out flags that don't apply to the first file we load.
+ unsigned ApplicableFlags = Flags & Linker::Flags::OverrideFromSrc;
+ // Similar to some flags, internalization doesn't apply to the first file.
+ bool InternalizeLinkedSymbols = false;
+ for (const auto &File : Files) {
+ std::unique_ptr<MemoryBuffer> Buffer =
+ ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(File)));
+
+ std::unique_ptr<Module> M =
+ identify_magic(Buffer->getBuffer()) == file_magic::archive
+ ? loadArFile(argv0, std::move(Buffer), Context)
+ : loadFile(argv0, std::move(Buffer), Context);
+ if (!M.get()) {
+ errs() << argv0 << ": ";
+ WithColor::error() << " loading file '" << File << "'\n";
+ return false;
+ }
+
+ // Note that when ODR merging types cannot verify input files in here When
+ // doing that debug metadata in the src module might already be pointing to
+ // the destination.
+ if (DisableDITypeMap && !NoVerify && verifyModule(*M, &errs())) {
+ errs() << argv0 << ": " << File << ": ";
+ WithColor::error() << "input module is broken!\n";
+ return false;
+ }
+
+ // If a module summary index is supplied, load it so linkInModule can treat
+ // local functions/variables as exported and promote if necessary.
+ if (!SummaryIndex.empty()) {
+ std::unique_ptr<ModuleSummaryIndex> Index =
+ ExitOnErr(llvm::getModuleSummaryIndexForFile(SummaryIndex));
+
+ // Conservatively mark all internal values as promoted, since this tool
+ // does not do the ThinLink that would normally determine what values to
+ // promote.
+ for (auto &I : *Index) {
+ for (auto &S : I.second.SummaryList) {
+ if (GlobalValue::isLocalLinkage(S->linkage()))
+ S->setLinkage(GlobalValue::ExternalLinkage);
+ }
+ }
+
+ // Promotion
+ if (renameModuleForThinLTO(*M, *Index,
+ /*ClearDSOLocalOnDeclarations=*/false))
+ return true;
+ }
+
+ if (Verbose)
+ errs() << "Linking in '" << File << "'\n";
+
+ bool Err = false;
+ if (InternalizeLinkedSymbols) {
+ Err = L.linkInModule(
+ std::move(M), ApplicableFlags, [](Module &M, const StringSet<> &GVS) {
+ internalizeModule(M, [&GVS](const GlobalValue &GV) {
+ return !GV.hasName() || (GVS.count(GV.getName()) == 0);
+ });
+ });
+ } else {
+ Err = L.linkInModule(std::move(M), ApplicableFlags);
+ }
+
+ if (Err)
+ return false;
+
+ // Internalization applies to linking of subsequent files.
+ InternalizeLinkedSymbols = Internalize;
+
+ // All linker flags apply to linking of subsequent files.
+ ApplicableFlags = Flags;
+ }
+
+ return true;
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ ExitOnErr.setBanner(std::string(argv[0]) + ": ");
+
+ LLVMContext Context;
+ Context.setDiagnosticHandler(std::make_unique<LLVMLinkDiagnosticHandler>(),
+ true);
+ cl::HideUnrelatedOptions({&LinkCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "llvm linker\n");
+
+ if (!DisableDITypeMap)
+ Context.enableDebugTypeODRUniquing();
+
+ auto Composite = std::make_unique<Module>("llvm-link", Context);
+ Linker L(*Composite);
+
+ unsigned Flags = Linker::Flags::None;
+ if (OnlyNeeded)
+ Flags |= Linker::Flags::LinkOnlyNeeded;
+
+ // First add all the regular input files
+ if (!linkFiles(argv[0], Context, L, InputFilenames, Flags))
+ return 1;
+
+ // Next the -override ones.
+ if (!linkFiles(argv[0], Context, L, OverridingInputs,
+ Flags | Linker::Flags::OverrideFromSrc))
+ return 1;
+
+ // Import any functions requested via -import
+ if (!importFunctions(argv[0], *Composite))
+ return 1;
+
+ if (DumpAsm)
+ errs() << "Here's the assembly:\n" << *Composite;
+
+ std::error_code EC;
+ ToolOutputFile Out(OutputFilename, EC,
+ OutputAssembly ? sys::fs::OF_TextWithCRLF
+ : sys::fs::OF_None);
+ if (EC) {
+ WithColor::error() << EC.message() << '\n';
+ return 1;
+ }
+
+ if (!NoVerify && verifyModule(*Composite, &errs())) {
+ errs() << argv[0] << ": ";
+ WithColor::error() << "linked module is broken!\n";
+ return 1;
+ }
+
+ if (Verbose)
+ errs() << "Writing bitcode...\n";
+ if (OutputAssembly) {
+ Composite->print(Out.os(), nullptr, PreserveAssemblyUseListOrder);
+ } else if (Force || !CheckBitcodeOutputToConsole(Out.os()))
+ WriteBitcodeToFile(*Composite, Out.os(), PreserveBitcodeUseListOrder);
+
+ // Declare success.
+ Out.keep();
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-link/ya.make b/contrib/libs/llvm14/tools/llvm-link/ya.make
new file mode 100644
index 00000000000..5e966ba8855
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-link/ya.make
@@ -0,0 +1,52 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-link
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-link.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-lipo/LipoOpts.td b/contrib/libs/llvm14/tools/llvm-lipo/LipoOpts.td
new file mode 100644
index 00000000000..866353573cb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-lipo/LipoOpts.td
@@ -0,0 +1,60 @@
+include "llvm/Option/OptParser.td"
+
+def help : Flag<["-", "--"], "help">;
+def h : Flag<["-"], "h">, Alias<help>;
+
+def version : Flag<["-", "--"], "version">,
+ HelpText<"Print the version and exit.">;
+
+def segalign
+ : MultiArg<["-", "--"], "segalign", 2>,
+ HelpText<"Specifies the segment alignment for the specified "
+ "architecture when creating a universal binary file. The "
+ "alignment is a hexadecimal number that is a power of 2.">;
+
+def arch
+ : MultiArg<["-", "--"], "arch", 2>,
+ HelpText<"Specifies the architecture and the corresponding input file">;
+
+def action_group : OptionGroup<"action group">;
+
+def verify_arch
+ : Option<["-", "--"], "verify_arch", KIND_REMAINING_ARGS>,
+ Group<action_group>,
+ HelpText<
+ "Verify that the specified arch_types are present in the input file">;
+
+def archs : Option<["-", "--"], "archs", KIND_FLAG>,
+ Group<action_group>,
+ HelpText<"Display the arch_types present in the input file">;
+
+def info : Option<["-", "--"], "info", KIND_FLAG>,
+ Group<action_group>,
+ HelpText<"Display descriptions of each input file including "
+ "filename and arch_types. Groups universal binaries "
+ "together followed by thin files">;
+
+def thin : Option<["-", "--"], "thin", KIND_SEPARATE>,
+ Group<action_group>,
+ HelpText<"Create a thin output file of specified arch_type from the "
+ "fat input file. Requires -output option">;
+
+def extract : Option<["-", "--"], "extract", KIND_SEPARATE>,
+ Group<action_group>,
+ HelpText<"Create a universal output file containing only the specified "
+ "arch_type from the fat input file. Requires -output option">;
+
+def create : Option<["-", "--"], "create", KIND_FLAG>,
+ Group<action_group>,
+ HelpText<"Create a universal binary output file from the input "
+ "files. Requires -output option">;
+
+def replace
+ : MultiArg<["-", "--"], "replace", 2>,
+ Group<action_group>,
+ HelpText<"Replace the specified arch type with the contents of the "
+ "input_file in a universal binary. Requires -output option">;
+
+def output : Option<["-", "--"], "output", KIND_SEPARATE>,
+ HelpText<"Create output file with specified name">;
+def o : JoinedOrSeparate<["-"], "o">, Alias<output>;
diff --git a/contrib/libs/llvm14/tools/llvm-lipo/llvm-lipo.cpp b/contrib/libs/llvm14/tools/llvm-lipo/llvm-lipo.cpp
new file mode 100644
index 00000000000..8bf58c624cf
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-lipo/llvm-lipo.cpp
@@ -0,0 +1,755 @@
+//===-- llvm-lipo.cpp - a tool for manipulating universal binaries --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A utility for creating / splitting / inspecting universal binaries.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/MachOUniversalWriter.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/TextAPI/Architecture.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+static const StringRef ToolName = "llvm-lipo";
+static LLVMContext LLVMCtx;
+
+[[noreturn]] static void reportError(Twine Message) {
+ WithColor::error(errs(), ToolName) << Message << "\n";
+ errs().flush();
+ exit(EXIT_FAILURE);
+}
+
+[[noreturn]] static void reportError(Error E) {
+ assert(E);
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS);
+ OS.flush();
+ reportError(Buf);
+}
+
+[[noreturn]] static void reportError(StringRef File, Error E) {
+ assert(E);
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS);
+ OS.flush();
+ WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf;
+ exit(EXIT_FAILURE);
+}
+
+namespace {
+enum LipoID {
+ LIPO_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ LIPO_##ID,
+#include "LipoOpts.inc"
+#undef OPTION
+};
+
+// LipoInfoTable below references LIPO_##PREFIX. OptionGroup has prefix nullptr.
+const char *const *LIPO_nullptr = nullptr;
+#define PREFIX(NAME, VALUE) const char *const LIPO_##NAME[] = VALUE;
+#include "LipoOpts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info LipoInfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {LIPO_##PREFIX, NAME, HELPTEXT, \
+ METAVAR, LIPO_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, LIPO_##GROUP, \
+ LIPO_##ALIAS, ALIASARGS, VALUES},
+#include "LipoOpts.inc"
+#undef OPTION
+};
+
+class LipoOptTable : public opt::OptTable {
+public:
+ LipoOptTable() : OptTable(LipoInfoTable) {}
+};
+
+enum class LipoAction {
+ PrintArchs,
+ PrintInfo,
+ VerifyArch,
+ ThinArch,
+ ExtractArch,
+ CreateUniversal,
+ ReplaceArch,
+};
+
+struct InputFile {
+ Optional<StringRef> ArchType;
+ StringRef FileName;
+};
+
+struct Config {
+ SmallVector<InputFile, 1> InputFiles;
+ SmallVector<std::string, 1> VerifyArchList;
+ SmallVector<InputFile, 1> ReplacementFiles;
+ StringMap<const uint32_t> SegmentAlignments;
+ std::string ArchType;
+ std::string OutputFile;
+ LipoAction ActionToPerform;
+};
+
+static Slice createSliceFromArchive(const Archive &A) {
+ Expected<Slice> ArchiveOrSlice = Slice::create(A, &LLVMCtx);
+ if (!ArchiveOrSlice)
+ reportError(A.getFileName(), ArchiveOrSlice.takeError());
+ return *ArchiveOrSlice;
+}
+
+static Slice createSliceFromIR(const IRObjectFile &IRO, unsigned Align) {
+ Expected<Slice> IROrErr = Slice::create(IRO, Align);
+ if (!IROrErr)
+ reportError(IRO.getFileName(), IROrErr.takeError());
+ return *IROrErr;
+}
+
+} // end namespace
+
+static void validateArchitectureName(StringRef ArchitectureName) {
+ if (!MachOObjectFile::isValidArch(ArchitectureName)) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ OS << "Invalid architecture: " << ArchitectureName
+ << "\nValid architecture names are:";
+ for (auto arch : MachOObjectFile::getValidArchs())
+ OS << " " << arch;
+ reportError(OS.str());
+ }
+}
+
+static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
+ Config C;
+ LipoOptTable T;
+ unsigned MissingArgumentIndex, MissingArgumentCount;
+ opt::InputArgList InputArgs =
+ T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+ if (MissingArgumentCount)
+ reportError("missing argument to " +
+ StringRef(InputArgs.getArgString(MissingArgumentIndex)) +
+ " option");
+
+ if (InputArgs.size() == 0) {
+ // printHelp does not accept Twine.
+ T.printHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
+ exit(EXIT_FAILURE);
+ }
+
+ if (InputArgs.hasArg(LIPO_help)) {
+ // printHelp does not accept Twine.
+ T.printHelp(outs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
+ exit(EXIT_SUCCESS);
+ }
+
+ if (InputArgs.hasArg(LIPO_version)) {
+ outs() << ToolName + "\n";
+ cl::PrintVersionMessage();
+ exit(EXIT_SUCCESS);
+ }
+
+ for (auto Arg : InputArgs.filtered(LIPO_UNKNOWN))
+ reportError("unknown argument '" + Arg->getAsString(InputArgs) + "'");
+
+ for (auto Arg : InputArgs.filtered(LIPO_INPUT))
+ C.InputFiles.push_back({None, Arg->getValue()});
+ for (auto Arg : InputArgs.filtered(LIPO_arch)) {
+ validateArchitectureName(Arg->getValue(0));
+ if (!Arg->getValue(1))
+ reportError(
+ "arch is missing an argument: expects -arch arch_type file_name");
+ C.InputFiles.push_back({StringRef(Arg->getValue(0)), Arg->getValue(1)});
+ }
+
+ if (C.InputFiles.empty())
+ reportError("at least one input file should be specified");
+
+ if (InputArgs.hasArg(LIPO_output))
+ C.OutputFile = std::string(InputArgs.getLastArgValue(LIPO_output));
+
+ for (auto Segalign : InputArgs.filtered(LIPO_segalign)) {
+ if (!Segalign->getValue(1))
+ reportError("segalign is missing an argument: expects -segalign "
+ "arch_type alignment_value");
+
+ validateArchitectureName(Segalign->getValue(0));
+
+ uint32_t AlignmentValue;
+ if (!to_integer<uint32_t>(Segalign->getValue(1), AlignmentValue, 16))
+ reportError("argument to -segalign <arch_type> " +
+ Twine(Segalign->getValue(1)) +
+ " (hex) is not a proper hexadecimal number");
+ if (!isPowerOf2_32(AlignmentValue))
+ reportError("argument to -segalign <arch_type> " +
+ Twine(Segalign->getValue(1)) +
+ " (hex) must be a non-zero power of two");
+ if (Log2_32(AlignmentValue) > MachOUniversalBinary::MaxSectionAlignment)
+ reportError(
+ "argument to -segalign <arch_type> " + Twine(Segalign->getValue(1)) +
+ " (hex) must be less than or equal to the maximum section align 2^" +
+ Twine(MachOUniversalBinary::MaxSectionAlignment));
+ auto Entry = C.SegmentAlignments.try_emplace(Segalign->getValue(0),
+ Log2_32(AlignmentValue));
+ if (!Entry.second)
+ reportError("-segalign " + Twine(Segalign->getValue(0)) +
+ " <alignment_value> specified multiple times: " +
+ Twine(1 << Entry.first->second) + ", " +
+ Twine(AlignmentValue));
+ }
+
+ SmallVector<opt::Arg *, 1> ActionArgs(InputArgs.filtered(LIPO_action_group));
+ if (ActionArgs.empty())
+ reportError("at least one action should be specified");
+ // errors if multiple actions specified other than replace
+ // multiple replace flags may be specified, as long as they are not mixed with
+ // other action flags
+ auto ReplacementArgsRange = InputArgs.filtered(LIPO_replace);
+ if (ActionArgs.size() > 1 &&
+ ActionArgs.size() !=
+ static_cast<size_t>(std::distance(ReplacementArgsRange.begin(),
+ ReplacementArgsRange.end()))) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ OS << "only one of the following actions can be specified:";
+ for (auto Arg : ActionArgs)
+ OS << " " << Arg->getSpelling();
+ reportError(OS.str());
+ }
+
+ switch (ActionArgs[0]->getOption().getID()) {
+ case LIPO_verify_arch:
+ for (auto A : InputArgs.getAllArgValues(LIPO_verify_arch))
+ C.VerifyArchList.push_back(A);
+ if (C.VerifyArchList.empty())
+ reportError(
+ "verify_arch requires at least one architecture to be specified");
+ if (C.InputFiles.size() > 1)
+ reportError("verify_arch expects a single input file");
+ C.ActionToPerform = LipoAction::VerifyArch;
+ return C;
+
+ case LIPO_archs:
+ if (C.InputFiles.size() > 1)
+ reportError("archs expects a single input file");
+ C.ActionToPerform = LipoAction::PrintArchs;
+ return C;
+
+ case LIPO_info:
+ C.ActionToPerform = LipoAction::PrintInfo;
+ return C;
+
+ case LIPO_thin:
+ if (C.InputFiles.size() > 1)
+ reportError("thin expects a single input file");
+ if (C.OutputFile.empty())
+ reportError("thin expects a single output file");
+ C.ArchType = ActionArgs[0]->getValue();
+ validateArchitectureName(C.ArchType);
+ C.ActionToPerform = LipoAction::ThinArch;
+ return C;
+
+ case LIPO_extract:
+ if (C.InputFiles.size() > 1)
+ reportError("extract expects a single input file");
+ if (C.OutputFile.empty())
+ reportError("extract expects a single output file");
+ C.ArchType = ActionArgs[0]->getValue();
+ validateArchitectureName(C.ArchType);
+ C.ActionToPerform = LipoAction::ExtractArch;
+ return C;
+
+ case LIPO_create:
+ if (C.OutputFile.empty())
+ reportError("create expects a single output file to be specified");
+ C.ActionToPerform = LipoAction::CreateUniversal;
+ return C;
+
+ case LIPO_replace:
+ for (auto Action : ActionArgs) {
+ if (!Action->getValue(1))
+ reportError(
+ "replace is missing an argument: expects -replace arch_type "
+ "file_name");
+ validateArchitectureName(Action->getValue(0));
+ C.ReplacementFiles.push_back(
+ {StringRef(Action->getValue(0)), Action->getValue(1)});
+ }
+
+ if (C.OutputFile.empty())
+ reportError("replace expects a single output file to be specified");
+ if (C.InputFiles.size() > 1)
+ reportError("replace expects a single input file");
+ C.ActionToPerform = LipoAction::ReplaceArch;
+ return C;
+
+ default:
+ reportError("llvm-lipo action unspecified");
+ }
+}
+
+static SmallVector<OwningBinary<Binary>, 1>
+readInputBinaries(ArrayRef<InputFile> InputFiles) {
+ SmallVector<OwningBinary<Binary>, 1> InputBinaries;
+ for (const InputFile &IF : InputFiles) {
+ Expected<OwningBinary<Binary>> BinaryOrErr =
+ createBinary(IF.FileName, &LLVMCtx);
+ if (!BinaryOrErr)
+ reportError(IF.FileName, BinaryOrErr.takeError());
+ const Binary *B = BinaryOrErr->getBinary();
+ if (!B->isArchive() && !B->isMachO() && !B->isMachOUniversalBinary() &&
+ !B->isIR())
+ reportError("File " + IF.FileName + " has unsupported binary format");
+ if (IF.ArchType && (B->isMachO() || B->isArchive() || B->isIR())) {
+ const auto S = B->isMachO()
+ ? Slice(*cast<MachOObjectFile>(B))
+ : B->isArchive()
+ ? createSliceFromArchive(*cast<Archive>(B))
+ : createSliceFromIR(*cast<IRObjectFile>(B), 0);
+ const auto SpecifiedCPUType = MachO::getCPUTypeFromArchitecture(
+ MachO::getArchitectureFromName(
+ Triple(*IF.ArchType).getArchName()))
+ .first;
+ // For compatibility with cctools' lipo the comparison is relaxed just to
+ // checking cputypes.
+ if (S.getCPUType() != SpecifiedCPUType)
+ reportError("specified architecture: " + *IF.ArchType +
+ " for file: " + B->getFileName() +
+ " does not match the file's architecture (" +
+ S.getArchString() + ")");
+ }
+ InputBinaries.push_back(std::move(*BinaryOrErr));
+ }
+ return InputBinaries;
+}
+
+[[noreturn]] static void
+verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries,
+ ArrayRef<std::string> VerifyArchList) {
+ assert(!VerifyArchList.empty() &&
+ "The list of architectures should be non-empty");
+ assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
+
+ for (StringRef Arch : VerifyArchList)
+ validateArchitectureName(Arch);
+
+ if (auto UO =
+ dyn_cast<MachOUniversalBinary>(InputBinaries.front().getBinary())) {
+ for (StringRef Arch : VerifyArchList) {
+ Expected<MachOUniversalBinary::ObjectForArch> Obj =
+ UO->getObjectForArch(Arch);
+ if (!Obj)
+ exit(EXIT_FAILURE);
+ }
+ } else if (auto O =
+ dyn_cast<MachOObjectFile>(InputBinaries.front().getBinary())) {
+ const Triple::ArchType ObjectArch = O->getArch();
+ for (StringRef Arch : VerifyArchList)
+ if (ObjectArch != Triple(Arch).getArch())
+ exit(EXIT_FAILURE);
+ } else {
+ llvm_unreachable("Unexpected binary format");
+ }
+ exit(EXIT_SUCCESS);
+}
+
+static void printBinaryArchs(const Binary *Binary, raw_ostream &OS) {
+ // Prints trailing space for compatibility with cctools lipo.
+ if (auto UO = dyn_cast<MachOUniversalBinary>(Binary)) {
+ for (const auto &O : UO->objects()) {
+ // Order here is important, because both MachOObjectFile and
+ // IRObjectFile can be created with a binary that has embedded bitcode.
+ Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrError =
+ O.getAsObjectFile();
+ if (MachOObjOrError) {
+ OS << Slice(*(MachOObjOrError->get())).getArchString() << " ";
+ continue;
+ }
+ Expected<std::unique_ptr<IRObjectFile>> IROrError =
+ O.getAsIRObject(LLVMCtx);
+ if (IROrError) {
+ consumeError(MachOObjOrError.takeError());
+ Expected<Slice> SliceOrErr = Slice::create(**IROrError, O.getAlign());
+ if (!SliceOrErr) {
+ reportError(Binary->getFileName(), SliceOrErr.takeError());
+ continue;
+ }
+ OS << SliceOrErr.get().getArchString() << " ";
+ continue;
+ }
+ Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive();
+ if (ArchiveOrError) {
+ consumeError(MachOObjOrError.takeError());
+ consumeError(IROrError.takeError());
+ OS << createSliceFromArchive(**ArchiveOrError).getArchString() << " ";
+ continue;
+ }
+ consumeError(ArchiveOrError.takeError());
+ reportError(Binary->getFileName(), MachOObjOrError.takeError());
+ reportError(Binary->getFileName(), IROrError.takeError());
+ }
+ OS << "\n";
+ return;
+ }
+
+ if (const auto *MachO = dyn_cast<MachOObjectFile>(Binary)) {
+ OS << Slice(*MachO).getArchString() << " \n";
+ return;
+ }
+
+ // This should be always the case, as this is tested in readInputBinaries
+ const auto *IR = cast<IRObjectFile>(Binary);
+ Expected<Slice> SliceOrErr = createSliceFromIR(*IR, 0);
+ if (!SliceOrErr)
+ reportError(IR->getFileName(), SliceOrErr.takeError());
+
+ OS << SliceOrErr->getArchString() << " \n";
+}
+
+[[noreturn]] static void
+printArchs(ArrayRef<OwningBinary<Binary>> InputBinaries) {
+ assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
+ printBinaryArchs(InputBinaries.front().getBinary(), outs());
+ exit(EXIT_SUCCESS);
+}
+
+[[noreturn]] static void
+printInfo(ArrayRef<OwningBinary<Binary>> InputBinaries) {
+ // Group universal and thin files together for compatibility with cctools lipo
+ for (auto &IB : InputBinaries) {
+ const Binary *Binary = IB.getBinary();
+ if (Binary->isMachOUniversalBinary()) {
+ outs() << "Architectures in the fat file: " << Binary->getFileName()
+ << " are: ";
+ printBinaryArchs(Binary, outs());
+ }
+ }
+ for (auto &IB : InputBinaries) {
+ const Binary *Binary = IB.getBinary();
+ if (!Binary->isMachOUniversalBinary()) {
+ assert(Binary->isMachO() && "expected MachO binary");
+ outs() << "Non-fat file: " << Binary->getFileName()
+ << " is architecture: ";
+ printBinaryArchs(Binary, outs());
+ }
+ }
+ exit(EXIT_SUCCESS);
+}
+
+[[noreturn]] static void thinSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
+ StringRef ArchType,
+ StringRef OutputFileName) {
+ assert(!ArchType.empty() && "The architecture type should be non-empty");
+ assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
+ assert(!OutputFileName.empty() && "Thin expects a single output file");
+
+ if (InputBinaries.front().getBinary()->isMachO()) {
+ reportError("input file " +
+ InputBinaries.front().getBinary()->getFileName() +
+ " must be a fat file when the -thin option is specified");
+ exit(EXIT_FAILURE);
+ }
+
+ auto *UO = cast<MachOUniversalBinary>(InputBinaries.front().getBinary());
+ Expected<std::unique_ptr<MachOObjectFile>> Obj =
+ UO->getMachOObjectForArch(ArchType);
+ Expected<std::unique_ptr<IRObjectFile>> IRObj =
+ UO->getIRObjectForArch(ArchType, LLVMCtx);
+ Expected<std::unique_ptr<Archive>> Ar = UO->getArchiveForArch(ArchType);
+ if (!Obj && !IRObj && !Ar)
+ reportError("fat input file " + UO->getFileName() +
+ " does not contain the specified architecture " + ArchType +
+ " to thin it to");
+ Binary *B;
+ // Order here is important, because both Obj and IRObj will be valid with a
+ // binary that has embedded bitcode.
+ if (Obj)
+ B = Obj->get();
+ else if (IRObj)
+ B = IRObj->get();
+ else
+ B = Ar->get();
+
+ Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError =
+ FileOutputBuffer::create(OutputFileName,
+ B->getMemoryBufferRef().getBufferSize(),
+ sys::fs::can_execute(UO->getFileName())
+ ? FileOutputBuffer::F_executable
+ : 0);
+ if (!OutFileOrError)
+ reportError(OutputFileName, OutFileOrError.takeError());
+ std::copy(B->getMemoryBufferRef().getBufferStart(),
+ B->getMemoryBufferRef().getBufferEnd(),
+ OutFileOrError.get()->getBufferStart());
+ if (Error E = OutFileOrError.get()->commit())
+ reportError(OutputFileName, std::move(E));
+ exit(EXIT_SUCCESS);
+}
+
+static void checkArchDuplicates(ArrayRef<Slice> Slices) {
+ DenseMap<uint64_t, const Binary *> CPUIds;
+ for (const auto &S : Slices) {
+ auto Entry = CPUIds.try_emplace(S.getCPUID(), S.getBinary());
+ if (!Entry.second)
+ reportError(Entry.first->second->getFileName() + " and " +
+ S.getBinary()->getFileName() +
+ " have the same architecture " + S.getArchString() +
+ " and therefore cannot be in the same universal binary");
+ }
+}
+
+template <typename Range>
+static void updateAlignments(Range &Slices,
+ const StringMap<const uint32_t> &Alignments) {
+ for (auto &Slice : Slices) {
+ auto Alignment = Alignments.find(Slice.getArchString());
+ if (Alignment != Alignments.end())
+ Slice.setP2Alignment(Alignment->second);
+ }
+}
+
+static void checkUnusedAlignments(ArrayRef<Slice> Slices,
+ const StringMap<const uint32_t> &Alignments) {
+ auto HasArch = [&](StringRef Arch) {
+ return llvm::any_of(Slices,
+ [Arch](Slice S) { return S.getArchString() == Arch; });
+ };
+ for (StringRef Arch : Alignments.keys())
+ if (!HasArch(Arch))
+ reportError("-segalign " + Arch +
+ " <value> specified but resulting fat file does not contain "
+ "that architecture ");
+}
+
+// Updates vector ExtractedObjects with the MachOObjectFiles extracted from
+// Universal Binary files to transfer ownership.
+static SmallVector<Slice, 2>
+buildSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,
+ const StringMap<const uint32_t> &Alignments,
+ SmallVectorImpl<std::unique_ptr<SymbolicFile>> &ExtractedObjects) {
+ SmallVector<Slice, 2> Slices;
+ for (auto &IB : InputBinaries) {
+ const Binary *InputBinary = IB.getBinary();
+ if (auto UO = dyn_cast<MachOUniversalBinary>(InputBinary)) {
+ for (const auto &O : UO->objects()) {
+ // Order here is important, because both MachOObjectFile and
+ // IRObjectFile can be created with a binary that has embedded bitcode.
+ Expected<std::unique_ptr<MachOObjectFile>> BinaryOrError =
+ O.getAsObjectFile();
+ if (BinaryOrError) {
+ Slices.emplace_back(*(BinaryOrError.get()), O.getAlign());
+ ExtractedObjects.push_back(std::move(BinaryOrError.get()));
+ continue;
+ }
+ Expected<std::unique_ptr<IRObjectFile>> IROrError =
+ O.getAsIRObject(LLVMCtx);
+ if (IROrError) {
+ consumeError(BinaryOrError.takeError());
+ Slice S = createSliceFromIR(**IROrError, O.getAlign());
+ ExtractedObjects.emplace_back(std::move(IROrError.get()));
+ Slices.emplace_back(std::move(S));
+ continue;
+ }
+ reportError(InputBinary->getFileName(), BinaryOrError.takeError());
+ }
+ } else if (const auto *O = dyn_cast<MachOObjectFile>(InputBinary)) {
+ Slices.emplace_back(*O);
+ } else if (const auto *A = dyn_cast<Archive>(InputBinary)) {
+ Slices.push_back(createSliceFromArchive(*A));
+ } else if (const auto *IRO = dyn_cast<IRObjectFile>(InputBinary)) {
+ // Original Apple's lipo set the alignment to 0
+ Expected<Slice> SliceOrErr = Slice::create(*IRO, 0);
+ if (!SliceOrErr) {
+ reportError(InputBinary->getFileName(), SliceOrErr.takeError());
+ continue;
+ }
+ Slices.emplace_back(std::move(SliceOrErr.get()));
+ } else {
+ llvm_unreachable("Unexpected binary format");
+ }
+ }
+ updateAlignments(Slices, Alignments);
+ return Slices;
+}
+
+[[noreturn]] static void
+createUniversalBinary(ArrayRef<OwningBinary<Binary>> InputBinaries,
+ const StringMap<const uint32_t> &Alignments,
+ StringRef OutputFileName) {
+ assert(InputBinaries.size() >= 1 && "Incorrect number of input binaries");
+ assert(!OutputFileName.empty() && "Create expects a single output file");
+
+ SmallVector<std::unique_ptr<SymbolicFile>, 1> ExtractedObjects;
+ SmallVector<Slice, 1> Slices =
+ buildSlices(InputBinaries, Alignments, ExtractedObjects);
+ checkArchDuplicates(Slices);
+ checkUnusedAlignments(Slices, Alignments);
+
+ llvm::stable_sort(Slices);
+ if (Error E = writeUniversalBinary(Slices, OutputFileName))
+ reportError(std::move(E));
+
+ exit(EXIT_SUCCESS);
+}
+
+[[noreturn]] static void
+extractSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
+ const StringMap<const uint32_t> &Alignments, StringRef ArchType,
+ StringRef OutputFileName) {
+ assert(!ArchType.empty() &&
+ "The architecture type should be non-empty");
+ assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
+ assert(!OutputFileName.empty() && "Thin expects a single output file");
+
+ if (InputBinaries.front().getBinary()->isMachO()) {
+ reportError("input file " +
+ InputBinaries.front().getBinary()->getFileName() +
+ " must be a fat file when the -extract option is specified");
+ }
+
+ SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;
+ SmallVector<Slice, 2> Slices =
+ buildSlices(InputBinaries, Alignments, ExtractedObjects);
+ erase_if(Slices, [ArchType](const Slice &S) {
+ return ArchType != S.getArchString();
+ });
+
+ if (Slices.empty())
+ reportError(
+ "fat input file " + InputBinaries.front().getBinary()->getFileName() +
+ " does not contain the specified architecture " + ArchType);
+
+ llvm::stable_sort(Slices);
+ if (Error E = writeUniversalBinary(Slices, OutputFileName))
+ reportError(std::move(E));
+ exit(EXIT_SUCCESS);
+}
+
+static StringMap<Slice>
+buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries,
+ const StringMap<const uint32_t> &Alignments) {
+ StringMap<Slice> Slices;
+ // populates StringMap of slices to replace with; error checks for mismatched
+ // replace flag args, fat files, and duplicate arch_types
+ for (const auto &OB : ReplacementBinaries) {
+ const Binary *ReplacementBinary = OB.getBinary();
+ auto O = dyn_cast<MachOObjectFile>(ReplacementBinary);
+ if (!O)
+ reportError("replacement file: " + ReplacementBinary->getFileName() +
+ " is a fat file (must be a thin file)");
+ Slice S(*O);
+ auto Entry = Slices.try_emplace(S.getArchString(), S);
+ if (!Entry.second)
+ reportError("-replace " + S.getArchString() +
+ " <file_name> specified multiple times: " +
+ Entry.first->second.getBinary()->getFileName() + ", " +
+ O->getFileName());
+ }
+ auto SlicesMapRange = map_range(
+ Slices, [](StringMapEntry<Slice> &E) -> Slice & { return E.getValue(); });
+ updateAlignments(SlicesMapRange, Alignments);
+ return Slices;
+}
+
+[[noreturn]] static void
+replaceSlices(ArrayRef<OwningBinary<Binary>> InputBinaries,
+ const StringMap<const uint32_t> &Alignments,
+ StringRef OutputFileName, ArrayRef<InputFile> ReplacementFiles) {
+ assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
+ assert(!OutputFileName.empty() && "Replace expects a single output file");
+
+ if (InputBinaries.front().getBinary()->isMachO())
+ reportError("input file " +
+ InputBinaries.front().getBinary()->getFileName() +
+ " must be a fat file when the -replace option is specified");
+
+ SmallVector<OwningBinary<Binary>, 1> ReplacementBinaries =
+ readInputBinaries(ReplacementFiles);
+
+ StringMap<Slice> ReplacementSlices =
+ buildReplacementSlices(ReplacementBinaries, Alignments);
+ SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;
+ SmallVector<Slice, 2> Slices =
+ buildSlices(InputBinaries, Alignments, ExtractedObjects);
+
+ for (auto &Slice : Slices) {
+ auto It = ReplacementSlices.find(Slice.getArchString());
+ if (It != ReplacementSlices.end()) {
+ Slice = It->second;
+ ReplacementSlices.erase(It); // only keep remaining replacing arch_types
+ }
+ }
+
+ if (!ReplacementSlices.empty())
+ reportError("-replace " + ReplacementSlices.begin()->first() +
+ " <file_name> specified but fat file: " +
+ InputBinaries.front().getBinary()->getFileName() +
+ " does not contain that architecture");
+
+ checkUnusedAlignments(Slices, Alignments);
+
+ llvm::stable_sort(Slices);
+ if (Error E = writeUniversalBinary(Slices, OutputFileName))
+ reportError(std::move(E));
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ Config C = parseLipoOptions(makeArrayRef(argv + 1, argc));
+ SmallVector<OwningBinary<Binary>, 1> InputBinaries =
+ readInputBinaries(C.InputFiles);
+
+ switch (C.ActionToPerform) {
+ case LipoAction::VerifyArch:
+ verifyArch(InputBinaries, C.VerifyArchList);
+ break;
+ case LipoAction::PrintArchs:
+ printArchs(InputBinaries);
+ break;
+ case LipoAction::PrintInfo:
+ printInfo(InputBinaries);
+ break;
+ case LipoAction::ThinArch:
+ thinSlice(InputBinaries, C.ArchType, C.OutputFile);
+ break;
+ case LipoAction::ExtractArch:
+ extractSlice(InputBinaries, C.SegmentAlignments, C.ArchType, C.OutputFile);
+ break;
+ case LipoAction::CreateUniversal:
+ createUniversalBinary(InputBinaries, C.SegmentAlignments, C.OutputFile);
+ break;
+ case LipoAction::ReplaceArch:
+ replaceSlices(InputBinaries, C.SegmentAlignments, C.OutputFile,
+ C.ReplacementFiles);
+ break;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-lipo/ya.make b/contrib/libs/llvm14/tools/llvm-lipo/ya.make
new file mode 100644
index 00000000000..4b5ff49a4cb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-lipo/ya.make
@@ -0,0 +1,69 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-lipo
+ contrib/libs/llvm14/tools/llvm-lipo
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-lipo.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-lto/llvm-lto.cpp b/contrib/libs/llvm14/tools/llvm-lto/llvm-lto.cpp
new file mode 100644
index 00000000000..8fc3a5d6850
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-lto/llvm-lto.cpp
@@ -0,0 +1,1134 @@
+//===- llvm-lto: a simple command-line program to link modules with LTO ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program takes in a list of bitcode files, links them, performs link-time
+// optimization, and outputs an object file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-c/lto.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/ModuleSummaryIndex.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/LTO/legacy/LTOCodeGenerator.h"
+#include "llvm/LTO/legacy/LTOModule.h"
+#include "llvm/LTO/legacy/ThinLTOCodeGenerator.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Target/TargetOptions.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <cstdlib>
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+using namespace llvm;
+
+static codegen::RegisterCodeGenFlags CGF;
+
+static cl::OptionCategory LTOCategory("LTO Options");
+
+static cl::opt<char>
+ OptLevel("O",
+ cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
+ "(default = '-O2')"),
+ cl::Prefix, cl::ZeroOrMore, cl::init('2'), cl::cat(LTOCategory));
+
+static cl::opt<bool>
+ IndexStats("thinlto-index-stats",
+ cl::desc("Print statistic for the index in every input files"),
+ cl::init(false), cl::cat(LTOCategory));
+
+static cl::opt<bool> DisableVerify(
+ "disable-verify", cl::init(false),
+ cl::desc("Do not run the verifier during the optimization pipeline"),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool> EnableFreestanding(
+ "lto-freestanding", cl::init(false),
+ cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool> UseDiagnosticHandler(
+ "use-diagnostic-handler", cl::init(false),
+ cl::desc("Use a diagnostic handler to test the handler interface"),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool>
+ ThinLTO("thinlto", cl::init(false),
+ cl::desc("Only write combined global index for ThinLTO backends"),
+ cl::cat(LTOCategory));
+
+enum ThinLTOModes {
+ THINLINK,
+ THINDISTRIBUTE,
+ THINEMITIMPORTS,
+ THINPROMOTE,
+ THINIMPORT,
+ THININTERNALIZE,
+ THINOPT,
+ THINCODEGEN,
+ THINALL
+};
+
+cl::opt<ThinLTOModes> ThinLTOMode(
+ "thinlto-action", cl::desc("Perform a single ThinLTO stage:"),
+ cl::values(
+ clEnumValN(
+ THINLINK, "thinlink",
+ "ThinLink: produces the index by linking only the summaries."),
+ clEnumValN(THINDISTRIBUTE, "distributedindexes",
+ "Produces individual indexes for distributed backends."),
+ clEnumValN(THINEMITIMPORTS, "emitimports",
+ "Emit imports files for distributed backends."),
+ clEnumValN(THINPROMOTE, "promote",
+ "Perform pre-import promotion (requires -thinlto-index)."),
+ clEnumValN(THINIMPORT, "import",
+ "Perform both promotion and "
+ "cross-module importing (requires "
+ "-thinlto-index)."),
+ clEnumValN(THININTERNALIZE, "internalize",
+ "Perform internalization driven by -exported-symbol "
+ "(requires -thinlto-index)."),
+ clEnumValN(THINOPT, "optimize", "Perform ThinLTO optimizations."),
+ clEnumValN(THINCODEGEN, "codegen", "CodeGen (expected to match llc)"),
+ clEnumValN(THINALL, "run", "Perform ThinLTO end-to-end")),
+ cl::cat(LTOCategory));
+
+static cl::opt<std::string>
+ ThinLTOIndex("thinlto-index",
+ cl::desc("Provide the index produced by a ThinLink, required "
+ "to perform the promotion and/or importing."),
+ cl::cat(LTOCategory));
+
+static cl::opt<std::string> ThinLTOPrefixReplace(
+ "thinlto-prefix-replace",
+ cl::desc("Control where files for distributed backends are "
+ "created. Expects 'oldprefix;newprefix' and if path "
+ "prefix of output file is oldprefix it will be "
+ "replaced with newprefix."),
+ cl::cat(LTOCategory));
+
+static cl::opt<std::string> ThinLTOModuleId(
+ "thinlto-module-id",
+ cl::desc("For the module ID for the file to process, useful to "
+ "match what is in the index."),
+ cl::cat(LTOCategory));
+
+static cl::opt<std::string> ThinLTOCacheDir("thinlto-cache-dir",
+ cl::desc("Enable ThinLTO caching."),
+ cl::cat(LTOCategory));
+
+static cl::opt<int> ThinLTOCachePruningInterval(
+ "thinlto-cache-pruning-interval", cl::init(1200),
+ cl::desc("Set ThinLTO cache pruning interval."), cl::cat(LTOCategory));
+
+static cl::opt<uint64_t> ThinLTOCacheMaxSizeBytes(
+ "thinlto-cache-max-size-bytes",
+ cl::desc("Set ThinLTO cache pruning directory maximum size in bytes."),
+ cl::cat(LTOCategory));
+
+static cl::opt<int> ThinLTOCacheMaxSizeFiles(
+ "thinlto-cache-max-size-files", cl::init(1000000),
+ cl::desc("Set ThinLTO cache pruning directory maximum number of files."),
+ cl::cat(LTOCategory));
+
+static cl::opt<unsigned> ThinLTOCacheEntryExpiration(
+ "thinlto-cache-entry-expiration", cl::init(604800) /* 1w */,
+ cl::desc("Set ThinLTO cache entry expiration time."), cl::cat(LTOCategory));
+
+static cl::opt<std::string> ThinLTOSaveTempsPrefix(
+ "thinlto-save-temps",
+ cl::desc("Save ThinLTO temp files using filenames created by adding "
+ "suffixes to the given file path prefix."),
+ cl::cat(LTOCategory));
+
+static cl::opt<std::string> ThinLTOGeneratedObjectsDir(
+ "thinlto-save-objects",
+ cl::desc("Save ThinLTO generated object files using filenames created in "
+ "the given directory."),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool> SaveLinkedModuleFile(
+ "save-linked-module", cl::init(false),
+ cl::desc("Write linked LTO module to file before optimize"),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool>
+ SaveModuleFile("save-merged-module", cl::init(false),
+ cl::desc("Write merged LTO module to file before CodeGen"),
+ cl::cat(LTOCategory));
+
+static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
+ cl::desc("<input bitcode files>"),
+ cl::cat(LTOCategory));
+
+static cl::opt<std::string> OutputFilename("o", cl::init(""),
+ cl::desc("Override output filename"),
+ cl::value_desc("filename"),
+ cl::cat(LTOCategory));
+
+static cl::list<std::string> ExportedSymbols(
+ "exported-symbol",
+ cl::desc("List of symbols to export from the resulting object file"),
+ cl::ZeroOrMore, cl::cat(LTOCategory));
+
+static cl::list<std::string>
+ DSOSymbols("dso-symbol",
+ cl::desc("Symbol to put in the symtab in the resulting dso"),
+ cl::ZeroOrMore, cl::cat(LTOCategory));
+
+static cl::opt<bool> ListSymbolsOnly(
+ "list-symbols-only", cl::init(false),
+ cl::desc("Instead of running LTO, list the symbols in each IR file"),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool> ListDependentLibrariesOnly(
+ "list-dependent-libraries-only", cl::init(false),
+ cl::desc(
+ "Instead of running LTO, list the dependent libraries in each IR file"),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool> QueryHasCtorDtor(
+ "query-hasCtorDtor", cl::init(false),
+ cl::desc("Queries LTOModule::hasCtorDtor() on each IR file"));
+
+static cl::opt<bool>
+ SetMergedModule("set-merged-module", cl::init(false),
+ cl::desc("Use the first input module as the merged module"),
+ cl::cat(LTOCategory));
+
+static cl::opt<unsigned> Parallelism("j", cl::Prefix, cl::init(1),
+ cl::desc("Number of backend threads"),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool> RestoreGlobalsLinkage(
+ "restore-linkage", cl::init(false),
+ cl::desc("Restore original linkage of globals prior to CodeGen"),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool> CheckHasObjC(
+ "check-for-objc", cl::init(false),
+ cl::desc("Only check if the module has objective-C defined in it"),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool> PrintMachOCPUOnly(
+ "print-macho-cpu-only", cl::init(false),
+ cl::desc("Instead of running LTO, print the mach-o cpu in each IR file"),
+ cl::cat(LTOCategory));
+
+static cl::opt<bool> UseNewPM(
+ "use-new-pm", cl::desc("Run LTO passes using the new pass manager"),
+ cl::init(LLVM_ENABLE_NEW_PASS_MANAGER), cl::Hidden, cl::cat(LTOCategory));
+
+static cl::opt<bool>
+ DebugPassManager("debug-pass-manager", cl::init(false), cl::Hidden,
+ cl::desc("Print pass management debugging information"),
+ cl::cat(LTOCategory));
+
+namespace {
+
+struct ModuleInfo {
+ BitVector CanBeHidden;
+};
+
+} // end anonymous namespace
+
+static void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity,
+ const char *Msg, void *) {
+ errs() << "llvm-lto: ";
+ switch (Severity) {
+ case LTO_DS_NOTE:
+ errs() << "note: ";
+ break;
+ case LTO_DS_REMARK:
+ errs() << "remark: ";
+ break;
+ case LTO_DS_ERROR:
+ errs() << "error: ";
+ break;
+ case LTO_DS_WARNING:
+ errs() << "warning: ";
+ break;
+ }
+ errs() << Msg << "\n";
+}
+
+static std::string CurrentActivity;
+
+namespace {
+ struct LLVMLTODiagnosticHandler : public DiagnosticHandler {
+ bool handleDiagnostics(const DiagnosticInfo &DI) override {
+ raw_ostream &OS = errs();
+ OS << "llvm-lto: ";
+ switch (DI.getSeverity()) {
+ case DS_Error:
+ OS << "error";
+ break;
+ case DS_Warning:
+ OS << "warning";
+ break;
+ case DS_Remark:
+ OS << "remark";
+ break;
+ case DS_Note:
+ OS << "note";
+ break;
+ }
+ if (!CurrentActivity.empty())
+ OS << ' ' << CurrentActivity;
+ OS << ": ";
+
+ DiagnosticPrinterRawOStream DP(OS);
+ DI.print(DP);
+ OS << '\n';
+
+ if (DI.getSeverity() == DS_Error)
+ exit(1);
+ return true;
+ }
+ };
+ }
+
+static void error(const Twine &Msg) {
+ errs() << "llvm-lto: " << Msg << '\n';
+ exit(1);
+}
+
+static void error(std::error_code EC, const Twine &Prefix) {
+ if (EC)
+ error(Prefix + ": " + EC.message());
+}
+
+template <typename T>
+static void error(const ErrorOr<T> &V, const Twine &Prefix) {
+ error(V.getError(), Prefix);
+}
+
+static void maybeVerifyModule(const Module &Mod) {
+ if (!DisableVerify && verifyModule(Mod, &errs()))
+ error("Broken Module");
+}
+
+static std::unique_ptr<LTOModule>
+getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer,
+ const TargetOptions &Options) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+ MemoryBuffer::getFile(Path);
+ error(BufferOrErr, "error loading file '" + Path + "'");
+ Buffer = std::move(BufferOrErr.get());
+ CurrentActivity = ("loading file '" + Path + "'").str();
+ std::unique_ptr<LLVMContext> Context = std::make_unique<LLVMContext>();
+ Context->setDiagnosticHandler(std::make_unique<LLVMLTODiagnosticHandler>(),
+ true);
+ ErrorOr<std::unique_ptr<LTOModule>> Ret = LTOModule::createInLocalContext(
+ std::move(Context), Buffer->getBufferStart(), Buffer->getBufferSize(),
+ Options, Path);
+ CurrentActivity = "";
+ maybeVerifyModule((*Ret)->getModule());
+ return std::move(*Ret);
+}
+
+/// Print some statistics on the index for each input files.
+static void printIndexStats() {
+ for (auto &Filename : InputFilenames) {
+ ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': ");
+ std::unique_ptr<ModuleSummaryIndex> Index =
+ ExitOnErr(getModuleSummaryIndexForFile(Filename));
+ // Skip files without a module summary.
+ if (!Index)
+ report_fatal_error(Twine(Filename) + " does not contain an index");
+
+ unsigned Calls = 0, Refs = 0, Functions = 0, Alias = 0, Globals = 0;
+ for (auto &Summaries : *Index) {
+ for (auto &Summary : Summaries.second.SummaryList) {
+ Refs += Summary->refs().size();
+ if (auto *FuncSummary = dyn_cast<FunctionSummary>(Summary.get())) {
+ Functions++;
+ Calls += FuncSummary->calls().size();
+ } else if (isa<AliasSummary>(Summary.get()))
+ Alias++;
+ else
+ Globals++;
+ }
+ }
+ outs() << "Index " << Filename << " contains "
+ << (Alias + Globals + Functions) << " nodes (" << Functions
+ << " functions, " << Alias << " alias, " << Globals
+ << " globals) and " << (Calls + Refs) << " edges (" << Refs
+ << " refs and " << Calls << " calls)\n";
+ }
+}
+
+/// Load each IR file and dump certain information based on active flags.
+///
+/// The main point here is to provide lit-testable coverage for the LTOModule
+/// functionality that's exposed by the C API. Moreover, this provides testing
+/// coverage for modules that have been created in their own contexts.
+static void testLTOModule(const TargetOptions &Options) {
+ for (auto &Filename : InputFilenames) {
+ std::unique_ptr<MemoryBuffer> Buffer;
+ std::unique_ptr<LTOModule> Module =
+ getLocalLTOModule(Filename, Buffer, Options);
+
+ if (ListSymbolsOnly) {
+ // List the symbols.
+ outs() << Filename << ":\n";
+ for (int I = 0, E = Module->getSymbolCount(); I != E; ++I)
+ outs() << Module->getSymbolName(I) << "\n";
+ }
+ if (QueryHasCtorDtor)
+ outs() << Filename
+ << ": hasCtorDtor = " << (Module->hasCtorDtor() ? "true" : "false")
+ << "\n";
+ }
+}
+
+static std::unique_ptr<MemoryBuffer> loadFile(StringRef Filename) {
+ ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename.str() +
+ "': ");
+ return ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Filename)));
+}
+
+static void listDependentLibraries() {
+ for (auto &Filename : InputFilenames) {
+ auto Buffer = loadFile(Filename);
+ std::string E;
+ std::unique_ptr<lto::InputFile> Input(LTOModule::createInputFile(
+ Buffer->getBufferStart(), Buffer->getBufferSize(), Filename.c_str(),
+ E));
+ if (!Input)
+ error(E);
+
+ // List the dependent libraries.
+ outs() << Filename << ":\n";
+ for (size_t I = 0, C = LTOModule::getDependentLibraryCount(Input.get());
+ I != C; ++I) {
+ size_t L = 0;
+ const char *S = LTOModule::getDependentLibrary(Input.get(), I, &L);
+ assert(S);
+ outs() << StringRef(S, L) << "\n";
+ }
+ }
+}
+
+static void printMachOCPUOnly() {
+ LLVMContext Context;
+ Context.setDiagnosticHandler(std::make_unique<LLVMLTODiagnosticHandler>(),
+ true);
+ TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+ for (auto &Filename : InputFilenames) {
+ ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr =
+ LTOModule::createFromFile(Context, Filename, Options);
+ if (!ModuleOrErr)
+ error(ModuleOrErr, "llvm-lto: ");
+
+ Expected<uint32_t> CPUType = (*ModuleOrErr)->getMachOCPUType();
+ Expected<uint32_t> CPUSubType = (*ModuleOrErr)->getMachOCPUSubType();
+ if (!CPUType)
+ error("Error while printing mach-o cputype: " +
+ toString(CPUType.takeError()));
+ if (!CPUSubType)
+ error("Error while printing mach-o cpusubtype: " +
+ toString(CPUSubType.takeError()));
+ outs() << llvm::format("%s:\ncputype: %u\ncpusubtype: %u\n",
+ Filename.c_str(), *CPUType, *CPUSubType);
+ }
+}
+
+/// Create a combined index file from the input IR files and write it.
+///
+/// This is meant to enable testing of ThinLTO combined index generation,
+/// currently available via the gold plugin via -thinlto.
+static void createCombinedModuleSummaryIndex() {
+ ModuleSummaryIndex CombinedIndex(/*HaveGVs=*/false);
+ uint64_t NextModuleId = 0;
+ for (auto &Filename : InputFilenames) {
+ ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': ");
+ std::unique_ptr<MemoryBuffer> MB =
+ ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Filename)));
+ ExitOnErr(readModuleSummaryIndex(*MB, CombinedIndex, NextModuleId++));
+ }
+ // In order to use this index for testing, specifically import testing, we
+ // need to update any indirect call edges created from SamplePGO, so that they
+ // point to the correct GUIDs.
+ updateIndirectCalls(CombinedIndex);
+ std::error_code EC;
+ assert(!OutputFilename.empty());
+ raw_fd_ostream OS(OutputFilename + ".thinlto.bc", EC,
+ sys::fs::OpenFlags::OF_None);
+ error(EC, "error opening the file '" + OutputFilename + ".thinlto.bc'");
+ writeIndexToFile(CombinedIndex, OS);
+ OS.close();
+}
+
+/// Parse the thinlto_prefix_replace option into the \p OldPrefix and
+/// \p NewPrefix strings, if it was specified.
+static void getThinLTOOldAndNewPrefix(std::string &OldPrefix,
+ std::string &NewPrefix) {
+ assert(ThinLTOPrefixReplace.empty() ||
+ ThinLTOPrefixReplace.find(';') != StringRef::npos);
+ StringRef PrefixReplace = ThinLTOPrefixReplace;
+ std::pair<StringRef, StringRef> Split = PrefixReplace.split(";");
+ OldPrefix = Split.first.str();
+ NewPrefix = Split.second.str();
+}
+
+/// Given the original \p Path to an output file, replace any path
+/// prefix matching \p OldPrefix with \p NewPrefix. Also, create the
+/// resulting directory if it does not yet exist.
+static std::string getThinLTOOutputFile(const std::string &Path,
+ const std::string &OldPrefix,
+ const std::string &NewPrefix) {
+ if (OldPrefix.empty() && NewPrefix.empty())
+ return Path;
+ SmallString<128> NewPath(Path);
+ llvm::sys::path::replace_path_prefix(NewPath, OldPrefix, NewPrefix);
+ StringRef ParentPath = llvm::sys::path::parent_path(NewPath.str());
+ if (!ParentPath.empty()) {
+ // Make sure the new directory exists, creating it if necessary.
+ if (std::error_code EC = llvm::sys::fs::create_directories(ParentPath))
+ error(EC, "error creating the directory '" + ParentPath + "'");
+ }
+ return std::string(NewPath.str());
+}
+
+namespace thinlto {
+
+std::vector<std::unique_ptr<MemoryBuffer>>
+loadAllFilesForIndex(const ModuleSummaryIndex &Index) {
+ std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
+
+ for (auto &ModPath : Index.modulePaths()) {
+ const auto &Filename = ModPath.first();
+ std::string CurrentActivity = ("loading file '" + Filename + "'").str();
+ auto InputOrErr = MemoryBuffer::getFile(Filename);
+ error(InputOrErr, "error " + CurrentActivity);
+ InputBuffers.push_back(std::move(*InputOrErr));
+ }
+ return InputBuffers;
+}
+
+std::unique_ptr<ModuleSummaryIndex> loadCombinedIndex() {
+ if (ThinLTOIndex.empty())
+ report_fatal_error("Missing -thinlto-index for ThinLTO promotion stage");
+ ExitOnError ExitOnErr("llvm-lto: error loading file '" + ThinLTOIndex +
+ "': ");
+ return ExitOnErr(getModuleSummaryIndexForFile(ThinLTOIndex));
+}
+
+static std::unique_ptr<lto::InputFile> loadInputFile(MemoryBufferRef Buffer) {
+ ExitOnError ExitOnErr("llvm-lto: error loading input '" +
+ Buffer.getBufferIdentifier().str() + "': ");
+ return ExitOnErr(lto::InputFile::create(Buffer));
+}
+
+static std::unique_ptr<Module> loadModuleFromInput(lto::InputFile &File,
+ LLVMContext &CTX) {
+ auto &Mod = File.getSingleBitcodeModule();
+ auto ModuleOrErr = Mod.parseModule(CTX);
+ if (!ModuleOrErr) {
+ handleAllErrors(ModuleOrErr.takeError(), [&](ErrorInfoBase &EIB) {
+ SMDiagnostic Err = SMDiagnostic(Mod.getModuleIdentifier(),
+ SourceMgr::DK_Error, EIB.message());
+ Err.print("llvm-lto", errs());
+ });
+ report_fatal_error("Can't load module, abort.");
+ }
+ maybeVerifyModule(**ModuleOrErr);
+ if (ThinLTOModuleId.getNumOccurrences()) {
+ if (InputFilenames.size() != 1)
+ report_fatal_error("Can't override the module id for multiple files");
+ (*ModuleOrErr)->setModuleIdentifier(ThinLTOModuleId);
+ }
+ return std::move(*ModuleOrErr);
+}
+
+static void writeModuleToFile(Module &TheModule, StringRef Filename) {
+ std::error_code EC;
+ raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::OF_None);
+ error(EC, "error opening the file '" + Filename + "'");
+ maybeVerifyModule(TheModule);
+ WriteBitcodeToFile(TheModule, OS, /* ShouldPreserveUseListOrder */ true);
+}
+
+class ThinLTOProcessing {
+public:
+ ThinLTOCodeGenerator ThinGenerator;
+
+ ThinLTOProcessing(const TargetOptions &Options) {
+ ThinGenerator.setCodePICModel(codegen::getExplicitRelocModel());
+ ThinGenerator.setTargetOptions(Options);
+ ThinGenerator.setCacheDir(ThinLTOCacheDir);
+ ThinGenerator.setCachePruningInterval(ThinLTOCachePruningInterval);
+ ThinGenerator.setCacheEntryExpiration(ThinLTOCacheEntryExpiration);
+ ThinGenerator.setCacheMaxSizeFiles(ThinLTOCacheMaxSizeFiles);
+ ThinGenerator.setCacheMaxSizeBytes(ThinLTOCacheMaxSizeBytes);
+ ThinGenerator.setFreestanding(EnableFreestanding);
+ ThinGenerator.setUseNewPM(UseNewPM);
+ ThinGenerator.setDebugPassManager(DebugPassManager);
+
+ // Add all the exported symbols to the table of symbols to preserve.
+ for (unsigned i = 0; i < ExportedSymbols.size(); ++i)
+ ThinGenerator.preserveSymbol(ExportedSymbols[i]);
+ }
+
+ void run() {
+ switch (ThinLTOMode) {
+ case THINLINK:
+ return thinLink();
+ case THINDISTRIBUTE:
+ return distributedIndexes();
+ case THINEMITIMPORTS:
+ return emitImports();
+ case THINPROMOTE:
+ return promote();
+ case THINIMPORT:
+ return import();
+ case THININTERNALIZE:
+ return internalize();
+ case THINOPT:
+ return optimize();
+ case THINCODEGEN:
+ return codegen();
+ case THINALL:
+ return runAll();
+ }
+ }
+
+private:
+ /// Load the input files, create the combined index, and write it out.
+ void thinLink() {
+ // Perform "ThinLink": just produce the index
+ if (OutputFilename.empty())
+ report_fatal_error(
+ "OutputFilename is necessary to store the combined index.\n");
+
+ LLVMContext Ctx;
+ std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
+ for (unsigned i = 0; i < InputFilenames.size(); ++i) {
+ auto &Filename = InputFilenames[i];
+ std::string CurrentActivity = "loading file '" + Filename + "'";
+ auto InputOrErr = MemoryBuffer::getFile(Filename);
+ error(InputOrErr, "error " + CurrentActivity);
+ InputBuffers.push_back(std::move(*InputOrErr));
+ ThinGenerator.addModule(Filename, InputBuffers.back()->getBuffer());
+ }
+
+ auto CombinedIndex = ThinGenerator.linkCombinedIndex();
+ if (!CombinedIndex)
+ report_fatal_error("ThinLink didn't create an index");
+ std::error_code EC;
+ raw_fd_ostream OS(OutputFilename, EC, sys::fs::OpenFlags::OF_None);
+ error(EC, "error opening the file '" + OutputFilename + "'");
+ writeIndexToFile(*CombinedIndex, OS);
+ }
+
+ /// Load the combined index from disk, then compute and generate
+ /// individual index files suitable for ThinLTO distributed backend builds
+ /// on the files mentioned on the command line (these must match the index
+ /// content).
+ void distributedIndexes() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+
+ std::string OldPrefix, NewPrefix;
+ getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix);
+
+ auto Index = loadCombinedIndex();
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto Buffer = loadFile(Filename);
+ auto Input = loadInputFile(Buffer->getMemBufferRef());
+ auto TheModule = loadModuleFromInput(*Input, 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;
+ ThinGenerator.gatherImportedSummariesForModule(
+ *TheModule, *Index, ModuleToSummariesForIndex, *Input);
+
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".thinlto.bc";
+ }
+ OutputName = getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix);
+ std::error_code EC;
+ raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None);
+ error(EC, "error opening the file '" + OutputName + "'");
+ writeIndexToFile(*Index, OS, &ModuleToSummariesForIndex);
+ }
+ }
+
+ /// Load the combined index from disk, compute the imports, and emit
+ /// the import file lists for each module to disk.
+ void emitImports() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+
+ std::string OldPrefix, NewPrefix;
+ getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix);
+
+ auto Index = loadCombinedIndex();
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto Buffer = loadFile(Filename);
+ auto Input = loadInputFile(Buffer->getMemBufferRef());
+ auto TheModule = loadModuleFromInput(*Input, Ctx);
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".imports";
+ }
+ OutputName =
+ getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix);
+ ThinGenerator.emitImports(*TheModule, OutputName, *Index, *Input);
+ }
+ }
+
+ /// Load the combined index from disk, then load every file referenced by
+ /// the index and add them to the generator, finally perform the promotion
+ /// on the files mentioned on the command line (these must match the index
+ /// content).
+ void promote() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+
+ auto Index = loadCombinedIndex();
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto Buffer = loadFile(Filename);
+ auto Input = loadInputFile(Buffer->getMemBufferRef());
+ auto TheModule = loadModuleFromInput(*Input, Ctx);
+
+ ThinGenerator.promote(*TheModule, *Index, *Input);
+
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".thinlto.promoted.bc";
+ }
+ writeModuleToFile(*TheModule, OutputName);
+ }
+ }
+
+ /// Load the combined index from disk, then load every file referenced by
+ /// the index and add them to the generator, then performs the promotion and
+ /// cross module importing on the files mentioned on the command line
+ /// (these must match the index content).
+ void import() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+
+ auto Index = loadCombinedIndex();
+ auto InputBuffers = loadAllFilesForIndex(*Index);
+ for (auto &MemBuffer : InputBuffers)
+ ThinGenerator.addModule(MemBuffer->getBufferIdentifier(),
+ MemBuffer->getBuffer());
+
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto Buffer = loadFile(Filename);
+ auto Input = loadInputFile(Buffer->getMemBufferRef());
+ auto TheModule = loadModuleFromInput(*Input, Ctx);
+
+ ThinGenerator.crossModuleImport(*TheModule, *Index, *Input);
+
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".thinlto.imported.bc";
+ }
+ writeModuleToFile(*TheModule, OutputName);
+ }
+ }
+
+ void internalize() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+
+ if (ExportedSymbols.empty())
+ errs() << "Warning: -internalize will not perform without "
+ "-exported-symbol\n";
+
+ auto Index = loadCombinedIndex();
+ auto InputBuffers = loadAllFilesForIndex(*Index);
+ for (auto &MemBuffer : InputBuffers)
+ ThinGenerator.addModule(MemBuffer->getBufferIdentifier(),
+ MemBuffer->getBuffer());
+
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto Buffer = loadFile(Filename);
+ auto Input = loadInputFile(Buffer->getMemBufferRef());
+ auto TheModule = loadModuleFromInput(*Input, Ctx);
+
+ ThinGenerator.internalize(*TheModule, *Index, *Input);
+
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".thinlto.internalized.bc";
+ }
+ writeModuleToFile(*TheModule, OutputName);
+ }
+ }
+
+ void optimize() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+ if (!ThinLTOIndex.empty())
+ errs() << "Warning: -thinlto-index ignored for optimize stage";
+
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto Buffer = loadFile(Filename);
+ auto Input = loadInputFile(Buffer->getMemBufferRef());
+ auto TheModule = loadModuleFromInput(*Input, Ctx);
+
+ ThinGenerator.optimize(*TheModule);
+
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty()) {
+ OutputName = Filename + ".thinlto.imported.bc";
+ }
+ writeModuleToFile(*TheModule, OutputName);
+ }
+ }
+
+ void codegen() {
+ if (InputFilenames.size() != 1 && !OutputFilename.empty())
+ report_fatal_error("Can't handle a single output filename and multiple "
+ "input files, do not provide an output filename and "
+ "the output files will be suffixed from the input "
+ "ones.");
+ if (!ThinLTOIndex.empty())
+ errs() << "Warning: -thinlto-index ignored for codegen stage";
+
+ std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
+ for (auto &Filename : InputFilenames) {
+ LLVMContext Ctx;
+ auto InputOrErr = MemoryBuffer::getFile(Filename);
+ error(InputOrErr, "error " + CurrentActivity);
+ InputBuffers.push_back(std::move(*InputOrErr));
+ ThinGenerator.addModule(Filename, InputBuffers.back()->getBuffer());
+ }
+ ThinGenerator.setCodeGenOnly(true);
+ ThinGenerator.run();
+ for (auto BinName :
+ zip(ThinGenerator.getProducedBinaries(), InputFilenames)) {
+ std::string OutputName = OutputFilename;
+ if (OutputName.empty())
+ OutputName = std::get<1>(BinName) + ".thinlto.o";
+ else if (OutputName == "-") {
+ outs() << std::get<0>(BinName)->getBuffer();
+ return;
+ }
+
+ std::error_code EC;
+ raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None);
+ error(EC, "error opening the file '" + OutputName + "'");
+ OS << std::get<0>(BinName)->getBuffer();
+ }
+ }
+
+ /// Full ThinLTO process
+ void runAll() {
+ if (!OutputFilename.empty())
+ report_fatal_error("Do not provide an output filename for ThinLTO "
+ " processing, the output files will be suffixed from "
+ "the input ones.");
+
+ if (!ThinLTOIndex.empty())
+ errs() << "Warning: -thinlto-index ignored for full ThinLTO process";
+
+ LLVMContext Ctx;
+ std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
+ for (unsigned i = 0; i < InputFilenames.size(); ++i) {
+ auto &Filename = InputFilenames[i];
+ std::string CurrentActivity = "loading file '" + Filename + "'";
+ auto InputOrErr = MemoryBuffer::getFile(Filename);
+ error(InputOrErr, "error " + CurrentActivity);
+ InputBuffers.push_back(std::move(*InputOrErr));
+ ThinGenerator.addModule(Filename, InputBuffers.back()->getBuffer());
+ }
+
+ if (!ThinLTOSaveTempsPrefix.empty())
+ ThinGenerator.setSaveTempsDir(ThinLTOSaveTempsPrefix);
+
+ if (!ThinLTOGeneratedObjectsDir.empty()) {
+ ThinGenerator.setGeneratedObjectsDirectory(ThinLTOGeneratedObjectsDir);
+ ThinGenerator.run();
+ return;
+ }
+
+ ThinGenerator.run();
+
+ auto &Binaries = ThinGenerator.getProducedBinaries();
+ if (Binaries.size() != InputFilenames.size())
+ report_fatal_error("Number of output objects does not match the number "
+ "of inputs");
+
+ for (unsigned BufID = 0; BufID < Binaries.size(); ++BufID) {
+ auto OutputName = InputFilenames[BufID] + ".thinlto.o";
+ std::error_code EC;
+ raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None);
+ error(EC, "error opening the file '" + OutputName + "'");
+ OS << Binaries[BufID]->getBuffer();
+ }
+ }
+
+ /// Load the combined index from disk, then load every file referenced by
+};
+
+} // end namespace thinlto
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ cl::HideUnrelatedOptions({&LTOCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "llvm LTO linker\n");
+
+ if (OptLevel < '0' || OptLevel > '3')
+ error("optimization level must be between 0 and 3");
+
+ // Initialize the configured targets.
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmPrinters();
+ InitializeAllAsmParsers();
+
+ // set up the TargetOptions for the machine
+ TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+
+ if (ListSymbolsOnly || QueryHasCtorDtor) {
+ testLTOModule(Options);
+ return 0;
+ }
+
+ if (ListDependentLibrariesOnly) {
+ listDependentLibraries();
+ return 0;
+ }
+
+ if (IndexStats) {
+ printIndexStats();
+ return 0;
+ }
+
+ if (CheckHasObjC) {
+ for (auto &Filename : InputFilenames) {
+ ExitOnError ExitOnErr(std::string(*argv) + ": error loading file '" +
+ Filename + "': ");
+ std::unique_ptr<MemoryBuffer> BufferOrErr =
+ ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(Filename)));
+ auto Buffer = std::move(BufferOrErr.get());
+ if (ExitOnErr(isBitcodeContainingObjCCategory(*Buffer)))
+ outs() << "Bitcode " << Filename << " contains ObjC\n";
+ else
+ outs() << "Bitcode " << Filename << " does not contain ObjC\n";
+ }
+ return 0;
+ }
+
+ if (PrintMachOCPUOnly) {
+ printMachOCPUOnly();
+ return 0;
+ }
+
+ if (ThinLTOMode.getNumOccurrences()) {
+ if (ThinLTOMode.getNumOccurrences() > 1)
+ report_fatal_error("You can't specify more than one -thinlto-action");
+ thinlto::ThinLTOProcessing ThinLTOProcessor(Options);
+ ThinLTOProcessor.run();
+ return 0;
+ }
+
+ if (ThinLTO) {
+ createCombinedModuleSummaryIndex();
+ return 0;
+ }
+
+ unsigned BaseArg = 0;
+
+ LLVMContext Context;
+ Context.setDiagnosticHandler(std::make_unique<LLVMLTODiagnosticHandler>(),
+ true);
+
+ LTOCodeGenerator CodeGen(Context);
+ CodeGen.setDisableVerify(DisableVerify);
+
+ if (UseDiagnosticHandler)
+ CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr);
+
+ CodeGen.setCodePICModel(codegen::getExplicitRelocModel());
+ CodeGen.setFreestanding(EnableFreestanding);
+
+ CodeGen.setDebugInfo(LTO_DEBUG_MODEL_DWARF);
+ CodeGen.setTargetOptions(Options);
+ CodeGen.setShouldRestoreGlobalsLinkage(RestoreGlobalsLinkage);
+
+ StringSet<MallocAllocator> DSOSymbolsSet;
+ for (unsigned i = 0; i < DSOSymbols.size(); ++i)
+ DSOSymbolsSet.insert(DSOSymbols[i]);
+
+ std::vector<std::string> KeptDSOSyms;
+
+ for (unsigned i = BaseArg; i < InputFilenames.size(); ++i) {
+ CurrentActivity = "loading file '" + InputFilenames[i] + "'";
+ ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr =
+ LTOModule::createFromFile(Context, InputFilenames[i], Options);
+ std::unique_ptr<LTOModule> &Module = *ModuleOrErr;
+ CurrentActivity = "";
+
+ unsigned NumSyms = Module->getSymbolCount();
+ for (unsigned I = 0; I < NumSyms; ++I) {
+ StringRef Name = Module->getSymbolName(I);
+ if (!DSOSymbolsSet.count(Name))
+ continue;
+ lto_symbol_attributes Attrs = Module->getSymbolAttributes(I);
+ unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK;
+ if (Scope != LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN)
+ KeptDSOSyms.push_back(std::string(Name));
+ }
+
+ // We use the first input module as the destination module when
+ // SetMergedModule is true.
+ if (SetMergedModule && i == BaseArg) {
+ // Transfer ownership to the code generator.
+ CodeGen.setModule(std::move(Module));
+ } else if (!CodeGen.addModule(Module.get())) {
+ // Print a message here so that we know addModule() did not abort.
+ error("error adding file '" + InputFilenames[i] + "'");
+ }
+ }
+
+ // Add all the exported symbols to the table of symbols to preserve.
+ for (unsigned i = 0; i < ExportedSymbols.size(); ++i)
+ CodeGen.addMustPreserveSymbol(ExportedSymbols[i]);
+
+ // Add all the dso symbols to the table of symbols to expose.
+ for (unsigned i = 0; i < KeptDSOSyms.size(); ++i)
+ CodeGen.addMustPreserveSymbol(KeptDSOSyms[i]);
+
+ // Set cpu and attrs strings for the default target/subtarget.
+ CodeGen.setCpu(codegen::getMCPU());
+
+ CodeGen.setOptLevel(OptLevel - '0');
+ CodeGen.setAttrs(codegen::getMAttrs());
+
+ CodeGen.setUseNewPM(UseNewPM);
+
+ if (auto FT = codegen::getExplicitFileType())
+ CodeGen.setFileType(FT.getValue());
+
+ if (!OutputFilename.empty()) {
+ if (SaveLinkedModuleFile) {
+ std::string ModuleFilename = OutputFilename;
+ ModuleFilename += ".linked.bc";
+ std::string ErrMsg;
+
+ if (!CodeGen.writeMergedModules(ModuleFilename))
+ error("writing linked module failed.");
+ }
+
+ if (!CodeGen.optimize()) {
+ // Diagnostic messages should have been printed by the handler.
+ error("error optimizing the code");
+ }
+
+ if (SaveModuleFile) {
+ std::string ModuleFilename = OutputFilename;
+ ModuleFilename += ".merged.bc";
+ std::string ErrMsg;
+
+ if (!CodeGen.writeMergedModules(ModuleFilename))
+ error("writing merged module failed.");
+ }
+
+ auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
+ std::string PartFilename = OutputFilename;
+ if (Parallelism != 1)
+ PartFilename += "." + utostr(Task);
+
+ std::error_code EC;
+ auto S =
+ std::make_unique<raw_fd_ostream>(PartFilename, EC, sys::fs::OF_None);
+ if (EC)
+ error("error opening the file '" + PartFilename + "': " + EC.message());
+ return std::make_unique<CachedFileStream>(std::move(S));
+ };
+
+ if (!CodeGen.compileOptimized(AddStream, Parallelism))
+ // Diagnostic messages should have been printed by the handler.
+ error("error compiling the code");
+
+ } else {
+ if (Parallelism != 1)
+ error("-j must be specified together with -o");
+
+ if (SaveModuleFile)
+ error(": -save-merged-module must be specified with -o");
+
+ const char *OutputName = nullptr;
+ if (!CodeGen.compile_to_file(&OutputName))
+ error("error compiling the code");
+ // Diagnostic messages should have been printed by the handler.
+
+ outs() << "Wrote native object file '" << OutputName << "'\n";
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-lto/ya.make b/contrib/libs/llvm14/tools/llvm-lto/ya.make
new file mode 100644
index 00000000000..2e6e9b82bd6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-lto/ya.make
@@ -0,0 +1,92 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/AsmPrinter
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/CodeGen/SelectionDAG
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/LTO
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Passes
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/CFGuard
+ contrib/libs/llvm14/lib/Transforms/Coroutines
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/ObjCARC
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+ contrib/libs/llvm14/tools/polly/lib
+ contrib/libs/llvm14/tools/polly/lib/External/isl
+ contrib/libs/llvm14/tools/polly/lib/External/ppcg
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-lto
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-lto.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-lto2/llvm-lto2.cpp b/contrib/libs/llvm14/tools/llvm-lto2/llvm-lto2.cpp
new file mode 100644
index 00000000000..7416e585094
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-lto2/llvm-lto2.cpp
@@ -0,0 +1,517 @@
+//===-- llvm-lto2: test harness for the resolution-based LTO interface ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program takes in a list of bitcode files, links them and performs
+// link-time optimization according to the provided symbol resolutions using the
+// resolution-based LTO interface, and outputs one or more object files.
+//
+// This program is intended to eventually replace llvm-lto which uses the legacy
+// LTO interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/Passes/PassPlugin.h"
+#include "llvm/Remarks/HotnessThresholdParser.h"
+#include "llvm/Support/Caching.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/PluginLoader.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Threading.h"
+#include <atomic>
+
+using namespace llvm;
+using namespace lto;
+
+static codegen::RegisterCodeGenFlags CGF;
+
+static cl::opt<char>
+ OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
+ "(default = '-O2')"),
+ cl::Prefix, cl::ZeroOrMore, cl::init('2'));
+
+static cl::opt<char> CGOptLevel(
+ "cg-opt-level",
+ cl::desc("Codegen optimization level (0, 1, 2 or 3, default = '2')"),
+ cl::init('2'));
+
+static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
+ cl::desc("<input bitcode files>"));
+
+static cl::opt<std::string> OutputFilename("o", cl::Required,
+ cl::desc("Output filename"),
+ cl::value_desc("filename"));
+
+static cl::opt<std::string> CacheDir("cache-dir", cl::desc("Cache Directory"),
+ cl::value_desc("directory"));
+
+static cl::opt<std::string> OptPipeline("opt-pipeline",
+ cl::desc("Optimizer Pipeline"),
+ cl::value_desc("pipeline"));
+
+static cl::opt<std::string> AAPipeline("aa-pipeline",
+ cl::desc("Alias Analysis Pipeline"),
+ cl::value_desc("aapipeline"));
+
+static cl::opt<bool> SaveTemps("save-temps", cl::desc("Save temporary files"));
+
+static cl::opt<bool>
+ ThinLTODistributedIndexes("thinlto-distributed-indexes", cl::init(false),
+ cl::desc("Write out individual index and "
+ "import files for the "
+ "distributed backend case"));
+
+// Default to using all available threads in the system, but using only one
+// thread per core (no SMT).
+// Use -thinlto-threads=all to use hardware_concurrency() instead, which means
+// to use all hardware threads or cores in the system.
+static cl::opt<std::string> Threads("thinlto-threads");
+
+static cl::list<std::string> SymbolResolutions(
+ "r",
+ cl::desc("Specify a symbol resolution: filename,symbolname,resolution\n"
+ "where \"resolution\" is a sequence (which may be empty) of the\n"
+ "following characters:\n"
+ " p - prevailing: the linker has chosen this definition of the\n"
+ " symbol\n"
+ " l - local: the definition of this symbol is unpreemptable at\n"
+ " runtime and is known to be in this linkage unit\n"
+ " x - externally visible: the definition of this symbol is\n"
+ " visible outside of the LTO unit\n"
+ "A resolution for each symbol must be specified."),
+ cl::ZeroOrMore);
+
+static cl::opt<std::string> OverrideTriple(
+ "override-triple",
+ cl::desc("Replace target triples in input files with this triple"));
+
+static cl::opt<std::string> DefaultTriple(
+ "default-triple",
+ cl::desc(
+ "Replace unspecified target triples in input files with this triple"));
+
+static cl::opt<bool> RemarksWithHotness(
+ "pass-remarks-with-hotness",
+ cl::desc("With PGO, include profile count in optimization remarks"),
+ cl::Hidden);
+
+cl::opt<Optional<uint64_t>, false, remarks::HotnessThresholdParser>
+ RemarksHotnessThreshold(
+ "pass-remarks-hotness-threshold",
+ cl::desc("Minimum profile count required for an "
+ "optimization remark to be output."
+ " Use 'auto' to apply the threshold from profile summary."),
+ cl::value_desc("uint or 'auto'"), cl::init(0), cl::Hidden);
+
+static cl::opt<std::string>
+ RemarksFilename("pass-remarks-output",
+ cl::desc("Output filename for pass remarks"),
+ cl::value_desc("filename"));
+
+static cl::opt<std::string>
+ RemarksPasses("pass-remarks-filter",
+ cl::desc("Only record optimization remarks from passes whose "
+ "names match the given regular expression"),
+ cl::value_desc("regex"));
+
+static cl::opt<std::string> RemarksFormat(
+ "pass-remarks-format",
+ cl::desc("The format used for serializing remarks (default: YAML)"),
+ cl::value_desc("format"), cl::init("yaml"));
+
+static cl::opt<std::string>
+ SamplePGOFile("lto-sample-profile-file",
+ cl::desc("Specify a SamplePGO profile file"));
+
+static cl::opt<std::string>
+ CSPGOFile("lto-cspgo-profile-file",
+ cl::desc("Specify a context sensitive PGO profile file"));
+
+static cl::opt<bool>
+ RunCSIRInstr("lto-cspgo-gen",
+ cl::desc("Run PGO context sensitive IR instrumentation"),
+ cl::init(false), cl::Hidden);
+
+static cl::opt<bool>
+ UseNewPM("use-new-pm",
+ cl::desc("Run LTO passes using the new pass manager"),
+ cl::init(LLVM_ENABLE_NEW_PASS_MANAGER), cl::Hidden);
+
+static cl::opt<bool>
+ DebugPassManager("debug-pass-manager", cl::init(false), cl::Hidden,
+ cl::desc("Print pass management debugging information"));
+
+static cl::opt<std::string>
+ StatsFile("stats-file", cl::desc("Filename to write statistics to"));
+
+static cl::list<std::string>
+ PassPlugins("load-pass-plugin",
+ cl::desc("Load passes from plugin library"));
+
+static cl::opt<bool> EnableFreestanding(
+ "lto-freestanding",
+ cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"),
+ cl::init(false), cl::Hidden);
+
+static void check(Error E, std::string Msg) {
+ if (!E)
+ return;
+ handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) {
+ errs() << "llvm-lto2: " << Msg << ": " << EIB.message().c_str() << '\n';
+ });
+ exit(1);
+}
+
+template <typename T> static T check(Expected<T> E, std::string Msg) {
+ if (E)
+ return std::move(*E);
+ check(E.takeError(), Msg);
+ return T();
+}
+
+static void check(std::error_code EC, std::string Msg) {
+ check(errorCodeToError(EC), Msg);
+}
+
+template <typename T> static T check(ErrorOr<T> E, std::string Msg) {
+ if (E)
+ return std::move(*E);
+ check(E.getError(), Msg);
+ return T();
+}
+
+static int usage() {
+ errs() << "Available subcommands: dump-symtab run\n";
+ return 1;
+}
+
+static int run(int argc, char **argv) {
+ cl::ParseCommandLineOptions(argc, argv, "Resolution-based LTO test harness");
+
+ // FIXME: Workaround PR30396 which means that a symbol can appear
+ // more than once if it is defined in module-level assembly and
+ // has a GV declaration. We allow (file, symbol) pairs to have multiple
+ // resolutions and apply them in the order observed.
+ std::map<std::pair<std::string, std::string>, std::list<SymbolResolution>>
+ CommandLineResolutions;
+ for (std::string R : SymbolResolutions) {
+ StringRef Rest = R;
+ StringRef FileName, SymbolName;
+ std::tie(FileName, Rest) = Rest.split(',');
+ if (Rest.empty()) {
+ llvm::errs() << "invalid resolution: " << R << '\n';
+ return 1;
+ }
+ std::tie(SymbolName, Rest) = Rest.split(',');
+ SymbolResolution Res;
+ for (char C : Rest) {
+ if (C == 'p')
+ Res.Prevailing = true;
+ else if (C == 'l')
+ Res.FinalDefinitionInLinkageUnit = true;
+ else if (C == 'x')
+ Res.VisibleToRegularObj = true;
+ else if (C == 'r')
+ Res.LinkerRedefined = true;
+ else {
+ llvm::errs() << "invalid character " << C << " in resolution: " << R
+ << '\n';
+ return 1;
+ }
+ }
+ CommandLineResolutions[{std::string(FileName), std::string(SymbolName)}]
+ .push_back(Res);
+ }
+
+ std::vector<std::unique_ptr<MemoryBuffer>> MBs;
+
+ Config Conf;
+
+ Conf.CPU = codegen::getMCPU();
+ Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+ Conf.MAttrs = codegen::getMAttrs();
+ if (auto RM = codegen::getExplicitRelocModel())
+ Conf.RelocModel = RM.getValue();
+ Conf.CodeModel = codegen::getExplicitCodeModel();
+
+ Conf.DebugPassManager = DebugPassManager;
+
+ if (SaveTemps)
+ check(Conf.addSaveTemps(OutputFilename + "."),
+ "Config::addSaveTemps failed");
+
+ // Optimization remarks.
+ Conf.RemarksFilename = RemarksFilename;
+ Conf.RemarksPasses = RemarksPasses;
+ Conf.RemarksWithHotness = RemarksWithHotness;
+ Conf.RemarksHotnessThreshold = RemarksHotnessThreshold;
+ Conf.RemarksFormat = RemarksFormat;
+
+ Conf.SampleProfile = SamplePGOFile;
+ Conf.CSIRProfile = CSPGOFile;
+ Conf.RunCSIRInstr = RunCSIRInstr;
+
+ // Run a custom pipeline, if asked for.
+ Conf.OptPipeline = OptPipeline;
+ Conf.AAPipeline = AAPipeline;
+
+ Conf.OptLevel = OptLevel - '0';
+ Conf.UseNewPM = UseNewPM;
+ Conf.Freestanding = EnableFreestanding;
+ for (auto &PluginFN : PassPlugins)
+ Conf.PassPlugins.push_back(PluginFN);
+ switch (CGOptLevel) {
+ case '0':
+ Conf.CGOptLevel = CodeGenOpt::None;
+ break;
+ case '1':
+ Conf.CGOptLevel = CodeGenOpt::Less;
+ break;
+ case '2':
+ Conf.CGOptLevel = CodeGenOpt::Default;
+ break;
+ case '3':
+ Conf.CGOptLevel = CodeGenOpt::Aggressive;
+ break;
+ default:
+ llvm::errs() << "invalid cg optimization level: " << CGOptLevel << '\n';
+ return 1;
+ }
+
+ if (auto FT = codegen::getExplicitFileType())
+ Conf.CGFileType = FT.getValue();
+
+ Conf.OverrideTriple = OverrideTriple;
+ Conf.DefaultTriple = DefaultTriple;
+ Conf.StatsFile = StatsFile;
+ Conf.PTO.LoopVectorization = Conf.OptLevel > 1;
+ Conf.PTO.SLPVectorization = Conf.OptLevel > 1;
+
+ ThinBackend Backend;
+ if (ThinLTODistributedIndexes)
+ Backend = createWriteIndexesThinBackend(/* OldPrefix */ "",
+ /* NewPrefix */ "",
+ /* ShouldEmitImportsFiles */ true,
+ /* LinkedObjectsFile */ nullptr,
+ /* OnWrite */ {});
+ else
+ Backend = createInProcessThinBackend(
+ llvm::heavyweight_hardware_concurrency(Threads));
+ // Track whether we hit an error; in particular, in the multi-threaded case,
+ // we can't exit() early because the rest of the threads wouldn't have had a
+ // change to be join-ed, and that would result in a "terminate called without
+ // an active exception". Altogether, this results in nondeterministic
+ // behavior. Instead, we don't exit in the multi-threaded case, but we make
+ // sure to report the error and then at the end (after joining cleanly)
+ // exit(1).
+ std::atomic<bool> HasErrors;
+ std::atomic_init(&HasErrors, false);
+ Conf.DiagHandler = [&](const DiagnosticInfo &DI) {
+ DiagnosticPrinterRawOStream DP(errs());
+ DI.print(DP);
+ errs() << '\n';
+ if (DI.getSeverity() == DS_Error)
+ HasErrors = true;
+ };
+
+ LTO Lto(std::move(Conf), std::move(Backend));
+
+ for (std::string F : InputFilenames) {
+ std::unique_ptr<MemoryBuffer> MB = check(MemoryBuffer::getFile(F), F);
+ std::unique_ptr<InputFile> Input =
+ check(InputFile::create(MB->getMemBufferRef()), F);
+
+ std::vector<SymbolResolution> Res;
+ for (const InputFile::Symbol &Sym : Input->symbols()) {
+ auto I = CommandLineResolutions.find({F, std::string(Sym.getName())});
+ // If it isn't found, look for ".", which would have been added
+ // (followed by a hash) when the symbol was promoted during module
+ // splitting if it was defined in one part and used in the other.
+ // Try looking up the symbol name before the suffix.
+ if (I == CommandLineResolutions.end()) {
+ auto SplitName = Sym.getName().rsplit(".");
+ I = CommandLineResolutions.find({F, std::string(SplitName.first)});
+ }
+ if (I == CommandLineResolutions.end()) {
+ llvm::errs() << argv[0] << ": missing symbol resolution for " << F
+ << ',' << Sym.getName() << '\n';
+ HasErrors = true;
+ } else {
+ Res.push_back(I->second.front());
+ I->second.pop_front();
+ if (I->second.empty())
+ CommandLineResolutions.erase(I);
+ }
+ }
+
+ if (HasErrors)
+ continue;
+
+ MBs.push_back(std::move(MB));
+ check(Lto.add(std::move(Input), Res), F);
+ }
+
+ if (!CommandLineResolutions.empty()) {
+ HasErrors = true;
+ for (auto UnusedRes : CommandLineResolutions)
+ llvm::errs() << argv[0] << ": unused symbol resolution for "
+ << UnusedRes.first.first << ',' << UnusedRes.first.second
+ << '\n';
+ }
+ if (HasErrors)
+ return 1;
+
+ auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
+ std::string Path = OutputFilename + "." + utostr(Task);
+
+ std::error_code EC;
+ auto S = std::make_unique<raw_fd_ostream>(Path, EC, sys::fs::OF_None);
+ check(EC, Path);
+ return std::make_unique<CachedFileStream>(std::move(S), Path);
+ };
+
+ auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
+ *AddStream(Task)->OS << MB->getBuffer();
+ };
+
+ FileCache Cache;
+ if (!CacheDir.empty())
+ Cache = check(localCache("ThinLTO", "Thin", CacheDir, AddBuffer),
+ "failed to create cache");
+
+ check(Lto.run(AddStream, Cache), "LTO::run failed");
+ return static_cast<int>(HasErrors);
+}
+
+static int dumpSymtab(int argc, char **argv) {
+ for (StringRef F : make_range(argv + 1, argv + argc)) {
+ std::unique_ptr<MemoryBuffer> MB =
+ check(MemoryBuffer::getFile(F), std::string(F));
+ BitcodeFileContents BFC =
+ check(getBitcodeFileContents(*MB), std::string(F));
+
+ if (BFC.Symtab.size() >= sizeof(irsymtab::storage::Header)) {
+ auto *Hdr = reinterpret_cast<const irsymtab::storage::Header *>(
+ BFC.Symtab.data());
+ outs() << "version: " << Hdr->Version << '\n';
+ if (Hdr->Version == irsymtab::storage::Header::kCurrentVersion)
+ outs() << "producer: " << Hdr->Producer.get(BFC.StrtabForSymtab)
+ << '\n';
+ }
+
+ std::unique_ptr<InputFile> Input =
+ check(InputFile::create(MB->getMemBufferRef()), std::string(F));
+
+ outs() << "target triple: " << Input->getTargetTriple() << '\n';
+ Triple TT(Input->getTargetTriple());
+
+ outs() << "source filename: " << Input->getSourceFileName() << '\n';
+
+ if (TT.isOSBinFormatCOFF())
+ outs() << "linker opts: " << Input->getCOFFLinkerOpts() << '\n';
+
+ if (TT.isOSBinFormatELF()) {
+ outs() << "dependent libraries:";
+ for (auto L : Input->getDependentLibraries())
+ outs() << " \"" << L << "\"";
+ outs() << '\n';
+ }
+
+ ArrayRef<std::pair<StringRef, Comdat::SelectionKind>> ComdatTable =
+ Input->getComdatTable();
+ for (const InputFile::Symbol &Sym : Input->symbols()) {
+ switch (Sym.getVisibility()) {
+ case GlobalValue::HiddenVisibility:
+ outs() << 'H';
+ break;
+ case GlobalValue::ProtectedVisibility:
+ outs() << 'P';
+ break;
+ case GlobalValue::DefaultVisibility:
+ outs() << 'D';
+ break;
+ }
+
+ auto PrintBool = [&](char C, bool B) { outs() << (B ? C : '-'); };
+ PrintBool('U', Sym.isUndefined());
+ PrintBool('C', Sym.isCommon());
+ PrintBool('W', Sym.isWeak());
+ PrintBool('I', Sym.isIndirect());
+ PrintBool('O', Sym.canBeOmittedFromSymbolTable());
+ PrintBool('T', Sym.isTLS());
+ PrintBool('X', Sym.isExecutable());
+ outs() << ' ' << Sym.getName() << '\n';
+
+ if (Sym.isCommon())
+ outs() << " size " << Sym.getCommonSize() << " align "
+ << Sym.getCommonAlignment() << '\n';
+
+ int Comdat = Sym.getComdatIndex();
+ if (Comdat != -1) {
+ outs() << " comdat ";
+ switch (ComdatTable[Comdat].second) {
+ case Comdat::Any:
+ outs() << "any";
+ break;
+ case Comdat::ExactMatch:
+ outs() << "exactmatch";
+ break;
+ case Comdat::Largest:
+ outs() << "largest";
+ break;
+ case Comdat::NoDeduplicate:
+ outs() << "nodeduplicate";
+ break;
+ case Comdat::SameSize:
+ outs() << "samesize";
+ break;
+ }
+ outs() << ' ' << ComdatTable[Comdat].first << '\n';
+ }
+
+ if (TT.isOSBinFormatCOFF() && Sym.isWeak() && Sym.isIndirect())
+ outs() << " fallback " << Sym.getCOFFWeakExternalFallback() << '\n';
+
+ if (!Sym.getSectionName().empty())
+ outs() << " section " << Sym.getSectionName() << "\n";
+ }
+
+ outs() << '\n';
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmPrinters();
+ InitializeAllAsmParsers();
+
+ // FIXME: This should use llvm::cl subcommands, but it isn't currently
+ // possible to pass an argument not associated with a subcommand to a
+ // subcommand (e.g. -use-new-pm).
+ if (argc < 2)
+ return usage();
+
+ StringRef Subcommand = argv[1];
+ // Ensure that argv[0] is correct after adjusting argv/argc.
+ argv[1] = argv[0];
+ if (Subcommand == "dump-symtab")
+ return dumpSymtab(argc - 1, argv + 1);
+ if (Subcommand == "run")
+ return run(argc - 1, argv + 1);
+ return usage();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-lto2/ya.make b/contrib/libs/llvm14/tools/llvm-lto2/ya.make
new file mode 100644
index 00000000000..e2dc4f94979
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-lto2/ya.make
@@ -0,0 +1,92 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/AsmPrinter
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/CodeGen/SelectionDAG
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/LTO
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Passes
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/CFGuard
+ contrib/libs/llvm14/lib/Transforms/Coroutines
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/ObjCARC
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+ contrib/libs/llvm14/tools/polly/lib
+ contrib/libs/llvm14/tools/polly/lib/External/isl
+ contrib/libs/llvm14/tools/polly/lib/External/ppcg
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-lto2
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-lto2.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-mc/Disassembler.cpp b/contrib/libs/llvm14/tools/llvm-mc/Disassembler.cpp
new file mode 100644
index 00000000000..ac55d05db19
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mc/Disassembler.cpp
@@ -0,0 +1,207 @@
+//===- Disassembler.cpp - Disassembler for hex strings --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements the disassembler of strings of bytes written in
+// hexadecimal, from standard input or from a file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Disassembler.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+typedef std::pair<std::vector<unsigned char>, std::vector<const char *>>
+ ByteArrayTy;
+
+static bool PrintInsts(const MCDisassembler &DisAsm,
+ const ByteArrayTy &Bytes,
+ SourceMgr &SM, raw_ostream &Out,
+ MCStreamer &Streamer, bool InAtomicBlock,
+ const MCSubtargetInfo &STI) {
+ ArrayRef<uint8_t> Data(Bytes.first.data(), Bytes.first.size());
+
+ // Disassemble it to strings.
+ uint64_t Size;
+ uint64_t Index;
+
+ for (Index = 0; Index < Bytes.first.size(); Index += Size) {
+ MCInst Inst;
+
+ MCDisassembler::DecodeStatus S;
+ S = DisAsm.getInstruction(Inst, Size, Data.slice(Index), Index, nulls());
+ switch (S) {
+ case MCDisassembler::Fail:
+ SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]),
+ SourceMgr::DK_Warning,
+ "invalid instruction encoding");
+ // Don't try to resynchronise the stream in a block
+ if (InAtomicBlock)
+ return true;
+
+ if (Size == 0)
+ Size = 1; // skip illegible bytes
+
+ break;
+
+ case MCDisassembler::SoftFail:
+ SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]),
+ SourceMgr::DK_Warning,
+ "potentially undefined instruction encoding");
+ LLVM_FALLTHROUGH;
+
+ case MCDisassembler::Success:
+ Streamer.emitInstruction(Inst, STI);
+ break;
+ }
+ }
+
+ return false;
+}
+
+static bool SkipToToken(StringRef &Str) {
+ for (;;) {
+ if (Str.empty())
+ return false;
+
+ // Strip horizontal whitespace and commas.
+ if (size_t Pos = Str.find_first_not_of(" \t\r\n,")) {
+ Str = Str.substr(Pos);
+ continue;
+ }
+
+ // If this is the start of a comment, remove the rest of the line.
+ if (Str[0] == '#') {
+ Str = Str.substr(Str.find_first_of('\n'));
+ continue;
+ }
+ return true;
+ }
+}
+
+
+static bool ByteArrayFromString(ByteArrayTy &ByteArray,
+ StringRef &Str,
+ SourceMgr &SM) {
+ while (SkipToToken(Str)) {
+ // Handled by higher level
+ if (Str[0] == '[' || Str[0] == ']')
+ return false;
+
+ // Get the current token.
+ size_t Next = Str.find_first_of(" \t\n\r,#[]");
+ StringRef Value = Str.substr(0, Next);
+
+ // Convert to a byte and add to the byte vector.
+ unsigned ByteVal;
+ if (Value.getAsInteger(0, ByteVal) || ByteVal > 255) {
+ // If we have an error, print it and skip to the end of line.
+ SM.PrintMessage(SMLoc::getFromPointer(Value.data()), SourceMgr::DK_Error,
+ "invalid input token");
+ Str = Str.substr(Str.find('\n'));
+ ByteArray.first.clear();
+ ByteArray.second.clear();
+ continue;
+ }
+
+ ByteArray.first.push_back(ByteVal);
+ ByteArray.second.push_back(Value.data());
+ Str = Str.substr(Next);
+ }
+
+ return false;
+}
+
+int Disassembler::disassemble(const Target &T, const std::string &Triple,
+ MCSubtargetInfo &STI, MCStreamer &Streamer,
+ MemoryBuffer &Buffer, SourceMgr &SM,
+ MCContext &Ctx, raw_ostream &Out,
+ const MCTargetOptions &MCOptions) {
+
+ std::unique_ptr<const MCRegisterInfo> MRI(T.createMCRegInfo(Triple));
+ if (!MRI) {
+ errs() << "error: no register info for target " << Triple << "\n";
+ return -1;
+ }
+
+ std::unique_ptr<const MCAsmInfo> MAI(
+ T.createMCAsmInfo(*MRI, Triple, MCOptions));
+ if (!MAI) {
+ errs() << "error: no assembly info for target " << Triple << "\n";
+ return -1;
+ }
+
+ std::unique_ptr<const MCDisassembler> DisAsm(
+ T.createMCDisassembler(STI, Ctx));
+ if (!DisAsm) {
+ errs() << "error: no disassembler for target " << Triple << "\n";
+ return -1;
+ }
+
+ // Set up initial section manually here
+ Streamer.initSections(false, STI);
+
+ bool ErrorOccurred = false;
+
+ // Convert the input to a vector for disassembly.
+ ByteArrayTy ByteArray;
+ StringRef Str = Buffer.getBuffer();
+ bool InAtomicBlock = false;
+
+ while (SkipToToken(Str)) {
+ ByteArray.first.clear();
+ ByteArray.second.clear();
+
+ if (Str[0] == '[') {
+ if (InAtomicBlock) {
+ SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error,
+ "nested atomic blocks make no sense");
+ ErrorOccurred = true;
+ }
+ InAtomicBlock = true;
+ Str = Str.drop_front();
+ continue;
+ } else if (Str[0] == ']') {
+ if (!InAtomicBlock) {
+ SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error,
+ "attempt to close atomic block without opening");
+ ErrorOccurred = true;
+ }
+ InAtomicBlock = false;
+ Str = Str.drop_front();
+ continue;
+ }
+
+ // It's a real token, get the bytes and emit them
+ ErrorOccurred |= ByteArrayFromString(ByteArray, Str, SM);
+
+ if (!ByteArray.first.empty())
+ ErrorOccurred |= PrintInsts(*DisAsm, ByteArray, SM, Out, Streamer,
+ InAtomicBlock, STI);
+ }
+
+ if (InAtomicBlock) {
+ SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error,
+ "unclosed atomic block");
+ ErrorOccurred = true;
+ }
+
+ return ErrorOccurred;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-mc/Disassembler.h b/contrib/libs/llvm14/tools/llvm-mc/Disassembler.h
new file mode 100644
index 00000000000..a1603e58498
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mc/Disassembler.h
@@ -0,0 +1,40 @@
+//===- Disassembler.h - Text File Disassembler ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements the disassembler of strings of bytes written in
+// hexadecimal, from standard input or from a file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MC_DISASSEMBLER_H
+#define LLVM_TOOLS_LLVM_MC_DISASSEMBLER_H
+
+#include <string>
+
+namespace llvm {
+
+class MemoryBuffer;
+class Target;
+class raw_ostream;
+class SourceMgr;
+class MCContext;
+class MCSubtargetInfo;
+class MCStreamer;
+class MCTargetOptions;
+
+class Disassembler {
+public:
+ static int disassemble(const Target &T, const std::string &Triple,
+ MCSubtargetInfo &STI, MCStreamer &Streamer,
+ MemoryBuffer &Buffer, SourceMgr &SM, MCContext &Ctx,
+ raw_ostream &Out, const MCTargetOptions &MCOptions);
+};
+
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mc/llvm-mc.cpp b/contrib/libs/llvm14/tools/llvm-mc/llvm-mc.cpp
new file mode 100644
index 00000000000..4e5a12e53a6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mc/llvm-mc.cpp
@@ -0,0 +1,610 @@
+//===-- llvm-mc.cpp - Machine Code Hacking Driver ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This utility is a simple driver that allows command line hacking on machine
+// code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Disassembler.h"
+#include "llvm/MC/MCAsmBackend.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
+#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"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Compression.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+
+using namespace llvm;
+
+static mc::RegisterMCTargetOptionsFlags MOF;
+
+static cl::OptionCategory MCCategory("MC Options");
+
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input file>"),
+ cl::init("-"), cl::cat(MCCategory));
+
+static cl::list<std::string>
+ DisassemblerOptions("M", cl::desc("Disassembler options"),
+ cl::cat(MCCategory));
+
+static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
+ cl::value_desc("filename"),
+ cl::init("-"), cl::cat(MCCategory));
+
+static cl::opt<std::string> SplitDwarfFile("split-dwarf-file",
+ cl::desc("DWO output filename"),
+ cl::value_desc("filename"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool> ShowEncoding("show-encoding",
+ cl::desc("Show instruction encodings"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool> RelaxELFRel(
+ "relax-relocations", cl::init(true),
+ cl::desc("Emit R_X86_64_GOTPCRELX instead of R_X86_64_GOTPCREL"),
+ cl::cat(MCCategory));
+
+static cl::opt<DebugCompressionType> CompressDebugSections(
+ "compress-debug-sections", cl::ValueOptional,
+ cl::init(DebugCompressionType::None),
+ cl::desc("Choose DWARF debug sections compression:"),
+ cl::values(clEnumValN(DebugCompressionType::None, "none", "No compression"),
+ clEnumValN(DebugCompressionType::Z, "zlib",
+ "Use zlib compression"),
+ clEnumValN(DebugCompressionType::GNU, "zlib-gnu",
+ "Use zlib-gnu compression (deprecated)")),
+ cl::cat(MCCategory));
+
+static cl::opt<bool>
+ ShowInst("show-inst", cl::desc("Show internal instruction representation"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool>
+ ShowInstOperands("show-inst-operands",
+ cl::desc("Show instructions operands as parsed"),
+ cl::cat(MCCategory));
+
+static cl::opt<unsigned>
+ OutputAsmVariant("output-asm-variant",
+ cl::desc("Syntax variant to use for output printing"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool>
+ PrintImmHex("print-imm-hex", cl::init(false),
+ cl::desc("Prefer hex format for immediate values"),
+ cl::cat(MCCategory));
+
+static cl::list<std::string>
+ DefineSymbol("defsym",
+ cl::desc("Defines a symbol to be an integer constant"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool>
+ PreserveComments("preserve-comments",
+ cl::desc("Preserve Comments in outputted assembly"),
+ cl::cat(MCCategory));
+
+enum OutputFileType {
+ OFT_Null,
+ OFT_AssemblyFile,
+ OFT_ObjectFile
+};
+static cl::opt<OutputFileType>
+ FileType("filetype", cl::init(OFT_AssemblyFile),
+ cl::desc("Choose an output file type:"),
+ cl::values(clEnumValN(OFT_AssemblyFile, "asm",
+ "Emit an assembly ('.s') file"),
+ clEnumValN(OFT_Null, "null",
+ "Don't emit anything (for timing purposes)"),
+ clEnumValN(OFT_ObjectFile, "obj",
+ "Emit a native object ('.o') file")),
+ cl::cat(MCCategory));
+
+static cl::list<std::string> IncludeDirs("I",
+ cl::desc("Directory of include files"),
+ cl::value_desc("directory"),
+ cl::Prefix, cl::cat(MCCategory));
+
+static cl::opt<std::string>
+ ArchName("arch",
+ cl::desc("Target arch to assemble for, "
+ "see -version for available targets"),
+ cl::cat(MCCategory));
+
+static cl::opt<std::string>
+ TripleName("triple",
+ cl::desc("Target triple to assemble for, "
+ "see -version for available targets"),
+ cl::cat(MCCategory));
+
+static cl::opt<std::string>
+ MCPU("mcpu",
+ cl::desc("Target a specific cpu type (-mcpu=help for details)"),
+ cl::value_desc("cpu-name"), cl::init(""), cl::cat(MCCategory));
+
+static cl::list<std::string>
+ MAttrs("mattr", cl::CommaSeparated,
+ cl::desc("Target specific attributes (-mattr=help for details)"),
+ cl::value_desc("a1,+a2,-a3,..."), cl::cat(MCCategory));
+
+static cl::opt<bool> PIC("position-independent",
+ cl::desc("Position independent"), cl::init(false),
+ cl::cat(MCCategory));
+
+static cl::opt<bool>
+ LargeCodeModel("large-code-model",
+ cl::desc("Create cfi directives that assume the code might "
+ "be more than 2gb away"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool>
+ NoInitialTextSection("n",
+ cl::desc("Don't assume assembly file starts "
+ "in the text section"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool>
+ GenDwarfForAssembly("g",
+ cl::desc("Generate dwarf debugging info for assembly "
+ "source files"),
+ cl::cat(MCCategory));
+
+static cl::opt<std::string>
+ DebugCompilationDir("fdebug-compilation-dir",
+ cl::desc("Specifies the debug info's compilation dir"),
+ cl::cat(MCCategory));
+
+static cl::list<std::string> DebugPrefixMap(
+ "fdebug-prefix-map", cl::desc("Map file source paths in debug info"),
+ cl::value_desc("= separated key-value pairs"), cl::cat(MCCategory));
+
+static cl::opt<std::string> MainFileName(
+ "main-file-name",
+ cl::desc("Specifies the name we should consider the input file"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool> SaveTempLabels("save-temp-labels",
+ cl::desc("Don't discard temporary labels"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool> LexMasmIntegers(
+ "masm-integers",
+ cl::desc("Enable binary and hex masm integers (0b110 and 0ABCh)"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool> LexMasmHexFloats(
+ "masm-hexfloats",
+ cl::desc("Enable MASM-style hex float initializers (3F800000r)"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool> LexMotorolaIntegers(
+ "motorola-integers",
+ cl::desc("Enable binary and hex Motorola integers (%110 and $ABC)"),
+ cl::cat(MCCategory));
+
+static cl::opt<bool> NoExecStack("no-exec-stack",
+ cl::desc("File doesn't need an exec stack"),
+ cl::cat(MCCategory));
+
+enum ActionType {
+ AC_AsLex,
+ AC_Assemble,
+ AC_Disassemble,
+ AC_MDisassemble,
+};
+
+static cl::opt<ActionType> Action(
+ cl::desc("Action to perform:"), cl::init(AC_Assemble),
+ cl::values(clEnumValN(AC_AsLex, "as-lex", "Lex tokens from a .s file"),
+ clEnumValN(AC_Assemble, "assemble",
+ "Assemble a .s file (default)"),
+ clEnumValN(AC_Disassemble, "disassemble",
+ "Disassemble strings of hex bytes"),
+ clEnumValN(AC_MDisassemble, "mdis",
+ "Marked up disassembly of strings of hex bytes")),
+ cl::cat(MCCategory));
+
+static const Target *GetTarget(const char *ProgName) {
+ // Figure out the target triple.
+ if (TripleName.empty())
+ TripleName = sys::getDefaultTargetTriple();
+ Triple TheTriple(Triple::normalize(TripleName));
+
+ // Get the target specific parser.
+ std::string Error;
+ const Target *TheTarget = TargetRegistry::lookupTarget(ArchName, TheTriple,
+ Error);
+ if (!TheTarget) {
+ WithColor::error(errs(), ProgName) << Error;
+ return nullptr;
+ }
+
+ // Update the triple name and return the found target.
+ TripleName = TheTriple.getTriple();
+ return TheTarget;
+}
+
+static std::unique_ptr<ToolOutputFile> GetOutputStream(StringRef Path,
+ sys::fs::OpenFlags Flags) {
+ std::error_code EC;
+ auto Out = std::make_unique<ToolOutputFile>(Path, EC, Flags);
+ if (EC) {
+ WithColor::error() << EC.message() << '\n';
+ return nullptr;
+ }
+
+ return Out;
+}
+
+static std::string DwarfDebugFlags;
+static void setDwarfDebugFlags(int argc, char **argv) {
+ if (!getenv("RC_DEBUG_OPTIONS"))
+ return;
+ for (int i = 0; i < argc; i++) {
+ DwarfDebugFlags += argv[i];
+ if (i + 1 < argc)
+ DwarfDebugFlags += " ";
+ }
+}
+
+static std::string DwarfDebugProducer;
+static void setDwarfDebugProducer() {
+ if(!getenv("DEBUG_PRODUCER"))
+ return;
+ DwarfDebugProducer += getenv("DEBUG_PRODUCER");
+}
+
+static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI,
+ raw_ostream &OS) {
+
+ AsmLexer Lexer(MAI);
+ Lexer.setBuffer(SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID())->getBuffer());
+
+ bool Error = false;
+ while (Lexer.Lex().isNot(AsmToken::Eof)) {
+ Lexer.getTok().dump(OS);
+ OS << "\n";
+ if (Lexer.getTok().getKind() == AsmToken::Error)
+ Error = true;
+ }
+
+ return Error;
+}
+
+static int fillCommandLineSymbols(MCAsmParser &Parser) {
+ for (auto &I: DefineSymbol) {
+ auto Pair = StringRef(I).split('=');
+ auto Sym = Pair.first;
+ auto Val = Pair.second;
+
+ if (Sym.empty() || Val.empty()) {
+ WithColor::error() << "defsym must be of the form: sym=value: " << I
+ << "\n";
+ return 1;
+ }
+ int64_t Value;
+ if (Val.getAsInteger(0, Value)) {
+ WithColor::error() << "value is not an integer: " << Val << "\n";
+ return 1;
+ }
+ Parser.getContext().setSymbolValue(Parser.getStreamer(), Sym, Value);
+ }
+ return 0;
+}
+
+static int AssembleInput(const char *ProgName, const Target *TheTarget,
+ SourceMgr &SrcMgr, MCContext &Ctx, MCStreamer &Str,
+ MCAsmInfo &MAI, MCSubtargetInfo &STI,
+ MCInstrInfo &MCII, MCTargetOptions const &MCOptions) {
+ std::unique_ptr<MCAsmParser> Parser(
+ createMCAsmParser(SrcMgr, Ctx, Str, MAI));
+ std::unique_ptr<MCTargetAsmParser> TAP(
+ TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions));
+
+ if (!TAP) {
+ WithColor::error(errs(), ProgName)
+ << "this target does not support assembly parsing.\n";
+ return 1;
+ }
+
+ int SymbolResult = fillCommandLineSymbols(*Parser);
+ if(SymbolResult)
+ return SymbolResult;
+ Parser->setShowParsedOperands(ShowInstOperands);
+ Parser->setTargetParser(*TAP);
+ Parser->getLexer().setLexMasmIntegers(LexMasmIntegers);
+ Parser->getLexer().setLexMasmHexFloats(LexMasmHexFloats);
+ Parser->getLexer().setLexMotorolaIntegers(LexMotorolaIntegers);
+
+ int Res = Parser->Run(NoInitialTextSection);
+
+ return Res;
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ // Initialize targets and assembly printers/parsers.
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmParsers();
+ llvm::InitializeAllDisassemblers();
+
+ // Register the target printer for --version.
+ cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
+
+ cl::HideUnrelatedOptions({&MCCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n");
+ const MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags();
+ setDwarfDebugFlags(argc, argv);
+
+ setDwarfDebugProducer();
+
+ const char *ProgName = argv[0];
+ const Target *TheTarget = GetTarget(ProgName);
+ if (!TheTarget)
+ return 1;
+ // Now that GetTarget() has (potentially) replaced TripleName, it's safe to
+ // construct the Triple object.
+ Triple TheTriple(TripleName);
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
+ MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true);
+ if (std::error_code EC = BufferPtr.getError()) {
+ WithColor::error(errs(), ProgName)
+ << InputFilename << ": " << EC.message() << '\n';
+ return 1;
+ }
+ MemoryBuffer *Buffer = BufferPtr->get();
+
+ SourceMgr SrcMgr;
+
+ // Tell SrcMgr about this buffer, which is what the parser will pick up.
+ SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc());
+
+ // Record the location of the include directories so that the lexer can find
+ // it later.
+ SrcMgr.setIncludeDirs(IncludeDirs);
+
+ std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
+ assert(MRI && "Unable to create target register info!");
+
+ std::unique_ptr<MCAsmInfo> MAI(
+ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ assert(MAI && "Unable to create target asm info!");
+
+ MAI->setRelaxELFRelocations(RelaxELFRel);
+
+ if (CompressDebugSections != DebugCompressionType::None) {
+ if (!zlib::isAvailable()) {
+ WithColor::error(errs(), ProgName)
+ << "build tools with zlib to enable -compress-debug-sections";
+ return 1;
+ }
+ MAI->setCompressDebugSections(CompressDebugSections);
+ }
+ MAI->setPreserveAsmComments(PreserveComments);
+
+ // Package up features to be passed to target/subtarget
+ std::string FeaturesStr;
+ if (MAttrs.size()) {
+ SubtargetFeatures Features;
+ for (unsigned i = 0; i != MAttrs.size(); ++i)
+ Features.AddFeature(MAttrs[i]);
+ FeaturesStr = Features.getString();
+ }
+
+ std::unique_ptr<MCSubtargetInfo> STI(
+ TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr));
+ assert(STI && "Unable to create subtarget info!");
+
+ // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
+ // MCObjectFileInfo needs a MCContext reference in order to initialize itself.
+ MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr,
+ &MCOptions);
+ std::unique_ptr<MCObjectFileInfo> MOFI(
+ TheTarget->createMCObjectFileInfo(Ctx, PIC, LargeCodeModel));
+ Ctx.setObjectFileInfo(MOFI.get());
+
+ if (SaveTempLabels)
+ Ctx.setAllowTemporaryLabels(false);
+
+ Ctx.setGenDwarfForAssembly(GenDwarfForAssembly);
+ // Default to 4 for dwarf version.
+ unsigned DwarfVersion = MCOptions.DwarfVersion ? MCOptions.DwarfVersion : 4;
+ if (DwarfVersion < 2 || DwarfVersion > 5) {
+ errs() << ProgName << ": Dwarf version " << DwarfVersion
+ << " is not supported." << '\n';
+ return 1;
+ }
+ Ctx.setDwarfVersion(DwarfVersion);
+ if (MCOptions.Dwarf64) {
+ // The 64-bit DWARF format was introduced in DWARFv3.
+ if (DwarfVersion < 3) {
+ errs() << ProgName
+ << ": the 64-bit DWARF format is not supported for DWARF versions "
+ "prior to 3\n";
+ return 1;
+ }
+ // 32-bit targets don't support DWARF64, which requires 64-bit relocations.
+ if (MAI->getCodePointerSize() < 8) {
+ errs() << ProgName
+ << ": the 64-bit DWARF format is only supported for 64-bit "
+ "targets\n";
+ return 1;
+ }
+ // If needsDwarfSectionOffsetDirective is true, we would eventually call
+ // MCStreamer::emitSymbolValue() with IsSectionRelative = true, but that
+ // is supported only for 4-byte long references.
+ if (MAI->needsDwarfSectionOffsetDirective()) {
+ errs() << ProgName << ": the 64-bit DWARF format is not supported for "
+ << TheTriple.normalize() << "\n";
+ return 1;
+ }
+ Ctx.setDwarfFormat(dwarf::DWARF64);
+ }
+ if (!DwarfDebugFlags.empty())
+ Ctx.setDwarfDebugFlags(StringRef(DwarfDebugFlags));
+ if (!DwarfDebugProducer.empty())
+ Ctx.setDwarfDebugProducer(StringRef(DwarfDebugProducer));
+ if (!DebugCompilationDir.empty())
+ Ctx.setCompilationDir(DebugCompilationDir);
+ else {
+ // If no compilation dir is set, try to use the current directory.
+ SmallString<128> CWD;
+ if (!sys::fs::current_path(CWD))
+ Ctx.setCompilationDir(CWD);
+ }
+ for (const auto &Arg : DebugPrefixMap) {
+ const auto &KV = StringRef(Arg).split('=');
+ Ctx.addDebugPrefixMapEntry(std::string(KV.first), std::string(KV.second));
+ }
+ if (!MainFileName.empty())
+ Ctx.setMainFileName(MainFileName);
+ if (GenDwarfForAssembly)
+ Ctx.setGenDwarfRootFile(InputFilename, Buffer->getBuffer());
+
+ sys::fs::OpenFlags Flags = (FileType == OFT_AssemblyFile)
+ ? sys::fs::OF_TextWithCRLF
+ : sys::fs::OF_None;
+ std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename, Flags);
+ if (!Out)
+ return 1;
+
+ std::unique_ptr<ToolOutputFile> DwoOut;
+ if (!SplitDwarfFile.empty()) {
+ if (FileType != OFT_ObjectFile) {
+ WithColor::error() << "dwo output only supported with object files\n";
+ return 1;
+ }
+ DwoOut = GetOutputStream(SplitDwarfFile, sys::fs::OF_None);
+ if (!DwoOut)
+ return 1;
+ }
+
+ std::unique_ptr<buffer_ostream> BOS;
+ raw_pwrite_stream *OS = &Out->os();
+ std::unique_ptr<MCStreamer> Str;
+
+ std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
+ assert(MCII && "Unable to create instruction info!");
+
+ MCInstPrinter *IP = nullptr;
+ if (FileType == OFT_AssemblyFile) {
+ IP = TheTarget->createMCInstPrinter(Triple(TripleName), OutputAsmVariant,
+ *MAI, *MCII, *MRI);
+
+ if (!IP) {
+ WithColor::error()
+ << "unable to create instruction printer for target triple '"
+ << TheTriple.normalize() << "' with assembly variant "
+ << OutputAsmVariant << ".\n";
+ return 1;
+ }
+
+ for (StringRef Opt : DisassemblerOptions)
+ if (!IP->applyTargetSpecificCLOption(Opt)) {
+ WithColor::error() << "invalid disassembler option '" << Opt << "'\n";
+ return 1;
+ }
+
+ // Set the display preference for hex vs. decimal immediates.
+ IP->setPrintImmHex(PrintImmHex);
+
+ // Set up the AsmStreamer.
+ std::unique_ptr<MCCodeEmitter> CE;
+ if (ShowEncoding)
+ CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
+
+ std::unique_ptr<MCAsmBackend> MAB(
+ TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
+ auto FOut = std::make_unique<formatted_raw_ostream>(*OS);
+ Str.reset(
+ TheTarget->createAsmStreamer(Ctx, std::move(FOut), /*asmverbose*/ true,
+ /*useDwarfDirectory*/ true, IP,
+ std::move(CE), std::move(MAB), ShowInst));
+
+ } else if (FileType == OFT_Null) {
+ Str.reset(TheTarget->createNullStreamer(Ctx));
+ } else {
+ assert(FileType == OFT_ObjectFile && "Invalid file type!");
+
+ if (!Out->os().supportsSeeking()) {
+ BOS = std::make_unique<buffer_ostream>(Out->os());
+ OS = BOS.get();
+ }
+
+ 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),
+ DwoOut ? MAB->createDwoObjectWriter(*OS, DwoOut->os())
+ : MAB->createObjectWriter(*OS),
+ std::unique_ptr<MCCodeEmitter>(CE), *STI, MCOptions.MCRelaxAll,
+ MCOptions.MCIncrementalLinkerCompatible,
+ /*DWARFMustBeAtTheEnd*/ false));
+ if (NoExecStack)
+ Str->initSections(true, *STI);
+ }
+
+ // Use Assembler information for parsing.
+ Str->setUseAssemblerInfoForParsing(true);
+
+ int Res = 1;
+ bool disassemble = false;
+ switch (Action) {
+ case AC_AsLex:
+ Res = AsLexInput(SrcMgr, *MAI, Out->os());
+ break;
+ case AC_Assemble:
+ Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI,
+ *MCII, MCOptions);
+ break;
+ case AC_MDisassemble:
+ assert(IP && "Expected assembly output");
+ IP->setUseMarkup(true);
+ disassemble = true;
+ break;
+ case AC_Disassemble:
+ disassemble = true;
+ break;
+ }
+ if (disassemble)
+ Res = Disassembler::disassemble(*TheTarget, TripleName, *STI, *Str, *Buffer,
+ SrcMgr, Ctx, Out->os(), MCOptions);
+
+ // Keep output if no errors.
+ if (Res == 0) {
+ Out->keep();
+ if (DwoOut)
+ DwoOut->keep();
+ }
+ return Res;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-mc/ya.make b/contrib/libs/llvm14/tools/llvm-mc/ya.make
new file mode 100644
index 00000000000..255dac8e1cc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mc/ya.make
@@ -0,0 +1,56 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-mc
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ Disassembler.cpp
+ llvm-mc.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-mca/CodeRegion.cpp b/contrib/libs/llvm14/tools/llvm-mca/CodeRegion.cpp
new file mode 100644
index 00000000000..7662538e3b6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/CodeRegion.cpp
@@ -0,0 +1,116 @@
+//===-------------------------- CodeRegion.cpp -----------------*- C++ -* -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements methods from the CodeRegions interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "CodeRegion.h"
+
+namespace llvm {
+namespace mca {
+
+CodeRegions::CodeRegions(llvm::SourceMgr &S) : SM(S), FoundErrors(false) {
+ // Create a default region for the input code sequence.
+ Regions.emplace_back(std::make_unique<CodeRegion>("", SMLoc()));
+}
+
+bool CodeRegion::isLocInRange(SMLoc Loc) const {
+ if (RangeEnd.isValid() && Loc.getPointer() > RangeEnd.getPointer())
+ return false;
+ if (RangeStart.isValid() && Loc.getPointer() < RangeStart.getPointer())
+ return false;
+ return true;
+}
+
+void CodeRegions::beginRegion(StringRef Description, SMLoc Loc) {
+ if (ActiveRegions.empty()) {
+ // Remove the default region if there is at least one user defined region.
+ // By construction, only the default region has an invalid start location.
+ if (Regions.size() == 1 && !Regions[0]->startLoc().isValid() &&
+ !Regions[0]->endLoc().isValid()) {
+ ActiveRegions[Description] = 0;
+ Regions[0] = std::make_unique<CodeRegion>(Description, Loc);
+ return;
+ }
+ } else {
+ auto It = ActiveRegions.find(Description);
+ if (It != ActiveRegions.end()) {
+ const CodeRegion &R = *Regions[It->second];
+ if (Description.empty()) {
+ SM.PrintMessage(Loc, SourceMgr::DK_Error,
+ "found multiple overlapping anonymous regions");
+ SM.PrintMessage(R.startLoc(), SourceMgr::DK_Note,
+ "Previous anonymous region was defined here");
+ FoundErrors = true;
+ return;
+ }
+
+ SM.PrintMessage(Loc, SourceMgr::DK_Error,
+ "overlapping regions cannot have the same name");
+ SM.PrintMessage(R.startLoc(), SourceMgr::DK_Note,
+ "region " + Description + " was previously defined here");
+ FoundErrors = true;
+ return;
+ }
+ }
+
+ ActiveRegions[Description] = Regions.size();
+ Regions.emplace_back(std::make_unique<CodeRegion>(Description, Loc));
+}
+
+void CodeRegions::endRegion(StringRef Description, SMLoc Loc) {
+ if (Description.empty()) {
+ // Special case where there is only one user defined region,
+ // and this LLVM-MCA-END directive doesn't provide a region name.
+ // In this case, we assume that the user simply wanted to just terminate
+ // the only active region.
+ if (ActiveRegions.size() == 1) {
+ auto It = ActiveRegions.begin();
+ Regions[It->second]->setEndLocation(Loc);
+ ActiveRegions.erase(It);
+ return;
+ }
+
+ // Special case where the region end marker applies to the default region.
+ if (ActiveRegions.empty() && Regions.size() == 1 &&
+ !Regions[0]->startLoc().isValid() && !Regions[0]->endLoc().isValid()) {
+ Regions[0]->setEndLocation(Loc);
+ return;
+ }
+ }
+
+ auto It = ActiveRegions.find(Description);
+ if (It != ActiveRegions.end()) {
+ Regions[It->second]->setEndLocation(Loc);
+ ActiveRegions.erase(It);
+ return;
+ }
+
+ FoundErrors = true;
+ SM.PrintMessage(Loc, SourceMgr::DK_Error,
+ "found an invalid region end directive");
+ if (!Description.empty()) {
+ SM.PrintMessage(Loc, SourceMgr::DK_Note,
+ "unable to find an active region named " + Description);
+ } else {
+ SM.PrintMessage(Loc, SourceMgr::DK_Note,
+ "unable to find an active anonymous region");
+ }
+}
+
+void CodeRegions::addInstruction(const MCInst &Instruction) {
+ SMLoc Loc = Instruction.getLoc();
+ for (UniqueCodeRegion &Region : Regions)
+ if (Region->isLocInRange(Loc))
+ Region->addInstruction(Instruction);
+}
+
+} // namespace mca
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-mca/CodeRegion.h b/contrib/libs/llvm14/tools/llvm-mca/CodeRegion.h
new file mode 100644
index 00000000000..0e1e02a533d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/CodeRegion.h
@@ -0,0 +1,130 @@
+//===-------------------------- CodeRegion.h -------------------*- C++ -* -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements class CodeRegion and CodeRegions.
+///
+/// A CodeRegion describes a region of assembly code guarded by special LLVM-MCA
+/// comment directives.
+///
+/// # LLVM-MCA-BEGIN foo
+/// ... ## asm
+/// # LLVM-MCA-END
+///
+/// A comment starting with substring LLVM-MCA-BEGIN marks the beginning of a
+/// new region of code.
+/// A comment starting with substring LLVM-MCA-END marks the end of the
+/// last-seen region of code.
+///
+/// Code regions are not allowed to overlap. Each region can have a optional
+/// description; internally, regions are described by a range of source
+/// locations (SMLoc objects).
+///
+/// An instruction (a MCInst) is added to a region R only if its location is in
+/// range [R.RangeStart, R.RangeEnd].
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_CODEREGION_H
+#define LLVM_TOOLS_LLVM_MCA_CODEREGION_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/SMLoc.h"
+#include "llvm/Support/SourceMgr.h"
+#include <vector>
+
+namespace llvm {
+namespace mca {
+
+/// A region of assembly code.
+///
+/// It identifies a sequence of machine instructions.
+class CodeRegion {
+ // An optional descriptor for this region.
+ llvm::StringRef Description;
+ // Instructions that form this region.
+ llvm::SmallVector<llvm::MCInst, 16> Instructions;
+ // Source location range.
+ llvm::SMLoc RangeStart;
+ llvm::SMLoc RangeEnd;
+
+ CodeRegion(const CodeRegion &) = delete;
+ CodeRegion &operator=(const CodeRegion &) = delete;
+
+public:
+ CodeRegion(llvm::StringRef Desc, llvm::SMLoc Start)
+ : Description(Desc), RangeStart(Start) {}
+
+ void addInstruction(const llvm::MCInst &Instruction) {
+ Instructions.emplace_back(Instruction);
+ }
+
+ llvm::SMLoc startLoc() const { return RangeStart; }
+ llvm::SMLoc endLoc() const { return RangeEnd; }
+
+ void setEndLocation(llvm::SMLoc End) { RangeEnd = End; }
+ bool empty() const { return Instructions.empty(); }
+ bool isLocInRange(llvm::SMLoc Loc) const;
+
+ llvm::ArrayRef<llvm::MCInst> getInstructions() const { return Instructions; }
+
+ llvm::StringRef getDescription() const { return Description; }
+};
+
+class CodeRegionParseError final : public Error {};
+
+class CodeRegions {
+ // A source manager. Used by the tool to generate meaningful warnings.
+ llvm::SourceMgr &SM;
+
+ using UniqueCodeRegion = std::unique_ptr<CodeRegion>;
+ std::vector<UniqueCodeRegion> Regions;
+ llvm::StringMap<unsigned> ActiveRegions;
+ bool FoundErrors;
+
+ CodeRegions(const CodeRegions &) = delete;
+ CodeRegions &operator=(const CodeRegions &) = delete;
+
+public:
+ CodeRegions(llvm::SourceMgr &S);
+
+ typedef std::vector<UniqueCodeRegion>::iterator iterator;
+ typedef std::vector<UniqueCodeRegion>::const_iterator const_iterator;
+
+ iterator begin() { return Regions.begin(); }
+ iterator end() { return Regions.end(); }
+ const_iterator begin() const { return Regions.cbegin(); }
+ const_iterator end() const { return Regions.cend(); }
+
+ void beginRegion(llvm::StringRef Description, llvm::SMLoc Loc);
+ void endRegion(llvm::StringRef Description, llvm::SMLoc Loc);
+ void addInstruction(const llvm::MCInst &Instruction);
+ llvm::SourceMgr &getSourceMgr() const { return SM; }
+
+ llvm::ArrayRef<llvm::MCInst> getInstructionSequence(unsigned Idx) const {
+ return Regions[Idx]->getInstructions();
+ }
+
+ bool empty() const {
+ return llvm::all_of(Regions, [](const UniqueCodeRegion &Region) {
+ return Region->empty();
+ });
+ }
+
+ bool isValid() const { return !FoundErrors; }
+};
+
+} // namespace mca
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mca/CodeRegionGenerator.cpp b/contrib/libs/llvm14/tools/llvm-mca/CodeRegionGenerator.cpp
new file mode 100644
index 00000000000..6cdd0ba797a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/CodeRegionGenerator.cpp
@@ -0,0 +1,150 @@
+//===----------------------- CodeRegionGenerator.cpp ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \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 */) 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")) {
+ // Skip spaces and tabs.
+ Position = Comment.find_first_not_of(" \t");
+ if (Position < Comment.size())
+ Comment = Comment.drop_front(Position);
+ Regions.endRegion(Comment, 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(
+ const std::unique_ptr<MCInstPrinter> &IP) {
+ MCTargetOptions Opts;
+ Opts.PreserveAsmComments = false;
+ MCStreamerWrapper Str(Ctx, Regions);
+
+ // Need to initialize an MCTargetStreamer otherwise
+ // certain asm directives will cause a segfault.
+ // Using nulls() so that anything emitted by the MCTargetStreamer
+ // doesn't show up in the llvm-mca output.
+ raw_ostream &OSRef = nulls();
+ formatted_raw_ostream FOSRef(OSRef);
+ TheTarget.createAsmTargetStreamer(Str, FOSRef, IP.get(),
+ /*IsVerboseAsm=*/true);
+
+ // 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);
+ // Enable support for MASM literal numbers (example: 05h, 101b).
+ Lexer.setLexMasmIntegers(true);
+
+ 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);
+
+ // Set 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/contrib/libs/llvm14/tools/llvm-mca/CodeRegionGenerator.h b/contrib/libs/llvm14/tools/llvm-mca/CodeRegionGenerator.h
new file mode 100644
index 00000000000..ac02131b2f3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/CodeRegionGenerator.h
@@ -0,0 +1,71 @@
+//===----------------------- CodeRegionGenerator.h --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \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/MC/TargetRegistry.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/SourceMgr.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(llvm::SourceMgr &SM) : Regions(SM) {}
+ virtual ~CodeRegionGenerator();
+ virtual Expected<const CodeRegions &>
+ parseCodeRegions(const std::unique_ptr<MCInstPrinter> &IP) = 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, llvm::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(const std::unique_ptr<MCInstPrinter> &IP) override;
+};
+
+} // namespace mca
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_MCA_CODEREGION_GENERATOR_H
diff --git a/contrib/libs/llvm14/tools/llvm-mca/PipelinePrinter.cpp b/contrib/libs/llvm14/tools/llvm-mca/PipelinePrinter.cpp
new file mode 100644
index 00000000000..9d06c6a1939
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/PipelinePrinter.cpp
@@ -0,0 +1,129 @@
+//===--------------------- PipelinePrinter.cpp ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the PipelinePrinter interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "PipelinePrinter.h"
+#include "CodeRegion.h"
+#include "Views/InstructionView.h"
+
+namespace llvm {
+namespace mca {
+
+void PipelinePrinter::printRegionHeader(llvm::raw_ostream &OS) const {
+ StringRef RegionName;
+ if (!Region.getDescription().empty())
+ RegionName = Region.getDescription();
+
+ OS << "\n[" << RegionIdx << "] Code Region";
+ if (!RegionName.empty())
+ OS << " - " << RegionName;
+ OS << "\n\n";
+}
+
+json::Object PipelinePrinter::getJSONReportRegion() const {
+ json::Object JO;
+
+ StringRef RegionName = "";
+ if (!Region.getDescription().empty())
+ RegionName = Region.getDescription();
+
+ JO.try_emplace("Name", RegionName);
+ for (const auto &V : Views)
+ if (V->isSerializable())
+ JO.try_emplace(V->getNameAsString().str(), V->toJSON());
+
+ return JO;
+}
+
+json::Object PipelinePrinter::getJSONSimulationParameters() const {
+ json::Object SimParameters({{"-mcpu", STI.getCPU()},
+ {"-mtriple", STI.getTargetTriple().getTriple()},
+ {"-march", STI.getTargetTriple().getArchName()}});
+
+ const MCSchedModel &SM = STI.getSchedModel();
+ if (!SM.isOutOfOrder())
+ return SimParameters;
+
+ if (PO.RegisterFileSize)
+ SimParameters.try_emplace("-register-file-size", PO.RegisterFileSize);
+
+ if (!PO.AssumeNoAlias)
+ SimParameters.try_emplace("-noalias", PO.AssumeNoAlias);
+
+ if (PO.DecodersThroughput)
+ SimParameters.try_emplace("-decoder-throughput", PO.DecodersThroughput);
+
+ if (PO.MicroOpQueueSize)
+ SimParameters.try_emplace("-micro-op-queue-size", PO.MicroOpQueueSize);
+
+ if (PO.DispatchWidth)
+ SimParameters.try_emplace("-dispatch", PO.DispatchWidth);
+
+ if (PO.LoadQueueSize)
+ SimParameters.try_emplace("-lqueue", PO.LoadQueueSize);
+
+ if (PO.StoreQueueSize)
+ SimParameters.try_emplace("-squeue", PO.StoreQueueSize);
+
+ return SimParameters;
+}
+
+json::Object PipelinePrinter::getJSONTargetInfo() const {
+ json::Array Resources;
+ const MCSchedModel &SM = STI.getSchedModel();
+ StringRef MCPU = STI.getCPU();
+
+ for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+ unsigned NumUnits = ProcResource.NumUnits;
+ if (ProcResource.SubUnitsIdxBegin || !NumUnits)
+ continue;
+
+ for (unsigned J = 0; J < NumUnits; ++J) {
+ std::string ResourceName = ProcResource.Name;
+ if (NumUnits > 1) {
+ ResourceName += ".";
+ ResourceName += J;
+ }
+
+ Resources.push_back(ResourceName);
+ }
+ }
+
+ return json::Object({{"CPUName", MCPU}, {"Resources", std::move(Resources)}});
+}
+
+void PipelinePrinter::printReport(json::Object &JO) const {
+ if (!RegionIdx) {
+ JO.try_emplace("TargetInfo", getJSONTargetInfo());
+ JO.try_emplace("SimulationParameters", getJSONSimulationParameters());
+ // Construct an array of regions.
+ JO.try_emplace("CodeRegions", json::Array());
+ }
+
+ json::Array *Regions = JO.getArray("CodeRegions");
+ assert(Regions && "This array must exist!");
+ Regions->push_back(getJSONReportRegion());
+}
+
+void PipelinePrinter::printReport(llvm::raw_ostream &OS) const {
+ // Don't print the header of this region if it is the default region, and if
+ // it doesn't have an end location.
+ if (Region.startLoc().isValid() || Region.endLoc().isValid())
+ printRegionHeader(OS);
+
+ for (const auto &V : Views)
+ V->printView(OS);
+}
+
+} // namespace mca
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-mca/PipelinePrinter.h b/contrib/libs/llvm14/tools/llvm-mca/PipelinePrinter.h
new file mode 100644
index 00000000000..d89e913f979
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/PipelinePrinter.h
@@ -0,0 +1,69 @@
+//===--------------------- PipelinePrinter.h --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements class PipelinePrinter.
+///
+/// PipelinePrinter allows the customization of the performance report.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H
+#define LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MCA/Context.h"
+#include "llvm/MCA/Pipeline.h"
+#include "llvm/MCA/View.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace llvm {
+namespace mca {
+
+class CodeRegion;
+
+/// A printer class that knows how to collects statistics on the
+/// code analyzed by the llvm-mca tool.
+///
+/// This class knows how to print out the analysis information collected
+/// during the execution of the code. Internally, it delegates to other
+/// classes the task of printing out timeline information as well as
+/// resource pressure.
+class PipelinePrinter {
+ Pipeline &P;
+ const CodeRegion &Region;
+ unsigned RegionIdx;
+ const MCSubtargetInfo &STI;
+ const PipelineOptions &PO;
+ llvm::SmallVector<std::unique_ptr<View>, 8> Views;
+
+ void printRegionHeader(llvm::raw_ostream &OS) const;
+ json::Object getJSONReportRegion() const;
+ json::Object getJSONTargetInfo() const;
+ json::Object getJSONSimulationParameters() const;
+
+public:
+ PipelinePrinter(Pipeline &Pipe, const CodeRegion &R, unsigned Idx,
+ const MCSubtargetInfo &STI, const PipelineOptions &PO)
+ : P(Pipe), Region(R), RegionIdx(Idx), STI(STI), PO(PO) {}
+
+ void addView(std::unique_ptr<View> V) {
+ P.addEventListener(V.get());
+ Views.emplace_back(std::move(V));
+ }
+
+ void printReport(llvm::raw_ostream &OS) const;
+ void printReport(json::Object &JO) const;
+};
+} // namespace mca
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/BottleneckAnalysis.cpp b/contrib/libs/llvm14/tools/llvm-mca/Views/BottleneckAnalysis.cpp
new file mode 100644
index 00000000000..dc0a07e75e4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/BottleneckAnalysis.cpp
@@ -0,0 +1,644 @@
+//===--------------------- BottleneckAnalysis.cpp ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the functionalities used by the BottleneckAnalysis
+/// to report bottleneck info.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Views/BottleneckAnalysis.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MCA/Support.h"
+#include "llvm/Support/Format.h"
+
+namespace llvm {
+namespace mca {
+
+#define DEBUG_TYPE "llvm-mca"
+
+PressureTracker::PressureTracker(const MCSchedModel &Model)
+ : SM(Model),
+ ResourcePressureDistribution(Model.getNumProcResourceKinds(), 0),
+ ProcResID2Mask(Model.getNumProcResourceKinds(), 0),
+ ResIdx2ProcResID(Model.getNumProcResourceKinds(), 0),
+ ProcResID2ResourceUsersIndex(Model.getNumProcResourceKinds(), 0) {
+ computeProcResourceMasks(SM, ProcResID2Mask);
+
+ // Ignore the invalid resource at index zero.
+ unsigned NextResourceUsersIdx = 0;
+ for (unsigned I = 1, E = Model.getNumProcResourceKinds(); I < E; ++I) {
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+ ProcResID2ResourceUsersIndex[I] = NextResourceUsersIdx;
+ NextResourceUsersIdx += ProcResource.NumUnits;
+ uint64_t ResourceMask = ProcResID2Mask[I];
+ ResIdx2ProcResID[getResourceStateIndex(ResourceMask)] = I;
+ }
+
+ ResourceUsers.resize(NextResourceUsersIdx);
+ std::fill(ResourceUsers.begin(), ResourceUsers.end(),
+ std::make_pair<unsigned, unsigned>(~0U, 0U));
+}
+
+void PressureTracker::getResourceUsers(uint64_t ResourceMask,
+ SmallVectorImpl<User> &Users) const {
+ unsigned Index = getResourceStateIndex(ResourceMask);
+ unsigned ProcResID = ResIdx2ProcResID[Index];
+ const MCProcResourceDesc &PRDesc = *SM.getProcResource(ProcResID);
+ for (unsigned I = 0, E = PRDesc.NumUnits; I < E; ++I) {
+ const User U = getResourceUser(ProcResID, I);
+ if (U.second && IPI.find(U.first) != IPI.end())
+ Users.emplace_back(U);
+ }
+}
+
+void PressureTracker::onInstructionDispatched(unsigned IID) {
+ IPI.insert(std::make_pair(IID, InstructionPressureInfo()));
+}
+
+void PressureTracker::onInstructionExecuted(unsigned IID) { IPI.erase(IID); }
+
+void PressureTracker::handleInstructionIssuedEvent(
+ const HWInstructionIssuedEvent &Event) {
+ unsigned IID = Event.IR.getSourceIndex();
+ for (const ResourceUse &Use : Event.UsedResources) {
+ const ResourceRef &RR = Use.first;
+ unsigned Index = ProcResID2ResourceUsersIndex[RR.first];
+ Index += countTrailingZeros(RR.second);
+ ResourceUsers[Index] = std::make_pair(IID, Use.second.getNumerator());
+ }
+}
+
+void PressureTracker::updateResourcePressureDistribution(
+ uint64_t CumulativeMask) {
+ while (CumulativeMask) {
+ uint64_t Current = CumulativeMask & (-CumulativeMask);
+ unsigned ResIdx = getResourceStateIndex(Current);
+ unsigned ProcResID = ResIdx2ProcResID[ResIdx];
+ uint64_t Mask = ProcResID2Mask[ProcResID];
+
+ if (Mask == Current) {
+ ResourcePressureDistribution[ProcResID]++;
+ CumulativeMask ^= Current;
+ continue;
+ }
+
+ Mask ^= Current;
+ while (Mask) {
+ uint64_t SubUnit = Mask & (-Mask);
+ ResIdx = getResourceStateIndex(SubUnit);
+ ProcResID = ResIdx2ProcResID[ResIdx];
+ ResourcePressureDistribution[ProcResID]++;
+ Mask ^= SubUnit;
+ }
+
+ CumulativeMask ^= Current;
+ }
+}
+
+void PressureTracker::handlePressureEvent(const HWPressureEvent &Event) {
+ assert(Event.Reason != HWPressureEvent::INVALID &&
+ "Unexpected invalid event!");
+
+ switch (Event.Reason) {
+ default:
+ break;
+
+ case HWPressureEvent::RESOURCES: {
+ const uint64_t ResourceMask = Event.ResourceMask;
+ updateResourcePressureDistribution(Event.ResourceMask);
+
+ for (const InstRef &IR : Event.AffectedInstructions) {
+ const Instruction &IS = *IR.getInstruction();
+ unsigned BusyResources = IS.getCriticalResourceMask() & ResourceMask;
+ if (!BusyResources)
+ continue;
+
+ unsigned IID = IR.getSourceIndex();
+ IPI[IID].ResourcePressureCycles++;
+ }
+ break;
+ }
+
+ case HWPressureEvent::REGISTER_DEPS:
+ for (const InstRef &IR : Event.AffectedInstructions) {
+ unsigned IID = IR.getSourceIndex();
+ IPI[IID].RegisterPressureCycles++;
+ }
+ break;
+
+ case HWPressureEvent::MEMORY_DEPS:
+ for (const InstRef &IR : Event.AffectedInstructions) {
+ unsigned IID = IR.getSourceIndex();
+ IPI[IID].MemoryPressureCycles++;
+ }
+ }
+}
+
+#ifndef NDEBUG
+void DependencyGraph::dumpDependencyEdge(raw_ostream &OS,
+ const DependencyEdge &DepEdge,
+ MCInstPrinter &MCIP) const {
+ unsigned FromIID = DepEdge.FromIID;
+ unsigned ToIID = DepEdge.ToIID;
+ assert(FromIID < ToIID && "Graph should be acyclic!");
+
+ const DependencyEdge::Dependency &DE = DepEdge.Dep;
+ assert(DE.Type != DependencyEdge::DT_INVALID && "Unexpected invalid edge!");
+
+ OS << " FROM: " << FromIID << " TO: " << ToIID << " ";
+ if (DE.Type == DependencyEdge::DT_REGISTER) {
+ OS << " - REGISTER: ";
+ MCIP.printRegName(OS, DE.ResourceOrRegID);
+ } else if (DE.Type == DependencyEdge::DT_MEMORY) {
+ OS << " - MEMORY";
+ } else {
+ assert(DE.Type == DependencyEdge::DT_RESOURCE &&
+ "Unsupported dependency type!");
+ OS << " - RESOURCE MASK: " << DE.ResourceOrRegID;
+ }
+ OS << " - COST: " << DE.Cost << '\n';
+}
+#endif // NDEBUG
+
+void DependencyGraph::pruneEdges(unsigned Iterations) {
+ for (DGNode &N : Nodes) {
+ unsigned NumPruned = 0;
+ const unsigned Size = N.OutgoingEdges.size();
+ // Use a cut-off threshold to prune edges with a low frequency.
+ for (unsigned I = 0, E = Size; I < E; ++I) {
+ DependencyEdge &Edge = N.OutgoingEdges[I];
+ if (Edge.Frequency == Iterations)
+ continue;
+ double Factor = (double)Edge.Frequency / Iterations;
+ if (0.10 < Factor)
+ continue;
+ Nodes[Edge.ToIID].NumPredecessors--;
+ std::swap(Edge, N.OutgoingEdges[E - 1]);
+ --E;
+ ++NumPruned;
+ }
+
+ if (NumPruned)
+ N.OutgoingEdges.resize(Size - NumPruned);
+ }
+}
+
+void DependencyGraph::initializeRootSet(
+ SmallVectorImpl<unsigned> &RootSet) const {
+ for (unsigned I = 0, E = Nodes.size(); I < E; ++I) {
+ const DGNode &N = Nodes[I];
+ if (N.NumPredecessors == 0 && !N.OutgoingEdges.empty())
+ RootSet.emplace_back(I);
+ }
+}
+
+void DependencyGraph::propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet,
+ unsigned Iterations) {
+ SmallVector<unsigned, 8> ToVisit;
+
+ // A critical sequence is computed as the longest path from a node of the
+ // RootSet to a leaf node (i.e. a node with no successors). The RootSet is
+ // composed of nodes with at least one successor, and no predecessors.
+ //
+ // Each node of the graph starts with an initial default cost of zero. The
+ // cost of a node is a measure of criticality: the higher the cost, the bigger
+ // is the performance impact.
+ // For register and memory dependencies, the cost is a function of the write
+ // latency as well as the actual delay (in cycles) caused to users.
+ // For processor resource dependencies, the cost is a function of the resource
+ // pressure. Resource interferences with low frequency values are ignored.
+ //
+ // This algorithm is very similar to a (reverse) Dijkstra. Every iteration of
+ // the inner loop selects (i.e. visits) a node N from a set of `unvisited
+ // nodes`, and then propagates the cost of N to all its neighbors.
+ //
+ // The `unvisited nodes` set initially contains all the nodes from the
+ // RootSet. A node N is added to the `unvisited nodes` if all its
+ // predecessors have been visited already.
+ //
+ // For simplicity, every node tracks the number of unvisited incoming edges in
+ // field `NumVisitedPredecessors`. When the value of that field drops to
+ // zero, then the corresponding node is added to a `ToVisit` set.
+ //
+ // At the end of every iteration of the outer loop, set `ToVisit` becomes our
+ // new `unvisited nodes` set.
+ //
+ // The algorithm terminates when the set of unvisited nodes (i.e. our RootSet)
+ // is empty. This algorithm works under the assumption that the graph is
+ // acyclic.
+ do {
+ for (unsigned IID : RootSet) {
+ const DGNode &N = Nodes[IID];
+ for (const DependencyEdge &DepEdge : N.OutgoingEdges) {
+ unsigned ToIID = DepEdge.ToIID;
+ DGNode &To = Nodes[ToIID];
+ uint64_t Cost = N.Cost + DepEdge.Dep.Cost;
+ // Check if this is the most expensive incoming edge seen so far. In
+ // case, update the total cost of the destination node (ToIID), as well
+ // its field `CriticalPredecessor`.
+ if (Cost > To.Cost) {
+ To.CriticalPredecessor = DepEdge;
+ To.Cost = Cost;
+ To.Depth = N.Depth + 1;
+ }
+ To.NumVisitedPredecessors++;
+ if (To.NumVisitedPredecessors == To.NumPredecessors)
+ ToVisit.emplace_back(ToIID);
+ }
+ }
+
+ std::swap(RootSet, ToVisit);
+ ToVisit.clear();
+ } while (!RootSet.empty());
+}
+
+void DependencyGraph::getCriticalSequence(
+ SmallVectorImpl<const DependencyEdge *> &Seq) const {
+ // At this stage, nodes of the graph have been already visited, and costs have
+ // been propagated through the edges (see method `propagateThroughEdges()`).
+
+ // Identify the node N with the highest cost in the graph. By construction,
+ // that node is the last instruction of our critical sequence.
+ // Field N.Depth would tell us the total length of the sequence.
+ //
+ // To obtain the sequence of critical edges, we simply follow the chain of
+ // critical predecessors starting from node N (field
+ // DGNode::CriticalPredecessor).
+ const auto It = std::max_element(
+ Nodes.begin(), Nodes.end(),
+ [](const DGNode &Lhs, const DGNode &Rhs) { return Lhs.Cost < Rhs.Cost; });
+ unsigned IID = std::distance(Nodes.begin(), It);
+ Seq.resize(Nodes[IID].Depth);
+ for (const DependencyEdge *&DE : llvm::reverse(Seq)) {
+ const DGNode &N = Nodes[IID];
+ DE = &N.CriticalPredecessor;
+ IID = N.CriticalPredecessor.FromIID;
+ }
+}
+
+void BottleneckAnalysis::printInstruction(formatted_raw_ostream &FOS,
+ const MCInst &MCI,
+ bool UseDifferentColor) const {
+ FOS.PadToColumn(14);
+ if (UseDifferentColor)
+ FOS.changeColor(raw_ostream::CYAN, true, false);
+ FOS << printInstructionString(MCI);
+ if (UseDifferentColor)
+ FOS.resetColor();
+}
+
+void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const {
+ // Early exit if no bottlenecks were found during the simulation.
+ if (!SeenStallCycles || !BPI.PressureIncreaseCycles)
+ return;
+
+ SmallVector<const DependencyEdge *, 16> Seq;
+ DG.getCriticalSequence(Seq);
+ if (Seq.empty())
+ return;
+
+ OS << "\nCritical sequence based on the simulation:\n\n";
+
+ const DependencyEdge &FirstEdge = *Seq[0];
+ ArrayRef<llvm::MCInst> Source = getSource();
+ unsigned FromIID = FirstEdge.FromIID % Source.size();
+ unsigned ToIID = FirstEdge.ToIID % Source.size();
+ bool IsLoopCarried = FromIID >= ToIID;
+
+ formatted_raw_ostream FOS(OS);
+ FOS.PadToColumn(14);
+ FOS << "Instruction";
+ FOS.PadToColumn(58);
+ FOS << "Dependency Information";
+
+ bool HasColors = FOS.has_colors();
+
+ unsigned CurrentIID = 0;
+ if (IsLoopCarried) {
+ FOS << "\n +----< " << FromIID << ".";
+ printInstruction(FOS, Source[FromIID], HasColors);
+ FOS << "\n |\n | < loop carried > \n |";
+ } else {
+ while (CurrentIID < FromIID) {
+ FOS << "\n " << CurrentIID << ".";
+ printInstruction(FOS, Source[CurrentIID]);
+ CurrentIID++;
+ }
+
+ FOS << "\n +----< " << CurrentIID << ".";
+ printInstruction(FOS, Source[CurrentIID], HasColors);
+ CurrentIID++;
+ }
+
+ for (const DependencyEdge *&DE : Seq) {
+ ToIID = DE->ToIID % Source.size();
+ unsigned LastIID = CurrentIID > ToIID ? Source.size() : ToIID;
+
+ while (CurrentIID < LastIID) {
+ FOS << "\n | " << CurrentIID << ".";
+ printInstruction(FOS, Source[CurrentIID]);
+ CurrentIID++;
+ }
+
+ if (CurrentIID == ToIID) {
+ FOS << "\n +----> " << ToIID << ".";
+ printInstruction(FOS, Source[CurrentIID], HasColors);
+ } else {
+ FOS << "\n |\n | < loop carried > \n |"
+ << "\n +----> " << ToIID << ".";
+ printInstruction(FOS, Source[ToIID], HasColors);
+ }
+ FOS.PadToColumn(58);
+
+ const DependencyEdge::Dependency &Dep = DE->Dep;
+ if (HasColors)
+ FOS.changeColor(raw_ostream::SAVEDCOLOR, true, false);
+
+ if (Dep.Type == DependencyEdge::DT_REGISTER) {
+ FOS << "## REGISTER dependency: ";
+ if (HasColors)
+ FOS.changeColor(raw_ostream::MAGENTA, true, false);
+ getInstPrinter().printRegName(FOS, Dep.ResourceOrRegID);
+ } else if (Dep.Type == DependencyEdge::DT_MEMORY) {
+ FOS << "## MEMORY dependency.";
+ } else {
+ assert(Dep.Type == DependencyEdge::DT_RESOURCE &&
+ "Unsupported dependency type!");
+ FOS << "## RESOURCE interference: ";
+ if (HasColors)
+ FOS.changeColor(raw_ostream::MAGENTA, true, false);
+ FOS << Tracker.resolveResourceName(Dep.ResourceOrRegID);
+ if (HasColors) {
+ FOS.resetColor();
+ FOS.changeColor(raw_ostream::SAVEDCOLOR, true, false);
+ }
+ FOS << " [ probability: " << ((DE->Frequency * 100) / Iterations)
+ << "% ]";
+ }
+ if (HasColors)
+ FOS.resetColor();
+ ++CurrentIID;
+ }
+
+ while (CurrentIID < Source.size()) {
+ FOS << "\n " << CurrentIID << ".";
+ printInstruction(FOS, Source[CurrentIID]);
+ CurrentIID++;
+ }
+
+ FOS << '\n';
+ FOS.flush();
+}
+
+#ifndef NDEBUG
+void DependencyGraph::dump(raw_ostream &OS, MCInstPrinter &MCIP) const {
+ OS << "\nREG DEPS\n";
+ for (const DGNode &Node : Nodes)
+ for (const DependencyEdge &DE : Node.OutgoingEdges)
+ if (DE.Dep.Type == DependencyEdge::DT_REGISTER)
+ dumpDependencyEdge(OS, DE, MCIP);
+
+ OS << "\nMEM DEPS\n";
+ for (const DGNode &Node : Nodes)
+ for (const DependencyEdge &DE : Node.OutgoingEdges)
+ if (DE.Dep.Type == DependencyEdge::DT_MEMORY)
+ dumpDependencyEdge(OS, DE, MCIP);
+
+ OS << "\nRESOURCE DEPS\n";
+ for (const DGNode &Node : Nodes)
+ for (const DependencyEdge &DE : Node.OutgoingEdges)
+ if (DE.Dep.Type == DependencyEdge::DT_RESOURCE)
+ dumpDependencyEdge(OS, DE, MCIP);
+}
+#endif // NDEBUG
+
+void DependencyGraph::addDependency(unsigned From, unsigned To,
+ DependencyEdge::Dependency &&Dep) {
+ DGNode &NodeFrom = Nodes[From];
+ DGNode &NodeTo = Nodes[To];
+ SmallVectorImpl<DependencyEdge> &Vec = NodeFrom.OutgoingEdges;
+
+ auto It = find_if(Vec, [To, Dep](DependencyEdge &DE) {
+ return DE.ToIID == To && DE.Dep.ResourceOrRegID == Dep.ResourceOrRegID;
+ });
+
+ if (It != Vec.end()) {
+ It->Dep.Cost += Dep.Cost;
+ It->Frequency++;
+ return;
+ }
+
+ DependencyEdge DE = {Dep, From, To, 1};
+ Vec.emplace_back(DE);
+ NodeTo.NumPredecessors++;
+}
+
+BottleneckAnalysis::BottleneckAnalysis(const MCSubtargetInfo &sti,
+ MCInstPrinter &Printer,
+ ArrayRef<MCInst> S, unsigned NumIter)
+ : InstructionView(sti, Printer, S), Tracker(sti.getSchedModel()),
+ DG(S.size() * 3), Iterations(NumIter), TotalCycles(0),
+ PressureIncreasedBecauseOfResources(false),
+ PressureIncreasedBecauseOfRegisterDependencies(false),
+ PressureIncreasedBecauseOfMemoryDependencies(false),
+ SeenStallCycles(false), BPI() {}
+
+void BottleneckAnalysis::addRegisterDep(unsigned From, unsigned To,
+ unsigned RegID, unsigned Cost) {
+ bool IsLoopCarried = From >= To;
+ unsigned SourceSize = getSource().size();
+ if (IsLoopCarried) {
+ DG.addRegisterDep(From, To + SourceSize, RegID, Cost);
+ DG.addRegisterDep(From + SourceSize, To + (SourceSize * 2), RegID, Cost);
+ return;
+ }
+ DG.addRegisterDep(From + SourceSize, To + SourceSize, RegID, Cost);
+}
+
+void BottleneckAnalysis::addMemoryDep(unsigned From, unsigned To,
+ unsigned Cost) {
+ bool IsLoopCarried = From >= To;
+ unsigned SourceSize = getSource().size();
+ if (IsLoopCarried) {
+ DG.addMemoryDep(From, To + SourceSize, Cost);
+ DG.addMemoryDep(From + SourceSize, To + (SourceSize * 2), Cost);
+ return;
+ }
+ DG.addMemoryDep(From + SourceSize, To + SourceSize, Cost);
+}
+
+void BottleneckAnalysis::addResourceDep(unsigned From, unsigned To,
+ uint64_t Mask, unsigned Cost) {
+ bool IsLoopCarried = From >= To;
+ unsigned SourceSize = getSource().size();
+ if (IsLoopCarried) {
+ DG.addResourceDep(From, To + SourceSize, Mask, Cost);
+ DG.addResourceDep(From + SourceSize, To + (SourceSize * 2), Mask, Cost);
+ return;
+ }
+ DG.addResourceDep(From + SourceSize, To + SourceSize, Mask, Cost);
+}
+
+void BottleneckAnalysis::onEvent(const HWInstructionEvent &Event) {
+ const unsigned IID = Event.IR.getSourceIndex();
+ if (Event.Type == HWInstructionEvent::Dispatched) {
+ Tracker.onInstructionDispatched(IID);
+ return;
+ }
+ if (Event.Type == HWInstructionEvent::Executed) {
+ Tracker.onInstructionExecuted(IID);
+ return;
+ }
+
+ if (Event.Type != HWInstructionEvent::Issued)
+ return;
+
+ ArrayRef<llvm::MCInst> Source = getSource();
+ const Instruction &IS = *Event.IR.getInstruction();
+ unsigned To = IID % Source.size();
+
+ unsigned Cycles = 2 * Tracker.getResourcePressureCycles(IID);
+ uint64_t ResourceMask = IS.getCriticalResourceMask();
+ SmallVector<std::pair<unsigned, unsigned>, 4> Users;
+ while (ResourceMask) {
+ uint64_t Current = ResourceMask & (-ResourceMask);
+ Tracker.getResourceUsers(Current, Users);
+ for (const std::pair<unsigned, unsigned> &U : Users)
+ addResourceDep(U.first % Source.size(), To, Current, U.second + Cycles);
+ Users.clear();
+ ResourceMask ^= Current;
+ }
+
+ const CriticalDependency &RegDep = IS.getCriticalRegDep();
+ if (RegDep.Cycles) {
+ Cycles = RegDep.Cycles + 2 * Tracker.getRegisterPressureCycles(IID);
+ unsigned From = RegDep.IID % Source.size();
+ addRegisterDep(From, To, RegDep.RegID, Cycles);
+ }
+
+ const CriticalDependency &MemDep = IS.getCriticalMemDep();
+ if (MemDep.Cycles) {
+ Cycles = MemDep.Cycles + 2 * Tracker.getMemoryPressureCycles(IID);
+ unsigned From = MemDep.IID % Source.size();
+ addMemoryDep(From, To, Cycles);
+ }
+
+ Tracker.handleInstructionIssuedEvent(
+ static_cast<const HWInstructionIssuedEvent &>(Event));
+
+ // Check if this is the last simulated instruction.
+ if (IID == ((Iterations * Source.size()) - 1))
+ DG.finalizeGraph(Iterations);
+}
+
+void BottleneckAnalysis::onEvent(const HWPressureEvent &Event) {
+ assert(Event.Reason != HWPressureEvent::INVALID &&
+ "Unexpected invalid event!");
+
+ Tracker.handlePressureEvent(Event);
+
+ switch (Event.Reason) {
+ default:
+ break;
+
+ case HWPressureEvent::RESOURCES:
+ PressureIncreasedBecauseOfResources = true;
+ break;
+ case HWPressureEvent::REGISTER_DEPS:
+ PressureIncreasedBecauseOfRegisterDependencies = true;
+ break;
+ case HWPressureEvent::MEMORY_DEPS:
+ PressureIncreasedBecauseOfMemoryDependencies = true;
+ break;
+ }
+}
+
+void BottleneckAnalysis::onCycleEnd() {
+ ++TotalCycles;
+
+ bool PressureIncreasedBecauseOfDataDependencies =
+ PressureIncreasedBecauseOfRegisterDependencies ||
+ PressureIncreasedBecauseOfMemoryDependencies;
+ if (!PressureIncreasedBecauseOfResources &&
+ !PressureIncreasedBecauseOfDataDependencies)
+ return;
+
+ ++BPI.PressureIncreaseCycles;
+ if (PressureIncreasedBecauseOfRegisterDependencies)
+ ++BPI.RegisterDependencyCycles;
+ if (PressureIncreasedBecauseOfMemoryDependencies)
+ ++BPI.MemoryDependencyCycles;
+ if (PressureIncreasedBecauseOfDataDependencies)
+ ++BPI.DataDependencyCycles;
+ if (PressureIncreasedBecauseOfResources)
+ ++BPI.ResourcePressureCycles;
+ PressureIncreasedBecauseOfResources = false;
+ PressureIncreasedBecauseOfRegisterDependencies = false;
+ PressureIncreasedBecauseOfMemoryDependencies = false;
+}
+
+void BottleneckAnalysis::printBottleneckHints(raw_ostream &OS) const {
+ if (!SeenStallCycles || !BPI.PressureIncreaseCycles) {
+ OS << "\n\nNo resource or data dependency bottlenecks discovered.\n";
+ return;
+ }
+
+ double PressurePerCycle =
+ (double)BPI.PressureIncreaseCycles * 100 / TotalCycles;
+ double ResourcePressurePerCycle =
+ (double)BPI.ResourcePressureCycles * 100 / TotalCycles;
+ double DDPerCycle = (double)BPI.DataDependencyCycles * 100 / TotalCycles;
+ double RegDepPressurePerCycle =
+ (double)BPI.RegisterDependencyCycles * 100 / TotalCycles;
+ double MemDepPressurePerCycle =
+ (double)BPI.MemoryDependencyCycles * 100 / TotalCycles;
+
+ OS << "\n\nCycles with backend pressure increase [ "
+ << format("%.2f", floor((PressurePerCycle * 100) + 0.5) / 100) << "% ]";
+
+ OS << "\nThroughput Bottlenecks: "
+ << "\n Resource Pressure [ "
+ << format("%.2f", floor((ResourcePressurePerCycle * 100) + 0.5) / 100)
+ << "% ]";
+
+ if (BPI.PressureIncreaseCycles) {
+ ArrayRef<unsigned> Distribution = Tracker.getResourcePressureDistribution();
+ const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
+ for (unsigned I = 0, E = Distribution.size(); I < E; ++I) {
+ unsigned ResourceCycles = Distribution[I];
+ if (ResourceCycles) {
+ double Frequency = (double)ResourceCycles * 100 / TotalCycles;
+ const MCProcResourceDesc &PRDesc = *SM.getProcResource(I);
+ OS << "\n - " << PRDesc.Name << " [ "
+ << format("%.2f", floor((Frequency * 100) + 0.5) / 100) << "% ]";
+ }
+ }
+ }
+
+ OS << "\n Data Dependencies: [ "
+ << format("%.2f", floor((DDPerCycle * 100) + 0.5) / 100) << "% ]";
+ OS << "\n - Register Dependencies [ "
+ << format("%.2f", floor((RegDepPressurePerCycle * 100) + 0.5) / 100)
+ << "% ]";
+ OS << "\n - Memory Dependencies [ "
+ << format("%.2f", floor((MemDepPressurePerCycle * 100) + 0.5) / 100)
+ << "% ]\n";
+}
+
+void BottleneckAnalysis::printView(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ printBottleneckHints(TempStream);
+ TempStream.flush();
+ OS << Buffer;
+ printCriticalSequence(OS);
+}
+
+} // namespace mca.
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/BottleneckAnalysis.h b/contrib/libs/llvm14/tools/llvm-mca/Views/BottleneckAnalysis.h
new file mode 100644
index 00000000000..cd5af0afcf5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/BottleneckAnalysis.h
@@ -0,0 +1,348 @@
+//===--------------------- BottleneckAnalysis.h -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the bottleneck analysis view.
+///
+/// This view internally observes backend pressure increase events in order to
+/// identify problematic data dependencies and processor resource interferences.
+///
+/// Example of bottleneck analysis report for a dot-product on X86 btver2:
+///
+/// Cycles with backend pressure increase [ 40.76% ]
+/// Throughput Bottlenecks:
+/// Resource Pressure [ 39.34% ]
+/// - JFPA [ 39.34% ]
+/// - JFPU0 [ 39.34% ]
+/// Data Dependencies: [ 1.42% ]
+/// - Register Dependencies [ 1.42% ]
+/// - Memory Dependencies [ 0.00% ]
+///
+/// According to the example, backend pressure increased during the 40.76% of
+/// the simulated cycles. In particular, the major cause of backend pressure
+/// increases was the contention on floating point adder JFPA accessible from
+/// pipeline resource JFPU0.
+///
+/// At the end of each cycle, if pressure on the simulated out-of-order buffers
+/// has increased, a backend pressure event is reported.
+/// In particular, this occurs when there is a delta between the number of uOps
+/// dispatched and the number of uOps issued to the underlying pipelines.
+///
+/// The bottleneck analysis view is also responsible for identifying and
+/// printing the most "critical" sequence of dependent instructions according to
+/// the simulated run.
+///
+/// Below is the critical sequence computed for the dot-product example on
+/// btver2:
+///
+/// Instruction Dependency Information
+/// +----< 2. vhaddps %xmm3, %xmm3, %xmm4
+/// |
+/// | < loop carried >
+/// |
+/// | 0. vmulps %xmm0, %xmm0, %xmm2
+/// +----> 1. vhaddps %xmm2, %xmm2, %xmm3 ## RESOURCE interference: JFPA [ probability: 73% ]
+/// +----> 2. vhaddps %xmm3, %xmm3, %xmm4 ## REGISTER dependency: %xmm3
+/// |
+/// | < loop carried >
+/// |
+/// +----> 1. vhaddps %xmm2, %xmm2, %xmm3 ## RESOURCE interference: JFPA [ probability: 73% ]
+///
+///
+/// The algorithm that computes the critical sequence is very similar to a
+/// critical path analysis.
+///
+/// A dependency graph is used internally to track dependencies between nodes.
+/// Nodes of the graph represent instructions from the input assembly sequence,
+/// and edges of the graph represent data dependencies or processor resource
+/// interferences.
+///
+/// Edges are dynamically 'discovered' by observing instruction state
+/// transitions and backend pressure increase events. Edges are internally
+/// ranked based on their "criticality". A dependency is considered to be
+/// critical if it takes a long time to execute, and if it contributes to
+/// backend pressure increases. Criticality is internally measured in terms of
+/// cycles; it is computed for every edge in the graph as a function of the edge
+/// latency and the number of backend pressure increase cycles contributed by
+/// that edge.
+///
+/// At the end of simulation, costs are propagated to nodes through the edges of
+/// the graph, and the most expensive path connecting the root-set (a
+/// set of nodes with no predecessors) to a leaf node is reported as critical
+/// sequence.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_BOTTLENECK_ANALYSIS_H
+#define LLVM_TOOLS_LLVM_MCA_BOTTLENECK_ANALYSIS_H
+
+#include "Views/InstructionView.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCSchedule.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace mca {
+
+class PressureTracker {
+ const MCSchedModel &SM;
+
+ // Resource pressure distribution. There is an element for every processor
+ // resource declared by the scheduling model. Quantities are number of cycles.
+ SmallVector<unsigned, 4> ResourcePressureDistribution;
+
+ // Each processor resource is associated with a so-called processor resource
+ // mask. This vector allows to correlate processor resource IDs with processor
+ // resource masks. There is exactly one element per each processor resource
+ // declared by the scheduling model.
+ SmallVector<uint64_t, 4> ProcResID2Mask;
+
+ // Maps processor resource state indices (returned by calls to
+ // `getResourceStateIndex(Mask)` to processor resource identifiers.
+ SmallVector<unsigned, 4> ResIdx2ProcResID;
+
+ // Maps Processor Resource identifiers to ResourceUsers indices.
+ SmallVector<unsigned, 4> ProcResID2ResourceUsersIndex;
+
+ // Identifies the last user of a processor resource unit.
+ // This vector is updated on every instruction issued event.
+ // There is one entry for every processor resource unit declared by the
+ // processor model. An all_ones value is treated like an invalid instruction
+ // identifier.
+ using User = std::pair<unsigned, unsigned>;
+ SmallVector<User, 4> ResourceUsers;
+
+ struct InstructionPressureInfo {
+ unsigned RegisterPressureCycles;
+ unsigned MemoryPressureCycles;
+ unsigned ResourcePressureCycles;
+ };
+ DenseMap<unsigned, InstructionPressureInfo> IPI;
+
+ void updateResourcePressureDistribution(uint64_t CumulativeMask);
+
+ User getResourceUser(unsigned ProcResID, unsigned UnitID) const {
+ unsigned Index = ProcResID2ResourceUsersIndex[ProcResID];
+ return ResourceUsers[Index + UnitID];
+ }
+
+public:
+ PressureTracker(const MCSchedModel &Model);
+
+ ArrayRef<unsigned> getResourcePressureDistribution() const {
+ return ResourcePressureDistribution;
+ }
+
+ void getResourceUsers(uint64_t ResourceMask,
+ SmallVectorImpl<User> &Users) const;
+
+ unsigned getRegisterPressureCycles(unsigned IID) const {
+ assert(IPI.find(IID) != IPI.end() && "Instruction is not tracked!");
+ const InstructionPressureInfo &Info = IPI.find(IID)->second;
+ return Info.RegisterPressureCycles;
+ }
+
+ unsigned getMemoryPressureCycles(unsigned IID) const {
+ assert(IPI.find(IID) != IPI.end() && "Instruction is not tracked!");
+ const InstructionPressureInfo &Info = IPI.find(IID)->second;
+ return Info.MemoryPressureCycles;
+ }
+
+ unsigned getResourcePressureCycles(unsigned IID) const {
+ assert(IPI.find(IID) != IPI.end() && "Instruction is not tracked!");
+ const InstructionPressureInfo &Info = IPI.find(IID)->second;
+ return Info.ResourcePressureCycles;
+ }
+
+ const char *resolveResourceName(uint64_t ResourceMask) const {
+ unsigned Index = getResourceStateIndex(ResourceMask);
+ unsigned ProcResID = ResIdx2ProcResID[Index];
+ const MCProcResourceDesc &PRDesc = *SM.getProcResource(ProcResID);
+ return PRDesc.Name;
+ }
+
+ void onInstructionDispatched(unsigned IID);
+ void onInstructionExecuted(unsigned IID);
+
+ void handlePressureEvent(const HWPressureEvent &Event);
+ void handleInstructionIssuedEvent(const HWInstructionIssuedEvent &Event);
+};
+
+// A dependency edge.
+struct DependencyEdge {
+ enum DependencyType { DT_INVALID, DT_REGISTER, DT_MEMORY, DT_RESOURCE };
+
+ // Dependency edge descriptor.
+ //
+ // It specifies the dependency type, as well as the edge cost in cycles.
+ struct Dependency {
+ DependencyType Type;
+ uint64_t ResourceOrRegID;
+ uint64_t Cost;
+ };
+ Dependency Dep;
+
+ unsigned FromIID;
+ unsigned ToIID;
+
+ // Used by the bottleneck analysis to compute the interference
+ // probability for processor resources.
+ unsigned Frequency;
+};
+
+// A dependency graph used by the bottleneck analysis to describe data
+// dependencies and processor resource interferences between instructions.
+//
+// There is a node (an instance of struct DGNode) for every instruction in the
+// input assembly sequence. Edges of the graph represent dependencies between
+// instructions.
+//
+// Each edge of the graph is associated with a cost value which is used
+// internally to rank dependency based on their impact on the runtime
+// performance (see field DependencyEdge::Dependency::Cost). In general, the
+// higher the cost of an edge, the higher the impact on performance.
+//
+// The cost of a dependency is a function of both the latency and the number of
+// cycles where the dependency has been seen as critical (i.e. contributing to
+// back-pressure increases).
+//
+// Loop carried dependencies are carefully expanded by the bottleneck analysis
+// to guarantee that the graph stays acyclic. To this end, extra nodes are
+// pre-allocated at construction time to describe instructions from "past and
+// future" iterations. The graph is kept acyclic mainly because it simplifies
+// the complexity of the algorithm that computes the critical sequence.
+class DependencyGraph {
+ struct DGNode {
+ unsigned NumPredecessors;
+ unsigned NumVisitedPredecessors;
+ uint64_t Cost;
+ unsigned Depth;
+
+ DependencyEdge CriticalPredecessor;
+ SmallVector<DependencyEdge, 8> OutgoingEdges;
+ };
+ SmallVector<DGNode, 16> Nodes;
+
+ DependencyGraph(const DependencyGraph &) = delete;
+ DependencyGraph &operator=(const DependencyGraph &) = delete;
+
+ void addDependency(unsigned From, unsigned To,
+ DependencyEdge::Dependency &&DE);
+
+ void pruneEdges(unsigned Iterations);
+ void initializeRootSet(SmallVectorImpl<unsigned> &RootSet) const;
+ void propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet,
+ unsigned Iterations);
+
+#ifndef NDEBUG
+ void dumpDependencyEdge(raw_ostream &OS, const DependencyEdge &DE,
+ MCInstPrinter &MCIP) const;
+#endif
+
+public:
+ DependencyGraph(unsigned Size) : Nodes(Size) {}
+
+ void addRegisterDep(unsigned From, unsigned To, unsigned RegID,
+ unsigned Cost) {
+ addDependency(From, To, {DependencyEdge::DT_REGISTER, RegID, Cost});
+ }
+
+ void addMemoryDep(unsigned From, unsigned To, unsigned Cost) {
+ addDependency(From, To, {DependencyEdge::DT_MEMORY, /* unused */ 0, Cost});
+ }
+
+ void addResourceDep(unsigned From, unsigned To, uint64_t Mask,
+ unsigned Cost) {
+ addDependency(From, To, {DependencyEdge::DT_RESOURCE, Mask, Cost});
+ }
+
+ // Called by the bottleneck analysis at the end of simulation to propagate
+ // costs through the edges of the graph, and compute a critical path.
+ void finalizeGraph(unsigned Iterations) {
+ SmallVector<unsigned, 16> RootSet;
+ pruneEdges(Iterations);
+ initializeRootSet(RootSet);
+ propagateThroughEdges(RootSet, Iterations);
+ }
+
+ // Returns a sequence of edges representing the critical sequence based on the
+ // simulated run. It assumes that the graph has already been finalized (i.e.
+ // method `finalizeGraph()` has already been called on this graph).
+ void getCriticalSequence(SmallVectorImpl<const DependencyEdge *> &Seq) const;
+
+#ifndef NDEBUG
+ void dump(raw_ostream &OS, MCInstPrinter &MCIP) const;
+#endif
+};
+
+/// A view that collects and prints a few performance numbers.
+class BottleneckAnalysis : public InstructionView {
+ PressureTracker Tracker;
+ DependencyGraph DG;
+
+ unsigned Iterations;
+ unsigned TotalCycles;
+
+ bool PressureIncreasedBecauseOfResources;
+ bool PressureIncreasedBecauseOfRegisterDependencies;
+ bool PressureIncreasedBecauseOfMemoryDependencies;
+ // True if throughput was affected by dispatch stalls.
+ bool SeenStallCycles;
+
+ struct BackPressureInfo {
+ // Cycles where backpressure increased.
+ unsigned PressureIncreaseCycles;
+ // Cycles where backpressure increased because of pipeline pressure.
+ unsigned ResourcePressureCycles;
+ // Cycles where backpressure increased because of data dependencies.
+ unsigned DataDependencyCycles;
+ // Cycles where backpressure increased because of register dependencies.
+ unsigned RegisterDependencyCycles;
+ // Cycles where backpressure increased because of memory dependencies.
+ unsigned MemoryDependencyCycles;
+ };
+ BackPressureInfo BPI;
+
+ // Used to populate the dependency graph DG.
+ void addRegisterDep(unsigned From, unsigned To, unsigned RegID, unsigned Cy);
+ void addMemoryDep(unsigned From, unsigned To, unsigned Cy);
+ void addResourceDep(unsigned From, unsigned To, uint64_t Mask, unsigned Cy);
+
+ void printInstruction(formatted_raw_ostream &FOS, const MCInst &MCI,
+ bool UseDifferentColor = false) const;
+
+ // Prints a bottleneck message to OS.
+ void printBottleneckHints(raw_ostream &OS) const;
+ void printCriticalSequence(raw_ostream &OS) const;
+
+public:
+ BottleneckAnalysis(const MCSubtargetInfo &STI, MCInstPrinter &MCIP,
+ ArrayRef<MCInst> Sequence, unsigned Iterations);
+
+ void onCycleEnd() override;
+ void onEvent(const HWStallEvent &Event) override { SeenStallCycles = true; }
+ void onEvent(const HWPressureEvent &Event) override;
+ void onEvent(const HWInstructionEvent &Event) override;
+
+ void printView(raw_ostream &OS) const override;
+ StringRef getNameAsString() const override { return "BottleneckAnalysis"; }
+ bool isSerializable() const override { return false; }
+
+#ifndef NDEBUG
+ void dump(raw_ostream &OS, MCInstPrinter &MCIP) const { DG.dump(OS, MCIP); }
+#endif
+};
+
+} // namespace mca
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/DispatchStatistics.cpp b/contrib/libs/llvm14/tools/llvm-mca/Views/DispatchStatistics.cpp
new file mode 100644
index 00000000000..3dc17c8754d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/DispatchStatistics.cpp
@@ -0,0 +1,98 @@
+//===--------------------- DispatchStatistics.cpp ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \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<const 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 << "\nUSH - Uncategorised Structural Hazard: ";
+ printStalls(SS, HWStalls[HWStallEvent::CustomBehaviourStall], NumCycles);
+ SS << '\n';
+ SS.flush();
+ OS << Buffer;
+}
+
+json::Value DispatchStatistics::toJSON() const {
+ json::Object JO({{"RAT", HWStalls[HWStallEvent::RegisterFileStall]},
+ {"RCU", HWStalls[HWStallEvent::RetireControlUnitStall]},
+ {"SCHEDQ", HWStalls[HWStallEvent::SchedulerQueueFull]},
+ {"LQ", HWStalls[HWStallEvent::LoadQueueFull]},
+ {"SQ", HWStalls[HWStallEvent::StoreQueueFull]},
+ {"GROUP", HWStalls[HWStallEvent::DispatchGroupStall]},
+ {"USH", HWStalls[HWStallEvent::CustomBehaviourStall]}});
+ return JO;
+}
+
+} // namespace mca
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/DispatchStatistics.h b/contrib/libs/llvm14/tools/llvm-mca/Views/DispatchStatistics.h
new file mode 100644
index 00000000000..cfd12691c03
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/DispatchStatistics.h
@@ -0,0 +1,87 @@
+//===--------------------- DispatchStatistics.h -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements a view that prints a few statistics related to the
+/// dispatch logic. It collects and analyzes instruction dispatch events as
+/// well as static/dynamic dispatch stall events.
+///
+/// Example:
+/// ========
+///
+/// Dynamic Dispatch Stall Cycles:
+/// RAT - Register unavailable: 0
+/// RCU - Retire tokens unavailable: 0
+/// SCHEDQ - Scheduler full: 42
+/// LQ - Load queue full: 0
+/// SQ - Store queue full: 0
+/// GROUP - Static restrictions on the dispatch group: 0
+///
+///
+/// Dispatch Logic - number of cycles where we saw N micro opcodes dispatched:
+/// [# dispatched], [# cycles]
+/// 0, 15 (11.5%)
+/// 2, 4 (3.1%)
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_DISPATCHVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_DISPATCHVIEW_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MCA/View.h"
+#include <map>
+
+namespace llvm {
+namespace mca {
+
+class DispatchStatistics : public View {
+ unsigned NumDispatched;
+ unsigned NumCycles;
+
+ // Counts dispatch stall events caused by unavailability of resources. There
+ // is one counter for every generic stall kind (see class HWStallEvent).
+ llvm::SmallVector<unsigned, 8> HWStalls;
+
+ using Histogram = std::map<unsigned, unsigned>;
+ Histogram DispatchGroupSizePerCycle;
+
+ void updateHistograms() {
+ DispatchGroupSizePerCycle[NumDispatched]++;
+ NumDispatched = 0;
+ }
+
+ void printDispatchHistogram(llvm::raw_ostream &OS) const;
+
+ void printDispatchStalls(llvm::raw_ostream &OS) const;
+
+public:
+ DispatchStatistics()
+ : NumDispatched(0), NumCycles(0),
+ HWStalls(HWStallEvent::LastGenericEvent) {}
+
+ void onEvent(const HWStallEvent &Event) override;
+
+ void onEvent(const HWInstructionEvent &Event) override;
+
+ void onCycleBegin() override { NumCycles++; }
+
+ void onCycleEnd() override { updateHistograms(); }
+
+ void printView(llvm::raw_ostream &OS) const override {
+ printDispatchStalls(OS);
+ printDispatchHistogram(OS);
+ }
+ StringRef getNameAsString() const override { return "DispatchStatistics"; }
+ json::Value toJSON() const override;
+};
+} // namespace mca
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionInfoView.cpp b/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionInfoView.cpp
new file mode 100644
index 00000000000..caa8554a416
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionInfoView.cpp
@@ -0,0 +1,177 @@
+//===--------------------- InstructionInfoView.cpp --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the InstructionInfoView API.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Views/InstructionInfoView.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/JSON.h"
+
+namespace llvm {
+namespace mca {
+
+void InstructionInfoView::printView(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+
+ ArrayRef<llvm::MCInst> Source = getSource();
+ if (!Source.size())
+ return;
+
+ IIVDVec IIVD(Source.size());
+ collectData(IIVD);
+
+ TempStream << "\n\nInstruction Info:\n";
+ TempStream << "[1]: #uOps\n[2]: Latency\n[3]: RThroughput\n"
+ << "[4]: MayLoad\n[5]: MayStore\n[6]: HasSideEffects (U)\n";
+ if (PrintBarriers) {
+ TempStream << "[7]: LoadBarrier\n[8]: StoreBarrier\n";
+ }
+ if (PrintEncodings) {
+ if (PrintBarriers) {
+ TempStream << "[9]: Encoding Size\n";
+ TempStream << "\n[1] [2] [3] [4] [5] [6] [7] [8] "
+ << "[9] Encodings: Instructions:\n";
+ } else {
+ TempStream << "[7]: Encoding Size\n";
+ TempStream << "\n[1] [2] [3] [4] [5] [6] [7] "
+ << "Encodings: Instructions:\n";
+ }
+ } else {
+ if (PrintBarriers) {
+ TempStream << "\n[1] [2] [3] [4] [5] [6] [7] [8] "
+ << "Instructions:\n";
+ } else {
+ TempStream << "\n[1] [2] [3] [4] [5] [6] "
+ << "Instructions:\n";
+ }
+ }
+
+ int Index = 0;
+ for (const auto &I : enumerate(zip(IIVD, Source))) {
+ const InstructionInfoViewData &IIVDEntry = std::get<0>(I.value());
+
+ TempStream << ' ' << IIVDEntry.NumMicroOpcodes << " ";
+ if (IIVDEntry.NumMicroOpcodes < 10)
+ TempStream << " ";
+ else if (IIVDEntry.NumMicroOpcodes < 100)
+ TempStream << ' ';
+ TempStream << IIVDEntry.Latency << " ";
+ if (IIVDEntry.Latency < 10)
+ TempStream << " ";
+ else if (IIVDEntry.Latency < 100)
+ TempStream << ' ';
+
+ if (IIVDEntry.RThroughput.hasValue()) {
+ double RT = IIVDEntry.RThroughput.getValue();
+ TempStream << format("%.2f", RT) << ' ';
+ if (RT < 10.0)
+ TempStream << " ";
+ else if (RT < 100.0)
+ TempStream << ' ';
+ } else {
+ TempStream << " - ";
+ }
+ TempStream << (IIVDEntry.mayLoad ? " * " : " ");
+ TempStream << (IIVDEntry.mayStore ? " * " : " ");
+ TempStream << (IIVDEntry.hasUnmodeledSideEffects ? " U " : " ");
+
+ if (PrintBarriers) {
+ TempStream << (LoweredInsts[Index]->isALoadBarrier() ? " * "
+ : " ");
+ TempStream << (LoweredInsts[Index]->isAStoreBarrier() ? " * "
+ : " ");
+ }
+
+ if (PrintEncodings) {
+ StringRef Encoding(CE.getEncoding(I.index()));
+ unsigned EncodingSize = Encoding.size();
+ TempStream << " " << EncodingSize
+ << (EncodingSize < 10 ? " " : " ");
+ TempStream.flush();
+ formatted_raw_ostream FOS(TempStream);
+ for (unsigned i = 0, e = Encoding.size(); i != e; ++i)
+ FOS << format("%02x ", (uint8_t)Encoding[i]);
+ FOS.PadToColumn(30);
+ FOS.flush();
+ }
+
+ const MCInst &Inst = std::get<1>(I.value());
+ TempStream << printInstructionString(Inst) << '\n';
+ ++Index;
+ }
+
+ TempStream.flush();
+ OS << Buffer;
+}
+
+void InstructionInfoView::collectData(
+ MutableArrayRef<InstructionInfoViewData> IIVD) const {
+ const llvm::MCSubtargetInfo &STI = getSubTargetInfo();
+ const MCSchedModel &SM = STI.getSchedModel();
+ for (const auto I : zip(getSource(), IIVD)) {
+ const MCInst &Inst = std::get<0>(I);
+ InstructionInfoViewData &IIVDEntry = std::get<1>(I);
+ const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode());
+
+ // Obtain the scheduling class information from the instruction.
+ unsigned SchedClassID = MCDesc.getSchedClass();
+ unsigned CPUID = SM.getProcessorID();
+
+ // Try to solve variant scheduling classes.
+ while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
+ SchedClassID =
+ STI.resolveVariantSchedClass(SchedClassID, &Inst, &MCII, CPUID);
+
+ const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
+ IIVDEntry.NumMicroOpcodes = SCDesc.NumMicroOps;
+ IIVDEntry.Latency = MCSchedModel::computeInstrLatency(STI, SCDesc);
+ // Add extra latency due to delays in the forwarding data paths.
+ IIVDEntry.Latency += MCSchedModel::getForwardingDelayCycles(
+ STI.getReadAdvanceEntries(SCDesc));
+ IIVDEntry.RThroughput = MCSchedModel::getReciprocalThroughput(STI, SCDesc);
+ IIVDEntry.mayLoad = MCDesc.mayLoad();
+ IIVDEntry.mayStore = MCDesc.mayStore();
+ IIVDEntry.hasUnmodeledSideEffects = MCDesc.hasUnmodeledSideEffects();
+ }
+}
+
+// Construct a JSON object from a single InstructionInfoViewData object.
+json::Object
+InstructionInfoView::toJSON(const InstructionInfoViewData &IIVD) const {
+ json::Object JO({{"NumMicroOpcodes", IIVD.NumMicroOpcodes},
+ {"Latency", IIVD.Latency},
+ {"mayLoad", IIVD.mayLoad},
+ {"mayStore", IIVD.mayStore},
+ {"hasUnmodeledSideEffects", IIVD.hasUnmodeledSideEffects}});
+ JO.try_emplace("RThroughput", IIVD.RThroughput.getValueOr(0.0));
+ return JO;
+}
+
+json::Value InstructionInfoView::toJSON() const {
+ ArrayRef<llvm::MCInst> Source = getSource();
+ if (!Source.size())
+ return json::Value(0);
+
+ IIVDVec IIVD(Source.size());
+ collectData(IIVD);
+
+ json::Array InstInfo;
+ for (const auto &I : enumerate(IIVD)) {
+ const InstructionInfoViewData &IIVDEntry = I.value();
+ json::Object JO = toJSON(IIVDEntry);
+ JO.try_emplace("Instruction", (unsigned)I.index());
+ InstInfo.push_back(std::move(JO));
+ }
+ return json::Object({{"InstructionList", json::Value(std::move(InstInfo))}});
+}
+} // namespace mca.
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionInfoView.h b/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionInfoView.h
new file mode 100644
index 00000000000..c35d316775f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionInfoView.h
@@ -0,0 +1,93 @@
+//===--------------------- InstructionInfoView.h ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the instruction info view.
+///
+/// The goal fo the instruction info view is to print the latency and reciprocal
+/// throughput information for every instruction in the input sequence.
+/// This section also reports extra information related to the number of micro
+/// opcodes, and opcode properties (i.e. 'MayLoad', 'MayStore', 'HasSideEffects)
+///
+/// Example:
+///
+/// Instruction Info:
+/// [1]: #uOps
+/// [2]: Latency
+/// [3]: RThroughput
+/// [4]: MayLoad
+/// [5]: MayStore
+/// [6]: HasSideEffects
+///
+/// [1] [2] [3] [4] [5] [6] Instructions:
+/// 1 2 1.00 vmulps %xmm0, %xmm1, %xmm2
+/// 1 3 1.00 vhaddps %xmm2, %xmm2, %xmm3
+/// 1 3 1.00 vhaddps %xmm3, %xmm3, %xmm4
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTIONINFOVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONINFOVIEW_H
+
+#include "Views/InstructionView.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MCA/CodeEmitter.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace llvm {
+namespace mca {
+
+/// A view that prints out generic instruction information.
+class InstructionInfoView : public InstructionView {
+ const llvm::MCInstrInfo &MCII;
+ CodeEmitter &CE;
+ bool PrintEncodings;
+ bool PrintBarriers;
+ using UniqueInst = std::unique_ptr<Instruction>;
+ ArrayRef<UniqueInst> LoweredInsts;
+
+ struct InstructionInfoViewData {
+ unsigned NumMicroOpcodes = 0;
+ unsigned Latency = 0;
+ Optional<double> RThroughput = 0.0;
+ bool mayLoad = false;
+ bool mayStore = false;
+ bool hasUnmodeledSideEffects = false;
+ };
+ using IIVDVec = SmallVector<InstructionInfoViewData, 16>;
+
+ /// Place the data into the array of InstructionInfoViewData IIVD.
+ void collectData(MutableArrayRef<InstructionInfoViewData> IIVD) const;
+
+public:
+ InstructionInfoView(const llvm::MCSubtargetInfo &ST,
+ const llvm::MCInstrInfo &II, CodeEmitter &C,
+ bool ShouldPrintEncodings, llvm::ArrayRef<llvm::MCInst> S,
+ llvm::MCInstPrinter &IP,
+ ArrayRef<UniqueInst> LoweredInsts,
+ bool ShouldPrintBarriers)
+ : InstructionView(ST, IP, S), MCII(II), CE(C),
+ PrintEncodings(ShouldPrintEncodings),
+ PrintBarriers(ShouldPrintBarriers), LoweredInsts(LoweredInsts) {}
+
+ void printView(llvm::raw_ostream &OS) const override;
+ StringRef getNameAsString() const override { return "InstructionInfoView"; }
+ json::Value toJSON() const override;
+ json::Object toJSON(const InstructionInfoViewData &IIVD) const;
+};
+} // namespace mca
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionView.cpp b/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionView.cpp
new file mode 100644
index 00000000000..3b174a06498
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionView.cpp
@@ -0,0 +1,43 @@
+//===----------------------- InstructionView.cpp ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the member functions of the class InstructionView.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Views/InstructionView.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+
+namespace llvm {
+namespace mca {
+
+InstructionView::~InstructionView() = default;
+
+StringRef
+InstructionView::printInstructionString(const llvm::MCInst &MCI) const {
+ InstructionString = "";
+ MCIP.printInst(&MCI, 0, "", STI, InstrStream);
+ InstrStream.flush();
+ // Remove any tabs or spaces at the beginning of the instruction.
+ return StringRef(InstructionString).ltrim();
+}
+
+json::Value InstructionView::toJSON() const {
+ json::Array SourceInfo;
+ for (const auto &MCI : getSource()) {
+ StringRef Instruction = printInstructionString(MCI);
+ SourceInfo.push_back(Instruction.str());
+ }
+ return SourceInfo;
+}
+
+} // namespace mca
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionView.h b/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionView.h
new file mode 100644
index 00000000000..cec07eef6a8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/InstructionView.h
@@ -0,0 +1,59 @@
+//===----------------------- InstructionView.h ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the main interface for Views that examine and reference
+/// a sequence of machine instructions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTIONVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONVIEW_H
+
+#include "llvm/MCA/View.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace mca {
+
+// The base class for views that deal with individual machine instructions.
+class InstructionView : public View {
+ const llvm::MCSubtargetInfo &STI;
+ llvm::MCInstPrinter &MCIP;
+ llvm::ArrayRef<llvm::MCInst> Source;
+
+ mutable std::string InstructionString;
+ mutable raw_string_ostream InstrStream;
+
+public:
+ void printView(llvm::raw_ostream &) const override {}
+ InstructionView(const llvm::MCSubtargetInfo &STI,
+ llvm::MCInstPrinter &Printer, llvm::ArrayRef<llvm::MCInst> S)
+ : STI(STI), MCIP(Printer), Source(S), InstrStream(InstructionString) {}
+
+ virtual ~InstructionView();
+
+ StringRef getNameAsString() const override { return "Instructions"; }
+
+ // Return a reference to a string representing a given machine instruction.
+ // The result should be used or copied before the next call to
+ // printInstructionString() as it will overwrite the previous result.
+ StringRef printInstructionString(const llvm::MCInst &MCI) const;
+ const llvm::MCSubtargetInfo &getSubTargetInfo() const { return STI; }
+
+ llvm::MCInstPrinter &getInstPrinter() const { return MCIP; }
+ llvm::ArrayRef<llvm::MCInst> getSource() const { return Source; }
+
+ json::Value toJSON() const override;
+};
+
+} // namespace mca
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/RegisterFileStatistics.cpp b/contrib/libs/llvm14/tools/llvm-mca/Views/RegisterFileStatistics.cpp
new file mode 100644
index 00000000000..4ef8053bff4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/RegisterFileStatistics.cpp
@@ -0,0 +1,170 @@
+//===--------------------- RegisterFileStatistics.cpp -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \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;
+
+ if (Inst.getDefs().size() != Inst.getUses().size())
+ return;
+
+ for (size_t I = 0, E = Inst.getDefs().size(); I < E; ++I) {
+ const WriteState &WS = Inst.getDefs()[I];
+ const ReadState &RS = Inst.getUses()[E - (I + 1)];
+
+ 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/contrib/libs/llvm14/tools/llvm-mca/Views/RegisterFileStatistics.h b/contrib/libs/llvm14/tools/llvm-mca/Views/RegisterFileStatistics.h
new file mode 100644
index 00000000000..3de2a22ac32
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/RegisterFileStatistics.h
@@ -0,0 +1,84 @@
+//===--------------------- RegisterFileStatistics.h -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This view collects and prints register file usage statistics.
+///
+/// Example (-mcpu=btver2):
+/// ========================
+///
+/// Register File statistics:
+/// Total number of mappings created: 6
+/// Max number of mappings used: 3
+///
+/// * Register File #1 -- FpuPRF:
+/// 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
+/// Total number of mappings created: 6
+/// Max number of mappings used: 3
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_REGISTERFILESTATISTICS_H
+#define LLVM_TOOLS_LLVM_MCA_REGISTERFILESTATISTICS_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MCA/View.h"
+
+namespace llvm {
+namespace mca {
+
+class RegisterFileStatistics : public View {
+ const llvm::MCSubtargetInfo &STI;
+
+ // Used to track the number of physical registers used in a register file.
+ struct RegisterFileUsage {
+ unsigned TotalMappings;
+ unsigned MaxUsedMappings;
+ 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> PRFUsage;
+ llvm::SmallVector<MoveEliminationInfo, 4> MoveElimInfo;
+
+ void updateRegisterFileUsage(ArrayRef<unsigned> UsedPhysRegs);
+ void updateMoveElimInfo(const Instruction &Inst);
+
+public:
+ RegisterFileStatistics(const llvm::MCSubtargetInfo &sti);
+
+ void onCycleEnd() override;
+ void onEvent(const HWInstructionEvent &Event) override;
+ void printView(llvm::raw_ostream &OS) const override;
+ StringRef getNameAsString() const override {
+ return "RegisterFileStatistics";
+ }
+ bool isSerializable() const override { return false; }
+};
+} // namespace mca
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/ResourcePressureView.cpp b/contrib/libs/llvm14/tools/llvm-mca/Views/ResourcePressureView.cpp
new file mode 100644
index 00000000000..77b3ba0b7c8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/ResourcePressureView.cpp
@@ -0,0 +1,200 @@
+//===--------------------- ResourcePressureView.cpp -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements methods in the ResourcePressureView interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Views/ResourcePressureView.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace mca {
+
+ResourcePressureView::ResourcePressureView(const llvm::MCSubtargetInfo &sti,
+ MCInstPrinter &Printer,
+ ArrayRef<MCInst> S)
+ : InstructionView(sti, Printer, S), LastInstructionIdx(0) {
+ // Populate the map of resource descriptors.
+ unsigned R2VIndex = 0;
+ const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
+ for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+ unsigned NumUnits = ProcResource.NumUnits;
+ // Skip groups and invalid resources with zero units.
+ if (ProcResource.SubUnitsIdxBegin || !NumUnits)
+ continue;
+
+ Resource2VecIndex.insert(std::pair<unsigned, unsigned>(I, R2VIndex));
+ R2VIndex += ProcResource.NumUnits;
+ }
+
+ NumResourceUnits = R2VIndex;
+ ResourceUsage.resize(NumResourceUnits * (getSource().size() + 1));
+ std::fill(ResourceUsage.begin(), ResourceUsage.end(), 0.0);
+}
+
+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);
+ ArrayRef<llvm::MCInst> Source = getSource();
+ const unsigned SourceIdx = Event.IR.getSourceIndex() % Source.size();
+ 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];
+ R2VIndex += countTrailingZeros(RR.second);
+ ResourceUsage[R2VIndex + NumResourceUnits * SourceIdx] += Use.second;
+ ResourceUsage[R2VIndex + NumResourceUnits * Source.size()] += Use.second;
+ }
+}
+
+static void printColumnNames(formatted_raw_ostream &OS,
+ const MCSchedModel &SM) {
+ unsigned Column = OS.getColumn();
+ for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();
+ I < E; ++I) {
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+ unsigned NumUnits = ProcResource.NumUnits;
+ // Skip groups and invalid resources with zero units.
+ if (ProcResource.SubUnitsIdxBegin || !NumUnits)
+ continue;
+
+ for (unsigned J = 0; J < NumUnits; ++J) {
+ Column += 7;
+ OS << "[" << ResourceIndex;
+ if (NumUnits > 1)
+ OS << '.' << J;
+ OS << ']';
+ OS.PadToColumn(Column);
+ }
+
+ ResourceIndex++;
+ }
+}
+
+static void printResourcePressure(formatted_raw_ostream &OS, double Pressure,
+ unsigned Col) {
+ if (!Pressure || Pressure < 0.005) {
+ OS << " - ";
+ } else {
+ // Round to the value to the nearest hundredth and then print it.
+ OS << format("%.2f", floor((Pressure * 100) + 0.5) / 100);
+ }
+ OS.PadToColumn(Col);
+}
+
+void ResourcePressureView::printResourcePressurePerIter(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ formatted_raw_ostream FOS(TempStream);
+
+ FOS << "\n\nResources:\n";
+ const MCSchedModel &SM = getSubTargetInfo().getSchedModel();
+ for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();
+ I < E; ++I) {
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+ unsigned NumUnits = ProcResource.NumUnits;
+ // Skip groups and invalid resources with zero units.
+ if (ProcResource.SubUnitsIdxBegin || !NumUnits)
+ continue;
+
+ for (unsigned J = 0; J < NumUnits; ++J) {
+ FOS << '[' << ResourceIndex;
+ if (NumUnits > 1)
+ FOS << '.' << J;
+ FOS << ']';
+ FOS.PadToColumn(6);
+ FOS << "- " << ProcResource.Name << '\n';
+ }
+
+ ResourceIndex++;
+ }
+
+ FOS << "\n\nResource pressure per iteration:\n";
+ FOS.flush();
+ printColumnNames(FOS, SM);
+ FOS << '\n';
+ FOS.flush();
+
+ ArrayRef<llvm::MCInst> Source = getSource();
+ 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);
+ }
+
+ FOS.flush();
+ OS << Buffer;
+}
+
+void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ formatted_raw_ostream FOS(TempStream);
+
+ FOS << "\n\nResource pressure by instruction:\n";
+ printColumnNames(FOS, getSubTargetInfo().getSchedModel());
+ FOS << "Instructions:\n";
+
+ unsigned InstrIndex = 0;
+ ArrayRef<llvm::MCInst> Source = getSource();
+ 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 + BaseEltIdx];
+ printResourcePressure(FOS, Usage / Executions, (J + 1) * 7);
+ }
+
+ FOS << printInstructionString(MCI) << '\n';
+ FOS.flush();
+ OS << Buffer;
+ Buffer = "";
+
+ ++InstrIndex;
+ }
+}
+
+json::Value ResourcePressureView::toJSON() const {
+ // We're dumping the instructions and the ResourceUsage array.
+ json::Array ResourcePressureInfo;
+
+ // The ResourceUsage matrix is sparse, so we only consider
+ // non-zero values.
+ ArrayRef<llvm::MCInst> Source = getSource();
+ const unsigned Executions = LastInstructionIdx / Source.size() + 1;
+ for (const auto &R : enumerate(ResourceUsage)) {
+ const ResourceCycles &RU = R.value();
+ if (RU.getNumerator() == 0)
+ continue;
+ unsigned InstructionIndex = R.index() / NumResourceUnits;
+ unsigned ResourceIndex = R.index() % NumResourceUnits;
+ double Usage = RU / Executions;
+ ResourcePressureInfo.push_back(
+ json::Object({{"InstructionIndex", InstructionIndex},
+ {"ResourceIndex", ResourceIndex},
+ {"ResourceUsage", Usage}}));
+ }
+
+ json::Object JO({{"ResourcePressureInfo", std::move(ResourcePressureInfo)}});
+ return JO;
+}
+} // namespace mca
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/ResourcePressureView.h b/contrib/libs/llvm14/tools/llvm-mca/Views/ResourcePressureView.h
new file mode 100644
index 00000000000..c3993a08c17
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/ResourcePressureView.h
@@ -0,0 +1,103 @@
+//===--------------------- ResourcePressureView.h ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file define class ResourcePressureView.
+/// Class ResourcePressureView observes hardware events generated by
+/// the Pipeline object and collects statistics related to resource usage at
+/// instruction granularity.
+/// Resource pressure information is then printed out to a stream in the
+/// form of a table like the one from the example below:
+///
+/// Resources:
+/// [0] - JALU0
+/// [1] - JALU1
+/// [2] - JDiv
+/// [3] - JFPM
+/// [4] - JFPU0
+/// [5] - JFPU1
+/// [6] - JLAGU
+/// [7] - JSAGU
+/// [8] - JSTC
+/// [9] - JVIMUL
+///
+/// Resource pressure per iteration:
+/// [0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
+/// 0.00 0.00 0.00 0.00 2.00 2.00 0.00 0.00 0.00 0.00
+///
+/// Resource pressure by instruction:
+/// [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] Instructions:
+/// - - - - - 1.00 - - - - vpermilpd $1, %xmm0,
+/// %xmm1
+/// - - - - 1.00 - - - - - vaddps %xmm0, %xmm1,
+/// %xmm2
+/// - - - - - 1.00 - - - - vmovshdup %xmm2, %xmm3
+/// - - - - 1.00 - - - - - vaddss %xmm2, %xmm3,
+/// %xmm4
+///
+/// In this example, we have AVX code executed on AMD Jaguar (btver2).
+/// Both shuffles and vector floating point add operations on XMM registers have
+/// a reciprocal throughput of 1cy.
+/// Each add is issued to pipeline JFPU0, while each shuffle is issued to
+/// pipeline JFPU1. The overall pressure per iteration is reported by two
+/// tables: the first smaller table is the resource pressure per iteration;
+/// the second table reports resource pressure per instruction. Values are the
+/// average resource cycles consumed by an instruction.
+/// Every vector add from the example uses resource JFPU0 for an average of 1cy
+/// per iteration. Consequently, the resource pressure on JFPU0 is of 2cy per
+/// iteration.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_RESOURCEPRESSUREVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_RESOURCEPRESSUREVIEW_H
+
+#include "Views/InstructionView.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 "llvm/Support/JSON.h"
+
+namespace llvm {
+namespace mca {
+
+/// This class collects resource pressure statistics and it is able to print
+/// out all the collected information as a table to an output stream.
+class ResourcePressureView : public InstructionView {
+ 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<ResourceCycles> ResourceUsage;
+ unsigned NumResourceUnits;
+
+ void printResourcePressurePerIter(llvm::raw_ostream &OS) const;
+ void printResourcePressurePerInst(llvm::raw_ostream &OS) const;
+
+public:
+ ResourcePressureView(const llvm::MCSubtargetInfo &sti,
+ llvm::MCInstPrinter &Printer,
+ llvm::ArrayRef<llvm::MCInst> S);
+
+ void onEvent(const HWInstructionEvent &Event) override;
+ void printView(llvm::raw_ostream &OS) const override {
+ printResourcePressurePerIter(OS);
+ printResourcePressurePerInst(OS);
+ }
+ StringRef getNameAsString() const override { return "ResourcePressureView"; }
+ json::Value toJSON() const override;
+};
+} // namespace mca
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp b/contrib/libs/llvm14/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp
new file mode 100644
index 00000000000..1c40428fb01
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp
@@ -0,0 +1,91 @@
+//===--------------------- RetireControlUnitStatistics.cpp ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \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<const 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/contrib/libs/llvm14/tools/llvm-mca/Views/RetireControlUnitStatistics.h b/contrib/libs/llvm14/tools/llvm-mca/Views/RetireControlUnitStatistics.h
new file mode 100644
index 00000000000..ed3736c6451
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/RetireControlUnitStatistics.h
@@ -0,0 +1,64 @@
+//===--------------------- RetireControlUnitStatistics.h --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines class RetireControlUnitStatistics: a view that knows how
+/// to print general statistics related to the retire control unit.
+///
+/// Example:
+/// ========
+///
+/// Retire Control Unit - number of cycles where we saw N instructions retired:
+/// [# retired], [# cycles]
+/// 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 "llvm/MC/MCSchedule.h"
+#include "llvm/MCA/View.h"
+#include <map>
+
+namespace llvm {
+namespace mca {
+
+class RetireControlUnitStatistics : public View {
+ using Histogram = std::map<unsigned, unsigned>;
+ Histogram RetiredPerCycle;
+
+ unsigned NumRetired;
+ unsigned NumCycles;
+ unsigned TotalROBEntries;
+ unsigned EntriesInUse;
+ unsigned MaxUsedEntries;
+ unsigned SumOfUsedEntries;
+
+public:
+ RetireControlUnitStatistics(const MCSchedModel &SM);
+
+ void onEvent(const HWInstructionEvent &Event) override;
+ void onCycleEnd() override;
+ void printView(llvm::raw_ostream &OS) const override;
+ StringRef getNameAsString() const override {
+ return "RetireControlUnitStatistics";
+ }
+ bool isSerializable() const override { return false; }
+};
+
+} // namespace mca
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/SchedulerStatistics.cpp b/contrib/libs/llvm14/tools/llvm-mca/Views/SchedulerStatistics.cpp
new file mode 100644
index 00000000000..7a341d4c207
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/SchedulerStatistics.cpp
@@ -0,0 +1,178 @@
+//===--------------------- SchedulerStatistics.cpp --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \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),
+ 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) {
+ const Instruction &Inst = *Event.IR.getInstruction();
+ NumIssued += Inst.getDesc().NumMicroOps;
+ } 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);
+ }
+
+ IssueWidthPerCycle[NumIssued]++;
+ NumIssued = 0;
+}
+
+void SchedulerStatistics::printSchedulerStats(raw_ostream &OS) const {
+ OS << "\n\nSchedulers - "
+ << "number of cycles where we saw N micro opcodes issued:\n";
+ OS << "[# issued], [# cycles]\n";
+
+ bool HasColors = OS.has_colors();
+ const auto It =
+ std::max_element(IssueWidthPerCycle.begin(), IssueWidthPerCycle.end());
+ for (const std::pair<const unsigned, unsigned> &Entry : IssueWidthPerCycle) {
+ unsigned NumIssued = Entry.first;
+ if (NumIssued == It->first && HasColors)
+ OS.changeColor(raw_ostream::SAVEDCOLOR, true, false);
+
+ unsigned IPC = Entry.second;
+ OS << " " << NumIssued << ", " << 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/contrib/libs/llvm14/tools/llvm-mca/Views/SchedulerStatistics.h b/contrib/libs/llvm14/tools/llvm-mca/Views/SchedulerStatistics.h
new file mode 100644
index 00000000000..9d2f71c13e5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/SchedulerStatistics.h
@@ -0,0 +1,97 @@
+//===--------------------- SchedulerStatistics.h ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines class SchedulerStatistics. Class SchedulerStatistics is a
+/// View that listens to instruction issue events in order to print general
+/// statistics related to the hardware schedulers.
+///
+/// Example:
+/// ========
+///
+/// Schedulers - number of cycles where we saw N instructions issued:
+/// [# issued], [# cycles]
+/// 0, 6 (2.9%)
+/// 1, 106 (50.7%)
+/// 2, 97 (46.4%)
+///
+/// Scheduler's queue usage:
+/// [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 "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MCA/View.h"
+#include <map>
+
+namespace llvm {
+namespace mca {
+
+class SchedulerStatistics final : public View {
+ const llvm::MCSchedModel &SM;
+ 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;
+ };
+
+ using Histogram = std::map<unsigned, unsigned>;
+ Histogram IssueWidthPerCycle;
+
+ std::vector<BufferUsage> Usage;
+
+ void updateHistograms();
+ void printSchedulerStats(llvm::raw_ostream &OS) const;
+ void printSchedulerUsage(llvm::raw_ostream &OS) const;
+
+public:
+ 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(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(const InstRef &IR,
+ llvm::ArrayRef<unsigned> Buffers) override;
+
+ void printView(llvm::raw_ostream &OS) const override;
+ StringRef getNameAsString() const override { return "SchedulerStatistics"; }
+ bool isSerializable() const override { return false; }
+};
+} // namespace mca
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/SummaryView.cpp b/contrib/libs/llvm14/tools/llvm-mca/Views/SummaryView.cpp
new file mode 100644
index 00000000000..bf258b4c26b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/SummaryView.cpp
@@ -0,0 +1,113 @@
+//===--------------------- SummaryView.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the functionalities used by the SummaryView to print
+/// the report information.
+///
+//===----------------------------------------------------------------------===//
+
+#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"
+
+SummaryView::SummaryView(const MCSchedModel &Model, ArrayRef<MCInst> S,
+ unsigned Width)
+ : SM(Model), Source(S), DispatchWidth(Width ? Width : Model.IssueWidth),
+ LastInstructionIdx(0), TotalCycles(0), NumMicroOps(0),
+ ProcResourceUsage(Model.getNumProcResourceKinds(), 0),
+ ProcResourceMasks(Model.getNumProcResourceKinds()),
+ ResIdx2ProcResID(Model.getNumProcResourceKinds(), 0) {
+ computeProcResourceMasks(SM, ProcResourceMasks);
+ for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ unsigned Index = getResourceStateIndex(ProcResourceMasks[I]);
+ ResIdx2ProcResID[Index] = I;
+ }
+}
+
+void SummaryView::onEvent(const HWInstructionEvent &Event) {
+ if (Event.Type == HWInstructionEvent::Dispatched)
+ LastInstructionIdx = Event.IR.getSourceIndex();
+
+ // 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
+ // resource usage information available from the instruction descriptor. We
+ // need to compute the cumulative number of resource cycles for every
+ // processor resource which is consumed by an instruction of the block.
+ const Instruction &Inst = *Event.IR.getInstruction();
+ const InstrDesc &Desc = Inst.getDesc();
+ NumMicroOps += Desc.NumMicroOps;
+ for (const std::pair<uint64_t, ResourceUsage> &RU : Desc.Resources) {
+ if (RU.second.size()) {
+ unsigned ProcResID = ResIdx2ProcResID[getResourceStateIndex(RU.first)];
+ ProcResourceUsage[ProcResID] += RU.second.size();
+ }
+ }
+}
+
+void SummaryView::printView(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ DisplayValues DV;
+
+ collectData(DV);
+ TempStream << "Iterations: " << DV.Iterations;
+ TempStream << "\nInstructions: " << DV.TotalInstructions;
+ TempStream << "\nTotal Cycles: " << DV.TotalCycles;
+ TempStream << "\nTotal uOps: " << DV.TotalUOps << '\n';
+ TempStream << "\nDispatch Width: " << DV.DispatchWidth;
+ TempStream << "\nuOps Per Cycle: "
+ << format("%.2f", floor((DV.UOpsPerCycle * 100) + 0.5) / 100);
+ TempStream << "\nIPC: "
+ << format("%.2f", floor((DV.IPC * 100) + 0.5) / 100);
+ TempStream << "\nBlock RThroughput: "
+ << format("%.1f", floor((DV.BlockRThroughput * 10) + 0.5) / 10)
+ << '\n';
+ TempStream.flush();
+ OS << Buffer;
+}
+
+void SummaryView::collectData(DisplayValues &DV) const {
+ DV.Instructions = Source.size();
+ DV.Iterations = (LastInstructionIdx / DV.Instructions) + 1;
+ DV.TotalInstructions = DV.Instructions * DV.Iterations;
+ DV.TotalCycles = TotalCycles;
+ DV.DispatchWidth = DispatchWidth;
+ DV.TotalUOps = NumMicroOps * DV.Iterations;
+ DV.UOpsPerCycle = (double)DV.TotalUOps / TotalCycles;
+ DV.IPC = (double)DV.TotalInstructions / TotalCycles;
+ DV.BlockRThroughput = computeBlockRThroughput(SM, DispatchWidth, NumMicroOps,
+ ProcResourceUsage);
+}
+
+json::Value SummaryView::toJSON() const {
+ DisplayValues DV;
+ collectData(DV);
+ json::Object JO({{"Iterations", DV.Iterations},
+ {"Instructions", DV.TotalInstructions},
+ {"TotalCycles", DV.TotalCycles},
+ {"TotaluOps", DV.TotalUOps},
+ {"DispatchWidth", DV.DispatchWidth},
+ {"uOpsPerCycle", DV.UOpsPerCycle},
+ {"IPC", DV.IPC},
+ {"BlockRThroughput", DV.BlockRThroughput}});
+ return JO;
+}
+} // namespace mca.
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/SummaryView.h b/contrib/libs/llvm14/tools/llvm-mca/Views/SummaryView.h
new file mode 100644
index 00000000000..21f3fad23ca
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/SummaryView.h
@@ -0,0 +1,90 @@
+//===--------------------- SummaryView.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the summary view.
+///
+/// The goal of the summary view is to give a very quick overview of the
+/// performance throughput. Below is an example of summary view:
+///
+///
+/// Iterations: 300
+/// Instructions: 900
+/// Total Cycles: 610
+/// Dispatch Width: 2
+/// IPC: 1.48
+/// Block RThroughput: 2.0
+///
+/// The summary view collects a few performance numbers. The two main
+/// performance indicators are 'Total Cycles' and IPC (Instructions Per Cycle).
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_SUMMARYVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_SUMMARYVIEW_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/MC/MCSchedule.h"
+#include "llvm/MCA/View.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;
+ 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;
+
+ struct DisplayValues {
+ unsigned Instructions;
+ unsigned Iterations;
+ unsigned TotalInstructions;
+ unsigned TotalCycles;
+ unsigned DispatchWidth;
+ unsigned TotalUOps;
+ double IPC;
+ double UOpsPerCycle;
+ double BlockRThroughput;
+ };
+
+ // For each processor resource, this vector stores the cumulative number of
+ // resource cycles consumed by the analyzed code block.
+ llvm::SmallVector<unsigned, 8> ProcResourceUsage;
+
+ // Each processor resource is associated with a so-called processor resource
+ // mask. This vector allows to correlate processor resource IDs with processor
+ // resource masks. There is exactly one element per each processor resource
+ // declared by the scheduling model.
+ llvm::SmallVector<uint64_t, 8> ProcResourceMasks;
+
+ // Used to map resource indices to actual processor resource IDs.
+ llvm::SmallVector<unsigned, 8> ResIdx2ProcResID;
+
+ /// Compute the data we want to print out in the object DV.
+ void collectData(DisplayValues &DV) const;
+
+public:
+ 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;
+ StringRef getNameAsString() const override { return "SummaryView"; }
+ json::Value toJSON() const override;
+};
+} // namespace mca
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/TimelineView.cpp b/contrib/libs/llvm14/tools/llvm-mca/Views/TimelineView.cpp
new file mode 100644
index 00000000000..5c05edbdea6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/TimelineView.cpp
@@ -0,0 +1,328 @@
+//===--------------------- TimelineView.cpp ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \brief
+///
+/// This file implements the TimelineView interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Views/TimelineView.h"
+#include <numeric>
+
+namespace llvm {
+namespace mca {
+
+TimelineView::TimelineView(const MCSubtargetInfo &sti, MCInstPrinter &Printer,
+ llvm::ArrayRef<llvm::MCInst> S, unsigned Iterations,
+ unsigned Cycles)
+ : InstructionView(sti, Printer, S), CurrentCycle(0),
+ MaxCycle(Cycles == 0 ? std::numeric_limits<unsigned>::max() : Cycles),
+ LastCycle(0), WaitTime(S.size()), UsedBuffer(S.size()) {
+ unsigned NumInstructions = getSource().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() >= getSource().size())
+ return;
+
+ const MCSchedModel &SM = getSubTargetInfo().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 % getSource().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;
+ if (CurrentCycle > TVEntry.CycleExecuted) {
+ 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 {
+ bool PrintingTotals = SourceIndex == getSource().size();
+ unsigned CumulativeExecutions = PrintingTotals ? Timeline.size() : Executions;
+
+ if (!PrintingTotals)
+ OS << SourceIndex << '.';
+
+ OS.PadToColumn(7);
+
+ double AverageTime1, AverageTime2, AverageTime3;
+ AverageTime1 =
+ (double)(Entry.CyclesSpentInSchedulerQueue * 10) / CumulativeExecutions;
+ AverageTime2 =
+ (double)(Entry.CyclesSpentInSQWhileReady * 10) / CumulativeExecutions;
+ AverageTime3 = (double)(Entry.CyclesSpentAfterWBAndBeforeRetire * 10) /
+ CumulativeExecutions;
+
+ OS << Executions;
+ OS.PadToColumn(13);
+
+ int BufferSize = PrintingTotals ? 0 : UsedBuffer[SourceIndex].second;
+ if (!PrintingTotals)
+ tryChangeColor(OS, Entry.CyclesSpentInSchedulerQueue, CumulativeExecutions,
+ BufferSize);
+ OS << format("%.1f", floor(AverageTime1 + 0.5) / 10);
+ OS.PadToColumn(20);
+ if (!PrintingTotals)
+ tryChangeColor(OS, Entry.CyclesSpentInSQWhileReady, CumulativeExecutions,
+ BufferSize);
+ OS << format("%.1f", floor(AverageTime2 + 0.5) / 10);
+ OS.PadToColumn(27);
+ if (!PrintingTotals)
+ tryChangeColor(OS, Entry.CyclesSpentAfterWBAndBeforeRetire,
+ CumulativeExecutions,
+ getSubTargetInfo().getSchedModel().MicroOpBufferSize);
+ OS << format("%.1f", floor(AverageTime3 + 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;
+ formatted_raw_ostream FOS(OS);
+ unsigned Executions = Timeline.size() / getSource().size();
+ unsigned IID = 0;
+ for (const MCInst &Inst : getSource()) {
+ printWaitTimeEntry(FOS, WaitTime[IID], IID, Executions);
+ FOS << " " << printInstructionString(Inst) << '\n';
+ FOS.flush();
+ ++IID;
+ }
+
+ // If the timeline contains more than one instruction,
+ // let's also print global averages.
+ if (getSource().size() != 1) {
+ WaitTimeEntry TotalWaitTime = std::accumulate(
+ WaitTime.begin(), WaitTime.end(), WaitTimeEntry{0, 0, 0},
+ [](const WaitTimeEntry &A, const WaitTimeEntry &B) {
+ return WaitTimeEntry{
+ A.CyclesSpentInSchedulerQueue + B.CyclesSpentInSchedulerQueue,
+ A.CyclesSpentInSQWhileReady + B.CyclesSpentInSQWhileReady,
+ A.CyclesSpentAfterWBAndBeforeRetire +
+ B.CyclesSpentAfterWBAndBeforeRetire};
+ });
+ printWaitTimeEntry(FOS, TotalWaitTime, IID, Executions);
+ FOS << " "
+ << "<total>" << '\n';
+ FOS.flush();
+ }
+}
+
+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;
+ if (Entry.CycleExecuted < Entry.CycleRetired)
+ 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();
+
+ unsigned IID = 0;
+ ArrayRef<llvm::MCInst> Source = getSource();
+ const unsigned Iterations = Timeline.size() / Source.size();
+ for (unsigned Iteration = 0; Iteration < Iterations; ++Iteration) {
+ for (const MCInst &Inst : Source) {
+ const TimelineViewEntry &Entry = Timeline[IID];
+ // When an instruction is retired after timeline-max-cycles,
+ // its CycleRetired is left at 0. However, it's possible for
+ // a 0 latency instruction to be retired during cycle 0 and we
+ // don't want to early exit in that case. The CycleExecuted
+ // attribute is set correctly whether or not it is greater
+ // than timeline-max-cycles so we can use that to ensure
+ // we don't early exit because of a 0 latency instruction.
+ if (Entry.CycleRetired == 0 && Entry.CycleExecuted != 0) {
+ FOS << "Truncated display due to cycle limit\n";
+ return;
+ }
+
+ unsigned SourceIndex = IID % Source.size();
+ printTimelineViewEntry(FOS, Entry, Iteration, SourceIndex);
+ FOS << " " << printInstructionString(Inst) << '\n';
+ FOS.flush();
+
+ ++IID;
+ }
+ }
+}
+
+json::Value TimelineView::toJSON() const {
+ json::Array TimelineInfo;
+
+ for (const TimelineViewEntry &TLE : Timeline) {
+ TimelineInfo.push_back(
+ json::Object({{"CycleDispatched", TLE.CycleDispatched},
+ {"CycleReady", TLE.CycleReady},
+ {"CycleIssued", TLE.CycleIssued},
+ {"CycleExecuted", TLE.CycleExecuted},
+ {"CycleRetired", TLE.CycleRetired}}));
+ }
+ return json::Object({{"TimelineInfo", std::move(TimelineInfo)}});
+}
+} // namespace mca
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-mca/Views/TimelineView.h b/contrib/libs/llvm14/tools/llvm-mca/Views/TimelineView.h
new file mode 100644
index 00000000000..81be8244b77
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/Views/TimelineView.h
@@ -0,0 +1,188 @@
+//===--------------------- TimelineView.h -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \brief
+///
+/// This file implements a timeline view for the llvm-mca tool.
+///
+/// Class TimelineView observes events generated by the pipeline. For every
+/// instruction executed by the pipeline, it stores information related to
+/// state transition. It then plots that information in the form of a table
+/// as reported by the example below:
+///
+/// Timeline view:
+/// 0123456
+/// Index 0123456789
+///
+/// [0,0] DeER . . .. vmovshdup %xmm0, %xmm1
+/// [0,1] DeER . . .. vpermilpd $1, %xmm0, %xmm2
+/// [0,2] .DeER. . .. vpermilps $231, %xmm0, %xmm5
+/// [0,3] .DeeeER . .. vaddss %xmm1, %xmm0, %xmm3
+/// [0,4] . D==eeeER. .. vaddss %xmm3, %xmm2, %xmm4
+/// [0,5] . D=====eeeER .. vaddss %xmm4, %xmm5, %xmm6
+///
+/// [1,0] . DeE------R .. vmovshdup %xmm0, %xmm1
+/// [1,1] . DeE------R .. vpermilpd $1, %xmm0, %xmm2
+/// [1,2] . DeE-----R .. vpermilps $231, %xmm0, %xmm5
+/// [1,3] . D=eeeE--R .. vaddss %xmm1, %xmm0, %xmm3
+/// [1,4] . D===eeeER .. vaddss %xmm3, %xmm2, %xmm4
+/// [1,5] . D======eeeER vaddss %xmm4, %xmm5, %xmm6
+///
+/// There is an entry for every instruction in the input assembly sequence.
+/// The first field is a pair of numbers obtained from the instruction index.
+/// The first element of the pair is the iteration index, while the second
+/// element of the pair is a sequence number (i.e. a position in the assembly
+/// sequence).
+/// The second field of the table is the actual timeline information; each
+/// column is the information related to a specific cycle of execution.
+/// The timeline of an instruction is described by a sequence of character
+/// where each character represents the instruction state at a specific cycle.
+///
+/// Possible instruction states are:
+/// D: Instruction Dispatched
+/// e: Instruction Executing
+/// E: Instruction Executed (write-back stage)
+/// R: Instruction retired
+/// =: Instruction waiting in the Scheduler's queue
+/// -: Instruction executed, waiting to retire in order.
+///
+/// dots ('.') and empty spaces are cycles where the instruction is not
+/// in-flight.
+///
+/// The last column is the assembly instruction associated to the entry.
+///
+/// Based on the timeline view information from the example, instruction 0
+/// at iteration 0 was dispatched at cycle 0, and was retired at cycle 3.
+/// Instruction [0,1] was also dispatched at cycle 0, and it retired at
+/// the same cycle than instruction [0,0].
+/// Instruction [0,4] has been dispatched at cycle 2. However, it had to
+/// wait for two cycles before being issued. That is because operands
+/// became ready only at cycle 5.
+///
+/// This view helps further understanding bottlenecks and the impact of
+/// resource pressure on the code.
+///
+/// To better understand why instructions had to wait for multiple cycles in
+/// the scheduler's queue, class TimelineView also reports extra timing info
+/// in another table named "Average Wait times" (see example below).
+///
+///
+/// Average Wait times (based on the timeline view):
+/// [0]: Executions
+/// [1]: Average time spent waiting in a scheduler's queue
+/// [2]: Average time spent waiting in a scheduler's queue while ready
+/// [3]: Average time elapsed from WB until retire stage
+///
+/// [0] [1] [2] [3]
+/// 0. 2 1.0 1.0 3.0 vmovshdup %xmm0, %xmm1
+/// 1. 2 1.0 1.0 3.0 vpermilpd $1, %xmm0, %xmm2
+/// 2. 2 1.0 1.0 2.5 vpermilps $231, %xmm0, %xmm5
+/// 3. 2 1.5 0.5 1.0 vaddss %xmm1, %xmm0, %xmm3
+/// 4. 2 3.5 0.0 0.0 vaddss %xmm3, %xmm2, %xmm4
+/// 5. 2 6.5 0.0 0.0 vaddss %xmm4, %xmm5, %xmm6
+/// 2 2.4 0.6 1.6 <total>
+///
+/// By comparing column [2] with column [1], we get an idea about how many
+/// cycles were spent in the scheduler's queue due to data dependencies.
+///
+/// In this example, instruction 5 spent an average of ~6 cycles in the
+/// scheduler's queue. As soon as operands became ready, the instruction
+/// was immediately issued to the pipeline(s).
+/// That is expected because instruction 5 cannot transition to the "ready"
+/// state until %xmm4 is written by instruction 4.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_TIMELINEVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_TIMELINEVIEW_H
+
+#include "Views/InstructionView.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/JSON.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace mca {
+
+/// This class listens to instruction state transition events
+/// in order to construct a timeline information.
+///
+/// For every instruction executed by the Pipeline, this class constructs
+/// a TimelineViewEntry object. TimelineViewEntry objects are then used
+/// to print the timeline information, as well as the "average wait times"
+/// for every instruction in the input assembly sequence.
+class TimelineView : public InstructionView {
+ unsigned CurrentCycle;
+ unsigned MaxCycle;
+ unsigned LastCycle;
+
+ struct TimelineViewEntry {
+ int CycleDispatched; // A negative value is an "invalid cycle".
+ unsigned CycleReady;
+ unsigned CycleIssued;
+ unsigned CycleExecuted;
+ unsigned CycleRetired;
+ };
+ std::vector<TimelineViewEntry> Timeline;
+
+ struct WaitTimeEntry {
+ 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,
+ unsigned Executions) const;
+
+ // Display characters for the TimelineView report output.
+ struct DisplayChar {
+ static const char Dispatched = 'D';
+ static const char Executed = 'E';
+ static const char Retired = 'R';
+ static const char Waiting = '='; // Instruction is waiting in the scheduler.
+ static const char Executing = 'e';
+ static const char RetireLag = '-'; // The instruction is waiting to retire.
+ };
+
+public:
+ TimelineView(const llvm::MCSubtargetInfo &sti, llvm::MCInstPrinter &Printer,
+ 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;
+ void printAverageWaitTimes(llvm::raw_ostream &OS) const;
+ void printView(llvm::raw_ostream &OS) const override {
+ printTimeline(OS);
+ printAverageWaitTimes(OS);
+ }
+ StringRef getNameAsString() const override { return "TimelineView"; }
+ json::Value toJSON() const override;
+};
+} // namespace mca
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-mca/llvm-mca.cpp b/contrib/libs/llvm14/tools/llvm-mca/llvm-mca.cpp
new file mode 100644
index 00000000000..1826491f3f3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/llvm-mca.cpp
@@ -0,0 +1,697 @@
+//===-- llvm-mca.cpp - Machine Code Analyzer -------------------*- C++ -* -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This utility is a simple driver that allows static performance analysis on
+// machine code similarly to how IACA (Intel Architecture Code Analyzer) works.
+//
+// llvm-mca [options] <file-name>
+// -march <type>
+// -mcpu <cpu>
+// -o <file>
+//
+// The target defaults to the host target.
+// The cpu defaults to the 'native' host cpu.
+// The output defaults to standard output.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CodeRegion.h"
+#include "CodeRegionGenerator.h"
+#include "PipelinePrinter.h"
+#include "Views/BottleneckAnalysis.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/MCAsmBackend.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/MCA/CodeEmitter.h"
+#include "llvm/MCA/Context.h"
+#include "llvm/MCA/CustomBehaviour.h"
+#include "llvm/MCA/InstrBuilder.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"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+
+using namespace llvm;
+
+static mc::RegisterMCTargetOptionsFlags MOF;
+
+static cl::OptionCategory ToolOptions("Tool Options");
+static cl::OptionCategory ViewOptions("View Options");
+
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input file>"),
+ cl::cat(ToolOptions), cl::init("-"));
+
+static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
+ cl::init("-"), cl::cat(ToolOptions),
+ cl::value_desc("filename"));
+
+static cl::opt<std::string>
+ 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. See -version for available targets"),
+ cl::cat(ToolOptions));
+
+static cl::opt<std::string>
+ MCPU("mcpu",
+ cl::desc("Target a specific cpu type (-mcpu=help for details)"),
+ cl::value_desc("cpu-name"), cl::cat(ToolOptions), cl::init("native"));
+
+static cl::opt<std::string> MATTR("mattr",
+ cl::desc("Additional target features."),
+ cl::cat(ToolOptions));
+
+static cl::opt<bool> PrintJson("json",
+ cl::desc("Print the output in json format"),
+ cl::cat(ToolOptions), cl::init(false));
+
+static cl::opt<int>
+ OutputAsmVariant("output-asm-variant",
+ cl::desc("Syntax variant to use for output printing"),
+ cl::cat(ToolOptions), cl::init(-1));
+
+static cl::opt<bool>
+ PrintImmHex("print-imm-hex", cl::cat(ToolOptions), cl::init(false),
+ cl::desc("Prefer hex format when printing immediate values"));
+
+static cl::opt<unsigned> Iterations("iterations",
+ cl::desc("Number of iterations to run"),
+ cl::cat(ToolOptions), cl::init(0));
+
+static cl::opt<unsigned>
+ DispatchWidth("dispatch", cl::desc("Override the processor dispatch width"),
+ cl::cat(ToolOptions), cl::init(0));
+
+static cl::opt<unsigned>
+ RegisterFileSize("register-file-size",
+ cl::desc("Maximum number of physical registers which can "
+ "be used for register mappings"),
+ cl::cat(ToolOptions), cl::init(0));
+
+static cl::opt<unsigned>
+ MicroOpQueue("micro-op-queue-size", cl::Hidden,
+ cl::desc("Number of entries in the micro-op queue"),
+ cl::cat(ToolOptions), cl::init(0));
+
+static cl::opt<unsigned>
+ DecoderThroughput("decoder-throughput", cl::Hidden,
+ cl::desc("Maximum throughput from the decoders "
+ "(instructions per cycle)"),
+ cl::cat(ToolOptions), cl::init(0));
+
+static cl::opt<bool>
+ PrintRegisterFileStats("register-file-stats",
+ cl::desc("Print register file statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool> PrintDispatchStats("dispatch-stats",
+ cl::desc("Print dispatch statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool>
+ PrintSummaryView("summary-view", cl::Hidden,
+ cl::desc("Print summary view (enabled by default)"),
+ cl::cat(ViewOptions), cl::init(true));
+
+static cl::opt<bool> PrintSchedulerStats("scheduler-stats",
+ cl::desc("Print scheduler statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool>
+ PrintRetireStats("retire-stats",
+ cl::desc("Print retire control unit statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool> PrintResourcePressureView(
+ "resource-pressure",
+ cl::desc("Print the resource pressure view (enabled by default)"),
+ cl::cat(ViewOptions), cl::init(true));
+
+static cl::opt<bool> PrintTimelineView("timeline",
+ cl::desc("Print the timeline view"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<unsigned> TimelineMaxIterations(
+ "timeline-max-iterations",
+ cl::desc("Maximum number of iterations to print in timeline view"),
+ cl::cat(ViewOptions), cl::init(0));
+
+static cl::opt<unsigned>
+ TimelineMaxCycles("timeline-max-cycles",
+ cl::desc("Maximum number of cycles in the timeline view, "
+ "or 0 for unlimited. Defaults to 80 cycles"),
+ cl::cat(ViewOptions), cl::init(80));
+
+static cl::opt<bool>
+ AssumeNoAlias("noalias",
+ 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"),
+ 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",
+ cl::desc("Print instruction tables"),
+ cl::cat(ToolOptions), cl::init(false));
+
+static cl::opt<bool> PrintInstructionInfoView(
+ "instruction-info",
+ cl::desc("Print the instruction info view (enabled by default)"),
+ cl::cat(ViewOptions), cl::init(true));
+
+static cl::opt<bool> EnableAllStats("all-stats",
+ cl::desc("Print all hardware statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool>
+ EnableAllViews("all-views",
+ cl::desc("Print all views including hardware statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool> EnableBottleneckAnalysis(
+ "bottleneck-analysis",
+ cl::desc("Enable bottleneck analysis (disabled by default)"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool> ShowEncoding(
+ "show-encoding",
+ cl::desc("Print encoding information in the instruction info view"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool> ShowBarriers(
+ "show-barriers",
+ cl::desc("Print memory barrier information in the instruction info view"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool> DisableCustomBehaviour(
+ "disable-cb",
+ cl::desc(
+ "Disable custom behaviour (use the default class which does nothing)."),
+ cl::cat(ViewOptions), cl::init(false));
+
+namespace {
+
+const Target *getTarget(const char *ProgName) {
+ if (TripleName.empty())
+ TripleName = Triple::normalize(sys::getDefaultTargetTriple());
+ Triple TheTriple(TripleName);
+
+ // Get the target specific parser.
+ std::string Error;
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget(ArchName, TheTriple, Error);
+ if (!TheTarget) {
+ errs() << ProgName << ": " << Error;
+ return nullptr;
+ }
+
+ // Update TripleName with the updated triple from the target lookup.
+ TripleName = TheTriple.str();
+
+ // Return the found target.
+ return TheTarget;
+}
+
+ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() {
+ if (OutputFilename == "")
+ OutputFilename = "-";
+ std::error_code EC;
+ auto Out = std::make_unique<ToolOutputFile>(OutputFilename, EC,
+ sys::fs::OF_TextWithCRLF);
+ if (!EC)
+ return std::move(Out);
+ return EC;
+}
+} // end of anonymous namespace
+
+static void processOptionImpl(cl::opt<bool> &O, const cl::opt<bool> &Default) {
+ if (!O.getNumOccurrences() || O.getPosition() < Default.getPosition())
+ O = Default.getValue();
+}
+
+static void processViewOptions(bool IsOutOfOrder) {
+ if (!EnableAllViews.getNumOccurrences() &&
+ !EnableAllStats.getNumOccurrences())
+ return;
+
+ if (EnableAllViews.getNumOccurrences()) {
+ processOptionImpl(PrintSummaryView, EnableAllViews);
+ if (IsOutOfOrder)
+ processOptionImpl(EnableBottleneckAnalysis, EnableAllViews);
+ processOptionImpl(PrintResourcePressureView, EnableAllViews);
+ processOptionImpl(PrintTimelineView, EnableAllViews);
+ processOptionImpl(PrintInstructionInfoView, EnableAllViews);
+ }
+
+ const cl::opt<bool> &Default =
+ EnableAllViews.getPosition() < EnableAllStats.getPosition()
+ ? EnableAllStats
+ : EnableAllViews;
+ processOptionImpl(PrintRegisterFileStats, Default);
+ processOptionImpl(PrintDispatchStats, Default);
+ processOptionImpl(PrintSchedulerStats, Default);
+ if (IsOutOfOrder)
+ 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.
+ InitializeAllTargetInfos();
+ InitializeAllTargetMCs();
+ InitializeAllAsmParsers();
+ InitializeAllTargetMCAs();
+
+ // Enable printing of available targets when flag --version is specified.
+ cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
+
+ cl::HideUnrelatedOptions({&ToolOptions, &ViewOptions});
+
+ // Parse flags and initialize target options.
+ cl::ParseCommandLineOptions(argc, argv,
+ "llvm machine code performance analyzer.\n");
+
+ // 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.
+ const char *ProgName = argv[0];
+ const Target *TheTarget = getTarget(ProgName);
+ if (!TheTarget)
+ return 1;
+
+ // GetTarget() may replaced TripleName with a default triple.
+ // For safety, reconstruct the Triple object.
+ Triple TheTriple(TripleName);
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
+ MemoryBuffer::getFileOrSTDIN(InputFilename);
+ if (std::error_code EC = BufferPtr.getError()) {
+ WithColor::error() << InputFilename << ": " << EC.message() << '\n';
+ return 1;
+ }
+
+ if (MCPU == "native")
+ MCPU = std::string(llvm::sys::getHostCPUName());
+
+ std::unique_ptr<MCSubtargetInfo> STI(
+ TheTarget->createMCSubtargetInfo(TripleName, MCPU, MATTR));
+ assert(STI && "Unable to create subtarget info!");
+ if (!STI->isCPUStringValid(MCPU))
+ return 1;
+
+ if (!STI->getSchedModel().hasInstrSchedModel()) {
+ WithColor::error()
+ << "unable to find instruction-level scheduling information for"
+ << " target triple '" << TheTriple.normalize() << "' and cpu '" << MCPU
+ << "'.\n";
+
+ if (STI->getSchedModel().InstrItineraries)
+ WithColor::note()
+ << "cpu '" << MCPU << "' provides itineraries. However, "
+ << "instruction itineraries are currently unsupported.\n";
+ return 1;
+ }
+
+ // Apply overrides to llvm-mca specific options.
+ bool IsOutOfOrder = STI->getSchedModel().isOutOfOrder();
+ processViewOptions(IsOutOfOrder);
+
+ std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
+ assert(MRI && "Unable to create target register info!");
+
+ MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags();
+ std::unique_ptr<MCAsmInfo> MAI(
+ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ assert(MAI && "Unable to create target asm info!");
+
+ SourceMgr SrcMgr;
+
+ // Tell SrcMgr about this buffer, which is what the parser will pick up.
+ SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc());
+
+ MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr);
+ std::unique_ptr<MCObjectFileInfo> MOFI(
+ TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
+ Ctx.setObjectFileInfo(MOFI.get());
+
+ std::unique_ptr<buffer_ostream> BOS;
+
+ std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
+ assert(MCII && "Unable to create instruction info!");
+
+ std::unique_ptr<MCInstrAnalysis> MCIA(
+ TheTarget->createMCInstrAnalysis(MCII.get()));
+
+ // Need to initialize an MCInstPrinter as it is
+ // required for initializing the MCTargetStreamer
+ // which needs to happen within the CRG.parseCodeRegions() call below.
+ // Without an MCTargetStreamer, certain assembly directives can trigger a
+ // segfault. (For example, the .cv_fpo_proc directive on x86 will segfault if
+ // we don't initialize the MCTargetStreamer.)
+ unsigned IPtempOutputAsmVariant =
+ OutputAsmVariant == -1 ? 0 : OutputAsmVariant;
+ std::unique_ptr<MCInstPrinter> IPtemp(TheTarget->createMCInstPrinter(
+ Triple(TripleName), IPtempOutputAsmVariant, *MAI, *MCII, *MRI));
+ if (!IPtemp) {
+ WithColor::error()
+ << "unable to create instruction printer for target triple '"
+ << TheTriple.normalize() << "' with assembly variant "
+ << IPtempOutputAsmVariant << ".\n";
+ return 1;
+ }
+
+ // 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(std::move(IPtemp));
+ 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;
+
+ // Early exit if errors were found by the code region parsing logic.
+ if (!Regions.isValid())
+ return 1;
+
+ if (Regions.empty()) {
+ WithColor::error() << "no assembly instructions found.\n";
+ return 1;
+ }
+
+ // Now initialize the output file.
+ auto OF = getOutputStream();
+ if (std::error_code EC = OF.getError()) {
+ WithColor::error() << EC.message() << '\n';
+ return 1;
+ }
+
+ unsigned AssemblerDialect = CRG.getAssemblerDialect();
+ if (OutputAsmVariant >= 0)
+ AssemblerDialect = static_cast<unsigned>(OutputAsmVariant);
+ std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
+ Triple(TripleName), AssemblerDialect, *MAI, *MCII, *MRI));
+ if (!IP) {
+ WithColor::error()
+ << "unable to create instruction printer for target triple '"
+ << TheTriple.normalize() << "' with assembly variant "
+ << AssemblerDialect << ".\n";
+ return 1;
+ }
+
+ // Set the display preference for hex vs. decimal immediates.
+ IP->setPrintImmHex(PrintImmHex);
+
+ std::unique_ptr<ToolOutputFile> TOF = std::move(*OF);
+
+ const MCSchedModel &SM = STI->getSchedModel();
+
+ // Create an instruction builder.
+ mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get());
+
+ // Create a context to control ownership of the pipeline hardware.
+ mca::Context MCA(*MRI, *STI);
+
+ mca::PipelineOptions PO(MicroOpQueue, DecoderThroughput, DispatchWidth,
+ RegisterFileSize, LoadQueueSize, StoreQueueSize,
+ AssumeNoAlias, EnableBottleneckAnalysis);
+
+ // Number each region in the sequence.
+ unsigned RegionIdx = 0;
+
+ std::unique_ptr<MCCodeEmitter> MCE(
+ TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
+ assert(MCE && "Unable to create code emitter!");
+
+ std::unique_ptr<MCAsmBackend> MAB(TheTarget->createMCAsmBackend(
+ *STI, *MRI, mc::InitMCTargetOptionsFromFlags()));
+ assert(MAB && "Unable to create asm backend!");
+
+ json::Object JSONOutput;
+ for (const std::unique_ptr<mca::CodeRegion> &Region : Regions) {
+ // Skip empty code regions.
+ if (Region->empty())
+ continue;
+
+ IB.clear();
+
+ // Lower the MCInst sequence into an mca::Instruction sequence.
+ ArrayRef<MCInst> Insts = Region->getInstructions();
+ mca::CodeEmitter CE(*STI, *MAB, *MCE, Insts);
+
+ std::unique_ptr<mca::InstrPostProcess> IPP;
+ if (!DisableCustomBehaviour) {
+ IPP = std::unique_ptr<mca::InstrPostProcess>(
+ TheTarget->createInstrPostProcess(*STI, *MCII));
+ }
+ if (!IPP)
+ // If the target doesn't have its own IPP implemented (or the
+ // -disable-cb flag is set) then we use the base class
+ // (which does nothing).
+ IPP = std::make_unique<mca::InstrPostProcess>(*STI, *MCII);
+
+ SmallVector<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, 0, "", *STI, SS);
+ SS.flush();
+ WithColor::note()
+ << "instruction: " << InstructionStr << '\n';
+ })) {
+ // Default case.
+ WithColor::error() << toString(std::move(NewE));
+ }
+ return 1;
+ }
+
+ IPP->postProcessInstruction(Inst.get(), MCI);
+
+ 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 = std::make_unique<mca::Pipeline>();
+ P->appendStage(std::make_unique<mca::EntryStage>(S));
+ P->appendStage(std::make_unique<mca::InstructionTables>(SM));
+
+ mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO);
+ if (PrintJson) {
+ Printer.addView(
+ std::make_unique<mca::InstructionView>(*STI, *IP, Insts));
+ }
+
+ // Create the views for this pipeline, execute, and emit a report.
+ if (PrintInstructionInfoView) {
+ Printer.addView(std::make_unique<mca::InstructionInfoView>(
+ *STI, *MCII, CE, ShowEncoding, Insts, *IP, LoweredSequence,
+ ShowBarriers));
+ }
+ Printer.addView(
+ std::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts));
+
+ if (!runPipeline(*P))
+ return 1;
+
+ if (PrintJson) {
+ Printer.printReport(JSONOutput);
+ } else {
+ Printer.printReport(TOF->os());
+ }
+
+ ++RegionIdx;
+ continue;
+ }
+
+ // Create the CustomBehaviour object for enforcing Target Specific
+ // behaviours and dependencies that aren't expressed well enough
+ // in the tablegen. CB cannot depend on the list of MCInst or
+ // the source code (but it can depend on the list of
+ // mca::Instruction or any objects that can be reconstructed
+ // from the target information).
+ std::unique_ptr<mca::CustomBehaviour> CB;
+ if (!DisableCustomBehaviour)
+ CB = std::unique_ptr<mca::CustomBehaviour>(
+ TheTarget->createCustomBehaviour(*STI, S, *MCII));
+ if (!CB)
+ // If the target doesn't have its own CB implemented (or the -disable-cb
+ // flag is set) then we use the base class (which does nothing).
+ CB = std::make_unique<mca::CustomBehaviour>(*STI, S, *MCII);
+
+ // Create a basic pipeline simulating an out-of-order backend.
+ auto P = MCA.createDefaultPipeline(PO, S, *CB);
+
+ mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO);
+
+ // Targets can define their own custom Views that exist within their
+ // /lib/Target/ directory so that the View can utilize their CustomBehaviour
+ // or other backend symbols / functionality that are not already exposed
+ // through one of the MC-layer classes. These Views will be initialized
+ // using the CustomBehaviour::getViews() variants.
+ // If a target makes a custom View that does not depend on their target
+ // CB or their backend, they should put the View within
+ // /tools/llvm-mca/Views/ instead.
+ if (!DisableCustomBehaviour) {
+ std::vector<std::unique_ptr<mca::View>> CBViews =
+ CB->getStartViews(*IP, Insts);
+ for (auto &CBView : CBViews)
+ Printer.addView(std::move(CBView));
+ }
+
+ // When we output JSON, we add a view that contains the instructions
+ // and CPU resource information.
+ if (PrintJson) {
+ auto IV = std::make_unique<mca::InstructionView>(*STI, *IP, Insts);
+ Printer.addView(std::move(IV));
+ }
+
+ if (PrintSummaryView)
+ Printer.addView(
+ std::make_unique<mca::SummaryView>(SM, Insts, DispatchWidth));
+
+ if (EnableBottleneckAnalysis) {
+ if (!IsOutOfOrder) {
+ WithColor::warning()
+ << "bottleneck analysis is not supported for in-order CPU '" << MCPU
+ << "'.\n";
+ }
+ Printer.addView(std::make_unique<mca::BottleneckAnalysis>(
+ *STI, *IP, Insts, S.getNumIterations()));
+ }
+
+ if (PrintInstructionInfoView)
+ Printer.addView(std::make_unique<mca::InstructionInfoView>(
+ *STI, *MCII, CE, ShowEncoding, Insts, *IP, LoweredSequence,
+ ShowBarriers));
+
+ // Fetch custom Views that are to be placed after the InstructionInfoView.
+ // Refer to the comment paired with the CB->getStartViews(*IP, Insts); line
+ // for more info.
+ if (!DisableCustomBehaviour) {
+ std::vector<std::unique_ptr<mca::View>> CBViews =
+ CB->getPostInstrInfoViews(*IP, Insts);
+ for (auto &CBView : CBViews)
+ Printer.addView(std::move(CBView));
+ }
+
+ if (PrintDispatchStats)
+ Printer.addView(std::make_unique<mca::DispatchStatistics>());
+
+ if (PrintSchedulerStats)
+ Printer.addView(std::make_unique<mca::SchedulerStatistics>(*STI));
+
+ if (PrintRetireStats)
+ Printer.addView(std::make_unique<mca::RetireControlUnitStatistics>(SM));
+
+ if (PrintRegisterFileStats)
+ Printer.addView(std::make_unique<mca::RegisterFileStatistics>(*STI));
+
+ if (PrintResourcePressureView)
+ Printer.addView(
+ std::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts));
+
+ if (PrintTimelineView) {
+ unsigned TimelineIterations =
+ TimelineMaxIterations ? TimelineMaxIterations : 10;
+ Printer.addView(std::make_unique<mca::TimelineView>(
+ *STI, *IP, Insts, std::min(TimelineIterations, S.getNumIterations()),
+ TimelineMaxCycles));
+ }
+
+ // Fetch custom Views that are to be placed after all other Views.
+ // Refer to the comment paired with the CB->getStartViews(*IP, Insts); line
+ // for more info.
+ if (!DisableCustomBehaviour) {
+ std::vector<std::unique_ptr<mca::View>> CBViews =
+ CB->getEndViews(*IP, Insts);
+ for (auto &CBView : CBViews)
+ Printer.addView(std::move(CBView));
+ }
+
+ if (!runPipeline(*P))
+ return 1;
+
+ if (PrintJson) {
+ Printer.printReport(JSONOutput);
+ } else {
+ Printer.printReport(TOF->os());
+ }
+
+ ++RegionIdx;
+ }
+
+ if (PrintJson)
+ TOF->os() << formatv("{0:2}", json::Value(std::move(JSONOutput))) << "\n";
+
+ TOF->keep();
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-mca/ya.make b/contrib/libs/llvm14/tools/llvm-mca/ya.make
new file mode 100644
index 00000000000..e0192a934a6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mca/ya.make
@@ -0,0 +1,76 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/MCA
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/MCA
+ contrib/libs/llvm14/tools/llvm-mca
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCDIR(contrib/libs/llvm14)
+
+SRCS(
+ lib/Target/X86/MCA/X86CustomBehaviour.cpp
+ tools/llvm-mca/CodeRegion.cpp
+ tools/llvm-mca/CodeRegionGenerator.cpp
+ tools/llvm-mca/PipelinePrinter.cpp
+ tools/llvm-mca/Views/BottleneckAnalysis.cpp
+ tools/llvm-mca/Views/DispatchStatistics.cpp
+ tools/llvm-mca/Views/InstructionInfoView.cpp
+ tools/llvm-mca/Views/InstructionView.cpp
+ tools/llvm-mca/Views/RegisterFileStatistics.cpp
+ tools/llvm-mca/Views/ResourcePressureView.cpp
+ tools/llvm-mca/Views/RetireControlUnitStatistics.cpp
+ tools/llvm-mca/Views/SchedulerStatistics.cpp
+ tools/llvm-mca/Views/SummaryView.cpp
+ tools/llvm-mca/Views/TimelineView.cpp
+ tools/llvm-mca/llvm-mca.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-ml/Disassembler.cpp b/contrib/libs/llvm14/tools/llvm-ml/Disassembler.cpp
new file mode 100644
index 00000000000..6a96c881842
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-ml/Disassembler.cpp
@@ -0,0 +1,203 @@
+//===- Disassembler.cpp - Disassembler for hex strings --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements the disassembler of strings of bytes written in
+// hexadecimal, from standard input or from a file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Disassembler.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+typedef std::pair<std::vector<unsigned char>, std::vector<const char *>>
+ ByteArrayTy;
+
+static bool PrintInsts(const MCDisassembler &DisAsm, const ByteArrayTy &Bytes,
+ SourceMgr &SM, raw_ostream &Out, MCStreamer &Streamer,
+ bool InAtomicBlock, const MCSubtargetInfo &STI) {
+ ArrayRef<uint8_t> Data(Bytes.first.data(), Bytes.first.size());
+
+ // Disassemble it to strings.
+ uint64_t Size;
+ uint64_t Index;
+
+ for (Index = 0; Index < Bytes.first.size(); Index += Size) {
+ MCInst Inst;
+
+ MCDisassembler::DecodeStatus S;
+ S = DisAsm.getInstruction(Inst, Size, Data.slice(Index), Index, nulls());
+ switch (S) {
+ case MCDisassembler::Fail:
+ SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]),
+ SourceMgr::DK_Warning, "invalid instruction encoding");
+ // Don't try to resynchronise the stream in a block
+ if (InAtomicBlock)
+ return true;
+
+ if (Size == 0)
+ Size = 1; // skip illegible bytes
+
+ break;
+
+ case MCDisassembler::SoftFail:
+ SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]),
+ SourceMgr::DK_Warning,
+ "potentially undefined instruction encoding");
+ LLVM_FALLTHROUGH;
+
+ case MCDisassembler::Success:
+ Streamer.emitInstruction(Inst, STI);
+ break;
+ }
+ }
+
+ return false;
+}
+
+static bool SkipToToken(StringRef &Str) {
+ for (;;) {
+ if (Str.empty())
+ return false;
+
+ // Strip horizontal whitespace and commas.
+ if (size_t Pos = Str.find_first_not_of(" \t\r\n,")) {
+ Str = Str.substr(Pos);
+ continue;
+ }
+
+ // If this is the start of a comment, remove the rest of the line.
+ if (Str[0] == '#') {
+ Str = Str.substr(Str.find_first_of('\n'));
+ continue;
+ }
+ return true;
+ }
+}
+
+static bool ByteArrayFromString(ByteArrayTy &ByteArray, StringRef &Str,
+ SourceMgr &SM) {
+ while (SkipToToken(Str)) {
+ // Handled by higher level
+ if (Str[0] == '[' || Str[0] == ']')
+ return false;
+
+ // Get the current token.
+ size_t Next = Str.find_first_of(" \t\n\r,#[]");
+ StringRef Value = Str.substr(0, Next);
+
+ // Convert to a byte and add to the byte vector.
+ unsigned ByteVal;
+ if (Value.getAsInteger(0, ByteVal) || ByteVal > 255) {
+ // If we have an error, print it and skip to the end of line.
+ SM.PrintMessage(SMLoc::getFromPointer(Value.data()), SourceMgr::DK_Error,
+ "invalid input token");
+ Str = Str.substr(Str.find('\n'));
+ ByteArray.first.clear();
+ ByteArray.second.clear();
+ continue;
+ }
+
+ ByteArray.first.push_back(ByteVal);
+ ByteArray.second.push_back(Value.data());
+ Str = Str.substr(Next);
+ }
+
+ return false;
+}
+
+int Disassembler::disassemble(const Target &T, const std::string &TripleName,
+ MCSubtargetInfo &STI, MCStreamer &Streamer,
+ MemoryBuffer &Buffer, SourceMgr &SM,
+ raw_ostream &Out) {
+ std::unique_ptr<const MCRegisterInfo> MRI(T.createMCRegInfo(TripleName));
+ if (!MRI) {
+ errs() << "error: no register info for target " << TripleName << "\n";
+ return -1;
+ }
+
+ MCTargetOptions MCOptions;
+ std::unique_ptr<const MCAsmInfo> MAI(
+ T.createMCAsmInfo(*MRI, TripleName, MCOptions));
+ if (!MAI) {
+ errs() << "error: no assembly info for target " << TripleName << "\n";
+ return -1;
+ }
+
+ // Set up the MCContext for creating symbols and MCExpr's.
+ MCContext Ctx(Triple(TripleName), MAI.get(), MRI.get(), &STI);
+
+ std::unique_ptr<const MCDisassembler> DisAsm(
+ T.createMCDisassembler(STI, Ctx));
+ if (!DisAsm) {
+ errs() << "error: no disassembler for target " << TripleName << "\n";
+ return -1;
+ }
+
+ // Set up initial section manually here
+ Streamer.initSections(false, STI);
+
+ bool ErrorOccurred = false;
+
+ // Convert the input to a vector for disassembly.
+ ByteArrayTy ByteArray;
+ StringRef Str = Buffer.getBuffer();
+ bool InAtomicBlock = false;
+
+ while (SkipToToken(Str)) {
+ ByteArray.first.clear();
+ ByteArray.second.clear();
+
+ if (Str[0] == '[') {
+ if (InAtomicBlock) {
+ SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error,
+ "nested atomic blocks make no sense");
+ ErrorOccurred = true;
+ }
+ InAtomicBlock = true;
+ Str = Str.drop_front();
+ continue;
+ } else if (Str[0] == ']') {
+ if (!InAtomicBlock) {
+ SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error,
+ "attempt to close atomic block without opening");
+ ErrorOccurred = true;
+ }
+ InAtomicBlock = false;
+ Str = Str.drop_front();
+ continue;
+ }
+
+ // It's a real token, get the bytes and emit them
+ ErrorOccurred |= ByteArrayFromString(ByteArray, Str, SM);
+
+ if (!ByteArray.first.empty())
+ ErrorOccurred |=
+ PrintInsts(*DisAsm, ByteArray, SM, Out, Streamer, InAtomicBlock, STI);
+ }
+
+ if (InAtomicBlock) {
+ SM.PrintMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error,
+ "unclosed atomic block");
+ ErrorOccurred = true;
+ }
+
+ return ErrorOccurred;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-ml/Disassembler.h b/contrib/libs/llvm14/tools/llvm-ml/Disassembler.h
new file mode 100644
index 00000000000..d3565089e2b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-ml/Disassembler.h
@@ -0,0 +1,37 @@
+//===- Disassembler.h - Text File Disassembler ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements the disassembler of strings of bytes written in
+// hexadecimal, from standard input or from a file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MC_DISASSEMBLER_H
+#define LLVM_TOOLS_LLVM_MC_DISASSEMBLER_H
+
+#include <string>
+
+namespace llvm {
+
+class MemoryBuffer;
+class Target;
+class raw_ostream;
+class SourceMgr;
+class MCSubtargetInfo;
+class MCStreamer;
+
+class Disassembler {
+public:
+ static int disassemble(const Target &T, const std::string &Triple,
+ MCSubtargetInfo &STI, MCStreamer &Streamer,
+ MemoryBuffer &Buffer, SourceMgr &SM, raw_ostream &Out);
+};
+
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-ml/Opts.td b/contrib/libs/llvm14/tools/llvm-ml/Opts.td
new file mode 100644
index 00000000000..631c8566e2a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-ml/Opts.td
@@ -0,0 +1,120 @@
+include "llvm/Option/OptParser.td"
+
+// For LLVM-specific options, we prefer a two-dash prefix, but accept one for
+// compatibility with llvm-mc. For clear separation from ML.EXE compatible
+// options, slash is not accepted.
+class LLVMFlag<string name> : Flag<["--", "-"], name>;
+class LLVMJoined<string name> : Joined<["--", "-"], name>;
+class LLVMJoinedOrSeparate<string name> : JoinedOrSeparate<["--", "-"], name>;
+class LLVMSeparate<string name> : Separate<["--", "-"], name>;
+
+def ml_Group : OptionGroup<"<ml options>">,
+ HelpText<"ML.EXE COMPATIBILITY OPTIONS">;
+class MLFlag<string name> : Flag<["/", "-"], name>, Group<ml_Group>;
+class MLJoined<string name> : Joined<["/", "-"], name>, Group<ml_Group>;
+class MLJoinedOrSeparate<string name> : JoinedOrSeparate<["/", "-"], name>,
+ Group<ml_Group>;
+class MLSeparate<string name> : Separate<["/", "-"], name>, Group<ml_Group>;
+
+def unsupported_Group : OptionGroup<"unsupported">, Flags<[HelpHidden]>,
+ HelpText<"UNSUPPORTED ML.EXE COMPATIBILITY OPTIONS">;
+class UnsupportedFlag<string name> : Flag<["/", "-"], name>,
+ Group<unsupported_Group>;
+class UnsupportedJoined<string name> : Joined<["/", "-"], name>,
+ Group<unsupported_Group>;
+class UnsupportedJoinedOrSeparate<string name> :
+ JoinedOrSeparate<["/", "-"], name>, Group<unsupported_Group>;
+class UnsupportedSeparate<string name> : Separate<["/", "-"], name>,
+ Group<unsupported_Group>;
+
+def bitness : LLVMJoined<"m">, Values<"32,64">,
+ HelpText<"Target platform (x86 or x86-64)">;
+def as_lex : LLVMFlag<"as-lex">,
+ HelpText<"Lex tokens from a .asm file without assembling">;
+def fatal_warnings : LLVMFlag<"fatal-warnings">,
+ HelpText<"Treat warnings as errors">;
+def filetype : LLVMJoined<"filetype=">, Values<"obj,s,null">,
+ HelpText<"Emit a file with the given type">;
+def output_att_asm : LLVMFlag<"output-att-asm">,
+ HelpText<"Use ATT syntax for output assembly">;
+def show_encoding : LLVMFlag<"show-encoding">,
+ HelpText<"Show instruction encodings in output assembly">;
+def show_inst : LLVMFlag<"show-inst">,
+ HelpText<"Show internal instruction representation in output "
+ "assembly">;
+def show_inst_operands : LLVMFlag<"show-inst-operands">,
+ HelpText<"Show instructions operands as parsed">;
+def print_imm_hex : LLVMFlag<"print-imm-hex">,
+ HelpText<"Prefer hex format for immediate values in output "
+ "assembly">;
+def preserve_comments : LLVMFlag<"preserve-comments">,
+ HelpText<"Preserve comments in output assembly">;
+def save_temp_labels : LLVMFlag<"save-temp-labels">,
+ HelpText<"Don't discard temporary labels">;
+def timestamp : LLVMJoined<"timestamp=">,
+ HelpText<"Specify the assembly timestamp (used for @Date and "
+ "@Time built-ins)">;
+def utc : LLVMFlag<"utc">,
+ HelpText<"Render @Date and @Time built-ins in GMT/UTC">;
+def gmtime : LLVMFlag<"gmtime">, Alias<utc>;
+
+def help : MLFlag<"?">,
+ HelpText<"Display available options">;
+def help_long : MLFlag<"help">, Alias<help>;
+def assemble_only : MLFlag<"c">, HelpText<"Assemble only; do not link">;
+def define : MLJoinedOrSeparate<"D">, MetaVarName<"<macro>=<value>">,
+ HelpText<"Define <macro> to <value> (or blank if <value> "
+ "omitted)">;
+def output_file : MLJoinedOrSeparate<"Fo">, HelpText<"Names the output file">;
+def include_path : MLJoinedOrSeparate<"I">,
+ HelpText<"Sets path for include files">;
+def safeseh : MLFlag<"safeseh">,
+ HelpText<"Mark resulting object files as either containing no "
+ "exception handlers or containing exception handlers "
+ "that are all declared with .SAFESEH. Only available in "
+ "32-bit.">;
+def assembly_file : MLJoinedOrSeparate<"Ta">,
+ HelpText<"Assemble source file with name not ending with "
+ "the .asm extension">;
+def error_on_warning : MLFlag<"WX">, Alias<fatal_warnings>;
+def parse_only : MLFlag<"Zs">, HelpText<"Run a syntax-check only">,
+ Alias<filetype>, AliasArgs<["null"]>;
+def ignore_include_envvar : MLFlag<"X">,
+ HelpText<"Ignore the INCLUDE environment variable">;
+
+def tiny_model_support : UnsupportedFlag<"AT">, HelpText<"">;
+def alternate_linker : UnsupportedJoined<"Bl">, HelpText<"">;
+def coff_object_file : UnsupportedFlag<"coff">, HelpText<"">;
+def preserve_identifier_case : UnsupportedFlag<"Cp">, HelpText<"">;
+def uppercase_identifiers : UnsupportedFlag<"Cu">, HelpText<"">;
+def preserve_extern_case : UnsupportedFlag<"Cx">, HelpText<"">;
+def output_preprocessed : UnsupportedFlag<"EP">, HelpText<"">;
+def errorreport : UnsupportedJoined<"ERRORREPORT">, HelpText<"">;
+def stacksize : UnsupportedSeparate<"F">, HelpText<"">;
+def executable_file : UnsupportedSeparate<"Fe">, HelpText<"">;
+def code_listing_file : UnsupportedJoined<"FI">, HelpText<"">;
+def linker_map_file : UnsupportedJoined<"Fm">, HelpText<"">;
+def fp_emulator_fixups : UnsupportedFlag<"FPi">, HelpText<"">;
+def source_browser_file : UnsupportedJoined<"Fr">, HelpText<"">;
+def extended_source_browser_file : UnsupportedJoined<"FR">, HelpText<"">;
+def pascal_conventions : UnsupportedFlag<"Gc">, HelpText<"">;
+def c_conventions : UnsupportedFlag<"Gd">, HelpText<"">;
+def stdcall_conventions : UnsupportedFlag<"GZ">, HelpText<"">;
+def extern_name_limit : UnsupportedSeparate<"H">, HelpText<"">;
+def nologo : UnsupportedFlag<"nologo">, HelpText<"">;
+def omf_object_file : UnsupportedFlag<"omf">, HelpText<"">;
+def full_listing : UnsupportedFlag<"Sa">, HelpText<"">;
+def first_pass_listing : UnsupportedFlag<"Sf">, HelpText<"">;
+def listing_width : UnsupportedSeparate<"SI">, HelpText<"">;
+def listing_without_symbols : UnsupportedFlag<"Sn">, HelpText<"">;
+def listing_page_length : UnsupportedSeparate<"Sp">, HelpText<"">;
+def listing_subtitle : UnsupportedSeparate<"Ss">, HelpText<"">;
+def listing_title : UnsupportedSeparate<"St">, HelpText<"">;
+def listing_false_conditionals : UnsupportedFlag<"Sx">, HelpText<"">;
+def extra_warnings : UnsupportedFlag<"w">, HelpText<"">;
+def warning_level : UnsupportedJoined<"W">, HelpText<"">;
+def line_number_info : UnsupportedFlag<"Zd">, HelpText<"">;
+def export_all_symbols : UnsupportedFlag<"Zf">, HelpText<"">;
+def codeview_info : UnsupportedFlag<"Zi">, HelpText<"">;
+def enable_m510_option : UnsupportedFlag<"Zm">, HelpText<"">;
+def structure_packing : UnsupportedJoined<"Zp">, HelpText<"">;
diff --git a/contrib/libs/llvm14/tools/llvm-ml/llvm-ml.cpp b/contrib/libs/llvm14/tools/llvm-ml/llvm-ml.cpp
new file mode 100644
index 00000000000..5fc289408fc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-ml/llvm-ml.cpp
@@ -0,0 +1,441 @@
+//===-- llvm-ml.cpp - masm-compatible assembler -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A simple driver around MasmParser; based on llvm-mc.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/MC/MCAsmBackend.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
+#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"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Compression.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+#include <ctime>
+
+using namespace llvm;
+using namespace llvm::opt;
+
+namespace {
+
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class MLOptTable : public opt::OptTable {
+public:
+ MLOptTable() : OptTable(InfoTable, /*IgnoreCase=*/false) {}
+};
+} // namespace
+
+static Triple GetTriple(StringRef ProgName, opt::InputArgList &Args) {
+ // Figure out the target triple.
+ StringRef DefaultBitness = "32";
+ SmallString<255> Program = ProgName;
+ sys::path::replace_extension(Program, "");
+ if (Program.endswith("ml64"))
+ DefaultBitness = "64";
+
+ StringRef TripleName =
+ StringSwitch<StringRef>(Args.getLastArgValue(OPT_bitness, DefaultBitness))
+ .Case("32", "i386-pc-windows")
+ .Case("64", "x86_64-pc-windows")
+ .Default("");
+ return Triple(Triple::normalize(TripleName));
+}
+
+static std::unique_ptr<ToolOutputFile> GetOutputStream(StringRef Path) {
+ std::error_code EC;
+ auto Out = std::make_unique<ToolOutputFile>(Path, EC, sys::fs::OF_None);
+ if (EC) {
+ WithColor::error() << EC.message() << '\n';
+ return nullptr;
+ }
+
+ return Out;
+}
+
+static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI, raw_ostream &OS) {
+ AsmLexer Lexer(MAI);
+ Lexer.setBuffer(SrcMgr.getMemoryBuffer(SrcMgr.getMainFileID())->getBuffer());
+ Lexer.setLexMasmIntegers(true);
+ Lexer.useMasmDefaultRadix(true);
+ Lexer.setLexMasmHexFloats(true);
+ Lexer.setLexMasmStrings(true);
+
+ bool Error = false;
+ while (Lexer.Lex().isNot(AsmToken::Eof)) {
+ Lexer.getTok().dump(OS);
+ OS << "\n";
+ if (Lexer.getTok().getKind() == AsmToken::Error)
+ Error = true;
+ }
+
+ return Error;
+}
+
+static int AssembleInput(StringRef ProgName, const Target *TheTarget,
+ SourceMgr &SrcMgr, MCContext &Ctx, MCStreamer &Str,
+ MCAsmInfo &MAI, MCSubtargetInfo &STI,
+ MCInstrInfo &MCII, MCTargetOptions &MCOptions,
+ const opt::ArgList &InputArgs) {
+ struct tm TM;
+ time_t Timestamp;
+ if (InputArgs.hasArg(OPT_timestamp)) {
+ StringRef TimestampStr = InputArgs.getLastArgValue(OPT_timestamp);
+ int64_t IntTimestamp;
+ if (TimestampStr.getAsInteger(10, IntTimestamp)) {
+ WithColor::error(errs(), ProgName)
+ << "invalid timestamp '" << TimestampStr
+ << "'; must be expressed in seconds since the UNIX epoch.\n";
+ return 1;
+ }
+ Timestamp = IntTimestamp;
+ } else {
+ Timestamp = time(nullptr);
+ }
+ if (InputArgs.hasArg(OPT_utc)) {
+ // Not thread-safe.
+ TM = *gmtime(&Timestamp);
+ } else {
+ // Not thread-safe.
+ TM = *localtime(&Timestamp);
+ }
+
+ std::unique_ptr<MCAsmParser> Parser(
+ createMCMasmParser(SrcMgr, Ctx, Str, MAI, TM, 0));
+ std::unique_ptr<MCTargetAsmParser> TAP(
+ TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions));
+
+ if (!TAP) {
+ WithColor::error(errs(), ProgName)
+ << "this target does not support assembly parsing.\n";
+ return 1;
+ }
+
+ Parser->setShowParsedOperands(InputArgs.hasArg(OPT_show_inst_operands));
+ Parser->setTargetParser(*TAP);
+ Parser->getLexer().setLexMasmIntegers(true);
+ Parser->getLexer().useMasmDefaultRadix(true);
+ Parser->getLexer().setLexMasmHexFloats(true);
+ Parser->getLexer().setLexMasmStrings(true);
+
+ auto Defines = InputArgs.getAllArgValues(OPT_define);
+ for (StringRef Define : Defines) {
+ const auto NameValue = Define.split('=');
+ StringRef Name = NameValue.first, Value = NameValue.second;
+ if (Parser->defineMacro(Name, Value)) {
+ WithColor::error(errs(), ProgName)
+ << "can't define macro '" << Name << "' = '" << Value << "'\n";
+ return 1;
+ }
+ }
+
+ int Res = Parser->Run(/*NoInitialTextSection=*/true);
+
+ return Res;
+}
+
+int main(int Argc, char **Argv) {
+ InitLLVM X(Argc, Argv);
+ StringRef ProgName = sys::path::filename(Argv[0]);
+
+ // Initialize targets and assembly printers/parsers.
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmParsers();
+ llvm::InitializeAllDisassemblers();
+
+ MLOptTable T;
+ unsigned MissingArgIndex, MissingArgCount;
+ ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1);
+ opt::InputArgList InputArgs =
+ T.ParseArgs(ArgsArr, MissingArgIndex, MissingArgCount);
+
+ std::string InputFilename;
+ for (auto *Arg : InputArgs.filtered(OPT_INPUT)) {
+ std::string ArgString = Arg->getAsString(InputArgs);
+ if (ArgString == "-" || StringRef(ArgString).endswith(".asm")) {
+ if (!InputFilename.empty()) {
+ WithColor::warning(errs(), ProgName)
+ << "does not support multiple assembly files in one command; "
+ << "ignoring '" << InputFilename << "'\n";
+ }
+ InputFilename = ArgString;
+ } else {
+ std::string Diag;
+ raw_string_ostream OS(Diag);
+ OS << "invalid option '" << ArgString << "'";
+
+ std::string Nearest;
+ if (T.findNearest(ArgString, Nearest) < 2)
+ OS << ", did you mean '" << Nearest << "'?";
+
+ WithColor::error(errs(), ProgName) << OS.str() << '\n';
+ exit(1);
+ }
+ }
+ for (auto *Arg : InputArgs.filtered(OPT_assembly_file)) {
+ if (!InputFilename.empty()) {
+ WithColor::warning(errs(), ProgName)
+ << "does not support multiple assembly files in one command; "
+ << "ignoring '" << InputFilename << "'\n";
+ }
+ InputFilename = Arg->getAsString(InputArgs);
+ }
+
+ for (auto *Arg : InputArgs.filtered(OPT_unsupported_Group)) {
+ WithColor::warning(errs(), ProgName)
+ << "ignoring unsupported '" << Arg->getOption().getName()
+ << "' option\n";
+ }
+
+ if (InputArgs.hasArg(OPT_help)) {
+ std::string Usage = llvm::formatv("{0} [ /options ] file", ProgName).str();
+ T.printHelp(outs(), Usage.c_str(), "LLVM MASM Assembler",
+ /*ShowHidden=*/false);
+ return 0;
+ } else if (InputFilename.empty()) {
+ outs() << "USAGE: " << ProgName << " [ /options ] file\n"
+ << "Run \"" << ProgName << " /?\" or \"" << ProgName
+ << " /help\" for more info.\n";
+ return 0;
+ }
+
+ MCTargetOptions MCOptions;
+ MCOptions.AssemblyLanguage = "masm";
+ MCOptions.MCFatalWarnings = InputArgs.hasArg(OPT_fatal_warnings);
+
+ Triple TheTriple = GetTriple(ProgName, InputArgs);
+ std::string Error;
+ const Target *TheTarget = TargetRegistry::lookupTarget("", TheTriple, Error);
+ if (!TheTarget) {
+ WithColor::error(errs(), ProgName) << Error;
+ return 1;
+ }
+ const std::string &TripleName = TheTriple.getTriple();
+
+ bool SafeSEH = InputArgs.hasArg(OPT_safeseh);
+ if (SafeSEH && !(TheTriple.isArch32Bit() && TheTriple.isX86())) {
+ WithColor::warning()
+ << "/safeseh applies only to 32-bit X86 platforms; ignoring.\n";
+ SafeSEH = false;
+ }
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
+ MemoryBuffer::getFileOrSTDIN(InputFilename);
+ if (std::error_code EC = BufferPtr.getError()) {
+ WithColor::error(errs(), ProgName)
+ << InputFilename << ": " << EC.message() << '\n';
+ return 1;
+ }
+
+ SourceMgr SrcMgr;
+
+ // Tell SrcMgr about this buffer, which is what the parser will pick up.
+ SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc());
+
+ // Record the location of the include directories so that the lexer can find
+ // included files later.
+ std::vector<std::string> IncludeDirs =
+ InputArgs.getAllArgValues(OPT_include_path);
+ if (!InputArgs.hasArg(OPT_ignore_include_envvar)) {
+ if (llvm::Optional<std::string> IncludeEnvVar =
+ llvm::sys::Process::GetEnv("INCLUDE")) {
+ SmallVector<StringRef, 8> Dirs;
+ StringRef(*IncludeEnvVar)
+ .split(Dirs, ";", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+ IncludeDirs.reserve(IncludeDirs.size() + Dirs.size());
+ for (StringRef Dir : Dirs)
+ IncludeDirs.push_back(Dir.str());
+ }
+ }
+ SrcMgr.setIncludeDirs(IncludeDirs);
+
+ std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
+ assert(MRI && "Unable to create target register info!");
+
+ std::unique_ptr<MCAsmInfo> MAI(
+ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ assert(MAI && "Unable to create target asm info!");
+
+ MAI->setPreserveAsmComments(InputArgs.hasArg(OPT_preserve_comments));
+
+ std::unique_ptr<MCSubtargetInfo> STI(TheTarget->createMCSubtargetInfo(
+ TripleName, /*CPU=*/"", /*Features=*/""));
+ assert(STI && "Unable to create subtarget info!");
+
+ // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
+ // MCObjectFileInfo needs a MCContext reference in order to initialize itself.
+ MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr);
+ std::unique_ptr<MCObjectFileInfo> MOFI(TheTarget->createMCObjectFileInfo(
+ Ctx, /*PIC=*/false, /*LargeCodeModel=*/true));
+ Ctx.setObjectFileInfo(MOFI.get());
+
+ if (InputArgs.hasArg(OPT_save_temp_labels))
+ Ctx.setAllowTemporaryLabels(false);
+
+ // Set compilation information.
+ SmallString<128> CWD;
+ if (!sys::fs::current_path(CWD))
+ Ctx.setCompilationDir(CWD);
+ Ctx.setMainFileName(InputFilename);
+
+ StringRef FileType = InputArgs.getLastArgValue(OPT_filetype, "obj");
+ SmallString<255> DefaultOutputFilename;
+ if (InputArgs.hasArg(OPT_as_lex)) {
+ DefaultOutputFilename = "-";
+ } else {
+ DefaultOutputFilename = InputFilename;
+ sys::path::replace_extension(DefaultOutputFilename, FileType);
+ }
+ const StringRef OutputFilename =
+ InputArgs.getLastArgValue(OPT_output_file, DefaultOutputFilename);
+ std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename);
+ if (!Out)
+ return 1;
+
+ std::unique_ptr<buffer_ostream> BOS;
+ raw_pwrite_stream *OS = &Out->os();
+ std::unique_ptr<MCStreamer> Str;
+
+ std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
+ assert(MCII && "Unable to create instruction info!");
+
+ MCInstPrinter *IP = nullptr;
+ if (FileType == "s") {
+ const bool OutputATTAsm = InputArgs.hasArg(OPT_output_att_asm);
+ const unsigned OutputAsmVariant = OutputATTAsm ? 0U // ATT dialect
+ : 1U; // Intel dialect
+ IP = TheTarget->createMCInstPrinter(TheTriple, OutputAsmVariant, *MAI,
+ *MCII, *MRI);
+
+ if (!IP) {
+ WithColor::error()
+ << "unable to create instruction printer for target triple '"
+ << TheTriple.normalize() << "' with "
+ << (OutputATTAsm ? "ATT" : "Intel") << " assembly variant.\n";
+ return 1;
+ }
+
+ // Set the display preference for hex vs. decimal immediates.
+ IP->setPrintImmHex(InputArgs.hasArg(OPT_print_imm_hex));
+
+ // Set up the AsmStreamer.
+ std::unique_ptr<MCCodeEmitter> CE;
+ if (InputArgs.hasArg(OPT_show_encoding))
+ CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
+
+ std::unique_ptr<MCAsmBackend> MAB(
+ TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
+ auto FOut = std::make_unique<formatted_raw_ostream>(*OS);
+ Str.reset(TheTarget->createAsmStreamer(
+ Ctx, std::move(FOut), /*asmverbose*/ true,
+ /*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB),
+ InputArgs.hasArg(OPT_show_inst)));
+
+ } else if (FileType == "null") {
+ Str.reset(TheTarget->createNullStreamer(Ctx));
+ } else if (FileType == "obj") {
+ if (!Out->os().supportsSeeking()) {
+ BOS = std::make_unique<buffer_ostream>(Out->os());
+ OS = BOS.get();
+ }
+
+ 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),
+ MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(CE), *STI,
+ MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible,
+ /*DWARFMustBeAtTheEnd*/ false));
+ } else {
+ llvm_unreachable("Invalid file type!");
+ }
+
+ if (TheTriple.isOSBinFormatCOFF()) {
+ // Emit an absolute @feat.00 symbol. This is a features bitfield read by
+ // link.exe.
+ int64_t Feat00Flags = 0x2;
+ if (SafeSEH) {
+ // According to the PE-COFF spec, the LSB of this value marks the object
+ // for "registered SEH". This means that all SEH handler entry points
+ // must be registered in .sxdata. Use of any unregistered handlers will
+ // cause the process to terminate immediately.
+ Feat00Flags |= 0x1;
+ }
+ MCSymbol *Feat00Sym = Ctx.getOrCreateSymbol("@feat.00");
+ Feat00Sym->setRedefinable(true);
+ Str->emitSymbolAttribute(Feat00Sym, MCSA_Global);
+ Str->emitAssignment(Feat00Sym, MCConstantExpr::create(Feat00Flags, Ctx));
+ }
+
+ // Use Assembler information for parsing.
+ Str->setUseAssemblerInfoForParsing(true);
+
+ int Res = 1;
+ if (InputArgs.hasArg(OPT_as_lex)) {
+ // -as-lex; Lex only, and output a stream of tokens
+ Res = AsLexInput(SrcMgr, *MAI, Out->os());
+ } else {
+ Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI,
+ *MCII, MCOptions, InputArgs);
+ }
+
+ // Keep output if no errors.
+ if (Res == 0)
+ Out->keep();
+ return Res;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-ml/ya.make b/contrib/libs/llvm14/tools/llvm-ml/ya.make
new file mode 100644
index 00000000000..8af930064e7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-ml/ya.make
@@ -0,0 +1,59 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-ml
+ contrib/libs/llvm14/tools/llvm-ml
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ Disassembler.cpp
+ llvm-ml.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-modextract/llvm-modextract.cpp b/contrib/libs/llvm14/tools/llvm-modextract/llvm-modextract.cpp
new file mode 100644
index 00000000000..b1d6bfb790e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-modextract/llvm-modextract.cpp
@@ -0,0 +1,85 @@
+//===-- llvm-modextract.cpp - LLVM module extractor utility ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is for testing features that rely on multi-module bitcode files.
+// It takes a multi-module bitcode file, extracts one of the modules and writes
+// it to the output file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+
+using namespace llvm;
+
+static cl::OptionCategory ModextractCategory("Modextract Options");
+
+static cl::opt<bool>
+ BinaryExtract("b", cl::desc("Whether to perform binary extraction"),
+ cl::cat(ModextractCategory));
+
+static cl::opt<std::string> OutputFilename("o", cl::Required,
+ cl::desc("Output filename"),
+ cl::value_desc("filename"),
+ cl::cat(ModextractCategory));
+
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input bitcode>"),
+ cl::init("-"),
+ cl::cat(ModextractCategory));
+
+static cl::opt<unsigned> ModuleIndex("n", cl::Required,
+ cl::desc("Index of module to extract"),
+ cl::value_desc("index"),
+ cl::cat(ModextractCategory));
+
+int main(int argc, char **argv) {
+ cl::HideUnrelatedOptions({&ModextractCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "Module extractor");
+
+ ExitOnError ExitOnErr("llvm-modextract: error: ");
+
+ std::unique_ptr<MemoryBuffer> MB =
+ ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename)));
+ std::vector<BitcodeModule> Ms = ExitOnErr(getBitcodeModuleList(*MB));
+
+ LLVMContext Context;
+ if (ModuleIndex >= Ms.size()) {
+ errs() << "llvm-modextract: error: module index out of range; bitcode file "
+ "contains "
+ << Ms.size() << " module(s)\n";
+ return 1;
+ }
+
+ std::error_code EC;
+ std::unique_ptr<ToolOutputFile> Out(
+ new ToolOutputFile(OutputFilename, EC, sys::fs::OF_None));
+ ExitOnErr(errorCodeToError(EC));
+
+ if (BinaryExtract) {
+ SmallVector<char, 0> Result;
+ BitcodeWriter Writer(Result);
+ Result.append(Ms[ModuleIndex].getBuffer().begin(),
+ Ms[ModuleIndex].getBuffer().end());
+ Writer.copyStrtab(Ms[ModuleIndex].getStrtab());
+ Out->os() << Result;
+ Out->keep();
+ return 0;
+ }
+
+ std::unique_ptr<Module> M = ExitOnErr(Ms[ModuleIndex].parseModule(Context));
+ WriteBitcodeToFile(*M, Out->os());
+
+ Out->keep();
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-modextract/ya.make b/contrib/libs/llvm14/tools/llvm-modextract/ya.make
new file mode 100644
index 00000000000..e8838a23bb4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-modextract/ya.make
@@ -0,0 +1,42 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-modextract
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-modextract.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-mt/Opts.td b/contrib/libs/llvm14/tools/llvm-mt/Opts.td
new file mode 100644
index 00000000000..c4f7375c6d8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mt/Opts.td
@@ -0,0 +1,30 @@
+include "llvm/Option/OptParser.td"
+
+def unsupported : OptionGroup<"unsupported">;
+def manifest : Separate<["/", "-"], "manifest">, HelpText<"Used to specify each manifest that need to be processed">, MetaVarName<"manifest">;
+def identity : Joined<["/", "-"], "identity:">, HelpText<"Not supported">, MetaVarName<"identity">, Group<unsupported>;
+def rgs : Joined<["/", "-"], "rgs:">, HelpText<"Not supported">, MetaVarName<"script">, Group<unsupported>;
+def tlb : Joined<["/", "-"], "tlb:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>;
+def dll : Joined<["/", "-"], "dll:">, HelpText<"Not supported">, MetaVarName<"dll">, Group<unsupported>;
+def replacements : Joined<["/", "-"], "replacements:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>;
+def managed_assembly_name : Joined<["/", "-"], "managedassemblyname:">, HelpText<"Not supported">, MetaVarName<"assembly">, Group<unsupported>;
+def no_dependency : Flag<["/", "-"], "nodependency">, HelpText<"Not supported">, Group<unsupported>;
+def category : Flag<["/", "-"], "category">, HelpText<"Not supported">, Group<unsupported>;
+def no_logo : Flag<["/", "-"], "nologo">, HelpText<"No effect as this tool never writes copyright data. Included for parity">;
+def out : Joined<["/", "-"], "out:">, HelpText<"Name of the output manifest. If this is skipped and only one manifest is being operated upon by the tool, that manifest is modified in place">, MetaVarName<"manifest">;
+def input_resource : Joined<["/", "-"], "inputresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>;
+def output_resource : Joined<["/", "-"], "outputresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>;
+def output_resource_flag : Flag<["/", "-"], "outputresource">, Alias<output_resource>, HelpText<"Not supported">, Group<unsupported>;
+def update_resource : Joined<["/", "-"], "updateresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>;
+def hash_update : Joined<["/", "-"], "hashupdate:">, HelpText<"Not supported">, MetaVarName<"file">, Group<unsupported>;
+def hash_update_flag : Flag<["/", "-"], "hashupdate">, Alias<hash_update>, HelpText<"Not supported">, Group<unsupported>;
+def validate_manifest : Flag<["/", "-"], "validate_manifest">, HelpText<"Not supported">, Group<unsupported>;
+def validate_file_hashes : Joined<["/", "-"], "validate_file_hashes:">, HelpText<"Not supported">, MetaVarName<"">, Group<unsupported>;
+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<"Exit with a special exit code if the output file has changed">;
+def verbose : Flag<["/", "-"], "verbose">, HelpText<"Not supported">, Group<unsupported>;
+def help : Flag<["/", "-"], "?">;
+def help_long : Flag<["/", "-"], "help">, Alias<help>;
+def h : Flag<["/", "-"], "h">, Alias<help>;
diff --git a/contrib/libs/llvm14/tools/llvm-mt/llvm-mt.cpp b/contrib/libs/llvm14/tools/llvm-mt/llvm-mt.cpp
new file mode 100644
index 00000000000..74885ec28f1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mt/llvm-mt.cpp
@@ -0,0 +1,176 @@
+//===- llvm-mt.cpp - Merge .manifest files ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// Merge .manifest files. This is intended to be a platform-independent port
+// of Microsoft's mt.exe.
+//
+//===---------------------------------------------------------------------===//
+
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/WindowsManifest/WindowsManifestMerger.h"
+
+#include <system_error>
+
+using namespace llvm;
+
+namespace {
+
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+{ \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class CvtResOptTable : public opt::OptTable {
+public:
+ CvtResOptTable() : OptTable(InfoTable, true) {}
+};
+} // namespace
+
+[[noreturn]] static void reportError(Twine Msg) {
+ WithColor::error(errs(), "llvm-mt") << Msg << '\n';
+ exit(1);
+}
+
+static void reportError(StringRef Input, std::error_code EC) {
+ reportError(Twine(Input) + ": " + EC.message());
+}
+
+static void error(Error EC) {
+ if (EC)
+ handleAllErrors(std::move(EC), [&](const ErrorInfoBase &EI) {
+ reportError(EI.message());
+ });
+}
+
+int main(int Argc, const char **Argv) {
+ InitLLVM X(Argc, Argv);
+
+ CvtResOptTable T;
+ unsigned MAI, MAC;
+ ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1);
+ opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
+
+ for (auto *Arg : InputArgs.filtered(OPT_INPUT)) {
+ auto ArgString = Arg->getAsString(InputArgs);
+ std::string Diag;
+ raw_string_ostream OS(Diag);
+ OS << "invalid option '" << ArgString << "'";
+
+ std::string Nearest;
+ if (T.findNearest(ArgString, Nearest) < 2)
+ OS << ", did you mean '" << Nearest << "'?";
+
+ reportError(OS.str());
+ }
+
+ for (auto &Arg : InputArgs) {
+ if (Arg->getOption().matches(OPT_unsupported)) {
+ outs() << "llvm-mt: ignoring unsupported '" << Arg->getOption().getName()
+ << "' option\n";
+ }
+ }
+
+ if (InputArgs.hasArg(OPT_help)) {
+ T.printHelp(outs(), "llvm-mt [options] file...", "Manifest Tool", false);
+ return 0;
+ }
+
+ std::vector<std::string> InputFiles = InputArgs.getAllArgValues(OPT_manifest);
+
+ if (InputFiles.size() == 0) {
+ reportError("no input file specified");
+ }
+
+ StringRef OutputFile;
+ if (InputArgs.hasArg(OPT_out)) {
+ OutputFile = InputArgs.getLastArgValue(OPT_out);
+ } else if (InputFiles.size() == 1) {
+ OutputFile = InputFiles[0];
+ } else {
+ reportError("no output file specified");
+ }
+
+ windows_manifest::WindowsManifestMerger Merger;
+
+ for (const auto &File : InputFiles) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> ManifestOrErr =
+ MemoryBuffer::getFile(File);
+ if (!ManifestOrErr)
+ reportError(File, ManifestOrErr.getError());
+ error(Merger.merge(*ManifestOrErr.get()));
+ }
+
+ std::unique_ptr<MemoryBuffer> OutputBuffer = Merger.getMergedManifest();
+ if (!OutputBuffer)
+ reportError("empty manifest not written");
+
+ int ExitCode = 0;
+ if (InputArgs.hasArg(OPT_notify_update)) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> OutBuffOrErr =
+ MemoryBuffer::getFile(OutputFile);
+ // Assume if we couldn't open the output file then it doesn't exist meaning
+ // there was a change.
+ bool Same = false;
+ if (OutBuffOrErr) {
+ const std::unique_ptr<MemoryBuffer> &FileBuffer = *OutBuffOrErr;
+ Same = std::equal(OutputBuffer->getBufferStart(),
+ OutputBuffer->getBufferEnd(),
+ FileBuffer->getBufferStart());
+ }
+ if (!Same) {
+#if LLVM_ON_UNIX
+ ExitCode = 0xbb;
+#elif defined(_WIN32)
+ ExitCode = 0x41020001;
+#endif
+ }
+ }
+
+ Expected<std::unique_ptr<FileOutputBuffer>> FileOrErr =
+ FileOutputBuffer::create(OutputFile, OutputBuffer->getBufferSize());
+ if (!FileOrErr)
+ reportError(OutputFile, errorToErrorCode(FileOrErr.takeError()));
+ std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr);
+ std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(),
+ FileBuffer->getBufferStart());
+ error(FileBuffer->commit());
+ return ExitCode;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-mt/ya.make b/contrib/libs/llvm14/tools/llvm-mt/ya.make
new file mode 100644
index 00000000000..6ab2c4a4875
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-mt/ya.make
@@ -0,0 +1,31 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/WindowsManifest
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-mt
+ contrib/libs/llvm14/tools/llvm-mt
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-mt.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-nm/Opts.td b/contrib/libs/llvm14/tools/llvm-nm/Opts.td
new file mode 100644
index 00000000000..3a790890909
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-nm/Opts.td
@@ -0,0 +1,76 @@
+include "llvm/Option/OptParser.td"
+
+class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>;
+class FF<string name, string help> : Flag<["--"], name>, HelpText<help>;
+
+multiclass BB<string name, string help1, string help2> {
+ def NAME: Flag<["--"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
+}
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["--"], name #"=">, HelpText<help>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def debug_syms : FF<"debug-syms", "Show all symbols, even debugger only">;
+def defined_only : FF<"defined-only", "Show only defined symbols">;
+defm demangle : BB<"demangle", "Demangle C++ symbol names", "Don't demangle symbol names">;
+def dynamic : FF<"dynamic", "Display dynamic symbols instead of normal symbols">;
+def extern_only : FF<"extern-only", "Show only external symbols">;
+defm format : Eq<"format", "Specify output format: bsd (default), posix, sysv, darwin, just-symbols">, MetaVarName<"<format>">;
+def help : FF<"help", "Display this help">;
+def no_llvm_bc : FF<"no-llvm-bc", "Disable LLVM bitcode reader">;
+def no_sort : FF<"no-sort", "Show symbols in order encountered">;
+def no_weak : FF<"no-weak", "Show only non-weak symbols">;
+def numeric_sort : FF<"numeric-sort", "Sort symbols by address">;
+def print_armap : FF<"print-armap", "Print the archive map">;
+def print_file_name : FF<"print-file-name", "Precede each symbol with the object file it came from">;
+def print_size : FF<"print-size", "Show symbol size as well as address">;
+def quiet : FF<"quiet", "Suppress 'no symbols' diagnostic">;
+defm radix : Eq<"radix", "Radix (o/d/x) for printing symbol Values">, MetaVarName<"<radix>">;
+def reverse_sort : FF<"reverse-sort", "Sort in reverse order">;
+def size_sort : FF<"size-sort", "Sort symbols by size">;
+def special_syms : FF<"special-syms", "Do not filter special symbols from the output">;
+def undefined_only : FF<"undefined-only", "Show only undefined symbols">;
+def version : FF<"version", "Display the version">;
+def without_aliases : FF<"without-aliases", "Exclude aliases from output">, Flags<[HelpHidden]>;
+
+// Mach-O specific options.
+def grp_mach_o : OptionGroup<"kind">, HelpText<"llvm-nm Mach-O Specific Options">;
+
+def add_dyldinfo : FF<"add-dyldinfo", "Add symbols from the dyldinfo not already in the symbol table">, Group<grp_mach_o>;
+def add_inlinedinfo : FF<"add-inlinedinfo", "Add symbols from the inlined libraries, TBD only">, Group<grp_mach_o>;
+def arch_EQ : Joined<["--"], "arch=">, HelpText<"architecture(s) from a Mach-O file to dump">, Group<grp_mach_o>;
+def : Separate<["--", "-"], "arch">, Alias<arch_EQ>;
+def dyldinfo_only : FF<"dyldinfo-only", "Show only symbols from the dyldinfo">, Group<grp_mach_o>;
+def no_dyldinfo : FF<"no-dyldinfo", "Don't add any symbols from the dyldinfo">, Group<grp_mach_o>;
+def s : F<"s", "Dump only symbols from this segment and section name">, Group<grp_mach_o>;
+def x : F<"x", "Print symbol entry in hex">, Group<grp_mach_o>;
+
+def : FF<"just-symbol-name", "Alias for --format=just-symbols">, Alias<format_EQ>, AliasArgs<["just-symbols"]>, Flags<[HelpHidden]>;
+def : FF<"portability", "Alias for --format=posix">, Alias<format_EQ>, AliasArgs<["posix"]>;
+
+def : F<"a", "Alias for --debug-syms">, Alias<debug_syms>;
+def : F<"A", "Alias for --print-file-name">, Alias<print_file_name>;
+def : F<"B", "Alias for --format=bsd">, Alias<format_EQ>, AliasArgs<["bsd"]>;
+def : F<"C", "Alias for --demangle">, Alias<demangle>;
+def : F<"D", "Alias for --dynamic">, Alias<dynamic>;
+def : JoinedOrSeparate<["-"], "f">, HelpText<"Alias for --format">, Alias<format_EQ>, MetaVarName<"<format>">;
+def : F<"h", "Alias for --help">, Alias<help>;
+def : F<"g", "Alias for --extern-only">, Alias<extern_only>;
+def : F<"j", "Alias for --format=just-symbols">, Alias<format_EQ>, AliasArgs<["just-symbols"]>;
+def : F<"m", "Alias for --format=darwin">, Alias<format_EQ>, AliasArgs<["darwin"]>;
+def : F<"M", "Deprecated alias for --print-armap">, Alias<print_armap>, Flags<[HelpHidden]>;
+def : F<"n", "Alias for --numeric-sort">, Alias<numeric_sort>;
+def : F<"o", "Alias for --print-file-name">, Alias<print_file_name>;
+def : F<"p", "Alias for --no-sort">, Alias<no_sort>;
+def : F<"P", "Alias for --format=posix">, Alias<format_EQ>, AliasArgs<["posix"]>;
+def : F<"r", "Alias for --reverse-sort">, Alias<reverse_sort>;
+def : F<"S", "Alias for --print-size">, Alias<print_size>;
+def : JoinedOrSeparate<["-"], "t">, HelpText<"Alias for --radix">, Alias<radix_EQ>, MetaVarName<"<radix>">;
+def : F<"u", "Alias for --undefined-only">, Alias<undefined_only>;
+def : F<"U", "Deprecated alias for --defined-only">, Alias<defined_only>, Flags<[HelpHidden]>;
+def : F<"v", "Alias for --numeric-sort">, Alias<numeric_sort>;
+def : F<"V", "Alias for --version">, Alias<version>;
+def : F<"W", "Deprecated alias for --no-weak">, Alias<no_weak>, Flags<[HelpHidden]>;
diff --git a/contrib/libs/llvm14/tools/llvm-nm/llvm-nm.cpp b/contrib/libs/llvm14/tools/llvm-nm/llvm-nm.cpp
new file mode 100644
index 00000000000..f1d8b002642
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-nm/llvm-nm.cpp
@@ -0,0 +1,2256 @@
+//===-- llvm-nm.cpp - Symbol table dumping utility for llvm ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is a utility that works like traditional Unix "nm", that is, it
+// prints out the names of symbols in a bitcode or object file, along with some
+// information about each symbol.
+//
+// This "nm" supports many of the features of GNU "nm", including its different
+// output formats.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/COFFImportFile.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/TapiFile.h"
+#include "llvm/Object/TapiUniversal.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Object/XCOFFObjectFile.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#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>
+
+using namespace llvm;
+using namespace object;
+
+namespace {
+using namespace llvm::opt; // for HelpHidden in Opts.inc
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class NmOptTable : public opt::OptTable {
+public:
+ NmOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
+};
+
+enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols };
+} // namespace
+
+static bool ArchiveMap;
+static bool DebugSyms;
+static bool DefinedOnly;
+static bool Demangle;
+static bool DynamicSyms;
+static bool ExternalOnly;
+static OutputFormatTy OutputFormat;
+static bool NoLLVMBitcode;
+static bool NoSort;
+static bool NoWeakSymbols;
+static bool NumericSort;
+static bool PrintFileName;
+static bool PrintSize;
+static bool Quiet;
+static bool ReverseSort;
+static bool SpecialSyms;
+static bool SizeSort;
+static bool UndefinedOnly;
+static bool WithoutAliases;
+
+namespace {
+enum Radix { d, o, x };
+} // namespace
+static Radix AddressRadix;
+
+// Mach-O specific options.
+static bool ArchAll = false;
+static std::vector<StringRef> ArchFlags;
+static bool AddDyldInfo;
+static bool AddInlinedInfo;
+static bool DyldInfoOnly;
+static bool FormatMachOasHex;
+static bool NoDyldInfo;
+static std::vector<StringRef> SegSect;
+static bool MachOPrintSizeWarning = false;
+
+// Miscellaneous states.
+static bool PrintAddress = true;
+static bool MultipleFiles = false;
+static bool HadError = false;
+
+static StringRef ToolName;
+
+static void warn(Error Err, Twine FileName, Twine Context = Twine()) {
+ assert(Err);
+
+ // Flush the standard output so that the warning isn't interleaved with other
+ // output if stdout and stderr are writing to the same place.
+ outs().flush();
+
+ handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) {
+ WithColor::warning(errs(), ToolName)
+ << FileName << ": " << (Context.str().empty() ? "" : Context + ": ")
+ << EI.message() << "\n";
+ });
+}
+
+static void error(Twine Message, Twine Path = Twine()) {
+ HadError = true;
+ WithColor::error(errs(), ToolName) << Path << ": " << Message << "\n";
+}
+
+static bool error(std::error_code EC, Twine Path = Twine()) {
+ if (EC) {
+ error(EC.message(), Path);
+ return true;
+ }
+ return false;
+}
+
+// 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.
+static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ 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
+ // the index of which archive member this is and or its offset in the
+ // archive instead of "???" as the name.
+ if (!NameOrErr) {
+ consumeError(NameOrErr.takeError());
+ errs() << "(" << "???" << ")";
+ } else
+ errs() << "(" << NameOrErr.get() << ")";
+
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ")";
+
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS);
+ OS.flush();
+ errs() << ": " << Buf << "\n";
+}
+
+// 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.
+static void error(llvm::Error E, StringRef FileName,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ 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);
+ OS.flush();
+ errs() << ": " << Buf << "\n";
+}
+
+namespace {
+struct NMSymbol {
+ uint64_t Address;
+ uint64_t Size;
+ char TypeChar;
+ std::string Name;
+ StringRef SectionName;
+ StringRef TypeName;
+ BasicSymbolRef Sym;
+ // The Sym field above points to the native symbol in the object file,
+ // for Mach-O when we are creating symbols from the dyld info the above
+ // pointer is null as there is no native symbol. In these cases the fields
+ // below are filled in to represent what would have been a Mach-O nlist
+ // native symbol.
+ uint32_t SymFlags;
+ SectionRef Section;
+ uint8_t NType;
+ uint8_t NSect;
+ uint16_t NDesc;
+ std::string IndirectName;
+};
+} // anonymous namespace
+
+static bool compareSymbolAddress(const NMSymbol &A, const NMSymbol &B) {
+ bool ADefined;
+ // Symbol flags have been checked in the caller.
+ if (A.Sym.getRawDataRefImpl().p) {
+ uint32_t AFlags = cantFail(A.Sym.getFlags());
+ ADefined = !(AFlags & SymbolRef::SF_Undefined);
+ } else {
+ ADefined = A.TypeChar != 'U';
+ }
+ bool BDefined;
+ // Symbol flags have been checked in the caller.
+ if (B.Sym.getRawDataRefImpl().p) {
+ uint32_t BFlags = cantFail(B.Sym.getFlags());
+ BDefined = !(BFlags & SymbolRef::SF_Undefined);
+ } else {
+ BDefined = B.TypeChar != 'U';
+ }
+ return std::make_tuple(ADefined, A.Address, A.Name, A.Size) <
+ std::make_tuple(BDefined, B.Address, B.Name, B.Size);
+}
+
+static bool compareSymbolSize(const NMSymbol &A, const NMSymbol &B) {
+ return std::make_tuple(A.Size, A.Name, A.Address) <
+ std::make_tuple(B.Size, B.Name, B.Address);
+}
+
+static bool compareSymbolName(const NMSymbol &A, const NMSymbol &B) {
+ return std::make_tuple(A.Name, A.Size, A.Address) <
+ std::make_tuple(B.Name, B.Size, B.Address);
+}
+
+static char isSymbolList64Bit(SymbolicFile &Obj) {
+ if (auto *IRObj = dyn_cast<IRObjectFile>(&Obj))
+ return Triple(IRObj->getTargetTriple()).isArch64Bit();
+ if (isa<COFFObjectFile>(Obj) || isa<COFFImportFile>(Obj))
+ return false;
+ if (XCOFFObjectFile *XCOFFObj = dyn_cast<XCOFFObjectFile>(&Obj))
+ return XCOFFObj->is64Bit();
+
+ if (isa<WasmObjectFile>(Obj))
+ return false;
+ if (TapiFile *Tapi = dyn_cast<TapiFile>(&Obj))
+ return Tapi->is64Bit();
+ if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj))
+ return MachO->is64Bit();
+ return cast<ELFObjectFileBase>(Obj).getBytesInAddress() == 8;
+}
+
+static StringRef CurrentFilename;
+static std::vector<NMSymbol> SymbolList;
+
+static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I);
+
+// darwinPrintSymbol() is used to print a symbol from a Mach-O file when the
+// the OutputFormat is darwin or we are printing Mach-O symbols in hex. For
+// the darwin format it produces the same output as darwin's nm(1) -m output
+// and when printing Mach-O symbols in hex it produces the same output as
+// darwin's nm(1) -x format.
+static void darwinPrintSymbol(SymbolicFile &Obj, const NMSymbol &S,
+ char *SymbolAddrStr, const char *printBlanks,
+ const char *printDashes,
+ const char *printFormat) {
+ MachO::mach_header H;
+ MachO::mach_header_64 H_64;
+ uint32_t Filetype = MachO::MH_OBJECT;
+ uint32_t Flags = 0;
+ uint8_t NType = 0;
+ uint8_t NSect = 0;
+ uint16_t NDesc = 0;
+ uint32_t NStrx = 0;
+ uint64_t NValue = 0;
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj);
+ if (Obj.isIR()) {
+ uint32_t SymFlags = cantFail(S.Sym.getFlags());
+ if (SymFlags & SymbolRef::SF_Global)
+ NType |= MachO::N_EXT;
+ if (SymFlags & SymbolRef::SF_Hidden)
+ NType |= MachO::N_PEXT;
+ if (SymFlags & SymbolRef::SF_Undefined)
+ NType |= MachO::N_EXT | MachO::N_UNDF;
+ else {
+ // Here we have a symbol definition. So to fake out a section name we
+ // use 1, 2 and 3 for section numbers. See below where they are used to
+ // print out fake section names.
+ NType |= MachO::N_SECT;
+ if (SymFlags & SymbolRef::SF_Const)
+ NSect = 3;
+ else if (SymFlags & SymbolRef::SF_Executable)
+ NSect = 1;
+ else
+ NSect = 2;
+ }
+ if (SymFlags & SymbolRef::SF_Weak)
+ NDesc |= MachO::N_WEAK_DEF;
+ } else {
+ DataRefImpl SymDRI = S.Sym.getRawDataRefImpl();
+ if (MachO->is64Bit()) {
+ H_64 = MachO->MachOObjectFile::getHeader64();
+ Filetype = H_64.filetype;
+ Flags = H_64.flags;
+ if (SymDRI.p){
+ MachO::nlist_64 STE_64 = MachO->getSymbol64TableEntry(SymDRI);
+ NType = STE_64.n_type;
+ NSect = STE_64.n_sect;
+ NDesc = STE_64.n_desc;
+ NStrx = STE_64.n_strx;
+ NValue = STE_64.n_value;
+ } else {
+ NType = S.NType;
+ NSect = S.NSect;
+ NDesc = S.NDesc;
+ NStrx = 0;
+ NValue = S.Address;
+ }
+ } else {
+ H = MachO->MachOObjectFile::getHeader();
+ Filetype = H.filetype;
+ Flags = H.flags;
+ if (SymDRI.p){
+ MachO::nlist STE = MachO->getSymbolTableEntry(SymDRI);
+ NType = STE.n_type;
+ NSect = STE.n_sect;
+ NDesc = STE.n_desc;
+ NStrx = STE.n_strx;
+ NValue = STE.n_value;
+ } else {
+ NType = S.NType;
+ NSect = S.NSect;
+ NDesc = S.NDesc;
+ NStrx = 0;
+ NValue = S.Address;
+ }
+ }
+ }
+
+ // If we are printing Mach-O symbols in hex do that and return.
+ if (FormatMachOasHex) {
+ outs() << format(printFormat, NValue) << ' '
+ << format("%02x %02x %04x %08x", NType, NSect, NDesc, NStrx) << ' '
+ << S.Name;
+ if ((NType & MachO::N_TYPE) == MachO::N_INDR) {
+ outs() << " (indirect for ";
+ outs() << format(printFormat, NValue) << ' ';
+ StringRef IndirectName;
+ if (S.Sym.getRawDataRefImpl().p) {
+ if (MachO->getIndirectName(S.Sym.getRawDataRefImpl(), IndirectName))
+ outs() << "?)";
+ else
+ outs() << IndirectName << ")";
+ } else
+ outs() << S.IndirectName << ")";
+ }
+ outs() << "\n";
+ return;
+ }
+
+ if (PrintAddress) {
+ if ((NType & MachO::N_TYPE) == MachO::N_INDR)
+ strcpy(SymbolAddrStr, printBlanks);
+ if (Obj.isIR() && (NType & MachO::N_TYPE) == MachO::N_TYPE)
+ strcpy(SymbolAddrStr, printDashes);
+ outs() << SymbolAddrStr << ' ';
+ }
+
+ switch (NType & MachO::N_TYPE) {
+ case MachO::N_UNDF:
+ if (NValue != 0) {
+ outs() << "(common) ";
+ if (MachO::GET_COMM_ALIGN(NDesc) != 0)
+ outs() << "(alignment 2^" << (int)MachO::GET_COMM_ALIGN(NDesc) << ") ";
+ } else {
+ if ((NType & MachO::N_TYPE) == MachO::N_PBUD)
+ outs() << "(prebound ";
+ else
+ outs() << "(";
+ if ((NDesc & MachO::REFERENCE_TYPE) ==
+ MachO::REFERENCE_FLAG_UNDEFINED_LAZY)
+ outs() << "undefined [lazy bound]) ";
+ else if ((NDesc & MachO::REFERENCE_TYPE) ==
+ MachO::REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY)
+ outs() << "undefined [private lazy bound]) ";
+ else if ((NDesc & MachO::REFERENCE_TYPE) ==
+ MachO::REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY)
+ outs() << "undefined [private]) ";
+ else
+ outs() << "undefined) ";
+ }
+ break;
+ case MachO::N_ABS:
+ outs() << "(absolute) ";
+ break;
+ case MachO::N_INDR:
+ outs() << "(indirect) ";
+ break;
+ case MachO::N_SECT: {
+ if (Obj.isIR()) {
+ // For llvm bitcode files print out a fake section name using the values
+ // use 1, 2 and 3 for section numbers as set above.
+ if (NSect == 1)
+ outs() << "(LTO,CODE) ";
+ else if (NSect == 2)
+ outs() << "(LTO,DATA) ";
+ else if (NSect == 3)
+ outs() << "(LTO,RODATA) ";
+ else
+ outs() << "(?,?) ";
+ break;
+ }
+ section_iterator Sec = SectionRef();
+ if (S.Sym.getRawDataRefImpl().p) {
+ Expected<section_iterator> SecOrErr =
+ MachO->getSymbolSection(S.Sym.getRawDataRefImpl());
+ if (!SecOrErr) {
+ consumeError(SecOrErr.takeError());
+ outs() << "(?,?) ";
+ break;
+ }
+ Sec = *SecOrErr;
+ if (Sec == MachO->section_end()) {
+ outs() << "(?,?) ";
+ break;
+ }
+ } else {
+ Sec = S.Section;
+ }
+ DataRefImpl Ref = Sec->getRawDataRefImpl();
+ StringRef SectionName;
+ if (Expected<StringRef> NameOrErr = MachO->getSectionName(Ref))
+ SectionName = *NameOrErr;
+ StringRef SegmentName = MachO->getSectionFinalSegmentName(Ref);
+ outs() << "(" << SegmentName << "," << SectionName << ") ";
+ break;
+ }
+ default:
+ outs() << "(?) ";
+ break;
+ }
+
+ if (NType & MachO::N_EXT) {
+ if (NDesc & MachO::REFERENCED_DYNAMICALLY)
+ outs() << "[referenced dynamically] ";
+ if (NType & MachO::N_PEXT) {
+ if ((NDesc & MachO::N_WEAK_DEF) == MachO::N_WEAK_DEF)
+ outs() << "weak private external ";
+ else
+ outs() << "private external ";
+ } else {
+ if ((NDesc & MachO::N_WEAK_REF) == MachO::N_WEAK_REF ||
+ (NDesc & MachO::N_WEAK_DEF) == MachO::N_WEAK_DEF) {
+ if ((NDesc & (MachO::N_WEAK_REF | MachO::N_WEAK_DEF)) ==
+ (MachO::N_WEAK_REF | MachO::N_WEAK_DEF))
+ outs() << "weak external automatically hidden ";
+ else
+ outs() << "weak external ";
+ } else
+ outs() << "external ";
+ }
+ } else {
+ if (NType & MachO::N_PEXT)
+ outs() << "non-external (was a private external) ";
+ else
+ outs() << "non-external ";
+ }
+
+ if (Filetype == MachO::MH_OBJECT) {
+ if (NDesc & MachO::N_NO_DEAD_STRIP)
+ outs() << "[no dead strip] ";
+ if ((NType & MachO::N_TYPE) != MachO::N_UNDF &&
+ NDesc & MachO::N_SYMBOL_RESOLVER)
+ outs() << "[symbol resolver] ";
+ if ((NType & MachO::N_TYPE) != MachO::N_UNDF && NDesc & MachO::N_ALT_ENTRY)
+ outs() << "[alt entry] ";
+ if ((NType & MachO::N_TYPE) != MachO::N_UNDF && NDesc & MachO::N_COLD_FUNC)
+ outs() << "[cold func] ";
+ }
+
+ if ((NDesc & MachO::N_ARM_THUMB_DEF) == MachO::N_ARM_THUMB_DEF)
+ outs() << "[Thumb] ";
+
+ if ((NType & MachO::N_TYPE) == MachO::N_INDR) {
+ outs() << S.Name << " (for ";
+ StringRef IndirectName;
+ if (MachO) {
+ if (S.Sym.getRawDataRefImpl().p) {
+ if (MachO->getIndirectName(S.Sym.getRawDataRefImpl(), IndirectName))
+ outs() << "?)";
+ else
+ outs() << IndirectName << ")";
+ } else
+ outs() << S.IndirectName << ")";
+ } else
+ outs() << "?)";
+ } else
+ outs() << S.Name;
+
+ if ((Flags & MachO::MH_TWOLEVEL) == MachO::MH_TWOLEVEL &&
+ (((NType & MachO::N_TYPE) == MachO::N_UNDF && NValue == 0) ||
+ (NType & MachO::N_TYPE) == MachO::N_PBUD)) {
+ uint32_t LibraryOrdinal = MachO::GET_LIBRARY_ORDINAL(NDesc);
+ if (LibraryOrdinal != 0) {
+ if (LibraryOrdinal == MachO::EXECUTABLE_ORDINAL)
+ outs() << " (from executable)";
+ else if (LibraryOrdinal == MachO::DYNAMIC_LOOKUP_ORDINAL)
+ outs() << " (dynamically looked up)";
+ else {
+ StringRef LibraryName;
+ if (!MachO ||
+ MachO->getLibraryShortNameByIndex(LibraryOrdinal - 1, LibraryName))
+ outs() << " (from bad library ordinal " << LibraryOrdinal << ")";
+ else
+ outs() << " (from " << LibraryName << ")";
+ }
+ }
+ }
+
+ outs() << "\n";
+}
+
+// Table that maps Darwin's Mach-O stab constants to strings to allow printing.
+struct DarwinStabName {
+ uint8_t NType;
+ const char *Name;
+};
+const struct DarwinStabName DarwinStabNames[] = {
+ {MachO::N_GSYM, "GSYM"},
+ {MachO::N_FNAME, "FNAME"},
+ {MachO::N_FUN, "FUN"},
+ {MachO::N_STSYM, "STSYM"},
+ {MachO::N_LCSYM, "LCSYM"},
+ {MachO::N_BNSYM, "BNSYM"},
+ {MachO::N_PC, "PC"},
+ {MachO::N_AST, "AST"},
+ {MachO::N_OPT, "OPT"},
+ {MachO::N_RSYM, "RSYM"},
+ {MachO::N_SLINE, "SLINE"},
+ {MachO::N_ENSYM, "ENSYM"},
+ {MachO::N_SSYM, "SSYM"},
+ {MachO::N_SO, "SO"},
+ {MachO::N_OSO, "OSO"},
+ {MachO::N_LSYM, "LSYM"},
+ {MachO::N_BINCL, "BINCL"},
+ {MachO::N_SOL, "SOL"},
+ {MachO::N_PARAMS, "PARAM"},
+ {MachO::N_VERSION, "VERS"},
+ {MachO::N_OLEVEL, "OLEV"},
+ {MachO::N_PSYM, "PSYM"},
+ {MachO::N_EINCL, "EINCL"},
+ {MachO::N_ENTRY, "ENTRY"},
+ {MachO::N_LBRAC, "LBRAC"},
+ {MachO::N_EXCL, "EXCL"},
+ {MachO::N_RBRAC, "RBRAC"},
+ {MachO::N_BCOMM, "BCOMM"},
+ {MachO::N_ECOMM, "ECOMM"},
+ {MachO::N_ECOML, "ECOML"},
+ {MachO::N_LENG, "LENG"},
+};
+
+static const char *getDarwinStabString(uint8_t NType) {
+ for (auto I : makeArrayRef(DarwinStabNames))
+ if (I.NType == NType)
+ return I.Name;
+ return nullptr;
+}
+
+// darwinPrintStab() prints the n_sect, n_desc along with a symbolic name of
+// a stab n_type value in a Mach-O file.
+static void darwinPrintStab(MachOObjectFile *MachO, const NMSymbol &S) {
+ MachO::nlist_64 STE_64;
+ MachO::nlist STE;
+ uint8_t NType;
+ uint8_t NSect;
+ uint16_t NDesc;
+ DataRefImpl SymDRI = S.Sym.getRawDataRefImpl();
+ if (MachO->is64Bit()) {
+ STE_64 = MachO->getSymbol64TableEntry(SymDRI);
+ NType = STE_64.n_type;
+ NSect = STE_64.n_sect;
+ NDesc = STE_64.n_desc;
+ } else {
+ STE = MachO->getSymbolTableEntry(SymDRI);
+ NType = STE.n_type;
+ NSect = STE.n_sect;
+ NDesc = STE.n_desc;
+ }
+
+ outs() << format(" %02x %04x ", NSect, NDesc);
+ if (const char *stabString = getDarwinStabString(NType))
+ outs() << format("%5.5s", stabString);
+ else
+ outs() << format(" %02x", NType);
+}
+
+static Optional<std::string> demangle(StringRef Name) {
+ std::string Demangled;
+ if (nonMicrosoftDemangle(Name.str().c_str(), Demangled))
+ return Demangled;
+ return None;
+}
+
+static Optional<std::string> demangleXCOFF(StringRef Name) {
+ if (Name.empty() || Name[0] != '.')
+ return demangle(Name);
+
+ Name = Name.drop_front();
+ Optional<std::string> DemangledName = demangle(Name);
+ if (DemangledName)
+ return "." + *DemangledName;
+ return None;
+}
+
+static Optional<std::string> demangleMachO(StringRef Name) {
+ if (!Name.empty() && Name[0] == '_')
+ Name = Name.drop_front();
+ return demangle(Name);
+}
+
+static bool symbolIsDefined(const NMSymbol &Sym) {
+ return Sym.TypeChar != 'U' && Sym.TypeChar != 'w' && Sym.TypeChar != 'v';
+}
+
+static void writeFileName(raw_ostream &S, StringRef ArchiveName,
+ StringRef ArchitectureName) {
+ if (!ArchitectureName.empty())
+ S << "(for architecture " << ArchitectureName << "):";
+ if (OutputFormat == posix && !ArchiveName.empty())
+ S << ArchiveName << "[" << CurrentFilename << "]: ";
+ else {
+ if (!ArchiveName.empty())
+ S << ArchiveName << ":";
+ S << CurrentFilename << ": ";
+ }
+}
+
+static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
+ StringRef ArchiveName,
+ StringRef ArchitectureName) {
+ if (!NoSort) {
+ using Comparator = bool (*)(const NMSymbol &, const NMSymbol &);
+ Comparator Cmp;
+ if (NumericSort)
+ Cmp = &compareSymbolAddress;
+ else if (SizeSort)
+ Cmp = &compareSymbolSize;
+ else
+ Cmp = &compareSymbolName;
+
+ if (ReverseSort)
+ llvm::sort(SymbolList, [=](const NMSymbol &A, const NMSymbol &B) -> bool {
+ return Cmp(B, A);
+ });
+ else
+ llvm::sort(SymbolList, Cmp);
+ }
+
+ if (!PrintFileName) {
+ if ((OutputFormat == bsd || OutputFormat == posix ||
+ OutputFormat == just_symbols) &&
+ MultipleFiles && printName) {
+ outs() << '\n' << CurrentFilename << ":\n";
+ } else if (OutputFormat == sysv) {
+ outs() << "\n\nSymbols from " << CurrentFilename << ":\n\n";
+ if (isSymbolList64Bit(Obj))
+ outs() << "Name Value Class Type"
+ << " Size Line Section\n";
+ else
+ outs() << "Name Value Class Type"
+ << " Size Line Section\n";
+ }
+ }
+
+ const char *printBlanks, *printDashes, *printFormat;
+ if (isSymbolList64Bit(Obj)) {
+ printBlanks = " ";
+ printDashes = "----------------";
+ switch (AddressRadix) {
+ case Radix::o:
+ printFormat = OutputFormat == posix ? "%" PRIo64 : "%016" PRIo64;
+ break;
+ case Radix::x:
+ printFormat = OutputFormat == posix ? "%" PRIx64 : "%016" PRIx64;
+ break;
+ default:
+ printFormat = OutputFormat == posix ? "%" PRId64 : "%016" PRId64;
+ }
+ } else {
+ printBlanks = " ";
+ printDashes = "--------";
+ switch (AddressRadix) {
+ case Radix::o:
+ printFormat = OutputFormat == posix ? "%" PRIo64 : "%08" PRIo64;
+ break;
+ case Radix::x:
+ printFormat = OutputFormat == posix ? "%" PRIx64 : "%08" PRIx64;
+ break;
+ default:
+ printFormat = OutputFormat == posix ? "%" PRId64 : "%08" PRId64;
+ }
+ }
+
+ for (const NMSymbol &S : SymbolList) {
+ uint32_t SymFlags;
+ std::string Name = S.Name;
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj);
+ if (Demangle) {
+ function_ref<Optional<std::string>(StringRef)> Fn = ::demangle;
+ if (Obj.isXCOFF())
+ Fn = demangleXCOFF;
+ if (Obj.isMachO())
+ Fn = demangleMachO;
+ if (Optional<std::string> Opt = Fn(S.Name))
+ Name = *Opt;
+ }
+ if (S.Sym.getRawDataRefImpl().p) {
+ Expected<uint32_t> SymFlagsOrErr = S.Sym.getFlags();
+ if (!SymFlagsOrErr) {
+ // TODO: Test this error.
+ error(SymFlagsOrErr.takeError(), Obj.getFileName());
+ return;
+ }
+ SymFlags = *SymFlagsOrErr;
+ } else
+ SymFlags = S.SymFlags;
+
+ bool Undefined = SymFlags & SymbolRef::SF_Undefined;
+ bool Global = SymFlags & SymbolRef::SF_Global;
+ bool Weak = SymFlags & SymbolRef::SF_Weak;
+ bool FormatSpecific = SymFlags & SymbolRef::SF_FormatSpecific;
+ if ((!Undefined && UndefinedOnly) || (Undefined && DefinedOnly) ||
+ (!Global && ExternalOnly) || (Weak && NoWeakSymbols) ||
+ (FormatSpecific && !(SpecialSyms || DebugSyms)))
+ continue;
+ if (PrintFileName)
+ writeFileName(outs(), ArchiveName, ArchitectureName);
+ if ((OutputFormat == just_symbols ||
+ (UndefinedOnly && MachO && OutputFormat != darwin)) &&
+ OutputFormat != posix) {
+ outs() << Name << "\n";
+ continue;
+ }
+
+ char SymbolAddrStr[23], SymbolSizeStr[23];
+
+ // If the format is SysV or the symbol isn't defined, then print spaces.
+ if (OutputFormat == sysv || !symbolIsDefined(S)) {
+ if (OutputFormat == posix) {
+ format(printFormat, S.Address)
+ .print(SymbolAddrStr, sizeof(SymbolAddrStr));
+ format(printFormat, S.Size).print(SymbolSizeStr, sizeof(SymbolSizeStr));
+ } else {
+ strcpy(SymbolAddrStr, printBlanks);
+ strcpy(SymbolSizeStr, printBlanks);
+ }
+ }
+
+ if (symbolIsDefined(S)) {
+ // Otherwise, print the symbol address and size.
+ if (Obj.isIR())
+ strcpy(SymbolAddrStr, printDashes);
+ else if (MachO && S.TypeChar == 'I')
+ strcpy(SymbolAddrStr, printBlanks);
+ else
+ format(printFormat, S.Address)
+ .print(SymbolAddrStr, sizeof(SymbolAddrStr));
+ format(printFormat, S.Size).print(SymbolSizeStr, sizeof(SymbolSizeStr));
+ }
+
+ // If OutputFormat is darwin or we are printing Mach-O symbols in hex and
+ // we have a MachOObjectFile, call darwinPrintSymbol to print as darwin's
+ // nm(1) -m output or hex, else if OutputFormat is darwin or we are
+ // printing Mach-O symbols in hex and not a Mach-O object fall back to
+ // OutputFormat bsd (see below).
+ if ((OutputFormat == darwin || FormatMachOasHex) && (MachO || Obj.isIR())) {
+ darwinPrintSymbol(Obj, S, SymbolAddrStr, printBlanks, printDashes,
+ printFormat);
+ } else if (OutputFormat == posix) {
+ outs() << Name << " " << S.TypeChar << " " << SymbolAddrStr << " "
+ << (MachO ? "0" : SymbolSizeStr) << "\n";
+ } else if (OutputFormat == bsd || (OutputFormat == darwin && !MachO)) {
+ if (PrintAddress)
+ outs() << SymbolAddrStr << ' ';
+ if (PrintSize)
+ outs() << SymbolSizeStr << ' ';
+ outs() << S.TypeChar;
+ if (S.TypeChar == '-' && MachO)
+ darwinPrintStab(MachO, S);
+ outs() << " " << Name;
+ if (S.TypeChar == 'I' && MachO) {
+ outs() << " (indirect for ";
+ if (S.Sym.getRawDataRefImpl().p) {
+ StringRef IndirectName;
+ if (MachO->getIndirectName(S.Sym.getRawDataRefImpl(), IndirectName))
+ outs() << "?)";
+ else
+ outs() << IndirectName << ")";
+ } else
+ outs() << S.IndirectName << ")";
+ }
+ outs() << "\n";
+ } else if (OutputFormat == sysv) {
+ outs() << left_justify(Name, 20) << "|" << SymbolAddrStr << "| "
+ << S.TypeChar << " |" << right_justify(S.TypeName, 18) << "|"
+ << SymbolSizeStr << "| |" << S.SectionName << "\n";
+ }
+ }
+
+ SymbolList.clear();
+}
+
+static char getSymbolNMTypeChar(ELFObjectFileBase &Obj,
+ basic_symbol_iterator I) {
+ // OK, this is ELF
+ elf_symbol_iterator SymI(I);
+
+ Expected<elf_section_iterator> SecIOrErr = SymI->getSection();
+ if (!SecIOrErr) {
+ consumeError(SecIOrErr.takeError());
+ return '?';
+ }
+
+ uint8_t Binding = SymI->getBinding();
+ if (Binding == ELF::STB_GNU_UNIQUE)
+ return 'u';
+
+ assert(Binding != ELF::STB_WEAK && "STB_WEAK not tested in calling function");
+ if (Binding != ELF::STB_GLOBAL && Binding != ELF::STB_LOCAL)
+ return '?';
+
+ elf_section_iterator SecI = *SecIOrErr;
+ if (SecI != Obj.section_end()) {
+ uint32_t Type = SecI->getType();
+ uint64_t Flags = SecI->getFlags();
+ if (Flags & ELF::SHF_EXECINSTR)
+ return 't';
+ if (Type == ELF::SHT_NOBITS)
+ return 'b';
+ if (Flags & ELF::SHF_ALLOC)
+ return Flags & ELF::SHF_WRITE ? 'd' : 'r';
+
+ auto NameOrErr = SecI->getName();
+ if (!NameOrErr) {
+ consumeError(NameOrErr.takeError());
+ return '?';
+ }
+ if ((*NameOrErr).startswith(".debug"))
+ return 'N';
+ if (!(Flags & ELF::SHF_WRITE))
+ return 'n';
+ }
+
+ return '?';
+}
+
+static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) {
+ COFFSymbolRef Symb = Obj.getCOFFSymbol(*I);
+ // OK, this is COFF.
+ symbol_iterator SymI(I);
+
+ Expected<StringRef> Name = SymI->getName();
+ if (!Name) {
+ consumeError(Name.takeError());
+ return '?';
+ }
+
+ char Ret = StringSwitch<char>(*Name)
+ .StartsWith(".debug", 'N')
+ .StartsWith(".sxdata", 'N')
+ .Default('?');
+
+ if (Ret != '?')
+ return Ret;
+
+ uint32_t Characteristics = 0;
+ if (!COFF::isReservedSectionNumber(Symb.getSectionNumber())) {
+ Expected<section_iterator> SecIOrErr = SymI->getSection();
+ if (!SecIOrErr) {
+ consumeError(SecIOrErr.takeError());
+ return '?';
+ }
+ section_iterator SecI = *SecIOrErr;
+ const coff_section *Section = Obj.getCOFFSection(*SecI);
+ Characteristics = Section->Characteristics;
+ if (Expected<StringRef> NameOrErr = Obj.getSectionName(Section))
+ if (NameOrErr->startswith(".idata"))
+ return 'i';
+ }
+
+ switch (Symb.getSectionNumber()) {
+ case COFF::IMAGE_SYM_DEBUG:
+ return 'n';
+ default:
+ // Check section type.
+ if (Characteristics & COFF::IMAGE_SCN_CNT_CODE)
+ return 't';
+ if (Characteristics & COFF::IMAGE_SCN_CNT_INITIALIZED_DATA)
+ return Characteristics & COFF::IMAGE_SCN_MEM_WRITE ? 'd' : 'r';
+ if (Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)
+ return 'b';
+ if (Characteristics & COFF::IMAGE_SCN_LNK_INFO)
+ return 'i';
+ // Check for section symbol.
+ if (Symb.isSectionDefinition())
+ return 's';
+ }
+
+ return '?';
+}
+
+static char getSymbolNMTypeChar(XCOFFObjectFile &Obj, symbol_iterator I) {
+ Expected<uint32_t> TypeOrErr = I->getType();
+ if (!TypeOrErr) {
+ warn(TypeOrErr.takeError(), Obj.getFileName(),
+ "for symbol with index " +
+ Twine(Obj.getSymbolIndex(I->getRawDataRefImpl().p)));
+ return '?';
+ }
+
+ uint32_t SymType = *TypeOrErr;
+
+ if (SymType == SymbolRef::ST_File)
+ return 'f';
+
+ // If the I->getSection() call would return an error, the earlier I->getType()
+ // call will already have returned the same error first.
+ section_iterator SecIter = cantFail(I->getSection());
+
+ if (SecIter == Obj.section_end())
+ return '?';
+
+ if (Obj.isDebugSection(SecIter->getRawDataRefImpl()))
+ return 'N';
+
+ if (SecIter->isText())
+ return 't';
+
+ if (SecIter->isData())
+ return 'd';
+
+ if (SecIter->isBSS())
+ return 'b';
+
+ return '?';
+}
+
+static char getSymbolNMTypeChar(COFFImportFile &Obj) {
+ switch (Obj.getCOFFImportHeader()->getType()) {
+ case COFF::IMPORT_CODE:
+ return 't';
+ case COFF::IMPORT_DATA:
+ return 'd';
+ case COFF::IMPORT_CONST:
+ return 'r';
+ }
+ return '?';
+}
+
+static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) {
+ DataRefImpl Symb = I->getRawDataRefImpl();
+ uint8_t NType = Obj.is64Bit() ? Obj.getSymbol64TableEntry(Symb).n_type
+ : Obj.getSymbolTableEntry(Symb).n_type;
+
+ if (NType & MachO::N_STAB)
+ return '-';
+
+ switch (NType & MachO::N_TYPE) {
+ case MachO::N_ABS:
+ return 's';
+ case MachO::N_INDR:
+ return 'i';
+ case MachO::N_SECT: {
+ Expected<section_iterator> SecOrErr = Obj.getSymbolSection(Symb);
+ if (!SecOrErr) {
+ consumeError(SecOrErr.takeError());
+ return 's';
+ }
+ section_iterator Sec = *SecOrErr;
+ if (Sec == Obj.section_end())
+ return 's';
+ DataRefImpl Ref = Sec->getRawDataRefImpl();
+ StringRef SectionName;
+ if (Expected<StringRef> NameOrErr = Obj.getSectionName(Ref))
+ SectionName = *NameOrErr;
+ StringRef SegmentName = Obj.getSectionFinalSegmentName(Ref);
+ if (Obj.is64Bit() && Obj.getHeader64().filetype == MachO::MH_KEXT_BUNDLE &&
+ SegmentName == "__TEXT_EXEC" && SectionName == "__text")
+ return 't';
+ if (SegmentName == "__TEXT" && SectionName == "__text")
+ return 't';
+ if (SegmentName == "__DATA" && SectionName == "__data")
+ return 'd';
+ if (SegmentName == "__DATA" && SectionName == "__bss")
+ return 'b';
+ return 's';
+ }
+ }
+
+ return '?';
+}
+
+static char getSymbolNMTypeChar(TapiFile &Obj, basic_symbol_iterator I) {
+ return 's';
+}
+
+static char getSymbolNMTypeChar(WasmObjectFile &Obj, basic_symbol_iterator I) {
+ uint32_t Flags = cantFail(I->getFlags());
+ if (Flags & SymbolRef::SF_Executable)
+ return 't';
+ return 'd';
+}
+
+static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I) {
+ uint32_t Flags = cantFail(I->getFlags());
+ // FIXME: should we print 'b'? At the IR level we cannot be sure if this
+ // will be in bss or not, but we could approximate.
+ if (Flags & SymbolRef::SF_Executable)
+ return 't';
+ else if (Triple(Obj.getTargetTriple()).isOSDarwin() &&
+ (Flags & SymbolRef::SF_Const))
+ return 's';
+ else
+ return 'd';
+}
+
+static bool isObject(SymbolicFile &Obj, basic_symbol_iterator I) {
+ return isa<ELFObjectFileBase>(&Obj) &&
+ elf_symbol_iterator(I)->getELFType() == ELF::STT_OBJECT;
+}
+
+// For ELF object files, Set TypeName to the symbol typename, to be printed
+// in the 'Type' column of the SYSV format output.
+static StringRef getNMTypeName(SymbolicFile &Obj, basic_symbol_iterator I) {
+ if (isa<ELFObjectFileBase>(&Obj)) {
+ elf_symbol_iterator SymI(I);
+ return SymI->getELFTypeName();
+ }
+ return "";
+}
+
+// Return Posix nm class type tag (single letter), but also set SecName and
+// section and name, to be used in format=sysv output.
+static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I,
+ StringRef &SecName) {
+ // Symbol Flags have been checked in the caller.
+ uint32_t Symflags = cantFail(I->getFlags());
+ if (ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj)) {
+ if (Symflags & object::SymbolRef::SF_Absolute)
+ SecName = "*ABS*";
+ else if (Symflags & object::SymbolRef::SF_Common)
+ SecName = "*COM*";
+ else if (Symflags & object::SymbolRef::SF_Undefined)
+ SecName = "*UND*";
+ else {
+ elf_symbol_iterator SymI(I);
+ Expected<elf_section_iterator> SecIOrErr = SymI->getSection();
+ if (!SecIOrErr) {
+ consumeError(SecIOrErr.takeError());
+ return '?';
+ }
+
+ if (*SecIOrErr == ELFObj->section_end())
+ return '?';
+
+ Expected<StringRef> NameOrErr = (*SecIOrErr)->getName();
+ if (!NameOrErr) {
+ consumeError(NameOrErr.takeError());
+ return '?';
+ }
+ SecName = *NameOrErr;
+ }
+ }
+
+ if (Symflags & object::SymbolRef::SF_Undefined) {
+ if (isa<MachOObjectFile>(Obj) || !(Symflags & object::SymbolRef::SF_Weak))
+ return 'U';
+ return isObject(Obj, I) ? 'v' : 'w';
+ }
+ if (isa<ELFObjectFileBase>(&Obj))
+ if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC)
+ return 'i';
+ if (!isa<MachOObjectFile>(Obj) && (Symflags & object::SymbolRef::SF_Weak))
+ return isObject(Obj, I) ? 'V' : 'W';
+
+ if (Symflags & object::SymbolRef::SF_Common)
+ return 'C';
+
+ char Ret = '?';
+ if (Symflags & object::SymbolRef::SF_Absolute)
+ Ret = 'a';
+ else if (IRObjectFile *IR = dyn_cast<IRObjectFile>(&Obj))
+ Ret = getSymbolNMTypeChar(*IR, I);
+ else if (COFFObjectFile *COFF = dyn_cast<COFFObjectFile>(&Obj))
+ Ret = getSymbolNMTypeChar(*COFF, I);
+ else if (XCOFFObjectFile *XCOFF = dyn_cast<XCOFFObjectFile>(&Obj))
+ Ret = getSymbolNMTypeChar(*XCOFF, I);
+ else if (COFFImportFile *COFFImport = dyn_cast<COFFImportFile>(&Obj))
+ Ret = getSymbolNMTypeChar(*COFFImport);
+ else if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj))
+ Ret = getSymbolNMTypeChar(*MachO, I);
+ else if (WasmObjectFile *Wasm = dyn_cast<WasmObjectFile>(&Obj))
+ Ret = getSymbolNMTypeChar(*Wasm, I);
+ else if (TapiFile *Tapi = dyn_cast<TapiFile>(&Obj))
+ Ret = getSymbolNMTypeChar(*Tapi, I);
+ else if (ELFObjectFileBase *ELF = dyn_cast<ELFObjectFileBase>(&Obj)) {
+ Ret = getSymbolNMTypeChar(*ELF, I);
+ if (ELFSymbolRef(*I).getBinding() == ELF::STB_GNU_UNIQUE)
+ return Ret;
+ } else
+ llvm_unreachable("unknown binary format");
+
+ if (!(Symflags & object::SymbolRef::SF_Global))
+ return Ret;
+
+ return toupper(Ret);
+}
+
+// getNsectForSegSect() is used to implement the Mach-O "-s segname sectname"
+// option to dump only those symbols from that section in a Mach-O file.
+// It is called once for each Mach-O file from dumpSymbolNamesFromObject()
+// to get the section number for that named section from the command line
+// arguments. It returns the section number for that section in the Mach-O
+// file or zero it is not present.
+static unsigned getNsectForSegSect(MachOObjectFile *Obj) {
+ unsigned Nsect = 1;
+ for (auto &S : Obj->sections()) {
+ DataRefImpl Ref = S.getRawDataRefImpl();
+ StringRef SectionName;
+ if (Expected<StringRef> NameOrErr = Obj->getSectionName(Ref))
+ SectionName = *NameOrErr;
+ StringRef SegmentName = Obj->getSectionFinalSegmentName(Ref);
+ if (SegmentName == SegSect[0] && SectionName == SegSect[1])
+ return Nsect;
+ Nsect++;
+ }
+ return 0;
+}
+
+// getNsectInMachO() is used to implement the Mach-O "-s segname sectname"
+// option to dump only those symbols from that section in a Mach-O file.
+// It is called once for each symbol in a Mach-O file from
+// dumpSymbolNamesFromObject() and returns the section number for that symbol
+// if it is in a section, else it returns 0.
+static unsigned getNsectInMachO(MachOObjectFile &Obj, BasicSymbolRef Sym) {
+ DataRefImpl Symb = Sym.getRawDataRefImpl();
+ if (Obj.is64Bit()) {
+ MachO::nlist_64 STE = Obj.getSymbol64TableEntry(Symb);
+ return (STE.n_type & MachO::N_TYPE) == MachO::N_SECT ? STE.n_sect : 0;
+ }
+ MachO::nlist STE = Obj.getSymbolTableEntry(Symb);
+ return (STE.n_type & MachO::N_TYPE) == MachO::N_SECT ? STE.n_sect : 0;
+}
+
+static void dumpSymbolsFromDLInfoMachO(MachOObjectFile &MachO) {
+ size_t I = SymbolList.size();
+ std::string ExportsNameBuffer;
+ raw_string_ostream EOS(ExportsNameBuffer);
+ std::string BindsNameBuffer;
+ raw_string_ostream BOS(BindsNameBuffer);
+ std::string LazysNameBuffer;
+ raw_string_ostream LOS(LazysNameBuffer);
+ std::string WeaksNameBuffer;
+ raw_string_ostream WOS(WeaksNameBuffer);
+ std::string FunctionStartsNameBuffer;
+ raw_string_ostream FOS(FunctionStartsNameBuffer);
+
+ MachO::mach_header H;
+ MachO::mach_header_64 H_64;
+ uint32_t HFlags = 0;
+ if (MachO.is64Bit()) {
+ H_64 = MachO.MachOObjectFile::getHeader64();
+ HFlags = H_64.flags;
+ } else {
+ H = MachO.MachOObjectFile::getHeader();
+ HFlags = H.flags;
+ }
+ uint64_t BaseSegmentAddress = 0;
+ for (const auto &Command : MachO.load_commands()) {
+ if (Command.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command Seg = MachO.getSegmentLoadCommand(Command);
+ if (Seg.fileoff == 0 && Seg.filesize != 0) {
+ BaseSegmentAddress = Seg.vmaddr;
+ break;
+ }
+ } else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = MachO.getSegment64LoadCommand(Command);
+ if (Seg.fileoff == 0 && Seg.filesize != 0) {
+ BaseSegmentAddress = Seg.vmaddr;
+ break;
+ }
+ }
+ }
+ if (DyldInfoOnly || AddDyldInfo ||
+ HFlags & MachO::MH_NLIST_OUTOFSYNC_WITH_DYLDINFO) {
+ unsigned ExportsAdded = 0;
+ Error Err = Error::success();
+ for (const llvm::object::ExportEntry &Entry : MachO.exports(Err)) {
+ bool found = false;
+ bool ReExport = false;
+ if (!DyldInfoOnly) {
+ for (const NMSymbol &S : SymbolList)
+ if (S.Address == Entry.address() + BaseSegmentAddress &&
+ S.Name == Entry.name()) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ NMSymbol S = {};
+ S.Address = Entry.address() + BaseSegmentAddress;
+ S.Size = 0;
+ S.TypeChar = '\0';
+ S.Name = Entry.name().str();
+ // There is no symbol in the nlist symbol table for this so we set
+ // Sym effectivly to null and the rest of code in here must test for
+ // it and not do things like Sym.getFlags() for it.
+ S.Sym = BasicSymbolRef();
+ S.SymFlags = SymbolRef::SF_Global;
+ S.Section = SectionRef();
+ S.NType = 0;
+ S.NSect = 0;
+ S.NDesc = 0;
+
+ uint64_t EFlags = Entry.flags();
+ bool Abs = ((EFlags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) ==
+ MachO::EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE);
+ bool Resolver = (EFlags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER);
+ ReExport = (EFlags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT);
+ bool WeakDef = (EFlags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
+ if (WeakDef)
+ S.NDesc |= MachO::N_WEAK_DEF;
+ if (Abs) {
+ S.NType = MachO::N_EXT | MachO::N_ABS;
+ S.TypeChar = 'A';
+ } else if (ReExport) {
+ S.NType = MachO::N_EXT | MachO::N_INDR;
+ S.TypeChar = 'I';
+ } else {
+ S.NType = MachO::N_EXT | MachO::N_SECT;
+ if (Resolver) {
+ S.Address = Entry.other() + BaseSegmentAddress;
+ if ((S.Address & 1) != 0 && !MachO.is64Bit() &&
+ H.cputype == MachO::CPU_TYPE_ARM) {
+ S.Address &= ~1LL;
+ S.NDesc |= MachO::N_ARM_THUMB_DEF;
+ }
+ } else {
+ S.Address = Entry.address() + BaseSegmentAddress;
+ }
+ StringRef SegmentName = StringRef();
+ StringRef SectionName = StringRef();
+ for (const SectionRef &Section : MachO.sections()) {
+ S.NSect++;
+
+ if (Expected<StringRef> NameOrErr = Section.getName())
+ SectionName = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ SegmentName =
+ MachO.getSectionFinalSegmentName(Section.getRawDataRefImpl());
+ if (S.Address >= Section.getAddress() &&
+ S.Address < Section.getAddress() + Section.getSize()) {
+ S.Section = Section;
+ break;
+ } else if (Entry.name() == "__mh_execute_header" &&
+ SegmentName == "__TEXT" && SectionName == "__text") {
+ S.Section = Section;
+ S.NDesc |= MachO::REFERENCED_DYNAMICALLY;
+ break;
+ }
+ }
+ if (SegmentName == "__TEXT" && SectionName == "__text")
+ S.TypeChar = 'T';
+ else if (SegmentName == "__DATA" && SectionName == "__data")
+ S.TypeChar = 'D';
+ else if (SegmentName == "__DATA" && SectionName == "__bss")
+ S.TypeChar = 'B';
+ else
+ S.TypeChar = 'S';
+ }
+ SymbolList.push_back(S);
+
+ EOS << Entry.name();
+ EOS << '\0';
+ ExportsAdded++;
+
+ // For ReExports there are a two more things to do, first add the
+ // indirect name and second create the undefined symbol using the
+ // referened dynamic library.
+ if (ReExport) {
+
+ // Add the indirect name.
+ if (Entry.otherName().empty())
+ EOS << Entry.name();
+ else
+ EOS << Entry.otherName();
+ EOS << '\0';
+
+ // Now create the undefined symbol using the referened dynamic
+ // library.
+ NMSymbol U = {};
+ U.Address = 0;
+ U.Size = 0;
+ U.TypeChar = 'U';
+ if (Entry.otherName().empty())
+ U.Name = Entry.name().str();
+ else
+ U.Name = Entry.otherName().str();
+ // Again there is no symbol in the nlist symbol table for this so
+ // we set Sym effectivly to null and the rest of code in here must
+ // test for it and not do things like Sym.getFlags() for it.
+ U.Sym = BasicSymbolRef();
+ U.SymFlags = SymbolRef::SF_Global | SymbolRef::SF_Undefined;
+ U.Section = SectionRef();
+ U.NType = MachO::N_EXT | MachO::N_UNDF;
+ U.NSect = 0;
+ U.NDesc = 0;
+ // The library ordinal for this undefined symbol is in the export
+ // trie Entry.other().
+ MachO::SET_LIBRARY_ORDINAL(U.NDesc, Entry.other());
+ SymbolList.push_back(U);
+
+ // Finally add the undefined symbol's name.
+ if (Entry.otherName().empty())
+ EOS << Entry.name();
+ else
+ EOS << Entry.otherName();
+ EOS << '\0';
+ ExportsAdded++;
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), MachO.getFileName());
+ // Set the symbol names and indirect names for the added symbols.
+ if (ExportsAdded) {
+ EOS.flush();
+ const char *Q = ExportsNameBuffer.c_str();
+ for (unsigned K = 0; K < ExportsAdded; K++) {
+ SymbolList[I].Name = Q;
+ Q += strlen(Q) + 1;
+ if (SymbolList[I].TypeChar == 'I') {
+ SymbolList[I].IndirectName = Q;
+ Q += strlen(Q) + 1;
+ }
+ I++;
+ }
+ }
+
+ // Add the undefined symbols from the bind entries.
+ unsigned BindsAdded = 0;
+ Error BErr = Error::success();
+ StringRef LastSymbolName = StringRef();
+ for (const llvm::object::MachOBindEntry &Entry : MachO.bindTable(BErr)) {
+ bool found = false;
+ if (LastSymbolName == Entry.symbolName())
+ found = true;
+ else if (!DyldInfoOnly) {
+ for (unsigned J = 0; J < SymbolList.size() && !found; ++J) {
+ if (SymbolList[J].Name == Entry.symbolName())
+ found = true;
+ }
+ }
+ if (!found) {
+ LastSymbolName = Entry.symbolName();
+ NMSymbol B = {};
+ B.Address = 0;
+ B.Size = 0;
+ B.TypeChar = 'U';
+ // There is no symbol in the nlist symbol table for this so we set
+ // Sym effectivly to null and the rest of code in here must test for
+ // it and not do things like Sym.getFlags() for it.
+ B.Sym = BasicSymbolRef();
+ B.SymFlags = SymbolRef::SF_Global | SymbolRef::SF_Undefined;
+ B.NType = MachO::N_EXT | MachO::N_UNDF;
+ B.NSect = 0;
+ B.NDesc = 0;
+ MachO::SET_LIBRARY_ORDINAL(B.NDesc, Entry.ordinal());
+ B.Name = Entry.symbolName().str();
+ SymbolList.push_back(B);
+ BOS << Entry.symbolName();
+ BOS << '\0';
+ BindsAdded++;
+ }
+ }
+ if (BErr)
+ error(std::move(BErr), MachO.getFileName());
+ // Set the symbol names and indirect names for the added symbols.
+ if (BindsAdded) {
+ BOS.flush();
+ const char *Q = BindsNameBuffer.c_str();
+ for (unsigned K = 0; K < BindsAdded; K++) {
+ SymbolList[I].Name = Q;
+ Q += strlen(Q) + 1;
+ if (SymbolList[I].TypeChar == 'I') {
+ SymbolList[I].IndirectName = Q;
+ Q += strlen(Q) + 1;
+ }
+ I++;
+ }
+ }
+
+ // Add the undefined symbols from the lazy bind entries.
+ unsigned LazysAdded = 0;
+ Error LErr = Error::success();
+ LastSymbolName = StringRef();
+ for (const llvm::object::MachOBindEntry &Entry :
+ MachO.lazyBindTable(LErr)) {
+ bool found = false;
+ if (LastSymbolName == Entry.symbolName())
+ found = true;
+ else {
+ // Here we must check to see it this symbol is already in the
+ // SymbolList as it might have already have been added above via a
+ // non-lazy (bind) entry.
+ for (unsigned J = 0; J < SymbolList.size() && !found; ++J) {
+ if (SymbolList[J].Name == Entry.symbolName())
+ found = true;
+ }
+ }
+ if (!found) {
+ LastSymbolName = Entry.symbolName();
+ NMSymbol L = {};
+ L.Name = Entry.symbolName().str();
+ L.Address = 0;
+ L.Size = 0;
+ L.TypeChar = 'U';
+ // There is no symbol in the nlist symbol table for this so we set
+ // Sym effectivly to null and the rest of code in here must test for
+ // it and not do things like Sym.getFlags() for it.
+ L.Sym = BasicSymbolRef();
+ L.SymFlags = SymbolRef::SF_Global | SymbolRef::SF_Undefined;
+ L.NType = MachO::N_EXT | MachO::N_UNDF;
+ L.NSect = 0;
+ // The REFERENCE_FLAG_UNDEFINED_LAZY is no longer used but here it
+ // makes sence since we are creating this from a lazy bind entry.
+ L.NDesc = MachO::REFERENCE_FLAG_UNDEFINED_LAZY;
+ MachO::SET_LIBRARY_ORDINAL(L.NDesc, Entry.ordinal());
+ SymbolList.push_back(L);
+ LOS << Entry.symbolName();
+ LOS << '\0';
+ LazysAdded++;
+ }
+ }
+ if (LErr)
+ error(std::move(LErr), MachO.getFileName());
+ // Set the symbol names and indirect names for the added symbols.
+ if (LazysAdded) {
+ LOS.flush();
+ const char *Q = LazysNameBuffer.c_str();
+ for (unsigned K = 0; K < LazysAdded; K++) {
+ SymbolList[I].Name = Q;
+ Q += strlen(Q) + 1;
+ if (SymbolList[I].TypeChar == 'I') {
+ SymbolList[I].IndirectName = Q;
+ Q += strlen(Q) + 1;
+ }
+ I++;
+ }
+ }
+
+ // Add the undefineds symbol from the weak bind entries which are not
+ // strong symbols.
+ unsigned WeaksAdded = 0;
+ Error WErr = Error::success();
+ LastSymbolName = StringRef();
+ for (const llvm::object::MachOBindEntry &Entry :
+ MachO.weakBindTable(WErr)) {
+ bool found = false;
+ unsigned J = 0;
+ if (LastSymbolName == Entry.symbolName() ||
+ Entry.flags() & MachO::BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) {
+ found = true;
+ } else {
+ for (J = 0; J < SymbolList.size() && !found; ++J) {
+ if (SymbolList[J].Name == Entry.symbolName()) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ LastSymbolName = Entry.symbolName();
+ NMSymbol W = {};
+ W.Name = Entry.symbolName().str();
+ W.Address = 0;
+ W.Size = 0;
+ W.TypeChar = 'U';
+ // There is no symbol in the nlist symbol table for this so we set
+ // Sym effectivly to null and the rest of code in here must test for
+ // it and not do things like Sym.getFlags() for it.
+ W.Sym = BasicSymbolRef();
+ W.SymFlags = SymbolRef::SF_Global | SymbolRef::SF_Undefined;
+ W.NType = MachO::N_EXT | MachO::N_UNDF;
+ W.NSect = 0;
+ // Odd that we are using N_WEAK_DEF on an undefined symbol but that is
+ // what is created in this case by the linker when there are real
+ // symbols in the nlist structs.
+ W.NDesc = MachO::N_WEAK_DEF;
+ SymbolList.push_back(W);
+ WOS << Entry.symbolName();
+ WOS << '\0';
+ WeaksAdded++;
+ } else {
+ // This is the case the symbol was previously been found and it could
+ // have been added from a bind or lazy bind symbol. If so and not
+ // a definition also mark it as weak.
+ if (SymbolList[J].TypeChar == 'U')
+ // See comment above about N_WEAK_DEF.
+ SymbolList[J].NDesc |= MachO::N_WEAK_DEF;
+ }
+ }
+ if (WErr)
+ error(std::move(WErr), MachO.getFileName());
+ // Set the symbol names and indirect names for the added symbols.
+ if (WeaksAdded) {
+ WOS.flush();
+ const char *Q = WeaksNameBuffer.c_str();
+ for (unsigned K = 0; K < WeaksAdded; K++) {
+ SymbolList[I].Name = Q;
+ Q += strlen(Q) + 1;
+ if (SymbolList[I].TypeChar == 'I') {
+ SymbolList[I].IndirectName = Q;
+ Q += strlen(Q) + 1;
+ }
+ I++;
+ }
+ }
+
+ // Trying adding symbol from the function starts table and LC_MAIN entry
+ // point.
+ SmallVector<uint64_t, 8> FoundFns;
+ 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
+ // consumption.
+ MachO::linkedit_data_command LLC =
+ MachO.getLinkeditDataLoadCommand(Command);
+
+ MachO.ReadULEB128s(LLC.dataoff, FoundFns);
+ } else if (Command.C.cmd == MachO::LC_MAIN) {
+ MachO::entry_point_command LCmain = MachO.getEntryPointCommand(Command);
+ lc_main_offset = LCmain.entryoff;
+ }
+ }
+ // See if these addresses are already in the symbol table.
+ unsigned FunctionStartsAdded = 0;
+ for (uint64_t f = 0; f < FoundFns.size(); f++) {
+ bool found = false;
+ for (unsigned J = 0; J < SymbolList.size() && !found; ++J) {
+ if (SymbolList[J].Address == FoundFns[f] + BaseSegmentAddress)
+ found = true;
+ }
+ // See this address is not already in the symbol table fake up an
+ // nlist for it.
+ if (!found) {
+ NMSymbol F = {};
+ F.Name = "<redacted function X>";
+ F.Address = FoundFns[f] + BaseSegmentAddress;
+ F.Size = 0;
+ // There is no symbol in the nlist symbol table for this so we set
+ // Sym effectivly to null and the rest of code in here must test for
+ // it and not do things like Sym.getFlags() for it.
+ F.Sym = BasicSymbolRef();
+ F.SymFlags = 0;
+ F.NType = MachO::N_SECT;
+ F.NSect = 0;
+ StringRef SegmentName = StringRef();
+ StringRef SectionName = StringRef();
+ for (const SectionRef &Section : MachO.sections()) {
+ if (Expected<StringRef> NameOrErr = Section.getName())
+ SectionName = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ SegmentName =
+ MachO.getSectionFinalSegmentName(Section.getRawDataRefImpl());
+ F.NSect++;
+ if (F.Address >= Section.getAddress() &&
+ F.Address < Section.getAddress() + Section.getSize()) {
+ F.Section = Section;
+ break;
+ }
+ }
+ if (SegmentName == "__TEXT" && SectionName == "__text")
+ F.TypeChar = 't';
+ else if (SegmentName == "__DATA" && SectionName == "__data")
+ F.TypeChar = 'd';
+ else if (SegmentName == "__DATA" && SectionName == "__bss")
+ F.TypeChar = 'b';
+ else
+ F.TypeChar = 's';
+ F.NDesc = 0;
+ SymbolList.push_back(F);
+ if (FoundFns[f] == lc_main_offset)
+ FOS << "<redacted LC_MAIN>";
+ else
+ FOS << "<redacted function " << f << ">";
+ FOS << '\0';
+ FunctionStartsAdded++;
+ }
+ }
+ if (FunctionStartsAdded) {
+ FOS.flush();
+ const char *Q = FunctionStartsNameBuffer.c_str();
+ for (unsigned K = 0; K < FunctionStartsAdded; K++) {
+ SymbolList[I].Name = Q;
+ Q += strlen(Q) + 1;
+ if (SymbolList[I].TypeChar == 'I') {
+ SymbolList[I].IndirectName = Q;
+ Q += strlen(Q) + 1;
+ }
+ I++;
+ }
+ }
+ }
+}
+
+static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
+ StringRef ArchiveName = {},
+ StringRef ArchitectureName = {}) {
+ auto Symbols = Obj.symbols();
+ std::vector<VersionEntry> SymbolVersions;
+ if (DynamicSyms) {
+ const auto *E = dyn_cast<ELFObjectFileBase>(&Obj);
+ if (!E) {
+ error("File format has no dynamic symbol table", Obj.getFileName());
+ return;
+ }
+ Symbols = E->getDynamicSymbolIterators();
+
+ if (Expected<std::vector<VersionEntry>> VersionsOrErr =
+ E->readDynsymVersions())
+ SymbolVersions = std::move(*VersionsOrErr);
+ else
+ WithColor::warning(errs(), ToolName)
+ << "unable to read symbol versions: "
+ << toString(VersionsOrErr.takeError()) << "\n";
+ }
+
+ // If a "-s segname sectname" option was specified and this is a Mach-O
+ // file get the section number for that section in this object file.
+ unsigned int Nsect = 0;
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj);
+ if (!SegSect.empty() && MachO) {
+ Nsect = getNsectForSegSect(MachO);
+ // If this section is not in the object file no symbols are printed.
+ if (Nsect == 0)
+ return;
+ }
+ if (!(MachO && DyldInfoOnly)) {
+ size_t I = -1;
+ for (BasicSymbolRef Sym : Symbols) {
+ ++I;
+ Expected<uint32_t> SymFlagsOrErr = Sym.getFlags();
+ if (!SymFlagsOrErr) {
+ error(SymFlagsOrErr.takeError(), Obj.getFileName());
+ return;
+ }
+
+ // Don't drop format specifc symbols for ARM and AArch64 ELF targets, they
+ // are used to repesent mapping symbols and needed to honor the
+ // --special-syms option.
+ auto *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj);
+ if ((!ELFObj || (ELFObj->getEMachine() != ELF::EM_ARM &&
+ ELFObj->getEMachine() != ELF::EM_AARCH64)) &&
+ !DebugSyms && (*SymFlagsOrErr & SymbolRef::SF_FormatSpecific))
+ continue;
+ if (WithoutAliases && (*SymFlagsOrErr & SymbolRef::SF_Indirect))
+ continue;
+ // If a "-s segname sectname" option was specified and this is a Mach-O
+ // file and this section appears in this file, Nsect will be non-zero then
+ // see if this symbol is a symbol from that section and if not skip it.
+ if (Nsect && Nsect != getNsectInMachO(*MachO, Sym))
+ continue;
+ NMSymbol S = {};
+ S.Size = 0;
+ S.Address = 0;
+ if (isa<ELFObjectFileBase>(&Obj))
+ S.Size = ELFSymbolRef(Sym).getSize();
+
+ if (const XCOFFObjectFile *XCOFFObj =
+ dyn_cast<const XCOFFObjectFile>(&Obj))
+ S.Size = XCOFFObj->getSymbolSize(Sym.getRawDataRefImpl());
+
+ if (PrintAddress && isa<ObjectFile>(Obj)) {
+ SymbolRef SymRef(Sym);
+ Expected<uint64_t> AddressOrErr = SymRef.getAddress();
+ if (!AddressOrErr) {
+ consumeError(AddressOrErr.takeError());
+ break;
+ }
+ S.Address = *AddressOrErr;
+ }
+ S.TypeName = getNMTypeName(Obj, Sym);
+ S.TypeChar = getNMSectionTagAndName(Obj, Sym, S.SectionName);
+
+ raw_string_ostream OS(S.Name);
+ if (Error E = Sym.printName(OS)) {
+ if (MachO) {
+ OS << "bad string index";
+ consumeError(std::move(E));
+ } else
+ error(std::move(E), Obj.getFileName());
+ }
+ if (!SymbolVersions.empty() && !SymbolVersions[I].Name.empty())
+ S.Name +=
+ (SymbolVersions[I].IsVerDef ? "@@" : "@") + SymbolVersions[I].Name;
+
+ S.Sym = Sym;
+ SymbolList.push_back(S);
+ }
+ }
+
+ // If this is a Mach-O file where the nlist symbol table is out of sync
+ // with the dyld export trie then look through exports and fake up symbols
+ // for the ones that are missing (also done with the -add-dyldinfo flag).
+ // This is needed if strip(1) -T is run on a binary containing swift
+ // language symbols for example. The option -only-dyldinfo will fake up
+ // all symbols from the dyld export trie as well as the bind info.
+ if (MachO && !NoDyldInfo)
+ dumpSymbolsFromDLInfoMachO(*MachO);
+
+ CurrentFilename = Obj.getFileName();
+
+ if (Symbols.empty() && SymbolList.empty() && !Quiet) {
+ writeFileName(errs(), ArchiveName, ArchitectureName);
+ errs() << "no symbols\n";
+ }
+
+ sortAndPrintSymbolList(Obj, printName, ArchiveName, ArchitectureName);
+}
+
+// checkMachOAndArchFlags() checks to see if the SymbolicFile is a Mach-O file
+// and if it is and there is a list of architecture flags is specified then
+// check to make sure this Mach-O file is one of those architectures or all
+// architectures was specificed. If not then an error is generated and this
+// routine returns false. Else it returns true.
+static bool checkMachOAndArchFlags(SymbolicFile *O, std::string &Filename) {
+ auto *MachO = dyn_cast<MachOObjectFile>(O);
+
+ if (!MachO || ArchAll || ArchFlags.empty())
+ return true;
+
+ MachO::mach_header H;
+ MachO::mach_header_64 H_64;
+ Triple T;
+ const char *McpuDefault, *ArchFlag;
+ if (MachO->is64Bit()) {
+ H_64 = MachO->MachOObjectFile::getHeader64();
+ T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype,
+ &McpuDefault, &ArchFlag);
+ } else {
+ H = MachO->MachOObjectFile::getHeader();
+ T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype,
+ &McpuDefault, &ArchFlag);
+ }
+ const std::string ArchFlagName(ArchFlag);
+ if (!llvm::is_contained(ArchFlags, ArchFlagName)) {
+ error("No architecture specified", Filename);
+ return false;
+ }
+ return true;
+}
+
+static void dumpSymbolNamesFromFile(std::string &Filename) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+ MemoryBuffer::getFileOrSTDIN(Filename);
+ if (error(BufferOrErr.getError(), Filename))
+ return;
+
+ LLVMContext Context;
+ LLVMContext *ContextPtr = NoLLVMBitcode ? nullptr : &Context;
+ Expected<std::unique_ptr<Binary>> BinaryOrErr =
+ createBinary(BufferOrErr.get()->getMemBufferRef(), ContextPtr);
+ if (!BinaryOrErr) {
+ error(BinaryOrErr.takeError(), Filename);
+ return;
+ }
+ Binary &Bin = *BinaryOrErr.get();
+
+ if (Archive *A = dyn_cast<Archive>(&Bin)) {
+ if (ArchiveMap) {
+ Archive::symbol_iterator I = A->symbol_begin();
+ Archive::symbol_iterator E = A->symbol_end();
+ if (I != E) {
+ outs() << "Archive map\n";
+ for (; I != E; ++I) {
+ Expected<Archive::Child> C = I->getMember();
+ if (!C) {
+ error(C.takeError(), Filename);
+ break;
+ }
+ Expected<StringRef> FileNameOrErr = C->getName();
+ if (!FileNameOrErr) {
+ error(FileNameOrErr.takeError(), Filename);
+ break;
+ }
+ StringRef SymName = I->getName();
+ outs() << SymName << " in " << FileNameOrErr.get() << "\n";
+ }
+ outs() << "\n";
+ }
+ }
+
+ {
+ Error Err = Error::success();
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr =
+ C.getAsBinary(ContextPtr);
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ error(std::move(E), Filename, C);
+ continue;
+ }
+ if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
+ if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(O)) {
+ WithColor::warning(errs(), ToolName)
+ << "sizes with -print-size for Mach-O files are always zero.\n";
+ MachOPrintSizeWarning = true;
+ }
+ if (!checkMachOAndArchFlags(O, Filename))
+ return;
+ if (!PrintFileName) {
+ outs() << "\n";
+ if (isa<MachOObjectFile>(O)) {
+ outs() << Filename << "(" << O->getFileName() << ")";
+ } else
+ outs() << O->getFileName();
+ outs() << ":\n";
+ }
+ dumpSymbolNamesFromObject(*O, false, Filename);
+ }
+ }
+ if (Err)
+ error(std::move(Err), A->getFileName());
+ }
+ return;
+ }
+ if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) {
+ // 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 (ArchFlags[i] == I->getArchFlagName()) {
+ ArchFound = true;
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
+ I->getAsObjectFile();
+ std::string ArchiveName;
+ std::string ArchitectureName;
+ ArchiveName.clear();
+ ArchitectureName.clear();
+ if (ObjOrErr) {
+ ObjectFile &Obj = *ObjOrErr.get();
+ if (ArchFlags.size() > 1) {
+ if (PrintFileName)
+ ArchitectureName = I->getArchFlagName();
+ else
+ outs() << "\n" << Obj.getFileName() << " (for architecture "
+ << I->getArchFlagName() << ")"
+ << ":\n";
+ }
+ dumpSymbolNamesFromObject(Obj, false, ArchiveName,
+ ArchitectureName);
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ error(std::move(E), Filename, ArchFlags.size() > 1 ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ continue;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &A = *AOrErr;
+ Error Err = Error::success();
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr =
+ C.getAsBinary(ContextPtr);
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError())) {
+ error(std::move(E), Filename, C, ArchFlags.size() > 1 ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ }
+ continue;
+ }
+ if (SymbolicFile *O =
+ dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
+ if (PrintFileName) {
+ ArchiveName = std::string(A->getFileName());
+ if (ArchFlags.size() > 1)
+ ArchitectureName = I->getArchFlagName();
+ } else {
+ outs() << "\n" << A->getFileName();
+ outs() << "(" << O->getFileName() << ")";
+ if (ArchFlags.size() > 1) {
+ outs() << " (for architecture " << I->getArchFlagName()
+ << ")";
+ }
+ outs() << ":\n";
+ }
+ dumpSymbolNamesFromObject(*O, false, ArchiveName,
+ ArchitectureName);
+ }
+ }
+ if (Err)
+ error(std::move(Err), A->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error(Filename + " for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a Mach-O file or an archive file",
+ "Mach-O universal file");
+ }
+ }
+ }
+ if (!ArchFound) {
+ error(ArchFlags[i],
+ "file: " + Filename + " does not contain architecture");
+ return;
+ }
+ }
+ return;
+ }
+ // No architecture flags were specified so if this contains a slice that
+ // matches the host architecture dump only that.
+ if (!ArchAll) {
+ 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;
+ if (ObjOrErr) {
+ ObjectFile &Obj = *ObjOrErr.get();
+ dumpSymbolNamesFromObject(Obj, false);
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ error(std::move(E), Filename);
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &A = *AOrErr;
+ Error Err = Error::success();
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr =
+ C.getAsBinary(ContextPtr);
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), Filename, C);
+ continue;
+ }
+ if (SymbolicFile *O =
+ dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
+ if (PrintFileName)
+ ArchiveName = std::string(A->getFileName());
+ else
+ outs() << "\n" << A->getFileName() << "(" << O->getFileName()
+ << ")"
+ << ":\n";
+ dumpSymbolNamesFromObject(*O, false, ArchiveName);
+ }
+ }
+ if (Err)
+ error(std::move(Err), A->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error(Filename + " for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a Mach-O file or an archive file",
+ "Mach-O universal file");
+ }
+ 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 (const MachOUniversalBinary::ObjectForArch &O : UB->objects()) {
+ Expected<std::unique_ptr<ObjectFile>> ObjOrErr = O.getAsObjectFile();
+ std::string ArchiveName;
+ std::string ArchitectureName;
+ ArchiveName.clear();
+ ArchitectureName.clear();
+ if (ObjOrErr) {
+ ObjectFile &Obj = *ObjOrErr.get();
+ if (PrintFileName) {
+ if (isa<MachOObjectFile>(Obj) && moreThanOneArch)
+ ArchitectureName = O.getArchFlagName();
+ } else {
+ if (moreThanOneArch)
+ outs() << "\n";
+ outs() << Obj.getFileName();
+ if (isa<MachOObjectFile>(Obj) && moreThanOneArch)
+ outs() << " (for architecture " << O.getArchFlagName() << ")";
+ outs() << ":\n";
+ }
+ dumpSymbolNamesFromObject(Obj, false, ArchiveName, ArchitectureName);
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ error(std::move(E), Filename, moreThanOneArch ?
+ StringRef(O.getArchFlagName()) : StringRef());
+ continue;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ O.getAsArchive()) {
+ std::unique_ptr<Archive> &A = *AOrErr;
+ Error Err = Error::success();
+ for (auto &C : A->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr =
+ C.getAsBinary(ContextPtr);
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), Filename, C, moreThanOneArch ?
+ StringRef(ArchitectureName) : StringRef());
+ continue;
+ }
+ if (SymbolicFile *F = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
+ if (PrintFileName) {
+ ArchiveName = std::string(A->getFileName());
+ if (isa<MachOObjectFile>(F) && moreThanOneArch)
+ ArchitectureName = O.getArchFlagName();
+ } else {
+ outs() << "\n" << A->getFileName();
+ if (isa<MachOObjectFile>(F)) {
+ outs() << "(" << F->getFileName() << ")";
+ if (moreThanOneArch)
+ outs() << " (for architecture " << O.getArchFlagName()
+ << ")";
+ } else
+ outs() << ":" << F->getFileName();
+ outs() << ":\n";
+ }
+ dumpSymbolNamesFromObject(*F, false, ArchiveName, ArchitectureName);
+ }
+ }
+ if (Err)
+ error(std::move(Err), A->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error(Filename + " for architecture " +
+ StringRef(O.getArchFlagName()) +
+ " is not a Mach-O file or an archive file",
+ "Mach-O universal file");
+ }
+ }
+ return;
+ }
+
+ if (TapiUniversal *TU = dyn_cast<TapiUniversal>(&Bin)) {
+ for (const TapiUniversal::ObjectForArch &I : TU->objects()) {
+ StringRef ArchName = I.getArchFlagName();
+ const bool ShowArch =
+ ArchFlags.empty() || llvm::is_contained(ArchFlags, ArchName);
+ if (!ShowArch)
+ continue;
+ if (!AddInlinedInfo && !I.isTopLevelLib())
+ continue;
+ if (auto ObjOrErr = I.getAsObjectFile()) {
+ outs() << "\n"
+ << I.getInstallName() << " (for architecture " << ArchName << ")"
+ << ":\n";
+ dumpSymbolNamesFromObject(*ObjOrErr.get(), false, {}, ArchName);
+ } else if (Error E =
+ isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) {
+ error(std::move(E), Filename, ArchName);
+ }
+ }
+
+ return;
+ }
+
+ if (SymbolicFile *O = dyn_cast<SymbolicFile>(&Bin)) {
+ if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(O)) {
+ WithColor::warning(errs(), ToolName)
+ << "sizes with --print-size for Mach-O files are always zero.\n";
+ MachOPrintSizeWarning = true;
+ }
+ if (!checkMachOAndArchFlags(O, Filename))
+ return;
+ dumpSymbolNamesFromObject(*O, true);
+ }
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ NmOptTable Tbl;
+ ToolName = argv[0];
+ opt::InputArgList Args =
+ Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
+ error(Msg);
+ exit(1);
+ });
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(
+ outs(),
+ (Twine(ToolName) + " [options] <input object files>").str().c_str(),
+ "LLVM symbol table dumper");
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ return 0;
+ }
+ if (Args.hasArg(OPT_version)) {
+ // This needs to contain the word "GNU", libtool looks for that string.
+ outs() << "llvm-nm, compatible with GNU nm" << '\n';
+ cl::PrintVersionMessage();
+ return 0;
+ }
+
+ DebugSyms = Args.hasArg(OPT_debug_syms);
+ DefinedOnly = Args.hasArg(OPT_defined_only);
+ Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false);
+ DynamicSyms = Args.hasArg(OPT_dynamic);
+ ExternalOnly = Args.hasArg(OPT_extern_only);
+ StringRef V = Args.getLastArgValue(OPT_format_EQ, "bsd");
+ if (V == "bsd")
+ OutputFormat = bsd;
+ else if (V == "posix")
+ OutputFormat = posix;
+ else if (V == "sysv")
+ OutputFormat = sysv;
+ else if (V == "darwin")
+ OutputFormat = darwin;
+ else if (V == "just-symbols")
+ OutputFormat = just_symbols;
+ else
+ error("--format value should be one of: bsd, posix, sysv, darwin, "
+ "just-symbols");
+ NoLLVMBitcode = Args.hasArg(OPT_no_llvm_bc);
+ NoSort = Args.hasArg(OPT_no_sort);
+ NoWeakSymbols = Args.hasArg(OPT_no_weak);
+ NumericSort = Args.hasArg(OPT_numeric_sort);
+ ArchiveMap = Args.hasArg(OPT_print_armap);
+ PrintFileName = Args.hasArg(OPT_print_file_name);
+ PrintSize = Args.hasArg(OPT_print_size);
+ ReverseSort = Args.hasArg(OPT_reverse_sort);
+ Quiet = Args.hasArg(OPT_quiet);
+ V = Args.getLastArgValue(OPT_radix_EQ, "x");
+ if (V == "o")
+ AddressRadix = Radix::o;
+ else if (V == "d")
+ AddressRadix = Radix::d;
+ else if (V == "x")
+ AddressRadix = Radix::x;
+ else
+ error("--radix value should be one of: 'o' (octal), 'd' (decimal), 'x' "
+ "(hexadecimal)");
+ SizeSort = Args.hasArg(OPT_size_sort);
+ SpecialSyms = Args.hasArg(OPT_special_syms);
+ UndefinedOnly = Args.hasArg(OPT_undefined_only);
+ WithoutAliases = Args.hasArg(OPT_without_aliases);
+
+ // Mach-O specific options.
+ FormatMachOasHex = Args.hasArg(OPT_x);
+ AddDyldInfo = Args.hasArg(OPT_add_dyldinfo);
+ AddInlinedInfo = Args.hasArg(OPT_add_inlinedinfo);
+ DyldInfoOnly = Args.hasArg(OPT_dyldinfo_only);
+ NoDyldInfo = Args.hasArg(OPT_no_dyldinfo);
+
+ // llvm-nm only reads binary files.
+ if (error(sys::ChangeStdinToBinary()))
+ return 1;
+
+ // These calls are needed so that we can read bitcode correctly.
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmParsers();
+
+ // The relative order of these is important. If you pass --size-sort it should
+ // only print out the size. However, if you pass -S --size-sort, it should
+ // print out both the size and address.
+ if (SizeSort && !PrintSize)
+ PrintAddress = false;
+ if (OutputFormat == sysv || SizeSort)
+ PrintSize = true;
+
+ for (const auto *A : Args.filtered(OPT_arch_EQ)) {
+ SmallVector<StringRef, 2> Values;
+ llvm::SplitString(A->getValue(), Values, ",");
+ for (StringRef V : Values) {
+ if (V == "all")
+ ArchAll = true;
+ else if (MachOObjectFile::isValidArch(V))
+ ArchFlags.push_back(V);
+ else
+ error("Unknown architecture named '" + V + "'",
+ "for the --arch option");
+ }
+ }
+
+ // Mach-O takes -s to accept two arguments. We emulate this by iterating over
+ // both OPT_s and OPT_INPUT.
+ std::vector<std::string> InputFilenames;
+ int SegSectArgs = 0;
+ for (opt::Arg *A : Args.filtered(OPT_s, OPT_INPUT)) {
+ if (SegSectArgs > 0) {
+ --SegSectArgs;
+ SegSect.push_back(A->getValue());
+ } else if (A->getOption().matches(OPT_s)) {
+ SegSectArgs = 2;
+ } else {
+ InputFilenames.push_back(A->getValue());
+ }
+ }
+ if (!SegSect.empty() && SegSect.size() != 2)
+ error("bad number of arguments (must be two arguments)",
+ "for the -s option");
+
+ if (InputFilenames.empty())
+ InputFilenames.push_back("a.out");
+ if (InputFilenames.size() > 1)
+ MultipleFiles = true;
+
+ if (NoDyldInfo && (AddDyldInfo || DyldInfoOnly))
+ error("--no-dyldinfo can't be used with --add-dyldinfo or --dyldinfo-only");
+
+ llvm::for_each(InputFilenames, dumpSymbolNamesFromFile);
+
+ if (HadError)
+ return 1;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-nm/ya.make b/contrib/libs/llvm14/tools/llvm-nm/ya.make
new file mode 100644
index 00000000000..04cb9974103
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-nm/ya.make
@@ -0,0 +1,59 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-nm
+ contrib/libs/llvm14/tools/llvm-nm
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-nm.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/BitcodeStripOpts.td b/contrib/libs/llvm14/tools/llvm-objcopy/BitcodeStripOpts.td
new file mode 100644
index 00000000000..cc178164b03
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/BitcodeStripOpts.td
@@ -0,0 +1,24 @@
+//===-- BitcodeStripOpts.td - llvm-bitcode-strip options ---------------*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file describes the command line options of llvm-bitcode-strip.
+//
+//===----------------------------------------------------------------------===//
+
+include "llvm/Option/OptParser.td"
+
+def help : Flag<["--"], "help">;
+
+def h : Flag<["-"], "h">, Alias<help>;
+
+def version : Flag<["--"], "version">,
+ HelpText<"Print the version and exit.">;
+
+def V : Flag<["-"], "V">,
+ Alias<version>,
+ HelpText<"Alias for --version">;
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFConfig.h b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFConfig.h
new file mode 100644
index 00000000000..7bf673fa4af
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFConfig.h
@@ -0,0 +1,27 @@
+//===- COFFConfig.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H
+
+#include "llvm/ADT/Optional.h"
+
+namespace llvm {
+namespace objcopy {
+
+// Coff specific configuration for copying/stripping a single file.
+struct COFFConfig {
+ Optional<unsigned> Subsystem;
+ Optional<unsigned> MajorSubsystemVersion;
+ Optional<unsigned> MinorSubsystemVersion;
+};
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
new file mode 100644
index 00000000000..e0039cd3a67
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -0,0 +1,297 @@
+//===- COFFObjcopy.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "COFFObjcopy.h"
+#include "COFFConfig.h"
+#include "CommonConfig.h"
+#include "Object.h"
+#include "Reader.h"
+#include "Writer.h"
+
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/CRC.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Path.h"
+#include <cassert>
+
+namespace llvm {
+namespace objcopy {
+namespace coff {
+
+using namespace object;
+using namespace COFF;
+
+static bool isDebugSection(const Section &Sec) {
+ return Sec.Name.startswith(".debug");
+}
+
+static uint64_t getNextRVA(const Object &Obj) {
+ if (Obj.getSections().empty())
+ return 0;
+ const Section &Last = Obj.getSections().back();
+ return alignTo(Last.Header.VirtualAddress + Last.Header.VirtualSize,
+ Obj.IsPE ? Obj.PeHeader.SectionAlignment : 1);
+}
+
+static Expected<std::vector<uint8_t>>
+createGnuDebugLinkSectionContents(StringRef File) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> LinkTargetOrErr =
+ MemoryBuffer::getFile(File);
+ if (!LinkTargetOrErr)
+ return createFileError(File, LinkTargetOrErr.getError());
+ auto LinkTarget = std::move(*LinkTargetOrErr);
+ uint32_t CRC32 = llvm::crc32(arrayRefFromStringRef(LinkTarget->getBuffer()));
+
+ StringRef FileName = sys::path::filename(File);
+ size_t CRCPos = alignTo(FileName.size() + 1, 4);
+ std::vector<uint8_t> Data(CRCPos + 4);
+ memcpy(Data.data(), FileName.data(), FileName.size());
+ support::endian::write32le(Data.data() + CRCPos, CRC32);
+ return Data;
+}
+
+// Adds named section with given contents to the object.
+static void addSection(Object &Obj, StringRef Name, ArrayRef<uint8_t> Contents,
+ uint32_t Characteristics) {
+ bool NeedVA = Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ |
+ IMAGE_SCN_MEM_WRITE);
+
+ Section Sec;
+ Sec.setOwnedContents(Contents);
+ Sec.Name = Name;
+ Sec.Header.VirtualSize = NeedVA ? Sec.getContents().size() : 0u;
+ Sec.Header.VirtualAddress = NeedVA ? getNextRVA(Obj) : 0u;
+ Sec.Header.SizeOfRawData =
+ NeedVA ? alignTo(Sec.Header.VirtualSize,
+ Obj.IsPE ? Obj.PeHeader.FileAlignment : 1)
+ : Sec.getContents().size();
+ // Sec.Header.PointerToRawData is filled in by the writer.
+ Sec.Header.PointerToRelocations = 0;
+ Sec.Header.PointerToLinenumbers = 0;
+ // Sec.Header.NumberOfRelocations is filled in by the writer.
+ Sec.Header.NumberOfLinenumbers = 0;
+ Sec.Header.Characteristics = Characteristics;
+
+ Obj.addSections(Sec);
+}
+
+static Error addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) {
+ Expected<std::vector<uint8_t>> Contents =
+ createGnuDebugLinkSectionContents(DebugLinkFile);
+ if (!Contents)
+ return Contents.takeError();
+
+ addSection(Obj, ".gnu_debuglink", *Contents,
+ IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
+ IMAGE_SCN_MEM_DISCARDABLE);
+
+ return Error::success();
+}
+
+static uint32_t flagsToCharacteristics(SectionFlag AllFlags, uint32_t OldChar) {
+ // Need to preserve alignment flags.
+ const uint32_t PreserveMask =
+ IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_ALIGN_4BYTES |
+ IMAGE_SCN_ALIGN_8BYTES | IMAGE_SCN_ALIGN_16BYTES |
+ IMAGE_SCN_ALIGN_32BYTES | IMAGE_SCN_ALIGN_64BYTES |
+ IMAGE_SCN_ALIGN_128BYTES | IMAGE_SCN_ALIGN_256BYTES |
+ IMAGE_SCN_ALIGN_512BYTES | IMAGE_SCN_ALIGN_1024BYTES |
+ IMAGE_SCN_ALIGN_2048BYTES | IMAGE_SCN_ALIGN_4096BYTES |
+ IMAGE_SCN_ALIGN_8192BYTES;
+
+ // Setup new section characteristics based on the flags provided in command
+ // line.
+ uint32_t NewCharacteristics = (OldChar & PreserveMask) | IMAGE_SCN_MEM_READ;
+
+ if ((AllFlags & SectionFlag::SecAlloc) && !(AllFlags & SectionFlag::SecLoad))
+ NewCharacteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA;
+ if (AllFlags & SectionFlag::SecNoload)
+ NewCharacteristics |= IMAGE_SCN_LNK_REMOVE;
+ if (!(AllFlags & SectionFlag::SecReadonly))
+ NewCharacteristics |= IMAGE_SCN_MEM_WRITE;
+ if (AllFlags & SectionFlag::SecDebug)
+ NewCharacteristics |=
+ IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE;
+ if (AllFlags & SectionFlag::SecCode)
+ NewCharacteristics |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE;
+ if (AllFlags & SectionFlag::SecData)
+ NewCharacteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA;
+ if (AllFlags & SectionFlag::SecShare)
+ NewCharacteristics |= IMAGE_SCN_MEM_SHARED;
+ if (AllFlags & SectionFlag::SecExclude)
+ NewCharacteristics |= IMAGE_SCN_LNK_REMOVE;
+
+ return NewCharacteristics;
+}
+
+static Error handleArgs(const CommonConfig &Config,
+ const COFFConfig &COFFConfig, Object &Obj) {
+ // Perform the actual section removals.
+ Obj.removeSections([&Config](const Section &Sec) {
+ // Contrary to --only-keep-debug, --only-section fully removes sections that
+ // aren't mentioned.
+ if (!Config.OnlySection.empty() && !Config.OnlySection.matches(Sec.Name))
+ return true;
+
+ if (Config.StripDebug || Config.StripAll || Config.StripAllGNU ||
+ Config.DiscardMode == DiscardType::All || Config.StripUnneeded) {
+ if (isDebugSection(Sec) &&
+ (Sec.Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0)
+ return true;
+ }
+
+ if (Config.ToRemove.matches(Sec.Name))
+ return true;
+
+ return false;
+ });
+
+ if (Config.OnlyKeepDebug) {
+ // For --only-keep-debug, we keep all other sections, but remove their
+ // content. The VirtualSize field in the section header is kept intact.
+ Obj.truncateSections([](const Section &Sec) {
+ return !isDebugSection(Sec) && Sec.Name != ".buildid" &&
+ ((Sec.Header.Characteristics &
+ (IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA)) != 0);
+ });
+ }
+
+ // StripAll removes all symbols and thus also removes all relocations.
+ if (Config.StripAll || Config.StripAllGNU)
+ for (Section &Sec : Obj.getMutableSections())
+ Sec.Relocs.clear();
+
+ // If we need to do per-symbol removals, initialize the Referenced field.
+ if (Config.StripUnneeded || Config.DiscardMode == DiscardType::All ||
+ !Config.SymbolsToRemove.empty())
+ if (Error E = Obj.markSymbols())
+ return E;
+
+ for (Symbol &Sym : Obj.getMutableSymbols()) {
+ auto I = Config.SymbolsToRename.find(Sym.Name);
+ if (I != Config.SymbolsToRename.end())
+ Sym.Name = I->getValue();
+ }
+
+ auto ToRemove = [&](const Symbol &Sym) -> Expected<bool> {
+ // For StripAll, all relocations have been stripped and we remove all
+ // symbols.
+ if (Config.StripAll || Config.StripAllGNU)
+ return true;
+
+ if (Config.SymbolsToRemove.matches(Sym.Name)) {
+ // Explicitly removing a referenced symbol is an error.
+ if (Sym.Referenced)
+ return createStringError(
+ llvm::errc::invalid_argument,
+ "'" + Config.OutputFilename + "': not stripping symbol '" +
+ Sym.Name.str() + "' because it is named in a relocation");
+ return true;
+ }
+
+ if (!Sym.Referenced) {
+ // With --strip-unneeded, GNU objcopy removes all unreferenced local
+ // symbols, and any unreferenced undefined external.
+ // With --strip-unneeded-symbol we strip only specific unreferenced
+ // local symbol instead of removing all of such.
+ if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC ||
+ Sym.Sym.SectionNumber == 0)
+ if (Config.StripUnneeded ||
+ Config.UnneededSymbolsToRemove.matches(Sym.Name))
+ 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.DiscardMode == DiscardType::All &&
+ Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC &&
+ Sym.Sym.SectionNumber != 0)
+ return true;
+ }
+
+ return false;
+ };
+
+ // Actually do removals of symbols.
+ if (Error Err = Obj.removeSymbols(ToRemove))
+ return Err;
+
+ if (!Config.SetSectionFlags.empty())
+ for (Section &Sec : Obj.getMutableSections()) {
+ const auto It = Config.SetSectionFlags.find(Sec.Name);
+ if (It != Config.SetSectionFlags.end())
+ Sec.Header.Characteristics = flagsToCharacteristics(
+ It->second.NewFlags, Sec.Header.Characteristics);
+ }
+
+ for (const auto &Flag : Config.AddSection) {
+ StringRef SecName, FileName;
+ std::tie(SecName, FileName) = Flag.split("=");
+
+ auto BufOrErr = MemoryBuffer::getFile(FileName);
+ if (!BufOrErr)
+ return createFileError(FileName, errorCodeToError(BufOrErr.getError()));
+ auto Buf = std::move(*BufOrErr);
+
+ uint32_t Characteristics;
+ const auto It = Config.SetSectionFlags.find(SecName);
+ if (It != Config.SetSectionFlags.end())
+ Characteristics = flagsToCharacteristics(It->second.NewFlags, 0);
+ else
+ Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES;
+
+ addSection(
+ Obj, SecName,
+ makeArrayRef(reinterpret_cast<const uint8_t *>(Buf->getBufferStart()),
+ Buf->getBufferSize()),
+ Characteristics);
+ }
+
+ if (!Config.AddGnuDebugLink.empty())
+ if (Error E = addGnuDebugLink(Obj, Config.AddGnuDebugLink))
+ return E;
+
+ if (COFFConfig.Subsystem || COFFConfig.MajorSubsystemVersion ||
+ COFFConfig.MinorSubsystemVersion) {
+ if (!Obj.IsPE)
+ return createStringError(
+ errc::invalid_argument,
+ "'" + Config.OutputFilename +
+ "': unable to set subsystem on a relocatable object file");
+ if (COFFConfig.Subsystem)
+ Obj.PeHeader.Subsystem = *COFFConfig.Subsystem;
+ if (COFFConfig.MajorSubsystemVersion)
+ Obj.PeHeader.MajorSubsystemVersion = *COFFConfig.MajorSubsystemVersion;
+ if (COFFConfig.MinorSubsystemVersion)
+ Obj.PeHeader.MinorSubsystemVersion = *COFFConfig.MinorSubsystemVersion;
+ }
+
+ return Error::success();
+}
+
+Error executeObjcopyOnBinary(const CommonConfig &Config,
+ const COFFConfig &COFFConfig, COFFObjectFile &In,
+ raw_ostream &Out) {
+ COFFReader Reader(In);
+ Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create();
+ if (!ObjOrErr)
+ return createFileError(Config.InputFilename, ObjOrErr.takeError());
+ Object *Obj = ObjOrErr->get();
+ assert(Obj && "Unable to deserialize COFF object");
+ if (Error E = handleArgs(Config, COFFConfig, *Obj))
+ return createFileError(Config.InputFilename, std::move(E));
+ COFFWriter Writer(*Obj, Out);
+ if (Error E = Writer.write())
+ return createFileError(Config.OutputFilename, std::move(E));
+ return Error::success();
+}
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFObjcopy.h b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFObjcopy.h
new file mode 100644
index 00000000000..2c7ccd34653
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/COFFObjcopy.h
@@ -0,0 +1,33 @@
+//===- COFFObjcopy.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H
+#define LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H
+
+namespace llvm {
+class Error;
+class raw_ostream;
+
+namespace object {
+class COFFObjectFile;
+} // end namespace object
+
+namespace objcopy {
+struct CommonConfig;
+struct COFFConfig;
+
+namespace coff {
+
+Error executeObjcopyOnBinary(const CommonConfig &Config, const COFFConfig &,
+ object::COFFObjectFile &In, raw_ostream &Out);
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Object.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Object.cpp
new file mode 100644
index 00000000000..ec2628c7eca
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Object.cpp
@@ -0,0 +1,132 @@
+//===- Object.cpp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Object.h"
+#include "llvm/ADT/DenseSet.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());
+ for (Symbol &Sym : Symbols)
+ SymbolMap[Sym.UniqueId] = &Sym;
+}
+
+const Symbol *Object::findSymbol(size_t UniqueId) const {
+ return SymbolMap.lookup(UniqueId);
+}
+
+Error Object::removeSymbols(
+ function_ref<Expected<bool>(const Symbol &)> ToRemove) {
+ Error Errs = Error::success();
+ llvm::erase_if(Symbols, [ToRemove, &Errs](const Symbol &Sym) {
+ Expected<bool> ShouldRemove = ToRemove(Sym);
+ if (!ShouldRemove) {
+ Errs = joinErrors(std::move(Errs), ShouldRemove.takeError());
+ return false;
+ }
+ return *ShouldRemove;
+ });
+
+ updateSymbols();
+ return Errs;
+}
+
+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 createStringError(object_error::invalid_symbol_index,
+ "relocation target %zu not found", R.Target);
+ It->second->Referenced = true;
+ }
+ }
+ return Error::success();
+}
+
+void Object::addSections(ArrayRef<Section> NewSections) {
+ for (Section S : NewSections) {
+ S.UniqueId = NextSectionUniqueId++;
+ Sections.emplace_back(S);
+ }
+ updateSections();
+}
+
+void Object::updateSections() {
+ SectionMap = DenseMap<ssize_t, Section *>(Sections.size());
+ size_t Index = 1;
+ for (Section &S : Sections) {
+ SectionMap[S.UniqueId] = &S;
+ S.Index = Index++;
+ }
+}
+
+const Section *Object::findSection(ssize_t UniqueId) const {
+ return SectionMap.lookup(UniqueId);
+}
+
+void Object::removeSections(function_ref<bool(const Section &)> ToRemove) {
+ DenseSet<ssize_t> AssociatedSections;
+ auto RemoveAssociated = [&AssociatedSections](const Section &Sec) {
+ return AssociatedSections.contains(Sec.UniqueId);
+ };
+ do {
+ DenseSet<ssize_t> RemovedSections;
+ llvm::erase_if(Sections, [ToRemove, &RemovedSections](const Section &Sec) {
+ bool Remove = ToRemove(Sec);
+ if (Remove)
+ RemovedSections.insert(Sec.UniqueId);
+ return Remove;
+ });
+ // Remove all symbols referring to the removed sections.
+ AssociatedSections.clear();
+ llvm::erase_if(
+ Symbols, [&RemovedSections, &AssociatedSections](const Symbol &Sym) {
+ // If there are sections that are associative to a removed
+ // section,
+ // remove those as well as nothing will include them (and we can't
+ // leave them dangling).
+ if (RemovedSections.contains(Sym.AssociativeComdatTargetSectionId))
+ AssociatedSections.insert(Sym.TargetSectionId);
+ return RemovedSections.contains(Sym.TargetSectionId);
+ });
+ ToRemove = RemoveAssociated;
+ } while (!AssociatedSections.empty());
+ updateSections();
+ updateSymbols();
+}
+
+void Object::truncateSections(function_ref<bool(const Section &)> ToTruncate) {
+ for (Section &Sec : Sections) {
+ if (ToTruncate(Sec)) {
+ Sec.clearContents();
+ Sec.Relocs.clear();
+ Sec.Header.SizeOfRawData = 0;
+ }
+ }
+}
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Object.h b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Object.h
new file mode 100644
index 00000000000..0e854b58cbd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Object.h
@@ -0,0 +1,211 @@
+//===- Object.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#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/Optional.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() = default;
+ Relocation(const object::coff_relocation &R) : Reloc(R) {}
+
+ object::coff_relocation Reloc;
+ size_t Target = 0;
+ StringRef TargetName; // Used for diagnostics only
+};
+
+struct Section {
+ object::coff_section Header;
+ std::vector<Relocation> Relocs;
+ StringRef Name;
+ ssize_t UniqueId;
+ size_t Index;
+
+ ArrayRef<uint8_t> getContents() const {
+ if (!OwnedContents.empty())
+ return OwnedContents;
+ return ContentsRef;
+ }
+
+ void setContentsRef(ArrayRef<uint8_t> Data) {
+ OwnedContents.clear();
+ ContentsRef = Data;
+ }
+
+ void setOwnedContents(std::vector<uint8_t> &&Data) {
+ ContentsRef = ArrayRef<uint8_t>();
+ OwnedContents = std::move(Data);
+ }
+
+ void clearContents() {
+ ContentsRef = ArrayRef<uint8_t>();
+ OwnedContents.clear();
+ }
+
+private:
+ ArrayRef<uint8_t> ContentsRef;
+ std::vector<uint8_t> OwnedContents;
+};
+
+struct AuxSymbol {
+ AuxSymbol(ArrayRef<uint8_t> In) {
+ assert(In.size() == sizeof(Opaque));
+ std::copy(In.begin(), In.end(), Opaque);
+ }
+
+ ArrayRef<uint8_t> getRef() const {
+ return ArrayRef<uint8_t>(Opaque, sizeof(Opaque));
+ }
+
+ uint8_t Opaque[sizeof(object::coff_symbol16)];
+};
+
+struct Symbol {
+ object::coff_symbol32 Sym;
+ StringRef Name;
+ std::vector<AuxSymbol> AuxData;
+ StringRef AuxFile;
+ ssize_t TargetSectionId;
+ ssize_t AssociativeComdatTargetSectionId = 0;
+ Optional<size_t> WeakTargetSymbolId;
+ 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;
+
+ 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);
+ Error removeSymbols(function_ref<Expected<bool>(const Symbol &)> ToRemove);
+
+ // Set the Referenced field on all Symbols, based on relocations in
+ // all sections.
+ Error markSymbols();
+
+ ArrayRef<Section> getSections() const { return Sections; }
+ // This allows mutating individual Sections, but not mutating the list
+ // of sections itself.
+ iterator_range<std::vector<Section>::iterator> getMutableSections() {
+ return make_range(Sections.begin(), Sections.end());
+ }
+
+ const Section *findSection(ssize_t UniqueId) const;
+
+ void addSections(ArrayRef<Section> NewSections);
+ void removeSections(function_ref<bool(const Section &)> ToRemove);
+ void truncateSections(function_ref<bool(const Section &)> ToTruncate);
+
+private:
+ std::vector<Symbol> Symbols;
+ DenseMap<size_t, Symbol *> SymbolMap;
+
+ size_t NextSymbolUniqueId = 0;
+
+ std::vector<Section> Sections;
+ DenseMap<ssize_t, Section *> SectionMap;
+
+ ssize_t NextSectionUniqueId = 1; // Allow a UniqueId 0 to mean undefined.
+
+ // Update SymbolMap.
+ void updateSymbols();
+
+ // Update SectionMap and Index in each Section.
+ void updateSections();
+};
+
+// 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/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Reader.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Reader.cpp
new file mode 100644
index 00000000000..d1beacb3bd6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Reader.cpp
@@ -0,0 +1,226 @@
+//===- Reader.cpp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Reader.h"
+#include "Object.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 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()) {
+ Obj.PeHeader = *COFFObj.getPE32PlusHeader();
+ } else {
+ const pe32_header *PE32 = COFFObj.getPE32Header();
+ 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 = COFFObj.getDataDirectory(I);
+ if (!Dir)
+ return errorCodeToError(object_error::parse_failed);
+ Obj.DataDirectories.emplace_back(*Dir);
+ }
+ return Error::success();
+}
+
+Error COFFReader::readSections(Object &Obj) const {
+ std::vector<Section> Sections;
+ // Section indexing starts from 1.
+ for (size_t I = 1, E = COFFObj.getNumberOfSections(); I <= E; I++) {
+ Expected<const coff_section *> SecOrErr = COFFObj.getSection(I);
+ if (!SecOrErr)
+ return SecOrErr.takeError();
+ const coff_section *Sec = *SecOrErr;
+ Sections.push_back(Section());
+ Section &S = Sections.back();
+ S.Header = *Sec;
+ S.Header.Characteristics &= ~COFF::IMAGE_SCN_LNK_NRELOC_OVFL;
+ ArrayRef<uint8_t> Contents;
+ if (Error E = COFFObj.getSectionContents(Sec, Contents))
+ return E;
+ S.setContentsRef(Contents);
+ ArrayRef<coff_relocation> Relocs = COFFObj.getRelocations(Sec);
+ for (const coff_relocation &R : Relocs)
+ S.Relocs.push_back(R);
+ if (Expected<StringRef> NameOrErr = COFFObj.getSectionName(Sec))
+ S.Name = *NameOrErr;
+ else
+ return NameOrErr.takeError();
+ }
+ Obj.addSections(Sections);
+ return Error::success();
+}
+
+Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const {
+ std::vector<Symbol> Symbols;
+ Symbols.reserve(COFFObj.getRawNumberOfSymbols());
+ ArrayRef<Section> Sections = Obj.getSections();
+ 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()));
+ auto NameOrErr = COFFObj.getSymbolName(SymRef);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ Sym.Name = *NameOrErr;
+
+ ArrayRef<uint8_t> AuxData = COFFObj.getSymbolAuxData(SymRef);
+ size_t SymSize = IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16);
+ assert(AuxData.size() == SymSize * SymRef.getNumberOfAuxSymbols());
+ // The auxillary symbols are structs of sizeof(coff_symbol16) each.
+ // In the big object format (where symbols are coff_symbol32), each
+ // auxillary symbol is padded with 2 bytes at the end. Copy each
+ // auxillary symbol to the Sym.AuxData vector. For file symbols,
+ // the whole range of aux symbols are interpreted as one null padded
+ // string instead.
+ if (SymRef.isFileRecord())
+ Sym.AuxFile = StringRef(reinterpret_cast<const char *>(AuxData.data()),
+ AuxData.size())
+ .rtrim('\0');
+ else
+ for (size_t I = 0; I < SymRef.getNumberOfAuxSymbols(); I++)
+ Sym.AuxData.push_back(AuxData.slice(I * SymSize, sizeof(AuxSymbol)));
+
+ // Find the unique id of the section
+ if (SymRef.getSectionNumber() <=
+ 0) // Special symbol (undefined/absolute/debug)
+ Sym.TargetSectionId = SymRef.getSectionNumber();
+ else if (static_cast<uint32_t>(SymRef.getSectionNumber() - 1) <
+ Sections.size())
+ Sym.TargetSectionId = Sections[SymRef.getSectionNumber() - 1].UniqueId;
+ else
+ return createStringError(object_error::parse_failed,
+ "section number out of range");
+ // For section definitions, check if it is comdat associative, and if
+ // it is, find the target section unique id.
+ const coff_aux_section_definition *SD = SymRef.getSectionDefinition();
+ const coff_aux_weak_external *WE = SymRef.getWeakExternal();
+ if (SD && SD->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
+ int32_t Index = SD->getNumber(IsBigObj);
+ if (Index <= 0 || static_cast<uint32_t>(Index - 1) >= Sections.size())
+ return createStringError(object_error::parse_failed,
+ "unexpected associative section index");
+ Sym.AssociativeComdatTargetSectionId = Sections[Index - 1].UniqueId;
+ } else if (WE) {
+ // This is a raw symbol index for now, but store it in the Symbol
+ // until we've added them to the Object, which assigns the final
+ // unique ids.
+ Sym.WeakTargetSymbolId = WE->TagIndex;
+ }
+ I += 1 + SymRef.getNumberOfAuxSymbols();
+ }
+ Obj.addSymbols(Symbols);
+ return Error::success();
+}
+
+Error COFFReader::setSymbolTargets(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 (Symbol &Sym : Obj.getMutableSymbols()) {
+ // Convert WeakTargetSymbolId from the original raw symbol index to
+ // a proper unique id.
+ if (Sym.WeakTargetSymbolId) {
+ if (*Sym.WeakTargetSymbolId >= RawSymbolTable.size())
+ return createStringError(object_error::parse_failed,
+ "weak external reference out of range");
+ const Symbol *Target = RawSymbolTable[*Sym.WeakTargetSymbolId];
+ if (Target == nullptr)
+ return createStringError(object_error::parse_failed,
+ "invalid SymbolTableIndex");
+ Sym.WeakTargetSymbolId = Target->UniqueId;
+ }
+ }
+ for (Section &Sec : Obj.getMutableSections()) {
+ for (Relocation &R : Sec.Relocs) {
+ if (R.Reloc.SymbolTableIndex >= RawSymbolTable.size())
+ return createStringError(object_error::parse_failed,
+ "SymbolTableIndex out of range");
+ const Symbol *Sym = RawSymbolTable[R.Reloc.SymbolTableIndex];
+ if (Sym == nullptr)
+ return createStringError(object_error::parse_failed,
+ "invalid SymbolTableIndex");
+ R.Target = Sym->UniqueId;
+ R.TargetName = Sym->Name;
+ }
+ }
+ return Error::success();
+}
+
+Expected<std::unique_ptr<Object>> COFFReader::create() const {
+ auto Obj = std::make_unique<Object>();
+
+ bool IsBigObj = false;
+ if (const coff_file_header *CFH = COFFObj.getCOFFHeader()) {
+ Obj->CoffFileHeader = *CFH;
+ } else {
+ const coff_bigobj_file_header *CBFH = COFFObj.getCOFFBigObjHeader();
+ if (!CBFH)
+ return createStringError(object_error::parse_failed,
+ "no COFF file header returned");
+ // 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 = setSymbolTargets(*Obj))
+ return std::move(E);
+
+ return std::move(Obj);
+}
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Reader.h b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Reader.h
new file mode 100644
index 00000000000..48c050b6ea1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Reader.h
@@ -0,0 +1,41 @@
+//===- Reader.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_COFF_READER_H
+#define LLVM_TOOLS_OBJCOPY_COFF_READER_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 setSymbolTargets(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/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Writer.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Writer.cpp
new file mode 100644
index 00000000000..fcbfef96d86
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Writer.cpp
@@ -0,0 +1,466 @@
+//===- Writer.cpp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Writer.h"
+#include "Object.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/Errc.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.getMutableSections()) {
+ for (Relocation &R : Sec.Relocs) {
+ const Symbol *Sym = Obj.findSymbol(R.Target);
+ if (Sym == nullptr)
+ return createStringError(object_error::invalid_symbol_index,
+ "relocation target '%s' (%zu) not found",
+ R.TargetName.str().c_str(), R.Target);
+ R.Reloc.SymbolTableIndex = Sym->RawIndex;
+ }
+ }
+ return Error::success();
+}
+
+Error COFFWriter::finalizeSymbolContents() {
+ for (Symbol &Sym : Obj.getMutableSymbols()) {
+ if (Sym.TargetSectionId <= 0) {
+ // Undefined, or a special kind of symbol. These negative values
+ // are stored in the SectionNumber field which is unsigned.
+ Sym.Sym.SectionNumber = static_cast<uint32_t>(Sym.TargetSectionId);
+ } else {
+ const Section *Sec = Obj.findSection(Sym.TargetSectionId);
+ if (Sec == nullptr)
+ return createStringError(object_error::invalid_symbol_index,
+ "symbol '%s' points to a removed section",
+ Sym.Name.str().c_str());
+ Sym.Sym.SectionNumber = Sec->Index;
+
+ if (Sym.Sym.NumberOfAuxSymbols == 1 &&
+ Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC) {
+ coff_aux_section_definition *SD =
+ reinterpret_cast<coff_aux_section_definition *>(
+ Sym.AuxData[0].Opaque);
+ uint32_t SDSectionNumber;
+ if (Sym.AssociativeComdatTargetSectionId == 0) {
+ // Not a comdat associative section; just set the Number field to
+ // the number of the section itself.
+ SDSectionNumber = Sec->Index;
+ } else {
+ Sec = Obj.findSection(Sym.AssociativeComdatTargetSectionId);
+ if (Sec == nullptr)
+ return createStringError(
+ object_error::invalid_symbol_index,
+ "symbol '%s' is associative to a removed section",
+ Sym.Name.str().c_str());
+ SDSectionNumber = Sec->Index;
+ }
+ // Update the section definition with the new section number.
+ SD->NumberLowPart = static_cast<uint16_t>(SDSectionNumber);
+ SD->NumberHighPart = static_cast<uint16_t>(SDSectionNumber >> 16);
+ }
+ }
+ // Check that we actually have got AuxData to match the weak symbol target
+ // we want to set. Only >= 1 would be required, but only == 1 makes sense.
+ if (Sym.WeakTargetSymbolId && Sym.Sym.NumberOfAuxSymbols == 1) {
+ coff_aux_weak_external *WE =
+ reinterpret_cast<coff_aux_weak_external *>(Sym.AuxData[0].Opaque);
+ const Symbol *Target = Obj.findSymbol(*Sym.WeakTargetSymbolId);
+ if (Target == nullptr)
+ return createStringError(object_error::invalid_symbol_index,
+ "symbol '%s' is missing its weak target",
+ Sym.Name.str().c_str());
+ WE->TagIndex = Target->RawIndex;
+ }
+ }
+ return Error::success();
+}
+
+void COFFWriter::layoutSections() {
+ for (auto &S : Obj.getMutableSections()) {
+ if (S.Header.SizeOfRawData > 0)
+ S.Header.PointerToRawData = FileSize;
+ FileSize += S.Header.SizeOfRawData; // For executables, this is already
+ // aligned to FileAlignment.
+ if (S.Relocs.size() >= 0xffff) {
+ S.Header.Characteristics |= COFF::IMAGE_SCN_LNK_NRELOC_OVFL;
+ S.Header.NumberOfRelocations = 0xffff;
+ S.Header.PointerToRelocations = FileSize;
+ FileSize += sizeof(coff_relocation);
+ } else {
+ S.Header.NumberOfRelocations = S.Relocs.size();
+ S.Header.PointerToRelocations = S.Relocs.size() ? 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;
+ }
+}
+
+Expected<size_t> COFFWriter::finalizeStringTable() {
+ for (const auto &S : Obj.getSections())
+ 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.getMutableSections()) {
+ memset(S.Header.Name, 0, sizeof(S.Header.Name));
+ if (S.Name.size() <= COFF::NameSize) {
+ // Short names can go in the field directly.
+ memcpy(S.Header.Name, S.Name.data(), S.Name.size());
+ } else {
+ // Offset of the section name in the string table.
+ size_t Offset = StrTabBuilder.getOffset(S.Name);
+ if (!COFF::encodeSectionName(S.Header.Name, Offset))
+ return createStringError(object_error::invalid_section_index,
+ "COFF string table is greater than 64GB, "
+ "unable to encode section name offset");
+ }
+ }
+ 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 RawSymIndex = 0;
+ for (auto &S : Obj.getMutableSymbols()) {
+ // Symbols normally have NumberOfAuxSymbols set correctly all the time.
+ // For file symbols, we need to know the output file's symbol size to be
+ // able to calculate the number of slots it occupies.
+ if (!S.AuxFile.empty())
+ S.Sym.NumberOfAuxSymbols =
+ alignTo(S.AuxFile.size(), sizeof(SymbolTy)) / sizeof(SymbolTy);
+ S.RawIndex = RawSymIndex;
+ RawSymIndex += 1 + S.Sym.NumberOfAuxSymbols;
+ }
+ return std::make_pair(RawSymIndex * sizeof(SymbolTy), sizeof(SymbolTy));
+}
+
+Error COFFWriter::finalize(bool IsBigObj) {
+ size_t SymTabSize, SymbolSize;
+ std::tie(SymTabSize, SymbolSize) = IsBigObj
+ ? finalizeSymbolTable<coff_symbol32>()
+ : finalizeSymbolTable<coff_symbol16>();
+
+ if (Error E = finalizeRelocTargets())
+ return E;
+ if (Error E = finalizeSymbolContents())
+ 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.getSections().size();
+ SizeOfHeaders +=
+ IsBigObj ? sizeof(coff_bigobj_file_header) : sizeof(coff_file_header);
+ SizeOfHeaders += sizeof(coff_section) * Obj.getSections().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.getSections().empty()) {
+ const Section &S = Obj.getSections().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;
+ }
+
+ Expected<size_t> StrTabSizeOrErr = finalizeStringTable();
+ if (!StrTabSizeOrErr)
+ return StrTabSizeOrErr.takeError();
+
+ size_t StrTabSize = *StrTabSizeOrErr;
+
+ 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 = reinterpret_cast<uint8_t *>(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.getSections().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.getSections()) {
+ memcpy(Ptr, &S.Header, sizeof(S.Header));
+ Ptr += sizeof(S.Header);
+ }
+}
+
+void COFFWriter::writeSections() {
+ for (const auto &S : Obj.getSections()) {
+ uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
+ S.Header.PointerToRawData;
+ ArrayRef<uint8_t> Contents = S.getContents();
+ std::copy(Contents.begin(), 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 > Contents.size())
+ memset(Ptr + Contents.size(), 0xcc,
+ S.Header.SizeOfRawData - Contents.size());
+
+ Ptr += S.Header.SizeOfRawData;
+
+ if (S.Relocs.size() >= 0xffff) {
+ object::coff_relocation R;
+ R.VirtualAddress = S.Relocs.size() + 1;
+ R.SymbolTableIndex = 0;
+ R.Type = 0;
+ memcpy(Ptr, &R, sizeof(R));
+ Ptr += sizeof(R);
+ }
+ 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 = reinterpret_cast<uint8_t *>(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);
+ if (!S.AuxFile.empty()) {
+ // For file symbols, just write the string into the aux symbol slots,
+ // assuming that the unwritten parts are initialized to zero in the memory
+ // mapped file.
+ std::copy(S.AuxFile.begin(), S.AuxFile.end(), Ptr);
+ Ptr += S.Sym.NumberOfAuxSymbols * sizeof(SymbolTy);
+ } else {
+ // For other auxillary symbols, write their opaque payload into one symbol
+ // table slot each. For big object files, the symbols are larger than the
+ // opaque auxillary symbol struct and we leave padding at the end of each
+ // entry.
+ for (const AuxSymbol &AuxSym : S.AuxData) {
+ ArrayRef<uint8_t> Ref = AuxSym.getRef();
+ std::copy(Ref.begin(), Ref.end(), Ptr);
+ Ptr += sizeof(SymbolTy);
+ }
+ }
+ }
+ 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 = WritableMemoryBuffer::getNewMemBuffer(FileSize);
+ if (!Buf)
+ return createStringError(llvm::errc::not_enough_memory,
+ "failed to allocate memory buffer of " +
+ Twine::utohexstr(FileSize) + " bytes.");
+
+ writeHeaders(IsBigObj);
+ writeSections();
+ if (IsBigObj)
+ writeSymbolStringTables<coff_symbol32>();
+ else
+ writeSymbolStringTables<coff_symbol16>();
+
+ if (Obj.IsPE)
+ if (Error E = patchDebugDirectory())
+ return E;
+
+ // TODO: Implement direct writing to the output stream (without intermediate
+ // memory buffer Buf).
+ Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+ return Error::success();
+}
+
+Expected<uint32_t> COFFWriter::virtualAddressToFileAddress(uint32_t RVA) {
+ for (const auto &S : Obj.getSections()) {
+ if (RVA >= S.Header.VirtualAddress &&
+ RVA < S.Header.VirtualAddress + S.Header.SizeOfRawData)
+ return S.Header.PointerToRawData + RVA - S.Header.VirtualAddress;
+ }
+ return createStringError(object_error::parse_failed,
+ "debug directory payload not found");
+}
+
+// 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.getSections()) {
+ 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 createStringError(object_error::parse_failed,
+ "debug directory extends past end of section");
+
+ size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress;
+ uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
+ S.Header.PointerToRawData + Offset;
+ uint8_t *End = Ptr + Dir->Size;
+ while (Ptr < End) {
+ debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr);
+ if (Debug->PointerToRawData) {
+ if (Expected<uint32_t> FilePosOrErr =
+ virtualAddressToFileAddress(Debug->AddressOfRawData))
+ Debug->PointerToRawData = *FilePosOrErr;
+ else
+ return FilePosOrErr.takeError();
+ }
+ Ptr += sizeof(debug_directory);
+ Offset += sizeof(debug_directory);
+ }
+ // Debug directory found and patched, all done.
+ return Error::success();
+ }
+ }
+ return createStringError(object_error::parse_failed,
+ "debug directory not found");
+}
+
+Error COFFWriter::write() {
+ bool IsBigObj = Obj.getSections().size() > MaxNumberOfSections16;
+ if (IsBigObj && Obj.IsPE)
+ return createStringError(object_error::parse_failed,
+ "too many sections for executable");
+ return write(IsBigObj);
+}
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Writer.h b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Writer.h
new file mode 100644
index 00000000000..5758aadb543
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/COFF/Writer.h
@@ -0,0 +1,63 @@
+//===- Writer.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_COFF_WRITER_H
+#define LLVM_TOOLS_OBJCOPY_COFF_WRITER_H
+
+#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <cstddef>
+#include <utility>
+
+namespace llvm {
+namespace objcopy {
+namespace coff {
+
+struct Object;
+
+class COFFWriter {
+ Object &Obj;
+ std::unique_ptr<WritableMemoryBuffer> Buf;
+ raw_ostream &Out;
+
+ size_t FileSize;
+ size_t FileAlignment;
+ size_t SizeOfInitializedData;
+ StringTableBuilder StrTabBuilder;
+
+ template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable();
+ Error finalizeRelocTargets();
+ Error finalizeSymbolContents();
+ void layoutSections();
+ Expected<size_t> finalizeStringTable();
+
+ Error finalize(bool IsBigObj);
+
+ void writeHeaders(bool IsBigObj);
+ void writeSections();
+ template <class SymbolTy> void writeSymbolStringTables();
+
+ Error write(bool IsBigObj);
+
+ Error patchDebugDirectory();
+ Expected<uint32_t> virtualAddressToFileAddress(uint32_t RVA);
+
+public:
+ virtual ~COFFWriter() {}
+ Error write();
+
+ COFFWriter(Object &Obj, raw_ostream &Out)
+ : Obj(Obj), Out(Out), StrTabBuilder(StringTableBuilder::WinCOFF) {}
+};
+
+} // end namespace coff
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_COFF_WRITER_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/CommonConfig.h b/contrib/libs/llvm14/tools/llvm-objcopy/CommonConfig.h
new file mode 100644
index 00000000000..ea39a6da2ba
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/CommonConfig.h
@@ -0,0 +1,260 @@
+//===- CommonConfig.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/CachedHashString.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/Support/GlobPattern.h"
+#include "llvm/Support/Regex.h"
+// Necessary for llvm::DebugCompressionType::None
+#include "llvm/Target/TargetOptions.h"
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+
+enum class FileFormat {
+ Unspecified,
+ ELF,
+ Binary,
+ IHex,
+};
+
+// 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 {
+ MachineInfo(uint16_t EM, uint8_t ABI, bool Is64, bool IsLittle)
+ : EMachine(EM), OSABI(ABI), Is64Bit(Is64), IsLittleEndian(IsLittle) {}
+ // Alternative constructor that defaults to NONE for OSABI.
+ MachineInfo(uint16_t EM, bool Is64, bool IsLittle)
+ : MachineInfo(EM, ELF::ELFOSABI_NONE, Is64, IsLittle) {}
+ // Default constructor for unset fields.
+ MachineInfo() : MachineInfo(0, 0, false, false) {}
+ uint16_t EMachine;
+ uint8_t OSABI;
+ bool Is64Bit;
+ bool IsLittleEndian;
+};
+
+// Flags set by --set-section-flags or --rename-section. Interpretation of these
+// is format-specific and not all flags are meaningful for all object file
+// formats. This is a bitmask; many section flags may be set.
+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,
+ SecExclude = 1 << 12,
+ LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecExclude)
+};
+
+struct SectionRename {
+ StringRef OriginalName;
+ StringRef NewName;
+ Optional<SectionFlag> NewFlags;
+};
+
+struct SectionFlagsUpdate {
+ StringRef Name;
+ SectionFlag NewFlags;
+};
+
+enum class DiscardType {
+ None, // Default
+ All, // --discard-all (-x)
+ Locals, // --discard-locals (-X)
+};
+
+enum class MatchStyle {
+ Literal, // Default for symbols.
+ Wildcard, // Default for sections, or enabled with --wildcard (-w).
+ Regex, // Enabled with --regex.
+};
+
+class NameOrPattern {
+ StringRef Name;
+ // Regex is shared between multiple CommonConfig instances.
+ std::shared_ptr<Regex> R;
+ std::shared_ptr<GlobPattern> G;
+ bool IsPositiveMatch = true;
+
+ NameOrPattern(StringRef N) : Name(N) {}
+ NameOrPattern(std::shared_ptr<Regex> R) : R(R) {}
+ NameOrPattern(std::shared_ptr<GlobPattern> G, bool IsPositiveMatch)
+ : G(G), IsPositiveMatch(IsPositiveMatch) {}
+
+public:
+ // ErrorCallback is used to handle recoverable errors. An Error returned
+ // by the callback aborts the parsing and is then returned by this function.
+ static Expected<NameOrPattern>
+ create(StringRef Pattern, MatchStyle MS,
+ llvm::function_ref<Error(Error)> ErrorCallback);
+
+ bool isPositiveMatch() const { return IsPositiveMatch; }
+ Optional<StringRef> getName() const {
+ if (!R && !G)
+ return Name;
+ return None;
+ }
+ bool operator==(StringRef S) const {
+ return R ? R->match(S) : G ? G->match(S) : Name == S;
+ }
+ bool operator!=(StringRef S) const { return !operator==(S); }
+};
+
+// Matcher that checks symbol or section names against the command line flags
+// provided for that option.
+class NameMatcher {
+ DenseSet<CachedHashStringRef> PosNames;
+ std::vector<NameOrPattern> PosPatterns;
+ std::vector<NameOrPattern> NegMatchers;
+
+public:
+ Error addMatcher(Expected<NameOrPattern> Matcher) {
+ if (!Matcher)
+ return Matcher.takeError();
+ if (Matcher->isPositiveMatch()) {
+ if (Optional<StringRef> MaybeName = Matcher->getName())
+ PosNames.insert(CachedHashStringRef(*MaybeName));
+ else
+ PosPatterns.push_back(std::move(*Matcher));
+ } else {
+ NegMatchers.push_back(std::move(*Matcher));
+ }
+ return Error::success();
+ }
+ bool matches(StringRef S) const {
+ return (PosNames.contains(CachedHashStringRef(S)) ||
+ is_contained(PosPatterns, S)) &&
+ !is_contained(NegMatchers, S);
+ }
+ bool empty() const {
+ return PosNames.empty() && PosPatterns.empty() && NegMatchers.empty();
+ }
+};
+
+enum class SymbolFlag {
+ Global,
+ Local,
+ Weak,
+ Default,
+ Hidden,
+ Protected,
+ File,
+ Section,
+ Object,
+ Function,
+ IndirectFunction,
+ Debug,
+ Constructor,
+ Warning,
+ Indirect,
+ Synthetic,
+ UniqueObject,
+};
+
+// Symbol info specified by --add-symbol option. Symbol flags not supported
+// by a concrete format should be ignored.
+struct NewSymbolInfo {
+ StringRef SymbolName;
+ StringRef SectionName;
+ uint64_t Value = 0;
+ std::vector<SymbolFlag> Flags;
+ std::vector<StringRef> BeforeSyms;
+};
+
+// Configuration for copying/stripping a single file.
+struct CommonConfig {
+ // Main input/output options
+ StringRef InputFilename;
+ FileFormat InputFormat = FileFormat::Unspecified;
+ StringRef OutputFilename;
+ FileFormat OutputFormat = FileFormat::Unspecified;
+
+ // Only applicable when --output-format!=binary (e.g. elf64-x86-64).
+ Optional<MachineInfo> OutputArch;
+
+ // Advanced options
+ StringRef AddGnuDebugLink;
+ // Cached gnu_debuglink's target CRC
+ uint32_t GnuDebugLinkCRC32;
+ Optional<StringRef> ExtractPartition;
+ StringRef SplitDWO;
+ StringRef SymbolsPrefix;
+ StringRef AllocSectionsPrefix;
+ DiscardType DiscardMode = DiscardType::None;
+
+ // Repeated options
+ std::vector<StringRef> AddSection;
+ std::vector<StringRef> DumpSection;
+ std::vector<StringRef> UpdateSection;
+
+ // Section matchers
+ NameMatcher KeepSection;
+ NameMatcher OnlySection;
+ NameMatcher ToRemove;
+
+ // Symbol matchers
+ NameMatcher SymbolsToGlobalize;
+ NameMatcher SymbolsToKeep;
+ NameMatcher SymbolsToLocalize;
+ NameMatcher SymbolsToRemove;
+ NameMatcher UnneededSymbolsToRemove;
+ NameMatcher SymbolsToWeaken;
+ NameMatcher SymbolsToKeepGlobal;
+
+ // Map options
+ StringMap<SectionRename> SectionsToRename;
+ StringMap<uint64_t> SetSectionAlignment;
+ StringMap<SectionFlagsUpdate> SetSectionFlags;
+ StringMap<StringRef> SymbolsToRename;
+
+ // Symbol info specified by --add-symbol option.
+ std::vector<NewSymbolInfo> SymbolsToAdd;
+
+ // Boolean options
+ bool DeterministicArchives = true;
+ bool ExtractDWO = false;
+ bool ExtractMainPartition = 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;
+};
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/CommonOpts.td b/contrib/libs/llvm14/tools/llvm-objcopy/CommonOpts.td
new file mode 100644
index 00000000000..4222532a1a3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/CommonOpts.td
@@ -0,0 +1,129 @@
+include "llvm/Option/OptParser.td"
+
+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">;
+def h : Flag<["-"], "h">, Alias<help>;
+
+def allow_broken_links
+ : Flag<["--"], "allow-broken-links">,
+ HelpText<"Allow the tool to remove sections even if it would leave "
+ "invalid section references. The appropriate sh_link fields "
+ "will be set to zero.">;
+
+def enable_deterministic_archives
+ : Flag<["--"], "enable-deterministic-archives">,
+ HelpText<"Enable deterministic mode when operating on 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 operating on 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>,
+ HelpText<"Alias for --preserve-dates">;
+
+def strip_all : Flag<["--"], "strip-all">,
+ HelpText<"Remove non-allocated sections outside segments. "
+ ".gnu.warning* and .ARM.attribute sections are not "
+ "removed">;
+
+def strip_all_gnu
+ : Flag<["--"], "strip-all-gnu">,
+ HelpText<"Compatible with GNU's --strip-all">;
+
+def strip_debug : Flag<["--"], "strip-debug">,
+ HelpText<"Remove all debug sections">;
+def g : Flag<["-"], "g">,
+ Alias<strip_debug>,
+ HelpText<"Alias for --strip-debug">;
+
+def strip_unneeded : Flag<["--"], "strip-unneeded">,
+ HelpText<"Remove all symbols not needed by relocations">;
+
+defm remove_section : Eq<"remove-section", "Remove <section>">,
+ MetaVarName<"section">;
+def R : JoinedOrSeparate<["-"], "R">,
+ Alias<remove_section>,
+ HelpText<"Alias for --remove-section">;
+
+def strip_sections
+ : Flag<["--"], "strip-sections">,
+ HelpText<"Remove all section headers and all sections not in segments">;
+
+defm strip_symbol : Eq<"strip-symbol", "Strip <symbol>">,
+ MetaVarName<"symbol">;
+def N : JoinedOrSeparate<["-"], "N">,
+ Alias<strip_symbol>,
+ HelpText<"Alias for --strip-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>,
+ HelpText<"Alias for --keep-symbol">;
+
+def keep_file_symbols : Flag<["--"], "keep-file-symbols">,
+ HelpText<"Do not remove file symbols">;
+
+def keep_undefined : Flag<["--"], "keep-undefined">,
+ HelpText<"Do not remove undefined symbols">;
+
+def only_keep_debug
+ : Flag<["--"], "only-keep-debug">,
+ HelpText<
+ "Produce a debug file as the output that only preserves contents of "
+ "sections useful for debugging purposes">;
+
+def discard_locals : Flag<["--"], "discard-locals">,
+ HelpText<"Remove compiler-generated local symbols, (e.g. "
+ "symbols starting with .L)">;
+def X : Flag<["-"], "X">,
+ Alias<discard_locals>,
+ HelpText<"Alias for --discard-locals">;
+
+def discard_all
+ : Flag<["--"], "discard-all">,
+ HelpText<"Remove all local symbols except file and section symbols. Also "
+ "remove all debug sections">;
+def x : Flag<["-"], "x">,
+ Alias<discard_all>,
+ HelpText<"Alias for --discard-all">;
+
+def regex
+ : Flag<["--"], "regex">,
+ HelpText<"Permit regular expressions in name comparison">;
+
+def version : Flag<["--"], "version">,
+ HelpText<"Print the version and exit.">;
+def V : Flag<["-"], "V">,
+ Alias<version>,
+ HelpText<"Alias for --version">;
+
+def wildcard
+ : Flag<["--"], "wildcard">,
+ HelpText<"Allow wildcard syntax for symbol-related flags. Incompatible "
+ "with --regex. Allows using '*' to match any number of "
+ "characters, '?' to match any single character, '\' to escape "
+ "special characters, and '[]' to define character classes. "
+ "Wildcards beginning with '!' will prevent a match, for example "
+ "\"-N '*' -N '!x'\" will strip all symbols except for \"x\".">;
+def w : Flag<["-"], "w">, Alias<wildcard>, HelpText<"Alias for --wildcard">;
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/ConfigManager.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/ConfigManager.cpp
new file mode 100644
index 00000000000..90730c421a4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/ConfigManager.cpp
@@ -0,0 +1,1432 @@
+//===- ConfigManager.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ConfigManager.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/CRC.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Compression.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/StringSaver.h"
+#include <memory>
+
+using namespace llvm;
+using namespace llvm::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
+
+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) {
+ setGroupedShortOptions(true);
+ }
+};
+
+enum InstallNameToolID {
+ INSTALL_NAME_TOOL_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ INSTALL_NAME_TOOL_##ID,
+#include "InstallNameToolOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) \
+ const char *const INSTALL_NAME_TOOL_##NAME[] = VALUE;
+#include "InstallNameToolOpts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InstallNameToolInfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {INSTALL_NAME_TOOL_##PREFIX, \
+ NAME, \
+ HELPTEXT, \
+ METAVAR, \
+ INSTALL_NAME_TOOL_##ID, \
+ opt::Option::KIND##Class, \
+ PARAM, \
+ FLAGS, \
+ INSTALL_NAME_TOOL_##GROUP, \
+ INSTALL_NAME_TOOL_##ALIAS, \
+ ALIASARGS, \
+ VALUES},
+#include "InstallNameToolOpts.inc"
+#undef OPTION
+};
+
+class InstallNameToolOptTable : public opt::OptTable {
+public:
+ InstallNameToolOptTable() : OptTable(InstallNameToolInfoTable) {}
+};
+
+enum BitcodeStripID {
+ BITCODE_STRIP_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ BITCODE_STRIP_##ID,
+#include "BitcodeStripOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const BITCODE_STRIP_##NAME[] = VALUE;
+#include "BitcodeStripOpts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info BitcodeStripInfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {BITCODE_STRIP_##PREFIX, \
+ NAME, \
+ HELPTEXT, \
+ METAVAR, \
+ BITCODE_STRIP_##ID, \
+ opt::Option::KIND##Class, \
+ PARAM, \
+ FLAGS, \
+ BITCODE_STRIP_##GROUP, \
+ BITCODE_STRIP_##ALIAS, \
+ ALIASARGS, \
+ VALUES},
+#include "BitcodeStripOpts.inc"
+#undef OPTION
+};
+
+class BitcodeStripOptTable : public opt::OptTable {
+public:
+ BitcodeStripOptTable() : OptTable(BitcodeStripInfoTable) {}
+};
+
+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
+
+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) { setGroupedShortOptions(true); }
+};
+
+} // namespace
+
+static SectionFlag parseSectionRenameFlag(StringRef SectionName) {
+ return llvm::StringSwitch<SectionFlag>(SectionName)
+ .CaseLower("alloc", SectionFlag::SecAlloc)
+ .CaseLower("load", SectionFlag::SecLoad)
+ .CaseLower("noload", SectionFlag::SecNoload)
+ .CaseLower("readonly", SectionFlag::SecReadonly)
+ .CaseLower("debug", SectionFlag::SecDebug)
+ .CaseLower("code", SectionFlag::SecCode)
+ .CaseLower("data", SectionFlag::SecData)
+ .CaseLower("rom", SectionFlag::SecRom)
+ .CaseLower("merge", SectionFlag::SecMerge)
+ .CaseLower("strings", SectionFlag::SecStrings)
+ .CaseLower("contents", SectionFlag::SecContents)
+ .CaseLower("share", SectionFlag::SecShare)
+ .CaseLower("exclude", SectionFlag::SecExclude)
+ .Default(SectionFlag::SecNone);
+}
+
+static Expected<SectionFlag>
+parseSectionFlagSet(ArrayRef<StringRef> SectionFlags) {
+ SectionFlag ParsedFlags = SectionFlag::SecNone;
+ for (StringRef Flag : SectionFlags) {
+ SectionFlag ParsedFlag = parseSectionRenameFlag(Flag);
+ if (ParsedFlag == SectionFlag::SecNone)
+ return createStringError(
+ errc::invalid_argument,
+ "unrecognized section flag '%s'. Flags supported for GNU "
+ "compatibility: alloc, load, noload, readonly, exclude, debug, "
+ "code, data, rom, share, contents, merge, strings",
+ Flag.str().c_str());
+ ParsedFlags |= ParsedFlag;
+ }
+
+ return ParsedFlags;
+}
+
+static Expected<SectionRename> parseRenameSectionValue(StringRef FlagValue) {
+ if (!FlagValue.contains('='))
+ return createStringError(errc::invalid_argument,
+ "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) {
+ Expected<SectionFlag> ParsedFlagSet =
+ parseSectionFlagSet(makeArrayRef(NameAndFlags).drop_front());
+ if (!ParsedFlagSet)
+ return ParsedFlagSet.takeError();
+ SR.NewFlags = *ParsedFlagSet;
+ }
+
+ return SR;
+}
+
+static Expected<std::pair<StringRef, uint64_t>>
+parseSetSectionAlignment(StringRef FlagValue) {
+ if (!FlagValue.contains('='))
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --set-section-alignment: missing '='");
+ auto Split = StringRef(FlagValue).split('=');
+ if (Split.first.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --set-section-alignment: missing section name");
+ uint64_t NewAlign;
+ if (Split.second.getAsInteger(0, NewAlign))
+ return createStringError(
+ errc::invalid_argument,
+ "invalid alignment for --set-section-alignment: '%s'",
+ Split.second.str().c_str());
+ return std::make_pair(Split.first, NewAlign);
+}
+
+static Expected<SectionFlagsUpdate>
+parseSetSectionFlagValue(StringRef FlagValue) {
+ if (!StringRef(FlagValue).contains('='))
+ return createStringError(errc::invalid_argument,
+ "bad format for --set-section-flags: missing '='");
+
+ // Initial split: ".foo" = "f1,f2,..."
+ auto Section2Flags = StringRef(FlagValue).split('=');
+ SectionFlagsUpdate SFU;
+ SFU.Name = Section2Flags.first;
+
+ // Flags split: "f1" "f2" ...
+ SmallVector<StringRef, 6> SectionFlags;
+ Section2Flags.second.split(SectionFlags, ',');
+ Expected<SectionFlag> ParsedFlagSet = parseSectionFlagSet(SectionFlags);
+ if (!ParsedFlagSet)
+ return ParsedFlagSet.takeError();
+ SFU.NewFlags = *ParsedFlagSet;
+
+ return SFU;
+}
+
+namespace {
+struct TargetInfo {
+ FileFormat Format;
+ MachineInfo Machine;
+};
+} // namespace
+
+// FIXME: consolidate with the bfd parsing used by lld.
+static const StringMap<MachineInfo> TargetMap{
+ // Name, {EMachine, 64bit, LittleEndian}
+ // x86
+ {"elf32-i386", {ELF::EM_386, false, true}},
+ {"elf32-x86-64", {ELF::EM_X86_64, false, true}},
+ {"elf64-x86-64", {ELF::EM_X86_64, true, true}},
+ // Intel MCU
+ {"elf32-iamcu", {ELF::EM_IAMCU, false, true}},
+ // ARM
+ {"elf32-littlearm", {ELF::EM_ARM, false, true}},
+ // ARM AArch64
+ {"elf64-aarch64", {ELF::EM_AARCH64, true, true}},
+ {"elf64-littleaarch64", {ELF::EM_AARCH64, true, true}},
+ // RISC-V
+ {"elf32-littleriscv", {ELF::EM_RISCV, false, true}},
+ {"elf64-littleriscv", {ELF::EM_RISCV, true, true}},
+ // PowerPC
+ {"elf32-powerpc", {ELF::EM_PPC, false, false}},
+ {"elf32-powerpcle", {ELF::EM_PPC, false, true}},
+ {"elf64-powerpc", {ELF::EM_PPC64, true, false}},
+ {"elf64-powerpcle", {ELF::EM_PPC64, true, true}},
+ // MIPS
+ {"elf32-bigmips", {ELF::EM_MIPS, false, false}},
+ {"elf32-ntradbigmips", {ELF::EM_MIPS, false, false}},
+ {"elf32-ntradlittlemips", {ELF::EM_MIPS, false, true}},
+ {"elf32-tradbigmips", {ELF::EM_MIPS, false, false}},
+ {"elf32-tradlittlemips", {ELF::EM_MIPS, false, true}},
+ {"elf64-tradbigmips", {ELF::EM_MIPS, true, false}},
+ {"elf64-tradlittlemips", {ELF::EM_MIPS, true, true}},
+ // SPARC
+ {"elf32-sparc", {ELF::EM_SPARC, false, false}},
+ {"elf32-sparcel", {ELF::EM_SPARC, false, true}},
+ {"elf32-hexagon", {ELF::EM_HEXAGON, false, true}},
+};
+
+static Expected<TargetInfo>
+getOutputTargetInfoByTargetName(StringRef TargetName) {
+ StringRef OriginalTargetName = TargetName;
+ bool IsFreeBSD = TargetName.consume_back("-freebsd");
+ auto Iter = TargetMap.find(TargetName);
+ if (Iter == std::end(TargetMap))
+ return createStringError(errc::invalid_argument,
+ "invalid output format: '%s'",
+ OriginalTargetName.str().c_str());
+ MachineInfo MI = Iter->getValue();
+ if (IsFreeBSD)
+ MI.OSABI = ELF::ELFOSABI_FREEBSD;
+
+ FileFormat Format;
+ if (TargetName.startswith("elf"))
+ Format = FileFormat::ELF;
+ else
+ // This should never happen because `TargetName` is valid (it certainly
+ // exists in the TargetMap).
+ llvm_unreachable("unknown target prefix");
+
+ return {TargetInfo{Format, MI}};
+}
+
+static Error addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc,
+ StringRef Filename, MatchStyle MS,
+ function_ref<Error(Error)> ErrorCallback) {
+ StringSaver Saver(Alloc);
+ SmallVector<StringRef, 16> Lines;
+ auto BufOrErr = MemoryBuffer::getFile(Filename);
+ if (!BufOrErr)
+ return createFileError(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())
+ if (Error E = Symbols.addMatcher(NameOrPattern::create(
+ Saver.save(TrimmedLine), MS, ErrorCallback)))
+ return E;
+ }
+
+ return Error::success();
+}
+
+Expected<NameOrPattern>
+NameOrPattern::create(StringRef Pattern, MatchStyle MS,
+ function_ref<Error(Error)> ErrorCallback) {
+ switch (MS) {
+ case MatchStyle::Literal:
+ return NameOrPattern(Pattern);
+ case MatchStyle::Wildcard: {
+ SmallVector<char, 32> Data;
+ bool IsPositiveMatch = true;
+ if (Pattern[0] == '!') {
+ IsPositiveMatch = false;
+ Pattern = Pattern.drop_front();
+ }
+ Expected<GlobPattern> GlobOrErr = GlobPattern::create(Pattern);
+
+ // If we couldn't create it as a glob, report the error, but try again with
+ // a literal if the error reporting is non-fatal.
+ if (!GlobOrErr) {
+ if (Error E = ErrorCallback(GlobOrErr.takeError()))
+ return std::move(E);
+ return create(Pattern, MatchStyle::Literal, ErrorCallback);
+ }
+
+ return NameOrPattern(std::make_shared<GlobPattern>(*GlobOrErr),
+ IsPositiveMatch);
+ }
+ case MatchStyle::Regex: {
+ SmallVector<char, 32> Data;
+ return NameOrPattern(std::make_shared<Regex>(
+ ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data)));
+ }
+ }
+ llvm_unreachable("Unhandled llvm.objcopy.MatchStyle enum");
+}
+
+static Error addSymbolsToRenameFromFile(StringMap<StringRef> &SymbolsToRename,
+ BumpPtrAllocator &Alloc,
+ StringRef Filename) {
+ StringSaver Saver(Alloc);
+ SmallVector<StringRef, 16> Lines;
+ auto BufOrErr = MemoryBuffer::getFile(Filename);
+ if (!BufOrErr)
+ return createFileError(Filename, BufOrErr.getError());
+
+ BufOrErr.get()->getBuffer().split(Lines, '\n');
+ size_t NumLines = Lines.size();
+ for (size_t LineNo = 0; LineNo < NumLines; ++LineNo) {
+ StringRef TrimmedLine = Lines[LineNo].split('#').first.trim();
+ if (TrimmedLine.empty())
+ continue;
+
+ std::pair<StringRef, StringRef> Pair = Saver.save(TrimmedLine).split(' ');
+ StringRef NewName = Pair.second.trim();
+ if (NewName.empty())
+ return createStringError(errc::invalid_argument,
+ "%s:%zu: missing new symbol name",
+ Filename.str().c_str(), LineNo + 1);
+ SymbolsToRename.insert({Pair.first, NewName});
+ }
+ return Error::success();
+}
+
+template <class T> static ErrorOr<T> getAsInteger(StringRef Val) {
+ T Result;
+ if (Val.getAsInteger(0, Result))
+ return errc::invalid_argument;
+ return Result;
+}
+
+namespace {
+
+enum class ToolType { Objcopy, Strip, InstallNameTool, BitcodeStrip };
+
+} // anonymous namespace
+
+static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS,
+ ToolType Tool) {
+ StringRef HelpText, ToolName;
+ switch (Tool) {
+ case ToolType::Objcopy:
+ ToolName = "llvm-objcopy";
+ HelpText = " [options] input [output]";
+ break;
+ case ToolType::Strip:
+ ToolName = "llvm-strip";
+ HelpText = " [options] inputs...";
+ break;
+ case ToolType::InstallNameTool:
+ ToolName = "llvm-install-name-tool";
+ HelpText = " [options] input";
+ break;
+ case ToolType::BitcodeStrip:
+ ToolName = "llvm-bitcode-strip";
+ HelpText = " [options] input";
+ break;
+ }
+ OptTable.printHelp(OS, (ToolName + HelpText).str().c_str(),
+ (ToolName + " tool").str().c_str());
+ // TODO: Replace this with libOption call once it adds extrahelp support.
+ // The CommandLine library has a cl::extrahelp class to support this,
+ // but libOption does not have that yet.
+ OS << "\nPass @FILE as argument to read options from FILE.\n";
+}
+
+static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue) {
+ // Parse value given with --add-symbol option and create the
+ // new symbol if possible. The value format for --add-symbol is:
+ //
+ // <name>=[<section>:]<value>[,<flags>]
+ //
+ // where:
+ // <name> - symbol name, can be empty string
+ // <section> - optional section name. If not given ABS symbol is created
+ // <value> - symbol value, can be decimal or hexadecimal number prefixed
+ // with 0x.
+ // <flags> - optional flags affecting symbol type, binding or visibility.
+ NewSymbolInfo SI;
+ StringRef Value;
+ std::tie(SI.SymbolName, Value) = FlagValue.split('=');
+ if (Value.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --add-symbol, missing '=' after '%s'",
+ SI.SymbolName.str().c_str());
+
+ if (Value.contains(':')) {
+ std::tie(SI.SectionName, Value) = Value.split(':');
+ if (SI.SectionName.empty() || Value.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --add-symbol, missing section name or symbol value");
+ }
+
+ SmallVector<StringRef, 6> Flags;
+ Value.split(Flags, ',');
+ if (Flags[0].getAsInteger(0, SI.Value))
+ return createStringError(errc::invalid_argument, "bad symbol value: '%s'",
+ Flags[0].str().c_str());
+
+ using Functor = std::function<void()>;
+ SmallVector<StringRef, 6> UnsupportedFlags;
+ for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I)
+ static_cast<Functor>(
+ StringSwitch<Functor>(Flags[I])
+ .CaseLower("global",
+ [&] { SI.Flags.push_back(SymbolFlag::Global); })
+ .CaseLower("local", [&] { SI.Flags.push_back(SymbolFlag::Local); })
+ .CaseLower("weak", [&] { SI.Flags.push_back(SymbolFlag::Weak); })
+ .CaseLower("default",
+ [&] { SI.Flags.push_back(SymbolFlag::Default); })
+ .CaseLower("hidden",
+ [&] { SI.Flags.push_back(SymbolFlag::Hidden); })
+ .CaseLower("protected",
+ [&] { SI.Flags.push_back(SymbolFlag::Protected); })
+ .CaseLower("file", [&] { SI.Flags.push_back(SymbolFlag::File); })
+ .CaseLower("section",
+ [&] { SI.Flags.push_back(SymbolFlag::Section); })
+ .CaseLower("object",
+ [&] { SI.Flags.push_back(SymbolFlag::Object); })
+ .CaseLower("function",
+ [&] { SI.Flags.push_back(SymbolFlag::Function); })
+ .CaseLower(
+ "indirect-function",
+ [&] { SI.Flags.push_back(SymbolFlag::IndirectFunction); })
+ .CaseLower("debug", [&] { SI.Flags.push_back(SymbolFlag::Debug); })
+ .CaseLower("constructor",
+ [&] { SI.Flags.push_back(SymbolFlag::Constructor); })
+ .CaseLower("warning",
+ [&] { SI.Flags.push_back(SymbolFlag::Warning); })
+ .CaseLower("indirect",
+ [&] { SI.Flags.push_back(SymbolFlag::Indirect); })
+ .CaseLower("synthetic",
+ [&] { SI.Flags.push_back(SymbolFlag::Synthetic); })
+ .CaseLower("unique-object",
+ [&] { SI.Flags.push_back(SymbolFlag::UniqueObject); })
+ .StartsWithLower("before=",
+ [&] {
+ StringRef SymNamePart =
+ Flags[I].split('=').second;
+
+ if (!SymNamePart.empty())
+ SI.BeforeSyms.push_back(SymNamePart);
+ })
+ .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))();
+ if (!UnsupportedFlags.empty())
+ return createStringError(errc::invalid_argument,
+ "unsupported flag%s for --add-symbol: '%s'",
+ UnsupportedFlags.size() > 1 ? "s" : "",
+ join(UnsupportedFlags, "', '").c_str());
+
+ return SI;
+}
+
+Expected<const ELFConfig &> ConfigManager::getELFConfig() const {
+ return ELF;
+}
+
+Expected<const COFFConfig &> ConfigManager::getCOFFConfig() const {
+ if (!Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() ||
+ !Common.AllocSectionsPrefix.empty() || !Common.DumpSection.empty() ||
+ !Common.KeepSection.empty() || !Common.SymbolsToGlobalize.empty() ||
+ !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() ||
+ !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() ||
+ !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() ||
+ Common.ExtractDWO || Common.PreserveDates || Common.StripDWO ||
+ Common.StripNonAlloc || Common.StripSections || Common.Weaken ||
+ Common.DecompressDebugSections ||
+ Common.DiscardMode == DiscardType::Locals ||
+ !Common.SymbolsToAdd.empty()) {
+ return createStringError(llvm::errc::invalid_argument,
+ "option not supported by llvm-objcopy for COFF");
+ }
+
+ return COFF;
+}
+
+Expected<const MachOConfig &> ConfigManager::getMachOConfig() const {
+ if (!Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() ||
+ !Common.AllocSectionsPrefix.empty() || !Common.KeepSection.empty() ||
+ !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToKeep.empty() ||
+ !Common.SymbolsToLocalize.empty() || !Common.SymbolsToWeaken.empty() ||
+ !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() ||
+ !Common.UnneededSymbolsToRemove.empty() ||
+ !Common.SetSectionAlignment.empty() || !Common.SetSectionFlags.empty() ||
+ Common.ExtractDWO || Common.PreserveDates || Common.StripAllGNU ||
+ Common.StripDWO || Common.StripNonAlloc || Common.StripSections ||
+ Common.Weaken || Common.DecompressDebugSections || Common.StripUnneeded ||
+ Common.DiscardMode == DiscardType::Locals ||
+ !Common.SymbolsToAdd.empty()) {
+ return createStringError(llvm::errc::invalid_argument,
+ "option not supported by llvm-objcopy for MachO");
+ }
+
+ return MachO;
+}
+
+Expected<const WasmConfig &> ConfigManager::getWasmConfig() const {
+ if (!Common.AddGnuDebugLink.empty() || Common.ExtractPartition ||
+ !Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() ||
+ !Common.AllocSectionsPrefix.empty() ||
+ Common.DiscardMode != DiscardType::None || !Common.SymbolsToAdd.empty() ||
+ !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToLocalize.empty() ||
+ !Common.SymbolsToKeep.empty() || !Common.SymbolsToRemove.empty() ||
+ !Common.UnneededSymbolsToRemove.empty() ||
+ !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() ||
+ !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() ||
+ !Common.SetSectionFlags.empty() || !Common.SymbolsToRename.empty()) {
+ return createStringError(
+ llvm::errc::invalid_argument,
+ "only flags for section dumping, removal, and addition are supported");
+ }
+
+ return Wasm;
+}
+
+// ParseObjcopyOptions returns the config and sets the input arguments. If a
+// help flag is set then ParseObjcopyOptions will print the help messege and
+// exit.
+Expected<DriverConfig>
+objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
+ function_ref<Error(Error)> ErrorCallback) {
+ DriverConfig DC;
+ ObjcopyOptTable T;
+
+ const char *const *DashDash =
+ std::find_if(RawArgsArr.begin(), RawArgsArr.end(),
+ [](StringRef Str) { return Str == "--"; });
+ ArrayRef<const char *> ArgsArr = makeArrayRef(RawArgsArr.begin(), DashDash);
+ if (DashDash != RawArgsArr.end())
+ DashDash = std::next(DashDash);
+
+ unsigned MissingArgumentIndex, MissingArgumentCount;
+ llvm::opt::InputArgList InputArgs =
+ T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+ if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) {
+ printHelp(T, errs(), ToolType::Objcopy);
+ exit(1);
+ }
+
+ if (InputArgs.hasArg(OBJCOPY_help)) {
+ printHelp(T, outs(), ToolType::Objcopy);
+ 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))
+ return createStringError(errc::invalid_argument, "unknown argument '%s'",
+ Arg->getAsString(InputArgs).c_str());
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT))
+ Positional.push_back(Arg->getValue());
+ std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional));
+
+ if (Positional.empty())
+ return createStringError(errc::invalid_argument, "no input file specified");
+
+ if (Positional.size() > 2)
+ return createStringError(errc::invalid_argument,
+ "too many positional arguments");
+
+ ConfigManager ConfigMgr;
+ CommonConfig &Config = ConfigMgr.Common;
+ COFFConfig &COFFConfig = ConfigMgr.COFF;
+ ELFConfig &ELFConfig = ConfigMgr.ELF;
+ MachOConfig &MachOConfig = ConfigMgr.MachO;
+ 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)))
+ return createStringError(
+ errc::invalid_argument,
+ "--target cannot be used with --input-target or --output-target");
+
+ if (InputArgs.hasArg(OBJCOPY_regex) && InputArgs.hasArg(OBJCOPY_wildcard))
+ return createStringError(errc::invalid_argument,
+ "--regex and --wildcard are incompatible");
+
+ MatchStyle SectionMatchStyle = InputArgs.hasArg(OBJCOPY_regex)
+ ? MatchStyle::Regex
+ : MatchStyle::Wildcard;
+ MatchStyle SymbolMatchStyle = InputArgs.hasArg(OBJCOPY_regex)
+ ? MatchStyle::Regex
+ : InputArgs.hasArg(OBJCOPY_wildcard)
+ ? MatchStyle::Wildcard
+ : MatchStyle::Literal;
+ StringRef InputFormat, OutputFormat;
+ if (InputArgs.hasArg(OBJCOPY_target)) {
+ InputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
+ OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
+ } else {
+ InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target);
+ OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target);
+ }
+
+ // FIXME: Currently, we ignore the target for non-binary/ihex formats
+ // explicitly specified by -I option (e.g. -Ielf32-x86-64) and guess the
+ // format by llvm::object::createBinary regardless of the option value.
+ Config.InputFormat = StringSwitch<FileFormat>(InputFormat)
+ .Case("binary", FileFormat::Binary)
+ .Case("ihex", FileFormat::IHex)
+ .Default(FileFormat::Unspecified);
+
+ if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) {
+ const uint8_t Invalid = 0xff;
+ StringRef VisibilityStr =
+ InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility);
+
+ ELFConfig.NewSymbolVisibility = StringSwitch<uint8_t>(VisibilityStr)
+ .Case("default", ELF::STV_DEFAULT)
+ .Case("hidden", ELF::STV_HIDDEN)
+ .Case("internal", ELF::STV_INTERNAL)
+ .Case("protected", ELF::STV_PROTECTED)
+ .Default(Invalid);
+
+ if (ELFConfig.NewSymbolVisibility == Invalid)
+ return createStringError(errc::invalid_argument,
+ "'%s' is not a valid symbol visibility",
+ VisibilityStr.str().c_str());
+ }
+
+ for (const auto *Arg : InputArgs.filtered(OBJCOPY_subsystem)) {
+ StringRef Subsystem, Version;
+ std::tie(Subsystem, Version) = StringRef(Arg->getValue()).split(':');
+ COFFConfig.Subsystem =
+ StringSwitch<unsigned>(Subsystem.lower())
+ .Case("boot_application",
+ COFF::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
+ .Case("console", COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI)
+ .Case("efi_application", COFF::IMAGE_SUBSYSTEM_EFI_APPLICATION)
+ .Case("efi_boot_service_driver",
+ COFF::IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER)
+ .Case("efi_rom", COFF::IMAGE_SUBSYSTEM_EFI_ROM)
+ .Case("efi_runtime_driver",
+ COFF::IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)
+ .Case("native", COFF::IMAGE_SUBSYSTEM_NATIVE)
+ .Case("posix", COFF::IMAGE_SUBSYSTEM_POSIX_CUI)
+ .Case("windows", COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI)
+ .Default(COFF::IMAGE_SUBSYSTEM_UNKNOWN);
+ if (*COFFConfig.Subsystem == COFF::IMAGE_SUBSYSTEM_UNKNOWN)
+ return createStringError(errc::invalid_argument,
+ "'%s' is not a valid subsystem",
+ Subsystem.str().c_str());
+ if (!Version.empty()) {
+ StringRef Major, Minor;
+ std::tie(Major, Minor) = Version.split('.');
+ unsigned Number;
+ if (Major.getAsInteger(10, Number))
+ return createStringError(errc::invalid_argument,
+ "'%s' is not a valid subsystem major version",
+ Major.str().c_str());
+ COFFConfig.MajorSubsystemVersion = Number;
+ Number = 0;
+ if (!Minor.empty() && Minor.getAsInteger(10, Number))
+ return createStringError(errc::invalid_argument,
+ "'%s' is not a valid subsystem minor version",
+ Minor.str().c_str());
+ COFFConfig.MinorSubsystemVersion = Number;
+ }
+ }
+
+ Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat)
+ .Case("binary", FileFormat::Binary)
+ .Case("ihex", FileFormat::IHex)
+ .Default(FileFormat::Unspecified);
+ if (Config.OutputFormat == FileFormat::Unspecified) {
+ if (OutputFormat.empty()) {
+ Config.OutputFormat = Config.InputFormat;
+ } else {
+ Expected<TargetInfo> Target =
+ getOutputTargetInfoByTargetName(OutputFormat);
+ if (!Target)
+ return Target.takeError();
+ Config.OutputFormat = Target->Format;
+ Config.OutputArch = Target->Machine;
+ }
+ }
+
+ 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)
+ return createStringError(
+ errc::invalid_argument,
+ "invalid or unsupported --compress-debug-sections format: %s",
+ InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq)
+ .str()
+ .c_str());
+ }
+ if (!zlib::isAvailable())
+ return createStringError(
+ errc::invalid_argument,
+ "LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress");
+ }
+
+ Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink);
+ // The gnu_debuglink's target is expected to not change or else its CRC would
+ // become invalidated and get rejected. We can avoid recalculating the
+ // checksum for every target file inside an archive by precomputing the CRC
+ // here. This prevents a significant amount of I/O.
+ if (!Config.AddGnuDebugLink.empty()) {
+ auto DebugOrErr = MemoryBuffer::getFile(Config.AddGnuDebugLink);
+ if (!DebugOrErr)
+ return createFileError(Config.AddGnuDebugLink, DebugOrErr.getError());
+ auto Debug = std::move(*DebugOrErr);
+ Config.GnuDebugLinkCRC32 =
+ llvm::crc32(arrayRefFromStringRef(Debug->getBuffer()));
+ }
+ Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo);
+ Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols);
+ Config.AllocSectionsPrefix =
+ InputArgs.getLastArgValue(OBJCOPY_prefix_alloc_sections);
+ if (auto Arg = InputArgs.getLastArg(OBJCOPY_extract_partition))
+ Config.ExtractPartition = Arg->getValue();
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) {
+ if (!StringRef(Arg->getValue()).contains('='))
+ return createStringError(errc::invalid_argument,
+ "bad format for --redefine-sym");
+ auto Old2New = StringRef(Arg->getValue()).split('=');
+ if (!Config.SymbolsToRename.insert(Old2New).second)
+ return createStringError(errc::invalid_argument,
+ "multiple redefinition of symbol '%s'",
+ Old2New.first.str().c_str());
+ }
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbols))
+ if (Error E = addSymbolsToRenameFromFile(Config.SymbolsToRename, DC.Alloc,
+ Arg->getValue()))
+ return std::move(E);
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) {
+ Expected<SectionRename> SR =
+ parseRenameSectionValue(StringRef(Arg->getValue()));
+ if (!SR)
+ return SR.takeError();
+ if (!Config.SectionsToRename.try_emplace(SR->OriginalName, *SR).second)
+ return createStringError(errc::invalid_argument,
+ "multiple renames of section '%s'",
+ SR->OriginalName.str().c_str());
+ }
+ for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_alignment)) {
+ Expected<std::pair<StringRef, uint64_t>> NameAndAlign =
+ parseSetSectionAlignment(Arg->getValue());
+ if (!NameAndAlign)
+ return NameAndAlign.takeError();
+ Config.SetSectionAlignment[NameAndAlign->first] = NameAndAlign->second;
+ }
+ for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_flags)) {
+ Expected<SectionFlagsUpdate> SFU =
+ parseSetSectionFlagValue(Arg->getValue());
+ if (!SFU)
+ return SFU.takeError();
+ if (!Config.SetSectionFlags.try_emplace(SFU->Name, *SFU).second)
+ return createStringError(
+ errc::invalid_argument,
+ "--set-section-flags set multiple times for section '%s'",
+ SFU->Name.str().c_str());
+ }
+ // Prohibit combinations of --set-section-flags when the section name is used
+ // by --rename-section, either as a source or a destination.
+ for (const auto &E : Config.SectionsToRename) {
+ const SectionRename &SR = E.second;
+ if (Config.SetSectionFlags.count(SR.OriginalName))
+ return createStringError(
+ errc::invalid_argument,
+ "--set-section-flags=%s conflicts with --rename-section=%s=%s",
+ SR.OriginalName.str().c_str(), SR.OriginalName.str().c_str(),
+ SR.NewName.str().c_str());
+ if (Config.SetSectionFlags.count(SR.NewName))
+ return createStringError(
+ errc::invalid_argument,
+ "--set-section-flags=%s conflicts with --rename-section=%s=%s",
+ SR.NewName.str().c_str(), SR.OriginalName.str().c_str(),
+ SR.NewName.str().c_str());
+ }
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section))
+ if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SectionMatchStyle, ErrorCallback)))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section))
+ if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SectionMatchStyle, ErrorCallback)))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_only_section))
+ if (Error E = Config.OnlySection.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SectionMatchStyle, ErrorCallback)))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) {
+ StringRef ArgValue(Arg->getValue());
+ if (!ArgValue.contains('='))
+ return createStringError(errc::invalid_argument,
+ "bad format for --add-section: missing '='");
+ if (ArgValue.split("=").second.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --add-section: missing file name");
+ Config.AddSection.push_back(ArgValue);
+ }
+ for (auto Arg : InputArgs.filtered(OBJCOPY_update_section)) {
+ StringRef ArgValue(Arg->getValue());
+ if (!ArgValue.contains('='))
+ return createStringError(errc::invalid_argument,
+ "bad format for --update-section: missing '='");
+ if (ArgValue.split("=").second.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --update-section: missing file name");
+ Config.UpdateSection.push_back(ArgValue);
+ }
+ for (auto *Arg : InputArgs.filtered(OBJCOPY_dump_section)) {
+ StringRef Value(Arg->getValue());
+ if (Value.split('=').second.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --dump-section, expected section=file");
+ Config.DumpSection.push_back(Value);
+ }
+ 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.ExtractMainPartition =
+ InputArgs.hasArg(OBJCOPY_extract_main_partition);
+ ELFConfig.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden);
+ Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken);
+ if (InputArgs.hasArg(OBJCOPY_discard_all, OBJCOPY_discard_locals))
+ Config.DiscardMode =
+ InputArgs.hasFlag(OBJCOPY_discard_all, OBJCOPY_discard_locals)
+ ? DiscardType::All
+ : DiscardType::Locals;
+ Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug);
+ ELFConfig.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols);
+ MachOConfig.KeepUndefined = InputArgs.hasArg(OBJCOPY_keep_undefined);
+ Config.DecompressDebugSections =
+ InputArgs.hasArg(OBJCOPY_decompress_debug_sections);
+ if (Config.DiscardMode == DiscardType::All) {
+ Config.StripDebug = true;
+ ELFConfig.KeepFileSymbols = true;
+ }
+ for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol))
+ if (Error E = Config.SymbolsToLocalize.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbols))
+ if (Error E = addSymbolsFromFile(Config.SymbolsToLocalize, DC.Alloc,
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol))
+ if (Error E = Config.SymbolsToKeepGlobal.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols))
+ if (Error E = addSymbolsFromFile(Config.SymbolsToKeepGlobal, DC.Alloc,
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol))
+ if (Error E = Config.SymbolsToGlobalize.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbols))
+ if (Error E = addSymbolsFromFile(Config.SymbolsToGlobalize, DC.Alloc,
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol))
+ if (Error E = Config.SymbolsToWeaken.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbols))
+ if (Error E = addSymbolsFromFile(Config.SymbolsToWeaken, DC.Alloc,
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol))
+ if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbols))
+ if (Error E = addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc,
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol))
+ if (Error E =
+ Config.UnneededSymbolsToRemove.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols))
+ if (Error E = addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc,
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol))
+ if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbols))
+ if (Error E =
+ addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(),
+ SymbolMatchStyle, ErrorCallback))
+ return std::move(E);
+ for (auto *Arg : InputArgs.filtered(OBJCOPY_add_symbol)) {
+ Expected<NewSymbolInfo> SymInfo = parseNewSymbolInfo(Arg->getValue());
+ if (!SymInfo)
+ return SymInfo.takeError();
+
+ Config.SymbolsToAdd.push_back(*SymInfo);
+ }
+
+ ELFConfig.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links);
+
+ Config.DeterministicArchives = InputArgs.hasFlag(
+ OBJCOPY_enable_deterministic_archives,
+ OBJCOPY_disable_deterministic_archives, /*default=*/true);
+
+ Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates);
+
+ if (Config.PreserveDates &&
+ (Config.OutputFilename == "-" || Config.InputFilename == "-"))
+ return createStringError(errc::invalid_argument,
+ "--preserve-dates requires a file");
+
+ for (auto Arg : InputArgs)
+ if (Arg->getOption().matches(OBJCOPY_set_start)) {
+ auto EAddr = getAsInteger<uint64_t>(Arg->getValue());
+ if (!EAddr)
+ return createStringError(
+ EAddr.getError(), "bad entry point address: '%s'", Arg->getValue());
+
+ ELFConfig.EntryExpr = [EAddr](uint64_t) { return *EAddr; };
+ } else if (Arg->getOption().matches(OBJCOPY_change_start)) {
+ auto EIncr = getAsInteger<int64_t>(Arg->getValue());
+ if (!EIncr)
+ return createStringError(EIncr.getError(),
+ "bad entry point increment: '%s'",
+ Arg->getValue());
+ auto Expr = ELFConfig.EntryExpr ? std::move(ELFConfig.EntryExpr)
+ : [](uint64_t A) { return A; };
+ ELFConfig.EntryExpr = [Expr, EIncr](uint64_t EAddr) {
+ return Expr(EAddr) + *EIncr;
+ };
+ }
+
+ if (Config.DecompressDebugSections &&
+ Config.CompressionType != DebugCompressionType::None) {
+ return createStringError(
+ errc::invalid_argument,
+ "cannot specify both --compress-debug-sections and "
+ "--decompress-debug-sections");
+ }
+
+ if (Config.DecompressDebugSections && !zlib::isAvailable())
+ return createStringError(
+ errc::invalid_argument,
+ "LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress");
+
+ if (Config.ExtractPartition && Config.ExtractMainPartition)
+ return createStringError(errc::invalid_argument,
+ "cannot specify --extract-partition together with "
+ "--extract-main-partition");
+
+ DC.CopyConfigs.push_back(std::move(ConfigMgr));
+ return std::move(DC);
+}
+
+// ParseInstallNameToolOptions returns the config and sets the input arguments.
+// If a help flag is set then ParseInstallNameToolOptions will print the help
+// messege and exit.
+Expected<DriverConfig>
+objcopy::parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) {
+ DriverConfig DC;
+ ConfigManager ConfigMgr;
+ CommonConfig &Config = ConfigMgr.Common;
+ MachOConfig &MachOConfig = ConfigMgr.MachO;
+ InstallNameToolOptTable T;
+ unsigned MissingArgumentIndex, MissingArgumentCount;
+ llvm::opt::InputArgList InputArgs =
+ T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+ if (MissingArgumentCount)
+ return createStringError(
+ errc::invalid_argument,
+ "missing argument to " +
+ StringRef(InputArgs.getArgString(MissingArgumentIndex)) +
+ " option");
+
+ if (InputArgs.size() == 0) {
+ printHelp(T, errs(), ToolType::InstallNameTool);
+ exit(1);
+ }
+
+ if (InputArgs.hasArg(INSTALL_NAME_TOOL_help)) {
+ printHelp(T, outs(), ToolType::InstallNameTool);
+ exit(0);
+ }
+
+ if (InputArgs.hasArg(INSTALL_NAME_TOOL_version)) {
+ outs() << "llvm-install-name-tool, compatible with cctools "
+ "install_name_tool\n";
+ cl::PrintVersionMessage();
+ exit(0);
+ }
+
+ for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_add_rpath))
+ MachOConfig.RPathToAdd.push_back(Arg->getValue());
+
+ for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_prepend_rpath))
+ MachOConfig.RPathToPrepend.push_back(Arg->getValue());
+
+ for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_delete_rpath)) {
+ StringRef RPath = Arg->getValue();
+
+ // Cannot add and delete the same rpath at the same time.
+ if (is_contained(MachOConfig.RPathToAdd, RPath))
+ return createStringError(
+ errc::invalid_argument,
+ "cannot specify both -add_rpath '%s' and -delete_rpath '%s'",
+ RPath.str().c_str(), RPath.str().c_str());
+ if (is_contained(MachOConfig.RPathToPrepend, RPath))
+ return createStringError(
+ errc::invalid_argument,
+ "cannot specify both -prepend_rpath '%s' and -delete_rpath '%s'",
+ RPath.str().c_str(), RPath.str().c_str());
+
+ MachOConfig.RPathsToRemove.insert(RPath);
+ }
+
+ for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_rpath)) {
+ StringRef Old = Arg->getValue(0);
+ StringRef New = Arg->getValue(1);
+
+ auto Match = [=](StringRef RPath) { return RPath == Old || RPath == New; };
+
+ // Cannot specify duplicate -rpath entries
+ auto It1 = find_if(
+ MachOConfig.RPathsToUpdate,
+ [&Match](const DenseMap<StringRef, StringRef>::value_type &OldNew) {
+ return Match(OldNew.getFirst()) || Match(OldNew.getSecond());
+ });
+ if (It1 != MachOConfig.RPathsToUpdate.end())
+ return createStringError(errc::invalid_argument,
+ "cannot specify both -rpath '" +
+ It1->getFirst() + "' '" + It1->getSecond() +
+ "' and -rpath '" + Old + "' '" + New + "'");
+
+ // Cannot specify the same rpath under both -delete_rpath and -rpath
+ auto It2 = find_if(MachOConfig.RPathsToRemove, Match);
+ if (It2 != MachOConfig.RPathsToRemove.end())
+ return createStringError(errc::invalid_argument,
+ "cannot specify both -delete_rpath '" + *It2 +
+ "' and -rpath '" + Old + "' '" + New + "'");
+
+ // Cannot specify the same rpath under both -add_rpath and -rpath
+ auto It3 = find_if(MachOConfig.RPathToAdd, Match);
+ if (It3 != MachOConfig.RPathToAdd.end())
+ return createStringError(errc::invalid_argument,
+ "cannot specify both -add_rpath '" + *It3 +
+ "' and -rpath '" + Old + "' '" + New + "'");
+
+ // Cannot specify the same rpath under both -prepend_rpath and -rpath.
+ auto It4 = find_if(MachOConfig.RPathToPrepend, Match);
+ if (It4 != MachOConfig.RPathToPrepend.end())
+ return createStringError(errc::invalid_argument,
+ "cannot specify both -prepend_rpath '" + *It4 +
+ "' and -rpath '" + Old + "' '" + New + "'");
+
+ MachOConfig.RPathsToUpdate.insert({Old, New});
+ }
+
+ if (auto *Arg = InputArgs.getLastArg(INSTALL_NAME_TOOL_id)) {
+ MachOConfig.SharedLibId = Arg->getValue();
+ if (MachOConfig.SharedLibId->empty())
+ return createStringError(errc::invalid_argument,
+ "cannot specify an empty id");
+ }
+
+ for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_change))
+ MachOConfig.InstallNamesToUpdate.insert(
+ {Arg->getValue(0), Arg->getValue(1)});
+
+ MachOConfig.RemoveAllRpaths =
+ InputArgs.hasArg(INSTALL_NAME_TOOL_delete_all_rpaths);
+
+ SmallVector<StringRef, 2> Positional;
+ for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_UNKNOWN))
+ return createStringError(errc::invalid_argument, "unknown argument '%s'",
+ Arg->getAsString(InputArgs).c_str());
+ for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_INPUT))
+ Positional.push_back(Arg->getValue());
+ if (Positional.empty())
+ return createStringError(errc::invalid_argument, "no input file specified");
+ if (Positional.size() > 1)
+ return createStringError(
+ errc::invalid_argument,
+ "llvm-install-name-tool expects a single input file");
+ Config.InputFilename = Positional[0];
+ Config.OutputFilename = Positional[0];
+
+ DC.CopyConfigs.push_back(std::move(ConfigMgr));
+ return std::move(DC);
+}
+
+Expected<DriverConfig>
+objcopy::parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) {
+ DriverConfig DC;
+ ConfigManager ConfigMgr;
+ CommonConfig &Config = ConfigMgr.Common;
+ BitcodeStripOptTable T;
+ unsigned MissingArgumentIndex, MissingArgumentCount;
+ opt::InputArgList InputArgs =
+ T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+ if (InputArgs.size() == 0) {
+ printHelp(T, errs(), ToolType::BitcodeStrip);
+ exit(1);
+ }
+
+ if (InputArgs.hasArg(BITCODE_STRIP_help)) {
+ printHelp(T, outs(), ToolType::BitcodeStrip);
+ exit(0);
+ }
+
+ if (InputArgs.hasArg(BITCODE_STRIP_version)) {
+ outs() << "llvm-bitcode-strip, compatible with cctools "
+ "bitcode_strip\n";
+ cl::PrintVersionMessage();
+ exit(0);
+ }
+
+ for (auto *Arg : InputArgs.filtered(BITCODE_STRIP_UNKNOWN))
+ return createStringError(errc::invalid_argument, "unknown argument '%s'",
+ Arg->getAsString(InputArgs).c_str());
+
+ SmallVector<StringRef, 2> Positional;
+ for (auto *Arg : InputArgs.filtered(BITCODE_STRIP_INPUT))
+ Positional.push_back(Arg->getValue());
+ if (Positional.size() > 1)
+ return createStringError(errc::invalid_argument,
+ "llvm-bitcode-strip expects a single input file");
+ assert(!Positional.empty());
+ Config.InputFilename = Positional[0];
+ Config.OutputFilename = Positional[0];
+
+ DC.CopyConfigs.push_back(std::move(ConfigMgr));
+ return std::move(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.
+Expected<DriverConfig>
+objcopy::parseStripOptions(ArrayRef<const char *> RawArgsArr,
+ function_ref<Error(Error)> ErrorCallback) {
+ const char *const *DashDash =
+ std::find_if(RawArgsArr.begin(), RawArgsArr.end(),
+ [](StringRef Str) { return Str == "--"; });
+ ArrayRef<const char *> ArgsArr = makeArrayRef(RawArgsArr.begin(), DashDash);
+ if (DashDash != RawArgsArr.end())
+ DashDash = std::next(DashDash);
+
+ StripOptTable T;
+ unsigned MissingArgumentIndex, MissingArgumentCount;
+ llvm::opt::InputArgList InputArgs =
+ T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+ if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) {
+ printHelp(T, errs(), ToolType::Strip);
+ exit(1);
+ }
+
+ if (InputArgs.hasArg(STRIP_help)) {
+ printHelp(T, outs(), ToolType::Strip);
+ exit(0);
+ }
+
+ if (InputArgs.hasArg(STRIP_version)) {
+ outs() << "llvm-strip, compatible with GNU strip\n";
+ cl::PrintVersionMessage();
+ exit(0);
+ }
+
+ SmallVector<StringRef, 2> Positional;
+ for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN))
+ return createStringError(errc::invalid_argument, "unknown argument '%s'",
+ Arg->getAsString(InputArgs).c_str());
+ for (auto Arg : InputArgs.filtered(STRIP_INPUT))
+ Positional.push_back(Arg->getValue());
+ std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional));
+
+ if (Positional.empty())
+ return createStringError(errc::invalid_argument, "no input file specified");
+
+ if (Positional.size() > 1 && InputArgs.hasArg(STRIP_output))
+ return createStringError(
+ errc::invalid_argument,
+ "multiple input files cannot be used in combination with -o");
+
+ ConfigManager ConfigMgr;
+ CommonConfig &Config = ConfigMgr.Common;
+ ELFConfig &ELFConfig = ConfigMgr.ELF;
+ MachOConfig &MachOConfig = ConfigMgr.MachO;
+
+ if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard))
+ return createStringError(errc::invalid_argument,
+ "--regex and --wildcard are incompatible");
+ MatchStyle SectionMatchStyle =
+ InputArgs.hasArg(STRIP_regex) ? MatchStyle::Regex : MatchStyle::Wildcard;
+ MatchStyle SymbolMatchStyle = InputArgs.hasArg(STRIP_regex)
+ ? MatchStyle::Regex
+ : InputArgs.hasArg(STRIP_wildcard)
+ ? MatchStyle::Wildcard
+ : MatchStyle::Literal;
+ ELFConfig.AllowBrokenLinks = InputArgs.hasArg(STRIP_allow_broken_links);
+ Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug);
+
+ if (InputArgs.hasArg(STRIP_discard_all, STRIP_discard_locals))
+ Config.DiscardMode =
+ InputArgs.hasFlag(STRIP_discard_all, STRIP_discard_locals)
+ ? DiscardType::All
+ : DiscardType::Locals;
+ Config.StripSections = InputArgs.hasArg(STRIP_strip_sections);
+ Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded);
+ if (auto Arg = InputArgs.getLastArg(STRIP_strip_all, STRIP_no_strip_all))
+ Config.StripAll = Arg->getOption().getID() == STRIP_strip_all;
+ Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu);
+ MachOConfig.StripSwiftSymbols = InputArgs.hasArg(STRIP_strip_swift_symbols);
+ Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug);
+ ELFConfig.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols);
+ MachOConfig.KeepUndefined = InputArgs.hasArg(STRIP_keep_undefined);
+
+ for (auto Arg : InputArgs.filtered(STRIP_keep_section))
+ if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SectionMatchStyle, ErrorCallback)))
+ return std::move(E);
+
+ for (auto Arg : InputArgs.filtered(STRIP_remove_section))
+ if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SectionMatchStyle, ErrorCallback)))
+ return std::move(E);
+
+ for (auto Arg : InputArgs.filtered(STRIP_strip_symbol))
+ if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
+
+ for (auto Arg : InputArgs.filtered(STRIP_keep_symbol))
+ if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
+
+ if (!InputArgs.hasArg(STRIP_no_strip_all) && !Config.StripDebug &&
+ !Config.StripUnneeded && Config.DiscardMode == DiscardType::None &&
+ !Config.StripAllGNU && Config.SymbolsToRemove.empty())
+ Config.StripAll = true;
+
+ if (Config.DiscardMode == DiscardType::All) {
+ Config.StripDebug = true;
+ ELFConfig.KeepFileSymbols = true;
+ }
+
+ Config.DeterministicArchives =
+ InputArgs.hasFlag(STRIP_enable_deterministic_archives,
+ STRIP_disable_deterministic_archives, /*default=*/true);
+
+ Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates);
+ Config.InputFormat = FileFormat::Unspecified;
+ Config.OutputFormat = FileFormat::Unspecified;
+
+ DriverConfig DC;
+ if (Positional.size() == 1) {
+ Config.InputFilename = Positional[0];
+ Config.OutputFilename =
+ InputArgs.getLastArgValue(STRIP_output, Positional[0]);
+ DC.CopyConfigs.push_back(std::move(ConfigMgr));
+ } else {
+ StringMap<unsigned> InputFiles;
+ for (StringRef Filename : Positional) {
+ if (InputFiles[Filename]++ == 1) {
+ if (Filename == "-")
+ return createStringError(
+ errc::invalid_argument,
+ "cannot specify '-' as an input file more than once");
+ if (Error E = ErrorCallback(createStringError(
+ errc::invalid_argument, "'%s' was already specified",
+ Filename.str().c_str())))
+ return std::move(E);
+ }
+ Config.InputFilename = Filename;
+ Config.OutputFilename = Filename;
+ DC.CopyConfigs.push_back(ConfigMgr);
+ }
+ }
+
+ if (Config.PreserveDates && (is_contained(Positional, "-") ||
+ InputArgs.getLastArgValue(STRIP_output) == "-"))
+ return createStringError(errc::invalid_argument,
+ "--preserve-dates requires a file");
+
+ return std::move(DC);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/ConfigManager.h b/contrib/libs/llvm14/tools/llvm-objcopy/ConfigManager.h
new file mode 100644
index 00000000000..c0d0e8bbc72
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/ConfigManager.h
@@ -0,0 +1,80 @@
+//===- ConfigManager.h ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H
+
+#include "COFF/COFFConfig.h"
+#include "CommonConfig.h"
+#include "ELF/ELFConfig.h"
+#include "MachO/MachOConfig.h"
+#include "MultiFormatConfig.h"
+#include "wasm/WasmConfig.h"
+#include "llvm/Support/Allocator.h"
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+
+// ConfigManager keeps all configurations and prepare
+// format-specific options.
+struct ConfigManager : public MultiFormatConfig {
+ virtual ~ConfigManager() {}
+
+ const CommonConfig &getCommonConfig() const override { return Common; }
+ Expected<const ELFConfig &> getELFConfig() const override;
+ Expected<const COFFConfig &> getCOFFConfig() const override;
+ Expected<const MachOConfig &> getMachOConfig() const override;
+ Expected<const WasmConfig &> getWasmConfig() const override;
+
+ // All configs.
+ CommonConfig Common;
+ ELFConfig ELF;
+ COFFConfig COFF;
+ MachOConfig MachO;
+ WasmConfig Wasm;
+};
+
+// 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<ConfigManager, 1> CopyConfigs;
+ BumpPtrAllocator Alloc;
+};
+
+// ParseObjcopyOptions returns the config and sets the input arguments. If a
+// help flag is set then ParseObjcopyOptions will print the help messege and
+// exit. ErrorCallback is used to handle recoverable errors. An Error returned
+// by the callback aborts the parsing and is then returned by this function.
+Expected<DriverConfig>
+parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
+ llvm::function_ref<Error(Error)> ErrorCallback);
+
+// ParseInstallNameToolOptions returns the config and sets the input arguments.
+// If a help flag is set then ParseInstallNameToolOptions will print the help
+// messege and exit.
+Expected<DriverConfig>
+parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr);
+
+// ParseBitcodeStripOptions returns the config and sets the input arguments.
+// If a help flag is set then ParseBitcodeStripOptions will print the help
+// messege and exit.
+Expected<DriverConfig> parseBitcodeStripOptions(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. ErrorCallback is used to handle recoverable errors. An Error returned
+// by the callback aborts the parsing and is then returned by this function.
+Expected<DriverConfig>
+parseStripOptions(ArrayRef<const char *> ArgsArr,
+ llvm::function_ref<Error(Error)> ErrorCallback);
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFConfig.h b/contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFConfig.h
new file mode 100644
index 00000000000..229a8d61fb8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFConfig.h
@@ -0,0 +1,38 @@
+//===- ELFConfig.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/ELFTypes.h"
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+
+// ELF specific configuration for copying/stripping a single file.
+struct ELFConfig {
+ uint8_t NewSymbolVisibility = (uint8_t)ELF::STV_DEFAULT;
+
+ // ELF entry point address expression. The input parameter is an entry point
+ // address in the input ELF file. The entry address in the output file is
+ // calculated with EntryExpr(input_address), when either --set-start or
+ // --change-start is used.
+ std::function<uint64_t(uint64_t)> EntryExpr;
+
+ bool AllowBrokenLinks = false;
+ bool KeepFileSymbols = false;
+ bool LocalizeHidden = false;
+};
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
new file mode 100644
index 00000000000..f8521fa0d5b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
@@ -0,0 +1,833 @@
+//===- ELFObjcopy.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ELFObjcopy.h"
+#include "CommonConfig.h"
+#include "ELFConfig.h"
+#include "Object.h"
+#include "llvm-objcopy.h"
+#include "llvm/ADT/BitmaskEnum.h"
+#include "llvm/ADT/DenseSet.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/FileSystem.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>
+
+using namespace llvm;
+using namespace llvm::ELF;
+using namespace llvm::objcopy;
+using namespace llvm::objcopy::elf;
+using namespace llvm::object;
+
+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 uint64_t getNewShfFlags(SectionFlag AllFlags) {
+ uint64_t NewFlags = 0;
+ if (AllFlags & SectionFlag::SecAlloc)
+ NewFlags |= ELF::SHF_ALLOC;
+ if (!(AllFlags & SectionFlag::SecReadonly))
+ NewFlags |= ELF::SHF_WRITE;
+ if (AllFlags & SectionFlag::SecCode)
+ NewFlags |= ELF::SHF_EXECINSTR;
+ if (AllFlags & SectionFlag::SecMerge)
+ NewFlags |= ELF::SHF_MERGE;
+ if (AllFlags & SectionFlag::SecStrings)
+ NewFlags |= ELF::SHF_STRINGS;
+ if (AllFlags & SectionFlag::SecExclude)
+ NewFlags |= ELF::SHF_EXCLUDE;
+ return NewFlags;
+}
+
+static uint64_t getSectionFlagsPreserveMask(uint64_t OldFlags,
+ uint64_t NewFlags) {
+ // 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_GROUP | ELF::SHF_LINK_ORDER |
+ ELF::SHF_MASKOS | ELF::SHF_MASKPROC | ELF::SHF_TLS |
+ ELF::SHF_INFO_LINK) &
+ ~ELF::SHF_EXCLUDE;
+ return (OldFlags & PreserveMask) | (NewFlags & ~PreserveMask);
+}
+
+static void setSectionFlagsAndType(SectionBase &Sec, SectionFlag Flags) {
+ Sec.Flags = getSectionFlagsPreserveMask(Sec.Flags, getNewShfFlags(Flags));
+
+ // In GNU objcopy, certain flags promote SHT_NOBITS to SHT_PROGBITS. This rule
+ // may promote more non-ALLOC sections than GNU objcopy, but it is fine as
+ // non-ALLOC SHT_NOBITS sections do not make much sense.
+ if (Sec.Type == SHT_NOBITS &&
+ (!(Sec.Flags & ELF::SHF_ALLOC) ||
+ Flags & (SectionFlag::SecContents | SectionFlag::SecLoad)))
+ Sec.Type = SHT_PROGBITS;
+}
+
+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> createELFWriter(const CommonConfig &Config,
+ Object &Obj, raw_ostream &Out,
+ ElfType OutputElfType) {
+ // Depending on the initial ELFT and OutputFormat we need a different Writer.
+ switch (OutputElfType) {
+ case ELFT_ELF32LE:
+ return std::make_unique<ELFWriter<ELF32LE>>(Obj, Out, !Config.StripSections,
+ Config.OnlyKeepDebug);
+ case ELFT_ELF64LE:
+ return std::make_unique<ELFWriter<ELF64LE>>(Obj, Out, !Config.StripSections,
+ Config.OnlyKeepDebug);
+ case ELFT_ELF32BE:
+ return std::make_unique<ELFWriter<ELF32BE>>(Obj, Out, !Config.StripSections,
+ Config.OnlyKeepDebug);
+ case ELFT_ELF64BE:
+ return std::make_unique<ELFWriter<ELF64BE>>(Obj, Out, !Config.StripSections,
+ Config.OnlyKeepDebug);
+ }
+ llvm_unreachable("Invalid output format");
+}
+
+static std::unique_ptr<Writer> createWriter(const CommonConfig &Config,
+ Object &Obj, raw_ostream &Out,
+ ElfType OutputElfType) {
+ switch (Config.OutputFormat) {
+ case FileFormat::Binary:
+ return std::make_unique<BinaryWriter>(Obj, Out);
+ case FileFormat::IHex:
+ return std::make_unique<IHexWriter>(Obj, Out);
+ default:
+ return createELFWriter(Config, Obj, Out, OutputElfType);
+ }
+}
+
+template <class... Ts>
+static Error makeStringError(std::error_code EC, const Twine &Msg,
+ Ts &&... Args) {
+ std::string FullMsg = (EC.message() + ": " + Msg).str();
+ return createStringError(EC, FullMsg.c_str(), std::forward<Ts>(Args)...);
+}
+
+static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
+ Object &Obj) {
+ for (auto &Sec : Obj.sections()) {
+ if (Sec.Name == SecName) {
+ if (Sec.Type == SHT_NOBITS)
+ return createStringError(object_error::parse_failed,
+ "cannot dump section '%s': it has no contents",
+ SecName.str().c_str());
+ 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 createStringError(object_error::parse_failed, "section '%s' not found",
+ SecName.str().c_str());
+}
+
+static bool isCompressable(const SectionBase &Sec) {
+ return !(Sec.Flags & ELF::SHF_COMPRESSED) &&
+ StringRef(Sec.Name).startswith(".debug");
+}
+
+static Error replaceDebugSections(
+ Object &Obj, function_ref<bool(const SectionBase &)> ShouldReplace,
+ function_ref<Expected<SectionBase *>(const SectionBase *)> AddSection) {
+ // Build a list of the debug sections we are going to replace.
+ // We can't call `AddSection` while iterating over sections,
+ // because it would mutate the sections array.
+ SmallVector<SectionBase *, 13> ToReplace;
+ for (auto &Sec : Obj.sections())
+ if (ShouldReplace(Sec))
+ ToReplace.push_back(&Sec);
+
+ // Build a mapping from original section to a new one.
+ DenseMap<SectionBase *, SectionBase *> FromTo;
+ for (SectionBase *S : ToReplace) {
+ Expected<SectionBase *> NewSection = AddSection(S);
+ if (!NewSection)
+ return NewSection.takeError();
+
+ FromTo[S] = *NewSection;
+ }
+
+ return Obj.replaceSections(FromTo);
+}
+
+static bool isAArch64MappingSymbol(const Symbol &Sym) {
+ if (Sym.Binding != STB_LOCAL || Sym.Type != STT_NOTYPE ||
+ Sym.getShndx() == SHN_UNDEF)
+ return false;
+ StringRef Name = Sym.Name;
+ if (!Name.consume_front("$x") && !Name.consume_front("$d"))
+ return false;
+ return Name.empty() || Name.startswith(".");
+}
+
+static bool isArmMappingSymbol(const Symbol &Sym) {
+ if (Sym.Binding != STB_LOCAL || Sym.Type != STT_NOTYPE ||
+ Sym.getShndx() == SHN_UNDEF)
+ return false;
+ StringRef Name = Sym.Name;
+ if (!Name.consume_front("$a") && !Name.consume_front("$d") &&
+ !Name.consume_front("$t"))
+ return false;
+ return Name.empty() || Name.startswith(".");
+}
+
+// Check if the symbol should be preserved because it is required by ABI.
+static bool isRequiredByABISymbol(const Object &Obj, const Symbol &Sym) {
+ switch (Obj.Machine) {
+ case EM_AARCH64:
+ // Mapping symbols should be preserved for a relocatable object file.
+ return Obj.isRelocatable() && isAArch64MappingSymbol(Sym);
+ case EM_ARM:
+ // Mapping symbols should be preserved for a relocatable object file.
+ return Obj.isRelocatable() && isArmMappingSymbol(Sym);
+ default:
+ return false;
+ }
+}
+
+static bool isUnneededSymbol(const Symbol &Sym) {
+ return !Sym.Referenced &&
+ (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) &&
+ Sym.Type != STT_SECTION;
+}
+
+static Error updateAndRemoveSymbols(const CommonConfig &Config,
+ const ELFConfig &ELFConfig, Object &Obj) {
+ // TODO: update or remove symbols only if there is an option that affects
+ // them.
+ if (!Obj.SymbolTable)
+ return Error::success();
+
+ Obj.SymbolTable->updateSymbols([&](Symbol &Sym) {
+ // Common and undefined symbols don't make sense as local symbols, and can
+ // even cause crashes if we localize those, so skip them.
+ if (!Sym.isCommon() && Sym.getShndx() != SHN_UNDEF &&
+ ((ELFConfig.LocalizeHidden &&
+ (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) ||
+ Config.SymbolsToLocalize.matches(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() &&
+ !Config.SymbolsToKeepGlobal.matches(Sym.Name) &&
+ Sym.getShndx() != SHN_UNDEF)
+ Sym.Binding = STB_LOCAL;
+
+ if (Config.SymbolsToGlobalize.matches(Sym.Name) &&
+ Sym.getShndx() != SHN_UNDEF)
+ Sym.Binding = STB_GLOBAL;
+
+ if (Config.SymbolsToWeaken.matches(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 = std::string(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 || !Config.UnneededSymbolsToRemove.empty() ||
+ !Config.OnlySection.empty()) {
+ for (SectionBase &Sec : Obj.sections())
+ Sec.markSymbols();
+ }
+
+ auto RemoveSymbolsPred = [&](const Symbol &Sym) {
+ if (Config.SymbolsToKeep.matches(Sym.Name) ||
+ (ELFConfig.KeepFileSymbols && Sym.Type == STT_FILE))
+ return false;
+
+ if (Config.SymbolsToRemove.matches(Sym.Name))
+ return true;
+
+ if (Config.StripAll || Config.StripAllGNU)
+ return true;
+
+ if (isRequiredByABISymbol(Obj, Sym))
+ return false;
+
+ if (Config.StripDebug && Sym.Type == STT_FILE)
+ return true;
+
+ if ((Config.DiscardMode == DiscardType::All ||
+ (Config.DiscardMode == DiscardType::Locals &&
+ StringRef(Sym.Name).startswith(".L"))) &&
+ Sym.Binding == STB_LOCAL && Sym.getShndx() != SHN_UNDEF &&
+ Sym.Type != STT_FILE && Sym.Type != STT_SECTION)
+ return true;
+
+ if ((Config.StripUnneeded ||
+ Config.UnneededSymbolsToRemove.matches(Sym.Name)) &&
+ (!Obj.isRelocatable() || isUnneededSymbol(Sym)))
+ return true;
+
+ // We want to remove undefined symbols if all references have been stripped.
+ if (!Config.OnlySection.empty() && !Sym.Referenced &&
+ Sym.getShndx() == SHN_UNDEF)
+ return true;
+
+ return false;
+ };
+
+ return Obj.removeSymbols(RemoveSymbolsPred);
+}
+
+static Error replaceAndRemoveSections(const CommonConfig &Config,
+ const ELFConfig &ELFConfig, Object &Obj) {
+ SectionPred RemovePred = [](const SectionBase &) { return false; };
+
+ // Removes:
+ if (!Config.ToRemove.empty()) {
+ RemovePred = [&Config](const SectionBase &Sec) {
+ return Config.ToRemove.matches(Sec.Name);
+ };
+ }
+
+ if (Config.StripDWO)
+ 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.ParentSegment == nullptr;
+ };
+ }
+
+ if (Config.StripDebug || Config.StripUnneeded) {
+ 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 && Sec.ParentSegment == nullptr;
+ };
+
+ 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;
+ // We keep the .ARM.attribute section to maintain compatibility
+ // with Debian derived distributions. This is a bug in their
+ // patchset as documented here:
+ // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=943798
+ if (Sec.Type == SHT_ARM_ATTRIBUTES)
+ return false;
+ if (Sec.ParentSegment != nullptr)
+ return false;
+ return (Sec.Flags & SHF_ALLOC) == 0;
+ };
+
+ if (Config.ExtractPartition || Config.ExtractMainPartition) {
+ RemovePred = [RemovePred](const SectionBase &Sec) {
+ if (RemovePred(Sec))
+ return true;
+ if (Sec.Type == SHT_LLVM_PART_EHDR || Sec.Type == SHT_LLVM_PART_PHDR)
+ return true;
+ return (Sec.Flags & SHF_ALLOC) != 0 && !Sec.ParentSegment;
+ };
+ }
+
+ // Explicit copies:
+ if (!Config.OnlySection.empty()) {
+ RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) {
+ // Explicitly keep these sections regardless of previous removes.
+ if (Config.OnlySection.matches(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 (Config.KeepSection.matches(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() || ELFConfig.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 (Error E = Obj.removeSections(ELFConfig.AllowBrokenLinks, RemovePred))
+ return E;
+
+ if (Config.CompressionType != DebugCompressionType::None) {
+ if (Error Err = replaceDebugSections(
+ Obj, isCompressable,
+ [&Config, &Obj](const SectionBase *S) -> Expected<SectionBase *> {
+ Expected<CompressedSection> NewSection =
+ CompressedSection::create(*S, Config.CompressionType);
+ if (!NewSection)
+ return NewSection.takeError();
+
+ return &Obj.addSection<CompressedSection>(std::move(*NewSection));
+ }))
+ return Err;
+ } else if (Config.DecompressDebugSections) {
+ if (Error Err = replaceDebugSections(
+ Obj,
+ [](const SectionBase &S) { return isa<CompressedSection>(&S); },
+ [&Obj](const SectionBase *S) {
+ const CompressedSection *CS = cast<CompressedSection>(S);
+ return &Obj.addSection<DecompressedSection>(*CS);
+ }))
+ return Err;
+ }
+
+ return Error::success();
+}
+
+// Add symbol to the Object symbol table with the specified properties.
+static void addSymbol(Object &Obj, const NewSymbolInfo &SymInfo,
+ uint8_t DefaultVisibility) {
+ SectionBase *Sec = Obj.findSection(SymInfo.SectionName);
+ uint64_t Value = Sec ? Sec->Addr + SymInfo.Value : SymInfo.Value;
+
+ uint8_t Bind = ELF::STB_GLOBAL;
+ uint8_t Type = ELF::STT_NOTYPE;
+ uint8_t Visibility = DefaultVisibility;
+
+ for (SymbolFlag FlagValue : SymInfo.Flags)
+ switch (FlagValue) {
+ case SymbolFlag::Global:
+ Bind = ELF::STB_GLOBAL;
+ break;
+ case SymbolFlag::Local:
+ Bind = ELF::STB_LOCAL;
+ break;
+ case SymbolFlag::Weak:
+ Bind = ELF::STB_WEAK;
+ break;
+ case SymbolFlag::Default:
+ Visibility = ELF::STV_DEFAULT;
+ break;
+ case SymbolFlag::Hidden:
+ Visibility = ELF::STV_HIDDEN;
+ break;
+ case SymbolFlag::Protected:
+ Visibility = ELF::STV_PROTECTED;
+ break;
+ case SymbolFlag::File:
+ Type = ELF::STT_FILE;
+ break;
+ case SymbolFlag::Section:
+ Type = ELF::STT_SECTION;
+ break;
+ case SymbolFlag::Object:
+ Type = ELF::STT_OBJECT;
+ break;
+ case SymbolFlag::Function:
+ Type = ELF::STT_FUNC;
+ break;
+ case SymbolFlag::IndirectFunction:
+ Type = ELF::STT_GNU_IFUNC;
+ break;
+ default: /* Other flag values are ignored for ELF. */
+ break;
+ };
+
+ Obj.SymbolTable->addSymbol(
+ SymInfo.SymbolName, Bind, Type, Sec, Value, Visibility,
+ Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0);
+}
+
+static Error
+handleUserSection(StringRef Flag,
+ function_ref<Error(StringRef, ArrayRef<uint8_t>)> F) {
+ 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)
+ return createFileError(File, errorCodeToError(BufOrErr.getError()));
+ std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr);
+ ArrayRef<uint8_t> Data(
+ reinterpret_cast<const uint8_t *>(Buf->getBufferStart()),
+ Buf->getBufferSize());
+ return F(SecName, Data);
+}
+
+// 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 Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
+ Object &Obj) {
+ if (Config.OutputArch) {
+ Obj.Machine = Config.OutputArch.getValue().EMachine;
+ Obj.OSABI = Config.OutputArch.getValue().OSABI;
+ }
+
+ if (!Config.SplitDWO.empty() && Config.ExtractDWO) {
+ return Obj.removeSections(
+ ELFConfig.AllowBrokenLinks,
+ [&Obj](const SectionBase &Sec) { return onlyKeepDWOPred(Obj, Sec); });
+ }
+
+ // Dump sections before add/remove for compatibility with GNU objcopy.
+ for (StringRef Flag : Config.DumpSection) {
+ StringRef SectionName;
+ StringRef FileName;
+ std::tie(SectionName, FileName) = Flag.split('=');
+ if (Error E = dumpSectionToFile(SectionName, FileName, Obj))
+ return E;
+ }
+
+ // It is important to remove the sections first. For example, we want to
+ // remove the relocation sections before removing the symbols. That allows
+ // us to avoid reporting the inappropriate errors about removing symbols
+ // named in relocations.
+ if (Error E = replaceAndRemoveSections(Config, ELFConfig, Obj))
+ return E;
+
+ if (Error E = updateAndRemoveSymbols(Config, ELFConfig, Obj))
+ return E;
+
+ if (!Config.SectionsToRename.empty()) {
+ std::vector<RelocationSectionBase *> RelocSections;
+ DenseSet<SectionBase *> RenamedSections;
+ for (SectionBase &Sec : Obj.sections()) {
+ auto *RelocSec = dyn_cast<RelocationSectionBase>(&Sec);
+ const auto Iter = Config.SectionsToRename.find(Sec.Name);
+ if (Iter != Config.SectionsToRename.end()) {
+ const SectionRename &SR = Iter->second;
+ Sec.Name = std::string(SR.NewName);
+ if (SR.NewFlags.hasValue())
+ setSectionFlagsAndType(Sec, SR.NewFlags.getValue());
+ RenamedSections.insert(&Sec);
+ } else if (RelocSec && !(Sec.Flags & SHF_ALLOC))
+ // Postpone processing relocation sections which are not specified in
+ // their explicit '--rename-section' commands until after their target
+ // sections are renamed.
+ // Dynamic relocation sections (i.e. ones with SHF_ALLOC) should be
+ // renamed only explicitly. Otherwise, renaming, for example, '.got.plt'
+ // would affect '.rela.plt', which is not desirable.
+ RelocSections.push_back(RelocSec);
+ }
+
+ // Rename relocation sections according to their target sections.
+ for (RelocationSectionBase *RelocSec : RelocSections) {
+ auto Iter = RenamedSections.find(RelocSec->getSection());
+ if (Iter != RenamedSections.end())
+ RelocSec->Name = (RelocSec->getNamePrefix() + (*Iter)->Name).str();
+ }
+ }
+
+ // Add a prefix to allocated sections and their relocation sections. This
+ // should be done after renaming the section by Config.SectionToRename to
+ // imitate the GNU objcopy behavior.
+ if (!Config.AllocSectionsPrefix.empty()) {
+ DenseSet<SectionBase *> PrefixedSections;
+ for (SectionBase &Sec : Obj.sections()) {
+ if (Sec.Flags & SHF_ALLOC) {
+ Sec.Name = (Config.AllocSectionsPrefix + Sec.Name).str();
+ PrefixedSections.insert(&Sec);
+ } else if (auto *RelocSec = dyn_cast<RelocationSectionBase>(&Sec)) {
+ // Rename relocation sections associated to the allocated sections.
+ // For example, if we rename .text to .prefix.text, we also rename
+ // .rel.text to .rel.prefix.text.
+ //
+ // Dynamic relocation sections (SHT_REL[A] with SHF_ALLOC) are handled
+ // above, e.g., .rela.plt is renamed to .prefix.rela.plt, not
+ // .rela.prefix.plt since GNU objcopy does so.
+ const SectionBase *TargetSec = RelocSec->getSection();
+ if (TargetSec && (TargetSec->Flags & SHF_ALLOC)) {
+ // If the relocation section comes *after* the target section, we
+ // don't add Config.AllocSectionsPrefix because we've already added
+ // the prefix to TargetSec->Name. Otherwise, if the relocation
+ // section comes *before* the target section, we add the prefix.
+ if (PrefixedSections.count(TargetSec))
+ Sec.Name = (RelocSec->getNamePrefix() + TargetSec->Name).str();
+ else
+ Sec.Name = (RelocSec->getNamePrefix() + Config.AllocSectionsPrefix +
+ TargetSec->Name)
+ .str();
+ }
+ }
+ }
+ }
+
+ if (!Config.SetSectionAlignment.empty()) {
+ for (SectionBase &Sec : Obj.sections()) {
+ auto I = Config.SetSectionAlignment.find(Sec.Name);
+ if (I != Config.SetSectionAlignment.end())
+ Sec.Align = I->second;
+ }
+ }
+
+ if (Config.OnlyKeepDebug)
+ for (auto &Sec : Obj.sections())
+ if (Sec.Flags & SHF_ALLOC && Sec.Type != SHT_NOTE)
+ Sec.Type = SHT_NOBITS;
+
+ for (const auto &Flag : Config.AddSection) {
+ auto AddSection = [&](StringRef Name, ArrayRef<uint8_t> Data) {
+ OwnedDataSection &NewSection =
+ Obj.addSection<OwnedDataSection>(Name, Data);
+ if (Name.startswith(".note") && Name != ".note.GNU-stack")
+ NewSection.Type = SHT_NOTE;
+ return Error::success();
+ };
+ if (Error E = handleUserSection(Flag, AddSection))
+ return E;
+ }
+
+ for (StringRef Flag : Config.UpdateSection) {
+ auto UpdateSection = [&](StringRef Name, ArrayRef<uint8_t> Data) {
+ return Obj.updateSection(Name, Data);
+ };
+ if (Error E = handleUserSection(Flag, UpdateSection))
+ return E;
+ }
+
+ if (!Config.AddGnuDebugLink.empty())
+ Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink,
+ Config.GnuDebugLinkCRC32);
+
+ // If the symbol table was previously removed, we need to create a new one
+ // before adding new symbols.
+ if (!Obj.SymbolTable && !Config.SymbolsToAdd.empty())
+ if (Error E = Obj.addNewSymbolTable())
+ return E;
+
+ for (const NewSymbolInfo &SI : Config.SymbolsToAdd)
+ addSymbol(Obj, SI, ELFConfig.NewSymbolVisibility);
+
+ // --set-section-flags works with sections added by --add-section.
+ if (!Config.SetSectionFlags.empty()) {
+ for (auto &Sec : Obj.sections()) {
+ const auto Iter = Config.SetSectionFlags.find(Sec.Name);
+ if (Iter != Config.SetSectionFlags.end()) {
+ const SectionFlagsUpdate &SFU = Iter->second;
+ setSectionFlagsAndType(Sec, SFU.NewFlags);
+ }
+ }
+ }
+
+ if (ELFConfig.EntryExpr)
+ Obj.Entry = ELFConfig.EntryExpr(Obj.Entry);
+ return Error::success();
+}
+
+static Error writeOutput(const CommonConfig &Config, Object &Obj,
+ raw_ostream &Out, ElfType OutputElfType) {
+ std::unique_ptr<Writer> Writer =
+ createWriter(Config, Obj, Out, OutputElfType);
+ if (Error E = Writer->finalize())
+ return E;
+ return Writer->write();
+}
+
+Error objcopy::elf::executeObjcopyOnIHex(const CommonConfig &Config,
+ const ELFConfig &ELFConfig,
+ MemoryBuffer &In, raw_ostream &Out) {
+ IHexReader Reader(&In);
+ Expected<std::unique_ptr<Object>> Obj = Reader.create(true);
+ if (!Obj)
+ return Obj.takeError();
+
+ const ElfType OutputElfType =
+ getOutputElfType(Config.OutputArch.getValueOr(MachineInfo()));
+ if (Error E = handleArgs(Config, ELFConfig, **Obj))
+ return E;
+ return writeOutput(Config, **Obj, Out, OutputElfType);
+}
+
+Error objcopy::elf::executeObjcopyOnRawBinary(const CommonConfig &Config,
+ const ELFConfig &ELFConfig,
+ MemoryBuffer &In,
+ raw_ostream &Out) {
+ BinaryReader Reader(&In, ELFConfig.NewSymbolVisibility);
+ Expected<std::unique_ptr<Object>> Obj = Reader.create(true);
+ if (!Obj)
+ return Obj.takeError();
+
+ // Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch
+ // (-B<arch>).
+ const ElfType OutputElfType =
+ getOutputElfType(Config.OutputArch.getValueOr(MachineInfo()));
+ if (Error E = handleArgs(Config, ELFConfig, **Obj))
+ return E;
+ return writeOutput(Config, **Obj, Out, OutputElfType);
+}
+
+Error objcopy::elf::executeObjcopyOnBinary(const CommonConfig &Config,
+ const ELFConfig &ELFConfig,
+ object::ELFObjectFileBase &In,
+ raw_ostream &Out) {
+ ELFReader Reader(&In, Config.ExtractPartition);
+ Expected<std::unique_ptr<Object>> Obj =
+ Reader.create(!Config.SymbolsToAdd.empty());
+ if (!Obj)
+ return Obj.takeError();
+ // Prefer OutputArch (-O<format>) if set, otherwise infer it from the input.
+ const ElfType OutputElfType =
+ Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue())
+ : getOutputElfType(In);
+
+ if (Error E = handleArgs(Config, ELFConfig, **Obj))
+ return createFileError(Config.InputFilename, std::move(E));
+
+ if (Error E = writeOutput(Config, **Obj, Out, OutputElfType))
+ return createFileError(Config.InputFilename, std::move(E));
+
+ return Error::success();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFObjcopy.h b/contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFObjcopy.h
new file mode 100644
index 00000000000..852661e68f3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/ELF/ELFObjcopy.h
@@ -0,0 +1,40 @@
+//===- ELFObjcopy.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H
+#define LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H
+
+namespace llvm {
+class Error;
+class MemoryBuffer;
+class raw_ostream;
+
+namespace object {
+class ELFObjectFileBase;
+} // end namespace object
+
+namespace objcopy {
+struct CommonConfig;
+struct ELFConfig;
+
+namespace elf {
+Error executeObjcopyOnIHex(const CommonConfig &Config,
+ const ELFConfig &ELFConfig, MemoryBuffer &In,
+ raw_ostream &Out);
+Error executeObjcopyOnRawBinary(const CommonConfig &Config,
+ const ELFConfig &ELFConfig, MemoryBuffer &In,
+ raw_ostream &Out);
+Error executeObjcopyOnBinary(const CommonConfig &Config,
+ const ELFConfig &ELFConfig,
+ object::ELFObjectFileBase &In, raw_ostream &Out);
+
+} // end namespace elf
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/ELF/Object.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/ELF/Object.cpp
new file mode 100644
index 00000000000..659e12bf030
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/ELF/Object.cpp
@@ -0,0 +1,2826 @@
+//===- Object.cpp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Object.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Support/Compression.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/Path.h"
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <iterator>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::ELF;
+using namespace llvm::objcopy::elf;
+using namespace llvm::object;
+
+template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) {
+ uint8_t *B = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) +
+ Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr);
+ Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(B);
+ Phdr.p_type = Seg.Type;
+ Phdr.p_flags = Seg.Flags;
+ Phdr.p_offset = Seg.Offset;
+ Phdr.p_vaddr = Seg.VAddr;
+ Phdr.p_paddr = Seg.PAddr;
+ Phdr.p_filesz = Seg.FileSize;
+ Phdr.p_memsz = Seg.MemSize;
+ Phdr.p_align = Seg.Align;
+}
+
+Error SectionBase::removeSectionReferences(
+ bool, function_ref<bool(const SectionBase *)>) {
+ return Error::success();
+}
+
+Error SectionBase::removeSymbols(function_ref<bool(const Symbol &)>) {
+ return Error::success();
+}
+
+Error SectionBase::initialize(SectionTableRef) { return Error::success(); }
+void SectionBase::finalize() {}
+void SectionBase::markSymbols() {}
+void SectionBase::replaceSectionReferences(
+ const DenseMap<SectionBase *, SectionBase *> &) {}
+void SectionBase::onRemove() {}
+
+template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) {
+ uint8_t *B =
+ reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Sec.HeaderOffset;
+ Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B);
+ Shdr.sh_name = Sec.NameIndex;
+ Shdr.sh_type = Sec.Type;
+ Shdr.sh_flags = Sec.Flags;
+ Shdr.sh_addr = Sec.Addr;
+ Shdr.sh_offset = Sec.Offset;
+ Shdr.sh_size = Sec.Size;
+ Shdr.sh_link = Sec.Link;
+ Shdr.sh_info = Sec.Info;
+ Shdr.sh_addralign = Sec.Align;
+ Shdr.sh_entsize = Sec.EntrySize;
+}
+
+template <class ELFT> Error ELFSectionSizer<ELFT>::visit(Section &) {
+ return Error::success();
+}
+
+template <class ELFT> Error ELFSectionSizer<ELFT>::visit(OwnedDataSection &) {
+ return Error::success();
+}
+
+template <class ELFT> Error ELFSectionSizer<ELFT>::visit(StringTableSection &) {
+ return Error::success();
+}
+
+template <class ELFT>
+Error ELFSectionSizer<ELFT>::visit(DynamicRelocationSection &) {
+ return Error::success();
+}
+
+template <class ELFT>
+Error 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);
+ return Error::success();
+}
+
+template <class ELFT>
+Error 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);
+ return Error::success();
+}
+
+template <class ELFT>
+Error ELFSectionSizer<ELFT>::visit(GnuDebugLinkSection &) {
+ return Error::success();
+}
+
+template <class ELFT> Error ELFSectionSizer<ELFT>::visit(GroupSection &Sec) {
+ Sec.Size = sizeof(Elf_Word) + Sec.GroupMembers.size() * sizeof(Elf_Word);
+ return Error::success();
+}
+
+template <class ELFT>
+Error ELFSectionSizer<ELFT>::visit(SectionIndexSection &) {
+ return Error::success();
+}
+
+template <class ELFT> Error ELFSectionSizer<ELFT>::visit(CompressedSection &) {
+ return Error::success();
+}
+
+template <class ELFT>
+Error ELFSectionSizer<ELFT>::visit(DecompressedSection &) {
+ return Error::success();
+}
+
+Error BinarySectionWriter::visit(const SectionIndexSection &Sec) {
+ return createStringError(errc::operation_not_permitted,
+ "cannot write symbol section index table '" +
+ Sec.Name + "' ");
+}
+
+Error BinarySectionWriter::visit(const SymbolTableSection &Sec) {
+ return createStringError(errc::operation_not_permitted,
+ "cannot write symbol table '" + Sec.Name +
+ "' out to binary");
+}
+
+Error BinarySectionWriter::visit(const RelocationSection &Sec) {
+ return createStringError(errc::operation_not_permitted,
+ "cannot write relocation section '" + Sec.Name +
+ "' out to binary");
+}
+
+Error BinarySectionWriter::visit(const GnuDebugLinkSection &Sec) {
+ return createStringError(errc::operation_not_permitted,
+ "cannot write '" + Sec.Name + "' out to binary");
+}
+
+Error BinarySectionWriter::visit(const GroupSection &Sec) {
+ return createStringError(errc::operation_not_permitted,
+ "cannot write '" + Sec.Name + "' out to binary");
+}
+
+Error SectionWriter::visit(const Section &Sec) {
+ if (Sec.Type != SHT_NOBITS)
+ llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset);
+
+ return Error::success();
+}
+
+static bool addressOverflows32bit(uint64_t Addr) {
+ // Sign extended 32 bit addresses (e.g 0xFFFFFFFF80000000) are ok
+ return Addr > UINT32_MAX && Addr + 0x80000000 > UINT32_MAX;
+}
+
+template <class T> static T checkedGetHex(StringRef S) {
+ T Value;
+ bool Fail = S.getAsInteger(16, Value);
+ assert(!Fail);
+ (void)Fail;
+ return Value;
+}
+
+// Fills exactly Len bytes of buffer with hexadecimal characters
+// representing value 'X'
+template <class T, class Iterator>
+static Iterator toHexStr(T X, Iterator It, size_t Len) {
+ // Fill range with '0'
+ std::fill(It, It + Len, '0');
+
+ for (long I = Len - 1; I >= 0; --I) {
+ unsigned char Mod = static_cast<unsigned char>(X) & 15;
+ *(It + I) = hexdigit(Mod, false);
+ X >>= 4;
+ }
+ assert(X == 0);
+ return It + Len;
+}
+
+uint8_t IHexRecord::getChecksum(StringRef S) {
+ assert((S.size() & 1) == 0);
+ uint8_t Checksum = 0;
+ while (!S.empty()) {
+ Checksum += checkedGetHex<uint8_t>(S.take_front(2));
+ S = S.drop_front(2);
+ }
+ return -Checksum;
+}
+
+IHexLineData IHexRecord::getLine(uint8_t Type, uint16_t Addr,
+ ArrayRef<uint8_t> Data) {
+ IHexLineData Line(getLineLength(Data.size()));
+ assert(Line.size());
+ auto Iter = Line.begin();
+ *Iter++ = ':';
+ Iter = toHexStr(Data.size(), Iter, 2);
+ Iter = toHexStr(Addr, Iter, 4);
+ Iter = toHexStr(Type, Iter, 2);
+ for (uint8_t X : Data)
+ Iter = toHexStr(X, Iter, 2);
+ StringRef S(Line.data() + 1, std::distance(Line.begin() + 1, Iter));
+ Iter = toHexStr(getChecksum(S), Iter, 2);
+ *Iter++ = '\r';
+ *Iter++ = '\n';
+ assert(Iter == Line.end());
+ return Line;
+}
+
+static Error checkRecord(const IHexRecord &R) {
+ switch (R.Type) {
+ case IHexRecord::Data:
+ if (R.HexData.size() == 0)
+ return createStringError(
+ errc::invalid_argument,
+ "zero data length is not allowed for data records");
+ break;
+ case IHexRecord::EndOfFile:
+ break;
+ case IHexRecord::SegmentAddr:
+ // 20-bit segment address. Data length must be 2 bytes
+ // (4 bytes in hex)
+ if (R.HexData.size() != 4)
+ return createStringError(
+ errc::invalid_argument,
+ "segment address data should be 2 bytes in size");
+ break;
+ case IHexRecord::StartAddr80x86:
+ case IHexRecord::StartAddr:
+ if (R.HexData.size() != 8)
+ return createStringError(errc::invalid_argument,
+ "start address data should be 4 bytes in size");
+ // According to Intel HEX specification '03' record
+ // only specifies the code address within the 20-bit
+ // segmented address space of the 8086/80186. This
+ // means 12 high order bits should be zeroes.
+ if (R.Type == IHexRecord::StartAddr80x86 &&
+ R.HexData.take_front(3) != "000")
+ return createStringError(errc::invalid_argument,
+ "start address exceeds 20 bit for 80x86");
+ break;
+ case IHexRecord::ExtendedAddr:
+ // 16-31 bits of linear base address
+ if (R.HexData.size() != 4)
+ return createStringError(
+ errc::invalid_argument,
+ "extended address data should be 2 bytes in size");
+ break;
+ default:
+ // Unknown record type
+ return createStringError(errc::invalid_argument, "unknown record type: %u",
+ static_cast<unsigned>(R.Type));
+ }
+ return Error::success();
+}
+
+// Checks that IHEX line contains valid characters.
+// This allows converting hexadecimal data to integers
+// without extra verification.
+static Error checkChars(StringRef Line) {
+ assert(!Line.empty());
+ if (Line[0] != ':')
+ return createStringError(errc::invalid_argument,
+ "missing ':' in the beginning of line.");
+
+ for (size_t Pos = 1; Pos < Line.size(); ++Pos)
+ if (hexDigitValue(Line[Pos]) == -1U)
+ return createStringError(errc::invalid_argument,
+ "invalid character at position %zu.", Pos + 1);
+ return Error::success();
+}
+
+Expected<IHexRecord> IHexRecord::parse(StringRef Line) {
+ assert(!Line.empty());
+
+ // ':' + Length + Address + Type + Checksum with empty data ':LLAAAATTCC'
+ if (Line.size() < 11)
+ return createStringError(errc::invalid_argument,
+ "line is too short: %zu chars.", Line.size());
+
+ if (Error E = checkChars(Line))
+ return std::move(E);
+
+ IHexRecord Rec;
+ size_t DataLen = checkedGetHex<uint8_t>(Line.substr(1, 2));
+ if (Line.size() != getLength(DataLen))
+ return createStringError(errc::invalid_argument,
+ "invalid line length %zu (should be %zu)",
+ Line.size(), getLength(DataLen));
+
+ Rec.Addr = checkedGetHex<uint16_t>(Line.substr(3, 4));
+ Rec.Type = checkedGetHex<uint8_t>(Line.substr(7, 2));
+ Rec.HexData = Line.substr(9, DataLen * 2);
+
+ if (getChecksum(Line.drop_front(1)) != 0)
+ return createStringError(errc::invalid_argument, "incorrect checksum.");
+ if (Error E = checkRecord(Rec))
+ return std::move(E);
+ return Rec;
+}
+
+static uint64_t sectionPhysicalAddr(const SectionBase *Sec) {
+ Segment *Seg = Sec->ParentSegment;
+ if (Seg && Seg->Type != ELF::PT_LOAD)
+ Seg = nullptr;
+ return Seg ? Seg->PAddr + Sec->OriginalOffset - Seg->OriginalOffset
+ : Sec->Addr;
+}
+
+void IHexSectionWriterBase::writeSection(const SectionBase *Sec,
+ ArrayRef<uint8_t> Data) {
+ assert(Data.size() == Sec->Size);
+ const uint32_t ChunkSize = 16;
+ uint32_t Addr = sectionPhysicalAddr(Sec) & 0xFFFFFFFFU;
+ while (!Data.empty()) {
+ uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize);
+ if (Addr > SegmentAddr + BaseAddr + 0xFFFFU) {
+ if (Addr > 0xFFFFFU) {
+ // Write extended address record, zeroing segment address
+ // if needed.
+ if (SegmentAddr != 0)
+ SegmentAddr = writeSegmentAddr(0U);
+ BaseAddr = writeBaseAddr(Addr);
+ } else {
+ // We can still remain 16-bit
+ SegmentAddr = writeSegmentAddr(Addr);
+ }
+ }
+ uint64_t SegOffset = Addr - BaseAddr - SegmentAddr;
+ assert(SegOffset <= 0xFFFFU);
+ DataSize = std::min(DataSize, 0x10000U - SegOffset);
+ writeData(0, SegOffset, Data.take_front(DataSize));
+ Addr += DataSize;
+ Data = Data.drop_front(DataSize);
+ }
+}
+
+uint64_t IHexSectionWriterBase::writeSegmentAddr(uint64_t Addr) {
+ assert(Addr <= 0xFFFFFU);
+ uint8_t Data[] = {static_cast<uint8_t>((Addr & 0xF0000U) >> 12), 0};
+ writeData(2, 0, Data);
+ return Addr & 0xF0000U;
+}
+
+uint64_t IHexSectionWriterBase::writeBaseAddr(uint64_t Addr) {
+ assert(Addr <= 0xFFFFFFFFU);
+ uint64_t Base = Addr & 0xFFFF0000U;
+ uint8_t Data[] = {static_cast<uint8_t>(Base >> 24),
+ static_cast<uint8_t>((Base >> 16) & 0xFF)};
+ writeData(4, 0, Data);
+ return Base;
+}
+
+void IHexSectionWriterBase::writeData(uint8_t, uint16_t,
+ ArrayRef<uint8_t> Data) {
+ Offset += IHexRecord::getLineLength(Data.size());
+}
+
+Error IHexSectionWriterBase::visit(const Section &Sec) {
+ writeSection(&Sec, Sec.Contents);
+ return Error::success();
+}
+
+Error IHexSectionWriterBase::visit(const OwnedDataSection &Sec) {
+ writeSection(&Sec, Sec.Data);
+ return Error::success();
+}
+
+Error IHexSectionWriterBase::visit(const StringTableSection &Sec) {
+ // Check that sizer has already done its work
+ assert(Sec.Size == Sec.StrTabBuilder.getSize());
+ // We are free to pass an invalid pointer to writeSection as long
+ // as we don't actually write any data. The real writer class has
+ // to override this method .
+ writeSection(&Sec, {nullptr, static_cast<size_t>(Sec.Size)});
+ return Error::success();
+}
+
+Error IHexSectionWriterBase::visit(const DynamicRelocationSection &Sec) {
+ writeSection(&Sec, Sec.Contents);
+ return Error::success();
+}
+
+void IHexSectionWriter::writeData(uint8_t Type, uint16_t Addr,
+ ArrayRef<uint8_t> Data) {
+ IHexLineData HexData = IHexRecord::getLine(Type, Addr, Data);
+ memcpy(Out.getBufferStart() + Offset, HexData.data(), HexData.size());
+ Offset += HexData.size();
+}
+
+Error IHexSectionWriter::visit(const StringTableSection &Sec) {
+ assert(Sec.Size == Sec.StrTabBuilder.getSize());
+ std::vector<uint8_t> Data(Sec.Size);
+ Sec.StrTabBuilder.write(Data.data());
+ writeSection(&Sec, Data);
+ return Error::success();
+}
+
+Error Section::accept(SectionVisitor &Visitor) const {
+ return Visitor.visit(*this);
+}
+
+Error Section::accept(MutableSectionVisitor &Visitor) {
+ return Visitor.visit(*this);
+}
+
+Error SectionWriter::visit(const OwnedDataSection &Sec) {
+ llvm::copy(Sec.Data, Out.getBufferStart() + Sec.Offset);
+ return Error::success();
+}
+
+static constexpr std::array<uint8_t, 4> 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(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>
+Error ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) {
+ 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 Err = zlib::uncompress(CompressedContent, DecompressedContent,
+ static_cast<size_t>(Sec.Size)))
+ return createStringError(errc::invalid_argument,
+ "'" + Sec.Name + "': " + toString(std::move(Err)));
+
+ uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
+ std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf);
+
+ return Error::success();
+}
+
+Error BinarySectionWriter::visit(const DecompressedSection &Sec) {
+ return createStringError(errc::operation_not_permitted,
+ "cannot write compressed section '" + Sec.Name +
+ "' ");
+}
+
+Error DecompressedSection::accept(SectionVisitor &Visitor) const {
+ return Visitor.visit(*this);
+}
+
+Error DecompressedSection::accept(MutableSectionVisitor &Visitor) {
+ return Visitor.visit(*this);
+}
+
+Error OwnedDataSection::accept(SectionVisitor &Visitor) const {
+ return Visitor.visit(*this);
+}
+
+Error OwnedDataSection::accept(MutableSectionVisitor &Visitor) {
+ return Visitor.visit(*this);
+}
+
+void OwnedDataSection::appendHexData(StringRef HexData) {
+ assert((HexData.size() & 1) == 0);
+ while (!HexData.empty()) {
+ Data.push_back(checkedGetHex<uint8_t>(HexData.take_front(2)));
+ HexData = HexData.drop_front(2);
+ }
+ Size = Data.size();
+}
+
+Error BinarySectionWriter::visit(const CompressedSection &Sec) {
+ return createStringError(errc::operation_not_permitted,
+ "cannot write compressed section '" + Sec.Name +
+ "' ");
+}
+
+template <class ELFT>
+Error ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) {
+ uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
+ if (Sec.CompressionType == DebugCompressionType::None) {
+ std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf);
+ return Error::success();
+ }
+
+ 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);
+ return Error::success();
+}
+
+Expected<CompressedSection>
+CompressedSection::create(const SectionBase &Sec,
+ DebugCompressionType CompressionType) {
+ Error Err = Error::success();
+ CompressedSection Section(Sec, CompressionType, Err);
+
+ if (Err)
+ return std::move(Err);
+
+ return Section;
+}
+Expected<CompressedSection>
+CompressedSection::create(ArrayRef<uint8_t> CompressedData,
+ uint64_t DecompressedSize,
+ uint64_t DecompressedAlign) {
+ return CompressedSection(CompressedData, DecompressedSize, DecompressedAlign);
+}
+
+CompressedSection::CompressedSection(const SectionBase &Sec,
+ DebugCompressionType CompressionType,
+ Error &OutErr)
+ : SectionBase(Sec), CompressionType(CompressionType),
+ DecompressedSize(Sec.OriginalData.size()), DecompressedAlign(Sec.Align) {
+ ErrorAsOutParameter EAO(&OutErr);
+
+ if (Error Err = zlib::compress(
+ StringRef(reinterpret_cast<const char *>(OriginalData.data()),
+ OriginalData.size()),
+ CompressedData)) {
+ OutErr = createStringError(llvm::errc::invalid_argument,
+ "'" + Name + "': " + toString(std::move(Err)));
+ return;
+ }
+
+ 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;
+}
+
+Error CompressedSection::accept(SectionVisitor &Visitor) const {
+ return Visitor.visit(*this);
+}
+
+Error CompressedSection::accept(MutableSectionVisitor &Visitor) {
+ return Visitor.visit(*this);
+}
+
+void StringTableSection::addString(StringRef Name) { StrTabBuilder.add(Name); }
+
+uint32_t StringTableSection::findIndex(StringRef Name) const {
+ return StrTabBuilder.getOffset(Name);
+}
+
+void StringTableSection::prepareForLayout() {
+ StrTabBuilder.finalize();
+ Size = StrTabBuilder.getSize();
+}
+
+Error SectionWriter::visit(const StringTableSection &Sec) {
+ Sec.StrTabBuilder.write(reinterpret_cast<uint8_t *>(Out.getBufferStart()) +
+ Sec.Offset);
+ return Error::success();
+}
+
+Error StringTableSection::accept(SectionVisitor &Visitor) const {
+ return Visitor.visit(*this);
+}
+
+Error StringTableSection::accept(MutableSectionVisitor &Visitor) {
+ return Visitor.visit(*this);
+}
+
+template <class ELFT>
+Error ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) {
+ uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
+ llvm::copy(Sec.Indexes, reinterpret_cast<Elf_Word *>(Buf));
+ return Error::success();
+}
+
+Error SectionIndexSection::initialize(SectionTableRef SecTable) {
+ Size = 0;
+ Expected<SymbolTableSection *> Sec =
+ SecTable.getSectionOfType<SymbolTableSection>(
+ 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 (!Sec)
+ return Sec.takeError();
+
+ setSymTab(*Sec);
+ Symbols->setShndxTable(this);
+ return Error::success();
+}
+
+void SectionIndexSection::finalize() { Link = Symbols->Index; }
+
+Error SectionIndexSection::accept(SectionVisitor &Visitor) const {
+ return Visitor.visit(*this);
+}
+
+Error SectionIndexSection::accept(MutableSectionVisitor &Visitor) {
+ return Visitor.visit(*this);
+}
+
+static bool isValidReservedSectionIndex(uint16_t Index, uint16_t Machine) {
+ switch (Index) {
+ case SHN_ABS:
+ case SHN_COMMON:
+ return true;
+ }
+
+ if (Machine == EM_AMDGPU) {
+ return Index == SHN_AMDGPU_LDS;
+ }
+
+ if (Machine == EM_HEXAGON) {
+ switch (Index) {
+ case SHN_HEXAGON_SCOMMON:
+ case SHN_HEXAGON_SCOMMON_1:
+ case SHN_HEXAGON_SCOMMON_2:
+ case SHN_HEXAGON_SCOMMON_4:
+ case SHN_HEXAGON_SCOMMON_8:
+ return true;
+ }
+ }
+ return false;
+}
+
+// Large indexes force us to clarify exactly what this function should do. This
+// function should return the value that will appear in st_shndx when written
+// out.
+uint16_t Symbol::getShndx() const {
+ if (DefinedIn != nullptr) {
+ if (DefinedIn->Index >= SHN_LORESERVE)
+ return SHN_XINDEX;
+ return DefinedIn->Index;
+ }
+
+ if (ShndxType == SYMBOL_SIMPLE_INDEX) {
+ // This means that we don't have a defined section but we do need to
+ // output a legitimate section index.
+ return SHN_UNDEF;
+ }
+
+ assert(ShndxType == SYMBOL_ABS || ShndxType == SYMBOL_COMMON ||
+ (ShndxType >= SYMBOL_LOPROC && ShndxType <= SYMBOL_HIPROC) ||
+ (ShndxType >= SYMBOL_LOOS && ShndxType <= SYMBOL_HIOS));
+ return static_cast<uint16_t>(ShndxType);
+}
+
+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(Twine Name, uint8_t Bind, uint8_t Type,
+ SectionBase *DefinedIn, uint64_t Value,
+ uint8_t Visibility, uint16_t Shndx,
+ uint64_t SymbolSize) {
+ Symbol Sym;
+ Sym.Name = Name.str();
+ Sym.Binding = Bind;
+ Sym.Type = Type;
+ Sym.DefinedIn = DefinedIn;
+ if (DefinedIn != nullptr)
+ DefinedIn->HasSymbol = true;
+ if (DefinedIn == nullptr) {
+ if (Shndx >= SHN_LORESERVE)
+ Sym.ShndxType = static_cast<SymbolShndxType>(Shndx);
+ else
+ Sym.ShndxType = SYMBOL_SIMPLE_INDEX;
+ }
+ Sym.Value = Value;
+ Sym.Visibility = Visibility;
+ Sym.Size = SymbolSize;
+ Sym.Index = Symbols.size();
+ Symbols.emplace_back(std::make_unique<Symbol>(Sym));
+ Size += this->EntrySize;
+}
+
+Error SymbolTableSection::removeSectionReferences(
+ bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) {
+ if (ToRemove(SectionIndexTable))
+ SectionIndexTable = nullptr;
+ if (ToRemove(SymbolNames)) {
+ if (!AllowBrokenLinks)
+ return createStringError(
+ llvm::errc::invalid_argument,
+ "string table '%s' cannot be removed because it is "
+ "referenced by the symbol table '%s'",
+ SymbolNames->Name.data(), this->Name.data());
+ SymbolNames = nullptr;
+ }
+ return removeSymbols(
+ [ToRemove](const Symbol &Sym) { return ToRemove(Sym.DefinedIn); });
+}
+
+void SymbolTableSection::updateSymbols(function_ref<void(Symbol &)> Callable) {
+ std::for_each(std::begin(Symbols) + 1, std::end(Symbols),
+ [Callable](SymPtr &Sym) { Callable(*Sym); });
+ std::stable_partition(
+ std::begin(Symbols), std::end(Symbols),
+ [](const SymPtr &Sym) { return Sym->Binding == STB_LOCAL; });
+ assignIndices();
+}
+
+Error SymbolTableSection::removeSymbols(
+ function_ref<bool(const Symbol &)> ToRemove) {
+ Symbols.erase(
+ std::remove_if(std::begin(Symbols) + 1, std::end(Symbols),
+ [ToRemove](const SymPtr &Sym) { return ToRemove(*Sym); }),
+ std::end(Symbols));
+ Size = Symbols.size() * EntrySize;
+ assignIndices();
+ return Error::success();
+}
+
+void SymbolTableSection::replaceSectionReferences(
+ const DenseMap<SectionBase *, SectionBase *> &FromTo) {
+ for (std::unique_ptr<Symbol> &Sym : Symbols)
+ if (SectionBase *To = FromTo.lookup(Sym->DefinedIn))
+ Sym->DefinedIn = To;
+}
+
+Error SymbolTableSection::initialize(SectionTableRef SecTable) {
+ Size = 0;
+ Expected<StringTableSection *> Sec =
+ SecTable.getSectionOfType<StringTableSection>(
+ Link,
+ "Symbol table has link index of " + Twine(Link) +
+ " which is not a valid index",
+ "Symbol table has link index of " + Twine(Link) +
+ " which is not a string table");
+ if (!Sec)
+ return Sec.takeError();
+
+ setStrTab(*Sec);
+ return Error::success();
+}
+
+void SymbolTableSection::finalize() {
+ uint32_t MaxLocalIndex = 0;
+ for (std::unique_ptr<Symbol> &Sym : Symbols) {
+ Sym->NameIndex =
+ SymbolNames == nullptr ? 0 : SymbolNames->findIndex(Sym->Name);
+ if (Sym->Binding == STB_LOCAL)
+ MaxLocalIndex = std::max(MaxLocalIndex, Sym->Index);
+ }
+ // Now we need to set the Link and Info fields.
+ Link = SymbolNames == nullptr ? 0 : SymbolNames->Index;
+ Info = MaxLocalIndex + 1;
+}
+
+void SymbolTableSection::prepareForLayout() {
+ // Reserve proper amount of space in section index table, so we can
+ // layout sections correctly. We will fill the table with correct
+ // indexes later in fillShdnxTable.
+ if (SectionIndexTable)
+ SectionIndexTable->reserve(Symbols.size());
+
+ // Add all of our strings to SymbolNames so that SymbolNames has the right
+ // size before layout is decided.
+ // If the symbol names section has been removed, don't try to add strings to
+ // the table.
+ if (SymbolNames != nullptr)
+ for (std::unique_ptr<Symbol> &Sym : Symbols)
+ SymbolNames->addString(Sym->Name);
+}
+
+void SymbolTableSection::fillShndxTable() {
+ if (SectionIndexTable == nullptr)
+ return;
+ // Fill section index table with real section indexes. This function must
+ // be called after assignOffsets.
+ for (const std::unique_ptr<Symbol> &Sym : Symbols) {
+ if (Sym->DefinedIn != nullptr && Sym->DefinedIn->Index >= SHN_LORESERVE)
+ SectionIndexTable->addIndex(Sym->DefinedIn->Index);
+ else
+ SectionIndexTable->addIndex(SHN_UNDEF);
+ }
+}
+
+Expected<const Symbol *>
+SymbolTableSection::getSymbolByIndex(uint32_t Index) const {
+ if (Symbols.size() <= Index)
+ return createStringError(errc::invalid_argument,
+ "invalid symbol index: " + Twine(Index));
+ return Symbols[Index].get();
+}
+
+Expected<Symbol *> SymbolTableSection::getSymbolByIndex(uint32_t Index) {
+ Expected<const Symbol *> Sym =
+ static_cast<const SymbolTableSection *>(this)->getSymbolByIndex(Index);
+ if (!Sym)
+ return Sym.takeError();
+
+ return const_cast<Symbol *>(*Sym);
+}
+
+template <class ELFT>
+Error ELFSectionWriter<ELFT>::visit(const SymbolTableSection &Sec) {
+ Elf_Sym *Sym = reinterpret_cast<Elf_Sym *>(Out.getBufferStart() + Sec.Offset);
+ // Loop though symbols setting each entry of the symbol table.
+ for (const std::unique_ptr<Symbol> &Symbol : Sec.Symbols) {
+ Sym->st_name = Symbol->NameIndex;
+ Sym->st_value = Symbol->Value;
+ Sym->st_size = Symbol->Size;
+ Sym->st_other = Symbol->Visibility;
+ Sym->setBinding(Symbol->Binding);
+ Sym->setType(Symbol->Type);
+ Sym->st_shndx = Symbol->getShndx();
+ ++Sym;
+ }
+ return Error::success();
+}
+
+Error SymbolTableSection::accept(SectionVisitor &Visitor) const {
+ return Visitor.visit(*this);
+}
+
+Error SymbolTableSection::accept(MutableSectionVisitor &Visitor) {
+ return Visitor.visit(*this);
+}
+
+StringRef RelocationSectionBase::getNamePrefix() const {
+ switch (Type) {
+ case SHT_REL:
+ return ".rel";
+ case SHT_RELA:
+ return ".rela";
+ default:
+ llvm_unreachable("not a relocation section");
+ }
+}
+
+Error RelocationSection::removeSectionReferences(
+ bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) {
+ if (ToRemove(Symbols)) {
+ if (!AllowBrokenLinks)
+ return createStringError(
+ llvm::errc::invalid_argument,
+ "symbol table '%s' cannot be removed because it is "
+ "referenced by the relocation section '%s'",
+ Symbols->Name.data(), this->Name.data());
+ Symbols = nullptr;
+ }
+
+ for (const Relocation &R : Relocations) {
+ if (!R.RelocSymbol || !R.RelocSymbol->DefinedIn ||
+ !ToRemove(R.RelocSymbol->DefinedIn))
+ continue;
+ return createStringError(llvm::errc::invalid_argument,
+ "section '%s' cannot be removed: (%s+0x%" PRIx64
+ ") has relocation against symbol '%s'",
+ R.RelocSymbol->DefinedIn->Name.data(),
+ SecToApplyRel->Name.data(), R.Offset,
+ R.RelocSymbol->Name.c_str());
+ }
+
+ return Error::success();
+}
+
+template <class SymTabType>
+Error RelocSectionWithSymtabBase<SymTabType>::initialize(
+ SectionTableRef SecTable) {
+ if (Link != SHN_UNDEF) {
+ Expected<SymTabType *> Sec = 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 (!Sec)
+ return Sec.takeError();
+
+ setSymTab(*Sec);
+ }
+
+ if (Info != SHN_UNDEF) {
+ Expected<SectionBase *> Sec =
+ SecTable.getSection(Info, "Info field value " + Twine(Info) +
+ " in section " + Name + " is invalid");
+ if (!Sec)
+ return Sec.takeError();
+
+ setSection(*Sec);
+ } else
+ setSection(nullptr);
+
+ return Error::success();
+}
+
+template <class SymTabType>
+void RelocSectionWithSymtabBase<SymTabType>::finalize() {
+ this->Link = Symbols ? Symbols->Index : 0;
+
+ if (SecToApplyRel != nullptr)
+ this->Info = SecToApplyRel->Index;
+}
+
+template <class ELFT>
+static void setAddend(Elf_Rel_Impl<ELFT, false> &, uint64_t) {}
+
+template <class ELFT>
+static void setAddend(Elf_Rel_Impl<ELFT, true> &Rela, uint64_t Addend) {
+ Rela.r_addend = Addend;
+}
+
+template <class RelRange, class T>
+static void writeRel(const RelRange &Relocations, T *Buf, bool IsMips64EL) {
+ for (const auto &Reloc : Relocations) {
+ Buf->r_offset = Reloc.Offset;
+ setAddend(*Buf, Reloc.Addend);
+ Buf->setSymbolAndType(Reloc.RelocSymbol ? Reloc.RelocSymbol->Index : 0,
+ Reloc.Type, IsMips64EL);
+ ++Buf;
+ }
+}
+
+template <class ELFT>
+Error ELFSectionWriter<ELFT>::visit(const RelocationSection &Sec) {
+ uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
+ if (Sec.Type == SHT_REL)
+ writeRel(Sec.Relocations, reinterpret_cast<Elf_Rel *>(Buf),
+ Sec.getObject().IsMips64EL);
+ else
+ writeRel(Sec.Relocations, reinterpret_cast<Elf_Rela *>(Buf),
+ Sec.getObject().IsMips64EL);
+ return Error::success();
+}
+
+Error RelocationSection::accept(SectionVisitor &Visitor) const {
+ return Visitor.visit(*this);
+}
+
+Error RelocationSection::accept(MutableSectionVisitor &Visitor) {
+ return Visitor.visit(*this);
+}
+
+Error RelocationSection::removeSymbols(
+ function_ref<bool(const Symbol &)> ToRemove) {
+ for (const Relocation &Reloc : Relocations)
+ if (Reloc.RelocSymbol && ToRemove(*Reloc.RelocSymbol))
+ return createStringError(
+ llvm::errc::invalid_argument,
+ "not stripping symbol '%s' because it is named in a relocation",
+ Reloc.RelocSymbol->Name.data());
+ return Error::success();
+}
+
+void RelocationSection::markSymbols() {
+ for (const Relocation &Reloc : Relocations)
+ if (Reloc.RelocSymbol)
+ Reloc.RelocSymbol->Referenced = true;
+}
+
+void RelocationSection::replaceSectionReferences(
+ const DenseMap<SectionBase *, SectionBase *> &FromTo) {
+ // Update the target section if it was replaced.
+ if (SectionBase *To = FromTo.lookup(SecToApplyRel))
+ SecToApplyRel = To;
+}
+
+Error SectionWriter::visit(const DynamicRelocationSection &Sec) {
+ llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset);
+ return Error::success();
+}
+
+Error DynamicRelocationSection::accept(SectionVisitor &Visitor) const {
+ return Visitor.visit(*this);
+}
+
+Error DynamicRelocationSection::accept(MutableSectionVisitor &Visitor) {
+ return Visitor.visit(*this);
+}
+
+Error DynamicRelocationSection::removeSectionReferences(
+ bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) {
+ if (ToRemove(Symbols)) {
+ if (!AllowBrokenLinks)
+ return createStringError(
+ llvm::errc::invalid_argument,
+ "symbol table '%s' cannot be removed because it is "
+ "referenced by the relocation section '%s'",
+ Symbols->Name.data(), this->Name.data());
+ Symbols = nullptr;
+ }
+
+ // SecToApplyRel contains a section referenced by sh_info field. It keeps
+ // a section to which the relocation section applies. When we remove any
+ // sections we also remove their relocation sections. Since we do that much
+ // earlier, this assert should never be triggered.
+ assert(!SecToApplyRel || !ToRemove(SecToApplyRel));
+ return Error::success();
+}
+
+Error Section::removeSectionReferences(
+ bool AllowBrokenDependency,
+ function_ref<bool(const SectionBase *)> ToRemove) {
+ if (ToRemove(LinkSection)) {
+ if (!AllowBrokenDependency)
+ return createStringError(llvm::errc::invalid_argument,
+ "section '%s' cannot be removed because it is "
+ "referenced by the section '%s'",
+ LinkSection->Name.data(), this->Name.data());
+ LinkSection = nullptr;
+ }
+ return Error::success();
+}
+
+void GroupSection::finalize() {
+ this->Info = Sym ? Sym->Index : 0;
+ this->Link = SymTab ? SymTab->Index : 0;
+ // Linker deduplication for GRP_COMDAT is based on Sym->Name. The local/global
+ // status is not part of the equation. If Sym is localized, the intention is
+ // likely to make the group fully localized. Drop GRP_COMDAT to suppress
+ // deduplication. See https://groups.google.com/g/generic-abi/c/2X6mR-s2zoc
+ if ((FlagWord & GRP_COMDAT) && Sym && Sym->Binding == STB_LOCAL)
+ this->FlagWord &= ~GRP_COMDAT;
+}
+
+Error GroupSection::removeSectionReferences(
+ bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) {
+ if (ToRemove(SymTab)) {
+ if (!AllowBrokenLinks)
+ return createStringError(
+ llvm::errc::invalid_argument,
+ "section '.symtab' cannot be removed because it is "
+ "referenced by the group section '%s'",
+ this->Name.data());
+ SymTab = nullptr;
+ Sym = nullptr;
+ }
+ llvm::erase_if(GroupMembers, ToRemove);
+ return Error::success();
+}
+
+Error GroupSection::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) {
+ if (ToRemove(*Sym))
+ return createStringError(llvm::errc::invalid_argument,
+ "symbol '%s' cannot be removed because it is "
+ "referenced by the section '%s[%d]'",
+ Sym->Name.data(), this->Name.data(), this->Index);
+ return Error::success();
+}
+
+void GroupSection::markSymbols() {
+ if (Sym)
+ Sym->Referenced = true;
+}
+
+void GroupSection::replaceSectionReferences(
+ const DenseMap<SectionBase *, SectionBase *> &FromTo) {
+ for (SectionBase *&Sec : GroupMembers)
+ if (SectionBase *To = FromTo.lookup(Sec))
+ Sec = To;
+}
+
+void GroupSection::onRemove() {
+ // As the header section of the group is removed, drop the Group flag in its
+ // former members.
+ for (SectionBase *Sec : GroupMembers)
+ Sec->Flags &= ~SHF_GROUP;
+}
+
+Error Section::initialize(SectionTableRef SecTable) {
+ if (Link == ELF::SHN_UNDEF)
+ return Error::success();
+
+ Expected<SectionBase *> Sec =
+ SecTable.getSection(Link, "Link field value " + Twine(Link) +
+ " in section " + Name + " is invalid");
+ if (!Sec)
+ return Sec.takeError();
+
+ LinkSection = *Sec;
+
+ if (LinkSection->Type == ELF::SHT_SYMTAB)
+ LinkSection = nullptr;
+
+ return Error::success();
+}
+
+void Section::finalize() { this->Link = LinkSection ? LinkSection->Index : 0; }
+
+void GnuDebugLinkSection::init(StringRef File) {
+ FileName = sys::path::filename(File);
+ // The format for the .gnu_debuglink starts with the file name and is
+ // followed by a null terminator and then the CRC32 of the file. The CRC32
+ // should be 4 byte aligned. So we add the FileName size, a 1 for the null
+ // byte, and then finally push the size to alignment and add 4.
+ Size = alignTo(FileName.size() + 1, 4) + 4;
+ // The CRC32 will only be aligned if we align the whole section.
+ Align = 4;
+ Type = OriginalType = ELF::SHT_PROGBITS;
+ Name = ".gnu_debuglink";
+ // For sections not found in segments, OriginalOffset is only used to
+ // 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();
+}
+
+GnuDebugLinkSection::GnuDebugLinkSection(StringRef File,
+ uint32_t PrecomputedCRC)
+ : FileName(File), CRC32(PrecomputedCRC) {
+ init(File);
+}
+
+template <class ELFT>
+Error ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) {
+ unsigned char *Buf =
+ reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
+ Elf_Word *CRC =
+ reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word));
+ *CRC = Sec.CRC32;
+ llvm::copy(Sec.FileName, Buf);
+ return Error::success();
+}
+
+Error GnuDebugLinkSection::accept(SectionVisitor &Visitor) const {
+ return Visitor.visit(*this);
+}
+
+Error GnuDebugLinkSection::accept(MutableSectionVisitor &Visitor) {
+ return Visitor.visit(*this);
+}
+
+template <class ELFT>
+Error ELFSectionWriter<ELFT>::visit(const GroupSection &Sec) {
+ ELF::Elf32_Word *Buf =
+ reinterpret_cast<ELF::Elf32_Word *>(Out.getBufferStart() + Sec.Offset);
+ support::endian::write32<ELFT::TargetEndianness>(Buf++, Sec.FlagWord);
+ for (SectionBase *S : Sec.GroupMembers)
+ support::endian::write32<ELFT::TargetEndianness>(Buf++, S->Index);
+ return Error::success();
+}
+
+Error GroupSection::accept(SectionVisitor &Visitor) const {
+ return Visitor.visit(*this);
+}
+
+Error GroupSection::accept(MutableSectionVisitor &Visitor) {
+ return Visitor.visit(*this);
+}
+
+// Returns true IFF a section is wholly inside the range of a segment
+static bool sectionWithinSegment(const SectionBase &Sec, const Segment &Seg) {
+ // If a section is empty it should be treated like it has a size of 1. This is
+ // to clarify the case when an empty section lies on a boundary between two
+ // segments and ensures that the section "belongs" to the second segment and
+ // not the first.
+ uint64_t SecSize = Sec.Size ? Sec.Size : 1;
+
+ // Ignore just added sections.
+ if (Sec.OriginalOffset == std::numeric_limits<uint64_t>::max())
+ return false;
+
+ if (Sec.Type == SHT_NOBITS) {
+ if (!(Sec.Flags & SHF_ALLOC))
+ return false;
+
+ bool SectionIsTLS = Sec.Flags & SHF_TLS;
+ bool SegmentIsTLS = Seg.Type == PT_TLS;
+ if (SectionIsTLS != SegmentIsTLS)
+ return false;
+
+ return Seg.VAddr <= Sec.Addr &&
+ Seg.VAddr + Seg.MemSize >= Sec.Addr + SecSize;
+ }
+
+ return Seg.Offset <= Sec.OriginalOffset &&
+ Seg.Offset + Seg.FileSize >= Sec.OriginalOffset + SecSize;
+}
+
+// Returns true IFF a segment's original offset is inside of another segment's
+// range.
+static bool segmentOverlapsSegment(const Segment &Child,
+ const Segment &Parent) {
+
+ return Parent.OriginalOffset <= Child.OriginalOffset &&
+ Parent.OriginalOffset + Parent.FileSize > Child.OriginalOffset;
+}
+
+static bool compareSegmentsByOffset(const Segment *A, const Segment *B) {
+ // Any segment without a parent segment should come before a segment
+ // that has a parent segment.
+ if (A->OriginalOffset < B->OriginalOffset)
+ return true;
+ if (A->OriginalOffset > B->OriginalOffset)
+ return false;
+ return A->Index < B->Index;
+}
+
+void BasicELFBuilder::initFileHeader() {
+ Obj->Flags = 0x0;
+ Obj->Type = ET_REL;
+ Obj->OSABI = ELFOSABI_NONE;
+ Obj->ABIVersion = 0;
+ Obj->Entry = 0x0;
+ Obj->Machine = EM_NONE;
+ Obj->Version = 1;
+}
+
+void BasicELFBuilder::initHeaderSegment() { Obj->ElfHdrSegment.Index = 0; }
+
+StringTableSection *BasicELFBuilder::addStrTab() {
+ auto &StrTab = Obj->addSection<StringTableSection>();
+ StrTab.Name = ".strtab";
+
+ Obj->SectionNames = &StrTab;
+ return &StrTab;
+}
+
+SymbolTableSection *BasicELFBuilder::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;
+}
+
+Error BasicELFBuilder::initSections() {
+ for (SectionBase &Sec : Obj->sections())
+ if (Error Err = Sec.initialize(Obj->sections()))
+ return Err;
+
+ return Error::success();
+}
+
+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, NewSymbolVisibility, 0, 0);
+ SymTab->addSymbol(Prefix + "_end", STB_GLOBAL, STT_NOTYPE, &DataSection,
+ /*Value=*/DataSection.Size, NewSymbolVisibility, 0, 0);
+ SymTab->addSymbol(Prefix + "_size", STB_GLOBAL, STT_NOTYPE, nullptr,
+ /*Value=*/DataSection.Size, NewSymbolVisibility, SHN_ABS,
+ 0);
+}
+
+Expected<std::unique_ptr<Object>> BinaryELFBuilder::build() {
+ initFileHeader();
+ initHeaderSegment();
+
+ SymbolTableSection *SymTab = addSymTab(addStrTab());
+ if (Error Err = initSections())
+ return std::move(Err);
+ addData(SymTab);
+
+ return std::move(Obj);
+}
+
+// Adds sections from IHEX data file. Data should have been
+// fully validated by this time.
+void IHexELFBuilder::addDataSections() {
+ OwnedDataSection *Section = nullptr;
+ uint64_t SegmentAddr = 0, BaseAddr = 0;
+ uint32_t SecNo = 1;
+
+ for (const IHexRecord &R : Records) {
+ uint64_t RecAddr;
+ switch (R.Type) {
+ case IHexRecord::Data:
+ // Ignore empty data records
+ if (R.HexData.empty())
+ continue;
+ RecAddr = R.Addr + SegmentAddr + BaseAddr;
+ if (!Section || Section->Addr + Section->Size != RecAddr) {
+ // OriginalOffset field is only used to sort sections before layout, so
+ // instead of keeping track of real offsets in IHEX file, and as
+ // layoutSections() and layoutSectionsForOnlyKeepDebug() use
+ // llvm::stable_sort(), we can just set it to a constant (zero).
+ Section = &Obj->addSection<OwnedDataSection>(
+ ".sec" + std::to_string(SecNo), RecAddr,
+ ELF::SHF_ALLOC | ELF::SHF_WRITE, 0);
+ SecNo++;
+ }
+ Section->appendHexData(R.HexData);
+ break;
+ case IHexRecord::EndOfFile:
+ break;
+ case IHexRecord::SegmentAddr:
+ // 20-bit segment address.
+ SegmentAddr = checkedGetHex<uint16_t>(R.HexData) << 4;
+ break;
+ case IHexRecord::StartAddr80x86:
+ case IHexRecord::StartAddr:
+ Obj->Entry = checkedGetHex<uint32_t>(R.HexData);
+ assert(Obj->Entry <= 0xFFFFFU);
+ break;
+ case IHexRecord::ExtendedAddr:
+ // 16-31 bits of linear base address
+ BaseAddr = checkedGetHex<uint16_t>(R.HexData) << 16;
+ break;
+ default:
+ llvm_unreachable("unknown record type");
+ }
+ }
+}
+
+Expected<std::unique_ptr<Object>> IHexELFBuilder::build() {
+ initFileHeader();
+ initHeaderSegment();
+ StringTableSection *StrTab = addStrTab();
+ addSymTab(StrTab);
+ if (Error Err = initSections())
+ return std::move(Err);
+ addDataSections();
+
+ return std::move(Obj);
+}
+
+template <class ELFT>
+ELFBuilder<ELFT>::ELFBuilder(const ELFObjectFile<ELFT> &ElfObj, Object &Obj,
+ Optional<StringRef> ExtractPartition)
+ : ElfFile(ElfObj.getELFFile()), Obj(Obj),
+ ExtractPartition(ExtractPartition) {
+ Obj.IsMips64EL = ElfFile.isMips64EL();
+}
+
+template <class ELFT> void ELFBuilder<ELFT>::setParentSegment(Segment &Child) {
+ for (Segment &Parent : Obj.segments()) {
+ // Every segment will overlap with itself but we don't want a segment to
+ // be its own parent so we avoid that situation.
+ if (&Child != &Parent && segmentOverlapsSegment(Child, Parent)) {
+ // We want a canonical "most parental" segment but this requires
+ // inspecting the ParentSegment.
+ if (compareSegmentsByOffset(&Parent, &Child))
+ if (Child.ParentSegment == nullptr ||
+ compareSegmentsByOffset(&Parent, Child.ParentSegment)) {
+ Child.ParentSegment = &Parent;
+ }
+ }
+ }
+}
+
+template <class ELFT> Error ELFBuilder<ELFT>::findEhdrOffset() {
+ if (!ExtractPartition)
+ return Error::success();
+
+ for (const SectionBase &Sec : Obj.sections()) {
+ if (Sec.Type == SHT_LLVM_PART_EHDR && Sec.Name == *ExtractPartition) {
+ EhdrOffset = Sec.Offset;
+ return Error::success();
+ }
+ }
+ return createStringError(errc::invalid_argument,
+ "could not find partition named '" +
+ *ExtractPartition + "'");
+}
+
+template <class ELFT>
+Error ELFBuilder<ELFT>::readProgramHeaders(const ELFFile<ELFT> &HeadersFile) {
+ uint32_t Index = 0;
+
+ Expected<typename ELFFile<ELFT>::Elf_Phdr_Range> Headers =
+ HeadersFile.program_headers();
+ if (!Headers)
+ return Headers.takeError();
+
+ for (const typename ELFFile<ELFT>::Elf_Phdr &Phdr : *Headers) {
+ if (Phdr.p_offset + Phdr.p_filesz > HeadersFile.getBufSize())
+ return createStringError(
+ errc::invalid_argument,
+ "program header with offset 0x" + Twine::utohexstr(Phdr.p_offset) +
+ " and file size 0x" + Twine::utohexstr(Phdr.p_filesz) +
+ " goes past the end of the file");
+
+ ArrayRef<uint8_t> Data{HeadersFile.base() + Phdr.p_offset,
+ (size_t)Phdr.p_filesz};
+ Segment &Seg = Obj.addSegment(Data);
+ Seg.Type = Phdr.p_type;
+ Seg.Flags = Phdr.p_flags;
+ Seg.OriginalOffset = Phdr.p_offset + EhdrOffset;
+ Seg.Offset = Phdr.p_offset + EhdrOffset;
+ Seg.VAddr = Phdr.p_vaddr;
+ Seg.PAddr = Phdr.p_paddr;
+ Seg.FileSize = Phdr.p_filesz;
+ Seg.MemSize = Phdr.p_memsz;
+ Seg.Align = Phdr.p_align;
+ Seg.Index = Index++;
+ for (SectionBase &Sec : Obj.sections())
+ if (sectionWithinSegment(Sec, Seg)) {
+ Seg.addSection(&Sec);
+ if (!Sec.ParentSegment || Sec.ParentSegment->Offset > Seg.Offset)
+ Sec.ParentSegment = &Seg;
+ }
+ }
+
+ auto &ElfHdr = Obj.ElfHdrSegment;
+ ElfHdr.Index = Index++;
+ ElfHdr.OriginalOffset = ElfHdr.Offset = EhdrOffset;
+
+ const typename ELFT::Ehdr &Ehdr = HeadersFile.getHeader();
+ auto &PrHdr = Obj.ProgramHdrSegment;
+ PrHdr.Type = PT_PHDR;
+ PrHdr.Flags = 0;
+ // The spec requires us to have p_vaddr % p_align == p_offset % p_align.
+ // Whereas this works automatically for ElfHdr, here OriginalOffset is
+ // always non-zero and to ensure the equation we assign the same value to
+ // VAddr as well.
+ PrHdr.OriginalOffset = PrHdr.Offset = PrHdr.VAddr = EhdrOffset + Ehdr.e_phoff;
+ PrHdr.PAddr = 0;
+ PrHdr.FileSize = PrHdr.MemSize = Ehdr.e_phentsize * Ehdr.e_phnum;
+ // The spec requires us to naturally align all the fields.
+ PrHdr.Align = sizeof(Elf_Addr);
+ PrHdr.Index = Index++;
+
+ // Now we do an O(n^2) loop through the segments in order to match up
+ // segments.
+ for (Segment &Child : Obj.segments())
+ setParentSegment(Child);
+ setParentSegment(ElfHdr);
+ setParentSegment(PrHdr);
+
+ return Error::success();
+}
+
+template <class ELFT>
+Error ELFBuilder<ELFT>::initGroupSection(GroupSection *GroupSec) {
+ if (GroupSec->Align % sizeof(ELF::Elf32_Word) != 0)
+ return createStringError(errc::invalid_argument,
+ "invalid alignment " + Twine(GroupSec->Align) +
+ " of group section '" + GroupSec->Name + "'");
+ SectionTableRef SecTable = Obj.sections();
+ if (GroupSec->Link != SHN_UNDEF) {
+ auto SymTab = SecTable.template getSectionOfType<SymbolTableSection>(
+ GroupSec->Link,
+ "link field value '" + Twine(GroupSec->Link) + "' in section '" +
+ GroupSec->Name + "' is invalid",
+ "link field value '" + Twine(GroupSec->Link) + "' in section '" +
+ GroupSec->Name + "' is not a symbol table");
+ if (!SymTab)
+ return SymTab.takeError();
+
+ Expected<Symbol *> Sym = (*SymTab)->getSymbolByIndex(GroupSec->Info);
+ if (!Sym)
+ return createStringError(errc::invalid_argument,
+ "info field value '" + Twine(GroupSec->Info) +
+ "' in section '" + GroupSec->Name +
+ "' is not a valid symbol index");
+ GroupSec->setSymTab(*SymTab);
+ GroupSec->setSymbol(*Sym);
+ }
+ if (GroupSec->Contents.size() % sizeof(ELF::Elf32_Word) ||
+ GroupSec->Contents.empty())
+ return createStringError(errc::invalid_argument,
+ "the content of the section " + GroupSec->Name +
+ " is malformed");
+ const ELF::Elf32_Word *Word =
+ reinterpret_cast<const ELF::Elf32_Word *>(GroupSec->Contents.data());
+ const ELF::Elf32_Word *End =
+ Word + GroupSec->Contents.size() / sizeof(ELF::Elf32_Word);
+ GroupSec->setFlagWord(
+ support::endian::read32<ELFT::TargetEndianness>(Word++));
+ for (; Word != End; ++Word) {
+ uint32_t Index = support::endian::read32<ELFT::TargetEndianness>(Word);
+ Expected<SectionBase *> Sec = SecTable.getSection(
+ Index, "group member index " + Twine(Index) + " in section '" +
+ GroupSec->Name + "' is invalid");
+ if (!Sec)
+ return Sec.takeError();
+
+ GroupSec->addMember(*Sec);
+ }
+
+ return Error::success();
+}
+
+template <class ELFT>
+Error ELFBuilder<ELFT>::initSymbolTable(SymbolTableSection *SymTab) {
+ Expected<const Elf_Shdr *> Shdr = ElfFile.getSection(SymTab->Index);
+ if (!Shdr)
+ return Shdr.takeError();
+
+ Expected<StringRef> StrTabData = ElfFile.getStringTableForSymtab(**Shdr);
+ if (!StrTabData)
+ return StrTabData.takeError();
+
+ ArrayRef<Elf_Word> ShndxData;
+
+ Expected<typename ELFFile<ELFT>::Elf_Sym_Range> Symbols =
+ ElfFile.symbols(*Shdr);
+ if (!Symbols)
+ return Symbols.takeError();
+
+ for (const typename ELFFile<ELFT>::Elf_Sym &Sym : *Symbols) {
+ SectionBase *DefSection = nullptr;
+
+ Expected<StringRef> Name = Sym.getName(*StrTabData);
+ if (!Name)
+ return Name.takeError();
+
+ if (Sym.st_shndx == SHN_XINDEX) {
+ if (SymTab->getShndxTable() == nullptr)
+ return createStringError(errc::invalid_argument,
+ "symbol '" + *Name +
+ "' has index SHN_XINDEX but no "
+ "SHT_SYMTAB_SHNDX section exists");
+ if (ShndxData.data() == nullptr) {
+ Expected<const Elf_Shdr *> ShndxSec =
+ ElfFile.getSection(SymTab->getShndxTable()->Index);
+ if (!ShndxSec)
+ return ShndxSec.takeError();
+
+ Expected<ArrayRef<Elf_Word>> Data =
+ ElfFile.template getSectionContentsAsArray<Elf_Word>(**ShndxSec);
+ if (!Data)
+ return Data.takeError();
+
+ ShndxData = *Data;
+ if (ShndxData.size() != Symbols->size())
+ return createStringError(
+ errc::invalid_argument,
+ "symbol section index table does not have the same number of "
+ "entries as the symbol table");
+ }
+ Elf_Word Index = ShndxData[&Sym - Symbols->begin()];
+ Expected<SectionBase *> Sec = Obj.sections().getSection(
+ Index,
+ "symbol '" + *Name + "' has invalid section index " + Twine(Index));
+ if (!Sec)
+ return Sec.takeError();
+
+ DefSection = *Sec;
+ } else if (Sym.st_shndx >= SHN_LORESERVE) {
+ if (!isValidReservedSectionIndex(Sym.st_shndx, Obj.Machine)) {
+ return createStringError(
+ errc::invalid_argument,
+ "symbol '" + *Name +
+ "' has unsupported value greater than or equal "
+ "to SHN_LORESERVE: " +
+ Twine(Sym.st_shndx));
+ }
+ } else if (Sym.st_shndx != SHN_UNDEF) {
+ Expected<SectionBase *> Sec = Obj.sections().getSection(
+ Sym.st_shndx, "symbol '" + *Name +
+ "' is defined has invalid section index " +
+ Twine(Sym.st_shndx));
+ if (!Sec)
+ return Sec.takeError();
+
+ DefSection = *Sec;
+ }
+
+ SymTab->addSymbol(*Name, Sym.getBinding(), Sym.getType(), DefSection,
+ Sym.getValue(), Sym.st_other, Sym.st_shndx, Sym.st_size);
+ }
+
+ return Error::success();
+}
+
+template <class ELFT>
+static void getAddend(uint64_t &, const Elf_Rel_Impl<ELFT, false> &) {}
+
+template <class ELFT>
+static void getAddend(uint64_t &ToSet, const Elf_Rel_Impl<ELFT, true> &Rela) {
+ ToSet = Rela.r_addend;
+}
+
+template <class T>
+static Error initRelocations(RelocationSection *Relocs, T RelRange) {
+ for (const auto &Rel : RelRange) {
+ Relocation ToAdd;
+ ToAdd.Offset = Rel.r_offset;
+ getAddend(ToAdd.Addend, Rel);
+ ToAdd.Type = Rel.getType(Relocs->getObject().IsMips64EL);
+
+ if (uint32_t Sym = Rel.getSymbol(Relocs->getObject().IsMips64EL)) {
+ if (!Relocs->getObject().SymbolTable)
+ return createStringError(
+ errc::invalid_argument,
+ "'" + Relocs->Name + "': relocation references symbol with index " +
+ Twine(Sym) + ", but there is no symbol table");
+ Expected<Symbol *> SymByIndex =
+ Relocs->getObject().SymbolTable->getSymbolByIndex(Sym);
+ if (!SymByIndex)
+ return SymByIndex.takeError();
+
+ ToAdd.RelocSymbol = *SymByIndex;
+ }
+
+ Relocs->addRelocation(ToAdd);
+ }
+
+ return Error::success();
+}
+
+Expected<SectionBase *> SectionTableRef::getSection(uint32_t Index,
+ Twine ErrMsg) {
+ if (Index == SHN_UNDEF || Index > Sections.size())
+ return createStringError(errc::invalid_argument, ErrMsg);
+ return Sections[Index - 1].get();
+}
+
+template <class T>
+Expected<T *> SectionTableRef::getSectionOfType(uint32_t Index,
+ Twine IndexErrMsg,
+ Twine TypeErrMsg) {
+ Expected<SectionBase *> BaseSec = getSection(Index, IndexErrMsg);
+ if (!BaseSec)
+ return BaseSec.takeError();
+
+ if (T *Sec = dyn_cast<T>(*BaseSec))
+ return Sec;
+
+ return createStringError(errc::invalid_argument, TypeErrMsg);
+}
+
+template <class ELFT>
+Expected<SectionBase &> ELFBuilder<ELFT>::makeSection(const Elf_Shdr &Shdr) {
+ switch (Shdr.sh_type) {
+ case SHT_REL:
+ case SHT_RELA:
+ if (Shdr.sh_flags & SHF_ALLOC) {
+ if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr))
+ return Obj.addSection<DynamicRelocationSection>(*Data);
+ else
+ return Data.takeError();
+ }
+ return Obj.addSection<RelocationSection>(Obj);
+ case SHT_STRTAB:
+ // If a string table is allocated we don't want to mess with it. That would
+ // mean altering the memory image. There are no special link types or
+ // anything so we can just use a Section.
+ if (Shdr.sh_flags & SHF_ALLOC) {
+ if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr))
+ return Obj.addSection<Section>(*Data);
+ else
+ return Data.takeError();
+ }
+ return Obj.addSection<StringTableSection>();
+ case SHT_HASH:
+ case SHT_GNU_HASH:
+ // Hash tables should refer to SHT_DYNSYM which we're not going to change.
+ // Because of this we don't need to mess with the hash tables either.
+ if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr))
+ return Obj.addSection<Section>(*Data);
+ else
+ return Data.takeError();
+ case SHT_GROUP:
+ if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr))
+ return Obj.addSection<GroupSection>(*Data);
+ else
+ return Data.takeError();
+ case SHT_DYNSYM:
+ if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr))
+ return Obj.addSection<DynamicSymbolTableSection>(*Data);
+ else
+ return Data.takeError();
+ case SHT_DYNAMIC:
+ if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr))
+ return Obj.addSection<DynamicSection>(*Data);
+ else
+ return Data.takeError();
+ case SHT_SYMTAB: {
+ auto &SymTab = Obj.addSection<SymbolTableSection>();
+ Obj.SymbolTable = &SymTab;
+ return SymTab;
+ }
+ case SHT_SYMTAB_SHNDX: {
+ auto &ShndxSection = Obj.addSection<SectionIndexSection>();
+ Obj.SectionIndexTable = &ShndxSection;
+ return ShndxSection;
+ }
+ case SHT_NOBITS:
+ return Obj.addSection<Section>(ArrayRef<uint8_t>());
+ default: {
+ Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr);
+ if (!Data)
+ return Data.takeError();
+
+ Expected<StringRef> Name = ElfFile.getSectionName(Shdr);
+ if (!Name)
+ return Name.takeError();
+
+ if (Name->startswith(".zdebug") || (Shdr.sh_flags & ELF::SHF_COMPRESSED)) {
+ uint64_t DecompressedSize, DecompressedAlign;
+ std::tie(DecompressedSize, DecompressedAlign) =
+ getDecompressedSizeAndAlignment<ELFT>(*Data);
+ Expected<CompressedSection> NewSection =
+ CompressedSection::create(*Data, DecompressedSize, DecompressedAlign);
+ if (!NewSection)
+ return NewSection.takeError();
+
+ return Obj.addSection<CompressedSection>(std::move(*NewSection));
+ }
+
+ return Obj.addSection<Section>(*Data);
+ }
+ }
+}
+
+template <class ELFT> Error ELFBuilder<ELFT>::readSectionHeaders() {
+ uint32_t Index = 0;
+ Expected<typename ELFFile<ELFT>::Elf_Shdr_Range> Sections =
+ ElfFile.sections();
+ if (!Sections)
+ return Sections.takeError();
+
+ for (const typename ELFFile<ELFT>::Elf_Shdr &Shdr : *Sections) {
+ if (Index == 0) {
+ ++Index;
+ continue;
+ }
+ Expected<SectionBase &> Sec = makeSection(Shdr);
+ if (!Sec)
+ return Sec.takeError();
+
+ Expected<StringRef> SecName = ElfFile.getSectionName(Shdr);
+ if (!SecName)
+ return SecName.takeError();
+ Sec->Name = SecName->str();
+ Sec->Type = Sec->OriginalType = Shdr.sh_type;
+ Sec->Flags = Sec->OriginalFlags = Shdr.sh_flags;
+ Sec->Addr = Shdr.sh_addr;
+ Sec->Offset = Shdr.sh_offset;
+ Sec->OriginalOffset = Shdr.sh_offset;
+ Sec->Size = Shdr.sh_size;
+ Sec->Link = Shdr.sh_link;
+ Sec->Info = Shdr.sh_info;
+ Sec->Align = Shdr.sh_addralign;
+ Sec->EntrySize = Shdr.sh_entsize;
+ Sec->Index = Index++;
+ Sec->OriginalIndex = Sec->Index;
+ Sec->OriginalData =
+ ArrayRef<uint8_t>(ElfFile.base() + Shdr.sh_offset,
+ (Shdr.sh_type == SHT_NOBITS) ? (size_t)0 : Shdr.sh_size);
+ }
+
+ return Error::success();
+}
+
+template <class ELFT> Error ELFBuilder<ELFT>::readSections(bool EnsureSymtab) {
+ uint32_t ShstrIndex = ElfFile.getHeader().e_shstrndx;
+ if (ShstrIndex == SHN_XINDEX) {
+ Expected<const Elf_Shdr *> Sec = ElfFile.getSection(0);
+ if (!Sec)
+ return Sec.takeError();
+
+ ShstrIndex = (*Sec)->sh_link;
+ }
+
+ if (ShstrIndex == SHN_UNDEF)
+ Obj.HadShdrs = false;
+ else {
+ Expected<StringTableSection *> Sec =
+ Obj.sections().template getSectionOfType<StringTableSection>(
+ ShstrIndex,
+ "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " +
+ " is invalid",
+ "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " +
+ " does not reference a string table");
+ if (!Sec)
+ return Sec.takeError();
+
+ Obj.SectionNames = *Sec;
+ }
+
+ // If a section index table exists we'll need to initialize it before we
+ // initialize the symbol table because the symbol table might need to
+ // reference it.
+ if (Obj.SectionIndexTable)
+ if (Error Err = Obj.SectionIndexTable->initialize(Obj.sections()))
+ return Err;
+
+ // Now that all of the sections have been added we can fill out some extra
+ // details about symbol tables. We need the symbol table filled out before
+ // any relocations.
+ if (Obj.SymbolTable) {
+ if (Error Err = Obj.SymbolTable->initialize(Obj.sections()))
+ return Err;
+ if (Error Err = initSymbolTable(Obj.SymbolTable))
+ return Err;
+ } else if (EnsureSymtab) {
+ if (Error Err = Obj.addNewSymbolTable())
+ return Err;
+ }
+
+ // Now that all sections and symbols have been added we can add
+ // relocations that reference symbols and set the link and info fields for
+ // relocation sections.
+ for (SectionBase &Sec : Obj.sections()) {
+ if (&Sec == Obj.SymbolTable)
+ continue;
+ if (Error Err = Sec.initialize(Obj.sections()))
+ return Err;
+ if (auto RelSec = dyn_cast<RelocationSection>(&Sec)) {
+ Expected<typename ELFFile<ELFT>::Elf_Shdr_Range> Sections =
+ ElfFile.sections();
+ if (!Sections)
+ return Sections.takeError();
+
+ const typename ELFFile<ELFT>::Elf_Shdr *Shdr =
+ Sections->begin() + RelSec->Index;
+ if (RelSec->Type == SHT_REL) {
+ Expected<typename ELFFile<ELFT>::Elf_Rel_Range> Rels =
+ ElfFile.rels(*Shdr);
+ if (!Rels)
+ return Rels.takeError();
+
+ if (Error Err = initRelocations(RelSec, *Rels))
+ return Err;
+ } else {
+ Expected<typename ELFFile<ELFT>::Elf_Rela_Range> Relas =
+ ElfFile.relas(*Shdr);
+ if (!Relas)
+ return Relas.takeError();
+
+ if (Error Err = initRelocations(RelSec, *Relas))
+ return Err;
+ }
+ } else if (auto GroupSec = dyn_cast<GroupSection>(&Sec)) {
+ if (Error Err = initGroupSection(GroupSec))
+ return Err;
+ }
+ }
+
+ return Error::success();
+}
+
+template <class ELFT> Error ELFBuilder<ELFT>::build(bool EnsureSymtab) {
+ if (Error E = readSectionHeaders())
+ return E;
+ if (Error E = findEhdrOffset())
+ return E;
+
+ // The ELFFile whose ELF headers and program headers are copied into the
+ // output file. Normally the same as ElfFile, but if we're extracting a
+ // loadable partition it will point to the partition's headers.
+ Expected<ELFFile<ELFT>> HeadersFile = ELFFile<ELFT>::create(toStringRef(
+ {ElfFile.base() + EhdrOffset, ElfFile.getBufSize() - EhdrOffset}));
+ if (!HeadersFile)
+ return HeadersFile.takeError();
+
+ const typename ELFFile<ELFT>::Elf_Ehdr &Ehdr = HeadersFile->getHeader();
+ 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;
+ Obj.Entry = Ehdr.e_entry;
+ Obj.Flags = Ehdr.e_flags;
+
+ if (Error E = readSections(EnsureSymtab))
+ return E;
+ return readProgramHeaders(*HeadersFile);
+}
+
+Writer::~Writer() {}
+
+Reader::~Reader() {}
+
+Expected<std::unique_ptr<Object>>
+BinaryReader::create(bool /*EnsureSymtab*/) const {
+ return BinaryELFBuilder(MemBuf, NewSymbolVisibility).build();
+}
+
+Expected<std::vector<IHexRecord>> IHexReader::parse() const {
+ SmallVector<StringRef, 16> Lines;
+ std::vector<IHexRecord> Records;
+ bool HasSections = false;
+
+ MemBuf->getBuffer().split(Lines, '\n');
+ Records.reserve(Lines.size());
+ for (size_t LineNo = 1; LineNo <= Lines.size(); ++LineNo) {
+ StringRef Line = Lines[LineNo - 1].trim();
+ if (Line.empty())
+ continue;
+
+ Expected<IHexRecord> R = IHexRecord::parse(Line);
+ if (!R)
+ return parseError(LineNo, R.takeError());
+ if (R->Type == IHexRecord::EndOfFile)
+ break;
+ HasSections |= (R->Type == IHexRecord::Data);
+ Records.push_back(*R);
+ }
+ if (!HasSections)
+ return parseError(-1U, "no sections");
+
+ return std::move(Records);
+}
+
+Expected<std::unique_ptr<Object>>
+IHexReader::create(bool /*EnsureSymtab*/) const {
+ Expected<std::vector<IHexRecord>> Records = parse();
+ if (!Records)
+ return Records.takeError();
+
+ return IHexELFBuilder(*Records).build();
+}
+
+Expected<std::unique_ptr<Object>> ELFReader::create(bool EnsureSymtab) const {
+ auto Obj = std::make_unique<Object>();
+ if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) {
+ ELFBuilder<ELF32LE> Builder(*O, *Obj, ExtractPartition);
+ if (Error Err = Builder.build(EnsureSymtab))
+ return std::move(Err);
+ return std::move(Obj);
+ } else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) {
+ ELFBuilder<ELF64LE> Builder(*O, *Obj, ExtractPartition);
+ if (Error Err = Builder.build(EnsureSymtab))
+ return std::move(Err);
+ return std::move(Obj);
+ } else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) {
+ ELFBuilder<ELF32BE> Builder(*O, *Obj, ExtractPartition);
+ if (Error Err = Builder.build(EnsureSymtab))
+ return std::move(Err);
+ return std::move(Obj);
+ } else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) {
+ ELFBuilder<ELF64BE> Builder(*O, *Obj, ExtractPartition);
+ if (Error Err = Builder.build(EnsureSymtab))
+ return std::move(Err);
+ return std::move(Obj);
+ }
+ return createStringError(errc::invalid_argument, "invalid file type");
+}
+
+template <class ELFT> void ELFWriter<ELFT>::writeEhdr() {
+ Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf->getBufferStart());
+ 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;
+ // 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);
+ if (WriteSectionHeaders && Obj.sections().size() != 0) {
+ Ehdr.e_shentsize = sizeof(Elf_Shdr);
+ Ehdr.e_shoff = Obj.SHOff;
+ // """
+ // If the number of sections is greater than or equal to
+ // SHN_LORESERVE (0xff00), this member has the value zero and the actual
+ // number of section header table entries is contained in the sh_size field
+ // of the section header at index 0.
+ // """
+ auto Shnum = Obj.sections().size() + 1;
+ if (Shnum >= SHN_LORESERVE)
+ Ehdr.e_shnum = 0;
+ else
+ Ehdr.e_shnum = Shnum;
+ // """
+ // If the section name string table section index is greater than or equal
+ // to SHN_LORESERVE (0xff00), this member has the value SHN_XINDEX (0xffff)
+ // and the actual index of the section name string table section is
+ // contained in the sh_link field of the section header at index 0.
+ // """
+ if (Obj.SectionNames->Index >= SHN_LORESERVE)
+ Ehdr.e_shstrndx = SHN_XINDEX;
+ else
+ Ehdr.e_shstrndx = Obj.SectionNames->Index;
+ } else {
+ Ehdr.e_shentsize = 0;
+ Ehdr.e_shoff = 0;
+ Ehdr.e_shnum = 0;
+ Ehdr.e_shstrndx = 0;
+ }
+}
+
+template <class ELFT> void ELFWriter<ELFT>::writePhdrs() {
+ for (auto &Seg : Obj.segments())
+ writePhdr(Seg);
+}
+
+template <class ELFT> void ELFWriter<ELFT>::writeShdrs() {
+ // This reference serves to write the dummy section header at the begining
+ // of the file. It is not used for anything else
+ Elf_Shdr &Shdr =
+ *reinterpret_cast<Elf_Shdr *>(Buf->getBufferStart() + Obj.SHOff);
+ Shdr.sh_name = 0;
+ Shdr.sh_type = SHT_NULL;
+ Shdr.sh_flags = 0;
+ Shdr.sh_addr = 0;
+ Shdr.sh_offset = 0;
+ // See writeEhdr for why we do this.
+ uint64_t Shnum = Obj.sections().size() + 1;
+ if (Shnum >= SHN_LORESERVE)
+ Shdr.sh_size = Shnum;
+ else
+ Shdr.sh_size = 0;
+ // See writeEhdr for why we do this.
+ if (Obj.SectionNames != nullptr && Obj.SectionNames->Index >= SHN_LORESERVE)
+ Shdr.sh_link = Obj.SectionNames->Index;
+ else
+ Shdr.sh_link = 0;
+ Shdr.sh_info = 0;
+ Shdr.sh_addralign = 0;
+ Shdr.sh_entsize = 0;
+
+ for (SectionBase &Sec : Obj.sections())
+ writeShdr(Sec);
+}
+
+template <class ELFT> Error ELFWriter<ELFT>::writeSectionData() {
+ for (SectionBase &Sec : Obj.sections())
+ // Segments are responsible for writing their contents, so only write the
+ // section data if the section is not in a segment. Note that this renders
+ // sections in segments effectively immutable.
+ if (Sec.ParentSegment == nullptr)
+ if (Error Err = Sec.accept(*SecWriter))
+ return Err;
+
+ return Error::success();
+}
+
+template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() {
+ for (Segment &Seg : Obj.segments()) {
+ size_t Size = std::min<size_t>(Seg.FileSize, Seg.getContents().size());
+ std::memcpy(Buf->getBufferStart() + Seg.Offset, Seg.getContents().data(),
+ Size);
+ }
+
+ for (auto it : Obj.getUpdatedSections()) {
+ SectionBase *Sec = it.first;
+ ArrayRef<uint8_t> Data = it.second;
+
+ auto *Parent = Sec->ParentSegment;
+ assert(Parent && "This section should've been part of a segment.");
+ uint64_t Offset =
+ Sec->OriginalOffset - Parent->OriginalOffset + Parent->Offset;
+ llvm::copy(Data, Buf->getBufferStart() + Offset);
+ }
+
+ // Iterate over removed sections and overwrite their old data with zeroes.
+ for (auto &Sec : Obj.removedSections()) {
+ Segment *Parent = Sec.ParentSegment;
+ if (Parent == nullptr || Sec.Type == SHT_NOBITS || Sec.Size == 0)
+ continue;
+ uint64_t Offset =
+ Sec.OriginalOffset - Parent->OriginalOffset + Parent->Offset;
+ std::memset(Buf->getBufferStart() + Offset, 0, Sec.Size);
+ }
+}
+
+template <class ELFT>
+ELFWriter<ELFT>::ELFWriter(Object &Obj, raw_ostream &Buf, bool WSH,
+ bool OnlyKeepDebug)
+ : Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs),
+ OnlyKeepDebug(OnlyKeepDebug) {}
+
+Error Object::updateSection(StringRef Name, ArrayRef<uint8_t> Data) {
+ auto It = llvm::find_if(Sections,
+ [&](const SecPtr &Sec) { return Sec->Name == Name; });
+ if (It == Sections.end())
+ return createStringError(errc::invalid_argument, "section '%s' not found",
+ Name.str().c_str());
+
+ auto *OldSec = It->get();
+ if (!OldSec->hasContents())
+ return createStringError(
+ errc::invalid_argument,
+ "section '%s' can't be updated because it does not have contents",
+ Name.str().c_str());
+
+ if (Data.size() > OldSec->Size && OldSec->ParentSegment)
+ return createStringError(errc::invalid_argument,
+ "cannot fit data of size %zu into section '%s' "
+ "with size %zu that is part of a segment",
+ Data.size(), Name.str().c_str(), OldSec->Size);
+
+ if (!OldSec->ParentSegment) {
+ *It = std::make_unique<OwnedDataSection>(*OldSec, Data);
+ } else {
+ // The segment writer will be in charge of updating these contents.
+ OldSec->Size = Data.size();
+ UpdatedSections[OldSec] = Data;
+ }
+
+ return Error::success();
+}
+
+Error Object::removeSections(
+ bool AllowBrokenLinks, std::function<bool(const SectionBase &)> ToRemove) {
+
+ auto Iter = std::stable_partition(
+ std::begin(Sections), std::end(Sections), [=](const SecPtr &Sec) {
+ if (ToRemove(*Sec))
+ return false;
+ if (auto RelSec = dyn_cast<RelocationSectionBase>(Sec.get())) {
+ if (auto ToRelSec = RelSec->getSection())
+ return !ToRemove(*ToRelSec);
+ }
+ return true;
+ });
+ if (SymbolTable != nullptr && ToRemove(*SymbolTable))
+ SymbolTable = nullptr;
+ if (SectionNames != nullptr && ToRemove(*SectionNames))
+ SectionNames = nullptr;
+ if (SectionIndexTable != nullptr && ToRemove(*SectionIndexTable))
+ SectionIndexTable = nullptr;
+ // Now make sure there are no remaining references to the sections that will
+ // be removed. Sometimes it is impossible to remove a reference so we emit
+ // an error here instead.
+ std::unordered_set<const SectionBase *> RemoveSections;
+ RemoveSections.reserve(std::distance(Iter, std::end(Sections)));
+ for (auto &RemoveSec : make_range(Iter, std::end(Sections))) {
+ for (auto &Segment : Segments)
+ Segment->removeSection(RemoveSec.get());
+ RemoveSec->onRemove();
+ RemoveSections.insert(RemoveSec.get());
+ }
+
+ // For each section that remains alive, we want to remove the dead references.
+ // This either might update the content of the section (e.g. remove symbols
+ // from symbol table that belongs to removed section) or trigger an error if
+ // a live section critically depends on a section being removed somehow
+ // (e.g. the removed section is referenced by a relocation).
+ for (auto &KeepSec : make_range(std::begin(Sections), Iter)) {
+ if (Error E = KeepSec->removeSectionReferences(
+ AllowBrokenLinks, [&RemoveSections](const SectionBase *Sec) {
+ return RemoveSections.find(Sec) != RemoveSections.end();
+ }))
+ return E;
+ }
+
+ // Transfer removed sections into the Object RemovedSections container for use
+ // later.
+ std::move(Iter, Sections.end(), std::back_inserter(RemovedSections));
+ // Now finally get rid of them all together.
+ Sections.erase(Iter, std::end(Sections));
+ return Error::success();
+}
+
+Error Object::replaceSections(
+ const DenseMap<SectionBase *, SectionBase *> &FromTo) {
+ auto SectionIndexLess = [](const SecPtr &Lhs, const SecPtr &Rhs) {
+ return Lhs->Index < Rhs->Index;
+ };
+ assert(llvm::is_sorted(Sections, SectionIndexLess) &&
+ "Sections are expected to be sorted by Index");
+ // Set indices of new sections so that they can be later sorted into positions
+ // of removed ones.
+ for (auto &I : FromTo)
+ I.second->Index = I.first->Index;
+
+ // Notify all sections about the replacement.
+ for (auto &Sec : Sections)
+ Sec->replaceSectionReferences(FromTo);
+
+ if (Error E = removeSections(
+ /*AllowBrokenLinks=*/false,
+ [=](const SectionBase &Sec) { return FromTo.count(&Sec) > 0; }))
+ return E;
+ llvm::sort(Sections, SectionIndexLess);
+ return Error::success();
+}
+
+Error Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) {
+ if (SymbolTable)
+ for (const SecPtr &Sec : Sections)
+ if (Error E = Sec->removeSymbols(ToRemove))
+ return E;
+ return Error::success();
+}
+
+Error Object::addNewSymbolTable() {
+ assert(!SymbolTable && "Object must not has a SymbolTable.");
+
+ // Reuse an existing SHT_STRTAB section if it exists.
+ StringTableSection *StrTab = nullptr;
+ for (SectionBase &Sec : sections()) {
+ if (Sec.Type == ELF::SHT_STRTAB && !(Sec.Flags & SHF_ALLOC)) {
+ StrTab = static_cast<StringTableSection *>(&Sec);
+
+ // Prefer a string table that is not the section header string table, if
+ // such a table exists.
+ if (SectionNames != &Sec)
+ break;
+ }
+ }
+ if (!StrTab)
+ StrTab = &addSection<StringTableSection>();
+
+ SymbolTableSection &SymTab = addSection<SymbolTableSection>();
+ SymTab.Name = ".symtab";
+ SymTab.Link = StrTab->Index;
+ if (Error Err = SymTab.initialize(sections()))
+ return Err;
+ SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0);
+
+ SymbolTable = &SymTab;
+
+ return Error::success();
+}
+
+// Orders segments such that if x = y->ParentSegment then y comes before x.
+static void orderSegments(std::vector<Segment *> &Segments) {
+ llvm::stable_sort(Segments, compareSegmentsByOffset);
+}
+
+// This function finds a consistent layout for a list of segments starting from
+// an Offset. It assumes that Segments have been sorted by orderSegments and
+// returns an Offset one past the end of the last segment.
+static uint64_t layoutSegments(std::vector<Segment *> &Segments,
+ uint64_t Offset) {
+ assert(llvm::is_sorted(Segments, compareSegmentsByOffset));
+ // The only way a segment should move is if a section was between two
+ // segments and that section was removed. If that section isn't in a segment
+ // then it's acceptable, but not ideal, to simply move it to after the
+ // segments. So we can simply layout segments one after the other accounting
+ // for alignment.
+ for (Segment *Seg : Segments) {
+ // We assume that segments have been ordered by OriginalOffset and Index
+ // such that a parent segment will always come before a child segment in
+ // OrderedSegments. This means that the Offset of the ParentSegment should
+ // already be set and we can set our offset relative to it.
+ if (Seg->ParentSegment != nullptr) {
+ Segment *Parent = Seg->ParentSegment;
+ Seg->Offset =
+ Parent->Offset + Seg->OriginalOffset - Parent->OriginalOffset;
+ } else {
+ Seg->Offset =
+ alignTo(Offset, std::max<uint64_t>(Seg->Align, 1), Seg->VAddr);
+ }
+ Offset = std::max(Offset, Seg->Offset + Seg->FileSize);
+ }
+ return Offset;
+}
+
+// This function finds a consistent layout for a list of sections. It assumes
+// that the ->ParentSegment of each section has already been laid out. The
+// supplied starting Offset is used for the starting offset of any section that
+// does not have a ParentSegment. It returns either the offset given if all
+// 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) {
+ // 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
+ // the offset from the start of the segment. Using the offset from the start
+ // of the segment we can assign a new offset to the section. For sections not
+ // covered by segments we can just bump Offset to the next valid location.
+ // While it is not necessary, layout the sections in the order based on their
+ // original offsets to resemble the input file as close as possible.
+ std::vector<SectionBase *> OutOfSegmentSections;
+ uint32_t Index = 1;
+ for (auto &Sec : Sections) {
+ Sec.Index = Index++;
+ if (Sec.ParentSegment != nullptr) {
+ auto Segment = *Sec.ParentSegment;
+ Sec.Offset =
+ Segment.Offset + (Sec.OriginalOffset - Segment.OriginalOffset);
+ } else
+ OutOfSegmentSections.push_back(&Sec);
+ }
+
+ llvm::stable_sort(OutOfSegmentSections,
+ [](const SectionBase *Lhs, const SectionBase *Rhs) {
+ return Lhs->OriginalOffset < Rhs->OriginalOffset;
+ });
+ for (auto *Sec : OutOfSegmentSections) {
+ Offset = alignTo(Offset, Sec->Align == 0 ? 1 : Sec->Align);
+ Sec->Offset = Offset;
+ if (Sec->Type != SHT_NOBITS)
+ Offset += Sec->Size;
+ }
+ return Offset;
+}
+
+// Rewrite sh_offset after some sections are changed to SHT_NOBITS and thus
+// occupy no space in the file.
+static uint64_t layoutSectionsForOnlyKeepDebug(Object &Obj, uint64_t Off) {
+ // The layout algorithm requires the sections to be handled in the order of
+ // their offsets in the input file, at least inside segments.
+ std::vector<SectionBase *> Sections;
+ Sections.reserve(Obj.sections().size());
+ uint32_t Index = 1;
+ for (auto &Sec : Obj.sections()) {
+ Sec.Index = Index++;
+ Sections.push_back(&Sec);
+ }
+ llvm::stable_sort(Sections,
+ [](const SectionBase *Lhs, const SectionBase *Rhs) {
+ return Lhs->OriginalOffset < Rhs->OriginalOffset;
+ });
+
+ for (auto *Sec : Sections) {
+ auto *FirstSec = Sec->ParentSegment && Sec->ParentSegment->Type == PT_LOAD
+ ? Sec->ParentSegment->firstSection()
+ : nullptr;
+
+ // The first section in a PT_LOAD has to have congruent offset and address
+ // modulo the alignment, which usually equals the maximum page size.
+ if (FirstSec && FirstSec == Sec)
+ Off = alignTo(Off, Sec->ParentSegment->Align, Sec->Addr);
+
+ // sh_offset is not significant for SHT_NOBITS sections, but the congruence
+ // rule must be followed if it is the first section in a PT_LOAD. Do not
+ // advance Off.
+ if (Sec->Type == SHT_NOBITS) {
+ Sec->Offset = Off;
+ continue;
+ }
+
+ if (!FirstSec) {
+ // FirstSec being nullptr generally means that Sec does not have the
+ // SHF_ALLOC flag.
+ Off = Sec->Align ? alignTo(Off, Sec->Align) : Off;
+ } else if (FirstSec != Sec) {
+ // The offset is relative to the first section in the PT_LOAD segment. Use
+ // sh_offset for non-SHF_ALLOC sections.
+ Off = Sec->OriginalOffset - FirstSec->OriginalOffset + FirstSec->Offset;
+ }
+ Sec->Offset = Off;
+ Off += Sec->Size;
+ }
+ return Off;
+}
+
+// Rewrite p_offset and p_filesz of non-PT_PHDR segments after sh_offset values
+// have been updated.
+static uint64_t layoutSegmentsForOnlyKeepDebug(std::vector<Segment *> &Segments,
+ uint64_t HdrEnd) {
+ uint64_t MaxOffset = 0;
+ for (Segment *Seg : Segments) {
+ if (Seg->Type == PT_PHDR)
+ continue;
+
+ // The segment offset is generally the offset of the first section.
+ //
+ // For a segment containing no section (see sectionWithinSegment), if it has
+ // a parent segment, copy the parent segment's offset field. This works for
+ // empty PT_TLS. If no parent segment, use 0: the segment is not useful for
+ // debugging anyway.
+ const SectionBase *FirstSec = Seg->firstSection();
+ uint64_t Offset =
+ FirstSec ? FirstSec->Offset
+ : (Seg->ParentSegment ? Seg->ParentSegment->Offset : 0);
+ uint64_t FileSize = 0;
+ for (const SectionBase *Sec : Seg->Sections) {
+ uint64_t Size = Sec->Type == SHT_NOBITS ? 0 : Sec->Size;
+ if (Sec->Offset + Size > Offset)
+ FileSize = std::max(FileSize, Sec->Offset + Size - Offset);
+ }
+
+ // If the segment includes EHDR and program headers, don't make it smaller
+ // than the headers.
+ if (Seg->Offset < HdrEnd && HdrEnd <= Seg->Offset + Seg->FileSize) {
+ FileSize += Offset - Seg->Offset;
+ Offset = Seg->Offset;
+ FileSize = std::max(FileSize, HdrEnd - Offset);
+ }
+
+ Seg->Offset = Offset;
+ Seg->FileSize = FileSize;
+ MaxOffset = std::max(MaxOffset, Offset + FileSize);
+ }
+ return MaxOffset;
+}
+
+template <class ELFT> void ELFWriter<ELFT>::initEhdrSegment() {
+ Segment &ElfHdr = Obj.ElfHdrSegment;
+ ElfHdr.Type = PT_PHDR;
+ ElfHdr.Flags = 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
+ // already had its offset properly set.
+ std::vector<Segment *> OrderedSegments;
+ for (Segment &Segment : Obj.segments())
+ OrderedSegments.push_back(&Segment);
+ OrderedSegments.push_back(&Obj.ElfHdrSegment);
+ OrderedSegments.push_back(&Obj.ProgramHdrSegment);
+ orderSegments(OrderedSegments);
+
+ uint64_t Offset;
+ if (OnlyKeepDebug) {
+ // For --only-keep-debug, the sections that did not preserve contents were
+ // changed to SHT_NOBITS. We now rewrite sh_offset fields of sections, and
+ // then rewrite p_offset/p_filesz of program headers.
+ uint64_t HdrEnd =
+ sizeof(Elf_Ehdr) + llvm::size(Obj.segments()) * sizeof(Elf_Phdr);
+ Offset = layoutSectionsForOnlyKeepDebug(Obj, HdrEnd);
+ Offset = std::max(Offset,
+ layoutSegmentsForOnlyKeepDebug(OrderedSegments, HdrEnd));
+ } else {
+ // 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.
+ Offset = layoutSegments(OrderedSegments, 0);
+ 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(Elf_Addr));
+ Obj.SHOff = Offset;
+}
+
+template <class ELFT> size_t ELFWriter<ELFT>::totalSize() const {
+ // We already have the section header offset so we can calculate the total
+ // size by just adding up the size of each section header.
+ if (!WriteSectionHeaders)
+ return Obj.SHOff;
+ size_t ShdrCount = Obj.sections().size() + 1; // Includes null shdr.
+ return Obj.SHOff + ShdrCount * sizeof(Elf_Shdr);
+}
+
+template <class ELFT> Error ELFWriter<ELFT>::write() {
+ // Segment data must be written first, so that the ELF header and program
+ // header tables can overwrite it, if covered by a segment.
+ writeSegmentData();
+ writeEhdr();
+ writePhdrs();
+ if (Error E = writeSectionData())
+ return E;
+ if (WriteSectionHeaders)
+ writeShdrs();
+
+ // TODO: Implement direct writing to the output stream (without intermediate
+ // memory buffer Buf).
+ Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+ return Error::success();
+}
+
+static Error removeUnneededSections(Object &Obj) {
+ // We can remove an empty symbol table from non-relocatable objects.
+ // Relocatable objects typically have relocation sections whose
+ // sh_link field points to .symtab, so we can't remove .symtab
+ // even if it is empty.
+ if (Obj.isRelocatable() || Obj.SymbolTable == nullptr ||
+ !Obj.SymbolTable->empty())
+ return Error::success();
+
+ // .strtab can be used for section names. In such a case we shouldn't
+ // remove it.
+ auto *StrTab = Obj.SymbolTable->getStrTab() == Obj.SectionNames
+ ? nullptr
+ : Obj.SymbolTable->getStrTab();
+ return Obj.removeSections(false, [&](const SectionBase &Sec) {
+ return &Sec == Obj.SymbolTable || &Sec == StrTab;
+ });
+}
+
+template <class ELFT> Error ELFWriter<ELFT>::finalize() {
+ // It could happen that SectionNames has been removed and yet the user wants
+ // a section header table output. We need to throw an error if a user tries
+ // to do that.
+ if (Obj.SectionNames == nullptr && WriteSectionHeaders)
+ return createStringError(llvm::errc::invalid_argument,
+ "cannot write section header table because "
+ "section header string table was removed");
+
+ if (Error E = removeUnneededSections(Obj))
+ return E;
+
+ // We need to assign indexes before we perform layout because we need to know
+ // if we need large indexes or not. We can assign indexes first and check as
+ // we go to see if we will actully need large indexes.
+ bool NeedsLargeIndexes = false;
+ if (Obj.sections().size() >= SHN_LORESERVE) {
+ SectionTableRef Sections = Obj.sections();
+ // Sections doesn't include the null section header, so account for this
+ // when skipping the first N sections.
+ NeedsLargeIndexes =
+ any_of(drop_begin(Sections, SHN_LORESERVE - 1),
+ [](const SectionBase &Sec) { return Sec.HasSymbol; });
+ // TODO: handle case where only one section needs the large index table but
+ // only needs it because the large index table hasn't been removed yet.
+ }
+
+ if (NeedsLargeIndexes) {
+ // This means we definitely need to have a section index table but if we
+ // already have one then we should use it instead of making a new one.
+ if (Obj.SymbolTable != nullptr && Obj.SectionIndexTable == nullptr) {
+ // Addition of a section to the end does not invalidate the indexes of
+ // other sections and assigns the correct index to the new section.
+ auto &Shndx = Obj.addSection<SectionIndexSection>();
+ Obj.SymbolTable->setShndxTable(&Shndx);
+ Shndx.setSymTab(Obj.SymbolTable);
+ }
+ } else {
+ // Since we don't need SectionIndexTable we should remove it and all
+ // references to it.
+ if (Obj.SectionIndexTable != nullptr) {
+ // We do not support sections referring to the section index table.
+ if (Error E = Obj.removeSections(false /*AllowBrokenLinks*/,
+ [this](const SectionBase &Sec) {
+ return &Sec == Obj.SectionIndexTable;
+ }))
+ return E;
+ }
+ }
+
+ // Make sure we add the names of all the sections. Importantly this must be
+ // done after we decide to add or remove SectionIndexes.
+ if (Obj.SectionNames != nullptr)
+ for (const SectionBase &Sec : Obj.sections())
+ Obj.SectionNames->addString(Sec.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;
+ auto SecSizer = std::make_unique<ELFSectionSizer<ELFT>>();
+ for (SectionBase &Sec : Obj.sections()) {
+ Sec.Index = Index++;
+ if (Error Err = Sec.accept(*SecSizer))
+ return Err;
+ }
+
+ // 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
+ // that some sections, like .strtab, don't yet have their final size.
+ if (Obj.SymbolTable != nullptr)
+ Obj.SymbolTable->prepareForLayout();
+
+ // Now that all strings are added we want to finalize string table builders,
+ // because that affects section sizes which in turn affects section offsets.
+ for (SectionBase &Sec : Obj.sections())
+ if (auto StrTab = dyn_cast<StringTableSection>(&Sec))
+ StrTab->prepareForLayout();
+
+ assignOffsets();
+
+ // layoutSections could have modified section indexes, so we need
+ // to fill the index table after assignOffsets.
+ if (Obj.SymbolTable != nullptr)
+ Obj.SymbolTable->fillShndxTable();
+
+ // Finally now that all offsets and indexes have been set we can finalize any
+ // remaining issues.
+ uint64_t Offset = Obj.SHOff + sizeof(Elf_Shdr);
+ for (SectionBase &Sec : Obj.sections()) {
+ Sec.HeaderOffset = Offset;
+ Offset += sizeof(Elf_Shdr);
+ if (WriteSectionHeaders)
+ Sec.NameIndex = Obj.SectionNames->findIndex(Sec.Name);
+ Sec.finalize();
+ }
+
+ size_t TotalSize = totalSize();
+ Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
+ if (!Buf)
+ return createStringError(errc::not_enough_memory,
+ "failed to allocate memory buffer of " +
+ Twine::utohexstr(TotalSize) + " bytes");
+
+ SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(*Buf);
+ return Error::success();
+}
+
+Error BinaryWriter::write() {
+ for (const SectionBase &Sec : Obj.allocSections())
+ if (Error Err = Sec.accept(*SecWriter))
+ return Err;
+
+ // TODO: Implement direct writing to the output stream (without intermediate
+ // memory buffer Buf).
+ Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+ return Error::success();
+}
+
+Error BinaryWriter::finalize() {
+ // Compute the section LMA based on its sh_offset and the containing segment's
+ // p_offset and p_paddr. Also compute the minimum LMA of all non-empty
+ // sections as MinAddr. In the output, the contents between address 0 and
+ // MinAddr will be skipped.
+ uint64_t MinAddr = UINT64_MAX;
+ for (SectionBase &Sec : Obj.allocSections()) {
+ if (Sec.ParentSegment != nullptr)
+ Sec.Addr =
+ Sec.Offset - Sec.ParentSegment->Offset + Sec.ParentSegment->PAddr;
+ if (Sec.Type != SHT_NOBITS && Sec.Size > 0)
+ MinAddr = std::min(MinAddr, Sec.Addr);
+ }
+
+ // 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
+ // layoutSections, because we want to truncate the last segment to the end of
+ // its last non-empty section, to match GNU objcopy's behaviour.
+ TotalSize = 0;
+ for (SectionBase &Sec : Obj.allocSections())
+ if (Sec.Type != SHT_NOBITS && Sec.Size > 0) {
+ Sec.Offset = Sec.Addr - MinAddr;
+ TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size);
+ }
+
+ Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
+ if (!Buf)
+ return createStringError(errc::not_enough_memory,
+ "failed to allocate memory buffer of " +
+ Twine::utohexstr(TotalSize) + " bytes");
+ SecWriter = std::make_unique<BinarySectionWriter>(*Buf);
+ return Error::success();
+}
+
+bool IHexWriter::SectionCompare::operator()(const SectionBase *Lhs,
+ const SectionBase *Rhs) const {
+ return (sectionPhysicalAddr(Lhs) & 0xFFFFFFFFU) <
+ (sectionPhysicalAddr(Rhs) & 0xFFFFFFFFU);
+}
+
+uint64_t IHexWriter::writeEntryPointRecord(uint8_t *Buf) {
+ IHexLineData HexData;
+ uint8_t Data[4] = {};
+ // We don't write entry point record if entry is zero.
+ if (Obj.Entry == 0)
+ return 0;
+
+ if (Obj.Entry <= 0xFFFFFU) {
+ Data[0] = ((Obj.Entry & 0xF0000U) >> 12) & 0xFF;
+ support::endian::write(&Data[2], static_cast<uint16_t>(Obj.Entry),
+ support::big);
+ HexData = IHexRecord::getLine(IHexRecord::StartAddr80x86, 0, Data);
+ } else {
+ support::endian::write(Data, static_cast<uint32_t>(Obj.Entry),
+ support::big);
+ HexData = IHexRecord::getLine(IHexRecord::StartAddr, 0, Data);
+ }
+ memcpy(Buf, HexData.data(), HexData.size());
+ return HexData.size();
+}
+
+uint64_t IHexWriter::writeEndOfFileRecord(uint8_t *Buf) {
+ IHexLineData HexData = IHexRecord::getLine(IHexRecord::EndOfFile, 0, {});
+ memcpy(Buf, HexData.data(), HexData.size());
+ return HexData.size();
+}
+
+Error IHexWriter::write() {
+ IHexSectionWriter Writer(*Buf);
+ // Write sections.
+ for (const SectionBase *Sec : Sections)
+ if (Error Err = Sec->accept(Writer))
+ return Err;
+
+ uint64_t Offset = Writer.getBufferOffset();
+ // Write entry point address.
+ Offset += writeEntryPointRecord(
+ reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Offset);
+ // Write EOF.
+ Offset += writeEndOfFileRecord(
+ reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Offset);
+ assert(Offset == TotalSize);
+
+ // TODO: Implement direct writing to the output stream (without intermediate
+ // memory buffer Buf).
+ Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+ return Error::success();
+}
+
+Error IHexWriter::checkSection(const SectionBase &Sec) {
+ uint64_t Addr = sectionPhysicalAddr(&Sec);
+ if (addressOverflows32bit(Addr) || addressOverflows32bit(Addr + Sec.Size - 1))
+ return createStringError(
+ errc::invalid_argument,
+ "Section '%s' address range [0x%llx, 0x%llx] is not 32 bit",
+ Sec.Name.c_str(), Addr, Addr + Sec.Size - 1);
+ return Error::success();
+}
+
+Error IHexWriter::finalize() {
+ // We can't write 64-bit addresses.
+ if (addressOverflows32bit(Obj.Entry))
+ return createStringError(errc::invalid_argument,
+ "Entry point address 0x%llx overflows 32 bits",
+ Obj.Entry);
+
+ for (const SectionBase &Sec : Obj.sections())
+ if ((Sec.Flags & ELF::SHF_ALLOC) && Sec.Type != ELF::SHT_NOBITS &&
+ Sec.Size > 0) {
+ if (Error E = checkSection(Sec))
+ return E;
+ Sections.insert(&Sec);
+ }
+
+ std::unique_ptr<WritableMemoryBuffer> EmptyBuffer =
+ WritableMemoryBuffer::getNewMemBuffer(0);
+ if (!EmptyBuffer)
+ return createStringError(errc::not_enough_memory,
+ "failed to allocate memory buffer of 0 bytes");
+
+ IHexSectionWriterBase LengthCalc(*EmptyBuffer);
+ for (const SectionBase *Sec : Sections)
+ if (Error Err = Sec->accept(LengthCalc))
+ return Err;
+
+ // We need space to write section records + StartAddress record
+ // (if start adress is not zero) + EndOfFile record.
+ TotalSize = LengthCalc.getBufferOffset() +
+ (Obj.Entry ? IHexRecord::getLineLength(4) : 0) +
+ IHexRecord::getLineLength(0);
+
+ Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
+ if (!Buf)
+ return createStringError(errc::not_enough_memory,
+ "failed to allocate memory buffer of " +
+ Twine::utohexstr(TotalSize) + " bytes");
+
+ return Error::success();
+}
+
+namespace llvm {
+namespace objcopy {
+namespace elf {
+
+template class ELFBuilder<ELF64LE>;
+template class ELFBuilder<ELF64BE>;
+template class ELFBuilder<ELF32LE>;
+template class ELFBuilder<ELF32BE>;
+
+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/contrib/libs/llvm14/tools/llvm-objcopy/ELF/Object.h b/contrib/libs/llvm14/tools/llvm-objcopy/ELF/Object.h
new file mode 100644
index 00000000000..681ab8f5638
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/ELF/Object.h
@@ -0,0 +1,1113 @@
+//===- Object.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_OBJECT_H
+#define LLVM_TOOLS_OBJCOPY_OBJECT_H
+
+#include "CommonConfig.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <set>
+#include <vector>
+
+namespace llvm {
+enum class DebugCompressionType;
+namespace objcopy {
+namespace elf {
+
+class SectionBase;
+class Section;
+class OwnedDataSection;
+class StringTableSection;
+class SymbolTableSection;
+class RelocationSection;
+class DynamicRelocationSection;
+class GnuDebugLinkSection;
+class GroupSection;
+class SectionIndexSection;
+class CompressedSection;
+class DecompressedSection;
+class Segment;
+class Object;
+struct Symbol;
+
+class SectionTableRef {
+ ArrayRef<std::unique_ptr<SectionBase>> Sections;
+
+public:
+ using iterator = pointee_iterator<const std::unique_ptr<SectionBase> *>;
+
+ explicit SectionTableRef(ArrayRef<std::unique_ptr<SectionBase>> Secs)
+ : Sections(Secs) {}
+ SectionTableRef(const SectionTableRef &) = default;
+
+ iterator begin() const { return iterator(Sections.data()); }
+ iterator end() const { return iterator(Sections.data() + Sections.size()); }
+ size_t size() const { return Sections.size(); }
+
+ Expected<SectionBase *> getSection(uint32_t Index, Twine ErrMsg);
+
+ template <class T>
+ Expected<T *> getSectionOfType(uint32_t Index, Twine IndexErrMsg,
+ Twine TypeErrMsg);
+};
+
+enum ElfType { ELFT_ELF32LE, ELFT_ELF64LE, ELFT_ELF32BE, ELFT_ELF64BE };
+
+class SectionVisitor {
+public:
+ virtual ~SectionVisitor() = default;
+
+ virtual Error visit(const Section &Sec) = 0;
+ virtual Error visit(const OwnedDataSection &Sec) = 0;
+ virtual Error visit(const StringTableSection &Sec) = 0;
+ virtual Error visit(const SymbolTableSection &Sec) = 0;
+ virtual Error visit(const RelocationSection &Sec) = 0;
+ virtual Error visit(const DynamicRelocationSection &Sec) = 0;
+ virtual Error visit(const GnuDebugLinkSection &Sec) = 0;
+ virtual Error visit(const GroupSection &Sec) = 0;
+ virtual Error visit(const SectionIndexSection &Sec) = 0;
+ virtual Error visit(const CompressedSection &Sec) = 0;
+ virtual Error visit(const DecompressedSection &Sec) = 0;
+};
+
+class MutableSectionVisitor {
+public:
+ virtual ~MutableSectionVisitor() = default;
+
+ virtual Error visit(Section &Sec) = 0;
+ virtual Error visit(OwnedDataSection &Sec) = 0;
+ virtual Error visit(StringTableSection &Sec) = 0;
+ virtual Error visit(SymbolTableSection &Sec) = 0;
+ virtual Error visit(RelocationSection &Sec) = 0;
+ virtual Error visit(DynamicRelocationSection &Sec) = 0;
+ virtual Error visit(GnuDebugLinkSection &Sec) = 0;
+ virtual Error visit(GroupSection &Sec) = 0;
+ virtual Error visit(SectionIndexSection &Sec) = 0;
+ virtual Error visit(CompressedSection &Sec) = 0;
+ virtual Error visit(DecompressedSection &Sec) = 0;
+};
+
+class SectionWriter : public SectionVisitor {
+protected:
+ WritableMemoryBuffer &Out;
+
+public:
+ virtual ~SectionWriter() = default;
+
+ Error visit(const Section &Sec) override;
+ Error visit(const OwnedDataSection &Sec) override;
+ Error visit(const StringTableSection &Sec) override;
+ Error visit(const DynamicRelocationSection &Sec) override;
+ virtual Error visit(const SymbolTableSection &Sec) override = 0;
+ virtual Error visit(const RelocationSection &Sec) override = 0;
+ virtual Error visit(const GnuDebugLinkSection &Sec) override = 0;
+ virtual Error visit(const GroupSection &Sec) override = 0;
+ virtual Error visit(const SectionIndexSection &Sec) override = 0;
+ virtual Error visit(const CompressedSection &Sec) override = 0;
+ virtual Error visit(const DecompressedSection &Sec) override = 0;
+
+ explicit SectionWriter(WritableMemoryBuffer &Buf) : Out(Buf) {}
+};
+
+template <class ELFT> class ELFSectionWriter : public SectionWriter {
+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() {}
+ Error visit(const SymbolTableSection &Sec) override;
+ Error visit(const RelocationSection &Sec) override;
+ Error visit(const GnuDebugLinkSection &Sec) override;
+ Error visit(const GroupSection &Sec) override;
+ Error visit(const SectionIndexSection &Sec) override;
+ Error visit(const CompressedSection &Sec) override;
+ Error visit(const DecompressedSection &Sec) override;
+
+ explicit ELFSectionWriter(WritableMemoryBuffer &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:
+ Error visit(Section &Sec) override;
+ Error visit(OwnedDataSection &Sec) override;
+ Error visit(StringTableSection &Sec) override;
+ Error visit(DynamicRelocationSection &Sec) override;
+ Error visit(SymbolTableSection &Sec) override;
+ Error visit(RelocationSection &Sec) override;
+ Error visit(GnuDebugLinkSection &Sec) override;
+ Error visit(GroupSection &Sec) override;
+ Error visit(SectionIndexSection &Sec) override;
+ Error visit(CompressedSection &Sec) override;
+ Error visit(DecompressedSection &Sec) override;
+};
+
+#define MAKE_SEC_WRITER_FRIEND \
+ friend class SectionWriter; \
+ friend class IHexSectionWriterBase; \
+ friend class IHexSectionWriter; \
+ template <class ELFT> friend class ELFSectionWriter; \
+ template <class ELFT> friend class ELFSectionSizer;
+
+class BinarySectionWriter : public SectionWriter {
+public:
+ virtual ~BinarySectionWriter() {}
+
+ Error visit(const SymbolTableSection &Sec) override;
+ Error visit(const RelocationSection &Sec) override;
+ Error visit(const GnuDebugLinkSection &Sec) override;
+ Error visit(const GroupSection &Sec) override;
+ Error visit(const SectionIndexSection &Sec) override;
+ Error visit(const CompressedSection &Sec) override;
+ Error visit(const DecompressedSection &Sec) override;
+
+ explicit BinarySectionWriter(WritableMemoryBuffer &Buf)
+ : SectionWriter(Buf) {}
+};
+
+using IHexLineData = SmallVector<char, 64>;
+
+struct IHexRecord {
+ // Memory address of the record.
+ uint16_t Addr;
+ // Record type (see below).
+ uint16_t Type;
+ // Record data in hexadecimal form.
+ StringRef HexData;
+
+ // Helper method to get file length of the record
+ // including newline character
+ static size_t getLength(size_t DataSize) {
+ // :LLAAAATT[DD...DD]CC'
+ return DataSize * 2 + 11;
+ }
+
+ // Gets length of line in a file (getLength + CRLF).
+ static size_t getLineLength(size_t DataSize) {
+ return getLength(DataSize) + 2;
+ }
+
+ // Given type, address and data returns line which can
+ // be written to output file.
+ static IHexLineData getLine(uint8_t Type, uint16_t Addr,
+ ArrayRef<uint8_t> Data);
+
+ // Parses the line and returns record if possible.
+ // Line should be trimmed from whitespace characters.
+ static Expected<IHexRecord> parse(StringRef Line);
+
+ // Calculates checksum of stringified record representation
+ // S must NOT contain leading ':' and trailing whitespace
+ // characters
+ static uint8_t getChecksum(StringRef S);
+
+ enum Type {
+ // Contains data and a 16-bit starting address for the data.
+ // The byte count specifies number of data bytes in the record.
+ Data = 0,
+ // Must occur exactly once per file in the last line of the file.
+ // The data field is empty (thus byte count is 00) and the address
+ // field is typically 0000.
+ EndOfFile = 1,
+ // The data field contains a 16-bit segment base address (thus byte
+ // count is always 02) compatible with 80x86 real mode addressing.
+ // The address field (typically 0000) is ignored. The segment address
+ // from the most recent 02 record is multiplied by 16 and added to each
+ // subsequent data record address to form the physical starting address
+ // for the data. This allows addressing up to one megabyte of address
+ // space.
+ SegmentAddr = 2,
+ // or 80x86 processors, specifies the initial content of the CS:IP
+ // registers. The address field is 0000, the byte count is always 04,
+ // the first two data bytes are the CS value, the latter two are the
+ // IP value.
+ StartAddr80x86 = 3,
+ // Allows for 32 bit addressing (up to 4GiB). The record's address field
+ // is ignored (typically 0000) and its byte count is always 02. The two
+ // data bytes (big endian) specify the upper 16 bits of the 32 bit
+ // absolute address for all subsequent type 00 records
+ ExtendedAddr = 4,
+ // The address field is 0000 (not used) and the byte count is always 04.
+ // The four data bytes represent a 32-bit address value. In the case of
+ // 80386 and higher CPUs, this address is loaded into the EIP register.
+ StartAddr = 5,
+ // We have no other valid types
+ InvalidType = 6
+ };
+};
+
+// Base class for IHexSectionWriter. This class implements writing algorithm,
+// but doesn't actually write records. It is used for output buffer size
+// calculation in IHexWriter::finalize.
+class IHexSectionWriterBase : public BinarySectionWriter {
+ // 20-bit segment address
+ uint32_t SegmentAddr = 0;
+ // Extended linear address
+ uint32_t BaseAddr = 0;
+
+ // Write segment address corresponding to 'Addr'
+ uint64_t writeSegmentAddr(uint64_t Addr);
+ // Write extended linear (base) address corresponding to 'Addr'
+ uint64_t writeBaseAddr(uint64_t Addr);
+
+protected:
+ // Offset in the output buffer
+ uint64_t Offset = 0;
+
+ void writeSection(const SectionBase *Sec, ArrayRef<uint8_t> Data);
+ virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data);
+
+public:
+ explicit IHexSectionWriterBase(WritableMemoryBuffer &Buf)
+ : BinarySectionWriter(Buf) {}
+
+ uint64_t getBufferOffset() const { return Offset; }
+ Error visit(const Section &Sec) final;
+ Error visit(const OwnedDataSection &Sec) final;
+ Error visit(const StringTableSection &Sec) override;
+ Error visit(const DynamicRelocationSection &Sec) final;
+ using BinarySectionWriter::visit;
+};
+
+// Real IHEX section writer
+class IHexSectionWriter : public IHexSectionWriterBase {
+public:
+ IHexSectionWriter(WritableMemoryBuffer &Buf) : IHexSectionWriterBase(Buf) {}
+
+ void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data) override;
+ Error visit(const StringTableSection &Sec) override;
+};
+
+class Writer {
+protected:
+ Object &Obj;
+ std::unique_ptr<WritableMemoryBuffer> Buf;
+ raw_ostream &Out;
+
+public:
+ virtual ~Writer();
+ virtual Error finalize() = 0;
+ virtual Error write() = 0;
+
+ Writer(Object &O, raw_ostream &Out) : Obj(O), Out(Out) {}
+};
+
+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);
+
+ void writePhdrs();
+ void writeShdrs();
+ Error writeSectionData();
+ void writeSegmentData();
+
+ void assignOffsets();
+
+ std::unique_ptr<ELFSectionWriter<ELFT>> SecWriter;
+
+ size_t totalSize() const;
+
+public:
+ virtual ~ELFWriter() {}
+ bool WriteSectionHeaders;
+
+ // For --only-keep-debug, select an alternative section/segment layout
+ // algorithm.
+ bool OnlyKeepDebug;
+
+ Error finalize() override;
+ Error write() override;
+ ELFWriter(Object &Obj, raw_ostream &Out, bool WSH, bool OnlyKeepDebug);
+};
+
+class BinaryWriter : public Writer {
+private:
+ std::unique_ptr<BinarySectionWriter> SecWriter;
+
+ uint64_t TotalSize = 0;
+
+public:
+ ~BinaryWriter() {}
+ Error finalize() override;
+ Error write() override;
+ BinaryWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {}
+};
+
+class IHexWriter : public Writer {
+ struct SectionCompare {
+ bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const;
+ };
+
+ std::set<const SectionBase *, SectionCompare> Sections;
+ size_t TotalSize = 0;
+
+ Error checkSection(const SectionBase &Sec);
+ uint64_t writeEntryPointRecord(uint8_t *Buf);
+ uint64_t writeEndOfFileRecord(uint8_t *Buf);
+
+public:
+ ~IHexWriter() {}
+ Error finalize() override;
+ Error write() override;
+ IHexWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {}
+};
+
+class SectionBase {
+public:
+ std::string Name;
+ Segment *ParentSegment = nullptr;
+ uint64_t HeaderOffset = 0;
+ uint32_t Index = 0;
+
+ uint32_t OriginalIndex = 0;
+ uint64_t OriginalFlags = 0;
+ uint64_t OriginalType = ELF::SHT_NULL;
+ uint64_t OriginalOffset = std::numeric_limits<uint64_t>::max();
+
+ uint64_t Addr = 0;
+ uint64_t Align = 1;
+ uint32_t EntrySize = 0;
+ uint64_t Flags = 0;
+ uint64_t Info = 0;
+ uint64_t Link = ELF::SHN_UNDEF;
+ uint64_t NameIndex = 0;
+ uint64_t Offset = 0;
+ uint64_t Size = 0;
+ uint64_t Type = ELF::SHT_NULL;
+ ArrayRef<uint8_t> OriginalData;
+ bool HasSymbol = false;
+
+ SectionBase() = default;
+ SectionBase(const SectionBase &) = default;
+
+ virtual ~SectionBase() = default;
+
+ virtual Error initialize(SectionTableRef SecTable);
+ virtual void finalize();
+ // Remove references to these sections. The list of sections must be sorted.
+ virtual Error
+ removeSectionReferences(bool AllowBrokenLinks,
+ function_ref<bool(const SectionBase *)> ToRemove);
+ virtual Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove);
+ virtual Error accept(SectionVisitor &Visitor) const = 0;
+ virtual Error accept(MutableSectionVisitor &Visitor) = 0;
+ virtual void markSymbols();
+ virtual void
+ replaceSectionReferences(const DenseMap<SectionBase *, SectionBase *> &);
+ virtual bool hasContents() const { return false; }
+ // Notify the section that it is subject to removal.
+ virtual void onRemove();
+};
+
+class Segment {
+private:
+ struct SectionCompare {
+ bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const {
+ // Some sections might have the same address if one of them is empty. To
+ // fix this we can use the lexicographic ordering on ->Addr and the
+ // original index.
+ if (Lhs->OriginalOffset == Rhs->OriginalOffset)
+ return Lhs->OriginalIndex < Rhs->OriginalIndex;
+ return Lhs->OriginalOffset < Rhs->OriginalOffset;
+ }
+ };
+
+public:
+ uint32_t Type = 0;
+ uint32_t Flags = 0;
+ uint64_t Offset = 0;
+ uint64_t VAddr = 0;
+ uint64_t PAddr = 0;
+ uint64_t FileSize = 0;
+ uint64_t MemSize = 0;
+ uint64_t Align = 0;
+
+ uint32_t Index = 0;
+ uint64_t OriginalOffset = 0;
+ Segment *ParentSegment = nullptr;
+ ArrayRef<uint8_t> Contents;
+ std::set<const SectionBase *, SectionCompare> Sections;
+
+ explicit Segment(ArrayRef<uint8_t> Data) : Contents(Data) {}
+ Segment() = default;
+
+ const SectionBase *firstSection() const {
+ if (!Sections.empty())
+ return *Sections.begin();
+ return nullptr;
+ }
+
+ void removeSection(const SectionBase *Sec) { Sections.erase(Sec); }
+ void addSection(const SectionBase *Sec) { Sections.insert(Sec); }
+
+ ArrayRef<uint8_t> getContents() const { return Contents; }
+};
+
+class Section : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+ ArrayRef<uint8_t> Contents;
+ SectionBase *LinkSection = nullptr;
+
+public:
+ explicit Section(ArrayRef<uint8_t> Data) : Contents(Data) {}
+
+ Error accept(SectionVisitor &Visitor) const override;
+ Error accept(MutableSectionVisitor &Visitor) override;
+ Error removeSectionReferences(
+ bool AllowBrokenLinks,
+ function_ref<bool(const SectionBase *)> ToRemove) override;
+ Error initialize(SectionTableRef SecTable) override;
+ void finalize() override;
+ bool hasContents() const override {
+ return Type != ELF::SHT_NOBITS && Type != ELF::SHT_NULL;
+ }
+};
+
+class OwnedDataSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+ std::vector<uint8_t> Data;
+
+public:
+ OwnedDataSection(StringRef SecName, ArrayRef<uint8_t> Data)
+ : Data(std::begin(Data), std::end(Data)) {
+ Name = SecName.str();
+ Type = OriginalType = ELF::SHT_PROGBITS;
+ Size = Data.size();
+ OriginalOffset = std::numeric_limits<uint64_t>::max();
+ }
+
+ OwnedDataSection(const Twine &SecName, uint64_t SecAddr, uint64_t SecFlags,
+ uint64_t SecOff) {
+ Name = SecName.str();
+ Type = OriginalType = ELF::SHT_PROGBITS;
+ Addr = SecAddr;
+ Flags = OriginalFlags = SecFlags;
+ OriginalOffset = SecOff;
+ }
+
+ OwnedDataSection(SectionBase &S, ArrayRef<uint8_t> Data)
+ : SectionBase(S), Data(std::begin(Data), std::end(Data)) {
+ Size = Data.size();
+ }
+
+ void appendHexData(StringRef HexData);
+ Error accept(SectionVisitor &Sec) const override;
+ Error accept(MutableSectionVisitor &Visitor) override;
+ bool hasContents() const override { return true; }
+};
+
+class CompressedSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+ DebugCompressionType CompressionType;
+ uint64_t DecompressedSize;
+ uint64_t DecompressedAlign;
+ SmallVector<char, 128> CompressedData;
+
+public:
+ static Expected<CompressedSection>
+ create(const SectionBase &Sec, DebugCompressionType CompressionType);
+ static Expected<CompressedSection> create(ArrayRef<uint8_t> CompressedData,
+ uint64_t DecompressedSize,
+ uint64_t DecompressedAlign);
+
+ uint64_t getDecompressedSize() const { return DecompressedSize; }
+ uint64_t getDecompressedAlign() const { return DecompressedAlign; }
+
+ Error accept(SectionVisitor &Visitor) const override;
+ Error accept(MutableSectionVisitor &Visitor) override;
+
+ static bool classof(const SectionBase *S) {
+ return (S->OriginalFlags & ELF::SHF_COMPRESSED) ||
+ (StringRef(S->Name).startswith(".zdebug"));
+ }
+
+private:
+ CompressedSection(const SectionBase &Sec,
+ DebugCompressionType CompressionType, Error &Err);
+ CompressedSection(ArrayRef<uint8_t> CompressedData, uint64_t DecompressedSize,
+ uint64_t DecompressedAlign);
+};
+
+class DecompressedSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+public:
+ explicit DecompressedSection(const CompressedSection &Sec)
+ : SectionBase(Sec) {
+ Size = Sec.getDecompressedSize();
+ Align = Sec.getDecompressedAlign();
+ Flags = OriginalFlags = (Flags & ~ELF::SHF_COMPRESSED);
+ if (StringRef(Name).startswith(".zdebug"))
+ Name = "." + Name.substr(2);
+ }
+
+ Error accept(SectionVisitor &Visitor) const override;
+ Error accept(MutableSectionVisitor &Visitor) override;
+};
+
+// There are two types of string tables that can exist, dynamic and not dynamic.
+// In the dynamic case the string table is allocated. Changing a dynamic string
+// table would mean altering virtual addresses and thus the memory image. So
+// dynamic string tables should not have an interface to modify them or
+// reconstruct them. This type lets us reconstruct a string table. To avoid
+// this class being used for dynamic string tables (which has happened) the
+// classof method checks that the particular instance is not allocated. This
+// then agrees with the makeSection method used to construct most sections.
+class StringTableSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+ StringTableBuilder StrTabBuilder;
+
+public:
+ StringTableSection() : StrTabBuilder(StringTableBuilder::ELF) {
+ Type = OriginalType = ELF::SHT_STRTAB;
+ }
+
+ void addString(StringRef Name);
+ uint32_t findIndex(StringRef Name) const;
+ void prepareForLayout();
+ Error accept(SectionVisitor &Visitor) const override;
+ Error accept(MutableSectionVisitor &Visitor) override;
+
+ static bool classof(const SectionBase *S) {
+ if (S->OriginalFlags & ELF::SHF_ALLOC)
+ return false;
+ return S->OriginalType == ELF::SHT_STRTAB;
+ }
+};
+
+// Symbols have a st_shndx field that normally stores an index but occasionally
+// stores a different special value. This enum keeps track of what the st_shndx
+// field means. Most of the values are just copies of the special SHN_* values.
+// SYMBOL_SIMPLE_INDEX means that the st_shndx is just an index of a section.
+enum SymbolShndxType {
+ SYMBOL_SIMPLE_INDEX = 0,
+ SYMBOL_ABS = ELF::SHN_ABS,
+ SYMBOL_COMMON = ELF::SHN_COMMON,
+ SYMBOL_LOPROC = ELF::SHN_LOPROC,
+ SYMBOL_AMDGPU_LDS = ELF::SHN_AMDGPU_LDS,
+ SYMBOL_HEXAGON_SCOMMON = ELF::SHN_HEXAGON_SCOMMON,
+ SYMBOL_HEXAGON_SCOMMON_2 = ELF::SHN_HEXAGON_SCOMMON_2,
+ SYMBOL_HEXAGON_SCOMMON_4 = ELF::SHN_HEXAGON_SCOMMON_4,
+ SYMBOL_HEXAGON_SCOMMON_8 = ELF::SHN_HEXAGON_SCOMMON_8,
+ SYMBOL_HIPROC = ELF::SHN_HIPROC,
+ SYMBOL_LOOS = ELF::SHN_LOOS,
+ SYMBOL_HIOS = ELF::SHN_HIOS,
+ SYMBOL_XINDEX = ELF::SHN_XINDEX,
+};
+
+struct Symbol {
+ uint8_t Binding;
+ SectionBase *DefinedIn = nullptr;
+ SymbolShndxType ShndxType;
+ uint32_t Index;
+ std::string Name;
+ uint32_t NameIndex;
+ uint64_t Size;
+ uint8_t Type;
+ uint64_t Value;
+ uint8_t Visibility;
+ bool Referenced = false;
+
+ uint16_t getShndx() const;
+ bool isCommon() const;
+};
+
+class SectionIndexSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+private:
+ std::vector<uint32_t> Indexes;
+ SymbolTableSection *Symbols = nullptr;
+
+public:
+ virtual ~SectionIndexSection() {}
+ void addIndex(uint32_t Index) {
+ assert(Size > 0);
+ Indexes.push_back(Index);
+ }
+
+ void reserve(size_t NumSymbols) {
+ Indexes.reserve(NumSymbols);
+ Size = NumSymbols * 4;
+ }
+ void setSymTab(SymbolTableSection *SymTab) { Symbols = SymTab; }
+ Error initialize(SectionTableRef SecTable) override;
+ void finalize() override;
+ Error accept(SectionVisitor &Visitor) const override;
+ Error accept(MutableSectionVisitor &Visitor) override;
+
+ SectionIndexSection() {
+ Name = ".symtab_shndx";
+ Align = 4;
+ EntrySize = 4;
+ Type = OriginalType = ELF::SHT_SYMTAB_SHNDX;
+ }
+};
+
+class SymbolTableSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+ void setStrTab(StringTableSection *StrTab) { SymbolNames = StrTab; }
+ void assignIndices();
+
+protected:
+ std::vector<std::unique_ptr<Symbol>> Symbols;
+ StringTableSection *SymbolNames = nullptr;
+ SectionIndexSection *SectionIndexTable = nullptr;
+
+ using SymPtr = std::unique_ptr<Symbol>;
+
+public:
+ SymbolTableSection() { Type = OriginalType = 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 SymbolSize);
+ void prepareForLayout();
+ // An 'empty' symbol table still contains a null symbol.
+ bool empty() const { return Symbols.size() == 1; }
+ void setShndxTable(SectionIndexSection *ShndxTable) {
+ SectionIndexTable = ShndxTable;
+ }
+ const SectionIndexSection *getShndxTable() const { return SectionIndexTable; }
+ void fillShndxTable();
+ const SectionBase *getStrTab() const { return SymbolNames; }
+ Expected<const Symbol *> getSymbolByIndex(uint32_t Index) const;
+ Expected<Symbol *> getSymbolByIndex(uint32_t Index);
+ void updateSymbols(function_ref<void(Symbol &)> Callable);
+
+ Error removeSectionReferences(
+ bool AllowBrokenLinks,
+ function_ref<bool(const SectionBase *)> ToRemove) override;
+ Error initialize(SectionTableRef SecTable) override;
+ void finalize() override;
+ Error accept(SectionVisitor &Visitor) const override;
+ Error accept(MutableSectionVisitor &Visitor) override;
+ Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override;
+ void replaceSectionReferences(
+ const DenseMap<SectionBase *, SectionBase *> &FromTo) override;
+
+ static bool classof(const SectionBase *S) {
+ return S->OriginalType == ELF::SHT_SYMTAB;
+ }
+};
+
+struct Relocation {
+ Symbol *RelocSymbol = nullptr;
+ uint64_t Offset;
+ uint64_t Addend;
+ uint32_t Type;
+};
+
+// All relocation sections denote relocations to apply to another section.
+// However, some relocation sections use a dynamic symbol table and others use
+// a regular symbol table. Because the types of the two symbol tables differ in
+// our system (because they should behave differently) we can't uniformly
+// represent all relocations with the same base class if we expose an interface
+// that mentions the symbol table type. So we split the two base types into two
+// different classes, one which handles the section the relocation is applied to
+// and another which handles the symbol table type. The symbol table type is
+// taken as a type parameter to the class (see RelocSectionWithSymtabBase).
+class RelocationSectionBase : public SectionBase {
+protected:
+ SectionBase *SecToApplyRel = nullptr;
+
+public:
+ const SectionBase *getSection() const { return SecToApplyRel; }
+ void setSection(SectionBase *Sec) { SecToApplyRel = Sec; }
+
+ StringRef getNamePrefix() const;
+
+ static bool classof(const SectionBase *S) {
+ return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA;
+ }
+};
+
+// Takes the symbol table type to use as a parameter so that we can deduplicate
+// that code between the two symbol table types.
+template <class SymTabType>
+class RelocSectionWithSymtabBase : public RelocationSectionBase {
+ void setSymTab(SymTabType *SymTab) { Symbols = SymTab; }
+
+protected:
+ RelocSectionWithSymtabBase() = default;
+
+ SymTabType *Symbols = nullptr;
+
+public:
+ Error initialize(SectionTableRef SecTable) override;
+ void finalize() override;
+};
+
+class RelocationSection
+ : public RelocSectionWithSymtabBase<SymbolTableSection> {
+ MAKE_SEC_WRITER_FRIEND
+
+ std::vector<Relocation> Relocations;
+ const Object &Obj;
+
+public:
+ RelocationSection(const Object &O) : Obj(O) {}
+ void addRelocation(Relocation Rel) { Relocations.push_back(Rel); }
+ Error accept(SectionVisitor &Visitor) const override;
+ Error accept(MutableSectionVisitor &Visitor) override;
+ Error removeSectionReferences(
+ bool AllowBrokenLinks,
+ function_ref<bool(const SectionBase *)> ToRemove) override;
+ Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override;
+ void markSymbols() override;
+ void replaceSectionReferences(
+ const DenseMap<SectionBase *, SectionBase *> &FromTo) override;
+ const Object &getObject() const { return Obj; }
+
+ static bool classof(const SectionBase *S) {
+ if (S->OriginalFlags & ELF::SHF_ALLOC)
+ return false;
+ return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA;
+ }
+};
+
+// TODO: The way stripping and groups interact is complicated
+// and still needs to be worked on.
+
+class GroupSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+ const SymbolTableSection *SymTab = nullptr;
+ Symbol *Sym = nullptr;
+ ELF::Elf32_Word FlagWord;
+ SmallVector<SectionBase *, 3> GroupMembers;
+
+public:
+ // TODO: Contents is present in several classes of the hierarchy.
+ // This needs to be refactored to avoid duplication.
+ ArrayRef<uint8_t> Contents;
+
+ explicit GroupSection(ArrayRef<uint8_t> Data) : Contents(Data) {}
+
+ void setSymTab(const SymbolTableSection *SymTabSec) { SymTab = SymTabSec; }
+ void setSymbol(Symbol *S) { Sym = S; }
+ void setFlagWord(ELF::Elf32_Word W) { FlagWord = W; }
+ void addMember(SectionBase *Sec) { GroupMembers.push_back(Sec); }
+
+ Error accept(SectionVisitor &) const override;
+ Error accept(MutableSectionVisitor &Visitor) override;
+ void finalize() override;
+ Error removeSectionReferences(
+ bool AllowBrokenLinks,
+ function_ref<bool(const SectionBase *)> ToRemove) override;
+ Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override;
+ void markSymbols() override;
+ void replaceSectionReferences(
+ const DenseMap<SectionBase *, SectionBase *> &FromTo) override;
+ void onRemove() override;
+
+ static bool classof(const SectionBase *S) {
+ return S->OriginalType == ELF::SHT_GROUP;
+ }
+};
+
+class DynamicSymbolTableSection : public Section {
+public:
+ explicit DynamicSymbolTableSection(ArrayRef<uint8_t> Data) : Section(Data) {}
+
+ static bool classof(const SectionBase *S) {
+ return S->OriginalType == ELF::SHT_DYNSYM;
+ }
+};
+
+class DynamicSection : public Section {
+public:
+ explicit DynamicSection(ArrayRef<uint8_t> Data) : Section(Data) {}
+
+ static bool classof(const SectionBase *S) {
+ return S->OriginalType == ELF::SHT_DYNAMIC;
+ }
+};
+
+class DynamicRelocationSection
+ : public RelocSectionWithSymtabBase<DynamicSymbolTableSection> {
+ MAKE_SEC_WRITER_FRIEND
+
+private:
+ ArrayRef<uint8_t> Contents;
+
+public:
+ explicit DynamicRelocationSection(ArrayRef<uint8_t> Data) : Contents(Data) {}
+
+ Error accept(SectionVisitor &) const override;
+ Error accept(MutableSectionVisitor &Visitor) override;
+ Error removeSectionReferences(
+ bool AllowBrokenLinks,
+ function_ref<bool(const SectionBase *)> ToRemove) override;
+
+ static bool classof(const SectionBase *S) {
+ if (!(S->OriginalFlags & ELF::SHF_ALLOC))
+ return false;
+ return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA;
+ }
+};
+
+class GnuDebugLinkSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+private:
+ StringRef FileName;
+ uint32_t CRC32;
+
+ void init(StringRef File);
+
+public:
+ // If we add this section from an external source we can use this ctor.
+ explicit GnuDebugLinkSection(StringRef File, uint32_t PrecomputedCRC);
+ Error accept(SectionVisitor &Visitor) const override;
+ Error accept(MutableSectionVisitor &Visitor) override;
+};
+
+class Reader {
+public:
+ virtual ~Reader();
+ virtual Expected<std::unique_ptr<Object>> create(bool EnsureSymtab) const = 0;
+};
+
+using object::Binary;
+using object::ELFFile;
+using object::ELFObjectFile;
+using object::OwningBinary;
+
+class BasicELFBuilder {
+protected:
+ std::unique_ptr<Object> Obj;
+
+ void initFileHeader();
+ void initHeaderSegment();
+ StringTableSection *addStrTab();
+ SymbolTableSection *addSymTab(StringTableSection *StrTab);
+ Error initSections();
+
+public:
+ BasicELFBuilder() : Obj(std::make_unique<Object>()) {}
+};
+
+class BinaryELFBuilder : public BasicELFBuilder {
+ MemoryBuffer *MemBuf;
+ uint8_t NewSymbolVisibility;
+ void addData(SymbolTableSection *SymTab);
+
+public:
+ BinaryELFBuilder(MemoryBuffer *MB, uint8_t NewSymbolVisibility)
+ : MemBuf(MB), NewSymbolVisibility(NewSymbolVisibility) {}
+
+ Expected<std::unique_ptr<Object>> build();
+};
+
+class IHexELFBuilder : public BasicELFBuilder {
+ const std::vector<IHexRecord> &Records;
+
+ void addDataSections();
+
+public:
+ IHexELFBuilder(const std::vector<IHexRecord> &Records) : Records(Records) {}
+
+ Expected<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_Word = typename ELFT::Word;
+
+ const ELFFile<ELFT> &ElfFile;
+ Object &Obj;
+ size_t EhdrOffset = 0;
+ Optional<StringRef> ExtractPartition;
+
+ void setParentSegment(Segment &Child);
+ Error readProgramHeaders(const ELFFile<ELFT> &HeadersFile);
+ Error initGroupSection(GroupSection *GroupSec);
+ Error initSymbolTable(SymbolTableSection *SymTab);
+ Error readSectionHeaders();
+ Error readSections(bool EnsureSymtab);
+ Error findEhdrOffset();
+ Expected<SectionBase &> makeSection(const Elf_Shdr &Shdr);
+
+public:
+ ELFBuilder(const ELFObjectFile<ELFT> &ElfObj, Object &Obj,
+ Optional<StringRef> ExtractPartition);
+
+ Error build(bool EnsureSymtab);
+};
+
+class BinaryReader : public Reader {
+ MemoryBuffer *MemBuf;
+ uint8_t NewSymbolVisibility;
+
+public:
+ BinaryReader(MemoryBuffer *MB, const uint8_t NewSymbolVisibility)
+ : MemBuf(MB), NewSymbolVisibility(NewSymbolVisibility) {}
+ Expected<std::unique_ptr<Object>> create(bool EnsureSymtab) const override;
+};
+
+class IHexReader : public Reader {
+ MemoryBuffer *MemBuf;
+
+ Expected<std::vector<IHexRecord>> parse() const;
+ Error parseError(size_t LineNo, Error E) const {
+ return LineNo == -1U
+ ? createFileError(MemBuf->getBufferIdentifier(), std::move(E))
+ : createFileError(MemBuf->getBufferIdentifier(), LineNo,
+ std::move(E));
+ }
+ template <typename... Ts>
+ Error parseError(size_t LineNo, char const *Fmt, const Ts &... Vals) const {
+ Error E = createStringError(errc::invalid_argument, Fmt, Vals...);
+ return parseError(LineNo, std::move(E));
+ }
+
+public:
+ IHexReader(MemoryBuffer *MB) : MemBuf(MB) {}
+
+ Expected<std::unique_ptr<Object>> create(bool EnsureSymtab) const override;
+};
+
+class ELFReader : public Reader {
+ Binary *Bin;
+ Optional<StringRef> ExtractPartition;
+
+public:
+ Expected<std::unique_ptr<Object>> create(bool EnsureSymtab) const override;
+ explicit ELFReader(Binary *B, Optional<StringRef> ExtractPartition)
+ : Bin(B), ExtractPartition(ExtractPartition) {}
+};
+
+class Object {
+private:
+ using SecPtr = std::unique_ptr<SectionBase>;
+ using SegPtr = std::unique_ptr<Segment>;
+
+ std::vector<SecPtr> Sections;
+ std::vector<SegPtr> Segments;
+ std::vector<SecPtr> RemovedSections;
+ DenseMap<SectionBase *, std::vector<uint8_t>> UpdatedSections;
+
+ static bool sectionIsAlloc(const SectionBase &Sec) {
+ return Sec.Flags & ELF::SHF_ALLOC;
+ };
+
+public:
+ template <class T>
+ using ConstRange = iterator_range<pointee_iterator<
+ typename std::vector<std::unique_ptr<T>>::const_iterator>>;
+
+ // It is often the case that the ELF header and the program header table are
+ // not present in any segment. This could be a problem during file layout,
+ // because other segments may get assigned an offset where either of the
+ // two should reside, which will effectively corrupt the resulting binary.
+ // Other than that we use these segments to track program header offsets
+ // when they may not follow the ELF header.
+ Segment ElfHdrSegment;
+ Segment ProgramHdrSegment;
+
+ uint8_t OSABI;
+ uint8_t ABIVersion;
+ uint64_t Entry;
+ uint64_t SHOff;
+ uint32_t Type;
+ uint32_t Machine;
+ uint32_t Version;
+ uint32_t Flags;
+
+ bool HadShdrs = true;
+ bool MustBeRelocatable = false;
+ StringTableSection *SectionNames = nullptr;
+ SymbolTableSection *SymbolTable = nullptr;
+ SectionIndexSection *SectionIndexTable = nullptr;
+
+ bool IsMips64EL = false;
+
+ SectionTableRef sections() const { return SectionTableRef(Sections); }
+ iterator_range<
+ filter_iterator<pointee_iterator<std::vector<SecPtr>::const_iterator>,
+ decltype(&sectionIsAlloc)>>
+ allocSections() const {
+ return make_filter_range(make_pointee_range(Sections), sectionIsAlloc);
+ }
+
+ const auto &getUpdatedSections() const { return UpdatedSections; }
+ Error updateSection(StringRef Name, ArrayRef<uint8_t> Data);
+
+ SectionBase *findSection(StringRef Name) {
+ auto SecIt =
+ find_if(Sections, [&](const SecPtr &Sec) { return Sec->Name == Name; });
+ return SecIt == Sections.end() ? nullptr : SecIt->get();
+ }
+ SectionTableRef removedSections() { return SectionTableRef(RemovedSections); }
+
+ ConstRange<Segment> segments() const { return make_pointee_range(Segments); }
+
+ Error removeSections(bool AllowBrokenLinks,
+ std::function<bool(const SectionBase &)> ToRemove);
+ Error replaceSections(const DenseMap<SectionBase *, SectionBase *> &FromTo);
+ Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove);
+ template <class T, class... Ts> T &addSection(Ts &&... Args) {
+ auto Sec = std::make_unique<T>(std::forward<Ts>(Args)...);
+ auto Ptr = Sec.get();
+ MustBeRelocatable |= isa<RelocationSection>(*Ptr);
+ Sections.emplace_back(std::move(Sec));
+ Ptr->Index = Sections.size();
+ return *Ptr;
+ }
+ Error addNewSymbolTable();
+ Segment &addSegment(ArrayRef<uint8_t> Data) {
+ Segments.emplace_back(std::make_unique<Segment>(Data));
+ return *Segments.back();
+ }
+ bool isRelocatable() const {
+ return (Type != ELF::ET_DYN && Type != ELF::ET_EXEC) || MustBeRelocatable;
+ }
+};
+
+} // end namespace elf
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_OBJECT_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/InstallNameToolOpts.td b/contrib/libs/llvm14/tools/llvm-objcopy/InstallNameToolOpts.td
new file mode 100644
index 00000000000..88dea84400f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/InstallNameToolOpts.td
@@ -0,0 +1,44 @@
+//===-- InstallNameToolOpts.td - llvm-install-name-tool options --------*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file describes the command line options of llvm-install-name.
+//
+//===----------------------------------------------------------------------===//
+
+include "llvm/Option/OptParser.td"
+
+def help : Flag<["--"], "help">;
+def h : Flag<["-"], "h">, Alias<help>;
+
+def add_rpath : Option<["-", "--"], "add_rpath", KIND_SEPARATE>,
+ HelpText<"Add new rpath">;
+
+def prepend_rpath : Option<["-", "--"], "prepend_rpath", KIND_SEPARATE>,
+ HelpText<"Add new rpath before other rpaths">;
+
+def delete_rpath: Option<["-", "--"], "delete_rpath", KIND_SEPARATE>,
+ HelpText<"Delete specified rpath">;
+
+def delete_all_rpaths: Flag<["-", "--"], "delete_all_rpaths">,
+ HelpText<"Delete all rpath directives">;
+
+def rpath: MultiArg<["-", "--"], "rpath", 2>,
+ HelpText<"Change rpath path name">;
+
+def id : Option<["-","--"], "id", KIND_SEPARATE>,
+ HelpText<"Change dynamic shared library id">;
+
+def change: MultiArg<["-", "--"], "change", 2>,
+ HelpText<"Change dependent shared library install name">;
+
+def version : Flag<["--"], "version">,
+ HelpText<"Print the version and exit.">;
+
+def V : Flag<["-"], "V">,
+ Alias<version>,
+ HelpText<"Alias for --version">;
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOConfig.h b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOConfig.h
new file mode 100644
index 00000000000..93f9facfcf0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOConfig.h
@@ -0,0 +1,43 @@
+//===- MachOConfig.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringRef.h"
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+
+// Mach-O specific configuration for copying/stripping a single file.
+struct MachOConfig {
+ // Repeated options
+ std::vector<StringRef> RPathToAdd;
+ std::vector<StringRef> RPathToPrepend;
+ DenseMap<StringRef, StringRef> RPathsToUpdate;
+ DenseMap<StringRef, StringRef> InstallNamesToUpdate;
+ DenseSet<StringRef> RPathsToRemove;
+
+ // install-name-tool's id option
+ Optional<StringRef> SharedLibId;
+
+ // Boolean options
+ bool StripSwiftSymbols = false;
+ bool KeepUndefined = false;
+
+ // install-name-tool's --delete_all_rpaths
+ bool RemoveAllRpaths = false;
+};
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
new file mode 100644
index 00000000000..6b731abd9ed
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
@@ -0,0 +1,441 @@
+//===- MachOLayoutBuilder.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachOLayoutBuilder.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm;
+using namespace llvm::objcopy::macho;
+
+StringTableBuilder::Kind
+MachOLayoutBuilder::getStringTableBuilderKind(const Object &O, bool Is64Bit) {
+ if (O.Header.FileType == MachO::HeaderFileType::MH_OBJECT)
+ return Is64Bit ? StringTableBuilder::MachO64 : StringTableBuilder::MachO;
+ return Is64Bit ? StringTableBuilder::MachO64Linked
+ : StringTableBuilder::MachOLinked;
+}
+
+uint32_t MachOLayoutBuilder::computeSizeOfCmds() const {
+ uint32_t Size = 0;
+ for (const LoadCommand &LC : O.LoadCommands) {
+ const MachO::macho_load_command &MLC = LC.MachOLoadCommand;
+ auto cmd = MLC.load_command_data.cmd;
+ switch (cmd) {
+ case MachO::LC_SEGMENT:
+ Size += sizeof(MachO::segment_command) +
+ sizeof(MachO::section) * LC.Sections.size();
+ continue;
+ case MachO::LC_SEGMENT_64:
+ Size += sizeof(MachO::segment_command_64) +
+ sizeof(MachO::section_64) * LC.Sections.size();
+ continue;
+ }
+
+ switch (cmd) {
+#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
+ case MachO::LCName: \
+ Size += sizeof(MachO::LCStruct) + LC.Payload.size(); \
+ break;
+#include "llvm/BinaryFormat/MachO.def"
+#undef HANDLE_LOAD_COMMAND
+ }
+ }
+
+ return Size;
+}
+
+void MachOLayoutBuilder::constructStringTable() {
+ for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols)
+ StrTableBuilder.add(Sym->Name);
+ StrTableBuilder.finalize();
+}
+
+void MachOLayoutBuilder::updateSymbolIndexes() {
+ uint32_t Index = 0;
+ for (auto &Symbol : O.SymTable.Symbols)
+ Symbol->Index = Index++;
+}
+
+// Updates the index and the number of local/external/undefined symbols.
+void MachOLayoutBuilder::updateDySymTab(MachO::macho_load_command &MLC) {
+ assert(MLC.load_command_data.cmd == MachO::LC_DYSYMTAB);
+ // Make sure that nlist entries in the symbol table are sorted by the those
+ // types. The order is: local < defined external < undefined external.
+ assert(llvm::is_sorted(O.SymTable.Symbols,
+ [](const std::unique_ptr<SymbolEntry> &A,
+ const std::unique_ptr<SymbolEntry> &B) {
+ bool AL = A->isLocalSymbol(),
+ BL = B->isLocalSymbol();
+ if (AL != BL)
+ return AL;
+ return !AL && !A->isUndefinedSymbol() &&
+ B->isUndefinedSymbol();
+ }) &&
+ "Symbols are not sorted by their types.");
+
+ uint32_t NumLocalSymbols = 0;
+ auto Iter = O.SymTable.Symbols.begin();
+ auto End = O.SymTable.Symbols.end();
+ for (; Iter != End; ++Iter) {
+ if ((*Iter)->isExternalSymbol())
+ break;
+
+ ++NumLocalSymbols;
+ }
+
+ uint32_t NumExtDefSymbols = 0;
+ for (; Iter != End; ++Iter) {
+ if ((*Iter)->isUndefinedSymbol())
+ break;
+
+ ++NumExtDefSymbols;
+ }
+
+ MLC.dysymtab_command_data.ilocalsym = 0;
+ MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols;
+ MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols;
+ MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols;
+ MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols;
+ MLC.dysymtab_command_data.nundefsym =
+ O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols);
+}
+
+// Recomputes and updates offset and size fields in load commands and sections
+// since they could be modified.
+uint64_t MachOLayoutBuilder::layoutSegments() {
+ auto HeaderSize =
+ Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
+ const bool IsObjectFile =
+ O.Header.FileType == MachO::HeaderFileType::MH_OBJECT;
+ uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0;
+ for (LoadCommand &LC : O.LoadCommands) {
+ auto &MLC = LC.MachOLoadCommand;
+ StringRef Segname;
+ uint64_t SegmentVmAddr;
+ uint64_t SegmentVmSize;
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ SegmentVmAddr = MLC.segment_command_data.vmaddr;
+ SegmentVmSize = MLC.segment_command_data.vmsize;
+ Segname = StringRef(MLC.segment_command_data.segname,
+ strnlen(MLC.segment_command_data.segname,
+ sizeof(MLC.segment_command_data.segname)));
+ break;
+ case MachO::LC_SEGMENT_64:
+ SegmentVmAddr = MLC.segment_command_64_data.vmaddr;
+ SegmentVmSize = MLC.segment_command_64_data.vmsize;
+ Segname = StringRef(MLC.segment_command_64_data.segname,
+ strnlen(MLC.segment_command_64_data.segname,
+ sizeof(MLC.segment_command_64_data.segname)));
+ break;
+ default:
+ continue;
+ }
+
+ if (Segname == "__LINKEDIT") {
+ // We update the __LINKEDIT segment later (in layoutTail).
+ assert(LC.Sections.empty() && "__LINKEDIT segment has sections");
+ LinkEditLoadCommand = &MLC;
+ continue;
+ }
+
+ // Update file offsets and sizes of sections.
+ uint64_t SegOffset = Offset;
+ uint64_t SegFileSize = 0;
+ uint64_t VMSize = 0;
+ for (std::unique_ptr<Section> &Sec : LC.Sections) {
+ assert(SegmentVmAddr <= Sec->Addr &&
+ "Section's address cannot be smaller than Segment's one");
+ uint32_t SectOffset = Sec->Addr - SegmentVmAddr;
+ if (IsObjectFile) {
+ if (!Sec->hasValidOffset()) {
+ Sec->Offset = 0;
+ } else {
+ uint64_t PaddingSize =
+ offsetToAlignment(SegFileSize, Align(1ull << Sec->Align));
+ Sec->Offset = SegOffset + SegFileSize + PaddingSize;
+ Sec->Size = Sec->Content.size();
+ SegFileSize += PaddingSize + Sec->Size;
+ }
+ } else {
+ if (!Sec->hasValidOffset()) {
+ Sec->Offset = 0;
+ } else {
+ Sec->Offset = SegOffset + SectOffset;
+ Sec->Size = Sec->Content.size();
+ SegFileSize = std::max(SegFileSize, SectOffset + Sec->Size);
+ }
+ }
+ VMSize = std::max(VMSize, SectOffset + Sec->Size);
+ }
+
+ if (IsObjectFile) {
+ Offset += SegFileSize;
+ } else {
+ Offset = alignTo(Offset + SegFileSize, PageSize);
+ SegFileSize = alignTo(SegFileSize, PageSize);
+ // Use the original vmsize if the segment is __PAGEZERO.
+ VMSize =
+ Segname == "__PAGEZERO" ? SegmentVmSize : alignTo(VMSize, PageSize);
+ }
+
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ MLC.segment_command_data.cmdsize =
+ sizeof(MachO::segment_command) +
+ sizeof(MachO::section) * LC.Sections.size();
+ MLC.segment_command_data.nsects = LC.Sections.size();
+ MLC.segment_command_data.fileoff = SegOffset;
+ MLC.segment_command_data.vmsize = VMSize;
+ MLC.segment_command_data.filesize = SegFileSize;
+ break;
+ case MachO::LC_SEGMENT_64:
+ MLC.segment_command_64_data.cmdsize =
+ sizeof(MachO::segment_command_64) +
+ sizeof(MachO::section_64) * LC.Sections.size();
+ MLC.segment_command_64_data.nsects = LC.Sections.size();
+ MLC.segment_command_64_data.fileoff = SegOffset;
+ MLC.segment_command_64_data.vmsize = VMSize;
+ MLC.segment_command_64_data.filesize = SegFileSize;
+ break;
+ }
+ }
+
+ return Offset;
+}
+
+uint64_t MachOLayoutBuilder::layoutRelocations(uint64_t Offset) {
+ for (LoadCommand &LC : O.LoadCommands)
+ for (std::unique_ptr<Section> &Sec : LC.Sections) {
+ Sec->RelOff = Sec->Relocations.empty() ? 0 : Offset;
+ Sec->NReloc = Sec->Relocations.size();
+ Offset += sizeof(MachO::any_relocation_info) * Sec->NReloc;
+ }
+
+ return Offset;
+}
+
+Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
+ // If we are building the layout of an executable or dynamic library
+ // which does not have any segments other than __LINKEDIT,
+ // the Offset can be equal to zero by this time. It happens because of the
+ // convention that in such cases the file offsets specified by LC_SEGMENT
+ // start with zero (unlike the case of a relocatable object file).
+ const uint64_t HeaderSize =
+ Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
+ assert((!(O.Header.FileType == MachO::HeaderFileType::MH_OBJECT) ||
+ Offset >= HeaderSize + O.Header.SizeOfCmds) &&
+ "Incorrect tail offset");
+ Offset = std::max(Offset, HeaderSize + O.Header.SizeOfCmds);
+
+ // The order of LINKEDIT elements is as follows:
+ // rebase info, binding info, weak binding info, lazy binding info, export
+ // trie, data-in-code, symbol table, indirect symbol table, symbol table
+ // strings, code signature.
+ uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);
+ uint64_t StartOfLinkEdit = Offset;
+ uint64_t StartOfRebaseInfo = StartOfLinkEdit;
+ uint64_t StartOfBindingInfo = StartOfRebaseInfo + O.Rebases.Opcodes.size();
+ uint64_t StartOfWeakBindingInfo = StartOfBindingInfo + O.Binds.Opcodes.size();
+ uint64_t StartOfLazyBindingInfo =
+ StartOfWeakBindingInfo + O.WeakBinds.Opcodes.size();
+ uint64_t StartOfExportTrie =
+ StartOfLazyBindingInfo + O.LazyBinds.Opcodes.size();
+ uint64_t StartOfFunctionStarts = StartOfExportTrie + O.Exports.Trie.size();
+ uint64_t StartOfDyldExportsTrie =
+ StartOfFunctionStarts + O.FunctionStarts.Data.size();
+ uint64_t StartOfChainedFixups =
+ StartOfDyldExportsTrie + O.ExportsTrie.Data.size();
+ uint64_t StartOfDataInCode =
+ StartOfChainedFixups + O.ChainedFixups.Data.size();
+ uint64_t StartOfLinkerOptimizationHint =
+ StartOfDataInCode + O.DataInCode.Data.size();
+ uint64_t StartOfSymbols =
+ StartOfLinkerOptimizationHint + O.LinkerOptimizationHint.Data.size();
+ uint64_t StartOfIndirectSymbols =
+ StartOfSymbols + NListSize * O.SymTable.Symbols.size();
+ uint64_t StartOfSymbolStrings =
+ StartOfIndirectSymbols +
+ sizeof(uint32_t) * O.IndirectSymTable.Symbols.size();
+ uint64_t StartOfCodeSignature =
+ StartOfSymbolStrings + StrTableBuilder.getSize();
+ uint32_t CodeSignatureSize = 0;
+ if (O.CodeSignatureCommandIndex) {
+ StartOfCodeSignature = alignTo(StartOfCodeSignature, 16);
+
+ // Note: These calculations are to be kept in sync with the same
+ // calculations performed in LLD's CodeSignatureSection.
+ const uint32_t AllHeadersSize =
+ alignTo(CodeSignature.FixedHeadersSize + OutputFileName.size() + 1,
+ CodeSignature.Align);
+ const uint32_t BlockCount =
+ (StartOfCodeSignature + CodeSignature.BlockSize - 1) /
+ CodeSignature.BlockSize;
+ const uint32_t Size =
+ alignTo(AllHeadersSize + BlockCount * CodeSignature.HashSize,
+ CodeSignature.Align);
+
+ CodeSignature.StartOffset = StartOfCodeSignature;
+ CodeSignature.AllHeadersSize = AllHeadersSize;
+ CodeSignature.BlockCount = BlockCount;
+ CodeSignature.OutputFileName = OutputFileName;
+ CodeSignature.Size = Size;
+ CodeSignatureSize = Size;
+ }
+ uint64_t LinkEditSize =
+ StartOfCodeSignature + CodeSignatureSize - StartOfLinkEdit;
+
+ // Now we have determined the layout of the contents of the __LINKEDIT
+ // segment. Update its load command.
+ if (LinkEditLoadCommand) {
+ MachO::macho_load_command *MLC = LinkEditLoadCommand;
+ switch (LinkEditLoadCommand->load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ MLC->segment_command_data.cmdsize = sizeof(MachO::segment_command);
+ MLC->segment_command_data.fileoff = StartOfLinkEdit;
+ MLC->segment_command_data.vmsize = alignTo(LinkEditSize, PageSize);
+ MLC->segment_command_data.filesize = LinkEditSize;
+ break;
+ case MachO::LC_SEGMENT_64:
+ MLC->segment_command_64_data.cmdsize = sizeof(MachO::segment_command_64);
+ MLC->segment_command_64_data.fileoff = StartOfLinkEdit;
+ MLC->segment_command_64_data.vmsize = alignTo(LinkEditSize, PageSize);
+ MLC->segment_command_64_data.filesize = LinkEditSize;
+ break;
+ }
+ }
+
+ for (LoadCommand &LC : O.LoadCommands) {
+ auto &MLC = LC.MachOLoadCommand;
+ auto cmd = MLC.load_command_data.cmd;
+ switch (cmd) {
+ case MachO::LC_CODE_SIGNATURE:
+ MLC.linkedit_data_command_data.dataoff = StartOfCodeSignature;
+ MLC.linkedit_data_command_data.datasize = CodeSignatureSize;
+ break;
+ case MachO::LC_SYMTAB:
+ MLC.symtab_command_data.symoff = StartOfSymbols;
+ MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size();
+ MLC.symtab_command_data.stroff = StartOfSymbolStrings;
+ MLC.symtab_command_data.strsize = StrTableBuilder.getSize();
+ break;
+ case MachO::LC_DYSYMTAB: {
+ if (MLC.dysymtab_command_data.ntoc != 0 ||
+ MLC.dysymtab_command_data.nmodtab != 0 ||
+ MLC.dysymtab_command_data.nextrefsyms != 0 ||
+ MLC.dysymtab_command_data.nlocrel != 0 ||
+ MLC.dysymtab_command_data.nextrel != 0)
+ return createStringError(llvm::errc::not_supported,
+ "shared library is not yet supported");
+
+ if (!O.IndirectSymTable.Symbols.empty()) {
+ MLC.dysymtab_command_data.indirectsymoff = StartOfIndirectSymbols;
+ MLC.dysymtab_command_data.nindirectsyms =
+ O.IndirectSymTable.Symbols.size();
+ }
+
+ updateDySymTab(MLC);
+ break;
+ }
+ case MachO::LC_DATA_IN_CODE:
+ MLC.linkedit_data_command_data.dataoff = StartOfDataInCode;
+ MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size();
+ break;
+ case MachO::LC_LINKER_OPTIMIZATION_HINT:
+ MLC.linkedit_data_command_data.dataoff = StartOfLinkerOptimizationHint;
+ MLC.linkedit_data_command_data.datasize =
+ O.LinkerOptimizationHint.Data.size();
+ break;
+ case MachO::LC_FUNCTION_STARTS:
+ MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts;
+ MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size();
+ break;
+ case MachO::LC_DYLD_CHAINED_FIXUPS:
+ MLC.linkedit_data_command_data.dataoff = StartOfChainedFixups;
+ MLC.linkedit_data_command_data.datasize = O.ChainedFixups.Data.size();
+ break;
+ case MachO::LC_DYLD_EXPORTS_TRIE:
+ MLC.linkedit_data_command_data.dataoff = StartOfDyldExportsTrie;
+ MLC.linkedit_data_command_data.datasize = O.ExportsTrie.Data.size();
+ break;
+ case MachO::LC_DYLD_INFO:
+ case MachO::LC_DYLD_INFO_ONLY:
+ MLC.dyld_info_command_data.rebase_off =
+ O.Rebases.Opcodes.empty() ? 0 : StartOfRebaseInfo;
+ MLC.dyld_info_command_data.rebase_size = O.Rebases.Opcodes.size();
+ MLC.dyld_info_command_data.bind_off =
+ O.Binds.Opcodes.empty() ? 0 : StartOfBindingInfo;
+ MLC.dyld_info_command_data.bind_size = O.Binds.Opcodes.size();
+ MLC.dyld_info_command_data.weak_bind_off =
+ O.WeakBinds.Opcodes.empty() ? 0 : StartOfWeakBindingInfo;
+ MLC.dyld_info_command_data.weak_bind_size = O.WeakBinds.Opcodes.size();
+ MLC.dyld_info_command_data.lazy_bind_off =
+ O.LazyBinds.Opcodes.empty() ? 0 : StartOfLazyBindingInfo;
+ MLC.dyld_info_command_data.lazy_bind_size = O.LazyBinds.Opcodes.size();
+ MLC.dyld_info_command_data.export_off =
+ O.Exports.Trie.empty() ? 0 : StartOfExportTrie;
+ MLC.dyld_info_command_data.export_size = O.Exports.Trie.size();
+ break;
+ // Note that LC_ENCRYPTION_INFO.cryptoff despite its name and the comment in
+ // <mach-o/loader.h> is not an offset in the binary file, instead, it is a
+ // relative virtual address. At the moment modification of the __TEXT
+ // segment of executables isn't supported anyway (e.g. data in code entries
+ // are not recalculated). Moreover, in general
+ // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 are nontrivial to update because
+ // without making additional assumptions (e.g. that the entire __TEXT
+ // segment should be encrypted) we do not know how to recalculate the
+ // boundaries of the encrypted part. For now just copy over these load
+ // commands until we encounter a real world usecase where
+ // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 need to be adjusted.
+ case MachO::LC_ENCRYPTION_INFO:
+ case MachO::LC_ENCRYPTION_INFO_64:
+ case MachO::LC_LOAD_DYLINKER:
+ case MachO::LC_MAIN:
+ case MachO::LC_RPATH:
+ case MachO::LC_SEGMENT:
+ case MachO::LC_SEGMENT_64:
+ case MachO::LC_VERSION_MIN_MACOSX:
+ case MachO::LC_VERSION_MIN_IPHONEOS:
+ case MachO::LC_VERSION_MIN_TVOS:
+ case MachO::LC_VERSION_MIN_WATCHOS:
+ case MachO::LC_BUILD_VERSION:
+ case MachO::LC_ID_DYLIB:
+ case MachO::LC_LOAD_DYLIB:
+ case MachO::LC_LOAD_WEAK_DYLIB:
+ case MachO::LC_UUID:
+ case MachO::LC_SOURCE_VERSION:
+ case MachO::LC_THREAD:
+ case MachO::LC_UNIXTHREAD:
+ case MachO::LC_SUB_FRAMEWORK:
+ case MachO::LC_SUB_UMBRELLA:
+ case MachO::LC_SUB_CLIENT:
+ case MachO::LC_SUB_LIBRARY:
+ case MachO::LC_LINKER_OPTION:
+ // Nothing to update.
+ break;
+ default:
+ // Abort if it's unsupported in order to prevent corrupting the object.
+ return createStringError(llvm::errc::not_supported,
+ "unsupported load command (cmd=0x%x)", cmd);
+ }
+ }
+
+ return Error::success();
+}
+
+Error MachOLayoutBuilder::layout() {
+ O.Header.NCmds = O.LoadCommands.size();
+ O.Header.SizeOfCmds = computeSizeOfCmds();
+ constructStringTable();
+ updateSymbolIndexes();
+ uint64_t Offset = layoutSegments();
+ Offset = layoutRelocations(Offset);
+ return layoutTail(Offset);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h
new file mode 100644
index 00000000000..44d03b4af7e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h
@@ -0,0 +1,97 @@
+//===- MachOLayoutBuilder.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H
+#define LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H
+
+#include "MachOObjcopy.h"
+#include "Object.h"
+
+namespace llvm {
+namespace objcopy {
+namespace macho {
+
+/// When MachO binaries include a LC_CODE_SIGNATURE load command,
+/// the __LINKEDIT data segment will include a section corresponding
+/// to the LC_CODE_SIGNATURE load command. This section serves as a signature
+/// for the binary. Included in the CodeSignature section is a header followed
+/// by a hash of the binary. If present, the CodeSignature section is the
+/// last component of the binary.
+struct CodeSignatureInfo {
+ // NOTE: These values are to be kept in sync with those in
+ // LLD's CodeSignatureSection class.
+
+ static constexpr uint32_t Align = 16;
+ static constexpr uint8_t BlockSizeShift = 12;
+ // The binary is read in blocks of the following size.
+ static constexpr size_t BlockSize = (1 << BlockSizeShift); // 4 KiB
+ // For each block, a SHA256 hash (256 bits, 32 bytes) is written to
+ // the CodeSignature section.
+ static constexpr size_t HashSize = 256 / 8;
+ static constexpr size_t BlobHeadersSize = llvm::alignTo<8>(
+ sizeof(llvm::MachO::CS_SuperBlob) + sizeof(llvm::MachO::CS_BlobIndex));
+ // The size of the entire header depends upon the filename the binary is being
+ // written to, but the rest of the header is fixed in size.
+ static constexpr uint32_t FixedHeadersSize =
+ BlobHeadersSize + sizeof(llvm::MachO::CS_CodeDirectory);
+
+ // The offset relative to the start of the binary where
+ // the CodeSignature section should begin.
+ uint32_t StartOffset;
+ // The size of the entire header, output file name size included.
+ uint32_t AllHeadersSize;
+ // The number of blocks required to hash the binary.
+ uint32_t BlockCount;
+ StringRef OutputFileName;
+ // The size of the entire CodeSignature section, including both the header and
+ // hashes.
+ uint32_t Size;
+};
+
+class MachOLayoutBuilder {
+ Object &O;
+ bool Is64Bit;
+ StringRef OutputFileName;
+ uint64_t PageSize;
+ CodeSignatureInfo CodeSignature;
+
+ // Points to the __LINKEDIT segment if it exists.
+ MachO::macho_load_command *LinkEditLoadCommand = nullptr;
+ StringTableBuilder StrTableBuilder;
+
+ uint32_t computeSizeOfCmds() const;
+ void constructStringTable();
+ void updateSymbolIndexes();
+ void updateDySymTab(MachO::macho_load_command &MLC);
+ uint64_t layoutSegments();
+ uint64_t layoutRelocations(uint64_t Offset);
+ Error layoutTail(uint64_t Offset);
+
+ static StringTableBuilder::Kind getStringTableBuilderKind(const Object &O,
+ bool Is64Bit);
+
+public:
+ MachOLayoutBuilder(Object &O, bool Is64Bit, StringRef OutputFileName,
+ uint64_t PageSize)
+ : O(O), Is64Bit(Is64Bit), OutputFileName(OutputFileName),
+ PageSize(PageSize),
+ StrTableBuilder(getStringTableBuilderKind(O, Is64Bit)) {}
+
+ // Recomputes and updates fields in the given object such as file offsets.
+ Error layout();
+
+ StringTableBuilder &getStringTableBuilder() { return StrTableBuilder; }
+
+ const CodeSignatureInfo &getCodeSignature() { return CodeSignature; }
+};
+
+} // end namespace macho
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
new file mode 100644
index 00000000000..0f92ca516be
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
@@ -0,0 +1,549 @@
+//===- MachOObjcopy.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachOObjcopy.h"
+#include "../llvm-objcopy.h"
+#include "CommonConfig.h"
+#include "MachO/MachOConfig.h"
+#include "MachOReader.h"
+#include "MachOWriter.h"
+#include "MultiFormatConfig.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/Object/ArchiveWriter.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/MachOUniversalWriter.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/SmallVectorMemoryBuffer.h"
+
+using namespace llvm;
+using namespace llvm::objcopy;
+using namespace llvm::objcopy::macho;
+using namespace llvm::object;
+
+using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>;
+using LoadCommandPred = std::function<bool(const LoadCommand &LC)>;
+
+#ifndef NDEBUG
+static bool isLoadCommandWithPayloadString(const LoadCommand &LC) {
+ // TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and
+ // LC_LAZY_LOAD_DYLIB
+ return LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH ||
+ LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_ID_DYLIB ||
+ LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_DYLIB ||
+ LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_WEAK_DYLIB;
+}
+#endif
+
+static StringRef getPayloadString(const LoadCommand &LC) {
+ assert(isLoadCommandWithPayloadString(LC) &&
+ "unsupported load command encountered");
+
+ return StringRef(reinterpret_cast<const char *>(LC.Payload.data()),
+ LC.Payload.size())
+ .rtrim('\0');
+}
+
+static Error removeSections(const CommonConfig &Config, Object &Obj) {
+ SectionPred RemovePred = [](const std::unique_ptr<Section> &) {
+ return false;
+ };
+
+ if (!Config.ToRemove.empty()) {
+ RemovePred = [&Config, RemovePred](const std::unique_ptr<Section> &Sec) {
+ return Config.ToRemove.matches(Sec->CanonicalName);
+ };
+ }
+
+ if (Config.StripAll || Config.StripDebug) {
+ // Remove all debug sections.
+ RemovePred = [RemovePred](const std::unique_ptr<Section> &Sec) {
+ if (Sec->Segname == "__DWARF")
+ return true;
+
+ return RemovePred(Sec);
+ };
+ }
+
+ if (!Config.OnlySection.empty()) {
+ // Overwrite RemovePred because --only-section takes priority.
+ RemovePred = [&Config](const std::unique_ptr<Section> &Sec) {
+ return !Config.OnlySection.matches(Sec->CanonicalName);
+ };
+ }
+
+ return Obj.removeSections(RemovePred);
+}
+
+static void markSymbols(const CommonConfig &, Object &Obj) {
+ // Symbols referenced from the indirect symbol table must not be removed.
+ for (IndirectSymbolEntry &ISE : Obj.IndirectSymTable.Symbols)
+ if (ISE.Symbol)
+ (*ISE.Symbol)->Referenced = true;
+}
+
+static void updateAndRemoveSymbols(const CommonConfig &Config,
+ const MachOConfig &MachOConfig,
+ Object &Obj) {
+ for (SymbolEntry &Sym : Obj.SymTable) {
+ auto I = Config.SymbolsToRename.find(Sym.Name);
+ if (I != Config.SymbolsToRename.end())
+ Sym.Name = std::string(I->getValue());
+ }
+
+ auto RemovePred = [Config, MachOConfig,
+ &Obj](const std::unique_ptr<SymbolEntry> &N) {
+ if (N->Referenced)
+ return false;
+ if (MachOConfig.KeepUndefined && N->isUndefinedSymbol())
+ return false;
+ if (N->n_desc & MachO::REFERENCED_DYNAMICALLY)
+ return false;
+ if (Config.StripAll)
+ return true;
+ if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT))
+ return true;
+ // This behavior is consistent with cctools' strip.
+ if (MachOConfig.StripSwiftSymbols &&
+ (Obj.Header.Flags & MachO::MH_DYLDLINK) && Obj.SwiftVersion &&
+ *Obj.SwiftVersion && N->isSwiftSymbol())
+ return true;
+ return false;
+ };
+
+ Obj.SymTable.removeSymbols(RemovePred);
+}
+
+template <typename LCType>
+static void updateLoadCommandPayloadString(LoadCommand &LC, StringRef S) {
+ assert(isLoadCommandWithPayloadString(LC) &&
+ "unsupported load command encountered");
+
+ uint32_t NewCmdsize = alignTo(sizeof(LCType) + S.size() + 1, 8);
+
+ LC.MachOLoadCommand.load_command_data.cmdsize = NewCmdsize;
+ LC.Payload.assign(NewCmdsize - sizeof(LCType), 0);
+ std::copy(S.begin(), S.end(), LC.Payload.begin());
+}
+
+static LoadCommand buildRPathLoadCommand(StringRef Path) {
+ LoadCommand LC;
+ MachO::rpath_command RPathLC;
+ RPathLC.cmd = MachO::LC_RPATH;
+ RPathLC.path = sizeof(MachO::rpath_command);
+ RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size() + 1, 8);
+ LC.MachOLoadCommand.rpath_command_data = RPathLC;
+ LC.Payload.assign(RPathLC.cmdsize - sizeof(MachO::rpath_command), 0);
+ std::copy(Path.begin(), Path.end(), LC.Payload.begin());
+ return LC;
+}
+
+static Error processLoadCommands(const MachOConfig &MachOConfig, Object &Obj) {
+ // Remove RPaths.
+ DenseSet<StringRef> RPathsToRemove(MachOConfig.RPathsToRemove.begin(),
+ MachOConfig.RPathsToRemove.end());
+
+ LoadCommandPred RemovePred = [&RPathsToRemove,
+ &MachOConfig](const LoadCommand &LC) {
+ if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) {
+ // When removing all RPaths we don't need to care
+ // about what it contains
+ if (MachOConfig.RemoveAllRpaths)
+ return true;
+
+ StringRef RPath = getPayloadString(LC);
+ if (RPathsToRemove.count(RPath)) {
+ RPathsToRemove.erase(RPath);
+ return true;
+ }
+ }
+ return false;
+ };
+
+ if (Error E = Obj.removeLoadCommands(RemovePred))
+ return E;
+
+ // Emit an error if the Mach-O binary does not contain an rpath path name
+ // specified in -delete_rpath.
+ for (StringRef RPath : MachOConfig.RPathsToRemove) {
+ if (RPathsToRemove.count(RPath))
+ return createStringError(errc::invalid_argument,
+ "no LC_RPATH load command with path: %s",
+ RPath.str().c_str());
+ }
+
+ DenseSet<StringRef> RPaths;
+
+ // Get all existing RPaths.
+ for (LoadCommand &LC : Obj.LoadCommands) {
+ if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH)
+ RPaths.insert(getPayloadString(LC));
+ }
+
+ // Throw errors for invalid RPaths.
+ for (const auto &OldNew : MachOConfig.RPathsToUpdate) {
+ StringRef Old = OldNew.getFirst();
+ StringRef New = OldNew.getSecond();
+ if (!RPaths.contains(Old))
+ return createStringError(errc::invalid_argument,
+ "no LC_RPATH load command with path: " + Old);
+ if (RPaths.contains(New))
+ return createStringError(errc::invalid_argument,
+ "rpath '" + New +
+ "' would create a duplicate load command");
+ }
+
+ // Update load commands.
+ for (LoadCommand &LC : Obj.LoadCommands) {
+ switch (LC.MachOLoadCommand.load_command_data.cmd) {
+ case MachO::LC_ID_DYLIB:
+ if (MachOConfig.SharedLibId)
+ updateLoadCommandPayloadString<MachO::dylib_command>(
+ LC, *MachOConfig.SharedLibId);
+ break;
+
+ case MachO::LC_RPATH: {
+ StringRef RPath = getPayloadString(LC);
+ StringRef NewRPath = MachOConfig.RPathsToUpdate.lookup(RPath);
+ if (!NewRPath.empty())
+ updateLoadCommandPayloadString<MachO::rpath_command>(LC, NewRPath);
+ break;
+ }
+
+ // TODO: Add LC_REEXPORT_DYLIB, LC_LAZY_LOAD_DYLIB, and LC_LOAD_UPWARD_DYLIB
+ // here once llvm-objcopy supports them.
+ case MachO::LC_LOAD_DYLIB:
+ case MachO::LC_LOAD_WEAK_DYLIB:
+ StringRef InstallName = getPayloadString(LC);
+ StringRef NewInstallName =
+ MachOConfig.InstallNamesToUpdate.lookup(InstallName);
+ if (!NewInstallName.empty())
+ updateLoadCommandPayloadString<MachO::dylib_command>(LC,
+ NewInstallName);
+ break;
+ }
+ }
+
+ // Add new RPaths.
+ for (StringRef RPath : MachOConfig.RPathToAdd) {
+ if (RPaths.contains(RPath))
+ return createStringError(errc::invalid_argument,
+ "rpath '" + RPath +
+ "' would create a duplicate load command");
+ RPaths.insert(RPath);
+ Obj.LoadCommands.push_back(buildRPathLoadCommand(RPath));
+ }
+
+ for (StringRef RPath : MachOConfig.RPathToPrepend) {
+ if (RPaths.contains(RPath))
+ return createStringError(errc::invalid_argument,
+ "rpath '" + RPath +
+ "' would create a duplicate load command");
+
+ RPaths.insert(RPath);
+ Obj.LoadCommands.insert(Obj.LoadCommands.begin(),
+ buildRPathLoadCommand(RPath));
+ }
+
+ // Unlike appending rpaths, the indexes of subsequent load commands must
+ // be recalculated after prepending one.
+ if (!MachOConfig.RPathToPrepend.empty())
+ Obj.updateLoadCommandIndexes();
+
+ return Error::success();
+}
+
+static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
+ Object &Obj) {
+ for (LoadCommand &LC : Obj.LoadCommands)
+ for (const std::unique_ptr<Section> &Sec : LC.Sections) {
+ if (Sec->CanonicalName == SecName) {
+ Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+ FileOutputBuffer::create(Filename, Sec->Content.size());
+ if (!BufferOrErr)
+ return BufferOrErr.takeError();
+ std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);
+ llvm::copy(Sec->Content, Buf->getBufferStart());
+
+ if (Error E = Buf->commit())
+ return E;
+ return Error::success();
+ }
+ }
+
+ return createStringError(object_error::parse_failed, "section '%s' not found",
+ SecName.str().c_str());
+}
+
+static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(Filename);
+ if (!BufOrErr)
+ return createFileError(Filename, errorCodeToError(BufOrErr.getError()));
+ std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr);
+
+ std::pair<StringRef, StringRef> Pair = SecName.split(',');
+ StringRef TargetSegName = Pair.first;
+ Section Sec(TargetSegName, Pair.second);
+ Sec.Content = Obj.NewSectionsContents.save(Buf->getBuffer());
+ Sec.Size = Sec.Content.size();
+
+ // Add the a section into an existing segment.
+ for (LoadCommand &LC : Obj.LoadCommands) {
+ Optional<StringRef> SegName = LC.getSegmentName();
+ if (SegName && SegName == TargetSegName) {
+ uint64_t Addr = *LC.getSegmentVMAddr();
+ for (const std::unique_ptr<Section> &S : LC.Sections)
+ Addr = std::max(Addr, S->Addr + S->Size);
+ LC.Sections.push_back(std::make_unique<Section>(Sec));
+ LC.Sections.back()->Addr = Addr;
+ return Error::success();
+ }
+ }
+
+ // There's no segment named TargetSegName. Create a new load command and
+ // Insert a new section into it.
+ LoadCommand &NewSegment =
+ Obj.addSegment(TargetSegName, alignTo(Sec.Size, 16384));
+ NewSegment.Sections.push_back(std::make_unique<Section>(Sec));
+ NewSegment.Sections.back()->Addr = *NewSegment.getSegmentVMAddr();
+ return Error::success();
+}
+
+static Expected<Section &> findSection(StringRef SecName, Object &O) {
+ StringRef SegName;
+ std::tie(SegName, SecName) = SecName.split(",");
+ auto FoundSeg =
+ llvm::find_if(O.LoadCommands, [SegName](const LoadCommand &LC) {
+ return LC.getSegmentName() == SegName;
+ });
+ if (FoundSeg == O.LoadCommands.end())
+ return createStringError(errc::invalid_argument,
+ "could not find segment with name '%s'",
+ SegName.str().c_str());
+ auto FoundSec = llvm::find_if(FoundSeg->Sections,
+ [SecName](const std::unique_ptr<Section> &Sec) {
+ return Sec->Sectname == SecName;
+ });
+ if (FoundSec == FoundSeg->Sections.end())
+ return createStringError(errc::invalid_argument,
+ "could not find section with name '%s'",
+ SecName.str().c_str());
+
+ assert(FoundSec->get()->CanonicalName == (SegName + "," + SecName).str());
+ return *FoundSec->get();
+}
+
+static Error updateSection(StringRef SecName, StringRef Filename, Object &O) {
+ Expected<Section &> SecToUpdateOrErr = findSection(SecName, O);
+
+ if (!SecToUpdateOrErr)
+ return SecToUpdateOrErr.takeError();
+ Section &Sec = *SecToUpdateOrErr;
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(Filename);
+ if (!BufOrErr)
+ return createFileError(Filename, errorCodeToError(BufOrErr.getError()));
+ std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr);
+
+ if (Buf->getBufferSize() > Sec.Size)
+ return createStringError(
+ errc::invalid_argument,
+ "new section cannot be larger than previous section");
+ Sec.Content = O.NewSectionsContents.save(Buf->getBuffer());
+ Sec.Size = Sec.Content.size();
+ return Error::success();
+}
+
+// isValidMachOCannonicalName returns success if Name is a MachO cannonical name
+// ("<segment>,<section>") and lengths of both segment and section names are
+// valid.
+static Error isValidMachOCannonicalName(StringRef Name) {
+ if (Name.count(',') != 1)
+ return createStringError(errc::invalid_argument,
+ "invalid section name '%s' (should be formatted "
+ "as '<segment name>,<section name>')",
+ Name.str().c_str());
+
+ std::pair<StringRef, StringRef> Pair = Name.split(',');
+ if (Pair.first.size() > 16)
+ return createStringError(errc::invalid_argument,
+ "too long segment name: '%s'",
+ Pair.first.str().c_str());
+ if (Pair.second.size() > 16)
+ return createStringError(errc::invalid_argument,
+ "too long section name: '%s'",
+ Pair.second.str().c_str());
+ return Error::success();
+}
+
+static Error handleArgs(const CommonConfig &Config,
+ const MachOConfig &MachOConfig, Object &Obj) {
+ // Dump sections before add/remove for compatibility with GNU objcopy.
+ for (StringRef Flag : Config.DumpSection) {
+ StringRef SectionName;
+ StringRef FileName;
+ std::tie(SectionName, FileName) = Flag.split('=');
+ if (Error E = dumpSectionToFile(SectionName, FileName, Obj))
+ return E;
+ }
+
+ if (Error E = removeSections(Config, Obj))
+ return E;
+
+ // Mark symbols to determine which symbols are still needed.
+ if (Config.StripAll)
+ markSymbols(Config, Obj);
+
+ updateAndRemoveSymbols(Config, MachOConfig, Obj);
+
+ if (Config.StripAll)
+ for (LoadCommand &LC : Obj.LoadCommands)
+ for (std::unique_ptr<Section> &Sec : LC.Sections)
+ Sec->Relocations.clear();
+
+ for (const auto &Flag : Config.AddSection) {
+ std::pair<StringRef, StringRef> SecPair = Flag.split("=");
+ StringRef SecName = SecPair.first;
+ StringRef File = SecPair.second;
+ if (Error E = isValidMachOCannonicalName(SecName))
+ return E;
+ if (Error E = addSection(SecName, File, Obj))
+ return E;
+ }
+
+ for (const auto &Flag : Config.UpdateSection) {
+ StringRef SectionName;
+ StringRef FileName;
+ std::tie(SectionName, FileName) = Flag.split('=');
+ if (Error E = isValidMachOCannonicalName(SectionName))
+ return E;
+ if (Error E = updateSection(SectionName, FileName, Obj))
+ return E;
+ }
+
+ if (Error E = processLoadCommands(MachOConfig, Obj))
+ return E;
+
+ return Error::success();
+}
+
+Error objcopy::macho::executeObjcopyOnBinary(const CommonConfig &Config,
+ const MachOConfig &MachOConfig,
+ object::MachOObjectFile &In,
+ raw_ostream &Out) {
+ MachOReader Reader(In);
+ Expected<std::unique_ptr<Object>> O = Reader.create();
+ if (!O)
+ return createFileError(Config.InputFilename, O.takeError());
+
+ if (O->get()->Header.FileType == MachO::HeaderFileType::MH_PRELOAD)
+ return createStringError(std::errc::not_supported,
+ "%s: MH_PRELOAD files are not supported",
+ Config.InputFilename.str().c_str());
+
+ if (Error E = handleArgs(Config, MachOConfig, **O))
+ return createFileError(Config.InputFilename, std::move(E));
+
+ // Page size used for alignment of segment sizes in Mach-O executables and
+ // dynamic libraries.
+ uint64_t PageSize;
+ switch (In.getArch()) {
+ case Triple::ArchType::arm:
+ case Triple::ArchType::aarch64:
+ case Triple::ArchType::aarch64_32:
+ PageSize = 16384;
+ break;
+ default:
+ PageSize = 4096;
+ }
+
+ MachOWriter Writer(**O, In.is64Bit(), In.isLittleEndian(),
+ sys::path::filename(Config.OutputFilename), PageSize, Out);
+ if (auto E = Writer.finalize())
+ return E;
+ return Writer.write();
+}
+
+Error objcopy::macho::executeObjcopyOnMachOUniversalBinary(
+ const MultiFormatConfig &Config, const MachOUniversalBinary &In,
+ raw_ostream &Out) {
+ SmallVector<OwningBinary<Binary>, 2> Binaries;
+ SmallVector<Slice, 2> Slices;
+ for (const auto &O : In.objects()) {
+ Expected<std::unique_ptr<Archive>> ArOrErr = O.getAsArchive();
+ if (ArOrErr) {
+ Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr =
+ createNewArchiveMembers(Config, **ArOrErr);
+ if (!NewArchiveMembersOrErr)
+ return NewArchiveMembersOrErr.takeError();
+ Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr =
+ writeArchiveToBuffer(*NewArchiveMembersOrErr,
+ (*ArOrErr)->hasSymbolTable(), (*ArOrErr)->kind(),
+ Config.getCommonConfig().DeterministicArchives,
+ (*ArOrErr)->isThin());
+ if (!OutputBufferOrErr)
+ return OutputBufferOrErr.takeError();
+ Expected<std::unique_ptr<Binary>> BinaryOrErr =
+ object::createBinary(**OutputBufferOrErr);
+ if (!BinaryOrErr)
+ return BinaryOrErr.takeError();
+ Binaries.emplace_back(std::move(*BinaryOrErr),
+ std::move(*OutputBufferOrErr));
+ Slices.emplace_back(*cast<Archive>(Binaries.back().getBinary()),
+ O.getCPUType(), O.getCPUSubType(),
+ O.getArchFlagName(), O.getAlign());
+ continue;
+ }
+ // The methods getAsArchive, getAsObjectFile, getAsIRObject of the class
+ // ObjectForArch return an Error in case of the type mismatch. We need to
+ // check each in turn to see what kind of slice this is, so ignore errors
+ // produced along the way.
+ consumeError(ArOrErr.takeError());
+
+ Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = O.getAsObjectFile();
+ if (!ObjOrErr) {
+ consumeError(ObjOrErr.takeError());
+ return createStringError(
+ std::errc::invalid_argument,
+ "slice for '%s' of the universal Mach-O binary "
+ "'%s' is not a Mach-O object or an archive",
+ O.getArchFlagName().c_str(),
+ Config.getCommonConfig().InputFilename.str().c_str());
+ }
+ std::string ArchFlagName = O.getArchFlagName();
+
+ SmallVector<char, 0> Buffer;
+ raw_svector_ostream MemStream(Buffer);
+
+ Expected<const MachOConfig &> MachO = Config.getMachOConfig();
+ if (!MachO)
+ return MachO.takeError();
+
+ if (Error E = executeObjcopyOnBinary(Config.getCommonConfig(), *MachO,
+ **ObjOrErr, MemStream))
+ return E;
+
+ auto MB = std::make_unique<SmallVectorMemoryBuffer>(
+ std::move(Buffer), ArchFlagName, /*RequiresNullTerminator=*/false);
+ Expected<std::unique_ptr<Binary>> BinaryOrErr = object::createBinary(*MB);
+ if (!BinaryOrErr)
+ return BinaryOrErr.takeError();
+ Binaries.emplace_back(std::move(*BinaryOrErr), std::move(MB));
+ Slices.emplace_back(*cast<MachOObjectFile>(Binaries.back().getBinary()),
+ O.getAlign());
+ }
+
+ if (Error Err = writeUniversalBinaryToStream(Slices, Out))
+ return Err;
+
+ return Error::success();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOObjcopy.h b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOObjcopy.h
new file mode 100644
index 00000000000..d03eee9d5fd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOObjcopy.h
@@ -0,0 +1,39 @@
+//===- MachOObjcopy.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_MACHOOBJCOPY_H
+#define LLVM_TOOLS_OBJCOPY_MACHOOBJCOPY_H
+
+namespace llvm {
+class Error;
+class raw_ostream;
+
+namespace object {
+class MachOObjectFile;
+class MachOUniversalBinary;
+} // end namespace object
+
+namespace objcopy {
+struct CommonConfig;
+struct MachOConfig;
+class MultiFormatConfig;
+
+namespace macho {
+Error executeObjcopyOnBinary(const CommonConfig &Config,
+ const MachOConfig &MachOConfig,
+ object::MachOObjectFile &In, raw_ostream &Out);
+
+Error executeObjcopyOnMachOUniversalBinary(
+ const MultiFormatConfig &Config, const object::MachOUniversalBinary &In,
+ raw_ostream &Out);
+
+} // end namespace macho
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_MACHOOBJCOPY_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOReader.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOReader.cpp
new file mode 100644
index 00000000000..d68d1692997
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOReader.cpp
@@ -0,0 +1,374 @@
+//===- MachOReader.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachOReader.h"
+#include "Object.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Support/Errc.h"
+#include <memory>
+
+using namespace llvm;
+using namespace llvm::objcopy;
+using namespace llvm::objcopy::macho;
+
+void MachOReader::readHeader(Object &O) const {
+ O.Header.Magic = MachOObj.getHeader().magic;
+ O.Header.CPUType = MachOObj.getHeader().cputype;
+ O.Header.CPUSubType = MachOObj.getHeader().cpusubtype;
+ O.Header.FileType = MachOObj.getHeader().filetype;
+ O.Header.NCmds = MachOObj.getHeader().ncmds;
+ O.Header.SizeOfCmds = MachOObj.getHeader().sizeofcmds;
+ O.Header.Flags = MachOObj.getHeader().flags;
+}
+
+template <typename SectionType>
+static Section constructSectionCommon(const SectionType &Sec, uint32_t Index) {
+ StringRef SegName(Sec.segname, strnlen(Sec.segname, sizeof(Sec.segname)));
+ StringRef SectName(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname)));
+ Section S(SegName, SectName);
+ S.Index = Index;
+ S.Addr = Sec.addr;
+ S.Size = Sec.size;
+ S.OriginalOffset = Sec.offset;
+ S.Align = Sec.align;
+ S.RelOff = Sec.reloff;
+ S.NReloc = Sec.nreloc;
+ S.Flags = Sec.flags;
+ S.Reserved1 = Sec.reserved1;
+ S.Reserved2 = Sec.reserved2;
+ S.Reserved3 = 0;
+ return S;
+}
+
+Section constructSection(const MachO::section &Sec, uint32_t Index) {
+ return constructSectionCommon(Sec, Index);
+}
+
+Section constructSection(const MachO::section_64 &Sec, uint32_t Index) {
+ Section S = constructSectionCommon(Sec, Index);
+ S.Reserved3 = Sec.reserved3;
+ return S;
+}
+
+template <typename SectionType, typename SegmentType>
+Expected<std::vector<std::unique_ptr<Section>>> static extractSections(
+ const object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ const object::MachOObjectFile &MachOObj, uint32_t &NextSectionIndex) {
+ std::vector<std::unique_ptr<Section>> Sections;
+ for (auto Curr = reinterpret_cast<const SectionType *>(LoadCmd.Ptr +
+ sizeof(SegmentType)),
+ End = reinterpret_cast<const SectionType *>(LoadCmd.Ptr +
+ LoadCmd.C.cmdsize);
+ Curr < End; ++Curr) {
+ SectionType Sec;
+ memcpy((void *)&Sec, Curr, sizeof(SectionType));
+
+ if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost)
+ MachO::swapStruct(Sec);
+
+ Sections.push_back(
+ std::make_unique<Section>(constructSection(Sec, NextSectionIndex)));
+
+ Section &S = *Sections.back();
+
+ Expected<object::SectionRef> SecRef =
+ MachOObj.getSection(NextSectionIndex++);
+ if (!SecRef)
+ return SecRef.takeError();
+
+ Expected<ArrayRef<uint8_t>> Data =
+ MachOObj.getSectionContents(SecRef->getRawDataRefImpl());
+ if (!Data)
+ return Data.takeError();
+
+ S.Content =
+ StringRef(reinterpret_cast<const char *>(Data->data()), Data->size());
+
+ const uint32_t CPUType = MachOObj.getHeader().cputype;
+ S.Relocations.reserve(S.NReloc);
+ for (auto RI = MachOObj.section_rel_begin(SecRef->getRawDataRefImpl()),
+ RE = MachOObj.section_rel_end(SecRef->getRawDataRefImpl());
+ RI != RE; ++RI) {
+ RelocationInfo R;
+ R.Symbol = nullptr; // We'll fill this field later.
+ R.Info = MachOObj.getRelocation(RI->getRawDataRefImpl());
+ R.Scattered = MachOObj.isRelocationScattered(R.Info);
+ unsigned Type = MachOObj.getAnyRelocationType(R.Info);
+ // TODO Support CPU_TYPE_ARM.
+ R.IsAddend = !R.Scattered && (CPUType == MachO::CPU_TYPE_ARM64 &&
+ Type == MachO::ARM64_RELOC_ADDEND);
+ R.Extern = !R.Scattered && MachOObj.getPlainRelocationExternal(R.Info);
+ S.Relocations.push_back(R);
+ }
+
+ assert(S.NReloc == S.Relocations.size() &&
+ "Incorrect number of relocations");
+ }
+ return std::move(Sections);
+}
+
+Error MachOReader::readLoadCommands(Object &O) const {
+ // For MachO sections indices start from 1.
+ uint32_t NextSectionIndex = 1;
+ static constexpr char TextSegmentName[] = "__TEXT";
+ for (auto LoadCmd : MachOObj.load_commands()) {
+ LoadCommand LC;
+ switch (LoadCmd.C.cmd) {
+ case MachO::LC_CODE_SIGNATURE:
+ O.CodeSignatureCommandIndex = O.LoadCommands.size();
+ break;
+ case MachO::LC_SEGMENT:
+ // LoadCmd.Ptr might not be aligned temporarily as
+ // MachO::segment_command requires, but the segname char pointer do not
+ // have alignment restrictions.
+ if (StringRef(reinterpret_cast<const char *>(
+ LoadCmd.Ptr + offsetof(MachO::segment_command, segname))) ==
+ TextSegmentName)
+ O.TextSegmentCommandIndex = O.LoadCommands.size();
+
+ if (Expected<std::vector<std::unique_ptr<Section>>> Sections =
+ extractSections<MachO::section, MachO::segment_command>(
+ LoadCmd, MachOObj, NextSectionIndex))
+ LC.Sections = std::move(*Sections);
+ else
+ return Sections.takeError();
+ break;
+ case MachO::LC_SEGMENT_64:
+ // LoadCmd.Ptr might not be aligned temporarily as
+ // MachO::segment_command_64 requires, but the segname char pointer do
+ // not have alignment restrictions.
+ if (StringRef(reinterpret_cast<const char *>(
+ LoadCmd.Ptr + offsetof(MachO::segment_command_64, segname))) ==
+ TextSegmentName)
+ O.TextSegmentCommandIndex = O.LoadCommands.size();
+
+ if (Expected<std::vector<std::unique_ptr<Section>>> Sections =
+ extractSections<MachO::section_64, MachO::segment_command_64>(
+ LoadCmd, MachOObj, NextSectionIndex))
+ LC.Sections = std::move(*Sections);
+ else
+ return Sections.takeError();
+ break;
+ case MachO::LC_SYMTAB:
+ O.SymTabCommandIndex = O.LoadCommands.size();
+ break;
+ case MachO::LC_DYSYMTAB:
+ O.DySymTabCommandIndex = O.LoadCommands.size();
+ break;
+ case MachO::LC_DYLD_INFO:
+ case MachO::LC_DYLD_INFO_ONLY:
+ O.DyLdInfoCommandIndex = O.LoadCommands.size();
+ break;
+ case MachO::LC_DATA_IN_CODE:
+ O.DataInCodeCommandIndex = O.LoadCommands.size();
+ break;
+ case MachO::LC_LINKER_OPTIMIZATION_HINT:
+ O.LinkerOptimizationHintCommandIndex = O.LoadCommands.size();
+ break;
+ case MachO::LC_FUNCTION_STARTS:
+ O.FunctionStartsCommandIndex = O.LoadCommands.size();
+ break;
+ case MachO::LC_DYLD_EXPORTS_TRIE:
+ O.ExportsTrieCommandIndex = O.LoadCommands.size();
+ break;
+ case MachO::LC_DYLD_CHAINED_FIXUPS:
+ O.ChainedFixupsCommandIndex = O.LoadCommands.size();
+ break;
+ }
+#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
+ case MachO::LCName: \
+ memcpy((void *)&(LC.MachOLoadCommand.LCStruct##_data), LoadCmd.Ptr, \
+ sizeof(MachO::LCStruct)); \
+ if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) \
+ MachO::swapStruct(LC.MachOLoadCommand.LCStruct##_data); \
+ if (LoadCmd.C.cmdsize > sizeof(MachO::LCStruct)) \
+ LC.Payload = ArrayRef<uint8_t>( \
+ reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + \
+ sizeof(MachO::LCStruct), \
+ LoadCmd.C.cmdsize - sizeof(MachO::LCStruct)); \
+ break;
+
+ switch (LoadCmd.C.cmd) {
+ default:
+ memcpy((void *)&(LC.MachOLoadCommand.load_command_data), LoadCmd.Ptr,
+ sizeof(MachO::load_command));
+ if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost)
+ MachO::swapStruct(LC.MachOLoadCommand.load_command_data);
+ if (LoadCmd.C.cmdsize > sizeof(MachO::load_command))
+ LC.Payload = ArrayRef<uint8_t>(
+ reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) +
+ sizeof(MachO::load_command),
+ LoadCmd.C.cmdsize - sizeof(MachO::load_command));
+ break;
+#include "llvm/BinaryFormat/MachO.def"
+ }
+ O.LoadCommands.push_back(std::move(LC));
+ }
+ return Error::success();
+}
+
+template <typename nlist_t>
+SymbolEntry constructSymbolEntry(StringRef StrTable, const nlist_t &nlist) {
+ assert(nlist.n_strx < StrTable.size() &&
+ "n_strx exceeds the size of the string table");
+ SymbolEntry SE;
+ SE.Name = StringRef(StrTable.data() + nlist.n_strx).str();
+ SE.n_type = nlist.n_type;
+ SE.n_sect = nlist.n_sect;
+ SE.n_desc = nlist.n_desc;
+ SE.n_value = nlist.n_value;
+ return SE;
+}
+
+void MachOReader::readSymbolTable(Object &O) const {
+ StringRef StrTable = MachOObj.getStringTableData();
+ for (auto Symbol : MachOObj.symbols()) {
+ SymbolEntry SE =
+ (MachOObj.is64Bit()
+ ? constructSymbolEntry(StrTable, MachOObj.getSymbol64TableEntry(
+ Symbol.getRawDataRefImpl()))
+ : constructSymbolEntry(StrTable, MachOObj.getSymbolTableEntry(
+ Symbol.getRawDataRefImpl())));
+
+ O.SymTable.Symbols.push_back(std::make_unique<SymbolEntry>(SE));
+ }
+}
+
+void MachOReader::setSymbolInRelocationInfo(Object &O) const {
+ std::vector<const Section *> Sections;
+ for (auto &LC : O.LoadCommands)
+ for (std::unique_ptr<Section> &Sec : LC.Sections)
+ Sections.push_back(Sec.get());
+
+ for (LoadCommand &LC : O.LoadCommands)
+ for (std::unique_ptr<Section> &Sec : LC.Sections)
+ for (auto &Reloc : Sec->Relocations)
+ if (!Reloc.Scattered && !Reloc.IsAddend) {
+ const uint32_t SymbolNum =
+ Reloc.getPlainRelocationSymbolNum(MachOObj.isLittleEndian());
+ if (Reloc.Extern) {
+ Reloc.Symbol = O.SymTable.getSymbolByIndex(SymbolNum);
+ } else {
+ // FIXME: Refactor error handling in MachOReader and report an error
+ // if we encounter an invalid relocation.
+ assert(SymbolNum >= 1 && SymbolNum <= Sections.size() &&
+ "Invalid section index.");
+ Reloc.Sec = Sections[SymbolNum - 1];
+ }
+ }
+}
+
+void MachOReader::readRebaseInfo(Object &O) const {
+ O.Rebases.Opcodes = MachOObj.getDyldInfoRebaseOpcodes();
+}
+
+void MachOReader::readBindInfo(Object &O) const {
+ O.Binds.Opcodes = MachOObj.getDyldInfoBindOpcodes();
+}
+
+void MachOReader::readWeakBindInfo(Object &O) const {
+ O.WeakBinds.Opcodes = MachOObj.getDyldInfoWeakBindOpcodes();
+}
+
+void MachOReader::readLazyBindInfo(Object &O) const {
+ O.LazyBinds.Opcodes = MachOObj.getDyldInfoLazyBindOpcodes();
+}
+
+void MachOReader::readExportInfo(Object &O) const {
+ O.Exports.Trie = MachOObj.getDyldInfoExportsTrie();
+}
+
+void MachOReader::readLinkData(Object &O, Optional<size_t> LCIndex,
+ LinkData &LD) const {
+ if (!LCIndex)
+ return;
+ const MachO::linkedit_data_command &LC =
+ O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data;
+ LD.Data =
+ arrayRefFromStringRef(MachOObj.getData().substr(LC.dataoff, LC.datasize));
+}
+
+void MachOReader::readDataInCodeData(Object &O) const {
+ return readLinkData(O, O.DataInCodeCommandIndex, O.DataInCode);
+}
+
+void MachOReader::readLinkerOptimizationHint(Object &O) const {
+ return readLinkData(O, O.LinkerOptimizationHintCommandIndex,
+ O.LinkerOptimizationHint);
+}
+
+void MachOReader::readFunctionStartsData(Object &O) const {
+ return readLinkData(O, O.FunctionStartsCommandIndex, O.FunctionStarts);
+}
+
+void MachOReader::readExportsTrie(Object &O) const {
+ return readLinkData(O, O.ExportsTrieCommandIndex, O.ExportsTrie);
+}
+
+void MachOReader::readChainedFixups(Object &O) const {
+ return readLinkData(O, O.ChainedFixupsCommandIndex, O.ChainedFixups);
+}
+
+void MachOReader::readIndirectSymbolTable(Object &O) const {
+ MachO::dysymtab_command DySymTab = MachOObj.getDysymtabLoadCommand();
+ constexpr uint32_t AbsOrLocalMask =
+ MachO::INDIRECT_SYMBOL_LOCAL | MachO::INDIRECT_SYMBOL_ABS;
+ for (uint32_t i = 0; i < DySymTab.nindirectsyms; ++i) {
+ uint32_t Index = MachOObj.getIndirectSymbolTableEntry(DySymTab, i);
+ if ((Index & AbsOrLocalMask) != 0)
+ O.IndirectSymTable.Symbols.emplace_back(Index, None);
+ else
+ O.IndirectSymTable.Symbols.emplace_back(
+ Index, O.SymTable.getSymbolByIndex(Index));
+ }
+}
+
+void MachOReader::readSwiftVersion(Object &O) const {
+ struct ObjCImageInfo {
+ uint32_t Version;
+ uint32_t Flags;
+ } ImageInfo;
+
+ for (const LoadCommand &LC : O.LoadCommands)
+ for (const std::unique_ptr<Section> &Sec : LC.Sections)
+ if (Sec->Sectname == "__objc_imageinfo" &&
+ (Sec->Segname == "__DATA" || Sec->Segname == "__DATA_CONST" ||
+ Sec->Segname == "__DATA_DIRTY") &&
+ Sec->Content.size() >= sizeof(ObjCImageInfo)) {
+ memcpy(&ImageInfo, Sec->Content.data(), sizeof(ObjCImageInfo));
+ if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) {
+ sys::swapByteOrder(ImageInfo.Version);
+ sys::swapByteOrder(ImageInfo.Flags);
+ }
+ O.SwiftVersion = (ImageInfo.Flags >> 8) & 0xff;
+ return;
+ }
+}
+
+Expected<std::unique_ptr<Object>> MachOReader::create() const {
+ auto Obj = std::make_unique<Object>();
+ readHeader(*Obj);
+ if (Error E = readLoadCommands(*Obj))
+ return std::move(E);
+ readSymbolTable(*Obj);
+ setSymbolInRelocationInfo(*Obj);
+ readRebaseInfo(*Obj);
+ readBindInfo(*Obj);
+ readWeakBindInfo(*Obj);
+ readLazyBindInfo(*Obj);
+ readExportInfo(*Obj);
+ readDataInCodeData(*Obj);
+ readLinkerOptimizationHint(*Obj);
+ readFunctionStartsData(*Obj);
+ readExportsTrie(*Obj);
+ readChainedFixups(*Obj);
+ readIndirectSymbolTable(*Obj);
+ readSwiftVersion(*Obj);
+ return std::move(Obj);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOReader.h b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOReader.h
new file mode 100644
index 00000000000..b29e86ca642
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOReader.h
@@ -0,0 +1,57 @@
+//===- MachOReader.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachOObjcopy.h"
+#include "Object.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Object/MachO.h"
+#include <memory>
+
+namespace llvm {
+namespace objcopy {
+namespace macho {
+
+// The hierarchy of readers is responsible for parsing different inputs:
+// raw binaries and regular MachO object files.
+class Reader {
+public:
+ virtual ~Reader(){};
+ virtual Expected<std::unique_ptr<Object>> create() const = 0;
+};
+
+class MachOReader : public Reader {
+ const object::MachOObjectFile &MachOObj;
+
+ void readHeader(Object &O) const;
+ Error readLoadCommands(Object &O) const;
+ void readSymbolTable(Object &O) const;
+ void setSymbolInRelocationInfo(Object &O) const;
+ void readRebaseInfo(Object &O) const;
+ void readBindInfo(Object &O) const;
+ void readWeakBindInfo(Object &O) const;
+ void readLazyBindInfo(Object &O) const;
+ void readExportInfo(Object &O) const;
+ void readLinkData(Object &O, Optional<size_t> LCIndex, LinkData &LD) const;
+ void readCodeSignature(Object &O) const;
+ void readDataInCodeData(Object &O) const;
+ void readLinkerOptimizationHint(Object &O) const;
+ void readFunctionStartsData(Object &O) const;
+ void readExportsTrie(Object &O) const;
+ void readChainedFixups(Object &O) const;
+ void readIndirectSymbolTable(Object &O) const;
+ void readSwiftVersion(Object &O) const;
+
+public:
+ explicit MachOReader(const object::MachOObjectFile &Obj) : MachOObj(Obj) {}
+
+ Expected<std::unique_ptr<Object>> create() const override;
+};
+
+} // end namespace macho
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOWriter.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOWriter.cpp
new file mode 100644
index 00000000000..52f20794cc5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOWriter.cpp
@@ -0,0 +1,748 @@
+//===- MachOWriter.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachOWriter.h"
+#include "MachOLayoutBuilder.h"
+#include "Object.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/SHA256.h"
+#include <memory>
+
+#if defined(__APPLE__)
+#include <sys/mman.h>
+#endif
+
+using namespace llvm;
+using namespace llvm::objcopy::macho;
+using namespace llvm::support::endian;
+
+size_t MachOWriter::headerSize() const {
+ return Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
+}
+
+size_t MachOWriter::loadCommandsSize() const { return O.Header.SizeOfCmds; }
+
+size_t MachOWriter::symTableSize() const {
+ return O.SymTable.Symbols.size() *
+ (Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist));
+}
+
+size_t MachOWriter::totalSize() const {
+ // Going from tail to head and looking for an appropriate "anchor" to
+ // calculate the total size assuming that all the offsets are either valid
+ // ("true") or 0 (0 indicates that the corresponding part is missing).
+
+ SmallVector<size_t, 7> Ends;
+ if (O.SymTabCommandIndex) {
+ const MachO::symtab_command &SymTabCommand =
+ O.LoadCommands[*O.SymTabCommandIndex]
+ .MachOLoadCommand.symtab_command_data;
+ if (SymTabCommand.symoff)
+ Ends.push_back(SymTabCommand.symoff + symTableSize());
+ if (SymTabCommand.stroff)
+ Ends.push_back(SymTabCommand.stroff + SymTabCommand.strsize);
+ }
+ if (O.DyLdInfoCommandIndex) {
+ const MachO::dyld_info_command &DyLdInfoCommand =
+ O.LoadCommands[*O.DyLdInfoCommandIndex]
+ .MachOLoadCommand.dyld_info_command_data;
+ if (DyLdInfoCommand.rebase_off) {
+ assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) &&
+ "Incorrect rebase opcodes size");
+ Ends.push_back(DyLdInfoCommand.rebase_off + DyLdInfoCommand.rebase_size);
+ }
+ if (DyLdInfoCommand.bind_off) {
+ assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) &&
+ "Incorrect bind opcodes size");
+ Ends.push_back(DyLdInfoCommand.bind_off + DyLdInfoCommand.bind_size);
+ }
+ if (DyLdInfoCommand.weak_bind_off) {
+ assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) &&
+ "Incorrect weak bind opcodes size");
+ Ends.push_back(DyLdInfoCommand.weak_bind_off +
+ DyLdInfoCommand.weak_bind_size);
+ }
+ if (DyLdInfoCommand.lazy_bind_off) {
+ assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) &&
+ "Incorrect lazy bind opcodes size");
+ Ends.push_back(DyLdInfoCommand.lazy_bind_off +
+ DyLdInfoCommand.lazy_bind_size);
+ }
+ if (DyLdInfoCommand.export_off) {
+ assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) &&
+ "Incorrect trie size");
+ Ends.push_back(DyLdInfoCommand.export_off + DyLdInfoCommand.export_size);
+ }
+ }
+
+ if (O.DySymTabCommandIndex) {
+ const MachO::dysymtab_command &DySymTabCommand =
+ O.LoadCommands[*O.DySymTabCommandIndex]
+ .MachOLoadCommand.dysymtab_command_data;
+
+ if (DySymTabCommand.indirectsymoff)
+ Ends.push_back(DySymTabCommand.indirectsymoff +
+ sizeof(uint32_t) * O.IndirectSymTable.Symbols.size());
+ }
+
+ if (O.CodeSignatureCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.CodeSignatureCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+ if (LinkEditDataCommand.dataoff)
+ Ends.push_back(LinkEditDataCommand.dataoff +
+ LinkEditDataCommand.datasize);
+ }
+
+ if (O.DataInCodeCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.DataInCodeCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Ends.push_back(LinkEditDataCommand.dataoff +
+ LinkEditDataCommand.datasize);
+ }
+
+ if (O.LinkerOptimizationHintCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.LinkerOptimizationHintCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Ends.push_back(LinkEditDataCommand.dataoff +
+ LinkEditDataCommand.datasize);
+ }
+
+ if (O.FunctionStartsCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.FunctionStartsCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Ends.push_back(LinkEditDataCommand.dataoff +
+ LinkEditDataCommand.datasize);
+ }
+
+ if (O.ChainedFixupsCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.ChainedFixupsCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Ends.push_back(LinkEditDataCommand.dataoff +
+ LinkEditDataCommand.datasize);
+ }
+
+ if (O.ExportsTrieCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.ExportsTrieCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Ends.push_back(LinkEditDataCommand.dataoff +
+ LinkEditDataCommand.datasize);
+ }
+
+ // Otherwise, use the last section / reloction.
+ for (const LoadCommand &LC : O.LoadCommands)
+ for (const std::unique_ptr<Section> &S : LC.Sections) {
+ if (!S->hasValidOffset()) {
+ assert((S->Offset == 0) && "Skipped section's offset must be zero");
+ assert((S->isVirtualSection() || S->Size == 0) &&
+ "Non-zero-fill sections with zero offset must have zero size");
+ continue;
+ }
+ assert((S->Offset != 0) &&
+ "Non-zero-fill section's offset cannot be zero");
+ Ends.push_back(S->Offset + S->Size);
+ if (S->RelOff)
+ Ends.push_back(S->RelOff +
+ S->NReloc * sizeof(MachO::any_relocation_info));
+ }
+
+ if (!Ends.empty())
+ return *std::max_element(Ends.begin(), Ends.end());
+
+ // Otherwise, we have only Mach header and load commands.
+ return headerSize() + loadCommandsSize();
+}
+
+void MachOWriter::writeHeader() {
+ MachO::mach_header_64 Header;
+
+ Header.magic = O.Header.Magic;
+ Header.cputype = O.Header.CPUType;
+ Header.cpusubtype = O.Header.CPUSubType;
+ Header.filetype = O.Header.FileType;
+ Header.ncmds = O.Header.NCmds;
+ Header.sizeofcmds = O.Header.SizeOfCmds;
+ Header.flags = O.Header.Flags;
+ Header.reserved = O.Header.Reserved;
+
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(Header);
+
+ auto HeaderSize =
+ Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
+ memcpy(Buf->getBufferStart(), &Header, HeaderSize);
+}
+
+void MachOWriter::writeLoadCommands() {
+ uint8_t *Begin =
+ reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + headerSize();
+ for (const LoadCommand &LC : O.LoadCommands) {
+ // Construct a load command.
+ MachO::macho_load_command MLC = LC.MachOLoadCommand;
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(MLC.segment_command_data);
+ memcpy(Begin, &MLC.segment_command_data, sizeof(MachO::segment_command));
+ Begin += sizeof(MachO::segment_command);
+
+ for (const std::unique_ptr<Section> &Sec : LC.Sections)
+ writeSectionInLoadCommand<MachO::section>(*Sec, Begin);
+ continue;
+ case MachO::LC_SEGMENT_64:
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(MLC.segment_command_64_data);
+ memcpy(Begin, &MLC.segment_command_64_data,
+ sizeof(MachO::segment_command_64));
+ Begin += sizeof(MachO::segment_command_64);
+
+ for (const std::unique_ptr<Section> &Sec : LC.Sections)
+ writeSectionInLoadCommand<MachO::section_64>(*Sec, Begin);
+ continue;
+ }
+
+#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
+ case MachO::LCName: \
+ assert(sizeof(MachO::LCStruct) + LC.Payload.size() == \
+ MLC.load_command_data.cmdsize); \
+ if (IsLittleEndian != sys::IsLittleEndianHost) \
+ MachO::swapStruct(MLC.LCStruct##_data); \
+ memcpy(Begin, &MLC.LCStruct##_data, sizeof(MachO::LCStruct)); \
+ Begin += sizeof(MachO::LCStruct); \
+ if (!LC.Payload.empty()) \
+ memcpy(Begin, LC.Payload.data(), LC.Payload.size()); \
+ Begin += LC.Payload.size(); \
+ break;
+
+ // Copy the load command as it is.
+ switch (MLC.load_command_data.cmd) {
+ default:
+ assert(sizeof(MachO::load_command) + LC.Payload.size() ==
+ MLC.load_command_data.cmdsize);
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(MLC.load_command_data);
+ memcpy(Begin, &MLC.load_command_data, sizeof(MachO::load_command));
+ Begin += sizeof(MachO::load_command);
+ if (!LC.Payload.empty())
+ memcpy(Begin, LC.Payload.data(), LC.Payload.size());
+ Begin += LC.Payload.size();
+ break;
+#include "llvm/BinaryFormat/MachO.def"
+ }
+ }
+}
+
+template <typename StructType>
+void MachOWriter::writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out) {
+ StructType Temp;
+ assert(Sec.Segname.size() <= sizeof(Temp.segname) && "too long segment name");
+ assert(Sec.Sectname.size() <= sizeof(Temp.sectname) &&
+ "too long section name");
+ memset(&Temp, 0, sizeof(StructType));
+ memcpy(Temp.segname, Sec.Segname.data(), Sec.Segname.size());
+ memcpy(Temp.sectname, Sec.Sectname.data(), Sec.Sectname.size());
+ Temp.addr = Sec.Addr;
+ Temp.size = Sec.Size;
+ Temp.offset = Sec.Offset;
+ Temp.align = Sec.Align;
+ Temp.reloff = Sec.RelOff;
+ Temp.nreloc = Sec.NReloc;
+ Temp.flags = Sec.Flags;
+ Temp.reserved1 = Sec.Reserved1;
+ Temp.reserved2 = Sec.Reserved2;
+
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(Temp);
+ memcpy(Out, &Temp, sizeof(StructType));
+ Out += sizeof(StructType);
+}
+
+void MachOWriter::writeSections() {
+ for (const LoadCommand &LC : O.LoadCommands)
+ for (const std::unique_ptr<Section> &Sec : LC.Sections) {
+ if (!Sec->hasValidOffset()) {
+ assert((Sec->Offset == 0) && "Skipped section's offset must be zero");
+ assert((Sec->isVirtualSection() || Sec->Size == 0) &&
+ "Non-zero-fill sections with zero offset must have zero size");
+ continue;
+ }
+
+ assert(Sec->Offset && "Section offset can not be zero");
+ assert((Sec->Size == Sec->Content.size()) && "Incorrect section size");
+ memcpy(Buf->getBufferStart() + Sec->Offset, Sec->Content.data(),
+ Sec->Content.size());
+ for (size_t Index = 0; Index < Sec->Relocations.size(); ++Index) {
+ RelocationInfo RelocInfo = Sec->Relocations[Index];
+ if (!RelocInfo.Scattered && !RelocInfo.IsAddend) {
+ const uint32_t SymbolNum = RelocInfo.Extern
+ ? (*RelocInfo.Symbol)->Index
+ : (*RelocInfo.Sec)->Index;
+ RelocInfo.setPlainRelocationSymbolNum(SymbolNum, IsLittleEndian);
+ }
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(
+ reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info));
+ memcpy(Buf->getBufferStart() + Sec->RelOff +
+ Index * sizeof(MachO::any_relocation_info),
+ &RelocInfo.Info, sizeof(RelocInfo.Info));
+ }
+ }
+}
+
+template <typename NListType>
+void writeNListEntry(const SymbolEntry &SE, bool IsLittleEndian, char *&Out,
+ uint32_t Nstrx) {
+ NListType ListEntry;
+ ListEntry.n_strx = Nstrx;
+ ListEntry.n_type = SE.n_type;
+ ListEntry.n_sect = SE.n_sect;
+ ListEntry.n_desc = SE.n_desc;
+ ListEntry.n_value = SE.n_value;
+
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(ListEntry);
+ memcpy(Out, reinterpret_cast<const char *>(&ListEntry), sizeof(NListType));
+ Out += sizeof(NListType);
+}
+
+void MachOWriter::writeStringTable() {
+ if (!O.SymTabCommandIndex)
+ return;
+ const MachO::symtab_command &SymTabCommand =
+ O.LoadCommands[*O.SymTabCommandIndex]
+ .MachOLoadCommand.symtab_command_data;
+
+ uint8_t *StrTable = (uint8_t *)Buf->getBufferStart() + SymTabCommand.stroff;
+ LayoutBuilder.getStringTableBuilder().write(StrTable);
+}
+
+void MachOWriter::writeSymbolTable() {
+ if (!O.SymTabCommandIndex)
+ return;
+ const MachO::symtab_command &SymTabCommand =
+ O.LoadCommands[*O.SymTabCommandIndex]
+ .MachOLoadCommand.symtab_command_data;
+
+ char *SymTable = (char *)Buf->getBufferStart() + SymTabCommand.symoff;
+ for (auto Iter = O.SymTable.Symbols.begin(), End = O.SymTable.Symbols.end();
+ Iter != End; Iter++) {
+ SymbolEntry *Sym = Iter->get();
+ uint32_t Nstrx = LayoutBuilder.getStringTableBuilder().getOffset(Sym->Name);
+
+ if (Is64Bit)
+ writeNListEntry<MachO::nlist_64>(*Sym, IsLittleEndian, SymTable, Nstrx);
+ else
+ writeNListEntry<MachO::nlist>(*Sym, IsLittleEndian, SymTable, Nstrx);
+ }
+}
+
+void MachOWriter::writeRebaseInfo() {
+ if (!O.DyLdInfoCommandIndex)
+ return;
+ const MachO::dyld_info_command &DyLdInfoCommand =
+ O.LoadCommands[*O.DyLdInfoCommandIndex]
+ .MachOLoadCommand.dyld_info_command_data;
+ char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.rebase_off;
+ assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) &&
+ "Incorrect rebase opcodes size");
+ memcpy(Out, O.Rebases.Opcodes.data(), O.Rebases.Opcodes.size());
+}
+
+void MachOWriter::writeBindInfo() {
+ if (!O.DyLdInfoCommandIndex)
+ return;
+ const MachO::dyld_info_command &DyLdInfoCommand =
+ O.LoadCommands[*O.DyLdInfoCommandIndex]
+ .MachOLoadCommand.dyld_info_command_data;
+ char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.bind_off;
+ assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) &&
+ "Incorrect bind opcodes size");
+ memcpy(Out, O.Binds.Opcodes.data(), O.Binds.Opcodes.size());
+}
+
+void MachOWriter::writeWeakBindInfo() {
+ if (!O.DyLdInfoCommandIndex)
+ return;
+ const MachO::dyld_info_command &DyLdInfoCommand =
+ O.LoadCommands[*O.DyLdInfoCommandIndex]
+ .MachOLoadCommand.dyld_info_command_data;
+ char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.weak_bind_off;
+ assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) &&
+ "Incorrect weak bind opcodes size");
+ memcpy(Out, O.WeakBinds.Opcodes.data(), O.WeakBinds.Opcodes.size());
+}
+
+void MachOWriter::writeLazyBindInfo() {
+ if (!O.DyLdInfoCommandIndex)
+ return;
+ const MachO::dyld_info_command &DyLdInfoCommand =
+ O.LoadCommands[*O.DyLdInfoCommandIndex]
+ .MachOLoadCommand.dyld_info_command_data;
+ char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.lazy_bind_off;
+ assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) &&
+ "Incorrect lazy bind opcodes size");
+ memcpy(Out, O.LazyBinds.Opcodes.data(), O.LazyBinds.Opcodes.size());
+}
+
+void MachOWriter::writeExportInfo() {
+ if (!O.DyLdInfoCommandIndex)
+ return;
+ const MachO::dyld_info_command &DyLdInfoCommand =
+ O.LoadCommands[*O.DyLdInfoCommandIndex]
+ .MachOLoadCommand.dyld_info_command_data;
+ char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.export_off;
+ assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) &&
+ "Incorrect export trie size");
+ memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size());
+}
+
+void MachOWriter::writeIndirectSymbolTable() {
+ if (!O.DySymTabCommandIndex)
+ return;
+
+ const MachO::dysymtab_command &DySymTabCommand =
+ O.LoadCommands[*O.DySymTabCommandIndex]
+ .MachOLoadCommand.dysymtab_command_data;
+
+ uint32_t *Out =
+ (uint32_t *)(Buf->getBufferStart() + DySymTabCommand.indirectsymoff);
+ for (const IndirectSymbolEntry &Sym : O.IndirectSymTable.Symbols) {
+ uint32_t Entry = (Sym.Symbol) ? (*Sym.Symbol)->Index : Sym.OriginalIndex;
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ sys::swapByteOrder(Entry);
+ *Out++ = Entry;
+ }
+}
+
+void MachOWriter::writeLinkData(Optional<size_t> LCIndex, const LinkData &LD) {
+ if (!LCIndex)
+ return;
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data;
+ char *Out = (char *)Buf->getBufferStart() + LinkEditDataCommand.dataoff;
+ assert((LinkEditDataCommand.datasize == LD.Data.size()) &&
+ "Incorrect data size");
+ memcpy(Out, LD.Data.data(), LD.Data.size());
+}
+
+static uint64_t
+getSegmentFileOffset(const LoadCommand &TextSegmentLoadCommand) {
+ const MachO::macho_load_command &MLC =
+ TextSegmentLoadCommand.MachOLoadCommand;
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ return MLC.segment_command_data.fileoff;
+ case MachO::LC_SEGMENT_64:
+ return MLC.segment_command_64_data.fileoff;
+ default:
+ return 0;
+ }
+}
+
+static uint64_t getSegmentFileSize(const LoadCommand &TextSegmentLoadCommand) {
+ const MachO::macho_load_command &MLC =
+ TextSegmentLoadCommand.MachOLoadCommand;
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ return MLC.segment_command_data.filesize;
+ case MachO::LC_SEGMENT_64:
+ return MLC.segment_command_64_data.filesize;
+ default:
+ return 0;
+ }
+}
+
+void MachOWriter::writeCodeSignatureData() {
+ // NOTE: This CodeSignature section behaviour must be kept in sync with that
+ // performed in LLD's CodeSignatureSection::write /
+ // CodeSignatureSection::writeHashes. Furthermore, this call must occur only
+ // after the rest of the binary has already been written to the buffer. This
+ // is because the buffer is read from to perform the necessary hashing.
+
+ // The CodeSignature section is the last section in the MachO binary and
+ // contains a hash of all content in the binary before it. Since llvm-objcopy
+ // has likely modified the target binary, the hash must be regenerated
+ // entirely. To generate this hash, we must read from the start of the binary
+ // (HashReadStart) to just before the start of the CodeSignature section
+ // (HashReadEnd).
+
+ const CodeSignatureInfo &CodeSignature = LayoutBuilder.getCodeSignature();
+
+ uint8_t *BufferStart = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
+ uint8_t *HashReadStart = BufferStart;
+ uint8_t *HashReadEnd = BufferStart + CodeSignature.StartOffset;
+
+ // The CodeSignature section begins with a header, after which the hashes
+ // of each page of the binary are written.
+ uint8_t *HashWriteStart = HashReadEnd + CodeSignature.AllHeadersSize;
+
+ uint32_t TextSegmentFileOff = 0;
+ uint32_t TextSegmentFileSize = 0;
+ if (O.TextSegmentCommandIndex) {
+ const LoadCommand &TextSegmentLoadCommand =
+ O.LoadCommands[*O.TextSegmentCommandIndex];
+ assert(TextSegmentLoadCommand.MachOLoadCommand.load_command_data.cmd ==
+ MachO::LC_SEGMENT ||
+ TextSegmentLoadCommand.MachOLoadCommand.load_command_data.cmd ==
+ MachO::LC_SEGMENT_64);
+ assert(StringRef(TextSegmentLoadCommand.MachOLoadCommand
+ .segment_command_data.segname) == "__TEXT");
+ TextSegmentFileOff = getSegmentFileOffset(TextSegmentLoadCommand);
+ TextSegmentFileSize = getSegmentFileSize(TextSegmentLoadCommand);
+ }
+
+ const uint32_t FileNamePad = CodeSignature.AllHeadersSize -
+ CodeSignature.FixedHeadersSize -
+ CodeSignature.OutputFileName.size();
+
+ // Write code section header.
+ auto *SuperBlob = reinterpret_cast<MachO::CS_SuperBlob *>(HashReadEnd);
+ write32be(&SuperBlob->magic, MachO::CSMAGIC_EMBEDDED_SIGNATURE);
+ write32be(&SuperBlob->length, CodeSignature.Size);
+ write32be(&SuperBlob->count, 1);
+ auto *BlobIndex = reinterpret_cast<MachO::CS_BlobIndex *>(&SuperBlob[1]);
+ write32be(&BlobIndex->type, MachO::CSSLOT_CODEDIRECTORY);
+ write32be(&BlobIndex->offset, CodeSignature.BlobHeadersSize);
+ auto *CodeDirectory = reinterpret_cast<MachO::CS_CodeDirectory *>(
+ HashReadEnd + CodeSignature.BlobHeadersSize);
+ write32be(&CodeDirectory->magic, MachO::CSMAGIC_CODEDIRECTORY);
+ write32be(&CodeDirectory->length,
+ CodeSignature.Size - CodeSignature.BlobHeadersSize);
+ write32be(&CodeDirectory->version, MachO::CS_SUPPORTSEXECSEG);
+ write32be(&CodeDirectory->flags, MachO::CS_ADHOC | MachO::CS_LINKER_SIGNED);
+ write32be(&CodeDirectory->hashOffset,
+ sizeof(MachO::CS_CodeDirectory) +
+ CodeSignature.OutputFileName.size() + FileNamePad);
+ write32be(&CodeDirectory->identOffset, sizeof(MachO::CS_CodeDirectory));
+ CodeDirectory->nSpecialSlots = 0;
+ write32be(&CodeDirectory->nCodeSlots, CodeSignature.BlockCount);
+ write32be(&CodeDirectory->codeLimit, CodeSignature.StartOffset);
+ CodeDirectory->hashSize = static_cast<uint8_t>(CodeSignature.HashSize);
+ CodeDirectory->hashType = MachO::kSecCodeSignatureHashSHA256;
+ CodeDirectory->platform = 0;
+ CodeDirectory->pageSize = CodeSignature.BlockSizeShift;
+ CodeDirectory->spare2 = 0;
+ CodeDirectory->scatterOffset = 0;
+ CodeDirectory->teamOffset = 0;
+ CodeDirectory->spare3 = 0;
+ CodeDirectory->codeLimit64 = 0;
+ write64be(&CodeDirectory->execSegBase, TextSegmentFileOff);
+ write64be(&CodeDirectory->execSegLimit, TextSegmentFileSize);
+ write64be(&CodeDirectory->execSegFlags, O.Header.FileType == MachO::MH_EXECUTE
+ ? MachO::CS_EXECSEG_MAIN_BINARY
+ : 0);
+
+ auto *Id = reinterpret_cast<char *>(&CodeDirectory[1]);
+ memcpy(Id, CodeSignature.OutputFileName.begin(),
+ CodeSignature.OutputFileName.size());
+ memset(Id + CodeSignature.OutputFileName.size(), 0, FileNamePad);
+
+ // Write the hashes.
+ uint8_t *CurrHashReadPosition = HashReadStart;
+ uint8_t *CurrHashWritePosition = HashWriteStart;
+ while (CurrHashReadPosition < HashReadEnd) {
+ StringRef Block(reinterpret_cast<char *>(CurrHashReadPosition),
+ std::min(HashReadEnd - CurrHashReadPosition,
+ static_cast<ssize_t>(CodeSignature.BlockSize)));
+ SHA256 Hasher;
+ Hasher.update(Block);
+ StringRef Hash = Hasher.final();
+ assert(Hash.size() == CodeSignature.HashSize);
+ memcpy(CurrHashWritePosition, Hash.data(), CodeSignature.HashSize);
+ CurrHashReadPosition += CodeSignature.BlockSize;
+ CurrHashWritePosition += CodeSignature.HashSize;
+ }
+#if defined(__APPLE__)
+ // This is macOS-specific work-around and makes no sense for any
+ // other host OS. See https://openradar.appspot.com/FB8914231
+ //
+ // The macOS kernel maintains a signature-verification cache to
+ // quickly validate applications at time of execve(2). The trouble
+ // is that for the kernel creates the cache entry at the time of the
+ // mmap(2) call, before we have a chance to write either the code to
+ // sign or the signature header+hashes. The fix is to invalidate
+ // all cached data associated with the output file, thus discarding
+ // the bogus prematurely-cached signature.
+ msync(BufferStart, CodeSignature.StartOffset + CodeSignature.Size,
+ MS_INVALIDATE);
+#endif
+}
+
+void MachOWriter::writeDataInCodeData() {
+ return writeLinkData(O.DataInCodeCommandIndex, O.DataInCode);
+}
+
+void MachOWriter::writeLinkerOptimizationHint() {
+ return writeLinkData(O.LinkerOptimizationHintCommandIndex,
+ O.LinkerOptimizationHint);
+}
+
+void MachOWriter::writeFunctionStartsData() {
+ return writeLinkData(O.FunctionStartsCommandIndex, O.FunctionStarts);
+}
+
+void MachOWriter::writeChainedFixupsData() {
+ return writeLinkData(O.ChainedFixupsCommandIndex, O.ChainedFixups);
+}
+
+void MachOWriter::writeExportsTrieData() {
+ return writeLinkData(O.ExportsTrieCommandIndex, O.ExportsTrie);
+}
+
+void MachOWriter::writeTail() {
+ typedef void (MachOWriter::*WriteHandlerType)();
+ typedef std::pair<uint64_t, WriteHandlerType> WriteOperation;
+ SmallVector<WriteOperation, 7> Queue;
+
+ if (O.SymTabCommandIndex) {
+ const MachO::symtab_command &SymTabCommand =
+ O.LoadCommands[*O.SymTabCommandIndex]
+ .MachOLoadCommand.symtab_command_data;
+ if (SymTabCommand.symoff)
+ Queue.push_back({SymTabCommand.symoff, &MachOWriter::writeSymbolTable});
+ if (SymTabCommand.stroff)
+ Queue.push_back({SymTabCommand.stroff, &MachOWriter::writeStringTable});
+ }
+
+ if (O.DyLdInfoCommandIndex) {
+ const MachO::dyld_info_command &DyLdInfoCommand =
+ O.LoadCommands[*O.DyLdInfoCommandIndex]
+ .MachOLoadCommand.dyld_info_command_data;
+ if (DyLdInfoCommand.rebase_off)
+ Queue.push_back(
+ {DyLdInfoCommand.rebase_off, &MachOWriter::writeRebaseInfo});
+ if (DyLdInfoCommand.bind_off)
+ Queue.push_back({DyLdInfoCommand.bind_off, &MachOWriter::writeBindInfo});
+ if (DyLdInfoCommand.weak_bind_off)
+ Queue.push_back(
+ {DyLdInfoCommand.weak_bind_off, &MachOWriter::writeWeakBindInfo});
+ if (DyLdInfoCommand.lazy_bind_off)
+ Queue.push_back(
+ {DyLdInfoCommand.lazy_bind_off, &MachOWriter::writeLazyBindInfo});
+ if (DyLdInfoCommand.export_off)
+ Queue.push_back(
+ {DyLdInfoCommand.export_off, &MachOWriter::writeExportInfo});
+ }
+
+ if (O.DySymTabCommandIndex) {
+ const MachO::dysymtab_command &DySymTabCommand =
+ O.LoadCommands[*O.DySymTabCommandIndex]
+ .MachOLoadCommand.dysymtab_command_data;
+
+ if (DySymTabCommand.indirectsymoff)
+ Queue.emplace_back(DySymTabCommand.indirectsymoff,
+ &MachOWriter::writeIndirectSymbolTable);
+ }
+
+ if (O.CodeSignatureCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.CodeSignatureCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Queue.emplace_back(LinkEditDataCommand.dataoff,
+ &MachOWriter::writeCodeSignatureData);
+ }
+
+ if (O.DataInCodeCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.DataInCodeCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Queue.emplace_back(LinkEditDataCommand.dataoff,
+ &MachOWriter::writeDataInCodeData);
+ }
+
+ if (O.LinkerOptimizationHintCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.LinkerOptimizationHintCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Queue.emplace_back(LinkEditDataCommand.dataoff,
+ &MachOWriter::writeLinkerOptimizationHint);
+ }
+
+ if (O.FunctionStartsCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.FunctionStartsCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Queue.emplace_back(LinkEditDataCommand.dataoff,
+ &MachOWriter::writeFunctionStartsData);
+ }
+
+ if (O.ChainedFixupsCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.ChainedFixupsCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Queue.emplace_back(LinkEditDataCommand.dataoff,
+ &MachOWriter::writeChainedFixupsData);
+ }
+
+ if (O.ExportsTrieCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.ExportsTrieCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Queue.emplace_back(LinkEditDataCommand.dataoff,
+ &MachOWriter::writeExportsTrieData);
+ }
+
+ llvm::sort(Queue, [](const WriteOperation &LHS, const WriteOperation &RHS) {
+ return LHS.first < RHS.first;
+ });
+
+ for (auto WriteOp : Queue)
+ (this->*WriteOp.second)();
+}
+
+Error MachOWriter::finalize() { return LayoutBuilder.layout(); }
+
+Error MachOWriter::write() {
+ size_t TotalSize = totalSize();
+ Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize);
+ if (!Buf)
+ return createStringError(errc::not_enough_memory,
+ "failed to allocate memory buffer of " +
+ Twine::utohexstr(TotalSize) + " bytes");
+ memset(Buf->getBufferStart(), 0, totalSize());
+ writeHeader();
+ writeLoadCommands();
+ writeSections();
+ writeTail();
+
+ // TODO: Implement direct writing to the output stream (without intermediate
+ // memory buffer Buf).
+ Out.write(Buf->getBufferStart(), Buf->getBufferSize());
+ return Error::success();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOWriter.h b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOWriter.h
new file mode 100644
index 00000000000..a172534dac8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/MachOWriter.h
@@ -0,0 +1,71 @@
+//===- MachOWriter.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachOLayoutBuilder.h"
+#include "MachOObjcopy.h"
+#include "Object.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Object/MachO.h"
+
+namespace llvm {
+class Error;
+
+namespace objcopy {
+namespace macho {
+
+class MachOWriter {
+ Object &O;
+ bool Is64Bit;
+ bool IsLittleEndian;
+ uint64_t PageSize;
+ std::unique_ptr<WritableMemoryBuffer> Buf;
+ raw_ostream &Out;
+ MachOLayoutBuilder LayoutBuilder;
+
+ size_t headerSize() const;
+ size_t loadCommandsSize() const;
+ size_t symTableSize() const;
+ size_t strTableSize() const;
+
+ void writeHeader();
+ void writeLoadCommands();
+ template <typename StructType>
+ void writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out);
+ void writeSections();
+ void writeSymbolTable();
+ void writeStringTable();
+ void writeRebaseInfo();
+ void writeBindInfo();
+ void writeWeakBindInfo();
+ void writeLazyBindInfo();
+ void writeExportInfo();
+ void writeIndirectSymbolTable();
+ void writeLinkData(Optional<size_t> LCIndex, const LinkData &LD);
+ void writeCodeSignatureData();
+ void writeDataInCodeData();
+ void writeLinkerOptimizationHint();
+ void writeFunctionStartsData();
+ void writeChainedFixupsData();
+ void writeExportsTrieData();
+ void writeTail();
+
+public:
+ MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian,
+ StringRef OutputFileName, uint64_t PageSize, raw_ostream &Out)
+ : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian),
+ PageSize(PageSize), Out(Out),
+ LayoutBuilder(O, Is64Bit, OutputFileName, PageSize) {}
+
+ size_t totalSize() const;
+ Error finalize();
+ Error write();
+};
+
+} // end namespace macho
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MachO/Object.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/Object.cpp
new file mode 100644
index 00000000000..6312adbbc9f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/Object.cpp
@@ -0,0 +1,214 @@
+//===- Object.cpp - Mach-O object file model --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Object.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include <unordered_set>
+
+using namespace llvm;
+using namespace llvm::objcopy::macho;
+
+const SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) const {
+ assert(Index < Symbols.size() && "invalid symbol index");
+ return Symbols[Index].get();
+}
+
+SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) {
+ return const_cast<SymbolEntry *>(
+ static_cast<const SymbolTable *>(this)->getSymbolByIndex(Index));
+}
+
+void SymbolTable::removeSymbols(
+ function_ref<bool(const std::unique_ptr<SymbolEntry> &)> ToRemove) {
+ llvm::erase_if(Symbols, ToRemove);
+}
+
+void Object::updateLoadCommandIndexes() {
+ static constexpr char TextSegmentName[] = "__TEXT";
+ // Update indices of special load commands
+ for (size_t Index = 0, Size = LoadCommands.size(); Index < Size; ++Index) {
+ LoadCommand &LC = LoadCommands[Index];
+ switch (LC.MachOLoadCommand.load_command_data.cmd) {
+ case MachO::LC_CODE_SIGNATURE:
+ CodeSignatureCommandIndex = Index;
+ break;
+ case MachO::LC_SEGMENT:
+ if (StringRef(LC.MachOLoadCommand.segment_command_data.segname) ==
+ TextSegmentName)
+ TextSegmentCommandIndex = Index;
+ break;
+ case MachO::LC_SEGMENT_64:
+ if (StringRef(LC.MachOLoadCommand.segment_command_64_data.segname) ==
+ TextSegmentName)
+ TextSegmentCommandIndex = Index;
+ break;
+ case MachO::LC_SYMTAB:
+ SymTabCommandIndex = Index;
+ break;
+ case MachO::LC_DYSYMTAB:
+ DySymTabCommandIndex = Index;
+ break;
+ case MachO::LC_DYLD_INFO:
+ case MachO::LC_DYLD_INFO_ONLY:
+ DyLdInfoCommandIndex = Index;
+ break;
+ case MachO::LC_DATA_IN_CODE:
+ DataInCodeCommandIndex = Index;
+ break;
+ case MachO::LC_LINKER_OPTIMIZATION_HINT:
+ LinkerOptimizationHintCommandIndex = Index;
+ break;
+ case MachO::LC_FUNCTION_STARTS:
+ FunctionStartsCommandIndex = Index;
+ break;
+ case MachO::LC_DYLD_CHAINED_FIXUPS:
+ ChainedFixupsCommandIndex = Index;
+ break;
+ case MachO::LC_DYLD_EXPORTS_TRIE:
+ ExportsTrieCommandIndex = Index;
+ break;
+ }
+ }
+}
+
+Error Object::removeLoadCommands(
+ function_ref<bool(const LoadCommand &)> ToRemove) {
+ auto It = std::stable_partition(
+ LoadCommands.begin(), LoadCommands.end(),
+ [&](const LoadCommand &LC) { return !ToRemove(LC); });
+ LoadCommands.erase(It, LoadCommands.end());
+
+ updateLoadCommandIndexes();
+ return Error::success();
+}
+
+Error Object::removeSections(
+ function_ref<bool(const std::unique_ptr<Section> &)> ToRemove) {
+ DenseMap<uint32_t, const Section *> OldIndexToSection;
+ uint32_t NextSectionIndex = 1;
+ for (LoadCommand &LC : LoadCommands) {
+ auto It = std::stable_partition(
+ std::begin(LC.Sections), std::end(LC.Sections),
+ [&](const std::unique_ptr<Section> &Sec) { return !ToRemove(Sec); });
+ for (auto I = LC.Sections.begin(), End = It; I != End; ++I) {
+ OldIndexToSection[(*I)->Index] = I->get();
+ (*I)->Index = NextSectionIndex++;
+ }
+ LC.Sections.erase(It, LC.Sections.end());
+ }
+
+ auto IsDead = [&](const std::unique_ptr<SymbolEntry> &S) -> bool {
+ Optional<uint32_t> Section = S->section();
+ return (Section && !OldIndexToSection.count(*Section));
+ };
+
+ SmallPtrSet<const SymbolEntry *, 2> DeadSymbols;
+ for (const std::unique_ptr<SymbolEntry> &Sym : SymTable.Symbols)
+ if (IsDead(Sym))
+ DeadSymbols.insert(Sym.get());
+
+ for (const LoadCommand &LC : LoadCommands)
+ for (const std::unique_ptr<Section> &Sec : LC.Sections)
+ for (const RelocationInfo &R : Sec->Relocations)
+ if (R.Symbol && *R.Symbol && DeadSymbols.count(*R.Symbol))
+ return createStringError(std::errc::invalid_argument,
+ "symbol '%s' defined in section with index "
+ "'%u' cannot be removed because it is "
+ "referenced by a relocation in section '%s'",
+ (*R.Symbol)->Name.c_str(),
+ *((*R.Symbol)->section()),
+ Sec->CanonicalName.c_str());
+ SymTable.removeSymbols(IsDead);
+ for (std::unique_ptr<SymbolEntry> &S : SymTable.Symbols)
+ if (S->section())
+ S->n_sect = OldIndexToSection[S->n_sect]->Index;
+ return Error::success();
+}
+
+uint64_t Object::nextAvailableSegmentAddress() const {
+ uint64_t HeaderSize =
+ is64Bit() ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
+ uint64_t Addr = HeaderSize + Header.SizeOfCmds;
+ for (const LoadCommand &LC : LoadCommands) {
+ const MachO::macho_load_command &MLC = LC.MachOLoadCommand;
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ Addr = std::max(Addr,
+ static_cast<uint64_t>(MLC.segment_command_data.vmaddr) +
+ MLC.segment_command_data.vmsize);
+ break;
+ case MachO::LC_SEGMENT_64:
+ Addr = std::max(Addr, MLC.segment_command_64_data.vmaddr +
+ MLC.segment_command_64_data.vmsize);
+ break;
+ default:
+ continue;
+ }
+ }
+ return Addr;
+}
+
+template <typename SegmentType>
+static void
+constructSegment(SegmentType &Seg, llvm::MachO::LoadCommandType CmdType,
+ StringRef SegName, uint64_t SegVMAddr, uint64_t SegVMSize) {
+ assert(SegName.size() <= sizeof(Seg.segname) && "too long segment name");
+ memset(&Seg, 0, sizeof(SegmentType));
+ Seg.cmd = CmdType;
+ strncpy(Seg.segname, SegName.data(), SegName.size());
+ Seg.maxprot |=
+ (MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE);
+ Seg.initprot |=
+ (MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE);
+ Seg.vmaddr = SegVMAddr;
+ Seg.vmsize = SegVMSize;
+}
+
+LoadCommand &Object::addSegment(StringRef SegName, uint64_t SegVMSize) {
+ LoadCommand LC;
+ const uint64_t SegVMAddr = nextAvailableSegmentAddress();
+ if (is64Bit())
+ constructSegment(LC.MachOLoadCommand.segment_command_64_data,
+ MachO::LC_SEGMENT_64, SegName, SegVMAddr, SegVMSize);
+ else
+ constructSegment(LC.MachOLoadCommand.segment_command_data,
+ MachO::LC_SEGMENT, SegName, SegVMAddr, SegVMSize);
+
+ LoadCommands.push_back(std::move(LC));
+ return LoadCommands.back();
+}
+
+/// Extracts a segment name from a string which is possibly non-null-terminated.
+static StringRef extractSegmentName(const char *SegName) {
+ return StringRef(SegName,
+ strnlen(SegName, sizeof(MachO::segment_command::segname)));
+}
+
+Optional<StringRef> LoadCommand::getSegmentName() const {
+ const MachO::macho_load_command &MLC = MachOLoadCommand;
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ return extractSegmentName(MLC.segment_command_data.segname);
+ case MachO::LC_SEGMENT_64:
+ return extractSegmentName(MLC.segment_command_64_data.segname);
+ default:
+ return None;
+ }
+}
+
+Optional<uint64_t> LoadCommand::getSegmentVMAddr() const {
+ const MachO::macho_load_command &MLC = MachOLoadCommand;
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ return MLC.segment_command_data.vmaddr;
+ case MachO::LC_SEGMENT_64:
+ return MLC.segment_command_64_data.vmaddr;
+ default:
+ return None;
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MachO/Object.h b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/Object.h
new file mode 100644
index 00000000000..13aaf42634b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MachO/Object.h
@@ -0,0 +1,374 @@
+//===- Object.h - Mach-O object file model ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJCOPY_MACHO_OBJECT_H
+#define LLVM_OBJCOPY_MACHO_OBJECT_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/ObjectYAML/DWARFYAML.h"
+#include "llvm/Support/StringSaver.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+namespace macho {
+
+struct MachHeader {
+ uint32_t Magic;
+ uint32_t CPUType;
+ uint32_t CPUSubType;
+ uint32_t FileType;
+ uint32_t NCmds;
+ uint32_t SizeOfCmds;
+ uint32_t Flags;
+ uint32_t Reserved = 0;
+};
+
+struct RelocationInfo;
+struct Section {
+ uint32_t Index;
+ std::string Segname;
+ std::string Sectname;
+ // CanonicalName is a string formatted as “<Segname>,<Sectname>".
+ std::string CanonicalName;
+ uint64_t Addr = 0;
+ uint64_t Size = 0;
+ // Offset in the input file.
+ Optional<uint32_t> OriginalOffset;
+ uint32_t Offset = 0;
+ uint32_t Align = 0;
+ uint32_t RelOff = 0;
+ uint32_t NReloc = 0;
+ uint32_t Flags = 0;
+ uint32_t Reserved1 = 0;
+ uint32_t Reserved2 = 0;
+ uint32_t Reserved3 = 0;
+ StringRef Content;
+ std::vector<RelocationInfo> Relocations;
+
+ Section(StringRef SegName, StringRef SectName)
+ : Segname(std::string(SegName)), Sectname(std::string(SectName)),
+ CanonicalName((Twine(SegName) + Twine(',') + SectName).str()) {}
+
+ Section(StringRef SegName, StringRef SectName, StringRef Content)
+ : Segname(std::string(SegName)), Sectname(std::string(SectName)),
+ CanonicalName((Twine(SegName) + Twine(',') + SectName).str()),
+ Content(Content) {}
+
+ MachO::SectionType getType() const {
+ return static_cast<MachO::SectionType>(Flags & MachO::SECTION_TYPE);
+ }
+
+ bool isVirtualSection() const {
+ return (getType() == MachO::S_ZEROFILL ||
+ getType() == MachO::S_GB_ZEROFILL ||
+ getType() == MachO::S_THREAD_LOCAL_ZEROFILL);
+ }
+
+ bool hasValidOffset() const {
+ return !(isVirtualSection() || (OriginalOffset && *OriginalOffset == 0));
+ }
+};
+
+struct LoadCommand {
+ // The type MachO::macho_load_command is defined in llvm/BinaryFormat/MachO.h
+ // and it is a union of all the structs corresponding to various load
+ // commands.
+ MachO::macho_load_command MachOLoadCommand;
+
+ // The raw content of the payload of the load command (located right after the
+ // corresponding struct). In some cases it is either empty or can be
+ // copied-over without digging into its structure.
+ std::vector<uint8_t> Payload;
+
+ // Some load commands can contain (inside the payload) an array of sections,
+ // though the contents of the sections are stored separately. The struct
+ // Section describes only sections' metadata and where to find the
+ // corresponding content inside the binary.
+ std::vector<std::unique_ptr<Section>> Sections;
+
+ // Returns the segment name if the load command is a segment command.
+ Optional<StringRef> getSegmentName() const;
+
+ // Returns the segment vm address if the load command is a segment command.
+ Optional<uint64_t> getSegmentVMAddr() const;
+};
+
+// A symbol information. Fields which starts with "n_" are same as them in the
+// nlist.
+struct SymbolEntry {
+ std::string Name;
+ bool Referenced = false;
+ uint32_t Index;
+ uint8_t n_type;
+ uint8_t n_sect;
+ uint16_t n_desc;
+ uint64_t n_value;
+
+ bool isExternalSymbol() const { return n_type & MachO::N_EXT; }
+
+ bool isLocalSymbol() const { return !isExternalSymbol(); }
+
+ bool isUndefinedSymbol() const {
+ return (n_type & MachO::N_TYPE) == MachO::N_UNDF;
+ }
+
+ bool isSwiftSymbol() const {
+ return StringRef(Name).startswith("_$s") ||
+ StringRef(Name).startswith("_$S");
+ }
+
+ Optional<uint32_t> section() const {
+ return n_sect == MachO::NO_SECT ? None : Optional<uint32_t>(n_sect);
+ }
+};
+
+/// The location of the symbol table inside the binary is described by LC_SYMTAB
+/// load command.
+struct SymbolTable {
+ std::vector<std::unique_ptr<SymbolEntry>> Symbols;
+
+ using iterator = pointee_iterator<
+ std::vector<std::unique_ptr<SymbolEntry>>::const_iterator>;
+
+ iterator begin() const { return iterator(Symbols.begin()); }
+ iterator end() const { return iterator(Symbols.end()); }
+
+ const SymbolEntry *getSymbolByIndex(uint32_t Index) const;
+ SymbolEntry *getSymbolByIndex(uint32_t Index);
+ void removeSymbols(
+ function_ref<bool(const std::unique_ptr<SymbolEntry> &)> ToRemove);
+};
+
+struct IndirectSymbolEntry {
+ // The original value in an indirect symbol table. Higher bits encode extra
+ // information (INDIRECT_SYMBOL_LOCAL and INDIRECT_SYMBOL_ABS).
+ uint32_t OriginalIndex;
+ /// The Symbol referenced by this entry. It's None if the index is
+ /// INDIRECT_SYMBOL_LOCAL or INDIRECT_SYMBOL_ABS.
+ Optional<SymbolEntry *> Symbol;
+
+ IndirectSymbolEntry(uint32_t OriginalIndex, Optional<SymbolEntry *> Symbol)
+ : OriginalIndex(OriginalIndex), Symbol(Symbol) {}
+};
+
+struct IndirectSymbolTable {
+ std::vector<IndirectSymbolEntry> Symbols;
+};
+
+/// The location of the string table inside the binary is described by LC_SYMTAB
+/// load command.
+struct StringTable {
+ std::vector<std::string> Strings;
+};
+
+struct RelocationInfo {
+ // The referenced symbol entry. Set if !Scattered && Extern.
+ Optional<const SymbolEntry *> Symbol;
+ // The referenced section. Set if !Scattered && !Extern.
+ Optional<const Section *> Sec;
+ // True if Info is a scattered_relocation_info.
+ bool Scattered;
+ // True if the type is an ADDEND. r_symbolnum holds the addend instead of a
+ // symbol index.
+ bool IsAddend;
+ // True if the r_symbolnum points to a section number (i.e. r_extern=0).
+ bool Extern;
+ MachO::any_relocation_info Info;
+
+ unsigned getPlainRelocationSymbolNum(bool IsLittleEndian) {
+ if (IsLittleEndian)
+ return Info.r_word1 & 0xffffff;
+ return Info.r_word1 >> 8;
+ }
+
+ void setPlainRelocationSymbolNum(unsigned SymbolNum, bool IsLittleEndian) {
+ assert(SymbolNum < (1 << 24) && "SymbolNum out of range");
+ if (IsLittleEndian)
+ Info.r_word1 = (Info.r_word1 & ~0x00ffffff) | SymbolNum;
+ else
+ Info.r_word1 = (Info.r_word1 & ~0xffffff00) | (SymbolNum << 8);
+ }
+};
+
+/// The location of the rebase info inside the binary is described by
+/// LC_DYLD_INFO load command. Dyld rebases an image whenever dyld loads it at
+/// an address different from its preferred address. The rebase information is
+/// a stream of byte sized opcodes whose symbolic names start with
+/// REBASE_OPCODE_. Conceptually the rebase information is a table of tuples:
+/// <seg-index, seg-offset, type>
+/// The opcodes are a compressed way to encode the table by only
+/// encoding when a column changes. In addition simple patterns
+/// like "every n'th offset for m times" can be encoded in a few
+/// bytes.
+struct RebaseInfo {
+ // At the moment we do not parse this info (and it is simply copied over),
+ // but the proper support will be added later.
+ ArrayRef<uint8_t> Opcodes;
+};
+
+/// The location of the bind info inside the binary is described by
+/// LC_DYLD_INFO load command. Dyld binds an image during the loading process,
+/// if the image requires any pointers to be initialized to symbols in other
+/// images. The bind information is a stream of byte sized opcodes whose
+/// symbolic names start with BIND_OPCODE_. Conceptually the bind information is
+/// a table of tuples: <seg-index, seg-offset, type, symbol-library-ordinal,
+/// symbol-name, addend> The opcodes are a compressed way to encode the table by
+/// only encoding when a column changes. In addition simple patterns like for
+/// runs of pointers initialized to the same value can be encoded in a few
+/// bytes.
+struct BindInfo {
+ // At the moment we do not parse this info (and it is simply copied over),
+ // but the proper support will be added later.
+ ArrayRef<uint8_t> Opcodes;
+};
+
+/// The location of the weak bind info inside the binary is described by
+/// LC_DYLD_INFO load command. Some C++ programs require dyld to unique symbols
+/// so that all images in the process use the same copy of some code/data. This
+/// step is done after binding. The content of the weak_bind info is an opcode
+/// stream like the bind_info. But it is sorted alphabetically by symbol name.
+/// This enable dyld to walk all images with weak binding information in order
+/// and look for collisions. If there are no collisions, dyld does no updating.
+/// That means that some fixups are also encoded in the bind_info. For
+/// instance, all calls to "operator new" are first bound to libstdc++.dylib
+/// using the information in bind_info. Then if some image overrides operator
+/// new that is detected when the weak_bind information is processed and the
+/// call to operator new is then rebound.
+struct WeakBindInfo {
+ // At the moment we do not parse this info (and it is simply copied over),
+ // but the proper support will be added later.
+ ArrayRef<uint8_t> Opcodes;
+};
+
+/// The location of the lazy bind info inside the binary is described by
+/// LC_DYLD_INFO load command. Some uses of external symbols do not need to be
+/// bound immediately. Instead they can be lazily bound on first use. The
+/// lazy_bind contains a stream of BIND opcodes to bind all lazy symbols. Normal
+/// use is that dyld ignores the lazy_bind section when loading an image.
+/// Instead the static linker arranged for the lazy pointer to initially point
+/// to a helper function which pushes the offset into the lazy_bind area for the
+/// symbol needing to be bound, then jumps to dyld which simply adds the offset
+/// to lazy_bind_off to get the information on what to bind.
+struct LazyBindInfo {
+ ArrayRef<uint8_t> Opcodes;
+};
+
+/// The location of the export info inside the binary is described by
+/// LC_DYLD_INFO load command. The symbols exported by a dylib are encoded in a
+/// trie. This is a compact representation that factors out common prefixes. It
+/// also reduces LINKEDIT pages in RAM because it encodes all information (name,
+/// address, flags) in one small, contiguous range. The export area is a stream
+/// of nodes. The first node sequentially is the start node for the trie. Nodes
+/// for a symbol start with a uleb128 that is the length of the exported symbol
+/// information for the string so far. If there is no exported symbol, the node
+/// starts with a zero byte. If there is exported info, it follows the length.
+/// First is a uleb128 containing flags. Normally, it is followed by
+/// a uleb128 encoded offset which is location of the content named
+/// by the symbol from the mach_header for the image. If the flags
+/// is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags is
+/// a uleb128 encoded library ordinal, then a zero terminated
+/// UTF8 string. If the string is zero length, then the symbol
+/// is re-export from the specified dylib with the same name.
+/// If the flags is EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER, then following
+/// the flags is two uleb128s: the stub offset and the resolver offset.
+/// The stub is used by non-lazy pointers. The resolver is used
+/// by lazy pointers and must be called to get the actual address to use.
+/// After the optional exported symbol information is a byte of
+/// how many edges (0-255) that this node has leaving it,
+/// followed by each edge.
+/// Each edge is a zero terminated UTF8 of the addition chars
+/// in the symbol, followed by a uleb128 offset for the node that
+/// edge points to.
+struct ExportInfo {
+ ArrayRef<uint8_t> Trie;
+};
+
+struct LinkData {
+ ArrayRef<uint8_t> Data;
+};
+
+struct Object {
+ MachHeader Header;
+ std::vector<LoadCommand> LoadCommands;
+
+ SymbolTable SymTable;
+ StringTable StrTable;
+
+ RebaseInfo Rebases;
+ BindInfo Binds;
+ WeakBindInfo WeakBinds;
+ LazyBindInfo LazyBinds;
+ ExportInfo Exports;
+ IndirectSymbolTable IndirectSymTable;
+ LinkData DataInCode;
+ LinkData LinkerOptimizationHint;
+ LinkData FunctionStarts;
+ LinkData ExportsTrie;
+ LinkData ChainedFixups;
+
+ Optional<uint32_t> SwiftVersion;
+
+ /// The index of LC_CODE_SIGNATURE load command if present.
+ Optional<size_t> CodeSignatureCommandIndex;
+ /// The index of LC_SYMTAB load command if present.
+ Optional<size_t> SymTabCommandIndex;
+ /// The index of LC_DYLD_INFO or LC_DYLD_INFO_ONLY load command if present.
+ Optional<size_t> DyLdInfoCommandIndex;
+ /// The index LC_DYSYMTAB load command if present.
+ Optional<size_t> DySymTabCommandIndex;
+ /// The index LC_DATA_IN_CODE load command if present.
+ Optional<size_t> DataInCodeCommandIndex;
+ /// The index of LC_LINKER_OPTIMIZATIN_HINT load command if present.
+ Optional<size_t> LinkerOptimizationHintCommandIndex;
+ /// The index LC_FUNCTION_STARTS load command if present.
+ Optional<size_t> FunctionStartsCommandIndex;
+ /// The index LC_DYLD_CHAINED_FIXUPS load command if present.
+ Optional<size_t> ChainedFixupsCommandIndex;
+ /// The index LC_DYLD_EXPORTS_TRIE load command if present.
+ Optional<size_t> ExportsTrieCommandIndex;
+ /// The index of the LC_SEGMENT or LC_SEGMENT_64 load command
+ /// corresponding to the __TEXT segment.
+ Optional<size_t> TextSegmentCommandIndex;
+
+ BumpPtrAllocator Alloc;
+ StringSaver NewSectionsContents;
+
+ Object() : NewSectionsContents(Alloc) {}
+
+ Error
+ removeSections(function_ref<bool(const std::unique_ptr<Section> &)> ToRemove);
+
+ Error removeLoadCommands(function_ref<bool(const LoadCommand &)> ToRemove);
+
+ void updateLoadCommandIndexes();
+
+ /// Creates a new segment load command in the object and returns a reference
+ /// to the newly created load command. The caller should verify that SegName
+ /// is not too long (SegName.size() should be less than or equal to 16).
+ LoadCommand &addSegment(StringRef SegName, uint64_t SegVMSize);
+
+ bool is64Bit() const {
+ return Header.Magic == MachO::MH_MAGIC_64 ||
+ Header.Magic == MachO::MH_CIGAM_64;
+ }
+
+ uint64_t nextAvailableSegmentAddress() const;
+};
+
+} // end namespace macho
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_OBJCOPY_MACHO_OBJECT_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/MultiFormatConfig.h b/contrib/libs/llvm14/tools/llvm-objcopy/MultiFormatConfig.h
new file mode 100644
index 00000000000..31d9883d6d3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/MultiFormatConfig.h
@@ -0,0 +1,37 @@
+//===- MultiFormatConfig.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H
+
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace objcopy {
+
+struct CommonConfig;
+struct ELFConfig;
+struct COFFConfig;
+struct MachOConfig;
+struct WasmConfig;
+
+class MultiFormatConfig {
+public:
+ virtual ~MultiFormatConfig() {}
+
+ virtual const CommonConfig &getCommonConfig() const = 0;
+ virtual Expected<const ELFConfig &> getELFConfig() const = 0;
+ virtual Expected<const COFFConfig &> getCOFFConfig() const = 0;
+ virtual Expected<const MachOConfig &> getMachOConfig() const = 0;
+ virtual Expected<const WasmConfig &> getWasmConfig() const = 0;
+};
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/ObjcopyOpts.td b/contrib/libs/llvm14/tools/llvm-objcopy/ObjcopyOpts.td
new file mode 100644
index 00000000000..bfd66caf41e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/ObjcopyOpts.td
@@ -0,0 +1,226 @@
+include "CommonOpts.td"
+
+defm binary_architecture
+ : Eq<"binary-architecture", "Ignored for compatibility">;
+def B : JoinedOrSeparate<["-"], "B">,
+ Alias<binary_architecture>,
+ HelpText<"Alias for --binary-architecture">;
+
+defm target : Eq<"target", "Format of the input and output file">,
+ Values<"binary">;
+def F : JoinedOrSeparate<["-"], "F">,
+ Alias<target>,
+ HelpText<"Alias for --target">;
+
+defm input_target : Eq<"input-target", "Format of the input file">,
+ Values<"binary">;
+def I : JoinedOrSeparate<["-"], "I">,
+ Alias<input_target>,
+ HelpText<"Alias for --input-target">;
+
+defm output_target : Eq<"output-target", "Format of the output file">,
+ Values<"binary">;
+def O : JoinedOrSeparate<["-"], "O">,
+ Alias<output_target>,
+ HelpText<"Alias for --output-target">;
+
+defm new_symbol_visibility : Eq<"new-symbol-visibility", "Visibility of "
+ "symbols generated for binary input or added"
+ " with --add-symbol unless otherwise"
+ " specified. The default value is 'default'.">;
+
+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">;
+
+defm add_gnu_debuglink
+ : Eq<"add-gnu-debuglink", "Add a .gnu_debuglink for <debug-file>">,
+ MetaVarName<"debug-file">;
+
+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, exclude, 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 redefine_symbols
+ : Eq<"redefine-syms",
+ "Reads a list of symbol pairs from <filename> and runs as if "
+ "--redefine-sym=<old>=<new> is set for each one. <filename> "
+ "contains two symbols per line separated with whitespace 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 only_section : Eq<"only-section", "Remove all but <section>">,
+ MetaVarName<"section">;
+def j : JoinedOrSeparate<["-"], "j">,
+ Alias<only_section>,
+ HelpText<"Alias for --only-section">;
+defm add_section
+ : Eq<"add-section",
+ "Make a section named <section> with the contents of <file>.">,
+ MetaVarName<"section=file">;
+
+defm set_section_alignment
+ : Eq<"set-section-alignment", "Set alignment for a given section.">,
+ MetaVarName<"section=align">;
+
+defm set_section_flags
+ : Eq<"set-section-flags",
+ "Set section flags for a given section. Flags supported for GNU "
+ "compatibility: alloc, load, noload, readonly, exclude, debug, code, "
+ "data, rom, share, contents, merge, strings.">,
+ MetaVarName<"section=flag1[,flag2,...]">;
+
+def S : Flag<["-"], "S">,
+ Alias<strip_all>,
+ HelpText<"Alias for --strip-all">;
+def strip_dwo : Flag<["--"], "strip-dwo">,
+ HelpText<"Remove all DWARF .dwo sections from file">;
+def strip_non_alloc
+ : Flag<["--"], "strip-non-alloc">,
+ HelpText<"Remove all non-allocated sections outside segments">;
+defm strip_unneeded_symbol
+ : Eq<"strip-unneeded-symbol",
+ "Remove symbol <symbol> if it is not needed by relocations">,
+ MetaVarName<"symbol">;
+defm strip_unneeded_symbols
+ : Eq<"strip-unneeded-symbols",
+ "Reads a list of symbols from <filename> and removes them "
+ "if they are not needed by relocations">,
+ MetaVarName<"filename">;
+
+defm subsystem
+ : Eq<"subsystem",
+ "Set PE subsystem and version">,
+ MetaVarName<"name[:version]">;
+
+def extract_dwo
+ : Flag<["--"], "extract-dwo">,
+ HelpText<
+ "Remove all sections that are not DWARF .dwo sections from file">;
+
+defm extract_partition
+ : Eq<"extract-partition", "Extract named partition from input file">,
+ MetaVarName<"name">;
+def extract_main_partition
+ : Flag<["--"], "extract-main-partition">,
+ HelpText<"Extract main partition from the input 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">;
+defm localize_symbols
+ : Eq<"localize-symbols",
+ "Reads a list of symbols from <filename> and marks them local.">,
+ MetaVarName<"filename">;
+
+def L : JoinedOrSeparate<["-"], "L">,
+ Alias<localize_symbol>,
+ HelpText<"Alias for --localize-symbol">;
+
+defm globalize_symbol : Eq<"globalize-symbol", "Mark <symbol> as global">,
+ MetaVarName<"symbol">;
+
+defm globalize_symbols
+ : Eq<"globalize-symbols",
+ "Reads a list of symbols from <filename> and marks them global.">,
+ MetaVarName<"filename">;
+
+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>,
+ HelpText<"Alias for --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">;
+defm weaken_symbols
+ : Eq<"weaken-symbols",
+ "Reads a list of symbols from <filename> and marks them weak.">,
+ MetaVarName<"filename">;
+
+def W : JoinedOrSeparate<["-"], "W">,
+ Alias<weaken_symbol>,
+ HelpText<"Alias for --weaken-symbol">;
+def weaken : Flag<["--"], "weaken">,
+ HelpText<"Mark all global symbols as weak">;
+
+defm strip_symbols
+ : Eq<"strip-symbols",
+ "Reads a list of symbols from <filename> and removes them.">,
+ MetaVarName<"filename">;
+
+defm keep_symbols
+ : Eq<"keep-symbols",
+ "Reads a list of symbols from <filename> and runs as if "
+ "--keep-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 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">;
+
+defm prefix_alloc_sections
+ : Eq<"prefix-alloc-sections", "Add <prefix> to the start of every allocated section name">,
+ MetaVarName<"prefix">;
+
+defm set_start : Eq<"set-start", "Set the start address to <addr>. Overrides "
+ "any previous --change-start or --adjust-start values.">,
+ MetaVarName<"addr">;
+defm change_start : Eq<"change-start", "Add <incr> to the start address. Can be "
+ "specified multiple times, all values will be applied "
+ "cumulatively.">,
+ MetaVarName<"incr">;
+def adjust_start : JoinedOrSeparate<["--"], "adjust-start">,
+ Alias<change_start>,
+ HelpText<"Alias for --change-start">;
+
+defm add_symbol
+ : Eq<"add-symbol", "Add new symbol <name> to .symtab. Accepted flags: "
+ "global, local, weak, default, hidden, protected, file, section, object, "
+ "function, indirect-function. Accepted but ignored for "
+ "compatibility: debug, constructor, warning, indirect, synthetic, "
+ "unique-object, before.">,
+ MetaVarName<"name=[section:]value[,flags]">;
+
+defm update_section
+ : Eq<"update-section", "Add section <name> with contents from a file <file>.">,
+ MetaVarName<"name=file">;
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/StripOpts.td b/contrib/libs/llvm14/tools/llvm-objcopy/StripOpts.td
new file mode 100644
index 00000000000..001da23528d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/StripOpts.td
@@ -0,0 +1,20 @@
+include "CommonOpts.td"
+
+def output : JoinedOrSeparate<["-"], "o">, HelpText<"Write output to <file>">,
+ MetaVarName<"<file>">;
+
+def s : Flag<["-"], "s">,
+ Alias<strip_all>,
+ HelpText<"Alias for --strip-all">;
+def no_strip_all : Flag<["--"], "no-strip-all">,
+ HelpText<"Disable --strip-all">;
+
+def d : Flag<["-"], "d">,
+ Alias<strip_debug>,
+ HelpText<"Alias for --strip-debug">;
+def S : Flag<["-"], "S">,
+ Alias<strip_debug>,
+ HelpText<"Alias for --strip-debug">;
+
+def strip_swift_symbols : Flag<["-"], "T">,
+ HelpText<"Remove Swift symbols">;
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/llvm-objcopy.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/llvm-objcopy.cpp
new file mode 100644
index 00000000000..a5963985f78
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -0,0 +1,438 @@
+//===- llvm-objcopy.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-objcopy.h"
+#include "COFF/COFFConfig.h"
+#include "COFF/COFFObjcopy.h"
+#include "CommonConfig.h"
+#include "ConfigManager.h"
+#include "ELF/ELFConfig.h"
+#include "ELF/ELFObjcopy.h"
+#include "MachO/MachOConfig.h"
+#include "MachO/MachOObjcopy.h"
+#include "wasm/WasmConfig.h"
+#include "wasm/WasmObjcopy.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"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Memory.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/SmallVectorMemoryBuffer.h"
+#include "llvm/Support/StringSaver.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdlib>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <utility>
+
+using namespace llvm;
+using namespace llvm::objcopy;
+using namespace llvm::object;
+
+// The name this program was invoked as.
+static StringRef ToolName;
+
+static ErrorSuccess reportWarning(Error E) {
+ assert(E);
+ WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n';
+ return Error::success();
+}
+
+static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) {
+ StringRef Stem = sys::path::stem(ToolName);
+ auto Is = [=](StringRef Tool) {
+ // We need to recognize the following filenames:
+ //
+ // llvm-objcopy -> objcopy
+ // strip-10.exe -> strip
+ // powerpc64-unknown-freebsd13-objcopy -> objcopy
+ // llvm-install-name-tool -> install-name-tool
+ auto I = Stem.rfind_insensitive(Tool);
+ return I != StringRef::npos &&
+ (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()]));
+ };
+
+ if (Is("bitcode-strip") || Is("bitcode_strip"))
+ return parseBitcodeStripOptions(Args);
+ else if (Is("strip"))
+ return parseStripOptions(Args, reportWarning);
+ else if (Is("install-name-tool") || Is("install_name_tool"))
+ return parseInstallNameToolOptions(Args);
+ else
+ return parseObjcopyOptions(Args, reportWarning);
+}
+
+// For regular archives this function simply calls llvm::writeArchive,
+// For thin archives it writes the archive file itself as well as its members.
+static Error deepWriteArchive(StringRef ArcName,
+ ArrayRef<NewArchiveMember> NewMembers,
+ bool WriteSymtab, object::Archive::Kind Kind,
+ bool Deterministic, bool Thin) {
+ if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind,
+ Deterministic, Thin))
+ return createFileError(ArcName, std::move(E));
+
+ if (!Thin)
+ return Error::success();
+
+ for (const NewArchiveMember &Member : NewMembers) {
+ // For regular files (as is the case for deepWriteArchive),
+ // FileOutputBuffer::create will return OnDiskBuffer.
+ // OnDiskBuffer uses a temporary file and then renames it. So in reality
+ // there is no inefficiency / duplicated in-memory buffers in this case. For
+ // now in-memory buffers can not be completely avoided since
+ // NewArchiveMember still requires them even though writeArchive does not
+ // write them on disk.
+ Expected<std::unique_ptr<FileOutputBuffer>> FB =
+ FileOutputBuffer::create(Member.MemberName, Member.Buf->getBufferSize(),
+ FileOutputBuffer::F_executable);
+ if (!FB)
+ return FB.takeError();
+ std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(),
+ (*FB)->getBufferStart());
+ if (Error E = (*FB)->commit())
+ return E;
+ }
+ return Error::success();
+}
+
+/// The function executeObjcopyOnIHex does the dispatch based on the format
+/// of the output specified by the command line options.
+static Error executeObjcopyOnIHex(ConfigManager &ConfigMgr, MemoryBuffer &In,
+ raw_ostream &Out) {
+ // TODO: support output formats other than ELF.
+ Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig();
+ if (!ELFConfig)
+ return ELFConfig.takeError();
+
+ return elf::executeObjcopyOnIHex(ConfigMgr.getCommonConfig(), *ELFConfig, In,
+ Out);
+}
+
+/// The function executeObjcopyOnRawBinary does the dispatch based on the format
+/// of the output specified by the command line options.
+static Error executeObjcopyOnRawBinary(ConfigManager &ConfigMgr,
+ MemoryBuffer &In, raw_ostream &Out) {
+ const CommonConfig &Config = ConfigMgr.getCommonConfig();
+ switch (Config.OutputFormat) {
+ case FileFormat::ELF:
+ // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the
+ // output format is binary/ihex or it's not given. This behavior differs from
+ // GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details.
+ case FileFormat::Binary:
+ case FileFormat::IHex:
+ case FileFormat::Unspecified:
+ Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig();
+ if (!ELFConfig)
+ return ELFConfig.takeError();
+
+ return elf::executeObjcopyOnRawBinary(Config, *ELFConfig, In, Out);
+ }
+
+ llvm_unreachable("unsupported output format");
+}
+
+/// The function executeObjcopyOnBinary does the dispatch based on the format
+/// of the input binary (ELF, MachO or COFF).
+static Error executeObjcopyOnBinary(const MultiFormatConfig &Config,
+ object::Binary &In, raw_ostream &Out) {
+ if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) {
+ Expected<const ELFConfig &> ELFConfig = Config.getELFConfig();
+ if (!ELFConfig)
+ return ELFConfig.takeError();
+
+ return elf::executeObjcopyOnBinary(Config.getCommonConfig(), *ELFConfig,
+ *ELFBinary, Out);
+ } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) {
+ Expected<const COFFConfig &> COFFConfig = Config.getCOFFConfig();
+ if (!COFFConfig)
+ return COFFConfig.takeError();
+
+ return coff::executeObjcopyOnBinary(Config.getCommonConfig(), *COFFConfig,
+ *COFFBinary, Out);
+ } else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) {
+ Expected<const MachOConfig &> MachOConfig = Config.getMachOConfig();
+ if (!MachOConfig)
+ return MachOConfig.takeError();
+
+ return macho::executeObjcopyOnBinary(Config.getCommonConfig(), *MachOConfig,
+ *MachOBinary, Out);
+ } else if (auto *MachOUniversalBinary =
+ dyn_cast<object::MachOUniversalBinary>(&In)) {
+ return macho::executeObjcopyOnMachOUniversalBinary(
+ Config, *MachOUniversalBinary, Out);
+ } else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In)) {
+ Expected<const WasmConfig &> WasmConfig = Config.getWasmConfig();
+ if (!WasmConfig)
+ return WasmConfig.takeError();
+
+ return objcopy::wasm::executeObjcopyOnBinary(Config.getCommonConfig(),
+ *WasmConfig, *WasmBinary, Out);
+ } else
+ return createStringError(object_error::invalid_file_type,
+ "unsupported object file format");
+}
+
+namespace llvm {
+namespace objcopy {
+
+Expected<std::vector<NewArchiveMember>>
+createNewArchiveMembers(const MultiFormatConfig &Config, const Archive &Ar) {
+ std::vector<NewArchiveMember> NewArchiveMembers;
+ Error Err = Error::success();
+ for (const Archive::Child &Child : Ar.children(Err)) {
+ Expected<StringRef> ChildNameOrErr = Child.getName();
+ if (!ChildNameOrErr)
+ return createFileError(Ar.getFileName(), ChildNameOrErr.takeError());
+
+ Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
+ if (!ChildOrErr)
+ return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")",
+ ChildOrErr.takeError());
+
+ SmallVector<char, 0> Buffer;
+ raw_svector_ostream MemStream(Buffer);
+
+ if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MemStream))
+ return std::move(E);
+
+ Expected<NewArchiveMember> Member = NewArchiveMember::getOldMember(
+ Child, Config.getCommonConfig().DeterministicArchives);
+ if (!Member)
+ return createFileError(Ar.getFileName(), Member.takeError());
+
+ Member->Buf = std::make_unique<SmallVectorMemoryBuffer>(
+ std::move(Buffer), ChildNameOrErr.get(),
+ /*RequiresNullTerminator=*/false);
+ Member->MemberName = Member->Buf->getBufferIdentifier();
+ NewArchiveMembers.push_back(std::move(*Member));
+ }
+ if (Err)
+ return createFileError(Config.getCommonConfig().InputFilename,
+ std::move(Err));
+ return std::move(NewArchiveMembers);
+}
+
+} // end namespace objcopy
+} // end namespace llvm
+
+static Error executeObjcopyOnArchive(const ConfigManager &ConfigMgr,
+ const object::Archive &Ar) {
+ Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr =
+ createNewArchiveMembers(ConfigMgr, Ar);
+ if (!NewArchiveMembersOrErr)
+ return NewArchiveMembersOrErr.takeError();
+ const CommonConfig &Config = ConfigMgr.getCommonConfig();
+ return deepWriteArchive(Config.OutputFilename, *NewArchiveMembersOrErr,
+ Ar.hasSymbolTable(), Ar.kind(),
+ Config.DeterministicArchives, Ar.isThin());
+}
+
+static Error restoreStatOnFile(StringRef Filename,
+ const sys::fs::file_status &Stat,
+ const ConfigManager &ConfigMgr) {
+ int FD;
+ const CommonConfig &Config = ConfigMgr.getCommonConfig();
+
+ // Writing to stdout should not be treated as an error here, just
+ // do not set access/modification times or permissions.
+ if (Filename == "-")
+ return Error::success();
+
+ if (auto EC =
+ sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting))
+ return createFileError(Filename, EC);
+
+ if (Config.PreserveDates)
+ if (auto EC = sys::fs::setLastAccessAndModificationTime(
+ FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime()))
+ return createFileError(Filename, EC);
+
+ sys::fs::file_status OStat;
+ if (std::error_code EC = sys::fs::status(FD, OStat))
+ return createFileError(Filename, EC);
+ if (OStat.type() == sys::fs::file_type::regular_file) {
+#ifndef _WIN32
+ // Keep ownership if llvm-objcopy is called under root.
+ if (Config.InputFilename == Config.OutputFilename && OStat.getUser() == 0)
+ sys::fs::changeFileOwnership(FD, Stat.getUser(), Stat.getGroup());
+#endif
+
+ sys::fs::perms Perm = Stat.permissions();
+ if (Config.InputFilename != Config.OutputFilename)
+ Perm = static_cast<sys::fs::perms>(Perm & ~sys::fs::getUmask() & ~06000);
+#ifdef _WIN32
+ if (auto EC = sys::fs::setPermissions(Filename, Perm))
+#else
+ if (auto EC = sys::fs::setPermissions(FD, Perm))
+#endif
+ return createFileError(Filename, EC);
+ }
+
+ if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))
+ return createFileError(Filename, EC);
+
+ return Error::success();
+}
+
+/// 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 Error executeObjcopy(ConfigManager &ConfigMgr) {
+ CommonConfig &Config = ConfigMgr.Common;
+
+ sys::fs::file_status Stat;
+ if (Config.InputFilename != "-") {
+ if (auto EC = sys::fs::status(Config.InputFilename, Stat))
+ return createFileError(Config.InputFilename, EC);
+ } else {
+ Stat.permissions(static_cast<sys::fs::perms>(0777));
+ }
+
+ std::function<Error(raw_ostream & OutFile)> ObjcopyFunc;
+
+ OwningBinary<llvm::object::Binary> BinaryHolder;
+ std::unique_ptr<MemoryBuffer> MemoryBufferHolder;
+
+ if (Config.InputFormat == FileFormat::Binary ||
+ Config.InputFormat == FileFormat::IHex) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFileOrSTDIN(Config.InputFilename);
+ if (!BufOrErr)
+ return createFileError(Config.InputFilename, BufOrErr.getError());
+ MemoryBufferHolder = std::move(*BufOrErr);
+
+ if (Config.InputFormat == FileFormat::Binary)
+ ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {
+ // Handle FileFormat::Binary.
+ return executeObjcopyOnRawBinary(ConfigMgr, *MemoryBufferHolder,
+ OutFile);
+ };
+ else
+ ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {
+ // Handle FileFormat::IHex.
+ return executeObjcopyOnIHex(ConfigMgr, *MemoryBufferHolder, OutFile);
+ };
+ } else {
+ Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr =
+ createBinary(Config.InputFilename);
+ if (!BinaryOrErr)
+ return createFileError(Config.InputFilename, BinaryOrErr.takeError());
+ BinaryHolder = std::move(*BinaryOrErr);
+
+ if (Archive *Ar = dyn_cast<Archive>(BinaryHolder.getBinary())) {
+ // Handle Archive.
+ if (Error E = executeObjcopyOnArchive(ConfigMgr, *Ar))
+ return E;
+ } else {
+ // Handle llvm::object::Binary.
+ ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {
+ return executeObjcopyOnBinary(ConfigMgr, *BinaryHolder.getBinary(),
+ OutFile);
+ };
+ }
+ }
+
+ if (ObjcopyFunc) {
+ if (Config.SplitDWO.empty()) {
+ // Apply transformations described by Config and store result into
+ // Config.OutputFilename using specified ObjcopyFunc function.
+ if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc))
+ return E;
+ } else {
+ Config.ExtractDWO = true;
+ Config.StripDWO = false;
+ // Copy .dwo tables from the Config.InputFilename into Config.SplitDWO
+ // file using specified ObjcopyFunc function.
+ if (Error E = writeToOutput(Config.SplitDWO, ObjcopyFunc))
+ return E;
+ Config.ExtractDWO = false;
+ Config.StripDWO = true;
+ // Apply transformations described by Config, remove .dwo tables and
+ // store result into Config.OutputFilename using specified ObjcopyFunc
+ // function.
+ if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc))
+ return E;
+ }
+ }
+
+ if (Error E = restoreStatOnFile(Config.OutputFilename, Stat, ConfigMgr))
+ return E;
+
+ if (!Config.SplitDWO.empty()) {
+ Stat.permissions(static_cast<sys::fs::perms>(0666));
+ if (Error E = restoreStatOnFile(Config.SplitDWO, Stat, ConfigMgr))
+ return E;
+ }
+
+ return Error::success();
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ ToolName = argv[0];
+
+ // Expand response files.
+ // TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp,
+ // into a separate function in the CommandLine library and call that function
+ // here. This is duplicated code.
+ SmallVector<const char *, 20> NewArgv(argv, argv + argc);
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ cl::ExpandResponseFiles(Saver,
+ Triple(sys::getProcessTriple()).isOSWindows()
+ ? cl::TokenizeWindowsCommandLine
+ : cl::TokenizeGNUCommandLine,
+ NewArgv);
+
+ auto Args = makeArrayRef(NewArgv).drop_front();
+ Expected<DriverConfig> DriverConfig = getDriverConfig(Args);
+
+ if (!DriverConfig) {
+ logAllUnhandledErrors(DriverConfig.takeError(),
+ WithColor::error(errs(), ToolName));
+ return 1;
+ }
+ for (ConfigManager &ConfigMgr : DriverConfig->CopyConfigs) {
+ if (Error E = executeObjcopy(ConfigMgr)) {
+ logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName));
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/llvm-objcopy.h b/contrib/libs/llvm14/tools/llvm-objcopy/llvm-objcopy.h
new file mode 100644
index 00000000000..182c95dc64c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/llvm-objcopy.h
@@ -0,0 +1,34 @@
+//===- llvm-objcopy.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_OBJCOPY_H
+#define LLVM_TOOLS_OBJCOPY_OBJCOPY_H
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+
+struct NewArchiveMember;
+
+namespace object {
+
+class Archive;
+
+} // end namespace object
+
+namespace objcopy {
+class MultiFormatConfig;
+Expected<std::vector<NewArchiveMember>>
+createNewArchiveMembers(const MultiFormatConfig &Config,
+ const object::Archive &Ar);
+
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OBJCOPY_OBJCOPY_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Object.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Object.cpp
new file mode 100644
index 00000000000..e7a2956fedc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Object.cpp
@@ -0,0 +1,34 @@
+//===- Object.cpp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Object.h"
+
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+using namespace object;
+using namespace llvm::wasm;
+
+void Object::addSectionWithOwnedContents(
+ Section NewSection, std::unique_ptr<MemoryBuffer> &&Content) {
+ Sections.push_back(NewSection);
+ OwnedContents.emplace_back(std::move(Content));
+}
+
+void Object::removeSections(function_ref<bool(const Section &)> ToRemove) {
+ // TODO: remove reloc sections for the removed section, handle symbols, etc.
+ llvm::erase_if(Sections, ToRemove);
+}
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Object.h b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Object.h
new file mode 100644
index 00000000000..9db91c41e2e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Object.h
@@ -0,0 +1,47 @@
+//===- Object.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+struct Section {
+ // For now, each section is only an opaque binary blob with no distinction
+ // between custom and known sections.
+ uint8_t SectionType;
+ StringRef Name;
+ ArrayRef<uint8_t> Contents;
+};
+
+struct Object {
+ llvm::wasm::WasmObjectHeader Header;
+ // For now don't discriminate between kinds of sections.
+ std::vector<Section> Sections;
+
+ void addSectionWithOwnedContents(Section NewSection,
+ std::unique_ptr<MemoryBuffer> &&Content);
+ void removeSections(function_ref<bool(const Section &)> ToRemove);
+
+private:
+ std::vector<std::unique_ptr<MemoryBuffer>> OwnedContents;
+};
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Reader.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Reader.cpp
new file mode 100644
index 00000000000..13fa84ad802
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Reader.cpp
@@ -0,0 +1,33 @@
+//===- Reader.cpp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Reader.h"
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+using namespace object;
+using namespace llvm::wasm;
+
+Expected<std::unique_ptr<Object>> Reader::create() const {
+ auto Obj = std::make_unique<Object>();
+ Obj->Header = WasmObj.getHeader();
+ std::vector<Section> Sections;
+ Obj->Sections.reserve(WasmObj.getNumSections());
+ for (const SectionRef &Sec : WasmObj.sections()) {
+ const WasmSection &WS = WasmObj.getWasmSection(Sec);
+ Obj->Sections.push_back(
+ {static_cast<uint8_t>(WS.Type), WS.Name, WS.Content});
+ }
+ return std::move(Obj);
+}
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Reader.h b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Reader.h
new file mode 100644
index 00000000000..2dcf7dde029
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Reader.h
@@ -0,0 +1,31 @@
+//===- Reader.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H
+
+#include "Object.h"
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+class Reader {
+public:
+ explicit Reader(const object::WasmObjectFile &O) : WasmObj(O) {}
+ Expected<std::unique_ptr<Object>> create() const;
+
+private:
+ const object::WasmObjectFile &WasmObj;
+};
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmConfig.h b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmConfig.h
new file mode 100644
index 00000000000..4e40926ae45
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmConfig.h
@@ -0,0 +1,21 @@
+//===- WasmConfig.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H
+
+namespace llvm {
+namespace objcopy {
+
+// Wasm specific configuration for copying/stripping a single file.
+struct WasmConfig {};
+
+} // namespace objcopy
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmObjcopy.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmObjcopy.cpp
new file mode 100644
index 00000000000..397d09757e5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmObjcopy.cpp
@@ -0,0 +1,162 @@
+//===- WasmObjcopy.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "WasmObjcopy.h"
+#include "CommonConfig.h"
+#include "Object.h"
+#include "Reader.h"
+#include "Writer.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileOutputBuffer.h"
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+using namespace object;
+using SectionPred = std::function<bool(const Section &Sec)>;
+
+static bool isDebugSection(const Section &Sec) {
+ return Sec.Name.startswith(".debug");
+}
+
+static bool isLinkerSection(const Section &Sec) {
+ return Sec.Name.startswith("reloc.") || Sec.Name == "linking";
+}
+
+static bool isNameSection(const Section &Sec) { return Sec.Name == "name"; }
+
+// Sections which are known to be "comments" or informational and do not affect
+// program semantics.
+static bool isCommentSection(const Section &Sec) {
+ return Sec.Name == "producers";
+}
+
+static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
+ Object &Obj) {
+ for (const Section &Sec : Obj.Sections) {
+ if (Sec.Name == SecName) {
+ ArrayRef<uint8_t> Contents = Sec.Contents;
+ Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+ FileOutputBuffer::create(Filename, Contents.size());
+ if (!BufferOrErr)
+ return BufferOrErr.takeError();
+ std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);
+ std::copy(Contents.begin(), Contents.end(), Buf->getBufferStart());
+ if (Error E = Buf->commit())
+ return E;
+ return Error::success();
+ }
+ }
+ return createStringError(errc::invalid_argument, "section '%s' not found",
+ SecName.str().c_str());
+}
+
+static void removeSections(const CommonConfig &Config, Object &Obj) {
+ SectionPred RemovePred = [](const Section &) { return false; };
+
+ // Explicitly-requested sections.
+ if (!Config.ToRemove.empty()) {
+ RemovePred = [&Config](const Section &Sec) {
+ return Config.ToRemove.matches(Sec.Name);
+ };
+ }
+
+ if (Config.StripDebug) {
+ RemovePred = [RemovePred](const Section &Sec) {
+ return RemovePred(Sec) || isDebugSection(Sec);
+ };
+ }
+
+ if (Config.StripAll) {
+ RemovePred = [RemovePred](const Section &Sec) {
+ return RemovePred(Sec) || isDebugSection(Sec) || isLinkerSection(Sec) ||
+ isNameSection(Sec) || isCommentSection(Sec);
+ };
+ }
+
+ if (Config.OnlyKeepDebug) {
+ RemovePred = [&Config](const Section &Sec) {
+ // Keep debug sections, unless explicitly requested to remove.
+ // Remove everything else, including known sections.
+ return Config.ToRemove.matches(Sec.Name) || !isDebugSection(Sec);
+ };
+ }
+
+ if (!Config.OnlySection.empty()) {
+ RemovePred = [&Config](const Section &Sec) {
+ // Explicitly keep these sections regardless of previous removes.
+ // Remove everything else, inluding known sections.
+ return !Config.OnlySection.matches(Sec.Name);
+ };
+ }
+
+ if (!Config.KeepSection.empty()) {
+ RemovePred = [&Config, RemovePred](const Section &Sec) {
+ // Explicitly keep these sections regardless of previous removes.
+ if (Config.KeepSection.matches(Sec.Name))
+ return false;
+ // Otherwise defer to RemovePred.
+ return RemovePred(Sec);
+ };
+ }
+
+ Obj.removeSections(RemovePred);
+}
+
+static Error handleArgs(const CommonConfig &Config, Object &Obj) {
+ // Only support AddSection, DumpSection, RemoveSection for now.
+ for (StringRef Flag : Config.DumpSection) {
+ StringRef SecName;
+ StringRef FileName;
+ std::tie(SecName, FileName) = Flag.split("=");
+ if (Error E = dumpSectionToFile(SecName, FileName, Obj))
+ return createFileError(FileName, std::move(E));
+ }
+
+ removeSections(Config, Obj);
+
+ for (StringRef Flag : Config.AddSection) {
+ StringRef SecName, FileName;
+ std::tie(SecName, FileName) = Flag.split("=");
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(FileName);
+ if (!BufOrErr)
+ return createFileError(FileName, errorCodeToError(BufOrErr.getError()));
+ Section Sec;
+ Sec.SectionType = llvm::wasm::WASM_SEC_CUSTOM;
+ Sec.Name = SecName;
+ std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr);
+ Sec.Contents = makeArrayRef<uint8_t>(
+ reinterpret_cast<const uint8_t *>(Buf->getBufferStart()),
+ Buf->getBufferSize());
+ Obj.addSectionWithOwnedContents(Sec, std::move(Buf));
+ }
+
+ return Error::success();
+}
+
+Error executeObjcopyOnBinary(const CommonConfig &Config, const WasmConfig &,
+ object::WasmObjectFile &In, raw_ostream &Out) {
+ Reader TheReader(In);
+ Expected<std::unique_ptr<Object>> ObjOrErr = TheReader.create();
+ if (!ObjOrErr)
+ return createFileError(Config.InputFilename, ObjOrErr.takeError());
+ Object *Obj = ObjOrErr->get();
+ assert(Obj && "Unable to deserialize Wasm object");
+ if (Error E = handleArgs(Config, *Obj))
+ return E;
+ Writer TheWriter(*Obj, Out);
+ if (Error E = TheWriter.write())
+ return createFileError(Config.OutputFilename, std::move(E));
+ return Error::success();
+}
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmObjcopy.h b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmObjcopy.h
new file mode 100644
index 00000000000..28268e38c58
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/WasmObjcopy.h
@@ -0,0 +1,32 @@
+//===- WasmObjcopy.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H
+
+namespace llvm {
+class Error;
+class raw_ostream;
+
+namespace object {
+class WasmObjectFile;
+} // end namespace object
+
+namespace objcopy {
+struct CommonConfig;
+struct WasmConfig;
+
+namespace wasm {
+Error executeObjcopyOnBinary(const CommonConfig &Config, const WasmConfig &,
+ object::WasmObjectFile &In, raw_ostream &Out);
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Writer.cpp b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Writer.cpp
new file mode 100644
index 00000000000..2fad9e60c50
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Writer.cpp
@@ -0,0 +1,79 @@
+//===- Writer.cpp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Writer.h"
+#include "llvm/BinaryFormat/Wasm.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+using namespace object;
+using namespace llvm::wasm;
+
+Writer::SectionHeader Writer::createSectionHeader(const Section &S,
+ size_t &SectionSize) {
+ SectionHeader Header;
+ raw_svector_ostream OS(Header);
+ OS << S.SectionType;
+ bool HasName = S.SectionType == WASM_SEC_CUSTOM;
+ SectionSize = S.Contents.size();
+ if (HasName)
+ SectionSize += getULEB128Size(S.Name.size()) + S.Name.size();
+ // Pad the LEB value out to 5 bytes to make it a predictable size, and
+ // match the behavior of clang.
+ encodeULEB128(SectionSize, OS, 5);
+ if (HasName) {
+ encodeULEB128(S.Name.size(), OS);
+ OS << S.Name;
+ }
+ // Total section size is the content size plus 1 for the section type and
+ // 5 for the LEB-encoded size.
+ SectionSize = SectionSize + 1 + 5;
+ return Header;
+}
+
+size_t Writer::finalize() {
+ size_t ObjectSize = sizeof(WasmMagic) + sizeof(WasmVersion);
+ SectionHeaders.reserve(Obj.Sections.size());
+ // Finalize the headers of each section so we know the total size.
+ for (const Section &S : Obj.Sections) {
+ size_t SectionSize;
+ SectionHeaders.push_back(createSectionHeader(S, SectionSize));
+ ObjectSize += SectionSize;
+ }
+ return ObjectSize;
+}
+
+Error Writer::write() {
+ size_t TotalSize = finalize();
+ Out.reserveExtraSpace(TotalSize);
+
+ // Write the header.
+ Out.write(Obj.Header.Magic.data(), Obj.Header.Magic.size());
+ uint32_t Version;
+ support::endian::write32le(&Version, Obj.Header.Version);
+ Out.write(reinterpret_cast<const char *>(&Version), sizeof(Version));
+
+ // Write each section.
+ for (size_t I = 0, S = SectionHeaders.size(); I < S; ++I) {
+ Out.write(SectionHeaders[I].data(), SectionHeaders[I].size());
+ Out.write(reinterpret_cast<const char *>(Obj.Sections[I].Contents.data()),
+ Obj.Sections[I].Contents.size());
+ }
+
+ return Error::success();
+}
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Writer.h b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Writer.h
new file mode 100644
index 00000000000..4404cd8caf8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/wasm/Writer.h
@@ -0,0 +1,49 @@
+//===- Writer.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H
+#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H
+
+#include "Object.h"
+#include <cstdint>
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+namespace wasm {
+
+class Writer {
+public:
+ Writer(Object &Obj, raw_ostream &Out) : Obj(Obj), Out(Out) {}
+ Error write();
+
+private:
+ using SectionHeader = SmallVector<char, 8>;
+ Object &Obj;
+ raw_ostream &Out;
+ std::vector<SectionHeader> SectionHeaders;
+
+ /// Generate a wasm section section header for S.
+ /// The header consists of
+ /// * A one-byte section ID (aka the section type).
+ /// * The size of the section contents, encoded as ULEB128.
+ /// * If the section is a custom section (type 0) it also has a name, which is
+ /// encoded as a length-prefixed string. The encoded section size *includes*
+ /// this string.
+ /// See https://webassembly.github.io/spec/core/binary/modules.html#sections
+ /// Return the header and store the total size in SectionSize.
+ static SectionHeader createSectionHeader(const Section &S,
+ size_t &SectionSize);
+ size_t finalize();
+};
+
+} // end namespace wasm
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H
diff --git a/contrib/libs/llvm14/tools/llvm-objcopy/ya.make b/contrib/libs/llvm14/tools/llvm-objcopy/ya.make
new file mode 100644
index 00000000000..7ae1ba019da
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objcopy/ya.make
@@ -0,0 +1,55 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-objcopy
+ contrib/libs/llvm14/tools/llvm-objcopy
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ COFF/COFFObjcopy.cpp
+ COFF/Object.cpp
+ COFF/Reader.cpp
+ COFF/Writer.cpp
+ ConfigManager.cpp
+ ELF/ELFObjcopy.cpp
+ ELF/Object.cpp
+ MachO/MachOLayoutBuilder.cpp
+ MachO/MachOObjcopy.cpp
+ MachO/MachOReader.cpp
+ MachO/MachOWriter.cpp
+ MachO/Object.cpp
+ llvm-objcopy.cpp
+ wasm/Object.cpp
+ wasm/Reader.cpp
+ wasm/WasmObjcopy.cpp
+ wasm/Writer.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/COFFDump.cpp b/contrib/libs/llvm14/tools/llvm-objdump/COFFDump.cpp
new file mode 100644
index 00000000000..32fdd1a4d5c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/COFFDump.cpp
@@ -0,0 +1,920 @@
+//===-- COFFDump.cpp - COFF-specific dumper ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the COFF-specific dumper for llvm-objdump.
+/// It outputs the Win64 EH data structures as plain text.
+/// The encoding of the unwind codes is described in MSDN:
+/// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
+///
+//===----------------------------------------------------------------------===//
+
+#include "COFFDump.h"
+
+#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;
+using namespace llvm::objdump;
+using namespace llvm::object;
+using namespace llvm::Win64EH;
+
+namespace {
+template <typename T> struct EnumEntry {
+ T Value;
+ StringRef Name;
+};
+
+class COFFDumper {
+public:
+ explicit COFFDumper(const llvm::object::COFFObjectFile &Obj) : Obj(Obj) {
+ Is64 = !Obj.getPE32Header();
+ }
+
+ template <class PEHeader> void printPEHeader(const PEHeader &Hdr) const;
+
+private:
+ template <typename T> FormattedNumber formatAddr(T V) const {
+ return format_hex_no_prefix(V, Is64 ? 16 : 8);
+ }
+
+ uint32_t getBaseOfData(const void *Hdr) const {
+ return Is64 ? 0 : static_cast<const pe32_header *>(Hdr)->BaseOfData;
+ }
+
+ const llvm::object::COFFObjectFile &Obj;
+ bool Is64;
+};
+} // namespace
+
+constexpr EnumEntry<uint16_t> PEHeaderMagic[] = {
+ {uint16_t(COFF::PE32Header::PE32), "PE32"},
+ {uint16_t(COFF::PE32Header::PE32_PLUS), "PE32+"},
+};
+
+constexpr EnumEntry<COFF::WindowsSubsystem> PEWindowsSubsystem[] = {
+ {COFF::IMAGE_SUBSYSTEM_UNKNOWN, "unspecified"},
+ {COFF::IMAGE_SUBSYSTEM_NATIVE, "NT native"},
+ {COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI, "Windows GUI"},
+ {COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, "Windows CUI"},
+ {COFF::IMAGE_SUBSYSTEM_POSIX_CUI, "POSIX CUI"},
+ {COFF::IMAGE_SUBSYSTEM_WINDOWS_CE_GUI, "Wince CUI"},
+ {COFF::IMAGE_SUBSYSTEM_EFI_APPLICATION, "EFI application"},
+ {COFF::IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER, "EFI boot service driver"},
+ {COFF::IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER, "EFI runtime driver"},
+ {COFF::IMAGE_SUBSYSTEM_EFI_ROM, "SAL runtime driver"},
+ {COFF::IMAGE_SUBSYSTEM_XBOX, "XBOX"},
+};
+
+template <typename T, typename TEnum>
+static void printOptionalEnumName(T Value,
+ ArrayRef<EnumEntry<TEnum>> EnumValues) {
+ for (const EnumEntry<TEnum> &I : EnumValues)
+ if (I.Value == Value) {
+ outs() << "\t(" << I.Name << ')';
+ return;
+ }
+}
+
+template <class PEHeader>
+void COFFDumper::printPEHeader(const PEHeader &Hdr) const {
+ auto print = [](const char *K, auto V, const char *Fmt = "%d\n") {
+ outs() << format("%-23s ", K) << format(Fmt, V);
+ };
+ auto printU16 = [&](const char *K, support::ulittle16_t V,
+ const char *Fmt = "%d\n") { print(K, uint16_t(V), Fmt); };
+ auto printU32 = [&](const char *K, support::ulittle32_t V,
+ const char *Fmt = "%d\n") { print(K, uint32_t(V), Fmt); };
+ auto printAddr = [=](const char *K, uint64_t V) {
+ outs() << format("%-23s ", K) << formatAddr(V) << '\n';
+ };
+
+ printU16("Magic", Hdr.Magic, "%04x");
+ printOptionalEnumName(Hdr.Magic, makeArrayRef(PEHeaderMagic));
+ outs() << '\n';
+ print("MajorLinkerVersion", Hdr.MajorLinkerVersion);
+ print("MinorLinkerVersion", Hdr.MinorLinkerVersion);
+ printAddr("SizeOfCode", Hdr.SizeOfCode);
+ printAddr("SizeOfInitializedData", Hdr.SizeOfInitializedData);
+ printAddr("SizeOfUninitializedData", Hdr.SizeOfUninitializedData);
+ printAddr("AddressOfEntryPoint", Hdr.AddressOfEntryPoint);
+ printAddr("BaseOfCode", Hdr.BaseOfCode);
+ if (!Is64)
+ printAddr("BaseOfData", getBaseOfData(&Hdr));
+ printAddr("ImageBase", Hdr.ImageBase);
+ printU32("SectionAlignment", Hdr.SectionAlignment, "%08x\n");
+ printU32("FileAlignment", Hdr.FileAlignment, "%08x\n");
+ printU16("MajorOSystemVersion", Hdr.MajorOperatingSystemVersion);
+ printU16("MinorOSystemVersion", Hdr.MinorOperatingSystemVersion);
+ printU16("MajorImageVersion", Hdr.MajorImageVersion);
+ printU16("MinorImageVersion", Hdr.MinorImageVersion);
+ printU16("MajorSubsystemVersion", Hdr.MajorSubsystemVersion);
+ printU16("MinorSubsystemVersion", Hdr.MinorSubsystemVersion);
+ printU32("Win32Version", Hdr.Win32VersionValue, "%08x\n");
+ printU32("SizeOfImage", Hdr.SizeOfImage, "%08x\n");
+ printU32("SizeOfHeaders", Hdr.SizeOfHeaders, "%08x\n");
+ printU32("CheckSum", Hdr.CheckSum, "%08x\n");
+ printU16("Subsystem", Hdr.Subsystem, "%08x");
+ printOptionalEnumName(Hdr.Subsystem, makeArrayRef(PEWindowsSubsystem));
+ outs() << '\n';
+
+ printU16("DllCharacteristics", Hdr.DLLCharacteristics, "%08x\n");
+#define FLAG(Name) \
+ if (Hdr.DLLCharacteristics & COFF::IMAGE_DLL_CHARACTERISTICS_##Name) \
+ outs() << "\t\t\t\t\t" << #Name << '\n';
+ FLAG(HIGH_ENTROPY_VA);
+ FLAG(DYNAMIC_BASE);
+ FLAG(FORCE_INTEGRITY);
+ FLAG(NX_COMPAT);
+ FLAG(NO_ISOLATION);
+ FLAG(NO_SEH);
+ FLAG(NO_BIND);
+ FLAG(APPCONTAINER);
+ FLAG(WDM_DRIVER);
+ FLAG(GUARD_CF);
+ FLAG(TERMINAL_SERVER_AWARE);
+#undef FLAG
+
+ printAddr("SizeOfStackReserve", Hdr.SizeOfStackReserve);
+ printAddr("SizeOfStackCommit", Hdr.SizeOfStackCommit);
+ printAddr("SizeOfHeapReserve", Hdr.SizeOfHeapReserve);
+ printAddr("SizeOfHeapCommit", Hdr.SizeOfHeapCommit);
+ printU32("LoaderFlags", Hdr.LoaderFlags, "%08x\n");
+ printU32("NumberOfRvaAndSizes", Hdr.NumberOfRvaAndSize, "%08x\n");
+
+ static const char *DirName[COFF::NUM_DATA_DIRECTORIES + 1] = {
+ "Export Directory [.edata (or where ever we found it)]",
+ "Import Directory [parts of .idata]",
+ "Resource Directory [.rsrc]",
+ "Exception Directory [.pdata]",
+ "Security Directory",
+ "Base Relocation Directory [.reloc]",
+ "Debug Directory",
+ "Description Directory",
+ "Special Directory",
+ "Thread Storage Directory [.tls]",
+ "Load Configuration Directory",
+ "Bound Import Directory",
+ "Import Address Table Directory",
+ "Delay Import Directory",
+ "CLR Runtime Header",
+ "Reserved",
+ };
+ outs() << "\nThe Data Directory\n";
+ for (uint32_t I = 0; I != array_lengthof(DirName); ++I) {
+ uint32_t Addr = 0, Size = 0;
+ if (const data_directory *Data = Obj.getDataDirectory(I)) {
+ Addr = Data->RelativeVirtualAddress;
+ Size = Data->Size;
+ }
+ outs() << format("Entry %x ", I) << formatAddr(Addr)
+ << format(" %08x %s\n", uint32_t(Size), DirName[I]);
+ }
+}
+
+// Returns the name of the unwind code.
+static StringRef getUnwindCodeTypeName(uint8_t Code) {
+ switch(Code) {
+ default: llvm_unreachable("Invalid unwind code");
+ case UOP_PushNonVol: return "UOP_PushNonVol";
+ case UOP_AllocLarge: return "UOP_AllocLarge";
+ case UOP_AllocSmall: return "UOP_AllocSmall";
+ case UOP_SetFPReg: return "UOP_SetFPReg";
+ case UOP_SaveNonVol: return "UOP_SaveNonVol";
+ case UOP_SaveNonVolBig: return "UOP_SaveNonVolBig";
+ case UOP_SaveXMM128: return "UOP_SaveXMM128";
+ case UOP_SaveXMM128Big: return "UOP_SaveXMM128Big";
+ case UOP_PushMachFrame: return "UOP_PushMachFrame";
+ }
+}
+
+// Returns the name of a referenced register.
+static StringRef getUnwindRegisterName(uint8_t Reg) {
+ switch(Reg) {
+ default: llvm_unreachable("Invalid register");
+ case 0: return "RAX";
+ case 1: return "RCX";
+ case 2: return "RDX";
+ case 3: return "RBX";
+ case 4: return "RSP";
+ case 5: return "RBP";
+ case 6: return "RSI";
+ case 7: return "RDI";
+ case 8: return "R8";
+ case 9: return "R9";
+ case 10: return "R10";
+ case 11: return "R11";
+ case 12: return "R12";
+ case 13: return "R13";
+ case 14: return "R14";
+ case 15: return "R15";
+ }
+}
+
+// Calculates the number of array slots required for the unwind code.
+static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) {
+ switch (UnwindCode.getUnwindOp()) {
+ default: llvm_unreachable("Invalid unwind code");
+ case UOP_PushNonVol:
+ case UOP_AllocSmall:
+ case UOP_SetFPReg:
+ case UOP_PushMachFrame:
+ return 1;
+ case UOP_SaveNonVol:
+ case UOP_SaveXMM128:
+ return 2;
+ case UOP_SaveNonVolBig:
+ case UOP_SaveXMM128Big:
+ return 3;
+ case UOP_AllocLarge:
+ return (UnwindCode.getOpInfo() == 0) ? 2 : 3;
+ }
+}
+
+// Prints one unwind code. Because an unwind code can occupy up to 3 slots in
+// the unwind codes array, this function requires that the correct number of
+// slots is provided.
+static void printUnwindCode(ArrayRef<UnwindCode> UCs) {
+ assert(UCs.size() >= getNumUsedSlots(UCs[0]));
+ outs() << format(" 0x%02x: ", unsigned(UCs[0].u.CodeOffset))
+ << getUnwindCodeTypeName(UCs[0].getUnwindOp());
+ switch (UCs[0].getUnwindOp()) {
+ case UOP_PushNonVol:
+ outs() << " " << getUnwindRegisterName(UCs[0].getOpInfo());
+ break;
+ case UOP_AllocLarge:
+ if (UCs[0].getOpInfo() == 0) {
+ outs() << " " << UCs[1].FrameOffset;
+ } else {
+ outs() << " " << UCs[1].FrameOffset
+ + (static_cast<uint32_t>(UCs[2].FrameOffset) << 16);
+ }
+ break;
+ case UOP_AllocSmall:
+ outs() << " " << ((UCs[0].getOpInfo() + 1) * 8);
+ break;
+ case UOP_SetFPReg:
+ outs() << " ";
+ break;
+ case UOP_SaveNonVol:
+ outs() << " " << getUnwindRegisterName(UCs[0].getOpInfo())
+ << format(" [0x%04x]", 8 * UCs[1].FrameOffset);
+ break;
+ case UOP_SaveNonVolBig:
+ outs() << " " << getUnwindRegisterName(UCs[0].getOpInfo())
+ << format(" [0x%08x]", UCs[1].FrameOffset
+ + (static_cast<uint32_t>(UCs[2].FrameOffset) << 16));
+ break;
+ case UOP_SaveXMM128:
+ outs() << " XMM" << static_cast<uint32_t>(UCs[0].getOpInfo())
+ << format(" [0x%04x]", 16 * UCs[1].FrameOffset);
+ break;
+ case UOP_SaveXMM128Big:
+ outs() << " XMM" << UCs[0].getOpInfo()
+ << format(" [0x%08x]", UCs[1].FrameOffset
+ + (static_cast<uint32_t>(UCs[2].FrameOffset) << 16));
+ break;
+ case UOP_PushMachFrame:
+ outs() << " " << (UCs[0].getOpInfo() ? "w/o" : "w")
+ << " error code";
+ break;
+ }
+ outs() << "\n";
+}
+
+static void printAllUnwindCodes(ArrayRef<UnwindCode> UCs) {
+ for (const UnwindCode *I = UCs.begin(), *E = UCs.end(); I < E; ) {
+ unsigned UsedSlots = getNumUsedSlots(*I);
+ if (UsedSlots > UCs.size()) {
+ outs() << "Unwind data corrupted: Encountered unwind op "
+ << getUnwindCodeTypeName((*I).getUnwindOp())
+ << " which requires " << UsedSlots
+ << " slots, but only " << UCs.size()
+ << " remaining in buffer";
+ return ;
+ }
+ printUnwindCode(makeArrayRef(I, E));
+ I += UsedSlots;
+ }
+}
+
+// Given a symbol sym this functions returns the address and section of it.
+static Error resolveSectionAndAddress(const COFFObjectFile *Obj,
+ const SymbolRef &Sym,
+ const coff_section *&ResolvedSection,
+ uint64_t &ResolvedAddr) {
+ Expected<uint64_t> ResolvedAddrOrErr = Sym.getAddress();
+ if (!ResolvedAddrOrErr)
+ return ResolvedAddrOrErr.takeError();
+ ResolvedAddr = *ResolvedAddrOrErr;
+ Expected<section_iterator> Iter = Sym.getSection();
+ if (!Iter)
+ return Iter.takeError();
+ ResolvedSection = Obj->getCOFFSection(**Iter);
+ return Error::success();
+}
+
+// Given a vector of relocations for a section and an offset into this section
+// the function returns the symbol used for the relocation at the offset.
+static Error resolveSymbol(const std::vector<RelocationRef> &Rels,
+ uint64_t Offset, SymbolRef &Sym) {
+ for (auto &R : Rels) {
+ uint64_t Ofs = R.getOffset();
+ if (Ofs == Offset) {
+ Sym = *R.getSymbol();
+ return Error::success();
+ }
+ }
+ return make_error<BinaryError>();
+}
+
+// Given a vector of relocations for a section and an offset into this section
+// the function resolves the symbol used for the relocation at the offset and
+// returns the section content and the address inside the content pointed to
+// by the symbol.
+static Error
+getSectionContents(const COFFObjectFile *Obj,
+ const std::vector<RelocationRef> &Rels, uint64_t Offset,
+ ArrayRef<uint8_t> &Contents, uint64_t &Addr) {
+ SymbolRef Sym;
+ if (Error E = resolveSymbol(Rels, Offset, Sym))
+ return E;
+ const coff_section *Section;
+ if (Error E = resolveSectionAndAddress(Obj, Sym, Section, Addr))
+ return E;
+ return Obj->getSectionContents(Section, Contents);
+}
+
+// Given a vector of relocations for a section and an offset into this section
+// the function returns the name of the symbol used for the relocation at the
+// offset.
+static Error resolveSymbolName(const std::vector<RelocationRef> &Rels,
+ uint64_t Offset, StringRef &Name) {
+ SymbolRef Sym;
+ if (Error EC = resolveSymbol(Rels, Offset, Sym))
+ return EC;
+ Expected<StringRef> NameOrErr = Sym.getName();
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ Name = *NameOrErr;
+ return Error::success();
+}
+
+static void printCOFFSymbolAddress(raw_ostream &Out,
+ const std::vector<RelocationRef> &Rels,
+ uint64_t Offset, uint32_t Disp) {
+ StringRef Sym;
+ if (!resolveSymbolName(Rels, Offset, Sym)) {
+ Out << Sym;
+ if (Disp > 0)
+ Out << format(" + 0x%04x", Disp);
+ } else {
+ Out << format("0x%04x", Disp);
+ }
+}
+
+static void
+printSEHTable(const COFFObjectFile *Obj, uint32_t TableVA, int Count) {
+ if (Count == 0)
+ return;
+
+ uintptr_t IntPtr = 0;
+ if (Error E = Obj->getVaPtr(TableVA, IntPtr))
+ reportError(std::move(E), Obj->getFileName());
+
+ const support::ulittle32_t *P = (const support::ulittle32_t *)IntPtr;
+ outs() << "SEH Table:";
+ for (int I = 0; I < Count; ++I)
+ outs() << format(" 0x%x", P[I] + Obj->getPE32Header()->ImageBase);
+ outs() << "\n\n";
+}
+
+template <typename T>
+static void printTLSDirectoryT(const coff_tls_directory<T> *TLSDir) {
+ size_t FormatWidth = sizeof(T) * 2;
+ outs() << "TLS directory:"
+ << "\n StartAddressOfRawData: "
+ << format_hex(TLSDir->StartAddressOfRawData, FormatWidth)
+ << "\n EndAddressOfRawData: "
+ << format_hex(TLSDir->EndAddressOfRawData, FormatWidth)
+ << "\n AddressOfIndex: "
+ << format_hex(TLSDir->AddressOfIndex, FormatWidth)
+ << "\n AddressOfCallBacks: "
+ << format_hex(TLSDir->AddressOfCallBacks, FormatWidth)
+ << "\n SizeOfZeroFill: "
+ << TLSDir->SizeOfZeroFill
+ << "\n Characteristics: "
+ << TLSDir->Characteristics
+ << "\n Alignment: "
+ << TLSDir->getAlignment()
+ << "\n\n";
+}
+
+static void printTLSDirectory(const COFFObjectFile *Obj) {
+ const pe32_header *PE32Header = Obj->getPE32Header();
+ const pe32plus_header *PE32PlusHeader = Obj->getPE32PlusHeader();
+
+ // Skip if it's not executable.
+ if (!PE32Header && !PE32PlusHeader)
+ return;
+
+ const data_directory *DataDir = Obj->getDataDirectory(COFF::TLS_TABLE);
+ if (!DataDir || DataDir->RelativeVirtualAddress == 0)
+ return;
+
+ uintptr_t IntPtr = 0;
+ if (Error E =
+ Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr))
+ reportError(std::move(E), Obj->getFileName());
+
+ if (PE32Header) {
+ auto *TLSDir = reinterpret_cast<const coff_tls_directory32 *>(IntPtr);
+ printTLSDirectoryT(TLSDir);
+ } else {
+ auto *TLSDir = reinterpret_cast<const coff_tls_directory64 *>(IntPtr);
+ printTLSDirectoryT(TLSDir);
+ }
+
+ outs() << "\n";
+}
+
+static void printLoadConfiguration(const COFFObjectFile *Obj) {
+ // Skip if it's not executable.
+ if (!Obj->getPE32Header())
+ return;
+
+ // Currently only x86 is supported
+ if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_I386)
+ return;
+
+ const data_directory *DataDir = Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE);
+ if (!DataDir)
+ reportError("no load config data dir", Obj->getFileName());
+
+ uintptr_t IntPtr = 0;
+ if (DataDir->RelativeVirtualAddress == 0)
+ return;
+
+ if (Error E =
+ Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr))
+ reportError(std::move(E), Obj->getFileName());
+
+ auto *LoadConf = reinterpret_cast<const coff_load_configuration32 *>(IntPtr);
+ outs() << "Load configuration:"
+ << "\n Timestamp: " << LoadConf->TimeDateStamp
+ << "\n Major Version: " << LoadConf->MajorVersion
+ << "\n Minor Version: " << LoadConf->MinorVersion
+ << "\n GlobalFlags Clear: " << LoadConf->GlobalFlagsClear
+ << "\n GlobalFlags Set: " << LoadConf->GlobalFlagsSet
+ << "\n Critical Section Default Timeout: " << LoadConf->CriticalSectionDefaultTimeout
+ << "\n Decommit Free Block Threshold: " << LoadConf->DeCommitFreeBlockThreshold
+ << "\n Decommit Total Free Threshold: " << LoadConf->DeCommitTotalFreeThreshold
+ << "\n Lock Prefix Table: " << LoadConf->LockPrefixTable
+ << "\n Maximum Allocation Size: " << LoadConf->MaximumAllocationSize
+ << "\n Virtual Memory Threshold: " << LoadConf->VirtualMemoryThreshold
+ << "\n Process Affinity Mask: " << LoadConf->ProcessAffinityMask
+ << "\n Process Heap Flags: " << LoadConf->ProcessHeapFlags
+ << "\n CSD Version: " << LoadConf->CSDVersion
+ << "\n Security Cookie: " << LoadConf->SecurityCookie
+ << "\n SEH Table: " << LoadConf->SEHandlerTable
+ << "\n SEH Count: " << LoadConf->SEHandlerCount
+ << "\n\n";
+ printSEHTable(Obj, LoadConf->SEHandlerTable, LoadConf->SEHandlerCount);
+ outs() << "\n";
+}
+
+// Prints import tables. The import table is a table containing the list of
+// DLL name and symbol names which will be linked by the loader.
+static void printImportTables(const COFFObjectFile *Obj) {
+ import_directory_iterator I = Obj->import_directory_begin();
+ import_directory_iterator E = Obj->import_directory_end();
+ if (I == E)
+ return;
+ outs() << "The Import Tables:\n";
+ for (const ImportDirectoryEntryRef &DirRef : Obj->import_directories()) {
+ const coff_import_directory_table_entry *Dir;
+ StringRef Name;
+ if (DirRef.getImportTableEntry(Dir)) return;
+ if (DirRef.getName(Name)) return;
+
+ outs() << format(" lookup %08x time %08x fwd %08x name %08x addr %08x\n\n",
+ static_cast<uint32_t>(Dir->ImportLookupTableRVA),
+ static_cast<uint32_t>(Dir->TimeDateStamp),
+ static_cast<uint32_t>(Dir->ForwarderChain),
+ static_cast<uint32_t>(Dir->NameRVA),
+ static_cast<uint32_t>(Dir->ImportAddressTableRVA));
+ outs() << " DLL Name: " << Name << "\n";
+ outs() << " Hint/Ord Name\n";
+ for (const ImportedSymbolRef &Entry : DirRef.imported_symbols()) {
+ bool IsOrdinal;
+ if (Entry.isOrdinal(IsOrdinal))
+ return;
+ if (IsOrdinal) {
+ uint16_t Ordinal;
+ if (Entry.getOrdinal(Ordinal))
+ return;
+ outs() << format(" % 6d\n", Ordinal);
+ continue;
+ }
+ uint32_t HintNameRVA;
+ if (Entry.getHintNameRVA(HintNameRVA))
+ return;
+ uint16_t Hint;
+ StringRef Name;
+ if (Obj->getHintName(HintNameRVA, Hint, Name))
+ return;
+ outs() << format(" % 6d ", Hint) << Name << "\n";
+ }
+ outs() << "\n";
+ }
+}
+
+// Prints export tables. The export table is a table containing the list of
+// exported symbol from the DLL.
+static void printExportTable(const COFFObjectFile *Obj) {
+ outs() << "Export Table:\n";
+ export_directory_iterator I = Obj->export_directory_begin();
+ export_directory_iterator E = Obj->export_directory_end();
+ if (I == E)
+ return;
+ StringRef DllName;
+ uint32_t OrdinalBase;
+ if (I->getDllName(DllName))
+ return;
+ if (I->getOrdinalBase(OrdinalBase))
+ return;
+ outs() << " DLL name: " << DllName << "\n";
+ outs() << " Ordinal base: " << OrdinalBase << "\n";
+ outs() << " Ordinal RVA Name\n";
+ for (; I != E; I = ++I) {
+ uint32_t Ordinal;
+ if (I->getOrdinal(Ordinal))
+ return;
+ uint32_t RVA;
+ if (I->getExportRVA(RVA))
+ return;
+ bool IsForwarder;
+ if (I->isForwarder(IsForwarder))
+ return;
+
+ if (IsForwarder) {
+ // Export table entries can be used to re-export symbols that
+ // this COFF file is imported from some DLLs. This is rare.
+ // In most cases IsForwarder is false.
+ outs() << format(" % 4d ", Ordinal);
+ } else {
+ outs() << format(" % 4d %# 8x", Ordinal, RVA);
+ }
+
+ StringRef Name;
+ if (I->getSymbolName(Name))
+ continue;
+ if (!Name.empty())
+ outs() << " " << Name;
+ if (IsForwarder) {
+ StringRef S;
+ if (I->getForwardTo(S))
+ return;
+ outs() << " (forwarded to " << S << ")";
+ }
+ outs() << "\n";
+ }
+}
+
+// Given the COFF object file, this function returns the relocations for .pdata
+// and the pointer to "runtime function" structs.
+static bool getPDataSection(const COFFObjectFile *Obj,
+ std::vector<RelocationRef> &Rels,
+ const RuntimeFunction *&RFStart, int &NumRFs) {
+ for (const SectionRef &Section : Obj->sections()) {
+ StringRef Name = unwrapOrError(Section.getName(), Obj->getFileName());
+ if (Name != ".pdata")
+ continue;
+
+ const coff_section *Pdata = Obj->getCOFFSection(Section);
+ append_range(Rels, Section.relocations());
+
+ // Sort relocations by address.
+ llvm::sort(Rels, isRelocAddressLess);
+
+ ArrayRef<uint8_t> Contents;
+ if (Error E = Obj->getSectionContents(Pdata, Contents))
+ reportError(std::move(E), Obj->getFileName());
+
+ if (Contents.empty())
+ continue;
+
+ RFStart = reinterpret_cast<const RuntimeFunction *>(Contents.data());
+ NumRFs = Contents.size() / sizeof(RuntimeFunction);
+ return true;
+ }
+ return false;
+}
+
+Error objdump::getCOFFRelocationValueString(const COFFObjectFile *Obj,
+ const RelocationRef &Rel,
+ SmallVectorImpl<char> &Result) {
+ symbol_iterator SymI = Rel.getSymbol();
+ Expected<StringRef> SymNameOrErr = SymI->getName();
+ if (!SymNameOrErr)
+ return SymNameOrErr.takeError();
+ StringRef SymName = *SymNameOrErr;
+ Result.append(SymName.begin(), SymName.end());
+ return Error::success();
+}
+
+static void printWin64EHUnwindInfo(const Win64EH::UnwindInfo *UI) {
+ // The casts to int are required in order to output the value as number.
+ // Without the casts the value would be interpreted as char data (which
+ // results in garbage output).
+ outs() << " Version: " << static_cast<int>(UI->getVersion()) << "\n";
+ outs() << " Flags: " << static_cast<int>(UI->getFlags());
+ if (UI->getFlags()) {
+ if (UI->getFlags() & UNW_ExceptionHandler)
+ outs() << " UNW_ExceptionHandler";
+ if (UI->getFlags() & UNW_TerminateHandler)
+ outs() << " UNW_TerminateHandler";
+ if (UI->getFlags() & UNW_ChainInfo)
+ outs() << " UNW_ChainInfo";
+ }
+ outs() << "\n";
+ outs() << " Size of prolog: " << static_cast<int>(UI->PrologSize) << "\n";
+ outs() << " Number of Codes: " << static_cast<int>(UI->NumCodes) << "\n";
+ // Maybe this should move to output of UOP_SetFPReg?
+ if (UI->getFrameRegister()) {
+ outs() << " Frame register: "
+ << getUnwindRegisterName(UI->getFrameRegister()) << "\n";
+ outs() << " Frame offset: " << 16 * UI->getFrameOffset() << "\n";
+ } else {
+ outs() << " No frame pointer used\n";
+ }
+ if (UI->getFlags() & (UNW_ExceptionHandler | UNW_TerminateHandler)) {
+ // FIXME: Output exception handler data
+ } else if (UI->getFlags() & UNW_ChainInfo) {
+ // FIXME: Output chained unwind info
+ }
+
+ if (UI->NumCodes)
+ outs() << " Unwind Codes:\n";
+
+ printAllUnwindCodes(makeArrayRef(&UI->UnwindCodes[0], UI->NumCodes));
+
+ outs() << "\n";
+ outs().flush();
+}
+
+/// Prints out the given RuntimeFunction struct for x64, assuming that Obj is
+/// pointing to an executable file.
+static void printRuntimeFunction(const COFFObjectFile *Obj,
+ const RuntimeFunction &RF) {
+ if (!RF.StartAddress)
+ return;
+ outs() << "Function Table:\n"
+ << format(" Start Address: 0x%04x\n",
+ static_cast<uint32_t>(RF.StartAddress))
+ << format(" End Address: 0x%04x\n",
+ static_cast<uint32_t>(RF.EndAddress))
+ << format(" Unwind Info Address: 0x%04x\n",
+ static_cast<uint32_t>(RF.UnwindInfoOffset));
+ uintptr_t addr;
+ if (Obj->getRvaPtr(RF.UnwindInfoOffset, addr))
+ return;
+ printWin64EHUnwindInfo(reinterpret_cast<const Win64EH::UnwindInfo *>(addr));
+}
+
+/// Prints out the given RuntimeFunction struct for x64, assuming that Obj is
+/// pointing to an object file. Unlike executable, fields in RuntimeFunction
+/// struct are filled with zeros, but instead there are relocations pointing to
+/// them so that the linker will fill targets' RVAs to the fields at link
+/// time. This function interprets the relocations to find the data to be used
+/// in the resulting executable.
+static void printRuntimeFunctionRels(const COFFObjectFile *Obj,
+ const RuntimeFunction &RF,
+ uint64_t SectionOffset,
+ const std::vector<RelocationRef> &Rels) {
+ outs() << "Function Table:\n";
+ outs() << " Start Address: ";
+ printCOFFSymbolAddress(outs(), Rels,
+ SectionOffset +
+ /*offsetof(RuntimeFunction, StartAddress)*/ 0,
+ RF.StartAddress);
+ outs() << "\n";
+
+ outs() << " End Address: ";
+ printCOFFSymbolAddress(outs(), Rels,
+ SectionOffset +
+ /*offsetof(RuntimeFunction, EndAddress)*/ 4,
+ RF.EndAddress);
+ outs() << "\n";
+
+ outs() << " Unwind Info Address: ";
+ printCOFFSymbolAddress(outs(), Rels,
+ SectionOffset +
+ /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8,
+ RF.UnwindInfoOffset);
+ outs() << "\n";
+
+ ArrayRef<uint8_t> XContents;
+ uint64_t UnwindInfoOffset = 0;
+ if (Error E = getSectionContents(
+ Obj, Rels,
+ SectionOffset +
+ /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8,
+ XContents, UnwindInfoOffset))
+ reportError(std::move(E), Obj->getFileName());
+ if (XContents.empty())
+ return;
+
+ UnwindInfoOffset += RF.UnwindInfoOffset;
+ if (UnwindInfoOffset > XContents.size())
+ return;
+
+ auto *UI = reinterpret_cast<const Win64EH::UnwindInfo *>(XContents.data() +
+ UnwindInfoOffset);
+ printWin64EHUnwindInfo(UI);
+}
+
+void objdump::printCOFFUnwindInfo(const COFFObjectFile *Obj) {
+ if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_AMD64) {
+ WithColor::error(errs(), "llvm-objdump")
+ << "unsupported image machine type "
+ "(currently only AMD64 is supported).\n";
+ return;
+ }
+
+ std::vector<RelocationRef> Rels;
+ const RuntimeFunction *RFStart;
+ int NumRFs;
+ if (!getPDataSection(Obj, Rels, RFStart, NumRFs))
+ return;
+ ArrayRef<RuntimeFunction> RFs(RFStart, NumRFs);
+
+ bool IsExecutable = Rels.empty();
+ if (IsExecutable) {
+ for (const RuntimeFunction &RF : RFs)
+ printRuntimeFunction(Obj, RF);
+ return;
+ }
+
+ for (const RuntimeFunction &RF : RFs) {
+ uint64_t SectionOffset =
+ std::distance(RFs.begin(), &RF) * sizeof(RuntimeFunction);
+ printRuntimeFunctionRels(Obj, RF, SectionOffset, Rels);
+ }
+}
+
+void objdump::printCOFFFileHeader(const COFFObjectFile &Obj) {
+ COFFDumper CD(Obj);
+ const uint16_t Cha = Obj.getCharacteristics();
+ outs() << "Characteristics 0x" << Twine::utohexstr(Cha) << '\n';
+#define FLAG(F, Name) \
+ if (Cha & F) \
+ outs() << '\t' << Name << '\n';
+ FLAG(COFF::IMAGE_FILE_RELOCS_STRIPPED, "relocations stripped");
+ FLAG(COFF::IMAGE_FILE_EXECUTABLE_IMAGE, "executable");
+ FLAG(COFF::IMAGE_FILE_LINE_NUMS_STRIPPED, "line numbers stripped");
+ FLAG(COFF::IMAGE_FILE_LOCAL_SYMS_STRIPPED, "symbols stripped");
+ FLAG(COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE, "large address aware");
+ FLAG(COFF::IMAGE_FILE_BYTES_REVERSED_LO, "little endian");
+ FLAG(COFF::IMAGE_FILE_32BIT_MACHINE, "32 bit words");
+ FLAG(COFF::IMAGE_FILE_DEBUG_STRIPPED, "debugging information removed");
+ FLAG(COFF::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP,
+ "copy to swap file if on removable media");
+ FLAG(COFF::IMAGE_FILE_NET_RUN_FROM_SWAP,
+ "copy to swap file if on network media");
+ FLAG(COFF::IMAGE_FILE_SYSTEM, "system file");
+ FLAG(COFF::IMAGE_FILE_DLL, "DLL");
+ FLAG(COFF::IMAGE_FILE_UP_SYSTEM_ONLY, "run only on uniprocessor machine");
+ FLAG(COFF::IMAGE_FILE_BYTES_REVERSED_HI, "big endian");
+#undef FLAG
+
+ // TODO Support PE_IMAGE_DEBUG_TYPE_REPRO.
+ // Since ctime(3) returns a 26 character string of the form:
+ // "Sun Sep 16 01:03:52 1973\n\0"
+ // just print 24 characters.
+ const time_t Timestamp = Obj.getTimeDateStamp();
+ outs() << format("\nTime/Date %.24s\n", ctime(&Timestamp));
+
+ if (const pe32_header *Hdr = Obj.getPE32Header())
+ CD.printPEHeader<pe32_header>(*Hdr);
+ else if (const pe32plus_header *Hdr = Obj.getPE32PlusHeader())
+ CD.printPEHeader<pe32plus_header>(*Hdr);
+
+ printTLSDirectory(&Obj);
+ printLoadConfiguration(&Obj);
+ printImportTables(&Obj);
+ printExportTable(&Obj);
+}
+
+void objdump::printCOFFSymbolTable(const object::COFFImportFile *i) {
+ unsigned Index = 0;
+ bool IsCode = i->getCOFFImportHeader()->getType() == COFF::IMPORT_CODE;
+
+ for (const object::BasicSymbolRef &Sym : i->symbols()) {
+ std::string Name;
+ raw_string_ostream NS(Name);
+
+ cantFail(Sym.printName(NS));
+ NS.flush();
+
+ outs() << "[" << format("%2d", Index) << "]"
+ << "(sec " << format("%2d", 0) << ")"
+ << "(fl 0x00)" // Flag bits, which COFF doesn't have.
+ << "(ty " << format("%3x", (IsCode && Index) ? 32 : 0) << ")"
+ << "(scl " << format("%3x", 0) << ") "
+ << "(nx " << 0 << ") "
+ << "0x" << format("%08x", 0) << " " << Name << '\n';
+
+ ++Index;
+ }
+}
+
+void objdump::printCOFFSymbolTable(const COFFObjectFile *coff) {
+ for (unsigned SI = 0, SE = coff->getNumberOfSymbols(); SI != SE; ++SI) {
+ Expected<COFFSymbolRef> Symbol = coff->getSymbol(SI);
+ if (!Symbol)
+ reportError(Symbol.takeError(), coff->getFileName());
+
+ Expected<StringRef> NameOrErr = coff->getSymbolName(*Symbol);
+ if (!NameOrErr)
+ reportError(NameOrErr.takeError(), coff->getFileName());
+ StringRef Name = *NameOrErr;
+
+ outs() << "[" << format("%2d", SI) << "]"
+ << "(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()))
+ << ") "
+ << "(nx " << unsigned(Symbol->getNumberOfAuxSymbols()) << ") "
+ << "0x" << format("%08x", unsigned(Symbol->getValue())) << " "
+ << Name;
+ if (Demangle && Name.startswith("?")) {
+ int Status = -1;
+ char *DemangledSymbol =
+ microsoftDemangle(Name.data(), nullptr, nullptr, nullptr, &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()) {
+ const coff_aux_section_definition *asd;
+ if (Error E =
+ coff->getAuxSymbol<coff_aux_section_definition>(SI + 1, asd))
+ reportError(std::move(E), coff->getFileName());
+
+ int32_t AuxNumber = asd->getNumber(Symbol->isBigObj());
+
+ outs() << "AUX "
+ << format("scnlen 0x%x nreloc %d nlnno %d checksum 0x%x "
+ , unsigned(asd->Length)
+ , unsigned(asd->NumberOfRelocations)
+ , unsigned(asd->NumberOfLinenumbers)
+ , unsigned(asd->CheckSum))
+ << format("assoc %d comdat %d\n"
+ , unsigned(AuxNumber)
+ , unsigned(asd->Selection));
+ } else if (Symbol->isFileRecord()) {
+ const char *FileName;
+ if (Error E = coff->getAuxSymbol<char>(SI + 1, FileName))
+ reportError(std::move(E), coff->getFileName());
+
+ StringRef Name(FileName, Symbol->getNumberOfAuxSymbols() *
+ coff->getSymbolTableEntrySize());
+ outs() << "AUX " << Name.rtrim(StringRef("\0", 1)) << '\n';
+
+ SI = SI + Symbol->getNumberOfAuxSymbols();
+ break;
+ } else if (Symbol->isWeakExternal()) {
+ const coff_aux_weak_external *awe;
+ if (Error E = coff->getAuxSymbol<coff_aux_weak_external>(SI + 1, awe))
+ reportError(std::move(E), coff->getFileName());
+
+ outs() << "AUX " << format("indx %d srch %d\n",
+ static_cast<uint32_t>(awe->TagIndex),
+ static_cast<uint32_t>(awe->Characteristics));
+ } else {
+ outs() << "AUX Unknown\n";
+ }
+ }
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/COFFDump.h b/contrib/libs/llvm14/tools/llvm-objdump/COFFDump.h
new file mode 100644
index 00000000000..ffd39671deb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/COFFDump.h
@@ -0,0 +1,36 @@
+//===-- COFFDump.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJDUMP_COFFDUMP_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_COFFDUMP_H
+
+#include "llvm/ADT/SmallVector.h"
+
+namespace llvm {
+
+class Error;
+
+namespace object {
+class COFFObjectFile;
+class COFFImportFile;
+class RelocationRef;
+} // namespace object
+
+namespace objdump {
+Error getCOFFRelocationValueString(const object::COFFObjectFile *Obj,
+ const object::RelocationRef &Rel,
+ llvm::SmallVectorImpl<char> &Result);
+
+void printCOFFUnwindInfo(const object::COFFObjectFile *O);
+void printCOFFFileHeader(const object::COFFObjectFile &Obj);
+void printCOFFSymbolTable(const object::COFFImportFile *I);
+void printCOFFSymbolTable(const object::COFFObjectFile *O);
+} // namespace objdump
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/ELFDump.cpp b/contrib/libs/llvm14/tools/llvm-objdump/ELFDump.cpp
new file mode 100644
index 00000000000..ca73dafe2b8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/ELFDump.cpp
@@ -0,0 +1,395 @@
+//===-- ELFDump.cpp - ELF-specific dumper -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the ELF-specific dumper for llvm-objdump.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ELFDump.h"
+
+#include "llvm-objdump.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::objdump;
+
+template <class ELFT>
+static Expected<StringRef> getDynamicStrTab(const ELFFile<ELFT> &Elf) {
+ auto DynamicEntriesOrError = Elf.dynamicEntries();
+ if (!DynamicEntriesOrError)
+ return DynamicEntriesOrError.takeError();
+
+ for (const typename ELFT::Dyn &Dyn : *DynamicEntriesOrError) {
+ if (Dyn.d_tag == ELF::DT_STRTAB) {
+ auto MappedAddrOrError = Elf.toMappedAddr(Dyn.getPtr());
+ if (!MappedAddrOrError)
+ consumeError(MappedAddrOrError.takeError());
+ return StringRef(reinterpret_cast<const char *>(*MappedAddrOrError));
+ }
+ }
+
+ // If the dynamic segment is not present, we fall back on the sections.
+ auto SectionsOrError = Elf.sections();
+ if (!SectionsOrError)
+ return SectionsOrError.takeError();
+
+ for (const typename ELFT::Shdr &Sec : *SectionsOrError) {
+ if (Sec.sh_type == ELF::SHT_DYNSYM)
+ return Elf.getStringTableForSymtab(Sec);
+ }
+
+ return createError("dynamic string table not found");
+}
+
+template <class ELFT>
+static Error getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
+ const RelocationRef &RelRef,
+ SmallVectorImpl<char> &Result) {
+ const ELFFile<ELFT> &EF = Obj->getELFFile();
+ DataRefImpl Rel = RelRef.getRawDataRefImpl();
+ auto SecOrErr = EF.getSection(Rel.d.a);
+ if (!SecOrErr)
+ return SecOrErr.takeError();
+
+ 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.
+ //
+ // In SHT_REL case we would need to read the addend from section data.
+ // GNU objdump does not do that and we just follow for simplicity atm.
+ bool Undef = false;
+ if ((*SecOrErr)->sh_type == ELF::SHT_RELA) {
+ const typename ELFT::Rela *ERela = Obj->getRela(Rel);
+ Addend = ERela->r_addend;
+ Undef = ERela->getSymbol(false) == 0;
+ } else if ((*SecOrErr)->sh_type == ELF::SHT_REL) {
+ const typename ELFT::Rel *ERel = Obj->getRel(Rel);
+ Undef = ERel->getSymbol(false) == 0;
+ } else {
+ return make_error<BinaryError>();
+ }
+
+ // 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);
+
+ if (!Undef) {
+ symbol_iterator SI = RelRef.getSymbol();
+ Expected<const typename ELFT::Sym *> SymOrErr =
+ Obj->getSymbol(SI->getRawDataRefImpl());
+ // TODO: test this error.
+ if (!SymOrErr)
+ return SymOrErr.takeError();
+
+ if ((*SymOrErr)->getType() == ELF::STT_SECTION) {
+ Expected<section_iterator> SymSI = SI->getSection();
+ if (!SymSI)
+ return SymSI.takeError();
+ const typename ELFT::Shdr *SymSec =
+ Obj->getSection((*SymSI)->getRawDataRefImpl());
+ auto SecName = EF.getSectionName(*SymSec);
+ if (!SecName)
+ return SecName.takeError();
+ Fmt << *SecName;
+ } else {
+ Expected<StringRef> SymName = SI->getName();
+ if (!SymName)
+ return SymName.takeError();
+ if (Demangle)
+ Fmt << demangle(std::string(*SymName));
+ else
+ Fmt << *SymName;
+ }
+ } else {
+ Fmt << "*ABS*";
+ }
+ if (Addend != 0) {
+ Fmt << (Addend < 0
+ ? "-"
+ : "+") << format("0x%" PRIx64,
+ (Addend < 0 ? -(uint64_t)Addend : (uint64_t)Addend));
+ }
+ Fmt.flush();
+ Result.append(FmtBuf.begin(), FmtBuf.end());
+ return Error::success();
+}
+
+Error objdump::getELFRelocationValueString(const ELFObjectFileBase *Obj,
+ const RelocationRef &Rel,
+ SmallVectorImpl<char> &Result) {
+ if (auto *ELF32LE = dyn_cast<ELF32LEObjectFile>(Obj))
+ return getRelocationValueString(ELF32LE, Rel, Result);
+ if (auto *ELF64LE = dyn_cast<ELF64LEObjectFile>(Obj))
+ return getRelocationValueString(ELF64LE, Rel, Result);
+ if (auto *ELF32BE = dyn_cast<ELF32BEObjectFile>(Obj))
+ return getRelocationValueString(ELF32BE, Rel, Result);
+ auto *ELF64BE = cast<ELF64BEObjectFile>(Obj);
+ return getRelocationValueString(ELF64BE, Rel, Result);
+}
+
+template <class ELFT>
+static uint64_t getSectionLMA(const ELFFile<ELFT> &Obj,
+ const object::ELFSectionRef &Sec) {
+ auto PhdrRangeOrErr = Obj.program_headers();
+ if (!PhdrRangeOrErr)
+ report_fatal_error(Twine(toString(PhdrRangeOrErr.takeError())));
+
+ // Search for a PT_LOAD segment containing the requested section. Use this
+ // segment's p_addr to calculate the section's LMA.
+ for (const typename ELFT::Phdr &Phdr : *PhdrRangeOrErr)
+ if ((Phdr.p_type == ELF::PT_LOAD) && (Phdr.p_vaddr <= Sec.getAddress()) &&
+ (Phdr.p_vaddr + Phdr.p_memsz > Sec.getAddress()))
+ return Sec.getAddress() - Phdr.p_vaddr + Phdr.p_paddr;
+
+ // Return section's VMA if it isn't in a PT_LOAD segment.
+ return Sec.getAddress();
+}
+
+uint64_t objdump::getELFSectionLMA(const object::ELFSectionRef &Sec) {
+ if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Sec.getObject()))
+ return getSectionLMA(ELFObj->getELFFile(), Sec);
+ else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Sec.getObject()))
+ return getSectionLMA(ELFObj->getELFFile(), Sec);
+ else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Sec.getObject()))
+ return getSectionLMA(ELFObj->getELFFile(), Sec);
+ const auto *ELFObj = cast<ELF64BEObjectFile>(Sec.getObject());
+ return getSectionLMA(ELFObj->getELFFile(), Sec);
+}
+
+template <class ELFT>
+static void printDynamicSection(const ELFFile<ELFT> &Elf, StringRef Filename) {
+ auto DynamicEntriesOrErr = Elf.dynamicEntries();
+ if (!DynamicEntriesOrErr) {
+ reportWarning(toString(DynamicEntriesOrErr.takeError()), Filename);
+ return;
+ }
+ ArrayRef<typename ELFT::Dyn> DynamicEntries = *DynamicEntriesOrErr;
+
+ // Find the maximum tag name length to format the value column properly.
+ size_t MaxLen = 0;
+ for (const typename ELFT::Dyn &Dyn : DynamicEntries)
+ MaxLen = std::max(MaxLen, Elf.getDynamicTagAsString(Dyn.d_tag).size());
+ std::string TagFmt = " %-" + std::to_string(MaxLen) + "s ";
+
+ outs() << "\nDynamic Section:\n";
+ for (const typename ELFT::Dyn &Dyn : DynamicEntries) {
+ if (Dyn.d_tag == ELF::DT_NULL)
+ continue;
+
+ std::string Str = Elf.getDynamicTagAsString(Dyn.d_tag);
+ outs() << format(TagFmt.c_str(), Str.c_str());
+
+ const char *Fmt =
+ ELFT::Is64Bits ? "0x%016" PRIx64 "\n" : "0x%08" PRIx64 "\n";
+ if (Dyn.d_tag == ELF::DT_NEEDED || Dyn.d_tag == ELF::DT_RPATH ||
+ Dyn.d_tag == ELF::DT_RUNPATH || Dyn.d_tag == ELF::DT_SONAME ||
+ Dyn.d_tag == ELF::DT_AUXILIARY || Dyn.d_tag == ELF::DT_FILTER) {
+ Expected<StringRef> StrTabOrErr = getDynamicStrTab(Elf);
+ if (StrTabOrErr) {
+ const char *Data = StrTabOrErr.get().data();
+ outs() << (Data + Dyn.d_un.d_val) << "\n";
+ continue;
+ }
+ reportWarning(toString(StrTabOrErr.takeError()), Filename);
+ consumeError(StrTabOrErr.takeError());
+ }
+ outs() << format(Fmt, (uint64_t)Dyn.d_un.d_val);
+ }
+}
+
+template <class ELFT>
+static void printProgramHeaders(const ELFFile<ELFT> &Obj, StringRef FileName) {
+ outs() << "\nProgram Header:\n";
+ auto ProgramHeaderOrError = Obj.program_headers();
+ if (!ProgramHeaderOrError) {
+ reportWarning("unable to read program headers: " +
+ toString(ProgramHeaderOrError.takeError()),
+ FileName);
+ return;
+ }
+
+ for (const typename ELFT::Phdr &Phdr : *ProgramHeaderOrError) {
+ switch (Phdr.p_type) {
+ case ELF::PT_DYNAMIC:
+ outs() << " DYNAMIC ";
+ break;
+ case ELF::PT_GNU_EH_FRAME:
+ outs() << "EH_FRAME ";
+ break;
+ case ELF::PT_GNU_RELRO:
+ outs() << " RELRO ";
+ break;
+ case ELF::PT_GNU_PROPERTY:
+ outs() << " PROPERTY ";
+ break;
+ case ELF::PT_GNU_STACK:
+ outs() << " STACK ";
+ break;
+ case ELF::PT_INTERP:
+ outs() << " INTERP ";
+ break;
+ case ELF::PT_LOAD:
+ outs() << " LOAD ";
+ break;
+ case ELF::PT_NOTE:
+ outs() << " NOTE ";
+ break;
+ case ELF::PT_OPENBSD_BOOTDATA:
+ outs() << " OPENBSD_BOOTDATA ";
+ break;
+ case ELF::PT_OPENBSD_RANDOMIZE:
+ outs() << " OPENBSD_RANDOMIZE ";
+ break;
+ case ELF::PT_OPENBSD_WXNEEDED:
+ outs() << " OPENBSD_WXNEEDED ";
+ break;
+ case ELF::PT_PHDR:
+ outs() << " PHDR ";
+ break;
+ case ELF::PT_TLS:
+ outs() << " TLS ";
+ break;
+ default:
+ outs() << " UNKNOWN ";
+ }
+
+ const char *Fmt = ELFT::Is64Bits ? "0x%016" PRIx64 " " : "0x%08" PRIx64 " ";
+
+ outs() << "off " << format(Fmt, (uint64_t)Phdr.p_offset) << "vaddr "
+ << format(Fmt, (uint64_t)Phdr.p_vaddr) << "paddr "
+ << format(Fmt, (uint64_t)Phdr.p_paddr)
+ << format("align 2**%u\n",
+ countTrailingZeros<uint64_t>(Phdr.p_align))
+ << " filesz " << format(Fmt, (uint64_t)Phdr.p_filesz)
+ << "memsz " << format(Fmt, (uint64_t)Phdr.p_memsz) << "flags "
+ << ((Phdr.p_flags & ELF::PF_R) ? "r" : "-")
+ << ((Phdr.p_flags & ELF::PF_W) ? "w" : "-")
+ << ((Phdr.p_flags & ELF::PF_X) ? "x" : "-") << "\n";
+ }
+}
+
+template <class ELFT>
+static void printSymbolVersionDependency(ArrayRef<uint8_t> Contents,
+ StringRef StrTab) {
+ outs() << "\nVersion References:\n";
+
+ const uint8_t *Buf = Contents.data();
+ while (Buf) {
+ auto *Verneed = reinterpret_cast<const typename ELFT::Verneed *>(Buf);
+ outs() << " required from "
+ << StringRef(StrTab.drop_front(Verneed->vn_file).data()) << ":\n";
+
+ const uint8_t *BufAux = Buf + Verneed->vn_aux;
+ while (BufAux) {
+ auto *Vernaux = reinterpret_cast<const typename ELFT::Vernaux *>(BufAux);
+ outs() << " "
+ << format("0x%08" PRIx32 " ", (uint32_t)Vernaux->vna_hash)
+ << format("0x%02" PRIx16 " ", (uint16_t)Vernaux->vna_flags)
+ << format("%02" PRIu16 " ", (uint16_t)Vernaux->vna_other)
+ << StringRef(StrTab.drop_front(Vernaux->vna_name).data()) << '\n';
+ BufAux = Vernaux->vna_next ? BufAux + Vernaux->vna_next : nullptr;
+ }
+ Buf = Verneed->vn_next ? Buf + Verneed->vn_next : nullptr;
+ }
+}
+
+template <class ELFT>
+static void printSymbolVersionDefinition(const typename ELFT::Shdr &Shdr,
+ ArrayRef<uint8_t> Contents,
+ StringRef StrTab) {
+ outs() << "\nVersion definitions:\n";
+
+ const uint8_t *Buf = Contents.data();
+ uint32_t VerdefIndex = 1;
+ // sh_info contains the number of entries in the SHT_GNU_verdef section. To
+ // make the index column have consistent width, we should insert blank spaces
+ // according to sh_info.
+ uint16_t VerdefIndexWidth = std::to_string(Shdr.sh_info).size();
+ while (Buf) {
+ auto *Verdef = reinterpret_cast<const typename ELFT::Verdef *>(Buf);
+ outs() << format_decimal(VerdefIndex++, VerdefIndexWidth) << " "
+ << format("0x%02" PRIx16 " ", (uint16_t)Verdef->vd_flags)
+ << format("0x%08" PRIx32 " ", (uint32_t)Verdef->vd_hash);
+
+ const uint8_t *BufAux = Buf + Verdef->vd_aux;
+ uint16_t VerdauxIndex = 0;
+ while (BufAux) {
+ auto *Verdaux = reinterpret_cast<const typename ELFT::Verdaux *>(BufAux);
+ if (VerdauxIndex)
+ outs() << std::string(VerdefIndexWidth + 17, ' ');
+ outs() << StringRef(StrTab.drop_front(Verdaux->vda_name).data()) << '\n';
+ BufAux = Verdaux->vda_next ? BufAux + Verdaux->vda_next : nullptr;
+ ++VerdauxIndex;
+ }
+ Buf = Verdef->vd_next ? Buf + Verdef->vd_next : nullptr;
+ }
+}
+
+template <class ELFT>
+static void printSymbolVersionInfo(const ELFFile<ELFT> &Elf,
+ StringRef FileName) {
+ ArrayRef<typename ELFT::Shdr> Sections =
+ unwrapOrError(Elf.sections(), FileName);
+ for (const typename ELFT::Shdr &Shdr : Sections) {
+ if (Shdr.sh_type != ELF::SHT_GNU_verneed &&
+ Shdr.sh_type != ELF::SHT_GNU_verdef)
+ continue;
+
+ ArrayRef<uint8_t> Contents =
+ unwrapOrError(Elf.getSectionContents(Shdr), FileName);
+ const typename ELFT::Shdr *StrTabSec =
+ unwrapOrError(Elf.getSection(Shdr.sh_link), FileName);
+ StringRef StrTab = unwrapOrError(Elf.getStringTable(*StrTabSec), FileName);
+
+ if (Shdr.sh_type == ELF::SHT_GNU_verneed)
+ printSymbolVersionDependency<ELFT>(Contents, StrTab);
+ else
+ printSymbolVersionDefinition<ELFT>(Shdr, Contents, StrTab);
+ }
+}
+
+void objdump::printELFFileHeader(const object::ObjectFile *Obj) {
+ if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
+ printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
+ printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
+ printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
+ printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName());
+}
+
+void objdump::printELFDynamicSection(const object::ObjectFile *Obj) {
+ if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
+ printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
+ printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
+ printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
+ printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
+}
+
+void objdump::printELFSymbolVersionInfo(const object::ObjectFile *Obj) {
+ if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
+ printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
+ printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
+ printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
+ printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName());
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/ELFDump.h b/contrib/libs/llvm14/tools/llvm-objdump/ELFDump.h
new file mode 100644
index 00000000000..9b6b1f341cf
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/ELFDump.h
@@ -0,0 +1,39 @@
+//===-- ELFDump.h - ELF-specific dumper -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJDUMP_ELFDUMP_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_ELFDUMP_H
+
+#include "llvm/ADT/SmallVector.h"
+
+namespace llvm {
+
+class Error;
+
+namespace object {
+class ELFObjectFileBase;
+class ELFSectionRef;
+class ObjectFile;
+class RelocationRef;
+} // namespace object
+
+namespace objdump {
+
+Error getELFRelocationValueString(const object::ELFObjectFileBase *Obj,
+ const object::RelocationRef &Rel,
+ llvm::SmallVectorImpl<char> &Result);
+uint64_t getELFSectionLMA(const object::ELFSectionRef &Sec);
+
+void printELFFileHeader(const object::ObjectFile *O);
+void printELFDynamicSection(const object::ObjectFile *Obj);
+void printELFSymbolVersionInfo(const object::ObjectFile *Obj);
+
+} // namespace objdump
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/MachODump.cpp b/contrib/libs/llvm14/tools/llvm-objdump/MachODump.cpp
new file mode 100644
index 00000000000..918cf7e52c5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/MachODump.cpp
@@ -0,0 +1,10521 @@
+//===-- MachODump.cpp - Object file dumping utility for llvm --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the MachO-specific dumper for llvm-objdump.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachODump.h"
+
+#include "ObjdumpOptID.h"
+#include "llvm-objdump.h"
+#include "llvm-c/Disassembler.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Config/config.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrDesc.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/GraphWriter.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/MemoryBuffer.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>
+#include <system_error>
+
+#ifdef LLVM_HAVE_LIBXAR
+extern "C" {
+#error #include <xar/xar.h>
+}
+#endif
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::objdump;
+
+bool objdump::FirstPrivateHeader;
+bool objdump::ExportsTrie;
+bool objdump::Rebase;
+bool objdump::Rpaths;
+bool objdump::Bind;
+bool objdump::LazyBind;
+bool objdump::WeakBind;
+static bool UseDbg;
+static std::string DSYMFile;
+bool objdump::FullLeadingAddr;
+bool objdump::LeadingHeaders;
+bool objdump::UniversalHeaders;
+static bool ArchiveMemberOffsets;
+bool objdump::IndirectSymbols;
+bool objdump::DataInCode;
+bool objdump::FunctionStarts;
+bool objdump::LinkOptHints;
+bool objdump::InfoPlist;
+bool objdump::DylibsUsed;
+bool objdump::DylibId;
+bool objdump::Verbose;
+bool objdump::ObjcMetaData;
+std::string objdump::DisSymName;
+bool objdump::SymbolicOperands;
+static std::vector<std::string> ArchFlags;
+
+static bool ArchAll = false;
+static std::string ThumbTripleName;
+
+void objdump::parseMachOOptions(const llvm::opt::InputArgList &InputArgs) {
+ FirstPrivateHeader = InputArgs.hasArg(OBJDUMP_private_header);
+ ExportsTrie = InputArgs.hasArg(OBJDUMP_exports_trie);
+ Rebase = InputArgs.hasArg(OBJDUMP_rebase);
+ Rpaths = InputArgs.hasArg(OBJDUMP_rpaths);
+ Bind = InputArgs.hasArg(OBJDUMP_bind);
+ LazyBind = InputArgs.hasArg(OBJDUMP_lazy_bind);
+ WeakBind = InputArgs.hasArg(OBJDUMP_weak_bind);
+ UseDbg = InputArgs.hasArg(OBJDUMP_g);
+ DSYMFile = InputArgs.getLastArgValue(OBJDUMP_dsym_EQ).str();
+ FullLeadingAddr = InputArgs.hasArg(OBJDUMP_full_leading_addr);
+ LeadingHeaders = !InputArgs.hasArg(OBJDUMP_no_leading_headers);
+ UniversalHeaders = InputArgs.hasArg(OBJDUMP_universal_headers);
+ ArchiveMemberOffsets = InputArgs.hasArg(OBJDUMP_archive_member_offsets);
+ IndirectSymbols = InputArgs.hasArg(OBJDUMP_indirect_symbols);
+ DataInCode = InputArgs.hasArg(OBJDUMP_data_in_code);
+ FunctionStarts = InputArgs.hasArg(OBJDUMP_function_starts);
+ LinkOptHints = InputArgs.hasArg(OBJDUMP_link_opt_hints);
+ InfoPlist = InputArgs.hasArg(OBJDUMP_info_plist);
+ DylibsUsed = InputArgs.hasArg(OBJDUMP_dylibs_used);
+ DylibId = InputArgs.hasArg(OBJDUMP_dylib_id);
+ Verbose = !InputArgs.hasArg(OBJDUMP_non_verbose);
+ ObjcMetaData = InputArgs.hasArg(OBJDUMP_objc_meta_data);
+ DisSymName = InputArgs.getLastArgValue(OBJDUMP_dis_symname).str();
+ SymbolicOperands = !InputArgs.hasArg(OBJDUMP_no_symbolic_operands);
+ ArchFlags = InputArgs.getAllArgValues(OBJDUMP_arch_EQ);
+}
+
+static const Target *GetTarget(const MachOObjectFile *MachOObj,
+ const char **McpuDefault,
+ const Target **ThumbTarget) {
+ // Figure out the target triple.
+ Triple TT(TripleName);
+ if (TripleName.empty()) {
+ TT = MachOObj->getArchTriple(McpuDefault);
+ TripleName = TT.str();
+ }
+
+ if (TT.getArch() == Triple::arm) {
+ // We've inferred a 32-bit ARM target from the object file. All MachO CPUs
+ // that support ARM are also capable of Thumb mode.
+ Triple ThumbTriple = TT;
+ std::string ThumbName = (Twine("thumb") + TT.getArchName().substr(3)).str();
+ ThumbTriple.setArchName(ThumbName);
+ ThumbTripleName = ThumbTriple.str();
+ }
+
+ // Get the target specific parser.
+ std::string Error;
+ const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error);
+ if (TheTarget && ThumbTripleName.empty())
+ return TheTarget;
+
+ *ThumbTarget = TargetRegistry::lookupTarget(ThumbTripleName, Error);
+ if (*ThumbTarget)
+ return TheTarget;
+
+ WithColor::error(errs(), "llvm-objdump") << "unable to get target for '";
+ if (!TheTarget)
+ errs() << TripleName;
+ else
+ errs() << ThumbTripleName;
+ errs() << "', see --version and --triple.\n";
+ return nullptr;
+}
+
+namespace {
+struct SymbolSorter {
+ bool operator()(const SymbolRef &A, const SymbolRef &B) {
+ Expected<SymbolRef::Type> ATypeOrErr = A.getType();
+ if (!ATypeOrErr)
+ reportError(ATypeOrErr.takeError(), A.getObject()->getFileName());
+ SymbolRef::Type AType = *ATypeOrErr;
+ Expected<SymbolRef::Type> BTypeOrErr = B.getType();
+ if (!BTypeOrErr)
+ reportError(BTypeOrErr.takeError(), B.getObject()->getFileName());
+ SymbolRef::Type BType = *BTypeOrErr;
+ uint64_t AAddr =
+ (AType != SymbolRef::ST_Function) ? 0 : cantFail(A.getValue());
+ uint64_t BAddr =
+ (BType != SymbolRef::ST_Function) ? 0 : cantFail(B.getValue());
+ return AAddr < BAddr;
+ }
+};
+} // namespace
+
+// Types for the storted data in code table that is built before disassembly
+// and the predicate function to sort them.
+typedef std::pair<uint64_t, DiceRef> DiceTableEntry;
+typedef std::vector<DiceTableEntry> DiceTable;
+typedef DiceTable::iterator dice_table_iterator;
+
+#ifdef LLVM_HAVE_LIBXAR
+namespace {
+struct ScopedXarFile {
+ xar_t xar;
+ ScopedXarFile(const char *filename, int32_t flags)
+ : xar(xar_open(filename, flags)) {}
+ ~ScopedXarFile() {
+ if (xar)
+ xar_close(xar);
+ }
+ ScopedXarFile(const ScopedXarFile &) = delete;
+ ScopedXarFile &operator=(const ScopedXarFile &) = delete;
+ operator xar_t() { return xar; }
+};
+
+struct ScopedXarIter {
+ xar_iter_t iter;
+ ScopedXarIter() : iter(xar_iter_new()) {}
+ ~ScopedXarIter() {
+ if (iter)
+ xar_iter_free(iter);
+ }
+ ScopedXarIter(const ScopedXarIter &) = delete;
+ ScopedXarIter &operator=(const ScopedXarIter &) = delete;
+ operator xar_iter_t() { return iter; }
+};
+} // namespace
+#endif // defined(LLVM_HAVE_LIBXAR)
+
+// This is used to search for a data in code table entry for the PC being
+// disassembled. The j parameter has the PC in j.first. A single data in code
+// table entry can cover many bytes for each of its Kind's. So if the offset,
+// aka the i.first value, of the data in code table entry plus its Length
+// covers the PC being searched for this will return true. If not it will
+// return false.
+static bool compareDiceTableEntries(const DiceTableEntry &i,
+ const DiceTableEntry &j) {
+ uint16_t Length;
+ i.second.getLength(Length);
+
+ return j.first >= i.first && j.first < i.first + Length;
+}
+
+static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length,
+ unsigned short Kind) {
+ uint32_t Value, Size = 1;
+
+ switch (Kind) {
+ default:
+ case MachO::DICE_KIND_DATA:
+ if (Length >= 4) {
+ if (ShowRawInsn)
+ dumpBytes(makeArrayRef(bytes, 4), outs());
+ Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
+ outs() << "\t.long " << Value;
+ Size = 4;
+ } else if (Length >= 2) {
+ if (ShowRawInsn)
+ dumpBytes(makeArrayRef(bytes, 2), outs());
+ Value = bytes[1] << 8 | bytes[0];
+ outs() << "\t.short " << Value;
+ Size = 2;
+ } else {
+ if (ShowRawInsn)
+ dumpBytes(makeArrayRef(bytes, 2), outs());
+ Value = bytes[0];
+ outs() << "\t.byte " << Value;
+ Size = 1;
+ }
+ if (Kind == MachO::DICE_KIND_DATA)
+ outs() << "\t@ KIND_DATA\n";
+ else
+ outs() << "\t@ data in code kind = " << Kind << "\n";
+ break;
+ case MachO::DICE_KIND_JUMP_TABLE8:
+ if (ShowRawInsn)
+ dumpBytes(makeArrayRef(bytes, 1), outs());
+ Value = bytes[0];
+ outs() << "\t.byte " << format("%3u", Value) << "\t@ KIND_JUMP_TABLE8\n";
+ Size = 1;
+ break;
+ case MachO::DICE_KIND_JUMP_TABLE16:
+ if (ShowRawInsn)
+ dumpBytes(makeArrayRef(bytes, 2), outs());
+ Value = bytes[1] << 8 | bytes[0];
+ outs() << "\t.short " << format("%5u", Value & 0xffff)
+ << "\t@ KIND_JUMP_TABLE16\n";
+ Size = 2;
+ break;
+ case MachO::DICE_KIND_JUMP_TABLE32:
+ case MachO::DICE_KIND_ABS_JUMP_TABLE32:
+ if (ShowRawInsn)
+ dumpBytes(makeArrayRef(bytes, 4), outs());
+ Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
+ outs() << "\t.long " << Value;
+ if (Kind == MachO::DICE_KIND_JUMP_TABLE32)
+ outs() << "\t@ KIND_JUMP_TABLE32\n";
+ else
+ outs() << "\t@ KIND_ABS_JUMP_TABLE32\n";
+ Size = 4;
+ break;
+ }
+ return Size;
+}
+
+static void getSectionsAndSymbols(MachOObjectFile *MachOObj,
+ std::vector<SectionRef> &Sections,
+ std::vector<SymbolRef> &Symbols,
+ SmallVectorImpl<uint64_t> &FoundFns,
+ uint64_t &BaseSegmentAddress) {
+ const StringRef FileName = MachOObj->getFileName();
+ for (const SymbolRef &Symbol : MachOObj->symbols()) {
+ StringRef SymName = unwrapOrError(Symbol.getName(), FileName);
+ if (!SymName.startswith("ltmp"))
+ Symbols.push_back(Symbol);
+ }
+
+ append_range(Sections, MachOObj->sections());
+
+ bool BaseSegmentAddressSet = false;
+ for (const auto &Command : MachOObj->load_commands()) {
+ if (Command.C.cmd == MachO::LC_FUNCTION_STARTS) {
+ // We found a function starts segment, parse the addresses for later
+ // consumption.
+ MachO::linkedit_data_command LLC =
+ MachOObj->getLinkeditDataLoadCommand(Command);
+
+ MachOObj->ReadULEB128s(LLC.dataoff, FoundFns);
+ } else if (Command.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command SLC = MachOObj->getSegmentLoadCommand(Command);
+ StringRef SegName = SLC.segname;
+ if (!BaseSegmentAddressSet && SegName != "__PAGEZERO") {
+ BaseSegmentAddressSet = true;
+ BaseSegmentAddress = SLC.vmaddr;
+ }
+ } else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 SLC = MachOObj->getSegment64LoadCommand(Command);
+ StringRef SegName = SLC.segname;
+ if (!BaseSegmentAddressSet && SegName != "__PAGEZERO") {
+ BaseSegmentAddressSet = true;
+ BaseSegmentAddress = SLC.vmaddr;
+ }
+ }
+ }
+}
+
+static bool DumpAndSkipDataInCode(uint64_t PC, const uint8_t *bytes,
+ DiceTable &Dices, uint64_t &InstSize) {
+ // Check the data in code table here to see if this is data not an
+ // instruction to be disassembled.
+ DiceTable Dice;
+ Dice.push_back(std::make_pair(PC, DiceRef()));
+ dice_table_iterator DTI =
+ std::search(Dices.begin(), Dices.end(), Dice.begin(), Dice.end(),
+ compareDiceTableEntries);
+ if (DTI != Dices.end()) {
+ uint16_t Length;
+ DTI->second.getLength(Length);
+ uint16_t Kind;
+ DTI->second.getKind(Kind);
+ InstSize = DumpDataInCode(bytes, Length, Kind);
+ if ((Kind == MachO::DICE_KIND_JUMP_TABLE8) &&
+ (PC == (DTI->first + Length - 1)) && (Length & 1))
+ InstSize++;
+ return true;
+ }
+ return false;
+}
+
+static void printRelocationTargetName(const MachOObjectFile *O,
+ const MachO::any_relocation_info &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.
+ const StringRef FileName = O->getFileName();
+ if (O->isRelocationScattered(RE)) {
+ uint32_t Val = O->getPlainRelocationSymbolNum(RE);
+
+ for (const SymbolRef &Symbol : O->symbols()) {
+ uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName);
+ if (Addr != Val)
+ continue;
+ Fmt << unwrapOrError(Symbol.getName(), FileName);
+ return;
+ }
+
+ // If we couldn't find a symbol that this relocation refers to, try
+ // to find a section beginning instead.
+ for (const SectionRef &Section : ToolSectionFilter(*O)) {
+ uint64_t Addr = Section.getAddress();
+ if (Addr != Val)
+ continue;
+ StringRef NameOrErr = unwrapOrError(Section.getName(), O->getFileName());
+ Fmt << NameOrErr;
+ return;
+ }
+
+ Fmt << format("0x%x", Val);
+ return;
+ }
+
+ StringRef S;
+ bool isExtern = O->getPlainRelocationExternal(RE);
+ uint64_t Val = O->getPlainRelocationSymbolNum(RE);
+
+ if (O->getAnyRelocationType(RE) == MachO::ARM64_RELOC_ADDEND &&
+ (O->getArch() == Triple::aarch64 || O->getArch() == Triple::aarch64_be)) {
+ Fmt << format("0x%0" PRIx64, Val);
+ return;
+ }
+
+ if (isExtern) {
+ symbol_iterator SI = O->symbol_begin();
+ std::advance(SI, Val);
+ S = unwrapOrError(SI->getName(), FileName);
+ } else {
+ section_iterator SI = O->section_begin();
+ // Adjust for the fact that sections are 1-indexed.
+ if (Val == 0) {
+ Fmt << "0 (?,?)";
+ return;
+ }
+ uint32_t I = Val - 1;
+ while (I != 0 && SI != O->section_end()) {
+ --I;
+ std::advance(SI, 1);
+ }
+ if (SI == O->section_end()) {
+ Fmt << Val << " (?,?)";
+ } else {
+ if (Expected<StringRef> NameOrErr = SI->getName())
+ S = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+ }
+ }
+
+ Fmt << S;
+}
+
+Error objdump::getMachORelocationValueString(const MachOObjectFile *Obj,
+ const RelocationRef &RelRef,
+ SmallVectorImpl<char> &Result) {
+ DataRefImpl Rel = RelRef.getRawDataRefImpl();
+ MachO::any_relocation_info RE = Obj->getRelocation(Rel);
+
+ unsigned Arch = Obj->getArch();
+
+ std::string FmtBuf;
+ raw_string_ostream Fmt(FmtBuf);
+ unsigned Type = Obj->getAnyRelocationType(RE);
+ bool IsPCRel = Obj->getAnyRelocationPCRel(RE);
+
+ // Determine any addends that should be displayed with the relocation.
+ // These require decoding the relocation type, which is triple-specific.
+
+ // X86_64 has entirely custom relocation types.
+ if (Arch == Triple::x86_64) {
+ 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";
+ break;
+ }
+ case MachO::X86_64_RELOC_SUBTRACTOR: {
+ DataRefImpl RelNext = Rel;
+ Obj->moveRelocationNext(RelNext);
+ MachO::any_relocation_info RENext = Obj->getRelocation(RelNext);
+
+ // X86_64_RELOC_SUBTRACTOR must be followed by a relocation of type
+ // X86_64_RELOC_UNSIGNED.
+ // NOTE: Scattered relocations don't exist on x86_64.
+ unsigned RType = Obj->getAnyRelocationType(RENext);
+ if (RType != MachO::X86_64_RELOC_UNSIGNED)
+ reportError(Obj->getFileName(), "Expected X86_64_RELOC_UNSIGNED after "
+ "X86_64_RELOC_SUBTRACTOR.");
+
+ // 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);
+ break;
+ }
+ case MachO::X86_64_RELOC_TLV:
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "@TLV";
+ if (IsPCRel)
+ Fmt << "P";
+ break;
+ case MachO::X86_64_RELOC_SIGNED_1:
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "-1";
+ break;
+ case MachO::X86_64_RELOC_SIGNED_2:
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "-2";
+ break;
+ case MachO::X86_64_RELOC_SIGNED_4:
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "-4";
+ break;
+ default:
+ printRelocationTargetName(Obj, RE, Fmt);
+ break;
+ }
+ // X86 and ARM share some relocation types in common.
+ } else if (Arch == Triple::x86 || Arch == Triple::arm ||
+ Arch == Triple::ppc) {
+ // Generic relocation types...
+ switch (Type) {
+ case MachO::GENERIC_RELOC_PAIR: // prints no info
+ return Error::success();
+ case MachO::GENERIC_RELOC_SECTDIFF: {
+ DataRefImpl RelNext = Rel;
+ Obj->moveRelocationNext(RelNext);
+ MachO::any_relocation_info RENext = Obj->getRelocation(RelNext);
+
+ // X86 sect diff's must be followed by a relocation of type
+ // GENERIC_RELOC_PAIR.
+ unsigned RType = Obj->getAnyRelocationType(RENext);
+
+ if (RType != MachO::GENERIC_RELOC_PAIR)
+ reportError(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after "
+ "GENERIC_RELOC_SECTDIFF.");
+
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "-";
+ printRelocationTargetName(Obj, RENext, Fmt);
+ break;
+ }
+ }
+
+ if (Arch == Triple::x86 || Arch == Triple::ppc) {
+ switch (Type) {
+ case MachO::GENERIC_RELOC_LOCAL_SECTDIFF: {
+ DataRefImpl RelNext = Rel;
+ Obj->moveRelocationNext(RelNext);
+ MachO::any_relocation_info RENext = Obj->getRelocation(RelNext);
+
+ // X86 sect diff's must be followed by a relocation of type
+ // GENERIC_RELOC_PAIR.
+ unsigned RType = Obj->getAnyRelocationType(RENext);
+ if (RType != MachO::GENERIC_RELOC_PAIR)
+ reportError(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after "
+ "GENERIC_RELOC_LOCAL_SECTDIFF.");
+
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "-";
+ printRelocationTargetName(Obj, RENext, Fmt);
+ break;
+ }
+ case MachO::GENERIC_RELOC_TLV: {
+ printRelocationTargetName(Obj, RE, Fmt);
+ Fmt << "@TLV";
+ if (IsPCRel)
+ Fmt << "P";
+ break;
+ }
+ default:
+ printRelocationTargetName(Obj, RE, Fmt);
+ }
+ } else { // ARM-specific relocations
+ switch (Type) {
+ case MachO::ARM_RELOC_HALF:
+ case MachO::ARM_RELOC_HALF_SECTDIFF: {
+ // Half relocations steal a bit from the length field to encode
+ // whether this is an upper16 or a lower16 relocation.
+ bool isUpper = (Obj->getAnyRelocationLength(RE) & 0x1) == 1;
+
+ if (isUpper)
+ Fmt << ":upper16:(";
+ else
+ Fmt << ":lower16:(";
+ printRelocationTargetName(Obj, RE, Fmt);
+
+ DataRefImpl RelNext = Rel;
+ Obj->moveRelocationNext(RelNext);
+ MachO::any_relocation_info RENext = Obj->getRelocation(RelNext);
+
+ // ARM half relocs must be followed by a relocation of type
+ // ARM_RELOC_PAIR.
+ unsigned RType = Obj->getAnyRelocationType(RENext);
+ if (RType != MachO::ARM_RELOC_PAIR)
+ reportError(Obj->getFileName(), "Expected ARM_RELOC_PAIR after "
+ "ARM_RELOC_HALF");
+
+ // NOTE: The half of the target virtual address is stashed in the
+ // address field of the secondary relocation, but we can't reverse
+ // engineer the constant offset from it without decoding the movw/movt
+ // instruction to find the other half in its immediate field.
+
+ // 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 << ")";
+ break;
+ }
+ default: {
+ printRelocationTargetName(Obj, RE, Fmt);
+ }
+ }
+ }
+ } else
+ printRelocationTargetName(Obj, RE, Fmt);
+
+ Fmt.flush();
+ Result.append(FmtBuf.begin(), FmtBuf.end());
+ return Error::success();
+}
+
+static void PrintIndirectSymbolTable(MachOObjectFile *O, bool verbose,
+ uint32_t n, uint32_t count,
+ uint32_t stride, uint64_t addr) {
+ MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand();
+ uint32_t nindirectsyms = Dysymtab.nindirectsyms;
+ if (n > nindirectsyms)
+ outs() << " (entries start past the end of the indirect symbol "
+ "table) (reserved1 field greater than the table size)";
+ else if (n + count > nindirectsyms)
+ outs() << " (entries extends past the end of the indirect symbol "
+ "table)";
+ outs() << "\n";
+ uint32_t cputype = O->getHeader().cputype;
+ if (cputype & MachO::CPU_ARCH_ABI64)
+ outs() << "address index";
+ else
+ outs() << "address index";
+ if (verbose)
+ outs() << " name\n";
+ else
+ outs() << "\n";
+ for (uint32_t j = 0; j < count && n + j < nindirectsyms; j++) {
+ if (cputype & MachO::CPU_ARCH_ABI64)
+ outs() << format("0x%016" PRIx64, addr + j * stride) << " ";
+ else
+ outs() << format("0x%08" PRIx32, (uint32_t)addr + j * stride) << " ";
+ MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand();
+ uint32_t indirect_symbol = O->getIndirectSymbolTableEntry(Dysymtab, n + j);
+ if (indirect_symbol == MachO::INDIRECT_SYMBOL_LOCAL) {
+ outs() << "LOCAL\n";
+ continue;
+ }
+ if (indirect_symbol ==
+ (MachO::INDIRECT_SYMBOL_LOCAL | MachO::INDIRECT_SYMBOL_ABS)) {
+ outs() << "LOCAL ABSOLUTE\n";
+ continue;
+ }
+ if (indirect_symbol == MachO::INDIRECT_SYMBOL_ABS) {
+ outs() << "ABSOLUTE\n";
+ continue;
+ }
+ outs() << format("%5u ", indirect_symbol);
+ if (verbose) {
+ MachO::symtab_command Symtab = O->getSymtabLoadCommand();
+ if (indirect_symbol < Symtab.nsyms) {
+ symbol_iterator Sym = O->getSymbolByIndex(indirect_symbol);
+ SymbolRef Symbol = *Sym;
+ outs() << unwrapOrError(Symbol.getName(), O->getFileName());
+ } else {
+ outs() << "?";
+ }
+ }
+ outs() << "\n";
+ }
+}
+
+static void PrintIndirectSymbols(MachOObjectFile *O, bool verbose) {
+ for (const auto &Load : O->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = O->getSegment64LoadCommand(Load);
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section_64 Sec = O->getSection64(Load, J);
+ uint32_t section_type = Sec.flags & MachO::SECTION_TYPE;
+ if (section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS ||
+ section_type == MachO::S_LAZY_SYMBOL_POINTERS ||
+ section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS ||
+ section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS ||
+ section_type == MachO::S_SYMBOL_STUBS) {
+ uint32_t stride;
+ if (section_type == MachO::S_SYMBOL_STUBS)
+ stride = Sec.reserved2;
+ else
+ stride = 8;
+ if (stride == 0) {
+ outs() << "Can't print indirect symbols for (" << Sec.segname << ","
+ << Sec.sectname << ") "
+ << "(size of stubs in reserved2 field is zero)\n";
+ continue;
+ }
+ uint32_t count = Sec.size / stride;
+ outs() << "Indirect symbols for (" << Sec.segname << ","
+ << Sec.sectname << ") " << count << " entries";
+ uint32_t n = Sec.reserved1;
+ PrintIndirectSymbolTable(O, verbose, n, count, stride, Sec.addr);
+ }
+ }
+ } else if (Load.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command Seg = O->getSegmentLoadCommand(Load);
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section Sec = O->getSection(Load, J);
+ uint32_t section_type = Sec.flags & MachO::SECTION_TYPE;
+ if (section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS ||
+ section_type == MachO::S_LAZY_SYMBOL_POINTERS ||
+ section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS ||
+ section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS ||
+ section_type == MachO::S_SYMBOL_STUBS) {
+ uint32_t stride;
+ if (section_type == MachO::S_SYMBOL_STUBS)
+ stride = Sec.reserved2;
+ else
+ stride = 4;
+ if (stride == 0) {
+ outs() << "Can't print indirect symbols for (" << Sec.segname << ","
+ << Sec.sectname << ") "
+ << "(size of stubs in reserved2 field is zero)\n";
+ continue;
+ }
+ uint32_t count = Sec.size / stride;
+ outs() << "Indirect symbols for (" << Sec.segname << ","
+ << Sec.sectname << ") " << count << " entries";
+ uint32_t n = Sec.reserved1;
+ PrintIndirectSymbolTable(O, verbose, n, count, stride, Sec.addr);
+ }
+ }
+ }
+ }
+}
+
+static void PrintRType(const uint64_t cputype, const unsigned r_type) {
+ static char const *generic_r_types[] = {
+ "VANILLA ", "PAIR ", "SECTDIF ", "PBLAPTR ", "LOCSDIF ", "TLV ",
+ " 6 (?) ", " 7 (?) ", " 8 (?) ", " 9 (?) ", " 10 (?) ", " 11 (?) ",
+ " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) "
+ };
+ static char const *x86_64_r_types[] = {
+ "UNSIGND ", "SIGNED ", "BRANCH ", "GOT_LD ", "GOT ", "SUB ",
+ "SIGNED1 ", "SIGNED2 ", "SIGNED4 ", "TLV ", " 10 (?) ", " 11 (?) ",
+ " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) "
+ };
+ static char const *arm_r_types[] = {
+ "VANILLA ", "PAIR ", "SECTDIFF", "LOCSDIF ", "PBLAPTR ",
+ "BR24 ", "T_BR22 ", "T_BR32 ", "HALF ", "HALFDIF ",
+ " 10 (?) ", " 11 (?) ", " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) "
+ };
+ static char const *arm64_r_types[] = {
+ "UNSIGND ", "SUB ", "BR26 ", "PAGE21 ", "PAGOF12 ",
+ "GOTLDP ", "GOTLDPOF", "PTRTGOT ", "TLVLDP ", "TLVLDPOF",
+ "ADDEND ", " 11 (?) ", " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) "
+ };
+
+ if (r_type > 0xf){
+ outs() << format("%-7u", r_type) << " ";
+ return;
+ }
+ switch (cputype) {
+ case MachO::CPU_TYPE_I386:
+ outs() << generic_r_types[r_type];
+ break;
+ case MachO::CPU_TYPE_X86_64:
+ outs() << x86_64_r_types[r_type];
+ break;
+ case MachO::CPU_TYPE_ARM:
+ outs() << arm_r_types[r_type];
+ break;
+ case MachO::CPU_TYPE_ARM64:
+ case MachO::CPU_TYPE_ARM64_32:
+ outs() << arm64_r_types[r_type];
+ break;
+ default:
+ outs() << format("%-7u ", r_type);
+ }
+}
+
+static void PrintRLength(const uint64_t cputype, const unsigned r_type,
+ const unsigned r_length, const bool previous_arm_half){
+ if (cputype == MachO::CPU_TYPE_ARM &&
+ (r_type == MachO::ARM_RELOC_HALF ||
+ r_type == MachO::ARM_RELOC_HALF_SECTDIFF || previous_arm_half == true)) {
+ if ((r_length & 0x1) == 0)
+ outs() << "lo/";
+ else
+ outs() << "hi/";
+ if ((r_length & 0x1) == 0)
+ outs() << "arm ";
+ else
+ outs() << "thm ";
+ } else {
+ switch (r_length) {
+ case 0:
+ outs() << "byte ";
+ break;
+ case 1:
+ outs() << "word ";
+ break;
+ case 2:
+ outs() << "long ";
+ break;
+ case 3:
+ if (cputype == MachO::CPU_TYPE_X86_64)
+ outs() << "quad ";
+ else
+ outs() << format("?(%2d) ", r_length);
+ break;
+ default:
+ outs() << format("?(%2d) ", r_length);
+ }
+ }
+}
+
+static void PrintRelocationEntries(const MachOObjectFile *O,
+ const relocation_iterator Begin,
+ const relocation_iterator End,
+ const uint64_t cputype,
+ const bool verbose) {
+ const MachO::symtab_command Symtab = O->getSymtabLoadCommand();
+ 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);
+ const unsigned r_type = O->getAnyRelocationType(RE);
+ const bool r_scattered = O->isRelocationScattered(RE);
+ const unsigned r_pcrel = O->getAnyRelocationPCRel(RE);
+ const unsigned r_length = O->getAnyRelocationLength(RE);
+ const unsigned r_address = O->getAnyRelocationAddress(RE);
+ const bool r_extern = (r_scattered ? false :
+ O->getPlainRelocationExternal(RE));
+ const uint32_t r_value = (r_scattered ?
+ 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
+ if ((cputype == MachO::CPU_TYPE_I386 &&
+ r_type == MachO::GENERIC_RELOC_PAIR) ||
+ (cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR))
+ 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) {
+ if ((cputype == MachO::CPU_TYPE_ARM &&
+ r_type == MachO::ARM_RELOC_PAIR))
+ outs() << format(" half = 0x%04x ", (unsigned int)r_address);
+ } else if (cputype == MachO::CPU_TYPE_ARM &&
+ sectdiff_r_type == MachO::ARM_RELOC_HALF_SECTDIFF)
+ outs() << format(" other_half = 0x%04x ", (unsigned int)r_address);
+ if ((cputype == MachO::CPU_TYPE_I386 &&
+ (r_type == MachO::GENERIC_RELOC_SECTDIFF ||
+ r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF)) ||
+ (cputype == MachO::CPU_TYPE_ARM &&
+ (sectdiff_r_type == MachO::ARM_RELOC_SECTDIFF ||
+ sectdiff_r_type == MachO::ARM_RELOC_LOCAL_SECTDIFF ||
+ sectdiff_r_type == MachO::ARM_RELOC_HALF_SECTDIFF))) {
+ previous_sectdiff = true;
+ sectdiff_r_type = r_type;
+ } else {
+ previous_sectdiff = false;
+ sectdiff_r_type = 0;
+ }
+ if (cputype == MachO::CPU_TYPE_ARM &&
+ (r_type == MachO::ARM_RELOC_HALF ||
+ r_type == MachO::ARM_RELOC_HALF_SECTDIFF))
+ previous_arm_half = true;
+ else
+ previous_arm_half = false;
+ outs() << "\n";
+ }
+ else {
+ // scattered: address pcrel length extern type scattered value
+ outs() << format("%08x %1d %-2d n/a %-7d 1 0x%08x\n",
+ (unsigned int)r_address, r_pcrel, r_length, r_type,
+ (unsigned int)r_value);
+ }
+ }
+ else {
+ if (verbose) {
+ // plain: address
+ if (cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR)
+ 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);
+ else {
+ SymbolRef Symbol = *O->getSymbolByIndex(r_symbolnum);
+ Expected<StringRef> SymNameNext = Symbol.getName();
+ const char *name = nullptr;
+ if (SymNameNext)
+ name = SymNameNext->data();
+ if (name == nullptr)
+ outs() << format("?(%d)\n", r_symbolnum);
+ else
+ outs() << name << "\n";
+ }
+ }
+ else {
+ // plain: extern & type & scattered
+ outs() << "False ";
+ PrintRType(cputype, r_type);
+ outs() << "False ";
+
+ // plain: symbolnum/value
+ if (cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR)
+ outs() << format("other_half = 0x%04x\n", (unsigned int)r_address);
+ else if ((cputype == MachO::CPU_TYPE_ARM64 ||
+ cputype == MachO::CPU_TYPE_ARM64_32) &&
+ r_type == MachO::ARM64_RELOC_ADDEND)
+ outs() << format("addend = 0x%06x\n", (unsigned int)r_symbolnum);
+ else {
+ outs() << format("%d ", r_symbolnum);
+ if (r_symbolnum == MachO::R_ABS)
+ outs() << "R_ABS\n";
+ else {
+ // in this case, r_symbolnum is actually a 1-based section number
+ uint32_t nsects = O->section_end()->getRawDataRefImpl().d.a;
+ if (r_symbolnum > 0 && r_symbolnum <= nsects) {
+ object::DataRefImpl DRI;
+ DRI.d.a = r_symbolnum-1;
+ StringRef SegName = O->getSectionFinalSegmentName(DRI);
+ if (Expected<StringRef> NameOrErr = O->getSectionName(DRI))
+ outs() << "(" << SegName << "," << *NameOrErr << ")\n";
+ else
+ outs() << "(?,?)\n";
+ }
+ else {
+ outs() << "(?,?)\n";
+ }
+ }
+ }
+ }
+ if (cputype == MachO::CPU_TYPE_ARM &&
+ (r_type == MachO::ARM_RELOC_HALF ||
+ r_type == MachO::ARM_RELOC_HALF_SECTDIFF))
+ previous_arm_half = true;
+ else
+ previous_arm_half = false;
+ }
+ else {
+ // plain: address pcrel length extern type scattered symbolnum/section
+ outs() << format("%08x %1d %-2d %1d %-7d 0 %d\n",
+ (unsigned int)r_address, r_pcrel, r_length, r_extern,
+ r_type, r_symbolnum);
+ }
+ }
+ }
+}
+
+static void PrintRelocations(const MachOObjectFile *O, const bool verbose) {
+ const uint64_t cputype = O->getHeader().cputype;
+ const MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand();
+ if (Dysymtab.nextrel != 0) {
+ outs() << "External relocation information " << Dysymtab.nextrel
+ << " entries";
+ outs() << "\naddress pcrel length extern type scattered "
+ "symbolnum/value\n";
+ PrintRelocationEntries(O, O->extrel_begin(), O->extrel_end(), cputype,
+ verbose);
+ }
+ if (Dysymtab.nlocrel != 0) {
+ outs() << format("Local relocation information %u entries",
+ Dysymtab.nlocrel);
+ outs() << "\naddress pcrel length extern type scattered "
+ "symbolnum/value\n";
+ PrintRelocationEntries(O, O->locrel_begin(), O->locrel_end(), cputype,
+ verbose);
+ }
+ for (const auto &Load : O->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ const MachO::segment_command_64 Seg = O->getSegment64LoadCommand(Load);
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ const MachO::section_64 Sec = O->getSection64(Load, J);
+ if (Sec.nreloc != 0) {
+ DataRefImpl DRI;
+ DRI.d.a = J;
+ const StringRef SegName = O->getSectionFinalSegmentName(DRI);
+ if (Expected<StringRef> NameOrErr = O->getSectionName(DRI))
+ outs() << "Relocation information (" << SegName << "," << *NameOrErr
+ << format(") %u entries", Sec.nreloc);
+ else
+ outs() << "Relocation information (" << SegName << ",?) "
+ << format("%u entries", Sec.nreloc);
+ outs() << "\naddress pcrel length extern type scattered "
+ "symbolnum/value\n";
+ PrintRelocationEntries(O, O->section_rel_begin(DRI),
+ O->section_rel_end(DRI), cputype, verbose);
+ }
+ }
+ } else if (Load.C.cmd == MachO::LC_SEGMENT) {
+ const MachO::segment_command Seg = O->getSegmentLoadCommand(Load);
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ const MachO::section Sec = O->getSection(Load, J);
+ if (Sec.nreloc != 0) {
+ DataRefImpl DRI;
+ DRI.d.a = J;
+ const StringRef SegName = O->getSectionFinalSegmentName(DRI);
+ if (Expected<StringRef> NameOrErr = O->getSectionName(DRI))
+ outs() << "Relocation information (" << SegName << "," << *NameOrErr
+ << format(") %u entries", Sec.nreloc);
+ else
+ outs() << "Relocation information (" << SegName << ",?) "
+ << format("%u entries", Sec.nreloc);
+ outs() << "\naddress pcrel length extern type scattered "
+ "symbolnum/value\n";
+ PrintRelocationEntries(O, O->section_rel_begin(DRI),
+ O->section_rel_end(DRI), cputype, verbose);
+ }
+ }
+ }
+ }
+}
+
+static void PrintFunctionStarts(MachOObjectFile *O) {
+ uint64_t BaseSegmentAddress = 0;
+ for (const MachOObjectFile::LoadCommandInfo &Command : O->load_commands()) {
+ if (Command.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command SLC = O->getSegmentLoadCommand(Command);
+ if (StringRef(SLC.segname) == "__TEXT") {
+ BaseSegmentAddress = SLC.vmaddr;
+ break;
+ }
+ } else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 SLC = O->getSegment64LoadCommand(Command);
+ if (StringRef(SLC.segname) == "__TEXT") {
+ BaseSegmentAddress = SLC.vmaddr;
+ break;
+ }
+ }
+ }
+
+ SmallVector<uint64_t, 8> FunctionStarts;
+ for (const MachOObjectFile::LoadCommandInfo &LC : O->load_commands()) {
+ if (LC.C.cmd == MachO::LC_FUNCTION_STARTS) {
+ MachO::linkedit_data_command FunctionStartsLC =
+ O->getLinkeditDataLoadCommand(LC);
+ O->ReadULEB128s(FunctionStartsLC.dataoff, FunctionStarts);
+ break;
+ }
+ }
+
+ for (uint64_t S : FunctionStarts) {
+ uint64_t Addr = BaseSegmentAddress + S;
+ if (O->is64Bit())
+ outs() << format("%016" PRIx64, Addr) << "\n";
+ else
+ outs() << format("%08" PRIx32, static_cast<uint32_t>(Addr)) << "\n";
+ }
+}
+
+static void PrintDataInCodeTable(MachOObjectFile *O, bool verbose) {
+ MachO::linkedit_data_command DIC = O->getDataInCodeLoadCommand();
+ uint32_t nentries = DIC.datasize / sizeof(struct MachO::data_in_code_entry);
+ outs() << "Data in code table (" << nentries << " entries)\n";
+ outs() << "offset length kind\n";
+ for (dice_iterator DI = O->begin_dices(), DE = O->end_dices(); DI != DE;
+ ++DI) {
+ uint32_t Offset;
+ DI->getOffset(Offset);
+ outs() << format("0x%08" PRIx32, Offset) << " ";
+ uint16_t Length;
+ DI->getLength(Length);
+ outs() << format("%6u", Length) << " ";
+ uint16_t Kind;
+ DI->getKind(Kind);
+ if (verbose) {
+ switch (Kind) {
+ case MachO::DICE_KIND_DATA:
+ outs() << "DATA";
+ break;
+ case MachO::DICE_KIND_JUMP_TABLE8:
+ outs() << "JUMP_TABLE8";
+ break;
+ case MachO::DICE_KIND_JUMP_TABLE16:
+ outs() << "JUMP_TABLE16";
+ break;
+ case MachO::DICE_KIND_JUMP_TABLE32:
+ outs() << "JUMP_TABLE32";
+ break;
+ case MachO::DICE_KIND_ABS_JUMP_TABLE32:
+ outs() << "ABS_JUMP_TABLE32";
+ break;
+ default:
+ outs() << format("0x%04" PRIx32, Kind);
+ break;
+ }
+ } else
+ outs() << format("0x%04" PRIx32, Kind);
+ outs() << "\n";
+ }
+}
+
+static void PrintLinkOptHints(MachOObjectFile *O) {
+ MachO::linkedit_data_command LohLC = O->getLinkOptHintsLoadCommand();
+ const char *loh = O->getData().substr(LohLC.dataoff, 1).data();
+ uint32_t nloh = LohLC.datasize;
+ outs() << "Linker optimiztion hints (" << nloh << " total bytes)\n";
+ for (uint32_t i = 0; i < nloh;) {
+ unsigned n;
+ uint64_t identifier = decodeULEB128((const uint8_t *)(loh + i), &n);
+ i += n;
+ outs() << " identifier " << identifier << " ";
+ if (i >= nloh)
+ return;
+ switch (identifier) {
+ case 1:
+ outs() << "AdrpAdrp\n";
+ break;
+ case 2:
+ outs() << "AdrpLdr\n";
+ break;
+ case 3:
+ outs() << "AdrpAddLdr\n";
+ break;
+ case 4:
+ outs() << "AdrpLdrGotLdr\n";
+ break;
+ case 5:
+ outs() << "AdrpAddStr\n";
+ break;
+ case 6:
+ outs() << "AdrpLdrGotStr\n";
+ break;
+ case 7:
+ outs() << "AdrpAdd\n";
+ break;
+ case 8:
+ outs() << "AdrpLdrGot\n";
+ break;
+ default:
+ outs() << "Unknown identifier value\n";
+ break;
+ }
+ uint64_t narguments = decodeULEB128((const uint8_t *)(loh + i), &n);
+ i += n;
+ outs() << " narguments " << narguments << "\n";
+ if (i >= nloh)
+ return;
+
+ for (uint32_t j = 0; j < narguments; j++) {
+ uint64_t value = decodeULEB128((const uint8_t *)(loh + i), &n);
+ i += n;
+ outs() << "\tvalue " << format("0x%" PRIx64, value) << "\n";
+ if (i >= nloh)
+ return;
+ }
+ }
+}
+
+static void PrintDylibs(MachOObjectFile *O, bool JustId) {
+ unsigned Index = 0;
+ for (const auto &Load : O->load_commands()) {
+ if ((JustId && Load.C.cmd == MachO::LC_ID_DYLIB) ||
+ (!JustId && (Load.C.cmd == MachO::LC_ID_DYLIB ||
+ Load.C.cmd == MachO::LC_LOAD_DYLIB ||
+ Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB ||
+ Load.C.cmd == MachO::LC_REEXPORT_DYLIB ||
+ Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB ||
+ Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB))) {
+ MachO::dylib_command dl = O->getDylibIDLoadCommand(Load);
+ if (dl.dylib.name < dl.cmdsize) {
+ const char *p = (const char *)(Load.Ptr) + dl.dylib.name;
+ if (JustId)
+ outs() << p << "\n";
+ else {
+ outs() << "\t" << p;
+ outs() << " (compatibility version "
+ << ((dl.dylib.compatibility_version >> 16) & 0xffff) << "."
+ << ((dl.dylib.compatibility_version >> 8) & 0xff) << "."
+ << (dl.dylib.compatibility_version & 0xff) << ",";
+ outs() << " current version "
+ << ((dl.dylib.current_version >> 16) & 0xffff) << "."
+ << ((dl.dylib.current_version >> 8) & 0xff) << "."
+ << (dl.dylib.current_version & 0xff);
+ if (Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB)
+ outs() << ", weak";
+ if (Load.C.cmd == MachO::LC_REEXPORT_DYLIB)
+ outs() << ", reexport";
+ if (Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB)
+ outs() << ", upward";
+ if (Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB)
+ outs() << ", lazy";
+ outs() << ")\n";
+ }
+ } else {
+ outs() << "\tBad offset (" << dl.dylib.name << ") for name of ";
+ if (Load.C.cmd == MachO::LC_ID_DYLIB)
+ outs() << "LC_ID_DYLIB ";
+ else if (Load.C.cmd == MachO::LC_LOAD_DYLIB)
+ outs() << "LC_LOAD_DYLIB ";
+ else if (Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB)
+ outs() << "LC_LOAD_WEAK_DYLIB ";
+ else if (Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB)
+ outs() << "LC_LAZY_LOAD_DYLIB ";
+ else if (Load.C.cmd == MachO::LC_REEXPORT_DYLIB)
+ outs() << "LC_REEXPORT_DYLIB ";
+ else if (Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB)
+ outs() << "LC_LOAD_UPWARD_DYLIB ";
+ else
+ outs() << "LC_??? ";
+ outs() << "command " << Index++ << "\n";
+ }
+ }
+ }
+}
+
+static void printRpaths(MachOObjectFile *O) {
+ for (const auto &Command : O->load_commands()) {
+ if (Command.C.cmd == MachO::LC_RPATH) {
+ auto Rpath = O->getRpathCommand(Command);
+ const char *P = (const char *)(Command.Ptr) + Rpath.path;
+ outs() << P << "\n";
+ }
+ }
+}
+
+typedef DenseMap<uint64_t, StringRef> SymbolAddressMap;
+
+static void CreateSymbolAddressMap(MachOObjectFile *O,
+ SymbolAddressMap *AddrMap) {
+ // Create a map of symbol addresses to symbol names.
+ const StringRef FileName = O->getFileName();
+ for (const SymbolRef &Symbol : O->symbols()) {
+ SymbolRef::Type ST = unwrapOrError(Symbol.getType(), FileName);
+ if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data ||
+ ST == SymbolRef::ST_Other) {
+ uint64_t Address = cantFail(Symbol.getValue());
+ StringRef SymName = unwrapOrError(Symbol.getName(), FileName);
+ if (!SymName.startswith(".objc"))
+ (*AddrMap)[Address] = SymName;
+ }
+ }
+}
+
+// GuessSymbolName is passed the address of what might be a symbol and a
+// pointer to the SymbolAddressMap. It returns the name of a symbol
+// with that address or nullptr if no symbol is found with that address.
+static const char *GuessSymbolName(uint64_t value, SymbolAddressMap *AddrMap) {
+ const char *SymbolName = nullptr;
+ // A DenseMap can't lookup up some values.
+ if (value != 0xffffffffffffffffULL && value != 0xfffffffffffffffeULL) {
+ StringRef name = AddrMap->lookup(value);
+ if (!name.empty())
+ SymbolName = name.data();
+ }
+ return SymbolName;
+}
+
+static void DumpCstringChar(const char c) {
+ char p[2];
+ p[0] = c;
+ p[1] = '\0';
+ outs().write_escaped(p);
+}
+
+static void DumpCstringSection(MachOObjectFile *O, const char *sect,
+ uint32_t sect_size, uint64_t sect_addr,
+ bool print_addresses) {
+ for (uint32_t i = 0; i < sect_size; i++) {
+ if (print_addresses) {
+ if (O->is64Bit())
+ outs() << format("%016" PRIx64, sect_addr + i) << " ";
+ else
+ outs() << format("%08" PRIx64, sect_addr + i) << " ";
+ }
+ for (; i < sect_size && sect[i] != '\0'; i++)
+ DumpCstringChar(sect[i]);
+ if (i < sect_size && sect[i] == '\0')
+ outs() << "\n";
+ }
+}
+
+static void DumpLiteral4(uint32_t l, float f) {
+ outs() << format("0x%08" PRIx32, l);
+ if ((l & 0x7f800000) != 0x7f800000)
+ outs() << format(" (%.16e)\n", f);
+ else {
+ if (l == 0x7f800000)
+ outs() << " (+Infinity)\n";
+ else if (l == 0xff800000)
+ outs() << " (-Infinity)\n";
+ else if ((l & 0x00400000) == 0x00400000)
+ outs() << " (non-signaling Not-a-Number)\n";
+ else
+ outs() << " (signaling Not-a-Number)\n";
+ }
+}
+
+static void DumpLiteral4Section(MachOObjectFile *O, const char *sect,
+ uint32_t sect_size, uint64_t sect_addr,
+ bool print_addresses) {
+ for (uint32_t i = 0; i < sect_size; i += sizeof(float)) {
+ if (print_addresses) {
+ if (O->is64Bit())
+ outs() << format("%016" PRIx64, sect_addr + i) << " ";
+ else
+ outs() << format("%08" PRIx64, sect_addr + i) << " ";
+ }
+ float f;
+ memcpy(&f, sect + i, sizeof(float));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(f);
+ uint32_t l;
+ memcpy(&l, sect + i, sizeof(uint32_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(l);
+ DumpLiteral4(l, f);
+ }
+}
+
+static void DumpLiteral8(MachOObjectFile *O, uint32_t l0, uint32_t l1,
+ double d) {
+ outs() << format("0x%08" PRIx32, l0) << " " << format("0x%08" PRIx32, l1);
+ uint32_t Hi, Lo;
+ Hi = (O->isLittleEndian()) ? l1 : l0;
+ Lo = (O->isLittleEndian()) ? l0 : l1;
+
+ // Hi is the high word, so this is equivalent to if(isfinite(d))
+ if ((Hi & 0x7ff00000) != 0x7ff00000)
+ outs() << format(" (%.16e)\n", d);
+ else {
+ if (Hi == 0x7ff00000 && Lo == 0)
+ outs() << " (+Infinity)\n";
+ else if (Hi == 0xfff00000 && Lo == 0)
+ outs() << " (-Infinity)\n";
+ else if ((Hi & 0x00080000) == 0x00080000)
+ outs() << " (non-signaling Not-a-Number)\n";
+ else
+ outs() << " (signaling Not-a-Number)\n";
+ }
+}
+
+static void DumpLiteral8Section(MachOObjectFile *O, const char *sect,
+ uint32_t sect_size, uint64_t sect_addr,
+ bool print_addresses) {
+ for (uint32_t i = 0; i < sect_size; i += sizeof(double)) {
+ if (print_addresses) {
+ if (O->is64Bit())
+ outs() << format("%016" PRIx64, sect_addr + i) << " ";
+ else
+ outs() << format("%08" PRIx64, sect_addr + i) << " ";
+ }
+ double d;
+ memcpy(&d, sect + i, sizeof(double));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(d);
+ uint32_t l0, l1;
+ memcpy(&l0, sect + i, sizeof(uint32_t));
+ memcpy(&l1, sect + i + sizeof(uint32_t), sizeof(uint32_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost) {
+ sys::swapByteOrder(l0);
+ sys::swapByteOrder(l1);
+ }
+ DumpLiteral8(O, l0, l1, d);
+ }
+}
+
+static void DumpLiteral16(uint32_t l0, uint32_t l1, uint32_t l2, uint32_t l3) {
+ outs() << format("0x%08" PRIx32, l0) << " ";
+ outs() << format("0x%08" PRIx32, l1) << " ";
+ outs() << format("0x%08" PRIx32, l2) << " ";
+ outs() << format("0x%08" PRIx32, l3) << "\n";
+}
+
+static void DumpLiteral16Section(MachOObjectFile *O, const char *sect,
+ uint32_t sect_size, uint64_t sect_addr,
+ bool print_addresses) {
+ for (uint32_t i = 0; i < sect_size; i += 16) {
+ if (print_addresses) {
+ if (O->is64Bit())
+ outs() << format("%016" PRIx64, sect_addr + i) << " ";
+ else
+ outs() << format("%08" PRIx64, sect_addr + i) << " ";
+ }
+ uint32_t l0, l1, l2, l3;
+ memcpy(&l0, sect + i, sizeof(uint32_t));
+ memcpy(&l1, sect + i + sizeof(uint32_t), sizeof(uint32_t));
+ memcpy(&l2, sect + i + 2 * sizeof(uint32_t), sizeof(uint32_t));
+ memcpy(&l3, sect + i + 3 * sizeof(uint32_t), sizeof(uint32_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost) {
+ sys::swapByteOrder(l0);
+ sys::swapByteOrder(l1);
+ sys::swapByteOrder(l2);
+ sys::swapByteOrder(l3);
+ }
+ DumpLiteral16(l0, l1, l2, l3);
+ }
+}
+
+static void DumpLiteralPointerSection(MachOObjectFile *O,
+ const SectionRef &Section,
+ const char *sect, uint32_t sect_size,
+ uint64_t sect_addr,
+ bool print_addresses) {
+ // Collect the literal sections in this Mach-O file.
+ std::vector<SectionRef> LiteralSections;
+ for (const SectionRef &Section : O->sections()) {
+ DataRefImpl Ref = Section.getRawDataRefImpl();
+ uint32_t section_type;
+ if (O->is64Bit()) {
+ const MachO::section_64 Sec = O->getSection64(Ref);
+ section_type = Sec.flags & MachO::SECTION_TYPE;
+ } else {
+ const MachO::section Sec = O->getSection(Ref);
+ section_type = Sec.flags & MachO::SECTION_TYPE;
+ }
+ if (section_type == MachO::S_CSTRING_LITERALS ||
+ section_type == MachO::S_4BYTE_LITERALS ||
+ section_type == MachO::S_8BYTE_LITERALS ||
+ section_type == MachO::S_16BYTE_LITERALS)
+ LiteralSections.push_back(Section);
+ }
+
+ // Set the size of the literal pointer.
+ uint32_t lp_size = O->is64Bit() ? 8 : 4;
+
+ // Collect the external relocation symbols for the literal pointers.
+ std::vector<std::pair<uint64_t, SymbolRef>> Relocs;
+ for (const RelocationRef &Reloc : Section.relocations()) {
+ DataRefImpl Rel;
+ MachO::any_relocation_info RE;
+ bool isExtern = false;
+ Rel = Reloc.getRawDataRefImpl();
+ RE = O->getRelocation(Rel);
+ isExtern = O->getPlainRelocationExternal(RE);
+ if (isExtern) {
+ uint64_t RelocOffset = Reloc.getOffset();
+ symbol_iterator RelocSym = Reloc.getSymbol();
+ Relocs.push_back(std::make_pair(RelocOffset, *RelocSym));
+ }
+ }
+ array_pod_sort(Relocs.begin(), Relocs.end());
+
+ // Dump each literal pointer.
+ for (uint32_t i = 0; i < sect_size; i += lp_size) {
+ if (print_addresses) {
+ if (O->is64Bit())
+ outs() << format("%016" PRIx64, sect_addr + i) << " ";
+ else
+ outs() << format("%08" PRIx64, sect_addr + i) << " ";
+ }
+ uint64_t lp;
+ if (O->is64Bit()) {
+ memcpy(&lp, sect + i, sizeof(uint64_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(lp);
+ } else {
+ uint32_t li;
+ memcpy(&li, sect + i, sizeof(uint32_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(li);
+ lp = li;
+ }
+
+ // First look for an external relocation entry for this literal pointer.
+ auto Reloc = find_if(Relocs, [&](const std::pair<uint64_t, SymbolRef> &P) {
+ return P.first == i;
+ });
+ if (Reloc != Relocs.end()) {
+ symbol_iterator RelocSym = Reloc->second;
+ StringRef SymName = unwrapOrError(RelocSym->getName(), O->getFileName());
+ outs() << "external relocation entry for symbol:" << SymName << "\n";
+ continue;
+ }
+
+ // For local references see what the section the literal pointer points to.
+ auto Sect = find_if(LiteralSections, [&](const SectionRef &R) {
+ return lp >= R.getAddress() && lp < R.getAddress() + R.getSize();
+ });
+ if (Sect == LiteralSections.end()) {
+ outs() << format("0x%" PRIx64, lp) << " (not in a literal section)\n";
+ continue;
+ }
+
+ uint64_t SectAddress = Sect->getAddress();
+ uint64_t SectSize = Sect->getSize();
+
+ StringRef SectName;
+ Expected<StringRef> SectNameOrErr = Sect->getName();
+ if (SectNameOrErr)
+ SectName = *SectNameOrErr;
+ else
+ consumeError(SectNameOrErr.takeError());
+
+ DataRefImpl Ref = Sect->getRawDataRefImpl();
+ StringRef SegmentName = O->getSectionFinalSegmentName(Ref);
+ outs() << SegmentName << ":" << SectName << ":";
+
+ uint32_t section_type;
+ if (O->is64Bit()) {
+ const MachO::section_64 Sec = O->getSection64(Ref);
+ section_type = Sec.flags & MachO::SECTION_TYPE;
+ } else {
+ const MachO::section Sec = O->getSection(Ref);
+ section_type = Sec.flags & MachO::SECTION_TYPE;
+ }
+
+ StringRef BytesStr = unwrapOrError(Sect->getContents(), O->getFileName());
+
+ const char *Contents = reinterpret_cast<const char *>(BytesStr.data());
+
+ switch (section_type) {
+ case MachO::S_CSTRING_LITERALS:
+ for (uint64_t i = lp - SectAddress; i < SectSize && Contents[i] != '\0';
+ i++) {
+ DumpCstringChar(Contents[i]);
+ }
+ outs() << "\n";
+ break;
+ case MachO::S_4BYTE_LITERALS:
+ float f;
+ memcpy(&f, Contents + (lp - SectAddress), sizeof(float));
+ uint32_t l;
+ memcpy(&l, Contents + (lp - SectAddress), sizeof(uint32_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost) {
+ sys::swapByteOrder(f);
+ sys::swapByteOrder(l);
+ }
+ DumpLiteral4(l, f);
+ break;
+ case MachO::S_8BYTE_LITERALS: {
+ double d;
+ memcpy(&d, Contents + (lp - SectAddress), sizeof(double));
+ uint32_t l0, l1;
+ memcpy(&l0, Contents + (lp - SectAddress), sizeof(uint32_t));
+ memcpy(&l1, Contents + (lp - SectAddress) + sizeof(uint32_t),
+ sizeof(uint32_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost) {
+ sys::swapByteOrder(f);
+ sys::swapByteOrder(l0);
+ sys::swapByteOrder(l1);
+ }
+ DumpLiteral8(O, l0, l1, d);
+ break;
+ }
+ case MachO::S_16BYTE_LITERALS: {
+ uint32_t l0, l1, l2, l3;
+ memcpy(&l0, Contents + (lp - SectAddress), sizeof(uint32_t));
+ memcpy(&l1, Contents + (lp - SectAddress) + sizeof(uint32_t),
+ sizeof(uint32_t));
+ memcpy(&l2, Contents + (lp - SectAddress) + 2 * sizeof(uint32_t),
+ sizeof(uint32_t));
+ memcpy(&l3, Contents + (lp - SectAddress) + 3 * sizeof(uint32_t),
+ sizeof(uint32_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost) {
+ sys::swapByteOrder(l0);
+ sys::swapByteOrder(l1);
+ sys::swapByteOrder(l2);
+ sys::swapByteOrder(l3);
+ }
+ DumpLiteral16(l0, l1, l2, l3);
+ break;
+ }
+ }
+ }
+}
+
+static void DumpInitTermPointerSection(MachOObjectFile *O,
+ const SectionRef &Section,
+ const char *sect,
+ uint32_t sect_size, uint64_t sect_addr,
+ SymbolAddressMap *AddrMap,
+ bool verbose) {
+ uint32_t stride;
+ stride = (O->is64Bit()) ? sizeof(uint64_t) : sizeof(uint32_t);
+
+ // Collect the external relocation symbols for the pointers.
+ std::vector<std::pair<uint64_t, SymbolRef>> Relocs;
+ for (const RelocationRef &Reloc : Section.relocations()) {
+ DataRefImpl Rel;
+ MachO::any_relocation_info RE;
+ bool isExtern = false;
+ Rel = Reloc.getRawDataRefImpl();
+ RE = O->getRelocation(Rel);
+ isExtern = O->getPlainRelocationExternal(RE);
+ if (isExtern) {
+ uint64_t RelocOffset = Reloc.getOffset();
+ symbol_iterator RelocSym = Reloc.getSymbol();
+ Relocs.push_back(std::make_pair(RelocOffset, *RelocSym));
+ }
+ }
+ array_pod_sort(Relocs.begin(), Relocs.end());
+
+ for (uint32_t i = 0; i < sect_size; i += stride) {
+ const char *SymbolName = nullptr;
+ uint64_t p;
+ if (O->is64Bit()) {
+ outs() << format("0x%016" PRIx64, sect_addr + i * stride) << " ";
+ uint64_t pointer_value;
+ memcpy(&pointer_value, sect + i, stride);
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(pointer_value);
+ outs() << format("0x%016" PRIx64, pointer_value);
+ p = pointer_value;
+ } else {
+ outs() << format("0x%08" PRIx64, sect_addr + i * stride) << " ";
+ uint32_t pointer_value;
+ memcpy(&pointer_value, sect + i, stride);
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(pointer_value);
+ outs() << format("0x%08" PRIx32, pointer_value);
+ p = pointer_value;
+ }
+ if (verbose) {
+ // First look for an external relocation entry for this pointer.
+ auto Reloc = find_if(Relocs, [&](const std::pair<uint64_t, SymbolRef> &P) {
+ return P.first == i;
+ });
+ if (Reloc != Relocs.end()) {
+ symbol_iterator RelocSym = Reloc->second;
+ outs() << " " << unwrapOrError(RelocSym->getName(), O->getFileName());
+ } else {
+ SymbolName = GuessSymbolName(p, AddrMap);
+ if (SymbolName)
+ outs() << " " << SymbolName;
+ }
+ }
+ outs() << "\n";
+ }
+}
+
+static void DumpRawSectionContents(MachOObjectFile *O, const char *sect,
+ uint32_t size, uint64_t addr) {
+ uint32_t cputype = O->getHeader().cputype;
+ if (cputype == MachO::CPU_TYPE_I386 || cputype == MachO::CPU_TYPE_X86_64) {
+ uint32_t j;
+ for (uint32_t i = 0; i < size; i += j, addr += j) {
+ if (O->is64Bit())
+ outs() << format("%016" PRIx64, addr) << "\t";
+ else
+ outs() << format("%08" PRIx64, addr) << "\t";
+ for (j = 0; j < 16 && i + j < size; j++) {
+ uint8_t byte_word = *(sect + i + j);
+ outs() << format("%02" PRIx32, (uint32_t)byte_word) << " ";
+ }
+ outs() << "\n";
+ }
+ } else {
+ uint32_t j;
+ for (uint32_t i = 0; i < size; i += j, addr += j) {
+ if (O->is64Bit())
+ outs() << format("%016" PRIx64, addr) << "\t";
+ else
+ outs() << format("%08" PRIx64, addr) << "\t";
+ for (j = 0; j < 4 * sizeof(int32_t) && i + j < size;
+ j += sizeof(int32_t)) {
+ if (i + j + sizeof(int32_t) <= size) {
+ uint32_t long_word;
+ memcpy(&long_word, sect + i + j, sizeof(int32_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(long_word);
+ outs() << format("%08" PRIx32, long_word) << " ";
+ } else {
+ for (uint32_t k = 0; i + j + k < size; k++) {
+ uint8_t byte_word = *(sect + i + j + k);
+ outs() << format("%02" PRIx32, (uint32_t)byte_word) << " ";
+ }
+ }
+ }
+ outs() << "\n";
+ }
+ }
+}
+
+static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
+ StringRef DisSegName, StringRef DisSectName);
+static void DumpProtocolSection(MachOObjectFile *O, const char *sect,
+ uint32_t size, uint32_t addr);
+#ifdef LLVM_HAVE_LIBXAR
+static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
+ uint32_t size, bool verbose,
+ bool PrintXarHeader, bool PrintXarFileHeaders,
+ std::string XarMemberName);
+#endif // defined(LLVM_HAVE_LIBXAR)
+
+static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
+ bool verbose) {
+ SymbolAddressMap AddrMap;
+ if (verbose)
+ CreateSymbolAddressMap(O, &AddrMap);
+
+ for (unsigned i = 0; i < FilterSections.size(); ++i) {
+ StringRef DumpSection = FilterSections[i];
+ std::pair<StringRef, StringRef> DumpSegSectName;
+ DumpSegSectName = DumpSection.split(',');
+ StringRef DumpSegName, DumpSectName;
+ if (!DumpSegSectName.second.empty()) {
+ DumpSegName = DumpSegSectName.first;
+ DumpSectName = DumpSegSectName.second;
+ } else {
+ DumpSegName = "";
+ DumpSectName = DumpSegSectName.first;
+ }
+ for (const SectionRef &Section : O->sections()) {
+ StringRef SectName;
+ Expected<StringRef> SecNameOrErr = Section.getName();
+ if (SecNameOrErr)
+ SectName = *SecNameOrErr;
+ else
+ consumeError(SecNameOrErr.takeError());
+
+ if (!DumpSection.empty())
+ FoundSectionSet.insert(DumpSection);
+
+ DataRefImpl Ref = Section.getRawDataRefImpl();
+ StringRef SegName = O->getSectionFinalSegmentName(Ref);
+ if ((DumpSegName.empty() || SegName == DumpSegName) &&
+ (SectName == DumpSectName)) {
+
+ uint32_t section_flags;
+ if (O->is64Bit()) {
+ const MachO::section_64 Sec = O->getSection64(Ref);
+ section_flags = Sec.flags;
+
+ } else {
+ const MachO::section Sec = O->getSection(Ref);
+ section_flags = Sec.flags;
+ }
+ uint32_t section_type = section_flags & MachO::SECTION_TYPE;
+
+ StringRef BytesStr =
+ unwrapOrError(Section.getContents(), O->getFileName());
+ const char *sect = reinterpret_cast<const char *>(BytesStr.data());
+ uint32_t sect_size = BytesStr.size();
+ uint64_t sect_addr = Section.getAddress();
+
+ if (LeadingHeaders)
+ outs() << "Contents of (" << SegName << "," << SectName
+ << ") section\n";
+
+ if (verbose) {
+ if ((section_flags & MachO::S_ATTR_PURE_INSTRUCTIONS) ||
+ (section_flags & MachO::S_ATTR_SOME_INSTRUCTIONS)) {
+ DisassembleMachO(Filename, O, SegName, SectName);
+ continue;
+ }
+ if (SegName == "__TEXT" && SectName == "__info_plist") {
+ outs() << sect;
+ continue;
+ }
+ if (SegName == "__OBJC" && SectName == "__protocol") {
+ DumpProtocolSection(O, sect, sect_size, sect_addr);
+ continue;
+ }
+#ifdef LLVM_HAVE_LIBXAR
+ if (SegName == "__LLVM" && SectName == "__bundle") {
+ DumpBitcodeSection(O, sect, sect_size, verbose, SymbolicOperands,
+ ArchiveHeaders, "");
+ continue;
+ }
+#endif // defined(LLVM_HAVE_LIBXAR)
+ switch (section_type) {
+ case MachO::S_REGULAR:
+ DumpRawSectionContents(O, sect, sect_size, sect_addr);
+ break;
+ case MachO::S_ZEROFILL:
+ outs() << "zerofill section and has no contents in the file\n";
+ break;
+ case MachO::S_CSTRING_LITERALS:
+ DumpCstringSection(O, sect, sect_size, sect_addr, LeadingAddr);
+ break;
+ case MachO::S_4BYTE_LITERALS:
+ DumpLiteral4Section(O, sect, sect_size, sect_addr, LeadingAddr);
+ break;
+ case MachO::S_8BYTE_LITERALS:
+ DumpLiteral8Section(O, sect, sect_size, sect_addr, LeadingAddr);
+ break;
+ case MachO::S_16BYTE_LITERALS:
+ DumpLiteral16Section(O, sect, sect_size, sect_addr, LeadingAddr);
+ break;
+ case MachO::S_LITERAL_POINTERS:
+ DumpLiteralPointerSection(O, Section, sect, sect_size, sect_addr,
+ LeadingAddr);
+ break;
+ case MachO::S_MOD_INIT_FUNC_POINTERS:
+ case MachO::S_MOD_TERM_FUNC_POINTERS:
+ DumpInitTermPointerSection(O, Section, sect, sect_size, sect_addr,
+ &AddrMap, verbose);
+ break;
+ default:
+ outs() << "Unknown section type ("
+ << format("0x%08" PRIx32, section_type) << ")\n";
+ DumpRawSectionContents(O, sect, sect_size, sect_addr);
+ break;
+ }
+ } else {
+ if (section_type == MachO::S_ZEROFILL)
+ outs() << "zerofill section and has no contents in the file\n";
+ else
+ DumpRawSectionContents(O, sect, sect_size, sect_addr);
+ }
+ }
+ }
+ }
+}
+
+static void DumpInfoPlistSectionContents(StringRef Filename,
+ MachOObjectFile *O) {
+ for (const SectionRef &Section : O->sections()) {
+ StringRef SectName;
+ Expected<StringRef> SecNameOrErr = Section.getName();
+ if (SecNameOrErr)
+ SectName = *SecNameOrErr;
+ else
+ consumeError(SecNameOrErr.takeError());
+
+ DataRefImpl Ref = Section.getRawDataRefImpl();
+ StringRef SegName = O->getSectionFinalSegmentName(Ref);
+ if (SegName == "__TEXT" && SectName == "__info_plist") {
+ if (LeadingHeaders)
+ outs() << "Contents of (" << SegName << "," << SectName << ") section\n";
+ StringRef BytesStr =
+ unwrapOrError(Section.getContents(), O->getFileName());
+ const char *sect = reinterpret_cast<const char *>(BytesStr.data());
+ outs() << format("%.*s", BytesStr.size(), sect) << "\n";
+ return;
+ }
+ }
+}
+
+// checkMachOAndArchFlags() checks to see if the ObjectFile is a Mach-O file
+// and if it is and there is a list of architecture flags is specified then
+// check to make sure this Mach-O file is one of those architectures or all
+// architectures were specified. If not then an error is generated and this
+// routine returns false. Else it returns true.
+static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) {
+ auto *MachO = dyn_cast<MachOObjectFile>(O);
+
+ if (!MachO || ArchAll || ArchFlags.empty())
+ return true;
+
+ MachO::mach_header H;
+ MachO::mach_header_64 H_64;
+ Triple T;
+ const char *McpuDefault, *ArchFlag;
+ if (MachO->is64Bit()) {
+ H_64 = MachO->MachOObjectFile::getHeader64();
+ T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype,
+ &McpuDefault, &ArchFlag);
+ } else {
+ H = MachO->MachOObjectFile::getHeader();
+ T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype,
+ &McpuDefault, &ArchFlag);
+ }
+ const std::string ArchFlagName(ArchFlag);
+ if (!llvm::is_contained(ArchFlags, ArchFlagName)) {
+ WithColor::error(errs(), "llvm-objdump")
+ << Filename << ": no architecture specified.\n";
+ return false;
+ }
+ return true;
+}
+
+static void printObjcMetaData(MachOObjectFile *O, bool verbose);
+
+// ProcessMachO() is passed a single opened Mach-O file, which may be an
+// archive member and or in a slice of a universal file. It prints the
+// the file name and header info and then processes it according to the
+// command line options.
+static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF,
+ StringRef ArchiveMemberName = StringRef(),
+ StringRef ArchitectureName = StringRef()) {
+ // If we are doing some processing here on the Mach-O file print the header
+ // info. And don't print it otherwise like in the case of printing the
+ // UniversalHeaders or ArchiveHeaders.
+ if (Disassemble || Relocations || PrivateHeaders || ExportsTrie || Rebase ||
+ Bind || SymbolTable || LazyBind || WeakBind || IndirectSymbols ||
+ DataInCode || FunctionStarts || LinkOptHints || DylibsUsed || DylibId ||
+ Rpaths || ObjcMetaData || (!FilterSections.empty())) {
+ if (LeadingHeaders) {
+ outs() << Name;
+ if (!ArchiveMemberName.empty())
+ outs() << '(' << ArchiveMemberName << ')';
+ if (!ArchitectureName.empty())
+ outs() << " (architecture " << ArchitectureName << ")";
+ outs() << ":\n";
+ }
+ }
+ // To use the report_error() form with an ArchiveName and FileName set
+ // these up based on what is passed for Name and ArchiveMemberName.
+ StringRef ArchiveName;
+ StringRef FileName;
+ if (!ArchiveMemberName.empty()) {
+ ArchiveName = Name;
+ FileName = ArchiveMemberName;
+ } else {
+ ArchiveName = StringRef();
+ FileName = Name;
+ }
+
+ // 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.empty() || UnwindInfo)
+ if (Error Err = MachOOF->checkSymbolTable())
+ reportError(std::move(Err), FileName, ArchiveName, ArchitectureName);
+
+ if (DisassembleAll) {
+ for (const SectionRef &Section : MachOOF->sections()) {
+ StringRef SectName;
+ if (Expected<StringRef> NameOrErr = Section.getName())
+ SectName = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ 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");
+ else
+ DisassembleMachO(FileName, MachOOF, "__TEXT", "__text");
+ }
+ if (IndirectSymbols)
+ PrintIndirectSymbols(MachOOF, Verbose);
+ if (DataInCode)
+ PrintDataInCodeTable(MachOOF, Verbose);
+ if (FunctionStarts)
+ PrintFunctionStarts(MachOOF);
+ if (LinkOptHints)
+ PrintLinkOptHints(MachOOF);
+ if (Relocations)
+ PrintRelocations(MachOOF, Verbose);
+ if (SectionHeaders)
+ printSectionHeaders(MachOOF);
+ if (SectionContents)
+ printSectionContents(MachOOF);
+ if (!FilterSections.empty())
+ DumpSectionContents(FileName, MachOOF, Verbose);
+ if (InfoPlist)
+ DumpInfoPlistSectionContents(FileName, MachOOF);
+ if (DylibsUsed)
+ PrintDylibs(MachOOF, false);
+ if (DylibId)
+ PrintDylibs(MachOOF, true);
+ if (SymbolTable)
+ printSymbolTable(MachOOF, ArchiveName, ArchitectureName);
+ if (UnwindInfo)
+ printMachOUnwindInfo(MachOOF);
+ if (PrivateHeaders) {
+ printMachOFileHeader(MachOOF);
+ printMachOLoadCommands(MachOOF);
+ }
+ if (FirstPrivateHeader)
+ printMachOFileHeader(MachOOF);
+ if (ObjcMetaData)
+ printObjcMetaData(MachOOF, Verbose);
+ if (ExportsTrie)
+ printExportsTrie(MachOOF);
+ if (Rebase)
+ printRebaseTable(MachOOF);
+ if (Rpaths)
+ printRpaths(MachOOF);
+ if (Bind)
+ printBindTable(MachOOF);
+ if (LazyBind)
+ printLazyBindTable(MachOOF);
+ if (WeakBind)
+ printWeakBindTable(MachOOF);
+
+ if (DwarfDumpType != DIDT_Null) {
+ std::unique_ptr<DIContext> DICtx = DWARFContext::create(*MachOOF);
+ // Dump the complete DWARF structure.
+ DIDumpOptions DumpOpts;
+ DumpOpts.DumpType = DwarfDumpType;
+ DICtx->dump(outs(), DumpOpts);
+ }
+}
+
+// printUnknownCPUType() helps print_fat_headers for unknown CPU's.
+static void printUnknownCPUType(uint32_t cputype, uint32_t cpusubtype) {
+ outs() << " cputype (" << cputype << ")\n";
+ outs() << " cpusubtype (" << cpusubtype << ")\n";
+}
+
+// printCPUType() helps print_fat_headers by printing the cputype and
+// pusubtype (symbolically for the one's it knows about).
+static void printCPUType(uint32_t cputype, uint32_t cpusubtype) {
+ switch (cputype) {
+ case MachO::CPU_TYPE_I386:
+ switch (cpusubtype) {
+ case MachO::CPU_SUBTYPE_I386_ALL:
+ outs() << " cputype CPU_TYPE_I386\n";
+ outs() << " cpusubtype CPU_SUBTYPE_I386_ALL\n";
+ break;
+ default:
+ printUnknownCPUType(cputype, cpusubtype);
+ break;
+ }
+ break;
+ case MachO::CPU_TYPE_X86_64:
+ switch (cpusubtype) {
+ case MachO::CPU_SUBTYPE_X86_64_ALL:
+ outs() << " cputype CPU_TYPE_X86_64\n";
+ outs() << " cpusubtype CPU_SUBTYPE_X86_64_ALL\n";
+ break;
+ case MachO::CPU_SUBTYPE_X86_64_H:
+ outs() << " cputype CPU_TYPE_X86_64\n";
+ outs() << " cpusubtype CPU_SUBTYPE_X86_64_H\n";
+ break;
+ default:
+ printUnknownCPUType(cputype, cpusubtype);
+ break;
+ }
+ break;
+ case MachO::CPU_TYPE_ARM:
+ switch (cpusubtype) {
+ case MachO::CPU_SUBTYPE_ARM_ALL:
+ outs() << " cputype CPU_TYPE_ARM\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM_ALL\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V4T:
+ outs() << " cputype CPU_TYPE_ARM\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM_V4T\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V5TEJ:
+ outs() << " cputype CPU_TYPE_ARM\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM_V5TEJ\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_XSCALE:
+ outs() << " cputype CPU_TYPE_ARM\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM_XSCALE\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V6:
+ outs() << " cputype CPU_TYPE_ARM\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM_V6\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V6M:
+ outs() << " cputype CPU_TYPE_ARM\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM_V6M\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V7:
+ outs() << " cputype CPU_TYPE_ARM\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM_V7\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V7EM:
+ outs() << " cputype CPU_TYPE_ARM\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM_V7EM\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V7K:
+ outs() << " cputype CPU_TYPE_ARM\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM_V7K\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V7M:
+ outs() << " cputype CPU_TYPE_ARM\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM_V7M\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V7S:
+ outs() << " cputype CPU_TYPE_ARM\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM_V7S\n";
+ break;
+ default:
+ printUnknownCPUType(cputype, cpusubtype);
+ break;
+ }
+ break;
+ case MachO::CPU_TYPE_ARM64:
+ switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) {
+ case MachO::CPU_SUBTYPE_ARM64_ALL:
+ outs() << " cputype CPU_TYPE_ARM64\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM64_ALL\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM64_V8:
+ outs() << " cputype CPU_TYPE_ARM64\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM64_V8\n";
+ break;
+ case MachO::CPU_SUBTYPE_ARM64E:
+ outs() << " cputype CPU_TYPE_ARM64\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM64E\n";
+ break;
+ default:
+ printUnknownCPUType(cputype, cpusubtype);
+ break;
+ }
+ break;
+ case MachO::CPU_TYPE_ARM64_32:
+ switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) {
+ case MachO::CPU_SUBTYPE_ARM64_32_V8:
+ outs() << " cputype CPU_TYPE_ARM64_32\n";
+ outs() << " cpusubtype CPU_SUBTYPE_ARM64_32_V8\n";
+ break;
+ default:
+ printUnknownCPUType(cputype, cpusubtype);
+ break;
+ }
+ break;
+ default:
+ printUnknownCPUType(cputype, cpusubtype);
+ break;
+ }
+}
+
+static void printMachOUniversalHeaders(const object::MachOUniversalBinary *UB,
+ bool verbose) {
+ outs() << "Fat headers\n";
+ if (verbose) {
+ if (UB->getMagic() == MachO::FAT_MAGIC)
+ outs() << "fat_magic FAT_MAGIC\n";
+ else // UB->getMagic() == MachO::FAT_MAGIC_64
+ outs() << "fat_magic FAT_MAGIC_64\n";
+ } else
+ outs() << "fat_magic " << format("0x%" PRIx32, MachO::FAT_MAGIC) << "\n";
+
+ uint32_t nfat_arch = UB->getNumberOfObjects();
+ StringRef Buf = UB->getData();
+ uint64_t size = Buf.size();
+ uint64_t big_size = sizeof(struct MachO::fat_header) +
+ nfat_arch * sizeof(struct MachO::fat_arch);
+ outs() << "nfat_arch " << UB->getNumberOfObjects();
+ if (nfat_arch == 0)
+ outs() << " (malformed, contains zero architecture types)\n";
+ else if (big_size > size)
+ outs() << " (malformed, architectures past end of file)\n";
+ else
+ outs() << "\n";
+
+ for (uint32_t i = 0; i < nfat_arch; ++i) {
+ MachOUniversalBinary::ObjectForArch OFA(UB, i);
+ uint32_t cputype = OFA.getCPUType();
+ uint32_t cpusubtype = OFA.getCPUSubType();
+ outs() << "architecture ";
+ for (uint32_t j = 0; i != 0 && j <= i - 1; j++) {
+ MachOUniversalBinary::ObjectForArch other_OFA(UB, j);
+ uint32_t other_cputype = other_OFA.getCPUType();
+ uint32_t other_cpusubtype = other_OFA.getCPUSubType();
+ if (cputype != 0 && cpusubtype != 0 && cputype == other_cputype &&
+ (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) ==
+ (other_cpusubtype & ~MachO::CPU_SUBTYPE_MASK)) {
+ outs() << "(illegal duplicate architecture) ";
+ break;
+ }
+ }
+ if (verbose) {
+ outs() << OFA.getArchFlagName() << "\n";
+ printCPUType(cputype, cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
+ } else {
+ outs() << i << "\n";
+ outs() << " cputype " << cputype << "\n";
+ outs() << " cpusubtype " << (cpusubtype & ~MachO::CPU_SUBTYPE_MASK)
+ << "\n";
+ }
+ if (verbose &&
+ (cpusubtype & MachO::CPU_SUBTYPE_MASK) == MachO::CPU_SUBTYPE_LIB64)
+ outs() << " capabilities CPU_SUBTYPE_LIB64\n";
+ else
+ outs() << " capabilities "
+ << format("0x%" PRIx32,
+ (cpusubtype & MachO::CPU_SUBTYPE_MASK) >> 24) << "\n";
+ outs() << " offset " << OFA.getOffset();
+ if (OFA.getOffset() > size)
+ outs() << " (past end of file)";
+ if (OFA.getOffset() % (1ull << OFA.getAlign()) != 0)
+ outs() << " (not aligned on it's alignment (2^" << OFA.getAlign() << ")";
+ outs() << "\n";
+ outs() << " size " << OFA.getSize();
+ big_size = OFA.getOffset() + OFA.getSize();
+ if (big_size > size)
+ outs() << " (past end of file)";
+ outs() << "\n";
+ outs() << " align 2^" << OFA.getAlign() << " (" << (1 << OFA.getAlign())
+ << ")\n";
+ }
+}
+
+static void printArchiveChild(StringRef Filename, const Archive::Child &C,
+ size_t ChildIndex, bool verbose,
+ bool print_offset,
+ StringRef ArchitectureName = StringRef()) {
+ if (print_offset)
+ outs() << C.getChildOffset() << "\t";
+ sys::fs::perms Mode =
+ unwrapOrError(C.getAccessMode(), getFileNameForError(C, ChildIndex),
+ Filename, ArchitectureName);
+ if (verbose) {
+ // FIXME: this first dash, "-", is for (Mode & S_IFMT) == S_IFREG.
+ // But there is nothing in sys::fs::perms for S_IFMT or S_IFREG.
+ outs() << "-";
+ outs() << ((Mode & sys::fs::owner_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::owner_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::owner_exe) ? "x" : "-");
+ outs() << ((Mode & sys::fs::group_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::group_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::group_exe) ? "x" : "-");
+ outs() << ((Mode & sys::fs::others_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::others_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::others_exe) ? "x" : "-");
+ } else {
+ outs() << format("0%o ", Mode);
+ }
+
+ outs() << format("%3d/%-3d %5" PRId64 " ",
+ unwrapOrError(C.getUID(), getFileNameForError(C, ChildIndex),
+ Filename, ArchitectureName),
+ unwrapOrError(C.getGID(), getFileNameForError(C, ChildIndex),
+ Filename, ArchitectureName),
+ unwrapOrError(C.getRawSize(),
+ getFileNameForError(C, ChildIndex), Filename,
+ ArchitectureName));
+
+ StringRef RawLastModified = C.getRawLastModified();
+ if (verbose) {
+ unsigned Seconds;
+ if (RawLastModified.getAsInteger(10, Seconds))
+ outs() << "(date: \"" << RawLastModified
+ << "\" contains non-decimal chars) ";
+ else {
+ // Since cime(3) returns a 26 character string of the form:
+ // "Sun Sep 16 01:03:52 1973\n\0"
+ // just print 24 characters.
+ time_t t = Seconds;
+ outs() << format("%.24s ", ctime(&t));
+ }
+ } else {
+ outs() << RawLastModified << " ";
+ }
+
+ if (verbose) {
+ Expected<StringRef> NameOrErr = C.getName();
+ if (!NameOrErr) {
+ consumeError(NameOrErr.takeError());
+ outs() << unwrapOrError(C.getRawName(),
+ getFileNameForError(C, ChildIndex), Filename,
+ ArchitectureName)
+ << "\n";
+ } else {
+ StringRef Name = NameOrErr.get();
+ outs() << Name << "\n";
+ }
+ } else {
+ outs() << unwrapOrError(C.getRawName(), getFileNameForError(C, ChildIndex),
+ Filename, ArchitectureName)
+ << "\n";
+ }
+}
+
+static void printArchiveHeaders(StringRef Filename, Archive *A, bool verbose,
+ bool print_offset,
+ StringRef ArchitectureName = StringRef()) {
+ Error Err = Error::success();
+ size_t I = 0;
+ for (const auto &C : A->children(Err, false))
+ printArchiveChild(Filename, C, I++, verbose, print_offset,
+ ArchitectureName);
+
+ if (Err)
+ reportError(std::move(Err), Filename, "", ArchitectureName);
+}
+
+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])) {
+ 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 objdump::parseInputMachO(StringRef Filename) {
+ if (!ValidateArchFlags())
+ return;
+
+ // Attempt to open the binary.
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Filename);
+ if (!BinaryOrErr) {
+ if (Error E = isNotObjectErrorInvalidFileType(BinaryOrErr.takeError()))
+ reportError(std::move(E), Filename);
+ else
+ outs() << Filename << ": is not an object file\n";
+ return;
+ }
+ Binary &Bin = *BinaryOrErr.get().getBinary();
+
+ if (Archive *A = dyn_cast<Archive>(&Bin)) {
+ outs() << "Archive : " << Filename << "\n";
+ if (ArchiveHeaders)
+ printArchiveHeaders(Filename, A, Verbose, ArchiveMemberOffsets);
+
+ Error Err = Error::success();
+ unsigned I = -1;
+ for (auto &C : A->children(Err)) {
+ ++I;
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ reportError(std::move(E), getFileNameForError(C, I), Filename);
+ continue;
+ }
+ if (MachOObjectFile *O = dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) {
+ if (!checkMachOAndArchFlags(O, Filename))
+ return;
+ ProcessMachO(Filename, O, O->getFileName());
+ }
+ }
+ if (Err)
+ reportError(std::move(Err), Filename);
+ return;
+ }
+ if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) {
+ parseInputMachO(UB);
+ return;
+ }
+ if (ObjectFile *O = dyn_cast<ObjectFile>(&Bin)) {
+ if (!checkMachOAndArchFlags(O, Filename))
+ return;
+ 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 objdump::parseInputMachO(MachOUniversalBinary *UB) {
+ if (!ValidateArchFlags())
+ return;
+
+ auto Filename = UB->getFileName();
+
+ if (UniversalHeaders)
+ printMachOUniversalHeaders(UB, Verbose);
+
+ // 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 (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 (Error E = isNotObjectErrorInvalidFileType(
+ ObjOrErr.takeError())) {
+ reportError(std::move(E), "", Filename, 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(), Verbose,
+ ArchiveMemberOffsets, ArchitectureName);
+ Error Err = Error::success();
+ unsigned I = -1;
+ for (auto &C : A->children(Err)) {
+ ++I;
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (Error E =
+ isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ reportError(std::move(E), getFileNameForError(C, I), Filename,
+ ArchitectureName);
+ continue;
+ }
+ if (MachOObjectFile *O =
+ dyn_cast<MachOObjectFile>(&*ChildOrErr.get()))
+ ProcessMachO(Filename, O, O->getFileName(), ArchitectureName);
+ }
+ if (Err)
+ reportError(std::move(Err), Filename);
+ } else {
+ consumeError(AOrErr.takeError());
+ reportError(Filename,
+ "Mach-O universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a Mach-O file or an archive file");
+ }
+ }
+ }
+ if (!ArchFound) {
+ WithColor::error(errs(), "llvm-objdump")
+ << "file: " + Filename + " does not contain "
+ << "architecture: " + ArchFlags[i] + "\n";
+ return;
+ }
+ }
+ 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) {
+ 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 (Error E =
+ isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) {
+ reportError(std::move(E), Filename);
+ } 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(), Verbose,
+ ArchiveMemberOffsets);
+ Error Err = Error::success();
+ unsigned I = -1;
+ for (auto &C : A->children(Err)) {
+ ++I;
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (Error E =
+ isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ reportError(std::move(E), getFileNameForError(C, I), Filename);
+ continue;
+ }
+ if (MachOObjectFile *O =
+ dyn_cast<MachOObjectFile>(&*ChildOrErr.get()))
+ ProcessMachO(Filename, O, O->getFileName());
+ }
+ if (Err)
+ reportError(std::move(Err), Filename);
+ } else {
+ consumeError(AOrErr.takeError());
+ reportError(Filename, "Mach-O universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a Mach-O file or an archive file");
+ }
+ 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 (Error E =
+ isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) {
+ reportError(std::move(E), Filename, "", 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(), Verbose, ArchiveMemberOffsets,
+ ArchitectureName);
+ Error Err = Error::success();
+ unsigned I = -1;
+ for (auto &C : A->children(Err)) {
+ ++I;
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ reportError(std::move(E), getFileNameForError(C, I), Filename,
+ 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)
+ reportError(std::move(Err), Filename);
+ } else {
+ consumeError(AOrErr.takeError());
+ reportError(Filename, "Mach-O universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a Mach-O file or an archive file");
+ }
+ }
+}
+
+namespace {
+// The block of info used by the Symbolizer call backs.
+struct DisassembleInfo {
+ DisassembleInfo(MachOObjectFile *O, SymbolAddressMap *AddrMap,
+ std::vector<SectionRef> *Sections, bool verbose)
+ : verbose(verbose), O(O), AddrMap(AddrMap), Sections(Sections) {}
+ bool verbose;
+ MachOObjectFile *O;
+ SectionRef S;
+ SymbolAddressMap *AddrMap;
+ std::vector<SectionRef> *Sections;
+ const char *class_name = nullptr;
+ const char *selector_name = nullptr;
+ std::unique_ptr<char[]> method = nullptr;
+ char *demangled_name = nullptr;
+ uint64_t adrp_addr = 0;
+ uint32_t adrp_inst = 0;
+ std::unique_ptr<SymbolAddressMap> bindtable;
+ uint32_t depth = 0;
+};
+} // namespace
+
+// SymbolizerGetOpInfo() is the operand information call back function.
+// This is called to get the symbolic information for operand(s) of an
+// instruction when it is being done. This routine does this from
+// the relocation information, symbol table, etc. That block of information
+// is a pointer to the struct DisassembleInfo that was passed when the
+// disassembler context was created and passed to back to here when
+// called back by the disassembler for instruction operands that could have
+// relocation information. The address of the instruction containing operand is
+// at the Pc parameter. The immediate value the operand has is passed in
+// op_info->Value and is at Offset past the start of the instruction and has a
+// byte Size of 1, 2 or 4. The symbolc information is returned in TagBuf is the
+// LLVMOpInfo1 struct defined in the header "llvm-c/Disassembler.h" as symbol
+// names and addends of the symbolic expression to add for the operand. The
+// value of TagType is currently 1 (for the LLVMOpInfo1 struct). If symbolic
+// information is returned then this function returns 1 else it returns 0.
+static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset,
+ uint64_t Size, int TagType, void *TagBuf) {
+ struct DisassembleInfo *info = (struct DisassembleInfo *)DisInfo;
+ struct LLVMOpInfo1 *op_info = (struct LLVMOpInfo1 *)TagBuf;
+ uint64_t value = op_info->Value;
+
+ // Make sure all fields returned are zero if we don't set them.
+ memset((void *)op_info, '\0', sizeof(struct LLVMOpInfo1));
+ op_info->Value = value;
+
+ // If the TagType is not the value 1 which it code knows about or if no
+ // verbose symbolic information is wanted then just return 0, indicating no
+ // information is being returned.
+ if (TagType != 1 || !info->verbose)
+ return 0;
+
+ unsigned int Arch = info->O->getArch();
+ if (Arch == Triple::x86) {
+ if (Size != 1 && Size != 2 && Size != 4 && Size != 0)
+ return 0;
+ if (info->O->getHeader().filetype != MachO::MH_OBJECT) {
+ // TODO:
+ // Search the external relocation entries of a fully linked image
+ // (if any) for an entry that matches this segment offset.
+ // uint32_t seg_offset = (Pc + Offset);
+ return 0;
+ }
+ // In MH_OBJECT filetypes search the section's relocation entries (if any)
+ // for an entry for this section offset.
+ uint32_t sect_addr = info->S.getAddress();
+ uint32_t sect_offset = (Pc + Offset) - sect_addr;
+ bool reloc_found = false;
+ DataRefImpl Rel;
+ MachO::any_relocation_info RE;
+ bool isExtern = false;
+ SymbolRef Symbol;
+ bool r_scattered = false;
+ uint32_t r_value, pair_r_value, r_type;
+ for (const RelocationRef &Reloc : info->S.relocations()) {
+ uint64_t RelocOffset = Reloc.getOffset();
+ if (RelocOffset == sect_offset) {
+ Rel = Reloc.getRawDataRefImpl();
+ RE = info->O->getRelocation(Rel);
+ r_type = info->O->getAnyRelocationType(RE);
+ r_scattered = info->O->isRelocationScattered(RE);
+ if (r_scattered) {
+ r_value = info->O->getScatteredRelocationValue(RE);
+ if (r_type == MachO::GENERIC_RELOC_SECTDIFF ||
+ r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF) {
+ DataRefImpl RelNext = Rel;
+ info->O->moveRelocationNext(RelNext);
+ MachO::any_relocation_info RENext;
+ RENext = info->O->getRelocation(RelNext);
+ if (info->O->isRelocationScattered(RENext))
+ pair_r_value = info->O->getScatteredRelocationValue(RENext);
+ else
+ return 0;
+ }
+ } else {
+ isExtern = info->O->getPlainRelocationExternal(RE);
+ if (isExtern) {
+ symbol_iterator RelocSym = Reloc.getSymbol();
+ Symbol = *RelocSym;
+ }
+ }
+ reloc_found = true;
+ break;
+ }
+ }
+ if (reloc_found && isExtern) {
+ op_info->AddSymbol.Present = 1;
+ op_info->AddSymbol.Name =
+ unwrapOrError(Symbol.getName(), info->O->getFileName()).data();
+ // For i386 extern relocation entries the value in the instruction is
+ // the offset from the symbol, and value is already set in op_info->Value.
+ return 1;
+ }
+ if (reloc_found && (r_type == MachO::GENERIC_RELOC_SECTDIFF ||
+ r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF)) {
+ const char *add = GuessSymbolName(r_value, info->AddrMap);
+ const char *sub = GuessSymbolName(pair_r_value, info->AddrMap);
+ uint32_t offset = value - (r_value - pair_r_value);
+ op_info->AddSymbol.Present = 1;
+ if (add != nullptr)
+ op_info->AddSymbol.Name = add;
+ else
+ op_info->AddSymbol.Value = r_value;
+ op_info->SubtractSymbol.Present = 1;
+ if (sub != nullptr)
+ op_info->SubtractSymbol.Name = sub;
+ else
+ op_info->SubtractSymbol.Value = pair_r_value;
+ op_info->Value = offset;
+ return 1;
+ }
+ return 0;
+ }
+ if (Arch == Triple::x86_64) {
+ if (Size != 1 && Size != 2 && Size != 4 && Size != 0)
+ return 0;
+ // For non MH_OBJECT types, like MH_KEXT_BUNDLE, Search the external
+ // relocation entries of a linked image (if any) for an entry that matches
+ // this segment offset.
+ if (info->O->getHeader().filetype != MachO::MH_OBJECT) {
+ uint64_t seg_offset = Pc + Offset;
+ bool reloc_found = false;
+ DataRefImpl Rel;
+ MachO::any_relocation_info RE;
+ bool isExtern = false;
+ SymbolRef Symbol;
+ for (const RelocationRef &Reloc : info->O->external_relocations()) {
+ uint64_t RelocOffset = Reloc.getOffset();
+ if (RelocOffset == seg_offset) {
+ Rel = Reloc.getRawDataRefImpl();
+ RE = info->O->getRelocation(Rel);
+ // external relocation entries should always be external.
+ isExtern = info->O->getPlainRelocationExternal(RE);
+ if (isExtern) {
+ symbol_iterator RelocSym = Reloc.getSymbol();
+ Symbol = *RelocSym;
+ }
+ reloc_found = true;
+ break;
+ }
+ }
+ if (reloc_found && isExtern) {
+ // The Value passed in will be adjusted by the Pc if the instruction
+ // adds the Pc. But for x86_64 external relocation entries the Value
+ // is the offset from the external symbol.
+ if (info->O->getAnyRelocationPCRel(RE))
+ op_info->Value -= Pc + Offset + Size;
+ const char *name =
+ unwrapOrError(Symbol.getName(), info->O->getFileName()).data();
+ op_info->AddSymbol.Present = 1;
+ op_info->AddSymbol.Name = name;
+ return 1;
+ }
+ return 0;
+ }
+ // In MH_OBJECT filetypes search the section's relocation entries (if any)
+ // for an entry for this section offset.
+ uint64_t sect_addr = info->S.getAddress();
+ uint64_t sect_offset = (Pc + Offset) - sect_addr;
+ bool reloc_found = false;
+ DataRefImpl Rel;
+ MachO::any_relocation_info RE;
+ bool isExtern = false;
+ SymbolRef Symbol;
+ for (const RelocationRef &Reloc : info->S.relocations()) {
+ uint64_t RelocOffset = Reloc.getOffset();
+ if (RelocOffset == sect_offset) {
+ Rel = Reloc.getRawDataRefImpl();
+ RE = info->O->getRelocation(Rel);
+ // NOTE: Scattered relocations don't exist on x86_64.
+ isExtern = info->O->getPlainRelocationExternal(RE);
+ if (isExtern) {
+ symbol_iterator RelocSym = Reloc.getSymbol();
+ Symbol = *RelocSym;
+ }
+ reloc_found = true;
+ break;
+ }
+ }
+ if (reloc_found && isExtern) {
+ // The Value passed in will be adjusted by the Pc if the instruction
+ // adds the Pc. But for x86_64 external relocation entries the Value
+ // is the offset from the external symbol.
+ if (info->O->getAnyRelocationPCRel(RE))
+ op_info->Value -= Pc + Offset + Size;
+ const char *name =
+ unwrapOrError(Symbol.getName(), info->O->getFileName()).data();
+ unsigned Type = info->O->getAnyRelocationType(RE);
+ if (Type == MachO::X86_64_RELOC_SUBTRACTOR) {
+ DataRefImpl RelNext = Rel;
+ info->O->moveRelocationNext(RelNext);
+ MachO::any_relocation_info RENext = info->O->getRelocation(RelNext);
+ unsigned TypeNext = info->O->getAnyRelocationType(RENext);
+ bool isExternNext = info->O->getPlainRelocationExternal(RENext);
+ unsigned SymbolNum = info->O->getPlainRelocationSymbolNum(RENext);
+ if (TypeNext == MachO::X86_64_RELOC_UNSIGNED && isExternNext) {
+ op_info->SubtractSymbol.Present = 1;
+ op_info->SubtractSymbol.Name = name;
+ symbol_iterator RelocSymNext = info->O->getSymbolByIndex(SymbolNum);
+ Symbol = *RelocSymNext;
+ name = unwrapOrError(Symbol.getName(), info->O->getFileName()).data();
+ }
+ }
+ // TODO: add the VariantKinds to op_info->VariantKind for relocation types
+ // like: X86_64_RELOC_TLV, X86_64_RELOC_GOT_LOAD and X86_64_RELOC_GOT.
+ op_info->AddSymbol.Present = 1;
+ op_info->AddSymbol.Name = name;
+ return 1;
+ }
+ return 0;
+ }
+ if (Arch == Triple::arm) {
+ if (Offset != 0 || (Size != 4 && Size != 2))
+ return 0;
+ if (info->O->getHeader().filetype != MachO::MH_OBJECT) {
+ // TODO:
+ // Search the external relocation entries of a fully linked image
+ // (if any) for an entry that matches this segment offset.
+ // uint32_t seg_offset = (Pc + Offset);
+ return 0;
+ }
+ // In MH_OBJECT filetypes search the section's relocation entries (if any)
+ // for an entry for this section offset.
+ uint32_t sect_addr = info->S.getAddress();
+ uint32_t sect_offset = (Pc + Offset) - sect_addr;
+ DataRefImpl Rel;
+ MachO::any_relocation_info RE;
+ bool isExtern = false;
+ SymbolRef Symbol;
+ bool r_scattered = false;
+ uint32_t r_value, pair_r_value, r_type, r_length, other_half;
+ auto Reloc =
+ find_if(info->S.relocations(), [&](const RelocationRef &Reloc) {
+ uint64_t RelocOffset = Reloc.getOffset();
+ return RelocOffset == sect_offset;
+ });
+
+ if (Reloc == info->S.relocations().end())
+ return 0;
+
+ Rel = Reloc->getRawDataRefImpl();
+ RE = info->O->getRelocation(Rel);
+ r_length = info->O->getAnyRelocationLength(RE);
+ r_scattered = info->O->isRelocationScattered(RE);
+ if (r_scattered) {
+ r_value = info->O->getScatteredRelocationValue(RE);
+ r_type = info->O->getScatteredRelocationType(RE);
+ } else {
+ r_type = info->O->getAnyRelocationType(RE);
+ isExtern = info->O->getPlainRelocationExternal(RE);
+ if (isExtern) {
+ symbol_iterator RelocSym = Reloc->getSymbol();
+ Symbol = *RelocSym;
+ }
+ }
+ if (r_type == MachO::ARM_RELOC_HALF ||
+ r_type == MachO::ARM_RELOC_SECTDIFF ||
+ r_type == MachO::ARM_RELOC_LOCAL_SECTDIFF ||
+ r_type == MachO::ARM_RELOC_HALF_SECTDIFF) {
+ DataRefImpl RelNext = Rel;
+ info->O->moveRelocationNext(RelNext);
+ MachO::any_relocation_info RENext;
+ RENext = info->O->getRelocation(RelNext);
+ other_half = info->O->getAnyRelocationAddress(RENext) & 0xffff;
+ if (info->O->isRelocationScattered(RENext))
+ pair_r_value = info->O->getScatteredRelocationValue(RENext);
+ }
+
+ if (isExtern) {
+ const char *name =
+ unwrapOrError(Symbol.getName(), info->O->getFileName()).data();
+ op_info->AddSymbol.Present = 1;
+ op_info->AddSymbol.Name = name;
+ switch (r_type) {
+ case MachO::ARM_RELOC_HALF:
+ if ((r_length & 0x1) == 1) {
+ op_info->Value = value << 16 | other_half;
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16;
+ } else {
+ op_info->Value = other_half << 16 | value;
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16;
+ }
+ break;
+ default:
+ break;
+ }
+ return 1;
+ }
+ // If we have a branch that is not an external relocation entry then
+ // return 0 so the code in tryAddingSymbolicOperand() can use the
+ // SymbolLookUp call back with the branch target address to look up the
+ // symbol and possibility add an annotation for a symbol stub.
+ if (isExtern == 0 && (r_type == MachO::ARM_RELOC_BR24 ||
+ r_type == MachO::ARM_THUMB_RELOC_BR22))
+ return 0;
+
+ uint32_t offset = 0;
+ if (r_type == MachO::ARM_RELOC_HALF ||
+ r_type == MachO::ARM_RELOC_HALF_SECTDIFF) {
+ if ((r_length & 0x1) == 1)
+ value = value << 16 | other_half;
+ else
+ value = other_half << 16 | value;
+ }
+ if (r_scattered && (r_type != MachO::ARM_RELOC_HALF &&
+ r_type != MachO::ARM_RELOC_HALF_SECTDIFF)) {
+ offset = value - r_value;
+ value = r_value;
+ }
+
+ if (r_type == MachO::ARM_RELOC_HALF_SECTDIFF) {
+ if ((r_length & 0x1) == 1)
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16;
+ else
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16;
+ const char *add = GuessSymbolName(r_value, info->AddrMap);
+ const char *sub = GuessSymbolName(pair_r_value, info->AddrMap);
+ int32_t offset = value - (r_value - pair_r_value);
+ op_info->AddSymbol.Present = 1;
+ if (add != nullptr)
+ op_info->AddSymbol.Name = add;
+ else
+ op_info->AddSymbol.Value = r_value;
+ op_info->SubtractSymbol.Present = 1;
+ if (sub != nullptr)
+ op_info->SubtractSymbol.Name = sub;
+ else
+ op_info->SubtractSymbol.Value = pair_r_value;
+ op_info->Value = offset;
+ return 1;
+ }
+
+ op_info->AddSymbol.Present = 1;
+ op_info->Value = offset;
+ if (r_type == MachO::ARM_RELOC_HALF) {
+ if ((r_length & 0x1) == 1)
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16;
+ else
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16;
+ }
+ const char *add = GuessSymbolName(value, info->AddrMap);
+ if (add != nullptr) {
+ op_info->AddSymbol.Name = add;
+ return 1;
+ }
+ op_info->AddSymbol.Value = value;
+ return 1;
+ }
+ if (Arch == Triple::aarch64) {
+ if (Offset != 0 || Size != 4)
+ return 0;
+ if (info->O->getHeader().filetype != MachO::MH_OBJECT) {
+ // TODO:
+ // Search the external relocation entries of a fully linked image
+ // (if any) for an entry that matches this segment offset.
+ // uint64_t seg_offset = (Pc + Offset);
+ return 0;
+ }
+ // In MH_OBJECT filetypes search the section's relocation entries (if any)
+ // for an entry for this section offset.
+ uint64_t sect_addr = info->S.getAddress();
+ uint64_t sect_offset = (Pc + Offset) - sect_addr;
+ auto Reloc =
+ find_if(info->S.relocations(), [&](const RelocationRef &Reloc) {
+ uint64_t RelocOffset = Reloc.getOffset();
+ return RelocOffset == sect_offset;
+ });
+
+ if (Reloc == info->S.relocations().end())
+ return 0;
+
+ DataRefImpl Rel = Reloc->getRawDataRefImpl();
+ MachO::any_relocation_info RE = info->O->getRelocation(Rel);
+ uint32_t r_type = info->O->getAnyRelocationType(RE);
+ if (r_type == MachO::ARM64_RELOC_ADDEND) {
+ DataRefImpl RelNext = Rel;
+ info->O->moveRelocationNext(RelNext);
+ MachO::any_relocation_info RENext = info->O->getRelocation(RelNext);
+ if (value == 0) {
+ value = info->O->getPlainRelocationSymbolNum(RENext);
+ op_info->Value = value;
+ }
+ }
+ // NOTE: Scattered relocations don't exist on arm64.
+ if (!info->O->getPlainRelocationExternal(RE))
+ return 0;
+ const char *name =
+ unwrapOrError(Reloc->getSymbol()->getName(), info->O->getFileName())
+ .data();
+ op_info->AddSymbol.Present = 1;
+ op_info->AddSymbol.Name = name;
+
+ switch (r_type) {
+ case MachO::ARM64_RELOC_PAGE21:
+ /* @page */
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_PAGE;
+ break;
+ case MachO::ARM64_RELOC_PAGEOFF12:
+ /* @pageoff */
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_PAGEOFF;
+ break;
+ case MachO::ARM64_RELOC_GOT_LOAD_PAGE21:
+ /* @gotpage */
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_GOTPAGE;
+ break;
+ case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12:
+ /* @gotpageoff */
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_GOTPAGEOFF;
+ break;
+ case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21:
+ /* @tvlppage is not implemented in llvm-mc */
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_TLVP;
+ break;
+ case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
+ /* @tvlppageoff is not implemented in llvm-mc */
+ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_TLVOFF;
+ break;
+ default:
+ case MachO::ARM64_RELOC_BRANCH26:
+ op_info->VariantKind = LLVMDisassembler_VariantKind_None;
+ break;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+// GuessCstringPointer is passed the address of what might be a pointer to a
+// literal string in a cstring section. If that address is in a cstring section
+// it returns a pointer to that string. Else it returns nullptr.
+static const char *GuessCstringPointer(uint64_t ReferenceValue,
+ struct DisassembleInfo *info) {
+ for (const auto &Load : info->O->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = info->O->getSegment64LoadCommand(Load);
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section_64 Sec = info->O->getSection64(Load, J);
+ uint32_t section_type = Sec.flags & MachO::SECTION_TYPE;
+ if (section_type == MachO::S_CSTRING_LITERALS &&
+ ReferenceValue >= Sec.addr &&
+ ReferenceValue < Sec.addr + Sec.size) {
+ uint64_t sect_offset = ReferenceValue - Sec.addr;
+ uint64_t object_offset = Sec.offset + sect_offset;
+ StringRef MachOContents = info->O->getData();
+ uint64_t object_size = MachOContents.size();
+ const char *object_addr = (const char *)MachOContents.data();
+ if (object_offset < object_size) {
+ const char *name = object_addr + object_offset;
+ return name;
+ } else {
+ return nullptr;
+ }
+ }
+ }
+ } else if (Load.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command Seg = info->O->getSegmentLoadCommand(Load);
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section Sec = info->O->getSection(Load, J);
+ uint32_t section_type = Sec.flags & MachO::SECTION_TYPE;
+ if (section_type == MachO::S_CSTRING_LITERALS &&
+ ReferenceValue >= Sec.addr &&
+ ReferenceValue < Sec.addr + Sec.size) {
+ uint64_t sect_offset = ReferenceValue - Sec.addr;
+ uint64_t object_offset = Sec.offset + sect_offset;
+ StringRef MachOContents = info->O->getData();
+ uint64_t object_size = MachOContents.size();
+ const char *object_addr = (const char *)MachOContents.data();
+ if (object_offset < object_size) {
+ const char *name = object_addr + object_offset;
+ return name;
+ } else {
+ return nullptr;
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+// GuessIndirectSymbol returns the name of the indirect symbol for the
+// ReferenceValue passed in or nullptr. This is used when ReferenceValue maybe
+// an address of a symbol stub or a lazy or non-lazy pointer to associate the
+// symbol name being referenced by the stub or pointer.
+static const char *GuessIndirectSymbol(uint64_t ReferenceValue,
+ struct DisassembleInfo *info) {
+ MachO::dysymtab_command Dysymtab = info->O->getDysymtabLoadCommand();
+ MachO::symtab_command Symtab = info->O->getSymtabLoadCommand();
+ for (const auto &Load : info->O->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = info->O->getSegment64LoadCommand(Load);
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section_64 Sec = info->O->getSection64(Load, J);
+ uint32_t section_type = Sec.flags & MachO::SECTION_TYPE;
+ if ((section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS ||
+ section_type == MachO::S_LAZY_SYMBOL_POINTERS ||
+ section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS ||
+ section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS ||
+ section_type == MachO::S_SYMBOL_STUBS) &&
+ ReferenceValue >= Sec.addr &&
+ ReferenceValue < Sec.addr + Sec.size) {
+ uint32_t stride;
+ if (section_type == MachO::S_SYMBOL_STUBS)
+ stride = Sec.reserved2;
+ else
+ stride = 8;
+ if (stride == 0)
+ return nullptr;
+ uint32_t index = Sec.reserved1 + (ReferenceValue - Sec.addr) / stride;
+ if (index < Dysymtab.nindirectsyms) {
+ uint32_t indirect_symbol =
+ info->O->getIndirectSymbolTableEntry(Dysymtab, index);
+ if (indirect_symbol < Symtab.nsyms) {
+ symbol_iterator Sym = info->O->getSymbolByIndex(indirect_symbol);
+ return unwrapOrError(Sym->getName(), info->O->getFileName())
+ .data();
+ }
+ }
+ }
+ }
+ } else if (Load.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command Seg = info->O->getSegmentLoadCommand(Load);
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section Sec = info->O->getSection(Load, J);
+ uint32_t section_type = Sec.flags & MachO::SECTION_TYPE;
+ if ((section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS ||
+ section_type == MachO::S_LAZY_SYMBOL_POINTERS ||
+ section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS ||
+ section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS ||
+ section_type == MachO::S_SYMBOL_STUBS) &&
+ ReferenceValue >= Sec.addr &&
+ ReferenceValue < Sec.addr + Sec.size) {
+ uint32_t stride;
+ if (section_type == MachO::S_SYMBOL_STUBS)
+ stride = Sec.reserved2;
+ else
+ stride = 4;
+ if (stride == 0)
+ return nullptr;
+ uint32_t index = Sec.reserved1 + (ReferenceValue - Sec.addr) / stride;
+ if (index < Dysymtab.nindirectsyms) {
+ uint32_t indirect_symbol =
+ info->O->getIndirectSymbolTableEntry(Dysymtab, index);
+ if (indirect_symbol < Symtab.nsyms) {
+ symbol_iterator Sym = info->O->getSymbolByIndex(indirect_symbol);
+ return unwrapOrError(Sym->getName(), info->O->getFileName())
+ .data();
+ }
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+// method_reference() is called passing it the ReferenceName that might be
+// a reference it to an Objective-C method call. If so then it allocates and
+// assembles a method call string with the values last seen and saved in
+// the DisassembleInfo's class_name and selector_name fields. This is saved
+// into the method field of the info and any previous string is free'ed.
+// Then the class_name field in the info is set to nullptr. The method call
+// string is set into ReferenceName and ReferenceType is set to
+// LLVMDisassembler_ReferenceType_Out_Objc_Message. If this not a method call
+// then both ReferenceType and ReferenceName are left unchanged.
+static void method_reference(struct DisassembleInfo *info,
+ uint64_t *ReferenceType,
+ const char **ReferenceName) {
+ unsigned int Arch = info->O->getArch();
+ if (*ReferenceName != nullptr) {
+ if (strcmp(*ReferenceName, "_objc_msgSend") == 0) {
+ if (info->selector_name != nullptr) {
+ if (info->class_name != nullptr) {
+ info->method = std::make_unique<char[]>(
+ 5 + strlen(info->class_name) + strlen(info->selector_name));
+ char *method = info->method.get();
+ if (method != nullptr) {
+ strcpy(method, "+[");
+ strcat(method, info->class_name);
+ strcat(method, " ");
+ strcat(method, info->selector_name);
+ strcat(method, "]");
+ *ReferenceName = method;
+ *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message;
+ }
+ } else {
+ info->method =
+ std::make_unique<char[]>(9 + strlen(info->selector_name));
+ char *method = info->method.get();
+ if (method != nullptr) {
+ if (Arch == Triple::x86_64)
+ strcpy(method, "-[%rdi ");
+ else if (Arch == Triple::aarch64)
+ strcpy(method, "-[x0 ");
+ else
+ strcpy(method, "-[r? ");
+ strcat(method, info->selector_name);
+ strcat(method, "]");
+ *ReferenceName = method;
+ *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message;
+ }
+ }
+ info->class_name = nullptr;
+ }
+ } else if (strcmp(*ReferenceName, "_objc_msgSendSuper2") == 0) {
+ if (info->selector_name != nullptr) {
+ info->method =
+ std::make_unique<char[]>(17 + strlen(info->selector_name));
+ char *method = info->method.get();
+ if (method != nullptr) {
+ if (Arch == Triple::x86_64)
+ strcpy(method, "-[[%rdi super] ");
+ else if (Arch == Triple::aarch64)
+ strcpy(method, "-[[x0 super] ");
+ else
+ strcpy(method, "-[[r? super] ");
+ strcat(method, info->selector_name);
+ strcat(method, "]");
+ *ReferenceName = method;
+ *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message;
+ }
+ info->class_name = nullptr;
+ }
+ }
+ }
+}
+
+// GuessPointerPointer() is passed the address of what might be a pointer to
+// a reference to an Objective-C class, selector, message ref or cfstring.
+// If so the value of the pointer is returned and one of the booleans are set
+// to true. If not zero is returned and all the booleans are set to false.
+static uint64_t GuessPointerPointer(uint64_t ReferenceValue,
+ struct DisassembleInfo *info,
+ bool &classref, bool &selref, bool &msgref,
+ bool &cfstring) {
+ classref = false;
+ selref = false;
+ msgref = false;
+ cfstring = false;
+ for (const auto &Load : info->O->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = info->O->getSegment64LoadCommand(Load);
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section_64 Sec = info->O->getSection64(Load, J);
+ if ((strncmp(Sec.sectname, "__objc_selrefs", 16) == 0 ||
+ strncmp(Sec.sectname, "__objc_classrefs", 16) == 0 ||
+ strncmp(Sec.sectname, "__objc_superrefs", 16) == 0 ||
+ strncmp(Sec.sectname, "__objc_msgrefs", 16) == 0 ||
+ strncmp(Sec.sectname, "__cfstring", 16) == 0) &&
+ ReferenceValue >= Sec.addr &&
+ ReferenceValue < Sec.addr + Sec.size) {
+ uint64_t sect_offset = ReferenceValue - Sec.addr;
+ uint64_t object_offset = Sec.offset + sect_offset;
+ StringRef MachOContents = info->O->getData();
+ uint64_t object_size = MachOContents.size();
+ const char *object_addr = (const char *)MachOContents.data();
+ if (object_offset < object_size) {
+ uint64_t pointer_value;
+ memcpy(&pointer_value, object_addr + object_offset,
+ sizeof(uint64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(pointer_value);
+ if (strncmp(Sec.sectname, "__objc_selrefs", 16) == 0)
+ selref = true;
+ else if (strncmp(Sec.sectname, "__objc_classrefs", 16) == 0 ||
+ strncmp(Sec.sectname, "__objc_superrefs", 16) == 0)
+ classref = true;
+ else if (strncmp(Sec.sectname, "__objc_msgrefs", 16) == 0 &&
+ ReferenceValue + 8 < Sec.addr + Sec.size) {
+ msgref = true;
+ memcpy(&pointer_value, object_addr + object_offset + 8,
+ sizeof(uint64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(pointer_value);
+ } else if (strncmp(Sec.sectname, "__cfstring", 16) == 0)
+ cfstring = true;
+ return pointer_value;
+ } else {
+ return 0;
+ }
+ }
+ }
+ }
+ // TODO: Look for LC_SEGMENT for 32-bit Mach-O files.
+ }
+ return 0;
+}
+
+// get_pointer_64 returns a pointer to the bytes in the object file at the
+// Address from a section in the Mach-O file. And indirectly returns the
+// offset into the section, number of bytes left in the section past the offset
+// and which section is was being referenced. If the Address is not in a
+// section nullptr is returned.
+static const char *get_pointer_64(uint64_t Address, uint32_t &offset,
+ uint32_t &left, SectionRef &S,
+ DisassembleInfo *info,
+ bool objc_only = false) {
+ offset = 0;
+ left = 0;
+ S = SectionRef();
+ for (unsigned SectIdx = 0; SectIdx != info->Sections->size(); SectIdx++) {
+ uint64_t SectAddress = ((*(info->Sections))[SectIdx]).getAddress();
+ uint64_t SectSize = ((*(info->Sections))[SectIdx]).getSize();
+ if (SectSize == 0)
+ continue;
+ if (objc_only) {
+ StringRef SectName;
+ Expected<StringRef> SecNameOrErr =
+ ((*(info->Sections))[SectIdx]).getName();
+ if (SecNameOrErr)
+ SectName = *SecNameOrErr;
+ else
+ consumeError(SecNameOrErr.takeError());
+
+ DataRefImpl Ref = ((*(info->Sections))[SectIdx]).getRawDataRefImpl();
+ StringRef SegName = info->O->getSectionFinalSegmentName(Ref);
+ if (SegName != "__OBJC" && SectName != "__cstring")
+ continue;
+ }
+ if (Address >= SectAddress && Address < SectAddress + SectSize) {
+ S = (*(info->Sections))[SectIdx];
+ offset = Address - SectAddress;
+ left = SectSize - offset;
+ StringRef SectContents = unwrapOrError(
+ ((*(info->Sections))[SectIdx]).getContents(), info->O->getFileName());
+ return SectContents.data() + offset;
+ }
+ }
+ return nullptr;
+}
+
+static const char *get_pointer_32(uint32_t Address, uint32_t &offset,
+ uint32_t &left, SectionRef &S,
+ DisassembleInfo *info,
+ bool objc_only = false) {
+ return get_pointer_64(Address, offset, left, S, info, objc_only);
+}
+
+// get_symbol_64() returns the name of a symbol (or nullptr) and the address of
+// the symbol indirectly through n_value. Based on the relocation information
+// for the specified section offset in the specified section reference.
+// If no relocation information is found and a non-zero ReferenceValue for the
+// symbol is passed, look up that address in the info's AddrMap.
+static const char *get_symbol_64(uint32_t sect_offset, SectionRef S,
+ DisassembleInfo *info, uint64_t &n_value,
+ uint64_t ReferenceValue = 0) {
+ n_value = 0;
+ if (!info->verbose)
+ return nullptr;
+
+ // See if there is an external relocation entry at the sect_offset.
+ bool reloc_found = false;
+ DataRefImpl Rel;
+ MachO::any_relocation_info RE;
+ bool isExtern = false;
+ SymbolRef Symbol;
+ for (const RelocationRef &Reloc : S.relocations()) {
+ uint64_t RelocOffset = Reloc.getOffset();
+ if (RelocOffset == sect_offset) {
+ Rel = Reloc.getRawDataRefImpl();
+ RE = info->O->getRelocation(Rel);
+ if (info->O->isRelocationScattered(RE))
+ continue;
+ isExtern = info->O->getPlainRelocationExternal(RE);
+ if (isExtern) {
+ symbol_iterator RelocSym = Reloc.getSymbol();
+ Symbol = *RelocSym;
+ }
+ reloc_found = true;
+ break;
+ }
+ }
+ // If there is an external relocation entry for a symbol in this section
+ // at this section_offset then use that symbol's value for the n_value
+ // and return its name.
+ const char *SymbolName = nullptr;
+ if (reloc_found && isExtern) {
+ n_value = cantFail(Symbol.getValue());
+ StringRef Name = unwrapOrError(Symbol.getName(), info->O->getFileName());
+ if (!Name.empty()) {
+ SymbolName = Name.data();
+ return SymbolName;
+ }
+ }
+
+ // TODO: For fully linked images, look through the external relocation
+ // entries off the dynamic symtab command. For these the r_offset is from the
+ // start of the first writeable segment in the Mach-O file. So the offset
+ // to this section from that segment is passed to this routine by the caller,
+ // as the database_offset. Which is the difference of the section's starting
+ // address and the first writable segment.
+ //
+ // NOTE: need add passing the database_offset to this routine.
+
+ // We did not find an external relocation entry so look up the ReferenceValue
+ // as an address of a symbol and if found return that symbol's name.
+ SymbolName = GuessSymbolName(ReferenceValue, info->AddrMap);
+
+ return SymbolName;
+}
+
+static const char *get_symbol_32(uint32_t sect_offset, SectionRef S,
+ DisassembleInfo *info,
+ uint32_t ReferenceValue) {
+ uint64_t n_value64;
+ return get_symbol_64(sect_offset, S, info, n_value64, ReferenceValue);
+}
+
+namespace {
+
+// These are structs in the Objective-C meta data and read to produce the
+// comments for disassembly. While these are part of the ABI they are no
+// public defintions. So the are here not in include/llvm/BinaryFormat/MachO.h
+// .
+
+// The cfstring object in a 64-bit Mach-O file.
+struct cfstring64_t {
+ uint64_t isa; // class64_t * (64-bit pointer)
+ uint64_t flags; // flag bits
+ uint64_t characters; // char * (64-bit pointer)
+ uint64_t length; // number of non-NULL characters in above
+};
+
+// The class object in a 64-bit Mach-O file.
+struct class64_t {
+ uint64_t isa; // class64_t * (64-bit pointer)
+ uint64_t superclass; // class64_t * (64-bit pointer)
+ uint64_t cache; // Cache (64-bit pointer)
+ uint64_t vtable; // IMP * (64-bit pointer)
+ uint64_t data; // class_ro64_t * (64-bit pointer)
+};
+
+struct class32_t {
+ uint32_t isa; /* class32_t * (32-bit pointer) */
+ uint32_t superclass; /* class32_t * (32-bit pointer) */
+ uint32_t cache; /* Cache (32-bit pointer) */
+ uint32_t vtable; /* IMP * (32-bit pointer) */
+ uint32_t data; /* class_ro32_t * (32-bit pointer) */
+};
+
+struct class_ro64_t {
+ uint32_t flags;
+ uint32_t instanceStart;
+ uint32_t instanceSize;
+ uint32_t reserved;
+ uint64_t ivarLayout; // const uint8_t * (64-bit pointer)
+ uint64_t name; // const char * (64-bit pointer)
+ uint64_t baseMethods; // const method_list_t * (64-bit pointer)
+ uint64_t baseProtocols; // const protocol_list_t * (64-bit pointer)
+ uint64_t ivars; // const ivar_list_t * (64-bit pointer)
+ uint64_t weakIvarLayout; // const uint8_t * (64-bit pointer)
+ uint64_t baseProperties; // const struct objc_property_list (64-bit pointer)
+};
+
+struct class_ro32_t {
+ uint32_t flags;
+ uint32_t instanceStart;
+ uint32_t instanceSize;
+ uint32_t ivarLayout; /* const uint8_t * (32-bit pointer) */
+ uint32_t name; /* const char * (32-bit pointer) */
+ uint32_t baseMethods; /* const method_list_t * (32-bit pointer) */
+ uint32_t baseProtocols; /* const protocol_list_t * (32-bit pointer) */
+ uint32_t ivars; /* const ivar_list_t * (32-bit pointer) */
+ uint32_t weakIvarLayout; /* const uint8_t * (32-bit pointer) */
+ uint32_t baseProperties; /* const struct objc_property_list *
+ (32-bit pointer) */
+};
+
+/* Values for class_ro{64,32}_t->flags */
+#define RO_META (1 << 0)
+#define RO_ROOT (1 << 1)
+#define RO_HAS_CXX_STRUCTORS (1 << 2)
+
+struct method_list64_t {
+ uint32_t entsize;
+ uint32_t count;
+ /* struct method64_t first; These structures follow inline */
+};
+
+struct method_list32_t {
+ uint32_t entsize;
+ uint32_t count;
+ /* struct method32_t first; These structures follow inline */
+};
+
+struct method64_t {
+ uint64_t name; /* SEL (64-bit pointer) */
+ uint64_t types; /* const char * (64-bit pointer) */
+ uint64_t imp; /* IMP (64-bit pointer) */
+};
+
+struct method32_t {
+ uint32_t name; /* SEL (32-bit pointer) */
+ uint32_t types; /* const char * (32-bit pointer) */
+ uint32_t imp; /* IMP (32-bit pointer) */
+};
+
+struct protocol_list64_t {
+ uint64_t count; /* uintptr_t (a 64-bit value) */
+ /* struct protocol64_t * list[0]; These pointers follow inline */
+};
+
+struct protocol_list32_t {
+ uint32_t count; /* uintptr_t (a 32-bit value) */
+ /* struct protocol32_t * list[0]; These pointers follow inline */
+};
+
+struct protocol64_t {
+ uint64_t isa; /* id * (64-bit pointer) */
+ uint64_t name; /* const char * (64-bit pointer) */
+ uint64_t protocols; /* struct protocol_list64_t *
+ (64-bit pointer) */
+ uint64_t instanceMethods; /* method_list_t * (64-bit pointer) */
+ uint64_t classMethods; /* method_list_t * (64-bit pointer) */
+ uint64_t optionalInstanceMethods; /* method_list_t * (64-bit pointer) */
+ uint64_t optionalClassMethods; /* method_list_t * (64-bit pointer) */
+ uint64_t instanceProperties; /* struct objc_property_list *
+ (64-bit pointer) */
+};
+
+struct protocol32_t {
+ uint32_t isa; /* id * (32-bit pointer) */
+ uint32_t name; /* const char * (32-bit pointer) */
+ uint32_t protocols; /* struct protocol_list_t *
+ (32-bit pointer) */
+ uint32_t instanceMethods; /* method_list_t * (32-bit pointer) */
+ uint32_t classMethods; /* method_list_t * (32-bit pointer) */
+ uint32_t optionalInstanceMethods; /* method_list_t * (32-bit pointer) */
+ uint32_t optionalClassMethods; /* method_list_t * (32-bit pointer) */
+ uint32_t instanceProperties; /* struct objc_property_list *
+ (32-bit pointer) */
+};
+
+struct ivar_list64_t {
+ uint32_t entsize;
+ uint32_t count;
+ /* struct ivar64_t first; These structures follow inline */
+};
+
+struct ivar_list32_t {
+ uint32_t entsize;
+ uint32_t count;
+ /* struct ivar32_t first; These structures follow inline */
+};
+
+struct ivar64_t {
+ uint64_t offset; /* uintptr_t * (64-bit pointer) */
+ uint64_t name; /* const char * (64-bit pointer) */
+ uint64_t type; /* const char * (64-bit pointer) */
+ uint32_t alignment;
+ uint32_t size;
+};
+
+struct ivar32_t {
+ uint32_t offset; /* uintptr_t * (32-bit pointer) */
+ uint32_t name; /* const char * (32-bit pointer) */
+ uint32_t type; /* const char * (32-bit pointer) */
+ uint32_t alignment;
+ uint32_t size;
+};
+
+struct objc_property_list64 {
+ uint32_t entsize;
+ uint32_t count;
+ /* struct objc_property64 first; These structures follow inline */
+};
+
+struct objc_property_list32 {
+ uint32_t entsize;
+ uint32_t count;
+ /* struct objc_property32 first; These structures follow inline */
+};
+
+struct objc_property64 {
+ uint64_t name; /* const char * (64-bit pointer) */
+ uint64_t attributes; /* const char * (64-bit pointer) */
+};
+
+struct objc_property32 {
+ uint32_t name; /* const char * (32-bit pointer) */
+ uint32_t attributes; /* const char * (32-bit pointer) */
+};
+
+struct category64_t {
+ uint64_t name; /* const char * (64-bit pointer) */
+ uint64_t cls; /* struct class_t * (64-bit pointer) */
+ uint64_t instanceMethods; /* struct method_list_t * (64-bit pointer) */
+ uint64_t classMethods; /* struct method_list_t * (64-bit pointer) */
+ uint64_t protocols; /* struct protocol_list_t * (64-bit pointer) */
+ uint64_t instanceProperties; /* struct objc_property_list *
+ (64-bit pointer) */
+};
+
+struct category32_t {
+ uint32_t name; /* const char * (32-bit pointer) */
+ uint32_t cls; /* struct class_t * (32-bit pointer) */
+ uint32_t instanceMethods; /* struct method_list_t * (32-bit pointer) */
+ uint32_t classMethods; /* struct method_list_t * (32-bit pointer) */
+ uint32_t protocols; /* struct protocol_list_t * (32-bit pointer) */
+ uint32_t instanceProperties; /* struct objc_property_list *
+ (32-bit pointer) */
+};
+
+struct objc_image_info64 {
+ uint32_t version;
+ uint32_t flags;
+};
+struct objc_image_info32 {
+ uint32_t version;
+ uint32_t flags;
+};
+struct imageInfo_t {
+ uint32_t version;
+ uint32_t flags;
+};
+/* masks for objc_image_info.flags */
+#define OBJC_IMAGE_IS_REPLACEMENT (1 << 0)
+#define OBJC_IMAGE_SUPPORTS_GC (1 << 1)
+#define OBJC_IMAGE_IS_SIMULATED (1 << 5)
+#define OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES (1 << 6)
+
+struct message_ref64 {
+ uint64_t imp; /* IMP (64-bit pointer) */
+ uint64_t sel; /* SEL (64-bit pointer) */
+};
+
+struct message_ref32 {
+ uint32_t imp; /* IMP (32-bit pointer) */
+ uint32_t sel; /* SEL (32-bit pointer) */
+};
+
+// Objective-C 1 (32-bit only) meta data structs.
+
+struct objc_module_t {
+ uint32_t version;
+ uint32_t size;
+ uint32_t name; /* char * (32-bit pointer) */
+ uint32_t symtab; /* struct objc_symtab * (32-bit pointer) */
+};
+
+struct objc_symtab_t {
+ uint32_t sel_ref_cnt;
+ uint32_t refs; /* SEL * (32-bit pointer) */
+ uint16_t cls_def_cnt;
+ uint16_t cat_def_cnt;
+ // uint32_t defs[1]; /* void * (32-bit pointer) variable size */
+};
+
+struct objc_class_t {
+ uint32_t isa; /* struct objc_class * (32-bit pointer) */
+ uint32_t super_class; /* struct objc_class * (32-bit pointer) */
+ uint32_t name; /* const char * (32-bit pointer) */
+ int32_t version;
+ int32_t info;
+ int32_t instance_size;
+ uint32_t ivars; /* struct objc_ivar_list * (32-bit pointer) */
+ uint32_t methodLists; /* struct objc_method_list ** (32-bit pointer) */
+ uint32_t cache; /* struct objc_cache * (32-bit pointer) */
+ uint32_t protocols; /* struct objc_protocol_list * (32-bit pointer) */
+};
+
+#define CLS_GETINFO(cls, infomask) ((cls)->info & (infomask))
+// class is not a metaclass
+#define CLS_CLASS 0x1
+// class is a metaclass
+#define CLS_META 0x2
+
+struct objc_category_t {
+ uint32_t category_name; /* char * (32-bit pointer) */
+ uint32_t class_name; /* char * (32-bit pointer) */
+ uint32_t instance_methods; /* struct objc_method_list * (32-bit pointer) */
+ uint32_t class_methods; /* struct objc_method_list * (32-bit pointer) */
+ uint32_t protocols; /* struct objc_protocol_list * (32-bit ptr) */
+};
+
+struct objc_ivar_t {
+ uint32_t ivar_name; /* char * (32-bit pointer) */
+ uint32_t ivar_type; /* char * (32-bit pointer) */
+ int32_t ivar_offset;
+};
+
+struct objc_ivar_list_t {
+ int32_t ivar_count;
+ // struct objc_ivar_t ivar_list[1]; /* variable length structure */
+};
+
+struct objc_method_list_t {
+ uint32_t obsolete; /* struct objc_method_list * (32-bit pointer) */
+ int32_t method_count;
+ // struct objc_method_t method_list[1]; /* variable length structure */
+};
+
+struct objc_method_t {
+ uint32_t method_name; /* SEL, aka struct objc_selector * (32-bit pointer) */
+ uint32_t method_types; /* char * (32-bit pointer) */
+ uint32_t method_imp; /* IMP, aka function pointer, (*IMP)(id, SEL, ...)
+ (32-bit pointer) */
+};
+
+struct objc_protocol_list_t {
+ uint32_t next; /* struct objc_protocol_list * (32-bit pointer) */
+ int32_t count;
+ // uint32_t list[1]; /* Protocol *, aka struct objc_protocol_t *
+ // (32-bit pointer) */
+};
+
+struct objc_protocol_t {
+ uint32_t isa; /* struct objc_class * (32-bit pointer) */
+ uint32_t protocol_name; /* char * (32-bit pointer) */
+ uint32_t protocol_list; /* struct objc_protocol_list * (32-bit pointer) */
+ uint32_t instance_methods; /* struct objc_method_description_list *
+ (32-bit pointer) */
+ uint32_t class_methods; /* struct objc_method_description_list *
+ (32-bit pointer) */
+};
+
+struct objc_method_description_list_t {
+ int32_t count;
+ // struct objc_method_description_t list[1];
+};
+
+struct objc_method_description_t {
+ uint32_t name; /* SEL, aka struct objc_selector * (32-bit pointer) */
+ uint32_t types; /* char * (32-bit pointer) */
+};
+
+inline void swapStruct(struct cfstring64_t &cfs) {
+ sys::swapByteOrder(cfs.isa);
+ sys::swapByteOrder(cfs.flags);
+ sys::swapByteOrder(cfs.characters);
+ sys::swapByteOrder(cfs.length);
+}
+
+inline void swapStruct(struct class64_t &c) {
+ sys::swapByteOrder(c.isa);
+ sys::swapByteOrder(c.superclass);
+ sys::swapByteOrder(c.cache);
+ sys::swapByteOrder(c.vtable);
+ sys::swapByteOrder(c.data);
+}
+
+inline void swapStruct(struct class32_t &c) {
+ sys::swapByteOrder(c.isa);
+ sys::swapByteOrder(c.superclass);
+ sys::swapByteOrder(c.cache);
+ sys::swapByteOrder(c.vtable);
+ sys::swapByteOrder(c.data);
+}
+
+inline void swapStruct(struct class_ro64_t &cro) {
+ sys::swapByteOrder(cro.flags);
+ sys::swapByteOrder(cro.instanceStart);
+ sys::swapByteOrder(cro.instanceSize);
+ sys::swapByteOrder(cro.reserved);
+ sys::swapByteOrder(cro.ivarLayout);
+ sys::swapByteOrder(cro.name);
+ sys::swapByteOrder(cro.baseMethods);
+ sys::swapByteOrder(cro.baseProtocols);
+ sys::swapByteOrder(cro.ivars);
+ sys::swapByteOrder(cro.weakIvarLayout);
+ sys::swapByteOrder(cro.baseProperties);
+}
+
+inline void swapStruct(struct class_ro32_t &cro) {
+ sys::swapByteOrder(cro.flags);
+ sys::swapByteOrder(cro.instanceStart);
+ sys::swapByteOrder(cro.instanceSize);
+ sys::swapByteOrder(cro.ivarLayout);
+ sys::swapByteOrder(cro.name);
+ sys::swapByteOrder(cro.baseMethods);
+ sys::swapByteOrder(cro.baseProtocols);
+ sys::swapByteOrder(cro.ivars);
+ sys::swapByteOrder(cro.weakIvarLayout);
+ sys::swapByteOrder(cro.baseProperties);
+}
+
+inline void swapStruct(struct method_list64_t &ml) {
+ sys::swapByteOrder(ml.entsize);
+ sys::swapByteOrder(ml.count);
+}
+
+inline void swapStruct(struct method_list32_t &ml) {
+ sys::swapByteOrder(ml.entsize);
+ sys::swapByteOrder(ml.count);
+}
+
+inline void swapStruct(struct method64_t &m) {
+ sys::swapByteOrder(m.name);
+ sys::swapByteOrder(m.types);
+ sys::swapByteOrder(m.imp);
+}
+
+inline void swapStruct(struct method32_t &m) {
+ sys::swapByteOrder(m.name);
+ sys::swapByteOrder(m.types);
+ sys::swapByteOrder(m.imp);
+}
+
+inline void swapStruct(struct protocol_list64_t &pl) {
+ sys::swapByteOrder(pl.count);
+}
+
+inline void swapStruct(struct protocol_list32_t &pl) {
+ sys::swapByteOrder(pl.count);
+}
+
+inline void swapStruct(struct protocol64_t &p) {
+ sys::swapByteOrder(p.isa);
+ sys::swapByteOrder(p.name);
+ sys::swapByteOrder(p.protocols);
+ sys::swapByteOrder(p.instanceMethods);
+ sys::swapByteOrder(p.classMethods);
+ sys::swapByteOrder(p.optionalInstanceMethods);
+ sys::swapByteOrder(p.optionalClassMethods);
+ sys::swapByteOrder(p.instanceProperties);
+}
+
+inline void swapStruct(struct protocol32_t &p) {
+ sys::swapByteOrder(p.isa);
+ sys::swapByteOrder(p.name);
+ sys::swapByteOrder(p.protocols);
+ sys::swapByteOrder(p.instanceMethods);
+ sys::swapByteOrder(p.classMethods);
+ sys::swapByteOrder(p.optionalInstanceMethods);
+ sys::swapByteOrder(p.optionalClassMethods);
+ sys::swapByteOrder(p.instanceProperties);
+}
+
+inline void swapStruct(struct ivar_list64_t &il) {
+ sys::swapByteOrder(il.entsize);
+ sys::swapByteOrder(il.count);
+}
+
+inline void swapStruct(struct ivar_list32_t &il) {
+ sys::swapByteOrder(il.entsize);
+ sys::swapByteOrder(il.count);
+}
+
+inline void swapStruct(struct ivar64_t &i) {
+ sys::swapByteOrder(i.offset);
+ sys::swapByteOrder(i.name);
+ sys::swapByteOrder(i.type);
+ sys::swapByteOrder(i.alignment);
+ sys::swapByteOrder(i.size);
+}
+
+inline void swapStruct(struct ivar32_t &i) {
+ sys::swapByteOrder(i.offset);
+ sys::swapByteOrder(i.name);
+ sys::swapByteOrder(i.type);
+ sys::swapByteOrder(i.alignment);
+ sys::swapByteOrder(i.size);
+}
+
+inline void swapStruct(struct objc_property_list64 &pl) {
+ sys::swapByteOrder(pl.entsize);
+ sys::swapByteOrder(pl.count);
+}
+
+inline void swapStruct(struct objc_property_list32 &pl) {
+ sys::swapByteOrder(pl.entsize);
+ sys::swapByteOrder(pl.count);
+}
+
+inline void swapStruct(struct objc_property64 &op) {
+ sys::swapByteOrder(op.name);
+ sys::swapByteOrder(op.attributes);
+}
+
+inline void swapStruct(struct objc_property32 &op) {
+ sys::swapByteOrder(op.name);
+ sys::swapByteOrder(op.attributes);
+}
+
+inline void swapStruct(struct category64_t &c) {
+ sys::swapByteOrder(c.name);
+ sys::swapByteOrder(c.cls);
+ sys::swapByteOrder(c.instanceMethods);
+ sys::swapByteOrder(c.classMethods);
+ sys::swapByteOrder(c.protocols);
+ sys::swapByteOrder(c.instanceProperties);
+}
+
+inline void swapStruct(struct category32_t &c) {
+ sys::swapByteOrder(c.name);
+ sys::swapByteOrder(c.cls);
+ sys::swapByteOrder(c.instanceMethods);
+ sys::swapByteOrder(c.classMethods);
+ sys::swapByteOrder(c.protocols);
+ sys::swapByteOrder(c.instanceProperties);
+}
+
+inline void swapStruct(struct objc_image_info64 &o) {
+ sys::swapByteOrder(o.version);
+ sys::swapByteOrder(o.flags);
+}
+
+inline void swapStruct(struct objc_image_info32 &o) {
+ sys::swapByteOrder(o.version);
+ sys::swapByteOrder(o.flags);
+}
+
+inline void swapStruct(struct imageInfo_t &o) {
+ sys::swapByteOrder(o.version);
+ sys::swapByteOrder(o.flags);
+}
+
+inline void swapStruct(struct message_ref64 &mr) {
+ sys::swapByteOrder(mr.imp);
+ sys::swapByteOrder(mr.sel);
+}
+
+inline void swapStruct(struct message_ref32 &mr) {
+ sys::swapByteOrder(mr.imp);
+ sys::swapByteOrder(mr.sel);
+}
+
+inline void swapStruct(struct objc_module_t &module) {
+ sys::swapByteOrder(module.version);
+ sys::swapByteOrder(module.size);
+ sys::swapByteOrder(module.name);
+ sys::swapByteOrder(module.symtab);
+}
+
+inline void swapStruct(struct objc_symtab_t &symtab) {
+ sys::swapByteOrder(symtab.sel_ref_cnt);
+ sys::swapByteOrder(symtab.refs);
+ sys::swapByteOrder(symtab.cls_def_cnt);
+ sys::swapByteOrder(symtab.cat_def_cnt);
+}
+
+inline void swapStruct(struct objc_class_t &objc_class) {
+ sys::swapByteOrder(objc_class.isa);
+ sys::swapByteOrder(objc_class.super_class);
+ sys::swapByteOrder(objc_class.name);
+ sys::swapByteOrder(objc_class.version);
+ sys::swapByteOrder(objc_class.info);
+ sys::swapByteOrder(objc_class.instance_size);
+ sys::swapByteOrder(objc_class.ivars);
+ sys::swapByteOrder(objc_class.methodLists);
+ sys::swapByteOrder(objc_class.cache);
+ sys::swapByteOrder(objc_class.protocols);
+}
+
+inline void swapStruct(struct objc_category_t &objc_category) {
+ sys::swapByteOrder(objc_category.category_name);
+ sys::swapByteOrder(objc_category.class_name);
+ sys::swapByteOrder(objc_category.instance_methods);
+ sys::swapByteOrder(objc_category.class_methods);
+ sys::swapByteOrder(objc_category.protocols);
+}
+
+inline void swapStruct(struct objc_ivar_list_t &objc_ivar_list) {
+ sys::swapByteOrder(objc_ivar_list.ivar_count);
+}
+
+inline void swapStruct(struct objc_ivar_t &objc_ivar) {
+ sys::swapByteOrder(objc_ivar.ivar_name);
+ sys::swapByteOrder(objc_ivar.ivar_type);
+ sys::swapByteOrder(objc_ivar.ivar_offset);
+}
+
+inline void swapStruct(struct objc_method_list_t &method_list) {
+ sys::swapByteOrder(method_list.obsolete);
+ sys::swapByteOrder(method_list.method_count);
+}
+
+inline void swapStruct(struct objc_method_t &method) {
+ sys::swapByteOrder(method.method_name);
+ sys::swapByteOrder(method.method_types);
+ sys::swapByteOrder(method.method_imp);
+}
+
+inline void swapStruct(struct objc_protocol_list_t &protocol_list) {
+ sys::swapByteOrder(protocol_list.next);
+ sys::swapByteOrder(protocol_list.count);
+}
+
+inline void swapStruct(struct objc_protocol_t &protocol) {
+ sys::swapByteOrder(protocol.isa);
+ sys::swapByteOrder(protocol.protocol_name);
+ sys::swapByteOrder(protocol.protocol_list);
+ sys::swapByteOrder(protocol.instance_methods);
+ sys::swapByteOrder(protocol.class_methods);
+}
+
+inline void swapStruct(struct objc_method_description_list_t &mdl) {
+ sys::swapByteOrder(mdl.count);
+}
+
+inline void swapStruct(struct objc_method_description_t &md) {
+ sys::swapByteOrder(md.name);
+ sys::swapByteOrder(md.types);
+}
+
+} // namespace
+
+static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue,
+ struct DisassembleInfo *info);
+
+// get_objc2_64bit_class_name() is used for disassembly and is passed a pointer
+// to an Objective-C class and returns the class name. It is also passed the
+// address of the pointer, so when the pointer is zero as it can be in an .o
+// file, that is used to look for an external relocation entry with a symbol
+// name.
+static const char *get_objc2_64bit_class_name(uint64_t pointer_value,
+ uint64_t ReferenceValue,
+ struct DisassembleInfo *info) {
+ const char *r;
+ uint32_t offset, left;
+ SectionRef S;
+
+ // The pointer_value can be 0 in an object file and have a relocation
+ // entry for the class symbol at the ReferenceValue (the address of the
+ // pointer).
+ if (pointer_value == 0) {
+ r = get_pointer_64(ReferenceValue, offset, left, S, info);
+ if (r == nullptr || left < sizeof(uint64_t))
+ return nullptr;
+ uint64_t n_value;
+ const char *symbol_name = get_symbol_64(offset, S, info, n_value);
+ if (symbol_name == nullptr)
+ return nullptr;
+ const char *class_name = strrchr(symbol_name, '$');
+ if (class_name != nullptr && class_name[1] == '_' && class_name[2] != '\0')
+ return class_name + 2;
+ else
+ return nullptr;
+ }
+
+ // The case were the pointer_value is non-zero and points to a class defined
+ // in this Mach-O file.
+ r = get_pointer_64(pointer_value, offset, left, S, info);
+ if (r == nullptr || left < sizeof(struct class64_t))
+ return nullptr;
+ struct class64_t c;
+ memcpy(&c, r, sizeof(struct class64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(c);
+ if (c.data == 0)
+ return nullptr;
+ r = get_pointer_64(c.data, offset, left, S, info);
+ if (r == nullptr || left < sizeof(struct class_ro64_t))
+ return nullptr;
+ struct class_ro64_t cro;
+ memcpy(&cro, r, sizeof(struct class_ro64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(cro);
+ if (cro.name == 0)
+ return nullptr;
+ const char *name = get_pointer_64(cro.name, offset, left, S, info);
+ return name;
+}
+
+// get_objc2_64bit_cfstring_name is used for disassembly and is passed a
+// pointer to a cfstring and returns its name or nullptr.
+static const char *get_objc2_64bit_cfstring_name(uint64_t ReferenceValue,
+ struct DisassembleInfo *info) {
+ const char *r, *name;
+ uint32_t offset, left;
+ SectionRef S;
+ struct cfstring64_t cfs;
+ uint64_t cfs_characters;
+
+ r = get_pointer_64(ReferenceValue, offset, left, S, info);
+ if (r == nullptr || left < sizeof(struct cfstring64_t))
+ return nullptr;
+ memcpy(&cfs, r, sizeof(struct cfstring64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(cfs);
+ if (cfs.characters == 0) {
+ uint64_t n_value;
+ const char *symbol_name = get_symbol_64(
+ offset + offsetof(struct cfstring64_t, characters), S, info, n_value);
+ if (symbol_name == nullptr)
+ return nullptr;
+ cfs_characters = n_value;
+ } else
+ cfs_characters = cfs.characters;
+ name = get_pointer_64(cfs_characters, offset, left, S, info);
+
+ return name;
+}
+
+// get_objc2_64bit_selref() is used for disassembly and is passed a the address
+// of a pointer to an Objective-C selector reference when the pointer value is
+// zero as in a .o file and is likely to have a external relocation entry with
+// who's symbol's n_value is the real pointer to the selector name. If that is
+// the case the real pointer to the selector name is returned else 0 is
+// returned
+static uint64_t get_objc2_64bit_selref(uint64_t ReferenceValue,
+ struct DisassembleInfo *info) {
+ uint32_t offset, left;
+ SectionRef S;
+
+ const char *r = get_pointer_64(ReferenceValue, offset, left, S, info);
+ if (r == nullptr || left < sizeof(uint64_t))
+ return 0;
+ uint64_t n_value;
+ const char *symbol_name = get_symbol_64(offset, S, info, n_value);
+ if (symbol_name == nullptr)
+ return 0;
+ return n_value;
+}
+
+static const SectionRef get_section(MachOObjectFile *O, const char *segname,
+ const char *sectname) {
+ for (const SectionRef &Section : O->sections()) {
+ StringRef SectName;
+ Expected<StringRef> SecNameOrErr = Section.getName();
+ if (SecNameOrErr)
+ SectName = *SecNameOrErr;
+ else
+ consumeError(SecNameOrErr.takeError());
+
+ DataRefImpl Ref = Section.getRawDataRefImpl();
+ StringRef SegName = O->getSectionFinalSegmentName(Ref);
+ if (SegName == segname && SectName == sectname)
+ return Section;
+ }
+ return SectionRef();
+}
+
+static void
+walk_pointer_list_64(const char *listname, const SectionRef S,
+ MachOObjectFile *O, struct DisassembleInfo *info,
+ void (*func)(uint64_t, struct DisassembleInfo *info)) {
+ if (S == SectionRef())
+ return;
+
+ StringRef SectName;
+ Expected<StringRef> SecNameOrErr = S.getName();
+ if (SecNameOrErr)
+ SectName = *SecNameOrErr;
+ else
+ consumeError(SecNameOrErr.takeError());
+
+ DataRefImpl Ref = S.getRawDataRefImpl();
+ StringRef SegName = O->getSectionFinalSegmentName(Ref);
+ outs() << "Contents of (" << SegName << "," << SectName << ") section\n";
+
+ StringRef BytesStr = unwrapOrError(S.getContents(), O->getFileName());
+ const char *Contents = reinterpret_cast<const char *>(BytesStr.data());
+
+ for (uint32_t i = 0; i < S.getSize(); i += sizeof(uint64_t)) {
+ uint32_t left = S.getSize() - i;
+ uint32_t size = left < sizeof(uint64_t) ? left : sizeof(uint64_t);
+ uint64_t p = 0;
+ memcpy(&p, Contents + i, size);
+ if (i + sizeof(uint64_t) > S.getSize())
+ outs() << listname << " list pointer extends past end of (" << SegName
+ << "," << SectName << ") section\n";
+ outs() << format("%016" PRIx64, S.getAddress() + i) << " ";
+
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(p);
+
+ uint64_t n_value = 0;
+ const char *name = get_symbol_64(i, S, info, n_value, p);
+ if (name == nullptr)
+ name = get_dyld_bind_info_symbolname(S.getAddress() + i, info);
+
+ if (n_value != 0) {
+ outs() << format("0x%" PRIx64, n_value);
+ if (p != 0)
+ outs() << " + " << format("0x%" PRIx64, p);
+ } else
+ outs() << format("0x%" PRIx64, p);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ p += n_value;
+ if (func)
+ func(p, info);
+ }
+}
+
+static void
+walk_pointer_list_32(const char *listname, const SectionRef S,
+ MachOObjectFile *O, struct DisassembleInfo *info,
+ void (*func)(uint32_t, struct DisassembleInfo *info)) {
+ if (S == SectionRef())
+ return;
+
+ StringRef SectName = unwrapOrError(S.getName(), O->getFileName());
+ DataRefImpl Ref = S.getRawDataRefImpl();
+ StringRef SegName = O->getSectionFinalSegmentName(Ref);
+ outs() << "Contents of (" << SegName << "," << SectName << ") section\n";
+
+ StringRef BytesStr = unwrapOrError(S.getContents(), O->getFileName());
+ const char *Contents = reinterpret_cast<const char *>(BytesStr.data());
+
+ for (uint32_t i = 0; i < S.getSize(); i += sizeof(uint32_t)) {
+ uint32_t left = S.getSize() - i;
+ uint32_t size = left < sizeof(uint32_t) ? left : sizeof(uint32_t);
+ uint32_t p = 0;
+ memcpy(&p, Contents + i, size);
+ if (i + sizeof(uint32_t) > S.getSize())
+ outs() << listname << " list pointer extends past end of (" << SegName
+ << "," << SectName << ") section\n";
+ uint32_t Address = S.getAddress() + i;
+ outs() << format("%08" PRIx32, Address) << " ";
+
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(p);
+ outs() << format("0x%" PRIx32, p);
+
+ const char *name = get_symbol_32(i, S, info, p);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ if (func)
+ func(p, info);
+ }
+}
+
+static void print_layout_map(const char *layout_map, uint32_t left) {
+ if (layout_map == nullptr)
+ return;
+ outs() << " layout map: ";
+ do {
+ outs() << format("0x%02" PRIx32, (*layout_map) & 0xff) << " ";
+ left--;
+ layout_map++;
+ } while (*layout_map != '\0' && left != 0);
+ outs() << "\n";
+}
+
+static void print_layout_map64(uint64_t p, struct DisassembleInfo *info) {
+ uint32_t offset, left;
+ SectionRef S;
+ const char *layout_map;
+
+ if (p == 0)
+ return;
+ layout_map = get_pointer_64(p, offset, left, S, info);
+ print_layout_map(layout_map, left);
+}
+
+static void print_layout_map32(uint32_t p, struct DisassembleInfo *info) {
+ uint32_t offset, left;
+ SectionRef S;
+ const char *layout_map;
+
+ if (p == 0)
+ return;
+ layout_map = get_pointer_32(p, offset, left, S, info);
+ print_layout_map(layout_map, left);
+}
+
+static void print_method_list64_t(uint64_t p, struct DisassembleInfo *info,
+ const char *indent) {
+ struct method_list64_t ml;
+ struct method64_t m;
+ const char *r;
+ uint32_t offset, xoffset, left, i;
+ SectionRef S, xS;
+ const char *name, *sym_name;
+ uint64_t n_value;
+
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&ml, '\0', sizeof(struct method_list64_t));
+ if (left < sizeof(struct method_list64_t)) {
+ memcpy(&ml, r, left);
+ outs() << " (method_list_t entends past the end of the section)\n";
+ } else
+ memcpy(&ml, r, sizeof(struct method_list64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(ml);
+ outs() << indent << "\t\t entsize " << ml.entsize << "\n";
+ outs() << indent << "\t\t count " << ml.count << "\n";
+
+ p += sizeof(struct method_list64_t);
+ offset += sizeof(struct method_list64_t);
+ for (i = 0; i < ml.count; i++) {
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&m, '\0', sizeof(struct method64_t));
+ if (left < sizeof(struct method64_t)) {
+ memcpy(&m, r, left);
+ outs() << indent << " (method_t extends past the end of the section)\n";
+ } else
+ memcpy(&m, r, sizeof(struct method64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(m);
+
+ outs() << indent << "\t\t name ";
+ sym_name = get_symbol_64(offset + offsetof(struct method64_t, name), S,
+ info, n_value, m.name);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (m.name != 0)
+ outs() << " + " << format("0x%" PRIx64, m.name);
+ } else
+ outs() << format("0x%" PRIx64, m.name);
+ name = get_pointer_64(m.name + n_value, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << indent << "\t\t types ";
+ sym_name = get_symbol_64(offset + offsetof(struct method64_t, types), S,
+ info, n_value, m.types);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (m.types != 0)
+ outs() << " + " << format("0x%" PRIx64, m.types);
+ } else
+ outs() << format("0x%" PRIx64, m.types);
+ name = get_pointer_64(m.types + n_value, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << indent << "\t\t imp ";
+ name = get_symbol_64(offset + offsetof(struct method64_t, imp), S, info,
+ n_value, m.imp);
+ if (info->verbose && name == nullptr) {
+ if (n_value != 0) {
+ outs() << format("0x%" PRIx64, n_value) << " ";
+ if (m.imp != 0)
+ outs() << "+ " << format("0x%" PRIx64, m.imp) << " ";
+ } else
+ outs() << format("0x%" PRIx64, m.imp) << " ";
+ }
+ if (name != nullptr)
+ outs() << name;
+ outs() << "\n";
+
+ p += sizeof(struct method64_t);
+ offset += sizeof(struct method64_t);
+ }
+}
+
+static void print_method_list32_t(uint64_t p, struct DisassembleInfo *info,
+ const char *indent) {
+ struct method_list32_t ml;
+ struct method32_t m;
+ const char *r, *name;
+ uint32_t offset, xoffset, left, i;
+ SectionRef S, xS;
+
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&ml, '\0', sizeof(struct method_list32_t));
+ if (left < sizeof(struct method_list32_t)) {
+ memcpy(&ml, r, left);
+ outs() << " (method_list_t entends past the end of the section)\n";
+ } else
+ memcpy(&ml, r, sizeof(struct method_list32_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(ml);
+ outs() << indent << "\t\t entsize " << ml.entsize << "\n";
+ outs() << indent << "\t\t count " << ml.count << "\n";
+
+ p += sizeof(struct method_list32_t);
+ offset += sizeof(struct method_list32_t);
+ for (i = 0; i < ml.count; i++) {
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&m, '\0', sizeof(struct method32_t));
+ if (left < sizeof(struct method32_t)) {
+ memcpy(&ml, r, left);
+ outs() << indent << " (method_t entends past the end of the section)\n";
+ } else
+ memcpy(&m, r, sizeof(struct method32_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(m);
+
+ outs() << indent << "\t\t name " << format("0x%" PRIx32, m.name);
+ name = get_pointer_32(m.name, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << indent << "\t\t types " << format("0x%" PRIx32, m.types);
+ name = get_pointer_32(m.types, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << indent << "\t\t imp " << format("0x%" PRIx32, m.imp);
+ name = get_symbol_32(offset + offsetof(struct method32_t, imp), S, info,
+ m.imp);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ p += sizeof(struct method32_t);
+ offset += sizeof(struct method32_t);
+ }
+}
+
+static bool print_method_list(uint32_t p, struct DisassembleInfo *info) {
+ uint32_t offset, left, xleft;
+ SectionRef S;
+ struct objc_method_list_t method_list;
+ struct objc_method_t method;
+ const char *r, *methods, *name, *SymbolName;
+ int32_t i;
+
+ r = get_pointer_32(p, offset, left, S, info, true);
+ if (r == nullptr)
+ return true;
+
+ outs() << "\n";
+ if (left > sizeof(struct objc_method_list_t)) {
+ memcpy(&method_list, r, sizeof(struct objc_method_list_t));
+ } else {
+ outs() << "\t\t objc_method_list extends past end of the section\n";
+ memset(&method_list, '\0', sizeof(struct objc_method_list_t));
+ memcpy(&method_list, r, left);
+ }
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(method_list);
+
+ outs() << "\t\t obsolete "
+ << format("0x%08" PRIx32, method_list.obsolete) << "\n";
+ outs() << "\t\t method_count " << method_list.method_count << "\n";
+
+ methods = r + sizeof(struct objc_method_list_t);
+ for (i = 0; i < method_list.method_count; i++) {
+ if ((i + 1) * sizeof(struct objc_method_t) > left) {
+ outs() << "\t\t remaining method's extend past the of the section\n";
+ break;
+ }
+ memcpy(&method, methods + i * sizeof(struct objc_method_t),
+ sizeof(struct objc_method_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(method);
+
+ outs() << "\t\t method_name "
+ << format("0x%08" PRIx32, method.method_name);
+ if (info->verbose) {
+ name = get_pointer_32(method.method_name, offset, xleft, S, info, true);
+ if (name != nullptr)
+ outs() << format(" %.*s", xleft, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+
+ outs() << "\t\t method_types "
+ << format("0x%08" PRIx32, method.method_types);
+ if (info->verbose) {
+ name = get_pointer_32(method.method_types, offset, xleft, S, info, true);
+ if (name != nullptr)
+ outs() << format(" %.*s", xleft, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+
+ outs() << "\t\t method_imp "
+ << format("0x%08" PRIx32, method.method_imp) << " ";
+ if (info->verbose) {
+ SymbolName = GuessSymbolName(method.method_imp, info->AddrMap);
+ if (SymbolName != nullptr)
+ outs() << SymbolName;
+ }
+ outs() << "\n";
+ }
+ return false;
+}
+
+static void print_protocol_list64_t(uint64_t p, struct DisassembleInfo *info) {
+ struct protocol_list64_t pl;
+ uint64_t q, n_value;
+ struct protocol64_t pc;
+ const char *r;
+ uint32_t offset, xoffset, left, i;
+ SectionRef S, xS;
+ const char *name, *sym_name;
+
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&pl, '\0', sizeof(struct protocol_list64_t));
+ if (left < sizeof(struct protocol_list64_t)) {
+ memcpy(&pl, r, left);
+ outs() << " (protocol_list_t entends past the end of the section)\n";
+ } else
+ memcpy(&pl, r, sizeof(struct protocol_list64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(pl);
+ outs() << " count " << pl.count << "\n";
+
+ p += sizeof(struct protocol_list64_t);
+ offset += sizeof(struct protocol_list64_t);
+ for (i = 0; i < pl.count; i++) {
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ q = 0;
+ if (left < sizeof(uint64_t)) {
+ memcpy(&q, r, left);
+ outs() << " (protocol_t * entends past the end of the section)\n";
+ } else
+ memcpy(&q, r, sizeof(uint64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(q);
+
+ outs() << "\t\t list[" << i << "] ";
+ sym_name = get_symbol_64(offset, S, info, n_value, q);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (q != 0)
+ outs() << " + " << format("0x%" PRIx64, q);
+ } else
+ outs() << format("0x%" PRIx64, q);
+ outs() << " (struct protocol_t *)\n";
+
+ r = get_pointer_64(q + n_value, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&pc, '\0', sizeof(struct protocol64_t));
+ if (left < sizeof(struct protocol64_t)) {
+ memcpy(&pc, r, left);
+ outs() << " (protocol_t entends past the end of the section)\n";
+ } else
+ memcpy(&pc, r, sizeof(struct protocol64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(pc);
+
+ outs() << "\t\t\t isa " << format("0x%" PRIx64, pc.isa) << "\n";
+
+ outs() << "\t\t\t name ";
+ sym_name = get_symbol_64(offset + offsetof(struct protocol64_t, name), S,
+ info, n_value, pc.name);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (pc.name != 0)
+ outs() << " + " << format("0x%" PRIx64, pc.name);
+ } else
+ outs() << format("0x%" PRIx64, pc.name);
+ name = get_pointer_64(pc.name + n_value, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << "\t\t\tprotocols " << format("0x%" PRIx64, pc.protocols) << "\n";
+
+ outs() << "\t\t instanceMethods ";
+ sym_name =
+ get_symbol_64(offset + offsetof(struct protocol64_t, instanceMethods),
+ S, info, n_value, pc.instanceMethods);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (pc.instanceMethods != 0)
+ outs() << " + " << format("0x%" PRIx64, pc.instanceMethods);
+ } else
+ outs() << format("0x%" PRIx64, pc.instanceMethods);
+ outs() << " (struct method_list_t *)\n";
+ if (pc.instanceMethods + n_value != 0)
+ print_method_list64_t(pc.instanceMethods + n_value, info, "\t");
+
+ outs() << "\t\t classMethods ";
+ sym_name =
+ get_symbol_64(offset + offsetof(struct protocol64_t, classMethods), S,
+ info, n_value, pc.classMethods);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (pc.classMethods != 0)
+ outs() << " + " << format("0x%" PRIx64, pc.classMethods);
+ } else
+ outs() << format("0x%" PRIx64, pc.classMethods);
+ outs() << " (struct method_list_t *)\n";
+ if (pc.classMethods + n_value != 0)
+ print_method_list64_t(pc.classMethods + n_value, info, "\t");
+
+ outs() << "\t optionalInstanceMethods "
+ << format("0x%" PRIx64, pc.optionalInstanceMethods) << "\n";
+ outs() << "\t optionalClassMethods "
+ << format("0x%" PRIx64, pc.optionalClassMethods) << "\n";
+ outs() << "\t instanceProperties "
+ << format("0x%" PRIx64, pc.instanceProperties) << "\n";
+
+ p += sizeof(uint64_t);
+ offset += sizeof(uint64_t);
+ }
+}
+
+static void print_protocol_list32_t(uint32_t p, struct DisassembleInfo *info) {
+ struct protocol_list32_t pl;
+ uint32_t q;
+ struct protocol32_t pc;
+ const char *r;
+ uint32_t offset, xoffset, left, i;
+ SectionRef S, xS;
+ const char *name;
+
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&pl, '\0', sizeof(struct protocol_list32_t));
+ if (left < sizeof(struct protocol_list32_t)) {
+ memcpy(&pl, r, left);
+ outs() << " (protocol_list_t entends past the end of the section)\n";
+ } else
+ memcpy(&pl, r, sizeof(struct protocol_list32_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(pl);
+ outs() << " count " << pl.count << "\n";
+
+ p += sizeof(struct protocol_list32_t);
+ offset += sizeof(struct protocol_list32_t);
+ for (i = 0; i < pl.count; i++) {
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ q = 0;
+ if (left < sizeof(uint32_t)) {
+ memcpy(&q, r, left);
+ outs() << " (protocol_t * entends past the end of the section)\n";
+ } else
+ memcpy(&q, r, sizeof(uint32_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(q);
+ outs() << "\t\t list[" << i << "] " << format("0x%" PRIx32, q)
+ << " (struct protocol_t *)\n";
+ r = get_pointer_32(q, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&pc, '\0', sizeof(struct protocol32_t));
+ if (left < sizeof(struct protocol32_t)) {
+ memcpy(&pc, r, left);
+ outs() << " (protocol_t entends past the end of the section)\n";
+ } else
+ memcpy(&pc, r, sizeof(struct protocol32_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(pc);
+ outs() << "\t\t\t isa " << format("0x%" PRIx32, pc.isa) << "\n";
+ outs() << "\t\t\t name " << format("0x%" PRIx32, pc.name);
+ name = get_pointer_32(pc.name, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+ outs() << "\t\t\tprotocols " << format("0x%" PRIx32, pc.protocols) << "\n";
+ outs() << "\t\t instanceMethods "
+ << format("0x%" PRIx32, pc.instanceMethods)
+ << " (struct method_list_t *)\n";
+ if (pc.instanceMethods != 0)
+ print_method_list32_t(pc.instanceMethods, info, "\t");
+ outs() << "\t\t classMethods " << format("0x%" PRIx32, pc.classMethods)
+ << " (struct method_list_t *)\n";
+ if (pc.classMethods != 0)
+ print_method_list32_t(pc.classMethods, info, "\t");
+ outs() << "\t optionalInstanceMethods "
+ << format("0x%" PRIx32, pc.optionalInstanceMethods) << "\n";
+ outs() << "\t optionalClassMethods "
+ << format("0x%" PRIx32, pc.optionalClassMethods) << "\n";
+ outs() << "\t instanceProperties "
+ << format("0x%" PRIx32, pc.instanceProperties) << "\n";
+ p += sizeof(uint32_t);
+ offset += sizeof(uint32_t);
+ }
+}
+
+static void print_indent(uint32_t indent) {
+ for (uint32_t i = 0; i < indent;) {
+ if (indent - i >= 8) {
+ outs() << "\t";
+ i += 8;
+ } else {
+ for (uint32_t j = i; j < indent; j++)
+ outs() << " ";
+ return;
+ }
+ }
+}
+
+static bool print_method_description_list(uint32_t p, uint32_t indent,
+ struct DisassembleInfo *info) {
+ uint32_t offset, left, xleft;
+ SectionRef S;
+ struct objc_method_description_list_t mdl;
+ struct objc_method_description_t md;
+ const char *r, *list, *name;
+ int32_t i;
+
+ r = get_pointer_32(p, offset, left, S, info, true);
+ if (r == nullptr)
+ return true;
+
+ outs() << "\n";
+ if (left > sizeof(struct objc_method_description_list_t)) {
+ memcpy(&mdl, r, sizeof(struct objc_method_description_list_t));
+ } else {
+ print_indent(indent);
+ outs() << " objc_method_description_list extends past end of the section\n";
+ memset(&mdl, '\0', sizeof(struct objc_method_description_list_t));
+ memcpy(&mdl, r, left);
+ }
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(mdl);
+
+ print_indent(indent);
+ outs() << " count " << mdl.count << "\n";
+
+ list = r + sizeof(struct objc_method_description_list_t);
+ for (i = 0; i < mdl.count; i++) {
+ if ((i + 1) * sizeof(struct objc_method_description_t) > left) {
+ print_indent(indent);
+ outs() << " remaining list entries extend past the of the section\n";
+ break;
+ }
+ print_indent(indent);
+ outs() << " list[" << i << "]\n";
+ memcpy(&md, list + i * sizeof(struct objc_method_description_t),
+ sizeof(struct objc_method_description_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(md);
+
+ print_indent(indent);
+ outs() << " name " << format("0x%08" PRIx32, md.name);
+ if (info->verbose) {
+ name = get_pointer_32(md.name, offset, xleft, S, info, true);
+ if (name != nullptr)
+ outs() << format(" %.*s", xleft, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+
+ print_indent(indent);
+ outs() << " types " << format("0x%08" PRIx32, md.types);
+ if (info->verbose) {
+ name = get_pointer_32(md.types, offset, xleft, S, info, true);
+ if (name != nullptr)
+ outs() << format(" %.*s", xleft, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+ }
+ return false;
+}
+
+static bool print_protocol_list(uint32_t p, uint32_t indent,
+ struct DisassembleInfo *info);
+
+static bool print_protocol(uint32_t p, uint32_t indent,
+ struct DisassembleInfo *info) {
+ uint32_t offset, left;
+ SectionRef S;
+ struct objc_protocol_t protocol;
+ const char *r, *name;
+
+ r = get_pointer_32(p, offset, left, S, info, true);
+ if (r == nullptr)
+ return true;
+
+ outs() << "\n";
+ if (left >= sizeof(struct objc_protocol_t)) {
+ memcpy(&protocol, r, sizeof(struct objc_protocol_t));
+ } else {
+ print_indent(indent);
+ outs() << " Protocol extends past end of the section\n";
+ memset(&protocol, '\0', sizeof(struct objc_protocol_t));
+ memcpy(&protocol, r, left);
+ }
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(protocol);
+
+ print_indent(indent);
+ outs() << " isa " << format("0x%08" PRIx32, protocol.isa)
+ << "\n";
+
+ print_indent(indent);
+ outs() << " protocol_name "
+ << format("0x%08" PRIx32, protocol.protocol_name);
+ if (info->verbose) {
+ name = get_pointer_32(protocol.protocol_name, offset, left, S, info, true);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+
+ print_indent(indent);
+ outs() << " protocol_list "
+ << format("0x%08" PRIx32, protocol.protocol_list);
+ if (print_protocol_list(protocol.protocol_list, indent + 4, info))
+ outs() << " (not in an __OBJC section)\n";
+
+ print_indent(indent);
+ outs() << " instance_methods "
+ << format("0x%08" PRIx32, protocol.instance_methods);
+ if (print_method_description_list(protocol.instance_methods, indent, info))
+ outs() << " (not in an __OBJC section)\n";
+
+ print_indent(indent);
+ outs() << " class_methods "
+ << format("0x%08" PRIx32, protocol.class_methods);
+ if (print_method_description_list(protocol.class_methods, indent, info))
+ outs() << " (not in an __OBJC section)\n";
+
+ return false;
+}
+
+static bool print_protocol_list(uint32_t p, uint32_t indent,
+ struct DisassembleInfo *info) {
+ uint32_t offset, left, l;
+ SectionRef S;
+ struct objc_protocol_list_t protocol_list;
+ const char *r, *list;
+ int32_t i;
+
+ r = get_pointer_32(p, offset, left, S, info, true);
+ if (r == nullptr)
+ return true;
+
+ outs() << "\n";
+ if (left > sizeof(struct objc_protocol_list_t)) {
+ memcpy(&protocol_list, r, sizeof(struct objc_protocol_list_t));
+ } else {
+ outs() << "\t\t objc_protocol_list_t extends past end of the section\n";
+ memset(&protocol_list, '\0', sizeof(struct objc_protocol_list_t));
+ memcpy(&protocol_list, r, left);
+ }
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(protocol_list);
+
+ print_indent(indent);
+ outs() << " next " << format("0x%08" PRIx32, protocol_list.next)
+ << "\n";
+ print_indent(indent);
+ outs() << " count " << protocol_list.count << "\n";
+
+ list = r + sizeof(struct objc_protocol_list_t);
+ for (i = 0; i < protocol_list.count; i++) {
+ if ((i + 1) * sizeof(uint32_t) > left) {
+ outs() << "\t\t remaining list entries extend past the of the section\n";
+ break;
+ }
+ memcpy(&l, list + i * sizeof(uint32_t), sizeof(uint32_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(l);
+
+ print_indent(indent);
+ outs() << " list[" << i << "] " << format("0x%08" PRIx32, l);
+ if (print_protocol(l, indent, info))
+ outs() << "(not in an __OBJC section)\n";
+ }
+ return false;
+}
+
+static void print_ivar_list64_t(uint64_t p, struct DisassembleInfo *info) {
+ struct ivar_list64_t il;
+ struct ivar64_t i;
+ const char *r;
+ uint32_t offset, xoffset, left, j;
+ SectionRef S, xS;
+ const char *name, *sym_name, *ivar_offset_p;
+ uint64_t ivar_offset, n_value;
+
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&il, '\0', sizeof(struct ivar_list64_t));
+ if (left < sizeof(struct ivar_list64_t)) {
+ memcpy(&il, r, left);
+ outs() << " (ivar_list_t entends past the end of the section)\n";
+ } else
+ memcpy(&il, r, sizeof(struct ivar_list64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(il);
+ outs() << " entsize " << il.entsize << "\n";
+ outs() << " count " << il.count << "\n";
+
+ p += sizeof(struct ivar_list64_t);
+ offset += sizeof(struct ivar_list64_t);
+ for (j = 0; j < il.count; j++) {
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&i, '\0', sizeof(struct ivar64_t));
+ if (left < sizeof(struct ivar64_t)) {
+ memcpy(&i, r, left);
+ outs() << " (ivar_t entends past the end of the section)\n";
+ } else
+ memcpy(&i, r, sizeof(struct ivar64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(i);
+
+ outs() << "\t\t\t offset ";
+ sym_name = get_symbol_64(offset + offsetof(struct ivar64_t, offset), S,
+ info, n_value, i.offset);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (i.offset != 0)
+ outs() << " + " << format("0x%" PRIx64, i.offset);
+ } else
+ outs() << format("0x%" PRIx64, i.offset);
+ ivar_offset_p = get_pointer_64(i.offset + n_value, xoffset, left, xS, info);
+ if (ivar_offset_p != nullptr && left >= sizeof(*ivar_offset_p)) {
+ memcpy(&ivar_offset, ivar_offset_p, sizeof(ivar_offset));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(ivar_offset);
+ outs() << " " << ivar_offset << "\n";
+ } else
+ outs() << "\n";
+
+ outs() << "\t\t\t name ";
+ sym_name = get_symbol_64(offset + offsetof(struct ivar64_t, name), S, info,
+ n_value, i.name);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (i.name != 0)
+ outs() << " + " << format("0x%" PRIx64, i.name);
+ } else
+ outs() << format("0x%" PRIx64, i.name);
+ name = get_pointer_64(i.name + n_value, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << "\t\t\t type ";
+ sym_name = get_symbol_64(offset + offsetof(struct ivar64_t, type), S, info,
+ n_value, i.name);
+ name = get_pointer_64(i.type + n_value, xoffset, left, xS, info);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (i.type != 0)
+ outs() << " + " << format("0x%" PRIx64, i.type);
+ } else
+ outs() << format("0x%" PRIx64, i.type);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << "\t\t\talignment " << i.alignment << "\n";
+ outs() << "\t\t\t size " << i.size << "\n";
+
+ p += sizeof(struct ivar64_t);
+ offset += sizeof(struct ivar64_t);
+ }
+}
+
+static void print_ivar_list32_t(uint32_t p, struct DisassembleInfo *info) {
+ struct ivar_list32_t il;
+ struct ivar32_t i;
+ const char *r;
+ uint32_t offset, xoffset, left, j;
+ SectionRef S, xS;
+ const char *name, *ivar_offset_p;
+ uint32_t ivar_offset;
+
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&il, '\0', sizeof(struct ivar_list32_t));
+ if (left < sizeof(struct ivar_list32_t)) {
+ memcpy(&il, r, left);
+ outs() << " (ivar_list_t entends past the end of the section)\n";
+ } else
+ memcpy(&il, r, sizeof(struct ivar_list32_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(il);
+ outs() << " entsize " << il.entsize << "\n";
+ outs() << " count " << il.count << "\n";
+
+ p += sizeof(struct ivar_list32_t);
+ offset += sizeof(struct ivar_list32_t);
+ for (j = 0; j < il.count; j++) {
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&i, '\0', sizeof(struct ivar32_t));
+ if (left < sizeof(struct ivar32_t)) {
+ memcpy(&i, r, left);
+ outs() << " (ivar_t entends past the end of the section)\n";
+ } else
+ memcpy(&i, r, sizeof(struct ivar32_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(i);
+
+ outs() << "\t\t\t offset " << format("0x%" PRIx32, i.offset);
+ ivar_offset_p = get_pointer_32(i.offset, xoffset, left, xS, info);
+ if (ivar_offset_p != nullptr && left >= sizeof(*ivar_offset_p)) {
+ memcpy(&ivar_offset, ivar_offset_p, sizeof(ivar_offset));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(ivar_offset);
+ outs() << " " << ivar_offset << "\n";
+ } else
+ outs() << "\n";
+
+ outs() << "\t\t\t name " << format("0x%" PRIx32, i.name);
+ name = get_pointer_32(i.name, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << "\t\t\t type " << format("0x%" PRIx32, i.type);
+ name = get_pointer_32(i.type, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << "\t\t\talignment " << i.alignment << "\n";
+ outs() << "\t\t\t size " << i.size << "\n";
+
+ p += sizeof(struct ivar32_t);
+ offset += sizeof(struct ivar32_t);
+ }
+}
+
+static void print_objc_property_list64(uint64_t p,
+ struct DisassembleInfo *info) {
+ struct objc_property_list64 opl;
+ struct objc_property64 op;
+ const char *r;
+ uint32_t offset, xoffset, left, j;
+ SectionRef S, xS;
+ const char *name, *sym_name;
+ uint64_t n_value;
+
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&opl, '\0', sizeof(struct objc_property_list64));
+ if (left < sizeof(struct objc_property_list64)) {
+ memcpy(&opl, r, left);
+ outs() << " (objc_property_list entends past the end of the section)\n";
+ } else
+ memcpy(&opl, r, sizeof(struct objc_property_list64));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(opl);
+ outs() << " entsize " << opl.entsize << "\n";
+ outs() << " count " << opl.count << "\n";
+
+ p += sizeof(struct objc_property_list64);
+ offset += sizeof(struct objc_property_list64);
+ for (j = 0; j < opl.count; j++) {
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&op, '\0', sizeof(struct objc_property64));
+ if (left < sizeof(struct objc_property64)) {
+ memcpy(&op, r, left);
+ outs() << " (objc_property entends past the end of the section)\n";
+ } else
+ memcpy(&op, r, sizeof(struct objc_property64));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(op);
+
+ outs() << "\t\t\t name ";
+ sym_name = get_symbol_64(offset + offsetof(struct objc_property64, name), S,
+ info, n_value, op.name);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (op.name != 0)
+ outs() << " + " << format("0x%" PRIx64, op.name);
+ } else
+ outs() << format("0x%" PRIx64, op.name);
+ name = get_pointer_64(op.name + n_value, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << "\t\t\tattributes ";
+ sym_name =
+ get_symbol_64(offset + offsetof(struct objc_property64, attributes), S,
+ info, n_value, op.attributes);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (op.attributes != 0)
+ outs() << " + " << format("0x%" PRIx64, op.attributes);
+ } else
+ outs() << format("0x%" PRIx64, op.attributes);
+ name = get_pointer_64(op.attributes + n_value, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ p += sizeof(struct objc_property64);
+ offset += sizeof(struct objc_property64);
+ }
+}
+
+static void print_objc_property_list32(uint32_t p,
+ struct DisassembleInfo *info) {
+ struct objc_property_list32 opl;
+ struct objc_property32 op;
+ const char *r;
+ uint32_t offset, xoffset, left, j;
+ SectionRef S, xS;
+ const char *name;
+
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&opl, '\0', sizeof(struct objc_property_list32));
+ if (left < sizeof(struct objc_property_list32)) {
+ memcpy(&opl, r, left);
+ outs() << " (objc_property_list entends past the end of the section)\n";
+ } else
+ memcpy(&opl, r, sizeof(struct objc_property_list32));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(opl);
+ outs() << " entsize " << opl.entsize << "\n";
+ outs() << " count " << opl.count << "\n";
+
+ p += sizeof(struct objc_property_list32);
+ offset += sizeof(struct objc_property_list32);
+ for (j = 0; j < opl.count; j++) {
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&op, '\0', sizeof(struct objc_property32));
+ if (left < sizeof(struct objc_property32)) {
+ memcpy(&op, r, left);
+ outs() << " (objc_property entends past the end of the section)\n";
+ } else
+ memcpy(&op, r, sizeof(struct objc_property32));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(op);
+
+ outs() << "\t\t\t name " << format("0x%" PRIx32, op.name);
+ name = get_pointer_32(op.name, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << "\t\t\tattributes " << format("0x%" PRIx32, op.attributes);
+ name = get_pointer_32(op.attributes, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ p += sizeof(struct objc_property32);
+ offset += sizeof(struct objc_property32);
+ }
+}
+
+static bool print_class_ro64_t(uint64_t p, struct DisassembleInfo *info,
+ bool &is_meta_class) {
+ struct class_ro64_t cro;
+ const char *r;
+ uint32_t offset, xoffset, left;
+ SectionRef S, xS;
+ const char *name, *sym_name;
+ uint64_t n_value;
+
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr || left < sizeof(struct class_ro64_t))
+ return false;
+ memcpy(&cro, r, sizeof(struct class_ro64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(cro);
+ outs() << " flags " << format("0x%" PRIx32, cro.flags);
+ if (cro.flags & RO_META)
+ outs() << " RO_META";
+ if (cro.flags & RO_ROOT)
+ outs() << " RO_ROOT";
+ if (cro.flags & RO_HAS_CXX_STRUCTORS)
+ outs() << " RO_HAS_CXX_STRUCTORS";
+ outs() << "\n";
+ outs() << " instanceStart " << cro.instanceStart << "\n";
+ outs() << " instanceSize " << cro.instanceSize << "\n";
+ outs() << " reserved " << format("0x%" PRIx32, cro.reserved)
+ << "\n";
+ outs() << " ivarLayout " << format("0x%" PRIx64, cro.ivarLayout)
+ << "\n";
+ print_layout_map64(cro.ivarLayout, info);
+
+ outs() << " name ";
+ sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, name), S,
+ info, n_value, cro.name);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (cro.name != 0)
+ outs() << " + " << format("0x%" PRIx64, cro.name);
+ } else
+ outs() << format("0x%" PRIx64, cro.name);
+ name = get_pointer_64(cro.name + n_value, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << " baseMethods ";
+ sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, baseMethods),
+ S, info, n_value, cro.baseMethods);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (cro.baseMethods != 0)
+ outs() << " + " << format("0x%" PRIx64, cro.baseMethods);
+ } else
+ outs() << format("0x%" PRIx64, cro.baseMethods);
+ outs() << " (struct method_list_t *)\n";
+ if (cro.baseMethods + n_value != 0)
+ print_method_list64_t(cro.baseMethods + n_value, info, "");
+
+ outs() << " baseProtocols ";
+ sym_name =
+ get_symbol_64(offset + offsetof(struct class_ro64_t, baseProtocols), S,
+ info, n_value, cro.baseProtocols);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (cro.baseProtocols != 0)
+ outs() << " + " << format("0x%" PRIx64, cro.baseProtocols);
+ } else
+ outs() << format("0x%" PRIx64, cro.baseProtocols);
+ outs() << "\n";
+ if (cro.baseProtocols + n_value != 0)
+ print_protocol_list64_t(cro.baseProtocols + n_value, info);
+
+ outs() << " ivars ";
+ sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, ivars), S,
+ info, n_value, cro.ivars);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (cro.ivars != 0)
+ outs() << " + " << format("0x%" PRIx64, cro.ivars);
+ } else
+ outs() << format("0x%" PRIx64, cro.ivars);
+ outs() << "\n";
+ if (cro.ivars + n_value != 0)
+ print_ivar_list64_t(cro.ivars + n_value, info);
+
+ outs() << " weakIvarLayout ";
+ sym_name =
+ get_symbol_64(offset + offsetof(struct class_ro64_t, weakIvarLayout), S,
+ info, n_value, cro.weakIvarLayout);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (cro.weakIvarLayout != 0)
+ outs() << " + " << format("0x%" PRIx64, cro.weakIvarLayout);
+ } else
+ outs() << format("0x%" PRIx64, cro.weakIvarLayout);
+ outs() << "\n";
+ print_layout_map64(cro.weakIvarLayout + n_value, info);
+
+ outs() << " baseProperties ";
+ sym_name =
+ get_symbol_64(offset + offsetof(struct class_ro64_t, baseProperties), S,
+ info, n_value, cro.baseProperties);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (cro.baseProperties != 0)
+ outs() << " + " << format("0x%" PRIx64, cro.baseProperties);
+ } else
+ outs() << format("0x%" PRIx64, cro.baseProperties);
+ outs() << "\n";
+ if (cro.baseProperties + n_value != 0)
+ print_objc_property_list64(cro.baseProperties + n_value, info);
+
+ is_meta_class = (cro.flags & RO_META) != 0;
+ return true;
+}
+
+static bool print_class_ro32_t(uint32_t p, struct DisassembleInfo *info,
+ bool &is_meta_class) {
+ struct class_ro32_t cro;
+ const char *r;
+ uint32_t offset, xoffset, left;
+ SectionRef S, xS;
+ const char *name;
+
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return false;
+ memset(&cro, '\0', sizeof(struct class_ro32_t));
+ if (left < sizeof(struct class_ro32_t)) {
+ memcpy(&cro, r, left);
+ outs() << " (class_ro_t entends past the end of the section)\n";
+ } else
+ memcpy(&cro, r, sizeof(struct class_ro32_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(cro);
+ outs() << " flags " << format("0x%" PRIx32, cro.flags);
+ if (cro.flags & RO_META)
+ outs() << " RO_META";
+ if (cro.flags & RO_ROOT)
+ outs() << " RO_ROOT";
+ if (cro.flags & RO_HAS_CXX_STRUCTORS)
+ outs() << " RO_HAS_CXX_STRUCTORS";
+ outs() << "\n";
+ outs() << " instanceStart " << cro.instanceStart << "\n";
+ outs() << " instanceSize " << cro.instanceSize << "\n";
+ outs() << " ivarLayout " << format("0x%" PRIx32, cro.ivarLayout)
+ << "\n";
+ print_layout_map32(cro.ivarLayout, info);
+
+ outs() << " name " << format("0x%" PRIx32, cro.name);
+ name = get_pointer_32(cro.name, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << " baseMethods "
+ << format("0x%" PRIx32, cro.baseMethods)
+ << " (struct method_list_t *)\n";
+ if (cro.baseMethods != 0)
+ print_method_list32_t(cro.baseMethods, info, "");
+
+ outs() << " baseProtocols "
+ << format("0x%" PRIx32, cro.baseProtocols) << "\n";
+ if (cro.baseProtocols != 0)
+ print_protocol_list32_t(cro.baseProtocols, info);
+ outs() << " ivars " << format("0x%" PRIx32, cro.ivars)
+ << "\n";
+ if (cro.ivars != 0)
+ print_ivar_list32_t(cro.ivars, info);
+ outs() << " weakIvarLayout "
+ << format("0x%" PRIx32, cro.weakIvarLayout) << "\n";
+ print_layout_map32(cro.weakIvarLayout, info);
+ outs() << " baseProperties "
+ << format("0x%" PRIx32, cro.baseProperties) << "\n";
+ if (cro.baseProperties != 0)
+ print_objc_property_list32(cro.baseProperties, info);
+ is_meta_class = (cro.flags & RO_META) != 0;
+ return true;
+}
+
+static void print_class64_t(uint64_t p, struct DisassembleInfo *info) {
+ struct class64_t c;
+ const char *r;
+ uint32_t offset, left;
+ SectionRef S;
+ const char *name;
+ uint64_t isa_n_value, n_value;
+
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr || left < sizeof(struct class64_t))
+ return;
+ memcpy(&c, r, sizeof(struct class64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(c);
+
+ outs() << " isa " << format("0x%" PRIx64, c.isa);
+ name = get_symbol_64(offset + offsetof(struct class64_t, isa), S, info,
+ isa_n_value, c.isa);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ outs() << " superclass " << format("0x%" PRIx64, c.superclass);
+ name = get_symbol_64(offset + offsetof(struct class64_t, superclass), S, info,
+ n_value, c.superclass);
+ if (name != nullptr)
+ outs() << " " << name;
+ else {
+ name = get_dyld_bind_info_symbolname(S.getAddress() +
+ offset + offsetof(struct class64_t, superclass), info);
+ if (name != nullptr)
+ outs() << " " << name;
+ }
+ outs() << "\n";
+
+ outs() << " cache " << format("0x%" PRIx64, c.cache);
+ name = get_symbol_64(offset + offsetof(struct class64_t, cache), S, info,
+ n_value, c.cache);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ outs() << " vtable " << format("0x%" PRIx64, c.vtable);
+ name = get_symbol_64(offset + offsetof(struct class64_t, vtable), S, info,
+ n_value, c.vtable);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ name = get_symbol_64(offset + offsetof(struct class64_t, data), S, info,
+ n_value, c.data);
+ outs() << " data ";
+ if (n_value != 0) {
+ if (info->verbose && name != nullptr)
+ outs() << name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (c.data != 0)
+ outs() << " + " << format("0x%" PRIx64, c.data);
+ } else
+ outs() << format("0x%" PRIx64, c.data);
+ outs() << " (struct class_ro_t *)";
+
+ // This is a Swift class if some of the low bits of the pointer are set.
+ if ((c.data + n_value) & 0x7)
+ outs() << " Swift class";
+ outs() << "\n";
+ bool is_meta_class;
+ if (!print_class_ro64_t((c.data + n_value) & ~0x7, info, is_meta_class))
+ return;
+
+ if (!is_meta_class &&
+ c.isa + isa_n_value != p &&
+ c.isa + isa_n_value != 0 &&
+ info->depth < 100) {
+ info->depth++;
+ outs() << "Meta Class\n";
+ print_class64_t(c.isa + isa_n_value, info);
+ }
+}
+
+static void print_class32_t(uint32_t p, struct DisassembleInfo *info) {
+ struct class32_t c;
+ const char *r;
+ uint32_t offset, left;
+ SectionRef S;
+ const char *name;
+
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&c, '\0', sizeof(struct class32_t));
+ if (left < sizeof(struct class32_t)) {
+ memcpy(&c, r, left);
+ outs() << " (class_t entends past the end of the section)\n";
+ } else
+ memcpy(&c, r, sizeof(struct class32_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(c);
+
+ outs() << " isa " << format("0x%" PRIx32, c.isa);
+ name =
+ get_symbol_32(offset + offsetof(struct class32_t, isa), S, info, c.isa);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ outs() << " superclass " << format("0x%" PRIx32, c.superclass);
+ name = get_symbol_32(offset + offsetof(struct class32_t, superclass), S, info,
+ c.superclass);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ outs() << " cache " << format("0x%" PRIx32, c.cache);
+ name = get_symbol_32(offset + offsetof(struct class32_t, cache), S, info,
+ c.cache);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ outs() << " vtable " << format("0x%" PRIx32, c.vtable);
+ name = get_symbol_32(offset + offsetof(struct class32_t, vtable), S, info,
+ c.vtable);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ name =
+ get_symbol_32(offset + offsetof(struct class32_t, data), S, info, c.data);
+ outs() << " data " << format("0x%" PRIx32, c.data)
+ << " (struct class_ro_t *)";
+
+ // This is a Swift class if some of the low bits of the pointer are set.
+ if (c.data & 0x3)
+ outs() << " Swift class";
+ outs() << "\n";
+ bool is_meta_class;
+ if (!print_class_ro32_t(c.data & ~0x3, info, is_meta_class))
+ return;
+
+ if (!is_meta_class) {
+ outs() << "Meta Class\n";
+ print_class32_t(c.isa, info);
+ }
+}
+
+static void print_objc_class_t(struct objc_class_t *objc_class,
+ struct DisassembleInfo *info) {
+ uint32_t offset, left, xleft;
+ const char *name, *p, *ivar_list;
+ SectionRef S;
+ int32_t i;
+ struct objc_ivar_list_t objc_ivar_list;
+ struct objc_ivar_t ivar;
+
+ outs() << "\t\t isa " << format("0x%08" PRIx32, objc_class->isa);
+ if (info->verbose && CLS_GETINFO(objc_class, CLS_META)) {
+ name = get_pointer_32(objc_class->isa, offset, left, S, info, true);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+
+ outs() << "\t super_class "
+ << format("0x%08" PRIx32, objc_class->super_class);
+ if (info->verbose) {
+ name = get_pointer_32(objc_class->super_class, offset, left, S, info, true);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+
+ outs() << "\t\t name " << format("0x%08" PRIx32, objc_class->name);
+ if (info->verbose) {
+ name = get_pointer_32(objc_class->name, offset, left, S, info, true);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+
+ outs() << "\t\t version " << format("0x%08" PRIx32, objc_class->version)
+ << "\n";
+
+ outs() << "\t\t info " << format("0x%08" PRIx32, objc_class->info);
+ if (info->verbose) {
+ if (CLS_GETINFO(objc_class, CLS_CLASS))
+ outs() << " CLS_CLASS";
+ else if (CLS_GETINFO(objc_class, CLS_META))
+ outs() << " CLS_META";
+ }
+ outs() << "\n";
+
+ outs() << "\t instance_size "
+ << format("0x%08" PRIx32, objc_class->instance_size) << "\n";
+
+ p = get_pointer_32(objc_class->ivars, offset, left, S, info, true);
+ outs() << "\t\t ivars " << format("0x%08" PRIx32, objc_class->ivars);
+ if (p != nullptr) {
+ if (left > sizeof(struct objc_ivar_list_t)) {
+ outs() << "\n";
+ memcpy(&objc_ivar_list, p, sizeof(struct objc_ivar_list_t));
+ } else {
+ outs() << " (entends past the end of the section)\n";
+ memset(&objc_ivar_list, '\0', sizeof(struct objc_ivar_list_t));
+ memcpy(&objc_ivar_list, p, left);
+ }
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(objc_ivar_list);
+ outs() << "\t\t ivar_count " << objc_ivar_list.ivar_count << "\n";
+ ivar_list = p + sizeof(struct objc_ivar_list_t);
+ for (i = 0; i < objc_ivar_list.ivar_count; i++) {
+ if ((i + 1) * sizeof(struct objc_ivar_t) > left) {
+ outs() << "\t\t remaining ivar's extend past the of the section\n";
+ break;
+ }
+ memcpy(&ivar, ivar_list + i * sizeof(struct objc_ivar_t),
+ sizeof(struct objc_ivar_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(ivar);
+
+ outs() << "\t\t\tivar_name " << format("0x%08" PRIx32, ivar.ivar_name);
+ if (info->verbose) {
+ name = get_pointer_32(ivar.ivar_name, offset, xleft, S, info, true);
+ if (name != nullptr)
+ outs() << format(" %.*s", xleft, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+
+ outs() << "\t\t\tivar_type " << format("0x%08" PRIx32, ivar.ivar_type);
+ if (info->verbose) {
+ name = get_pointer_32(ivar.ivar_type, offset, xleft, S, info, true);
+ if (name != nullptr)
+ outs() << format(" %.*s", xleft, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+
+ outs() << "\t\t ivar_offset "
+ << format("0x%08" PRIx32, ivar.ivar_offset) << "\n";
+ }
+ } else {
+ outs() << " (not in an __OBJC section)\n";
+ }
+
+ outs() << "\t\t methods " << format("0x%08" PRIx32, objc_class->methodLists);
+ if (print_method_list(objc_class->methodLists, info))
+ outs() << " (not in an __OBJC section)\n";
+
+ outs() << "\t\t cache " << format("0x%08" PRIx32, objc_class->cache)
+ << "\n";
+
+ outs() << "\t\tprotocols " << format("0x%08" PRIx32, objc_class->protocols);
+ if (print_protocol_list(objc_class->protocols, 16, info))
+ outs() << " (not in an __OBJC section)\n";
+}
+
+static void print_objc_objc_category_t(struct objc_category_t *objc_category,
+ struct DisassembleInfo *info) {
+ uint32_t offset, left;
+ const char *name;
+ SectionRef S;
+
+ outs() << "\t category name "
+ << format("0x%08" PRIx32, objc_category->category_name);
+ if (info->verbose) {
+ name = get_pointer_32(objc_category->category_name, offset, left, S, info,
+ true);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+
+ outs() << "\t\t class name "
+ << format("0x%08" PRIx32, objc_category->class_name);
+ if (info->verbose) {
+ name =
+ get_pointer_32(objc_category->class_name, offset, left, S, info, true);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ else
+ outs() << " (not in an __OBJC section)";
+ }
+ outs() << "\n";
+
+ outs() << "\t instance methods "
+ << format("0x%08" PRIx32, objc_category->instance_methods);
+ if (print_method_list(objc_category->instance_methods, info))
+ outs() << " (not in an __OBJC section)\n";
+
+ outs() << "\t class methods "
+ << format("0x%08" PRIx32, objc_category->class_methods);
+ if (print_method_list(objc_category->class_methods, info))
+ outs() << " (not in an __OBJC section)\n";
+}
+
+static void print_category64_t(uint64_t p, struct DisassembleInfo *info) {
+ struct category64_t c;
+ const char *r;
+ uint32_t offset, xoffset, left;
+ SectionRef S, xS;
+ const char *name, *sym_name;
+ uint64_t n_value;
+
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&c, '\0', sizeof(struct category64_t));
+ if (left < sizeof(struct category64_t)) {
+ memcpy(&c, r, left);
+ outs() << " (category_t entends past the end of the section)\n";
+ } else
+ memcpy(&c, r, sizeof(struct category64_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(c);
+
+ outs() << " name ";
+ sym_name = get_symbol_64(offset + offsetof(struct category64_t, name), S,
+ info, n_value, c.name);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (c.name != 0)
+ outs() << " + " << format("0x%" PRIx64, c.name);
+ } else
+ outs() << format("0x%" PRIx64, c.name);
+ name = get_pointer_64(c.name + n_value, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ outs() << " cls ";
+ sym_name = get_symbol_64(offset + offsetof(struct category64_t, cls), S, info,
+ n_value, c.cls);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (c.cls != 0)
+ outs() << " + " << format("0x%" PRIx64, c.cls);
+ } else
+ outs() << format("0x%" PRIx64, c.cls);
+ outs() << "\n";
+ if (c.cls + n_value != 0)
+ print_class64_t(c.cls + n_value, info);
+
+ outs() << " instanceMethods ";
+ sym_name =
+ get_symbol_64(offset + offsetof(struct category64_t, instanceMethods), S,
+ info, n_value, c.instanceMethods);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (c.instanceMethods != 0)
+ outs() << " + " << format("0x%" PRIx64, c.instanceMethods);
+ } else
+ outs() << format("0x%" PRIx64, c.instanceMethods);
+ outs() << "\n";
+ if (c.instanceMethods + n_value != 0)
+ print_method_list64_t(c.instanceMethods + n_value, info, "");
+
+ outs() << " classMethods ";
+ sym_name = get_symbol_64(offset + offsetof(struct category64_t, classMethods),
+ S, info, n_value, c.classMethods);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (c.classMethods != 0)
+ outs() << " + " << format("0x%" PRIx64, c.classMethods);
+ } else
+ outs() << format("0x%" PRIx64, c.classMethods);
+ outs() << "\n";
+ if (c.classMethods + n_value != 0)
+ print_method_list64_t(c.classMethods + n_value, info, "");
+
+ outs() << " protocols ";
+ sym_name = get_symbol_64(offset + offsetof(struct category64_t, protocols), S,
+ info, n_value, c.protocols);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (c.protocols != 0)
+ outs() << " + " << format("0x%" PRIx64, c.protocols);
+ } else
+ outs() << format("0x%" PRIx64, c.protocols);
+ outs() << "\n";
+ if (c.protocols + n_value != 0)
+ print_protocol_list64_t(c.protocols + n_value, info);
+
+ outs() << "instanceProperties ";
+ sym_name =
+ get_symbol_64(offset + offsetof(struct category64_t, instanceProperties),
+ S, info, n_value, c.instanceProperties);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (c.instanceProperties != 0)
+ outs() << " + " << format("0x%" PRIx64, c.instanceProperties);
+ } else
+ outs() << format("0x%" PRIx64, c.instanceProperties);
+ outs() << "\n";
+ if (c.instanceProperties + n_value != 0)
+ print_objc_property_list64(c.instanceProperties + n_value, info);
+}
+
+static void print_category32_t(uint32_t p, struct DisassembleInfo *info) {
+ struct category32_t c;
+ const char *r;
+ uint32_t offset, left;
+ SectionRef S, xS;
+ const char *name;
+
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&c, '\0', sizeof(struct category32_t));
+ if (left < sizeof(struct category32_t)) {
+ memcpy(&c, r, left);
+ outs() << " (category_t entends past the end of the section)\n";
+ } else
+ memcpy(&c, r, sizeof(struct category32_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(c);
+
+ outs() << " name " << format("0x%" PRIx32, c.name);
+ name = get_symbol_32(offset + offsetof(struct category32_t, name), S, info,
+ c.name);
+ if (name)
+ outs() << " " << name;
+ outs() << "\n";
+
+ outs() << " cls " << format("0x%" PRIx32, c.cls) << "\n";
+ if (c.cls != 0)
+ print_class32_t(c.cls, info);
+ outs() << " instanceMethods " << format("0x%" PRIx32, c.instanceMethods)
+ << "\n";
+ if (c.instanceMethods != 0)
+ print_method_list32_t(c.instanceMethods, info, "");
+ outs() << " classMethods " << format("0x%" PRIx32, c.classMethods)
+ << "\n";
+ if (c.classMethods != 0)
+ print_method_list32_t(c.classMethods, info, "");
+ outs() << " protocols " << format("0x%" PRIx32, c.protocols) << "\n";
+ if (c.protocols != 0)
+ print_protocol_list32_t(c.protocols, info);
+ outs() << "instanceProperties " << format("0x%" PRIx32, c.instanceProperties)
+ << "\n";
+ if (c.instanceProperties != 0)
+ print_objc_property_list32(c.instanceProperties, info);
+}
+
+static void print_message_refs64(SectionRef S, struct DisassembleInfo *info) {
+ uint32_t i, left, offset, xoffset;
+ uint64_t p, n_value;
+ struct message_ref64 mr;
+ const char *name, *sym_name;
+ const char *r;
+ SectionRef xS;
+
+ if (S == SectionRef())
+ return;
+
+ StringRef SectName;
+ Expected<StringRef> SecNameOrErr = S.getName();
+ if (SecNameOrErr)
+ SectName = *SecNameOrErr;
+ else
+ consumeError(SecNameOrErr.takeError());
+
+ DataRefImpl Ref = S.getRawDataRefImpl();
+ StringRef SegName = info->O->getSectionFinalSegmentName(Ref);
+ outs() << "Contents of (" << SegName << "," << SectName << ") section\n";
+ offset = 0;
+ for (i = 0; i < S.getSize(); i += sizeof(struct message_ref64)) {
+ p = S.getAddress() + i;
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&mr, '\0', sizeof(struct message_ref64));
+ if (left < sizeof(struct message_ref64)) {
+ memcpy(&mr, r, left);
+ outs() << " (message_ref entends past the end of the section)\n";
+ } else
+ memcpy(&mr, r, sizeof(struct message_ref64));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(mr);
+
+ outs() << " imp ";
+ name = get_symbol_64(offset + offsetof(struct message_ref64, imp), S, info,
+ n_value, mr.imp);
+ if (n_value != 0) {
+ outs() << format("0x%" PRIx64, n_value) << " ";
+ if (mr.imp != 0)
+ outs() << "+ " << format("0x%" PRIx64, mr.imp) << " ";
+ } else
+ outs() << format("0x%" PRIx64, mr.imp) << " ";
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ outs() << " sel ";
+ sym_name = get_symbol_64(offset + offsetof(struct message_ref64, sel), S,
+ info, n_value, mr.sel);
+ if (n_value != 0) {
+ if (info->verbose && sym_name != nullptr)
+ outs() << sym_name;
+ else
+ outs() << format("0x%" PRIx64, n_value);
+ if (mr.sel != 0)
+ outs() << " + " << format("0x%" PRIx64, mr.sel);
+ } else
+ outs() << format("0x%" PRIx64, mr.sel);
+ name = get_pointer_64(mr.sel + n_value, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << format(" %.*s", left, name);
+ outs() << "\n";
+
+ offset += sizeof(struct message_ref64);
+ }
+}
+
+static void print_message_refs32(SectionRef S, struct DisassembleInfo *info) {
+ uint32_t i, left, offset, xoffset, p;
+ struct message_ref32 mr;
+ const char *name, *r;
+ SectionRef xS;
+
+ if (S == SectionRef())
+ return;
+
+ StringRef SectName;
+ Expected<StringRef> SecNameOrErr = S.getName();
+ if (SecNameOrErr)
+ SectName = *SecNameOrErr;
+ else
+ consumeError(SecNameOrErr.takeError());
+
+ DataRefImpl Ref = S.getRawDataRefImpl();
+ StringRef SegName = info->O->getSectionFinalSegmentName(Ref);
+ outs() << "Contents of (" << SegName << "," << SectName << ") section\n";
+ offset = 0;
+ for (i = 0; i < S.getSize(); i += sizeof(struct message_ref64)) {
+ p = S.getAddress() + i;
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&mr, '\0', sizeof(struct message_ref32));
+ if (left < sizeof(struct message_ref32)) {
+ memcpy(&mr, r, left);
+ outs() << " (message_ref entends past the end of the section)\n";
+ } else
+ memcpy(&mr, r, sizeof(struct message_ref32));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(mr);
+
+ outs() << " imp " << format("0x%" PRIx32, mr.imp);
+ name = get_symbol_32(offset + offsetof(struct message_ref32, imp), S, info,
+ mr.imp);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ outs() << " sel " << format("0x%" PRIx32, mr.sel);
+ name = get_pointer_32(mr.sel, xoffset, left, xS, info);
+ if (name != nullptr)
+ outs() << " " << name;
+ outs() << "\n";
+
+ offset += sizeof(struct message_ref32);
+ }
+}
+
+static void print_image_info64(SectionRef S, struct DisassembleInfo *info) {
+ uint32_t left, offset, swift_version;
+ uint64_t p;
+ struct objc_image_info64 o;
+ const char *r;
+
+ if (S == SectionRef())
+ return;
+
+ StringRef SectName;
+ Expected<StringRef> SecNameOrErr = S.getName();
+ if (SecNameOrErr)
+ SectName = *SecNameOrErr;
+ else
+ consumeError(SecNameOrErr.takeError());
+
+ DataRefImpl Ref = S.getRawDataRefImpl();
+ StringRef SegName = info->O->getSectionFinalSegmentName(Ref);
+ outs() << "Contents of (" << SegName << "," << SectName << ") section\n";
+ p = S.getAddress();
+ r = get_pointer_64(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&o, '\0', sizeof(struct objc_image_info64));
+ if (left < sizeof(struct objc_image_info64)) {
+ memcpy(&o, r, left);
+ outs() << " (objc_image_info entends past the end of the section)\n";
+ } else
+ memcpy(&o, r, sizeof(struct objc_image_info64));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(o);
+ outs() << " version " << o.version << "\n";
+ outs() << " flags " << format("0x%" PRIx32, o.flags);
+ if (o.flags & OBJC_IMAGE_IS_REPLACEMENT)
+ outs() << " OBJC_IMAGE_IS_REPLACEMENT";
+ if (o.flags & OBJC_IMAGE_SUPPORTS_GC)
+ outs() << " OBJC_IMAGE_SUPPORTS_GC";
+ if (o.flags & OBJC_IMAGE_IS_SIMULATED)
+ outs() << " OBJC_IMAGE_IS_SIMULATED";
+ if (o.flags & OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES)
+ outs() << " OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES";
+ swift_version = (o.flags >> 8) & 0xff;
+ if (swift_version != 0) {
+ if (swift_version == 1)
+ outs() << " Swift 1.0";
+ else if (swift_version == 2)
+ outs() << " Swift 1.1";
+ else if(swift_version == 3)
+ outs() << " Swift 2.0";
+ else if(swift_version == 4)
+ outs() << " Swift 3.0";
+ else if(swift_version == 5)
+ outs() << " Swift 4.0";
+ else if(swift_version == 6)
+ 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 << ")";
+ }
+ outs() << "\n";
+}
+
+static void print_image_info32(SectionRef S, struct DisassembleInfo *info) {
+ uint32_t left, offset, swift_version, p;
+ struct objc_image_info32 o;
+ const char *r;
+
+ if (S == SectionRef())
+ return;
+
+ StringRef SectName;
+ Expected<StringRef> SecNameOrErr = S.getName();
+ if (SecNameOrErr)
+ SectName = *SecNameOrErr;
+ else
+ consumeError(SecNameOrErr.takeError());
+
+ DataRefImpl Ref = S.getRawDataRefImpl();
+ StringRef SegName = info->O->getSectionFinalSegmentName(Ref);
+ outs() << "Contents of (" << SegName << "," << SectName << ") section\n";
+ p = S.getAddress();
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&o, '\0', sizeof(struct objc_image_info32));
+ if (left < sizeof(struct objc_image_info32)) {
+ memcpy(&o, r, left);
+ outs() << " (objc_image_info entends past the end of the section)\n";
+ } else
+ memcpy(&o, r, sizeof(struct objc_image_info32));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(o);
+ outs() << " version " << o.version << "\n";
+ outs() << " flags " << format("0x%" PRIx32, o.flags);
+ if (o.flags & OBJC_IMAGE_IS_REPLACEMENT)
+ outs() << " OBJC_IMAGE_IS_REPLACEMENT";
+ if (o.flags & OBJC_IMAGE_SUPPORTS_GC)
+ outs() << " OBJC_IMAGE_SUPPORTS_GC";
+ swift_version = (o.flags >> 8) & 0xff;
+ if (swift_version != 0) {
+ if (swift_version == 1)
+ outs() << " Swift 1.0";
+ else if (swift_version == 2)
+ outs() << " Swift 1.1";
+ else if(swift_version == 3)
+ outs() << " Swift 2.0";
+ else if(swift_version == 4)
+ outs() << " Swift 3.0";
+ else if(swift_version == 5)
+ outs() << " Swift 4.0";
+ else if(swift_version == 6)
+ 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 << ")";
+ }
+ outs() << "\n";
+}
+
+static void print_image_info(SectionRef S, struct DisassembleInfo *info) {
+ uint32_t left, offset, p;
+ struct imageInfo_t o;
+ const char *r;
+
+ StringRef SectName;
+ Expected<StringRef> SecNameOrErr = S.getName();
+ if (SecNameOrErr)
+ SectName = *SecNameOrErr;
+ else
+ consumeError(SecNameOrErr.takeError());
+
+ DataRefImpl Ref = S.getRawDataRefImpl();
+ StringRef SegName = info->O->getSectionFinalSegmentName(Ref);
+ outs() << "Contents of (" << SegName << "," << SectName << ") section\n";
+ p = S.getAddress();
+ r = get_pointer_32(p, offset, left, S, info);
+ if (r == nullptr)
+ return;
+ memset(&o, '\0', sizeof(struct imageInfo_t));
+ if (left < sizeof(struct imageInfo_t)) {
+ memcpy(&o, r, left);
+ outs() << " (imageInfo entends past the end of the section)\n";
+ } else
+ memcpy(&o, r, sizeof(struct imageInfo_t));
+ if (info->O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(o);
+ outs() << " version " << o.version << "\n";
+ outs() << " flags " << format("0x%" PRIx32, o.flags);
+ if (o.flags & 0x1)
+ outs() << " F&C";
+ if (o.flags & 0x2)
+ outs() << " GC";
+ if (o.flags & 0x4)
+ outs() << " GC-only";
+ else
+ outs() << " RR";
+ outs() << "\n";
+}
+
+static void printObjc2_64bit_MetaData(MachOObjectFile *O, bool verbose) {
+ SymbolAddressMap AddrMap;
+ if (verbose)
+ CreateSymbolAddressMap(O, &AddrMap);
+
+ std::vector<SectionRef> Sections;
+ append_range(Sections, O->sections());
+
+ struct DisassembleInfo info(O, &AddrMap, &Sections, verbose);
+
+ SectionRef CL = get_section(O, "__OBJC2", "__class_list");
+ if (CL == SectionRef())
+ CL = get_section(O, "__DATA", "__objc_classlist");
+ if (CL == SectionRef())
+ CL = get_section(O, "__DATA_CONST", "__objc_classlist");
+ if (CL == SectionRef())
+ CL = get_section(O, "__DATA_DIRTY", "__objc_classlist");
+ info.S = CL;
+ walk_pointer_list_64("class", CL, O, &info, print_class64_t);
+
+ SectionRef CR = get_section(O, "__OBJC2", "__class_refs");
+ if (CR == SectionRef())
+ CR = get_section(O, "__DATA", "__objc_classrefs");
+ if (CR == SectionRef())
+ CR = get_section(O, "__DATA_CONST", "__objc_classrefs");
+ if (CR == SectionRef())
+ CR = get_section(O, "__DATA_DIRTY", "__objc_classrefs");
+ info.S = CR;
+ walk_pointer_list_64("class refs", CR, O, &info, nullptr);
+
+ SectionRef SR = get_section(O, "__OBJC2", "__super_refs");
+ if (SR == SectionRef())
+ SR = get_section(O, "__DATA", "__objc_superrefs");
+ if (SR == SectionRef())
+ SR = get_section(O, "__DATA_CONST", "__objc_superrefs");
+ if (SR == SectionRef())
+ SR = get_section(O, "__DATA_DIRTY", "__objc_superrefs");
+ info.S = SR;
+ walk_pointer_list_64("super refs", SR, O, &info, nullptr);
+
+ SectionRef CA = get_section(O, "__OBJC2", "__category_list");
+ if (CA == SectionRef())
+ CA = get_section(O, "__DATA", "__objc_catlist");
+ if (CA == SectionRef())
+ CA = get_section(O, "__DATA_CONST", "__objc_catlist");
+ if (CA == SectionRef())
+ CA = get_section(O, "__DATA_DIRTY", "__objc_catlist");
+ info.S = CA;
+ walk_pointer_list_64("category", CA, O, &info, print_category64_t);
+
+ SectionRef PL = get_section(O, "__OBJC2", "__protocol_list");
+ if (PL == SectionRef())
+ PL = get_section(O, "__DATA", "__objc_protolist");
+ if (PL == SectionRef())
+ PL = get_section(O, "__DATA_CONST", "__objc_protolist");
+ if (PL == SectionRef())
+ PL = get_section(O, "__DATA_DIRTY", "__objc_protolist");
+ info.S = PL;
+ walk_pointer_list_64("protocol", PL, O, &info, nullptr);
+
+ SectionRef MR = get_section(O, "__OBJC2", "__message_refs");
+ if (MR == SectionRef())
+ MR = get_section(O, "__DATA", "__objc_msgrefs");
+ if (MR == SectionRef())
+ MR = get_section(O, "__DATA_CONST", "__objc_msgrefs");
+ if (MR == SectionRef())
+ MR = get_section(O, "__DATA_DIRTY", "__objc_msgrefs");
+ info.S = MR;
+ print_message_refs64(MR, &info);
+
+ SectionRef II = get_section(O, "__OBJC2", "__image_info");
+ if (II == SectionRef())
+ II = get_section(O, "__DATA", "__objc_imageinfo");
+ if (II == SectionRef())
+ II = get_section(O, "__DATA_CONST", "__objc_imageinfo");
+ if (II == SectionRef())
+ II = get_section(O, "__DATA_DIRTY", "__objc_imageinfo");
+ info.S = II;
+ print_image_info64(II, &info);
+}
+
+static void printObjc2_32bit_MetaData(MachOObjectFile *O, bool verbose) {
+ SymbolAddressMap AddrMap;
+ if (verbose)
+ CreateSymbolAddressMap(O, &AddrMap);
+
+ std::vector<SectionRef> Sections;
+ append_range(Sections, O->sections());
+
+ struct DisassembleInfo info(O, &AddrMap, &Sections, verbose);
+
+ SectionRef CL = get_section(O, "__OBJC2", "__class_list");
+ if (CL == SectionRef())
+ CL = get_section(O, "__DATA", "__objc_classlist");
+ if (CL == SectionRef())
+ CL = get_section(O, "__DATA_CONST", "__objc_classlist");
+ if (CL == SectionRef())
+ CL = get_section(O, "__DATA_DIRTY", "__objc_classlist");
+ info.S = CL;
+ walk_pointer_list_32("class", CL, O, &info, print_class32_t);
+
+ SectionRef CR = get_section(O, "__OBJC2", "__class_refs");
+ if (CR == SectionRef())
+ CR = get_section(O, "__DATA", "__objc_classrefs");
+ if (CR == SectionRef())
+ CR = get_section(O, "__DATA_CONST", "__objc_classrefs");
+ if (CR == SectionRef())
+ CR = get_section(O, "__DATA_DIRTY", "__objc_classrefs");
+ info.S = CR;
+ walk_pointer_list_32("class refs", CR, O, &info, nullptr);
+
+ SectionRef SR = get_section(O, "__OBJC2", "__super_refs");
+ if (SR == SectionRef())
+ SR = get_section(O, "__DATA", "__objc_superrefs");
+ if (SR == SectionRef())
+ SR = get_section(O, "__DATA_CONST", "__objc_superrefs");
+ if (SR == SectionRef())
+ SR = get_section(O, "__DATA_DIRTY", "__objc_superrefs");
+ info.S = SR;
+ walk_pointer_list_32("super refs", SR, O, &info, nullptr);
+
+ SectionRef CA = get_section(O, "__OBJC2", "__category_list");
+ if (CA == SectionRef())
+ CA = get_section(O, "__DATA", "__objc_catlist");
+ if (CA == SectionRef())
+ CA = get_section(O, "__DATA_CONST", "__objc_catlist");
+ if (CA == SectionRef())
+ CA = get_section(O, "__DATA_DIRTY", "__objc_catlist");
+ info.S = CA;
+ walk_pointer_list_32("category", CA, O, &info, print_category32_t);
+
+ SectionRef PL = get_section(O, "__OBJC2", "__protocol_list");
+ if (PL == SectionRef())
+ PL = get_section(O, "__DATA", "__objc_protolist");
+ if (PL == SectionRef())
+ PL = get_section(O, "__DATA_CONST", "__objc_protolist");
+ if (PL == SectionRef())
+ PL = get_section(O, "__DATA_DIRTY", "__objc_protolist");
+ info.S = PL;
+ walk_pointer_list_32("protocol", PL, O, &info, nullptr);
+
+ SectionRef MR = get_section(O, "__OBJC2", "__message_refs");
+ if (MR == SectionRef())
+ MR = get_section(O, "__DATA", "__objc_msgrefs");
+ if (MR == SectionRef())
+ MR = get_section(O, "__DATA_CONST", "__objc_msgrefs");
+ if (MR == SectionRef())
+ MR = get_section(O, "__DATA_DIRTY", "__objc_msgrefs");
+ info.S = MR;
+ print_message_refs32(MR, &info);
+
+ SectionRef II = get_section(O, "__OBJC2", "__image_info");
+ if (II == SectionRef())
+ II = get_section(O, "__DATA", "__objc_imageinfo");
+ if (II == SectionRef())
+ II = get_section(O, "__DATA_CONST", "__objc_imageinfo");
+ if (II == SectionRef())
+ II = get_section(O, "__DATA_DIRTY", "__objc_imageinfo");
+ info.S = II;
+ print_image_info32(II, &info);
+}
+
+static bool printObjc1_32bit_MetaData(MachOObjectFile *O, bool verbose) {
+ uint32_t i, j, p, offset, xoffset, left, defs_left, def;
+ const char *r, *name, *defs;
+ struct objc_module_t module;
+ SectionRef S, xS;
+ struct objc_symtab_t symtab;
+ struct objc_class_t objc_class;
+ struct objc_category_t objc_category;
+
+ outs() << "Objective-C segment\n";
+ S = get_section(O, "__OBJC", "__module_info");
+ if (S == SectionRef())
+ return false;
+
+ SymbolAddressMap AddrMap;
+ if (verbose)
+ CreateSymbolAddressMap(O, &AddrMap);
+
+ std::vector<SectionRef> Sections;
+ append_range(Sections, O->sections());
+
+ struct DisassembleInfo info(O, &AddrMap, &Sections, verbose);
+
+ for (i = 0; i < S.getSize(); i += sizeof(struct objc_module_t)) {
+ p = S.getAddress() + i;
+ r = get_pointer_32(p, offset, left, S, &info, true);
+ if (r == nullptr)
+ return true;
+ memset(&module, '\0', sizeof(struct objc_module_t));
+ if (left < sizeof(struct objc_module_t)) {
+ memcpy(&module, r, left);
+ outs() << " (module extends past end of __module_info section)\n";
+ } else
+ memcpy(&module, r, sizeof(struct objc_module_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(module);
+
+ outs() << "Module " << format("0x%" PRIx32, p) << "\n";
+ outs() << " version " << module.version << "\n";
+ outs() << " size " << module.size << "\n";
+ outs() << " name ";
+ name = get_pointer_32(module.name, xoffset, left, xS, &info, true);
+ if (name != nullptr)
+ outs() << format("%.*s", left, name);
+ else
+ outs() << format("0x%08" PRIx32, module.name)
+ << "(not in an __OBJC section)";
+ outs() << "\n";
+
+ r = get_pointer_32(module.symtab, xoffset, left, xS, &info, true);
+ if (module.symtab == 0 || r == nullptr) {
+ outs() << " symtab " << format("0x%08" PRIx32, module.symtab)
+ << " (not in an __OBJC section)\n";
+ continue;
+ }
+ outs() << " symtab " << format("0x%08" PRIx32, module.symtab) << "\n";
+ memset(&symtab, '\0', sizeof(struct objc_symtab_t));
+ defs_left = 0;
+ defs = nullptr;
+ if (left < sizeof(struct objc_symtab_t)) {
+ memcpy(&symtab, r, left);
+ outs() << "\tsymtab extends past end of an __OBJC section)\n";
+ } else {
+ memcpy(&symtab, r, sizeof(struct objc_symtab_t));
+ if (left > sizeof(struct objc_symtab_t)) {
+ defs_left = left - sizeof(struct objc_symtab_t);
+ defs = r + sizeof(struct objc_symtab_t);
+ }
+ }
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(symtab);
+
+ outs() << "\tsel_ref_cnt " << symtab.sel_ref_cnt << "\n";
+ r = get_pointer_32(symtab.refs, xoffset, left, xS, &info, true);
+ outs() << "\trefs " << format("0x%08" PRIx32, symtab.refs);
+ if (r == nullptr)
+ outs() << " (not in an __OBJC section)";
+ outs() << "\n";
+ outs() << "\tcls_def_cnt " << symtab.cls_def_cnt << "\n";
+ outs() << "\tcat_def_cnt " << symtab.cat_def_cnt << "\n";
+ if (symtab.cls_def_cnt > 0)
+ outs() << "\tClass Definitions\n";
+ for (j = 0; j < symtab.cls_def_cnt; j++) {
+ if ((j + 1) * sizeof(uint32_t) > defs_left) {
+ outs() << "\t(remaining class defs entries entends past the end of the "
+ << "section)\n";
+ break;
+ }
+ memcpy(&def, defs + j * sizeof(uint32_t), sizeof(uint32_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(def);
+
+ r = get_pointer_32(def, xoffset, left, xS, &info, true);
+ outs() << "\tdefs[" << j << "] " << format("0x%08" PRIx32, def);
+ if (r != nullptr) {
+ if (left > sizeof(struct objc_class_t)) {
+ outs() << "\n";
+ memcpy(&objc_class, r, sizeof(struct objc_class_t));
+ } else {
+ outs() << " (entends past the end of the section)\n";
+ memset(&objc_class, '\0', sizeof(struct objc_class_t));
+ memcpy(&objc_class, r, left);
+ }
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(objc_class);
+ print_objc_class_t(&objc_class, &info);
+ } else {
+ outs() << "(not in an __OBJC section)\n";
+ }
+
+ if (CLS_GETINFO(&objc_class, CLS_CLASS)) {
+ outs() << "\tMeta Class";
+ r = get_pointer_32(objc_class.isa, xoffset, left, xS, &info, true);
+ if (r != nullptr) {
+ if (left > sizeof(struct objc_class_t)) {
+ outs() << "\n";
+ memcpy(&objc_class, r, sizeof(struct objc_class_t));
+ } else {
+ outs() << " (entends past the end of the section)\n";
+ memset(&objc_class, '\0', sizeof(struct objc_class_t));
+ memcpy(&objc_class, r, left);
+ }
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(objc_class);
+ print_objc_class_t(&objc_class, &info);
+ } else {
+ outs() << "(not in an __OBJC section)\n";
+ }
+ }
+ }
+ if (symtab.cat_def_cnt > 0)
+ outs() << "\tCategory Definitions\n";
+ for (j = 0; j < symtab.cat_def_cnt; j++) {
+ if ((j + symtab.cls_def_cnt + 1) * sizeof(uint32_t) > defs_left) {
+ outs() << "\t(remaining category defs entries entends past the end of "
+ << "the section)\n";
+ break;
+ }
+ memcpy(&def, defs + (j + symtab.cls_def_cnt) * sizeof(uint32_t),
+ sizeof(uint32_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(def);
+
+ r = get_pointer_32(def, xoffset, left, xS, &info, true);
+ outs() << "\tdefs[" << j + symtab.cls_def_cnt << "] "
+ << format("0x%08" PRIx32, def);
+ if (r != nullptr) {
+ if (left > sizeof(struct objc_category_t)) {
+ outs() << "\n";
+ memcpy(&objc_category, r, sizeof(struct objc_category_t));
+ } else {
+ outs() << " (entends past the end of the section)\n";
+ memset(&objc_category, '\0', sizeof(struct objc_category_t));
+ memcpy(&objc_category, r, left);
+ }
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(objc_category);
+ print_objc_objc_category_t(&objc_category, &info);
+ } else {
+ outs() << "(not in an __OBJC section)\n";
+ }
+ }
+ }
+ const SectionRef II = get_section(O, "__OBJC", "__image_info");
+ if (II != SectionRef())
+ print_image_info(II, &info);
+
+ return true;
+}
+
+static void DumpProtocolSection(MachOObjectFile *O, const char *sect,
+ uint32_t size, uint32_t addr) {
+ SymbolAddressMap AddrMap;
+ CreateSymbolAddressMap(O, &AddrMap);
+
+ std::vector<SectionRef> Sections;
+ append_range(Sections, O->sections());
+
+ struct DisassembleInfo info(O, &AddrMap, &Sections, true);
+
+ const char *p;
+ struct objc_protocol_t protocol;
+ uint32_t left, paddr;
+ for (p = sect; p < sect + size; p += sizeof(struct objc_protocol_t)) {
+ memset(&protocol, '\0', sizeof(struct objc_protocol_t));
+ left = size - (p - sect);
+ if (left < sizeof(struct objc_protocol_t)) {
+ outs() << "Protocol extends past end of __protocol section\n";
+ memcpy(&protocol, p, left);
+ } else
+ memcpy(&protocol, p, sizeof(struct objc_protocol_t));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ swapStruct(protocol);
+ paddr = addr + (p - sect);
+ outs() << "Protocol " << format("0x%" PRIx32, paddr);
+ if (print_protocol(paddr, 0, &info))
+ outs() << "(not in an __OBJC section)\n";
+ }
+}
+
+#ifdef LLVM_HAVE_LIBXAR
+static inline void swapStruct(struct xar_header &xar) {
+ sys::swapByteOrder(xar.magic);
+ sys::swapByteOrder(xar.size);
+ sys::swapByteOrder(xar.version);
+ sys::swapByteOrder(xar.toc_length_compressed);
+ sys::swapByteOrder(xar.toc_length_uncompressed);
+ sys::swapByteOrder(xar.cksum_alg);
+}
+
+static void PrintModeVerbose(uint32_t mode) {
+ switch(mode & S_IFMT){
+ case S_IFDIR:
+ outs() << "d";
+ break;
+ case S_IFCHR:
+ outs() << "c";
+ break;
+ case S_IFBLK:
+ outs() << "b";
+ break;
+ case S_IFREG:
+ outs() << "-";
+ break;
+ case S_IFLNK:
+ outs() << "l";
+ break;
+ case S_IFSOCK:
+ outs() << "s";
+ break;
+ default:
+ outs() << "?";
+ break;
+ }
+
+ /* owner permissions */
+ if(mode & S_IREAD)
+ outs() << "r";
+ else
+ outs() << "-";
+ if(mode & S_IWRITE)
+ outs() << "w";
+ else
+ outs() << "-";
+ if(mode & S_ISUID)
+ outs() << "s";
+ else if(mode & S_IEXEC)
+ outs() << "x";
+ else
+ outs() << "-";
+
+ /* group permissions */
+ if(mode & (S_IREAD >> 3))
+ outs() << "r";
+ else
+ outs() << "-";
+ if(mode & (S_IWRITE >> 3))
+ outs() << "w";
+ else
+ outs() << "-";
+ if(mode & S_ISGID)
+ outs() << "s";
+ else if(mode & (S_IEXEC >> 3))
+ outs() << "x";
+ else
+ outs() << "-";
+
+ /* other permissions */
+ if(mode & (S_IREAD >> 6))
+ outs() << "r";
+ else
+ outs() << "-";
+ if(mode & (S_IWRITE >> 6))
+ outs() << "w";
+ else
+ outs() << "-";
+ if(mode & S_ISVTX)
+ outs() << "t";
+ else if(mode & (S_IEXEC >> 6))
+ outs() << "x";
+ else
+ outs() << "-";
+}
+
+static void PrintXarFilesSummary(const char *XarFilename, xar_t xar) {
+ xar_file_t xf;
+ const char *key, *type, *mode, *user, *group, *size, *mtime, *name, *m;
+ char *endp;
+ uint32_t mode_value;
+
+ ScopedXarIter xi;
+ if (!xi) {
+ WithColor::error(errs(), "llvm-objdump")
+ << "can't obtain an xar iterator for xar archive " << XarFilename
+ << "\n";
+ return;
+ }
+
+ // Go through the xar's files.
+ for (xf = xar_file_first(xar, xi); xf; xf = xar_file_next(xi)) {
+ ScopedXarIter xp;
+ if(!xp){
+ WithColor::error(errs(), "llvm-objdump")
+ << "can't obtain an xar iterator for xar archive " << XarFilename
+ << "\n";
+ return;
+ }
+ type = nullptr;
+ mode = nullptr;
+ user = nullptr;
+ group = nullptr;
+ size = nullptr;
+ mtime = nullptr;
+ name = nullptr;
+ for(key = xar_prop_first(xf, xp); key; key = xar_prop_next(xp)){
+ const char *val = nullptr;
+ xar_prop_get(xf, key, &val);
+#if 0 // Useful for debugging.
+ outs() << "key: " << key << " value: " << val << "\n";
+#endif
+ if(strcmp(key, "type") == 0)
+ type = val;
+ if(strcmp(key, "mode") == 0)
+ mode = val;
+ if(strcmp(key, "user") == 0)
+ user = val;
+ if(strcmp(key, "group") == 0)
+ group = val;
+ if(strcmp(key, "data/size") == 0)
+ size = val;
+ if(strcmp(key, "mtime") == 0)
+ mtime = val;
+ if(strcmp(key, "name") == 0)
+ name = val;
+ }
+ if(mode != nullptr){
+ mode_value = strtoul(mode, &endp, 8);
+ if(*endp != '\0')
+ outs() << "(mode: \"" << mode << "\" contains non-octal chars) ";
+ if(strcmp(type, "file") == 0)
+ mode_value |= S_IFREG;
+ PrintModeVerbose(mode_value);
+ outs() << " ";
+ }
+ if(user != nullptr)
+ outs() << format("%10s/", user);
+ if(group != nullptr)
+ outs() << format("%-10s ", group);
+ if(size != nullptr)
+ outs() << format("%7s ", size);
+ if(mtime != nullptr){
+ for(m = mtime; *m != 'T' && *m != '\0'; m++)
+ outs() << *m;
+ if(*m == 'T')
+ m++;
+ outs() << " ";
+ for( ; *m != 'Z' && *m != '\0'; m++)
+ outs() << *m;
+ outs() << " ";
+ }
+ if(name != nullptr)
+ outs() << name;
+ outs() << "\n";
+ }
+}
+
+static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
+ uint32_t size, bool verbose,
+ bool PrintXarHeader, bool PrintXarFileHeaders,
+ std::string XarMemberName) {
+ if(size < sizeof(struct xar_header)) {
+ outs() << "size of (__LLVM,__bundle) section too small (smaller than size "
+ "of struct xar_header)\n";
+ return;
+ }
+ struct xar_header XarHeader;
+ memcpy(&XarHeader, sect, sizeof(struct xar_header));
+ if (sys::IsLittleEndianHost)
+ swapStruct(XarHeader);
+ if (PrintXarHeader) {
+ if (!XarMemberName.empty())
+ outs() << "In xar member " << XarMemberName << ": ";
+ else
+ outs() << "For (__LLVM,__bundle) section: ";
+ outs() << "xar header\n";
+ if (XarHeader.magic == XAR_HEADER_MAGIC)
+ outs() << " magic XAR_HEADER_MAGIC\n";
+ else
+ outs() << " magic "
+ << format_hex(XarHeader.magic, 10, true)
+ << " (not XAR_HEADER_MAGIC)\n";
+ outs() << " size " << XarHeader.size << "\n";
+ outs() << " version " << XarHeader.version << "\n";
+ outs() << " toc_length_compressed " << XarHeader.toc_length_compressed
+ << "\n";
+ outs() << "toc_length_uncompressed " << XarHeader.toc_length_uncompressed
+ << "\n";
+ outs() << " cksum_alg ";
+ switch (XarHeader.cksum_alg) {
+ case XAR_CKSUM_NONE:
+ outs() << "XAR_CKSUM_NONE\n";
+ break;
+ case XAR_CKSUM_SHA1:
+ outs() << "XAR_CKSUM_SHA1\n";
+ break;
+ case XAR_CKSUM_MD5:
+ outs() << "XAR_CKSUM_MD5\n";
+ break;
+#ifdef XAR_CKSUM_SHA256
+ case XAR_CKSUM_SHA256:
+ outs() << "XAR_CKSUM_SHA256\n";
+ break;
+#endif
+#ifdef XAR_CKSUM_SHA512
+ case XAR_CKSUM_SHA512:
+ outs() << "XAR_CKSUM_SHA512\n";
+ break;
+#endif
+ default:
+ outs() << XarHeader.cksum_alg << "\n";
+ }
+ }
+
+ SmallString<128> XarFilename;
+ int FD;
+ std::error_code XarEC =
+ sys::fs::createTemporaryFile("llvm-objdump", "xar", FD, XarFilename);
+ if (XarEC) {
+ WithColor::error(errs(), "llvm-objdump") << XarEC.message() << "\n";
+ return;
+ }
+ ToolOutputFile XarFile(XarFilename, FD);
+ raw_fd_ostream &XarOut = XarFile.os();
+ StringRef XarContents(sect, size);
+ XarOut << XarContents;
+ XarOut.close();
+ if (XarOut.has_error())
+ return;
+
+ ScopedXarFile xar(XarFilename.c_str(), READ);
+ if (!xar) {
+ WithColor::error(errs(), "llvm-objdump")
+ << "can't create temporary xar archive " << XarFilename << "\n";
+ return;
+ }
+
+ SmallString<128> TocFilename;
+ std::error_code TocEC =
+ sys::fs::createTemporaryFile("llvm-objdump", "toc", TocFilename);
+ if (TocEC) {
+ WithColor::error(errs(), "llvm-objdump") << TocEC.message() << "\n";
+ return;
+ }
+ xar_serialize(xar, TocFilename.c_str());
+
+ if (PrintXarFileHeaders) {
+ if (!XarMemberName.empty())
+ outs() << "In xar member " << XarMemberName << ": ";
+ else
+ outs() << "For (__LLVM,__bundle) section: ";
+ outs() << "xar archive files:\n";
+ PrintXarFilesSummary(XarFilename.c_str(), xar);
+ }
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
+ MemoryBuffer::getFileOrSTDIN(TocFilename.c_str());
+ if (std::error_code EC = FileOrErr.getError()) {
+ WithColor::error(errs(), "llvm-objdump") << EC.message() << "\n";
+ return;
+ }
+ std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
+
+ if (!XarMemberName.empty())
+ outs() << "In xar member " << XarMemberName << ": ";
+ else
+ outs() << "For (__LLVM,__bundle) section: ";
+ outs() << "xar table of contents:\n";
+ outs() << Buffer->getBuffer() << "\n";
+
+ // TODO: Go through the xar's files.
+ ScopedXarIter xi;
+ if(!xi){
+ 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)){
+ const char *key;
+ const char *member_name, *member_type, *member_size_string;
+ size_t member_size;
+
+ ScopedXarIter xp;
+ if(!xp){
+ WithColor::error(errs(), "llvm-objdump")
+ << "can't obtain an xar iterator for xar archive "
+ << XarFilename.c_str() << "\n";
+ return;
+ }
+ member_name = NULL;
+ member_type = NULL;
+ member_size_string = NULL;
+ for(key = xar_prop_first(xf, xp); key; key = xar_prop_next(xp)){
+ const char *val = nullptr;
+ xar_prop_get(xf, key, &val);
+#if 0 // Useful for debugging.
+ outs() << "key: " << key << " value: " << val << "\n";
+#endif
+ if (strcmp(key, "name") == 0)
+ member_name = val;
+ if (strcmp(key, "type") == 0)
+ member_type = val;
+ if (strcmp(key, "data/size") == 0)
+ member_size_string = val;
+ }
+ /*
+ * If we find a file with a name, date/size and type properties
+ * and with the type being "file" see if that is a xar file.
+ */
+ if (member_name != NULL && member_type != NULL &&
+ strcmp(member_type, "file") == 0 &&
+ member_size_string != NULL){
+ // Extract the file into a buffer.
+ char *endptr;
+ member_size = strtoul(member_size_string, &endptr, 10);
+ if (*endptr == '\0' && member_size != 0) {
+ char *buffer;
+ if (xar_extract_tobuffersz(xar, xf, &buffer, &member_size) == 0) {
+#if 0 // Useful for debugging.
+ outs() << "xar member: " << member_name << " extracted\n";
+#endif
+ // Set the XarMemberName we want to see printed in the header.
+ std::string OldXarMemberName;
+ // If XarMemberName is already set this is nested. So
+ // save the old name and create the nested name.
+ if (!XarMemberName.empty()) {
+ OldXarMemberName = XarMemberName;
+ XarMemberName =
+ (Twine("[") + XarMemberName + "]" + member_name).str();
+ } else {
+ OldXarMemberName = "";
+ XarMemberName = member_name;
+ }
+ // See if this is could be a xar file (nested).
+ if (member_size >= sizeof(struct xar_header)) {
+#if 0 // Useful for debugging.
+ outs() << "could be a xar file: " << member_name << "\n";
+#endif
+ memcpy((char *)&XarHeader, buffer, sizeof(struct xar_header));
+ if (sys::IsLittleEndianHost)
+ swapStruct(XarHeader);
+ if (XarHeader.magic == XAR_HEADER_MAGIC)
+ DumpBitcodeSection(O, buffer, member_size, verbose,
+ PrintXarHeader, PrintXarFileHeaders,
+ XarMemberName);
+ }
+ XarMemberName = OldXarMemberName;
+ delete buffer;
+ }
+ }
+ }
+ }
+}
+#endif // defined(LLVM_HAVE_LIBXAR)
+
+static void printObjcMetaData(MachOObjectFile *O, bool verbose) {
+ if (O->is64Bit())
+ printObjc2_64bit_MetaData(O, verbose);
+ else {
+ MachO::mach_header H;
+ H = O->getHeader();
+ if (H.cputype == MachO::CPU_TYPE_ARM)
+ printObjc2_32bit_MetaData(O, verbose);
+ else {
+ // This is the 32-bit non-arm cputype case. Which is normally
+ // the first Objective-C ABI. But it may be the case of a
+ // binary for the iOS simulator which is the second Objective-C
+ // ABI. In that case printObjc1_32bit_MetaData() will determine that
+ // and return false.
+ if (!printObjc1_32bit_MetaData(O, verbose))
+ printObjc2_32bit_MetaData(O, verbose);
+ }
+ }
+}
+
+// GuessLiteralPointer returns a string which for the item in the Mach-O file
+// for the address passed in as ReferenceValue for printing as a comment with
+// the instruction and also returns the corresponding type of that item
+// indirectly through ReferenceType.
+//
+// If ReferenceValue is an address of literal cstring then a pointer to the
+// cstring is returned and ReferenceType is set to
+// LLVMDisassembler_ReferenceType_Out_LitPool_CstrAddr .
+//
+// If ReferenceValue is an address of an Objective-C CFString, Selector ref or
+// Class ref that name is returned and the ReferenceType is set accordingly.
+//
+// Lastly, literals which are Symbol address in a literal pool are looked for
+// and if found the symbol name is returned and ReferenceType is set to
+// LLVMDisassembler_ReferenceType_Out_LitPool_SymAddr .
+//
+// If there is no item in the Mach-O file for the address passed in as
+// ReferenceValue nullptr is returned and ReferenceType is unchanged.
+static const char *GuessLiteralPointer(uint64_t ReferenceValue,
+ uint64_t ReferencePC,
+ uint64_t *ReferenceType,
+ struct DisassembleInfo *info) {
+ // First see if there is an external relocation entry at the ReferencePC.
+ if (info->O->getHeader().filetype == MachO::MH_OBJECT) {
+ uint64_t sect_addr = info->S.getAddress();
+ uint64_t sect_offset = ReferencePC - sect_addr;
+ bool reloc_found = false;
+ DataRefImpl Rel;
+ MachO::any_relocation_info RE;
+ bool isExtern = false;
+ SymbolRef Symbol;
+ for (const RelocationRef &Reloc : info->S.relocations()) {
+ uint64_t RelocOffset = Reloc.getOffset();
+ if (RelocOffset == sect_offset) {
+ Rel = Reloc.getRawDataRefImpl();
+ RE = info->O->getRelocation(Rel);
+ if (info->O->isRelocationScattered(RE))
+ continue;
+ isExtern = info->O->getPlainRelocationExternal(RE);
+ if (isExtern) {
+ symbol_iterator RelocSym = Reloc.getSymbol();
+ Symbol = *RelocSym;
+ }
+ reloc_found = true;
+ break;
+ }
+ }
+ // If there is an external relocation entry for a symbol in a section
+ // then used that symbol's value for the value of the reference.
+ if (reloc_found && isExtern) {
+ if (info->O->getAnyRelocationPCRel(RE)) {
+ unsigned Type = info->O->getAnyRelocationType(RE);
+ if (Type == MachO::X86_64_RELOC_SIGNED) {
+ ReferenceValue = cantFail(Symbol.getValue());
+ }
+ }
+ }
+ }
+
+ // Look for literals such as Objective-C CFStrings refs, Selector refs,
+ // Message refs and Class refs.
+ bool classref, selref, msgref, cfstring;
+ uint64_t pointer_value = GuessPointerPointer(ReferenceValue, info, classref,
+ selref, msgref, cfstring);
+ if (classref && pointer_value == 0) {
+ // Note the ReferenceValue is a pointer into the __objc_classrefs section.
+ // And the pointer_value in that section is typically zero as it will be
+ // set by dyld as part of the "bind information".
+ const char *name = get_dyld_bind_info_symbolname(ReferenceValue, info);
+ if (name != nullptr) {
+ *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Class_Ref;
+ const char *class_name = strrchr(name, '$');
+ if (class_name != nullptr && class_name[1] == '_' &&
+ class_name[2] != '\0') {
+ info->class_name = class_name + 2;
+ return name;
+ }
+ }
+ }
+
+ if (classref) {
+ *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Class_Ref;
+ const char *name =
+ get_objc2_64bit_class_name(pointer_value, ReferenceValue, info);
+ if (name != nullptr)
+ info->class_name = name;
+ else
+ name = "bad class ref";
+ return name;
+ }
+
+ if (cfstring) {
+ *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_CFString_Ref;
+ const char *name = get_objc2_64bit_cfstring_name(ReferenceValue, info);
+ return name;
+ }
+
+ if (selref && pointer_value == 0)
+ pointer_value = get_objc2_64bit_selref(ReferenceValue, info);
+
+ if (pointer_value != 0)
+ ReferenceValue = pointer_value;
+
+ const char *name = GuessCstringPointer(ReferenceValue, info);
+ if (name) {
+ if (pointer_value != 0 && selref) {
+ *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Selector_Ref;
+ info->selector_name = name;
+ } else if (pointer_value != 0 && msgref) {
+ info->class_name = nullptr;
+ *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message_Ref;
+ info->selector_name = name;
+ } else
+ *ReferenceType = LLVMDisassembler_ReferenceType_Out_LitPool_CstrAddr;
+ return name;
+ }
+
+ // Lastly look for an indirect symbol with this ReferenceValue which is in
+ // a literal pool. If found return that symbol name.
+ name = GuessIndirectSymbol(ReferenceValue, info);
+ if (name) {
+ *ReferenceType = LLVMDisassembler_ReferenceType_Out_LitPool_SymAddr;
+ return name;
+ }
+
+ return nullptr;
+}
+
+// SymbolizerSymbolLookUp is the symbol lookup function passed when creating
+// the Symbolizer. It looks up the ReferenceValue using the info passed via the
+// pointer to the struct DisassembleInfo that was passed when MCSymbolizer
+// is created and returns the symbol name that matches the ReferenceValue or
+// nullptr if none. The ReferenceType is passed in for the IN type of
+// reference the instruction is making from the values in defined in the header
+// "llvm-c/Disassembler.h". On return the ReferenceType can set to a specific
+// Out type and the ReferenceName will also be set which is added as a comment
+// to the disassembled instruction.
+//
+// If the symbol name is a C++ mangled name then the demangled name is
+// returned through ReferenceName and ReferenceType is set to
+// LLVMDisassembler_ReferenceType_DeMangled_Name .
+//
+// When this is called to get a symbol name for a branch target then the
+// ReferenceType will be LLVMDisassembler_ReferenceType_In_Branch and then
+// SymbolValue will be looked for in the indirect symbol table to determine if
+// it is an address for a symbol stub. If so then the symbol name for that
+// stub is returned indirectly through ReferenceName and then ReferenceType is
+// set to LLVMDisassembler_ReferenceType_Out_SymbolStub.
+//
+// When this is called with an value loaded via a PC relative load then
+// ReferenceType will be LLVMDisassembler_ReferenceType_In_PCrel_Load then the
+// SymbolValue is checked to be an address of literal pointer, symbol pointer,
+// or an Objective-C meta data reference. If so the output ReferenceType is
+// set to correspond to that as well as setting the ReferenceName.
+static const char *SymbolizerSymbolLookUp(void *DisInfo,
+ uint64_t ReferenceValue,
+ uint64_t *ReferenceType,
+ uint64_t ReferencePC,
+ const char **ReferenceName) {
+ struct DisassembleInfo *info = (struct DisassembleInfo *)DisInfo;
+ // If no verbose symbolic information is wanted then just return nullptr.
+ if (!info->verbose) {
+ *ReferenceName = nullptr;
+ *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None;
+ return nullptr;
+ }
+
+ const char *SymbolName = GuessSymbolName(ReferenceValue, info->AddrMap);
+
+ if (*ReferenceType == LLVMDisassembler_ReferenceType_In_Branch) {
+ *ReferenceName = GuessIndirectSymbol(ReferenceValue, info);
+ if (*ReferenceName != nullptr) {
+ method_reference(info, ReferenceType, ReferenceName);
+ if (*ReferenceType != LLVMDisassembler_ReferenceType_Out_Objc_Message)
+ *ReferenceType = LLVMDisassembler_ReferenceType_Out_SymbolStub;
+ } else if (SymbolName != nullptr && strncmp(SymbolName, "__Z", 3) == 0) {
+ if (info->demangled_name != nullptr)
+ free(info->demangled_name);
+ int status;
+ info->demangled_name =
+ itaniumDemangle(SymbolName + 1, nullptr, nullptr, &status);
+ if (info->demangled_name != nullptr) {
+ *ReferenceName = info->demangled_name;
+ *ReferenceType = LLVMDisassembler_ReferenceType_DeMangled_Name;
+ } else
+ *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None;
+ } else
+ *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None;
+ } else if (*ReferenceType == LLVMDisassembler_ReferenceType_In_PCrel_Load) {
+ *ReferenceName =
+ GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info);
+ if (*ReferenceName)
+ method_reference(info, ReferenceType, ReferenceName);
+ else
+ *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None;
+ // If this is arm64 and the reference is an adrp instruction save the
+ // instruction, passed in ReferenceValue and the address of the instruction
+ // for use later if we see and add immediate instruction.
+ } else if (info->O->getArch() == Triple::aarch64 &&
+ *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_ADRP) {
+ info->adrp_inst = ReferenceValue;
+ info->adrp_addr = ReferencePC;
+ SymbolName = nullptr;
+ *ReferenceName = nullptr;
+ *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None;
+ // If this is arm64 and reference is an add immediate instruction and we
+ // have
+ // seen an adrp instruction just before it and the adrp's Xd register
+ // matches
+ // this add's Xn register reconstruct the value being referenced and look to
+ // see if it is a literal pointer. Note the add immediate instruction is
+ // passed in ReferenceValue.
+ } else if (info->O->getArch() == Triple::aarch64 &&
+ *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_ADDXri &&
+ ReferencePC - 4 == info->adrp_addr &&
+ (info->adrp_inst & 0x9f000000) == 0x90000000 &&
+ (info->adrp_inst & 0x1f) == ((ReferenceValue >> 5) & 0x1f)) {
+ uint32_t addxri_inst;
+ uint64_t adrp_imm, addxri_imm;
+
+ adrp_imm =
+ ((info->adrp_inst & 0x00ffffe0) >> 3) | ((info->adrp_inst >> 29) & 0x3);
+ if (info->adrp_inst & 0x0200000)
+ adrp_imm |= 0xfffffffffc000000LL;
+
+ addxri_inst = ReferenceValue;
+ addxri_imm = (addxri_inst >> 10) & 0xfff;
+ if (((addxri_inst >> 22) & 0x3) == 1)
+ addxri_imm <<= 12;
+
+ ReferenceValue = (info->adrp_addr & 0xfffffffffffff000LL) +
+ (adrp_imm << 12) + addxri_imm;
+
+ *ReferenceName =
+ GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info);
+ if (*ReferenceName == nullptr)
+ *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None;
+ // If this is arm64 and the reference is a load register instruction and we
+ // have seen an adrp instruction just before it and the adrp's Xd register
+ // matches this add's Xn register reconstruct the value being referenced and
+ // look to see if it is a literal pointer. Note the load register
+ // instruction is passed in ReferenceValue.
+ } else if (info->O->getArch() == Triple::aarch64 &&
+ *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_LDRXui &&
+ ReferencePC - 4 == info->adrp_addr &&
+ (info->adrp_inst & 0x9f000000) == 0x90000000 &&
+ (info->adrp_inst & 0x1f) == ((ReferenceValue >> 5) & 0x1f)) {
+ uint32_t ldrxui_inst;
+ uint64_t adrp_imm, ldrxui_imm;
+
+ adrp_imm =
+ ((info->adrp_inst & 0x00ffffe0) >> 3) | ((info->adrp_inst >> 29) & 0x3);
+ if (info->adrp_inst & 0x0200000)
+ adrp_imm |= 0xfffffffffc000000LL;
+
+ ldrxui_inst = ReferenceValue;
+ ldrxui_imm = (ldrxui_inst >> 10) & 0xfff;
+
+ ReferenceValue = (info->adrp_addr & 0xfffffffffffff000LL) +
+ (adrp_imm << 12) + (ldrxui_imm << 3);
+
+ *ReferenceName =
+ GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info);
+ if (*ReferenceName == nullptr)
+ *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None;
+ }
+ // If this arm64 and is an load register (PC-relative) instruction the
+ // ReferenceValue is the PC plus the immediate value.
+ else if (info->O->getArch() == Triple::aarch64 &&
+ (*ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_LDRXl ||
+ *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_ADR)) {
+ *ReferenceName =
+ GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info);
+ if (*ReferenceName == nullptr)
+ *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None;
+ } else if (SymbolName != nullptr && strncmp(SymbolName, "__Z", 3) == 0) {
+ if (info->demangled_name != nullptr)
+ free(info->demangled_name);
+ int status;
+ info->demangled_name =
+ itaniumDemangle(SymbolName + 1, nullptr, nullptr, &status);
+ if (info->demangled_name != nullptr) {
+ *ReferenceName = info->demangled_name;
+ *ReferenceType = LLVMDisassembler_ReferenceType_DeMangled_Name;
+ }
+ }
+ else {
+ *ReferenceName = nullptr;
+ *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None;
+ }
+
+ return SymbolName;
+}
+
+/// Emits the comments that are stored in the CommentStream.
+/// Each comment in the CommentStream must end with a newline.
+static void emitComments(raw_svector_ostream &CommentStream,
+ SmallString<128> &CommentsToEmit,
+ formatted_raw_ostream &FormattedOS,
+ const MCAsmInfo &MAI) {
+ // Flush the stream before taking its content.
+ StringRef Comments = CommentsToEmit.str();
+ // Get the default information for printing a comment.
+ StringRef CommentBegin = MAI.getCommentString();
+ unsigned CommentColumn = MAI.getCommentColumn();
+ ListSeparator LS("\n");
+ while (!Comments.empty()) {
+ FormattedOS << LS;
+ // Emit a line of comments.
+ FormattedOS.PadToColumn(CommentColumn);
+ size_t Position = Comments.find('\n');
+ FormattedOS << CommentBegin << ' ' << Comments.substr(0, Position);
+ // Move after the newline character.
+ Comments = Comments.substr(Position + 1);
+ }
+ FormattedOS.flush();
+
+ // Tell the comment stream that the vector changed underneath it.
+ CommentsToEmit.clear();
+}
+
+static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
+ StringRef DisSegName, StringRef DisSectName) {
+ const char *McpuDefault = nullptr;
+ const Target *ThumbTarget = nullptr;
+ const Target *TheTarget = GetTarget(MachOOF, &McpuDefault, &ThumbTarget);
+ if (!TheTarget) {
+ // GetTarget prints out stuff.
+ return;
+ }
+ std::string MachOMCPU;
+ if (MCPU.empty() && McpuDefault)
+ MachOMCPU = McpuDefault;
+ else
+ MachOMCPU = MCPU;
+
+#define CHECK_TARGET_INFO_CREATION(NAME) \
+ do { \
+ if (!NAME) { \
+ WithColor::error(errs(), "llvm-objdump") \
+ << "couldn't initialize disassembler for target " << TripleName \
+ << '\n'; \
+ return; \
+ } \
+ } while (false)
+#define CHECK_THUMB_TARGET_INFO_CREATION(NAME) \
+ do { \
+ if (!NAME) { \
+ WithColor::error(errs(), "llvm-objdump") \
+ << "couldn't initialize disassembler for target " << ThumbTripleName \
+ << '\n'; \
+ return; \
+ } \
+ } while (false)
+
+ std::unique_ptr<const MCInstrInfo> InstrInfo(TheTarget->createMCInstrInfo());
+ CHECK_TARGET_INFO_CREATION(InstrInfo);
+ std::unique_ptr<const MCInstrInfo> ThumbInstrInfo;
+ if (ThumbTarget) {
+ ThumbInstrInfo.reset(ThumbTarget->createMCInstrInfo());
+ CHECK_THUMB_TARGET_INFO_CREATION(ThumbInstrInfo);
+ }
+
+ // Package up features to be passed to target/subtarget
+ std::string FeaturesStr;
+ if (!MAttrs.empty()) {
+ SubtargetFeatures Features;
+ for (unsigned i = 0; i != MAttrs.size(); ++i)
+ Features.AddFeature(MAttrs[i]);
+ FeaturesStr = Features.getString();
+ }
+
+ MCTargetOptions MCOptions;
+ // Set up disassembler.
+ std::unique_ptr<const MCRegisterInfo> MRI(
+ TheTarget->createMCRegInfo(TripleName));
+ CHECK_TARGET_INFO_CREATION(MRI);
+ std::unique_ptr<const MCAsmInfo> AsmInfo(
+ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ CHECK_TARGET_INFO_CREATION(AsmInfo);
+ std::unique_ptr<const MCSubtargetInfo> STI(
+ TheTarget->createMCSubtargetInfo(TripleName, MachOMCPU, FeaturesStr));
+ CHECK_TARGET_INFO_CREATION(STI);
+ MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get());
+ std::unique_ptr<MCDisassembler> DisAsm(
+ TheTarget->createMCDisassembler(*STI, Ctx));
+ CHECK_TARGET_INFO_CREATION(DisAsm);
+ std::unique_ptr<MCSymbolizer> Symbolizer;
+ struct DisassembleInfo SymbolizerInfo(nullptr, nullptr, nullptr, false);
+ std::unique_ptr<MCRelocationInfo> RelInfo(
+ TheTarget->createMCRelocationInfo(TripleName, Ctx));
+ if (RelInfo) {
+ Symbolizer.reset(TheTarget->createMCSymbolizer(
+ TripleName, SymbolizerGetOpInfo, SymbolizerSymbolLookUp,
+ &SymbolizerInfo, &Ctx, std::move(RelInfo)));
+ DisAsm->setSymbolizer(std::move(Symbolizer));
+ }
+ int AsmPrinterVariant = AsmInfo->getAssemblerDialect();
+ std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
+ Triple(TripleName), AsmPrinterVariant, *AsmInfo, *InstrInfo, *MRI));
+ CHECK_TARGET_INFO_CREATION(IP);
+ // Set the display preference for hex vs. decimal immediates.
+ IP->setPrintImmHex(PrintImmHex);
+ // Comment stream and backing vector.
+ SmallString<128> CommentsToEmit;
+ raw_svector_ostream CommentStream(CommentsToEmit);
+ // FIXME: Setting the CommentStream in the InstPrinter is problematic in that
+ // if it is done then arm64 comments for string literals don't get printed
+ // and some constant get printed instead and not setting it causes intel
+ // (32-bit and 64-bit) comments printed with different spacing before the
+ // comment causing different diffs with the 'C' disassembler library API.
+ // IP->setCommentStream(CommentStream);
+
+ // Set up separate thumb disassembler if needed.
+ std::unique_ptr<const MCRegisterInfo> ThumbMRI;
+ std::unique_ptr<const MCAsmInfo> ThumbAsmInfo;
+ std::unique_ptr<const MCSubtargetInfo> ThumbSTI;
+ std::unique_ptr<MCDisassembler> ThumbDisAsm;
+ std::unique_ptr<MCInstPrinter> ThumbIP;
+ std::unique_ptr<MCContext> ThumbCtx;
+ std::unique_ptr<MCSymbolizer> ThumbSymbolizer;
+ struct DisassembleInfo ThumbSymbolizerInfo(nullptr, nullptr, nullptr, false);
+ std::unique_ptr<MCRelocationInfo> ThumbRelInfo;
+ if (ThumbTarget) {
+ ThumbMRI.reset(ThumbTarget->createMCRegInfo(ThumbTripleName));
+ CHECK_THUMB_TARGET_INFO_CREATION(ThumbMRI);
+ ThumbAsmInfo.reset(
+ ThumbTarget->createMCAsmInfo(*ThumbMRI, ThumbTripleName, MCOptions));
+ CHECK_THUMB_TARGET_INFO_CREATION(ThumbAsmInfo);
+ ThumbSTI.reset(
+ ThumbTarget->createMCSubtargetInfo(ThumbTripleName, MachOMCPU,
+ FeaturesStr));
+ CHECK_THUMB_TARGET_INFO_CREATION(ThumbSTI);
+ ThumbCtx.reset(new MCContext(Triple(ThumbTripleName), ThumbAsmInfo.get(),
+ ThumbMRI.get(), ThumbSTI.get()));
+ ThumbDisAsm.reset(ThumbTarget->createMCDisassembler(*ThumbSTI, *ThumbCtx));
+ CHECK_THUMB_TARGET_INFO_CREATION(ThumbDisAsm);
+ MCContext *PtrThumbCtx = ThumbCtx.get();
+ ThumbRelInfo.reset(
+ ThumbTarget->createMCRelocationInfo(ThumbTripleName, *PtrThumbCtx));
+ if (ThumbRelInfo) {
+ ThumbSymbolizer.reset(ThumbTarget->createMCSymbolizer(
+ ThumbTripleName, SymbolizerGetOpInfo, SymbolizerSymbolLookUp,
+ &ThumbSymbolizerInfo, PtrThumbCtx, std::move(ThumbRelInfo)));
+ ThumbDisAsm->setSymbolizer(std::move(ThumbSymbolizer));
+ }
+ int ThumbAsmPrinterVariant = ThumbAsmInfo->getAssemblerDialect();
+ ThumbIP.reset(ThumbTarget->createMCInstPrinter(
+ Triple(ThumbTripleName), ThumbAsmPrinterVariant, *ThumbAsmInfo,
+ *ThumbInstrInfo, *ThumbMRI));
+ CHECK_THUMB_TARGET_INFO_CREATION(ThumbIP);
+ // Set the display preference for hex vs. decimal immediates.
+ ThumbIP->setPrintImmHex(PrintImmHex);
+ }
+
+#undef CHECK_TARGET_INFO_CREATION
+#undef CHECK_THUMB_TARGET_INFO_CREATION
+
+ MachO::mach_header Header = MachOOF->getHeader();
+
+ // FIXME: Using the -cfg command line option, this code used to be able to
+ // annotate relocations with the referenced symbol's name, and if this was
+ // inside a __[cf]string section, the data it points to. This is now replaced
+ // by the upcoming MCSymbolizer, which needs the appropriate setup done above.
+ std::vector<SectionRef> Sections;
+ std::vector<SymbolRef> Symbols;
+ SmallVector<uint64_t, 8> FoundFns;
+ uint64_t BaseSegmentAddress = 0;
+
+ getSectionsAndSymbols(MachOOF, Sections, Symbols, FoundFns,
+ BaseSegmentAddress);
+
+ // Sort the symbols by address, just in case they didn't come in that way.
+ llvm::stable_sort(Symbols, SymbolSorter());
+
+ // Build a data in code table that is sorted on by the address of each entry.
+ uint64_t BaseAddress = 0;
+ if (Header.filetype == MachO::MH_OBJECT)
+ BaseAddress = Sections[0].getAddress();
+ else
+ BaseAddress = BaseSegmentAddress;
+ DiceTable Dices;
+ for (dice_iterator DI = MachOOF->begin_dices(), DE = MachOOF->end_dices();
+ DI != DE; ++DI) {
+ uint32_t Offset;
+ DI->getOffset(Offset);
+ Dices.push_back(std::make_pair(BaseAddress + Offset, *DI));
+ }
+ array_pod_sort(Dices.begin(), Dices.end());
+
+ // Try to find debug info and set up the DIContext for it.
+ std::unique_ptr<DIContext> diContext;
+ std::unique_ptr<Binary> DSYMBinary;
+ std::unique_ptr<MemoryBuffer> DSYMBuf;
+ if (UseDbg) {
+ ObjectFile *DbgObj = MachOOF;
+
+ // A separate DSym file path was specified, parse it as a macho file,
+ // get the sections and supply it to the section name parsing machinery.
+ if (!DSYMFile.empty()) {
+ std::string DSYMPath(DSYMFile);
+
+ // If DSYMPath is a .dSYM directory, append the Mach-O file.
+ if (llvm::sys::fs::is_directory(DSYMPath) &&
+ llvm::sys::path::extension(DSYMPath) == ".dSYM") {
+ SmallString<128> ShortName(llvm::sys::path::filename(DSYMPath));
+ llvm::sys::path::replace_extension(ShortName, "");
+ SmallString<1024> FullPath(DSYMPath);
+ llvm::sys::path::append(FullPath, "Contents", "Resources", "DWARF",
+ ShortName);
+ DSYMPath = std::string(FullPath.str());
+ }
+
+ // Load the file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFileOrSTDIN(DSYMPath);
+ if (std::error_code EC = BufOrErr.getError()) {
+ reportError(errorCodeToError(EC), DSYMPath);
+ return;
+ }
+
+ // We need to keep the file alive, because we're replacing DbgObj with it.
+ DSYMBuf = std::move(BufOrErr.get());
+
+ Expected<std::unique_ptr<Binary>> BinaryOrErr =
+ createBinary(DSYMBuf.get()->getMemBufferRef());
+ if (!BinaryOrErr) {
+ reportError(BinaryOrErr.takeError(), DSYMPath);
+ return;
+ }
+
+ // We need to keep the Binary alive with the buffer
+ DSYMBinary = std::move(BinaryOrErr.get());
+ if (ObjectFile *O = dyn_cast<ObjectFile>(DSYMBinary.get())) {
+ // this is a Mach-O object file, use it
+ if (MachOObjectFile *MachDSYM = dyn_cast<MachOObjectFile>(&*O)) {
+ DbgObj = MachDSYM;
+ }
+ else {
+ WithColor::error(errs(), "llvm-objdump")
+ << DSYMPath << " is not a Mach-O file type.\n";
+ return;
+ }
+ }
+ else if (auto UB = dyn_cast<MachOUniversalBinary>(DSYMBinary.get())){
+ // this is a Universal Binary, find a Mach-O for this architecture
+ uint32_t CPUType, CPUSubType;
+ const char *ArchFlag;
+ if (MachOOF->is64Bit()) {
+ const MachO::mach_header_64 H_64 = MachOOF->getHeader64();
+ CPUType = H_64.cputype;
+ CPUSubType = H_64.cpusubtype;
+ } else {
+ const MachO::mach_header H = MachOOF->getHeader();
+ CPUType = H.cputype;
+ CPUSubType = H.cpusubtype;
+ }
+ Triple T = MachOObjectFile::getArchTriple(CPUType, CPUSubType, nullptr,
+ &ArchFlag);
+ Expected<std::unique_ptr<MachOObjectFile>> MachDSYM =
+ UB->getMachOObjectForArch(ArchFlag);
+ if (!MachDSYM) {
+ reportError(MachDSYM.takeError(), DSYMPath);
+ return;
+ }
+
+ // We need to keep the Binary alive with the buffer
+ DbgObj = &*MachDSYM.get();
+ DSYMBinary = std::move(*MachDSYM);
+ }
+ else {
+ WithColor::error(errs(), "llvm-objdump")
+ << DSYMPath << " is not a Mach-O or Universal file type.\n";
+ return;
+ }
+ }
+
+ // Setup the DIContext
+ diContext = DWARFContext::create(*DbgObj);
+ }
+
+ if (FilterSections.empty())
+ outs() << "(" << DisSegName << "," << DisSectName << ") section\n";
+
+ for (unsigned SectIdx = 0; SectIdx != Sections.size(); SectIdx++) {
+ Expected<StringRef> SecNameOrErr = Sections[SectIdx].getName();
+ if (!SecNameOrErr) {
+ consumeError(SecNameOrErr.takeError());
+ continue;
+ }
+ if (*SecNameOrErr != DisSectName)
+ continue;
+
+ DataRefImpl DR = Sections[SectIdx].getRawDataRefImpl();
+
+ StringRef SegmentName = MachOOF->getSectionFinalSegmentName(DR);
+ if (SegmentName != DisSegName)
+ continue;
+
+ StringRef BytesStr =
+ unwrapOrError(Sections[SectIdx].getContents(), Filename);
+ ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(BytesStr);
+ uint64_t SectAddress = Sections[SectIdx].getAddress();
+
+ bool symbolTableWorked = false;
+
+ // Create a map of symbol addresses to symbol names for use by
+ // the SymbolizerSymbolLookUp() routine.
+ SymbolAddressMap AddrMap;
+ bool DisSymNameFound = false;
+ for (const SymbolRef &Symbol : MachOOF->symbols()) {
+ SymbolRef::Type ST =
+ unwrapOrError(Symbol.getType(), MachOOF->getFileName());
+ if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data ||
+ ST == SymbolRef::ST_Other) {
+ uint64_t Address = cantFail(Symbol.getValue());
+ StringRef SymName =
+ unwrapOrError(Symbol.getName(), MachOOF->getFileName());
+ AddrMap[Address] = SymName;
+ if (!DisSymName.empty() && DisSymName == SymName)
+ DisSymNameFound = true;
+ }
+ }
+ if (!DisSymName.empty() && !DisSymNameFound) {
+ outs() << "Can't find -dis-symname: " << DisSymName << "\n";
+ return;
+ }
+ // Set up the block of info used by the Symbolizer call backs.
+ SymbolizerInfo.verbose = SymbolicOperands;
+ SymbolizerInfo.O = MachOOF;
+ SymbolizerInfo.S = Sections[SectIdx];
+ SymbolizerInfo.AddrMap = &AddrMap;
+ SymbolizerInfo.Sections = &Sections;
+ // Same for the ThumbSymbolizer
+ ThumbSymbolizerInfo.verbose = SymbolicOperands;
+ ThumbSymbolizerInfo.O = MachOOF;
+ ThumbSymbolizerInfo.S = Sections[SectIdx];
+ ThumbSymbolizerInfo.AddrMap = &AddrMap;
+ ThumbSymbolizerInfo.Sections = &Sections;
+
+ unsigned int Arch = MachOOF->getArch();
+
+ // Skip all symbols if this is a stubs file.
+ if (Bytes.empty())
+ return;
+
+ // If the section has symbols but no symbol at the start of the section
+ // these are used to make sure the bytes before the first symbol are
+ // disassembled.
+ bool FirstSymbol = true;
+ bool FirstSymbolAtSectionStart = true;
+
+ // Disassemble symbol by symbol.
+ for (unsigned SymIdx = 0; SymIdx != Symbols.size(); SymIdx++) {
+ StringRef SymName =
+ unwrapOrError(Symbols[SymIdx].getName(), MachOOF->getFileName());
+ SymbolRef::Type ST =
+ unwrapOrError(Symbols[SymIdx].getType(), MachOOF->getFileName());
+ if (ST != SymbolRef::ST_Function && ST != SymbolRef::ST_Data)
+ continue;
+
+ // Make sure the symbol is defined in this section.
+ bool containsSym = Sections[SectIdx].containsSymbol(Symbols[SymIdx]);
+ if (!containsSym) {
+ if (!DisSymName.empty() && DisSymName == SymName) {
+ outs() << "-dis-symname: " << DisSymName << " not in the section\n";
+ return;
+ }
+ continue;
+ }
+ // The __mh_execute_header is special and we need to deal with that fact
+ // this symbol is before the start of the (__TEXT,__text) section and at the
+ // address of the start of the __TEXT segment. This is because this symbol
+ // is an N_SECT symbol in the (__TEXT,__text) but its address is before the
+ // start of the section in a standard MH_EXECUTE filetype.
+ if (!DisSymName.empty() && DisSymName == "__mh_execute_header") {
+ outs() << "-dis-symname: __mh_execute_header not in any section\n";
+ return;
+ }
+ // When this code is trying to disassemble a symbol at a time and in the
+ // case there is only the __mh_execute_header symbol left as in a stripped
+ // executable, we need to deal with this by ignoring this symbol so the
+ // whole section is disassembled and this symbol is then not displayed.
+ if (SymName == "__mh_execute_header" || SymName == "__mh_dylib_header" ||
+ SymName == "__mh_bundle_header" || SymName == "__mh_object_header" ||
+ SymName == "__mh_preload_header" || SymName == "__mh_dylinker_header")
+ continue;
+
+ // If we are only disassembling one symbol see if this is that symbol.
+ if (!DisSymName.empty() && DisSymName != SymName)
+ continue;
+
+ // Start at the address of the symbol relative to the section's address.
+ uint64_t SectSize = Sections[SectIdx].getSize();
+ uint64_t Start = cantFail(Symbols[SymIdx].getValue());
+ uint64_t SectionAddress = Sections[SectIdx].getAddress();
+ Start -= SectionAddress;
+
+ if (Start > SectSize) {
+ outs() << "section data ends, " << SymName
+ << " lies outside valid range\n";
+ return;
+ }
+
+ // Stop disassembling either at the beginning of the next symbol or at
+ // the end of the section.
+ bool containsNextSym = false;
+ uint64_t NextSym = 0;
+ uint64_t NextSymIdx = SymIdx + 1;
+ while (Symbols.size() > NextSymIdx) {
+ SymbolRef::Type NextSymType = unwrapOrError(
+ Symbols[NextSymIdx].getType(), MachOOF->getFileName());
+ if (NextSymType == SymbolRef::ST_Function) {
+ containsNextSym =
+ Sections[SectIdx].containsSymbol(Symbols[NextSymIdx]);
+ NextSym = cantFail(Symbols[NextSymIdx].getValue());
+ NextSym -= SectionAddress;
+ break;
+ }
+ ++NextSymIdx;
+ }
+
+ uint64_t End = containsNextSym ? std::min(NextSym, SectSize) : SectSize;
+ uint64_t Size;
+
+ symbolTableWorked = true;
+
+ DataRefImpl Symb = Symbols[SymIdx].getRawDataRefImpl();
+ uint32_t SymbolFlags = cantFail(MachOOF->getSymbolFlags(Symb));
+ bool IsThumb = SymbolFlags & SymbolRef::SF_Thumb;
+
+ // We only need the dedicated Thumb target if there's a real choice
+ // (i.e. we're not targeting M-class) and the function is Thumb.
+ bool UseThumbTarget = IsThumb && ThumbTarget;
+
+ // If we are not specifying a symbol to start disassembly with and this
+ // is the first symbol in the section but not at the start of the section
+ // then move the disassembly index to the start of the section and
+ // don't print the symbol name just yet. This is so the bytes before the
+ // first symbol are disassembled.
+ uint64_t SymbolStart = Start;
+ if (DisSymName.empty() && FirstSymbol && Start != 0) {
+ FirstSymbolAtSectionStart = false;
+ Start = 0;
+ }
+ else
+ outs() << SymName << ":\n";
+
+ DILineInfo lastLine;
+ for (uint64_t Index = Start; Index < End; Index += Size) {
+ MCInst Inst;
+
+ // If this is the first symbol in the section and it was not at the
+ // start of the section, see if we are at its Index now and if so print
+ // the symbol name.
+ if (FirstSymbol && !FirstSymbolAtSectionStart && Index == SymbolStart)
+ outs() << SymName << ":\n";
+
+ uint64_t PC = SectAddress + Index;
+ if (LeadingAddr) {
+ if (FullLeadingAddr) {
+ if (MachOOF->is64Bit())
+ outs() << format("%016" PRIx64, PC);
+ else
+ outs() << format("%08" PRIx64, PC);
+ } else {
+ outs() << format("%8" PRIx64 ":", PC);
+ }
+ }
+ if (ShowRawInsn || Arch == Triple::arm)
+ outs() << "\t";
+
+ if (DumpAndSkipDataInCode(PC, Bytes.data() + Index, Dices, Size))
+ continue;
+
+ SmallVector<char, 64> AnnotationsBytes;
+ raw_svector_ostream Annotations(AnnotationsBytes);
+
+ bool gotInst;
+ if (UseThumbTarget)
+ gotInst = ThumbDisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
+ PC, Annotations);
+ else
+ gotInst = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), PC,
+ Annotations);
+ if (gotInst) {
+ if (ShowRawInsn || Arch == Triple::arm) {
+ dumpBytes(makeArrayRef(Bytes.data() + Index, Size), outs());
+ }
+ formatted_raw_ostream FormattedOS(outs());
+ StringRef AnnotationsStr = Annotations.str();
+ if (UseThumbTarget)
+ ThumbIP->printInst(&Inst, PC, AnnotationsStr, *ThumbSTI,
+ FormattedOS);
+ else
+ IP->printInst(&Inst, PC, AnnotationsStr, *STI, FormattedOS);
+ emitComments(CommentStream, CommentsToEmit, FormattedOS, *AsmInfo);
+
+ // Print debug info.
+ if (diContext) {
+ DILineInfo dli = diContext->getLineInfoForAddress({PC, SectIdx});
+ // Print valid line info if it changed.
+ if (dli != lastLine && dli.Line != 0)
+ outs() << "\t## " << dli.FileName << ':' << dli.Line << ':'
+ << dli.Column;
+ lastLine = dli;
+ }
+ outs() << "\n";
+ } else {
+ if (MachOOF->getArchTriple().isX86()) {
+ outs() << format("\t.byte 0x%02x #bad opcode\n",
+ *(Bytes.data() + Index) & 0xff);
+ Size = 1; // skip exactly one illegible byte and move on.
+ } else if (Arch == Triple::aarch64 ||
+ (Arch == Triple::arm && !IsThumb)) {
+ uint32_t opcode = (*(Bytes.data() + Index) & 0xff) |
+ (*(Bytes.data() + Index + 1) & 0xff) << 8 |
+ (*(Bytes.data() + Index + 2) & 0xff) << 16 |
+ (*(Bytes.data() + Index + 3) & 0xff) << 24;
+ outs() << format("\t.long\t0x%08x\n", opcode);
+ Size = 4;
+ } else if (Arch == Triple::arm) {
+ assert(IsThumb && "ARM mode should have been dealt with above");
+ uint32_t opcode = (*(Bytes.data() + Index) & 0xff) |
+ (*(Bytes.data() + Index + 1) & 0xff) << 8;
+ outs() << format("\t.short\t0x%04x\n", opcode);
+ Size = 2;
+ } else{
+ WithColor::warning(errs(), "llvm-objdump")
+ << "invalid instruction encoding\n";
+ if (Size == 0)
+ Size = 1; // skip illegible bytes
+ }
+ }
+ }
+ // Now that we are done disassembled the first symbol set the bool that
+ // were doing this to false.
+ FirstSymbol = false;
+ }
+ if (!symbolTableWorked) {
+ // Reading the symbol table didn't work, disassemble the whole section.
+ uint64_t SectAddress = Sections[SectIdx].getAddress();
+ uint64_t SectSize = Sections[SectIdx].getSize();
+ uint64_t InstSize;
+ for (uint64_t Index = 0; Index < SectSize; Index += InstSize) {
+ MCInst Inst;
+
+ uint64_t PC = SectAddress + Index;
+
+ if (DumpAndSkipDataInCode(PC, Bytes.data() + Index, Dices, InstSize))
+ continue;
+
+ SmallVector<char, 64> AnnotationsBytes;
+ raw_svector_ostream Annotations(AnnotationsBytes);
+ if (DisAsm->getInstruction(Inst, InstSize, Bytes.slice(Index), PC,
+ Annotations)) {
+ if (LeadingAddr) {
+ if (FullLeadingAddr) {
+ if (MachOOF->is64Bit())
+ outs() << format("%016" PRIx64, PC);
+ else
+ outs() << format("%08" PRIx64, PC);
+ } else {
+ outs() << format("%8" PRIx64 ":", PC);
+ }
+ }
+ if (ShowRawInsn || Arch == Triple::arm) {
+ outs() << "\t";
+ dumpBytes(makeArrayRef(Bytes.data() + Index, InstSize), outs());
+ }
+ StringRef AnnotationsStr = Annotations.str();
+ IP->printInst(&Inst, PC, AnnotationsStr, *STI, outs());
+ outs() << "\n";
+ } else {
+ if (MachOOF->getArchTriple().isX86()) {
+ outs() << format("\t.byte 0x%02x #bad opcode\n",
+ *(Bytes.data() + Index) & 0xff);
+ InstSize = 1; // skip exactly one illegible byte and move on.
+ } else {
+ WithColor::warning(errs(), "llvm-objdump")
+ << "invalid instruction encoding\n";
+ if (InstSize == 0)
+ InstSize = 1; // skip illegible bytes
+ }
+ }
+ }
+ }
+ // The TripleName's need to be reset if we are called again for a different
+ // architecture.
+ TripleName = "";
+ ThumbTripleName = "";
+
+ if (SymbolizerInfo.demangled_name != nullptr)
+ free(SymbolizerInfo.demangled_name);
+ if (ThumbSymbolizerInfo.demangled_name != nullptr)
+ free(ThumbSymbolizerInfo.demangled_name);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// __compact_unwind section dumping
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+template <typename T>
+static uint64_t read(StringRef Contents, ptrdiff_t Offset) {
+ using llvm::support::little;
+ using llvm::support::unaligned;
+
+ if (Offset + sizeof(T) > Contents.size()) {
+ outs() << "warning: attempt to read past end of buffer\n";
+ return T();
+ }
+
+ uint64_t Val =
+ support::endian::read<T, little, unaligned>(Contents.data() + Offset);
+ return Val;
+}
+
+template <typename T>
+static uint64_t readNext(StringRef Contents, ptrdiff_t &Offset) {
+ T Val = read<T>(Contents, Offset);
+ Offset += sizeof(T);
+ return Val;
+}
+
+struct CompactUnwindEntry {
+ uint32_t OffsetInSection;
+
+ uint64_t FunctionAddr;
+ uint32_t Length;
+ uint32_t CompactEncoding;
+ uint64_t PersonalityAddr;
+ uint64_t LSDAAddr;
+
+ RelocationRef FunctionReloc;
+ RelocationRef PersonalityReloc;
+ RelocationRef LSDAReloc;
+
+ CompactUnwindEntry(StringRef Contents, unsigned Offset, bool Is64)
+ : OffsetInSection(Offset) {
+ if (Is64)
+ read<uint64_t>(Contents, Offset);
+ else
+ read<uint32_t>(Contents, Offset);
+ }
+
+private:
+ template <typename UIntPtr> void read(StringRef Contents, ptrdiff_t Offset) {
+ FunctionAddr = readNext<UIntPtr>(Contents, Offset);
+ Length = readNext<uint32_t>(Contents, Offset);
+ CompactEncoding = readNext<uint32_t>(Contents, Offset);
+ PersonalityAddr = readNext<UIntPtr>(Contents, Offset);
+ LSDAAddr = readNext<UIntPtr>(Contents, Offset);
+ }
+};
+}
+
+/// Given a relocation from __compact_unwind, consisting of the RelocationRef
+/// and data being relocated, determine the best base Name and Addend to use for
+/// display purposes.
+///
+/// 1. An Extern relocation will directly reference a symbol (and the data is
+/// then already an addend), so use that.
+/// 2. Otherwise the data is an offset in the object file's layout; try to find
+// a symbol before it in the same section, and use the offset from there.
+/// 3. Finally, if all that fails, fall back to an offset from the start of the
+/// referenced section.
+static void findUnwindRelocNameAddend(const MachOObjectFile *Obj,
+ std::map<uint64_t, SymbolRef> &Symbols,
+ const RelocationRef &Reloc, uint64_t Addr,
+ StringRef &Name, uint64_t &Addend) {
+ if (Reloc.getSymbol() != Obj->symbol_end()) {
+ Name = unwrapOrError(Reloc.getSymbol()->getName(), Obj->getFileName());
+ Addend = Addr;
+ return;
+ }
+
+ auto RE = Obj->getRelocation(Reloc.getRawDataRefImpl());
+ SectionRef RelocSection = Obj->getAnyRelocationSection(RE);
+
+ uint64_t SectionAddr = RelocSection.getAddress();
+
+ auto Sym = Symbols.upper_bound(Addr);
+ if (Sym == Symbols.begin()) {
+ // The first symbol in the object is after this reference, the best we can
+ // do is section-relative notation.
+ if (Expected<StringRef> NameOrErr = RelocSection.getName())
+ Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ Addend = Addr - SectionAddr;
+ return;
+ }
+
+ // Go back one so that SymbolAddress <= Addr.
+ --Sym;
+
+ section_iterator SymSection =
+ unwrapOrError(Sym->second.getSection(), Obj->getFileName());
+ if (RelocSection == *SymSection) {
+ // There's a valid symbol in the same section before this reference.
+ Name = unwrapOrError(Sym->second.getName(), Obj->getFileName());
+ Addend = Addr - Sym->first;
+ return;
+ }
+
+ // There is a symbol before this reference, but it's in a different
+ // section. Probably not helpful to mention it, so use the section name.
+ if (Expected<StringRef> NameOrErr = RelocSection.getName())
+ Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ Addend = Addr - SectionAddr;
+}
+
+static void printUnwindRelocDest(const MachOObjectFile *Obj,
+ std::map<uint64_t, SymbolRef> &Symbols,
+ const RelocationRef &Reloc, uint64_t Addr) {
+ StringRef Name;
+ uint64_t Addend;
+
+ if (!Reloc.getObject())
+ return;
+
+ findUnwindRelocNameAddend(Obj, Symbols, Reloc, Addr, Name, Addend);
+
+ outs() << Name;
+ if (Addend)
+ outs() << " + " << format("0x%" PRIx64, Addend);
+}
+
+static void
+printMachOCompactUnwindSection(const MachOObjectFile *Obj,
+ std::map<uint64_t, SymbolRef> &Symbols,
+ const SectionRef &CompactUnwind) {
+
+ if (!Obj->isLittleEndian()) {
+ outs() << "Skipping big-endian __compact_unwind section\n";
+ return;
+ }
+
+ bool Is64 = Obj->is64Bit();
+ uint32_t PointerSize = Is64 ? sizeof(uint64_t) : sizeof(uint32_t);
+ uint32_t EntrySize = 3 * PointerSize + 2 * sizeof(uint32_t);
+
+ StringRef Contents =
+ unwrapOrError(CompactUnwind.getContents(), Obj->getFileName());
+ SmallVector<CompactUnwindEntry, 4> CompactUnwinds;
+
+ // First populate the initial raw offsets, encodings and so on from the entry.
+ for (unsigned Offset = 0; Offset < Contents.size(); Offset += EntrySize) {
+ CompactUnwindEntry Entry(Contents, Offset, Is64);
+ CompactUnwinds.push_back(Entry);
+ }
+
+ // Next we need to look at the relocations to find out what objects are
+ // actually being referred to.
+ for (const RelocationRef &Reloc : CompactUnwind.relocations()) {
+ uint64_t RelocAddress = Reloc.getOffset();
+
+ uint32_t EntryIdx = RelocAddress / EntrySize;
+ uint32_t OffsetInEntry = RelocAddress - EntryIdx * EntrySize;
+ CompactUnwindEntry &Entry = CompactUnwinds[EntryIdx];
+
+ if (OffsetInEntry == 0)
+ Entry.FunctionReloc = Reloc;
+ else if (OffsetInEntry == PointerSize + 2 * sizeof(uint32_t))
+ Entry.PersonalityReloc = Reloc;
+ else if (OffsetInEntry == 2 * PointerSize + 2 * sizeof(uint32_t))
+ Entry.LSDAReloc = Reloc;
+ else {
+ outs() << "Invalid relocation in __compact_unwind section\n";
+ return;
+ }
+ }
+
+ // Finally, we're ready to print the data we've gathered.
+ outs() << "Contents of __compact_unwind section:\n";
+ for (auto &Entry : CompactUnwinds) {
+ outs() << " Entry at offset "
+ << format("0x%" PRIx32, Entry.OffsetInSection) << ":\n";
+
+ // 1. Start of the region this entry applies to.
+ outs() << " start: " << format("0x%" PRIx64,
+ Entry.FunctionAddr) << ' ';
+ printUnwindRelocDest(Obj, Symbols, Entry.FunctionReloc, Entry.FunctionAddr);
+ outs() << '\n';
+
+ // 2. Length of the region this entry applies to.
+ outs() << " length: " << format("0x%" PRIx32, Entry.Length)
+ << '\n';
+ // 3. The 32-bit compact encoding.
+ outs() << " compact encoding: "
+ << format("0x%08" PRIx32, Entry.CompactEncoding) << '\n';
+
+ // 4. The personality function, if present.
+ if (Entry.PersonalityReloc.getObject()) {
+ outs() << " personality function: "
+ << format("0x%" PRIx64, Entry.PersonalityAddr) << ' ';
+ printUnwindRelocDest(Obj, Symbols, Entry.PersonalityReloc,
+ Entry.PersonalityAddr);
+ outs() << '\n';
+ }
+
+ // 5. This entry's language-specific data area.
+ if (Entry.LSDAReloc.getObject()) {
+ outs() << " LSDA: " << format("0x%" PRIx64,
+ Entry.LSDAAddr) << ' ';
+ printUnwindRelocDest(Obj, Symbols, Entry.LSDAReloc, Entry.LSDAAddr);
+ outs() << '\n';
+ }
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// __unwind_info section dumping
+//===----------------------------------------------------------------------===//
+
+static void printRegularSecondLevelUnwindPage(StringRef PageData) {
+ ptrdiff_t Pos = 0;
+ uint32_t Kind = readNext<uint32_t>(PageData, Pos);
+ (void)Kind;
+ assert(Kind == 2 && "kind for a regular 2nd level index should be 2");
+
+ uint16_t EntriesStart = readNext<uint16_t>(PageData, Pos);
+ uint16_t NumEntries = readNext<uint16_t>(PageData, Pos);
+
+ Pos = EntriesStart;
+ for (unsigned i = 0; i < NumEntries; ++i) {
+ uint32_t FunctionOffset = readNext<uint32_t>(PageData, Pos);
+ uint32_t Encoding = readNext<uint32_t>(PageData, Pos);
+
+ outs() << " [" << i << "]: "
+ << "function offset=" << format("0x%08" PRIx32, FunctionOffset)
+ << ", "
+ << "encoding=" << format("0x%08" PRIx32, Encoding) << '\n';
+ }
+}
+
+static void printCompressedSecondLevelUnwindPage(
+ StringRef PageData, uint32_t FunctionBase,
+ const SmallVectorImpl<uint32_t> &CommonEncodings) {
+ ptrdiff_t Pos = 0;
+ uint32_t Kind = readNext<uint32_t>(PageData, Pos);
+ (void)Kind;
+ assert(Kind == 3 && "kind for a compressed 2nd level index should be 3");
+
+ uint32_t NumCommonEncodings = CommonEncodings.size();
+ uint16_t EntriesStart = readNext<uint16_t>(PageData, Pos);
+ uint16_t NumEntries = readNext<uint16_t>(PageData, Pos);
+
+ uint16_t PageEncodingsStart = readNext<uint16_t>(PageData, Pos);
+ uint16_t NumPageEncodings = readNext<uint16_t>(PageData, Pos);
+ SmallVector<uint32_t, 64> PageEncodings;
+ if (NumPageEncodings) {
+ outs() << " Page encodings: (count = " << NumPageEncodings << ")\n";
+ Pos = PageEncodingsStart;
+ for (unsigned i = 0; i < NumPageEncodings; ++i) {
+ uint32_t Encoding = readNext<uint32_t>(PageData, Pos);
+ PageEncodings.push_back(Encoding);
+ outs() << " encoding[" << (i + NumCommonEncodings)
+ << "]: " << format("0x%08" PRIx32, Encoding) << '\n';
+ }
+ }
+
+ Pos = EntriesStart;
+ for (unsigned i = 0; i < NumEntries; ++i) {
+ uint32_t Entry = readNext<uint32_t>(PageData, Pos);
+ uint32_t FunctionOffset = FunctionBase + (Entry & 0xffffff);
+ uint32_t EncodingIdx = Entry >> 24;
+
+ uint32_t Encoding;
+ if (EncodingIdx < NumCommonEncodings)
+ Encoding = CommonEncodings[EncodingIdx];
+ else
+ Encoding = PageEncodings[EncodingIdx - NumCommonEncodings];
+
+ outs() << " [" << i << "]: "
+ << "function offset=" << format("0x%08" PRIx32, FunctionOffset)
+ << ", "
+ << "encoding[" << EncodingIdx
+ << "]=" << format("0x%08" PRIx32, Encoding) << '\n';
+ }
+}
+
+static void printMachOUnwindInfoSection(const MachOObjectFile *Obj,
+ std::map<uint64_t, SymbolRef> &Symbols,
+ const SectionRef &UnwindInfo) {
+
+ if (!Obj->isLittleEndian()) {
+ outs() << "Skipping big-endian __unwind_info section\n";
+ return;
+ }
+
+ outs() << "Contents of __unwind_info section:\n";
+
+ StringRef Contents =
+ unwrapOrError(UnwindInfo.getContents(), Obj->getFileName());
+ ptrdiff_t Pos = 0;
+
+ //===----------------------------------
+ // Section header
+ //===----------------------------------
+
+ uint32_t Version = readNext<uint32_t>(Contents, Pos);
+ outs() << " Version: "
+ << format("0x%" PRIx32, Version) << '\n';
+ if (Version != 1) {
+ outs() << " Skipping section with unknown version\n";
+ return;
+ }
+
+ uint32_t CommonEncodingsStart = readNext<uint32_t>(Contents, Pos);
+ outs() << " Common encodings array section offset: "
+ << format("0x%" PRIx32, CommonEncodingsStart) << '\n';
+ uint32_t NumCommonEncodings = readNext<uint32_t>(Contents, Pos);
+ outs() << " Number of common encodings in array: "
+ << format("0x%" PRIx32, NumCommonEncodings) << '\n';
+
+ uint32_t PersonalitiesStart = readNext<uint32_t>(Contents, Pos);
+ outs() << " Personality function array section offset: "
+ << format("0x%" PRIx32, PersonalitiesStart) << '\n';
+ uint32_t NumPersonalities = readNext<uint32_t>(Contents, Pos);
+ outs() << " Number of personality functions in array: "
+ << format("0x%" PRIx32, NumPersonalities) << '\n';
+
+ uint32_t IndicesStart = readNext<uint32_t>(Contents, Pos);
+ outs() << " Index array section offset: "
+ << format("0x%" PRIx32, IndicesStart) << '\n';
+ uint32_t NumIndices = readNext<uint32_t>(Contents, Pos);
+ outs() << " Number of indices in array: "
+ << format("0x%" PRIx32, NumIndices) << '\n';
+
+ //===----------------------------------
+ // A shared list of common encodings
+ //===----------------------------------
+
+ // These occupy indices in the range [0, N] whenever an encoding is referenced
+ // from a compressed 2nd level index table. In practice the linker only
+ // creates ~128 of these, so that indices are available to embed encodings in
+ // the 2nd level index.
+
+ SmallVector<uint32_t, 64> CommonEncodings;
+ outs() << " Common encodings: (count = " << NumCommonEncodings << ")\n";
+ Pos = CommonEncodingsStart;
+ for (unsigned i = 0; i < NumCommonEncodings; ++i) {
+ uint32_t Encoding = readNext<uint32_t>(Contents, Pos);
+ CommonEncodings.push_back(Encoding);
+
+ outs() << " encoding[" << i << "]: " << format("0x%08" PRIx32, Encoding)
+ << '\n';
+ }
+
+ //===----------------------------------
+ // Personality functions used in this executable
+ //===----------------------------------
+
+ // There should be only a handful of these (one per source language,
+ // roughly). Particularly since they only get 2 bits in the compact encoding.
+
+ outs() << " Personality functions: (count = " << NumPersonalities << ")\n";
+ Pos = PersonalitiesStart;
+ for (unsigned i = 0; i < NumPersonalities; ++i) {
+ uint32_t PersonalityFn = readNext<uint32_t>(Contents, Pos);
+ outs() << " personality[" << i + 1
+ << "]: " << format("0x%08" PRIx32, PersonalityFn) << '\n';
+ }
+
+ //===----------------------------------
+ // The level 1 index entries
+ //===----------------------------------
+
+ // These specify an approximate place to start searching for the more detailed
+ // information, sorted by PC.
+
+ struct IndexEntry {
+ uint32_t FunctionOffset;
+ uint32_t SecondLevelPageStart;
+ uint32_t LSDAStart;
+ };
+
+ SmallVector<IndexEntry, 4> IndexEntries;
+
+ outs() << " Top level indices: (count = " << NumIndices << ")\n";
+ Pos = IndicesStart;
+ for (unsigned i = 0; i < NumIndices; ++i) {
+ IndexEntry Entry;
+
+ Entry.FunctionOffset = readNext<uint32_t>(Contents, Pos);
+ Entry.SecondLevelPageStart = readNext<uint32_t>(Contents, Pos);
+ Entry.LSDAStart = readNext<uint32_t>(Contents, Pos);
+ IndexEntries.push_back(Entry);
+
+ outs() << " [" << i << "]: "
+ << "function offset=" << format("0x%08" PRIx32, Entry.FunctionOffset)
+ << ", "
+ << "2nd level page offset="
+ << format("0x%08" PRIx32, Entry.SecondLevelPageStart) << ", "
+ << "LSDA offset=" << format("0x%08" PRIx32, Entry.LSDAStart) << '\n';
+ }
+
+ //===----------------------------------
+ // Next come the LSDA tables
+ //===----------------------------------
+
+ // The LSDA layout is rather implicit: it's a contiguous array of entries from
+ // the first top-level index's LSDAOffset to the last (sentinel).
+
+ outs() << " LSDA descriptors:\n";
+ Pos = IndexEntries[0].LSDAStart;
+ const uint32_t LSDASize = 2 * sizeof(uint32_t);
+ int NumLSDAs =
+ (IndexEntries.back().LSDAStart - IndexEntries[0].LSDAStart) / LSDASize;
+
+ for (int i = 0; i < NumLSDAs; ++i) {
+ uint32_t FunctionOffset = readNext<uint32_t>(Contents, Pos);
+ uint32_t LSDAOffset = readNext<uint32_t>(Contents, Pos);
+ outs() << " [" << i << "]: "
+ << "function offset=" << format("0x%08" PRIx32, FunctionOffset)
+ << ", "
+ << "LSDA offset=" << format("0x%08" PRIx32, LSDAOffset) << '\n';
+ }
+
+ //===----------------------------------
+ // Finally, the 2nd level indices
+ //===----------------------------------
+
+ // Generally these are 4K in size, and have 2 possible forms:
+ // + Regular stores up to 511 entries with disparate encodings
+ // + Compressed stores up to 1021 entries if few enough compact encoding
+ // values are used.
+ outs() << " Second level indices:\n";
+ for (unsigned i = 0; i < IndexEntries.size() - 1; ++i) {
+ // The final sentinel top-level index has no associated 2nd level page
+ if (IndexEntries[i].SecondLevelPageStart == 0)
+ break;
+
+ outs() << " Second level index[" << i << "]: "
+ << "offset in section="
+ << format("0x%08" PRIx32, IndexEntries[i].SecondLevelPageStart)
+ << ", "
+ << "base function offset="
+ << format("0x%08" PRIx32, IndexEntries[i].FunctionOffset) << '\n';
+
+ Pos = IndexEntries[i].SecondLevelPageStart;
+ if (Pos + sizeof(uint32_t) > Contents.size()) {
+ outs() << "warning: invalid offset for second level page: " << Pos << '\n';
+ continue;
+ }
+
+ uint32_t Kind =
+ *reinterpret_cast<const support::ulittle32_t *>(Contents.data() + Pos);
+ if (Kind == 2)
+ printRegularSecondLevelUnwindPage(Contents.substr(Pos, 4096));
+ else if (Kind == 3)
+ printCompressedSecondLevelUnwindPage(Contents.substr(Pos, 4096),
+ IndexEntries[i].FunctionOffset,
+ CommonEncodings);
+ else
+ outs() << " Skipping 2nd level page with unknown kind " << Kind
+ << '\n';
+ }
+}
+
+void objdump::printMachOUnwindInfo(const MachOObjectFile *Obj) {
+ std::map<uint64_t, SymbolRef> Symbols;
+ for (const SymbolRef &SymRef : Obj->symbols()) {
+ // Discard any undefined or absolute symbols. They're not going to take part
+ // in the convenience lookup for unwind info and just take up resources.
+ auto SectOrErr = SymRef.getSection();
+ if (!SectOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(SectOrErr.takeError());
+ continue;
+ }
+ section_iterator Section = *SectOrErr;
+ if (Section == Obj->section_end())
+ continue;
+
+ uint64_t Addr = cantFail(SymRef.getValue());
+ Symbols.insert(std::make_pair(Addr, SymRef));
+ }
+
+ for (const SectionRef &Section : Obj->sections()) {
+ StringRef SectName;
+ if (Expected<StringRef> NameOrErr = Section.getName())
+ SectName = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ if (SectName == "__compact_unwind")
+ printMachOCompactUnwindSection(Obj, Symbols, Section);
+ else if (SectName == "__unwind_info")
+ printMachOUnwindInfoSection(Obj, Symbols, Section);
+ }
+}
+
+static void PrintMachHeader(uint32_t magic, uint32_t cputype,
+ uint32_t cpusubtype, uint32_t filetype,
+ uint32_t ncmds, uint32_t sizeofcmds, uint32_t flags,
+ bool verbose) {
+ outs() << "Mach header\n";
+ outs() << " magic cputype cpusubtype caps filetype ncmds "
+ "sizeofcmds flags\n";
+ if (verbose) {
+ if (magic == MachO::MH_MAGIC)
+ outs() << " MH_MAGIC";
+ else if (magic == MachO::MH_MAGIC_64)
+ outs() << "MH_MAGIC_64";
+ else
+ outs() << format(" 0x%08" PRIx32, magic);
+ switch (cputype) {
+ case MachO::CPU_TYPE_I386:
+ outs() << " I386";
+ switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) {
+ case MachO::CPU_SUBTYPE_I386_ALL:
+ outs() << " ALL";
+ break;
+ default:
+ outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
+ break;
+ }
+ break;
+ case MachO::CPU_TYPE_X86_64:
+ outs() << " X86_64";
+ switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) {
+ case MachO::CPU_SUBTYPE_X86_64_ALL:
+ outs() << " ALL";
+ break;
+ case MachO::CPU_SUBTYPE_X86_64_H:
+ outs() << " Haswell";
+ break;
+ default:
+ outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
+ break;
+ }
+ break;
+ case MachO::CPU_TYPE_ARM:
+ outs() << " ARM";
+ switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) {
+ case MachO::CPU_SUBTYPE_ARM_ALL:
+ outs() << " ALL";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V4T:
+ outs() << " V4T";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V5TEJ:
+ outs() << " V5TEJ";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_XSCALE:
+ outs() << " XSCALE";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V6:
+ outs() << " V6";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V6M:
+ outs() << " V6M";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V7:
+ outs() << " V7";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V7EM:
+ outs() << " V7EM";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V7K:
+ outs() << " V7K";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V7M:
+ outs() << " V7M";
+ break;
+ case MachO::CPU_SUBTYPE_ARM_V7S:
+ outs() << " V7S";
+ break;
+ default:
+ outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
+ break;
+ }
+ break;
+ case MachO::CPU_TYPE_ARM64:
+ outs() << " ARM64";
+ switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) {
+ case MachO::CPU_SUBTYPE_ARM64_ALL:
+ outs() << " ALL";
+ break;
+ case MachO::CPU_SUBTYPE_ARM64_V8:
+ outs() << " V8";
+ break;
+ case MachO::CPU_SUBTYPE_ARM64E:
+ outs() << " E";
+ break;
+ default:
+ outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
+ break;
+ }
+ break;
+ case MachO::CPU_TYPE_ARM64_32:
+ outs() << " ARM64_32";
+ switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) {
+ case MachO::CPU_SUBTYPE_ARM64_32_V8:
+ outs() << " V8";
+ break;
+ default:
+ outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
+ break;
+ }
+ break;
+ case MachO::CPU_TYPE_POWERPC:
+ outs() << " PPC";
+ switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) {
+ case MachO::CPU_SUBTYPE_POWERPC_ALL:
+ outs() << " ALL";
+ break;
+ default:
+ outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
+ break;
+ }
+ break;
+ case MachO::CPU_TYPE_POWERPC64:
+ outs() << " PPC64";
+ switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) {
+ case MachO::CPU_SUBTYPE_POWERPC_ALL:
+ outs() << " ALL";
+ break;
+ default:
+ outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
+ break;
+ }
+ break;
+ default:
+ outs() << format(" %7d", cputype);
+ outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
+ break;
+ }
+ if ((cpusubtype & MachO::CPU_SUBTYPE_MASK) == MachO::CPU_SUBTYPE_LIB64) {
+ outs() << " LIB64";
+ } else {
+ outs() << format(" 0x%02" PRIx32,
+ (cpusubtype & MachO::CPU_SUBTYPE_MASK) >> 24);
+ }
+ switch (filetype) {
+ case MachO::MH_OBJECT:
+ outs() << " OBJECT";
+ break;
+ case MachO::MH_EXECUTE:
+ outs() << " EXECUTE";
+ break;
+ case MachO::MH_FVMLIB:
+ outs() << " FVMLIB";
+ break;
+ case MachO::MH_CORE:
+ outs() << " CORE";
+ break;
+ case MachO::MH_PRELOAD:
+ outs() << " PRELOAD";
+ break;
+ case MachO::MH_DYLIB:
+ outs() << " DYLIB";
+ break;
+ case MachO::MH_DYLIB_STUB:
+ outs() << " DYLIB_STUB";
+ break;
+ case MachO::MH_DYLINKER:
+ outs() << " DYLINKER";
+ break;
+ case MachO::MH_BUNDLE:
+ outs() << " BUNDLE";
+ break;
+ case MachO::MH_DSYM:
+ outs() << " DSYM";
+ break;
+ case MachO::MH_KEXT_BUNDLE:
+ outs() << " KEXTBUNDLE";
+ break;
+ default:
+ outs() << format(" %10u", filetype);
+ break;
+ }
+ outs() << format(" %5u", ncmds);
+ outs() << format(" %10u", sizeofcmds);
+ uint32_t f = flags;
+ if (f & MachO::MH_NOUNDEFS) {
+ outs() << " NOUNDEFS";
+ f &= ~MachO::MH_NOUNDEFS;
+ }
+ if (f & MachO::MH_INCRLINK) {
+ outs() << " INCRLINK";
+ f &= ~MachO::MH_INCRLINK;
+ }
+ if (f & MachO::MH_DYLDLINK) {
+ outs() << " DYLDLINK";
+ f &= ~MachO::MH_DYLDLINK;
+ }
+ if (f & MachO::MH_BINDATLOAD) {
+ outs() << " BINDATLOAD";
+ f &= ~MachO::MH_BINDATLOAD;
+ }
+ if (f & MachO::MH_PREBOUND) {
+ outs() << " PREBOUND";
+ f &= ~MachO::MH_PREBOUND;
+ }
+ if (f & MachO::MH_SPLIT_SEGS) {
+ outs() << " SPLIT_SEGS";
+ f &= ~MachO::MH_SPLIT_SEGS;
+ }
+ if (f & MachO::MH_LAZY_INIT) {
+ outs() << " LAZY_INIT";
+ f &= ~MachO::MH_LAZY_INIT;
+ }
+ if (f & MachO::MH_TWOLEVEL) {
+ outs() << " TWOLEVEL";
+ f &= ~MachO::MH_TWOLEVEL;
+ }
+ if (f & MachO::MH_FORCE_FLAT) {
+ outs() << " FORCE_FLAT";
+ f &= ~MachO::MH_FORCE_FLAT;
+ }
+ if (f & MachO::MH_NOMULTIDEFS) {
+ outs() << " NOMULTIDEFS";
+ f &= ~MachO::MH_NOMULTIDEFS;
+ }
+ if (f & MachO::MH_NOFIXPREBINDING) {
+ outs() << " NOFIXPREBINDING";
+ f &= ~MachO::MH_NOFIXPREBINDING;
+ }
+ if (f & MachO::MH_PREBINDABLE) {
+ outs() << " PREBINDABLE";
+ f &= ~MachO::MH_PREBINDABLE;
+ }
+ if (f & MachO::MH_ALLMODSBOUND) {
+ outs() << " ALLMODSBOUND";
+ f &= ~MachO::MH_ALLMODSBOUND;
+ }
+ if (f & MachO::MH_SUBSECTIONS_VIA_SYMBOLS) {
+ outs() << " SUBSECTIONS_VIA_SYMBOLS";
+ f &= ~MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
+ }
+ if (f & MachO::MH_CANONICAL) {
+ outs() << " CANONICAL";
+ f &= ~MachO::MH_CANONICAL;
+ }
+ if (f & MachO::MH_WEAK_DEFINES) {
+ outs() << " WEAK_DEFINES";
+ f &= ~MachO::MH_WEAK_DEFINES;
+ }
+ if (f & MachO::MH_BINDS_TO_WEAK) {
+ outs() << " BINDS_TO_WEAK";
+ f &= ~MachO::MH_BINDS_TO_WEAK;
+ }
+ if (f & MachO::MH_ALLOW_STACK_EXECUTION) {
+ outs() << " ALLOW_STACK_EXECUTION";
+ f &= ~MachO::MH_ALLOW_STACK_EXECUTION;
+ }
+ if (f & MachO::MH_DEAD_STRIPPABLE_DYLIB) {
+ outs() << " DEAD_STRIPPABLE_DYLIB";
+ f &= ~MachO::MH_DEAD_STRIPPABLE_DYLIB;
+ }
+ if (f & MachO::MH_PIE) {
+ outs() << " PIE";
+ f &= ~MachO::MH_PIE;
+ }
+ if (f & MachO::MH_NO_REEXPORTED_DYLIBS) {
+ outs() << " NO_REEXPORTED_DYLIBS";
+ f &= ~MachO::MH_NO_REEXPORTED_DYLIBS;
+ }
+ if (f & MachO::MH_HAS_TLV_DESCRIPTORS) {
+ outs() << " MH_HAS_TLV_DESCRIPTORS";
+ f &= ~MachO::MH_HAS_TLV_DESCRIPTORS;
+ }
+ if (f & MachO::MH_NO_HEAP_EXECUTION) {
+ outs() << " MH_NO_HEAP_EXECUTION";
+ f &= ~MachO::MH_NO_HEAP_EXECUTION;
+ }
+ if (f & MachO::MH_APP_EXTENSION_SAFE) {
+ outs() << " APP_EXTENSION_SAFE";
+ f &= ~MachO::MH_APP_EXTENSION_SAFE;
+ }
+ if (f & MachO::MH_NLIST_OUTOFSYNC_WITH_DYLDINFO) {
+ outs() << " NLIST_OUTOFSYNC_WITH_DYLDINFO";
+ f &= ~MachO::MH_NLIST_OUTOFSYNC_WITH_DYLDINFO;
+ }
+ if (f != 0 || flags == 0)
+ outs() << format(" 0x%08" PRIx32, f);
+ } else {
+ outs() << format(" 0x%08" PRIx32, magic);
+ outs() << format(" %7d", cputype);
+ outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK);
+ outs() << format(" 0x%02" PRIx32,
+ (cpusubtype & MachO::CPU_SUBTYPE_MASK) >> 24);
+ outs() << format(" %10u", filetype);
+ outs() << format(" %5u", ncmds);
+ outs() << format(" %10u", sizeofcmds);
+ outs() << format(" 0x%08" PRIx32, flags);
+ }
+ outs() << "\n";
+}
+
+static void PrintSegmentCommand(uint32_t cmd, uint32_t cmdsize,
+ StringRef SegName, uint64_t vmaddr,
+ uint64_t vmsize, uint64_t fileoff,
+ uint64_t filesize, uint32_t maxprot,
+ uint32_t initprot, uint32_t nsects,
+ uint32_t flags, uint32_t object_size,
+ bool verbose) {
+ uint64_t expected_cmdsize;
+ if (cmd == MachO::LC_SEGMENT) {
+ outs() << " cmd LC_SEGMENT\n";
+ expected_cmdsize = nsects;
+ expected_cmdsize *= sizeof(struct MachO::section);
+ expected_cmdsize += sizeof(struct MachO::segment_command);
+ } else {
+ outs() << " cmd LC_SEGMENT_64\n";
+ expected_cmdsize = nsects;
+ expected_cmdsize *= sizeof(struct MachO::section_64);
+ expected_cmdsize += sizeof(struct MachO::segment_command_64);
+ }
+ outs() << " cmdsize " << cmdsize;
+ if (cmdsize != expected_cmdsize)
+ outs() << " Inconsistent size\n";
+ else
+ outs() << "\n";
+ outs() << " segname " << SegName << "\n";
+ if (cmd == MachO::LC_SEGMENT_64) {
+ outs() << " vmaddr " << format("0x%016" PRIx64, vmaddr) << "\n";
+ outs() << " vmsize " << format("0x%016" PRIx64, vmsize) << "\n";
+ } else {
+ outs() << " vmaddr " << format("0x%08" PRIx64, vmaddr) << "\n";
+ outs() << " vmsize " << format("0x%08" PRIx64, vmsize) << "\n";
+ }
+ outs() << " fileoff " << fileoff;
+ if (fileoff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " filesize " << filesize;
+ if (fileoff + filesize > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ if (verbose) {
+ if ((maxprot &
+ ~(MachO::VM_PROT_READ | MachO::VM_PROT_WRITE |
+ MachO::VM_PROT_EXECUTE)) != 0)
+ outs() << " maxprot ?" << format("0x%08" PRIx32, maxprot) << "\n";
+ else {
+ outs() << " maxprot ";
+ outs() << ((maxprot & MachO::VM_PROT_READ) ? "r" : "-");
+ outs() << ((maxprot & MachO::VM_PROT_WRITE) ? "w" : "-");
+ outs() << ((maxprot & MachO::VM_PROT_EXECUTE) ? "x\n" : "-\n");
+ }
+ if ((initprot &
+ ~(MachO::VM_PROT_READ | MachO::VM_PROT_WRITE |
+ MachO::VM_PROT_EXECUTE)) != 0)
+ outs() << " initprot ?" << format("0x%08" PRIx32, initprot) << "\n";
+ else {
+ outs() << " initprot ";
+ outs() << ((initprot & MachO::VM_PROT_READ) ? "r" : "-");
+ outs() << ((initprot & MachO::VM_PROT_WRITE) ? "w" : "-");
+ outs() << ((initprot & MachO::VM_PROT_EXECUTE) ? "x\n" : "-\n");
+ }
+ } else {
+ outs() << " maxprot " << format("0x%08" PRIx32, maxprot) << "\n";
+ outs() << " initprot " << format("0x%08" PRIx32, initprot) << "\n";
+ }
+ outs() << " nsects " << nsects << "\n";
+ if (verbose) {
+ outs() << " flags";
+ if (flags == 0)
+ outs() << " (none)\n";
+ else {
+ if (flags & MachO::SG_HIGHVM) {
+ outs() << " HIGHVM";
+ flags &= ~MachO::SG_HIGHVM;
+ }
+ if (flags & MachO::SG_FVMLIB) {
+ outs() << " FVMLIB";
+ flags &= ~MachO::SG_FVMLIB;
+ }
+ if (flags & MachO::SG_NORELOC) {
+ outs() << " NORELOC";
+ flags &= ~MachO::SG_NORELOC;
+ }
+ if (flags & MachO::SG_PROTECTED_VERSION_1) {
+ outs() << " PROTECTED_VERSION_1";
+ flags &= ~MachO::SG_PROTECTED_VERSION_1;
+ }
+ if (flags)
+ outs() << format(" 0x%08" PRIx32, flags) << " (unknown flags)\n";
+ else
+ outs() << "\n";
+ }
+ } else {
+ outs() << " flags " << format("0x%" PRIx32, flags) << "\n";
+ }
+}
+
+static void PrintSection(const char *sectname, const char *segname,
+ uint64_t addr, uint64_t size, uint32_t offset,
+ uint32_t align, uint32_t reloff, uint32_t nreloc,
+ uint32_t flags, uint32_t reserved1, uint32_t reserved2,
+ uint32_t cmd, const char *sg_segname,
+ uint32_t filetype, uint32_t object_size,
+ bool verbose) {
+ outs() << "Section\n";
+ outs() << " sectname " << format("%.16s\n", sectname);
+ outs() << " segname " << format("%.16s", segname);
+ if (filetype != MachO::MH_OBJECT && strncmp(sg_segname, segname, 16) != 0)
+ outs() << " (does not match segment)\n";
+ else
+ outs() << "\n";
+ if (cmd == MachO::LC_SEGMENT_64) {
+ outs() << " addr " << format("0x%016" PRIx64, addr) << "\n";
+ outs() << " size " << format("0x%016" PRIx64, size);
+ } else {
+ outs() << " addr " << format("0x%08" PRIx64, addr) << "\n";
+ outs() << " size " << format("0x%08" PRIx64, size);
+ }
+ if ((flags & MachO::S_ZEROFILL) != 0 && offset + size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " offset " << offset;
+ if (offset > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ uint32_t align_shifted = 1 << align;
+ outs() << " align 2^" << align << " (" << align_shifted << ")\n";
+ outs() << " reloff " << reloff;
+ if (reloff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " nreloc " << nreloc;
+ if (reloff + nreloc * sizeof(struct MachO::relocation_info) > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ uint32_t section_type = flags & MachO::SECTION_TYPE;
+ if (verbose) {
+ outs() << " type";
+ if (section_type == MachO::S_REGULAR)
+ outs() << " S_REGULAR\n";
+ else if (section_type == MachO::S_ZEROFILL)
+ outs() << " S_ZEROFILL\n";
+ else if (section_type == MachO::S_CSTRING_LITERALS)
+ outs() << " S_CSTRING_LITERALS\n";
+ else if (section_type == MachO::S_4BYTE_LITERALS)
+ outs() << " S_4BYTE_LITERALS\n";
+ else if (section_type == MachO::S_8BYTE_LITERALS)
+ outs() << " S_8BYTE_LITERALS\n";
+ else if (section_type == MachO::S_16BYTE_LITERALS)
+ outs() << " S_16BYTE_LITERALS\n";
+ else if (section_type == MachO::S_LITERAL_POINTERS)
+ outs() << " S_LITERAL_POINTERS\n";
+ else if (section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS)
+ outs() << " S_NON_LAZY_SYMBOL_POINTERS\n";
+ else if (section_type == MachO::S_LAZY_SYMBOL_POINTERS)
+ outs() << " S_LAZY_SYMBOL_POINTERS\n";
+ else if (section_type == MachO::S_SYMBOL_STUBS)
+ outs() << " S_SYMBOL_STUBS\n";
+ else if (section_type == MachO::S_MOD_INIT_FUNC_POINTERS)
+ outs() << " S_MOD_INIT_FUNC_POINTERS\n";
+ else if (section_type == MachO::S_MOD_TERM_FUNC_POINTERS)
+ outs() << " S_MOD_TERM_FUNC_POINTERS\n";
+ else if (section_type == MachO::S_COALESCED)
+ outs() << " S_COALESCED\n";
+ else if (section_type == MachO::S_INTERPOSING)
+ outs() << " S_INTERPOSING\n";
+ else if (section_type == MachO::S_DTRACE_DOF)
+ outs() << " S_DTRACE_DOF\n";
+ else if (section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS)
+ outs() << " S_LAZY_DYLIB_SYMBOL_POINTERS\n";
+ else if (section_type == MachO::S_THREAD_LOCAL_REGULAR)
+ outs() << " S_THREAD_LOCAL_REGULAR\n";
+ else if (section_type == MachO::S_THREAD_LOCAL_ZEROFILL)
+ outs() << " S_THREAD_LOCAL_ZEROFILL\n";
+ else if (section_type == MachO::S_THREAD_LOCAL_VARIABLES)
+ outs() << " S_THREAD_LOCAL_VARIABLES\n";
+ else if (section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS)
+ outs() << " S_THREAD_LOCAL_VARIABLE_POINTERS\n";
+ else if (section_type == MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS)
+ outs() << " S_THREAD_LOCAL_INIT_FUNCTION_POINTERS\n";
+ else
+ outs() << format("0x%08" PRIx32, section_type) << "\n";
+ outs() << "attributes";
+ uint32_t section_attributes = flags & MachO::SECTION_ATTRIBUTES;
+ if (section_attributes & MachO::S_ATTR_PURE_INSTRUCTIONS)
+ outs() << " PURE_INSTRUCTIONS";
+ if (section_attributes & MachO::S_ATTR_NO_TOC)
+ outs() << " NO_TOC";
+ if (section_attributes & MachO::S_ATTR_STRIP_STATIC_SYMS)
+ outs() << " STRIP_STATIC_SYMS";
+ if (section_attributes & MachO::S_ATTR_NO_DEAD_STRIP)
+ outs() << " NO_DEAD_STRIP";
+ if (section_attributes & MachO::S_ATTR_LIVE_SUPPORT)
+ outs() << " LIVE_SUPPORT";
+ if (section_attributes & MachO::S_ATTR_SELF_MODIFYING_CODE)
+ outs() << " SELF_MODIFYING_CODE";
+ if (section_attributes & MachO::S_ATTR_DEBUG)
+ outs() << " DEBUG";
+ if (section_attributes & MachO::S_ATTR_SOME_INSTRUCTIONS)
+ outs() << " SOME_INSTRUCTIONS";
+ if (section_attributes & MachO::S_ATTR_EXT_RELOC)
+ outs() << " EXT_RELOC";
+ if (section_attributes & MachO::S_ATTR_LOC_RELOC)
+ outs() << " LOC_RELOC";
+ if (section_attributes == 0)
+ outs() << " (none)";
+ outs() << "\n";
+ } else
+ outs() << " flags " << format("0x%08" PRIx32, flags) << "\n";
+ outs() << " reserved1 " << reserved1;
+ if (section_type == MachO::S_SYMBOL_STUBS ||
+ section_type == MachO::S_LAZY_SYMBOL_POINTERS ||
+ section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS ||
+ section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS ||
+ section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS)
+ outs() << " (index into indirect symbol table)\n";
+ else
+ outs() << "\n";
+ outs() << " reserved2 " << reserved2;
+ if (section_type == MachO::S_SYMBOL_STUBS)
+ outs() << " (size of stubs)\n";
+ else
+ outs() << "\n";
+}
+
+static void PrintSymtabLoadCommand(MachO::symtab_command st, bool Is64Bit,
+ uint32_t object_size) {
+ outs() << " cmd LC_SYMTAB\n";
+ outs() << " cmdsize " << st.cmdsize;
+ if (st.cmdsize != sizeof(struct MachO::symtab_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " symoff " << st.symoff;
+ if (st.symoff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " nsyms " << st.nsyms;
+ uint64_t big_size;
+ if (Is64Bit) {
+ big_size = st.nsyms;
+ big_size *= sizeof(struct MachO::nlist_64);
+ big_size += st.symoff;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ } else {
+ big_size = st.nsyms;
+ big_size *= sizeof(struct MachO::nlist);
+ big_size += st.symoff;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ }
+ outs() << " stroff " << st.stroff;
+ if (st.stroff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " strsize " << st.strsize;
+ big_size = st.stroff;
+ big_size += st.strsize;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+}
+
+static void PrintDysymtabLoadCommand(MachO::dysymtab_command dyst,
+ uint32_t nsyms, uint32_t object_size,
+ bool Is64Bit) {
+ outs() << " cmd LC_DYSYMTAB\n";
+ outs() << " cmdsize " << dyst.cmdsize;
+ if (dyst.cmdsize != sizeof(struct MachO::dysymtab_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " ilocalsym " << dyst.ilocalsym;
+ if (dyst.ilocalsym > nsyms)
+ outs() << " (greater than the number of symbols)\n";
+ else
+ outs() << "\n";
+ outs() << " nlocalsym " << dyst.nlocalsym;
+ uint64_t big_size;
+ big_size = dyst.ilocalsym;
+ big_size += dyst.nlocalsym;
+ if (big_size > nsyms)
+ outs() << " (past the end of the symbol table)\n";
+ else
+ outs() << "\n";
+ outs() << " iextdefsym " << dyst.iextdefsym;
+ if (dyst.iextdefsym > nsyms)
+ outs() << " (greater than the number of symbols)\n";
+ else
+ outs() << "\n";
+ outs() << " nextdefsym " << dyst.nextdefsym;
+ big_size = dyst.iextdefsym;
+ big_size += dyst.nextdefsym;
+ if (big_size > nsyms)
+ outs() << " (past the end of the symbol table)\n";
+ else
+ outs() << "\n";
+ outs() << " iundefsym " << dyst.iundefsym;
+ if (dyst.iundefsym > nsyms)
+ outs() << " (greater than the number of symbols)\n";
+ else
+ outs() << "\n";
+ outs() << " nundefsym " << dyst.nundefsym;
+ big_size = dyst.iundefsym;
+ big_size += dyst.nundefsym;
+ if (big_size > nsyms)
+ outs() << " (past the end of the symbol table)\n";
+ else
+ outs() << "\n";
+ outs() << " tocoff " << dyst.tocoff;
+ if (dyst.tocoff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " ntoc " << dyst.ntoc;
+ big_size = dyst.ntoc;
+ big_size *= sizeof(struct MachO::dylib_table_of_contents);
+ big_size += dyst.tocoff;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " modtaboff " << dyst.modtaboff;
+ if (dyst.modtaboff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " nmodtab " << dyst.nmodtab;
+ uint64_t modtabend;
+ if (Is64Bit) {
+ modtabend = dyst.nmodtab;
+ modtabend *= sizeof(struct MachO::dylib_module_64);
+ modtabend += dyst.modtaboff;
+ } else {
+ modtabend = dyst.nmodtab;
+ modtabend *= sizeof(struct MachO::dylib_module);
+ modtabend += dyst.modtaboff;
+ }
+ if (modtabend > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " extrefsymoff " << dyst.extrefsymoff;
+ if (dyst.extrefsymoff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " nextrefsyms " << dyst.nextrefsyms;
+ big_size = dyst.nextrefsyms;
+ big_size *= sizeof(struct MachO::dylib_reference);
+ big_size += dyst.extrefsymoff;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " indirectsymoff " << dyst.indirectsymoff;
+ if (dyst.indirectsymoff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " nindirectsyms " << dyst.nindirectsyms;
+ big_size = dyst.nindirectsyms;
+ big_size *= sizeof(uint32_t);
+ big_size += dyst.indirectsymoff;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " extreloff " << dyst.extreloff;
+ if (dyst.extreloff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " nextrel " << dyst.nextrel;
+ big_size = dyst.nextrel;
+ big_size *= sizeof(struct MachO::relocation_info);
+ big_size += dyst.extreloff;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " locreloff " << dyst.locreloff;
+ if (dyst.locreloff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " nlocrel " << dyst.nlocrel;
+ big_size = dyst.nlocrel;
+ big_size *= sizeof(struct MachO::relocation_info);
+ big_size += dyst.locreloff;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+}
+
+static void PrintDyldInfoLoadCommand(MachO::dyld_info_command dc,
+ uint32_t object_size) {
+ if (dc.cmd == MachO::LC_DYLD_INFO)
+ outs() << " cmd LC_DYLD_INFO\n";
+ else
+ outs() << " cmd LC_DYLD_INFO_ONLY\n";
+ outs() << " cmdsize " << dc.cmdsize;
+ if (dc.cmdsize != sizeof(struct MachO::dyld_info_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " rebase_off " << dc.rebase_off;
+ if (dc.rebase_off > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " rebase_size " << dc.rebase_size;
+ uint64_t big_size;
+ big_size = dc.rebase_off;
+ big_size += dc.rebase_size;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " bind_off " << dc.bind_off;
+ if (dc.bind_off > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " bind_size " << dc.bind_size;
+ big_size = dc.bind_off;
+ big_size += dc.bind_size;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " weak_bind_off " << dc.weak_bind_off;
+ if (dc.weak_bind_off > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " weak_bind_size " << dc.weak_bind_size;
+ big_size = dc.weak_bind_off;
+ big_size += dc.weak_bind_size;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " lazy_bind_off " << dc.lazy_bind_off;
+ if (dc.lazy_bind_off > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " lazy_bind_size " << dc.lazy_bind_size;
+ big_size = dc.lazy_bind_off;
+ big_size += dc.lazy_bind_size;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " export_off " << dc.export_off;
+ if (dc.export_off > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " export_size " << dc.export_size;
+ big_size = dc.export_off;
+ big_size += dc.export_size;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+}
+
+static void PrintDyldLoadCommand(MachO::dylinker_command dyld,
+ const char *Ptr) {
+ if (dyld.cmd == MachO::LC_ID_DYLINKER)
+ outs() << " cmd LC_ID_DYLINKER\n";
+ else if (dyld.cmd == MachO::LC_LOAD_DYLINKER)
+ outs() << " cmd LC_LOAD_DYLINKER\n";
+ else if (dyld.cmd == MachO::LC_DYLD_ENVIRONMENT)
+ outs() << " cmd LC_DYLD_ENVIRONMENT\n";
+ else
+ outs() << " cmd ?(" << dyld.cmd << ")\n";
+ outs() << " cmdsize " << dyld.cmdsize;
+ if (dyld.cmdsize < sizeof(struct MachO::dylinker_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ if (dyld.name >= dyld.cmdsize)
+ outs() << " name ?(bad offset " << dyld.name << ")\n";
+ else {
+ const char *P = (const char *)(Ptr) + dyld.name;
+ outs() << " name " << P << " (offset " << dyld.name << ")\n";
+ }
+}
+
+static void PrintUuidLoadCommand(MachO::uuid_command uuid) {
+ outs() << " cmd LC_UUID\n";
+ outs() << " cmdsize " << uuid.cmdsize;
+ if (uuid.cmdsize != sizeof(struct MachO::uuid_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " uuid ";
+ for (int i = 0; i < 16; ++i) {
+ outs() << format("%02" PRIX32, uuid.uuid[i]);
+ if (i == 3 || i == 5 || i == 7 || i == 9)
+ outs() << "-";
+ }
+ outs() << "\n";
+}
+
+static void PrintRpathLoadCommand(MachO::rpath_command rpath, const char *Ptr) {
+ outs() << " cmd LC_RPATH\n";
+ outs() << " cmdsize " << rpath.cmdsize;
+ if (rpath.cmdsize < sizeof(struct MachO::rpath_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ if (rpath.path >= rpath.cmdsize)
+ outs() << " path ?(bad offset " << rpath.path << ")\n";
+ else {
+ const char *P = (const char *)(Ptr) + rpath.path;
+ outs() << " path " << P << " (offset " << rpath.path << ")\n";
+ }
+}
+
+static void PrintVersionMinLoadCommand(MachO::version_min_command vd) {
+ StringRef LoadCmdName;
+ switch (vd.cmd) {
+ case MachO::LC_VERSION_MIN_MACOSX:
+ LoadCmdName = "LC_VERSION_MIN_MACOSX";
+ break;
+ case MachO::LC_VERSION_MIN_IPHONEOS:
+ LoadCmdName = "LC_VERSION_MIN_IPHONEOS";
+ break;
+ case MachO::LC_VERSION_MIN_TVOS:
+ LoadCmdName = "LC_VERSION_MIN_TVOS";
+ break;
+ case MachO::LC_VERSION_MIN_WATCHOS:
+ LoadCmdName = "LC_VERSION_MIN_WATCHOS";
+ break;
+ default:
+ llvm_unreachable("Unknown version min load command");
+ }
+
+ outs() << " cmd " << LoadCmdName << '\n';
+ outs() << " cmdsize " << vd.cmdsize;
+ if (vd.cmdsize != sizeof(struct MachO::version_min_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " version "
+ << MachOObjectFile::getVersionMinMajor(vd, false) << "."
+ << MachOObjectFile::getVersionMinMinor(vd, false);
+ uint32_t Update = MachOObjectFile::getVersionMinUpdate(vd, false);
+ if (Update != 0)
+ outs() << "." << Update;
+ outs() << "\n";
+ if (vd.sdk == 0)
+ outs() << " sdk n/a";
+ else {
+ outs() << " sdk "
+ << MachOObjectFile::getVersionMinMajor(vd, true) << "."
+ << MachOObjectFile::getVersionMinMinor(vd, true);
+ }
+ Update = MachOObjectFile::getVersionMinUpdate(vd, true);
+ if (Update != 0)
+ outs() << "." << Update;
+ outs() << "\n";
+}
+
+static void PrintNoteLoadCommand(MachO::note_command Nt) {
+ outs() << " cmd LC_NOTE\n";
+ outs() << " cmdsize " << Nt.cmdsize;
+ if (Nt.cmdsize != sizeof(struct MachO::note_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ const char *d = Nt.data_owner;
+ outs() << "data_owner " << format("%.16s\n", d);
+ outs() << " offset " << Nt.offset << "\n";
+ outs() << " size " << Nt.size << "\n";
+}
+
+static void PrintBuildToolVersion(MachO::build_tool_version bv) {
+ outs() << " tool " << MachOObjectFile::getBuildTool(bv.tool) << "\n";
+ outs() << " version " << MachOObjectFile::getVersionString(bv.version)
+ << "\n";
+}
+
+static void PrintBuildVersionLoadCommand(const MachOObjectFile *obj,
+ MachO::build_version_command bd) {
+ outs() << " cmd LC_BUILD_VERSION\n";
+ outs() << " cmdsize " << bd.cmdsize;
+ if (bd.cmdsize !=
+ sizeof(struct MachO::build_version_command) +
+ bd.ntools * sizeof(struct MachO::build_tool_version))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " platform " << MachOObjectFile::getBuildPlatform(bd.platform)
+ << "\n";
+ if (bd.sdk)
+ outs() << " sdk " << MachOObjectFile::getVersionString(bd.sdk)
+ << "\n";
+ else
+ outs() << " sdk n/a\n";
+ outs() << " minos " << MachOObjectFile::getVersionString(bd.minos)
+ << "\n";
+ outs() << " ntools " << bd.ntools << "\n";
+ for (unsigned i = 0; i < bd.ntools; ++i) {
+ MachO::build_tool_version bv = obj->getBuildToolVersion(i);
+ PrintBuildToolVersion(bv);
+ }
+}
+
+static void PrintSourceVersionCommand(MachO::source_version_command sd) {
+ outs() << " cmd LC_SOURCE_VERSION\n";
+ outs() << " cmdsize " << sd.cmdsize;
+ if (sd.cmdsize != sizeof(struct MachO::source_version_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ uint64_t a = (sd.version >> 40) & 0xffffff;
+ uint64_t b = (sd.version >> 30) & 0x3ff;
+ uint64_t c = (sd.version >> 20) & 0x3ff;
+ uint64_t d = (sd.version >> 10) & 0x3ff;
+ uint64_t e = sd.version & 0x3ff;
+ outs() << " version " << a << "." << b;
+ if (e != 0)
+ outs() << "." << c << "." << d << "." << e;
+ else if (d != 0)
+ outs() << "." << c << "." << d;
+ else if (c != 0)
+ outs() << "." << c;
+ outs() << "\n";
+}
+
+static void PrintEntryPointCommand(MachO::entry_point_command ep) {
+ outs() << " cmd LC_MAIN\n";
+ outs() << " cmdsize " << ep.cmdsize;
+ if (ep.cmdsize != sizeof(struct MachO::entry_point_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " entryoff " << ep.entryoff << "\n";
+ outs() << " stacksize " << ep.stacksize << "\n";
+}
+
+static void PrintEncryptionInfoCommand(MachO::encryption_info_command ec,
+ uint32_t object_size) {
+ outs() << " cmd LC_ENCRYPTION_INFO\n";
+ outs() << " cmdsize " << ec.cmdsize;
+ if (ec.cmdsize != sizeof(struct MachO::encryption_info_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " cryptoff " << ec.cryptoff;
+ if (ec.cryptoff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " cryptsize " << ec.cryptsize;
+ if (ec.cryptsize > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " cryptid " << ec.cryptid << "\n";
+}
+
+static void PrintEncryptionInfoCommand64(MachO::encryption_info_command_64 ec,
+ uint32_t object_size) {
+ outs() << " cmd LC_ENCRYPTION_INFO_64\n";
+ outs() << " cmdsize " << ec.cmdsize;
+ if (ec.cmdsize != sizeof(struct MachO::encryption_info_command_64))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " cryptoff " << ec.cryptoff;
+ if (ec.cryptoff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " cryptsize " << ec.cryptsize;
+ if (ec.cryptsize > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " cryptid " << ec.cryptid << "\n";
+ outs() << " pad " << ec.pad << "\n";
+}
+
+static void PrintLinkerOptionCommand(MachO::linker_option_command lo,
+ const char *Ptr) {
+ outs() << " cmd LC_LINKER_OPTION\n";
+ outs() << " cmdsize " << lo.cmdsize;
+ if (lo.cmdsize < sizeof(struct MachO::linker_option_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " count " << lo.count << "\n";
+ const char *string = Ptr + sizeof(struct MachO::linker_option_command);
+ uint32_t left = lo.cmdsize - sizeof(struct MachO::linker_option_command);
+ uint32_t i = 0;
+ while (left > 0) {
+ while (*string == '\0' && left > 0) {
+ string++;
+ left--;
+ }
+ if (left > 0) {
+ i++;
+ outs() << " string #" << i << " " << format("%.*s\n", left, string);
+ uint32_t NullPos = StringRef(string, left).find('\0');
+ uint32_t len = std::min(NullPos, left) + 1;
+ string += len;
+ left -= len;
+ }
+ }
+ if (lo.count != i)
+ outs() << " count " << lo.count << " does not match number of strings "
+ << i << "\n";
+}
+
+static void PrintSubFrameworkCommand(MachO::sub_framework_command sub,
+ const char *Ptr) {
+ outs() << " cmd LC_SUB_FRAMEWORK\n";
+ outs() << " cmdsize " << sub.cmdsize;
+ if (sub.cmdsize < sizeof(struct MachO::sub_framework_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ if (sub.umbrella < sub.cmdsize) {
+ const char *P = Ptr + sub.umbrella;
+ outs() << " umbrella " << P << " (offset " << sub.umbrella << ")\n";
+ } else {
+ outs() << " umbrella ?(bad offset " << sub.umbrella << ")\n";
+ }
+}
+
+static void PrintSubUmbrellaCommand(MachO::sub_umbrella_command sub,
+ const char *Ptr) {
+ outs() << " cmd LC_SUB_UMBRELLA\n";
+ outs() << " cmdsize " << sub.cmdsize;
+ if (sub.cmdsize < sizeof(struct MachO::sub_umbrella_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ if (sub.sub_umbrella < sub.cmdsize) {
+ const char *P = Ptr + sub.sub_umbrella;
+ outs() << " sub_umbrella " << P << " (offset " << sub.sub_umbrella << ")\n";
+ } else {
+ outs() << " sub_umbrella ?(bad offset " << sub.sub_umbrella << ")\n";
+ }
+}
+
+static void PrintSubLibraryCommand(MachO::sub_library_command sub,
+ const char *Ptr) {
+ outs() << " cmd LC_SUB_LIBRARY\n";
+ outs() << " cmdsize " << sub.cmdsize;
+ if (sub.cmdsize < sizeof(struct MachO::sub_library_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ if (sub.sub_library < sub.cmdsize) {
+ const char *P = Ptr + sub.sub_library;
+ outs() << " sub_library " << P << " (offset " << sub.sub_library << ")\n";
+ } else {
+ outs() << " sub_library ?(bad offset " << sub.sub_library << ")\n";
+ }
+}
+
+static void PrintSubClientCommand(MachO::sub_client_command sub,
+ const char *Ptr) {
+ outs() << " cmd LC_SUB_CLIENT\n";
+ outs() << " cmdsize " << sub.cmdsize;
+ if (sub.cmdsize < sizeof(struct MachO::sub_client_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ if (sub.client < sub.cmdsize) {
+ const char *P = Ptr + sub.client;
+ outs() << " client " << P << " (offset " << sub.client << ")\n";
+ } else {
+ outs() << " client ?(bad offset " << sub.client << ")\n";
+ }
+}
+
+static void PrintRoutinesCommand(MachO::routines_command r) {
+ outs() << " cmd LC_ROUTINES\n";
+ outs() << " cmdsize " << r.cmdsize;
+ if (r.cmdsize != sizeof(struct MachO::routines_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " init_address " << format("0x%08" PRIx32, r.init_address) << "\n";
+ outs() << " init_module " << r.init_module << "\n";
+ outs() << " reserved1 " << r.reserved1 << "\n";
+ outs() << " reserved2 " << r.reserved2 << "\n";
+ outs() << " reserved3 " << r.reserved3 << "\n";
+ outs() << " reserved4 " << r.reserved4 << "\n";
+ outs() << " reserved5 " << r.reserved5 << "\n";
+ outs() << " reserved6 " << r.reserved6 << "\n";
+}
+
+static void PrintRoutinesCommand64(MachO::routines_command_64 r) {
+ outs() << " cmd LC_ROUTINES_64\n";
+ outs() << " cmdsize " << r.cmdsize;
+ if (r.cmdsize != sizeof(struct MachO::routines_command_64))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " init_address " << format("0x%016" PRIx64, r.init_address) << "\n";
+ outs() << " init_module " << r.init_module << "\n";
+ outs() << " reserved1 " << r.reserved1 << "\n";
+ outs() << " reserved2 " << r.reserved2 << "\n";
+ outs() << " reserved3 " << r.reserved3 << "\n";
+ outs() << " reserved4 " << r.reserved4 << "\n";
+ outs() << " reserved5 " << r.reserved5 << "\n";
+ outs() << " reserved6 " << r.reserved6 << "\n";
+}
+
+static void Print_x86_thread_state32_t(MachO::x86_thread_state32_t &cpu32) {
+ outs() << "\t eax " << format("0x%08" PRIx32, cpu32.eax);
+ outs() << " ebx " << format("0x%08" PRIx32, cpu32.ebx);
+ outs() << " ecx " << format("0x%08" PRIx32, cpu32.ecx);
+ outs() << " edx " << format("0x%08" PRIx32, cpu32.edx) << "\n";
+ outs() << "\t edi " << format("0x%08" PRIx32, cpu32.edi);
+ outs() << " esi " << format("0x%08" PRIx32, cpu32.esi);
+ outs() << " ebp " << format("0x%08" PRIx32, cpu32.ebp);
+ outs() << " esp " << format("0x%08" PRIx32, cpu32.esp) << "\n";
+ outs() << "\t ss " << format("0x%08" PRIx32, cpu32.ss);
+ outs() << " eflags " << format("0x%08" PRIx32, cpu32.eflags);
+ outs() << " eip " << format("0x%08" PRIx32, cpu32.eip);
+ outs() << " cs " << format("0x%08" PRIx32, cpu32.cs) << "\n";
+ outs() << "\t ds " << format("0x%08" PRIx32, cpu32.ds);
+ outs() << " es " << format("0x%08" PRIx32, cpu32.es);
+ outs() << " fs " << format("0x%08" PRIx32, cpu32.fs);
+ outs() << " gs " << format("0x%08" PRIx32, cpu32.gs) << "\n";
+}
+
+static void Print_x86_thread_state64_t(MachO::x86_thread_state64_t &cpu64) {
+ outs() << " rax " << format("0x%016" PRIx64, cpu64.rax);
+ outs() << " rbx " << format("0x%016" PRIx64, cpu64.rbx);
+ outs() << " rcx " << format("0x%016" PRIx64, cpu64.rcx) << "\n";
+ outs() << " rdx " << format("0x%016" PRIx64, cpu64.rdx);
+ outs() << " rdi " << format("0x%016" PRIx64, cpu64.rdi);
+ outs() << " rsi " << format("0x%016" PRIx64, cpu64.rsi) << "\n";
+ outs() << " rbp " << format("0x%016" PRIx64, cpu64.rbp);
+ outs() << " rsp " << format("0x%016" PRIx64, cpu64.rsp);
+ outs() << " r8 " << format("0x%016" PRIx64, cpu64.r8) << "\n";
+ outs() << " r9 " << format("0x%016" PRIx64, cpu64.r9);
+ outs() << " r10 " << format("0x%016" PRIx64, cpu64.r10);
+ outs() << " r11 " << format("0x%016" PRIx64, cpu64.r11) << "\n";
+ outs() << " r12 " << format("0x%016" PRIx64, cpu64.r12);
+ outs() << " r13 " << format("0x%016" PRIx64, cpu64.r13);
+ outs() << " r14 " << format("0x%016" PRIx64, cpu64.r14) << "\n";
+ outs() << " r15 " << format("0x%016" PRIx64, cpu64.r15);
+ outs() << " rip " << format("0x%016" PRIx64, cpu64.rip) << "\n";
+ outs() << "rflags " << format("0x%016" PRIx64, cpu64.rflags);
+ outs() << " cs " << format("0x%016" PRIx64, cpu64.cs);
+ outs() << " fs " << format("0x%016" PRIx64, cpu64.fs) << "\n";
+ outs() << " gs " << format("0x%016" PRIx64, cpu64.gs) << "\n";
+}
+
+static void Print_mmst_reg(MachO::mmst_reg_t &r) {
+ uint32_t f;
+ outs() << "\t mmst_reg ";
+ for (f = 0; f < 10; f++)
+ outs() << format("%02" PRIx32, (r.mmst_reg[f] & 0xff)) << " ";
+ outs() << "\n";
+ outs() << "\t mmst_rsrv ";
+ for (f = 0; f < 6; f++)
+ outs() << format("%02" PRIx32, (r.mmst_rsrv[f] & 0xff)) << " ";
+ outs() << "\n";
+}
+
+static void Print_xmm_reg(MachO::xmm_reg_t &r) {
+ uint32_t f;
+ outs() << "\t xmm_reg ";
+ for (f = 0; f < 16; f++)
+ outs() << format("%02" PRIx32, (r.xmm_reg[f] & 0xff)) << " ";
+ outs() << "\n";
+}
+
+static void Print_x86_float_state_t(MachO::x86_float_state64_t &fpu) {
+ outs() << "\t fpu_reserved[0] " << fpu.fpu_reserved[0];
+ outs() << " fpu_reserved[1] " << fpu.fpu_reserved[1] << "\n";
+ outs() << "\t control: invalid " << fpu.fpu_fcw.invalid;
+ outs() << " denorm " << fpu.fpu_fcw.denorm;
+ outs() << " zdiv " << fpu.fpu_fcw.zdiv;
+ outs() << " ovrfl " << fpu.fpu_fcw.ovrfl;
+ outs() << " undfl " << fpu.fpu_fcw.undfl;
+ outs() << " precis " << fpu.fpu_fcw.precis << "\n";
+ outs() << "\t\t pc ";
+ if (fpu.fpu_fcw.pc == MachO::x86_FP_PREC_24B)
+ outs() << "FP_PREC_24B ";
+ else if (fpu.fpu_fcw.pc == MachO::x86_FP_PREC_53B)
+ outs() << "FP_PREC_53B ";
+ else if (fpu.fpu_fcw.pc == MachO::x86_FP_PREC_64B)
+ outs() << "FP_PREC_64B ";
+ else
+ outs() << fpu.fpu_fcw.pc << " ";
+ outs() << "rc ";
+ if (fpu.fpu_fcw.rc == MachO::x86_FP_RND_NEAR)
+ outs() << "FP_RND_NEAR ";
+ else if (fpu.fpu_fcw.rc == MachO::x86_FP_RND_DOWN)
+ outs() << "FP_RND_DOWN ";
+ else if (fpu.fpu_fcw.rc == MachO::x86_FP_RND_UP)
+ outs() << "FP_RND_UP ";
+ else if (fpu.fpu_fcw.rc == MachO::x86_FP_CHOP)
+ outs() << "FP_CHOP ";
+ outs() << "\n";
+ outs() << "\t status: invalid " << fpu.fpu_fsw.invalid;
+ outs() << " denorm " << fpu.fpu_fsw.denorm;
+ outs() << " zdiv " << fpu.fpu_fsw.zdiv;
+ outs() << " ovrfl " << fpu.fpu_fsw.ovrfl;
+ outs() << " undfl " << fpu.fpu_fsw.undfl;
+ outs() << " precis " << fpu.fpu_fsw.precis;
+ outs() << " stkflt " << fpu.fpu_fsw.stkflt << "\n";
+ outs() << "\t errsumm " << fpu.fpu_fsw.errsumm;
+ outs() << " c0 " << fpu.fpu_fsw.c0;
+ outs() << " c1 " << fpu.fpu_fsw.c1;
+ outs() << " c2 " << fpu.fpu_fsw.c2;
+ outs() << " tos " << fpu.fpu_fsw.tos;
+ outs() << " c3 " << fpu.fpu_fsw.c3;
+ outs() << " busy " << fpu.fpu_fsw.busy << "\n";
+ outs() << "\t fpu_ftw " << format("0x%02" PRIx32, fpu.fpu_ftw);
+ outs() << " fpu_rsrv1 " << format("0x%02" PRIx32, fpu.fpu_rsrv1);
+ outs() << " fpu_fop " << format("0x%04" PRIx32, fpu.fpu_fop);
+ outs() << " fpu_ip " << format("0x%08" PRIx32, fpu.fpu_ip) << "\n";
+ outs() << "\t fpu_cs " << format("0x%04" PRIx32, fpu.fpu_cs);
+ outs() << " fpu_rsrv2 " << format("0x%04" PRIx32, fpu.fpu_rsrv2);
+ outs() << " fpu_dp " << format("0x%08" PRIx32, fpu.fpu_dp);
+ outs() << " fpu_ds " << format("0x%04" PRIx32, fpu.fpu_ds) << "\n";
+ outs() << "\t fpu_rsrv3 " << format("0x%04" PRIx32, fpu.fpu_rsrv3);
+ outs() << " fpu_mxcsr " << format("0x%08" PRIx32, fpu.fpu_mxcsr);
+ outs() << " fpu_mxcsrmask " << format("0x%08" PRIx32, fpu.fpu_mxcsrmask);
+ outs() << "\n";
+ outs() << "\t fpu_stmm0:\n";
+ Print_mmst_reg(fpu.fpu_stmm0);
+ outs() << "\t fpu_stmm1:\n";
+ Print_mmst_reg(fpu.fpu_stmm1);
+ outs() << "\t fpu_stmm2:\n";
+ Print_mmst_reg(fpu.fpu_stmm2);
+ outs() << "\t fpu_stmm3:\n";
+ Print_mmst_reg(fpu.fpu_stmm3);
+ outs() << "\t fpu_stmm4:\n";
+ Print_mmst_reg(fpu.fpu_stmm4);
+ outs() << "\t fpu_stmm5:\n";
+ Print_mmst_reg(fpu.fpu_stmm5);
+ outs() << "\t fpu_stmm6:\n";
+ Print_mmst_reg(fpu.fpu_stmm6);
+ outs() << "\t fpu_stmm7:\n";
+ Print_mmst_reg(fpu.fpu_stmm7);
+ outs() << "\t fpu_xmm0:\n";
+ Print_xmm_reg(fpu.fpu_xmm0);
+ outs() << "\t fpu_xmm1:\n";
+ Print_xmm_reg(fpu.fpu_xmm1);
+ outs() << "\t fpu_xmm2:\n";
+ Print_xmm_reg(fpu.fpu_xmm2);
+ outs() << "\t fpu_xmm3:\n";
+ Print_xmm_reg(fpu.fpu_xmm3);
+ outs() << "\t fpu_xmm4:\n";
+ Print_xmm_reg(fpu.fpu_xmm4);
+ outs() << "\t fpu_xmm5:\n";
+ Print_xmm_reg(fpu.fpu_xmm5);
+ outs() << "\t fpu_xmm6:\n";
+ Print_xmm_reg(fpu.fpu_xmm6);
+ outs() << "\t fpu_xmm7:\n";
+ Print_xmm_reg(fpu.fpu_xmm7);
+ outs() << "\t fpu_xmm8:\n";
+ Print_xmm_reg(fpu.fpu_xmm8);
+ outs() << "\t fpu_xmm9:\n";
+ Print_xmm_reg(fpu.fpu_xmm9);
+ outs() << "\t fpu_xmm10:\n";
+ Print_xmm_reg(fpu.fpu_xmm10);
+ outs() << "\t fpu_xmm11:\n";
+ Print_xmm_reg(fpu.fpu_xmm11);
+ outs() << "\t fpu_xmm12:\n";
+ Print_xmm_reg(fpu.fpu_xmm12);
+ outs() << "\t fpu_xmm13:\n";
+ Print_xmm_reg(fpu.fpu_xmm13);
+ outs() << "\t fpu_xmm14:\n";
+ Print_xmm_reg(fpu.fpu_xmm14);
+ outs() << "\t fpu_xmm15:\n";
+ Print_xmm_reg(fpu.fpu_xmm15);
+ outs() << "\t fpu_rsrv4:\n";
+ for (uint32_t f = 0; f < 6; f++) {
+ outs() << "\t ";
+ for (uint32_t g = 0; g < 16; g++)
+ outs() << format("%02" PRIx32, fpu.fpu_rsrv4[f * g]) << " ";
+ outs() << "\n";
+ }
+ outs() << "\t fpu_reserved1 " << format("0x%08" PRIx32, fpu.fpu_reserved1);
+ outs() << "\n";
+}
+
+static void Print_x86_exception_state_t(MachO::x86_exception_state64_t &exc64) {
+ outs() << "\t trapno " << format("0x%08" PRIx32, exc64.trapno);
+ outs() << " err " << format("0x%08" PRIx32, exc64.err);
+ outs() << " faultvaddr " << format("0x%016" PRIx64, exc64.faultvaddr) << "\n";
+}
+
+static void Print_arm_thread_state32_t(MachO::arm_thread_state32_t &cpu32) {
+ outs() << "\t r0 " << format("0x%08" PRIx32, cpu32.r[0]);
+ outs() << " r1 " << format("0x%08" PRIx32, cpu32.r[1]);
+ outs() << " r2 " << format("0x%08" PRIx32, cpu32.r[2]);
+ outs() << " r3 " << format("0x%08" PRIx32, cpu32.r[3]) << "\n";
+ outs() << "\t r4 " << format("0x%08" PRIx32, cpu32.r[4]);
+ outs() << " r5 " << format("0x%08" PRIx32, cpu32.r[5]);
+ outs() << " r6 " << format("0x%08" PRIx32, cpu32.r[6]);
+ outs() << " r7 " << format("0x%08" PRIx32, cpu32.r[7]) << "\n";
+ outs() << "\t r8 " << format("0x%08" PRIx32, cpu32.r[8]);
+ outs() << " r9 " << format("0x%08" PRIx32, cpu32.r[9]);
+ outs() << " r10 " << format("0x%08" PRIx32, cpu32.r[10]);
+ outs() << " r11 " << format("0x%08" PRIx32, cpu32.r[11]) << "\n";
+ outs() << "\t r12 " << format("0x%08" PRIx32, cpu32.r[12]);
+ outs() << " sp " << format("0x%08" PRIx32, cpu32.sp);
+ outs() << " lr " << format("0x%08" PRIx32, cpu32.lr);
+ outs() << " pc " << format("0x%08" PRIx32, cpu32.pc) << "\n";
+ outs() << "\t cpsr " << format("0x%08" PRIx32, cpu32.cpsr) << "\n";
+}
+
+static void Print_arm_thread_state64_t(MachO::arm_thread_state64_t &cpu64) {
+ outs() << "\t x0 " << format("0x%016" PRIx64, cpu64.x[0]);
+ outs() << " x1 " << format("0x%016" PRIx64, cpu64.x[1]);
+ outs() << " x2 " << format("0x%016" PRIx64, cpu64.x[2]) << "\n";
+ outs() << "\t x3 " << format("0x%016" PRIx64, cpu64.x[3]);
+ outs() << " x4 " << format("0x%016" PRIx64, cpu64.x[4]);
+ outs() << " x5 " << format("0x%016" PRIx64, cpu64.x[5]) << "\n";
+ outs() << "\t x6 " << format("0x%016" PRIx64, cpu64.x[6]);
+ outs() << " x7 " << format("0x%016" PRIx64, cpu64.x[7]);
+ outs() << " x8 " << format("0x%016" PRIx64, cpu64.x[8]) << "\n";
+ outs() << "\t x9 " << format("0x%016" PRIx64, cpu64.x[9]);
+ outs() << " x10 " << format("0x%016" PRIx64, cpu64.x[10]);
+ outs() << " x11 " << format("0x%016" PRIx64, cpu64.x[11]) << "\n";
+ outs() << "\t x12 " << format("0x%016" PRIx64, cpu64.x[12]);
+ outs() << " x13 " << format("0x%016" PRIx64, cpu64.x[13]);
+ outs() << " x14 " << format("0x%016" PRIx64, cpu64.x[14]) << "\n";
+ outs() << "\t x15 " << format("0x%016" PRIx64, cpu64.x[15]);
+ outs() << " x16 " << format("0x%016" PRIx64, cpu64.x[16]);
+ outs() << " x17 " << format("0x%016" PRIx64, cpu64.x[17]) << "\n";
+ outs() << "\t x18 " << format("0x%016" PRIx64, cpu64.x[18]);
+ outs() << " x19 " << format("0x%016" PRIx64, cpu64.x[19]);
+ outs() << " x20 " << format("0x%016" PRIx64, cpu64.x[20]) << "\n";
+ outs() << "\t x21 " << format("0x%016" PRIx64, cpu64.x[21]);
+ outs() << " x22 " << format("0x%016" PRIx64, cpu64.x[22]);
+ outs() << " x23 " << format("0x%016" PRIx64, cpu64.x[23]) << "\n";
+ outs() << "\t x24 " << format("0x%016" PRIx64, cpu64.x[24]);
+ outs() << " x25 " << format("0x%016" PRIx64, cpu64.x[25]);
+ outs() << " x26 " << format("0x%016" PRIx64, cpu64.x[26]) << "\n";
+ outs() << "\t x27 " << format("0x%016" PRIx64, cpu64.x[27]);
+ outs() << " x28 " << format("0x%016" PRIx64, cpu64.x[28]);
+ outs() << " fp " << format("0x%016" PRIx64, cpu64.fp) << "\n";
+ outs() << "\t lr " << format("0x%016" PRIx64, cpu64.lr);
+ outs() << " sp " << format("0x%016" PRIx64, cpu64.sp);
+ outs() << " pc " << format("0x%016" PRIx64, cpu64.pc) << "\n";
+ outs() << "\t cpsr " << format("0x%08" PRIx32, cpu64.cpsr) << "\n";
+}
+
+static void PrintThreadCommand(MachO::thread_command t, const char *Ptr,
+ bool isLittleEndian, uint32_t cputype) {
+ if (t.cmd == MachO::LC_THREAD)
+ outs() << " cmd LC_THREAD\n";
+ else if (t.cmd == MachO::LC_UNIXTHREAD)
+ outs() << " cmd LC_UNIXTHREAD\n";
+ else
+ outs() << " cmd " << t.cmd << " (unknown)\n";
+ outs() << " cmdsize " << t.cmdsize;
+ if (t.cmdsize < sizeof(struct MachO::thread_command) + 2 * sizeof(uint32_t))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+
+ const char *begin = Ptr + sizeof(struct MachO::thread_command);
+ const char *end = Ptr + t.cmdsize;
+ uint32_t flavor, count, left;
+ if (cputype == MachO::CPU_TYPE_I386) {
+ while (begin < end) {
+ if (end - begin > (ptrdiff_t)sizeof(uint32_t)) {
+ memcpy((char *)&flavor, begin, sizeof(uint32_t));
+ begin += sizeof(uint32_t);
+ } else {
+ flavor = 0;
+ begin = end;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ sys::swapByteOrder(flavor);
+ if (end - begin > (ptrdiff_t)sizeof(uint32_t)) {
+ memcpy((char *)&count, begin, sizeof(uint32_t));
+ begin += sizeof(uint32_t);
+ } else {
+ count = 0;
+ begin = end;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ sys::swapByteOrder(count);
+ if (flavor == MachO::x86_THREAD_STATE32) {
+ outs() << " flavor i386_THREAD_STATE\n";
+ if (count == MachO::x86_THREAD_STATE32_COUNT)
+ outs() << " count i386_THREAD_STATE_COUNT\n";
+ else
+ outs() << " count " << count
+ << " (not x86_THREAD_STATE32_COUNT)\n";
+ MachO::x86_thread_state32_t cpu32;
+ left = end - begin;
+ if (left >= sizeof(MachO::x86_thread_state32_t)) {
+ memcpy(&cpu32, begin, sizeof(MachO::x86_thread_state32_t));
+ begin += sizeof(MachO::x86_thread_state32_t);
+ } else {
+ memset(&cpu32, '\0', sizeof(MachO::x86_thread_state32_t));
+ memcpy(&cpu32, begin, left);
+ begin += left;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ swapStruct(cpu32);
+ Print_x86_thread_state32_t(cpu32);
+ } else if (flavor == MachO::x86_THREAD_STATE) {
+ outs() << " flavor x86_THREAD_STATE\n";
+ if (count == MachO::x86_THREAD_STATE_COUNT)
+ outs() << " count x86_THREAD_STATE_COUNT\n";
+ else
+ outs() << " count " << count
+ << " (not x86_THREAD_STATE_COUNT)\n";
+ struct MachO::x86_thread_state_t ts;
+ left = end - begin;
+ if (left >= sizeof(MachO::x86_thread_state_t)) {
+ memcpy(&ts, begin, sizeof(MachO::x86_thread_state_t));
+ begin += sizeof(MachO::x86_thread_state_t);
+ } else {
+ memset(&ts, '\0', sizeof(MachO::x86_thread_state_t));
+ memcpy(&ts, begin, left);
+ begin += left;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ swapStruct(ts);
+ if (ts.tsh.flavor == MachO::x86_THREAD_STATE32) {
+ outs() << "\t tsh.flavor x86_THREAD_STATE32 ";
+ if (ts.tsh.count == MachO::x86_THREAD_STATE32_COUNT)
+ outs() << "tsh.count x86_THREAD_STATE32_COUNT\n";
+ else
+ outs() << "tsh.count " << ts.tsh.count
+ << " (not x86_THREAD_STATE32_COUNT\n";
+ Print_x86_thread_state32_t(ts.uts.ts32);
+ } else {
+ outs() << "\t tsh.flavor " << ts.tsh.flavor << " tsh.count "
+ << ts.tsh.count << "\n";
+ }
+ } else {
+ outs() << " flavor " << flavor << " (unknown)\n";
+ outs() << " count " << count << "\n";
+ outs() << " state (unknown)\n";
+ begin += count * sizeof(uint32_t);
+ }
+ }
+ } else if (cputype == MachO::CPU_TYPE_X86_64) {
+ while (begin < end) {
+ if (end - begin > (ptrdiff_t)sizeof(uint32_t)) {
+ memcpy((char *)&flavor, begin, sizeof(uint32_t));
+ begin += sizeof(uint32_t);
+ } else {
+ flavor = 0;
+ begin = end;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ sys::swapByteOrder(flavor);
+ if (end - begin > (ptrdiff_t)sizeof(uint32_t)) {
+ memcpy((char *)&count, begin, sizeof(uint32_t));
+ begin += sizeof(uint32_t);
+ } else {
+ count = 0;
+ begin = end;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ sys::swapByteOrder(count);
+ if (flavor == MachO::x86_THREAD_STATE64) {
+ outs() << " flavor x86_THREAD_STATE64\n";
+ if (count == MachO::x86_THREAD_STATE64_COUNT)
+ outs() << " count x86_THREAD_STATE64_COUNT\n";
+ else
+ outs() << " count " << count
+ << " (not x86_THREAD_STATE64_COUNT)\n";
+ MachO::x86_thread_state64_t cpu64;
+ left = end - begin;
+ if (left >= sizeof(MachO::x86_thread_state64_t)) {
+ memcpy(&cpu64, begin, sizeof(MachO::x86_thread_state64_t));
+ begin += sizeof(MachO::x86_thread_state64_t);
+ } else {
+ memset(&cpu64, '\0', sizeof(MachO::x86_thread_state64_t));
+ memcpy(&cpu64, begin, left);
+ begin += left;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ swapStruct(cpu64);
+ Print_x86_thread_state64_t(cpu64);
+ } else if (flavor == MachO::x86_THREAD_STATE) {
+ outs() << " flavor x86_THREAD_STATE\n";
+ if (count == MachO::x86_THREAD_STATE_COUNT)
+ outs() << " count x86_THREAD_STATE_COUNT\n";
+ else
+ outs() << " count " << count
+ << " (not x86_THREAD_STATE_COUNT)\n";
+ struct MachO::x86_thread_state_t ts;
+ left = end - begin;
+ if (left >= sizeof(MachO::x86_thread_state_t)) {
+ memcpy(&ts, begin, sizeof(MachO::x86_thread_state_t));
+ begin += sizeof(MachO::x86_thread_state_t);
+ } else {
+ memset(&ts, '\0', sizeof(MachO::x86_thread_state_t));
+ memcpy(&ts, begin, left);
+ begin += left;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ swapStruct(ts);
+ if (ts.tsh.flavor == MachO::x86_THREAD_STATE64) {
+ outs() << "\t tsh.flavor x86_THREAD_STATE64 ";
+ if (ts.tsh.count == MachO::x86_THREAD_STATE64_COUNT)
+ outs() << "tsh.count x86_THREAD_STATE64_COUNT\n";
+ else
+ outs() << "tsh.count " << ts.tsh.count
+ << " (not x86_THREAD_STATE64_COUNT\n";
+ Print_x86_thread_state64_t(ts.uts.ts64);
+ } else {
+ outs() << "\t tsh.flavor " << ts.tsh.flavor << " tsh.count "
+ << ts.tsh.count << "\n";
+ }
+ } else if (flavor == MachO::x86_FLOAT_STATE) {
+ outs() << " flavor x86_FLOAT_STATE\n";
+ if (count == MachO::x86_FLOAT_STATE_COUNT)
+ outs() << " count x86_FLOAT_STATE_COUNT\n";
+ else
+ outs() << " count " << count << " (not x86_FLOAT_STATE_COUNT)\n";
+ struct MachO::x86_float_state_t fs;
+ left = end - begin;
+ if (left >= sizeof(MachO::x86_float_state_t)) {
+ memcpy(&fs, begin, sizeof(MachO::x86_float_state_t));
+ begin += sizeof(MachO::x86_float_state_t);
+ } else {
+ memset(&fs, '\0', sizeof(MachO::x86_float_state_t));
+ memcpy(&fs, begin, left);
+ begin += left;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ swapStruct(fs);
+ if (fs.fsh.flavor == MachO::x86_FLOAT_STATE64) {
+ outs() << "\t fsh.flavor x86_FLOAT_STATE64 ";
+ if (fs.fsh.count == MachO::x86_FLOAT_STATE64_COUNT)
+ outs() << "fsh.count x86_FLOAT_STATE64_COUNT\n";
+ else
+ outs() << "fsh.count " << fs.fsh.count
+ << " (not x86_FLOAT_STATE64_COUNT\n";
+ Print_x86_float_state_t(fs.ufs.fs64);
+ } else {
+ outs() << "\t fsh.flavor " << fs.fsh.flavor << " fsh.count "
+ << fs.fsh.count << "\n";
+ }
+ } else if (flavor == MachO::x86_EXCEPTION_STATE) {
+ outs() << " flavor x86_EXCEPTION_STATE\n";
+ if (count == MachO::x86_EXCEPTION_STATE_COUNT)
+ outs() << " count x86_EXCEPTION_STATE_COUNT\n";
+ else
+ outs() << " count " << count
+ << " (not x86_EXCEPTION_STATE_COUNT)\n";
+ struct MachO::x86_exception_state_t es;
+ left = end - begin;
+ if (left >= sizeof(MachO::x86_exception_state_t)) {
+ memcpy(&es, begin, sizeof(MachO::x86_exception_state_t));
+ begin += sizeof(MachO::x86_exception_state_t);
+ } else {
+ memset(&es, '\0', sizeof(MachO::x86_exception_state_t));
+ memcpy(&es, begin, left);
+ begin += left;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ swapStruct(es);
+ if (es.esh.flavor == MachO::x86_EXCEPTION_STATE64) {
+ outs() << "\t esh.flavor x86_EXCEPTION_STATE64\n";
+ if (es.esh.count == MachO::x86_EXCEPTION_STATE64_COUNT)
+ outs() << "\t esh.count x86_EXCEPTION_STATE64_COUNT\n";
+ else
+ outs() << "\t esh.count " << es.esh.count
+ << " (not x86_EXCEPTION_STATE64_COUNT\n";
+ Print_x86_exception_state_t(es.ues.es64);
+ } else {
+ outs() << "\t esh.flavor " << es.esh.flavor << " esh.count "
+ << es.esh.count << "\n";
+ }
+ } else if (flavor == MachO::x86_EXCEPTION_STATE64) {
+ outs() << " flavor x86_EXCEPTION_STATE64\n";
+ if (count == MachO::x86_EXCEPTION_STATE64_COUNT)
+ outs() << " count x86_EXCEPTION_STATE64_COUNT\n";
+ else
+ outs() << " count " << count
+ << " (not x86_EXCEPTION_STATE64_COUNT)\n";
+ struct MachO::x86_exception_state64_t es64;
+ left = end - begin;
+ if (left >= sizeof(MachO::x86_exception_state64_t)) {
+ memcpy(&es64, begin, sizeof(MachO::x86_exception_state64_t));
+ begin += sizeof(MachO::x86_exception_state64_t);
+ } else {
+ memset(&es64, '\0', sizeof(MachO::x86_exception_state64_t));
+ memcpy(&es64, begin, left);
+ begin += left;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ swapStruct(es64);
+ Print_x86_exception_state_t(es64);
+ } else {
+ outs() << " flavor " << flavor << " (unknown)\n";
+ outs() << " count " << count << "\n";
+ outs() << " state (unknown)\n";
+ begin += count * sizeof(uint32_t);
+ }
+ }
+ } else if (cputype == MachO::CPU_TYPE_ARM) {
+ while (begin < end) {
+ if (end - begin > (ptrdiff_t)sizeof(uint32_t)) {
+ memcpy((char *)&flavor, begin, sizeof(uint32_t));
+ begin += sizeof(uint32_t);
+ } else {
+ flavor = 0;
+ begin = end;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ sys::swapByteOrder(flavor);
+ if (end - begin > (ptrdiff_t)sizeof(uint32_t)) {
+ memcpy((char *)&count, begin, sizeof(uint32_t));
+ begin += sizeof(uint32_t);
+ } else {
+ count = 0;
+ begin = end;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ sys::swapByteOrder(count);
+ if (flavor == MachO::ARM_THREAD_STATE) {
+ outs() << " flavor ARM_THREAD_STATE\n";
+ if (count == MachO::ARM_THREAD_STATE_COUNT)
+ outs() << " count ARM_THREAD_STATE_COUNT\n";
+ else
+ outs() << " count " << count
+ << " (not ARM_THREAD_STATE_COUNT)\n";
+ MachO::arm_thread_state32_t cpu32;
+ left = end - begin;
+ if (left >= sizeof(MachO::arm_thread_state32_t)) {
+ memcpy(&cpu32, begin, sizeof(MachO::arm_thread_state32_t));
+ begin += sizeof(MachO::arm_thread_state32_t);
+ } else {
+ memset(&cpu32, '\0', sizeof(MachO::arm_thread_state32_t));
+ memcpy(&cpu32, begin, left);
+ begin += left;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ swapStruct(cpu32);
+ Print_arm_thread_state32_t(cpu32);
+ } else {
+ outs() << " flavor " << flavor << " (unknown)\n";
+ outs() << " count " << count << "\n";
+ outs() << " state (unknown)\n";
+ begin += count * sizeof(uint32_t);
+ }
+ }
+ } else if (cputype == MachO::CPU_TYPE_ARM64 ||
+ cputype == MachO::CPU_TYPE_ARM64_32) {
+ while (begin < end) {
+ if (end - begin > (ptrdiff_t)sizeof(uint32_t)) {
+ memcpy((char *)&flavor, begin, sizeof(uint32_t));
+ begin += sizeof(uint32_t);
+ } else {
+ flavor = 0;
+ begin = end;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ sys::swapByteOrder(flavor);
+ if (end - begin > (ptrdiff_t)sizeof(uint32_t)) {
+ memcpy((char *)&count, begin, sizeof(uint32_t));
+ begin += sizeof(uint32_t);
+ } else {
+ count = 0;
+ begin = end;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ sys::swapByteOrder(count);
+ if (flavor == MachO::ARM_THREAD_STATE64) {
+ outs() << " flavor ARM_THREAD_STATE64\n";
+ if (count == MachO::ARM_THREAD_STATE64_COUNT)
+ outs() << " count ARM_THREAD_STATE64_COUNT\n";
+ else
+ outs() << " count " << count
+ << " (not ARM_THREAD_STATE64_COUNT)\n";
+ MachO::arm_thread_state64_t cpu64;
+ left = end - begin;
+ if (left >= sizeof(MachO::arm_thread_state64_t)) {
+ memcpy(&cpu64, begin, sizeof(MachO::arm_thread_state64_t));
+ begin += sizeof(MachO::arm_thread_state64_t);
+ } else {
+ memset(&cpu64, '\0', sizeof(MachO::arm_thread_state64_t));
+ memcpy(&cpu64, begin, left);
+ begin += left;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ swapStruct(cpu64);
+ Print_arm_thread_state64_t(cpu64);
+ } else {
+ outs() << " flavor " << flavor << " (unknown)\n";
+ outs() << " count " << count << "\n";
+ outs() << " state (unknown)\n";
+ begin += count * sizeof(uint32_t);
+ }
+ }
+ } else {
+ while (begin < end) {
+ if (end - begin > (ptrdiff_t)sizeof(uint32_t)) {
+ memcpy((char *)&flavor, begin, sizeof(uint32_t));
+ begin += sizeof(uint32_t);
+ } else {
+ flavor = 0;
+ begin = end;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ sys::swapByteOrder(flavor);
+ if (end - begin > (ptrdiff_t)sizeof(uint32_t)) {
+ memcpy((char *)&count, begin, sizeof(uint32_t));
+ begin += sizeof(uint32_t);
+ } else {
+ count = 0;
+ begin = end;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ sys::swapByteOrder(count);
+ outs() << " flavor " << flavor << "\n";
+ outs() << " count " << count << "\n";
+ outs() << " state (Unknown cputype/cpusubtype)\n";
+ begin += count * sizeof(uint32_t);
+ }
+ }
+}
+
+static void PrintDylibCommand(MachO::dylib_command dl, const char *Ptr) {
+ if (dl.cmd == MachO::LC_ID_DYLIB)
+ outs() << " cmd LC_ID_DYLIB\n";
+ else if (dl.cmd == MachO::LC_LOAD_DYLIB)
+ outs() << " cmd LC_LOAD_DYLIB\n";
+ else if (dl.cmd == MachO::LC_LOAD_WEAK_DYLIB)
+ outs() << " cmd LC_LOAD_WEAK_DYLIB\n";
+ else if (dl.cmd == MachO::LC_REEXPORT_DYLIB)
+ outs() << " cmd LC_REEXPORT_DYLIB\n";
+ else if (dl.cmd == MachO::LC_LAZY_LOAD_DYLIB)
+ outs() << " cmd LC_LAZY_LOAD_DYLIB\n";
+ else if (dl.cmd == MachO::LC_LOAD_UPWARD_DYLIB)
+ outs() << " cmd LC_LOAD_UPWARD_DYLIB\n";
+ else
+ outs() << " cmd " << dl.cmd << " (unknown)\n";
+ outs() << " cmdsize " << dl.cmdsize;
+ if (dl.cmdsize < sizeof(struct MachO::dylib_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ if (dl.dylib.name < dl.cmdsize) {
+ const char *P = (const char *)(Ptr) + dl.dylib.name;
+ outs() << " name " << P << " (offset " << dl.dylib.name << ")\n";
+ } else {
+ outs() << " name ?(bad offset " << dl.dylib.name << ")\n";
+ }
+ outs() << " time stamp " << dl.dylib.timestamp << " ";
+ time_t t = dl.dylib.timestamp;
+ outs() << ctime(&t);
+ outs() << " current version ";
+ if (dl.dylib.current_version == 0xffffffff)
+ outs() << "n/a\n";
+ else
+ outs() << ((dl.dylib.current_version >> 16) & 0xffff) << "."
+ << ((dl.dylib.current_version >> 8) & 0xff) << "."
+ << (dl.dylib.current_version & 0xff) << "\n";
+ outs() << "compatibility version ";
+ if (dl.dylib.compatibility_version == 0xffffffff)
+ outs() << "n/a\n";
+ else
+ outs() << ((dl.dylib.compatibility_version >> 16) & 0xffff) << "."
+ << ((dl.dylib.compatibility_version >> 8) & 0xff) << "."
+ << (dl.dylib.compatibility_version & 0xff) << "\n";
+}
+
+static void PrintLinkEditDataCommand(MachO::linkedit_data_command ld,
+ uint32_t object_size) {
+ if (ld.cmd == MachO::LC_CODE_SIGNATURE)
+ outs() << " cmd LC_CODE_SIGNATURE\n";
+ else if (ld.cmd == MachO::LC_SEGMENT_SPLIT_INFO)
+ outs() << " cmd LC_SEGMENT_SPLIT_INFO\n";
+ else if (ld.cmd == MachO::LC_FUNCTION_STARTS)
+ outs() << " cmd LC_FUNCTION_STARTS\n";
+ else if (ld.cmd == MachO::LC_DATA_IN_CODE)
+ outs() << " cmd LC_DATA_IN_CODE\n";
+ else if (ld.cmd == MachO::LC_DYLIB_CODE_SIGN_DRS)
+ outs() << " cmd LC_DYLIB_CODE_SIGN_DRS\n";
+ else if (ld.cmd == MachO::LC_LINKER_OPTIMIZATION_HINT)
+ outs() << " cmd LC_LINKER_OPTIMIZATION_HINT\n";
+ else if (ld.cmd == MachO::LC_DYLD_EXPORTS_TRIE)
+ outs() << " cmd LC_DYLD_EXPORTS_TRIE\n";
+ else if (ld.cmd == MachO::LC_DYLD_CHAINED_FIXUPS)
+ outs() << " cmd LC_DYLD_CHAINED_FIXUPS\n";
+ else
+ outs() << " cmd " << ld.cmd << " (?)\n";
+ outs() << " cmdsize " << ld.cmdsize;
+ if (ld.cmdsize != sizeof(struct MachO::linkedit_data_command))
+ outs() << " Incorrect size\n";
+ else
+ outs() << "\n";
+ outs() << " dataoff " << ld.dataoff;
+ if (ld.dataoff > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+ outs() << " datasize " << ld.datasize;
+ uint64_t big_size = ld.dataoff;
+ big_size += ld.datasize;
+ if (big_size > object_size)
+ outs() << " (past end of file)\n";
+ else
+ outs() << "\n";
+}
+
+static void PrintLoadCommands(const MachOObjectFile *Obj, uint32_t filetype,
+ uint32_t cputype, bool verbose) {
+ StringRef Buf = Obj->getData();
+ unsigned Index = 0;
+ for (const auto &Command : Obj->load_commands()) {
+ outs() << "Load command " << Index++ << "\n";
+ if (Command.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command SLC = Obj->getSegmentLoadCommand(Command);
+ const char *sg_segname = SLC.segname;
+ PrintSegmentCommand(SLC.cmd, SLC.cmdsize, SLC.segname, SLC.vmaddr,
+ SLC.vmsize, SLC.fileoff, SLC.filesize, SLC.maxprot,
+ SLC.initprot, SLC.nsects, SLC.flags, Buf.size(),
+ verbose);
+ for (unsigned j = 0; j < SLC.nsects; j++) {
+ MachO::section S = Obj->getSection(Command, j);
+ PrintSection(S.sectname, S.segname, S.addr, S.size, S.offset, S.align,
+ S.reloff, S.nreloc, S.flags, S.reserved1, S.reserved2,
+ SLC.cmd, sg_segname, filetype, Buf.size(), verbose);
+ }
+ } else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 SLC_64 = Obj->getSegment64LoadCommand(Command);
+ const char *sg_segname = SLC_64.segname;
+ PrintSegmentCommand(SLC_64.cmd, SLC_64.cmdsize, SLC_64.segname,
+ SLC_64.vmaddr, SLC_64.vmsize, SLC_64.fileoff,
+ SLC_64.filesize, SLC_64.maxprot, SLC_64.initprot,
+ SLC_64.nsects, SLC_64.flags, Buf.size(), verbose);
+ for (unsigned j = 0; j < SLC_64.nsects; j++) {
+ MachO::section_64 S_64 = Obj->getSection64(Command, j);
+ PrintSection(S_64.sectname, S_64.segname, S_64.addr, S_64.size,
+ S_64.offset, S_64.align, S_64.reloff, S_64.nreloc,
+ S_64.flags, S_64.reserved1, S_64.reserved2, SLC_64.cmd,
+ sg_segname, filetype, Buf.size(), verbose);
+ }
+ } else if (Command.C.cmd == MachO::LC_SYMTAB) {
+ MachO::symtab_command Symtab = Obj->getSymtabLoadCommand();
+ PrintSymtabLoadCommand(Symtab, Obj->is64Bit(), Buf.size());
+ } else if (Command.C.cmd == MachO::LC_DYSYMTAB) {
+ MachO::dysymtab_command Dysymtab = Obj->getDysymtabLoadCommand();
+ MachO::symtab_command Symtab = Obj->getSymtabLoadCommand();
+ PrintDysymtabLoadCommand(Dysymtab, Symtab.nsyms, Buf.size(),
+ Obj->is64Bit());
+ } else if (Command.C.cmd == MachO::LC_DYLD_INFO ||
+ Command.C.cmd == MachO::LC_DYLD_INFO_ONLY) {
+ MachO::dyld_info_command DyldInfo = Obj->getDyldInfoLoadCommand(Command);
+ PrintDyldInfoLoadCommand(DyldInfo, Buf.size());
+ } else if (Command.C.cmd == MachO::LC_LOAD_DYLINKER ||
+ Command.C.cmd == MachO::LC_ID_DYLINKER ||
+ Command.C.cmd == MachO::LC_DYLD_ENVIRONMENT) {
+ MachO::dylinker_command Dyld = Obj->getDylinkerCommand(Command);
+ PrintDyldLoadCommand(Dyld, Command.Ptr);
+ } else if (Command.C.cmd == MachO::LC_UUID) {
+ MachO::uuid_command Uuid = Obj->getUuidCommand(Command);
+ PrintUuidLoadCommand(Uuid);
+ } else if (Command.C.cmd == MachO::LC_RPATH) {
+ MachO::rpath_command Rpath = Obj->getRpathCommand(Command);
+ PrintRpathLoadCommand(Rpath, Command.Ptr);
+ } else if (Command.C.cmd == MachO::LC_VERSION_MIN_MACOSX ||
+ Command.C.cmd == MachO::LC_VERSION_MIN_IPHONEOS ||
+ Command.C.cmd == MachO::LC_VERSION_MIN_TVOS ||
+ Command.C.cmd == MachO::LC_VERSION_MIN_WATCHOS) {
+ MachO::version_min_command Vd = Obj->getVersionMinLoadCommand(Command);
+ PrintVersionMinLoadCommand(Vd);
+ } else if (Command.C.cmd == MachO::LC_NOTE) {
+ MachO::note_command Nt = Obj->getNoteLoadCommand(Command);
+ PrintNoteLoadCommand(Nt);
+ } else if (Command.C.cmd == MachO::LC_BUILD_VERSION) {
+ MachO::build_version_command Bv =
+ Obj->getBuildVersionLoadCommand(Command);
+ PrintBuildVersionLoadCommand(Obj, Bv);
+ } else if (Command.C.cmd == MachO::LC_SOURCE_VERSION) {
+ MachO::source_version_command Sd = Obj->getSourceVersionCommand(Command);
+ PrintSourceVersionCommand(Sd);
+ } else if (Command.C.cmd == MachO::LC_MAIN) {
+ MachO::entry_point_command Ep = Obj->getEntryPointCommand(Command);
+ PrintEntryPointCommand(Ep);
+ } else if (Command.C.cmd == MachO::LC_ENCRYPTION_INFO) {
+ MachO::encryption_info_command Ei =
+ Obj->getEncryptionInfoCommand(Command);
+ PrintEncryptionInfoCommand(Ei, Buf.size());
+ } else if (Command.C.cmd == MachO::LC_ENCRYPTION_INFO_64) {
+ MachO::encryption_info_command_64 Ei =
+ Obj->getEncryptionInfoCommand64(Command);
+ PrintEncryptionInfoCommand64(Ei, Buf.size());
+ } else if (Command.C.cmd == MachO::LC_LINKER_OPTION) {
+ MachO::linker_option_command Lo =
+ Obj->getLinkerOptionLoadCommand(Command);
+ PrintLinkerOptionCommand(Lo, Command.Ptr);
+ } else if (Command.C.cmd == MachO::LC_SUB_FRAMEWORK) {
+ MachO::sub_framework_command Sf = Obj->getSubFrameworkCommand(Command);
+ PrintSubFrameworkCommand(Sf, Command.Ptr);
+ } else if (Command.C.cmd == MachO::LC_SUB_UMBRELLA) {
+ MachO::sub_umbrella_command Sf = Obj->getSubUmbrellaCommand(Command);
+ PrintSubUmbrellaCommand(Sf, Command.Ptr);
+ } else if (Command.C.cmd == MachO::LC_SUB_LIBRARY) {
+ MachO::sub_library_command Sl = Obj->getSubLibraryCommand(Command);
+ PrintSubLibraryCommand(Sl, Command.Ptr);
+ } else if (Command.C.cmd == MachO::LC_SUB_CLIENT) {
+ MachO::sub_client_command Sc = Obj->getSubClientCommand(Command);
+ PrintSubClientCommand(Sc, Command.Ptr);
+ } else if (Command.C.cmd == MachO::LC_ROUTINES) {
+ MachO::routines_command Rc = Obj->getRoutinesCommand(Command);
+ PrintRoutinesCommand(Rc);
+ } else if (Command.C.cmd == MachO::LC_ROUTINES_64) {
+ MachO::routines_command_64 Rc = Obj->getRoutinesCommand64(Command);
+ PrintRoutinesCommand64(Rc);
+ } else if (Command.C.cmd == MachO::LC_THREAD ||
+ Command.C.cmd == MachO::LC_UNIXTHREAD) {
+ MachO::thread_command Tc = Obj->getThreadCommand(Command);
+ PrintThreadCommand(Tc, Command.Ptr, Obj->isLittleEndian(), cputype);
+ } else if (Command.C.cmd == MachO::LC_LOAD_DYLIB ||
+ Command.C.cmd == MachO::LC_ID_DYLIB ||
+ Command.C.cmd == MachO::LC_LOAD_WEAK_DYLIB ||
+ Command.C.cmd == MachO::LC_REEXPORT_DYLIB ||
+ Command.C.cmd == MachO::LC_LAZY_LOAD_DYLIB ||
+ Command.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) {
+ MachO::dylib_command Dl = Obj->getDylibIDLoadCommand(Command);
+ PrintDylibCommand(Dl, Command.Ptr);
+ } else if (Command.C.cmd == MachO::LC_CODE_SIGNATURE ||
+ Command.C.cmd == MachO::LC_SEGMENT_SPLIT_INFO ||
+ Command.C.cmd == MachO::LC_FUNCTION_STARTS ||
+ Command.C.cmd == MachO::LC_DATA_IN_CODE ||
+ Command.C.cmd == MachO::LC_DYLIB_CODE_SIGN_DRS ||
+ Command.C.cmd == MachO::LC_LINKER_OPTIMIZATION_HINT ||
+ Command.C.cmd == MachO::LC_DYLD_EXPORTS_TRIE ||
+ Command.C.cmd == MachO::LC_DYLD_CHAINED_FIXUPS) {
+ MachO::linkedit_data_command Ld =
+ Obj->getLinkeditDataLoadCommand(Command);
+ PrintLinkEditDataCommand(Ld, Buf.size());
+ } else {
+ outs() << " cmd ?(" << format("0x%08" PRIx32, Command.C.cmd)
+ << ")\n";
+ outs() << " cmdsize " << Command.C.cmdsize << "\n";
+ // TODO: get and print the raw bytes of the load command.
+ }
+ // TODO: print all the other kinds of load commands.
+ }
+}
+
+static void PrintMachHeader(const MachOObjectFile *Obj, bool verbose) {
+ if (Obj->is64Bit()) {
+ MachO::mach_header_64 H_64;
+ H_64 = Obj->getHeader64();
+ PrintMachHeader(H_64.magic, H_64.cputype, H_64.cpusubtype, H_64.filetype,
+ H_64.ncmds, H_64.sizeofcmds, H_64.flags, verbose);
+ } else {
+ MachO::mach_header H;
+ H = Obj->getHeader();
+ PrintMachHeader(H.magic, H.cputype, H.cpusubtype, H.filetype, H.ncmds,
+ H.sizeofcmds, H.flags, verbose);
+ }
+}
+
+void objdump::printMachOFileHeader(const object::ObjectFile *Obj) {
+ const MachOObjectFile *file = cast<const MachOObjectFile>(Obj);
+ PrintMachHeader(file, Verbose);
+}
+
+void objdump::printMachOLoadCommands(const object::ObjectFile *Obj) {
+ const MachOObjectFile *file = cast<const MachOObjectFile>(Obj);
+ uint32_t filetype = 0;
+ uint32_t cputype = 0;
+ if (file->is64Bit()) {
+ MachO::mach_header_64 H_64;
+ H_64 = file->getHeader64();
+ filetype = H_64.filetype;
+ cputype = H_64.cputype;
+ } else {
+ MachO::mach_header H;
+ H = file->getHeader();
+ filetype = H.filetype;
+ cputype = H.cputype;
+ }
+ PrintLoadCommands(file, filetype, cputype, Verbose);
+}
+
+//===----------------------------------------------------------------------===//
+// export trie dumping
+//===----------------------------------------------------------------------===//
+
+static void printMachOExportsTrie(const object::MachOObjectFile *Obj) {
+ uint64_t BaseSegmentAddress = 0;
+ for (const auto &Command : Obj->load_commands()) {
+ if (Command.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command Seg = Obj->getSegmentLoadCommand(Command);
+ if (Seg.fileoff == 0 && Seg.filesize != 0) {
+ BaseSegmentAddress = Seg.vmaddr;
+ break;
+ }
+ } else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = Obj->getSegment64LoadCommand(Command);
+ if (Seg.fileoff == 0 && Seg.filesize != 0) {
+ BaseSegmentAddress = Seg.vmaddr;
+ break;
+ }
+ }
+ }
+ Error Err = Error::success();
+ for (const object::ExportEntry &Entry : Obj->exports(Err)) {
+ uint64_t Flags = Entry.flags();
+ bool ReExport = (Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT);
+ bool WeakDef = (Flags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
+ bool ThreadLocal = ((Flags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) ==
+ MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL);
+ bool Abs = ((Flags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) ==
+ MachO::EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE);
+ bool Resolver = (Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER);
+ if (ReExport)
+ outs() << "[re-export] ";
+ else
+ outs() << format("0x%08llX ",
+ Entry.address() + BaseSegmentAddress);
+ outs() << Entry.name();
+ if (WeakDef || ThreadLocal || Resolver || Abs) {
+ ListSeparator LS;
+ outs() << " [";
+ if (WeakDef)
+ outs() << LS << "weak_def";
+ if (ThreadLocal)
+ outs() << LS << "per-thread";
+ if (Abs)
+ outs() << LS << "absolute";
+ if (Resolver)
+ outs() << LS << format("resolver=0x%08llX", Entry.other());
+ outs() << "]";
+ }
+ if (ReExport) {
+ StringRef DylibName = "unknown";
+ int Ordinal = Entry.other() - 1;
+ Obj->getLibraryShortNameByIndex(Ordinal, DylibName);
+ if (Entry.otherName().empty())
+ outs() << " (from " << DylibName << ")";
+ else
+ outs() << " (" << Entry.otherName() << " from " << DylibName << ")";
+ }
+ outs() << "\n";
+ }
+ if (Err)
+ reportError(std::move(Err), Obj->getFileName());
+}
+
+//===----------------------------------------------------------------------===//
+// rebase table dumping
+//===----------------------------------------------------------------------===//
+
+static void printMachORebaseTable(object::MachOObjectFile *Obj) {
+ outs() << "segment section address type\n";
+ Error Err = Error::success();
+ for (const object::MachORebaseEntry &Entry : Obj->rebaseTable(Err)) {
+ StringRef SegmentName = Entry.segmentName();
+ StringRef SectionName = Entry.sectionName();
+ uint64_t Address = Entry.address();
+
+ // Table lines look like: __DATA __nl_symbol_ptr 0x0000F00C pointer
+ outs() << format("%-8s %-18s 0x%08" PRIX64 " %s\n",
+ SegmentName.str().c_str(), SectionName.str().c_str(),
+ Address, Entry.typeName().str().c_str());
+ }
+ if (Err)
+ reportError(std::move(Err), Obj->getFileName());
+}
+
+static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) {
+ StringRef DylibName;
+ switch (Ordinal) {
+ case MachO::BIND_SPECIAL_DYLIB_SELF:
+ return "this-image";
+ case MachO::BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
+ return "main-executable";
+ case MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
+ return "flat-namespace";
+ default:
+ if (Ordinal > 0) {
+ std::error_code EC =
+ Obj->getLibraryShortNameByIndex(Ordinal - 1, DylibName);
+ if (EC)
+ return "<<bad library ordinal>>";
+ return DylibName;
+ }
+ }
+ return "<<unknown special ordinal>>";
+}
+
+//===----------------------------------------------------------------------===//
+// bind table dumping
+//===----------------------------------------------------------------------===//
+
+static void printMachOBindTable(object::MachOObjectFile *Obj) {
+ // Build table of sections so names can used in final output.
+ outs() << "segment section address type "
+ "addend dylib symbol\n";
+ Error Err = Error::success();
+ for (const object::MachOBindEntry &Entry : Obj->bindTable(Err)) {
+ StringRef SegmentName = Entry.segmentName();
+ StringRef SectionName = Entry.sectionName();
+ uint64_t Address = Entry.address();
+
+ // Table lines look like:
+ // __DATA __got 0x00012010 pointer 0 libSystem ___stack_chk_guard
+ StringRef Attr;
+ if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_WEAK_IMPORT)
+ Attr = " (weak_import)";
+ outs() << left_justify(SegmentName, 8) << " "
+ << left_justify(SectionName, 18) << " "
+ << format_hex(Address, 10, true) << " "
+ << left_justify(Entry.typeName(), 8) << " "
+ << format_decimal(Entry.addend(), 8) << " "
+ << left_justify(ordinalName(Obj, Entry.ordinal()), 16) << " "
+ << Entry.symbolName() << Attr << "\n";
+ }
+ if (Err)
+ reportError(std::move(Err), Obj->getFileName());
+}
+
+//===----------------------------------------------------------------------===//
+// lazy bind table dumping
+//===----------------------------------------------------------------------===//
+
+static void printMachOLazyBindTable(object::MachOObjectFile *Obj) {
+ outs() << "segment section address "
+ "dylib symbol\n";
+ Error Err = Error::success();
+ for (const object::MachOBindEntry &Entry : Obj->lazyBindTable(Err)) {
+ StringRef SegmentName = Entry.segmentName();
+ StringRef SectionName = Entry.sectionName();
+ uint64_t Address = Entry.address();
+
+ // Table lines look like:
+ // __DATA __got 0x00012010 libSystem ___stack_chk_guard
+ outs() << left_justify(SegmentName, 8) << " "
+ << left_justify(SectionName, 18) << " "
+ << format_hex(Address, 10, true) << " "
+ << left_justify(ordinalName(Obj, Entry.ordinal()), 16) << " "
+ << Entry.symbolName() << "\n";
+ }
+ if (Err)
+ reportError(std::move(Err), Obj->getFileName());
+}
+
+//===----------------------------------------------------------------------===//
+// weak bind table dumping
+//===----------------------------------------------------------------------===//
+
+static void printMachOWeakBindTable(object::MachOObjectFile *Obj) {
+ outs() << "segment section address "
+ "type addend symbol\n";
+ Error Err = Error::success();
+ for (const object::MachOBindEntry &Entry : Obj->weakBindTable(Err)) {
+ // Strong symbols don't have a location to update.
+ if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) {
+ outs() << " strong "
+ << Entry.symbolName() << "\n";
+ continue;
+ }
+ StringRef SegmentName = Entry.segmentName();
+ StringRef SectionName = Entry.sectionName();
+ uint64_t Address = Entry.address();
+
+ // Table lines look like:
+ // __DATA __data 0x00001000 pointer 0 _foo
+ outs() << left_justify(SegmentName, 8) << " "
+ << left_justify(SectionName, 18) << " "
+ << format_hex(Address, 10, true) << " "
+ << left_justify(Entry.typeName(), 8) << " "
+ << format_decimal(Entry.addend(), 8) << " " << Entry.symbolName()
+ << "\n";
+ }
+ if (Err)
+ reportError(std::move(Err), Obj->getFileName());
+}
+
+// get_dyld_bind_info_symbolname() is used for disassembly and passed an
+// address, ReferenceValue, in the Mach-O file and looks in the dyld bind
+// information for that address. If the address is found its binding symbol
+// name is returned. If not nullptr is returned.
+static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue,
+ struct DisassembleInfo *info) {
+ if (info->bindtable == nullptr) {
+ info->bindtable = std::make_unique<SymbolAddressMap>();
+ Error Err = Error::success();
+ for (const object::MachOBindEntry &Entry : info->O->bindTable(Err)) {
+ uint64_t Address = Entry.address();
+ StringRef name = Entry.symbolName();
+ if (!name.empty())
+ (*info->bindtable)[Address] = name;
+ }
+ if (Err)
+ reportError(std::move(Err), info->O->getFileName());
+ }
+ auto name = info->bindtable->lookup(ReferenceValue);
+ return !name.empty() ? name.data() : nullptr;
+}
+
+void objdump::printLazyBindTable(ObjectFile *o) {
+ outs() << "\nLazy bind table:\n";
+ if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
+ printMachOLazyBindTable(MachO);
+ else
+ WithColor::error()
+ << "This operation is only currently supported "
+ "for Mach-O executable files.\n";
+}
+
+void objdump::printWeakBindTable(ObjectFile *o) {
+ outs() << "\nWeak bind table:\n";
+ if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
+ printMachOWeakBindTable(MachO);
+ else
+ WithColor::error()
+ << "This operation is only currently supported "
+ "for Mach-O executable files.\n";
+}
+
+void objdump::printExportsTrie(const ObjectFile *o) {
+ outs() << "\nExports trie:\n";
+ if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
+ printMachOExportsTrie(MachO);
+ else
+ WithColor::error()
+ << "This operation is only currently supported "
+ "for Mach-O executable files.\n";
+}
+
+void objdump::printRebaseTable(ObjectFile *o) {
+ outs() << "\nRebase table:\n";
+ if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
+ printMachORebaseTable(MachO);
+ else
+ WithColor::error()
+ << "This operation is only currently supported "
+ "for Mach-O executable files.\n";
+}
+
+void objdump::printBindTable(ObjectFile *o) {
+ outs() << "\nBind table:\n";
+ if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o))
+ printMachOBindTable(MachO);
+ else
+ WithColor::error()
+ << "This operation is only currently supported "
+ "for Mach-O executable files.\n";
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/MachODump.h b/contrib/libs/llvm14/tools/llvm-objdump/MachODump.h
new file mode 100644
index 00000000000..7568062bd6b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/MachODump.h
@@ -0,0 +1,78 @@
+//===-- MachODump.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJDUMP_MACHODUMP_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_MACHODUMP_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/CommandLine.h"
+
+namespace llvm {
+
+class Error;
+class StringRef;
+
+namespace object {
+class MachOObjectFile;
+class MachOUniversalBinary;
+class ObjectFile;
+class RelocationRef;
+} // namespace object
+
+namespace opt {
+class InputArgList;
+} // namespace opt
+
+namespace objdump {
+
+void parseMachOOptions(const llvm::opt::InputArgList &InputArgs);
+
+// MachO specific options
+extern bool Bind;
+extern bool DataInCode;
+extern std::string DisSymName;
+extern bool DylibId;
+extern bool DylibsUsed;
+extern bool ExportsTrie;
+extern bool FirstPrivateHeader;
+extern bool FullLeadingAddr;
+extern bool FunctionStarts;
+extern bool IndirectSymbols;
+extern bool InfoPlist;
+extern bool LazyBind;
+extern bool LeadingHeaders;
+extern bool LinkOptHints;
+extern bool ObjcMetaData;
+extern bool Rebase;
+extern bool Rpaths;
+extern bool SymbolicOperands;
+extern bool UniversalHeaders;
+extern bool Verbose;
+extern bool WeakBind;
+
+Error getMachORelocationValueString(const object::MachOObjectFile *Obj,
+ const object::RelocationRef &RelRef,
+ llvm::SmallVectorImpl<char> &Result);
+
+void parseInputMachO(StringRef Filename);
+void parseInputMachO(object::MachOUniversalBinary *UB);
+
+void printMachOUnwindInfo(const object::MachOObjectFile *O);
+void printMachOFileHeader(const object::ObjectFile *O);
+void printMachOLoadCommands(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);
+
+} // namespace objdump
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/ObjdumpOptID.h b/contrib/libs/llvm14/tools/llvm-objdump/ObjdumpOptID.h
new file mode 100644
index 00000000000..65f6c60ad88
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/ObjdumpOptID.h
@@ -0,0 +1,13 @@
+#ifndef LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H
+
+enum ObjdumpOptID {
+ OBJDUMP_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OBJDUMP_##ID,
+#include "ObjdumpOpts.inc"
+#undef OPTION
+};
+
+#endif // LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/ObjdumpOpts.td b/contrib/libs/llvm14/tools/llvm-objdump/ObjdumpOpts.td
new file mode 100644
index 00000000000..9f27a6cdf16
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/ObjdumpOpts.td
@@ -0,0 +1,337 @@
+include "llvm/Option/OptParser.td"
+
+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">,
+ HelpText<"Display available options (--help-hidden for more)">;
+
+def help_hidden : Flag<["--"], "help-hidden">,
+ Flags<[HelpHidden]>,
+ HelpText<"Display all available options">;
+
+def version : Flag<["--"], "version">,
+ HelpText<"Display the version of this program">;
+def : Flag<["-"], "v">, Alias<version>, HelpText<"Alias for --version">;
+
+def adjust_vma_EQ : Joined<["--"], "adjust-vma=">,
+ MetaVarName<"offset">,
+ HelpText<"Increase the displayed address by the specified offset">;
+
+def all_headers : Flag<["--"], "all-headers">,
+ HelpText<"Display all available header information, "
+ "relocation entries and the symbol table">;
+def : Flag<["-"], "x">, Alias<all_headers>, HelpText<"Alias for --all-headers">;
+
+def arch_name_EQ : Joined<["--"], "arch-name=">,
+ HelpText<"Target arch to disassemble for, "
+ "see --version for available targets">;
+def archive_headers : Flag<["--"], "archive-headers">,
+ HelpText<"Display archive header information">;
+
+def : Flag<["-"], "a">, Alias<archive_headers>,
+ HelpText<"Alias for --archive-headers">;
+
+def demangle : Flag<["--"], "demangle">, HelpText<"Demangle symbol names">;
+def : Flag<["-"], "C">, Alias<demangle>, HelpText<"Alias for --demangle">;
+
+def disassemble : Flag<["--"], "disassemble">,
+ HelpText<"Disassemble all executable sections found in the input files">;
+def : Flag<["-"], "d">, Alias<disassemble>, HelpText<"Alias for --disassemble">;
+
+def disassemble_all : Flag<["--"], "disassemble-all">,
+ HelpText<"Disassemble all sections found in the input files">;
+def : Flag<["-"], "D">, Alias<disassemble_all>,
+ HelpText<"Alias for --disassemble-all">;
+
+def symbol_description : Flag<["--"], "symbol-description">,
+ HelpText<"Add symbol description for disassembly. This "
+ "option is for XCOFF files only.">;
+
+def disassemble_symbols_EQ : Joined<["--"], "disassemble-symbols=">,
+ HelpText<"List of symbols to disassemble. "
+ "Accept demangled names when --demangle is "
+ "specified, otherwise accept mangled names">;
+
+def disassemble_zeroes : Flag<["--"], "disassemble-zeroes">,
+ HelpText<"Do not skip blocks of zeroes when disassembling">;
+def : Flag<["-"], "z">, Alias<disassemble_zeroes>,
+ HelpText<"Alias for --disassemble-zeroes">;
+
+def disassembler_options_EQ : Joined<["--"], "disassembler-options=">,
+ MetaVarName<"options">,
+ HelpText<"Pass target specific disassembler options">;
+def : JoinedOrSeparate<["-"], "M">, Alias<disassembler_options_EQ>,
+ HelpText<"Alias for --disassembler-options=">;
+
+def dynamic_reloc : Flag<["--"], "dynamic-reloc">,
+ HelpText<"Display the dynamic relocation entries in the file">;
+def : Flag<["-"], "R">, Alias<dynamic_reloc>,
+ HelpText<"Alias for --dynamic-reloc">;
+
+def dwarf_EQ : Joined<["--"], "dwarf=">,
+ HelpText<"Dump the specified DWARF debug sections. The "
+ "only supported value is 'frames'">,
+ Values<"frames">;
+
+def fault_map_section : Flag<["--"], "fault-map-section">,
+ HelpText<"Display the content of the fault map section">;
+
+def file_headers : Flag<["--"], "file-headers">,
+ HelpText<"Display the contents of the overall file header">;
+def : Flag<["-"], "f">, Alias<file_headers>,
+ HelpText<"Alias for --file-headers">;
+
+def full_contents : Flag<["--"], "full-contents">,
+ HelpText<"Display the content of each section">;
+def : Flag<["-"], "s">, Alias<full_contents>,
+ HelpText<"Alias for --full-contents">;
+
+def line_numbers : Flag<["--"], "line-numbers">,
+ HelpText<"When disassembling, display source line numbers. "
+ "Implies --disassemble">;
+def : Flag<["-"], "l">,
+ Alias<line_numbers>,
+ HelpText<"Alias for --line-numbers">;
+
+def macho : Flag<["--"], "macho">,
+ HelpText<"Use MachO specific object file parser">;
+def : Flag<["-"], "m">, Alias<macho>, HelpText<"Alias for --macho">;
+
+def mcpu_EQ : Joined<["--"], "mcpu=">,
+ MetaVarName<"cpu-name">,
+ HelpText<"Target a specific cpu type (--mcpu=help for details)">;
+
+def mattr_EQ : Joined<["--"], "mattr=">,
+ MetaVarName<"a1,+a2,-a3,...">,
+ HelpText<"Target specific attributes (--mattr=help for details)">;
+
+def no_show_raw_insn : Flag<["--"], "no-show-raw-insn">,
+ HelpText<"When disassembling instructions, "
+ "do not print the instruction bytes.">;
+
+def no_leading_addr : Flag<["--"], "no-leading-addr">,
+ HelpText<"When disassembling, do not print leading addresses">;
+
+def raw_clang_ast : Flag<["--"], "raw-clang-ast">,
+ HelpText<"Dump the raw binary contents of the clang AST section">;
+
+def reloc : Flag<["--"], "reloc">,
+ HelpText<"Display the relocation entries in the file">;
+def : Flag<["-"], "r">, Alias<reloc>, HelpText<"Alias for --reloc">;
+
+def print_imm_hex : Flag<["--"], "print-imm-hex">,
+ HelpText<"Use hex format for immediate values">;
+
+def no_print_imm_hex : Flag<["--"], "no-print-imm-hex">,
+ HelpText<"Do not use hex format for immediate values (default)">;
+def : Flag<["--"], "print-imm-hex=false">, Alias<no_print_imm_hex>;
+
+def private_headers : Flag<["--"], "private-headers">,
+ HelpText<"Display format specific file headers">;
+def : Flag<["-"], "p">, Alias<private_headers>,
+ HelpText<"Alias for --private-headers">;
+
+def section_EQ : Joined<["--"], "section=">,
+ HelpText<"Operate on the specified sections only. "
+ "With --macho dump segment,section">;
+def : Separate<["--"], "section">, Alias<section_EQ>;
+def : JoinedOrSeparate<["-"], "j">, Alias<section_EQ>,
+ HelpText<"Alias for --section">;
+
+def section_headers : Flag<["--"], "section-headers">,
+ HelpText<"Display summaries of the headers for each section.">;
+def : Flag<["--"], "headers">, Alias<section_headers>,
+ HelpText<"Alias for --section-headers">;
+def : Flag<["-"], "h">, Alias<section_headers>,
+ HelpText<"Alias for --section-headers">;
+
+def show_lma : Flag<["--"], "show-lma">,
+ HelpText<"Display LMA column when dumping ELF section headers">;
+
+def source : Flag<["--"], "source">,
+ HelpText<"When disassembling, display source interleaved with the "
+ "disassembly. Implies --disassemble">;
+def : Flag<["-"], "S">, Alias<source>, HelpText<"Alias for --source">;
+
+def start_address_EQ : Joined<["--"], "start-address=">,
+ MetaVarName<"address">,
+ HelpText<"Set the start address for disassembling, "
+ "printing relocations and printing symbols">;
+def stop_address_EQ : Joined<["--"], "stop-address=">,
+ MetaVarName<"address">,
+ HelpText<"Set the stop address for disassembling, "
+ "printing relocations and printing symbols">;
+
+def syms : Flag<["--"], "syms">,
+ HelpText<"Display the symbol table">;
+def : Flag<["-"], "t">, Alias<syms>, HelpText<"Alias for --syms">;
+
+def symbolize_operands : Flag<["--"], "symbolize-operands">,
+ HelpText<"Symbolize instruction operands when disassembling">;
+
+def dynamic_syms : Flag<["--"], "dynamic-syms">,
+ HelpText<"Display the contents of the dynamic symbol table">;
+def : Flag<["-"], "T">, Alias<dynamic_syms>,
+ HelpText<"Alias for --dynamic-syms">;
+
+def triple_EQ : Joined<["--"], "triple=">,
+ HelpText<"Target triple to disassemble for, "
+ "see --version for available targets">;
+def : Separate<["--"], "triple">,
+ Alias<triple_EQ>;
+
+def unwind_info : Flag<["--"], "unwind-info">,
+ HelpText<"Display unwind information">;
+def : Flag<["-"], "u">, Alias<unwind_info>,
+ HelpText<"Alias for --unwind-info">;
+
+def wide : Flag<["--"], "wide">,
+ HelpText<"Ignored for compatibility with GNU objdump">;
+def : Flag<["-"], "w">, Alias<wide>;
+
+defm prefix : Eq<"prefix", "Add prefix to absolute paths">,
+ MetaVarName<"prefix">;
+defm prefix_strip
+ : Eq<"prefix-strip", "Strip out initial directories from absolute "
+ "paths. No effect without --prefix">,
+ MetaVarName<"prefix">;
+
+def debug_vars_EQ : Joined<["--"], "debug-vars=">,
+ HelpText<"Print the locations (in registers or memory) of "
+ "source-level variables alongside disassembly. "
+ "Supported formats: ascii, unicode (default)">,
+ Values<"unicode,ascii">;
+def : Flag<["--"], "debug-vars">, Alias<debug_vars_EQ>, AliasArgs<["unicode"]>;
+
+def debug_vars_indent_EQ : Joined<["--"], "debug-vars-indent=">,
+ HelpText<"Distance to indent the source-level variable display, "
+ "relative to the start of the disassembly">;
+
+def x86_asm_syntax_att : Flag<["--"], "x86-asm-syntax=att">,
+ HelpText<"Emit AT&T-style disassembly">;
+
+def x86_asm_syntax_intel : Flag<["--"], "x86-asm-syntax=intel">,
+ HelpText<"Emit Intel-style disassembly">;
+
+
+def grp_mach_o : OptionGroup<"kind">, HelpText<"llvm-objdump MachO Specific Options">;
+
+def private_header : Flag<["--"], "private-header">,
+ HelpText<"Display only the first format specific file header">,
+ Group<grp_mach_o>;
+
+def exports_trie : Flag<["--"], "exports-trie">,
+ HelpText<"Display mach-o exported symbols">,
+ Group<grp_mach_o>;
+
+def rebase : Flag<["--"], "rebase">,
+ HelpText<"Display mach-o rebasing info">,
+ Group<grp_mach_o>;
+
+def bind : Flag<["--"], "bind">,
+ HelpText<"Display mach-o binding info">,
+ Group<grp_mach_o>;
+
+def lazy_bind : Flag<["--"], "lazy-bind">,
+ HelpText<"Display mach-o lazy binding info">,
+ Group<grp_mach_o>;
+
+def weak_bind : Flag<["--"], "weak-bind">,
+ HelpText<"Display mach-o weak binding info">,
+ Group<grp_mach_o>;
+
+def g : Flag<["-"], "g">,
+ HelpText<"Print line information from debug info if available">,
+ Group<grp_mach_o>;
+
+def dsym_EQ : Joined<["--"], "dsym=">,
+ HelpText<"Use .dSYM file for debug info">,
+ Group<grp_mach_o>;
+def : Separate<["--"], "dsym">,
+ Alias<dsym_EQ>,
+ Group<grp_mach_o>;
+
+def full_leading_addr : Flag<["--"], "full-leading-addr">,
+ HelpText<"Print full leading address">,
+ Group<grp_mach_o>;
+
+def no_leading_headers : Flag<["--"], "no-leading-headers">,
+ HelpText<"Print no leading headers">,
+ Group<grp_mach_o>;
+
+def universal_headers : Flag<["--"], "universal-headers">,
+ HelpText<"Print Mach-O universal headers (requires --macho)">,
+ Group<grp_mach_o>;
+
+def archive_member_offsets : Flag<["--"], "archive-member-offsets">,
+ HelpText<"Print the offset to each archive member for Mach-O archives "
+ "(requires --macho and --archive-headers)">,
+ Group<grp_mach_o>;
+
+def indirect_symbols : Flag<["--"], "indirect-symbols">,
+ HelpText<"Print indirect symbol table for Mach-O objects (requires --macho)">,
+ Group<grp_mach_o>;
+
+def data_in_code : Flag<["--"], "data-in-code">,
+ HelpText<"Print the data in code table for Mach-O objects (requires --macho)">,
+ Group<grp_mach_o>;
+
+def function_starts : Flag<["--"], "function-starts">,
+ HelpText<"Print the function starts table for "
+ "Mach-O objects (requires --macho)">,
+ Group<grp_mach_o>;
+
+def link_opt_hints : Flag<["--"], "link-opt-hints">,
+ HelpText<"Print the linker optimization hints for "
+ "Mach-O objects (requires --macho)">,
+ Group<grp_mach_o>;
+
+def info_plist : Flag<["--"], "info-plist">,
+ HelpText<"Print the info plist section as strings for "
+ "Mach-O objects (requires --macho)">,
+ Group<grp_mach_o>;
+
+def dylibs_used : Flag<["--"], "dylibs-used">,
+ HelpText<"Print the shared libraries used for linked "
+ "Mach-O files (requires --macho)">,
+ Group<grp_mach_o>;
+
+def dylib_id : Flag<["--"], "dylib-id">,
+ HelpText<"Print the shared library's id for the "
+ "dylib Mach-O file (requires --macho)">,
+ Group<grp_mach_o>;
+
+def rpaths : Flag<["--"], "rpaths">,
+ HelpText<"Print the runtime search paths for the "
+ "Mach-O file (requires --macho)">,
+ Group<grp_mach_o>;
+
+def non_verbose : Flag<["--"], "non-verbose">,
+ HelpText<"Print the info for Mach-O objects in non-verbose or "
+ "numeric form (requires --macho)">,
+ Group<grp_mach_o>;
+
+def objc_meta_data : Flag<["--"], "objc-meta-data">,
+ HelpText<"Print the Objective-C runtime meta data "
+ "for Mach-O files (requires --macho)">,
+ Group<grp_mach_o>;
+
+def dis_symname : Separate<["--"], "dis-symname">,
+ HelpText<"disassemble just this symbol's instructions (requires --macho)">,
+ Group<grp_mach_o>;
+
+def no_symbolic_operands : Flag<["--"], "no-symbolic-operands">,
+ HelpText<"do not symbolic operands when disassembling (requires --macho)">,
+ Group<grp_mach_o>;
+
+def arch_EQ : Joined<["--"], "arch=">,
+ HelpText<"architecture(s) from a Mach-O file to dump">,
+ Group<grp_mach_o>;
+def : Separate<["--"], "arch">,
+ Alias<arch_EQ>,
+ Group<grp_mach_o>;
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/OtoolOpts.td b/contrib/libs/llvm14/tools/llvm-objdump/OtoolOpts.td
new file mode 100644
index 00000000000..61ea701ed75
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/OtoolOpts.td
@@ -0,0 +1,68 @@
+include "llvm/Option/OptParser.td"
+
+def help : Flag<["--"], "help">, HelpText<"print help">;
+def help_hidden : Flag<["--"], "help-hidden">,
+ HelpText<"print help for hidden flags">;
+
+def arch : Separate<["-"], "arch">,
+ HelpText<"select slice of universal Mach-O file">;
+def C : Flag<["-"], "C">, HelpText<"print linker optimization hints">;
+def d : Flag<["-"], "d">, HelpText<"print data section">;
+def D : Flag<["-"], "D">, HelpText<"print shared library id">;
+def f : Flag<["-"], "f">, HelpText<"print universal headers">;
+def G : Flag<["-"], "G">, HelpText<"print data-in-code table">;
+def h : Flag<["-"], "h">, HelpText<"print mach header">;
+def I : Flag<["-"], "I">, HelpText<"print indirect symbol table">;
+def j : Flag<["-"], "j">, HelpText<"print opcode bytes">;
+def l : Flag<["-"], "l">, HelpText<"print load commnads">;
+def L : Flag<["-"], "L">, HelpText<"print used shared libraries">;
+def mcpu_EQ : Joined<["-"], "mcpu=">, HelpText<"select cpu for disassembly">;
+def o : Flag<["-"], "o">, HelpText<"print Objective-C segment">;
+def p : Separate<["-"], "p">,
+ MetaVarName<"<function name>">,
+ HelpText<"start disassembly at <function name>">;
+def P : Flag<["-"], "P">, HelpText<"print __TEXT,__info_plist section as strings">;
+def : Flag<["-"], "q">, Flags<[HelpHidden]>,
+ HelpText<"use LLVM's disassembler (default)">;
+def r : Flag<["-"], "r">, HelpText<"print relocation entries">;
+def s : MultiArg<["-"], "s", 2>,
+ MetaVarName<"<segname> <sectname>">,
+ HelpText<"print contents of section">;
+def t : Flag<["-"], "t">, HelpText<"print text section">;
+def version : Flag<["--"], "version">, HelpText<"print version">;
+def v : Flag<["-"], "v">,
+ HelpText<"verbose output / disassemble when printing text sections">;
+def V : Flag<["-"], "V">,
+ HelpText<"symbolize disassembled operands (implies -v)">;
+def x : Flag<["-"], "x">, HelpText<"print all text sections">;
+def X : Flag<["-"], "X">, HelpText<"omit leading addresses or headers">;
+
+// Not (yet?) implemented:
+// def a : Flag<["-"], "a">, HelpText<"print archive header">;
+// -c print argument strings of a core file
+// -m don't use archive(member) syntax
+// -dyld_info
+// -dyld_opcodes
+// -chained_fixups
+// -addr_slide=arg
+// -function_offsets
+
+
+// Obsolete and unsupported:
+def grp_obsolete : OptionGroup<"kind">,
+ HelpText<"Obsolete and unsupported flags">;
+
+def : Flag<["-"], "B">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"force Thum disassembly (ARM 32-bit objects only)">;
+def : Flag<["-"], "H">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"print two-level hints table">;
+def : Flag<["-"], "M">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"print module table of shared library">;
+def : Flag<["-"], "R">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"print reference table of shared library">;
+def : Flag<["-"], "S">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"print table of contents of library">;
+def : Flag<["-"], "T">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"print table of contents of shared library">;
+def : Flag<["-"], "Q">, Flags<[HelpHidden]>, Group<grp_obsolete>,
+ HelpText<"llvm-otool cannot use otool-classic's disassembler">;
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/SourcePrinter.cpp b/contrib/libs/llvm14/tools/llvm-objdump/SourcePrinter.cpp
new file mode 100644
index 00000000000..8befac54620
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/SourcePrinter.cpp
@@ -0,0 +1,483 @@
+//===-- SourcePrinter.cpp - source interleaving utilities ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the LiveVariablePrinter and SourcePrinter classes to
+// keep track of DWARF info as the current address is updated, and print out the
+// source file line and variable liveness as needed.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SourcePrinter.h"
+#include "llvm-objdump.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/FormatVariadic.h"
+
+#define DEBUG_TYPE "objdump"
+
+namespace llvm {
+namespace objdump {
+
+unsigned getInstStartColumn(const MCSubtargetInfo &STI) {
+ return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24;
+}
+
+bool LiveVariable::liveAtAddress(object::SectionedAddress Addr) {
+ if (LocExpr.Range == None)
+ return false;
+ return LocExpr.Range->SectionIndex == Addr.SectionIndex &&
+ LocExpr.Range->LowPC <= Addr.Address &&
+ LocExpr.Range->HighPC > Addr.Address;
+}
+
+void LiveVariable::print(raw_ostream &OS, const MCRegisterInfo &MRI) const {
+ DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()},
+ Unit->getContext().isLittleEndian(), 0);
+ DWARFExpression Expression(Data, Unit->getAddressByteSize());
+ Expression.printCompact(OS, MRI);
+}
+
+void LiveVariablePrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) {
+ uint64_t FuncLowPC, FuncHighPC, SectionIndex;
+ FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex);
+ const char *VarName = VarDie.getName(DINameKind::ShortName);
+ DWARFUnit *U = VarDie.getDwarfUnit();
+
+ Expected<DWARFLocationExpressionsVector> Locs =
+ VarDie.getLocations(dwarf::DW_AT_location);
+ if (!Locs) {
+ // If the variable doesn't have any locations, just ignore it. We don't
+ // report an error or warning here as that could be noisy on optimised
+ // code.
+ consumeError(Locs.takeError());
+ return;
+ }
+
+ for (const DWARFLocationExpression &LocExpr : *Locs) {
+ if (LocExpr.Range) {
+ LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie);
+ } else {
+ // If the LocExpr does not have an associated range, it is valid for
+ // the whole of the function.
+ // TODO: technically it is not valid for any range covered by another
+ // LocExpr, does that happen in reality?
+ DWARFLocationExpression WholeFuncExpr{
+ DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex), LocExpr.Expr};
+ LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie);
+ }
+ }
+}
+
+void LiveVariablePrinter::addFunction(DWARFDie D) {
+ for (const DWARFDie &Child : D.children()) {
+ if (Child.getTag() == dwarf::DW_TAG_variable ||
+ Child.getTag() == dwarf::DW_TAG_formal_parameter)
+ addVariable(D, Child);
+ else
+ addFunction(Child);
+ }
+}
+
+// Get the column number (in characters) at which the first live variable
+// line should be printed.
+unsigned LiveVariablePrinter::getIndentLevel() const {
+ return DbgIndent + getInstStartColumn(STI);
+}
+
+// Indent to the first live-range column to the right of the currently
+// printed line, and return the index of that column.
+// TODO: formatted_raw_ostream uses "column" to mean a number of characters
+// since the last \n, and we use it to mean the number of slots in which we
+// put live variable lines. Pick a less overloaded word.
+unsigned LiveVariablePrinter::moveToFirstVarColumn(formatted_raw_ostream &OS) {
+ // Logical column number: column zero is the first column we print in, each
+ // logical column is 2 physical columns wide.
+ unsigned FirstUnprintedLogicalColumn =
+ std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0);
+ // Physical column number: the actual column number in characters, with
+ // zero being the left-most side of the screen.
+ unsigned FirstUnprintedPhysicalColumn =
+ getIndentLevel() + FirstUnprintedLogicalColumn * 2;
+
+ if (FirstUnprintedPhysicalColumn > OS.getColumn())
+ OS.PadToColumn(FirstUnprintedPhysicalColumn);
+
+ return FirstUnprintedLogicalColumn;
+}
+
+unsigned LiveVariablePrinter::findFreeColumn() {
+ for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx)
+ if (!ActiveCols[ColIdx].isActive())
+ return ColIdx;
+
+ size_t OldSize = ActiveCols.size();
+ ActiveCols.grow(std::max<size_t>(OldSize * 2, 1));
+ return OldSize;
+}
+
+void LiveVariablePrinter::dump() const {
+ for (const LiveVariable &LV : LiveVariables) {
+ dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": ";
+ LV.print(dbgs(), MRI);
+ dbgs() << "\n";
+ }
+}
+
+void LiveVariablePrinter::addCompileUnit(DWARFDie D) {
+ if (D.getTag() == dwarf::DW_TAG_subprogram)
+ addFunction(D);
+ else
+ for (const DWARFDie &Child : D.children())
+ addFunction(Child);
+}
+
+/// Update to match the state of the instruction between ThisAddr and
+/// NextAddr. In the common case, any live range active at ThisAddr is
+/// live-in to the instruction, and any live range active at NextAddr is
+/// live-out of the instruction. If IncludeDefinedVars is false, then live
+/// ranges starting at NextAddr will be ignored.
+void LiveVariablePrinter::update(object::SectionedAddress ThisAddr,
+ object::SectionedAddress NextAddr,
+ bool IncludeDefinedVars) {
+ // First, check variables which have already been assigned a column, so
+ // that we don't change their order.
+ SmallSet<unsigned, 8> CheckedVarIdxs;
+ for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
+ if (!ActiveCols[ColIdx].isActive())
+ continue;
+ CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx);
+ LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx];
+ ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr);
+ ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr);
+ LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-"
+ << NextAddr.Address << ", " << LV.VarName << ", Col "
+ << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn
+ << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n");
+
+ if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
+ ActiveCols[ColIdx].VarIdx = Column::NullVarIdx;
+ }
+
+ // Next, look for variables which don't already have a column, but which
+ // are now live.
+ if (IncludeDefinedVars) {
+ for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End;
+ ++VarIdx) {
+ if (CheckedVarIdxs.count(VarIdx))
+ continue;
+ LiveVariable &LV = LiveVariables[VarIdx];
+ bool LiveIn = LV.liveAtAddress(ThisAddr);
+ bool LiveOut = LV.liveAtAddress(NextAddr);
+ if (!LiveIn && !LiveOut)
+ continue;
+
+ unsigned ColIdx = findFreeColumn();
+ LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-"
+ << NextAddr.Address << ", " << LV.VarName << ", Col "
+ << ColIdx << ": LiveIn=" << LiveIn
+ << ", LiveOut=" << LiveOut << "\n");
+ ActiveCols[ColIdx].VarIdx = VarIdx;
+ ActiveCols[ColIdx].LiveIn = LiveIn;
+ ActiveCols[ColIdx].LiveOut = LiveOut;
+ ActiveCols[ColIdx].MustDrawLabel = true;
+ }
+ }
+}
+
+enum class LineChar {
+ RangeStart,
+ RangeMid,
+ RangeEnd,
+ LabelVert,
+ LabelCornerNew,
+ LabelCornerActive,
+ LabelHoriz,
+};
+const char *LiveVariablePrinter::getLineChar(LineChar C) const {
+ bool IsASCII = DbgVariables == DVASCII;
+ switch (C) {
+ case LineChar::RangeStart:
+ return IsASCII ? "^" : (const char *)u8"\u2548";
+ case LineChar::RangeMid:
+ return IsASCII ? "|" : (const char *)u8"\u2503";
+ case LineChar::RangeEnd:
+ return IsASCII ? "v" : (const char *)u8"\u253b";
+ case LineChar::LabelVert:
+ return IsASCII ? "|" : (const char *)u8"\u2502";
+ case LineChar::LabelCornerNew:
+ return IsASCII ? "/" : (const char *)u8"\u250c";
+ case LineChar::LabelCornerActive:
+ return IsASCII ? "|" : (const char *)u8"\u2520";
+ case LineChar::LabelHoriz:
+ return IsASCII ? "-" : (const char *)u8"\u2500";
+ }
+ llvm_unreachable("Unhandled LineChar enum");
+}
+
+/// Print live ranges to the right of an existing line. This assumes the
+/// line is not an instruction, so doesn't start or end any live ranges, so
+/// we only need to print active ranges or empty columns. If AfterInst is
+/// true, this is being printed after the last instruction fed to update(),
+/// otherwise this is being printed before it.
+void LiveVariablePrinter::printAfterOtherLine(formatted_raw_ostream &OS,
+ bool AfterInst) {
+ if (ActiveCols.size()) {
+ unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
+ for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
+ ColIdx < End; ++ColIdx) {
+ if (ActiveCols[ColIdx].isActive()) {
+ if ((AfterInst && ActiveCols[ColIdx].LiveOut) ||
+ (!AfterInst && ActiveCols[ColIdx].LiveIn))
+ OS << getLineChar(LineChar::RangeMid);
+ else if (!AfterInst && ActiveCols[ColIdx].LiveOut)
+ OS << getLineChar(LineChar::LabelVert);
+ else
+ OS << " ";
+ }
+ OS << " ";
+ }
+ }
+ OS << "\n";
+}
+
+/// Print any live variable range info needed to the right of a
+/// non-instruction line of disassembly. This is where we print the variable
+/// names and expressions, with thin line-drawing characters connecting them
+/// to the live range which starts at the next instruction. If MustPrint is
+/// true, we have to print at least one line (with the continuation of any
+/// already-active live ranges) because something has already been printed
+/// earlier on this line.
+void LiveVariablePrinter::printBetweenInsts(formatted_raw_ostream &OS,
+ bool MustPrint) {
+ bool PrintedSomething = false;
+ for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
+ if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) {
+ // First we need to print the live range markers for any active
+ // columns to the left of this one.
+ OS.PadToColumn(getIndentLevel());
+ for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) {
+ if (ActiveCols[ColIdx2].isActive()) {
+ if (ActiveCols[ColIdx2].MustDrawLabel && !ActiveCols[ColIdx2].LiveIn)
+ OS << getLineChar(LineChar::LabelVert) << " ";
+ else
+ OS << getLineChar(LineChar::RangeMid) << " ";
+ } else
+ OS << " ";
+ }
+
+ // Then print the variable name and location of the new live range,
+ // with box drawing characters joining it to the live range line.
+ OS << getLineChar(ActiveCols[ColIdx].LiveIn ? LineChar::LabelCornerActive
+ : LineChar::LabelCornerNew)
+ << getLineChar(LineChar::LabelHoriz) << " ";
+ WithColor(OS, raw_ostream::GREEN)
+ << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName;
+ OS << " = ";
+ {
+ WithColor ExprColor(OS, raw_ostream::CYAN);
+ LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI);
+ }
+
+ // If there are any columns to the right of the expression we just
+ // printed, then continue their live range lines.
+ unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
+ for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size();
+ ColIdx2 < End; ++ColIdx2) {
+ if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn)
+ OS << getLineChar(LineChar::RangeMid) << " ";
+ else
+ OS << " ";
+ }
+
+ OS << "\n";
+ PrintedSomething = true;
+ }
+ }
+
+ for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx)
+ if (ActiveCols[ColIdx].isActive())
+ ActiveCols[ColIdx].MustDrawLabel = false;
+
+ // If we must print something (because we printed a line/column number),
+ // but don't have any new variables to print, then print a line which
+ // just continues any existing live ranges.
+ if (MustPrint && !PrintedSomething)
+ printAfterOtherLine(OS, false);
+}
+
+/// Print the live variable ranges to the right of a disassembled instruction.
+void LiveVariablePrinter::printAfterInst(formatted_raw_ostream &OS) {
+ if (!ActiveCols.size())
+ return;
+ unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
+ for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
+ ColIdx < End; ++ColIdx) {
+ if (!ActiveCols[ColIdx].isActive())
+ OS << " ";
+ else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut)
+ OS << getLineChar(LineChar::RangeMid) << " ";
+ else if (ActiveCols[ColIdx].LiveOut)
+ OS << getLineChar(LineChar::RangeStart) << " ";
+ else if (ActiveCols[ColIdx].LiveIn)
+ OS << getLineChar(LineChar::RangeEnd) << " ";
+ else
+ llvm_unreachable("var must be live in or out!");
+ }
+}
+
+bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) {
+ std::unique_ptr<MemoryBuffer> Buffer;
+ if (LineInfo.Source) {
+ Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source);
+ } else {
+ auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName);
+ if (!BufferOrError) {
+ if (MissingSources.insert(LineInfo.FileName).second)
+ reportWarning("failed to find source " + LineInfo.FileName,
+ Obj->getFileName());
+ return false;
+ }
+ Buffer = std::move(*BufferOrError);
+ }
+ // Chomp the file to get lines
+ const char *BufferStart = Buffer->getBufferStart(),
+ *BufferEnd = Buffer->getBufferEnd();
+ std::vector<StringRef> &Lines = LineCache[LineInfo.FileName];
+ const char *Start = BufferStart;
+ for (const char *I = BufferStart; I != BufferEnd; ++I)
+ if (*I == '\n') {
+ Lines.emplace_back(Start, I - Start - (BufferStart < I && I[-1] == '\r'));
+ Start = I + 1;
+ }
+ if (Start < BufferEnd)
+ Lines.emplace_back(Start, BufferEnd - Start);
+ SourceCache[LineInfo.FileName] = std::move(Buffer);
+ return true;
+}
+
+void SourcePrinter::printSourceLine(formatted_raw_ostream &OS,
+ object::SectionedAddress Address,
+ StringRef ObjectFilename,
+ LiveVariablePrinter &LVP,
+ StringRef Delimiter) {
+ if (!Symbolizer)
+ return;
+
+ DILineInfo LineInfo = DILineInfo();
+ Expected<DILineInfo> ExpectedLineInfo =
+ Symbolizer->symbolizeCode(*Obj, Address);
+ std::string ErrorMessage;
+ if (ExpectedLineInfo) {
+ LineInfo = *ExpectedLineInfo;
+ } else if (!WarnedInvalidDebugInfo) {
+ WarnedInvalidDebugInfo = true;
+ // TODO Untested.
+ reportWarning("failed to parse debug information: " +
+ toString(ExpectedLineInfo.takeError()),
+ ObjectFilename);
+ }
+
+ if (!objdump::Prefix.empty() &&
+ sys::path::is_absolute_gnu(LineInfo.FileName)) {
+ // FileName has at least one character since is_absolute_gnu is false for
+ // an empty string.
+ assert(!LineInfo.FileName.empty());
+ if (PrefixStrip > 0) {
+ uint32_t Level = 0;
+ auto StrippedNameStart = LineInfo.FileName.begin();
+
+ // Path.h iterator skips extra separators. Therefore it cannot be used
+ // here to keep compatibility with GNU Objdump.
+ for (auto Pos = StrippedNameStart + 1, End = LineInfo.FileName.end();
+ Pos != End && Level < PrefixStrip; ++Pos) {
+ if (sys::path::is_separator(*Pos)) {
+ StrippedNameStart = Pos;
+ ++Level;
+ }
+ }
+
+ LineInfo.FileName =
+ std::string(StrippedNameStart, LineInfo.FileName.end());
+ }
+
+ SmallString<128> FilePath;
+ sys::path::append(FilePath, Prefix, LineInfo.FileName);
+
+ LineInfo.FileName = std::string(FilePath);
+ }
+
+ if (PrintLines)
+ printLines(OS, LineInfo, Delimiter, LVP);
+ if (PrintSource)
+ printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP);
+ OldLineInfo = LineInfo;
+}
+
+void SourcePrinter::printLines(formatted_raw_ostream &OS,
+ const DILineInfo &LineInfo, StringRef Delimiter,
+ LiveVariablePrinter &LVP) {
+ bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString &&
+ LineInfo.FunctionName != OldLineInfo.FunctionName;
+ if (PrintFunctionName) {
+ OS << Delimiter << LineInfo.FunctionName;
+ // If demangling is successful, FunctionName will end with "()". Print it
+ // only if demangling did not run or was unsuccessful.
+ if (!StringRef(LineInfo.FunctionName).endswith("()"))
+ OS << "()";
+ OS << ":\n";
+ }
+ if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 &&
+ (OldLineInfo.Line != LineInfo.Line ||
+ OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) {
+ OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line;
+ LVP.printBetweenInsts(OS, true);
+ }
+}
+
+void SourcePrinter::printSources(formatted_raw_ostream &OS,
+ const DILineInfo &LineInfo,
+ StringRef ObjectFilename, StringRef Delimiter,
+ LiveVariablePrinter &LVP) {
+ if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 ||
+ (OldLineInfo.Line == LineInfo.Line &&
+ OldLineInfo.FileName == LineInfo.FileName))
+ return;
+
+ if (SourceCache.find(LineInfo.FileName) == SourceCache.end())
+ if (!cacheSource(LineInfo))
+ return;
+ auto LineBuffer = LineCache.find(LineInfo.FileName);
+ if (LineBuffer != LineCache.end()) {
+ if (LineInfo.Line > LineBuffer->second.size()) {
+ reportWarning(
+ formatv(
+ "debug info line number {0} exceeds the number of lines in {1}",
+ LineInfo.Line, LineInfo.FileName),
+ ObjectFilename);
+ return;
+ }
+ // Vector begins at 0, line numbers are non-zero
+ OS << Delimiter << LineBuffer->second[LineInfo.Line - 1];
+ LVP.printBetweenInsts(OS, true);
+ }
+}
+
+SourcePrinter::SourcePrinter(const object::ObjectFile *Obj,
+ StringRef DefaultArch)
+ : Obj(Obj) {
+ symbolize::LLVMSymbolizer::Options SymbolizerOpts;
+ SymbolizerOpts.PrintFunctions =
+ DILineInfoSpecifier::FunctionNameKind::LinkageName;
+ SymbolizerOpts.Demangle = Demangle;
+ SymbolizerOpts.DefaultArch = std::string(DefaultArch);
+ Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts));
+}
+
+} // namespace objdump
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/SourcePrinter.h b/contrib/libs/llvm14/tools/llvm-objdump/SourcePrinter.h
new file mode 100644
index 00000000000..31d46e3108f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/SourcePrinter.h
@@ -0,0 +1,166 @@
+//===-- SourcePrinter.h - source interleaving utilities --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJDUMP_SOURCEPRINTER_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_SOURCEPRINTER_H
+
+#include "llvm/ADT/IndexedMap.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/Support/FormattedStream.h"
+#include <unordered_map>
+#include <vector>
+
+namespace llvm {
+namespace objdump {
+
+/// Stores a single expression representing the location of a source-level
+/// variable, along with the PC range for which that expression is valid.
+struct LiveVariable {
+ DWARFLocationExpression LocExpr;
+ const char *VarName;
+ DWARFUnit *Unit;
+ const DWARFDie FuncDie;
+
+ LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName,
+ DWARFUnit *Unit, const DWARFDie FuncDie)
+ : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {}
+
+ bool liveAtAddress(object::SectionedAddress Addr);
+
+ void print(raw_ostream &OS, const MCRegisterInfo &MRI) const;
+};
+
+/// Helper class for printing source variable locations alongside disassembly.
+class LiveVariablePrinter {
+ // Information we want to track about one column in which we are printing a
+ // variable live range.
+ struct Column {
+ unsigned VarIdx = NullVarIdx;
+ bool LiveIn = false;
+ bool LiveOut = false;
+ bool MustDrawLabel = false;
+
+ bool isActive() const { return VarIdx != NullVarIdx; }
+
+ static constexpr unsigned NullVarIdx = std::numeric_limits<unsigned>::max();
+ };
+
+ // All live variables we know about in the object/image file.
+ std::vector<LiveVariable> LiveVariables;
+
+ // The columns we are currently drawing.
+ IndexedMap<Column> ActiveCols;
+
+ const MCRegisterInfo &MRI;
+ const MCSubtargetInfo &STI;
+
+ void addVariable(DWARFDie FuncDie, DWARFDie VarDie);
+
+ void addFunction(DWARFDie D);
+
+ // Get the column number (in characters) at which the first live variable
+ // line should be printed.
+ unsigned getIndentLevel() const;
+
+ // Indent to the first live-range column to the right of the currently
+ // printed line, and return the index of that column.
+ // TODO: formatted_raw_ostream uses "column" to mean a number of characters
+ // since the last \n, and we use it to mean the number of slots in which we
+ // put live variable lines. Pick a less overloaded word.
+ unsigned moveToFirstVarColumn(formatted_raw_ostream &OS);
+
+ unsigned findFreeColumn();
+
+public:
+ LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI)
+ : ActiveCols(Column()), MRI(MRI), STI(STI) {}
+
+ void dump() const;
+
+ void addCompileUnit(DWARFDie D);
+
+ /// Update to match the state of the instruction between ThisAddr and
+ /// NextAddr. In the common case, any live range active at ThisAddr is
+ /// live-in to the instruction, and any live range active at NextAddr is
+ /// live-out of the instruction. If IncludeDefinedVars is false, then live
+ /// ranges starting at NextAddr will be ignored.
+ void update(object::SectionedAddress ThisAddr,
+ object::SectionedAddress NextAddr, bool IncludeDefinedVars);
+
+ enum class LineChar {
+ RangeStart,
+ RangeMid,
+ RangeEnd,
+ LabelVert,
+ LabelCornerNew,
+ LabelCornerActive,
+ LabelHoriz,
+ };
+ const char *getLineChar(LineChar C) const;
+
+ /// Print live ranges to the right of an existing line. This assumes the
+ /// line is not an instruction, so doesn't start or end any live ranges, so
+ /// we only need to print active ranges or empty columns. If AfterInst is
+ /// true, this is being printed after the last instruction fed to update(),
+ /// otherwise this is being printed before it.
+ void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst);
+
+ /// Print any live variable range info needed to the right of a
+ /// non-instruction line of disassembly. This is where we print the variable
+ /// names and expressions, with thin line-drawing characters connecting them
+ /// to the live range which starts at the next instruction. If MustPrint is
+ /// true, we have to print at least one line (with the continuation of any
+ /// already-active live ranges) because something has already been printed
+ /// earlier on this line.
+ void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint);
+
+ /// Print the live variable ranges to the right of a disassembled instruction.
+ void printAfterInst(formatted_raw_ostream &OS);
+};
+
+class SourcePrinter {
+protected:
+ DILineInfo OldLineInfo;
+ const object::ObjectFile *Obj = nullptr;
+ std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer;
+ // File name to file contents of source.
+ std::unordered_map<std::string, std::unique_ptr<MemoryBuffer>> SourceCache;
+ // Mark the line endings of the cached source.
+ std::unordered_map<std::string, std::vector<StringRef>> LineCache;
+ // Keep track of missing sources.
+ StringSet<> MissingSources;
+ // Only emit 'invalid debug info' warning once.
+ bool WarnedInvalidDebugInfo = false;
+
+private:
+ bool cacheSource(const DILineInfo &LineInfoFile);
+
+ void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo,
+ StringRef Delimiter, LiveVariablePrinter &LVP);
+
+ void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo,
+ StringRef ObjectFilename, StringRef Delimiter,
+ LiveVariablePrinter &LVP);
+
+public:
+ SourcePrinter() = default;
+ SourcePrinter(const object::ObjectFile *Obj, StringRef DefaultArch);
+ virtual ~SourcePrinter() = default;
+ virtual void printSourceLine(formatted_raw_ostream &OS,
+ object::SectionedAddress Address,
+ StringRef ObjectFilename,
+ LiveVariablePrinter &LVP,
+ StringRef Delimiter = "; ");
+};
+
+} // namespace objdump
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/WasmDump.cpp b/contrib/libs/llvm14/tools/llvm-objdump/WasmDump.cpp
new file mode 100644
index 00000000000..df0a08e5b1d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/WasmDump.cpp
@@ -0,0 +1,53 @@
+//===-- WasmDump.cpp - wasm-specific dumper ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the wasm-specific dumper for llvm-objdump.
+///
+//===----------------------------------------------------------------------===//
+
+#include "WasmDump.h"
+
+#include "llvm-objdump.h"
+#include "llvm/Object/Wasm.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+void objdump::printWasmFileHeader(const object::ObjectFile *Obj) {
+ const auto *File = cast<const WasmObjectFile>(Obj);
+
+ outs() << "Program Header:\n";
+ outs() << "Version: 0x";
+ outs().write_hex(File->getHeader().Version);
+ outs() << "\n";
+}
+
+Error objdump::getWasmRelocationValueString(const WasmObjectFile *Obj,
+ const RelocationRef &RelRef,
+ SmallVectorImpl<char> &Result) {
+ const wasm::WasmRelocation &Rel = Obj->getWasmRelocation(RelRef);
+ symbol_iterator SI = RelRef.getSymbol();
+ 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_WASM_TYPE_INDEX_LEB.
+ Fmt << Rel.Index;
+ } else {
+ Expected<StringRef> SymNameOrErr = SI->getName();
+ if (!SymNameOrErr)
+ return SymNameOrErr.takeError();
+ StringRef SymName = *SymNameOrErr;
+ Result.append(SymName.begin(), SymName.end());
+ }
+ Fmt << (Rel.Addend < 0 ? "" : "+") << Rel.Addend;
+ Fmt.flush();
+ Result.append(FmtBuf.begin(), FmtBuf.end());
+ return Error::success();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/WasmDump.h b/contrib/libs/llvm14/tools/llvm-objdump/WasmDump.h
new file mode 100644
index 00000000000..03ff9aed8e0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/WasmDump.h
@@ -0,0 +1,35 @@
+//===-- WasmDump.h - wasm-specific dumper -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJDUMP_WASMDUMP_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_WASMDUMP_H
+
+#include "llvm/ADT/SmallVector.h"
+
+namespace llvm {
+
+class Error;
+
+namespace object {
+class WasmObjectFile;
+class ObjectFile;
+class RelocationRef;
+} // namespace object
+
+namespace objdump {
+
+Error getWasmRelocationValueString(const object::WasmObjectFile *Obj,
+ const object::RelocationRef &RelRef,
+ llvm::SmallVectorImpl<char> &Result);
+
+void printWasmFileHeader(const object::ObjectFile *O);
+
+} // namespace objdump
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/XCOFFDump.cpp b/contrib/libs/llvm14/tools/llvm-objdump/XCOFFDump.cpp
new file mode 100644
index 00000000000..b8fb2ed3d06
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/XCOFFDump.cpp
@@ -0,0 +1,114 @@
+//===-- XCOFFDump.cpp - XCOFF-specific dumper -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the XCOFF-specific dumper for llvm-objdump.
+///
+//===----------------------------------------------------------------------===//
+
+#include "XCOFFDump.h"
+
+#include "llvm-objdump.h"
+#include "llvm/Demangle/Demangle.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile *Obj,
+ const RelocationRef &Rel,
+ SmallVectorImpl<char> &Result) {
+ symbol_iterator SymI = Rel.getSymbol();
+ if (SymI == Obj->symbol_end())
+ return make_error<GenericBinaryError>(
+ "invalid symbol reference in relocation entry",
+ object_error::parse_failed);
+
+ Expected<StringRef> SymNameOrErr = SymI->getName();
+ if (!SymNameOrErr)
+ return SymNameOrErr.takeError();
+
+ std::string SymName = (*SymNameOrErr).str();
+ if (Demangle)
+ SymName = demangle(SymName);
+
+ if (SymbolDescription)
+ SymName = getXCOFFSymbolDescription(createSymbolInfo(Obj, *SymI), SymName);
+
+ Result.append(SymName.begin(), SymName.end());
+ return Error::success();
+}
+
+Optional<XCOFF::StorageMappingClass>
+objdump::getXCOFFSymbolCsectSMC(const XCOFFObjectFile *Obj,
+ const SymbolRef &Sym) {
+ const XCOFFSymbolRef SymRef = Obj->toSymbolRef(Sym.getRawDataRefImpl());
+
+ if (!SymRef.isCsectSymbol())
+ return None;
+
+ auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef();
+ if (!CsectAuxEntOrErr)
+ return None;
+
+ return CsectAuxEntOrErr.get().getStorageMappingClass();
+}
+
+Optional<object::SymbolRef>
+objdump::getXCOFFSymbolContainingSymbolRef(const XCOFFObjectFile *Obj,
+ const SymbolRef &Sym) {
+
+ const XCOFFSymbolRef SymRef = Obj->toSymbolRef(Sym.getRawDataRefImpl());
+ if (!SymRef.isCsectSymbol())
+ return None;
+
+ Expected<XCOFFCsectAuxRef> CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef();
+ if (!CsectAuxEntOrErr || !CsectAuxEntOrErr.get().isLabel())
+ return None;
+ uint32_t Idx =
+ static_cast<uint32_t>(CsectAuxEntOrErr.get().getSectionOrLength());
+ DataRefImpl DRI;
+ DRI.p = Obj->getSymbolByIndex(Idx);
+ return SymbolRef(DRI, Obj);
+}
+
+bool objdump::isLabel(const XCOFFObjectFile *Obj, const SymbolRef &Sym) {
+
+ const XCOFFSymbolRef SymRef = Obj->toSymbolRef(Sym.getRawDataRefImpl());
+
+ if (!SymRef.isCsectSymbol())
+ return false;
+
+ auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef();
+ if (!CsectAuxEntOrErr)
+ return false;
+
+ return CsectAuxEntOrErr.get().isLabel();
+}
+
+std::string objdump::getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo,
+ StringRef SymbolName) {
+ assert(SymbolInfo.isXCOFF() && "Must be a XCOFFSymInfo.");
+
+ std::string Result;
+ // Dummy symbols have no symbol index.
+ if (SymbolInfo.XCOFFSymInfo.Index)
+ Result = ("(idx: " + Twine(SymbolInfo.XCOFFSymInfo.Index.getValue()) +
+ ") " + SymbolName)
+ .str();
+ else
+ Result.append(SymbolName.begin(), SymbolName.end());
+
+ if (SymbolInfo.XCOFFSymInfo.StorageMappingClass &&
+ !SymbolInfo.XCOFFSymInfo.IsLabel) {
+ const XCOFF::StorageMappingClass Smc =
+ SymbolInfo.XCOFFSymInfo.StorageMappingClass.getValue();
+ Result.append(("[" + XCOFF::getMappingClassString(Smc) + "]").str());
+ }
+
+ return Result;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/XCOFFDump.h b/contrib/libs/llvm14/tools/llvm-objdump/XCOFFDump.h
new file mode 100644
index 00000000000..6796f00aef6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/XCOFFDump.h
@@ -0,0 +1,37 @@
+//===-- XCOFFDump.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJDUMP_XCOFFDUMP_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_XCOFFDUMP_H
+
+#include "llvm/Object/XCOFFObjectFile.h"
+
+namespace llvm {
+
+struct SymbolInfoTy;
+
+namespace objdump {
+Optional<XCOFF::StorageMappingClass>
+getXCOFFSymbolCsectSMC(const object::XCOFFObjectFile *Obj,
+ const object::SymbolRef &Sym);
+
+Optional<object::SymbolRef>
+getXCOFFSymbolContainingSymbolRef(const object::XCOFFObjectFile *Obj,
+ const object::SymbolRef &Sym);
+
+bool isLabel(const object::XCOFFObjectFile *Obj, const object::SymbolRef &Sym);
+
+std::string getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo,
+ StringRef SymbolName);
+
+Error getXCOFFRelocationValueString(const object::XCOFFObjectFile *Obj,
+ const object::RelocationRef &RelRef,
+ llvm::SmallVectorImpl<char> &Result);
+} // namespace objdump
+} // namespace llvm
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/llvm-objdump.cpp b/contrib/libs/llvm14/tools/llvm-objdump/llvm-objdump.cpp
new file mode 100644
index 00000000000..6b238fa01d2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/llvm-objdump.cpp
@@ -0,0 +1,2776 @@
+//===-- llvm-objdump.cpp - Object file dumping utility for llvm -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is a utility that works like binutils "objdump", that is, it
+// dumps out a plethora of information about an object file depending on the
+// flags.
+//
+// The flags and output of this program should be near identical to those of
+// binutils objdump.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-objdump.h"
+#include "COFFDump.h"
+#include "ELFDump.h"
+#include "MachODump.h"
+#include "ObjdumpOptID.h"
+#include "SourcePrinter.h"
+#include "WasmDump.h"
+#include "XCOFFDump.h"
+#include "llvm/ADT/IndexedMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SetOperations.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCDisassembler/MCRelocationInfo.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrAnalysis.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/COFFImportFile.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/FaultMapParser.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/GraphWriter.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/StringSaver.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cctype>
+#include <cstring>
+#include <system_error>
+#include <unordered_map>
+#include <utility>
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::objdump;
+using namespace llvm::opt;
+
+namespace {
+
+class CommonOptTable : public opt::OptTable {
+public:
+ CommonOptTable(ArrayRef<Info> OptionInfos, const char *Usage,
+ const char *Description)
+ : OptTable(OptionInfos), Usage(Usage), Description(Description) {
+ setGroupedShortOptions(true);
+ }
+
+ void printHelp(StringRef Argv0, bool ShowHidden = false) const {
+ Argv0 = sys::path::filename(Argv0);
+ opt::OptTable::printHelp(outs(), (Argv0 + Usage).str().c_str(), Description,
+ ShowHidden, ShowHidden);
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ }
+
+private:
+ const char *Usage;
+ const char *Description;
+};
+
+// ObjdumpOptID is in ObjdumpOptID.h
+
+#define PREFIX(NAME, VALUE) const char *const OBJDUMP_##NAME[] = VALUE;
+#include "ObjdumpOpts.inc"
+#undef PREFIX
+
+static constexpr opt::OptTable::Info ObjdumpInfoTable[] = {
+#define OBJDUMP_nullptr nullptr
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {OBJDUMP_##PREFIX, NAME, HELPTEXT, \
+ METAVAR, OBJDUMP_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OBJDUMP_##GROUP, \
+ OBJDUMP_##ALIAS, ALIASARGS, VALUES},
+#include "ObjdumpOpts.inc"
+#undef OPTION
+#undef OBJDUMP_nullptr
+};
+
+class ObjdumpOptTable : public CommonOptTable {
+public:
+ ObjdumpOptTable()
+ : CommonOptTable(ObjdumpInfoTable, " [options] <input object files>",
+ "llvm object file dumper") {}
+};
+
+enum OtoolOptID {
+ OTOOL_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OTOOL_##ID,
+#include "OtoolOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const OTOOL_##NAME[] = VALUE;
+#include "OtoolOpts.inc"
+#undef PREFIX
+
+static constexpr opt::OptTable::Info OtoolInfoTable[] = {
+#define OTOOL_nullptr nullptr
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {OTOOL_##PREFIX, NAME, HELPTEXT, \
+ METAVAR, OTOOL_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OTOOL_##GROUP, \
+ OTOOL_##ALIAS, ALIASARGS, VALUES},
+#include "OtoolOpts.inc"
+#undef OPTION
+#undef OTOOL_nullptr
+};
+
+class OtoolOptTable : public CommonOptTable {
+public:
+ OtoolOptTable()
+ : CommonOptTable(OtoolInfoTable, " [option...] [file...]",
+ "Mach-O object file displaying tool") {}
+};
+
+} // namespace
+
+#define DEBUG_TYPE "objdump"
+
+static uint64_t AdjustVMA;
+static bool AllHeaders;
+static std::string ArchName;
+bool objdump::ArchiveHeaders;
+bool objdump::Demangle;
+bool objdump::Disassemble;
+bool objdump::DisassembleAll;
+bool objdump::SymbolDescription;
+static std::vector<std::string> DisassembleSymbols;
+static bool DisassembleZeroes;
+static std::vector<std::string> DisassemblerOptions;
+DIDumpType objdump::DwarfDumpType;
+static bool DynamicRelocations;
+static bool FaultMapSection;
+static bool FileHeaders;
+bool objdump::SectionContents;
+static std::vector<std::string> InputFilenames;
+bool objdump::PrintLines;
+static bool MachOOpt;
+std::string objdump::MCPU;
+std::vector<std::string> objdump::MAttrs;
+bool objdump::ShowRawInsn;
+bool objdump::LeadingAddr;
+static bool RawClangAST;
+bool objdump::Relocations;
+bool objdump::PrintImmHex;
+bool objdump::PrivateHeaders;
+std::vector<std::string> objdump::FilterSections;
+bool objdump::SectionHeaders;
+static bool ShowLMA;
+bool objdump::PrintSource;
+
+static uint64_t StartAddress;
+static bool HasStartAddressFlag;
+static uint64_t StopAddress = UINT64_MAX;
+static bool HasStopAddressFlag;
+
+bool objdump::SymbolTable;
+static bool SymbolizeOperands;
+static bool DynamicSymbolTable;
+std::string objdump::TripleName;
+bool objdump::UnwindInfo;
+static bool Wide;
+std::string objdump::Prefix;
+uint32_t objdump::PrefixStrip;
+
+DebugVarsFormat objdump::DbgVariables = DVDisabled;
+
+int objdump::DbgIndent = 52;
+
+static StringSet<> DisasmSymbolSet;
+StringSet<> objdump::FoundSectionSet;
+static StringRef ToolName;
+
+namespace {
+struct FilterResult {
+ // True if the section should not be skipped.
+ bool Keep;
+
+ // True if the index counter should be incremented, even if the section should
+ // be skipped. For example, sections may be skipped if they are not included
+ // in the --section flag, but we still want those to count toward the section
+ // count.
+ bool IncrementIndex;
+};
+} // namespace
+
+static FilterResult checkSectionFilter(object::SectionRef S) {
+ if (FilterSections.empty())
+ return {/*Keep=*/true, /*IncrementIndex=*/true};
+
+ Expected<StringRef> SecNameOrErr = S.getName();
+ if (!SecNameOrErr) {
+ consumeError(SecNameOrErr.takeError());
+ return {/*Keep=*/false, /*IncrementIndex=*/false};
+ }
+ StringRef SecName = *SecNameOrErr;
+
+ // StringSet does not allow empty key so avoid adding sections with
+ // no name (such as the section with index 0) here.
+ if (!SecName.empty())
+ FoundSectionSet.insert(SecName);
+
+ // Only show the section if it's in the FilterSections list, but always
+ // increment so the indexing is stable.
+ return {/*Keep=*/is_contained(FilterSections, SecName),
+ /*IncrementIndex=*/true};
+}
+
+SectionFilter objdump::ToolSectionFilter(object::ObjectFile const &O,
+ uint64_t *Idx) {
+ // Start at UINT64_MAX so that the first index returned after an increment is
+ // zero (after the unsigned wrap).
+ if (Idx)
+ *Idx = UINT64_MAX;
+ return SectionFilter(
+ [Idx](object::SectionRef S) {
+ FilterResult Result = checkSectionFilter(S);
+ if (Idx != nullptr && Result.IncrementIndex)
+ *Idx += 1;
+ return Result.Keep;
+ },
+ O);
+}
+
+std::string objdump::getFileNameForError(const object::Archive::Child &C,
+ unsigned Index) {
+ Expected<StringRef> NameOrErr = C.getName();
+ if (NameOrErr)
+ return std::string(NameOrErr.get());
+ // If we have an error getting the name then we print the index of the archive
+ // member. Since we are already in an error state, we just ignore this error.
+ consumeError(NameOrErr.takeError());
+ return "<file index: " + std::to_string(Index) + ">";
+}
+
+void objdump::reportWarning(const Twine &Message, StringRef File) {
+ // Output order between errs() and outs() matters especially for archive
+ // files where the output is per member object.
+ outs().flush();
+ WithColor::warning(errs(), ToolName)
+ << "'" << File << "': " << Message << "\n";
+}
+
+[[noreturn]] void objdump::reportError(StringRef File, const Twine &Message) {
+ outs().flush();
+ WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n";
+ exit(1);
+}
+
+[[noreturn]] void objdump::reportError(Error E, StringRef FileName,
+ StringRef ArchiveName,
+ StringRef ArchitectureName) {
+ assert(E);
+ outs().flush();
+ WithColor::error(errs(), ToolName);
+ if (ArchiveName != "")
+ errs() << ArchiveName << "(" << FileName << ")";
+ else
+ errs() << "'" << FileName << "'";
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ")";
+ errs() << ": ";
+ logAllUnhandledErrors(std::move(E), errs());
+ exit(1);
+}
+
+static void reportCmdLineWarning(const Twine &Message) {
+ WithColor::warning(errs(), ToolName) << Message << "\n";
+}
+
+[[noreturn]] static void reportCmdLineError(const Twine &Message) {
+ WithColor::error(errs(), ToolName) << Message << "\n";
+ exit(1);
+}
+
+static void warnOnNoMatchForSections() {
+ SetVector<StringRef> MissingSections;
+ for (StringRef S : FilterSections) {
+ if (FoundSectionSet.count(S))
+ return;
+ // User may specify a unnamed section. Don't warn for it.
+ if (!S.empty())
+ MissingSections.insert(S);
+ }
+
+ // Warn only if no section in FilterSections is matched.
+ for (StringRef S : MissingSections)
+ reportCmdLineWarning("section '" + S +
+ "' mentioned in a -j/--section option, but not "
+ "found in any input file");
+}
+
+static const Target *getTarget(const ObjectFile *Obj) {
+ // Figure out the target triple.
+ Triple TheTriple("unknown-unknown-unknown");
+ if (TripleName.empty()) {
+ TheTriple = Obj->makeTriple();
+ } else {
+ TheTriple.setTriple(Triple::normalize(TripleName));
+ auto Arch = Obj->getArch();
+ if (Arch == Triple::arm || Arch == Triple::armeb)
+ Obj->setARMSubArch(TheTriple);
+ }
+
+ // Get the target specific parser.
+ std::string Error;
+ const Target *TheTarget = TargetRegistry::lookupTarget(ArchName, TheTriple,
+ Error);
+ if (!TheTarget)
+ reportError(Obj->getFileName(), "can't find target: " + Error);
+
+ // Update the triple name and return the found target.
+ TripleName = TheTriple.getTriple();
+ return TheTarget;
+}
+
+bool objdump::isRelocAddressLess(RelocationRef A, RelocationRef B) {
+ return A.getOffset() < B.getOffset();
+}
+
+static Error getRelocationValueString(const RelocationRef &Rel,
+ SmallVectorImpl<char> &Result) {
+ const ObjectFile *Obj = Rel.getObject();
+ if (auto *ELF = dyn_cast<ELFObjectFileBase>(Obj))
+ return getELFRelocationValueString(ELF, Rel, Result);
+ if (auto *COFF = dyn_cast<COFFObjectFile>(Obj))
+ return getCOFFRelocationValueString(COFF, Rel, Result);
+ if (auto *Wasm = dyn_cast<WasmObjectFile>(Obj))
+ return getWasmRelocationValueString(Wasm, Rel, Result);
+ if (auto *MachO = dyn_cast<MachOObjectFile>(Obj))
+ return getMachORelocationValueString(MachO, Rel, Result);
+ if (auto *XCOFF = dyn_cast<XCOFFObjectFile>(Obj))
+ return getXCOFFRelocationValueString(XCOFF, Rel, Result);
+ llvm_unreachable("unknown object file format");
+}
+
+/// Indicates whether this relocation should hidden when listing
+/// 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) {
+ auto *MachO = dyn_cast<MachOObjectFile>(RelRef.getObject());
+ if (!MachO)
+ return false;
+
+ unsigned Arch = MachO->getArch();
+ DataRefImpl Rel = RelRef.getRawDataRefImpl();
+ uint64_t Type = MachO->getRelocationType(Rel);
+
+ // On arches that use the generic relocations, GENERIC_RELOC_PAIR
+ // is always hidden.
+ 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) {
+ DataRefImpl RelPrev = Rel;
+ RelPrev.d.a--;
+ uint64_t PrevType = MachO->getRelocationType(RelPrev);
+ if (PrevType == MachO::X86_64_RELOC_SUBTRACTOR)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+namespace {
+
+/// Get the column at which we want to start printing the instruction
+/// disassembly, taking into account anything which appears to the left of it.
+unsigned getInstStartColumn(const MCSubtargetInfo &STI) {
+ return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24;
+}
+
+static bool isAArch64Elf(const ObjectFile *Obj) {
+ const auto *Elf = dyn_cast<ELFObjectFileBase>(Obj);
+ return Elf && Elf->getEMachine() == ELF::EM_AARCH64;
+}
+
+static bool isArmElf(const ObjectFile *Obj) {
+ const auto *Elf = dyn_cast<ELFObjectFileBase>(Obj);
+ return Elf && Elf->getEMachine() == ELF::EM_ARM;
+}
+
+static bool hasMappingSymbols(const ObjectFile *Obj) {
+ return isArmElf(Obj) || isAArch64Elf(Obj);
+}
+
+static void printRelocation(formatted_raw_ostream &OS, StringRef FileName,
+ const RelocationRef &Rel, uint64_t Address,
+ bool Is64Bits) {
+ StringRef Fmt = Is64Bits ? "\t\t%016" PRIx64 ": " : "\t\t\t%08" PRIx64 ": ";
+ SmallString<16> Name;
+ SmallString<32> Val;
+ Rel.getTypeName(Name);
+ if (Error E = getRelocationValueString(Rel, Val))
+ reportError(std::move(E), FileName);
+ OS << format(Fmt.data(), Address) << Name << "\t" << Val;
+}
+
+class PrettyPrinter {
+public:
+ virtual ~PrettyPrinter() = default;
+ virtual void
+ printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
+ StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) {
+ if (SP && (PrintSource || PrintLines))
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP);
+ LVP.printBetweenInsts(OS, false);
+
+ size_t Start = OS.tell();
+ if (LeadingAddr)
+ OS << format("%8" PRIx64 ":", Address.Address);
+ if (ShowRawInsn) {
+ OS << ' ';
+ dumpBytes(Bytes, OS);
+ }
+
+ // The output of printInst starts with a tab. Print some spaces so that
+ // the tab has 1 column and advances to the target tab stop.
+ unsigned TabStop = getInstStartColumn(STI);
+ unsigned Column = OS.tell() - Start;
+ OS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8);
+
+ if (MI) {
+ // See MCInstPrinter::printInst. On targets where a PC relative immediate
+ // is relative to the next instruction and the length of a MCInst is
+ // difficult to measure (x86), this is the address of the next
+ // instruction.
+ uint64_t Addr =
+ Address.Address + (STI.getTargetTriple().isX86() ? Bytes.size() : 0);
+ IP.printInst(MI, Addr, "", STI, OS);
+ } else
+ OS << "\t<unknown>";
+ }
+};
+PrettyPrinter PrettyPrinterInst;
+
+class HexagonPrettyPrinter : public PrettyPrinter {
+public:
+ void printLead(ArrayRef<uint8_t> Bytes, uint64_t Address,
+ formatted_raw_ostream &OS) {
+ uint32_t opcode =
+ (Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0];
+ if (LeadingAddr)
+ OS << format("%8" PRIx64 ":", Address);
+ if (ShowRawInsn) {
+ OS << "\t";
+ dumpBytes(Bytes.slice(0, 4), OS);
+ OS << format("\t%08" PRIx32, opcode);
+ }
+ }
+ void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
+ StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) override {
+ if (SP && (PrintSource || PrintLines))
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP, "");
+ if (!MI) {
+ printLead(Bytes, Address.Address, OS);
+ OS << " <unknown>";
+ return;
+ }
+ std::string Buffer;
+ {
+ raw_string_ostream TempStream(Buffer);
+ IP.printInst(MI, Address.Address, "", STI, TempStream);
+ }
+ StringRef Contents(Buffer);
+ // Split off bundle attributes
+ auto PacketBundle = Contents.rsplit('\n');
+ // Split off first instruction from the rest
+ auto HeadTail = PacketBundle.first.split('\n');
+ auto Preamble = " { ";
+ auto Separator = "";
+
+ // Hexagon's packets require relocations to be inline rather than
+ // clustered at the end of the packet.
+ std::vector<RelocationRef>::const_iterator RelCur = Rels->begin();
+ std::vector<RelocationRef>::const_iterator RelEnd = Rels->end();
+ auto PrintReloc = [&]() -> void {
+ while ((RelCur != RelEnd) && (RelCur->getOffset() <= Address.Address)) {
+ if (RelCur->getOffset() == Address.Address) {
+ printRelocation(OS, ObjectFilename, *RelCur, Address.Address, false);
+ return;
+ }
+ ++RelCur;
+ }
+ };
+
+ while (!HeadTail.first.empty()) {
+ OS << Separator;
+ Separator = "\n";
+ if (SP && (PrintSource || PrintLines))
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP, "");
+ printLead(Bytes, Address.Address, OS);
+ OS << Preamble;
+ Preamble = " ";
+ StringRef Inst;
+ auto Duplex = HeadTail.first.split('\v');
+ if (!Duplex.second.empty()) {
+ OS << Duplex.first;
+ OS << "; ";
+ Inst = Duplex.second;
+ }
+ else
+ Inst = HeadTail.first;
+ OS << Inst;
+ HeadTail = HeadTail.second.split('\n');
+ if (HeadTail.first.empty())
+ OS << " } " << PacketBundle.second;
+ PrintReloc();
+ Bytes = Bytes.slice(4);
+ Address.Address += 4;
+ }
+ }
+};
+HexagonPrettyPrinter HexagonPrettyPrinterInst;
+
+class AMDGCNPrettyPrinter : public PrettyPrinter {
+public:
+ void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
+ StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) override {
+ if (SP && (PrintSource || PrintLines))
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP);
+
+ if (MI) {
+ SmallString<40> InstStr;
+ raw_svector_ostream IS(InstStr);
+
+ IP.printInst(MI, Address.Address, "", STI, IS);
+
+ OS << left_justify(IS.str(), 60);
+ } else {
+ // an unrecognized encoding - this is probably data so represent it
+ // using the .long directive, or .byte directive if fewer than 4 bytes
+ // remaining
+ if (Bytes.size() >= 4) {
+ OS << format("\t.long 0x%08" PRIx32 " ",
+ support::endian::read32<support::little>(Bytes.data()));
+ OS.indent(42);
+ } else {
+ OS << format("\t.byte 0x%02" PRIx8, Bytes[0]);
+ for (unsigned int i = 1; i < Bytes.size(); i++)
+ OS << format(", 0x%02" PRIx8, Bytes[i]);
+ OS.indent(55 - (6 * Bytes.size()));
+ }
+ }
+
+ OS << format("// %012" PRIX64 ":", Address.Address);
+ if (Bytes.size() >= 4) {
+ // D should be casted to uint32_t here as it is passed by format to
+ // snprintf as vararg.
+ for (uint32_t D : makeArrayRef(
+ reinterpret_cast<const support::little32_t *>(Bytes.data()),
+ Bytes.size() / 4))
+ OS << format(" %08" PRIX32, D);
+ } else {
+ for (unsigned char B : Bytes)
+ OS << format(" %02" PRIX8, B);
+ }
+
+ if (!Annot.empty())
+ OS << " // " << Annot;
+ }
+};
+AMDGCNPrettyPrinter AMDGCNPrettyPrinterInst;
+
+class BPFPrettyPrinter : public PrettyPrinter {
+public:
+ void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
+ StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) override {
+ if (SP && (PrintSource || PrintLines))
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP);
+ if (LeadingAddr)
+ OS << format("%8" PRId64 ":", Address.Address / 8);
+ if (ShowRawInsn) {
+ OS << "\t";
+ dumpBytes(Bytes, OS);
+ }
+ if (MI)
+ IP.printInst(MI, Address.Address, "", STI, OS);
+ else
+ OS << "\t<unknown>";
+ }
+};
+BPFPrettyPrinter BPFPrettyPrinterInst;
+
+PrettyPrinter &selectPrettyPrinter(Triple const &Triple) {
+ switch(Triple.getArch()) {
+ default:
+ return PrettyPrinterInst;
+ case Triple::hexagon:
+ return HexagonPrettyPrinterInst;
+ case Triple::amdgcn:
+ return AMDGCNPrettyPrinterInst;
+ case Triple::bpfel:
+ case Triple::bpfeb:
+ return BPFPrettyPrinterInst;
+ }
+}
+}
+
+static uint8_t getElfSymbolType(const ObjectFile *Obj, const SymbolRef &Sym) {
+ assert(Obj->isELF());
+ if (auto *Elf32LEObj = dyn_cast<ELF32LEObjectFile>(Obj))
+ return unwrapOrError(Elf32LEObj->getSymbol(Sym.getRawDataRefImpl()),
+ Obj->getFileName())
+ ->getType();
+ if (auto *Elf64LEObj = dyn_cast<ELF64LEObjectFile>(Obj))
+ return unwrapOrError(Elf64LEObj->getSymbol(Sym.getRawDataRefImpl()),
+ Obj->getFileName())
+ ->getType();
+ if (auto *Elf32BEObj = dyn_cast<ELF32BEObjectFile>(Obj))
+ return unwrapOrError(Elf32BEObj->getSymbol(Sym.getRawDataRefImpl()),
+ Obj->getFileName())
+ ->getType();
+ if (auto *Elf64BEObj = cast<ELF64BEObjectFile>(Obj))
+ return unwrapOrError(Elf64BEObj->getSymbol(Sym.getRawDataRefImpl()),
+ Obj->getFileName())
+ ->getType();
+ llvm_unreachable("Unsupported binary format");
+}
+
+template <class ELFT> static void
+addDynamicElfSymbols(const ELFObjectFile<ELFT> *Obj,
+ std::map<SectionRef, SectionSymbolsTy> &AllSymbols) {
+ for (auto Symbol : Obj->getDynamicSymbolIterators()) {
+ uint8_t SymbolType = Symbol.getELFType();
+ if (SymbolType == ELF::STT_SECTION)
+ continue;
+
+ uint64_t Address = unwrapOrError(Symbol.getAddress(), Obj->getFileName());
+ // ELFSymbolRef::getAddress() returns size instead of value for common
+ // symbols which is not desirable for disassembly output. Overriding.
+ if (SymbolType == ELF::STT_COMMON)
+ Address = unwrapOrError(Obj->getSymbol(Symbol.getRawDataRefImpl()),
+ Obj->getFileName())
+ ->st_value;
+
+ StringRef Name = unwrapOrError(Symbol.getName(), Obj->getFileName());
+ if (Name.empty())
+ continue;
+
+ section_iterator SecI =
+ unwrapOrError(Symbol.getSection(), Obj->getFileName());
+ if (SecI == Obj->section_end())
+ continue;
+
+ AllSymbols[*SecI].emplace_back(Address, Name, SymbolType);
+ }
+}
+
+static void
+addDynamicElfSymbols(const ObjectFile *Obj,
+ std::map<SectionRef, SectionSymbolsTy> &AllSymbols) {
+ assert(Obj->isELF());
+ if (auto *Elf32LEObj = dyn_cast<ELF32LEObjectFile>(Obj))
+ addDynamicElfSymbols(Elf32LEObj, AllSymbols);
+ else if (auto *Elf64LEObj = dyn_cast<ELF64LEObjectFile>(Obj))
+ addDynamicElfSymbols(Elf64LEObj, AllSymbols);
+ else if (auto *Elf32BEObj = dyn_cast<ELF32BEObjectFile>(Obj))
+ addDynamicElfSymbols(Elf32BEObj, AllSymbols);
+ else if (auto *Elf64BEObj = cast<ELF64BEObjectFile>(Obj))
+ addDynamicElfSymbols(Elf64BEObj, AllSymbols);
+ else
+ llvm_unreachable("Unsupported binary format");
+}
+
+static Optional<SectionRef> getWasmCodeSection(const WasmObjectFile *Obj) {
+ for (auto SecI : Obj->sections()) {
+ const WasmSection &Section = Obj->getWasmSection(SecI);
+ if (Section.Type == wasm::WASM_SEC_CODE)
+ return SecI;
+ }
+ return None;
+}
+
+static void
+addMissingWasmCodeSymbols(const WasmObjectFile *Obj,
+ std::map<SectionRef, SectionSymbolsTy> &AllSymbols) {
+ Optional<SectionRef> Section = getWasmCodeSection(Obj);
+ if (!Section)
+ return;
+ SectionSymbolsTy &Symbols = AllSymbols[*Section];
+
+ std::set<uint64_t> SymbolAddresses;
+ for (const auto &Sym : Symbols)
+ SymbolAddresses.insert(Sym.Addr);
+
+ for (const wasm::WasmFunction &Function : Obj->functions()) {
+ uint64_t Address = Function.CodeSectionOffset;
+ // Only add fallback symbols for functions not already present in the symbol
+ // table.
+ if (SymbolAddresses.count(Address))
+ continue;
+ // This function has no symbol, so it should have no SymbolName.
+ assert(Function.SymbolName.empty());
+ // We use DebugName for the name, though it may be empty if there is no
+ // "name" custom section, or that section is missing a name for this
+ // function.
+ StringRef Name = Function.DebugName;
+ Symbols.emplace_back(Address, Name, ELF::STT_NOTYPE);
+ }
+}
+
+static void addPltEntries(const ObjectFile *Obj,
+ std::map<SectionRef, SectionSymbolsTy> &AllSymbols,
+ StringSaver &Saver) {
+ Optional<SectionRef> Plt = None;
+ for (const SectionRef &Section : Obj->sections()) {
+ Expected<StringRef> SecNameOrErr = Section.getName();
+ if (!SecNameOrErr) {
+ consumeError(SecNameOrErr.takeError());
+ continue;
+ }
+ if (*SecNameOrErr == ".plt")
+ Plt = Section;
+ }
+ if (!Plt)
+ return;
+ if (auto *ElfObj = dyn_cast<ELFObjectFileBase>(Obj)) {
+ for (auto PltEntry : ElfObj->getPltAddresses()) {
+ if (PltEntry.first) {
+ SymbolRef Symbol(*PltEntry.first, ElfObj);
+ uint8_t SymbolType = getElfSymbolType(Obj, Symbol);
+ if (Expected<StringRef> NameOrErr = Symbol.getName()) {
+ if (!NameOrErr->empty())
+ AllSymbols[*Plt].emplace_back(
+ PltEntry.second, Saver.save((*NameOrErr + "@plt").str()),
+ SymbolType);
+ continue;
+ } else {
+ // The warning has been reported in disassembleObject().
+ consumeError(NameOrErr.takeError());
+ }
+ }
+ reportWarning("PLT entry at 0x" + Twine::utohexstr(PltEntry.second) +
+ " references an invalid symbol",
+ Obj->getFileName());
+ }
+ }
+}
+
+// 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) {
+ // 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;
+}
+
+// Returns a map from sections to their relocations.
+static std::map<SectionRef, std::vector<RelocationRef>>
+getRelocsMap(object::ObjectFile const &Obj) {
+ std::map<SectionRef, std::vector<RelocationRef>> Ret;
+ uint64_t I = (uint64_t)-1;
+ for (SectionRef Sec : Obj.sections()) {
+ ++I;
+ Expected<section_iterator> RelocatedOrErr = Sec.getRelocatedSection();
+ if (!RelocatedOrErr)
+ reportError(Obj.getFileName(),
+ "section (" + Twine(I) +
+ "): failed to get a relocated section: " +
+ toString(RelocatedOrErr.takeError()));
+
+ section_iterator Relocated = *RelocatedOrErr;
+ if (Relocated == Obj.section_end() || !checkSectionFilter(*Relocated).Keep)
+ continue;
+ std::vector<RelocationRef> &V = Ret[*Relocated];
+ append_range(V, Sec.relocations());
+ // Sort relocations by address.
+ llvm::stable_sort(V, isRelocAddressLess);
+ }
+ return Ret;
+}
+
+// Used for --adjust-vma to check if address should be adjusted by the
+// specified value for a given section.
+// For ELF we do not adjust non-allocatable sections like debug ones,
+// because they are not loadable.
+// TODO: implement for other file formats.
+static bool shouldAdjustVA(const SectionRef &Section) {
+ const ObjectFile *Obj = Section.getObject();
+ if (Obj->isELF())
+ return ELFSectionRef(Section).getFlags() & ELF::SHF_ALLOC;
+ return false;
+}
+
+
+typedef std::pair<uint64_t, char> MappingSymbolPair;
+static char getMappingSymbolKind(ArrayRef<MappingSymbolPair> MappingSymbols,
+ uint64_t Address) {
+ auto It =
+ partition_point(MappingSymbols, [Address](const MappingSymbolPair &Val) {
+ return Val.first <= Address;
+ });
+ // Return zero for any address before the first mapping symbol; this means
+ // we should use the default disassembly mode, depending on the target.
+ if (It == MappingSymbols.begin())
+ return '\x00';
+ return (It - 1)->second;
+}
+
+static uint64_t dumpARMELFData(uint64_t SectionAddr, uint64_t Index,
+ uint64_t End, const ObjectFile *Obj,
+ ArrayRef<uint8_t> Bytes,
+ ArrayRef<MappingSymbolPair> MappingSymbols,
+ raw_ostream &OS) {
+ support::endianness Endian =
+ Obj->isLittleEndian() ? support::little : support::big;
+ OS << format("%8" PRIx64 ":\t", SectionAddr + Index);
+ if (Index + 4 <= End) {
+ dumpBytes(Bytes.slice(Index, 4), OS);
+ OS << "\t.word\t"
+ << format_hex(support::endian::read32(Bytes.data() + Index, Endian),
+ 10);
+ return 4;
+ }
+ if (Index + 2 <= End) {
+ dumpBytes(Bytes.slice(Index, 2), OS);
+ OS << "\t\t.short\t"
+ << format_hex(support::endian::read16(Bytes.data() + Index, Endian),
+ 6);
+ return 2;
+ }
+ dumpBytes(Bytes.slice(Index, 1), OS);
+ OS << "\t\t.byte\t" << format_hex(Bytes[0], 4);
+ return 1;
+}
+
+static void dumpELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End,
+ ArrayRef<uint8_t> Bytes) {
+ // print out data up to 8 bytes at a time in hex and ascii
+ uint8_t AsciiData[9] = {'\0'};
+ uint8_t Byte;
+ int NumBytes = 0;
+
+ for (; Index < End; ++Index) {
+ if (NumBytes == 0)
+ outs() << format("%8" PRIx64 ":", SectionAddr + Index);
+ Byte = Bytes.slice(Index)[0];
+ outs() << format(" %02x", Byte);
+ AsciiData[NumBytes] = isPrint(Byte) ? Byte : '.';
+
+ uint8_t IndentOffset = 0;
+ NumBytes++;
+ if (Index == End - 1 || NumBytes > 8) {
+ // Indent the space for less than 8 bytes data.
+ // 2 spaces for byte and one for space between bytes
+ IndentOffset = 3 * (8 - NumBytes);
+ for (int Excess = NumBytes; Excess < 8; Excess++)
+ AsciiData[Excess] = '\0';
+ NumBytes = 8;
+ }
+ if (NumBytes == 8) {
+ AsciiData[8] = '\0';
+ outs() << std::string(IndentOffset, ' ') << " ";
+ outs() << reinterpret_cast<char *>(AsciiData);
+ outs() << '\n';
+ NumBytes = 0;
+ }
+ }
+}
+
+SymbolInfoTy objdump::createSymbolInfo(const ObjectFile *Obj,
+ const SymbolRef &Symbol) {
+ const StringRef FileName = Obj->getFileName();
+ const uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName);
+ const StringRef Name = unwrapOrError(Symbol.getName(), FileName);
+
+ if (Obj->isXCOFF() && SymbolDescription) {
+ const auto *XCOFFObj = cast<XCOFFObjectFile>(Obj);
+ DataRefImpl SymbolDRI = Symbol.getRawDataRefImpl();
+
+ const uint32_t SymbolIndex = XCOFFObj->getSymbolIndex(SymbolDRI.p);
+ Optional<XCOFF::StorageMappingClass> Smc =
+ getXCOFFSymbolCsectSMC(XCOFFObj, Symbol);
+ return SymbolInfoTy(Addr, Name, Smc, SymbolIndex,
+ isLabel(XCOFFObj, Symbol));
+ } else
+ return SymbolInfoTy(Addr, Name,
+ Obj->isELF() ? getElfSymbolType(Obj, Symbol)
+ : (uint8_t)ELF::STT_NOTYPE);
+}
+
+static SymbolInfoTy createDummySymbolInfo(const ObjectFile *Obj,
+ const uint64_t Addr, StringRef &Name,
+ uint8_t Type) {
+ if (Obj->isXCOFF() && SymbolDescription)
+ return SymbolInfoTy(Addr, Name, None, None, false);
+ else
+ return SymbolInfoTy(Addr, Name, Type);
+}
+
+static void
+collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, const MCInstrAnalysis *MIA,
+ MCDisassembler *DisAsm, MCInstPrinter *IP,
+ const MCSubtargetInfo *STI, uint64_t SectionAddr,
+ uint64_t Start, uint64_t End,
+ std::unordered_map<uint64_t, std::string> &Labels) {
+ // So far only supports PowerPC and X86.
+ if (!STI->getTargetTriple().isPPC() && !STI->getTargetTriple().isX86())
+ return;
+
+ Labels.clear();
+ unsigned LabelCount = 0;
+ Start += SectionAddr;
+ End += SectionAddr;
+ uint64_t Index = Start;
+ while (Index < End) {
+ // Disassemble a real instruction and record function-local branch labels.
+ MCInst Inst;
+ uint64_t Size;
+ bool Disassembled = DisAsm->getInstruction(
+ Inst, Size, Bytes.slice(Index - SectionAddr), Index, nulls());
+ if (Size == 0)
+ Size = 1;
+
+ if (Disassembled && MIA) {
+ uint64_t Target;
+ bool TargetKnown = MIA->evaluateBranch(Inst, Index, Size, Target);
+ // On PowerPC, if the address of a branch is the same as the target, it
+ // means that it's a function call. Do not mark the label for this case.
+ if (TargetKnown && (Target >= Start && Target < End) &&
+ !Labels.count(Target) &&
+ !(STI->getTargetTriple().isPPC() && Target == Index))
+ Labels[Target] = ("L" + Twine(LabelCount++)).str();
+ }
+
+ Index += Size;
+ }
+}
+
+// Create an MCSymbolizer for the target and add it to the MCDisassembler.
+// This is currently only used on AMDGPU, and assumes the format of the
+// void * argument passed to AMDGPU's createMCSymbolizer.
+static void addSymbolizer(
+ MCContext &Ctx, const Target *Target, StringRef TripleName,
+ MCDisassembler *DisAsm, uint64_t SectionAddr, ArrayRef<uint8_t> Bytes,
+ SectionSymbolsTy &Symbols,
+ std::vector<std::unique_ptr<std::string>> &SynthesizedLabelNames) {
+
+ std::unique_ptr<MCRelocationInfo> RelInfo(
+ Target->createMCRelocationInfo(TripleName, Ctx));
+ if (!RelInfo)
+ return;
+ std::unique_ptr<MCSymbolizer> Symbolizer(Target->createMCSymbolizer(
+ TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo)));
+ MCSymbolizer *SymbolizerPtr = &*Symbolizer;
+ DisAsm->setSymbolizer(std::move(Symbolizer));
+
+ if (!SymbolizeOperands)
+ return;
+
+ // Synthesize labels referenced by branch instructions by
+ // disassembling, discarding the output, and collecting the referenced
+ // addresses from the symbolizer.
+ for (size_t Index = 0; Index != Bytes.size();) {
+ MCInst Inst;
+ uint64_t Size;
+ DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), SectionAddr + Index,
+ nulls());
+ if (Size == 0)
+ Size = 1;
+ Index += Size;
+ }
+ ArrayRef<uint64_t> LabelAddrsRef = SymbolizerPtr->getReferencedAddresses();
+ // Copy and sort to remove duplicates.
+ std::vector<uint64_t> LabelAddrs;
+ LabelAddrs.insert(LabelAddrs.end(), LabelAddrsRef.begin(),
+ LabelAddrsRef.end());
+ llvm::sort(LabelAddrs);
+ LabelAddrs.resize(std::unique(LabelAddrs.begin(), LabelAddrs.end()) -
+ LabelAddrs.begin());
+ // Add the labels.
+ for (unsigned LabelNum = 0; LabelNum != LabelAddrs.size(); ++LabelNum) {
+ auto Name = std::make_unique<std::string>();
+ *Name = (Twine("L") + Twine(LabelNum)).str();
+ SynthesizedLabelNames.push_back(std::move(Name));
+ Symbols.push_back(SymbolInfoTy(
+ LabelAddrs[LabelNum], *SynthesizedLabelNames.back(), ELF::STT_NOTYPE));
+ }
+ llvm::stable_sort(Symbols);
+ // Recreate the symbolizer with the new symbols list.
+ RelInfo.reset(Target->createMCRelocationInfo(TripleName, Ctx));
+ Symbolizer.reset(Target->createMCSymbolizer(
+ TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo)));
+ DisAsm->setSymbolizer(std::move(Symbolizer));
+}
+
+static StringRef getSegmentName(const MachOObjectFile *MachO,
+ const SectionRef &Section) {
+ if (MachO) {
+ DataRefImpl DR = Section.getRawDataRefImpl();
+ StringRef SegmentName = MachO->getSectionFinalSegmentName(DR);
+ return SegmentName;
+ }
+ return "";
+}
+
+static void emitPostInstructionInfo(formatted_raw_ostream &FOS,
+ const MCAsmInfo &MAI,
+ const MCSubtargetInfo &STI,
+ StringRef Comments,
+ LiveVariablePrinter &LVP) {
+ do {
+ if (!Comments.empty()) {
+ // Emit a line of comments.
+ StringRef Comment;
+ std::tie(Comment, Comments) = Comments.split('\n');
+ // MAI.getCommentColumn() assumes that instructions are printed at the
+ // position of 8, while getInstStartColumn() returns the actual position.
+ unsigned CommentColumn =
+ MAI.getCommentColumn() - 8 + getInstStartColumn(STI);
+ FOS.PadToColumn(CommentColumn);
+ FOS << MAI.getCommentString() << ' ' << Comment;
+ }
+ LVP.printAfterInst(FOS);
+ FOS << '\n';
+ } while (!Comments.empty());
+ FOS.flush();
+}
+
+static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
+ MCContext &Ctx, MCDisassembler *PrimaryDisAsm,
+ MCDisassembler *SecondaryDisAsm,
+ const MCInstrAnalysis *MIA, MCInstPrinter *IP,
+ const MCSubtargetInfo *PrimarySTI,
+ const MCSubtargetInfo *SecondarySTI,
+ PrettyPrinter &PIP,
+ SourcePrinter &SP, bool InlineRelocs) {
+ const MCSubtargetInfo *STI = PrimarySTI;
+ MCDisassembler *DisAsm = PrimaryDisAsm;
+ bool PrimaryIsThumb = false;
+ if (isArmElf(Obj))
+ PrimaryIsThumb = STI->checkFeatures("+thumb-mode");
+
+ std::map<SectionRef, std::vector<RelocationRef>> RelocMap;
+ if (InlineRelocs)
+ RelocMap = getRelocsMap(*Obj);
+ bool Is64Bits = Obj->getBytesInAddress() > 4;
+
+ // Create a mapping from virtual address to symbol name. This is used to
+ // pretty print the symbols while disassembling.
+ std::map<SectionRef, SectionSymbolsTy> AllSymbols;
+ SectionSymbolsTy AbsoluteSymbols;
+ const StringRef FileName = Obj->getFileName();
+ const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(Obj);
+ for (const SymbolRef &Symbol : Obj->symbols()) {
+ Expected<StringRef> NameOrErr = Symbol.getName();
+ if (!NameOrErr) {
+ reportWarning(toString(NameOrErr.takeError()), FileName);
+ continue;
+ }
+ if (NameOrErr->empty() && !(Obj->isXCOFF() && SymbolDescription))
+ continue;
+
+ if (Obj->isELF() && getElfSymbolType(Obj, Symbol) == ELF::STT_SECTION)
+ continue;
+
+ if (MachO) {
+ // __mh_(execute|dylib|dylinker|bundle|preload|object)_header are special
+ // symbols that support MachO header introspection. They do not bind to
+ // code locations and are irrelevant for disassembly.
+ if (NameOrErr->startswith("__mh_") && NameOrErr->endswith("_header"))
+ continue;
+ // Don't ask a Mach-O STAB symbol for its section unless you know that
+ // STAB symbol's section field refers to a valid section index. Otherwise
+ // the symbol may error trying to load a section that does not exist.
+ DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
+ uint8_t NType = (MachO->is64Bit() ?
+ MachO->getSymbol64TableEntry(SymDRI).n_type:
+ MachO->getSymbolTableEntry(SymDRI).n_type);
+ if (NType & MachO::N_STAB)
+ continue;
+ }
+
+ section_iterator SecI = unwrapOrError(Symbol.getSection(), FileName);
+ if (SecI != Obj->section_end())
+ AllSymbols[*SecI].push_back(createSymbolInfo(Obj, Symbol));
+ else
+ AbsoluteSymbols.push_back(createSymbolInfo(Obj, Symbol));
+ }
+
+ if (AllSymbols.empty() && Obj->isELF())
+ addDynamicElfSymbols(Obj, AllSymbols);
+
+ if (Obj->isWasm())
+ addMissingWasmCodeSymbols(cast<WasmObjectFile>(Obj), AllSymbols);
+
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ addPltEntries(Obj, AllSymbols, Saver);
+
+ // Create a mapping from virtual address to section. An empty section can
+ // cause more than one section at the same address. Sort such sections to be
+ // before same-addressed non-empty sections so that symbol lookups prefer the
+ // non-empty section.
+ std::vector<std::pair<uint64_t, SectionRef>> SectionAddresses;
+ for (SectionRef Sec : Obj->sections())
+ SectionAddresses.emplace_back(Sec.getAddress(), Sec);
+ llvm::stable_sort(SectionAddresses, [](const auto &LHS, const auto &RHS) {
+ if (LHS.first != RHS.first)
+ return LHS.first < RHS.first;
+ return LHS.second.getSize() < RHS.second.getSize();
+ });
+
+ // Linked executables (.exe and .dll files) typically don't include a real
+ // symbol table but they might contain an export table.
+ if (const auto *COFFObj = dyn_cast<COFFObjectFile>(Obj)) {
+ for (const auto &ExportEntry : COFFObj->export_directories()) {
+ StringRef Name;
+ if (Error E = ExportEntry.getSymbolName(Name))
+ reportError(std::move(E), Obj->getFileName());
+ if (Name.empty())
+ continue;
+
+ uint32_t RVA;
+ if (Error E = ExportEntry.getExportRVA(RVA))
+ reportError(std::move(E), Obj->getFileName());
+
+ uint64_t VA = COFFObj->getImageBase() + RVA;
+ auto Sec = partition_point(
+ SectionAddresses, [VA](const std::pair<uint64_t, SectionRef> &O) {
+ return O.first <= VA;
+ });
+ if (Sec != SectionAddresses.begin()) {
+ --Sec;
+ AllSymbols[Sec->second].emplace_back(VA, Name, ELF::STT_NOTYPE);
+ } else
+ AbsoluteSymbols.emplace_back(VA, Name, ELF::STT_NOTYPE);
+ }
+ }
+
+ // Sort all the symbols, this allows us to use a simple binary search to find
+ // Multiple symbols can have the same address. Use a stable sort to stabilize
+ // the output.
+ StringSet<> FoundDisasmSymbolSet;
+ for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
+ llvm::stable_sort(SecSyms.second);
+ llvm::stable_sort(AbsoluteSymbols);
+
+ std::unique_ptr<DWARFContext> DICtx;
+ LiveVariablePrinter LVP(*Ctx.getRegisterInfo(), *STI);
+
+ if (DbgVariables != DVDisabled) {
+ DICtx = DWARFContext::create(*Obj);
+ for (const std::unique_ptr<DWARFUnit> &CU : DICtx->compile_units())
+ LVP.addCompileUnit(CU->getUnitDIE(false));
+ }
+
+ LLVM_DEBUG(LVP.dump());
+
+ for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
+ if (FilterSections.empty() && !DisassembleAll &&
+ (!Section.isText() || Section.isVirtual()))
+ continue;
+
+ uint64_t SectionAddr = Section.getAddress();
+ uint64_t SectSize = Section.getSize();
+ if (!SectSize)
+ continue;
+
+ // Get the list of all the symbols in this section.
+ SectionSymbolsTy &Symbols = AllSymbols[Section];
+ std::vector<MappingSymbolPair> MappingSymbols;
+ if (hasMappingSymbols(Obj)) {
+ for (const auto &Symb : Symbols) {
+ uint64_t Address = Symb.Addr;
+ StringRef Name = Symb.Name;
+ if (Name.startswith("$d"))
+ MappingSymbols.emplace_back(Address - SectionAddr, 'd');
+ if (Name.startswith("$x"))
+ MappingSymbols.emplace_back(Address - SectionAddr, 'x');
+ if (Name.startswith("$a"))
+ MappingSymbols.emplace_back(Address - SectionAddr, 'a');
+ if (Name.startswith("$t"))
+ MappingSymbols.emplace_back(Address - SectionAddr, 't');
+ }
+ }
+
+ llvm::sort(MappingSymbols);
+
+ ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(
+ unwrapOrError(Section.getContents(), Obj->getFileName()));
+
+ std::vector<std::unique_ptr<std::string>> SynthesizedLabelNames;
+ if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) {
+ // AMDGPU disassembler uses symbolizer for printing labels
+ addSymbolizer(Ctx, TheTarget, TripleName, DisAsm, SectionAddr, Bytes,
+ Symbols, SynthesizedLabelNames);
+ }
+
+ StringRef SegmentName = getSegmentName(MachO, Section);
+ StringRef SectionName = unwrapOrError(Section.getName(), Obj->getFileName());
+ // If the section has no symbol at the start, just insert a dummy one.
+ if (Symbols.empty() || Symbols[0].Addr != 0) {
+ Symbols.insert(Symbols.begin(),
+ createDummySymbolInfo(Obj, SectionAddr, SectionName,
+ Section.isText() ? ELF::STT_FUNC
+ : ELF::STT_OBJECT));
+ }
+
+ SmallString<40> Comments;
+ raw_svector_ostream CommentStream(Comments);
+
+ uint64_t VMAAdjustment = 0;
+ if (shouldAdjustVA(Section))
+ VMAAdjustment = AdjustVMA;
+
+ // In executable and shared objects, r_offset holds a virtual address.
+ // Subtract SectionAddr from the r_offset field of a relocation to get
+ // the section offset.
+ uint64_t RelAdjustment = Obj->isRelocatableObject() ? 0 : SectionAddr;
+ uint64_t Size;
+ uint64_t Index;
+ bool PrintedSection = false;
+ std::vector<RelocationRef> Rels = RelocMap[Section];
+ 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) {
+ std::string SymbolName = Symbols[SI].Name.str();
+ if (Demangle)
+ SymbolName = demangle(SymbolName);
+
+ // Skip if --disassemble-symbols is not empty and the symbol is not in
+ // the list.
+ if (!DisasmSymbolSet.empty() && !DisasmSymbolSet.count(SymbolName))
+ continue;
+
+ uint64_t Start = Symbols[SI].Addr;
+ if (Start < SectionAddr || StopAddress <= Start)
+ continue;
+ else
+ FoundDisasmSymbolSet.insert(SymbolName);
+
+ // The end is the section end, the beginning of the next symbol, or
+ // --stop-address.
+ uint64_t End = std::min<uint64_t>(SectionAddr + SectSize, StopAddress);
+ if (SI + 1 < SE)
+ End = std::min(End, Symbols[SI + 1].Addr);
+ if (Start >= End || End <= StartAddress)
+ continue;
+ Start -= SectionAddr;
+ End -= SectionAddr;
+
+ if (!PrintedSection) {
+ PrintedSection = true;
+ outs() << "\nDisassembly of section ";
+ if (!SegmentName.empty())
+ outs() << SegmentName << ",";
+ outs() << SectionName << ":\n";
+ }
+
+ outs() << '\n';
+ if (LeadingAddr)
+ outs() << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ",
+ SectionAddr + Start + VMAAdjustment);
+ if (Obj->isXCOFF() && SymbolDescription) {
+ outs() << getXCOFFSymbolDescription(Symbols[SI], 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.
+ if (Section.isVirtual()) {
+ outs() << "...\n";
+ continue;
+ }
+
+ auto Status = DisAsm->onSymbolStart(Symbols[SI], Size,
+ Bytes.slice(Start, End - Start),
+ SectionAddr + Start, CommentStream);
+ // To have round trippable disassembly, we fall back to decoding the
+ // remaining bytes as instructions.
+ //
+ // If there is a failure, we disassemble the failed region as bytes before
+ // falling back. The target is expected to print nothing in this case.
+ //
+ // If there is Success or SoftFail i.e no 'real' failure, we go ahead by
+ // Size bytes before falling back.
+ // So if the entire symbol is 'eaten' by the target:
+ // Start += Size // Now Start = End and we will never decode as
+ // // instructions
+ //
+ // Right now, most targets return None i.e ignore to treat a symbol
+ // separately. But WebAssembly decodes preludes for some symbols.
+ //
+ if (Status.hasValue()) {
+ if (Status.getValue() == MCDisassembler::Fail) {
+ outs() << "// Error in decoding " << SymbolName
+ << " : Decoding failed region as bytes.\n";
+ for (uint64_t I = 0; I < Size; ++I) {
+ outs() << "\t.byte\t " << format_hex(Bytes[I], 1, /*Upper=*/true)
+ << "\n";
+ }
+ }
+ } else {
+ Size = 0;
+ }
+
+ Start += Size;
+
+ Index = Start;
+ if (SectionAddr < StartAddress)
+ Index = std::max<uint64_t>(Index, StartAddress - SectionAddr);
+
+ // If there is a data/common symbol inside an ELF text section and we are
+ // only disassembling text (applicable all architectures), we are in a
+ // situation where we must print the data and not disassemble it.
+ if (Obj->isELF() && !DisassembleAll && Section.isText()) {
+ uint8_t SymTy = Symbols[SI].Type;
+ if (SymTy == ELF::STT_OBJECT || SymTy == ELF::STT_COMMON) {
+ dumpELFData(SectionAddr, Index, End, Bytes);
+ Index = End;
+ }
+ }
+
+ bool CheckARMELFData = hasMappingSymbols(Obj) &&
+ Symbols[SI].Type != ELF::STT_OBJECT &&
+ !DisassembleAll;
+ bool DumpARMELFData = false;
+ formatted_raw_ostream FOS(outs());
+
+ std::unordered_map<uint64_t, std::string> AllLabels;
+ if (SymbolizeOperands)
+ collectLocalBranchTargets(Bytes, MIA, DisAsm, IP, PrimarySTI,
+ SectionAddr, Index, End, AllLabels);
+
+ while (Index < End) {
+ // ARM and AArch64 ELF binaries can interleave data and text in the
+ // 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 (CheckARMELFData) {
+ char Kind = getMappingSymbolKind(MappingSymbols, Index);
+ DumpARMELFData = Kind == 'd';
+ if (SecondarySTI) {
+ if (Kind == 'a') {
+ STI = PrimaryIsThumb ? SecondarySTI : PrimarySTI;
+ DisAsm = PrimaryIsThumb ? SecondaryDisAsm : PrimaryDisAsm;
+ } else if (Kind == 't') {
+ STI = PrimaryIsThumb ? PrimarySTI : SecondarySTI;
+ DisAsm = PrimaryIsThumb ? PrimaryDisAsm : SecondaryDisAsm;
+ }
+ }
+ }
+
+ if (DumpARMELFData) {
+ Size = dumpARMELFData(SectionAddr, Index, End, Obj, Bytes,
+ MappingSymbols, FOS);
+ } else {
+ // When -z or --disassemble-zeroes are given we always dissasemble
+ // them. Otherwise we might want to skip zero bytes we see.
+ if (!DisassembleZeroes) {
+ uint64_t MaxOffset = End - Index;
+ // For --reloc: print zero blocks patched by relocations, so that
+ // relocations can be shown in the dump.
+ if (RelCur != RelEnd)
+ MaxOffset = std::min(RelCur->getOffset() - RelAdjustment - Index,
+ MaxOffset);
+
+ if (size_t N =
+ countSkippableZeroBytes(Bytes.slice(Index, MaxOffset))) {
+ FOS << "\t\t..." << '\n';
+ Index += N;
+ continue;
+ }
+ }
+
+ // Print local label if there's any.
+ auto Iter = AllLabels.find(SectionAddr + Index);
+ if (Iter != AllLabels.end())
+ FOS << "<" << Iter->second << ">:\n";
+
+ // Disassemble a real instruction or a data when disassemble all is
+ // provided
+ MCInst Inst;
+ bool Disassembled =
+ DisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
+ SectionAddr + Index, CommentStream);
+ if (Size == 0)
+ Size = 1;
+
+ LVP.update({Index, Section.getIndex()},
+ {Index + Size, Section.getIndex()}, Index + Size != End);
+
+ IP->setCommentStream(CommentStream);
+
+ PIP.printInst(
+ *IP, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size),
+ {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, FOS,
+ "", *STI, &SP, Obj->getFileName(), &Rels, LVP);
+
+ IP->setCommentStream(llvm::nulls());
+
+ // If disassembly has failed, avoid analysing invalid/incomplete
+ // instruction information. Otherwise, try to resolve the target
+ // address (jump target or memory operand address) and print it on the
+ // right of the instruction.
+ if (Disassembled && MIA) {
+ // Branch targets are printed just after the instructions.
+ llvm::raw_ostream *TargetOS = &FOS;
+ uint64_t Target;
+ bool PrintTarget =
+ MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target);
+ if (!PrintTarget)
+ if (Optional<uint64_t> MaybeTarget =
+ MIA->evaluateMemoryOperandAddress(
+ Inst, STI, SectionAddr + Index, Size)) {
+ Target = *MaybeTarget;
+ PrintTarget = true;
+ // Do not print real address when symbolizing.
+ if (!SymbolizeOperands) {
+ // Memory operand addresses are printed as comments.
+ TargetOS = &CommentStream;
+ *TargetOS << "0x" << Twine::utohexstr(Target);
+ }
+ }
+ if (PrintTarget) {
+ // In a relocatable object, the target's section must reside in
+ // the same section as the call instruction or it is accessed
+ // through a relocation.
+ //
+ // In a non-relocatable object, the target may be in any section.
+ // In that case, locate the section(s) containing the target
+ // address and find the symbol in one of those, if possible.
+ //
+ // N.B. We don't walk the relocations in the relocatable case yet.
+ std::vector<const SectionSymbolsTy *> TargetSectionSymbols;
+ if (!Obj->isRelocatableObject()) {
+ auto It = llvm::partition_point(
+ SectionAddresses,
+ [=](const std::pair<uint64_t, SectionRef> &O) {
+ return O.first <= Target;
+ });
+ uint64_t TargetSecAddr = 0;
+ while (It != SectionAddresses.begin()) {
+ --It;
+ if (TargetSecAddr == 0)
+ TargetSecAddr = It->first;
+ if (It->first != TargetSecAddr)
+ break;
+ TargetSectionSymbols.push_back(&AllSymbols[It->second]);
+ }
+ } else {
+ TargetSectionSymbols.push_back(&Symbols);
+ }
+ TargetSectionSymbols.push_back(&AbsoluteSymbols);
+
+ // Find the last symbol in the first candidate section whose
+ // offset is less than or equal to the target. If there are no
+ // such symbols, try in the next section and so on, before finally
+ // using the nearest preceding absolute symbol (if any), if there
+ // are no other valid symbols.
+ const SymbolInfoTy *TargetSym = nullptr;
+ for (const SectionSymbolsTy *TargetSymbols :
+ TargetSectionSymbols) {
+ auto It = llvm::partition_point(
+ *TargetSymbols,
+ [=](const SymbolInfoTy &O) { return O.Addr <= Target; });
+ if (It != TargetSymbols->begin()) {
+ TargetSym = &*(It - 1);
+ break;
+ }
+ }
+
+ // Print the labels corresponding to the target if there's any.
+ bool LabelAvailable = AllLabels.count(Target);
+ if (TargetSym != nullptr) {
+ uint64_t TargetAddress = TargetSym->Addr;
+ uint64_t Disp = Target - TargetAddress;
+ std::string TargetName = TargetSym->Name.str();
+ if (Demangle)
+ TargetName = demangle(TargetName);
+
+ *TargetOS << " <";
+ if (!Disp) {
+ // Always Print the binary symbol precisely corresponding to
+ // the target address.
+ *TargetOS << TargetName;
+ } else if (!LabelAvailable) {
+ // Always Print the binary symbol plus an offset if there's no
+ // local label corresponding to the target address.
+ *TargetOS << TargetName << "+0x" << Twine::utohexstr(Disp);
+ } else {
+ *TargetOS << AllLabels[Target];
+ }
+ *TargetOS << ">";
+ } else if (LabelAvailable) {
+ *TargetOS << " <" << AllLabels[Target] << ">";
+ }
+ // By convention, each record in the comment stream should be
+ // terminated.
+ if (TargetOS == &CommentStream)
+ *TargetOS << "\n";
+ }
+ }
+ }
+
+ assert(Ctx.getAsmInfo());
+ emitPostInstructionInfo(FOS, *Ctx.getAsmInfo(), *STI,
+ CommentStream.str(), LVP);
+ Comments.clear();
+
+ // Hexagon does this in pretty printer
+ if (Obj->getArch() != Triple::hexagon) {
+ // Print relocation for instruction and data.
+ while (RelCur != RelEnd) {
+ uint64_t Offset = RelCur->getOffset() - RelAdjustment;
+ // If this relocation is hidden, skip it.
+ if (getHidden(*RelCur) || SectionAddr + Offset < StartAddress) {
+ ++RelCur;
+ continue;
+ }
+
+ // Stop when RelCur's offset is past the disassembled
+ // instruction/data. Note that it's possible the disassembled data
+ // is not the complete data: we might see the relocation printed in
+ // the middle of the data, but this matches the binutils objdump
+ // output.
+ if (Offset >= Index + Size)
+ break;
+
+ // When --adjust-vma is used, update the address printed.
+ if (RelCur->getSymbol() != Obj->symbol_end()) {
+ Expected<section_iterator> SymSI =
+ RelCur->getSymbol()->getSection();
+ if (SymSI && *SymSI != Obj->section_end() &&
+ shouldAdjustVA(**SymSI))
+ Offset += AdjustVMA;
+ }
+
+ printRelocation(FOS, Obj->getFileName(), *RelCur,
+ SectionAddr + Offset, Is64Bits);
+ LVP.printAfterOtherLine(FOS, true);
+ ++RelCur;
+ }
+ }
+
+ Index += Size;
+ }
+ }
+ }
+ StringSet<> MissingDisasmSymbolSet =
+ set_difference(DisasmSymbolSet, FoundDisasmSymbolSet);
+ for (StringRef Sym : MissingDisasmSymbolSet.keys())
+ reportWarning("failed to disassemble missing symbol " + Sym, FileName);
+}
+
+static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
+ const Target *TheTarget = getTarget(Obj);
+
+ // Package up features to be passed to target/subtarget
+ SubtargetFeatures Features = Obj->getFeatures();
+ if (!MAttrs.empty())
+ for (unsigned I = 0; I != MAttrs.size(); ++I)
+ Features.AddFeature(MAttrs[I]);
+
+ std::unique_ptr<const MCRegisterInfo> MRI(
+ TheTarget->createMCRegInfo(TripleName));
+ if (!MRI)
+ reportError(Obj->getFileName(),
+ "no register info for target " + TripleName);
+
+ // Set up disassembler.
+ MCTargetOptions MCOptions;
+ std::unique_ptr<const MCAsmInfo> AsmInfo(
+ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ if (!AsmInfo)
+ reportError(Obj->getFileName(),
+ "no assembly info for target " + TripleName);
+
+ if (MCPU.empty())
+ MCPU = Obj->tryGetCPUName().getValueOr("").str();
+
+ std::unique_ptr<const MCSubtargetInfo> STI(
+ TheTarget->createMCSubtargetInfo(TripleName, MCPU, Features.getString()));
+ if (!STI)
+ reportError(Obj->getFileName(),
+ "no subtarget info for target " + TripleName);
+ std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo());
+ if (!MII)
+ reportError(Obj->getFileName(),
+ "no instruction info for target " + TripleName);
+ MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get());
+ // FIXME: for now initialize MCObjectFileInfo with default values
+ std::unique_ptr<MCObjectFileInfo> MOFI(
+ TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
+ Ctx.setObjectFileInfo(MOFI.get());
+
+ std::unique_ptr<MCDisassembler> DisAsm(
+ TheTarget->createMCDisassembler(*STI, Ctx));
+ if (!DisAsm)
+ reportError(Obj->getFileName(), "no disassembler for target " + TripleName);
+
+ // If we have an ARM object file, we need a second disassembler, because
+ // ARM CPUs have two different instruction sets: ARM mode, and Thumb mode.
+ // We use mapping symbols to switch between the two assemblers, where
+ // appropriate.
+ std::unique_ptr<MCDisassembler> SecondaryDisAsm;
+ std::unique_ptr<const MCSubtargetInfo> SecondarySTI;
+ if (isArmElf(Obj) && !STI->checkFeatures("+mclass")) {
+ if (STI->checkFeatures("+thumb-mode"))
+ Features.AddFeature("-thumb-mode");
+ else
+ Features.AddFeature("+thumb-mode");
+ SecondarySTI.reset(TheTarget->createMCSubtargetInfo(TripleName, MCPU,
+ Features.getString()));
+ SecondaryDisAsm.reset(TheTarget->createMCDisassembler(*SecondarySTI, Ctx));
+ }
+
+ std::unique_ptr<const MCInstrAnalysis> MIA(
+ TheTarget->createMCInstrAnalysis(MII.get()));
+
+ int AsmPrinterVariant = AsmInfo->getAssemblerDialect();
+ std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
+ Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI));
+ if (!IP)
+ reportError(Obj->getFileName(),
+ "no instruction printer for target " + TripleName);
+ IP->setPrintImmHex(PrintImmHex);
+ IP->setPrintBranchImmAsAddress(true);
+ IP->setSymbolizeOperands(SymbolizeOperands);
+ IP->setMCInstrAnalysis(MIA.get());
+
+ PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName));
+ SourcePrinter SP(Obj, TheTarget->getName());
+
+ for (StringRef Opt : DisassemblerOptions)
+ if (!IP->applyTargetSpecificCLOption(Opt))
+ reportError(Obj->getFileName(),
+ "Unrecognized disassembler option: " + Opt);
+
+ disassembleObject(TheTarget, Obj, Ctx, DisAsm.get(), SecondaryDisAsm.get(),
+ MIA.get(), IP.get(), STI.get(), SecondarySTI.get(), PIP,
+ SP, InlineRelocs);
+}
+
+void objdump::printRelocations(const ObjectFile *Obj) {
+ StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 :
+ "%08" PRIx64;
+ // Regular objdump doesn't print relocations in non-relocatable object
+ // files.
+ if (!Obj->isRelocatableObject())
+ return;
+
+ // Build a mapping from relocation target to a vector of relocation
+ // sections. Usually, there is an only one relocation section for
+ // each relocated section.
+ MapVector<SectionRef, std::vector<SectionRef>> SecToRelSec;
+ uint64_t Ndx;
+ for (const SectionRef &Section : ToolSectionFilter(*Obj, &Ndx)) {
+ if (Section.relocation_begin() == Section.relocation_end())
+ continue;
+ Expected<section_iterator> SecOrErr = Section.getRelocatedSection();
+ if (!SecOrErr)
+ reportError(Obj->getFileName(),
+ "section (" + Twine(Ndx) +
+ "): unable to get a relocation target: " +
+ toString(SecOrErr.takeError()));
+ SecToRelSec[**SecOrErr].push_back(Section);
+ }
+
+ for (std::pair<SectionRef, std::vector<SectionRef>> &P : SecToRelSec) {
+ StringRef SecName = unwrapOrError(P.first.getName(), Obj->getFileName());
+ outs() << "\nRELOCATION RECORDS FOR [" << SecName << "]:\n";
+ uint32_t OffsetPadding = (Obj->getBytesInAddress() > 4 ? 16 : 8);
+ uint32_t TypePadding = 24;
+ outs() << left_justify("OFFSET", OffsetPadding) << " "
+ << left_justify("TYPE", TypePadding) << " "
+ << "VALUE\n";
+
+ for (SectionRef Section : P.second) {
+ for (const RelocationRef &Reloc : Section.relocations()) {
+ uint64_t Address = Reloc.getOffset();
+ SmallString<32> RelocName;
+ SmallString<32> ValueStr;
+ if (Address < StartAddress || Address > StopAddress || getHidden(Reloc))
+ continue;
+ Reloc.getTypeName(RelocName);
+ if (Error E = getRelocationValueString(Reloc, ValueStr))
+ reportError(std::move(E), Obj->getFileName());
+
+ outs() << format(Fmt.data(), Address) << " "
+ << left_justify(RelocName, TypePadding) << " " << ValueStr
+ << "\n";
+ }
+ }
+ }
+}
+
+void objdump::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 || !any_of(Elf->sections(), [](const ELFSectionRef Sec) {
+ return Sec.getType() == ELF::SHT_DYNAMIC;
+ })) {
+ reportError(Obj->getFileName(), "not a dynamic object");
+ return;
+ }
+
+ std::vector<SectionRef> DynRelSec = Obj->dynamic_relocation_sections();
+ if (DynRelSec.empty())
+ return;
+
+ outs() << "\nDYNAMIC RELOCATION RECORDS\n";
+ const uint32_t OffsetPadding = (Obj->getBytesInAddress() > 4 ? 16 : 8);
+ const uint32_t TypePadding = 24;
+ outs() << left_justify("OFFSET", OffsetPadding) << ' '
+ << left_justify("TYPE", TypePadding) << " VALUE\n";
+
+ StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
+ for (const SectionRef &Section : DynRelSec)
+ for (const RelocationRef &Reloc : Section.relocations()) {
+ uint64_t Address = Reloc.getOffset();
+ SmallString<32> RelocName;
+ SmallString<32> ValueStr;
+ Reloc.getTypeName(RelocName);
+ if (Error E = getRelocationValueString(Reloc, ValueStr))
+ reportError(std::move(E), Obj->getFileName());
+ outs() << format(Fmt.data(), Address) << ' '
+ << left_justify(RelocName, TypePadding) << ' ' << ValueStr << '\n';
+ }
+}
+
+// Returns true if we need to show LMA column when dumping section headers. We
+// show it only when the platform is ELF and either we have at least one section
+// whose VMA and LMA are different and/or when --show-lma flag is used.
+static bool shouldDisplayLMA(const ObjectFile *Obj) {
+ if (!Obj->isELF())
+ return false;
+ for (const SectionRef &S : ToolSectionFilter(*Obj))
+ if (S.getAddress() != getELFSectionLMA(S))
+ return true;
+ return ShowLMA;
+}
+
+static size_t getMaxSectionNameWidth(const ObjectFile *Obj) {
+ // Default column width for names is 13 even if no names are that long.
+ size_t MaxWidth = 13;
+ for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
+ StringRef Name = unwrapOrError(Section.getName(), Obj->getFileName());
+ MaxWidth = std::max(MaxWidth, Name.size());
+ }
+ return MaxWidth;
+}
+
+void objdump::printSectionHeaders(const ObjectFile *Obj) {
+ size_t NameWidth = getMaxSectionNameWidth(Obj);
+ size_t AddressWidth = 2 * Obj->getBytesInAddress();
+ bool HasLMAColumn = shouldDisplayLMA(Obj);
+ outs() << "\nSections:\n";
+ if (HasLMAColumn)
+ outs() << "Idx " << left_justify("Name", NameWidth) << " Size "
+ << left_justify("VMA", AddressWidth) << " "
+ << left_justify("LMA", AddressWidth) << " Type\n";
+ else
+ outs() << "Idx " << left_justify("Name", NameWidth) << " Size "
+ << left_justify("VMA", AddressWidth) << " Type\n";
+
+ uint64_t Idx;
+ for (const SectionRef &Section : ToolSectionFilter(*Obj, &Idx)) {
+ StringRef Name = unwrapOrError(Section.getName(), Obj->getFileName());
+ uint64_t VMA = Section.getAddress();
+ if (shouldAdjustVA(Section))
+ VMA += AdjustVMA;
+
+ uint64_t Size = Section.getSize();
+
+ std::string Type = Section.isText() ? "TEXT" : "";
+ if (Section.isData())
+ Type += Type.empty() ? "DATA" : ", DATA";
+ if (Section.isBSS())
+ Type += Type.empty() ? "BSS" : ", BSS";
+ if (Section.isDebugSection())
+ Type += Type.empty() ? "DEBUG" : ", DEBUG";
+
+ if (HasLMAColumn)
+ outs() << format("%3" PRIu64 " %-*s %08" PRIx64 " ", Idx, NameWidth,
+ Name.str().c_str(), Size)
+ << format_hex_no_prefix(VMA, AddressWidth) << " "
+ << format_hex_no_prefix(getELFSectionLMA(Section), AddressWidth)
+ << " " << Type << "\n";
+ else
+ outs() << format("%3" PRIu64 " %-*s %08" PRIx64 " ", Idx, NameWidth,
+ Name.str().c_str(), Size)
+ << format_hex_no_prefix(VMA, AddressWidth) << " " << Type << "\n";
+ }
+}
+
+void objdump::printSectionContents(const ObjectFile *Obj) {
+ const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(Obj);
+
+ for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
+ StringRef Name = unwrapOrError(Section.getName(), Obj->getFileName());
+ uint64_t BaseAddr = Section.getAddress();
+ uint64_t Size = Section.getSize();
+ if (!Size)
+ continue;
+
+ outs() << "Contents of section ";
+ StringRef SegmentName = getSegmentName(MachO, Section);
+ if (!SegmentName.empty())
+ outs() << SegmentName << ",";
+ outs() << Name << ":\n";
+ if (Section.isBSS()) {
+ outs() << format("<skipping contents of bss section at [%04" PRIx64
+ ", %04" PRIx64 ")>\n",
+ BaseAddr, BaseAddr + Size);
+ continue;
+ }
+
+ StringRef Contents = unwrapOrError(Section.getContents(), Obj->getFileName());
+
+ // 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);
+ // Dump line of hex.
+ 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);
+ 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];
+ else
+ outs() << ".";
+ }
+ outs() << "\n";
+ }
+ }
+}
+
+void objdump::printSymbolTable(const ObjectFile *O, StringRef ArchiveName,
+ StringRef ArchitectureName, bool DumpDynamic) {
+ if (O->isCOFF() && !DumpDynamic) {
+ outs() << "\nSYMBOL TABLE:\n";
+ printCOFFSymbolTable(cast<const COFFObjectFile>(O));
+ return;
+ }
+
+ const StringRef FileName = O->getFileName();
+
+ if (!DumpDynamic) {
+ outs() << "\nSYMBOL TABLE:\n";
+ for (auto I = O->symbol_begin(); I != O->symbol_end(); ++I)
+ printSymbol(O, *I, {}, FileName, ArchiveName, ArchitectureName,
+ DumpDynamic);
+ return;
+ }
+
+ outs() << "\nDYNAMIC SYMBOL TABLE:\n";
+ if (!O->isELF()) {
+ reportWarning(
+ "this operation is not currently supported for this file format",
+ FileName);
+ return;
+ }
+
+ const ELFObjectFileBase *ELF = cast<const ELFObjectFileBase>(O);
+ auto Symbols = ELF->getDynamicSymbolIterators();
+ Expected<std::vector<VersionEntry>> SymbolVersionsOrErr =
+ ELF->readDynsymVersions();
+ if (!SymbolVersionsOrErr) {
+ reportWarning(toString(SymbolVersionsOrErr.takeError()), FileName);
+ SymbolVersionsOrErr = std::vector<VersionEntry>();
+ (void)!SymbolVersionsOrErr;
+ }
+ for (auto &Sym : Symbols)
+ printSymbol(O, Sym, *SymbolVersionsOrErr, FileName, ArchiveName,
+ ArchitectureName, DumpDynamic);
+}
+
+void objdump::printSymbol(const ObjectFile *O, const SymbolRef &Symbol,
+ ArrayRef<VersionEntry> SymbolVersions,
+ StringRef FileName, StringRef ArchiveName,
+ StringRef ArchitectureName, bool DumpDynamic) {
+ const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(O);
+ uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName, ArchiveName,
+ ArchitectureName);
+ if ((Address < StartAddress) || (Address > StopAddress))
+ return;
+ SymbolRef::Type Type =
+ unwrapOrError(Symbol.getType(), FileName, ArchiveName, ArchitectureName);
+ uint32_t Flags =
+ unwrapOrError(Symbol.getFlags(), FileName, ArchiveName, ArchitectureName);
+
+ // Don't ask a Mach-O STAB symbol for its section unless you know that
+ // STAB symbol's section field refers to a valid section index. Otherwise
+ // the symbol may error trying to load a section that does not exist.
+ bool IsSTAB = false;
+ if (MachO) {
+ DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
+ uint8_t NType =
+ (MachO->is64Bit() ? MachO->getSymbol64TableEntry(SymDRI).n_type
+ : MachO->getSymbolTableEntry(SymDRI).n_type);
+ if (NType & MachO::N_STAB)
+ IsSTAB = true;
+ }
+ section_iterator Section = IsSTAB
+ ? O->section_end()
+ : unwrapOrError(Symbol.getSection(), FileName,
+ ArchiveName, ArchitectureName);
+
+ StringRef Name;
+ if (Type == SymbolRef::ST_Debug && Section != O->section_end()) {
+ if (Expected<StringRef> NameOrErr = Section->getName())
+ Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ } else {
+ Name = unwrapOrError(Symbol.getName(), FileName, ArchiveName,
+ ArchitectureName);
+ }
+
+ bool Global = Flags & SymbolRef::SF_Global;
+ bool Weak = Flags & SymbolRef::SF_Weak;
+ bool Absolute = Flags & SymbolRef::SF_Absolute;
+ bool Common = Flags & SymbolRef::SF_Common;
+ bool Hidden = Flags & SymbolRef::SF_Hidden;
+
+ char GlobLoc = ' ';
+ if ((Section != O->section_end() || Absolute) && !Weak)
+ GlobLoc = Global ? 'g' : 'l';
+ char IFunc = ' ';
+ if (O->isELF()) {
+ if (ELFSymbolRef(Symbol).getELFType() == ELF::STT_GNU_IFUNC)
+ IFunc = 'i';
+ if (ELFSymbolRef(Symbol).getBinding() == ELF::STB_GNU_UNIQUE)
+ GlobLoc = 'u';
+ }
+
+ char Debug = ' ';
+ if (DumpDynamic)
+ Debug = 'D';
+ else if (Type == SymbolRef::ST_Debug || Type == SymbolRef::ST_File)
+ Debug = 'd';
+
+ char FileFunc = ' ';
+ if (Type == SymbolRef::ST_File)
+ 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 : "%08" PRIx64;
+
+ outs() << format(Fmt, Address) << " "
+ << GlobLoc // Local -> 'l', Global -> 'g', Neither -> ' '
+ << (Weak ? 'w' : ' ') // Weak?
+ << ' ' // Constructor. Not supported yet.
+ << ' ' // Warning. Not supported yet.
+ << IFunc // Indirect reference to another symbol.
+ << Debug // Debugging (d) or dynamic (D) symbol.
+ << FileFunc // Name of function (F), file (f) or object (O).
+ << ' ';
+ if (Absolute) {
+ outs() << "*ABS*";
+ } else if (Common) {
+ outs() << "*COM*";
+ } else if (Section == O->section_end()) {
+ if (O->isXCOFF()) {
+ XCOFFSymbolRef XCOFFSym = dyn_cast<const XCOFFObjectFile>(O)->toSymbolRef(
+ Symbol.getRawDataRefImpl());
+ if (XCOFF::N_DEBUG == XCOFFSym.getSectionNumber())
+ outs() << "*DEBUG*";
+ else
+ outs() << "*UND*";
+ } else
+ outs() << "*UND*";
+ } else {
+ StringRef SegmentName = getSegmentName(MachO, *Section);
+ if (!SegmentName.empty())
+ outs() << SegmentName << ",";
+ StringRef SectionName = unwrapOrError(Section->getName(), FileName);
+ outs() << SectionName;
+ if (O->isXCOFF()) {
+ Optional<SymbolRef> SymRef = getXCOFFSymbolContainingSymbolRef(
+ dyn_cast<const XCOFFObjectFile>(O), Symbol);
+ if (SymRef) {
+
+ Expected<StringRef> NameOrErr = SymRef.getValue().getName();
+
+ if (NameOrErr) {
+ outs() << " (csect:";
+ std::string SymName(NameOrErr.get());
+
+ if (Demangle)
+ SymName = demangle(SymName);
+
+ if (SymbolDescription)
+ SymName = getXCOFFSymbolDescription(
+ createSymbolInfo(O, SymRef.getValue()), SymName);
+
+ outs() << ' ' << SymName;
+ outs() << ") ";
+ } else
+ reportWarning(toString(NameOrErr.takeError()), FileName);
+ }
+ }
+ }
+
+ if (Common)
+ outs() << '\t' << format(Fmt, static_cast<uint64_t>(Symbol.getAlignment()));
+ else if (O->isXCOFF())
+ outs() << '\t'
+ << format(Fmt, dyn_cast<const XCOFFObjectFile>(O)->getSymbolSize(
+ Symbol.getRawDataRefImpl()));
+ else if (O->isELF())
+ outs() << '\t' << format(Fmt, ELFSymbolRef(Symbol).getSize());
+
+ if (O->isELF()) {
+ if (!SymbolVersions.empty()) {
+ const VersionEntry &Ver =
+ SymbolVersions[Symbol.getRawDataRefImpl().d.b - 1];
+ std::string Str;
+ if (!Ver.Name.empty())
+ Str = Ver.IsVerDef ? ' ' + Ver.Name : '(' + Ver.Name + ')';
+ outs() << ' ' << left_justify(Str, 12);
+ }
+
+ uint8_t Other = ELFSymbolRef(Symbol).getOther();
+ switch (Other) {
+ case ELF::STV_DEFAULT:
+ break;
+ case ELF::STV_INTERNAL:
+ outs() << " .internal";
+ break;
+ case ELF::STV_HIDDEN:
+ outs() << " .hidden";
+ break;
+ case ELF::STV_PROTECTED:
+ outs() << " .protected";
+ break;
+ default:
+ outs() << format(" 0x%02x", Other);
+ break;
+ }
+ } else if (Hidden) {
+ outs() << " .hidden";
+ }
+
+ std::string SymName(Name);
+ if (Demangle)
+ SymName = demangle(SymName);
+
+ if (O->isXCOFF() && SymbolDescription)
+ SymName = getXCOFFSymbolDescription(createSymbolInfo(O, Symbol), SymName);
+
+ outs() << ' ' << SymName << '\n';
+}
+
+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))
+ printMachOUnwindInfo(MachO);
+ else
+ // TODO: Extract DWARF dump tool to objdump.
+ WithColor::error(errs(), ToolName)
+ << "This operation is only currently supported "
+ "for COFF and MachO object files.\n";
+}
+
+/// Dump the raw contents of the __clangast section so the output can be piped
+/// into llvm-bcanalyzer.
+static void printRawClangAST(const ObjectFile *Obj) {
+ if (outs().is_displayed()) {
+ 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;
+ }
+
+ StringRef ClangASTSectionName("__clangast");
+ if (Obj->isCOFF()) {
+ ClangASTSectionName = "clangast";
+ }
+
+ Optional<object::SectionRef> ClangASTSection;
+ for (auto Sec : ToolSectionFilter(*Obj)) {
+ StringRef Name;
+ if (Expected<StringRef> NameOrErr = Sec.getName())
+ Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ if (Name == ClangASTSectionName) {
+ ClangASTSection = Sec;
+ break;
+ }
+ }
+ if (!ClangASTSection)
+ return;
+
+ StringRef ClangASTContents = unwrapOrError(
+ ClangASTSection.getValue().getContents(), Obj->getFileName());
+ outs().write(ClangASTContents.data(), ClangASTContents.size());
+}
+
+static void printFaultMaps(const ObjectFile *Obj) {
+ StringRef FaultMapSectionName;
+
+ if (Obj->isELF()) {
+ FaultMapSectionName = ".llvm_faultmaps";
+ } else if (Obj->isMachO()) {
+ FaultMapSectionName = "__llvm_faultmaps";
+ } else {
+ WithColor::error(errs(), ToolName)
+ << "This operation is only currently supported "
+ "for ELF and Mach-O executable files.\n";
+ return;
+ }
+
+ Optional<object::SectionRef> FaultMapSection;
+
+ for (auto Sec : ToolSectionFilter(*Obj)) {
+ StringRef Name;
+ if (Expected<StringRef> NameOrErr = Sec.getName())
+ Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ if (Name == FaultMapSectionName) {
+ FaultMapSection = Sec;
+ break;
+ }
+ }
+
+ outs() << "FaultMap table:\n";
+
+ if (!FaultMapSection.hasValue()) {
+ outs() << "<not found>\n";
+ return;
+ }
+
+ StringRef FaultMapContents =
+ unwrapOrError(FaultMapSection.getValue().getContents(), Obj->getFileName());
+ FaultMapParser FMP(FaultMapContents.bytes_begin(),
+ FaultMapContents.bytes_end());
+
+ outs() << FMP;
+}
+
+static void printPrivateFileHeaders(const ObjectFile *O, bool OnlyFirst) {
+ if (O->isELF()) {
+ printELFFileHeader(O);
+ printELFDynamicSection(O);
+ printELFSymbolVersionInfo(O);
+ return;
+ }
+ if (O->isCOFF())
+ return printCOFFFileHeader(cast<object::COFFObjectFile>(*O));
+ if (O->isWasm())
+ return printWasmFileHeader(O);
+ if (O->isMachO()) {
+ printMachOFileHeader(O);
+ if (!OnlyFirst)
+ printMachOLoadCommands(O);
+ return;
+ }
+ reportError(O->getFileName(), "Invalid/Unsupported object file format");
+}
+
+static void printFileHeaders(const ObjectFile *O) {
+ if (!O->isELF() && !O->isCOFF())
+ reportError(O->getFileName(), "Invalid/Unsupported object file format");
+
+ Triple::ArchType AT = O->getArch();
+ outs() << "architecture: " << Triple::getArchTypeName(AT) << "\n";
+ uint64_t Address = unwrapOrError(O->getStartAddress(), O->getFileName());
+
+ StringRef Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
+ outs() << "start address: "
+ << "0x" << format(Fmt.data(), Address) << "\n";
+}
+
+static void printArchiveChild(StringRef Filename, const Archive::Child &C) {
+ Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();
+ if (!ModeOrErr) {
+ WithColor::error(errs(), ToolName) << "ill-formed archive entry.\n";
+ consumeError(ModeOrErr.takeError());
+ return;
+ }
+ sys::fs::perms Mode = ModeOrErr.get();
+ outs() << ((Mode & sys::fs::owner_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::owner_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::owner_exe) ? "x" : "-");
+ outs() << ((Mode & sys::fs::group_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::group_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::group_exe) ? "x" : "-");
+ outs() << ((Mode & sys::fs::others_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::others_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::others_exe) ? "x" : "-");
+
+ outs() << " ";
+
+ outs() << format("%d/%d %6" PRId64 " ", unwrapOrError(C.getUID(), Filename),
+ unwrapOrError(C.getGID(), Filename),
+ unwrapOrError(C.getRawSize(), Filename));
+
+ StringRef RawLastModified = C.getRawLastModified();
+ unsigned Seconds;
+ if (RawLastModified.getAsInteger(10, Seconds))
+ outs() << "(date: \"" << RawLastModified
+ << "\" contains non-decimal chars) ";
+ else {
+ // Since ctime(3) returns a 26 character string of the form:
+ // "Sun Sep 16 01:03:52 1973\n\0"
+ // just print 24 characters.
+ time_t t = Seconds;
+ outs() << format("%.24s ", ctime(&t));
+ }
+
+ StringRef Name = "";
+ Expected<StringRef> NameOrErr = C.getName();
+ if (!NameOrErr) {
+ consumeError(NameOrErr.takeError());
+ Name = unwrapOrError(C.getRawName(), Filename);
+ } else {
+ Name = NameOrErr.get();
+ }
+ outs() << Name << "\n";
+}
+
+// For ELF only now.
+static bool shouldWarnForInvalidStartStopAddress(ObjectFile *Obj) {
+ if (const auto *Elf = dyn_cast<ELFObjectFileBase>(Obj)) {
+ if (Elf->getEType() != ELF::ET_REL)
+ return true;
+ }
+ return false;
+}
+
+static void checkForInvalidStartStopAddress(ObjectFile *Obj,
+ uint64_t Start, uint64_t Stop) {
+ if (!shouldWarnForInvalidStartStopAddress(Obj))
+ return;
+
+ for (const SectionRef &Section : Obj->sections())
+ if (ELFSectionRef(Section).getFlags() & ELF::SHF_ALLOC) {
+ uint64_t BaseAddr = Section.getAddress();
+ uint64_t Size = Section.getSize();
+ if ((Start < BaseAddr + Size) && Stop > BaseAddr)
+ return;
+ }
+
+ if (!HasStartAddressFlag)
+ reportWarning("no section has address less than 0x" +
+ Twine::utohexstr(Stop) + " specified by --stop-address",
+ Obj->getFileName());
+ else if (!HasStopAddressFlag)
+ reportWarning("no section has address greater than or equal to 0x" +
+ Twine::utohexstr(Start) + " specified by --start-address",
+ Obj->getFileName());
+ else
+ reportWarning("no section overlaps the range [0x" +
+ Twine::utohexstr(Start) + ",0x" + Twine::utohexstr(Stop) +
+ ") specified by --start-address/--stop-address",
+ Obj->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() << ")";
+ else
+ outs() << O->getFileName();
+ outs() << ":\tfile format " << O->getFileFormatName().lower() << "\n";
+ }
+
+ if (HasStartAddressFlag || HasStopAddressFlag)
+ checkForInvalidStartStopAddress(O, StartAddress, StopAddress);
+
+ // Note: the order here matches GNU objdump for compatability.
+ StringRef ArchiveName = A ? A->getFileName() : "";
+ if (ArchiveHeaders && !MachOOpt && C)
+ printArchiveChild(ArchiveName, *C);
+ if (FileHeaders)
+ printFileHeaders(O);
+ if (PrivateHeaders || FirstPrivateHeader)
+ printPrivateFileHeaders(O, FirstPrivateHeader);
+ if (SectionHeaders)
+ printSectionHeaders(O);
+ if (SymbolTable)
+ printSymbolTable(O, ArchiveName);
+ if (DynamicSymbolTable)
+ printSymbolTable(O, ArchiveName, /*ArchitectureName=*/"",
+ /*DumpDynamic=*/true);
+ if (DwarfDumpType != DIDT_Null) {
+ std::unique_ptr<DIContext> DICtx = DWARFContext::create(*O);
+ // Dump the complete DWARF structure.
+ DIDumpOptions DumpOpts;
+ DumpOpts.DumpType = DwarfDumpType;
+ DICtx->dump(outs(), DumpOpts);
+ }
+ if (Relocations && !Disassemble)
+ printRelocations(O);
+ if (DynamicRelocations)
+ printDynamicRelocations(O);
+ if (SectionContents)
+ printSectionContents(O);
+ if (Disassemble)
+ disassembleObject(O, Relocations);
+ if (UnwindInfo)
+ printUnwindInfo(O);
+
+ // Mach-O specific options:
+ if (ExportsTrie)
+ printExportsTrie(O);
+ if (Rebase)
+ printRebaseTable(O);
+ if (Bind)
+ printBindTable(O);
+ if (LazyBind)
+ printLazyBindTable(O);
+ if (WeakBind)
+ printWeakBindTable(O);
+
+ // Other special sections:
+ if (RawClangAST)
+ printRawClangAST(O);
+ if (FaultMapSection)
+ printFaultMaps(O);
+}
+
+static void dumpObject(const COFFImportFile *I, const Archive *A,
+ const Archive::Child *C = nullptr) {
+ StringRef ArchiveName = A ? A->getFileName() : "";
+
+ // Avoid other output when using a raw option.
+ if (!RawClangAST)
+ outs() << '\n'
+ << ArchiveName << "(" << I->getFileName() << ")"
+ << ":\tfile format COFF-import-file"
+ << "\n\n";
+
+ if (ArchiveHeaders && !MachOOpt && C)
+ printArchiveChild(ArchiveName, *C);
+ if (SymbolTable)
+ printCOFFSymbolTable(I);
+}
+
+/// Dump each object file in \a a;
+static void dumpArchive(const Archive *A) {
+ Error Err = Error::success();
+ unsigned I = -1;
+ for (auto &C : A->children(Err)) {
+ ++I;
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ reportError(std::move(E), getFileNameForError(C, I), A->getFileName());
+ continue;
+ }
+ 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);
+ else
+ reportError(errorCodeToError(object_error::invalid_file_type),
+ A->getFileName());
+ }
+ if (Err)
+ reportError(std::move(Err), A->getFileName());
+}
+
+/// Open file and figure out how to dump it.
+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);
+ return;
+ }
+
+ // Attempt to open the binary.
+ OwningBinary<Binary> OBinary = unwrapOrError(createBinary(file), file);
+ Binary &Binary = *OBinary.getBinary();
+
+ 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
+ reportError(errorCodeToError(object_error::invalid_file_type), file);
+}
+
+template <typename T>
+static void parseIntArg(const llvm::opt::InputArgList &InputArgs, int ID,
+ T &Value) {
+ if (const opt::Arg *A = InputArgs.getLastArg(ID)) {
+ StringRef V(A->getValue());
+ if (!llvm::to_integer(V, Value, 0)) {
+ reportCmdLineError(A->getSpelling() +
+ ": expected a non-negative integer, but got '" + V +
+ "'");
+ }
+ }
+}
+
+static void invalidArgValue(const opt::Arg *A) {
+ reportCmdLineError("'" + StringRef(A->getValue()) +
+ "' is not a valid value for '" + A->getSpelling() + "'");
+}
+
+static std::vector<std::string>
+commaSeparatedValues(const llvm::opt::InputArgList &InputArgs, int ID) {
+ std::vector<std::string> Values;
+ for (StringRef Value : InputArgs.getAllArgValues(ID)) {
+ llvm::SmallVector<StringRef, 2> SplitValues;
+ llvm::SplitString(Value, SplitValues, ",");
+ for (StringRef SplitValue : SplitValues)
+ Values.push_back(SplitValue.str());
+ }
+ return Values;
+}
+
+static void parseOtoolOptions(const llvm::opt::InputArgList &InputArgs) {
+ MachOOpt = true;
+ FullLeadingAddr = true;
+ PrintImmHex = true;
+
+ ArchName = InputArgs.getLastArgValue(OTOOL_arch).str();
+ LinkOptHints = InputArgs.hasArg(OTOOL_C);
+ if (InputArgs.hasArg(OTOOL_d))
+ FilterSections.push_back("__DATA,__data");
+ DylibId = InputArgs.hasArg(OTOOL_D);
+ UniversalHeaders = InputArgs.hasArg(OTOOL_f);
+ DataInCode = InputArgs.hasArg(OTOOL_G);
+ FirstPrivateHeader = InputArgs.hasArg(OTOOL_h);
+ IndirectSymbols = InputArgs.hasArg(OTOOL_I);
+ ShowRawInsn = InputArgs.hasArg(OTOOL_j);
+ PrivateHeaders = InputArgs.hasArg(OTOOL_l);
+ DylibsUsed = InputArgs.hasArg(OTOOL_L);
+ MCPU = InputArgs.getLastArgValue(OTOOL_mcpu_EQ).str();
+ ObjcMetaData = InputArgs.hasArg(OTOOL_o);
+ DisSymName = InputArgs.getLastArgValue(OTOOL_p).str();
+ InfoPlist = InputArgs.hasArg(OTOOL_P);
+ Relocations = InputArgs.hasArg(OTOOL_r);
+ if (const Arg *A = InputArgs.getLastArg(OTOOL_s)) {
+ auto Filter = (A->getValue(0) + StringRef(",") + A->getValue(1)).str();
+ FilterSections.push_back(Filter);
+ }
+ if (InputArgs.hasArg(OTOOL_t))
+ FilterSections.push_back("__TEXT,__text");
+ Verbose = InputArgs.hasArg(OTOOL_v) || InputArgs.hasArg(OTOOL_V) ||
+ InputArgs.hasArg(OTOOL_o);
+ SymbolicOperands = InputArgs.hasArg(OTOOL_V);
+ if (InputArgs.hasArg(OTOOL_x))
+ FilterSections.push_back(",__text");
+ LeadingAddr = LeadingHeaders = !InputArgs.hasArg(OTOOL_X);
+
+ InputFilenames = InputArgs.getAllArgValues(OTOOL_INPUT);
+ if (InputFilenames.empty())
+ reportCmdLineError("no input file");
+
+ for (const Arg *A : InputArgs) {
+ const Option &O = A->getOption();
+ if (O.getGroup().isValid() && O.getGroup().getID() == OTOOL_grp_obsolete) {
+ reportCmdLineWarning(O.getPrefixedName() +
+ " is obsolete and not implemented");
+ }
+ }
+}
+
+static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) {
+ parseIntArg(InputArgs, OBJDUMP_adjust_vma_EQ, AdjustVMA);
+ AllHeaders = InputArgs.hasArg(OBJDUMP_all_headers);
+ ArchName = InputArgs.getLastArgValue(OBJDUMP_arch_name_EQ).str();
+ ArchiveHeaders = InputArgs.hasArg(OBJDUMP_archive_headers);
+ Demangle = InputArgs.hasArg(OBJDUMP_demangle);
+ Disassemble = InputArgs.hasArg(OBJDUMP_disassemble);
+ DisassembleAll = InputArgs.hasArg(OBJDUMP_disassemble_all);
+ SymbolDescription = InputArgs.hasArg(OBJDUMP_symbol_description);
+ DisassembleSymbols =
+ commaSeparatedValues(InputArgs, OBJDUMP_disassemble_symbols_EQ);
+ DisassembleZeroes = InputArgs.hasArg(OBJDUMP_disassemble_zeroes);
+ if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_dwarf_EQ)) {
+ DwarfDumpType = StringSwitch<DIDumpType>(A->getValue())
+ .Case("frames", DIDT_DebugFrame)
+ .Default(DIDT_Null);
+ if (DwarfDumpType == DIDT_Null)
+ invalidArgValue(A);
+ }
+ DynamicRelocations = InputArgs.hasArg(OBJDUMP_dynamic_reloc);
+ FaultMapSection = InputArgs.hasArg(OBJDUMP_fault_map_section);
+ FileHeaders = InputArgs.hasArg(OBJDUMP_file_headers);
+ SectionContents = InputArgs.hasArg(OBJDUMP_full_contents);
+ PrintLines = InputArgs.hasArg(OBJDUMP_line_numbers);
+ InputFilenames = InputArgs.getAllArgValues(OBJDUMP_INPUT);
+ MachOOpt = InputArgs.hasArg(OBJDUMP_macho);
+ MCPU = InputArgs.getLastArgValue(OBJDUMP_mcpu_EQ).str();
+ MAttrs = commaSeparatedValues(InputArgs, OBJDUMP_mattr_EQ);
+ ShowRawInsn = !InputArgs.hasArg(OBJDUMP_no_show_raw_insn);
+ LeadingAddr = !InputArgs.hasArg(OBJDUMP_no_leading_addr);
+ RawClangAST = InputArgs.hasArg(OBJDUMP_raw_clang_ast);
+ Relocations = InputArgs.hasArg(OBJDUMP_reloc);
+ PrintImmHex =
+ InputArgs.hasFlag(OBJDUMP_print_imm_hex, OBJDUMP_no_print_imm_hex, false);
+ PrivateHeaders = InputArgs.hasArg(OBJDUMP_private_headers);
+ FilterSections = InputArgs.getAllArgValues(OBJDUMP_section_EQ);
+ SectionHeaders = InputArgs.hasArg(OBJDUMP_section_headers);
+ ShowLMA = InputArgs.hasArg(OBJDUMP_show_lma);
+ PrintSource = InputArgs.hasArg(OBJDUMP_source);
+ parseIntArg(InputArgs, OBJDUMP_start_address_EQ, StartAddress);
+ HasStartAddressFlag = InputArgs.hasArg(OBJDUMP_start_address_EQ);
+ parseIntArg(InputArgs, OBJDUMP_stop_address_EQ, StopAddress);
+ HasStopAddressFlag = InputArgs.hasArg(OBJDUMP_stop_address_EQ);
+ SymbolTable = InputArgs.hasArg(OBJDUMP_syms);
+ SymbolizeOperands = InputArgs.hasArg(OBJDUMP_symbolize_operands);
+ DynamicSymbolTable = InputArgs.hasArg(OBJDUMP_dynamic_syms);
+ TripleName = InputArgs.getLastArgValue(OBJDUMP_triple_EQ).str();
+ UnwindInfo = InputArgs.hasArg(OBJDUMP_unwind_info);
+ Wide = InputArgs.hasArg(OBJDUMP_wide);
+ Prefix = InputArgs.getLastArgValue(OBJDUMP_prefix).str();
+ parseIntArg(InputArgs, OBJDUMP_prefix_strip, PrefixStrip);
+ if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_debug_vars_EQ)) {
+ DbgVariables = StringSwitch<DebugVarsFormat>(A->getValue())
+ .Case("ascii", DVASCII)
+ .Case("unicode", DVUnicode)
+ .Default(DVInvalid);
+ if (DbgVariables == DVInvalid)
+ invalidArgValue(A);
+ }
+ parseIntArg(InputArgs, OBJDUMP_debug_vars_indent_EQ, DbgIndent);
+
+ parseMachOOptions(InputArgs);
+
+ // Parse -M (--disassembler-options) and deprecated
+ // --x86-asm-syntax={att,intel}.
+ //
+ // Note, for x86, the asm dialect (AssemblerDialect) is initialized when the
+ // MCAsmInfo is constructed. MCInstPrinter::applyTargetSpecificCLOption is
+ // called too late. For now we have to use the internal cl::opt option.
+ const char *AsmSyntax = nullptr;
+ for (const auto *A : InputArgs.filtered(OBJDUMP_disassembler_options_EQ,
+ OBJDUMP_x86_asm_syntax_att,
+ OBJDUMP_x86_asm_syntax_intel)) {
+ switch (A->getOption().getID()) {
+ case OBJDUMP_x86_asm_syntax_att:
+ AsmSyntax = "--x86-asm-syntax=att";
+ continue;
+ case OBJDUMP_x86_asm_syntax_intel:
+ AsmSyntax = "--x86-asm-syntax=intel";
+ continue;
+ }
+
+ SmallVector<StringRef, 2> Values;
+ llvm::SplitString(A->getValue(), Values, ",");
+ for (StringRef V : Values) {
+ if (V == "att")
+ AsmSyntax = "--x86-asm-syntax=att";
+ else if (V == "intel")
+ AsmSyntax = "--x86-asm-syntax=intel";
+ else
+ DisassemblerOptions.push_back(V.str());
+ }
+ }
+ if (AsmSyntax) {
+ const char *Argv[] = {"llvm-objdump", AsmSyntax};
+ llvm::cl::ParseCommandLineOptions(2, Argv);
+ }
+
+ // objdump defaults to a.out if no filenames specified.
+ if (InputFilenames.empty())
+ InputFilenames.push_back("a.out");
+}
+
+int main(int argc, char **argv) {
+ using namespace llvm;
+ InitLLVM X(argc, argv);
+
+ ToolName = argv[0];
+ std::unique_ptr<CommonOptTable> T;
+ OptSpecifier Unknown, HelpFlag, HelpHiddenFlag, VersionFlag;
+
+ StringRef Stem = sys::path::stem(ToolName);
+ auto Is = [=](StringRef Tool) {
+ // We need to recognize the following filenames:
+ //
+ // llvm-objdump -> objdump
+ // llvm-otool-10.exe -> otool
+ // powerpc64-unknown-freebsd13-objdump -> objdump
+ auto I = Stem.rfind_insensitive(Tool);
+ return I != StringRef::npos &&
+ (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()]));
+ };
+ if (Is("otool")) {
+ T = std::make_unique<OtoolOptTable>();
+ Unknown = OTOOL_UNKNOWN;
+ HelpFlag = OTOOL_help;
+ HelpHiddenFlag = OTOOL_help_hidden;
+ VersionFlag = OTOOL_version;
+ } else {
+ T = std::make_unique<ObjdumpOptTable>();
+ Unknown = OBJDUMP_UNKNOWN;
+ HelpFlag = OBJDUMP_help;
+ HelpHiddenFlag = OBJDUMP_help_hidden;
+ VersionFlag = OBJDUMP_version;
+ }
+
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ opt::InputArgList InputArgs =
+ T->parseArgs(argc, argv, Unknown, Saver,
+ [&](StringRef Msg) { reportCmdLineError(Msg); });
+
+ if (InputArgs.size() == 0 || InputArgs.hasArg(HelpFlag)) {
+ T->printHelp(ToolName);
+ return 0;
+ }
+ if (InputArgs.hasArg(HelpHiddenFlag)) {
+ T->printHelp(ToolName, /*ShowHidden=*/true);
+ return 0;
+ }
+
+ // Initialize targets and assembly printers/parsers.
+ InitializeAllTargetInfos();
+ InitializeAllTargetMCs();
+ InitializeAllDisassemblers();
+
+ if (InputArgs.hasArg(VersionFlag)) {
+ cl::PrintVersionMessage();
+ if (!Is("otool")) {
+ outs() << '\n';
+ TargetRegistry::printRegisteredTargetsForVersion(outs());
+ }
+ return 0;
+ }
+
+ if (Is("otool"))
+ parseOtoolOptions(InputArgs);
+ else
+ parseObjdumpOptions(InputArgs);
+
+ if (StartAddress >= StopAddress)
+ reportCmdLineError("start address should be less than stop address");
+
+ // Removes trailing separators from prefix.
+ while (!Prefix.empty() && sys::path::is_separator(Prefix.back()))
+ Prefix.pop_back();
+
+ if (AllHeaders)
+ ArchiveHeaders = FileHeaders = PrivateHeaders = Relocations =
+ SectionHeaders = SymbolTable = true;
+
+ if (DisassembleAll || PrintSource || PrintLines ||
+ !DisassembleSymbols.empty())
+ Disassemble = true;
+
+ if (!ArchiveHeaders && !Disassemble && DwarfDumpType == DIDT_Null &&
+ !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST &&
+ !Relocations && !SectionHeaders && !SectionContents && !SymbolTable &&
+ !DynamicSymbolTable && !UnwindInfo && !FaultMapSection &&
+ !(MachOOpt &&
+ (Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie ||
+ FirstPrivateHeader || FunctionStarts || IndirectSymbols || InfoPlist ||
+ LazyBind || LinkOptHints || ObjcMetaData || Rebase || Rpaths ||
+ UniversalHeaders || WeakBind || !FilterSections.empty()))) {
+ T->printHelp(ToolName);
+ return 2;
+ }
+
+ DisasmSymbolSet.insert(DisassembleSymbols.begin(), DisassembleSymbols.end());
+
+ llvm::for_each(InputFilenames, dumpInput);
+
+ warnOnNoMatchForSections();
+
+ return EXIT_SUCCESS;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/llvm-objdump.h b/contrib/libs/llvm14/tools/llvm-objdump/llvm-objdump.h
new file mode 100644
index 00000000000..61b6215aa5f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/llvm-objdump.h
@@ -0,0 +1,157 @@
+//===--- llvm-objdump.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_OBJDUMP_LLVM_OBJDUMP_H
+#define LLVM_TOOLS_LLVM_OBJDUMP_LLVM_OBJDUMP_H
+
+#include "llvm/ADT/StringSet.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/DataTypes.h"
+
+namespace llvm {
+class StringRef;
+class Twine;
+
+namespace object {
+class RelocationRef;
+struct VersionEntry;
+} // namespace object
+
+namespace objdump {
+
+enum DebugVarsFormat { DVDisabled, DVUnicode, DVASCII, DVInvalid };
+
+extern bool ArchiveHeaders;
+extern int DbgIndent;
+extern DebugVarsFormat DbgVariables;
+extern bool Demangle;
+extern bool Disassemble;
+extern bool DisassembleAll;
+extern DIDumpType DwarfDumpType;
+extern std::vector<std::string> FilterSections;
+extern bool LeadingAddr;
+extern std::vector<std::string> MAttrs;
+extern std::string MCPU;
+extern std::string Prefix;
+extern uint32_t PrefixStrip;
+extern bool PrintImmHex;
+extern bool PrintLines;
+extern bool PrintSource;
+extern bool PrivateHeaders;
+extern bool Relocations;
+extern bool SectionHeaders;
+extern bool SectionContents;
+extern bool ShowRawInsn;
+extern bool SymbolDescription;
+extern bool SymbolTable;
+extern std::string TripleName;
+extern bool UnwindInfo;
+
+extern StringSet<> FoundSectionSet;
+
+typedef std::function<bool(llvm::object::SectionRef const &)> FilterPredicate;
+
+/// A filtered iterator for SectionRefs that skips sections based on some given
+/// predicate.
+class SectionFilterIterator {
+public:
+ SectionFilterIterator(FilterPredicate P,
+ llvm::object::section_iterator const &I,
+ llvm::object::section_iterator const &E)
+ : Predicate(std::move(P)), Iterator(I), End(E) {
+ ScanPredicate();
+ }
+ const llvm::object::SectionRef &operator*() const { return *Iterator; }
+ SectionFilterIterator &operator++() {
+ ++Iterator;
+ ScanPredicate();
+ return *this;
+ }
+ bool operator!=(SectionFilterIterator const &Other) const {
+ return Iterator != Other.Iterator;
+ }
+
+private:
+ void ScanPredicate() {
+ while (Iterator != End && !Predicate(*Iterator)) {
+ ++Iterator;
+ }
+ }
+ FilterPredicate Predicate;
+ llvm::object::section_iterator Iterator;
+ llvm::object::section_iterator End;
+};
+
+/// Creates an iterator range of SectionFilterIterators for a given Object and
+/// predicate.
+class SectionFilter {
+public:
+ SectionFilter(FilterPredicate P, llvm::object::ObjectFile const &O)
+ : Predicate(std::move(P)), Object(O) {}
+ SectionFilterIterator begin() {
+ return SectionFilterIterator(Predicate, Object.section_begin(),
+ Object.section_end());
+ }
+ SectionFilterIterator end() {
+ return SectionFilterIterator(Predicate, Object.section_end(),
+ Object.section_end());
+ }
+
+private:
+ FilterPredicate Predicate;
+ llvm::object::ObjectFile const &Object;
+};
+
+// Various helper functions.
+
+/// Creates a SectionFilter with a standard predicate that conditionally skips
+/// sections when the --section objdump flag is provided.
+///
+/// Idx is an optional output parameter that keeps track of which section index
+/// this is. This may be different than the actual section number, as some
+/// sections may be filtered (e.g. symbol tables).
+SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O,
+ uint64_t *Idx = nullptr);
+
+bool isRelocAddressLess(object::RelocationRef A, object::RelocationRef B);
+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(),
+ bool DumpDynamic = false);
+void printSymbol(const object::ObjectFile *O, const object::SymbolRef &Symbol,
+ ArrayRef<object::VersionEntry> SymbolVersions,
+ StringRef FileName, StringRef ArchiveName,
+ StringRef ArchitectureName, bool DumpDynamic);
+[[noreturn]] void reportError(StringRef File, const Twine &Message);
+[[noreturn]] void reportError(Error E, StringRef FileName,
+ StringRef ArchiveName = "",
+ StringRef ArchitectureName = "");
+void reportWarning(const Twine &Message, StringRef File);
+
+template <typename T, typename... Ts>
+T unwrapOrError(Expected<T> EO, Ts &&... Args) {
+ if (EO)
+ return std::move(*EO);
+ reportError(EO.takeError(), std::forward<Ts>(Args)...);
+}
+
+std::string getFileNameForError(const object::Archive::Child &C,
+ unsigned Index);
+SymbolInfoTy createSymbolInfo(const object::ObjectFile *Obj,
+ const object::SymbolRef &Symbol);
+
+} // namespace objdump
+} // end namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-objdump/ya.make b/contrib/libs/llvm14/tools/llvm-objdump/ya.make
new file mode 100644
index 00000000000..e1462e56b2c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-objdump/ya.make
@@ -0,0 +1,70 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/DebugInfo/MSF
+ contrib/libs/llvm14/lib/DebugInfo/PDB
+ contrib/libs/llvm14/lib/DebugInfo/Symbolize
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-objdump
+ contrib/libs/llvm14/tools/llvm-objdump
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ COFFDump.cpp
+ ELFDump.cpp
+ MachODump.cpp
+ SourcePrinter.cpp
+ WasmDump.cpp
+ XCOFFDump.cpp
+ llvm-objdump.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-opt-report/OptReport.cpp b/contrib/libs/llvm14/tools/llvm-opt-report/OptReport.cpp
new file mode 100644
index 00000000000..e0388632ba5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-opt-report/OptReport.cpp
@@ -0,0 +1,478 @@
+//===------------------ llvm-opt-report/OptReport.cpp ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements a tool that can parse the YAML optimization
+/// records and generate an optimization summary annotated source listing
+/// report.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm-c/Remarks.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Remarks/Remark.h"
+#include "llvm/Remarks/RemarkFormat.h"
+#include "llvm/Remarks/RemarkParser.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstdlib>
+#include <map>
+#include <set>
+
+using namespace llvm;
+
+// Mark all our options with this category, everything else (except for -version
+// and -help) will be hidden.
+static cl::OptionCategory
+ OptReportCategory("llvm-opt-report options");
+
+static cl::opt<std::string>
+ InputFileName(cl::Positional, cl::desc("<input>"), cl::init("-"),
+ cl::cat(OptReportCategory));
+
+static cl::opt<std::string>
+ OutputFileName("o", cl::desc("Output file"), cl::init("-"),
+ cl::cat(OptReportCategory));
+
+static cl::opt<std::string>
+ InputRelDir("r", cl::desc("Root for relative input paths"), cl::init(""),
+ cl::cat(OptReportCategory));
+
+static cl::opt<bool>
+ Succinct("s", cl::desc("Don't include vectorization factors, etc."),
+ cl::init(false), cl::cat(OptReportCategory));
+
+static cl::opt<bool>
+ NoDemangle("no-demangle", cl::desc("Don't demangle function names"),
+ cl::init(false), cl::cat(OptReportCategory));
+
+static cl::opt<std::string> ParserFormat("format",
+ cl::desc("The format of the remarks."),
+ cl::init("yaml"),
+ cl::cat(OptReportCategory));
+
+namespace {
+// For each location in the source file, the common per-transformation state
+// collected.
+struct OptReportLocationItemInfo {
+ bool Analyzed = false;
+ bool Transformed = false;
+
+ OptReportLocationItemInfo &operator |= (
+ const OptReportLocationItemInfo &RHS) {
+ Analyzed |= RHS.Analyzed;
+ Transformed |= RHS.Transformed;
+
+ return *this;
+ }
+
+ bool operator < (const OptReportLocationItemInfo &RHS) const {
+ if (Analyzed < RHS.Analyzed)
+ return true;
+ else if (Analyzed > RHS.Analyzed)
+ return false;
+ else if (Transformed < RHS.Transformed)
+ return true;
+ return false;
+ }
+};
+
+// The per-location information collected for producing an optimization report.
+struct OptReportLocationInfo {
+ OptReportLocationItemInfo Inlined;
+ OptReportLocationItemInfo Unrolled;
+ OptReportLocationItemInfo Vectorized;
+
+ int VectorizationFactor = 1;
+ int InterleaveCount = 1;
+ int UnrollCount = 1;
+
+ OptReportLocationInfo &operator |= (const OptReportLocationInfo &RHS) {
+ Inlined |= RHS.Inlined;
+ Unrolled |= RHS.Unrolled;
+ Vectorized |= RHS.Vectorized;
+
+ VectorizationFactor =
+ std::max(VectorizationFactor, RHS.VectorizationFactor);
+ InterleaveCount = std::max(InterleaveCount, RHS.InterleaveCount);
+ UnrollCount = std::max(UnrollCount, RHS.UnrollCount);
+
+ return *this;
+ }
+
+ bool operator < (const OptReportLocationInfo &RHS) const {
+ if (Inlined < RHS.Inlined)
+ return true;
+ else if (RHS.Inlined < Inlined)
+ return false;
+ else if (Unrolled < RHS.Unrolled)
+ return true;
+ else if (RHS.Unrolled < Unrolled)
+ return false;
+ else if (Vectorized < RHS.Vectorized)
+ return true;
+ else if (RHS.Vectorized < Vectorized || Succinct)
+ return false;
+ else if (VectorizationFactor < RHS.VectorizationFactor)
+ return true;
+ else if (VectorizationFactor > RHS.VectorizationFactor)
+ return false;
+ else if (InterleaveCount < RHS.InterleaveCount)
+ return true;
+ else if (InterleaveCount > RHS.InterleaveCount)
+ return false;
+ else if (UnrollCount < RHS.UnrollCount)
+ return true;
+ return false;
+ }
+};
+
+typedef std::map<std::string, std::map<int, std::map<std::string, std::map<int,
+ OptReportLocationInfo>>>> LocationInfoTy;
+} // anonymous namespace
+
+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;
+ }
+
+ Expected<remarks::Format> Format = remarks::parseFormat(ParserFormat);
+ if (!Format) {
+ handleAllErrors(Format.takeError(), [&](const ErrorInfoBase &PE) {
+ PE.log(WithColor::error());
+ errs() << '\n';
+ });
+ return false;
+ }
+
+ Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
+ remarks::createRemarkParserFromMeta(*Format, (*Buf)->getBuffer());
+ if (!MaybeParser) {
+ handleAllErrors(MaybeParser.takeError(), [&](const ErrorInfoBase &PE) {
+ PE.log(WithColor::error());
+ errs() << '\n';
+ });
+ return false;
+ }
+ remarks::RemarkParser &Parser = **MaybeParser;
+
+ while (true) {
+ Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();
+ if (!MaybeRemark) {
+ Error E = MaybeRemark.takeError();
+ if (E.isA<remarks::EndOfFileError>()) {
+ // EOF.
+ consumeError(std::move(E));
+ break;
+ }
+ handleAllErrors(std::move(E), [&](const ErrorInfoBase &PE) {
+ PE.log(WithColor::error());
+ errs() << '\n';
+ });
+ return false;
+ }
+
+ const remarks::Remark &Remark = **MaybeRemark;
+
+ bool Transformed = Remark.RemarkType == remarks::Type::Passed;
+
+ int VectorizationFactor = 1;
+ int InterleaveCount = 1;
+ int UnrollCount = 1;
+
+ for (const remarks::Argument &Arg : Remark.Args) {
+ if (Arg.Key == "VectorizationFactor")
+ Arg.Val.getAsInteger(10, VectorizationFactor);
+ else if (Arg.Key == "InterleaveCount")
+ Arg.Val.getAsInteger(10, InterleaveCount);
+ else if (Arg.Key == "UnrollCount")
+ Arg.Val.getAsInteger(10, UnrollCount);
+ }
+
+ const Optional<remarks::RemarkLocation> &Loc = Remark.Loc;
+ if (!Loc)
+ continue;
+
+ StringRef File = Loc->SourceFilePath;
+ unsigned Line = Loc->SourceLine;
+ unsigned Column = Loc->SourceColumn;
+
+ // We track information on both actual and potential transformations. This
+ // way, if there are multiple possible things on a line that are, or could
+ // have been transformed, we can indicate that explicitly in the output.
+ auto UpdateLLII = [Transformed](OptReportLocationItemInfo &LLII) {
+ LLII.Analyzed = true;
+ if (Transformed)
+ LLII.Transformed = true;
+ };
+
+ if (Remark.PassName == "inline") {
+ auto &LI = LocationInfo[std::string(File)][Line]
+ [std::string(Remark.FunctionName)][Column];
+ UpdateLLII(LI.Inlined);
+ } else if (Remark.PassName == "loop-unroll") {
+ auto &LI = LocationInfo[std::string(File)][Line]
+ [std::string(Remark.FunctionName)][Column];
+ LI.UnrollCount = UnrollCount;
+ UpdateLLII(LI.Unrolled);
+ } else if (Remark.PassName == "loop-vectorize") {
+ auto &LI = LocationInfo[std::string(File)][Line]
+ [std::string(Remark.FunctionName)][Column];
+ LI.VectorizationFactor = VectorizationFactor;
+ LI.InterleaveCount = InterleaveCount;
+ UpdateLLII(LI.Vectorized);
+ }
+ }
+
+ return true;
+}
+
+static bool writeReport(LocationInfoTy &LocationInfo) {
+ std::error_code EC;
+ llvm::raw_fd_ostream OS(OutputFileName, EC, llvm::sys::fs::OF_TextWithCRLF);
+ if (EC) {
+ WithColor::error() << "Can't open file " << OutputFileName << ": "
+ << EC.message() << "\n";
+ return false;
+ }
+
+ bool FirstFile = true;
+ for (auto &FI : LocationInfo) {
+ SmallString<128> FileName(FI.first);
+ if (!InputRelDir.empty())
+ sys::fs::make_absolute(InputRelDir, FileName);
+
+ const auto &FileInfo = FI.second;
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
+ MemoryBuffer::getFile(FileName);
+ if (std::error_code EC = Buf.getError()) {
+ WithColor::error() << "Can't open file " << FileName << ": "
+ << EC.message() << "\n";
+ return false;
+ }
+
+ if (FirstFile)
+ FirstFile = false;
+ else
+ OS << "\n";
+
+ OS << "< " << FileName << "\n";
+
+ // Figure out how many characters we need for the vectorization factors
+ // and similar.
+ OptReportLocationInfo MaxLI;
+ for (auto &FLI : FileInfo)
+ for (auto &FI : FLI.second)
+ for (auto &LI : FI.second)
+ MaxLI |= LI.second;
+
+ bool NothingInlined = !MaxLI.Inlined.Transformed;
+ bool NothingUnrolled = !MaxLI.Unrolled.Transformed;
+ bool NothingVectorized = !MaxLI.Vectorized.Transformed;
+
+ unsigned VFDigits = llvm::utostr(MaxLI.VectorizationFactor).size();
+ unsigned ICDigits = llvm::utostr(MaxLI.InterleaveCount).size();
+ unsigned UCDigits = llvm::utostr(MaxLI.UnrollCount).size();
+
+ // Figure out how many characters we need for the line numbers.
+ int64_t NumLines = 0;
+ for (line_iterator LI(*Buf.get(), false); LI != line_iterator(); ++LI)
+ ++NumLines;
+
+ unsigned LNDigits = llvm::utostr(NumLines).size();
+
+ for (line_iterator LI(*Buf.get(), false); LI != line_iterator(); ++LI) {
+ int64_t L = LI.line_number();
+ auto LII = FileInfo.find(L);
+
+ auto PrintLine = [&](bool PrintFuncName,
+ const std::set<std::string> &FuncNameSet) {
+ OptReportLocationInfo LLI;
+
+ std::map<int, OptReportLocationInfo> ColsInfo;
+ unsigned InlinedCols = 0, UnrolledCols = 0, VectorizedCols = 0;
+
+ if (LII != FileInfo.end() && !FuncNameSet.empty()) {
+ const auto &LineInfo = LII->second;
+
+ for (auto &CI : LineInfo.find(*FuncNameSet.begin())->second) {
+ int Col = CI.first;
+ ColsInfo[Col] = CI.second;
+ InlinedCols += CI.second.Inlined.Analyzed;
+ UnrolledCols += CI.second.Unrolled.Analyzed;
+ VectorizedCols += CI.second.Vectorized.Analyzed;
+ LLI |= CI.second;
+ }
+ }
+
+ if (PrintFuncName) {
+ OS << " > ";
+
+ bool FirstFunc = true;
+ for (const auto &FuncName : FuncNameSet) {
+ if (FirstFunc)
+ FirstFunc = false;
+ else
+ OS << ", ";
+
+ bool Printed = false;
+ if (!NoDemangle) {
+ int Status = 0;
+ char *Demangled =
+ itaniumDemangle(FuncName.c_str(), nullptr, nullptr, &Status);
+ if (Demangled && Status == 0) {
+ OS << Demangled;
+ Printed = true;
+ }
+
+ if (Demangled)
+ std::free(Demangled);
+ }
+
+ if (!Printed)
+ OS << FuncName;
+ }
+
+ OS << ":\n";
+ }
+
+ // We try to keep the output as concise as possible. If only one thing on
+ // a given line could have been inlined, vectorized, etc. then we can put
+ // the marker on the source line itself. If there are multiple options
+ // then we want to distinguish them by placing the marker for each
+ // transformation on a separate line following the source line. When we
+ // do this, we use a '^' character to point to the appropriate column in
+ // the source line.
+
+ std::string USpaces(Succinct ? 0 : UCDigits, ' ');
+ std::string VSpaces(Succinct ? 0 : VFDigits + ICDigits + 1, ' ');
+
+ auto UStr = [UCDigits](OptReportLocationInfo &LLI) {
+ std::string R;
+ raw_string_ostream RS(R);
+
+ if (!Succinct) {
+ RS << LLI.UnrollCount;
+ RS << std::string(UCDigits - RS.str().size(), ' ');
+ }
+
+ return RS.str();
+ };
+
+ auto VStr = [VFDigits,
+ ICDigits](OptReportLocationInfo &LLI) -> std::string {
+ std::string R;
+ raw_string_ostream RS(R);
+
+ if (!Succinct) {
+ RS << LLI.VectorizationFactor << "," << LLI.InterleaveCount;
+ RS << std::string(VFDigits + ICDigits + 1 - RS.str().size(), ' ');
+ }
+
+ return RS.str();
+ };
+
+ OS << llvm::format_decimal(L, LNDigits) << " ";
+ OS << (LLI.Inlined.Transformed && InlinedCols < 2 ? "I" :
+ (NothingInlined ? "" : " "));
+ OS << (LLI.Unrolled.Transformed && UnrolledCols < 2 ?
+ "U" + UStr(LLI) : (NothingUnrolled ? "" : " " + USpaces));
+ OS << (LLI.Vectorized.Transformed && VectorizedCols < 2 ?
+ "V" + VStr(LLI) : (NothingVectorized ? "" : " " + VSpaces));
+
+ OS << " | " << *LI << "\n";
+
+ for (auto &J : ColsInfo) {
+ if ((J.second.Inlined.Transformed && InlinedCols > 1) ||
+ (J.second.Unrolled.Transformed && UnrolledCols > 1) ||
+ (J.second.Vectorized.Transformed && VectorizedCols > 1)) {
+ OS << std::string(LNDigits + 1, ' ');
+ OS << (J.second.Inlined.Transformed &&
+ InlinedCols > 1 ? "I" : (NothingInlined ? "" : " "));
+ OS << (J.second.Unrolled.Transformed &&
+ UnrolledCols > 1 ? "U" + UStr(J.second) :
+ (NothingUnrolled ? "" : " " + USpaces));
+ OS << (J.second.Vectorized.Transformed &&
+ VectorizedCols > 1 ? "V" + VStr(J.second) :
+ (NothingVectorized ? "" : " " + VSpaces));
+
+ OS << " | " << std::string(J.first - 1, ' ') << "^\n";
+ }
+ }
+ };
+
+ // We need to figure out if the optimizations for this line were the same
+ // in each function context. If not, then we want to group the similar
+ // function contexts together and display each group separately. If
+ // they're all the same, then we only display the line once without any
+ // additional markings.
+ std::map<std::map<int, OptReportLocationInfo>,
+ std::set<std::string>> UniqueLIs;
+
+ OptReportLocationInfo AllLI;
+ if (LII != FileInfo.end()) {
+ const auto &FuncLineInfo = LII->second;
+ for (const auto &FLII : FuncLineInfo) {
+ UniqueLIs[FLII.second].insert(FLII.first);
+
+ for (const auto &OI : FLII.second)
+ AllLI |= OI.second;
+ }
+ }
+
+ bool NothingHappened = !AllLI.Inlined.Transformed &&
+ !AllLI.Unrolled.Transformed &&
+ !AllLI.Vectorized.Transformed;
+ if (UniqueLIs.size() > 1 && !NothingHappened) {
+ OS << " [[\n";
+ for (const auto &FSLI : UniqueLIs)
+ PrintLine(true, FSLI.second);
+ OS << " ]]\n";
+ } else if (UniqueLIs.size() == 1) {
+ PrintLine(false, UniqueLIs.begin()->second);
+ } else {
+ PrintLine(false, std::set<std::string>());
+ }
+ }
+ }
+
+ return true;
+}
+
+int main(int argc, const char **argv) {
+ InitLLVM X(argc, argv);
+
+ cl::HideUnrelatedOptions(OptReportCategory);
+ cl::ParseCommandLineOptions(
+ argc, argv,
+ "A tool to generate an optimization report from YAML optimization"
+ " record files.\n");
+
+ LocationInfoTy LocationInfo;
+ if (!readLocationInfo(LocationInfo))
+ return 1;
+ if (!writeReport(LocationInfo))
+ return 1;
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-opt-report/ya.make b/contrib/libs/llvm14/tools/llvm-opt-report/ya.make
new file mode 100644
index 00000000000..e2f26daed7a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-opt-report/ya.make
@@ -0,0 +1,31 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-opt-report
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ OptReport.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/BytesOutputStyle.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/BytesOutputStyle.cpp
new file mode 100644
index 00000000000..ffc907e09f1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/BytesOutputStyle.cpp
@@ -0,0 +1,491 @@
+//===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "BytesOutputStyle.h"
+
+#include "FormatUtil.h"
+#include "StreamUtil.h"
+#include "llvm-pdbutil.h"
+
+#include "llvm/DebugInfo/CodeView/Formatters.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/MSF/MSFCommon.h"
+#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.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/RawError.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/Support/BinaryStreamReader.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::msf;
+using namespace llvm::pdb;
+
+namespace {
+struct StreamSpec {
+ uint32_t SI = 0;
+ uint32_t Begin = 0;
+ uint32_t Size = 0;
+};
+} // namespace
+
+static Expected<StreamSpec> parseStreamSpec(StringRef Str) {
+ StreamSpec Result;
+ if (Str.consumeInteger(0, Result.SI))
+ return make_error<RawError>(raw_error_code::invalid_format,
+ "Invalid Stream Specification");
+ if (Str.consume_front(":")) {
+ if (Str.consumeInteger(0, Result.Begin))
+ return make_error<RawError>(raw_error_code::invalid_format,
+ "Invalid Stream Specification");
+ }
+ if (Str.consume_front("@")) {
+ if (Str.consumeInteger(0, Result.Size))
+ return make_error<RawError>(raw_error_code::invalid_format,
+ "Invalid Stream Specification");
+ }
+
+ if (!Str.empty())
+ return make_error<RawError>(raw_error_code::invalid_format,
+ "Invalid Stream Specification");
+ return Result;
+}
+
+static SmallVector<StreamSpec, 2> parseStreamSpecs(LinePrinter &P) {
+ SmallVector<StreamSpec, 2> Result;
+
+ for (auto &Str : opts::bytes::DumpStreamData) {
+ auto ESS = parseStreamSpec(Str);
+ if (!ESS) {
+ P.formatLine("Error parsing stream spec {0}: {1}", Str,
+ toString(ESS.takeError()));
+ continue;
+ }
+ Result.push_back(*ESS);
+ }
+ return Result;
+}
+
+static void printHeader(LinePrinter &P, const Twine &S) {
+ P.NewLine();
+ P.formatLine("{0,=60}", S);
+ P.formatLine("{0}", fmt_repeat('=', 60));
+}
+
+BytesOutputStyle::BytesOutputStyle(PDBFile &File)
+ : File(File), P(2, false, outs()) {}
+
+Error BytesOutputStyle::dump() {
+
+ if (opts::bytes::DumpBlockRange.hasValue()) {
+ auto &R = *opts::bytes::DumpBlockRange;
+ uint32_t Max = R.Max.getValueOr(R.Min);
+
+ if (Max < R.Min)
+ return make_error<StringError>(
+ "Invalid block range specified. Max < Min",
+ inconvertibleErrorCode());
+ if (Max >= File.getBlockCount())
+ return make_error<StringError>(
+ "Invalid block range specified. Requested block out of bounds",
+ inconvertibleErrorCode());
+
+ dumpBlockRanges(R.Min, Max);
+ P.NewLine();
+ }
+
+ if (opts::bytes::DumpByteRange.hasValue()) {
+ auto &R = *opts::bytes::DumpByteRange;
+ uint32_t Max = R.Max.getValueOr(File.getFileSize());
+
+ if (Max < R.Min)
+ return make_error<StringError>("Invalid byte range specified. Max < Min",
+ inconvertibleErrorCode());
+ if (Max >= File.getFileSize())
+ return make_error<StringError>(
+ "Invalid byte range specified. Requested byte larger than file size",
+ inconvertibleErrorCode());
+
+ dumpByteRanges(R.Min, Max);
+ P.NewLine();
+ }
+
+ if (opts::bytes::Fpm) {
+ dumpFpm();
+ P.NewLine();
+ }
+
+ if (!opts::bytes::DumpStreamData.empty()) {
+ dumpStreamBytes();
+ P.NewLine();
+ }
+
+ if (opts::bytes::NameMap) {
+ dumpNameMap();
+ P.NewLine();
+ }
+
+ if (opts::bytes::SectionContributions) {
+ dumpSectionContributions();
+ P.NewLine();
+ }
+
+ if (opts::bytes::SectionMap) {
+ dumpSectionMap();
+ P.NewLine();
+ }
+
+ if (opts::bytes::ModuleInfos) {
+ dumpModuleInfos();
+ P.NewLine();
+ }
+
+ if (opts::bytes::FileInfo) {
+ dumpFileInfo();
+ P.NewLine();
+ }
+
+ if (opts::bytes::TypeServerMap) {
+ dumpTypeServerMap();
+ P.NewLine();
+ }
+
+ if (opts::bytes::ECData) {
+ dumpECData();
+ P.NewLine();
+ }
+
+ if (!opts::bytes::TypeIndex.empty()) {
+ dumpTypeIndex(StreamTPI, opts::bytes::TypeIndex);
+ P.NewLine();
+ }
+
+ if (!opts::bytes::IdIndex.empty()) {
+ dumpTypeIndex(StreamIPI, opts::bytes::IdIndex);
+ P.NewLine();
+ }
+
+ if (opts::bytes::ModuleSyms) {
+ dumpModuleSyms();
+ P.NewLine();
+ }
+
+ if (opts::bytes::ModuleC11) {
+ dumpModuleC11();
+ P.NewLine();
+ }
+
+ if (opts::bytes::ModuleC13) {
+ dumpModuleC13();
+ P.NewLine();
+ }
+
+ return Error::success();
+}
+
+void BytesOutputStyle::dumpNameMap() {
+ printHeader(P, "Named Stream Map");
+
+ AutoIndent Indent(P);
+
+ auto &InfoS = Err(File.getPDBInfoStream());
+ BinarySubstreamRef NS = InfoS.getNamedStreamsBuffer();
+ auto Layout = File.getStreamLayout(StreamPDB);
+ P.formatMsfStreamData("Named Stream Map", File, Layout, NS);
+}
+
+void BytesOutputStyle::dumpBlockRanges(uint32_t Min, uint32_t Max) {
+ printHeader(P, "MSF Blocks");
+
+ AutoIndent Indent(P);
+ for (uint32_t I = Min; I <= Max; ++I) {
+ uint64_t Base = I;
+ Base *= File.getBlockSize();
+
+ auto ExpectedData = File.getBlockData(I, File.getBlockSize());
+ if (!ExpectedData) {
+ P.formatLine("Could not get block {0}. Reason = {1}", I,
+ toString(ExpectedData.takeError()));
+ continue;
+ }
+ std::string Label = formatv("Block {0}", I).str();
+ P.formatBinary(Label, *ExpectedData, Base, 0);
+ }
+}
+
+void BytesOutputStyle::dumpSectionContributions() {
+ printHeader(P, "Section Contributions");
+
+ AutoIndent Indent(P);
+
+ auto &DbiS = Err(File.getPDBDbiStream());
+ BinarySubstreamRef NS = DbiS.getSectionContributionData();
+ auto Layout = File.getStreamLayout(StreamDBI);
+ P.formatMsfStreamData("Section Contributions", File, Layout, NS);
+}
+
+void BytesOutputStyle::dumpSectionMap() {
+ printHeader(P, "Section Map");
+
+ AutoIndent Indent(P);
+
+ auto &DbiS = Err(File.getPDBDbiStream());
+ BinarySubstreamRef NS = DbiS.getSecMapSubstreamData();
+ auto Layout = File.getStreamLayout(StreamDBI);
+ P.formatMsfStreamData("Section Map", File, Layout, NS);
+}
+
+void BytesOutputStyle::dumpModuleInfos() {
+ printHeader(P, "Module Infos");
+
+ AutoIndent Indent(P);
+
+ auto &DbiS = Err(File.getPDBDbiStream());
+ BinarySubstreamRef NS = DbiS.getModiSubstreamData();
+ auto Layout = File.getStreamLayout(StreamDBI);
+ P.formatMsfStreamData("Module Infos", File, Layout, NS);
+}
+
+void BytesOutputStyle::dumpFileInfo() {
+ printHeader(P, "File Info");
+
+ AutoIndent Indent(P);
+
+ auto &DbiS = Err(File.getPDBDbiStream());
+ BinarySubstreamRef NS = DbiS.getFileInfoSubstreamData();
+ auto Layout = File.getStreamLayout(StreamDBI);
+ P.formatMsfStreamData("File Info", File, Layout, NS);
+}
+
+void BytesOutputStyle::dumpTypeServerMap() {
+ printHeader(P, "Type Server Map");
+
+ AutoIndent Indent(P);
+
+ auto &DbiS = Err(File.getPDBDbiStream());
+ BinarySubstreamRef NS = DbiS.getTypeServerMapSubstreamData();
+ auto Layout = File.getStreamLayout(StreamDBI);
+ P.formatMsfStreamData("Type Server Map", File, Layout, NS);
+}
+
+void BytesOutputStyle::dumpECData() {
+ printHeader(P, "Edit and Continue Data");
+
+ AutoIndent Indent(P);
+
+ auto &DbiS = Err(File.getPDBDbiStream());
+ BinarySubstreamRef NS = DbiS.getECSubstreamData();
+ auto Layout = File.getStreamLayout(StreamDBI);
+ P.formatMsfStreamData("Edit and Continue Data", File, Layout, NS);
+}
+
+void BytesOutputStyle::dumpTypeIndex(uint32_t StreamIdx,
+ ArrayRef<uint32_t> Indices) {
+ assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
+ assert(!Indices.empty());
+
+ bool IsTpi = (StreamIdx == StreamTPI);
+
+ StringRef Label = IsTpi ? "Type (TPI) Records" : "Index (IPI) Records";
+ printHeader(P, Label);
+ auto &Stream = Err(IsTpi ? File.getPDBTpiStream() : File.getPDBIpiStream());
+
+ AutoIndent Indent(P);
+
+ auto Substream = Stream.getTypeRecordsSubstream();
+ auto &Types = Err(initializeTypes(StreamIdx));
+ auto Layout = File.getStreamLayout(StreamIdx);
+ for (const auto &Id : Indices) {
+ TypeIndex TI(Id);
+ if (TI.toArrayIndex() >= Types.capacity()) {
+ P.formatLine("Error: TypeIndex {0} does not exist", TI);
+ continue;
+ }
+
+ auto Type = Types.getType(TI);
+ uint32_t Offset = Types.getOffsetOfType(TI);
+ auto OneType = Substream.slice(Offset, Type.length());
+ P.formatMsfStreamData(formatv("Type {0}", TI).str(), File, Layout, OneType);
+ }
+}
+
+template <typename CallbackT>
+static void iterateOneModule(PDBFile &File, LinePrinter &P,
+ const DbiModuleList &Modules, uint32_t I,
+ uint32_t Digits, uint32_t IndentLevel,
+ CallbackT Callback) {
+ if (I >= Modules.getModuleCount()) {
+ P.formatLine("Mod {0:4} | Invalid module index ",
+ fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)));
+ return;
+ }
+
+ auto Modi = Modules.getModuleDescriptor(I);
+ P.formatLine("Mod {0:4} | `{1}`: ",
+ fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)),
+ Modi.getModuleName());
+
+ uint16_t ModiStream = Modi.getModuleStreamIndex();
+ AutoIndent Indent2(P, IndentLevel);
+ if (ModiStream == kInvalidStreamIndex)
+ return;
+
+ auto ModStreamData = File.createIndexedStream(ModiStream);
+ ModuleDebugStreamRef ModStream(Modi, std::move(ModStreamData));
+ if (auto EC = ModStream.reload()) {
+ P.formatLine("Could not parse debug information.");
+ return;
+ }
+ auto Layout = File.getStreamLayout(ModiStream);
+ Callback(I, ModStream, Layout);
+}
+
+template <typename CallbackT>
+static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel,
+ CallbackT Callback) {
+ AutoIndent Indent(P);
+ if (!File.hasPDBDbiStream()) {
+ P.formatLine("DBI Stream not present");
+ return;
+ }
+
+ ExitOnError Err("Unexpected error processing modules");
+
+ auto &Stream = Err(File.getPDBDbiStream());
+
+ const DbiModuleList &Modules = Stream.modules();
+
+ if (opts::bytes::ModuleIndex.getNumOccurrences() > 0) {
+ iterateOneModule(File, P, Modules, opts::bytes::ModuleIndex, 1, IndentLevel,
+ Callback);
+ } else {
+ uint32_t Count = Modules.getModuleCount();
+ uint32_t Digits = NumDigits(Count);
+ for (uint32_t I = 0; I < Count; ++I) {
+ iterateOneModule(File, P, Modules, I, Digits, IndentLevel, Callback);
+ }
+ }
+}
+
+void BytesOutputStyle::dumpModuleSyms() {
+ printHeader(P, "Module Symbols");
+
+ AutoIndent Indent(P);
+
+ iterateModules(File, P, 2,
+ [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
+ const MSFStreamLayout &Layout) {
+ auto Symbols = Stream.getSymbolsSubstream();
+ P.formatMsfStreamData("Symbols", File, Layout, Symbols);
+ });
+}
+
+void BytesOutputStyle::dumpModuleC11() {
+ printHeader(P, "C11 Debug Chunks");
+
+ AutoIndent Indent(P);
+
+ iterateModules(File, P, 2,
+ [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
+ const MSFStreamLayout &Layout) {
+ auto Chunks = Stream.getC11LinesSubstream();
+ P.formatMsfStreamData("C11 Debug Chunks", File, Layout,
+ Chunks);
+ });
+}
+
+void BytesOutputStyle::dumpModuleC13() {
+ printHeader(P, "Debug Chunks");
+
+ AutoIndent Indent(P);
+
+ iterateModules(
+ File, P, 2,
+ [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
+ const MSFStreamLayout &Layout) {
+ auto Chunks = Stream.getC13LinesSubstream();
+ if (opts::bytes::SplitChunks) {
+ for (const auto &SS : Stream.subsections()) {
+ BinarySubstreamRef ThisChunk;
+ std::tie(ThisChunk, Chunks) = Chunks.split(SS.getRecordLength());
+ P.formatMsfStreamData(formatChunkKind(SS.kind()), File, Layout,
+ ThisChunk);
+ }
+ } else {
+ P.formatMsfStreamData("Debug Chunks", File, Layout, Chunks);
+ }
+ });
+}
+
+void BytesOutputStyle::dumpByteRanges(uint32_t Min, uint32_t Max) {
+ printHeader(P, "MSF Bytes");
+
+ AutoIndent Indent(P);
+
+ BinaryStreamReader Reader(File.getMsfBuffer());
+ ArrayRef<uint8_t> Data;
+ consumeError(Reader.skip(Min));
+ uint32_t Size = Max - Min + 1;
+ auto EC = Reader.readBytes(Data, Size);
+ assert(!EC);
+ consumeError(std::move(EC));
+ P.formatBinary("Bytes", Data, Min);
+}
+
+Expected<codeview::LazyRandomTypeCollection &>
+BytesOutputStyle::initializeTypes(uint32_t StreamIdx) {
+ auto &TypeCollection = (StreamIdx == StreamTPI) ? TpiTypes : IpiTypes;
+ if (TypeCollection)
+ return *TypeCollection;
+
+ auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream()
+ : File.getPDBIpiStream();
+ if (!Tpi)
+ return Tpi.takeError();
+
+ auto &Types = Tpi->typeArray();
+ uint32_t Count = Tpi->getNumTypeRecords();
+ auto Offsets = Tpi->getTypeIndexOffsets();
+ TypeCollection =
+ std::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets);
+
+ return *TypeCollection;
+}
+
+void BytesOutputStyle::dumpFpm() {
+ printHeader(P, "Free Page Map");
+
+ msf::MSFStreamLayout FpmLayout = File.getFpmStreamLayout();
+ P.formatMsfStreamBlocks(File, FpmLayout);
+}
+
+void BytesOutputStyle::dumpStreamBytes() {
+ if (StreamPurposes.empty())
+ discoverStreamPurposes(File, StreamPurposes);
+
+ printHeader(P, "Stream Data");
+ ExitOnError Err("Unexpected error reading stream data");
+
+ auto Specs = parseStreamSpecs(P);
+
+ for (const auto &Spec : Specs) {
+ AutoIndent Indent(P);
+ if (Spec.SI >= StreamPurposes.size()) {
+ P.formatLine("Stream {0}: Not present", Spec.SI);
+ continue;
+ }
+ P.formatMsfStreamData("Data", File, Spec.SI,
+ StreamPurposes[Spec.SI].getShortName(), Spec.Begin,
+ Spec.Size);
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/BytesOutputStyle.h b/contrib/libs/llvm14/tools/llvm-pdbutil/BytesOutputStyle.h
new file mode 100644
index 00000000000..d3aceb47679
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/BytesOutputStyle.h
@@ -0,0 +1,68 @@
+//===- BytesOutputStyle.h ------------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_BYTESOUTPUTSTYLE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_BYTESOUTPUTSTYLE_H
+
+#include "LinePrinter.h"
+#include "OutputStyle.h"
+#include "StreamUtil.h"
+
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+
+namespace codeview {
+class LazyRandomTypeCollection;
+}
+
+namespace pdb {
+
+class PDBFile;
+
+class BytesOutputStyle : public OutputStyle {
+public:
+ BytesOutputStyle(PDBFile &File);
+
+ Error dump() override;
+
+private:
+ void dumpNameMap();
+ void dumpBlockRanges(uint32_t Min, uint32_t Max);
+ void dumpByteRanges(uint32_t Min, uint32_t Max);
+ void dumpFpm();
+ void dumpStreamBytes();
+
+ void dumpSectionContributions();
+ void dumpSectionMap();
+ void dumpModuleInfos();
+ void dumpFileInfo();
+ void dumpTypeServerMap();
+ void dumpECData();
+
+ void dumpModuleSyms();
+ void dumpModuleC11();
+ void dumpModuleC13();
+
+ void dumpTypeIndex(uint32_t StreamIdx, ArrayRef<uint32_t> Indices);
+
+ Expected<codeview::LazyRandomTypeCollection &>
+ initializeTypes(uint32_t StreamIdx);
+
+ std::unique_ptr<codeview::LazyRandomTypeCollection> TpiTypes;
+ std::unique_ptr<codeview::LazyRandomTypeCollection> IpiTypes;
+
+ PDBFile &File;
+ LinePrinter P;
+ ExitOnError Err;
+ SmallVector<StreamInfo, 8> StreamPurposes;
+};
+} // namespace pdb
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/DumpOutputStyle.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/DumpOutputStyle.cpp
new file mode 100644
index 00000000000..ef299ea9d48
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/DumpOutputStyle.cpp
@@ -0,0 +1,1989 @@
+//===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DumpOutputStyle.h"
+
+#include "FormatUtil.h"
+#include "InputFile.h"
+#include "MinimalSymbolDumper.h"
+#include "MinimalTypeDumper.h"
+#include "StreamUtil.h"
+#include "TypeReferenceTracker.h"
+#include "llvm-pdbutil.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#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"
+#include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h"
+#include "llvm/DebugInfo/CodeView/Formatters.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/Line.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"
+#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h"
+#include "llvm/DebugInfo/CodeView/TypeHashing.h"
+#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
+#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
+#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
+#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.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/RawError.h"
+#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/BinaryStreamReader.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+
+#include <cctype>
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::msf;
+using namespace llvm::pdb;
+
+DumpOutputStyle::DumpOutputStyle(InputFile &File)
+ : File(File), P(2, false, outs()) {
+ if (opts::dump::DumpTypeRefStats)
+ RefTracker.reset(new TypeReferenceTracker(File));
+}
+
+DumpOutputStyle::~DumpOutputStyle() {}
+
+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() {
+ // Walk symbols & globals if we are supposed to mark types referenced.
+ if (opts::dump::DumpTypeRefStats)
+ RefTracker->mark();
+
+ if (opts::dump::DumpSummary) {
+ if (auto EC = dumpFileSummary())
+ return EC;
+ P.NewLine();
+ }
+
+ if (opts::dump::DumpStreams) {
+ if (auto EC = dumpStreamSummary())
+ return EC;
+ P.NewLine();
+ }
+
+ if (opts::dump::DumpSymbolStats) {
+ if (auto EC = dumpSymbolStats())
+ return EC;
+ P.NewLine();
+ }
+
+ if (opts::dump::DumpUdtStats) {
+ if (auto EC = dumpUdtStats())
+ return EC;
+ P.NewLine();
+ }
+
+ if (opts::dump::DumpTypeStats || opts::dump::DumpIDStats) {
+ if (auto EC = dumpTypeStats())
+ return EC;
+ P.NewLine();
+ }
+
+ if (opts::dump::DumpNamedStreams) {
+ if (auto EC = dumpNamedStreams())
+ return EC;
+ P.NewLine();
+ }
+
+ if (opts::dump::DumpStringTable || opts::dump::DumpStringTableDetails) {
+ if (auto EC = dumpStringTable())
+ return EC;
+ P.NewLine();
+ }
+
+ if (opts::dump::DumpModules) {
+ if (auto EC = dumpModules())
+ return EC;
+ }
+
+ if (opts::dump::DumpModuleFiles) {
+ if (auto EC = dumpModuleFiles())
+ return EC;
+ }
+
+ if (opts::dump::DumpLines) {
+ if (auto EC = dumpLines())
+ return EC;
+ }
+
+ if (opts::dump::DumpInlineeLines) {
+ if (auto EC = dumpInlineeLines())
+ return EC;
+ }
+
+ if (opts::dump::DumpXmi) {
+ if (auto EC = dumpXmi())
+ return EC;
+ }
+
+ if (opts::dump::DumpXme) {
+ if (auto EC = dumpXme())
+ 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)
+ if (auto EC = dumpTypesFromObjectFile())
+ return EC;
+ } else {
+ if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||
+ opts::dump::DumpTypeExtras) {
+ if (auto EC = dumpTpiStream(StreamTPI))
+ return EC;
+ }
+
+ if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() ||
+ opts::dump::DumpIdExtras) {
+ if (auto EC = dumpTpiStream(StreamIPI))
+ return EC;
+ }
+ }
+
+ if (opts::dump::DumpGSIRecords) {
+ if (auto EC = dumpGSIRecords())
+ return EC;
+ }
+
+ if (opts::dump::DumpGlobals) {
+ if (auto EC = dumpGlobals())
+ return EC;
+ }
+
+ if (opts::dump::DumpPublics) {
+ if (auto EC = dumpPublics())
+ return EC;
+ }
+
+ if (opts::dump::DumpSymbols) {
+ auto EC = File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj();
+ if (EC)
+ return EC;
+ }
+
+ if (opts::dump::DumpTypeRefStats) {
+ if (auto EC = dumpTypeRefStats())
+ return EC;
+ }
+
+ if (opts::dump::DumpSectionHeaders) {
+ if (auto EC = dumpSectionHeaders())
+ return EC;
+ }
+
+ if (opts::dump::DumpSectionContribs) {
+ if (auto EC = dumpSectionContribs())
+ return EC;
+ }
+
+ if (opts::dump::DumpSectionMap) {
+ if (auto EC = dumpSectionMap())
+ return EC;
+ }
+
+ P.NewLine();
+
+ return Error::success();
+}
+
+static void printHeader(LinePrinter &P, const Twine &S) {
+ P.NewLine();
+ P.formatLine("{0,=60}", S);
+ P.formatLine("{0}", fmt_repeat('=', 60));
+}
+
+Error DumpOutputStyle::dumpFileSummary() {
+ printHeader(P, "Summary");
+
+ if (File.isObj()) {
+ 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());
+
+ auto &PS = Err(getPdb().getPDBInfoStream());
+ P.formatLine("Signature: {0}", PS.getSignature());
+ P.formatLine("Age: {0}", PS.getAge());
+ P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid));
+ P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures()));
+ P.formatLine("Has Debug Info: {0}", getPdb().hasPDBDbiStream());
+ P.formatLine("Has Types: {0}", getPdb().hasPDBTpiStream());
+ P.formatLine("Has IDs: {0}", getPdb().hasPDBIpiStream());
+ P.formatLine("Has Globals: {0}", getPdb().hasPDBGlobalsStream());
+ P.formatLine("Has Publics: {0}", getPdb().hasPDBPublicsStream());
+ if (getPdb().hasPDBDbiStream()) {
+ auto &DBI = Err(getPdb().getPDBDbiStream());
+ P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked());
+ P.formatLine("Has conflicting types: {0}", DBI.hasCTypes());
+ P.formatLine("Is stripped: {0}", DBI.isStripped());
+ }
+
+ return Error::success();
+}
+
+static StatCollection getSymbolStats(const SymbolGroup &SG,
+ StatCollection &CumulativeStats) {
+ StatCollection Stats;
+ 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());
+ CumulativeStats.update(S.kind(), S.length());
+ }
+ return Stats;
+ }
+
+ for (const auto &SS : SG.getDebugSubsections()) {
+ // For object files, all symbols are spread across multiple Symbol
+ // subsections of a given .debug$S section.
+ if (SS.kind() != DebugSubsectionKind::Symbols)
+ continue;
+ DebugSymbolsSubsectionRef Symbols;
+ BinaryStreamReader Reader(SS.getRecordData());
+ cantFail(Symbols.initialize(Reader));
+ for (const auto &S : Symbols) {
+ Stats.update(S.kind(), S.length());
+ CumulativeStats.update(S.kind(), S.length());
+ }
+ }
+ return Stats;
+}
+
+static StatCollection getChunkStats(const SymbolGroup &SG,
+ StatCollection &CumulativeStats) {
+ StatCollection Stats;
+ for (const auto &Chunk : SG.getDebugSubsections()) {
+ Stats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());
+ CumulativeStats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());
+ }
+ return Stats;
+}
+
+static inline std::string formatModuleDetailKind(DebugSubsectionKind K) {
+ return formatChunkKind(K, false);
+}
+
+static inline std::string formatModuleDetailKind(SymbolKind K) {
+ return formatSymbolKind(K);
+}
+
+// Get the stats sorted by size, descending.
+std::vector<StatCollection::KindAndStat>
+StatCollection::getStatsSortedBySize() const {
+ std::vector<KindAndStat> SortedStats(Individual.begin(), Individual.end());
+ llvm::stable_sort(SortedStats,
+ [](const KindAndStat &LHS, const KindAndStat &RHS) {
+ return LHS.second.Size > RHS.second.Size;
+ });
+ return SortedStats;
+}
+
+template <typename Kind>
+static void printModuleDetailStats(LinePrinter &P, StringRef Label,
+ const StatCollection &Stats) {
+ P.NewLine();
+ P.formatLine(" {0}", Label);
+ AutoIndent Indent(P);
+ P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", "Total",
+ Stats.Totals.Count, Stats.Totals.Size);
+ P.formatLine("{0}", fmt_repeat('-', 74));
+
+ for (const auto &K : Stats.getStatsSortedBySize()) {
+ std::string KindName = formatModuleDetailKind(Kind(K.first));
+ P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", KindName,
+ K.second.Count, K.second.Size);
+ }
+}
+
+static bool isMyCode(const SymbolGroup &Group) {
+ if (Group.getFile().isObj())
+ return true;
+
+ StringRef Name = Group.name();
+ if (Name.startswith("Import:"))
+ return false;
+ if (Name.endswith_insensitive(".dll"))
+ return false;
+ if (Name.equals_insensitive("* linker *"))
+ return false;
+ if (Name.startswith_insensitive("f:\\binaries\\Intermediate\\vctools"))
+ return false;
+ if (Name.startswith_insensitive("f:\\dd\\vctools\\crt"))
+ return false;
+ return true;
+}
+
+static bool shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group) {
+ if (opts::dump::JustMyCode && !isMyCode(Group))
+ return false;
+
+ // If the arg was not specified on the command line, always dump all modules.
+ if (opts::dump::DumpModi.getNumOccurrences() == 0)
+ return true;
+
+ // Otherwise, only dump if this is the same module specified.
+ return (opts::dump::DumpModi == Idx);
+}
+
+Error DumpOutputStyle::dumpStreamSummary() {
+ printHeader(P, "Streams");
+
+ if (File.isObj()) {
+ printStreamNotValidForObj();
+ return Error::success();
+ }
+
+ AutoIndent Indent(P);
+
+ if (StreamPurposes.empty())
+ discoverStreamPurposes(getPdb(), StreamPurposes);
+
+ uint32_t StreamCount = getPdb().getNumStreams();
+ uint32_t MaxStreamSize = getPdb().getMaxStreamSize();
+
+ for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
+ P.formatLine(
+ "Stream {0} ({1} bytes): [{2}]",
+ fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)),
+ fmt_align(getPdb().getStreamByteSize(StreamIdx), AlignStyle::Right,
+ NumDigits(MaxStreamSize)),
+ StreamPurposes[StreamIdx].getLongName());
+
+ if (opts::dump::DumpStreamBlocks) {
+ auto Blocks = getPdb().getStreamBlockList(StreamIdx);
+ std::vector<uint32_t> BV(Blocks.begin(), Blocks.end());
+ P.formatLine(" {0} Blocks: [{1}]",
+ fmt_repeat(' ', NumDigits(StreamCount)),
+ make_range(BV.begin(), BV.end()));
+ }
+ }
+
+ return Error::success();
+}
+
+static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File,
+ uint32_t Index) {
+ ExitOnError Err("Unexpected error: ");
+
+ auto &Dbi = Err(File.getPDBDbiStream());
+ const auto &Modules = Dbi.modules();
+ auto Modi = Modules.getModuleDescriptor(Index);
+
+ uint16_t ModiStream = Modi.getModuleStreamIndex();
+ if (ModiStream == kInvalidStreamIndex)
+ return make_error<RawError>(raw_error_code::no_stream,
+ "Module stream not present");
+
+ auto ModStreamData = File.createIndexedStream(ModiStream);
+
+ ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));
+ if (auto EC = ModS.reload())
+ return make_error<RawError>(raw_error_code::corrupt_file,
+ "Invalid module stream");
+
+ return std::move(ModS);
+}
+
+template <typename CallbackT>
+static void
+iterateOneModule(InputFile &File, const Optional<PrintScope> &HeaderScope,
+ const SymbolGroup &SG, uint32_t Modi, CallbackT Callback) {
+ if (HeaderScope) {
+ HeaderScope->P.formatLine(
+ "Mod {0:4} | `{1}`: ",
+ fmt_align(Modi, AlignStyle::Right, HeaderScope->LabelWidth), SG.name());
+ }
+
+ AutoIndent Indent(HeaderScope);
+ Callback(Modi, SG);
+}
+
+template <typename CallbackT>
+static void iterateSymbolGroups(InputFile &Input,
+ const Optional<PrintScope> &HeaderScope,
+ CallbackT Callback) {
+ AutoIndent Indent(HeaderScope);
+
+ ExitOnError Err("Unexpected error processing modules: ");
+
+ if (opts::dump::DumpModi.getNumOccurrences() > 0) {
+ assert(opts::dump::DumpModi.getNumOccurrences() == 1);
+ uint32_t Modi = opts::dump::DumpModi;
+ SymbolGroup SG(&Input, Modi);
+ iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(Modi)), SG,
+ Modi, Callback);
+ return;
+ }
+
+ uint32_t I = 0;
+
+ for (const auto &SG : Input.symbol_groups()) {
+ if (shouldDumpSymbolGroup(I, SG))
+ iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(I)), SG, I,
+ Callback);
+
+ ++I;
+ }
+}
+
+template <typename SubsectionT>
+static void iterateModuleSubsections(
+ InputFile &File, const Optional<PrintScope> &HeaderScope,
+ llvm::function_ref<void(uint32_t, const SymbolGroup &, SubsectionT &)>
+ Callback) {
+
+ iterateSymbolGroups(File, HeaderScope,
+ [&](uint32_t Modi, const SymbolGroup &SG) {
+ for (const auto &SS : SG.getDebugSubsections()) {
+ SubsectionT Subsection;
+
+ if (SS.kind() != Subsection.kind())
+ continue;
+
+ BinaryStreamReader Reader(SS.getRecordData());
+ if (auto EC = Subsection.initialize(Reader))
+ continue;
+ Callback(Modi, SG, Subsection);
+ }
+ });
+}
+
+static Expected<std::pair<std::unique_ptr<MappedBlockStream>,
+ ArrayRef<llvm::object::coff_section>>>
+loadSectionHeaders(PDBFile &File, DbgHeaderType Type) {
+ if (!File.hasPDBDbiStream())
+ return make_error<StringError>(
+ "Section headers require a DBI Stream, which could not be loaded",
+ inconvertibleErrorCode());
+
+ auto &Dbi = cantFail(File.getPDBDbiStream());
+ uint32_t SI = Dbi.getDebugStreamIndex(Type);
+
+ if (SI == kInvalidStreamIndex)
+ return make_error<StringError>(
+ "PDB does not contain the requested image section header type",
+ inconvertibleErrorCode());
+
+ auto Stream = File.createIndexedStream(SI);
+ if (!Stream)
+ return make_error<StringError>("Could not load the required stream data",
+ inconvertibleErrorCode());
+
+ ArrayRef<object::coff_section> Headers;
+ if (Stream->getLength() % sizeof(object::coff_section) != 0)
+ return make_error<StringError>(
+ "Section header array size is not a multiple of section header size",
+ inconvertibleErrorCode());
+
+ uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section);
+ BinaryStreamReader Reader(*Stream);
+ cantFail(Reader.readArray(Headers, NumHeaders));
+ return std::make_pair(std::move(Stream), Headers);
+}
+
+static std::vector<std::string> getSectionNames(PDBFile &File) {
+ auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr);
+ if (!ExpectedHeaders)
+ return {};
+
+ std::unique_ptr<MappedBlockStream> Stream;
+ ArrayRef<object::coff_section> Headers;
+ std::tie(Stream, Headers) = std::move(*ExpectedHeaders);
+ std::vector<std::string> Names;
+ for (const auto &H : Headers)
+ Names.push_back(H.Name);
+ return Names;
+}
+
+static void dumpSectionContrib(LinePrinter &P, const SectionContrib &SC,
+ ArrayRef<std::string> SectionNames,
+ uint32_t FieldWidth) {
+ std::string NameInsert;
+ if (SC.ISect > 0 && SC.ISect <= SectionNames.size()) {
+ StringRef SectionName = SectionNames[SC.ISect - 1];
+ NameInsert = formatv("[{0}]", SectionName).str();
+ } else
+ NameInsert = "[???]";
+ P.formatLine("SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
+ "crc = {4}",
+ formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size),
+ fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc),
+ fmt_align(NameInsert, AlignStyle::Left, FieldWidth + 2));
+ AutoIndent Indent(P, FieldWidth + 2);
+ P.formatLine(" {0}",
+ formatSectionCharacteristics(P.getIndentLevel() + 6,
+ SC.Characteristics, 3, " | "));
+}
+
+static void dumpSectionContrib(LinePrinter &P, const SectionContrib2 &SC,
+ ArrayRef<std::string> SectionNames,
+ uint32_t FieldWidth) {
+ P.formatLine("SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
+ "crc = {4}, coff section = {5}",
+ formatSegmentOffset(SC.Base.ISect, SC.Base.Off),
+ fmtle(SC.Base.Size), fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc),
+ fmtle(SC.Base.RelocCrc), fmtle(SC.ISectCoff));
+ P.formatLine(" {0}",
+ formatSectionCharacteristics(P.getIndentLevel() + 6,
+ SC.Base.Characteristics, 3, " | "));
+}
+
+Error DumpOutputStyle::dumpModules() {
+ printHeader(P, "Modules");
+
+ if (File.isObj()) {
+ printStreamNotValidForObj();
+ return Error::success();
+ }
+
+ if (!getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
+ return Error::success();
+ }
+
+ AutoIndent Indent(P);
+ ExitOnError Err("Unexpected error processing modules: ");
+
+ auto &Stream = Err(getPdb().getPDBDbiStream());
+
+ const DbiModuleList &Modules = Stream.modules();
+ iterateSymbolGroups(
+ File, PrintScope{P, 11}, [&](uint32_t Modi, const SymbolGroup &Strings) {
+ auto Desc = Modules.getModuleDescriptor(Modi);
+ if (opts::dump::DumpSectionContribs) {
+ std::vector<std::string> Sections = getSectionNames(getPdb());
+ dumpSectionContrib(P, Desc.getSectionContrib(), Sections, 0);
+ }
+ P.formatLine("Obj: `{0}`: ", Desc.getObjFileName());
+ P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}",
+ Desc.getModuleStreamIndex(), Desc.getNumberOfFiles(),
+ Desc.hasECInfo());
+ StringRef PdbFilePath =
+ Err(Stream.getECName(Desc.getPdbFilePathNameIndex()));
+ StringRef SrcFilePath =
+ Err(Stream.getECName(Desc.getSourceFileNameIndex()));
+ P.formatLine("pdb file ni: {0} `{1}`, src file ni: {2} `{3}`",
+ Desc.getPdbFilePathNameIndex(), PdbFilePath,
+ Desc.getSourceFileNameIndex(), SrcFilePath);
+ });
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpModuleFiles() {
+ printHeader(P, "Files");
+
+ if (File.isObj()) {
+ printStreamNotValidForObj();
+ return Error::success();
+ }
+
+ if (!getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
+ return Error::success();
+ }
+
+ ExitOnError Err("Unexpected error processing modules: ");
+
+ iterateSymbolGroups(File, PrintScope{P, 11},
+ [this, &Err](uint32_t Modi, const SymbolGroup &Strings) {
+ auto &Stream = Err(getPdb().getPDBDbiStream());
+
+ const DbiModuleList &Modules = Stream.modules();
+ for (const auto &F : Modules.source_files(Modi)) {
+ Strings.formatFromFileName(P, F);
+ }
+ });
+ return Error::success();
+}
+
+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;
+ StatCollection ChunkStats;
+
+ Optional<PrintScope> Scope;
+ if (File.isPdb())
+ Scope.emplace(P, 2);
+
+ iterateSymbolGroups(File, Scope, [&](uint32_t Modi, const SymbolGroup &SG) {
+ StatCollection SS = getSymbolStats(SG, SymStats);
+ StatCollection CS = getChunkStats(SG, ChunkStats);
+
+ if (SG.getFile().isPdb()) {
+ AutoIndent Indent(P);
+ auto Modules = cantFail(File.pdb().getPDBDbiStream()).modules();
+ uint32_t ModCount = Modules.getModuleCount();
+ DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi);
+ uint32_t StreamIdx = Desc.getModuleStreamIndex();
+
+ if (StreamIdx == kInvalidStreamIndex) {
+ P.formatLine("Mod {0} (debug info not present): [{1}]",
+ fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)),
+ Desc.getModuleName());
+ return;
+ }
+ P.formatLine("Stream {0}, {1} bytes", StreamIdx,
+ getPdb().getStreamByteSize(StreamIdx));
+
+ printModuleDetailStats<SymbolKind>(P, "Symbols", SS);
+ printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS);
+ }
+ });
+
+ if (SymStats.Totals.Count > 0) {
+ P.printLine(" Summary |");
+ AutoIndent Indent(P, 4);
+ printModuleDetailStats<SymbolKind>(P, "Symbols", SymStats);
+ printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", ChunkStats);
+ }
+
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpTypeStats() {
+ printHeader(P, "Type Record Stats");
+
+ // Iterate the types, categorize by kind, accumulate size stats.
+ StatCollection TypeStats;
+ LazyRandomTypeCollection &Types =
+ opts::dump::DumpTypeStats ? File.types() : File.ids();
+ for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) {
+ CVType Type = Types.getType(*TI);
+ TypeStats.update(uint32_t(Type.kind()), Type.length());
+ }
+
+ P.NewLine();
+ P.formatLine(" Types");
+ AutoIndent Indent(P);
+ P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", "Total",
+ TypeStats.Totals.Count, TypeStats.Totals.Size,
+ (double)TypeStats.Totals.Size / TypeStats.Totals.Count);
+ P.formatLine("{0}", fmt_repeat('-', 74));
+
+ for (const auto &K : TypeStats.getStatsSortedBySize()) {
+ P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)",
+ formatTypeLeafKind(TypeLeafKind(K.first)), K.second.Count,
+ K.second.Size, (double)K.second.Size / K.second.Count);
+ }
+ return Error::success();
+}
+
+static bool isValidNamespaceIdentifier(StringRef S) {
+ if (S.empty())
+ return false;
+
+ if (std::isdigit(S[0]))
+ return false;
+
+ return llvm::all_of(S, [](char C) { return std::isalnum(C); });
+}
+
+namespace {
+constexpr uint32_t kNoneUdtKind = 0;
+constexpr uint32_t kSimpleUdtKind = 1;
+constexpr uint32_t kUnknownUdtKind = 2;
+} // namespace
+
+static std::string getUdtStatLabel(uint32_t Kind) {
+ if (Kind == kNoneUdtKind)
+ return "<none type>";
+
+ if (Kind == kSimpleUdtKind)
+ return "<simple type>";
+
+ if (Kind == kUnknownUdtKind)
+ return "<unknown type>";
+
+ return formatTypeLeafKind(static_cast<TypeLeafKind>(Kind));
+}
+
+static uint32_t getLongestTypeLeafName(const StatCollection &Stats) {
+ size_t L = 0;
+ for (const auto &Stat : Stats.Individual) {
+ std::string Label = getUdtStatLabel(Stat.first);
+ L = std::max(L, Label.size());
+ }
+ return static_cast<uint32_t>(L);
+}
+
+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);
+
+ auto &TpiTypes = File.types();
+
+ StringMap<StatCollection::Stat> NamespacedStats;
+
+ size_t LongestNamespace = 0;
+ auto HandleOneSymbol = [&](const CVSymbol &Sym) {
+ if (Sym.kind() != SymbolKind::S_UDT)
+ return;
+ UdtStats.update(SymbolKind::S_UDT, Sym.length());
+
+ UDTSym UDT = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(Sym));
+
+ uint32_t Kind = 0;
+ uint32_t RecordSize = 0;
+
+ if (UDT.Type.isNoneType())
+ Kind = kNoneUdtKind;
+ else if (UDT.Type.isSimple())
+ Kind = kSimpleUdtKind;
+ else if (Optional<CVType> T = TpiTypes.tryGetType(UDT.Type)) {
+ Kind = T->kind();
+ RecordSize = T->length();
+ } else
+ Kind = kUnknownUdtKind;
+
+ UdtTargetStats.update(Kind, RecordSize);
+
+ size_t Pos = UDT.Name.find("::");
+ if (Pos == StringRef::npos)
+ return;
+
+ StringRef Scope = UDT.Name.take_front(Pos);
+ if (Scope.empty() || !isValidNamespaceIdentifier(Scope))
+ return;
+
+ LongestNamespace = std::max(LongestNamespace, Scope.size());
+ NamespacedStats[Scope].update(RecordSize);
+ };
+
+ P.NewLine();
+
+ if (File.isPdb()) {
+ auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream());
+ auto ExpGlobals = getPdb().getPDBGlobalsStream();
+ if (!ExpGlobals)
+ return ExpGlobals.takeError();
+
+ for (uint32_t PubSymOff : ExpGlobals->getGlobalsTable()) {
+ CVSymbol Sym = SymbolRecords.readRecord(PubSymOff);
+ HandleOneSymbol(Sym);
+ }
+ } else {
+ for (const auto &Sec : File.symbol_groups()) {
+ for (const auto &SS : Sec.getDebugSubsections()) {
+ if (SS.kind() != DebugSubsectionKind::Symbols)
+ continue;
+
+ DebugSymbolsSubsectionRef Symbols;
+ BinaryStreamReader Reader(SS.getRecordData());
+ cantFail(Symbols.initialize(Reader));
+ for (const auto &S : Symbols)
+ HandleOneSymbol(S);
+ }
+ }
+ }
+
+ LongestNamespace += StringRef(" namespace ''").size();
+ size_t LongestTypeLeafKind = getLongestTypeLeafName(UdtTargetStats);
+ size_t FieldWidth = std::max(LongestNamespace, LongestTypeLeafKind);
+
+ // Compute the max number of digits for count and size fields, including comma
+ // separators.
+ StringRef CountHeader("Count");
+ StringRef SizeHeader("Size");
+ size_t CD = NumDigits(UdtStats.Totals.Count);
+ CD += (CD - 1) / 3;
+ CD = std::max(CD, CountHeader.size());
+
+ size_t SD = NumDigits(UdtStats.Totals.Size);
+ SD += (SD - 1) / 3;
+ SD = std::max(SD, SizeHeader.size());
+
+ uint32_t TableWidth = FieldWidth + 3 + CD + 2 + SD + 1;
+
+ P.formatLine("{0} | {1} {2}",
+ fmt_align("Record Kind", AlignStyle::Right, FieldWidth),
+ fmt_align(CountHeader, AlignStyle::Right, CD),
+ fmt_align(SizeHeader, AlignStyle::Right, SD));
+
+ P.formatLine("{0}", fmt_repeat('-', TableWidth));
+ for (const auto &Stat : UdtTargetStats.getStatsSortedBySize()) {
+ std::string Label = getUdtStatLabel(Stat.first);
+ P.formatLine("{0} | {1:N} {2:N}",
+ fmt_align(Label, AlignStyle::Right, FieldWidth),
+ fmt_align(Stat.second.Count, AlignStyle::Right, CD),
+ fmt_align(Stat.second.Size, AlignStyle::Right, SD));
+ }
+ P.formatLine("{0}", fmt_repeat('-', TableWidth));
+ P.formatLine("{0} | {1:N} {2:N}",
+ fmt_align("Total (S_UDT)", AlignStyle::Right, FieldWidth),
+ fmt_align(UdtStats.Totals.Count, AlignStyle::Right, CD),
+ fmt_align(UdtStats.Totals.Size, AlignStyle::Right, SD));
+ P.formatLine("{0}", fmt_repeat('-', TableWidth));
+ struct StrAndStat {
+ StringRef Key;
+ StatCollection::Stat Stat;
+ };
+
+ // Print namespace stats in descending order of size.
+ std::vector<StrAndStat> NamespacedStatsSorted;
+ for (const auto &Stat : NamespacedStats)
+ NamespacedStatsSorted.push_back({Stat.getKey(), Stat.second});
+ llvm::stable_sort(NamespacedStatsSorted,
+ [](const StrAndStat &L, const StrAndStat &R) {
+ return L.Stat.Size > R.Stat.Size;
+ });
+ for (const auto &Stat : NamespacedStatsSorted) {
+ std::string Label = std::string(formatv("namespace '{0}'", Stat.Key));
+ P.formatLine("{0} | {1:N} {2:N}",
+ fmt_align(Label, AlignStyle::Right, FieldWidth),
+ fmt_align(Stat.Stat.Count, AlignStyle::Right, CD),
+ fmt_align(Stat.Stat.Size, AlignStyle::Right, SD));
+ }
+ return Error::success();
+}
+
+static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start,
+ const LineColumnEntry &E) {
+ const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number
+ uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5;
+
+ // Let's try to keep it under 100 characters
+ constexpr uint32_t kMaxRowLength = 100;
+ // At least 3 spaces between columns.
+ uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3);
+ uint32_t ItemsLeft = E.LineNumbers.size();
+ auto LineIter = E.LineNumbers.begin();
+ while (ItemsLeft != 0) {
+ uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow);
+ for (uint32_t I = 0; I < RowColumns; ++I) {
+ LineInfo Line(LineIter->Flags);
+ std::string LineStr;
+ if (Line.isAlwaysStepInto())
+ LineStr = "ASI";
+ else if (Line.isNeverStepInto())
+ LineStr = "NSI";
+ else
+ LineStr = utostr(Line.getStartLine());
+ char Statement = Line.isStatement() ? ' ' : '!';
+ P.format("{0} {1:X-} {2} ",
+ fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber),
+ fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'),
+ Statement);
+ ++LineIter;
+ --ItemsLeft;
+ }
+ P.NewLine();
+ }
+}
+
+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>(
+ File, PrintScope{P, 4},
+ [this, &LastModi, &LastNameIndex](uint32_t Modi,
+ const SymbolGroup &Strings,
+ DebugLinesSubsectionRef &Lines) {
+ uint16_t Segment = Lines.header()->RelocSegment;
+ uint32_t Begin = Lines.header()->RelocOffset;
+ uint32_t End = Begin + Lines.header()->CodeSize;
+ for (const auto &Block : Lines) {
+ if (LastModi != Modi || LastNameIndex != Block.NameIndex) {
+ LastModi = Modi;
+ LastNameIndex = Block.NameIndex;
+ Strings.formatFromChecksumsOffset(P, Block.NameIndex);
+ }
+
+ AutoIndent Indent(P, 2);
+ P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End);
+ uint32_t Count = Block.LineNumbers.size();
+ if (Lines.hasColumnInfo())
+ P.format("line/column/addr entries = {0}", Count);
+ else
+ P.format("line/addr entries = {0}", Count);
+
+ P.NewLine();
+ typesetLinesAndColumns(P, Begin, Block);
+ }
+ });
+
+ return Error::success();
+}
+
+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,
+ DebugInlineeLinesSubsectionRef &Lines) {
+ P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File");
+ for (const auto &Entry : Lines) {
+ P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee,
+ fmtle(Entry.Header->SourceLineNum));
+ Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true);
+ for (const auto &ExtraFileID : Entry.ExtraFiles) {
+ P.formatLine(" ");
+ Strings.formatFromChecksumsOffset(P, ExtraFileID, true);
+ }
+ }
+ P.NewLine();
+ });
+
+ return Error::success();
+}
+
+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,
+ DebugCrossModuleImportsSubsectionRef &Imports) {
+ P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs");
+
+ for (const auto &Xmi : Imports) {
+ auto ExpectedModule =
+ Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset);
+ StringRef Module;
+ SmallString<32> ModuleStorage;
+ if (!ExpectedModule) {
+ Module = "(unknown module)";
+ consumeError(ExpectedModule.takeError());
+ } else
+ Module = *ExpectedModule;
+ if (Module.size() > 32) {
+ ModuleStorage = "...";
+ ModuleStorage += Module.take_back(32 - 3);
+ Module = ModuleStorage;
+ }
+ std::vector<std::string> TIs;
+ for (const auto I : Xmi.Imports)
+ TIs.push_back(std::string(formatv("{0,+10:X+}", fmtle(I))));
+ std::string Result =
+ typesetItemList(TIs, P.getIndentLevel() + 35, 12, " ");
+ P.formatLine("{0,+32} | {1}", Module, Result);
+ }
+ });
+
+ return Error::success();
+}
+
+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,
+ DebugCrossModuleExportsSubsectionRef &Exports) {
+ P.formatLine("{0,-10} | {1}", "Local ID", "Global ID");
+ for (const auto &Export : Exports) {
+ P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local),
+ TypeIndex(Export.Global));
+ }
+ });
+
+ 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());
+
+ if (!Dbi.hasOldFpoRecords()) {
+ printStreamNotPresent("FPO");
+ return Error::success();
+ }
+
+ const FixedStreamArray<object::FpoData>& Records = Dbi.getOldFpoRecords();
+
+ 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());
+
+ if (!Dbi.hasNewFpoRecords()) {
+ printStreamNotPresent("New FPO");
+ return Error::success();
+ }
+
+ const DebugFrameDataSubsectionRef& FDS = Dbi.getNewFpoRecords();
+
+ 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();
+ if (!IS) {
+ P.formatLine("Not present in file");
+ consumeError(IS.takeError());
+ return Error::success();
+ }
+
+ if (opts::dump::DumpStringTable) {
+ if (IS->name_ids().empty())
+ P.formatLine("Empty");
+ else {
+ auto MaxID =
+ std::max_element(IS->name_ids().begin(), IS->name_ids().end());
+ uint32_t Digits = NumDigits(*MaxID);
+
+ P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits),
+ "String");
+
+ std::vector<uint32_t> SortedIDs(IS->name_ids().begin(),
+ IS->name_ids().end());
+ llvm::sort(SortedIDs);
+ for (uint32_t I : SortedIDs) {
+ auto ES = IS->getStringForID(I);
+ llvm::SmallString<32> Str;
+ if (!ES) {
+ consumeError(ES.takeError());
+ Str = "Error reading string";
+ } else if (!ES->empty()) {
+ Str.append("'");
+ Str.append(*ES);
+ Str.append("'");
+ }
+
+ if (!Str.empty())
+ P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits),
+ Str);
+ }
+ }
+ }
+
+ if (opts::dump::DumpStringTableDetails) {
+ P.NewLine();
+ {
+ P.printLine("String Table Header:");
+ AutoIndent Indent(P);
+ P.formatLine("Signature: {0}", IS->getSignature());
+ P.formatLine("Hash Version: {0}", IS->getHashVersion());
+ P.formatLine("Name Buffer Size: {0}", IS->getByteSize());
+ P.NewLine();
+ }
+
+ BinaryStreamRef NameBuffer = IS->getStringTable().getBuffer();
+ ArrayRef<uint8_t> Contents;
+ cantFail(NameBuffer.readBytes(0, NameBuffer.getLength(), Contents));
+ P.formatBinary("Name Buffer", Contents, 0);
+ P.NewLine();
+ {
+ P.printLine("Hash Table:");
+ AutoIndent Indent(P);
+ P.formatLine("Bucket Count: {0}", IS->name_ids().size());
+ for (const auto &Entry : enumerate(IS->name_ids()))
+ P.formatLine("Bucket[{0}] : {1}", Entry.index(),
+ uint32_t(Entry.value()));
+ P.formatLine("Name Count: {0}", IS->getNameCount());
+ }
+ }
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpStringTableFromObj() {
+ iterateModuleSubsections<DebugStringTableSubsectionRef>(
+ File, PrintScope{P, 4},
+ [&](uint32_t Modi, const SymbolGroup &Strings,
+ DebugStringTableSubsectionRef &Strings2) {
+ BinaryStreamRef StringTableBuffer = Strings2.getBuffer();
+ BinaryStreamReader Reader(StringTableBuffer);
+ while (Reader.bytesRemaining() > 0) {
+ StringRef Str;
+ uint32_t Offset = Reader.getOffset();
+ cantFail(Reader.readCString(Str));
+ if (Str.empty())
+ continue;
+
+ P.formatLine("{0} | {1}", fmt_align(Offset, AlignStyle::Right, 4),
+ Str);
+ }
+ });
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpNamedStreams() {
+ printHeader(P, "Named Streams");
+
+ if (File.isObj()) {
+ printStreamNotValidForObj();
+ return Error::success();
+ }
+
+ AutoIndent Indent(P);
+ ExitOnError Err("Invalid PDB File: ");
+
+ auto &IS = Err(File.pdb().getPDBInfoStream());
+ const NamedStreamMap &NS = IS.getNamedStreams();
+ for (const auto &Entry : NS.entries()) {
+ P.printLine(Entry.getKey());
+ AutoIndent Indent2(P, 2);
+ P.formatLine("Index: {0}", Entry.getValue());
+ P.formatLine("Size in bytes: {0}",
+ File.pdb().getStreamByteSize(Entry.getValue()));
+ }
+
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpStringTable() {
+ printHeader(P, "String Table");
+
+ if (File.isPdb())
+ return dumpStringTableFromPdb();
+
+ return dumpStringTableFromObj();
+}
+
+static void buildDepSet(LazyRandomTypeCollection &Types,
+ ArrayRef<TypeIndex> Indices,
+ std::map<TypeIndex, CVType> &DepSet) {
+ SmallVector<TypeIndex, 4> DepList;
+ for (const auto &I : Indices) {
+ TypeIndex TI(I);
+ if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType())
+ continue;
+
+ CVType Type = Types.getType(TI);
+ DepSet[TI] = Type;
+ codeview::discoverTypeIndices(Type, DepList);
+ buildDepSet(Types, DepList, DepSet);
+ }
+}
+
+static void
+dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types,
+ TypeReferenceTracker *RefTracker, uint32_t NumTypeRecords,
+ uint32_t NumHashBuckets,
+ FixedStreamArray<support::ulittle32_t> HashValues,
+ 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, RefTracker,
+ NumHashBuckets, HashValues, Stream);
+
+ if (auto EC = codeview::visitTypeStream(Types, V)) {
+ Printer.formatLine("An error occurred dumping type records: {0}",
+ toString(std::move(EC)));
+ }
+}
+
+static void dumpPartialTypeStream(LinePrinter &Printer,
+ LazyRandomTypeCollection &Types,
+ TypeReferenceTracker *RefTracker,
+ TpiStream &Stream, ArrayRef<TypeIndex> TiList,
+ bool Bytes, bool Extras, bool Deps) {
+ uint32_t Width =
+ NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
+
+ MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker,
+ Stream.getNumHashBuckets(), Stream.getHashValues(),
+ &Stream);
+
+ if (opts::dump::DumpTypeDependents) {
+ // If we need to dump all dependents, then iterate each index and find
+ // all dependents, adding them to a map ordered by TypeIndex.
+ std::map<TypeIndex, CVType> DepSet;
+ buildDepSet(Types, TiList, DepSet);
+
+ Printer.formatLine(
+ "Showing {0:N} records and their dependents ({1:N} records total)",
+ TiList.size(), DepSet.size());
+
+ for (auto &Dep : DepSet) {
+ if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V))
+ Printer.formatLine("An error occurred dumping type record {0}: {1}",
+ Dep.first, toString(std::move(EC)));
+ }
+ } else {
+ Printer.formatLine("Showing {0:N} records.", TiList.size());
+
+ for (const auto &I : TiList) {
+ TypeIndex TI(I);
+ CVType Type = Types.getType(TI);
+ if (auto EC = codeview::visitTypeRecord(Type, TI, V))
+ Printer.formatLine("An error occurred dumping type record {0}: {1}", TI,
+ toString(std::move(EC)));
+ }
+ }
+}
+
+Error DumpOutputStyle::dumpTypesFromObjectFile() {
+ LazyRandomTypeCollection Types(100);
+
+ for (const auto &S : getObj().sections()) {
+ Expected<StringRef> NameOrErr = S.getName();
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ StringRef SectionName = *NameOrErr;
+
+ // .debug$T is a standard CodeView type section, while .debug$P is the same
+ // format but used for MSVC precompiled header object files.
+ if (SectionName == ".debug$T")
+ printHeader(P, "Types (.debug$T)");
+ else if (SectionName == ".debug$P")
+ printHeader(P, "Precompiled Types (.debug$P)");
+ else
+ continue;
+
+ Expected<StringRef> ContentsOrErr = S.getContents();
+ if (!ContentsOrErr)
+ return ContentsOrErr.takeError();
+
+ uint32_t Magic;
+ BinaryStreamReader Reader(*ContentsOrErr, llvm::support::little);
+ if (auto EC = Reader.readInteger(Magic))
+ return EC;
+ if (Magic != COFF::DEBUG_SECTION_MAGIC)
+ return make_error<StringError>("Invalid CodeView debug section.",
+ inconvertibleErrorCode());
+
+ Types.reset(Reader, 100);
+
+ if (opts::dump::DumpTypes) {
+ dumpFullTypeStream(P, Types, RefTracker.get(), 0, 0, {}, nullptr,
+ opts::dump::DumpTypeData, false);
+ } else if (opts::dump::DumpTypeExtras) {
+ auto LocalHashes = LocallyHashedType::hashTypeCollection(Types);
+ auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types);
+ assert(LocalHashes.size() == GlobalHashes.size());
+
+ P.formatLine("Local / Global hashes:");
+ TypeIndex TI(TypeIndex::FirstNonSimpleIndex);
+ for (auto H : zip(LocalHashes, GlobalHashes)) {
+ AutoIndent Indent2(P);
+ LocallyHashedType &L = std::get<0>(H);
+ GloballyHashedType &G = std::get<1>(H);
+
+ P.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI, L, G);
+
+ ++TI;
+ }
+ P.NewLine();
+ }
+ }
+
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
+ assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
+
+ if (StreamIdx == StreamTPI) {
+ printHeader(P, "Types (TPI Stream)");
+ } else if (StreamIdx == StreamIPI) {
+ printHeader(P, "Types (IPI Stream)");
+ }
+
+ assert(!File.isObj());
+
+ bool Present = false;
+ bool DumpTypes = false;
+ bool DumpBytes = false;
+ bool DumpExtras = false;
+ std::vector<uint32_t> Indices;
+ if (StreamIdx == StreamTPI) {
+ Present = getPdb().hasPDBTpiStream();
+ DumpTypes = opts::dump::DumpTypes;
+ DumpBytes = opts::dump::DumpTypeData;
+ DumpExtras = opts::dump::DumpTypeExtras;
+ Indices.assign(opts::dump::DumpTypeIndex.begin(),
+ opts::dump::DumpTypeIndex.end());
+ } else if (StreamIdx == StreamIPI) {
+ Present = getPdb().hasPDBIpiStream();
+ DumpTypes = opts::dump::DumpIds;
+ DumpBytes = opts::dump::DumpIdData;
+ DumpExtras = opts::dump::DumpIdExtras;
+ Indices.assign(opts::dump::DumpIdIndex.begin(),
+ opts::dump::DumpIdIndex.end());
+ }
+
+ if (!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()
+ : getPdb().getPDBIpiStream());
+
+ auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids();
+
+ // Only emit notes about referenced/unreferenced for types.
+ TypeReferenceTracker *MaybeTracker =
+ (StreamIdx == StreamTPI) ? RefTracker.get() : nullptr;
+
+ // Enable resolving forward decls.
+ Stream.buildHashMap();
+
+ if (DumpTypes || !Indices.empty()) {
+ if (Indices.empty())
+ dumpFullTypeStream(P, Types, MaybeTracker, Stream.getNumTypeRecords(),
+ Stream.getNumHashBuckets(), Stream.getHashValues(),
+ &Stream, DumpBytes, DumpExtras);
+ else {
+ std::vector<TypeIndex> TiList(Indices.begin(), Indices.end());
+ dumpPartialTypeStream(P, Types, MaybeTracker, Stream, TiList, DumpBytes,
+ DumpExtras, opts::dump::DumpTypeDependents);
+ }
+ }
+
+ if (DumpExtras) {
+ P.NewLine();
+
+ P.formatLine("Header Version: {0}",
+ static_cast<uint32_t>(Stream.getTpiVersion()));
+ P.formatLine("Hash Stream Index: {0}", Stream.getTypeHashStreamIndex());
+ P.formatLine("Aux Hash Stream Index: {0}",
+ Stream.getTypeHashStreamAuxIndex());
+ P.formatLine("Hash Key Size: {0}", Stream.getHashKeySize());
+ P.formatLine("Num Hash Buckets: {0}", Stream.getNumHashBuckets());
+
+ auto IndexOffsets = Stream.getTypeIndexOffsets();
+ P.formatLine("Type Index Offsets:");
+ for (const auto &IO : IndexOffsets) {
+ AutoIndent Indent2(P);
+ P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset));
+ }
+
+ 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());
+ }
+ }
+ }
+ }
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpModuleSymsForObj() {
+ printHeader(P, "Symbols");
+
+ AutoIndent Indent(P);
+
+ ExitOnError Err("Unexpected error processing symbols: ");
+
+ auto &Types = File.types();
+
+ SymbolVisitorCallbackPipeline Pipeline;
+ SymbolDeserializer Deserializer(nullptr, CodeViewContainer::ObjectFile);
+ MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types, Types);
+
+ Pipeline.addCallbackToPipeline(Deserializer);
+ Pipeline.addCallbackToPipeline(Dumper);
+ CVSymbolVisitor Visitor(Pipeline);
+
+ std::unique_ptr<llvm::Error> SymbolError;
+
+ iterateModuleSubsections<DebugSymbolsSubsectionRef>(
+ File, PrintScope{P, 2},
+ [&](uint32_t Modi, const SymbolGroup &Strings,
+ DebugSymbolsSubsectionRef &Symbols) {
+ Dumper.setSymbolGroup(&Strings);
+ for (auto Symbol : Symbols) {
+ if (auto EC = Visitor.visitSymbolRecord(Symbol)) {
+ SymbolError = std::make_unique<Error>(std::move(EC));
+ return;
+ }
+ }
+ });
+
+ if (SymbolError)
+ return std::move(*SymbolError);
+
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpModuleSymsForPdb() {
+ printHeader(P, "Symbols");
+
+ if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
+ return Error::success();
+ }
+
+ AutoIndent Indent(P);
+ ExitOnError Err("Unexpected error processing symbols: ");
+
+ auto &Ids = File.ids();
+ auto &Types = File.types();
+
+ iterateSymbolGroups(
+ File, PrintScope{P, 2}, [&](uint32_t I, const SymbolGroup &Strings) {
+ auto ExpectedModS = getModuleDebugStream(File.pdb(), I);
+ if (!ExpectedModS) {
+ P.formatLine("Error loading module stream {0}. {1}", I,
+ toString(ExpectedModS.takeError()));
+ return;
+ }
+
+ ModuleDebugStreamRef &ModS = *ExpectedModS;
+
+ SymbolVisitorCallbackPipeline Pipeline;
+ SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
+ MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Strings,
+ Ids, Types);
+
+ Pipeline.addCallbackToPipeline(Deserializer);
+ Pipeline.addCallbackToPipeline(Dumper);
+ CVSymbolVisitor Visitor(Pipeline);
+ auto SS = ModS.getSymbolsSubstream();
+ if (auto EC =
+ Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) {
+ P.formatLine("Error while processing symbol records. {0}",
+ toString(std::move(EC)));
+ return;
+ }
+ });
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpTypeRefStats() {
+ printHeader(P, "Type Reference Statistics");
+ AutoIndent Indent(P);
+
+ // Sum the byte size of all type records, and the size and count of all
+ // referenced records.
+ size_t TotalRecs = File.types().size();
+ size_t RefRecs = 0;
+ size_t TotalBytes = 0;
+ size_t RefBytes = 0;
+ auto &Types = File.types();
+ for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) {
+ CVType Type = File.types().getType(*TI);
+ TotalBytes += Type.length();
+ if (RefTracker->isTypeReferenced(*TI)) {
+ ++RefRecs;
+ RefBytes += Type.length();
+ }
+ }
+
+ P.formatLine("Records referenced: {0:N} / {1:N} {2:P}", RefRecs, TotalRecs,
+ (double)RefRecs / TotalRecs);
+ P.formatLine("Bytes referenced: {0:N} / {1:N} {2:P}", RefBytes, TotalBytes,
+ (double)RefBytes / TotalBytes);
+
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpGSIRecords() {
+ printHeader(P, "GSI Records");
+
+ if (File.isObj()) {
+ printStreamNotValidForObj();
+ return Error::success();
+ }
+
+ if (!getPdb().hasPDBSymbolStream()) {
+ printStreamNotPresent("GSI Common Symbol");
+ return Error::success();
+ }
+
+ AutoIndent Indent(P);
+
+ auto &Records = cantFail(getPdb().getPDBSymbolStream());
+ auto &Types = File.types();
+ auto &Ids = File.ids();
+
+ P.printLine("Records");
+ SymbolVisitorCallbackPipeline Pipeline;
+ SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
+ MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
+
+ Pipeline.addCallbackToPipeline(Deserializer);
+ Pipeline.addCallbackToPipeline(Dumper);
+ CVSymbolVisitor Visitor(Pipeline);
+
+ BinaryStreamRef SymStream = Records.getSymbolArray().getUnderlyingStream();
+ if (auto E = Visitor.visitSymbolStream(Records.getSymbolArray(), 0))
+ return E;
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpGlobals() {
+ printHeader(P, "Global Symbols");
+
+ if (File.isObj()) {
+ printStreamNotValidForObj();
+ return Error::success();
+ }
+
+ if (!getPdb().hasPDBGlobalsStream()) {
+ printStreamNotPresent("Globals");
+ return Error::success();
+ }
+
+ AutoIndent Indent(P);
+ ExitOnError Err("Error dumping globals stream: ");
+ auto &Globals = Err(getPdb().getPDBGlobalsStream());
+
+ 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");
+
+ if (File.isObj()) {
+ printStreamNotValidForObj();
+ return Error::success();
+ }
+
+ if (!getPdb().hasPDBPublicsStream()) {
+ printStreamNotPresent("Publics");
+ return Error::success();
+ }
+
+ AutoIndent Indent(P);
+ ExitOnError Err("Error dumping publics stream: ");
+ auto &Publics = Err(getPdb().getPDBPublicsStream());
+
+ const GSIHashTable &PublicsTable = Publics.getPublicsTable();
+ if (opts::dump::DumpPublicExtras) {
+ P.printLine("Publics Header");
+ AutoIndent Indent(P);
+ P.formatLine("sym hash = {0}, thunk table addr = {1}", Publics.getSymHash(),
+ formatSegmentOffset(Publics.getThunkTableSection(),
+ Publics.getThunkTableOffset()));
+ }
+ Err(dumpSymbolsFromGSI(PublicsTable, opts::dump::DumpPublicExtras));
+
+ // Skip the rest if we aren't dumping extras.
+ if (!opts::dump::DumpPublicExtras)
+ return Error::success();
+
+ P.formatLine("Address Map");
+ {
+ // These are offsets into the publics stream sorted by secidx:secrel.
+ AutoIndent Indent2(P);
+ for (uint32_t Addr : Publics.getAddressMap())
+ P.formatLine("off = {0}", Addr);
+ }
+
+ // The thunk map is optional debug info used for ILT thunks.
+ if (!Publics.getThunkMap().empty()) {
+ P.formatLine("Thunk Map");
+ AutoIndent Indent2(P);
+ for (uint32_t Addr : Publics.getThunkMap())
+ P.formatLine("{0:x8}", Addr);
+ }
+
+ // The section offsets table appears to be empty when incremental linking
+ // isn't in use.
+ if (!Publics.getSectionOffsets().empty()) {
+ P.formatLine("Section Offsets");
+ AutoIndent Indent2(P);
+ for (const SectionOffset &SO : Publics.getSectionOffsets())
+ P.formatLine("{0:x4}:{1:x8}", uint16_t(SO.Isect), uint32_t(SO.Off));
+ }
+
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table,
+ bool HashExtras) {
+ auto ExpectedSyms = getPdb().getPDBSymbolStream();
+ if (!ExpectedSyms)
+ return ExpectedSyms.takeError();
+ auto &Types = File.types();
+ auto &Ids = File.ids();
+
+ if (HashExtras) {
+ P.printLine("GSI Header");
+ AutoIndent Indent(P);
+ P.formatLine("sig = {0:X}, hdr = {1:X}, hr size = {2}, num buckets = {3}",
+ Table.getVerSignature(), Table.getVerHeader(),
+ Table.getHashRecordSize(), Table.getNumBuckets());
+ }
+
+ {
+ P.printLine("Records");
+ SymbolVisitorCallbackPipeline Pipeline;
+ SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
+ MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
+
+ Pipeline.addCallbackToPipeline(Deserializer);
+ Pipeline.addCallbackToPipeline(Dumper);
+ CVSymbolVisitor Visitor(Pipeline);
+
+
+ BinaryStreamRef SymStream =
+ ExpectedSyms->getSymbolArray().getUnderlyingStream();
+ for (uint32_t PubSymOff : Table) {
+ Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff);
+ if (!Sym)
+ return Sym.takeError();
+ if (auto E = Visitor.visitSymbolRecord(*Sym, PubSymOff))
+ return E;
+ }
+ }
+
+ // Return early if we aren't dumping public hash table and address map info.
+ if (HashExtras) {
+ P.formatLine("Hash Entries");
+ {
+ AutoIndent Indent2(P);
+ for (const PSHashRecord &HR : Table.HashRecords)
+ P.formatLine("off = {0}, refcnt = {1}", uint32_t(HR.Off),
+ uint32_t(HR.CRef));
+ }
+
+ P.formatLine("Hash Buckets");
+ {
+ AutoIndent Indent2(P);
+ for (uint32_t Hash : Table.HashBuckets)
+ P.formatLine("{0:x8}", Hash);
+ }
+ }
+
+ return Error::success();
+}
+
+static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel,
+ OMFSegDescFlags Flags) {
+ std::vector<std::string> Opts;
+ if (Flags == OMFSegDescFlags::None)
+ return "none";
+
+ PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read");
+ PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write");
+ PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute");
+ PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr");
+ PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector");
+ PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr");
+ PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group");
+ return typesetItemList(Opts, IndentLevel, 4, " | ");
+}
+
+Error DumpOutputStyle::dumpSectionHeaders() {
+ dumpSectionHeaders("Section Headers", DbgHeaderType::SectionHdr);
+ dumpSectionHeaders("Original Section Headers", DbgHeaderType::SectionHdrOrig);
+ return Error::success();
+}
+
+void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {
+ printHeader(P, Label);
+
+ if (File.isObj()) {
+ 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;
+ auto ExpectedHeaders = loadSectionHeaders(getPdb(), Type);
+ if (!ExpectedHeaders) {
+ P.printLine(toString(ExpectedHeaders.takeError()));
+ return;
+ }
+ std::tie(Stream, Headers) = std::move(*ExpectedHeaders);
+
+ uint32_t I = 1;
+ for (const auto &Header : Headers) {
+ P.NewLine();
+ P.formatLine("SECTION HEADER #{0}", I);
+ P.formatLine("{0,8} name", Header.Name);
+ P.formatLine("{0,8:X-} virtual size", uint32_t(Header.VirtualSize));
+ P.formatLine("{0,8:X-} virtual address", uint32_t(Header.VirtualAddress));
+ P.formatLine("{0,8:X-} size of raw data", uint32_t(Header.SizeOfRawData));
+ P.formatLine("{0,8:X-} file pointer to raw data",
+ uint32_t(Header.PointerToRawData));
+ P.formatLine("{0,8:X-} file pointer to relocation table",
+ uint32_t(Header.PointerToRelocations));
+ P.formatLine("{0,8:X-} file pointer to line numbers",
+ uint32_t(Header.PointerToLinenumbers));
+ P.formatLine("{0,8:X-} number of relocations",
+ uint32_t(Header.NumberOfRelocations));
+ P.formatLine("{0,8:X-} number of line numbers",
+ uint32_t(Header.NumberOfLinenumbers));
+ P.formatLine("{0,8:X-} flags", uint32_t(Header.Characteristics));
+ AutoIndent IndentMore(P, 9);
+ P.formatLine("{0}", formatSectionCharacteristics(
+ P.getIndentLevel(), Header.Characteristics, 1, ""));
+ ++I;
+ }
+}
+
+Error DumpOutputStyle::dumpSectionContribs() {
+ printHeader(P, "Section Contributions");
+
+ if (File.isObj()) {
+ printStreamNotValidForObj();
+ return Error::success();
+ }
+
+ if (!getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
+ return Error::success();
+ }
+
+ AutoIndent Indent(P);
+ ExitOnError Err("Error dumping section contributions: ");
+
+ auto &Dbi = Err(getPdb().getPDBDbiStream());
+
+ class Visitor : public ISectionContribVisitor {
+ public:
+ Visitor(LinePrinter &P, ArrayRef<std::string> Names) : P(P), Names(Names) {
+ auto Max = std::max_element(
+ Names.begin(), Names.end(),
+ [](StringRef S1, StringRef S2) { return S1.size() < S2.size(); });
+ MaxNameLen = (Max == Names.end() ? 0 : Max->size());
+ }
+ void visit(const SectionContrib &SC) override {
+ dumpSectionContrib(P, SC, Names, MaxNameLen);
+ }
+ void visit(const SectionContrib2 &SC) override {
+ dumpSectionContrib(P, SC, Names, MaxNameLen);
+ }
+
+ private:
+ LinePrinter &P;
+ uint32_t MaxNameLen;
+ ArrayRef<std::string> Names;
+ };
+
+ std::vector<std::string> Names = getSectionNames(getPdb());
+ Visitor V(P, makeArrayRef(Names));
+ Dbi.visitSectionContributions(V);
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpSectionMap() {
+ printHeader(P, "Section Map");
+
+ if (File.isObj()) {
+ printStreamNotValidForObj();
+ return Error::success();
+ }
+
+ if (!getPdb().hasPDBDbiStream()) {
+ printStreamNotPresent("DBI");
+ return Error::success();
+ }
+
+ AutoIndent Indent(P);
+ ExitOnError Err("Error dumping section map: ");
+
+ auto &Dbi = Err(getPdb().getPDBDbiStream());
+
+ uint32_t I = 0;
+ for (auto &M : Dbi.getSectionMap()) {
+ P.formatLine(
+ "Section {0:4} | ovl = {1}, group = {2}, frame = {3}, name = {4}", I,
+ fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName));
+ P.formatLine(" class = {0}, offset = {1}, size = {2}",
+ fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength));
+ P.formatLine(" flags = {0}",
+ formatSegMapDescriptorFlag(
+ P.getIndentLevel() + 13,
+ static_cast<OMFSegDescFlags>(uint16_t(M.Flags))));
+ ++I;
+ }
+ return Error::success();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/DumpOutputStyle.h b/contrib/libs/llvm14/tools/llvm-pdbutil/DumpOutputStyle.h
new file mode 100644
index 00000000000..041fb93a18a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/DumpOutputStyle.h
@@ -0,0 +1,116 @@
+//===- DumpOutputStyle.h -------------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_DUMPOUTPUTSTYLE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_DUMPOUTPUTSTYLE_H
+
+#include "LinePrinter.h"
+#include "OutputStyle.h"
+#include "StreamUtil.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
+
+#include <string>
+
+namespace llvm {
+namespace object {
+class COFFObjectFile;
+}
+
+namespace pdb {
+class GSIHashTable;
+class InputFile;
+class TypeReferenceTracker;
+
+struct StatCollection {
+ struct Stat {
+ Stat() {}
+ Stat(uint32_t Count, uint32_t Size) : Count(Count), Size(Size) {}
+ uint32_t Count = 0;
+ uint32_t Size = 0;
+
+ void update(uint32_t RecordSize) {
+ ++Count;
+ Size += RecordSize;
+ }
+ };
+
+ using KindAndStat = std::pair<uint32_t, Stat>;
+
+ void update(uint32_t Kind, uint32_t RecordSize) {
+ Totals.update(RecordSize);
+ auto Iter = Individual.try_emplace(Kind, 1, RecordSize);
+ if (!Iter.second)
+ Iter.first->second.update(RecordSize);
+ }
+ Stat Totals;
+ DenseMap<uint32_t, Stat> Individual;
+
+ std::vector<KindAndStat> getStatsSortedBySize() const;
+};
+
+class DumpOutputStyle : public OutputStyle {
+
+public:
+ DumpOutputStyle(InputFile &File);
+ ~DumpOutputStyle() override;
+
+ Error dump() override;
+
+private:
+ PDBFile &getPdb();
+ object::COFFObjectFile &getObj();
+
+ void printStreamNotValidForObj();
+ void printStreamNotPresent(StringRef StreamName);
+
+ Error dumpFileSummary();
+ Error dumpStreamSummary();
+ Error dumpSymbolStats();
+ Error dumpUdtStats();
+ Error dumpTypeStats();
+ Error dumpNamedStreams();
+ Error dumpStringTable();
+ Error dumpStringTableFromPdb();
+ Error dumpStringTableFromObj();
+ Error dumpLines();
+ Error dumpInlineeLines();
+ Error dumpXmi();
+ Error dumpXme();
+ Error dumpFpo();
+ Error dumpOldFpo(PDBFile &File);
+ Error dumpNewFpo(PDBFile &File);
+ Error dumpTpiStream(uint32_t StreamIdx);
+ Error dumpTypesFromObjectFile();
+ Error dumpTypeRefStats();
+ Error dumpModules();
+ Error dumpModuleFiles();
+ Error dumpModuleSymsForPdb();
+ Error dumpModuleSymsForObj();
+ Error dumpGSIRecords();
+ Error dumpGlobals();
+ Error dumpPublics();
+ Error dumpSymbolsFromGSI(const GSIHashTable &Table, bool HashExtras);
+ Error dumpSectionHeaders();
+ Error dumpSectionContribs();
+ Error dumpSectionMap();
+
+ void dumpSectionHeaders(StringRef Label, DbgHeaderType Type);
+
+ InputFile &File;
+ std::unique_ptr<TypeReferenceTracker> RefTracker;
+ LinePrinter P;
+ SmallVector<StreamInfo, 32> StreamPurposes;
+};
+} // namespace pdb
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/ExplainOutputStyle.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/ExplainOutputStyle.cpp
new file mode 100644
index 00000000000..b631bdf8f2b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/ExplainOutputStyle.cpp
@@ -0,0 +1,468 @@
+//===- ExplainOutputStyle.cpp --------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExplainOutputStyle.h"
+
+#include "FormatUtil.h"
+#include "InputFile.h"
+#include "StreamUtil.h"
+#include "llvm-pdbutil.h"
+
+#include "llvm/DebugInfo/CodeView/Formatters.h"
+#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
+#include "llvm/Support/BinaryByteStream.h"
+#include "llvm/Support/BinaryStreamArray.h"
+#include "llvm/Support/Error.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::msf;
+using namespace llvm::pdb;
+
+ExplainOutputStyle::ExplainOutputStyle(InputFile &File, uint64_t FileOffset)
+ : File(File), FileOffset(FileOffset), P(2, false, outs()) {}
+
+Error ExplainOutputStyle::dump() {
+ P.formatLine("Explaining file offset {0} of file '{1}'.", FileOffset,
+ File.getFilePath());
+
+ if (File.isPdb())
+ return explainPdbFile();
+
+ return explainBinaryFile();
+}
+
+Error ExplainOutputStyle::explainPdbFile() {
+ bool IsAllocated = explainPdbBlockStatus();
+ if (!IsAllocated)
+ return Error::success();
+
+ AutoIndent Indent(P);
+ if (isPdbSuperBlock())
+ explainPdbSuperBlockOffset();
+ else if (isPdbFpmBlock())
+ explainPdbFpmBlockOffset();
+ else if (isPdbBlockMapBlock())
+ explainPdbBlockMapOffset();
+ else if (isPdbStreamDirectoryBlock())
+ explainPdbStreamDirectoryOffset();
+ else if (auto Index = getPdbBlockStreamIndex())
+ explainPdbStreamOffset(*Index);
+ else
+ explainPdbUnknownBlock();
+ return Error::success();
+}
+
+Error ExplainOutputStyle::explainBinaryFile() {
+ std::unique_ptr<BinaryByteStream> Stream =
+ std::make_unique<BinaryByteStream>(File.unknown().getBuffer(),
+ llvm::support::little);
+ switch (opts::explain::InputType) {
+ case opts::explain::InputFileType::DBIStream: {
+ DbiStream Dbi(std::move(Stream));
+ if (auto EC = Dbi.reload(nullptr))
+ return EC;
+ explainStreamOffset(Dbi, FileOffset);
+ break;
+ }
+ case opts::explain::InputFileType::PDBStream: {
+ InfoStream Info(std::move(Stream));
+ if (auto EC = Info.reload())
+ return EC;
+ explainStreamOffset(Info, FileOffset);
+ break;
+ }
+ default:
+ llvm_unreachable("Invalid input file type!");
+ }
+ return Error::success();
+}
+
+uint32_t ExplainOutputStyle::pdbBlockIndex() const {
+ return FileOffset / File.pdb().getBlockSize();
+}
+
+uint32_t ExplainOutputStyle::pdbBlockOffset() const {
+ uint64_t BlockStart = pdbBlockIndex() * File.pdb().getBlockSize();
+ assert(FileOffset >= BlockStart);
+ return FileOffset - BlockStart;
+}
+
+bool ExplainOutputStyle::isPdbSuperBlock() const {
+ return pdbBlockIndex() == 0;
+}
+
+bool ExplainOutputStyle::isPdbFpm1() const {
+ return ((pdbBlockIndex() - 1) % File.pdb().getBlockSize() == 0);
+}
+bool ExplainOutputStyle::isPdbFpm2() const {
+ return ((pdbBlockIndex() - 2) % File.pdb().getBlockSize() == 0);
+}
+
+bool ExplainOutputStyle::isPdbFpmBlock() const {
+ return isPdbFpm1() || isPdbFpm2();
+}
+
+bool ExplainOutputStyle::isPdbBlockMapBlock() const {
+ return pdbBlockIndex() == File.pdb().getBlockMapIndex();
+}
+
+bool ExplainOutputStyle::isPdbStreamDirectoryBlock() const {
+ const auto &Layout = File.pdb().getMsfLayout();
+ return llvm::is_contained(Layout.DirectoryBlocks, pdbBlockIndex());
+}
+
+Optional<uint32_t> ExplainOutputStyle::getPdbBlockStreamIndex() const {
+ const auto &Layout = File.pdb().getMsfLayout();
+ for (const auto &Entry : enumerate(Layout.StreamMap)) {
+ if (!llvm::is_contained(Entry.value(), pdbBlockIndex()))
+ continue;
+ return Entry.index();
+ }
+ return None;
+}
+
+bool ExplainOutputStyle::explainPdbBlockStatus() {
+ if (FileOffset >= File.pdb().getFileSize()) {
+ P.formatLine("Address {0} is not in the file (file size = {1}).",
+ FileOffset, File.pdb().getFileSize());
+ return false;
+ }
+ P.formatLine("Block:Offset = {2:X-}:{1:X-4}.", FileOffset, pdbBlockOffset(),
+ pdbBlockIndex());
+
+ bool IsFree = File.pdb().getMsfLayout().FreePageMap[pdbBlockIndex()];
+ P.formatLine("Address is in block {0} ({1}allocated).", pdbBlockIndex(),
+ IsFree ? "un" : "");
+ return !IsFree;
+}
+
+#define endof(Class, Field) (offsetof(Class, Field) + sizeof(Class::Field))
+
+void ExplainOutputStyle::explainPdbSuperBlockOffset() {
+ P.formatLine("This corresponds to offset {0} of the MSF super block, ",
+ pdbBlockOffset());
+ if (pdbBlockOffset() < endof(SuperBlock, MagicBytes))
+ P.printLine("which is part of the MSF file magic.");
+ else if (pdbBlockOffset() < endof(SuperBlock, BlockSize)) {
+ P.printLine("which contains the block size of the file.");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->BlockSize));
+ } else if (pdbBlockOffset() < endof(SuperBlock, FreeBlockMapBlock)) {
+ P.printLine("which contains the index of the FPM block (e.g. 1 or 2).");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->FreeBlockMapBlock));
+ } else if (pdbBlockOffset() < endof(SuperBlock, NumBlocks)) {
+ P.printLine("which contains the number of blocks in the file.");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->NumBlocks));
+ } else if (pdbBlockOffset() < endof(SuperBlock, NumDirectoryBytes)) {
+ P.printLine("which contains the number of bytes in the stream directory.");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->NumDirectoryBytes));
+ } else if (pdbBlockOffset() < endof(SuperBlock, Unknown1)) {
+ P.printLine("whose purpose is unknown.");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->Unknown1));
+ } else if (pdbBlockOffset() < endof(SuperBlock, BlockMapAddr)) {
+ P.printLine("which contains the file offset of the block map.");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->BlockMapAddr));
+ } else {
+ assert(pdbBlockOffset() > sizeof(SuperBlock));
+ P.printLine(
+ "which is outside the range of valid data for the super block.");
+ }
+}
+
+static std::string toBinaryString(uint8_t Byte) {
+ char Result[9] = {0};
+ for (int I = 0; I < 8; ++I) {
+ char C = (Byte & 1) ? '1' : '0';
+ Result[I] = C;
+ Byte >>= 1;
+ }
+ return std::string(Result);
+}
+
+void ExplainOutputStyle::explainPdbFpmBlockOffset() {
+ const MSFLayout &Layout = File.pdb().getMsfLayout();
+ uint32_t MainFpm = Layout.mainFpmBlock();
+ uint32_t AltFpm = Layout.alternateFpmBlock();
+
+ assert(isPdbFpmBlock());
+ uint32_t Fpm = isPdbFpm1() ? 1 : 2;
+ uint32_t FpmChunk = pdbBlockIndex() / File.pdb().getBlockSize();
+ assert((Fpm == MainFpm) || (Fpm == AltFpm));
+ (void)AltFpm;
+ bool IsMain = (Fpm == MainFpm);
+ P.formatLine("Address is in FPM{0} ({1} FPM)", Fpm, IsMain ? "Main" : "Alt");
+ uint32_t DescribedBlockStart =
+ 8 * (FpmChunk * File.pdb().getBlockSize() + pdbBlockOffset());
+ if (DescribedBlockStart > File.pdb().getBlockCount()) {
+ P.printLine("Address is in extraneous FPM space.");
+ return;
+ }
+
+ P.formatLine("Address describes the allocation status of blocks [{0},{1})",
+ DescribedBlockStart, DescribedBlockStart + 8);
+ ArrayRef<uint8_t> Bytes;
+ cantFail(File.pdb().getMsfBuffer().readBytes(FileOffset, 1, Bytes));
+ P.formatLine("Status = {0} (Note: 0 = allocated, 1 = free)",
+ toBinaryString(Bytes[0]));
+}
+
+void ExplainOutputStyle::explainPdbBlockMapOffset() {
+ uint64_t BlockMapOffset = File.pdb().getBlockMapOffset();
+ uint32_t OffsetInBlock = FileOffset - BlockMapOffset;
+ P.formatLine("Address is at offset {0} of the directory block list",
+ OffsetInBlock);
+}
+
+static uint32_t getOffsetInStream(ArrayRef<support::ulittle32_t> StreamBlocks,
+ uint64_t FileOffset, uint32_t BlockSize) {
+ uint32_t BlockIndex = FileOffset / BlockSize;
+ uint32_t OffsetInBlock = FileOffset - BlockIndex * BlockSize;
+
+ auto Iter = llvm::find(StreamBlocks, BlockIndex);
+ assert(Iter != StreamBlocks.end());
+ uint32_t StreamBlockIndex = std::distance(StreamBlocks.begin(), Iter);
+ return StreamBlockIndex * BlockSize + OffsetInBlock;
+}
+
+void ExplainOutputStyle::explainPdbStreamOffset(uint32_t Stream) {
+ SmallVector<StreamInfo, 12> Streams;
+ discoverStreamPurposes(File.pdb(), Streams);
+
+ assert(Stream <= Streams.size());
+ const StreamInfo &S = Streams[Stream];
+ const auto &Layout = File.pdb().getStreamLayout(Stream);
+ uint32_t StreamOff =
+ getOffsetInStream(Layout.Blocks, FileOffset, File.pdb().getBlockSize());
+ P.formatLine("Address is at offset {0}/{1} of Stream {2} ({3}){4}.",
+ StreamOff, Layout.Length, Stream, S.getLongName(),
+ (StreamOff > Layout.Length) ? " in unused space" : "");
+ switch (S.getPurpose()) {
+ case StreamPurpose::DBI: {
+ DbiStream &Dbi = cantFail(File.pdb().getPDBDbiStream());
+ explainStreamOffset(Dbi, StreamOff);
+ break;
+ }
+ case StreamPurpose::PDB: {
+ InfoStream &Info = cantFail(File.pdb().getPDBInfoStream());
+ explainStreamOffset(Info, StreamOff);
+ break;
+ }
+ case StreamPurpose::IPI:
+ case StreamPurpose::TPI:
+ case StreamPurpose::ModuleStream:
+ case StreamPurpose::NamedStream:
+ default:
+ break;
+ }
+}
+
+void ExplainOutputStyle::explainPdbStreamDirectoryOffset() {
+ auto DirectoryBlocks = File.pdb().getDirectoryBlockArray();
+ const auto &Layout = File.pdb().getMsfLayout();
+ uint32_t StreamOff =
+ getOffsetInStream(DirectoryBlocks, FileOffset, File.pdb().getBlockSize());
+ P.formatLine("Address is at offset {0}/{1} of Stream Directory{2}.",
+ StreamOff, uint32_t(Layout.SB->NumDirectoryBytes),
+ uint32_t(StreamOff > Layout.SB->NumDirectoryBytes)
+ ? " in unused space"
+ : "");
+}
+
+void ExplainOutputStyle::explainPdbUnknownBlock() {
+ P.formatLine("Address has unknown purpose.");
+}
+
+template <typename T>
+static void printStructField(LinePrinter &P, StringRef Label, T Value) {
+ P.formatLine("which contains {0}.", Label);
+ P.formatLine("The current value is {0}.", Value);
+}
+
+static void explainDbiHeaderOffset(LinePrinter &P, DbiStream &Dbi,
+ uint32_t Offset) {
+ const DbiStreamHeader *Header = Dbi.getHeader();
+ assert(Header != nullptr);
+
+ if (Offset < endof(DbiStreamHeader, VersionSignature))
+ printStructField(P, "the DBI Stream Version Signature",
+ int32_t(Header->VersionSignature));
+ else if (Offset < endof(DbiStreamHeader, VersionHeader))
+ printStructField(P, "the DBI Stream Version Header",
+ uint32_t(Header->VersionHeader));
+ else if (Offset < endof(DbiStreamHeader, Age))
+ printStructField(P, "the age of the DBI Stream", uint32_t(Header->Age));
+ else if (Offset < endof(DbiStreamHeader, GlobalSymbolStreamIndex))
+ printStructField(P, "the index of the Global Symbol Stream",
+ uint16_t(Header->GlobalSymbolStreamIndex));
+ else if (Offset < endof(DbiStreamHeader, BuildNumber))
+ printStructField(P, "the build number", uint16_t(Header->BuildNumber));
+ else if (Offset < endof(DbiStreamHeader, PublicSymbolStreamIndex))
+ printStructField(P, "the index of the Public Symbol Stream",
+ uint16_t(Header->PublicSymbolStreamIndex));
+ else if (Offset < endof(DbiStreamHeader, PdbDllVersion))
+ printStructField(P, "the version of mspdb.dll",
+ uint16_t(Header->PdbDllVersion));
+ else if (Offset < endof(DbiStreamHeader, SymRecordStreamIndex))
+ printStructField(P, "the index of the Symbol Record Stream",
+ uint16_t(Header->SymRecordStreamIndex));
+ else if (Offset < endof(DbiStreamHeader, PdbDllRbld))
+ printStructField(P, "the rbld of mspdb.dll", uint16_t(Header->PdbDllRbld));
+ else if (Offset < endof(DbiStreamHeader, ModiSubstreamSize))
+ printStructField(P, "the size of the Module Info Substream",
+ int32_t(Header->ModiSubstreamSize));
+ else if (Offset < endof(DbiStreamHeader, SecContrSubstreamSize))
+ printStructField(P, "the size of the Section Contribution Substream",
+ int32_t(Header->SecContrSubstreamSize));
+ else if (Offset < endof(DbiStreamHeader, SectionMapSize))
+ printStructField(P, "the size of the Section Map Substream",
+ int32_t(Header->SectionMapSize));
+ else if (Offset < endof(DbiStreamHeader, FileInfoSize))
+ printStructField(P, "the size of the File Info Substream",
+ int32_t(Header->FileInfoSize));
+ else if (Offset < endof(DbiStreamHeader, TypeServerSize))
+ printStructField(P, "the size of the Type Server Map",
+ int32_t(Header->TypeServerSize));
+ else if (Offset < endof(DbiStreamHeader, MFCTypeServerIndex))
+ printStructField(P, "the index of the MFC Type Server stream",
+ uint32_t(Header->MFCTypeServerIndex));
+ else if (Offset < endof(DbiStreamHeader, OptionalDbgHdrSize))
+ printStructField(P, "the size of the Optional Debug Stream array",
+ int32_t(Header->OptionalDbgHdrSize));
+ else if (Offset < endof(DbiStreamHeader, ECSubstreamSize))
+ printStructField(P, "the size of the Edit & Continue Substream",
+ int32_t(Header->ECSubstreamSize));
+ else if (Offset < endof(DbiStreamHeader, Flags))
+ printStructField(P, "the DBI Stream flags", uint16_t(Header->Flags));
+ else if (Offset < endof(DbiStreamHeader, MachineType))
+ printStructField(P, "the machine type", uint16_t(Header->MachineType));
+ else if (Offset < endof(DbiStreamHeader, Reserved))
+ printStructField(P, "reserved data", uint32_t(Header->Reserved));
+}
+
+static void explainDbiModiSubstreamOffset(LinePrinter &P, DbiStream &Dbi,
+ uint32_t Offset) {
+ VarStreamArray<DbiModuleDescriptor> ModuleDescriptors;
+ BinaryStreamRef ModiSubstreamData = Dbi.getModiSubstreamData().StreamData;
+ BinaryStreamReader Reader(ModiSubstreamData);
+
+ cantFail(Reader.readArray(ModuleDescriptors, ModiSubstreamData.getLength()));
+ auto Prev = ModuleDescriptors.begin();
+ assert(Prev.offset() == 0);
+ auto Current = Prev;
+ uint32_t Index = 0;
+ while (true) {
+ Prev = Current;
+ ++Current;
+ if (Current == ModuleDescriptors.end() || Offset < Current.offset())
+ break;
+ ++Index;
+ }
+
+ const DbiModuleDescriptor &Descriptor = *Prev;
+ P.formatLine("which contains the descriptor for module {0} ({1}).", Index,
+ Descriptor.getModuleName());
+}
+
+template <typename T>
+static void dontExplain(LinePrinter &Printer, T &Stream, uint32_t Offset) {}
+
+template <typename T, typename SubstreamRangeT>
+static void explainSubstreamOffset(LinePrinter &P, uint32_t OffsetInStream,
+ T &Stream,
+ const SubstreamRangeT &Substreams) {
+ uint32_t SubOffset = OffsetInStream;
+ for (const auto &Entry : Substreams) {
+ if (Entry.Size <= 0)
+ continue;
+ uint32_t S = static_cast<uint32_t>(Entry.Size);
+ if (SubOffset < S) {
+ P.formatLine("address is at offset {0}/{1} of the {2}.", SubOffset, S,
+ Entry.Label);
+ Entry.Explain(P, Stream, SubOffset);
+ return;
+ }
+ SubOffset -= S;
+ }
+}
+
+void ExplainOutputStyle::explainStreamOffset(DbiStream &Dbi,
+ uint32_t OffsetInStream) {
+ P.printLine("Within the DBI stream:");
+ AutoIndent Indent(P);
+ const DbiStreamHeader *Header = Dbi.getHeader();
+ assert(Header != nullptr);
+
+ struct SubstreamInfo {
+ int32_t Size;
+ StringRef Label;
+ void (*Explain)(LinePrinter &, DbiStream &, uint32_t);
+ } Substreams[] = {
+ {sizeof(DbiStreamHeader), "DBI Stream Header", explainDbiHeaderOffset},
+ {int32_t(Header->ModiSubstreamSize), "Module Info Substream",
+ explainDbiModiSubstreamOffset},
+ {int32_t(Header->SecContrSubstreamSize), "Section Contribution Substream",
+ dontExplain<DbiStream>},
+ {int32_t(Header->SectionMapSize), "Section Map", dontExplain<DbiStream>},
+ {int32_t(Header->FileInfoSize), "File Info Substream",
+ dontExplain<DbiStream>},
+ {int32_t(Header->TypeServerSize), "Type Server Map Substream",
+ dontExplain<DbiStream>},
+ {int32_t(Header->ECSubstreamSize), "Edit & Continue Substream",
+ dontExplain<DbiStream>},
+ {int32_t(Header->OptionalDbgHdrSize), "Optional Debug Stream Array",
+ dontExplain<DbiStream>},
+ };
+
+ explainSubstreamOffset(P, OffsetInStream, Dbi, Substreams);
+}
+
+static void explainPdbStreamHeaderOffset(LinePrinter &P, InfoStream &Info,
+ uint32_t Offset) {
+ const InfoStreamHeader *Header = Info.getHeader();
+ assert(Header != nullptr);
+
+ if (Offset < endof(InfoStreamHeader, Version))
+ printStructField(P, "the PDB Stream Version Signature",
+ uint32_t(Header->Version));
+ else if (Offset < endof(InfoStreamHeader, Signature))
+ printStructField(P, "the signature of the PDB Stream",
+ uint32_t(Header->Signature));
+ else if (Offset < endof(InfoStreamHeader, Age))
+ printStructField(P, "the age of the PDB", uint32_t(Header->Age));
+ else if (Offset < endof(InfoStreamHeader, Guid))
+ printStructField(P, "the guid of the PDB", fmt_guid(Header->Guid.Guid));
+}
+
+void ExplainOutputStyle::explainStreamOffset(InfoStream &Info,
+ uint32_t OffsetInStream) {
+ P.printLine("Within the PDB stream:");
+ AutoIndent Indent(P);
+
+ struct SubstreamInfo {
+ uint32_t Size;
+ StringRef Label;
+ void (*Explain)(LinePrinter &, InfoStream &, uint32_t);
+ } Substreams[] = {{sizeof(InfoStreamHeader), "PDB Stream Header",
+ explainPdbStreamHeaderOffset},
+ {Info.getNamedStreamMapByteSize(), "Named Stream Map",
+ dontExplain<InfoStream>},
+ {Info.getStreamSize(), "PDB Feature Signatures",
+ dontExplain<InfoStream>}};
+
+ explainSubstreamOffset(P, OffsetInStream, Info, Substreams);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/ExplainOutputStyle.h b/contrib/libs/llvm14/tools/llvm-pdbutil/ExplainOutputStyle.h
new file mode 100644
index 00000000000..f405cf615e9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/ExplainOutputStyle.h
@@ -0,0 +1,67 @@
+//===- ExplainOutputStyle.h ----------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_EXPLAINOUTPUTSTYLE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_EXPLAINOUTPUTSTYLE_H
+
+#include "LinePrinter.h"
+#include "OutputStyle.h"
+
+#include <string>
+
+namespace llvm {
+
+namespace pdb {
+
+class DbiStream;
+class InfoStream;
+class InputFile;
+
+class ExplainOutputStyle : public OutputStyle {
+
+public:
+ ExplainOutputStyle(InputFile &File, uint64_t FileOffset);
+
+ Error dump() override;
+
+private:
+ Error explainPdbFile();
+ Error explainBinaryFile();
+
+ bool explainPdbBlockStatus();
+
+ bool isPdbFpm1() const;
+ bool isPdbFpm2() const;
+
+ bool isPdbSuperBlock() const;
+ bool isPdbFpmBlock() const;
+ bool isPdbBlockMapBlock() const;
+ bool isPdbStreamDirectoryBlock() const;
+ Optional<uint32_t> getPdbBlockStreamIndex() const;
+
+ void explainPdbSuperBlockOffset();
+ void explainPdbFpmBlockOffset();
+ void explainPdbBlockMapOffset();
+ void explainPdbStreamDirectoryOffset();
+ void explainPdbStreamOffset(uint32_t Stream);
+ void explainPdbUnknownBlock();
+
+ void explainStreamOffset(DbiStream &Stream, uint32_t OffsetInStream);
+ void explainStreamOffset(InfoStream &Stream, uint32_t OffsetInStream);
+
+ uint32_t pdbBlockIndex() const;
+ uint32_t pdbBlockOffset() const;
+
+ InputFile &File;
+ const uint64_t FileOffset;
+ LinePrinter P;
+};
+} // namespace pdb
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/FormatUtil.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/FormatUtil.cpp
new file mode 100644
index 00000000000..b4837398f1d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/FormatUtil.cpp
@@ -0,0 +1,258 @@
+//===- FormatUtil.cpp ----------------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FormatUtil.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+std::string llvm::pdb::truncateStringBack(StringRef S, uint32_t MaxLen) {
+ if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3)
+ return std::string(S);
+
+ assert(MaxLen >= 3);
+ uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3);
+ S = S.take_front(FinalLen);
+ return std::string(S) + std::string("...");
+}
+
+std::string llvm::pdb::truncateStringMiddle(StringRef S, uint32_t MaxLen) {
+ if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3)
+ return std::string(S);
+
+ assert(MaxLen >= 3);
+ uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3);
+ StringRef Front = S.take_front(FinalLen / 2);
+ StringRef Back = S.take_back(Front.size());
+ return std::string(Front) + std::string("...") + std::string(Back);
+}
+
+std::string llvm::pdb::truncateStringFront(StringRef S, uint32_t MaxLen) {
+ if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3)
+ return std::string(S);
+
+ assert(MaxLen >= 3);
+ S = S.take_back(MaxLen - 3);
+ return std::string("...") + std::string(S);
+}
+
+std::string llvm::pdb::truncateQuotedNameFront(StringRef Label, StringRef Name,
+ uint32_t MaxLen) {
+ uint32_t RequiredExtraChars = Label.size() + 1 + 2;
+ if (MaxLen == 0 || RequiredExtraChars + Name.size() <= MaxLen)
+ return formatv("{0} \"{1}\"", Label, Name).str();
+
+ assert(MaxLen >= RequiredExtraChars);
+ std::string TN = truncateStringFront(Name, MaxLen - RequiredExtraChars);
+ return formatv("{0} \"{1}\"", Label, TN).str();
+}
+
+std::string llvm::pdb::truncateQuotedNameBack(StringRef Label, StringRef Name,
+ uint32_t MaxLen) {
+ uint32_t RequiredExtraChars = Label.size() + 1 + 2;
+ if (MaxLen == 0 || RequiredExtraChars + Name.size() <= MaxLen)
+ return formatv("{0} \"{1}\"", Label, Name).str();
+
+ assert(MaxLen >= RequiredExtraChars);
+ std::string TN = truncateStringBack(Name, MaxLen - RequiredExtraChars);
+ return formatv("{0} \"{1}\"", Label, TN).str();
+}
+
+std::string llvm::pdb::typesetItemList(ArrayRef<std::string> Opts,
+ uint32_t IndentLevel, uint32_t GroupSize,
+ StringRef Sep) {
+ std::string Result;
+ while (!Opts.empty()) {
+ ArrayRef<std::string> ThisGroup;
+ ThisGroup = Opts.take_front(GroupSize);
+ Opts = Opts.drop_front(ThisGroup.size());
+ Result += join(ThisGroup, Sep);
+ if (!Opts.empty()) {
+ Result += Sep;
+ Result += "\n";
+ Result += std::string(formatv("{0}", fmt_repeat(' ', IndentLevel)));
+ }
+ }
+ return Result;
+}
+
+std::string llvm::pdb::typesetStringList(uint32_t IndentLevel,
+ ArrayRef<StringRef> Strings) {
+ std::string Result = "[";
+ for (const auto &S : Strings) {
+ Result += std::string(formatv("\n{0}{1}", fmt_repeat(' ', IndentLevel), S));
+ }
+ Result += "]";
+ return Result;
+}
+
+std::string llvm::pdb::formatChunkKind(DebugSubsectionKind Kind,
+ bool Friendly) {
+ if (Friendly) {
+ switch (Kind) {
+ RETURN_CASE(DebugSubsectionKind, None, "none");
+ RETURN_CASE(DebugSubsectionKind, Symbols, "symbols");
+ RETURN_CASE(DebugSubsectionKind, Lines, "lines");
+ RETURN_CASE(DebugSubsectionKind, StringTable, "strings");
+ RETURN_CASE(DebugSubsectionKind, FileChecksums, "checksums");
+ RETURN_CASE(DebugSubsectionKind, FrameData, "frames");
+ RETURN_CASE(DebugSubsectionKind, InlineeLines, "inlinee lines");
+ RETURN_CASE(DebugSubsectionKind, CrossScopeImports, "xmi");
+ RETURN_CASE(DebugSubsectionKind, CrossScopeExports, "xme");
+ RETURN_CASE(DebugSubsectionKind, ILLines, "il lines");
+ RETURN_CASE(DebugSubsectionKind, FuncMDTokenMap, "func md token map");
+ RETURN_CASE(DebugSubsectionKind, TypeMDTokenMap, "type md token map");
+ RETURN_CASE(DebugSubsectionKind, MergedAssemblyInput,
+ "merged assembly input");
+ RETURN_CASE(DebugSubsectionKind, CoffSymbolRVA, "coff symbol rva");
+ }
+ } else {
+ switch (Kind) {
+ RETURN_CASE(DebugSubsectionKind, None, "none");
+ RETURN_CASE(DebugSubsectionKind, Symbols, "DEBUG_S_SYMBOLS");
+ RETURN_CASE(DebugSubsectionKind, Lines, "DEBUG_S_LINES");
+ RETURN_CASE(DebugSubsectionKind, StringTable, "DEBUG_S_STRINGTABLE");
+ RETURN_CASE(DebugSubsectionKind, FileChecksums, "DEBUG_S_FILECHKSMS");
+ RETURN_CASE(DebugSubsectionKind, FrameData, "DEBUG_S_FRAMEDATA");
+ RETURN_CASE(DebugSubsectionKind, InlineeLines, "DEBUG_S_INLINEELINES");
+ RETURN_CASE(DebugSubsectionKind, CrossScopeImports,
+ "DEBUG_S_CROSSSCOPEIMPORTS");
+ RETURN_CASE(DebugSubsectionKind, CrossScopeExports,
+ "DEBUG_S_CROSSSCOPEEXPORTS");
+ RETURN_CASE(DebugSubsectionKind, ILLines, "DEBUG_S_IL_LINES");
+ RETURN_CASE(DebugSubsectionKind, FuncMDTokenMap,
+ "DEBUG_S_FUNC_MDTOKEN_MAP");
+ RETURN_CASE(DebugSubsectionKind, TypeMDTokenMap,
+ "DEBUG_S_TYPE_MDTOKEN_MAP");
+ RETURN_CASE(DebugSubsectionKind, MergedAssemblyInput,
+ "DEBUG_S_MERGED_ASSEMBLYINPUT");
+ RETURN_CASE(DebugSubsectionKind, CoffSymbolRVA,
+ "DEBUG_S_COFF_SYMBOL_RVA");
+ }
+ }
+ return formatUnknownEnum(Kind);
+}
+
+std::string llvm::pdb::formatSymbolKind(SymbolKind K) {
+ switch (uint32_t(K)) {
+#define SYMBOL_RECORD(EnumName, value, name) \
+ case EnumName: \
+ return #EnumName;
+#define CV_SYMBOL(EnumName, value) SYMBOL_RECORD(EnumName, value, EnumName)
+#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def"
+ }
+ return formatUnknownEnum(K);
+}
+
+std::string llvm::pdb::formatTypeLeafKind(TypeLeafKind K) {
+ switch (K) {
+#define TYPE_RECORD(EnumName, value, name) \
+ case EnumName: \
+ return #EnumName;
+#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
+ default:
+ return formatv("UNKNOWN RECORD ({0:X})",
+ static_cast<std::underlying_type_t<TypeLeafKind>>(K))
+ .str();
+ }
+}
+
+std::string llvm::pdb::formatSegmentOffset(uint16_t Segment, uint32_t Offset) {
+ return std::string(formatv("{0:4}:{1:4}", Segment, Offset));
+}
+
+#define PUSH_CHARACTERISTIC_FLAG(Enum, TheOpt, Value, Style, Descriptive) \
+ PUSH_FLAG(Enum, TheOpt, Value, \
+ ((Style == CharacteristicStyle::HeaderDefinition) ? #TheOpt \
+ : Descriptive))
+
+#define PUSH_MASKED_CHARACTERISTIC_FLAG(Enum, Mask, TheOpt, Value, Style, \
+ Descriptive) \
+ PUSH_MASKED_FLAG(Enum, Mask, TheOpt, Value, \
+ ((Style == CharacteristicStyle::HeaderDefinition) \
+ ? #TheOpt \
+ : Descriptive))
+
+std::string llvm::pdb::formatSectionCharacteristics(uint32_t IndentLevel,
+ uint32_t C,
+ uint32_t FlagsPerLine,
+ StringRef Separator,
+ CharacteristicStyle Style) {
+ using SC = COFF::SectionCharacteristics;
+ std::vector<std::string> Opts;
+ if (C == COFF::SC_Invalid)
+ return "invalid";
+ if (C == 0)
+ return "none";
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_TYPE_NOLOAD, C, Style, "noload");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_TYPE_NO_PAD, C, Style, "no padding");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_CNT_CODE, C, Style, "code");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_CNT_INITIALIZED_DATA, C, Style,
+ "initialized data");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_CNT_UNINITIALIZED_DATA, C, Style,
+ "uninitialized data");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_LNK_OTHER, C, Style, "other");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_LNK_INFO, C, Style, "info");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_LNK_REMOVE, C, Style, "remove");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_LNK_COMDAT, C, Style, "comdat");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_GPREL, C, Style, "gp rel");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_MEM_PURGEABLE, C, Style, "purgeable");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_MEM_16BIT, C, Style, "16-bit");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_MEM_LOCKED, C, Style, "locked");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_MEM_PRELOAD, C, Style, "preload");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1BYTES, C,
+ Style, "1 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2BYTES, C,
+ Style, "2 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4BYTES, C,
+ Style, "4 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8BYTES, C,
+ Style, "8 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_16BYTES, C,
+ Style, "16 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_32BYTES, C,
+ Style, "32 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_64BYTES, C,
+ Style, "64 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_128BYTES, C,
+ Style, "128 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_256BYTES, C,
+ Style, "256 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_512BYTES, C,
+ Style, "512 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1024BYTES, C,
+ Style, "1024 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2048BYTES, C,
+ Style, "2048 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4096BYTES, C,
+ Style, "4096 byte align");
+ PUSH_MASKED_CHARACTERISTIC_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8192BYTES, C,
+ Style, "8192 byte align");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_LNK_NRELOC_OVFL, C, Style,
+ "noreloc overflow");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_MEM_DISCARDABLE, C, Style,
+ "discardable");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_MEM_NOT_CACHED, C, Style,
+ "not cached");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_MEM_NOT_PAGED, C, Style, "not paged");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_MEM_SHARED, C, Style, "shared");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_MEM_EXECUTE, C, Style,
+ "execute permissions");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_MEM_READ, C, Style,
+ "read permissions");
+ PUSH_CHARACTERISTIC_FLAG(SC, IMAGE_SCN_MEM_WRITE, C, Style,
+ "write permissions");
+ return typesetItemList(Opts, IndentLevel, FlagsPerLine, Separator);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/FormatUtil.h b/contrib/libs/llvm14/tools/llvm-pdbutil/FormatUtil.h
new file mode 100644
index 00000000000..b99ccec215b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/FormatUtil.h
@@ -0,0 +1,141 @@
+//===- FormatUtil.h ------------------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBUTIL_FORMAT_UTIL_H
+#define LLVM_TOOLS_LLVMPDBUTIL_FORMAT_UTIL_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+
+#include <string>
+#include <type_traits>
+
+namespace llvm {
+namespace pdb {
+
+std::string truncateStringBack(StringRef S, uint32_t MaxLen);
+std::string truncateStringMiddle(StringRef S, uint32_t MaxLen);
+std::string truncateStringFront(StringRef S, uint32_t MaxLen);
+std::string truncateQuotedNameFront(StringRef Label, StringRef Name,
+ uint32_t MaxLen);
+std::string truncateQuotedNameBack(StringRef Label, StringRef Name,
+ uint32_t MaxLen);
+
+#define PUSH_MASKED_FLAG(Enum, Mask, TheOpt, Value, Text) \
+ if (Enum::TheOpt == (Value & Mask)) \
+ Opts.push_back(Text);
+
+#define PUSH_FLAG(Enum, TheOpt, Value, Text) \
+ PUSH_MASKED_FLAG(Enum, Enum::TheOpt, TheOpt, Value, Text)
+
+#define RETURN_CASE(Enum, X, Ret) \
+ case Enum::X: \
+ return Ret;
+
+template <typename T> std::string formatUnknownEnum(T Value) {
+ return formatv("unknown ({0})", static_cast<std::underlying_type_t<T>>(Value))
+ .str();
+}
+
+std::string formatSegmentOffset(uint16_t Segment, uint32_t Offset);
+
+enum class CharacteristicStyle {
+ HeaderDefinition, // format as windows header definition
+ Descriptive, // format as human readable words
+};
+std::string formatSectionCharacteristics(
+ uint32_t IndentLevel, uint32_t C, uint32_t FlagsPerLine,
+ StringRef Separator,
+ CharacteristicStyle Style = CharacteristicStyle::HeaderDefinition);
+
+std::string typesetItemList(ArrayRef<std::string> Opts, uint32_t IndentLevel,
+ uint32_t GroupSize, StringRef Sep);
+
+std::string typesetStringList(uint32_t IndentLevel,
+ ArrayRef<StringRef> Strings);
+
+std::string formatChunkKind(codeview::DebugSubsectionKind Kind,
+ bool Friendly = true);
+std::string formatSymbolKind(codeview::SymbolKind K);
+std::string formatTypeLeafKind(codeview::TypeLeafKind K);
+
+/// Returns the number of digits in the given integer.
+inline int NumDigits(uint64_t N) {
+ if (N < 10ULL)
+ return 1;
+ if (N < 100ULL)
+ return 2;
+ if (N < 1000ULL)
+ return 3;
+ if (N < 10000ULL)
+ return 4;
+ if (N < 100000ULL)
+ return 5;
+ if (N < 1000000ULL)
+ return 6;
+ if (N < 10000000ULL)
+ return 7;
+ if (N < 100000000ULL)
+ return 8;
+ if (N < 1000000000ULL)
+ return 9;
+ if (N < 10000000000ULL)
+ return 10;
+ if (N < 100000000000ULL)
+ return 11;
+ if (N < 1000000000000ULL)
+ return 12;
+ if (N < 10000000000000ULL)
+ return 13;
+ if (N < 100000000000000ULL)
+ return 14;
+ if (N < 1000000000000000ULL)
+ return 15;
+ if (N < 10000000000000000ULL)
+ return 16;
+ if (N < 100000000000000000ULL)
+ return 17;
+ if (N < 1000000000000000000ULL)
+ return 18;
+ if (N < 10000000000000000000ULL)
+ return 19;
+ return 20;
+}
+
+namespace detail {
+template <typename T>
+struct EndianAdapter final
+ : public FormatAdapter<support::detail::packed_endian_specific_integral<
+ T, support::little, support::unaligned>> {
+ using EndianType =
+ support::detail::packed_endian_specific_integral<T, support::little,
+ support::unaligned>;
+
+ explicit EndianAdapter(EndianType &&Item)
+ : FormatAdapter<EndianType>(std::move(Item)) {}
+
+ void format(llvm::raw_ostream &Stream, StringRef Style) override {
+ format_provider<T>::format(static_cast<T>(this->Item), Stream, Style);
+ }
+};
+} // namespace detail
+
+template <typename T>
+detail::EndianAdapter<T>
+fmtle(support::detail::packed_endian_specific_integral<T, support::little,
+ support::unaligned>
+ Value) {
+ return detail::EndianAdapter<T>(std::move(Value));
+}
+}
+} // namespace llvm
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/InputFile.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/InputFile.cpp
new file mode 100644
index 00000000000..40b35625b6f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/InputFile.cpp
@@ -0,0 +1,510 @@
+//===- InputFile.cpp ------------------------------------------ *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFile.h"
+
+#include "FormatUtil.h"
+#include "LinePrinter.h"
+
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
+#include "llvm/DebugInfo/PDB/Native/RawError.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/PDB.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::object;
+using namespace llvm::pdb;
+
+InputFile::InputFile() {}
+InputFile::~InputFile() {}
+
+static Expected<ModuleDebugStreamRef>
+getModuleDebugStream(PDBFile &File, StringRef &ModuleName, uint32_t Index) {
+ ExitOnError Err("Unexpected error: ");
+
+ 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();
+
+ uint16_t ModiStream = Modi.getModuleStreamIndex();
+ if (ModiStream == kInvalidStreamIndex)
+ return make_error<RawError>(raw_error_code::no_stream,
+ "Module stream not present");
+
+ auto ModStreamData = File.createIndexedStream(ModiStream);
+
+ ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));
+ if (auto EC = ModS.reload())
+ return make_error<RawError>(raw_error_code::corrupt_file,
+ "Invalid module stream");
+
+ return std::move(ModS);
+}
+
+static inline bool isCodeViewDebugSubsection(object::SectionRef Section,
+ StringRef Name,
+ BinaryStreamReader &Reader) {
+ if (Expected<StringRef> NameOrErr = Section.getName()) {
+ if (*NameOrErr != Name)
+ return false;
+ } else {
+ consumeError(NameOrErr.takeError());
+ return false;
+ }
+
+ Expected<StringRef> ContentsOrErr = Section.getContents();
+ if (!ContentsOrErr) {
+ consumeError(ContentsOrErr.takeError());
+ return false;
+ }
+
+ Reader = BinaryStreamReader(*ContentsOrErr, support::little);
+ uint32_t Magic;
+ if (Reader.bytesRemaining() < sizeof(uint32_t))
+ return false;
+ cantFail(Reader.readInteger(Magic));
+ if (Magic != COFF::DEBUG_SECTION_MAGIC)
+ return false;
+ return true;
+}
+
+static inline bool isDebugSSection(object::SectionRef Section,
+ DebugSubsectionArray &Subsections) {
+ BinaryStreamReader Reader;
+ if (!isCodeViewDebugSubsection(Section, ".debug$S", Reader))
+ return false;
+
+ cantFail(Reader.readArray(Subsections, Reader.bytesRemaining()));
+ return true;
+}
+
+static bool isDebugTSection(SectionRef Section, CVTypeArray &Types) {
+ BinaryStreamReader Reader;
+ if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader) &&
+ !isCodeViewDebugSubsection(Section, ".debug$P", Reader))
+ return false;
+ cantFail(Reader.readArray(Types, Reader.bytesRemaining()));
+ return true;
+}
+
+static std::string formatChecksumKind(FileChecksumKind Kind) {
+ switch (Kind) {
+ RETURN_CASE(FileChecksumKind, None, "None");
+ RETURN_CASE(FileChecksumKind, MD5, "MD5");
+ RETURN_CASE(FileChecksumKind, SHA1, "SHA-1");
+ RETURN_CASE(FileChecksumKind, SHA256, "SHA-256");
+ }
+ return formatUnknownEnum(Kind);
+}
+
+template <typename... Args>
+static void formatInternal(LinePrinter &Printer, bool Append, Args &&... args) {
+ if (Append)
+ Printer.format(std::forward<Args>(args)...);
+ else
+ Printer.formatLine(std::forward<Args>(args)...);
+}
+
+SymbolGroup::SymbolGroup(InputFile *File, uint32_t GroupIndex) : File(File) {
+ if (!File)
+ return;
+
+ if (File->isPdb())
+ initializeForPdb(GroupIndex);
+ else {
+ Name = ".debug$S";
+ uint32_t I = 0;
+ for (const auto &S : File->obj().sections()) {
+ DebugSubsectionArray SS;
+ if (!isDebugSSection(S, SS))
+ continue;
+
+ if (!SC.hasChecksums() || !SC.hasStrings())
+ SC.initialize(SS);
+
+ if (I == GroupIndex)
+ Subsections = SS;
+
+ if (SC.hasChecksums() && SC.hasStrings())
+ break;
+ }
+ rebuildChecksumMap();
+ }
+}
+
+StringRef SymbolGroup::name() const { return Name; }
+
+void SymbolGroup::updateDebugS(const codeview::DebugSubsectionArray &SS) {
+ Subsections = SS;
+}
+
+void SymbolGroup::updatePdbModi(uint32_t Modi) { initializeForPdb(Modi); }
+
+void SymbolGroup::initializeForPdb(uint32_t Modi) {
+ assert(File && File->isPdb());
+
+ // 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()) {
+ auto StringTable = File->pdb().getStringTable();
+ if (StringTable)
+ SC.setStrings(StringTable->getStringTable());
+ else
+ consumeError(StringTable.takeError());
+ }
+
+ SC.resetChecksums();
+ auto MDS = getModuleDebugStream(File->pdb(), Name, Modi);
+ if (!MDS) {
+ consumeError(MDS.takeError());
+ return;
+ }
+
+ DebugStream = std::make_shared<ModuleDebugStreamRef>(std::move(*MDS));
+ Subsections = DebugStream->getSubsectionsArray();
+ SC.initialize(Subsections);
+ rebuildChecksumMap();
+}
+
+void SymbolGroup::rebuildChecksumMap() {
+ if (!SC.hasChecksums())
+ return;
+
+ for (const auto &Entry : SC.checksums()) {
+ auto S = SC.strings().getString(Entry.FileNameOffset);
+ if (!S)
+ continue;
+ ChecksumsByFile[*S] = Entry;
+ }
+}
+
+const ModuleDebugStreamRef &SymbolGroup::getPdbModuleStream() const {
+ assert(File && File->isPdb() && DebugStream);
+ return *DebugStream;
+}
+
+Expected<StringRef> SymbolGroup::getNameFromStringTable(uint32_t Offset) const {
+ return SC.strings().getString(Offset);
+}
+
+void SymbolGroup::formatFromFileName(LinePrinter &Printer, StringRef File,
+ bool Append) const {
+ auto FC = ChecksumsByFile.find(File);
+ if (FC == ChecksumsByFile.end()) {
+ formatInternal(Printer, Append, "- (no checksum) {0}", File);
+ return;
+ }
+
+ formatInternal(Printer, Append, "- ({0}: {1}) {2}",
+ formatChecksumKind(FC->getValue().Kind),
+ toHex(FC->getValue().Checksum), File);
+}
+
+void SymbolGroup::formatFromChecksumsOffset(LinePrinter &Printer,
+ uint32_t Offset,
+ bool Append) const {
+ if (!SC.hasChecksums()) {
+ formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
+ return;
+ }
+
+ auto Iter = SC.checksums().getArray().at(Offset);
+ if (Iter == SC.checksums().getArray().end()) {
+ formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
+ return;
+ }
+
+ uint32_t FO = Iter->FileNameOffset;
+ auto ExpectedFile = getNameFromStringTable(FO);
+ if (!ExpectedFile) {
+ formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
+ consumeError(ExpectedFile.takeError());
+ return;
+ }
+ if (Iter->Kind == FileChecksumKind::None) {
+ formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile);
+ } else {
+ formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile,
+ formatChecksumKind(Iter->Kind), toHex(Iter->Checksum));
+ }
+}
+
+Expected<InputFile> InputFile::open(StringRef Path, bool AllowUnknownFile) {
+ InputFile IF;
+ if (!llvm::sys::fs::exists(Path))
+ return make_error<StringError>(formatv("File {0} not found", Path),
+ inconvertibleErrorCode());
+
+ file_magic Magic;
+ if (auto EC = identify_magic(Path, Magic))
+ return make_error<StringError>(
+ formatv("Unable to identify file type for file {0}", Path), EC);
+
+ if (Magic == file_magic::coff_object) {
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Path);
+ if (!BinaryOrErr)
+ return BinaryOrErr.takeError();
+
+ IF.CoffObject = std::move(*BinaryOrErr);
+ IF.PdbOrObj = llvm::cast<COFFObjectFile>(IF.CoffObject.getBinary());
+ return std::move(IF);
+ }
+
+ if (Magic == file_magic::pdb) {
+ std::unique_ptr<IPDBSession> Session;
+ if (auto Err = loadDataForPDB(PDB_ReaderType::Native, Path, Session))
+ return std::move(Err);
+
+ IF.PdbSession.reset(static_cast<NativeSession *>(Session.release()));
+ IF.PdbOrObj = &IF.PdbSession->getPDBFile();
+
+ return std::move(IF);
+ }
+
+ if (!AllowUnknownFile)
+ return make_error<StringError>(
+ formatv("File {0} is not a supported file type", Path),
+ inconvertibleErrorCode());
+
+ auto Result = MemoryBuffer::getFile(Path, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
+ if (!Result)
+ return make_error<StringError>(
+ formatv("File {0} could not be opened", Path), Result.getError());
+
+ IF.UnknownFile = std::move(*Result);
+ IF.PdbOrObj = IF.UnknownFile.get();
+ return std::move(IF);
+}
+
+PDBFile &InputFile::pdb() {
+ assert(isPdb());
+ return *PdbOrObj.get<PDBFile *>();
+}
+
+const PDBFile &InputFile::pdb() const {
+ assert(isPdb());
+ return *PdbOrObj.get<PDBFile *>();
+}
+
+object::COFFObjectFile &InputFile::obj() {
+ assert(isObj());
+ return *PdbOrObj.get<object::COFFObjectFile *>();
+}
+
+const object::COFFObjectFile &InputFile::obj() const {
+ assert(isObj());
+ return *PdbOrObj.get<object::COFFObjectFile *>();
+}
+
+MemoryBuffer &InputFile::unknown() {
+ assert(isUnknown());
+ return *PdbOrObj.get<MemoryBuffer *>();
+}
+
+const MemoryBuffer &InputFile::unknown() const {
+ assert(isUnknown());
+ return *PdbOrObj.get<MemoryBuffer *>();
+}
+
+StringRef InputFile::getFilePath() const {
+ if (isPdb())
+ return pdb().getFilePath();
+ if (isObj())
+ return obj().getFileName();
+ assert(isUnknown());
+ return unknown().getBufferIdentifier();
+}
+
+bool InputFile::hasTypes() const {
+ if (isPdb())
+ return pdb().hasPDBTpiStream();
+
+ for (const auto &Section : obj().sections()) {
+ CVTypeArray Types;
+ if (isDebugTSection(Section, Types))
+ return true;
+ }
+ return false;
+}
+
+bool InputFile::hasIds() const {
+ if (isObj())
+ return false;
+ return pdb().hasPDBIpiStream();
+}
+
+bool InputFile::isPdb() const { return PdbOrObj.is<PDBFile *>(); }
+
+bool InputFile::isObj() const {
+ return PdbOrObj.is<object::COFFObjectFile *>();
+}
+
+bool InputFile::isUnknown() const { return PdbOrObj.is<MemoryBuffer *>(); }
+
+codeview::LazyRandomTypeCollection &
+InputFile::getOrCreateTypeCollection(TypeCollectionKind Kind) {
+ if (Types && Kind == kTypes)
+ return *Types;
+ if (Ids && Kind == kIds)
+ return *Ids;
+
+ if (Kind == kIds) {
+ assert(isPdb() && pdb().hasPDBIpiStream());
+ }
+
+ // If the collection was already initialized, we should have just returned it
+ // in step 1.
+ if (isPdb()) {
+ TypeCollectionPtr &Collection = (Kind == kIds) ? Ids : Types;
+ auto &Stream = cantFail((Kind == kIds) ? pdb().getPDBIpiStream()
+ : pdb().getPDBTpiStream());
+
+ auto &Array = Stream.typeArray();
+ uint32_t Count = Stream.getNumTypeRecords();
+ auto Offsets = Stream.getTypeIndexOffsets();
+ Collection =
+ std::make_unique<LazyRandomTypeCollection>(Array, Count, Offsets);
+ return *Collection;
+ }
+
+ assert(isObj());
+ assert(Kind == kTypes);
+ assert(!Types);
+
+ for (const auto &Section : obj().sections()) {
+ CVTypeArray Records;
+ if (!isDebugTSection(Section, Records))
+ continue;
+
+ Types = std::make_unique<LazyRandomTypeCollection>(Records, 100);
+ return *Types;
+ }
+
+ Types = std::make_unique<LazyRandomTypeCollection>(100);
+ return *Types;
+}
+
+codeview::LazyRandomTypeCollection &InputFile::types() {
+ return getOrCreateTypeCollection(kTypes);
+}
+
+codeview::LazyRandomTypeCollection &InputFile::ids() {
+ // Object files have only one type stream that contains both types and ids.
+ // Similarly, some PDBs don't contain an IPI stream, and for those both types
+ // and IDs are in the same stream.
+ if (isObj() || !pdb().hasPDBIpiStream())
+ return types();
+
+ return getOrCreateTypeCollection(kIds);
+}
+
+iterator_range<SymbolGroupIterator> InputFile::symbol_groups() {
+ return make_range<SymbolGroupIterator>(symbol_groups_begin(),
+ symbol_groups_end());
+}
+
+SymbolGroupIterator InputFile::symbol_groups_begin() {
+ return SymbolGroupIterator(*this);
+}
+
+SymbolGroupIterator InputFile::symbol_groups_end() {
+ return SymbolGroupIterator();
+}
+
+SymbolGroupIterator::SymbolGroupIterator() : Value(nullptr) {}
+
+SymbolGroupIterator::SymbolGroupIterator(InputFile &File) : Value(&File) {
+ if (File.isObj()) {
+ SectionIter = File.obj().section_begin();
+ scanToNextDebugS();
+ }
+}
+
+bool SymbolGroupIterator::operator==(const SymbolGroupIterator &R) const {
+ bool E = isEnd();
+ bool RE = R.isEnd();
+ if (E || RE)
+ return E == RE;
+
+ if (Value.File != R.Value.File)
+ return false;
+ return Index == R.Index;
+}
+
+const SymbolGroup &SymbolGroupIterator::operator*() const {
+ assert(!isEnd());
+ return Value;
+}
+SymbolGroup &SymbolGroupIterator::operator*() {
+ assert(!isEnd());
+ return Value;
+}
+
+SymbolGroupIterator &SymbolGroupIterator::operator++() {
+ assert(Value.File && !isEnd());
+ ++Index;
+ if (isEnd())
+ return *this;
+
+ if (Value.File->isPdb()) {
+ Value.updatePdbModi(Index);
+ return *this;
+ }
+
+ scanToNextDebugS();
+ return *this;
+}
+
+void SymbolGroupIterator::scanToNextDebugS() {
+ assert(SectionIter.hasValue());
+ auto End = Value.File->obj().section_end();
+ auto &Iter = *SectionIter;
+ assert(!isEnd());
+
+ while (++Iter != End) {
+ DebugSubsectionArray SS;
+ SectionRef SR = *Iter;
+ if (!isDebugSSection(SR, SS))
+ continue;
+
+ Value.updateDebugS(SS);
+ return;
+ }
+}
+
+bool SymbolGroupIterator::isEnd() const {
+ if (!Value.File)
+ return true;
+ if (Value.File->isPdb()) {
+ auto &Dbi = cantFail(Value.File->pdb().getPDBDbiStream());
+ uint32_t Count = Dbi.modules().getModuleCount();
+ assert(Index <= Count);
+ return Index == Count;
+ }
+
+ assert(SectionIter.hasValue());
+ return *SectionIter == Value.File->obj().section_end();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/InputFile.h b/contrib/libs/llvm14/tools/llvm-pdbutil/InputFile.h
new file mode 100644
index 00000000000..633ab34a54d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/InputFile.h
@@ -0,0 +1,154 @@
+//===- InputFile.h -------------------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_INPUTFILE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_INPUTFILE_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/iterator.h"
+#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"
+#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
+#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace codeview {
+class LazyRandomTypeCollection;
+}
+namespace object {
+class COFFObjectFile;
+} // namespace object
+
+namespace pdb {
+class InputFile;
+class LinePrinter;
+class PDBFile;
+class NativeSession;
+class SymbolGroupIterator;
+class SymbolGroup;
+
+class InputFile {
+ InputFile();
+
+ std::unique_ptr<NativeSession> PdbSession;
+ object::OwningBinary<object::Binary> CoffObject;
+ std::unique_ptr<MemoryBuffer> UnknownFile;
+ PointerUnion<PDBFile *, object::COFFObjectFile *, MemoryBuffer *> PdbOrObj;
+
+ using TypeCollectionPtr = std::unique_ptr<codeview::LazyRandomTypeCollection>;
+
+ TypeCollectionPtr Types;
+ TypeCollectionPtr Ids;
+
+ enum TypeCollectionKind { kTypes, kIds };
+ codeview::LazyRandomTypeCollection &
+ getOrCreateTypeCollection(TypeCollectionKind Kind);
+
+public:
+ ~InputFile();
+ InputFile(InputFile &&Other) = default;
+
+ static Expected<InputFile> open(StringRef Path,
+ bool AllowUnknownFile = false);
+
+ PDBFile &pdb();
+ const PDBFile &pdb() const;
+ object::COFFObjectFile &obj();
+ const object::COFFObjectFile &obj() const;
+ MemoryBuffer &unknown();
+ const MemoryBuffer &unknown() const;
+
+ StringRef getFilePath() const;
+
+ bool hasTypes() const;
+ bool hasIds() const;
+
+ codeview::LazyRandomTypeCollection &types();
+ codeview::LazyRandomTypeCollection &ids();
+
+ iterator_range<SymbolGroupIterator> symbol_groups();
+ SymbolGroupIterator symbol_groups_begin();
+ SymbolGroupIterator symbol_groups_end();
+
+ bool isPdb() const;
+ bool isObj() const;
+ bool isUnknown() const;
+};
+
+class SymbolGroup {
+ friend class SymbolGroupIterator;
+
+public:
+ explicit SymbolGroup(InputFile *File, uint32_t GroupIndex = 0);
+
+ Expected<StringRef> getNameFromStringTable(uint32_t Offset) const;
+
+ void formatFromFileName(LinePrinter &Printer, StringRef File,
+ bool Append = false) const;
+
+ void formatFromChecksumsOffset(LinePrinter &Printer, uint32_t Offset,
+ bool Append = false) const;
+
+ StringRef name() const;
+
+ codeview::DebugSubsectionArray getDebugSubsections() const {
+ return Subsections;
+ }
+ const ModuleDebugStreamRef &getPdbModuleStream() const;
+
+ 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);
+ void updateDebugS(const codeview::DebugSubsectionArray &SS);
+
+ void rebuildChecksumMap();
+ InputFile *File = nullptr;
+ StringRef Name;
+ codeview::DebugSubsectionArray Subsections;
+ std::shared_ptr<ModuleDebugStreamRef> DebugStream;
+ codeview::StringsAndChecksumsRef SC;
+ StringMap<codeview::FileChecksumEntry> ChecksumsByFile;
+};
+
+class SymbolGroupIterator
+ : public iterator_facade_base<SymbolGroupIterator,
+ std::forward_iterator_tag, SymbolGroup> {
+public:
+ SymbolGroupIterator();
+ explicit SymbolGroupIterator(InputFile &File);
+ SymbolGroupIterator(const SymbolGroupIterator &Other) = default;
+ SymbolGroupIterator &operator=(const SymbolGroupIterator &R) = default;
+
+ const SymbolGroup &operator*() const;
+ SymbolGroup &operator*();
+
+ bool operator==(const SymbolGroupIterator &R) const;
+ SymbolGroupIterator &operator++();
+
+private:
+ void scanToNextDebugS();
+ bool isEnd() const;
+
+ uint32_t Index = 0;
+ Optional<object::section_iterator> SectionIter;
+ SymbolGroup Value;
+};
+
+} // namespace pdb
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/LinePrinter.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/LinePrinter.cpp
new file mode 100644
index 00000000000..dd6ca5bf41b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/LinePrinter.cpp
@@ -0,0 +1,335 @@
+//===- LinePrinter.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LinePrinter.h"
+
+#include "llvm-pdbutil.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/DebugInfo/MSF/MSFCommon.h"
+#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/UDTLayout.h"
+#include "llvm/Support/BinaryStreamReader.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Regex.h"
+
+#include <algorithm>
+
+using namespace llvm;
+using namespace llvm::msf;
+using namespace llvm::pdb;
+
+namespace {
+bool IsItemExcluded(llvm::StringRef Item,
+ std::list<llvm::Regex> &IncludeFilters,
+ std::list<llvm::Regex> &ExcludeFilters) {
+ if (Item.empty())
+ return false;
+
+ auto match_pred = [Item](llvm::Regex &R) { return R.match(Item); };
+
+ // Include takes priority over exclude. If the user specified include
+ // filters, and none of them include this item, them item is gone.
+ if (!IncludeFilters.empty() && !any_of(IncludeFilters, match_pred))
+ return true;
+
+ if (any_of(ExcludeFilters, match_pred))
+ return true;
+
+ return false;
+}
+}
+
+using namespace llvm;
+
+LinePrinter::LinePrinter(int Indent, bool UseColor, llvm::raw_ostream &Stream)
+ : OS(Stream), IndentSpaces(Indent), CurrentIndent(0), UseColor(UseColor) {
+ SetFilters(ExcludeTypeFilters, opts::pretty::ExcludeTypes.begin(),
+ opts::pretty::ExcludeTypes.end());
+ SetFilters(ExcludeSymbolFilters, opts::pretty::ExcludeSymbols.begin(),
+ opts::pretty::ExcludeSymbols.end());
+ SetFilters(ExcludeCompilandFilters, opts::pretty::ExcludeCompilands.begin(),
+ opts::pretty::ExcludeCompilands.end());
+
+ SetFilters(IncludeTypeFilters, opts::pretty::IncludeTypes.begin(),
+ opts::pretty::IncludeTypes.end());
+ SetFilters(IncludeSymbolFilters, opts::pretty::IncludeSymbols.begin(),
+ opts::pretty::IncludeSymbols.end());
+ SetFilters(IncludeCompilandFilters, opts::pretty::IncludeCompilands.begin(),
+ opts::pretty::IncludeCompilands.end());
+}
+
+void LinePrinter::Indent(uint32_t Amount) {
+ if (Amount == 0)
+ Amount = IndentSpaces;
+ CurrentIndent += Amount;
+}
+
+void LinePrinter::Unindent(uint32_t Amount) {
+ if (Amount == 0)
+ Amount = IndentSpaces;
+ CurrentIndent = std::max<int>(0, CurrentIndent - Amount);
+}
+
+void LinePrinter::NewLine() {
+ OS << "\n";
+ OS.indent(CurrentIndent);
+}
+
+void LinePrinter::print(const Twine &T) { OS << T; }
+
+void LinePrinter::printLine(const Twine &T) {
+ NewLine();
+ OS << T;
+}
+
+bool LinePrinter::IsClassExcluded(const ClassLayout &Class) {
+ if (IsTypeExcluded(Class.getName(), Class.getSize()))
+ return true;
+ if (Class.deepPaddingSize() < opts::pretty::PaddingThreshold)
+ return true;
+ return false;
+}
+
+void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
+ uint64_t StartOffset) {
+ NewLine();
+ OS << Label << " (";
+ if (!Data.empty()) {
+ OS << "\n";
+ OS << format_bytes_with_ascii(Data, StartOffset, 32, 4,
+ CurrentIndent + IndentSpaces, true);
+ NewLine();
+ }
+ OS << ")";
+}
+
+void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
+ uint64_t Base, uint64_t StartOffset) {
+ NewLine();
+ OS << Label << " (";
+ if (!Data.empty()) {
+ OS << "\n";
+ Base += StartOffset;
+ OS << format_bytes_with_ascii(Data, Base, 32, 4,
+ CurrentIndent + IndentSpaces, true);
+ NewLine();
+ }
+ OS << ")";
+}
+
+namespace {
+struct Run {
+ Run() = default;
+ explicit Run(uint32_t Block) : Block(Block) {}
+ uint32_t Block = 0;
+ uint64_t ByteLen = 0;
+};
+} // namespace
+
+static std::vector<Run> computeBlockRuns(uint32_t BlockSize,
+ const msf::MSFStreamLayout &Layout) {
+ std::vector<Run> Runs;
+ if (Layout.Length == 0)
+ return Runs;
+
+ ArrayRef<support::ulittle32_t> Blocks = Layout.Blocks;
+ assert(!Blocks.empty());
+ uint64_t StreamBytesRemaining = Layout.Length;
+ uint32_t CurrentBlock = Blocks[0];
+ Runs.emplace_back(CurrentBlock);
+ while (!Blocks.empty()) {
+ Run *CurrentRun = &Runs.back();
+ uint32_t NextBlock = Blocks.front();
+ if (NextBlock < CurrentBlock || (NextBlock - CurrentBlock > 1)) {
+ Runs.emplace_back(NextBlock);
+ CurrentRun = &Runs.back();
+ }
+ uint64_t Used =
+ std::min(static_cast<uint64_t>(BlockSize), StreamBytesRemaining);
+ CurrentRun->ByteLen += Used;
+ StreamBytesRemaining -= Used;
+ CurrentBlock = NextBlock;
+ Blocks = Blocks.drop_front();
+ }
+ return Runs;
+}
+
+static std::pair<Run, uint64_t> findRun(uint64_t Offset, ArrayRef<Run> Runs) {
+ for (const auto &R : Runs) {
+ if (Offset < R.ByteLen)
+ return std::make_pair(R, Offset);
+ Offset -= R.ByteLen;
+ }
+ llvm_unreachable("Invalid offset!");
+}
+
+void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File,
+ uint32_t StreamIdx,
+ StringRef StreamPurpose, uint64_t Offset,
+ uint64_t Size) {
+ if (StreamIdx >= File.getNumStreams()) {
+ formatLine("Stream {0}: Not present", StreamIdx);
+ return;
+ }
+ if (Size + Offset > File.getStreamByteSize(StreamIdx)) {
+ formatLine(
+ "Stream {0}: Invalid offset and size, range out of stream bounds",
+ StreamIdx);
+ return;
+ }
+
+ auto S = File.createIndexedStream(StreamIdx);
+ if (!S) {
+ NewLine();
+ formatLine("Stream {0}: Not present", StreamIdx);
+ return;
+ }
+
+ uint64_t End =
+ (Size == 0) ? S->getLength() : std::min(Offset + Size, S->getLength());
+ Size = End - Offset;
+
+ formatLine("Stream {0}: {1} (dumping {2:N} / {3:N} bytes)", StreamIdx,
+ StreamPurpose, Size, S->getLength());
+ AutoIndent Indent(*this);
+ BinaryStreamRef Slice(*S);
+ BinarySubstreamRef Substream;
+ Substream.Offset = Offset;
+ Substream.StreamData = Slice.drop_front(Offset).keep_front(Size);
+
+ auto Layout = File.getStreamLayout(StreamIdx);
+ formatMsfStreamData(Label, File, Layout, Substream);
+}
+
+void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File,
+ const msf::MSFStreamLayout &Stream,
+ BinarySubstreamRef Substream) {
+ BinaryStreamReader Reader(Substream.StreamData);
+
+ auto Runs = computeBlockRuns(File.getBlockSize(), Stream);
+
+ NewLine();
+ OS << Label << " (";
+ while (Reader.bytesRemaining() > 0) {
+ OS << "\n";
+
+ Run FoundRun;
+ uint64_t RunOffset;
+ std::tie(FoundRun, RunOffset) = findRun(Substream.Offset, Runs);
+ assert(FoundRun.ByteLen >= RunOffset);
+ uint64_t Len = FoundRun.ByteLen - RunOffset;
+ Len = std::min(Len, Reader.bytesRemaining());
+ uint64_t Base = FoundRun.Block * File.getBlockSize() + RunOffset;
+ ArrayRef<uint8_t> Data;
+ consumeError(Reader.readBytes(Data, Len));
+ OS << format_bytes_with_ascii(Data, Base, 32, 4,
+ CurrentIndent + IndentSpaces, true);
+ if (Reader.bytesRemaining() > 0) {
+ NewLine();
+ OS << formatv(" {0}",
+ fmt_align("<discontinuity>", AlignStyle::Center, 114, '-'));
+ }
+ Substream.Offset += Len;
+ }
+ NewLine();
+ OS << ")";
+}
+
+void LinePrinter::formatMsfStreamBlocks(
+ PDBFile &File, const msf::MSFStreamLayout &StreamLayout) {
+ auto Blocks = makeArrayRef(StreamLayout.Blocks);
+ uint64_t L = StreamLayout.Length;
+
+ while (L > 0) {
+ NewLine();
+ assert(!Blocks.empty());
+ OS << formatv("Block {0} (\n", uint32_t(Blocks.front()));
+ uint64_t UsedBytes =
+ std::min(L, static_cast<uint64_t>(File.getBlockSize()));
+ ArrayRef<uint8_t> BlockData =
+ cantFail(File.getBlockData(Blocks.front(), File.getBlockSize()));
+ uint64_t BaseOffset = Blocks.front();
+ BaseOffset *= File.getBlockSize();
+ OS << format_bytes_with_ascii(BlockData, BaseOffset, 32, 4,
+ CurrentIndent + IndentSpaces, true);
+ NewLine();
+ OS << ")";
+ NewLine();
+ L -= UsedBytes;
+ Blocks = Blocks.drop_front();
+ }
+}
+
+bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint64_t Size) {
+ if (IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters))
+ return true;
+ if (Size < opts::pretty::SizeThreshold)
+ return true;
+ return false;
+}
+
+bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) {
+ return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters);
+}
+
+bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) {
+ return IsItemExcluded(CompilandName, IncludeCompilandFilters,
+ ExcludeCompilandFilters);
+}
+
+WithColor::WithColor(LinePrinter &P, PDB_ColorItem C)
+ : OS(P.OS), UseColor(P.hasColor()) {
+ if (UseColor)
+ applyColor(C);
+}
+
+WithColor::~WithColor() {
+ if (UseColor)
+ OS.resetColor();
+}
+
+void WithColor::applyColor(PDB_ColorItem C) {
+ switch (C) {
+ case PDB_ColorItem::None:
+ OS.resetColor();
+ return;
+ case PDB_ColorItem::Comment:
+ OS.changeColor(raw_ostream::GREEN, false);
+ return;
+ case PDB_ColorItem::Address:
+ OS.changeColor(raw_ostream::YELLOW, /*bold=*/true);
+ return;
+ case PDB_ColorItem::Keyword:
+ OS.changeColor(raw_ostream::MAGENTA, true);
+ return;
+ case PDB_ColorItem::Register:
+ case PDB_ColorItem::Offset:
+ OS.changeColor(raw_ostream::YELLOW, false);
+ return;
+ case PDB_ColorItem::Type:
+ OS.changeColor(raw_ostream::CYAN, true);
+ return;
+ case PDB_ColorItem::Identifier:
+ OS.changeColor(raw_ostream::CYAN, false);
+ return;
+ case PDB_ColorItem::Path:
+ OS.changeColor(raw_ostream::CYAN, false);
+ return;
+ case PDB_ColorItem::Padding:
+ case PDB_ColorItem::SectionHeader:
+ OS.changeColor(raw_ostream::RED, true);
+ return;
+ case PDB_ColorItem::LiteralValue:
+ OS.changeColor(raw_ostream::GREEN, true);
+ return;
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/LinePrinter.h b/contrib/libs/llvm14/tools/llvm-pdbutil/LinePrinter.h
new file mode 100644
index 00000000000..b6bb77280fd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/LinePrinter.h
@@ -0,0 +1,167 @@
+//===- LinePrinter.h ------------------------------------------ *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_LINEPRINTER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_LINEPRINTER_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/BinaryStreamRef.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <list>
+
+namespace llvm {
+namespace msf {
+class MSFStreamLayout;
+} // namespace msf
+namespace pdb {
+
+class ClassLayout;
+class PDBFile;
+
+class LinePrinter {
+ friend class WithColor;
+
+public:
+ LinePrinter(int Indent, bool UseColor, raw_ostream &Stream);
+
+ void Indent(uint32_t Amount = 0);
+ void Unindent(uint32_t Amount = 0);
+ void NewLine();
+
+ void printLine(const Twine &T);
+ void print(const Twine &T);
+ template <typename... Ts> void formatLine(const char *Fmt, Ts &&... Items) {
+ printLine(formatv(Fmt, std::forward<Ts>(Items)...));
+ }
+ template <typename... Ts> void format(const char *Fmt, Ts &&... Items) {
+ print(formatv(Fmt, std::forward<Ts>(Items)...));
+ }
+
+ void formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
+ uint64_t StartOffset);
+ void formatBinary(StringRef Label, ArrayRef<uint8_t> Data, uint64_t BaseAddr,
+ uint64_t StartOffset);
+
+ void formatMsfStreamData(StringRef Label, PDBFile &File, uint32_t StreamIdx,
+ StringRef StreamPurpose, uint64_t Offset,
+ uint64_t Size);
+ void formatMsfStreamData(StringRef Label, PDBFile &File,
+ const msf::MSFStreamLayout &Stream,
+ BinarySubstreamRef Substream);
+ void formatMsfStreamBlocks(PDBFile &File, const msf::MSFStreamLayout &Stream);
+
+ bool hasColor() const { return UseColor; }
+ raw_ostream &getStream() { return OS; }
+ int getIndentLevel() const { return CurrentIndent; }
+
+ bool IsClassExcluded(const ClassLayout &Class);
+ bool IsTypeExcluded(llvm::StringRef TypeName, uint64_t Size);
+ bool IsSymbolExcluded(llvm::StringRef SymbolName);
+ bool IsCompilandExcluded(llvm::StringRef CompilandName);
+
+private:
+ template <typename Iter>
+ void SetFilters(std::list<Regex> &List, Iter Begin, Iter End) {
+ List.clear();
+ for (; Begin != End; ++Begin)
+ List.emplace_back(StringRef(*Begin));
+ }
+
+ raw_ostream &OS;
+ int IndentSpaces;
+ int CurrentIndent;
+ bool UseColor;
+
+ std::list<Regex> ExcludeCompilandFilters;
+ std::list<Regex> ExcludeTypeFilters;
+ std::list<Regex> ExcludeSymbolFilters;
+
+ std::list<Regex> IncludeCompilandFilters;
+ std::list<Regex> IncludeTypeFilters;
+ std::list<Regex> IncludeSymbolFilters;
+};
+
+struct PrintScope {
+ explicit PrintScope(LinePrinter &P, uint32_t IndentLevel)
+ : P(P), IndentLevel(IndentLevel) {}
+ explicit PrintScope(const PrintScope &Other, uint32_t LabelWidth)
+ : P(Other.P), IndentLevel(Other.IndentLevel), LabelWidth(LabelWidth) {}
+
+ LinePrinter &P;
+ uint32_t IndentLevel;
+ uint32_t LabelWidth = 0;
+};
+
+inline Optional<PrintScope> withLabelWidth(const Optional<PrintScope> &Scope,
+ uint32_t W) {
+ if (!Scope)
+ return None;
+ return PrintScope{*Scope, W};
+}
+
+struct AutoIndent {
+ explicit AutoIndent(LinePrinter &L, uint32_t Amount = 0)
+ : L(&L), Amount(Amount) {
+ L.Indent(Amount);
+ }
+ explicit AutoIndent(const Optional<PrintScope> &Scope) {
+ if (Scope.hasValue()) {
+ L = &Scope->P;
+ Amount = Scope->IndentLevel;
+ }
+ }
+ ~AutoIndent() {
+ if (L)
+ L->Unindent(Amount);
+ }
+
+ LinePrinter *L = nullptr;
+ uint32_t Amount = 0;
+};
+
+template <class T>
+inline raw_ostream &operator<<(LinePrinter &Printer, const T &Item) {
+ return Printer.getStream() << Item;
+}
+
+enum class PDB_ColorItem {
+ None,
+ Address,
+ Type,
+ Comment,
+ Padding,
+ Keyword,
+ Offset,
+ Identifier,
+ Path,
+ SectionHeader,
+ LiteralValue,
+ Register,
+};
+
+class WithColor {
+public:
+ WithColor(LinePrinter &P, PDB_ColorItem C);
+ ~WithColor();
+
+ raw_ostream &get() { return OS; }
+
+private:
+ void applyColor(PDB_ColorItem C);
+ raw_ostream &OS;
+ bool UseColor;
+};
+}
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
new file mode 100644
index 00000000000..e6b5d21f36e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
@@ -0,0 +1,902 @@
+//===- MinimalSymbolDumper.cpp -------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MinimalSymbolDumper.h"
+
+#include "FormatUtil.h"
+#include "InputFile.h"
+#include "LinePrinter.h"
+
+#include "llvm/DebugInfo/CodeView/CVRecord.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/Formatters.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static std::string formatLocalSymFlags(uint32_t IndentLevel,
+ LocalSymFlags Flags) {
+ std::vector<std::string> Opts;
+ if (Flags == LocalSymFlags::None)
+ return "none";
+
+ PUSH_FLAG(LocalSymFlags, IsParameter, Flags, "param");
+ PUSH_FLAG(LocalSymFlags, IsAddressTaken, Flags, "address is taken");
+ PUSH_FLAG(LocalSymFlags, IsCompilerGenerated, Flags, "compiler generated");
+ PUSH_FLAG(LocalSymFlags, IsAggregate, Flags, "aggregate");
+ PUSH_FLAG(LocalSymFlags, IsAggregated, Flags, "aggregated");
+ PUSH_FLAG(LocalSymFlags, IsAliased, Flags, "aliased");
+ PUSH_FLAG(LocalSymFlags, IsAlias, Flags, "alias");
+ PUSH_FLAG(LocalSymFlags, IsReturnValue, Flags, "return val");
+ PUSH_FLAG(LocalSymFlags, IsOptimizedOut, Flags, "optimized away");
+ PUSH_FLAG(LocalSymFlags, IsEnregisteredGlobal, Flags, "enreg global");
+ PUSH_FLAG(LocalSymFlags, IsEnregisteredStatic, Flags, "enreg static");
+ return typesetItemList(Opts, 4, IndentLevel, " | ");
+}
+
+static std::string formatExportFlags(uint32_t IndentLevel, ExportFlags Flags) {
+ std::vector<std::string> Opts;
+ if (Flags == ExportFlags::None)
+ return "none";
+
+ PUSH_FLAG(ExportFlags, IsConstant, Flags, "constant");
+ PUSH_FLAG(ExportFlags, IsData, Flags, "data");
+ PUSH_FLAG(ExportFlags, IsPrivate, Flags, "private");
+ PUSH_FLAG(ExportFlags, HasNoName, Flags, "no name");
+ PUSH_FLAG(ExportFlags, HasExplicitOrdinal, Flags, "explicit ord");
+ PUSH_FLAG(ExportFlags, IsForwarder, Flags, "forwarder");
+
+ return typesetItemList(Opts, 4, IndentLevel, " | ");
+}
+
+static std::string formatCompileSym2Flags(uint32_t IndentLevel,
+ CompileSym2Flags Flags) {
+ std::vector<std::string> Opts;
+ Flags &= ~CompileSym2Flags::SourceLanguageMask;
+ if (Flags == CompileSym2Flags::None)
+ return "none";
+
+ PUSH_FLAG(CompileSym2Flags, EC, Flags, "edit and continue");
+ PUSH_FLAG(CompileSym2Flags, NoDbgInfo, Flags, "no dbg info");
+ PUSH_FLAG(CompileSym2Flags, LTCG, Flags, "ltcg");
+ PUSH_FLAG(CompileSym2Flags, NoDataAlign, Flags, "no data align");
+ PUSH_FLAG(CompileSym2Flags, ManagedPresent, Flags, "has managed code");
+ PUSH_FLAG(CompileSym2Flags, SecurityChecks, Flags, "security checks");
+ PUSH_FLAG(CompileSym2Flags, HotPatch, Flags, "hot patchable");
+ PUSH_FLAG(CompileSym2Flags, CVTCIL, Flags, "cvtcil");
+ PUSH_FLAG(CompileSym2Flags, MSILModule, Flags, "msil module");
+ return typesetItemList(Opts, 4, IndentLevel, " | ");
+}
+
+static std::string formatCompileSym3Flags(uint32_t IndentLevel,
+ CompileSym3Flags Flags) {
+ std::vector<std::string> Opts;
+ Flags &= ~CompileSym3Flags::SourceLanguageMask;
+
+ if (Flags == CompileSym3Flags::None)
+ return "none";
+
+ PUSH_FLAG(CompileSym3Flags, EC, Flags, "edit and continue");
+ PUSH_FLAG(CompileSym3Flags, NoDbgInfo, Flags, "no dbg info");
+ PUSH_FLAG(CompileSym3Flags, LTCG, Flags, "ltcg");
+ PUSH_FLAG(CompileSym3Flags, NoDataAlign, Flags, "no data align");
+ PUSH_FLAG(CompileSym3Flags, ManagedPresent, Flags, "has managed code");
+ PUSH_FLAG(CompileSym3Flags, SecurityChecks, Flags, "security checks");
+ PUSH_FLAG(CompileSym3Flags, HotPatch, Flags, "hot patchable");
+ PUSH_FLAG(CompileSym3Flags, CVTCIL, Flags, "cvtcil");
+ PUSH_FLAG(CompileSym3Flags, MSILModule, Flags, "msil module");
+ PUSH_FLAG(CompileSym3Flags, Sdl, Flags, "sdl");
+ PUSH_FLAG(CompileSym3Flags, PGO, Flags, "pgo");
+ PUSH_FLAG(CompileSym3Flags, Exp, Flags, "exp");
+ return typesetItemList(Opts, 4, IndentLevel, " | ");
+}
+
+static std::string formatFrameProcedureOptions(uint32_t IndentLevel,
+ FrameProcedureOptions FPO) {
+ std::vector<std::string> Opts;
+ if (FPO == FrameProcedureOptions::None)
+ return "none";
+
+ PUSH_FLAG(FrameProcedureOptions, HasAlloca, FPO, "has alloca");
+ PUSH_FLAG(FrameProcedureOptions, HasSetJmp, FPO, "has setjmp");
+ PUSH_FLAG(FrameProcedureOptions, HasLongJmp, FPO, "has longjmp");
+ PUSH_FLAG(FrameProcedureOptions, HasInlineAssembly, FPO, "has inline asm");
+ PUSH_FLAG(FrameProcedureOptions, HasExceptionHandling, FPO, "has eh");
+ PUSH_FLAG(FrameProcedureOptions, MarkedInline, FPO, "marked inline");
+ PUSH_FLAG(FrameProcedureOptions, HasStructuredExceptionHandling, FPO,
+ "has seh");
+ PUSH_FLAG(FrameProcedureOptions, Naked, FPO, "naked");
+ PUSH_FLAG(FrameProcedureOptions, SecurityChecks, FPO, "secure checks");
+ PUSH_FLAG(FrameProcedureOptions, AsynchronousExceptionHandling, FPO,
+ "has async eh");
+ PUSH_FLAG(FrameProcedureOptions, NoStackOrderingForSecurityChecks, FPO,
+ "no stack order");
+ PUSH_FLAG(FrameProcedureOptions, Inlined, FPO, "inlined");
+ PUSH_FLAG(FrameProcedureOptions, StrictSecurityChecks, FPO,
+ "strict secure checks");
+ PUSH_FLAG(FrameProcedureOptions, SafeBuffers, FPO, "safe buffers");
+ PUSH_FLAG(FrameProcedureOptions, ProfileGuidedOptimization, FPO, "pgo");
+ PUSH_FLAG(FrameProcedureOptions, ValidProfileCounts, FPO,
+ "has profile counts");
+ PUSH_FLAG(FrameProcedureOptions, OptimizedForSpeed, FPO, "opt speed");
+ PUSH_FLAG(FrameProcedureOptions, GuardCfg, FPO, "guard cfg");
+ PUSH_FLAG(FrameProcedureOptions, GuardCfw, FPO, "guard cfw");
+ return typesetItemList(Opts, 4, IndentLevel, " | ");
+}
+
+static std::string formatPublicSymFlags(uint32_t IndentLevel,
+ PublicSymFlags Flags) {
+ std::vector<std::string> Opts;
+ if (Flags == PublicSymFlags::None)
+ return "none";
+
+ PUSH_FLAG(PublicSymFlags, Code, Flags, "code");
+ PUSH_FLAG(PublicSymFlags, Function, Flags, "function");
+ PUSH_FLAG(PublicSymFlags, Managed, Flags, "managed");
+ PUSH_FLAG(PublicSymFlags, MSIL, Flags, "msil");
+ return typesetItemList(Opts, 4, IndentLevel, " | ");
+}
+
+static std::string formatProcSymFlags(uint32_t IndentLevel,
+ ProcSymFlags Flags) {
+ std::vector<std::string> Opts;
+ if (Flags == ProcSymFlags::None)
+ return "none";
+
+ PUSH_FLAG(ProcSymFlags, HasFP, Flags, "has fp");
+ PUSH_FLAG(ProcSymFlags, HasIRET, Flags, "has iret");
+ PUSH_FLAG(ProcSymFlags, HasFRET, Flags, "has fret");
+ PUSH_FLAG(ProcSymFlags, IsNoReturn, Flags, "noreturn");
+ PUSH_FLAG(ProcSymFlags, IsUnreachable, Flags, "unreachable");
+ PUSH_FLAG(ProcSymFlags, HasCustomCallingConv, Flags, "custom calling conv");
+ PUSH_FLAG(ProcSymFlags, IsNoInline, Flags, "noinline");
+ PUSH_FLAG(ProcSymFlags, HasOptimizedDebugInfo, Flags, "opt debuginfo");
+ return typesetItemList(Opts, 4, IndentLevel, " | ");
+}
+
+static std::string formatThunkOrdinal(ThunkOrdinal Ordinal) {
+ switch (Ordinal) {
+ RETURN_CASE(ThunkOrdinal, Standard, "thunk");
+ RETURN_CASE(ThunkOrdinal, ThisAdjustor, "this adjustor");
+ RETURN_CASE(ThunkOrdinal, Vcall, "vcall");
+ RETURN_CASE(ThunkOrdinal, Pcode, "pcode");
+ RETURN_CASE(ThunkOrdinal, UnknownLoad, "unknown load");
+ RETURN_CASE(ThunkOrdinal, TrampIncremental, "tramp incremental");
+ RETURN_CASE(ThunkOrdinal, BranchIsland, "branch island");
+ }
+ return formatUnknownEnum(Ordinal);
+}
+
+static std::string formatTrampolineType(TrampolineType Tramp) {
+ switch (Tramp) {
+ RETURN_CASE(TrampolineType, TrampIncremental, "tramp incremental");
+ RETURN_CASE(TrampolineType, BranchIsland, "branch island");
+ }
+ return formatUnknownEnum(Tramp);
+}
+
+static std::string formatSourceLanguage(SourceLanguage Lang) {
+ switch (Lang) {
+ RETURN_CASE(SourceLanguage, C, "c");
+ RETURN_CASE(SourceLanguage, Cpp, "c++");
+ RETURN_CASE(SourceLanguage, Fortran, "fortran");
+ RETURN_CASE(SourceLanguage, Masm, "masm");
+ RETURN_CASE(SourceLanguage, Pascal, "pascal");
+ RETURN_CASE(SourceLanguage, Basic, "basic");
+ RETURN_CASE(SourceLanguage, Cobol, "cobol");
+ RETURN_CASE(SourceLanguage, Link, "link");
+ RETURN_CASE(SourceLanguage, VB, "vb");
+ RETURN_CASE(SourceLanguage, Cvtres, "cvtres");
+ RETURN_CASE(SourceLanguage, Cvtpgd, "cvtpgd");
+ RETURN_CASE(SourceLanguage, CSharp, "c#");
+ RETURN_CASE(SourceLanguage, ILAsm, "il asm");
+ RETURN_CASE(SourceLanguage, Java, "java");
+ RETURN_CASE(SourceLanguage, JScript, "javascript");
+ RETURN_CASE(SourceLanguage, MSIL, "msil");
+ RETURN_CASE(SourceLanguage, HLSL, "hlsl");
+ RETURN_CASE(SourceLanguage, D, "d");
+ RETURN_CASE(SourceLanguage, Swift, "swift");
+ RETURN_CASE(SourceLanguage, Rust, "rust");
+ }
+ return formatUnknownEnum(Lang);
+}
+
+static std::string formatMachineType(CPUType Cpu) {
+ switch (Cpu) {
+ RETURN_CASE(CPUType, Intel8080, "intel 8080");
+ RETURN_CASE(CPUType, Intel8086, "intel 8086");
+ RETURN_CASE(CPUType, Intel80286, "intel 80286");
+ RETURN_CASE(CPUType, Intel80386, "intel 80386");
+ RETURN_CASE(CPUType, Intel80486, "intel 80486");
+ RETURN_CASE(CPUType, Pentium, "intel pentium");
+ RETURN_CASE(CPUType, PentiumPro, "intel pentium pro");
+ RETURN_CASE(CPUType, Pentium3, "intel pentium 3");
+ RETURN_CASE(CPUType, MIPS, "mips");
+ RETURN_CASE(CPUType, MIPS16, "mips-16");
+ RETURN_CASE(CPUType, MIPS32, "mips-32");
+ RETURN_CASE(CPUType, MIPS64, "mips-64");
+ RETURN_CASE(CPUType, MIPSI, "mips i");
+ RETURN_CASE(CPUType, MIPSII, "mips ii");
+ RETURN_CASE(CPUType, MIPSIII, "mips iii");
+ RETURN_CASE(CPUType, MIPSIV, "mips iv");
+ RETURN_CASE(CPUType, MIPSV, "mips v");
+ RETURN_CASE(CPUType, M68000, "motorola 68000");
+ RETURN_CASE(CPUType, M68010, "motorola 68010");
+ RETURN_CASE(CPUType, M68020, "motorola 68020");
+ RETURN_CASE(CPUType, M68030, "motorola 68030");
+ RETURN_CASE(CPUType, M68040, "motorola 68040");
+ RETURN_CASE(CPUType, Alpha, "alpha");
+ RETURN_CASE(CPUType, Alpha21164, "alpha 21164");
+ RETURN_CASE(CPUType, Alpha21164A, "alpha 21164a");
+ RETURN_CASE(CPUType, Alpha21264, "alpha 21264");
+ RETURN_CASE(CPUType, Alpha21364, "alpha 21364");
+ RETURN_CASE(CPUType, PPC601, "powerpc 601");
+ RETURN_CASE(CPUType, PPC603, "powerpc 603");
+ RETURN_CASE(CPUType, PPC604, "powerpc 604");
+ RETURN_CASE(CPUType, PPC620, "powerpc 620");
+ RETURN_CASE(CPUType, PPCFP, "powerpc fp");
+ RETURN_CASE(CPUType, PPCBE, "powerpc be");
+ RETURN_CASE(CPUType, SH3, "sh3");
+ RETURN_CASE(CPUType, SH3E, "sh3e");
+ RETURN_CASE(CPUType, SH3DSP, "sh3 dsp");
+ RETURN_CASE(CPUType, SH4, "sh4");
+ RETURN_CASE(CPUType, SHMedia, "shmedia");
+ RETURN_CASE(CPUType, ARM3, "arm 3");
+ RETURN_CASE(CPUType, ARM4, "arm 4");
+ RETURN_CASE(CPUType, ARM4T, "arm 4t");
+ RETURN_CASE(CPUType, ARM5, "arm 5");
+ RETURN_CASE(CPUType, ARM5T, "arm 5t");
+ RETURN_CASE(CPUType, ARM6, "arm 6");
+ RETURN_CASE(CPUType, ARM_XMAC, "arm xmac");
+ RETURN_CASE(CPUType, ARM_WMMX, "arm wmmx");
+ RETURN_CASE(CPUType, ARM7, "arm 7");
+ RETURN_CASE(CPUType, ARM64, "arm64");
+ RETURN_CASE(CPUType, ARM64EC, "arm64ec");
+ RETURN_CASE(CPUType, ARM64X, "arm64x");
+ RETURN_CASE(CPUType, HybridX86ARM64, "hybrid x86 arm64");
+ RETURN_CASE(CPUType, Omni, "omni");
+ RETURN_CASE(CPUType, Ia64, "intel itanium ia64");
+ RETURN_CASE(CPUType, Ia64_2, "intel itanium ia64 2");
+ RETURN_CASE(CPUType, CEE, "cee");
+ RETURN_CASE(CPUType, AM33, "am33");
+ RETURN_CASE(CPUType, M32R, "m32r");
+ RETURN_CASE(CPUType, TriCore, "tri-core");
+ RETURN_CASE(CPUType, X64, "intel x86-x64");
+ RETURN_CASE(CPUType, EBC, "ebc");
+ RETURN_CASE(CPUType, Thumb, "thumb");
+ RETURN_CASE(CPUType, ARMNT, "arm nt");
+ RETURN_CASE(CPUType, D3D11_Shader, "d3d11 shader");
+ }
+ return formatUnknownEnum(Cpu);
+}
+
+static std::string formatCookieKind(FrameCookieKind Kind) {
+ switch (Kind) {
+ RETURN_CASE(FrameCookieKind, Copy, "copy");
+ RETURN_CASE(FrameCookieKind, XorStackPointer, "xor stack ptr");
+ RETURN_CASE(FrameCookieKind, XorFramePointer, "xor frame ptr");
+ RETURN_CASE(FrameCookieKind, XorR13, "xor rot13");
+ }
+ return formatUnknownEnum(Kind);
+}
+
+static std::string formatRegisterId(RegisterId Id, CPUType Cpu) {
+ if (Cpu == CPUType::ARMNT) {
+ switch (Id) {
+#define CV_REGISTERS_ARM
+#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name)
+#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def"
+#undef CV_REGISTER
+#undef CV_REGISTERS_ARM
+
+ default:
+ break;
+ }
+ } else if (Cpu == CPUType::ARM64) {
+ switch (Id) {
+#define CV_REGISTERS_ARM64
+#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name)
+#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def"
+#undef CV_REGISTER
+#undef CV_REGISTERS_ARM64
+
+ default:
+ break;
+ }
+ } else {
+ switch (Id) {
+#define CV_REGISTERS_X86
+#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name)
+#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def"
+#undef CV_REGISTER
+#undef CV_REGISTERS_X86
+
+ default:
+ break;
+ }
+ }
+ return formatUnknownEnum(Id);
+}
+
+static std::string formatRegisterId(uint16_t Reg16, CPUType Cpu) {
+ return formatRegisterId(RegisterId(Reg16), Cpu);
+}
+
+static std::string formatRegisterId(ulittle16_t &Reg16, CPUType Cpu) {
+ return formatRegisterId(uint16_t(Reg16), Cpu);
+}
+
+static std::string formatRange(LocalVariableAddrRange Range) {
+ return formatv("[{0},+{1})",
+ formatSegmentOffset(Range.ISectStart, Range.OffsetStart),
+ Range.Range)
+ .str();
+}
+
+static std::string formatGaps(uint32_t IndentLevel,
+ ArrayRef<LocalVariableAddrGap> Gaps) {
+ std::vector<std::string> GapStrs;
+ for (const auto &G : Gaps) {
+ GapStrs.push_back(formatv("({0},{1})", G.GapStartOffset, G.Range).str());
+ }
+ return typesetItemList(GapStrs, 7, IndentLevel, ", ");
+}
+
+Error MinimalSymbolDumper::visitSymbolBegin(codeview::CVSymbol &Record) {
+ return visitSymbolBegin(Record, 0);
+}
+
+Error MinimalSymbolDumper::visitSymbolBegin(codeview::CVSymbol &Record,
+ uint32_t Offset) {
+ // 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.
+ P.formatLine("{0} | {1} [size = {2}]",
+ fmt_align(Offset, AlignStyle::Right, 6),
+ formatSymbolKind(Record.kind()), Record.length());
+ P.Indent();
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitSymbolEnd(CVSymbol &Record) {
+ if (RecordBytes) {
+ AutoIndent Indent(P, 7);
+ P.formatBinary("bytes", Record.content(), 0);
+ }
+ P.Unindent();
+ return Error::success();
+}
+
+std::string MinimalSymbolDumper::typeOrIdIndex(codeview::TypeIndex TI,
+ bool IsType) const {
+ if (TI.isSimple() || TI.isDecoratedItemId())
+ return formatv("{0}", TI).str();
+ auto &Container = IsType ? Types : Ids;
+ StringRef Name = Container.getTypeName(TI);
+ if (Name.size() > 32) {
+ Name = Name.take_front(32);
+ return std::string(formatv("{0} ({1}...)", TI, Name));
+ } else
+ return std::string(formatv("{0} ({1})", TI, Name));
+}
+
+std::string MinimalSymbolDumper::idIndex(codeview::TypeIndex TI) const {
+ return typeOrIdIndex(TI, false);
+}
+
+std::string MinimalSymbolDumper::typeIndex(TypeIndex TI) const {
+ return typeOrIdIndex(TI, true);
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, BlockSym &Block) {
+ P.format(" `{0}`", Block.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("parent = {0}, end = {1}", Block.Parent, Block.End);
+ P.formatLine("code size = {0}, addr = {1}", Block.CodeSize,
+ formatSegmentOffset(Block.Segment, Block.CodeOffset));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, Thunk32Sym &Thunk) {
+ P.format(" `{0}`", Thunk.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("parent = {0}, end = {1}, next = {2}", Thunk.Parent, Thunk.End,
+ Thunk.Next);
+ P.formatLine("kind = {0}, size = {1}, addr = {2}",
+ formatThunkOrdinal(Thunk.Thunk), Thunk.Length,
+ formatSegmentOffset(Thunk.Segment, Thunk.Offset));
+
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ TrampolineSym &Tramp) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("type = {0}, size = {1}, source = {2}, target = {3}",
+ formatTrampolineType(Tramp.Type), Tramp.Size,
+ formatSegmentOffset(Tramp.ThunkSection, Tramp.ThunkOffset),
+ formatSegmentOffset(Tramp.TargetSection, Tramp.ThunkOffset));
+
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ SectionSym &Section) {
+ P.format(" `{0}`", Section.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("length = {0}, alignment = {1}, rva = {2}, section # = {3}",
+ Section.Length, Section.Alignment, Section.Rva,
+ Section.SectionNumber);
+ P.printLine("characteristics =");
+ AutoIndent Indent2(P, 2);
+ P.printLine(formatSectionCharacteristics(P.getIndentLevel(),
+ Section.Characteristics, 1, "",
+ CharacteristicStyle::Descriptive));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, CoffGroupSym &CG) {
+ P.format(" `{0}`", CG.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("length = {0}, addr = {1}", CG.Size,
+ formatSegmentOffset(CG.Segment, CG.Offset));
+ P.printLine("characteristics =");
+ AutoIndent Indent2(P, 2);
+ P.printLine(formatSectionCharacteristics(P.getIndentLevel(),
+ CG.Characteristics, 1, "",
+ CharacteristicStyle::Descriptive));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ BPRelativeSym &BPRel) {
+ P.format(" `{0}`", BPRel.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("type = {0}, offset = {1}", typeIndex(BPRel.Type), BPRel.Offset);
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ BuildInfoSym &BuildInfo) {
+ P.format(" BuildId = `{0}`", BuildInfo.BuildId);
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ CallSiteInfoSym &CSI) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("type = {0}, addr = {1}", typeIndex(CSI.Type),
+ formatSegmentOffset(CSI.Segment, CSI.CodeOffset));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ EnvBlockSym &EnvBlock) {
+ AutoIndent Indent(P, 7);
+ for (const auto &Entry : EnvBlock.Fields) {
+ P.formatLine("- {0}", Entry);
+ }
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FileStaticSym &FS) {
+ P.format(" `{0}`", FS.Name);
+ AutoIndent Indent(P, 7);
+ if (SymGroup) {
+ Expected<StringRef> FileName =
+ SymGroup->getNameFromStringTable(FS.ModFilenameOffset);
+ if (FileName) {
+ P.formatLine("type = {0}, file name = {1} ({2}), flags = {3}",
+ typeIndex(FS.Index), FS.ModFilenameOffset, *FileName,
+ formatLocalSymFlags(P.getIndentLevel() + 9, FS.Flags));
+ }
+ return Error::success();
+ }
+
+ P.formatLine("type = {0}, file name offset = {1}, flags = {2}",
+ typeIndex(FS.Index), FS.ModFilenameOffset,
+ formatLocalSymFlags(P.getIndentLevel() + 9, FS.Flags));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, ExportSym &Export) {
+ P.format(" `{0}`", Export.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("ordinal = {0}, flags = {1}", Export.Ordinal,
+ formatExportFlags(P.getIndentLevel() + 9, Export.Flags));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ Compile2Sym &Compile2) {
+ 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));
+ P.formatLine("frontend = {0}.{1}.{2}, backend = {3}.{4}.{5}",
+ Compile2.VersionFrontendMajor, Compile2.VersionFrontendMinor,
+ Compile2.VersionFrontendBuild, Compile2.VersionBackendMajor,
+ Compile2.VersionBackendMinor, Compile2.VersionBackendBuild);
+ P.formatLine("flags = {0}",
+ formatCompileSym2Flags(P.getIndentLevel() + 9, Compile2.Flags));
+ P.formatLine(
+ "extra strings = {0}",
+ typesetStringList(P.getIndentLevel() + 9 + 2, Compile2.ExtraStrings));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ Compile3Sym &Compile3) {
+ 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));
+ P.formatLine("frontend = {0}.{1}.{2}.{3}, backend = {4}.{5}.{6}.{7}",
+ Compile3.VersionFrontendMajor, Compile3.VersionFrontendMinor,
+ Compile3.VersionFrontendBuild, Compile3.VersionFrontendQFE,
+ Compile3.VersionBackendMajor, Compile3.VersionBackendMinor,
+ Compile3.VersionBackendBuild, Compile3.VersionBackendQFE);
+ P.formatLine("flags = {0}",
+ formatCompileSym3Flags(P.getIndentLevel() + 9, Compile3.Flags));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ ConstantSym &Constant) {
+ P.format(" `{0}`", Constant.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("type = {0}, value = {1}", typeIndex(Constant.Type),
+ toString(Constant.Value, 10));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, DataSym &Data) {
+ P.format(" `{0}`", Data.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("type = {0}, addr = {1}", typeIndex(Data.Type),
+ formatSegmentOffset(Data.Segment, Data.DataOffset));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(
+ CVSymbol &CVR, DefRangeFramePointerRelFullScopeSym &Def) {
+ P.format(" offset = {0}", Def.Offset);
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ DefRangeFramePointerRelSym &Def) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("offset = {0}, range = {1}", Def.Hdr.Offset,
+ formatRange(Def.Range));
+ P.formatLine("gaps = [{0}]", formatGaps(P.getIndentLevel() + 9, Def.Gaps));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ DefRangeRegisterRelSym &Def) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("register = {0}, offset = {1}, offset in parent = {2}, has "
+ "spilled udt = {3}",
+ formatRegisterId(Def.Hdr.Register, CompilationCPU),
+ 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();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(
+ CVSymbol &CVR, DefRangeRegisterSym &DefRangeRegister) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("register = {0}, may have no name = {1}, range start = "
+ "{2}, length = {3}",
+ formatRegisterId(DefRangeRegister.Hdr.Register, CompilationCPU),
+ bool(DefRangeRegister.Hdr.MayHaveNoName),
+ formatSegmentOffset(DefRangeRegister.Range.ISectStart,
+ DefRangeRegister.Range.OffsetStart),
+ DefRangeRegister.Range.Range);
+ P.formatLine("gaps = [{0}]",
+ formatGaps(P.getIndentLevel() + 9, DefRangeRegister.Gaps));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ DefRangeSubfieldRegisterSym &Def) {
+ AutoIndent Indent(P, 7);
+ bool NoName = !!(Def.Hdr.MayHaveNoName == 0);
+ P.formatLine("register = {0}, may have no name = {1}, offset in parent = {2}",
+ formatRegisterId(Def.Hdr.Register, CompilationCPU), NoName,
+ uint32_t(Def.Hdr.OffsetInParent));
+ P.formatLine("range = {0}, gaps = [{1}]", formatRange(Def.Range),
+ formatGaps(P.getIndentLevel() + 9, Def.Gaps));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ DefRangeSubfieldSym &Def) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("program = {0}, offset in parent = {1}, range = {2}",
+ Def.Program, Def.OffsetInParent, formatRange(Def.Range));
+ P.formatLine("gaps = [{0}]", formatGaps(P.getIndentLevel() + 9, Def.Gaps));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, DefRangeSym &Def) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("program = {0}, range = {1}", Def.Program,
+ formatRange(Def.Range));
+ P.formatLine("gaps = [{0}]", formatGaps(P.getIndentLevel() + 9, Def.Gaps));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameCookieSym &FC) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("code offset = {0}, Register = {1}, kind = {2}, flags = {3}",
+ FC.CodeOffset, formatRegisterId(FC.Register, CompilationCPU),
+ formatCookieKind(FC.CookieKind), FC.Flags);
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameProcSym &FP) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("size = {0}, padding size = {1}, offset to padding = {2}",
+ FP.TotalFrameBytes, FP.PaddingFrameBytes, FP.OffsetToPadding);
+ P.formatLine("bytes of callee saved registers = {0}, exception handler addr "
+ "= {1}",
+ FP.BytesOfCalleeSavedRegisters,
+ formatSegmentOffset(FP.SectionIdOfExceptionHandler,
+ FP.OffsetOfExceptionHandler));
+ P.formatLine(
+ "local fp reg = {0}, param fp reg = {1}",
+ formatRegisterId(FP.getLocalFramePtrReg(CompilationCPU), CompilationCPU),
+ formatRegisterId(FP.getParamFramePtrReg(CompilationCPU), CompilationCPU));
+ P.formatLine("flags = {0}",
+ formatFrameProcedureOptions(P.getIndentLevel() + 9, FP.Flags));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ HeapAllocationSiteSym &HAS) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("type = {0}, addr = {1} call size = {2}", typeIndex(HAS.Type),
+ formatSegmentOffset(HAS.Segment, HAS.CodeOffset),
+ HAS.CallInstructionSize);
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, InlineSiteSym &IS) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("inlinee = {0}, parent = {1}, end = {2}", idIndex(IS.Inlinee),
+ IS.Parent, IS.End);
+
+ // Break down the annotation byte code and calculate code and line offsets.
+ // FIXME: It would be helpful if we could look up the initial file and inlinee
+ // lines offset using the inlinee index above.
+ uint32_t CodeOffset = 0;
+ int32_t LineOffset = 0;
+ for (auto &Annot : IS.annotations()) {
+ P.formatLine(" {0}", fmt_align(toHex(Annot.Bytes), AlignStyle::Left, 9));
+
+ auto formatCodeOffset = [&](uint32_t Delta) {
+ CodeOffset += Delta;
+ P.format(" code 0x{0} (+0x{1})", utohexstr(CodeOffset), utohexstr(Delta));
+ };
+ auto formatCodeLength = [&](uint32_t Length) {
+ // Notably, changing the code length does not affect the code offset.
+ P.format(" code end 0x{0} (+0x{1})", utohexstr(CodeOffset + Length),
+ utohexstr(Length));
+ };
+ auto formatLineOffset = [&](int32_t Delta) {
+ LineOffset += Delta;
+ char Sign = Delta > 0 ? '+' : '-';
+ P.format(" line {0} ({1}{2})", LineOffset, Sign, std::abs(Delta));
+ };
+
+ // Use the opcode to interpret the integer values.
+ switch (Annot.OpCode) {
+ case BinaryAnnotationsOpCode::Invalid:
+ break;
+ case BinaryAnnotationsOpCode::CodeOffset:
+ case BinaryAnnotationsOpCode::ChangeCodeOffset:
+ formatCodeOffset(Annot.U1);
+ break;
+ case BinaryAnnotationsOpCode::ChangeLineOffset:
+ formatLineOffset(Annot.S1);
+ break;
+ case BinaryAnnotationsOpCode::ChangeCodeLength:
+ formatCodeLength(Annot.U1);
+ // Apparently this annotation updates the code offset. It's hard to make
+ // MSVC produce this opcode, but clang uses it, and debuggers seem to use
+ // this interpretation.
+ CodeOffset += Annot.U1;
+ break;
+ case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset:
+ formatCodeOffset(Annot.U1);
+ formatLineOffset(Annot.S1);
+ break;
+ case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset:
+ formatCodeOffset(Annot.U2);
+ formatCodeLength(Annot.U1);
+ break;
+
+ case BinaryAnnotationsOpCode::ChangeFile: {
+ uint32_t FileOffset = Annot.U1;
+ StringRef Filename = "<unknown>";
+ if (SymGroup) {
+ if (Expected<StringRef> MaybeFile =
+ SymGroup->getNameFromStringTable(FileOffset))
+ Filename = *MaybeFile;
+ else
+ return MaybeFile.takeError();
+ }
+ P.format(" setfile {0} 0x{1}", utohexstr(FileOffset));
+ break;
+ }
+
+ // The rest of these are hard to convince MSVC to emit, so they are not as
+ // well understood.
+ case BinaryAnnotationsOpCode::ChangeCodeOffsetBase:
+ formatCodeOffset(Annot.U1);
+ break;
+ case BinaryAnnotationsOpCode::ChangeLineEndDelta:
+ case BinaryAnnotationsOpCode::ChangeRangeKind:
+ case BinaryAnnotationsOpCode::ChangeColumnStart:
+ case BinaryAnnotationsOpCode::ChangeColumnEnd:
+ P.format(" {0} {1}", Annot.Name, Annot.U1);
+ break;
+ case BinaryAnnotationsOpCode::ChangeColumnEndDelta:
+ P.format(" {0} {1}", Annot.Name, Annot.S1);
+ break;
+ }
+ }
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ RegisterSym &Register) {
+ P.format(" `{0}`", Register.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("register = {0}, type = {1}",
+ formatRegisterId(Register.Register, CompilationCPU),
+ typeIndex(Register.Index));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ PublicSym32 &Public) {
+ P.format(" `{0}`", Public.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("flags = {0}, addr = {1}",
+ formatPublicSymFlags(P.getIndentLevel() + 9, Public.Flags),
+ formatSegmentOffset(Public.Segment, Public.Offset));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, ProcRefSym &PR) {
+ P.format(" `{0}`", PR.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("module = {0}, sum name = {1}, offset = {2}", PR.Module,
+ PR.SumName, PR.SymOffset);
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, LabelSym &Label) {
+ P.format(" `{0}` (addr = {1})", Label.Name,
+ formatSegmentOffset(Label.Segment, Label.CodeOffset));
+ AutoIndent Indent(P, 7);
+ P.formatLine("flags = {0}",
+ formatProcSymFlags(P.getIndentLevel() + 9, Label.Flags));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, LocalSym &Local) {
+ P.format(" `{0}`", Local.Name);
+ AutoIndent Indent(P, 7);
+
+ std::string FlagStr =
+ formatLocalSymFlags(P.getIndentLevel() + 9, Local.Flags);
+ P.formatLine("type={0}, flags = {1}", typeIndex(Local.Type), FlagStr);
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ ObjNameSym &ObjName) {
+ P.format(" sig={0}, `{1}`", ObjName.Signature, ObjName.Name);
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, ProcSym &Proc) {
+ P.format(" `{0}`", Proc.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("parent = {0}, end = {1}, addr = {2}, code size = {3}",
+ Proc.Parent, Proc.End,
+ formatSegmentOffset(Proc.Segment, Proc.CodeOffset),
+ Proc.CodeSize);
+ bool IsType = true;
+ switch (Proc.getKind()) {
+ case SymbolRecordKind::GlobalProcIdSym:
+ case SymbolRecordKind::ProcIdSym:
+ case SymbolRecordKind::DPCProcIdSym:
+ IsType = false;
+ break;
+ default:
+ break;
+ }
+ P.formatLine("type = `{0}`, debug start = {1}, debug end = {2}, flags = {3}",
+ typeOrIdIndex(Proc.FunctionType, IsType), Proc.DbgStart,
+ Proc.DbgEnd,
+ formatProcSymFlags(P.getIndentLevel() + 9, Proc.Flags));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ ScopeEndSym &ScopeEnd) {
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, CallerSym &Caller) {
+ AutoIndent Indent(P, 7);
+ for (const auto &I : Caller.Indices) {
+ P.formatLine("callee: {0}", idIndex(I));
+ }
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ RegRelativeSym &RegRel) {
+ P.format(" `{0}`", RegRel.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine(
+ "type = {0}, register = {1}, offset = {2}", typeIndex(RegRel.Type),
+ formatRegisterId(RegRel.Register, CompilationCPU), RegRel.Offset);
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ ThreadLocalDataSym &Data) {
+ P.format(" `{0}`", Data.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("type = {0}, addr = {1}", typeIndex(Data.Type),
+ formatSegmentOffset(Data.Segment, Data.DataOffset));
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, UDTSym &UDT) {
+ P.format(" `{0}`", UDT.Name);
+ AutoIndent Indent(P, 7);
+ P.formatLine("original type = {0}", UDT.Type);
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ UsingNamespaceSym &UN) {
+ P.format(" `{0}`", UN.Name);
+ return Error::success();
+}
+
+Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
+ AnnotationSym &Annot) {
+ AutoIndent Indent(P, 7);
+ P.formatLine("addr = {0}", formatSegmentOffset(Annot.Segment, Annot.CodeOffset));
+ P.formatLine("strings = {0}", typesetStringList(P.getIndentLevel() + 9 + 2,
+ Annot.Strings));
+ return Error::success();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalSymbolDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalSymbolDumper.h
new file mode 100644
index 00000000000..cdc75c1cfba
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalSymbolDumper.h
@@ -0,0 +1,68 @@
+//===- MinimalSymbolDumper.h ---------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBUTIL_MINIMAL_SYMBOL_DUMPER_H
+#define LLVM_TOOLS_LLVMPDBUTIL_MINIMAL_SYMBOL_DUMPER_H
+
+#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h"
+
+namespace llvm {
+namespace codeview {
+class LazyRandomTypeCollection;
+}
+
+namespace pdb {
+class LinePrinter;
+class SymbolGroup;
+
+class MinimalSymbolDumper : public codeview::SymbolVisitorCallbacks {
+public:
+ MinimalSymbolDumper(LinePrinter &P, bool RecordBytes,
+ codeview::LazyRandomTypeCollection &Ids,
+ codeview::LazyRandomTypeCollection &Types)
+ : P(P), RecordBytes(RecordBytes), Ids(Ids), Types(Types) {}
+ MinimalSymbolDumper(LinePrinter &P, bool RecordBytes,
+ const SymbolGroup &SymGroup,
+ codeview::LazyRandomTypeCollection &Ids,
+ codeview::LazyRandomTypeCollection &Types)
+ : P(P), RecordBytes(RecordBytes), SymGroup(&SymGroup), Ids(Ids),
+ Types(Types) {}
+
+ Error visitSymbolBegin(codeview::CVSymbol &Record) override;
+ Error visitSymbolBegin(codeview::CVSymbol &Record, uint32_t Offset) override;
+ Error visitSymbolEnd(codeview::CVSymbol &Record) override;
+
+ void setSymbolGroup(const SymbolGroup *Group) { SymGroup = Group; }
+
+#define SYMBOL_RECORD(EnumName, EnumVal, Name) \
+ virtual Error visitKnownRecord(codeview::CVSymbol &CVR, \
+ codeview::Name &Record) override;
+#define SYMBOL_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
+#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def"
+
+private:
+ std::string typeOrIdIndex(codeview::TypeIndex TI, bool IsType) const;
+
+ std::string typeIndex(codeview::TypeIndex TI) const;
+ 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;
+ codeview::LazyRandomTypeCollection &Types;
+};
+} // namespace pdb
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalTypeDumper.cpp
new file mode 100644
index 00000000000..08006e9c62d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalTypeDumper.cpp
@@ -0,0 +1,592 @@
+//===- MinimalTypeDumper.cpp ---------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MinimalTypeDumper.h"
+
+#include "FormatUtil.h"
+#include "LinePrinter.h"
+#include "TypeReferenceTracker.h"
+
+#include "llvm-pdbutil.h"
+#include "llvm/DebugInfo/CodeView/CVRecord.h"
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/Formatters.h"
+#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"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static std::string formatClassOptions(uint32_t IndentLevel,
+ 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, HasUniqueName, Options, "has unique name");
+ PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin");
+ PUSH_FLAG(ClassOptions, Nested, Options, "is nested");
+ PUSH_FLAG(ClassOptions, HasOverloadedOperator, Options,
+ "overloaded operator");
+ PUSH_FLAG(ClassOptions, HasOverloadedAssignmentOperator, Options,
+ "overloaded operator=");
+ PUSH_FLAG(ClassOptions, Packed, Options, "packed");
+ PUSH_FLAG(ClassOptions, Scoped, Options, "scoped");
+ PUSH_FLAG(ClassOptions, Sealed, Options, "sealed");
+
+ return typesetItemList(Opts, 4, IndentLevel, " | ");
+}
+
+static std::string pointerOptions(PointerOptions Options) {
+ std::vector<std::string> Opts;
+ PUSH_FLAG(PointerOptions, Flat32, Options, "flat32");
+ PUSH_FLAG(PointerOptions, Volatile, Options, "volatile");
+ PUSH_FLAG(PointerOptions, Const, Options, "const");
+ PUSH_FLAG(PointerOptions, Unaligned, Options, "unaligned");
+ PUSH_FLAG(PointerOptions, Restrict, Options, "restrict");
+ PUSH_FLAG(PointerOptions, WinRTSmartPointer, Options, "winrt");
+ if (Opts.empty())
+ return "None";
+ return join(Opts, " | ");
+}
+
+static std::string modifierOptions(ModifierOptions Options) {
+ std::vector<std::string> Opts;
+ PUSH_FLAG(ModifierOptions, Const, Options, "const");
+ PUSH_FLAG(ModifierOptions, Volatile, Options, "volatile");
+ PUSH_FLAG(ModifierOptions, Unaligned, Options, "unaligned");
+ if (Opts.empty())
+ return "None";
+ return join(Opts, " | ");
+}
+
+static std::string formatCallingConvention(CallingConvention Convention) {
+ switch (Convention) {
+ RETURN_CASE(CallingConvention, AlphaCall, "alphacall");
+ RETURN_CASE(CallingConvention, AM33Call, "am33call");
+ RETURN_CASE(CallingConvention, ArmCall, "armcall");
+ RETURN_CASE(CallingConvention, ClrCall, "clrcall");
+ RETURN_CASE(CallingConvention, FarC, "far cdecl");
+ RETURN_CASE(CallingConvention, FarFast, "far fastcall");
+ RETURN_CASE(CallingConvention, FarPascal, "far pascal");
+ RETURN_CASE(CallingConvention, FarStdCall, "far stdcall");
+ RETURN_CASE(CallingConvention, FarSysCall, "far syscall");
+ RETURN_CASE(CallingConvention, Generic, "generic");
+ RETURN_CASE(CallingConvention, Inline, "inline");
+ RETURN_CASE(CallingConvention, M32RCall, "m32rcall");
+ RETURN_CASE(CallingConvention, MipsCall, "mipscall");
+ RETURN_CASE(CallingConvention, NearC, "cdecl");
+ RETURN_CASE(CallingConvention, NearFast, "fastcall");
+ RETURN_CASE(CallingConvention, NearPascal, "pascal");
+ RETURN_CASE(CallingConvention, NearStdCall, "stdcall");
+ RETURN_CASE(CallingConvention, NearSysCall, "near syscall");
+ RETURN_CASE(CallingConvention, NearVector, "vectorcall");
+ RETURN_CASE(CallingConvention, PpcCall, "ppccall");
+ RETURN_CASE(CallingConvention, SHCall, "shcall");
+ RETURN_CASE(CallingConvention, SH5Call, "sh5call");
+ RETURN_CASE(CallingConvention, ThisCall, "thiscall");
+ RETURN_CASE(CallingConvention, TriCall, "tricall");
+ }
+ return formatUnknownEnum(Convention);
+}
+
+static std::string formatPointerMode(PointerMode Mode) {
+ switch (Mode) {
+ RETURN_CASE(PointerMode, LValueReference, "ref");
+ RETURN_CASE(PointerMode, Pointer, "pointer");
+ RETURN_CASE(PointerMode, PointerToDataMember, "data member pointer");
+ RETURN_CASE(PointerMode, PointerToMemberFunction, "member fn pointer");
+ RETURN_CASE(PointerMode, RValueReference, "rvalue ref");
+ }
+ return formatUnknownEnum(Mode);
+}
+
+static std::string memberAccess(MemberAccess Access) {
+ switch (Access) {
+ RETURN_CASE(MemberAccess, None, "");
+ RETURN_CASE(MemberAccess, Private, "private");
+ RETURN_CASE(MemberAccess, Protected, "protected");
+ RETURN_CASE(MemberAccess, Public, "public");
+ }
+ return formatUnknownEnum(Access);
+}
+
+static std::string methodKind(MethodKind Kind) {
+ switch (Kind) {
+ RETURN_CASE(MethodKind, Vanilla, "");
+ RETURN_CASE(MethodKind, Virtual, "virtual");
+ RETURN_CASE(MethodKind, Static, "static");
+ RETURN_CASE(MethodKind, Friend, "friend");
+ RETURN_CASE(MethodKind, IntroducingVirtual, "intro virtual");
+ RETURN_CASE(MethodKind, PureVirtual, "pure virtual");
+ RETURN_CASE(MethodKind, PureIntroducingVirtual, "pure intro virtual");
+ }
+ return formatUnknownEnum(Kind);
+}
+
+static std::string pointerKind(PointerKind Kind) {
+ switch (Kind) {
+ RETURN_CASE(PointerKind, Near16, "ptr16");
+ RETURN_CASE(PointerKind, Far16, "far ptr16");
+ RETURN_CASE(PointerKind, Huge16, "huge ptr16");
+ RETURN_CASE(PointerKind, BasedOnSegment, "segment based");
+ RETURN_CASE(PointerKind, BasedOnValue, "value based");
+ RETURN_CASE(PointerKind, BasedOnSegmentValue, "segment value based");
+ RETURN_CASE(PointerKind, BasedOnAddress, "address based");
+ RETURN_CASE(PointerKind, BasedOnSegmentAddress, "segment address based");
+ RETURN_CASE(PointerKind, BasedOnType, "type based");
+ RETURN_CASE(PointerKind, BasedOnSelf, "self based");
+ RETURN_CASE(PointerKind, Near32, "ptr32");
+ RETURN_CASE(PointerKind, Far32, "far ptr32");
+ RETURN_CASE(PointerKind, Near64, "ptr64");
+ }
+ return formatUnknownEnum(Kind);
+}
+
+static std::string memberAttributes(const MemberAttributes &Attrs) {
+ std::vector<std::string> Opts;
+ std::string Access = memberAccess(Attrs.getAccess());
+ std::string Kind = methodKind(Attrs.getMethodKind());
+ if (!Access.empty())
+ Opts.push_back(Access);
+ if (!Kind.empty())
+ Opts.push_back(Kind);
+ MethodOptions Flags = Attrs.getFlags();
+ PUSH_FLAG(MethodOptions, Pseudo, Flags, "pseudo");
+ PUSH_FLAG(MethodOptions, NoInherit, Flags, "noinherit");
+ PUSH_FLAG(MethodOptions, NoConstruct, Flags, "noconstruct");
+ PUSH_FLAG(MethodOptions, CompilerGenerated, Flags, "compiler-generated");
+ PUSH_FLAG(MethodOptions, Sealed, Flags, "sealed");
+ return join(Opts, " ");
+}
+
+static std::string formatPointerAttrs(const PointerRecord &Record) {
+ PointerMode Mode = Record.getMode();
+ PointerOptions Opts = Record.getOptions();
+ PointerKind Kind = Record.getPointerKind();
+ return std::string(formatv("mode = {0}, opts = {1}, kind = {2}",
+ formatPointerMode(Mode), pointerOptions(Opts),
+ pointerKind(Kind)));
+}
+
+static std::string formatFunctionOptions(FunctionOptions Options) {
+ std::vector<std::string> Opts;
+
+ PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt");
+ PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options,
+ "constructor with virtual bases");
+ PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor");
+ if (Opts.empty())
+ return "None";
+ return join(Opts, " | ");
+}
+
+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.
+ P.formatLine("{0} | {1} [size = {2}",
+ fmt_align(Index, AlignStyle::Right, Width),
+ formatTypeLeafKind(Record.kind()), Record.length());
+ if (Hashes) {
+ std::string H;
+ if (Index.toArrayIndex() >= HashValues.size()) {
+ H = "(not present)";
+ } else {
+ uint32_t Hash = HashValues[Index.toArrayIndex()];
+ Expected<uint32_t> MaybeHash = hashTypeRecord(Record);
+ if (!MaybeHash)
+ return MaybeHash.takeError();
+ uint32_t OurHash = *MaybeHash;
+ OurHash %= NumHashBuckets;
+ if (Hash == OurHash)
+ H = "0x" + utohexstr(Hash);
+ else
+ H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash);
+ }
+ P.format(", hash = {0}", H);
+ }
+ if (RefTracker) {
+ if (RefTracker->isTypeReferenced(Index))
+ P.format(", referenced");
+ else
+ P.format(", unreferenced");
+ }
+ P.format("]");
+ P.Indent(Width + 3);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) {
+ P.Unindent(Width + 3);
+ if (RecordBytes) {
+ AutoIndent Indent(P, 9);
+ P.formatBinary("Bytes", Record.RecordData, 0);
+ }
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
+ P.formatLine("- {0}", formatTypeLeafKind(Record.Kind));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) {
+ if (RecordBytes) {
+ AutoIndent Indent(P, 2);
+ P.formatBinary("Bytes", Record.Data, 0);
+ }
+ return Error::success();
+}
+
+StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {
+ if (TI.isNoneType())
+ return "";
+ return Types.getTypeName(TI);
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ FieldListRecord &FieldList) {
+ if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this))
+ return EC;
+
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ StringIdRecord &String) {
+ P.format(" ID: {0}, String: {1}", String.getId(), String.getString());
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ ArgListRecord &Args) {
+ auto Indices = Args.getIndices();
+ if (Indices.empty())
+ return Error::success();
+
+ auto Max = std::max_element(Indices.begin(), Indices.end());
+ uint32_t W = NumDigits(Max->getIndex()) + 2;
+
+ for (auto I : Indices)
+ P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
+ getTypeName(I));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ StringListRecord &Strings) {
+ auto Indices = Strings.getIndices();
+ if (Indices.empty())
+ return Error::success();
+
+ auto Max = std::max_element(Indices.begin(), Indices.end());
+ uint32_t W = NumDigits(Max->getIndex()) + 2;
+
+ for (auto I : Indices)
+ P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
+ getTypeName(I));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ ClassRecord &Class) {
+ P.format(" `{0}`", Class.Name);
+ if (Class.hasUniqueName())
+ P.formatLine("unique name: `{0}`", Class.UniqueName);
+ 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, Stream,
+ CurrentTypeIndex),
+ Class.Size);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ UnionRecord &Union) {
+ P.format(" `{0}`", Union.Name);
+ if (Union.hasUniqueName())
+ 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, Stream,
+ CurrentTypeIndex),
+ Union.Size);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
+ P.format(" `{0}`", Enum.Name);
+ if (Enum.hasUniqueName())
+ P.formatLine("unique name: `{0}`", Enum.UniqueName);
+ P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList,
+ Enum.UnderlyingType);
+ P.formatLine("options: {0}",
+ formatClassOptions(P.getIndentLevel(), Enum.Options, Stream,
+ CurrentTypeIndex));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
+ if (AT.Name.empty()) {
+ P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size,
+ AT.IndexType, AT.ElementType);
+ } else {
+ P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}",
+ AT.Name, AT.Size, AT.IndexType, AT.ElementType);
+ }
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ VFTableRecord &VFT) {
+ P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}",
+ VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable);
+ P.formatLine("method names: ");
+ if (!VFT.MethodNames.empty()) {
+ std::string Sep =
+ formatv("\n{0}",
+ fmt_repeat(' ', P.getIndentLevel() + strlen("method names: ")))
+ .str();
+ P.print(join(VFT.MethodNames, Sep));
+ }
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ MemberFuncIdRecord &Id) {
+ P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name,
+ Id.FunctionType, Id.ClassType);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ ProcedureRecord &Proc) {
+ P.formatLine("return type = {0}, # args = {1}, param list = {2}",
+ Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList);
+ P.formatLine("calling conv = {0}, options = {1}",
+ formatCallingConvention(Proc.CallConv),
+ formatFunctionOptions(Proc.Options));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ MemberFunctionRecord &MF) {
+ P.formatLine("return type = {0}, # args = {1}, param list = {2}",
+ MF.ReturnType, MF.ParameterCount, MF.ArgumentList);
+ P.formatLine("class type = {0}, this type = {1}, this adjust = {2}",
+ MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment);
+ P.formatLine("calling conv = {0}, options = {1}",
+ formatCallingConvention(MF.CallConv),
+ formatFunctionOptions(MF.Options));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ FuncIdRecord &Func) {
+ P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name,
+ Func.FunctionType, Func.ParentScope);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ TypeServer2Record &TS) {
+ P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, TS.Guid);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ PointerRecord &Ptr) {
+ P.formatLine("referent = {0}, {1}", Ptr.ReferentType,
+ formatPointerAttrs(Ptr));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ ModifierRecord &Mod) {
+ P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType,
+ modifierOptions(Mod.Modifiers));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ VFTableShapeRecord &Shape) {
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ UdtModSourceLineRecord &U) {
+ P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module,
+ U.SourceFile.getIndex(), U.LineNumber);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ UdtSourceLineRecord &U) {
+ P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT,
+ U.SourceFile.getIndex(), U.LineNumber);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ BitFieldRecord &BF) {
+ P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type,
+ BF.BitOffset, BF.BitSize);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(
+ CVType &CVR, MethodOverloadListRecord &Overloads) {
+ for (auto &M : Overloads.Methods)
+ P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]",
+ M.Type, M.VFTableOffset, memberAttributes(M.Attrs));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ BuildInfoRecord &BI) {
+ auto Indices = BI.ArgIndices;
+ if (Indices.empty())
+ return Error::success();
+
+ auto Max = std::max_element(Indices.begin(), Indices.end());
+ uint32_t W = NumDigits(Max->getIndex()) + 2;
+
+ for (auto I : Indices)
+ P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
+ getTypeName(I));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) {
+ std::string Type = (R.Mode == LabelType::Far) ? "far" : "near";
+ P.format(" type = {0}", Type);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ PrecompRecord &Precomp) {
+ P.format(" start index = {0:X+}, types count = {1:X+}, signature = {2:X+},"
+ " precomp path = {3}",
+ Precomp.StartTypeIndex, Precomp.TypesCount, Precomp.Signature,
+ Precomp.PrecompFilePath);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ EndPrecompRecord &EP) {
+ P.format(" signature = {0:X+}", EP.Signature);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
+ NestedTypeRecord &Nested) {
+ P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
+ OneMethodRecord &Method) {
+ P.format(" [name = `{0}`]", Method.Name);
+ AutoIndent Indent(P);
+ P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type,
+ Method.VFTableOffset, memberAttributes(Method.Attrs));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
+ OverloadedMethodRecord &Method) {
+ P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]",
+ Method.Name, Method.NumOverloads, Method.MethodList);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
+ DataMemberRecord &Field) {
+ P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name,
+ Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
+ StaticDataMemberRecord &Field) {
+ P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type,
+ memberAttributes(Field.Attrs));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
+ EnumeratorRecord &Enum) {
+ P.format(" [{0} = {1}]", Enum.Name,
+ toString(Enum.Value, 10, Enum.Value.isSigned()));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
+ BaseClassRecord &Base) {
+ AutoIndent Indent(P);
+ P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset,
+ memberAttributes(Base.Attrs));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
+ VirtualBaseClassRecord &Base) {
+ AutoIndent Indent(P);
+ P.formatLine(
+ "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}",
+ Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex);
+ P.formatLine("attrs = {0}", memberAttributes(Base.Attrs));
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
+ ListContinuationRecord &Cont) {
+ P.format(" continuation = {0}", Cont.ContinuationIndex);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
+ VFPtrRecord &VFP) {
+ P.format(" type = {0}", VFP.Type);
+ return Error::success();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalTypeDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalTypeDumper.h
new file mode 100644
index 00000000000..6bc456d47ac
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/MinimalTypeDumper.h
@@ -0,0 +1,70 @@
+//===- MinimalTypeDumper.h ------------------------------------ *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBUTIL_MINIMAL_TYPE_DUMPER_H
+#define LLVM_TOOLS_LLVMPDBUTIL_MINIMAL_TYPE_DUMPER_H
+
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
+#include "llvm/Support/BinaryStreamArray.h"
+
+namespace llvm {
+namespace codeview {
+class LazyRandomTypeCollection;
+}
+
+namespace pdb {
+class LinePrinter;
+class TpiStream;
+class TypeReferenceTracker;
+
+class MinimalTypeDumpVisitor : public codeview::TypeVisitorCallbacks {
+public:
+ MinimalTypeDumpVisitor(LinePrinter &P, uint32_t Width, bool RecordBytes,
+ bool Hashes, codeview::LazyRandomTypeCollection &Types,
+ TypeReferenceTracker *RefTracker,
+ uint32_t NumHashBuckets,
+ FixedStreamArray<support::ulittle32_t> HashValues,
+ pdb::TpiStream *Stream)
+ : P(P), Width(Width), RecordBytes(RecordBytes), Hashes(Hashes),
+ Types(Types), RefTracker(RefTracker), NumHashBuckets(NumHashBuckets),
+ HashValues(HashValues), Stream(Stream) {}
+
+ Error visitTypeBegin(codeview::CVType &Record,
+ codeview::TypeIndex Index) override;
+ Error visitTypeEnd(codeview::CVType &Record) override;
+ Error visitMemberBegin(codeview::CVMemberRecord &Record) override;
+ Error visitMemberEnd(codeview::CVMemberRecord &Record) override;
+
+#define TYPE_RECORD(EnumName, EnumVal, Name) \
+ Error visitKnownRecord(codeview::CVType &CVR, \
+ codeview::Name##Record &Record) override;
+#define MEMBER_RECORD(EnumName, EnumVal, Name) \
+ Error visitKnownMember(codeview::CVMemberRecord &CVR, \
+ codeview::Name##Record &Record) override;
+#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
+#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
+#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
+
+private:
+ StringRef getTypeName(codeview::TypeIndex TI) const;
+
+ LinePrinter &P;
+ uint32_t Width;
+ bool RecordBytes = false;
+ bool Hashes = false;
+ codeview::LazyRandomTypeCollection &Types;
+ pdb::TypeReferenceTracker *RefTracker = nullptr;
+ uint32_t NumHashBuckets;
+ codeview::TypeIndex CurrentTypeIndex;
+ FixedStreamArray<support::ulittle32_t> HashValues;
+ pdb::TpiStream *Stream = nullptr;
+};
+} // namespace pdb
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/OutputStyle.h b/contrib/libs/llvm14/tools/llvm-pdbutil/OutputStyle.h
new file mode 100644
index 00000000000..da93c32053f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/OutputStyle.h
@@ -0,0 +1,26 @@
+//===- OutputStyle.h ------------------------------------------ *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_OUTPUTSTYLE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_OUTPUTSTYLE_H
+
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace pdb {
+
+class OutputStyle {
+public:
+ virtual ~OutputStyle() {}
+
+ virtual Error dump() = 0;
+};
+}
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PdbYaml.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/PdbYaml.cpp
new file mode 100644
index 00000000000..a26241967b5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PdbYaml.cpp
@@ -0,0 +1,189 @@
+//===-- PdbYaml.cpp ------------------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PdbYaml.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
+#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+#include "llvm/ObjectYAML/CodeViewYAMLDebugSections.h"
+#include "llvm/ObjectYAML/CodeViewYAMLTypes.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+using namespace llvm::pdb::yaml;
+using namespace llvm::yaml;
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::NamedStreamMapping)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbDbiModuleInfo)
+LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::StreamBlockList)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::pdb::PdbRaw_FeatureSig)
+
+namespace llvm {
+namespace yaml {
+
+template <> struct ScalarEnumerationTraits<llvm::pdb::PDB_Machine> {
+ static void enumeration(IO &io, llvm::pdb::PDB_Machine &Value) {
+ io.enumCase(Value, "Invalid", PDB_Machine::Invalid);
+ io.enumCase(Value, "Am33", PDB_Machine::Am33);
+ io.enumCase(Value, "Amd64", PDB_Machine::Amd64);
+ io.enumCase(Value, "Arm", PDB_Machine::Arm);
+ io.enumCase(Value, "ArmNT", PDB_Machine::ArmNT);
+ io.enumCase(Value, "Ebc", PDB_Machine::Ebc);
+ io.enumCase(Value, "x86", PDB_Machine::x86);
+ io.enumCase(Value, "Ia64", PDB_Machine::Ia64);
+ io.enumCase(Value, "M32R", PDB_Machine::M32R);
+ io.enumCase(Value, "Mips16", PDB_Machine::Mips16);
+ io.enumCase(Value, "MipsFpu", PDB_Machine::MipsFpu);
+ io.enumCase(Value, "MipsFpu16", PDB_Machine::MipsFpu16);
+ io.enumCase(Value, "PowerPCFP", PDB_Machine::PowerPCFP);
+ io.enumCase(Value, "R4000", PDB_Machine::R4000);
+ io.enumCase(Value, "SH3", PDB_Machine::SH3);
+ io.enumCase(Value, "SH3DSP", PDB_Machine::SH3DSP);
+ io.enumCase(Value, "Thumb", PDB_Machine::Thumb);
+ io.enumCase(Value, "WceMipsV2", PDB_Machine::WceMipsV2);
+ io.enumCase(Value, "Arm64", PDB_Machine::Arm64);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_DbiVer> {
+ static void enumeration(IO &io, llvm::pdb::PdbRaw_DbiVer &Value) {
+ io.enumCase(Value, "V41", llvm::pdb::PdbRaw_DbiVer::PdbDbiVC41);
+ io.enumCase(Value, "V50", llvm::pdb::PdbRaw_DbiVer::PdbDbiV50);
+ io.enumCase(Value, "V60", llvm::pdb::PdbRaw_DbiVer::PdbDbiV60);
+ io.enumCase(Value, "V70", llvm::pdb::PdbRaw_DbiVer::PdbDbiV70);
+ io.enumCase(Value, "V110", llvm::pdb::PdbRaw_DbiVer::PdbDbiV110);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_ImplVer> {
+ static void enumeration(IO &io, llvm::pdb::PdbRaw_ImplVer &Value) {
+ io.enumCase(Value, "VC2", llvm::pdb::PdbRaw_ImplVer::PdbImplVC2);
+ io.enumCase(Value, "VC4", llvm::pdb::PdbRaw_ImplVer::PdbImplVC4);
+ io.enumCase(Value, "VC41", llvm::pdb::PdbRaw_ImplVer::PdbImplVC41);
+ io.enumCase(Value, "VC50", llvm::pdb::PdbRaw_ImplVer::PdbImplVC50);
+ io.enumCase(Value, "VC98", llvm::pdb::PdbRaw_ImplVer::PdbImplVC98);
+ io.enumCase(Value, "VC70Dep", llvm::pdb::PdbRaw_ImplVer::PdbImplVC70Dep);
+ io.enumCase(Value, "VC70", llvm::pdb::PdbRaw_ImplVer::PdbImplVC70);
+ io.enumCase(Value, "VC80", llvm::pdb::PdbRaw_ImplVer::PdbImplVC80);
+ io.enumCase(Value, "VC110", llvm::pdb::PdbRaw_ImplVer::PdbImplVC110);
+ io.enumCase(Value, "VC140", llvm::pdb::PdbRaw_ImplVer::PdbImplVC140);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_TpiVer> {
+ static void enumeration(IO &io, llvm::pdb::PdbRaw_TpiVer &Value) {
+ io.enumCase(Value, "VC40", llvm::pdb::PdbRaw_TpiVer::PdbTpiV40);
+ io.enumCase(Value, "VC41", llvm::pdb::PdbRaw_TpiVer::PdbTpiV41);
+ io.enumCase(Value, "VC50", llvm::pdb::PdbRaw_TpiVer::PdbTpiV50);
+ io.enumCase(Value, "VC70", llvm::pdb::PdbRaw_TpiVer::PdbTpiV70);
+ io.enumCase(Value, "VC80", llvm::pdb::PdbRaw_TpiVer::PdbTpiV80);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_FeatureSig> {
+ static void enumeration(IO &io, PdbRaw_FeatureSig &Features) {
+ io.enumCase(Features, "MinimalDebugInfo",
+ PdbRaw_FeatureSig::MinimalDebugInfo);
+ io.enumCase(Features, "NoTypeMerge", PdbRaw_FeatureSig::NoTypeMerge);
+ io.enumCase(Features, "VC110", PdbRaw_FeatureSig::VC110);
+ io.enumCase(Features, "VC140", PdbRaw_FeatureSig::VC140);
+ }
+};
+}
+}
+
+void MappingTraits<PdbObject>::mapping(IO &IO, PdbObject &Obj) {
+ IO.mapOptional("MSF", Obj.Headers);
+ IO.mapOptional("StreamSizes", Obj.StreamSizes);
+ IO.mapOptional("StreamMap", Obj.StreamMap);
+ IO.mapOptional("StringTable", Obj.StringTable);
+ IO.mapOptional("PdbStream", Obj.PdbStream);
+ 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) {
+ IO.mapOptional("SuperBlock", Obj.SuperBlock);
+ IO.mapOptional("NumDirectoryBlocks", Obj.NumDirectoryBlocks);
+ IO.mapOptional("DirectoryBlocks", Obj.DirectoryBlocks);
+ IO.mapOptional("NumStreams", Obj.NumStreams);
+ IO.mapOptional("FileSize", Obj.FileSize);
+}
+
+void MappingTraits<msf::SuperBlock>::mapping(IO &IO, msf::SuperBlock &SB) {
+ if (!IO.outputting()) {
+ ::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic));
+ }
+
+ using u32 = support::ulittle32_t;
+ IO.mapOptional("BlockSize", SB.BlockSize, u32(4096U));
+ IO.mapOptional("FreeBlockMap", SB.FreeBlockMapBlock, u32(0U));
+ IO.mapOptional("NumBlocks", SB.NumBlocks, u32(0U));
+ IO.mapOptional("NumDirectoryBytes", SB.NumDirectoryBytes, u32(0U));
+ IO.mapOptional("Unknown1", SB.Unknown1, u32(0U));
+ IO.mapOptional("BlockMapAddr", SB.BlockMapAddr, u32(0U));
+}
+
+void MappingTraits<StreamBlockList>::mapping(IO &IO, StreamBlockList &SB) {
+ IO.mapRequired("Stream", SB.Blocks);
+}
+
+void MappingTraits<PdbInfoStream>::mapping(IO &IO, PdbInfoStream &Obj) {
+ IO.mapOptional("Age", Obj.Age, 1U);
+ IO.mapOptional("Guid", Obj.Guid);
+ IO.mapOptional("Signature", Obj.Signature, 0U);
+ IO.mapOptional("Features", Obj.Features);
+ IO.mapOptional("Version", Obj.Version, PdbImplVC70);
+}
+
+void MappingTraits<PdbDbiStream>::mapping(IO &IO, PdbDbiStream &Obj) {
+ IO.mapOptional("VerHeader", Obj.VerHeader, PdbDbiV70);
+ IO.mapOptional("Age", Obj.Age, 1U);
+ IO.mapOptional("BuildNumber", Obj.BuildNumber, uint16_t(0U));
+ IO.mapOptional("PdbDllVersion", Obj.PdbDllVersion, 0U);
+ IO.mapOptional("PdbDllRbld", Obj.PdbDllRbld, uint16_t(0U));
+ IO.mapOptional("Flags", Obj.Flags, uint16_t(1U));
+ IO.mapOptional("MachineType", Obj.MachineType, PDB_Machine::x86);
+ IO.mapOptional("Modules", Obj.ModInfos);
+}
+
+void MappingTraits<PdbTpiStream>::mapping(IO &IO,
+ pdb::yaml::PdbTpiStream &Obj) {
+ IO.mapOptional("Version", Obj.Version, PdbTpiV80);
+ 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);
+ IO.mapRequired("StreamNum", Obj.StreamNumber);
+}
+
+void MappingTraits<PdbModiStream>::mapping(IO &IO, PdbModiStream &Obj) {
+ IO.mapOptional("Signature", Obj.Signature, 4U);
+ IO.mapRequired("Records", Obj.Symbols);
+}
+
+void MappingTraits<PdbDbiModuleInfo>::mapping(IO &IO, PdbDbiModuleInfo &Obj) {
+ IO.mapRequired("Module", Obj.Mod);
+ IO.mapOptional("ObjFile", Obj.Obj, Obj.Mod);
+ IO.mapOptional("SourceFiles", Obj.SourceFiles);
+ IO.mapOptional("Subsections", Obj.Subsections);
+ IO.mapOptional("Modi", Obj.Modi);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PdbYaml.h b/contrib/libs/llvm14/tools/llvm-pdbutil/PdbYaml.h
new file mode 100644
index 00000000000..c335eef2f1c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PdbYaml.h
@@ -0,0 +1,126 @@
+//===- PdbYAML.h ---------------------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H
+
+#include "OutputStyle.h"
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/MSF/MSFCommon.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
+#include "llvm/ObjectYAML/CodeViewYAMLDebugSections.h"
+#include "llvm/ObjectYAML/CodeViewYAMLSymbols.h"
+#include "llvm/ObjectYAML/CodeViewYAMLTypes.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/YAMLTraits.h"
+
+#include <vector>
+
+namespace llvm {
+namespace pdb {
+
+namespace yaml {
+
+struct MSFHeaders {
+ msf::SuperBlock SuperBlock;
+ uint32_t NumDirectoryBlocks = 0;
+ std::vector<uint32_t> DirectoryBlocks;
+ uint32_t NumStreams = 0;
+ uint64_t FileSize = 0;
+};
+
+struct StreamBlockList {
+ std::vector<uint32_t> Blocks;
+};
+
+struct NamedStreamMapping {
+ StringRef StreamName;
+ uint32_t StreamNumber;
+};
+
+struct PdbInfoStream {
+ PdbRaw_ImplVer Version = PdbImplVC70;
+ uint32_t Signature = 0;
+ uint32_t Age = 1;
+ codeview::GUID Guid;
+ std::vector<PdbRaw_FeatureSig> Features;
+ std::vector<NamedStreamMapping> NamedStreams;
+};
+
+struct PdbModiStream {
+ uint32_t Signature;
+ std::vector<CodeViewYAML::SymbolRecord> Symbols;
+};
+
+struct PdbDbiModuleInfo {
+ StringRef Obj;
+ StringRef Mod;
+ std::vector<StringRef> SourceFiles;
+ std::vector<CodeViewYAML::YAMLDebugSubsection> Subsections;
+ Optional<PdbModiStream> Modi;
+};
+
+struct PdbDbiStream {
+ PdbRaw_DbiVer VerHeader = PdbDbiV70;
+ uint32_t Age = 1;
+ uint16_t BuildNumber = 0;
+ uint32_t PdbDllVersion = 0;
+ uint16_t PdbDllRbld = 0;
+ uint16_t Flags = 1;
+ PDB_Machine MachineType = PDB_Machine::x86;
+
+ std::vector<PdbDbiModuleInfo> ModInfos;
+};
+
+struct PdbTpiStream {
+ PdbRaw_TpiVer Version = PdbTpiV80;
+ std::vector<CodeViewYAML::LeafRecord> Records;
+};
+
+struct PdbPublicsStream {
+ std::vector<CodeViewYAML::SymbolRecord> PubSyms;
+};
+
+struct PdbObject {
+ explicit PdbObject(BumpPtrAllocator &Allocator) : Allocator(Allocator) {}
+
+ Optional<MSFHeaders> Headers;
+ Optional<std::vector<uint32_t>> StreamSizes;
+ Optional<std::vector<StreamBlockList>> StreamMap;
+ Optional<PdbInfoStream> PdbStream;
+ Optional<PdbDbiStream> DbiStream;
+ Optional<PdbTpiStream> TpiStream;
+ Optional<PdbTpiStream> IpiStream;
+ Optional<PdbPublicsStream> PublicsStream;
+
+ Optional<std::vector<StringRef>> StringTable;
+
+ BumpPtrAllocator &Allocator;
+};
+}
+}
+}
+
+LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbObject)
+LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::MSFHeaders)
+LLVM_YAML_DECLARE_MAPPING_TRAITS(msf::SuperBlock)
+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)
+
+#endif // LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp
new file mode 100644
index 00000000000..cd01a400481
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp
@@ -0,0 +1,97 @@
+//===- PrettyBuiltinDumper.cpp ---------------------------------- *- C++ *-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyBuiltinDumper.h"
+#include "LinePrinter.h"
+
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+BuiltinDumper::BuiltinDumper(LinePrinter &P)
+ : PDBSymDumper(false), Printer(P) {}
+
+void BuiltinDumper::start(const PDBSymbolTypeBuiltin &Symbol) {
+ if (Symbol.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
+ if (Symbol.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
+ WithColor(Printer, PDB_ColorItem::Type).get() << getTypeName(Symbol);
+}
+
+StringRef BuiltinDumper::getTypeName(const PDBSymbolTypeBuiltin &Symbol) {
+ PDB_BuiltinType Type = Symbol.getBuiltinType();
+ switch (Type) {
+ case PDB_BuiltinType::Float:
+ if (Symbol.getLength() == 4)
+ return "float";
+ return "double";
+ case PDB_BuiltinType::UInt:
+ switch (Symbol.getLength()) {
+ case 8:
+ return "unsigned __int64";
+ case 4:
+ return "unsigned int";
+ case 2:
+ return "unsigned short";
+ case 1:
+ return "unsigned char";
+ default:
+ return "unsigned";
+ }
+ case PDB_BuiltinType::Int:
+ switch (Symbol.getLength()) {
+ case 8:
+ return "__int64";
+ case 4:
+ return "int";
+ case 2:
+ return "short";
+ case 1:
+ return "char";
+ default:
+ return "int";
+ }
+ case PDB_BuiltinType::Char:
+ return "char";
+ case PDB_BuiltinType::WCharT:
+ return "wchar_t";
+ case PDB_BuiltinType::Void:
+ return "void";
+ case PDB_BuiltinType::Long:
+ return "long";
+ case PDB_BuiltinType::ULong:
+ return "unsigned long";
+ case PDB_BuiltinType::Bool:
+ return "bool";
+ case PDB_BuiltinType::Currency:
+ return "CURRENCY";
+ case PDB_BuiltinType::Date:
+ return "DATE";
+ case PDB_BuiltinType::Variant:
+ return "VARIANT";
+ case PDB_BuiltinType::Complex:
+ return "complex";
+ case PDB_BuiltinType::Bitfield:
+ return "bitfield";
+ case PDB_BuiltinType::BSTR:
+ return "BSTR";
+ case PDB_BuiltinType::HResult:
+ return "HRESULT";
+ case PDB_BuiltinType::BCD:
+ return "HRESULT";
+ case PDB_BuiltinType::Char16:
+ return "char16_t";
+ case PDB_BuiltinType::Char32:
+ return "char32_t";
+ case PDB_BuiltinType::None:
+ return "...";
+ }
+ llvm_unreachable("Unknown PDB_BuiltinType");
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyBuiltinDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyBuiltinDumper.h
new file mode 100644
index 00000000000..3bdef34c48f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyBuiltinDumper.h
@@ -0,0 +1,34 @@
+//===- PrettyBuiltinDumper.h ---------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYBUILTINDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYBUILTINDUMPER_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+
+namespace llvm {
+namespace pdb {
+
+class LinePrinter;
+
+class BuiltinDumper : public PDBSymDumper {
+public:
+ BuiltinDumper(LinePrinter &P);
+
+ void start(const PDBSymbolTypeBuiltin &Symbol);
+
+private:
+ StringRef getTypeName(const PDBSymbolTypeBuiltin &Symbol);
+
+ LinePrinter &Printer;
+};
+}
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp
new file mode 100644
index 00000000000..b7eccac5988
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp
@@ -0,0 +1,114 @@
+//===- PrettyClassDefinitionDumper.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyClassDefinitionDumper.h"
+
+#include "LinePrinter.h"
+#include "PrettyClassLayoutGraphicalDumper.h"
+#include "llvm-pdbutil.h"
+
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
+#include "llvm/DebugInfo/PDB/UDTLayout.h"
+
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+ClassDefinitionDumper::ClassDefinitionDumper(LinePrinter &P)
+ : PDBSymDumper(true), Printer(P) {}
+
+void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) {
+ assert(opts::pretty::ClassFormat !=
+ opts::pretty::ClassDefinitionFormat::None);
+
+ ClassLayout Layout(Class);
+ start(Layout);
+}
+
+void ClassDefinitionDumper::start(const ClassLayout &Layout) {
+ prettyPrintClassIntro(Layout);
+
+ PrettyClassLayoutGraphicalDumper Dumper(Printer, 1, 0);
+ DumpedAnything |= Dumper.start(Layout);
+
+ prettyPrintClassOutro(Layout);
+}
+
+void ClassDefinitionDumper::prettyPrintClassIntro(const ClassLayout &Layout) {
+ DumpedAnything = false;
+ Printer.NewLine();
+
+ 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
+ << "]";
+ uint32_t BaseCount = Layout.bases().size();
+ if (BaseCount > 0) {
+ Printer.Indent();
+ char NextSeparator = ':';
+ for (auto BC : Layout.bases()) {
+ const auto &Base = BC->getBase();
+ if (Base.isIndirectVirtualBaseClass())
+ continue;
+
+ Printer.NewLine();
+ Printer << NextSeparator << " ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << Base.getAccess();
+ if (BC->isVirtualBase())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual";
+
+ WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base.getName();
+ NextSeparator = ',';
+ }
+
+ Printer.Unindent();
+ }
+
+ Printer << " {";
+ Printer.Indent();
+}
+
+void ClassDefinitionDumper::prettyPrintClassOutro(const ClassLayout &Layout) {
+ Printer.Unindent();
+ if (DumpedAnything)
+ Printer.NewLine();
+ Printer << "}";
+ Printer.NewLine();
+ if (Layout.deepPaddingSize() > 0) {
+ APFloat Pct(100.0 * (double)Layout.deepPaddingSize() /
+ (double)Layout.getSize());
+ SmallString<8> PctStr;
+ Pct.toString(PctStr, 4);
+ WithColor(Printer, PDB_ColorItem::Padding).get()
+ << "Total padding " << Layout.deepPaddingSize() << " bytes (" << PctStr
+ << "% of class size)";
+ Printer.NewLine();
+ APFloat Pct2(100.0 * (double)Layout.immediatePadding() /
+ (double)Layout.getSize());
+ PctStr.clear();
+ Pct2.toString(PctStr, 4);
+ WithColor(Printer, PDB_ColorItem::Padding).get()
+ << "Immediate padding " << Layout.immediatePadding() << " bytes ("
+ << PctStr << "% of class size)";
+ Printer.NewLine();
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h
new file mode 100644
index 00000000000..f43c5c11bdf
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h
@@ -0,0 +1,46 @@
+//===- PrettyClassDefinitionDumper.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSDEFINITIONDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSDEFINITIONDUMPER_H
+
+#include "llvm/ADT/BitVector.h"
+
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+
+#include <list>
+#include <memory>
+#include <unordered_map>
+
+namespace llvm {
+class BitVector;
+
+namespace pdb {
+
+class ClassLayout;
+class LinePrinter;
+
+class ClassDefinitionDumper : public PDBSymDumper {
+public:
+ ClassDefinitionDumper(LinePrinter &P);
+
+ void start(const PDBSymbolTypeUDT &Class);
+ void start(const ClassLayout &Class);
+
+private:
+ void prettyPrintClassIntro(const ClassLayout &Class);
+ void prettyPrintClassOutro(const ClassLayout &Class);
+
+ LinePrinter &Printer;
+ bool DumpedAnything = false;
+};
+}
+}
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp
new file mode 100644
index 00000000000..a522935e34f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp
@@ -0,0 +1,212 @@
+//===- PrettyClassLayoutGraphicalDumper.h -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyClassLayoutGraphicalDumper.h"
+
+#include "LinePrinter.h"
+#include "PrettyClassDefinitionDumper.h"
+#include "PrettyEnumDumper.h"
+#include "PrettyFunctionDumper.h"
+#include "PrettyTypedefDumper.h"
+#include "PrettyVariableDumper.h"
+#include "PrettyVariableDumper.h"
+#include "llvm-pdbutil.h"
+
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
+#include "llvm/DebugInfo/PDB/UDTLayout.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+PrettyClassLayoutGraphicalDumper::PrettyClassLayoutGraphicalDumper(
+ LinePrinter &P, uint32_t RecurseLevel, uint32_t InitialOffset)
+ : PDBSymDumper(true), Printer(P), RecursionLevel(RecurseLevel),
+ ClassOffsetZero(InitialOffset), CurrentAbsoluteOffset(InitialOffset) {}
+
+bool PrettyClassLayoutGraphicalDumper::start(const UDTLayoutBase &Layout) {
+
+ if (RecursionLevel == 1 &&
+ opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::All) {
+ for (auto &Other : Layout.other_items())
+ Other->dump(*this);
+ for (auto &Func : Layout.funcs())
+ Func->dump(*this);
+ }
+
+ const BitVector &UseMap = Layout.usedBytes();
+ int NextPaddingByte = UseMap.find_first_unset();
+
+ for (auto &Item : Layout.layout_items()) {
+ // Calculate the absolute offset of the first byte of the next field.
+ uint32_t RelativeOffset = Item->getOffsetInParent();
+ CurrentAbsoluteOffset = ClassOffsetZero + RelativeOffset;
+
+ // This might be an empty base, in which case it could extend outside the
+ // bounds of the parent class.
+ if (RelativeOffset < UseMap.size() && (Item->getSize() > 0)) {
+ // If there is any remaining padding in this class, and the offset of the
+ // new item is after the padding, then we must have just jumped over some
+ // padding. Print a padding row and then look for where the next block
+ // of padding begins.
+ if ((NextPaddingByte >= 0) &&
+ (RelativeOffset > uint32_t(NextPaddingByte))) {
+ printPaddingRow(RelativeOffset - NextPaddingByte);
+ NextPaddingByte = UseMap.find_next_unset(RelativeOffset);
+ }
+ }
+
+ CurrentItem = Item;
+ if (Item->isVBPtr()) {
+ VTableLayoutItem &Layout = static_cast<VTableLayoutItem &>(*CurrentItem);
+
+ VariableDumper VarDumper(Printer);
+ VarDumper.startVbptr(CurrentAbsoluteOffset, Layout.getSize());
+ } else {
+ if (auto Sym = Item->getSymbol())
+ Sym->dump(*this);
+ }
+
+ if (Item->getLayoutSize() > 0) {
+ uint32_t Prev = RelativeOffset + Item->getLayoutSize() - 1;
+ if (Prev < UseMap.size())
+ NextPaddingByte = UseMap.find_next_unset(Prev);
+ }
+ }
+
+ auto TailPadding = Layout.tailPadding();
+ if (TailPadding > 0) {
+ if (TailPadding != 1 || Layout.getSize() != 1) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Padding).get()
+ << "<padding> (" << TailPadding << " bytes)";
+ DumpedAnything = true;
+ }
+ }
+
+ return DumpedAnything;
+}
+
+void PrettyClassLayoutGraphicalDumper::printPaddingRow(uint32_t Amount) {
+ if (Amount == 0)
+ return;
+
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << Amount
+ << " bytes)";
+ DumpedAnything = true;
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(
+ const PDBSymbolTypeBaseClass &Symbol) {
+ assert(CurrentItem != nullptr);
+
+ Printer.NewLine();
+ BaseClassLayout &Layout = static_cast<BaseClassLayout &>(*CurrentItem);
+
+ std::string Label = "base";
+ if (Layout.isVirtualBase()) {
+ Label.insert(Label.begin(), 'v');
+ if (Layout.getBase().isIndirectVirtualBaseClass())
+ Label.insert(Label.begin(), 'i');
+ }
+ Printer << Label << " ";
+
+ uint32_t Size = Layout.isEmptyBase() ? 1 : Layout.getLayoutSize();
+
+ WithColor(Printer, PDB_ColorItem::Offset).get()
+ << "+" << format_hex(CurrentAbsoluteOffset, 4) << " [sizeof=" << Size
+ << "] ";
+
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Layout.getName();
+
+ if (shouldRecurse()) {
+ Printer.Indent();
+ uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent();
+ PrettyClassLayoutGraphicalDumper BaseDumper(Printer, RecursionLevel + 1,
+ ChildOffsetZero);
+ DumpedAnything |= BaseDumper.start(Layout);
+ Printer.Unindent();
+ }
+
+ DumpedAnything = true;
+}
+
+bool PrettyClassLayoutGraphicalDumper::shouldRecurse() const {
+ uint32_t Limit = opts::pretty::ClassRecursionDepth;
+ if (Limit == 0)
+ return true;
+ return RecursionLevel < Limit;
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolData &Symbol) {
+ VariableDumper VarDumper(Printer);
+ VarDumper.start(Symbol, ClassOffsetZero);
+
+ if (CurrentItem != nullptr) {
+ DataMemberLayoutItem &Layout =
+ static_cast<DataMemberLayoutItem &>(*CurrentItem);
+
+ if (Layout.hasUDTLayout() && shouldRecurse()) {
+ uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent();
+ Printer.Indent();
+ PrettyClassLayoutGraphicalDumper TypeDumper(Printer, RecursionLevel + 1,
+ ChildOffsetZero);
+ TypeDumper.start(Layout.getUDTLayout());
+ Printer.Unindent();
+ }
+ }
+
+ DumpedAnything = true;
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeVTable &Symbol) {
+ assert(CurrentItem != nullptr);
+
+ VariableDumper VarDumper(Printer);
+ VarDumper.start(Symbol, ClassOffsetZero);
+
+ DumpedAnything = true;
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeEnum &Symbol) {
+ DumpedAnything = true;
+ Printer.NewLine();
+ EnumDumper Dumper(Printer);
+ Dumper.start(Symbol);
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(
+ const PDBSymbolTypeTypedef &Symbol) {
+ DumpedAnything = true;
+ Printer.NewLine();
+ TypedefDumper Dumper(Printer);
+ Dumper.start(Symbol);
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(
+ const PDBSymbolTypeBuiltin &Symbol) {}
+
+void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeUDT &Symbol) {}
+
+void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolFunc &Symbol) {
+ if (Printer.IsSymbolExcluded(Symbol.getName()))
+ return;
+ if (Symbol.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
+ return;
+ if (Symbol.getLength() == 0 && !Symbol.isPureVirtual() &&
+ !Symbol.isIntroVirtualFunction())
+ return;
+
+ DumpedAnything = true;
+ Printer.NewLine();
+ FunctionDumper Dumper(Printer);
+ Dumper.start(Symbol, FunctionDumper::PointerType::None);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h
new file mode 100644
index 00000000000..8f78b3b503d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h
@@ -0,0 +1,57 @@
+//===- PrettyClassLayoutGraphicalDumper.h -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTGRAPHICALDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTGRAPHICALDUMPER_H
+
+#include "llvm/ADT/BitVector.h"
+
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+
+namespace llvm {
+
+namespace pdb {
+
+class UDTLayoutBase;
+class LayoutItemBase;
+class LinePrinter;
+
+class PrettyClassLayoutGraphicalDumper : public PDBSymDumper {
+public:
+ PrettyClassLayoutGraphicalDumper(LinePrinter &P, uint32_t RecurseLevel,
+ uint32_t InitialOffset);
+
+ bool start(const UDTLayoutBase &Layout);
+
+ // Layout based symbol types.
+ void dump(const PDBSymbolTypeBaseClass &Symbol) override;
+ void dump(const PDBSymbolData &Symbol) override;
+ void dump(const PDBSymbolTypeVTable &Symbol) override;
+
+ // Non layout-based symbol types.
+ void dump(const PDBSymbolTypeEnum &Symbol) override;
+ void dump(const PDBSymbolFunc &Symbol) override;
+ void dump(const PDBSymbolTypeTypedef &Symbol) override;
+ void dump(const PDBSymbolTypeUDT &Symbol) override;
+ void dump(const PDBSymbolTypeBuiltin &Symbol) override;
+
+private:
+ bool shouldRecurse() const;
+ void printPaddingRow(uint32_t Amount);
+
+ LinePrinter &Printer;
+
+ LayoutItemBase *CurrentItem = nullptr;
+ uint32_t RecursionLevel = 0;
+ uint32_t ClassOffsetZero = 0;
+ uint32_t CurrentAbsoluteOffset = 0;
+ bool DumpedAnything = false;
+};
+}
+}
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyCompilandDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyCompilandDumper.cpp
new file mode 100644
index 00000000000..cf769ff6647
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyCompilandDumper.cpp
@@ -0,0 +1,228 @@
+//===- PrettyCompilandDumper.cpp - llvm-pdbutil compiland dumper -*- C++ *-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyCompilandDumper.h"
+
+#include "LinePrinter.h"
+#include "PrettyFunctionDumper.h"
+#include "llvm-pdbutil.h"
+
+#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
+#include "llvm/DebugInfo/PDB/IPDBLineNumber.h"
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/IPDBSourceFile.h"
+#include "llvm/DebugInfo/PDB/PDBExtras.h"
+#include "llvm/DebugInfo/PDB/PDBSymbol.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolLabel.h"
+#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"
+
+#include <utility>
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+CompilandDumper::CompilandDumper(LinePrinter &P)
+ : PDBSymDumper(true), Printer(P) {}
+
+void CompilandDumper::dump(const PDBSymbolCompilandDetails &Symbol) {}
+
+void CompilandDumper::dump(const PDBSymbolCompilandEnv &Symbol) {}
+
+void CompilandDumper::start(const PDBSymbolCompiland &Symbol,
+ CompilandDumpFlags opts) {
+ std::string FullName = Symbol.getName();
+ if (Printer.IsCompilandExcluded(FullName))
+ return;
+
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Path).get() << FullName;
+
+ if (opts & Flags::Lines) {
+ const IPDBSession &Session = Symbol.getSession();
+ if (auto Files = Session.getSourceFilesForCompiland(Symbol)) {
+ Printer.Indent();
+ while (auto File = Files->getNext()) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Path).get() << File->getFileName();
+ if (File->getChecksumType() != PDB_Checksum::None) {
+ auto ChecksumType = File->getChecksumType();
+ auto ChecksumHexString = toHex(File->getChecksum());
+ WithColor(Printer, PDB_ColorItem::Comment).get()
+ << " (" << ChecksumType << ": " << ChecksumHexString << ")";
+ }
+
+ auto Lines = Session.findLineNumbers(Symbol, *File);
+ if (!Lines)
+ continue;
+
+ Printer.Indent();
+ while (auto Line = Lines->getNext()) {
+ Printer.NewLine();
+ uint32_t LineStart = Line->getLineNumber();
+ uint32_t LineEnd = Line->getLineNumberEnd();
+
+ Printer << "Line ";
+ PDB_ColorItem StatementColor = Line->isStatement()
+ ? PDB_ColorItem::Keyword
+ : PDB_ColorItem::LiteralValue;
+ WithColor(Printer, StatementColor).get() << LineStart;
+ if (LineStart != LineEnd)
+ WithColor(Printer, StatementColor).get() << " - " << LineEnd;
+
+ uint32_t ColumnStart = Line->getColumnNumber();
+ uint32_t ColumnEnd = Line->getColumnNumberEnd();
+ if (ColumnStart != 0 || ColumnEnd != 0) {
+ Printer << ", Column: ";
+ WithColor(Printer, StatementColor).get() << ColumnStart;
+ if (ColumnEnd != ColumnStart)
+ WithColor(Printer, StatementColor).get() << " - " << ColumnEnd;
+ }
+
+ Printer << ", Address: ";
+ if (Line->getLength() > 0) {
+ uint64_t AddrStart = Line->getVirtualAddress();
+ uint64_t AddrEnd = AddrStart + Line->getLength() - 1;
+ WithColor(Printer, PDB_ColorItem::Address).get()
+ << "[" << format_hex(AddrStart, 10) << " - "
+ << format_hex(AddrEnd, 10) << "]";
+ Printer << " (" << Line->getLength() << " bytes)";
+ } else {
+ uint64_t AddrStart = Line->getVirtualAddress();
+ WithColor(Printer, PDB_ColorItem::Address).get()
+ << "[" << format_hex(AddrStart, 10) << "] ";
+ Printer << "(0 bytes)";
+ }
+ }
+ Printer.Unindent();
+ }
+ Printer.Unindent();
+ }
+ }
+
+ if (opts & Flags::Children) {
+ if (auto ChildrenEnum = Symbol.findAllChildren()) {
+ Printer.Indent();
+ while (auto Child = ChildrenEnum->getNext())
+ Child->dump(*this);
+ Printer.Unindent();
+ }
+ }
+}
+
+void CompilandDumper::dump(const PDBSymbolData &Symbol) {
+ if (!shouldDumpSymLevel(opts::pretty::SymLevel::Data))
+ return;
+ if (Printer.IsSymbolExcluded(Symbol.getName()))
+ return;
+
+ Printer.NewLine();
+
+ switch (auto LocType = Symbol.getLocationType()) {
+ case PDB_LocType::Static:
+ Printer << "data: ";
+ WithColor(Printer, PDB_ColorItem::Address).get()
+ << "[" << format_hex(Symbol.getVirtualAddress(), 10) << "]";
+
+ WithColor(Printer, PDB_ColorItem::Comment).get()
+ << " [sizeof = " << getTypeLength(Symbol) << "]";
+
+ break;
+ case PDB_LocType::Constant:
+ Printer << "constant: ";
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get()
+ << "[" << Symbol.getValue() << "]";
+ WithColor(Printer, PDB_ColorItem::Comment).get()
+ << " [sizeof = " << getTypeLength(Symbol) << "]";
+ break;
+ default:
+ Printer << "data(unexpected type=" << LocType << ")";
+ }
+
+ Printer << " ";
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
+}
+
+void CompilandDumper::dump(const PDBSymbolFunc &Symbol) {
+ if (!shouldDumpSymLevel(opts::pretty::SymLevel::Functions))
+ return;
+ if (Symbol.getLength() == 0)
+ return;
+ if (Printer.IsSymbolExcluded(Symbol.getName()))
+ return;
+
+ Printer.NewLine();
+ FunctionDumper Dumper(Printer);
+ Dumper.start(Symbol, FunctionDumper::PointerType::None);
+}
+
+void CompilandDumper::dump(const PDBSymbolLabel &Symbol) {
+ if (Printer.IsSymbolExcluded(Symbol.getName()))
+ return;
+
+ Printer.NewLine();
+ Printer << "label ";
+ WithColor(Printer, PDB_ColorItem::Address).get()
+ << "[" << format_hex(Symbol.getVirtualAddress(), 10) << "] ";
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
+}
+
+void CompilandDumper::dump(const PDBSymbolThunk &Symbol) {
+ if (!shouldDumpSymLevel(opts::pretty::SymLevel::Thunks))
+ return;
+ if (Printer.IsSymbolExcluded(Symbol.getName()))
+ return;
+
+ Printer.NewLine();
+ Printer << "thunk ";
+ codeview::ThunkOrdinal Ordinal = Symbol.getThunkOrdinal();
+ uint64_t VA = Symbol.getVirtualAddress();
+ if (Ordinal == codeview::ThunkOrdinal::TrampIncremental) {
+ uint64_t Target = Symbol.getTargetVirtualAddress();
+ WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(VA, 10);
+ Printer << " -> ";
+ WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(Target, 10);
+ } else {
+ WithColor(Printer, PDB_ColorItem::Address).get()
+ << "[" << format_hex(VA, 10) << " - "
+ << format_hex(VA + Symbol.getLength(), 10) << "]";
+ }
+ Printer << " (";
+ WithColor(Printer, PDB_ColorItem::Register).get() << Ordinal;
+ Printer << ") ";
+ std::string Name = Symbol.getName();
+ if (!Name.empty())
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Name;
+}
+
+void CompilandDumper::dump(const PDBSymbolTypeTypedef &Symbol) {}
+
+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/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyCompilandDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyCompilandDumper.h
new file mode 100644
index 00000000000..c83a58672d1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyCompilandDumper.h
@@ -0,0 +1,44 @@
+//===- PrettyCompilandDumper.h - llvm-pdbutil compiland dumper -*- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYCOMPILANDDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCOMPILANDDUMPER_H
+
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+
+namespace llvm {
+namespace pdb {
+
+class LinePrinter;
+
+typedef int CompilandDumpFlags;
+class CompilandDumper : public PDBSymDumper {
+public:
+ enum Flags { None = 0x0, Children = 0x1, Symbols = 0x2, Lines = 0x4 };
+
+ CompilandDumper(LinePrinter &P);
+
+ void start(const PDBSymbolCompiland &Symbol, CompilandDumpFlags flags);
+
+ void dump(const PDBSymbolCompilandDetails &Symbol) override;
+ void dump(const PDBSymbolCompilandEnv &Symbol) override;
+ void dump(const PDBSymbolData &Symbol) override;
+ void dump(const PDBSymbolFunc &Symbol) override;
+ void dump(const PDBSymbolLabel &Symbol) override;
+ 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;
+};
+}
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyEnumDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyEnumDumper.cpp
new file mode 100644
index 00000000000..9ed5893f252
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyEnumDumper.cpp
@@ -0,0 +1,68 @@
+//===- PrettyEnumDumper.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyEnumDumper.h"
+
+#include "LinePrinter.h"
+#include "PrettyBuiltinDumper.h"
+#include "llvm-pdbutil.h"
+
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
+
+using namespace llvm;
+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) {
+ auto UnderlyingType = Symbol.getUnderlyingType();
+ if (!UnderlyingType)
+ return;
+ if (UnderlyingType->getBuiltinType() != PDB_BuiltinType::Int ||
+ UnderlyingType->getLength() != 4) {
+ Printer << " : ";
+ BuiltinDumper Dumper(Printer);
+ Dumper.start(*UnderlyingType);
+ }
+ auto EnumValues = Symbol.findAllChildren<PDBSymbolData>();
+ Printer << " {";
+ Printer.Indent();
+ if (EnumValues && EnumValues->getChildCount() > 0) {
+ while (auto EnumValue = EnumValues->getNext()) {
+ if (EnumValue->getDataKind() != PDB_DataKind::Constant)
+ continue;
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Identifier).get()
+ << EnumValue->getName();
+ Printer << " = ";
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get()
+ << EnumValue->getValue();
+ }
+ }
+ Printer.Unindent();
+ Printer.NewLine();
+ Printer << "}";
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyEnumDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyEnumDumper.h
new file mode 100644
index 00000000000..e7c5c1aeb01
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyEnumDumper.h
@@ -0,0 +1,30 @@
+//===- PrettyEnumDumper.h ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYENUMDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYENUMDUMPER_H
+
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+
+namespace llvm {
+namespace pdb {
+
+class LinePrinter;
+
+class EnumDumper : public PDBSymDumper {
+public:
+ EnumDumper(LinePrinter &P);
+
+ void start(const PDBSymbolTypeEnum &Symbol);
+
+private:
+ LinePrinter &Printer;
+};
+}
+}
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp
new file mode 100644
index 00000000000..fede031ec0c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp
@@ -0,0 +1,41 @@
+//===- PrettyExternalSymbolDumper.cpp -------------------------- *- C++ *-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyExternalSymbolDumper.h"
+#include "LinePrinter.h"
+
+#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+ExternalSymbolDumper::ExternalSymbolDumper(LinePrinter &P)
+ : PDBSymDumper(true), Printer(P) {}
+
+void ExternalSymbolDumper::start(const PDBSymbolExe &Symbol) {
+ if (auto Vars = Symbol.findAllChildren<PDBSymbolPublicSymbol>()) {
+ while (auto Var = Vars->getNext())
+ Var->dump(*this);
+ }
+}
+
+void ExternalSymbolDumper::dump(const PDBSymbolPublicSymbol &Symbol) {
+ std::string LinkageName = Symbol.getName();
+ if (Printer.IsSymbolExcluded(LinkageName))
+ return;
+
+ Printer.NewLine();
+ uint64_t Addr = Symbol.getVirtualAddress();
+
+ Printer << "public [";
+ WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(Addr, 10);
+ Printer << "] ";
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << LinkageName;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h
new file mode 100644
index 00000000000..58fafe94331
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h
@@ -0,0 +1,33 @@
+//===- PrettyExternalSymbolDumper.h --------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYEXTERNALSYMBOLDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYEXTERNALSYMBOLDUMPER_H
+
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+
+namespace llvm {
+namespace pdb {
+
+class LinePrinter;
+
+class ExternalSymbolDumper : public PDBSymDumper {
+public:
+ ExternalSymbolDumper(LinePrinter &P);
+
+ void start(const PDBSymbolExe &Symbol);
+
+ void dump(const PDBSymbolPublicSymbol &Symbol) override;
+
+private:
+ LinePrinter &Printer;
+};
+}
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyFunctionDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyFunctionDumper.cpp
new file mode 100644
index 00000000000..b820ca33396
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyFunctionDumper.cpp
@@ -0,0 +1,267 @@
+//===- PrettyFunctionDumper.cpp --------------------------------- *- C++ *-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyFunctionDumper.h"
+#include "LinePrinter.h"
+#include "PrettyBuiltinDumper.h"
+
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/PDBExtras.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.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/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+namespace {
+template <class T>
+void dumpClassParentWithScopeOperator(const T &Symbol, LinePrinter &Printer,
+ FunctionDumper &Dumper) {
+ uint32_t ClassParentId = Symbol.getClassParentId();
+ auto ClassParent =
+ Symbol.getSession().template getConcreteSymbolById<PDBSymbolTypeUDT>(
+ ClassParentId);
+ if (!ClassParent)
+ return;
+
+ WithColor(Printer, PDB_ColorItem::Type).get() << ClassParent->getName();
+ Printer << "::";
+}
+}
+
+FunctionDumper::FunctionDumper(LinePrinter &P)
+ : PDBSymDumper(true), Printer(P) {}
+
+void FunctionDumper::start(const PDBSymbolTypeFunctionSig &Symbol,
+ const char *Name, PointerType Pointer) {
+ auto ReturnType = Symbol.getReturnType();
+ if (!ReturnType)
+ Printer << "<unknown-type>";
+ else
+ ReturnType->dump(*this);
+ Printer << " ";
+ uint32_t ClassParentId = Symbol.getClassParentId();
+ auto ClassParent =
+ Symbol.getSession().getConcreteSymbolById<PDBSymbolTypeUDT>(
+ ClassParentId);
+
+ PDB_CallingConv CC = Symbol.getCallingConvention();
+ bool ShouldDumpCallingConvention = true;
+ if ((ClassParent && CC == CallingConvention::ThisCall) ||
+ (!ClassParent && CC == CallingConvention::NearStdCall)) {
+ ShouldDumpCallingConvention = false;
+ }
+
+ if (Pointer == PointerType::None) {
+ if (ShouldDumpCallingConvention)
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " ";
+ if (ClassParent) {
+ Printer << "(";
+ WithColor(Printer, PDB_ColorItem::Identifier).get()
+ << ClassParent->getName();
+ Printer << "::)";
+ }
+ } else {
+ Printer << "(";
+ if (ShouldDumpCallingConvention)
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " ";
+ if (ClassParent) {
+ WithColor(Printer, PDB_ColorItem::Identifier).get()
+ << ClassParent->getName();
+ Printer << "::";
+ }
+ if (Pointer == PointerType::Reference)
+ Printer << "&";
+ else
+ Printer << "*";
+ if (Name)
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Name;
+ Printer << ")";
+ }
+
+ Printer << "(";
+ if (auto ChildEnum = Symbol.getArguments()) {
+ uint32_t Index = 0;
+ while (auto Arg = ChildEnum->getNext()) {
+ Arg->dump(*this);
+ if (++Index < ChildEnum->getChildCount())
+ Printer << ", ";
+ }
+ }
+ Printer << ")";
+
+ if (Symbol.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " const";
+ if (Symbol.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile";
+}
+
+void FunctionDumper::start(const PDBSymbolFunc &Symbol, PointerType Pointer) {
+ uint64_t FuncStart = Symbol.getVirtualAddress();
+ uint64_t FuncEnd = FuncStart + Symbol.getLength();
+
+ Printer << "func [";
+ WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncStart, 10);
+ if (auto DebugStart = Symbol.findOneChild<PDBSymbolFuncDebugStart>()) {
+ uint64_t Prologue = DebugStart->getVirtualAddress() - FuncStart;
+ WithColor(Printer, PDB_ColorItem::Offset).get()
+ << formatv("+{0,2}", Prologue);
+ }
+ Printer << " - ";
+ WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncEnd, 10);
+ if (auto DebugEnd = Symbol.findOneChild<PDBSymbolFuncDebugEnd>()) {
+ uint64_t Epilogue = FuncEnd - DebugEnd->getVirtualAddress();
+ WithColor(Printer, PDB_ColorItem::Offset).get()
+ << formatv("-{0,2}", Epilogue);
+ }
+
+ WithColor(Printer, PDB_ColorItem::Comment).get()
+ << formatv(" | sizeof={0,3}", Symbol.getLength());
+ Printer << "] (";
+
+ if (Symbol.hasFramePointer()) {
+ WithColor(Printer, PDB_ColorItem::Register).get()
+ << CPURegister{Symbol.getRawSymbol().getPlatform(),
+ Symbol.getLocalBasePointerRegisterId()};
+ } else {
+ WithColor(Printer, PDB_ColorItem::Register).get() << "FPO";
+ }
+ Printer << ") ";
+
+ if (Symbol.isVirtual() || Symbol.isPureVirtual())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "virtual ";
+
+ auto Signature = Symbol.getSignature();
+ if (!Signature) {
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
+ if (Pointer == PointerType::Pointer)
+ Printer << "*";
+ else if (Pointer == FunctionDumper::PointerType::Reference)
+ Printer << "&";
+ return;
+ }
+
+ auto ReturnType = Signature->getReturnType();
+ ReturnType->dump(*this);
+ Printer << " ";
+
+ auto ClassParent = Symbol.getClassParent();
+ CallingConvention CC = Signature->getCallingConvention();
+ if (Pointer != FunctionDumper::PointerType::None)
+ Printer << "(";
+
+ if ((ClassParent && CC != CallingConvention::ThisCall) ||
+ (!ClassParent && CC != CallingConvention::NearStdCall)) {
+ WithColor(Printer, PDB_ColorItem::Keyword).get()
+ << Signature->getCallingConvention() << " ";
+ }
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
+ if (Pointer != FunctionDumper::PointerType::None) {
+ if (Pointer == PointerType::Pointer)
+ Printer << "*";
+ else if (Pointer == FunctionDumper::PointerType::Reference)
+ Printer << "&";
+ Printer << ")";
+ }
+
+ Printer << "(";
+ if (auto Arguments = Symbol.getArguments()) {
+ uint32_t Index = 0;
+ while (auto Arg = Arguments->getNext()) {
+ auto ArgType = Arg->getType();
+ ArgType->dump(*this);
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << " "
+ << Arg->getName();
+ if (++Index < Arguments->getChildCount())
+ Printer << ", ";
+ }
+ if (Signature->isCVarArgs())
+ Printer << ", ...";
+ }
+ Printer << ")";
+ if (Symbol.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " const";
+ if (Symbol.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile";
+ if (Symbol.isPureVirtual())
+ Printer << " = 0";
+}
+
+void FunctionDumper::dump(const PDBSymbolTypeArray &Symbol) {
+ auto ElementType = Symbol.getElementType();
+
+ ElementType->dump(*this);
+ Printer << "[";
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Symbol.getLength();
+ Printer << "]";
+}
+
+void FunctionDumper::dump(const PDBSymbolTypeBuiltin &Symbol) {
+ BuiltinDumper Dumper(Printer);
+ Dumper.start(Symbol);
+}
+
+void FunctionDumper::dump(const PDBSymbolTypeEnum &Symbol) {
+ dumpClassParentWithScopeOperator(Symbol, Printer, *this);
+ WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
+}
+
+void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) {
+ // PDBSymbolTypeFunctionArg is just a shim over the real argument. Just drill
+ // through to the real thing and dump it.
+ uint32_t TypeId = Symbol.getTypeId();
+ auto Type = Symbol.getSession().getSymbolById(TypeId);
+ if (Type)
+ Type->dump(*this);
+ else
+ Printer << "<unknown-type>";
+}
+
+void FunctionDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
+ dumpClassParentWithScopeOperator(Symbol, Printer, *this);
+ WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
+}
+
+void FunctionDumper::dump(const PDBSymbolTypePointer &Symbol) {
+ auto PointeeType = Symbol.getPointeeType();
+ if (!PointeeType)
+ return;
+
+ if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) {
+ FunctionDumper NestedDumper(Printer);
+ PointerType Pointer =
+ Symbol.isReference() ? PointerType::Reference : PointerType::Pointer;
+ NestedDumper.start(*FuncSig, nullptr, Pointer);
+ } else {
+ if (Symbol.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
+ if (Symbol.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
+ PointeeType->dump(*this);
+ Printer << (Symbol.isReference() ? "&" : "*");
+
+ if (Symbol.getRawSymbol().isRestrictedType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " __restrict";
+ }
+}
+
+void FunctionDumper::dump(const PDBSymbolTypeUDT &Symbol) {
+ WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyFunctionDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyFunctionDumper.h
new file mode 100644
index 00000000000..df62604ac88
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyFunctionDumper.h
@@ -0,0 +1,42 @@
+//===- PrettyFunctionDumper.h --------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYFUNCTIONDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYFUNCTIONDUMPER_H
+
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+
+namespace llvm {
+namespace pdb {
+class LinePrinter;
+
+class FunctionDumper : public PDBSymDumper {
+public:
+ FunctionDumper(LinePrinter &P);
+
+ enum class PointerType { None, Pointer, Reference };
+
+ void start(const PDBSymbolTypeFunctionSig &Symbol, const char *Name,
+ PointerType Pointer);
+ void start(const PDBSymbolFunc &Symbol, PointerType Pointer);
+
+ void dump(const PDBSymbolTypeArray &Symbol) override;
+ void dump(const PDBSymbolTypeBuiltin &Symbol) override;
+ void dump(const PDBSymbolTypeEnum &Symbol) override;
+ void dump(const PDBSymbolTypeFunctionArg &Symbol) override;
+ void dump(const PDBSymbolTypePointer &Symbol) override;
+ void dump(const PDBSymbolTypeTypedef &Symbol) override;
+ void dump(const PDBSymbolTypeUDT &Symbol) override;
+
+private:
+ LinePrinter &Printer;
+};
+}
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypeDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypeDumper.cpp
new file mode 100644
index 00000000000..2f7a39803ca
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypeDumper.cpp
@@ -0,0 +1,359 @@
+//===- PrettyTypeDumper.cpp - PDBSymDumper type dumper *------------ C++ *-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyTypeDumper.h"
+
+#include "LinePrinter.h"
+#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"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+using LayoutPtr = std::unique_ptr<ClassLayout>;
+
+typedef bool (*CompareFunc)(const LayoutPtr &S1, const LayoutPtr &S2);
+
+static bool CompareNames(const LayoutPtr &S1, const LayoutPtr &S2) {
+ return S1->getName() < S2->getName();
+}
+
+static bool CompareSizes(const LayoutPtr &S1, const LayoutPtr &S2) {
+ return S1->getSize() < S2->getSize();
+}
+
+static bool ComparePadding(const LayoutPtr &S1, const LayoutPtr &S2) {
+ return S1->deepPaddingSize() < S2->deepPaddingSize();
+}
+
+static bool ComparePaddingPct(const LayoutPtr &S1, const LayoutPtr &S2) {
+ double Pct1 = (double)S1->deepPaddingSize() / (double)S1->getSize();
+ double Pct2 = (double)S2->deepPaddingSize() / (double)S2->getSize();
+ return Pct1 < Pct2;
+}
+
+static bool ComparePaddingImmediate(const LayoutPtr &S1, const LayoutPtr &S2) {
+ return S1->immediatePadding() < S2->immediatePadding();
+}
+
+static bool ComparePaddingPctImmediate(const LayoutPtr &S1,
+ const LayoutPtr &S2) {
+ double Pct1 = (double)S1->immediatePadding() / (double)S1->getSize();
+ double Pct2 = (double)S2->immediatePadding() / (double)S2->getSize();
+ return Pct1 < Pct2;
+}
+
+static CompareFunc getComparisonFunc(opts::pretty::ClassSortMode Mode) {
+ switch (Mode) {
+ case opts::pretty::ClassSortMode::Name:
+ return CompareNames;
+ case opts::pretty::ClassSortMode::Size:
+ return CompareSizes;
+ case opts::pretty::ClassSortMode::Padding:
+ return ComparePadding;
+ case opts::pretty::ClassSortMode::PaddingPct:
+ return ComparePaddingPct;
+ case opts::pretty::ClassSortMode::PaddingImmediate:
+ return ComparePaddingImmediate;
+ case opts::pretty::ClassSortMode::PaddingPctImmediate:
+ return ComparePaddingPctImmediate;
+ default:
+ return nullptr;
+ }
+}
+
+template <typename Enumerator>
+static std::vector<std::unique_ptr<ClassLayout>>
+filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E,
+ uint32_t UnfilteredCount) {
+ std::vector<std::unique_ptr<ClassLayout>> Filtered;
+
+ Filtered.reserve(UnfilteredCount);
+ CompareFunc Comp = getComparisonFunc(opts::pretty::ClassOrder);
+
+ if (UnfilteredCount > 10000) {
+ errs() << formatv("Filtering and sorting {0} types", UnfilteredCount);
+ errs().flush();
+ }
+ uint32_t Examined = 0;
+ uint32_t Discarded = 0;
+ while (auto Class = E.getNext()) {
+ ++Examined;
+ if (Examined % 10000 == 0) {
+ errs() << formatv("Examined {0}/{1} items. {2} items discarded\n",
+ Examined, UnfilteredCount, Discarded);
+ errs().flush();
+ }
+
+ if (Class->getUnmodifiedTypeId() != 0) {
+ ++Discarded;
+ continue;
+ }
+
+ if (Printer.IsTypeExcluded(Class->getName(), Class->getLength())) {
+ ++Discarded;
+ continue;
+ }
+
+ auto Layout = std::make_unique<ClassLayout>(std::move(Class));
+ if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) {
+ ++Discarded;
+ continue;
+ }
+ if (Layout->immediatePadding() < opts::pretty::ImmediatePaddingThreshold) {
+ ++Discarded;
+ continue;
+ }
+
+ Filtered.push_back(std::move(Layout));
+ }
+
+ if (Comp)
+ llvm::sort(Filtered, Comp);
+ return Filtered;
+}
+
+TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {}
+
+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();
+ Child->dump(TD);
+ }
+ 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>()) {
+ uint32_t All = Classes->getChildCount();
+
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << "Classes";
+
+ bool Precompute = false;
+ Precompute =
+ (opts::pretty::ClassOrder != opts::pretty::ClassSortMode::None);
+
+ // If we're using no sort mode, then we can start getting immediate output
+ // from the tool by just filtering as we go, rather than processing
+ // everything up front so that we can sort it. This makes the tool more
+ // responsive. So only precompute the filtered/sorted set of classes if
+ // necessary due to the specified options.
+ std::vector<LayoutPtr> Filtered;
+ uint32_t Shown = All;
+ if (Precompute) {
+ Filtered = filterAndSortClassDefs(Printer, *Classes, All);
+
+ Shown = Filtered.size();
+ }
+
+ Printer << ": (Showing " << Shown << " items";
+ if (Shown < All)
+ Printer << ", " << (All - Shown) << " filtered";
+ Printer << ")";
+ Printer.Indent();
+
+ // If we pre-computed, iterate the filtered/sorted list, otherwise iterate
+ // the DIA enumerator and filter on the fly.
+ if (Precompute) {
+ for (auto &Class : Filtered)
+ dumpClassLayout(*Class);
+ } else {
+ while (auto Class = Classes->getNext()) {
+ if (Printer.IsTypeExcluded(Class->getName(), Class->getLength()))
+ continue;
+
+ // 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 = std::make_unique<ClassLayout>(std::move(Class));
+ if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold)
+ continue;
+
+ dumpClassLayout(*Layout);
+ }
+ }
+
+ Printer.Unindent();
+ }
+ }
+}
+
+void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) {
+ assert(opts::pretty::Enums);
+
+ 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);
+
+ 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) {
+ 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/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypeDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypeDumper.h
new file mode 100644
index 00000000000..b6539d95bf3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypeDumper.h
@@ -0,0 +1,41 @@
+//===- PrettyTypeDumper.h - PDBSymDumper implementation for types *- C++ *-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYTYPEDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYTYPEDUMPER_H
+
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+
+namespace llvm {
+namespace pdb {
+class LinePrinter;
+class ClassLayout;
+
+class TypeDumper : public PDBSymDumper {
+public:
+ TypeDumper(LinePrinter &P);
+
+ void start(const PDBSymbolExe &Exe);
+
+ 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);
+
+private:
+ LinePrinter &Printer;
+};
+}
+}
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypedefDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypedefDumper.cpp
new file mode 100644
index 00000000000..ef73a8cdf9c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypedefDumper.cpp
@@ -0,0 +1,82 @@
+//===- PrettyTypedefDumper.cpp - PDBSymDumper impl for typedefs -- * C++ *-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyTypedefDumper.h"
+
+#include "LinePrinter.h"
+#include "PrettyBuiltinDumper.h"
+#include "PrettyFunctionDumper.h"
+#include "PrettyTypeDumper.h"
+
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/PDBExtras.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"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+TypedefDumper::TypedefDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {}
+
+void TypedefDumper::start(const PDBSymbolTypeTypedef &Symbol) {
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "typedef ";
+ uint32_t TargetId = Symbol.getTypeId();
+ if (auto TypeSymbol = Symbol.getSession().getSymbolById(TargetId))
+ TypeSymbol->dump(*this);
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << " "
+ << Symbol.getName();
+}
+
+void TypedefDumper::dump(const PDBSymbolTypeArray &Symbol) {
+ TypeDumper Dumper(Printer);
+ Dumper.dump(Symbol);
+}
+
+void TypedefDumper::dump(const PDBSymbolTypeBuiltin &Symbol) {
+ BuiltinDumper Dumper(Printer);
+ Dumper.start(Symbol);
+}
+
+void TypedefDumper::dump(const PDBSymbolTypeEnum &Symbol) {
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum ";
+ WithColor(Printer, PDB_ColorItem::Type).get() << " " << Symbol.getName();
+}
+
+void TypedefDumper::dump(const PDBSymbolTypePointer &Symbol) {
+ if (Symbol.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
+ if (Symbol.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
+ auto PointeeType = Symbol.getPointeeType();
+ if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) {
+ FunctionDumper::PointerType Pointer = FunctionDumper::PointerType::Pointer;
+ if (Symbol.isReference())
+ Pointer = FunctionDumper::PointerType::Reference;
+ FunctionDumper NestedDumper(Printer);
+ NestedDumper.start(*FuncSig, nullptr, Pointer);
+ } else {
+ PointeeType->dump(*this);
+ Printer << ((Symbol.isReference()) ? "&" : "*");
+ }
+
+ if (Symbol.getRawSymbol().isRestrictedType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " __restrict";
+}
+
+void TypedefDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) {
+ FunctionDumper Dumper(Printer);
+ Dumper.start(Symbol, nullptr, FunctionDumper::PointerType::None);
+}
+
+void TypedefDumper::dump(const PDBSymbolTypeUDT &Symbol) {
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "class ";
+ WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypedefDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypedefDumper.h
new file mode 100644
index 00000000000..ad8b3f37dcf
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyTypedefDumper.h
@@ -0,0 +1,38 @@
+//===- PrettyTypedefDumper.h - llvm-pdbutil typedef dumper ---*- C++ ----*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYTYPEDEFDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYTYPEDEFDUMPER_H
+
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+
+namespace llvm {
+namespace pdb {
+
+class LinePrinter;
+
+class TypedefDumper : public PDBSymDumper {
+public:
+ TypedefDumper(LinePrinter &P);
+
+ void start(const PDBSymbolTypeTypedef &Symbol);
+
+ void dump(const PDBSymbolTypeArray &Symbol) override;
+ void dump(const PDBSymbolTypeBuiltin &Symbol) override;
+ void dump(const PDBSymbolTypeEnum &Symbol) override;
+ void dump(const PDBSymbolTypeFunctionSig &Symbol) override;
+ void dump(const PDBSymbolTypePointer &Symbol) override;
+ void dump(const PDBSymbolTypeUDT &Symbol) override;
+
+private:
+ LinePrinter &Printer;
+};
+}
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyVariableDumper.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyVariableDumper.cpp
new file mode 100644
index 00000000000..6dd7cc384cc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyVariableDumper.cpp
@@ -0,0 +1,225 @@
+//===- PrettyVariableDumper.cpp ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyVariableDumper.h"
+
+#include "LinePrinter.h"
+#include "PrettyBuiltinDumper.h"
+#include "PrettyFunctionDumper.h"
+#include "llvm-pdbutil.h"
+
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.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/PDBTypes.h"
+
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+VariableDumper::VariableDumper(LinePrinter &P)
+ : PDBSymDumper(true), Printer(P) {}
+
+void VariableDumper::start(const PDBSymbolData &Var, uint32_t Offset) {
+ if (Var.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
+ return;
+ if (Printer.IsSymbolExcluded(Var.getName()))
+ return;
+
+ auto VarType = Var.getType();
+
+ uint64_t Length = VarType->getRawSymbol().getLength();
+
+ switch (auto LocType = Var.getLocationType()) {
+ case PDB_LocType::Static:
+ Printer.NewLine();
+ Printer << "data [";
+ WithColor(Printer, PDB_ColorItem::Address).get()
+ << format_hex(Var.getVirtualAddress(), 10);
+ Printer << ", sizeof=" << Length << "] ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "static ";
+ dumpSymbolTypeAndName(*VarType, Var.getName());
+ break;
+ case PDB_LocType::Constant:
+ if (isa<PDBSymbolTypeEnum>(*VarType))
+ break;
+ Printer.NewLine();
+ Printer << "data [sizeof=" << Length << "] ";
+ dumpSymbolTypeAndName(*VarType, Var.getName());
+ Printer << " = ";
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getValue();
+ break;
+ case PDB_LocType::ThisRel:
+ Printer.NewLine();
+ Printer << "data ";
+ WithColor(Printer, PDB_ColorItem::Offset).get()
+ << "+" << format_hex(Offset + Var.getOffset(), 4)
+ << " [sizeof=" << Length << "] ";
+ dumpSymbolTypeAndName(*VarType, Var.getName());
+ break;
+ case PDB_LocType::BitField:
+ Printer.NewLine();
+ Printer << "data ";
+ WithColor(Printer, PDB_ColorItem::Offset).get()
+ << "+" << format_hex(Offset + Var.getOffset(), 4)
+ << " [sizeof=" << Length << "] ";
+ dumpSymbolTypeAndName(*VarType, Var.getName());
+ Printer << " : ";
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getLength();
+ break;
+ default:
+ Printer.NewLine();
+ Printer << "data [sizeof=" << Length << "] ";
+ Printer << "unknown(" << LocType << ") ";
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Var.getName();
+ break;
+ }
+}
+
+void VariableDumper::startVbptr(uint32_t Offset, uint32_t Size) {
+ Printer.NewLine();
+ Printer << "vbptr ";
+
+ WithColor(Printer, PDB_ColorItem::Offset).get()
+ << "+" << format_hex(Offset, 4) << " [sizeof=" << Size << "] ";
+}
+
+void VariableDumper::start(const PDBSymbolTypeVTable &Var, uint32_t Offset) {
+ Printer.NewLine();
+ Printer << "vfptr ";
+ auto VTableType = cast<PDBSymbolTypePointer>(Var.getType());
+ uint32_t PointerSize = VTableType->getLength();
+
+ WithColor(Printer, PDB_ColorItem::Offset).get()
+ << "+" << format_hex(Offset + Var.getOffset(), 4)
+ << " [sizeof=" << PointerSize << "] ";
+}
+
+void VariableDumper::dump(const PDBSymbolTypeArray &Symbol) {
+ auto ElementType = Symbol.getElementType();
+ assert(ElementType);
+ if (!ElementType)
+ return;
+ ElementType->dump(*this);
+}
+
+void VariableDumper::dumpRight(const PDBSymbolTypeArray &Symbol) {
+ auto ElementType = Symbol.getElementType();
+ assert(ElementType);
+ if (!ElementType)
+ return;
+ Printer << '[' << Symbol.getCount() << ']';
+ ElementType->dumpRight(*this);
+}
+
+void VariableDumper::dump(const PDBSymbolTypeBuiltin &Symbol) {
+ BuiltinDumper Dumper(Printer);
+ Dumper.start(Symbol);
+}
+
+void VariableDumper::dump(const PDBSymbolTypeEnum &Symbol) {
+ WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
+}
+
+void VariableDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) {
+ auto ReturnType = Symbol.getReturnType();
+ ReturnType->dump(*this);
+ Printer << " ";
+
+ uint32_t ClassParentId = Symbol.getClassParentId();
+ auto ClassParent =
+ Symbol.getSession().getConcreteSymbolById<PDBSymbolTypeUDT>(
+ ClassParentId);
+
+ if (ClassParent) {
+ WithColor(Printer, PDB_ColorItem::Identifier).get()
+ << ClassParent->getName();
+ Printer << "::";
+ }
+}
+
+void VariableDumper::dumpRight(const PDBSymbolTypeFunctionSig &Symbol) {
+ Printer << "(";
+ if (auto Arguments = Symbol.getArguments()) {
+ uint32_t Index = 0;
+ while (auto Arg = Arguments->getNext()) {
+ Arg->dump(*this);
+ if (++Index < Arguments->getChildCount())
+ Printer << ", ";
+ }
+ }
+ Printer << ")";
+
+ if (Symbol.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " const";
+ if (Symbol.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile";
+
+ if (Symbol.getRawSymbol().isRestrictedType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " __restrict";
+}
+
+void VariableDumper::dump(const PDBSymbolTypePointer &Symbol) {
+ auto PointeeType = Symbol.getPointeeType();
+ if (!PointeeType)
+ return;
+ PointeeType->dump(*this);
+ if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) {
+ // A hack to get the calling convention in the right spot.
+ Printer << " (";
+ PDB_CallingConv CC = FuncSig->getCallingConvention();
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " ";
+ } else if (isa<PDBSymbolTypeArray>(PointeeType)) {
+ Printer << " (";
+ }
+ Printer << (Symbol.isReference() ? "&" : "*");
+ if (Symbol.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " const ";
+ if (Symbol.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile ";
+
+ if (Symbol.getRawSymbol().isRestrictedType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " __restrict ";
+}
+
+void VariableDumper::dumpRight(const PDBSymbolTypePointer &Symbol) {
+ auto PointeeType = Symbol.getPointeeType();
+ assert(PointeeType);
+ if (!PointeeType)
+ return;
+ if (isa<PDBSymbolTypeFunctionSig>(PointeeType) ||
+ isa<PDBSymbolTypeArray>(PointeeType)) {
+ Printer << ")";
+ }
+ PointeeType->dumpRight(*this);
+}
+
+void VariableDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "typedef ";
+ WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
+}
+
+void VariableDumper::dump(const PDBSymbolTypeUDT &Symbol) {
+ WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
+}
+
+void VariableDumper::dumpSymbolTypeAndName(const PDBSymbol &Type,
+ StringRef Name) {
+ Type.dump(*this);
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << " " << Name;
+ Type.dumpRight(*this);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyVariableDumper.h b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyVariableDumper.h
new file mode 100644
index 00000000000..65cf5cd2cf5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/PrettyVariableDumper.h
@@ -0,0 +1,49 @@
+//===- PrettyVariableDumper.h - PDBSymDumper variable dumper ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYVARIABLEDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYVARIABLEDUMPER_H
+
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+
+namespace llvm {
+
+class StringRef;
+
+namespace pdb {
+
+class LinePrinter;
+
+class VariableDumper : public PDBSymDumper {
+public:
+ VariableDumper(LinePrinter &P);
+
+ void start(const PDBSymbolData &Var, uint32_t Offset = 0);
+ void start(const PDBSymbolTypeVTable &Var, uint32_t Offset = 0);
+ void startVbptr(uint32_t Offset, uint32_t Size);
+
+ void dump(const PDBSymbolTypeArray &Symbol) override;
+ void dump(const PDBSymbolTypeBuiltin &Symbol) override;
+ void dump(const PDBSymbolTypeEnum &Symbol) override;
+ void dump(const PDBSymbolTypeFunctionSig &Symbol) override;
+ void dump(const PDBSymbolTypePointer &Symbol) override;
+ void dump(const PDBSymbolTypeTypedef &Symbol) override;
+ void dump(const PDBSymbolTypeUDT &Symbol) override;
+
+ void dumpRight(const PDBSymbolTypeArray &Symbol) override;
+ void dumpRight(const PDBSymbolTypeFunctionSig &Symbol) override;
+ void dumpRight(const PDBSymbolTypePointer &Symbol) override;
+
+private:
+ void dumpSymbolTypeAndName(const PDBSymbol &Type, StringRef Name);
+
+ LinePrinter &Printer;
+};
+}
+}
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/StreamUtil.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/StreamUtil.cpp
new file mode 100644
index 00000000000..d0d0a9fbe92
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/StreamUtil.cpp
@@ -0,0 +1,194 @@
+//===- StreamUtil.cpp - PDB stream utilities --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "StreamUtil.h"
+#include "FormatUtil.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
+#include "llvm/DebugInfo/PDB/Native/DbiModuleList.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+std::string StreamInfo::getLongName() const {
+ if (Purpose == StreamPurpose::NamedStream)
+ return formatv("Named Stream \"{0}\"", Name).str();
+ if (Purpose == StreamPurpose::ModuleStream)
+ return formatv("Module \"{0}\"", Name).str();
+ return Name;
+}
+
+StreamInfo StreamInfo::createStream(StreamPurpose Purpose, StringRef Name,
+ uint32_t StreamIndex) {
+ StreamInfo Result;
+ Result.Name = std::string(Name);
+ Result.StreamIndex = StreamIndex;
+ Result.Purpose = Purpose;
+ return Result;
+}
+
+StreamInfo StreamInfo::createModuleStream(StringRef Module,
+ uint32_t StreamIndex, uint32_t Modi) {
+ StreamInfo Result;
+ Result.Name = std::string(Module);
+ Result.StreamIndex = StreamIndex;
+ Result.ModuleIndex = Modi;
+ Result.Purpose = StreamPurpose::ModuleStream;
+ return Result;
+}
+
+static inline StreamInfo stream(StreamPurpose Purpose, StringRef Label,
+ uint32_t Idx) {
+ return StreamInfo::createStream(Purpose, Label, Idx);
+}
+
+static inline StreamInfo moduleStream(StringRef Label, uint32_t StreamIdx,
+ uint32_t Modi) {
+ return StreamInfo::createModuleStream(Label, StreamIdx, Modi);
+}
+
+struct IndexedModuleDescriptor {
+ uint32_t Modi;
+ DbiModuleDescriptor Descriptor;
+};
+
+void llvm::pdb::discoverStreamPurposes(PDBFile &File,
+ SmallVectorImpl<StreamInfo> &Streams) {
+ // It's OK if we fail to load some of these streams, we still attempt to print
+ // what we can.
+ auto Dbi = File.getPDBDbiStream();
+ auto Tpi = File.getPDBTpiStream();
+ auto Ipi = File.getPDBIpiStream();
+ auto Info = File.getPDBInfoStream();
+
+ uint32_t StreamCount = File.getNumStreams();
+ DenseMap<uint16_t, IndexedModuleDescriptor> ModStreams;
+ DenseMap<uint16_t, std::string> NamedStreams;
+
+ if (Dbi) {
+ const DbiModuleList &Modules = Dbi->modules();
+ for (uint32_t I = 0; I < Modules.getModuleCount(); ++I) {
+ IndexedModuleDescriptor IMD;
+ IMD.Modi = I;
+ IMD.Descriptor = Modules.getModuleDescriptor(I);
+ uint16_t SN = IMD.Descriptor.getModuleStreamIndex();
+ if (SN != kInvalidStreamIndex)
+ ModStreams[SN] = IMD;
+ }
+ }
+ if (Info) {
+ for (auto &NSE : Info->named_streams()) {
+ if (NSE.second != kInvalidStreamIndex)
+ NamedStreams[NSE.second] = std::string(NSE.first());
+ }
+ }
+
+ Streams.resize(StreamCount);
+ for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
+ if (StreamIdx == OldMSFDirectory)
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Old MSF Directory", StreamIdx);
+ else if (StreamIdx == StreamPDB)
+ Streams[StreamIdx] = stream(StreamPurpose::PDB, "PDB Stream", StreamIdx);
+ else if (StreamIdx == StreamDBI)
+ Streams[StreamIdx] = stream(StreamPurpose::DBI, "DBI Stream", StreamIdx);
+ else if (StreamIdx == StreamTPI)
+ Streams[StreamIdx] = stream(StreamPurpose::TPI, "TPI Stream", StreamIdx);
+ else if (StreamIdx == StreamIPI)
+ Streams[StreamIdx] = stream(StreamPurpose::IPI, "IPI Stream", StreamIdx);
+ else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex())
+ Streams[StreamIdx] =
+ stream(StreamPurpose::GlobalHash, "Global Symbol Hash", StreamIdx);
+ else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex())
+ Streams[StreamIdx] =
+ stream(StreamPurpose::PublicHash, "Public Symbol Hash", StreamIdx);
+ else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex())
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Symbols, "Symbol Records", StreamIdx);
+ else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex())
+ Streams[StreamIdx] =
+ stream(StreamPurpose::TpiHash, "TPI Hash", StreamIdx);
+ else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex())
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "TPI Aux Hash", StreamIdx);
+ else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex())
+ Streams[StreamIdx] =
+ stream(StreamPurpose::IpiHash, "IPI Hash", StreamIdx);
+ else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex())
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "IPI Aux Hash", StreamIdx);
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception))
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Exception Data", StreamIdx);
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup))
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Fixup Data", StreamIdx);
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO))
+ Streams[StreamIdx] = stream(StreamPurpose::Other, "FPO Data", StreamIdx);
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO))
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "New FPO Data", StreamIdx);
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc))
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Omap From Source Data", StreamIdx);
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc))
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Omap To Source Data", StreamIdx);
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata))
+ Streams[StreamIdx] = stream(StreamPurpose::Other, "Pdata", StreamIdx);
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr))
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Section Header Data", StreamIdx);
+ else if (Dbi &&
+ StreamIdx ==
+ Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig))
+ Streams[StreamIdx] = stream(StreamPurpose::Other,
+ "Section Header Original Data", StreamIdx);
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap))
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Token Rid Data", StreamIdx);
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata))
+ Streams[StreamIdx] = stream(StreamPurpose::Other, "Xdata", StreamIdx);
+ else {
+ auto ModIter = ModStreams.find(StreamIdx);
+ auto NSIter = NamedStreams.find(StreamIdx);
+ if (ModIter != ModStreams.end()) {
+ Streams[StreamIdx] =
+ moduleStream(ModIter->second.Descriptor.getModuleName(), StreamIdx,
+ ModIter->second.Modi);
+ } else if (NSIter != NamedStreams.end()) {
+ Streams[StreamIdx] =
+ stream(StreamPurpose::NamedStream, NSIter->second, StreamIdx);
+ } else {
+ Streams[StreamIdx] = stream(StreamPurpose::Other, "???", StreamIdx);
+ }
+ }
+ }
+
+ // Consume errors from missing streams.
+ if (!Dbi)
+ consumeError(Dbi.takeError());
+ if (!Tpi)
+ consumeError(Tpi.takeError());
+ if (!Ipi)
+ consumeError(Ipi.takeError());
+ if (!Info)
+ consumeError(Info.takeError());
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/StreamUtil.h b/contrib/libs/llvm14/tools/llvm-pdbutil/StreamUtil.h
new file mode 100644
index 00000000000..f810f7dc15b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/StreamUtil.h
@@ -0,0 +1,63 @@
+//===- Streamutil.h - PDB stream utilities ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_STREAMUTIL_H
+#define LLVM_TOOLS_LLVMPDBDUMP_STREAMUTIL_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <string>
+
+namespace llvm {
+namespace pdb {
+class PDBFile;
+enum class StreamPurpose {
+ NamedStream,
+ ModuleStream,
+ Symbols,
+ PDB,
+ DBI,
+ TPI,
+ IPI,
+ GlobalHash,
+ PublicHash,
+ TpiHash,
+ IpiHash,
+ Other
+};
+
+struct StreamInfo {
+public:
+ StreamInfo() {}
+
+ uint32_t getModuleIndex() const { return *ModuleIndex; }
+ StreamPurpose getPurpose() const { return Purpose; }
+ StringRef getShortName() const { return Name; }
+ uint32_t getStreamIndex() const { return StreamIndex; }
+ std::string getLongName() const;
+
+ static StreamInfo createStream(StreamPurpose Purpose, StringRef Name,
+ uint32_t StreamIndex);
+ static StreamInfo createModuleStream(StringRef Module, uint32_t StreamIndex,
+ uint32_t Modi);
+
+private:
+ StreamPurpose Purpose;
+ uint32_t StreamIndex;
+ std::string Name;
+ Optional<uint32_t> ModuleIndex;
+};
+
+void discoverStreamPurposes(PDBFile &File,
+ SmallVectorImpl<StreamInfo> &Streams);
+}
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/TypeReferenceTracker.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/TypeReferenceTracker.cpp
new file mode 100644
index 00000000000..f184f02e01e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/TypeReferenceTracker.cpp
@@ -0,0 +1,160 @@
+//===- TypeReferenceTracker.cpp ------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypeReferenceTracker.h"
+
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
+#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+using namespace llvm::codeview;
+
+// LazyRandomTypeCollection doesn't appear to expose the number of records, so
+// just iterate up front to find out.
+static uint32_t getNumRecordsInCollection(LazyRandomTypeCollection &Types) {
+ uint32_t NumTypes = 0;
+ for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI))
+ ++NumTypes;
+ return NumTypes;
+}
+
+TypeReferenceTracker::TypeReferenceTracker(InputFile &File)
+ : File(File), Types(File.types()),
+ Ids(File.isPdb() ? &File.ids() : nullptr) {
+ NumTypeRecords = getNumRecordsInCollection(Types);
+ TypeReferenced.resize(NumTypeRecords, false);
+
+ // If this is a PDB, ids are stored separately, so make a separate bit vector.
+ if (Ids) {
+ NumIdRecords = getNumRecordsInCollection(*Ids);
+ IdReferenced.resize(NumIdRecords, false);
+ }
+
+ // Get the TpiStream pointer for forward decl resolution if this is a pdb.
+ // Build the hash map to enable resolving forward decls.
+ if (File.isPdb()) {
+ Tpi = &cantFail(File.pdb().getPDBTpiStream());
+ Tpi->buildHashMap();
+ }
+}
+
+void TypeReferenceTracker::mark() {
+ // Walk type roots:
+ // - globals
+ // - modi symbols
+ // - LF_UDT_MOD_SRC_LINE? VC always links these in.
+ for (SymbolGroup SG : File.symbol_groups()) {
+ if (File.isObj()) {
+ for (const auto &SS : SG.getDebugSubsections()) {
+ // FIXME: Are there other type-referencing subsections? Inlinees?
+ // Probably for IDs.
+ if (SS.kind() != DebugSubsectionKind::Symbols)
+ continue;
+
+ CVSymbolArray Symbols;
+ BinaryStreamReader Reader(SS.getRecordData());
+ cantFail(Reader.readArray(Symbols, Reader.getLength()));
+ for (const CVSymbol &S : Symbols)
+ addTypeRefsFromSymbol(S);
+ }
+ } else if (SG.hasDebugStream()) {
+ for (const CVSymbol &S : SG.getPdbModuleStream().getSymbolArray())
+ addTypeRefsFromSymbol(S);
+ }
+ }
+
+ // Walk globals and mark types referenced from globals.
+ if (File.isPdb() && File.pdb().hasPDBGlobalsStream()) {
+ SymbolStream &SymStream = cantFail(File.pdb().getPDBSymbolStream());
+ GlobalsStream &GS = cantFail(File.pdb().getPDBGlobalsStream());
+ for (uint32_t PubSymOff : GS.getGlobalsTable()) {
+ CVSymbol Sym = SymStream.readRecord(PubSymOff);
+ addTypeRefsFromSymbol(Sym);
+ }
+ }
+
+ // FIXME: Should we walk Ids?
+}
+
+void TypeReferenceTracker::addOneTypeRef(TiRefKind RefKind, TypeIndex RefTI) {
+ // If it's simple or already seen, no need to add to work list.
+ BitVector &TypeOrIdReferenced =
+ (Ids && RefKind == TiRefKind::IndexRef) ? IdReferenced : TypeReferenced;
+ if (RefTI.isSimple() || TypeOrIdReferenced.test(RefTI.toArrayIndex()))
+ return;
+
+ // Otherwise, mark it seen and add it to the work list.
+ TypeOrIdReferenced.set(RefTI.toArrayIndex());
+ RefWorklist.push_back({RefKind, RefTI});
+}
+
+void TypeReferenceTracker::addTypeRefsFromSymbol(const CVSymbol &Sym) {
+ SmallVector<TiReference, 4> DepList;
+ // FIXME: Check for failure.
+ discoverTypeIndicesInSymbol(Sym, DepList);
+ addReferencedTypes(Sym.content(), DepList);
+ markReferencedTypes();
+}
+
+void TypeReferenceTracker::addReferencedTypes(ArrayRef<uint8_t> RecData,
+ ArrayRef<TiReference> DepList) {
+ for (const auto &Ref : DepList) {
+ // FIXME: Report OOB slice instead of truncating.
+ ArrayRef<uint8_t> ByteSlice =
+ RecData.drop_front(Ref.Offset).take_front(4 * Ref.Count);
+ ArrayRef<TypeIndex> TIs(
+ reinterpret_cast<const TypeIndex *>(ByteSlice.data()),
+ ByteSlice.size() / 4);
+
+ // If this is a PDB and this is an item reference, track it in the IPI
+ // bitvector. Otherwise, it's a type ref, or there is only one stream.
+ for (TypeIndex RefTI : TIs)
+ addOneTypeRef(Ref.Kind, RefTI);
+ }
+}
+
+void TypeReferenceTracker::markReferencedTypes() {
+ while (!RefWorklist.empty()) {
+ TiRefKind RefKind;
+ TypeIndex RefTI;
+ std::tie(RefKind, RefTI) = RefWorklist.pop_back_val();
+ Optional<CVType> Rec = (Ids && RefKind == TiRefKind::IndexRef)
+ ? Ids->tryGetType(RefTI)
+ : Types.tryGetType(RefTI);
+ if (!Rec)
+ continue; // FIXME: Report a reference to a non-existant type.
+
+ SmallVector<TiReference, 4> DepList;
+ // FIXME: Check for failure.
+ discoverTypeIndices(*Rec, DepList);
+ addReferencedTypes(Rec->content(), DepList);
+
+ // If this is a tag kind and this is a PDB input, mark the complete type as
+ // referenced.
+ // FIXME: This limitation makes this feature somewhat useless on object file
+ // inputs.
+ if (Tpi) {
+ switch (Rec->kind()) {
+ default:
+ break;
+ case LF_CLASS:
+ case LF_INTERFACE:
+ case LF_STRUCTURE:
+ case LF_UNION:
+ case LF_ENUM:
+ addOneTypeRef(TiRefKind::TypeRef,
+ cantFail(Tpi->findFullDeclForForwardRef(RefTI)));
+ break;
+ }
+ }
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/TypeReferenceTracker.h b/contrib/libs/llvm14/tools/llvm-pdbutil/TypeReferenceTracker.h
new file mode 100644
index 00000000000..8861731ab6e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/TypeReferenceTracker.h
@@ -0,0 +1,69 @@
+//===- TypeReferenceTracker.h --------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_TYPEREFERENCETRACKER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_TYPEREFERENCETRACKER_H
+
+#include "InputFile.h"
+
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/DebugInfo/CodeView/CVRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace pdb {
+
+class TpiStream;
+
+/// Maintains bitvector to track whether a type was referenced by a symbol
+/// record.
+class TypeReferenceTracker {
+public:
+ TypeReferenceTracker(InputFile &File);
+
+ // Do the work of marking referenced types.
+ void mark();
+
+ // Return true if a symbol record transitively references this type.
+ bool isTypeReferenced(codeview::TypeIndex TI) {
+ return TI.toArrayIndex() <= NumTypeRecords &&
+ TypeReferenced.test(TI.toArrayIndex());
+ }
+
+private:
+ void addTypeRefsFromSymbol(const codeview::CVSymbol &Sym);
+
+ // Mark types on this list as referenced.
+ void addReferencedTypes(ArrayRef<uint8_t> RecData,
+ ArrayRef<codeview::TiReference> Refs);
+
+ // Consume all types on the worklist.
+ void markReferencedTypes();
+
+ void addOneTypeRef(codeview::TiRefKind RefKind, codeview::TypeIndex RefTI);
+
+ InputFile &File;
+ codeview::LazyRandomTypeCollection &Types;
+ codeview::LazyRandomTypeCollection *Ids = nullptr;
+ TpiStream *Tpi = nullptr;
+ BitVector TypeReferenced;
+ BitVector IdReferenced;
+ SmallVector<std::pair<codeview::TiRefKind, codeview::TypeIndex>, 10>
+ RefWorklist;
+ uint32_t NumTypeRecords = 0;
+ uint32_t NumIdRecords = 0;
+};
+
+} // namespace pdb
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVMPDBDUMP_TYPEREFERENCETRACKER_H
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/YAMLOutputStyle.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/YAMLOutputStyle.cpp
new file mode 100644
index 00000000000..80b76657fac
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/YAMLOutputStyle.cpp
@@ -0,0 +1,370 @@
+//===- YAMLOutputStyle.cpp ------------------------------------ *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "YAMLOutputStyle.h"
+
+#include "PdbYaml.h"
+#include "llvm-pdbutil.h"
+
+#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"
+#include "llvm/DebugInfo/CodeView/DebugSubsection.h"
+#include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h"
+#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;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static bool checkModuleSubsection(opts::ModuleSubsection MS) {
+ return any_of(opts::pdb2yaml::DumpModuleSubsections,
+ [=](opts::ModuleSubsection M) {
+ return M == MS || M == opts::ModuleSubsection::All;
+ });
+}
+
+YAMLOutputStyle::YAMLOutputStyle(PDBFile &File)
+ : File(File), Out(outs()), Obj(File.getAllocator()) {
+ Out.setWriteDefaultValues(!opts::pdb2yaml::Minimal);
+}
+
+Error YAMLOutputStyle::dump() {
+ if (opts::pdb2yaml::StreamDirectory)
+ opts::pdb2yaml::StreamMetadata = true;
+
+ if (auto EC = dumpFileHeaders())
+ return EC;
+
+ if (auto EC = dumpStreamMetadata())
+ return EC;
+
+ if (auto EC = dumpStreamDirectory())
+ return EC;
+
+ if (auto EC = dumpStringTable())
+ return EC;
+
+ if (auto EC = dumpPDBStream())
+ return EC;
+
+ if (auto EC = dumpDbiStream())
+ return EC;
+
+ if (auto EC = dumpTpiStream())
+ return EC;
+
+ if (auto EC = dumpIpiStream())
+ return EC;
+
+ if (auto EC = dumpPublics())
+ return EC;
+
+ flush();
+ return Error::success();
+}
+
+
+Error YAMLOutputStyle::dumpFileHeaders() {
+ if (opts::pdb2yaml::NoFileHeaders)
+ return Error::success();
+
+ yaml::MSFHeaders Headers;
+ Obj.Headers.emplace();
+ Obj.Headers->SuperBlock.NumBlocks = File.getBlockCount();
+ Obj.Headers->SuperBlock.BlockMapAddr = File.getBlockMapIndex();
+ Obj.Headers->SuperBlock.BlockSize = File.getBlockSize();
+ auto Blocks = File.getDirectoryBlockArray();
+ Obj.Headers->DirectoryBlocks.assign(Blocks.begin(), Blocks.end());
+ Obj.Headers->NumDirectoryBlocks = File.getNumDirectoryBlocks();
+ Obj.Headers->SuperBlock.NumDirectoryBytes = File.getNumDirectoryBytes();
+ Obj.Headers->NumStreams =
+ opts::pdb2yaml::StreamMetadata ? File.getNumStreams() : 0;
+ Obj.Headers->SuperBlock.FreeBlockMapBlock = File.getFreeBlockMapBlock();
+ Obj.Headers->SuperBlock.Unknown1 = File.getUnknown1();
+ Obj.Headers->FileSize = File.getFileSize();
+
+ return Error::success();
+}
+
+Error YAMLOutputStyle::dumpStringTable() {
+ bool RequiresStringTable = opts::pdb2yaml::DumpModuleFiles ||
+ !opts::pdb2yaml::DumpModuleSubsections.empty();
+ bool RequestedStringTable = opts::pdb2yaml::StringTable;
+ if (!RequiresStringTable && !RequestedStringTable)
+ return Error::success();
+
+ auto ExpectedST = File.getStringTable();
+ if (!ExpectedST)
+ return ExpectedST.takeError();
+
+ Obj.StringTable.emplace();
+ const auto &ST = ExpectedST.get();
+ for (auto ID : ST.name_ids()) {
+ auto S = ST.getStringForID(ID);
+ if (!S)
+ return S.takeError();
+ if (S->empty())
+ continue;
+ Obj.StringTable->push_back(*S);
+ }
+ return Error::success();
+}
+
+Error YAMLOutputStyle::dumpStreamMetadata() {
+ if (!opts::pdb2yaml::StreamMetadata)
+ return Error::success();
+
+ Obj.StreamSizes.emplace();
+ Obj.StreamSizes->assign(File.getStreamSizes().begin(),
+ File.getStreamSizes().end());
+ return Error::success();
+}
+
+Error YAMLOutputStyle::dumpStreamDirectory() {
+ if (!opts::pdb2yaml::StreamDirectory)
+ return Error::success();
+
+ auto StreamMap = File.getStreamMap();
+ Obj.StreamMap.emplace();
+ for (auto &Stream : StreamMap) {
+ pdb::yaml::StreamBlockList BlockList;
+ BlockList.Blocks.assign(Stream.begin(), Stream.end());
+ Obj.StreamMap->push_back(BlockList);
+ }
+
+ return Error::success();
+}
+
+Error YAMLOutputStyle::dumpPDBStream() {
+ if (!opts::pdb2yaml::PdbStream)
+ return Error::success();
+
+ auto IS = File.getPDBInfoStream();
+ if (!IS)
+ return IS.takeError();
+
+ auto &InfoS = IS.get();
+ Obj.PdbStream.emplace();
+ Obj.PdbStream->Age = InfoS.getAge();
+ Obj.PdbStream->Guid = InfoS.getGuid();
+ Obj.PdbStream->Signature = InfoS.getSignature();
+ Obj.PdbStream->Version = InfoS.getVersion();
+ Obj.PdbStream->Features = InfoS.getFeatureSignatures();
+
+ return Error::success();
+}
+
+static opts::ModuleSubsection convertSubsectionKind(DebugSubsectionKind K) {
+ switch (K) {
+ case DebugSubsectionKind::CrossScopeExports:
+ return opts::ModuleSubsection::CrossScopeExports;
+ case DebugSubsectionKind::CrossScopeImports:
+ return opts::ModuleSubsection::CrossScopeImports;
+ case DebugSubsectionKind::FileChecksums:
+ return opts::ModuleSubsection::FileChecksums;
+ case DebugSubsectionKind::InlineeLines:
+ return opts::ModuleSubsection::InlineeLines;
+ case DebugSubsectionKind::Lines:
+ return opts::ModuleSubsection::Lines;
+ case DebugSubsectionKind::Symbols:
+ return opts::ModuleSubsection::Symbols;
+ case DebugSubsectionKind::StringTable:
+ return opts::ModuleSubsection::StringTable;
+ case DebugSubsectionKind::FrameData:
+ return opts::ModuleSubsection::FrameData;
+ default:
+ return opts::ModuleSubsection::Unknown;
+ }
+ llvm_unreachable("Unreachable!");
+}
+
+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();
+
+ auto &DS = DbiS.get();
+ Obj.DbiStream.emplace();
+ Obj.DbiStream->Age = DS.getAge();
+ Obj.DbiStream->BuildNumber = DS.getBuildNumber();
+ Obj.DbiStream->Flags = DS.getFlags();
+ Obj.DbiStream->MachineType = DS.getMachineType();
+ Obj.DbiStream->PdbDllRbld = DS.getPdbDllRbld();
+ Obj.DbiStream->PdbDllVersion = DS.getPdbDllVersion();
+ Obj.DbiStream->VerHeader = DS.getDbiVersion();
+ if (opts::pdb2yaml::DumpModules) {
+ const auto &Modules = DS.modules();
+ for (uint32_t I = 0; I < Modules.getModuleCount(); ++I) {
+ DbiModuleDescriptor MI = Modules.getModuleDescriptor(I);
+
+ Obj.DbiStream->ModInfos.emplace_back();
+ yaml::PdbDbiModuleInfo &DMI = Obj.DbiStream->ModInfos.back();
+
+ DMI.Mod = MI.getModuleName();
+ DMI.Obj = MI.getObjFileName();
+ if (opts::pdb2yaml::DumpModuleFiles) {
+ auto Files = Modules.source_files(I);
+ DMI.SourceFiles.assign(Files.begin(), Files.end());
+ }
+
+ uint16_t ModiStream = MI.getModuleStreamIndex();
+ if (ModiStream == kInvalidStreamIndex)
+ continue;
+
+ auto ModStreamData = File.createIndexedStream(ModiStream);
+ pdb::ModuleDebugStreamRef ModS(MI, std::move(ModStreamData));
+ if (auto EC = ModS.reload())
+ return EC;
+
+ auto ExpectedST = File.getStringTable();
+ if (!ExpectedST)
+ return ExpectedST.takeError();
+ if (!opts::pdb2yaml::DumpModuleSubsections.empty() &&
+ ModS.hasDebugSubsections()) {
+ auto ExpectedChecksums = ModS.findChecksumsSubsection();
+ if (!ExpectedChecksums)
+ return ExpectedChecksums.takeError();
+
+ StringsAndChecksumsRef SC(ExpectedST->getStringTable(),
+ *ExpectedChecksums);
+
+ for (const auto &SS : ModS.subsections()) {
+ opts::ModuleSubsection OptionKind = convertSubsectionKind(SS.kind());
+ if (!checkModuleSubsection(OptionKind))
+ continue;
+
+ auto Converted =
+ CodeViewYAML::YAMLDebugSubsection::fromCodeViewSubection(SC, SS);
+ if (!Converted)
+ return Converted.takeError();
+ DMI.Subsections.push_back(*Converted);
+ }
+ }
+
+ if (opts::pdb2yaml::DumpModuleSyms) {
+ DMI.Modi.emplace();
+
+ DMI.Modi->Signature = ModS.signature();
+ bool HadError = false;
+ for (auto &Sym : ModS.symbols(&HadError)) {
+ auto ES = CodeViewYAML::SymbolRecord::fromCodeViewSymbol(Sym);
+ if (!ES)
+ return ES.takeError();
+
+ DMI.Modi->Symbols.push_back(*ES);
+ }
+ }
+ }
+ }
+ return Error::success();
+}
+
+Error YAMLOutputStyle::dumpTpiStream() {
+ if (!opts::pdb2yaml::TpiStream)
+ return Error::success();
+
+ auto TpiS = File.getPDBTpiStream();
+ if (!TpiS)
+ return TpiS.takeError();
+
+ auto &TS = TpiS.get();
+ Obj.TpiStream.emplace();
+ Obj.TpiStream->Version = TS.getTpiVersion();
+ for (auto &Record : TS.types(nullptr)) {
+ auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Record);
+ if (!ExpectedRecord)
+ return ExpectedRecord.takeError();
+ Obj.TpiStream->Records.push_back(*ExpectedRecord);
+ }
+
+ return Error::success();
+}
+
+Error YAMLOutputStyle::dumpIpiStream() {
+ if (!opts::pdb2yaml::IpiStream)
+ return Error::success();
+
+ auto InfoS = File.getPDBInfoStream();
+ if (!InfoS)
+ return InfoS.takeError();
+ if (!InfoS->containsIdStream())
+ return Error::success();
+
+ auto IpiS = File.getPDBIpiStream();
+ if (!IpiS)
+ return IpiS.takeError();
+
+ auto &IS = IpiS.get();
+ Obj.IpiStream.emplace();
+ Obj.IpiStream->Version = IS.getTpiVersion();
+ for (auto &Record : IS.types(nullptr)) {
+ auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Record);
+ if (!ExpectedRecord)
+ return ExpectedRecord.takeError();
+
+ Obj.IpiStream->Records.push_back(*ExpectedRecord);
+ }
+
+ 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/contrib/libs/llvm14/tools/llvm-pdbutil/YAMLOutputStyle.h b/contrib/libs/llvm14/tools/llvm-pdbutil/YAMLOutputStyle.h
new file mode 100644
index 00000000000..5d53e0b65d0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/YAMLOutputStyle.h
@@ -0,0 +1,48 @@
+//===- YAMLOutputStyle.h -------------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_YAMLOUTPUTSTYLE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_YAMLOUTPUTSTYLE_H
+
+#include "OutputStyle.h"
+#include "PdbYaml.h"
+
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/YAMLTraits.h"
+
+namespace llvm {
+namespace pdb {
+
+class YAMLOutputStyle : public OutputStyle {
+public:
+ YAMLOutputStyle(PDBFile &File);
+
+ Error dump() override;
+
+private:
+ Error dumpStringTable();
+ Error dumpFileHeaders();
+ Error dumpStreamMetadata();
+ Error dumpStreamDirectory();
+ Error dumpPDBStream();
+ Error dumpDbiStream();
+ Error dumpTpiStream();
+ Error dumpIpiStream();
+ Error dumpPublics();
+
+ void flush();
+
+ PDBFile &File;
+ llvm::yaml::Output Out;
+
+ yaml::PdbObject Obj;
+};
+} // namespace pdb
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVMPDBDUMP_YAMLOUTPUTSTYLE_H
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/llvm-pdbutil.cpp b/contrib/libs/llvm14/tools/llvm-pdbutil/llvm-pdbutil.cpp
new file mode 100644
index 00000000000..b152ebd6dcc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/llvm-pdbutil.cpp
@@ -0,0 +1,1576 @@
+//===- llvm-pdbutil.cpp - Dump debug info from a PDB file -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Dumps debug information present in PDB files.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-pdbutil.h"
+
+#include "BytesOutputStyle.h"
+#include "DumpOutputStyle.h"
+#include "ExplainOutputStyle.h"
+#include "InputFile.h"
+#include "LinePrinter.h"
+#include "OutputStyle.h"
+#include "PrettyClassDefinitionDumper.h"
+#include "PrettyCompilandDumper.h"
+#include "PrettyEnumDumper.h"
+#include "PrettyExternalSymbolDumper.h"
+#include "PrettyFunctionDumper.h"
+#include "PrettyTypeDumper.h"
+#include "PrettyTypedefDumper.h"
+#include "PrettyVariableDumper.h"
+#include "YAMLOutputStyle.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Config/config.h"
+#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h"
+#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"
+#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h"
+#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
+#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h"
+#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
+#include "llvm/DebugInfo/MSF/MSFBuilder.h"
+#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
+#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h"
+#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h"
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
+#include "llvm/DebugInfo/PDB/Native/RawError.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/PDB.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#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"
+#include "llvm/Support/COM.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::msf;
+using namespace llvm::pdb;
+
+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");
+
+cl::SubCommand
+ YamlToPdbSubcommand("yaml2pdb",
+ "Generate a PDB file from a YAML description");
+cl::SubCommand
+ PdbToYamlSubcommand("pdb2yaml",
+ "Generate a detailed YAML description of a PDB File");
+
+cl::SubCommand MergeSubcommand("merge",
+ "Merge multiple PDBs into a single PDB");
+
+cl::SubCommand ExplainSubcommand("explain",
+ "Explain the meaning of a file offset");
+
+cl::SubCommand ExportSubcommand("export",
+ "Write binary data from a stream to a file");
+
+cl::OptionCategory TypeCategory("Symbol Type Options");
+cl::OptionCategory FilterCategory("Filtering and Sorting Options");
+cl::OptionCategory OtherOptions("Other Options");
+
+cl::ValuesClass ChunkValues = cl::values(
+ clEnumValN(ModuleSubsection::CrossScopeExports, "cme",
+ "Cross module exports (DEBUG_S_CROSSSCOPEEXPORTS subsection)"),
+ clEnumValN(ModuleSubsection::CrossScopeImports, "cmi",
+ "Cross module imports (DEBUG_S_CROSSSCOPEIMPORTS subsection)"),
+ clEnumValN(ModuleSubsection::FileChecksums, "fc",
+ "File checksums (DEBUG_S_CHECKSUMS subsection)"),
+ clEnumValN(ModuleSubsection::InlineeLines, "ilines",
+ "Inlinee lines (DEBUG_S_INLINEELINES subsection)"),
+ clEnumValN(ModuleSubsection::Lines, "lines",
+ "Lines (DEBUG_S_LINES subsection)"),
+ clEnumValN(ModuleSubsection::StringTable, "strings",
+ "String Table (DEBUG_S_STRINGTABLE subsection) (not "
+ "typically present in PDB file)"),
+ clEnumValN(ModuleSubsection::FrameData, "frames",
+ "Frame Data (DEBUG_S_FRAMEDATA subsection)"),
+ clEnumValN(ModuleSubsection::Symbols, "symbols",
+ "Symbols (DEBUG_S_SYMBOLS subsection) (not typically "
+ "present in PDB file)"),
+ clEnumValN(ModuleSubsection::CoffSymbolRVAs, "rvas",
+ "COFF Symbol RVAs (DEBUG_S_COFF_SYMBOL_RVA subsection)"),
+ clEnumValN(ModuleSubsection::Unknown, "unknown",
+ "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>"),
+ cl::OneOrMore, cl::sub(PrettySubcommand));
+
+cl::opt<bool> InjectedSources("injected-sources",
+ cl::desc("Display injected sources"),
+ cl::cat(OtherOptions), cl::sub(PrettySubcommand));
+cl::opt<bool> ShowInjectedSourceContent(
+ "injected-source-content",
+ cl::desc("When displaying an injected source, display the file content"),
+ cl::cat(OtherOptions), cl::sub(PrettySubcommand));
+
+cl::list<std::string> WithName(
+ "with-name",
+ cl::desc("Display any symbol or type with the specified exact name"),
+ cl::cat(TypeCategory), cl::ZeroOrMore, cl::sub(PrettySubcommand));
+
+cl::opt<bool> Compilands("compilands", cl::desc("Display compilands"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> Symbols("module-syms",
+ cl::desc("Display symbols for each compiland"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> Globals("globals", cl::desc("Dump global symbols"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> Externals("externals", cl::desc("Dump external symbols"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::list<SymLevel> SymTypes(
+ "sym-types", cl::desc("Type of symbols to dump (default all)"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand), cl::ZeroOrMore,
+ cl::values(
+ clEnumValN(SymLevel::Thunks, "thunks", "Display thunk symbols"),
+ clEnumValN(SymLevel::Data, "data", "Display data symbols"),
+ clEnumValN(SymLevel::Functions, "funcs", "Display function symbols"),
+ clEnumValN(SymLevel::All, "all", "Display all symbols (default)")));
+
+cl::opt<bool>
+ Types("types",
+ cl::desc("Display all types (implies -classes, -enums, -typedefs)"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> Classes("classes", cl::desc("Display class types"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+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),
+ cl::values(clEnumValN(SymbolSortMode::None, "none",
+ "Undefined / no particular sort order"),
+ clEnumValN(SymbolSortMode::Name, "name", "Sort symbols by name"),
+ clEnumValN(SymbolSortMode::Size, "size",
+ "Sort symbols by size")),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+
+cl::opt<ClassSortMode> ClassOrder(
+ "class-order", cl::desc("Class sort order"), cl::init(ClassSortMode::None),
+ cl::values(
+ clEnumValN(ClassSortMode::None, "none",
+ "Undefined / no particular sort order"),
+ clEnumValN(ClassSortMode::Name, "name", "Sort classes by name"),
+ clEnumValN(ClassSortMode::Size, "size", "Sort classes by size"),
+ clEnumValN(ClassSortMode::Padding, "padding",
+ "Sort classes by amount of padding"),
+ clEnumValN(ClassSortMode::PaddingPct, "padding-pct",
+ "Sort classes by percentage of space consumed by padding"),
+ clEnumValN(ClassSortMode::PaddingImmediate, "padding-imm",
+ "Sort classes by amount of immediate padding"),
+ clEnumValN(ClassSortMode::PaddingPctImmediate, "padding-pct-imm",
+ "Sort classes by percentage of space consumed by immediate "
+ "padding")),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+
+cl::opt<ClassDefinitionFormat> ClassFormat(
+ "class-definitions", cl::desc("Class definition format"),
+ cl::init(ClassDefinitionFormat::All),
+ cl::values(
+ clEnumValN(ClassDefinitionFormat::All, "all",
+ "Display all class members including data, constants, "
+ "typedefs, functions, etc"),
+ clEnumValN(ClassDefinitionFormat::Layout, "layout",
+ "Only display members that contribute to class size."),
+ clEnumValN(ClassDefinitionFormat::None, "none",
+ "Don't display class definitions")),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<uint32_t> ClassRecursionDepth(
+ "class-recurse-depth", cl::desc("Class recursion depth (0=no limit)"),
+ cl::init(0), cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+
+cl::opt<bool> Lines("lines", cl::desc("Line tables"), cl::cat(TypeCategory),
+ cl::sub(PrettySubcommand));
+cl::opt<bool>
+ All("all", cl::desc("Implies all other options in 'Symbol Types' category"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+
+cl::opt<uint64_t> LoadAddress(
+ "load-address",
+ cl::desc("Assume the module is loaded at the specified address"),
+ cl::cat(OtherOptions), cl::sub(PrettySubcommand));
+cl::opt<bool> Native("native", cl::desc("Use native PDB reader instead of DIA"),
+ cl::cat(OtherOptions), cl::sub(PrettySubcommand));
+cl::opt<cl::boolOrDefault>
+ ColorOutput("color-output",
+ cl::desc("Override use of color (default = isatty)"),
+ cl::cat(OtherOptions), cl::sub(PrettySubcommand));
+cl::list<std::string> ExcludeTypes(
+ "exclude-types", cl::desc("Exclude types by regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::list<std::string> ExcludeSymbols(
+ "exclude-symbols", cl::desc("Exclude symbols by regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::list<std::string> ExcludeCompilands(
+ "exclude-compilands", cl::desc("Exclude compilands by regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+
+cl::list<std::string> IncludeTypes(
+ "include-types",
+ cl::desc("Include only types which match a regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::list<std::string> IncludeSymbols(
+ "include-symbols",
+ cl::desc("Include only symbols which match a regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::list<std::string> IncludeCompilands(
+ "include-compilands",
+ cl::desc("Include only compilands those which match a regular expression"),
+ cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::opt<uint32_t> SizeThreshold(
+ "min-type-size", cl::desc("Displays only those types which are greater "
+ "than or equal to the specified size."),
+ cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::opt<uint32_t> PaddingThreshold(
+ "min-class-padding", cl::desc("Displays only those classes which have at "
+ "least the specified amount of padding."),
+ cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::opt<uint32_t> ImmediatePaddingThreshold(
+ "min-class-padding-imm",
+ cl::desc("Displays only those classes which have at least the specified "
+ "amount of immediate padding, ignoring padding internal to bases "
+ "and aggregates."),
+ cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+
+cl::opt<bool> ExcludeCompilerGenerated(
+ "no-compiler-generated",
+ cl::desc("Don't show compiler generated types and symbols"),
+ cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::opt<bool>
+ ExcludeSystemLibraries("no-system-libs",
+ cl::desc("Don't show symbols from system libraries"),
+ cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+
+cl::opt<bool> NoEnumDefs("no-enum-definitions",
+ cl::desc("Don't display full enum definitions"),
+ cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+}
+
+cl::OptionCategory FileOptions("Module & File Options");
+
+namespace bytes {
+cl::OptionCategory MsfBytes("MSF File Options");
+cl::OptionCategory DbiBytes("Dbi Stream Options");
+cl::OptionCategory PdbBytes("PDB Stream Options");
+cl::OptionCategory Types("Type Options");
+cl::OptionCategory ModuleCategory("Module Options");
+
+llvm::Optional<NumberRange> DumpBlockRange;
+llvm::Optional<NumberRange> DumpByteRange;
+
+cl::opt<std::string> DumpBlockRangeOpt(
+ "block-range", cl::value_desc("start[-end]"),
+ cl::desc("Dump binary data from specified range of blocks."),
+ cl::sub(BytesSubcommand), cl::cat(MsfBytes));
+
+cl::opt<std::string>
+ DumpByteRangeOpt("byte-range", cl::value_desc("start[-end]"),
+ cl::desc("Dump binary data from specified range of bytes"),
+ cl::sub(BytesSubcommand), cl::cat(MsfBytes));
+
+cl::list<std::string>
+ DumpStreamData("stream-data", cl::CommaSeparated, cl::ZeroOrMore,
+ cl::desc("Dump binary data from specified streams. Format "
+ "is SN[:Start][@Size]"),
+ cl::sub(BytesSubcommand), cl::cat(MsfBytes));
+
+cl::opt<bool> NameMap("name-map", cl::desc("Dump bytes of PDB Name Map"),
+ cl::sub(BytesSubcommand), cl::cat(PdbBytes));
+cl::opt<bool> Fpm("fpm", cl::desc("Dump free page map"),
+ cl::sub(BytesSubcommand), cl::cat(MsfBytes));
+
+cl::opt<bool> SectionContributions("sc", cl::desc("Dump section contributions"),
+ cl::sub(BytesSubcommand), cl::cat(DbiBytes));
+cl::opt<bool> SectionMap("sm", cl::desc("Dump section map"),
+ cl::sub(BytesSubcommand), cl::cat(DbiBytes));
+cl::opt<bool> ModuleInfos("modi", cl::desc("Dump module info"),
+ cl::sub(BytesSubcommand), cl::cat(DbiBytes));
+cl::opt<bool> FileInfo("files", cl::desc("Dump source file info"),
+ cl::sub(BytesSubcommand), cl::cat(DbiBytes));
+cl::opt<bool> TypeServerMap("type-server", cl::desc("Dump type server map"),
+ cl::sub(BytesSubcommand), cl::cat(DbiBytes));
+cl::opt<bool> ECData("ec", cl::desc("Dump edit and continue map"),
+ cl::sub(BytesSubcommand), cl::cat(DbiBytes));
+
+cl::list<uint32_t>
+ TypeIndex("type",
+ cl::desc("Dump the type record with the given type index"),
+ cl::ZeroOrMore, cl::CommaSeparated, cl::sub(BytesSubcommand),
+ cl::cat(TypeCategory));
+cl::list<uint32_t>
+ IdIndex("id", cl::desc("Dump the id record with the given type index"),
+ cl::ZeroOrMore, cl::CommaSeparated, cl::sub(BytesSubcommand),
+ cl::cat(TypeCategory));
+
+cl::opt<uint32_t> ModuleIndex(
+ "mod",
+ cl::desc(
+ "Limit options in the Modules category to the specified module index"),
+ cl::Optional, cl::sub(BytesSubcommand), cl::cat(ModuleCategory));
+cl::opt<bool> ModuleSyms("syms", cl::desc("Dump symbol record substream"),
+ cl::sub(BytesSubcommand), cl::cat(ModuleCategory));
+cl::opt<bool> ModuleC11("c11-chunks", cl::Hidden,
+ cl::desc("Dump C11 CodeView debug chunks"),
+ cl::sub(BytesSubcommand), cl::cat(ModuleCategory));
+cl::opt<bool> ModuleC13("chunks",
+ cl::desc("Dump C13 CodeView debug chunk subsection"),
+ cl::sub(BytesSubcommand), cl::cat(ModuleCategory));
+cl::opt<bool> SplitChunks(
+ "split-chunks",
+ cl::desc(
+ "When dumping debug chunks, show a different section for each chunk"),
+ cl::sub(BytesSubcommand), cl::cat(ModuleCategory));
+cl::list<std::string> InputFilenames(cl::Positional,
+ cl::desc("<input PDB files>"),
+ cl::OneOrMore, cl::sub(BytesSubcommand));
+
+} // namespace bytes
+
+namespace dump {
+
+cl::OptionCategory MsfOptions("MSF Container Options");
+cl::OptionCategory TypeOptions("Type Record Options");
+cl::OptionCategory SymbolOptions("Symbol Options");
+cl::OptionCategory MiscOptions("Miscellaneous Options");
+
+// MSF OPTIONS
+cl::opt<bool> DumpSummary("summary", cl::desc("dump file summary"),
+ cl::cat(MsfOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpStreams("streams",
+ cl::desc("dump summary of the PDB streams"),
+ cl::cat(MsfOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpStreamBlocks(
+ "stream-blocks",
+ cl::desc("Add block information to the output of -streams"),
+ cl::cat(MsfOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpSymbolStats(
+ "sym-stats",
+ cl::desc("Dump a detailed breakdown of symbol usage/size for each module"),
+ cl::cat(MsfOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpTypeStats(
+ "type-stats",
+ cl::desc("Dump a detailed breakdown of type usage/size"),
+ cl::cat(MsfOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpIDStats(
+ "id-stats",
+ cl::desc("Dump a detailed breakdown of IPI types usage/size"),
+ cl::cat(MsfOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpUdtStats(
+ "udt-stats",
+ cl::desc("Dump a detailed breakdown of S_UDT record usage / stats"),
+ cl::cat(MsfOptions), cl::sub(DumpSubcommand));
+
+// TYPE OPTIONS
+cl::opt<bool> DumpTypes("types",
+ cl::desc("dump CodeView type records from TPI stream"),
+ cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpTypeData(
+ "type-data",
+ cl::desc("dump CodeView type record raw bytes from TPI stream"),
+ cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+cl::opt<bool>
+ DumpTypeRefStats("type-ref-stats",
+ cl::desc("dump statistics on the number and size of types "
+ "transitively referenced by symbol records"),
+ cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+
+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"),
+ cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+
+cl::opt<bool> DumpIds("ids",
+ cl::desc("dump CodeView type records from IPI stream"),
+ cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+cl::opt<bool>
+ DumpIdData("id-data",
+ cl::desc("dump CodeView type record raw bytes from IPI stream"),
+ cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+
+cl::opt<bool> DumpIdExtras("id-extras",
+ cl::desc("dump id hashes and index offsets"),
+ cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+cl::list<uint32_t> DumpIdIndex(
+ "id-index", cl::ZeroOrMore, cl::CommaSeparated,
+ cl::desc("only dump ids with the specified hexadecimal type index"),
+ cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+
+cl::opt<bool> DumpTypeDependents(
+ "dependents",
+ cl::desc("In conjunection with -type-index and -id-index, dumps the entire "
+ "dependency graph for the specified index instead of "
+ "just the single record with the specified index"),
+ cl::cat(TypeOptions), cl::sub(DumpSubcommand));
+
+// SYMBOL OPTIONS
+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",
+ cl::desc("dump Publics hashes and address maps"),
+ cl::cat(SymbolOptions), cl::sub(DumpSubcommand));
+cl::opt<bool>
+ DumpGSIRecords("gsi-records",
+ cl::desc("dump public / global common record stream"),
+ cl::cat(SymbolOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpSymbols("symbols", cl::desc("dump module symbols"),
+ cl::cat(SymbolOptions), cl::sub(DumpSubcommand));
+
+cl::opt<bool>
+ DumpSymRecordBytes("sym-data",
+ 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));
+cl::opt<bool> DumpModuleFiles(
+ "files",
+ cl::desc("Dump the source files that contribute to each module's."),
+ cl::cat(FileOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpLines(
+ "l",
+ cl::desc("dump source file/line information (DEBUG_S_LINES subsection)"),
+ cl::cat(FileOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpInlineeLines(
+ "il",
+ cl::desc("dump inlinee line information (DEBUG_S_INLINEELINES subsection)"),
+ cl::cat(FileOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpXmi(
+ "xmi",
+ cl::desc(
+ "dump cross module imports (DEBUG_S_CROSSSCOPEIMPORTS subsection)"),
+ cl::cat(FileOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpXme(
+ "xme",
+ cl::desc(
+ "dump cross module exports (DEBUG_S_CROSSSCOPEEXPORTS subsection)"),
+ cl::cat(FileOptions), cl::sub(DumpSubcommand));
+cl::opt<uint32_t> DumpModi("modi", cl::Optional,
+ cl::desc("For all options that iterate over "
+ "modules, limit to the specified module"),
+ cl::cat(FileOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> JustMyCode("jmc", cl::Optional,
+ cl::desc("For all options that iterate over modules, "
+ "ignore modules from system libraries"),
+ cl::cat(FileOptions), cl::sub(DumpSubcommand));
+
+// MISCELLANEOUS OPTIONS
+cl::opt<bool> DumpNamedStreams("named-streams",
+ cl::desc("dump PDB named stream table"),
+ cl::cat(MiscOptions), cl::sub(DumpSubcommand));
+
+cl::opt<bool> DumpStringTable("string-table", cl::desc("dump PDB String Table"),
+ cl::cat(MiscOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpStringTableDetails("string-table-details",
+ cl::desc("dump PDB String Table Details"),
+ cl::cat(MiscOptions),
+ cl::sub(DumpSubcommand));
+
+cl::opt<bool> DumpSectionContribs("section-contribs",
+ cl::desc("dump section contributions"),
+ cl::cat(MiscOptions),
+ cl::sub(DumpSubcommand));
+cl::opt<bool> DumpSectionMap("section-map", cl::desc("dump section map"),
+ cl::cat(MiscOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpSectionHeaders("section-headers",
+ cl::desc("Dump image section headers"),
+ cl::cat(MiscOptions), cl::sub(DumpSubcommand));
+
+cl::opt<bool> RawAll("all", cl::desc("Implies most other options."),
+ cl::cat(MiscOptions), cl::sub(DumpSubcommand));
+
+cl::list<std::string> InputFilenames(cl::Positional,
+ cl::desc("<input PDB files>"),
+ cl::OneOrMore, cl::sub(DumpSubcommand));
+}
+
+namespace yaml2pdb {
+cl::opt<std::string>
+ YamlPdbOutputFile("pdb", cl::desc("the name of the PDB file to write"),
+ cl::sub(YamlToPdbSubcommand));
+
+cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input YAML file>"), cl::Required,
+ cl::sub(YamlToPdbSubcommand));
+}
+
+namespace pdb2yaml {
+cl::opt<bool> All("all",
+ cl::desc("Dump everything we know how to dump."),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+cl::opt<bool> NoFileHeaders("no-file-headers",
+ cl::desc("Do not dump MSF file headers"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+cl::opt<bool> Minimal("minimal",
+ cl::desc("Don't write fields with default values"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+
+cl::opt<bool> StreamMetadata(
+ "stream-metadata",
+ cl::desc("Dump the number of streams and each stream's size"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+cl::opt<bool> StreamDirectory(
+ "stream-directory",
+ cl::desc("Dump each stream's block map (implies -stream-metadata)"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+cl::opt<bool> PdbStream("pdb-stream",
+ cl::desc("Dump the PDB Stream (Stream 1)"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+
+cl::opt<bool> StringTable("string-table", cl::desc("Dump the PDB String Table"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+
+cl::opt<bool> DbiStream("dbi-stream",
+ cl::desc("Dump the DBI Stream Headers (Stream 2)"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+
+cl::opt<bool> TpiStream("tpi-stream",
+ cl::desc("Dump the TPI Stream (Stream 3)"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+
+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));
+cl::opt<bool> DumpModuleFiles("module-files", cl::desc("dump file information"),
+ cl::cat(FileOptions),
+ cl::sub(PdbToYamlSubcommand));
+cl::list<ModuleSubsection> DumpModuleSubsections(
+ "subsections", cl::ZeroOrMore, cl::CommaSeparated,
+ cl::desc("dump subsections from each module's debug stream"), ChunkValues,
+ cl::cat(FileOptions), cl::sub(PdbToYamlSubcommand));
+cl::opt<bool> DumpModuleSyms("module-syms", cl::desc("dump module symbols"),
+ cl::cat(FileOptions),
+ cl::sub(PdbToYamlSubcommand));
+
+cl::list<std::string> InputFilename(cl::Positional,
+ cl::desc("<input PDB file>"), cl::Required,
+ cl::sub(PdbToYamlSubcommand));
+} // namespace pdb2yaml
+
+namespace merge {
+cl::list<std::string> InputFilenames(cl::Positional,
+ cl::desc("<input PDB files>"),
+ cl::OneOrMore, cl::sub(MergeSubcommand));
+cl::opt<std::string>
+ PdbOutputFile("pdb", cl::desc("the name of the PDB file to write"),
+ cl::sub(MergeSubcommand));
+}
+
+namespace explain {
+cl::list<std::string> InputFilename(cl::Positional,
+ cl::desc("<input PDB file>"), cl::Required,
+ cl::sub(ExplainSubcommand));
+
+cl::list<uint64_t> Offsets("offset", cl::desc("The file offset to explain"),
+ cl::sub(ExplainSubcommand), cl::OneOrMore);
+
+cl::opt<InputFileType> InputType(
+ "input-type", cl::desc("Specify how to interpret the input file"),
+ cl::init(InputFileType::PDBFile), cl::Optional, cl::sub(ExplainSubcommand),
+ cl::values(clEnumValN(InputFileType::PDBFile, "pdb-file",
+ "Treat input as a PDB file (default)"),
+ clEnumValN(InputFileType::PDBStream, "pdb-stream",
+ "Treat input as raw contents of PDB stream"),
+ clEnumValN(InputFileType::DBIStream, "dbi-stream",
+ "Treat input as raw contents of DBI stream"),
+ clEnumValN(InputFileType::Names, "names-stream",
+ "Treat input as raw contents of /names named stream"),
+ clEnumValN(InputFileType::ModuleStream, "mod-stream",
+ "Treat input as raw contents of a module stream")));
+} // namespace explain
+
+namespace exportstream {
+cl::list<std::string> InputFilename(cl::Positional,
+ cl::desc("<input PDB file>"), cl::Required,
+ cl::sub(ExportSubcommand));
+cl::opt<std::string> OutputFile("out",
+ cl::desc("The file to write the stream to"),
+ cl::Required, cl::sub(ExportSubcommand));
+cl::opt<std::string>
+ Stream("stream", cl::Required,
+ cl::desc("The index or name of the stream whose contents to export"),
+ cl::sub(ExportSubcommand));
+cl::opt<bool> ForceName("name",
+ cl::desc("Force the interpretation of -stream as a "
+ "string, even if it is a valid integer"),
+ cl::sub(ExportSubcommand), cl::Optional,
+ cl::init(false));
+} // namespace exportstream
+}
+
+static ExitOnError ExitOnErr;
+
+static void yamlToPdb(StringRef Path) {
+ BumpPtrAllocator Allocator;
+ ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer =
+ MemoryBuffer::getFileOrSTDIN(Path, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
+
+ if (ErrorOrBuffer.getError()) {
+ ExitOnErr(createFileError(Path, errorCodeToError(ErrorOrBuffer.getError())));
+ }
+
+ std::unique_ptr<MemoryBuffer> &Buffer = ErrorOrBuffer.get();
+
+ llvm::yaml::Input In(Buffer->getBuffer());
+ pdb::yaml::PdbObject YamlObj(Allocator);
+ In >> YamlObj;
+
+ PDBFileBuilder Builder(Allocator);
+
+ uint32_t BlockSize = 4096;
+ if (YamlObj.Headers.hasValue())
+ BlockSize = YamlObj.Headers->SuperBlock.BlockSize;
+ ExitOnErr(Builder.initialize(BlockSize));
+ // Add each of the reserved streams. We ignore stream metadata in the
+ // yaml, because we will reconstruct our own view of the streams. For
+ // example, the YAML may say that there were 20 streams in the original
+ // PDB, but maybe we only dump a subset of those 20 streams, so we will
+ // have fewer, and the ones we do have may end up with different indices
+ // than the ones in the original PDB. So we just start with a clean slate.
+ for (uint32_t I = 0; I < kSpecialStreamCount; ++I)
+ ExitOnErr(Builder.getMsfBuilder().addStream(0));
+
+ StringsAndChecksums Strings;
+ Strings.setStrings(std::make_shared<DebugStringTableSubsection>());
+
+ if (YamlObj.StringTable.hasValue()) {
+ for (auto S : *YamlObj.StringTable)
+ Strings.strings()->insert(S);
+ }
+
+ pdb::yaml::PdbInfoStream DefaultInfoStream;
+ pdb::yaml::PdbDbiStream DefaultDbiStream;
+ pdb::yaml::PdbTpiStream DefaultTpiStream;
+ pdb::yaml::PdbTpiStream DefaultIpiStream;
+
+ const auto &Info = YamlObj.PdbStream.getValueOr(DefaultInfoStream);
+
+ auto &InfoBuilder = Builder.getInfoBuilder();
+ InfoBuilder.setAge(Info.Age);
+ InfoBuilder.setGuid(Info.Guid);
+ InfoBuilder.setSignature(Info.Signature);
+ InfoBuilder.setVersion(Info.Version);
+ for (auto F : Info.Features)
+ InfoBuilder.addFeature(F);
+
+ const auto &Dbi = YamlObj.DbiStream.getValueOr(DefaultDbiStream);
+ auto &DbiBuilder = Builder.getDbiBuilder();
+ DbiBuilder.setAge(Dbi.Age);
+ DbiBuilder.setBuildNumber(Dbi.BuildNumber);
+ DbiBuilder.setFlags(Dbi.Flags);
+ DbiBuilder.setMachineType(Dbi.MachineType);
+ DbiBuilder.setPdbDllRbld(Dbi.PdbDllRbld);
+ DbiBuilder.setPdbDllVersion(Dbi.PdbDllVersion);
+ DbiBuilder.setVersionHeader(Dbi.VerHeader);
+ for (const auto &MI : Dbi.ModInfos) {
+ auto &ModiBuilder = ExitOnErr(DbiBuilder.addModuleInfo(MI.Mod));
+ ModiBuilder.setObjFileName(MI.Obj);
+
+ for (auto S : MI.SourceFiles)
+ ExitOnErr(DbiBuilder.addModuleSourceFile(ModiBuilder, S));
+ if (MI.Modi.hasValue()) {
+ const auto &ModiStream = *MI.Modi;
+ for (auto Symbol : ModiStream.Symbols) {
+ ModiBuilder.addSymbol(
+ Symbol.toCodeViewSymbol(Allocator, CodeViewContainer::Pdb));
+ }
+ }
+
+ // Each module has its own checksum subsection, so scan for it every time.
+ Strings.setChecksums(nullptr);
+ CodeViewYAML::initializeStringsAndChecksums(MI.Subsections, Strings);
+
+ auto CodeViewSubsections = ExitOnErr(CodeViewYAML::toCodeViewSubsectionList(
+ Allocator, MI.Subsections, Strings));
+ for (auto &SS : CodeViewSubsections) {
+ ModiBuilder.addDebugSubsection(SS);
+ }
+ }
+
+ auto &TpiBuilder = Builder.getTpiBuilder();
+ const auto &Tpi = YamlObj.TpiStream.getValueOr(DefaultTpiStream);
+ TpiBuilder.setVersionHeader(Tpi.Version);
+ AppendingTypeTableBuilder TS(Allocator);
+ for (const auto &R : Tpi.Records) {
+ CVType Type = R.toCodeViewRecord(TS);
+ TpiBuilder.addTypeRecord(Type.RecordData, None);
+ }
+
+ const auto &Ipi = YamlObj.IpiStream.getValueOr(DefaultIpiStream);
+ auto &IpiBuilder = Builder.getIpiBuilder();
+ IpiBuilder.setVersionHeader(Ipi.Version);
+ for (const auto &R : Ipi.Records) {
+ CVType Type = R.toCodeViewRecord(TS);
+ IpiBuilder.addTypeRecord(Type.RecordData, None);
+ }
+
+ Builder.getStringTableBuilder().setStrings(*Strings.strings());
+
+ codeview::GUID IgnoredOutGuid;
+ ExitOnErr(Builder.commit(opts::yaml2pdb::YamlPdbOutputFile, &IgnoredOutGuid));
+}
+
+static PDBFile &loadPDB(StringRef Path, std::unique_ptr<IPDBSession> &Session) {
+ ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session));
+
+ NativeSession *NS = static_cast<NativeSession *>(Session.get());
+ return NS->getPDBFile();
+}
+
+static void pdb2Yaml(StringRef Path) {
+ std::unique_ptr<IPDBSession> Session;
+ auto &File = loadPDB(Path, Session);
+
+ auto O = std::make_unique<YAMLOutputStyle>(File);
+
+ ExitOnErr(O->dump());
+}
+
+static void dumpRaw(StringRef Path) {
+ InputFile IF = ExitOnErr(InputFile::open(Path));
+
+ auto O = std::make_unique<DumpOutputStyle>(IF);
+ ExitOnErr(O->dump());
+}
+
+static void dumpBytes(StringRef Path) {
+ std::unique_ptr<IPDBSession> Session;
+ auto &File = loadPDB(Path, Session);
+
+ auto O = std::make_unique<BytesOutputStyle>(File);
+
+ ExitOnErr(O->dump());
+}
+
+bool opts::pretty::shouldDumpSymLevel(SymLevel Search) {
+ if (SymTypes.empty())
+ return true;
+ if (llvm::is_contained(SymTypes, Search))
+ return true;
+ if (llvm::is_contained(SymTypes, SymLevel::All))
+ return true;
+ return false;
+}
+
+uint32_t llvm::pdb::getTypeLength(const PDBSymbolData &Symbol) {
+ auto SymbolType = Symbol.getType();
+ const IPDBRawSymbol &RawType = SymbolType->getRawSymbol();
+
+ return RawType.getLength();
+}
+
+bool opts::pretty::compareFunctionSymbols(
+ const std::unique_ptr<PDBSymbolFunc> &F1,
+ const std::unique_ptr<PDBSymbolFunc> &F2) {
+ assert(opts::pretty::SymbolOrder != opts::pretty::SymbolSortMode::None);
+
+ if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::Name)
+ return F1->getName() < F2->getName();
+
+ // Note that we intentionally sort in descending order on length, since
+ // long functions are more interesting than short functions.
+ return F1->getLength() > F2->getLength();
+}
+
+bool opts::pretty::compareDataSymbols(
+ const std::unique_ptr<PDBSymbolData> &F1,
+ const std::unique_ptr<PDBSymbolData> &F2) {
+ assert(opts::pretty::SymbolOrder != opts::pretty::SymbolSortMode::None);
+
+ if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::Name)
+ return F1->getName() < F2->getName();
+
+ // Note that we intentionally sort in descending order on length, since
+ // large types are more interesting than short ones.
+ return getTypeLength(*F1) > getTypeLength(*F2);
+}
+
+static std::string stringOr(std::string Str, std::string IfEmpty) {
+ return (Str.empty()) ? IfEmpty : Str;
+}
+
+static void dumpInjectedSources(LinePrinter &Printer, IPDBSession &Session) {
+ auto Sources = Session.getInjectedSources();
+ if (!Sources || !Sources->getChildCount()) {
+ Printer.printLine("There are no injected sources.");
+ return;
+ }
+
+ while (auto IS = Sources->getNext()) {
+ Printer.NewLine();
+ std::string File = stringOr(IS->getFileName(), "<null>");
+ uint64_t Size = IS->getCodeByteSize();
+ std::string Obj = stringOr(IS->getObjectFileName(), "<null>");
+ std::string VFName = stringOr(IS->getVirtualFileName(), "<null>");
+ uint32_t CRC = IS->getCrc32();
+
+ WithColor(Printer, PDB_ColorItem::Path).get() << File;
+ Printer << " (";
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Size;
+ Printer << " bytes): ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "obj";
+ Printer << "=";
+ WithColor(Printer, PDB_ColorItem::Path).get() << Obj;
+ Printer << ", ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "vname";
+ Printer << "=";
+ WithColor(Printer, PDB_ColorItem::Path).get() << VFName;
+ Printer << ", ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "crc";
+ Printer << "=";
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get() << CRC;
+ Printer << ", ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "compression";
+ Printer << "=";
+ dumpPDBSourceCompression(
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get(),
+ IS->getCompression());
+
+ if (!opts::pretty::ShowInjectedSourceContent)
+ continue;
+
+ // Set the indent level to 0 when printing file content.
+ int Indent = Printer.getIndentLevel();
+ Printer.Unindent(Indent);
+
+ if (IS->getCompression() == PDB_SourceCompression::None)
+ Printer.printLine(IS->getCode());
+ else
+ Printer.formatBinary("Compressed data",
+ arrayRefFromStringRef(IS->getCode()),
+ /*StartOffset=*/0);
+
+ // Re-indent back to the original level.
+ Printer.Indent(Indent);
+ }
+}
+
+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;
+
+ const auto ReaderType =
+ opts::pretty::Native ? PDB_ReaderType::Native : PDB_ReaderType::DIA;
+ ExitOnErr(loadDataForPDB(ReaderType, Path, Session));
+
+ if (opts::pretty::LoadAddress)
+ Session->setLoadAddress(opts::pretty::LoadAddress);
+
+ auto &Stream = outs();
+ const bool UseColor = opts::pretty::ColorOutput == cl::BOU_UNSET
+ ? Stream.has_colors()
+ : opts::pretty::ColorOutput == cl::BOU_TRUE;
+ LinePrinter Printer(2, UseColor, Stream);
+
+ auto GlobalScope(Session->getGlobalScope());
+ if (!GlobalScope)
+ return;
+ std::string FileName(GlobalScope->getSymbolsFileName());
+
+ WithColor(Printer, PDB_ColorItem::None).get() << "Summary for ";
+ WithColor(Printer, PDB_ColorItem::Path).get() << FileName;
+ Printer.Indent();
+ uint64_t FileSize = 0;
+
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << "Size";
+ if (!sys::fs::file_size(FileName, FileSize)) {
+ Printer << ": " << FileSize << " bytes";
+ } else {
+ Printer << ": (Unable to obtain file size)";
+ }
+
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << "Guid";
+ Printer << ": " << GlobalScope->getGuid();
+
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << "Age";
+ Printer << ": " << GlobalScope->getAge();
+
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << "Attributes";
+ Printer << ": ";
+ if (GlobalScope->hasCTypes())
+ outs() << "HasCTypes ";
+ if (GlobalScope->hasPrivateSymbols())
+ outs() << "HasPrivateSymbols ";
+ Printer.Unindent();
+
+ if (!opts::pretty::WithName.empty()) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::SectionHeader).get()
+ << "---SYMBOLS & TYPES BY NAME---";
+
+ for (StringRef Name : opts::pretty::WithName) {
+ auto Symbols = GlobalScope->findChildren(
+ PDB_SymType::None, Name, PDB_NameSearchFlags::NS_CaseSensitive);
+ if (!Symbols || Symbols->getChildCount() == 0) {
+ Printer.formatLine("[not found] - {0}", Name);
+ continue;
+ }
+ Printer.formatLine("[{0} occurrences] - {1}", Symbols->getChildCount(),
+ Name);
+
+ AutoIndent Indent(Printer);
+ Printer.NewLine();
+
+ while (auto Symbol = Symbols->getNext()) {
+ switch (Symbol->getSymTag()) {
+ case PDB_SymType::Typedef: {
+ TypedefDumper TD(Printer);
+ std::unique_ptr<PDBSymbolTypeTypedef> T =
+ llvm::unique_dyn_cast<PDBSymbolTypeTypedef>(std::move(Symbol));
+ TD.start(*T);
+ break;
+ }
+ case PDB_SymType::Enum: {
+ EnumDumper ED(Printer);
+ std::unique_ptr<PDBSymbolTypeEnum> E =
+ llvm::unique_dyn_cast<PDBSymbolTypeEnum>(std::move(Symbol));
+ ED.start(*E);
+ break;
+ }
+ case PDB_SymType::UDT: {
+ ClassDefinitionDumper CD(Printer);
+ std::unique_ptr<PDBSymbolTypeUDT> C =
+ llvm::unique_dyn_cast<PDBSymbolTypeUDT>(std::move(Symbol));
+ CD.start(*C);
+ break;
+ }
+ case PDB_SymType::BaseClass:
+ case PDB_SymType::Friend: {
+ TypeDumper TD(Printer);
+ Symbol->dump(TD);
+ break;
+ }
+ case PDB_SymType::Function: {
+ FunctionDumper FD(Printer);
+ std::unique_ptr<PDBSymbolFunc> F =
+ llvm::unique_dyn_cast<PDBSymbolFunc>(std::move(Symbol));
+ FD.start(*F, FunctionDumper::PointerType::None);
+ break;
+ }
+ case PDB_SymType::Data: {
+ VariableDumper VD(Printer);
+ std::unique_ptr<PDBSymbolData> D =
+ llvm::unique_dyn_cast<PDBSymbolData>(std::move(Symbol));
+ VD.start(*D);
+ break;
+ }
+ case PDB_SymType::PublicSymbol: {
+ ExternalSymbolDumper ED(Printer);
+ std::unique_ptr<PDBSymbolPublicSymbol> PS =
+ llvm::unique_dyn_cast<PDBSymbolPublicSymbol>(std::move(Symbol));
+ ED.dump(*PS);
+ break;
+ }
+ default:
+ llvm_unreachable("Unexpected symbol tag!");
+ }
+ }
+ }
+ llvm::outs().flush();
+ }
+
+ if (opts::pretty::Compilands) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::SectionHeader).get()
+ << "---COMPILANDS---";
+ auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>();
+
+ if (Compilands) {
+ Printer.Indent();
+ CompilandDumper Dumper(Printer);
+ CompilandDumpFlags options = CompilandDumper::Flags::None;
+ if (opts::pretty::Lines)
+ options = options | CompilandDumper::Flags::Lines;
+ while (auto Compiland = Compilands->getNext())
+ Dumper.start(*Compiland, options);
+ Printer.Unindent();
+ }
+ }
+
+ 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();
+ TypeDumper Dumper(Printer);
+ Dumper.start(*GlobalScope);
+ Printer.Unindent();
+ }
+
+ if (opts::pretty::Symbols) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---SYMBOLS---";
+ if (auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>()) {
+ Printer.Indent();
+ CompilandDumper Dumper(Printer);
+ while (auto Compiland = Compilands->getNext())
+ Dumper.start(*Compiland, true);
+ Printer.Unindent();
+ }
+ }
+
+ if (opts::pretty::Globals) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---GLOBALS---";
+ Printer.Indent();
+ if (shouldDumpSymLevel(opts::pretty::SymLevel::Functions)) {
+ if (auto Functions = GlobalScope->findAllChildren<PDBSymbolFunc>()) {
+ FunctionDumper Dumper(Printer);
+ if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::None) {
+ while (auto Function = Functions->getNext()) {
+ Printer.NewLine();
+ Dumper.start(*Function, FunctionDumper::PointerType::None);
+ }
+ } else {
+ std::vector<std::unique_ptr<PDBSymbolFunc>> Funcs;
+ while (auto Func = Functions->getNext())
+ Funcs.push_back(std::move(Func));
+ llvm::sort(Funcs, opts::pretty::compareFunctionSymbols);
+ for (const auto &Func : Funcs) {
+ Printer.NewLine();
+ Dumper.start(*Func, FunctionDumper::PointerType::None);
+ }
+ }
+ }
+ }
+ if (shouldDumpSymLevel(opts::pretty::SymLevel::Data)) {
+ if (auto Vars = GlobalScope->findAllChildren<PDBSymbolData>()) {
+ VariableDumper Dumper(Printer);
+ if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::None) {
+ while (auto Var = Vars->getNext())
+ Dumper.start(*Var);
+ } else {
+ std::vector<std::unique_ptr<PDBSymbolData>> Datas;
+ while (auto Var = Vars->getNext())
+ Datas.push_back(std::move(Var));
+ llvm::sort(Datas, opts::pretty::compareDataSymbols);
+ for (const auto &Var : Datas)
+ Dumper.start(*Var);
+ }
+ }
+ }
+ if (shouldDumpSymLevel(opts::pretty::SymLevel::Thunks)) {
+ if (auto Thunks = GlobalScope->findAllChildren<PDBSymbolThunk>()) {
+ CompilandDumper Dumper(Printer);
+ while (auto Thunk = Thunks->getNext())
+ Dumper.dump(*Thunk);
+ }
+ }
+ Printer.Unindent();
+ }
+ if (opts::pretty::Externals) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---EXTERNALS---";
+ Printer.Indent();
+ ExternalSymbolDumper Dumper(Printer);
+ Dumper.start(*GlobalScope);
+ }
+ if (opts::pretty::Lines) {
+ Printer.NewLine();
+ }
+ if (opts::pretty::InjectedSources) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::SectionHeader).get()
+ << "---INJECTED SOURCES---";
+ AutoIndent Indent1(Printer);
+ dumpInjectedSources(Printer, *Session);
+ }
+
+ Printer.NewLine();
+ outs().flush();
+}
+
+static void mergePdbs() {
+ BumpPtrAllocator Allocator;
+ MergingTypeTableBuilder MergedTpi(Allocator);
+ MergingTypeTableBuilder MergedIpi(Allocator);
+
+ // Create a Tpi and Ipi type table with all types from all input files.
+ for (const auto &Path : opts::merge::InputFilenames) {
+ std::unique_ptr<IPDBSession> Session;
+ auto &File = loadPDB(Path, Session);
+ SmallVector<TypeIndex, 128> TypeMap;
+ SmallVector<TypeIndex, 128> IdMap;
+ if (File.hasPDBTpiStream()) {
+ auto &Tpi = ExitOnErr(File.getPDBTpiStream());
+ ExitOnErr(
+ codeview::mergeTypeRecords(MergedTpi, TypeMap, Tpi.typeArray()));
+ }
+ if (File.hasPDBIpiStream()) {
+ auto &Ipi = ExitOnErr(File.getPDBIpiStream());
+ ExitOnErr(codeview::mergeIdRecords(MergedIpi, TypeMap, IdMap,
+ Ipi.typeArray()));
+ }
+ }
+
+ // Then write the PDB.
+ PDBFileBuilder Builder(Allocator);
+ ExitOnErr(Builder.initialize(4096));
+ // Add each of the reserved streams. We might not put any data in them,
+ // but at least they have to be present.
+ for (uint32_t I = 0; I < kSpecialStreamCount; ++I)
+ ExitOnErr(Builder.getMsfBuilder().addStream(0));
+
+ auto &DestTpi = Builder.getTpiBuilder();
+ auto &DestIpi = Builder.getIpiBuilder();
+ MergedTpi.ForEachRecord([&DestTpi](TypeIndex TI, const CVType &Type) {
+ DestTpi.addTypeRecord(Type.RecordData, None);
+ });
+ MergedIpi.ForEachRecord([&DestIpi](TypeIndex TI, const CVType &Type) {
+ DestIpi.addTypeRecord(Type.RecordData, None);
+ });
+ Builder.getInfoBuilder().addFeature(PdbRaw_FeatureSig::VC140);
+
+ SmallString<64> OutFile(opts::merge::PdbOutputFile);
+ if (OutFile.empty()) {
+ OutFile = opts::merge::InputFilenames[0];
+ llvm::sys::path::replace_extension(OutFile, "merged.pdb");
+ }
+
+ codeview::GUID IgnoredOutGuid;
+ ExitOnErr(Builder.commit(OutFile, &IgnoredOutGuid));
+}
+
+static void explain() {
+ std::unique_ptr<IPDBSession> Session;
+ InputFile IF =
+ ExitOnErr(InputFile::open(opts::explain::InputFilename.front(), true));
+
+ for (uint64_t Off : opts::explain::Offsets) {
+ auto O = std::make_unique<ExplainOutputStyle>(IF, Off);
+
+ ExitOnErr(O->dump());
+ }
+}
+
+static void exportStream() {
+ std::unique_ptr<IPDBSession> Session;
+ PDBFile &File = loadPDB(opts::exportstream::InputFilename.front(), Session);
+
+ std::unique_ptr<MappedBlockStream> SourceStream;
+ uint32_t Index = 0;
+ bool Success = false;
+ std::string OutFileName = opts::exportstream::OutputFile;
+
+ if (!opts::exportstream::ForceName) {
+ // First try to parse it as an integer, if it fails fall back to treating it
+ // as a named stream.
+ if (to_integer(opts::exportstream::Stream, Index)) {
+ if (Index >= File.getNumStreams()) {
+ errs() << "Error: " << Index << " is not a valid stream index.\n";
+ exit(1);
+ }
+ Success = true;
+ outs() << "Dumping contents of stream index " << Index << " to file "
+ << OutFileName << ".\n";
+ }
+ }
+
+ if (!Success) {
+ InfoStream &IS = cantFail(File.getPDBInfoStream());
+ Index = ExitOnErr(IS.getNamedStreamIndex(opts::exportstream::Stream));
+ outs() << "Dumping contents of stream '" << opts::exportstream::Stream
+ << "' (index " << Index << ") to file " << OutFileName << ".\n";
+ }
+
+ SourceStream = File.createIndexedStream(Index);
+ auto OutFile = ExitOnErr(
+ FileOutputBuffer::create(OutFileName, SourceStream->getLength()));
+ FileBufferByteStream DestStream(std::move(OutFile), llvm::support::little);
+ BinaryStreamWriter Writer(DestStream);
+ ExitOnErr(Writer.writeStreamRef(*SourceStream));
+ ExitOnErr(DestStream.commit());
+}
+
+static bool parseRange(StringRef Str,
+ Optional<opts::bytes::NumberRange> &Parsed) {
+ if (Str.empty())
+ return true;
+
+ llvm::Regex R("^([^-]+)(-([^-]+))?$");
+ llvm::SmallVector<llvm::StringRef, 2> Matches;
+ if (!R.match(Str, &Matches))
+ return false;
+
+ Parsed.emplace();
+ if (!to_integer(Matches[1], Parsed->Min))
+ return false;
+
+ if (!Matches[3].empty()) {
+ Parsed->Max.emplace();
+ if (!to_integer(Matches[3], *Parsed->Max))
+ return false;
+ }
+ return true;
+}
+
+static void simplifyChunkList(llvm::cl::list<opts::ModuleSubsection> &Chunks) {
+ // If this list contains "All" plus some other stuff, remove the other stuff
+ // and just keep "All" in the list.
+ if (!llvm::is_contained(Chunks, opts::ModuleSubsection::All))
+ return;
+ Chunks.reset();
+ Chunks.push_back(opts::ModuleSubsection::All);
+}
+
+int main(int Argc, const char **Argv) {
+ InitLLVM X(Argc, Argv);
+ ExitOnErr.setBanner("llvm-pdbutil: ");
+
+ cl::HideUnrelatedOptions(
+ {&opts::TypeCategory, &opts::FilterCategory, &opts::OtherOptions});
+ cl::ParseCommandLineOptions(Argc, Argv, "LLVM PDB Dumper\n");
+
+ if (opts::BytesSubcommand) {
+ if (!parseRange(opts::bytes::DumpBlockRangeOpt,
+ opts::bytes::DumpBlockRange)) {
+ errs() << "Argument '" << opts::bytes::DumpBlockRangeOpt
+ << "' invalid format.\n";
+ errs().flush();
+ exit(1);
+ }
+ if (!parseRange(opts::bytes::DumpByteRangeOpt,
+ opts::bytes::DumpByteRange)) {
+ errs() << "Argument '" << opts::bytes::DumpByteRangeOpt
+ << "' invalid format.\n";
+ errs().flush();
+ exit(1);
+ }
+ }
+
+ 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;
+ opts::dump::DumpLines = true;
+ opts::dump::DumpModules = true;
+ opts::dump::DumpModuleFiles = true;
+ opts::dump::DumpPublics = true;
+ opts::dump::DumpSectionContribs = true;
+ opts::dump::DumpSectionHeaders = true;
+ opts::dump::DumpSectionMap = true;
+ opts::dump::DumpStreams = true;
+ opts::dump::DumpStreamBlocks = true;
+ opts::dump::DumpStringTable = true;
+ opts::dump::DumpStringTableDetails = true;
+ opts::dump::DumpSummary = true;
+ opts::dump::DumpSymbols = true;
+ opts::dump::DumpSymbolStats = true;
+ opts::dump::DumpTypes = true;
+ opts::dump::DumpTypeExtras = true;
+ opts::dump::DumpUdtStats = true;
+ opts::dump::DumpXme = true;
+ opts::dump::DumpXmi = true;
+ }
+ }
+ if (opts::PdbToYamlSubcommand) {
+ if (opts::pdb2yaml::All) {
+ opts::pdb2yaml::StreamMetadata = true;
+ opts::pdb2yaml::StreamDirectory = true;
+ opts::pdb2yaml::PdbStream = true;
+ opts::pdb2yaml::StringTable = true;
+ 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;
+ opts::pdb2yaml::DumpModuleSubsections.push_back(
+ opts::ModuleSubsection::All);
+ }
+ simplifyChunkList(opts::pdb2yaml::DumpModuleSubsections);
+
+ if (opts::pdb2yaml::DumpModuleSyms || opts::pdb2yaml::DumpModuleFiles)
+ opts::pdb2yaml::DumpModules = true;
+
+ if (opts::pdb2yaml::DumpModules)
+ opts::pdb2yaml::DbiStream = true;
+ }
+
+ llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
+
+ if (opts::PdbToYamlSubcommand) {
+ pdb2Yaml(opts::pdb2yaml::InputFilename.front());
+ } else if (opts::YamlToPdbSubcommand) {
+ if (opts::yaml2pdb::YamlPdbOutputFile.empty()) {
+ SmallString<16> OutputFilename(opts::yaml2pdb::InputFilename.getValue());
+ sys::path::replace_extension(OutputFilename, ".pdb");
+ opts::yaml2pdb::YamlPdbOutputFile = std::string(OutputFilename.str());
+ }
+ yamlToPdb(opts::yaml2pdb::InputFilename);
+ } else if (opts::DiaDumpSubcommand) {
+ llvm::for_each(opts::diadump::InputFilenames, dumpDia);
+ } else if (opts::PrettySubcommand) {
+ if (opts::pretty::Lines)
+ opts::pretty::Compilands = true;
+
+ if (opts::pretty::All) {
+ opts::pretty::Compilands = true;
+ opts::pretty::Symbols = true;
+ opts::pretty::Globals = true;
+ opts::pretty::Types = true;
+ opts::pretty::Externals = true;
+ opts::pretty::Lines = true;
+ }
+
+ if (opts::pretty::Types) {
+ 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
+ // remember that these are regexes. So special characters such as * and \
+ // need to be escaped in the regex. In the case of a literal \, this means
+ // it needs to be escaped again in the C++. So matching a single \ in the
+ // input requires 4 \es in the C++.
+ if (opts::pretty::ExcludeCompilerGenerated) {
+ opts::pretty::ExcludeTypes.push_back("__vc_attributes");
+ opts::pretty::ExcludeCompilands.push_back("\\* Linker \\*");
+ }
+ if (opts::pretty::ExcludeSystemLibraries) {
+ opts::pretty::ExcludeCompilands.push_back(
+ "f:\\\\binaries\\\\Intermediate\\\\vctools\\\\crt_bld");
+ opts::pretty::ExcludeCompilands.push_back("f:\\\\dd\\\\vctools\\\\crt");
+ opts::pretty::ExcludeCompilands.push_back(
+ "d:\\\\th.obj.x86fre\\\\minkernel");
+ }
+ llvm::for_each(opts::pretty::InputFilenames, dumpPretty);
+ } else if (opts::DumpSubcommand) {
+ llvm::for_each(opts::dump::InputFilenames, dumpRaw);
+ } else if (opts::BytesSubcommand) {
+ llvm::for_each(opts::bytes::InputFilenames, dumpBytes);
+ } else if (opts::MergeSubcommand) {
+ if (opts::merge::InputFilenames.size() < 2) {
+ errs() << "merge subcommand requires at least 2 input files.\n";
+ exit(1);
+ }
+ mergePdbs();
+ } else if (opts::ExplainSubcommand) {
+ explain();
+ } else if (opts::ExportSubcommand) {
+ exportStream();
+ }
+
+ outs().flush();
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/llvm-pdbutil.h b/contrib/libs/llvm14/tools/llvm-pdbutil/llvm-pdbutil.h
new file mode 100644
index 00000000000..9fe92c2c9d7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/llvm-pdbutil.h
@@ -0,0 +1,220 @@
+//===- llvm-pdbutil.h ----------------------------------------- *- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_LLVMPDBDUMP_H
+#define LLVM_TOOLS_LLVMPDBDUMP_LLVMPDBDUMP_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <memory>
+#include <stdint.h>
+
+namespace llvm {
+namespace object {
+class COFFObjectFile;
+}
+namespace pdb {
+class PDBSymbolData;
+class PDBSymbolFunc;
+class PDBFile;
+uint32_t getTypeLength(const PDBSymbolData &Symbol);
+}
+typedef llvm::PointerUnion<object::COFFObjectFile *, pdb::PDBFile *>
+ PdbOrCoffObj;
+}
+
+namespace opts {
+
+enum class DumpLevel { None, Basic, Verbose };
+
+enum class ModuleSubsection {
+ Unknown,
+ Lines,
+ FileChecksums,
+ InlineeLines,
+ CrossScopeImports,
+ CrossScopeExports,
+ StringTable,
+ Symbols,
+ FrameData,
+ CoffSymbolRVAs,
+ All
+};
+
+namespace pretty {
+
+enum class ClassDefinitionFormat { None, Layout, All };
+enum class ClassSortMode {
+ None,
+ Name,
+ Size,
+ Padding,
+ PaddingPct,
+ PaddingImmediate,
+ PaddingPctImmediate
+};
+
+enum class SymbolSortMode { None, Name, Size };
+
+enum class SymLevel { Functions, Data, Thunks, All };
+
+bool shouldDumpSymLevel(SymLevel Level);
+bool compareFunctionSymbols(
+ const std::unique_ptr<llvm::pdb::PDBSymbolFunc> &F1,
+ const std::unique_ptr<llvm::pdb::PDBSymbolFunc> &F2);
+bool compareDataSymbols(const std::unique_ptr<llvm::pdb::PDBSymbolData> &F1,
+ const std::unique_ptr<llvm::pdb::PDBSymbolData> &F2);
+
+extern llvm::cl::list<std::string> WithName;
+
+extern llvm::cl::opt<bool> Compilands;
+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;
+
+extern llvm::cl::opt<bool> NoEnumDefs;
+extern llvm::cl::list<std::string> ExcludeTypes;
+extern llvm::cl::list<std::string> ExcludeSymbols;
+extern llvm::cl::list<std::string> ExcludeCompilands;
+extern llvm::cl::list<std::string> IncludeTypes;
+extern llvm::cl::list<std::string> IncludeSymbols;
+extern llvm::cl::list<std::string> IncludeCompilands;
+extern llvm::cl::opt<SymbolSortMode> SymbolOrder;
+extern llvm::cl::opt<ClassSortMode> ClassOrder;
+extern llvm::cl::opt<uint32_t> SizeThreshold;
+extern llvm::cl::opt<uint32_t> PaddingThreshold;
+extern llvm::cl::opt<uint32_t> ImmediatePaddingThreshold;
+extern llvm::cl::opt<ClassDefinitionFormat> ClassFormat;
+extern llvm::cl::opt<uint32_t> ClassRecursionDepth;
+}
+
+namespace bytes {
+struct NumberRange {
+ uint64_t Min;
+ llvm::Optional<uint64_t> Max;
+};
+
+extern llvm::Optional<NumberRange> DumpBlockRange;
+extern llvm::Optional<NumberRange> DumpByteRange;
+extern llvm::cl::list<std::string> DumpStreamData;
+extern llvm::cl::opt<bool> NameMap;
+extern llvm::cl::opt<bool> Fpm;
+
+extern llvm::cl::opt<bool> SectionContributions;
+extern llvm::cl::opt<bool> SectionMap;
+extern llvm::cl::opt<bool> ModuleInfos;
+extern llvm::cl::opt<bool> FileInfo;
+extern llvm::cl::opt<bool> TypeServerMap;
+extern llvm::cl::opt<bool> ECData;
+
+extern llvm::cl::list<uint32_t> TypeIndex;
+extern llvm::cl::list<uint32_t> IdIndex;
+
+extern llvm::cl::opt<uint32_t> ModuleIndex;
+extern llvm::cl::opt<bool> ModuleSyms;
+extern llvm::cl::opt<bool> ModuleC11;
+extern llvm::cl::opt<bool> ModuleC13;
+extern llvm::cl::opt<bool> SplitChunks;
+} // namespace bytes
+
+namespace dump {
+
+extern llvm::cl::opt<bool> DumpSummary;
+extern llvm::cl::opt<bool> DumpFpm;
+extern llvm::cl::opt<bool> DumpStreams;
+extern llvm::cl::opt<bool> DumpSymbolStats;
+extern llvm::cl::opt<bool> DumpTypeStats;
+extern llvm::cl::opt<bool> DumpIDStats;
+extern llvm::cl::opt<bool> DumpUdtStats;
+extern llvm::cl::opt<bool> DumpStreamBlocks;
+
+extern llvm::cl::opt<bool> DumpLines;
+extern llvm::cl::opt<bool> DumpInlineeLines;
+extern llvm::cl::opt<bool> DumpXmi;
+extern llvm::cl::opt<bool> DumpXme;
+extern llvm::cl::opt<bool> DumpNamedStreams;
+extern llvm::cl::opt<bool> DumpStringTable;
+extern llvm::cl::opt<bool> DumpStringTableDetails;
+extern llvm::cl::opt<bool> DumpTypes;
+extern llvm::cl::opt<bool> DumpTypeData;
+extern llvm::cl::opt<bool> DumpTypeExtras;
+extern llvm::cl::list<uint32_t> DumpTypeIndex;
+extern llvm::cl::opt<bool> DumpTypeDependents;
+extern llvm::cl::opt<bool> DumpTypeRefStats;
+extern llvm::cl::opt<bool> DumpSectionHeaders;
+
+extern llvm::cl::opt<bool> DumpIds;
+extern llvm::cl::opt<bool> DumpIdData;
+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;
+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;
+}
+
+namespace pdb2yaml {
+extern llvm::cl::opt<bool> All;
+extern llvm::cl::opt<bool> NoFileHeaders;
+extern llvm::cl::opt<bool> Minimal;
+extern llvm::cl::opt<bool> StreamMetadata;
+extern llvm::cl::opt<bool> StreamDirectory;
+extern llvm::cl::opt<bool> StringTable;
+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;
+extern llvm::cl::list<ModuleSubsection> DumpModuleSubsections;
+extern llvm::cl::opt<bool> DumpModuleSyms;
+} // namespace pdb2yaml
+
+namespace explain {
+enum class InputFileType { PDBFile, PDBStream, DBIStream, Names, ModuleStream };
+
+extern llvm::cl::list<std::string> InputFilename;
+extern llvm::cl::list<uint64_t> Offsets;
+extern llvm::cl::opt<InputFileType> InputType;
+} // namespace explain
+
+namespace exportstream {
+extern llvm::cl::opt<std::string> OutputFile;
+extern llvm::cl::opt<std::string> Stream;
+extern llvm::cl::opt<bool> ForceName;
+} // namespace exportstream
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-pdbutil/ya.make b/contrib/libs/llvm14/tools/llvm-pdbutil/ya.make
new file mode 100644
index 00000000000..47ef71da065
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-pdbutil/ya.make
@@ -0,0 +1,62 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/MSF
+ contrib/libs/llvm14/lib/DebugInfo/PDB
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ObjectYAML
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-pdbutil
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ BytesOutputStyle.cpp
+ DumpOutputStyle.cpp
+ ExplainOutputStyle.cpp
+ FormatUtil.cpp
+ InputFile.cpp
+ LinePrinter.cpp
+ MinimalSymbolDumper.cpp
+ MinimalTypeDumper.cpp
+ PdbYaml.cpp
+ PrettyBuiltinDumper.cpp
+ PrettyClassDefinitionDumper.cpp
+ PrettyClassLayoutGraphicalDumper.cpp
+ PrettyCompilandDumper.cpp
+ PrettyEnumDumper.cpp
+ PrettyExternalSymbolDumper.cpp
+ PrettyFunctionDumper.cpp
+ PrettyTypeDumper.cpp
+ PrettyTypedefDumper.cpp
+ PrettyVariableDumper.cpp
+ StreamUtil.cpp
+ TypeReferenceTracker.cpp
+ YAMLOutputStyle.cpp
+ llvm-pdbutil.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-profdata/llvm-profdata.cpp b/contrib/libs/llvm14/tools/llvm-profdata/llvm-profdata.cpp
new file mode 100644
index 00000000000..6000460d3c2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profdata/llvm-profdata.cpp
@@ -0,0 +1,2669 @@
+//===- llvm-profdata.cpp - LLVM profile data tool -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// llvm-profdata merges .profdata files.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/ProfileData/InstrProfCorrelator.h"
+#include "llvm/ProfileData/InstrProfReader.h"
+#include "llvm/ProfileData/InstrProfWriter.h"
+#include "llvm/ProfileData/ProfileCommon.h"
+#include "llvm/ProfileData/RawMemProfReader.h"
+#include "llvm/ProfileData/SampleProfReader.h"
+#include "llvm/ProfileData/SampleProfWriter.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Discriminator.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+
+using namespace llvm;
+
+enum ProfileFormat {
+ PF_None = 0,
+ PF_Text,
+ PF_Compact_Binary,
+ PF_Ext_Binary,
+ PF_GCC,
+ PF_Binary
+};
+
+static void warn(Twine Message, std::string Whence = "",
+ std::string Hint = "") {
+ WithColor::warning();
+ if (!Whence.empty())
+ errs() << Whence << ": ";
+ errs() << Message << "\n";
+ if (!Hint.empty())
+ WithColor::note() << Hint << "\n";
+}
+
+static void warn(Error E, StringRef Whence = "") {
+ if (E.isA<InstrProfError>()) {
+ handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
+ warn(IPE.message(), std::string(Whence), std::string(""));
+ });
+ }
+}
+
+static void exitWithError(Twine Message, std::string Whence = "",
+ std::string Hint = "") {
+ WithColor::error();
+ if (!Whence.empty())
+ errs() << Whence << ": ";
+ errs() << Message << "\n";
+ if (!Hint.empty())
+ WithColor::note() << Hint << "\n";
+ ::exit(1);
+}
+
+static void exitWithError(Error E, StringRef Whence = "") {
+ if (E.isA<InstrProfError>()) {
+ handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
+ instrprof_error instrError = IPE.get();
+ StringRef Hint = "";
+ if (instrError == instrprof_error::unrecognized_format) {
+ // Hint in case user missed specifying the profile type.
+ Hint = "Perhaps you forgot to use the --sample or --memory option?";
+ }
+ exitWithError(IPE.message(), std::string(Whence), std::string(Hint));
+ });
+ }
+
+ exitWithError(toString(std::move(E)), std::string(Whence));
+}
+
+static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
+ exitWithError(EC.message(), std::string(Whence));
+}
+
+namespace {
+enum ProfileKinds { instr, sample, memory };
+enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid };
+}
+
+static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC,
+ StringRef Whence = "") {
+ if (FailMode == failIfAnyAreInvalid)
+ exitWithErrorCode(EC, Whence);
+ else
+ warn(EC.message(), std::string(Whence));
+}
+
+static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
+ StringRef WhenceFunction = "",
+ bool ShowHint = true) {
+ if (!WhenceFile.empty())
+ errs() << WhenceFile << ": ";
+ if (!WhenceFunction.empty())
+ errs() << WhenceFunction << ": ";
+
+ auto IPE = instrprof_error::success;
+ E = handleErrors(std::move(E),
+ [&IPE](std::unique_ptr<InstrProfError> E) -> Error {
+ IPE = E->get();
+ return Error(std::move(E));
+ });
+ errs() << toString(std::move(E)) << "\n";
+
+ if (ShowHint) {
+ StringRef Hint = "";
+ if (IPE != instrprof_error::success) {
+ switch (IPE) {
+ case instrprof_error::hash_mismatch:
+ case instrprof_error::count_mismatch:
+ case instrprof_error::value_site_count_mismatch:
+ Hint = "Make sure that all profile data to be merged is generated "
+ "from the same binary.";
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!Hint.empty())
+ errs() << Hint << "\n";
+ }
+}
+
+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 = std::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;
+};
+typedef SmallVector<WeightedFile, 5> WeightedFileVector;
+
+/// Keep track of merged data and reported errors.
+struct WriterContext {
+ std::mutex Lock;
+ InstrProfWriter Writer;
+ std::vector<std::pair<Error, std::string>> Errors;
+ std::mutex &ErrLock;
+ SmallSet<instrprof_error, 4> &WriterErrorCodes;
+
+ WriterContext(bool IsSparse, std::mutex &ErrLock,
+ SmallSet<instrprof_error, 4> &WriterErrorCodes)
+ : Writer(IsSparse), ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {
+ }
+};
+
+/// Computer the overlap b/w profile BaseFilename and TestFileName,
+/// and store the program level result to Overlap.
+static void overlapInput(const std::string &BaseFilename,
+ const std::string &TestFilename, WriterContext *WC,
+ OverlapStats &Overlap,
+ const OverlapFuncFilters &FuncFilter,
+ raw_fd_ostream &OS, bool IsCS) {
+ auto ReaderOrErr = InstrProfReader::create(TestFilename);
+ if (Error E = ReaderOrErr.takeError()) {
+ // Skip the empty profiles by returning sliently.
+ instrprof_error IPE = InstrProfError::take(std::move(E));
+ if (IPE != instrprof_error::empty_raw_profile)
+ WC->Errors.emplace_back(make_error<InstrProfError>(IPE), TestFilename);
+ return;
+ }
+
+ auto Reader = std::move(ReaderOrErr.get());
+ for (auto &I : *Reader) {
+ OverlapStats FuncOverlap(OverlapStats::FunctionLevel);
+ FuncOverlap.setFuncInfo(I.Name, I.Hash);
+
+ WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter);
+ FuncOverlap.dump(OS);
+ }
+}
+
+/// Load an input into a writer context.
+static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
+ const InstrProfCorrelator *Correlator,
+ WriterContext *WC) {
+ std::unique_lock<std::mutex> CtxGuard{WC->Lock};
+
+ // Copy the filename, because llvm::ThreadPool copied the input "const
+ // WeightedFile &" by value, making a reference to the filename within it
+ // invalid outside of this packaged task.
+ std::string Filename = Input.Filename;
+
+ auto ReaderOrErr = InstrProfReader::create(Input.Filename, Correlator);
+ if (Error E = ReaderOrErr.takeError()) {
+ // Skip the empty profiles by returning sliently.
+ instrprof_error IPE = InstrProfError::take(std::move(E));
+ if (IPE != instrprof_error::empty_raw_profile)
+ WC->Errors.emplace_back(make_error<InstrProfError>(IPE), Filename);
+ return;
+ }
+
+ auto Reader = std::move(ReaderOrErr.get());
+ if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
+ consumeError(std::move(E));
+ WC->Errors.emplace_back(
+ make_error<StringError>(
+ "Merge IR generated profile with Clang generated profile.",
+ std::error_code()),
+ Filename);
+ return;
+ }
+
+ 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) {
+ if (Reported) {
+ consumeError(std::move(E));
+ return;
+ }
+ Reported = true;
+ // Only show hint the first time an error occurs.
+ instrprof_error IPE = InstrProfError::take(std::move(E));
+ std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
+ bool firstTime = WC->WriterErrorCodes.insert(IPE).second;
+ handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename,
+ FuncName, firstTime);
+ });
+ }
+ if (Reader->hasError())
+ if (Error E = Reader->getError())
+ WC->Errors.emplace_back(std::move(E), Filename);
+}
+
+/// Merge the \p Src writer context into \p Dst.
+static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
+ for (auto &ErrorPair : Src->Errors)
+ Dst->Errors.push_back(std::move(ErrorPair));
+ Src->Errors.clear();
+
+ Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
+ instrprof_error IPE = InstrProfError::take(std::move(E));
+ std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock};
+ bool firstTime = Dst->WriterErrorCodes.insert(IPE).second;
+ if (firstTime)
+ warn(toString(make_error<InstrProfError>(IPE)));
+ });
+}
+
+static void writeInstrProfile(StringRef OutputFilename,
+ ProfileFormat OutputFormat,
+ InstrProfWriter &Writer) {
+ std::error_code EC;
+ raw_fd_ostream Output(OutputFilename.data(), EC,
+ OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF
+ : sys::fs::OF_None);
+ if (EC)
+ exitWithErrorCode(EC, OutputFilename);
+
+ if (OutputFormat == PF_Text) {
+ if (Error E = Writer.writeText(Output))
+ warn(std::move(E));
+ } else {
+ if (Output.is_displayed())
+ exitWithError("cannot write a non-text format profile to the terminal");
+ if (Error E = Writer.write(Output))
+ warn(std::move(E));
+ }
+}
+
+static void mergeInstrProfile(const WeightedFileVector &Inputs,
+ StringRef DebugInfoFilename,
+ SymbolRemapper *Remapper,
+ StringRef OutputFilename,
+ ProfileFormat OutputFormat, bool OutputSparse,
+ unsigned NumThreads, FailureMode FailMode) {
+ if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary &&
+ OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text)
+ exitWithError("unknown format is specified");
+
+ std::unique_ptr<InstrProfCorrelator> Correlator;
+ if (!DebugInfoFilename.empty()) {
+ if (auto Err =
+ InstrProfCorrelator::get(DebugInfoFilename).moveInto(Correlator))
+ exitWithError(std::move(Err), DebugInfoFilename);
+ if (auto Err = Correlator->correlateProfileData())
+ exitWithError(std::move(Err), DebugInfoFilename);
+ }
+
+ std::mutex ErrorLock;
+ SmallSet<instrprof_error, 4> WriterErrorCodes;
+
+ // If NumThreads is not specified, auto-detect a good default.
+ if (NumThreads == 0)
+ NumThreads = std::min(hardware_concurrency().compute_thread_count(),
+ unsigned((Inputs.size() + 1) / 2));
+ // FIXME: There's a bug here, where setting NumThreads = Inputs.size() fails
+ // the merge_empty_profile.test because the InstrProfWriter.ProfileKind isn't
+ // merged, thus the emitted file ends up with a PF_Unknown kind.
+
+ // Initialize the writer contexts.
+ SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
+ for (unsigned I = 0; I < NumThreads; ++I)
+ Contexts.emplace_back(std::make_unique<WriterContext>(
+ OutputSparse, ErrorLock, WriterErrorCodes));
+
+ if (NumThreads == 1) {
+ for (const auto &Input : Inputs)
+ loadInput(Input, Remapper, Correlator.get(), Contexts[0].get());
+ } else {
+ ThreadPool Pool(hardware_concurrency(NumThreads));
+
+ // Load the inputs in parallel (N/NumThreads serial steps).
+ unsigned Ctx = 0;
+ for (const auto &Input : Inputs) {
+ Pool.async(loadInput, Input, Remapper, Correlator.get(),
+ Contexts[Ctx].get());
+ Ctx = (Ctx + 1) % NumThreads;
+ }
+ Pool.wait();
+
+ // Merge the writer contexts together (~ lg(NumThreads) serial steps).
+ unsigned Mid = Contexts.size() / 2;
+ unsigned End = Contexts.size();
+ assert(Mid > 0 && "Expected more than one context");
+ do {
+ for (unsigned I = 0; I < Mid; ++I)
+ Pool.async(mergeWriterContexts, Contexts[I].get(),
+ Contexts[I + Mid].get());
+ Pool.wait();
+ if (End & 1) {
+ Pool.async(mergeWriterContexts, Contexts[0].get(),
+ Contexts[End - 1].get());
+ Pool.wait();
+ }
+ End = Mid;
+ Mid /= 2;
+ } while (Mid > 0);
+ }
+
+ // Handle deferred errors encountered during merging. If the number of errors
+ // is equal to the number of inputs the merge failed.
+ unsigned NumErrors = 0;
+ for (std::unique_ptr<WriterContext> &WC : Contexts) {
+ for (auto &ErrorPair : WC->Errors) {
+ ++NumErrors;
+ warn(toString(std::move(ErrorPair.first)), ErrorPair.second);
+ }
+ }
+ if (NumErrors == Inputs.size() ||
+ (NumErrors > 0 && FailMode == failIfAnyAreInvalid))
+ exitWithError("no profile can be merged");
+
+ writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
+}
+
+/// The profile entry for a function in instrumentation profile.
+struct InstrProfileEntry {
+ uint64_t MaxCount = 0;
+ float ZeroCounterRatio = 0.0;
+ InstrProfRecord *ProfRecord;
+ InstrProfileEntry(InstrProfRecord *Record);
+ InstrProfileEntry() = default;
+};
+
+InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) {
+ ProfRecord = Record;
+ uint64_t CntNum = Record->Counts.size();
+ uint64_t ZeroCntNum = 0;
+ for (size_t I = 0; I < CntNum; ++I) {
+ MaxCount = std::max(MaxCount, Record->Counts[I]);
+ ZeroCntNum += !Record->Counts[I];
+ }
+ ZeroCounterRatio = (float)ZeroCntNum / CntNum;
+}
+
+/// Either set all the counters in the instr profile entry \p IFE to -1
+/// in order to drop the profile or scale up the counters in \p IFP to
+/// be above hot threshold. We use the ratio of zero counters in the
+/// profile of a function to decide the profile is helpful or harmful
+/// for performance, and to choose whether to scale up or drop it.
+static void updateInstrProfileEntry(InstrProfileEntry &IFE,
+ uint64_t HotInstrThreshold,
+ float ZeroCounterThreshold) {
+ InstrProfRecord *ProfRecord = IFE.ProfRecord;
+ if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) {
+ // If all or most of the counters of the function are zero, the
+ // profile is unaccountable and shuld be dropped. Reset all the
+ // counters to be -1 and PGO profile-use will drop the profile.
+ // All counters being -1 also implies that the function is hot so
+ // PGO profile-use will also set the entry count metadata to be
+ // above hot threshold.
+ for (size_t I = 0; I < ProfRecord->Counts.size(); ++I)
+ ProfRecord->Counts[I] = -1;
+ return;
+ }
+
+ // Scale up the MaxCount to be multiple times above hot threshold.
+ const unsigned MultiplyFactor = 3;
+ uint64_t Numerator = HotInstrThreshold * MultiplyFactor;
+ uint64_t Denominator = IFE.MaxCount;
+ ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) {
+ warn(toString(make_error<InstrProfError>(E)));
+ });
+}
+
+const uint64_t ColdPercentileIdx = 15;
+const uint64_t HotPercentileIdx = 11;
+
+using sampleprof::FSDiscriminatorPass;
+
+// Internal options to set FSDiscriminatorPass. Used in merge and show
+// commands.
+static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption(
+ "fs-discriminator-pass", cl::init(PassLast), cl::Hidden,
+ cl::desc("Zero out the discriminator bits for the FS discrimiantor "
+ "pass beyond this value. The enum values are defined in "
+ "Support/Discriminator.h"),
+ cl::values(clEnumVal(Base, "Use base discriminators only"),
+ clEnumVal(Pass1, "Use base and pass 1 discriminators"),
+ clEnumVal(Pass2, "Use base and pass 1-2 discriminators"),
+ clEnumVal(Pass3, "Use base and pass 1-3 discriminators"),
+ clEnumVal(PassLast, "Use all discriminator bits (default)")));
+
+static unsigned getDiscriminatorMask() {
+ return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue()));
+}
+
+/// Adjust the instr profile in \p WC based on the sample profile in
+/// \p Reader.
+static void
+adjustInstrProfile(std::unique_ptr<WriterContext> &WC,
+ std::unique_ptr<sampleprof::SampleProfileReader> &Reader,
+ unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
+ unsigned InstrProfColdThreshold) {
+ // Function to its entry in instr profile.
+ StringMap<InstrProfileEntry> InstrProfileMap;
+ InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs);
+ for (auto &PD : WC->Writer.getProfileData()) {
+ // Populate IPBuilder.
+ for (const auto &PDV : PD.getValue()) {
+ InstrProfRecord Record = PDV.second;
+ IPBuilder.addRecord(Record);
+ }
+
+ // If a function has multiple entries in instr profile, skip it.
+ if (PD.getValue().size() != 1)
+ continue;
+
+ // Initialize InstrProfileMap.
+ InstrProfRecord *R = &PD.getValue().begin()->second;
+ InstrProfileMap[PD.getKey()] = InstrProfileEntry(R);
+ }
+
+ ProfileSummary InstrPS = *IPBuilder.getSummary();
+ ProfileSummary SamplePS = Reader->getSummary();
+
+ // Compute cold thresholds for instr profile and sample profile.
+ uint64_t ColdSampleThreshold =
+ ProfileSummaryBuilder::getEntryForPercentile(
+ SamplePS.getDetailedSummary(),
+ ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
+ .MinCount;
+ uint64_t HotInstrThreshold =
+ ProfileSummaryBuilder::getEntryForPercentile(
+ InstrPS.getDetailedSummary(),
+ ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
+ .MinCount;
+ uint64_t ColdInstrThreshold =
+ InstrProfColdThreshold
+ ? InstrProfColdThreshold
+ : ProfileSummaryBuilder::getEntryForPercentile(
+ InstrPS.getDetailedSummary(),
+ ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
+ .MinCount;
+
+ // Find hot/warm functions in sample profile which is cold in instr profile
+ // and adjust the profiles of those functions in the instr profile.
+ for (const auto &PD : Reader->getProfiles()) {
+ auto &FContext = PD.first;
+ const sampleprof::FunctionSamples &FS = PD.second;
+ auto It = InstrProfileMap.find(FContext.toString());
+ if (FS.getHeadSamples() > ColdSampleThreshold &&
+ It != InstrProfileMap.end() &&
+ It->second.MaxCount <= ColdInstrThreshold &&
+ FS.getBodySamples().size() >= SupplMinSizeThreshold) {
+ updateInstrProfileEntry(It->second, HotInstrThreshold,
+ ZeroCounterThreshold);
+ }
+ }
+}
+
+/// The main function to supplement instr profile with sample profile.
+/// \Inputs contains the instr profile. \p SampleFilename specifies the
+/// sample profile. \p OutputFilename specifies the output profile name.
+/// \p OutputFormat specifies the output profile format. \p OutputSparse
+/// specifies whether to generate sparse profile. \p SupplMinSizeThreshold
+/// specifies the minimal size for the functions whose profile will be
+/// adjusted. \p ZeroCounterThreshold is the threshold to check whether
+/// a function contains too many zero counters and whether its profile
+/// should be dropped. \p InstrProfColdThreshold is the user specified
+/// cold threshold which will override the cold threshold got from the
+/// instr profile summary.
+static void supplementInstrProfile(
+ const WeightedFileVector &Inputs, StringRef SampleFilename,
+ StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse,
+ unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
+ unsigned InstrProfColdThreshold) {
+ if (OutputFilename.compare("-") == 0)
+ exitWithError("cannot write indexed profdata format to stdout");
+ if (Inputs.size() != 1)
+ exitWithError("expect one input to be an instr profile");
+ if (Inputs[0].Weight != 1)
+ exitWithError("expect instr profile doesn't have weight");
+
+ StringRef InstrFilename = Inputs[0].Filename;
+
+ // Read sample profile.
+ LLVMContext Context;
+ auto ReaderOrErr = sampleprof::SampleProfileReader::create(
+ SampleFilename.str(), Context, FSDiscriminatorPassOption);
+ if (std::error_code EC = ReaderOrErr.getError())
+ exitWithErrorCode(EC, SampleFilename);
+ auto Reader = std::move(ReaderOrErr.get());
+ if (std::error_code EC = Reader->read())
+ exitWithErrorCode(EC, SampleFilename);
+
+ // Read instr profile.
+ std::mutex ErrorLock;
+ SmallSet<instrprof_error, 4> WriterErrorCodes;
+ auto WC = std::make_unique<WriterContext>(OutputSparse, ErrorLock,
+ WriterErrorCodes);
+ loadInput(Inputs[0], nullptr, nullptr, WC.get());
+ if (WC->Errors.size() > 0)
+ exitWithError(std::move(WC->Errors[0].first), InstrFilename);
+
+ adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold,
+ InstrProfColdThreshold);
+ writeInstrProfile(OutputFilename, OutputFormat, WC->Writer);
+}
+
+/// 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()) {
+ uint32_t MaskedDiscriminator =
+ BodySample.first.Discriminator & getDiscriminatorMask();
+ Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator,
+ BodySample.second.getSamples());
+ for (const auto &Target : BodySample.second.getCallTargets()) {
+ Result.addCalledTargetSamples(BodySample.first.LineOffset,
+ MaskedDiscriminator,
+ 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[std::string(Remapped.getName())].merge(Remapped));
+ }
+ }
+ return Result;
+}
+
+static sampleprof::SampleProfileFormat FormatMap[] = {
+ sampleprof::SPF_None,
+ sampleprof::SPF_Text,
+ sampleprof::SPF_Compact_Binary,
+ sampleprof::SPF_Ext_Binary,
+ sampleprof::SPF_GCC,
+ sampleprof::SPF_Binary};
+
+static std::unique_ptr<MemoryBuffer>
+getInputFileBuf(const StringRef &InputFile) {
+ if (InputFile == "")
+ return {};
+
+ auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
+ if (!BufOrError)
+ exitWithErrorCode(BufOrError.getError(), InputFile);
+
+ return std::move(*BufOrError);
+}
+
+static void populateProfileSymbolList(MemoryBuffer *Buffer,
+ sampleprof::ProfileSymbolList &PSL) {
+ if (!Buffer)
+ return;
+
+ SmallVector<StringRef, 32> SymbolVec;
+ StringRef Data = Buffer->getBuffer();
+ Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+
+ for (StringRef SymbolStr : SymbolVec)
+ PSL.add(SymbolStr.trim());
+}
+
+static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,
+ ProfileFormat OutputFormat,
+ MemoryBuffer *Buffer,
+ sampleprof::ProfileSymbolList &WriterList,
+ bool CompressAllSections, bool UseMD5,
+ bool GenPartialProfile) {
+ populateProfileSymbolList(Buffer, WriterList);
+ if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)
+ warn("Profile Symbol list is not empty but the output format is not "
+ "ExtBinary format. The list will be lost in the output. ");
+
+ Writer.setProfileSymbolList(&WriterList);
+
+ if (CompressAllSections) {
+ if (OutputFormat != PF_Ext_Binary)
+ warn("-compress-all-section is ignored. Specify -extbinary to enable it");
+ else
+ Writer.setToCompressAllSections();
+ }
+ if (UseMD5) {
+ if (OutputFormat != PF_Ext_Binary)
+ warn("-use-md5 is ignored. Specify -extbinary to enable it");
+ else
+ Writer.setUseMD5();
+ }
+ if (GenPartialProfile) {
+ if (OutputFormat != PF_Ext_Binary)
+ warn("-gen-partial-profile is ignored. Specify -extbinary to enable it");
+ else
+ Writer.setPartialProfile();
+ }
+}
+
+static void
+mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
+ StringRef OutputFilename, ProfileFormat OutputFormat,
+ StringRef ProfileSymbolListFile, bool CompressAllSections,
+ bool UseMD5, bool GenPartialProfile, bool GenCSNestedProfile,
+ bool SampleMergeColdContext, bool SampleTrimColdContext,
+ bool SampleColdContextFrameDepth, FailureMode FailMode) {
+ using namespace sampleprof;
+ SampleProfileMap ProfileMap;
+ SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
+ LLVMContext Context;
+ sampleprof::ProfileSymbolList WriterList;
+ Optional<bool> ProfileIsProbeBased;
+ Optional<bool> ProfileIsCSFlat;
+ for (const auto &Input : Inputs) {
+ auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context,
+ FSDiscriminatorPassOption);
+ if (std::error_code EC = ReaderOrErr.getError()) {
+ warnOrExitGivenError(FailMode, EC, Input.Filename);
+ continue;
+ }
+
+ // We need to keep the readers around until after all the files are
+ // read so that we do not lose the function names stored in each
+ // reader's memory. The function names are needed to write out the
+ // merged profile map.
+ Readers.push_back(std::move(ReaderOrErr.get()));
+ const auto Reader = Readers.back().get();
+ if (std::error_code EC = Reader->read()) {
+ warnOrExitGivenError(FailMode, EC, Input.Filename);
+ Readers.pop_back();
+ continue;
+ }
+
+ SampleProfileMap &Profiles = Reader->getProfiles();
+ if (ProfileIsProbeBased.hasValue() &&
+ ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased)
+ exitWithError(
+ "cannot merge probe-based profile with non-probe-based profile");
+ ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased;
+ if (ProfileIsCSFlat.hasValue() &&
+ ProfileIsCSFlat != FunctionSamples::ProfileIsCSFlat)
+ exitWithError("cannot merge CS profile with non-CS profile");
+ ProfileIsCSFlat = FunctionSamples::ProfileIsCSFlat;
+ for (SampleProfileMap::iterator I = Profiles.begin(), E = Profiles.end();
+ I != E; ++I) {
+ sampleprof_error Result = sampleprof_error::success;
+ FunctionSamples Remapped =
+ Remapper ? remapSamples(I->second, *Remapper, Result)
+ : FunctionSamples();
+ FunctionSamples &Samples = Remapper ? Remapped : I->second;
+ SampleContext FContext = Samples.getContext();
+ MergeResult(Result, ProfileMap[FContext].merge(Samples, Input.Weight));
+ if (Result != sampleprof_error::success) {
+ std::error_code EC = make_error_code(Result);
+ handleMergeWriterError(errorCodeToError(EC), Input.Filename,
+ FContext.toString());
+ }
+ }
+
+ std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
+ Reader->getProfileSymbolList();
+ if (ReaderList)
+ WriterList.merge(*ReaderList);
+ }
+
+ if (ProfileIsCSFlat && (SampleMergeColdContext || SampleTrimColdContext)) {
+ // Use threshold calculated from profile summary unless specified.
+ SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
+ auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
+ uint64_t SampleProfColdThreshold =
+ ProfileSummaryBuilder::getColdCountThreshold(
+ (Summary->getDetailedSummary()));
+
+ // Trim and merge cold context profile using cold threshold above;
+ SampleContextTrimmer(ProfileMap)
+ .trimAndMergeColdContextProfiles(
+ SampleProfColdThreshold, SampleTrimColdContext,
+ SampleMergeColdContext, SampleColdContextFrameDepth, false);
+ }
+
+ if (ProfileIsCSFlat && GenCSNestedProfile) {
+ CSProfileConverter CSConverter(ProfileMap);
+ CSConverter.convertProfiles();
+ ProfileIsCSFlat = FunctionSamples::ProfileIsCSFlat = false;
+ }
+
+ auto WriterOrErr =
+ SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
+ if (std::error_code EC = WriterOrErr.getError())
+ exitWithErrorCode(EC, OutputFilename);
+
+ auto Writer = std::move(WriterOrErr.get());
+ // WriterList will have StringRef refering to string in Buffer.
+ // Make sure Buffer lives as long as WriterList.
+ auto Buffer = getInputFileBuf(ProfileSymbolListFile);
+ handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,
+ CompressAllSections, UseMD5, GenPartialProfile);
+ if (std::error_code EC = Writer->write(ProfileMap))
+ exitWithErrorCode(std::move(EC));
+}
+
+static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
+ StringRef WeightStr, FileName;
+ std::tie(WeightStr, FileName) = WeightedFilename.split(',');
+
+ uint64_t Weight;
+ if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
+ exitWithError("input weight must be a positive integer");
+
+ return {std::string(FileName), Weight};
+}
+
+static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
+ StringRef Filename = WF.Filename;
+ uint64_t Weight = WF.Weight;
+
+ // If it's STDIN just pass it on.
+ if (Filename == "-") {
+ WNI.push_back({std::string(Filename), Weight});
+ return;
+ }
+
+ llvm::sys::fs::file_status Status;
+ llvm::sys::fs::status(Filename, Status);
+ if (!llvm::sys::fs::exists(Status))
+ exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
+ Filename);
+ // If it's a source file, collect it.
+ if (llvm::sys::fs::is_regular_file(Status)) {
+ WNI.push_back({std::string(Filename), Weight});
+ return;
+ }
+
+ if (llvm::sys::fs::is_directory(Status)) {
+ std::error_code EC;
+ for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;
+ F != E && !EC; F.increment(EC)) {
+ if (llvm::sys::fs::is_regular_file(F->path())) {
+ addWeightedInput(WNI, {F->path(), Weight});
+ }
+ }
+ if (EC)
+ exitWithErrorCode(EC, Filename);
+ }
+}
+
+static void parseInputFilenamesFile(MemoryBuffer *Buffer,
+ WeightedFileVector &WFV) {
+ if (!Buffer)
+ return;
+
+ SmallVector<StringRef, 8> Entries;
+ StringRef Data = Buffer->getBuffer();
+ Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+ for (const StringRef &FileWeightEntry : Entries) {
+ StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
+ // Skip comments.
+ if (SanitizedEntry.startswith("#"))
+ continue;
+ // If there's no comma, it's an unweighted profile.
+ else if (!SanitizedEntry.contains(','))
+ addWeightedInput(WFV, {std::string(SanitizedEntry), 1});
+ else
+ addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
+ }
+}
+
+static int merge_main(int argc, const char *argv[]) {
+ cl::list<std::string> InputFilenames(cl::Positional,
+ cl::desc("<filename...>"));
+ cl::list<std::string> WeightedInputFilenames("weighted-input",
+ cl::desc("<weight>,<filename>"));
+ cl::opt<std::string> InputFilenamesFile(
+ "input-files", cl::init(""),
+ cl::desc("Path to file containing newline-separated "
+ "[<weight>,]<filename> entries"));
+ cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"),
+ cl::aliasopt(InputFilenamesFile));
+ 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::desc("Output file"));
+ cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
+ cl::aliasopt(OutputFilename));
+ cl::opt<ProfileKinds> ProfileKind(
+ cl::desc("Profile kind:"), cl::init(instr),
+ cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
+ clEnumVal(sample, "Sample profile")));
+ cl::opt<ProfileFormat> OutputFormat(
+ cl::desc("Format of output profile"), cl::init(PF_Binary),
+ cl::values(
+ clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
+ clEnumValN(PF_Compact_Binary, "compbinary",
+ "Compact binary encoding"),
+ clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"),
+ clEnumValN(PF_Text, "text", "Text encoding"),
+ clEnumValN(PF_GCC, "gcc",
+ "GCC encoding (only meaningful for -sample)")));
+ cl::opt<FailureMode> FailureMode(
+ "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"),
+ cl::values(clEnumValN(failIfAnyAreInvalid, "any",
+ "Fail if any profile is invalid."),
+ clEnumValN(failIfAllAreInvalid, "all",
+ "Fail only if all profiles are invalid.")));
+ cl::opt<bool> OutputSparse("sparse", cl::init(false),
+ cl::desc("Generate a sparse profile (only meaningful for -instr)"));
+ cl::opt<unsigned> NumThreads(
+ "num-threads", cl::init(0),
+ cl::desc("Number of merge threads to use (default: autodetect)"));
+ cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
+ cl::aliasopt(NumThreads));
+ cl::opt<std::string> ProfileSymbolListFile(
+ "prof-sym-list", cl::init(""),
+ cl::desc("Path to file containing the list of function symbols "
+ "used to populate profile symbol list"));
+ cl::opt<bool> CompressAllSections(
+ "compress-all-sections", cl::init(false), cl::Hidden,
+ cl::desc("Compress all sections when writing the profile (only "
+ "meaningful for -extbinary)"));
+ cl::opt<bool> UseMD5(
+ "use-md5", cl::init(false), cl::Hidden,
+ cl::desc("Choose to use MD5 to represent string in name table (only "
+ "meaningful for -extbinary)"));
+ cl::opt<bool> SampleMergeColdContext(
+ "sample-merge-cold-context", cl::init(false), cl::Hidden,
+ cl::desc(
+ "Merge context sample profiles whose count is below cold threshold"));
+ cl::opt<bool> SampleTrimColdContext(
+ "sample-trim-cold-context", cl::init(false), cl::Hidden,
+ cl::desc(
+ "Trim context sample profiles whose count is below cold threshold"));
+ cl::opt<uint32_t> SampleColdContextFrameDepth(
+ "sample-frame-depth-for-cold-context", cl::init(1), cl::ZeroOrMore,
+ cl::desc("Keep the last K frames while merging cold profile. 1 means the "
+ "context-less base profile"));
+ cl::opt<bool> GenPartialProfile(
+ "gen-partial-profile", cl::init(false), cl::Hidden,
+ cl::desc("Generate a partial profile (only meaningful for -extbinary)"));
+ cl::opt<std::string> SupplInstrWithSample(
+ "supplement-instr-with-sample", cl::init(""), cl::Hidden,
+ cl::desc("Supplement an instr profile with sample profile, to correct "
+ "the profile unrepresentativeness issue. The sample "
+ "profile is the input of the flag. Output will be in instr "
+ "format (The flag only works with -instr)"));
+ cl::opt<float> ZeroCounterThreshold(
+ "zero-counter-threshold", cl::init(0.7), cl::Hidden,
+ cl::desc("For the function which is cold in instr profile but hot in "
+ "sample profile, if the ratio of the number of zero counters "
+ "divided by the the total number of counters is above the "
+ "threshold, the profile of the function will be regarded as "
+ "being harmful for performance and will be dropped."));
+ cl::opt<unsigned> SupplMinSizeThreshold(
+ "suppl-min-size-threshold", cl::init(10), cl::Hidden,
+ cl::desc("If the size of a function is smaller than the threshold, "
+ "assume it can be inlined by PGO early inliner and it won't "
+ "be adjusted based on sample profile."));
+ cl::opt<unsigned> InstrProfColdThreshold(
+ "instr-prof-cold-threshold", cl::init(0), cl::Hidden,
+ cl::desc("User specified cold threshold for instr profile which will "
+ "override the cold threshold got from profile summary. "));
+ cl::opt<bool> GenCSNestedProfile(
+ "gen-cs-nested-profile", cl::Hidden, cl::init(false),
+ cl::desc("Generate nested function profiles for CSSPGO"));
+ cl::opt<std::string> DebugInfoFilename(
+ "debug-info", cl::init(""),
+ cl::desc("Use the provided debug info to correlate the raw profile."));
+
+ cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
+
+ WeightedFileVector WeightedInputs;
+ for (StringRef Filename : InputFilenames)
+ addWeightedInput(WeightedInputs, {std::string(Filename), 1});
+ for (StringRef WeightedFilename : WeightedInputFilenames)
+ addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
+
+ // Make sure that the file buffer stays alive for the duration of the
+ // weighted input vector's lifetime.
+ auto Buffer = getInputFileBuf(InputFilenamesFile);
+ parseInputFilenamesFile(Buffer.get(), WeightedInputs);
+
+ if (WeightedInputs.empty())
+ exitWithError("no input files specified. See " +
+ sys::path::filename(argv[0]) + " -help");
+
+ if (DumpInputFileList) {
+ for (auto &WF : WeightedInputs)
+ outs() << WF.Weight << "," << WF.Filename << "\n";
+ return 0;
+ }
+
+ std::unique_ptr<SymbolRemapper> Remapper;
+ if (!RemappingFile.empty())
+ Remapper = SymbolRemapper::create(RemappingFile);
+
+ if (!SupplInstrWithSample.empty()) {
+ if (ProfileKind != instr)
+ exitWithError(
+ "-supplement-instr-with-sample can only work with -instr. ");
+
+ supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputFilename,
+ OutputFormat, OutputSparse, SupplMinSizeThreshold,
+ ZeroCounterThreshold, InstrProfColdThreshold);
+ return 0;
+ }
+
+ if (ProfileKind == instr)
+ mergeInstrProfile(WeightedInputs, DebugInfoFilename, Remapper.get(),
+ OutputFilename, OutputFormat, OutputSparse, NumThreads,
+ FailureMode);
+ else
+ mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename,
+ OutputFormat, ProfileSymbolListFile, CompressAllSections,
+ UseMD5, GenPartialProfile, GenCSNestedProfile,
+ SampleMergeColdContext, SampleTrimColdContext,
+ SampleColdContextFrameDepth, FailureMode);
+ return 0;
+}
+
+/// Computer the overlap b/w profile BaseFilename and profile TestFilename.
+static void overlapInstrProfile(const std::string &BaseFilename,
+ const std::string &TestFilename,
+ const OverlapFuncFilters &FuncFilter,
+ raw_fd_ostream &OS, bool IsCS) {
+ std::mutex ErrorLock;
+ SmallSet<instrprof_error, 4> WriterErrorCodes;
+ WriterContext Context(false, ErrorLock, WriterErrorCodes);
+ WeightedFile WeightedInput{BaseFilename, 1};
+ OverlapStats Overlap;
+ Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS);
+ if (E)
+ exitWithError(std::move(E), "error in getting profile count sums");
+ if (Overlap.Base.CountSum < 1.0f) {
+ OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n";
+ exit(0);
+ }
+ if (Overlap.Test.CountSum < 1.0f) {
+ OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n";
+ exit(0);
+ }
+ loadInput(WeightedInput, nullptr, nullptr, &Context);
+ overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS,
+ IsCS);
+ Overlap.dump(OS);
+}
+
+namespace {
+struct SampleOverlapStats {
+ SampleContext BaseName;
+ SampleContext TestName;
+ // Number of overlap units
+ uint64_t OverlapCount;
+ // Total samples of overlap units
+ uint64_t OverlapSample;
+ // Number of and total samples of units that only present in base or test
+ // profile
+ uint64_t BaseUniqueCount;
+ uint64_t BaseUniqueSample;
+ uint64_t TestUniqueCount;
+ uint64_t TestUniqueSample;
+ // Number of units and total samples in base or test profile
+ uint64_t BaseCount;
+ uint64_t BaseSample;
+ uint64_t TestCount;
+ uint64_t TestSample;
+ // Number of and total samples of units that present in at least one profile
+ uint64_t UnionCount;
+ uint64_t UnionSample;
+ // Weighted similarity
+ double Similarity;
+ // For SampleOverlapStats instances representing functions, weights of the
+ // function in base and test profiles
+ double BaseWeight;
+ double TestWeight;
+
+ SampleOverlapStats()
+ : OverlapCount(0), OverlapSample(0), BaseUniqueCount(0),
+ BaseUniqueSample(0), TestUniqueCount(0), TestUniqueSample(0),
+ BaseCount(0), BaseSample(0), TestCount(0), TestSample(0), UnionCount(0),
+ UnionSample(0), Similarity(0.0), BaseWeight(0.0), TestWeight(0.0) {}
+};
+} // end anonymous namespace
+
+namespace {
+struct FuncSampleStats {
+ uint64_t SampleSum;
+ uint64_t MaxSample;
+ uint64_t HotBlockCount;
+ FuncSampleStats() : SampleSum(0), MaxSample(0), HotBlockCount(0) {}
+ FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample,
+ uint64_t HotBlockCount)
+ : SampleSum(SampleSum), MaxSample(MaxSample),
+ HotBlockCount(HotBlockCount) {}
+};
+} // end anonymous namespace
+
+namespace {
+enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None };
+
+// Class for updating merging steps for two sorted maps. The class should be
+// instantiated with a map iterator type.
+template <class T> class MatchStep {
+public:
+ MatchStep() = delete;
+
+ MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd)
+ : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter),
+ SecondEnd(SecondEnd), Status(MS_None) {}
+
+ bool areBothFinished() const {
+ return (FirstIter == FirstEnd && SecondIter == SecondEnd);
+ }
+
+ bool isFirstFinished() const { return FirstIter == FirstEnd; }
+
+ bool isSecondFinished() const { return SecondIter == SecondEnd; }
+
+ /// Advance one step based on the previous match status unless the previous
+ /// status is MS_None. Then update Status based on the comparison between two
+ /// container iterators at the current step. If the previous status is
+ /// MS_None, it means two iterators are at the beginning and no comparison has
+ /// been made, so we simply update Status without advancing the iterators.
+ void updateOneStep();
+
+ T getFirstIter() const { return FirstIter; }
+
+ T getSecondIter() const { return SecondIter; }
+
+ MatchStatus getMatchStatus() const { return Status; }
+
+private:
+ // Current iterator and end iterator of the first container.
+ T FirstIter;
+ T FirstEnd;
+ // Current iterator and end iterator of the second container.
+ T SecondIter;
+ T SecondEnd;
+ // Match status of the current step.
+ MatchStatus Status;
+};
+} // end anonymous namespace
+
+template <class T> void MatchStep<T>::updateOneStep() {
+ switch (Status) {
+ case MS_Match:
+ ++FirstIter;
+ ++SecondIter;
+ break;
+ case MS_FirstUnique:
+ ++FirstIter;
+ break;
+ case MS_SecondUnique:
+ ++SecondIter;
+ break;
+ case MS_None:
+ break;
+ }
+
+ // Update Status according to iterators at the current step.
+ if (areBothFinished())
+ return;
+ if (FirstIter != FirstEnd &&
+ (SecondIter == SecondEnd || FirstIter->first < SecondIter->first))
+ Status = MS_FirstUnique;
+ else if (SecondIter != SecondEnd &&
+ (FirstIter == FirstEnd || SecondIter->first < FirstIter->first))
+ Status = MS_SecondUnique;
+ else
+ Status = MS_Match;
+}
+
+// Return the sum of line/block samples, the max line/block sample, and the
+// number of line/block samples above the given threshold in a function
+// including its inlinees.
+static void getFuncSampleStats(const sampleprof::FunctionSamples &Func,
+ FuncSampleStats &FuncStats,
+ uint64_t HotThreshold) {
+ for (const auto &L : Func.getBodySamples()) {
+ uint64_t Sample = L.second.getSamples();
+ FuncStats.SampleSum += Sample;
+ FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample);
+ if (Sample >= HotThreshold)
+ ++FuncStats.HotBlockCount;
+ }
+
+ for (const auto &C : Func.getCallsiteSamples()) {
+ for (const auto &F : C.second)
+ getFuncSampleStats(F.second, FuncStats, HotThreshold);
+ }
+}
+
+/// Predicate that determines if a function is hot with a given threshold. We
+/// keep it separate from its callsites for possible extension in the future.
+static bool isFunctionHot(const FuncSampleStats &FuncStats,
+ uint64_t HotThreshold) {
+ // We intentionally compare the maximum sample count in a function with the
+ // HotThreshold to get an approximate determination on hot functions.
+ return (FuncStats.MaxSample >= HotThreshold);
+}
+
+namespace {
+class SampleOverlapAggregator {
+public:
+ SampleOverlapAggregator(const std::string &BaseFilename,
+ const std::string &TestFilename,
+ double LowSimilarityThreshold, double Epsilon,
+ const OverlapFuncFilters &FuncFilter)
+ : BaseFilename(BaseFilename), TestFilename(TestFilename),
+ LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon),
+ FuncFilter(FuncFilter) {}
+
+ /// Detect 0-sample input profile and report to output stream. This interface
+ /// should be called after loadProfiles().
+ bool detectZeroSampleProfile(raw_fd_ostream &OS) const;
+
+ /// Write out function-level similarity statistics for functions specified by
+ /// options --function, --value-cutoff, and --similarity-cutoff.
+ void dumpFuncSimilarity(raw_fd_ostream &OS) const;
+
+ /// Write out program-level similarity and overlap statistics.
+ void dumpProgramSummary(raw_fd_ostream &OS) const;
+
+ /// Write out hot-function and hot-block statistics for base_profile,
+ /// test_profile, and their overlap. For both cases, the overlap HO is
+ /// calculated as follows:
+ /// Given the number of functions (or blocks) that are hot in both profiles
+ /// HCommon and the number of functions (or blocks) that are hot in at
+ /// least one profile HUnion, HO = HCommon / HUnion.
+ void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const;
+
+ /// This function tries matching functions in base and test profiles. For each
+ /// pair of matched functions, it aggregates the function-level
+ /// similarity into a profile-level similarity. It also dump function-level
+ /// similarity information of functions specified by --function,
+ /// --value-cutoff, and --similarity-cutoff options. The program-level
+ /// similarity PS is computed as follows:
+ /// Given function-level similarity FS(A) for all function A, the
+ /// weight of function A in base profile WB(A), and the weight of function
+ /// A in test profile WT(A), compute PS(base_profile, test_profile) =
+ /// sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0
+ /// meaning no-overlap.
+ void computeSampleProfileOverlap(raw_fd_ostream &OS);
+
+ /// Initialize ProfOverlap with the sum of samples in base and test
+ /// profiles. This function also computes and keeps the sum of samples and
+ /// max sample counts of each function in BaseStats and TestStats for later
+ /// use to avoid re-computations.
+ void initializeSampleProfileOverlap();
+
+ /// Load profiles specified by BaseFilename and TestFilename.
+ std::error_code loadProfiles();
+
+ using FuncSampleStatsMap =
+ std::unordered_map<SampleContext, FuncSampleStats, SampleContext::Hash>;
+
+private:
+ SampleOverlapStats ProfOverlap;
+ SampleOverlapStats HotFuncOverlap;
+ SampleOverlapStats HotBlockOverlap;
+ std::string BaseFilename;
+ std::string TestFilename;
+ std::unique_ptr<sampleprof::SampleProfileReader> BaseReader;
+ std::unique_ptr<sampleprof::SampleProfileReader> TestReader;
+ // BaseStats and TestStats hold FuncSampleStats for each function, with
+ // function name as the key.
+ FuncSampleStatsMap BaseStats;
+ FuncSampleStatsMap TestStats;
+ // Low similarity threshold in floating point number
+ double LowSimilarityThreshold;
+ // Block samples above BaseHotThreshold or TestHotThreshold are considered hot
+ // for tracking hot blocks.
+ uint64_t BaseHotThreshold;
+ uint64_t TestHotThreshold;
+ // A small threshold used to round the results of floating point accumulations
+ // to resolve imprecision.
+ const double Epsilon;
+ std::multimap<double, SampleOverlapStats, std::greater<double>>
+ FuncSimilarityDump;
+ // FuncFilter carries specifications in options --value-cutoff and
+ // --function.
+ OverlapFuncFilters FuncFilter;
+ // Column offsets for printing the function-level details table.
+ static const unsigned int TestWeightCol = 15;
+ static const unsigned int SimilarityCol = 30;
+ static const unsigned int OverlapCol = 43;
+ static const unsigned int BaseUniqueCol = 53;
+ static const unsigned int TestUniqueCol = 67;
+ static const unsigned int BaseSampleCol = 81;
+ static const unsigned int TestSampleCol = 96;
+ static const unsigned int FuncNameCol = 111;
+
+ /// Return a similarity of two line/block sample counters in the same
+ /// function in base and test profiles. The line/block-similarity BS(i) is
+ /// computed as follows:
+ /// For an offsets i, given the sample count at i in base profile BB(i),
+ /// the sample count at i in test profile BT(i), the sum of sample counts
+ /// in this function in base profile SB, and the sum of sample counts in
+ /// this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB -
+ /// BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap.
+ double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample,
+ const SampleOverlapStats &FuncOverlap) const;
+
+ void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample,
+ uint64_t HotBlockCount);
+
+ void getHotFunctions(const FuncSampleStatsMap &ProfStats,
+ FuncSampleStatsMap &HotFunc,
+ uint64_t HotThreshold) const;
+
+ void computeHotFuncOverlap();
+
+ /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
+ /// Difference for two sample units in a matched function according to the
+ /// given match status.
+ void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample,
+ uint64_t HotBlockCount,
+ SampleOverlapStats &FuncOverlap,
+ double &Difference, MatchStatus Status);
+
+ /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
+ /// Difference for unmatched callees that only present in one profile in a
+ /// matched caller function.
+ void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func,
+ SampleOverlapStats &FuncOverlap,
+ double &Difference, MatchStatus Status);
+
+ /// This function updates sample overlap statistics of an overlap function in
+ /// base and test profile. It also calculates a function-internal similarity
+ /// FIS as follows:
+ /// For offsets i that have samples in at least one profile in this
+ /// function A, given BS(i) returned by computeBlockSimilarity(), compute
+ /// FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with
+ /// 0.0 meaning no overlap.
+ double computeSampleFunctionInternalOverlap(
+ const sampleprof::FunctionSamples &BaseFunc,
+ const sampleprof::FunctionSamples &TestFunc,
+ SampleOverlapStats &FuncOverlap);
+
+ /// Function-level similarity (FS) is a weighted value over function internal
+ /// similarity (FIS). This function computes a function's FS from its FIS by
+ /// applying the weight.
+ double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample,
+ uint64_t TestFuncSample) const;
+
+ /// The function-level similarity FS(A) for a function A is computed as
+ /// follows:
+ /// Compute a function-internal similarity FIS(A) by
+ /// computeSampleFunctionInternalOverlap(). Then, with the weight of
+ /// function A in base profile WB(A), and the weight of function A in test
+ /// profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A)))
+ /// ranging in [0.0f to 1.0f] with 0.0 meaning no overlap.
+ double
+ computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc,
+ const sampleprof::FunctionSamples *TestFunc,
+ SampleOverlapStats *FuncOverlap,
+ uint64_t BaseFuncSample,
+ uint64_t TestFuncSample);
+
+ /// Profile-level similarity (PS) is a weighted aggregate over function-level
+ /// similarities (FS). This method weights the FS value by the function
+ /// weights in the base and test profiles for the aggregation.
+ double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample,
+ uint64_t TestFuncSample) const;
+};
+} // end anonymous namespace
+
+bool SampleOverlapAggregator::detectZeroSampleProfile(
+ raw_fd_ostream &OS) const {
+ bool HaveZeroSample = false;
+ if (ProfOverlap.BaseSample == 0) {
+ OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n";
+ HaveZeroSample = true;
+ }
+ if (ProfOverlap.TestSample == 0) {
+ OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n";
+ HaveZeroSample = true;
+ }
+ return HaveZeroSample;
+}
+
+double SampleOverlapAggregator::computeBlockSimilarity(
+ uint64_t BaseSample, uint64_t TestSample,
+ const SampleOverlapStats &FuncOverlap) const {
+ double BaseFrac = 0.0;
+ double TestFrac = 0.0;
+ if (FuncOverlap.BaseSample > 0)
+ BaseFrac = static_cast<double>(BaseSample) / FuncOverlap.BaseSample;
+ if (FuncOverlap.TestSample > 0)
+ TestFrac = static_cast<double>(TestSample) / FuncOverlap.TestSample;
+ return 1.0 - std::fabs(BaseFrac - TestFrac);
+}
+
+void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample,
+ uint64_t TestSample,
+ uint64_t HotBlockCount) {
+ bool IsBaseHot = (BaseSample >= BaseHotThreshold);
+ bool IsTestHot = (TestSample >= TestHotThreshold);
+ if (!IsBaseHot && !IsTestHot)
+ return;
+
+ HotBlockOverlap.UnionCount += HotBlockCount;
+ if (IsBaseHot)
+ HotBlockOverlap.BaseCount += HotBlockCount;
+ if (IsTestHot)
+ HotBlockOverlap.TestCount += HotBlockCount;
+ if (IsBaseHot && IsTestHot)
+ HotBlockOverlap.OverlapCount += HotBlockCount;
+}
+
+void SampleOverlapAggregator::getHotFunctions(
+ const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc,
+ uint64_t HotThreshold) const {
+ for (const auto &F : ProfStats) {
+ if (isFunctionHot(F.second, HotThreshold))
+ HotFunc.emplace(F.first, F.second);
+ }
+}
+
+void SampleOverlapAggregator::computeHotFuncOverlap() {
+ FuncSampleStatsMap BaseHotFunc;
+ getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold);
+ HotFuncOverlap.BaseCount = BaseHotFunc.size();
+
+ FuncSampleStatsMap TestHotFunc;
+ getHotFunctions(TestStats, TestHotFunc, TestHotThreshold);
+ HotFuncOverlap.TestCount = TestHotFunc.size();
+ HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount;
+
+ for (const auto &F : BaseHotFunc) {
+ if (TestHotFunc.count(F.first))
+ ++HotFuncOverlap.OverlapCount;
+ else
+ ++HotFuncOverlap.UnionCount;
+ }
+}
+
+void SampleOverlapAggregator::updateOverlapStatsForFunction(
+ uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount,
+ SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) {
+ assert(Status != MS_None &&
+ "Match status should be updated before updating overlap statistics");
+ if (Status == MS_FirstUnique) {
+ TestSample = 0;
+ FuncOverlap.BaseUniqueSample += BaseSample;
+ } else if (Status == MS_SecondUnique) {
+ BaseSample = 0;
+ FuncOverlap.TestUniqueSample += TestSample;
+ } else {
+ ++FuncOverlap.OverlapCount;
+ }
+
+ FuncOverlap.UnionSample += std::max(BaseSample, TestSample);
+ FuncOverlap.OverlapSample += std::min(BaseSample, TestSample);
+ Difference +=
+ 1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap);
+ updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount);
+}
+
+void SampleOverlapAggregator::updateForUnmatchedCallee(
+ const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap,
+ double &Difference, MatchStatus Status) {
+ assert((Status == MS_FirstUnique || Status == MS_SecondUnique) &&
+ "Status must be either of the two unmatched cases");
+ FuncSampleStats FuncStats;
+ if (Status == MS_FirstUnique) {
+ getFuncSampleStats(Func, FuncStats, BaseHotThreshold);
+ updateOverlapStatsForFunction(FuncStats.SampleSum, 0,
+ FuncStats.HotBlockCount, FuncOverlap,
+ Difference, Status);
+ } else {
+ getFuncSampleStats(Func, FuncStats, TestHotThreshold);
+ updateOverlapStatsForFunction(0, FuncStats.SampleSum,
+ FuncStats.HotBlockCount, FuncOverlap,
+ Difference, Status);
+ }
+}
+
+double SampleOverlapAggregator::computeSampleFunctionInternalOverlap(
+ const sampleprof::FunctionSamples &BaseFunc,
+ const sampleprof::FunctionSamples &TestFunc,
+ SampleOverlapStats &FuncOverlap) {
+
+ using namespace sampleprof;
+
+ double Difference = 0;
+
+ // Accumulate Difference for regular line/block samples in the function.
+ // We match them through sort-merge join algorithm because
+ // FunctionSamples::getBodySamples() returns a map of sample counters ordered
+ // by their offsets.
+ MatchStep<BodySampleMap::const_iterator> BlockIterStep(
+ BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(),
+ TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend());
+ BlockIterStep.updateOneStep();
+ while (!BlockIterStep.areBothFinished()) {
+ uint64_t BaseSample =
+ BlockIterStep.isFirstFinished()
+ ? 0
+ : BlockIterStep.getFirstIter()->second.getSamples();
+ uint64_t TestSample =
+ BlockIterStep.isSecondFinished()
+ ? 0
+ : BlockIterStep.getSecondIter()->second.getSamples();
+ updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap,
+ Difference, BlockIterStep.getMatchStatus());
+
+ BlockIterStep.updateOneStep();
+ }
+
+ // Accumulate Difference for callsite lines in the function. We match
+ // them through sort-merge algorithm because
+ // FunctionSamples::getCallsiteSamples() returns a map of callsite records
+ // ordered by their offsets.
+ MatchStep<CallsiteSampleMap::const_iterator> CallsiteIterStep(
+ BaseFunc.getCallsiteSamples().cbegin(),
+ BaseFunc.getCallsiteSamples().cend(),
+ TestFunc.getCallsiteSamples().cbegin(),
+ TestFunc.getCallsiteSamples().cend());
+ CallsiteIterStep.updateOneStep();
+ while (!CallsiteIterStep.areBothFinished()) {
+ MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus();
+ assert(CallsiteStepStatus != MS_None &&
+ "Match status should be updated before entering loop body");
+
+ if (CallsiteStepStatus != MS_Match) {
+ auto Callsite = (CallsiteStepStatus == MS_FirstUnique)
+ ? CallsiteIterStep.getFirstIter()
+ : CallsiteIterStep.getSecondIter();
+ for (const auto &F : Callsite->second)
+ updateForUnmatchedCallee(F.second, FuncOverlap, Difference,
+ CallsiteStepStatus);
+ } else {
+ // There may be multiple inlinees at the same offset, so we need to try
+ // matching all of them. This match is implemented through sort-merge
+ // algorithm because callsite records at the same offset are ordered by
+ // function names.
+ MatchStep<FunctionSamplesMap::const_iterator> CalleeIterStep(
+ CallsiteIterStep.getFirstIter()->second.cbegin(),
+ CallsiteIterStep.getFirstIter()->second.cend(),
+ CallsiteIterStep.getSecondIter()->second.cbegin(),
+ CallsiteIterStep.getSecondIter()->second.cend());
+ CalleeIterStep.updateOneStep();
+ while (!CalleeIterStep.areBothFinished()) {
+ MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus();
+ if (CalleeStepStatus != MS_Match) {
+ auto Callee = (CalleeStepStatus == MS_FirstUnique)
+ ? CalleeIterStep.getFirstIter()
+ : CalleeIterStep.getSecondIter();
+ updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference,
+ CalleeStepStatus);
+ } else {
+ // An inlined function can contain other inlinees inside, so compute
+ // the Difference recursively.
+ Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap(
+ CalleeIterStep.getFirstIter()->second,
+ CalleeIterStep.getSecondIter()->second,
+ FuncOverlap);
+ }
+ CalleeIterStep.updateOneStep();
+ }
+ }
+ CallsiteIterStep.updateOneStep();
+ }
+
+ // Difference reflects the total differences of line/block samples in this
+ // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to
+ // reflect the similarity between function profiles in [0.0f to 1.0f].
+ return (2.0 - Difference) / 2;
+}
+
+double SampleOverlapAggregator::weightForFuncSimilarity(
+ double FuncInternalSimilarity, uint64_t BaseFuncSample,
+ uint64_t TestFuncSample) const {
+ // Compute the weight as the distance between the function weights in two
+ // profiles.
+ double BaseFrac = 0.0;
+ double TestFrac = 0.0;
+ assert(ProfOverlap.BaseSample > 0 &&
+ "Total samples in base profile should be greater than 0");
+ BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample;
+ assert(ProfOverlap.TestSample > 0 &&
+ "Total samples in test profile should be greater than 0");
+ TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample;
+ double WeightDistance = std::fabs(BaseFrac - TestFrac);
+
+ // Take WeightDistance into the similarity.
+ return FuncInternalSimilarity * (1 - WeightDistance);
+}
+
+double
+SampleOverlapAggregator::weightByImportance(double FuncSimilarity,
+ uint64_t BaseFuncSample,
+ uint64_t TestFuncSample) const {
+
+ double BaseFrac = 0.0;
+ double TestFrac = 0.0;
+ assert(ProfOverlap.BaseSample > 0 &&
+ "Total samples in base profile should be greater than 0");
+ BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample / 2.0;
+ assert(ProfOverlap.TestSample > 0 &&
+ "Total samples in test profile should be greater than 0");
+ TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample / 2.0;
+ return FuncSimilarity * (BaseFrac + TestFrac);
+}
+
+double SampleOverlapAggregator::computeSampleFunctionOverlap(
+ const sampleprof::FunctionSamples *BaseFunc,
+ const sampleprof::FunctionSamples *TestFunc,
+ SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample,
+ uint64_t TestFuncSample) {
+ // Default function internal similarity before weighted, meaning two functions
+ // has no overlap.
+ const double DefaultFuncInternalSimilarity = 0;
+ double FuncSimilarity;
+ double FuncInternalSimilarity;
+
+ // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap.
+ // In this case, we use DefaultFuncInternalSimilarity as the function internal
+ // similarity.
+ if (!BaseFunc || !TestFunc) {
+ FuncInternalSimilarity = DefaultFuncInternalSimilarity;
+ } else {
+ assert(FuncOverlap != nullptr &&
+ "FuncOverlap should be provided in this case");
+ FuncInternalSimilarity = computeSampleFunctionInternalOverlap(
+ *BaseFunc, *TestFunc, *FuncOverlap);
+ // Now, FuncInternalSimilarity may be a little less than 0 due to
+ // imprecision of floating point accumulations. Make it zero if the
+ // difference is below Epsilon.
+ FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon)
+ ? 0
+ : FuncInternalSimilarity;
+ }
+ FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity,
+ BaseFuncSample, TestFuncSample);
+ return FuncSimilarity;
+}
+
+void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {
+ using namespace sampleprof;
+
+ std::unordered_map<SampleContext, const FunctionSamples *,
+ SampleContext::Hash>
+ BaseFuncProf;
+ const auto &BaseProfiles = BaseReader->getProfiles();
+ for (const auto &BaseFunc : BaseProfiles) {
+ BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second));
+ }
+ ProfOverlap.UnionCount = BaseFuncProf.size();
+
+ const auto &TestProfiles = TestReader->getProfiles();
+ for (const auto &TestFunc : TestProfiles) {
+ SampleOverlapStats FuncOverlap;
+ FuncOverlap.TestName = TestFunc.second.getContext();
+ assert(TestStats.count(FuncOverlap.TestName) &&
+ "TestStats should have records for all functions in test profile "
+ "except inlinees");
+ FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum;
+
+ bool Matched = false;
+ const auto Match = BaseFuncProf.find(FuncOverlap.TestName);
+ if (Match == BaseFuncProf.end()) {
+ const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName];
+ ++ProfOverlap.TestUniqueCount;
+ ProfOverlap.TestUniqueSample += FuncStats.SampleSum;
+ FuncOverlap.TestUniqueSample = FuncStats.SampleSum;
+
+ updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount);
+
+ double FuncSimilarity = computeSampleFunctionOverlap(
+ nullptr, nullptr, nullptr, 0, FuncStats.SampleSum);
+ ProfOverlap.Similarity +=
+ weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum);
+
+ ++ProfOverlap.UnionCount;
+ ProfOverlap.UnionSample += FuncStats.SampleSum;
+ } else {
+ ++ProfOverlap.OverlapCount;
+
+ // Two functions match with each other. Compute function-level overlap and
+ // aggregate them into profile-level overlap.
+ FuncOverlap.BaseName = Match->second->getContext();
+ assert(BaseStats.count(FuncOverlap.BaseName) &&
+ "BaseStats should have records for all functions in base profile "
+ "except inlinees");
+ FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum;
+
+ FuncOverlap.Similarity = computeSampleFunctionOverlap(
+ Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample,
+ FuncOverlap.TestSample);
+ ProfOverlap.Similarity +=
+ weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample,
+ FuncOverlap.TestSample);
+ ProfOverlap.OverlapSample += FuncOverlap.OverlapSample;
+ ProfOverlap.UnionSample += FuncOverlap.UnionSample;
+
+ // Accumulate the percentage of base unique and test unique samples into
+ // ProfOverlap.
+ ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample;
+ ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample;
+
+ // Remove matched base functions for later reporting functions not found
+ // in test profile.
+ BaseFuncProf.erase(Match);
+ Matched = true;
+ }
+
+ // Print function-level similarity information if specified by options.
+ assert(TestStats.count(FuncOverlap.TestName) &&
+ "TestStats should have records for all functions in test profile "
+ "except inlinees");
+ if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff ||
+ (Matched && FuncOverlap.Similarity < LowSimilarityThreshold) ||
+ (Matched && !FuncFilter.NameFilter.empty() &&
+ FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) !=
+ std::string::npos)) {
+ assert(ProfOverlap.BaseSample > 0 &&
+ "Total samples in base profile should be greater than 0");
+ FuncOverlap.BaseWeight =
+ static_cast<double>(FuncOverlap.BaseSample) / ProfOverlap.BaseSample;
+ assert(ProfOverlap.TestSample > 0 &&
+ "Total samples in test profile should be greater than 0");
+ FuncOverlap.TestWeight =
+ static_cast<double>(FuncOverlap.TestSample) / ProfOverlap.TestSample;
+ FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap);
+ }
+ }
+
+ // Traverse through functions in base profile but not in test profile.
+ for (const auto &F : BaseFuncProf) {
+ assert(BaseStats.count(F.second->getContext()) &&
+ "BaseStats should have records for all functions in base profile "
+ "except inlinees");
+ const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()];
+ ++ProfOverlap.BaseUniqueCount;
+ ProfOverlap.BaseUniqueSample += FuncStats.SampleSum;
+
+ updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount);
+
+ double FuncSimilarity = computeSampleFunctionOverlap(
+ nullptr, nullptr, nullptr, FuncStats.SampleSum, 0);
+ ProfOverlap.Similarity +=
+ weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0);
+
+ ProfOverlap.UnionSample += FuncStats.SampleSum;
+ }
+
+ // Now, ProfSimilarity may be a little greater than 1 due to imprecision
+ // of floating point accumulations. Make it 1.0 if the difference is below
+ // Epsilon.
+ ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon)
+ ? 1
+ : ProfOverlap.Similarity;
+
+ computeHotFuncOverlap();
+}
+
+void SampleOverlapAggregator::initializeSampleProfileOverlap() {
+ const auto &BaseProf = BaseReader->getProfiles();
+ for (const auto &I : BaseProf) {
+ ++ProfOverlap.BaseCount;
+ FuncSampleStats FuncStats;
+ getFuncSampleStats(I.second, FuncStats, BaseHotThreshold);
+ ProfOverlap.BaseSample += FuncStats.SampleSum;
+ BaseStats.emplace(I.second.getContext(), FuncStats);
+ }
+
+ const auto &TestProf = TestReader->getProfiles();
+ for (const auto &I : TestProf) {
+ ++ProfOverlap.TestCount;
+ FuncSampleStats FuncStats;
+ getFuncSampleStats(I.second, FuncStats, TestHotThreshold);
+ ProfOverlap.TestSample += FuncStats.SampleSum;
+ TestStats.emplace(I.second.getContext(), FuncStats);
+ }
+
+ ProfOverlap.BaseName = StringRef(BaseFilename);
+ ProfOverlap.TestName = StringRef(TestFilename);
+}
+
+void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const {
+ using namespace sampleprof;
+
+ if (FuncSimilarityDump.empty())
+ return;
+
+ formatted_raw_ostream FOS(OS);
+ FOS << "Function-level details:\n";
+ FOS << "Base weight";
+ FOS.PadToColumn(TestWeightCol);
+ FOS << "Test weight";
+ FOS.PadToColumn(SimilarityCol);
+ FOS << "Similarity";
+ FOS.PadToColumn(OverlapCol);
+ FOS << "Overlap";
+ FOS.PadToColumn(BaseUniqueCol);
+ FOS << "Base unique";
+ FOS.PadToColumn(TestUniqueCol);
+ FOS << "Test unique";
+ FOS.PadToColumn(BaseSampleCol);
+ FOS << "Base samples";
+ FOS.PadToColumn(TestSampleCol);
+ FOS << "Test samples";
+ FOS.PadToColumn(FuncNameCol);
+ FOS << "Function name\n";
+ for (const auto &F : FuncSimilarityDump) {
+ double OverlapPercent =
+ F.second.UnionSample > 0
+ ? static_cast<double>(F.second.OverlapSample) / F.second.UnionSample
+ : 0;
+ double BaseUniquePercent =
+ F.second.BaseSample > 0
+ ? static_cast<double>(F.second.BaseUniqueSample) /
+ F.second.BaseSample
+ : 0;
+ double TestUniquePercent =
+ F.second.TestSample > 0
+ ? static_cast<double>(F.second.TestUniqueSample) /
+ F.second.TestSample
+ : 0;
+
+ FOS << format("%.2f%%", F.second.BaseWeight * 100);
+ FOS.PadToColumn(TestWeightCol);
+ FOS << format("%.2f%%", F.second.TestWeight * 100);
+ FOS.PadToColumn(SimilarityCol);
+ FOS << format("%.2f%%", F.second.Similarity * 100);
+ FOS.PadToColumn(OverlapCol);
+ FOS << format("%.2f%%", OverlapPercent * 100);
+ FOS.PadToColumn(BaseUniqueCol);
+ FOS << format("%.2f%%", BaseUniquePercent * 100);
+ FOS.PadToColumn(TestUniqueCol);
+ FOS << format("%.2f%%", TestUniquePercent * 100);
+ FOS.PadToColumn(BaseSampleCol);
+ FOS << F.second.BaseSample;
+ FOS.PadToColumn(TestSampleCol);
+ FOS << F.second.TestSample;
+ FOS.PadToColumn(FuncNameCol);
+ FOS << F.second.TestName.toString() << "\n";
+ }
+}
+
+void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const {
+ OS << "Profile overlap infomation for base_profile: "
+ << ProfOverlap.BaseName.toString()
+ << " and test_profile: " << ProfOverlap.TestName.toString()
+ << "\nProgram level:\n";
+
+ OS << " Whole program profile similarity: "
+ << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n";
+
+ assert(ProfOverlap.UnionSample > 0 &&
+ "Total samples in two profile should be greater than 0");
+ double OverlapPercent =
+ static_cast<double>(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample;
+ assert(ProfOverlap.BaseSample > 0 &&
+ "Total samples in base profile should be greater than 0");
+ double BaseUniquePercent = static_cast<double>(ProfOverlap.BaseUniqueSample) /
+ ProfOverlap.BaseSample;
+ assert(ProfOverlap.TestSample > 0 &&
+ "Total samples in test profile should be greater than 0");
+ double TestUniquePercent = static_cast<double>(ProfOverlap.TestUniqueSample) /
+ ProfOverlap.TestSample;
+
+ OS << " Whole program sample overlap: "
+ << format("%.3f%%", OverlapPercent * 100) << "\n";
+ OS << " percentage of samples unique in base profile: "
+ << format("%.3f%%", BaseUniquePercent * 100) << "\n";
+ OS << " percentage of samples unique in test profile: "
+ << format("%.3f%%", TestUniquePercent * 100) << "\n";
+ OS << " total samples in base profile: " << ProfOverlap.BaseSample << "\n"
+ << " total samples in test profile: " << ProfOverlap.TestSample << "\n";
+
+ assert(ProfOverlap.UnionCount > 0 &&
+ "There should be at least one function in two input profiles");
+ double FuncOverlapPercent =
+ static_cast<double>(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount;
+ OS << " Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100)
+ << "\n";
+ OS << " overlap functions: " << ProfOverlap.OverlapCount << "\n";
+ OS << " functions unique in base profile: " << ProfOverlap.BaseUniqueCount
+ << "\n";
+ OS << " functions unique in test profile: " << ProfOverlap.TestUniqueCount
+ << "\n";
+}
+
+void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap(
+ raw_fd_ostream &OS) const {
+ assert(HotFuncOverlap.UnionCount > 0 &&
+ "There should be at least one hot function in two input profiles");
+ OS << " Hot-function overlap: "
+ << format("%.3f%%", static_cast<double>(HotFuncOverlap.OverlapCount) /
+ HotFuncOverlap.UnionCount * 100)
+ << "\n";
+ OS << " overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n";
+ OS << " hot functions unique in base profile: "
+ << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n";
+ OS << " hot functions unique in test profile: "
+ << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n";
+
+ assert(HotBlockOverlap.UnionCount > 0 &&
+ "There should be at least one hot block in two input profiles");
+ OS << " Hot-block overlap: "
+ << format("%.3f%%", static_cast<double>(HotBlockOverlap.OverlapCount) /
+ HotBlockOverlap.UnionCount * 100)
+ << "\n";
+ OS << " overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n";
+ OS << " hot blocks unique in base profile: "
+ << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n";
+ OS << " hot blocks unique in test profile: "
+ << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n";
+}
+
+std::error_code SampleOverlapAggregator::loadProfiles() {
+ using namespace sampleprof;
+
+ LLVMContext Context;
+ auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context,
+ FSDiscriminatorPassOption);
+ if (std::error_code EC = BaseReaderOrErr.getError())
+ exitWithErrorCode(EC, BaseFilename);
+
+ auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context,
+ FSDiscriminatorPassOption);
+ if (std::error_code EC = TestReaderOrErr.getError())
+ exitWithErrorCode(EC, TestFilename);
+
+ BaseReader = std::move(BaseReaderOrErr.get());
+ TestReader = std::move(TestReaderOrErr.get());
+
+ if (std::error_code EC = BaseReader->read())
+ exitWithErrorCode(EC, BaseFilename);
+ if (std::error_code EC = TestReader->read())
+ exitWithErrorCode(EC, TestFilename);
+ if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased())
+ exitWithError(
+ "cannot compare probe-based profile with non-probe-based profile");
+ if (BaseReader->profileIsCSFlat() != TestReader->profileIsCSFlat())
+ exitWithError("cannot compare CS profile with non-CS profile");
+
+ // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in
+ // profile summary.
+ ProfileSummary &BasePS = BaseReader->getSummary();
+ ProfileSummary &TestPS = TestReader->getSummary();
+ BaseHotThreshold =
+ ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary());
+ TestHotThreshold =
+ ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary());
+
+ return std::error_code();
+}
+
+void overlapSampleProfile(const std::string &BaseFilename,
+ const std::string &TestFilename,
+ const OverlapFuncFilters &FuncFilter,
+ uint64_t SimilarityCutoff, raw_fd_ostream &OS) {
+ using namespace sampleprof;
+
+ // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics
+ // report 2--3 places after decimal point in percentage numbers.
+ SampleOverlapAggregator OverlapAggr(
+ BaseFilename, TestFilename,
+ static_cast<double>(SimilarityCutoff) / 1000000, 0.000005, FuncFilter);
+ if (std::error_code EC = OverlapAggr.loadProfiles())
+ exitWithErrorCode(EC);
+
+ OverlapAggr.initializeSampleProfileOverlap();
+ if (OverlapAggr.detectZeroSampleProfile(OS))
+ return;
+
+ OverlapAggr.computeSampleProfileOverlap(OS);
+
+ OverlapAggr.dumpProgramSummary(OS);
+ OverlapAggr.dumpHotFuncAndBlockOverlap(OS);
+ OverlapAggr.dumpFuncSimilarity(OS);
+}
+
+static int overlap_main(int argc, const char *argv[]) {
+ cl::opt<std::string> BaseFilename(cl::Positional, cl::Required,
+ cl::desc("<base profile file>"));
+ cl::opt<std::string> TestFilename(cl::Positional, cl::Required,
+ cl::desc("<test profile file>"));
+ cl::opt<std::string> Output("output", cl::value_desc("output"), cl::init("-"),
+ cl::desc("Output file"));
+ cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output));
+ cl::opt<bool> IsCS(
+ "cs", cl::init(false),
+ cl::desc("For context sensitive PGO counts. Does not work with CSSPGO."));
+ cl::opt<unsigned long long> ValueCutoff(
+ "value-cutoff", cl::init(-1),
+ cl::desc(
+ "Function level overlap information for every function (with calling "
+ "context for csspgo) in test "
+ "profile with max count value greater then the parameter value"));
+ cl::opt<std::string> FuncNameFilter(
+ "function",
+ cl::desc("Function level overlap information for matching functions. For "
+ "CSSPGO this takes a a function name with calling context"));
+ cl::opt<unsigned long long> SimilarityCutoff(
+ "similarity-cutoff", cl::init(0),
+ cl::desc("For sample profiles, list function names (with calling context "
+ "for csspgo) for overlapped functions "
+ "with similarities below the cutoff (percentage times 10000)."));
+ cl::opt<ProfileKinds> ProfileKind(
+ cl::desc("Profile kind:"), cl::init(instr),
+ cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
+ clEnumVal(sample, "Sample profile")));
+ cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n");
+
+ std::error_code EC;
+ raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_TextWithCRLF);
+ if (EC)
+ exitWithErrorCode(EC, Output);
+
+ if (ProfileKind == instr)
+ overlapInstrProfile(BaseFilename, TestFilename,
+ OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS,
+ IsCS);
+ else
+ overlapSampleProfile(BaseFilename, TestFilename,
+ OverlapFuncFilters{ValueCutoff, FuncNameFilter},
+ SimilarityCutoff, OS);
+
+ return 0;
+}
+
+namespace {
+struct ValueSitesStats {
+ ValueSitesStats()
+ : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0),
+ TotalNumValues(0) {}
+ uint64_t TotalNumValueSites;
+ uint64_t TotalNumValueSitesWithValueProfile;
+ uint64_t TotalNumValues;
+ std::vector<unsigned> ValueSitesHistogram;
+};
+} // namespace
+
+static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
+ ValueSitesStats &Stats, raw_fd_ostream &OS,
+ InstrProfSymtab *Symtab) {
+ uint32_t NS = Func.getNumValueSites(VK);
+ Stats.TotalNumValueSites += NS;
+ for (size_t I = 0; I < NS; ++I) {
+ uint32_t NV = Func.getNumValueDataForSite(VK, I);
+ std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I);
+ Stats.TotalNumValues += NV;
+ if (NV) {
+ Stats.TotalNumValueSitesWithValueProfile++;
+ if (NV > Stats.ValueSitesHistogram.size())
+ 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[ " << format("%2u", I) << ", ";
+ if (Symtab == nullptr)
+ OS << format("%4" PRIu64, VD[V].Value);
+ else
+ OS << Symtab->getFuncName(VD[V].Value);
+ OS << ", " << format("%10" PRId64, VD[V].Count) << " ] ("
+ << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n";
+ }
+ }
+}
+
+static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
+ ValueSitesStats &Stats) {
+ OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n";
+ OS << " Total number of sites with values: "
+ << Stats.TotalNumValueSitesWithValueProfile << "\n";
+ OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n";
+
+ OS << " Value sites histogram:\n\tNumTargets, SiteCount\n";
+ for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {
+ if (Stats.ValueSitesHistogram[I] > 0)
+ OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";
+ }
+}
+
+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, bool ShowCS,
+ uint64_t ValueCutoff, bool OnlyListBelow,
+ const std::string &ShowFunction, bool TextFormat,
+ bool ShowBinaryIds, bool ShowCovered,
+ raw_fd_ostream &OS) {
+ auto ReaderOrErr = InstrProfReader::create(Filename);
+ std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
+ if (ShowDetailedSummary && Cutoffs.empty()) {
+ Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990};
+ }
+ InstrProfSummaryBuilder Builder(std::move(Cutoffs));
+ if (Error E = ReaderOrErr.takeError())
+ exitWithError(std::move(E), Filename);
+
+ 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);
+
+ auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
+ const std::pair<std::string, uint64_t> &v2) {
+ return v1.second > v2.second;
+ };
+
+ std::priority_queue<std::pair<std::string, uint64_t>,
+ std::vector<std::pair<std::string, uint64_t>>,
+ 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) {
+ if (Reader->isIRLevelProfile()) {
+ bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash);
+ if (FuncIsCS != ShowCS)
+ continue;
+ }
+ bool Show = ShowAllFunctions ||
+ (!ShowFunction.empty() && Func.Name.contains(ShowFunction));
+
+ bool doTextFormatDump = (Show && TextFormat);
+
+ if (doTextFormatDump) {
+ InstrProfSymtab &Symtab = Reader->getSymtab();
+ InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
+ OS);
+ continue;
+ }
+
+ assert(Func.Counts.size() > 0 && "function missing entry counter");
+ Builder.addRecord(Func);
+
+ if (ShowCovered) {
+ if (std::any_of(Func.Counts.begin(), Func.Counts.end(),
+ [](uint64_t C) { return C; }))
+ OS << Func.Name << "\n";
+ continue;
+ }
+
+ uint64_t FuncMax = 0;
+ uint64_t FuncSum = 0;
+ for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {
+ if (Func.Counts[I] == (uint64_t)-1)
+ continue;
+ 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();
+ HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
+ }
+ } else
+ HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
+ }
+
+ if (Show) {
+ if (!ShownFunctions)
+ OS << "Counters:\n";
+
+ ++ShownFunctions;
+
+ OS << " " << Func.Name << ":\n"
+ << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
+ << " Counters: " << Func.Counts.size() << "\n";
+ if (!IsIRInstr)
+ OS << " Function count: " << Func.Counts[0] << "\n";
+
+ if (ShowIndirectCallTargets)
+ OS << " Indirect Call Site Count: "
+ << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
+
+ uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);
+ if (ShowMemOPSizes && NumMemOPCalls > 0)
+ OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls
+ << "\n";
+
+ if (ShowCounts) {
+ OS << " Block counts: [";
+ size_t Start = (IsIRInstr ? 0 : 1);
+ for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
+ OS << (I == Start ? "" : ", ") << Func.Counts[I];
+ }
+ OS << "]\n";
+ }
+
+ if (ShowIndirectCallTargets) {
+ OS << " Indirect Target Results:\n";
+ traverseAllValueSites(Func, IPVK_IndirectCallTarget,
+ VPStats[IPVK_IndirectCallTarget], OS,
+ &(Reader->getSymtab()));
+ }
+
+ if (ShowMemOPSizes && NumMemOPCalls > 0) {
+ OS << " Memory Intrinsic Size Results:\n";
+ traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,
+ nullptr);
+ }
+ }
+ }
+ if (Reader->hasError())
+ exitWithError(Reader->getError(), Filename);
+
+ if (TextFormat || ShowCovered)
+ return 0;
+ std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
+ bool IsIR = Reader->isIRLevelProfile();
+ OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end");
+ if (IsIR)
+ OS << " entry_first = " << Reader->instrEntryBBEnabled();
+ OS << "\n";
+ 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";
+
+ if (TopN) {
+ std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
+ while (!HottestFuncs.empty()) {
+ SortedHottestFuncs.emplace_back(HottestFuncs.top());
+ HottestFuncs.pop();
+ }
+ OS << "Top " << TopN
+ << " functions with the largest internal block counts: \n";
+ for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
+ OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
+ }
+
+ if (ShownFunctions && ShowIndirectCallTargets) {
+ OS << "Statistics for indirect call sites profile:\n";
+ showValueSitesStats(OS, IPVK_IndirectCallTarget,
+ VPStats[IPVK_IndirectCallTarget]);
+ }
+
+ if (ShownFunctions && ShowMemOPSizes) {
+ OS << "Statistics for memory intrinsic calls sizes profile:\n";
+ showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);
+ }
+
+ if (ShowDetailedSummary) {
+ OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
+ OS << "Total count: " << PS->getTotalCount() << "\n";
+ PS->printDetailedSummary(OS);
+ }
+
+ if (ShowBinaryIds)
+ if (Error E = Reader->printBinaryIds(OS))
+ exitWithError(std::move(E), Filename);
+
+ return 0;
+}
+
+static void showSectionInfo(sampleprof::SampleProfileReader *Reader,
+ raw_fd_ostream &OS) {
+ if (!Reader->dumpSectionInfo(OS)) {
+ WithColor::warning() << "-show-sec-info-only is only supported for "
+ << "sample profile in extbinary format and is "
+ << "ignored for other formats.\n";
+ return;
+ }
+}
+
+namespace {
+struct HotFuncInfo {
+ std::string FuncName;
+ uint64_t TotalCount;
+ double TotalCountPercent;
+ uint64_t MaxCount;
+ uint64_t EntryCount;
+
+ HotFuncInfo()
+ : TotalCount(0), TotalCountPercent(0.0f), MaxCount(0), EntryCount(0) {}
+
+ HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)
+ : FuncName(FN.begin(), FN.end()), TotalCount(TS), TotalCountPercent(TSP),
+ MaxCount(MS), EntryCount(ES) {}
+};
+} // namespace
+
+// Print out detailed information about hot functions in PrintValues vector.
+// Users specify titles and offset of every columns through ColumnTitle and
+// ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same
+// and at least 4. Besides, users can optionally give a HotFuncMetric string to
+// print out or let it be an empty string.
+static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle,
+ const std::vector<int> &ColumnOffset,
+ const std::vector<HotFuncInfo> &PrintValues,
+ uint64_t HotFuncCount, uint64_t TotalFuncCount,
+ uint64_t HotProfCount, uint64_t TotalProfCount,
+ const std::string &HotFuncMetric,
+ uint32_t TopNFunctions, raw_fd_ostream &OS) {
+ assert(ColumnOffset.size() == ColumnTitle.size() &&
+ "ColumnOffset and ColumnTitle should have the same size");
+ assert(ColumnTitle.size() >= 4 &&
+ "ColumnTitle should have at least 4 elements");
+ assert(TotalFuncCount > 0 &&
+ "There should be at least one function in the profile");
+ double TotalProfPercent = 0;
+ if (TotalProfCount > 0)
+ TotalProfPercent = static_cast<double>(HotProfCount) / TotalProfCount * 100;
+
+ formatted_raw_ostream FOS(OS);
+ FOS << HotFuncCount << " out of " << TotalFuncCount
+ << " functions with profile ("
+ << format("%.2f%%",
+ (static_cast<double>(HotFuncCount) / TotalFuncCount * 100))
+ << ") are considered hot functions";
+ if (!HotFuncMetric.empty())
+ FOS << " (" << HotFuncMetric << ")";
+ FOS << ".\n";
+ FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("
+ << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n";
+
+ for (size_t I = 0; I < ColumnTitle.size(); ++I) {
+ FOS.PadToColumn(ColumnOffset[I]);
+ FOS << ColumnTitle[I];
+ }
+ FOS << "\n";
+
+ uint32_t Count = 0;
+ for (const auto &R : PrintValues) {
+ if (TopNFunctions && (Count++ == TopNFunctions))
+ break;
+ FOS.PadToColumn(ColumnOffset[0]);
+ FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")";
+ FOS.PadToColumn(ColumnOffset[1]);
+ FOS << R.MaxCount;
+ FOS.PadToColumn(ColumnOffset[2]);
+ FOS << R.EntryCount;
+ FOS.PadToColumn(ColumnOffset[3]);
+ FOS << R.FuncName << "\n";
+ }
+}
+
+static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles,
+ ProfileSummary &PS, uint32_t TopN,
+ raw_fd_ostream &OS) {
+ using namespace sampleprof;
+
+ const uint32_t HotFuncCutoff = 990000;
+ auto &SummaryVector = PS.getDetailedSummary();
+ uint64_t MinCountThreshold = 0;
+ for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) {
+ if (SummaryEntry.Cutoff == HotFuncCutoff) {
+ MinCountThreshold = SummaryEntry.MinCount;
+ break;
+ }
+ }
+
+ // Traverse all functions in the profile and keep only hot functions.
+ // The following loop also calculates the sum of total samples of all
+ // functions.
+ std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,
+ std::greater<uint64_t>>
+ HotFunc;
+ uint64_t ProfileTotalSample = 0;
+ uint64_t HotFuncSample = 0;
+ uint64_t HotFuncCount = 0;
+
+ for (const auto &I : Profiles) {
+ FuncSampleStats FuncStats;
+ const FunctionSamples &FuncProf = I.second;
+ ProfileTotalSample += FuncProf.getTotalSamples();
+ getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold);
+
+ if (isFunctionHot(FuncStats, MinCountThreshold)) {
+ HotFunc.emplace(FuncProf.getTotalSamples(),
+ std::make_pair(&(I.second), FuncStats.MaxSample));
+ HotFuncSample += FuncProf.getTotalSamples();
+ ++HotFuncCount;
+ }
+ }
+
+ std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",
+ "Entry sample", "Function name"};
+ std::vector<int> ColumnOffset{0, 24, 42, 58};
+ std::string Metric =
+ std::string("max sample >= ") + std::to_string(MinCountThreshold);
+ std::vector<HotFuncInfo> PrintValues;
+ for (const auto &FuncPair : HotFunc) {
+ const FunctionSamples &Func = *FuncPair.second.first;
+ double TotalSamplePercent =
+ (ProfileTotalSample > 0)
+ ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample
+ : 0;
+ PrintValues.emplace_back(HotFuncInfo(
+ Func.getContext().toString(), Func.getTotalSamples(),
+ TotalSamplePercent, FuncPair.second.second, Func.getEntrySamples()));
+ }
+ dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,
+ Profiles.size(), HotFuncSample, ProfileTotalSample,
+ Metric, TopN, OS);
+
+ return 0;
+}
+
+static int showSampleProfile(const std::string &Filename, bool ShowCounts,
+ uint32_t TopN, bool ShowAllFunctions,
+ bool ShowDetailedSummary,
+ const std::string &ShowFunction,
+ bool ShowProfileSymbolList,
+ bool ShowSectionInfoOnly, bool ShowHotFuncList,
+ raw_fd_ostream &OS) {
+ using namespace sampleprof;
+ LLVMContext Context;
+ auto ReaderOrErr =
+ SampleProfileReader::create(Filename, Context, FSDiscriminatorPassOption);
+ if (std::error_code EC = ReaderOrErr.getError())
+ exitWithErrorCode(EC, Filename);
+
+ auto Reader = std::move(ReaderOrErr.get());
+ if (ShowSectionInfoOnly) {
+ showSectionInfo(Reader.get(), OS);
+ return 0;
+ }
+
+ if (std::error_code EC = Reader->read())
+ exitWithErrorCode(EC, Filename);
+
+ if (ShowAllFunctions || ShowFunction.empty())
+ Reader->dump(OS);
+ else
+ // TODO: parse context string to support filtering by contexts.
+ Reader->dumpFunctionProfile(StringRef(ShowFunction), OS);
+
+ if (ShowProfileSymbolList) {
+ std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
+ Reader->getProfileSymbolList();
+ ReaderList->dump(OS);
+ }
+
+ if (ShowDetailedSummary) {
+ auto &PS = Reader->getSummary();
+ PS.printSummary(OS);
+ PS.printDetailedSummary(OS);
+ }
+
+ if (ShowHotFuncList || TopN)
+ showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), TopN, OS);
+
+ return 0;
+}
+
+static int showMemProfProfile(const std::string &Filename, raw_fd_ostream &OS) {
+ auto ReaderOr = llvm::memprof::RawMemProfReader::create(Filename);
+ if (Error E = ReaderOr.takeError())
+ exitWithError(std::move(E), Filename);
+
+ std::unique_ptr<llvm::memprof::RawMemProfReader> Reader(
+ ReaderOr.get().release());
+ Reader->printSummaries(OS);
+ return 0;
+}
+
+static int showDebugInfoCorrelation(const std::string &Filename,
+ bool ShowDetailedSummary,
+ bool ShowProfileSymbolList,
+ raw_fd_ostream &OS) {
+ std::unique_ptr<InstrProfCorrelator> Correlator;
+ if (auto Err = InstrProfCorrelator::get(Filename).moveInto(Correlator))
+ exitWithError(std::move(Err), Filename);
+ if (auto Err = Correlator->correlateProfileData())
+ exitWithError(std::move(Err), Filename);
+
+ InstrProfSymtab Symtab;
+ if (auto Err = Symtab.create(
+ StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize())))
+ exitWithError(std::move(Err), Filename);
+
+ if (ShowProfileSymbolList)
+ Symtab.dumpNames(OS);
+ // TODO: Read "Profile Data Type" from debug info to compute and show how many
+ // counters the section holds.
+ if (ShowDetailedSummary)
+ OS << "Counters section size: 0x"
+ << Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n";
+ OS << "Found " << Correlator->getDataSize() << " functions\n";
+
+ return 0;
+}
+
+static int show_main(int argc, const char *argv[]) {
+ cl::opt<std::string> Filename(cl::Positional, cl::desc("<profdata-file>"));
+
+ cl::opt<bool> ShowCounts("counts", cl::init(false),
+ cl::desc("Show counter values for shown functions"));
+ cl::opt<bool> TextFormat(
+ "text", cl::init(false),
+ cl::desc("Show instr profile data in text dump format"));
+ cl::opt<bool> ShowIndirectCallTargets(
+ "ic-targets", cl::init(false),
+ cl::desc("Show indirect call site target values for shown functions"));
+ cl::opt<bool> ShowMemOPSizes(
+ "memop-sizes", cl::init(false),
+ cl::desc("Show the profiled sizes of the memory intrinsic calls "
+ "for shown functions"));
+ cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
+ cl::desc("Show detailed profile summary"));
+ cl::list<uint32_t> DetailedSummaryCutoffs(
+ cl::CommaSeparated, "detailed-summary-cutoffs",
+ cl::desc(
+ "Cutoff percentages (times 10000) for generating detailed summary"),
+ cl::value_desc("800000,901000,999999"));
+ cl::opt<bool> ShowHotFuncList(
+ "hot-func-list", cl::init(false),
+ cl::desc("Show profile summary of a list of hot functions"));
+ cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
+ cl::desc("Details for every function"));
+ cl::opt<bool> ShowCS("showcs", cl::init(false),
+ cl::desc("Show context sensitive counts"));
+ cl::opt<std::string> ShowFunction("function",
+ cl::desc("Details for matching functions"));
+
+ cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
+ cl::init("-"), cl::desc("Output file"));
+ cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
+ cl::aliasopt(OutputFilename));
+ cl::opt<ProfileKinds> ProfileKind(
+ cl::desc("Profile kind:"), cl::init(instr),
+ cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
+ clEnumVal(sample, "Sample profile"),
+ clEnumVal(memory, "MemProf memory access profile")));
+ 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::opt<bool> ShowProfileSymbolList(
+ "show-prof-sym-list", cl::init(false),
+ cl::desc("Show profile symbol list if it exists in the profile. "));
+ cl::opt<bool> ShowSectionInfoOnly(
+ "show-sec-info-only", cl::init(false),
+ cl::desc("Show the information of each section in the sample profile. "
+ "The flag is only usable when the sample profile is in "
+ "extbinary format"));
+ cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false),
+ cl::desc("Show binary ids in the profile. "));
+ cl::opt<std::string> DebugInfoFilename(
+ "debug-info", cl::init(""),
+ cl::desc("Read and extract profile metadata from debug info and show "
+ "the functions it found."));
+ cl::opt<bool> ShowCovered(
+ "covered", cl::init(false),
+ cl::desc("Show only the functions that have been executed."));
+
+ cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
+
+ if (Filename.empty() && DebugInfoFilename.empty())
+ exitWithError(
+ "the positional argument '<profdata-file>' is required unless '--" +
+ DebugInfoFilename.ArgStr + "' is provided");
+
+ if (Filename == OutputFilename) {
+ errs() << sys::path::filename(argv[0])
+ << ": Input file name cannot be the same as the output file name!\n";
+ return 1;
+ }
+
+ std::error_code EC;
+ raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
+ if (EC)
+ exitWithErrorCode(EC, OutputFilename);
+
+ if (ShowAllFunctions && !ShowFunction.empty())
+ WithColor::warning() << "-function argument ignored: showing all functions\n";
+
+ if (!DebugInfoFilename.empty())
+ return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary,
+ ShowProfileSymbolList, OS);
+
+ if (ProfileKind == instr)
+ return showInstrProfile(
+ Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets,
+ ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs,
+ ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction,
+ TextFormat, ShowBinaryIds, ShowCovered, OS);
+ if (ProfileKind == sample)
+ return showSampleProfile(Filename, ShowCounts, TopNFunctions,
+ ShowAllFunctions, ShowDetailedSummary,
+ ShowFunction, ShowProfileSymbolList,
+ ShowSectionInfoOnly, ShowHotFuncList, OS);
+ return showMemProfProfile(Filename, OS);
+}
+
+int main(int argc, const char *argv[]) {
+ InitLLVM X(argc, argv);
+
+ StringRef ProgName(sys::path::filename(argv[0]));
+ if (argc > 1) {
+ int (*func)(int, const char *[]) = nullptr;
+
+ if (strcmp(argv[1], "merge") == 0)
+ func = merge_main;
+ else if (strcmp(argv[1], "show") == 0)
+ func = show_main;
+ else if (strcmp(argv[1], "overlap") == 0)
+ func = overlap_main;
+
+ if (func) {
+ std::string Invocation(ProgName.str() + " " + argv[1]);
+ argv[1] = Invocation.c_str();
+ return func(argc - 1, argv + 1);
+ }
+
+ if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 ||
+ strcmp(argv[1], "--help") == 0) {
+
+ errs() << "OVERVIEW: LLVM profile data tools\n\n"
+ << "USAGE: " << ProgName << " <command> [args...]\n"
+ << "USAGE: " << ProgName << " <command> -help\n\n"
+ << "See each individual command --help for more details.\n"
+ << "Available commands: merge, show, overlap\n";
+ return 0;
+ }
+ }
+
+ if (argc < 2)
+ errs() << ProgName << ": No command specified!\n";
+ else
+ errs() << ProgName << ": Unknown command!\n";
+
+ errs() << "USAGE: " << ProgName << " <merge|show|overlap> [args...]\n";
+ return 1;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-profdata/ya.make b/contrib/libs/llvm14/tools/llvm-profdata/ya.make
new file mode 100644
index 00000000000..e14c494e3b6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profdata/ya.make
@@ -0,0 +1,39 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-profdata
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-profdata.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/CSPreInliner.cpp b/contrib/libs/llvm14/tools/llvm-profgen/CSPreInliner.cpp
new file mode 100644
index 00000000000..1e642639902
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/CSPreInliner.cpp
@@ -0,0 +1,285 @@
+//===-- CSPreInliner.cpp - Profile guided preinliner -------------- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CSPreInliner.h"
+#include "ProfiledBinary.h"
+#include "llvm/ADT/SCCIterator.h"
+#include "llvm/ADT/Statistic.h"
+#include <cstdint>
+#include <queue>
+
+#define DEBUG_TYPE "cs-preinliner"
+
+using namespace llvm;
+using namespace sampleprof;
+
+STATISTIC(PreInlNumCSInlined,
+ "Number of functions inlined with context sensitive profile");
+STATISTIC(PreInlNumCSNotInlined,
+ "Number of functions not inlined with context sensitive profile");
+STATISTIC(PreInlNumCSInlinedHitMinLimit,
+ "Number of functions with FDO inline stopped due to min size limit");
+STATISTIC(PreInlNumCSInlinedHitMaxLimit,
+ "Number of functions with FDO inline stopped due to max size limit");
+STATISTIC(
+ PreInlNumCSInlinedHitGrowthLimit,
+ "Number of functions with FDO inline stopped due to growth size limit");
+
+// The switches specify inline thresholds used in SampleProfileLoader inlining.
+// TODO: the actual threshold to be tuned here because the size here is based
+// on machine code not LLVM IR.
+extern cl::opt<int> SampleHotCallSiteThreshold;
+extern cl::opt<int> SampleColdCallSiteThreshold;
+extern cl::opt<int> ProfileInlineGrowthLimit;
+extern cl::opt<int> ProfileInlineLimitMin;
+extern cl::opt<int> ProfileInlineLimitMax;
+extern cl::opt<bool> SortProfiledSCC;
+
+cl::opt<bool> EnableCSPreInliner(
+ "csspgo-preinliner", cl::Hidden, cl::init(true),
+ cl::desc("Run a global pre-inliner to merge context profile based on "
+ "estimated global top-down inline decisions"));
+
+cl::opt<bool> UseContextCostForPreInliner(
+ "use-context-cost-for-preinliner", cl::Hidden, cl::init(true),
+ cl::desc("Use context-sensitive byte size cost for preinliner decisions"));
+
+static cl::opt<bool> SamplePreInlineReplay(
+ "csspgo-replay-preinline", cl::Hidden, cl::init(false),
+ cl::desc(
+ "Replay previous inlining and adjust context profile accordingly"));
+
+CSPreInliner::CSPreInliner(SampleProfileMap &Profiles, ProfiledBinary &Binary,
+ uint64_t HotThreshold, uint64_t ColdThreshold)
+ : UseContextCost(UseContextCostForPreInliner),
+ // TODO: Pass in a guid-to-name map in order for
+ // ContextTracker.getFuncNameFor to work, if `Profiles` can have md5 codes
+ // as their profile context.
+ ContextTracker(Profiles, nullptr), ProfileMap(Profiles), Binary(Binary),
+ HotCountThreshold(HotThreshold), ColdCountThreshold(ColdThreshold) {
+ // Set default preinliner hot/cold call site threshold tuned with CSSPGO.
+ // for good performance with reasonable profile size.
+ if (!SampleHotCallSiteThreshold.getNumOccurrences())
+ SampleHotCallSiteThreshold = 1500;
+ if (!SampleColdCallSiteThreshold.getNumOccurrences())
+ SampleColdCallSiteThreshold = 0;
+}
+
+std::vector<StringRef> CSPreInliner::buildTopDownOrder() {
+ std::vector<StringRef> Order;
+ ProfiledCallGraph ProfiledCG(ContextTracker);
+
+ // Now that we have a profiled call graph, construct top-down order
+ // by building up SCC and reversing SCC order.
+ scc_iterator<ProfiledCallGraph *> I = scc_begin(&ProfiledCG);
+ while (!I.isAtEnd()) {
+ auto Range = *I;
+ if (SortProfiledSCC) {
+ // Sort nodes in one SCC based on callsite hotness.
+ scc_member_iterator<ProfiledCallGraph *> SI(*I);
+ Range = *SI;
+ }
+ for (auto *Node : Range) {
+ if (Node != ProfiledCG.getEntryNode())
+ Order.push_back(Node->Name);
+ }
+ ++I;
+ }
+ std::reverse(Order.begin(), Order.end());
+
+ return Order;
+}
+
+bool CSPreInliner::getInlineCandidates(ProfiledCandidateQueue &CQueue,
+ const FunctionSamples *CallerSamples) {
+ assert(CallerSamples && "Expect non-null caller samples");
+
+ // Ideally we want to consider everything a function calls, but as far as
+ // context profile is concerned, only those frames that are children of
+ // current one in the trie is relavent. So we walk the trie instead of call
+ // targets from function profile.
+ ContextTrieNode *CallerNode =
+ ContextTracker.getContextFor(CallerSamples->getContext());
+
+ bool HasNewCandidate = false;
+ for (auto &Child : CallerNode->getAllChildContext()) {
+ ContextTrieNode *CalleeNode = &Child.second;
+ FunctionSamples *CalleeSamples = CalleeNode->getFunctionSamples();
+ if (!CalleeSamples)
+ continue;
+
+ // Call site count is more reliable, so we look up the corresponding call
+ // target profile in caller's context profile to retrieve call site count.
+ uint64_t CalleeEntryCount = CalleeSamples->getEntrySamples();
+ uint64_t CallsiteCount = 0;
+ LineLocation Callsite = CalleeNode->getCallSiteLoc();
+ if (auto CallTargets = CallerSamples->findCallTargetMapAt(Callsite)) {
+ SampleRecord::CallTargetMap &TargetCounts = CallTargets.get();
+ auto It = TargetCounts.find(CalleeSamples->getName());
+ if (It != TargetCounts.end())
+ CallsiteCount = It->second;
+ }
+
+ // TODO: call site and callee entry count should be mostly consistent, add
+ // check for that.
+ HasNewCandidate = true;
+ uint32_t CalleeSize = getFuncSize(*CalleeSamples);
+ CQueue.emplace(CalleeSamples, std::max(CallsiteCount, CalleeEntryCount),
+ CalleeSize);
+ }
+
+ return HasNewCandidate;
+}
+
+uint32_t CSPreInliner::getFuncSize(const FunctionSamples &FSamples) {
+ if (UseContextCost) {
+ return Binary.getFuncSizeForContext(FSamples.getContext());
+ }
+
+ return FSamples.getBodySamples().size();
+}
+
+bool CSPreInliner::shouldInline(ProfiledInlineCandidate &Candidate) {
+ // If replay inline is requested, simply follow the inline decision of the
+ // profiled binary.
+ if (SamplePreInlineReplay)
+ return Candidate.CalleeSamples->getContext().hasAttribute(
+ ContextWasInlined);
+
+ // Adjust threshold based on call site hotness, only do this for callsite
+ // prioritized inliner because otherwise cost-benefit check is done earlier.
+ unsigned int SampleThreshold = SampleColdCallSiteThreshold;
+ if (Candidate.CallsiteCount > HotCountThreshold)
+ SampleThreshold = SampleHotCallSiteThreshold;
+
+ // TODO: for small cold functions, we may inlined them and we need to keep
+ // context profile accordingly.
+ if (Candidate.CallsiteCount < ColdCountThreshold)
+ SampleThreshold = SampleColdCallSiteThreshold;
+
+ return (Candidate.SizeCost < SampleThreshold);
+}
+
+void CSPreInliner::processFunction(const StringRef Name) {
+ FunctionSamples *FSamples = ContextTracker.getBaseSamplesFor(Name);
+ if (!FSamples)
+ return;
+
+ unsigned FuncSize = getFuncSize(*FSamples);
+ unsigned FuncFinalSize = FuncSize;
+ unsigned SizeLimit = FuncSize * ProfileInlineGrowthLimit;
+ SizeLimit = std::min(SizeLimit, (unsigned)ProfileInlineLimitMax);
+ SizeLimit = std::max(SizeLimit, (unsigned)ProfileInlineLimitMin);
+
+ LLVM_DEBUG(dbgs() << "Process " << Name
+ << " for context-sensitive pre-inlining (pre-inline size: "
+ << FuncSize << ", size limit: " << SizeLimit << ")\n");
+
+ ProfiledCandidateQueue CQueue;
+ getInlineCandidates(CQueue, FSamples);
+
+ while (!CQueue.empty() && FuncFinalSize < SizeLimit) {
+ ProfiledInlineCandidate Candidate = CQueue.top();
+ CQueue.pop();
+ bool ShouldInline = false;
+ if ((ShouldInline = shouldInline(Candidate))) {
+ // We mark context as inlined as the corresponding context profile
+ // won't be merged into that function's base profile.
+ ++PreInlNumCSInlined;
+ ContextTracker.markContextSamplesInlined(Candidate.CalleeSamples);
+ Candidate.CalleeSamples->getContext().setAttribute(
+ ContextShouldBeInlined);
+ FuncFinalSize += Candidate.SizeCost;
+ getInlineCandidates(CQueue, Candidate.CalleeSamples);
+ } else {
+ ++PreInlNumCSNotInlined;
+ }
+ LLVM_DEBUG(dbgs() << (ShouldInline ? " Inlined" : " Outlined")
+ << " context profile for: "
+ << Candidate.CalleeSamples->getContext().toString()
+ << " (callee size: " << Candidate.SizeCost
+ << ", call count:" << Candidate.CallsiteCount << ")\n");
+ }
+
+ if (!CQueue.empty()) {
+ if (SizeLimit == (unsigned)ProfileInlineLimitMax)
+ ++PreInlNumCSInlinedHitMaxLimit;
+ else if (SizeLimit == (unsigned)ProfileInlineLimitMin)
+ ++PreInlNumCSInlinedHitMinLimit;
+ else
+ ++PreInlNumCSInlinedHitGrowthLimit;
+ }
+
+ LLVM_DEBUG({
+ if (!CQueue.empty())
+ dbgs() << " Inline candidates ignored due to size limit (inliner "
+ "original size: "
+ << FuncSize << ", inliner final size: " << FuncFinalSize
+ << ", size limit: " << SizeLimit << ")\n";
+
+ while (!CQueue.empty()) {
+ ProfiledInlineCandidate Candidate = CQueue.top();
+ CQueue.pop();
+ bool WasInlined =
+ Candidate.CalleeSamples->getContext().hasAttribute(ContextWasInlined);
+ dbgs() << " " << Candidate.CalleeSamples->getContext().toString()
+ << " (candidate size:" << Candidate.SizeCost
+ << ", call count: " << Candidate.CallsiteCount << ", previously "
+ << (WasInlined ? "inlined)\n" : "not inlined)\n");
+ }
+ });
+}
+
+void CSPreInliner::run() {
+#ifndef NDEBUG
+ auto printProfileNames = [](SampleProfileMap &Profiles, bool IsInput) {
+ dbgs() << (IsInput ? "Input" : "Output") << " context-sensitive profiles ("
+ << Profiles.size() << " total):\n";
+ for (auto &It : Profiles) {
+ const FunctionSamples &Samples = It.second;
+ dbgs() << " [" << Samples.getContext().toString() << "] "
+ << Samples.getTotalSamples() << ":" << Samples.getHeadSamples()
+ << "\n";
+ }
+ };
+#endif
+
+ LLVM_DEBUG(printProfileNames(ProfileMap, true));
+
+ // Execute global pre-inliner to estimate a global top-down inline
+ // decision and merge profiles accordingly. This helps with profile
+ // merge for ThinLTO otherwise we won't be able to merge profiles back
+ // to base profile across module/thin-backend boundaries.
+ // It also helps better compress context profile to control profile
+ // size, as we now only need context profile for functions going to
+ // be inlined.
+ for (StringRef FuncName : buildTopDownOrder()) {
+ processFunction(FuncName);
+ }
+
+ // Not inlined context profiles are merged into its base, so we can
+ // trim out such profiles from the output.
+ std::vector<SampleContext> ProfilesToBeRemoved;
+ for (auto &It : ProfileMap) {
+ SampleContext &Context = It.second.getContext();
+ if (!Context.isBaseContext() && !Context.hasState(InlinedContext)) {
+ assert(Context.hasState(MergedContext) &&
+ "Not inlined context profile should be merged already");
+ ProfilesToBeRemoved.push_back(It.first);
+ }
+ }
+
+ for (auto &ContextName : ProfilesToBeRemoved) {
+ ProfileMap.erase(ContextName);
+ }
+
+ // Make sure ProfileMap's key is consistent with FunctionSamples' name.
+ SampleContextTrimmer(ProfileMap).canonicalizeContextProfiles();
+
+ LLVM_DEBUG(printProfileNames(ProfileMap, false));
+}
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/CSPreInliner.h b/contrib/libs/llvm14/tools/llvm-profgen/CSPreInliner.h
new file mode 100644
index 00000000000..9f63f7ef7be
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/CSPreInliner.h
@@ -0,0 +1,95 @@
+//===-- CSPreInliner.h - Profile guided preinliner ---------------- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_PROFGEN_PGOINLINEADVISOR_H
+#define LLVM_TOOLS_LLVM_PROFGEN_PGOINLINEADVISOR_H
+
+#include "ProfiledBinary.h"
+#include "llvm/ADT/PriorityQueue.h"
+#include "llvm/ProfileData/ProfileCommon.h"
+#include "llvm/ProfileData/SampleProf.h"
+#include "llvm/Transforms/IPO/ProfiledCallGraph.h"
+#include "llvm/Transforms/IPO/SampleContextTracker.h"
+
+using namespace llvm;
+using namespace sampleprof;
+
+namespace llvm {
+namespace sampleprof {
+
+// Inline candidate seen from profile
+struct ProfiledInlineCandidate {
+ ProfiledInlineCandidate(const FunctionSamples *Samples, uint64_t Count,
+ uint32_t Size)
+ : CalleeSamples(Samples), CallsiteCount(Count), SizeCost(Size) {}
+ // Context-sensitive function profile for inline candidate
+ const FunctionSamples *CalleeSamples;
+ // Call site count for an inline candidate
+ // TODO: make sure entry count for context profile and call site
+ // target count for corresponding call are consistent.
+ uint64_t CallsiteCount;
+ // Size proxy for function under particular call context.
+ uint64_t SizeCost;
+};
+
+// Inline candidate comparer using call site weight
+struct ProfiledCandidateComparer {
+ bool operator()(const ProfiledInlineCandidate &LHS,
+ const ProfiledInlineCandidate &RHS) {
+ if (LHS.CallsiteCount != RHS.CallsiteCount)
+ return LHS.CallsiteCount < RHS.CallsiteCount;
+
+ if (LHS.SizeCost != RHS.SizeCost)
+ return LHS.SizeCost > RHS.SizeCost;
+
+ // Tie breaker using GUID so we have stable/deterministic inlining order
+ assert(LHS.CalleeSamples && RHS.CalleeSamples &&
+ "Expect non-null FunctionSamples");
+ return LHS.CalleeSamples->getGUID(LHS.CalleeSamples->getName()) <
+ RHS.CalleeSamples->getGUID(RHS.CalleeSamples->getName());
+ }
+};
+
+using ProfiledCandidateQueue =
+ PriorityQueue<ProfiledInlineCandidate, std::vector<ProfiledInlineCandidate>,
+ ProfiledCandidateComparer>;
+
+// Pre-compilation inliner based on context-sensitive profile.
+// The PreInliner estimates inline decision using hotness from profile
+// and cost estimation from machine code size. It helps merges context
+// profile globally and achieves better post-inine profile quality, which
+// otherwise won't be possible for ThinLTO. It also reduce context profile
+// size by only keep context that is estimated to be inlined.
+class CSPreInliner {
+public:
+ CSPreInliner(SampleProfileMap &Profiles, ProfiledBinary &Binary,
+ uint64_t HotThreshold, uint64_t ColdThreshold);
+ void run();
+
+private:
+ bool getInlineCandidates(ProfiledCandidateQueue &CQueue,
+ const FunctionSamples *FCallerContextSamples);
+ std::vector<StringRef> buildTopDownOrder();
+ void processFunction(StringRef Name);
+ bool shouldInline(ProfiledInlineCandidate &Candidate);
+ uint32_t getFuncSize(const FunctionSamples &FSamples);
+ bool UseContextCost;
+ SampleContextTracker ContextTracker;
+ SampleProfileMap &ProfileMap;
+ ProfiledBinary &Binary;
+
+ // Count thresholds to answer isHotCount and isColdCount queries.
+ // Mirrors the threshold in ProfileSummaryInfo.
+ uint64_t HotCountThreshold;
+ uint64_t ColdCountThreshold;
+};
+
+} // end namespace sampleprof
+} // end namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/CallContext.h b/contrib/libs/llvm14/tools/llvm-profgen/CallContext.h
new file mode 100644
index 00000000000..5e552130d03
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/CallContext.h
@@ -0,0 +1,59 @@
+//===-- CallContext.h - Call Context Handler ---------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_PROFGEN_CALLCONTEXT_H
+#define LLVM_TOOLS_LLVM_PROFGEN_CALLCONTEXT_H
+
+#include "llvm/ProfileData/SampleProf.h"
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace llvm {
+namespace sampleprof {
+
+inline std::string getCallSite(const SampleContextFrame &Callsite) {
+ std::string CallsiteStr = Callsite.FuncName.str();
+ CallsiteStr += ":";
+ CallsiteStr += Twine(Callsite.Location.LineOffset).str();
+ if (Callsite.Location.Discriminator > 0) {
+ CallsiteStr += ".";
+ CallsiteStr += Twine(Callsite.Location.Discriminator).str();
+ }
+ return CallsiteStr;
+}
+
+// TODO: This operation is expansive. If it ever gets called multiple times we
+// may think of making a class wrapper with internal states for it.
+inline std::string getLocWithContext(const SampleContextFrameVector &Context) {
+ std::ostringstream OContextStr;
+ for (const auto &Callsite : Context) {
+ if (OContextStr.str().size())
+ OContextStr << " @ ";
+ OContextStr << getCallSite(Callsite);
+ }
+ return OContextStr.str();
+}
+
+// Reverse call context, i.e., in the order of callee frames to caller frames,
+// is useful during instruction printing or pseudo probe printing.
+inline std::string
+getReversedLocWithContext(const SampleContextFrameVector &Context) {
+ std::ostringstream OContextStr;
+ for (const auto &Callsite : reverse(Context)) {
+ if (OContextStr.str().size())
+ OContextStr << " @ ";
+ OContextStr << getCallSite(Callsite);
+ }
+ return OContextStr.str();
+}
+
+} // end namespace sampleprof
+} // end namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/ErrorHandling.h b/contrib/libs/llvm14/tools/llvm-profgen/ErrorHandling.h
new file mode 100644
index 00000000000..b797add8a89
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/ErrorHandling.h
@@ -0,0 +1,56 @@
+//===-- ErrorHandling.h - Error handler -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_PROFGEN_ERRORHANDLING_H
+#define LLVM_TOOLS_LLVM_PROFGEN_ERRORHANDLING_H
+
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/WithColor.h"
+#include <system_error>
+
+using namespace llvm;
+
+[[noreturn]] inline void exitWithError(const Twine &Message,
+ StringRef Whence = StringRef(),
+ StringRef Hint = StringRef()) {
+ WithColor::error(errs(), "llvm-profgen");
+ if (!Whence.empty())
+ errs() << Whence.str() << ": ";
+ errs() << Message << "\n";
+ if (!Hint.empty())
+ WithColor::note() << Hint.str() << "\n";
+ ::exit(EXIT_FAILURE);
+}
+
+[[noreturn]] inline void exitWithError(std::error_code EC,
+ StringRef Whence = StringRef()) {
+ exitWithError(EC.message(), Whence);
+}
+
+[[noreturn]] inline void exitWithError(Error E, StringRef Whence) {
+ exitWithError(errorToErrorCode(std::move(E)), Whence);
+}
+
+template <typename T, typename... Ts>
+T unwrapOrError(Expected<T> EO, Ts &&... Args) {
+ if (EO)
+ return std::move(*EO);
+ exitWithError(EO.takeError(), std::forward<Ts>(Args)...);
+}
+
+inline void emitWarningSummary(uint64_t Num, uint64_t Total, StringRef Msg) {
+ if (!Total || !Num)
+ return;
+ WithColor::warning() << format("%.2f", static_cast<double>(Num) * 100 / Total)
+ << "%(" << Num << "/" << Total << ") " << Msg << "\n";
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/PerfReader.cpp b/contrib/libs/llvm14/tools/llvm-profgen/PerfReader.cpp
new file mode 100644
index 00000000000..98b4c7cdf16
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/PerfReader.cpp
@@ -0,0 +1,1222 @@
+//===-- PerfReader.cpp - perfscript reader ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "PerfReader.h"
+#include "ProfileGenerator.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Process.h"
+
+#define DEBUG_TYPE "perf-reader"
+
+cl::opt<bool> SkipSymbolization("skip-symbolization", cl::init(false),
+ cl::ZeroOrMore,
+ cl::desc("Dump the unsymbolized profile to the "
+ "output file. It will show unwinder "
+ "output for CS profile generation."));
+
+static cl::opt<bool> ShowMmapEvents("show-mmap-events", cl::init(false),
+ cl::ZeroOrMore,
+ cl::desc("Print binary load events."));
+
+static cl::opt<bool>
+ UseOffset("use-offset", cl::init(true), cl::ZeroOrMore,
+ cl::desc("Work with `--skip-symbolization` or "
+ "`--unsymbolized-profile` to write/read the "
+ "offset instead of virtual address."));
+
+static cl::opt<bool> UseLoadableSegmentAsBase(
+ "use-first-loadable-segment-as-base", cl::init(false), cl::ZeroOrMore,
+ cl::desc("Use first loadable segment address as base address "
+ "for offsets in unsymbolized profile. By default "
+ "first executable segment address is used"));
+
+static cl::opt<bool>
+ IgnoreStackSamples("ignore-stack-samples", cl::init(false), cl::ZeroOrMore,
+ cl::desc("Ignore call stack samples for hybrid samples "
+ "and produce context-insensitive profile."));
+cl::opt<bool> ShowDetailedWarning("show-detailed-warning", cl::init(false),
+ cl::ZeroOrMore,
+ cl::desc("Show detailed warning message."));
+
+extern cl::opt<std::string> PerfTraceFilename;
+extern cl::opt<bool> ShowDisassemblyOnly;
+extern cl::opt<bool> ShowSourceLocations;
+extern cl::opt<std::string> OutputFilename;
+
+namespace llvm {
+namespace sampleprof {
+
+void VirtualUnwinder::unwindCall(UnwindState &State) {
+ uint64_t Source = State.getCurrentLBRSource();
+ // An artificial return should push an external frame and an artificial call
+ // will match it and pop the external frame so that the context before and
+ // after the external call will be the same.
+ if (State.getCurrentLBR().IsArtificial) {
+ NumExtCallBranch++;
+ // A return is matched and pop the external frame.
+ if (State.getParentFrame()->isExternalFrame()) {
+ State.popFrame();
+ } else {
+ // An artificial return is missing, it happens that the sample is just hit
+ // in the middle of the external code. In this case, the leading branch is
+ // a call to external, we just keep unwinding use a context-less stack.
+ if (State.getParentFrame() != State.getDummyRootPtr())
+ NumMissingExternalFrame++;
+ State.clearCallStack();
+ State.pushFrame(Source);
+ State.InstPtr.update(Source);
+ return;
+ }
+ }
+
+ auto *ParentFrame = State.getParentFrame();
+ // The 2nd frame after leaf could be missing if stack sample is
+ // taken when IP is within prolog/epilog, as frame chain isn't
+ // setup yet. Fill in the missing frame in that case.
+ // TODO: Currently we just assume all the addr that can't match the
+ // 2nd frame is in prolog/epilog. In the future, we will switch to
+ // pro/epi tracker(Dwarf CFI) for the precise check.
+ if (ParentFrame == State.getDummyRootPtr() ||
+ ParentFrame->Address != Source) {
+ State.switchToFrame(Source);
+ if (ParentFrame != State.getDummyRootPtr()) {
+ if (State.getCurrentLBR().IsArtificial)
+ NumMismatchedExtCallBranch++;
+ else
+ NumMismatchedProEpiBranch++;
+ }
+ } else {
+ State.popFrame();
+ }
+ State.InstPtr.update(Source);
+}
+
+void VirtualUnwinder::unwindLinear(UnwindState &State, uint64_t Repeat) {
+ InstructionPointer &IP = State.InstPtr;
+ uint64_t Target = State.getCurrentLBRTarget();
+ uint64_t End = IP.Address;
+ if (Binary->usePseudoProbes()) {
+ // We don't need to top frame probe since it should be extracted
+ // from the range.
+ // The outcome of the virtual unwinding with pseudo probes is a
+ // map from a context key to the address range being unwound.
+ // This means basically linear unwinding is not needed for pseudo
+ // probes. The range will be simply recorded here and will be
+ // converted to a list of pseudo probes to report in ProfileGenerator.
+ State.getParentFrame()->recordRangeCount(Target, End, Repeat);
+ } else {
+ // Unwind linear execution part.
+ // Split and record the range by different inline context. For example:
+ // [0x01] ... main:1 # Target
+ // [0x02] ... main:2
+ // [0x03] ... main:3 @ foo:1
+ // [0x04] ... main:3 @ foo:2
+ // [0x05] ... main:3 @ foo:3
+ // [0x06] ... main:4
+ // [0x07] ... main:5 # End
+ // It will be recorded:
+ // [main:*] : [0x06, 0x07], [0x01, 0x02]
+ // [main:3 @ foo:*] : [0x03, 0x05]
+ while (IP.Address > Target) {
+ uint64_t PrevIP = IP.Address;
+ IP.backward();
+ // Break into segments for implicit call/return due to inlining
+ bool SameInlinee = Binary->inlineContextEqual(PrevIP, IP.Address);
+ if (!SameInlinee) {
+ State.switchToFrame(PrevIP);
+ State.CurrentLeafFrame->recordRangeCount(PrevIP, End, Repeat);
+ End = IP.Address;
+ }
+ }
+ assert(IP.Address == Target && "The last one must be the target address.");
+ // Record the remaining range, [0x01, 0x02] in the example
+ State.switchToFrame(IP.Address);
+ State.CurrentLeafFrame->recordRangeCount(IP.Address, End, Repeat);
+ }
+}
+
+void VirtualUnwinder::unwindReturn(UnwindState &State) {
+ // Add extra frame as we unwind through the return
+ const LBREntry &LBR = State.getCurrentLBR();
+ uint64_t CallAddr = Binary->getCallAddrFromFrameAddr(LBR.Target);
+ State.switchToFrame(CallAddr);
+ // Push an external frame for the case of returning to external
+ // address(callback), later if an aitificial call is matched and it will be
+ // popped up. This is to 1)avoid context being interrupted by callback,
+ // context before or after the callback should be the same. 2) the call stack
+ // of function called by callback should be truncated which is done during
+ // recording the context on trie. For example:
+ // main (call)--> foo (call)--> callback (call)--> bar (return)--> callback
+ // (return)--> foo (return)--> main
+ // Context for bar should not include main and foo.
+ // For the code of foo, the context of before and after callback should both
+ // be [foo, main].
+ if (LBR.IsArtificial)
+ State.pushFrame(ExternalAddr);
+ State.pushFrame(LBR.Source);
+ State.InstPtr.update(LBR.Source);
+}
+
+void VirtualUnwinder::unwindBranch(UnwindState &State) {
+ // TODO: Tolerate tail call for now, as we may see tail call from libraries.
+ // This is only for intra function branches, excluding tail calls.
+ uint64_t Source = State.getCurrentLBRSource();
+ State.switchToFrame(Source);
+ State.InstPtr.update(Source);
+}
+
+std::shared_ptr<StringBasedCtxKey> FrameStack::getContextKey() {
+ std::shared_ptr<StringBasedCtxKey> KeyStr =
+ std::make_shared<StringBasedCtxKey>();
+ KeyStr->Context = Binary->getExpandedContext(Stack, KeyStr->WasLeafInlined);
+ if (KeyStr->Context.empty())
+ return nullptr;
+ return KeyStr;
+}
+
+std::shared_ptr<ProbeBasedCtxKey> ProbeStack::getContextKey() {
+ std::shared_ptr<ProbeBasedCtxKey> ProbeBasedKey =
+ std::make_shared<ProbeBasedCtxKey>();
+ for (auto CallProbe : Stack) {
+ ProbeBasedKey->Probes.emplace_back(CallProbe);
+ }
+ CSProfileGenerator::compressRecursionContext<const MCDecodedPseudoProbe *>(
+ ProbeBasedKey->Probes);
+ CSProfileGenerator::trimContext<const MCDecodedPseudoProbe *>(
+ ProbeBasedKey->Probes);
+ return ProbeBasedKey;
+}
+
+template <typename T>
+void VirtualUnwinder::collectSamplesFromFrame(UnwindState::ProfiledFrame *Cur,
+ T &Stack) {
+ if (Cur->RangeSamples.empty() && Cur->BranchSamples.empty())
+ return;
+
+ std::shared_ptr<ContextKey> Key = Stack.getContextKey();
+ if (Key == nullptr)
+ return;
+ auto Ret = CtxCounterMap->emplace(Hashable<ContextKey>(Key), SampleCounter());
+ SampleCounter &SCounter = Ret.first->second;
+ for (auto &Item : Cur->RangeSamples) {
+ uint64_t StartOffset = Binary->virtualAddrToOffset(std::get<0>(Item));
+ uint64_t EndOffset = Binary->virtualAddrToOffset(std::get<1>(Item));
+ SCounter.recordRangeCount(StartOffset, EndOffset, std::get<2>(Item));
+ }
+
+ for (auto &Item : Cur->BranchSamples) {
+ uint64_t SourceOffset = Binary->virtualAddrToOffset(std::get<0>(Item));
+ uint64_t TargetOffset = Binary->virtualAddrToOffset(std::get<1>(Item));
+ SCounter.recordBranchCount(SourceOffset, TargetOffset, std::get<2>(Item));
+ }
+}
+
+template <typename T>
+void VirtualUnwinder::collectSamplesFromFrameTrie(
+ UnwindState::ProfiledFrame *Cur, T &Stack) {
+ if (!Cur->isDummyRoot()) {
+ // Truncate the context for external frame since this isn't a real call
+ // context the compiler will see.
+ if (Cur->isExternalFrame() || !Stack.pushFrame(Cur)) {
+ // Process truncated context
+ // Start a new traversal ignoring its bottom context
+ T EmptyStack(Binary);
+ collectSamplesFromFrame(Cur, EmptyStack);
+ for (const auto &Item : Cur->Children) {
+ collectSamplesFromFrameTrie(Item.second.get(), EmptyStack);
+ }
+
+ // Keep note of untracked call site and deduplicate them
+ // for warning later.
+ if (!Cur->isLeafFrame())
+ UntrackedCallsites.insert(Cur->Address);
+
+ return;
+ }
+ }
+
+ collectSamplesFromFrame(Cur, Stack);
+ // Process children frame
+ for (const auto &Item : Cur->Children) {
+ collectSamplesFromFrameTrie(Item.second.get(), Stack);
+ }
+ // Recover the call stack
+ Stack.popFrame();
+}
+
+void VirtualUnwinder::collectSamplesFromFrameTrie(
+ UnwindState::ProfiledFrame *Cur) {
+ if (Binary->usePseudoProbes()) {
+ ProbeStack Stack(Binary);
+ collectSamplesFromFrameTrie<ProbeStack>(Cur, Stack);
+ } else {
+ FrameStack Stack(Binary);
+ collectSamplesFromFrameTrie<FrameStack>(Cur, Stack);
+ }
+}
+
+void VirtualUnwinder::recordBranchCount(const LBREntry &Branch,
+ UnwindState &State, uint64_t Repeat) {
+ if (Branch.IsArtificial || Branch.Target == ExternalAddr)
+ return;
+
+ if (Binary->usePseudoProbes()) {
+ // Same as recordRangeCount, We don't need to top frame probe since we will
+ // extract it from branch's source address
+ State.getParentFrame()->recordBranchCount(Branch.Source, Branch.Target,
+ Repeat);
+ } else {
+ State.CurrentLeafFrame->recordBranchCount(Branch.Source, Branch.Target,
+ Repeat);
+ }
+}
+
+bool VirtualUnwinder::unwind(const PerfSample *Sample, uint64_t Repeat) {
+ // Capture initial state as starting point for unwinding.
+ UnwindState State(Sample, Binary);
+
+ // Sanity check - making sure leaf of LBR aligns with leaf of stack sample
+ // Stack sample sometimes can be unreliable, so filter out bogus ones.
+ if (!State.validateInitialState())
+ return false;
+
+ // Now process the LBR samples in parrallel with stack sample
+ // Note that we do not reverse the LBR entry order so we can
+ // unwind the sample stack as we walk through LBR entries.
+ while (State.hasNextLBR()) {
+ State.checkStateConsistency();
+
+ // Do not attempt linear unwind for the leaf range as it's incomplete.
+ if (!State.IsLastLBR()) {
+ // Unwind implicit calls/returns from inlining, along the linear path,
+ // break into smaller sub section each with its own calling context.
+ unwindLinear(State, Repeat);
+ }
+
+ // Save the LBR branch before it gets unwound.
+ const LBREntry &Branch = State.getCurrentLBR();
+
+ if (isCallState(State)) {
+ // Unwind calls - we know we encountered call if LBR overlaps with
+ // transition between leaf the 2nd frame. Note that for calls that
+ // were not in the original stack sample, we should have added the
+ // extra frame when processing the return paired with this call.
+ unwindCall(State);
+ } else if (isReturnState(State)) {
+ // Unwind returns - check whether the IP is indeed at a return instruction
+ unwindReturn(State);
+ } else {
+ // Unwind branches
+ // For regular intra function branches, we only need to record branch with
+ // context. For an artificial branch cross function boundaries, we got an
+ // issue with returning to external code. Take the two LBR enties for
+ // example: [foo:8(RETURN), ext:1] [ext:3(CALL), bar:1] After perf reader,
+ // we only get[foo:8(RETURN), bar:1], unwinder will be confused like foo
+ // return to bar. Here we detect and treat this case as BRANCH instead of
+ // RETURN which only update the source address.
+ unwindBranch(State);
+ }
+ State.advanceLBR();
+ // Record `branch` with calling context after unwinding.
+ recordBranchCount(Branch, State, Repeat);
+ }
+ // As samples are aggregated on trie, record them into counter map
+ collectSamplesFromFrameTrie(State.getDummyRootPtr());
+
+ return true;
+}
+
+std::unique_ptr<PerfReaderBase>
+PerfReaderBase::create(ProfiledBinary *Binary, PerfInputFile &PerfInput) {
+ std::unique_ptr<PerfReaderBase> PerfReader;
+
+ if (PerfInput.Format == PerfFormat::UnsymbolizedProfile) {
+ PerfReader.reset(
+ new UnsymbolizedProfileReader(Binary, PerfInput.InputFile));
+ return PerfReader;
+ }
+
+ // For perf data input, we need to convert them into perf script first.
+ if (PerfInput.Format == PerfFormat::PerfData)
+ PerfInput = PerfScriptReader::convertPerfDataToTrace(Binary, PerfInput);
+
+ assert((PerfInput.Format == PerfFormat::PerfScript) &&
+ "Should be a perfscript!");
+
+ PerfInput.Content =
+ PerfScriptReader::checkPerfScriptType(PerfInput.InputFile);
+ if (PerfInput.Content == PerfContent::LBRStack) {
+ PerfReader.reset(new HybridPerfReader(Binary, PerfInput.InputFile));
+ } else if (PerfInput.Content == PerfContent::LBR) {
+ PerfReader.reset(new LBRPerfReader(Binary, PerfInput.InputFile));
+ } else {
+ exitWithError("Unsupported perfscript!");
+ }
+
+ return PerfReader;
+}
+
+PerfInputFile PerfScriptReader::convertPerfDataToTrace(ProfiledBinary *Binary,
+ PerfInputFile &File) {
+ StringRef PerfData = File.InputFile;
+ // Run perf script to retrieve PIDs matching binary we're interested in.
+ auto PerfExecutable = sys::Process::FindInEnvPath("PATH", "perf");
+ if (!PerfExecutable) {
+ exitWithError("Perf not found.");
+ }
+ std::string PerfPath = *PerfExecutable;
+ std::string PerfTraceFile = PerfData.str() + ".script.tmp";
+ StringRef ScriptMMapArgs[] = {PerfPath, "script", "--show-mmap-events",
+ "-F", "comm,pid", "-i",
+ PerfData};
+ Optional<StringRef> Redirects[] = {llvm::None, // Stdin
+ StringRef(PerfTraceFile), // Stdout
+ StringRef(PerfTraceFile)}; // Stderr
+ sys::ExecuteAndWait(PerfPath, ScriptMMapArgs, llvm::None, Redirects);
+
+ // Collect the PIDs
+ TraceStream TraceIt(PerfTraceFile);
+ std::string PIDs;
+ std::unordered_set<uint32_t> PIDSet;
+ while (!TraceIt.isAtEoF()) {
+ MMapEvent MMap;
+ if (isMMap2Event(TraceIt.getCurrentLine()) &&
+ extractMMap2EventForBinary(Binary, TraceIt.getCurrentLine(), MMap)) {
+ auto It = PIDSet.emplace(MMap.PID);
+ if (It.second) {
+ if (!PIDs.empty()) {
+ PIDs.append(",");
+ }
+ PIDs.append(utostr(MMap.PID));
+ }
+ }
+ TraceIt.advance();
+ }
+
+ if (PIDs.empty()) {
+ exitWithError("No relevant mmap event is found in perf data.");
+ }
+
+ // Run perf script again to retrieve events for PIDs collected above
+ StringRef ScriptSampleArgs[] = {PerfPath, "script", "--show-mmap-events",
+ "-F", "ip,brstack", "--pid",
+ PIDs, "-i", PerfData};
+ sys::ExecuteAndWait(PerfPath, ScriptSampleArgs, llvm::None, Redirects);
+
+ return {PerfTraceFile, PerfFormat::PerfScript, PerfContent::UnknownContent};
+}
+
+void PerfScriptReader::updateBinaryAddress(const MMapEvent &Event) {
+ // Drop the event which doesn't belong to user-provided binary
+ StringRef BinaryName = llvm::sys::path::filename(Event.BinaryPath);
+ if (Binary->getName() != BinaryName)
+ return;
+
+ // Drop the event if its image is loaded at the same address
+ if (Event.Address == Binary->getBaseAddress()) {
+ Binary->setIsLoadedByMMap(true);
+ return;
+ }
+
+ if (Event.Offset == Binary->getTextSegmentOffset()) {
+ // A binary image could be unloaded and then reloaded at different
+ // place, so update binary load address.
+ // Only update for the first executable segment and assume all other
+ // segments are loaded at consecutive memory addresses, which is the case on
+ // X64.
+ Binary->setBaseAddress(Event.Address);
+ Binary->setIsLoadedByMMap(true);
+ } else {
+ // Verify segments are loaded consecutively.
+ const auto &Offsets = Binary->getTextSegmentOffsets();
+ auto It = std::lower_bound(Offsets.begin(), Offsets.end(), Event.Offset);
+ if (It != Offsets.end() && *It == Event.Offset) {
+ // The event is for loading a separate executable segment.
+ auto I = std::distance(Offsets.begin(), It);
+ const auto &PreferredAddrs = Binary->getPreferredTextSegmentAddresses();
+ if (PreferredAddrs[I] - Binary->getPreferredBaseAddress() !=
+ Event.Address - Binary->getBaseAddress())
+ exitWithError("Executable segments not loaded consecutively");
+ } else {
+ if (It == Offsets.begin())
+ exitWithError("File offset not found");
+ else {
+ // Find the segment the event falls in. A large segment could be loaded
+ // via multiple mmap calls with consecutive memory addresses.
+ --It;
+ assert(*It < Event.Offset);
+ if (Event.Offset - *It != Event.Address - Binary->getBaseAddress())
+ exitWithError("Segment not loaded by consecutive mmaps");
+ }
+ }
+ }
+}
+
+static std::string getContextKeyStr(ContextKey *K,
+ const ProfiledBinary *Binary) {
+ if (const auto *CtxKey = dyn_cast<StringBasedCtxKey>(K)) {
+ return SampleContext::getContextString(CtxKey->Context);
+ } else if (const auto *CtxKey = dyn_cast<ProbeBasedCtxKey>(K)) {
+ SampleContextFrameVector ContextStack;
+ for (const auto *Probe : CtxKey->Probes) {
+ Binary->getInlineContextForProbe(Probe, ContextStack, true);
+ }
+ // Probe context key at this point does not have leaf probe, so do not
+ // include the leaf inline location.
+ return SampleContext::getContextString(ContextStack, true);
+ } else {
+ llvm_unreachable("unexpected key type");
+ }
+}
+
+void HybridPerfReader::unwindSamples() {
+ if (Binary->useFSDiscriminator())
+ exitWithError("FS discriminator is not supported in CS profile.");
+ VirtualUnwinder Unwinder(&SampleCounters, Binary);
+ for (const auto &Item : AggregatedSamples) {
+ const PerfSample *Sample = Item.first.getPtr();
+ Unwinder.unwind(Sample, Item.second);
+ }
+
+ // Warn about untracked frames due to missing probes.
+ if (ShowDetailedWarning) {
+ for (auto Address : Unwinder.getUntrackedCallsites())
+ WithColor::warning() << "Profile context truncated due to missing probe "
+ << "for call instruction at "
+ << format("0x%" PRIx64, Address) << "\n";
+ }
+
+ emitWarningSummary(Unwinder.getUntrackedCallsites().size(),
+ SampleCounters.size(),
+ "of profiled contexts are truncated due to missing probe "
+ "for call instruction.");
+
+ emitWarningSummary(
+ Unwinder.NumMismatchedExtCallBranch, Unwinder.NumTotalBranches,
+ "of branches'source is a call instruction but doesn't match call frame "
+ "stack, likely due to unwinding error of external frame.");
+
+ emitWarningSummary(
+ Unwinder.NumMismatchedProEpiBranch, Unwinder.NumTotalBranches,
+ "of branches'source is a call instruction but doesn't match call frame "
+ "stack, likely due to frame in prolog/epilog.");
+
+ emitWarningSummary(Unwinder.NumMissingExternalFrame,
+ Unwinder.NumExtCallBranch,
+ "of artificial call branches but doesn't have an external "
+ "frame to match.");
+}
+
+bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
+ SmallVectorImpl<LBREntry> &LBRStack) {
+ // The raw format of LBR stack is like:
+ // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
+ // ... 0x4005c8/0x4005dc/P/-/-/0
+ // It's in FIFO order and seperated by whitespace.
+ SmallVector<StringRef, 32> Records;
+ TraceIt.getCurrentLine().split(Records, " ", -1, false);
+ auto WarnInvalidLBR = [](TraceStream &TraceIt) {
+ WithColor::warning() << "Invalid address in LBR record at line "
+ << TraceIt.getLineNumber() << ": "
+ << TraceIt.getCurrentLine() << "\n";
+ };
+
+ // Skip the leading instruction pointer.
+ size_t Index = 0;
+ uint64_t LeadingAddr;
+ if (!Records.empty() && !Records[0].contains('/')) {
+ if (Records[0].getAsInteger(16, LeadingAddr)) {
+ WarnInvalidLBR(TraceIt);
+ TraceIt.advance();
+ return false;
+ }
+ Index = 1;
+ }
+ // Now extract LBR samples - note that we do not reverse the
+ // LBR entry order so we can unwind the sample stack as we walk
+ // through LBR entries.
+ uint64_t PrevTrDst = 0;
+
+ while (Index < Records.size()) {
+ auto &Token = Records[Index++];
+ if (Token.size() == 0)
+ continue;
+
+ SmallVector<StringRef, 8> Addresses;
+ Token.split(Addresses, "/");
+ uint64_t Src;
+ uint64_t Dst;
+
+ // Stop at broken LBR records.
+ if (Addresses.size() < 2 || Addresses[0].substr(2).getAsInteger(16, Src) ||
+ Addresses[1].substr(2).getAsInteger(16, Dst)) {
+ WarnInvalidLBR(TraceIt);
+ break;
+ }
+
+ bool SrcIsInternal = Binary->addressIsCode(Src);
+ bool DstIsInternal = Binary->addressIsCode(Dst);
+ bool IsExternal = !SrcIsInternal && !DstIsInternal;
+ bool IsIncoming = !SrcIsInternal && DstIsInternal;
+ bool IsOutgoing = SrcIsInternal && !DstIsInternal;
+ bool IsArtificial = false;
+
+ // Ignore branches outside the current binary.
+ if (IsExternal) {
+ if (!PrevTrDst && !LBRStack.empty()) {
+ WithColor::warning()
+ << "Invalid transfer to external code in LBR record at line "
+ << TraceIt.getLineNumber() << ": " << TraceIt.getCurrentLine()
+ << "\n";
+ }
+ // Do not ignore the entire samples, the remaining LBR can still be
+ // unwound using a context-less stack.
+ continue;
+ }
+
+ if (IsOutgoing) {
+ if (!PrevTrDst) {
+ // This is a leading outgoing LBR, we should keep processing the LBRs.
+ if (LBRStack.empty()) {
+ NumLeadingOutgoingLBR++;
+ // Record this LBR since current source and next LBR' target is still
+ // a valid range.
+ LBRStack.emplace_back(LBREntry(Src, ExternalAddr, false));
+ continue;
+ }
+ // This is middle unpaired outgoing jump which is likely due to
+ // interrupt or incomplete LBR trace. Ignore current and subsequent
+ // entries since they are likely in different contexts.
+ break;
+ }
+
+ // For transition to external code, group the Source with the next
+ // availabe transition target.
+ Dst = PrevTrDst;
+ PrevTrDst = 0;
+ IsArtificial = true;
+ } else {
+ if (PrevTrDst) {
+ // If we have seen an incoming transition from external code to internal
+ // code, but not a following outgoing transition, the incoming
+ // transition is likely due to interrupt which is usually unpaired.
+ // Ignore current and subsequent entries since they are likely in
+ // different contexts.
+ break;
+ }
+
+ if (IsIncoming) {
+ // For transition from external code (such as dynamic libraries) to
+ // the current binary, keep track of the branch target which will be
+ // grouped with the Source of the last transition from the current
+ // binary.
+ PrevTrDst = Dst;
+ continue;
+ }
+ }
+
+ // TODO: filter out buggy duplicate branches on Skylake
+
+ LBRStack.emplace_back(LBREntry(Src, Dst, IsArtificial));
+ }
+ TraceIt.advance();
+ return !LBRStack.empty();
+}
+
+bool PerfScriptReader::extractCallstack(TraceStream &TraceIt,
+ SmallVectorImpl<uint64_t> &CallStack) {
+ // The raw format of call stack is like:
+ // 4005dc # leaf frame
+ // 400634
+ // 400684 # root frame
+ // It's in bottom-up order with each frame in one line.
+
+ // Extract stack frames from sample
+ while (!TraceIt.isAtEoF() && !TraceIt.getCurrentLine().startswith(" 0x")) {
+ StringRef FrameStr = TraceIt.getCurrentLine().ltrim();
+ uint64_t FrameAddr = 0;
+ if (FrameStr.getAsInteger(16, FrameAddr)) {
+ // We might parse a non-perf sample line like empty line and comments,
+ // skip it
+ TraceIt.advance();
+ return false;
+ }
+ TraceIt.advance();
+ // Currently intermixed frame from different binaries is not supported.
+ if (!Binary->addressIsCode(FrameAddr)) {
+ if (CallStack.empty())
+ NumLeafExternalFrame++;
+ // Push a special value(ExternalAddr) for the external frames so that
+ // unwinder can still work on this with artificial Call/Return branch.
+ // After unwinding, the context will be truncated for external frame.
+ // Also deduplicate the consecutive external addresses.
+ if (CallStack.empty() || CallStack.back() != ExternalAddr)
+ CallStack.emplace_back(ExternalAddr);
+ continue;
+ }
+
+ // We need to translate return address to call address for non-leaf frames.
+ if (!CallStack.empty()) {
+ auto CallAddr = Binary->getCallAddrFromFrameAddr(FrameAddr);
+ if (!CallAddr) {
+ // Stop at an invalid return address caused by bad unwinding. This could
+ // happen to frame-pointer-based unwinding and the callee functions that
+ // do not have the frame pointer chain set up.
+ InvalidReturnAddresses.insert(FrameAddr);
+ break;
+ }
+ FrameAddr = CallAddr;
+ }
+
+ CallStack.emplace_back(FrameAddr);
+ }
+
+ // Strip out the bottom external addr.
+ if (CallStack.size() > 1 && CallStack.back() == ExternalAddr)
+ CallStack.pop_back();
+
+ // Skip other unrelated line, find the next valid LBR line
+ // Note that even for empty call stack, we should skip the address at the
+ // bottom, otherwise the following pass may generate a truncated callstack
+ while (!TraceIt.isAtEoF() && !TraceIt.getCurrentLine().startswith(" 0x")) {
+ TraceIt.advance();
+ }
+ // Filter out broken stack sample. We may not have complete frame info
+ // if sample end up in prolog/epilog, the result is dangling context not
+ // connected to entry point. This should be relatively rare thus not much
+ // impact on overall profile quality. However we do want to filter them
+ // out to reduce the number of different calling contexts. One instance
+ // of such case - when sample landed in prolog/epilog, somehow stack
+ // walking will be broken in an unexpected way that higher frames will be
+ // missing.
+ return !CallStack.empty() &&
+ !Binary->addressInPrologEpilog(CallStack.front());
+}
+
+void PerfScriptReader::warnIfMissingMMap() {
+ if (!Binary->getMissingMMapWarned() && !Binary->getIsLoadedByMMap()) {
+ WithColor::warning() << "No relevant mmap event is matched for "
+ << Binary->getName()
+ << ", will use preferred address ("
+ << format("0x%" PRIx64,
+ Binary->getPreferredBaseAddress())
+ << ") as the base loading address!\n";
+ // Avoid redundant warning, only warn at the first unmatched sample.
+ Binary->setMissingMMapWarned(true);
+ }
+}
+
+void HybridPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
+ // The raw hybird sample started with call stack in FILO order and followed
+ // intermediately by LBR sample
+ // e.g.
+ // 4005dc # call stack leaf
+ // 400634
+ // 400684 # call stack root
+ // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
+ // ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries
+ //
+ std::shared_ptr<PerfSample> Sample = std::make_shared<PerfSample>();
+
+ // Parsing call stack and populate into PerfSample.CallStack
+ if (!extractCallstack(TraceIt, Sample->CallStack)) {
+ // Skip the next LBR line matched current call stack
+ if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().startswith(" 0x"))
+ TraceIt.advance();
+ return;
+ }
+
+ warnIfMissingMMap();
+
+ if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().startswith(" 0x")) {
+ // Parsing LBR stack and populate into PerfSample.LBRStack
+ if (extractLBRStack(TraceIt, Sample->LBRStack)) {
+ if (IgnoreStackSamples) {
+ Sample->CallStack.clear();
+ } else {
+ // Canonicalize stack leaf to avoid 'random' IP from leaf frame skew LBR
+ // ranges
+ Sample->CallStack.front() = Sample->LBRStack[0].Target;
+ }
+ // Record samples by aggregation
+ AggregatedSamples[Hashable<PerfSample>(Sample)] += Count;
+ }
+ } else {
+ // LBR sample is encoded in single line after stack sample
+ exitWithError("'Hybrid perf sample is corrupted, No LBR sample line");
+ }
+}
+
+void PerfScriptReader::writeUnsymbolizedProfile(StringRef Filename) {
+ std::error_code EC;
+ raw_fd_ostream OS(Filename, EC, llvm::sys::fs::OF_TextWithCRLF);
+ if (EC)
+ exitWithError(EC, Filename);
+ writeUnsymbolizedProfile(OS);
+}
+
+// Use ordered map to make the output deterministic
+using OrderedCounterForPrint = std::map<std::string, SampleCounter *>;
+
+void PerfScriptReader::writeUnsymbolizedProfile(raw_fd_ostream &OS) {
+ OrderedCounterForPrint OrderedCounters;
+ for (auto &CI : SampleCounters) {
+ OrderedCounters[getContextKeyStr(CI.first.getPtr(), Binary)] = &CI.second;
+ }
+
+ auto SCounterPrinter = [&](RangeSample &Counter, StringRef Separator,
+ uint32_t Indent) {
+ OS.indent(Indent);
+ OS << Counter.size() << "\n";
+ for (auto &I : Counter) {
+ uint64_t Start = I.first.first;
+ uint64_t End = I.first.second;
+
+ if (!UseOffset || (UseOffset && UseLoadableSegmentAsBase)) {
+ Start = Binary->offsetToVirtualAddr(Start);
+ End = Binary->offsetToVirtualAddr(End);
+ }
+
+ if (UseOffset && UseLoadableSegmentAsBase) {
+ Start -= Binary->getFirstLoadableAddress();
+ End -= Binary->getFirstLoadableAddress();
+ }
+
+ OS.indent(Indent);
+ OS << Twine::utohexstr(Start) << Separator << Twine::utohexstr(End) << ":"
+ << I.second << "\n";
+ }
+ };
+
+ for (auto &CI : OrderedCounters) {
+ uint32_t Indent = 0;
+ if (ProfileIsCSFlat) {
+ // Context string key
+ OS << "[" << CI.first << "]\n";
+ Indent = 2;
+ }
+
+ SampleCounter &Counter = *CI.second;
+ SCounterPrinter(Counter.RangeCounter, "-", Indent);
+ SCounterPrinter(Counter.BranchCounter, "->", Indent);
+ }
+}
+
+// Format of input:
+// number of entries in RangeCounter
+// from_1-to_1:count_1
+// from_2-to_2:count_2
+// ......
+// from_n-to_n:count_n
+// number of entries in BranchCounter
+// src_1->dst_1:count_1
+// src_2->dst_2:count_2
+// ......
+// src_n->dst_n:count_n
+void UnsymbolizedProfileReader::readSampleCounters(TraceStream &TraceIt,
+ SampleCounter &SCounters) {
+ auto exitWithErrorForTraceLine = [](TraceStream &TraceIt) {
+ std::string Msg = TraceIt.isAtEoF()
+ ? "Invalid raw profile!"
+ : "Invalid raw profile at line " +
+ Twine(TraceIt.getLineNumber()).str() + ": " +
+ TraceIt.getCurrentLine().str();
+ exitWithError(Msg);
+ };
+ auto ReadNumber = [&](uint64_t &Num) {
+ if (TraceIt.isAtEoF())
+ exitWithErrorForTraceLine(TraceIt);
+ if (TraceIt.getCurrentLine().ltrim().getAsInteger(10, Num))
+ exitWithErrorForTraceLine(TraceIt);
+ TraceIt.advance();
+ };
+
+ auto ReadCounter = [&](RangeSample &Counter, StringRef Separator) {
+ uint64_t Num = 0;
+ ReadNumber(Num);
+ while (Num--) {
+ if (TraceIt.isAtEoF())
+ exitWithErrorForTraceLine(TraceIt);
+ StringRef Line = TraceIt.getCurrentLine().ltrim();
+
+ uint64_t Count = 0;
+ auto LineSplit = Line.split(":");
+ if (LineSplit.second.empty() || LineSplit.second.getAsInteger(10, Count))
+ exitWithErrorForTraceLine(TraceIt);
+
+ uint64_t Source = 0;
+ uint64_t Target = 0;
+ auto Range = LineSplit.first.split(Separator);
+ if (Range.second.empty() || Range.first.getAsInteger(16, Source) ||
+ Range.second.getAsInteger(16, Target))
+ exitWithErrorForTraceLine(TraceIt);
+
+ if (!UseOffset || (UseOffset && UseLoadableSegmentAsBase)) {
+ uint64_t BaseAddr = 0;
+ if (UseOffset && UseLoadableSegmentAsBase)
+ BaseAddr = Binary->getFirstLoadableAddress();
+
+ Source = Binary->virtualAddrToOffset(Source + BaseAddr);
+ Target = Binary->virtualAddrToOffset(Target + BaseAddr);
+ }
+
+ Counter[{Source, Target}] += Count;
+ TraceIt.advance();
+ }
+ };
+
+ ReadCounter(SCounters.RangeCounter, "-");
+ ReadCounter(SCounters.BranchCounter, "->");
+}
+
+void UnsymbolizedProfileReader::readUnsymbolizedProfile(StringRef FileName) {
+ TraceStream TraceIt(FileName);
+ while (!TraceIt.isAtEoF()) {
+ std::shared_ptr<StringBasedCtxKey> Key =
+ std::make_shared<StringBasedCtxKey>();
+ StringRef Line = TraceIt.getCurrentLine();
+ // Read context stack for CS profile.
+ if (Line.startswith("[")) {
+ ProfileIsCSFlat = true;
+ auto I = ContextStrSet.insert(Line.str());
+ SampleContext::createCtxVectorFromStr(*I.first, Key->Context);
+ TraceIt.advance();
+ }
+ auto Ret =
+ SampleCounters.emplace(Hashable<ContextKey>(Key), SampleCounter());
+ readSampleCounters(TraceIt, Ret.first->second);
+ }
+}
+
+void UnsymbolizedProfileReader::parsePerfTraces() {
+ readUnsymbolizedProfile(PerfTraceFile);
+}
+
+void PerfScriptReader::computeCounterFromLBR(const PerfSample *Sample,
+ uint64_t Repeat) {
+ SampleCounter &Counter = SampleCounters.begin()->second;
+ uint64_t EndOffeset = 0;
+ for (const LBREntry &LBR : Sample->LBRStack) {
+ assert(LBR.Source != ExternalAddr &&
+ "Branch' source should not be an external address, it should be "
+ "converted to aritificial branch.");
+ uint64_t SourceOffset = Binary->virtualAddrToOffset(LBR.Source);
+ uint64_t TargetOffset = LBR.Target == static_cast<uint64_t>(ExternalAddr)
+ ? static_cast<uint64_t>(ExternalAddr)
+ : Binary->virtualAddrToOffset(LBR.Target);
+
+ if (!LBR.IsArtificial && TargetOffset != ExternalAddr) {
+ Counter.recordBranchCount(SourceOffset, TargetOffset, Repeat);
+ }
+
+ // If this not the first LBR, update the range count between TO of current
+ // LBR and FROM of next LBR.
+ uint64_t StartOffset = TargetOffset;
+ if (EndOffeset != 0)
+ Counter.recordRangeCount(StartOffset, EndOffeset, Repeat);
+ EndOffeset = SourceOffset;
+ }
+}
+
+void LBRPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
+ std::shared_ptr<PerfSample> Sample = std::make_shared<PerfSample>();
+ // Parsing LBR stack and populate into PerfSample.LBRStack
+ if (extractLBRStack(TraceIt, Sample->LBRStack)) {
+ warnIfMissingMMap();
+ // Record LBR only samples by aggregation
+ AggregatedSamples[Hashable<PerfSample>(Sample)] += Count;
+ }
+}
+
+void PerfScriptReader::generateUnsymbolizedProfile() {
+ // There is no context for LBR only sample, so initialize one entry with
+ // fake "empty" context key.
+ assert(SampleCounters.empty() &&
+ "Sample counter map should be empty before raw profile generation");
+ std::shared_ptr<StringBasedCtxKey> Key =
+ std::make_shared<StringBasedCtxKey>();
+ SampleCounters.emplace(Hashable<ContextKey>(Key), SampleCounter());
+ for (const auto &Item : AggregatedSamples) {
+ const PerfSample *Sample = Item.first.getPtr();
+ computeCounterFromLBR(Sample, Item.second);
+ }
+}
+
+uint64_t PerfScriptReader::parseAggregatedCount(TraceStream &TraceIt) {
+ // The aggregated count is optional, so do not skip the line and return 1 if
+ // it's unmatched
+ uint64_t Count = 1;
+ if (!TraceIt.getCurrentLine().getAsInteger(10, Count))
+ TraceIt.advance();
+ return Count;
+}
+
+void PerfScriptReader::parseSample(TraceStream &TraceIt) {
+ NumTotalSample++;
+ uint64_t Count = parseAggregatedCount(TraceIt);
+ assert(Count >= 1 && "Aggregated count should be >= 1!");
+ parseSample(TraceIt, Count);
+}
+
+bool PerfScriptReader::extractMMap2EventForBinary(ProfiledBinary *Binary,
+ StringRef Line,
+ MMapEvent &MMap) {
+ // Parse a line like:
+ // PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0
+ // 08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so
+ constexpr static const char *const Pattern =
+ "PERF_RECORD_MMAP2 ([0-9]+)/[0-9]+: "
+ "\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ "
+ "(0x[a-f0-9]+|0) .*\\]: [-a-z]+ (.*)";
+ // Field 0 - whole line
+ // Field 1 - PID
+ // Field 2 - base address
+ // Field 3 - mmapped size
+ // Field 4 - page offset
+ // Field 5 - binary path
+ enum EventIndex {
+ WHOLE_LINE = 0,
+ PID = 1,
+ MMAPPED_ADDRESS = 2,
+ MMAPPED_SIZE = 3,
+ PAGE_OFFSET = 4,
+ BINARY_PATH = 5
+ };
+
+ Regex RegMmap2(Pattern);
+ SmallVector<StringRef, 6> Fields;
+ bool R = RegMmap2.match(Line, &Fields);
+ if (!R) {
+ std::string ErrorMsg = "Cannot parse mmap event: " + Line.str() + " \n";
+ exitWithError(ErrorMsg);
+ }
+ Fields[PID].getAsInteger(10, MMap.PID);
+ Fields[MMAPPED_ADDRESS].getAsInteger(0, MMap.Address);
+ Fields[MMAPPED_SIZE].getAsInteger(0, MMap.Size);
+ Fields[PAGE_OFFSET].getAsInteger(0, MMap.Offset);
+ MMap.BinaryPath = Fields[BINARY_PATH];
+ if (ShowMmapEvents) {
+ outs() << "Mmap: Binary " << MMap.BinaryPath << " loaded at "
+ << format("0x%" PRIx64 ":", MMap.Address) << " \n";
+ }
+
+ StringRef BinaryName = llvm::sys::path::filename(MMap.BinaryPath);
+ return Binary->getName() == BinaryName;
+}
+
+void PerfScriptReader::parseMMap2Event(TraceStream &TraceIt) {
+ MMapEvent MMap;
+ if (extractMMap2EventForBinary(Binary, TraceIt.getCurrentLine(), MMap))
+ updateBinaryAddress(MMap);
+ TraceIt.advance();
+}
+
+void PerfScriptReader::parseEventOrSample(TraceStream &TraceIt) {
+ if (isMMap2Event(TraceIt.getCurrentLine()))
+ parseMMap2Event(TraceIt);
+ else
+ parseSample(TraceIt);
+}
+
+void PerfScriptReader::parseAndAggregateTrace() {
+ // Trace line iterator
+ TraceStream TraceIt(PerfTraceFile);
+ while (!TraceIt.isAtEoF())
+ parseEventOrSample(TraceIt);
+}
+
+// A LBR sample is like:
+// 40062f 0x5c6313f/0x5c63170/P/-/-/0 0x5c630e7/0x5c63130/P/-/-/0 ...
+// A heuristic for fast detection by checking whether a
+// leading " 0x" and the '/' exist.
+bool PerfScriptReader::isLBRSample(StringRef Line) {
+ // Skip the leading instruction pointer
+ SmallVector<StringRef, 32> Records;
+ Line.trim().split(Records, " ", 2, false);
+ if (Records.size() < 2)
+ return false;
+ if (Records[1].startswith("0x") && Records[1].contains('/'))
+ return true;
+ return false;
+}
+
+bool PerfScriptReader::isMMap2Event(StringRef Line) {
+ // Short cut to avoid string find is possible.
+ if (Line.empty() || Line.size() < 50)
+ return false;
+
+ if (std::isdigit(Line[0]))
+ return false;
+
+ // PERF_RECORD_MMAP2 does not appear at the beginning of the line
+ // for ` perf script --show-mmap-events -i ...`
+ return Line.contains("PERF_RECORD_MMAP2");
+}
+
+// The raw hybird sample is like
+// e.g.
+// 4005dc # call stack leaf
+// 400634
+// 400684 # call stack root
+// 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
+// ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries
+// Determine the perfscript contains hybrid samples(call stack + LBRs) by
+// checking whether there is a non-empty call stack immediately followed by
+// a LBR sample
+PerfContent PerfScriptReader::checkPerfScriptType(StringRef FileName) {
+ TraceStream TraceIt(FileName);
+ uint64_t FrameAddr = 0;
+ while (!TraceIt.isAtEoF()) {
+ // Skip the aggregated count
+ if (!TraceIt.getCurrentLine().getAsInteger(10, FrameAddr))
+ TraceIt.advance();
+
+ // Detect sample with call stack
+ int32_t Count = 0;
+ while (!TraceIt.isAtEoF() &&
+ !TraceIt.getCurrentLine().ltrim().getAsInteger(16, FrameAddr)) {
+ Count++;
+ TraceIt.advance();
+ }
+ if (!TraceIt.isAtEoF()) {
+ if (isLBRSample(TraceIt.getCurrentLine())) {
+ if (Count > 0)
+ return PerfContent::LBRStack;
+ else
+ return PerfContent::LBR;
+ }
+ TraceIt.advance();
+ }
+ }
+
+ exitWithError("Invalid perf script input!");
+ return PerfContent::UnknownContent;
+}
+
+void HybridPerfReader::generateUnsymbolizedProfile() {
+ ProfileIsCSFlat = !IgnoreStackSamples;
+ if (ProfileIsCSFlat)
+ unwindSamples();
+ else
+ PerfScriptReader::generateUnsymbolizedProfile();
+}
+
+void PerfScriptReader::warnTruncatedStack() {
+ if (ShowDetailedWarning) {
+ for (auto Address : InvalidReturnAddresses) {
+ WithColor::warning()
+ << "Truncated stack sample due to invalid return address at "
+ << format("0x%" PRIx64, Address)
+ << ", likely caused by frame pointer omission\n";
+ }
+ }
+ emitWarningSummary(
+ InvalidReturnAddresses.size(), AggregatedSamples.size(),
+ "of truncated stack samples due to invalid return address, "
+ "likely caused by frame pointer omission.");
+}
+
+void PerfScriptReader::warnInvalidRange() {
+ std::unordered_map<std::pair<uint64_t, uint64_t>, uint64_t,
+ pair_hash<uint64_t, uint64_t>>
+ Ranges;
+
+ for (const auto &Item : AggregatedSamples) {
+ const PerfSample *Sample = Item.first.getPtr();
+ uint64_t Count = Item.second;
+ uint64_t EndOffeset = 0;
+ for (const LBREntry &LBR : Sample->LBRStack) {
+ uint64_t SourceOffset = Binary->virtualAddrToOffset(LBR.Source);
+ uint64_t StartOffset = Binary->virtualAddrToOffset(LBR.Target);
+ if (EndOffeset != 0)
+ Ranges[{StartOffset, EndOffeset}] += Count;
+ EndOffeset = SourceOffset;
+ }
+ }
+
+ if (Ranges.empty()) {
+ WithColor::warning() << "No samples in perf script!\n";
+ return;
+ }
+
+ auto WarnInvalidRange =
+ [&](uint64_t StartOffset, uint64_t EndOffset, StringRef Msg) {
+ if (!ShowDetailedWarning)
+ return;
+ WithColor::warning()
+ << "["
+ << format("%8" PRIx64, Binary->offsetToVirtualAddr(StartOffset))
+ << ","
+ << format("%8" PRIx64, Binary->offsetToVirtualAddr(EndOffset))
+ << "]: " << Msg << "\n";
+ };
+
+ const char *EndNotBoundaryMsg = "Range is not on instruction boundary, "
+ "likely due to profile and binary mismatch.";
+ const char *DanglingRangeMsg = "Range does not belong to any functions, "
+ "likely from PLT, .init or .fini section.";
+ const char *RangeCrossFuncMsg =
+ "Fall through range should not cross function boundaries, likely due to "
+ "profile and binary mismatch.";
+
+ uint64_t InstNotBoundary = 0;
+ uint64_t UnmatchedRange = 0;
+ uint64_t RangeCrossFunc = 0;
+
+ for (auto &I : Ranges) {
+ uint64_t StartOffset = I.first.first;
+ uint64_t EndOffset = I.first.second;
+
+ if (!Binary->offsetIsCode(StartOffset) ||
+ !Binary->offsetIsTransfer(EndOffset)) {
+ InstNotBoundary++;
+ WarnInvalidRange(StartOffset, EndOffset, EndNotBoundaryMsg);
+ }
+
+ auto *FRange = Binary->findFuncRangeForOffset(StartOffset);
+ if (!FRange) {
+ UnmatchedRange++;
+ WarnInvalidRange(StartOffset, EndOffset, DanglingRangeMsg);
+ continue;
+ }
+
+ if (EndOffset >= FRange->EndOffset) {
+ RangeCrossFunc++;
+ WarnInvalidRange(StartOffset, EndOffset, RangeCrossFuncMsg);
+ }
+ }
+
+ uint64_t TotalRangeNum = Ranges.size();
+ emitWarningSummary(InstNotBoundary, TotalRangeNum,
+ "of profiled ranges are not on instruction boundary.");
+ emitWarningSummary(UnmatchedRange, TotalRangeNum,
+ "of profiled ranges do not belong to any functions.");
+ emitWarningSummary(RangeCrossFunc, TotalRangeNum,
+ "of profiled ranges do cross function boundaries.");
+}
+
+void PerfScriptReader::parsePerfTraces() {
+ // Parse perf traces and do aggregation.
+ parseAndAggregateTrace();
+
+ emitWarningSummary(NumLeafExternalFrame, NumTotalSample,
+ "of samples have leaf external frame in call stack.");
+ emitWarningSummary(NumLeadingOutgoingLBR, NumTotalSample,
+ "of samples have leading external LBR.");
+
+ // Generate unsymbolized profile.
+ warnTruncatedStack();
+ warnInvalidRange();
+ generateUnsymbolizedProfile();
+ AggregatedSamples.clear();
+
+ if (SkipSymbolization)
+ writeUnsymbolizedProfile(OutputFilename);
+}
+
+} // end namespace sampleprof
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/PerfReader.h b/contrib/libs/llvm14/tools/llvm-profgen/PerfReader.h
new file mode 100644
index 00000000000..9d84ad34bb3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/PerfReader.h
@@ -0,0 +1,728 @@
+//===-- PerfReader.h - perfscript reader -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_PROFGEN_PERFREADER_H
+#define LLVM_TOOLS_LLVM_PROFGEN_PERFREADER_H
+#include "ErrorHandling.h"
+#include "ProfiledBinary.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Regex.h"
+#include <cstdint>
+#include <fstream>
+#include <list>
+#include <map>
+#include <vector>
+
+using namespace llvm;
+using namespace sampleprof;
+
+namespace llvm {
+namespace sampleprof {
+
+// Stream based trace line iterator
+class TraceStream {
+ std::string CurrentLine;
+ std::ifstream Fin;
+ bool IsAtEoF = false;
+ uint64_t LineNumber = 0;
+
+public:
+ TraceStream(StringRef Filename) : Fin(Filename.str()) {
+ if (!Fin.good())
+ exitWithError("Error read input perf script file", Filename);
+ advance();
+ }
+
+ StringRef getCurrentLine() {
+ assert(!IsAtEoF && "Line iterator reaches the End-of-File!");
+ return CurrentLine;
+ }
+
+ uint64_t getLineNumber() { return LineNumber; }
+
+ bool isAtEoF() { return IsAtEoF; }
+
+ // Read the next line
+ void advance() {
+ if (!std::getline(Fin, CurrentLine)) {
+ IsAtEoF = true;
+ return;
+ }
+ LineNumber++;
+ }
+};
+
+// The type of input format.
+enum PerfFormat {
+ UnknownFormat = 0,
+ PerfData = 1, // Raw linux perf.data.
+ PerfScript = 2, // Perf script create by `perf script` command.
+ UnsymbolizedProfile = 3, // Unsymbolized profile generated by llvm-profgen.
+
+};
+
+// The type of perfscript content.
+enum PerfContent {
+ UnknownContent = 0,
+ LBR = 1, // Only LBR sample.
+ LBRStack = 2, // Hybrid sample including call stack and LBR stack.
+};
+
+struct PerfInputFile {
+ std::string InputFile;
+ PerfFormat Format = PerfFormat::UnknownFormat;
+ PerfContent Content = PerfContent::UnknownContent;
+};
+
+// The parsed LBR sample entry.
+struct LBREntry {
+ uint64_t Source = 0;
+ uint64_t Target = 0;
+ // An artificial branch stands for a series of consecutive branches starting
+ // from the current binary with a transition through external code and
+ // eventually landing back in the current binary.
+ bool IsArtificial = false;
+ LBREntry(uint64_t S, uint64_t T, bool I)
+ : Source(S), Target(T), IsArtificial(I) {}
+
+#ifndef NDEBUG
+ void print() const {
+ dbgs() << "from " << format("%#010x", Source) << " to "
+ << format("%#010x", Target);
+ if (IsArtificial)
+ dbgs() << " Artificial";
+ }
+#endif
+};
+
+#ifndef NDEBUG
+static inline void printLBRStack(const SmallVectorImpl<LBREntry> &LBRStack) {
+ for (size_t I = 0; I < LBRStack.size(); I++) {
+ dbgs() << "[" << I << "] ";
+ LBRStack[I].print();
+ dbgs() << "\n";
+ }
+}
+
+static inline void printCallStack(const SmallVectorImpl<uint64_t> &CallStack) {
+ for (size_t I = 0; I < CallStack.size(); I++) {
+ dbgs() << "[" << I << "] " << format("%#010x", CallStack[I]) << "\n";
+ }
+}
+#endif
+
+// Hash interface for generic data of type T
+// Data should implement a \fn getHashCode and a \fn isEqual
+// Currently getHashCode is non-virtual to avoid the overhead of calling vtable,
+// i.e we explicitly calculate hash of derived class, assign to base class's
+// HashCode. This also provides the flexibility for calculating the hash code
+// incrementally(like rolling hash) during frame stack unwinding since unwinding
+// only changes the leaf of frame stack. \fn isEqual is a virtual function,
+// which will have perf overhead. In the future, if we redesign a better hash
+// function, then we can just skip this or switch to non-virtual function(like
+// just ignore comparision if hash conflicts probabilities is low)
+template <class T> class Hashable {
+public:
+ std::shared_ptr<T> Data;
+ Hashable(const std::shared_ptr<T> &D) : Data(D) {}
+
+ // Hash code generation
+ struct Hash {
+ uint64_t operator()(const Hashable<T> &Key) const {
+ // Don't make it virtual for getHashCode
+ uint64_t Hash = Key.Data->getHashCode();
+ assert(Hash && "Should generate HashCode for it!");
+ return Hash;
+ }
+ };
+
+ // Hash equal
+ struct Equal {
+ bool operator()(const Hashable<T> &LHS, const Hashable<T> &RHS) const {
+ // Precisely compare the data, vtable will have overhead.
+ return LHS.Data->isEqual(RHS.Data.get());
+ }
+ };
+
+ T *getPtr() const { return Data.get(); }
+};
+
+struct PerfSample {
+ // LBR stack recorded in FIFO order.
+ SmallVector<LBREntry, 16> LBRStack;
+ // Call stack recorded in FILO(leaf to root) order, it's used for CS-profile
+ // generation
+ SmallVector<uint64_t, 16> CallStack;
+
+ virtual ~PerfSample() = default;
+ uint64_t getHashCode() const {
+ // Use simple DJB2 hash
+ auto HashCombine = [](uint64_t H, uint64_t V) {
+ return ((H << 5) + H) + V;
+ };
+ uint64_t Hash = 5381;
+ for (const auto &Value : CallStack) {
+ Hash = HashCombine(Hash, Value);
+ }
+ for (const auto &Entry : LBRStack) {
+ Hash = HashCombine(Hash, Entry.Source);
+ Hash = HashCombine(Hash, Entry.Target);
+ }
+ return Hash;
+ }
+
+ bool isEqual(const PerfSample *Other) const {
+ const SmallVector<uint64_t, 16> &OtherCallStack = Other->CallStack;
+ const SmallVector<LBREntry, 16> &OtherLBRStack = Other->LBRStack;
+
+ if (CallStack.size() != OtherCallStack.size() ||
+ LBRStack.size() != OtherLBRStack.size())
+ return false;
+
+ if (!std::equal(CallStack.begin(), CallStack.end(), OtherCallStack.begin()))
+ return false;
+
+ for (size_t I = 0; I < OtherLBRStack.size(); I++) {
+ if (LBRStack[I].Source != OtherLBRStack[I].Source ||
+ LBRStack[I].Target != OtherLBRStack[I].Target)
+ return false;
+ }
+ return true;
+ }
+
+#ifndef NDEBUG
+ void print() const {
+ dbgs() << "LBR stack\n";
+ printLBRStack(LBRStack);
+ dbgs() << "Call stack\n";
+ printCallStack(CallStack);
+ }
+#endif
+};
+// After parsing the sample, we record the samples by aggregating them
+// into this counter. The key stores the sample data and the value is
+// the sample repeat times.
+using AggregatedCounter =
+ std::unordered_map<Hashable<PerfSample>, uint64_t,
+ Hashable<PerfSample>::Hash, Hashable<PerfSample>::Equal>;
+
+using SampleVector = SmallVector<std::tuple<uint64_t, uint64_t, uint64_t>, 16>;
+
+// The state for the unwinder, it doesn't hold the data but only keep the
+// pointer/index of the data, While unwinding, the CallStack is changed
+// dynamicially and will be recorded as the context of the sample
+struct UnwindState {
+ // Profiled binary that current frame address belongs to
+ const ProfiledBinary *Binary;
+ // Call stack trie node
+ struct ProfiledFrame {
+ const uint64_t Address = DummyRoot;
+ ProfiledFrame *Parent;
+ SampleVector RangeSamples;
+ SampleVector BranchSamples;
+ std::unordered_map<uint64_t, std::unique_ptr<ProfiledFrame>> Children;
+
+ ProfiledFrame(uint64_t Addr = 0, ProfiledFrame *P = nullptr)
+ : Address(Addr), Parent(P) {}
+ ProfiledFrame *getOrCreateChildFrame(uint64_t Address) {
+ assert(Address && "Address can't be zero!");
+ auto Ret = Children.emplace(
+ Address, std::make_unique<ProfiledFrame>(Address, this));
+ return Ret.first->second.get();
+ }
+ void recordRangeCount(uint64_t Start, uint64_t End, uint64_t Count) {
+ RangeSamples.emplace_back(std::make_tuple(Start, End, Count));
+ }
+ void recordBranchCount(uint64_t Source, uint64_t Target, uint64_t Count) {
+ BranchSamples.emplace_back(std::make_tuple(Source, Target, Count));
+ }
+ bool isDummyRoot() { return Address == DummyRoot; }
+ bool isExternalFrame() { return Address == ExternalAddr; }
+ bool isLeafFrame() { return Children.empty(); }
+ };
+
+ ProfiledFrame DummyTrieRoot;
+ ProfiledFrame *CurrentLeafFrame;
+ // Used to fall through the LBR stack
+ uint32_t LBRIndex = 0;
+ // Reference to PerfSample.LBRStack
+ const SmallVector<LBREntry, 16> &LBRStack;
+ // Used to iterate the address range
+ InstructionPointer InstPtr;
+ UnwindState(const PerfSample *Sample, const ProfiledBinary *Binary)
+ : Binary(Binary), LBRStack(Sample->LBRStack),
+ InstPtr(Binary, Sample->CallStack.front()) {
+ initFrameTrie(Sample->CallStack);
+ }
+
+ bool validateInitialState() {
+ uint64_t LBRLeaf = LBRStack[LBRIndex].Target;
+ uint64_t LeafAddr = CurrentLeafFrame->Address;
+ assert((LBRLeaf != ExternalAddr || LBRLeaf == LeafAddr) &&
+ "External leading LBR should match the leaf frame.");
+
+ // When we take a stack sample, ideally the sampling distance between the
+ // leaf IP of stack and the last LBR target shouldn't be very large.
+ // Use a heuristic size (0x100) to filter out broken records.
+ if (LeafAddr < LBRLeaf || LeafAddr >= LBRLeaf + 0x100) {
+ WithColor::warning() << "Bogus trace: stack tip = "
+ << format("%#010x", LeafAddr)
+ << ", LBR tip = " << format("%#010x\n", LBRLeaf);
+ return false;
+ }
+ return true;
+ }
+
+ void checkStateConsistency() {
+ assert(InstPtr.Address == CurrentLeafFrame->Address &&
+ "IP should align with context leaf");
+ }
+
+ bool hasNextLBR() const { return LBRIndex < LBRStack.size(); }
+ uint64_t getCurrentLBRSource() const { return LBRStack[LBRIndex].Source; }
+ uint64_t getCurrentLBRTarget() const { return LBRStack[LBRIndex].Target; }
+ const LBREntry &getCurrentLBR() const { return LBRStack[LBRIndex]; }
+ bool IsLastLBR() const { return LBRIndex == 0; }
+ bool getLBRStackSize() const { return LBRStack.size(); }
+ void advanceLBR() { LBRIndex++; }
+ ProfiledFrame *getParentFrame() { return CurrentLeafFrame->Parent; }
+
+ void pushFrame(uint64_t Address) {
+ CurrentLeafFrame = CurrentLeafFrame->getOrCreateChildFrame(Address);
+ }
+
+ void switchToFrame(uint64_t Address) {
+ if (CurrentLeafFrame->Address == Address)
+ return;
+ CurrentLeafFrame = CurrentLeafFrame->Parent->getOrCreateChildFrame(Address);
+ }
+
+ void popFrame() { CurrentLeafFrame = CurrentLeafFrame->Parent; }
+
+ void clearCallStack() { CurrentLeafFrame = &DummyTrieRoot; }
+
+ void initFrameTrie(const SmallVectorImpl<uint64_t> &CallStack) {
+ ProfiledFrame *Cur = &DummyTrieRoot;
+ for (auto Address : reverse(CallStack)) {
+ Cur = Cur->getOrCreateChildFrame(Address);
+ }
+ CurrentLeafFrame = Cur;
+ }
+
+ ProfiledFrame *getDummyRootPtr() { return &DummyTrieRoot; }
+};
+
+// Base class for sample counter key with context
+struct ContextKey {
+ uint64_t HashCode = 0;
+ virtual ~ContextKey() = default;
+ uint64_t getHashCode() {
+ if (HashCode == 0)
+ genHashCode();
+ return HashCode;
+ }
+ virtual void genHashCode() = 0;
+ virtual bool isEqual(const ContextKey *K) const {
+ return HashCode == K->HashCode;
+ };
+
+ // Utilities for LLVM-style RTTI
+ enum ContextKind { CK_StringBased, CK_ProbeBased };
+ const ContextKind Kind;
+ ContextKind getKind() const { return Kind; }
+ ContextKey(ContextKind K) : Kind(K){};
+};
+
+// String based context id
+struct StringBasedCtxKey : public ContextKey {
+ SampleContextFrameVector Context;
+
+ bool WasLeafInlined;
+ StringBasedCtxKey() : ContextKey(CK_StringBased), WasLeafInlined(false){};
+ static bool classof(const ContextKey *K) {
+ return K->getKind() == CK_StringBased;
+ }
+
+ bool isEqual(const ContextKey *K) const override {
+ const StringBasedCtxKey *Other = dyn_cast<StringBasedCtxKey>(K);
+ return Context == Other->Context;
+ }
+
+ void genHashCode() override {
+ HashCode = hash_value(SampleContextFrames(Context));
+ }
+};
+
+// Probe based context key as the intermediate key of context
+// String based context key will introduce redundant string handling
+// since the callee context is inferred from the context string which
+// need to be splitted by '@' to get the last location frame, so we
+// can just use probe instead and generate the string in the end.
+struct ProbeBasedCtxKey : public ContextKey {
+ SmallVector<const MCDecodedPseudoProbe *, 16> Probes;
+
+ ProbeBasedCtxKey() : ContextKey(CK_ProbeBased) {}
+ static bool classof(const ContextKey *K) {
+ return K->getKind() == CK_ProbeBased;
+ }
+
+ bool isEqual(const ContextKey *K) const override {
+ const ProbeBasedCtxKey *O = dyn_cast<ProbeBasedCtxKey>(K);
+ assert(O != nullptr && "Probe based key shouldn't be null in isEqual");
+ return std::equal(Probes.begin(), Probes.end(), O->Probes.begin(),
+ O->Probes.end());
+ }
+
+ void genHashCode() override {
+ for (const auto *P : Probes) {
+ HashCode = hash_combine(HashCode, P);
+ }
+ if (HashCode == 0) {
+ // Avoid zero value of HashCode when it's an empty list
+ HashCode = 1;
+ }
+ }
+};
+
+// The counter of branch samples for one function indexed by the branch,
+// which is represented as the source and target offset pair.
+using BranchSample = std::map<std::pair<uint64_t, uint64_t>, uint64_t>;
+// The counter of range samples for one function indexed by the range,
+// which is represented as the start and end offset pair.
+using RangeSample = std::map<std::pair<uint64_t, uint64_t>, uint64_t>;
+// Wrapper for sample counters including range counter and branch counter
+struct SampleCounter {
+ RangeSample RangeCounter;
+ BranchSample BranchCounter;
+
+ void recordRangeCount(uint64_t Start, uint64_t End, uint64_t Repeat) {
+ assert(Start <= End && "Invalid instruction range");
+ RangeCounter[{Start, End}] += Repeat;
+ }
+ void recordBranchCount(uint64_t Source, uint64_t Target, uint64_t Repeat) {
+ BranchCounter[{Source, Target}] += Repeat;
+ }
+};
+
+// Sample counter with context to support context-sensitive profile
+using ContextSampleCounterMap =
+ std::unordered_map<Hashable<ContextKey>, SampleCounter,
+ Hashable<ContextKey>::Hash, Hashable<ContextKey>::Equal>;
+
+struct FrameStack {
+ SmallVector<uint64_t, 16> Stack;
+ ProfiledBinary *Binary;
+ FrameStack(ProfiledBinary *B) : Binary(B) {}
+ bool pushFrame(UnwindState::ProfiledFrame *Cur) {
+ assert(!Cur->isExternalFrame() &&
+ "External frame's not expected for context stack.");
+ Stack.push_back(Cur->Address);
+ return true;
+ }
+
+ void popFrame() {
+ if (!Stack.empty())
+ Stack.pop_back();
+ }
+ std::shared_ptr<StringBasedCtxKey> getContextKey();
+};
+
+struct ProbeStack {
+ SmallVector<const MCDecodedPseudoProbe *, 16> Stack;
+ ProfiledBinary *Binary;
+ ProbeStack(ProfiledBinary *B) : Binary(B) {}
+ bool pushFrame(UnwindState::ProfiledFrame *Cur) {
+ assert(!Cur->isExternalFrame() &&
+ "External frame's not expected for context stack.");
+ const MCDecodedPseudoProbe *CallProbe =
+ Binary->getCallProbeForAddr(Cur->Address);
+ // We may not find a probe for a merged or external callsite.
+ // Callsite merging may cause the loss of original probe IDs.
+ // Cutting off the context from here since the inliner will
+ // not know how to consume a context with unknown callsites.
+ if (!CallProbe)
+ return false;
+ Stack.push_back(CallProbe);
+ return true;
+ }
+
+ void popFrame() {
+ if (!Stack.empty())
+ Stack.pop_back();
+ }
+ // Use pseudo probe based context key to get the sample counter
+ // A context stands for a call path from 'main' to an uninlined
+ // callee with all inline frames recovered on that path. The probes
+ // belonging to that call path is the probes either originated from
+ // the callee or from any functions inlined into the callee. Since
+ // pseudo probes are organized in a tri-tree style after decoded,
+ // the tree path from the tri-tree root (which is the uninlined
+ // callee) to the probe node forms an inline context.
+ // Here we use a list of probe(pointer) as the context key to speed up
+ // aggregation and the final context string will be generate in
+ // ProfileGenerator
+ std::shared_ptr<ProbeBasedCtxKey> getContextKey();
+};
+
+/*
+As in hybrid sample we have a group of LBRs and the most recent sampling call
+stack, we can walk through those LBRs to infer more call stacks which would be
+used as context for profile. VirtualUnwinder is the class to do the call stack
+unwinding based on LBR state. Two types of unwinding are processd here:
+1) LBR unwinding and 2) linear range unwinding.
+Specifically, for each LBR entry(can be classified into call, return, regular
+branch), LBR unwinding will replay the operation by pushing, popping or
+switching leaf frame towards the call stack and since the initial call stack
+is most recently sampled, the replay should be in anti-execution order, i.e. for
+the regular case, pop the call stack when LBR is call, push frame on call stack
+when LBR is return. After each LBR processed, it also needs to align with the
+next LBR by going through instructions from previous LBR's target to current
+LBR's source, which is the linear unwinding. As instruction from linear range
+can come from different function by inlining, linear unwinding will do the range
+splitting and record counters by the range with same inline context. Over those
+unwinding process we will record each call stack as context id and LBR/linear
+range as sample counter for further CS profile generation.
+*/
+class VirtualUnwinder {
+public:
+ VirtualUnwinder(ContextSampleCounterMap *Counter, ProfiledBinary *B)
+ : CtxCounterMap(Counter), Binary(B) {}
+ bool unwind(const PerfSample *Sample, uint64_t Repeat);
+ std::set<uint64_t> &getUntrackedCallsites() { return UntrackedCallsites; }
+
+ uint64_t NumTotalBranches = 0;
+ uint64_t NumExtCallBranch = 0;
+ uint64_t NumMissingExternalFrame = 0;
+ uint64_t NumMismatchedProEpiBranch = 0;
+ uint64_t NumMismatchedExtCallBranch = 0;
+
+private:
+ bool isCallState(UnwindState &State) const {
+ // The tail call frame is always missing here in stack sample, we will
+ // use a specific tail call tracker to infer it.
+ return Binary->addressIsCall(State.getCurrentLBRSource());
+ }
+
+ bool isReturnState(UnwindState &State) const {
+ // Simply check addressIsReturn, as ret is always reliable, both for
+ // regular call and tail call.
+ if (!Binary->addressIsReturn(State.getCurrentLBRSource()))
+ return false;
+
+ // In a callback case, a return from internal code, say A, to external
+ // runtime can happen. The external runtime can then call back to
+ // another internal routine, say B. Making an artificial branch that
+ // looks like a return from A to B can confuse the unwinder to treat
+ // the instruction before B as the call instruction. Here we detect this
+ // case if the return target is not the next inst of call inst, then we just
+ // do not treat it as a return.
+ uint64_t CallAddr =
+ Binary->getCallAddrFromFrameAddr(State.getCurrentLBRTarget());
+ return (CallAddr != 0);
+ }
+
+ void unwindCall(UnwindState &State);
+ void unwindLinear(UnwindState &State, uint64_t Repeat);
+ void unwindReturn(UnwindState &State);
+ void unwindBranch(UnwindState &State);
+
+ template <typename T>
+ void collectSamplesFromFrame(UnwindState::ProfiledFrame *Cur, T &Stack);
+ // Collect each samples on trie node by DFS traversal
+ template <typename T>
+ void collectSamplesFromFrameTrie(UnwindState::ProfiledFrame *Cur, T &Stack);
+ void collectSamplesFromFrameTrie(UnwindState::ProfiledFrame *Cur);
+
+ void recordRangeCount(uint64_t Start, uint64_t End, UnwindState &State,
+ uint64_t Repeat);
+ void recordBranchCount(const LBREntry &Branch, UnwindState &State,
+ uint64_t Repeat);
+
+ ContextSampleCounterMap *CtxCounterMap;
+ // Profiled binary that current frame address belongs to
+ ProfiledBinary *Binary;
+ // Keep track of all untracked callsites
+ std::set<uint64_t> UntrackedCallsites;
+};
+
+// Read perf trace to parse the events and samples.
+class PerfReaderBase {
+public:
+ PerfReaderBase(ProfiledBinary *B, StringRef PerfTrace)
+ : Binary(B), PerfTraceFile(PerfTrace) {
+ // Initialize the base address to preferred address.
+ Binary->setBaseAddress(Binary->getPreferredBaseAddress());
+ };
+ virtual ~PerfReaderBase() = default;
+ static std::unique_ptr<PerfReaderBase> create(ProfiledBinary *Binary,
+ PerfInputFile &PerfInput);
+
+ // Entry of the reader to parse multiple perf traces
+ virtual void parsePerfTraces() = 0;
+ const ContextSampleCounterMap &getSampleCounters() const {
+ return SampleCounters;
+ }
+ bool profileIsCSFlat() { return ProfileIsCSFlat; }
+
+protected:
+ ProfiledBinary *Binary = nullptr;
+ StringRef PerfTraceFile;
+
+ ContextSampleCounterMap SampleCounters;
+ bool ProfileIsCSFlat = false;
+
+ uint64_t NumTotalSample = 0;
+ uint64_t NumLeafExternalFrame = 0;
+ uint64_t NumLeadingOutgoingLBR = 0;
+};
+
+// Read perf script to parse the events and samples.
+class PerfScriptReader : public PerfReaderBase {
+public:
+ PerfScriptReader(ProfiledBinary *B, StringRef PerfTrace)
+ : PerfReaderBase(B, PerfTrace){};
+
+ // Entry of the reader to parse multiple perf traces
+ virtual void parsePerfTraces() override;
+ // Generate perf script from perf data
+ static PerfInputFile convertPerfDataToTrace(ProfiledBinary *Binary,
+ PerfInputFile &File);
+ // Extract perf script type by peaking at the input
+ static PerfContent checkPerfScriptType(StringRef FileName);
+
+protected:
+ // The parsed MMap event
+ struct MMapEvent {
+ uint64_t PID = 0;
+ uint64_t Address = 0;
+ uint64_t Size = 0;
+ uint64_t Offset = 0;
+ StringRef BinaryPath;
+ };
+
+ // Check whether a given line is LBR sample
+ static bool isLBRSample(StringRef Line);
+ // Check whether a given line is MMAP event
+ static bool isMMap2Event(StringRef Line);
+ // Parse a single line of a PERF_RECORD_MMAP2 event looking for a
+ // mapping between the binary name and its memory layout.
+ static bool extractMMap2EventForBinary(ProfiledBinary *Binary, StringRef Line,
+ MMapEvent &MMap);
+ // Update base address based on mmap events
+ void updateBinaryAddress(const MMapEvent &Event);
+ // Parse mmap event and update binary address
+ void parseMMap2Event(TraceStream &TraceIt);
+ // Parse perf events/samples and do aggregation
+ void parseAndAggregateTrace();
+ // Parse either an MMAP event or a perf sample
+ void parseEventOrSample(TraceStream &TraceIt);
+ // Warn if the relevant mmap event is missing.
+ void warnIfMissingMMap();
+ // Emit accumulate warnings.
+ void warnTruncatedStack();
+ // Warn if range is invalid.
+ void warnInvalidRange();
+ // Extract call stack from the perf trace lines
+ bool extractCallstack(TraceStream &TraceIt,
+ SmallVectorImpl<uint64_t> &CallStack);
+ // Extract LBR stack from one perf trace line
+ bool extractLBRStack(TraceStream &TraceIt,
+ SmallVectorImpl<LBREntry> &LBRStack);
+ uint64_t parseAggregatedCount(TraceStream &TraceIt);
+ // Parse one sample from multiple perf lines, override this for different
+ // sample type
+ void parseSample(TraceStream &TraceIt);
+ // An aggregated count is given to indicate how many times the sample is
+ // repeated.
+ virtual void parseSample(TraceStream &TraceIt, uint64_t Count){};
+ void computeCounterFromLBR(const PerfSample *Sample, uint64_t Repeat);
+ // Post process the profile after trace aggregation, we will do simple range
+ // overlap computation for AutoFDO, or unwind for CSSPGO(hybrid sample).
+ virtual void generateUnsymbolizedProfile();
+ void writeUnsymbolizedProfile(StringRef Filename);
+ void writeUnsymbolizedProfile(raw_fd_ostream &OS);
+
+ // Samples with the repeating time generated by the perf reader
+ AggregatedCounter AggregatedSamples;
+ // Keep track of all invalid return addresses
+ std::set<uint64_t> InvalidReturnAddresses;
+};
+
+/*
+ The reader of LBR only perf script.
+ A typical LBR sample is like:
+ 40062f 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
+ ... 0x4005c8/0x4005dc/P/-/-/0
+*/
+class LBRPerfReader : public PerfScriptReader {
+public:
+ LBRPerfReader(ProfiledBinary *Binary, StringRef PerfTrace)
+ : PerfScriptReader(Binary, PerfTrace){};
+ // Parse the LBR only sample.
+ virtual void parseSample(TraceStream &TraceIt, uint64_t Count) override;
+};
+
+/*
+ Hybrid perf script includes a group of hybrid samples(LBRs + call stack),
+ which is used to generate CS profile. An example of hybrid sample:
+ 4005dc # call stack leaf
+ 400634
+ 400684 # call stack root
+ 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
+ ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries
+*/
+class HybridPerfReader : public PerfScriptReader {
+public:
+ HybridPerfReader(ProfiledBinary *Binary, StringRef PerfTrace)
+ : PerfScriptReader(Binary, PerfTrace){};
+ // Parse the hybrid sample including the call and LBR line
+ void parseSample(TraceStream &TraceIt, uint64_t Count) override;
+ void generateUnsymbolizedProfile() override;
+
+private:
+ // Unwind the hybrid samples after aggregration
+ void unwindSamples();
+};
+
+/*
+ Format of unsymbolized profile:
+
+ [frame1 @ frame2 @ ...] # If it's a CS profile
+ number of entries in RangeCounter
+ from_1-to_1:count_1
+ from_2-to_2:count_2
+ ......
+ from_n-to_n:count_n
+ number of entries in BranchCounter
+ src_1->dst_1:count_1
+ src_2->dst_2:count_2
+ ......
+ src_n->dst_n:count_n
+ [frame1 @ frame2 @ ...] # Next context
+ ......
+
+Note that non-CS profile doesn't have the empty `[]` context.
+*/
+class UnsymbolizedProfileReader : public PerfReaderBase {
+public:
+ UnsymbolizedProfileReader(ProfiledBinary *Binary, StringRef PerfTrace)
+ : PerfReaderBase(Binary, PerfTrace){};
+ void parsePerfTraces() override;
+
+private:
+ void readSampleCounters(TraceStream &TraceIt, SampleCounter &SCounters);
+ void readUnsymbolizedProfile(StringRef Filename);
+
+ std::unordered_set<std::string> ContextStrSet;
+};
+
+} // end namespace sampleprof
+} // end namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/ProfileGenerator.cpp b/contrib/libs/llvm14/tools/llvm-profgen/ProfileGenerator.cpp
new file mode 100644
index 00000000000..1248e37dc50
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/ProfileGenerator.cpp
@@ -0,0 +1,979 @@
+//===-- ProfileGenerator.cpp - Profile Generator ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProfileGenerator.h"
+#include "ErrorHandling.h"
+#include "ProfiledBinary.h"
+#include "llvm/ProfileData/ProfileCommon.h"
+#include <float.h>
+#include <unordered_set>
+
+cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
+ cl::Required,
+ cl::desc("Output profile file"));
+static cl::alias OutputA("o", cl::desc("Alias for --output"),
+ cl::aliasopt(OutputFilename));
+
+static cl::opt<SampleProfileFormat> OutputFormat(
+ "format", cl::desc("Format of output profile"), cl::init(SPF_Ext_Binary),
+ cl::values(
+ clEnumValN(SPF_Binary, "binary", "Binary encoding (default)"),
+ clEnumValN(SPF_Compact_Binary, "compbinary", "Compact binary encoding"),
+ clEnumValN(SPF_Ext_Binary, "extbinary", "Extensible binary encoding"),
+ clEnumValN(SPF_Text, "text", "Text encoding"),
+ clEnumValN(SPF_GCC, "gcc",
+ "GCC encoding (only meaningful for -sample)")));
+
+cl::opt<bool> UseMD5(
+ "use-md5", cl::init(false), cl::Hidden,
+ cl::desc("Use md5 to represent function names in the output profile (only "
+ "meaningful for -extbinary)"));
+
+static cl::opt<bool> PopulateProfileSymbolList(
+ "populate-profile-symbol-list", cl::init(false), cl::Hidden,
+ cl::desc("Populate profile symbol list (only meaningful for -extbinary)"));
+
+static cl::opt<bool> FillZeroForAllFuncs(
+ "fill-zero-for-all-funcs", cl::init(false), cl::Hidden,
+ cl::desc("Attribute all functions' range with zero count "
+ "even it's not hit by any samples."));
+
+static cl::opt<int32_t, true> RecursionCompression(
+ "compress-recursion",
+ cl::desc("Compressing recursion by deduplicating adjacent frame "
+ "sequences up to the specified size. -1 means no size limit."),
+ cl::Hidden,
+ cl::location(llvm::sampleprof::CSProfileGenerator::MaxCompressionSize));
+
+static cl::opt<bool>
+ TrimColdProfile("trim-cold-profile", cl::init(false), cl::ZeroOrMore,
+ cl::desc("If the total count of the profile is smaller "
+ "than threshold, it will be trimmed."));
+
+static cl::opt<bool> CSProfMergeColdContext(
+ "csprof-merge-cold-context", cl::init(true), cl::ZeroOrMore,
+ cl::desc("If the total count of context profile is smaller than "
+ "the threshold, it will be merged into context-less base "
+ "profile."));
+
+static cl::opt<uint32_t> CSProfMaxColdContextDepth(
+ "csprof-max-cold-context-depth", cl::init(1), cl::ZeroOrMore,
+ cl::desc("Keep the last K contexts while merging cold profile. 1 means the "
+ "context-less base profile"));
+
+static cl::opt<int, true> CSProfMaxContextDepth(
+ "csprof-max-context-depth", cl::ZeroOrMore,
+ cl::desc("Keep the last K contexts while merging profile. -1 means no "
+ "depth limit."),
+ cl::location(llvm::sampleprof::CSProfileGenerator::MaxContextDepth));
+
+static cl::opt<double> HotFunctionDensityThreshold(
+ "hot-function-density-threshold", llvm::cl::init(1000),
+ llvm::cl::desc(
+ "specify density threshold for hot functions (default: 1000)"),
+ llvm::cl::Optional);
+static cl::opt<bool> ShowDensity("show-density", llvm::cl::init(false),
+ llvm::cl::desc("show profile density details"),
+ llvm::cl::Optional);
+
+static cl::opt<bool> UpdateTotalSamples(
+ "update-total-samples", llvm::cl::init(false),
+ llvm::cl::desc(
+ "Update total samples by accumulating all its body samples."),
+ llvm::cl::Optional);
+
+extern cl::opt<int> ProfileSummaryCutoffHot;
+
+static cl::opt<bool> GenCSNestedProfile(
+ "gen-cs-nested-profile", cl::Hidden, cl::init(false),
+ cl::desc("Generate nested function profiles for CSSPGO"));
+
+using namespace llvm;
+using namespace sampleprof;
+
+namespace llvm {
+namespace sampleprof {
+
+// Initialize the MaxCompressionSize to -1 which means no size limit
+int32_t CSProfileGenerator::MaxCompressionSize = -1;
+
+int CSProfileGenerator::MaxContextDepth = -1;
+
+bool ProfileGeneratorBase::UseFSDiscriminator = false;
+
+std::unique_ptr<ProfileGeneratorBase>
+ProfileGeneratorBase::create(ProfiledBinary *Binary,
+ const ContextSampleCounterMap &SampleCounters,
+ bool ProfileIsCSFlat) {
+ std::unique_ptr<ProfileGeneratorBase> Generator;
+ if (ProfileIsCSFlat) {
+ if (Binary->useFSDiscriminator())
+ exitWithError("FS discriminator is not supported in CS profile.");
+ Generator.reset(new CSProfileGenerator(Binary, SampleCounters));
+ } else {
+ Generator.reset(new ProfileGenerator(Binary, SampleCounters));
+ }
+ ProfileGeneratorBase::UseFSDiscriminator = Binary->useFSDiscriminator();
+ FunctionSamples::ProfileIsFS = Binary->useFSDiscriminator();
+
+ return Generator;
+}
+
+void ProfileGeneratorBase::write(std::unique_ptr<SampleProfileWriter> Writer,
+ SampleProfileMap &ProfileMap) {
+ // Populate profile symbol list if extended binary format is used.
+ ProfileSymbolList SymbolList;
+
+ if (PopulateProfileSymbolList && OutputFormat == SPF_Ext_Binary) {
+ Binary->populateSymbolListFromDWARF(SymbolList);
+ Writer->setProfileSymbolList(&SymbolList);
+ }
+
+ if (std::error_code EC = Writer->write(ProfileMap))
+ exitWithError(std::move(EC));
+}
+
+void ProfileGeneratorBase::write() {
+ auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat);
+ if (std::error_code EC = WriterOrErr.getError())
+ exitWithError(EC, OutputFilename);
+
+ if (UseMD5) {
+ if (OutputFormat != SPF_Ext_Binary)
+ WithColor::warning() << "-use-md5 is ignored. Specify "
+ "--format=extbinary to enable it\n";
+ else
+ WriterOrErr.get()->setUseMD5();
+ }
+
+ write(std::move(WriterOrErr.get()), ProfileMap);
+}
+
+void ProfileGeneratorBase::showDensitySuggestion(double Density) {
+ if (Density == 0.0)
+ WithColor::warning() << "The --profile-summary-cutoff-hot option may be "
+ "set too low. Please check your command.\n";
+ else if (Density < HotFunctionDensityThreshold)
+ WithColor::warning()
+ << "AutoFDO is estimated to optimize better with "
+ << format("%.1f", HotFunctionDensityThreshold / Density)
+ << "x more samples. Please consider increasing sampling rate or "
+ "profiling for longer duration to get more samples.\n";
+
+ if (ShowDensity)
+ outs() << "Minimum profile density for hot functions with top "
+ << format("%.2f",
+ static_cast<double>(ProfileSummaryCutoffHot.getValue()) /
+ 10000)
+ << "% total samples: " << format("%.1f", Density) << "\n";
+}
+
+double ProfileGeneratorBase::calculateDensity(const SampleProfileMap &Profiles,
+ uint64_t HotCntThreshold) {
+ double Density = DBL_MAX;
+ std::vector<const FunctionSamples *> HotFuncs;
+ for (auto &I : Profiles) {
+ auto &FuncSamples = I.second;
+ if (FuncSamples.getTotalSamples() < HotCntThreshold)
+ continue;
+ HotFuncs.emplace_back(&FuncSamples);
+ }
+
+ for (auto *FuncSamples : HotFuncs) {
+ auto *Func = Binary->getBinaryFunction(FuncSamples->getName());
+ if (!Func)
+ continue;
+ uint64_t FuncSize = Func->getFuncSize();
+ if (FuncSize == 0)
+ continue;
+ Density =
+ std::min(Density, static_cast<double>(FuncSamples->getTotalSamples()) /
+ FuncSize);
+ }
+
+ return Density == DBL_MAX ? 0.0 : Density;
+}
+
+void ProfileGeneratorBase::findDisjointRanges(RangeSample &DisjointRanges,
+ const RangeSample &Ranges) {
+
+ /*
+ Regions may overlap with each other. Using the boundary info, find all
+ disjoint ranges and their sample count. BoundaryPoint contains the count
+ multiple samples begin/end at this points.
+
+ |<--100-->| Sample1
+ |<------200------>| Sample2
+ A B C
+
+ In the example above,
+ Sample1 begins at A, ends at B, its value is 100.
+ Sample2 beings at A, ends at C, its value is 200.
+ For A, BeginCount is the sum of sample begins at A, which is 300 and no
+ samples ends at A, so EndCount is 0.
+ Then boundary points A, B, and C with begin/end counts are:
+ A: (300, 0)
+ B: (0, 100)
+ C: (0, 200)
+ */
+ struct BoundaryPoint {
+ // Sum of sample counts beginning at this point
+ uint64_t BeginCount = UINT64_MAX;
+ // Sum of sample counts ending at this point
+ uint64_t EndCount = UINT64_MAX;
+ // Is the begin point of a zero range.
+ bool IsZeroRangeBegin = false;
+ // Is the end point of a zero range.
+ bool IsZeroRangeEnd = false;
+
+ void addBeginCount(uint64_t Count) {
+ if (BeginCount == UINT64_MAX)
+ BeginCount = 0;
+ BeginCount += Count;
+ }
+
+ void addEndCount(uint64_t Count) {
+ if (EndCount == UINT64_MAX)
+ EndCount = 0;
+ EndCount += Count;
+ }
+ };
+
+ /*
+ For the above example. With boundary points, follwing logic finds two
+ disjoint region of
+
+ [A,B]: 300
+ [B+1,C]: 200
+
+ If there is a boundary point that both begin and end, the point itself
+ becomes a separate disjoint region. For example, if we have original
+ ranges of
+
+ |<--- 100 --->|
+ |<--- 200 --->|
+ A B C
+
+ there are three boundary points with their begin/end counts of
+
+ A: (100, 0)
+ B: (200, 100)
+ C: (0, 200)
+
+ the disjoint ranges would be
+
+ [A, B-1]: 100
+ [B, B]: 300
+ [B+1, C]: 200.
+
+ Example for zero value range:
+
+ |<--- 100 --->|
+ |<--- 200 --->|
+ |<--------------- 0 ----------------->|
+ A B C D E F
+
+ [A, B-1] : 0
+ [B, C] : 100
+ [C+1, D-1]: 0
+ [D, E] : 200
+ [E+1, F] : 0
+ */
+ std::map<uint64_t, BoundaryPoint> Boundaries;
+
+ for (const auto &Item : Ranges) {
+ assert(Item.first.first <= Item.first.second &&
+ "Invalid instruction range");
+ auto &BeginPoint = Boundaries[Item.first.first];
+ auto &EndPoint = Boundaries[Item.first.second];
+ uint64_t Count = Item.second;
+
+ BeginPoint.addBeginCount(Count);
+ EndPoint.addEndCount(Count);
+ if (Count == 0) {
+ BeginPoint.IsZeroRangeBegin = true;
+ EndPoint.IsZeroRangeEnd = true;
+ }
+ }
+
+ // Use UINT64_MAX to indicate there is no existing range between BeginAddress
+ // and the next valid address
+ uint64_t BeginAddress = UINT64_MAX;
+ int ZeroRangeDepth = 0;
+ uint64_t Count = 0;
+ for (const auto &Item : Boundaries) {
+ uint64_t Address = Item.first;
+ const BoundaryPoint &Point = Item.second;
+ if (Point.BeginCount != UINT64_MAX) {
+ if (BeginAddress != UINT64_MAX)
+ DisjointRanges[{BeginAddress, Address - 1}] = Count;
+ Count += Point.BeginCount;
+ BeginAddress = Address;
+ ZeroRangeDepth += Point.IsZeroRangeBegin;
+ }
+ if (Point.EndCount != UINT64_MAX) {
+ assert((BeginAddress != UINT64_MAX) &&
+ "First boundary point cannot be 'end' point");
+ DisjointRanges[{BeginAddress, Address}] = Count;
+ assert(Count >= Point.EndCount && "Mismatched live ranges");
+ Count -= Point.EndCount;
+ BeginAddress = Address + 1;
+ ZeroRangeDepth -= Point.IsZeroRangeEnd;
+ // If the remaining count is zero and it's no longer in a zero range, this
+ // means we consume all the ranges before, thus mark BeginAddress as
+ // UINT64_MAX. e.g. supposing we have two non-overlapping ranges:
+ // [<---- 10 ---->]
+ // [<---- 20 ---->]
+ // A B C D
+ // The BeginAddress(B+1) will reset to invalid(UINT64_MAX), so we won't
+ // have the [B+1, C-1] zero range.
+ if (Count == 0 && ZeroRangeDepth == 0)
+ BeginAddress = UINT64_MAX;
+ }
+ }
+}
+
+void ProfileGeneratorBase::updateBodySamplesforFunctionProfile(
+ FunctionSamples &FunctionProfile, const SampleContextFrame &LeafLoc,
+ uint64_t Count) {
+ // Use the maximum count of samples with same line location
+ uint32_t Discriminator = getBaseDiscriminator(LeafLoc.Location.Discriminator);
+
+ // Use duplication factor to compensated for loop unroll/vectorization.
+ // Note that this is only needed when we're taking MAX of the counts at
+ // the location instead of SUM.
+ Count *= getDuplicationFactor(LeafLoc.Location.Discriminator);
+
+ ErrorOr<uint64_t> R =
+ FunctionProfile.findSamplesAt(LeafLoc.Location.LineOffset, Discriminator);
+
+ uint64_t PreviousCount = R ? R.get() : 0;
+ if (PreviousCount <= Count) {
+ FunctionProfile.addBodySamples(LeafLoc.Location.LineOffset, Discriminator,
+ Count - PreviousCount);
+ }
+}
+
+void ProfileGeneratorBase::updateTotalSamples() {
+ if (!UpdateTotalSamples)
+ return;
+
+ for (auto &Item : ProfileMap) {
+ FunctionSamples &FunctionProfile = Item.second;
+ FunctionProfile.updateTotalSamples();
+ }
+}
+
+FunctionSamples &
+ProfileGenerator::getTopLevelFunctionProfile(StringRef FuncName) {
+ SampleContext Context(FuncName);
+ auto Ret = ProfileMap.emplace(Context, FunctionSamples());
+ if (Ret.second) {
+ FunctionSamples &FProfile = Ret.first->second;
+ FProfile.setContext(Context);
+ }
+ return Ret.first->second;
+}
+
+void ProfileGenerator::generateProfile() {
+ if (Binary->usePseudoProbes()) {
+ // TODO: Support probe based profile generation
+ exitWithError("Probe based profile generation not supported for AutoFDO, "
+ "consider dropping `--ignore-stack-samples` or adding `--use-dwarf-correlation`.");
+ } else {
+ generateLineNumBasedProfile();
+ }
+ postProcessProfiles();
+}
+
+void ProfileGenerator::postProcessProfiles() {
+ computeSummaryAndThreshold();
+ trimColdProfiles(ProfileMap, ColdCountThreshold);
+ calculateAndShowDensity(ProfileMap);
+}
+
+void ProfileGenerator::trimColdProfiles(const SampleProfileMap &Profiles,
+ uint64_t ColdCntThreshold) {
+ if (!TrimColdProfile)
+ return;
+
+ // Move cold profiles into a tmp container.
+ std::vector<SampleContext> ColdProfiles;
+ for (const auto &I : ProfileMap) {
+ if (I.second.getTotalSamples() < ColdCntThreshold)
+ ColdProfiles.emplace_back(I.first);
+ }
+
+ // Remove the cold profile from ProfileMap.
+ for (const auto &I : ColdProfiles)
+ ProfileMap.erase(I);
+}
+
+void ProfileGenerator::generateLineNumBasedProfile() {
+ assert(SampleCounters.size() == 1 &&
+ "Must have one entry for profile generation.");
+ const SampleCounter &SC = SampleCounters.begin()->second;
+ // Fill in function body samples
+ populateBodySamplesForAllFunctions(SC.RangeCounter);
+ // Fill in boundary sample counts as well as call site samples for calls
+ populateBoundarySamplesForAllFunctions(SC.BranchCounter);
+
+ updateTotalSamples();
+}
+
+FunctionSamples &ProfileGenerator::getLeafProfileAndAddTotalSamples(
+ const SampleContextFrameVector &FrameVec, uint64_t Count) {
+ // Get top level profile
+ FunctionSamples *FunctionProfile =
+ &getTopLevelFunctionProfile(FrameVec[0].FuncName);
+ FunctionProfile->addTotalSamples(Count);
+
+ for (size_t I = 1; I < FrameVec.size(); I++) {
+ LineLocation Callsite(
+ FrameVec[I - 1].Location.LineOffset,
+ getBaseDiscriminator(FrameVec[I - 1].Location.Discriminator));
+ FunctionSamplesMap &SamplesMap =
+ FunctionProfile->functionSamplesAt(Callsite);
+ auto Ret =
+ SamplesMap.emplace(FrameVec[I].FuncName.str(), FunctionSamples());
+ if (Ret.second) {
+ SampleContext Context(FrameVec[I].FuncName);
+ Ret.first->second.setContext(Context);
+ }
+ FunctionProfile = &Ret.first->second;
+ FunctionProfile->addTotalSamples(Count);
+ }
+
+ return *FunctionProfile;
+}
+
+RangeSample
+ProfileGenerator::preprocessRangeCounter(const RangeSample &RangeCounter) {
+ RangeSample Ranges(RangeCounter.begin(), RangeCounter.end());
+ if (FillZeroForAllFuncs) {
+ for (auto &FuncI : Binary->getAllBinaryFunctions()) {
+ for (auto &R : FuncI.second.Ranges) {
+ Ranges[{R.first, R.second - 1}] += 0;
+ }
+ }
+ } else {
+ // For each range, we search for all ranges of the function it belongs to
+ // and initialize it with zero count, so it remains zero if doesn't hit any
+ // samples. This is to be consistent with compiler that interpret zero count
+ // as unexecuted(cold).
+ for (const auto &I : RangeCounter) {
+ uint64_t StartOffset = I.first.first;
+ for (const auto &Range : Binary->getRangesForOffset(StartOffset))
+ Ranges[{Range.first, Range.second - 1}] += 0;
+ }
+ }
+ RangeSample DisjointRanges;
+ findDisjointRanges(DisjointRanges, Ranges);
+ return DisjointRanges;
+}
+
+void ProfileGenerator::populateBodySamplesForAllFunctions(
+ const RangeSample &RangeCounter) {
+ for (const auto &Range : preprocessRangeCounter(RangeCounter)) {
+ uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first);
+ uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second);
+ uint64_t Count = Range.second;
+
+ InstructionPointer IP(Binary, RangeBegin, true);
+ // Disjoint ranges may have range in the middle of two instr,
+ // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range
+ // can be Addr1+1 to Addr2-1. We should ignore such range.
+ if (IP.Address > RangeEnd)
+ continue;
+
+ do {
+ uint64_t Offset = Binary->virtualAddrToOffset(IP.Address);
+ const SampleContextFrameVector &FrameVec =
+ Binary->getFrameLocationStack(Offset);
+ if (!FrameVec.empty()) {
+ // FIXME: As accumulating total count per instruction caused some
+ // regression, we changed to accumulate total count per byte as a
+ // workaround. Tuning hotness threshold on the compiler side might be
+ // necessary in the future.
+ FunctionSamples &FunctionProfile = getLeafProfileAndAddTotalSamples(
+ FrameVec, Count * Binary->getInstSize(Offset));
+ updateBodySamplesforFunctionProfile(FunctionProfile, FrameVec.back(),
+ Count);
+ }
+ } while (IP.advance() && IP.Address <= RangeEnd);
+ }
+}
+
+StringRef ProfileGeneratorBase::getCalleeNameForOffset(uint64_t TargetOffset) {
+ // Get the function range by branch target if it's a call branch.
+ auto *FRange = Binary->findFuncRangeForStartOffset(TargetOffset);
+
+ // We won't accumulate sample count for a range whose start is not the real
+ // function entry such as outlined function or inner labels.
+ if (!FRange || !FRange->IsFuncEntry)
+ return StringRef();
+
+ return FunctionSamples::getCanonicalFnName(FRange->getFuncName());
+}
+
+void ProfileGenerator::populateBoundarySamplesForAllFunctions(
+ const BranchSample &BranchCounters) {
+ for (const auto &Entry : BranchCounters) {
+ uint64_t SourceOffset = Entry.first.first;
+ uint64_t TargetOffset = Entry.first.second;
+ uint64_t Count = Entry.second;
+ assert(Count != 0 && "Unexpected zero weight branch");
+
+ StringRef CalleeName = getCalleeNameForOffset(TargetOffset);
+ if (CalleeName.size() == 0)
+ continue;
+ // Record called target sample and its count.
+ const SampleContextFrameVector &FrameVec =
+ Binary->getFrameLocationStack(SourceOffset);
+ if (!FrameVec.empty()) {
+ FunctionSamples &FunctionProfile =
+ getLeafProfileAndAddTotalSamples(FrameVec, 0);
+ FunctionProfile.addCalledTargetSamples(
+ FrameVec.back().Location.LineOffset,
+ getBaseDiscriminator(FrameVec.back().Location.Discriminator),
+ CalleeName, Count);
+ }
+ // Add head samples for callee.
+ FunctionSamples &CalleeProfile = getTopLevelFunctionProfile(CalleeName);
+ CalleeProfile.addHeadSamples(Count);
+ }
+}
+
+void ProfileGeneratorBase::calculateAndShowDensity(
+ const SampleProfileMap &Profiles) {
+ double Density = calculateDensity(Profiles, HotCountThreshold);
+ showDensitySuggestion(Density);
+}
+
+FunctionSamples &CSProfileGenerator::getFunctionProfileForContext(
+ const SampleContextFrameVector &Context, bool WasLeafInlined) {
+ auto I = ProfileMap.find(SampleContext(Context));
+ if (I == ProfileMap.end()) {
+ // Save the new context for future references.
+ SampleContextFrames NewContext = *Contexts.insert(Context).first;
+ SampleContext FContext(NewContext, RawContext);
+ auto Ret = ProfileMap.emplace(FContext, FunctionSamples());
+ if (WasLeafInlined)
+ FContext.setAttribute(ContextWasInlined);
+ FunctionSamples &FProfile = Ret.first->second;
+ FProfile.setContext(FContext);
+ return Ret.first->second;
+ }
+ return I->second;
+}
+
+void CSProfileGenerator::generateProfile() {
+ FunctionSamples::ProfileIsCSFlat = true;
+
+ if (Binary->getTrackFuncContextSize())
+ computeSizeForProfiledFunctions();
+
+ if (Binary->usePseudoProbes()) {
+ // Enable pseudo probe functionalities in SampleProf
+ FunctionSamples::ProfileIsProbeBased = true;
+ generateProbeBasedProfile();
+ } else {
+ generateLineNumBasedProfile();
+ }
+ postProcessProfiles();
+}
+
+void CSProfileGenerator::computeSizeForProfiledFunctions() {
+ // Hash map to deduplicate the function range and the item is a pair of
+ // function start and end offset.
+ std::unordered_map<uint64_t, uint64_t> AggregatedRanges;
+ // Go through all the ranges in the CS counters, use the start of the range to
+ // look up the function it belongs and record the function range.
+ for (const auto &CI : SampleCounters) {
+ for (const auto &Item : CI.second.RangeCounter) {
+ // FIXME: Filter the bogus crossing function range.
+ uint64_t StartOffset = Item.first.first;
+ // Note that a function can be spilt into multiple ranges, so get all
+ // ranges of the function.
+ for (const auto &Range : Binary->getRangesForOffset(StartOffset))
+ AggregatedRanges[Range.first] = Range.second;
+ }
+ }
+
+ for (const auto &I : AggregatedRanges) {
+ uint64_t StartOffset = I.first;
+ uint64_t EndOffset = I.second;
+ Binary->computeInlinedContextSizeForRange(StartOffset, EndOffset);
+ }
+}
+
+void CSProfileGenerator::generateLineNumBasedProfile() {
+ for (const auto &CI : SampleCounters) {
+ const auto *CtxKey = cast<StringBasedCtxKey>(CI.first.getPtr());
+
+ // Get or create function profile for the range
+ FunctionSamples &FunctionProfile =
+ getFunctionProfileForContext(CtxKey->Context, CtxKey->WasLeafInlined);
+
+ // Fill in function body samples
+ populateBodySamplesForFunction(FunctionProfile, CI.second.RangeCounter);
+ // Fill in boundary sample counts as well as call site samples for calls
+ populateBoundarySamplesForFunction(CtxKey->Context, FunctionProfile,
+ CI.second.BranchCounter);
+ }
+ // Fill in call site value sample for inlined calls and also use context to
+ // infer missing samples. Since we don't have call count for inlined
+ // functions, we estimate it from inlinee's profile using the entry of the
+ // body sample.
+ populateInferredFunctionSamples();
+
+ updateTotalSamples();
+}
+
+void CSProfileGenerator::populateBodySamplesForFunction(
+ FunctionSamples &FunctionProfile, const RangeSample &RangeCounter) {
+ // Compute disjoint ranges first, so we can use MAX
+ // for calculating count for each location.
+ RangeSample Ranges;
+ findDisjointRanges(Ranges, RangeCounter);
+ for (const auto &Range : Ranges) {
+ uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first);
+ uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second);
+ uint64_t Count = Range.second;
+ // Disjoint ranges have introduce zero-filled gap that
+ // doesn't belong to current context, filter them out.
+ if (Count == 0)
+ continue;
+
+ InstructionPointer IP(Binary, RangeBegin, true);
+ // Disjoint ranges may have range in the middle of two instr,
+ // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range
+ // can be Addr1+1 to Addr2-1. We should ignore such range.
+ if (IP.Address > RangeEnd)
+ continue;
+
+ do {
+ uint64_t Offset = Binary->virtualAddrToOffset(IP.Address);
+ auto LeafLoc = Binary->getInlineLeafFrameLoc(Offset);
+ if (LeafLoc.hasValue()) {
+ // Recording body sample for this specific context
+ updateBodySamplesforFunctionProfile(FunctionProfile, *LeafLoc, Count);
+ FunctionProfile.addTotalSamples(Count);
+ }
+ } while (IP.advance() && IP.Address <= RangeEnd);
+ }
+}
+
+void CSProfileGenerator::populateBoundarySamplesForFunction(
+ SampleContextFrames ContextId, FunctionSamples &FunctionProfile,
+ const BranchSample &BranchCounters) {
+
+ for (const auto &Entry : BranchCounters) {
+ uint64_t SourceOffset = Entry.first.first;
+ uint64_t TargetOffset = Entry.first.second;
+ uint64_t Count = Entry.second;
+ assert(Count != 0 && "Unexpected zero weight branch");
+
+ StringRef CalleeName = getCalleeNameForOffset(TargetOffset);
+ if (CalleeName.size() == 0)
+ continue;
+
+ // Record called target sample and its count
+ auto LeafLoc = Binary->getInlineLeafFrameLoc(SourceOffset);
+ if (!LeafLoc.hasValue())
+ continue;
+ FunctionProfile.addCalledTargetSamples(
+ LeafLoc->Location.LineOffset,
+ getBaseDiscriminator(LeafLoc->Location.Discriminator), CalleeName,
+ Count);
+
+ // Record head sample for called target(callee)
+ SampleContextFrameVector CalleeCtx(ContextId.begin(), ContextId.end());
+ assert(CalleeCtx.back().FuncName == LeafLoc->FuncName &&
+ "Leaf function name doesn't match");
+ CalleeCtx.back() = *LeafLoc;
+ CalleeCtx.emplace_back(CalleeName, LineLocation(0, 0));
+ FunctionSamples &CalleeProfile = getFunctionProfileForContext(CalleeCtx);
+ CalleeProfile.addHeadSamples(Count);
+ }
+}
+
+static SampleContextFrame
+getCallerContext(SampleContextFrames CalleeContext,
+ SampleContextFrameVector &CallerContext) {
+ assert(CalleeContext.size() > 1 && "Unexpected empty context");
+ CalleeContext = CalleeContext.drop_back();
+ CallerContext.assign(CalleeContext.begin(), CalleeContext.end());
+ SampleContextFrame CallerFrame = CallerContext.back();
+ CallerContext.back().Location = LineLocation(0, 0);
+ return CallerFrame;
+}
+
+void CSProfileGenerator::populateInferredFunctionSamples() {
+ for (const auto &Item : ProfileMap) {
+ const auto &CalleeContext = Item.first;
+ const FunctionSamples &CalleeProfile = Item.second;
+
+ // If we already have head sample counts, we must have value profile
+ // for call sites added already. Skip to avoid double counting.
+ if (CalleeProfile.getHeadSamples())
+ continue;
+ // If we don't have context, nothing to do for caller's call site.
+ // This could happen for entry point function.
+ if (CalleeContext.isBaseContext())
+ continue;
+
+ // Infer Caller's frame loc and context ID through string splitting
+ SampleContextFrameVector CallerContextId;
+ SampleContextFrame &&CallerLeafFrameLoc =
+ getCallerContext(CalleeContext.getContextFrames(), CallerContextId);
+ SampleContextFrames CallerContext(CallerContextId);
+
+ // It's possible that we haven't seen any sample directly in the caller,
+ // in which case CallerProfile will not exist. But we can't modify
+ // ProfileMap while iterating it.
+ // TODO: created function profile for those callers too
+ if (ProfileMap.find(CallerContext) == ProfileMap.end())
+ continue;
+ FunctionSamples &CallerProfile = ProfileMap[CallerContext];
+
+ // Since we don't have call count for inlined functions, we
+ // estimate it from inlinee's profile using entry body sample.
+ uint64_t EstimatedCallCount = CalleeProfile.getEntrySamples();
+ // If we don't have samples with location, use 1 to indicate live.
+ if (!EstimatedCallCount && !CalleeProfile.getBodySamples().size())
+ EstimatedCallCount = 1;
+ CallerProfile.addCalledTargetSamples(
+ CallerLeafFrameLoc.Location.LineOffset,
+ CallerLeafFrameLoc.Location.Discriminator,
+ CalleeProfile.getContext().getName(), EstimatedCallCount);
+ CallerProfile.addBodySamples(CallerLeafFrameLoc.Location.LineOffset,
+ CallerLeafFrameLoc.Location.Discriminator,
+ EstimatedCallCount);
+ CallerProfile.addTotalSamples(EstimatedCallCount);
+ }
+}
+
+void CSProfileGenerator::postProcessProfiles() {
+ // Compute hot/cold threshold based on profile. This will be used for cold
+ // context profile merging/trimming.
+ computeSummaryAndThreshold();
+
+ // Run global pre-inliner to adjust/merge context profile based on estimated
+ // inline decisions.
+ if (EnableCSPreInliner) {
+ CSPreInliner(ProfileMap, *Binary, HotCountThreshold, ColdCountThreshold)
+ .run();
+ // Turn off the profile merger by default unless it is explicitly enabled.
+ if (!CSProfMergeColdContext.getNumOccurrences())
+ CSProfMergeColdContext = false;
+ }
+
+ // Trim and merge cold context profile using cold threshold above.
+ if (TrimColdProfile || CSProfMergeColdContext) {
+ SampleContextTrimmer(ProfileMap)
+ .trimAndMergeColdContextProfiles(
+ HotCountThreshold, TrimColdProfile, CSProfMergeColdContext,
+ CSProfMaxColdContextDepth, EnableCSPreInliner);
+ }
+
+ // Merge function samples of CS profile to calculate profile density.
+ sampleprof::SampleProfileMap ContextLessProfiles;
+ for (const auto &I : ProfileMap) {
+ ContextLessProfiles[I.second.getName()].merge(I.second);
+ }
+
+ calculateAndShowDensity(ContextLessProfiles);
+ if (GenCSNestedProfile) {
+ CSProfileConverter CSConverter(ProfileMap);
+ CSConverter.convertProfiles();
+ FunctionSamples::ProfileIsCSFlat = false;
+ FunctionSamples::ProfileIsCSNested = EnableCSPreInliner;
+ }
+}
+
+void ProfileGeneratorBase::computeSummaryAndThreshold() {
+ SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
+ auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
+ HotCountThreshold = ProfileSummaryBuilder::getHotCountThreshold(
+ (Summary->getDetailedSummary()));
+ ColdCountThreshold = ProfileSummaryBuilder::getColdCountThreshold(
+ (Summary->getDetailedSummary()));
+}
+
+// Helper function to extract context prefix string stack
+// Extract context stack for reusing, leaf context stack will
+// be added compressed while looking up function profile
+static void extractPrefixContextStack(
+ SampleContextFrameVector &ContextStack,
+ const SmallVectorImpl<const MCDecodedPseudoProbe *> &Probes,
+ ProfiledBinary *Binary) {
+ for (const auto *P : Probes) {
+ Binary->getInlineContextForProbe(P, ContextStack, true);
+ }
+}
+
+void CSProfileGenerator::generateProbeBasedProfile() {
+ for (const auto &CI : SampleCounters) {
+ const ProbeBasedCtxKey *CtxKey =
+ dyn_cast<ProbeBasedCtxKey>(CI.first.getPtr());
+ SampleContextFrameVector ContextStack;
+ extractPrefixContextStack(ContextStack, CtxKey->Probes, Binary);
+ // Fill in function body samples from probes, also infer caller's samples
+ // from callee's probe
+ populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStack);
+ // Fill in boundary samples for a call probe
+ populateBoundarySamplesWithProbes(CI.second.BranchCounter, ContextStack);
+ }
+}
+
+void CSProfileGenerator::extractProbesFromRange(const RangeSample &RangeCounter,
+ ProbeCounterMap &ProbeCounter) {
+ RangeSample Ranges;
+ findDisjointRanges(Ranges, RangeCounter);
+ for (const auto &Range : Ranges) {
+ uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first);
+ uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second);
+ uint64_t Count = Range.second;
+ // Disjoint ranges have introduce zero-filled gap that
+ // doesn't belong to current context, filter them out.
+ if (Count == 0)
+ continue;
+
+ InstructionPointer IP(Binary, RangeBegin, true);
+ // Disjoint ranges may have range in the middle of two instr,
+ // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range
+ // can be Addr1+1 to Addr2-1. We should ignore such range.
+ if (IP.Address > RangeEnd)
+ continue;
+
+ do {
+ const AddressProbesMap &Address2ProbesMap =
+ Binary->getAddress2ProbesMap();
+ auto It = Address2ProbesMap.find(IP.Address);
+ if (It != Address2ProbesMap.end()) {
+ for (const auto &Probe : It->second) {
+ if (!Probe.isBlock())
+ continue;
+ ProbeCounter[&Probe] += Count;
+ }
+ }
+ } while (IP.advance() && IP.Address <= RangeEnd);
+ }
+}
+
+void CSProfileGenerator::populateBodySamplesWithProbes(
+ const RangeSample &RangeCounter, SampleContextFrames ContextStack) {
+ ProbeCounterMap ProbeCounter;
+ // Extract the top frame probes by looking up each address among the range in
+ // the Address2ProbeMap
+ extractProbesFromRange(RangeCounter, ProbeCounter);
+ std::unordered_map<MCDecodedPseudoProbeInlineTree *,
+ std::unordered_set<FunctionSamples *>>
+ FrameSamples;
+ for (const auto &PI : ProbeCounter) {
+ const MCDecodedPseudoProbe *Probe = PI.first;
+ uint64_t Count = PI.second;
+ FunctionSamples &FunctionProfile =
+ getFunctionProfileForLeafProbe(ContextStack, Probe);
+ // Record the current frame and FunctionProfile whenever samples are
+ // collected for non-danglie probes. This is for reporting all of the
+ // zero count probes of the frame later.
+ FrameSamples[Probe->getInlineTreeNode()].insert(&FunctionProfile);
+ FunctionProfile.addBodySamplesForProbe(Probe->getIndex(), Count);
+ FunctionProfile.addTotalSamples(Count);
+ if (Probe->isEntry()) {
+ FunctionProfile.addHeadSamples(Count);
+ // Look up for the caller's function profile
+ const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe);
+ SampleContextFrames CalleeContextId =
+ FunctionProfile.getContext().getContextFrames();
+ if (InlinerDesc != nullptr && CalleeContextId.size() > 1) {
+ // Since the context id will be compressed, we have to use callee's
+ // context id to infer caller's context id to ensure they share the
+ // same context prefix.
+ SampleContextFrameVector CallerContextId;
+ SampleContextFrame &&CallerLeafFrameLoc =
+ getCallerContext(CalleeContextId, CallerContextId);
+ uint64_t CallerIndex = CallerLeafFrameLoc.Location.LineOffset;
+ assert(CallerIndex &&
+ "Inferred caller's location index shouldn't be zero!");
+ FunctionSamples &CallerProfile =
+ getFunctionProfileForContext(CallerContextId);
+ CallerProfile.setFunctionHash(InlinerDesc->FuncHash);
+ CallerProfile.addBodySamples(CallerIndex, 0, Count);
+ CallerProfile.addTotalSamples(Count);
+ CallerProfile.addCalledTargetSamples(
+ CallerIndex, 0, FunctionProfile.getContext().getName(), Count);
+ }
+ }
+ }
+
+ // Assign zero count for remaining probes without sample hits to
+ // differentiate from probes optimized away, of which the counts are unknown
+ // and will be inferred by the compiler.
+ for (auto &I : FrameSamples) {
+ for (auto *FunctionProfile : I.second) {
+ for (auto *Probe : I.first->getProbes()) {
+ FunctionProfile->addBodySamplesForProbe(Probe->getIndex(), 0);
+ }
+ }
+ }
+}
+
+void CSProfileGenerator::populateBoundarySamplesWithProbes(
+ const BranchSample &BranchCounter, SampleContextFrames ContextStack) {
+ for (const auto &BI : BranchCounter) {
+ uint64_t SourceOffset = BI.first.first;
+ uint64_t TargetOffset = BI.first.second;
+ uint64_t Count = BI.second;
+ uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset);
+ const MCDecodedPseudoProbe *CallProbe =
+ Binary->getCallProbeForAddr(SourceAddress);
+ if (CallProbe == nullptr)
+ continue;
+ FunctionSamples &FunctionProfile =
+ getFunctionProfileForLeafProbe(ContextStack, CallProbe);
+ FunctionProfile.addBodySamples(CallProbe->getIndex(), 0, Count);
+ FunctionProfile.addTotalSamples(Count);
+ StringRef CalleeName = getCalleeNameForOffset(TargetOffset);
+ if (CalleeName.size() == 0)
+ continue;
+ FunctionProfile.addCalledTargetSamples(CallProbe->getIndex(), 0, CalleeName,
+ Count);
+ }
+}
+
+FunctionSamples &CSProfileGenerator::getFunctionProfileForLeafProbe(
+ SampleContextFrames ContextStack, const MCDecodedPseudoProbe *LeafProbe) {
+
+ // Explicitly copy the context for appending the leaf context
+ SampleContextFrameVector NewContextStack(ContextStack.begin(),
+ ContextStack.end());
+ Binary->getInlineContextForProbe(LeafProbe, NewContextStack, true);
+ // For leaf inlined context with the top frame, we should strip off the top
+ // frame's probe id, like:
+ // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar"
+ auto LeafFrame = NewContextStack.back();
+ LeafFrame.Location = LineLocation(0, 0);
+ NewContextStack.pop_back();
+ // Compress the context string except for the leaf frame
+ CSProfileGenerator::compressRecursionContext(NewContextStack);
+ CSProfileGenerator::trimContext(NewContextStack);
+ NewContextStack.push_back(LeafFrame);
+
+ const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->getGuid());
+ bool WasLeafInlined = LeafProbe->getInlineTreeNode()->hasInlineSite();
+ FunctionSamples &FunctionProile =
+ getFunctionProfileForContext(NewContextStack, WasLeafInlined);
+ FunctionProile.setFunctionHash(FuncDesc->FuncHash);
+ return FunctionProile;
+}
+
+} // end namespace sampleprof
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/ProfileGenerator.h b/contrib/libs/llvm14/tools/llvm-profgen/ProfileGenerator.h
new file mode 100644
index 00000000000..af349ac9911
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/ProfileGenerator.h
@@ -0,0 +1,312 @@
+//===-- ProfileGenerator.h - Profile Generator -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_PROGEN_PROFILEGENERATOR_H
+#define LLVM_TOOLS_LLVM_PROGEN_PROFILEGENERATOR_H
+#include "CSPreInliner.h"
+#include "ErrorHandling.h"
+#include "PerfReader.h"
+#include "ProfiledBinary.h"
+#include "llvm/ProfileData/SampleProfWriter.h"
+#include <memory>
+#include <unordered_set>
+
+using namespace llvm;
+using namespace sampleprof;
+
+namespace llvm {
+namespace sampleprof {
+
+// This base class for profile generation of sample-based PGO. We reuse all
+// structures relating to function profiles and profile writers as seen in
+// /ProfileData/SampleProf.h.
+class ProfileGeneratorBase {
+
+public:
+ ProfileGeneratorBase(ProfiledBinary *Binary,
+ const ContextSampleCounterMap &Counters)
+ : Binary(Binary), SampleCounters(Counters){};
+ virtual ~ProfileGeneratorBase() = default;
+ static std::unique_ptr<ProfileGeneratorBase>
+ create(ProfiledBinary *Binary, const ContextSampleCounterMap &SampleCounters,
+ bool ProfileIsCSFlat);
+ virtual void generateProfile() = 0;
+ void write();
+
+ static uint32_t
+ getDuplicationFactor(unsigned Discriminator,
+ bool UseFSD = ProfileGeneratorBase::UseFSDiscriminator) {
+ return UseFSD ? 1
+ : llvm::DILocation::getDuplicationFactorFromDiscriminator(
+ Discriminator);
+ }
+
+ static uint32_t
+ getBaseDiscriminator(unsigned Discriminator,
+ bool UseFSD = ProfileGeneratorBase::UseFSDiscriminator) {
+ return UseFSD ? Discriminator
+ : DILocation::getBaseDiscriminatorFromDiscriminator(
+ Discriminator, /* IsFSDiscriminator */ false);
+ }
+
+ static bool UseFSDiscriminator;
+
+protected:
+ // Use SampleProfileWriter to serialize profile map
+ void write(std::unique_ptr<SampleProfileWriter> Writer,
+ SampleProfileMap &ProfileMap);
+ /*
+ For each region boundary point, mark if it is begin or end (or both) of
+ the region. Boundary points are inclusive. Log the sample count as well
+ so we can use it when we compute the sample count of each disjoint region
+ later. Note that there might be multiple ranges with different sample
+ count that share same begin/end point. We need to accumulate the sample
+ count for the boundary point for such case, because for the example
+ below,
+
+ |<--100-->|
+ |<------200------>|
+ A B C
+
+ sample count for disjoint region [A,B] would be 300.
+ */
+ void findDisjointRanges(RangeSample &DisjointRanges,
+ const RangeSample &Ranges);
+ // Helper function for updating body sample for a leaf location in
+ // FunctionProfile
+ void updateBodySamplesforFunctionProfile(FunctionSamples &FunctionProfile,
+ const SampleContextFrame &LeafLoc,
+ uint64_t Count);
+ void updateTotalSamples();
+
+ StringRef getCalleeNameForOffset(uint64_t TargetOffset);
+
+ void computeSummaryAndThreshold();
+
+ void calculateAndShowDensity(const SampleProfileMap &Profiles);
+
+ double calculateDensity(const SampleProfileMap &Profiles,
+ uint64_t HotCntThreshold);
+
+ void showDensitySuggestion(double Density);
+
+ // Thresholds from profile summary to answer isHotCount/isColdCount queries.
+ uint64_t HotCountThreshold;
+
+ uint64_t ColdCountThreshold;
+
+ // Used by SampleProfileWriter
+ SampleProfileMap ProfileMap;
+
+ ProfiledBinary *Binary = nullptr;
+
+ const ContextSampleCounterMap &SampleCounters;
+};
+
+class ProfileGenerator : public ProfileGeneratorBase {
+
+public:
+ ProfileGenerator(ProfiledBinary *Binary,
+ const ContextSampleCounterMap &Counters)
+ : ProfileGeneratorBase(Binary, Counters){};
+ void generateProfile() override;
+
+private:
+ void generateLineNumBasedProfile();
+ RangeSample preprocessRangeCounter(const RangeSample &RangeCounter);
+ FunctionSamples &getTopLevelFunctionProfile(StringRef FuncName);
+ // Helper function to get the leaf frame's FunctionProfile by traversing the
+ // inline stack and meanwhile it adds the total samples for each frame's
+ // function profile.
+ FunctionSamples &
+ getLeafProfileAndAddTotalSamples(const SampleContextFrameVector &FrameVec,
+ uint64_t Count);
+ void populateBodySamplesForAllFunctions(const RangeSample &RangeCounter);
+ void
+ populateBoundarySamplesForAllFunctions(const BranchSample &BranchCounters);
+ void postProcessProfiles();
+ void trimColdProfiles(const SampleProfileMap &Profiles,
+ uint64_t ColdCntThreshold);
+};
+
+using ProbeCounterMap =
+ std::unordered_map<const MCDecodedPseudoProbe *, uint64_t>;
+
+class CSProfileGenerator : public ProfileGeneratorBase {
+public:
+ CSProfileGenerator(ProfiledBinary *Binary,
+ const ContextSampleCounterMap &Counters)
+ : ProfileGeneratorBase(Binary, Counters){};
+
+ void generateProfile() override;
+
+ // Trim the context stack at a given depth.
+ template <typename T>
+ static void trimContext(SmallVectorImpl<T> &S, int Depth = MaxContextDepth) {
+ if (Depth < 0 || static_cast<size_t>(Depth) >= S.size())
+ return;
+ std::copy(S.begin() + S.size() - static_cast<size_t>(Depth), S.end(),
+ S.begin());
+ S.resize(Depth);
+ }
+
+ // Remove adjacent repeated context sequences up to a given sequence length,
+ // -1 means no size limit. Note that repeated sequences are identified based
+ // on the exact call site, this is finer granularity than function recursion.
+ template <typename T>
+ static void compressRecursionContext(SmallVectorImpl<T> &Context,
+ int32_t CSize = MaxCompressionSize) {
+ uint32_t I = 1;
+ uint32_t HS = static_cast<uint32_t>(Context.size() / 2);
+ uint32_t MaxDedupSize =
+ CSize == -1 ? HS : std::min(static_cast<uint32_t>(CSize), HS);
+ auto BeginIter = Context.begin();
+ // Use an in-place algorithm to save memory copy
+ // End indicates the end location of current iteration's data
+ uint32_t End = 0;
+ // Deduplicate from length 1 to the max possible size of a repeated
+ // sequence.
+ while (I <= MaxDedupSize) {
+ // This is a linear algorithm that deduplicates adjacent repeated
+ // sequences of size I. The deduplication detection runs on a sliding
+ // window whose size is 2*I and it keeps sliding the window to deduplicate
+ // the data inside. Once duplication is detected, deduplicate it by
+ // skipping the right half part of the window, otherwise just copy back
+ // the new one by appending them at the back of End pointer(for the next
+ // iteration).
+ //
+ // For example:
+ // Input: [a1, a2, b1, b2]
+ // (Added index to distinguish the same char, the origin is [a, a, b,
+ // b], the size of the dedup window is 2(I = 1) at the beginning)
+ //
+ // 1) The initial status is a dummy window[null, a1], then just copy the
+ // right half of the window(End = 0), then slide the window.
+ // Result: [a1], a2, b1, b2 (End points to the element right before ],
+ // after ] is the data of the previous iteration)
+ //
+ // 2) Next window is [a1, a2]. Since a1 == a2, then skip the right half of
+ // the window i.e the duplication happen. Only slide the window.
+ // Result: [a1], a2, b1, b2
+ //
+ // 3) Next window is [a2, b1], copy the right half of the window(b1 is
+ // new) to the End and slide the window.
+ // Result: [a1, b1], b1, b2
+ //
+ // 4) Next window is [b1, b2], same to 2), skip b2.
+ // Result: [a1, b1], b1, b2
+ // After resize, it will be [a, b]
+
+ // Use pointers like below to do comparison inside the window
+ // [a b c a b c]
+ // | | | | |
+ // LeftBoundary Left Right Left+I Right+I
+ // A duplication found if Left < LeftBoundry.
+
+ int32_t Right = I - 1;
+ End = I;
+ int32_t LeftBoundary = 0;
+ while (Right + I < Context.size()) {
+ // To avoids scanning a part of a sequence repeatedly, it finds out
+ // the common suffix of two hald in the window. The common suffix will
+ // serve as the common prefix of next possible pair of duplicate
+ // sequences. The non-common part will be ignored and never scanned
+ // again.
+
+ // For example.
+ // Input: [a, b1], c1, b2, c2
+ // I = 2
+ //
+ // 1) For the window [a, b1, c1, b2], non-common-suffix for the right
+ // part is 'c1', copy it and only slide the window 1 step.
+ // Result: [a, b1, c1], b2, c2
+ //
+ // 2) Next window is [b1, c1, b2, c2], so duplication happen.
+ // Result after resize: [a, b, c]
+
+ int32_t Left = Right;
+ while (Left >= LeftBoundary && Context[Left] == Context[Left + I]) {
+ // Find the longest suffix inside the window. When stops, Left points
+ // at the diverging point in the current sequence.
+ Left--;
+ }
+
+ bool DuplicationFound = (Left < LeftBoundary);
+ // Don't need to recheck the data before Right
+ LeftBoundary = Right + 1;
+ if (DuplicationFound) {
+ // Duplication found, skip right half of the window.
+ Right += I;
+ } else {
+ // Copy the non-common-suffix part of the adjacent sequence.
+ std::copy(BeginIter + Right + 1, BeginIter + Left + I + 1,
+ BeginIter + End);
+ End += Left + I - Right;
+ // Only slide the window by the size of non-common-suffix
+ Right = Left + I;
+ }
+ }
+ // Don't forget the remaining part that's not scanned.
+ std::copy(BeginIter + Right + 1, Context.end(), BeginIter + End);
+ End += Context.size() - Right - 1;
+ I++;
+ Context.resize(End);
+ MaxDedupSize = std::min(static_cast<uint32_t>(End / 2), MaxDedupSize);
+ }
+ }
+
+private:
+ void generateLineNumBasedProfile();
+ // Lookup or create FunctionSamples for the context
+ FunctionSamples &
+ getFunctionProfileForContext(const SampleContextFrameVector &Context,
+ bool WasLeafInlined = false);
+ // For profiled only functions, on-demand compute their inline context
+ // function byte size which is used by the pre-inliner.
+ void computeSizeForProfiledFunctions();
+ // Post processing for profiles before writing out, such as mermining
+ // and trimming cold profiles, running preinliner on profiles.
+ void postProcessProfiles();
+
+ void populateBodySamplesForFunction(FunctionSamples &FunctionProfile,
+ const RangeSample &RangeCounters);
+ void populateBoundarySamplesForFunction(SampleContextFrames ContextId,
+ FunctionSamples &FunctionProfile,
+ const BranchSample &BranchCounters);
+ void populateInferredFunctionSamples();
+
+ void generateProbeBasedProfile();
+ // Go through each address from range to extract the top frame probe by
+ // looking up in the Address2ProbeMap
+ void extractProbesFromRange(const RangeSample &RangeCounter,
+ ProbeCounterMap &ProbeCounter);
+ // Fill in function body samples from probes
+ void populateBodySamplesWithProbes(const RangeSample &RangeCounter,
+ SampleContextFrames ContextStack);
+ // Fill in boundary samples for a call probe
+ void populateBoundarySamplesWithProbes(const BranchSample &BranchCounter,
+ SampleContextFrames ContextStack);
+ // Helper function to get FunctionSamples for the leaf probe
+ FunctionSamples &
+ getFunctionProfileForLeafProbe(SampleContextFrames ContextStack,
+ const MCDecodedPseudoProbe *LeafProbe);
+
+ // Underlying context table serves for sample profile writer.
+ std::unordered_set<SampleContextFrameVector, SampleContextFrameHash> Contexts;
+
+public:
+ // Deduplicate adjacent repeated context sequences up to a given sequence
+ // length. -1 means no size limit.
+ static int32_t MaxCompressionSize;
+ static int MaxContextDepth;
+};
+
+} // end namespace sampleprof
+} // end namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/ProfiledBinary.cpp b/contrib/libs/llvm14/tools/llvm-profgen/ProfiledBinary.cpp
new file mode 100644
index 00000000000..a773a3c98d4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/ProfiledBinary.cpp
@@ -0,0 +1,790 @@
+//===-- ProfiledBinary.cpp - Binary decoder ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProfiledBinary.h"
+#include "ErrorHandling.h"
+#include "ProfileGenerator.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/TargetSelect.h"
+
+#define DEBUG_TYPE "load-binary"
+
+using namespace llvm;
+using namespace sampleprof;
+
+cl::opt<bool> ShowDisassemblyOnly("show-disassembly-only", cl::init(false),
+ cl::ZeroOrMore,
+ cl::desc("Print disassembled code."));
+
+cl::opt<bool> ShowSourceLocations("show-source-locations", cl::init(false),
+ cl::ZeroOrMore,
+ cl::desc("Print source locations."));
+
+static cl::opt<bool>
+ ShowCanonicalFnName("show-canonical-fname", cl::init(false), cl::ZeroOrMore,
+ cl::desc("Print canonical function name."));
+
+static cl::opt<bool> ShowPseudoProbe(
+ "show-pseudo-probe", cl::init(false), cl::ZeroOrMore,
+ cl::desc("Print pseudo probe section and disassembled info."));
+
+static cl::opt<bool> UseDwarfCorrelation(
+ "use-dwarf-correlation", cl::init(false), cl::ZeroOrMore,
+ cl::desc("Use dwarf for profile correlation even when binary contains "
+ "pseudo probe."));
+
+static cl::list<std::string> DisassembleFunctions(
+ "disassemble-functions", cl::CommaSeparated,
+ cl::desc("List of functions to print disassembly for. Accept demangled "
+ "names only. Only work with show-disassembly-only"));
+
+extern cl::opt<bool> ShowDetailedWarning;
+
+namespace llvm {
+namespace sampleprof {
+
+static const Target *getTarget(const ObjectFile *Obj) {
+ Triple TheTriple = Obj->makeTriple();
+ std::string Error;
+ std::string ArchName;
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget(ArchName, TheTriple, Error);
+ if (!TheTarget)
+ exitWithError(Error, Obj->getFileName());
+ return TheTarget;
+}
+
+void BinarySizeContextTracker::addInstructionForContext(
+ const SampleContextFrameVector &Context, uint32_t InstrSize) {
+ ContextTrieNode *CurNode = &RootContext;
+ bool IsLeaf = true;
+ for (const auto &Callsite : reverse(Context)) {
+ StringRef CallerName = Callsite.FuncName;
+ LineLocation CallsiteLoc = IsLeaf ? LineLocation(0, 0) : Callsite.Location;
+ CurNode = CurNode->getOrCreateChildContext(CallsiteLoc, CallerName);
+ IsLeaf = false;
+ }
+
+ CurNode->addFunctionSize(InstrSize);
+}
+
+uint32_t
+BinarySizeContextTracker::getFuncSizeForContext(const SampleContext &Context) {
+ ContextTrieNode *CurrNode = &RootContext;
+ ContextTrieNode *PrevNode = nullptr;
+ SampleContextFrames Frames = Context.getContextFrames();
+ int32_t I = Frames.size() - 1;
+ Optional<uint32_t> Size;
+
+ // Start from top-level context-less function, traverse down the reverse
+ // context trie to find the best/longest match for given context, then
+ // retrieve the size.
+
+ while (CurrNode && I >= 0) {
+ // Process from leaf function to callers (added to context).
+ const auto &ChildFrame = Frames[I--];
+ PrevNode = CurrNode;
+ CurrNode =
+ CurrNode->getChildContext(ChildFrame.Location, ChildFrame.FuncName);
+ if (CurrNode && CurrNode->getFunctionSize().hasValue())
+ Size = CurrNode->getFunctionSize().getValue();
+ }
+
+ // If we traversed all nodes along the path of the context and haven't
+ // found a size yet, pivot to look for size from sibling nodes, i.e size
+ // of inlinee under different context.
+ if (!Size.hasValue()) {
+ if (!CurrNode)
+ CurrNode = PrevNode;
+ while (!Size.hasValue() && CurrNode &&
+ !CurrNode->getAllChildContext().empty()) {
+ CurrNode = &CurrNode->getAllChildContext().begin()->second;
+ if (CurrNode->getFunctionSize().hasValue())
+ Size = CurrNode->getFunctionSize().getValue();
+ }
+ }
+
+ assert(Size.hasValue() && "We should at least find one context size.");
+ return Size.getValue();
+}
+
+void BinarySizeContextTracker::trackInlineesOptimizedAway(
+ MCPseudoProbeDecoder &ProbeDecoder) {
+ ProbeFrameStack ProbeContext;
+ for (const auto &Child : ProbeDecoder.getDummyInlineRoot().getChildren())
+ trackInlineesOptimizedAway(ProbeDecoder, *Child.second.get(), ProbeContext);
+}
+
+void BinarySizeContextTracker::trackInlineesOptimizedAway(
+ MCPseudoProbeDecoder &ProbeDecoder,
+ MCDecodedPseudoProbeInlineTree &ProbeNode, ProbeFrameStack &ProbeContext) {
+ StringRef FuncName =
+ ProbeDecoder.getFuncDescForGUID(ProbeNode.Guid)->FuncName;
+ ProbeContext.emplace_back(FuncName, 0);
+
+ // This ProbeContext has a probe, so it has code before inlining and
+ // optimization. Make sure we mark its size as known.
+ if (!ProbeNode.getProbes().empty()) {
+ ContextTrieNode *SizeContext = &RootContext;
+ for (auto &ProbeFrame : reverse(ProbeContext)) {
+ StringRef CallerName = ProbeFrame.first;
+ LineLocation CallsiteLoc(ProbeFrame.second, 0);
+ SizeContext =
+ SizeContext->getOrCreateChildContext(CallsiteLoc, CallerName);
+ }
+ // Add 0 size to make known.
+ SizeContext->addFunctionSize(0);
+ }
+
+ // DFS down the probe inline tree
+ for (const auto &ChildNode : ProbeNode.getChildren()) {
+ InlineSite Location = ChildNode.first;
+ ProbeContext.back().second = std::get<1>(Location);
+ trackInlineesOptimizedAway(ProbeDecoder, *ChildNode.second.get(), ProbeContext);
+ }
+
+ ProbeContext.pop_back();
+}
+
+void ProfiledBinary::warnNoFuncEntry() {
+ uint64_t NoFuncEntryNum = 0;
+ for (auto &F : BinaryFunctions) {
+ if (F.second.Ranges.empty())
+ continue;
+ bool hasFuncEntry = false;
+ for (auto &R : F.second.Ranges) {
+ if (FuncRange *FR = findFuncRangeForStartOffset(R.first)) {
+ if (FR->IsFuncEntry) {
+ hasFuncEntry = true;
+ break;
+ }
+ }
+ }
+
+ if (!hasFuncEntry) {
+ NoFuncEntryNum++;
+ if (ShowDetailedWarning)
+ WithColor::warning()
+ << "Failed to determine function entry for " << F.first
+ << " due to inconsistent name from symbol table and dwarf info.\n";
+ }
+ }
+ emitWarningSummary(NoFuncEntryNum, BinaryFunctions.size(),
+ "of functions failed to determine function entry due to "
+ "inconsistent name from symbol table and dwarf info.");
+}
+
+void ProfiledBinary::load() {
+ // Attempt to open the binary.
+ OwningBinary<Binary> OBinary = unwrapOrError(createBinary(Path), Path);
+ Binary &ExeBinary = *OBinary.getBinary();
+
+ auto *Obj = dyn_cast<ELFObjectFileBase>(&ExeBinary);
+ if (!Obj)
+ exitWithError("not a valid Elf image", Path);
+
+ TheTriple = Obj->makeTriple();
+ // Current only support X86
+ if (!TheTriple.isX86())
+ exitWithError("unsupported target", TheTriple.getTriple());
+ LLVM_DEBUG(dbgs() << "Loading " << Path << "\n");
+
+ // Find the preferred load address for text sections.
+ setPreferredTextSegmentAddresses(Obj);
+
+ // Decode pseudo probe related section
+ decodePseudoProbe(Obj);
+
+ // Load debug info of subprograms from DWARF section.
+ // If path of debug info binary is specified, use the debug info from it,
+ // otherwise use the debug info from the executable binary.
+ if (!DebugBinaryPath.empty()) {
+ OwningBinary<Binary> DebugPath =
+ unwrapOrError(createBinary(DebugBinaryPath), DebugBinaryPath);
+ loadSymbolsFromDWARF(*dyn_cast<ObjectFile>(DebugPath.getBinary()));
+ } else {
+ loadSymbolsFromDWARF(*dyn_cast<ObjectFile>(&ExeBinary));
+ }
+
+ // Disassemble the text sections.
+ disassemble(Obj);
+
+ // Track size for optimized inlinees when probe is available
+ if (UsePseudoProbes && TrackFuncContextSize)
+ FuncSizeTracker.trackInlineesOptimizedAway(ProbeDecoder);
+
+ // Use function start and return address to infer prolog and epilog
+ ProEpilogTracker.inferPrologOffsets(StartOffset2FuncRangeMap);
+ ProEpilogTracker.inferEpilogOffsets(RetOffsets);
+
+ warnNoFuncEntry();
+
+ // TODO: decode other sections.
+}
+
+bool ProfiledBinary::inlineContextEqual(uint64_t Address1, uint64_t Address2) {
+ uint64_t Offset1 = virtualAddrToOffset(Address1);
+ uint64_t Offset2 = virtualAddrToOffset(Address2);
+ const SampleContextFrameVector &Context1 = getFrameLocationStack(Offset1);
+ const SampleContextFrameVector &Context2 = getFrameLocationStack(Offset2);
+ if (Context1.size() != Context2.size())
+ return false;
+ if (Context1.empty())
+ return false;
+ // The leaf frame contains location within the leaf, and it
+ // needs to be remove that as it's not part of the calling context
+ return std::equal(Context1.begin(), Context1.begin() + Context1.size() - 1,
+ Context2.begin(), Context2.begin() + Context2.size() - 1);
+}
+
+SampleContextFrameVector
+ProfiledBinary::getExpandedContext(const SmallVectorImpl<uint64_t> &Stack,
+ bool &WasLeafInlined) {
+ SampleContextFrameVector ContextVec;
+ // Process from frame root to leaf
+ for (auto Address : Stack) {
+ uint64_t Offset = virtualAddrToOffset(Address);
+ const SampleContextFrameVector &ExpandedContext =
+ getFrameLocationStack(Offset);
+ // An instruction without a valid debug line will be ignored by sample
+ // processing
+ if (ExpandedContext.empty())
+ return SampleContextFrameVector();
+ // Set WasLeafInlined to the size of inlined frame count for the last
+ // address which is leaf
+ WasLeafInlined = (ExpandedContext.size() > 1);
+ ContextVec.append(ExpandedContext);
+ }
+
+ // Replace with decoded base discriminator
+ for (auto &Frame : ContextVec) {
+ Frame.Location.Discriminator = ProfileGeneratorBase::getBaseDiscriminator(
+ Frame.Location.Discriminator, UseFSDiscriminator);
+ }
+
+ assert(ContextVec.size() && "Context length should be at least 1");
+
+ // Compress the context string except for the leaf frame
+ auto LeafFrame = ContextVec.back();
+ LeafFrame.Location = LineLocation(0, 0);
+ ContextVec.pop_back();
+ CSProfileGenerator::compressRecursionContext(ContextVec);
+ CSProfileGenerator::trimContext(ContextVec);
+ ContextVec.push_back(LeafFrame);
+ return ContextVec;
+}
+
+template <class ELFT>
+void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj, StringRef FileName) {
+ const auto &PhdrRange = unwrapOrError(Obj.program_headers(), FileName);
+ // FIXME: This should be the page size of the system running profiling.
+ // However such info isn't available at post-processing time, assuming
+ // 4K page now. Note that we don't use EXEC_PAGESIZE from <linux/param.h>
+ // because we may build the tools on non-linux.
+ uint32_t PageSize = 0x1000;
+ for (const typename ELFT::Phdr &Phdr : PhdrRange) {
+ if (Phdr.p_type == ELF::PT_LOAD) {
+ if (!FirstLoadableAddress)
+ FirstLoadableAddress = Phdr.p_vaddr & ~(PageSize - 1U);
+ if (Phdr.p_flags & ELF::PF_X) {
+ // Segments will always be loaded at a page boundary.
+ PreferredTextSegmentAddresses.push_back(Phdr.p_vaddr &
+ ~(PageSize - 1U));
+ TextSegmentOffsets.push_back(Phdr.p_offset & ~(PageSize - 1U));
+ }
+ }
+ }
+
+ if (PreferredTextSegmentAddresses.empty())
+ exitWithError("no executable segment found", FileName);
+}
+
+void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFObjectFileBase *Obj) {
+ if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
+ setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
+ setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
+ setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
+ else if (const auto *ELFObj = cast<ELF64BEObjectFile>(Obj))
+ setPreferredTextSegmentAddresses(ELFObj->getELFFile(), Obj->getFileName());
+ else
+ llvm_unreachable("invalid ELF object format");
+}
+
+void ProfiledBinary::decodePseudoProbe(const ELFObjectFileBase *Obj) {
+ if (UseDwarfCorrelation)
+ return;
+
+ StringRef FileName = Obj->getFileName();
+ for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
+ SI != SE; ++SI) {
+ const SectionRef &Section = *SI;
+ StringRef SectionName = unwrapOrError(Section.getName(), FileName);
+
+ if (SectionName == ".pseudo_probe_desc") {
+ StringRef Contents = unwrapOrError(Section.getContents(), FileName);
+ if (!ProbeDecoder.buildGUID2FuncDescMap(
+ reinterpret_cast<const uint8_t *>(Contents.data()),
+ Contents.size()))
+ exitWithError("Pseudo Probe decoder fail in .pseudo_probe_desc section");
+ } else if (SectionName == ".pseudo_probe") {
+ StringRef Contents = unwrapOrError(Section.getContents(), FileName);
+ if (!ProbeDecoder.buildAddress2ProbeMap(
+ reinterpret_cast<const uint8_t *>(Contents.data()),
+ Contents.size()))
+ exitWithError("Pseudo Probe decoder fail in .pseudo_probe section");
+ // set UsePseudoProbes flag, used for PerfReader
+ UsePseudoProbes = true;
+ }
+ }
+
+ if (ShowPseudoProbe)
+ ProbeDecoder.printGUID2FuncDescMap(outs());
+}
+
+void ProfiledBinary::setIsFuncEntry(uint64_t Offset, StringRef RangeSymName) {
+ // Note that the start offset of each ELF section can be a non-function
+ // symbol, we need to binary search for the start of a real function range.
+ auto *FuncRange = findFuncRangeForOffset(Offset);
+ // Skip external function symbol.
+ if (!FuncRange)
+ return;
+
+ // Set IsFuncEntry to ture if there is only one range in the function or the
+ // RangeSymName from ELF is equal to its DWARF-based function name.
+ if (FuncRange->Func->Ranges.size() == 1 ||
+ (!FuncRange->IsFuncEntry && FuncRange->getFuncName() == RangeSymName))
+ FuncRange->IsFuncEntry = true;
+}
+
+bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
+ SectionSymbolsTy &Symbols,
+ const SectionRef &Section) {
+ std::size_t SE = Symbols.size();
+ uint64_t SectionOffset = Section.getAddress() - getPreferredBaseAddress();
+ uint64_t SectSize = Section.getSize();
+ uint64_t StartOffset = Symbols[SI].Addr - getPreferredBaseAddress();
+ uint64_t NextStartOffset =
+ (SI + 1 < SE) ? Symbols[SI + 1].Addr - getPreferredBaseAddress()
+ : SectionOffset + SectSize;
+ setIsFuncEntry(StartOffset,
+ FunctionSamples::getCanonicalFnName(Symbols[SI].Name));
+
+ StringRef SymbolName =
+ ShowCanonicalFnName
+ ? FunctionSamples::getCanonicalFnName(Symbols[SI].Name)
+ : Symbols[SI].Name;
+ bool ShowDisassembly =
+ ShowDisassemblyOnly && (DisassembleFunctionSet.empty() ||
+ DisassembleFunctionSet.count(SymbolName));
+ if (ShowDisassembly)
+ outs() << '<' << SymbolName << ">:\n";
+
+ auto WarnInvalidInsts = [](uint64_t Start, uint64_t End) {
+ WithColor::warning() << "Invalid instructions at "
+ << format("%8" PRIx64, Start) << " - "
+ << format("%8" PRIx64, End) << "\n";
+ };
+
+ uint64_t Offset = StartOffset;
+ // Size of a consecutive invalid instruction range starting from Offset -1
+ // backwards.
+ uint64_t InvalidInstLength = 0;
+ while (Offset < NextStartOffset) {
+ MCInst Inst;
+ uint64_t Size;
+ // Disassemble an instruction.
+ bool Disassembled =
+ DisAsm->getInstruction(Inst, Size, Bytes.slice(Offset - SectionOffset),
+ Offset + getPreferredBaseAddress(), nulls());
+ if (Size == 0)
+ Size = 1;
+
+ if (ShowDisassembly) {
+ if (ShowPseudoProbe) {
+ ProbeDecoder.printProbeForAddress(outs(),
+ Offset + getPreferredBaseAddress());
+ }
+ outs() << format("%8" PRIx64 ":", Offset + getPreferredBaseAddress());
+ size_t Start = outs().tell();
+ if (Disassembled)
+ IPrinter->printInst(&Inst, Offset + Size, "", *STI.get(), outs());
+ else
+ outs() << "\t<unknown>";
+ if (ShowSourceLocations) {
+ unsigned Cur = outs().tell() - Start;
+ if (Cur < 40)
+ outs().indent(40 - Cur);
+ InstructionPointer IP(this, Offset);
+ outs() << getReversedLocWithContext(
+ symbolize(IP, ShowCanonicalFnName, ShowPseudoProbe));
+ }
+ outs() << "\n";
+ }
+
+ if (Disassembled) {
+ const MCInstrDesc &MCDesc = MII->get(Inst.getOpcode());
+
+ // Record instruction size.
+ Offset2InstSizeMap[Offset] = Size;
+
+ // Populate address maps.
+ CodeAddrOffsets.push_back(Offset);
+ if (MCDesc.isCall())
+ CallOffsets.insert(Offset);
+ else if (MCDesc.isReturn())
+ RetOffsets.insert(Offset);
+ else if (MCDesc.isBranch())
+ BranchOffsets.insert(Offset);
+
+ if (InvalidInstLength) {
+ WarnInvalidInsts(Offset - InvalidInstLength, Offset - 1);
+ InvalidInstLength = 0;
+ }
+ } else {
+ InvalidInstLength += Size;
+ }
+
+ Offset += Size;
+ }
+
+ if (InvalidInstLength)
+ WarnInvalidInsts(Offset - InvalidInstLength, Offset - 1);
+
+ if (ShowDisassembly)
+ outs() << "\n";
+
+ return true;
+}
+
+void ProfiledBinary::setUpDisassembler(const ELFObjectFileBase *Obj) {
+ const Target *TheTarget = getTarget(Obj);
+ std::string TripleName = TheTriple.getTriple();
+ StringRef FileName = Obj->getFileName();
+
+ MRI.reset(TheTarget->createMCRegInfo(TripleName));
+ if (!MRI)
+ exitWithError("no register info for target " + TripleName, FileName);
+
+ MCTargetOptions MCOptions;
+ AsmInfo.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ if (!AsmInfo)
+ exitWithError("no assembly info for target " + TripleName, FileName);
+
+ SubtargetFeatures Features = Obj->getFeatures();
+ STI.reset(
+ TheTarget->createMCSubtargetInfo(TripleName, "", Features.getString()));
+ if (!STI)
+ exitWithError("no subtarget info for target " + TripleName, FileName);
+
+ MII.reset(TheTarget->createMCInstrInfo());
+ if (!MII)
+ exitWithError("no instruction info for target " + TripleName, FileName);
+
+ MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get());
+ std::unique_ptr<MCObjectFileInfo> MOFI(
+ TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false));
+ Ctx.setObjectFileInfo(MOFI.get());
+ DisAsm.reset(TheTarget->createMCDisassembler(*STI, Ctx));
+ if (!DisAsm)
+ exitWithError("no disassembler for target " + TripleName, FileName);
+
+ MIA.reset(TheTarget->createMCInstrAnalysis(MII.get()));
+
+ int AsmPrinterVariant = AsmInfo->getAssemblerDialect();
+ IPrinter.reset(TheTarget->createMCInstPrinter(
+ Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI));
+ IPrinter->setPrintBranchImmAsAddress(true);
+}
+
+void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) {
+ // Set up disassembler and related components.
+ setUpDisassembler(Obj);
+
+ // Create a mapping from virtual address to symbol name. The symbols in text
+ // sections are the candidates to dissassemble.
+ std::map<SectionRef, SectionSymbolsTy> AllSymbols;
+ StringRef FileName = Obj->getFileName();
+ for (const SymbolRef &Symbol : Obj->symbols()) {
+ const uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName);
+ const StringRef Name = unwrapOrError(Symbol.getName(), FileName);
+ section_iterator SecI = unwrapOrError(Symbol.getSection(), FileName);
+ if (SecI != Obj->section_end())
+ AllSymbols[*SecI].push_back(SymbolInfoTy(Addr, Name, ELF::STT_NOTYPE));
+ }
+
+ // Sort all the symbols. Use a stable sort to stabilize the output.
+ for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
+ stable_sort(SecSyms.second);
+
+ DisassembleFunctionSet.insert(DisassembleFunctions.begin(),
+ DisassembleFunctions.end());
+ assert((DisassembleFunctionSet.empty() || ShowDisassemblyOnly) &&
+ "Functions to disassemble should be only specified together with "
+ "--show-disassembly-only");
+
+ if (ShowDisassemblyOnly)
+ outs() << "\nDisassembly of " << FileName << ":\n";
+
+ // Dissassemble a text section.
+ for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
+ SI != SE; ++SI) {
+ const SectionRef &Section = *SI;
+ if (!Section.isText())
+ continue;
+
+ uint64_t ImageLoadAddr = getPreferredBaseAddress();
+ uint64_t SectionOffset = Section.getAddress() - ImageLoadAddr;
+ uint64_t SectSize = Section.getSize();
+ if (!SectSize)
+ continue;
+
+ // Register the text section.
+ TextSections.insert({SectionOffset, SectSize});
+
+ StringRef SectionName = unwrapOrError(Section.getName(), FileName);
+
+ if (ShowDisassemblyOnly) {
+ outs() << "\nDisassembly of section " << SectionName;
+ outs() << " [" << format("0x%" PRIx64, Section.getAddress()) << ", "
+ << format("0x%" PRIx64, Section.getAddress() + SectSize)
+ << "]:\n\n";
+ }
+
+ if (SectionName == ".plt")
+ continue;
+
+ // Get the section data.
+ ArrayRef<uint8_t> Bytes =
+ arrayRefFromStringRef(unwrapOrError(Section.getContents(), FileName));
+
+ // Get the list of all the symbols in this section.
+ SectionSymbolsTy &Symbols = AllSymbols[Section];
+
+ // Disassemble symbol by symbol.
+ for (std::size_t SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
+ if (!dissassembleSymbol(SI, Bytes, Symbols, Section))
+ exitWithError("disassembling error", FileName);
+ }
+ }
+
+ // Dissassemble rodata section to check if FS discriminator symbol exists.
+ checkUseFSDiscriminator(Obj, AllSymbols);
+}
+
+void ProfiledBinary::checkUseFSDiscriminator(
+ const ELFObjectFileBase *Obj,
+ std::map<SectionRef, SectionSymbolsTy> &AllSymbols) {
+ const char *FSDiscriminatorVar = "__llvm_fs_discriminator__";
+ for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
+ SI != SE; ++SI) {
+ const SectionRef &Section = *SI;
+ if (!Section.isData() || Section.getSize() == 0)
+ continue;
+ SectionSymbolsTy &Symbols = AllSymbols[Section];
+
+ for (std::size_t SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
+ if (Symbols[SI].Name == FSDiscriminatorVar) {
+ UseFSDiscriminator = true;
+ return;
+ }
+ }
+ }
+}
+
+void ProfiledBinary::loadSymbolsFromDWARF(ObjectFile &Obj) {
+ auto DebugContext = llvm::DWARFContext::create(Obj);
+ if (!DebugContext)
+ exitWithError("Misssing debug info.", Path);
+
+ for (const auto &CompilationUnit : DebugContext->compile_units()) {
+ for (const auto &DieInfo : CompilationUnit->dies()) {
+ llvm::DWARFDie Die(CompilationUnit.get(), &DieInfo);
+
+ if (!Die.isSubprogramDIE())
+ continue;
+ auto Name = Die.getName(llvm::DINameKind::LinkageName);
+ if (!Name)
+ Name = Die.getName(llvm::DINameKind::ShortName);
+ if (!Name)
+ continue;
+
+ auto RangesOrError = Die.getAddressRanges();
+ if (!RangesOrError)
+ continue;
+ const DWARFAddressRangesVector &Ranges = RangesOrError.get();
+
+ if (Ranges.empty())
+ continue;
+
+ // Different DWARF symbols can have same function name, search or create
+ // BinaryFunction indexed by the name.
+ auto Ret = BinaryFunctions.emplace(Name, BinaryFunction());
+ auto &Func = Ret.first->second;
+ if (Ret.second)
+ Func.FuncName = Ret.first->first;
+
+ for (const auto &Range : Ranges) {
+ uint64_t FuncStart = Range.LowPC;
+ uint64_t FuncSize = Range.HighPC - FuncStart;
+
+ if (FuncSize == 0 || FuncStart < getPreferredBaseAddress())
+ continue;
+
+ uint64_t StartOffset = FuncStart - getPreferredBaseAddress();
+ uint64_t EndOffset = Range.HighPC - getPreferredBaseAddress();
+
+ // We may want to know all ranges for one function. Here group the
+ // ranges and store them into BinaryFunction.
+ Func.Ranges.emplace_back(StartOffset, EndOffset);
+
+ auto R = StartOffset2FuncRangeMap.emplace(StartOffset, FuncRange());
+ if (R.second) {
+ FuncRange &FRange = R.first->second;
+ FRange.Func = &Func;
+ FRange.StartOffset = StartOffset;
+ FRange.EndOffset = EndOffset;
+ } else {
+ WithColor::warning()
+ << "Duplicated symbol start address at "
+ << format("%8" PRIx64, StartOffset + getPreferredBaseAddress())
+ << " " << R.first->second.getFuncName() << " and " << Name
+ << "\n";
+ }
+ }
+ }
+ }
+ assert(!StartOffset2FuncRangeMap.empty() && "Misssing debug info.");
+}
+
+void ProfiledBinary::populateSymbolListFromDWARF(
+ ProfileSymbolList &SymbolList) {
+ for (auto &I : StartOffset2FuncRangeMap)
+ SymbolList.add(I.second.getFuncName());
+}
+
+void ProfiledBinary::setupSymbolizer() {
+ symbolize::LLVMSymbolizer::Options SymbolizerOpts;
+ SymbolizerOpts.PrintFunctions =
+ DILineInfoSpecifier::FunctionNameKind::LinkageName;
+ SymbolizerOpts.Demangle = false;
+ SymbolizerOpts.DefaultArch = TheTriple.getArchName().str();
+ SymbolizerOpts.UseSymbolTable = false;
+ SymbolizerOpts.RelativeAddresses = false;
+ Symbolizer = std::make_unique<symbolize::LLVMSymbolizer>(SymbolizerOpts);
+}
+
+SampleContextFrameVector ProfiledBinary::symbolize(const InstructionPointer &IP,
+ bool UseCanonicalFnName,
+ bool UseProbeDiscriminator) {
+ assert(this == IP.Binary &&
+ "Binary should only symbolize its own instruction");
+ auto Addr = object::SectionedAddress{IP.Offset + getPreferredBaseAddress(),
+ object::SectionedAddress::UndefSection};
+ DIInliningInfo InlineStack = unwrapOrError(
+ Symbolizer->symbolizeInlinedCode(SymbolizerPath.str(), Addr),
+ SymbolizerPath);
+
+ SampleContextFrameVector CallStack;
+ for (int32_t I = InlineStack.getNumberOfFrames() - 1; I >= 0; I--) {
+ const auto &CallerFrame = InlineStack.getFrame(I);
+ if (CallerFrame.FunctionName == "<invalid>")
+ break;
+
+ StringRef FunctionName(CallerFrame.FunctionName);
+ if (UseCanonicalFnName)
+ FunctionName = FunctionSamples::getCanonicalFnName(FunctionName);
+
+ uint32_t Discriminator = CallerFrame.Discriminator;
+ uint32_t LineOffset = (CallerFrame.Line - CallerFrame.StartLine) & 0xffff;
+ if (UseProbeDiscriminator) {
+ LineOffset =
+ PseudoProbeDwarfDiscriminator::extractProbeIndex(Discriminator);
+ Discriminator = 0;
+ }
+
+ LineLocation Line(LineOffset, Discriminator);
+ auto It = NameStrings.insert(FunctionName.str());
+ CallStack.emplace_back(*It.first, Line);
+ }
+
+ return CallStack;
+}
+
+void ProfiledBinary::computeInlinedContextSizeForRange(uint64_t StartOffset,
+ uint64_t EndOffset) {
+ uint64_t RangeBegin = offsetToVirtualAddr(StartOffset);
+ uint64_t RangeEnd = offsetToVirtualAddr(EndOffset);
+ InstructionPointer IP(this, RangeBegin, true);
+
+ if (IP.Address != RangeBegin)
+ WithColor::warning() << "Invalid start instruction at "
+ << format("%8" PRIx64, RangeBegin) << "\n";
+
+ if (IP.Address >= RangeEnd)
+ return;
+
+ do {
+ uint64_t Offset = virtualAddrToOffset(IP.Address);
+ const SampleContextFrameVector &SymbolizedCallStack =
+ getFrameLocationStack(Offset, UsePseudoProbes);
+ uint64_t Size = Offset2InstSizeMap[Offset];
+
+ // Record instruction size for the corresponding context
+ FuncSizeTracker.addInstructionForContext(SymbolizedCallStack, Size);
+
+ } while (IP.advance() && IP.Address < RangeEnd);
+}
+
+InstructionPointer::InstructionPointer(const ProfiledBinary *Binary,
+ uint64_t Address, bool RoundToNext)
+ : Binary(Binary), Address(Address) {
+ Index = Binary->getIndexForAddr(Address);
+ if (RoundToNext) {
+ // we might get address which is not the code
+ // it should round to the next valid address
+ if (Index >= Binary->getCodeOffsetsSize())
+ this->Address = UINT64_MAX;
+ else
+ this->Address = Binary->getAddressforIndex(Index);
+ }
+}
+
+bool InstructionPointer::advance() {
+ Index++;
+ if (Index >= Binary->getCodeOffsetsSize()) {
+ Address = UINT64_MAX;
+ return false;
+ }
+ Address = Binary->getAddressforIndex(Index);
+ return true;
+}
+
+bool InstructionPointer::backward() {
+ if (Index == 0) {
+ Address = 0;
+ return false;
+ }
+ Index--;
+ Address = Binary->getAddressforIndex(Index);
+ return true;
+}
+
+void InstructionPointer::update(uint64_t Addr) {
+ Address = Addr;
+ Index = Binary->getIndexForAddr(Address);
+}
+
+} // end namespace sampleprof
+} // end namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/ProfiledBinary.h b/contrib/libs/llvm14/tools/llvm-profgen/ProfiledBinary.h
new file mode 100644
index 00000000000..d3d1c6f1fd2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/ProfiledBinary.h
@@ -0,0 +1,541 @@
+//===-- ProfiledBinary.h - Binary decoder -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_PROFGEN_PROFILEDBINARY_H
+#define LLVM_TOOLS_LLVM_PROFGEN_PROFILEDBINARY_H
+
+#include "CallContext.h"
+#include "ErrorHandling.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrAnalysis.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCPseudoProbe.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/ProfileData/SampleProf.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Transforms/IPO/SampleContextTracker.h"
+#include <list>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+extern cl::opt<bool> EnableCSPreInliner;
+extern cl::opt<bool> UseContextCostForPreInliner;
+
+using namespace llvm;
+using namespace sampleprof;
+using namespace llvm::object;
+
+namespace llvm {
+namespace sampleprof {
+
+class ProfiledBinary;
+
+struct InstructionPointer {
+ const ProfiledBinary *Binary;
+ union {
+ // Offset of the executable segment of the binary.
+ uint64_t Offset = 0;
+ // Also used as address in unwinder
+ uint64_t Address;
+ };
+ // Index to the sorted code address array of the binary.
+ uint64_t Index = 0;
+ InstructionPointer(const ProfiledBinary *Binary, uint64_t Address,
+ bool RoundToNext = false);
+ bool advance();
+ bool backward();
+ void update(uint64_t Addr);
+};
+
+// The special frame addresses.
+enum SpecialFrameAddr {
+ // Dummy root of frame trie.
+ DummyRoot = 0,
+ // Represent all the addresses outside of current binary.
+ // This's also used to indicate the call stack should be truncated since this
+ // isn't a real call context the compiler will see.
+ ExternalAddr = 1,
+};
+
+using RangesTy = std::vector<std::pair<uint64_t, uint64_t>>;
+
+struct BinaryFunction {
+ StringRef FuncName;
+ // End of range is an exclusive bound.
+ RangesTy Ranges;
+
+ uint64_t getFuncSize() {
+ uint64_t Sum = 0;
+ for (auto &R : Ranges) {
+ Sum += R.second - R.first;
+ }
+ return Sum;
+ }
+};
+
+// Info about function range. A function can be split into multiple
+// non-continuous ranges, each range corresponds to one FuncRange.
+struct FuncRange {
+ uint64_t StartOffset;
+ // EndOffset is an exclusive bound.
+ uint64_t EndOffset;
+ // Function the range belongs to
+ BinaryFunction *Func;
+ // Whether the start offset is the real entry of the function.
+ bool IsFuncEntry = false;
+
+ StringRef getFuncName() { return Func->FuncName; }
+};
+
+// PrologEpilog offset tracker, used to filter out broken stack samples
+// Currently we use a heuristic size (two) to infer prolog and epilog
+// based on the start address and return address. In the future,
+// we will switch to Dwarf CFI based tracker
+struct PrologEpilogTracker {
+ // A set of prolog and epilog offsets. Used by virtual unwinding.
+ std::unordered_set<uint64_t> PrologEpilogSet;
+ ProfiledBinary *Binary;
+ PrologEpilogTracker(ProfiledBinary *Bin) : Binary(Bin){};
+
+ // Take the two addresses from the start of function as prolog
+ void inferPrologOffsets(std::map<uint64_t, FuncRange> &FuncStartOffsetMap) {
+ for (auto I : FuncStartOffsetMap) {
+ PrologEpilogSet.insert(I.first);
+ InstructionPointer IP(Binary, I.first);
+ if (!IP.advance())
+ break;
+ PrologEpilogSet.insert(IP.Offset);
+ }
+ }
+
+ // Take the last two addresses before the return address as epilog
+ void inferEpilogOffsets(std::unordered_set<uint64_t> &RetAddrs) {
+ for (auto Addr : RetAddrs) {
+ PrologEpilogSet.insert(Addr);
+ InstructionPointer IP(Binary, Addr);
+ if (!IP.backward())
+ break;
+ PrologEpilogSet.insert(IP.Offset);
+ }
+ }
+};
+
+// Track function byte size under different context (outlined version as well as
+// various inlined versions). It also provides query support to get function
+// size with the best matching context, which is used to help pre-inliner use
+// accurate post-optimization size to make decisions.
+// TODO: If an inlinee is completely optimized away, ideally we should have zero
+// for its context size, currently we would misss such context since it doesn't
+// have instructions. To fix this, we need to mark all inlinee with entry probe
+// but without instructions as having zero size.
+class BinarySizeContextTracker {
+public:
+ // Add instruction with given size to a context
+ void addInstructionForContext(const SampleContextFrameVector &Context,
+ uint32_t InstrSize);
+
+ // Get function size with a specific context. When there's no exact match
+ // for the given context, try to retrieve the size of that function from
+ // closest matching context.
+ uint32_t getFuncSizeForContext(const SampleContext &Context);
+
+ // For inlinees that are full optimized away, we can establish zero size using
+ // their remaining probes.
+ void trackInlineesOptimizedAway(MCPseudoProbeDecoder &ProbeDecoder);
+
+ void dump() { RootContext.dumpTree(); }
+
+private:
+ using ProbeFrameStack = SmallVector<std::pair<StringRef, uint32_t>>;
+ void trackInlineesOptimizedAway(MCPseudoProbeDecoder &ProbeDecoder,
+ MCDecodedPseudoProbeInlineTree &ProbeNode,
+ ProbeFrameStack &Context);
+
+ // Root node for context trie tree, node that this is a reverse context trie
+ // with callee as parent and caller as child. This way we can traverse from
+ // root to find the best/longest matching context if an exact match does not
+ // exist. It gives us the best possible estimate for function's post-inline,
+ // post-optimization byte size.
+ ContextTrieNode RootContext;
+};
+
+using OffsetRange = std::pair<uint64_t, uint64_t>;
+
+class ProfiledBinary {
+ // Absolute path of the executable binary.
+ std::string Path;
+ // Path of the debug info binary.
+ std::string DebugBinaryPath;
+ // Path of symbolizer path which should be pointed to binary with debug info.
+ StringRef SymbolizerPath;
+ // The target triple.
+ Triple TheTriple;
+ // The runtime base address that the first executable segment is loaded at.
+ uint64_t BaseAddress = 0;
+ // The runtime base address that the first loadabe segment is loaded at.
+ uint64_t FirstLoadableAddress = 0;
+ // The preferred load address of each executable segment.
+ std::vector<uint64_t> PreferredTextSegmentAddresses;
+ // The file offset of each executable segment.
+ std::vector<uint64_t> TextSegmentOffsets;
+
+ // Mutiple MC component info
+ std::unique_ptr<const MCRegisterInfo> MRI;
+ std::unique_ptr<const MCAsmInfo> AsmInfo;
+ std::unique_ptr<const MCSubtargetInfo> STI;
+ std::unique_ptr<const MCInstrInfo> MII;
+ std::unique_ptr<MCDisassembler> DisAsm;
+ std::unique_ptr<const MCInstrAnalysis> MIA;
+ std::unique_ptr<MCInstPrinter> IPrinter;
+ // A list of text sections sorted by start RVA and size. Used to check
+ // if a given RVA is a valid code address.
+ std::set<std::pair<uint64_t, uint64_t>> TextSections;
+
+ // A map of mapping function name to BinaryFunction info.
+ std::unordered_map<std::string, BinaryFunction> BinaryFunctions;
+
+ // An ordered map of mapping function's start offset to function range
+ // relevant info. Currently to determine if the offset of ELF is the start of
+ // a real function, we leverage the function range info from DWARF.
+ std::map<uint64_t, FuncRange> StartOffset2FuncRangeMap;
+
+ // Offset to context location map. Used to expand the context.
+ std::unordered_map<uint64_t, SampleContextFrameVector> Offset2LocStackMap;
+
+ // Offset to instruction size map. Also used for quick offset lookup.
+ std::unordered_map<uint64_t, uint64_t> Offset2InstSizeMap;
+
+ // An array of offsets of all instructions sorted in increasing order. The
+ // sorting is needed to fast advance to the next forward/backward instruction.
+ std::vector<uint64_t> CodeAddrOffsets;
+ // A set of call instruction offsets. Used by virtual unwinding.
+ std::unordered_set<uint64_t> CallOffsets;
+ // A set of return instruction offsets. Used by virtual unwinding.
+ std::unordered_set<uint64_t> RetOffsets;
+ // A set of branch instruction offsets.
+ std::unordered_set<uint64_t> BranchOffsets;
+
+ // Estimate and track function prolog and epilog ranges.
+ PrologEpilogTracker ProEpilogTracker;
+
+ // Track function sizes under different context
+ BinarySizeContextTracker FuncSizeTracker;
+
+ // The symbolizer used to get inline context for an instruction.
+ std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer;
+
+ // String table owning function name strings created from the symbolizer.
+ std::unordered_set<std::string> NameStrings;
+
+ // A collection of functions to print disassembly for.
+ StringSet<> DisassembleFunctionSet;
+
+ // Pseudo probe decoder
+ MCPseudoProbeDecoder ProbeDecoder;
+
+ bool UsePseudoProbes = false;
+
+ bool UseFSDiscriminator = false;
+
+ // Whether we need to symbolize all instructions to get function context size.
+ bool TrackFuncContextSize = false;
+
+ // Indicate if the base loading address is parsed from the mmap event or uses
+ // the preferred address
+ bool IsLoadedByMMap = false;
+ // Use to avoid redundant warning.
+ bool MissingMMapWarned = false;
+
+ void setPreferredTextSegmentAddresses(const ELFObjectFileBase *O);
+
+ template <class ELFT>
+ void setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj, StringRef FileName);
+
+ void decodePseudoProbe(const ELFObjectFileBase *Obj);
+
+ void
+ checkUseFSDiscriminator(const ELFObjectFileBase *Obj,
+ std::map<SectionRef, SectionSymbolsTy> &AllSymbols);
+
+ // Set up disassembler and related components.
+ void setUpDisassembler(const ELFObjectFileBase *Obj);
+ void setupSymbolizer();
+
+ // Load debug info of subprograms from DWARF section.
+ void loadSymbolsFromDWARF(ObjectFile &Obj);
+
+ // A function may be spilt into multiple non-continuous address ranges. We use
+ // this to set whether start offset of a function is the real entry of the
+ // function and also set false to the non-function label.
+ void setIsFuncEntry(uint64_t Offset, StringRef RangeSymName);
+
+ // Warn if no entry range exists in the function.
+ void warnNoFuncEntry();
+
+ /// Dissassemble the text section and build various address maps.
+ void disassemble(const ELFObjectFileBase *O);
+
+ /// Helper function to dissassemble the symbol and extract info for unwinding
+ bool dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
+ SectionSymbolsTy &Symbols, const SectionRef &Section);
+ /// Symbolize a given instruction pointer and return a full call context.
+ SampleContextFrameVector symbolize(const InstructionPointer &IP,
+ bool UseCanonicalFnName = false,
+ bool UseProbeDiscriminator = false);
+ /// Decode the interesting parts of the binary and build internal data
+ /// structures. On high level, the parts of interest are:
+ /// 1. Text sections, including the main code section and the PLT
+ /// entries that will be used to handle cross-module call transitions.
+ /// 2. The .debug_line section, used by Dwarf-based profile generation.
+ /// 3. Pseudo probe related sections, used by probe-based profile
+ /// generation.
+ void load();
+
+public:
+ ProfiledBinary(const StringRef ExeBinPath, const StringRef DebugBinPath)
+ : Path(ExeBinPath), DebugBinaryPath(DebugBinPath), ProEpilogTracker(this),
+ TrackFuncContextSize(EnableCSPreInliner &&
+ UseContextCostForPreInliner) {
+ // Point to executable binary if debug info binary is not specified.
+ SymbolizerPath = DebugBinPath.empty() ? ExeBinPath : DebugBinPath;
+ setupSymbolizer();
+ load();
+ }
+ uint64_t virtualAddrToOffset(uint64_t VirtualAddress) const {
+ return VirtualAddress - BaseAddress;
+ }
+ uint64_t offsetToVirtualAddr(uint64_t Offset) const {
+ return Offset + BaseAddress;
+ }
+ StringRef getPath() const { return Path; }
+ StringRef getName() const { return llvm::sys::path::filename(Path); }
+ uint64_t getBaseAddress() const { return BaseAddress; }
+ void setBaseAddress(uint64_t Address) { BaseAddress = Address; }
+
+ // Return the preferred load address for the first executable segment.
+ uint64_t getPreferredBaseAddress() const { return PreferredTextSegmentAddresses[0]; }
+ // Return the preferred load address for the first loadable segment.
+ uint64_t getFirstLoadableAddress() const { return FirstLoadableAddress; }
+ // Return the file offset for the first executable segment.
+ uint64_t getTextSegmentOffset() const { return TextSegmentOffsets[0]; }
+ const std::vector<uint64_t> &getPreferredTextSegmentAddresses() const {
+ return PreferredTextSegmentAddresses;
+ }
+ const std::vector<uint64_t> &getTextSegmentOffsets() const {
+ return TextSegmentOffsets;
+ }
+
+ uint64_t getInstSize(uint64_t Offset) const {
+ auto I = Offset2InstSizeMap.find(Offset);
+ if (I == Offset2InstSizeMap.end())
+ return 0;
+ return I->second;
+ }
+
+ bool offsetIsCode(uint64_t Offset) const {
+ return Offset2InstSizeMap.find(Offset) != Offset2InstSizeMap.end();
+ }
+ bool addressIsCode(uint64_t Address) const {
+ uint64_t Offset = virtualAddrToOffset(Address);
+ return offsetIsCode(Offset);
+ }
+ bool addressIsCall(uint64_t Address) const {
+ uint64_t Offset = virtualAddrToOffset(Address);
+ return CallOffsets.count(Offset);
+ }
+ bool addressIsReturn(uint64_t Address) const {
+ uint64_t Offset = virtualAddrToOffset(Address);
+ return RetOffsets.count(Offset);
+ }
+ bool addressInPrologEpilog(uint64_t Address) const {
+ uint64_t Offset = virtualAddrToOffset(Address);
+ return ProEpilogTracker.PrologEpilogSet.count(Offset);
+ }
+
+ bool offsetIsTransfer(uint64_t Offset) {
+ return BranchOffsets.count(Offset) || RetOffsets.count(Offset) ||
+ CallOffsets.count(Offset);
+ }
+
+ uint64_t getAddressforIndex(uint64_t Index) const {
+ return offsetToVirtualAddr(CodeAddrOffsets[Index]);
+ }
+
+ size_t getCodeOffsetsSize() const { return CodeAddrOffsets.size(); }
+
+ bool usePseudoProbes() const { return UsePseudoProbes; }
+ bool useFSDiscriminator() const { return UseFSDiscriminator; }
+ // Get the index in CodeAddrOffsets for the address
+ // As we might get an address which is not the code
+ // here it would round to the next valid code address by
+ // using lower bound operation
+ uint32_t getIndexForOffset(uint64_t Offset) const {
+ auto Low = llvm::lower_bound(CodeAddrOffsets, Offset);
+ return Low - CodeAddrOffsets.begin();
+ }
+ uint32_t getIndexForAddr(uint64_t Address) const {
+ uint64_t Offset = virtualAddrToOffset(Address);
+ return getIndexForOffset(Offset);
+ }
+
+ uint64_t getCallAddrFromFrameAddr(uint64_t FrameAddr) const {
+ if (FrameAddr == ExternalAddr)
+ return ExternalAddr;
+ auto I = getIndexForAddr(FrameAddr);
+ FrameAddr = I ? getAddressforIndex(I - 1) : 0;
+ if (FrameAddr && addressIsCall(FrameAddr))
+ return FrameAddr;
+ return 0;
+ }
+
+ FuncRange *findFuncRangeForStartOffset(uint64_t Offset) {
+ auto I = StartOffset2FuncRangeMap.find(Offset);
+ if (I == StartOffset2FuncRangeMap.end())
+ return nullptr;
+ return &I->second;
+ }
+
+ // Binary search the function range which includes the input offset.
+ FuncRange *findFuncRangeForOffset(uint64_t Offset) {
+ auto I = StartOffset2FuncRangeMap.upper_bound(Offset);
+ if (I == StartOffset2FuncRangeMap.begin())
+ return nullptr;
+ I--;
+
+ if (Offset >= I->second.EndOffset)
+ return nullptr;
+
+ return &I->second;
+ }
+
+ // Get all ranges of one function.
+ RangesTy getRangesForOffset(uint64_t Offset) {
+ auto *FRange = findFuncRangeForOffset(Offset);
+ // Ignore the range which falls into plt section or system lib.
+ if (!FRange)
+ return RangesTy();
+
+ return FRange->Func->Ranges;
+ }
+
+ const std::unordered_map<std::string, BinaryFunction> &
+ getAllBinaryFunctions() {
+ return BinaryFunctions;
+ }
+
+ BinaryFunction *getBinaryFunction(StringRef FName) {
+ auto I = BinaryFunctions.find(FName.str());
+ if (I == BinaryFunctions.end())
+ return nullptr;
+ return &I->second;
+ }
+
+ uint32_t getFuncSizeForContext(SampleContext &Context) {
+ return FuncSizeTracker.getFuncSizeForContext(Context);
+ }
+
+ // Load the symbols from debug table and populate into symbol list.
+ void populateSymbolListFromDWARF(ProfileSymbolList &SymbolList);
+
+ const SampleContextFrameVector &
+ getFrameLocationStack(uint64_t Offset, bool UseProbeDiscriminator = false) {
+ auto I = Offset2LocStackMap.emplace(Offset, SampleContextFrameVector());
+ if (I.second) {
+ InstructionPointer IP(this, Offset);
+ I.first->second = symbolize(IP, true, UseProbeDiscriminator);
+ }
+ return I.first->second;
+ }
+
+ Optional<SampleContextFrame> getInlineLeafFrameLoc(uint64_t Offset) {
+ const auto &Stack = getFrameLocationStack(Offset);
+ if (Stack.empty())
+ return {};
+ return Stack.back();
+ }
+
+ // Compare two addresses' inline context
+ bool inlineContextEqual(uint64_t Add1, uint64_t Add2);
+
+ // Get the full context of the current stack with inline context filled in.
+ // It will search the disassembling info stored in Offset2LocStackMap. This is
+ // used as the key of function sample map
+ SampleContextFrameVector
+ getExpandedContext(const SmallVectorImpl<uint64_t> &Stack,
+ bool &WasLeafInlined);
+ // Go through instructions among the given range and record its size for the
+ // inline context.
+ void computeInlinedContextSizeForRange(uint64_t StartOffset,
+ uint64_t EndOffset);
+
+ const MCDecodedPseudoProbe *getCallProbeForAddr(uint64_t Address) const {
+ return ProbeDecoder.getCallProbeForAddr(Address);
+ }
+
+ void getInlineContextForProbe(const MCDecodedPseudoProbe *Probe,
+ SampleContextFrameVector &InlineContextStack,
+ bool IncludeLeaf = false) const {
+ SmallVector<MCPseduoProbeFrameLocation, 16> ProbeInlineContext;
+ ProbeDecoder.getInlineContextForProbe(Probe, ProbeInlineContext,
+ IncludeLeaf);
+ for (uint32_t I = 0; I < ProbeInlineContext.size(); I++) {
+ auto &Callsite = ProbeInlineContext[I];
+ // Clear the current context for an unknown probe.
+ if (Callsite.second == 0 && I != ProbeInlineContext.size() - 1) {
+ InlineContextStack.clear();
+ continue;
+ }
+ InlineContextStack.emplace_back(Callsite.first,
+ LineLocation(Callsite.second, 0));
+ }
+ }
+ const AddressProbesMap &getAddress2ProbesMap() const {
+ return ProbeDecoder.getAddress2ProbesMap();
+ }
+ const MCPseudoProbeFuncDesc *getFuncDescForGUID(uint64_t GUID) {
+ return ProbeDecoder.getFuncDescForGUID(GUID);
+ }
+
+ const MCPseudoProbeFuncDesc *
+ getInlinerDescForProbe(const MCDecodedPseudoProbe *Probe) {
+ return ProbeDecoder.getInlinerDescForProbe(Probe);
+ }
+
+ bool getTrackFuncContextSize() { return TrackFuncContextSize; }
+
+ bool getIsLoadedByMMap() { return IsLoadedByMMap; }
+
+ void setIsLoadedByMMap(bool Value) { IsLoadedByMMap = Value; }
+
+ bool getMissingMMapWarned() { return MissingMMapWarned; }
+
+ void setMissingMMapWarned(bool Value) { MissingMMapWarned = Value; }
+};
+
+} // end namespace sampleprof
+} // end namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/llvm-profgen.cpp b/contrib/libs/llvm14/tools/llvm-profgen/llvm-profgen.cpp
new file mode 100644
index 00000000000..f092df04d52
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/llvm-profgen.cpp
@@ -0,0 +1,164 @@
+//===- llvm-profgen.cpp - LLVM SPGO profile generation tool -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// llvm-profgen generates SPGO profiles from perf script ouput.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ErrorHandling.h"
+#include "PerfReader.h"
+#include "ProfileGenerator.h"
+#include "ProfiledBinary.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/TargetSelect.h"
+
+static cl::OptionCategory ProfGenCategory("ProfGen Options");
+
+static cl::opt<std::string> PerfScriptFilename(
+ "perfscript", cl::value_desc("perfscript"), cl::ZeroOrMore,
+ llvm::cl::MiscFlags::CommaSeparated,
+ cl::desc("Path of perf-script trace created by Linux perf tool with "
+ "`script` command(the raw perf.data should be profiled with -b)"),
+ cl::cat(ProfGenCategory));
+static cl::alias PSA("ps", cl::desc("Alias for --perfscript"),
+ cl::aliasopt(PerfScriptFilename));
+
+static cl::opt<std::string> PerfDataFilename(
+ "perfdata", cl::value_desc("perfdata"), cl::ZeroOrMore,
+ llvm::cl::MiscFlags::CommaSeparated,
+ cl::desc("Path of raw perf data created by Linux perf tool (it should be "
+ "profiled with -b)"),
+ cl::cat(ProfGenCategory));
+static cl::alias PDA("pd", cl::desc("Alias for --perfdata"),
+ cl::aliasopt(PerfDataFilename));
+
+static cl::opt<std::string> UnsymbolizedProfFilename(
+ "unsymbolized-profile", cl::value_desc("unsymbolized profile"),
+ cl::ZeroOrMore, llvm::cl::MiscFlags::CommaSeparated,
+ cl::desc("Path of the unsymbolized profile created by "
+ "`llvm-profgen` with `--skip-symbolization`"),
+ cl::cat(ProfGenCategory));
+static cl::alias UPA("up", cl::desc("Alias for --unsymbolized-profile"),
+ cl::aliasopt(UnsymbolizedProfFilename));
+
+static cl::opt<std::string>
+ BinaryPath("binary", cl::value_desc("binary"), cl::Required,
+ cl::desc("Path of profiled executable binary."),
+ cl::cat(ProfGenCategory));
+
+static cl::opt<std::string> DebugBinPath(
+ "debug-binary", cl::value_desc("debug-binary"), cl::ZeroOrMore,
+ cl::desc("Path of debug info binary, llvm-profgen will load the DWARF info "
+ "from it instead of the executable binary."),
+ cl::cat(ProfGenCategory));
+
+extern cl::opt<bool> ShowDisassemblyOnly;
+extern cl::opt<bool> ShowSourceLocations;
+extern cl::opt<bool> SkipSymbolization;
+
+using namespace llvm;
+using namespace sampleprof;
+
+// Validate the command line input.
+static void validateCommandLine() {
+ // Allow the missing perfscript if we only use to show binary disassembly.
+ if (!ShowDisassemblyOnly) {
+ // Validate input profile is provided only once
+ uint16_t HasPerfData = PerfDataFilename.getNumOccurrences();
+ uint16_t HasPerfScript = PerfScriptFilename.getNumOccurrences();
+ uint16_t HasUnsymbolizedProfile =
+ UnsymbolizedProfFilename.getNumOccurrences();
+ uint16_t S = HasPerfData + HasPerfScript + HasUnsymbolizedProfile;
+ if (S != 1) {
+ std::string Msg =
+ S > 1
+ ? "`--perfscript`, `--perfdata` and `--unsymbolized-profile` "
+ "cannot be used together."
+ : "Perf input file is missing, please use one of `--perfscript`, "
+ "`--perfdata` and `--unsymbolized-profile` for the input.";
+ exitWithError(Msg);
+ }
+
+ auto CheckFileExists = [](bool H, StringRef File) {
+ if (H && !llvm::sys::fs::exists(File)) {
+ std::string Msg = "Input perf file(" + File.str() + ") doesn't exist.";
+ exitWithError(Msg);
+ }
+ };
+
+ CheckFileExists(HasPerfData, PerfDataFilename);
+ CheckFileExists(HasPerfScript, PerfScriptFilename);
+ CheckFileExists(HasUnsymbolizedProfile, UnsymbolizedProfFilename);
+ }
+
+ if (!llvm::sys::fs::exists(BinaryPath)) {
+ std::string Msg = "Input binary(" + BinaryPath + ") doesn't exist.";
+ exitWithError(Msg);
+ }
+
+ if (CSProfileGenerator::MaxCompressionSize < -1) {
+ exitWithError("Value of --compress-recursion should >= -1");
+ }
+ if (ShowSourceLocations && !ShowDisassemblyOnly) {
+ exitWithError("--show-source-locations should work together with "
+ "--show-disassembly-only!");
+ }
+}
+
+static PerfInputFile getPerfInputFile() {
+ PerfInputFile File;
+ if (PerfDataFilename.getNumOccurrences()) {
+ File.InputFile = PerfDataFilename;
+ File.Format = PerfFormat::PerfData;
+ } else if (PerfScriptFilename.getNumOccurrences()) {
+ File.InputFile = PerfScriptFilename;
+ File.Format = PerfFormat::PerfScript;
+ } else if (UnsymbolizedProfFilename.getNumOccurrences()) {
+ File.InputFile = UnsymbolizedProfFilename;
+ File.Format = PerfFormat::UnsymbolizedProfile;
+ }
+ return File;
+}
+
+int main(int argc, const char *argv[]) {
+ InitLLVM X(argc, argv);
+
+ // Initialize targets and assembly printers/parsers.
+ InitializeAllTargetInfos();
+ InitializeAllTargetMCs();
+ InitializeAllDisassemblers();
+
+ cl::HideUnrelatedOptions({&ProfGenCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "llvm SPGO profile generator\n");
+ validateCommandLine();
+
+ // Load symbols and disassemble the code of a given binary.
+ std::unique_ptr<ProfiledBinary> Binary =
+ std::make_unique<ProfiledBinary>(BinaryPath, DebugBinPath);
+ if (ShowDisassemblyOnly)
+ return EXIT_SUCCESS;
+
+ PerfInputFile PerfFile = getPerfInputFile();
+ std::unique_ptr<PerfReaderBase> Reader =
+ PerfReaderBase::create(Binary.get(), PerfFile);
+ // Parse perf events and samples
+ Reader->parsePerfTraces();
+
+ if (SkipSymbolization)
+ return EXIT_SUCCESS;
+
+ std::unique_ptr<ProfileGeneratorBase> Generator =
+ ProfileGeneratorBase::create(Binary.get(), Reader->getSampleCounters(),
+ Reader->profileIsCSFlat());
+ Generator->generateProfile();
+ Generator->write();
+
+ return EXIT_SUCCESS;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-profgen/ya.make b/contrib/libs/llvm14/tools/llvm-profgen/ya.make
new file mode 100644
index 00000000000..c6c45c6e36e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-profgen/ya.make
@@ -0,0 +1,80 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/DebugInfo/MSF
+ contrib/libs/llvm14/lib/DebugInfo/PDB
+ contrib/libs/llvm14/lib/DebugInfo/Symbolize
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-profgen
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ CSPreInliner.cpp
+ PerfReader.cpp
+ ProfileGenerator.cpp
+ ProfiledBinary.cpp
+ llvm-profgen.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-rc/Opts.td b/contrib/libs/llvm14/tools/llvm-rc/Opts.td
new file mode 100644
index 00000000000..6d9c0e2601a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/Opts.td
@@ -0,0 +1,65 @@
+include "llvm/Option/OptParser.td"
+
+// All the switches can be preceded by either '/' or '-'.
+// These options seem to be important for the tool
+// and should be implemented.
+
+class S<string name, string help> :
+ Separate<["/", "-"], name>, HelpText<help>;
+
+class JS<string name, string help> :
+ JoinedOrSeparate<["/", "-"], name>, HelpText<help>;
+
+class F<string name, string help> : Flag<["/", "-"], name>, HelpText<help>;
+
+class F_nodoc<string name> : Flag<["/", "-"], name>;
+class S_nodoc<string name> : Separate<["/", "-"], name>;
+
+def fileout : JS<"FO", "Change the output file location.">;
+
+def define : JS<"D", "Define a symbol for the C preprocessor.">;
+def undef : JS<"U", "Undefine a symbol for the C preprocessor.">;
+
+def lang_id : JS<"L", "Set the default language identifier.">;
+def lang_name : S<"LN", "Set the default language name.">;
+
+def includepath : JS<"I", "Add an include path.">;
+def noinclude : F<"X", "Ignore 'include' variable.">;
+
+def add_null : F<"N", "Null-terminate all strings in the string table.">;
+
+def dupid_nowarn : F<"Y", "Suppress warnings on duplicate resource IDs.">;
+
+def verbose : F<"V", "Be verbose.">;
+def help : F<"?", "Display this help and exit.">;
+def h : F<"H", "Display this help and exit.">, Alias<help>;
+
+def codepage : JS<"C", "Set the codepage used for input strings.">;
+
+// llvm-rc specific options:
+
+def dry_run : F<"dry-run", "Don't compile the input; only try to parse it.">;
+
+def no_preprocess : F<"no-preprocess", "Don't try to preprocess the input file.">;
+
+// Print (but do not run) the commands to run for preprocessing
+def _HASH_HASH_HASH : F_nodoc<"###">;
+
+// Unused switches (at least for now). These will stay unimplemented
+// in an early stage of development and can be ignored. However, we need to
+// parse them in order to preserve the compatibility with the original tool.
+
+def nologo : F_nodoc<"NOLOGO">;
+def r : F_nodoc<"R">;
+def sl : F_nodoc<"SL">;
+
+// (Codepages support.)
+def w : F_nodoc<"W">;
+
+// (Support of MUI and similar.)
+def fm : S_nodoc<"FM">;
+def q : S_nodoc<"Q">;
+def g : F_nodoc<"G">;
+def gn : F_nodoc<"GN">;
+def g1 : F_nodoc<"G1">;
+def g2 : F_nodoc<"G2">;
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.cpp b/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.cpp
new file mode 100644
index 00000000000..60287a37f0b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.cpp
@@ -0,0 +1,1567 @@
+//===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This implements the visitor serializing resources to a .res stream.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceFileWriter.h"
+#include "llvm/Object/WindowsResource.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+
+using namespace llvm::support;
+
+// Take an expression returning llvm::Error and forward the error if it exists.
+#define RETURN_IF_ERROR(Expr) \
+ if (auto Err = (Expr)) \
+ return Err;
+
+namespace llvm {
+namespace rc {
+
+// Class that employs RAII to save the current FileWriter object state
+// and revert to it as soon as we leave the scope. This is useful if resources
+// declare their own resource-local statements.
+class ContextKeeper {
+ ResourceFileWriter *FileWriter;
+ ResourceFileWriter::ObjectInfo SavedInfo;
+
+public:
+ ContextKeeper(ResourceFileWriter *V)
+ : FileWriter(V), SavedInfo(V->ObjectData) {}
+ ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
+};
+
+static Error createError(const Twine &Message,
+ std::errc Type = std::errc::invalid_argument) {
+ return make_error<StringError>(Message, std::make_error_code(Type));
+}
+
+static Error checkNumberFits(uint32_t Number, size_t MaxBits,
+ const Twine &FieldName) {
+ assert(1 <= MaxBits && MaxBits <= 32);
+ if (!(Number >> MaxBits))
+ return Error::success();
+ return createError(FieldName + " (" + Twine(Number) + ") does not fit in " +
+ Twine(MaxBits) + " bits.",
+ std::errc::value_too_large);
+}
+
+template <typename FitType>
+static Error checkNumberFits(uint32_t Number, const Twine &FieldName) {
+ return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
+}
+
+// A similar function for signed integers.
+template <typename FitType>
+static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName,
+ bool CanBeNegative) {
+ int32_t SignedNum = Number;
+ if (SignedNum < std::numeric_limits<FitType>::min() ||
+ SignedNum > std::numeric_limits<FitType>::max())
+ return createError(FieldName + " (" + Twine(SignedNum) +
+ ") does not fit in " + Twine(sizeof(FitType) * 8) +
+ "-bit signed integer type.",
+ std::errc::value_too_large);
+
+ if (!CanBeNegative && SignedNum < 0)
+ return createError(FieldName + " (" + Twine(SignedNum) +
+ ") cannot be negative.");
+
+ return Error::success();
+}
+
+static Error checkRCInt(RCInt Number, const Twine &FieldName) {
+ if (Number.isLong())
+ return Error::success();
+ return checkNumberFits<uint16_t>(Number, FieldName);
+}
+
+static Error checkIntOrString(IntOrString Value, const Twine &FieldName) {
+ if (!Value.isInt())
+ return Error::success();
+ return checkNumberFits<uint16_t>(Value.getInt(), FieldName);
+}
+
+static bool stripQuotes(StringRef &Str, bool &IsLongString) {
+ if (!Str.contains('"'))
+ return false;
+
+ // Just take the contents of the string, checking if it's been marked long.
+ IsLongString = Str.startswith_insensitive("L");
+ if (IsLongString)
+ Str = Str.drop_front();
+
+ bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
+ (void)StripSuccess;
+ assert(StripSuccess && "Strings should be enclosed in quotes.");
+ return true;
+}
+
+static UTF16 cp1252ToUnicode(unsigned char C) {
+ static const UTF16 Map80[] = {
+ 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
+ 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
+ 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
+ 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
+ };
+ if (C >= 0x80 && C <= 0x9F)
+ return Map80[C - 0x80];
+ return C;
+}
+
+// Describes a way to handle '\0' characters when processing the string.
+// rc.exe tool sometimes behaves in a weird way in postprocessing.
+// If the string to be output is equivalent to a C-string (e.g. in MENU
+// titles), string is (predictably) truncated after first 0-byte.
+// When outputting a string table, the behavior is equivalent to appending
+// '\0\0' at the end of the string, and then stripping the string
+// before the first '\0\0' occurrence.
+// Finally, when handling strings in user-defined resources, 0-bytes
+// aren't stripped, nor do they terminate the string.
+
+enum class NullHandlingMethod {
+ UserResource, // Don't terminate string on '\0'.
+ CutAtNull, // Terminate string on '\0'.
+ CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
+};
+
+// Parses an identifier or string and returns a processed version of it:
+// * Strip the string boundary quotes.
+// * Convert the input code page characters to UTF16.
+// * Squash "" to a single ".
+// * Replace the escape sequences with their processed version.
+// For identifiers, this is no-op.
+static Error processString(StringRef Str, NullHandlingMethod NullHandler,
+ bool &IsLongString, SmallVectorImpl<UTF16> &Result,
+ int CodePage) {
+ bool IsString = stripQuotes(Str, IsLongString);
+ SmallVector<UTF16, 128> Chars;
+
+ // Convert the input bytes according to the chosen codepage.
+ if (CodePage == CpUtf8) {
+ convertUTF8ToUTF16String(Str, Chars);
+ } else if (CodePage == CpWin1252) {
+ for (char C : Str)
+ Chars.push_back(cp1252ToUnicode((unsigned char)C));
+ } else {
+ // For other, unknown codepages, only allow plain ASCII input.
+ for (char C : Str) {
+ if ((unsigned char)C > 0x7F)
+ return createError("Non-ASCII 8-bit codepoint (" + Twine(C) +
+ ") can't be interpreted in the current codepage");
+ Chars.push_back((unsigned char)C);
+ }
+ }
+
+ if (!IsString) {
+ // It's an identifier if it's not a string. Make all characters uppercase.
+ for (UTF16 &Ch : Chars) {
+ assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII");
+ Ch = toupper(Ch);
+ }
+ Result.swap(Chars);
+ return Error::success();
+ }
+ Result.reserve(Chars.size());
+ size_t Pos = 0;
+
+ auto AddRes = [&Result, NullHandler, IsLongString](UTF16 Char) -> Error {
+ if (!IsLongString) {
+ if (NullHandler == NullHandlingMethod::UserResource) {
+ // Narrow strings in user-defined resources are *not* output in
+ // UTF-16 format.
+ if (Char > 0xFF)
+ return createError("Non-8-bit codepoint (" + Twine(Char) +
+ ") can't occur in a user-defined narrow string");
+ }
+ }
+
+ Result.push_back(Char);
+ return Error::success();
+ };
+ auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error {
+ if (!IsLongString) {
+ // Escaped chars in narrow strings have to be interpreted according to
+ // the chosen code page.
+ if (Char > 0xFF)
+ return createError("Non-8-bit escaped char (" + Twine(Char) +
+ ") can't occur in narrow string");
+ if (CodePage == CpUtf8) {
+ if (Char >= 0x80)
+ return createError("Unable to interpret single byte (" + Twine(Char) +
+ ") as UTF-8");
+ } else if (CodePage == CpWin1252) {
+ Char = cp1252ToUnicode(Char);
+ } else {
+ // Unknown/unsupported codepage, only allow ASCII input.
+ if (Char > 0x7F)
+ return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) +
+ ") can't "
+ "occur in a non-Unicode string");
+ }
+ }
+
+ return AddRes(Char);
+ };
+
+ while (Pos < Chars.size()) {
+ UTF16 CurChar = Chars[Pos];
+ ++Pos;
+
+ // Strip double "".
+ if (CurChar == '"') {
+ if (Pos == Chars.size() || Chars[Pos] != '"')
+ return createError("Expected \"\"");
+ ++Pos;
+ RETURN_IF_ERROR(AddRes('"'));
+ continue;
+ }
+
+ if (CurChar == '\\') {
+ UTF16 TypeChar = Chars[Pos];
+ ++Pos;
+
+ if (TypeChar == 'x' || TypeChar == 'X') {
+ // Read a hex number. Max number of characters to read differs between
+ // narrow and wide strings.
+ UTF16 ReadInt = 0;
+ size_t RemainingChars = IsLongString ? 4 : 2;
+ // We don't want to read non-ASCII hex digits. std:: functions past
+ // 0xFF invoke UB.
+ //
+ // FIXME: actually, Microsoft version probably doesn't check this
+ // condition and uses their Unicode version of 'isxdigit'. However,
+ // there are some hex-digit Unicode character outside of ASCII, and
+ // some of these are actually accepted by rc.exe, the notable example
+ // being fullwidth forms (U+FF10..U+FF19 etc.) These can be written
+ // instead of ASCII digits in \x... escape sequence and get accepted.
+ // However, the resulting hexcodes seem totally unpredictable.
+ // We think it's infeasible to try to reproduce this behavior, nor to
+ // put effort in order to detect it.
+ while (RemainingChars && Pos < Chars.size() && Chars[Pos] < 0x80) {
+ if (!isxdigit(Chars[Pos]))
+ break;
+ char Digit = tolower(Chars[Pos]);
+ ++Pos;
+
+ ReadInt <<= 4;
+ if (isdigit(Digit))
+ ReadInt |= Digit - '0';
+ else
+ ReadInt |= Digit - 'a' + 10;
+
+ --RemainingChars;
+ }
+
+ RETURN_IF_ERROR(AddEscapedChar(ReadInt));
+ continue;
+ }
+
+ if (TypeChar >= '0' && TypeChar < '8') {
+ // Read an octal number. Note that we've already read the first digit.
+ UTF16 ReadInt = TypeChar - '0';
+ size_t RemainingChars = IsLongString ? 6 : 2;
+
+ while (RemainingChars && Pos < Chars.size() && Chars[Pos] >= '0' &&
+ Chars[Pos] < '8') {
+ ReadInt <<= 3;
+ ReadInt |= Chars[Pos] - '0';
+ --RemainingChars;
+ ++Pos;
+ }
+
+ RETURN_IF_ERROR(AddEscapedChar(ReadInt));
+
+ continue;
+ }
+
+ switch (TypeChar) {
+ case 'A':
+ case 'a':
+ // Windows '\a' translates into '\b' (Backspace).
+ RETURN_IF_ERROR(AddRes('\b'));
+ break;
+
+ case 'n': // Somehow, RC doesn't recognize '\N' and '\R'.
+ RETURN_IF_ERROR(AddRes('\n'));
+ break;
+
+ case 'r':
+ RETURN_IF_ERROR(AddRes('\r'));
+ break;
+
+ case 'T':
+ case 't':
+ RETURN_IF_ERROR(AddRes('\t'));
+ break;
+
+ case '\\':
+ RETURN_IF_ERROR(AddRes('\\'));
+ break;
+
+ case '"':
+ // RC accepts \" only if another " comes afterwards; then, \"" means
+ // a single ".
+ if (Pos == Chars.size() || Chars[Pos] != '"')
+ return createError("Expected \\\"\"");
+ ++Pos;
+ RETURN_IF_ERROR(AddRes('"'));
+ break;
+
+ default:
+ // If TypeChar means nothing, \ is should be output to stdout with
+ // following char. However, rc.exe consumes these characters when
+ // dealing with wide strings.
+ if (!IsLongString) {
+ RETURN_IF_ERROR(AddRes('\\'));
+ RETURN_IF_ERROR(AddRes(TypeChar));
+ }
+ break;
+ }
+
+ continue;
+ }
+
+ // If nothing interesting happens, just output the character.
+ RETURN_IF_ERROR(AddRes(CurChar));
+ }
+
+ switch (NullHandler) {
+ case NullHandlingMethod::CutAtNull:
+ for (size_t Pos = 0; Pos < Result.size(); ++Pos)
+ if (Result[Pos] == '\0')
+ Result.resize(Pos);
+ break;
+
+ case NullHandlingMethod::CutAtDoubleNull:
+ for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos)
+ if (Result[Pos] == '\0' && Result[Pos + 1] == '\0')
+ Result.resize(Pos);
+ if (Result.size() > 0 && Result.back() == '\0')
+ Result.pop_back();
+ break;
+
+ case NullHandlingMethod::UserResource:
+ break;
+ }
+
+ return Error::success();
+}
+
+uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) {
+ uint64_t Result = tell();
+ FS->write((const char *)Data.begin(), Data.size());
+ return Result;
+}
+
+Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
+ SmallVector<UTF16, 128> ProcessedString;
+ bool IsLongString;
+ RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,
+ IsLongString, ProcessedString,
+ Params.CodePage));
+ for (auto Ch : ProcessedString)
+ writeInt<uint16_t>(Ch);
+ if (WriteTerminator)
+ writeInt<uint16_t>(0);
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
+ return writeIntOrString(Ident);
+}
+
+Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) {
+ if (!Value.isInt())
+ return writeCString(Value.getString());
+
+ writeInt<uint16_t>(0xFFFF);
+ writeInt<uint16_t>(Value.getInt());
+ return Error::success();
+}
+
+void ResourceFileWriter::writeRCInt(RCInt Value) {
+ if (Value.isLong())
+ writeInt<uint32_t>(Value);
+ else
+ writeInt<uint16_t>(Value);
+}
+
+Error ResourceFileWriter::appendFile(StringRef Filename) {
+ bool IsLong;
+ stripQuotes(Filename, IsLong);
+
+ auto File = loadFile(Filename);
+ if (!File)
+ return File.takeError();
+
+ *FS << (*File)->getBuffer();
+ return Error::success();
+}
+
+void ResourceFileWriter::padStream(uint64_t Length) {
+ assert(Length > 0);
+ uint64_t Location = tell();
+ Location %= Length;
+ uint64_t Pad = (Length - Location) % Length;
+ for (uint64_t i = 0; i < Pad; ++i)
+ writeInt<uint8_t>(0);
+}
+
+Error ResourceFileWriter::handleError(Error Err, const RCResource *Res) {
+ if (Err)
+ return joinErrors(createError("Error in " + Res->getResourceTypeName() +
+ " statement (ID " + Twine(Res->ResName) +
+ "): "),
+ std::move(Err));
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeNullBody);
+}
+
+Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
+}
+
+Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeBitmapBody);
+}
+
+Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
+ return handleError(visitIconOrCursorResource(Res), Res);
+}
+
+Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeDialogBody);
+}
+
+Error ResourceFileWriter::visitIconResource(const RCResource *Res) {
+ return handleError(visitIconOrCursorResource(Res), Res);
+}
+
+Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
+ ObjectData.Caption = Stmt->Value;
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitClassStmt(const ClassStmt *Stmt) {
+ ObjectData.Class = Stmt->Value;
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
+}
+
+Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeMenuBody);
+}
+
+Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
+ const auto *Res = cast<StringTableResource>(Base);
+
+ ContextKeeper RAII(this);
+ RETURN_IF_ERROR(Res->applyStmts(this));
+
+ for (auto &String : Res->Table) {
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID"));
+ uint16_t BundleID = String.first >> 4;
+ StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo);
+ auto &BundleData = StringTableData.BundleData;
+ auto Iter = BundleData.find(Key);
+
+ if (Iter == BundleData.end()) {
+ // Need to create a bundle.
+ StringTableData.BundleList.push_back(Key);
+ auto EmplaceResult = BundleData.emplace(
+ Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags));
+ assert(EmplaceResult.second && "Could not create a bundle");
+ Iter = EmplaceResult.first;
+ }
+
+ RETURN_IF_ERROR(
+ insertStringIntoBundle(Iter->second, String.first, String.second));
+ }
+
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody);
+}
+
+Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
+}
+
+Error ResourceFileWriter::visitCharacteristicsStmt(
+ const CharacteristicsStmt *Stmt) {
+ ObjectData.Characteristics = Stmt->Value;
+ 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"));
+ RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"));
+ ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
+ Stmt->Charset};
+ ObjectData.Font.emplace(Font);
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
+ RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
+ RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
+ ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
+ ObjectData.Style = Stmt->Value;
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
+ ObjectData.VersionInfo = Stmt->Value;
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeResource(
+ const RCResource *Res,
+ Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
+ // We don't know the sizes yet.
+ object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
+ uint64_t HeaderLoc = writeObject(HeaderPrefix);
+
+ auto ResType = Res->getResourceType();
+ RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"));
+ RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"));
+ RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
+ RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
+
+ // Apply the resource-local optional statements.
+ ContextKeeper RAII(this);
+ RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res));
+
+ padStream(sizeof(uint32_t));
+ object::WinResHeaderSuffix HeaderSuffix{
+ ulittle32_t(0), // DataVersion; seems to always be 0
+ ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo),
+ ulittle32_t(ObjectData.VersionInfo),
+ ulittle32_t(ObjectData.Characteristics)};
+ writeObject(HeaderSuffix);
+
+ uint64_t DataLoc = tell();
+ RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res));
+ // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
+
+ // Update the sizes.
+ HeaderPrefix.DataSize = tell() - DataLoc;
+ HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
+ writeObjectAt(HeaderPrefix, HeaderLoc);
+ padStream(sizeof(uint32_t));
+
+ return Error::success();
+}
+
+// --- NullResource helpers. --- //
+
+Error ResourceFileWriter::writeNullBody(const RCResource *) {
+ return Error::success();
+}
+
+// --- AcceleratorsResource helpers. --- //
+
+Error ResourceFileWriter::writeSingleAccelerator(
+ const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
+ using Accelerator = AcceleratorsResource::Accelerator;
+ using Opt = Accelerator::Options;
+
+ struct AccelTableEntry {
+ ulittle16_t Flags;
+ ulittle16_t ANSICode;
+ ulittle16_t Id;
+ uint16_t Padding;
+ } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
+
+ bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
+
+ // Remove ASCII flags (which doesn't occur in .res files).
+ Entry.Flags = Obj.Flags & ~Opt::ASCII;
+
+ if (IsLastItem)
+ Entry.Flags |= 0x80;
+
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"));
+ Entry.Id = ulittle16_t(Obj.Id);
+
+ auto createAccError = [&Obj](const char *Msg) {
+ return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
+ };
+
+ if (IsASCII && IsVirtKey)
+ return createAccError("Accelerator can't be both ASCII and VIRTKEY");
+
+ if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
+ return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
+ " accelerators");
+
+ if (Obj.Event.isInt()) {
+ if (!IsASCII && !IsVirtKey)
+ return createAccError(
+ "Accelerator with a numeric event must be either ASCII"
+ " or VIRTKEY");
+
+ uint32_t EventVal = Obj.Event.getInt();
+ RETURN_IF_ERROR(
+ checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"));
+ Entry.ANSICode = ulittle16_t(EventVal);
+ writeObject(Entry);
+ return Error::success();
+ }
+
+ StringRef Str = Obj.Event.getString();
+ bool IsWide;
+ stripQuotes(Str, IsWide);
+
+ if (Str.size() == 0 || Str.size() > 2)
+ return createAccError(
+ "Accelerator string events should have length 1 or 2");
+
+ if (Str[0] == '^') {
+ if (Str.size() == 1)
+ return createAccError("No character following '^' in accelerator event");
+ if (IsVirtKey)
+ return createAccError(
+ "VIRTKEY accelerator events can't be preceded by '^'");
+
+ char Ch = Str[1];
+ if (Ch >= 'a' && Ch <= 'z')
+ Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
+ else if (Ch >= 'A' && Ch <= 'Z')
+ Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
+ else
+ return createAccError("Control character accelerator event should be"
+ " alphabetic");
+
+ writeObject(Entry);
+ return Error::success();
+ }
+
+ if (Str.size() == 2)
+ return createAccError("Event string should be one-character, possibly"
+ " preceded by '^'");
+
+ uint8_t EventCh = Str[0];
+ // The original tool just warns in this situation. We chose to fail.
+ if (IsVirtKey && !isalnum(EventCh))
+ return createAccError("Non-alphanumeric characters cannot describe virtual"
+ " keys");
+ if (EventCh > 0x7F)
+ return createAccError("Non-ASCII description of accelerator");
+
+ if (IsVirtKey)
+ EventCh = toupper(EventCh);
+ Entry.ANSICode = ulittle16_t(EventCh);
+ writeObject(Entry);
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
+ auto *Res = cast<AcceleratorsResource>(Base);
+ size_t AcceleratorId = 0;
+ for (auto &Acc : Res->Accelerators) {
+ ++AcceleratorId;
+ RETURN_IF_ERROR(
+ writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()));
+ }
+ return Error::success();
+}
+
+// --- BitmapResource helpers. --- //
+
+Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) {
+ StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc;
+ bool IsLong;
+ stripQuotes(Filename, IsLong);
+
+ auto File = loadFile(Filename);
+ if (!File)
+ return File.takeError();
+
+ StringRef Buffer = (*File)->getBuffer();
+
+ // Skip the 14 byte BITMAPFILEHEADER.
+ constexpr size_t BITMAPFILEHEADER_size = 14;
+ if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' ||
+ Buffer[1] != 'M')
+ return createError("Incorrect bitmap file.");
+
+ *FS << Buffer.substr(BITMAPFILEHEADER_size);
+ return Error::success();
+}
+
+// --- CursorResource and IconResource helpers. --- //
+
+// ICONRESDIR structure. Describes a single icon in resource group.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
+struct IconResDir {
+ uint8_t Width;
+ uint8_t Height;
+ uint8_t ColorCount;
+ uint8_t Reserved;
+};
+
+// CURSORDIR structure. Describes a single cursor in resource group.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
+struct CursorDir {
+ ulittle16_t Width;
+ ulittle16_t Height;
+};
+
+// RESDIRENTRY structure, stripped from the last item. Stripping made
+// for compatibility with RESDIR.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
+struct ResourceDirEntryStart {
+ union {
+ CursorDir Cursor; // Used in CURSOR resources.
+ IconResDir Icon; // Used in .ico and .cur files, and ICON resources.
+ };
+ ulittle16_t Planes; // HotspotX (.cur files but not CURSOR resource).
+ ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
+ ulittle32_t Size;
+ // ulittle32_t ImageOffset; // Offset to image data (ICONDIRENTRY only).
+ // ulittle16_t IconID; // Resource icon ID (RESDIR only).
+};
+
+// BITMAPINFOHEADER structure. Describes basic information about the bitmap
+// being read.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
+struct BitmapInfoHeader {
+ ulittle32_t Size;
+ ulittle32_t Width;
+ ulittle32_t Height;
+ ulittle16_t Planes;
+ ulittle16_t BitCount;
+ ulittle32_t Compression;
+ ulittle32_t SizeImage;
+ ulittle32_t XPelsPerMeter;
+ ulittle32_t YPelsPerMeter;
+ ulittle32_t ClrUsed;
+ ulittle32_t ClrImportant;
+};
+
+// Group icon directory header. Called ICONDIR in .ico/.cur files and
+// NEWHEADER in .res files.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
+struct GroupIconDir {
+ ulittle16_t Reserved; // Always 0.
+ ulittle16_t ResType; // 1 for icons, 2 for cursors.
+ ulittle16_t ResCount; // Number of items.
+};
+
+enum class IconCursorGroupType { Icon, Cursor };
+
+class SingleIconCursorResource : public RCResource {
+public:
+ IconCursorGroupType Type;
+ const ResourceDirEntryStart &Header;
+ ArrayRef<uint8_t> Image;
+
+ SingleIconCursorResource(IconCursorGroupType ResourceType,
+ const ResourceDirEntryStart &HeaderEntry,
+ ArrayRef<uint8_t> ImageData, uint16_t Flags)
+ : RCResource(Flags), Type(ResourceType), Header(HeaderEntry),
+ Image(ImageData) {}
+
+ Twine getResourceTypeName() const override { return "Icon/cursor image"; }
+ IntOrString getResourceType() const override {
+ return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
+ }
+ ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkSingleCursorOrIconRes;
+ }
+};
+
+class IconCursorGroupResource : public RCResource {
+public:
+ IconCursorGroupType Type;
+ GroupIconDir Header;
+ std::vector<ResourceDirEntryStart> ItemEntries;
+
+ IconCursorGroupResource(IconCursorGroupType ResourceType,
+ const GroupIconDir &HeaderData,
+ std::vector<ResourceDirEntryStart> &&Entries)
+ : Type(ResourceType), Header(HeaderData),
+ ItemEntries(std::move(Entries)) {}
+
+ Twine getResourceTypeName() const override { return "Icon/cursor group"; }
+ IntOrString getResourceType() const override {
+ return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
+ }
+ ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkCursorOrIconGroupRes;
+ }
+};
+
+Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
+ auto *Res = cast<SingleIconCursorResource>(Base);
+ if (Res->Type == IconCursorGroupType::Cursor) {
+ // In case of cursors, two WORDS are appended to the beginning
+ // of the resource: HotspotX (Planes in RESDIRENTRY),
+ // and HotspotY (BitCount).
+ //
+ // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
+ // (Remarks section).
+ writeObject(Res->Header.Planes);
+ writeObject(Res->Header.BitCount);
+ }
+
+ writeObject(Res->Image);
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
+ auto *Res = cast<IconCursorGroupResource>(Base);
+ writeObject(Res->Header);
+ for (auto Item : Res->ItemEntries) {
+ writeObject(Item);
+ writeInt(IconCursorID++);
+ }
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
+}
+
+Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
+}
+
+Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
+ IconCursorGroupType Type;
+ StringRef FileStr;
+ IntOrString ResName = Base->ResName;
+
+ if (auto *IconRes = dyn_cast<IconResource>(Base)) {
+ FileStr = IconRes->IconLoc;
+ Type = IconCursorGroupType::Icon;
+ } else {
+ auto *CursorRes = dyn_cast<CursorResource>(Base);
+ FileStr = CursorRes->CursorLoc;
+ Type = IconCursorGroupType::Cursor;
+ }
+
+ bool IsLong;
+ stripQuotes(FileStr, IsLong);
+ auto File = loadFile(FileStr);
+
+ if (!File)
+ return File.takeError();
+
+ BinaryStreamReader Reader((*File)->getBuffer(), support::little);
+
+ // Read the file headers.
+ // - At the beginning, ICONDIR/NEWHEADER header.
+ // - Then, a number of RESDIR headers follow. These contain offsets
+ // to data.
+ const GroupIconDir *Header;
+
+ RETURN_IF_ERROR(Reader.readObject(Header));
+ if (Header->Reserved != 0)
+ return createError("Incorrect icon/cursor Reserved field; should be 0.");
+ uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
+ if (Header->ResType != NeededType)
+ return createError("Incorrect icon/cursor ResType field; should be " +
+ Twine(NeededType) + ".");
+
+ uint16_t NumItems = Header->ResCount;
+
+ // Read single ico/cur headers.
+ std::vector<ResourceDirEntryStart> ItemEntries;
+ ItemEntries.reserve(NumItems);
+ std::vector<uint32_t> ItemOffsets(NumItems);
+ for (size_t ID = 0; ID < NumItems; ++ID) {
+ const ResourceDirEntryStart *Object;
+ RETURN_IF_ERROR(Reader.readObject(Object));
+ ItemEntries.push_back(*Object);
+ RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]));
+ }
+
+ // Now write each icon/cursors one by one. At first, all the contents
+ // without ICO/CUR header. This is described by SingleIconCursorResource.
+ for (size_t ID = 0; ID < NumItems; ++ID) {
+ // Load the fragment of file.
+ Reader.setOffset(ItemOffsets[ID]);
+ ArrayRef<uint8_t> Image;
+ RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
+ SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image,
+ Base->MemoryFlags);
+ SingleRes.setName(IconCursorID + ID);
+ RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
+ }
+
+ // Now, write all the headers concatenated into a separate resource.
+ for (size_t ID = 0; ID < NumItems; ++ID) {
+ // We need to rewrite the cursor headers, and fetch actual values
+ // for Planes/BitCount.
+ const auto &OldHeader = ItemEntries[ID];
+ ResourceDirEntryStart NewHeader = OldHeader;
+
+ if (Type == IconCursorGroupType::Cursor) {
+ NewHeader.Cursor.Width = OldHeader.Icon.Width;
+ // Each cursor in fact stores two bitmaps, one under another.
+ // Height provided in cursor definition describes the height of the
+ // cursor, whereas the value existing in resource definition describes
+ // the height of the bitmap. Therefore, we need to double this height.
+ NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
+
+ // Two WORDs were written at the beginning of the resource (hotspot
+ // location). This is reflected in Size field.
+ NewHeader.Size += 2 * sizeof(uint16_t);
+ }
+
+ // Now, we actually need to read the bitmap header to find
+ // the number of planes and the number of bits per pixel.
+ Reader.setOffset(ItemOffsets[ID]);
+ const BitmapInfoHeader *BMPHeader;
+ RETURN_IF_ERROR(Reader.readObject(BMPHeader));
+ if (BMPHeader->Size == sizeof(BitmapInfoHeader)) {
+ NewHeader.Planes = BMPHeader->Planes;
+ NewHeader.BitCount = BMPHeader->BitCount;
+ } else {
+ // A PNG .ico file.
+ // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473
+ // "The image must be in 32bpp"
+ NewHeader.Planes = 1;
+ NewHeader.BitCount = 32;
+ }
+
+ ItemEntries[ID] = NewHeader;
+ }
+
+ IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
+ HeaderRes.setName(ResName);
+ if (Base->MemoryFlags & MfPreload) {
+ HeaderRes.MemoryFlags |= MfPreload;
+ HeaderRes.MemoryFlags &= ~MfPure;
+ }
+ RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
+
+ return Error::success();
+}
+
+// --- DialogResource helpers. --- //
+
+Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
+ bool IsExtended) {
+ // Each control should be aligned to DWORD.
+ padStream(sizeof(uint32_t));
+
+ auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
+ IntWithNotMask CtlStyle(TypeInfo.Style);
+ CtlStyle |= Ctl.Style.getValueOr(RCInt(0));
+ uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
+
+ // DIALOG(EX) item header prefix.
+ if (!IsExtended) {
+ struct {
+ ulittle32_t Style;
+ ulittle32_t ExtStyle;
+ } Prefix{ulittle32_t(CtlStyle.getValue()), ulittle32_t(CtlExtStyle)};
+ writeObject(Prefix);
+ } else {
+ struct {
+ ulittle32_t HelpID;
+ ulittle32_t ExtStyle;
+ ulittle32_t Style;
+ } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
+ ulittle32_t(CtlStyle.getValue())};
+ writeObject(Prefix);
+ }
+
+ // Common fixed-length part.
+ RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
+ Ctl.X, "Dialog control x-coordinate", true));
+ RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
+ Ctl.Y, "Dialog control y-coordinate", true));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
+ RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
+ Ctl.Height, "Dialog control height", false));
+ struct {
+ ulittle16_t X;
+ ulittle16_t Y;
+ ulittle16_t Width;
+ ulittle16_t Height;
+ } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
+ ulittle16_t(Ctl.Height)};
+ writeObject(Middle);
+
+ // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
+ if (!IsExtended) {
+ // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't
+ // want to refer to later.
+ if (Ctl.ID != static_cast<uint32_t>(-1))
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(
+ Ctl.ID, "Control ID in simple DIALOG resource"));
+ writeInt<uint16_t>(Ctl.ID);
+ } else {
+ writeInt<uint32_t>(Ctl.ID);
+ }
+
+ // Window class - either 0xFFFF + 16-bit integer or a string.
+ RETURN_IF_ERROR(writeIntOrString(Ctl.Class));
+
+ // Element caption/reference ID. ID is preceded by 0xFFFF.
+ RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
+ RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
+
+ // # bytes of extra creation data count. Don't pass any.
+ writeInt<uint16_t>(0);
+
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
+ auto *Res = cast<DialogResource>(Base);
+
+ // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
+ const uint32_t DefaultStyle = 0x80880000;
+ const uint32_t StyleFontFlag = 0x40;
+ const uint32_t StyleCaptionFlag = 0x00C00000;
+
+ uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
+ if (ObjectData.Font)
+ UsedStyle |= StyleFontFlag;
+ else
+ UsedStyle &= ~StyleFontFlag;
+
+ // Actually, in case of empty (but existent) caption, the examined field
+ // is equal to "\"\"". That's why empty captions are still noticed.
+ if (ObjectData.Caption != "")
+ 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) {
+ // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
+ // In such a case, whole object (in .res file) is equivalent to a
+ // DIALOGEX. It might lead to access violation/segmentation fault in
+ // resource readers. For example,
+ // 1 DIALOG 0, 0, 0, 65432
+ // STYLE 0xFFFF0001 {}
+ // would be compiled to a DIALOGEX with 65432 controls.
+ if ((UsedStyle >> 16) == DialogExMagic)
+ return createError("16 higher bits of DIALOG resource style cannot be"
+ " equal to 0xFFFF");
+
+ struct {
+ ulittle32_t Style;
+ ulittle32_t ExtStyle;
+ } Prefix{ulittle32_t(UsedStyle),
+ ulittle32_t(ExStyle)};
+
+ writeObject(Prefix);
+ } else {
+ struct {
+ ulittle16_t Version;
+ ulittle16_t Magic;
+ ulittle32_t HelpID;
+ ulittle32_t ExtStyle;
+ ulittle32_t Style;
+ } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
+ ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)};
+
+ writeObject(Prefix);
+ }
+
+ // Now, a common part. First, fixed-length fields.
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
+ "Number of dialog controls"));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
+ struct {
+ ulittle16_t Count;
+ ulittle16_t PosX;
+ ulittle16_t PosY;
+ ulittle16_t DialogWidth;
+ ulittle16_t DialogHeight;
+ } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
+ ulittle16_t(Res->Y), ulittle16_t(Res->Width),
+ ulittle16_t(Res->Height)};
+ writeObject(Middle);
+
+ // MENU field. As of now, we don't keep them in the state and can peacefully
+ // think there is no menu attached to the dialog.
+ writeInt<uint16_t>(0);
+
+ // Window CLASS field.
+ RETURN_IF_ERROR(writeIntOrString(ObjectData.Class));
+
+ // Window title or a single word equal to 0.
+ RETURN_IF_ERROR(writeCString(ObjectData.Caption));
+
+ // If there *is* a window font declared, output its data.
+ auto &Font = ObjectData.Font;
+ if (Font) {
+ writeInt<uint16_t>(Font->Size);
+ // Additional description occurs only in DIALOGEX.
+ if (Res->IsExtended) {
+ writeInt<uint16_t>(Font->Weight);
+ writeInt<uint8_t>(Font->IsItalic);
+ writeInt<uint8_t>(Font->Charset);
+ }
+ RETURN_IF_ERROR(writeCString(Font->Typeface));
+ }
+
+ auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
+ if (!Err)
+ return Error::success();
+ return joinErrors(createError("Error in " + Twine(Ctl.Type) +
+ " control (ID " + Twine(Ctl.ID) + "):"),
+ std::move(Err));
+ };
+
+ for (auto &Ctl : Res->Controls)
+ RETURN_IF_ERROR(
+ handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
+
+ return Error::success();
+}
+
+// --- HTMLResource helpers. --- //
+
+Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
+ return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
+}
+
+// --- MenuResource helpers. --- //
+
+Error ResourceFileWriter::writeMenuDefinition(
+ const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
+ assert(Def);
+ const MenuDefinition *DefPtr = Def.get();
+
+ if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
+ writeInt<uint16_t>(Flags);
+ // Some resource files use -1, i.e. UINT32_MAX, for empty menu items.
+ if (MenuItemPtr->Id != static_cast<uint32_t>(-1))
+ RETURN_IF_ERROR(
+ checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
+ writeInt<uint16_t>(MenuItemPtr->Id);
+ RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
+ return Error::success();
+ }
+
+ if (isa<MenuSeparator>(DefPtr)) {
+ writeInt<uint16_t>(Flags);
+ writeInt<uint32_t>(0);
+ return Error::success();
+ }
+
+ auto *PopupPtr = cast<PopupItem>(DefPtr);
+ writeInt<uint16_t>(Flags);
+ RETURN_IF_ERROR(writeCString(PopupPtr->Name));
+ return writeMenuDefinitionList(PopupPtr->SubItems);
+}
+
+Error ResourceFileWriter::writeMenuDefinitionList(
+ const MenuDefinitionList &List) {
+ for (auto &Def : List.Definitions) {
+ uint16_t Flags = Def->getResFlags();
+ // Last element receives an additional 0x80 flag.
+ const uint16_t LastElementFlag = 0x0080;
+ if (&Def == &List.Definitions.back())
+ Flags |= LastElementFlag;
+
+ RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
+ }
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
+ // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
+ // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
+ writeInt<uint32_t>(0);
+
+ return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
+}
+
+// --- StringTableResource helpers. --- //
+
+class BundleResource : public RCResource {
+public:
+ using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
+ BundleType Bundle;
+
+ BundleResource(const BundleType &StrBundle)
+ : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {}
+ IntOrString getResourceType() const override { return 6; }
+
+ ResourceKind getKind() const override { return RkStringTableBundle; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkStringTableBundle;
+ }
+ Twine getResourceTypeName() const override { return "STRINGTABLE"; }
+};
+
+Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
+}
+
+Error ResourceFileWriter::insertStringIntoBundle(
+ StringTableInfo::Bundle &Bundle, uint16_t StringID,
+ const std::vector<StringRef> &String) {
+ uint16_t StringLoc = StringID & 15;
+ if (Bundle.Data[StringLoc])
+ return createError("Multiple STRINGTABLE strings located under ID " +
+ Twine(StringID));
+ Bundle.Data[StringLoc] = String;
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
+ auto *Res = cast<BundleResource>(Base);
+ for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
+ // The string format is a tiny bit different here. We
+ // first output the size of the string, and then the string itself
+ // (which is not null-terminated).
+ SmallVector<UTF16, 128> Data;
+ if (Res->Bundle.Data[ID]) {
+ bool IsLongString;
+ for (StringRef S : *Res->Bundle.Data[ID])
+ RETURN_IF_ERROR(processString(S, NullHandlingMethod::CutAtDoubleNull,
+ IsLongString, Data, Params.CodePage));
+ if (AppendNull)
+ Data.push_back('\0');
+ }
+ RETURN_IF_ERROR(
+ checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"));
+ writeInt<uint16_t>(Data.size());
+ for (auto Char : Data)
+ writeInt(Char);
+ }
+ return Error::success();
+}
+
+Error ResourceFileWriter::dumpAllStringTables() {
+ for (auto Key : StringTableData.BundleList) {
+ auto Iter = StringTableData.BundleData.find(Key);
+ assert(Iter != StringTableData.BundleData.end());
+
+ // For a moment, revert the context info to moment of bundle declaration.
+ ContextKeeper RAII(this);
+ ObjectData = Iter->second.DeclTimeInfo;
+
+ BundleResource Res(Iter->second);
+ // Bundle #(k+1) contains keys [16k, 16k + 15].
+ Res.setName(Key.first + 1);
+ RETURN_IF_ERROR(visitStringTableBundle(&Res));
+ }
+ return Error::success();
+}
+
+// --- UserDefinedResource helpers. --- //
+
+Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
+ auto *Res = cast<UserDefinedResource>(Base);
+
+ if (Res->IsFileResource)
+ return appendFile(Res->FileLoc);
+
+ for (auto &Elem : Res->Contents) {
+ if (Elem.isInt()) {
+ RETURN_IF_ERROR(
+ checkRCInt(Elem.getInt(), "Number in user-defined resource"));
+ writeRCInt(Elem.getInt());
+ continue;
+ }
+
+ SmallVector<UTF16, 128> ProcessedString;
+ bool IsLongString;
+ RETURN_IF_ERROR(
+ processString(Elem.getString(), NullHandlingMethod::UserResource,
+ IsLongString, ProcessedString, Params.CodePage));
+
+ for (auto Ch : ProcessedString) {
+ if (IsLongString) {
+ writeInt(Ch);
+ continue;
+ }
+
+ RETURN_IF_ERROR(checkNumberFits<uint8_t>(
+ Ch, "Character in narrow string in user-defined resource"));
+ writeInt<uint8_t>(Ch);
+ }
+ }
+
+ return Error::success();
+}
+
+// --- VersionInfoResourceResource helpers. --- //
+
+Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
+ // Output the header if the block has name.
+ bool OutputHeader = Blk.Name != "";
+ uint64_t LengthLoc;
+
+ padStream(sizeof(uint32_t));
+ if (OutputHeader) {
+ LengthLoc = writeInt<uint16_t>(0);
+ writeInt<uint16_t>(0);
+ writeInt<uint16_t>(1); // true
+ RETURN_IF_ERROR(writeCString(Blk.Name));
+ padStream(sizeof(uint32_t));
+ }
+
+ for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
+ VersionInfoStmt *ItemPtr = Item.get();
+
+ if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
+ RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
+ continue;
+ }
+
+ auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
+ RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
+ }
+
+ if (OutputHeader) {
+ uint64_t CurLoc = tell();
+ writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
+ }
+
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
+ // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
+ // is a mapping from the key (string) to the value (a sequence of ints or
+ // a sequence of strings).
+ //
+ // If integers are to be written: width of each integer written depends on
+ // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
+ // ValueLength defined in structure referenced below is then the total
+ // number of bytes taken by these integers.
+ //
+ // If strings are to be written: characters are always WORDs.
+ // Moreover, '\0' character is written after the last string, and between
+ // every two strings separated by comma (if strings are not comma-separated,
+ // they're simply concatenated). ValueLength is equal to the number of WORDs
+ // written (that is, half of the bytes written).
+ //
+ // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
+ bool HasStrings = false, HasInts = false;
+ for (auto &Item : Val.Values)
+ (Item.isInt() ? HasInts : HasStrings) = true;
+
+ assert((HasStrings || HasInts) && "VALUE must have at least one argument");
+ if (HasStrings && HasInts)
+ return createError(Twine("VALUE ") + Val.Key +
+ " cannot contain both strings and integers");
+
+ padStream(sizeof(uint32_t));
+ auto LengthLoc = writeInt<uint16_t>(0);
+ auto ValLengthLoc = writeInt<uint16_t>(0);
+ writeInt<uint16_t>(HasStrings);
+ RETURN_IF_ERROR(writeCString(Val.Key));
+ padStream(sizeof(uint32_t));
+
+ auto DataLoc = tell();
+ for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
+ auto &Item = Val.Values[Id];
+ if (Item.isInt()) {
+ auto Value = Item.getInt();
+ RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
+ writeRCInt(Value);
+ continue;
+ }
+
+ bool WriteTerminator =
+ Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
+ RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
+ }
+
+ auto CurLoc = tell();
+ auto ValueLength = CurLoc - DataLoc;
+ if (HasStrings) {
+ assert(ValueLength % 2 == 0);
+ ValueLength /= 2;
+ }
+ writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
+ writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
+ return Error::success();
+}
+
+template <typename Ty>
+static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
+ const Ty &Default) {
+ auto Iter = Map.find(Key);
+ if (Iter != Map.end())
+ return Iter->getValue();
+ return Default;
+}
+
+Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
+ auto *Res = cast<VersionInfoResource>(Base);
+
+ const auto &FixedData = Res->FixedData;
+
+ struct /* VS_FIXEDFILEINFO */ {
+ ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
+ ulittle32_t StructVersion = ulittle32_t(0x10000);
+ // It's weird to have most-significant DWORD first on the little-endian
+ // machines, but let it be this way.
+ ulittle32_t FileVersionMS;
+ ulittle32_t FileVersionLS;
+ ulittle32_t ProductVersionMS;
+ ulittle32_t ProductVersionLS;
+ ulittle32_t FileFlagsMask;
+ ulittle32_t FileFlags;
+ ulittle32_t FileOS;
+ ulittle32_t FileType;
+ ulittle32_t FileSubtype;
+ // MS implementation seems to always set these fields to 0.
+ ulittle32_t FileDateMS = ulittle32_t(0);
+ ulittle32_t FileDateLS = ulittle32_t(0);
+ } FixedInfo;
+
+ // First, VS_VERSIONINFO.
+ auto LengthLoc = writeInt<uint16_t>(0);
+ writeInt<uint16_t>(sizeof(FixedInfo));
+ writeInt<uint16_t>(0);
+ cantFail(writeCString("VS_VERSION_INFO"));
+ padStream(sizeof(uint32_t));
+
+ using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
+ auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
+ static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
+ if (!FixedData.IsTypePresent[(int)Type])
+ return DefaultOut;
+ return FixedData.FixedInfo[(int)Type];
+ };
+
+ auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(
+ *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"));
+ FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
+ FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
+
+ auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(
+ *std::max_element(ProdVer.begin(), ProdVer.end()),
+ "PRODUCTVERSION fields"));
+ FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
+ FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
+
+ FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
+ FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
+ FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
+ FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
+ FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
+
+ writeObject(FixedInfo);
+ padStream(sizeof(uint32_t));
+
+ RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
+
+ // FIXME: check overflow?
+ writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
+
+ return Error::success();
+}
+
+Expected<std::unique_ptr<MemoryBuffer>>
+ResourceFileWriter::loadFile(StringRef File) const {
+ SmallString<128> Path;
+ SmallString<128> Cwd;
+ std::unique_ptr<MemoryBuffer> Result;
+
+ // 0. The file path is absolute or has a root directory, so we shouldn't
+ // try to append it on top of other base directories. (An absolute path
+ // must have a root directory, but e.g. the path "\dir\file" on windows
+ // isn't considered absolute, but it does have a root directory. As long as
+ // sys::path::append doesn't handle appending an absolute path or a path
+ // starting with a root directory on top of a base, we must handle this
+ // case separately at the top. C++17's path::append handles that case
+ // properly though, so if using that to append paths below, this early
+ // exception case could be removed.)
+ if (sys::path::has_root_directory(File))
+ return errorOrToExpected(MemoryBuffer::getFile(
+ File, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+
+ // 1. The current working directory.
+ sys::fs::current_path(Cwd);
+ Path.assign(Cwd.begin(), Cwd.end());
+ sys::path::append(Path, File);
+ if (sys::fs::exists(Path))
+ return errorOrToExpected(MemoryBuffer::getFile(
+ Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+
+ // 2. The directory of the input resource file, if it is different from the
+ // current working directory.
+ StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
+ Path.assign(InputFileDir.begin(), InputFileDir.end());
+ sys::path::append(Path, File);
+ if (sys::fs::exists(Path))
+ return errorOrToExpected(MemoryBuffer::getFile(
+ Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+
+ // 3. All of the include directories specified on the command line.
+ for (StringRef ForceInclude : Params.Include) {
+ Path.assign(ForceInclude.begin(), ForceInclude.end());
+ sys::path::append(Path, File);
+ if (sys::fs::exists(Path))
+ return errorOrToExpected(MemoryBuffer::getFile(
+ Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+ }
+
+ if (!Params.NoInclude) {
+ if (auto Result = llvm::sys::Process::FindInEnvPath("INCLUDE", File))
+ return errorOrToExpected(MemoryBuffer::getFile(
+ *Result, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+ }
+
+ return make_error<StringError>("error : file not found : " + Twine(File),
+ inconvertibleErrorCode());
+}
+
+} // namespace rc
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.h
new file mode 100644
index 00000000000..0f3d5937259
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.h
@@ -0,0 +1,217 @@
+//===-- ResourceSerializator.h ----------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This defines a visitor serializing resources to a .res stream.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H
+
+#include "ResourceScriptStmt.h"
+#include "ResourceVisitor.h"
+
+#include "llvm/Support/Endian.h"
+
+namespace llvm {
+
+class MemoryBuffer;
+
+namespace rc {
+
+enum CodePage {
+ CpAcp = 0, // The current used codepage. Since there's no such
+ // notion in LLVM what codepage it actually means,
+ // this only allows ASCII.
+ CpWin1252 = 1252, // A codepage where most 8 bit values correspond to
+ // unicode code points with the same value.
+ CpUtf8 = 65001, // UTF-8.
+};
+
+struct WriterParams {
+ std::vector<std::string> Include; // Additional folders to search for files.
+ bool NoInclude; // Ignore the INCLUDE variable.
+ StringRef InputFilePath; // The full path of the input file.
+ int CodePage = CpAcp; // The codepage for interpreting characters.
+};
+
+class ResourceFileWriter : public Visitor {
+public:
+ ResourceFileWriter(const WriterParams &Params,
+ std::unique_ptr<raw_fd_ostream> Stream)
+ : Params(Params), FS(std::move(Stream)), IconCursorID(1) {
+ assert(FS && "Output stream needs to be provided to the serializator");
+ }
+
+ Error visitNullResource(const RCResource *) override;
+ Error visitAcceleratorsResource(const RCResource *) override;
+ Error visitCursorResource(const RCResource *) override;
+ Error visitDialogResource(const RCResource *) override;
+ Error visitHTMLResource(const RCResource *) override;
+ Error visitIconResource(const RCResource *) override;
+ Error visitMenuResource(const RCResource *) override;
+ Error visitVersionInfoResource(const RCResource *) override;
+ Error visitStringTableResource(const RCResource *) override;
+ Error visitUserDefinedResource(const RCResource *) override;
+
+ 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;
+ Error visitVersionStmt(const VersionStmt *) override;
+
+ // Stringtables are output at the end of .res file. We need a separate
+ // function to do it.
+ Error dumpAllStringTables();
+
+ bool AppendNull = false; // Append '\0' to each existing STRINGTABLE element?
+
+ struct ObjectInfo {
+ uint16_t LanguageInfo;
+ uint32_t Characteristics;
+ uint32_t VersionInfo;
+
+ Optional<uint32_t> Style;
+ Optional<uint32_t> ExStyle;
+ StringRef Caption;
+ struct FontInfo {
+ uint32_t Size;
+ StringRef Typeface;
+ uint32_t Weight;
+ bool IsItalic;
+ uint32_t Charset;
+ };
+ Optional<FontInfo> Font;
+ IntOrString Class;
+
+ ObjectInfo()
+ : LanguageInfo(0), Characteristics(0), VersionInfo(0),
+ Class(StringRef()) {}
+ } ObjectData;
+
+ struct StringTableInfo {
+ // Each STRINGTABLE bundle depends on ID of the bundle and language
+ // description.
+ using BundleKey = std::pair<uint16_t, uint16_t>;
+ // Each bundle is in fact an array of 16 strings.
+ struct Bundle {
+ std::array<Optional<std::vector<StringRef>>, 16> Data;
+ ObjectInfo DeclTimeInfo;
+ uint16_t MemoryFlags;
+ Bundle(const ObjectInfo &Info, uint16_t Flags)
+ : DeclTimeInfo(Info), MemoryFlags(Flags) {}
+ };
+ std::map<BundleKey, Bundle> BundleData;
+ // Bundles are listed in the order of their first occurrence.
+ std::vector<BundleKey> BundleList;
+ } StringTableData;
+
+private:
+ Error handleError(Error Err, const RCResource *Res);
+
+ Error
+ writeResource(const RCResource *Res,
+ Error (ResourceFileWriter::*BodyWriter)(const RCResource *));
+
+ // NullResource
+ Error writeNullBody(const RCResource *);
+
+ // AcceleratorsResource
+ Error writeSingleAccelerator(const AcceleratorsResource::Accelerator &,
+ bool IsLastItem);
+ Error writeAcceleratorsBody(const RCResource *);
+
+ // BitmapResource
+ Error visitBitmapResource(const RCResource *) override;
+ Error writeBitmapBody(const RCResource *);
+
+ // CursorResource and IconResource
+ Error visitIconOrCursorResource(const RCResource *);
+ Error visitIconOrCursorGroup(const RCResource *);
+ Error visitSingleIconOrCursor(const RCResource *);
+ Error writeSingleIconOrCursorBody(const RCResource *);
+ Error writeIconOrCursorGroupBody(const RCResource *);
+
+ // DialogResource
+ Error writeSingleDialogControl(const Control &, bool IsExtended);
+ Error writeDialogBody(const RCResource *);
+
+ // HTMLResource
+ Error writeHTMLBody(const RCResource *);
+
+ // MenuResource
+ Error writeMenuDefinition(const std::unique_ptr<MenuDefinition> &,
+ uint16_t Flags);
+ Error writeMenuDefinitionList(const MenuDefinitionList &List);
+ Error writeMenuBody(const RCResource *);
+
+ // StringTableResource
+ Error visitStringTableBundle(const RCResource *);
+ Error writeStringTableBundleBody(const RCResource *);
+ Error insertStringIntoBundle(StringTableInfo::Bundle &Bundle,
+ uint16_t StringID,
+ const std::vector<StringRef> &String);
+
+ // User defined resource
+ Error writeUserDefinedBody(const RCResource *);
+
+ // VersionInfoResource
+ Error writeVersionInfoBody(const RCResource *);
+ Error writeVersionInfoBlock(const VersionInfoBlock &);
+ Error writeVersionInfoValue(const VersionInfoValue &);
+
+ const WriterParams &Params;
+
+ // Output stream handling.
+ std::unique_ptr<raw_fd_ostream> FS;
+
+ uint64_t tell() const { return FS->tell(); }
+
+ uint64_t writeObject(const ArrayRef<uint8_t> Data);
+
+ template <typename T> uint64_t writeInt(const T &Value) {
+ support::detail::packed_endian_specific_integral<T, support::little,
+ support::unaligned>
+ Object(Value);
+ return writeObject(Object);
+ }
+
+ template <typename T> uint64_t writeObject(const T &Value) {
+ return writeObject(ArrayRef<uint8_t>(
+ reinterpret_cast<const uint8_t *>(&Value), sizeof(T)));
+ }
+
+ template <typename T> void writeObjectAt(const T &Value, uint64_t Position) {
+ FS->pwrite((const char *)&Value, sizeof(T), Position);
+ }
+
+ Error writeCString(StringRef Str, bool WriteTerminator = true);
+
+ Error writeIdentifier(const IntOrString &Ident);
+ Error writeIntOrString(const IntOrString &Data);
+
+ void writeRCInt(RCInt);
+
+ Error appendFile(StringRef Filename);
+
+ void padStream(uint64_t Length);
+
+ Expected<std::unique_ptr<MemoryBuffer>> loadFile(StringRef File) const;
+
+ // Icon and cursor IDs are allocated starting from 1 and increasing for
+ // each icon/cursor dumped. This maintains the current ID to be allocated.
+ uint16_t IconCursorID;
+};
+
+} // namespace rc
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.cpp b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.cpp
new file mode 100644
index 00000000000..6657aa54cf6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.cpp
@@ -0,0 +1,111 @@
+//===-- ResourceScriptCppFilter.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This file implements an interface defined in ResourceScriptCppFilter.h.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceScriptCppFilter.h"
+#include "llvm/ADT/StringExtras.h"
+
+#include <vector>
+
+using namespace llvm;
+
+namespace {
+
+class Filter {
+public:
+ explicit Filter(StringRef Input) : Data(Input), DataLength(Input.size()) {}
+
+ std::string run();
+
+private:
+ // Parse the line, returning whether the line should be included in
+ // the output.
+ bool parseLine(StringRef Line);
+
+ bool streamEof() const;
+
+ StringRef Data;
+ size_t DataLength;
+
+ size_t Pos = 0;
+ bool Outputting = true;
+};
+
+std::string Filter::run() {
+ std::vector<StringRef> Output;
+
+ while (!streamEof() && Pos != StringRef::npos) {
+ size_t LineStart = Pos;
+ Pos = Data.find_first_of("\r\n", Pos);
+ Pos = Data.find_first_not_of("\r\n", Pos);
+ StringRef Line = Data.take_front(Pos).drop_front(LineStart);
+
+ if (parseLine(Line))
+ Output.push_back(Line);
+ }
+
+ return llvm::join(Output, "");
+}
+
+bool Filter::parseLine(StringRef Line) {
+ Line = Line.ltrim();
+
+ if (!Line.consume_front("#")) {
+ // A normal content line, filtered according to the current mode.
+ return Outputting;
+ }
+
+ // Found a preprocessing directive line. From here on, we always return
+ // false since the preprocessing directives should be filtered out.
+
+ Line.consume_front("line");
+ if (!Line.startswith(" "))
+ return false; // Not a line directive (pragma etc).
+
+ // #line 123 "path/file.h"
+ // # 123 "path/file.h" 1
+
+ Line =
+ Line.ltrim(); // There could be multiple spaces after the #line directive
+
+ size_t N;
+ if (Line.consumeInteger(10, N)) // Returns true to signify an error
+ return false;
+
+ Line = Line.ltrim();
+
+ if (!Line.consume_front("\""))
+ return false; // Malformed line, no quote found.
+
+ // Split the string at the last quote (in case the path name had
+ // escaped quotes as well).
+ Line = Line.rsplit('"').first;
+
+ StringRef Ext = Line.rsplit('.').second;
+
+ if (Ext.equals_insensitive("h") || Ext.equals_insensitive("c")) {
+ Outputting = false;
+ } else {
+ Outputting = true;
+ }
+
+ return false;
+}
+
+bool Filter::streamEof() const { return Pos == DataLength; }
+
+} // anonymous namespace
+
+namespace llvm {
+
+std::string filterCppOutput(StringRef Input) { return Filter(Input).run(); }
+
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.h
new file mode 100644
index 00000000000..780db317c9f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.h
@@ -0,0 +1,34 @@
+//===-- ResourceScriptCppFilter.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This filters the input to llvm-rc for preprocessor markers, removing
+// preprocessing directives that a preprocessor can output or leave behind.
+//
+// It also filters out any contribution from files named *.h or *.c, based
+// on preprocessor line markers. When preprocessing RC files, the included
+// headers can leave behind C declarations, that RC doesn't understand.
+// Rc.exe simply discards anything that comes from files named *.h or *.h.
+//
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa381033(v=vs.85).aspx
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H
+
+#include "llvm/ADT/StringRef.h"
+
+#include <string>
+
+namespace llvm {
+
+std::string filterCppOutput(StringRef Input);
+
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.cpp b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.cpp
new file mode 100644
index 00000000000..7cb4d02e3c5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.cpp
@@ -0,0 +1,856 @@
+//===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This implements the parser defined in ResourceScriptParser.h.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceScriptParser.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+
+// Take an expression returning llvm::Error and forward the error if it exists.
+#define RETURN_IF_ERROR(Expr) \
+ if (auto Err = (Expr)) \
+ return std::move(Err);
+
+// Take an expression returning llvm::Expected<T> and assign it to Var or
+// forward the error out of the function.
+#define ASSIGN_OR_RETURN(Var, Expr) \
+ auto Var = (Expr); \
+ if (!Var) \
+ return Var.takeError();
+
+namespace llvm {
+namespace rc {
+
+RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
+ const LocIter End)
+ : ErrorLoc(CurLoc), FileEnd(End) {
+ CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
+ (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
+}
+
+char RCParser::ParserError::ID = 0;
+
+RCParser::RCParser(std::vector<RCToken> TokenList)
+ : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
+
+bool RCParser::isEof() const { return CurLoc == End; }
+
+RCParser::ParseType RCParser::parseSingleResource() {
+ // The first thing we read is usually a resource's name. However, in some
+ // cases (LANGUAGE and STRINGTABLE) the resources don't have their names
+ // and the first token to be read is the type.
+ ASSIGN_OR_RETURN(NameToken, readTypeOrName());
+
+ if (NameToken->equalsLower("LANGUAGE"))
+ return parseLanguageResource();
+ else if (NameToken->equalsLower("STRINGTABLE"))
+ return parseStringTableResource();
+
+ // If it's not an unnamed resource, what we've just read is a name. Now,
+ // read resource type;
+ ASSIGN_OR_RETURN(TypeToken, readTypeOrName());
+
+ ParseType Result = std::unique_ptr<RCResource>();
+ (void)!Result;
+
+ if (TypeToken->equalsLower("ACCELERATORS"))
+ Result = parseAcceleratorsResource();
+ else if (TypeToken->equalsLower("BITMAP"))
+ Result = parseBitmapResource();
+ else if (TypeToken->equalsLower("CURSOR"))
+ Result = parseCursorResource();
+ else if (TypeToken->equalsLower("DIALOG"))
+ Result = parseDialogResource(false);
+ else if (TypeToken->equalsLower("DIALOGEX"))
+ Result = parseDialogResource(true);
+ else if (TypeToken->equalsLower("HTML"))
+ Result = parseHTMLResource();
+ else if (TypeToken->equalsLower("ICON"))
+ Result = parseIconResource();
+ else if (TypeToken->equalsLower("MENU"))
+ Result = parseMenuResource();
+ else if (TypeToken->equalsLower("RCDATA"))
+ Result = parseUserDefinedResource(RkRcData);
+ else if (TypeToken->equalsLower("VERSIONINFO"))
+ Result = parseVersionInfoResource();
+ else
+ Result = parseUserDefinedResource(*TypeToken);
+
+ if (Result)
+ (*Result)->setName(*NameToken);
+
+ return Result;
+}
+
+bool RCParser::isNextTokenKind(Kind TokenKind) const {
+ return !isEof() && look().kind() == TokenKind;
+}
+
+const RCToken &RCParser::look() const {
+ assert(!isEof());
+ return *CurLoc;
+}
+
+const RCToken &RCParser::read() {
+ assert(!isEof());
+ return *CurLoc++;
+}
+
+void RCParser::consume() {
+ assert(!isEof());
+ CurLoc++;
+}
+
+// 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> ( ) + - | & ~ 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 (- ~ 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 || not Expr2 || Int || (Exp1).
+// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
+// separated by binary operators.)
+//
+// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
+// is read by parseIntExpr2().
+//
+// The original Microsoft tool handles multiple unary operators incorrectly.
+// For example, in 16-bit little-endian integers:
+// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
+// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
+// Our implementation differs from the original one and handles these
+// operators correctly:
+// 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() {
+ ASSIGN_OR_RETURN(Value, parseIntExpr1());
+ return (*Value).getValue();
+}
+
+Expected<IntWithNotMask> RCParser::parseIntExpr1() {
+ // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
+ ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
+ IntWithNotMask Result = *FirstResult;
+
+ while (!isEof() && look().isBinaryOp()) {
+ auto OpToken = read();
+ ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
+
+ switch (OpToken.kind()) {
+ case Kind::Plus:
+ Result += *NextResult;
+ break;
+
+ case Kind::Minus:
+ Result -= *NextResult;
+ break;
+
+ case Kind::Pipe:
+ Result |= *NextResult;
+ break;
+
+ case Kind::Amp:
+ Result &= *NextResult;
+ break;
+
+ default:
+ llvm_unreachable("Already processed all binary ops.");
+ }
+ }
+
+ return Result;
+}
+
+Expected<IntWithNotMask> RCParser::parseIntExpr2() {
+ // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
+ static const char ErrorMsg[] = "'-', '~', integer or '('";
+
+ if (isEof())
+ return getExpectedError(ErrorMsg);
+
+ switch (look().kind()) {
+ case Kind::Minus: {
+ consume();
+ ASSIGN_OR_RETURN(Result, parseIntExpr2());
+ return -(*Result);
+ }
+
+ case Kind::Tilde: {
+ consume();
+ ASSIGN_OR_RETURN(Result, parseIntExpr2());
+ return ~(*Result);
+ }
+
+ case Kind::Int:
+ return RCInt(read());
+
+ case Kind::LeftParen: {
+ consume();
+ ASSIGN_OR_RETURN(Result, parseIntExpr1());
+ RETURN_IF_ERROR(consumeType(Kind::RightParen));
+ return *Result;
+ }
+
+ case Kind::Identifier: {
+ if (!read().value().equals_insensitive("not"))
+ return getExpectedError(ErrorMsg, true);
+ ASSIGN_OR_RETURN(Result, parseIntExpr2());
+ return IntWithNotMask(0, (*Result).getValue());
+ }
+
+ default:
+ return getExpectedError(ErrorMsg);
+ }
+}
+
+Expected<StringRef> RCParser::readString() {
+ if (!isNextTokenKind(Kind::String))
+ return getExpectedError("string");
+ return read().value();
+}
+
+Expected<StringRef> RCParser::readFilename() {
+ if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
+ return getExpectedError("string");
+ return read().value();
+}
+
+Expected<StringRef> RCParser::readIdentifier() {
+ if (!isNextTokenKind(Kind::Identifier))
+ return getExpectedError("identifier");
+ return read().value();
+}
+
+Expected<IntOrString> RCParser::readIntOrString() {
+ if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
+ return getExpectedError("int or string");
+ return IntOrString(read());
+}
+
+Expected<IntOrString> RCParser::readTypeOrName() {
+ // We suggest that the correct resource name or type should be either an
+ // identifier or an integer. The original RC tool is much more liberal.
+ if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
+ return getExpectedError("int or identifier");
+ return IntOrString(read());
+}
+
+Error RCParser::consumeType(Kind TokenKind) {
+ if (isNextTokenKind(TokenKind)) {
+ consume();
+ return Error::success();
+ }
+
+ switch (TokenKind) {
+#define TOKEN(TokenName) \
+ case Kind::TokenName: \
+ return getExpectedError(#TokenName);
+#define SHORT_TOKEN(TokenName, TokenCh) \
+ case Kind::TokenName: \
+ return getExpectedError(#TokenCh);
+#include "ResourceScriptTokenList.def"
+ }
+
+ llvm_unreachable("All case options exhausted.");
+}
+
+bool RCParser::consumeOptionalType(Kind TokenKind) {
+ if (isNextTokenKind(TokenKind)) {
+ consume();
+ return true;
+ }
+
+ return false;
+}
+
+Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
+ size_t MaxCount) {
+ assert(MinCount <= MaxCount);
+
+ SmallVector<RCInt, 8> Result;
+
+ auto FailureHandler =
+ [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
+ if (Result.size() < MinCount)
+ return std::move(Err);
+ consumeError(std::move(Err));
+ return Result;
+ };
+
+ for (size_t i = 0; i < MaxCount; ++i) {
+ // Try to read a comma unless we read the first token.
+ // Sometimes RC tool requires them and sometimes not. We decide to
+ // always require them.
+ if (i >= 1) {
+ if (auto CommaError = consumeType(Kind::Comma))
+ return FailureHandler(std::move(CommaError));
+ }
+
+ if (auto IntResult = readInt())
+ Result.push_back(*IntResult);
+ else
+ return FailureHandler(IntResult.takeError());
+ }
+
+ return std::move(Result);
+}
+
+Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
+ ArrayRef<uint32_t> FlagValues) {
+ assert(!FlagDesc.empty());
+ assert(FlagDesc.size() == FlagValues.size());
+
+ uint32_t Result = 0;
+ while (isNextTokenKind(Kind::Comma)) {
+ consume();
+ ASSIGN_OR_RETURN(FlagResult, readIdentifier());
+ bool FoundFlag = false;
+
+ for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
+ if (!FlagResult->equals_insensitive(FlagDesc[FlagId]))
+ continue;
+
+ Result |= FlagValues[FlagId];
+ FoundFlag = true;
+ break;
+ }
+
+ if (!FoundFlag)
+ return getExpectedError(join(FlagDesc, "/"), true);
+ }
+
+ return Result;
+}
+
+uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {
+ while (!isEof()) {
+ const RCToken &Token = look();
+ if (Token.kind() != Kind::Identifier)
+ return Flags;
+ const StringRef Ident = Token.value();
+ if (Ident.equals_insensitive("PRELOAD"))
+ Flags |= MfPreload;
+ else if (Ident.equals_insensitive("LOADONCALL"))
+ Flags &= ~MfPreload;
+ else if (Ident.equals_insensitive("FIXED"))
+ Flags &= ~(MfMoveable | MfDiscardable);
+ else if (Ident.equals_insensitive("MOVEABLE"))
+ Flags |= MfMoveable;
+ else if (Ident.equals_insensitive("DISCARDABLE"))
+ Flags |= MfDiscardable | MfMoveable | MfPure;
+ else if (Ident.equals_insensitive("PURE"))
+ Flags |= MfPure;
+ else if (Ident.equals_insensitive("IMPURE"))
+ Flags &= ~(MfPure | MfDiscardable);
+ else if (Ident.equals_insensitive("SHARED"))
+ Flags |= MfPure;
+ else if (Ident.equals_insensitive("NONSHARED"))
+ Flags &= ~(MfPure | MfDiscardable);
+ else
+ return Flags;
+ consume();
+ }
+ return Flags;
+}
+
+Expected<OptionalStmtList>
+RCParser::parseOptionalStatements(OptStmtType StmtsType) {
+ OptionalStmtList Result;
+
+ // The last statement is always followed by the start of the block.
+ while (!isNextTokenKind(Kind::BlockBegin)) {
+ ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
+ Result.addStmt(std::move(*SingleParse));
+ }
+
+ return std::move(Result);
+}
+
+Expected<std::unique_ptr<OptionalStmt>>
+RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
+ ASSIGN_OR_RETURN(TypeToken, readIdentifier());
+ if (TypeToken->equals_insensitive("CHARACTERISTICS"))
+ return parseCharacteristicsStmt();
+ if (TypeToken->equals_insensitive("LANGUAGE"))
+ return parseLanguageStmt();
+ if (TypeToken->equals_insensitive("VERSION"))
+ return parseVersionStmt();
+
+ if (StmtsType != OptStmtType::BasicStmt) {
+ if (TypeToken->equals_insensitive("CAPTION"))
+ return parseCaptionStmt();
+ if (TypeToken->equals_insensitive("CLASS"))
+ return parseClassStmt();
+ if (TypeToken->equals_insensitive("EXSTYLE"))
+ return parseExStyleStmt();
+ if (TypeToken->equals_insensitive("FONT"))
+ return parseFontStmt(StmtsType);
+ if (TypeToken->equals_insensitive("STYLE"))
+ return parseStyleStmt();
+ }
+
+ return getExpectedError("optional statement type, BEGIN or '{'",
+ /* IsAlreadyRead = */ true);
+}
+
+RCParser::ParseType RCParser::parseLanguageResource() {
+ // Read LANGUAGE as an optional statement. If it's read correctly, we can
+ // upcast it to RCResource.
+ return parseLanguageStmt();
+}
+
+RCParser::ParseType RCParser::parseAcceleratorsResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
+ RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+ auto Accels = std::make_unique<AcceleratorsResource>(
+ std::move(*OptStatements), MemoryFlags);
+
+ while (!consumeOptionalType(Kind::BlockEnd)) {
+ ASSIGN_OR_RETURN(EventResult, readIntOrString());
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ ASSIGN_OR_RETURN(IDResult, readInt());
+ ASSIGN_OR_RETURN(
+ FlagsResult,
+ parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
+ AcceleratorsResource::Accelerator::OptionsFlags));
+ Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
+ }
+
+ return std::move(Accels);
+}
+
+RCParser::ParseType RCParser::parseCursorResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(CursorResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(Arg, readFilename());
+ return std::make_unique<CursorResource>(*Arg, MemoryFlags);
+}
+
+RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(DialogResource::getDefaultMemoryFlags());
+ // Dialog resources have the following format of the arguments:
+ // DIALOG: x, y, width, height [opt stmts...] {controls...}
+ // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
+ // These are very similar, so we parse them together.
+ ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
+
+ uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
+ if (IsExtended && consumeOptionalType(Kind::Comma)) {
+ ASSIGN_OR_RETURN(HelpIDResult, readInt());
+ HelpID = *HelpIDResult;
+ }
+
+ ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
+ IsExtended ? OptStmtType::DialogExStmt
+ : OptStmtType::DialogStmt));
+
+ assert(isNextTokenKind(Kind::BlockBegin) &&
+ "parseOptionalStatements, when successful, halts on BlockBegin.");
+ consume();
+
+ auto Dialog = std::make_unique<DialogResource>(
+ (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
+ HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);
+
+ while (!consumeOptionalType(Kind::BlockEnd)) {
+ ASSIGN_OR_RETURN(ControlDefResult, parseControl());
+ Dialog->addControl(std::move(*ControlDefResult));
+ }
+
+ return std::move(Dialog);
+}
+
+RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());
+ if (isEof())
+ return getExpectedError("filename, '{' or BEGIN");
+
+ // Check if this is a file resource.
+ switch (look().kind()) {
+ case Kind::String:
+ case Kind::Identifier:
+ return std::make_unique<UserDefinedResource>(Type, read().value(),
+ MemoryFlags);
+ default:
+ break;
+ }
+
+ RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+ std::vector<IntOrString> Data;
+
+ while (!consumeOptionalType(Kind::BlockEnd)) {
+ ASSIGN_OR_RETURN(Item, readIntOrString());
+ Data.push_back(*Item);
+
+ // There can be zero or more commas after each token (but not before
+ // the first one).
+ while (consumeOptionalType(Kind::Comma)) {
+ }
+ }
+
+ return std::make_unique<UserDefinedResource>(Type, std::move(Data),
+ MemoryFlags);
+}
+
+RCParser::ParseType RCParser::parseVersionInfoResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
+ ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
+ return std::make_unique<VersionInfoResource>(
+ std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
+}
+
+Expected<Control> RCParser::parseControl() {
+ // Each control definition (except CONTROL) follows one of the schemes below
+ // depending on the control class:
+ // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
+ // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
+ // Note that control ids must be integers.
+ // Text might be either a string or an integer pointing to resource ID.
+ ASSIGN_OR_RETURN(ClassResult, readIdentifier());
+ std::string ClassUpper = ClassResult->upper();
+ auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
+ if (CtlInfo == Control::SupportedCtls.end())
+ return getExpectedError("control type, END or '}'", true);
+
+ // Read caption if necessary.
+ IntOrString Caption{StringRef()};
+ if (CtlInfo->getValue().HasTitle) {
+ ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ Caption = *CaptionResult;
+ }
+
+ ASSIGN_OR_RETURN(ID, readInt());
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+
+ IntOrString Class;
+ 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, parseIntExpr1());
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ Style = *StyleVal;
+ } else {
+ Class = CtlInfo->getValue().CtlClass;
+ }
+
+ // x, y, width, height
+ ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
+
+ if (ClassUpper != "CONTROL") {
+ if (consumeOptionalType(Kind::Comma)) {
+ ASSIGN_OR_RETURN(Val, parseIntExpr1());
+ Style = *Val;
+ }
+ }
+
+ Optional<uint32_t> ExStyle;
+ if (consumeOptionalType(Kind::Comma)) {
+ ASSIGN_OR_RETURN(Val, readInt());
+ ExStyle = *Val;
+ }
+ Optional<uint32_t> HelpID;
+ if (consumeOptionalType(Kind::Comma)) {
+ ASSIGN_OR_RETURN(Val, readInt());
+ HelpID = *Val;
+ }
+
+ return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
+ (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
+}
+
+RCParser::ParseType RCParser::parseBitmapResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(Arg, readFilename());
+ return std::make_unique<BitmapResource>(*Arg, MemoryFlags);
+}
+
+RCParser::ParseType RCParser::parseIconResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(IconResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(Arg, readFilename());
+ return std::make_unique<IconResource>(*Arg, MemoryFlags);
+}
+
+RCParser::ParseType RCParser::parseHTMLResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(Arg, readFilename());
+ return std::make_unique<HTMLResource>(*Arg, MemoryFlags);
+}
+
+RCParser::ParseType RCParser::parseMenuResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
+ ASSIGN_OR_RETURN(Items, parseMenuItemsList());
+ return std::make_unique<MenuResource>(std::move(*OptStatements),
+ std::move(*Items), MemoryFlags);
+}
+
+Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
+ RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+ MenuDefinitionList List;
+
+ // Read a set of items. Each item is of one of three kinds:
+ // MENUITEM SEPARATOR
+ // MENUITEM caption:String, result:Int [, menu flags]...
+ // POPUP caption:String [, menu flags]... { items... }
+ while (!consumeOptionalType(Kind::BlockEnd)) {
+ ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
+
+ bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM");
+ bool IsPopup = ItemTypeResult->equals_insensitive("POPUP");
+ if (!IsMenuItem && !IsPopup)
+ return getExpectedError("MENUITEM, POPUP, END or '}'", true);
+
+ if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
+ // Now, expecting SEPARATOR.
+ ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
+ if (SeparatorResult->equals_insensitive("SEPARATOR")) {
+ List.addDefinition(std::make_unique<MenuSeparator>());
+ continue;
+ }
+
+ return getExpectedError("SEPARATOR or string", true);
+ }
+
+ // Not a separator. Read the caption.
+ ASSIGN_OR_RETURN(CaptionResult, readString());
+
+ // If MENUITEM, expect also a comma and an integer.
+ uint32_t MenuResult = -1;
+
+ if (IsMenuItem) {
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ ASSIGN_OR_RETURN(IntResult, readInt());
+ MenuResult = *IntResult;
+ }
+
+ ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
+ MenuDefinition::OptionsFlags));
+
+ if (IsPopup) {
+ // If POPUP, read submenu items recursively.
+ ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
+ List.addDefinition(std::make_unique<PopupItem>(
+ *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
+ continue;
+ }
+
+ assert(IsMenuItem);
+ List.addDefinition(
+ std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
+ }
+
+ return std::move(List);
+}
+
+RCParser::ParseType RCParser::parseStringTableResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
+ RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+ auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements),
+ MemoryFlags);
+
+ // Read strings until we reach the end of the block.
+ while (!consumeOptionalType(Kind::BlockEnd)) {
+ // Each definition consists of string's ID (an integer) and a string.
+ // Some examples in documentation suggest that there might be a comma in
+ // between, however we strictly adhere to the single statement definition.
+ ASSIGN_OR_RETURN(IDResult, readInt());
+ consumeOptionalType(Kind::Comma);
+
+ std::vector<StringRef> Strings;
+ ASSIGN_OR_RETURN(StrResult, readString());
+ Strings.push_back(*StrResult);
+ while (isNextTokenKind(Kind::String))
+ Strings.push_back(read().value());
+
+ Table->addStrings(*IDResult, std::move(Strings));
+ }
+
+ return std::move(Table);
+}
+
+Expected<std::unique_ptr<VersionInfoBlock>>
+RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
+ RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+ auto Contents = std::make_unique<VersionInfoBlock>(BlockName);
+
+ while (!isNextTokenKind(Kind::BlockEnd)) {
+ ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
+ Contents->addStmt(std::move(*Stmt));
+ }
+
+ consume(); // Consume BlockEnd.
+
+ return std::move(Contents);
+}
+
+Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
+ // Expect either BLOCK or VALUE, then a name or a key (a string).
+ ASSIGN_OR_RETURN(TypeResult, readIdentifier());
+
+ if (TypeResult->equals_insensitive("BLOCK")) {
+ ASSIGN_OR_RETURN(NameResult, readString());
+ return parseVersionInfoBlockContents(*NameResult);
+ }
+
+ if (TypeResult->equals_insensitive("VALUE")) {
+ ASSIGN_OR_RETURN(KeyResult, readString());
+ // Read a non-empty list of strings and/or ints, each
+ // possibly preceded by a comma. Unfortunately, the tool behavior depends
+ // on them existing or not, so we need to memorize where we found them.
+ std::vector<IntOrString> Values;
+ BitVector PrecedingCommas;
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ while (!isNextTokenKind(Kind::Identifier) &&
+ !isNextTokenKind(Kind::BlockEnd)) {
+ // Try to eat a comma if it's not the first statement.
+ bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
+ ASSIGN_OR_RETURN(ValueResult, readIntOrString());
+ Values.push_back(*ValueResult);
+ PrecedingCommas.push_back(HadComma);
+ }
+ return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
+ std::move(PrecedingCommas));
+ }
+
+ return getExpectedError("BLOCK or VALUE", true);
+}
+
+Expected<VersionInfoResource::VersionInfoFixed>
+RCParser::parseVersionInfoFixed() {
+ using RetType = VersionInfoResource::VersionInfoFixed;
+ RetType Result;
+
+ // Read until the beginning of the block.
+ while (!isNextTokenKind(Kind::BlockBegin)) {
+ ASSIGN_OR_RETURN(TypeResult, readIdentifier());
+ auto FixedType = RetType::getFixedType(*TypeResult);
+
+ if (!RetType::isTypeSupported(FixedType))
+ return getExpectedError("fixed VERSIONINFO statement type", true);
+ if (Result.IsTypePresent[FixedType])
+ return getExpectedError("yet unread fixed VERSIONINFO statement type",
+ true);
+
+ // VERSION variations take multiple integers.
+ size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
+ ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(1, NumInts));
+ SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
+ while (ArgInts.size() < NumInts)
+ ArgInts.push_back(0);
+ Result.setValue(FixedType, ArgInts);
+ }
+
+ return Result;
+}
+
+RCParser::ParseOptionType RCParser::parseLanguageStmt() {
+ ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
+ return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
+}
+
+RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
+ ASSIGN_OR_RETURN(Arg, readInt());
+ return std::make_unique<CharacteristicsStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseVersionStmt() {
+ ASSIGN_OR_RETURN(Arg, readInt());
+ return std::make_unique<VersionStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseCaptionStmt() {
+ ASSIGN_OR_RETURN(Arg, readString());
+ return std::make_unique<CaptionStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseClassStmt() {
+ ASSIGN_OR_RETURN(Arg, readIntOrString());
+ return std::make_unique<ClassStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
+ assert(DialogType != OptStmtType::BasicStmt);
+
+ ASSIGN_OR_RETURN(SizeResult, readInt());
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ ASSIGN_OR_RETURN(NameResult, readString());
+
+ // Default values for the optional arguments.
+ uint32_t FontWeight = 0;
+ bool FontItalic = false;
+ uint32_t FontCharset = 1;
+ if (DialogType == OptStmtType::DialogExStmt) {
+ if (consumeOptionalType(Kind::Comma)) {
+ ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
+ if (Args->size() >= 1)
+ FontWeight = (*Args)[0];
+ if (Args->size() >= 2)
+ FontItalic = (*Args)[1] != 0;
+ if (Args->size() >= 3)
+ FontCharset = (*Args)[2];
+ }
+ }
+ return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
+ FontItalic, FontCharset);
+}
+
+RCParser::ParseOptionType RCParser::parseStyleStmt() {
+ ASSIGN_OR_RETURN(Arg, readInt());
+ return std::make_unique<StyleStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseExStyleStmt() {
+ ASSIGN_OR_RETURN(Arg, readInt());
+ return std::make_unique<ExStyleStmt>(*Arg);
+}
+
+Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
+ return make_error<ParserError>(
+ Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
+}
+
+} // namespace rc
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.h
new file mode 100644
index 00000000000..a7d3b595f79
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.h
@@ -0,0 +1,189 @@
+//===-- ResourceScriptParser.h ----------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This defines the RC scripts parser. It takes a sequence of RC tokens
+// and then provides the method to parse the resources one by one.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H
+
+#include "ResourceScriptStmt.h"
+#include "ResourceScriptToken.h"
+
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <system_error>
+#include <vector>
+
+namespace llvm {
+namespace rc {
+
+class RCParser {
+public:
+ using LocIter = std::vector<RCToken>::iterator;
+ using ParseType = Expected<std::unique_ptr<RCResource>>;
+ using ParseOptionType = Expected<std::unique_ptr<OptionalStmt>>;
+
+ // Class describing a single failure of parser.
+ class ParserError : public ErrorInfo<ParserError> {
+ public:
+ ParserError(const Twine &Expected, const LocIter CurLoc, const LocIter End);
+
+ void log(raw_ostream &OS) const override { OS << CurMessage; }
+ std::error_code convertToErrorCode() const override {
+ return std::make_error_code(std::errc::invalid_argument);
+ }
+ const std::string &getMessage() const { return CurMessage; }
+
+ static char ID; // Keep llvm::Error happy.
+
+ private:
+ std::string CurMessage;
+ LocIter ErrorLoc, FileEnd;
+ };
+
+ explicit RCParser(std::vector<RCToken> TokenList);
+
+ // Reads and returns a single resource definition, or error message if any
+ // occurred.
+ ParseType parseSingleResource();
+
+ bool isEof() const;
+
+private:
+ using Kind = RCToken::Kind;
+
+ // Checks if the current parser state points to the token of type TokenKind.
+ bool isNextTokenKind(Kind TokenKind) const;
+
+ // These methods assume that the parser is not in EOF state.
+
+ // Take a look at the current token. Do not fetch it.
+ const RCToken &look() const;
+ // Read the current token and advance the state by one token.
+ const RCToken &read();
+ // Advance the state by one token, discarding the current token.
+ void consume();
+
+ // The following methods try to read a single token, check if it has the
+ // correct type and then parse it.
+ // Each integer can be written as an arithmetic expression producing an
+ // unsigned 32-bit integer.
+ Expected<RCInt> readInt(); // Parse an integer.
+ Expected<StringRef> readString(); // Parse a string.
+ Expected<StringRef> readIdentifier(); // Parse an identifier.
+ Expected<StringRef> readFilename(); // Parse a filename.
+ Expected<IntOrString> readIntOrString(); // Parse an integer or a string.
+ Expected<IntOrString> readTypeOrName(); // Parse an integer or an identifier.
+
+ // Helper integer expression parsing methods.
+ Expected<IntWithNotMask> parseIntExpr1();
+ Expected<IntWithNotMask> parseIntExpr2();
+
+ // Advance the state by one, discarding the current token.
+ // If the discarded token had an incorrect type, fail.
+ Error consumeType(Kind TokenKind);
+
+ // Check the current token type. If it's TokenKind, discard it.
+ // Return true if the parser consumed this token successfully.
+ bool consumeOptionalType(Kind TokenKind);
+
+ // Read at least MinCount, and at most MaxCount integers separated by
+ // commas. The parser stops reading after fetching MaxCount integers
+ // or after an error occurs. Whenever the parser reads a comma, it
+ // expects an integer to follow.
+ Expected<SmallVector<RCInt, 8>> readIntsWithCommas(size_t MinCount,
+ size_t MaxCount);
+
+ // Read an unknown number of flags preceded by commas. Each correct flag
+ // has an entry in FlagDesc array of length NumFlags. In case i-th
+ // flag (0-based) has been read, the result is OR-ed with FlagValues[i].
+ // As long as parser has a comma to read, it expects to be fed with
+ // a correct flag afterwards.
+ Expected<uint32_t> parseFlags(ArrayRef<StringRef> FlagDesc,
+ ArrayRef<uint32_t> FlagValues);
+
+ // Reads a set of optional statements. These can change the behavior of
+ // a number of resource types (e.g. STRINGTABLE, MENU or DIALOG) if provided
+ // before the main block with the contents of the resource.
+ // Usually, resources use a basic set of optional statements:
+ // CHARACTERISTICS, LANGUAGE, VERSION
+ // However, DIALOG and DIALOGEX extend this list by the following items:
+ // CAPTION, CLASS, EXSTYLE, FONT, MENU, STYLE
+ // UseExtendedStatements flag (off by default) allows the parser to read
+ // the additional types of statements.
+ //
+ // Ref (to the list of all optional statements):
+ // msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx
+ enum class OptStmtType { BasicStmt, DialogStmt, DialogExStmt };
+
+ uint16_t parseMemoryFlags(uint16_t DefaultFlags);
+
+ Expected<OptionalStmtList>
+ parseOptionalStatements(OptStmtType StmtsType = OptStmtType::BasicStmt);
+
+ // Read a single optional statement.
+ Expected<std::unique_ptr<OptionalStmt>>
+ parseSingleOptionalStatement(OptStmtType StmtsType = OptStmtType::BasicStmt);
+
+ // Top-level resource parsers.
+ ParseType parseLanguageResource();
+ ParseType parseAcceleratorsResource();
+ ParseType parseBitmapResource();
+ ParseType parseCursorResource();
+ ParseType parseDialogResource(bool IsExtended);
+ ParseType parseIconResource();
+ ParseType parseHTMLResource();
+ ParseType parseMenuResource();
+ ParseType parseStringTableResource();
+ ParseType parseUserDefinedResource(IntOrString Type);
+ ParseType parseVersionInfoResource();
+
+ // Helper DIALOG parser - a single control.
+ Expected<Control> parseControl();
+
+ // Helper MENU parser.
+ Expected<MenuDefinitionList> parseMenuItemsList();
+
+ // Helper VERSIONINFO parser - read the contents of a single BLOCK statement,
+ // from BEGIN to END.
+ Expected<std::unique_ptr<VersionInfoBlock>>
+ parseVersionInfoBlockContents(StringRef BlockName);
+ // Helper VERSIONINFO parser - read either VALUE or BLOCK statement.
+ Expected<std::unique_ptr<VersionInfoStmt>> parseVersionInfoStmt();
+ // Helper VERSIONINFO parser - read fixed VERSIONINFO statements.
+ Expected<VersionInfoResource::VersionInfoFixed> parseVersionInfoFixed();
+
+ // Optional statement parsers.
+ ParseOptionType parseLanguageStmt();
+ ParseOptionType parseCharacteristicsStmt();
+ ParseOptionType parseVersionStmt();
+ ParseOptionType parseCaptionStmt();
+ ParseOptionType parseClassStmt();
+ ParseOptionType parseExStyleStmt();
+ ParseOptionType parseFontStmt(OptStmtType DialogType);
+ ParseOptionType parseStyleStmt();
+
+ // Raises an error. If IsAlreadyRead = false (default), this complains about
+ // the token that couldn't be parsed. If the flag is on, this complains about
+ // the correctly read token that makes no sense (that is, the current parser
+ // state is beyond the erroneous token.)
+ Error getExpectedError(const Twine &Message, bool IsAlreadyRead = false);
+
+ std::vector<RCToken> Tokens;
+ LocIter CurLoc;
+ const LocIter End;
+};
+
+} // namespace rc
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.cpp b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.cpp
new file mode 100644
index 00000000000..ef8c3454188
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.cpp
@@ -0,0 +1,294 @@
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This implements methods defined in ResourceScriptStmt.h.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceScriptStmt.h"
+
+namespace llvm {
+namespace rc {
+
+raw_ostream &operator<<(raw_ostream &OS, const IntOrString &Item) {
+ if (Item.IsInt)
+ return OS << Item.Data.Int;
+ else
+ return OS << Item.Data.String;
+}
+
+raw_ostream &OptionalStmtList::log(raw_ostream &OS) const {
+ for (const auto &Stmt : Statements) {
+ OS << " Option: ";
+ Stmt->log(OS);
+ }
+ return OS;
+}
+
+raw_ostream &LanguageResource::log(raw_ostream &OS) const {
+ return OS << "Language: " << Lang << ", Sublanguage: " << SubLang << "\n";
+}
+
+StringRef AcceleratorsResource::Accelerator::OptionsStr
+ [AcceleratorsResource::Accelerator::NumFlags] = {
+ "ASCII", "VIRTKEY", "NOINVERT", "ALT", "SHIFT", "CONTROL"};
+
+uint32_t AcceleratorsResource::Accelerator::OptionsFlags
+ [AcceleratorsResource::Accelerator::NumFlags] = {ASCII, VIRTKEY, NOINVERT,
+ ALT, SHIFT, CONTROL};
+
+raw_ostream &AcceleratorsResource::log(raw_ostream &OS) const {
+ OS << "Accelerators (" << ResName << "): \n";
+ OptStatements->log(OS);
+ for (const auto &Acc : Accelerators) {
+ OS << " Accelerator: " << Acc.Event << " " << Acc.Id;
+ for (size_t i = 0; i < Accelerator::NumFlags; ++i)
+ if (Acc.Flags & Accelerator::OptionsFlags[i])
+ OS << " " << Accelerator::OptionsStr[i];
+ OS << "\n";
+ }
+ return OS;
+}
+
+raw_ostream &BitmapResource::log(raw_ostream &OS) const {
+ return OS << "Bitmap (" << ResName << "): " << BitmapLoc << "\n";
+}
+
+raw_ostream &CursorResource::log(raw_ostream &OS) const {
+ return OS << "Cursor (" << ResName << "): " << CursorLoc << "\n";
+}
+
+raw_ostream &IconResource::log(raw_ostream &OS) const {
+ return OS << "Icon (" << ResName << "): " << IconLoc << "\n";
+}
+
+raw_ostream &HTMLResource::log(raw_ostream &OS) const {
+ return OS << "HTML (" << ResName << "): " << HTMLLoc << "\n";
+}
+
+StringRef MenuDefinition::OptionsStr[MenuDefinition::NumFlags] = {
+ "CHECKED", "GRAYED", "HELP", "INACTIVE", "MENUBARBREAK", "MENUBREAK"};
+
+uint32_t MenuDefinition::OptionsFlags[MenuDefinition::NumFlags] = {
+ CHECKED, GRAYED, HELP, INACTIVE, MENUBARBREAK, MENUBREAK};
+
+raw_ostream &MenuDefinition::logFlags(raw_ostream &OS, uint16_t Flags) {
+ for (size_t i = 0; i < NumFlags; ++i)
+ if (Flags & OptionsFlags[i])
+ OS << " " << OptionsStr[i];
+ return OS;
+}
+
+raw_ostream &MenuDefinitionList::log(raw_ostream &OS) const {
+ OS << " Menu list starts\n";
+ for (auto &Item : Definitions)
+ Item->log(OS);
+ return OS << " Menu list ends\n";
+}
+
+raw_ostream &MenuItem::log(raw_ostream &OS) const {
+ OS << " MenuItem (" << Name << "), ID = " << Id;
+ logFlags(OS, Flags);
+ return OS << "\n";
+}
+
+raw_ostream &MenuSeparator::log(raw_ostream &OS) const {
+ return OS << " Menu separator\n";
+}
+
+raw_ostream &PopupItem::log(raw_ostream &OS) const {
+ OS << " Popup (" << Name << ")";
+ logFlags(OS, Flags);
+ OS << ":\n";
+ return SubItems.log(OS);
+}
+
+raw_ostream &MenuResource::log(raw_ostream &OS) const {
+ OS << "Menu (" << ResName << "):\n";
+ OptStatements->log(OS);
+ return Elements.log(OS);
+}
+
+raw_ostream &StringTableResource::log(raw_ostream &OS) const {
+ OS << "StringTable:\n";
+ OptStatements->log(OS);
+ for (const auto &String : Table) {
+ OS << " " << String.first << " =>";
+ for (const auto &S : String.second)
+ OS << " " << S;
+ OS << "\n";
+ }
+ return OS;
+}
+
+const StringMap<Control::CtlInfo> Control::SupportedCtls = {
+ {"LTEXT", CtlInfo{0x50020000, ClsStatic, true}},
+ {"CTEXT", CtlInfo{0x50020001, ClsStatic, true}},
+ {"RTEXT", CtlInfo{0x50020002, ClsStatic, true}},
+ {"ICON", CtlInfo{0x50000003, ClsStatic, true}},
+ {"PUSHBUTTON", CtlInfo{0x50010000, ClsButton, true}},
+ {"DEFPUSHBUTTON", CtlInfo{0x50010001, ClsButton, true}},
+ {"AUTO3STATE", CtlInfo{0x50010006, ClsButton, true}},
+ {"AUTOCHECKBOX", CtlInfo{0x50010003, ClsButton, true}},
+ {"AUTORADIOBUTTON", CtlInfo{0x50000009, ClsButton, true}},
+ {"CHECKBOX", CtlInfo{0x50010002, ClsButton, true}},
+ {"GROUPBOX", CtlInfo{0x50000007, ClsButton, true}},
+ {"RADIOBUTTON", CtlInfo{0x50000004, ClsButton, true}},
+ {"STATE3", CtlInfo{0x50010005, ClsButton, true}},
+ {"PUSHBOX", CtlInfo{0x5001000A, ClsButton, true}},
+ {"EDITTEXT", CtlInfo{0x50810000, ClsEdit, false}},
+ {"COMBOBOX", CtlInfo{0x50000000, ClsComboBox, false}},
+ {"LISTBOX", CtlInfo{0x50800001, ClsListBox, false}},
+ {"SCROLLBAR", CtlInfo{0x50000000, ClsScrollBar, false}},
+ {"CONTROL", CtlInfo{0x50000000, 0, true}},
+};
+
+raw_ostream &Control::log(raw_ostream &OS) const {
+ OS << " Control (" << ID << "): " << Type << ", title: " << Title
+ << ", loc: (" << X << ", " << Y << "), size: [" << Width << ", " << Height
+ << "]";
+ if (Style)
+ OS << ", style: " << (*Style).getValue();
+ if (ExtStyle)
+ OS << ", ext. style: " << *ExtStyle;
+ if (HelpID)
+ OS << ", help ID: " << *HelpID;
+ return OS << "\n";
+}
+
+raw_ostream &DialogResource::log(raw_ostream &OS) const {
+ OS << "Dialog" << (IsExtended ? "Ex" : "") << " (" << ResName << "): loc: ("
+ << X << ", " << Y << "), size: [" << Width << ", " << Height
+ << "], help ID: " << HelpID << "\n";
+ OptStatements->log(OS);
+ for (auto &Ctl : Controls)
+ Ctl.log(OS);
+ return OS;
+}
+
+raw_ostream &VersionInfoBlock::log(raw_ostream &OS) const {
+ OS << " Start of block (name: " << Name << ")\n";
+ for (auto &Stmt : Stmts)
+ Stmt->log(OS);
+ return OS << " End of block\n";
+}
+
+raw_ostream &VersionInfoValue::log(raw_ostream &OS) const {
+ OS << " " << Key << " =>";
+ size_t NumValues = Values.size();
+ for (size_t Id = 0; Id < NumValues; ++Id) {
+ if (Id > 0 && HasPrecedingComma[Id])
+ OS << ",";
+ OS << " " << Values[Id];
+ }
+ return OS << "\n";
+}
+
+using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
+using VersionInfoFixedType = VersionInfoFixed::VersionInfoFixedType;
+
+const StringRef
+ VersionInfoFixed::FixedFieldsNames[VersionInfoFixed::FtNumTypes] = {
+ "", "FILEVERSION", "PRODUCTVERSION", "FILEFLAGSMASK",
+ "FILEFLAGS", "FILEOS", "FILETYPE", "FILESUBTYPE"};
+
+const StringMap<VersionInfoFixedType> VersionInfoFixed::FixedFieldsInfoMap = {
+ {FixedFieldsNames[FtFileVersion], FtFileVersion},
+ {FixedFieldsNames[FtProductVersion], FtProductVersion},
+ {FixedFieldsNames[FtFileFlagsMask], FtFileFlagsMask},
+ {FixedFieldsNames[FtFileFlags], FtFileFlags},
+ {FixedFieldsNames[FtFileOS], FtFileOS},
+ {FixedFieldsNames[FtFileType], FtFileType},
+ {FixedFieldsNames[FtFileSubtype], FtFileSubtype}};
+
+VersionInfoFixedType VersionInfoFixed::getFixedType(StringRef Type) {
+ auto UpperType = Type.upper();
+ auto Iter = FixedFieldsInfoMap.find(UpperType);
+ if (Iter != FixedFieldsInfoMap.end())
+ return Iter->getValue();
+ return FtUnknown;
+}
+
+bool VersionInfoFixed::isTypeSupported(VersionInfoFixedType Type) {
+ return FtUnknown < Type && Type < FtNumTypes;
+}
+
+bool VersionInfoFixed::isVersionType(VersionInfoFixedType Type) {
+ switch (Type) {
+ case FtFileVersion:
+ case FtProductVersion:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+raw_ostream &VersionInfoFixed::log(raw_ostream &OS) const {
+ for (int Type = FtUnknown; Type < FtNumTypes; ++Type) {
+ if (!isTypeSupported((VersionInfoFixedType)Type))
+ continue;
+ OS << " Fixed: " << FixedFieldsNames[Type] << ":";
+ for (uint32_t Val : FixedInfo[Type])
+ OS << " " << Val;
+ OS << "\n";
+ }
+ return OS;
+}
+
+raw_ostream &VersionInfoResource::log(raw_ostream &OS) const {
+ OS << "VersionInfo (" << ResName << "):\n";
+ FixedData.log(OS);
+ return MainBlock.log(OS);
+}
+
+raw_ostream &UserDefinedResource::log(raw_ostream &OS) const {
+ OS << "User-defined (type: " << Type << ", name: " << ResName << "): ";
+ if (IsFileResource)
+ return OS << FileLoc << "\n";
+ OS << "data = ";
+ for (auto &Item : Contents)
+ OS << Item << " ";
+ return OS << "\n";
+}
+
+raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const {
+ return OS << "Characteristics: " << Value << "\n";
+}
+
+raw_ostream &VersionStmt::log(raw_ostream &OS) const {
+ return OS << "Version: " << Value << "\n";
+}
+
+raw_ostream &CaptionStmt::log(raw_ostream &OS) const {
+ return OS << "Caption: " << Value << "\n";
+}
+
+raw_ostream &ClassStmt::log(raw_ostream &OS) const {
+ return OS << "Class: " << Value << "\n";
+}
+
+raw_ostream &FontStmt::log(raw_ostream &OS) const {
+ OS << "Font: size = " << Size << ", face = " << Name
+ << ", weight = " << Weight;
+ if (Italic)
+ OS << ", italic";
+ return OS << ", charset = " << Charset << "\n";
+}
+
+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/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.h
new file mode 100644
index 00000000000..4b659f083b4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.h
@@ -0,0 +1,954 @@
+//===-- ResourceScriptStmt.h ------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This lists all the resource and statement types occurring in RC scripts.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H
+
+#include "ResourceScriptToken.h"
+#include "ResourceVisitor.h"
+
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/StringSet.h"
+
+namespace llvm {
+namespace rc {
+
+// Integer wrapper that also holds information whether the user declared
+// the integer to be long (by appending L to the end of the integer) or not.
+// It allows to be implicitly cast from and to uint32_t in order
+// to be compatible with the parts of code that don't care about the integers
+// being marked long.
+class RCInt {
+ uint32_t Val;
+ bool Long;
+
+public:
+ RCInt(const RCToken &Token)
+ : Val(Token.intValue()), Long(Token.isLongInt()) {}
+ RCInt(uint32_t Value) : Val(Value), Long(false) {}
+ RCInt(uint32_t Value, bool IsLong) : Val(Value), Long(IsLong) {}
+ operator uint32_t() const { return Val; }
+ bool isLong() const { return Long; }
+
+ RCInt &operator+=(const RCInt &Rhs) {
+ std::tie(Val, Long) = std::make_pair(Val + Rhs.Val, Long | Rhs.Long);
+ return *this;
+ }
+
+ RCInt &operator-=(const RCInt &Rhs) {
+ std::tie(Val, Long) = std::make_pair(Val - Rhs.Val, Long | Rhs.Long);
+ return *this;
+ }
+
+ RCInt &operator|=(const RCInt &Rhs) {
+ std::tie(Val, Long) = std::make_pair(Val | Rhs.Val, Long | Rhs.Long);
+ return *this;
+ }
+
+ RCInt &operator&=(const RCInt &Rhs) {
+ std::tie(Val, Long) = std::make_pair(Val & Rhs.Val, Long | Rhs.Long);
+ return *this;
+ }
+
+ RCInt operator-() const { return {-Val, Long}; }
+ RCInt operator~() const { return {~Val, Long}; }
+
+ friend raw_ostream &operator<<(raw_ostream &OS, const RCInt &Int) {
+ return OS << Int.Val << (Int.Long ? "L" : "");
+ }
+};
+
+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:
+ union Data {
+ RCInt Int;
+ StringRef String;
+ Data(RCInt Value) : Int(Value) {}
+ Data(const StringRef Value) : String(Value) {}
+ Data(const RCToken &Token) {
+ if (Token.kind() == RCToken::Kind::Int)
+ Int = RCInt(Token);
+ else
+ String = Token.value();
+ }
+ } Data;
+ bool IsInt;
+
+public:
+ IntOrString() : IntOrString(RCInt(0)) {}
+ IntOrString(uint32_t Value) : Data(Value), IsInt(true) {}
+ IntOrString(RCInt Value) : Data(Value), IsInt(true) {}
+ IntOrString(StringRef Value) : Data(Value), IsInt(false) {}
+ IntOrString(const RCToken &Token)
+ : Data(Token), IsInt(Token.kind() == RCToken::Kind::Int) {}
+
+ bool equalsLower(const char *Str) {
+ return !IsInt && Data.String.equals_insensitive(Str);
+ }
+
+ bool isInt() const { return IsInt; }
+
+ RCInt getInt() const {
+ assert(IsInt);
+ return Data.Int;
+ }
+
+ const StringRef &getString() const {
+ assert(!IsInt);
+ return Data.String;
+ }
+
+ operator Twine() const {
+ return isInt() ? Twine(getInt()) : Twine(getString());
+ }
+
+ friend raw_ostream &operator<<(raw_ostream &, const IntOrString &);
+};
+
+enum ResourceKind {
+ // These resource kinds have corresponding .res resource type IDs
+ // (TYPE in RESOURCEHEADER structure). The numeric value assigned to each
+ // kind is equal to this type ID.
+ RkNull = 0,
+ RkSingleCursor = 1,
+ RkBitmap = 2,
+ RkSingleIcon = 3,
+ RkMenu = 4,
+ RkDialog = 5,
+ RkStringTableBundle = 6,
+ RkAccelerators = 9,
+ RkRcData = 10,
+ RkCursorGroup = 12,
+ RkIconGroup = 14,
+ RkVersionInfo = 16,
+ RkHTML = 23,
+
+ // These kinds don't have assigned type IDs (they might be the resources
+ // of invalid kind, expand to many resource structures in .res files,
+ // or have variable type ID). In order to avoid ID clashes with IDs above,
+ // we assign the kinds the values 256 and larger.
+ RkInvalid = 256,
+ RkBase,
+ RkCursor,
+ RkIcon,
+ RkStringTable,
+ RkUser,
+ RkSingleCursorOrIconRes,
+ RkCursorOrIconGroupRes,
+};
+
+// Non-zero memory flags.
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648027(v=vs.85).aspx
+enum MemoryFlags {
+ MfMoveable = 0x10,
+ MfPure = 0x20,
+ MfPreload = 0x40,
+ MfDiscardable = 0x1000
+};
+
+// Base resource. All the resources should derive from this base.
+class RCResource {
+public:
+ IntOrString ResName;
+ uint16_t MemoryFlags = getDefaultMemoryFlags();
+ void setName(const IntOrString &Name) { ResName = Name; }
+ virtual raw_ostream &log(raw_ostream &OS) const {
+ return OS << "Base statement\n";
+ };
+ RCResource() {}
+ RCResource(uint16_t Flags) : MemoryFlags(Flags) {}
+ virtual ~RCResource() {}
+
+ virtual Error visit(Visitor *) const {
+ llvm_unreachable("This is unable to call methods from Visitor base");
+ }
+
+ // Apply the statements attached to this resource. Generic resources
+ // don't have any.
+ virtual Error applyStmts(Visitor *) const { return Error::success(); }
+
+ // By default, memory flags are DISCARDABLE | PURE | MOVEABLE.
+ static uint16_t getDefaultMemoryFlags() {
+ return MfDiscardable | MfPure | MfMoveable;
+ }
+
+ virtual ResourceKind getKind() const { return RkBase; }
+ static bool classof(const RCResource *Res) { return true; }
+
+ virtual IntOrString getResourceType() const {
+ llvm_unreachable("This cannot be called on objects without types.");
+ }
+ virtual Twine getResourceTypeName() const {
+ llvm_unreachable("This cannot be called on objects without types.");
+ };
+};
+
+// An empty resource. It has no content, type 0, ID 0 and all of its
+// characteristics are equal to 0.
+class NullResource : public RCResource {
+public:
+ NullResource() : RCResource(0) {}
+ raw_ostream &log(raw_ostream &OS) const override {
+ return OS << "Null resource\n";
+ }
+ Error visit(Visitor *V) const override { return V->visitNullResource(this); }
+ IntOrString getResourceType() const override { return 0; }
+ Twine getResourceTypeName() const override { return "(NULL)"; }
+};
+
+// Optional statement base. All such statements should derive from this base.
+class OptionalStmt : public RCResource {};
+
+class OptionalStmtList : public OptionalStmt {
+ std::vector<std::unique_ptr<OptionalStmt>> Statements;
+
+public:
+ OptionalStmtList() {}
+ raw_ostream &log(raw_ostream &OS) const override;
+
+ void addStmt(std::unique_ptr<OptionalStmt> Stmt) {
+ Statements.push_back(std::move(Stmt));
+ }
+
+ Error visit(Visitor *V) const override {
+ for (auto &StmtPtr : Statements)
+ if (auto Err = StmtPtr->visit(V))
+ return Err;
+ return Error::success();
+ }
+};
+
+class OptStatementsRCResource : public RCResource {
+public:
+ std::unique_ptr<OptionalStmtList> OptStatements;
+
+ OptStatementsRCResource(OptionalStmtList &&Stmts,
+ uint16_t Flags = RCResource::getDefaultMemoryFlags())
+ : RCResource(Flags),
+ OptStatements(std::make_unique<OptionalStmtList>(std::move(Stmts))) {}
+
+ Error applyStmts(Visitor *V) const override {
+ return OptStatements->visit(V);
+ }
+};
+
+// LANGUAGE statement. It can occur both as a top-level statement (in such
+// a situation, it changes the default language until the end of the file)
+// and as an optional resource statement (then it changes the language
+// of a single resource).
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381019(v=vs.85).aspx
+class LanguageResource : public OptionalStmt {
+public:
+ uint32_t Lang, SubLang;
+
+ LanguageResource(uint32_t LangId, uint32_t SubLangId)
+ : Lang(LangId), SubLang(SubLangId) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ // This is not a regular top-level statement; when it occurs, it just
+ // modifies the language context.
+ Error visit(Visitor *V) const override { return V->visitLanguageStmt(this); }
+ Twine getResourceTypeName() const override { return "LANGUAGE"; }
+};
+
+// ACCELERATORS resource. Defines a named table of accelerators for the app.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380610(v=vs.85).aspx
+class AcceleratorsResource : public OptStatementsRCResource {
+public:
+ class Accelerator {
+ public:
+ IntOrString Event;
+ uint32_t Id;
+ uint16_t Flags;
+
+ enum Options {
+ // This is actually 0x0000 (accelerator is assumed to be ASCII if it's
+ // not VIRTKEY). However, rc.exe behavior is different in situations
+ // "only ASCII defined" and "neither ASCII nor VIRTKEY defined".
+ // Therefore, we include ASCII as another flag. This must be zeroed
+ // when serialized.
+ ASCII = 0x8000,
+ VIRTKEY = 0x0001,
+ NOINVERT = 0x0002,
+ ALT = 0x0010,
+ SHIFT = 0x0004,
+ CONTROL = 0x0008
+ };
+
+ static constexpr size_t NumFlags = 6;
+ static StringRef OptionsStr[NumFlags];
+ static uint32_t OptionsFlags[NumFlags];
+ };
+
+ AcceleratorsResource(OptionalStmtList &&List, uint16_t Flags)
+ : OptStatementsRCResource(std::move(List), Flags) {}
+
+ std::vector<Accelerator> Accelerators;
+
+ void addAccelerator(IntOrString Event, uint32_t Id, uint16_t Flags) {
+ Accelerators.push_back(Accelerator{Event, Id, Flags});
+ }
+ raw_ostream &log(raw_ostream &) const override;
+
+ IntOrString getResourceType() const override { return RkAccelerators; }
+ static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }
+ Twine getResourceTypeName() const override { return "ACCELERATORS"; }
+
+ Error visit(Visitor *V) const override {
+ return V->visitAcceleratorsResource(this);
+ }
+ ResourceKind getKind() const override { return RkAccelerators; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkAccelerators;
+ }
+};
+
+// BITMAP resource. Represents a bitmap (".bmp") file.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380680(v=vs.85).aspx
+class BitmapResource : public RCResource {
+public:
+ StringRef BitmapLoc;
+
+ BitmapResource(StringRef Location, uint16_t Flags)
+ : RCResource(Flags), BitmapLoc(Location) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ IntOrString getResourceType() const override { return RkBitmap; }
+ static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }
+
+ Twine getResourceTypeName() const override { return "BITMAP"; }
+ Error visit(Visitor *V) const override {
+ return V->visitBitmapResource(this);
+ }
+ ResourceKind getKind() const override { return RkBitmap; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkBitmap;
+ }
+};
+
+// CURSOR resource. Represents a single cursor (".cur") file.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380920(v=vs.85).aspx
+class CursorResource : public RCResource {
+public:
+ StringRef CursorLoc;
+
+ CursorResource(StringRef Location, uint16_t Flags)
+ : RCResource(Flags), CursorLoc(Location) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ Twine getResourceTypeName() const override { return "CURSOR"; }
+ static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; }
+ Error visit(Visitor *V) const override {
+ return V->visitCursorResource(this);
+ }
+ ResourceKind getKind() const override { return RkCursor; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkCursor;
+ }
+};
+
+// ICON resource. Represents a single ".ico" file containing a group of icons.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381018(v=vs.85).aspx
+class IconResource : public RCResource {
+public:
+ StringRef IconLoc;
+
+ IconResource(StringRef Location, uint16_t Flags)
+ : RCResource(Flags), IconLoc(Location) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ Twine getResourceTypeName() const override { return "ICON"; }
+ static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; }
+ Error visit(Visitor *V) const override { return V->visitIconResource(this); }
+ ResourceKind getKind() const override { return RkIcon; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkIcon;
+ }
+};
+
+// HTML resource. Represents a local webpage that is to be embedded into the
+// resulting resource file. It embeds a file only - no additional resources
+// (images etc.) are included with this resource.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa966018(v=vs.85).aspx
+class HTMLResource : public RCResource {
+public:
+ StringRef HTMLLoc;
+
+ HTMLResource(StringRef Location, uint16_t Flags)
+ : RCResource(Flags), HTMLLoc(Location) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ Error visit(Visitor *V) const override { return V->visitHTMLResource(this); }
+
+ // Curiously, file resources don't have DISCARDABLE flag set.
+ static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }
+ IntOrString getResourceType() const override { return RkHTML; }
+ Twine getResourceTypeName() const override { return "HTML"; }
+ ResourceKind getKind() const override { return RkHTML; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkHTML;
+ }
+};
+
+// -- MENU resource and its helper classes --
+// This resource describes the contents of an application menu
+// (usually located in the upper part of the dialog.)
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381025(v=vs.85).aspx
+
+// Description of a single submenu item.
+class MenuDefinition {
+public:
+ enum Options {
+ CHECKED = 0x0008,
+ GRAYED = 0x0001,
+ HELP = 0x4000,
+ INACTIVE = 0x0002,
+ MENUBARBREAK = 0x0020,
+ MENUBREAK = 0x0040
+ };
+
+ enum MenuDefKind { MkBase, MkSeparator, MkMenuItem, MkPopup };
+
+ static constexpr size_t NumFlags = 6;
+ static StringRef OptionsStr[NumFlags];
+ static uint32_t OptionsFlags[NumFlags];
+ static raw_ostream &logFlags(raw_ostream &, uint16_t Flags);
+ virtual raw_ostream &log(raw_ostream &OS) const {
+ return OS << "Base menu definition\n";
+ }
+ virtual ~MenuDefinition() {}
+
+ virtual uint16_t getResFlags() const { return 0; }
+ virtual MenuDefKind getKind() const { return MkBase; }
+};
+
+// Recursive description of a whole submenu.
+class MenuDefinitionList : public MenuDefinition {
+public:
+ std::vector<std::unique_ptr<MenuDefinition>> Definitions;
+
+ void addDefinition(std::unique_ptr<MenuDefinition> Def) {
+ Definitions.push_back(std::move(Def));
+ }
+ raw_ostream &log(raw_ostream &) const override;
+};
+
+// Separator in MENU definition (MENUITEM SEPARATOR).
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx
+class MenuSeparator : public MenuDefinition {
+public:
+ raw_ostream &log(raw_ostream &) const override;
+
+ MenuDefKind getKind() const override { return MkSeparator; }
+ static bool classof(const MenuDefinition *D) {
+ return D->getKind() == MkSeparator;
+ }
+};
+
+// MENUITEM statement definition.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx
+class MenuItem : public MenuDefinition {
+public:
+ StringRef Name;
+ uint32_t Id;
+ uint16_t Flags;
+
+ MenuItem(StringRef Caption, uint32_t ItemId, uint16_t ItemFlags)
+ : Name(Caption), Id(ItemId), Flags(ItemFlags) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ uint16_t getResFlags() const override { return Flags; }
+ MenuDefKind getKind() const override { return MkMenuItem; }
+ static bool classof(const MenuDefinition *D) {
+ return D->getKind() == MkMenuItem;
+ }
+};
+
+// POPUP statement definition.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381030(v=vs.85).aspx
+class PopupItem : public MenuDefinition {
+public:
+ StringRef Name;
+ uint16_t Flags;
+ MenuDefinitionList SubItems;
+
+ PopupItem(StringRef Caption, uint16_t ItemFlags,
+ MenuDefinitionList &&SubItemsList)
+ : Name(Caption), Flags(ItemFlags), SubItems(std::move(SubItemsList)) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ // This has an additional (0x10) flag. It doesn't match with documented
+ // 0x01 flag, though.
+ uint16_t getResFlags() const override { return Flags | 0x10; }
+ MenuDefKind getKind() const override { return MkPopup; }
+ static bool classof(const MenuDefinition *D) {
+ return D->getKind() == MkPopup;
+ }
+};
+
+// Menu resource definition.
+class MenuResource : public OptStatementsRCResource {
+public:
+ MenuDefinitionList Elements;
+
+ MenuResource(OptionalStmtList &&OptStmts, MenuDefinitionList &&Items,
+ uint16_t Flags)
+ : OptStatementsRCResource(std::move(OptStmts), Flags),
+ Elements(std::move(Items)) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ IntOrString getResourceType() const override { return RkMenu; }
+ Twine getResourceTypeName() const override { return "MENU"; }
+ Error visit(Visitor *V) const override { return V->visitMenuResource(this); }
+ ResourceKind getKind() const override { return RkMenu; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkMenu;
+ }
+};
+
+// STRINGTABLE resource. Contains a list of strings, each having its unique ID.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx
+class StringTableResource : public OptStatementsRCResource {
+public:
+ std::vector<std::pair<uint32_t, std::vector<StringRef>>> Table;
+
+ StringTableResource(OptionalStmtList &&List, uint16_t Flags)
+ : OptStatementsRCResource(std::move(List), Flags) {}
+ void addStrings(uint32_t ID, std::vector<StringRef> &&Strings) {
+ Table.emplace_back(ID, Strings);
+ }
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "STRINGTABLE"; }
+ Error visit(Visitor *V) const override {
+ return V->visitStringTableResource(this);
+ }
+};
+
+// -- DIALOG(EX) resource and its helper classes --
+//
+// This resource describes dialog boxes and controls residing inside them.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381003(v=vs.85).aspx
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx
+
+// Single control definition.
+class Control {
+public:
+ StringRef Type;
+ IntOrString Title;
+ uint32_t ID, X, Y, Width, Height;
+ Optional<IntWithNotMask> Style;
+ Optional<uint32_t> ExtStyle, HelpID;
+ IntOrString Class;
+
+ // Control classes as described in DLGITEMTEMPLATEEX documentation.
+ //
+ // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms645389.aspx
+ enum CtlClasses {
+ ClsButton = 0x80,
+ ClsEdit = 0x81,
+ ClsStatic = 0x82,
+ ClsListBox = 0x83,
+ ClsScrollBar = 0x84,
+ ClsComboBox = 0x85
+ };
+
+ // Simple information about a single control type.
+ struct CtlInfo {
+ uint32_t Style;
+ uint16_t CtlClass;
+ bool HasTitle;
+ };
+
+ Control(StringRef CtlType, IntOrString CtlTitle, uint32_t CtlID,
+ uint32_t PosX, uint32_t PosY, uint32_t ItemWidth, uint32_t ItemHeight,
+ 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),
+ ExtStyle(ExtItemStyle), HelpID(CtlHelpID), Class(CtlClass) {}
+
+ static const StringMap<CtlInfo> SupportedCtls;
+
+ raw_ostream &log(raw_ostream &) const;
+};
+
+// Single dialog definition. We don't create distinct classes for DIALOG and
+// DIALOGEX because of their being too similar to each other. We only have a
+// flag determining the type of the dialog box.
+class DialogResource : public OptStatementsRCResource {
+public:
+ uint32_t X, Y, Width, Height, HelpID;
+ std::vector<Control> Controls;
+ bool IsExtended;
+
+ DialogResource(uint32_t PosX, uint32_t PosY, uint32_t DlgWidth,
+ uint32_t DlgHeight, uint32_t DlgHelpID,
+ OptionalStmtList &&OptStmts, bool IsDialogEx, uint16_t Flags)
+ : OptStatementsRCResource(std::move(OptStmts), Flags), X(PosX), Y(PosY),
+ Width(DlgWidth), Height(DlgHeight), HelpID(DlgHelpID),
+ IsExtended(IsDialogEx) {}
+
+ void addControl(Control &&Ctl) { Controls.push_back(std::move(Ctl)); }
+
+ raw_ostream &log(raw_ostream &) const override;
+
+ // It was a weird design decision to assign the same resource type number
+ // both for DIALOG and DIALOGEX (and the same structure version number).
+ // It makes it possible for DIALOG to be mistaken for DIALOGEX.
+ IntOrString getResourceType() const override { return RkDialog; }
+ Twine getResourceTypeName() const override {
+ return "DIALOG" + Twine(IsExtended ? "EX" : "");
+ }
+ Error visit(Visitor *V) const override {
+ return V->visitDialogResource(this);
+ }
+ ResourceKind getKind() const override { return RkDialog; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkDialog;
+ }
+};
+
+// User-defined resource. It is either:
+// * a link to the file, e.g. NAME TYPE "filename",
+// * or contains a list of integers and strings, e.g. NAME TYPE {1, "a", 2}.
+class UserDefinedResource : public RCResource {
+public:
+ IntOrString Type;
+ StringRef FileLoc;
+ std::vector<IntOrString> Contents;
+ bool IsFileResource;
+
+ UserDefinedResource(IntOrString ResourceType, StringRef FileLocation,
+ uint16_t Flags)
+ : RCResource(Flags), Type(ResourceType), FileLoc(FileLocation),
+ IsFileResource(true) {}
+ UserDefinedResource(IntOrString ResourceType, std::vector<IntOrString> &&Data,
+ uint16_t Flags)
+ : RCResource(Flags), Type(ResourceType), Contents(std::move(Data)),
+ IsFileResource(false) {}
+
+ raw_ostream &log(raw_ostream &) const override;
+ IntOrString getResourceType() const override { return Type; }
+ Twine getResourceTypeName() const override { return Type; }
+ static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }
+
+ Error visit(Visitor *V) const override {
+ return V->visitUserDefinedResource(this);
+ }
+ ResourceKind getKind() const override { return RkUser; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkUser;
+ }
+};
+
+// -- VERSIONINFO resource and its helper classes --
+//
+// This resource lists the version information on the executable/library.
+// The declaration consists of the following items:
+// * A number of fixed optional version statements (e.g. FILEVERSION, FILEOS)
+// * BEGIN
+// * A number of BLOCK and/or VALUE statements. BLOCK recursively defines
+// another block of version information, whereas VALUE defines a
+// key -> value correspondence. There might be more than one value
+// corresponding to the single key.
+// * END
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381058(v=vs.85).aspx
+
+// A single VERSIONINFO statement;
+class VersionInfoStmt {
+public:
+ enum StmtKind { StBase = 0, StBlock = 1, StValue = 2 };
+
+ virtual raw_ostream &log(raw_ostream &OS) const { return OS << "VI stmt\n"; }
+ virtual ~VersionInfoStmt() {}
+
+ virtual StmtKind getKind() const { return StBase; }
+ static bool classof(const VersionInfoStmt *S) {
+ return S->getKind() == StBase;
+ }
+};
+
+// BLOCK definition; also the main VERSIONINFO declaration is considered a
+// BLOCK, although it has no name.
+// The correct top-level blocks are "VarFileInfo" and "StringFileInfo". We don't
+// care about them at the parsing phase.
+class VersionInfoBlock : public VersionInfoStmt {
+public:
+ std::vector<std::unique_ptr<VersionInfoStmt>> Stmts;
+ StringRef Name;
+
+ VersionInfoBlock(StringRef BlockName) : Name(BlockName) {}
+ void addStmt(std::unique_ptr<VersionInfoStmt> Stmt) {
+ Stmts.push_back(std::move(Stmt));
+ }
+ raw_ostream &log(raw_ostream &) const override;
+
+ StmtKind getKind() const override { return StBlock; }
+ static bool classof(const VersionInfoStmt *S) {
+ return S->getKind() == StBlock;
+ }
+};
+
+class VersionInfoValue : public VersionInfoStmt {
+public:
+ StringRef Key;
+ std::vector<IntOrString> Values;
+ BitVector HasPrecedingComma;
+
+ VersionInfoValue(StringRef InfoKey, std::vector<IntOrString> &&Vals,
+ BitVector &&CommasBeforeVals)
+ : Key(InfoKey), Values(std::move(Vals)),
+ HasPrecedingComma(std::move(CommasBeforeVals)) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ StmtKind getKind() const override { return StValue; }
+ static bool classof(const VersionInfoStmt *S) {
+ return S->getKind() == StValue;
+ }
+};
+
+class VersionInfoResource : public RCResource {
+public:
+ // A class listing fixed VERSIONINFO statements (occuring before main BEGIN).
+ // If any of these is not specified, it is assumed by the original tool to
+ // be equal to 0.
+ class VersionInfoFixed {
+ public:
+ enum VersionInfoFixedType {
+ FtUnknown,
+ FtFileVersion,
+ FtProductVersion,
+ FtFileFlagsMask,
+ FtFileFlags,
+ FtFileOS,
+ FtFileType,
+ FtFileSubtype,
+ FtNumTypes
+ };
+
+ private:
+ static const StringMap<VersionInfoFixedType> FixedFieldsInfoMap;
+ static const StringRef FixedFieldsNames[FtNumTypes];
+
+ public:
+ SmallVector<uint32_t, 4> FixedInfo[FtNumTypes];
+ SmallVector<bool, FtNumTypes> IsTypePresent;
+
+ static VersionInfoFixedType getFixedType(StringRef Type);
+ static bool isTypeSupported(VersionInfoFixedType Type);
+ static bool isVersionType(VersionInfoFixedType Type);
+
+ VersionInfoFixed() : IsTypePresent(FtNumTypes, false) {}
+
+ void setValue(VersionInfoFixedType Type, ArrayRef<uint32_t> Value) {
+ FixedInfo[Type] = SmallVector<uint32_t, 4>(Value.begin(), Value.end());
+ IsTypePresent[Type] = true;
+ }
+
+ raw_ostream &log(raw_ostream &) const;
+ };
+
+ VersionInfoBlock MainBlock;
+ VersionInfoFixed FixedData;
+
+ VersionInfoResource(VersionInfoBlock &&TopLevelBlock,
+ VersionInfoFixed &&FixedInfo, uint16_t Flags)
+ : RCResource(Flags), MainBlock(std::move(TopLevelBlock)),
+ FixedData(std::move(FixedInfo)) {}
+
+ raw_ostream &log(raw_ostream &) const override;
+ IntOrString getResourceType() const override { return RkVersionInfo; }
+ static uint16_t getDefaultMemoryFlags() { return MfMoveable | MfPure; }
+ Twine getResourceTypeName() const override { return "VERSIONINFO"; }
+ Error visit(Visitor *V) const override {
+ return V->visitVersionInfoResource(this);
+ }
+ ResourceKind getKind() const override { return RkVersionInfo; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkVersionInfo;
+ }
+};
+
+// CHARACTERISTICS optional statement.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx
+class CharacteristicsStmt : public OptionalStmt {
+public:
+ uint32_t Value;
+
+ CharacteristicsStmt(uint32_t Characteristic) : Value(Characteristic) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ Twine getResourceTypeName() const override { return "CHARACTERISTICS"; }
+ Error visit(Visitor *V) const override {
+ return V->visitCharacteristicsStmt(this);
+ }
+};
+
+// VERSION optional statement.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381059(v=vs.85).aspx
+class VersionStmt : public OptionalStmt {
+public:
+ uint32_t Value;
+
+ VersionStmt(uint32_t Version) : Value(Version) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ Twine getResourceTypeName() const override { return "VERSION"; }
+ Error visit(Visitor *V) const override { return V->visitVersionStmt(this); }
+};
+
+// CAPTION optional statement.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380778(v=vs.85).aspx
+class CaptionStmt : public OptionalStmt {
+public:
+ StringRef Value;
+
+ CaptionStmt(StringRef Caption) : Value(Caption) {}
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "CAPTION"; }
+ Error visit(Visitor *V) const override { return V->visitCaptionStmt(this); }
+};
+
+// FONT optional statement.
+// Note that the documentation is inaccurate: it expects five arguments to be
+// given, however the example provides only two. In fact, the original tool
+// expects two arguments - point size and name of the typeface.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381013(v=vs.85).aspx
+class FontStmt : public OptionalStmt {
+public:
+ uint32_t Size, Weight, Charset;
+ StringRef Name;
+ bool Italic;
+
+ FontStmt(uint32_t FontSize, StringRef FontName, uint32_t FontWeight,
+ bool FontItalic, uint32_t FontCharset)
+ : Size(FontSize), Weight(FontWeight), Charset(FontCharset),
+ Name(FontName), Italic(FontItalic) {}
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "FONT"; }
+ Error visit(Visitor *V) const override { return V->visitFontStmt(this); }
+};
+
+// STYLE optional statement.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381051(v=vs.85).aspx
+class StyleStmt : public OptionalStmt {
+public:
+ uint32_t Value;
+
+ StyleStmt(uint32_t Style) : Value(Style) {}
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "STYLE"; }
+ 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
+class ClassStmt : public OptionalStmt {
+public:
+ IntOrString Value;
+
+ ClassStmt(IntOrString Class) : Value(Class) {}
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "CLASS"; }
+ Error visit(Visitor *V) const override { return V->visitClassStmt(this); }
+};
+
+} // namespace rc
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.cpp b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.cpp
new file mode 100644
index 00000000000..a8f40abdf8a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.cpp
@@ -0,0 +1,367 @@
+//===-- ResourceScriptToken.cpp ---------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This file implements an interface defined in ResourceScriptToken.h.
+// In particular, it defines an .rc script tokenizer.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceScriptToken.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cstdlib>
+#include <utility>
+
+using namespace llvm;
+
+using Kind = RCToken::Kind;
+
+// Checks if Representation is a correct description of an RC integer.
+// It should be a 32-bit unsigned integer, either decimal, octal (0[0-7]+),
+// or hexadecimal (0x[0-9a-f]+). It might be followed by a single 'L'
+// character (that is the difference between our representation and
+// StringRef's one). If Representation is correct, 'true' is returned and
+// the return value is put back in Num.
+static bool rcGetAsInteger(StringRef Representation, uint32_t &Num) {
+ size_t Length = Representation.size();
+ if (Length == 0)
+ return false;
+ // Strip the last 'L' if unnecessary.
+ if (std::toupper(Representation.back()) == 'L')
+ Representation = Representation.drop_back(1);
+
+ return !Representation.getAsInteger<uint32_t>(0, Num);
+}
+
+RCToken::RCToken(RCToken::Kind RCTokenKind, StringRef Value)
+ : TokenKind(RCTokenKind), TokenValue(Value) {}
+
+uint32_t RCToken::intValue() const {
+ assert(TokenKind == Kind::Int);
+ // We assume that the token already is a correct integer (checked by
+ // rcGetAsInteger).
+ uint32_t Result;
+ bool IsSuccess = rcGetAsInteger(TokenValue, Result);
+ assert(IsSuccess);
+ (void)IsSuccess; // Silence the compiler warning when -DNDEBUG flag is on.
+ return Result;
+}
+
+bool RCToken::isLongInt() const {
+ return TokenKind == Kind::Int && std::toupper(TokenValue.back()) == 'L';
+}
+
+StringRef RCToken::value() const { return TokenValue; }
+
+Kind RCToken::kind() const { return TokenKind; }
+
+bool RCToken::isBinaryOp() const {
+ switch (TokenKind) {
+ case Kind::Plus:
+ case Kind::Minus:
+ case Kind::Pipe:
+ case Kind::Amp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static Error getStringError(const Twine &message) {
+ return make_error<StringError>("Error parsing file: " + message,
+ inconvertibleErrorCode());
+}
+
+namespace {
+
+class Tokenizer {
+public:
+ Tokenizer(StringRef Input) : Data(Input), DataLength(Input.size()), Pos(0) {}
+
+ Expected<std::vector<RCToken>> run();
+
+private:
+ // All 'advancing' methods return boolean values; if they're equal to false,
+ // the stream has ended or failed.
+ bool advance(size_t Amount = 1);
+ bool skipWhitespaces();
+
+ // Consumes a token. If any problem occurred, a non-empty Error is returned.
+ Error consumeToken(const Kind TokenKind);
+
+ // Check if tokenizer is about to read FollowingChars.
+ bool willNowRead(StringRef FollowingChars) const;
+
+ // Check if tokenizer can start reading an identifier at current position.
+ // The original tool did non specify the rules to determine what is a correct
+ // identifier. We assume they should follow the C convention:
+ // [a-zA-Z_][a-zA-Z0-9_]*.
+ bool canStartIdentifier() const;
+ // Check if tokenizer can continue reading an identifier.
+ bool canContinueIdentifier() const;
+
+ // Check if tokenizer can start reading an integer.
+ // A correct integer always starts with a 0-9 digit,
+ // can contain characters 0-9A-Fa-f (digits),
+ // Ll (marking the integer is 32-bit), Xx (marking the representation
+ // is hexadecimal). As some kind of separator should come after the
+ // integer, we can consume the integer until a non-alphanumeric
+ // character.
+ bool canStartInt() const;
+ bool canContinueInt() const;
+
+ bool canStartString() const;
+
+ // Check if tokenizer can start reading a single line comment (e.g. a comment
+ // that begins with '//')
+ bool canStartLineComment() const;
+
+ // Check if tokenizer can start or finish reading a block comment (e.g. a
+ // comment that begins with '/*' and ends with '*/')
+ bool canStartBlockComment() const;
+
+ // Throw away all remaining characters on the current line.
+ void skipCurrentLine();
+
+ bool streamEof() const;
+
+ // Classify the token that is about to be read from the current position.
+ Kind classifyCurrentToken() const;
+
+ // Process the Kind::Identifier token - check if it is
+ // an identifier describing a block start or end.
+ void processIdentifier(RCToken &token) const;
+
+ StringRef Data;
+ size_t DataLength, Pos;
+};
+
+void Tokenizer::skipCurrentLine() {
+ Pos = Data.find_first_of("\r\n", Pos);
+ Pos = Data.find_first_not_of("\r\n", Pos);
+
+ if (Pos == StringRef::npos)
+ Pos = DataLength;
+}
+
+Expected<std::vector<RCToken>> Tokenizer::run() {
+ Pos = 0;
+ std::vector<RCToken> Result;
+
+ // Consume an optional UTF-8 Byte Order Mark.
+ if (willNowRead("\xef\xbb\xbf"))
+ advance(3);
+
+ while (!streamEof()) {
+ if (!skipWhitespaces())
+ break;
+
+ Kind TokenKind = classifyCurrentToken();
+ if (TokenKind == Kind::Invalid)
+ return getStringError("Invalid token found at position " + Twine(Pos));
+
+ const size_t TokenStart = Pos;
+ if (Error TokenError = consumeToken(TokenKind))
+ return std::move(TokenError);
+
+ // Comments are just deleted, don't bother saving them.
+ if (TokenKind == Kind::LineComment || TokenKind == Kind::StartComment)
+ continue;
+
+ RCToken Token(TokenKind, Data.take_front(Pos).drop_front(TokenStart));
+ if (TokenKind == Kind::Identifier) {
+ processIdentifier(Token);
+ } else if (TokenKind == Kind::Int) {
+ uint32_t TokenInt;
+ if (!rcGetAsInteger(Token.value(), TokenInt)) {
+ // The integer has incorrect format or cannot be represented in
+ // a 32-bit integer.
+ return getStringError("Integer invalid or too large: " +
+ Token.value().str());
+ }
+ }
+
+ Result.push_back(Token);
+ }
+
+ return Result;
+}
+
+bool Tokenizer::advance(size_t Amount) {
+ Pos += Amount;
+ return !streamEof();
+}
+
+bool Tokenizer::skipWhitespaces() {
+ while (!streamEof() && isSpace(Data[Pos]))
+ advance();
+ return !streamEof();
+}
+
+Error Tokenizer::consumeToken(const Kind TokenKind) {
+ switch (TokenKind) {
+ // One-character token consumption.
+#define TOKEN(Name)
+#define SHORT_TOKEN(Name, Ch) case Kind::Name:
+#include "ResourceScriptTokenList.def"
+ advance();
+ return Error::success();
+
+ case Kind::LineComment:
+ advance(2);
+ skipCurrentLine();
+ return Error::success();
+
+ case Kind::StartComment: {
+ advance(2);
+ auto EndPos = Data.find("*/", Pos);
+ if (EndPos == StringRef::npos)
+ return getStringError(
+ "Unclosed multi-line comment beginning at position " + Twine(Pos));
+ advance(EndPos - Pos);
+ advance(2);
+ return Error::success();
+ }
+ case Kind::Identifier:
+ while (!streamEof() && canContinueIdentifier())
+ advance();
+ return Error::success();
+
+ case Kind::Int:
+ while (!streamEof() && canContinueInt())
+ advance();
+ return Error::success();
+
+ case Kind::String:
+ // Consume the preceding 'L', if there is any.
+ if (std::toupper(Data[Pos]) == 'L')
+ advance();
+ // Consume the double-quote.
+ advance();
+
+ // Consume the characters until the end of the file, line or string.
+ while (true) {
+ if (streamEof()) {
+ return getStringError("Unterminated string literal.");
+ } else if (Data[Pos] == '"') {
+ // Consume the ending double-quote.
+ advance();
+ // However, if another '"' follows this double-quote, the string didn't
+ // end and we just included '"' into the string.
+ if (!willNowRead("\""))
+ return Error::success();
+ } else if (Data[Pos] == '\n') {
+ return getStringError("String literal not terminated in the line.");
+ }
+
+ advance();
+ }
+
+ case Kind::Invalid:
+ assert(false && "Cannot consume an invalid token.");
+ }
+
+ llvm_unreachable("Unknown RCToken::Kind");
+}
+
+bool Tokenizer::willNowRead(StringRef FollowingChars) const {
+ return Data.drop_front(Pos).startswith(FollowingChars);
+}
+
+bool Tokenizer::canStartIdentifier() const {
+ assert(!streamEof());
+
+ const char CurChar = Data[Pos];
+ return std::isalpha(CurChar) || CurChar == '_' || CurChar == '.';
+}
+
+bool Tokenizer::canContinueIdentifier() const {
+ assert(!streamEof());
+ const char CurChar = Data[Pos];
+ return std::isalnum(CurChar) || CurChar == '_' || CurChar == '.' ||
+ CurChar == '/' || CurChar == '\\' || CurChar == '-';
+}
+
+bool Tokenizer::canStartInt() const {
+ assert(!streamEof());
+ return std::isdigit(Data[Pos]);
+}
+
+bool Tokenizer::canStartBlockComment() const {
+ assert(!streamEof());
+ return Data.drop_front(Pos).startswith("/*");
+}
+
+bool Tokenizer::canStartLineComment() const {
+ assert(!streamEof());
+ return Data.drop_front(Pos).startswith("//");
+}
+
+bool Tokenizer::canContinueInt() const {
+ assert(!streamEof());
+ return std::isalnum(Data[Pos]);
+}
+
+bool Tokenizer::canStartString() const {
+ return willNowRead("\"") || willNowRead("L\"") || willNowRead("l\"");
+}
+
+bool Tokenizer::streamEof() const { return Pos == DataLength; }
+
+Kind Tokenizer::classifyCurrentToken() const {
+ if (canStartBlockComment())
+ return Kind::StartComment;
+ if (canStartLineComment())
+ return Kind::LineComment;
+
+ if (canStartInt())
+ return Kind::Int;
+ if (canStartString())
+ return Kind::String;
+ // BEGIN and END are at this point of lexing recognized as identifiers.
+ if (canStartIdentifier())
+ return Kind::Identifier;
+
+ const char CurChar = Data[Pos];
+
+ switch (CurChar) {
+ // One-character token classification.
+#define TOKEN(Name)
+#define SHORT_TOKEN(Name, Ch) \
+ case Ch: \
+ return Kind::Name;
+#include "ResourceScriptTokenList.def"
+
+ default:
+ return Kind::Invalid;
+ }
+}
+
+void Tokenizer::processIdentifier(RCToken &Token) const {
+ assert(Token.kind() == Kind::Identifier);
+ StringRef Name = Token.value();
+
+ if (Name.equals_insensitive("begin"))
+ Token = RCToken(Kind::BlockBegin, Name);
+ else if (Name.equals_insensitive("end"))
+ Token = RCToken(Kind::BlockEnd, Name);
+}
+
+} // anonymous namespace
+
+namespace llvm {
+
+Expected<std::vector<RCToken>> tokenizeRC(StringRef Input) {
+ return Tokenizer(Input).run();
+}
+
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.h
new file mode 100644
index 00000000000..cc8ca48b490
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.h
@@ -0,0 +1,81 @@
+//===-- ResourceScriptToken.h -----------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This declares the .rc script tokens and defines an interface for tokenizing
+// the input data. The list of available tokens is located at
+// ResourceScriptTokenList.def.
+//
+// Note that the tokenizer does not support preprocessor directives. The
+// preprocessor should do its work on the .rc file before running llvm-rc.
+//
+// As for now, it is possible to parse ASCII files only (the behavior on
+// UTF files might be undefined). However, it already consumes UTF-8 BOM, if
+// there is any. Thus, ASCII-compatible UTF-8 files are tokenized correctly.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTTOKEN_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTTOKEN_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+
+#include <cstdint>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace llvm {
+
+// A definition of a single resource script token. Each token has its kind
+// (declared in ResourceScriptTokenList) and holds a value - a reference
+// representation of the token.
+// RCToken does not claim ownership on its value. A memory buffer containing
+// the token value should be stored in a safe place and cannot be freed
+// nor reallocated.
+class RCToken {
+public:
+ enum class Kind {
+#define TOKEN(Name) Name,
+#define SHORT_TOKEN(Name, Ch) Name,
+#include "ResourceScriptTokenList.def"
+ };
+
+ RCToken(RCToken::Kind RCTokenKind, StringRef Value);
+
+ // Get an integer value of the integer token.
+ uint32_t intValue() const;
+ bool isLongInt() const;
+
+ StringRef value() const;
+ Kind kind() const;
+
+ // Check if a token describes a binary operator.
+ bool isBinaryOp() const;
+
+private:
+ Kind TokenKind;
+ StringRef TokenValue;
+};
+
+// Tokenize Input.
+// In case no error occurred, the return value contains
+// tokens in order they were in the input file.
+// In case of any error, the return value contains
+// a textual representation of error.
+//
+// Tokens returned by this function hold only references to the parts
+// of the Input. Memory buffer containing Input cannot be freed,
+// modified or reallocated.
+Expected<std::vector<RCToken>> tokenizeRC(StringRef Input);
+
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptTokenList.def b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptTokenList.def
new file mode 100644
index 00000000000..a61a96461f0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptTokenList.def
@@ -0,0 +1,39 @@
+//===-- ResourceScriptTokenList.h -------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This is a part of llvm-rc tokenizer. It lists all the possible tokens
+// that might occur in a correct .rc script.
+//
+//===---------------------------------------------------------------------===//
+
+
+// Long tokens. They might consist of more than one character.
+TOKEN(Invalid) // Invalid token. Should not occur in a valid script.
+TOKEN(Int) // Integer (decimal, octal or hexadecimal).
+TOKEN(String) // String value.
+TOKEN(Identifier) // Script identifier (resource name or type).
+TOKEN(LineComment) // Beginning of single-line comment.
+TOKEN(StartComment) // Beginning of multi-line comment.
+
+// Short tokens. They usually consist of exactly one character.
+// The definitions are of the form SHORT_TOKEN(TokenName, TokenChar).
+// TokenChar is the one-character token representation occuring in the correct
+// .rc scripts.
+SHORT_TOKEN(BlockBegin, '{') // Start of the script block; can also be BEGIN.
+SHORT_TOKEN(BlockEnd, '}') // End of the block; can also be END.
+SHORT_TOKEN(Comma, ',') // Comma - resource arguments separator.
+SHORT_TOKEN(Plus, '+') // Addition operator.
+SHORT_TOKEN(Minus, '-') // Subtraction operator.
+SHORT_TOKEN(Pipe, '|') // Bitwise-OR operator.
+SHORT_TOKEN(Amp, '&') // Bitwise-AND operator.
+SHORT_TOKEN(Tilde, '~') // Bitwise-NOT operator.
+SHORT_TOKEN(LeftParen, '(') // Left parenthesis in the script expressions.
+SHORT_TOKEN(RightParen, ')') // Right parenthesis.
+
+#undef TOKEN
+#undef SHORT_TOKEN
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceVisitor.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceVisitor.h
new file mode 100644
index 00000000000..843c8d898a2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceVisitor.h
@@ -0,0 +1,61 @@
+//===-- ResourceVisitor.h ---------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This defines a base class visiting resource script resources.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H
+#define LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H
+
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace rc {
+
+class RCResource;
+
+class CaptionStmt;
+class ClassStmt;
+class CharacteristicsStmt;
+class ExStyleStmt;
+class FontStmt;
+class LanguageResource;
+class StyleStmt;
+class VersionStmt;
+
+class Visitor {
+public:
+ virtual Error visitNullResource(const RCResource *) = 0;
+ virtual Error visitAcceleratorsResource(const RCResource *) = 0;
+ virtual Error visitBitmapResource(const RCResource *) = 0;
+ virtual Error visitCursorResource(const RCResource *) = 0;
+ virtual Error visitDialogResource(const RCResource *) = 0;
+ virtual Error visitHTMLResource(const RCResource *) = 0;
+ virtual Error visitIconResource(const RCResource *) = 0;
+ virtual Error visitMenuResource(const RCResource *) = 0;
+ virtual Error visitStringTableResource(const RCResource *) = 0;
+ virtual Error visitUserDefinedResource(const RCResource *) = 0;
+ virtual Error visitVersionInfoResource(const RCResource *) = 0;
+
+ 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;
+ virtual Error visitVersionStmt(const VersionStmt *) = 0;
+
+ virtual ~Visitor() {}
+};
+
+} // namespace rc
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/WindresOpts.td b/contrib/libs/llvm14/tools/llvm-rc/WindresOpts.td
new file mode 100644
index 00000000000..3c75c85ece0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/WindresOpts.td
@@ -0,0 +1,62 @@
+include "llvm/Option/OptParser.td"
+
+multiclass Long<string name, string help> {
+ def NAME: Separate<["--"], name>;
+ def NAME # _eq: Joined<["--"], name # "=">, Alias<!cast<Separate>(NAME)>,
+ HelpText<help>;
+}
+
+multiclass LongAlias<string name, Option orig> {
+ def NAME: Separate<["--"], name>, Alias<orig>;
+ def NAME # _eq: Joined<["--"], name # "=">, Alias<orig>;
+}
+
+multiclass LongShort<string short, string long, string help> {
+ def NAME: Separate<["--"], long>;
+ def NAME # _eq: Joined<["--"], long # "=">, Alias<!cast<Separate>(NAME)>,
+ HelpText<help>;
+ def NAME # _short: JoinedOrSeparate<["-"], short>, Alias<!cast<Separate>(NAME)>;
+}
+
+multiclass F<string short, string long, string help> {
+ def NAME: Flag<["-"], short>;
+ def NAME # _long: Flag<["--"], long>, Alias<!cast<Flag>(NAME)>,
+ HelpText<help>;
+}
+
+defm input : LongShort<"i", "input", "Input file">;
+
+defm output : LongShort<"o", "output", "Output file">;
+
+defm input_format : LongShort<"J", "input-format", "Input format">;
+
+defm output_format : LongShort<"O", "output-format", "Output format">;
+
+defm preprocessor : Long<"preprocessor", "Custom preprocessor command">;
+defm preprocessor_arg : Long<"preprocessor-arg", "Preprocessor command argument">;
+
+defm target : LongShort<"F", "target", "Target BFD format name">;
+
+defm include_dir : LongShort<"I", "include-dir", "Include directory">;
+defm include_alias : LongAlias<"include", include_dir>;
+
+defm define : LongShort<"D", "define", "Define to pass to the preprocessor">;
+
+defm undef : LongShort<"U", "undefine", "Undefine to pass to the preprocessor">;
+
+defm codepage : LongShort<"c", "codepage", "Default codepage to use">;
+
+defm language : LongShort<"l", "language", "Default language to use (0x0-0xffff)">;
+
+defm verbose : F<"v", "verbose", "Enable verbose output">;
+defm version : F<"V", "version", "Display version">;
+
+defm help : F<"h", "help", "Display this message and exit">;
+
+// Print (but do not run) the commands to run for preprocessing
+def _HASH_HASH_HASH : Flag<["-"], "###">;
+
+def no_preprocess : Flag<["--"], "no-preprocess">;
+
+// Unimplemented options for compatibility
+def use_temp_file: Flag<["--"], "use-temp-file">;
diff --git a/contrib/libs/llvm14/tools/llvm-rc/llvm-rc.cpp b/contrib/libs/llvm14/tools/llvm-rc/llvm-rc.cpp
new file mode 100644
index 00000000000..d43994deecc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/llvm-rc.cpp
@@ -0,0 +1,755 @@
+//===-- llvm-rc.cpp - Compile .rc scripts into .res -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Compile .rc scripts into .res files. This is intended to be a
+// platform-independent port of Microsoft's rc.exe tool.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ResourceFileWriter.h"
+#include "ResourceScriptCppFilter.h"
+#include "ResourceScriptParser.h"
+#include "ResourceScriptStmt.h"
+#include "ResourceScriptToken.h"
+
+#include "llvm/ADT/Triple.h"
+#include "llvm/Object/WindowsResource.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/StringSaver.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+#include <system_error>
+
+using namespace llvm;
+using namespace llvm::rc;
+
+namespace {
+
+// Input options tables.
+
+enum ID {
+ OPT_INVALID = 0, // This is not a correct option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class RcOptTable : public opt::OptTable {
+public:
+ RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {}
+};
+
+enum Windres_ID {
+ WINDRES_INVALID = 0, // This is not a correct option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ WINDRES_##ID,
+#include "WindresOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const WINDRES_##NAME[] = VALUE;
+#include "WindresOpts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info WindresInfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ WINDRES_##PREFIX, NAME, HELPTEXT, \
+ METAVAR, WINDRES_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, WINDRES_##GROUP, \
+ WINDRES_##ALIAS, ALIASARGS, VALUES},
+#include "WindresOpts.inc"
+#undef OPTION
+};
+
+class WindresOptTable : public opt::OptTable {
+public:
+ WindresOptTable() : OptTable(WindresInfoTable, /* IgnoreCase = */ false) {}
+};
+
+static ExitOnError ExitOnErr;
+static FileRemover TempPreprocFile;
+static FileRemover TempResFile;
+
+[[noreturn]] static void fatalError(const Twine &Message) {
+ errs() << Message << "\n";
+ exit(1);
+}
+
+std::string createTempFile(const Twine &Prefix, StringRef Suffix) {
+ std::error_code EC;
+ SmallString<128> FileName;
+ if ((EC = sys::fs::createTemporaryFile(Prefix, Suffix, FileName)))
+ fatalError("Unable to create temp file: " + EC.message());
+ return static_cast<std::string>(FileName);
+}
+
+ErrorOr<std::string> findClang(const char *Argv0) {
+ StringRef Parent = llvm::sys::path::parent_path(Argv0);
+ ErrorOr<std::string> Path = std::error_code();
+ if (!Parent.empty()) {
+ // First look for the tool with all potential names in the specific
+ // directory of Argv0, if known
+ for (const auto *Name : {"clang", "clang-cl"}) {
+ Path = sys::findProgramByName(Name, Parent);
+ if (Path)
+ return Path;
+ }
+ }
+ // If no parent directory known, or not found there, look everywhere in PATH
+ for (const auto *Name : {"clang", "clang-cl"}) {
+ Path = sys::findProgramByName(Name);
+ if (Path)
+ return Path;
+ }
+ return Path;
+}
+
+bool isUsableArch(Triple::ArchType Arch) {
+ switch (Arch) {
+ case Triple::x86:
+ case Triple::x86_64:
+ case Triple::arm:
+ case Triple::thumb:
+ case Triple::aarch64:
+ // These work properly with the clang driver, setting the expected
+ // defines such as _WIN32 etc.
+ return true;
+ default:
+ // Other archs aren't set up for use with windows as target OS, (clang
+ // doesn't define e.g. _WIN32 etc), so with them we need to set a
+ // different default arch.
+ return false;
+ }
+}
+
+Triple::ArchType getDefaultFallbackArch() {
+ return Triple::x86_64;
+}
+
+std::string getClangClTriple() {
+ Triple T(sys::getDefaultTargetTriple());
+ if (!isUsableArch(T.getArch()))
+ T.setArch(getDefaultFallbackArch());
+ T.setOS(Triple::Win32);
+ T.setVendor(Triple::PC);
+ T.setEnvironment(Triple::MSVC);
+ T.setObjectFormat(Triple::COFF);
+ return T.str();
+}
+
+std::string getMingwTriple() {
+ Triple T(sys::getDefaultTargetTriple());
+ if (!isUsableArch(T.getArch()))
+ T.setArch(getDefaultFallbackArch());
+ if (T.isWindowsGNUEnvironment())
+ return T.str();
+ // Write out the literal form of the vendor/env here, instead of
+ // constructing them with enum values (which end up with them in
+ // normalized form). The literal form of the triple can matter for
+ // finding include files.
+ return (Twine(T.getArchName()) + "-w64-mingw32").str();
+}
+
+enum Format { Rc, Res, Coff, Unknown };
+
+struct RcOptions {
+ bool Preprocess = true;
+ bool PrintCmdAndExit = false;
+ std::string Triple;
+ std::vector<std::string> PreprocessCmd;
+ std::vector<std::string> PreprocessArgs;
+
+ std::string InputFile;
+ Format InputFormat = Rc;
+ std::string OutputFile;
+ Format OutputFormat = Res;
+
+ bool BeVerbose = false;
+ WriterParams Params;
+ bool AppendNull = false;
+ bool IsDryRun = false;
+ // Set the default language; choose en-US arbitrarily.
+ unsigned LangId = (/*PrimaryLangId*/ 0x09) | (/*SubLangId*/ 0x01 << 10);
+};
+
+bool preprocess(StringRef Src, StringRef Dst, const RcOptions &Opts,
+ const char *Argv0) {
+ std::string Clang;
+ if (Opts.PrintCmdAndExit) {
+ Clang = "clang";
+ } else {
+ ErrorOr<std::string> ClangOrErr = findClang(Argv0);
+ if (ClangOrErr) {
+ Clang = *ClangOrErr;
+ } else {
+ errs() << "llvm-rc: Unable to find clang, skipping preprocessing."
+ << "\n";
+ errs() << "Pass -no-cpp to disable preprocessing. This will be an error "
+ "in the future."
+ << "\n";
+ return false;
+ }
+ }
+
+ SmallVector<StringRef, 8> Args = {
+ Clang, "--driver-mode=gcc", "-target", Opts.Triple, "-E",
+ "-xc", "-DRC_INVOKED"};
+ if (!Opts.PreprocessCmd.empty()) {
+ Args.clear();
+ for (const auto &S : Opts.PreprocessCmd)
+ Args.push_back(S);
+ }
+ Args.push_back(Src);
+ Args.push_back("-o");
+ Args.push_back(Dst);
+ for (const auto &S : Opts.PreprocessArgs)
+ Args.push_back(S);
+ if (Opts.PrintCmdAndExit || Opts.BeVerbose) {
+ for (const auto &A : Args) {
+ outs() << " ";
+ sys::printArg(outs(), A, Opts.PrintCmdAndExit);
+ }
+ outs() << "\n";
+ if (Opts.PrintCmdAndExit)
+ exit(0);
+ }
+ // The llvm Support classes don't handle reading from stdout of a child
+ // process; otherwise we could avoid using a temp file.
+ int Res = sys::ExecuteAndWait(Clang, Args);
+ if (Res) {
+ fatalError("llvm-rc: Preprocessing failed.");
+ }
+ return true;
+}
+
+static std::pair<bool, std::string> isWindres(llvm::StringRef Argv0) {
+ StringRef ProgName = llvm::sys::path::stem(Argv0);
+ // x86_64-w64-mingw32-windres -> x86_64-w64-mingw32, windres
+ // llvm-rc -> "", llvm-rc
+ // aarch64-w64-mingw32-llvm-windres-10.exe -> aarch64-w64-mingw32, llvm-windres
+ ProgName = ProgName.rtrim("0123456789.-");
+ if (!ProgName.consume_back_insensitive("windres"))
+ return std::make_pair<bool, std::string>(false, "");
+ ProgName.consume_back_insensitive("llvm-");
+ ProgName.consume_back_insensitive("-");
+ return std::make_pair<bool, std::string>(true, ProgName.str());
+}
+
+Format parseFormat(StringRef S) {
+ Format F = StringSwitch<Format>(S.lower())
+ .Case("rc", Rc)
+ .Case("res", Res)
+ .Case("coff", Coff)
+ .Default(Unknown);
+ if (F == Unknown)
+ fatalError("Unable to parse '" + Twine(S) + "' as a format");
+ return F;
+}
+
+void deduceFormat(Format &Dest, StringRef File) {
+ Format F = StringSwitch<Format>(sys::path::extension(File.lower()))
+ .Case(".rc", Rc)
+ .Case(".res", Res)
+ .Case(".o", Coff)
+ .Case(".obj", Coff)
+ .Default(Unknown);
+ if (F != Unknown)
+ Dest = F;
+}
+
+std::string unescape(StringRef S) {
+ std::string Out;
+ Out.reserve(S.size());
+ for (int I = 0, E = S.size(); I < E; I++) {
+ if (S[I] == '\\') {
+ if (I + 1 < E)
+ Out.push_back(S[++I]);
+ else
+ fatalError("Unterminated escape");
+ continue;
+ }
+ Out.push_back(S[I]);
+ }
+ return Out;
+}
+
+std::vector<std::string> unescapeSplit(StringRef S) {
+ std::vector<std::string> OutArgs;
+ std::string Out;
+ bool InQuote = false;
+ for (int I = 0, E = S.size(); I < E; I++) {
+ if (S[I] == '\\') {
+ if (I + 1 < E)
+ Out.push_back(S[++I]);
+ else
+ fatalError("Unterminated escape");
+ continue;
+ }
+ if (S[I] == '"') {
+ InQuote = !InQuote;
+ continue;
+ }
+ if (S[I] == ' ' && !InQuote) {
+ OutArgs.push_back(Out);
+ Out.clear();
+ continue;
+ }
+ Out.push_back(S[I]);
+ }
+ if (InQuote)
+ fatalError("Unterminated quote");
+ if (!Out.empty())
+ OutArgs.push_back(Out);
+ return OutArgs;
+}
+
+RcOptions parseWindresOptions(ArrayRef<const char *> ArgsArr,
+ ArrayRef<const char *> InputArgsArray,
+ std::string Prefix) {
+ WindresOptTable T;
+ RcOptions Opts;
+ unsigned MAI, MAC;
+ opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
+
+ // The tool prints nothing when invoked with no command-line arguments.
+ if (InputArgs.hasArg(WINDRES_help)) {
+ T.printHelp(outs(), "windres [options] file...",
+ "LLVM windres (GNU windres compatible)", false, true);
+ exit(0);
+ }
+
+ if (InputArgs.hasArg(WINDRES_version)) {
+ outs() << "llvm-windres, compatible with GNU windres\n";
+ cl::PrintVersionMessage();
+ exit(0);
+ }
+
+ std::vector<std::string> FileArgs = InputArgs.getAllArgValues(WINDRES_INPUT);
+ FileArgs.insert(FileArgs.end(), InputArgsArray.begin(), InputArgsArray.end());
+
+ if (InputArgs.hasArg(WINDRES_input)) {
+ Opts.InputFile = InputArgs.getLastArgValue(WINDRES_input).str();
+ } else if (!FileArgs.empty()) {
+ Opts.InputFile = FileArgs.front();
+ FileArgs.erase(FileArgs.begin());
+ } else {
+ // TODO: GNU windres takes input on stdin in this case.
+ fatalError("Missing input file");
+ }
+
+ if (InputArgs.hasArg(WINDRES_output)) {
+ Opts.OutputFile = InputArgs.getLastArgValue(WINDRES_output).str();
+ } else if (!FileArgs.empty()) {
+ Opts.OutputFile = FileArgs.front();
+ FileArgs.erase(FileArgs.begin());
+ } else {
+ // TODO: GNU windres writes output in rc form to stdout in this case.
+ fatalError("Missing output file");
+ }
+
+ if (InputArgs.hasArg(WINDRES_input_format)) {
+ Opts.InputFormat =
+ parseFormat(InputArgs.getLastArgValue(WINDRES_input_format));
+ } else {
+ deduceFormat(Opts.InputFormat, Opts.InputFile);
+ }
+ if (Opts.InputFormat == Coff)
+ fatalError("Unsupported input format");
+
+ if (InputArgs.hasArg(WINDRES_output_format)) {
+ Opts.OutputFormat =
+ parseFormat(InputArgs.getLastArgValue(WINDRES_output_format));
+ } else {
+ // The default in windres differs from the default in RcOptions
+ Opts.OutputFormat = Coff;
+ deduceFormat(Opts.OutputFormat, Opts.OutputFile);
+ }
+ if (Opts.OutputFormat == Rc)
+ fatalError("Unsupported output format");
+ if (Opts.InputFormat == Opts.OutputFormat) {
+ outs() << "Nothing to do.\n";
+ exit(0);
+ }
+
+ Opts.PrintCmdAndExit = InputArgs.hasArg(WINDRES__HASH_HASH_HASH);
+ Opts.Preprocess = !InputArgs.hasArg(WINDRES_no_preprocess);
+ Triple TT(Prefix);
+ if (InputArgs.hasArg(WINDRES_target)) {
+ StringRef Value = InputArgs.getLastArgValue(WINDRES_target);
+ if (Value == "pe-i386")
+ Opts.Triple = "i686-w64-mingw32";
+ else if (Value == "pe-x86-64")
+ Opts.Triple = "x86_64-w64-mingw32";
+ else
+ // Implicit extension; if the --target value isn't one of the known
+ // BFD targets, allow setting the full triple string via this instead.
+ Opts.Triple = Value.str();
+ } else if (TT.getArch() != Triple::UnknownArch)
+ Opts.Triple = Prefix;
+ else
+ Opts.Triple = getMingwTriple();
+
+ for (const auto *Arg :
+ InputArgs.filtered(WINDRES_include_dir, WINDRES_define, WINDRES_undef,
+ WINDRES_preprocessor_arg)) {
+ // GNU windres passes the arguments almost as-is on to popen() (it only
+ // backslash escapes spaces in the arguments), where a shell would
+ // unescape backslash escapes for quotes and similar. This means that
+ // when calling GNU windres, callers need to double escape chars like
+ // quotes, e.g. as -DSTRING=\\\"1.2.3\\\".
+ //
+ // Exactly how the arguments are interpreted depends on the platform
+ // though - but the cases where this matters (where callers would have
+ // done this double escaping) probably is confined to cases like these
+ // quoted string defines, and those happen to work the same across unix
+ // and windows.
+ std::string Unescaped = unescape(Arg->getValue());
+ switch (Arg->getOption().getID()) {
+ case WINDRES_include_dir:
+ // Technically, these are handled the same way as e.g. defines, but
+ // the way we consistently unescape the unix way breaks windows paths
+ // with single backslashes. Alternatively, our unescape function would
+ // need to mimic the platform specific command line parsing/unescaping
+ // logic.
+ Opts.Params.Include.push_back(Arg->getValue());
+ Opts.PreprocessArgs.push_back("-I");
+ Opts.PreprocessArgs.push_back(Arg->getValue());
+ break;
+ case WINDRES_define:
+ Opts.PreprocessArgs.push_back("-D");
+ Opts.PreprocessArgs.push_back(Unescaped);
+ break;
+ case WINDRES_undef:
+ Opts.PreprocessArgs.push_back("-U");
+ Opts.PreprocessArgs.push_back(Unescaped);
+ break;
+ case WINDRES_preprocessor_arg:
+ Opts.PreprocessArgs.push_back(Unescaped);
+ break;
+ }
+ }
+ if (InputArgs.hasArg(WINDRES_preprocessor))
+ Opts.PreprocessCmd =
+ unescapeSplit(InputArgs.getLastArgValue(WINDRES_preprocessor));
+
+ Opts.Params.CodePage = CpWin1252; // Different default
+ if (InputArgs.hasArg(WINDRES_codepage)) {
+ if (InputArgs.getLastArgValue(WINDRES_codepage)
+ .getAsInteger(0, Opts.Params.CodePage))
+ fatalError("Invalid code page: " +
+ InputArgs.getLastArgValue(WINDRES_codepage));
+ }
+ if (InputArgs.hasArg(WINDRES_language)) {
+ StringRef Val = InputArgs.getLastArgValue(WINDRES_language);
+ Val.consume_front_insensitive("0x");
+ if (Val.getAsInteger(16, Opts.LangId))
+ fatalError("Invalid language id: " +
+ InputArgs.getLastArgValue(WINDRES_language));
+ }
+
+ Opts.BeVerbose = InputArgs.hasArg(WINDRES_verbose);
+
+ return Opts;
+}
+
+RcOptions parseRcOptions(ArrayRef<const char *> ArgsArr,
+ ArrayRef<const char *> InputArgsArray) {
+ RcOptTable T;
+ RcOptions Opts;
+ unsigned MAI, MAC;
+ 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 [options] file...", "Resource Converter", false);
+ exit(0);
+ }
+
+ std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT);
+ InArgsInfo.insert(InArgsInfo.end(), InputArgsArray.begin(),
+ InputArgsArray.end());
+ if (InArgsInfo.size() != 1) {
+ fatalError("Exactly one input file should be provided.");
+ }
+
+ Opts.PrintCmdAndExit = InputArgs.hasArg(OPT__HASH_HASH_HASH);
+ Opts.Triple = getClangClTriple();
+ for (const auto *Arg :
+ InputArgs.filtered(OPT_includepath, OPT_define, OPT_undef)) {
+ switch (Arg->getOption().getID()) {
+ case OPT_includepath:
+ Opts.PreprocessArgs.push_back("-I");
+ break;
+ case OPT_define:
+ Opts.PreprocessArgs.push_back("-D");
+ break;
+ case OPT_undef:
+ Opts.PreprocessArgs.push_back("-U");
+ break;
+ }
+ Opts.PreprocessArgs.push_back(Arg->getValue());
+ }
+
+ Opts.InputFile = InArgsInfo[0];
+ Opts.BeVerbose = InputArgs.hasArg(OPT_verbose);
+ Opts.Preprocess = !InputArgs.hasArg(OPT_no_preprocess);
+ Opts.Params.Include = InputArgs.getAllArgValues(OPT_includepath);
+ Opts.Params.NoInclude = InputArgs.hasArg(OPT_noinclude);
+ if (Opts.Params.NoInclude) {
+ // Clear the INLCUDE variable for the external preprocessor
+#ifdef _WIN32
+ ::_putenv("INCLUDE=");
+#else
+ ::unsetenv("INCLUDE");
+#endif
+ }
+ if (InputArgs.hasArg(OPT_codepage)) {
+ if (InputArgs.getLastArgValue(OPT_codepage)
+ .getAsInteger(10, Opts.Params.CodePage))
+ fatalError("Invalid code page: " +
+ InputArgs.getLastArgValue(OPT_codepage));
+ }
+ Opts.IsDryRun = InputArgs.hasArg(OPT_dry_run);
+ auto OutArgsInfo = InputArgs.getAllArgValues(OPT_fileout);
+ if (OutArgsInfo.empty()) {
+ SmallString<128> OutputFile(Opts.InputFile);
+ llvm::sys::fs::make_absolute(OutputFile);
+ llvm::sys::path::replace_extension(OutputFile, "res");
+ OutArgsInfo.push_back(std::string(OutputFile.str()));
+ }
+ if (!Opts.IsDryRun) {
+ if (OutArgsInfo.size() != 1)
+ fatalError(
+ "No more than one output file should be provided (using /FO flag).");
+ Opts.OutputFile = OutArgsInfo[0];
+ }
+ Opts.AppendNull = InputArgs.hasArg(OPT_add_null);
+ if (InputArgs.hasArg(OPT_lang_id)) {
+ StringRef Val = InputArgs.getLastArgValue(OPT_lang_id);
+ Val.consume_front_insensitive("0x");
+ if (Val.getAsInteger(16, Opts.LangId))
+ fatalError("Invalid language id: " +
+ InputArgs.getLastArgValue(OPT_lang_id));
+ }
+ return Opts;
+}
+
+RcOptions getOptions(const char *Argv0, ArrayRef<const char *> ArgsArr,
+ ArrayRef<const char *> InputArgs) {
+ std::string Prefix;
+ bool IsWindres;
+ std::tie(IsWindres, Prefix) = isWindres(Argv0);
+ if (IsWindres)
+ return parseWindresOptions(ArgsArr, InputArgs, Prefix);
+ else
+ return parseRcOptions(ArgsArr, InputArgs);
+}
+
+void doRc(std::string Src, std::string Dest, RcOptions &Opts,
+ const char *Argv0) {
+ std::string PreprocessedFile = Src;
+ if (Opts.Preprocess) {
+ std::string OutFile = createTempFile("preproc", "rc");
+ TempPreprocFile.setFile(OutFile);
+ if (preprocess(Src, OutFile, Opts, Argv0))
+ PreprocessedFile = OutFile;
+ }
+
+ // Read and tokenize the input file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> File =
+ MemoryBuffer::getFile(PreprocessedFile);
+ if (!File) {
+ fatalError("Error opening file '" + Twine(PreprocessedFile) +
+ "': " + File.getError().message());
+ }
+
+ std::unique_ptr<MemoryBuffer> FileContents = std::move(*File);
+ StringRef Contents = FileContents->getBuffer();
+
+ std::string FilteredContents = filterCppOutput(Contents);
+ std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(FilteredContents));
+
+ if (Opts.BeVerbose) {
+ const Twine TokenNames[] = {
+#define TOKEN(Name) #Name,
+#define SHORT_TOKEN(Name, Ch) #Name,
+#include "ResourceScriptTokenList.def"
+ };
+
+ for (const RCToken &Token : Tokens) {
+ outs() << TokenNames[static_cast<int>(Token.kind())] << ": "
+ << Token.value();
+ if (Token.kind() == RCToken::Kind::Int)
+ outs() << "; int value = " << Token.intValue();
+
+ outs() << "\n";
+ }
+ }
+
+ WriterParams &Params = Opts.Params;
+ SmallString<128> InputFile(Src);
+ llvm::sys::fs::make_absolute(InputFile);
+ Params.InputFilePath = InputFile;
+
+ switch (Params.CodePage) {
+ case CpAcp:
+ case CpWin1252:
+ case CpUtf8:
+ break;
+ default:
+ fatalError("Unsupported code page, only 0, 1252 and 65001 are supported!");
+ }
+
+ std::unique_ptr<ResourceFileWriter> Visitor;
+
+ if (!Opts.IsDryRun) {
+ std::error_code EC;
+ auto FOut = std::make_unique<raw_fd_ostream>(
+ Dest, EC, sys::fs::FA_Read | sys::fs::FA_Write);
+ if (EC)
+ fatalError("Error opening output file '" + Dest + "': " + EC.message());
+ Visitor = std::make_unique<ResourceFileWriter>(Params, std::move(FOut));
+ Visitor->AppendNull = Opts.AppendNull;
+
+ ExitOnErr(NullResource().visit(Visitor.get()));
+
+ unsigned PrimaryLangId = Opts.LangId & 0x3ff;
+ unsigned SubLangId = Opts.LangId >> 10;
+ ExitOnErr(LanguageResource(PrimaryLangId, SubLangId).visit(Visitor.get()));
+ }
+
+ rc::RCParser Parser{std::move(Tokens)};
+ while (!Parser.isEof()) {
+ auto Resource = ExitOnErr(Parser.parseSingleResource());
+ if (Opts.BeVerbose)
+ Resource->log(outs());
+ if (!Opts.IsDryRun)
+ ExitOnErr(Resource->visit(Visitor.get()));
+ }
+
+ // STRINGTABLE resources come at the very end.
+ if (!Opts.IsDryRun)
+ ExitOnErr(Visitor->dumpAllStringTables());
+}
+
+void doCvtres(std::string Src, std::string Dest, std::string TargetTriple) {
+ object::WindowsResourceParser Parser;
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+ MemoryBuffer::getFile(Src);
+ if (!BufferOrErr)
+ fatalError("Error opening file '" + Twine(Src) +
+ "': " + BufferOrErr.getError().message());
+ std::unique_ptr<MemoryBuffer> &Buffer = BufferOrErr.get();
+ std::unique_ptr<object::WindowsResource> Binary =
+ ExitOnErr(object::WindowsResource::createWindowsResource(
+ Buffer->getMemBufferRef()));
+
+ std::vector<std::string> Duplicates;
+ ExitOnErr(Parser.parse(Binary.get(), Duplicates));
+ for (const auto &DupeDiag : Duplicates)
+ fatalError("Duplicate resources: " + DupeDiag);
+
+ Triple T(TargetTriple);
+ COFF::MachineTypes MachineType;
+ switch (T.getArch()) {
+ case Triple::x86:
+ MachineType = COFF::IMAGE_FILE_MACHINE_I386;
+ break;
+ case Triple::x86_64:
+ MachineType = COFF::IMAGE_FILE_MACHINE_AMD64;
+ break;
+ case Triple::arm:
+ case Triple::thumb:
+ MachineType = COFF::IMAGE_FILE_MACHINE_ARMNT;
+ break;
+ case Triple::aarch64:
+ MachineType = COFF::IMAGE_FILE_MACHINE_ARM64;
+ break;
+ default:
+ fatalError("Unsupported architecture in target '" + Twine(TargetTriple) +
+ "'");
+ }
+
+ std::unique_ptr<MemoryBuffer> OutputBuffer =
+ ExitOnErr(object::writeWindowsResourceCOFF(MachineType, Parser,
+ /*DateTimeStamp*/ 0));
+ std::unique_ptr<FileOutputBuffer> FileBuffer =
+ ExitOnErr(FileOutputBuffer::create(Dest, OutputBuffer->getBufferSize()));
+ std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(),
+ FileBuffer->getBufferStart());
+ ExitOnErr(FileBuffer->commit());
+}
+
+} // anonymous namespace
+
+int main(int Argc, const char **Argv) {
+ InitLLVM X(Argc, Argv);
+ ExitOnErr.setBanner("llvm-rc: ");
+
+ const char **DashDash = std::find_if(
+ Argv + 1, Argv + Argc, [](StringRef Str) { return Str == "--"; });
+ ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, DashDash);
+ ArrayRef<const char *> FileArgsArr;
+ if (DashDash != Argv + Argc)
+ FileArgsArr = makeArrayRef(DashDash + 1, Argv + Argc);
+
+ RcOptions Opts = getOptions(Argv[0], ArgsArr, FileArgsArr);
+
+ std::string ResFile = Opts.OutputFile;
+ if (Opts.InputFormat == Rc) {
+ if (Opts.OutputFormat == Coff) {
+ ResFile = createTempFile("rc", "res");
+ TempResFile.setFile(ResFile);
+ }
+ doRc(Opts.InputFile, ResFile, Opts, Argv[0]);
+ } else {
+ ResFile = Opts.InputFile;
+ }
+ if (Opts.OutputFormat == Coff) {
+ doCvtres(ResFile, Opts.OutputFile, Opts.Triple);
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ya.make b/contrib/libs/llvm14/tools/llvm-rc/ya.make
new file mode 100644
index 00000000000..6b75c5a0c1a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ya.make
@@ -0,0 +1,44 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-rc
+ contrib/libs/llvm14/tools/llvm-rc
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ ResourceFileWriter.cpp
+ ResourceScriptCppFilter.cpp
+ ResourceScriptParser.cpp
+ ResourceScriptStmt.cpp
+ ResourceScriptToken.cpp
+ llvm-rc.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/ARMEHABIPrinter.h b/contrib/libs/llvm14/tools/llvm-readobj/ARMEHABIPrinter.h
new file mode 100644
index 00000000000..d641b172eb9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/ARMEHABIPrinter.h
@@ -0,0 +1,647 @@
+//===--- ARMEHABIPrinter.h - ARM EHABI Unwind Information Printer ----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_READOBJ_ARMEHABIPRINTER_H
+#define LLVM_TOOLS_LLVM_READOBJ_ARMEHABIPRINTER_H
+
+#include "llvm-readobj.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/Support/ARMEHABI.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/type_traits.h"
+
+namespace llvm {
+namespace ARM {
+namespace EHABI {
+
+class OpcodeDecoder {
+ ScopedPrinter &SW;
+ raw_ostream &OS;
+
+ struct RingEntry {
+ uint8_t Mask;
+ uint8_t Value;
+ void (OpcodeDecoder::*Routine)(const uint8_t *Opcodes, unsigned &OI);
+ };
+ static ArrayRef<RingEntry> ring();
+
+ void Decode_00xxxxxx(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_01xxxxxx(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_1000iiii_iiiiiiii(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_10011101(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_10011111(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_1001nnnn(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_10100nnn(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_10101nnn(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_10110000(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_10110001_0000iiii(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_10110010_uleb128(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_10110011_sssscccc(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_101101nn(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_10111nnn(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_11000110_sssscccc(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_11000111_0000iiii(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_11001000_sssscccc(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_11001001_sssscccc(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_11001yyy(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_11000nnn(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_11010nnn(const uint8_t *Opcodes, unsigned &OI);
+ void Decode_11xxxyyy(const uint8_t *Opcodes, unsigned &OI);
+
+ void PrintGPR(uint16_t GPRMask);
+ void PrintRegisters(uint32_t Mask, StringRef Prefix);
+
+public:
+ OpcodeDecoder(ScopedPrinter &SW) : SW(SW), OS(SW.getOStream()) {}
+ void Decode(const uint8_t *Opcodes, off_t Offset, size_t Length);
+};
+
+inline ArrayRef<OpcodeDecoder::RingEntry> OpcodeDecoder::ring() {
+ static const OpcodeDecoder::RingEntry Ring[] = {
+ {0xc0, 0x00, &OpcodeDecoder::Decode_00xxxxxx},
+ {0xc0, 0x40, &OpcodeDecoder::Decode_01xxxxxx},
+ {0xf0, 0x80, &OpcodeDecoder::Decode_1000iiii_iiiiiiii},
+ {0xff, 0x9d, &OpcodeDecoder::Decode_10011101},
+ {0xff, 0x9f, &OpcodeDecoder::Decode_10011111},
+ {0xf0, 0x90, &OpcodeDecoder::Decode_1001nnnn},
+ {0xf8, 0xa0, &OpcodeDecoder::Decode_10100nnn},
+ {0xf8, 0xa8, &OpcodeDecoder::Decode_10101nnn},
+ {0xff, 0xb0, &OpcodeDecoder::Decode_10110000},
+ {0xff, 0xb1, &OpcodeDecoder::Decode_10110001_0000iiii},
+ {0xff, 0xb2, &OpcodeDecoder::Decode_10110010_uleb128},
+ {0xff, 0xb3, &OpcodeDecoder::Decode_10110011_sssscccc},
+ {0xfc, 0xb4, &OpcodeDecoder::Decode_101101nn},
+ {0xf8, 0xb8, &OpcodeDecoder::Decode_10111nnn},
+ {0xff, 0xc6, &OpcodeDecoder::Decode_11000110_sssscccc},
+ {0xff, 0xc7, &OpcodeDecoder::Decode_11000111_0000iiii},
+ {0xff, 0xc8, &OpcodeDecoder::Decode_11001000_sssscccc},
+ {0xff, 0xc9, &OpcodeDecoder::Decode_11001001_sssscccc},
+ {0xc8, 0xc8, &OpcodeDecoder::Decode_11001yyy},
+ {0xf8, 0xc0, &OpcodeDecoder::Decode_11000nnn},
+ {0xf8, 0xd0, &OpcodeDecoder::Decode_11010nnn},
+ {0xc0, 0xc0, &OpcodeDecoder::Decode_11xxxyyy},
+ };
+ return makeArrayRef(Ring);
+}
+
+inline void OpcodeDecoder::Decode_00xxxxxx(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; vsp = vsp + %u\n", Opcode,
+ ((Opcode & 0x3f) << 2) + 4);
+}
+inline void OpcodeDecoder::Decode_01xxxxxx(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; vsp = vsp - %u\n", Opcode,
+ ((Opcode & 0x3f) << 2) + 4);
+}
+inline void OpcodeDecoder::Decode_1000iiii_iiiiiiii(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+ uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+
+ uint16_t GPRMask = (Opcode1 << 4) | ((Opcode0 & 0x0f) << 12);
+ SW.startLine()
+ << format("0x%02X 0x%02X ; %s",
+ Opcode0, Opcode1, GPRMask ? "pop " : "refuse to unwind");
+ if (GPRMask)
+ PrintGPR(GPRMask);
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_10011101(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; reserved (ARM MOVrr)\n", Opcode);
+}
+inline void OpcodeDecoder::Decode_10011111(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; reserved (WiMMX MOVrr)\n", Opcode);
+}
+inline void OpcodeDecoder::Decode_1001nnnn(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; vsp = r%u\n", Opcode, (Opcode & 0x0f));
+}
+inline void OpcodeDecoder::Decode_10100nnn(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; pop ", Opcode);
+ PrintGPR((((1 << ((Opcode & 0x7) + 1)) - 1) << 4));
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_10101nnn(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; pop ", Opcode);
+ PrintGPR((((1 << ((Opcode & 0x7) + 1)) - 1) << 4) | (1 << 14));
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_10110000(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; finish\n", Opcode);
+}
+inline void OpcodeDecoder::Decode_10110001_0000iiii(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+ uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+
+ SW.startLine() << format("0x%02X 0x%02X ; %s", Opcode0, Opcode1,
+ (Opcode1 & 0xf0) ? "spare" : "pop ");
+ if (((Opcode1 & 0xf0) == 0x00) && Opcode1)
+ PrintGPR((Opcode1 & 0x0f));
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_10110010_uleb128(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ", Opcode);
+
+ SmallVector<uint8_t, 4> ULEB;
+ do { ULEB.push_back(Opcodes[OI ^ 3]); } while (Opcodes[OI++ ^ 3] & 0x80);
+
+ for (unsigned BI = 0, BE = ULEB.size(); BI != BE; ++BI)
+ OS << format("0x%02X ", ULEB[BI]);
+
+ uint64_t Value = 0;
+ for (unsigned BI = 0, BE = ULEB.size(); BI != BE; ++BI)
+ Value = Value | ((ULEB[BI] & 0x7f) << (7 * BI));
+
+ OS << format("; vsp = vsp + %" PRIu64 "\n", 0x204 + (Value << 2));
+}
+inline void OpcodeDecoder::Decode_10110011_sssscccc(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+ uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1);
+ uint8_t Start = ((Opcode1 & 0xf0) >> 4);
+ uint8_t Count = ((Opcode1 & 0x0f) >> 0);
+ PrintRegisters((((1 << (Count + 1)) - 1) << Start), "d");
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_101101nn(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; %s\n", Opcode,
+ (Opcode == 0xb4) ? "pop ra_auth_code" : "spare");
+}
+inline void OpcodeDecoder::Decode_10111nnn(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; pop ", Opcode);
+ PrintRegisters((((1 << ((Opcode & 0x07) + 1)) - 1) << 8), "d");
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_11000110_sssscccc(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+ uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1);
+ uint8_t Start = ((Opcode1 & 0xf0) >> 4);
+ uint8_t Count = ((Opcode1 & 0x0f) >> 0);
+ PrintRegisters((((1 << (Count + 1)) - 1) << Start), "wR");
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_11000111_0000iiii(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+ uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+ SW.startLine()
+ << format("0x%02X 0x%02X ; %s", Opcode0, Opcode1,
+ ((Opcode1 & 0xf0) || Opcode1 == 0x00) ? "spare" : "pop ");
+ if ((Opcode1 & 0xf0) == 0x00 && Opcode1)
+ PrintRegisters(Opcode1 & 0x0f, "wCGR");
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_11001000_sssscccc(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+ uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1);
+ uint8_t Start = 16 + ((Opcode1 & 0xf0) >> 4);
+ uint8_t Count = ((Opcode1 & 0x0f) >> 0);
+ PrintRegisters((((1 << (Count + 1)) - 1) << Start), "d");
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_11001001_sssscccc(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+ uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1);
+ uint8_t Start = ((Opcode1 & 0xf0) >> 4);
+ uint8_t Count = ((Opcode1 & 0x0f) >> 0);
+ PrintRegisters((((1 << (Count + 1)) - 1) << Start), "d");
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_11001yyy(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; spare\n", Opcode);
+}
+inline void OpcodeDecoder::Decode_11000nnn(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; pop ", Opcode);
+ PrintRegisters((((1 << ((Opcode & 0x07) + 1)) - 1) << 10), "wR");
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_11010nnn(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; pop ", Opcode);
+ PrintRegisters((((1 << ((Opcode & 0x07) + 1)) - 1) << 8), "d");
+ OS << '\n';
+}
+inline void OpcodeDecoder::Decode_11xxxyyy(const uint8_t *Opcodes,
+ unsigned &OI) {
+ uint8_t Opcode = Opcodes[OI++ ^ 3];
+ SW.startLine() << format("0x%02X ; spare\n", Opcode);
+}
+
+inline void OpcodeDecoder::PrintGPR(uint16_t GPRMask) {
+ static const char *GPRRegisterNames[16] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
+ "fp", "ip", "sp", "lr", "pc"
+ };
+
+ OS << '{';
+ bool Comma = false;
+ for (unsigned RI = 0, RE = 17; RI < RE; ++RI) {
+ if (GPRMask & (1 << RI)) {
+ if (Comma)
+ OS << ", ";
+ OS << GPRRegisterNames[RI];
+ Comma = true;
+ }
+ }
+ OS << '}';
+}
+
+inline void OpcodeDecoder::PrintRegisters(uint32_t VFPMask, StringRef Prefix) {
+ OS << '{';
+ bool Comma = false;
+ for (unsigned RI = 0, RE = 32; RI < RE; ++RI) {
+ if (VFPMask & (1 << RI)) {
+ if (Comma)
+ OS << ", ";
+ OS << Prefix << RI;
+ Comma = true;
+ }
+ }
+ OS << '}';
+}
+
+inline void OpcodeDecoder::Decode(const uint8_t *Opcodes, off_t Offset,
+ size_t Length) {
+ for (unsigned OCI = Offset; OCI < Length + Offset; ) {
+ bool Decoded = false;
+ for (const auto &RE : ring()) {
+ if ((Opcodes[OCI ^ 3] & RE.Mask) == RE.Value) {
+ (this->*RE.Routine)(Opcodes, OCI);
+ Decoded = true;
+ break;
+ }
+ }
+ if (!Decoded)
+ SW.startLine() << format("0x%02X ; reserved\n", Opcodes[OCI++ ^ 3]);
+ }
+}
+
+template <typename ET>
+class PrinterContext {
+ typedef typename ET::Sym Elf_Sym;
+ typedef typename ET::Shdr Elf_Shdr;
+ typedef typename ET::Rel Elf_Rel;
+ typedef typename ET::Word Elf_Word;
+
+ ScopedPrinter &SW;
+ const object::ELFFile<ET> &ELF;
+ StringRef FileName;
+ const Elf_Shdr *Symtab;
+ ArrayRef<Elf_Word> ShndxTable;
+
+ static const size_t IndexTableEntrySize;
+
+ static uint64_t PREL31(uint32_t Address, uint32_t Place) {
+ uint64_t Location = Address & 0x7fffffff;
+ if (Location & 0x40000000)
+ Location |= (uint64_t) ~0x7fffffff;
+ return Location + Place;
+ }
+
+ ErrorOr<StringRef> FunctionAtAddress(uint64_t Address,
+ Optional<unsigned> SectionIndex) const;
+ const Elf_Shdr *FindExceptionTable(unsigned IndexTableIndex,
+ off_t IndexTableOffset) const;
+
+ void PrintIndexTable(unsigned SectionIndex, const Elf_Shdr *IT) const;
+ void PrintExceptionTable(const Elf_Shdr &EHT,
+ uint64_t TableEntryOffset) const;
+ void PrintOpcodes(const uint8_t *Entry, size_t Length, off_t Offset) const;
+
+public:
+ PrinterContext(ScopedPrinter &SW, const object::ELFFile<ET> &ELF,
+ StringRef FileName, const Elf_Shdr *Symtab)
+ : SW(SW), ELF(ELF), FileName(FileName), Symtab(Symtab) {}
+
+ void PrintUnwindInformation() const;
+};
+
+template <typename ET>
+const size_t PrinterContext<ET>::IndexTableEntrySize = 8;
+
+template <typename ET>
+ErrorOr<StringRef>
+PrinterContext<ET>::FunctionAtAddress(uint64_t Address,
+ Optional<unsigned> SectionIndex) const {
+ if (!Symtab)
+ return inconvertibleErrorCode();
+ auto StrTableOrErr = ELF.getStringTableForSymtab(*Symtab);
+ if (!StrTableOrErr)
+ reportError(StrTableOrErr.takeError(), FileName);
+ StringRef StrTable = *StrTableOrErr;
+
+ for (const Elf_Sym &Sym : unwrapOrError(FileName, ELF.symbols(Symtab))) {
+ if (SectionIndex && *SectionIndex != Sym.st_shndx)
+ continue;
+
+ if (Sym.st_value == Address && Sym.getType() == ELF::STT_FUNC) {
+ auto NameOrErr = Sym.getName(StrTable);
+ if (!NameOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(NameOrErr.takeError());
+ return inconvertibleErrorCode();
+ }
+ return *NameOrErr;
+ }
+ }
+
+ return inconvertibleErrorCode();
+}
+
+template <typename ET>
+const typename ET::Shdr *
+PrinterContext<ET>::FindExceptionTable(unsigned IndexSectionIndex,
+ off_t IndexTableOffset) const {
+ /// Iterate through the sections, searching for the relocation section
+ /// associated with the unwind index table section specified by
+ /// IndexSectionIndex. Iterate the associated section searching for the
+ /// relocation associated with the index table entry specified by
+ /// IndexTableOffset. The symbol is the section symbol for the exception
+ /// handling table. Use this symbol to recover the actual exception handling
+ /// table.
+
+ for (const Elf_Shdr &Sec : unwrapOrError(FileName, ELF.sections())) {
+ if (Sec.sh_type != ELF::SHT_REL || Sec.sh_info != IndexSectionIndex)
+ continue;
+
+ auto SymTabOrErr = ELF.getSection(Sec.sh_link);
+ if (!SymTabOrErr)
+ reportError(SymTabOrErr.takeError(), FileName);
+ const Elf_Shdr *SymTab = *SymTabOrErr;
+
+ for (const Elf_Rel &R : unwrapOrError(FileName, ELF.rels(Sec))) {
+ if (R.r_offset != static_cast<unsigned>(IndexTableOffset))
+ continue;
+
+ typename ET::Rela RelA;
+ RelA.r_offset = R.r_offset;
+ RelA.r_info = R.r_info;
+ RelA.r_addend = 0;
+
+ const Elf_Sym *Symbol =
+ unwrapOrError(FileName, ELF.getRelocationSymbol(RelA, SymTab));
+
+ auto Ret = ELF.getSection(*Symbol, SymTab, ShndxTable);
+ if (!Ret)
+ report_fatal_error(Twine(errorToErrorCode(Ret.takeError()).message()));
+ return *Ret;
+ }
+ }
+ return nullptr;
+}
+
+template <typename ET>
+static const typename ET::Shdr *
+findSectionContainingAddress(const object::ELFFile<ET> &Obj, StringRef FileName,
+ uint64_t Address) {
+ for (const typename ET::Shdr &Sec : unwrapOrError(FileName, Obj.sections()))
+ if (Address >= Sec.sh_addr && Address < Sec.sh_addr + Sec.sh_size)
+ return &Sec;
+ return nullptr;
+}
+
+template <typename ET>
+void PrinterContext<ET>::PrintExceptionTable(const Elf_Shdr &EHT,
+ uint64_t TableEntryOffset) const {
+ // TODO: handle failure.
+ Expected<ArrayRef<uint8_t>> Contents = ELF.getSectionContents(EHT);
+ if (!Contents)
+ return;
+
+ /// ARM EHABI Section 6.2 - The generic model
+ ///
+ /// An exception-handling table entry for the generic model is laid out as:
+ ///
+ /// 3 3
+ /// 1 0 0
+ /// +-+------------------------------+
+ /// |0| personality routine offset |
+ /// +-+------------------------------+
+ /// | personality routine data ... |
+ ///
+ ///
+ /// ARM EHABI Section 6.3 - The ARM-defined compact model
+ ///
+ /// An exception-handling table entry for the compact model looks like:
+ ///
+ /// 3 3 2 2 2 2
+ /// 1 0 8 7 4 3 0
+ /// +-+---+----+-----------------------+
+ /// |1| 0 | Ix | data for pers routine |
+ /// +-+---+----+-----------------------+
+ /// | more personality routine data |
+
+ const support::ulittle32_t Word =
+ *reinterpret_cast<const support::ulittle32_t *>(Contents->data() + TableEntryOffset);
+
+ if (Word & 0x80000000) {
+ SW.printString("Model", StringRef("Compact"));
+
+ unsigned PersonalityIndex = (Word & 0x0f000000) >> 24;
+ SW.printNumber("PersonalityIndex", PersonalityIndex);
+
+ switch (PersonalityIndex) {
+ case AEABI_UNWIND_CPP_PR0:
+ PrintOpcodes(Contents->data() + TableEntryOffset, 3, 1);
+ break;
+ case AEABI_UNWIND_CPP_PR1:
+ case AEABI_UNWIND_CPP_PR2:
+ unsigned AdditionalWords = (Word & 0x00ff0000) >> 16;
+ PrintOpcodes(Contents->data() + TableEntryOffset, 2 + 4 * AdditionalWords,
+ 2);
+ break;
+ }
+ } else {
+ SW.printString("Model", StringRef("Generic"));
+ const bool IsRelocatable = ELF.getHeader().e_type == ELF::ET_REL;
+ uint64_t Address = IsRelocatable
+ ? PREL31(Word, EHT.sh_addr)
+ : PREL31(Word, EHT.sh_addr + TableEntryOffset);
+ SW.printHex("PersonalityRoutineAddress", Address);
+ Optional<unsigned> SecIndex =
+ IsRelocatable ? Optional<unsigned>(EHT.sh_link) : None;
+ if (ErrorOr<StringRef> Name = FunctionAtAddress(Address, SecIndex))
+ SW.printString("PersonalityRoutineName", *Name);
+ }
+}
+
+template <typename ET>
+void PrinterContext<ET>::PrintOpcodes(const uint8_t *Entry,
+ size_t Length, off_t Offset) const {
+ ListScope OCC(SW, "Opcodes");
+ OpcodeDecoder(SW).Decode(Entry, Offset, Length);
+}
+
+template <typename ET>
+void PrinterContext<ET>::PrintIndexTable(unsigned SectionIndex,
+ const Elf_Shdr *IT) const {
+ // TODO: handle failure.
+ Expected<ArrayRef<uint8_t>> Contents = ELF.getSectionContents(*IT);
+ if (!Contents)
+ return;
+
+ /// ARM EHABI Section 5 - Index Table Entries
+ /// * The first word contains a PREL31 offset to the start of a function with
+ /// bit 31 clear
+ /// * The second word contains one of:
+ /// - The PREL31 offset of the start of the table entry for the function,
+ /// with bit 31 clear
+ /// - The exception-handling table entry itself with bit 31 set
+ /// - The special bit pattern EXIDX_CANTUNWIND, indicating that associated
+ /// frames cannot be unwound
+
+ const support::ulittle32_t *Data =
+ reinterpret_cast<const support::ulittle32_t *>(Contents->data());
+ const unsigned Entries = IT->sh_size / IndexTableEntrySize;
+ const bool IsRelocatable = ELF.getHeader().e_type == ELF::ET_REL;
+
+ ListScope E(SW, "Entries");
+ for (unsigned Entry = 0; Entry < Entries; ++Entry) {
+ DictScope E(SW, "Entry");
+
+ const support::ulittle32_t Word0 =
+ Data[Entry * (IndexTableEntrySize / sizeof(*Data)) + 0];
+ const support::ulittle32_t Word1 =
+ Data[Entry * (IndexTableEntrySize / sizeof(*Data)) + 1];
+
+ if (Word0 & 0x80000000) {
+ errs() << "corrupt unwind data in section " << SectionIndex << "\n";
+ continue;
+ }
+
+ // FIXME: For a relocatable object ideally we might want to:
+ // 1) Find a relocation for the offset of Word0.
+ // 2) Verify this relocation is of an expected type (R_ARM_PREL31) and
+ // verify the symbol index.
+ // 3) Resolve the relocation using it's symbol value, addend etc.
+ // Currently the code assumes that Word0 contains an addend of a
+ // R_ARM_PREL31 REL relocation that references a section symbol. RELA
+ // relocations are not supported and it works because addresses of sections
+ // are nulls in relocatable objects.
+ //
+ // For a non-relocatable object, Word0 contains a place-relative signed
+ // offset to the referenced entity.
+ const uint64_t Address =
+ IsRelocatable
+ ? PREL31(Word0, IT->sh_addr)
+ : PREL31(Word0, IT->sh_addr + Entry * IndexTableEntrySize);
+ SW.printHex("FunctionAddress", Address);
+
+ // In a relocatable output we might have many .ARM.exidx sections linked to
+ // their code sections via the sh_link field. For a non-relocatable ELF file
+ // the sh_link field is not reliable, because we have one .ARM.exidx section
+ // normally, but might have many code sections.
+ Optional<unsigned> SecIndex =
+ IsRelocatable ? Optional<unsigned>(IT->sh_link) : None;
+ if (ErrorOr<StringRef> Name = FunctionAtAddress(Address, SecIndex))
+ SW.printString("FunctionName", *Name);
+
+ if (Word1 == EXIDX_CANTUNWIND) {
+ SW.printString("Model", StringRef("CantUnwind"));
+ continue;
+ }
+
+ if (Word1 & 0x80000000) {
+ SW.printString("Model", StringRef("Compact (Inline)"));
+
+ unsigned PersonalityIndex = (Word1 & 0x0f000000) >> 24;
+ SW.printNumber("PersonalityIndex", PersonalityIndex);
+
+ PrintOpcodes(Contents->data() + Entry * IndexTableEntrySize + 4, 3, 1);
+ } else {
+ const Elf_Shdr *EHT;
+ uint64_t TableEntryAddress;
+ if (IsRelocatable) {
+ TableEntryAddress = PREL31(Word1, IT->sh_addr);
+ EHT = FindExceptionTable(SectionIndex, Entry * IndexTableEntrySize + 4);
+ } else {
+ TableEntryAddress =
+ PREL31(Word1, IT->sh_addr + Entry * IndexTableEntrySize + 4);
+ EHT = findSectionContainingAddress(ELF, FileName, TableEntryAddress);
+ }
+
+ if (EHT)
+ // TODO: handle failure.
+ if (Expected<StringRef> Name = ELF.getSectionName(*EHT))
+ SW.printString("ExceptionHandlingTable", *Name);
+
+ SW.printHex(IsRelocatable ? "TableEntryOffset" : "TableEntryAddress",
+ TableEntryAddress);
+ if (EHT) {
+ if (IsRelocatable)
+ PrintExceptionTable(*EHT, TableEntryAddress);
+ else
+ PrintExceptionTable(*EHT, TableEntryAddress - EHT->sh_addr);
+ }
+ }
+ }
+}
+
+template <typename ET>
+void PrinterContext<ET>::PrintUnwindInformation() const {
+ DictScope UI(SW, "UnwindInformation");
+
+ int SectionIndex = 0;
+ for (const Elf_Shdr &Sec : unwrapOrError(FileName, ELF.sections())) {
+ if (Sec.sh_type == ELF::SHT_ARM_EXIDX) {
+ DictScope UIT(SW, "UnwindIndexTable");
+
+ SW.printNumber("SectionIndex", SectionIndex);
+ // TODO: handle failure.
+ if (Expected<StringRef> SectionName = ELF.getSectionName(Sec))
+ SW.printString("SectionName", *SectionName);
+ SW.printHex("SectionOffset", Sec.sh_offset);
+
+ PrintIndexTable(SectionIndex, &Sec);
+ }
+ ++SectionIndex;
+ }
+}
+}
+}
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/ARMWinEHPrinter.cpp b/contrib/libs/llvm14/tools/llvm-readobj/ARMWinEHPrinter.cpp
new file mode 100644
index 00000000000..78be632f215
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -0,0 +1,1303 @@
+//===-- ARMWinEHPrinter.cpp - Windows on ARM EH Data Printer ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Windows on ARM uses a series of serialised data structures (RuntimeFunction)
+// to create a table of information for unwinding. In order to conserve space,
+// there are two different ways that this data is represented.
+//
+// For functions with canonical forms for the prologue and epilogue, the data
+// can be stored in a "packed" form. In this case, the data is packed into the
+// RuntimeFunction's remaining 30-bits and can fully describe the entire frame.
+//
+// +---------------------------------------+
+// | Function Entry Address |
+// +---------------------------------------+
+// | Packed Form Data |
+// +---------------------------------------+
+//
+// This layout is parsed by Decoder::dumpPackedEntry. No unwind bytecode is
+// associated with such a frame as they can be derived from the provided data.
+// The decoder does not synthesize this data as it is unnecessary for the
+// purposes of validation, with the synthesis being required only by a proper
+// unwinder.
+//
+// For functions that are large or do not match canonical forms, the data is
+// split up into two portions, with the actual data residing in the "exception
+// data" table (.xdata) with a reference to the entry from the "procedure data"
+// (.pdata) entry.
+//
+// The exception data contains information about the frame setup, all of the
+// epilogue scopes (for functions for which there are multiple exit points) and
+// the associated exception handler. Additionally, the entry contains byte-code
+// describing how to unwind the function (c.f. Decoder::decodeOpcodes).
+//
+// +---------------------------------------+
+// | Function Entry Address |
+// +---------------------------------------+
+// | Exception Data Entry Address |
+// +---------------------------------------+
+//
+// This layout is parsed by Decoder::dumpUnpackedEntry. Such an entry must
+// first resolve the exception data entry address. This structure
+// (ExceptionDataRecord) has a variable sized header
+// (c.f. ARM::WinEH::HeaderWords) and encodes most of the same information as
+// the packed form. However, because this information is insufficient to
+// synthesize the unwinding, there are associated unwinding bytecode which make
+// up the bulk of the Decoder.
+//
+// The decoder itself is table-driven, using the first byte to determine the
+// opcode and dispatching to the associated printing routine. The bytecode
+// itself is a variable length instruction encoding that can fully describe the
+// state of the stack and the necessary operations for unwinding to the
+// beginning of the frame.
+//
+// The byte-code maintains a 1-1 instruction mapping, indicating both the width
+// of the instruction (Thumb2 instructions are variable length, 16 or 32 bits
+// wide) allowing the program to unwind from any point in the prologue, body, or
+// epilogue of the function.
+
+#include "ARMWinEHPrinter.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/ARMWinEH.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::support;
+
+namespace llvm {
+raw_ostream &operator<<(raw_ostream &OS, const ARM::WinEH::ReturnType &RT) {
+ switch (RT) {
+ case ARM::WinEH::ReturnType::RT_POP:
+ OS << "pop {pc}";
+ break;
+ case ARM::WinEH::ReturnType::RT_B:
+ OS << "b target";
+ break;
+ case ARM::WinEH::ReturnType::RT_BW:
+ OS << "b.w target";
+ break;
+ case ARM::WinEH::ReturnType::RT_NoEpilogue:
+ OS << "(no epilogue)";
+ break;
+ }
+ return OS;
+}
+}
+
+static std::string formatSymbol(StringRef Name, uint64_t Address,
+ uint64_t Offset = 0) {
+ std::string Buffer;
+ raw_string_ostream OS(Buffer);
+
+ if (!Name.empty())
+ OS << Name << " ";
+
+ if (Offset)
+ OS << format("+0x%" PRIX64 " (0x%" PRIX64 ")", Offset, Address);
+ else if (!Name.empty())
+ OS << format("(0x%" PRIX64 ")", Address);
+ else
+ OS << format("0x%" PRIX64, Address);
+
+ return OS.str();
+}
+
+namespace llvm {
+namespace ARM {
+namespace WinEH {
+const size_t Decoder::PDataEntrySize = sizeof(RuntimeFunction);
+
+// TODO name the uops more appropriately
+const Decoder::RingEntry Decoder::Ring[] = {
+ { 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, 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 },
+ { 0xff, 0xe6, 1, &Decoder::opcode_save_next },
+ { 0xff, 0xe8, 1, &Decoder::opcode_trap_frame },
+ { 0xff, 0xe9, 1, &Decoder::opcode_machine_frame },
+ { 0xff, 0xea, 1, &Decoder::opcode_context },
+ { 0xff, 0xec, 1, &Decoder::opcode_clear_unwound_to_call },
+};
+
+void Decoder::printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask) {
+ static const char * const GPRRegisterNames[16] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
+ "r11", "ip", "sp", "lr", "pc",
+ };
+
+ const uint16_t GPRMask = std::get<0>(RegisterMask);
+ const uint16_t VFPMask = std::get<1>(RegisterMask);
+
+ OS << '{';
+ ListSeparator LS;
+ for (unsigned RI = 0, RE = 11; RI < RE; ++RI)
+ if (GPRMask & (1 << RI))
+ OS << LS << GPRRegisterNames[RI];
+ for (unsigned RI = 0, RE = 32; RI < RE; ++RI)
+ if (VFPMask & (1 << RI))
+ OS << LS << "d" << unsigned(RI);
+ for (unsigned RI = 11, RE = 16; RI < RE; ++RI)
+ if (GPRMask & (1 << RI))
+ OS << LS << GPRRegisterNames[RI];
+ OS << '}';
+}
+
+ErrorOr<object::SectionRef>
+Decoder::getSectionContaining(const COFFObjectFile &COFF, uint64_t VA) {
+ for (const auto &Section : COFF.sections()) {
+ uint64_t Address = Section.getAddress();
+ uint64_t Size = Section.getSize();
+
+ if (VA >= Address && (VA - Address) <= Size)
+ return Section;
+ }
+ return inconvertibleErrorCode();
+}
+
+ErrorOr<object::SymbolRef> Decoder::getSymbol(const COFFObjectFile &COFF,
+ uint64_t VA, bool FunctionOnly) {
+ for (const auto &Symbol : COFF.symbols()) {
+ Expected<SymbolRef::Type> Type = Symbol.getType();
+ if (!Type)
+ return errorToErrorCode(Type.takeError());
+ if (FunctionOnly && *Type != SymbolRef::ST_Function)
+ continue;
+
+ Expected<uint64_t> Address = Symbol.getAddress();
+ if (!Address)
+ return errorToErrorCode(Address.takeError());
+ if (*Address == VA)
+ return Symbol;
+ }
+ return inconvertibleErrorCode();
+}
+
+ErrorOr<SymbolRef> Decoder::getRelocatedSymbol(const COFFObjectFile &,
+ const SectionRef &Section,
+ uint64_t Offset) {
+ for (const auto &Relocation : Section.relocations()) {
+ uint64_t RelocationOffset = Relocation.getOffset();
+ if (RelocationOffset == Offset)
+ return *Relocation.getSymbol();
+ }
+ return inconvertibleErrorCode();
+}
+
+SymbolRef Decoder::getPreferredSymbol(const COFFObjectFile &COFF, SymbolRef Sym,
+ uint64_t &SymbolOffset) {
+ // The symbol resolved by getRelocatedSymbol can be any internal
+ // nondescriptive symbol; try to resolve a more descriptive one.
+ COFFSymbolRef CoffSym = COFF.getCOFFSymbol(Sym);
+ if (CoffSym.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL &&
+ CoffSym.getSectionDefinition() == nullptr)
+ return Sym;
+ for (const auto &S : COFF.symbols()) {
+ COFFSymbolRef CS = COFF.getCOFFSymbol(S);
+ if (CS.getSectionNumber() == CoffSym.getSectionNumber() &&
+ CS.getValue() <= CoffSym.getValue() + SymbolOffset &&
+ CS.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL &&
+ CS.getSectionDefinition() == nullptr) {
+ uint32_t Offset = CoffSym.getValue() + SymbolOffset - CS.getValue();
+ if (Offset <= SymbolOffset) {
+ SymbolOffset = Offset;
+ Sym = S;
+ CoffSym = CS;
+ if (CS.isExternal() && SymbolOffset == 0)
+ return Sym;
+ }
+ }
+ }
+ return Sym;
+}
+
+ErrorOr<SymbolRef> Decoder::getSymbolForLocation(
+ const COFFObjectFile &COFF, const SectionRef &Section,
+ uint64_t OffsetInSection, uint64_t ImmediateOffset, uint64_t &SymbolAddress,
+ uint64_t &SymbolOffset, bool FunctionOnly) {
+ // Try to locate a relocation that points at the offset in the section
+ ErrorOr<SymbolRef> SymOrErr =
+ getRelocatedSymbol(COFF, Section, OffsetInSection);
+ if (SymOrErr) {
+ // We found a relocation symbol; the immediate offset needs to be added
+ // to the symbol address.
+ SymbolOffset = ImmediateOffset;
+
+ Expected<uint64_t> AddressOrErr = SymOrErr->getAddress();
+ if (!AddressOrErr) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(AddressOrErr.takeError(), OS);
+ report_fatal_error(Twine(OS.str()));
+ }
+ // We apply SymbolOffset here directly. We return it separately to allow
+ // the caller to print it as an offset on the symbol name.
+ SymbolAddress = *AddressOrErr + SymbolOffset;
+
+ if (FunctionOnly) // Resolve label/section symbols into function names.
+ SymOrErr = getPreferredSymbol(COFF, *SymOrErr, SymbolOffset);
+ } else {
+ // No matching relocation found; operating on a linked image. Try to
+ // find a descriptive symbol if possible. The immediate offset contains
+ // the image relative address, and we shouldn't add any offset to the
+ // symbol.
+ SymbolAddress = COFF.getImageBase() + ImmediateOffset;
+ SymbolOffset = 0;
+ SymOrErr = getSymbol(COFF, SymbolAddress, FunctionOnly);
+ }
+ return SymOrErr;
+}
+
+bool Decoder::opcode_0xxxxxxx(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint8_t Imm = OC[Offset] & 0x7f;
+ SW.startLine() << format("0x%02x ; %s sp, #(%u * 4)\n",
+ OC[Offset],
+ static_cast<const char *>(Prologue ? "sub" : "add"),
+ Imm);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_10Lxxxxx(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ unsigned Link = (OC[Offset] & 0x20) >> 5;
+ uint16_t RegisterMask = (Link << (Prologue ? 14 : 15))
+ | ((OC[Offset + 0] & 0x1f) << 8)
+ | ((OC[Offset + 1] & 0xff) << 0);
+ assert((~RegisterMask & (1 << 13)) && "sp must not be set");
+ assert((~RegisterMask & (1 << (Prologue ? 15 : 14))) && "pc must not be set");
+
+ SW.startLine() << format("0x%02x 0x%02x ; %s.w ",
+ OC[Offset + 0], OC[Offset + 1],
+ Prologue ? "push" : "pop");
+ printRegisters(std::make_pair(RegisterMask, 0));
+ OS << '\n';
+
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_1100xxxx(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ if (Prologue)
+ SW.startLine() << format("0x%02x ; mov r%u, sp\n",
+ OC[Offset], OC[Offset] & 0xf);
+ else
+ SW.startLine() << format("0x%02x ; mov sp, r%u\n",
+ OC[Offset], OC[Offset] & 0xf);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_11010Lxx(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ unsigned Link = (OC[Offset] & 0x4) >> 3;
+ unsigned Count = (OC[Offset] & 0x3);
+
+ uint16_t GPRMask = (Link << (Prologue ? 14 : 15))
+ | (((1 << (Count + 1)) - 1) << 4);
+
+ SW.startLine() << format("0x%02x ; %s ", OC[Offset],
+ Prologue ? "push" : "pop");
+ printRegisters(std::make_pair(GPRMask, 0));
+ OS << '\n';
+
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_11011Lxx(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ unsigned Link = (OC[Offset] & 0x4) >> 2;
+ unsigned Count = (OC[Offset] & 0x3) + 4;
+
+ uint16_t GPRMask = (Link << (Prologue ? 14 : 15))
+ | (((1 << (Count + 1)) - 1) << 4);
+
+ SW.startLine() << format("0x%02x ; %s.w ", OC[Offset],
+ Prologue ? "push" : "pop");
+ printRegisters(std::make_pair(GPRMask, 0));
+ OS << '\n';
+
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_11100xxx(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ unsigned High = (OC[Offset] & 0x7);
+ uint32_t VFPMask = (((1 << (High + 1)) - 1) << 8);
+
+ SW.startLine() << format("0x%02x ; %s ", OC[Offset],
+ Prologue ? "vpush" : "vpop");
+ printRegisters(std::make_pair(0, VFPMask));
+ OS << '\n';
+
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_111010xx(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint16_t Imm = ((OC[Offset + 0] & 0x03) << 8) | ((OC[Offset + 1] & 0xff) << 0);
+
+ SW.startLine() << format("0x%02x 0x%02x ; %s.w sp, #(%u * 4)\n",
+ OC[Offset + 0], OC[Offset + 1],
+ static_cast<const char *>(Prologue ? "sub" : "add"),
+ Imm);
+
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_1110110L(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint8_t GPRMask = ((OC[Offset + 0] & 0x01) << (Prologue ? 14 : 15))
+ | ((OC[Offset + 1] & 0xff) << 0);
+
+ SW.startLine() << format("0x%02x 0x%02x ; %s ", OC[Offset + 0],
+ OC[Offset + 1], Prologue ? "push" : "pop");
+ printRegisters(std::make_pair(GPRMask, 0));
+ OS << '\n';
+
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_11101110(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ assert(!Prologue && "may not be used in prologue");
+
+ if (OC[Offset + 1] & 0xf0)
+ SW.startLine() << format("0x%02x 0x%02x ; reserved\n",
+ OC[Offset + 0], OC[Offset + 1]);
+ else
+ SW.startLine()
+ << format("0x%02x 0x%02x ; microsoft-specific (type: %u)\n",
+ OC[Offset + 0], OC[Offset + 1], OC[Offset + 1] & 0x0f);
+
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_11101111(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ assert(!Prologue && "may not be used in prologue");
+
+ if (OC[Offset + 1] & 0xf0)
+ SW.startLine() << format("0x%02x 0x%02x ; reserved\n",
+ OC[Offset + 0], OC[Offset + 1]);
+ else
+ SW.startLine()
+ << format("0x%02x 0x%02x ; ldr.w lr, [sp], #%u\n",
+ OC[Offset + 0], OC[Offset + 1], OC[Offset + 1] << 2);
+
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_11110101(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ unsigned Start = (OC[Offset + 1] & 0xf0) >> 4;
+ unsigned End = (OC[Offset + 1] & 0x0f) >> 0;
+ uint32_t VFPMask = ((1 << (End - Start)) - 1) << Start;
+
+ SW.startLine() << format("0x%02x 0x%02x ; %s ", OC[Offset + 0],
+ OC[Offset + 1], Prologue ? "vpush" : "vpop");
+ printRegisters(std::make_pair(0, VFPMask));
+ OS << '\n';
+
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_11110110(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ unsigned Start = (OC[Offset + 1] & 0xf0) >> 4;
+ unsigned End = (OC[Offset + 1] & 0x0f) >> 0;
+ uint32_t VFPMask = ((1 << (End - Start)) - 1) << 16;
+
+ SW.startLine() << format("0x%02x 0x%02x ; %s ", OC[Offset + 0],
+ OC[Offset + 1], Prologue ? "vpush" : "vpop");
+ printRegisters(std::make_pair(0, VFPMask));
+ OS << '\n';
+
+ Offset += 2;
+ return false;
+}
+
+bool Decoder::opcode_11110111(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Imm = (OC[Offset + 1] << 8) | (OC[Offset + 2] << 0);
+
+ SW.startLine() << format("0x%02x 0x%02x 0x%02x ; %s sp, sp, #(%u * 4)\n",
+ OC[Offset + 0], OC[Offset + 1], OC[Offset + 2],
+ static_cast<const char *>(Prologue ? "sub" : "add"),
+ Imm);
+
+ Offset += 3;
+ return false;
+}
+
+bool Decoder::opcode_11111000(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Imm = (OC[Offset + 1] << 16)
+ | (OC[Offset + 2] << 8)
+ | (OC[Offset + 3] << 0);
+
+ SW.startLine()
+ << format("0x%02x 0x%02x 0x%02x 0x%02x ; %s sp, sp, #(%u * 4)\n",
+ OC[Offset + 0], OC[Offset + 1], OC[Offset + 2], OC[Offset + 3],
+ static_cast<const char *>(Prologue ? "sub" : "add"), Imm);
+
+ Offset += 4;
+ return false;
+}
+
+bool Decoder::opcode_11111001(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Imm = (OC[Offset + 1] << 8) | (OC[Offset + 2] << 0);
+
+ SW.startLine()
+ << format("0x%02x 0x%02x 0x%02x ; %s.w sp, sp, #(%u * 4)\n",
+ OC[Offset + 0], OC[Offset + 1], OC[Offset + 2],
+ static_cast<const char *>(Prologue ? "sub" : "add"), Imm);
+
+ Offset += 3;
+ return false;
+}
+
+bool Decoder::opcode_11111010(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ uint32_t Imm = (OC[Offset + 1] << 16)
+ | (OC[Offset + 2] << 8)
+ | (OC[Offset + 3] << 0);
+
+ SW.startLine()
+ << format("0x%02x 0x%02x 0x%02x 0x%02x ; %s.w sp, sp, #(%u * 4)\n",
+ OC[Offset + 0], OC[Offset + 1], OC[Offset + 2], OC[Offset + 3],
+ static_cast<const char *>(Prologue ? "sub" : "add"), Imm);
+
+ Offset += 4;
+ return false;
+}
+
+bool Decoder::opcode_11111011(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_11111100(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ SW.startLine() << format("0x%02x ; nop.w\n", OC[Offset]);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_11111101(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ SW.startLine() << format("0x%02x ; b\n", OC[Offset]);
+ ++Offset;
+ return true;
+}
+
+bool Decoder::opcode_11111110(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ SW.startLine() << format("0x%02x ; b.w\n", OC[Offset]);
+ ++Offset;
+ return true;
+}
+
+bool Decoder::opcode_11111111(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ ++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 %s, %s\n", OC[Offset],
+ static_cast<const char *>(Prologue ? "fp" : "sp"),
+ static_cast<const char *>(Prologue ? "sp" : "fp"));
+ ++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 ; %s %s, %s, #%u\n", OC[Offset], OC[Offset + 1],
+ static_cast<const char *>(Prologue ? "add" : "sub"),
+ static_cast<const char *>(Prologue ? "fp" : "sp"),
+ static_cast<const char *>(Prologue ? "sp" : "fp"), 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;
+}
+
+bool Decoder::opcode_save_next(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ if (Prologue)
+ SW.startLine() << format("0x%02x ; save next\n", OC[Offset]);
+ else
+ SW.startLine() << format("0x%02x ; restore next\n",
+ OC[Offset]);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_trap_frame(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ SW.startLine() << format("0x%02x ; trap frame\n", OC[Offset]);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_machine_frame(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ SW.startLine() << format("0x%02x ; machine frame\n",
+ OC[Offset]);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_context(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ SW.startLine() << format("0x%02x ; context\n", OC[Offset]);
+ ++Offset;
+ return false;
+}
+
+bool Decoder::opcode_clear_unwound_to_call(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ SW.startLine() << format("0x%02x ; clear unwound to call\n",
+ OC[Offset]);
+ ++Offset;
+ return false;
+}
+
+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 ((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;
+ }
+ }
+ }
+}
+
+bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
+ const SectionRef &Section,
+ uint64_t FunctionAddress, uint64_t VA) {
+ ArrayRef<uint8_t> Contents;
+ if (COFF.getSectionContents(COFF.getCOFFSection(Section), Contents))
+ return false;
+
+ uint64_t SectionVA = Section.getAddress();
+ uint64_t Offset = VA - SectionVA;
+ const ulittle32_t *Data =
+ reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset);
+
+ // 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",
+ isAArch64 ? XData.FunctionLengthInBytesAArch64() :
+ XData.FunctionLengthInBytesARM());
+ SW.printNumber("Version", XData.Vers());
+ SW.printBoolean("ExceptionData", XData.X());
+ SW.printBoolean("EpiloguePacked", XData.E());
+ if (!isAArch64)
+ SW.printBoolean("Fragment", XData.F());
+ SW.printNumber(XData.E() ? "EpilogueOffset" : "EpilogueScopes",
+ XData.EpilogueCount());
+ 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) {
+ SW.flush();
+ report_fatal_error("Malformed unwind data");
+ }
+
+ if (XData.E()) {
+ ArrayRef<uint8_t> UC = XData.UnwindByteCode();
+ if (isAArch64 || !XData.F()) {
+ ListScope PS(SW, "Prologue");
+ decodeOpcodes(UC, 0, /*Prologue=*/true);
+ }
+ if (XData.EpilogueCount()) {
+ ListScope ES(SW, "Epilogue");
+ 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());
+ 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(),
+ isAArch64 ? ES.EpilogueStartIndexAArch64()
+ : ES.EpilogueStartIndexARM(),
+ /*Prologue=*/false);
+ }
+ }
+
+ if (XData.X()) {
+ const uint32_t Parameter = XData.ExceptionHandlerParameter();
+ const size_t HandlerOffset = HeaderWords(XData) +
+ (XData.E() ? 0 : XData.EpilogueCount()) +
+ XData.CodeWords();
+
+ uint64_t Address, SymbolOffset;
+ ErrorOr<SymbolRef> Symbol = getSymbolForLocation(
+ COFF, Section, Offset + HandlerOffset * sizeof(uint32_t),
+ XData.ExceptionHandlerRVA(), Address, SymbolOffset,
+ /*FunctionOnly=*/true);
+ if (!Symbol) {
+ ListScope EHS(SW, "ExceptionHandler");
+ SW.printHex("Routine", Address);
+ SW.printHex("Parameter", Parameter);
+ return true;
+ }
+
+ Expected<StringRef> Name = Symbol->getName();
+ if (!Name) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(Name.takeError(), OS);
+ report_fatal_error(Twine(OS.str()));
+ }
+
+ ListScope EHS(SW, "ExceptionHandler");
+ SW.printString("Routine", formatSymbol(*Name, Address, SymbolOffset));
+ SW.printHex("Parameter", Parameter);
+ }
+
+ return true;
+}
+
+bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF,
+ const SectionRef Section, uint64_t Offset,
+ unsigned Index, const RuntimeFunction &RF) {
+ assert(RF.Flag() == RuntimeFunctionFlag::RFF_Unpacked &&
+ "packed entry cannot be treated as an unpacked entry");
+
+ uint64_t FunctionAddress, FunctionOffset;
+ ErrorOr<SymbolRef> Function = getSymbolForLocation(
+ COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset,
+ /*FunctionOnly=*/true);
+
+ uint64_t XDataAddress, XDataOffset;
+ ErrorOr<SymbolRef> XDataRecord = getSymbolForLocation(
+ COFF, Section, Offset + 4, RF.ExceptionInformationRVA(), XDataAddress,
+ XDataOffset);
+
+ if (!RF.BeginAddress && !Function)
+ return false;
+ if (!RF.UnwindData && !XDataRecord)
+ return false;
+
+ StringRef FunctionName;
+ if (Function) {
+ Expected<StringRef> FunctionNameOrErr = Function->getName();
+ if (!FunctionNameOrErr) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS);
+ report_fatal_error(Twine(OS.str()));
+ }
+ FunctionName = *FunctionNameOrErr;
+ }
+
+ SW.printString("Function",
+ formatSymbol(FunctionName, FunctionAddress, FunctionOffset));
+
+ if (XDataRecord) {
+ Expected<StringRef> Name = XDataRecord->getName();
+ if (!Name) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(Name.takeError(), OS);
+ report_fatal_error(Twine(OS.str()));
+ }
+
+ SW.printString("ExceptionRecord",
+ formatSymbol(*Name, XDataAddress, XDataOffset));
+
+ Expected<section_iterator> SIOrErr = XDataRecord->getSection();
+ if (!SIOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(SIOrErr.takeError());
+ return false;
+ }
+ section_iterator SI = *SIOrErr;
+
+ return dumpXDataRecord(COFF, *SI, FunctionAddress, XDataAddress);
+ } else {
+ SW.printString("ExceptionRecord", formatSymbol("", XDataAddress));
+
+ ErrorOr<SectionRef> Section = getSectionContaining(COFF, XDataAddress);
+ if (!Section)
+ return false;
+
+ return dumpXDataRecord(COFF, *Section, FunctionAddress, XDataAddress);
+ }
+}
+
+bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF,
+ const SectionRef Section, uint64_t Offset,
+ unsigned Index, const RuntimeFunction &RF) {
+ assert((RF.Flag() == RuntimeFunctionFlag::RFF_Packed ||
+ RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) &&
+ "unpacked entry cannot be treated as a packed entry");
+
+ uint64_t FunctionAddress, FunctionOffset;
+ ErrorOr<SymbolRef> Function = getSymbolForLocation(
+ COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset,
+ /*FunctionOnly=*/true);
+
+ StringRef FunctionName;
+ if (Function) {
+ Expected<StringRef> FunctionNameOrErr = Function->getName();
+ if (!FunctionNameOrErr) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS);
+ report_fatal_error(Twine(OS.str()));
+ }
+ FunctionName = *FunctionNameOrErr;
+ }
+
+ SW.printString("Function",
+ formatSymbol(FunctionName, FunctionAddress, FunctionOffset));
+ 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());
+ SW.startLine() << "SavedRegisters: ";
+ printRegisters(SavedRegisterMask(RF));
+ OS << '\n';
+ SW.printNumber("StackAdjustment", StackAdjustment(RF) << 2);
+
+ return true;
+}
+
+bool Decoder::dumpPackedARM64Entry(const object::COFFObjectFile &COFF,
+ const SectionRef Section, uint64_t Offset,
+ unsigned Index,
+ const RuntimeFunctionARM64 &RF) {
+ assert((RF.Flag() == RuntimeFunctionFlag::RFF_Packed ||
+ RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) &&
+ "unpacked entry cannot be treated as a packed entry");
+
+ uint64_t FunctionAddress, FunctionOffset;
+ ErrorOr<SymbolRef> Function = getSymbolForLocation(
+ COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset,
+ /*FunctionOnly=*/true);
+
+ StringRef FunctionName;
+ if (Function) {
+ Expected<StringRef> FunctionNameOrErr = Function->getName();
+ if (!FunctionNameOrErr) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS);
+ report_fatal_error(Twine(OS.str()));
+ }
+ FunctionName = *FunctionNameOrErr;
+ }
+
+ SW.printString("Function",
+ formatSymbol(FunctionName, FunctionAddress, FunctionOffset));
+ SW.printBoolean("Fragment",
+ RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment);
+ SW.printNumber("FunctionLength", RF.FunctionLength());
+ SW.printNumber("RegF", RF.RegF());
+ SW.printNumber("RegI", RF.RegI());
+ SW.printBoolean("HomedParameters", RF.H());
+ SW.printNumber("CR", RF.CR());
+ SW.printNumber("FrameSize", RF.FrameSize() << 4);
+ ListScope PS(SW, "Prologue");
+
+ // Synthesize the equivalent prologue according to the documentation
+ // at https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling,
+ // printed in reverse order compared to the docs, to match how prologues
+ // are printed for the non-packed case.
+ int IntSZ = 8 * RF.RegI();
+ if (RF.CR() == 1)
+ IntSZ += 8;
+ int FpSZ = 8 * RF.RegF();
+ if (RF.RegF())
+ FpSZ += 8;
+ int SavSZ = (IntSZ + FpSZ + 8 * 8 * RF.H() + 0xf) & ~0xf;
+ int LocSZ = (RF.FrameSize() << 4) - SavSZ;
+
+ if (RF.CR() == 3) {
+ SW.startLine() << "mov x29, sp\n";
+ if (LocSZ <= 512) {
+ SW.startLine() << format("stp x29, lr, [sp, #-%d]!\n", LocSZ);
+ } else {
+ SW.startLine() << "stp x29, lr, [sp, #0]\n";
+ }
+ }
+ if (LocSZ > 4080) {
+ SW.startLine() << format("sub sp, sp, #%d\n", LocSZ - 4080);
+ SW.startLine() << "sub sp, sp, #4080\n";
+ } else if ((RF.CR() != 3 && LocSZ > 0) || LocSZ > 512) {
+ SW.startLine() << format("sub sp, sp, #%d\n", LocSZ);
+ }
+ if (RF.H()) {
+ SW.startLine() << format("stp x6, x7, [sp, #%d]\n", IntSZ + FpSZ + 48);
+ SW.startLine() << format("stp x4, x5, [sp, #%d]\n", IntSZ + FpSZ + 32);
+ SW.startLine() << format("stp x2, x3, [sp, #%d]\n", IntSZ + FpSZ + 16);
+ if (RF.RegI() > 0 || RF.RegF() > 0 || RF.CR() == 1) {
+ SW.startLine() << format("stp x0, x1, [sp, #%d]\n", IntSZ + FpSZ);
+ } else {
+ // This case isn't documented; if neither RegI nor RegF nor CR=1
+ // have decremented the stack pointer by SavSZ, we need to do it here
+ // (as the final stack adjustment of LocSZ excludes SavSZ).
+ SW.startLine() << format("stp x0, x1, [sp, #-%d]!\n", SavSZ);
+ }
+ }
+ int FloatRegs = RF.RegF() > 0 ? RF.RegF() + 1 : 0;
+ for (int I = (FloatRegs + 1) / 2 - 1; I >= 0; I--) {
+ if (I == (FloatRegs + 1) / 2 - 1 && FloatRegs % 2 == 1) {
+ // The last register, an odd register without a pair
+ SW.startLine() << format("str d%d, [sp, #%d]\n", 8 + 2 * I,
+ IntSZ + 16 * I);
+ } else if (I == 0 && RF.RegI() == 0 && RF.CR() != 1) {
+ SW.startLine() << format("stp d%d, d%d, [sp, #-%d]!\n", 8 + 2 * I,
+ 8 + 2 * I + 1, SavSZ);
+ } else {
+ SW.startLine() << format("stp d%d, d%d, [sp, #%d]\n", 8 + 2 * I,
+ 8 + 2 * I + 1, IntSZ + 16 * I);
+ }
+ }
+ if (RF.CR() == 1 && (RF.RegI() % 2) == 0) {
+ if (RF.RegI() == 0)
+ SW.startLine() << format("str lr, [sp, #-%d]!\n", SavSZ);
+ else
+ SW.startLine() << format("str lr, [sp, #%d]\n", IntSZ - 8);
+ }
+ for (int I = (RF.RegI() + 1) / 2 - 1; I >= 0; I--) {
+ if (I == (RF.RegI() + 1) / 2 - 1 && RF.RegI() % 2 == 1) {
+ // The last register, an odd register without a pair
+ if (RF.CR() == 1) {
+ if (I == 0) { // If this is the only register pair
+ // CR=1 combined with RegI=1 doesn't map to a documented case;
+ // it doesn't map to any regular unwind info opcode, and the
+ // actual unwinder doesn't support it.
+ SW.startLine() << "INVALID!\n";
+ } else
+ SW.startLine() << format("stp x%d, lr, [sp, #%d]\n", 19 + 2 * I,
+ 16 * I);
+ } else {
+ if (I == 0)
+ SW.startLine() << format("str x%d, [sp, #-%d]!\n", 19 + 2 * I, SavSZ);
+ else
+ SW.startLine() << format("str x%d, [sp, #%d]\n", 19 + 2 * I, 16 * I);
+ }
+ } else if (I == 0) {
+ // The first register pair
+ SW.startLine() << format("stp x19, x20, [sp, #-%d]!\n", SavSZ);
+ } else {
+ SW.startLine() << format("stp x%d, x%d, [sp, #%d]\n", 19 + 2 * I,
+ 19 + 2 * I + 1, 16 * I);
+ }
+ }
+ SW.startLine() << "end\n";
+
+ return true;
+}
+
+bool Decoder::dumpProcedureDataEntry(const COFFObjectFile &COFF,
+ const SectionRef Section, unsigned Index,
+ ArrayRef<uint8_t> Contents) {
+ uint64_t Offset = PDataEntrySize * Index;
+ const ulittle32_t *Data =
+ reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset);
+
+ const RuntimeFunction Entry(Data);
+ DictScope RFS(SW, "RuntimeFunction");
+ if (Entry.Flag() == RuntimeFunctionFlag::RFF_Unpacked)
+ return dumpUnpackedEntry(COFF, Section, Offset, Index, Entry);
+ if (isAArch64) {
+ const RuntimeFunctionARM64 EntryARM64(Data);
+ return dumpPackedARM64Entry(COFF, Section, Offset, Index, EntryARM64);
+ }
+ return dumpPackedEntry(COFF, Section, Offset, Index, Entry);
+}
+
+void Decoder::dumpProcedureData(const COFFObjectFile &COFF,
+ const SectionRef Section) {
+ ArrayRef<uint8_t> Contents;
+ if (COFF.getSectionContents(COFF.getCOFFSection(Section), Contents))
+ return;
+
+ if (Contents.size() % PDataEntrySize) {
+ errs() << ".pdata content is not " << PDataEntrySize << "-byte aligned\n";
+ return;
+ }
+
+ for (unsigned EI = 0, EE = Contents.size() / PDataEntrySize; EI < EE; ++EI)
+ if (!dumpProcedureDataEntry(COFF, Section, EI, Contents))
+ break;
+}
+
+Error Decoder::dumpProcedureData(const COFFObjectFile &COFF) {
+ for (const auto &Section : COFF.sections()) {
+ Expected<StringRef> NameOrErr =
+ COFF.getSectionName(COFF.getCOFFSection(Section));
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+
+ if (NameOrErr->startswith(".pdata"))
+ dumpProcedureData(COFF, Section);
+ }
+ return Error::success();
+}
+}
+}
+}
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/ARMWinEHPrinter.h b/contrib/libs/llvm14/tools/llvm-readobj/ARMWinEHPrinter.h
new file mode 100644
index 00000000000..920d4e5f733
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/ARMWinEHPrinter.h
@@ -0,0 +1,188 @@
+//===--- ARMWinEHPrinter.h - Windows on ARM Unwind Information Printer ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_READOBJ_ARMWINEHPRINTER_H
+#define LLVM_TOOLS_LLVM_READOBJ_ARMWINEHPRINTER_H
+
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+namespace llvm {
+namespace ARM {
+namespace WinEH {
+class RuntimeFunction;
+class RuntimeFunctionARM64;
+
+class Decoder {
+ static const size_t PDataEntrySize;
+
+ 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);
+ bool opcode_10Lxxxxx(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_1100xxxx(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11010Lxx(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11011Lxx(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11100xxx(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_111010xx(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_1110110L(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11101110(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11101111(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11110101(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11110110(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11110111(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11111000(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11111001(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11111010(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11111011(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11111100(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11111101(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_11111110(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ 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);
+ bool opcode_trap_frame(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_machine_frame(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+ bool opcode_context(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
+ bool Prologue);
+ bool opcode_clear_unwound_to_call(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
+
+ void decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset,
+ bool Prologue);
+
+ void printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask);
+
+ ErrorOr<object::SectionRef>
+ getSectionContaining(const object::COFFObjectFile &COFF, uint64_t Address);
+
+ ErrorOr<object::SymbolRef>
+ getSymbol(const object::COFFObjectFile &COFF, uint64_t Address,
+ bool FunctionOnly = false);
+
+ ErrorOr<object::SymbolRef>
+ getRelocatedSymbol(const object::COFFObjectFile &COFF,
+ const object::SectionRef &Section, uint64_t Offset);
+
+ ErrorOr<object::SymbolRef>
+ getSymbolForLocation(const object::COFFObjectFile &COFF,
+ const object::SectionRef &Section,
+ uint64_t OffsetInSection, uint64_t ImmediateOffset,
+ uint64_t &SymbolAddress, uint64_t &SymbolOffset,
+ bool FunctionOnly = false);
+
+ object::SymbolRef getPreferredSymbol(const object::COFFObjectFile &COFF,
+ object::SymbolRef Sym,
+ uint64_t &SymbolOffset);
+
+ bool dumpXDataRecord(const object::COFFObjectFile &COFF,
+ const object::SectionRef &Section,
+ uint64_t FunctionAddress, uint64_t VA);
+ bool dumpUnpackedEntry(const object::COFFObjectFile &COFF,
+ const object::SectionRef Section, uint64_t Offset,
+ unsigned Index, const RuntimeFunction &Entry);
+ bool dumpPackedEntry(const object::COFFObjectFile &COFF,
+ const object::SectionRef Section, uint64_t Offset,
+ unsigned Index, const RuntimeFunction &Entry);
+ bool dumpPackedARM64Entry(const object::COFFObjectFile &COFF,
+ const object::SectionRef Section, uint64_t Offset,
+ unsigned Index, const RuntimeFunctionARM64 &Entry);
+ bool dumpProcedureDataEntry(const object::COFFObjectFile &COFF,
+ const object::SectionRef Section, unsigned Entry,
+ ArrayRef<uint8_t> Contents);
+ void dumpProcedureData(const object::COFFObjectFile &COFF,
+ const object::SectionRef Section);
+
+public:
+ Decoder(ScopedPrinter &SW, bool isAArch64) : SW(SW),
+ OS(SW.getOStream()),
+ isAArch64(isAArch64) {}
+ Error dumpProcedureData(const object::COFFObjectFile &COFF);
+};
+}
+}
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/COFFDumper.cpp b/contrib/libs/llvm14/tools/llvm-readobj/COFFDumper.cpp
new file mode 100644
index 00000000000..caeb49af24b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/COFFDumper.cpp
@@ -0,0 +1,2079 @@
+//===-- COFFDumper.cpp - COFF-specific dumper -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the COFF-specific dumper for llvm-readobj.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ARMWinEHPrinter.h"
+#include "ObjDumper.h"
+#include "StackMapPrinter.h"
+#include "Win64EHDumper.h"
+#include "llvm-readobj.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/CodeView/CodeView.h"
+#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.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"
+#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/Line.h"
+#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
+#include "llvm/DebugInfo/CodeView/RecordSerialization.h"
+#include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h"
+#include "llvm/DebugInfo/CodeView/SymbolDumper.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
+#include "llvm/DebugInfo/CodeView/TypeHashing.h"
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
+#include "llvm/DebugInfo/CodeView/TypeTableCollection.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/WindowsResource.h"
+#include "llvm/Support/BinaryStreamReader.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/Win64EH.h"
+#include "llvm/Support/raw_ostream.h"
+#include <ctime>
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::codeview;
+using namespace llvm::support;
+using namespace llvm::Win64EH;
+
+namespace {
+
+struct LoadConfigTables {
+ uint64_t SEHTableVA = 0;
+ uint64_t SEHTableCount = 0;
+ uint32_t GuardFlags = 0;
+ uint64_t GuardFidTableVA = 0;
+ uint64_t GuardFidTableCount = 0;
+ uint64_t GuardIatTableVA = 0;
+ uint64_t GuardIatTableCount = 0;
+ uint64_t GuardLJmpTableVA = 0;
+ uint64_t GuardLJmpTableCount = 0;
+ uint64_t GuardEHContTableVA = 0;
+ uint64_t GuardEHContTableCount = 0;
+};
+
+class COFFDumper : public ObjDumper {
+public:
+ friend class COFFObjectDumpDelegate;
+ COFFDumper(const llvm::object::COFFObjectFile *Obj, ScopedPrinter &Writer)
+ : ObjDumper(Writer, Obj->getFileName()), Obj(Obj), Writer(Writer),
+ Types(100) {}
+
+ void printFileHeaders() override;
+ void printSectionHeaders() override;
+ void printRelocations() override;
+ void printUnwindInfo() override;
+
+ void printNeededLibraries() override;
+
+ void printCOFFImports() override;
+ void printCOFFExports() override;
+ void printCOFFDirectives() override;
+ void printCOFFBaseReloc() override;
+ void printCOFFDebugDirectory() override;
+ void printCOFFTLSDirectory() override;
+ void printCOFFResources() override;
+ void printCOFFLoadConfig() override;
+ void printCodeViewDebugInfo() override;
+ void mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs,
+ llvm::codeview::MergingTypeTableBuilder &CVTypes,
+ llvm::codeview::GlobalTypeTableBuilder &GlobalCVIDs,
+ llvm::codeview::GlobalTypeTableBuilder &GlobalCVTypes,
+ bool GHash) override;
+ void printStackMap() const override;
+ void printAddrsig() override;
+ void printCGProfile() override;
+
+private:
+ StringRef getSymbolName(uint32_t Index);
+ void printSymbols() override;
+ void printDynamicSymbols() override;
+ void printSymbol(const SymbolRef &Sym);
+ void printRelocation(const SectionRef &Section, const RelocationRef &Reloc,
+ uint64_t Bias = 0);
+ void printDataDirectory(uint32_t Index, const std::string &FieldName);
+
+ void printDOSHeader(const dos_header *DH);
+ template <class PEHeader> void printPEHeader(const PEHeader *Hdr);
+ void printBaseOfDataField(const pe32_header *Hdr);
+ void printBaseOfDataField(const pe32plus_header *Hdr);
+ template <typename T>
+ void printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables);
+ template <typename IntTy>
+ void printCOFFTLSDirectory(const coff_tls_directory<IntTy> *TlsTable);
+ typedef void (*PrintExtraCB)(raw_ostream &, const uint8_t *);
+ void printRVATable(uint64_t TableVA, uint64_t Count, uint64_t EntrySize,
+ PrintExtraCB PrintExtra = nullptr);
+
+ void printCodeViewSymbolSection(StringRef SectionName, const SectionRef &Section);
+ void printCodeViewTypeSection(StringRef SectionName, const SectionRef &Section);
+ StringRef getFileNameForFileOffset(uint32_t FileOffset);
+ void printFileNameForOffset(StringRef Label, uint32_t FileOffset);
+ void printTypeIndex(StringRef FieldName, TypeIndex TI) {
+ // Forward to CVTypeDumper for simplicity.
+ codeview::printTypeIndex(Writer, FieldName, TI, Types);
+ }
+
+ void printCodeViewSymbolsSubsection(StringRef Subsection,
+ const SectionRef &Section,
+ StringRef SectionContents);
+
+ void printCodeViewFileChecksums(StringRef Subsection);
+
+ void printCodeViewInlineeLines(StringRef Subsection);
+
+ void printRelocatedField(StringRef Label, const coff_section *Sec,
+ uint32_t RelocOffset, uint32_t Offset,
+ StringRef *RelocSym = nullptr);
+
+ uint32_t countTotalTableEntries(ResourceSectionRef RSF,
+ const coff_resource_dir_table &Table,
+ StringRef Level);
+
+ void printResourceDirectoryTable(ResourceSectionRef RSF,
+ const coff_resource_dir_table &Table,
+ StringRef Level);
+
+ void printBinaryBlockWithRelocs(StringRef Label, const SectionRef &Sec,
+ StringRef SectionContents, StringRef Block);
+
+ /// Given a .debug$S section, find the string table and file checksum table.
+ void initializeFileAndStringTables(BinaryStreamReader &Reader);
+
+ void cacheRelocations();
+
+ std::error_code resolveSymbol(const coff_section *Section, uint64_t Offset,
+ SymbolRef &Sym);
+ std::error_code resolveSymbolName(const coff_section *Section,
+ uint64_t Offset, StringRef &Name);
+ std::error_code resolveSymbolName(const coff_section *Section,
+ StringRef SectionContents,
+ const void *RelocPtr, StringRef &Name);
+ void printImportedSymbols(iterator_range<imported_symbol_iterator> Range);
+ void printDelayImportedSymbols(
+ const DelayImportDirectoryEntryRef &I,
+ iterator_range<imported_symbol_iterator> Range);
+
+ typedef DenseMap<const coff_section*, std::vector<RelocationRef> > RelocMapTy;
+
+ const llvm::object::COFFObjectFile *Obj;
+ bool RelocCached = false;
+ RelocMapTy RelocMap;
+
+ DebugChecksumsSubsectionRef CVFileChecksumTable;
+
+ 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;
+};
+
+class COFFObjectDumpDelegate : public SymbolDumpDelegate {
+public:
+ COFFObjectDumpDelegate(COFFDumper &CD, const SectionRef &SR,
+ const COFFObjectFile *Obj, StringRef SectionContents)
+ : CD(CD), SR(SR), SectionContents(SectionContents) {
+ Sec = Obj->getCOFFSection(SR);
+ }
+
+ uint32_t getRecordOffset(BinaryStreamReader Reader) override {
+ ArrayRef<uint8_t> Data;
+ if (auto EC = Reader.readLongestContiguousChunk(Data)) {
+ llvm::consumeError(std::move(EC));
+ return 0;
+ }
+ return Data.data() - SectionContents.bytes_begin();
+ }
+
+ void printRelocatedField(StringRef Label, uint32_t RelocOffset,
+ uint32_t Offset, StringRef *RelocSym) override {
+ CD.printRelocatedField(Label, Sec, RelocOffset, Offset, RelocSym);
+ }
+
+ void printBinaryBlockWithRelocs(StringRef Label,
+ ArrayRef<uint8_t> Block) override {
+ StringRef SBlock(reinterpret_cast<const char *>(Block.data()),
+ Block.size());
+ if (opts::CodeViewSubsectionBytes)
+ CD.printBinaryBlockWithRelocs(Label, SR, SectionContents, SBlock);
+ }
+
+ StringRef getFileNameForFileOffset(uint32_t FileOffset) override {
+ return CD.getFileNameForFileOffset(FileOffset);
+ }
+
+ DebugStringTableSubsectionRef getStringTable() override {
+ return CD.CVStringTable;
+ }
+
+private:
+ COFFDumper &CD;
+ const SectionRef &SR;
+ const coff_section *Sec;
+ StringRef SectionContents;
+};
+
+} // end namespace
+
+namespace llvm {
+
+std::unique_ptr<ObjDumper> createCOFFDumper(const object::COFFObjectFile &Obj,
+ ScopedPrinter &Writer) {
+ return std::make_unique<COFFDumper>(&Obj, Writer);
+}
+
+} // namespace llvm
+
+// Given a section and an offset into this section the function returns the
+// symbol used for the relocation at the offset.
+std::error_code COFFDumper::resolveSymbol(const coff_section *Section,
+ uint64_t Offset, SymbolRef &Sym) {
+ cacheRelocations();
+ const auto &Relocations = RelocMap[Section];
+ auto SymI = Obj->symbol_end();
+ for (const auto &Relocation : Relocations) {
+ uint64_t RelocationOffset = Relocation.getOffset();
+
+ if (RelocationOffset == Offset) {
+ SymI = Relocation.getSymbol();
+ break;
+ }
+ }
+ if (SymI == Obj->symbol_end())
+ return inconvertibleErrorCode();
+ Sym = *SymI;
+ return std::error_code();
+}
+
+// Given a section and an offset into this section the function returns the name
+// of the symbol used for the relocation at the offset.
+std::error_code COFFDumper::resolveSymbolName(const coff_section *Section,
+ uint64_t Offset,
+ StringRef &Name) {
+ SymbolRef Symbol;
+ if (std::error_code EC = resolveSymbol(Section, Offset, Symbol))
+ return EC;
+ Expected<StringRef> NameOrErr = Symbol.getName();
+ if (!NameOrErr)
+ return errorToErrorCode(NameOrErr.takeError());
+ Name = *NameOrErr;
+ return std::error_code();
+}
+
+// Helper for when you have a pointer to real data and you want to know about
+// relocations against it.
+std::error_code COFFDumper::resolveSymbolName(const coff_section *Section,
+ StringRef SectionContents,
+ const void *RelocPtr,
+ StringRef &Name) {
+ assert(SectionContents.data() < RelocPtr &&
+ RelocPtr < SectionContents.data() + SectionContents.size() &&
+ "pointer to relocated object is not in section");
+ uint64_t Offset = ptrdiff_t(reinterpret_cast<const char *>(RelocPtr) -
+ SectionContents.data());
+ return resolveSymbolName(Section, Offset, Name);
+}
+
+void COFFDumper::printRelocatedField(StringRef Label, const coff_section *Sec,
+ uint32_t RelocOffset, uint32_t Offset,
+ StringRef *RelocSym) {
+ StringRef SymStorage;
+ StringRef &Symbol = RelocSym ? *RelocSym : SymStorage;
+ if (!resolveSymbolName(Sec, RelocOffset, Symbol))
+ W.printSymbolOffset(Label, Symbol, Offset);
+ else
+ W.printHex(Label, RelocOffset);
+}
+
+void COFFDumper::printBinaryBlockWithRelocs(StringRef Label,
+ const SectionRef &Sec,
+ StringRef SectionContents,
+ StringRef Block) {
+ W.printBinaryBlock(Label, Block);
+
+ assert(SectionContents.begin() < Block.begin() &&
+ SectionContents.end() >= Block.end() &&
+ "Block is not contained in SectionContents");
+ uint64_t OffsetStart = Block.data() - SectionContents.data();
+ uint64_t OffsetEnd = OffsetStart + Block.size();
+
+ W.flush();
+ cacheRelocations();
+ ListScope D(W, "BlockRelocations");
+ const coff_section *Section = Obj->getCOFFSection(Sec);
+ const auto &Relocations = RelocMap[Section];
+ for (const auto &Relocation : Relocations) {
+ uint64_t RelocationOffset = Relocation.getOffset();
+ if (OffsetStart <= RelocationOffset && RelocationOffset < OffsetEnd)
+ printRelocation(Sec, Relocation, OffsetStart);
+ }
+}
+
+const EnumEntry<COFF::MachineTypes> ImageFileMachineType[] = {
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_UNKNOWN ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_AM33 ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_AMD64 ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_ARM ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_ARM64 ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_ARMNT ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_EBC ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_I386 ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_IA64 ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_M32R ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MIPS16 ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MIPSFPU ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MIPSFPU16),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_POWERPC ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_POWERPCFP),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_R4000 ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_SH3 ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_SH3DSP ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_SH4 ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_SH5 ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_THUMB ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_WCEMIPSV2)
+};
+
+const EnumEntry<COFF::Characteristics> ImageFileCharacteristics[] = {
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_RELOCS_STRIPPED ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_EXECUTABLE_IMAGE ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_LINE_NUMS_STRIPPED ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_LOCAL_SYMS_STRIPPED ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_AGGRESSIVE_WS_TRIM ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_LARGE_ADDRESS_AWARE ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_BYTES_REVERSED_LO ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_32BIT_MACHINE ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_DEBUG_STRIPPED ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_NET_RUN_FROM_SWAP ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_SYSTEM ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_DLL ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_UP_SYSTEM_ONLY ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_BYTES_REVERSED_HI )
+};
+
+const EnumEntry<COFF::WindowsSubsystem> PEWindowsSubsystem[] = {
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_UNKNOWN ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_NATIVE ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_WINDOWS_GUI ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_WINDOWS_CUI ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_POSIX_CUI ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_WINDOWS_CE_GUI ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_EFI_APPLICATION ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_EFI_ROM ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_XBOX ),
+};
+
+const EnumEntry<COFF::DLLCharacteristics> PEDLLCharacteristics[] = {
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_NO_SEH ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_NO_BIND ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_APPCONTAINER ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_GUARD_CF ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE),
+};
+
+static const EnumEntry<COFF::ExtendedDLLCharacteristics>
+ PEExtendedDLLCharacteristics[] = {
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT),
+};
+
+static const EnumEntry<COFF::SectionCharacteristics>
+ImageSectionCharacteristics[] = {
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NOLOAD ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NO_PAD ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_CNT_CODE ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_CNT_INITIALIZED_DATA ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_CNT_UNINITIALIZED_DATA),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_LNK_OTHER ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_LNK_INFO ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_LNK_REMOVE ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_LNK_COMDAT ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_GPREL ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_PURGEABLE ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_16BIT ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_LOCKED ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_PRELOAD ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_1BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_2BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_4BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_8BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_16BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_32BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_64BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_128BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_256BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_512BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_1024BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_2048BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_4096BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_8192BYTES ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_LNK_NRELOC_OVFL ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_DISCARDABLE ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_NOT_CACHED ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_NOT_PAGED ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_SHARED ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_EXECUTE ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_READ ),
+ LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_WRITE )
+};
+
+const EnumEntry<COFF::SymbolBaseType> ImageSymType[] = {
+ { "Null" , COFF::IMAGE_SYM_TYPE_NULL },
+ { "Void" , COFF::IMAGE_SYM_TYPE_VOID },
+ { "Char" , COFF::IMAGE_SYM_TYPE_CHAR },
+ { "Short" , COFF::IMAGE_SYM_TYPE_SHORT },
+ { "Int" , COFF::IMAGE_SYM_TYPE_INT },
+ { "Long" , COFF::IMAGE_SYM_TYPE_LONG },
+ { "Float" , COFF::IMAGE_SYM_TYPE_FLOAT },
+ { "Double", COFF::IMAGE_SYM_TYPE_DOUBLE },
+ { "Struct", COFF::IMAGE_SYM_TYPE_STRUCT },
+ { "Union" , COFF::IMAGE_SYM_TYPE_UNION },
+ { "Enum" , COFF::IMAGE_SYM_TYPE_ENUM },
+ { "MOE" , COFF::IMAGE_SYM_TYPE_MOE },
+ { "Byte" , COFF::IMAGE_SYM_TYPE_BYTE },
+ { "Word" , COFF::IMAGE_SYM_TYPE_WORD },
+ { "UInt" , COFF::IMAGE_SYM_TYPE_UINT },
+ { "DWord" , COFF::IMAGE_SYM_TYPE_DWORD }
+};
+
+const EnumEntry<COFF::SymbolComplexType> ImageSymDType[] = {
+ { "Null" , COFF::IMAGE_SYM_DTYPE_NULL },
+ { "Pointer" , COFF::IMAGE_SYM_DTYPE_POINTER },
+ { "Function", COFF::IMAGE_SYM_DTYPE_FUNCTION },
+ { "Array" , COFF::IMAGE_SYM_DTYPE_ARRAY }
+};
+
+const EnumEntry<COFF::SymbolStorageClass> ImageSymClass[] = {
+ { "EndOfFunction" , COFF::IMAGE_SYM_CLASS_END_OF_FUNCTION },
+ { "Null" , COFF::IMAGE_SYM_CLASS_NULL },
+ { "Automatic" , COFF::IMAGE_SYM_CLASS_AUTOMATIC },
+ { "External" , COFF::IMAGE_SYM_CLASS_EXTERNAL },
+ { "Static" , COFF::IMAGE_SYM_CLASS_STATIC },
+ { "Register" , COFF::IMAGE_SYM_CLASS_REGISTER },
+ { "ExternalDef" , COFF::IMAGE_SYM_CLASS_EXTERNAL_DEF },
+ { "Label" , COFF::IMAGE_SYM_CLASS_LABEL },
+ { "UndefinedLabel" , COFF::IMAGE_SYM_CLASS_UNDEFINED_LABEL },
+ { "MemberOfStruct" , COFF::IMAGE_SYM_CLASS_MEMBER_OF_STRUCT },
+ { "Argument" , COFF::IMAGE_SYM_CLASS_ARGUMENT },
+ { "StructTag" , COFF::IMAGE_SYM_CLASS_STRUCT_TAG },
+ { "MemberOfUnion" , COFF::IMAGE_SYM_CLASS_MEMBER_OF_UNION },
+ { "UnionTag" , COFF::IMAGE_SYM_CLASS_UNION_TAG },
+ { "TypeDefinition" , COFF::IMAGE_SYM_CLASS_TYPE_DEFINITION },
+ { "UndefinedStatic", COFF::IMAGE_SYM_CLASS_UNDEFINED_STATIC },
+ { "EnumTag" , COFF::IMAGE_SYM_CLASS_ENUM_TAG },
+ { "MemberOfEnum" , COFF::IMAGE_SYM_CLASS_MEMBER_OF_ENUM },
+ { "RegisterParam" , COFF::IMAGE_SYM_CLASS_REGISTER_PARAM },
+ { "BitField" , COFF::IMAGE_SYM_CLASS_BIT_FIELD },
+ { "Block" , COFF::IMAGE_SYM_CLASS_BLOCK },
+ { "Function" , COFF::IMAGE_SYM_CLASS_FUNCTION },
+ { "EndOfStruct" , COFF::IMAGE_SYM_CLASS_END_OF_STRUCT },
+ { "File" , COFF::IMAGE_SYM_CLASS_FILE },
+ { "Section" , COFF::IMAGE_SYM_CLASS_SECTION },
+ { "WeakExternal" , COFF::IMAGE_SYM_CLASS_WEAK_EXTERNAL },
+ { "CLRToken" , COFF::IMAGE_SYM_CLASS_CLR_TOKEN }
+};
+
+const EnumEntry<COFF::COMDATType> ImageCOMDATSelect[] = {
+ { "NoDuplicates", COFF::IMAGE_COMDAT_SELECT_NODUPLICATES },
+ { "Any" , COFF::IMAGE_COMDAT_SELECT_ANY },
+ { "SameSize" , COFF::IMAGE_COMDAT_SELECT_SAME_SIZE },
+ { "ExactMatch" , COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH },
+ { "Associative" , COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE },
+ { "Largest" , COFF::IMAGE_COMDAT_SELECT_LARGEST },
+ { "Newest" , COFF::IMAGE_COMDAT_SELECT_NEWEST }
+};
+
+const EnumEntry<COFF::DebugType> ImageDebugType[] = {
+ {"Unknown", COFF::IMAGE_DEBUG_TYPE_UNKNOWN},
+ {"COFF", COFF::IMAGE_DEBUG_TYPE_COFF},
+ {"CodeView", COFF::IMAGE_DEBUG_TYPE_CODEVIEW},
+ {"FPO", COFF::IMAGE_DEBUG_TYPE_FPO},
+ {"Misc", COFF::IMAGE_DEBUG_TYPE_MISC},
+ {"Exception", COFF::IMAGE_DEBUG_TYPE_EXCEPTION},
+ {"Fixup", COFF::IMAGE_DEBUG_TYPE_FIXUP},
+ {"OmapToSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC},
+ {"OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC},
+ {"Borland", COFF::IMAGE_DEBUG_TYPE_BORLAND},
+ {"Reserved10", COFF::IMAGE_DEBUG_TYPE_RESERVED10},
+ {"CLSID", COFF::IMAGE_DEBUG_TYPE_CLSID},
+ {"VCFeature", COFF::IMAGE_DEBUG_TYPE_VC_FEATURE},
+ {"POGO", COFF::IMAGE_DEBUG_TYPE_POGO},
+ {"ILTCG", COFF::IMAGE_DEBUG_TYPE_ILTCG},
+ {"MPX", COFF::IMAGE_DEBUG_TYPE_MPX},
+ {"Repro", COFF::IMAGE_DEBUG_TYPE_REPRO},
+ {"ExtendedDLLCharacteristics",
+ COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS},
+};
+
+static const EnumEntry<COFF::WeakExternalCharacteristics>
+WeakExternalCharacteristics[] = {
+ { "NoLibrary", COFF::IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY },
+ { "Library" , COFF::IMAGE_WEAK_EXTERN_SEARCH_LIBRARY },
+ { "Alias" , COFF::IMAGE_WEAK_EXTERN_SEARCH_ALIAS }
+};
+
+const EnumEntry<uint32_t> SubSectionTypes[] = {
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, Symbols),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, Lines),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, StringTable),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, FileChecksums),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, FrameData),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, InlineeLines),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, CrossScopeImports),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, CrossScopeExports),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, ILLines),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, FuncMDTokenMap),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, TypeMDTokenMap),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, MergedAssemblyInput),
+ LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, CoffSymbolRVA),
+};
+
+const EnumEntry<uint32_t> FrameDataFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(FrameData, HasSEH),
+ LLVM_READOBJ_ENUM_ENT(FrameData, HasEH),
+ LLVM_READOBJ_ENUM_ENT(FrameData, IsFunctionStart),
+};
+
+const EnumEntry<uint8_t> FileChecksumKindNames[] = {
+ LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, None),
+ LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, MD5),
+ LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, SHA1),
+ LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, SHA256),
+};
+
+template <typename T>
+static std::error_code getSymbolAuxData(const COFFObjectFile *Obj,
+ COFFSymbolRef Symbol,
+ uint8_t AuxSymbolIdx, const T *&Aux) {
+ ArrayRef<uint8_t> AuxData = Obj->getSymbolAuxData(Symbol);
+ AuxData = AuxData.slice(AuxSymbolIdx * Obj->getSymbolTableEntrySize());
+ Aux = reinterpret_cast<const T*>(AuxData.data());
+ return std::error_code();
+}
+
+void COFFDumper::cacheRelocations() {
+ if (RelocCached)
+ return;
+ RelocCached = true;
+
+ for (const SectionRef &S : Obj->sections()) {
+ const coff_section *Section = Obj->getCOFFSection(S);
+
+ append_range(RelocMap[Section], S.relocations());
+
+ // Sort relocations by address.
+ llvm::sort(RelocMap[Section], [](RelocationRef L, RelocationRef R) {
+ return L.getOffset() < R.getOffset();
+ });
+ }
+}
+
+void COFFDumper::printDataDirectory(uint32_t Index,
+ const std::string &FieldName) {
+ const data_directory *Data = Obj->getDataDirectory(Index);
+ if (!Data)
+ return;
+ W.printHex(FieldName + "RVA", Data->RelativeVirtualAddress);
+ W.printHex(FieldName + "Size", Data->Size);
+}
+
+void COFFDumper::printFileHeaders() {
+ time_t TDS = Obj->getTimeDateStamp();
+ char FormattedTime[20] = { };
+ strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS));
+
+ {
+ DictScope D(W, "ImageFileHeader");
+ W.printEnum ("Machine", Obj->getMachine(),
+ makeArrayRef(ImageFileMachineType));
+ W.printNumber("SectionCount", Obj->getNumberOfSections());
+ W.printHex ("TimeDateStamp", FormattedTime, Obj->getTimeDateStamp());
+ W.printHex ("PointerToSymbolTable", Obj->getPointerToSymbolTable());
+ W.printNumber("SymbolCount", Obj->getNumberOfSymbols());
+ W.printNumber("StringTableSize", Obj->getStringTableSize());
+ W.printNumber("OptionalHeaderSize", Obj->getSizeOfOptionalHeader());
+ W.printFlags ("Characteristics", Obj->getCharacteristics(),
+ makeArrayRef(ImageFileCharacteristics));
+ }
+
+ // Print PE header. This header does not exist if this is an object file and
+ // not an executable.
+ if (const pe32_header *PEHeader = Obj->getPE32Header())
+ printPEHeader<pe32_header>(PEHeader);
+
+ if (const pe32plus_header *PEPlusHeader = Obj->getPE32PlusHeader())
+ printPEHeader<pe32plus_header>(PEPlusHeader);
+
+ if (const dos_header *DH = Obj->getDOSHeader())
+ printDOSHeader(DH);
+}
+
+void COFFDumper::printDOSHeader(const dos_header *DH) {
+ DictScope D(W, "DOSHeader");
+ W.printString("Magic", StringRef(DH->Magic, sizeof(DH->Magic)));
+ W.printNumber("UsedBytesInTheLastPage", DH->UsedBytesInTheLastPage);
+ W.printNumber("FileSizeInPages", DH->FileSizeInPages);
+ W.printNumber("NumberOfRelocationItems", DH->NumberOfRelocationItems);
+ W.printNumber("HeaderSizeInParagraphs", DH->HeaderSizeInParagraphs);
+ W.printNumber("MinimumExtraParagraphs", DH->MinimumExtraParagraphs);
+ W.printNumber("MaximumExtraParagraphs", DH->MaximumExtraParagraphs);
+ W.printNumber("InitialRelativeSS", DH->InitialRelativeSS);
+ W.printNumber("InitialSP", DH->InitialSP);
+ W.printNumber("Checksum", DH->Checksum);
+ W.printNumber("InitialIP", DH->InitialIP);
+ W.printNumber("InitialRelativeCS", DH->InitialRelativeCS);
+ W.printNumber("AddressOfRelocationTable", DH->AddressOfRelocationTable);
+ W.printNumber("OverlayNumber", DH->OverlayNumber);
+ W.printNumber("OEMid", DH->OEMid);
+ W.printNumber("OEMinfo", DH->OEMinfo);
+ W.printNumber("AddressOfNewExeHeader", DH->AddressOfNewExeHeader);
+}
+
+template <class PEHeader>
+void COFFDumper::printPEHeader(const PEHeader *Hdr) {
+ DictScope D(W, "ImageOptionalHeader");
+ W.printHex ("Magic", Hdr->Magic);
+ W.printNumber("MajorLinkerVersion", Hdr->MajorLinkerVersion);
+ W.printNumber("MinorLinkerVersion", Hdr->MinorLinkerVersion);
+ W.printNumber("SizeOfCode", Hdr->SizeOfCode);
+ W.printNumber("SizeOfInitializedData", Hdr->SizeOfInitializedData);
+ W.printNumber("SizeOfUninitializedData", Hdr->SizeOfUninitializedData);
+ W.printHex ("AddressOfEntryPoint", Hdr->AddressOfEntryPoint);
+ W.printHex ("BaseOfCode", Hdr->BaseOfCode);
+ printBaseOfDataField(Hdr);
+ W.printHex ("ImageBase", Hdr->ImageBase);
+ W.printNumber("SectionAlignment", Hdr->SectionAlignment);
+ W.printNumber("FileAlignment", Hdr->FileAlignment);
+ W.printNumber("MajorOperatingSystemVersion",
+ Hdr->MajorOperatingSystemVersion);
+ W.printNumber("MinorOperatingSystemVersion",
+ Hdr->MinorOperatingSystemVersion);
+ W.printNumber("MajorImageVersion", Hdr->MajorImageVersion);
+ W.printNumber("MinorImageVersion", Hdr->MinorImageVersion);
+ W.printNumber("MajorSubsystemVersion", Hdr->MajorSubsystemVersion);
+ W.printNumber("MinorSubsystemVersion", Hdr->MinorSubsystemVersion);
+ W.printNumber("SizeOfImage", Hdr->SizeOfImage);
+ W.printNumber("SizeOfHeaders", Hdr->SizeOfHeaders);
+ W.printEnum ("Subsystem", Hdr->Subsystem, makeArrayRef(PEWindowsSubsystem));
+ W.printFlags ("Characteristics", Hdr->DLLCharacteristics,
+ makeArrayRef(PEDLLCharacteristics));
+ W.printNumber("SizeOfStackReserve", Hdr->SizeOfStackReserve);
+ W.printNumber("SizeOfStackCommit", Hdr->SizeOfStackCommit);
+ W.printNumber("SizeOfHeapReserve", Hdr->SizeOfHeapReserve);
+ W.printNumber("SizeOfHeapCommit", Hdr->SizeOfHeapCommit);
+ W.printNumber("NumberOfRvaAndSize", Hdr->NumberOfRvaAndSize);
+
+ if (Hdr->NumberOfRvaAndSize > 0) {
+ DictScope D(W, "DataDirectory");
+ static const char * const directory[] = {
+ "ExportTable", "ImportTable", "ResourceTable", "ExceptionTable",
+ "CertificateTable", "BaseRelocationTable", "Debug", "Architecture",
+ "GlobalPtr", "TLSTable", "LoadConfigTable", "BoundImport", "IAT",
+ "DelayImportDescriptor", "CLRRuntimeHeader", "Reserved"
+ };
+
+ for (uint32_t i = 0; i < Hdr->NumberOfRvaAndSize; ++i)
+ if (i < sizeof(directory) / sizeof(char *))
+ printDataDirectory(i, directory[i]);
+ else
+ printDataDirectory(i, "Unknown");
+ }
+}
+
+void COFFDumper::printCOFFDebugDirectory() {
+ ListScope LS(W, "DebugDirectory");
+ for (const debug_directory &D : Obj->debug_directories()) {
+ char FormattedTime[20] = {};
+ time_t TDS = D.TimeDateStamp;
+ strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS));
+ DictScope S(W, "DebugEntry");
+ W.printHex("Characteristics", D.Characteristics);
+ W.printHex("TimeDateStamp", FormattedTime, D.TimeDateStamp);
+ W.printHex("MajorVersion", D.MajorVersion);
+ W.printHex("MinorVersion", D.MinorVersion);
+ W.printEnum("Type", D.Type, makeArrayRef(ImageDebugType));
+ W.printHex("SizeOfData", D.SizeOfData);
+ W.printHex("AddressOfRawData", D.AddressOfRawData);
+ W.printHex("PointerToRawData", D.PointerToRawData);
+ // Ideally, if D.AddressOfRawData == 0, we should try to load the payload
+ // using D.PointerToRawData instead.
+ if (D.AddressOfRawData == 0)
+ continue;
+ if (D.Type == COFF::IMAGE_DEBUG_TYPE_CODEVIEW) {
+ const codeview::DebugInfo *DebugInfo;
+ StringRef PDBFileName;
+ if (Error E = Obj->getDebugPDBInfo(&D, DebugInfo, PDBFileName))
+ reportError(std::move(E), Obj->getFileName());
+
+ DictScope PDBScope(W, "PDBInfo");
+ W.printHex("PDBSignature", DebugInfo->Signature.CVSignature);
+ if (DebugInfo->Signature.CVSignature == OMF::Signature::PDB70) {
+ W.printBinary("PDBGUID", makeArrayRef(DebugInfo->PDB70.Signature));
+ W.printNumber("PDBAge", DebugInfo->PDB70.Age);
+ W.printString("PDBFileName", PDBFileName);
+ }
+ } else if (D.SizeOfData != 0) {
+ // FIXME: Data visualization for IMAGE_DEBUG_TYPE_VC_FEATURE and
+ // IMAGE_DEBUG_TYPE_POGO?
+ ArrayRef<uint8_t> RawData;
+ if (Error E = Obj->getRvaAndSizeAsBytes(D.AddressOfRawData,
+ D.SizeOfData, RawData))
+ reportError(std::move(E), Obj->getFileName());
+ if (D.Type == COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS) {
+ // FIXME right now the only possible value would fit in 8 bits,
+ // but that might change in the future
+ uint16_t Characteristics = RawData[0];
+ W.printFlags("ExtendedCharacteristics", Characteristics,
+ makeArrayRef(PEExtendedDLLCharacteristics));
+ }
+ W.printBinaryBlock("RawData", RawData);
+ }
+ }
+}
+
+void COFFDumper::printRVATable(uint64_t TableVA, uint64_t Count,
+ uint64_t EntrySize, PrintExtraCB PrintExtra) {
+ uintptr_t TableStart, TableEnd;
+ if (Error E = Obj->getVaPtr(TableVA, TableStart))
+ reportError(std::move(E), Obj->getFileName());
+ if (Error E =
+ Obj->getVaPtr(TableVA + Count * EntrySize - 1, TableEnd))
+ reportError(std::move(E), Obj->getFileName());
+ TableEnd++;
+ for (uintptr_t I = TableStart; I < TableEnd; I += EntrySize) {
+ uint32_t RVA = *reinterpret_cast<const ulittle32_t *>(I);
+ raw_ostream &OS = W.startLine();
+ OS << W.hex(Obj->getImageBase() + RVA);
+ if (PrintExtra)
+ PrintExtra(OS, reinterpret_cast<const uint8_t *>(I));
+ OS << '\n';
+ }
+}
+
+void COFFDumper::printCOFFLoadConfig() {
+ LoadConfigTables Tables;
+ if (Obj->is64())
+ printCOFFLoadConfig(Obj->getLoadConfig64(), Tables);
+ else
+ printCOFFLoadConfig(Obj->getLoadConfig32(), Tables);
+
+ if (Tables.SEHTableVA) {
+ ListScope LS(W, "SEHTable");
+ printRVATable(Tables.SEHTableVA, Tables.SEHTableCount, 4);
+ }
+
+ auto PrintGuardFlags = [](raw_ostream &OS, const uint8_t *Entry) {
+ uint8_t Flags = *reinterpret_cast<const uint8_t *>(Entry + 4);
+ if (Flags)
+ OS << " flags " << utohexstr(Flags);
+ };
+
+ if (Tables.GuardFidTableVA) {
+ ListScope LS(W, "GuardFidTable");
+ if (Tables.GuardFlags & uint32_t(coff_guard_flags::FidTableHasFlags))
+ printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 5,
+ PrintGuardFlags);
+ else
+ printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 4);
+ }
+
+ if (Tables.GuardIatTableVA) {
+ ListScope LS(W, "GuardIatTable");
+ printRVATable(Tables.GuardIatTableVA, Tables.GuardIatTableCount, 4);
+ }
+
+ if (Tables.GuardLJmpTableVA) {
+ ListScope LS(W, "GuardLJmpTable");
+ printRVATable(Tables.GuardLJmpTableVA, Tables.GuardLJmpTableCount, 4);
+ }
+
+ if (Tables.GuardEHContTableVA) {
+ ListScope LS(W, "GuardEHContTable");
+ printRVATable(Tables.GuardEHContTableVA, Tables.GuardEHContTableCount, 5,
+ PrintGuardFlags);
+ }
+}
+
+template <typename T>
+void COFFDumper::printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables) {
+ if (!Conf)
+ return;
+
+ ListScope LS(W, "LoadConfig");
+ char FormattedTime[20] = {};
+ time_t TDS = Conf->TimeDateStamp;
+ strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS));
+ W.printHex("Size", Conf->Size);
+
+ // Print everything before SecurityCookie. The vast majority of images today
+ // have all these fields.
+ if (Conf->Size < offsetof(T, SEHandlerTable))
+ return;
+ W.printHex("TimeDateStamp", FormattedTime, TDS);
+ W.printHex("MajorVersion", Conf->MajorVersion);
+ W.printHex("MinorVersion", Conf->MinorVersion);
+ W.printHex("GlobalFlagsClear", Conf->GlobalFlagsClear);
+ W.printHex("GlobalFlagsSet", Conf->GlobalFlagsSet);
+ W.printHex("CriticalSectionDefaultTimeout",
+ Conf->CriticalSectionDefaultTimeout);
+ W.printHex("DeCommitFreeBlockThreshold", Conf->DeCommitFreeBlockThreshold);
+ W.printHex("DeCommitTotalFreeThreshold", Conf->DeCommitTotalFreeThreshold);
+ W.printHex("LockPrefixTable", Conf->LockPrefixTable);
+ W.printHex("MaximumAllocationSize", Conf->MaximumAllocationSize);
+ W.printHex("VirtualMemoryThreshold", Conf->VirtualMemoryThreshold);
+ W.printHex("ProcessHeapFlags", Conf->ProcessHeapFlags);
+ W.printHex("ProcessAffinityMask", Conf->ProcessAffinityMask);
+ W.printHex("CSDVersion", Conf->CSDVersion);
+ W.printHex("DependentLoadFlags", Conf->DependentLoadFlags);
+ W.printHex("EditList", Conf->EditList);
+ W.printHex("SecurityCookie", Conf->SecurityCookie);
+
+ // Print the safe SEH table if present.
+ if (Conf->Size < offsetof(coff_load_configuration32, GuardCFCheckFunction))
+ return;
+ W.printHex("SEHandlerTable", Conf->SEHandlerTable);
+ W.printNumber("SEHandlerCount", Conf->SEHandlerCount);
+
+ Tables.SEHTableVA = Conf->SEHandlerTable;
+ Tables.SEHTableCount = Conf->SEHandlerCount;
+
+ // Print everything before CodeIntegrity. (2015)
+ if (Conf->Size < offsetof(T, CodeIntegrity))
+ return;
+ W.printHex("GuardCFCheckFunction", Conf->GuardCFCheckFunction);
+ W.printHex("GuardCFCheckDispatch", Conf->GuardCFCheckDispatch);
+ W.printHex("GuardCFFunctionTable", Conf->GuardCFFunctionTable);
+ W.printNumber("GuardCFFunctionCount", Conf->GuardCFFunctionCount);
+ W.printHex("GuardFlags", Conf->GuardFlags);
+
+ Tables.GuardFidTableVA = Conf->GuardCFFunctionTable;
+ Tables.GuardFidTableCount = Conf->GuardCFFunctionCount;
+ Tables.GuardFlags = Conf->GuardFlags;
+
+ // Print everything before Reserved3. (2017)
+ if (Conf->Size < offsetof(T, Reserved3))
+ return;
+ W.printHex("GuardAddressTakenIatEntryTable",
+ Conf->GuardAddressTakenIatEntryTable);
+ W.printNumber("GuardAddressTakenIatEntryCount",
+ Conf->GuardAddressTakenIatEntryCount);
+ W.printHex("GuardLongJumpTargetTable", Conf->GuardLongJumpTargetTable);
+ W.printNumber("GuardLongJumpTargetCount", Conf->GuardLongJumpTargetCount);
+ W.printHex("DynamicValueRelocTable", Conf->DynamicValueRelocTable);
+ W.printHex("CHPEMetadataPointer", Conf->CHPEMetadataPointer);
+ W.printHex("GuardRFFailureRoutine", Conf->GuardRFFailureRoutine);
+ W.printHex("GuardRFFailureRoutineFunctionPointer",
+ Conf->GuardRFFailureRoutineFunctionPointer);
+ W.printHex("DynamicValueRelocTableOffset",
+ Conf->DynamicValueRelocTableOffset);
+ W.printNumber("DynamicValueRelocTableSection",
+ Conf->DynamicValueRelocTableSection);
+ W.printHex("GuardRFVerifyStackPointerFunctionPointer",
+ Conf->GuardRFVerifyStackPointerFunctionPointer);
+ W.printHex("HotPatchTableOffset", Conf->HotPatchTableOffset);
+
+ Tables.GuardIatTableVA = Conf->GuardAddressTakenIatEntryTable;
+ Tables.GuardIatTableCount = Conf->GuardAddressTakenIatEntryCount;
+
+ Tables.GuardLJmpTableVA = Conf->GuardLongJumpTargetTable;
+ Tables.GuardLJmpTableCount = Conf->GuardLongJumpTargetCount;
+
+ // Print the rest. (2019)
+ if (Conf->Size < sizeof(T))
+ return;
+ W.printHex("EnclaveConfigurationPointer", Conf->EnclaveConfigurationPointer);
+ W.printHex("VolatileMetadataPointer", Conf->VolatileMetadataPointer);
+ W.printHex("GuardEHContinuationTable", Conf->GuardEHContinuationTable);
+ W.printNumber("GuardEHContinuationCount", Conf->GuardEHContinuationCount);
+
+ Tables.GuardEHContTableVA = Conf->GuardEHContinuationTable;
+ Tables.GuardEHContTableCount = Conf->GuardEHContinuationCount;
+}
+
+void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) {
+ W.printHex("BaseOfData", Hdr->BaseOfData);
+}
+
+void COFFDumper::printBaseOfDataField(const pe32plus_header *) {}
+
+void COFFDumper::printCodeViewDebugInfo() {
+ // Print types first to build CVUDTNames, then print symbols.
+ for (const SectionRef &S : Obj->sections()) {
+ StringRef SectionName = unwrapOrError(Obj->getFileName(), S.getName());
+ // .debug$T is a standard CodeView type section, while .debug$P is the same
+ // format but used for MSVC precompiled header object files.
+ if (SectionName == ".debug$T" || SectionName == ".debug$P")
+ printCodeViewTypeSection(SectionName, S);
+ }
+ for (const SectionRef &S : Obj->sections()) {
+ StringRef SectionName = unwrapOrError(Obj->getFileName(), S.getName());
+ if (SectionName == ".debug$S")
+ printCodeViewSymbolSection(SectionName, S);
+ }
+}
+
+void COFFDumper::initializeFileAndStringTables(BinaryStreamReader &Reader) {
+ while (Reader.bytesRemaining() > 0 &&
+ (!CVFileChecksumTable.valid() || !CVStringTable.valid())) {
+ // The section consists of a number of subsection in the following format:
+ // |SubSectionType|SubSectionSize|Contents...|
+ uint32_t SubType, SubSectionSize;
+
+ if (Error E = Reader.readInteger(SubType))
+ reportError(std::move(E), Obj->getFileName());
+ if (Error E = Reader.readInteger(SubSectionSize))
+ reportError(std::move(E), Obj->getFileName());
+
+ StringRef Contents;
+ if (Error E = Reader.readFixedString(Contents, SubSectionSize))
+ reportError(std::move(E), Obj->getFileName());
+
+ BinaryStreamRef ST(Contents, support::little);
+ switch (DebugSubsectionKind(SubType)) {
+ case DebugSubsectionKind::FileChecksums:
+ if (Error E = CVFileChecksumTable.initialize(ST))
+ reportError(std::move(E), Obj->getFileName());
+ break;
+ case DebugSubsectionKind::StringTable:
+ if (Error E = CVStringTable.initialize(ST))
+ reportError(std::move(E), Obj->getFileName());
+ break;
+ default:
+ break;
+ }
+
+ uint32_t PaddedSize = alignTo(SubSectionSize, 4);
+ if (Error E = Reader.skip(PaddedSize - SubSectionSize))
+ reportError(std::move(E), Obj->getFileName());
+ }
+}
+
+void COFFDumper::printCodeViewSymbolSection(StringRef SectionName,
+ const SectionRef &Section) {
+ StringRef SectionContents =
+ unwrapOrError(Obj->getFileName(), Section.getContents());
+ StringRef Data = SectionContents;
+
+ SmallVector<StringRef, 10> FunctionNames;
+ StringMap<StringRef> FunctionLineTables;
+
+ ListScope D(W, "CodeViewDebugInfo");
+ // Print the section to allow correlation with printSectionHeaders.
+ W.printNumber("Section", SectionName, Obj->getSectionID(Section));
+
+ uint32_t Magic;
+ if (Error E = consume(Data, Magic))
+ reportError(std::move(E), Obj->getFileName());
+
+ W.printHex("Magic", Magic);
+ if (Magic != COFF::DEBUG_SECTION_MAGIC)
+ reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+
+ BinaryStreamReader FSReader(Data, support::little);
+ initializeFileAndStringTables(FSReader);
+
+ // TODO: Convert this over to using ModuleSubstreamVisitor.
+ while (!Data.empty()) {
+ // The section consists of a number of subsection in the following format:
+ // |SubSectionType|SubSectionSize|Contents...|
+ uint32_t SubType, SubSectionSize;
+ if (Error E = consume(Data, SubType))
+ reportError(std::move(E), Obj->getFileName());
+ if (Error E = consume(Data, SubSectionSize))
+ reportError(std::move(E), Obj->getFileName());
+
+ ListScope S(W, "Subsection");
+ // Dump the subsection as normal even if the ignore bit is set.
+ if (SubType & SubsectionIgnoreFlag) {
+ W.printHex("IgnoredSubsectionKind", SubType);
+ SubType &= ~SubsectionIgnoreFlag;
+ }
+ W.printEnum("SubSectionType", SubType, makeArrayRef(SubSectionTypes));
+ W.printHex("SubSectionSize", SubSectionSize);
+
+ // Get the contents of the subsection.
+ if (SubSectionSize > Data.size())
+ return reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+ StringRef Contents = Data.substr(0, SubSectionSize);
+
+ // Add SubSectionSize to the current offset and align that offset to find
+ // the next subsection.
+ size_t SectionOffset = Data.data() - SectionContents.data();
+ size_t NextOffset = SectionOffset + SubSectionSize;
+ NextOffset = alignTo(NextOffset, 4);
+ if (NextOffset > SectionContents.size())
+ return reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+ Data = SectionContents.drop_front(NextOffset);
+
+ // Optionally print the subsection bytes in case our parsing gets confused
+ // later.
+ if (opts::CodeViewSubsectionBytes)
+ printBinaryBlockWithRelocs("SubSectionContents", Section, SectionContents,
+ Contents);
+
+ switch (DebugSubsectionKind(SubType)) {
+ case DebugSubsectionKind::Symbols:
+ printCodeViewSymbolsSubsection(Contents, Section, SectionContents);
+ break;
+
+ case DebugSubsectionKind::InlineeLines:
+ printCodeViewInlineeLines(Contents);
+ break;
+
+ case DebugSubsectionKind::FileChecksums:
+ printCodeViewFileChecksums(Contents);
+ break;
+
+ case DebugSubsectionKind::Lines: {
+ // Holds a PC to file:line table. Some data to parse this subsection is
+ // stored in the other subsections, so just check sanity and store the
+ // pointers for deferred processing.
+
+ if (SubSectionSize < 12) {
+ // There should be at least three words to store two function
+ // relocations and size of the code.
+ reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+ return;
+ }
+
+ StringRef LinkageName;
+ if (std::error_code EC = resolveSymbolName(Obj->getCOFFSection(Section),
+ SectionOffset, LinkageName))
+ reportError(errorCodeToError(EC), Obj->getFileName());
+
+ W.printString("LinkageName", LinkageName);
+ if (FunctionLineTables.count(LinkageName) != 0) {
+ // Saw debug info for this function already?
+ reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+ return;
+ }
+
+ FunctionLineTables[LinkageName] = Contents;
+ FunctionNames.push_back(LinkageName);
+ break;
+ }
+ case DebugSubsectionKind::FrameData: {
+ // First four bytes is a relocation against the function.
+ BinaryStreamReader SR(Contents, llvm::support::little);
+
+ DebugFrameDataSubsectionRef FrameData;
+ if (Error E = FrameData.initialize(SR))
+ reportError(std::move(E), Obj->getFileName());
+
+ StringRef LinkageName;
+ if (std::error_code EC =
+ resolveSymbolName(Obj->getCOFFSection(Section), SectionContents,
+ FrameData.getRelocPtr(), LinkageName))
+ reportError(errorCodeToError(EC), Obj->getFileName());
+ W.printString("LinkageName", LinkageName);
+
+ // To find the active frame description, search this array for the
+ // smallest PC range that includes the current PC.
+ for (const auto &FD : FrameData) {
+ StringRef FrameFunc = unwrapOrError(
+ Obj->getFileName(), CVStringTable.getString(FD.FrameFunc));
+
+ DictScope S(W, "FrameData");
+ W.printHex("RvaStart", FD.RvaStart);
+ W.printHex("CodeSize", FD.CodeSize);
+ W.printHex("LocalSize", FD.LocalSize);
+ W.printHex("ParamsSize", FD.ParamsSize);
+ W.printHex("MaxStackSize", FD.MaxStackSize);
+ 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;
+ }
+
+ // Do nothing for unrecognized subsections.
+ default:
+ break;
+ }
+ W.flush();
+ }
+
+ // Dump the line tables now that we've read all the subsections and know all
+ // the required information.
+ for (unsigned I = 0, E = FunctionNames.size(); I != E; ++I) {
+ StringRef Name = FunctionNames[I];
+ ListScope S(W, "FunctionLineTable");
+ W.printString("LinkageName", Name);
+
+ BinaryStreamReader Reader(FunctionLineTables[Name], support::little);
+
+ DebugLinesSubsectionRef LineInfo;
+ if (Error E = LineInfo.initialize(Reader))
+ reportError(std::move(E), Obj->getFileName());
+
+ W.printHex("Flags", LineInfo.header()->Flags);
+ W.printHex("CodeSize", LineInfo.header()->CodeSize);
+ for (const auto &Entry : LineInfo) {
+
+ ListScope S(W, "FilenameSegment");
+ printFileNameForOffset("Filename", Entry.NameIndex);
+ uint32_t ColumnIndex = 0;
+ for (const auto &Line : Entry.LineNumbers) {
+ if (Line.Offset >= LineInfo.header()->CodeSize) {
+ reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+ return;
+ }
+
+ std::string PC = std::string(formatv("+{0:X}", uint32_t(Line.Offset)));
+ ListScope PCScope(W, PC);
+ codeview::LineInfo LI(Line.Flags);
+
+ if (LI.isAlwaysStepInto())
+ W.printString("StepInto", StringRef("Always"));
+ else if (LI.isNeverStepInto())
+ W.printString("StepInto", StringRef("Never"));
+ else
+ W.printNumber("LineNumberStart", LI.getStartLine());
+ W.printNumber("LineNumberEndDelta", LI.getLineDelta());
+ W.printBoolean("IsStatement", LI.isStatement());
+ if (LineInfo.hasColumnInfo()) {
+ W.printNumber("ColStart", Entry.Columns[ColumnIndex].StartColumn);
+ W.printNumber("ColEnd", Entry.Columns[ColumnIndex].EndColumn);
+ ++ColumnIndex;
+ }
+ }
+ }
+ }
+}
+
+void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection,
+ const SectionRef &Section,
+ StringRef SectionContents) {
+ ArrayRef<uint8_t> BinaryData(Subsection.bytes_begin(),
+ Subsection.bytes_end());
+ auto CODD = std::make_unique<COFFObjectDumpDelegate>(*this, Section, Obj,
+ SectionContents);
+ CVSymbolDumper CVSD(W, Types, CodeViewContainer::ObjectFile, std::move(CODD),
+ CompilationCPUType, opts::CodeViewSubsectionBytes);
+ CVSymbolArray Symbols;
+ BinaryStreamReader Reader(BinaryData, llvm::support::little);
+ if (Error E = Reader.readArray(Symbols, Reader.getLength())) {
+ W.flush();
+ reportError(std::move(E), Obj->getFileName());
+ }
+
+ if (Error E = CVSD.dump(Symbols)) {
+ W.flush();
+ reportError(std::move(E), Obj->getFileName());
+ }
+ CompilationCPUType = CVSD.getCompilationCPUType();
+ W.flush();
+}
+
+void COFFDumper::printCodeViewFileChecksums(StringRef Subsection) {
+ BinaryStreamRef Stream(Subsection, llvm::support::little);
+ DebugChecksumsSubsectionRef Checksums;
+ if (Error E = Checksums.initialize(Stream))
+ reportError(std::move(E), Obj->getFileName());
+
+ for (auto &FC : Checksums) {
+ DictScope S(W, "FileChecksum");
+
+ StringRef Filename = unwrapOrError(
+ Obj->getFileName(), CVStringTable.getString(FC.FileNameOffset));
+ W.printHex("Filename", Filename, FC.FileNameOffset);
+ W.printHex("ChecksumSize", FC.Checksum.size());
+ W.printEnum("ChecksumKind", uint8_t(FC.Kind),
+ makeArrayRef(FileChecksumKindNames));
+
+ W.printBinary("ChecksumBytes", FC.Checksum);
+ }
+}
+
+void COFFDumper::printCodeViewInlineeLines(StringRef Subsection) {
+ BinaryStreamReader SR(Subsection, llvm::support::little);
+ DebugInlineeLinesSubsectionRef Lines;
+ if (Error E = Lines.initialize(SR))
+ reportError(std::move(E), Obj->getFileName());
+
+ for (auto &Line : Lines) {
+ DictScope S(W, "InlineeSourceLine");
+ printTypeIndex("Inlinee", Line.Header->Inlinee);
+ printFileNameForOffset("FileID", Line.Header->FileID);
+ W.printNumber("SourceLineNum", Line.Header->SourceLineNum);
+
+ if (Lines.hasExtraFiles()) {
+ W.printNumber("ExtraFileCount", Line.ExtraFiles.size());
+ ListScope ExtraFiles(W, "ExtraFiles");
+ for (const auto &FID : Line.ExtraFiles) {
+ printFileNameForOffset("FileID", FID);
+ }
+ }
+ }
+}
+
+StringRef COFFDumper::getFileNameForFileOffset(uint32_t FileOffset) {
+ // The file checksum subsection should precede all references to it.
+ if (!CVFileChecksumTable.valid() || !CVStringTable.valid())
+ reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+
+ auto Iter = CVFileChecksumTable.getArray().at(FileOffset);
+
+ // Check if the file checksum table offset is valid.
+ if (Iter == CVFileChecksumTable.end())
+ reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+
+ return unwrapOrError(Obj->getFileName(),
+ CVStringTable.getString(Iter->FileNameOffset));
+}
+
+void COFFDumper::printFileNameForOffset(StringRef Label, uint32_t FileOffset) {
+ W.printHex(Label, getFileNameForFileOffset(FileOffset), FileOffset);
+}
+
+void COFFDumper::mergeCodeViewTypes(MergingTypeTableBuilder &CVIDs,
+ MergingTypeTableBuilder &CVTypes,
+ GlobalTypeTableBuilder &GlobalCVIDs,
+ GlobalTypeTableBuilder &GlobalCVTypes,
+ bool GHash) {
+ for (const SectionRef &S : Obj->sections()) {
+ StringRef SectionName = unwrapOrError(Obj->getFileName(), S.getName());
+ if (SectionName == ".debug$T") {
+ StringRef Data = unwrapOrError(Obj->getFileName(), S.getContents());
+ uint32_t Magic;
+ if (Error E = consume(Data, Magic))
+ reportError(std::move(E), Obj->getFileName());
+
+ if (Magic != 4)
+ reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+
+ CVTypeArray Types;
+ BinaryStreamReader Reader(Data, llvm::support::little);
+ if (auto EC = Reader.readArray(Types, Reader.getLength())) {
+ consumeError(std::move(EC));
+ W.flush();
+ reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+ }
+ SmallVector<TypeIndex, 128> SourceToDest;
+ Optional<uint32_t> PCHSignature;
+ if (GHash) {
+ std::vector<GloballyHashedType> Hashes =
+ GloballyHashedType::hashTypes(Types);
+ if (Error E =
+ mergeTypeAndIdRecords(GlobalCVIDs, GlobalCVTypes, SourceToDest,
+ Types, Hashes, PCHSignature))
+ return reportError(std::move(E), Obj->getFileName());
+ } else {
+ if (Error E = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types,
+ PCHSignature))
+ return reportError(std::move(E), Obj->getFileName());
+ }
+ }
+ }
+}
+
+void COFFDumper::printCodeViewTypeSection(StringRef SectionName,
+ const SectionRef &Section) {
+ ListScope D(W, "CodeViewTypes");
+ W.printNumber("Section", SectionName, Obj->getSectionID(Section));
+
+ StringRef Data = unwrapOrError(Obj->getFileName(), Section.getContents());
+ if (opts::CodeViewSubsectionBytes)
+ W.printBinaryBlock("Data", Data);
+
+ uint32_t Magic;
+ if (Error E = consume(Data, Magic))
+ reportError(std::move(E), Obj->getFileName());
+
+ W.printHex("Magic", Magic);
+ if (Magic != COFF::DEBUG_SECTION_MAGIC)
+ reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+
+ Types.reset(Data, 100);
+
+ TypeDumpVisitor TDV(Types, &W, opts::CodeViewSubsectionBytes);
+ if (Error E = codeview::visitTypeStream(Types, TDV))
+ reportError(std::move(E), Obj->getFileName());
+
+ W.flush();
+}
+
+void COFFDumper::printSectionHeaders() {
+ ListScope SectionsD(W, "Sections");
+ int SectionNumber = 0;
+ for (const SectionRef &Sec : Obj->sections()) {
+ ++SectionNumber;
+ const coff_section *Section = Obj->getCOFFSection(Sec);
+
+ StringRef Name = unwrapOrError(Obj->getFileName(), Sec.getName());
+
+ DictScope D(W, "Section");
+ W.printNumber("Number", SectionNumber);
+ W.printBinary("Name", Name, Section->Name);
+ W.printHex ("VirtualSize", Section->VirtualSize);
+ W.printHex ("VirtualAddress", Section->VirtualAddress);
+ W.printNumber("RawDataSize", Section->SizeOfRawData);
+ W.printHex ("PointerToRawData", Section->PointerToRawData);
+ W.printHex ("PointerToRelocations", Section->PointerToRelocations);
+ W.printHex ("PointerToLineNumbers", Section->PointerToLinenumbers);
+ W.printNumber("RelocationCount", Section->NumberOfRelocations);
+ W.printNumber("LineNumberCount", Section->NumberOfLinenumbers);
+ W.printFlags ("Characteristics", Section->Characteristics,
+ makeArrayRef(ImageSectionCharacteristics),
+ COFF::SectionCharacteristics(0x00F00000));
+
+ if (opts::SectionRelocations) {
+ ListScope D(W, "Relocations");
+ for (const RelocationRef &Reloc : Sec.relocations())
+ printRelocation(Sec, Reloc);
+ }
+
+ if (opts::SectionSymbols) {
+ ListScope D(W, "Symbols");
+ for (const SymbolRef &Symbol : Obj->symbols()) {
+ if (!Sec.containsSymbol(Symbol))
+ continue;
+
+ printSymbol(Symbol);
+ }
+ }
+
+ if (opts::SectionData &&
+ !(Section->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)) {
+ StringRef Data = unwrapOrError(Obj->getFileName(), Sec.getContents());
+ W.printBinaryBlock("SectionData", Data);
+ }
+ }
+}
+
+void COFFDumper::printRelocations() {
+ ListScope D(W, "Relocations");
+
+ int SectionNumber = 0;
+ for (const SectionRef &Section : Obj->sections()) {
+ ++SectionNumber;
+ StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName());
+
+ bool PrintedGroup = false;
+ for (const RelocationRef &Reloc : Section.relocations()) {
+ if (!PrintedGroup) {
+ W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n";
+ W.indent();
+ PrintedGroup = true;
+ }
+
+ printRelocation(Section, Reloc);
+ }
+
+ if (PrintedGroup) {
+ W.unindent();
+ W.startLine() << "}\n";
+ }
+ }
+}
+
+void COFFDumper::printRelocation(const SectionRef &Section,
+ const RelocationRef &Reloc, uint64_t Bias) {
+ uint64_t Offset = Reloc.getOffset() - Bias;
+ uint64_t RelocType = Reloc.getType();
+ SmallString<32> RelocName;
+ StringRef SymbolName;
+ Reloc.getTypeName(RelocName);
+ symbol_iterator Symbol = Reloc.getSymbol();
+ int64_t SymbolIndex = -1;
+ if (Symbol != Obj->symbol_end()) {
+ Expected<StringRef> SymbolNameOrErr = Symbol->getName();
+ if (!SymbolNameOrErr)
+ reportError(SymbolNameOrErr.takeError(), Obj->getFileName());
+
+ SymbolName = *SymbolNameOrErr;
+ SymbolIndex = Obj->getSymbolIndex(Obj->getCOFFSymbol(*Symbol));
+ }
+
+ if (opts::ExpandRelocs) {
+ DictScope Group(W, "Relocation");
+ 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";
+ }
+}
+
+void COFFDumper::printSymbols() {
+ ListScope Group(W, "Symbols");
+
+ for (const SymbolRef &Symbol : Obj->symbols())
+ printSymbol(Symbol);
+}
+
+void COFFDumper::printDynamicSymbols() { ListScope Group(W, "DynamicSymbols"); }
+
+static Expected<StringRef>
+getSectionName(const llvm::object::COFFObjectFile *Obj, int32_t SectionNumber,
+ const coff_section *Section) {
+ if (Section)
+ return Obj->getSectionName(Section);
+ if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
+ return StringRef("IMAGE_SYM_DEBUG");
+ if (SectionNumber == llvm::COFF::IMAGE_SYM_ABSOLUTE)
+ return StringRef("IMAGE_SYM_ABSOLUTE");
+ if (SectionNumber == llvm::COFF::IMAGE_SYM_UNDEFINED)
+ return StringRef("IMAGE_SYM_UNDEFINED");
+ return StringRef("");
+}
+
+void COFFDumper::printSymbol(const SymbolRef &Sym) {
+ DictScope D(W, "Symbol");
+
+ COFFSymbolRef Symbol = Obj->getCOFFSymbol(Sym);
+ Expected<const coff_section *> SecOrErr =
+ Obj->getSection(Symbol.getSectionNumber());
+ if (!SecOrErr) {
+ W.startLine() << "Invalid section number: " << Symbol.getSectionNumber()
+ << "\n";
+ W.flush();
+ consumeError(SecOrErr.takeError());
+ return;
+ }
+ const coff_section *Section = *SecOrErr;
+
+ StringRef SymbolName;
+ if (Expected<StringRef> SymNameOrErr = Obj->getSymbolName(Symbol))
+ SymbolName = *SymNameOrErr;
+
+ StringRef SectionName;
+ if (Expected<StringRef> SecNameOrErr =
+ getSectionName(Obj, Symbol.getSectionNumber(), Section))
+ SectionName = *SecNameOrErr;
+
+ W.printString("Name", SymbolName);
+ W.printNumber("Value", Symbol.getValue());
+ W.printNumber("Section", SectionName, Symbol.getSectionNumber());
+ W.printEnum ("BaseType", Symbol.getBaseType(), makeArrayRef(ImageSymType));
+ W.printEnum ("ComplexType", Symbol.getComplexType(),
+ makeArrayRef(ImageSymDType));
+ W.printEnum ("StorageClass", Symbol.getStorageClass(),
+ makeArrayRef(ImageSymClass));
+ W.printNumber("AuxSymbolCount", Symbol.getNumberOfAuxSymbols());
+
+ for (uint8_t I = 0; I < Symbol.getNumberOfAuxSymbols(); ++I) {
+ if (Symbol.isFunctionDefinition()) {
+ const coff_aux_function_definition *Aux;
+ if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux))
+ reportError(errorCodeToError(EC), Obj->getFileName());
+
+ DictScope AS(W, "AuxFunctionDef");
+ W.printNumber("TagIndex", Aux->TagIndex);
+ W.printNumber("TotalSize", Aux->TotalSize);
+ W.printHex("PointerToLineNumber", Aux->PointerToLinenumber);
+ W.printHex("PointerToNextFunction", Aux->PointerToNextFunction);
+
+ } else if (Symbol.isAnyUndefined()) {
+ const coff_aux_weak_external *Aux;
+ if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux))
+ reportError(errorCodeToError(EC), Obj->getFileName());
+
+ DictScope AS(W, "AuxWeakExternal");
+ W.printNumber("Linked", getSymbolName(Aux->TagIndex), Aux->TagIndex);
+ W.printEnum ("Search", Aux->Characteristics,
+ makeArrayRef(WeakExternalCharacteristics));
+
+ } else if (Symbol.isFileRecord()) {
+ const char *FileName;
+ if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, FileName))
+ reportError(errorCodeToError(EC), Obj->getFileName());
+ DictScope AS(W, "AuxFileRecord");
+
+ StringRef Name(FileName, Symbol.getNumberOfAuxSymbols() *
+ Obj->getSymbolTableEntrySize());
+ W.printString("FileName", Name.rtrim(StringRef("\0", 1)));
+ break;
+ } else if (Symbol.isSectionDefinition()) {
+ const coff_aux_section_definition *Aux;
+ if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux))
+ reportError(errorCodeToError(EC), Obj->getFileName());
+
+ int32_t AuxNumber = Aux->getNumber(Symbol.isBigObj());
+
+ DictScope AS(W, "AuxSectionDef");
+ W.printNumber("Length", Aux->Length);
+ W.printNumber("RelocationCount", Aux->NumberOfRelocations);
+ W.printNumber("LineNumberCount", Aux->NumberOfLinenumbers);
+ W.printHex("Checksum", Aux->CheckSum);
+ W.printNumber("Number", AuxNumber);
+ W.printEnum("Selection", Aux->Selection, makeArrayRef(ImageCOMDATSelect));
+
+ if (Section && Section->Characteristics & COFF::IMAGE_SCN_LNK_COMDAT
+ && Aux->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
+ Expected<const coff_section *> Assoc = Obj->getSection(AuxNumber);
+ if (!Assoc)
+ reportError(Assoc.takeError(), Obj->getFileName());
+ Expected<StringRef> AssocName = getSectionName(Obj, AuxNumber, *Assoc);
+ if (!AssocName)
+ reportError(AssocName.takeError(), Obj->getFileName());
+
+ W.printNumber("AssocSection", *AssocName, AuxNumber);
+ }
+ } else if (Symbol.isCLRToken()) {
+ const coff_aux_clr_token *Aux;
+ if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux))
+ reportError(errorCodeToError(EC), Obj->getFileName());
+
+ DictScope AS(W, "AuxCLRToken");
+ W.printNumber("AuxType", Aux->AuxType);
+ W.printNumber("Reserved", Aux->Reserved);
+ W.printNumber("SymbolTableIndex", getSymbolName(Aux->SymbolTableIndex),
+ Aux->SymbolTableIndex);
+
+ } else {
+ W.startLine() << "<unhandled auxiliary record>\n";
+ }
+ }
+}
+
+void COFFDumper::printUnwindInfo() {
+ ListScope D(W, "UnwindInformation");
+ switch (Obj->getMachine()) {
+ case COFF::IMAGE_FILE_MACHINE_AMD64: {
+ Win64EH::Dumper Dumper(W);
+ Win64EH::Dumper::SymbolResolver
+ Resolver = [](const object::coff_section *Section, uint64_t Offset,
+ SymbolRef &Symbol, void *user_data) -> std::error_code {
+ COFFDumper *Dumper = reinterpret_cast<COFFDumper *>(user_data);
+ return Dumper->resolveSymbol(Section, Offset, Symbol);
+ };
+ Win64EH::Dumper::Context Ctx(*Obj, Resolver, this);
+ Dumper.printData(Ctx);
+ break;
+ }
+ case COFF::IMAGE_FILE_MACHINE_ARM64:
+ case COFF::IMAGE_FILE_MACHINE_ARMNT: {
+ ARM::WinEH::Decoder Decoder(W, Obj->getMachine() ==
+ COFF::IMAGE_FILE_MACHINE_ARM64);
+ // TODO Propagate the error.
+ consumeError(Decoder.dumpProcedureData(*Obj));
+ break;
+ }
+ default:
+ W.printEnum("unsupported Image Machine", Obj->getMachine(),
+ makeArrayRef(ImageFileMachineType));
+ break;
+ }
+}
+
+void COFFDumper::printNeededLibraries() {
+ ListScope D(W, "NeededLibraries");
+
+ using LibsTy = std::vector<StringRef>;
+ LibsTy Libs;
+
+ for (const ImportDirectoryEntryRef &DirRef : Obj->import_directories()) {
+ StringRef Name;
+ if (!DirRef.getName(Name))
+ Libs.push_back(Name);
+ }
+
+ llvm::stable_sort(Libs);
+
+ for (const auto &L : Libs) {
+ W.startLine() << L << "\n";
+ }
+}
+
+void COFFDumper::printImportedSymbols(
+ iterator_range<imported_symbol_iterator> Range) {
+ for (const ImportedSymbolRef &I : Range) {
+ StringRef Sym;
+ if (Error E = I.getSymbolName(Sym))
+ reportError(std::move(E), Obj->getFileName());
+ uint16_t Ordinal;
+ if (Error E = I.getOrdinal(Ordinal))
+ reportError(std::move(E), Obj->getFileName());
+ W.printNumber("Symbol", Sym, Ordinal);
+ }
+}
+
+void COFFDumper::printDelayImportedSymbols(
+ const DelayImportDirectoryEntryRef &I,
+ iterator_range<imported_symbol_iterator> Range) {
+ int Index = 0;
+ for (const ImportedSymbolRef &S : Range) {
+ DictScope Import(W, "Import");
+ StringRef Sym;
+ if (Error E = S.getSymbolName(Sym))
+ reportError(std::move(E), Obj->getFileName());
+
+ uint16_t Ordinal;
+ if (Error E = S.getOrdinal(Ordinal))
+ reportError(std::move(E), Obj->getFileName());
+ W.printNumber("Symbol", Sym, Ordinal);
+
+ uint64_t Addr;
+ if (Error E = I.getImportAddress(Index++, Addr))
+ reportError(std::move(E), Obj->getFileName());
+ W.printHex("Address", Addr);
+ }
+}
+
+void COFFDumper::printCOFFImports() {
+ // Regular imports
+ for (const ImportDirectoryEntryRef &I : Obj->import_directories()) {
+ DictScope Import(W, "Import");
+ StringRef Name;
+ if (Error E = I.getName(Name))
+ reportError(std::move(E), Obj->getFileName());
+ W.printString("Name", Name);
+ uint32_t ILTAddr;
+ if (Error E = I.getImportLookupTableRVA(ILTAddr))
+ reportError(std::move(E), Obj->getFileName());
+ W.printHex("ImportLookupTableRVA", ILTAddr);
+ uint32_t IATAddr;
+ if (Error E = I.getImportAddressTableRVA(IATAddr))
+ reportError(std::move(E), Obj->getFileName());
+ W.printHex("ImportAddressTableRVA", IATAddr);
+ // The import lookup table can be missing with certain older linkers, so
+ // fall back to the import address table in that case.
+ if (ILTAddr)
+ printImportedSymbols(I.lookup_table_symbols());
+ else
+ printImportedSymbols(I.imported_symbols());
+ }
+
+ // Delay imports
+ for (const DelayImportDirectoryEntryRef &I : Obj->delay_import_directories()) {
+ DictScope Import(W, "DelayImport");
+ StringRef Name;
+ if (Error E = I.getName(Name))
+ reportError(std::move(E), Obj->getFileName());
+ W.printString("Name", Name);
+ const delay_import_directory_table_entry *Table;
+ if (Error E = I.getDelayImportTable(Table))
+ reportError(std::move(E), Obj->getFileName());
+ W.printHex("Attributes", Table->Attributes);
+ W.printHex("ModuleHandle", Table->ModuleHandle);
+ W.printHex("ImportAddressTable", Table->DelayImportAddressTable);
+ W.printHex("ImportNameTable", Table->DelayImportNameTable);
+ W.printHex("BoundDelayImportTable", Table->BoundDelayImportTable);
+ W.printHex("UnloadDelayImportTable", Table->UnloadDelayImportTable);
+ printDelayImportedSymbols(I, I.imported_symbols());
+ }
+}
+
+void COFFDumper::printCOFFExports() {
+ for (const ExportDirectoryEntryRef &Exp : Obj->export_directories()) {
+ DictScope Export(W, "Export");
+
+ StringRef Name;
+ uint32_t Ordinal, RVA;
+
+ if (Error E = Exp.getSymbolName(Name))
+ reportError(std::move(E), Obj->getFileName());
+ if (Error E = Exp.getOrdinal(Ordinal))
+ reportError(std::move(E), Obj->getFileName());
+ if (Error E = Exp.getExportRVA(RVA))
+ reportError(std::move(E), Obj->getFileName());
+
+ W.printNumber("Ordinal", Ordinal);
+ W.printString("Name", Name);
+ W.printHex("RVA", RVA);
+ }
+}
+
+void COFFDumper::printCOFFDirectives() {
+ for (const SectionRef &Section : Obj->sections()) {
+ StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName());
+ if (Name != ".drectve")
+ continue;
+
+ StringRef Contents =
+ unwrapOrError(Obj->getFileName(), Section.getContents());
+ W.printString("Directive(s)", Contents);
+ }
+}
+
+static std::string getBaseRelocTypeName(uint8_t Type) {
+ switch (Type) {
+ case COFF::IMAGE_REL_BASED_ABSOLUTE: return "ABSOLUTE";
+ case COFF::IMAGE_REL_BASED_HIGH: return "HIGH";
+ case COFF::IMAGE_REL_BASED_LOW: return "LOW";
+ case COFF::IMAGE_REL_BASED_HIGHLOW: return "HIGHLOW";
+ case COFF::IMAGE_REL_BASED_HIGHADJ: return "HIGHADJ";
+ case COFF::IMAGE_REL_BASED_ARM_MOV32T: return "ARM_MOV32(T)";
+ case COFF::IMAGE_REL_BASED_DIR64: return "DIR64";
+ default: return "unknown (" + llvm::utostr(Type) + ")";
+ }
+}
+
+void COFFDumper::printCOFFBaseReloc() {
+ ListScope D(W, "BaseReloc");
+ for (const BaseRelocRef &I : Obj->base_relocs()) {
+ uint8_t Type;
+ uint32_t RVA;
+ if (Error E = I.getRVA(RVA))
+ reportError(std::move(E), Obj->getFileName());
+ if (Error E = I.getType(Type))
+ reportError(std::move(E), Obj->getFileName());
+ DictScope Import(W, "Entry");
+ W.printString("Type", getBaseRelocTypeName(Type));
+ W.printHex("Address", RVA);
+ }
+}
+
+void COFFDumper::printCOFFResources() {
+ ListScope ResourcesD(W, "Resources");
+ for (const SectionRef &S : Obj->sections()) {
+ StringRef Name = unwrapOrError(Obj->getFileName(), S.getName());
+ if (!Name.startswith(".rsrc"))
+ continue;
+
+ StringRef Ref = unwrapOrError(Obj->getFileName(), S.getContents());
+
+ if ((Name == ".rsrc") || (Name == ".rsrc$01")) {
+ ResourceSectionRef RSF;
+ Error E = RSF.load(Obj, S);
+ if (E)
+ reportError(std::move(E), Obj->getFileName());
+ auto &BaseTable = unwrapOrError(Obj->getFileName(), RSF.getBaseTable());
+ W.printNumber("Total Number of Resources",
+ countTotalTableEntries(RSF, BaseTable, "Type"));
+ W.printHex("Base Table Address",
+ Obj->getCOFFSection(S)->PointerToRawData);
+ W.startLine() << "\n";
+ printResourceDirectoryTable(RSF, BaseTable, "Type");
+ }
+ if (opts::SectionData)
+ W.printBinaryBlock(Name.str() + " Data", Ref);
+ }
+}
+
+uint32_t
+COFFDumper::countTotalTableEntries(ResourceSectionRef RSF,
+ const coff_resource_dir_table &Table,
+ StringRef Level) {
+ uint32_t TotalEntries = 0;
+ for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries;
+ i++) {
+ auto Entry = unwrapOrError(Obj->getFileName(), RSF.getTableEntry(Table, i));
+ if (Entry.Offset.isSubDir()) {
+ StringRef NextLevel;
+ if (Level == "Name")
+ NextLevel = "Language";
+ else
+ NextLevel = "Name";
+ auto &NextTable =
+ unwrapOrError(Obj->getFileName(), RSF.getEntrySubDir(Entry));
+ TotalEntries += countTotalTableEntries(RSF, NextTable, NextLevel);
+ } else {
+ TotalEntries += 1;
+ }
+ }
+ return TotalEntries;
+}
+
+void COFFDumper::printResourceDirectoryTable(
+ ResourceSectionRef RSF, const coff_resource_dir_table &Table,
+ StringRef Level) {
+
+ W.printNumber("Number of String Entries", Table.NumberOfNameEntries);
+ W.printNumber("Number of ID Entries", Table.NumberOfIDEntries);
+
+ // Iterate through level in resource directory tree.
+ for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries;
+ i++) {
+ auto Entry = unwrapOrError(Obj->getFileName(), RSF.getTableEntry(Table, i));
+ StringRef Name;
+ SmallString<20> IDStr;
+ raw_svector_ostream OS(IDStr);
+ if (i < Table.NumberOfNameEntries) {
+ ArrayRef<UTF16> RawEntryNameString =
+ unwrapOrError(Obj->getFileName(), RSF.getEntryNameString(Entry));
+ std::vector<UTF16> EndianCorrectedNameString;
+ if (llvm::sys::IsBigEndianHost) {
+ EndianCorrectedNameString.resize(RawEntryNameString.size() + 1);
+ std::copy(RawEntryNameString.begin(), RawEntryNameString.end(),
+ EndianCorrectedNameString.begin() + 1);
+ EndianCorrectedNameString[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED;
+ RawEntryNameString = makeArrayRef(EndianCorrectedNameString);
+ }
+ std::string EntryNameString;
+ if (!llvm::convertUTF16ToUTF8String(RawEntryNameString, EntryNameString))
+ reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+ OS << ": ";
+ OS << EntryNameString;
+ } else {
+ if (Level == "Type") {
+ OS << ": ";
+ printResourceTypeName(Entry.Identifier.ID, OS);
+ } else {
+ OS << ": (ID " << Entry.Identifier.ID << ")";
+ }
+ }
+ Name = IDStr;
+ ListScope ResourceType(W, Level.str() + Name.str());
+ if (Entry.Offset.isSubDir()) {
+ W.printHex("Table Offset", Entry.Offset.value());
+ StringRef NextLevel;
+ if (Level == "Name")
+ NextLevel = "Language";
+ else
+ NextLevel = "Name";
+ auto &NextTable =
+ unwrapOrError(Obj->getFileName(), RSF.getEntrySubDir(Entry));
+ printResourceDirectoryTable(RSF, NextTable, NextLevel);
+ } else {
+ W.printHex("Entry Offset", Entry.Offset.value());
+ char FormattedTime[20] = {};
+ time_t TDS = time_t(Table.TimeDateStamp);
+ strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS));
+ W.printHex("Time/Date Stamp", FormattedTime, Table.TimeDateStamp);
+ W.printNumber("Major Version", Table.MajorVersion);
+ W.printNumber("Minor Version", Table.MinorVersion);
+ W.printNumber("Characteristics", Table.Characteristics);
+ ListScope DataScope(W, "Data");
+ auto &DataEntry =
+ unwrapOrError(Obj->getFileName(), RSF.getEntryData(Entry));
+ W.printHex("DataRVA", DataEntry.DataRVA);
+ W.printNumber("DataSize", DataEntry.DataSize);
+ W.printNumber("Codepage", DataEntry.Codepage);
+ W.printNumber("Reserved", DataEntry.Reserved);
+ StringRef Contents =
+ unwrapOrError(Obj->getFileName(), RSF.getContents(DataEntry));
+ W.printBinaryBlock("Data", Contents);
+ }
+ }
+}
+
+void COFFDumper::printStackMap() const {
+ SectionRef StackMapSection;
+ for (auto Sec : Obj->sections()) {
+ StringRef Name;
+ if (Expected<StringRef> NameOrErr = Sec.getName())
+ Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ if (Name == ".llvm_stackmaps") {
+ StackMapSection = Sec;
+ break;
+ }
+ }
+
+ if (StackMapSection == SectionRef())
+ return;
+
+ StringRef StackMapContents =
+ unwrapOrError(Obj->getFileName(), StackMapSection.getContents());
+ ArrayRef<uint8_t> StackMapContentsArray =
+ arrayRefFromStringRef(StackMapContents);
+
+ if (Obj->isLittleEndian())
+ prettyPrintStackMap(
+ W, StackMapParser<support::little>(StackMapContentsArray));
+ else
+ prettyPrintStackMap(
+ W, StackMapParser<support::big>(StackMapContentsArray));
+}
+
+void COFFDumper::printAddrsig() {
+ SectionRef AddrsigSection;
+ for (auto Sec : Obj->sections()) {
+ StringRef Name;
+ if (Expected<StringRef> NameOrErr = Sec.getName())
+ Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ if (Name == ".llvm_addrsig") {
+ AddrsigSection = Sec;
+ break;
+ }
+ }
+
+ if (AddrsigSection == SectionRef())
+ return;
+
+ StringRef AddrsigContents =
+ unwrapOrError(Obj->getFileName(), AddrsigSection.getContents());
+ ArrayRef<uint8_t> AddrsigContentsArray(AddrsigContents.bytes_begin(),
+ AddrsigContents.size());
+
+ ListScope L(W, "Addrsig");
+ const uint8_t *Cur = AddrsigContents.bytes_begin();
+ const uint8_t *End = AddrsigContents.bytes_end();
+ while (Cur != End) {
+ unsigned Size;
+ const char *Err;
+ uint64_t SymIndex = decodeULEB128(Cur, &Size, End, &Err);
+ if (Err)
+ reportError(createError(Err), Obj->getFileName());
+
+ W.printNumber("Sym", getSymbolName(SymIndex), SymIndex);
+ Cur += Size;
+ }
+}
+
+void COFFDumper::printCGProfile() {
+ SectionRef CGProfileSection;
+ for (SectionRef Sec : Obj->sections()) {
+ StringRef Name = unwrapOrError(Obj->getFileName(), Sec.getName());
+ if (Name == ".llvm.call-graph-profile") {
+ CGProfileSection = Sec;
+ break;
+ }
+ }
+
+ if (CGProfileSection == SectionRef())
+ return;
+
+ StringRef CGProfileContents =
+ unwrapOrError(Obj->getFileName(), CGProfileSection.getContents());
+ BinaryStreamReader Reader(CGProfileContents, llvm::support::little);
+
+ ListScope L(W, "CGProfile");
+ while (!Reader.empty()) {
+ uint32_t FromIndex, ToIndex;
+ uint64_t Count;
+ if (Error Err = Reader.readInteger(FromIndex))
+ reportError(std::move(Err), Obj->getFileName());
+ if (Error Err = Reader.readInteger(ToIndex))
+ reportError(std::move(Err), Obj->getFileName());
+ if (Error Err = Reader.readInteger(Count))
+ reportError(std::move(Err), Obj->getFileName());
+
+ DictScope D(W, "CGProfileEntry");
+ W.printNumber("From", getSymbolName(FromIndex), FromIndex);
+ W.printNumber("To", getSymbolName(ToIndex), ToIndex);
+ W.printNumber("Weight", Count);
+ }
+}
+
+StringRef COFFDumper::getSymbolName(uint32_t Index) {
+ Expected<COFFSymbolRef> Sym = Obj->getSymbol(Index);
+ if (!Sym)
+ reportError(Sym.takeError(), Obj->getFileName());
+
+ Expected<StringRef> SymName = Obj->getSymbolName(*Sym);
+ if (!SymName)
+ reportError(SymName.takeError(), Obj->getFileName());
+
+ return *SymName;
+}
+
+void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer,
+ ArrayRef<ArrayRef<uint8_t>> IpiRecords,
+ ArrayRef<ArrayRef<uint8_t>> TpiRecords) {
+ TypeTableCollection TpiTypes(TpiRecords);
+ {
+ ListScope S(Writer, "MergedTypeStream");
+ TypeDumpVisitor TDV(TpiTypes, &Writer, opts::CodeViewSubsectionBytes);
+ if (Error Err = codeview::visitTypeStream(TpiTypes, TDV))
+ reportError(std::move(Err), "<?>");
+ Writer.flush();
+ }
+
+ // Flatten the id stream and print it next. The ID stream refers to names from
+ // the type stream.
+ TypeTableCollection IpiTypes(IpiRecords);
+ {
+ ListScope S(Writer, "MergedIDStream");
+ TypeDumpVisitor TDV(TpiTypes, &Writer, opts::CodeViewSubsectionBytes);
+ TDV.setIpiTypes(IpiTypes);
+ if (Error Err = codeview::visitTypeStream(IpiTypes, TDV))
+ reportError(std::move(Err), "<?>");
+ Writer.flush();
+ }
+}
+
+void COFFDumper::printCOFFTLSDirectory() {
+ if (Obj->is64())
+ printCOFFTLSDirectory(Obj->getTLSDirectory64());
+ else
+ printCOFFTLSDirectory(Obj->getTLSDirectory32());
+}
+
+template <typename IntTy>
+void COFFDumper::printCOFFTLSDirectory(
+ const coff_tls_directory<IntTy> *TlsTable) {
+ DictScope D(W, "TLSDirectory");
+ if (!TlsTable)
+ return;
+
+ W.printHex("StartAddressOfRawData", TlsTable->StartAddressOfRawData);
+ W.printHex("EndAddressOfRawData", TlsTable->EndAddressOfRawData);
+ W.printHex("AddressOfIndex", TlsTable->AddressOfIndex);
+ W.printHex("AddressOfCallBacks", TlsTable->AddressOfCallBacks);
+ W.printHex("SizeOfZeroFill", TlsTable->SizeOfZeroFill);
+ W.printFlags("Characteristics", TlsTable->Characteristics,
+ makeArrayRef(ImageSectionCharacteristics),
+ COFF::SectionCharacteristics(COFF::IMAGE_SCN_ALIGN_MASK));
+}
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/COFFImportDumper.cpp b/contrib/libs/llvm14/tools/llvm-readobj/COFFImportDumper.cpp
new file mode 100644
index 00000000000..c9d5e82263d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/COFFImportDumper.cpp
@@ -0,0 +1,58 @@
+//===-- COFFImportDumper.cpp - COFF import library dumper -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the COFF import library dumper for llvm-readobj.
+///
+//===----------------------------------------------------------------------===//
+
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/COFFImportFile.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+using namespace llvm::object;
+
+namespace llvm {
+
+void dumpCOFFImportFile(const COFFImportFile *File, ScopedPrinter &Writer) {
+ Writer.startLine() << '\n';
+ Writer.printString("File", File->getFileName());
+ Writer.printString("Format", "COFF-import-file");
+
+ const coff_import_header *H = File->getCOFFImportHeader();
+ switch (H->getType()) {
+ case COFF::IMPORT_CODE: Writer.printString("Type", "code"); break;
+ case COFF::IMPORT_DATA: Writer.printString("Type", "data"); break;
+ case COFF::IMPORT_CONST: Writer.printString("Type", "const"); break;
+ }
+
+ switch (H->getNameType()) {
+ case COFF::IMPORT_ORDINAL:
+ Writer.printString("Name type", "ordinal");
+ break;
+ case COFF::IMPORT_NAME:
+ Writer.printString("Name type", "name");
+ break;
+ case COFF::IMPORT_NAME_NOPREFIX:
+ Writer.printString("Name type", "noprefix");
+ break;
+ case COFF::IMPORT_NAME_UNDECORATE:
+ Writer.printString("Name type", "undecorate");
+ break;
+ }
+
+ for (const object::BasicSymbolRef &Sym : File->symbols()) {
+ raw_ostream &OS = Writer.startLine();
+ OS << "Symbol: ";
+ cantFail(Sym.printName(OS));
+ OS << "\n";
+ }
+}
+
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/DwarfCFIEHPrinter.h b/contrib/libs/llvm14/tools/llvm-readobj/DwarfCFIEHPrinter.h
new file mode 100644
index 00000000000..5dc947e024b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/DwarfCFIEHPrinter.h
@@ -0,0 +1,241 @@
+//===--- DwarfCFIEHPrinter.h - DWARF-based Unwind Information Printer -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H
+#define LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H
+
+#include "llvm-readobj.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/type_traits.h"
+
+namespace llvm {
+namespace DwarfCFIEH {
+
+template <typename ELFT> class PrinterContext {
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Phdr = typename ELFT::Phdr;
+
+ ScopedPrinter &W;
+ const object::ELFObjectFile<ELFT> &ObjF;
+
+ void printEHFrameHdr(const Elf_Phdr *EHFramePHdr) const;
+ void printEHFrame(const Elf_Shdr *EHFrameShdr) const;
+
+public:
+ PrinterContext(ScopedPrinter &W, const object::ELFObjectFile<ELFT> &ObjF)
+ : W(W), ObjF(ObjF) {}
+
+ void printUnwindInformation() const;
+};
+
+template <class ELFT>
+static const typename ELFT::Shdr *
+findSectionByAddress(const object::ELFObjectFile<ELFT> &ObjF, uint64_t Addr) {
+ Expected<typename ELFT::ShdrRange> SectionsOrErr =
+ ObjF.getELFFile().sections();
+ if (!SectionsOrErr)
+ reportError(SectionsOrErr.takeError(), ObjF.getFileName());
+
+ for (const typename ELFT::Shdr &Shdr : *SectionsOrErr)
+ if (Shdr.sh_addr == Addr)
+ return &Shdr;
+ return nullptr;
+}
+
+template <typename ELFT>
+void PrinterContext<ELFT>::printUnwindInformation() const {
+ const object::ELFFile<ELFT> &Obj = ObjF.getELFFile();
+
+ Expected<typename ELFT::PhdrRange> PhdrsOrErr = Obj.program_headers();
+ if (!PhdrsOrErr)
+ reportError(PhdrsOrErr.takeError(), ObjF.getFileName());
+
+ for (const Elf_Phdr &Phdr : *PhdrsOrErr) {
+ if (Phdr.p_type != ELF::PT_GNU_EH_FRAME)
+ continue;
+
+ if (Phdr.p_memsz != Phdr.p_filesz)
+ reportError(object::createError(
+ "p_memsz does not match p_filesz for GNU_EH_FRAME"),
+ ObjF.getFileName());
+ printEHFrameHdr(&Phdr);
+ break;
+ }
+
+ Expected<typename ELFT::ShdrRange> SectionsOrErr = Obj.sections();
+ if (!SectionsOrErr)
+ reportError(SectionsOrErr.takeError(), ObjF.getFileName());
+
+ for (const Elf_Shdr &Shdr : *SectionsOrErr) {
+ Expected<StringRef> NameOrErr = Obj.getSectionName(Shdr);
+ if (!NameOrErr)
+ reportError(NameOrErr.takeError(), ObjF.getFileName());
+ if (*NameOrErr == ".eh_frame")
+ printEHFrame(&Shdr);
+ }
+}
+
+template <typename ELFT>
+void PrinterContext<ELFT>::printEHFrameHdr(const Elf_Phdr *EHFramePHdr) const {
+ DictScope L(W, "EHFrameHeader");
+ uint64_t EHFrameHdrAddress = EHFramePHdr->p_vaddr;
+ W.startLine() << format("Address: 0x%" PRIx64 "\n", EHFrameHdrAddress);
+ W.startLine() << format("Offset: 0x%" PRIx64 "\n", (uint64_t)EHFramePHdr->p_offset);
+ W.startLine() << format("Size: 0x%" PRIx64 "\n", (uint64_t)EHFramePHdr->p_memsz);
+
+ const object::ELFFile<ELFT> &Obj = ObjF.getELFFile();
+ if (const Elf_Shdr *EHFrameHdr =
+ findSectionByAddress(ObjF, EHFramePHdr->p_vaddr)) {
+ Expected<StringRef> NameOrErr = Obj.getSectionName(*EHFrameHdr);
+ if (!NameOrErr)
+ reportError(NameOrErr.takeError(), ObjF.getFileName());
+ W.printString("Corresponding Section", *NameOrErr);
+ }
+
+ Expected<ArrayRef<uint8_t>> Content = Obj.getSegmentContents(*EHFramePHdr);
+ if (!Content)
+ reportError(Content.takeError(), ObjF.getFileName());
+
+ DataExtractor DE(*Content,
+ ELFT::TargetEndianness == support::endianness::little,
+ ELFT::Is64Bits ? 8 : 4);
+
+ DictScope D(W, "Header");
+ uint64_t Offset = 0;
+
+ auto Version = DE.getU8(&Offset);
+ W.printNumber("version", Version);
+ if (Version != 1)
+ reportError(
+ object::createError("only version 1 of .eh_frame_hdr is supported"),
+ ObjF.getFileName());
+
+ uint64_t EHFramePtrEnc = DE.getU8(&Offset);
+ W.startLine() << format("eh_frame_ptr_enc: 0x%" PRIx64 "\n", EHFramePtrEnc);
+ if (EHFramePtrEnc != (dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4))
+ reportError(object::createError("unexpected encoding eh_frame_ptr_enc"),
+ ObjF.getFileName());
+
+ uint64_t FDECountEnc = DE.getU8(&Offset);
+ W.startLine() << format("fde_count_enc: 0x%" PRIx64 "\n", FDECountEnc);
+ if (FDECountEnc != dwarf::DW_EH_PE_udata4)
+ reportError(object::createError("unexpected encoding fde_count_enc"),
+ ObjF.getFileName());
+
+ uint64_t TableEnc = DE.getU8(&Offset);
+ W.startLine() << format("table_enc: 0x%" PRIx64 "\n", TableEnc);
+ if (TableEnc != (dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4))
+ reportError(object::createError("unexpected encoding table_enc"),
+ ObjF.getFileName());
+
+ auto EHFramePtr = DE.getSigned(&Offset, 4) + EHFrameHdrAddress + 4;
+ W.startLine() << format("eh_frame_ptr: 0x%" PRIx64 "\n", EHFramePtr);
+
+ auto FDECount = DE.getUnsigned(&Offset, 4);
+ W.printNumber("fde_count", FDECount);
+
+ unsigned NumEntries = 0;
+ uint64_t PrevPC = 0;
+ while (Offset + 8 <= EHFramePHdr->p_memsz && NumEntries < FDECount) {
+ DictScope D(W, std::string("entry ") + std::to_string(NumEntries));
+
+ auto InitialPC = DE.getSigned(&Offset, 4) + EHFrameHdrAddress;
+ W.startLine() << format("initial_location: 0x%" PRIx64 "\n", InitialPC);
+ auto Address = DE.getSigned(&Offset, 4) + EHFrameHdrAddress;
+ W.startLine() << format("address: 0x%" PRIx64 "\n", Address);
+
+ if (InitialPC < PrevPC)
+ reportError(object::createError("initial_location is out of order"),
+ ObjF.getFileName());
+
+ PrevPC = InitialPC;
+ ++NumEntries;
+ }
+}
+
+template <typename ELFT>
+void PrinterContext<ELFT>::printEHFrame(const Elf_Shdr *EHFrameShdr) const {
+ uint64_t Address = EHFrameShdr->sh_addr;
+ uint64_t ShOffset = EHFrameShdr->sh_offset;
+ W.startLine() << format(".eh_frame section at offset 0x%" PRIx64
+ " address 0x%" PRIx64 ":\n",
+ ShOffset, Address);
+ W.indent();
+
+ Expected<ArrayRef<uint8_t>> DataOrErr =
+ ObjF.getELFFile().getSectionContents(*EHFrameShdr);
+ if (!DataOrErr)
+ reportError(DataOrErr.takeError(), ObjF.getFileName());
+
+ // Construct DWARFDataExtractor to handle relocations ("PC Begin" fields).
+ std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(
+ ObjF, DWARFContext::ProcessDebugRelocations::Process, nullptr);
+ DWARFDataExtractor DE(DICtx->getDWARFObj(),
+ DICtx->getDWARFObj().getEHFrameSection(),
+ ELFT::TargetEndianness == support::endianness::little,
+ ELFT::Is64Bits ? 8 : 4);
+ DWARFDebugFrame EHFrame(Triple::ArchType(ObjF.getArch()), /*IsEH=*/true,
+ /*EHFrameAddress=*/Address);
+ if (Error E = EHFrame.parse(DE))
+ reportError(std::move(E), ObjF.getFileName());
+
+ for (const dwarf::FrameEntry &Entry : EHFrame) {
+ if (const dwarf::CIE *CIE = dyn_cast<dwarf::CIE>(&Entry)) {
+ W.startLine() << format("[0x%" PRIx64 "] CIE length=%" PRIu64 "\n",
+ Address + CIE->getOffset(), CIE->getLength());
+ W.indent();
+
+ W.printNumber("version", CIE->getVersion());
+ W.printString("augmentation", CIE->getAugmentationString());
+ W.printNumber("code_alignment_factor", CIE->getCodeAlignmentFactor());
+ W.printNumber("data_alignment_factor", CIE->getDataAlignmentFactor());
+ W.printNumber("return_address_register", CIE->getReturnAddressRegister());
+ } else {
+ const dwarf::FDE *FDE = cast<dwarf::FDE>(&Entry);
+ W.startLine() << format("[0x%" PRIx64 "] FDE length=%" PRIu64
+ " cie=[0x%" PRIx64 "]\n",
+ Address + FDE->getOffset(), FDE->getLength(),
+ Address + FDE->getLinkedCIE()->getOffset());
+ W.indent();
+
+ W.startLine() << format("initial_location: 0x%" PRIx64 "\n",
+ FDE->getInitialLocation());
+ W.startLine() << format(
+ "address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n",
+ FDE->getAddressRange(),
+ FDE->getInitialLocation() + FDE->getAddressRange());
+ }
+
+ W.getOStream() << "\n";
+ W.startLine() << "Program:\n";
+ W.indent();
+ Entry.cfis().dump(W.getOStream(), DIDumpOptions(), nullptr,
+ W.getIndentLevel());
+ W.unindent();
+ W.unindent();
+ W.getOStream() << "\n";
+ }
+
+ W.unindent();
+}
+} // namespace DwarfCFIEH
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/ELFDumper.cpp b/contrib/libs/llvm14/tools/llvm-readobj/ELFDumper.cpp
new file mode 100644
index 00000000000..3d43d1a72e7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/ELFDumper.cpp
@@ -0,0 +1,7362 @@
+//===- ELFDumper.cpp - ELF-specific dumper --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the ELF-specific dumper for llvm-readobj.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ARMEHABIPrinter.h"
+#include "DwarfCFIEHPrinter.h"
+#include "ObjDumper.h"
+#include "StackMapPrinter.h"
+#include "llvm-readobj.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/MapVector.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/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/BinaryFormat/MsgPackDocument.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/Object/Error.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/RelocationResolver.h"
+#include "llvm/Object/StackMapParser.h"
+#include "llvm/Support/AMDGPUMetadata.h"
+#include "llvm/Support/ARMAttributeParser.h"
+#include "llvm/Support/ARMBuildAttributes.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#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/MSP430AttributeParser.h"
+#include "llvm/Support/MSP430Attributes.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/MipsABIFlags.h"
+#include "llvm/Support/RISCVAttributeParser.h"
+#include "llvm/Support/RISCVAttributes.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cinttypes>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace ELF;
+
+#define LLVM_READOBJ_ENUM_CASE(ns, enum) \
+ case ns::enum: \
+ return #enum;
+
+#define ENUM_ENT(enum, altName) \
+ { #enum, altName, ELF::enum }
+
+#define ENUM_ENT_1(enum) \
+ { #enum, #enum, ELF::enum }
+
+namespace {
+
+template <class ELFT> struct RelSymbol {
+ RelSymbol(const typename ELFT::Sym *S, StringRef N)
+ : Sym(S), Name(N.str()) {}
+ const typename ELFT::Sym *Sym;
+ std::string Name;
+};
+
+/// Represents a contiguous uniform range in the file. We cannot just create a
+/// range directly because when creating one of these from the .dynamic table
+/// the size, entity size and virtual address are different entries in arbitrary
+/// order (DT_REL, DT_RELSZ, DT_RELENT for example).
+struct DynRegionInfo {
+ DynRegionInfo(const Binary &Owner, const ObjDumper &D)
+ : Obj(&Owner), Dumper(&D) {}
+ DynRegionInfo(const Binary &Owner, const ObjDumper &D, const uint8_t *A,
+ uint64_t S, uint64_t ES)
+ : Addr(A), Size(S), EntSize(ES), Obj(&Owner), Dumper(&D) {}
+
+ /// Address in current address space.
+ const uint8_t *Addr = nullptr;
+ /// Size in bytes of the region.
+ uint64_t Size = 0;
+ /// Size of each entity in the region.
+ uint64_t EntSize = 0;
+
+ /// Owner object. Used for error reporting.
+ const Binary *Obj;
+ /// Dumper used for error reporting.
+ const ObjDumper *Dumper;
+ /// Error prefix. Used for error reporting to provide more information.
+ std::string Context;
+ /// Region size name. Used for error reporting.
+ StringRef SizePrintName = "size";
+ /// Entry size name. Used for error reporting. If this field is empty, errors
+ /// will not mention the entry size.
+ StringRef EntSizePrintName = "entry size";
+
+ template <typename Type> ArrayRef<Type> getAsArrayRef() const {
+ const Type *Start = reinterpret_cast<const Type *>(Addr);
+ if (!Start)
+ return {Start, Start};
+
+ const uint64_t Offset =
+ Addr - (const uint8_t *)Obj->getMemoryBufferRef().getBufferStart();
+ const uint64_t ObjSize = Obj->getMemoryBufferRef().getBufferSize();
+
+ if (Size > ObjSize - Offset) {
+ Dumper->reportUniqueWarning(
+ "unable to read data at 0x" + Twine::utohexstr(Offset) +
+ " of size 0x" + Twine::utohexstr(Size) + " (" + SizePrintName +
+ "): it goes past the end of the file of size 0x" +
+ Twine::utohexstr(ObjSize));
+ return {Start, Start};
+ }
+
+ if (EntSize == sizeof(Type) && (Size % EntSize == 0))
+ return {Start, Start + (Size / EntSize)};
+
+ std::string Msg;
+ if (!Context.empty())
+ Msg += Context + " has ";
+
+ Msg += ("invalid " + SizePrintName + " (0x" + Twine::utohexstr(Size) + ")")
+ .str();
+ if (!EntSizePrintName.empty())
+ Msg +=
+ (" or " + EntSizePrintName + " (0x" + Twine::utohexstr(EntSize) + ")")
+ .str();
+
+ Dumper->reportUniqueWarning(Msg);
+ return {Start, Start};
+ }
+};
+
+struct GroupMember {
+ StringRef Name;
+ uint64_t Index;
+};
+
+struct GroupSection {
+ StringRef Name;
+ std::string Signature;
+ uint64_t ShName;
+ uint64_t Index;
+ uint32_t Link;
+ uint32_t Info;
+ uint32_t Type;
+ std::vector<GroupMember> Members;
+};
+
+namespace {
+
+struct NoteType {
+ uint32_t ID;
+ StringRef Name;
+};
+
+} // namespace
+
+template <class ELFT> class Relocation {
+public:
+ Relocation(const typename ELFT::Rel &R, bool IsMips64EL)
+ : Type(R.getType(IsMips64EL)), Symbol(R.getSymbol(IsMips64EL)),
+ Offset(R.r_offset), Info(R.r_info) {}
+
+ Relocation(const typename ELFT::Rela &R, bool IsMips64EL)
+ : Relocation((const typename ELFT::Rel &)R, IsMips64EL) {
+ Addend = R.r_addend;
+ }
+
+ uint32_t Type;
+ uint32_t Symbol;
+ typename ELFT::uint Offset;
+ typename ELFT::uint Info;
+ Optional<int64_t> Addend;
+};
+
+template <class ELFT> class MipsGOTParser;
+
+template <typename ELFT> class ELFDumper : public ObjDumper {
+ LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
+
+public:
+ ELFDumper(const object::ELFObjectFile<ELFT> &ObjF, ScopedPrinter &Writer);
+
+ void printUnwindInfo() override;
+ void printNeededLibraries() override;
+ void printHashTable() override;
+ void printGnuHashTable() override;
+ void printLoadName() override;
+ void printVersionInfo() override;
+ void printArchSpecificInfo() override;
+ void printStackMap() const override;
+
+ const object::ELFObjectFile<ELFT> &getElfObject() const { return ObjF; };
+
+ std::string describe(const Elf_Shdr &Sec) const;
+
+ unsigned getHashTableEntSize() const {
+ // EM_S390 and ELF::EM_ALPHA platforms use 8-bytes entries in SHT_HASH
+ // sections. This violates the ELF specification.
+ if (Obj.getHeader().e_machine == ELF::EM_S390 ||
+ Obj.getHeader().e_machine == ELF::EM_ALPHA)
+ return 8;
+ return 4;
+ }
+
+ Elf_Dyn_Range dynamic_table() const {
+ // A valid .dynamic section contains an array of entries terminated
+ // with a DT_NULL entry. However, sometimes the section content may
+ // continue past the DT_NULL entry, so to dump the section correctly,
+ // we first find the end of the entries by iterating over them.
+ Elf_Dyn_Range Table = DynamicTable.template getAsArrayRef<Elf_Dyn>();
+
+ size_t Size = 0;
+ while (Size < Table.size())
+ if (Table[Size++].getTag() == DT_NULL)
+ break;
+
+ return Table.slice(0, Size);
+ }
+
+ Elf_Sym_Range dynamic_symbols() const {
+ if (!DynSymRegion)
+ return Elf_Sym_Range();
+ return DynSymRegion->template getAsArrayRef<Elf_Sym>();
+ }
+
+ const Elf_Shdr *findSectionByName(StringRef Name) const;
+
+ StringRef getDynamicStringTable() const { return DynamicStringTable; }
+
+protected:
+ virtual void printVersionSymbolSection(const Elf_Shdr *Sec) = 0;
+ virtual void printVersionDefinitionSection(const Elf_Shdr *Sec) = 0;
+ virtual void printVersionDependencySection(const Elf_Shdr *Sec) = 0;
+
+ void
+ printDependentLibsHelper(function_ref<void(const Elf_Shdr &)> OnSectionStart,
+ function_ref<void(StringRef, uint64_t)> OnLibEntry);
+
+ virtual void printRelRelaReloc(const Relocation<ELFT> &R,
+ const RelSymbol<ELFT> &RelSym) = 0;
+ virtual void printRelrReloc(const Elf_Relr &R) = 0;
+ virtual void printDynamicRelocHeader(unsigned Type, StringRef Name,
+ const DynRegionInfo &Reg) {}
+ void printReloc(const Relocation<ELFT> &R, unsigned RelIndex,
+ const Elf_Shdr &Sec, const Elf_Shdr *SymTab);
+ void printDynamicReloc(const Relocation<ELFT> &R);
+ void printDynamicRelocationsHelper();
+ void printRelocationsHelper(const Elf_Shdr &Sec);
+ void forEachRelocationDo(
+ const Elf_Shdr &Sec, bool RawRelr,
+ llvm::function_ref<void(const Relocation<ELFT> &, unsigned,
+ const Elf_Shdr &, const Elf_Shdr *)>
+ RelRelaFn,
+ llvm::function_ref<void(const Elf_Relr &)> RelrFn);
+
+ virtual void printSymtabMessage(const Elf_Shdr *Symtab, size_t Offset,
+ bool NonVisibilityBitsUsed) const {};
+ virtual void printSymbol(const Elf_Sym &Symbol, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable,
+ Optional<StringRef> StrTable, bool IsDynamic,
+ bool NonVisibilityBitsUsed) const = 0;
+
+ virtual void printMipsABIFlags() = 0;
+ virtual void printMipsGOT(const MipsGOTParser<ELFT> &Parser) = 0;
+ virtual void printMipsPLT(const MipsGOTParser<ELFT> &Parser) = 0;
+
+ Expected<ArrayRef<Elf_Versym>>
+ getVersionTable(const Elf_Shdr &Sec, ArrayRef<Elf_Sym> *SymTab,
+ StringRef *StrTab, const Elf_Shdr **SymTabSec) const;
+ StringRef getPrintableSectionName(const Elf_Shdr &Sec) const;
+
+ std::vector<GroupSection> getGroups();
+
+ // Returns the function symbol index for the given address. Matches the
+ // symbol's section with FunctionSec when specified.
+ // Returns None if no function symbol can be found for the address or in case
+ // it is not defined in the specified section.
+ SmallVector<uint32_t>
+ getSymbolIndexesForFunctionAddress(uint64_t SymValue,
+ Optional<const Elf_Shdr *> FunctionSec);
+ bool printFunctionStackSize(uint64_t SymValue,
+ Optional<const Elf_Shdr *> FunctionSec,
+ const Elf_Shdr &StackSizeSec, DataExtractor Data,
+ uint64_t *Offset);
+ void printStackSize(const Relocation<ELFT> &R, const Elf_Shdr &RelocSec,
+ unsigned Ndx, const Elf_Shdr *SymTab,
+ const Elf_Shdr *FunctionSec, const Elf_Shdr &StackSizeSec,
+ const RelocationResolver &Resolver, DataExtractor Data);
+ virtual void printStackSizeEntry(uint64_t Size,
+ ArrayRef<std::string> FuncNames) = 0;
+
+ void printRelocatableStackSizes(std::function<void()> PrintHeader);
+ void printNonRelocatableStackSizes(std::function<void()> PrintHeader);
+
+ /// Retrieves sections with corresponding relocation sections based on
+ /// IsMatch.
+ void getSectionAndRelocations(
+ std::function<bool(const Elf_Shdr &)> IsMatch,
+ llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> &SecToRelocMap);
+
+ const object::ELFObjectFile<ELFT> &ObjF;
+ const ELFFile<ELFT> &Obj;
+ StringRef FileName;
+
+ Expected<DynRegionInfo> createDRI(uint64_t Offset, uint64_t Size,
+ uint64_t EntSize) {
+ if (Offset + Size < Offset || Offset + Size > Obj.getBufSize())
+ return createError("offset (0x" + Twine::utohexstr(Offset) +
+ ") + size (0x" + Twine::utohexstr(Size) +
+ ") is greater than the file size (0x" +
+ Twine::utohexstr(Obj.getBufSize()) + ")");
+ return DynRegionInfo(ObjF, *this, Obj.base() + Offset, Size, EntSize);
+ }
+
+ void printAttributes(unsigned, std::unique_ptr<ELFAttributeParser>,
+ support::endianness);
+ void printMipsReginfo();
+ void printMipsOptions();
+
+ std::pair<const Elf_Phdr *, const Elf_Shdr *> findDynamic();
+ void loadDynamicTable();
+ void parseDynamicTable();
+
+ Expected<StringRef> getSymbolVersion(const Elf_Sym &Sym,
+ bool &IsDefault) const;
+ Expected<SmallVector<Optional<VersionEntry>, 0> *> getVersionMap() const;
+
+ DynRegionInfo DynRelRegion;
+ DynRegionInfo DynRelaRegion;
+ DynRegionInfo DynRelrRegion;
+ DynRegionInfo DynPLTRelRegion;
+ Optional<DynRegionInfo> DynSymRegion;
+ DynRegionInfo DynSymTabShndxRegion;
+ DynRegionInfo DynamicTable;
+ StringRef DynamicStringTable;
+ const Elf_Hash *HashTable = nullptr;
+ const Elf_GnuHash *GnuHashTable = nullptr;
+ const Elf_Shdr *DotSymtabSec = nullptr;
+ const Elf_Shdr *DotDynsymSec = nullptr;
+ const Elf_Shdr *DotAddrsigSec = nullptr;
+ DenseMap<const Elf_Shdr *, ArrayRef<Elf_Word>> ShndxTables;
+ Optional<uint64_t> SONameOffset;
+ Optional<DenseMap<uint64_t, std::vector<uint32_t>>> AddressToIndexMap;
+
+ const Elf_Shdr *SymbolVersionSection = nullptr; // .gnu.version
+ const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r
+ const Elf_Shdr *SymbolVersionDefSection = nullptr; // .gnu.version_d
+
+ std::string getFullSymbolName(const Elf_Sym &Symbol, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable,
+ Optional<StringRef> StrTable,
+ bool IsDynamic) const;
+ Expected<unsigned>
+ getSymbolSectionIndex(const Elf_Sym &Symbol, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable) const;
+ Expected<StringRef> getSymbolSectionName(const Elf_Sym &Symbol,
+ unsigned SectionIndex) const;
+ std::string getStaticSymbolName(uint32_t Index) const;
+ StringRef getDynamicString(uint64_t Value) const;
+
+ void printSymbolsHelper(bool IsDynamic) const;
+ std::string getDynamicEntry(uint64_t Type, uint64_t Value) const;
+
+ Expected<RelSymbol<ELFT>> getRelocationTarget(const Relocation<ELFT> &R,
+ const Elf_Shdr *SymTab) const;
+
+ ArrayRef<Elf_Word> getShndxTable(const Elf_Shdr *Symtab) const;
+
+private:
+ mutable SmallVector<Optional<VersionEntry>, 0> VersionMap;
+};
+
+template <class ELFT>
+std::string ELFDumper<ELFT>::describe(const Elf_Shdr &Sec) const {
+ return ::describe(Obj, Sec);
+}
+
+namespace {
+
+template <class ELFT> struct SymtabLink {
+ typename ELFT::SymRange Symbols;
+ StringRef StringTable;
+ const typename ELFT::Shdr *SymTab;
+};
+
+// Returns the linked symbol table, symbols and associated string table for a
+// given section.
+template <class ELFT>
+Expected<SymtabLink<ELFT>> getLinkAsSymtab(const ELFFile<ELFT> &Obj,
+ const typename ELFT::Shdr &Sec,
+ unsigned ExpectedType) {
+ Expected<const typename ELFT::Shdr *> SymtabOrErr =
+ Obj.getSection(Sec.sh_link);
+ if (!SymtabOrErr)
+ return createError("invalid section linked to " + describe(Obj, Sec) +
+ ": " + toString(SymtabOrErr.takeError()));
+
+ if ((*SymtabOrErr)->sh_type != ExpectedType)
+ return createError(
+ "invalid section linked to " + describe(Obj, Sec) + ": expected " +
+ object::getELFSectionTypeName(Obj.getHeader().e_machine, ExpectedType) +
+ ", but got " +
+ object::getELFSectionTypeName(Obj.getHeader().e_machine,
+ (*SymtabOrErr)->sh_type));
+
+ Expected<StringRef> StrTabOrErr = Obj.getLinkAsStrtab(**SymtabOrErr);
+ if (!StrTabOrErr)
+ return createError(
+ "can't get a string table for the symbol table linked to " +
+ describe(Obj, Sec) + ": " + toString(StrTabOrErr.takeError()));
+
+ Expected<typename ELFT::SymRange> SymsOrErr = Obj.symbols(*SymtabOrErr);
+ if (!SymsOrErr)
+ return createError("unable to read symbols from the " + describe(Obj, Sec) +
+ ": " + toString(SymsOrErr.takeError()));
+
+ return SymtabLink<ELFT>{*SymsOrErr, *StrTabOrErr, *SymtabOrErr};
+}
+
+} // namespace
+
+template <class ELFT>
+Expected<ArrayRef<typename ELFT::Versym>>
+ELFDumper<ELFT>::getVersionTable(const Elf_Shdr &Sec, ArrayRef<Elf_Sym> *SymTab,
+ StringRef *StrTab,
+ const Elf_Shdr **SymTabSec) const {
+ assert((!SymTab && !StrTab && !SymTabSec) || (SymTab && StrTab && SymTabSec));
+ if (reinterpret_cast<uintptr_t>(Obj.base() + Sec.sh_offset) %
+ sizeof(uint16_t) !=
+ 0)
+ return createError("the " + describe(Sec) + " is misaligned");
+
+ Expected<ArrayRef<Elf_Versym>> VersionsOrErr =
+ Obj.template getSectionContentsAsArray<Elf_Versym>(Sec);
+ if (!VersionsOrErr)
+ return createError("cannot read content of " + describe(Sec) + ": " +
+ toString(VersionsOrErr.takeError()));
+
+ Expected<SymtabLink<ELFT>> SymTabOrErr =
+ getLinkAsSymtab(Obj, Sec, SHT_DYNSYM);
+ if (!SymTabOrErr) {
+ reportUniqueWarning(SymTabOrErr.takeError());
+ return *VersionsOrErr;
+ }
+
+ if (SymTabOrErr->Symbols.size() != VersionsOrErr->size())
+ reportUniqueWarning(describe(Sec) + ": the number of entries (" +
+ Twine(VersionsOrErr->size()) +
+ ") does not match the number of symbols (" +
+ Twine(SymTabOrErr->Symbols.size()) +
+ ") in the symbol table with index " +
+ Twine(Sec.sh_link));
+
+ if (SymTab) {
+ *SymTab = SymTabOrErr->Symbols;
+ *StrTab = SymTabOrErr->StringTable;
+ *SymTabSec = SymTabOrErr->SymTab;
+ }
+ return *VersionsOrErr;
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const {
+ Optional<StringRef> StrTable;
+ size_t Entries = 0;
+ Elf_Sym_Range Syms(nullptr, nullptr);
+ const Elf_Shdr *SymtabSec = IsDynamic ? DotDynsymSec : DotSymtabSec;
+
+ if (IsDynamic) {
+ StrTable = DynamicStringTable;
+ Syms = dynamic_symbols();
+ Entries = Syms.size();
+ } else if (DotSymtabSec) {
+ if (Expected<StringRef> StrTableOrErr =
+ Obj.getStringTableForSymtab(*DotSymtabSec))
+ StrTable = *StrTableOrErr;
+ else
+ reportUniqueWarning(
+ "unable to get the string table for the SHT_SYMTAB section: " +
+ toString(StrTableOrErr.takeError()));
+
+ if (Expected<Elf_Sym_Range> SymsOrErr = Obj.symbols(DotSymtabSec))
+ Syms = *SymsOrErr;
+ else
+ reportUniqueWarning(
+ "unable to read symbols from the SHT_SYMTAB section: " +
+ toString(SymsOrErr.takeError()));
+ Entries = DotSymtabSec->getEntityCount();
+ }
+ if (Syms.empty())
+ return;
+
+ // The st_other field has 2 logical parts. The first two bits hold the symbol
+ // visibility (STV_*) and the remainder hold other platform-specific values.
+ bool NonVisibilityBitsUsed =
+ llvm::any_of(Syms, [](const Elf_Sym &S) { return S.st_other & ~0x3; });
+
+ DataRegion<Elf_Word> ShndxTable =
+ IsDynamic ? DataRegion<Elf_Word>(
+ (const Elf_Word *)this->DynSymTabShndxRegion.Addr,
+ this->getElfObject().getELFFile().end())
+ : DataRegion<Elf_Word>(this->getShndxTable(SymtabSec));
+
+ printSymtabMessage(SymtabSec, Entries, NonVisibilityBitsUsed);
+ for (const Elf_Sym &Sym : Syms)
+ printSymbol(Sym, &Sym - Syms.begin(), ShndxTable, StrTable, IsDynamic,
+ NonVisibilityBitsUsed);
+}
+
+template <typename ELFT> class GNUELFDumper : public ELFDumper<ELFT> {
+ formatted_raw_ostream &OS;
+
+public:
+ LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
+
+ GNUELFDumper(const object::ELFObjectFile<ELFT> &ObjF, ScopedPrinter &Writer)
+ : ELFDumper<ELFT>(ObjF, Writer),
+ OS(static_cast<formatted_raw_ostream &>(Writer.getOStream())) {
+ assert(&this->W.getOStream() == &llvm::fouts());
+ }
+
+ void printFileSummary(StringRef FileStr, ObjectFile &Obj,
+ ArrayRef<std::string> InputFilenames,
+ const Archive *A) override;
+ void printFileHeaders() override;
+ void printGroupSections() override;
+ void printRelocations() override;
+ void printSectionHeaders() override;
+ void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) override;
+ void printHashSymbols() override;
+ void printSectionDetails() override;
+ void printDependentLibs() override;
+ void printDynamicTable() override;
+ void printDynamicRelocations() override;
+ void printSymtabMessage(const Elf_Shdr *Symtab, size_t Offset,
+ bool NonVisibilityBitsUsed) const override;
+ void printProgramHeaders(bool PrintProgramHeaders,
+ cl::boolOrDefault PrintSectionMapping) override;
+ void printVersionSymbolSection(const Elf_Shdr *Sec) override;
+ void printVersionDefinitionSection(const Elf_Shdr *Sec) override;
+ void printVersionDependencySection(const Elf_Shdr *Sec) override;
+ void printHashHistograms() override;
+ void printCGProfile() override;
+ void printBBAddrMaps() override;
+ void printAddrsig() override;
+ void printNotes() override;
+ void printELFLinkerOptions() override;
+ void printStackSizes() override;
+
+private:
+ void printHashHistogram(const Elf_Hash &HashTable);
+ void printGnuHashHistogram(const Elf_GnuHash &GnuHashTable);
+ void printHashTableSymbols(const Elf_Hash &HashTable);
+ void printGnuHashTableSymbols(const Elf_GnuHash &GnuHashTable);
+
+ struct Field {
+ std::string Str;
+ unsigned Column;
+
+ Field(StringRef S, unsigned Col) : Str(std::string(S)), Column(Col) {}
+ Field(unsigned Col) : Column(Col) {}
+ };
+
+ template <typename T, typename TEnum>
+ std::string printFlags(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues,
+ TEnum EnumMask1 = {}, TEnum EnumMask2 = {},
+ TEnum EnumMask3 = {}) const {
+ std::string Str;
+ for (const EnumEntry<TEnum> &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) const {
+ if (F.Column != 0)
+ OS.PadToColumn(F.Column);
+ OS << F.Str;
+ OS.flush();
+ return OS;
+ }
+ void printHashedSymbol(const Elf_Sym *Sym, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable, StringRef StrTable,
+ uint32_t Bucket);
+ void printRelrReloc(const Elf_Relr &R) override;
+ void printRelRelaReloc(const Relocation<ELFT> &R,
+ const RelSymbol<ELFT> &RelSym) override;
+ void printSymbol(const Elf_Sym &Symbol, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable,
+ Optional<StringRef> StrTable, bool IsDynamic,
+ bool NonVisibilityBitsUsed) const override;
+ void printDynamicRelocHeader(unsigned Type, StringRef Name,
+ const DynRegionInfo &Reg) override;
+
+ std::string getSymbolSectionNdx(const Elf_Sym &Symbol, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable) const;
+ void printProgramHeaders() override;
+ void printSectionMapping() override;
+ void printGNUVersionSectionProlog(const typename ELFT::Shdr &Sec,
+ const Twine &Label, unsigned EntriesNum);
+
+ void printStackSizeEntry(uint64_t Size,
+ ArrayRef<std::string> FuncNames) override;
+
+ void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override;
+ void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override;
+ void printMipsABIFlags() override;
+};
+
+template <typename ELFT> class LLVMELFDumper : public ELFDumper<ELFT> {
+public:
+ LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
+
+ LLVMELFDumper(const object::ELFObjectFile<ELFT> &ObjF, ScopedPrinter &Writer)
+ : ELFDumper<ELFT>(ObjF, Writer), W(Writer) {}
+
+ void printFileHeaders() override;
+ void printGroupSections() override;
+ void printRelocations() override;
+ void printSectionHeaders() override;
+ void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) override;
+ void printDependentLibs() override;
+ void printDynamicTable() override;
+ void printDynamicRelocations() override;
+ void printProgramHeaders(bool PrintProgramHeaders,
+ cl::boolOrDefault PrintSectionMapping) override;
+ void printVersionSymbolSection(const Elf_Shdr *Sec) override;
+ void printVersionDefinitionSection(const Elf_Shdr *Sec) override;
+ void printVersionDependencySection(const Elf_Shdr *Sec) override;
+ void printHashHistograms() override;
+ void printCGProfile() override;
+ void printBBAddrMaps() override;
+ void printAddrsig() override;
+ void printNotes() override;
+ void printELFLinkerOptions() override;
+ void printStackSizes() override;
+
+private:
+ void printRelrReloc(const Elf_Relr &R) override;
+ void printRelRelaReloc(const Relocation<ELFT> &R,
+ const RelSymbol<ELFT> &RelSym) override;
+
+ void printSymbolSection(const Elf_Sym &Symbol, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable) const;
+ void printSymbol(const Elf_Sym &Symbol, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable,
+ Optional<StringRef> StrTable, bool IsDynamic,
+ bool /*NonVisibilityBitsUsed*/) const override;
+ void printProgramHeaders() override;
+ void printSectionMapping() override {}
+ void printStackSizeEntry(uint64_t Size,
+ ArrayRef<std::string> FuncNames) override;
+
+ void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override;
+ void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override;
+ void printMipsABIFlags() override;
+
+protected:
+ ScopedPrinter &W;
+};
+
+// JSONELFDumper shares most of the same implementation as LLVMELFDumper except
+// it uses a JSONScopedPrinter.
+template <typename ELFT> class JSONELFDumper : public LLVMELFDumper<ELFT> {
+public:
+ LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
+
+ JSONELFDumper(const object::ELFObjectFile<ELFT> &ObjF, ScopedPrinter &Writer)
+ : LLVMELFDumper<ELFT>(ObjF, Writer) {}
+
+ void printFileSummary(StringRef FileStr, ObjectFile &Obj,
+ ArrayRef<std::string> InputFilenames,
+ const Archive *A) override;
+
+private:
+ std::unique_ptr<DictScope> FileScope;
+};
+
+} // end anonymous namespace
+
+namespace llvm {
+
+template <class ELFT>
+static std::unique_ptr<ObjDumper>
+createELFDumper(const ELFObjectFile<ELFT> &Obj, ScopedPrinter &Writer) {
+ if (opts::Output == opts::GNU)
+ return std::make_unique<GNUELFDumper<ELFT>>(Obj, Writer);
+ else if (opts::Output == opts::JSON)
+ return std::make_unique<JSONELFDumper<ELFT>>(Obj, Writer);
+ return std::make_unique<LLVMELFDumper<ELFT>>(Obj, Writer);
+}
+
+std::unique_ptr<ObjDumper> createELFDumper(const object::ELFObjectFileBase &Obj,
+ ScopedPrinter &Writer) {
+ // Little-endian 32-bit
+ if (const ELF32LEObjectFile *ELFObj = dyn_cast<ELF32LEObjectFile>(&Obj))
+ return createELFDumper(*ELFObj, Writer);
+
+ // Big-endian 32-bit
+ if (const ELF32BEObjectFile *ELFObj = dyn_cast<ELF32BEObjectFile>(&Obj))
+ return createELFDumper(*ELFObj, Writer);
+
+ // Little-endian 64-bit
+ if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(&Obj))
+ return createELFDumper(*ELFObj, Writer);
+
+ // Big-endian 64-bit
+ return createELFDumper(*cast<ELF64BEObjectFile>(&Obj), Writer);
+}
+
+} // end namespace llvm
+
+template <class ELFT>
+Expected<SmallVector<Optional<VersionEntry>, 0> *>
+ELFDumper<ELFT>::getVersionMap() const {
+ // If the VersionMap has already been loaded or if there is no dynamic symtab
+ // or version table, there is nothing to do.
+ if (!VersionMap.empty() || !DynSymRegion || !SymbolVersionSection)
+ return &VersionMap;
+
+ Expected<SmallVector<Optional<VersionEntry>, 0>> MapOrErr =
+ Obj.loadVersionMap(SymbolVersionNeedSection, SymbolVersionDefSection);
+ if (MapOrErr)
+ VersionMap = *MapOrErr;
+ else
+ return MapOrErr.takeError();
+
+ return &VersionMap;
+}
+
+template <typename ELFT>
+Expected<StringRef> ELFDumper<ELFT>::getSymbolVersion(const Elf_Sym &Sym,
+ bool &IsDefault) const {
+ // This is a dynamic symbol. Look in the GNU symbol version table.
+ if (!SymbolVersionSection) {
+ // No version table.
+ IsDefault = false;
+ return "";
+ }
+
+ assert(DynSymRegion && "DynSymRegion has not been initialised");
+ // Determine the position in the symbol table of this entry.
+ size_t EntryIndex = (reinterpret_cast<uintptr_t>(&Sym) -
+ reinterpret_cast<uintptr_t>(DynSymRegion->Addr)) /
+ sizeof(Elf_Sym);
+
+ // Get the corresponding version index entry.
+ Expected<const Elf_Versym *> EntryOrErr =
+ Obj.template getEntry<Elf_Versym>(*SymbolVersionSection, EntryIndex);
+ if (!EntryOrErr)
+ return EntryOrErr.takeError();
+
+ unsigned Version = (*EntryOrErr)->vs_index;
+ if (Version == VER_NDX_LOCAL || Version == VER_NDX_GLOBAL) {
+ IsDefault = false;
+ return "";
+ }
+
+ Expected<SmallVector<Optional<VersionEntry>, 0> *> MapOrErr =
+ getVersionMap();
+ if (!MapOrErr)
+ return MapOrErr.takeError();
+
+ return Obj.getSymbolVersionByIndex(Version, IsDefault, **MapOrErr,
+ Sym.st_shndx == ELF::SHN_UNDEF);
+}
+
+template <typename ELFT>
+Expected<RelSymbol<ELFT>>
+ELFDumper<ELFT>::getRelocationTarget(const Relocation<ELFT> &R,
+ const Elf_Shdr *SymTab) const {
+ if (R.Symbol == 0)
+ return RelSymbol<ELFT>(nullptr, "");
+
+ Expected<const Elf_Sym *> SymOrErr =
+ Obj.template getEntry<Elf_Sym>(*SymTab, R.Symbol);
+ if (!SymOrErr)
+ return createError("unable to read an entry with index " + Twine(R.Symbol) +
+ " from " + describe(*SymTab) + ": " +
+ toString(SymOrErr.takeError()));
+ const Elf_Sym *Sym = *SymOrErr;
+ if (!Sym)
+ return RelSymbol<ELFT>(nullptr, "");
+
+ Expected<StringRef> StrTableOrErr = Obj.getStringTableForSymtab(*SymTab);
+ if (!StrTableOrErr)
+ return StrTableOrErr.takeError();
+
+ const Elf_Sym *FirstSym =
+ cantFail(Obj.template getEntry<Elf_Sym>(*SymTab, 0));
+ std::string SymbolName =
+ getFullSymbolName(*Sym, Sym - FirstSym, getShndxTable(SymTab),
+ *StrTableOrErr, SymTab->sh_type == SHT_DYNSYM);
+ return RelSymbol<ELFT>(Sym, SymbolName);
+}
+
+template <typename ELFT>
+ArrayRef<typename ELFT::Word>
+ELFDumper<ELFT>::getShndxTable(const Elf_Shdr *Symtab) const {
+ if (Symtab) {
+ auto It = ShndxTables.find(Symtab);
+ if (It != ShndxTables.end())
+ return It->second;
+ }
+ return {};
+}
+
+static std::string maybeDemangle(StringRef Name) {
+ return opts::Demangle ? demangle(std::string(Name)) : Name.str();
+}
+
+template <typename ELFT>
+std::string ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const {
+ auto Warn = [&](Error E) -> std::string {
+ reportUniqueWarning("unable to read the name of symbol with index " +
+ Twine(Index) + ": " + toString(std::move(E)));
+ return "<?>";
+ };
+
+ Expected<const typename ELFT::Sym *> SymOrErr =
+ Obj.getSymbol(DotSymtabSec, Index);
+ if (!SymOrErr)
+ return Warn(SymOrErr.takeError());
+
+ Expected<StringRef> StrTabOrErr = Obj.getStringTableForSymtab(*DotSymtabSec);
+ if (!StrTabOrErr)
+ return Warn(StrTabOrErr.takeError());
+
+ Expected<StringRef> NameOrErr = (*SymOrErr)->getName(*StrTabOrErr);
+ if (!NameOrErr)
+ return Warn(NameOrErr.takeError());
+ return maybeDemangle(*NameOrErr);
+}
+
+template <typename ELFT>
+std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym &Symbol,
+ unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable,
+ Optional<StringRef> StrTable,
+ bool IsDynamic) const {
+ if (!StrTable)
+ return "<?>";
+
+ std::string SymbolName;
+ if (Expected<StringRef> NameOrErr = Symbol.getName(*StrTable)) {
+ SymbolName = maybeDemangle(*NameOrErr);
+ } else {
+ reportUniqueWarning(NameOrErr.takeError());
+ return "<?>";
+ }
+
+ if (SymbolName.empty() && Symbol.getType() == ELF::STT_SECTION) {
+ Expected<unsigned> SectionIndex =
+ getSymbolSectionIndex(Symbol, SymIndex, ShndxTable);
+ if (!SectionIndex) {
+ reportUniqueWarning(SectionIndex.takeError());
+ return "<?>";
+ }
+ Expected<StringRef> NameOrErr = getSymbolSectionName(Symbol, *SectionIndex);
+ if (!NameOrErr) {
+ reportUniqueWarning(NameOrErr.takeError());
+ return ("<section " + Twine(*SectionIndex) + ">").str();
+ }
+ return std::string(*NameOrErr);
+ }
+
+ if (!IsDynamic)
+ return SymbolName;
+
+ bool IsDefault;
+ Expected<StringRef> VersionOrErr = getSymbolVersion(Symbol, IsDefault);
+ if (!VersionOrErr) {
+ reportUniqueWarning(VersionOrErr.takeError());
+ return SymbolName + "@<corrupt>";
+ }
+
+ if (!VersionOrErr->empty()) {
+ SymbolName += (IsDefault ? "@@" : "@");
+ SymbolName += *VersionOrErr;
+ }
+ return SymbolName;
+}
+
+template <typename ELFT>
+Expected<unsigned>
+ELFDumper<ELFT>::getSymbolSectionIndex(const Elf_Sym &Symbol, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable) const {
+ unsigned Ndx = Symbol.st_shndx;
+ if (Ndx == SHN_XINDEX)
+ return object::getExtendedSymbolTableIndex<ELFT>(Symbol, SymIndex,
+ ShndxTable);
+ if (Ndx != SHN_UNDEF && Ndx < SHN_LORESERVE)
+ return Ndx;
+
+ auto CreateErr = [&](const Twine &Name, Optional<unsigned> Offset = None) {
+ std::string Desc;
+ if (Offset)
+ Desc = (Name + "+0x" + Twine::utohexstr(*Offset)).str();
+ else
+ Desc = Name.str();
+ return createError(
+ "unable to get section index for symbol with st_shndx = 0x" +
+ Twine::utohexstr(Ndx) + " (" + Desc + ")");
+ };
+
+ if (Ndx >= ELF::SHN_LOPROC && Ndx <= ELF::SHN_HIPROC)
+ return CreateErr("SHN_LOPROC", Ndx - ELF::SHN_LOPROC);
+ if (Ndx >= ELF::SHN_LOOS && Ndx <= ELF::SHN_HIOS)
+ return CreateErr("SHN_LOOS", Ndx - ELF::SHN_LOOS);
+ if (Ndx == ELF::SHN_UNDEF)
+ return CreateErr("SHN_UNDEF");
+ if (Ndx == ELF::SHN_ABS)
+ return CreateErr("SHN_ABS");
+ if (Ndx == ELF::SHN_COMMON)
+ return CreateErr("SHN_COMMON");
+ return CreateErr("SHN_LORESERVE", Ndx - SHN_LORESERVE);
+}
+
+template <typename ELFT>
+Expected<StringRef>
+ELFDumper<ELFT>::getSymbolSectionName(const Elf_Sym &Symbol,
+ unsigned SectionIndex) const {
+ Expected<const Elf_Shdr *> SecOrErr = Obj.getSection(SectionIndex);
+ if (!SecOrErr)
+ return SecOrErr.takeError();
+ return Obj.getSectionName(**SecOrErr);
+}
+
+template <class ELFO>
+static const typename ELFO::Elf_Shdr *
+findNotEmptySectionByAddress(const ELFO &Obj, StringRef FileName,
+ uint64_t Addr) {
+ for (const typename ELFO::Elf_Shdr &Shdr : cantFail(Obj.sections()))
+ if (Shdr.sh_addr == Addr && Shdr.sh_size > 0)
+ return &Shdr;
+ return nullptr;
+}
+
+const EnumEntry<unsigned> ElfClass[] = {
+ {"None", "none", ELF::ELFCLASSNONE},
+ {"32-bit", "ELF32", ELF::ELFCLASS32},
+ {"64-bit", "ELF64", ELF::ELFCLASS64},
+};
+
+const EnumEntry<unsigned> ElfDataEncoding[] = {
+ {"None", "none", ELF::ELFDATANONE},
+ {"LittleEndian", "2's complement, little endian", ELF::ELFDATA2LSB},
+ {"BigEndian", "2's complement, big endian", ELF::ELFDATA2MSB},
+};
+
+const EnumEntry<unsigned> ElfObjectFileType[] = {
+ {"None", "NONE (none)", ELF::ET_NONE},
+ {"Relocatable", "REL (Relocatable file)", ELF::ET_REL},
+ {"Executable", "EXEC (Executable file)", ELF::ET_EXEC},
+ {"SharedObject", "DYN (Shared object file)", ELF::ET_DYN},
+ {"Core", "CORE (Core file)", ELF::ET_CORE},
+};
+
+const EnumEntry<unsigned> ElfOSABI[] = {
+ {"SystemV", "UNIX - System V", ELF::ELFOSABI_NONE},
+ {"HPUX", "UNIX - HP-UX", ELF::ELFOSABI_HPUX},
+ {"NetBSD", "UNIX - NetBSD", ELF::ELFOSABI_NETBSD},
+ {"GNU/Linux", "UNIX - GNU", ELF::ELFOSABI_LINUX},
+ {"GNU/Hurd", "GNU/Hurd", ELF::ELFOSABI_HURD},
+ {"Solaris", "UNIX - Solaris", ELF::ELFOSABI_SOLARIS},
+ {"AIX", "UNIX - AIX", ELF::ELFOSABI_AIX},
+ {"IRIX", "UNIX - IRIX", ELF::ELFOSABI_IRIX},
+ {"FreeBSD", "UNIX - FreeBSD", ELF::ELFOSABI_FREEBSD},
+ {"TRU64", "UNIX - TRU64", ELF::ELFOSABI_TRU64},
+ {"Modesto", "Novell - Modesto", ELF::ELFOSABI_MODESTO},
+ {"OpenBSD", "UNIX - OpenBSD", ELF::ELFOSABI_OPENBSD},
+ {"OpenVMS", "VMS - OpenVMS", ELF::ELFOSABI_OPENVMS},
+ {"NSK", "HP - Non-Stop Kernel", ELF::ELFOSABI_NSK},
+ {"AROS", "AROS", ELF::ELFOSABI_AROS},
+ {"FenixOS", "FenixOS", ELF::ELFOSABI_FENIXOS},
+ {"CloudABI", "CloudABI", ELF::ELFOSABI_CLOUDABI},
+ {"Standalone", "Standalone App", ELF::ELFOSABI_STANDALONE}
+};
+
+const EnumEntry<unsigned> AMDGPUElfOSABI[] = {
+ {"AMDGPU_HSA", "AMDGPU - HSA", ELF::ELFOSABI_AMDGPU_HSA},
+ {"AMDGPU_PAL", "AMDGPU - PAL", ELF::ELFOSABI_AMDGPU_PAL},
+ {"AMDGPU_MESA3D", "AMDGPU - MESA3D", ELF::ELFOSABI_AMDGPU_MESA3D}
+};
+
+const EnumEntry<unsigned> ARMElfOSABI[] = {
+ {"ARM", "ARM", ELF::ELFOSABI_ARM}
+};
+
+const EnumEntry<unsigned> C6000ElfOSABI[] = {
+ {"C6000_ELFABI", "Bare-metal C6000", ELF::ELFOSABI_C6000_ELFABI},
+ {"C6000_LINUX", "Linux C6000", ELF::ELFOSABI_C6000_LINUX}
+};
+
+const EnumEntry<unsigned> ElfMachineType[] = {
+ ENUM_ENT(EM_NONE, "None"),
+ ENUM_ENT(EM_M32, "WE32100"),
+ ENUM_ENT(EM_SPARC, "Sparc"),
+ ENUM_ENT(EM_386, "Intel 80386"),
+ ENUM_ENT(EM_68K, "MC68000"),
+ ENUM_ENT(EM_88K, "MC88000"),
+ ENUM_ENT(EM_IAMCU, "EM_IAMCU"),
+ ENUM_ENT(EM_860, "Intel 80860"),
+ ENUM_ENT(EM_MIPS, "MIPS R3000"),
+ ENUM_ENT(EM_S370, "IBM System/370"),
+ ENUM_ENT(EM_MIPS_RS3_LE, "MIPS R3000 little-endian"),
+ ENUM_ENT(EM_PARISC, "HPPA"),
+ ENUM_ENT(EM_VPP500, "Fujitsu VPP500"),
+ ENUM_ENT(EM_SPARC32PLUS, "Sparc v8+"),
+ ENUM_ENT(EM_960, "Intel 80960"),
+ ENUM_ENT(EM_PPC, "PowerPC"),
+ ENUM_ENT(EM_PPC64, "PowerPC64"),
+ ENUM_ENT(EM_S390, "IBM S/390"),
+ ENUM_ENT(EM_SPU, "SPU"),
+ ENUM_ENT(EM_V800, "NEC V800 series"),
+ ENUM_ENT(EM_FR20, "Fujistsu FR20"),
+ ENUM_ENT(EM_RH32, "TRW RH-32"),
+ ENUM_ENT(EM_RCE, "Motorola RCE"),
+ ENUM_ENT(EM_ARM, "ARM"),
+ ENUM_ENT(EM_ALPHA, "EM_ALPHA"),
+ ENUM_ENT(EM_SH, "Hitachi SH"),
+ ENUM_ENT(EM_SPARCV9, "Sparc v9"),
+ ENUM_ENT(EM_TRICORE, "Siemens Tricore"),
+ ENUM_ENT(EM_ARC, "ARC"),
+ ENUM_ENT(EM_H8_300, "Hitachi H8/300"),
+ ENUM_ENT(EM_H8_300H, "Hitachi H8/300H"),
+ ENUM_ENT(EM_H8S, "Hitachi H8S"),
+ ENUM_ENT(EM_H8_500, "Hitachi H8/500"),
+ ENUM_ENT(EM_IA_64, "Intel IA-64"),
+ ENUM_ENT(EM_MIPS_X, "Stanford MIPS-X"),
+ ENUM_ENT(EM_COLDFIRE, "Motorola Coldfire"),
+ ENUM_ENT(EM_68HC12, "Motorola MC68HC12 Microcontroller"),
+ ENUM_ENT(EM_MMA, "Fujitsu Multimedia Accelerator"),
+ ENUM_ENT(EM_PCP, "Siemens PCP"),
+ ENUM_ENT(EM_NCPU, "Sony nCPU embedded RISC processor"),
+ ENUM_ENT(EM_NDR1, "Denso NDR1 microprocesspr"),
+ ENUM_ENT(EM_STARCORE, "Motorola Star*Core processor"),
+ ENUM_ENT(EM_ME16, "Toyota ME16 processor"),
+ ENUM_ENT(EM_ST100, "STMicroelectronics ST100 processor"),
+ ENUM_ENT(EM_TINYJ, "Advanced Logic Corp. TinyJ embedded processor"),
+ ENUM_ENT(EM_X86_64, "Advanced Micro Devices X86-64"),
+ ENUM_ENT(EM_PDSP, "Sony DSP processor"),
+ ENUM_ENT(EM_PDP10, "Digital Equipment Corp. PDP-10"),
+ ENUM_ENT(EM_PDP11, "Digital Equipment Corp. PDP-11"),
+ ENUM_ENT(EM_FX66, "Siemens FX66 microcontroller"),
+ ENUM_ENT(EM_ST9PLUS, "STMicroelectronics ST9+ 8/16 bit microcontroller"),
+ ENUM_ENT(EM_ST7, "STMicroelectronics ST7 8-bit microcontroller"),
+ ENUM_ENT(EM_68HC16, "Motorola MC68HC16 Microcontroller"),
+ ENUM_ENT(EM_68HC11, "Motorola MC68HC11 Microcontroller"),
+ ENUM_ENT(EM_68HC08, "Motorola MC68HC08 Microcontroller"),
+ ENUM_ENT(EM_68HC05, "Motorola MC68HC05 Microcontroller"),
+ ENUM_ENT(EM_SVX, "Silicon Graphics SVx"),
+ ENUM_ENT(EM_ST19, "STMicroelectronics ST19 8-bit microcontroller"),
+ ENUM_ENT(EM_VAX, "Digital VAX"),
+ ENUM_ENT(EM_CRIS, "Axis Communications 32-bit embedded processor"),
+ ENUM_ENT(EM_JAVELIN, "Infineon Technologies 32-bit embedded cpu"),
+ ENUM_ENT(EM_FIREPATH, "Element 14 64-bit DSP processor"),
+ ENUM_ENT(EM_ZSP, "LSI Logic's 16-bit DSP processor"),
+ ENUM_ENT(EM_MMIX, "Donald Knuth's educational 64-bit processor"),
+ ENUM_ENT(EM_HUANY, "Harvard Universitys's machine-independent object format"),
+ ENUM_ENT(EM_PRISM, "Vitesse Prism"),
+ ENUM_ENT(EM_AVR, "Atmel AVR 8-bit microcontroller"),
+ ENUM_ENT(EM_FR30, "Fujitsu FR30"),
+ ENUM_ENT(EM_D10V, "Mitsubishi D10V"),
+ ENUM_ENT(EM_D30V, "Mitsubishi D30V"),
+ ENUM_ENT(EM_V850, "NEC v850"),
+ ENUM_ENT(EM_M32R, "Renesas M32R (formerly Mitsubishi M32r)"),
+ ENUM_ENT(EM_MN10300, "Matsushita MN10300"),
+ ENUM_ENT(EM_MN10200, "Matsushita MN10200"),
+ ENUM_ENT(EM_PJ, "picoJava"),
+ ENUM_ENT(EM_OPENRISC, "OpenRISC 32-bit embedded processor"),
+ ENUM_ENT(EM_ARC_COMPACT, "EM_ARC_COMPACT"),
+ ENUM_ENT(EM_XTENSA, "Tensilica Xtensa Processor"),
+ ENUM_ENT(EM_VIDEOCORE, "Alphamosaic VideoCore processor"),
+ ENUM_ENT(EM_TMM_GPP, "Thompson Multimedia General Purpose Processor"),
+ ENUM_ENT(EM_NS32K, "National Semiconductor 32000 series"),
+ ENUM_ENT(EM_TPC, "Tenor Network TPC processor"),
+ ENUM_ENT(EM_SNP1K, "EM_SNP1K"),
+ ENUM_ENT(EM_ST200, "STMicroelectronics ST200 microcontroller"),
+ ENUM_ENT(EM_IP2K, "Ubicom IP2xxx 8-bit microcontrollers"),
+ ENUM_ENT(EM_MAX, "MAX Processor"),
+ ENUM_ENT(EM_CR, "National Semiconductor CompactRISC"),
+ ENUM_ENT(EM_F2MC16, "Fujitsu F2MC16"),
+ ENUM_ENT(EM_MSP430, "Texas Instruments msp430 microcontroller"),
+ ENUM_ENT(EM_BLACKFIN, "Analog Devices Blackfin"),
+ ENUM_ENT(EM_SE_C33, "S1C33 Family of Seiko Epson processors"),
+ ENUM_ENT(EM_SEP, "Sharp embedded microprocessor"),
+ ENUM_ENT(EM_ARCA, "Arca RISC microprocessor"),
+ ENUM_ENT(EM_UNICORE, "Unicore"),
+ ENUM_ENT(EM_EXCESS, "eXcess 16/32/64-bit configurable embedded CPU"),
+ ENUM_ENT(EM_DXP, "Icera Semiconductor Inc. Deep Execution Processor"),
+ ENUM_ENT(EM_ALTERA_NIOS2, "Altera Nios"),
+ ENUM_ENT(EM_CRX, "National Semiconductor CRX microprocessor"),
+ ENUM_ENT(EM_XGATE, "Motorola XGATE embedded processor"),
+ ENUM_ENT(EM_C166, "Infineon Technologies xc16x"),
+ ENUM_ENT(EM_M16C, "Renesas M16C"),
+ ENUM_ENT(EM_DSPIC30F, "Microchip Technology dsPIC30F Digital Signal Controller"),
+ ENUM_ENT(EM_CE, "Freescale Communication Engine RISC core"),
+ ENUM_ENT(EM_M32C, "Renesas M32C"),
+ ENUM_ENT(EM_TSK3000, "Altium TSK3000 core"),
+ ENUM_ENT(EM_RS08, "Freescale RS08 embedded processor"),
+ ENUM_ENT(EM_SHARC, "EM_SHARC"),
+ ENUM_ENT(EM_ECOG2, "Cyan Technology eCOG2 microprocessor"),
+ ENUM_ENT(EM_SCORE7, "SUNPLUS S+Core"),
+ ENUM_ENT(EM_DSP24, "New Japan Radio (NJR) 24-bit DSP Processor"),
+ ENUM_ENT(EM_VIDEOCORE3, "Broadcom VideoCore III processor"),
+ ENUM_ENT(EM_LATTICEMICO32, "Lattice Mico32"),
+ ENUM_ENT(EM_SE_C17, "Seiko Epson C17 family"),
+ ENUM_ENT(EM_TI_C6000, "Texas Instruments TMS320C6000 DSP family"),
+ ENUM_ENT(EM_TI_C2000, "Texas Instruments TMS320C2000 DSP family"),
+ ENUM_ENT(EM_TI_C5500, "Texas Instruments TMS320C55x DSP family"),
+ ENUM_ENT(EM_MMDSP_PLUS, "STMicroelectronics 64bit VLIW Data Signal Processor"),
+ ENUM_ENT(EM_CYPRESS_M8C, "Cypress M8C microprocessor"),
+ ENUM_ENT(EM_R32C, "Renesas R32C series microprocessors"),
+ ENUM_ENT(EM_TRIMEDIA, "NXP Semiconductors TriMedia architecture family"),
+ ENUM_ENT(EM_HEXAGON, "Qualcomm Hexagon"),
+ ENUM_ENT(EM_8051, "Intel 8051 and variants"),
+ ENUM_ENT(EM_STXP7X, "STMicroelectronics STxP7x family"),
+ ENUM_ENT(EM_NDS32, "Andes Technology compact code size embedded RISC processor family"),
+ ENUM_ENT(EM_ECOG1, "Cyan Technology eCOG1 microprocessor"),
+ // FIXME: Following EM_ECOG1X definitions is dead code since EM_ECOG1X has
+ // an identical number to EM_ECOG1.
+ ENUM_ENT(EM_ECOG1X, "Cyan Technology eCOG1X family"),
+ ENUM_ENT(EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core microcontrollers"),
+ ENUM_ENT(EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor"),
+ ENUM_ENT(EM_MANIK, "M2000 Reconfigurable RISC Microprocessor"),
+ ENUM_ENT(EM_CRAYNV2, "Cray Inc. NV2 vector architecture"),
+ ENUM_ENT(EM_RX, "Renesas RX"),
+ ENUM_ENT(EM_METAG, "Imagination Technologies Meta processor architecture"),
+ ENUM_ENT(EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture"),
+ ENUM_ENT(EM_ECOG16, "Cyan Technology eCOG16 family"),
+ ENUM_ENT(EM_CR16, "National Semiconductor CompactRISC 16-bit processor"),
+ ENUM_ENT(EM_ETPU, "Freescale Extended Time Processing Unit"),
+ ENUM_ENT(EM_SLE9X, "Infineon Technologies SLE9X core"),
+ ENUM_ENT(EM_L10M, "EM_L10M"),
+ ENUM_ENT(EM_K10M, "EM_K10M"),
+ ENUM_ENT(EM_AARCH64, "AArch64"),
+ ENUM_ENT(EM_AVR32, "Atmel Corporation 32-bit microprocessor family"),
+ ENUM_ENT(EM_STM8, "STMicroeletronics STM8 8-bit microcontroller"),
+ ENUM_ENT(EM_TILE64, "Tilera TILE64 multicore architecture family"),
+ ENUM_ENT(EM_TILEPRO, "Tilera TILEPro multicore architecture family"),
+ ENUM_ENT(EM_MICROBLAZE, "Xilinx MicroBlaze 32-bit RISC soft processor core"),
+ ENUM_ENT(EM_CUDA, "NVIDIA CUDA architecture"),
+ ENUM_ENT(EM_TILEGX, "Tilera TILE-Gx multicore architecture family"),
+ ENUM_ENT(EM_CLOUDSHIELD, "EM_CLOUDSHIELD"),
+ ENUM_ENT(EM_COREA_1ST, "EM_COREA_1ST"),
+ ENUM_ENT(EM_COREA_2ND, "EM_COREA_2ND"),
+ ENUM_ENT(EM_ARC_COMPACT2, "EM_ARC_COMPACT2"),
+ ENUM_ENT(EM_OPEN8, "EM_OPEN8"),
+ ENUM_ENT(EM_RL78, "Renesas RL78"),
+ ENUM_ENT(EM_VIDEOCORE5, "Broadcom VideoCore V processor"),
+ ENUM_ENT(EM_78KOR, "EM_78KOR"),
+ ENUM_ENT(EM_56800EX, "EM_56800EX"),
+ ENUM_ENT(EM_AMDGPU, "EM_AMDGPU"),
+ ENUM_ENT(EM_RISCV, "RISC-V"),
+ ENUM_ENT(EM_LANAI, "EM_LANAI"),
+ ENUM_ENT(EM_BPF, "EM_BPF"),
+ ENUM_ENT(EM_VE, "NEC SX-Aurora Vector Engine"),
+};
+
+const EnumEntry<unsigned> ElfSymbolBindings[] = {
+ {"Local", "LOCAL", ELF::STB_LOCAL},
+ {"Global", "GLOBAL", ELF::STB_GLOBAL},
+ {"Weak", "WEAK", ELF::STB_WEAK},
+ {"Unique", "UNIQUE", ELF::STB_GNU_UNIQUE}};
+
+const EnumEntry<unsigned> ElfSymbolVisibilities[] = {
+ {"DEFAULT", "DEFAULT", ELF::STV_DEFAULT},
+ {"INTERNAL", "INTERNAL", ELF::STV_INTERNAL},
+ {"HIDDEN", "HIDDEN", ELF::STV_HIDDEN},
+ {"PROTECTED", "PROTECTED", ELF::STV_PROTECTED}};
+
+const EnumEntry<unsigned> AMDGPUSymbolTypes[] = {
+ { "AMDGPU_HSA_KERNEL", ELF::STT_AMDGPU_HSA_KERNEL }
+};
+
+static const char *getGroupType(uint32_t Flag) {
+ if (Flag & ELF::GRP_COMDAT)
+ return "COMDAT";
+ else
+ return "(unknown)";
+}
+
+const EnumEntry<unsigned> ElfSectionFlags[] = {
+ ENUM_ENT(SHF_WRITE, "W"),
+ ENUM_ENT(SHF_ALLOC, "A"),
+ ENUM_ENT(SHF_EXECINSTR, "X"),
+ ENUM_ENT(SHF_MERGE, "M"),
+ ENUM_ENT(SHF_STRINGS, "S"),
+ ENUM_ENT(SHF_INFO_LINK, "I"),
+ ENUM_ENT(SHF_LINK_ORDER, "L"),
+ ENUM_ENT(SHF_OS_NONCONFORMING, "O"),
+ ENUM_ENT(SHF_GROUP, "G"),
+ ENUM_ENT(SHF_TLS, "T"),
+ ENUM_ENT(SHF_COMPRESSED, "C"),
+ ENUM_ENT(SHF_GNU_RETAIN, "R"),
+ ENUM_ENT(SHF_EXCLUDE, "E"),
+};
+
+const EnumEntry<unsigned> ElfXCoreSectionFlags[] = {
+ ENUM_ENT(XCORE_SHF_CP_SECTION, ""),
+ ENUM_ENT(XCORE_SHF_DP_SECTION, "")
+};
+
+const EnumEntry<unsigned> ElfARMSectionFlags[] = {
+ ENUM_ENT(SHF_ARM_PURECODE, "y")
+};
+
+const EnumEntry<unsigned> ElfHexagonSectionFlags[] = {
+ ENUM_ENT(SHF_HEX_GPREL, "")
+};
+
+const EnumEntry<unsigned> ElfMipsSectionFlags[] = {
+ ENUM_ENT(SHF_MIPS_NODUPES, ""),
+ ENUM_ENT(SHF_MIPS_NAMES, ""),
+ ENUM_ENT(SHF_MIPS_LOCAL, ""),
+ ENUM_ENT(SHF_MIPS_NOSTRIP, ""),
+ ENUM_ENT(SHF_MIPS_GPREL, ""),
+ ENUM_ENT(SHF_MIPS_MERGE, ""),
+ ENUM_ENT(SHF_MIPS_ADDR, ""),
+ ENUM_ENT(SHF_MIPS_STRING, "")
+};
+
+const EnumEntry<unsigned> ElfX86_64SectionFlags[] = {
+ ENUM_ENT(SHF_X86_64_LARGE, "l")
+};
+
+static std::vector<EnumEntry<unsigned>>
+getSectionFlagsForTarget(unsigned EMachine) {
+ std::vector<EnumEntry<unsigned>> Ret(std::begin(ElfSectionFlags),
+ std::end(ElfSectionFlags));
+ switch (EMachine) {
+ case EM_ARM:
+ Ret.insert(Ret.end(), std::begin(ElfARMSectionFlags),
+ std::end(ElfARMSectionFlags));
+ break;
+ case EM_HEXAGON:
+ Ret.insert(Ret.end(), std::begin(ElfHexagonSectionFlags),
+ std::end(ElfHexagonSectionFlags));
+ break;
+ case EM_MIPS:
+ Ret.insert(Ret.end(), std::begin(ElfMipsSectionFlags),
+ std::end(ElfMipsSectionFlags));
+ break;
+ case EM_X86_64:
+ Ret.insert(Ret.end(), std::begin(ElfX86_64SectionFlags),
+ std::end(ElfX86_64SectionFlags));
+ break;
+ case EM_XCORE:
+ Ret.insert(Ret.end(), std::begin(ElfXCoreSectionFlags),
+ std::end(ElfXCoreSectionFlags));
+ break;
+ default:
+ break;
+ }
+ return Ret;
+}
+
+static std::string getGNUFlags(unsigned EMachine, uint64_t Flags) {
+ // Here we are trying to build the flags string in the same way as GNU does.
+ // It is not that straightforward. Imagine we have sh_flags == 0x90000000.
+ // SHF_EXCLUDE ("E") has a value of 0x80000000 and SHF_MASKPROC is 0xf0000000.
+ // GNU readelf will not print "E" or "Ep" in this case, but will print just
+ // "p". It only will print "E" when no other processor flag is set.
+ std::string Str;
+ bool HasUnknownFlag = false;
+ bool HasOSFlag = false;
+ bool HasProcFlag = false;
+ std::vector<EnumEntry<unsigned>> FlagsList =
+ getSectionFlagsForTarget(EMachine);
+ while (Flags) {
+ // Take the least significant bit as a flag.
+ uint64_t Flag = Flags & -Flags;
+ Flags -= Flag;
+
+ // Find the flag in the known flags list.
+ auto I = llvm::find_if(FlagsList, [=](const EnumEntry<unsigned> &E) {
+ // Flags with empty names are not printed in GNU style output.
+ return E.Value == Flag && !E.AltName.empty();
+ });
+ if (I != FlagsList.end()) {
+ Str += I->AltName;
+ continue;
+ }
+
+ // If we did not find a matching regular flag, then we deal with an OS
+ // specific flag, processor specific flag or an unknown flag.
+ if (Flag & ELF::SHF_MASKOS) {
+ HasOSFlag = true;
+ Flags &= ~ELF::SHF_MASKOS;
+ } else if (Flag & ELF::SHF_MASKPROC) {
+ HasProcFlag = true;
+ // Mask off all the processor-specific bits. This removes the SHF_EXCLUDE
+ // bit if set so that it doesn't also get printed.
+ Flags &= ~ELF::SHF_MASKPROC;
+ } else {
+ HasUnknownFlag = true;
+ }
+ }
+
+ // "o", "p" and "x" are printed last.
+ if (HasOSFlag)
+ Str += "o";
+ if (HasProcFlag)
+ Str += "p";
+ if (HasUnknownFlag)
+ Str += "x";
+ return Str;
+}
+
+static StringRef segmentTypeToString(unsigned Arch, unsigned Type) {
+ // Check potentially overlapped processor-specific program header type.
+ switch (Arch) {
+ case ELF::EM_ARM:
+ switch (Type) { LLVM_READOBJ_ENUM_CASE(ELF, PT_ARM_EXIDX); }
+ break;
+ case ELF::EM_MIPS:
+ case ELF::EM_MIPS_RS3_LE:
+ switch (Type) {
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_REGINFO);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_RTPROC);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_OPTIONS);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_ABIFLAGS);
+ }
+ break;
+ }
+
+ switch (Type) {
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_NULL);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_LOAD);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_DYNAMIC);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_INTERP);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_NOTE);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_SHLIB);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_PHDR);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_TLS);
+
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_EH_FRAME);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_SUNW_UNWIND);
+
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_STACK);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_RELRO);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_PROPERTY);
+
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_RANDOMIZE);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_WXNEEDED);
+ LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_BOOTDATA);
+ default:
+ return "";
+ }
+}
+
+static std::string getGNUPtType(unsigned Arch, unsigned Type) {
+ StringRef Seg = segmentTypeToString(Arch, Type);
+ if (Seg.empty())
+ return std::string("<unknown>: ") + to_string(format_hex(Type, 1));
+
+ // E.g. "PT_ARM_EXIDX" -> "EXIDX".
+ if (Seg.startswith("PT_ARM_"))
+ return Seg.drop_front(7).str();
+
+ // E.g. "PT_MIPS_REGINFO" -> "REGINFO".
+ if (Seg.startswith("PT_MIPS_"))
+ return Seg.drop_front(8).str();
+
+ // E.g. "PT_LOAD" -> "LOAD".
+ assert(Seg.startswith("PT_"));
+ return Seg.drop_front(3).str();
+}
+
+const EnumEntry<unsigned> ElfSegmentFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, PF_X),
+ LLVM_READOBJ_ENUM_ENT(ELF, PF_W),
+ LLVM_READOBJ_ENUM_ENT(ELF, PF_R)
+};
+
+const EnumEntry<unsigned> ElfHeaderMipsFlags[] = {
+ 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")
+};
+
+const EnumEntry<unsigned> ElfHeaderAMDGPUFlagsABIVersion3[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_NONE),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R600),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R630),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RS880),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV670),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV710),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV730),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV770),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CEDAR),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CYPRESS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_JUNIPER),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_REDWOOD),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_SUMO),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_BARTS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAICOS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAYMAN),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_TURKS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX600),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX601),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX602),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX700),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX701),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX702),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX703),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX704),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX705),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX801),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX802),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX803),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX805),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX810),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX900),
+ 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_MACH_AMDGCN_GFX908),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX909),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90A),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90C),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1010),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1011),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1012),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1013),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1030),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1031),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1032),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1033),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1034),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1035),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_V3),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_V3)
+};
+
+const EnumEntry<unsigned> ElfHeaderAMDGPUFlagsABIVersion4[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_NONE),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R600),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R630),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RS880),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV670),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV710),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV730),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV770),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CEDAR),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CYPRESS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_JUNIPER),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_REDWOOD),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_SUMO),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_BARTS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAICOS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAYMAN),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_TURKS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX600),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX601),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX602),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX700),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX701),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX702),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX703),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX704),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX705),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX801),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX802),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX803),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX805),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX810),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX900),
+ 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_MACH_AMDGCN_GFX908),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX909),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90A),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90C),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1010),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1011),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1012),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1013),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1030),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1031),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1032),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1033),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1034),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1035),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_ANY_V4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_OFF_V4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_ON_V4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_ANY_V4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_OFF_V4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_ON_V4)
+};
+
+const EnumEntry<unsigned> ElfHeaderRISCVFlags[] = {
+ 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"),
+ ENUM_ENT(EF_RISCV_TSO, "TSO"),
+};
+
+const EnumEntry<unsigned> ElfHeaderAVRFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR1),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR2),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR25),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR3),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR31),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR35),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR5),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR51),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR6),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVRTINY),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA1),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA2),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA3),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA4),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA5),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA6),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA7),
+ ENUM_ENT(EF_AVR_LINKRELAX_PREPARED, "relaxable"),
+};
+
+
+const EnumEntry<unsigned> ElfSymOtherFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, STV_INTERNAL),
+ LLVM_READOBJ_ENUM_ENT(ELF, STV_HIDDEN),
+ LLVM_READOBJ_ENUM_ENT(ELF, STV_PROTECTED)
+};
+
+const EnumEntry<unsigned> ElfMipsSymOtherFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_OPTIONAL),
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_PLT),
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_PIC),
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_MICROMIPS)
+};
+
+const EnumEntry<unsigned> ElfAArch64SymOtherFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_AARCH64_VARIANT_PCS)
+};
+
+const EnumEntry<unsigned> ElfMips16SymOtherFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_OPTIONAL),
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_PLT),
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_MIPS16)
+};
+
+const EnumEntry<unsigned> ElfRISCVSymOtherFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(ELF, STO_RISCV_VARIANT_CC)};
+
+static const char *getElfMipsOptionsOdkType(unsigned Odk) {
+ switch (Odk) {
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_NULL);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_REGINFO);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_EXCEPTIONS);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_PAD);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWPATCH);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_FILL);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_TAGS);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWAND);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWOR);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_GP_GROUP);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_IDENT);
+ LLVM_READOBJ_ENUM_CASE(ELF, ODK_PAGESIZE);
+ default:
+ return "Unknown";
+ }
+}
+
+template <typename ELFT>
+std::pair<const typename ELFT::Phdr *, const typename ELFT::Shdr *>
+ELFDumper<ELFT>::findDynamic() {
+ // Try to locate the PT_DYNAMIC header.
+ const Elf_Phdr *DynamicPhdr = nullptr;
+ if (Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj.program_headers()) {
+ for (const Elf_Phdr &Phdr : *PhdrsOrErr) {
+ if (Phdr.p_type != ELF::PT_DYNAMIC)
+ continue;
+ DynamicPhdr = &Phdr;
+ break;
+ }
+ } else {
+ reportUniqueWarning(
+ "unable to read program headers to locate the PT_DYNAMIC segment: " +
+ toString(PhdrsOrErr.takeError()));
+ }
+
+ // Try to locate the .dynamic section in the sections header table.
+ const Elf_Shdr *DynamicSec = nullptr;
+ for (const Elf_Shdr &Sec : cantFail(Obj.sections())) {
+ if (Sec.sh_type != ELF::SHT_DYNAMIC)
+ continue;
+ DynamicSec = &Sec;
+ break;
+ }
+
+ if (DynamicPhdr && ((DynamicPhdr->p_offset + DynamicPhdr->p_filesz >
+ ObjF.getMemoryBufferRef().getBufferSize()) ||
+ (DynamicPhdr->p_offset + DynamicPhdr->p_filesz <
+ DynamicPhdr->p_offset))) {
+ reportUniqueWarning(
+ "PT_DYNAMIC segment offset (0x" +
+ Twine::utohexstr(DynamicPhdr->p_offset) + ") + file size (0x" +
+ Twine::utohexstr(DynamicPhdr->p_filesz) +
+ ") exceeds the size of the file (0x" +
+ Twine::utohexstr(ObjF.getMemoryBufferRef().getBufferSize()) + ")");
+ // Don't use the broken dynamic header.
+ DynamicPhdr = nullptr;
+ }
+
+ if (DynamicPhdr && DynamicSec) {
+ if (DynamicSec->sh_addr + DynamicSec->sh_size >
+ DynamicPhdr->p_vaddr + DynamicPhdr->p_memsz ||
+ DynamicSec->sh_addr < DynamicPhdr->p_vaddr)
+ reportUniqueWarning(describe(*DynamicSec) +
+ " is not contained within the "
+ "PT_DYNAMIC segment");
+
+ if (DynamicSec->sh_addr != DynamicPhdr->p_vaddr)
+ reportUniqueWarning(describe(*DynamicSec) + " is not at the start of "
+ "PT_DYNAMIC segment");
+ }
+
+ return std::make_pair(DynamicPhdr, DynamicSec);
+}
+
+template <typename ELFT>
+void ELFDumper<ELFT>::loadDynamicTable() {
+ const Elf_Phdr *DynamicPhdr;
+ const Elf_Shdr *DynamicSec;
+ std::tie(DynamicPhdr, DynamicSec) = findDynamic();
+ if (!DynamicPhdr && !DynamicSec)
+ return;
+
+ DynRegionInfo FromPhdr(ObjF, *this);
+ bool IsPhdrTableValid = false;
+ if (DynamicPhdr) {
+ // Use cantFail(), because p_offset/p_filesz fields of a PT_DYNAMIC are
+ // validated in findDynamic() and so createDRI() is not expected to fail.
+ FromPhdr = cantFail(createDRI(DynamicPhdr->p_offset, DynamicPhdr->p_filesz,
+ sizeof(Elf_Dyn)));
+ FromPhdr.SizePrintName = "PT_DYNAMIC size";
+ FromPhdr.EntSizePrintName = "";
+ IsPhdrTableValid = !FromPhdr.template getAsArrayRef<Elf_Dyn>().empty();
+ }
+
+ // Locate the dynamic table described in a section header.
+ // Ignore sh_entsize and use the expected value for entry size explicitly.
+ // This allows us to dump dynamic sections with a broken sh_entsize
+ // field.
+ DynRegionInfo FromSec(ObjF, *this);
+ bool IsSecTableValid = false;
+ if (DynamicSec) {
+ Expected<DynRegionInfo> RegOrErr =
+ createDRI(DynamicSec->sh_offset, DynamicSec->sh_size, sizeof(Elf_Dyn));
+ if (RegOrErr) {
+ FromSec = *RegOrErr;
+ FromSec.Context = describe(*DynamicSec);
+ FromSec.EntSizePrintName = "";
+ IsSecTableValid = !FromSec.template getAsArrayRef<Elf_Dyn>().empty();
+ } else {
+ reportUniqueWarning("unable to read the dynamic table from " +
+ describe(*DynamicSec) + ": " +
+ toString(RegOrErr.takeError()));
+ }
+ }
+
+ // When we only have information from one of the SHT_DYNAMIC section header or
+ // PT_DYNAMIC program header, just use that.
+ if (!DynamicPhdr || !DynamicSec) {
+ if ((DynamicPhdr && IsPhdrTableValid) || (DynamicSec && IsSecTableValid)) {
+ DynamicTable = DynamicPhdr ? FromPhdr : FromSec;
+ parseDynamicTable();
+ } else {
+ reportUniqueWarning("no valid dynamic table was found");
+ }
+ return;
+ }
+
+ // At this point we have tables found from the section header and from the
+ // dynamic segment. Usually they match, but we have to do sanity checks to
+ // verify that.
+
+ if (FromPhdr.Addr != FromSec.Addr)
+ reportUniqueWarning("SHT_DYNAMIC section header and PT_DYNAMIC "
+ "program header disagree about "
+ "the location of the dynamic table");
+
+ if (!IsPhdrTableValid && !IsSecTableValid) {
+ reportUniqueWarning("no valid dynamic table was found");
+ return;
+ }
+
+ // Information in the PT_DYNAMIC program header has priority over the
+ // information in a section header.
+ if (IsPhdrTableValid) {
+ if (!IsSecTableValid)
+ reportUniqueWarning(
+ "SHT_DYNAMIC dynamic table is invalid: PT_DYNAMIC will be used");
+ DynamicTable = FromPhdr;
+ } else {
+ reportUniqueWarning(
+ "PT_DYNAMIC dynamic table is invalid: SHT_DYNAMIC will be used");
+ DynamicTable = FromSec;
+ }
+
+ parseDynamicTable();
+}
+
+template <typename ELFT>
+ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> &O,
+ ScopedPrinter &Writer)
+ : ObjDumper(Writer, O.getFileName()), ObjF(O), Obj(O.getELFFile()),
+ FileName(O.getFileName()), DynRelRegion(O, *this),
+ DynRelaRegion(O, *this), DynRelrRegion(O, *this),
+ DynPLTRelRegion(O, *this), DynSymTabShndxRegion(O, *this),
+ DynamicTable(O, *this) {
+ if (!O.IsContentValid())
+ return;
+
+ typename ELFT::ShdrRange Sections = cantFail(Obj.sections());
+ for (const Elf_Shdr &Sec : Sections) {
+ switch (Sec.sh_type) {
+ case ELF::SHT_SYMTAB:
+ if (!DotSymtabSec)
+ DotSymtabSec = &Sec;
+ break;
+ case ELF::SHT_DYNSYM:
+ if (!DotDynsymSec)
+ DotDynsymSec = &Sec;
+
+ if (!DynSymRegion) {
+ Expected<DynRegionInfo> RegOrErr =
+ createDRI(Sec.sh_offset, Sec.sh_size, Sec.sh_entsize);
+ if (RegOrErr) {
+ DynSymRegion = *RegOrErr;
+ DynSymRegion->Context = describe(Sec);
+
+ if (Expected<StringRef> E = Obj.getStringTableForSymtab(Sec))
+ DynamicStringTable = *E;
+ else
+ reportUniqueWarning("unable to get the string table for the " +
+ describe(Sec) + ": " + toString(E.takeError()));
+ } else {
+ reportUniqueWarning("unable to read dynamic symbols from " +
+ describe(Sec) + ": " +
+ toString(RegOrErr.takeError()));
+ }
+ }
+ break;
+ case ELF::SHT_SYMTAB_SHNDX: {
+ uint32_t SymtabNdx = Sec.sh_link;
+ if (SymtabNdx >= Sections.size()) {
+ reportUniqueWarning(
+ "unable to get the associated symbol table for " + describe(Sec) +
+ ": sh_link (" + Twine(SymtabNdx) +
+ ") is greater than or equal to the total number of sections (" +
+ Twine(Sections.size()) + ")");
+ continue;
+ }
+
+ if (Expected<ArrayRef<Elf_Word>> ShndxTableOrErr =
+ Obj.getSHNDXTable(Sec)) {
+ if (!ShndxTables.insert({&Sections[SymtabNdx], *ShndxTableOrErr})
+ .second)
+ reportUniqueWarning(
+ "multiple SHT_SYMTAB_SHNDX sections are linked to " +
+ describe(Sec));
+ } else {
+ reportUniqueWarning(ShndxTableOrErr.takeError());
+ }
+ break;
+ }
+ case ELF::SHT_GNU_versym:
+ if (!SymbolVersionSection)
+ SymbolVersionSection = &Sec;
+ break;
+ case ELF::SHT_GNU_verdef:
+ if (!SymbolVersionDefSection)
+ SymbolVersionDefSection = &Sec;
+ break;
+ case ELF::SHT_GNU_verneed:
+ if (!SymbolVersionNeedSection)
+ SymbolVersionNeedSection = &Sec;
+ break;
+ case ELF::SHT_LLVM_ADDRSIG:
+ if (!DotAddrsigSec)
+ DotAddrsigSec = &Sec;
+ break;
+ }
+ }
+
+ loadDynamicTable();
+}
+
+template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() {
+ auto toMappedAddr = [&](uint64_t Tag, uint64_t VAddr) -> const uint8_t * {
+ auto MappedAddrOrError = Obj.toMappedAddr(VAddr, [&](const Twine &Msg) {
+ this->reportUniqueWarning(Msg);
+ return Error::success();
+ });
+ if (!MappedAddrOrError) {
+ this->reportUniqueWarning("unable to parse DT_" +
+ Obj.getDynamicTagAsString(Tag) + ": " +
+ llvm::toString(MappedAddrOrError.takeError()));
+ return nullptr;
+ }
+ return MappedAddrOrError.get();
+ };
+
+ const char *StringTableBegin = nullptr;
+ uint64_t StringTableSize = 0;
+ Optional<DynRegionInfo> DynSymFromTable;
+ for (const Elf_Dyn &Dyn : dynamic_table()) {
+ switch (Dyn.d_tag) {
+ case ELF::DT_HASH:
+ HashTable = reinterpret_cast<const Elf_Hash *>(
+ toMappedAddr(Dyn.getTag(), Dyn.getPtr()));
+ break;
+ case ELF::DT_GNU_HASH:
+ GnuHashTable = reinterpret_cast<const Elf_GnuHash *>(
+ toMappedAddr(Dyn.getTag(), Dyn.getPtr()));
+ break;
+ case ELF::DT_STRTAB:
+ StringTableBegin = reinterpret_cast<const char *>(
+ toMappedAddr(Dyn.getTag(), Dyn.getPtr()));
+ break;
+ case ELF::DT_STRSZ:
+ StringTableSize = Dyn.getVal();
+ break;
+ case ELF::DT_SYMTAB: {
+ // If we can't map the DT_SYMTAB value to an address (e.g. when there are
+ // no program headers), we ignore its value.
+ if (const uint8_t *VA = toMappedAddr(Dyn.getTag(), Dyn.getPtr())) {
+ DynSymFromTable.emplace(ObjF, *this);
+ DynSymFromTable->Addr = VA;
+ DynSymFromTable->EntSize = sizeof(Elf_Sym);
+ DynSymFromTable->EntSizePrintName = "";
+ }
+ break;
+ }
+ case ELF::DT_SYMENT: {
+ uint64_t Val = Dyn.getVal();
+ if (Val != sizeof(Elf_Sym))
+ this->reportUniqueWarning("DT_SYMENT value of 0x" +
+ Twine::utohexstr(Val) +
+ " is not the size of a symbol (0x" +
+ Twine::utohexstr(sizeof(Elf_Sym)) + ")");
+ break;
+ }
+ case ELF::DT_RELA:
+ DynRelaRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr());
+ break;
+ case ELF::DT_RELASZ:
+ DynRelaRegion.Size = Dyn.getVal();
+ DynRelaRegion.SizePrintName = "DT_RELASZ value";
+ break;
+ case ELF::DT_RELAENT:
+ DynRelaRegion.EntSize = Dyn.getVal();
+ DynRelaRegion.EntSizePrintName = "DT_RELAENT value";
+ break;
+ case ELF::DT_SONAME:
+ SONameOffset = Dyn.getVal();
+ break;
+ case ELF::DT_REL:
+ DynRelRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr());
+ break;
+ case ELF::DT_RELSZ:
+ DynRelRegion.Size = Dyn.getVal();
+ DynRelRegion.SizePrintName = "DT_RELSZ value";
+ break;
+ case ELF::DT_RELENT:
+ DynRelRegion.EntSize = Dyn.getVal();
+ DynRelRegion.EntSizePrintName = "DT_RELENT value";
+ break;
+ case ELF::DT_RELR:
+ case ELF::DT_ANDROID_RELR:
+ DynRelrRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr());
+ break;
+ case ELF::DT_RELRSZ:
+ case ELF::DT_ANDROID_RELRSZ:
+ DynRelrRegion.Size = Dyn.getVal();
+ DynRelrRegion.SizePrintName = Dyn.d_tag == ELF::DT_RELRSZ
+ ? "DT_RELRSZ value"
+ : "DT_ANDROID_RELRSZ value";
+ break;
+ case ELF::DT_RELRENT:
+ case ELF::DT_ANDROID_RELRENT:
+ DynRelrRegion.EntSize = Dyn.getVal();
+ DynRelrRegion.EntSizePrintName = Dyn.d_tag == ELF::DT_RELRENT
+ ? "DT_RELRENT value"
+ : "DT_ANDROID_RELRENT value";
+ break;
+ case ELF::DT_PLTREL:
+ if (Dyn.getVal() == DT_REL)
+ DynPLTRelRegion.EntSize = sizeof(Elf_Rel);
+ else if (Dyn.getVal() == DT_RELA)
+ DynPLTRelRegion.EntSize = sizeof(Elf_Rela);
+ else
+ reportUniqueWarning(Twine("unknown DT_PLTREL value of ") +
+ Twine((uint64_t)Dyn.getVal()));
+ DynPLTRelRegion.EntSizePrintName = "PLTREL entry size";
+ break;
+ case ELF::DT_JMPREL:
+ DynPLTRelRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr());
+ break;
+ case ELF::DT_PLTRELSZ:
+ DynPLTRelRegion.Size = Dyn.getVal();
+ DynPLTRelRegion.SizePrintName = "DT_PLTRELSZ value";
+ break;
+ case ELF::DT_SYMTAB_SHNDX:
+ DynSymTabShndxRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr());
+ DynSymTabShndxRegion.EntSize = sizeof(Elf_Word);
+ break;
+ }
+ }
+
+ if (StringTableBegin) {
+ const uint64_t FileSize = Obj.getBufSize();
+ const uint64_t Offset = (const uint8_t *)StringTableBegin - Obj.base();
+ if (StringTableSize > FileSize - Offset)
+ reportUniqueWarning(
+ "the dynamic string table at 0x" + Twine::utohexstr(Offset) +
+ " goes past the end of the file (0x" + Twine::utohexstr(FileSize) +
+ ") with DT_STRSZ = 0x" + Twine::utohexstr(StringTableSize));
+ else
+ DynamicStringTable = StringRef(StringTableBegin, StringTableSize);
+ }
+
+ const bool IsHashTableSupported = getHashTableEntSize() == 4;
+ if (DynSymRegion) {
+ // Often we find the information about the dynamic symbol table
+ // location in the SHT_DYNSYM section header. However, the value in
+ // DT_SYMTAB has priority, because it is used by dynamic loaders to
+ // locate .dynsym at runtime. The location we find in the section header
+ // and the location we find here should match.
+ if (DynSymFromTable && DynSymFromTable->Addr != DynSymRegion->Addr)
+ reportUniqueWarning(
+ createError("SHT_DYNSYM section header and DT_SYMTAB disagree about "
+ "the location of the dynamic symbol table"));
+
+ // According to the ELF gABI: "The number of symbol table entries should
+ // equal nchain". Check to see if the DT_HASH hash table nchain value
+ // conflicts with the number of symbols in the dynamic symbol table
+ // according to the section header.
+ if (HashTable && IsHashTableSupported) {
+ if (DynSymRegion->EntSize == 0)
+ reportUniqueWarning("SHT_DYNSYM section has sh_entsize == 0");
+ else if (HashTable->nchain != DynSymRegion->Size / DynSymRegion->EntSize)
+ reportUniqueWarning(
+ "hash table nchain (" + Twine(HashTable->nchain) +
+ ") differs from symbol count derived from SHT_DYNSYM section "
+ "header (" +
+ Twine(DynSymRegion->Size / DynSymRegion->EntSize) + ")");
+ }
+ }
+
+ // Delay the creation of the actual dynamic symbol table until now, so that
+ // checks can always be made against the section header-based properties,
+ // without worrying about tag order.
+ if (DynSymFromTable) {
+ if (!DynSymRegion) {
+ DynSymRegion = DynSymFromTable;
+ } else {
+ DynSymRegion->Addr = DynSymFromTable->Addr;
+ DynSymRegion->EntSize = DynSymFromTable->EntSize;
+ DynSymRegion->EntSizePrintName = DynSymFromTable->EntSizePrintName;
+ }
+ }
+
+ // Derive the dynamic symbol table size from the DT_HASH hash table, if
+ // present.
+ if (HashTable && IsHashTableSupported && DynSymRegion) {
+ const uint64_t FileSize = Obj.getBufSize();
+ const uint64_t DerivedSize =
+ (uint64_t)HashTable->nchain * DynSymRegion->EntSize;
+ const uint64_t Offset = (const uint8_t *)DynSymRegion->Addr - Obj.base();
+ if (DerivedSize > FileSize - Offset)
+ reportUniqueWarning(
+ "the size (0x" + Twine::utohexstr(DerivedSize) +
+ ") of the dynamic symbol table at 0x" + Twine::utohexstr(Offset) +
+ ", derived from the hash table, goes past the end of the file (0x" +
+ Twine::utohexstr(FileSize) + ") and will be ignored");
+ else
+ DynSymRegion->Size = HashTable->nchain * DynSymRegion->EntSize;
+ }
+}
+
+template <typename ELFT> void ELFDumper<ELFT>::printVersionInfo() {
+ // Dump version symbol section.
+ printVersionSymbolSection(SymbolVersionSection);
+
+ // Dump version definition section.
+ printVersionDefinitionSection(SymbolVersionDefSection);
+
+ // Dump version dependency section.
+ printVersionDependencySection(SymbolVersionNeedSection);
+}
+
+#define LLVM_READOBJ_DT_FLAG_ENT(prefix, enum) \
+ { #enum, prefix##_##enum }
+
+const EnumEntry<unsigned> ElfDynamicDTFlags[] = {
+ LLVM_READOBJ_DT_FLAG_ENT(DF, ORIGIN),
+ LLVM_READOBJ_DT_FLAG_ENT(DF, SYMBOLIC),
+ LLVM_READOBJ_DT_FLAG_ENT(DF, TEXTREL),
+ LLVM_READOBJ_DT_FLAG_ENT(DF, BIND_NOW),
+ LLVM_READOBJ_DT_FLAG_ENT(DF, STATIC_TLS)
+};
+
+const EnumEntry<unsigned> ElfDynamicDTFlags1[] = {
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOW),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, GLOBAL),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, GROUP),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODELETE),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, LOADFLTR),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, INITFIRST),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOOPEN),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, ORIGIN),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, DIRECT),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, TRANS),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, INTERPOSE),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODEFLIB),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODUMP),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, CONFALT),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, ENDFILTEE),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, DISPRELDNE),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, DISPRELPND),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODIRECT),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, IGNMULDEF),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOKSYMS),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOHDR),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, EDITED),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, NORELOC),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, SYMINTPOSE),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, GLOBAUDIT),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, SINGLETON),
+ LLVM_READOBJ_DT_FLAG_ENT(DF_1, PIE),
+};
+
+const EnumEntry<unsigned> ElfDynamicDTMipsFlags[] = {
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, NONE),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, QUICKSTART),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, NOTPOT),
+ LLVM_READOBJ_DT_FLAG_ENT(RHS, NO_LIBRARY_REPLACEMENT),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, NO_MOVE),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, SGI_ONLY),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, GUARANTEE_INIT),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, DELTA_C_PLUS_PLUS),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, GUARANTEE_START_INIT),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, PIXIE),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, DEFAULT_DELAY_LOAD),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, REQUICKSTART),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, REQUICKSTARTED),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, CORD),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, NO_UNRES_UNDEF),
+ LLVM_READOBJ_DT_FLAG_ENT(RHF, RLD_ORDER_SAFE)
+};
+
+#undef LLVM_READOBJ_DT_FLAG_ENT
+
+template <typename T, typename TFlag>
+void printFlags(T Value, ArrayRef<EnumEntry<TFlag>> Flags, raw_ostream &OS) {
+ SmallVector<EnumEntry<TFlag>, 10> SetFlags;
+ for (const EnumEntry<TFlag> &Flag : Flags)
+ if (Flag.Value != 0 && (Value & Flag.Value) == Flag.Value)
+ SetFlags.push_back(Flag);
+
+ for (const EnumEntry<TFlag> &Flag : SetFlags)
+ OS << Flag.Name << " ";
+}
+
+template <class ELFT>
+const typename ELFT::Shdr *
+ELFDumper<ELFT>::findSectionByName(StringRef Name) const {
+ for (const Elf_Shdr &Shdr : cantFail(Obj.sections())) {
+ if (Expected<StringRef> NameOrErr = Obj.getSectionName(Shdr)) {
+ if (*NameOrErr == Name)
+ return &Shdr;
+ } else {
+ reportUniqueWarning("unable to read the name of " + describe(Shdr) +
+ ": " + toString(NameOrErr.takeError()));
+ }
+ }
+ return nullptr;
+}
+
+template <class ELFT>
+std::string ELFDumper<ELFT>::getDynamicEntry(uint64_t Type,
+ uint64_t Value) const {
+ auto FormatHexValue = [](uint64_t V) {
+ std::string Str;
+ raw_string_ostream OS(Str);
+ const char *ConvChar =
+ (opts::Output == opts::GNU) ? "0x%" PRIx64 : "0x%" PRIX64;
+ OS << format(ConvChar, V);
+ return OS.str();
+ };
+
+ auto FormatFlags = [](uint64_t V,
+ llvm::ArrayRef<llvm::EnumEntry<unsigned int>> Array) {
+ std::string Str;
+ raw_string_ostream OS(Str);
+ printFlags(V, Array, OS);
+ return OS.str();
+ };
+
+ // Handle custom printing of architecture specific tags
+ switch (Obj.getHeader().e_machine) {
+ case EM_AARCH64:
+ switch (Type) {
+ case DT_AARCH64_BTI_PLT:
+ case DT_AARCH64_PAC_PLT:
+ case DT_AARCH64_VARIANT_PCS:
+ return std::to_string(Value);
+ default:
+ break;
+ }
+ break;
+ case EM_HEXAGON:
+ switch (Type) {
+ case DT_HEXAGON_VER:
+ return std::to_string(Value);
+ case DT_HEXAGON_SYMSZ:
+ case DT_HEXAGON_PLT:
+ return FormatHexValue(Value);
+ default:
+ break;
+ }
+ break;
+ case EM_MIPS:
+ switch (Type) {
+ case DT_MIPS_RLD_VERSION:
+ case DT_MIPS_LOCAL_GOTNO:
+ case DT_MIPS_SYMTABNO:
+ case DT_MIPS_UNREFEXTNO:
+ return std::to_string(Value);
+ case DT_MIPS_TIME_STAMP:
+ case DT_MIPS_ICHECKSUM:
+ case DT_MIPS_IVERSION:
+ case DT_MIPS_BASE_ADDRESS:
+ case DT_MIPS_MSYM:
+ case DT_MIPS_CONFLICT:
+ case DT_MIPS_LIBLIST:
+ case DT_MIPS_CONFLICTNO:
+ case DT_MIPS_LIBLISTNO:
+ case DT_MIPS_GOTSYM:
+ case DT_MIPS_HIPAGENO:
+ case DT_MIPS_RLD_MAP:
+ case DT_MIPS_DELTA_CLASS:
+ case DT_MIPS_DELTA_CLASS_NO:
+ case DT_MIPS_DELTA_INSTANCE:
+ case DT_MIPS_DELTA_RELOC:
+ case DT_MIPS_DELTA_RELOC_NO:
+ case DT_MIPS_DELTA_SYM:
+ case DT_MIPS_DELTA_SYM_NO:
+ case DT_MIPS_DELTA_CLASSSYM:
+ case DT_MIPS_DELTA_CLASSSYM_NO:
+ case DT_MIPS_CXX_FLAGS:
+ case DT_MIPS_PIXIE_INIT:
+ case DT_MIPS_SYMBOL_LIB:
+ case DT_MIPS_LOCALPAGE_GOTIDX:
+ case DT_MIPS_LOCAL_GOTIDX:
+ case DT_MIPS_HIDDEN_GOTIDX:
+ case DT_MIPS_PROTECTED_GOTIDX:
+ case DT_MIPS_OPTIONS:
+ case DT_MIPS_INTERFACE:
+ case DT_MIPS_DYNSTR_ALIGN:
+ case DT_MIPS_INTERFACE_SIZE:
+ case DT_MIPS_RLD_TEXT_RESOLVE_ADDR:
+ case DT_MIPS_PERF_SUFFIX:
+ case DT_MIPS_COMPACT_SIZE:
+ case DT_MIPS_GP_VALUE:
+ case DT_MIPS_AUX_DYNAMIC:
+ case DT_MIPS_PLTGOT:
+ case DT_MIPS_RWPLT:
+ case DT_MIPS_RLD_MAP_REL:
+ case DT_MIPS_XHASH:
+ return FormatHexValue(Value);
+ case DT_MIPS_FLAGS:
+ return FormatFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags));
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (Type) {
+ case DT_PLTREL:
+ if (Value == DT_REL)
+ return "REL";
+ if (Value == DT_RELA)
+ return "RELA";
+ LLVM_FALLTHROUGH;
+ case DT_PLTGOT:
+ case DT_HASH:
+ case DT_STRTAB:
+ case DT_SYMTAB:
+ case DT_RELA:
+ case DT_INIT:
+ case DT_FINI:
+ case DT_REL:
+ case DT_JMPREL:
+ case DT_INIT_ARRAY:
+ case DT_FINI_ARRAY:
+ case DT_PREINIT_ARRAY:
+ case DT_DEBUG:
+ case DT_VERDEF:
+ case DT_VERNEED:
+ case DT_VERSYM:
+ case DT_GNU_HASH:
+ case DT_NULL:
+ return FormatHexValue(Value);
+ case DT_RELACOUNT:
+ case DT_RELCOUNT:
+ case DT_VERDEFNUM:
+ case DT_VERNEEDNUM:
+ return std::to_string(Value);
+ case DT_PLTRELSZ:
+ case DT_RELASZ:
+ case DT_RELAENT:
+ case DT_STRSZ:
+ case DT_SYMENT:
+ case DT_RELSZ:
+ case DT_RELENT:
+ case DT_INIT_ARRAYSZ:
+ case DT_FINI_ARRAYSZ:
+ case DT_PREINIT_ARRAYSZ:
+ case DT_RELRSZ:
+ case DT_RELRENT:
+ case DT_ANDROID_RELSZ:
+ case DT_ANDROID_RELASZ:
+ return std::to_string(Value) + " (bytes)";
+ case DT_NEEDED:
+ case DT_SONAME:
+ case DT_AUXILIARY:
+ case DT_USED:
+ case DT_FILTER:
+ case DT_RPATH:
+ case DT_RUNPATH: {
+ const std::map<uint64_t, const char *> TagNames = {
+ {DT_NEEDED, "Shared library"}, {DT_SONAME, "Library soname"},
+ {DT_AUXILIARY, "Auxiliary library"}, {DT_USED, "Not needed object"},
+ {DT_FILTER, "Filter library"}, {DT_RPATH, "Library rpath"},
+ {DT_RUNPATH, "Library runpath"},
+ };
+
+ return (Twine(TagNames.at(Type)) + ": [" + getDynamicString(Value) + "]")
+ .str();
+ }
+ case DT_FLAGS:
+ return FormatFlags(Value, makeArrayRef(ElfDynamicDTFlags));
+ case DT_FLAGS_1:
+ return FormatFlags(Value, makeArrayRef(ElfDynamicDTFlags1));
+ default:
+ return FormatHexValue(Value);
+ }
+}
+
+template <class ELFT>
+StringRef ELFDumper<ELFT>::getDynamicString(uint64_t Value) const {
+ if (DynamicStringTable.empty() && !DynamicStringTable.data()) {
+ reportUniqueWarning("string table was not found");
+ return "<?>";
+ }
+
+ auto WarnAndReturn = [this](const Twine &Msg, uint64_t Offset) {
+ reportUniqueWarning("string table at offset 0x" + Twine::utohexstr(Offset) +
+ Msg);
+ return "<?>";
+ };
+
+ const uint64_t FileSize = Obj.getBufSize();
+ const uint64_t Offset =
+ (const uint8_t *)DynamicStringTable.data() - Obj.base();
+ if (DynamicStringTable.size() > FileSize - Offset)
+ return WarnAndReturn(" with size 0x" +
+ Twine::utohexstr(DynamicStringTable.size()) +
+ " goes past the end of the file (0x" +
+ Twine::utohexstr(FileSize) + ")",
+ Offset);
+
+ if (Value >= DynamicStringTable.size())
+ return WarnAndReturn(
+ ": unable to read the string at 0x" + Twine::utohexstr(Offset + Value) +
+ ": it goes past the end of the table (0x" +
+ Twine::utohexstr(Offset + DynamicStringTable.size()) + ")",
+ Offset);
+
+ if (DynamicStringTable.back() != '\0')
+ return WarnAndReturn(": unable to read the string at 0x" +
+ Twine::utohexstr(Offset + Value) +
+ ": the string table is not null-terminated",
+ Offset);
+
+ return DynamicStringTable.data() + Value;
+}
+
+template <class ELFT> void ELFDumper<ELFT>::printUnwindInfo() {
+ DwarfCFIEH::PrinterContext<ELFT> Ctx(W, ObjF);
+ Ctx.printUnwindInformation();
+}
+
+// The namespace is needed to fix the compilation with GCC older than 7.0+.
+namespace {
+template <> void ELFDumper<ELF32LE>::printUnwindInfo() {
+ if (Obj.getHeader().e_machine == EM_ARM) {
+ ARM::EHABI::PrinterContext<ELF32LE> Ctx(W, Obj, ObjF.getFileName(),
+ DotSymtabSec);
+ Ctx.PrintUnwindInformation();
+ }
+ DwarfCFIEH::PrinterContext<ELF32LE> Ctx(W, ObjF);
+ Ctx.printUnwindInformation();
+}
+} // namespace
+
+template <class ELFT> void ELFDumper<ELFT>::printNeededLibraries() {
+ ListScope D(W, "NeededLibraries");
+
+ std::vector<StringRef> Libs;
+ for (const auto &Entry : dynamic_table())
+ if (Entry.d_tag == ELF::DT_NEEDED)
+ Libs.push_back(getDynamicString(Entry.d_un.d_val));
+
+ llvm::sort(Libs);
+
+ for (StringRef L : Libs)
+ W.startLine() << L << "\n";
+}
+
+template <class ELFT>
+static Error checkHashTable(const ELFDumper<ELFT> &Dumper,
+ const typename ELFT::Hash *H,
+ bool *IsHeaderValid = nullptr) {
+ const ELFFile<ELFT> &Obj = Dumper.getElfObject().getELFFile();
+ const uint64_t SecOffset = (const uint8_t *)H - Obj.base();
+ if (Dumper.getHashTableEntSize() == 8) {
+ auto It = llvm::find_if(ElfMachineType, [&](const EnumEntry<unsigned> &E) {
+ return E.Value == Obj.getHeader().e_machine;
+ });
+ if (IsHeaderValid)
+ *IsHeaderValid = false;
+ return createError("the hash table at 0x" + Twine::utohexstr(SecOffset) +
+ " is not supported: it contains non-standard 8 "
+ "byte entries on " +
+ It->AltName + " platform");
+ }
+
+ auto MakeError = [&](const Twine &Msg = "") {
+ return createError("the hash table at offset 0x" +
+ Twine::utohexstr(SecOffset) +
+ " goes past the end of the file (0x" +
+ Twine::utohexstr(Obj.getBufSize()) + ")" + Msg);
+ };
+
+ // Each SHT_HASH section starts from two 32-bit fields: nbucket and nchain.
+ const unsigned HeaderSize = 2 * sizeof(typename ELFT::Word);
+
+ if (IsHeaderValid)
+ *IsHeaderValid = Obj.getBufSize() - SecOffset >= HeaderSize;
+
+ if (Obj.getBufSize() - SecOffset < HeaderSize)
+ return MakeError();
+
+ if (Obj.getBufSize() - SecOffset - HeaderSize <
+ ((uint64_t)H->nbucket + H->nchain) * sizeof(typename ELFT::Word))
+ return MakeError(", nbucket = " + Twine(H->nbucket) +
+ ", nchain = " + Twine(H->nchain));
+ return Error::success();
+}
+
+template <class ELFT>
+static Error checkGNUHashTable(const ELFFile<ELFT> &Obj,
+ const typename ELFT::GnuHash *GnuHashTable,
+ bool *IsHeaderValid = nullptr) {
+ const uint8_t *TableData = reinterpret_cast<const uint8_t *>(GnuHashTable);
+ assert(TableData >= Obj.base() && TableData < Obj.base() + Obj.getBufSize() &&
+ "GnuHashTable must always point to a location inside the file");
+
+ uint64_t TableOffset = TableData - Obj.base();
+ if (IsHeaderValid)
+ *IsHeaderValid = TableOffset + /*Header size:*/ 16 < Obj.getBufSize();
+ if (TableOffset + 16 + (uint64_t)GnuHashTable->nbuckets * 4 +
+ (uint64_t)GnuHashTable->maskwords * sizeof(typename ELFT::Off) >=
+ Obj.getBufSize())
+ return createError("unable to dump the SHT_GNU_HASH "
+ "section at 0x" +
+ Twine::utohexstr(TableOffset) +
+ ": it goes past the end of the file");
+ return Error::success();
+}
+
+template <typename ELFT> void ELFDumper<ELFT>::printHashTable() {
+ DictScope D(W, "HashTable");
+ if (!HashTable)
+ return;
+
+ bool IsHeaderValid;
+ Error Err = checkHashTable(*this, HashTable, &IsHeaderValid);
+ if (IsHeaderValid) {
+ W.printNumber("Num Buckets", HashTable->nbucket);
+ W.printNumber("Num Chains", HashTable->nchain);
+ }
+
+ if (Err) {
+ reportUniqueWarning(std::move(Err));
+ return;
+ }
+
+ W.printList("Buckets", HashTable->buckets());
+ W.printList("Chains", HashTable->chains());
+}
+
+template <class ELFT>
+static Expected<ArrayRef<typename ELFT::Word>>
+getGnuHashTableChains(Optional<DynRegionInfo> DynSymRegion,
+ const typename ELFT::GnuHash *GnuHashTable) {
+ if (!DynSymRegion)
+ return createError("no dynamic symbol table found");
+
+ ArrayRef<typename ELFT::Sym> DynSymTable =
+ DynSymRegion->template getAsArrayRef<typename ELFT::Sym>();
+ size_t NumSyms = DynSymTable.size();
+ if (!NumSyms)
+ return createError("the dynamic symbol table is empty");
+
+ if (GnuHashTable->symndx < NumSyms)
+ return GnuHashTable->values(NumSyms);
+
+ // A normal empty GNU hash table section produced by linker might have
+ // symndx set to the number of dynamic symbols + 1 (for the zero symbol)
+ // and have dummy null values in the Bloom filter and in the buckets
+ // vector (or no values at all). It happens because the value of symndx is not
+ // important for dynamic loaders when the GNU hash table is empty. They just
+ // skip the whole object during symbol lookup. In such cases, the symndx value
+ // is irrelevant and we should not report a warning.
+ ArrayRef<typename ELFT::Word> Buckets = GnuHashTable->buckets();
+ if (!llvm::all_of(Buckets, [](typename ELFT::Word V) { return V == 0; }))
+ return createError(
+ "the first hashed symbol index (" + Twine(GnuHashTable->symndx) +
+ ") is greater than or equal to the number of dynamic symbols (" +
+ Twine(NumSyms) + ")");
+ // There is no way to represent an array of (dynamic symbols count - symndx)
+ // length.
+ return ArrayRef<typename ELFT::Word>();
+}
+
+template <typename ELFT>
+void ELFDumper<ELFT>::printGnuHashTable() {
+ DictScope D(W, "GnuHashTable");
+ if (!GnuHashTable)
+ return;
+
+ bool IsHeaderValid;
+ Error Err = checkGNUHashTable<ELFT>(Obj, GnuHashTable, &IsHeaderValid);
+ if (IsHeaderValid) {
+ W.printNumber("Num Buckets", GnuHashTable->nbuckets);
+ W.printNumber("First Hashed Symbol Index", GnuHashTable->symndx);
+ W.printNumber("Num Mask Words", GnuHashTable->maskwords);
+ W.printNumber("Shift Count", GnuHashTable->shift2);
+ }
+
+ if (Err) {
+ reportUniqueWarning(std::move(Err));
+ return;
+ }
+
+ ArrayRef<typename ELFT::Off> BloomFilter = GnuHashTable->filter();
+ W.printHexList("Bloom Filter", BloomFilter);
+
+ ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets();
+ W.printList("Buckets", Buckets);
+
+ Expected<ArrayRef<Elf_Word>> Chains =
+ getGnuHashTableChains<ELFT>(DynSymRegion, GnuHashTable);
+ if (!Chains) {
+ reportUniqueWarning("unable to dump 'Values' for the SHT_GNU_HASH "
+ "section: " +
+ toString(Chains.takeError()));
+ return;
+ }
+
+ W.printHexList("Values", *Chains);
+}
+
+template <typename ELFT> void ELFDumper<ELFT>::printLoadName() {
+ StringRef SOName = "<Not found>";
+ if (SONameOffset)
+ SOName = getDynamicString(*SONameOffset);
+ W.printString("LoadName", SOName);
+}
+
+template <class ELFT> void ELFDumper<ELFT>::printArchSpecificInfo() {
+ switch (Obj.getHeader().e_machine) {
+ case EM_ARM:
+ if (Obj.isLE())
+ printAttributes(ELF::SHT_ARM_ATTRIBUTES,
+ std::make_unique<ARMAttributeParser>(&W),
+ support::little);
+ else
+ reportUniqueWarning("attribute printing not implemented for big-endian "
+ "ARM objects");
+ break;
+ case EM_RISCV:
+ if (Obj.isLE())
+ printAttributes(ELF::SHT_RISCV_ATTRIBUTES,
+ std::make_unique<RISCVAttributeParser>(&W),
+ support::little);
+ else
+ reportUniqueWarning("attribute printing not implemented for big-endian "
+ "RISC-V objects");
+ break;
+ case EM_MSP430:
+ printAttributes(ELF::SHT_MSP430_ATTRIBUTES,
+ std::make_unique<MSP430AttributeParser>(&W),
+ support::little);
+ break;
+ case EM_MIPS: {
+ printMipsABIFlags();
+ printMipsOptions();
+ printMipsReginfo();
+ MipsGOTParser<ELFT> Parser(*this);
+ if (Error E = Parser.findGOT(dynamic_table(), dynamic_symbols()))
+ reportUniqueWarning(std::move(E));
+ else if (!Parser.isGotEmpty())
+ printMipsGOT(Parser);
+
+ if (Error E = Parser.findPLT(dynamic_table()))
+ reportUniqueWarning(std::move(E));
+ else if (!Parser.isPltEmpty())
+ printMipsPLT(Parser);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::printAttributes(
+ unsigned AttrShType, std::unique_ptr<ELFAttributeParser> AttrParser,
+ support::endianness Endianness) {
+ assert((AttrShType != ELF::SHT_NULL) && AttrParser &&
+ "Incomplete ELF attribute implementation");
+ DictScope BA(W, "BuildAttributes");
+ for (const Elf_Shdr &Sec : cantFail(Obj.sections())) {
+ if (Sec.sh_type != AttrShType)
+ continue;
+
+ ArrayRef<uint8_t> Contents;
+ if (Expected<ArrayRef<uint8_t>> ContentOrErr =
+ Obj.getSectionContents(Sec)) {
+ Contents = *ContentOrErr;
+ if (Contents.empty()) {
+ reportUniqueWarning("the " + describe(Sec) + " is empty");
+ continue;
+ }
+ } else {
+ reportUniqueWarning("unable to read the content of the " + describe(Sec) +
+ ": " + toString(ContentOrErr.takeError()));
+ continue;
+ }
+
+ W.printHex("FormatVersion", Contents[0]);
+
+ if (Error E = AttrParser->parse(Contents, Endianness))
+ reportUniqueWarning("unable to dump attributes from the " +
+ describe(Sec) + ": " + toString(std::move(E)));
+ }
+}
+
+namespace {
+
+template <class ELFT> class MipsGOTParser {
+public:
+ LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
+ using Entry = typename ELFT::Addr;
+ using Entries = ArrayRef<Entry>;
+
+ const bool IsStatic;
+ const ELFFile<ELFT> &Obj;
+ const ELFDumper<ELFT> &Dumper;
+
+ MipsGOTParser(const ELFDumper<ELFT> &D);
+ Error findGOT(Elf_Dyn_Range DynTable, Elf_Sym_Range DynSyms);
+ Error findPLT(Elf_Dyn_Range DynTable);
+
+ bool isGotEmpty() const { return GotEntries.empty(); }
+ bool isPltEmpty() const { return PltEntries.empty(); }
+
+ uint64_t getGp() const;
+
+ const Entry *getGotLazyResolver() const;
+ const Entry *getGotModulePointer() const;
+ const Entry *getPltLazyResolver() const;
+ const Entry *getPltModulePointer() const;
+
+ Entries getLocalEntries() const;
+ Entries getGlobalEntries() const;
+ Entries getOtherEntries() const;
+ Entries getPltEntries() const;
+
+ uint64_t getGotAddress(const Entry * E) const;
+ int64_t getGotOffset(const Entry * E) const;
+ const Elf_Sym *getGotSym(const Entry *E) const;
+
+ uint64_t getPltAddress(const Entry * E) const;
+ const Elf_Sym *getPltSym(const Entry *E) const;
+
+ StringRef getPltStrTable() const { return PltStrTable; }
+ const Elf_Shdr *getPltSymTable() const { return PltSymTable; }
+
+private:
+ const Elf_Shdr *GotSec;
+ size_t LocalNum;
+ size_t GlobalNum;
+
+ const Elf_Shdr *PltSec;
+ const Elf_Shdr *PltRelSec;
+ const Elf_Shdr *PltSymTable;
+ StringRef FileName;
+
+ Elf_Sym_Range GotDynSyms;
+ StringRef PltStrTable;
+
+ Entries GotEntries;
+ Entries PltEntries;
+};
+
+} // end anonymous namespace
+
+template <class ELFT>
+MipsGOTParser<ELFT>::MipsGOTParser(const ELFDumper<ELFT> &D)
+ : IsStatic(D.dynamic_table().empty()), Obj(D.getElfObject().getELFFile()),
+ Dumper(D), GotSec(nullptr), LocalNum(0), GlobalNum(0), PltSec(nullptr),
+ PltRelSec(nullptr), PltSymTable(nullptr),
+ FileName(D.getElfObject().getFileName()) {}
+
+template <class ELFT>
+Error MipsGOTParser<ELFT>::findGOT(Elf_Dyn_Range DynTable,
+ Elf_Sym_Range DynSyms) {
+ // See "Global Offset Table" in Chapter 5 in the following document
+ // for detailed GOT description.
+ // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
+
+ // Find static GOT secton.
+ if (IsStatic) {
+ GotSec = Dumper.findSectionByName(".got");
+ if (!GotSec)
+ return Error::success();
+
+ ArrayRef<uint8_t> Content =
+ unwrapOrError(FileName, Obj.getSectionContents(*GotSec));
+ GotEntries = Entries(reinterpret_cast<const Entry *>(Content.data()),
+ Content.size() / sizeof(Entry));
+ LocalNum = GotEntries.size();
+ return Error::success();
+ }
+
+ // Lookup dynamic table tags which define the GOT layout.
+ Optional<uint64_t> DtPltGot;
+ Optional<uint64_t> DtLocalGotNum;
+ Optional<uint64_t> DtGotSym;
+ for (const auto &Entry : DynTable) {
+ switch (Entry.getTag()) {
+ case ELF::DT_PLTGOT:
+ DtPltGot = Entry.getVal();
+ break;
+ case ELF::DT_MIPS_LOCAL_GOTNO:
+ DtLocalGotNum = Entry.getVal();
+ break;
+ case ELF::DT_MIPS_GOTSYM:
+ DtGotSym = Entry.getVal();
+ break;
+ }
+ }
+
+ if (!DtPltGot && !DtLocalGotNum && !DtGotSym)
+ return Error::success();
+
+ if (!DtPltGot)
+ return createError("cannot find PLTGOT dynamic tag");
+ if (!DtLocalGotNum)
+ return createError("cannot find MIPS_LOCAL_GOTNO dynamic tag");
+ if (!DtGotSym)
+ return createError("cannot find MIPS_GOTSYM dynamic tag");
+
+ size_t DynSymTotal = DynSyms.size();
+ if (*DtGotSym > DynSymTotal)
+ return createError("DT_MIPS_GOTSYM value (" + Twine(*DtGotSym) +
+ ") exceeds the number of dynamic symbols (" +
+ Twine(DynSymTotal) + ")");
+
+ GotSec = findNotEmptySectionByAddress(Obj, FileName, *DtPltGot);
+ if (!GotSec)
+ return createError("there is no non-empty GOT section at 0x" +
+ Twine::utohexstr(*DtPltGot));
+
+ LocalNum = *DtLocalGotNum;
+ GlobalNum = DynSymTotal - *DtGotSym;
+
+ ArrayRef<uint8_t> Content =
+ unwrapOrError(FileName, Obj.getSectionContents(*GotSec));
+ GotEntries = Entries(reinterpret_cast<const Entry *>(Content.data()),
+ Content.size() / sizeof(Entry));
+ GotDynSyms = DynSyms.drop_front(*DtGotSym);
+
+ return Error::success();
+}
+
+template <class ELFT>
+Error MipsGOTParser<ELFT>::findPLT(Elf_Dyn_Range DynTable) {
+ // Lookup dynamic table tags which define the PLT layout.
+ Optional<uint64_t> DtMipsPltGot;
+ Optional<uint64_t> DtJmpRel;
+ for (const auto &Entry : DynTable) {
+ switch (Entry.getTag()) {
+ case ELF::DT_MIPS_PLTGOT:
+ DtMipsPltGot = Entry.getVal();
+ break;
+ case ELF::DT_JMPREL:
+ DtJmpRel = Entry.getVal();
+ break;
+ }
+ }
+
+ if (!DtMipsPltGot && !DtJmpRel)
+ return Error::success();
+
+ // Find PLT section.
+ if (!DtMipsPltGot)
+ return createError("cannot find MIPS_PLTGOT dynamic tag");
+ if (!DtJmpRel)
+ return createError("cannot find JMPREL dynamic tag");
+
+ PltSec = findNotEmptySectionByAddress(Obj, FileName, *DtMipsPltGot);
+ if (!PltSec)
+ return createError("there is no non-empty PLTGOT section at 0x" +
+ Twine::utohexstr(*DtMipsPltGot));
+
+ PltRelSec = findNotEmptySectionByAddress(Obj, FileName, *DtJmpRel);
+ if (!PltRelSec)
+ return createError("there is no non-empty RELPLT section at 0x" +
+ Twine::utohexstr(*DtJmpRel));
+
+ if (Expected<ArrayRef<uint8_t>> PltContentOrErr =
+ Obj.getSectionContents(*PltSec))
+ PltEntries =
+ Entries(reinterpret_cast<const Entry *>(PltContentOrErr->data()),
+ PltContentOrErr->size() / sizeof(Entry));
+ else
+ return createError("unable to read PLTGOT section content: " +
+ toString(PltContentOrErr.takeError()));
+
+ if (Expected<const Elf_Shdr *> PltSymTableOrErr =
+ Obj.getSection(PltRelSec->sh_link))
+ PltSymTable = *PltSymTableOrErr;
+ else
+ return createError("unable to get a symbol table linked to the " +
+ describe(Obj, *PltRelSec) + ": " +
+ toString(PltSymTableOrErr.takeError()));
+
+ if (Expected<StringRef> StrTabOrErr =
+ Obj.getStringTableForSymtab(*PltSymTable))
+ PltStrTable = *StrTabOrErr;
+ else
+ return createError("unable to get a string table for the " +
+ describe(Obj, *PltSymTable) + ": " +
+ toString(StrTabOrErr.takeError()));
+
+ return Error::success();
+}
+
+template <class ELFT> uint64_t MipsGOTParser<ELFT>::getGp() const {
+ return GotSec->sh_addr + 0x7ff0;
+}
+
+template <class ELFT>
+const typename MipsGOTParser<ELFT>::Entry *
+MipsGOTParser<ELFT>::getGotLazyResolver() const {
+ return LocalNum > 0 ? &GotEntries[0] : nullptr;
+}
+
+template <class ELFT>
+const typename MipsGOTParser<ELFT>::Entry *
+MipsGOTParser<ELFT>::getGotModulePointer() const {
+ if (LocalNum < 2)
+ return nullptr;
+ const Entry &E = GotEntries[1];
+ if ((E >> (sizeof(Entry) * 8 - 1)) == 0)
+ return nullptr;
+ return &E;
+}
+
+template <class ELFT>
+typename MipsGOTParser<ELFT>::Entries
+MipsGOTParser<ELFT>::getLocalEntries() const {
+ size_t Skip = getGotModulePointer() ? 2 : 1;
+ if (LocalNum - Skip <= 0)
+ return Entries();
+ return GotEntries.slice(Skip, LocalNum - Skip);
+}
+
+template <class ELFT>
+typename MipsGOTParser<ELFT>::Entries
+MipsGOTParser<ELFT>::getGlobalEntries() const {
+ if (GlobalNum == 0)
+ return Entries();
+ return GotEntries.slice(LocalNum, GlobalNum);
+}
+
+template <class ELFT>
+typename MipsGOTParser<ELFT>::Entries
+MipsGOTParser<ELFT>::getOtherEntries() const {
+ size_t OtherNum = GotEntries.size() - LocalNum - GlobalNum;
+ if (OtherNum == 0)
+ return Entries();
+ return GotEntries.slice(LocalNum + GlobalNum, OtherNum);
+}
+
+template <class ELFT>
+uint64_t MipsGOTParser<ELFT>::getGotAddress(const Entry *E) const {
+ int64_t Offset = std::distance(GotEntries.data(), E) * sizeof(Entry);
+ return GotSec->sh_addr + Offset;
+}
+
+template <class ELFT>
+int64_t MipsGOTParser<ELFT>::getGotOffset(const Entry *E) const {
+ int64_t Offset = std::distance(GotEntries.data(), E) * sizeof(Entry);
+ return Offset - 0x7ff0;
+}
+
+template <class ELFT>
+const typename MipsGOTParser<ELFT>::Elf_Sym *
+MipsGOTParser<ELFT>::getGotSym(const Entry *E) const {
+ int64_t Offset = std::distance(GotEntries.data(), E);
+ return &GotDynSyms[Offset - LocalNum];
+}
+
+template <class ELFT>
+const typename MipsGOTParser<ELFT>::Entry *
+MipsGOTParser<ELFT>::getPltLazyResolver() const {
+ return PltEntries.empty() ? nullptr : &PltEntries[0];
+}
+
+template <class ELFT>
+const typename MipsGOTParser<ELFT>::Entry *
+MipsGOTParser<ELFT>::getPltModulePointer() const {
+ return PltEntries.size() < 2 ? nullptr : &PltEntries[1];
+}
+
+template <class ELFT>
+typename MipsGOTParser<ELFT>::Entries
+MipsGOTParser<ELFT>::getPltEntries() const {
+ if (PltEntries.size() <= 2)
+ return Entries();
+ return PltEntries.slice(2, PltEntries.size() - 2);
+}
+
+template <class ELFT>
+uint64_t MipsGOTParser<ELFT>::getPltAddress(const Entry *E) const {
+ int64_t Offset = std::distance(PltEntries.data(), E) * sizeof(Entry);
+ return PltSec->sh_addr + Offset;
+}
+
+template <class ELFT>
+const typename MipsGOTParser<ELFT>::Elf_Sym *
+MipsGOTParser<ELFT>::getPltSym(const Entry *E) const {
+ int64_t Offset = std::distance(getPltEntries().data(), E);
+ if (PltRelSec->sh_type == ELF::SHT_REL) {
+ Elf_Rel_Range Rels = unwrapOrError(FileName, Obj.rels(*PltRelSec));
+ return unwrapOrError(FileName,
+ Obj.getRelocationSymbol(Rels[Offset], PltSymTable));
+ } else {
+ Elf_Rela_Range Rels = unwrapOrError(FileName, Obj.relas(*PltRelSec));
+ return unwrapOrError(FileName,
+ Obj.getRelocationSymbol(Rels[Offset], PltSymTable));
+ }
+}
+
+const EnumEntry<unsigned> ElfMipsISAExtType[] = {
+ {"None", Mips::AFL_EXT_NONE},
+ {"Broadcom SB-1", Mips::AFL_EXT_SB1},
+ {"Cavium Networks Octeon", Mips::AFL_EXT_OCTEON},
+ {"Cavium Networks Octeon2", Mips::AFL_EXT_OCTEON2},
+ {"Cavium Networks OcteonP", Mips::AFL_EXT_OCTEONP},
+ {"Cavium Networks Octeon3", Mips::AFL_EXT_OCTEON3},
+ {"LSI R4010", Mips::AFL_EXT_4010},
+ {"Loongson 2E", Mips::AFL_EXT_LOONGSON_2E},
+ {"Loongson 2F", Mips::AFL_EXT_LOONGSON_2F},
+ {"Loongson 3A", Mips::AFL_EXT_LOONGSON_3A},
+ {"MIPS R4650", Mips::AFL_EXT_4650},
+ {"MIPS R5900", Mips::AFL_EXT_5900},
+ {"MIPS R10000", Mips::AFL_EXT_10000},
+ {"NEC VR4100", Mips::AFL_EXT_4100},
+ {"NEC VR4111/VR4181", Mips::AFL_EXT_4111},
+ {"NEC VR4120", Mips::AFL_EXT_4120},
+ {"NEC VR5400", Mips::AFL_EXT_5400},
+ {"NEC VR5500", Mips::AFL_EXT_5500},
+ {"RMI Xlr", Mips::AFL_EXT_XLR},
+ {"Toshiba R3900", Mips::AFL_EXT_3900}
+};
+
+const EnumEntry<unsigned> ElfMipsASEFlags[] = {
+ {"DSP", Mips::AFL_ASE_DSP},
+ {"DSPR2", Mips::AFL_ASE_DSPR2},
+ {"Enhanced VA Scheme", Mips::AFL_ASE_EVA},
+ {"MCU", Mips::AFL_ASE_MCU},
+ {"MDMX", Mips::AFL_ASE_MDMX},
+ {"MIPS-3D", Mips::AFL_ASE_MIPS3D},
+ {"MT", Mips::AFL_ASE_MT},
+ {"SmartMIPS", Mips::AFL_ASE_SMARTMIPS},
+ {"VZ", Mips::AFL_ASE_VIRT},
+ {"MSA", Mips::AFL_ASE_MSA},
+ {"MIPS16", Mips::AFL_ASE_MIPS16},
+ {"microMIPS", Mips::AFL_ASE_MICROMIPS},
+ {"XPA", Mips::AFL_ASE_XPA},
+ {"CRC", Mips::AFL_ASE_CRC},
+ {"GINV", Mips::AFL_ASE_GINV},
+};
+
+const EnumEntry<unsigned> ElfMipsFpABIType[] = {
+ {"Hard or soft float", Mips::Val_GNU_MIPS_ABI_FP_ANY},
+ {"Hard float (double precision)", Mips::Val_GNU_MIPS_ABI_FP_DOUBLE},
+ {"Hard float (single precision)", Mips::Val_GNU_MIPS_ABI_FP_SINGLE},
+ {"Soft float", Mips::Val_GNU_MIPS_ABI_FP_SOFT},
+ {"Hard float (MIPS32r2 64-bit FPU 12 callee-saved)",
+ Mips::Val_GNU_MIPS_ABI_FP_OLD_64},
+ {"Hard float (32-bit CPU, Any FPU)", Mips::Val_GNU_MIPS_ABI_FP_XX},
+ {"Hard float (32-bit CPU, 64-bit FPU)", Mips::Val_GNU_MIPS_ABI_FP_64},
+ {"Hard float compat (32-bit CPU, 64-bit FPU)",
+ Mips::Val_GNU_MIPS_ABI_FP_64A}
+};
+
+static const EnumEntry<unsigned> ElfMipsFlags1[] {
+ {"ODDSPREG", Mips::AFL_FLAGS1_ODDSPREG},
+};
+
+static int getMipsRegisterSize(uint8_t Flag) {
+ switch (Flag) {
+ case Mips::AFL_REG_NONE:
+ return 0;
+ case Mips::AFL_REG_32:
+ return 32;
+ case Mips::AFL_REG_64:
+ return 64;
+ case Mips::AFL_REG_128:
+ return 128;
+ default:
+ return -1;
+ }
+}
+
+template <class ELFT>
+static void printMipsReginfoData(ScopedPrinter &W,
+ const Elf_Mips_RegInfo<ELFT> &Reginfo) {
+ W.printHex("GP", Reginfo.ri_gp_value);
+ W.printHex("General Mask", Reginfo.ri_gprmask);
+ W.printHex("Co-Proc Mask0", Reginfo.ri_cprmask[0]);
+ W.printHex("Co-Proc Mask1", Reginfo.ri_cprmask[1]);
+ W.printHex("Co-Proc Mask2", Reginfo.ri_cprmask[2]);
+ W.printHex("Co-Proc Mask3", Reginfo.ri_cprmask[3]);
+}
+
+template <class ELFT> void ELFDumper<ELFT>::printMipsReginfo() {
+ const Elf_Shdr *RegInfoSec = findSectionByName(".reginfo");
+ if (!RegInfoSec) {
+ W.startLine() << "There is no .reginfo section in the file.\n";
+ return;
+ }
+
+ Expected<ArrayRef<uint8_t>> ContentsOrErr =
+ Obj.getSectionContents(*RegInfoSec);
+ if (!ContentsOrErr) {
+ this->reportUniqueWarning(
+ "unable to read the content of the .reginfo section (" +
+ describe(*RegInfoSec) + "): " + toString(ContentsOrErr.takeError()));
+ return;
+ }
+
+ if (ContentsOrErr->size() < sizeof(Elf_Mips_RegInfo<ELFT>)) {
+ this->reportUniqueWarning("the .reginfo section has an invalid size (0x" +
+ Twine::utohexstr(ContentsOrErr->size()) + ")");
+ return;
+ }
+
+ DictScope GS(W, "MIPS RegInfo");
+ printMipsReginfoData(W, *reinterpret_cast<const Elf_Mips_RegInfo<ELFT> *>(
+ ContentsOrErr->data()));
+}
+
+template <class ELFT>
+static Expected<const Elf_Mips_Options<ELFT> *>
+readMipsOptions(const uint8_t *SecBegin, ArrayRef<uint8_t> &SecData,
+ bool &IsSupported) {
+ if (SecData.size() < sizeof(Elf_Mips_Options<ELFT>))
+ return createError("the .MIPS.options section has an invalid size (0x" +
+ Twine::utohexstr(SecData.size()) + ")");
+
+ const Elf_Mips_Options<ELFT> *O =
+ reinterpret_cast<const Elf_Mips_Options<ELFT> *>(SecData.data());
+ const uint8_t Size = O->size;
+ if (Size > SecData.size()) {
+ const uint64_t Offset = SecData.data() - SecBegin;
+ const uint64_t SecSize = Offset + SecData.size();
+ return createError("a descriptor of size 0x" + Twine::utohexstr(Size) +
+ " at offset 0x" + Twine::utohexstr(Offset) +
+ " goes past the end of the .MIPS.options "
+ "section of size 0x" +
+ Twine::utohexstr(SecSize));
+ }
+
+ IsSupported = O->kind == ODK_REGINFO;
+ const size_t ExpectedSize =
+ sizeof(Elf_Mips_Options<ELFT>) + sizeof(Elf_Mips_RegInfo<ELFT>);
+
+ if (IsSupported)
+ if (Size < ExpectedSize)
+ return createError(
+ "a .MIPS.options entry of kind " +
+ Twine(getElfMipsOptionsOdkType(O->kind)) +
+ " has an invalid size (0x" + Twine::utohexstr(Size) +
+ "), the expected size is 0x" + Twine::utohexstr(ExpectedSize));
+
+ SecData = SecData.drop_front(Size);
+ return O;
+}
+
+template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() {
+ const Elf_Shdr *MipsOpts = findSectionByName(".MIPS.options");
+ if (!MipsOpts) {
+ W.startLine() << "There is no .MIPS.options section in the file.\n";
+ return;
+ }
+
+ DictScope GS(W, "MIPS Options");
+
+ ArrayRef<uint8_t> Data =
+ unwrapOrError(ObjF.getFileName(), Obj.getSectionContents(*MipsOpts));
+ const uint8_t *const SecBegin = Data.begin();
+ while (!Data.empty()) {
+ bool IsSupported;
+ Expected<const Elf_Mips_Options<ELFT> *> OptsOrErr =
+ readMipsOptions<ELFT>(SecBegin, Data, IsSupported);
+ if (!OptsOrErr) {
+ reportUniqueWarning(OptsOrErr.takeError());
+ break;
+ }
+
+ unsigned Kind = (*OptsOrErr)->kind;
+ const char *Type = getElfMipsOptionsOdkType(Kind);
+ if (!IsSupported) {
+ W.startLine() << "Unsupported MIPS options tag: " << Type << " (" << Kind
+ << ")\n";
+ continue;
+ }
+
+ DictScope GS(W, Type);
+ if (Kind == ODK_REGINFO)
+ printMipsReginfoData(W, (*OptsOrErr)->getRegInfo());
+ else
+ llvm_unreachable("unexpected .MIPS.options section descriptor kind");
+ }
+}
+
+template <class ELFT> void ELFDumper<ELFT>::printStackMap() const {
+ const Elf_Shdr *StackMapSection = findSectionByName(".llvm_stackmaps");
+ if (!StackMapSection)
+ return;
+
+ auto Warn = [&](Error &&E) {
+ this->reportUniqueWarning("unable to read the stack map from " +
+ describe(*StackMapSection) + ": " +
+ toString(std::move(E)));
+ };
+
+ Expected<ArrayRef<uint8_t>> ContentOrErr =
+ Obj.getSectionContents(*StackMapSection);
+ if (!ContentOrErr) {
+ Warn(ContentOrErr.takeError());
+ return;
+ }
+
+ if (Error E = StackMapParser<ELFT::TargetEndianness>::validateHeader(
+ *ContentOrErr)) {
+ Warn(std::move(E));
+ return;
+ }
+
+ prettyPrintStackMap(W, StackMapParser<ELFT::TargetEndianness>(*ContentOrErr));
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::printReloc(const Relocation<ELFT> &R, unsigned RelIndex,
+ const Elf_Shdr &Sec, const Elf_Shdr *SymTab) {
+ Expected<RelSymbol<ELFT>> Target = getRelocationTarget(R, SymTab);
+ if (!Target)
+ reportUniqueWarning("unable to print relocation " + Twine(RelIndex) +
+ " in " + describe(Sec) + ": " +
+ toString(Target.takeError()));
+ else
+ printRelRelaReloc(R, *Target);
+}
+
+static inline void printFields(formatted_raw_ostream &OS, StringRef Str1,
+ StringRef Str2) {
+ OS.PadToColumn(2u);
+ OS << Str1;
+ OS.PadToColumn(37u);
+ OS << Str2 << "\n";
+ OS.flush();
+}
+
+template <class ELFT>
+static std::string getSectionHeadersNumString(const ELFFile<ELFT> &Obj,
+ StringRef FileName) {
+ const typename ELFT::Ehdr &ElfHeader = Obj.getHeader();
+ if (ElfHeader.e_shnum != 0)
+ return to_string(ElfHeader.e_shnum);
+
+ Expected<ArrayRef<typename ELFT::Shdr>> ArrOrErr = Obj.sections();
+ if (!ArrOrErr) {
+ // In this case we can ignore an error, because we have already reported a
+ // warning about the broken section header table earlier.
+ consumeError(ArrOrErr.takeError());
+ return "<?>";
+ }
+
+ if (ArrOrErr->empty())
+ return "0";
+ return "0 (" + to_string((*ArrOrErr)[0].sh_size) + ")";
+}
+
+template <class ELFT>
+static std::string getSectionHeaderTableIndexString(const ELFFile<ELFT> &Obj,
+ StringRef FileName) {
+ const typename ELFT::Ehdr &ElfHeader = Obj.getHeader();
+ if (ElfHeader.e_shstrndx != SHN_XINDEX)
+ return to_string(ElfHeader.e_shstrndx);
+
+ Expected<ArrayRef<typename ELFT::Shdr>> ArrOrErr = Obj.sections();
+ if (!ArrOrErr) {
+ // In this case we can ignore an error, because we have already reported a
+ // warning about the broken section header table earlier.
+ consumeError(ArrOrErr.takeError());
+ return "<?>";
+ }
+
+ if (ArrOrErr->empty())
+ return "65535 (corrupt: out of range)";
+ return to_string(ElfHeader.e_shstrndx) + " (" +
+ to_string((*ArrOrErr)[0].sh_link) + ")";
+}
+
+static const EnumEntry<unsigned> *getObjectFileEnumEntry(unsigned Type) {
+ auto It = llvm::find_if(ElfObjectFileType, [&](const EnumEntry<unsigned> &E) {
+ return E.Value == Type;
+ });
+ if (It != makeArrayRef(ElfObjectFileType).end())
+ return It;
+ return nullptr;
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printFileSummary(StringRef FileStr, ObjectFile &Obj,
+ ArrayRef<std::string> InputFilenames,
+ const Archive *A) {
+ if (InputFilenames.size() > 1 || A) {
+ this->W.startLine() << "\n";
+ this->W.printString("File", FileStr);
+ }
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printFileHeaders() {
+ const Elf_Ehdr &e = this->Obj.getHeader();
+ OS << "ELF Header:\n";
+ OS << " Magic: ";
+ std::string Str;
+ for (int i = 0; i < ELF::EI_NIDENT; i++)
+ OS << format(" %02x", static_cast<int>(e.e_ident[i]));
+ OS << "\n";
+ Str = enumToString(e.e_ident[ELF::EI_CLASS], makeArrayRef(ElfClass));
+ printFields(OS, "Class:", Str);
+ Str = enumToString(e.e_ident[ELF::EI_DATA], makeArrayRef(ElfDataEncoding));
+ printFields(OS, "Data:", Str);
+ OS.PadToColumn(2u);
+ OS << "Version:";
+ OS.PadToColumn(37u);
+ OS << to_hexString(e.e_ident[ELF::EI_VERSION]);
+ if (e.e_version == ELF::EV_CURRENT)
+ OS << " (current)";
+ OS << "\n";
+ Str = enumToString(e.e_ident[ELF::EI_OSABI], makeArrayRef(ElfOSABI));
+ printFields(OS, "OS/ABI:", Str);
+ printFields(OS,
+ "ABI Version:", std::to_string(e.e_ident[ELF::EI_ABIVERSION]));
+
+ if (const EnumEntry<unsigned> *E = getObjectFileEnumEntry(e.e_type)) {
+ Str = E->AltName.str();
+ } else {
+ if (e.e_type >= ET_LOPROC)
+ Str = "Processor Specific: (" + to_hexString(e.e_type, false) + ")";
+ else if (e.e_type >= ET_LOOS)
+ Str = "OS Specific: (" + to_hexString(e.e_type, false) + ")";
+ else
+ Str = "<unknown>: " + to_hexString(e.e_type, false);
+ }
+ printFields(OS, "Type:", Str);
+
+ Str = enumToString(e.e_machine, makeArrayRef(ElfMachineType));
+ printFields(OS, "Machine:", Str);
+ Str = "0x" + to_hexString(e.e_version);
+ printFields(OS, "Version:", Str);
+ Str = "0x" + to_hexString(e.e_entry);
+ printFields(OS, "Entry point address:", Str);
+ Str = to_string(e.e_phoff) + " (bytes into file)";
+ 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));
+ else if (e.e_machine == EM_AVR)
+ ElfFlags = printFlags(e.e_flags, makeArrayRef(ElfHeaderAVRFlags),
+ unsigned(ELF::EF_AVR_ARCH_MASK));
+ 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);
+ Str = to_string(e.e_phentsize) + " (bytes)";
+ printFields(OS, "Size of program headers:", Str);
+ Str = to_string(e.e_phnum);
+ printFields(OS, "Number of program headers:", Str);
+ Str = to_string(e.e_shentsize) + " (bytes)";
+ printFields(OS, "Size of section headers:", Str);
+ Str = getSectionHeadersNumString(this->Obj, this->FileName);
+ printFields(OS, "Number of section headers:", Str);
+ Str = getSectionHeaderTableIndexString(this->Obj, this->FileName);
+ printFields(OS, "Section header string table index:", Str);
+}
+
+template <class ELFT> std::vector<GroupSection> ELFDumper<ELFT>::getGroups() {
+ auto GetSignature = [&](const Elf_Sym &Sym, unsigned SymNdx,
+ const Elf_Shdr &Symtab) -> StringRef {
+ Expected<StringRef> StrTableOrErr = Obj.getStringTableForSymtab(Symtab);
+ if (!StrTableOrErr) {
+ reportUniqueWarning("unable to get the string table for " +
+ describe(Symtab) + ": " +
+ toString(StrTableOrErr.takeError()));
+ return "<?>";
+ }
+
+ StringRef Strings = *StrTableOrErr;
+ if (Sym.st_name >= Strings.size()) {
+ reportUniqueWarning("unable to get the name of the symbol with index " +
+ Twine(SymNdx) + ": st_name (0x" +
+ Twine::utohexstr(Sym.st_name) +
+ ") is past the end of the string table of size 0x" +
+ Twine::utohexstr(Strings.size()));
+ return "<?>";
+ }
+
+ return StrTableOrErr->data() + Sym.st_name;
+ };
+
+ std::vector<GroupSection> Ret;
+ uint64_t I = 0;
+ for (const Elf_Shdr &Sec : cantFail(Obj.sections())) {
+ ++I;
+ if (Sec.sh_type != ELF::SHT_GROUP)
+ continue;
+
+ StringRef Signature = "<?>";
+ if (Expected<const Elf_Shdr *> SymtabOrErr = Obj.getSection(Sec.sh_link)) {
+ if (Expected<const Elf_Sym *> SymOrErr =
+ Obj.template getEntry<Elf_Sym>(**SymtabOrErr, Sec.sh_info))
+ Signature = GetSignature(**SymOrErr, Sec.sh_info, **SymtabOrErr);
+ else
+ reportUniqueWarning("unable to get the signature symbol for " +
+ describe(Sec) + ": " +
+ toString(SymOrErr.takeError()));
+ } else {
+ reportUniqueWarning("unable to get the symbol table for " +
+ describe(Sec) + ": " +
+ toString(SymtabOrErr.takeError()));
+ }
+
+ ArrayRef<Elf_Word> Data;
+ if (Expected<ArrayRef<Elf_Word>> ContentsOrErr =
+ Obj.template getSectionContentsAsArray<Elf_Word>(Sec)) {
+ if (ContentsOrErr->empty())
+ reportUniqueWarning("unable to read the section group flag from the " +
+ describe(Sec) + ": the section is empty");
+ else
+ Data = *ContentsOrErr;
+ } else {
+ reportUniqueWarning("unable to get the content of the " + describe(Sec) +
+ ": " + toString(ContentsOrErr.takeError()));
+ }
+
+ Ret.push_back({getPrintableSectionName(Sec),
+ maybeDemangle(Signature),
+ Sec.sh_name,
+ I - 1,
+ Sec.sh_link,
+ Sec.sh_info,
+ Data.empty() ? Elf_Word(0) : Data[0],
+ {}});
+
+ if (Data.empty())
+ continue;
+
+ std::vector<GroupMember> &GM = Ret.back().Members;
+ for (uint32_t Ndx : Data.slice(1)) {
+ if (Expected<const Elf_Shdr *> SecOrErr = Obj.getSection(Ndx)) {
+ GM.push_back({getPrintableSectionName(**SecOrErr), Ndx});
+ } else {
+ reportUniqueWarning("unable to get the section with index " +
+ Twine(Ndx) + " when dumping the " + describe(Sec) +
+ ": " + toString(SecOrErr.takeError()));
+ GM.push_back({"<?>", Ndx});
+ }
+ }
+ }
+ return Ret;
+}
+
+static DenseMap<uint64_t, const GroupSection *>
+mapSectionsToGroups(ArrayRef<GroupSection> Groups) {
+ DenseMap<uint64_t, const GroupSection *> Ret;
+ for (const GroupSection &G : Groups)
+ for (const GroupMember &GM : G.Members)
+ Ret.insert({GM.Index, &G});
+ return Ret;
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printGroupSections() {
+ std::vector<GroupSection> V = this->getGroups();
+ DenseMap<uint64_t, const GroupSection *> Map = mapSectionsToGroups(V);
+ for (const GroupSection &G : V) {
+ OS << "\n"
+ << getGroupType(G.Type) << " group section ["
+ << format_decimal(G.Index, 5) << "] `" << G.Name << "' [" << G.Signature
+ << "] contains " << G.Members.size() << " sections:\n"
+ << " [Index] Name\n";
+ for (const GroupMember &GM : G.Members) {
+ const GroupSection *MainGroup = Map[GM.Index];
+ if (MainGroup != &G)
+ this->reportUniqueWarning(
+ "section with index " + Twine(GM.Index) +
+ ", included in the group section with index " +
+ Twine(MainGroup->Index) +
+ ", was also found in the group section with index " +
+ Twine(G.Index));
+ OS << " [" << format_decimal(GM.Index, 5) << "] " << GM.Name << "\n";
+ }
+ }
+
+ if (V.empty())
+ OS << "There are no section groups in this file.\n";
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printRelrReloc(const Elf_Relr &R) {
+ OS << to_string(format_hex_no_prefix(R, ELFT::Is64Bits ? 16 : 8)) << "\n";
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printRelRelaReloc(const Relocation<ELFT> &R,
+ const RelSymbol<ELFT> &RelSym) {
+ // First two fields are bit width dependent. The rest of them are fixed width.
+ unsigned Bias = ELFT::Is64Bits ? 8 : 0;
+ Field Fields[5] = {0, 10 + Bias, 19 + 2 * Bias, 42 + 2 * Bias, 53 + 2 * Bias};
+ unsigned Width = ELFT::Is64Bits ? 16 : 8;
+
+ Fields[0].Str = to_string(format_hex_no_prefix(R.Offset, Width));
+ Fields[1].Str = to_string(format_hex_no_prefix(R.Info, Width));
+
+ SmallString<32> RelocName;
+ this->Obj.getRelocationTypeName(R.Type, RelocName);
+ Fields[2].Str = RelocName.c_str();
+
+ if (RelSym.Sym)
+ Fields[3].Str =
+ to_string(format_hex_no_prefix(RelSym.Sym->getValue(), Width));
+
+ Fields[4].Str = std::string(RelSym.Name);
+ for (const Field &F : Fields)
+ printField(F);
+
+ std::string Addend;
+ if (Optional<int64_t> A = R.Addend) {
+ int64_t RelAddend = *A;
+ if (!RelSym.Name.empty()) {
+ if (RelAddend < 0) {
+ Addend = " - ";
+ RelAddend = std::abs(RelAddend);
+ } else {
+ Addend = " + ";
+ }
+ }
+ Addend += to_hexString(RelAddend, false);
+ }
+ OS << Addend << "\n";
+}
+
+template <class ELFT>
+static void printRelocHeaderFields(formatted_raw_ostream &OS, unsigned SType) {
+ bool IsRela = SType == ELF::SHT_RELA || SType == ELF::SHT_ANDROID_RELA;
+ bool IsRelr = SType == ELF::SHT_RELR || SType == ELF::SHT_ANDROID_RELR;
+ if (ELFT::Is64Bits)
+ OS << " ";
+ else
+ OS << " ";
+ if (IsRelr && opts::RawRelr)
+ OS << "Data ";
+ else
+ OS << "Offset";
+ if (ELFT::Is64Bits)
+ OS << " Info Type"
+ << " Symbol's Value Symbol's Name";
+ else
+ OS << " Info Type Sym. Value Symbol's Name";
+ if (IsRela)
+ OS << " + Addend";
+ OS << "\n";
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printDynamicRelocHeader(unsigned Type, StringRef Name,
+ const DynRegionInfo &Reg) {
+ uint64_t Offset = Reg.Addr - this->Obj.base();
+ OS << "\n'" << Name.str().c_str() << "' relocation section at offset 0x"
+ << to_hexString(Offset, false) << " contains " << Reg.Size << " bytes:\n";
+ printRelocHeaderFields<ELFT>(OS, Type);
+}
+
+template <class ELFT>
+static bool isRelocationSec(const typename ELFT::Shdr &Sec) {
+ return Sec.sh_type == ELF::SHT_REL || Sec.sh_type == ELF::SHT_RELA ||
+ Sec.sh_type == ELF::SHT_RELR || Sec.sh_type == ELF::SHT_ANDROID_REL ||
+ Sec.sh_type == ELF::SHT_ANDROID_RELA ||
+ Sec.sh_type == ELF::SHT_ANDROID_RELR;
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printRelocations() {
+ auto GetEntriesNum = [&](const Elf_Shdr &Sec) -> Expected<size_t> {
+ // Android's packed relocation section needs to be unpacked first
+ // to get the actual number of entries.
+ if (Sec.sh_type == ELF::SHT_ANDROID_REL ||
+ Sec.sh_type == ELF::SHT_ANDROID_RELA) {
+ Expected<std::vector<typename ELFT::Rela>> RelasOrErr =
+ this->Obj.android_relas(Sec);
+ if (!RelasOrErr)
+ return RelasOrErr.takeError();
+ return RelasOrErr->size();
+ }
+
+ if (!opts::RawRelr && (Sec.sh_type == ELF::SHT_RELR ||
+ Sec.sh_type == ELF::SHT_ANDROID_RELR)) {
+ Expected<Elf_Relr_Range> RelrsOrErr = this->Obj.relrs(Sec);
+ if (!RelrsOrErr)
+ return RelrsOrErr.takeError();
+ return this->Obj.decode_relrs(*RelrsOrErr).size();
+ }
+
+ return Sec.getEntityCount();
+ };
+
+ bool HasRelocSections = false;
+ for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) {
+ if (!isRelocationSec<ELFT>(Sec))
+ continue;
+ HasRelocSections = true;
+
+ std::string EntriesNum = "<?>";
+ if (Expected<size_t> NumOrErr = GetEntriesNum(Sec))
+ EntriesNum = std::to_string(*NumOrErr);
+ else
+ this->reportUniqueWarning("unable to get the number of relocations in " +
+ this->describe(Sec) + ": " +
+ toString(NumOrErr.takeError()));
+
+ uintX_t Offset = Sec.sh_offset;
+ StringRef Name = this->getPrintableSectionName(Sec);
+ OS << "\nRelocation section '" << Name << "' at offset 0x"
+ << to_hexString(Offset, false) << " contains " << EntriesNum
+ << " entries:\n";
+ printRelocHeaderFields<ELFT>(OS, Sec.sh_type);
+ this->printRelocationsHelper(Sec);
+ }
+ if (!HasRelocSections)
+ OS << "\nThere are no relocations in this file.\n";
+}
+
+// Print the offset of a particular section from anyone of the ranges:
+// [SHT_LOOS, SHT_HIOS], [SHT_LOPROC, SHT_HIPROC], [SHT_LOUSER, SHT_HIUSER].
+// If 'Type' does not fall within any of those ranges, then a string is
+// returned as '<unknown>' followed by the type value.
+static std::string getSectionTypeOffsetString(unsigned Type) {
+ if (Type >= SHT_LOOS && Type <= SHT_HIOS)
+ return "LOOS+0x" + to_hexString(Type - SHT_LOOS);
+ else if (Type >= SHT_LOPROC && Type <= SHT_HIPROC)
+ return "LOPROC+0x" + to_hexString(Type - SHT_LOPROC);
+ else if (Type >= SHT_LOUSER && Type <= SHT_HIUSER)
+ return "LOUSER+0x" + to_hexString(Type - SHT_LOUSER);
+ return "0x" + to_hexString(Type) + ": <unknown>";
+}
+
+static std::string getSectionTypeString(unsigned Machine, unsigned Type) {
+ StringRef Name = getELFSectionTypeName(Machine, Type);
+
+ // Handle SHT_GNU_* type names.
+ if (Name.startswith("SHT_GNU_")) {
+ if (Name == "SHT_GNU_HASH")
+ return "GNU_HASH";
+ // E.g. SHT_GNU_verneed -> VERNEED.
+ return Name.drop_front(8).upper();
+ }
+
+ if (Name == "SHT_SYMTAB_SHNDX")
+ return "SYMTAB SECTION INDICES";
+
+ if (Name.startswith("SHT_"))
+ return Name.drop_front(4).str();
+ return getSectionTypeOffsetString(Type);
+}
+
+static void printSectionDescription(formatted_raw_ostream &OS,
+ unsigned EMachine) {
+ OS << "Key to Flags:\n";
+ OS << " W (write), A (alloc), X (execute), M (merge), S (strings), I "
+ "(info),\n";
+ OS << " L (link order), O (extra OS processing required), G (group), T "
+ "(TLS),\n";
+ OS << " C (compressed), x (unknown), o (OS specific), E (exclude),\n";
+ OS << " R (retain)";
+
+ if (EMachine == EM_X86_64)
+ OS << ", l (large)";
+ else if (EMachine == EM_ARM)
+ OS << ", y (purecode)";
+
+ OS << ", p (processor specific)\n";
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printSectionHeaders() {
+ unsigned Bias = ELFT::Is64Bits ? 0 : 8;
+ ArrayRef<Elf_Shdr> Sections = cantFail(this->Obj.sections());
+ OS << "There are " << to_string(Sections.size())
+ << " section headers, starting at offset "
+ << "0x" << to_hexString(this->Obj.getHeader().e_shoff, false) << ":\n\n";
+ OS << "Section Headers:\n";
+ Field Fields[11] = {
+ {"[Nr]", 2}, {"Name", 7}, {"Type", 25},
+ {"Address", 41}, {"Off", 58 - Bias}, {"Size", 65 - Bias},
+ {"ES", 72 - Bias}, {"Flg", 75 - Bias}, {"Lk", 79 - Bias},
+ {"Inf", 82 - Bias}, {"Al", 86 - Bias}};
+ for (const Field &F : Fields)
+ printField(F);
+ OS << "\n";
+
+ StringRef SecStrTable;
+ if (Expected<StringRef> SecStrTableOrErr =
+ this->Obj.getSectionStringTable(Sections, this->WarningHandler))
+ SecStrTable = *SecStrTableOrErr;
+ else
+ this->reportUniqueWarning(SecStrTableOrErr.takeError());
+
+ size_t SectionIndex = 0;
+ for (const Elf_Shdr &Sec : Sections) {
+ Fields[0].Str = to_string(SectionIndex);
+ if (SecStrTable.empty())
+ Fields[1].Str = "<no-strings>";
+ else
+ Fields[1].Str = std::string(unwrapOrError<StringRef>(
+ this->FileName, this->Obj.getSectionName(Sec, SecStrTable)));
+ Fields[2].Str =
+ getSectionTypeString(this->Obj.getHeader().e_machine, Sec.sh_type);
+ Fields[3].Str =
+ to_string(format_hex_no_prefix(Sec.sh_addr, ELFT::Is64Bits ? 16 : 8));
+ Fields[4].Str = to_string(format_hex_no_prefix(Sec.sh_offset, 6));
+ Fields[5].Str = to_string(format_hex_no_prefix(Sec.sh_size, 6));
+ Fields[6].Str = to_string(format_hex_no_prefix(Sec.sh_entsize, 2));
+ Fields[7].Str = getGNUFlags(this->Obj.getHeader().e_machine, Sec.sh_flags);
+ Fields[8].Str = to_string(Sec.sh_link);
+ Fields[9].Str = to_string(Sec.sh_info);
+ Fields[10].Str = to_string(Sec.sh_addralign);
+
+ OS.PadToColumn(Fields[0].Column);
+ OS << "[" << right_justify(Fields[0].Str, 2) << "]";
+ for (int i = 1; i < 7; i++)
+ printField(Fields[i]);
+ OS.PadToColumn(Fields[7].Column);
+ OS << right_justify(Fields[7].Str, 3);
+ OS.PadToColumn(Fields[8].Column);
+ OS << right_justify(Fields[8].Str, 2);
+ OS.PadToColumn(Fields[9].Column);
+ OS << right_justify(Fields[9].Str, 3);
+ OS.PadToColumn(Fields[10].Column);
+ OS << right_justify(Fields[10].Str, 2);
+ OS << "\n";
+ ++SectionIndex;
+ }
+ printSectionDescription(OS, this->Obj.getHeader().e_machine);
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printSymtabMessage(const Elf_Shdr *Symtab,
+ size_t Entries,
+ bool NonVisibilityBitsUsed) const {
+ StringRef Name;
+ if (Symtab)
+ Name = this->getPrintableSectionName(*Symtab);
+ if (!Name.empty())
+ OS << "\nSymbol table '" << Name << "'";
+ else
+ OS << "\nSymbol table for image";
+ OS << " contains " << Entries << " entries:\n";
+
+ if (ELFT::Is64Bits)
+ OS << " Num: Value Size Type Bind Vis";
+ else
+ OS << " Num: Value Size Type Bind Vis";
+
+ if (NonVisibilityBitsUsed)
+ OS << " ";
+ OS << " Ndx Name\n";
+}
+
+template <class ELFT>
+std::string
+GNUELFDumper<ELFT>::getSymbolSectionNdx(const Elf_Sym &Symbol,
+ unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable) const {
+ unsigned SectionIndex = Symbol.st_shndx;
+ switch (SectionIndex) {
+ case ELF::SHN_UNDEF:
+ return "UND";
+ case ELF::SHN_ABS:
+ return "ABS";
+ case ELF::SHN_COMMON:
+ return "COM";
+ case ELF::SHN_XINDEX: {
+ Expected<uint32_t> IndexOrErr =
+ object::getExtendedSymbolTableIndex<ELFT>(Symbol, SymIndex, ShndxTable);
+ if (!IndexOrErr) {
+ assert(Symbol.st_shndx == SHN_XINDEX &&
+ "getExtendedSymbolTableIndex should only fail due to an invalid "
+ "SHT_SYMTAB_SHNDX table/reference");
+ this->reportUniqueWarning(IndexOrErr.takeError());
+ return "RSV[0xffff]";
+ }
+ return to_string(format_decimal(*IndexOrErr, 3));
+ }
+ default:
+ // Find if:
+ // Processor specific
+ if (SectionIndex >= ELF::SHN_LOPROC && SectionIndex <= ELF::SHN_HIPROC)
+ return std::string("PRC[0x") +
+ to_string(format_hex_no_prefix(SectionIndex, 4)) + "]";
+ // OS specific
+ if (SectionIndex >= ELF::SHN_LOOS && SectionIndex <= ELF::SHN_HIOS)
+ return std::string("OS[0x") +
+ to_string(format_hex_no_prefix(SectionIndex, 4)) + "]";
+ // Architecture reserved:
+ if (SectionIndex >= ELF::SHN_LORESERVE &&
+ SectionIndex <= ELF::SHN_HIRESERVE)
+ return std::string("RSV[0x") +
+ to_string(format_hex_no_prefix(SectionIndex, 4)) + "]";
+ // A normal section with an index
+ return to_string(format_decimal(SectionIndex, 3));
+ }
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printSymbol(const Elf_Sym &Symbol, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable,
+ Optional<StringRef> StrTable,
+ bool IsDynamic,
+ bool NonVisibilityBitsUsed) const {
+ unsigned Bias = ELFT::Is64Bits ? 8 : 0;
+ Field Fields[8] = {0, 8, 17 + Bias, 23 + Bias,
+ 31 + Bias, 38 + Bias, 48 + Bias, 51 + Bias};
+ Fields[0].Str = to_string(format_decimal(SymIndex, 6)) + ":";
+ Fields[1].Str =
+ to_string(format_hex_no_prefix(Symbol.st_value, ELFT::Is64Bits ? 16 : 8));
+ Fields[2].Str = to_string(format_decimal(Symbol.st_size, 5));
+
+ unsigned char SymbolType = Symbol.getType();
+ if (this->Obj.getHeader().e_machine == ELF::EM_AMDGPU &&
+ SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS)
+ Fields[3].Str = enumToString(SymbolType, makeArrayRef(AMDGPUSymbolTypes));
+ else
+ Fields[3].Str = enumToString(SymbolType, makeArrayRef(ElfSymbolTypes));
+
+ Fields[4].Str =
+ enumToString(Symbol.getBinding(), makeArrayRef(ElfSymbolBindings));
+ Fields[5].Str =
+ enumToString(Symbol.getVisibility(), makeArrayRef(ElfSymbolVisibilities));
+
+ if (Symbol.st_other & ~0x3) {
+ if (this->Obj.getHeader().e_machine == ELF::EM_AARCH64) {
+ uint8_t Other = Symbol.st_other & ~0x3;
+ if (Other & STO_AARCH64_VARIANT_PCS) {
+ Other &= ~STO_AARCH64_VARIANT_PCS;
+ Fields[5].Str += " [VARIANT_PCS";
+ if (Other != 0)
+ Fields[5].Str.append(" | " + to_hexString(Other, false));
+ Fields[5].Str.append("]");
+ }
+ } else if (this->Obj.getHeader().e_machine == ELF::EM_RISCV) {
+ uint8_t Other = Symbol.st_other & ~0x3;
+ if (Other & STO_RISCV_VARIANT_CC) {
+ Other &= ~STO_RISCV_VARIANT_CC;
+ Fields[5].Str += " [VARIANT_CC";
+ if (Other != 0)
+ Fields[5].Str.append(" | " + to_hexString(Other, false));
+ Fields[5].Str.append("]");
+ }
+ } else {
+ Fields[5].Str +=
+ " [<other: " + to_string(format_hex(Symbol.st_other, 2)) + ">]";
+ }
+ }
+
+ Fields[6].Column += NonVisibilityBitsUsed ? 13 : 0;
+ Fields[6].Str = getSymbolSectionNdx(Symbol, SymIndex, ShndxTable);
+
+ Fields[7].Str = this->getFullSymbolName(Symbol, SymIndex, ShndxTable,
+ StrTable, IsDynamic);
+ for (const Field &Entry : Fields)
+ printField(Entry);
+ OS << "\n";
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printHashedSymbol(const Elf_Sym *Symbol,
+ unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable,
+ StringRef StrTable,
+ uint32_t Bucket) {
+ unsigned Bias = ELFT::Is64Bits ? 8 : 0;
+ Field Fields[9] = {0, 6, 11, 20 + Bias, 25 + Bias,
+ 34 + Bias, 41 + Bias, 49 + Bias, 53 + Bias};
+ Fields[0].Str = to_string(format_decimal(SymIndex, 5));
+ Fields[1].Str = to_string(format_decimal(Bucket, 3)) + ":";
+
+ Fields[2].Str = to_string(
+ format_hex_no_prefix(Symbol->st_value, ELFT::Is64Bits ? 16 : 8));
+ Fields[3].Str = to_string(format_decimal(Symbol->st_size, 5));
+
+ unsigned char SymbolType = Symbol->getType();
+ if (this->Obj.getHeader().e_machine == ELF::EM_AMDGPU &&
+ SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS)
+ Fields[4].Str = enumToString(SymbolType, makeArrayRef(AMDGPUSymbolTypes));
+ else
+ Fields[4].Str = enumToString(SymbolType, makeArrayRef(ElfSymbolTypes));
+
+ Fields[5].Str =
+ enumToString(Symbol->getBinding(), makeArrayRef(ElfSymbolBindings));
+ Fields[6].Str = enumToString(Symbol->getVisibility(),
+ makeArrayRef(ElfSymbolVisibilities));
+ Fields[7].Str = getSymbolSectionNdx(*Symbol, SymIndex, ShndxTable);
+ Fields[8].Str =
+ this->getFullSymbolName(*Symbol, SymIndex, ShndxTable, StrTable, true);
+
+ for (const Field &Entry : Fields)
+ printField(Entry);
+ OS << "\n";
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printSymbols(bool PrintSymbols,
+ bool PrintDynamicSymbols) {
+ if (!PrintSymbols && !PrintDynamicSymbols)
+ return;
+ // GNU readelf prints both the .dynsym and .symtab with --symbols.
+ this->printSymbolsHelper(true);
+ if (PrintSymbols)
+ this->printSymbolsHelper(false);
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printHashTableSymbols(const Elf_Hash &SysVHash) {
+ if (this->DynamicStringTable.empty())
+ return;
+
+ if (ELFT::Is64Bits)
+ OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
+ else
+ OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
+ OS << "\n";
+
+ Elf_Sym_Range DynSyms = this->dynamic_symbols();
+ const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0];
+ if (!FirstSym) {
+ this->reportUniqueWarning(
+ Twine("unable to print symbols for the .hash table: the "
+ "dynamic symbol table ") +
+ (this->DynSymRegion ? "is empty" : "was not found"));
+ return;
+ }
+
+ DataRegion<Elf_Word> ShndxTable(
+ (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end());
+ auto Buckets = SysVHash.buckets();
+ auto Chains = SysVHash.chains();
+ for (uint32_t Buc = 0; Buc < SysVHash.nbucket; Buc++) {
+ if (Buckets[Buc] == ELF::STN_UNDEF)
+ continue;
+ BitVector Visited(SysVHash.nchain);
+ for (uint32_t Ch = Buckets[Buc]; Ch < SysVHash.nchain; Ch = Chains[Ch]) {
+ if (Ch == ELF::STN_UNDEF)
+ break;
+
+ if (Visited[Ch]) {
+ this->reportUniqueWarning(".hash section is invalid: bucket " +
+ Twine(Ch) +
+ ": a cycle was detected in the linked chain");
+ break;
+ }
+
+ printHashedSymbol(FirstSym + Ch, Ch, ShndxTable, this->DynamicStringTable,
+ Buc);
+ Visited[Ch] = true;
+ }
+ }
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printGnuHashTableSymbols(const Elf_GnuHash &GnuHash) {
+ if (this->DynamicStringTable.empty())
+ return;
+
+ Elf_Sym_Range DynSyms = this->dynamic_symbols();
+ const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0];
+ if (!FirstSym) {
+ this->reportUniqueWarning(
+ Twine("unable to print symbols for the .gnu.hash table: the "
+ "dynamic symbol table ") +
+ (this->DynSymRegion ? "is empty" : "was not found"));
+ return;
+ }
+
+ auto GetSymbol = [&](uint64_t SymIndex,
+ uint64_t SymsTotal) -> const Elf_Sym * {
+ if (SymIndex >= SymsTotal) {
+ this->reportUniqueWarning(
+ "unable to print hashed symbol with index " + Twine(SymIndex) +
+ ", which is greater than or equal to the number of dynamic symbols "
+ "(" +
+ Twine::utohexstr(SymsTotal) + ")");
+ return nullptr;
+ }
+ return FirstSym + SymIndex;
+ };
+
+ Expected<ArrayRef<Elf_Word>> ValuesOrErr =
+ getGnuHashTableChains<ELFT>(this->DynSymRegion, &GnuHash);
+ ArrayRef<Elf_Word> Values;
+ if (!ValuesOrErr)
+ this->reportUniqueWarning("unable to get hash values for the SHT_GNU_HASH "
+ "section: " +
+ toString(ValuesOrErr.takeError()));
+ else
+ Values = *ValuesOrErr;
+
+ DataRegion<Elf_Word> ShndxTable(
+ (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end());
+ ArrayRef<Elf_Word> Buckets = GnuHash.buckets();
+ for (uint32_t Buc = 0; Buc < GnuHash.nbuckets; Buc++) {
+ if (Buckets[Buc] == ELF::STN_UNDEF)
+ continue;
+ uint32_t Index = Buckets[Buc];
+ // Print whole chain.
+ while (true) {
+ uint32_t SymIndex = Index++;
+ if (const Elf_Sym *Sym = GetSymbol(SymIndex, DynSyms.size()))
+ printHashedSymbol(Sym, SymIndex, ShndxTable, this->DynamicStringTable,
+ Buc);
+ else
+ break;
+
+ if (SymIndex < GnuHash.symndx) {
+ this->reportUniqueWarning(
+ "unable to read the hash value for symbol with index " +
+ Twine(SymIndex) +
+ ", which is less than the index of the first hashed symbol (" +
+ Twine(GnuHash.symndx) + ")");
+ break;
+ }
+
+ // Chain ends at symbol with stopper bit.
+ if ((Values[SymIndex - GnuHash.symndx] & 1) == 1)
+ break;
+ }
+ }
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printHashSymbols() {
+ if (this->HashTable) {
+ OS << "\n Symbol table of .hash for image:\n";
+ if (Error E = checkHashTable<ELFT>(*this, this->HashTable))
+ this->reportUniqueWarning(std::move(E));
+ else
+ printHashTableSymbols(*this->HashTable);
+ }
+
+ // Try printing the .gnu.hash table.
+ if (this->GnuHashTable) {
+ OS << "\n Symbol table of .gnu.hash for image:\n";
+ if (ELFT::Is64Bits)
+ OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
+ else
+ OS << " Num Buc: Value Size Type Bind Vis Ndx Name";
+ OS << "\n";
+
+ if (Error E = checkGNUHashTable<ELFT>(this->Obj, this->GnuHashTable))
+ this->reportUniqueWarning(std::move(E));
+ else
+ printGnuHashTableSymbols(*this->GnuHashTable);
+ }
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printSectionDetails() {
+ ArrayRef<Elf_Shdr> Sections = cantFail(this->Obj.sections());
+ OS << "There are " << to_string(Sections.size())
+ << " section headers, starting at offset "
+ << "0x" << to_hexString(this->Obj.getHeader().e_shoff, false) << ":\n\n";
+
+ OS << "Section Headers:\n";
+
+ auto PrintFields = [&](ArrayRef<Field> V) {
+ for (const Field &F : V)
+ printField(F);
+ OS << "\n";
+ };
+
+ PrintFields({{"[Nr]", 2}, {"Name", 7}});
+
+ constexpr bool Is64 = ELFT::Is64Bits;
+ PrintFields({{"Type", 7},
+ {Is64 ? "Address" : "Addr", 23},
+ {"Off", Is64 ? 40 : 32},
+ {"Size", Is64 ? 47 : 39},
+ {"ES", Is64 ? 54 : 46},
+ {"Lk", Is64 ? 59 : 51},
+ {"Inf", Is64 ? 62 : 54},
+ {"Al", Is64 ? 66 : 57}});
+ PrintFields({{"Flags", 7}});
+
+ StringRef SecStrTable;
+ if (Expected<StringRef> SecStrTableOrErr =
+ this->Obj.getSectionStringTable(Sections, this->WarningHandler))
+ SecStrTable = *SecStrTableOrErr;
+ else
+ this->reportUniqueWarning(SecStrTableOrErr.takeError());
+
+ size_t SectionIndex = 0;
+ const unsigned AddrSize = Is64 ? 16 : 8;
+ for (const Elf_Shdr &S : Sections) {
+ StringRef Name = "<?>";
+ if (Expected<StringRef> NameOrErr =
+ this->Obj.getSectionName(S, SecStrTable))
+ Name = *NameOrErr;
+ else
+ this->reportUniqueWarning(NameOrErr.takeError());
+
+ OS.PadToColumn(2);
+ OS << "[" << right_justify(to_string(SectionIndex), 2) << "]";
+ PrintFields({{Name, 7}});
+ PrintFields(
+ {{getSectionTypeString(this->Obj.getHeader().e_machine, S.sh_type), 7},
+ {to_string(format_hex_no_prefix(S.sh_addr, AddrSize)), 23},
+ {to_string(format_hex_no_prefix(S.sh_offset, 6)), Is64 ? 39 : 32},
+ {to_string(format_hex_no_prefix(S.sh_size, 6)), Is64 ? 47 : 39},
+ {to_string(format_hex_no_prefix(S.sh_entsize, 2)), Is64 ? 54 : 46},
+ {to_string(S.sh_link), Is64 ? 59 : 51},
+ {to_string(S.sh_info), Is64 ? 63 : 55},
+ {to_string(S.sh_addralign), Is64 ? 66 : 58}});
+
+ OS.PadToColumn(7);
+ OS << "[" << to_string(format_hex_no_prefix(S.sh_flags, AddrSize)) << "]: ";
+
+ DenseMap<unsigned, StringRef> FlagToName = {
+ {SHF_WRITE, "WRITE"}, {SHF_ALLOC, "ALLOC"},
+ {SHF_EXECINSTR, "EXEC"}, {SHF_MERGE, "MERGE"},
+ {SHF_STRINGS, "STRINGS"}, {SHF_INFO_LINK, "INFO LINK"},
+ {SHF_LINK_ORDER, "LINK ORDER"}, {SHF_OS_NONCONFORMING, "OS NONCONF"},
+ {SHF_GROUP, "GROUP"}, {SHF_TLS, "TLS"},
+ {SHF_COMPRESSED, "COMPRESSED"}, {SHF_EXCLUDE, "EXCLUDE"}};
+
+ uint64_t Flags = S.sh_flags;
+ uint64_t UnknownFlags = 0;
+ ListSeparator LS;
+ while (Flags) {
+ // Take the least significant bit as a flag.
+ uint64_t Flag = Flags & -Flags;
+ Flags -= Flag;
+
+ auto It = FlagToName.find(Flag);
+ if (It != FlagToName.end())
+ OS << LS << It->second;
+ else
+ UnknownFlags |= Flag;
+ }
+
+ auto PrintUnknownFlags = [&](uint64_t Mask, StringRef Name) {
+ uint64_t FlagsToPrint = UnknownFlags & Mask;
+ if (!FlagsToPrint)
+ return;
+
+ OS << LS << Name << " ("
+ << to_string(format_hex_no_prefix(FlagsToPrint, AddrSize)) << ")";
+ UnknownFlags &= ~Mask;
+ };
+
+ PrintUnknownFlags(SHF_MASKOS, "OS");
+ PrintUnknownFlags(SHF_MASKPROC, "PROC");
+ PrintUnknownFlags(uint64_t(-1), "UNKNOWN");
+
+ OS << "\n";
+ ++SectionIndex;
+ }
+}
+
+static inline std::string printPhdrFlags(unsigned Flag) {
+ std::string Str;
+ Str = (Flag & PF_R) ? "R" : " ";
+ Str += (Flag & PF_W) ? "W" : " ";
+ Str += (Flag & PF_X) ? "E" : " ";
+ return Str;
+}
+
+template <class ELFT>
+static bool checkTLSSections(const typename ELFT::Phdr &Phdr,
+ const typename ELFT::Shdr &Sec) {
+ if (Sec.sh_flags & ELF::SHF_TLS) {
+ // .tbss must only be shown in the PT_TLS segment.
+ if (Sec.sh_type == ELF::SHT_NOBITS)
+ return Phdr.p_type == ELF::PT_TLS;
+
+ // SHF_TLS sections are only shown in PT_TLS, PT_LOAD or PT_GNU_RELRO
+ // segments.
+ return (Phdr.p_type == ELF::PT_TLS) || (Phdr.p_type == ELF::PT_LOAD) ||
+ (Phdr.p_type == ELF::PT_GNU_RELRO);
+ }
+
+ // PT_TLS must only have SHF_TLS sections.
+ return Phdr.p_type != ELF::PT_TLS;
+}
+
+template <class ELFT>
+static bool checkOffsets(const typename ELFT::Phdr &Phdr,
+ const typename ELFT::Shdr &Sec) {
+ // SHT_NOBITS sections don't need to have an offset inside the segment.
+ if (Sec.sh_type == ELF::SHT_NOBITS)
+ return true;
+
+ if (Sec.sh_offset < Phdr.p_offset)
+ return false;
+
+ // Only non-empty sections can be at the end of a segment.
+ if (Sec.sh_size == 0)
+ return (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz);
+ return Sec.sh_offset + Sec.sh_size <= Phdr.p_offset + Phdr.p_filesz;
+}
+
+// Check that an allocatable section belongs to a virtual address
+// space of a segment.
+template <class ELFT>
+static bool checkVMA(const typename ELFT::Phdr &Phdr,
+ const typename ELFT::Shdr &Sec) {
+ if (!(Sec.sh_flags & ELF::SHF_ALLOC))
+ return true;
+
+ if (Sec.sh_addr < Phdr.p_vaddr)
+ return false;
+
+ bool IsTbss =
+ (Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0);
+ // .tbss is special, it only has memory in PT_TLS and has NOBITS properties.
+ bool IsTbssInNonTLS = IsTbss && Phdr.p_type != ELF::PT_TLS;
+ // Only non-empty sections can be at the end of a segment.
+ if (Sec.sh_size == 0 || IsTbssInNonTLS)
+ return Sec.sh_addr + 1 <= Phdr.p_vaddr + Phdr.p_memsz;
+ return Sec.sh_addr + Sec.sh_size <= Phdr.p_vaddr + Phdr.p_memsz;
+}
+
+template <class ELFT>
+static bool checkPTDynamic(const typename ELFT::Phdr &Phdr,
+ const typename ELFT::Shdr &Sec) {
+ if (Phdr.p_type != ELF::PT_DYNAMIC || Phdr.p_memsz == 0 || Sec.sh_size != 0)
+ return true;
+
+ // We get here when we have an empty section. Only non-empty sections can be
+ // at the start or at the end of PT_DYNAMIC.
+ // Is section within the phdr both based on offset and VMA?
+ bool CheckOffset = (Sec.sh_type == ELF::SHT_NOBITS) ||
+ (Sec.sh_offset > Phdr.p_offset &&
+ Sec.sh_offset < Phdr.p_offset + Phdr.p_filesz);
+ bool CheckVA = !(Sec.sh_flags & ELF::SHF_ALLOC) ||
+ (Sec.sh_addr > Phdr.p_vaddr && Sec.sh_addr < Phdr.p_memsz);
+ return CheckOffset && CheckVA;
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printProgramHeaders(
+ bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) {
+ if (PrintProgramHeaders)
+ printProgramHeaders();
+
+ // Display the section mapping along with the program headers, unless
+ // -section-mapping is explicitly set to false.
+ if (PrintSectionMapping != cl::BOU_FALSE)
+ printSectionMapping();
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printProgramHeaders() {
+ unsigned Bias = ELFT::Is64Bits ? 8 : 0;
+ const Elf_Ehdr &Header = this->Obj.getHeader();
+ Field Fields[8] = {2, 17, 26, 37 + Bias,
+ 48 + Bias, 56 + Bias, 64 + Bias, 68 + Bias};
+ OS << "\nElf file type is "
+ << enumToString(Header.e_type, makeArrayRef(ElfObjectFileType)) << "\n"
+ << "Entry point " << format_hex(Header.e_entry, 3) << "\n"
+ << "There are " << Header.e_phnum << " program headers,"
+ << " starting at offset " << Header.e_phoff << "\n\n"
+ << "Program Headers:\n";
+ if (ELFT::Is64Bits)
+ OS << " Type Offset VirtAddr PhysAddr "
+ << " FileSiz MemSiz Flg Align\n";
+ else
+ OS << " Type Offset VirtAddr PhysAddr FileSiz "
+ << "MemSiz Flg Align\n";
+
+ unsigned Width = ELFT::Is64Bits ? 18 : 10;
+ unsigned SizeWidth = ELFT::Is64Bits ? 8 : 7;
+
+ Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = this->Obj.program_headers();
+ if (!PhdrsOrErr) {
+ this->reportUniqueWarning("unable to dump program headers: " +
+ toString(PhdrsOrErr.takeError()));
+ return;
+ }
+
+ for (const Elf_Phdr &Phdr : *PhdrsOrErr) {
+ Fields[0].Str = getGNUPtType(Header.e_machine, Phdr.p_type);
+ Fields[1].Str = to_string(format_hex(Phdr.p_offset, 8));
+ Fields[2].Str = to_string(format_hex(Phdr.p_vaddr, Width));
+ Fields[3].Str = to_string(format_hex(Phdr.p_paddr, Width));
+ Fields[4].Str = to_string(format_hex(Phdr.p_filesz, SizeWidth));
+ Fields[5].Str = to_string(format_hex(Phdr.p_memsz, SizeWidth));
+ Fields[6].Str = printPhdrFlags(Phdr.p_flags);
+ Fields[7].Str = to_string(format_hex(Phdr.p_align, 1));
+ for (const Field &F : Fields)
+ printField(F);
+ if (Phdr.p_type == ELF::PT_INTERP) {
+ OS << "\n";
+ auto ReportBadInterp = [&](const Twine &Msg) {
+ this->reportUniqueWarning(
+ "unable to read program interpreter name at offset 0x" +
+ Twine::utohexstr(Phdr.p_offset) + ": " + Msg);
+ };
+
+ if (Phdr.p_offset >= this->Obj.getBufSize()) {
+ ReportBadInterp("it goes past the end of the file (0x" +
+ Twine::utohexstr(this->Obj.getBufSize()) + ")");
+ continue;
+ }
+
+ const char *Data =
+ reinterpret_cast<const char *>(this->Obj.base()) + Phdr.p_offset;
+ size_t MaxSize = this->Obj.getBufSize() - Phdr.p_offset;
+ size_t Len = strnlen(Data, MaxSize);
+ if (Len == MaxSize) {
+ ReportBadInterp("it is not null-terminated");
+ continue;
+ }
+
+ OS << " [Requesting program interpreter: ";
+ OS << StringRef(Data, Len) << "]";
+ }
+ OS << "\n";
+ }
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printSectionMapping() {
+ OS << "\n Section to Segment mapping:\n Segment Sections...\n";
+ DenseSet<const Elf_Shdr *> BelongsToSegment;
+ int Phnum = 0;
+
+ Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = this->Obj.program_headers();
+ if (!PhdrsOrErr) {
+ this->reportUniqueWarning(
+ "can't read program headers to build section to segment mapping: " +
+ toString(PhdrsOrErr.takeError()));
+ return;
+ }
+
+ for (const Elf_Phdr &Phdr : *PhdrsOrErr) {
+ std::string Sections;
+ OS << format(" %2.2d ", Phnum++);
+ // Check if each section is in a segment and then print mapping.
+ for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) {
+ if (Sec.sh_type == ELF::SHT_NULL)
+ continue;
+
+ // readelf additionally makes sure it does not print zero sized sections
+ // at end of segments and for PT_DYNAMIC both start and end of section
+ // .tbss must only be shown in PT_TLS section.
+ if (checkTLSSections<ELFT>(Phdr, Sec) && checkOffsets<ELFT>(Phdr, Sec) &&
+ checkVMA<ELFT>(Phdr, Sec) && checkPTDynamic<ELFT>(Phdr, Sec)) {
+ Sections +=
+ unwrapOrError(this->FileName, this->Obj.getSectionName(Sec)).str() +
+ " ";
+ BelongsToSegment.insert(&Sec);
+ }
+ }
+ OS << Sections << "\n";
+ OS.flush();
+ }
+
+ // Display sections that do not belong to a segment.
+ std::string Sections;
+ for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) {
+ if (BelongsToSegment.find(&Sec) == BelongsToSegment.end())
+ Sections +=
+ unwrapOrError(this->FileName, this->Obj.getSectionName(Sec)).str() +
+ ' ';
+ }
+ if (!Sections.empty()) {
+ OS << " None " << Sections << '\n';
+ OS.flush();
+ }
+}
+
+namespace {
+
+template <class ELFT>
+RelSymbol<ELFT> getSymbolForReloc(const ELFDumper<ELFT> &Dumper,
+ const Relocation<ELFT> &Reloc) {
+ using Elf_Sym = typename ELFT::Sym;
+ auto WarnAndReturn = [&](const Elf_Sym *Sym,
+ const Twine &Reason) -> RelSymbol<ELFT> {
+ Dumper.reportUniqueWarning(
+ "unable to get name of the dynamic symbol with index " +
+ Twine(Reloc.Symbol) + ": " + Reason);
+ return {Sym, "<corrupt>"};
+ };
+
+ ArrayRef<Elf_Sym> Symbols = Dumper.dynamic_symbols();
+ const Elf_Sym *FirstSym = Symbols.begin();
+ if (!FirstSym)
+ return WarnAndReturn(nullptr, "no dynamic symbol table found");
+
+ // We might have an object without a section header. In this case the size of
+ // Symbols is zero, because there is no way to know the size of the dynamic
+ // table. We should allow this case and not print a warning.
+ if (!Symbols.empty() && Reloc.Symbol >= Symbols.size())
+ return WarnAndReturn(
+ nullptr,
+ "index is greater than or equal to the number of dynamic symbols (" +
+ Twine(Symbols.size()) + ")");
+
+ const ELFFile<ELFT> &Obj = Dumper.getElfObject().getELFFile();
+ const uint64_t FileSize = Obj.getBufSize();
+ const uint64_t SymOffset = ((const uint8_t *)FirstSym - Obj.base()) +
+ (uint64_t)Reloc.Symbol * sizeof(Elf_Sym);
+ if (SymOffset + sizeof(Elf_Sym) > FileSize)
+ return WarnAndReturn(nullptr, "symbol at 0x" + Twine::utohexstr(SymOffset) +
+ " goes past the end of the file (0x" +
+ Twine::utohexstr(FileSize) + ")");
+
+ const Elf_Sym *Sym = FirstSym + Reloc.Symbol;
+ Expected<StringRef> ErrOrName = Sym->getName(Dumper.getDynamicStringTable());
+ if (!ErrOrName)
+ return WarnAndReturn(Sym, toString(ErrOrName.takeError()));
+
+ return {Sym == FirstSym ? nullptr : Sym, maybeDemangle(*ErrOrName)};
+}
+} // namespace
+
+template <class ELFT>
+static size_t getMaxDynamicTagSize(const ELFFile<ELFT> &Obj,
+ typename ELFT::DynRange Tags) {
+ size_t Max = 0;
+ for (const typename ELFT::Dyn &Dyn : Tags)
+ Max = std::max(Max, Obj.getDynamicTagAsString(Dyn.d_tag).size());
+ return Max;
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printDynamicTable() {
+ Elf_Dyn_Range Table = this->dynamic_table();
+ if (Table.empty())
+ return;
+
+ OS << "Dynamic section at offset "
+ << format_hex(reinterpret_cast<const uint8_t *>(this->DynamicTable.Addr) -
+ this->Obj.base(),
+ 1)
+ << " contains " << Table.size() << " entries:\n";
+
+ // The type name is surrounded with round brackets, hence add 2.
+ size_t MaxTagSize = getMaxDynamicTagSize(this->Obj, Table) + 2;
+ // The "Name/Value" column should be indented from the "Type" column by N
+ // spaces, where N = MaxTagSize - length of "Type" (4) + trailing
+ // space (1) = 3.
+ OS << " Tag" + std::string(ELFT::Is64Bits ? 16 : 8, ' ') + "Type"
+ << std::string(MaxTagSize - 3, ' ') << "Name/Value\n";
+
+ std::string ValueFmt = " %-" + std::to_string(MaxTagSize) + "s ";
+ for (auto Entry : Table) {
+ uintX_t Tag = Entry.getTag();
+ std::string Type =
+ std::string("(") + this->Obj.getDynamicTagAsString(Tag) + ")";
+ std::string Value = this->getDynamicEntry(Tag, Entry.getVal());
+ OS << " " << format_hex(Tag, ELFT::Is64Bits ? 18 : 10)
+ << format(ValueFmt.c_str(), Type.c_str()) << Value << "\n";
+ }
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printDynamicRelocations() {
+ this->printDynamicRelocationsHelper();
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::printDynamicReloc(const Relocation<ELFT> &R) {
+ printRelRelaReloc(R, getSymbolForReloc(*this, R));
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::printRelocationsHelper(const Elf_Shdr &Sec) {
+ this->forEachRelocationDo(
+ Sec, opts::RawRelr,
+ [&](const Relocation<ELFT> &R, unsigned Ndx, const Elf_Shdr &Sec,
+ const Elf_Shdr *SymTab) { printReloc(R, Ndx, Sec, SymTab); },
+ [&](const Elf_Relr &R) { printRelrReloc(R); });
+}
+
+template <class ELFT> void ELFDumper<ELFT>::printDynamicRelocationsHelper() {
+ const bool IsMips64EL = this->Obj.isMips64EL();
+ if (this->DynRelaRegion.Size > 0) {
+ printDynamicRelocHeader(ELF::SHT_RELA, "RELA", this->DynRelaRegion);
+ for (const Elf_Rela &Rela :
+ this->DynRelaRegion.template getAsArrayRef<Elf_Rela>())
+ printDynamicReloc(Relocation<ELFT>(Rela, IsMips64EL));
+ }
+
+ if (this->DynRelRegion.Size > 0) {
+ printDynamicRelocHeader(ELF::SHT_REL, "REL", this->DynRelRegion);
+ for (const Elf_Rel &Rel :
+ this->DynRelRegion.template getAsArrayRef<Elf_Rel>())
+ printDynamicReloc(Relocation<ELFT>(Rel, IsMips64EL));
+ }
+
+ if (this->DynRelrRegion.Size > 0) {
+ printDynamicRelocHeader(ELF::SHT_REL, "RELR", this->DynRelrRegion);
+ Elf_Relr_Range Relrs =
+ this->DynRelrRegion.template getAsArrayRef<Elf_Relr>();
+ for (const Elf_Rel &Rel : Obj.decode_relrs(Relrs))
+ printDynamicReloc(Relocation<ELFT>(Rel, IsMips64EL));
+ }
+
+ if (this->DynPLTRelRegion.Size) {
+ if (this->DynPLTRelRegion.EntSize == sizeof(Elf_Rela)) {
+ printDynamicRelocHeader(ELF::SHT_RELA, "PLT", this->DynPLTRelRegion);
+ for (const Elf_Rela &Rela :
+ this->DynPLTRelRegion.template getAsArrayRef<Elf_Rela>())
+ printDynamicReloc(Relocation<ELFT>(Rela, IsMips64EL));
+ } else {
+ printDynamicRelocHeader(ELF::SHT_REL, "PLT", this->DynPLTRelRegion);
+ for (const Elf_Rel &Rel :
+ this->DynPLTRelRegion.template getAsArrayRef<Elf_Rel>())
+ printDynamicReloc(Relocation<ELFT>(Rel, IsMips64EL));
+ }
+ }
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printGNUVersionSectionProlog(
+ const typename ELFT::Shdr &Sec, const Twine &Label, unsigned EntriesNum) {
+ // Don't inline the SecName, because it might report a warning to stderr and
+ // corrupt the output.
+ StringRef SecName = this->getPrintableSectionName(Sec);
+ OS << Label << " section '" << SecName << "' "
+ << "contains " << EntriesNum << " entries:\n";
+
+ StringRef LinkedSecName = "<corrupt>";
+ if (Expected<const typename ELFT::Shdr *> LinkedSecOrErr =
+ this->Obj.getSection(Sec.sh_link))
+ LinkedSecName = this->getPrintableSectionName(**LinkedSecOrErr);
+ else
+ this->reportUniqueWarning("invalid section linked to " +
+ this->describe(Sec) + ": " +
+ toString(LinkedSecOrErr.takeError()));
+
+ OS << " Addr: " << format_hex_no_prefix(Sec.sh_addr, 16)
+ << " Offset: " << format_hex(Sec.sh_offset, 8)
+ << " Link: " << Sec.sh_link << " (" << LinkedSecName << ")\n";
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printVersionSymbolSection(const Elf_Shdr *Sec) {
+ if (!Sec)
+ return;
+
+ printGNUVersionSectionProlog(*Sec, "Version symbols",
+ Sec->sh_size / sizeof(Elf_Versym));
+ Expected<ArrayRef<Elf_Versym>> VerTableOrErr =
+ this->getVersionTable(*Sec, /*SymTab=*/nullptr,
+ /*StrTab=*/nullptr, /*SymTabSec=*/nullptr);
+ if (!VerTableOrErr) {
+ this->reportUniqueWarning(VerTableOrErr.takeError());
+ return;
+ }
+
+ SmallVector<Optional<VersionEntry>, 0> *VersionMap = nullptr;
+ if (Expected<SmallVector<Optional<VersionEntry>, 0> *> MapOrErr =
+ this->getVersionMap())
+ VersionMap = *MapOrErr;
+ else
+ this->reportUniqueWarning(MapOrErr.takeError());
+
+ ArrayRef<Elf_Versym> VerTable = *VerTableOrErr;
+ std::vector<StringRef> Versions;
+ for (size_t I = 0, E = VerTable.size(); I < E; ++I) {
+ unsigned Ndx = VerTable[I].vs_index;
+ if (Ndx == VER_NDX_LOCAL || Ndx == VER_NDX_GLOBAL) {
+ Versions.emplace_back(Ndx == VER_NDX_LOCAL ? "*local*" : "*global*");
+ continue;
+ }
+
+ if (!VersionMap) {
+ Versions.emplace_back("<corrupt>");
+ continue;
+ }
+
+ bool IsDefault;
+ Expected<StringRef> NameOrErr = this->Obj.getSymbolVersionByIndex(
+ Ndx, IsDefault, *VersionMap, /*IsSymHidden=*/None);
+ if (!NameOrErr) {
+ this->reportUniqueWarning("unable to get a version for entry " +
+ Twine(I) + " of " + this->describe(*Sec) +
+ ": " + toString(NameOrErr.takeError()));
+ Versions.emplace_back("<corrupt>");
+ continue;
+ }
+ Versions.emplace_back(*NameOrErr);
+ }
+
+ // readelf prints 4 entries per line.
+ uint64_t Entries = VerTable.size();
+ for (uint64_t VersymRow = 0; VersymRow < Entries; VersymRow += 4) {
+ OS << " " << format_hex_no_prefix(VersymRow, 3) << ":";
+ for (uint64_t I = 0; (I < 4) && (I + VersymRow) < Entries; ++I) {
+ unsigned Ndx = VerTable[VersymRow + I].vs_index;
+ OS << format("%4x%c", Ndx & VERSYM_VERSION,
+ Ndx & VERSYM_HIDDEN ? 'h' : ' ');
+ OS << left_justify("(" + std::string(Versions[VersymRow + I]) + ")", 13);
+ }
+ OS << '\n';
+ }
+ OS << '\n';
+}
+
+static std::string versionFlagToString(unsigned Flags) {
+ if (Flags == 0)
+ return "none";
+
+ std::string Ret;
+ auto AddFlag = [&Ret, &Flags](unsigned Flag, StringRef Name) {
+ if (!(Flags & Flag))
+ return;
+ if (!Ret.empty())
+ Ret += " | ";
+ Ret += Name;
+ Flags &= ~Flag;
+ };
+
+ AddFlag(VER_FLG_BASE, "BASE");
+ AddFlag(VER_FLG_WEAK, "WEAK");
+ AddFlag(VER_FLG_INFO, "INFO");
+ AddFlag(~0, "<unknown>");
+ return Ret;
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printVersionDefinitionSection(const Elf_Shdr *Sec) {
+ if (!Sec)
+ return;
+
+ printGNUVersionSectionProlog(*Sec, "Version definition", Sec->sh_info);
+
+ Expected<std::vector<VerDef>> V = this->Obj.getVersionDefinitions(*Sec);
+ if (!V) {
+ this->reportUniqueWarning(V.takeError());
+ return;
+ }
+
+ for (const VerDef &Def : *V) {
+ OS << format(" 0x%04x: Rev: %u Flags: %s Index: %u Cnt: %u Name: %s\n",
+ Def.Offset, Def.Version,
+ versionFlagToString(Def.Flags).c_str(), Def.Ndx, Def.Cnt,
+ Def.Name.data());
+ unsigned I = 0;
+ for (const VerdAux &Aux : Def.AuxV)
+ OS << format(" 0x%04x: Parent %u: %s\n", Aux.Offset, ++I,
+ Aux.Name.data());
+ }
+
+ OS << '\n';
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printVersionDependencySection(const Elf_Shdr *Sec) {
+ if (!Sec)
+ return;
+
+ unsigned VerneedNum = Sec->sh_info;
+ printGNUVersionSectionProlog(*Sec, "Version needs", VerneedNum);
+
+ Expected<std::vector<VerNeed>> V =
+ this->Obj.getVersionDependencies(*Sec, this->WarningHandler);
+ if (!V) {
+ this->reportUniqueWarning(V.takeError());
+ return;
+ }
+
+ for (const VerNeed &VN : *V) {
+ OS << format(" 0x%04x: Version: %u File: %s Cnt: %u\n", VN.Offset,
+ VN.Version, VN.File.data(), VN.Cnt);
+ for (const VernAux &Aux : VN.AuxV)
+ OS << format(" 0x%04x: Name: %s Flags: %s Version: %u\n", Aux.Offset,
+ Aux.Name.data(), versionFlagToString(Aux.Flags).c_str(),
+ Aux.Other);
+ }
+ OS << '\n';
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printHashHistogram(const Elf_Hash &HashTable) {
+ size_t NBucket = HashTable.nbucket;
+ size_t NChain = HashTable.nchain;
+ ArrayRef<Elf_Word> Buckets = HashTable.buckets();
+ ArrayRef<Elf_Word> Chains = HashTable.chains();
+ size_t TotalSyms = 0;
+ // If hash table is correct, we have at least chains with 0 length
+ size_t MaxChain = 1;
+ size_t CumulativeNonZero = 0;
+
+ if (NChain == 0 || NBucket == 0)
+ return;
+
+ std::vector<size_t> ChainLen(NBucket, 0);
+ // Go over all buckets and and note chain lengths of each bucket (total
+ // unique chain lengths).
+ for (size_t B = 0; B < NBucket; B++) {
+ BitVector Visited(NChain);
+ for (size_t C = Buckets[B]; C < NChain; C = Chains[C]) {
+ if (C == ELF::STN_UNDEF)
+ break;
+ if (Visited[C]) {
+ this->reportUniqueWarning(".hash section is invalid: bucket " +
+ Twine(C) +
+ ": a cycle was detected in the linked chain");
+ break;
+ }
+ Visited[C] = true;
+ if (MaxChain <= ++ChainLen[B])
+ MaxChain++;
+ }
+ TotalSyms += ChainLen[B];
+ }
+
+ if (!TotalSyms)
+ return;
+
+ std::vector<size_t> Count(MaxChain, 0);
+ // Count how long is the chain for each bucket
+ for (size_t B = 0; B < NBucket; B++)
+ ++Count[ChainLen[B]];
+ // Print Number of buckets with each chain lengths and their cumulative
+ // coverage of the symbols
+ OS << "Histogram for bucket list length (total of " << NBucket
+ << " buckets)\n"
+ << " Length Number % of total Coverage\n";
+ for (size_t I = 0; I < MaxChain; I++) {
+ CumulativeNonZero += Count[I] * I;
+ OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I],
+ (Count[I] * 100.0) / NBucket,
+ (CumulativeNonZero * 100.0) / TotalSyms);
+ }
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printGnuHashHistogram(
+ const Elf_GnuHash &GnuHashTable) {
+ Expected<ArrayRef<Elf_Word>> ChainsOrErr =
+ getGnuHashTableChains<ELFT>(this->DynSymRegion, &GnuHashTable);
+ if (!ChainsOrErr) {
+ this->reportUniqueWarning("unable to print the GNU hash table histogram: " +
+ toString(ChainsOrErr.takeError()));
+ return;
+ }
+
+ ArrayRef<Elf_Word> Chains = *ChainsOrErr;
+ size_t Symndx = GnuHashTable.symndx;
+ size_t TotalSyms = 0;
+ size_t MaxChain = 1;
+ size_t CumulativeNonZero = 0;
+
+ size_t NBucket = GnuHashTable.nbuckets;
+ if (Chains.empty() || NBucket == 0)
+ return;
+
+ ArrayRef<Elf_Word> Buckets = GnuHashTable.buckets();
+ std::vector<size_t> ChainLen(NBucket, 0);
+ for (size_t B = 0; B < NBucket; B++) {
+ if (!Buckets[B])
+ continue;
+ size_t Len = 1;
+ for (size_t C = Buckets[B] - Symndx;
+ C < Chains.size() && (Chains[C] & 1) == 0; C++)
+ if (MaxChain < ++Len)
+ MaxChain++;
+ ChainLen[B] = Len;
+ TotalSyms += Len;
+ }
+ MaxChain++;
+
+ if (!TotalSyms)
+ return;
+
+ std::vector<size_t> Count(MaxChain, 0);
+ for (size_t B = 0; B < NBucket; B++)
+ ++Count[ChainLen[B]];
+ // Print Number of buckets with each chain lengths and their cumulative
+ // coverage of the symbols
+ OS << "Histogram for `.gnu.hash' bucket list length (total of " << NBucket
+ << " buckets)\n"
+ << " Length Number % of total Coverage\n";
+ for (size_t I = 0; I < MaxChain; I++) {
+ CumulativeNonZero += Count[I] * I;
+ OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I],
+ (Count[I] * 100.0) / NBucket,
+ (CumulativeNonZero * 100.0) / TotalSyms);
+ }
+}
+
+// Hash histogram shows statistics of how efficient the hash was for the
+// dynamic symbol table. The table shows the number of hash buckets for
+// different lengths of chains as an absolute number and percentage of the total
+// buckets, and the cumulative coverage of symbols for each set of buckets.
+template <class ELFT> void GNUELFDumper<ELFT>::printHashHistograms() {
+ // Print histogram for the .hash section.
+ if (this->HashTable) {
+ if (Error E = checkHashTable<ELFT>(*this, this->HashTable))
+ this->reportUniqueWarning(std::move(E));
+ else
+ printHashHistogram(*this->HashTable);
+ }
+
+ // Print histogram for the .gnu.hash section.
+ if (this->GnuHashTable) {
+ if (Error E = checkGNUHashTable<ELFT>(this->Obj, this->GnuHashTable))
+ this->reportUniqueWarning(std::move(E));
+ else
+ printGnuHashHistogram(*this->GnuHashTable);
+ }
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() {
+ OS << "GNUStyle::printCGProfile not implemented\n";
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printBBAddrMaps() {
+ OS << "GNUStyle::printBBAddrMaps not implemented\n";
+}
+
+static Expected<std::vector<uint64_t>> toULEB128Array(ArrayRef<uint8_t> Data) {
+ std::vector<uint64_t> Ret;
+ const uint8_t *Cur = Data.begin();
+ const uint8_t *End = Data.end();
+ while (Cur != End) {
+ unsigned Size;
+ const char *Err;
+ Ret.push_back(decodeULEB128(Cur, &Size, End, &Err));
+ if (Err)
+ return createError(Err);
+ Cur += Size;
+ }
+ return Ret;
+}
+
+template <class ELFT>
+static Expected<std::vector<uint64_t>>
+decodeAddrsigSection(const ELFFile<ELFT> &Obj, const typename ELFT::Shdr &Sec) {
+ Expected<ArrayRef<uint8_t>> ContentsOrErr = Obj.getSectionContents(Sec);
+ if (!ContentsOrErr)
+ return ContentsOrErr.takeError();
+
+ if (Expected<std::vector<uint64_t>> SymsOrErr =
+ toULEB128Array(*ContentsOrErr))
+ return *SymsOrErr;
+ else
+ return createError("unable to decode " + describe(Obj, Sec) + ": " +
+ toString(SymsOrErr.takeError()));
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printAddrsig() {
+ if (!this->DotAddrsigSec)
+ return;
+
+ Expected<std::vector<uint64_t>> SymsOrErr =
+ decodeAddrsigSection(this->Obj, *this->DotAddrsigSec);
+ if (!SymsOrErr) {
+ this->reportUniqueWarning(SymsOrErr.takeError());
+ return;
+ }
+
+ StringRef Name = this->getPrintableSectionName(*this->DotAddrsigSec);
+ OS << "\nAddress-significant symbols section '" << Name << "'"
+ << " contains " << SymsOrErr->size() << " entries:\n";
+ OS << " Num: Name\n";
+
+ Field Fields[2] = {0, 8};
+ size_t SymIndex = 0;
+ for (uint64_t Sym : *SymsOrErr) {
+ Fields[0].Str = to_string(format_decimal(++SymIndex, 6)) + ":";
+ Fields[1].Str = this->getStaticSymbolName(Sym);
+ for (const Field &Entry : Fields)
+ printField(Entry);
+ OS << "\n";
+ }
+}
+
+template <typename ELFT>
+static std::string getGNUProperty(uint32_t Type, uint32_t DataSize,
+ ArrayRef<uint8_t> Data) {
+ std::string str;
+ raw_string_ostream OS(str);
+ uint32_t PrData;
+ auto DumpBit = [&](uint32_t Flag, StringRef Name) {
+ if (PrData & Flag) {
+ PrData &= ~Flag;
+ OS << Name;
+ if (PrData)
+ OS << ", ";
+ }
+ };
+
+ switch (Type) {
+ default:
+ OS << format("<application-specific type 0x%x>", Type);
+ return OS.str();
+ case GNU_PROPERTY_STACK_SIZE: {
+ OS << "stack size: ";
+ if (DataSize == sizeof(typename ELFT::uint))
+ OS << formatv("{0:x}",
+ (uint64_t)(*(const typename ELFT::Addr *)Data.data()));
+ else
+ OS << format("<corrupt length: 0x%x>", DataSize);
+ return OS.str();
+ }
+ case GNU_PROPERTY_NO_COPY_ON_PROTECTED:
+ OS << "no copy on protected";
+ if (DataSize)
+ OS << format(" <corrupt length: 0x%x>", DataSize);
+ return OS.str();
+ case GNU_PROPERTY_AARCH64_FEATURE_1_AND:
+ case GNU_PROPERTY_X86_FEATURE_1_AND:
+ OS << ((Type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) ? "aarch64 feature: "
+ : "x86 feature: ");
+ if (DataSize != 4) {
+ OS << format("<corrupt length: 0x%x>", DataSize);
+ return OS.str();
+ }
+ PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data());
+ if (PrData == 0) {
+ OS << "<None>";
+ return OS.str();
+ }
+ if (Type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) {
+ DumpBit(GNU_PROPERTY_AARCH64_FEATURE_1_BTI, "BTI");
+ DumpBit(GNU_PROPERTY_AARCH64_FEATURE_1_PAC, "PAC");
+ } else {
+ DumpBit(GNU_PROPERTY_X86_FEATURE_1_IBT, "IBT");
+ DumpBit(GNU_PROPERTY_X86_FEATURE_1_SHSTK, "SHSTK");
+ }
+ if (PrData)
+ OS << format("<unknown flags: 0x%x>", PrData);
+ return OS.str();
+ case GNU_PROPERTY_X86_FEATURE_2_NEEDED:
+ case GNU_PROPERTY_X86_FEATURE_2_USED:
+ OS << "x86 feature "
+ << (Type == GNU_PROPERTY_X86_FEATURE_2_NEEDED ? "needed: " : "used: ");
+ if (DataSize != 4) {
+ OS << format("<corrupt length: 0x%x>", DataSize);
+ return OS.str();
+ }
+ PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data());
+ if (PrData == 0) {
+ OS << "<None>";
+ return OS.str();
+ }
+ DumpBit(GNU_PROPERTY_X86_FEATURE_2_X86, "x86");
+ DumpBit(GNU_PROPERTY_X86_FEATURE_2_X87, "x87");
+ DumpBit(GNU_PROPERTY_X86_FEATURE_2_MMX, "MMX");
+ DumpBit(GNU_PROPERTY_X86_FEATURE_2_XMM, "XMM");
+ DumpBit(GNU_PROPERTY_X86_FEATURE_2_YMM, "YMM");
+ DumpBit(GNU_PROPERTY_X86_FEATURE_2_ZMM, "ZMM");
+ DumpBit(GNU_PROPERTY_X86_FEATURE_2_FXSR, "FXSR");
+ DumpBit(GNU_PROPERTY_X86_FEATURE_2_XSAVE, "XSAVE");
+ DumpBit(GNU_PROPERTY_X86_FEATURE_2_XSAVEOPT, "XSAVEOPT");
+ DumpBit(GNU_PROPERTY_X86_FEATURE_2_XSAVEC, "XSAVEC");
+ if (PrData)
+ OS << format("<unknown flags: 0x%x>", PrData);
+ return OS.str();
+ case GNU_PROPERTY_X86_ISA_1_NEEDED:
+ case GNU_PROPERTY_X86_ISA_1_USED:
+ OS << "x86 ISA "
+ << (Type == GNU_PROPERTY_X86_ISA_1_NEEDED ? "needed: " : "used: ");
+ if (DataSize != 4) {
+ OS << format("<corrupt length: 0x%x>", DataSize);
+ return OS.str();
+ }
+ PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data());
+ if (PrData == 0) {
+ OS << "<None>";
+ return OS.str();
+ }
+ DumpBit(GNU_PROPERTY_X86_ISA_1_BASELINE, "x86-64-baseline");
+ DumpBit(GNU_PROPERTY_X86_ISA_1_V2, "x86-64-v2");
+ DumpBit(GNU_PROPERTY_X86_ISA_1_V3, "x86-64-v3");
+ DumpBit(GNU_PROPERTY_X86_ISA_1_V4, "x86-64-v4");
+ if (PrData)
+ OS << format("<unknown flags: 0x%x>", PrData);
+ return OS.str();
+ }
+}
+
+template <typename ELFT>
+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 {std::string(OSName), ABI.str(), /*IsValid=*/true};
+}
+
+static std::string getGNUBuildId(ArrayRef<uint8_t> Desc) {
+ std::string str;
+ raw_string_ostream OS(str);
+ for (uint8_t B : Desc)
+ OS << format_hex_no_prefix(B, 2);
+ return OS.str();
+}
+
+static StringRef getDescAsStringRef(ArrayRef<uint8_t> Desc) {
+ return StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size());
+}
+
+template <typename ELFT>
+static bool printGNUNote(raw_ostream &OS, uint32_t NoteType,
+ ArrayRef<uint8_t> Desc) {
+ // Return true if we were able to pretty-print the note, false otherwise.
+ switch (NoteType) {
+ default:
+ return false;
+ case ELF::NT_GNU_ABI_TAG: {
+ const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc);
+ if (!AbiTag.IsValid)
+ OS << " <corrupt GNU_ABI_TAG>";
+ else
+ OS << " OS: " << AbiTag.OSName << ", ABI: " << AbiTag.ABI;
+ break;
+ }
+ case ELF::NT_GNU_BUILD_ID: {
+ OS << " Build ID: " << getGNUBuildId(Desc);
+ break;
+ }
+ case ELF::NT_GNU_GOLD_VERSION:
+ OS << " Version: " << getDescAsStringRef(Desc);
+ break;
+ case ELF::NT_GNU_PROPERTY_TYPE_0:
+ OS << " Properties:";
+ for (const std::string &Property : getGNUPropertyList<ELFT>(Desc))
+ OS << " " << Property << "\n";
+ break;
+ }
+ OS << '\n';
+ return true;
+}
+
+template <typename ELFT>
+static bool printLLVMOMPOFFLOADNote(raw_ostream &OS, uint32_t NoteType,
+ ArrayRef<uint8_t> Desc) {
+ switch (NoteType) {
+ default:
+ return false;
+ case ELF::NT_LLVM_OPENMP_OFFLOAD_VERSION:
+ OS << " Version: " << getDescAsStringRef(Desc);
+ break;
+ case ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER:
+ OS << " Producer: " << getDescAsStringRef(Desc);
+ break;
+ case ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER_VERSION:
+ OS << " Producer version: " << getDescAsStringRef(Desc);
+ break;
+ }
+ OS << '\n';
+ return true;
+}
+
+const EnumEntry<unsigned> FreeBSDFeatureCtlFlags[] = {
+ {"ASLR_DISABLE", NT_FREEBSD_FCTL_ASLR_DISABLE},
+ {"PROTMAX_DISABLE", NT_FREEBSD_FCTL_PROTMAX_DISABLE},
+ {"STKGAP_DISABLE", NT_FREEBSD_FCTL_STKGAP_DISABLE},
+ {"WXNEEDED", NT_FREEBSD_FCTL_WXNEEDED},
+ {"LA48", NT_FREEBSD_FCTL_LA48},
+ {"ASG_DISABLE", NT_FREEBSD_FCTL_ASG_DISABLE},
+};
+
+struct FreeBSDNote {
+ std::string Type;
+ std::string Value;
+};
+
+template <typename ELFT>
+static Optional<FreeBSDNote>
+getFreeBSDNote(uint32_t NoteType, ArrayRef<uint8_t> Desc, bool IsCore) {
+ if (IsCore)
+ return None; // No pretty-printing yet.
+ switch (NoteType) {
+ case ELF::NT_FREEBSD_ABI_TAG:
+ if (Desc.size() != 4)
+ return None;
+ return FreeBSDNote{
+ "ABI tag",
+ utostr(support::endian::read32<ELFT::TargetEndianness>(Desc.data()))};
+ case ELF::NT_FREEBSD_ARCH_TAG:
+ return FreeBSDNote{"Arch tag", toStringRef(Desc).str()};
+ case ELF::NT_FREEBSD_FEATURE_CTL: {
+ if (Desc.size() != 4)
+ return None;
+ unsigned Value =
+ support::endian::read32<ELFT::TargetEndianness>(Desc.data());
+ std::string FlagsStr;
+ raw_string_ostream OS(FlagsStr);
+ printFlags(Value, makeArrayRef(FreeBSDFeatureCtlFlags), OS);
+ if (OS.str().empty())
+ OS << "0x" << utohexstr(Value);
+ else
+ OS << "(0x" << utohexstr(Value) << ")";
+ return FreeBSDNote{"Feature flags", OS.str()};
+ }
+ default:
+ return None;
+ }
+}
+
+struct AMDNote {
+ std::string Type;
+ std::string Value;
+};
+
+template <typename ELFT>
+static AMDNote getAMDNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) {
+ switch (NoteType) {
+ default:
+ return {"", ""};
+ case ELF::NT_AMD_HSA_CODE_OBJECT_VERSION: {
+ struct CodeObjectVersion {
+ uint32_t MajorVersion;
+ uint32_t MinorVersion;
+ };
+ if (Desc.size() != sizeof(CodeObjectVersion))
+ return {"AMD HSA Code Object Version",
+ "Invalid AMD HSA Code Object Version"};
+ std::string VersionString;
+ raw_string_ostream StrOS(VersionString);
+ auto Version = reinterpret_cast<const CodeObjectVersion *>(Desc.data());
+ StrOS << "[Major: " << Version->MajorVersion
+ << ", Minor: " << Version->MinorVersion << "]";
+ return {"AMD HSA Code Object Version", VersionString};
+ }
+ case ELF::NT_AMD_HSA_HSAIL: {
+ struct HSAILProperties {
+ uint32_t HSAILMajorVersion;
+ uint32_t HSAILMinorVersion;
+ uint8_t Profile;
+ uint8_t MachineModel;
+ uint8_t DefaultFloatRound;
+ };
+ if (Desc.size() != sizeof(HSAILProperties))
+ return {"AMD HSA HSAIL Properties", "Invalid AMD HSA HSAIL Properties"};
+ auto Properties = reinterpret_cast<const HSAILProperties *>(Desc.data());
+ std::string HSAILPropetiesString;
+ raw_string_ostream StrOS(HSAILPropetiesString);
+ StrOS << "[HSAIL Major: " << Properties->HSAILMajorVersion
+ << ", HSAIL Minor: " << Properties->HSAILMinorVersion
+ << ", Profile: " << uint32_t(Properties->Profile)
+ << ", Machine Model: " << uint32_t(Properties->MachineModel)
+ << ", Default Float Round: "
+ << uint32_t(Properties->DefaultFloatRound) << "]";
+ return {"AMD HSA HSAIL Properties", HSAILPropetiesString};
+ }
+ case ELF::NT_AMD_HSA_ISA_VERSION: {
+ struct IsaVersion {
+ uint16_t VendorNameSize;
+ uint16_t ArchitectureNameSize;
+ uint32_t Major;
+ uint32_t Minor;
+ uint32_t Stepping;
+ };
+ if (Desc.size() < sizeof(IsaVersion))
+ return {"AMD HSA ISA Version", "Invalid AMD HSA ISA Version"};
+ auto Isa = reinterpret_cast<const IsaVersion *>(Desc.data());
+ if (Desc.size() < sizeof(IsaVersion) +
+ Isa->VendorNameSize + Isa->ArchitectureNameSize ||
+ Isa->VendorNameSize == 0 || Isa->ArchitectureNameSize == 0)
+ return {"AMD HSA ISA Version", "Invalid AMD HSA ISA Version"};
+ std::string IsaString;
+ raw_string_ostream StrOS(IsaString);
+ StrOS << "[Vendor: "
+ << StringRef((const char*)Desc.data() + sizeof(IsaVersion), Isa->VendorNameSize - 1)
+ << ", Architecture: "
+ << StringRef((const char*)Desc.data() + sizeof(IsaVersion) + Isa->VendorNameSize,
+ Isa->ArchitectureNameSize - 1)
+ << ", Major: " << Isa->Major << ", Minor: " << Isa->Minor
+ << ", Stepping: " << Isa->Stepping << "]";
+ return {"AMD HSA ISA Version", IsaString};
+ }
+ case ELF::NT_AMD_HSA_METADATA: {
+ if (Desc.size() == 0)
+ return {"AMD HSA Metadata", ""};
+ return {
+ "AMD HSA Metadata",
+ std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size() - 1)};
+ }
+ case ELF::NT_AMD_HSA_ISA_NAME: {
+ if (Desc.size() == 0)
+ return {"AMD HSA ISA Name", ""};
+ return {
+ "AMD HSA ISA Name",
+ std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size())};
+ }
+ case ELF::NT_AMD_PAL_METADATA: {
+ struct PALMetadata {
+ uint32_t Key;
+ uint32_t Value;
+ };
+ if (Desc.size() % sizeof(PALMetadata) != 0)
+ return {"AMD PAL Metadata", "Invalid AMD PAL Metadata"};
+ auto Isa = reinterpret_cast<const PALMetadata *>(Desc.data());
+ std::string MetadataString;
+ raw_string_ostream StrOS(MetadataString);
+ for (size_t I = 0, E = Desc.size() / sizeof(PALMetadata); I < E; ++I) {
+ StrOS << "[" << Isa[I].Key << ": " << Isa[I].Value << "]";
+ }
+ return {"AMD PAL Metadata", MetadataString};
+ }
+ }
+}
+
+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: {
+ StringRef MsgPackString =
+ StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size());
+ msgpack::Document MsgPackDoc;
+ if (!MsgPackDoc.readFromBlob(MsgPackString, /*Multi=*/false))
+ return {"", ""};
+
+ AMDGPU::HSAMD::V3::MetadataVerifier Verifier(true);
+ std::string MetadataString;
+ if (!Verifier.verify(MsgPackDoc.getRoot()))
+ MetadataString = "Invalid AMDGPU Metadata\n";
+
+ raw_string_ostream StrOS(MetadataString);
+ if (MsgPackDoc.getRoot().isScalar()) {
+ // TODO: passing a scalar root to toYAML() asserts:
+ // (PolymorphicTraits<T>::getKind(Val) != NodeKind::Scalar &&
+ // "plain scalar documents are not supported")
+ // To avoid this crash we print the raw data instead.
+ return {"", ""};
+ }
+ MsgPackDoc.toYAML(StrOS);
+ return {"AMDGPU Metadata", StrOS.str()};
+ }
+ }
+}
+
+struct CoreFileMapping {
+ uint64_t Start, End, Offset;
+ StringRef Filename;
+};
+
+struct CoreNote {
+ uint64_t PageSize;
+ std::vector<CoreFileMapping> Mappings;
+};
+
+static Expected<CoreNote> readCoreNote(DataExtractor Desc) {
+ // Expected format of the NT_FILE note description:
+ // 1. # of file mappings (call it N)
+ // 2. Page size
+ // 3. N (start, end, offset) triples
+ // 4. N packed filenames (null delimited)
+ // Each field is an Elf_Addr, except for filenames which are char* strings.
+
+ CoreNote Ret;
+ const int Bytes = Desc.getAddressSize();
+
+ if (!Desc.isValidOffsetForAddress(2))
+ return createError("the note of size 0x" + Twine::utohexstr(Desc.size()) +
+ " is too short, expected at least 0x" +
+ Twine::utohexstr(Bytes * 2));
+ if (Desc.getData().back() != 0)
+ return createError("the note is not NUL terminated");
+
+ uint64_t DescOffset = 0;
+ uint64_t FileCount = Desc.getAddress(&DescOffset);
+ Ret.PageSize = Desc.getAddress(&DescOffset);
+
+ if (!Desc.isValidOffsetForAddress(3 * FileCount * Bytes))
+ return createError("unable to read file mappings (found " +
+ Twine(FileCount) + "): the note of size 0x" +
+ Twine::utohexstr(Desc.size()) + " is too short");
+
+ uint64_t FilenamesOffset = 0;
+ DataExtractor Filenames(
+ Desc.getData().drop_front(DescOffset + 3 * FileCount * Bytes),
+ Desc.isLittleEndian(), Desc.getAddressSize());
+
+ Ret.Mappings.resize(FileCount);
+ size_t I = 0;
+ for (CoreFileMapping &Mapping : Ret.Mappings) {
+ ++I;
+ if (!Filenames.isValidOffsetForDataOfSize(FilenamesOffset, 1))
+ return createError(
+ "unable to read the file name for the mapping with index " +
+ Twine(I) + ": the note of size 0x" + Twine::utohexstr(Desc.size()) +
+ " is truncated");
+ Mapping.Start = Desc.getAddress(&DescOffset);
+ Mapping.End = Desc.getAddress(&DescOffset);
+ Mapping.Offset = Desc.getAddress(&DescOffset);
+ Mapping.Filename = Filenames.getCStrRef(&FilenamesOffset);
+ }
+
+ return Ret;
+}
+
+template <typename ELFT>
+static void printCoreNote(raw_ostream &OS, const CoreNote &Note) {
+ // Length of "0x<address>" string.
+ const int FieldWidth = ELFT::Is64Bits ? 18 : 10;
+
+ OS << " Page size: " << format_decimal(Note.PageSize, 0) << '\n';
+ OS << " " << right_justify("Start", FieldWidth) << " "
+ << right_justify("End", FieldWidth) << " "
+ << right_justify("Page Offset", FieldWidth) << '\n';
+ for (const CoreFileMapping &Mapping : Note.Mappings) {
+ OS << " " << format_hex(Mapping.Start, FieldWidth) << " "
+ << format_hex(Mapping.End, FieldWidth) << " "
+ << format_hex(Mapping.Offset, FieldWidth) << "\n "
+ << Mapping.Filename << '\n';
+ }
+}
+
+const NoteType GenericNoteTypes[] = {
+ {ELF::NT_VERSION, "NT_VERSION (version)"},
+ {ELF::NT_ARCH, "NT_ARCH (architecture)"},
+ {ELF::NT_GNU_BUILD_ATTRIBUTE_OPEN, "OPEN"},
+ {ELF::NT_GNU_BUILD_ATTRIBUTE_FUNC, "func"},
+};
+
+const NoteType GNUNoteTypes[] = {
+ {ELF::NT_GNU_ABI_TAG, "NT_GNU_ABI_TAG (ABI version tag)"},
+ {ELF::NT_GNU_HWCAP, "NT_GNU_HWCAP (DSO-supplied software HWCAP info)"},
+ {ELF::NT_GNU_BUILD_ID, "NT_GNU_BUILD_ID (unique build ID bitstring)"},
+ {ELF::NT_GNU_GOLD_VERSION, "NT_GNU_GOLD_VERSION (gold version)"},
+ {ELF::NT_GNU_PROPERTY_TYPE_0, "NT_GNU_PROPERTY_TYPE_0 (property note)"},
+};
+
+const NoteType FreeBSDCoreNoteTypes[] = {
+ {ELF::NT_FREEBSD_THRMISC, "NT_THRMISC (thrmisc structure)"},
+ {ELF::NT_FREEBSD_PROCSTAT_PROC, "NT_PROCSTAT_PROC (proc data)"},
+ {ELF::NT_FREEBSD_PROCSTAT_FILES, "NT_PROCSTAT_FILES (files data)"},
+ {ELF::NT_FREEBSD_PROCSTAT_VMMAP, "NT_PROCSTAT_VMMAP (vmmap data)"},
+ {ELF::NT_FREEBSD_PROCSTAT_GROUPS, "NT_PROCSTAT_GROUPS (groups data)"},
+ {ELF::NT_FREEBSD_PROCSTAT_UMASK, "NT_PROCSTAT_UMASK (umask data)"},
+ {ELF::NT_FREEBSD_PROCSTAT_RLIMIT, "NT_PROCSTAT_RLIMIT (rlimit data)"},
+ {ELF::NT_FREEBSD_PROCSTAT_OSREL, "NT_PROCSTAT_OSREL (osreldate data)"},
+ {ELF::NT_FREEBSD_PROCSTAT_PSSTRINGS,
+ "NT_PROCSTAT_PSSTRINGS (ps_strings data)"},
+ {ELF::NT_FREEBSD_PROCSTAT_AUXV, "NT_PROCSTAT_AUXV (auxv data)"},
+};
+
+const NoteType FreeBSDNoteTypes[] = {
+ {ELF::NT_FREEBSD_ABI_TAG, "NT_FREEBSD_ABI_TAG (ABI version tag)"},
+ {ELF::NT_FREEBSD_NOINIT_TAG, "NT_FREEBSD_NOINIT_TAG (no .init tag)"},
+ {ELF::NT_FREEBSD_ARCH_TAG, "NT_FREEBSD_ARCH_TAG (architecture tag)"},
+ {ELF::NT_FREEBSD_FEATURE_CTL,
+ "NT_FREEBSD_FEATURE_CTL (FreeBSD feature control)"},
+};
+
+const NoteType NetBSDCoreNoteTypes[] = {
+ {ELF::NT_NETBSDCORE_PROCINFO,
+ "NT_NETBSDCORE_PROCINFO (procinfo structure)"},
+ {ELF::NT_NETBSDCORE_AUXV, "NT_NETBSDCORE_AUXV (ELF auxiliary vector data)"},
+ {ELF::NT_NETBSDCORE_LWPSTATUS, "PT_LWPSTATUS (ptrace_lwpstatus structure)"},
+};
+
+const NoteType OpenBSDCoreNoteTypes[] = {
+ {ELF::NT_OPENBSD_PROCINFO, "NT_OPENBSD_PROCINFO (procinfo structure)"},
+ {ELF::NT_OPENBSD_AUXV, "NT_OPENBSD_AUXV (ELF auxiliary vector data)"},
+ {ELF::NT_OPENBSD_REGS, "NT_OPENBSD_REGS (regular registers)"},
+ {ELF::NT_OPENBSD_FPREGS, "NT_OPENBSD_FPREGS (floating point registers)"},
+ {ELF::NT_OPENBSD_WCOOKIE, "NT_OPENBSD_WCOOKIE (window cookie)"},
+};
+
+const NoteType AMDNoteTypes[] = {
+ {ELF::NT_AMD_HSA_CODE_OBJECT_VERSION,
+ "NT_AMD_HSA_CODE_OBJECT_VERSION (AMD HSA Code Object Version)"},
+ {ELF::NT_AMD_HSA_HSAIL, "NT_AMD_HSA_HSAIL (AMD HSA HSAIL Properties)"},
+ {ELF::NT_AMD_HSA_ISA_VERSION, "NT_AMD_HSA_ISA_VERSION (AMD HSA ISA Version)"},
+ {ELF::NT_AMD_HSA_METADATA, "NT_AMD_HSA_METADATA (AMD HSA Metadata)"},
+ {ELF::NT_AMD_HSA_ISA_NAME, "NT_AMD_HSA_ISA_NAME (AMD HSA ISA Name)"},
+ {ELF::NT_AMD_PAL_METADATA, "NT_AMD_PAL_METADATA (AMD PAL Metadata)"},
+};
+
+const NoteType AMDGPUNoteTypes[] = {
+ {ELF::NT_AMDGPU_METADATA, "NT_AMDGPU_METADATA (AMDGPU Metadata)"},
+};
+
+const NoteType LLVMOMPOFFLOADNoteTypes[] = {
+ {ELF::NT_LLVM_OPENMP_OFFLOAD_VERSION,
+ "NT_LLVM_OPENMP_OFFLOAD_VERSION (image format version)"},
+ {ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER,
+ "NT_LLVM_OPENMP_OFFLOAD_PRODUCER (producing toolchain)"},
+ {ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER_VERSION,
+ "NT_LLVM_OPENMP_OFFLOAD_PRODUCER_VERSION (producing toolchain version)"},
+};
+
+const NoteType CoreNoteTypes[] = {
+ {ELF::NT_PRSTATUS, "NT_PRSTATUS (prstatus structure)"},
+ {ELF::NT_FPREGSET, "NT_FPREGSET (floating point registers)"},
+ {ELF::NT_PRPSINFO, "NT_PRPSINFO (prpsinfo structure)"},
+ {ELF::NT_TASKSTRUCT, "NT_TASKSTRUCT (task structure)"},
+ {ELF::NT_AUXV, "NT_AUXV (auxiliary vector)"},
+ {ELF::NT_PSTATUS, "NT_PSTATUS (pstatus structure)"},
+ {ELF::NT_FPREGS, "NT_FPREGS (floating point registers)"},
+ {ELF::NT_PSINFO, "NT_PSINFO (psinfo structure)"},
+ {ELF::NT_LWPSTATUS, "NT_LWPSTATUS (lwpstatus_t structure)"},
+ {ELF::NT_LWPSINFO, "NT_LWPSINFO (lwpsinfo_t structure)"},
+ {ELF::NT_WIN32PSTATUS, "NT_WIN32PSTATUS (win32_pstatus structure)"},
+
+ {ELF::NT_PPC_VMX, "NT_PPC_VMX (ppc Altivec registers)"},
+ {ELF::NT_PPC_VSX, "NT_PPC_VSX (ppc VSX registers)"},
+ {ELF::NT_PPC_TAR, "NT_PPC_TAR (ppc TAR register)"},
+ {ELF::NT_PPC_PPR, "NT_PPC_PPR (ppc PPR register)"},
+ {ELF::NT_PPC_DSCR, "NT_PPC_DSCR (ppc DSCR register)"},
+ {ELF::NT_PPC_EBB, "NT_PPC_EBB (ppc EBB registers)"},
+ {ELF::NT_PPC_PMU, "NT_PPC_PMU (ppc PMU registers)"},
+ {ELF::NT_PPC_TM_CGPR, "NT_PPC_TM_CGPR (ppc checkpointed GPR registers)"},
+ {ELF::NT_PPC_TM_CFPR,
+ "NT_PPC_TM_CFPR (ppc checkpointed floating point registers)"},
+ {ELF::NT_PPC_TM_CVMX,
+ "NT_PPC_TM_CVMX (ppc checkpointed Altivec registers)"},
+ {ELF::NT_PPC_TM_CVSX, "NT_PPC_TM_CVSX (ppc checkpointed VSX registers)"},
+ {ELF::NT_PPC_TM_SPR, "NT_PPC_TM_SPR (ppc TM special purpose registers)"},
+ {ELF::NT_PPC_TM_CTAR, "NT_PPC_TM_CTAR (ppc checkpointed TAR register)"},
+ {ELF::NT_PPC_TM_CPPR, "NT_PPC_TM_CPPR (ppc checkpointed PPR register)"},
+ {ELF::NT_PPC_TM_CDSCR, "NT_PPC_TM_CDSCR (ppc checkpointed DSCR register)"},
+
+ {ELF::NT_386_TLS, "NT_386_TLS (x86 TLS information)"},
+ {ELF::NT_386_IOPERM, "NT_386_IOPERM (x86 I/O permissions)"},
+ {ELF::NT_X86_XSTATE, "NT_X86_XSTATE (x86 XSAVE extended state)"},
+
+ {ELF::NT_S390_HIGH_GPRS, "NT_S390_HIGH_GPRS (s390 upper register halves)"},
+ {ELF::NT_S390_TIMER, "NT_S390_TIMER (s390 timer register)"},
+ {ELF::NT_S390_TODCMP, "NT_S390_TODCMP (s390 TOD comparator register)"},
+ {ELF::NT_S390_TODPREG, "NT_S390_TODPREG (s390 TOD programmable register)"},
+ {ELF::NT_S390_CTRS, "NT_S390_CTRS (s390 control registers)"},
+ {ELF::NT_S390_PREFIX, "NT_S390_PREFIX (s390 prefix register)"},
+ {ELF::NT_S390_LAST_BREAK,
+ "NT_S390_LAST_BREAK (s390 last breaking event address)"},
+ {ELF::NT_S390_SYSTEM_CALL,
+ "NT_S390_SYSTEM_CALL (s390 system call restart data)"},
+ {ELF::NT_S390_TDB, "NT_S390_TDB (s390 transaction diagnostic block)"},
+ {ELF::NT_S390_VXRS_LOW,
+ "NT_S390_VXRS_LOW (s390 vector registers 0-15 upper half)"},
+ {ELF::NT_S390_VXRS_HIGH, "NT_S390_VXRS_HIGH (s390 vector registers 16-31)"},
+ {ELF::NT_S390_GS_CB, "NT_S390_GS_CB (s390 guarded-storage registers)"},
+ {ELF::NT_S390_GS_BC,
+ "NT_S390_GS_BC (s390 guarded-storage broadcast control)"},
+
+ {ELF::NT_ARM_VFP, "NT_ARM_VFP (arm VFP registers)"},
+ {ELF::NT_ARM_TLS, "NT_ARM_TLS (AArch TLS registers)"},
+ {ELF::NT_ARM_HW_BREAK,
+ "NT_ARM_HW_BREAK (AArch hardware breakpoint registers)"},
+ {ELF::NT_ARM_HW_WATCH,
+ "NT_ARM_HW_WATCH (AArch hardware watchpoint registers)"},
+
+ {ELF::NT_FILE, "NT_FILE (mapped files)"},
+ {ELF::NT_PRXFPREG, "NT_PRXFPREG (user_xfpregs structure)"},
+ {ELF::NT_SIGINFO, "NT_SIGINFO (siginfo_t data)"},
+};
+
+template <class ELFT>
+StringRef getNoteTypeName(const typename ELFT::Note &Note, unsigned ELFType) {
+ uint32_t Type = Note.getType();
+ auto FindNote = [&](ArrayRef<NoteType> V) -> StringRef {
+ for (const NoteType &N : V)
+ if (N.ID == Type)
+ return N.Name;
+ return "";
+ };
+
+ StringRef Name = Note.getName();
+ if (Name == "GNU")
+ return FindNote(GNUNoteTypes);
+ if (Name == "FreeBSD") {
+ if (ELFType == ELF::ET_CORE) {
+ // FreeBSD also places the generic core notes in the FreeBSD namespace.
+ StringRef Result = FindNote(FreeBSDCoreNoteTypes);
+ if (!Result.empty())
+ return Result;
+ return FindNote(CoreNoteTypes);
+ } else {
+ return FindNote(FreeBSDNoteTypes);
+ }
+ }
+ if (ELFType == ELF::ET_CORE && Name.startswith("NetBSD-CORE")) {
+ StringRef Result = FindNote(NetBSDCoreNoteTypes);
+ if (!Result.empty())
+ return Result;
+ return FindNote(CoreNoteTypes);
+ }
+ if (ELFType == ELF::ET_CORE && Name.startswith("OpenBSD")) {
+ // OpenBSD also places the generic core notes in the OpenBSD namespace.
+ StringRef Result = FindNote(OpenBSDCoreNoteTypes);
+ if (!Result.empty())
+ return Result;
+ return FindNote(CoreNoteTypes);
+ }
+ if (Name == "AMD")
+ return FindNote(AMDNoteTypes);
+ if (Name == "AMDGPU")
+ return FindNote(AMDGPUNoteTypes);
+ if (Name == "LLVMOMPOFFLOAD")
+ return FindNote(LLVMOMPOFFLOADNoteTypes);
+
+ if (ELFType == ELF::ET_CORE)
+ return FindNote(CoreNoteTypes);
+ return FindNote(GenericNoteTypes);
+}
+
+template <class ELFT>
+static void printNotesHelper(
+ const ELFDumper<ELFT> &Dumper,
+ llvm::function_ref<void(Optional<StringRef>, typename ELFT::Off,
+ typename ELFT::Addr)>
+ StartNotesFn,
+ llvm::function_ref<Error(const typename ELFT::Note &, bool)> ProcessNoteFn,
+ llvm::function_ref<void()> FinishNotesFn) {
+ const ELFFile<ELFT> &Obj = Dumper.getElfObject().getELFFile();
+ bool IsCoreFile = Obj.getHeader().e_type == ELF::ET_CORE;
+
+ ArrayRef<typename ELFT::Shdr> Sections = cantFail(Obj.sections());
+ if (!IsCoreFile && !Sections.empty()) {
+ for (const typename ELFT::Shdr &S : Sections) {
+ if (S.sh_type != SHT_NOTE)
+ continue;
+ StartNotesFn(expectedToOptional(Obj.getSectionName(S)), S.sh_offset,
+ S.sh_size);
+ Error Err = Error::success();
+ size_t I = 0;
+ for (const typename ELFT::Note Note : Obj.notes(S, Err)) {
+ if (Error E = ProcessNoteFn(Note, IsCoreFile))
+ Dumper.reportUniqueWarning(
+ "unable to read note with index " + Twine(I) + " from the " +
+ describe(Obj, S) + ": " + toString(std::move(E)));
+ ++I;
+ }
+ if (Err)
+ Dumper.reportUniqueWarning("unable to read notes from the " +
+ describe(Obj, S) + ": " +
+ toString(std::move(Err)));
+ FinishNotesFn();
+ }
+ return;
+ }
+
+ Expected<ArrayRef<typename ELFT::Phdr>> PhdrsOrErr = Obj.program_headers();
+ if (!PhdrsOrErr) {
+ Dumper.reportUniqueWarning(
+ "unable to read program headers to locate the PT_NOTE segment: " +
+ toString(PhdrsOrErr.takeError()));
+ return;
+ }
+
+ for (size_t I = 0, E = (*PhdrsOrErr).size(); I != E; ++I) {
+ const typename ELFT::Phdr &P = (*PhdrsOrErr)[I];
+ if (P.p_type != PT_NOTE)
+ continue;
+ StartNotesFn(/*SecName=*/None, P.p_offset, P.p_filesz);
+ Error Err = Error::success();
+ size_t Index = 0;
+ for (const typename ELFT::Note Note : Obj.notes(P, Err)) {
+ if (Error E = ProcessNoteFn(Note, IsCoreFile))
+ Dumper.reportUniqueWarning("unable to read note with index " +
+ Twine(Index) +
+ " from the PT_NOTE segment with index " +
+ Twine(I) + ": " + toString(std::move(E)));
+ ++Index;
+ }
+ if (Err)
+ Dumper.reportUniqueWarning(
+ "unable to read notes from the PT_NOTE segment with index " +
+ Twine(I) + ": " + toString(std::move(Err)));
+ FinishNotesFn();
+ }
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printNotes() {
+ bool IsFirstHeader = true;
+ auto PrintHeader = [&](Optional<StringRef> SecName,
+ const typename ELFT::Off Offset,
+ const typename ELFT::Addr Size) {
+ // Print a newline between notes sections to match GNU readelf.
+ if (!IsFirstHeader) {
+ OS << '\n';
+ } else {
+ IsFirstHeader = false;
+ }
+
+ OS << "Displaying notes found ";
+
+ if (SecName)
+ OS << "in: " << *SecName << "\n";
+ else
+ OS << "at file offset " << format_hex(Offset, 10) << " with length "
+ << format_hex(Size, 10) << ":\n";
+
+ OS << " Owner Data size \tDescription\n";
+ };
+
+ auto ProcessNote = [&](const Elf_Note &Note, bool IsCore) -> Error {
+ StringRef Name = Note.getName();
+ ArrayRef<uint8_t> Descriptor = Note.getDesc();
+ Elf_Word Type = Note.getType();
+
+ // Print the note owner/type.
+ OS << " " << left_justify(Name, 20) << ' '
+ << format_hex(Descriptor.size(), 10) << '\t';
+
+ StringRef NoteType =
+ getNoteTypeName<ELFT>(Note, this->Obj.getHeader().e_type);
+ if (!NoteType.empty())
+ OS << NoteType << '\n';
+ else
+ OS << "Unknown note type: (" << format_hex(Type, 10) << ")\n";
+
+ // Print the description, or fallback to printing raw bytes for unknown
+ // owners/if we fail to pretty-print the contents.
+ if (Name == "GNU") {
+ if (printGNUNote<ELFT>(OS, Type, Descriptor))
+ return Error::success();
+ } else if (Name == "FreeBSD") {
+ if (Optional<FreeBSDNote> N =
+ getFreeBSDNote<ELFT>(Type, Descriptor, IsCore)) {
+ OS << " " << N->Type << ": " << N->Value << '\n';
+ return Error::success();
+ }
+ } else if (Name == "AMD") {
+ const AMDNote N = getAMDNote<ELFT>(Type, Descriptor);
+ if (!N.Type.empty()) {
+ OS << " " << N.Type << ":\n " << N.Value << '\n';
+ return Error::success();
+ }
+ } else if (Name == "AMDGPU") {
+ const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor);
+ if (!N.Type.empty()) {
+ OS << " " << N.Type << ":\n " << N.Value << '\n';
+ return Error::success();
+ }
+ } else if (Name == "LLVMOMPOFFLOAD") {
+ if (printLLVMOMPOFFLOADNote<ELFT>(OS, Type, Descriptor))
+ return Error::success();
+ } else if (Name == "CORE") {
+ if (Type == ELF::NT_FILE) {
+ DataExtractor DescExtractor(Descriptor,
+ ELFT::TargetEndianness == support::little,
+ sizeof(Elf_Addr));
+ if (Expected<CoreNote> NoteOrErr = readCoreNote(DescExtractor)) {
+ printCoreNote<ELFT>(OS, *NoteOrErr);
+ return Error::success();
+ } else {
+ return NoteOrErr.takeError();
+ }
+ }
+ }
+ if (!Descriptor.empty()) {
+ OS << " description data:";
+ for (uint8_t B : Descriptor)
+ OS << " " << format("%02x", B);
+ OS << '\n';
+ }
+ return Error::success();
+ };
+
+ printNotesHelper(*this, PrintHeader, ProcessNote, []() {});
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printELFLinkerOptions() {
+ OS << "printELFLinkerOptions not implemented!\n";
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::printDependentLibsHelper(
+ function_ref<void(const Elf_Shdr &)> OnSectionStart,
+ function_ref<void(StringRef, uint64_t)> OnLibEntry) {
+ auto Warn = [this](unsigned SecNdx, StringRef Msg) {
+ this->reportUniqueWarning("SHT_LLVM_DEPENDENT_LIBRARIES section at index " +
+ Twine(SecNdx) + " is broken: " + Msg);
+ };
+
+ unsigned I = -1;
+ for (const Elf_Shdr &Shdr : cantFail(Obj.sections())) {
+ ++I;
+ if (Shdr.sh_type != ELF::SHT_LLVM_DEPENDENT_LIBRARIES)
+ continue;
+
+ OnSectionStart(Shdr);
+
+ Expected<ArrayRef<uint8_t>> ContentsOrErr = Obj.getSectionContents(Shdr);
+ if (!ContentsOrErr) {
+ Warn(I, toString(ContentsOrErr.takeError()));
+ continue;
+ }
+
+ ArrayRef<uint8_t> Contents = *ContentsOrErr;
+ if (!Contents.empty() && Contents.back() != 0) {
+ Warn(I, "the content is not null-terminated");
+ continue;
+ }
+
+ for (const uint8_t *I = Contents.begin(), *E = Contents.end(); I < E;) {
+ StringRef Lib((const char *)I);
+ OnLibEntry(Lib, I - Contents.begin());
+ I += Lib.size() + 1;
+ }
+ }
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::forEachRelocationDo(
+ const Elf_Shdr &Sec, bool RawRelr,
+ llvm::function_ref<void(const Relocation<ELFT> &, unsigned,
+ const Elf_Shdr &, const Elf_Shdr *)>
+ RelRelaFn,
+ llvm::function_ref<void(const Elf_Relr &)> RelrFn) {
+ auto Warn = [&](Error &&E,
+ const Twine &Prefix = "unable to read relocations from") {
+ this->reportUniqueWarning(Prefix + " " + describe(Sec) + ": " +
+ toString(std::move(E)));
+ };
+
+ // SHT_RELR/SHT_ANDROID_RELR sections do not have an associated symbol table.
+ // For them we should not treat the value of the sh_link field as an index of
+ // a symbol table.
+ const Elf_Shdr *SymTab;
+ if (Sec.sh_type != ELF::SHT_RELR && Sec.sh_type != ELF::SHT_ANDROID_RELR) {
+ Expected<const Elf_Shdr *> SymTabOrErr = Obj.getSection(Sec.sh_link);
+ if (!SymTabOrErr) {
+ Warn(SymTabOrErr.takeError(), "unable to locate a symbol table for");
+ return;
+ }
+ SymTab = *SymTabOrErr;
+ }
+
+ unsigned RelNdx = 0;
+ const bool IsMips64EL = this->Obj.isMips64EL();
+ switch (Sec.sh_type) {
+ case ELF::SHT_REL:
+ if (Expected<Elf_Rel_Range> RangeOrErr = Obj.rels(Sec)) {
+ for (const Elf_Rel &R : *RangeOrErr)
+ RelRelaFn(Relocation<ELFT>(R, IsMips64EL), RelNdx++, Sec, SymTab);
+ } else {
+ Warn(RangeOrErr.takeError());
+ }
+ break;
+ case ELF::SHT_RELA:
+ if (Expected<Elf_Rela_Range> RangeOrErr = Obj.relas(Sec)) {
+ for (const Elf_Rela &R : *RangeOrErr)
+ RelRelaFn(Relocation<ELFT>(R, IsMips64EL), RelNdx++, Sec, SymTab);
+ } else {
+ Warn(RangeOrErr.takeError());
+ }
+ break;
+ case ELF::SHT_RELR:
+ case ELF::SHT_ANDROID_RELR: {
+ Expected<Elf_Relr_Range> RangeOrErr = Obj.relrs(Sec);
+ if (!RangeOrErr) {
+ Warn(RangeOrErr.takeError());
+ break;
+ }
+ if (RawRelr) {
+ for (const Elf_Relr &R : *RangeOrErr)
+ RelrFn(R);
+ break;
+ }
+
+ for (const Elf_Rel &R : Obj.decode_relrs(*RangeOrErr))
+ RelRelaFn(Relocation<ELFT>(R, IsMips64EL), RelNdx++, Sec,
+ /*SymTab=*/nullptr);
+ break;
+ }
+ case ELF::SHT_ANDROID_REL:
+ case ELF::SHT_ANDROID_RELA:
+ if (Expected<std::vector<Elf_Rela>> RelasOrErr = Obj.android_relas(Sec)) {
+ for (const Elf_Rela &R : *RelasOrErr)
+ RelRelaFn(Relocation<ELFT>(R, IsMips64EL), RelNdx++, Sec, SymTab);
+ } else {
+ Warn(RelasOrErr.takeError());
+ }
+ break;
+ }
+}
+
+template <class ELFT>
+StringRef ELFDumper<ELFT>::getPrintableSectionName(const Elf_Shdr &Sec) const {
+ StringRef Name = "<?>";
+ if (Expected<StringRef> SecNameOrErr =
+ Obj.getSectionName(Sec, this->WarningHandler))
+ Name = *SecNameOrErr;
+ else
+ this->reportUniqueWarning("unable to get the name of " + describe(Sec) +
+ ": " + toString(SecNameOrErr.takeError()));
+ return Name;
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printDependentLibs() {
+ bool SectionStarted = false;
+ struct NameOffset {
+ StringRef Name;
+ uint64_t Offset;
+ };
+ std::vector<NameOffset> SecEntries;
+ NameOffset Current;
+ auto PrintSection = [&]() {
+ OS << "Dependent libraries section " << Current.Name << " at offset "
+ << format_hex(Current.Offset, 1) << " contains " << SecEntries.size()
+ << " entries:\n";
+ for (NameOffset Entry : SecEntries)
+ OS << " [" << format("%6" PRIx64, Entry.Offset) << "] " << Entry.Name
+ << "\n";
+ OS << "\n";
+ SecEntries.clear();
+ };
+
+ auto OnSectionStart = [&](const Elf_Shdr &Shdr) {
+ if (SectionStarted)
+ PrintSection();
+ SectionStarted = true;
+ Current.Offset = Shdr.sh_offset;
+ Current.Name = this->getPrintableSectionName(Shdr);
+ };
+ auto OnLibEntry = [&](StringRef Lib, uint64_t Offset) {
+ SecEntries.push_back(NameOffset{Lib, Offset});
+ };
+
+ this->printDependentLibsHelper(OnSectionStart, OnLibEntry);
+ if (SectionStarted)
+ PrintSection();
+}
+
+template <class ELFT>
+SmallVector<uint32_t> ELFDumper<ELFT>::getSymbolIndexesForFunctionAddress(
+ uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec) {
+ SmallVector<uint32_t> SymbolIndexes;
+ if (!this->AddressToIndexMap.hasValue()) {
+ // Populate the address to index map upon the first invocation of this
+ // function.
+ this->AddressToIndexMap.emplace();
+ if (this->DotSymtabSec) {
+ if (Expected<Elf_Sym_Range> SymsOrError =
+ Obj.symbols(this->DotSymtabSec)) {
+ uint32_t Index = (uint32_t)-1;
+ for (const Elf_Sym &Sym : *SymsOrError) {
+ ++Index;
+
+ if (Sym.st_shndx == ELF::SHN_UNDEF || Sym.getType() != ELF::STT_FUNC)
+ continue;
+
+ Expected<uint64_t> SymAddrOrErr =
+ ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress();
+ if (!SymAddrOrErr) {
+ std::string Name = this->getStaticSymbolName(Index);
+ reportUniqueWarning("unable to get address of symbol '" + Name +
+ "': " + toString(SymAddrOrErr.takeError()));
+ return SymbolIndexes;
+ }
+
+ (*this->AddressToIndexMap)[*SymAddrOrErr].push_back(Index);
+ }
+ } else {
+ reportUniqueWarning("unable to read the symbol table: " +
+ toString(SymsOrError.takeError()));
+ }
+ }
+ }
+
+ auto Symbols = this->AddressToIndexMap->find(SymValue);
+ if (Symbols == this->AddressToIndexMap->end())
+ return SymbolIndexes;
+
+ for (uint32_t Index : Symbols->second) {
+ // Check if the symbol is in the right section. FunctionSec == None
+ // means "any section".
+ if (FunctionSec) {
+ const Elf_Sym &Sym = *cantFail(Obj.getSymbol(this->DotSymtabSec, Index));
+ if (Expected<const Elf_Shdr *> SecOrErr =
+ Obj.getSection(Sym, this->DotSymtabSec,
+ this->getShndxTable(this->DotSymtabSec))) {
+ if (*FunctionSec != *SecOrErr)
+ continue;
+ } else {
+ std::string Name = this->getStaticSymbolName(Index);
+ // Note: it is impossible to trigger this error currently, it is
+ // untested.
+ reportUniqueWarning("unable to get section of symbol '" + Name +
+ "': " + toString(SecOrErr.takeError()));
+ return SymbolIndexes;
+ }
+ }
+
+ SymbolIndexes.push_back(Index);
+ }
+
+ return SymbolIndexes;
+}
+
+template <class ELFT>
+bool ELFDumper<ELFT>::printFunctionStackSize(
+ uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec,
+ const Elf_Shdr &StackSizeSec, DataExtractor Data, uint64_t *Offset) {
+ SmallVector<uint32_t> FuncSymIndexes =
+ this->getSymbolIndexesForFunctionAddress(SymValue, FunctionSec);
+ if (FuncSymIndexes.empty())
+ reportUniqueWarning(
+ "could not identify function symbol for stack size entry in " +
+ describe(StackSizeSec));
+
+ // Extract the size. The expectation is that Offset is pointing to the right
+ // place, i.e. past the function address.
+ Error Err = Error::success();
+ uint64_t StackSize = Data.getULEB128(Offset, &Err);
+ if (Err) {
+ reportUniqueWarning("could not extract a valid stack size from " +
+ describe(StackSizeSec) + ": " +
+ toString(std::move(Err)));
+ return false;
+ }
+
+ if (FuncSymIndexes.empty()) {
+ printStackSizeEntry(StackSize, {"?"});
+ } else {
+ SmallVector<std::string> FuncSymNames;
+ for (uint32_t Index : FuncSymIndexes)
+ FuncSymNames.push_back(this->getStaticSymbolName(Index));
+ printStackSizeEntry(StackSize, FuncSymNames);
+ }
+
+ return true;
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printStackSizeEntry(uint64_t Size,
+ ArrayRef<std::string> FuncNames) {
+ OS.PadToColumn(2);
+ OS << format_decimal(Size, 11);
+ OS.PadToColumn(18);
+
+ OS << join(FuncNames.begin(), FuncNames.end(), ", ") << "\n";
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::printStackSize(const Relocation<ELFT> &R,
+ const Elf_Shdr &RelocSec, unsigned Ndx,
+ const Elf_Shdr *SymTab,
+ const Elf_Shdr *FunctionSec,
+ const Elf_Shdr &StackSizeSec,
+ const RelocationResolver &Resolver,
+ DataExtractor Data) {
+ // This function ignores potentially erroneous input, unless it is directly
+ // related to stack size reporting.
+ const Elf_Sym *Sym = nullptr;
+ Expected<RelSymbol<ELFT>> TargetOrErr = this->getRelocationTarget(R, SymTab);
+ if (!TargetOrErr)
+ reportUniqueWarning("unable to get the target of relocation with index " +
+ Twine(Ndx) + " in " + describe(RelocSec) + ": " +
+ toString(TargetOrErr.takeError()));
+ else
+ Sym = TargetOrErr->Sym;
+
+ uint64_t RelocSymValue = 0;
+ if (Sym) {
+ Expected<const Elf_Shdr *> SectionOrErr =
+ this->Obj.getSection(*Sym, SymTab, this->getShndxTable(SymTab));
+ if (!SectionOrErr) {
+ reportUniqueWarning(
+ "cannot identify the section for relocation symbol '" +
+ (*TargetOrErr).Name + "': " + toString(SectionOrErr.takeError()));
+ } else if (*SectionOrErr != FunctionSec) {
+ reportUniqueWarning("relocation symbol '" + (*TargetOrErr).Name +
+ "' is not in the expected section");
+ // Pretend that the symbol is in the correct section and report its
+ // stack size anyway.
+ FunctionSec = *SectionOrErr;
+ }
+
+ RelocSymValue = Sym->st_value;
+ }
+
+ uint64_t Offset = R.Offset;
+ if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(Elf_Addr) + 1)) {
+ reportUniqueWarning("found invalid relocation offset (0x" +
+ Twine::utohexstr(Offset) + ") into " +
+ describe(StackSizeSec) +
+ " while trying to extract a stack size entry");
+ return;
+ }
+
+ uint64_t SymValue =
+ Resolver(R.Type, Offset, RelocSymValue, Data.getAddress(&Offset),
+ R.Addend.getValueOr(0));
+ this->printFunctionStackSize(SymValue, FunctionSec, StackSizeSec, Data,
+ &Offset);
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::printNonRelocatableStackSizes(
+ std::function<void()> PrintHeader) {
+ // This function ignores potentially erroneous input, unless it is directly
+ // related to stack size reporting.
+ for (const Elf_Shdr &Sec : cantFail(Obj.sections())) {
+ if (this->getPrintableSectionName(Sec) != ".stack_sizes")
+ continue;
+ PrintHeader();
+ ArrayRef<uint8_t> Contents =
+ unwrapOrError(this->FileName, Obj.getSectionContents(Sec));
+ DataExtractor Data(Contents, Obj.isLE(), sizeof(Elf_Addr));
+ uint64_t Offset = 0;
+ while (Offset < Contents.size()) {
+ // The function address is followed by a ULEB representing the stack
+ // size. Check for an extra byte before we try to process the entry.
+ if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(Elf_Addr) + 1)) {
+ reportUniqueWarning(
+ describe(Sec) +
+ " ended while trying to extract a stack size entry");
+ break;
+ }
+ uint64_t SymValue = Data.getAddress(&Offset);
+ if (!printFunctionStackSize(SymValue, /*FunctionSec=*/None, Sec, Data,
+ &Offset))
+ break;
+ }
+ }
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::getSectionAndRelocations(
+ std::function<bool(const Elf_Shdr &)> IsMatch,
+ llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> &SecToRelocMap) {
+ for (const Elf_Shdr &Sec : cantFail(Obj.sections())) {
+ if (IsMatch(Sec))
+ if (SecToRelocMap.insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr))
+ .second)
+ continue;
+
+ if (Sec.sh_type != ELF::SHT_RELA && Sec.sh_type != ELF::SHT_REL)
+ continue;
+
+ Expected<const Elf_Shdr *> RelSecOrErr = Obj.getSection(Sec.sh_info);
+ if (!RelSecOrErr) {
+ reportUniqueWarning(describe(Sec) +
+ ": failed to get a relocated section: " +
+ toString(RelSecOrErr.takeError()));
+ continue;
+ }
+ const Elf_Shdr *ContentsSec = *RelSecOrErr;
+ if (IsMatch(*ContentsSec))
+ SecToRelocMap[ContentsSec] = &Sec;
+ }
+}
+
+template <class ELFT>
+void ELFDumper<ELFT>::printRelocatableStackSizes(
+ std::function<void()> PrintHeader) {
+ // Build a map between stack size sections and their corresponding relocation
+ // sections.
+ llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> StackSizeRelocMap;
+ auto IsMatch = [&](const Elf_Shdr &Sec) -> bool {
+ StringRef SectionName;
+ if (Expected<StringRef> NameOrErr = Obj.getSectionName(Sec))
+ SectionName = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ return SectionName == ".stack_sizes";
+ };
+ getSectionAndRelocations(IsMatch, StackSizeRelocMap);
+
+ for (const auto &StackSizeMapEntry : StackSizeRelocMap) {
+ PrintHeader();
+ const Elf_Shdr *StackSizesELFSec = StackSizeMapEntry.first;
+ const Elf_Shdr *RelocSec = StackSizeMapEntry.second;
+
+ // Warn about stack size sections without a relocation section.
+ if (!RelocSec) {
+ reportWarning(createError(".stack_sizes (" + describe(*StackSizesELFSec) +
+ ") does not have a corresponding "
+ "relocation section"),
+ FileName);
+ continue;
+ }
+
+ // A .stack_sizes section header's sh_link field is supposed to point
+ // to the section that contains the functions whose stack sizes are
+ // described in it.
+ const Elf_Shdr *FunctionSec = unwrapOrError(
+ this->FileName, Obj.getSection(StackSizesELFSec->sh_link));
+
+ SupportsRelocation IsSupportedFn;
+ RelocationResolver Resolver;
+ std::tie(IsSupportedFn, Resolver) = getRelocationResolver(this->ObjF);
+ ArrayRef<uint8_t> Contents =
+ unwrapOrError(this->FileName, Obj.getSectionContents(*StackSizesELFSec));
+ DataExtractor Data(Contents, Obj.isLE(), sizeof(Elf_Addr));
+
+ forEachRelocationDo(
+ *RelocSec, /*RawRelr=*/false,
+ [&](const Relocation<ELFT> &R, unsigned Ndx, const Elf_Shdr &Sec,
+ const Elf_Shdr *SymTab) {
+ if (!IsSupportedFn || !IsSupportedFn(R.Type)) {
+ reportUniqueWarning(
+ describe(*RelocSec) +
+ " contains an unsupported relocation with index " + Twine(Ndx) +
+ ": " + Obj.getRelocationTypeName(R.Type));
+ return;
+ }
+
+ this->printStackSize(R, *RelocSec, Ndx, SymTab, FunctionSec,
+ *StackSizesELFSec, Resolver, Data);
+ },
+ [](const Elf_Relr &) {
+ llvm_unreachable("can't get here, because we only support "
+ "SHT_REL/SHT_RELA sections");
+ });
+ }
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printStackSizes() {
+ bool HeaderHasBeenPrinted = false;
+ auto PrintHeader = [&]() {
+ if (HeaderHasBeenPrinted)
+ return;
+ OS << "\nStack Sizes:\n";
+ OS.PadToColumn(9);
+ OS << "Size";
+ OS.PadToColumn(18);
+ OS << "Functions\n";
+ HeaderHasBeenPrinted = true;
+ };
+
+ // For non-relocatable objects, look directly for sections whose name starts
+ // with .stack_sizes and process the contents.
+ if (this->Obj.getHeader().e_type == ELF::ET_REL)
+ this->printRelocatableStackSizes(PrintHeader);
+ else
+ this->printNonRelocatableStackSizes(PrintHeader);
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) {
+ size_t Bias = ELFT::Is64Bits ? 8 : 0;
+ auto PrintEntry = [&](const Elf_Addr *E, StringRef Purpose) {
+ OS.PadToColumn(2);
+ OS << format_hex_no_prefix(Parser.getGotAddress(E), 8 + Bias);
+ OS.PadToColumn(11 + Bias);
+ OS << format_decimal(Parser.getGotOffset(E), 6) << "(gp)";
+ OS.PadToColumn(22 + Bias);
+ OS << format_hex_no_prefix(*E, 8 + Bias);
+ OS.PadToColumn(31 + 2 * Bias);
+ OS << Purpose << "\n";
+ };
+
+ OS << (Parser.IsStatic ? "Static GOT:\n" : "Primary GOT:\n");
+ OS << " Canonical gp value: "
+ << format_hex_no_prefix(Parser.getGp(), 8 + Bias) << "\n\n";
+
+ OS << " Reserved entries:\n";
+ if (ELFT::Is64Bits)
+ OS << " Address Access Initial Purpose\n";
+ else
+ OS << " Address Access Initial Purpose\n";
+ PrintEntry(Parser.getGotLazyResolver(), "Lazy resolver");
+ if (Parser.getGotModulePointer())
+ PrintEntry(Parser.getGotModulePointer(), "Module pointer (GNU extension)");
+
+ if (!Parser.getLocalEntries().empty()) {
+ OS << "\n";
+ OS << " Local entries:\n";
+ if (ELFT::Is64Bits)
+ OS << " Address Access Initial\n";
+ else
+ OS << " Address Access Initial\n";
+ for (auto &E : Parser.getLocalEntries())
+ PrintEntry(&E, "");
+ }
+
+ if (Parser.IsStatic)
+ return;
+
+ if (!Parser.getGlobalEntries().empty()) {
+ OS << "\n";
+ OS << " Global entries:\n";
+ if (ELFT::Is64Bits)
+ OS << " Address Access Initial Sym.Val."
+ << " Type Ndx Name\n";
+ else
+ OS << " Address Access Initial Sym.Val. Type Ndx Name\n";
+
+ DataRegion<Elf_Word> ShndxTable(
+ (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end());
+ for (auto &E : Parser.getGlobalEntries()) {
+ const Elf_Sym &Sym = *Parser.getGotSym(&E);
+ const Elf_Sym &FirstSym = this->dynamic_symbols()[0];
+ std::string SymName = this->getFullSymbolName(
+ Sym, &Sym - &FirstSym, ShndxTable, this->DynamicStringTable, false);
+
+ OS.PadToColumn(2);
+ OS << to_string(format_hex_no_prefix(Parser.getGotAddress(&E), 8 + Bias));
+ OS.PadToColumn(11 + Bias);
+ OS << to_string(format_decimal(Parser.getGotOffset(&E), 6)) + "(gp)";
+ OS.PadToColumn(22 + Bias);
+ OS << to_string(format_hex_no_prefix(E, 8 + Bias));
+ OS.PadToColumn(31 + 2 * Bias);
+ OS << to_string(format_hex_no_prefix(Sym.st_value, 8 + Bias));
+ OS.PadToColumn(40 + 3 * Bias);
+ OS << enumToString(Sym.getType(), makeArrayRef(ElfSymbolTypes));
+ OS.PadToColumn(48 + 3 * Bias);
+ OS << getSymbolSectionNdx(Sym, &Sym - this->dynamic_symbols().begin(),
+ ShndxTable);
+ OS.PadToColumn(52 + 3 * Bias);
+ OS << SymName << "\n";
+ }
+ }
+
+ if (!Parser.getOtherEntries().empty())
+ OS << "\n Number of TLS and multi-GOT entries "
+ << Parser.getOtherEntries().size() << "\n";
+}
+
+template <class ELFT>
+void GNUELFDumper<ELFT>::printMipsPLT(const MipsGOTParser<ELFT> &Parser) {
+ size_t Bias = ELFT::Is64Bits ? 8 : 0;
+ auto PrintEntry = [&](const Elf_Addr *E, StringRef Purpose) {
+ OS.PadToColumn(2);
+ OS << format_hex_no_prefix(Parser.getPltAddress(E), 8 + Bias);
+ OS.PadToColumn(11 + Bias);
+ OS << format_hex_no_prefix(*E, 8 + Bias);
+ OS.PadToColumn(20 + 2 * Bias);
+ OS << Purpose << "\n";
+ };
+
+ OS << "PLT GOT:\n\n";
+
+ OS << " Reserved entries:\n";
+ OS << " Address Initial Purpose\n";
+ PrintEntry(Parser.getPltLazyResolver(), "PLT lazy resolver");
+ if (Parser.getPltModulePointer())
+ PrintEntry(Parser.getPltModulePointer(), "Module pointer");
+
+ if (!Parser.getPltEntries().empty()) {
+ OS << "\n";
+ OS << " Entries:\n";
+ OS << " Address Initial Sym.Val. Type Ndx Name\n";
+ DataRegion<Elf_Word> ShndxTable(
+ (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end());
+ for (auto &E : Parser.getPltEntries()) {
+ const Elf_Sym &Sym = *Parser.getPltSym(&E);
+ const Elf_Sym &FirstSym = *cantFail(
+ this->Obj.template getEntry<Elf_Sym>(*Parser.getPltSymTable(), 0));
+ std::string SymName = this->getFullSymbolName(
+ Sym, &Sym - &FirstSym, ShndxTable, this->DynamicStringTable, false);
+
+ OS.PadToColumn(2);
+ OS << to_string(format_hex_no_prefix(Parser.getPltAddress(&E), 8 + Bias));
+ OS.PadToColumn(11 + Bias);
+ OS << to_string(format_hex_no_prefix(E, 8 + Bias));
+ OS.PadToColumn(20 + 2 * Bias);
+ OS << to_string(format_hex_no_prefix(Sym.st_value, 8 + Bias));
+ OS.PadToColumn(29 + 3 * Bias);
+ OS << enumToString(Sym.getType(), makeArrayRef(ElfSymbolTypes));
+ OS.PadToColumn(37 + 3 * Bias);
+ OS << getSymbolSectionNdx(Sym, &Sym - this->dynamic_symbols().begin(),
+ ShndxTable);
+ OS.PadToColumn(41 + 3 * Bias);
+ OS << SymName << "\n";
+ }
+ }
+}
+
+template <class ELFT>
+Expected<const Elf_Mips_ABIFlags<ELFT> *>
+getMipsAbiFlagsSection(const ELFDumper<ELFT> &Dumper) {
+ const typename ELFT::Shdr *Sec = Dumper.findSectionByName(".MIPS.abiflags");
+ if (Sec == nullptr)
+ return nullptr;
+
+ constexpr StringRef ErrPrefix = "unable to read the .MIPS.abiflags section: ";
+ Expected<ArrayRef<uint8_t>> DataOrErr =
+ Dumper.getElfObject().getELFFile().getSectionContents(*Sec);
+ if (!DataOrErr)
+ return createError(ErrPrefix + toString(DataOrErr.takeError()));
+
+ if (DataOrErr->size() != sizeof(Elf_Mips_ABIFlags<ELFT>))
+ return createError(ErrPrefix + "it has a wrong size (" +
+ Twine(DataOrErr->size()) + ")");
+ return reinterpret_cast<const Elf_Mips_ABIFlags<ELFT> *>(DataOrErr->data());
+}
+
+template <class ELFT> void GNUELFDumper<ELFT>::printMipsABIFlags() {
+ const Elf_Mips_ABIFlags<ELFT> *Flags = nullptr;
+ if (Expected<const Elf_Mips_ABIFlags<ELFT> *> SecOrErr =
+ getMipsAbiFlagsSection(*this))
+ Flags = *SecOrErr;
+ else
+ this->reportUniqueWarning(SecOrErr.takeError());
+ if (!Flags)
+ return;
+
+ OS << "MIPS ABI Flags Version: " << Flags->version << "\n\n";
+ OS << "ISA: MIPS" << int(Flags->isa_level);
+ if (Flags->isa_rev > 1)
+ OS << "r" << int(Flags->isa_rev);
+ OS << "\n";
+ OS << "GPR size: " << getMipsRegisterSize(Flags->gpr_size) << "\n";
+ OS << "CPR1 size: " << getMipsRegisterSize(Flags->cpr1_size) << "\n";
+ OS << "CPR2 size: " << getMipsRegisterSize(Flags->cpr2_size) << "\n";
+ OS << "FP ABI: "
+ << enumToString(Flags->fp_abi, makeArrayRef(ElfMipsFpABIType)) << "\n";
+ OS << "ISA Extension: "
+ << enumToString(Flags->isa_ext, makeArrayRef(ElfMipsISAExtType)) << "\n";
+ if (Flags->ases == 0)
+ OS << "ASEs: None\n";
+ else
+ // FIXME: Print each flag on a separate line.
+ OS << "ASEs: " << printFlags(Flags->ases, makeArrayRef(ElfMipsASEFlags))
+ << "\n";
+ OS << "FLAGS 1: " << format_hex_no_prefix(Flags->flags1, 8, false) << "\n";
+ OS << "FLAGS 2: " << format_hex_no_prefix(Flags->flags2, 8, false) << "\n";
+ OS << "\n";
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printFileHeaders() {
+ const Elf_Ehdr &E = this->Obj.getHeader();
+ {
+ DictScope D(W, "ElfHeader");
+ {
+ DictScope D(W, "Ident");
+ W.printBinary("Magic", makeArrayRef(E.e_ident).slice(ELF::EI_MAG0, 4));
+ W.printEnum("Class", E.e_ident[ELF::EI_CLASS], makeArrayRef(ElfClass));
+ W.printEnum("DataEncoding", E.e_ident[ELF::EI_DATA],
+ makeArrayRef(ElfDataEncoding));
+ W.printNumber("FileVersion", E.e_ident[ELF::EI_VERSION]);
+
+ auto OSABI = makeArrayRef(ElfOSABI);
+ if (E.e_ident[ELF::EI_OSABI] >= ELF::ELFOSABI_FIRST_ARCH &&
+ E.e_ident[ELF::EI_OSABI] <= ELF::ELFOSABI_LAST_ARCH) {
+ switch (E.e_machine) {
+ case ELF::EM_AMDGPU:
+ OSABI = makeArrayRef(AMDGPUElfOSABI);
+ break;
+ case ELF::EM_ARM:
+ OSABI = makeArrayRef(ARMElfOSABI);
+ break;
+ case ELF::EM_TI_C6000:
+ OSABI = makeArrayRef(C6000ElfOSABI);
+ break;
+ }
+ }
+ W.printEnum("OS/ABI", E.e_ident[ELF::EI_OSABI], OSABI);
+ W.printNumber("ABIVersion", E.e_ident[ELF::EI_ABIVERSION]);
+ W.printBinary("Unused", makeArrayRef(E.e_ident).slice(ELF::EI_PAD));
+ }
+
+ std::string TypeStr;
+ if (const EnumEntry<unsigned> *Ent = getObjectFileEnumEntry(E.e_type)) {
+ TypeStr = Ent->Name.str();
+ } else {
+ if (E.e_type >= ET_LOPROC)
+ TypeStr = "Processor Specific";
+ else if (E.e_type >= ET_LOOS)
+ TypeStr = "OS Specific";
+ else
+ TypeStr = "Unknown";
+ }
+ W.printString("Type", TypeStr + " (0x" + to_hexString(E.e_type) + ")");
+
+ W.printEnum("Machine", E.e_machine, makeArrayRef(ElfMachineType));
+ W.printNumber("Version", E.e_version);
+ W.printHex("Entry", E.e_entry);
+ W.printHex("ProgramHeaderOffset", E.e_phoff);
+ W.printHex("SectionHeaderOffset", E.e_shoff);
+ if (E.e_machine == EM_MIPS)
+ W.printFlags("Flags", 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_AMDGPU) {
+ switch (E.e_ident[ELF::EI_ABIVERSION]) {
+ default:
+ W.printHex("Flags", E.e_flags);
+ break;
+ case 0:
+ // ELFOSABI_AMDGPU_PAL, ELFOSABI_AMDGPU_MESA3D support *_V3 flags.
+ LLVM_FALLTHROUGH;
+ case ELF::ELFABIVERSION_AMDGPU_HSA_V3:
+ W.printFlags("Flags", E.e_flags,
+ makeArrayRef(ElfHeaderAMDGPUFlagsABIVersion3),
+ unsigned(ELF::EF_AMDGPU_MACH));
+ break;
+ case ELF::ELFABIVERSION_AMDGPU_HSA_V4:
+ case ELF::ELFABIVERSION_AMDGPU_HSA_V5:
+ W.printFlags("Flags", E.e_flags,
+ makeArrayRef(ElfHeaderAMDGPUFlagsABIVersion4),
+ unsigned(ELF::EF_AMDGPU_MACH),
+ unsigned(ELF::EF_AMDGPU_FEATURE_XNACK_V4),
+ unsigned(ELF::EF_AMDGPU_FEATURE_SRAMECC_V4));
+ break;
+ }
+ } else if (E.e_machine == EM_RISCV)
+ W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderRISCVFlags));
+ else if (E.e_machine == EM_AVR)
+ W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderAVRFlags),
+ unsigned(ELF::EF_AVR_ARCH_MASK));
+ else
+ W.printFlags("Flags", E.e_flags);
+ W.printNumber("HeaderSize", E.e_ehsize);
+ W.printNumber("ProgramHeaderEntrySize", E.e_phentsize);
+ W.printNumber("ProgramHeaderCount", E.e_phnum);
+ W.printNumber("SectionHeaderEntrySize", E.e_shentsize);
+ W.printString("SectionHeaderCount",
+ getSectionHeadersNumString(this->Obj, this->FileName));
+ W.printString("StringTableSectionIndex",
+ getSectionHeaderTableIndexString(this->Obj, this->FileName));
+ }
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printGroupSections() {
+ DictScope Lists(W, "Groups");
+ std::vector<GroupSection> V = this->getGroups();
+ DenseMap<uint64_t, const GroupSection *> Map = mapSectionsToGroups(V);
+ for (const GroupSection &G : V) {
+ DictScope D(W, "Group");
+ W.printNumber("Name", G.Name, G.ShName);
+ W.printNumber("Index", G.Index);
+ W.printNumber("Link", G.Link);
+ W.printNumber("Info", G.Info);
+ W.printHex("Type", getGroupType(G.Type), G.Type);
+ W.startLine() << "Signature: " << G.Signature << "\n";
+
+ ListScope L(W, "Section(s) in group");
+ for (const GroupMember &GM : G.Members) {
+ const GroupSection *MainGroup = Map[GM.Index];
+ if (MainGroup != &G)
+ this->reportUniqueWarning(
+ "section with index " + Twine(GM.Index) +
+ ", included in the group section with index " +
+ Twine(MainGroup->Index) +
+ ", was also found in the group section with index " +
+ Twine(G.Index));
+ W.startLine() << GM.Name << " (" << GM.Index << ")\n";
+ }
+ }
+
+ if (V.empty())
+ W.startLine() << "There are no group sections in the file.\n";
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printRelocations() {
+ ListScope D(W, "Relocations");
+
+ for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) {
+ if (!isRelocationSec<ELFT>(Sec))
+ continue;
+
+ StringRef Name = this->getPrintableSectionName(Sec);
+ unsigned SecNdx = &Sec - &cantFail(this->Obj.sections()).front();
+ W.startLine() << "Section (" << SecNdx << ") " << Name << " {\n";
+ W.indent();
+ this->printRelocationsHelper(Sec);
+ W.unindent();
+ W.startLine() << "}\n";
+ }
+}
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printRelrReloc(const Elf_Relr &R) {
+ W.startLine() << W.hex(R) << "\n";
+}
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printRelRelaReloc(const Relocation<ELFT> &R,
+ const RelSymbol<ELFT> &RelSym) {
+ StringRef SymbolName = RelSym.Name;
+ SmallString<32> RelocName;
+ this->Obj.getRelocationTypeName(R.Type, RelocName);
+
+ if (opts::ExpandRelocs) {
+ DictScope Group(W, "Relocation");
+ W.printHex("Offset", R.Offset);
+ W.printNumber("Type", RelocName, R.Type);
+ W.printNumber("Symbol", !SymbolName.empty() ? SymbolName : "-", R.Symbol);
+ if (R.Addend)
+ W.printHex("Addend", (uintX_t)*R.Addend);
+ } else {
+ raw_ostream &OS = W.startLine();
+ OS << W.hex(R.Offset) << " " << RelocName << " "
+ << (!SymbolName.empty() ? SymbolName : "-");
+ if (R.Addend)
+ OS << " " << W.hex((uintX_t)*R.Addend);
+ OS << "\n";
+ }
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printSectionHeaders() {
+ ListScope SectionsD(W, "Sections");
+
+ int SectionIndex = -1;
+ std::vector<EnumEntry<unsigned>> FlagsList =
+ getSectionFlagsForTarget(this->Obj.getHeader().e_machine);
+ for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) {
+ DictScope SectionD(W, "Section");
+ W.printNumber("Index", ++SectionIndex);
+ W.printNumber("Name", this->getPrintableSectionName(Sec), Sec.sh_name);
+ W.printHex("Type",
+ object::getELFSectionTypeName(this->Obj.getHeader().e_machine,
+ Sec.sh_type),
+ Sec.sh_type);
+ W.printFlags("Flags", Sec.sh_flags, makeArrayRef(FlagsList));
+ W.printHex("Address", Sec.sh_addr);
+ W.printHex("Offset", Sec.sh_offset);
+ W.printNumber("Size", Sec.sh_size);
+ W.printNumber("Link", Sec.sh_link);
+ W.printNumber("Info", Sec.sh_info);
+ W.printNumber("AddressAlignment", Sec.sh_addralign);
+ W.printNumber("EntrySize", Sec.sh_entsize);
+
+ if (opts::SectionRelocations) {
+ ListScope D(W, "Relocations");
+ this->printRelocationsHelper(Sec);
+ }
+
+ if (opts::SectionSymbols) {
+ ListScope D(W, "Symbols");
+ if (this->DotSymtabSec) {
+ StringRef StrTable = unwrapOrError(
+ this->FileName,
+ this->Obj.getStringTableForSymtab(*this->DotSymtabSec));
+ ArrayRef<Elf_Word> ShndxTable = this->getShndxTable(this->DotSymtabSec);
+
+ typename ELFT::SymRange Symbols = unwrapOrError(
+ this->FileName, this->Obj.symbols(this->DotSymtabSec));
+ for (const Elf_Sym &Sym : Symbols) {
+ const Elf_Shdr *SymSec = unwrapOrError(
+ this->FileName,
+ this->Obj.getSection(Sym, this->DotSymtabSec, ShndxTable));
+ if (SymSec == &Sec)
+ printSymbol(Sym, &Sym - &Symbols[0], ShndxTable, StrTable, false,
+ false);
+ }
+ }
+ }
+
+ if (opts::SectionData && Sec.sh_type != ELF::SHT_NOBITS) {
+ ArrayRef<uint8_t> Data =
+ unwrapOrError(this->FileName, this->Obj.getSectionContents(Sec));
+ W.printBinaryBlock(
+ "SectionData",
+ StringRef(reinterpret_cast<const char *>(Data.data()), Data.size()));
+ }
+ }
+}
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printSymbolSection(
+ const Elf_Sym &Symbol, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable) const {
+ auto GetSectionSpecialType = [&]() -> Optional<StringRef> {
+ if (Symbol.isUndefined())
+ return StringRef("Undefined");
+ if (Symbol.isProcessorSpecific())
+ return StringRef("Processor Specific");
+ if (Symbol.isOSSpecific())
+ return StringRef("Operating System Specific");
+ if (Symbol.isAbsolute())
+ return StringRef("Absolute");
+ if (Symbol.isCommon())
+ return StringRef("Common");
+ if (Symbol.isReserved() && Symbol.st_shndx != SHN_XINDEX)
+ return StringRef("Reserved");
+ return None;
+ };
+
+ if (Optional<StringRef> Type = GetSectionSpecialType()) {
+ W.printHex("Section", *Type, Symbol.st_shndx);
+ return;
+ }
+
+ Expected<unsigned> SectionIndex =
+ this->getSymbolSectionIndex(Symbol, SymIndex, ShndxTable);
+ if (!SectionIndex) {
+ assert(Symbol.st_shndx == SHN_XINDEX &&
+ "getSymbolSectionIndex should only fail due to an invalid "
+ "SHT_SYMTAB_SHNDX table/reference");
+ this->reportUniqueWarning(SectionIndex.takeError());
+ W.printHex("Section", "Reserved", SHN_XINDEX);
+ return;
+ }
+
+ Expected<StringRef> SectionName =
+ this->getSymbolSectionName(Symbol, *SectionIndex);
+ if (!SectionName) {
+ // Don't report an invalid section name if the section headers are missing.
+ // In such situations, all sections will be "invalid".
+ if (!this->ObjF.sections().empty())
+ this->reportUniqueWarning(SectionName.takeError());
+ else
+ consumeError(SectionName.takeError());
+ W.printHex("Section", "<?>", *SectionIndex);
+ } else {
+ W.printHex("Section", *SectionName, *SectionIndex);
+ }
+}
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printSymbol(const Elf_Sym &Symbol, unsigned SymIndex,
+ DataRegion<Elf_Word> ShndxTable,
+ Optional<StringRef> StrTable,
+ bool IsDynamic,
+ bool /*NonVisibilityBitsUsed*/) const {
+ std::string FullSymbolName = this->getFullSymbolName(
+ Symbol, SymIndex, ShndxTable, StrTable, IsDynamic);
+ unsigned char SymbolType = Symbol.getType();
+
+ DictScope D(W, "Symbol");
+ W.printNumber("Name", FullSymbolName, Symbol.st_name);
+ W.printHex("Value", Symbol.st_value);
+ W.printNumber("Size", Symbol.st_size);
+ W.printEnum("Binding", Symbol.getBinding(), makeArrayRef(ElfSymbolBindings));
+ if (this->Obj.getHeader().e_machine == ELF::EM_AMDGPU &&
+ SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS)
+ W.printEnum("Type", SymbolType, makeArrayRef(AMDGPUSymbolTypes));
+ else
+ W.printEnum("Type", SymbolType, makeArrayRef(ElfSymbolTypes));
+ if (Symbol.st_other == 0)
+ // Usually st_other flag is zero. Do not pollute the output
+ // by flags enumeration in that case.
+ W.printNumber("Other", 0);
+ else {
+ std::vector<EnumEntry<unsigned>> SymOtherFlags(std::begin(ElfSymOtherFlags),
+ std::end(ElfSymOtherFlags));
+ if (this->Obj.getHeader().e_machine == EM_MIPS) {
+ // Someones in their infinite wisdom decided to make STO_MIPS_MIPS16
+ // flag overlapped with other ST_MIPS_xxx flags. So consider both
+ // cases separately.
+ if ((Symbol.st_other & STO_MIPS_MIPS16) == STO_MIPS_MIPS16)
+ SymOtherFlags.insert(SymOtherFlags.end(),
+ std::begin(ElfMips16SymOtherFlags),
+ std::end(ElfMips16SymOtherFlags));
+ else
+ SymOtherFlags.insert(SymOtherFlags.end(),
+ std::begin(ElfMipsSymOtherFlags),
+ std::end(ElfMipsSymOtherFlags));
+ } else if (this->Obj.getHeader().e_machine == EM_AARCH64) {
+ SymOtherFlags.insert(SymOtherFlags.end(),
+ std::begin(ElfAArch64SymOtherFlags),
+ std::end(ElfAArch64SymOtherFlags));
+ } else if (this->Obj.getHeader().e_machine == EM_RISCV) {
+ SymOtherFlags.insert(SymOtherFlags.end(),
+ std::begin(ElfRISCVSymOtherFlags),
+ std::end(ElfRISCVSymOtherFlags));
+ }
+ W.printFlags("Other", Symbol.st_other, makeArrayRef(SymOtherFlags), 0x3u);
+ }
+ printSymbolSection(Symbol, SymIndex, ShndxTable);
+}
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printSymbols(bool PrintSymbols,
+ bool PrintDynamicSymbols) {
+ if (PrintSymbols) {
+ ListScope Group(W, "Symbols");
+ this->printSymbolsHelper(false);
+ }
+ if (PrintDynamicSymbols) {
+ ListScope Group(W, "DynamicSymbols");
+ this->printSymbolsHelper(true);
+ }
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printDynamicTable() {
+ Elf_Dyn_Range Table = this->dynamic_table();
+ if (Table.empty())
+ return;
+
+ W.startLine() << "DynamicSection [ (" << Table.size() << " entries)\n";
+
+ size_t MaxTagSize = getMaxDynamicTagSize(this->Obj, Table);
+ // The "Name/Value" column should be indented from the "Type" column by N
+ // spaces, where N = MaxTagSize - length of "Type" (4) + trailing
+ // space (1) = -3.
+ W.startLine() << " Tag" << std::string(ELFT::Is64Bits ? 16 : 8, ' ')
+ << "Type" << std::string(MaxTagSize - 3, ' ') << "Name/Value\n";
+
+ std::string ValueFmt = "%-" + std::to_string(MaxTagSize) + "s ";
+ for (auto Entry : Table) {
+ uintX_t Tag = Entry.getTag();
+ std::string Value = this->getDynamicEntry(Tag, Entry.getVal());
+ W.startLine() << " " << format_hex(Tag, ELFT::Is64Bits ? 18 : 10, true)
+ << " "
+ << format(ValueFmt.c_str(),
+ this->Obj.getDynamicTagAsString(Tag).c_str())
+ << Value << "\n";
+ }
+ W.startLine() << "]\n";
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printDynamicRelocations() {
+ W.startLine() << "Dynamic Relocations {\n";
+ W.indent();
+ this->printDynamicRelocationsHelper();
+ W.unindent();
+ W.startLine() << "}\n";
+}
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printProgramHeaders(
+ bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) {
+ if (PrintProgramHeaders)
+ printProgramHeaders();
+ if (PrintSectionMapping == cl::BOU_TRUE)
+ printSectionMapping();
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printProgramHeaders() {
+ ListScope L(W, "ProgramHeaders");
+
+ Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = this->Obj.program_headers();
+ if (!PhdrsOrErr) {
+ this->reportUniqueWarning("unable to dump program headers: " +
+ toString(PhdrsOrErr.takeError()));
+ return;
+ }
+
+ for (const Elf_Phdr &Phdr : *PhdrsOrErr) {
+ DictScope P(W, "ProgramHeader");
+ StringRef Type =
+ segmentTypeToString(this->Obj.getHeader().e_machine, Phdr.p_type);
+
+ W.printHex("Type", Type.empty() ? "Unknown" : Type, Phdr.p_type);
+ W.printHex("Offset", Phdr.p_offset);
+ W.printHex("VirtualAddress", Phdr.p_vaddr);
+ W.printHex("PhysicalAddress", Phdr.p_paddr);
+ W.printNumber("FileSize", Phdr.p_filesz);
+ W.printNumber("MemSize", Phdr.p_memsz);
+ W.printFlags("Flags", Phdr.p_flags, makeArrayRef(ElfSegmentFlags));
+ W.printNumber("Alignment", Phdr.p_align);
+ }
+}
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printVersionSymbolSection(const Elf_Shdr *Sec) {
+ ListScope SS(W, "VersionSymbols");
+ if (!Sec)
+ return;
+
+ StringRef StrTable;
+ ArrayRef<Elf_Sym> Syms;
+ const Elf_Shdr *SymTabSec;
+ Expected<ArrayRef<Elf_Versym>> VerTableOrErr =
+ this->getVersionTable(*Sec, &Syms, &StrTable, &SymTabSec);
+ if (!VerTableOrErr) {
+ this->reportUniqueWarning(VerTableOrErr.takeError());
+ return;
+ }
+
+ if (StrTable.empty() || Syms.empty() || Syms.size() != VerTableOrErr->size())
+ return;
+
+ ArrayRef<Elf_Word> ShNdxTable = this->getShndxTable(SymTabSec);
+ for (size_t I = 0, E = Syms.size(); I < E; ++I) {
+ DictScope S(W, "Symbol");
+ W.printNumber("Version", (*VerTableOrErr)[I].vs_index & VERSYM_VERSION);
+ W.printString("Name",
+ this->getFullSymbolName(Syms[I], I, ShNdxTable, StrTable,
+ /*IsDynamic=*/true));
+ }
+}
+
+const EnumEntry<unsigned> SymVersionFlags[] = {
+ {"Base", "BASE", VER_FLG_BASE},
+ {"Weak", "WEAK", VER_FLG_WEAK},
+ {"Info", "INFO", VER_FLG_INFO}};
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printVersionDefinitionSection(const Elf_Shdr *Sec) {
+ ListScope SD(W, "VersionDefinitions");
+ if (!Sec)
+ return;
+
+ Expected<std::vector<VerDef>> V = this->Obj.getVersionDefinitions(*Sec);
+ if (!V) {
+ this->reportUniqueWarning(V.takeError());
+ return;
+ }
+
+ for (const VerDef &D : *V) {
+ DictScope Def(W, "Definition");
+ W.printNumber("Version", D.Version);
+ W.printFlags("Flags", D.Flags, makeArrayRef(SymVersionFlags));
+ W.printNumber("Index", D.Ndx);
+ W.printNumber("Hash", D.Hash);
+ W.printString("Name", D.Name.c_str());
+ W.printList(
+ "Predecessors", D.AuxV,
+ [](raw_ostream &OS, const VerdAux &Aux) { OS << Aux.Name.c_str(); });
+ }
+}
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printVersionDependencySection(const Elf_Shdr *Sec) {
+ ListScope SD(W, "VersionRequirements");
+ if (!Sec)
+ return;
+
+ Expected<std::vector<VerNeed>> V =
+ this->Obj.getVersionDependencies(*Sec, this->WarningHandler);
+ if (!V) {
+ this->reportUniqueWarning(V.takeError());
+ return;
+ }
+
+ for (const VerNeed &VN : *V) {
+ DictScope Entry(W, "Dependency");
+ W.printNumber("Version", VN.Version);
+ W.printNumber("Count", VN.Cnt);
+ W.printString("FileName", VN.File.c_str());
+
+ ListScope L(W, "Entries");
+ for (const VernAux &Aux : VN.AuxV) {
+ DictScope Entry(W, "Entry");
+ W.printNumber("Hash", Aux.Hash);
+ W.printFlags("Flags", Aux.Flags, makeArrayRef(SymVersionFlags));
+ W.printNumber("Index", Aux.Other);
+ W.printString("Name", Aux.Name.c_str());
+ }
+ }
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printHashHistograms() {
+ W.startLine() << "Hash Histogram not implemented!\n";
+}
+
+// Returns true if rel/rela section exists, and populates SymbolIndices.
+// Otherwise returns false.
+template <class ELFT>
+static bool getSymbolIndices(const typename ELFT::Shdr *CGRelSection,
+ const ELFFile<ELFT> &Obj,
+ const LLVMELFDumper<ELFT> *Dumper,
+ SmallVector<uint32_t, 128> &SymbolIndices) {
+ if (!CGRelSection) {
+ Dumper->reportUniqueWarning(
+ "relocation section for a call graph section doesn't exist");
+ return false;
+ }
+
+ if (CGRelSection->sh_type == SHT_REL) {
+ typename ELFT::RelRange CGProfileRel;
+ Expected<typename ELFT::RelRange> CGProfileRelOrError =
+ Obj.rels(*CGRelSection);
+ if (!CGProfileRelOrError) {
+ Dumper->reportUniqueWarning("unable to load relocations for "
+ "SHT_LLVM_CALL_GRAPH_PROFILE section: " +
+ toString(CGProfileRelOrError.takeError()));
+ return false;
+ }
+
+ CGProfileRel = *CGProfileRelOrError;
+ for (const typename ELFT::Rel &Rel : CGProfileRel)
+ SymbolIndices.push_back(Rel.getSymbol(Obj.isMips64EL()));
+ } else {
+ // MC unconditionally produces SHT_REL, but GNU strip/objcopy may convert
+ // the format to SHT_RELA
+ // (https://sourceware.org/bugzilla/show_bug.cgi?id=28035)
+ typename ELFT::RelaRange CGProfileRela;
+ Expected<typename ELFT::RelaRange> CGProfileRelaOrError =
+ Obj.relas(*CGRelSection);
+ if (!CGProfileRelaOrError) {
+ Dumper->reportUniqueWarning("unable to load relocations for "
+ "SHT_LLVM_CALL_GRAPH_PROFILE section: " +
+ toString(CGProfileRelaOrError.takeError()));
+ return false;
+ }
+
+ CGProfileRela = *CGProfileRelaOrError;
+ for (const typename ELFT::Rela &Rela : CGProfileRela)
+ SymbolIndices.push_back(Rela.getSymbol(Obj.isMips64EL()));
+ }
+
+ return true;
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() {
+ llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> SecToRelocMap;
+
+ auto IsMatch = [](const Elf_Shdr &Sec) -> bool {
+ return Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE;
+ };
+ this->getSectionAndRelocations(IsMatch, SecToRelocMap);
+
+ for (const auto &CGMapEntry : SecToRelocMap) {
+ const Elf_Shdr *CGSection = CGMapEntry.first;
+ const Elf_Shdr *CGRelSection = CGMapEntry.second;
+
+ Expected<ArrayRef<Elf_CGProfile>> CGProfileOrErr =
+ this->Obj.template getSectionContentsAsArray<Elf_CGProfile>(*CGSection);
+ if (!CGProfileOrErr) {
+ this->reportUniqueWarning(
+ "unable to load the SHT_LLVM_CALL_GRAPH_PROFILE section: " +
+ toString(CGProfileOrErr.takeError()));
+ return;
+ }
+
+ SmallVector<uint32_t, 128> SymbolIndices;
+ bool UseReloc =
+ getSymbolIndices<ELFT>(CGRelSection, this->Obj, this, SymbolIndices);
+ if (UseReloc && SymbolIndices.size() != CGProfileOrErr->size() * 2) {
+ this->reportUniqueWarning(
+ "number of from/to pairs does not match number of frequencies");
+ UseReloc = false;
+ }
+
+ ListScope L(W, "CGProfile");
+ for (uint32_t I = 0, Size = CGProfileOrErr->size(); I != Size; ++I) {
+ const Elf_CGProfile &CGPE = (*CGProfileOrErr)[I];
+ DictScope D(W, "CGProfileEntry");
+ if (UseReloc) {
+ uint32_t From = SymbolIndices[I * 2];
+ uint32_t To = SymbolIndices[I * 2 + 1];
+ W.printNumber("From", this->getStaticSymbolName(From), From);
+ W.printNumber("To", this->getStaticSymbolName(To), To);
+ }
+ W.printNumber("Weight", CGPE.cgp_weight);
+ }
+ }
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printBBAddrMaps() {
+ bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL;
+ for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) {
+ if (Sec.sh_type != SHT_LLVM_BB_ADDR_MAP)
+ continue;
+ Optional<const Elf_Shdr *> FunctionSec = None;
+ if (IsRelocatable)
+ FunctionSec =
+ unwrapOrError(this->FileName, this->Obj.getSection(Sec.sh_link));
+ ListScope L(W, "BBAddrMap");
+ Expected<std::vector<BBAddrMap>> BBAddrMapOrErr =
+ this->Obj.decodeBBAddrMap(Sec);
+ if (!BBAddrMapOrErr) {
+ this->reportUniqueWarning("unable to dump " + this->describe(Sec) + ": " +
+ toString(BBAddrMapOrErr.takeError()));
+ continue;
+ }
+ for (const BBAddrMap &AM : *BBAddrMapOrErr) {
+ DictScope D(W, "Function");
+ W.printHex("At", AM.Addr);
+ SmallVector<uint32_t> FuncSymIndex =
+ this->getSymbolIndexesForFunctionAddress(AM.Addr, FunctionSec);
+ std::string FuncName = "<?>";
+ if (FuncSymIndex.empty())
+ this->reportUniqueWarning(
+ "could not identify function symbol for address (0x" +
+ Twine::utohexstr(AM.Addr) + ") in " + this->describe(Sec));
+ else
+ FuncName = this->getStaticSymbolName(FuncSymIndex.front());
+ W.printString("Name", FuncName);
+
+ ListScope L(W, "BB entries");
+ for (const BBAddrMap::BBEntry &BBE : AM.BBEntries) {
+ DictScope L(W);
+ W.printHex("Offset", BBE.Offset);
+ W.printHex("Size", BBE.Size);
+ W.printBoolean("HasReturn", BBE.HasReturn);
+ W.printBoolean("HasTailCall", BBE.HasTailCall);
+ W.printBoolean("IsEHPad", BBE.IsEHPad);
+ W.printBoolean("CanFallThrough", BBE.CanFallThrough);
+ }
+ }
+ }
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printAddrsig() {
+ ListScope L(W, "Addrsig");
+ if (!this->DotAddrsigSec)
+ return;
+
+ Expected<std::vector<uint64_t>> SymsOrErr =
+ decodeAddrsigSection(this->Obj, *this->DotAddrsigSec);
+ if (!SymsOrErr) {
+ this->reportUniqueWarning(SymsOrErr.takeError());
+ return;
+ }
+
+ for (uint64_t Sym : *SymsOrErr)
+ W.printNumber("Sym", this->getStaticSymbolName(Sym), Sym);
+}
+
+template <typename ELFT>
+static bool printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc,
+ ScopedPrinter &W) {
+ // Return true if we were able to pretty-print the note, false otherwise.
+ switch (NoteType) {
+ default:
+ return false;
+ case ELF::NT_GNU_ABI_TAG: {
+ const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc);
+ if (!AbiTag.IsValid) {
+ W.printString("ABI", "<corrupt GNU_ABI_TAG>");
+ return false;
+ } 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", getDescAsStringRef(Desc));
+ break;
+ case ELF::NT_GNU_PROPERTY_TYPE_0:
+ ListScope D(W, "Property");
+ for (const std::string &Property : getGNUPropertyList<ELFT>(Desc))
+ W.printString(Property);
+ break;
+ }
+ return true;
+}
+
+template <typename ELFT>
+static bool printLLVMOMPOFFLOADNoteLLVMStyle(uint32_t NoteType,
+ ArrayRef<uint8_t> Desc,
+ ScopedPrinter &W) {
+ switch (NoteType) {
+ default:
+ return false;
+ case ELF::NT_LLVM_OPENMP_OFFLOAD_VERSION:
+ W.printString("Version", getDescAsStringRef(Desc));
+ break;
+ case ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER:
+ W.printString("Producer", getDescAsStringRef(Desc));
+ break;
+ case ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER_VERSION:
+ W.printString("Producer version", getDescAsStringRef(Desc));
+ break;
+ }
+ return true;
+}
+
+static void printCoreNoteLLVMStyle(const CoreNote &Note, ScopedPrinter &W) {
+ W.printNumber("Page Size", Note.PageSize);
+ for (const CoreFileMapping &Mapping : Note.Mappings) {
+ ListScope D(W, "Mapping");
+ W.printHex("Start", Mapping.Start);
+ W.printHex("End", Mapping.End);
+ W.printHex("Offset", Mapping.Offset);
+ W.printString("Filename", Mapping.Filename);
+ }
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printNotes() {
+ ListScope L(W, "Notes");
+
+ std::unique_ptr<DictScope> NoteScope;
+ auto StartNotes = [&](Optional<StringRef> SecName,
+ const typename ELFT::Off Offset,
+ const typename ELFT::Addr Size) {
+ NoteScope = std::make_unique<DictScope>(W, "NoteSection");
+ W.printString("Name", SecName ? *SecName : "<?>");
+ W.printHex("Offset", Offset);
+ W.printHex("Size", Size);
+ };
+
+ auto EndNotes = [&] { NoteScope.reset(); };
+
+ auto ProcessNote = [&](const Elf_Note &Note, bool IsCore) -> Error {
+ DictScope D2(W, "Note");
+ StringRef Name = Note.getName();
+ ArrayRef<uint8_t> Descriptor = Note.getDesc();
+ Elf_Word Type = Note.getType();
+
+ // Print the note owner/type.
+ W.printString("Owner", Name);
+ W.printHex("Data size", Descriptor.size());
+
+ StringRef NoteType =
+ getNoteTypeName<ELFT>(Note, this->Obj.getHeader().e_type);
+ if (!NoteType.empty())
+ W.printString("Type", NoteType);
+ else
+ W.printString("Type",
+ "Unknown (" + to_string(format_hex(Type, 10)) + ")");
+
+ // Print the description, or fallback to printing raw bytes for unknown
+ // owners/if we fail to pretty-print the contents.
+ if (Name == "GNU") {
+ if (printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W))
+ return Error::success();
+ } else if (Name == "FreeBSD") {
+ if (Optional<FreeBSDNote> N =
+ getFreeBSDNote<ELFT>(Type, Descriptor, IsCore)) {
+ W.printString(N->Type, N->Value);
+ return Error::success();
+ }
+ } else if (Name == "AMD") {
+ const AMDNote N = getAMDNote<ELFT>(Type, Descriptor);
+ if (!N.Type.empty()) {
+ W.printString(N.Type, N.Value);
+ return Error::success();
+ }
+ } else if (Name == "AMDGPU") {
+ const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor);
+ if (!N.Type.empty()) {
+ W.printString(N.Type, N.Value);
+ return Error::success();
+ }
+ } else if (Name == "LLVMOMPOFFLOAD") {
+ if (printLLVMOMPOFFLOADNoteLLVMStyle<ELFT>(Type, Descriptor, W))
+ return Error::success();
+ } else if (Name == "CORE") {
+ if (Type == ELF::NT_FILE) {
+ DataExtractor DescExtractor(Descriptor,
+ ELFT::TargetEndianness == support::little,
+ sizeof(Elf_Addr));
+ if (Expected<CoreNote> N = readCoreNote(DescExtractor)) {
+ printCoreNoteLLVMStyle(*N, W);
+ return Error::success();
+ } else {
+ return N.takeError();
+ }
+ }
+ }
+ if (!Descriptor.empty()) {
+ W.printBinaryBlock("Description data", Descriptor);
+ }
+ return Error::success();
+ };
+
+ printNotesHelper(*this, StartNotes, ProcessNote, EndNotes);
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printELFLinkerOptions() {
+ ListScope L(W, "LinkerOptions");
+
+ unsigned I = -1;
+ for (const Elf_Shdr &Shdr : cantFail(this->Obj.sections())) {
+ ++I;
+ if (Shdr.sh_type != ELF::SHT_LLVM_LINKER_OPTIONS)
+ continue;
+
+ Expected<ArrayRef<uint8_t>> ContentsOrErr =
+ this->Obj.getSectionContents(Shdr);
+ if (!ContentsOrErr) {
+ this->reportUniqueWarning("unable to read the content of the "
+ "SHT_LLVM_LINKER_OPTIONS section: " +
+ toString(ContentsOrErr.takeError()));
+ continue;
+ }
+ if (ContentsOrErr->empty())
+ continue;
+
+ if (ContentsOrErr->back() != 0) {
+ this->reportUniqueWarning("SHT_LLVM_LINKER_OPTIONS section at index " +
+ Twine(I) +
+ " is broken: the "
+ "content is not null-terminated");
+ continue;
+ }
+
+ SmallVector<StringRef, 16> Strings;
+ toStringRef(ContentsOrErr->drop_back()).split(Strings, '\0');
+ if (Strings.size() % 2 != 0) {
+ this->reportUniqueWarning(
+ "SHT_LLVM_LINKER_OPTIONS section at index " + Twine(I) +
+ " is broken: an incomplete "
+ "key-value pair was found. The last possible key was: \"" +
+ Strings.back() + "\"");
+ continue;
+ }
+
+ for (size_t I = 0; I < Strings.size(); I += 2)
+ W.printString(Strings[I], Strings[I + 1]);
+ }
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printDependentLibs() {
+ ListScope L(W, "DependentLibs");
+ this->printDependentLibsHelper(
+ [](const Elf_Shdr &) {},
+ [this](StringRef Lib, uint64_t) { W.printString(Lib); });
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printStackSizes() {
+ ListScope L(W, "StackSizes");
+ if (this->Obj.getHeader().e_type == ELF::ET_REL)
+ this->printRelocatableStackSizes([]() {});
+ else
+ this->printNonRelocatableStackSizes([]() {});
+}
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size,
+ ArrayRef<std::string> FuncNames) {
+ DictScope D(W, "Entry");
+ W.printList("Functions", FuncNames);
+ W.printHex("Size", Size);
+}
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) {
+ auto PrintEntry = [&](const Elf_Addr *E) {
+ W.printHex("Address", Parser.getGotAddress(E));
+ W.printNumber("Access", Parser.getGotOffset(E));
+ W.printHex("Initial", *E);
+ };
+
+ DictScope GS(W, Parser.IsStatic ? "Static GOT" : "Primary GOT");
+
+ W.printHex("Canonical gp value", Parser.getGp());
+ {
+ ListScope RS(W, "Reserved entries");
+ {
+ DictScope D(W, "Entry");
+ PrintEntry(Parser.getGotLazyResolver());
+ W.printString("Purpose", StringRef("Lazy resolver"));
+ }
+
+ if (Parser.getGotModulePointer()) {
+ DictScope D(W, "Entry");
+ PrintEntry(Parser.getGotModulePointer());
+ W.printString("Purpose", StringRef("Module pointer (GNU extension)"));
+ }
+ }
+ {
+ ListScope LS(W, "Local entries");
+ for (auto &E : Parser.getLocalEntries()) {
+ DictScope D(W, "Entry");
+ PrintEntry(&E);
+ }
+ }
+
+ if (Parser.IsStatic)
+ return;
+
+ {
+ ListScope GS(W, "Global entries");
+ for (auto &E : Parser.getGlobalEntries()) {
+ DictScope D(W, "Entry");
+
+ PrintEntry(&E);
+
+ const Elf_Sym &Sym = *Parser.getGotSym(&E);
+ W.printHex("Value", Sym.st_value);
+ W.printEnum("Type", Sym.getType(), makeArrayRef(ElfSymbolTypes));
+
+ const unsigned SymIndex = &Sym - this->dynamic_symbols().begin();
+ DataRegion<Elf_Word> ShndxTable(
+ (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end());
+ printSymbolSection(Sym, SymIndex, ShndxTable);
+
+ std::string SymName = this->getFullSymbolName(
+ Sym, SymIndex, ShndxTable, this->DynamicStringTable, true);
+ W.printNumber("Name", SymName, Sym.st_name);
+ }
+ }
+
+ W.printNumber("Number of TLS and multi-GOT entries",
+ uint64_t(Parser.getOtherEntries().size()));
+}
+
+template <class ELFT>
+void LLVMELFDumper<ELFT>::printMipsPLT(const MipsGOTParser<ELFT> &Parser) {
+ auto PrintEntry = [&](const Elf_Addr *E) {
+ W.printHex("Address", Parser.getPltAddress(E));
+ W.printHex("Initial", *E);
+ };
+
+ DictScope GS(W, "PLT GOT");
+
+ {
+ ListScope RS(W, "Reserved entries");
+ {
+ DictScope D(W, "Entry");
+ PrintEntry(Parser.getPltLazyResolver());
+ W.printString("Purpose", StringRef("PLT lazy resolver"));
+ }
+
+ if (auto E = Parser.getPltModulePointer()) {
+ DictScope D(W, "Entry");
+ PrintEntry(E);
+ W.printString("Purpose", StringRef("Module pointer"));
+ }
+ }
+ {
+ ListScope LS(W, "Entries");
+ DataRegion<Elf_Word> ShndxTable(
+ (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end());
+ for (auto &E : Parser.getPltEntries()) {
+ DictScope D(W, "Entry");
+ PrintEntry(&E);
+
+ const Elf_Sym &Sym = *Parser.getPltSym(&E);
+ W.printHex("Value", Sym.st_value);
+ W.printEnum("Type", Sym.getType(), makeArrayRef(ElfSymbolTypes));
+ printSymbolSection(Sym, &Sym - this->dynamic_symbols().begin(),
+ ShndxTable);
+
+ const Elf_Sym *FirstSym = cantFail(
+ this->Obj.template getEntry<Elf_Sym>(*Parser.getPltSymTable(), 0));
+ std::string SymName = this->getFullSymbolName(
+ Sym, &Sym - FirstSym, ShndxTable, Parser.getPltStrTable(), true);
+ W.printNumber("Name", SymName, Sym.st_name);
+ }
+ }
+}
+
+template <class ELFT> void LLVMELFDumper<ELFT>::printMipsABIFlags() {
+ const Elf_Mips_ABIFlags<ELFT> *Flags;
+ if (Expected<const Elf_Mips_ABIFlags<ELFT> *> SecOrErr =
+ getMipsAbiFlagsSection(*this)) {
+ Flags = *SecOrErr;
+ if (!Flags) {
+ W.startLine() << "There is no .MIPS.abiflags section in the file.\n";
+ return;
+ }
+ } else {
+ this->reportUniqueWarning(SecOrErr.takeError());
+ return;
+ }
+
+ raw_ostream &OS = W.getOStream();
+ DictScope GS(W, "MIPS ABI Flags");
+
+ W.printNumber("Version", Flags->version);
+ W.startLine() << "ISA: ";
+ if (Flags->isa_rev <= 1)
+ OS << format("MIPS%u", Flags->isa_level);
+ else
+ OS << format("MIPS%ur%u", Flags->isa_level, Flags->isa_rev);
+ OS << "\n";
+ W.printEnum("ISA Extension", Flags->isa_ext, makeArrayRef(ElfMipsISAExtType));
+ W.printFlags("ASEs", Flags->ases, makeArrayRef(ElfMipsASEFlags));
+ W.printEnum("FP ABI", Flags->fp_abi, makeArrayRef(ElfMipsFpABIType));
+ W.printNumber("GPR size", getMipsRegisterSize(Flags->gpr_size));
+ W.printNumber("CPR1 size", getMipsRegisterSize(Flags->cpr1_size));
+ W.printNumber("CPR2 size", getMipsRegisterSize(Flags->cpr2_size));
+ W.printFlags("Flags 1", Flags->flags1, makeArrayRef(ElfMipsFlags1));
+ W.printHex("Flags 2", Flags->flags2);
+}
+
+template <class ELFT>
+void JSONELFDumper<ELFT>::printFileSummary(StringRef FileStr, ObjectFile &Obj,
+ ArrayRef<std::string> InputFilenames,
+ const Archive *A) {
+ FileScope = std::make_unique<DictScope>(this->W, FileStr);
+ DictScope D(this->W, "FileSummary");
+ this->W.printString("File", FileStr);
+ this->W.printString("Format", Obj.getFileFormatName());
+ this->W.printString("Arch", Triple::getArchTypeName(Obj.getArch()));
+ this->W.printString(
+ "AddressSize",
+ std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress())));
+ this->printLoadName();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/MachODumper.cpp b/contrib/libs/llvm14/tools/llvm-readobj/MachODumper.cpp
new file mode 100644
index 00000000000..599b0355917
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/MachODumper.cpp
@@ -0,0 +1,938 @@
+//===- MachODumper.cpp - Object file dumping utility for llvm -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the MachO-specific dumper for llvm-readobj.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ObjDumper.h"
+#include "StackMapPrinter.h"
+#include "llvm-readobj.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Support/BinaryStreamReader.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+using namespace llvm;
+using namespace object;
+
+namespace {
+
+class MachODumper : public ObjDumper {
+public:
+ MachODumper(const MachOObjectFile *Obj, ScopedPrinter &Writer)
+ : ObjDumper(Writer, Obj->getFileName()), Obj(Obj) {}
+
+ void printFileHeaders() override;
+ void printSectionHeaders() override;
+ void printRelocations() override;
+ void printUnwindInfo() override;
+ void printStackMap() const override;
+ void printCGProfile() override;
+
+ void printNeededLibraries() override;
+
+ // MachO-specific.
+ void printMachODataInCode() override;
+ void printMachOVersionMin() override;
+ void printMachODysymtab() override;
+ void printMachOSegment() override;
+ void printMachOIndirectSymbols() override;
+ void printMachOLinkerOptions () override;
+
+private:
+ template<class MachHeader>
+ void printFileHeaders(const MachHeader &Header);
+
+ StringRef getSymbolName(const SymbolRef &Symbol);
+
+ void printSymbols() override;
+ void printDynamicSymbols() override;
+ void printSymbol(const SymbolRef &Symbol);
+
+ void printRelocation(const RelocationRef &Reloc);
+
+ void printRelocation(const MachOObjectFile *Obj, const RelocationRef &Reloc);
+
+ void printSectionHeaders(const MachOObjectFile *Obj);
+
+ const MachOObjectFile *Obj;
+};
+
+} // namespace
+
+
+namespace llvm {
+
+std::unique_ptr<ObjDumper> createMachODumper(const object::MachOObjectFile &Obj,
+ ScopedPrinter &Writer) {
+ return std::make_unique<MachODumper>(&Obj, Writer);
+}
+
+} // namespace llvm
+
+const EnumEntry<uint32_t> MachOMagics[] = {
+ { "Magic", MachO::MH_MAGIC },
+ { "Cigam", MachO::MH_CIGAM },
+ { "Magic64", MachO::MH_MAGIC_64 },
+ { "Cigam64", MachO::MH_CIGAM_64 },
+ { "FatMagic", MachO::FAT_MAGIC },
+ { "FatCigam", MachO::FAT_CIGAM },
+};
+
+const EnumEntry<uint32_t> MachOHeaderFileTypes[] = {
+ { "Relocatable", MachO::MH_OBJECT },
+ { "Executable", MachO::MH_EXECUTE },
+ { "FixedVMLibrary", MachO::MH_FVMLIB },
+ { "Core", MachO::MH_CORE },
+ { "PreloadedExecutable", MachO::MH_PRELOAD },
+ { "DynamicLibrary", MachO::MH_DYLIB },
+ { "DynamicLinker", MachO::MH_DYLINKER },
+ { "Bundle", MachO::MH_BUNDLE },
+ { "DynamicLibraryStub", MachO::MH_DYLIB_STUB },
+ { "DWARFSymbol", MachO::MH_DSYM },
+ { "KextBundle", MachO::MH_KEXT_BUNDLE },
+};
+
+const EnumEntry<uint32_t> MachOHeaderCpuTypes[] = {
+ { "Any" , static_cast<uint32_t>(MachO::CPU_TYPE_ANY) },
+ { "X86" , MachO::CPU_TYPE_X86 },
+ { "X86-64" , MachO::CPU_TYPE_X86_64 },
+ { "Mc98000" , MachO::CPU_TYPE_MC98000 },
+ { "Arm" , MachO::CPU_TYPE_ARM },
+ { "Arm64" , MachO::CPU_TYPE_ARM64 },
+ { "Sparc" , MachO::CPU_TYPE_SPARC },
+ { "PowerPC" , MachO::CPU_TYPE_POWERPC },
+ { "PowerPC64" , MachO::CPU_TYPE_POWERPC64 },
+};
+
+const EnumEntry<uint32_t> MachOHeaderCpuSubtypesX86[] = {
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_I386_ALL),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_386),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_486),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_486SX),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_586),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTPRO),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTII_M3),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTII_M5),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_CELERON),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_CELERON_MOBILE),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_3),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_3_M),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_3_XEON),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_M),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_4),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_4_M),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ITANIUM),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ITANIUM_2),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_XEON),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_XEON_MP),
+};
+
+const EnumEntry<uint32_t> MachOHeaderCpuSubtypesX64[] = {
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_X86_64_ALL),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_X86_ARCH1),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_X86_64_H),
+};
+
+const EnumEntry<uint32_t> MachOHeaderCpuSubtypesARM[] = {
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_ALL),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V4T),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V6),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V5),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V5TEJ),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_XSCALE),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7S),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7K),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V6M),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7M),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7EM),
+};
+
+const EnumEntry<uint32_t> MachOHeaderCpuSubtypesARM64[] = {
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM64_ALL),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM64_V8),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM64E),
+};
+
+const EnumEntry<uint32_t> MachOHeaderCpuSubtypesSPARC[] = {
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_SPARC_ALL),
+};
+
+const EnumEntry<uint32_t> MachOHeaderCpuSubtypesPPC[] = {
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_ALL),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_601),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_602),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_603),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_603e),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_603ev),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_604),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_604e),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_620),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_750),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_7400),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_7450),
+ LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_970),
+};
+
+const EnumEntry<uint32_t> MachOHeaderFlags[] = {
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_NOUNDEFS),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_INCRLINK),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_DYLDLINK),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_BINDATLOAD),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_PREBOUND),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_SPLIT_SEGS),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_LAZY_INIT),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_TWOLEVEL),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_FORCE_FLAT),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_NOMULTIDEFS),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_NOFIXPREBINDING),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_PREBINDABLE),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_ALLMODSBOUND),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_SUBSECTIONS_VIA_SYMBOLS),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_CANONICAL),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_WEAK_DEFINES),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_BINDS_TO_WEAK),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_ALLOW_STACK_EXECUTION),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_ROOT_SAFE),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_SETUID_SAFE),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_NO_REEXPORTED_DYLIBS),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_PIE),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_DEAD_STRIPPABLE_DYLIB),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_HAS_TLV_DESCRIPTORS),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_NO_HEAP_EXECUTION),
+ LLVM_READOBJ_ENUM_ENT(MachO, MH_APP_EXTENSION_SAFE),
+};
+
+const EnumEntry<unsigned> MachOSectionTypes[] = {
+ { "Regular" , MachO::S_REGULAR },
+ { "ZeroFill" , MachO::S_ZEROFILL },
+ { "CStringLiterals" , MachO::S_CSTRING_LITERALS },
+ { "4ByteLiterals" , MachO::S_4BYTE_LITERALS },
+ { "8ByteLiterals" , MachO::S_8BYTE_LITERALS },
+ { "LiteralPointers" , MachO::S_LITERAL_POINTERS },
+ { "NonLazySymbolPointers" , MachO::S_NON_LAZY_SYMBOL_POINTERS },
+ { "LazySymbolPointers" , MachO::S_LAZY_SYMBOL_POINTERS },
+ { "SymbolStubs" , MachO::S_SYMBOL_STUBS },
+ { "ModInitFuncPointers" , MachO::S_MOD_INIT_FUNC_POINTERS },
+ { "ModTermFuncPointers" , MachO::S_MOD_TERM_FUNC_POINTERS },
+ { "Coalesced" , MachO::S_COALESCED },
+ { "GBZeroFill" , MachO::S_GB_ZEROFILL },
+ { "Interposing" , MachO::S_INTERPOSING },
+ { "16ByteLiterals" , MachO::S_16BYTE_LITERALS },
+ { "DTraceDOF" , MachO::S_DTRACE_DOF },
+ { "LazyDylibSymbolPointers" , MachO::S_LAZY_DYLIB_SYMBOL_POINTERS },
+ { "ThreadLocalRegular" , MachO::S_THREAD_LOCAL_REGULAR },
+ { "ThreadLocalZerofill" , MachO::S_THREAD_LOCAL_ZEROFILL },
+ { "ThreadLocalVariables" , MachO::S_THREAD_LOCAL_VARIABLES },
+ { "ThreadLocalVariablePointers" , MachO::S_THREAD_LOCAL_VARIABLE_POINTERS },
+ { "ThreadLocalInitFunctionPointers", MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS }
+};
+
+const EnumEntry<unsigned> MachOSectionAttributes[] = {
+ { "LocReloc" , 1 << 0 /*S_ATTR_LOC_RELOC */ },
+ { "ExtReloc" , 1 << 1 /*S_ATTR_EXT_RELOC */ },
+ { "SomeInstructions" , 1 << 2 /*S_ATTR_SOME_INSTRUCTIONS */ },
+ { "Debug" , 1 << 17 /*S_ATTR_DEBUG */ },
+ { "SelfModifyingCode", 1 << 18 /*S_ATTR_SELF_MODIFYING_CODE*/ },
+ { "LiveSupport" , 1 << 19 /*S_ATTR_LIVE_SUPPORT */ },
+ { "NoDeadStrip" , 1 << 20 /*S_ATTR_NO_DEAD_STRIP */ },
+ { "StripStaticSyms" , 1 << 21 /*S_ATTR_STRIP_STATIC_SYMS */ },
+ { "NoTOC" , 1 << 22 /*S_ATTR_NO_TOC */ },
+ { "PureInstructions" , 1 << 23 /*S_ATTR_PURE_INSTRUCTIONS */ },
+};
+
+const EnumEntry<unsigned> MachOSymbolRefTypes[] = {
+ { "UndefinedNonLazy", 0 },
+ { "ReferenceFlagUndefinedLazy", 1 },
+ { "ReferenceFlagDefined", 2 },
+ { "ReferenceFlagPrivateDefined", 3 },
+ { "ReferenceFlagPrivateUndefinedNonLazy", 4 },
+ { "ReferenceFlagPrivateUndefinedLazy", 5 }
+};
+
+const EnumEntry<unsigned> MachOSymbolFlags[] = {
+ { "ThumbDef", 0x8 },
+ { "ReferencedDynamically", 0x10 },
+ { "NoDeadStrip", 0x20 },
+ { "WeakRef", 0x40 },
+ { "WeakDef", 0x80 },
+ { "SymbolResolver", 0x100 },
+ { "AltEntry", 0x200 },
+ { "ColdFunc", 0x400 },
+};
+
+const EnumEntry<unsigned> MachOSymbolTypes[] = {
+ { "Undef", 0x0 },
+ { "Abs", 0x2 },
+ { "Indirect", 0xA },
+ { "PreboundUndef", 0xC },
+ { "Section", 0xE }
+};
+
+namespace {
+ struct MachOSection {
+ ArrayRef<char> Name;
+ ArrayRef<char> SegmentName;
+ uint64_t Address;
+ uint64_t Size;
+ uint32_t Offset;
+ uint32_t Alignment;
+ uint32_t RelocationTableOffset;
+ uint32_t NumRelocationTableEntries;
+ uint32_t Flags;
+ uint32_t Reserved1;
+ uint32_t Reserved2;
+ uint32_t Reserved3;
+ };
+
+ struct MachOSegment {
+ std::string CmdName;
+ std::string SegName;
+ uint64_t cmdsize;
+ uint64_t vmaddr;
+ uint64_t vmsize;
+ uint64_t fileoff;
+ uint64_t filesize;
+ uint32_t maxprot;
+ uint32_t initprot;
+ uint32_t nsects;
+ uint32_t flags;
+ };
+
+ struct MachOSymbol {
+ uint32_t StringIndex;
+ uint8_t Type;
+ uint8_t SectionIndex;
+ uint16_t Flags;
+ uint64_t Value;
+ };
+}
+
+static std::string getMask(uint32_t prot)
+{
+ // TODO (davide): This always assumes prot is valid.
+ // Catch mistakes and report if needed.
+ std::string Prot;
+ Prot = "";
+ Prot += (prot & MachO::VM_PROT_READ) ? "r" : "-";
+ Prot += (prot & MachO::VM_PROT_WRITE) ? "w" : "-";
+ Prot += (prot & MachO::VM_PROT_EXECUTE) ? "x" : "-";
+ return Prot;
+}
+
+static void getSection(const MachOObjectFile *Obj,
+ DataRefImpl Sec,
+ MachOSection &Section) {
+ if (!Obj->is64Bit()) {
+ MachO::section Sect = Obj->getSection(Sec);
+ Section.Address = Sect.addr;
+ Section.Size = Sect.size;
+ Section.Offset = Sect.offset;
+ Section.Alignment = Sect.align;
+ Section.RelocationTableOffset = Sect.reloff;
+ Section.NumRelocationTableEntries = Sect.nreloc;
+ Section.Flags = Sect.flags;
+ Section.Reserved1 = Sect.reserved1;
+ Section.Reserved2 = Sect.reserved2;
+ return;
+ }
+ MachO::section_64 Sect = Obj->getSection64(Sec);
+ Section.Address = Sect.addr;
+ Section.Size = Sect.size;
+ Section.Offset = Sect.offset;
+ Section.Alignment = Sect.align;
+ Section.RelocationTableOffset = Sect.reloff;
+ Section.NumRelocationTableEntries = Sect.nreloc;
+ Section.Flags = Sect.flags;
+ Section.Reserved1 = Sect.reserved1;
+ Section.Reserved2 = Sect.reserved2;
+ Section.Reserved3 = Sect.reserved3;
+}
+
+static void getSegment(const MachOObjectFile *Obj,
+ const MachOObjectFile::LoadCommandInfo &L,
+ MachOSegment &Segment) {
+ if (!Obj->is64Bit()) {
+ MachO::segment_command SC = Obj->getSegmentLoadCommand(L);
+ Segment.CmdName = "LC_SEGMENT";
+ Segment.SegName = SC.segname;
+ Segment.cmdsize = SC.cmdsize;
+ Segment.vmaddr = SC.vmaddr;
+ Segment.vmsize = SC.vmsize;
+ Segment.fileoff = SC.fileoff;
+ Segment.filesize = SC.filesize;
+ Segment.maxprot = SC.maxprot;
+ Segment.initprot = SC.initprot;
+ Segment.nsects = SC.nsects;
+ Segment.flags = SC.flags;
+ return;
+ }
+ MachO::segment_command_64 SC = Obj->getSegment64LoadCommand(L);
+ Segment.CmdName = "LC_SEGMENT_64";
+ Segment.SegName = SC.segname;
+ Segment.cmdsize = SC.cmdsize;
+ Segment.vmaddr = SC.vmaddr;
+ Segment.vmsize = SC.vmsize;
+ Segment.fileoff = SC.fileoff;
+ Segment.filesize = SC.filesize;
+ Segment.maxprot = SC.maxprot;
+ Segment.initprot = SC.initprot;
+ Segment.nsects = SC.nsects;
+ Segment.flags = SC.flags;
+}
+
+static void getSymbol(const MachOObjectFile *Obj,
+ DataRefImpl DRI,
+ MachOSymbol &Symbol) {
+ if (!Obj->is64Bit()) {
+ MachO::nlist Entry = Obj->getSymbolTableEntry(DRI);
+ Symbol.StringIndex = Entry.n_strx;
+ Symbol.Type = Entry.n_type;
+ Symbol.SectionIndex = Entry.n_sect;
+ Symbol.Flags = Entry.n_desc;
+ Symbol.Value = Entry.n_value;
+ return;
+ }
+ MachO::nlist_64 Entry = Obj->getSymbol64TableEntry(DRI);
+ Symbol.StringIndex = Entry.n_strx;
+ Symbol.Type = Entry.n_type;
+ Symbol.SectionIndex = Entry.n_sect;
+ Symbol.Flags = Entry.n_desc;
+ Symbol.Value = Entry.n_value;
+}
+
+void MachODumper::printFileHeaders() {
+ DictScope H(W, "MachHeader");
+ if (!Obj->is64Bit()) {
+ printFileHeaders(Obj->getHeader());
+ } else {
+ printFileHeaders(Obj->getHeader64());
+ W.printHex("Reserved", Obj->getHeader64().reserved);
+ }
+}
+
+template<class MachHeader>
+void MachODumper::printFileHeaders(const MachHeader &Header) {
+ W.printEnum("Magic", Header.magic, makeArrayRef(MachOMagics));
+ W.printEnum("CpuType", Header.cputype, makeArrayRef(MachOHeaderCpuTypes));
+ uint32_t subtype = Header.cpusubtype & ~MachO::CPU_SUBTYPE_MASK;
+ switch (Header.cputype) {
+ case MachO::CPU_TYPE_X86:
+ W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesX86));
+ break;
+ case MachO::CPU_TYPE_X86_64:
+ W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesX64));
+ break;
+ case MachO::CPU_TYPE_ARM:
+ W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesARM));
+ break;
+ case MachO::CPU_TYPE_POWERPC:
+ W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesPPC));
+ break;
+ case MachO::CPU_TYPE_SPARC:
+ W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesSPARC));
+ break;
+ case MachO::CPU_TYPE_ARM64:
+ W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesARM64));
+ break;
+ case MachO::CPU_TYPE_POWERPC64:
+ default:
+ W.printHex("CpuSubtype", subtype);
+ }
+ W.printEnum("FileType", Header.filetype, makeArrayRef(MachOHeaderFileTypes));
+ W.printNumber("NumOfLoadCommands", Header.ncmds);
+ W.printNumber("SizeOfLoadCommands", Header.sizeofcmds);
+ W.printFlags("Flags", Header.flags, makeArrayRef(MachOHeaderFlags));
+}
+
+void MachODumper::printSectionHeaders() { return printSectionHeaders(Obj); }
+
+void MachODumper::printSectionHeaders(const MachOObjectFile *Obj) {
+ ListScope Group(W, "Sections");
+
+ int SectionIndex = -1;
+ for (const SectionRef &Section : Obj->sections()) {
+ ++SectionIndex;
+
+ MachOSection MOSection;
+ getSection(Obj, Section.getRawDataRefImpl(), MOSection);
+ DataRefImpl DR = Section.getRawDataRefImpl();
+ StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName());
+ ArrayRef<char> RawName = Obj->getSectionRawName(DR);
+ StringRef SegmentName = Obj->getSectionFinalSegmentName(DR);
+ ArrayRef<char> RawSegmentName = Obj->getSectionRawFinalSegmentName(DR);
+
+ DictScope SectionD(W, "Section");
+ W.printNumber("Index", SectionIndex);
+ W.printBinary("Name", Name, RawName);
+ W.printBinary("Segment", SegmentName, RawSegmentName);
+ W.printHex("Address", MOSection.Address);
+ W.printHex("Size", MOSection.Size);
+ W.printNumber("Offset", MOSection.Offset);
+ W.printNumber("Alignment", MOSection.Alignment);
+ W.printHex("RelocationOffset", MOSection.RelocationTableOffset);
+ W.printNumber("RelocationCount", MOSection.NumRelocationTableEntries);
+ W.printEnum("Type", MOSection.Flags & 0xFF,
+ makeArrayRef(MachOSectionTypes));
+ W.printFlags("Attributes", MOSection.Flags >> 8,
+ makeArrayRef(MachOSectionAttributes));
+ W.printHex("Reserved1", MOSection.Reserved1);
+ W.printHex("Reserved2", MOSection.Reserved2);
+ if (Obj->is64Bit())
+ W.printHex("Reserved3", MOSection.Reserved3);
+
+ if (opts::SectionRelocations) {
+ ListScope D(W, "Relocations");
+ for (const RelocationRef &Reloc : Section.relocations())
+ printRelocation(Reloc);
+ }
+
+ if (opts::SectionSymbols) {
+ ListScope D(W, "Symbols");
+ for (const SymbolRef &Symbol : Obj->symbols()) {
+ if (!Section.containsSymbol(Symbol))
+ continue;
+
+ printSymbol(Symbol);
+ }
+ }
+
+ if (opts::SectionData && !Section.isBSS())
+ W.printBinaryBlock("SectionData", unwrapOrError(Obj->getFileName(),
+ Section.getContents()));
+ }
+}
+
+void MachODumper::printRelocations() {
+ ListScope D(W, "Relocations");
+
+ std::error_code EC;
+ for (const SectionRef &Section : Obj->sections()) {
+ StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName());
+ bool PrintedGroup = false;
+ for (const RelocationRef &Reloc : Section.relocations()) {
+ if (!PrintedGroup) {
+ W.startLine() << "Section " << Name << " {\n";
+ W.indent();
+ PrintedGroup = true;
+ }
+
+ printRelocation(Reloc);
+ }
+
+ if (PrintedGroup) {
+ W.unindent();
+ W.startLine() << "}\n";
+ }
+ }
+}
+
+void MachODumper::printRelocation(const RelocationRef &Reloc) {
+ return printRelocation(Obj, Reloc);
+}
+
+void MachODumper::printRelocation(const MachOObjectFile *Obj,
+ const RelocationRef &Reloc) {
+ uint64_t Offset = Reloc.getOffset();
+ SmallString<32> RelocName;
+ Reloc.getTypeName(RelocName);
+
+ DataRefImpl DR = Reloc.getRawDataRefImpl();
+ MachO::any_relocation_info RE = Obj->getRelocation(DR);
+ bool IsScattered = Obj->isRelocationScattered(RE);
+ bool IsExtern = !IsScattered && Obj->getPlainRelocationExternal(RE);
+
+ StringRef TargetName;
+ if (IsExtern) {
+ symbol_iterator Symbol = Reloc.getSymbol();
+ if (Symbol != Obj->symbol_end()) {
+ TargetName = getSymbolName(*Symbol);
+ }
+ } else if (!IsScattered) {
+ section_iterator SecI = Obj->getRelocationSection(DR);
+ if (SecI != Obj->section_end())
+ TargetName = unwrapOrError(Obj->getFileName(), SecI->getName());
+ }
+ if (TargetName.empty())
+ TargetName = "-";
+
+ if (opts::ExpandRelocs) {
+ DictScope Group(W, "Relocation");
+ W.printHex("Offset", Offset);
+ W.printNumber("PCRel", Obj->getAnyRelocationPCRel(RE));
+ W.printNumber("Length", Obj->getAnyRelocationLength(RE));
+ W.printNumber("Type", RelocName, Obj->getAnyRelocationType(RE));
+ if (IsScattered) {
+ W.printHex("Value", Obj->getScatteredRelocationValue(RE));
+ } else {
+ const char *Kind = IsExtern ? "Symbol" : "Section";
+ W.printNumber(Kind, TargetName, Obj->getPlainRelocationSymbolNum(RE));
+ }
+ } else {
+ SmallString<32> SymbolNameOrOffset("0x");
+ if (IsScattered) {
+ // Scattered relocations don't really have an associated symbol for some
+ // reason, even if one exists in the symtab at the correct address.
+ SymbolNameOrOffset += utohexstr(Obj->getScatteredRelocationValue(RE));
+ } else {
+ SymbolNameOrOffset = TargetName;
+ }
+
+ raw_ostream& OS = W.startLine();
+ OS << W.hex(Offset)
+ << " " << Obj->getAnyRelocationPCRel(RE)
+ << " " << Obj->getAnyRelocationLength(RE);
+ if (IsScattered)
+ OS << " n/a";
+ else
+ OS << " " << Obj->getPlainRelocationExternal(RE);
+ OS << " " << RelocName
+ << " " << IsScattered
+ << " " << SymbolNameOrOffset
+ << "\n";
+ }
+}
+
+StringRef MachODumper::getSymbolName(const SymbolRef &Symbol) {
+ Expected<StringRef> SymbolNameOrErr = Symbol.getName();
+ if (!SymbolNameOrErr) {
+ reportError(SymbolNameOrErr.takeError(), Obj->getFileName());
+ }
+ return *SymbolNameOrErr;
+}
+
+void MachODumper::printSymbols() {
+ ListScope Group(W, "Symbols");
+
+ for (const SymbolRef &Symbol : Obj->symbols()) {
+ printSymbol(Symbol);
+ }
+}
+
+void MachODumper::printDynamicSymbols() {
+ ListScope Group(W, "DynamicSymbols");
+}
+
+void MachODumper::printSymbol(const SymbolRef &Symbol) {
+ StringRef SymbolName = getSymbolName(Symbol);
+
+ MachOSymbol MOSymbol;
+ getSymbol(Obj, Symbol.getRawDataRefImpl(), MOSymbol);
+
+ StringRef SectionName = "";
+ // Don't ask a Mach-O STABS symbol for its section unless we know that
+ // STAB symbol's section field refers to a valid section index. Otherwise
+ // the symbol may error trying to load a section that does not exist.
+ // TODO: Add a whitelist of STABS symbol types that contain valid section
+ // indices.
+ if (!(MOSymbol.Type & MachO::N_STAB)) {
+ Expected<section_iterator> SecIOrErr = Symbol.getSection();
+ if (!SecIOrErr)
+ reportError(SecIOrErr.takeError(), Obj->getFileName());
+
+ section_iterator SecI = *SecIOrErr;
+ if (SecI != Obj->section_end())
+ SectionName = unwrapOrError(Obj->getFileName(), SecI->getName());
+ }
+
+ DictScope D(W, "Symbol");
+ W.printNumber("Name", SymbolName, MOSymbol.StringIndex);
+ if (MOSymbol.Type & MachO::N_STAB) {
+ W.printHex("Type", "SymDebugTable", MOSymbol.Type);
+ } else {
+ if (MOSymbol.Type & MachO::N_PEXT)
+ W.startLine() << "PrivateExtern\n";
+ if (MOSymbol.Type & MachO::N_EXT)
+ W.startLine() << "Extern\n";
+ W.printEnum("Type", uint8_t(MOSymbol.Type & MachO::N_TYPE),
+ makeArrayRef(MachOSymbolTypes));
+ }
+ W.printHex("Section", SectionName, MOSymbol.SectionIndex);
+ W.printEnum("RefType", static_cast<uint16_t>(MOSymbol.Flags & 0x7),
+ makeArrayRef(MachOSymbolRefTypes));
+ W.printFlags("Flags", static_cast<uint16_t>(MOSymbol.Flags & ~0x7),
+ makeArrayRef(MachOSymbolFlags));
+ W.printHex("Value", MOSymbol.Value);
+}
+
+void MachODumper::printUnwindInfo() {
+ W.startLine() << "UnwindInfo not implemented.\n";
+}
+
+void MachODumper::printStackMap() const {
+ object::SectionRef StackMapSection;
+ for (auto Sec : Obj->sections()) {
+ StringRef Name;
+ if (Expected<StringRef> NameOrErr = Sec.getName())
+ Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ if (Name == "__llvm_stackmaps") {
+ StackMapSection = Sec;
+ break;
+ }
+ }
+
+ if (StackMapSection == object::SectionRef())
+ return;
+
+ StringRef StackMapContents =
+ unwrapOrError(Obj->getFileName(), StackMapSection.getContents());
+ ArrayRef<uint8_t> StackMapContentsArray =
+ arrayRefFromStringRef(StackMapContents);
+
+ if (Obj->isLittleEndian())
+ prettyPrintStackMap(
+ W, StackMapParser<support::little>(StackMapContentsArray));
+ else
+ prettyPrintStackMap(
+ W, StackMapParser<support::big>(StackMapContentsArray));
+}
+
+void MachODumper::printCGProfile() {
+ object::SectionRef CGProfileSection;
+ for (auto Sec : Obj->sections()) {
+ StringRef Name;
+ if (Expected<StringRef> NameOrErr = Sec.getName())
+ Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ if (Name == "__cg_profile") {
+ CGProfileSection = Sec;
+ break;
+ }
+ }
+ if (CGProfileSection == object::SectionRef())
+ return;
+
+ StringRef CGProfileContents =
+ unwrapOrError(Obj->getFileName(), CGProfileSection.getContents());
+ BinaryStreamReader Reader(CGProfileContents, Obj->isLittleEndian()
+ ? llvm::support::little
+ : llvm::support::big);
+
+ ListScope L(W, "CGProfile");
+ while (!Reader.empty()) {
+ uint32_t FromIndex, ToIndex;
+ uint64_t Count;
+ if (Error Err = Reader.readInteger(FromIndex))
+ reportError(std::move(Err), Obj->getFileName());
+ if (Error Err = Reader.readInteger(ToIndex))
+ reportError(std::move(Err), Obj->getFileName());
+ if (Error Err = Reader.readInteger(Count))
+ reportError(std::move(Err), Obj->getFileName());
+ DictScope D(W, "CGProfileEntry");
+ W.printNumber("From", getSymbolName(*Obj->getSymbolByIndex(FromIndex)),
+ FromIndex);
+ W.printNumber("To", getSymbolName(*Obj->getSymbolByIndex(ToIndex)),
+ ToIndex);
+ W.printNumber("Weight", Count);
+ }
+}
+
+void MachODumper::printNeededLibraries() {
+ ListScope D(W, "NeededLibraries");
+
+ using LibsTy = std::vector<StringRef>;
+ LibsTy Libs;
+
+ for (const auto &Command : Obj->load_commands()) {
+ if (Command.C.cmd == MachO::LC_LOAD_DYLIB ||
+ Command.C.cmd == MachO::LC_ID_DYLIB ||
+ Command.C.cmd == MachO::LC_LOAD_WEAK_DYLIB ||
+ Command.C.cmd == MachO::LC_REEXPORT_DYLIB ||
+ Command.C.cmd == MachO::LC_LAZY_LOAD_DYLIB ||
+ Command.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) {
+ MachO::dylib_command Dl = Obj->getDylibIDLoadCommand(Command);
+ if (Dl.dylib.name < Dl.cmdsize) {
+ auto *P = static_cast<const char*>(Command.Ptr) + Dl.dylib.name;
+ Libs.push_back(P);
+ }
+ }
+ }
+
+ llvm::stable_sort(Libs);
+
+ for (const auto &L : Libs) {
+ W.startLine() << L << "\n";
+ }
+}
+
+void MachODumper::printMachODataInCode() {
+ for (const auto &Load : Obj->load_commands()) {
+ if (Load.C.cmd == MachO::LC_DATA_IN_CODE) {
+ MachO::linkedit_data_command LLC = Obj->getLinkeditDataLoadCommand(Load);
+ DictScope Group(W, "DataInCode");
+ W.printNumber("Data offset", LLC.dataoff);
+ W.printNumber("Data size", LLC.datasize);
+ ListScope D(W, "Data entries");
+ unsigned NumRegions = LLC.datasize / sizeof(MachO::data_in_code_entry);
+ for (unsigned i = 0; i < NumRegions; ++i) {
+ MachO::data_in_code_entry DICE = Obj->getDataInCodeTableEntry(
+ LLC.dataoff, i);
+ DictScope Group(W, "Entry");
+ W.printNumber("Index", i);
+ W.printNumber("Offset", DICE.offset);
+ W.printNumber("Length", DICE.length);
+ W.printNumber("Kind", DICE.kind);
+ }
+ }
+ }
+}
+
+void MachODumper::printMachOVersionMin() {
+ for (const auto &Load : Obj->load_commands()) {
+ StringRef Cmd;
+ switch (Load.C.cmd) {
+ case MachO::LC_VERSION_MIN_MACOSX:
+ Cmd = "LC_VERSION_MIN_MACOSX";
+ break;
+ case MachO::LC_VERSION_MIN_IPHONEOS:
+ Cmd = "LC_VERSION_MIN_IPHONEOS";
+ break;
+ case MachO::LC_VERSION_MIN_TVOS:
+ Cmd = "LC_VERSION_MIN_TVOS";
+ break;
+ case MachO::LC_VERSION_MIN_WATCHOS:
+ Cmd = "LC_VERSION_MIN_WATCHOS";
+ break;
+ case MachO::LC_BUILD_VERSION:
+ Cmd = "LC_BUILD_VERSION";
+ break;
+ default:
+ continue;
+ }
+
+ DictScope Group(W, "MinVersion");
+ // Handle LC_BUILD_VERSION.
+ if (Load.C.cmd == MachO::LC_BUILD_VERSION) {
+ MachO::build_version_command BVC = Obj->getBuildVersionLoadCommand(Load);
+ W.printString("Cmd", Cmd);
+ W.printNumber("Size", BVC.cmdsize);
+ W.printString("Platform",
+ MachOObjectFile::getBuildPlatform(BVC.platform));
+ W.printString("Version", MachOObjectFile::getVersionString(BVC.minos));
+ if (BVC.sdk)
+ W.printString("SDK", MachOObjectFile::getVersionString(BVC.sdk));
+ else
+ W.printString("SDK", StringRef("n/a"));
+ continue;
+ }
+
+ MachO::version_min_command VMC = Obj->getVersionMinLoadCommand(Load);
+ W.printString("Cmd", Cmd);
+ W.printNumber("Size", VMC.cmdsize);
+ SmallString<32> Version;
+ Version = utostr(MachOObjectFile::getVersionMinMajor(VMC, false)) + "." +
+ utostr(MachOObjectFile::getVersionMinMinor(VMC, false));
+ uint32_t Update = MachOObjectFile::getVersionMinUpdate(VMC, false);
+ if (Update != 0)
+ Version += "." + utostr(MachOObjectFile::getVersionMinUpdate(VMC, false));
+ W.printString("Version", Version);
+ SmallString<32> SDK;
+ if (VMC.sdk == 0)
+ SDK = "n/a";
+ else {
+ SDK = utostr(MachOObjectFile::getVersionMinMajor(VMC, true)) + "." +
+ utostr(MachOObjectFile::getVersionMinMinor(VMC, true));
+ uint32_t Update = MachOObjectFile::getVersionMinUpdate(VMC, true);
+ if (Update != 0)
+ SDK += "." + utostr(MachOObjectFile::getVersionMinUpdate(VMC, true));
+ }
+ W.printString("SDK", SDK);
+ }
+}
+
+void MachODumper::printMachODysymtab() {
+ for (const auto &Load : Obj->load_commands()) {
+ if (Load.C.cmd == MachO::LC_DYSYMTAB) {
+ MachO::dysymtab_command DLC = Obj->getDysymtabLoadCommand();
+ DictScope Group(W, "Dysymtab");
+ W.printNumber("ilocalsym", DLC.ilocalsym);
+ W.printNumber("nlocalsym", DLC.nlocalsym);
+ W.printNumber("iextdefsym", DLC.iextdefsym);
+ W.printNumber("nextdefsym", DLC.nextdefsym);
+ W.printNumber("iundefsym", DLC.iundefsym);
+ W.printNumber("nundefsym", DLC.nundefsym);
+ W.printNumber("tocoff", DLC.tocoff);
+ W.printNumber("ntoc", DLC.ntoc);
+ W.printNumber("modtaboff", DLC.modtaboff);
+ W.printNumber("nmodtab", DLC.nmodtab);
+ W.printNumber("extrefsymoff", DLC.extrefsymoff);
+ W.printNumber("nextrefsyms", DLC.nextrefsyms);
+ W.printNumber("indirectsymoff", DLC.indirectsymoff);
+ W.printNumber("nindirectsyms", DLC.nindirectsyms);
+ W.printNumber("extreloff", DLC.extreloff);
+ W.printNumber("nextrel", DLC.nextrel);
+ W.printNumber("locreloff", DLC.locreloff);
+ W.printNumber("nlocrel", DLC.nlocrel);
+ }
+ }
+}
+
+void MachODumper::printMachOSegment() {
+ for (const auto &Load : Obj->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT || Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachOSegment MOSegment;
+ getSegment(Obj, Load, MOSegment);
+ DictScope Group(W, "Segment");
+ W.printString("Cmd", MOSegment.CmdName);
+ W.printString("Name", MOSegment.SegName);
+ W.printNumber("Size", MOSegment.cmdsize);
+ W.printHex("vmaddr", MOSegment.vmaddr);
+ W.printHex("vmsize", MOSegment.vmsize);
+ W.printNumber("fileoff", MOSegment.fileoff);
+ W.printNumber("filesize", MOSegment.filesize);
+ W.printString("maxprot", getMask(MOSegment.maxprot));
+ W.printString("initprot", getMask(MOSegment.initprot));
+ W.printNumber("nsects", MOSegment.nsects);
+ W.printHex("flags", MOSegment.flags);
+ }
+ }
+}
+
+void MachODumper::printMachOIndirectSymbols() {
+ for (const auto &Load : Obj->load_commands()) {
+ if (Load.C.cmd == MachO::LC_DYSYMTAB) {
+ MachO::dysymtab_command DLC = Obj->getDysymtabLoadCommand();
+ DictScope Group(W, "Indirect Symbols");
+ W.printNumber("Number", DLC.nindirectsyms);
+ ListScope D(W, "Symbols");
+ for (unsigned i = 0; i < DLC.nindirectsyms; ++i) {
+ DictScope Group(W, "Entry");
+ W.printNumber("Entry Index", i);
+ W.printHex("Symbol Index", Obj->getIndirectSymbolTableEntry(DLC, i));
+ }
+ }
+ }
+}
+
+void MachODumper::printMachOLinkerOptions() {
+ for (const auto &Load : Obj->load_commands()) {
+ if (Load.C.cmd == MachO::LC_LINKER_OPTION) {
+ MachO::linker_option_command LOLC = Obj->getLinkerOptionLoadCommand(Load);
+ DictScope Group(W, "Linker Options");
+ W.printNumber("Size", LOLC.cmdsize);
+ ListScope D(W, "Strings");
+ uint64_t DataSize = LOLC.cmdsize - sizeof(MachO::linker_option_command);
+ const char *P = Load.Ptr + sizeof(MachO::linker_option_command);
+ StringRef Data(P, DataSize);
+ for (unsigned i = 0; i < LOLC.count; ++i) {
+ std::pair<StringRef,StringRef> Split = Data.split('\0');
+ W.printString("Value", Split.first);
+ Data = Split.second;
+ }
+ }
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/ObjDumper.cpp b/contrib/libs/llvm14/tools/llvm-readobj/ObjDumper.cpp
new file mode 100644
index 00000000000..6dde3725b4d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/ObjDumper.cpp
@@ -0,0 +1,216 @@
+//===-- ObjDumper.cpp - Base dumper class -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements ObjDumper.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ObjDumper.h"
+#include "llvm-readobj.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/raw_ostream.h"
+#include <map>
+
+namespace llvm {
+
+static inline Error createError(const Twine &Msg) {
+ return createStringError(object::object_error::parse_failed, Msg);
+}
+
+ObjDumper::ObjDumper(ScopedPrinter &Writer, StringRef ObjName) : W(Writer) {
+ // Dumper reports all non-critical errors as warnings.
+ // It does not print the same warning more than once.
+ WarningHandler = [=](const Twine &Msg) {
+ if (Warnings.insert(Msg.str()).second)
+ reportWarning(createError(Msg), ObjName);
+ return Error::success();
+ };
+}
+
+ObjDumper::~ObjDumper() {}
+
+void ObjDumper::reportUniqueWarning(Error Err) const {
+ reportUniqueWarning(toString(std::move(Err)));
+}
+
+void ObjDumper::reportUniqueWarning(const Twine &Msg) const {
+ cantFail(WarningHandler(Msg),
+ "WarningHandler should always return ErrorSuccess");
+}
+
+static void printAsPrintable(raw_ostream &W, const uint8_t *Start, size_t Len) {
+ for (size_t i = 0; i < Len; i++)
+ W << (isPrint(Start[i]) ? static_cast<char>(Start[i]) : '.');
+}
+
+void ObjDumper::printAsStringList(StringRef StringContent,
+ size_t StringDataOffset) {
+ size_t StrSize = StringContent.size();
+ if (StrSize == 0)
+ return;
+ if (StrSize < StringDataOffset) {
+ reportUniqueWarning("offset (0x" + Twine::utohexstr(StringDataOffset) +
+ ") is past the end of the contents (size 0x" +
+ Twine::utohexstr(StrSize) + ")");
+ return;
+ }
+
+ const uint8_t *StrContent = StringContent.bytes_begin();
+ // Some formats contain additional metadata at the start which should not be
+ // interpreted as strings. Skip these bytes, but account for them in the
+ // string offsets.
+ const uint8_t *CurrentWord = StrContent + StringDataOffset;
+ const uint8_t *StrEnd = StringContent.bytes_end();
+
+ while (CurrentWord <= StrEnd) {
+ size_t WordSize = strnlen(reinterpret_cast<const char *>(CurrentWord),
+ StrEnd - CurrentWord);
+ if (!WordSize) {
+ CurrentWord++;
+ continue;
+ }
+ W.startLine() << format("[%6tx] ", CurrentWord - StrContent);
+ printAsPrintable(W.startLine(), CurrentWord, WordSize);
+ W.startLine() << '\n';
+ CurrentWord += WordSize + 1;
+ }
+}
+
+void ObjDumper::printFileSummary(StringRef FileStr, object::ObjectFile &Obj,
+ ArrayRef<std::string> InputFilenames,
+ const object::Archive *A) {
+ W.startLine() << "\n";
+ W.printString("File", FileStr);
+ W.printString("Format", Obj.getFileFormatName());
+ W.printString("Arch", Triple::getArchTypeName(Obj.getArch()));
+ W.printString("AddressSize",
+ std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress())));
+ this->printLoadName();
+}
+
+static std::vector<object::SectionRef>
+getSectionRefsByNameOrIndex(const object::ObjectFile &Obj,
+ ArrayRef<std::string> Sections) {
+ std::vector<object::SectionRef> Ret;
+ std::map<std::string, bool> SecNames;
+ std::map<unsigned, bool> SecIndices;
+ unsigned SecIndex;
+ for (StringRef Section : Sections) {
+ if (!Section.getAsInteger(0, SecIndex))
+ SecIndices.emplace(SecIndex, false);
+ else
+ SecNames.emplace(std::string(Section), false);
+ }
+
+ SecIndex = Obj.isELF() ? 0 : 1;
+ for (object::SectionRef SecRef : Obj.sections()) {
+ StringRef SecName = unwrapOrError(Obj.getFileName(), SecRef.getName());
+ auto NameIt = SecNames.find(std::string(SecName));
+ if (NameIt != SecNames.end())
+ NameIt->second = true;
+ auto IndexIt = SecIndices.find(SecIndex);
+ if (IndexIt != SecIndices.end())
+ IndexIt->second = true;
+ if (NameIt != SecNames.end() || IndexIt != SecIndices.end())
+ Ret.push_back(SecRef);
+ SecIndex++;
+ }
+
+ for (const std::pair<const std::string, bool> &S : SecNames)
+ if (!S.second)
+ reportWarning(
+ createError(formatv("could not find section '{0}'", S.first).str()),
+ Obj.getFileName());
+
+ for (std::pair<unsigned, bool> S : SecIndices)
+ if (!S.second)
+ reportWarning(
+ createError(formatv("could not find section {0}", S.first).str()),
+ Obj.getFileName());
+
+ return Ret;
+}
+
+void ObjDumper::printSectionsAsString(const object::ObjectFile &Obj,
+ ArrayRef<std::string> Sections) {
+ bool First = true;
+ for (object::SectionRef Section :
+ getSectionRefsByNameOrIndex(Obj, Sections)) {
+ StringRef SectionName = unwrapOrError(Obj.getFileName(), Section.getName());
+
+ if (!First)
+ W.startLine() << '\n';
+ First = false;
+ W.startLine() << "String dump of section '" << SectionName << "':\n";
+
+ StringRef SectionContent =
+ unwrapOrError(Obj.getFileName(), Section.getContents());
+ printAsStringList(SectionContent);
+ }
+}
+
+void ObjDumper::printSectionsAsHex(const object::ObjectFile &Obj,
+ ArrayRef<std::string> Sections) {
+ bool First = true;
+ for (object::SectionRef Section :
+ getSectionRefsByNameOrIndex(Obj, Sections)) {
+ StringRef SectionName = unwrapOrError(Obj.getFileName(), Section.getName());
+
+ if (!First)
+ W.startLine() << '\n';
+ First = false;
+ W.startLine() << "Hex dump of section '" << SectionName << "':\n";
+
+ StringRef SectionContent =
+ unwrapOrError(Obj.getFileName(), Section.getContents());
+ const uint8_t *SecContent = SectionContent.bytes_begin();
+ const uint8_t *SecEnd = SecContent + SectionContent.size();
+
+ for (const uint8_t *SecPtr = SecContent; SecPtr < SecEnd; SecPtr += 16) {
+ const uint8_t *TmpSecPtr = SecPtr;
+ uint8_t i;
+ uint8_t k;
+
+ W.startLine() << format_hex(Section.getAddress() + (SecPtr - SecContent),
+ 10);
+ W.startLine() << ' ';
+ for (i = 0; TmpSecPtr < SecEnd && i < 4; ++i) {
+ for (k = 0; TmpSecPtr < SecEnd && k < 4; k++, TmpSecPtr++) {
+ uint8_t Val = *(reinterpret_cast<const uint8_t *>(TmpSecPtr));
+ W.startLine() << format_hex_no_prefix(Val, 2);
+ }
+ W.startLine() << ' ';
+ }
+
+ // We need to print the correct amount of spaces to match the format.
+ // We are adding the (4 - i) last rows that are 8 characters each.
+ // Then, the (4 - i) spaces that are in between the rows.
+ // Least, if we cut in a middle of a row, we add the remaining characters,
+ // which is (8 - (k * 2)).
+ if (i < 4)
+ W.startLine() << format("%*c", (4 - i) * 8 + (4 - i), ' ');
+ if (k < 4)
+ W.startLine() << format("%*c", 8 - k * 2, ' ');
+
+ TmpSecPtr = SecPtr;
+ for (i = 0; TmpSecPtr + i < SecEnd && i < 16; ++i)
+ W.startLine() << (isPrint(TmpSecPtr[i])
+ ? static_cast<char>(TmpSecPtr[i])
+ : '.');
+
+ W.startLine() << '\n';
+ }
+ }
+}
+
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/ObjDumper.h b/contrib/libs/llvm14/tools/llvm-readobj/ObjDumper.h
new file mode 100644
index 00000000000..a09a243d381
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/ObjDumper.h
@@ -0,0 +1,167 @@
+//===-- ObjDumper.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_READOBJ_OBJDUMPER_H
+#define LLVM_TOOLS_LLVM_READOBJ_OBJDUMPER_H
+
+#include <memory>
+#include <system_error>
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+
+#include <unordered_set>
+
+namespace llvm {
+namespace object {
+class Archive;
+class COFFImportFile;
+class ObjectFile;
+class XCOFFObjectFile;
+class ELFObjectFileBase;
+}
+namespace codeview {
+class GlobalTypeTableBuilder;
+class MergingTypeTableBuilder;
+} // namespace codeview
+
+class ScopedPrinter;
+
+class ObjDumper {
+public:
+ ObjDumper(ScopedPrinter &Writer, StringRef ObjName);
+ virtual ~ObjDumper();
+
+ virtual bool canDumpContent() { return true; }
+
+ virtual void printFileSummary(StringRef FileStr, object::ObjectFile &Obj,
+ ArrayRef<std::string> InputFilenames,
+ const object::Archive *A);
+ virtual void printFileHeaders() = 0;
+ virtual void printSectionHeaders() = 0;
+ virtual void printRelocations() = 0;
+ virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) {
+ if (PrintSymbols)
+ printSymbols();
+ if (PrintDynamicSymbols)
+ printDynamicSymbols();
+ }
+ virtual void printProgramHeaders(bool PrintProgramHeaders,
+ cl::boolOrDefault PrintSectionMapping) {
+ if (PrintProgramHeaders)
+ printProgramHeaders();
+ if (PrintSectionMapping == cl::BOU_TRUE)
+ printSectionMapping();
+ }
+
+ virtual void printUnwindInfo() = 0;
+
+ // Only implemented for ELF at this time.
+ virtual void printDependentLibs() {}
+ virtual void printDynamicRelocations() { }
+ virtual void printDynamicTable() { }
+ virtual void printNeededLibraries() { }
+ virtual void printSectionAsHex(StringRef SectionName) {}
+ virtual void printHashTable() { }
+ virtual void printGnuHashTable() {}
+ virtual void printHashSymbols() {}
+ virtual void printLoadName() {}
+ virtual void printVersionInfo() {}
+ virtual void printGroupSections() {}
+ virtual void printHashHistograms() {}
+ virtual void printCGProfile() {}
+ virtual void printBBAddrMaps() {}
+ virtual void printAddrsig() {}
+ virtual void printNotes() {}
+ virtual void printELFLinkerOptions() {}
+ virtual void printStackSizes() {}
+ virtual void printSectionDetails() {}
+ virtual void printArchSpecificInfo() {}
+
+ // Only implemented for PE/COFF.
+ virtual void printCOFFImports() { }
+ virtual void printCOFFExports() { }
+ virtual void printCOFFDirectives() { }
+ virtual void printCOFFBaseReloc() { }
+ virtual void printCOFFDebugDirectory() { }
+ virtual void printCOFFTLSDirectory() {}
+ virtual void printCOFFResources() {}
+ virtual void printCOFFLoadConfig() { }
+ virtual void printCodeViewDebugInfo() { }
+ virtual void
+ mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs,
+ llvm::codeview::MergingTypeTableBuilder &CVTypes,
+ llvm::codeview::GlobalTypeTableBuilder &GlobalCVIDs,
+ llvm::codeview::GlobalTypeTableBuilder &GlobalCVTypes,
+ bool GHash) {}
+
+ // Only implement for XCOFF
+ virtual void printAuxiliaryHeader() {}
+
+ // Only implemented for MachO.
+ virtual void printMachODataInCode() { }
+ virtual void printMachOVersionMin() { }
+ virtual void printMachODysymtab() { }
+ virtual void printMachOSegment() { }
+ virtual void printMachOIndirectSymbols() { }
+ virtual void printMachOLinkerOptions() { }
+
+ // Currently only implemented for XCOFF.
+ virtual void printStringTable() { }
+
+ virtual void printStackMap() const = 0;
+
+ void printAsStringList(StringRef StringContent, size_t StringDataOffset = 0);
+
+ void printSectionsAsString(const object::ObjectFile &Obj,
+ ArrayRef<std::string> Sections);
+ void printSectionsAsHex(const object::ObjectFile &Obj,
+ ArrayRef<std::string> Sections);
+
+ std::function<Error(const Twine &Msg)> WarningHandler;
+ void reportUniqueWarning(Error Err) const;
+ void reportUniqueWarning(const Twine &Msg) const;
+
+protected:
+ ScopedPrinter &W;
+
+private:
+ virtual void printSymbols() {}
+ virtual void printDynamicSymbols() {}
+ virtual void printProgramHeaders() {}
+ virtual void printSectionMapping() {}
+
+ std::unordered_set<std::string> Warnings;
+};
+
+std::unique_ptr<ObjDumper> createCOFFDumper(const object::COFFObjectFile &Obj,
+ ScopedPrinter &Writer);
+
+std::unique_ptr<ObjDumper> createELFDumper(const object::ELFObjectFileBase &Obj,
+ ScopedPrinter &Writer);
+
+std::unique_ptr<ObjDumper> createMachODumper(const object::MachOObjectFile &Obj,
+ ScopedPrinter &Writer);
+
+std::unique_ptr<ObjDumper> createWasmDumper(const object::WasmObjectFile &Obj,
+ ScopedPrinter &Writer);
+
+std::unique_ptr<ObjDumper> createXCOFFDumper(const object::XCOFFObjectFile &Obj,
+ ScopedPrinter &Writer);
+
+void dumpCOFFImportFile(const object::COFFImportFile *File,
+ ScopedPrinter &Writer);
+
+void dumpCodeViewMergedTypes(ScopedPrinter &Writer,
+ ArrayRef<ArrayRef<uint8_t>> IpiRecords,
+ ArrayRef<ArrayRef<uint8_t>> TpiRecords);
+
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/Opts.td b/contrib/libs/llvm14/tools/llvm-readobj/Opts.td
new file mode 100644
index 00000000000..d0f273fa60c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/Opts.td
@@ -0,0 +1,133 @@
+include "llvm/Option/OptParser.td"
+
+class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>;
+class FF<string name, string help> : Flag<["--"], name>, HelpText<help>;
+
+multiclass BB<string name, string help1, string help2> {
+ def NAME: Flag<["--"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
+}
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["--"], name #"=">, HelpText<help>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def addrsig : FF<"addrsig", "Display address-significance table">;
+def all : FF<"all", "Equivalent to setting: --file-header, --program-headers, --section-headers, "
+ "--symbols, --relocations, --dynamic-table, --notes, --version-info, --unwind, "
+ "--section-groups and --histogram">;
+def arch_specific : FF<"arch-specific", "Display architecture-specific information">;
+def bb_addr_map : FF<"bb-addr-map", "Display the BB address map section">;
+def cg_profile : FF<"cg-profile", "Display call graph profile section">;
+defm demangle : BB<"demangle", "Demangle symbol names", "Do not demangle symbol names (default)">;
+def dependent_libraries : FF<"dependent-libraries", "Display the dependent libraries section">;
+def dyn_relocations : FF<"dyn-relocations", "Display the dynamic relocation entries in the file">;
+def dyn_syms : FF<"dyn-syms", "Display the dynamic symbol table">;
+def expand_relocs : FF<"expand-relocs", "Expand each shown relocation to multiple lines">;
+def file_header : FF<"file-header", "Display file header">;
+def headers : FF<"headers", "Equivalent to setting: --file-header, --program-headers, --section-headers">;
+defm hex_dump : Eq<"hex-dump", "Display the specified section(s) as hexadecimal bytes">, MetaVarName<"<name or index>">;
+def pretty_print : FF<"pretty-print", "Pretty print JSON output">;
+def relocs : FF<"relocs", "Display the relocation entries in the file">;
+def section_data : FF<"section-data", "Display section data for each section shown. This option has no effect for GNU style output">;
+def section_details : FF<"section-details", "Display the section details">;
+def section_headers : FF<"section-headers", "Display section headers">;
+def section_mapping : FF<"section-mapping", "Display the section to segment mapping">;
+def section_mapping_EQ_false : FF<"section-mapping=false", "Don't display the section to segment mapping">, Flags<[HelpHidden]>;
+def section_relocations : FF<"section-relocations", "Display relocations for each section shown. This option has no effect for GNU style output">;
+def section_symbols : FF<"section-symbols", "Display symbols for each section shown. This option has no effect for GNU style output">;
+def stack_sizes : FF<"stack-sizes", "Display contents of all stack sizes sections. This option has no effect for GNU style output">;
+def stackmap : FF<"stackmap", "Display contents of stackmap section">;
+defm string_dump : Eq<"string-dump", "Display the specified section(s) as a list of strings">, MetaVarName<"<name or index>">;
+def string_table : FF<"string-table", "Display the string table (only for XCOFF now)">;
+def symbols : FF<"symbols", "Display the symbol table. Also display the dynamic symbol table when using GNU output style for ELF">;
+def unwind : FF<"unwind", "Display unwind information">;
+
+// ELF specific options.
+def grp_elf : OptionGroup<"kind">, HelpText<"OPTIONS (ELF specific)">;
+def dynamic_table : FF<"dynamic-table", "Display the dynamic section table">, Group<grp_elf>;
+def elf_linker_options : FF<"elf-linker-options", "Display the .linker-options section">, Group<grp_elf>;
+defm elf_output_style : Eq<"elf-output-style", "Specify ELF dump style: LLVM, GNU, JSON">, Group<grp_elf>;
+def histogram : FF<"histogram", "Display bucket list histogram for hash sections">, Group<grp_elf>;
+def section_groups : FF<"section-groups", "Display section groups">, Group<grp_elf>;
+def gnu_hash_table : FF<"gnu-hash-table", "Display the GNU hash table for dynamic symbols">, Group<grp_elf>;
+def hash_symbols : FF<"hash-symbols", "Display the dynamic symbols derived from the hash section">, Group<grp_elf>;
+def hash_table : FF<"hash-table", "Display .hash section">, Group<grp_elf>;
+def needed_libs : FF<"needed-libs", "Display the needed libraries">, Group<grp_elf>;
+def notes : FF<"notes", "Display notes">, Group<grp_elf>;
+def program_headers : FF<"program-headers", "Display program headers">, Group<grp_elf>;
+def raw_relr : FF<"raw-relr", "Do not decode relocations in SHT_RELR section, display raw contents">, Group<grp_elf>;
+def version_info : FF<"version-info", "Display version sections">, Group<grp_elf>;
+
+// Mach-O specific options.
+def grp_mach_o : OptionGroup<"kind">, HelpText<"OPTIONS (Mach-O specific)">;
+def macho_data_in_code : FF<"macho-data-in-code", "Display Data in Code command">, Group<grp_mach_o>;
+def macho_dysymtab : FF<"macho-dysymtab", "Display Dysymtab command">, Group<grp_mach_o>;
+def macho_indirect_symbols : FF<"macho-indirect-symbols", "Display indirect symbols">, Group<grp_mach_o>;
+def macho_linker_options : FF<"macho-linker-options", "Display linker options">, Group<grp_mach_o>;
+def macho_segment : FF<"macho-segment", "Display Segment command">, Group<grp_mach_o>;
+def macho_version_min : FF<"macho-version-min", "Display version min command">, Group<grp_mach_o>;
+
+// PE/COFF specific options.
+def grp_coff : OptionGroup<"kind">, HelpText<"OPTIONS (PE/COFF specific)">;
+def codeview : FF<"codeview", "Display CodeView debug information">, Group<grp_coff>;
+def codeview_ghash : FF<"codeview-ghash", "Enable global hashing for CodeView type stream de-duplication">, Group<grp_coff>;
+def codeview_merged_types : FF<"codeview-merged-types", "Display the merged CodeView type stream">, Group<grp_coff>;
+def codeview_subsection_bytes : FF<"codeview-subsection-bytes", "Dump raw contents of codeview debug sections and records">, Group<grp_coff>;
+def coff_basereloc : FF<"coff-basereloc", "Display .reloc section">, Group<grp_coff>;
+def coff_debug_directory : FF<"coff-debug-directory", "Display debug directory">, Group<grp_coff>;
+def coff_directives : FF<"coff-directives", "Display .drectve section">, Group<grp_coff>;
+def coff_exports : FF<"coff-exports", "Display export table">, Group<grp_coff>;
+def coff_imports : FF<"coff-imports", "Display import table">, Group<grp_coff>;
+def coff_load_config : FF<"coff-load-config", "Display load config">, Group<grp_coff>;
+def coff_resources : FF<"coff-resources", "Display .rsrc section">, Group<grp_coff>;
+def coff_tls_directory : FF<"coff-tls-directory", "Display TLS directory">, Group<grp_coff>;
+
+// XCOFF specific options.
+def grp_xcoff : OptionGroup<"kind">, HelpText<"OPTIONS (XCOFF specific)">;
+def auxiliary_header : FF<"auxiliary-header" , "display the auxiliary header">, Group<grp_xcoff>;
+
+def help : FF<"help", "Display this help">;
+def version : FF<"version", "Display the version">;
+
+// Ignored for GNU readelf compatibility.
+def wide : FF<"wide", "Ignored for GNU readelf compatibility">;
+def : F<"W", "Ignored for GNU readelf compatibility">, Alias<wide>;
+
+// Traditional llvm-readobj Aliases.
+def : Flag<["--"], "dt">, Alias<dyn_syms>, HelpText<"Alias for --dyn-syms">;
+def : Flag<["--"], "sd">, Alias<section_data>, HelpText<"Alias for --section-data">;
+def : Flag<["--"], "st">, Alias<section_symbols>, HelpText<"Alias for --section-symbols">;
+def : Flag<["--"], "sr">, Alias<section_relocations>, HelpText<"Alias for --section-relocations">;
+
+// Aliases.
+def : FF<"dyn-symbols", "Alias for --dyn-syms">, Alias<dyn_syms>;
+def : FF<"dynamic", "Alias for --dynamic-table">, Alias<dynamic_table>;
+def : FF<"elf-cg-profile", "Alias for --cg-profile">, Alias<cg_profile>, Flags<[HelpHidden]>;
+def : FF<"elf-hash-histogram", "Alias for --histogram">, Alias<histogram>, Flags<[HelpHidden]>;
+def : FF<"elf-section-groups", "Alias for --section-groups">, Alias<section_groups>, Flags<[HelpHidden]>;
+def : FF<"file-headers", "Alias for --file-header">, Alias<file_header>, Flags<[HelpHidden]>;
+def : FF<"relocations", "Alias for --relocs">, Alias<relocs>;
+def : FF<"sections", "Alias for --section-headers">, Alias<section_headers>;
+def : FF<"segments", "Alias for --program-headers">, Alias<program_headers>, Group<grp_elf>;
+def : FF<"syms", "Alias for --symbols">, Alias<symbols>;
+
+def : F<"A", "Alias for --arch-specific">, Alias<arch_specific>;
+def : F<"a", "Alias for --all">, Alias<all>;
+def : F<"C", "Alias for --demangle">, Alias<demangle>;
+def : F<"d", "Alias for --dynamic-table">, Alias<dynamic_table>, Group<grp_elf>;
+def : F<"e", "Alias for --headers">, Alias<headers>;
+def : F<"g", "Alias for --section-groups">, Alias<section_groups>, Group<grp_elf>;
+def : F<"h", "Alias for --file-header">, Alias<file_header>;
+def : F<"I", "Alias for --histogram">, Alias<histogram>, Group<grp_elf>;
+def : F<"l", "Alias for --program-headers">, Alias<program_headers>;
+def : F<"n", "Alias for --notes">, Alias<notes>;
+def : JoinedOrSeparate<["-"], "p">, Alias<string_dump_EQ>, HelpText<"Alias for --string-dump">, MetaVarName<"<name or index>">;
+def : F<"r", "Alias for --relocs">, Alias<relocs>;
+def : F<"S", "Alias for --section-headers">, Alias<section_headers>;
+def : F<"s", "Alias for --symbols">, Alias<symbols>;
+def : F<"t", "Alias for --section-details">, Alias<section_details>;
+def : F<"u", "Alias for --unwind">, Alias<unwind>;
+def : F<"V", "Alias for --version-info">, Alias<version_info>, Group<grp_elf>;
+def : JoinedOrSeparate<["-"], "x">, Alias<hex_dump_EQ>, HelpText<"Alias for --hex-dump">, MetaVarName<"<name or index>">;
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/StackMapPrinter.h b/contrib/libs/llvm14/tools/llvm-readobj/StackMapPrinter.h
new file mode 100644
index 00000000000..ef757564026
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/StackMapPrinter.h
@@ -0,0 +1,81 @@
+//===-------- StackMapPrinter.h - Pretty-print stackmaps --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_READOBJ_STACKMAPPRINTER_H
+#define LLVM_TOOLS_LLVM_READOBJ_STACKMAPPRINTER_H
+
+#include "llvm/Object/StackMapParser.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+namespace llvm {
+
+// Pretty print a stackmap to the given ostream.
+template <typename StackMapParserT>
+void prettyPrintStackMap(ScopedPrinter &W, const StackMapParserT &SMP) {
+
+ W.printNumber("LLVM StackMap Version", SMP.getVersion());
+ W.printNumber("Num Functions", SMP.getNumFunctions());
+
+ // Functions:
+ for (const auto &F : SMP.functions())
+ W.startLine() << " Function address: " << F.getFunctionAddress()
+ << ", stack size: " << F.getStackSize()
+ << ", callsite record count: " << F.getRecordCount() << "\n";
+
+ // Constants:
+ W.printNumber("Num Constants", SMP.getNumConstants());
+ unsigned ConstantIndex = 0;
+ for (const auto &C : SMP.constants())
+ W.startLine() << " #" << ++ConstantIndex << ": " << C.getValue() << "\n";
+
+ // Records:
+ W.printNumber("Num Records", SMP.getNumRecords());
+ for (const auto &R : SMP.records()) {
+ W.startLine() << " Record ID: " << R.getID()
+ << ", instruction offset: " << R.getInstructionOffset()
+ << "\n";
+ W.startLine() << " " << R.getNumLocations() << " locations:\n";
+
+ unsigned LocationIndex = 0;
+ for (const auto &Loc : R.locations()) {
+ raw_ostream &OS = W.startLine();
+ OS << " #" << ++LocationIndex << ": ";
+ switch (Loc.getKind()) {
+ case StackMapParserT::LocationKind::Register:
+ OS << "Register R#" << Loc.getDwarfRegNum();
+ break;
+ case StackMapParserT::LocationKind::Direct:
+ OS << "Direct R#" << Loc.getDwarfRegNum() << " + " << Loc.getOffset();
+ break;
+ case StackMapParserT::LocationKind::Indirect:
+ OS << "Indirect [R#" << Loc.getDwarfRegNum() << " + " << Loc.getOffset()
+ << "]";
+ break;
+ case StackMapParserT::LocationKind::Constant:
+ OS << "Constant " << Loc.getSmallConstant();
+ break;
+ case StackMapParserT::LocationKind::ConstantIndex:
+ OS << "ConstantIndex #" << Loc.getConstantIndex() << " ("
+ << SMP.getConstant(Loc.getConstantIndex()).getValue() << ")";
+ break;
+ }
+ OS << ", size: " << Loc.getSizeInBytes() << "\n";
+ }
+
+ raw_ostream &OS = W.startLine();
+ OS << " " << R.getNumLiveOuts() << " live-outs: [ ";
+ for (const auto &LO : R.liveouts())
+ OS << "R#" << LO.getDwarfRegNum() << " ("
+ << LO.getSizeInBytes() << "-bytes) ";
+ OS << "]\n";
+ }
+}
+
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/WasmDumper.cpp b/contrib/libs/llvm14/tools/llvm-readobj/WasmDumper.cpp
new file mode 100644
index 00000000000..b4d72601643
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/WasmDumper.cpp
@@ -0,0 +1,251 @@
+//===-- WasmDumper.cpp - Wasm-specific object file dumper -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Wasm-specific dumper for llvm-readobj.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ObjDumper.h"
+#include "llvm-readobj.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+using namespace llvm;
+using namespace object;
+
+namespace {
+
+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), ENUM_ENTRY(TAG), ENUM_ENTRY(TABLE),
+#undef ENUM_ENTRY
+};
+
+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(TAG), ENUM_ENTRY(EXPORT),
+ ENUM_ENTRY(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE),
+ ENUM_ENTRY(DATA), ENUM_ENTRY(DATACOUNT),
+#undef ENUM_ENTRY
+};
+
+const EnumEntry<unsigned> WasmSymbolFlags[] = {
+#define ENUM_ENTRY(X) \
+ { #X, wasm::WASM_SYMBOL_##X }
+ ENUM_ENTRY(BINDING_GLOBAL),
+ ENUM_ENTRY(BINDING_WEAK),
+ ENUM_ENTRY(BINDING_LOCAL),
+ ENUM_ENTRY(VISIBILITY_DEFAULT),
+ ENUM_ENTRY(VISIBILITY_HIDDEN),
+ ENUM_ENTRY(UNDEFINED),
+ ENUM_ENTRY(EXPORTED),
+ ENUM_ENTRY(EXPLICIT_NAME),
+ ENUM_ENTRY(NO_STRIP),
+#undef ENUM_ENTRY
+};
+
+class WasmDumper : public ObjDumper {
+public:
+ WasmDumper(const WasmObjectFile *Obj, ScopedPrinter &Writer)
+ : ObjDumper(Writer, Obj->getFileName()), Obj(Obj) {}
+
+ void printFileHeaders() override;
+ void printSectionHeaders() override;
+ void printRelocations() override;
+ void printUnwindInfo() override { llvm_unreachable("unimplemented"); }
+ void printStackMap() const override { llvm_unreachable("unimplemented"); }
+
+protected:
+ void printSymbol(const SymbolRef &Sym);
+ void printRelocation(const SectionRef &Section, const RelocationRef &Reloc);
+
+private:
+ void printSymbols() override;
+ void printDynamicSymbols() override { llvm_unreachable("unimplemented"); }
+
+ const WasmObjectFile *Obj;
+};
+
+void WasmDumper::printFileHeaders() {
+ W.printHex("Version", Obj->getHeader().Version);
+}
+
+void WasmDumper::printRelocation(const SectionRef &Section,
+ const RelocationRef &Reloc) {
+ SmallString<64> RelocTypeName;
+ uint64_t RelocType = Reloc.getType();
+ Reloc.getTypeName(RelocTypeName);
+ const wasm::WasmRelocation &WasmReloc = Obj->getWasmRelocation(Reloc);
+
+ StringRef SymName;
+ symbol_iterator SI = Reloc.getSymbol();
+ if (SI != Obj->symbol_end())
+ SymName = unwrapOrError(Obj->getFileName(), SI->getName());
+
+ bool HasAddend = wasm::relocTypeHasAddend(static_cast<uint32_t>(RelocType));
+
+ if (opts::ExpandRelocs) {
+ DictScope Group(W, "Relocation");
+ W.printNumber("Type", RelocTypeName, RelocType);
+ W.printHex("Offset", Reloc.getOffset());
+ if (!SymName.empty())
+ W.printString("Symbol", SymName);
+ else
+ W.printHex("Index", WasmReloc.Index);
+ if (HasAddend)
+ W.printNumber("Addend", WasmReloc.Addend);
+ } else {
+ raw_ostream &OS = W.startLine();
+ OS << W.hex(Reloc.getOffset()) << " " << RelocTypeName << " ";
+ if (!SymName.empty())
+ OS << SymName;
+ else
+ OS << WasmReloc.Index;
+ if (HasAddend)
+ OS << " " << WasmReloc.Addend;
+ OS << "\n";
+ }
+}
+
+void WasmDumper::printRelocations() {
+ ListScope D(W, "Relocations");
+
+ int SectionNumber = 0;
+ for (const SectionRef &Section : Obj->sections()) {
+ bool PrintedGroup = false;
+ StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName());
+
+ ++SectionNumber;
+
+ for (const RelocationRef &Reloc : Section.relocations()) {
+ if (!PrintedGroup) {
+ W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n";
+ W.indent();
+ PrintedGroup = true;
+ }
+
+ printRelocation(Section, Reloc);
+ }
+
+ if (PrintedGroup) {
+ W.unindent();
+ W.startLine() << "}\n";
+ }
+ }
+}
+
+void WasmDumper::printSymbols() {
+ ListScope Group(W, "Symbols");
+
+ for (const SymbolRef &Symbol : Obj->symbols())
+ printSymbol(Symbol);
+}
+
+void WasmDumper::printSectionHeaders() {
+ ListScope Group(W, "Sections");
+ for (const SectionRef &Section : Obj->sections()) {
+ const WasmSection &WasmSec = Obj->getWasmSection(Section);
+ DictScope SectionD(W, "Section");
+ W.printEnum("Type", WasmSec.Type, makeArrayRef(WasmSectionTypes));
+ W.printNumber("Size", static_cast<uint64_t>(WasmSec.Content.size()));
+ W.printNumber("Offset", WasmSec.Offset);
+ switch (WasmSec.Type) {
+ case wasm::WASM_SEC_CUSTOM:
+ W.printString("Name", WasmSec.Name);
+ if (WasmSec.Name == "linking") {
+ const wasm::WasmLinkingData &LinkingData = Obj->linkingData();
+ if (!LinkingData.InitFunctions.empty()) {
+ ListScope Group(W, "InitFunctions");
+ for (const wasm::WasmInitFunc &F : LinkingData.InitFunctions)
+ W.startLine() << F.Symbol << " (priority=" << F.Priority << ")\n";
+ }
+ }
+ break;
+ case wasm::WASM_SEC_DATA: {
+ ListScope Group(W, "Segments");
+ for (const WasmSegment &Segment : Obj->dataSegments()) {
+ const wasm::WasmDataSegment &Seg = Segment.Data;
+ DictScope Group(W, "Segment");
+ if (!Seg.Name.empty())
+ W.printString("Name", Seg.Name);
+ W.printNumber("Size", static_cast<uint64_t>(Seg.Content.size()));
+ if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I32_CONST)
+ W.printNumber("Offset", Seg.Offset.Value.Int32);
+ else if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I64_CONST)
+ W.printNumber("Offset", Seg.Offset.Value.Int64);
+ else if (Seg.Offset.Opcode == wasm::WASM_OPCODE_GLOBAL_GET) {
+ ListScope Group(W, "Offset");
+ W.printNumber("Global", Seg.Offset.Value.Global);
+ } else
+ llvm_unreachable("unknown init expr opcode");
+ }
+ break;
+ }
+ case wasm::WASM_SEC_MEMORY:
+ ListScope Group(W, "Memories");
+ for (const wasm::WasmLimits &Memory : Obj->memories()) {
+ DictScope Group(W, "Memory");
+ W.printNumber("MinPages", Memory.Minimum);
+ if (Memory.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) {
+ W.printNumber("MaxPages", WasmSec.Offset);
+ }
+ }
+ break;
+ }
+
+ if (opts::SectionRelocations) {
+ ListScope D(W, "Relocations");
+ for (const RelocationRef &Reloc : Section.relocations())
+ printRelocation(Section, Reloc);
+ }
+
+ if (opts::SectionData) {
+ W.printBinaryBlock("SectionData", WasmSec.Content);
+ }
+ }
+}
+
+void WasmDumper::printSymbol(const SymbolRef &Sym) {
+ DictScope D(W, "Symbol");
+ WasmSymbol Symbol = Obj->getWasmSymbol(Sym.getRawDataRefImpl());
+ W.printString("Name", Symbol.Info.Name);
+ W.printEnum("Type", Symbol.Info.Kind, makeArrayRef(WasmSymbolTypes));
+ W.printFlags("Flags", Symbol.Info.Flags, makeArrayRef(WasmSymbolFlags));
+
+ if (Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) {
+ if (Symbol.Info.ImportName) {
+ W.printString("ImportName", *Symbol.Info.ImportName);
+ }
+ if (Symbol.Info.ImportModule) {
+ W.printString("ImportModule", *Symbol.Info.ImportModule);
+ }
+ }
+ if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) {
+ W.printHex("ElementIndex", Symbol.Info.ElementIndex);
+ } else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) {
+ W.printHex("Offset", Symbol.Info.DataRef.Offset);
+ W.printHex("Segment", Symbol.Info.DataRef.Segment);
+ W.printHex("Size", Symbol.Info.DataRef.Size);
+ }
+}
+
+} // namespace
+
+namespace llvm {
+
+std::unique_ptr<ObjDumper> createWasmDumper(const object::WasmObjectFile &Obj,
+ ScopedPrinter &Writer) {
+ return std::make_unique<WasmDumper>(&Obj, Writer);
+}
+
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/Win64EHDumper.cpp b/contrib/libs/llvm14/tools/llvm-readobj/Win64EHDumper.cpp
new file mode 100644
index 00000000000..da964d3132e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/Win64EHDumper.cpp
@@ -0,0 +1,426 @@
+//===- Win64EHDumper.cpp - Win64 EH Printer ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Win64EHDumper.h"
+#include "llvm-readobj.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::Win64EH;
+
+const EnumEntry<unsigned> UnwindFlags[] = {
+ { "ExceptionHandler", UNW_ExceptionHandler },
+ { "TerminateHandler", UNW_TerminateHandler },
+ { "ChainInfo" , UNW_ChainInfo }
+};
+
+const EnumEntry<unsigned> UnwindOpInfo[] = {
+ { "RAX", 0 },
+ { "RCX", 1 },
+ { "RDX", 2 },
+ { "RBX", 3 },
+ { "RSP", 4 },
+ { "RBP", 5 },
+ { "RSI", 6 },
+ { "RDI", 7 },
+ { "R8", 8 },
+ { "R9", 9 },
+ { "R10", 10 },
+ { "R11", 11 },
+ { "R12", 12 },
+ { "R13", 13 },
+ { "R14", 14 },
+ { "R15", 15 }
+};
+
+static uint64_t getOffsetOfLSDA(const UnwindInfo& UI) {
+ return static_cast<const char*>(UI.getLanguageSpecificData())
+ - reinterpret_cast<const char*>(&UI);
+}
+
+static uint32_t getLargeSlotValue(ArrayRef<UnwindCode> UC) {
+ if (UC.size() < 3)
+ return 0;
+ return UC[1].FrameOffset + (static_cast<uint32_t>(UC[2].FrameOffset) << 16);
+}
+
+// Returns the name of the unwind code.
+static StringRef getUnwindCodeTypeName(uint8_t Code) {
+ switch (Code) {
+ default: llvm_unreachable("Invalid unwind code");
+ case UOP_PushNonVol: return "PUSH_NONVOL";
+ case UOP_AllocLarge: return "ALLOC_LARGE";
+ case UOP_AllocSmall: return "ALLOC_SMALL";
+ case UOP_SetFPReg: return "SET_FPREG";
+ case UOP_SaveNonVol: return "SAVE_NONVOL";
+ case UOP_SaveNonVolBig: return "SAVE_NONVOL_FAR";
+ case UOP_SaveXMM128: return "SAVE_XMM128";
+ case UOP_SaveXMM128Big: return "SAVE_XMM128_FAR";
+ case UOP_PushMachFrame: return "PUSH_MACHFRAME";
+ }
+}
+
+// Returns the name of a referenced register.
+static StringRef getUnwindRegisterName(uint8_t Reg) {
+ switch (Reg) {
+ default: llvm_unreachable("Invalid register");
+ case 0: return "RAX";
+ case 1: return "RCX";
+ case 2: return "RDX";
+ case 3: return "RBX";
+ case 4: return "RSP";
+ case 5: return "RBP";
+ case 6: return "RSI";
+ case 7: return "RDI";
+ case 8: return "R8";
+ case 9: return "R9";
+ case 10: return "R10";
+ case 11: return "R11";
+ case 12: return "R12";
+ case 13: return "R13";
+ case 14: return "R14";
+ case 15: return "R15";
+ }
+}
+
+// Calculates the number of array slots required for the unwind code.
+static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) {
+ switch (UnwindCode.getUnwindOp()) {
+ default: llvm_unreachable("Invalid unwind code");
+ case UOP_PushNonVol:
+ case UOP_AllocSmall:
+ case UOP_SetFPReg:
+ case UOP_PushMachFrame:
+ return 1;
+ case UOP_SaveNonVol:
+ case UOP_SaveXMM128:
+ return 2;
+ case UOP_SaveNonVolBig:
+ case UOP_SaveXMM128Big:
+ return 3;
+ case UOP_AllocLarge:
+ return (UnwindCode.getOpInfo() == 0) ? 2 : 3;
+ }
+}
+
+static std::error_code getSymbol(const COFFObjectFile &COFF, uint64_t VA,
+ object::SymbolRef &Sym) {
+ for (const auto &Symbol : COFF.symbols()) {
+ Expected<uint64_t> Address = Symbol.getAddress();
+ if (!Address)
+ return errorToErrorCode(Address.takeError());
+ if (*Address == VA) {
+ Sym = Symbol;
+ return std::error_code();
+ }
+ }
+ return inconvertibleErrorCode();
+}
+
+static object::SymbolRef getPreferredSymbol(const COFFObjectFile &COFF,
+ object::SymbolRef Sym,
+ uint32_t &SymbolOffset,
+ bool IsRangeEnd) {
+ // The symbol resolved by ResolveSymbol can be any internal
+ // nondescriptive symbol; try to resolve a more descriptive one.
+ COFFSymbolRef CoffSym = COFF.getCOFFSymbol(Sym);
+ if (CoffSym.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL &&
+ CoffSym.getSectionDefinition() == nullptr)
+ return Sym;
+ for (const auto &S : COFF.symbols()) {
+ COFFSymbolRef CS = COFF.getCOFFSymbol(S);
+ if (CS.getSectionNumber() == CoffSym.getSectionNumber() &&
+ CS.getValue() <= CoffSym.getValue() + SymbolOffset &&
+ CS.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL &&
+ CS.getSectionDefinition() == nullptr) {
+ uint32_t Offset = CoffSym.getValue() + SymbolOffset - CS.getValue();
+ // For the end of a range, don't pick a symbol with a zero offset;
+ // prefer a symbol with a small positive offset.
+ if (Offset <= SymbolOffset && (!IsRangeEnd || Offset > 0)) {
+ SymbolOffset = Offset;
+ Sym = S;
+ CoffSym = CS;
+ if (CS.isExternal() && SymbolOffset == 0)
+ return Sym;
+ }
+ }
+ }
+ return Sym;
+}
+
+static std::string formatSymbol(const Dumper::Context &Ctx,
+ const coff_section *Section, uint64_t Offset,
+ uint32_t Displacement,
+ bool IsRangeEnd = false) {
+ std::string Buffer;
+ raw_string_ostream OS(Buffer);
+
+ SymbolRef Symbol;
+ if (!Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData)) {
+ // We found a relocation at the given offset in the section, pointing
+ // at a symbol.
+
+ // Try to resolve label/section symbols into function names.
+ Symbol = getPreferredSymbol(Ctx.COFF, Symbol, Displacement, IsRangeEnd);
+
+ Expected<StringRef> Name = Symbol.getName();
+ if (Name) {
+ OS << *Name;
+ if (Displacement > 0)
+ OS << format(" +0x%X (0x%" PRIX64 ")", Displacement, Offset);
+ else
+ OS << format(" (0x%" PRIX64 ")", Offset);
+ return OS.str();
+ } else {
+ // TODO: Actually report errors helpfully.
+ consumeError(Name.takeError());
+ }
+ } else if (!getSymbol(Ctx.COFF, Ctx.COFF.getImageBase() + Displacement,
+ Symbol)) {
+ Expected<StringRef> Name = Symbol.getName();
+ if (Name) {
+ OS << *Name;
+ OS << format(" (0x%" PRIX64 ")", Ctx.COFF.getImageBase() + Displacement);
+ return OS.str();
+ } else {
+ consumeError(Name.takeError());
+ }
+ }
+
+ if (Displacement > 0)
+ OS << format("(0x%" PRIX64 ")", Ctx.COFF.getImageBase() + Displacement);
+ else
+ OS << format("(0x%" PRIX64 ")", Offset);
+ return OS.str();
+}
+
+static std::error_code resolveRelocation(const Dumper::Context &Ctx,
+ const coff_section *Section,
+ uint64_t Offset,
+ const coff_section *&ResolvedSection,
+ uint64_t &ResolvedAddress) {
+ SymbolRef Symbol;
+ if (std::error_code EC =
+ Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData))
+ return EC;
+
+ Expected<uint64_t> ResolvedAddressOrErr = Symbol.getAddress();
+ if (!ResolvedAddressOrErr)
+ return errorToErrorCode(ResolvedAddressOrErr.takeError());
+ ResolvedAddress = *ResolvedAddressOrErr;
+
+ Expected<section_iterator> SI = Symbol.getSection();
+ if (!SI)
+ return errorToErrorCode(SI.takeError());
+ ResolvedSection = Ctx.COFF.getCOFFSection(**SI);
+ return std::error_code();
+}
+
+static const object::coff_section *
+getSectionContaining(const COFFObjectFile &COFF, uint64_t VA) {
+ for (const auto &Section : COFF.sections()) {
+ uint64_t Address = Section.getAddress();
+ uint64_t Size = Section.getSize();
+
+ if (VA >= Address && (VA - Address) <= Size)
+ return COFF.getCOFFSection(Section);
+ }
+ return nullptr;
+}
+
+namespace llvm {
+namespace Win64EH {
+void Dumper::printRuntimeFunctionEntry(const Context &Ctx,
+ const coff_section *Section,
+ uint64_t Offset,
+ const RuntimeFunction &RF) {
+ SW.printString("StartAddress",
+ formatSymbol(Ctx, Section, Offset + 0, RF.StartAddress));
+ SW.printString("EndAddress",
+ formatSymbol(Ctx, Section, Offset + 4, RF.EndAddress,
+ /*IsRangeEnd=*/true));
+ SW.printString("UnwindInfoAddress",
+ formatSymbol(Ctx, Section, Offset + 8, RF.UnwindInfoOffset));
+}
+
+// Prints one unwind code. Because an unwind code can occupy up to 3 slots in
+// the unwind codes array, this function requires that the correct number of
+// slots is provided.
+void Dumper::printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC) {
+ assert(UC.size() >= getNumUsedSlots(UC[0]));
+
+ SW.startLine() << format("0x%02X: ", unsigned(UC[0].u.CodeOffset))
+ << getUnwindCodeTypeName(UC[0].getUnwindOp());
+
+ switch (UC[0].getUnwindOp()) {
+ case UOP_PushNonVol:
+ OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo());
+ break;
+
+ case UOP_AllocLarge:
+ OS << " size="
+ << ((UC[0].getOpInfo() == 0) ? UC[1].FrameOffset * 8
+ : getLargeSlotValue(UC));
+ break;
+
+ case UOP_AllocSmall:
+ OS << " size=" << (UC[0].getOpInfo() + 1) * 8;
+ break;
+
+ case UOP_SetFPReg:
+ if (UI.getFrameRegister() == 0)
+ OS << " reg=<invalid>";
+ else
+ OS << " reg=" << getUnwindRegisterName(UI.getFrameRegister())
+ << format(", offset=0x%X", UI.getFrameOffset() * 16);
+ break;
+
+ case UOP_SaveNonVol:
+ OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo())
+ << format(", offset=0x%X", UC[1].FrameOffset * 8);
+ break;
+
+ case UOP_SaveNonVolBig:
+ OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo())
+ << format(", offset=0x%X", getLargeSlotValue(UC));
+ break;
+
+ case UOP_SaveXMM128:
+ OS << " reg=XMM" << static_cast<uint32_t>(UC[0].getOpInfo())
+ << format(", offset=0x%X", UC[1].FrameOffset * 16);
+ break;
+
+ case UOP_SaveXMM128Big:
+ OS << " reg=XMM" << static_cast<uint32_t>(UC[0].getOpInfo())
+ << format(", offset=0x%X", getLargeSlotValue(UC));
+ break;
+
+ case UOP_PushMachFrame:
+ OS << " errcode=" << (UC[0].getOpInfo() == 0 ? "no" : "yes");
+ break;
+ }
+
+ OS << "\n";
+}
+
+void Dumper::printUnwindInfo(const Context &Ctx, const coff_section *Section,
+ off_t Offset, const UnwindInfo &UI) {
+ DictScope UIS(SW, "UnwindInfo");
+ SW.printNumber("Version", UI.getVersion());
+ SW.printFlags("Flags", UI.getFlags(), makeArrayRef(UnwindFlags));
+ SW.printNumber("PrologSize", UI.PrologSize);
+ if (UI.getFrameRegister()) {
+ SW.printEnum("FrameRegister", UI.getFrameRegister(),
+ makeArrayRef(UnwindOpInfo));
+ SW.printHex("FrameOffset", UI.getFrameOffset());
+ } else {
+ SW.printString("FrameRegister", StringRef("-"));
+ SW.printString("FrameOffset", StringRef("-"));
+ }
+
+ SW.printNumber("UnwindCodeCount", UI.NumCodes);
+ {
+ ListScope UCS(SW, "UnwindCodes");
+ ArrayRef<UnwindCode> UC(&UI.UnwindCodes[0], UI.NumCodes);
+ for (const UnwindCode *UCI = UC.begin(), *UCE = UC.end(); UCI < UCE; ++UCI) {
+ unsigned UsedSlots = getNumUsedSlots(*UCI);
+ if (UsedSlots > UC.size()) {
+ errs() << "corrupt unwind data";
+ return;
+ }
+
+ printUnwindCode(UI, makeArrayRef(UCI, UCE));
+ UCI = UCI + UsedSlots - 1;
+ }
+ }
+
+ uint64_t LSDAOffset = Offset + getOffsetOfLSDA(UI);
+ if (UI.getFlags() & (UNW_ExceptionHandler | UNW_TerminateHandler)) {
+ SW.printString("Handler",
+ formatSymbol(Ctx, Section, LSDAOffset,
+ UI.getLanguageSpecificHandlerOffset()));
+ } else if (UI.getFlags() & UNW_ChainInfo) {
+ if (const RuntimeFunction *Chained = UI.getChainedFunctionEntry()) {
+ DictScope CS(SW, "Chained");
+ printRuntimeFunctionEntry(Ctx, Section, LSDAOffset, *Chained);
+ }
+ }
+}
+
+void Dumper::printRuntimeFunction(const Context &Ctx,
+ const coff_section *Section,
+ uint64_t SectionOffset,
+ const RuntimeFunction &RF) {
+ DictScope RFS(SW, "RuntimeFunction");
+ printRuntimeFunctionEntry(Ctx, Section, SectionOffset, RF);
+
+ const coff_section *XData = nullptr;
+ uint64_t Offset;
+ resolveRelocation(Ctx, Section, SectionOffset + 8, XData, Offset);
+ Offset = Offset + RF.UnwindInfoOffset;
+
+ if (!XData) {
+ uint64_t Address = Ctx.COFF.getImageBase() + RF.UnwindInfoOffset;
+ XData = getSectionContaining(Ctx.COFF, Address);
+ if (!XData)
+ return;
+ Offset = RF.UnwindInfoOffset - XData->VirtualAddress;
+ }
+
+ ArrayRef<uint8_t> Contents;
+ if (Error E = Ctx.COFF.getSectionContents(XData, Contents))
+ reportError(std::move(E), Ctx.COFF.getFileName());
+
+ if (Contents.empty())
+ return;
+
+ if (Offset > Contents.size())
+ return;
+
+ const auto UI = reinterpret_cast<const UnwindInfo*>(Contents.data() + Offset);
+ printUnwindInfo(Ctx, XData, Offset, *UI);
+}
+
+void Dumper::printData(const Context &Ctx) {
+ for (const auto &Section : Ctx.COFF.sections()) {
+ StringRef Name;
+ if (Expected<StringRef> NameOrErr = Section.getName())
+ Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ if (Name != ".pdata" && !Name.startswith(".pdata$"))
+ continue;
+
+ const coff_section *PData = Ctx.COFF.getCOFFSection(Section);
+ ArrayRef<uint8_t> Contents;
+
+ if (Error E = Ctx.COFF.getSectionContents(PData, Contents))
+ reportError(std::move(E), Ctx.COFF.getFileName());
+ if (Contents.empty())
+ continue;
+
+ const RuntimeFunction *Entries =
+ reinterpret_cast<const RuntimeFunction *>(Contents.data());
+ const size_t Count = Contents.size() / sizeof(RuntimeFunction);
+ ArrayRef<RuntimeFunction> RuntimeFunctions(Entries, Count);
+
+ size_t Index = 0;
+ for (const auto &RF : RuntimeFunctions) {
+ printRuntimeFunction(Ctx, Ctx.COFF.getCOFFSection(Section),
+ Index * sizeof(RuntimeFunction), RF);
+ ++Index;
+ }
+ }
+}
+}
+}
+
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/Win64EHDumper.h b/contrib/libs/llvm14/tools/llvm-readobj/Win64EHDumper.h
new file mode 100644
index 00000000000..97458c916be
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/Win64EHDumper.h
@@ -0,0 +1,62 @@
+//===- Win64EHDumper.h - Win64 EH Printing ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_READOBJ_WIN64EHDUMPER_H
+#define LLVM_TOOLS_LLVM_READOBJ_WIN64EHDUMPER_H
+
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/Win64EH.h"
+
+namespace llvm {
+namespace object {
+class COFFObjectFile;
+class SymbolRef;
+struct coff_section;
+}
+
+namespace Win64EH {
+class Dumper {
+ ScopedPrinter &SW;
+ raw_ostream &OS;
+
+public:
+ typedef std::error_code (*SymbolResolver)(const object::coff_section *,
+ uint64_t, object::SymbolRef &,
+ void *);
+
+ struct Context {
+ const object::COFFObjectFile &COFF;
+ SymbolResolver ResolveSymbol;
+ void *UserData;
+
+ Context(const object::COFFObjectFile &COFF, SymbolResolver Resolver,
+ void *UserData)
+ : COFF(COFF), ResolveSymbol(Resolver), UserData(UserData) {}
+ };
+
+private:
+ void printRuntimeFunctionEntry(const Context &Ctx,
+ const object::coff_section *Section,
+ uint64_t SectionOffset,
+ const RuntimeFunction &RF);
+ void printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC);
+ void printUnwindInfo(const Context &Ctx, const object::coff_section *Section,
+ off_t Offset, const UnwindInfo &UI);
+ void printRuntimeFunction(const Context &Ctx,
+ const object::coff_section *Section,
+ uint64_t SectionOffset, const RuntimeFunction &RF);
+
+public:
+ Dumper(ScopedPrinter &SW) : SW(SW), OS(SW.getOStream()) {}
+
+ void printData(const Context &Ctx);
+};
+}
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/WindowsResourceDumper.cpp b/contrib/libs/llvm14/tools/llvm-readobj/WindowsResourceDumper.cpp
new file mode 100644
index 00000000000..fb085ecaa76
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/WindowsResourceDumper.cpp
@@ -0,0 +1,84 @@
+//===-- WindowsResourceDumper.cpp - Windows Resource printer --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Windows resource (.res) dumper for llvm-readobj.
+//
+//===----------------------------------------------------------------------===//
+
+#include "WindowsResourceDumper.h"
+#include "llvm/Object/WindowsResource.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+namespace llvm {
+namespace object {
+namespace WindowsRes {
+
+std::string stripUTF16(const ArrayRef<UTF16> &UTF16Str) {
+ std::string Result;
+ Result.reserve(UTF16Str.size());
+
+ for (UTF16 Ch : UTF16Str) {
+ // UTF16Str will have swapped byte order in case of big-endian machines.
+ // Swap it back in such a case.
+ uint16_t ChValue = support::endian::byte_swap(Ch, support::little);
+ if (ChValue <= 0xFF)
+ Result += ChValue;
+ else
+ Result += '?';
+ }
+ return Result;
+}
+
+Error Dumper::printData() {
+ auto EntryPtrOrErr = WinRes->getHeadEntry();
+ if (!EntryPtrOrErr)
+ return EntryPtrOrErr.takeError();
+ auto EntryPtr = *EntryPtrOrErr;
+
+ bool IsEnd = false;
+ while (!IsEnd) {
+ printEntry(EntryPtr);
+
+ if (auto Err = EntryPtr.moveNext(IsEnd))
+ return Err;
+ }
+ return Error::success();
+}
+
+void Dumper::printEntry(const ResourceEntryRef &Ref) {
+ if (Ref.checkTypeString()) {
+ auto NarrowStr = stripUTF16(Ref.getTypeString());
+ SW.printString("Resource type (string)", NarrowStr);
+ } else {
+ SmallString<20> IDStr;
+ raw_svector_ostream OS(IDStr);
+ printResourceTypeName(Ref.getTypeID(), OS);
+ SW.printString("Resource type (int)", IDStr);
+ }
+
+ if (Ref.checkNameString()) {
+ auto NarrowStr = stripUTF16(Ref.getNameString());
+ SW.printString("Resource name (string)", NarrowStr);
+ } else
+ SW.printNumber("Resource name (int)", Ref.getNameID());
+
+ SW.printNumber("Data version", Ref.getDataVersion());
+ SW.printHex("Memory flags", Ref.getMemoryFlags());
+ SW.printNumber("Language ID", Ref.getLanguage());
+ SW.printNumber("Version (major)", Ref.getMajorVersion());
+ SW.printNumber("Version (minor)", Ref.getMinorVersion());
+ SW.printNumber("Characteristics", Ref.getCharacteristics());
+ SW.printNumber("Data size", (uint64_t)Ref.getData().size());
+ SW.printBinary("Data:", Ref.getData());
+ SW.startLine() << "\n";
+}
+
+} // namespace WindowsRes
+} // namespace object
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/WindowsResourceDumper.h b/contrib/libs/llvm14/tools/llvm-readobj/WindowsResourceDumper.h
new file mode 100644
index 00000000000..6a5878804eb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/WindowsResourceDumper.h
@@ -0,0 +1,36 @@
+//===- WindowsResourceDumper.h - Windows Resource printer -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_READOBJ_WINDOWSRESOURCEDUMPER_H
+#define LLVM_TOOLS_LLVM_READOBJ_WINDOWSRESOURCEDUMPER_H
+
+#include "llvm/Object/WindowsResource.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+namespace llvm {
+namespace object {
+namespace WindowsRes {
+
+class Dumper {
+public:
+ Dumper(WindowsResource *Res, ScopedPrinter &SW) : SW(SW), WinRes(Res) {}
+
+ Error printData();
+
+private:
+ ScopedPrinter &SW;
+ WindowsResource *WinRes;
+
+ void printEntry(const ResourceEntryRef &Ref);
+};
+
+} // namespace WindowsRes
+} // namespace object
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/XCOFFDumper.cpp b/contrib/libs/llvm14/tools/llvm-readobj/XCOFFDumper.cpp
new file mode 100644
index 00000000000..6e778d558d4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/XCOFFDumper.cpp
@@ -0,0 +1,955 @@
+//===-- XCOFFDumper.cpp - XCOFF dumping utility -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements an XCOFF specific dumper for llvm-readobj.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ObjDumper.h"
+#include "llvm-readobj.h"
+#include "llvm/Object/XCOFFObjectFile.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+#include <ctime>
+#include <stddef.h>
+
+using namespace llvm;
+using namespace object;
+
+namespace {
+
+class XCOFFDumper : public ObjDumper {
+
+public:
+ XCOFFDumper(const XCOFFObjectFile &Obj, ScopedPrinter &Writer)
+ : ObjDumper(Writer, Obj.getFileName()), Obj(Obj) {}
+
+ void printFileHeaders() override;
+ void printAuxiliaryHeader() override;
+ void printSectionHeaders() override;
+ void printRelocations() override;
+ void printSymbols() override;
+ void printDynamicSymbols() override;
+ void printUnwindInfo() override;
+ void printStackMap() const override;
+ void printNeededLibraries() override;
+ void printStringTable() override;
+
+private:
+ template <typename T> void printSectionHeaders(ArrayRef<T> Sections);
+ template <typename T> void printGenericSectionHeader(T &Sec) const;
+ template <typename T> void printOverflowSectionHeader(T &Sec) const;
+ template <typename T> const T *getAuxEntPtr(uintptr_t AuxAddress);
+ void printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr);
+ void printCsectAuxEnt(XCOFFCsectAuxRef AuxEntRef);
+ void printSectAuxEntForStat(const XCOFFSectAuxEntForStat *AuxEntPtr);
+ void printExceptionAuxEnt(const XCOFFExceptionAuxEnt *AuxEntPtr);
+ void printFunctionAuxEnt(const XCOFFFunctionAuxEnt32 *AuxEntPtr);
+ void printFunctionAuxEnt(const XCOFFFunctionAuxEnt64 *AuxEntPtr);
+ void printBlockAuxEnt(const XCOFFBlockAuxEnt32 *AuxEntPtr);
+ void printBlockAuxEnt(const XCOFFBlockAuxEnt64 *AuxEntPtr);
+ template <typename T> void printSectAuxEntForDWARF(const T *AuxEntPtr);
+ void printSymbol(const SymbolRef &);
+ template <typename RelTy> void printRelocation(RelTy Reloc);
+ template <typename Shdr, typename RelTy>
+ void printRelocations(ArrayRef<Shdr> Sections);
+ void printAuxiliaryHeader(const XCOFFAuxiliaryHeader32 *AuxHeader);
+ void printAuxiliaryHeader(const XCOFFAuxiliaryHeader64 *AuxHeader);
+ const XCOFFObjectFile &Obj;
+};
+} // anonymous namespace
+
+void XCOFFDumper::printFileHeaders() {
+ DictScope DS(W, "FileHeader");
+ W.printHex("Magic", Obj.getMagic());
+ W.printNumber("NumberOfSections", Obj.getNumberOfSections());
+
+ // Negative timestamp values are reserved for future use.
+ int32_t TimeStamp = Obj.getTimeStamp();
+ if (TimeStamp > 0) {
+ // This handling of the time stamp assumes that the host system's time_t is
+ // compatible with AIX time_t. If a platform is not compatible, the lit
+ // tests will let us know.
+ time_t TimeDate = TimeStamp;
+
+ char FormattedTime[21] = {};
+ size_t BytesWritten =
+ strftime(FormattedTime, 21, "%Y-%m-%dT%H:%M:%SZ", gmtime(&TimeDate));
+ if (BytesWritten)
+ W.printHex("TimeStamp", FormattedTime, TimeStamp);
+ else
+ W.printHex("Timestamp", TimeStamp);
+ } else {
+ W.printHex("TimeStamp", TimeStamp == 0 ? "None" : "Reserved Value",
+ TimeStamp);
+ }
+
+ // The number of symbol table entries is an unsigned value in 64-bit objects
+ // and a signed value (with negative values being 'reserved') in 32-bit
+ // objects.
+ if (Obj.is64Bit()) {
+ W.printHex("SymbolTableOffset", Obj.getSymbolTableOffset64());
+ W.printNumber("SymbolTableEntries", Obj.getNumberOfSymbolTableEntries64());
+ } else {
+ W.printHex("SymbolTableOffset", Obj.getSymbolTableOffset32());
+ int32_t SymTabEntries = Obj.getRawNumberOfSymbolTableEntries32();
+ if (SymTabEntries >= 0)
+ W.printNumber("SymbolTableEntries", SymTabEntries);
+ else
+ W.printHex("SymbolTableEntries", "Reserved Value", SymTabEntries);
+ }
+
+ W.printHex("OptionalHeaderSize", Obj.getOptionalHeaderSize());
+ W.printHex("Flags", Obj.getFlags());
+
+ // TODO FIXME Add support for the auxiliary header (if any) once
+ // XCOFFObjectFile has the necessary support.
+}
+
+void XCOFFDumper::printAuxiliaryHeader() {
+ if (Obj.is64Bit())
+ printAuxiliaryHeader(Obj.auxiliaryHeader64());
+ else
+ printAuxiliaryHeader(Obj.auxiliaryHeader32());
+}
+
+void XCOFFDumper::printSectionHeaders() {
+ if (Obj.is64Bit())
+ printSectionHeaders(Obj.sections64());
+ else
+ printSectionHeaders(Obj.sections32());
+}
+
+void XCOFFDumper::printRelocations() {
+ if (Obj.is64Bit())
+ printRelocations<XCOFFSectionHeader64, XCOFFRelocation64>(Obj.sections64());
+ else
+ printRelocations<XCOFFSectionHeader32, XCOFFRelocation32>(Obj.sections32());
+}
+
+const EnumEntry<XCOFF::RelocationType> RelocationTypeNameclass[] = {
+#define ECase(X) \
+ { #X, XCOFF::X }
+ ECase(R_POS), ECase(R_RL), ECase(R_RLA), ECase(R_NEG),
+ ECase(R_REL), ECase(R_TOC), ECase(R_TRL), ECase(R_TRLA),
+ ECase(R_GL), ECase(R_TCL), ECase(R_REF), ECase(R_BA),
+ ECase(R_BR), ECase(R_RBA), ECase(R_RBR), ECase(R_TLS),
+ ECase(R_TLS_IE), ECase(R_TLS_LD), ECase(R_TLS_LE), ECase(R_TLSM),
+ ECase(R_TLSML), ECase(R_TOCU), ECase(R_TOCL)
+#undef ECase
+};
+
+template <typename RelTy> void XCOFFDumper::printRelocation(RelTy Reloc) {
+ Expected<StringRef> ErrOrSymbolName =
+ Obj.getSymbolNameByIndex(Reloc.SymbolIndex);
+ if (Error E = ErrOrSymbolName.takeError()) {
+ reportUniqueWarning(std::move(E));
+ return;
+ }
+ StringRef SymbolName = *ErrOrSymbolName;
+ StringRef RelocName = XCOFF::getRelocationTypeString(Reloc.Type);
+ if (opts::ExpandRelocs) {
+ DictScope Group(W, "Relocation");
+ W.printHex("Virtual Address", Reloc.VirtualAddress);
+ W.printNumber("Symbol", SymbolName, Reloc.SymbolIndex);
+ W.printString("IsSigned", Reloc.isRelocationSigned() ? "Yes" : "No");
+ W.printNumber("FixupBitValue", Reloc.isFixupIndicated() ? 1 : 0);
+ W.printNumber("Length", Reloc.getRelocatedLength());
+ W.printEnum("Type", (uint8_t)Reloc.Type,
+ makeArrayRef(RelocationTypeNameclass));
+ } else {
+ raw_ostream &OS = W.startLine();
+ OS << W.hex(Reloc.VirtualAddress) << " " << RelocName << " " << SymbolName
+ << "(" << Reloc.SymbolIndex << ") " << W.hex(Reloc.Info) << "\n";
+ }
+}
+
+template <typename Shdr, typename RelTy>
+void XCOFFDumper::printRelocations(ArrayRef<Shdr> Sections) {
+ ListScope LS(W, "Relocations");
+ uint16_t Index = 0;
+ for (const Shdr &Sec : Sections) {
+ ++Index;
+ // Only the .text, .data, .tdata, and STYP_DWARF sections have relocation.
+ if (Sec.Flags != XCOFF::STYP_TEXT && Sec.Flags != XCOFF::STYP_DATA &&
+ Sec.Flags != XCOFF::STYP_TDATA && Sec.Flags != XCOFF::STYP_DWARF)
+ continue;
+ Expected<ArrayRef<RelTy>> ErrOrRelocations = Obj.relocations<Shdr, RelTy>(Sec);
+ if (Error E = ErrOrRelocations.takeError()) {
+ reportUniqueWarning(std::move(E));
+ continue;
+ }
+
+ const ArrayRef<RelTy> Relocations = *ErrOrRelocations;
+ if (Relocations.empty())
+ continue;
+
+ W.startLine() << "Section (index: " << Index << ") " << Sec.getName()
+ << " {\n";
+ W.indent();
+
+ for (const RelTy Reloc : Relocations)
+ printRelocation(Reloc);
+
+ W.unindent();
+ W.startLine() << "}\n";
+ }
+}
+
+const EnumEntry<XCOFF::CFileStringType> FileStringType[] = {
+#define ECase(X) \
+ { #X, XCOFF::X }
+ ECase(XFT_FN), ECase(XFT_CT), ECase(XFT_CV), ECase(XFT_CD)
+#undef ECase
+};
+
+const EnumEntry<XCOFF::SymbolAuxType> SymAuxType[] = {
+#define ECase(X) \
+ { #X, XCOFF::X }
+ ECase(AUX_EXCEPT), ECase(AUX_FCN), ECase(AUX_SYM), ECase(AUX_FILE),
+ ECase(AUX_CSECT), ECase(AUX_SECT)
+#undef ECase
+};
+
+void XCOFFDumper::printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr) {
+ assert((!Obj.is64Bit() || AuxEntPtr->AuxType == XCOFF::AUX_FILE) &&
+ "Mismatched auxiliary type!");
+ StringRef FileName =
+ unwrapOrError(Obj.getFileName(), Obj.getCFileName(AuxEntPtr));
+ DictScope SymDs(W, "File Auxiliary Entry");
+ W.printNumber("Index",
+ Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
+ W.printString("Name", FileName);
+ W.printEnum("Type", static_cast<uint8_t>(AuxEntPtr->Type),
+ makeArrayRef(FileStringType));
+ if (Obj.is64Bit()) {
+ W.printEnum("Auxiliary Type", static_cast<uint8_t>(AuxEntPtr->AuxType),
+ makeArrayRef(SymAuxType));
+ }
+}
+
+static const EnumEntry<XCOFF::StorageMappingClass> CsectStorageMappingClass[] =
+ {
+#define ECase(X) \
+ { #X, XCOFF::X }
+ ECase(XMC_PR), ECase(XMC_RO), ECase(XMC_DB), ECase(XMC_GL),
+ ECase(XMC_XO), ECase(XMC_SV), ECase(XMC_SV64), ECase(XMC_SV3264),
+ ECase(XMC_TI), ECase(XMC_TB), ECase(XMC_RW), ECase(XMC_TC0),
+ ECase(XMC_TC), ECase(XMC_TD), ECase(XMC_DS), ECase(XMC_UA),
+ ECase(XMC_BS), ECase(XMC_UC), ECase(XMC_TL), ECase(XMC_UL),
+ ECase(XMC_TE)
+#undef ECase
+};
+
+const EnumEntry<XCOFF::SymbolType> CsectSymbolTypeClass[] = {
+#define ECase(X) \
+ { #X, XCOFF::X }
+ ECase(XTY_ER), ECase(XTY_SD), ECase(XTY_LD), ECase(XTY_CM)
+#undef ECase
+};
+
+void XCOFFDumper::printCsectAuxEnt(XCOFFCsectAuxRef AuxEntRef) {
+ assert((!Obj.is64Bit() || AuxEntRef.getAuxType64() == XCOFF::AUX_CSECT) &&
+ "Mismatched auxiliary type!");
+
+ DictScope SymDs(W, "CSECT Auxiliary Entry");
+ W.printNumber("Index", Obj.getSymbolIndex(AuxEntRef.getEntryAddress()));
+ W.printNumber(AuxEntRef.isLabel() ? "ContainingCsectSymbolIndex"
+ : "SectionLen",
+ AuxEntRef.getSectionOrLength());
+ W.printHex("ParameterHashIndex", AuxEntRef.getParameterHashIndex());
+ W.printHex("TypeChkSectNum", AuxEntRef.getTypeChkSectNum());
+ // Print out symbol alignment and type.
+ W.printNumber("SymbolAlignmentLog2", AuxEntRef.getAlignmentLog2());
+ W.printEnum("SymbolType", AuxEntRef.getSymbolType(),
+ makeArrayRef(CsectSymbolTypeClass));
+ W.printEnum("StorageMappingClass",
+ static_cast<uint8_t>(AuxEntRef.getStorageMappingClass()),
+ makeArrayRef(CsectStorageMappingClass));
+
+ if (Obj.is64Bit()) {
+ W.printEnum("Auxiliary Type", static_cast<uint8_t>(XCOFF::AUX_CSECT),
+ makeArrayRef(SymAuxType));
+ } else {
+ W.printHex("StabInfoIndex", AuxEntRef.getStabInfoIndex32());
+ W.printHex("StabSectNum", AuxEntRef.getStabSectNum32());
+ }
+}
+
+void XCOFFDumper::printSectAuxEntForStat(
+ const XCOFFSectAuxEntForStat *AuxEntPtr) {
+ assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file.");
+
+ DictScope SymDs(W, "Sect Auxiliary Entry For Stat");
+ W.printNumber("Index",
+ Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
+ W.printNumber("SectionLength", AuxEntPtr->SectionLength);
+
+ // Unlike the corresponding fields in the section header, NumberOfRelocEnt
+ // and NumberOfLineNum do not handle values greater than 65535.
+ W.printNumber("NumberOfRelocEnt", AuxEntPtr->NumberOfRelocEnt);
+ W.printNumber("NumberOfLineNum", AuxEntPtr->NumberOfLineNum);
+}
+
+void XCOFFDumper::printExceptionAuxEnt(const XCOFFExceptionAuxEnt *AuxEntPtr) {
+ assert(Obj.is64Bit() && "64-bit interface called on 32-bit object file.");
+
+ DictScope SymDs(W, "Exception Auxiliary Entry");
+ W.printNumber("Index",
+ Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
+ W.printHex("OffsetToExceptionTable", AuxEntPtr->OffsetToExceptionTbl);
+ W.printHex("SizeOfFunction", AuxEntPtr->SizeOfFunction);
+ W.printNumber("SymbolIndexOfNextBeyond", AuxEntPtr->SymIdxOfNextBeyond);
+ W.printEnum("Auxiliary Type", static_cast<uint8_t>(AuxEntPtr->AuxType),
+ makeArrayRef(SymAuxType));
+}
+
+void XCOFFDumper::printFunctionAuxEnt(const XCOFFFunctionAuxEnt32 *AuxEntPtr) {
+ assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file.");
+
+ DictScope SymDs(W, "Function Auxiliary Entry");
+ W.printNumber("Index",
+ Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
+ W.printHex("OffsetToExceptionTable", AuxEntPtr->OffsetToExceptionTbl);
+ W.printHex("SizeOfFunction", AuxEntPtr->SizeOfFunction);
+ W.printHex("PointerToLineNum", AuxEntPtr->PtrToLineNum);
+ W.printNumber("SymbolIndexOfNextBeyond", AuxEntPtr->SymIdxOfNextBeyond);
+}
+
+void XCOFFDumper::printFunctionAuxEnt(const XCOFFFunctionAuxEnt64 *AuxEntPtr) {
+ assert(Obj.is64Bit() && "64-bit interface called on 32-bit object file.");
+
+ DictScope SymDs(W, "Function Auxiliary Entry");
+ W.printNumber("Index",
+ Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
+ W.printHex("SizeOfFunction", AuxEntPtr->SizeOfFunction);
+ W.printHex("PointerToLineNum", AuxEntPtr->PtrToLineNum);
+ W.printNumber("SymbolIndexOfNextBeyond", AuxEntPtr->SymIdxOfNextBeyond);
+ W.printEnum("Auxiliary Type", static_cast<uint8_t>(AuxEntPtr->AuxType),
+ makeArrayRef(SymAuxType));
+}
+
+void XCOFFDumper::printBlockAuxEnt(const XCOFFBlockAuxEnt32 *AuxEntPtr) {
+ assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file.");
+
+ DictScope SymDs(W, "Block Auxiliary Entry");
+ W.printNumber("Index",
+ Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
+ W.printHex("LineNumber (High 2 Bytes)", AuxEntPtr->LineNumHi);
+ W.printHex("LineNumber (Low 2 Bytes)", AuxEntPtr->LineNumLo);
+}
+
+void XCOFFDumper::printBlockAuxEnt(const XCOFFBlockAuxEnt64 *AuxEntPtr) {
+ assert(Obj.is64Bit() && "64-bit interface called on 32-bit object file.");
+
+ DictScope SymDs(W, "Block Auxiliary Entry");
+ W.printNumber("Index",
+ Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
+ W.printHex("LineNumber", AuxEntPtr->LineNum);
+ W.printEnum("Auxiliary Type", static_cast<uint8_t>(AuxEntPtr->AuxType),
+ makeArrayRef(SymAuxType));
+}
+
+template <typename T>
+void XCOFFDumper::printSectAuxEntForDWARF(const T *AuxEntPtr) {
+ DictScope SymDs(W, "Sect Auxiliary Entry For DWARF");
+ W.printNumber("Index",
+ Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr)));
+ W.printHex("LengthOfSectionPortion", AuxEntPtr->LengthOfSectionPortion);
+ W.printNumber("NumberOfRelocEntries", AuxEntPtr->NumberOfRelocEnt);
+ if (Obj.is64Bit())
+ W.printEnum("Auxiliary Type", static_cast<uint8_t>(XCOFF::AUX_SECT),
+ makeArrayRef(SymAuxType));
+}
+
+const EnumEntry<XCOFF::StorageClass> SymStorageClass[] = {
+#define ECase(X) \
+ { #X, XCOFF::X }
+ ECase(C_NULL), ECase(C_AUTO), ECase(C_EXT), ECase(C_STAT),
+ ECase(C_REG), ECase(C_EXTDEF), ECase(C_LABEL), ECase(C_ULABEL),
+ ECase(C_MOS), ECase(C_ARG), ECase(C_STRTAG), ECase(C_MOU),
+ ECase(C_UNTAG), ECase(C_TPDEF), ECase(C_USTATIC), ECase(C_ENTAG),
+ ECase(C_MOE), ECase(C_REGPARM), ECase(C_FIELD), ECase(C_BLOCK),
+ ECase(C_FCN), ECase(C_EOS), ECase(C_FILE), ECase(C_LINE),
+ ECase(C_ALIAS), ECase(C_HIDDEN), ECase(C_HIDEXT), ECase(C_BINCL),
+ ECase(C_EINCL), ECase(C_INFO), ECase(C_WEAKEXT), ECase(C_DWARF),
+ ECase(C_GSYM), ECase(C_LSYM), ECase(C_PSYM), ECase(C_RSYM),
+ ECase(C_RPSYM), ECase(C_STSYM), ECase(C_TCSYM), ECase(C_BCOMM),
+ ECase(C_ECOML), ECase(C_ECOMM), ECase(C_DECL), ECase(C_ENTRY),
+ ECase(C_FUN), ECase(C_BSTAT), ECase(C_ESTAT), ECase(C_GTLS),
+ ECase(C_STTLS), ECase(C_EFCN)
+#undef ECase
+};
+
+static StringRef GetSymbolValueName(XCOFF::StorageClass SC) {
+ switch (SC) {
+ case XCOFF::C_EXT:
+ case XCOFF::C_WEAKEXT:
+ case XCOFF::C_HIDEXT:
+ case XCOFF::C_STAT:
+ case XCOFF::C_FCN:
+ case XCOFF::C_BLOCK:
+ return "Value (RelocatableAddress)";
+ case XCOFF::C_FILE:
+ return "Value (SymbolTableIndex)";
+ case XCOFF::C_DWARF:
+ return "Value (OffsetInDWARF)";
+ case XCOFF::C_FUN:
+ case XCOFF::C_STSYM:
+ case XCOFF::C_BINCL:
+ case XCOFF::C_EINCL:
+ case XCOFF::C_INFO:
+ case XCOFF::C_BSTAT:
+ case XCOFF::C_LSYM:
+ case XCOFF::C_PSYM:
+ case XCOFF::C_RPSYM:
+ case XCOFF::C_RSYM:
+ case XCOFF::C_ECOML:
+ assert(false && "This StorageClass for the symbol is not yet implemented.");
+ return "";
+ default:
+ return "Value";
+ }
+}
+
+const EnumEntry<XCOFF::CFileLangId> CFileLangIdClass[] = {
+#define ECase(X) \
+ { #X, XCOFF::X }
+ ECase(TB_C), ECase(TB_CPLUSPLUS)
+#undef ECase
+};
+
+const EnumEntry<XCOFF::CFileCpuId> CFileCpuIdClass[] = {
+#define ECase(X) \
+ { #X, XCOFF::X }
+ ECase(TCPU_PPC64), ECase(TCPU_COM), ECase(TCPU_970)
+#undef ECase
+};
+
+template <typename T> const T *XCOFFDumper::getAuxEntPtr(uintptr_t AuxAddress) {
+ const T *AuxEntPtr = reinterpret_cast<const T *>(AuxAddress);
+ Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(AuxEntPtr));
+ return AuxEntPtr;
+}
+
+static void printUnexpectedRawAuxEnt(ScopedPrinter &W, uintptr_t AuxAddress) {
+ W.startLine() << "!Unexpected raw auxiliary entry data:\n";
+ W.startLine() << format_bytes(
+ ArrayRef<uint8_t>(
+ reinterpret_cast<const uint8_t *>(AuxAddress),
+ XCOFF::SymbolTableEntrySize),
+ None, XCOFF::SymbolTableEntrySize)
+ << "\n";
+}
+
+void XCOFFDumper::printSymbol(const SymbolRef &S) {
+ DataRefImpl SymbolDRI = S.getRawDataRefImpl();
+ XCOFFSymbolRef SymbolEntRef = Obj.toSymbolRef(SymbolDRI);
+
+ uint8_t NumberOfAuxEntries = SymbolEntRef.getNumberOfAuxEntries();
+
+ DictScope SymDs(W, "Symbol");
+
+ StringRef SymbolName =
+ unwrapOrError(Obj.getFileName(), SymbolEntRef.getName());
+
+ uint32_t SymbolIdx = Obj.getSymbolIndex(SymbolEntRef.getEntryAddress());
+ XCOFF::StorageClass SymbolClass = SymbolEntRef.getStorageClass();
+
+ W.printNumber("Index", SymbolIdx);
+ W.printString("Name", SymbolName);
+ W.printHex(GetSymbolValueName(SymbolClass), SymbolEntRef.getValue());
+
+ StringRef SectionName =
+ unwrapOrError(Obj.getFileName(), Obj.getSymbolSectionName(SymbolEntRef));
+
+ W.printString("Section", SectionName);
+ if (SymbolClass == XCOFF::C_FILE) {
+ W.printEnum("Source Language ID", SymbolEntRef.getLanguageIdForCFile(),
+ makeArrayRef(CFileLangIdClass));
+ W.printEnum("CPU Version ID", SymbolEntRef.getCPUTypeIddForCFile(),
+ makeArrayRef(CFileCpuIdClass));
+ } else
+ W.printHex("Type", SymbolEntRef.getSymbolType());
+
+ W.printEnum("StorageClass", static_cast<uint8_t>(SymbolClass),
+ makeArrayRef(SymStorageClass));
+ W.printNumber("NumberOfAuxEntries", NumberOfAuxEntries);
+
+ if (NumberOfAuxEntries == 0)
+ return;
+
+ auto checkNumOfAux = [=] {
+ if (NumberOfAuxEntries > 1)
+ reportUniqueWarning("the " +
+ enumToString(static_cast<uint8_t>(SymbolClass),
+ makeArrayRef(SymStorageClass)) +
+ " symbol at index " + Twine(SymbolIdx) +
+ " should not have more than 1 "
+ "auxiliary entry");
+ };
+
+ switch (SymbolClass) {
+ case XCOFF::C_FILE:
+ // If the symbol is C_FILE and has auxiliary entries...
+ for (int I = 1; I <= NumberOfAuxEntries; I++) {
+ uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress(
+ SymbolEntRef.getEntryAddress(), I);
+
+ if (Obj.is64Bit() &&
+ *Obj.getSymbolAuxType(AuxAddress) != XCOFF::SymbolAuxType::AUX_FILE) {
+ printUnexpectedRawAuxEnt(W, AuxAddress);
+ continue;
+ }
+
+ const XCOFFFileAuxEnt *FileAuxEntPtr =
+ getAuxEntPtr<XCOFFFileAuxEnt>(AuxAddress);
+ printFileAuxEnt(FileAuxEntPtr);
+ }
+ break;
+ case XCOFF::C_EXT:
+ case XCOFF::C_WEAKEXT:
+ case XCOFF::C_HIDEXT: {
+ if (!SymbolEntRef.isFunction() && NumberOfAuxEntries > 1)
+ reportUniqueWarning("the non-function " +
+ enumToString(static_cast<uint8_t>(SymbolClass),
+ makeArrayRef(SymStorageClass)) +
+ " symbol at index " + Twine(SymbolIdx) +
+ " should have only 1 auxiliary entry, i.e. the CSECT "
+ "auxiliary entry");
+
+ // For 32-bit objects, print the function auxiliary symbol table entry. The
+ // last one must be a CSECT auxiliary entry.
+ // For 64-bit objects, both a function auxiliary entry and an exception
+ // auxiliary entry may appear, print them in the loop and skip printing the
+ // CSECT auxiliary entry, which will be printed outside the loop.
+ for (int I = 1; I <= NumberOfAuxEntries; I++) {
+ if ((I == NumberOfAuxEntries && !Obj.is64Bit()) ||
+ !SymbolEntRef.isFunction())
+ break;
+
+ uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress(
+ SymbolEntRef.getEntryAddress(), I);
+
+ if (Obj.is64Bit()) {
+ XCOFF::SymbolAuxType Type = *Obj.getSymbolAuxType(AuxAddress);
+ if (Type == XCOFF::SymbolAuxType::AUX_CSECT)
+ continue;
+ if (Type == XCOFF::SymbolAuxType::AUX_FCN) {
+ const XCOFFFunctionAuxEnt64 *AuxEntPtr =
+ getAuxEntPtr<XCOFFFunctionAuxEnt64>(AuxAddress);
+ printFunctionAuxEnt(AuxEntPtr);
+ } else if (Type == XCOFF::SymbolAuxType::AUX_EXCEPT) {
+ const XCOFFExceptionAuxEnt *AuxEntPtr =
+ getAuxEntPtr<XCOFFExceptionAuxEnt>(AuxAddress);
+ printExceptionAuxEnt(AuxEntPtr);
+ } else {
+ printUnexpectedRawAuxEnt(W, AuxAddress);
+ }
+ } else {
+ const XCOFFFunctionAuxEnt32 *AuxEntPtr =
+ getAuxEntPtr<XCOFFFunctionAuxEnt32>(AuxAddress);
+ printFunctionAuxEnt(AuxEntPtr);
+ }
+ }
+
+ // Print the CSECT auxiliary entry.
+ auto ErrOrCsectAuxRef = SymbolEntRef.getXCOFFCsectAuxRef();
+ if (!ErrOrCsectAuxRef)
+ reportUniqueWarning(ErrOrCsectAuxRef.takeError());
+ else
+ printCsectAuxEnt(*ErrOrCsectAuxRef);
+
+ break;
+ }
+ case XCOFF::C_STAT: {
+ checkNumOfAux();
+
+ const XCOFFSectAuxEntForStat *StatAuxEntPtr =
+ getAuxEntPtr<XCOFFSectAuxEntForStat>(
+ XCOFFObjectFile::getAdvancedSymbolEntryAddress(
+ SymbolEntRef.getEntryAddress(), 1));
+ printSectAuxEntForStat(StatAuxEntPtr);
+ break;
+ }
+ case XCOFF::C_DWARF: {
+ checkNumOfAux();
+
+ uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress(
+ SymbolEntRef.getEntryAddress(), 1);
+
+ if (Obj.is64Bit()) {
+ const XCOFFSectAuxEntForDWARF64 *AuxEntPtr =
+ getAuxEntPtr<XCOFFSectAuxEntForDWARF64>(AuxAddress);
+ printSectAuxEntForDWARF<XCOFFSectAuxEntForDWARF64>(AuxEntPtr);
+ } else {
+ const XCOFFSectAuxEntForDWARF32 *AuxEntPtr =
+ getAuxEntPtr<XCOFFSectAuxEntForDWARF32>(AuxAddress);
+ printSectAuxEntForDWARF<XCOFFSectAuxEntForDWARF32>(AuxEntPtr);
+ }
+ break;
+ }
+ case XCOFF::C_BLOCK:
+ case XCOFF::C_FCN: {
+ checkNumOfAux();
+
+ uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress(
+ SymbolEntRef.getEntryAddress(), 1);
+
+ if (Obj.is64Bit()) {
+ const XCOFFBlockAuxEnt64 *AuxEntPtr =
+ getAuxEntPtr<XCOFFBlockAuxEnt64>(AuxAddress);
+ printBlockAuxEnt(AuxEntPtr);
+ } else {
+ const XCOFFBlockAuxEnt32 *AuxEntPtr =
+ getAuxEntPtr<XCOFFBlockAuxEnt32>(AuxAddress);
+ printBlockAuxEnt(AuxEntPtr);
+ }
+ break;
+ }
+ default:
+ for (int i = 1; i <= NumberOfAuxEntries; i++) {
+ printUnexpectedRawAuxEnt(W,
+ XCOFFObjectFile::getAdvancedSymbolEntryAddress(
+ SymbolEntRef.getEntryAddress(), i));
+ }
+ break;
+ }
+}
+
+void XCOFFDumper::printSymbols() {
+ ListScope Group(W, "Symbols");
+ for (const SymbolRef &S : Obj.symbols())
+ printSymbol(S);
+}
+
+void XCOFFDumper::printStringTable() {
+ DictScope DS(W, "StringTable");
+ StringRef StrTable = Obj.getStringTable();
+ uint32_t StrTabSize = StrTable.size();
+ W.printNumber("Length", StrTabSize);
+ // Print strings from the fifth byte, since the first four bytes contain the
+ // length (in bytes) of the string table (including the length field).
+ if (StrTabSize > 4)
+ printAsStringList(StrTable, 4);
+}
+
+void XCOFFDumper::printDynamicSymbols() {
+ llvm_unreachable("Unimplemented functionality for XCOFFDumper");
+}
+
+void XCOFFDumper::printUnwindInfo() {
+ llvm_unreachable("Unimplemented functionality for XCOFFDumper");
+}
+
+void XCOFFDumper::printStackMap() const {
+ llvm_unreachable("Unimplemented functionality for XCOFFDumper");
+}
+
+void XCOFFDumper::printNeededLibraries() {
+ ListScope D(W, "NeededLibraries");
+ auto ImportFilesOrError = Obj.getImportFileTable();
+ if (!ImportFilesOrError) {
+ reportUniqueWarning(ImportFilesOrError.takeError());
+ return;
+ }
+
+ StringRef ImportFileTable = ImportFilesOrError.get();
+ const char *CurrentStr = ImportFileTable.data();
+ const char *TableEnd = ImportFileTable.end();
+ // Default column width for names is 13 even if no names are that long.
+ size_t BaseWidth = 13;
+
+ // Get the max width of BASE columns.
+ for (size_t StrIndex = 0; CurrentStr < TableEnd; ++StrIndex) {
+ size_t CurrentLen = strlen(CurrentStr);
+ CurrentStr += strlen(CurrentStr) + 1;
+ if (StrIndex % 3 == 1)
+ BaseWidth = std::max(BaseWidth, CurrentLen);
+ }
+
+ auto &OS = static_cast<formatted_raw_ostream &>(W.startLine());
+ // Each entry consists of 3 strings: the path_name, base_name and
+ // archive_member_name. The first entry is a default LIBPATH value and other
+ // entries have no path_name. We just dump the base_name and
+ // archive_member_name here.
+ OS << left_justify("BASE", BaseWidth) << " MEMBER\n";
+ CurrentStr = ImportFileTable.data();
+ for (size_t StrIndex = 0; CurrentStr < TableEnd;
+ ++StrIndex, CurrentStr += strlen(CurrentStr) + 1) {
+ if (StrIndex >= 3 && StrIndex % 3 != 0) {
+ if (StrIndex % 3 == 1)
+ OS << " " << left_justify(CurrentStr, BaseWidth) << " ";
+ else
+ OS << CurrentStr << "\n";
+ }
+ }
+}
+
+const EnumEntry<XCOFF::SectionTypeFlags> SectionTypeFlagsNames[] = {
+#define ECase(X) \
+ { #X, XCOFF::X }
+ ECase(STYP_PAD), ECase(STYP_DWARF), ECase(STYP_TEXT),
+ ECase(STYP_DATA), ECase(STYP_BSS), ECase(STYP_EXCEPT),
+ ECase(STYP_INFO), ECase(STYP_TDATA), ECase(STYP_TBSS),
+ ECase(STYP_LOADER), ECase(STYP_DEBUG), ECase(STYP_TYPCHK),
+ ECase(STYP_OVRFLO)
+#undef ECase
+};
+
+template <typename T>
+void XCOFFDumper::printOverflowSectionHeader(T &Sec) const {
+ if (Obj.is64Bit()) {
+ reportWarning(make_error<StringError>("An 64-bit XCOFF object file may not "
+ "contain an overflow section header.",
+ object_error::parse_failed),
+ Obj.getFileName());
+ }
+
+ W.printString("Name", Sec.getName());
+ W.printNumber("NumberOfRelocations", Sec.PhysicalAddress);
+ W.printNumber("NumberOfLineNumbers", Sec.VirtualAddress);
+ W.printHex("Size", Sec.SectionSize);
+ W.printHex("RawDataOffset", Sec.FileOffsetToRawData);
+ W.printHex("RelocationPointer", Sec.FileOffsetToRelocationInfo);
+ W.printHex("LineNumberPointer", Sec.FileOffsetToLineNumberInfo);
+ W.printNumber("IndexOfSectionOverflowed", Sec.NumberOfRelocations);
+ W.printNumber("IndexOfSectionOverflowed", Sec.NumberOfLineNumbers);
+}
+
+template <typename T>
+void XCOFFDumper::printGenericSectionHeader(T &Sec) const {
+ W.printString("Name", Sec.getName());
+ W.printHex("PhysicalAddress", Sec.PhysicalAddress);
+ W.printHex("VirtualAddress", Sec.VirtualAddress);
+ W.printHex("Size", Sec.SectionSize);
+ W.printHex("RawDataOffset", Sec.FileOffsetToRawData);
+ W.printHex("RelocationPointer", Sec.FileOffsetToRelocationInfo);
+ W.printHex("LineNumberPointer", Sec.FileOffsetToLineNumberInfo);
+ W.printNumber("NumberOfRelocations", Sec.NumberOfRelocations);
+ W.printNumber("NumberOfLineNumbers", Sec.NumberOfLineNumbers);
+}
+
+void XCOFFDumper::printAuxiliaryHeader(
+ const XCOFFAuxiliaryHeader32 *AuxHeader) {
+ if (AuxHeader == nullptr)
+ return;
+ uint16_t AuxSize = Obj.getOptionalHeaderSize();
+ uint16_t PartialFieldOffset = AuxSize;
+ const char *PartialFieldName = nullptr;
+
+ DictScope DS(W, "AuxiliaryHeader");
+
+#define PrintAuxMember32(H, S, T) \
+ if (offsetof(XCOFFAuxiliaryHeader32, T) + \
+ sizeof(XCOFFAuxiliaryHeader32::T) <= \
+ AuxSize) \
+ W.print##H(S, AuxHeader->T); \
+ else if (offsetof(XCOFFAuxiliaryHeader32, T) < AuxSize) { \
+ PartialFieldOffset = offsetof(XCOFFAuxiliaryHeader32, T); \
+ PartialFieldName = S; \
+ }
+
+ PrintAuxMember32(Hex, "Magic", AuxMagic);
+ PrintAuxMember32(Hex, "Version", Version);
+ PrintAuxMember32(Hex, "Size of .text section", TextSize);
+ PrintAuxMember32(Hex, "Size of .data section", InitDataSize);
+ PrintAuxMember32(Hex, "Size of .bss section", BssDataSize);
+ PrintAuxMember32(Hex, "Entry point address", EntryPointAddr);
+ PrintAuxMember32(Hex, ".text section start address", TextStartAddr);
+ PrintAuxMember32(Hex, ".data section start address", DataStartAddr);
+ PrintAuxMember32(Hex, "TOC anchor address", TOCAnchorAddr);
+ PrintAuxMember32(Number, "Section number of entryPoint", SecNumOfEntryPoint);
+ PrintAuxMember32(Number, "Section number of .text", SecNumOfText);
+ PrintAuxMember32(Number, "Section number of .data", SecNumOfData);
+ PrintAuxMember32(Number, "Section number of TOC", SecNumOfTOC);
+ PrintAuxMember32(Number, "Section number of loader data", SecNumOfLoader);
+ PrintAuxMember32(Number, "Section number of .bss", SecNumOfBSS);
+ PrintAuxMember32(Hex, "Maxium alignment of .text", MaxAlignOfText);
+ PrintAuxMember32(Hex, "Maxium alignment of .data", MaxAlignOfData);
+ PrintAuxMember32(Hex, "Module type", ModuleType);
+ PrintAuxMember32(Hex, "CPU type of objects", CpuFlag);
+ PrintAuxMember32(Hex, "(Reserved)", CpuType);
+ PrintAuxMember32(Hex, "Maximum stack size", MaxStackSize);
+ PrintAuxMember32(Hex, "Maximum data size", MaxDataSize);
+ PrintAuxMember32(Hex, "Reserved for debugger", ReservedForDebugger);
+ PrintAuxMember32(Hex, "Text page size", TextPageSize);
+ PrintAuxMember32(Hex, "Data page size", DataPageSize);
+ PrintAuxMember32(Hex, "Stack page size", StackPageSize);
+ if (offsetof(XCOFFAuxiliaryHeader32, FlagAndTDataAlignment) +
+ sizeof(XCOFFAuxiliaryHeader32::FlagAndTDataAlignment) <=
+ AuxSize) {
+ W.printHex("Flag", AuxHeader->getFlag());
+ W.printHex("Alignment of thread-local storage",
+ AuxHeader->getTDataAlignment());
+ }
+
+ PrintAuxMember32(Number, "Section number for .tdata", SecNumOfTData);
+ PrintAuxMember32(Number, "Section number for .tbss", SecNumOfTBSS);
+
+ // Deal with error.
+ if (PartialFieldOffset < AuxSize) {
+ std::string ErrInfo;
+ llvm::raw_string_ostream StringOS(ErrInfo);
+ StringOS << "Only partial field for " << PartialFieldName << " at offset ("
+ << PartialFieldOffset << ").";
+ StringOS.flush();
+ reportWarning(
+ make_error<GenericBinaryError>(ErrInfo, object_error::parse_failed),
+ "-");
+ W.printBinary(
+ "Raw data", "",
+ ArrayRef<uint8_t>((const uint8_t *)(AuxHeader) + PartialFieldOffset,
+ AuxSize - PartialFieldOffset));
+ } else if (sizeof(XCOFFAuxiliaryHeader32) < AuxSize) {
+ reportWarning(make_error<GenericBinaryError>(
+ "There are extra data beyond auxiliary header",
+ object_error::parse_failed),
+ "-");
+ W.printBinary("Extra raw data", "",
+ ArrayRef<uint8_t>((const uint8_t *)(AuxHeader) +
+ sizeof(XCOFFAuxiliaryHeader32),
+ AuxSize - sizeof(XCOFFAuxiliaryHeader32)));
+ }
+
+#undef PrintAuxMember32
+}
+
+void XCOFFDumper::printAuxiliaryHeader(
+ const XCOFFAuxiliaryHeader64 *AuxHeader) {
+ if (AuxHeader == nullptr)
+ return;
+ uint16_t AuxSize = Obj.getOptionalHeaderSize();
+ uint16_t PartialFieldOffset = AuxSize;
+ const char *PartialFieldName = nullptr;
+
+ DictScope DS(W, "AuxiliaryHeader");
+
+#define PrintAuxMember64(H, S, T) \
+ if (offsetof(XCOFFAuxiliaryHeader64, T) + \
+ sizeof(XCOFFAuxiliaryHeader64::T) <= \
+ AuxSize) \
+ W.print##H(S, AuxHeader->T); \
+ else if (offsetof(XCOFFAuxiliaryHeader64, T) < AuxSize) { \
+ PartialFieldOffset = offsetof(XCOFFAuxiliaryHeader64, T); \
+ PartialFieldName = S; \
+ }
+
+ PrintAuxMember64(Hex, "Magic", AuxMagic);
+ PrintAuxMember64(Hex, "Version", Version);
+ PrintAuxMember64(Hex, "Reserved for debugger", ReservedForDebugger);
+ PrintAuxMember64(Hex, ".text section start address", TextStartAddr);
+ PrintAuxMember64(Hex, ".data section start address", DataStartAddr);
+ PrintAuxMember64(Hex, "TOC anchor address", TOCAnchorAddr);
+ PrintAuxMember64(Number, "Section number of entryPoint", SecNumOfEntryPoint);
+ PrintAuxMember64(Number, "Section number of .text", SecNumOfText);
+ PrintAuxMember64(Number, "Section number of .data", SecNumOfData);
+ PrintAuxMember64(Number, "Section number of TOC", SecNumOfTOC);
+ PrintAuxMember64(Number, "Section number of loader data", SecNumOfLoader);
+ PrintAuxMember64(Number, "Section number of .bss", SecNumOfBSS);
+ PrintAuxMember64(Hex, "Maxium alignment of .text", MaxAlignOfText);
+ PrintAuxMember64(Hex, "Maxium alignment of .data", MaxAlignOfData);
+ PrintAuxMember64(Hex, "Module type", ModuleType);
+ PrintAuxMember64(Hex, "CPU type of objects", CpuFlag);
+ PrintAuxMember64(Hex, "(Reserved)", CpuType);
+ PrintAuxMember64(Hex, "Text page size", TextPageSize);
+ PrintAuxMember64(Hex, "Data page size", DataPageSize);
+ PrintAuxMember64(Hex, "Stack page size", StackPageSize);
+ if (offsetof(XCOFFAuxiliaryHeader64, FlagAndTDataAlignment) +
+ sizeof(XCOFFAuxiliaryHeader64::FlagAndTDataAlignment) <=
+ AuxSize) {
+ W.printHex("Flag", AuxHeader->getFlag());
+ W.printHex("Alignment of thread-local storage",
+ AuxHeader->getTDataAlignment());
+ }
+ PrintAuxMember64(Hex, "Size of .text section", TextSize);
+ PrintAuxMember64(Hex, "Size of .data section", InitDataSize);
+ PrintAuxMember64(Hex, "Size of .bss section", BssDataSize);
+ PrintAuxMember64(Hex, "Entry point address", EntryPointAddr);
+ PrintAuxMember64(Hex, "Maximum stack size", MaxStackSize);
+ PrintAuxMember64(Hex, "Maximum data size", MaxDataSize);
+ PrintAuxMember64(Number, "Section number for .tdata", SecNumOfTData);
+ PrintAuxMember64(Number, "Section number for .tbss", SecNumOfTBSS);
+ PrintAuxMember64(Hex, "Additional flags 64-bit XCOFF", XCOFF64Flag);
+
+ if (PartialFieldOffset < AuxSize) {
+ std::string ErrInfo;
+ llvm::raw_string_ostream StringOS(ErrInfo);
+ StringOS << "Only partial field for " << PartialFieldName << " at offset ("
+ << PartialFieldOffset << ").";
+ StringOS.flush();
+ reportWarning(
+ make_error<GenericBinaryError>(ErrInfo, object_error::parse_failed),
+ "-");
+ ;
+ W.printBinary(
+ "Raw data", "",
+ ArrayRef<uint8_t>((const uint8_t *)(AuxHeader) + PartialFieldOffset,
+ AuxSize - PartialFieldOffset));
+ } else if (sizeof(XCOFFAuxiliaryHeader64) < AuxSize) {
+ reportWarning(make_error<GenericBinaryError>(
+ "There are extra data beyond auxiliary header",
+ object_error::parse_failed),
+ "-");
+ W.printBinary("Extra raw data", "",
+ ArrayRef<uint8_t>((const uint8_t *)(AuxHeader) +
+ sizeof(XCOFFAuxiliaryHeader64),
+ AuxSize - sizeof(XCOFFAuxiliaryHeader64)));
+ }
+
+#undef PrintAuxMember64
+}
+
+template <typename T>
+void XCOFFDumper::printSectionHeaders(ArrayRef<T> Sections) {
+ ListScope Group(W, "Sections");
+
+ uint16_t Index = 1;
+ for (const T &Sec : Sections) {
+ DictScope SecDS(W, "Section");
+
+ W.printNumber("Index", Index++);
+ uint16_t SectionType = Sec.getSectionType();
+ switch (SectionType) {
+ case XCOFF::STYP_OVRFLO:
+ printOverflowSectionHeader(Sec);
+ break;
+ case XCOFF::STYP_LOADER:
+ case XCOFF::STYP_EXCEPT:
+ case XCOFF::STYP_TYPCHK:
+ // TODO The interpretation of loader, exception and type check section
+ // headers are different from that of generic section headers. We will
+ // implement them later. We interpret them as generic section headers for
+ // now.
+ default:
+ printGenericSectionHeader(Sec);
+ break;
+ }
+ if (Sec.isReservedSectionType())
+ W.printHex("Flags", "Reserved", SectionType);
+ else
+ W.printEnum("Type", SectionType, makeArrayRef(SectionTypeFlagsNames));
+ }
+
+ if (opts::SectionRelocations)
+ report_fatal_error("Dumping section relocations is unimplemented");
+
+ if (opts::SectionSymbols)
+ report_fatal_error("Dumping symbols is unimplemented");
+
+ if (opts::SectionData)
+ report_fatal_error("Dumping section data is unimplemented");
+}
+
+namespace llvm {
+std::unique_ptr<ObjDumper>
+createXCOFFDumper(const object::XCOFFObjectFile &XObj, ScopedPrinter &Writer) {
+ return std::make_unique<XCOFFDumper>(XObj, Writer);
+}
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/llvm-readobj.cpp b/contrib/libs/llvm14/tools/llvm-readobj/llvm-readobj.cpp
new file mode 100644
index 00000000000..543b0de82cd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/llvm-readobj.cpp
@@ -0,0 +1,632 @@
+//===- llvm-readobj.cpp - Dump contents of an Object File -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a tool similar to readelf, except it works on multiple object file
+// formats. The main purpose of this tool is to provide detailed output suitable
+// for FileCheck.
+//
+// Flags should be similar to readelf where supported, but the output format
+// does not need to be identical. The point is to not make users learn yet
+// another set of flags.
+//
+// Output should be specialized for each format where appropriate.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-readobj.h"
+#include "ObjDumper.h"
+#include "WindowsResourceDumper.h"
+#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
+#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/COFFImportFile.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Object/WindowsResource.h"
+#include "llvm/Object/XCOFFObjectFile.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/DataTypes.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/WithColor.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+namespace {
+using namespace llvm::opt; // for HelpHidden in Opts.inc
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class ReadobjOptTable : public opt::OptTable {
+public:
+ ReadobjOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
+};
+
+enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols };
+} // namespace
+
+namespace opts {
+static bool Addrsig;
+static bool All;
+static bool ArchSpecificInfo;
+static bool BBAddrMap;
+bool ExpandRelocs;
+static bool CGProfile;
+bool Demangle;
+static bool DependentLibraries;
+static bool DynRelocs;
+static bool DynamicSymbols;
+static bool FileHeaders;
+static bool Headers;
+static std::vector<std::string> HexDump;
+static bool PrettyPrint;
+static bool PrintStackMap;
+static bool PrintStackSizes;
+static bool Relocations;
+bool SectionData;
+static bool SectionDetails;
+static bool SectionHeaders;
+bool SectionRelocations;
+bool SectionSymbols;
+static std::vector<std::string> StringDump;
+static bool StringTable;
+static bool Symbols;
+static bool UnwindInfo;
+static cl::boolOrDefault SectionMapping;
+
+// ELF specific options.
+static bool DynamicTable;
+static bool ELFLinkerOptions;
+static bool GnuHashTable;
+static bool HashSymbols;
+static bool HashTable;
+static bool HashHistogram;
+static bool NeededLibraries;
+static bool Notes;
+static bool ProgramHeaders;
+bool RawRelr;
+static bool SectionGroups;
+static bool VersionInfo;
+
+// Mach-O specific options.
+static bool MachODataInCode;
+static bool MachODysymtab;
+static bool MachOIndirectSymbols;
+static bool MachOLinkerOptions;
+static bool MachOSegment;
+static bool MachOVersionMin;
+
+// PE/COFF specific options.
+static bool CodeView;
+static bool CodeViewEnableGHash;
+static bool CodeViewMergedTypes;
+bool CodeViewSubsectionBytes;
+static bool COFFBaseRelocs;
+static bool COFFDebugDirectory;
+static bool COFFDirectives;
+static bool COFFExports;
+static bool COFFImports;
+static bool COFFLoadConfig;
+static bool COFFResources;
+static bool COFFTLSDirectory;
+
+// XCOFF specific options.
+static bool XCOFFAuxiliaryHeader;
+
+OutputStyleTy Output = OutputStyleTy::LLVM;
+static std::vector<std::string> InputFilenames;
+} // namespace opts
+
+static StringRef ToolName;
+
+namespace llvm {
+
+[[noreturn]] static void error(Twine Msg) {
+ // Flush the standard output to print the error at a
+ // proper place.
+ fouts().flush();
+ WithColor::error(errs(), ToolName) << Msg << "\n";
+ exit(1);
+}
+
+[[noreturn]] void reportError(Error Err, StringRef Input) {
+ assert(Err);
+ if (Input == "-")
+ Input = "<stdin>";
+ handleAllErrors(createFileError(Input, std::move(Err)),
+ [&](const ErrorInfoBase &EI) { error(EI.message()); });
+ llvm_unreachable("error() call should never return");
+}
+
+void reportWarning(Error Err, StringRef Input) {
+ assert(Err);
+ if (Input == "-")
+ Input = "<stdin>";
+
+ // Flush the standard output to print the warning at a
+ // proper place.
+ fouts().flush();
+ handleAllErrors(
+ createFileError(Input, std::move(Err)), [&](const ErrorInfoBase &EI) {
+ WithColor::warning(errs(), ToolName) << EI.message() << "\n";
+ });
+}
+
+} // namespace llvm
+
+static void parseOptions(const opt::InputArgList &Args) {
+ opts::Addrsig = Args.hasArg(OPT_addrsig);
+ opts::All = Args.hasArg(OPT_all);
+ opts::ArchSpecificInfo = Args.hasArg(OPT_arch_specific);
+ opts::BBAddrMap = Args.hasArg(OPT_bb_addr_map);
+ opts::CGProfile = Args.hasArg(OPT_cg_profile);
+ opts::Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false);
+ opts::DependentLibraries = Args.hasArg(OPT_dependent_libraries);
+ opts::DynRelocs = Args.hasArg(OPT_dyn_relocations);
+ opts::DynamicSymbols = Args.hasArg(OPT_dyn_syms);
+ opts::ExpandRelocs = Args.hasArg(OPT_expand_relocs);
+ opts::FileHeaders = Args.hasArg(OPT_file_header);
+ opts::Headers = Args.hasArg(OPT_headers);
+ opts::HexDump = Args.getAllArgValues(OPT_hex_dump_EQ);
+ opts::Relocations = Args.hasArg(OPT_relocs);
+ opts::SectionData = Args.hasArg(OPT_section_data);
+ opts::SectionDetails = Args.hasArg(OPT_section_details);
+ opts::SectionHeaders = Args.hasArg(OPT_section_headers);
+ opts::SectionRelocations = Args.hasArg(OPT_section_relocations);
+ opts::SectionSymbols = Args.hasArg(OPT_section_symbols);
+ if (Args.hasArg(OPT_section_mapping))
+ opts::SectionMapping = cl::BOU_TRUE;
+ else if (Args.hasArg(OPT_section_mapping_EQ_false))
+ opts::SectionMapping = cl::BOU_FALSE;
+ else
+ opts::SectionMapping = cl::BOU_UNSET;
+ opts::PrintStackSizes = Args.hasArg(OPT_stack_sizes);
+ opts::PrintStackMap = Args.hasArg(OPT_stackmap);
+ opts::StringDump = Args.getAllArgValues(OPT_string_dump_EQ);
+ opts::StringTable = Args.hasArg(OPT_string_table);
+ opts::Symbols = Args.hasArg(OPT_symbols);
+ opts::UnwindInfo = Args.hasArg(OPT_unwind);
+
+ // ELF specific options.
+ opts::DynamicTable = Args.hasArg(OPT_dynamic_table);
+ opts::ELFLinkerOptions = Args.hasArg(OPT_elf_linker_options);
+ if (Arg *A = Args.getLastArg(OPT_elf_output_style_EQ)) {
+ std::string OutputStyleChoice = A->getValue();
+ opts::Output = StringSwitch<opts::OutputStyleTy>(OutputStyleChoice)
+ .Case("LLVM", opts::OutputStyleTy::LLVM)
+ .Case("GNU", opts::OutputStyleTy::GNU)
+ .Case("JSON", opts::OutputStyleTy::JSON)
+ .Default(opts::OutputStyleTy::UNKNOWN);
+ if (opts::Output == opts::OutputStyleTy::UNKNOWN) {
+ error("--elf-output-style value should be either 'LLVM', 'GNU', or "
+ "'JSON', but was '" +
+ OutputStyleChoice + "'");
+ }
+ }
+ opts::GnuHashTable = Args.hasArg(OPT_gnu_hash_table);
+ opts::HashSymbols = Args.hasArg(OPT_hash_symbols);
+ opts::HashTable = Args.hasArg(OPT_hash_table);
+ opts::HashHistogram = Args.hasArg(OPT_histogram);
+ opts::NeededLibraries = Args.hasArg(OPT_needed_libs);
+ opts::Notes = Args.hasArg(OPT_notes);
+ opts::PrettyPrint = Args.hasArg(OPT_pretty_print);
+ opts::ProgramHeaders = Args.hasArg(OPT_program_headers);
+ opts::RawRelr = Args.hasArg(OPT_raw_relr);
+ opts::SectionGroups = Args.hasArg(OPT_section_groups);
+ opts::VersionInfo = Args.hasArg(OPT_version_info);
+
+ // Mach-O specific options.
+ opts::MachODataInCode = Args.hasArg(OPT_macho_data_in_code);
+ opts::MachODysymtab = Args.hasArg(OPT_macho_dysymtab);
+ opts::MachOIndirectSymbols = Args.hasArg(OPT_macho_indirect_symbols);
+ opts::MachOLinkerOptions = Args.hasArg(OPT_macho_linker_options);
+ opts::MachOSegment = Args.hasArg(OPT_macho_segment);
+ opts::MachOVersionMin = Args.hasArg(OPT_macho_version_min);
+
+ // PE/COFF specific options.
+ opts::CodeView = Args.hasArg(OPT_codeview);
+ opts::CodeViewEnableGHash = Args.hasArg(OPT_codeview_ghash);
+ opts::CodeViewMergedTypes = Args.hasArg(OPT_codeview_merged_types);
+ opts::CodeViewSubsectionBytes = Args.hasArg(OPT_codeview_subsection_bytes);
+ opts::COFFBaseRelocs = Args.hasArg(OPT_coff_basereloc);
+ opts::COFFDebugDirectory = Args.hasArg(OPT_coff_debug_directory);
+ opts::COFFDirectives = Args.hasArg(OPT_coff_directives);
+ opts::COFFExports = Args.hasArg(OPT_coff_exports);
+ opts::COFFImports = Args.hasArg(OPT_coff_imports);
+ opts::COFFLoadConfig = Args.hasArg(OPT_coff_load_config);
+ opts::COFFResources = Args.hasArg(OPT_coff_resources);
+ opts::COFFTLSDirectory = Args.hasArg(OPT_coff_tls_directory);
+
+ // XCOFF specific options.
+ opts::XCOFFAuxiliaryHeader = Args.hasArg(OPT_auxiliary_header);
+
+ opts::InputFilenames = Args.getAllArgValues(OPT_INPUT);
+}
+
+namespace {
+struct ReadObjTypeTableBuilder {
+ ReadObjTypeTableBuilder()
+ : IDTable(Allocator), TypeTable(Allocator), GlobalIDTable(Allocator),
+ GlobalTypeTable(Allocator) {}
+
+ llvm::BumpPtrAllocator Allocator;
+ llvm::codeview::MergingTypeTableBuilder IDTable;
+ llvm::codeview::MergingTypeTableBuilder TypeTable;
+ llvm::codeview::GlobalTypeTableBuilder GlobalIDTable;
+ llvm::codeview::GlobalTypeTableBuilder GlobalTypeTable;
+ std::vector<OwningBinary<Binary>> Binaries;
+};
+} // namespace
+static ReadObjTypeTableBuilder CVTypes;
+
+/// Creates an format-specific object file dumper.
+static Expected<std::unique_ptr<ObjDumper>>
+createDumper(const ObjectFile &Obj, ScopedPrinter &Writer) {
+ if (const COFFObjectFile *COFFObj = dyn_cast<COFFObjectFile>(&Obj))
+ return createCOFFDumper(*COFFObj, Writer);
+
+ if (const ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj))
+ return createELFDumper(*ELFObj, Writer);
+
+ if (const MachOObjectFile *MachOObj = dyn_cast<MachOObjectFile>(&Obj))
+ return createMachODumper(*MachOObj, Writer);
+
+ if (const WasmObjectFile *WasmObj = dyn_cast<WasmObjectFile>(&Obj))
+ return createWasmDumper(*WasmObj, Writer);
+
+ if (const XCOFFObjectFile *XObj = dyn_cast<XCOFFObjectFile>(&Obj))
+ return createXCOFFDumper(*XObj, Writer);
+
+ return createStringError(errc::invalid_argument,
+ "unsupported object file format");
+}
+
+/// Dumps the specified object file.
+static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
+ const Archive *A = nullptr) {
+ std::string FileStr =
+ A ? Twine(A->getFileName() + "(" + Obj.getFileName() + ")").str()
+ : Obj.getFileName().str();
+
+ std::string ContentErrString;
+ if (Error ContentErr = Obj.initContent())
+ ContentErrString = "unable to continue dumping, the file is corrupt: " +
+ toString(std::move(ContentErr));
+
+ ObjDumper *Dumper;
+ Expected<std::unique_ptr<ObjDumper>> DumperOrErr = createDumper(Obj, Writer);
+ if (!DumperOrErr)
+ reportError(DumperOrErr.takeError(), FileStr);
+ Dumper = (*DumperOrErr).get();
+
+ Dumper->printFileSummary(FileStr, Obj, opts::InputFilenames, A);
+
+ if (opts::FileHeaders)
+ Dumper->printFileHeaders();
+
+ if (Obj.isXCOFF() && opts::XCOFFAuxiliaryHeader)
+ Dumper->printAuxiliaryHeader();
+
+ // This is only used for ELF currently. In some cases, when an object is
+ // corrupt (e.g. truncated), we can't dump anything except the file header.
+ if (!ContentErrString.empty())
+ reportError(createError(ContentErrString), FileStr);
+
+ if (opts::SectionDetails || opts::SectionHeaders) {
+ if (opts::Output == opts::GNU && opts::SectionDetails)
+ Dumper->printSectionDetails();
+ else
+ Dumper->printSectionHeaders();
+ }
+
+ if (opts::HashSymbols)
+ Dumper->printHashSymbols();
+ if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE)
+ Dumper->printProgramHeaders(opts::ProgramHeaders, opts::SectionMapping);
+ if (opts::DynamicTable)
+ Dumper->printDynamicTable();
+ if (opts::NeededLibraries)
+ Dumper->printNeededLibraries();
+ if (opts::Relocations)
+ Dumper->printRelocations();
+ if (opts::DynRelocs)
+ Dumper->printDynamicRelocations();
+ if (opts::UnwindInfo)
+ Dumper->printUnwindInfo();
+ if (opts::Symbols || opts::DynamicSymbols)
+ Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols);
+ if (!opts::StringDump.empty())
+ Dumper->printSectionsAsString(Obj, opts::StringDump);
+ if (!opts::HexDump.empty())
+ Dumper->printSectionsAsHex(Obj, opts::HexDump);
+ if (opts::HashTable)
+ Dumper->printHashTable();
+ if (opts::GnuHashTable)
+ Dumper->printGnuHashTable();
+ if (opts::VersionInfo)
+ Dumper->printVersionInfo();
+ if (opts::StringTable)
+ Dumper->printStringTable();
+ if (Obj.isELF()) {
+ if (opts::DependentLibraries)
+ Dumper->printDependentLibs();
+ if (opts::ELFLinkerOptions)
+ Dumper->printELFLinkerOptions();
+ if (opts::ArchSpecificInfo)
+ Dumper->printArchSpecificInfo();
+ if (opts::SectionGroups)
+ Dumper->printGroupSections();
+ if (opts::HashHistogram)
+ Dumper->printHashHistograms();
+ if (opts::CGProfile)
+ Dumper->printCGProfile();
+ if (opts::BBAddrMap)
+ Dumper->printBBAddrMaps();
+ if (opts::Addrsig)
+ Dumper->printAddrsig();
+ if (opts::Notes)
+ Dumper->printNotes();
+ }
+ if (Obj.isCOFF()) {
+ if (opts::COFFImports)
+ Dumper->printCOFFImports();
+ if (opts::COFFExports)
+ Dumper->printCOFFExports();
+ if (opts::COFFDirectives)
+ Dumper->printCOFFDirectives();
+ if (opts::COFFBaseRelocs)
+ Dumper->printCOFFBaseReloc();
+ if (opts::COFFDebugDirectory)
+ Dumper->printCOFFDebugDirectory();
+ if (opts::COFFTLSDirectory)
+ Dumper->printCOFFTLSDirectory();
+ if (opts::COFFResources)
+ Dumper->printCOFFResources();
+ if (opts::COFFLoadConfig)
+ Dumper->printCOFFLoadConfig();
+ if (opts::CGProfile)
+ Dumper->printCGProfile();
+ if (opts::Addrsig)
+ Dumper->printAddrsig();
+ if (opts::CodeView)
+ Dumper->printCodeViewDebugInfo();
+ if (opts::CodeViewMergedTypes)
+ Dumper->mergeCodeViewTypes(CVTypes.IDTable, CVTypes.TypeTable,
+ CVTypes.GlobalIDTable, CVTypes.GlobalTypeTable,
+ opts::CodeViewEnableGHash);
+ }
+ if (Obj.isMachO()) {
+ if (opts::MachODataInCode)
+ Dumper->printMachODataInCode();
+ if (opts::MachOIndirectSymbols)
+ Dumper->printMachOIndirectSymbols();
+ if (opts::MachOLinkerOptions)
+ Dumper->printMachOLinkerOptions();
+ if (opts::MachOSegment)
+ Dumper->printMachOSegment();
+ if (opts::MachOVersionMin)
+ Dumper->printMachOVersionMin();
+ if (opts::MachODysymtab)
+ Dumper->printMachODysymtab();
+ if (opts::CGProfile)
+ Dumper->printCGProfile();
+ }
+ if (opts::PrintStackMap)
+ Dumper->printStackMap();
+ if (opts::PrintStackSizes)
+ Dumper->printStackSizes();
+}
+
+/// Dumps each object file in \a Arc;
+static void dumpArchive(const Archive *Arc, ScopedPrinter &Writer) {
+ Error Err = Error::success();
+ for (auto &Child : Arc->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+ reportError(std::move(E), Arc->getFileName());
+ continue;
+ }
+
+ Binary *Bin = ChildOrErr->get();
+ if (ObjectFile *Obj = dyn_cast<ObjectFile>(Bin))
+ dumpObject(*Obj, Writer, Arc);
+ else if (COFFImportFile *Imp = dyn_cast<COFFImportFile>(Bin))
+ dumpCOFFImportFile(Imp, Writer);
+ else
+ reportWarning(createStringError(errc::invalid_argument,
+ Bin->getFileName() +
+ " has an unsupported file type"),
+ Arc->getFileName());
+ }
+ if (Err)
+ reportError(std::move(Err), Arc->getFileName());
+}
+
+/// Dumps each object file in \a MachO Universal Binary;
+static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary,
+ ScopedPrinter &Writer) {
+ for (const MachOUniversalBinary::ObjectForArch &Obj : UBinary->objects()) {
+ Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile();
+ if (ObjOrErr)
+ dumpObject(*ObjOrErr.get(), Writer);
+ else if (auto E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError()))
+ reportError(ObjOrErr.takeError(), UBinary->getFileName());
+ else if (Expected<std::unique_ptr<Archive>> AOrErr = Obj.getAsArchive())
+ dumpArchive(&*AOrErr.get(), Writer);
+ }
+}
+
+/// Dumps \a WinRes, Windows Resource (.res) file;
+static void dumpWindowsResourceFile(WindowsResource *WinRes,
+ ScopedPrinter &Printer) {
+ WindowsRes::Dumper Dumper(WinRes, Printer);
+ if (auto Err = Dumper.printData())
+ reportError(std::move(Err), WinRes->getFileName());
+}
+
+
+/// Opens \a File and dumps it.
+static void dumpInput(StringRef File, ScopedPrinter &Writer) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
+ MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
+ if (std::error_code EC = FileOrErr.getError())
+ return reportError(errorCodeToError(EC), File);
+
+ std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
+ file_magic Type = identify_magic(Buffer->getBuffer());
+ if (Type == file_magic::bitcode) {
+ reportWarning(createStringError(errc::invalid_argument,
+ "bitcode files are not supported"),
+ File);
+ return;
+ }
+
+ Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(
+ Buffer->getMemBufferRef(), /*Context=*/nullptr, /*InitContent=*/false);
+ if (!BinaryOrErr)
+ reportError(BinaryOrErr.takeError(), File);
+
+ std::unique_ptr<Binary> Bin = std::move(*BinaryOrErr);
+ if (Archive *Arc = dyn_cast<Archive>(Bin.get()))
+ dumpArchive(Arc, Writer);
+ else if (MachOUniversalBinary *UBinary =
+ dyn_cast<MachOUniversalBinary>(Bin.get()))
+ dumpMachOUniversalBinary(UBinary, Writer);
+ else if (ObjectFile *Obj = dyn_cast<ObjectFile>(Bin.get()))
+ dumpObject(*Obj, Writer);
+ else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(Bin.get()))
+ dumpCOFFImportFile(Import, Writer);
+ else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(Bin.get()))
+ dumpWindowsResourceFile(WinRes, Writer);
+ else
+ llvm_unreachable("unrecognized file type");
+
+ CVTypes.Binaries.push_back(
+ OwningBinary<Binary>(std::move(Bin), std::move(Buffer)));
+}
+
+std::unique_ptr<ScopedPrinter> createWriter() {
+ if (opts::Output == opts::JSON)
+ return std::make_unique<JSONScopedPrinter>(
+ fouts(), opts::PrettyPrint ? 2 : 0, std::make_unique<ListScope>());
+ return std::make_unique<ScopedPrinter>(fouts());
+}
+
+int main(int argc, char *argv[]) {
+ InitLLVM X(argc, argv);
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ ReadobjOptTable Tbl;
+ ToolName = argv[0];
+ opt::InputArgList Args =
+ Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
+ error(Msg);
+ exit(1);
+ });
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(
+ outs(),
+ (Twine(ToolName) + " [options] <input object files>").str().c_str(),
+ "LLVM Object Reader");
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ return 0;
+ }
+ if (Args.hasArg(OPT_version)) {
+ cl::PrintVersionMessage();
+ return 0;
+ }
+
+ if (sys::path::stem(argv[0]).contains("readelf"))
+ opts::Output = opts::GNU;
+ parseOptions(Args);
+
+ // Default to print error if no filename is specified.
+ if (opts::InputFilenames.empty()) {
+ error("no input files specified");
+ }
+
+ if (opts::All) {
+ opts::FileHeaders = true;
+ opts::XCOFFAuxiliaryHeader = 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::Output == opts::LLVM) {
+ opts::Addrsig = true;
+ opts::PrintStackSizes = true;
+ }
+ }
+
+ if (opts::Headers) {
+ opts::FileHeaders = true;
+ opts::XCOFFAuxiliaryHeader = true;
+ opts::ProgramHeaders = true;
+ opts::SectionHeaders = true;
+ }
+
+ std::unique_ptr<ScopedPrinter> Writer = createWriter();
+
+ for (const std::string &I : opts::InputFilenames)
+ dumpInput(I, *Writer.get());
+
+ if (opts::CodeViewMergedTypes) {
+ if (opts::CodeViewEnableGHash)
+ dumpCodeViewMergedTypes(*Writer.get(), CVTypes.GlobalIDTable.records(),
+ CVTypes.GlobalTypeTable.records());
+ else
+ dumpCodeViewMergedTypes(*Writer.get(), CVTypes.IDTable.records(),
+ CVTypes.TypeTable.records());
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/llvm-readobj.h b/contrib/libs/llvm14/tools/llvm-readobj/llvm-readobj.h
new file mode 100644
index 00000000000..0ea695d1673
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/llvm-readobj.h
@@ -0,0 +1,52 @@
+//===-- llvm-readobj.h ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_READOBJ_LLVM_READOBJ_H
+#define LLVM_TOOLS_LLVM_READOBJ_LLVM_READOBJ_H
+
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/Error.h"
+#include <string>
+
+namespace llvm {
+ namespace object {
+ class RelocationRef;
+ }
+
+ // Various helper functions.
+ [[noreturn]] void reportError(Error Err, StringRef Input);
+ void reportWarning(Error Err, StringRef Input);
+
+ template <class T> T unwrapOrError(StringRef Input, Expected<T> EO) {
+ if (EO)
+ return *EO;
+ reportError(EO.takeError(), Input);
+ }
+} // namespace llvm
+
+namespace opts {
+extern bool SectionRelocations;
+extern bool SectionSymbols;
+extern bool SectionData;
+extern bool ExpandRelocs;
+extern bool RawRelr;
+extern bool CodeViewSubsectionBytes;
+extern bool Demangle;
+enum OutputStyleTy { LLVM, GNU, JSON, UNKNOWN };
+extern OutputStyleTy Output;
+} // namespace opts
+
+#define LLVM_READOBJ_ENUM_ENT(ns, enum) \
+ { #enum, ns::enum }
+
+#define LLVM_READOBJ_ENUM_CLASS_ENT(enum_class, enum) \
+ { #enum, std::underlying_type<enum_class>::type(enum_class::enum) }
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-readobj/ya.make b/contrib/libs/llvm14/tools/llvm-readobj/ya.make
new file mode 100644
index 00000000000..d05c353d131
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-readobj/ya.make
@@ -0,0 +1,53 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/DebugInfo/MSF
+ contrib/libs/llvm14/lib/DebugInfo/PDB
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-readobj
+ contrib/libs/llvm14/tools/llvm-readobj
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ ARMWinEHPrinter.cpp
+ COFFDumper.cpp
+ COFFImportDumper.cpp
+ ELFDumper.cpp
+ MachODumper.cpp
+ ObjDumper.cpp
+ WasmDumper.cpp
+ Win64EHDumper.cpp
+ WindowsResourceDumper.cpp
+ XCOFFDumper.cpp
+ llvm-readobj.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/DeltaManager.cpp b/contrib/libs/llvm14/tools/llvm-reduce/DeltaManager.cpp
new file mode 100644
index 00000000000..4d646a7c861
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/DeltaManager.cpp
@@ -0,0 +1,134 @@
+//===- DeltaManager.cpp - Runs Delta Passes to reduce Input ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file calls each specialized Delta pass in order to reduce the input IR
+// file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DeltaManager.h"
+#include "TestRunner.h"
+#include "deltas/Delta.h"
+#include "deltas/ReduceAliases.h"
+#include "deltas/ReduceArguments.h"
+#include "deltas/ReduceAttributes.h"
+#include "deltas/ReduceBasicBlocks.h"
+#include "deltas/ReduceFunctionBodies.h"
+#include "deltas/ReduceFunctions.h"
+#include "deltas/ReduceGlobalObjects.h"
+#include "deltas/ReduceGlobalValues.h"
+#include "deltas/ReduceGlobalVarInitializers.h"
+#include "deltas/ReduceGlobalVars.h"
+#include "deltas/ReduceInstructions.h"
+#include "deltas/ReduceInstructionsMIR.h"
+#include "deltas/ReduceMetadata.h"
+#include "deltas/ReduceModuleData.h"
+#include "deltas/ReduceOperandBundles.h"
+#include "deltas/ReduceOperands.h"
+#include "deltas/ReduceOperandsSkip.h"
+#include "deltas/ReduceOperandsToArgs.h"
+#include "deltas/ReduceSpecialGlobals.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace llvm;
+
+static cl::opt<std::string>
+ DeltaPasses("delta-passes",
+ cl::desc("Delta passes to run, separated by commas. By "
+ "default, run all delta passes."));
+
+#define DELTA_PASSES \
+ DELTA_PASS("special-globals", reduceSpecialGlobalsDeltaPass) \
+ DELTA_PASS("aliases", reduceAliasesDeltaPass) \
+ DELTA_PASS("function-bodies", reduceFunctionBodiesDeltaPass) \
+ DELTA_PASS("functions", reduceFunctionsDeltaPass) \
+ DELTA_PASS("basic-blocks", reduceBasicBlocksDeltaPass) \
+ DELTA_PASS("global-values", reduceGlobalValuesDeltaPass) \
+ DELTA_PASS("global-objects", reduceGlobalObjectsDeltaPass) \
+ DELTA_PASS("global-initializers", reduceGlobalsInitializersDeltaPass) \
+ DELTA_PASS("global-variables", reduceGlobalsDeltaPass) \
+ DELTA_PASS("metadata", reduceMetadataDeltaPass) \
+ DELTA_PASS("arguments", reduceArgumentsDeltaPass) \
+ DELTA_PASS("instructions", reduceInstructionsDeltaPass) \
+ DELTA_PASS("operands-zero", reduceOperandsZeroDeltaPass) \
+ DELTA_PASS("operands-one", reduceOperandsOneDeltaPass) \
+ DELTA_PASS("operands-undef", reduceOperandsUndefDeltaPass) \
+ DELTA_PASS("operands-to-args", reduceOperandsToArgsDeltaPass) \
+ DELTA_PASS("operands-skip", reduceOperandsSkipDeltaPass) \
+ DELTA_PASS("operand-bundles", reduceOperandBundesDeltaPass) \
+ DELTA_PASS("attributes", reduceAttributesDeltaPass) \
+ DELTA_PASS("module-data", reduceModuleDataDeltaPass)
+
+#define DELTA_PASSES_MIR \
+ DELTA_PASS("instructions", reduceInstructionsMIRDeltaPass)
+
+static void runAllDeltaPasses(TestRunner &Tester) {
+#define DELTA_PASS(NAME, FUNC) FUNC(Tester);
+ if (Tester.getProgram().isMIR()) {
+ DELTA_PASSES_MIR
+ } else {
+ DELTA_PASSES
+ }
+#undef DELTA_PASS
+}
+
+static void runDeltaPassName(TestRunner &Tester, StringRef PassName) {
+#define DELTA_PASS(NAME, FUNC) \
+ if (PassName == NAME) { \
+ FUNC(Tester); \
+ return; \
+ }
+ if (Tester.getProgram().isMIR()) {
+ DELTA_PASSES_MIR
+ } else {
+ DELTA_PASSES
+ }
+#undef DELTA_PASS
+ errs() << "unknown pass \"" << PassName << "\"\n";
+ exit(1);
+}
+
+void llvm::printDeltaPasses(raw_ostream &OS) {
+ OS << "Delta passes (pass to `--delta-passes=` as a comma separated list):\n";
+#define DELTA_PASS(NAME, FUNC) OS << " " << NAME << "\n";
+ OS << " IR:\n";
+ DELTA_PASSES
+ OS << " MIR:\n";
+ DELTA_PASSES_MIR
+#undef DELTA_PASS
+}
+
+// FIXME: We might want to use a different metric than "number of
+// bytes in serialized IR" to detect non-progress of the main delta
+// loop
+static int getIRSize(TestRunner &Tester) {
+ std::string Str;
+ raw_string_ostream SS(Str);
+ Tester.getProgram().print(SS, /*AnnotationWriter=*/nullptr);
+ return Str.length();
+}
+
+void llvm::runDeltaPasses(TestRunner &Tester, int MaxPassIterations) {
+ int OldSize = getIRSize(Tester);
+ for (int Iter = 0; Iter < MaxPassIterations; ++Iter) {
+ if (DeltaPasses.empty()) {
+ runAllDeltaPasses(Tester);
+ } else {
+ StringRef Passes = DeltaPasses;
+ while (!Passes.empty()) {
+ auto Split = Passes.split(",");
+ runDeltaPassName(Tester, Split.first);
+ Passes = Split.second;
+ }
+ }
+ int NewSize = getIRSize(Tester);
+ if (NewSize >= OldSize)
+ break;
+ OldSize = NewSize;
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/DeltaManager.h b/contrib/libs/llvm14/tools/llvm-reduce/DeltaManager.h
new file mode 100644
index 00000000000..b72e5604bd5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/DeltaManager.h
@@ -0,0 +1,25 @@
+//===- DeltaManager.h - Runs Delta Passes to reduce Input -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file calls each specialized Delta pass in order to reduce the input IR
+// file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAMANAGER_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAMANAGER_H
+
+namespace llvm {
+class raw_ostream;
+class TestRunner;
+
+void printDeltaPasses(raw_ostream &OS);
+void runDeltaPasses(TestRunner &Tester, int MaxPassIterations);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/ReducerWorkItem.cpp b/contrib/libs/llvm14/tools/llvm-reduce/ReducerWorkItem.cpp
new file mode 100644
index 00000000000..ba36eb827d7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/ReducerWorkItem.cpp
@@ -0,0 +1,175 @@
+//===- ReducerWorkItem.cpp - Wrapper for Module and MachineFunction -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReducerWorkItem.h"
+#include "llvm/CodeGen/MIRParser/MIRParser.h"
+#include "llvm/CodeGen/MIRPrinter.h"
+#include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+static std::unique_ptr<MachineFunction> cloneMF(MachineFunction *SrcMF) {
+ auto DstMF = std::make_unique<MachineFunction>(
+ SrcMF->getFunction(), SrcMF->getTarget(), SrcMF->getSubtarget(),
+ SrcMF->getFunctionNumber(), SrcMF->getMMI());
+ DenseMap<MachineBasicBlock *, MachineBasicBlock *> Src2DstMBB;
+ DenseMap<Register, Register> Src2DstReg;
+
+ auto *SrcMRI = &SrcMF->getRegInfo();
+ auto *DstMRI = &DstMF->getRegInfo();
+
+ // Create vregs.
+ for (auto &SrcMBB : *SrcMF) {
+ for (auto &SrcMI : SrcMBB) {
+ for (unsigned I = 0, E = SrcMI.getNumOperands(); I < E; ++I) {
+ auto &DMO = SrcMI.getOperand(I);
+ if (!DMO.isReg() || !DMO.isDef())
+ continue;
+ Register SrcReg = DMO.getReg();
+ if (Register::isPhysicalRegister(SrcReg))
+ continue;
+ auto SrcRC = SrcMRI->getRegClass(SrcReg);
+ auto DstReg = DstMRI->createVirtualRegister(SrcRC);
+ Src2DstReg[SrcReg] = DstReg;
+ }
+ }
+ }
+
+ // Clone blocks.
+ for (auto &SrcMBB : *SrcMF)
+ Src2DstMBB[&SrcMBB] = DstMF->CreateMachineBasicBlock();
+ // Link blocks.
+ for (auto &SrcMBB : *SrcMF) {
+ auto *DstMBB = Src2DstMBB[&SrcMBB];
+ DstMF->push_back(DstMBB);
+ for (auto It = SrcMBB.succ_begin(), IterEnd = SrcMBB.succ_end();
+ It != IterEnd; ++It) {
+ auto *SrcSuccMBB = *It;
+ auto *DstSuccMBB = Src2DstMBB[SrcSuccMBB];
+ DstMBB->addSuccessor(DstSuccMBB);
+ }
+ for (auto &LI : SrcMBB.liveins())
+ DstMBB->addLiveIn(LI);
+ }
+ // Clone instructions.
+ for (auto &SrcMBB : *SrcMF) {
+ auto *DstMBB = Src2DstMBB[&SrcMBB];
+ for (auto &SrcMI : SrcMBB) {
+ const auto &MCID =
+ DstMF->getSubtarget().getInstrInfo()->get(SrcMI.getOpcode());
+ auto *DstMI = DstMF->CreateMachineInstr(MCID, SrcMI.getDebugLoc(),
+ /*NoImplicit=*/true);
+ DstMBB->push_back(DstMI);
+ for (auto &SrcMO : SrcMI.operands()) {
+ MachineOperand DstMO(SrcMO);
+ DstMO.clearParent();
+ // Update vreg.
+ if (DstMO.isReg() && Src2DstReg.count(DstMO.getReg())) {
+ DstMO.setReg(Src2DstReg[DstMO.getReg()]);
+ }
+ // Update MBB.
+ if (DstMO.isMBB()) {
+ DstMO.setMBB(Src2DstMBB[DstMO.getMBB()]);
+ }
+ DstMI->addOperand(DstMO);
+ }
+ DstMI->setMemRefs(*DstMF, SrcMI.memoperands());
+ }
+ }
+
+ DstMF->verify(nullptr, "", /*AbortOnError=*/true);
+ return DstMF;
+}
+
+std::unique_ptr<ReducerWorkItem> parseReducerWorkItem(StringRef Filename,
+ LLVMContext &Ctxt,
+ MachineModuleInfo *MMI) {
+ auto MMM = std::make_unique<ReducerWorkItem>();
+ if (MMI) {
+ auto FileOrErr = MemoryBuffer::getFileOrSTDIN(Filename, /*IsText=*/true);
+ std::unique_ptr<MIRParser> MParser =
+ createMIRParser(std::move(FileOrErr.get()), Ctxt);
+
+ auto SetDataLayout =
+ [&](StringRef DataLayoutTargetTriple) -> Optional<std::string> {
+ return MMI->getTarget().createDataLayout().getStringRepresentation();
+ };
+
+ std::unique_ptr<Module> M = MParser->parseIRModule(SetDataLayout);
+ MParser->parseMachineFunctions(*M, *MMI);
+ MachineFunction *MF = nullptr;
+ for (auto &F : *M) {
+ if (auto *MF4F = MMI->getMachineFunction(F)) {
+ // XXX: Maybe it would not be a lot of effort to handle multiple MFs by
+ // simply storing them in a ReducerWorkItem::SmallVector or similar. The
+ // single MF use-case seems a lot more common though so that will do for
+ // now.
+ assert(!MF && "Only single MF supported!");
+ MF = MF4F;
+ }
+ }
+ assert(MF && "No MF found!");
+
+ MMM->M = std::move(M);
+ MMM->MF = cloneMF(MF);
+ } else {
+ SMDiagnostic Err;
+ std::unique_ptr<Module> Result = parseIRFile(Filename, Err, Ctxt);
+ if (!Result) {
+ Err.print("llvm-reduce", errs());
+ return std::unique_ptr<ReducerWorkItem>();
+ }
+ MMM->M = std::move(Result);
+ }
+ if (verifyReducerWorkItem(*MMM, &errs())) {
+ errs() << "Error: " << Filename << " - input module is broken!\n";
+ return std::unique_ptr<ReducerWorkItem>();
+ }
+ return MMM;
+}
+
+std::unique_ptr<ReducerWorkItem>
+cloneReducerWorkItem(const ReducerWorkItem &MMM) {
+ auto CloneMMM = std::make_unique<ReducerWorkItem>();
+ if (MMM.MF) {
+ // Note that we cannot clone the Module as then we would need a way to
+ // updated the cloned MachineFunction's IR references.
+ // XXX: Actually have a look at
+ // std::unique_ptr<Module> CloneModule(const Module &M, ValueToValueMapTy
+ // &VMap);
+ CloneMMM->M = MMM.M;
+ CloneMMM->MF = cloneMF(MMM.MF.get());
+ } else {
+ CloneMMM->M = CloneModule(*MMM.M);
+ }
+ return CloneMMM;
+}
+
+bool verifyReducerWorkItem(const ReducerWorkItem &MMM, raw_fd_ostream *OS) {
+ if (verifyModule(*MMM.M, OS))
+ return true;
+ if (MMM.MF && !MMM.MF->verify(nullptr, "", /*AbortOnError=*/false))
+ return true;
+ return false;
+}
+
+void ReducerWorkItem::print(raw_ostream &ROS, void *p) const {
+ if (MF) {
+ printMIR(ROS, *M);
+ printMIR(ROS, *MF);
+ } else {
+ M->print(ROS, /*AssemblyAnnotationWriter=*/nullptr,
+ /*ShouldPreserveUseListOrder=*/true);
+ }
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/ReducerWorkItem.h b/contrib/libs/llvm14/tools/llvm-reduce/ReducerWorkItem.h
new file mode 100644
index 00000000000..a86e158d916
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/ReducerWorkItem.h
@@ -0,0 +1,37 @@
+//===- ReducerWorkItem.h - Wrapper for Module and MachineFunction ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_REDUCERWORKITEM_H
+#define LLVM_TOOLS_LLVM_REDUCE_REDUCERWORKITEM_H
+
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/IR/Module.h"
+
+using namespace llvm;
+
+class ReducerWorkItem {
+public:
+ std::shared_ptr<Module> M;
+ std::unique_ptr<MachineFunction> MF;
+ void print(raw_ostream &ROS, void *p = nullptr) const;
+ bool isMIR() { return MF != nullptr; }
+ operator Module &() const { return *M; }
+ operator MachineFunction &() const { return *MF; }
+};
+
+std::unique_ptr<ReducerWorkItem> parseReducerWorkItem(StringRef Filename,
+ LLVMContext &Ctxt,
+ MachineModuleInfo *MMI);
+
+std::unique_ptr<ReducerWorkItem>
+cloneReducerWorkItem(const ReducerWorkItem &MMM);
+
+bool verifyReducerWorkItem(const ReducerWorkItem &MMM, raw_fd_ostream *OS);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/TestRunner.cpp b/contrib/libs/llvm14/tools/llvm-reduce/TestRunner.cpp
new file mode 100644
index 00000000000..e8c12138936
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/TestRunner.cpp
@@ -0,0 +1,45 @@
+//===-- TestRunner.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestRunner.h"
+
+using namespace llvm;
+
+TestRunner::TestRunner(StringRef TestName,
+ const std::vector<std::string> &TestArgs,
+ std::unique_ptr<ReducerWorkItem> Program)
+ : TestName(TestName), TestArgs(TestArgs), Program(std::move(Program)) {
+ assert(this->Program && "Initialized with null program?");
+}
+
+/// Runs the interestingness test, passes file to be tested as first argument
+/// and other specified test arguments after that.
+int TestRunner::run(StringRef Filename) {
+ std::vector<StringRef> ProgramArgs;
+ ProgramArgs.push_back(TestName);
+
+ for (const auto &Arg : TestArgs)
+ ProgramArgs.push_back(Arg);
+
+ ProgramArgs.push_back(Filename);
+
+ std::string ErrMsg;
+ int Result = sys::ExecuteAndWait(
+ TestName, ProgramArgs, /*Env=*/None, /*Redirects=*/None,
+ /*SecondsToWait=*/0, /*MemoryLimit=*/0, &ErrMsg);
+
+ if (Result < 0) {
+ Error E = make_error<StringError>("Error running interesting-ness test: " +
+ ErrMsg,
+ inconvertibleErrorCode());
+ errs() << toString(std::move(E));
+ exit(1);
+ }
+
+ return !Result;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/TestRunner.h b/contrib/libs/llvm14/tools/llvm-reduce/TestRunner.h
new file mode 100644
index 00000000000..c14d0459a0f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/TestRunner.h
@@ -0,0 +1,51 @@
+//===-- tools/llvm-reduce/TestRunner.h ---------------------------*- C++ -*-===/
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_TESTRUNNER_H
+#define LLVM_TOOLS_LLVM_REDUCE_TESTRUNNER_H
+
+#include "ReducerWorkItem.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+#include <vector>
+
+namespace llvm {
+
+// This class contains all the info necessary for running the provided
+// interesting-ness test, as well as the most reduced module and its
+// respective filename.
+class TestRunner {
+public:
+ TestRunner(StringRef TestName, const std::vector<std::string> &TestArgs,
+ std::unique_ptr<ReducerWorkItem> Program);
+
+ /// Runs the interesting-ness test for the specified file
+ /// @returns 0 if test was successful, 1 if otherwise
+ int run(StringRef Filename);
+
+ /// Returns the most reduced version of the original testcase
+ ReducerWorkItem &getProgram() const { return *Program; }
+
+ void setProgram(std::unique_ptr<ReducerWorkItem> P) {
+ assert(P && "Setting null program?");
+ Program = std::move(P);
+ }
+
+private:
+ StringRef TestName;
+ const std::vector<std::string> &TestArgs;
+ std::unique_ptr<ReducerWorkItem> Program;
+};
+
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/Delta.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/Delta.cpp
new file mode 100644
index 00000000000..1bab82f823c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/Delta.cpp
@@ -0,0 +1,404 @@
+//===- Delta.cpp - Delta Debugging Algorithm Implementation ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation for the Delta Debugging Algorithm:
+// it splits a given set of Targets (i.e. Functions, Instructions, BBs, etc.)
+// into chunks and tries to reduce the number chunks that are interesting.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Delta.h"
+#include "ReducerWorkItem.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include <fstream>
+#include <set>
+
+using namespace llvm;
+
+static cl::opt<bool> AbortOnInvalidReduction(
+ "abort-on-invalid-reduction",
+ cl::desc("Abort if any reduction results in invalid IR"));
+
+static cl::opt<unsigned int> StartingGranularityLevel(
+ "starting-granularity-level",
+ cl::desc("Number of times to divide chunks prior to first test"));
+
+static cl::opt<bool> TmpFilesAsBitcode(
+ "write-tmp-files-as-bitcode",
+ cl::desc("Write temporary files as bitcode, instead of textual IR"),
+ cl::init(false));
+
+#ifdef LLVM_ENABLE_THREADS
+static cl::opt<unsigned> NumJobs(
+ "j",
+ cl::desc("Maximum number of threads to use to process chunks. Set to 1 to "
+ "disables parallelism."),
+ cl::init(1));
+#else
+unsigned NumJobs = 1;
+#endif
+
+void writeOutput(ReducerWorkItem &M, llvm::StringRef Message);
+
+bool isReduced(ReducerWorkItem &M, TestRunner &Test,
+ SmallString<128> &CurrentFilepath) {
+ // Write ReducerWorkItem to tmp file
+ int FD;
+ std::error_code EC = sys::fs::createTemporaryFile(
+ "llvm-reduce", M.isMIR() ? "mir" : (TmpFilesAsBitcode ? "bc" : "ll"), FD,
+ CurrentFilepath);
+ if (EC) {
+ errs() << "Error making unique filename: " << EC.message() << "!\n";
+ exit(1);
+ }
+
+ if (TmpFilesAsBitcode) {
+ llvm::raw_fd_ostream OutStream(FD, true);
+ WriteBitcodeToFile(M, OutStream);
+ OutStream.close();
+ if (OutStream.has_error()) {
+ errs() << "Error emitting bitcode to file '" << CurrentFilepath << "'!\n";
+ sys::fs::remove(CurrentFilepath);
+ exit(1);
+ }
+ bool Res = Test.run(CurrentFilepath);
+ sys::fs::remove(CurrentFilepath);
+ return Res;
+ }
+ ToolOutputFile Out(CurrentFilepath, FD);
+ M.print(Out.os(), /*AnnotationWriter=*/nullptr);
+ Out.os().close();
+ if (Out.os().has_error()) {
+ errs() << "Error emitting bitcode to file '" << CurrentFilepath << "'!\n";
+ exit(1);
+ }
+
+ // Current Chunks aren't interesting
+ return Test.run(CurrentFilepath);
+}
+
+/// Counts the amount of lines for a given file
+static int getLines(StringRef Filepath) {
+ int Lines = 0;
+ std::string CurrLine;
+ std::ifstream FileStream{std::string(Filepath)};
+
+ while (std::getline(FileStream, CurrLine))
+ ++Lines;
+
+ return Lines;
+}
+
+/// Splits Chunks in half and prints them.
+/// If unable to split (when chunk size is 1) returns false.
+static bool increaseGranularity(std::vector<Chunk> &Chunks) {
+ errs() << "Increasing granularity...";
+ std::vector<Chunk> NewChunks;
+ bool SplitOne = false;
+
+ for (auto &C : Chunks) {
+ if (C.End - C.Begin == 0)
+ NewChunks.push_back(C);
+ else {
+ int Half = (C.Begin + C.End) / 2;
+ NewChunks.push_back({C.Begin, Half});
+ NewChunks.push_back({Half + 1, C.End});
+ SplitOne = true;
+ }
+ }
+ if (SplitOne) {
+ Chunks = NewChunks;
+ errs() << "Success! New Chunks:\n";
+ for (auto C : Chunks) {
+ errs() << '\t';
+ C.print();
+ errs() << '\n';
+ }
+ }
+ return SplitOne;
+}
+// Check if \p ChunkToCheckForUninterestingness is interesting. Returns the
+// modified module if the chunk resulted in a reduction.
+template <typename T>
+static std::unique_ptr<ReducerWorkItem>
+CheckChunk(Chunk &ChunkToCheckForUninterestingness,
+ std::unique_ptr<ReducerWorkItem> Clone, TestRunner &Test,
+ function_ref<void(Oracle &, T &)> ExtractChunksFromModule,
+ std::set<Chunk> &UninterestingChunks,
+ std::vector<Chunk> &ChunksStillConsideredInteresting) {
+ // Take all of ChunksStillConsideredInteresting chunks, except those we've
+ // already deemed uninteresting (UninterestingChunks) but didn't remove
+ // from ChunksStillConsideredInteresting yet, and additionally ignore
+ // ChunkToCheckForUninterestingness chunk.
+ std::vector<Chunk> CurrentChunks;
+ CurrentChunks.reserve(ChunksStillConsideredInteresting.size() -
+ UninterestingChunks.size() - 1);
+ copy_if(ChunksStillConsideredInteresting, std::back_inserter(CurrentChunks),
+ [&](const Chunk &C) {
+ return !UninterestingChunks.count(C) &&
+ C != ChunkToCheckForUninterestingness;
+ });
+
+ // Generate Module with only Targets inside Current Chunks
+ Oracle O(CurrentChunks);
+ ExtractChunksFromModule(O, *Clone);
+
+ // Some reductions may result in invalid IR. Skip such reductions.
+ if (verifyReducerWorkItem(*Clone, &errs())) {
+ if (AbortOnInvalidReduction) {
+ errs() << "Invalid reduction\n";
+ exit(1);
+ }
+ errs() << " **** WARNING | reduction resulted in invalid module, "
+ "skipping\n";
+ return nullptr;
+ }
+
+ errs() << "Ignoring: ";
+ ChunkToCheckForUninterestingness.print();
+ for (const Chunk &C : UninterestingChunks)
+ C.print();
+
+ SmallString<128> CurrentFilepath;
+ if (!isReduced(*Clone, Test, CurrentFilepath)) {
+ // Program became non-reduced, so this chunk appears to be interesting.
+ errs() << "\n";
+ return nullptr;
+ }
+ return Clone;
+}
+
+template <typename T>
+SmallString<0> ProcessChunkFromSerializedBitcode(
+ Chunk &ChunkToCheckForUninterestingness, TestRunner &Test,
+ function_ref<void(Oracle &, T &)> ExtractChunksFromModule,
+ std::set<Chunk> &UninterestingChunks,
+ std::vector<Chunk> &ChunksStillConsideredInteresting,
+ SmallString<0> &OriginalBC, std::atomic<bool> &AnyReduced) {
+ LLVMContext Ctx;
+ Expected<std::unique_ptr<Module>> MOrErr = parseBitcodeFile(
+ MemoryBufferRef(StringRef(OriginalBC.data(), OriginalBC.size()),
+ "<llvm-reduce tmp module>"),
+ Ctx);
+ if (!MOrErr)
+ report_fatal_error("Failed to read bitcode");
+ auto CloneMMM = std::make_unique<ReducerWorkItem>();
+ CloneMMM->M = std::move(MOrErr.get());
+
+ SmallString<0> Result;
+ if (std::unique_ptr<ReducerWorkItem> ChunkResult =
+ CheckChunk(ChunkToCheckForUninterestingness, std::move(CloneMMM),
+ Test, ExtractChunksFromModule, UninterestingChunks,
+ ChunksStillConsideredInteresting)) {
+ raw_svector_ostream BCOS(Result);
+ WriteBitcodeToFile(*ChunkResult->M, BCOS);
+ // Communicate that the task reduced a chunk.
+ AnyReduced = true;
+ }
+ return Result;
+}
+
+/// Runs the Delta Debugging algorithm, splits the code into chunks and
+/// reduces the amount of chunks that are considered interesting by the
+/// given test. The number of chunks is determined by a preliminary run of the
+/// reduction pass where no change must be made to the module.
+template <typename T>
+void runDeltaPassInt(
+ TestRunner &Test,
+ function_ref<void(Oracle &, T &)> ExtractChunksFromModule) {
+ assert(!verifyReducerWorkItem(Test.getProgram(), &errs()) &&
+ "input module is broken before making changes");
+
+ SmallString<128> CurrentFilepath;
+ if (!isReduced(Test.getProgram(), Test, CurrentFilepath)) {
+ errs() << "\nInput isn't interesting! Verify interesting-ness test\n";
+ exit(1);
+ }
+
+ int Targets;
+ {
+ // Count the number of chunks by counting the number of calls to
+ // Oracle::shouldKeep() but always returning true so no changes are
+ // made.
+ std::vector<Chunk> AllChunks = {{0, INT_MAX}};
+ Oracle Counter(AllChunks);
+ ExtractChunksFromModule(Counter, Test.getProgram());
+ Targets = Counter.count();
+
+ assert(!verifyReducerWorkItem(Test.getProgram(), &errs()) &&
+ "input module is broken after counting chunks");
+ assert(isReduced(Test.getProgram(), Test, CurrentFilepath) &&
+ "input module no longer interesting after counting chunks");
+
+#ifndef NDEBUG
+ // Make sure that the number of chunks does not change as we reduce.
+ std::vector<Chunk> NoChunks;
+ Oracle NoChunksCounter(NoChunks);
+ std::unique_ptr<ReducerWorkItem> Clone =
+ cloneReducerWorkItem(Test.getProgram());
+ ExtractChunksFromModule(NoChunksCounter, *Clone);
+ assert(Targets == NoChunksCounter.count() &&
+ "number of chunks changes when reducing");
+#endif
+ }
+ if (!Targets) {
+ errs() << "\nNothing to reduce\n";
+ return;
+ }
+
+ std::vector<Chunk> ChunksStillConsideredInteresting = {{0, Targets - 1}};
+ std::unique_ptr<ReducerWorkItem> ReducedProgram;
+
+ for (unsigned int Level = 0; Level < StartingGranularityLevel; Level++) {
+ increaseGranularity(ChunksStillConsideredInteresting);
+ }
+
+ std::atomic<bool> AnyReduced;
+ std::unique_ptr<ThreadPool> ChunkThreadPoolPtr;
+ if (NumJobs > 1)
+ ChunkThreadPoolPtr =
+ std::make_unique<ThreadPool>(hardware_concurrency(NumJobs));
+
+ bool FoundAtLeastOneNewUninterestingChunkWithCurrentGranularity;
+ do {
+ FoundAtLeastOneNewUninterestingChunkWithCurrentGranularity = false;
+
+ std::set<Chunk> UninterestingChunks;
+
+ // When running with more than one thread, serialize the original bitcode
+ // to OriginalBC.
+ SmallString<0> OriginalBC;
+ if (NumJobs > 1) {
+ raw_svector_ostream BCOS(OriginalBC);
+ WriteBitcodeToFile(*Test.getProgram().M, BCOS);
+ }
+
+ std::deque<std::shared_future<SmallString<0>>> TaskQueue;
+ for (auto I = ChunksStillConsideredInteresting.rbegin(),
+ E = ChunksStillConsideredInteresting.rend();
+ I != E; ++I) {
+ std::unique_ptr<ReducerWorkItem> Result = nullptr;
+ unsigned WorkLeft = std::distance(I, E);
+
+ // Run in parallel mode, if the user requested more than one thread and
+ // there are at least a few chunks to process.
+ if (NumJobs > 1 && WorkLeft > 1) {
+ unsigned NumInitialTasks = std::min(WorkLeft, unsigned(NumJobs));
+ unsigned NumChunksProcessed = 0;
+
+ ThreadPool &ChunkThreadPool = *ChunkThreadPoolPtr;
+ TaskQueue.clear();
+
+ AnyReduced = false;
+ // Queue jobs to process NumInitialTasks chunks in parallel using
+ // ChunkThreadPool. When the tasks are added to the pool, parse the
+ // original module from OriginalBC with a fresh LLVMContext object. This
+ // ensures that the cloned module of each task uses an independent
+ // LLVMContext object. If a task reduces the input, serialize the result
+ // back in the corresponding Result element.
+ for (unsigned J = 0; J < NumInitialTasks; ++J) {
+ TaskQueue.emplace_back(ChunkThreadPool.async(
+ [J, I, &Test, &ExtractChunksFromModule, &UninterestingChunks,
+ &ChunksStillConsideredInteresting, &OriginalBC, &AnyReduced]() {
+ return ProcessChunkFromSerializedBitcode(
+ *(I + J), Test, ExtractChunksFromModule,
+ UninterestingChunks, ChunksStillConsideredInteresting,
+ OriginalBC, AnyReduced);
+ }));
+ }
+
+ // Start processing results of the queued tasks. We wait for the first
+ // task in the queue to finish. If it reduced a chunk, we parse the
+ // result and exit the loop.
+ // Otherwise we will try to schedule a new task, if
+ // * no other pending job reduced a chunk and
+ // * we have not reached the end of the chunk.
+ while (!TaskQueue.empty()) {
+ auto &Future = TaskQueue.front();
+ Future.wait();
+
+ NumChunksProcessed++;
+ SmallString<0> Res = Future.get();
+ TaskQueue.pop_front();
+ if (Res.empty()) {
+ unsigned NumScheduledTasks = NumChunksProcessed + TaskQueue.size();
+ if (!AnyReduced && I + NumScheduledTasks != E) {
+ Chunk &ChunkToCheck = *(I + NumScheduledTasks);
+ TaskQueue.emplace_back(ChunkThreadPool.async(
+ [&Test, &ExtractChunksFromModule, &UninterestingChunks,
+ &ChunksStillConsideredInteresting, &OriginalBC,
+ &ChunkToCheck, &AnyReduced]() {
+ return ProcessChunkFromSerializedBitcode(
+ ChunkToCheck, Test, ExtractChunksFromModule,
+ UninterestingChunks, ChunksStillConsideredInteresting,
+ OriginalBC, AnyReduced);
+ }));
+ }
+ continue;
+ }
+
+ Expected<std::unique_ptr<Module>> MOrErr = parseBitcodeFile(
+ MemoryBufferRef(StringRef(Res.data(), Res.size()),
+ "<llvm-reduce tmp module>"),
+ Test.getProgram().M->getContext());
+ if (!MOrErr)
+ report_fatal_error("Failed to read bitcode");
+ Result = std::make_unique<ReducerWorkItem>();
+ Result->M = std::move(MOrErr.get());
+ break;
+ }
+ // Forward I to the last chunk processed in parallel.
+ I += NumChunksProcessed - 1;
+ } else {
+ Result = CheckChunk(*I, cloneReducerWorkItem(Test.getProgram()), Test,
+ ExtractChunksFromModule, UninterestingChunks,
+ ChunksStillConsideredInteresting);
+ }
+
+ if (!Result)
+ continue;
+
+ Chunk &ChunkToCheckForUninterestingness = *I;
+ FoundAtLeastOneNewUninterestingChunkWithCurrentGranularity = true;
+ UninterestingChunks.insert(ChunkToCheckForUninterestingness);
+ ReducedProgram = std::move(Result);
+ errs() << " **** SUCCESS | lines: " << getLines(CurrentFilepath) << "\n";
+ writeOutput(*ReducedProgram, "Saved new best reduction to ");
+ }
+ // Delete uninteresting chunks
+ erase_if(ChunksStillConsideredInteresting,
+ [&UninterestingChunks](const Chunk &C) {
+ return UninterestingChunks.count(C);
+ });
+ } while (!ChunksStillConsideredInteresting.empty() &&
+ (FoundAtLeastOneNewUninterestingChunkWithCurrentGranularity ||
+ increaseGranularity(ChunksStillConsideredInteresting)));
+
+ // If we reduced the testcase replace it
+ if (ReducedProgram)
+ Test.setProgram(std::move(ReducedProgram));
+ errs() << "Couldn't increase anymore.\n";
+}
+
+void llvm::runDeltaPass(
+ TestRunner &Test,
+ function_ref<void(Oracle &, Module &)> ExtractChunksFromModule) {
+ runDeltaPassInt<Module>(Test, ExtractChunksFromModule);
+}
+
+void llvm::runDeltaPass(
+ TestRunner &Test,
+ function_ref<void(Oracle &, MachineFunction &)> ExtractChunksFromModule) {
+ runDeltaPassInt<MachineFunction>(Test, ExtractChunksFromModule);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/Delta.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/Delta.h
new file mode 100644
index 00000000000..a98da41957d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/Delta.h
@@ -0,0 +1,114 @@
+//===- Delta.h - Delta Debugging Algorithm Implementation -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation for the Delta Debugging Algorithm:
+// it splits a given set of Targets (i.e. Functions, Instructions, BBs, etc.)
+// into chunks and tries to reduce the number chunks that are interesting.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_DELTA_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_DELTA_H
+
+#include "TestRunner.h"
+#include "llvm/ADT/ScopeExit.h"
+#include <functional>
+#include <utility>
+#include <vector>
+
+namespace llvm {
+
+struct Chunk {
+ int Begin;
+ int End;
+
+ /// Helper function to verify if a given Target-index is inside the Chunk
+ bool contains(int Index) const { return Index >= Begin && Index <= End; }
+
+ void print() const {
+ errs() << "[" << Begin;
+ if (End - Begin != 0)
+ errs() << "," << End;
+ errs() << "]";
+ }
+
+ /// Operator when populating CurrentChunks in Generic Delta Pass
+ friend bool operator!=(const Chunk &C1, const Chunk &C2) {
+ return C1.Begin != C2.Begin || C1.End != C2.End;
+ }
+
+ /// Operator used for sets
+ friend bool operator<(const Chunk &C1, const Chunk &C2) {
+ return std::tie(C1.Begin, C1.End) < std::tie(C2.Begin, C2.End);
+ }
+};
+
+/// Provides opaque interface for querying into ChunksToKeep without having to
+/// actually understand what is going on.
+class Oracle {
+ /// Out of all the features that we promised to be,
+ /// how many have we already processed?
+ int Index = 0;
+
+ /// The actual workhorse, contains the knowledge whether or not
+ /// some particular feature should be preserved this time.
+ ArrayRef<Chunk> ChunksToKeep;
+
+public:
+ explicit Oracle(ArrayRef<Chunk> ChunksToKeep) : ChunksToKeep(ChunksToKeep) {}
+
+ /// Should be called for each feature on which we are operating.
+ /// Name is self-explanatory - if returns true, then it should be preserved.
+ bool shouldKeep() {
+ if (ChunksToKeep.empty()) {
+ ++Index;
+ return false; // All further features are to be discarded.
+ }
+
+ // Does the current (front) chunk contain such a feature?
+ bool ShouldKeep = ChunksToKeep.front().contains(Index);
+
+ // Is this the last feature in the chunk?
+ if (ChunksToKeep.front().End == Index)
+ ChunksToKeep = ChunksToKeep.drop_front(); // Onto next chunk.
+
+ ++Index;
+
+ return ShouldKeep;
+ }
+
+ int count() { return Index; }
+};
+
+/// This function implements the Delta Debugging algorithm, it receives a
+/// number of Targets (e.g. Functions, Instructions, Basic Blocks, etc.) and
+/// splits them in half; these chunks of targets are then tested while ignoring
+/// one chunk, if a chunk is proven to be uninteresting (i.e. fails the test)
+/// it is removed from consideration. The algorithm will attempt to split the
+/// Chunks in half and start the process again until it can't split chunks
+/// anymore.
+///
+/// This function is intended to be called by each specialized delta pass (e.g.
+/// RemoveFunctions) and receives three key parameters:
+/// * Test: The main TestRunner instance which is used to run the provided
+/// interesting-ness test, as well as to store and access the reduced Program.
+/// * ExtractChunksFromModule: A function used to tailor the main program so it
+/// only contains Targets that are inside Chunks of the given iteration.
+/// Note: This function is implemented by each specialized Delta pass
+///
+/// Other implementations of the Delta Debugging algorithm can also be found in
+/// the CReduce, Delta, and Lithium projects.
+void runDeltaPass(
+ TestRunner &Test,
+ function_ref<void(Oracle &, Module &)> ExtractChunksFromModule);
+void runDeltaPass(
+ TestRunner &Test,
+ function_ref<void(Oracle &, MachineFunction &)> ExtractChunksFromModule);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAliases.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAliases.cpp
new file mode 100644
index 00000000000..cdcd4261cb5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAliases.cpp
@@ -0,0 +1,36 @@
+//===- ReduceAliases.cpp - Specialized Delta Pass -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce aliases in the provided Module.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceAliases.h"
+#include "Delta.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/GlobalValue.h"
+
+using namespace llvm;
+
+/// Removes all aliases aren't inside any of the
+/// desired Chunks.
+static void extractAliasesFromModule(Oracle &O, Module &Program) {
+ for (auto &GA : make_early_inc_range(Program.aliases())) {
+ if (!O.shouldKeep()) {
+ GA.replaceAllUsesWith(GA.getAliasee());
+ GA.eraseFromParent();
+ }
+ }
+}
+
+void llvm::reduceAliasesDeltaPass(TestRunner &Test) {
+ errs() << "*** Reducing Aliases ...\n";
+ runDeltaPass(Test, extractAliasesFromModule);
+ errs() << "----------------------------\n";
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAliases.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAliases.h
new file mode 100644
index 00000000000..0660efe5e9d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAliases.h
@@ -0,0 +1,23 @@
+//===- ReduceAliases.h - Specialized Delta Pass ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce aliases in the provided Module.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEALIASES_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEALIASES_H
+
+#include "Delta.h"
+
+namespace llvm {
+void reduceAliasesDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceArguments.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceArguments.cpp
new file mode 100644
index 00000000000..04ac47ebc4a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceArguments.cpp
@@ -0,0 +1,121 @@
+//===- ReduceArguments.cpp - Specialized Delta Pass -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting Arguments from declared and defined functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceArguments.h"
+#include "Delta.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/Intrinsics.h"
+#include <set>
+#include <vector>
+
+using namespace llvm;
+
+/// Goes over OldF calls and replaces them with a call to NewF
+static void replaceFunctionCalls(Function &OldF, Function &NewF,
+ const std::set<int> &ArgIndexesToKeep) {
+ const auto &Users = OldF.users();
+ for (auto I = Users.begin(), E = Users.end(); I != E; )
+ if (auto *CI = dyn_cast<CallInst>(*I++)) {
+ // Skip uses in call instructions where OldF isn't the called function
+ // (e.g. if OldF is an argument of the call).
+ if (CI->getCalledFunction() != &OldF)
+ continue;
+ SmallVector<Value *, 8> Args;
+ for (auto ArgI = CI->arg_begin(), E = CI->arg_end(); ArgI != E; ++ArgI)
+ if (ArgIndexesToKeep.count(ArgI - CI->arg_begin()))
+ Args.push_back(*ArgI);
+
+ CallInst *NewCI = CallInst::Create(&NewF, Args);
+ NewCI->setCallingConv(NewF.getCallingConv());
+ if (!CI->use_empty())
+ CI->replaceAllUsesWith(NewCI);
+ ReplaceInstWithInst(CI, NewCI);
+ }
+}
+
+/// Returns whether or not this function should be considered a candidate for
+/// argument removal. Currently, functions with no arguments and intrinsics are
+/// not considered. Intrinsics aren't considered because their signatures are
+/// fixed.
+static bool shouldRemoveArguments(const Function &F) {
+ return !F.arg_empty() && !F.isIntrinsic();
+}
+
+/// Removes out-of-chunk arguments from functions, and modifies their calls
+/// accordingly. It also removes allocations of out-of-chunk arguments.
+static void extractArgumentsFromModule(Oracle &O, Module &Program) {
+ std::vector<Argument *> InitArgsToKeep;
+ std::vector<Function *> Funcs;
+ // Get inside-chunk arguments, as well as their parent function
+ for (auto &F : Program)
+ if (shouldRemoveArguments(F)) {
+ Funcs.push_back(&F);
+ for (auto &A : F.args())
+ if (O.shouldKeep())
+ InitArgsToKeep.push_back(&A);
+ }
+
+ // We create a vector first, then convert it to a set, so that we don't have
+ // to pay the cost of rebalancing the set frequently if the order we insert
+ // the elements doesn't match the order they should appear inside the set.
+ std::set<Argument *> ArgsToKeep(InitArgsToKeep.begin(), InitArgsToKeep.end());
+
+ for (auto *F : Funcs) {
+ ValueToValueMapTy VMap;
+ std::vector<WeakVH> InstToDelete;
+ for (auto &A : F->args())
+ if (!ArgsToKeep.count(&A)) {
+ // By adding undesired arguments to the VMap, CloneFunction will remove
+ // them from the resulting Function
+ VMap[&A] = UndefValue::get(A.getType());
+ for (auto *U : A.users())
+ if (auto *I = dyn_cast<Instruction>(*&U))
+ InstToDelete.push_back(I);
+ }
+ // Delete any (unique) instruction that uses the argument
+ for (Value *V : InstToDelete) {
+ if (!V)
+ continue;
+ auto *I = cast<Instruction>(V);
+ I->replaceAllUsesWith(UndefValue::get(I->getType()));
+ if (!I->isTerminator())
+ I->eraseFromParent();
+ }
+
+ // No arguments to reduce
+ if (VMap.empty())
+ continue;
+
+ std::set<int> ArgIndexesToKeep;
+ for (auto &Arg : enumerate(F->args()))
+ if (ArgsToKeep.count(&Arg.value()))
+ ArgIndexesToKeep.insert(Arg.index());
+
+ auto *ClonedFunc = CloneFunction(F, VMap);
+ // In order to preserve function order, we move Clone after old Function
+ ClonedFunc->removeFromParent();
+ Program.getFunctionList().insertAfter(F->getIterator(), ClonedFunc);
+
+ replaceFunctionCalls(*F, *ClonedFunc, ArgIndexesToKeep);
+ // Rename Cloned Function to Old's name
+ std::string FName = std::string(F->getName());
+ F->replaceAllUsesWith(ConstantExpr::getBitCast(ClonedFunc, F->getType()));
+ F->eraseFromParent();
+ ClonedFunc->setName(FName);
+ }
+}
+
+void llvm::reduceArgumentsDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing Arguments...\n";
+ runDeltaPass(Test, extractArgumentsFromModule);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceArguments.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceArguments.h
new file mode 100644
index 00000000000..228409d3ca3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceArguments.h
@@ -0,0 +1,26 @@
+//===- ReduceArguments.h - Specialized Delta Pass -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting Arguments from defined functions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEARGUMENTS_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEARGUMENTS_H
+
+#include "Delta.h"
+#include "llvm/IR/Argument.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+namespace llvm {
+void reduceArgumentsDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAttributes.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAttributes.cpp
new file mode 100644
index 00000000000..3a2397464be
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAttributes.cpp
@@ -0,0 +1,186 @@
+//===- ReduceAttributes.cpp - Specialized Delta Pass ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting attributes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceAttributes.h"
+#include "Delta.h"
+#include "TestRunner.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/InstVisitor.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <utility>
+#include <vector>
+
+namespace llvm {
+class LLVMContext;
+} // namespace llvm
+
+using namespace llvm;
+
+namespace {
+
+using AttrPtrVecTy = std::vector<const Attribute *>;
+using AttrPtrIdxVecVecTy = std::pair<unsigned, AttrPtrVecTy>;
+using AttrPtrVecVecTy = SmallVector<AttrPtrIdxVecVecTy, 3>;
+
+/// Given ChunksToKeep, produce a map of global variables/functions/calls
+/// and indexes of attributes to be preserved for each of them.
+class AttributeRemapper : public InstVisitor<AttributeRemapper> {
+ Oracle &O;
+
+public:
+ DenseMap<GlobalVariable *, AttrPtrVecTy> GlobalVariablesToRefine;
+ DenseMap<Function *, AttrPtrVecVecTy> FunctionsToRefine;
+ DenseMap<CallBase *, AttrPtrVecVecTy> CallsToRefine;
+
+ explicit AttributeRemapper(Oracle &O) : O(O) {}
+
+ void visitModule(Module &M) {
+ for (GlobalVariable &GV : M.getGlobalList())
+ visitGlobalVariable(GV);
+ }
+
+ void visitGlobalVariable(GlobalVariable &GV) {
+ // Global variables only have one attribute set.
+ const AttributeSet &AS = GV.getAttributes();
+ if (AS.hasAttributes())
+ visitAttributeSet(AS, GlobalVariablesToRefine[&GV]);
+ }
+
+ void visitFunction(Function &F) {
+ if (F.getIntrinsicID() != Intrinsic::not_intrinsic)
+ return; // We can neither add nor remove attributes from intrinsics.
+ visitAttributeList(F.getAttributes(), FunctionsToRefine[&F]);
+ }
+
+ void visitCallBase(CallBase &I) {
+ visitAttributeList(I.getAttributes(), CallsToRefine[&I]);
+ }
+
+ void visitAttributeList(const AttributeList &AL,
+ AttrPtrVecVecTy &AttributeSetsToPreserve) {
+ assert(AttributeSetsToPreserve.empty() && "Should not be sharing vectors.");
+ AttributeSetsToPreserve.reserve(AL.getNumAttrSets());
+ for (unsigned SetIdx : AL.indexes()) {
+ AttrPtrIdxVecVecTy AttributesToPreserve;
+ AttributesToPreserve.first = SetIdx;
+ visitAttributeSet(AL.getAttributes(AttributesToPreserve.first),
+ AttributesToPreserve.second);
+ if (!AttributesToPreserve.second.empty())
+ AttributeSetsToPreserve.emplace_back(std::move(AttributesToPreserve));
+ }
+ }
+
+ void visitAttributeSet(const AttributeSet &AS,
+ AttrPtrVecTy &AttrsToPreserve) {
+ assert(AttrsToPreserve.empty() && "Should not be sharing vectors.");
+ AttrsToPreserve.reserve(AS.getNumAttributes());
+ for (const Attribute &A : AS)
+ if (O.shouldKeep())
+ AttrsToPreserve.emplace_back(&A);
+ }
+};
+
+struct AttributeCounter : public InstVisitor<AttributeCounter> {
+ /// How many features (in this case, attributes) did we count, total?
+ int AttributeCount = 0;
+
+ void visitModule(Module &M) {
+ for (GlobalVariable &GV : M.getGlobalList())
+ visitGlobalVariable(GV);
+ }
+
+ void visitGlobalVariable(GlobalVariable &GV) {
+ // Global variables only have one attribute set.
+ visitAttributeSet(GV.getAttributes());
+ }
+
+ void visitFunction(Function &F) {
+ if (F.getIntrinsicID() != Intrinsic::not_intrinsic)
+ return; // We can neither add nor remove attributes from intrinsics.
+ visitAttributeList(F.getAttributes());
+ }
+
+ void visitCallBase(CallBase &I) { visitAttributeList(I.getAttributes()); }
+
+ void visitAttributeList(const AttributeList &AL) {
+ for (const AttributeSet &AS : AL)
+ visitAttributeSet(AS);
+ }
+
+ void visitAttributeSet(const AttributeSet &AS) {
+ AttributeCount += AS.getNumAttributes();
+ }
+};
+
+} // namespace
+
+AttributeSet
+convertAttributeRefToAttributeSet(LLVMContext &C,
+ ArrayRef<const Attribute *> Attributes) {
+ AttrBuilder B(C);
+ for (const Attribute *A : Attributes)
+ B.addAttribute(*A);
+ return AttributeSet::get(C, B);
+}
+
+AttributeList convertAttributeRefVecToAttributeList(
+ LLVMContext &C, ArrayRef<AttrPtrIdxVecVecTy> AttributeSets) {
+ std::vector<std::pair<unsigned, AttributeSet>> SetVec;
+ SetVec.reserve(AttributeSets.size());
+
+ transform(AttributeSets, std::back_inserter(SetVec),
+ [&C](const AttrPtrIdxVecVecTy &V) {
+ return std::make_pair(
+ V.first, convertAttributeRefToAttributeSet(C, V.second));
+ });
+
+ sort(SetVec, [](const std::pair<unsigned, AttributeSet> &LHS,
+ const std::pair<unsigned, AttributeSet> &RHS) {
+ return LHS.first < RHS.first; // All values are unique.
+ });
+
+ return AttributeList::get(C, SetVec);
+}
+
+/// Removes out-of-chunk attributes from module.
+static void extractAttributesFromModule(Oracle &O, Module &Program) {
+ AttributeRemapper R(O);
+ R.visit(Program);
+
+ LLVMContext &C = Program.getContext();
+ for (const auto &I : R.GlobalVariablesToRefine)
+ I.first->setAttributes(convertAttributeRefToAttributeSet(C, I.second));
+ for (const auto &I : R.FunctionsToRefine)
+ I.first->setAttributes(convertAttributeRefVecToAttributeList(C, I.second));
+ for (const auto &I : R.CallsToRefine)
+ I.first->setAttributes(convertAttributeRefVecToAttributeList(C, I.second));
+}
+
+void llvm::reduceAttributesDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing Attributes...\n";
+ runDeltaPass(Test, extractAttributesFromModule);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAttributes.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAttributes.h
new file mode 100644
index 00000000000..375e764df03
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceAttributes.h
@@ -0,0 +1,22 @@
+//===- ReduceAttributes.h - Specialized Delta Pass ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting attributes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEATTRIBUTES_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEATTRIBUTES_H
+
+namespace llvm {
+class TestRunner;
+void reduceAttributesDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp
new file mode 100644
index 00000000000..c76322b2553
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp
@@ -0,0 +1,138 @@
+//===- ReduceArguments.cpp - Specialized Delta Pass -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting Arguments from defined functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceBasicBlocks.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/raw_ostream.h"
+#include <vector>
+
+using namespace llvm;
+
+/// Replaces BB Terminator with one that only contains Chunk BBs
+static void replaceBranchTerminator(BasicBlock &BB,
+ const DenseSet<BasicBlock *> &BBsToKeep) {
+ auto *Term = BB.getTerminator();
+ std::vector<BasicBlock *> ChunkSucessors;
+ for (auto *Succ : successors(&BB))
+ if (BBsToKeep.count(Succ))
+ ChunkSucessors.push_back(Succ);
+
+ // BB only references Chunk BBs
+ if (ChunkSucessors.size() == Term->getNumSuccessors())
+ return;
+
+ bool IsBranch = isa<BranchInst>(Term) || isa<InvokeInst>(Term);
+ Value *Address = nullptr;
+ if (auto *IndBI = dyn_cast<IndirectBrInst>(Term))
+ Address = IndBI->getAddress();
+
+ Term->replaceAllUsesWith(UndefValue::get(Term->getType()));
+ Term->eraseFromParent();
+
+ if (ChunkSucessors.empty()) {
+ auto *FnRetTy = BB.getParent()->getReturnType();
+ ReturnInst::Create(BB.getContext(),
+ FnRetTy->isVoidTy() ? nullptr : UndefValue::get(FnRetTy),
+ &BB);
+ return;
+ }
+
+ if (IsBranch)
+ BranchInst::Create(ChunkSucessors[0], &BB);
+
+ if (Address) {
+ auto *NewIndBI =
+ IndirectBrInst::Create(Address, ChunkSucessors.size(), &BB);
+ for (auto *Dest : ChunkSucessors)
+ NewIndBI->addDestination(Dest);
+ }
+}
+
+/// Removes uninteresting BBs from switch, if the default case ends up being
+/// uninteresting, the switch is replaced with a void return (since it has to be
+/// replace with something)
+static void
+removeUninterestingBBsFromSwitch(SwitchInst &SwInst,
+ const DenseSet<BasicBlock *> &BBsToKeep) {
+ if (!BBsToKeep.count(SwInst.getDefaultDest())) {
+ auto *FnRetTy = SwInst.getParent()->getParent()->getReturnType();
+ ReturnInst::Create(SwInst.getContext(),
+ FnRetTy->isVoidTy() ? nullptr : UndefValue::get(FnRetTy),
+ SwInst.getParent());
+ SwInst.eraseFromParent();
+ } else
+ for (int I = 0, E = SwInst.getNumCases(); I != E; ++I) {
+ auto Case = SwInst.case_begin() + I;
+ if (!BBsToKeep.count(Case->getCaseSuccessor())) {
+ SwInst.removeCase(Case);
+ --I;
+ --E;
+ }
+ }
+}
+
+/// Removes out-of-chunk arguments from functions, and modifies their calls
+/// accordingly. It also removes allocations of out-of-chunk arguments.
+static void extractBasicBlocksFromModule(Oracle &O, Module &Program) {
+ DenseSet<BasicBlock *> BBsToKeep;
+
+ SmallVector<BasicBlock *> BBsToDelete;
+ for (auto &F : Program) {
+ for (auto &BB : F) {
+ if (O.shouldKeep())
+ BBsToKeep.insert(&BB);
+ else {
+ BBsToDelete.push_back(&BB);
+ // Remove out-of-chunk BB from successor phi nodes
+ for (auto *Succ : successors(&BB))
+ Succ->removePredecessor(&BB);
+ }
+ }
+ }
+
+ // Replace terminators that reference out-of-chunk BBs
+ for (auto &F : Program)
+ for (auto &BB : F) {
+ if (auto *SwInst = dyn_cast<SwitchInst>(BB.getTerminator()))
+ removeUninterestingBBsFromSwitch(*SwInst, BBsToKeep);
+ else
+ replaceBranchTerminator(BB, BBsToKeep);
+ }
+
+ // Replace out-of-chunk switch uses
+ for (auto &BB : BBsToDelete) {
+ // Instructions might be referenced in other BBs
+ for (auto &I : *BB)
+ I.replaceAllUsesWith(UndefValue::get(I.getType()));
+ if (BB->getParent()->size() == 1) {
+ // this is the last basic block of the function, thus we must also make
+ // sure to remove comdat and set linkage to external
+ auto F = BB->getParent();
+ F->deleteBody();
+ F->setComdat(nullptr);
+ } else {
+ BB->eraseFromParent();
+ }
+ }
+}
+
+void llvm::reduceBasicBlocksDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing Basic Blocks...\n";
+ runDeltaPass(Test, extractBasicBlocksFromModule);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceBasicBlocks.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceBasicBlocks.h
new file mode 100644
index 00000000000..49385527cb8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceBasicBlocks.h
@@ -0,0 +1,24 @@
+//===- ReduceArguments.h - Specialized Delta Pass -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting Arguments from defined functions.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEBASICBLOCKS_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEBASICBLOCKS_H
+
+#include "Delta.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+namespace llvm {
+void reduceBasicBlocksDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctionBodies.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctionBodies.cpp
new file mode 100644
index 00000000000..63dd2f03b18
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctionBodies.cpp
@@ -0,0 +1,36 @@
+//===- ReduceFunctions.cpp - Specialized Delta Pass -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce function bodies in the provided Module.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceFunctionBodies.h"
+#include "Delta.h"
+#include "llvm/IR/GlobalValue.h"
+
+using namespace llvm;
+
+/// Removes all the bodies of defined functions that aren't inside any of the
+/// desired Chunks.
+static void extractFunctionBodiesFromModule(Oracle &O, Module &Program) {
+ // Delete out-of-chunk function bodies
+ std::vector<Function *> FuncDefsToReduce;
+ for (auto &F : Program)
+ if (!F.isDeclaration() && !O.shouldKeep()) {
+ F.deleteBody();
+ F.setComdat(nullptr);
+ }
+}
+
+void llvm::reduceFunctionBodiesDeltaPass(TestRunner &Test) {
+ errs() << "*** Reducing Function Bodies...\n";
+ runDeltaPass(Test, extractFunctionBodiesFromModule);
+ errs() << "----------------------------\n";
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctionBodies.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctionBodies.h
new file mode 100644
index 00000000000..bfe701b588b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctionBodies.h
@@ -0,0 +1,23 @@
+//===- ReduceFunctionBodies.h - Specialized Delta Pass --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce function bodies in the provided Module.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEFUNCTIONBODIES_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEFUNCTIONBODIES_H
+
+#include "Delta.h"
+
+namespace llvm {
+void reduceFunctionBodiesDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctions.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctions.cpp
new file mode 100644
index 00000000000..2a26d4340af
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctions.cpp
@@ -0,0 +1,55 @@
+//===- ReduceFunctions.cpp - Specialized Delta Pass -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce functions (and any instruction that calls it) in the provided
+// Module.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceFunctions.h"
+#include "Delta.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/IR/Instructions.h"
+#include <iterator>
+#include <vector>
+
+using namespace llvm;
+
+/// Removes all the Defined Functions
+/// that aren't inside any of the desired Chunks.
+static void extractFunctionsFromModule(Oracle &O, Module &Program) {
+ // Record all out-of-chunk functions.
+ std::vector<std::reference_wrapper<Function>> FuncsToRemove;
+ copy_if(Program.functions(), std::back_inserter(FuncsToRemove),
+ [&O](Function &F) {
+ // Intrinsics don't have function bodies that are useful to
+ // reduce. Additionally, intrinsics may have additional operand
+ // constraints. But, do drop intrinsics that are not referenced.
+ return (!F.isIntrinsic() || F.use_empty()) && !O.shouldKeep();
+ });
+
+ // Then, drop body of each of them. We want to batch this and do nothing else
+ // here so that minimal number of remaining exteranal uses will remain.
+ for (Function &F : FuncsToRemove)
+ F.dropAllReferences();
+
+ // And finally, we can actually delete them.
+ for (Function &F : FuncsToRemove) {
+ // Replace all *still* remaining uses with undef.
+ F.replaceAllUsesWith(UndefValue::get(F.getType()));
+ // And finally, fully drop it.
+ F.eraseFromParent();
+ }
+}
+
+void llvm::reduceFunctionsDeltaPass(TestRunner &Test) {
+ errs() << "*** Reducing Functions...\n";
+ runDeltaPass(Test, extractFunctionsFromModule);
+ errs() << "----------------------------\n";
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctions.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctions.h
new file mode 100644
index 00000000000..f5bc83b9a1e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceFunctions.h
@@ -0,0 +1,24 @@
+//===- ReduceFunctions.h - Specialized Delta Pass -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce functions (and any instruction that calls it) in the provided
+// Module.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEFUNCTIONS_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEFUNCTIONS_H
+
+#include "Delta.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+namespace llvm {
+void reduceFunctionsDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalObjects.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalObjects.cpp
new file mode 100644
index 00000000000..8bbaf448950
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalObjects.cpp
@@ -0,0 +1,32 @@
+//===- ReduceGlobalObjects.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceGlobalObjects.h"
+#include "llvm/IR/GlobalObject.h"
+
+using namespace llvm;
+
+static bool shouldReduceSection(GlobalObject &GO) { return GO.hasSection(); }
+
+static bool shouldReduceAlign(GlobalObject &GO) {
+ return GO.getAlign().hasValue();
+}
+
+static void reduceGOs(Oracle &O, Module &Program) {
+ for (auto &GO : Program.global_objects()) {
+ if (shouldReduceSection(GO) && !O.shouldKeep())
+ GO.setSection("");
+ if (shouldReduceAlign(GO) && !O.shouldKeep())
+ GO.setAlignment(MaybeAlign());
+ }
+}
+
+void llvm::reduceGlobalObjectsDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing GlobalObjects...\n";
+ runDeltaPass(Test, reduceGOs);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalObjects.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalObjects.h
new file mode 100644
index 00000000000..7224b9bfbd5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalObjects.h
@@ -0,0 +1,18 @@
+//===- ReduceGlobalObjects.h ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALOBJECTS_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALOBJECTS_H
+
+#include "Delta.h"
+
+namespace llvm {
+void reduceGlobalObjectsDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalValues.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalValues.cpp
new file mode 100644
index 00000000000..26a3cbded08
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalValues.cpp
@@ -0,0 +1,63 @@
+//===- ReduceGlobalValues.cpp - Specialized Delta Pass --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass to reduce
+// global value attributes/specifiers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceGlobalValues.h"
+#include "llvm/IR/GlobalValue.h"
+
+using namespace llvm;
+
+static bool shouldReduceDSOLocal(GlobalValue &GV) {
+ return GV.isDSOLocal() && !GV.isImplicitDSOLocal();
+}
+
+static bool shouldReduceVisibility(GlobalValue &GV) {
+ return GV.getVisibility() != GlobalValue::VisibilityTypes::DefaultVisibility;
+}
+
+static bool shouldReduceUnnamedAddress(GlobalValue &GV) {
+ return GV.getUnnamedAddr() != GlobalValue::UnnamedAddr::None;
+}
+
+static bool shouldReduceDLLStorageClass(GlobalValue &GV) {
+ return GV.getDLLStorageClass() !=
+ GlobalValue::DLLStorageClassTypes::DefaultStorageClass;
+}
+
+static bool shouldReduceThreadLocal(GlobalValue &GV) {
+ return GV.isThreadLocal();
+}
+
+static void reduceGVs(Oracle &O, Module &Program) {
+ for (auto &GV : Program.global_values()) {
+ if (shouldReduceDSOLocal(GV) && !O.shouldKeep())
+ GV.setDSOLocal(false);
+ if (shouldReduceVisibility(GV) && !O.shouldKeep()) {
+ bool IsImplicitDSOLocal = GV.isImplicitDSOLocal();
+ GV.setVisibility(GlobalValue::VisibilityTypes::DefaultVisibility);
+ if (IsImplicitDSOLocal)
+ GV.setDSOLocal(false);
+ }
+ if (shouldReduceUnnamedAddress(GV) && !O.shouldKeep())
+ GV.setUnnamedAddr(GlobalValue::UnnamedAddr::None);
+ if (shouldReduceDLLStorageClass(GV) && !O.shouldKeep())
+ GV.setDLLStorageClass(
+ GlobalValue::DLLStorageClassTypes::DefaultStorageClass);
+ if (shouldReduceThreadLocal(GV) && !O.shouldKeep())
+ GV.setThreadLocal(false);
+ }
+}
+
+void llvm::reduceGlobalValuesDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing GlobalValues...\n";
+ runDeltaPass(Test, reduceGVs);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalValues.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalValues.h
new file mode 100644
index 00000000000..ea32a6c20bc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalValues.h
@@ -0,0 +1,23 @@
+//===- ReduceGlobalValues.h - Specialized Delta Pass ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass to reduce
+// global value attributes/specifiers.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVALUES_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVALUES_H
+
+#include "Delta.h"
+
+namespace llvm {
+void reduceGlobalValuesDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.cpp
new file mode 100644
index 00000000000..c9e4d9f6768
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.cpp
@@ -0,0 +1,34 @@
+//===- ReduceGlobalVars.cpp - Specialized Delta Pass ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce initializers of Global Variables in the provided Module.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceGlobalVarInitializers.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/GlobalValue.h"
+
+using namespace llvm;
+
+/// Removes all the Initialized GVs that aren't inside the desired Chunks.
+static void extractGVsFromModule(Oracle &O, Module &Program) {
+ // Drop initializers of out-of-chunk GVs
+ for (auto &GV : Program.globals())
+ if (GV.hasInitializer() && !O.shouldKeep()) {
+ GV.setInitializer(nullptr);
+ GV.setLinkage(GlobalValue::LinkageTypes::ExternalLinkage);
+ GV.setComdat(nullptr);
+ }
+}
+
+void llvm::reduceGlobalsInitializersDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing GVs initializers...\n";
+ runDeltaPass(Test, extractGVsFromModule);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.h
new file mode 100644
index 00000000000..0f5f22ad43a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVarInitializers.h
@@ -0,0 +1,25 @@
+//===- reduceGlobalsInitializersDeltaPass.h - Specialized Delta Pass ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce initializers of Global Variables in the provided Module.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVARINITIALIZERS_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVARINITIALIZERS_H
+
+#include "Delta.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+namespace llvm {
+void reduceGlobalsInitializersDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp
new file mode 100644
index 00000000000..1651a37eaa1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp
@@ -0,0 +1,63 @@
+//===- ReduceGlobalVars.cpp - Specialized Delta Pass ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce Global Variables in the provided Module.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceGlobalVars.h"
+#include "llvm/IR/Constants.h"
+#include <set>
+
+using namespace llvm;
+
+/// Removes all the GVs that aren't inside the desired Chunks.
+static void extractGVsFromModule(Oracle &O, Module &Program) {
+ // Get GVs inside desired chunks
+ std::vector<GlobalVariable *> InitGVsToKeep;
+ for (auto &GV : Program.globals())
+ if (O.shouldKeep())
+ InitGVsToKeep.push_back(&GV);
+
+ // We create a vector first, then convert it to a set, so that we don't have
+ // to pay the cost of rebalancing the set frequently if the order we insert
+ // the elements doesn't match the order they should appear inside the set.
+ std::set<GlobalVariable *> GVsToKeep(InitGVsToKeep.begin(),
+ InitGVsToKeep.end());
+
+ // Delete out-of-chunk GVs and their uses
+ std::vector<GlobalVariable *> ToRemove;
+ std::vector<WeakVH> InstToRemove;
+ for (auto &GV : Program.globals())
+ if (!GVsToKeep.count(&GV)) {
+ for (auto *U : GV.users())
+ if (auto *Inst = dyn_cast<Instruction>(U))
+ InstToRemove.push_back(Inst);
+
+ GV.replaceAllUsesWith(UndefValue::get(GV.getType()));
+ ToRemove.push_back(&GV);
+ }
+
+ // Delete (unique) Instruction uses of unwanted GVs
+ for (Value *V : InstToRemove) {
+ if (!V)
+ continue;
+ auto *Inst = cast<Instruction>(V);
+ Inst->replaceAllUsesWith(UndefValue::get(Inst->getType()));
+ Inst->eraseFromParent();
+ }
+
+ for (auto *GV : ToRemove)
+ GV->eraseFromParent();
+}
+
+void llvm::reduceGlobalsDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing GVs...\n";
+ runDeltaPass(Test, extractGVsFromModule);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVars.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVars.h
new file mode 100644
index 00000000000..fe7813c5385
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceGlobalVars.h
@@ -0,0 +1,25 @@
+//===- ReduceGlobalVars.h - Specialized Delta Pass ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce Global Variables in the provided Module.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVARS_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEGLOBALVARS_H
+
+#include "Delta.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+namespace llvm {
+void reduceGlobalsDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructions.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructions.cpp
new file mode 100644
index 00000000000..f27959fb98a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructions.cpp
@@ -0,0 +1,55 @@
+//===- ReduceArguments.cpp - Specialized Delta Pass -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting Arguments from defined functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceInstructions.h"
+
+using namespace llvm;
+
+/// Removes out-of-chunk arguments from functions, and modifies their calls
+/// accordingly. It also removes allocations of out-of-chunk arguments.
+static void extractInstrFromModule(Oracle &O, Module &Program) {
+ std::vector<Instruction *> InitInstToKeep;
+
+ for (auto &F : Program)
+ for (auto &BB : F) {
+ // Removing the terminator would make the block invalid. Only iterate over
+ // instructions before the terminator.
+ InitInstToKeep.push_back(BB.getTerminator());
+ for (auto &Inst : make_range(BB.begin(), std::prev(BB.end())))
+ if (O.shouldKeep())
+ InitInstToKeep.push_back(&Inst);
+ }
+
+ // We create a vector first, then convert it to a set, so that we don't have
+ // to pay the cost of rebalancing the set frequently if the order we insert
+ // the elements doesn't match the order they should appear inside the set.
+ std::set<Instruction *> InstToKeep(InitInstToKeep.begin(),
+ InitInstToKeep.end());
+
+ std::vector<Instruction *> InstToDelete;
+ for (auto &F : Program)
+ for (auto &BB : F)
+ for (auto &Inst : BB)
+ if (!InstToKeep.count(&Inst)) {
+ Inst.replaceAllUsesWith(UndefValue::get(Inst.getType()));
+ InstToDelete.push_back(&Inst);
+ }
+
+ for (auto &I : InstToDelete)
+ I->eraseFromParent();
+}
+
+void llvm::reduceInstructionsDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing Instructions...\n";
+ runDeltaPass(Test, extractInstrFromModule);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructions.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructions.h
new file mode 100644
index 00000000000..be568f18496
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructions.h
@@ -0,0 +1,25 @@
+//===- ReduceArguments.h - Specialized Delta Pass -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting Arguments from defined functions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEINSTRUCTIONS_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEINSTRUCTIONS_H
+
+#include "Delta.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+namespace llvm {
+void reduceInstructionsDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructionsMIR.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructionsMIR.cpp
new file mode 100644
index 00000000000..e64d6f3c5ee
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructionsMIR.cpp
@@ -0,0 +1,125 @@
+//===- ReduceInstructionsMIR.cpp - Specialized Delta Pass -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting MachineInstr from the MachineFunction.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceInstructionsMIR.h"
+
+#include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
+
+using namespace llvm;
+
+static Register getPrevDefOfRCInMBB(MachineBasicBlock &MBB,
+ MachineBasicBlock::reverse_iterator &RI,
+ const TargetRegisterClass *RC,
+ SetVector<MachineInstr *> &ExcludeMIs) {
+ auto MRI = &MBB.getParent()->getRegInfo();
+ for (MachineBasicBlock::reverse_instr_iterator E = MBB.instr_rend(); RI != E;
+ ++RI) {
+ auto &MI = *RI;
+ // All Def operands explicit and implicit.
+ for (auto &MO : MI.operands()) {
+ if (!MO.isReg() || !MO.isDef())
+ continue;
+ auto Reg = MO.getReg();
+ if (Register::isPhysicalRegister(Reg))
+ continue;
+
+ if (MRI->getRegClass(Reg) == RC && !ExcludeMIs.count(MO.getParent()))
+ return Reg;
+ }
+ }
+ return 0;
+}
+
+static void extractInstrFromModule(Oracle &O, MachineFunction &MF) {
+ MachineDominatorTree MDT;
+ MDT.runOnMachineFunction(MF);
+
+ auto MRI = &MF.getRegInfo();
+ SetVector<MachineInstr *> ToDelete;
+
+ MachineInstr *TopMI = nullptr;
+
+ // Mark MIs for deletion according to some criteria.
+ for (auto &MBB : MF) {
+ for (auto &MI : MBB) {
+ if (MI.isTerminator())
+ continue;
+ if (MBB.isEntryBlock() && !TopMI) {
+ TopMI = &MI;
+ continue;
+ }
+ if (!O.shouldKeep())
+ ToDelete.insert(&MI);
+ }
+ }
+
+ // For each MI to be deleted update users of regs defined by that MI to use
+ // some other dominating definition (that is not to be deleted).
+ for (auto *MI : ToDelete) {
+ for (auto &MO : MI->operands()) {
+ if (!MO.isReg() || !MO.isDef())
+ continue;
+ auto Reg = MO.getReg();
+ if (Register::isPhysicalRegister(Reg))
+ continue;
+ auto UI = MRI->use_begin(Reg);
+ auto UE = MRI->use_end();
+
+ auto RegRC = MRI->getRegClass(Reg);
+ Register NewReg = 0;
+ // If this is not a physical register and there are some uses.
+ if (UI != UE) {
+ MachineBasicBlock::reverse_iterator RI(*MI);
+ MachineBasicBlock *BB = MI->getParent();
+ ++RI;
+ while (NewReg == 0 && BB) {
+ NewReg = getPrevDefOfRCInMBB(*BB, RI, RegRC, ToDelete);
+ // Prepare for idom(BB).
+ if (auto *IDM = MDT.getNode(BB)->getIDom()) {
+ BB = IDM->getBlock();
+ RI = BB->rbegin();
+ } else {
+ BB = nullptr;
+ }
+ }
+ }
+
+ // If no dominating definition was found then add an implicit one to the
+ // first instruction in the entry block.
+ if (!NewReg && TopMI) {
+ NewReg = MRI->createVirtualRegister(RegRC);
+ TopMI->addOperand(MachineOperand::CreateReg(
+ NewReg, true /*IsDef*/, true /*IsImp*/, false /*IsKill*/));
+ }
+
+ // Update all uses.
+ while (UI != UE) {
+ auto &UMO = *UI++;
+ UMO.setReg(NewReg);
+ }
+ }
+ }
+
+ // Finally delete the MIs.
+ for (auto *MI : ToDelete)
+ MI->eraseFromParent();
+}
+
+void llvm::reduceInstructionsMIRDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing Instructions...\n";
+ runDeltaPass(Test, extractInstrFromModule);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructionsMIR.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructionsMIR.h
new file mode 100644
index 00000000000..564cf63a0a3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceInstructionsMIR.h
@@ -0,0 +1,23 @@
+//===- ReduceInstructionsMIR.h - Specialized Delta Pass ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting MachineInstr from the MachineFunction.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEINSTRUCTIONS_MIR_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEINSTRUCTIONS_MIR_H
+
+#include "Delta.h"
+
+namespace llvm {
+void reduceInstructionsMIRDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceMetadata.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceMetadata.cpp
new file mode 100644
index 00000000000..078230e8095
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceMetadata.cpp
@@ -0,0 +1,72 @@
+//===- ReduceMetadata.cpp - Specialized Delta Pass ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements two functions used by the Generic Delta Debugging
+// Algorithm, which are used to reduce Metadata nodes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceMetadata.h"
+#include "Delta.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/InstIterator.h"
+#include <vector>
+
+using namespace llvm;
+
+/// Removes all the Named and Unnamed Metadata Nodes, as well as any debug
+/// functions that aren't inside the desired Chunks.
+static void extractMetadataFromModule(Oracle &O, Module &Program) {
+ // Get out-of-chunk Named metadata nodes
+ SmallVector<NamedMDNode *> NamedNodesToDelete;
+ for (NamedMDNode &MD : Program.named_metadata())
+ if (!O.shouldKeep())
+ NamedNodesToDelete.push_back(&MD);
+
+ for (NamedMDNode *NN : NamedNodesToDelete) {
+ for (auto I : seq<unsigned>(0, NN->getNumOperands()))
+ NN->setOperand(I, nullptr);
+ NN->eraseFromParent();
+ }
+
+ // Delete out-of-chunk metadata attached to globals.
+ for (GlobalVariable &GV : Program.globals()) {
+ SmallVector<std::pair<unsigned, MDNode *>> MDs;
+ GV.getAllMetadata(MDs);
+ for (std::pair<unsigned, MDNode *> &MD : MDs)
+ if (!O.shouldKeep())
+ GV.setMetadata(MD.first, nullptr);
+ }
+
+ for (Function &F : Program) {
+ {
+ SmallVector<std::pair<unsigned, MDNode *>> MDs;
+ // Delete out-of-chunk metadata attached to functions.
+ F.getAllMetadata(MDs);
+ for (std::pair<unsigned, MDNode *> &MD : MDs)
+ if (!O.shouldKeep())
+ F.setMetadata(MD.first, nullptr);
+ }
+
+ // Delete out-of-chunk metadata attached to instructions.
+ for (Instruction &I : instructions(F)) {
+ SmallVector<std::pair<unsigned, MDNode *>> MDs;
+ I.getAllMetadata(MDs);
+ for (std::pair<unsigned, MDNode *> &MD : MDs)
+ if (!O.shouldKeep())
+ I.setMetadata(MD.first, nullptr);
+ }
+ }
+}
+
+void llvm::reduceMetadataDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing Metadata...\n";
+ runDeltaPass(Test, extractMetadataFromModule);
+ outs() << "----------------------------\n";
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceMetadata.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceMetadata.h
new file mode 100644
index 00000000000..6efc3f5598d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceMetadata.h
@@ -0,0 +1,23 @@
+//===- ReduceMetadata.h - Specialized Delta Pass --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements two functions used by the Generic Delta Debugging
+// Algorithm, which are used to reduce Metadata nodes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEMETADATA_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEMETADATA_H
+
+#include "TestRunner.h"
+
+namespace llvm {
+void reduceMetadataDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceModuleData.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceModuleData.cpp
new file mode 100644
index 00000000000..7a59950ca90
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceModuleData.cpp
@@ -0,0 +1,34 @@
+//===- ReduceModuleData.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a reduce pass to reduce various module data.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceModuleData.h"
+
+using namespace llvm;
+
+static void clearModuleData(Oracle &O, Module &Program) {
+ if (!Program.getModuleIdentifier().empty() && !O.shouldKeep())
+ Program.setModuleIdentifier("");
+ if (!Program.getSourceFileName().empty() && !O.shouldKeep())
+ Program.setSourceFileName("");
+ if (!Program.getDataLayoutStr().empty() && !O.shouldKeep())
+ Program.setDataLayout("");
+ if (!Program.getTargetTriple().empty() && !O.shouldKeep())
+ Program.setTargetTriple("");
+ // TODO: clear line by line rather than all at once
+ if (!Program.getModuleInlineAsm().empty() && !O.shouldKeep())
+ Program.setModuleInlineAsm("");
+}
+
+void llvm::reduceModuleDataDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing Module Data...\n";
+ runDeltaPass(Test, clearModuleData);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceModuleData.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceModuleData.h
new file mode 100644
index 00000000000..972c53460d7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceModuleData.h
@@ -0,0 +1,18 @@
+//===- ReduceModuleData.h --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEMODULEDATA_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEMODULEDATA_H
+
+#include "Delta.h"
+
+namespace llvm {
+void reduceModuleDataDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandBundles.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandBundles.cpp
new file mode 100644
index 00000000000..c28bbb2e48d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandBundles.cpp
@@ -0,0 +1,109 @@
+//===- ReduceOperandBundes.cpp - Specialized Delta Pass -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting operand bundes from calls.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceOperandBundles.h"
+#include "Delta.h"
+#include "TestRunner.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/IR/InstVisitor.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+namespace llvm {
+class Module;
+} // namespace llvm
+
+using namespace llvm;
+
+namespace {
+
+/// Given ChunksToKeep, produce a map of calls and indexes of operand bundles
+/// to be preserved for each call.
+class OperandBundleRemapper : public InstVisitor<OperandBundleRemapper> {
+ Oracle &O;
+
+public:
+ DenseMap<CallBase *, std::vector<unsigned>> CallsToRefine;
+
+ explicit OperandBundleRemapper(Oracle &O) : O(O) {}
+
+ /// So far only CallBase sub-classes can have operand bundles.
+ /// Let's see which of the operand bundles of this call are to be kept.
+ void visitCallBase(CallBase &Call) {
+ if (!Call.hasOperandBundles())
+ return; // No bundles to begin with.
+
+ // Insert this call into map, we will likely want to rebuild it.
+ auto &OperandBundlesToKeepIndexes = CallsToRefine[&Call];
+ OperandBundlesToKeepIndexes.reserve(Call.getNumOperandBundles());
+
+ // Enumerate every operand bundle on this call.
+ for (unsigned BundleIndex : seq(0U, Call.getNumOperandBundles()))
+ if (O.shouldKeep()) // Should we keep this one?
+ OperandBundlesToKeepIndexes.emplace_back(BundleIndex);
+ }
+};
+
+struct OperandBundleCounter : public InstVisitor<OperandBundleCounter> {
+ /// How many features (in this case, operand bundles) did we count, total?
+ int OperandBundeCount = 0;
+
+ /// So far only CallBase sub-classes can have operand bundles.
+ void visitCallBase(CallBase &Call) {
+ // Just accumulate the total number of operand bundles.
+ OperandBundeCount += Call.getNumOperandBundles();
+ }
+};
+
+} // namespace
+
+static void maybeRewriteCallWithDifferentBundles(
+ CallBase *OrigCall, ArrayRef<unsigned> OperandBundlesToKeepIndexes) {
+ if (OperandBundlesToKeepIndexes.size() == OrigCall->getNumOperandBundles())
+ return; // Not modifying operand bundles of this call after all.
+
+ std::vector<OperandBundleDef> NewBundles;
+ NewBundles.reserve(OperandBundlesToKeepIndexes.size());
+
+ // Actually copy over the bundles that we want to keep.
+ transform(OperandBundlesToKeepIndexes, std::back_inserter(NewBundles),
+ [OrigCall](unsigned Index) {
+ return OperandBundleDef(OrigCall->getOperandBundleAt(Index));
+ });
+
+ // Finally actually replace the bundles on the call.
+ CallBase *NewCall = CallBase::Create(OrigCall, NewBundles, OrigCall);
+ OrigCall->replaceAllUsesWith(NewCall);
+ OrigCall->eraseFromParent();
+}
+
+/// Removes out-of-chunk operand bundles from calls.
+static void extractOperandBundesFromModule(Oracle &O, Module &Program) {
+ OperandBundleRemapper R(O);
+ R.visit(Program);
+
+ for (const auto &I : R.CallsToRefine)
+ maybeRewriteCallWithDifferentBundles(I.first, I.second);
+}
+
+void llvm::reduceOperandBundesDeltaPass(TestRunner &Test) {
+ outs() << "*** Reducing OperandBundes...\n";
+ runDeltaPass(Test, extractOperandBundesFromModule);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandBundles.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandBundles.h
new file mode 100644
index 00000000000..d07e0218595
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandBundles.h
@@ -0,0 +1,22 @@
+//===- ReduceOperandBundes.h - Specialized Delta Pass ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce uninteresting operand bundes from calls.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDBUNDLES_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDBUNDLES_H
+
+namespace llvm {
+class TestRunner;
+void reduceOperandBundesDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperands.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperands.cpp
new file mode 100644
index 00000000000..5bfd87633f1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperands.cpp
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceOperands.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Operator.h"
+#include "llvm/IR/Type.h"
+
+using namespace llvm;
+
+static void
+extractOperandsFromModule(Oracle &O, Module &Program,
+ function_ref<Value *(Use &)> ReduceValue) {
+ for (auto &F : Program.functions()) {
+ for (auto &I : instructions(&F)) {
+ for (auto &Op : I.operands()) {
+ Value *Reduced = ReduceValue(Op);
+ if (Reduced && !O.shouldKeep())
+ Op.set(Reduced);
+ }
+ }
+ }
+}
+
+static bool isOne(Use &Op) {
+ auto *C = dyn_cast<Constant>(Op);
+ return C && C->isOneValue();
+}
+
+static bool isZero(Use &Op) {
+ auto *C = dyn_cast<Constant>(Op);
+ return C && C->isNullValue();
+}
+
+static bool shouldReduceOperand(Use &Op) {
+ Type *Ty = Op->getType();
+ if (Ty->isLabelTy() || Ty->isMetadataTy())
+ return false;
+ // TODO: be more precise about which GEP operands we can reduce (e.g. array
+ // indexes)
+ if (isa<GEPOperator>(Op.getUser()))
+ return false;
+ if (auto *CB = dyn_cast<CallBase>(Op.getUser())) {
+ if (&CB->getCalledOperandUse() == &Op)
+ return false;
+ }
+ return true;
+}
+
+void llvm::reduceOperandsUndefDeltaPass(TestRunner &Test) {
+ errs() << "*** Reducing Operands to undef...\n";
+ auto ReduceValue = [](Use &Op) -> Value * {
+ if (!shouldReduceOperand(Op))
+ return nullptr;
+ // Don't replace existing ConstantData Uses.
+ return isa<ConstantData>(*Op) ? nullptr : UndefValue::get(Op->getType());
+ };
+ runDeltaPass(Test, [ReduceValue](Oracle &O, Module &Program) {
+ extractOperandsFromModule(O, Program, ReduceValue);
+ });
+}
+
+void llvm::reduceOperandsOneDeltaPass(TestRunner &Test) {
+ errs() << "*** Reducing Operands to one...\n";
+ auto ReduceValue = [](Use &Op) -> Value * {
+ // TODO: support floats
+ if (!shouldReduceOperand(Op))
+ return nullptr;
+ auto *Ty = dyn_cast<IntegerType>(Op->getType());
+ if (!Ty)
+ return nullptr;
+ // Don't replace existing ones and zeroes.
+ return (isOne(Op) || isZero(Op)) ? nullptr : ConstantInt::get(Ty, 1);
+ };
+ runDeltaPass(Test, [ReduceValue](Oracle &O, Module &Program) {
+ extractOperandsFromModule(O, Program, ReduceValue);
+ });
+}
+
+void llvm::reduceOperandsZeroDeltaPass(TestRunner &Test) {
+ errs() << "*** Reducing Operands to zero...\n";
+ auto ReduceValue = [](Use &Op) -> Value * {
+ if (!shouldReduceOperand(Op))
+ return nullptr;
+ // Don't replace existing zeroes.
+ return isZero(Op) ? nullptr : Constant::getNullValue(Op->getType());
+ };
+ runDeltaPass(Test, [ReduceValue](Oracle &O, Module &Program) {
+ extractOperandsFromModule(O, Program, ReduceValue);
+ });
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperands.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperands.h
new file mode 100644
index 00000000000..18fdb07dd3f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperands.h
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDS_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDS_H
+
+#include "Delta.h"
+
+namespace llvm {
+void reduceOperandsUndefDeltaPass(TestRunner &Test);
+void reduceOperandsOneDeltaPass(TestRunner &Test);
+void reduceOperandsZeroDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsSkip.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsSkip.cpp
new file mode 100644
index 00000000000..a239eca1d4e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsSkip.cpp
@@ -0,0 +1,223 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceOperandsSkip.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Operator.h"
+
+using namespace llvm;
+
+/// Collect all values that are directly or indirectly referenced by @p Root,
+/// including Root itself. This is a BF search such that the more steps needed
+/// to get to the reference, the more behind it is found in @p Collection. Each
+/// step could be its own reduction, therefore we consider later values "more
+/// reduced".
+static SetVector<Value *> collectReferencedValues(Value *Root) {
+ SetVector<Value *> Refs;
+ std::deque<Value *> Worklist;
+ Worklist.push_back(Root);
+
+ while (!Worklist.empty()) {
+ Value *Val = Worklist.front();
+ Worklist.pop_front();
+ if (!Refs.insert(Val))
+ continue;
+
+ if (auto *O = dyn_cast<Operator>(Val)) {
+ for (Use &Op : O->operands())
+ Worklist.push_back(Op.get());
+ }
+ }
+
+ return Refs;
+}
+
+static bool shouldReduceOperand(Use &Op) {
+ Type *Ty = Op->getType();
+ if (Ty->isLabelTy() || Ty->isMetadataTy())
+ return false;
+ // TODO: be more precise about which GEP operands we can reduce (e.g. array
+ // indexes)
+ if (isa<GEPOperator>(Op.getUser()))
+ return false;
+ if (auto *CB = dyn_cast<CallBase>(Op.getUser())) {
+ if (&CB->getCalledOperandUse() == &Op)
+ return false;
+ }
+ return true;
+}
+
+/// Return a reduction priority for @p V. A higher values means "more reduced".
+static int classifyReductivePower(Value *V) {
+ if (auto *C = dyn_cast<ConstantData>(V)) {
+ if (isa<UndefValue>(V))
+ return 4;
+ if (C->isNullValue())
+ return 7;
+ if (C->isOneValue())
+ return 6;
+ return 5;
+ }
+
+ if (isa<Argument>(V))
+ return 3;
+
+ if (isa<GlobalValue>(V))
+ return 2;
+
+ if (isa<Constant>(V))
+ return 1;
+
+ if (isa<Instruction>(V))
+ return -1;
+
+ return 0;
+}
+
+/// Calls @p Callback for every reduction opportunity in @p F. Used by
+/// countOperands() and extractOperandsFromModule() to ensure consistency
+/// between the two.
+static void
+opportunities(Function &F,
+ function_ref<void(Use &, ArrayRef<Value *>)> Callback) {
+ if (F.isDeclaration())
+ return;
+
+ // Need DominatorTree to find out whether an SSA value can be referenced.
+ DominatorTree DT(F);
+
+ // Return whether @p LHS is "more reduced" that @p RHS. That is, whether
+ // @p RHS should be preferred over @p LHS in a reduced output. This is a
+ // partial order, a Value may not be preferable over another.
+ auto IsMoreReduced = [&DT](Value *LHS, Value *RHS) -> bool {
+ // A value is not more reduced than itself.
+ if (LHS == RHS)
+ return false;
+
+ int ReductivePowerDiff =
+ classifyReductivePower(RHS) - classifyReductivePower(LHS);
+ if (ReductivePowerDiff != 0)
+ return ReductivePowerDiff < 0;
+
+ // LHS is more reduced if it is defined further up the dominance tree. In a
+ // chain of definitions,
+ //
+ // %a = ..
+ // %b = op %a
+ // %c = op %b
+ //
+ // every use of %b can be replaced by %a, but not by a use of %c. That is, a
+ // use %c can be replaced in steps first by %b, then by %a, making %a the
+ // "more reduced" choice that skips over more instructions.
+ auto *LHSInst = dyn_cast<Instruction>(LHS);
+ auto *RHSInst = dyn_cast<Instruction>(RHS);
+ if (LHSInst && RHSInst) {
+ if (DT.dominates(LHSInst, RHSInst))
+ return true;
+ }
+
+ // Compress the number of used arguments by prefering the first ones. Unused
+ // trailing argument can be removed by the arguments pass.
+ auto *LHSArg = dyn_cast<Argument>(LHS);
+ auto *RHSArg = dyn_cast<Argument>(RHS);
+ if (LHSArg && RHSArg) {
+ if (LHSArg->getArgNo() < RHSArg->getArgNo())
+ return true;
+ }
+
+ return false;
+ };
+
+ for (Instruction &I : instructions(&F)) {
+ for (Use &Op : I.operands()) {
+ if (!shouldReduceOperand(Op))
+ continue;
+ Value *OpVal = Op.get();
+
+ // Collect refenced values as potential replacement candidates.
+ SetVector<Value *> ReferencedVals = collectReferencedValues(OpVal);
+
+ // Regardless whether referenced, add the function arguments as
+ // replacement possibility with the goal of reducing the number of (used)
+ // function arguments, possibly created by the the operands-to-args.
+ for (Argument &Arg : F.args())
+ ReferencedVals.insert(&Arg);
+
+ // After all candidates have been added, it doesn't need to be a set
+ // anymore.
+ std::vector<Value *> Candidates = ReferencedVals.takeVector();
+
+ // Remove ineligible candidates.
+ llvm::erase_if(Candidates, [&, OpVal](Value *V) {
+ // Candidate value must have the same type.
+ if (OpVal->getType() != V->getType())
+ return true;
+
+ // Only consider candidates that are "more reduced" than the original
+ // value. This explicitly also rules out candidates with the same
+ // reduction power. This is to ensure that repeated invocations of this
+ // pass eventually reach a fixpoint without switch back and forth
+ // between two opportunities with the same reductive power.
+ return !IsMoreReduced(V, OpVal);
+ });
+
+ if (Candidates.empty())
+ continue;
+
+ // collectReferencedValues pushed the more reductive values to the end of
+ // the collection, but we need them at the front.
+ std::reverse(Candidates.begin(), Candidates.end());
+
+ // Independency of collectReferencedValues's idea of reductive power,
+ // ensure the the partial order of IsMoreReduced is enforced.
+ llvm::stable_sort(Candidates, IsMoreReduced);
+
+ Callback(Op, Candidates);
+ }
+ }
+}
+
+static void extractOperandsFromModule(Oracle &O, Module &Program) {
+ for (Function &F : Program.functions()) {
+ SmallVector<std::pair<Use *, Value *>> Replacements;
+ opportunities(F, [&](Use &Op, ArrayRef<Value *> Candidates) {
+ // Only apply the candidate the Oracle selected to keep that is the most
+ // reduced. Candidates with less reductive power can be interpreted as an
+ // intermediate step that is immediately replaced with the more reduced
+ // one. The number of shouldKeep() calls must be independent of the result
+ // of previous shouldKeep() calls to keep the total number of calls
+ // in-sync with what countOperands() has computed.
+ bool AlreadyReplaced = false;
+ for (Value *C : Candidates) {
+ bool Keep = O.shouldKeep();
+ if (AlreadyReplaced || Keep)
+ continue;
+
+ // Replacing the operand value immediately would influence the candidate
+ // set for the following operands. Delay it until after all candidates
+ // have been determined.
+ Replacements.push_back({&Op, C});
+
+ AlreadyReplaced = true;
+ }
+ });
+
+ for (std::pair<Use *, Value *> P : Replacements)
+ P.first->set(P.second);
+ }
+}
+
+void llvm::reduceOperandsSkipDeltaPass(TestRunner &Test) {
+ errs() << "*** Reducing operands by skipping over instructions ...\n";
+ runDeltaPass(Test, extractOperandsFromModule);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsSkip.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsSkip.h
new file mode 100644
index 00000000000..79ee46235e6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsSkip.h
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSSKIP_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSSKIP_H
+
+#include "Delta.h"
+
+namespace llvm {
+void reduceOperandsSkipDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif /* LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSSKIP_H */
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsToArgs.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsToArgs.cpp
new file mode 100644
index 00000000000..f1dcf24c6ea
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsToArgs.cpp
@@ -0,0 +1,198 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceOperandsToArgs.h"
+#include "Delta.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+using namespace llvm;
+
+static bool canReplaceFunction(Function *F) {
+ return all_of(F->uses(), [](Use &Op) {
+ if (auto *CI = dyn_cast<CallBase>(Op.getUser()))
+ return &CI->getCalledOperandUse() == &Op;
+ return false;
+ });
+}
+
+static bool canReduceUse(Use &Op) {
+ Value *Val = Op.get();
+ Type *Ty = Val->getType();
+
+ // Only replace operands that can be passed-by-value.
+ if (!Ty->isFirstClassType())
+ return false;
+
+ // Don't pass labels/metadata as arguments.
+ if (Ty->isLabelTy() || Ty->isMetadataTy())
+ return false;
+
+ // No need to replace values that are already arguments.
+ if (isa<Argument>(Val))
+ return false;
+
+ // Do not replace literals.
+ if (isa<ConstantData>(Val))
+ return false;
+
+ // Do not convert direct function calls to indirect calls.
+ if (auto *CI = dyn_cast<CallBase>(Op.getUser()))
+ if (&CI->getCalledOperandUse() == &Op)
+ return false;
+
+ return true;
+}
+
+/// Goes over OldF calls and replaces them with a call to NewF.
+static void replaceFunctionCalls(Function *OldF, Function *NewF) {
+ SmallVector<CallBase *> Callers;
+ for (Use &U : OldF->uses()) {
+ auto *CI = cast<CallBase>(U.getUser());
+ assert(&U == &CI->getCalledOperandUse());
+ assert(CI->getCalledFunction() == OldF);
+ Callers.push_back(CI);
+ }
+
+ // Call arguments for NewF.
+ SmallVector<Value *> Args(NewF->arg_size(), nullptr);
+
+ // Fill up the additional parameters with undef values.
+ for (auto ArgIdx : llvm::seq<size_t>(OldF->arg_size(), NewF->arg_size())) {
+ Type *NewArgTy = NewF->getArg(ArgIdx)->getType();
+ Args[ArgIdx] = UndefValue::get(NewArgTy);
+ }
+
+ for (CallBase *CI : Callers) {
+ // Preserve the original function arguments.
+ for (auto Z : zip_first(CI->args(), Args))
+ std::get<1>(Z) = std::get<0>(Z);
+
+ // Also preserve operand bundles.
+ SmallVector<OperandBundleDef> OperandBundles;
+ CI->getOperandBundlesAsDefs(OperandBundles);
+
+ // Create the new function call.
+ CallBase *NewCI;
+ if (auto *II = dyn_cast<InvokeInst>(CI)) {
+ NewCI = InvokeInst::Create(NewF, cast<InvokeInst>(II)->getNormalDest(),
+ cast<InvokeInst>(II)->getUnwindDest(), Args,
+ OperandBundles, CI->getName());
+ } else {
+ assert(isa<CallInst>(CI));
+ NewCI = CallInst::Create(NewF, Args, OperandBundles, CI->getName());
+ }
+ NewCI->setCallingConv(NewF->getCallingConv());
+
+ // Do the replacement for this use.
+ if (!CI->use_empty())
+ CI->replaceAllUsesWith(NewCI);
+ ReplaceInstWithInst(CI, NewCI);
+ }
+}
+
+/// Add a new function argument to @p F for each use in @OpsToReplace, and
+/// replace those operand values with the new function argument.
+static void substituteOperandWithArgument(Function *OldF,
+ ArrayRef<Use *> OpsToReplace) {
+ if (OpsToReplace.empty())
+ return;
+
+ SetVector<Value *> UniqueValues;
+ for (Use *Op : OpsToReplace)
+ UniqueValues.insert(Op->get());
+
+ // Determine the new function's signature.
+ SmallVector<Type *> NewArgTypes;
+ llvm::append_range(NewArgTypes, OldF->getFunctionType()->params());
+ size_t ArgOffset = NewArgTypes.size();
+ for (Value *V : UniqueValues)
+ NewArgTypes.push_back(V->getType());
+ FunctionType *FTy =
+ FunctionType::get(OldF->getFunctionType()->getReturnType(), NewArgTypes,
+ OldF->getFunctionType()->isVarArg());
+
+ // Create the new function...
+ Function *NewF =
+ Function::Create(FTy, OldF->getLinkage(), OldF->getAddressSpace(),
+ OldF->getName(), OldF->getParent());
+
+ // In order to preserve function order, we move NewF behind OldF
+ NewF->removeFromParent();
+ OldF->getParent()->getFunctionList().insertAfter(OldF->getIterator(), NewF);
+
+ // Preserve the parameters of OldF.
+ ValueToValueMapTy VMap;
+ for (auto Z : zip_first(OldF->args(), NewF->args())) {
+ Argument &OldArg = std::get<0>(Z);
+ Argument &NewArg = std::get<1>(Z);
+
+ NewArg.setName(OldArg.getName()); // Copy the name over...
+ VMap[&OldArg] = &NewArg; // Add mapping to VMap
+ }
+
+ // Adjust the new parameters.
+ ValueToValueMapTy OldValMap;
+ for (auto Z : zip_first(UniqueValues, drop_begin(NewF->args(), ArgOffset))) {
+ Value *OldVal = std::get<0>(Z);
+ Argument &NewArg = std::get<1>(Z);
+
+ NewArg.setName(OldVal->getName());
+ OldValMap[OldVal] = &NewArg;
+ }
+
+ SmallVector<ReturnInst *, 8> Returns; // Ignore returns cloned.
+ CloneFunctionInto(NewF, OldF, VMap, CloneFunctionChangeType::LocalChangesOnly,
+ Returns, "", /*CodeInfo=*/nullptr);
+
+ // Replace the actual operands.
+ for (Use *Op : OpsToReplace) {
+ Value *NewArg = OldValMap.lookup(Op->get());
+ auto *NewUser = cast<Instruction>(VMap.lookup(Op->getUser()));
+ NewUser->setOperand(Op->getOperandNo(), NewArg);
+ }
+
+ // Replace all OldF uses with NewF.
+ replaceFunctionCalls(OldF, NewF);
+
+ // Rename NewF to OldF's name.
+ std::string FName = OldF->getName().str();
+ OldF->replaceAllUsesWith(ConstantExpr::getBitCast(NewF, OldF->getType()));
+ OldF->eraseFromParent();
+ NewF->setName(FName);
+}
+
+static void reduceOperandsToArgs(Oracle &O, Module &Program) {
+ SmallVector<Use *> OperandsToReduce;
+ for (Function &F : make_early_inc_range(Program.functions())) {
+ if (!canReplaceFunction(&F))
+ continue;
+ OperandsToReduce.clear();
+ for (Instruction &I : instructions(&F)) {
+ for (Use &Op : I.operands()) {
+ if (!canReduceUse(Op))
+ continue;
+ if (O.shouldKeep())
+ continue;
+
+ OperandsToReduce.push_back(&Op);
+ }
+ }
+
+ substituteOperandWithArgument(&F, OperandsToReduce);
+ }
+}
+
+void llvm::reduceOperandsToArgsDeltaPass(TestRunner &Test) {
+ outs() << "*** Converting operands to function arguments ...\n";
+ return runDeltaPass(Test, reduceOperandsToArgs);
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsToArgs.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsToArgs.h
new file mode 100644
index 00000000000..2bff3936167
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceOperandsToArgs.h
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSTOARGS_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSTOARGS_H
+
+#include "Delta.h"
+
+namespace llvm {
+void reduceOperandsToArgsDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif /* LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCEOPERANDSTOARGS_H */
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceSpecialGlobals.cpp b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceSpecialGlobals.cpp
new file mode 100644
index 00000000000..57160f46ff8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceSpecialGlobals.cpp
@@ -0,0 +1,42 @@
+//===- ReduceSpecialGlobals.cpp - Specialized Delta Pass ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce special globals, like @llvm.used, in the provided Module.
+//
+// For more details about special globals, see
+// https://llvm.org/docs/LangRef.html#intrinsic-global-variables
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReduceSpecialGlobals.h"
+#include "Delta.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/GlobalValue.h"
+
+using namespace llvm;
+
+static StringRef SpecialGlobalNames[] = {"llvm.used", "llvm.compiler.used"};
+
+/// Removes all special globals aren't inside any of the
+/// desired Chunks.
+static void extractSpecialGlobalsFromModule(Oracle &O, Module &Program) {
+ for (StringRef Name : SpecialGlobalNames) {
+ if (auto *Used = Program.getNamedGlobal(Name)) {
+ Used->replaceAllUsesWith(UndefValue::get(Used->getType()));
+ Used->eraseFromParent();
+ }
+ }
+}
+
+void llvm::reduceSpecialGlobalsDeltaPass(TestRunner &Test) {
+ errs() << "*** Reducing Special Globals ...\n";
+ runDeltaPass(Test, extractSpecialGlobalsFromModule);
+ errs() << "----------------------------\n";
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceSpecialGlobals.h b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceSpecialGlobals.h
new file mode 100644
index 00000000000..c0f3f9ee337
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/deltas/ReduceSpecialGlobals.h
@@ -0,0 +1,26 @@
+//===- ReduceSpecialGlobals.h - Specialized Delta Pass --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a function which calls the Generic Delta pass in order
+// to reduce special globals, like @llvm.used, in the provided Module.
+//
+// For more details about special globals, see
+// https://llvm.org/docs/LangRef.html#intrinsic-global-variables
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCESPECIALGLOBALS_H
+#define LLVM_TOOLS_LLVM_REDUCE_DELTAS_REDUCESPECIALGLOBALS_H
+
+#include "Delta.h"
+
+namespace llvm {
+void reduceSpecialGlobalsDeltaPass(TestRunner &Test);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/llvm-reduce.cpp b/contrib/libs/llvm14/tools/llvm-reduce/llvm-reduce.cpp
new file mode 100644
index 00000000000..59cc055a087
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/llvm-reduce.cpp
@@ -0,0 +1,180 @@
+//===- llvm-reduce.cpp - The LLVM Delta Reduction utility -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program tries to reduce an IR test case for a given interesting-ness
+// test. It runs multiple delta debugging passes in order to minimize the input
+// file. It's worth noting that this is a part of the bugpoint redesign
+// proposal, and thus a *temporary* tool that will eventually be integrated
+// into the bugpoint tool itself.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DeltaManager.h"
+#include "ReducerWorkItem.h"
+#include "TestRunner.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Target/TargetMachine.h"
+#include <system_error>
+#include <vector>
+
+using namespace llvm;
+
+static cl::OptionCategory Options("llvm-reduce options");
+
+static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden,
+ cl::cat(Options));
+static cl::opt<bool> Version("v", cl::desc("Alias for -version"), cl::Hidden,
+ cl::cat(Options));
+
+static cl::opt<bool>
+ PrintDeltaPasses("print-delta-passes",
+ cl::desc("Print list of delta passes, passable to "
+ "--delta-passes as a comma separated list"),
+ cl::cat(Options));
+
+static cl::opt<std::string> InputFilename(cl::Positional, cl::Required,
+ cl::desc("<input llvm ll/bc file>"),
+ cl::cat(Options));
+
+static cl::opt<std::string>
+ TestFilename("test", cl::Required,
+ cl::desc("Name of the interesting-ness test to be run"),
+ cl::cat(Options));
+
+static cl::list<std::string>
+ TestArguments("test-arg", cl::ZeroOrMore,
+ cl::desc("Arguments passed onto the interesting-ness test"),
+ cl::cat(Options));
+
+static cl::opt<std::string> OutputFilename(
+ "output", cl::desc("Specify the output file. default: reduced.ll|mir"));
+static cl::alias OutputFileAlias("o", cl::desc("Alias for -output"),
+ cl::aliasopt(OutputFilename),
+ cl::cat(Options));
+
+static cl::opt<bool>
+ ReplaceInput("in-place",
+ cl::desc("WARNING: This option will replace your input file "
+ "with the reduced version!"),
+ cl::cat(Options));
+
+enum class InputLanguages { None, IR, MIR };
+
+static cl::opt<InputLanguages>
+ InputLanguage("x", cl::ValueOptional,
+ cl::desc("Input language ('ir' or 'mir')"),
+ cl::init(InputLanguages::None),
+ cl::values(clEnumValN(InputLanguages::IR, "ir", ""),
+ clEnumValN(InputLanguages::MIR, "mir", "")),
+ cl::cat(Options));
+
+static cl::opt<std::string> TargetTriple("mtriple",
+ cl::desc("Set the target triple"),
+ cl::cat(Options));
+
+static cl::opt<int>
+ MaxPassIterations("max-pass-iterations",
+ cl::desc("Maximum number of times to run the full set "
+ "of delta passes (default=1)"),
+ cl::init(1), cl::cat(Options));
+
+static codegen::RegisterCodeGenFlags CGF;
+
+void writeOutput(ReducerWorkItem &M, StringRef Message) {
+ if (ReplaceInput) // In-place
+ OutputFilename = InputFilename.c_str();
+ else if (OutputFilename.empty() || OutputFilename == "-")
+ OutputFilename = M.isMIR() ? "reduced.mir" : "reduced.ll";
+ std::error_code EC;
+ raw_fd_ostream Out(OutputFilename, EC);
+ if (EC) {
+ errs() << "Error opening output file: " << EC.message() << "!\n";
+ exit(1);
+ }
+ M.print(Out, /*AnnotationWriter=*/nullptr);
+ errs() << Message << OutputFilename << "\n";
+}
+
+static std::unique_ptr<LLVMTargetMachine> createTargetMachine() {
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmPrinters();
+ InitializeAllAsmParsers();
+
+ if (TargetTriple == "")
+ TargetTriple = sys::getDefaultTargetTriple();
+ auto TT(Triple::normalize(TargetTriple));
+ std::string CPU(codegen::getCPUStr());
+ std::string FS(codegen::getFeaturesStr());
+
+ std::string Error;
+ const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error);
+
+ return std::unique_ptr<LLVMTargetMachine>(
+ static_cast<LLVMTargetMachine *>(TheTarget->createTargetMachine(
+ TT, CPU, FS, TargetOptions(), None, None, CodeGenOpt::Default)));
+}
+
+int main(int Argc, char **Argv) {
+ InitLLVM X(Argc, Argv);
+
+ cl::HideUnrelatedOptions({&Options, &getColorCategory()});
+ cl::ParseCommandLineOptions(Argc, Argv, "LLVM automatic testcase reducer.\n");
+
+ bool ReduceModeMIR = false;
+ if (InputLanguage != InputLanguages::None) {
+ if (InputLanguage == InputLanguages::MIR)
+ ReduceModeMIR = true;
+ } else if (StringRef(InputFilename).endswith(".mir")) {
+ ReduceModeMIR = true;
+ }
+
+ if (PrintDeltaPasses) {
+ printDeltaPasses(errs());
+ return 0;
+ }
+
+ LLVMContext Context;
+ std::unique_ptr<LLVMTargetMachine> TM;
+ std::unique_ptr<MachineModuleInfo> MMI;
+ std::unique_ptr<ReducerWorkItem> OriginalProgram;
+ if (ReduceModeMIR) {
+ TM = createTargetMachine();
+ MMI = std::make_unique<MachineModuleInfo>(TM.get());
+ }
+ OriginalProgram = parseReducerWorkItem(InputFilename, Context, MMI.get());
+ if (!OriginalProgram) {
+ return 1;
+ }
+
+ // Initialize test environment
+ TestRunner Tester(TestFilename, TestArguments, std::move(OriginalProgram));
+
+ // Try to reduce code
+ runDeltaPasses(Tester, MaxPassIterations);
+
+ // Print reduced file to STDOUT
+ if (OutputFilename == "-")
+ Tester.getProgram().print(outs(), nullptr);
+ else
+ writeOutput(Tester.getProgram(), "\nDone reducing! Reduced testcase: ");
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-reduce/ya.make b/contrib/libs/llvm14/tools/llvm-reduce/ya.make
new file mode 100644
index 00000000000..1157ee259a6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-reduce/ya.make
@@ -0,0 +1,109 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/AsmPrinter
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/CodeGen/MIRParser
+ contrib/libs/llvm14/lib/CodeGen/SelectionDAG
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/CFGuard
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-reduce
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ DeltaManager.cpp
+ ReducerWorkItem.cpp
+ TestRunner.cpp
+ deltas/Delta.cpp
+ deltas/ReduceAliases.cpp
+ deltas/ReduceArguments.cpp
+ deltas/ReduceAttributes.cpp
+ deltas/ReduceBasicBlocks.cpp
+ deltas/ReduceFunctionBodies.cpp
+ deltas/ReduceFunctions.cpp
+ deltas/ReduceGlobalObjects.cpp
+ deltas/ReduceGlobalValues.cpp
+ deltas/ReduceGlobalVarInitializers.cpp
+ deltas/ReduceGlobalVars.cpp
+ deltas/ReduceInstructions.cpp
+ deltas/ReduceInstructionsMIR.cpp
+ deltas/ReduceMetadata.cpp
+ deltas/ReduceModuleData.cpp
+ deltas/ReduceOperandBundles.cpp
+ deltas/ReduceOperands.cpp
+ deltas/ReduceOperandsSkip.cpp
+ deltas/ReduceOperandsToArgs.cpp
+ deltas/ReduceSpecialGlobals.cpp
+ llvm-reduce.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-rtdyld/llvm-rtdyld.cpp b/contrib/libs/llvm14/tools/llvm-rtdyld/llvm-rtdyld.cpp
new file mode 100644
index 00000000000..893d8a55c89
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rtdyld/llvm-rtdyld.cpp
@@ -0,0 +1,1049 @@
+//===-- llvm-rtdyld.cpp - MCJIT Testing Tool ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a testing tool for use with the MC-JIT LLVM components.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
+#include "llvm/ExecutionEngine/RuntimeDyld.h"
+#include "llvm/ExecutionEngine/RuntimeDyldChecker.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/SymbolSize.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MSVCErrorWorkarounds.h"
+#include "llvm/Support/Memory.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <future>
+#include <list>
+
+using namespace llvm;
+using namespace llvm::object;
+
+static cl::OptionCategory RTDyldCategory("RTDyld Options");
+
+static cl::list<std::string> InputFileList(cl::Positional, cl::ZeroOrMore,
+ cl::desc("<input files>"),
+ cl::cat(RTDyldCategory));
+
+enum ActionType {
+ AC_Execute,
+ AC_PrintObjectLineInfo,
+ AC_PrintLineInfo,
+ AC_PrintDebugLineInfo,
+ AC_Verify
+};
+
+static cl::opt<ActionType> Action(
+ cl::desc("Action to perform:"), cl::init(AC_Execute),
+ cl::values(
+ clEnumValN(AC_Execute, "execute",
+ "Load, link, and execute the inputs."),
+ clEnumValN(AC_PrintLineInfo, "printline",
+ "Load, link, and print line information for each function."),
+ clEnumValN(AC_PrintDebugLineInfo, "printdebugline",
+ "Load, link, and print line information for each function "
+ "using the debug object"),
+ clEnumValN(AC_PrintObjectLineInfo, "printobjline",
+ "Like -printlineinfo but does not load the object first"),
+ clEnumValN(AC_Verify, "verify",
+ "Load, link and verify the resulting memory image.")),
+ cl::cat(RTDyldCategory));
+
+static cl::opt<std::string>
+ EntryPoint("entry", cl::desc("Function to call as entry point."),
+ cl::init("_main"), cl::cat(RTDyldCategory));
+
+static cl::list<std::string> Dylibs("dylib", cl::desc("Add library."),
+ cl::ZeroOrMore, cl::cat(RTDyldCategory));
+
+static cl::list<std::string> InputArgv("args", cl::Positional,
+ cl::desc("<program arguments>..."),
+ cl::ZeroOrMore, cl::PositionalEatsArgs,
+ cl::cat(RTDyldCategory));
+
+static cl::opt<std::string>
+ TripleName("triple", cl::desc("Target triple for disassembler"),
+ cl::cat(RTDyldCategory));
+
+static cl::opt<std::string>
+ MCPU("mcpu",
+ cl::desc("Target a specific cpu type (-mcpu=help for details)"),
+ cl::value_desc("cpu-name"), cl::init(""), cl::cat(RTDyldCategory));
+
+static cl::list<std::string>
+ CheckFiles("check",
+ cl::desc("File containing RuntimeDyld verifier checks."),
+ cl::ZeroOrMore, cl::cat(RTDyldCategory));
+
+static cl::opt<uint64_t>
+ PreallocMemory("preallocate",
+ cl::desc("Allocate memory upfront rather than on-demand"),
+ cl::init(0), cl::cat(RTDyldCategory));
+
+static cl::opt<uint64_t> 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, cl::cat(RTDyldCategory));
+
+static cl::opt<uint64_t> TargetAddrEnd(
+ "target-addr-end",
+ cl::desc("For -verify only: end of phony target address range."),
+ cl::init(~0ULL), cl::Hidden, cl::cat(RTDyldCategory));
+
+static cl::opt<uint64_t> TargetSectionSep(
+ "target-section-sep",
+ cl::desc("For -verify only: Separation between sections in "
+ "phony target address space."),
+ cl::init(0), cl::Hidden, cl::cat(RTDyldCategory));
+
+static cl::list<std::string>
+ SpecificSectionMappings("map-section",
+ cl::desc("For -verify only: Map a section to a "
+ "specific address."),
+ cl::ZeroOrMore, cl::Hidden,
+ cl::cat(RTDyldCategory));
+
+static cl::list<std::string> DummySymbolMappings(
+ "dummy-extern",
+ cl::desc("For -verify only: Inject a symbol into the extern "
+ "symbol table."),
+ cl::ZeroOrMore, cl::Hidden, cl::cat(RTDyldCategory));
+
+static cl::opt<bool> PrintAllocationRequests(
+ "print-alloc-requests",
+ cl::desc("Print allocation requests made to the memory "
+ "manager by RuntimeDyld"),
+ cl::Hidden, cl::cat(RTDyldCategory));
+
+static cl::opt<bool> ShowTimes("show-times",
+ cl::desc("Show times for llvm-rtdyld phases"),
+ cl::init(false), cl::cat(RTDyldCategory));
+
+ExitOnError ExitOnErr;
+
+struct RTDyldTimers {
+ TimerGroup RTDyldTG{"llvm-rtdyld timers", "timers for llvm-rtdyld phases"};
+ Timer LoadObjectsTimer{"load", "time to load/add object files", RTDyldTG};
+ Timer LinkTimer{"link", "time to link object files", RTDyldTG};
+ Timer RunTimer{"run", "time to execute jitlink'd code", RTDyldTG};
+};
+
+std::unique_ptr<RTDyldTimers> Timers;
+
+/* *** */
+
+using SectionIDMap = StringMap<unsigned>;
+using FileToSectionIDMap = StringMap<SectionIDMap>;
+
+void dumpFileToSectionIDMap(const FileToSectionIDMap &FileToSecIDMap) {
+ for (const auto &KV : FileToSecIDMap) {
+ llvm::dbgs() << "In " << KV.first() << "\n";
+ for (auto &KV2 : KV.second)
+ llvm::dbgs() << " \"" << KV2.first() << "\" -> " << KV2.second << "\n";
+ }
+}
+
+Expected<unsigned> getSectionId(const FileToSectionIDMap &FileToSecIDMap,
+ StringRef FileName, StringRef SectionName) {
+ auto I = FileToSecIDMap.find(FileName);
+ if (I == FileToSecIDMap.end())
+ return make_error<StringError>("No file named " + FileName,
+ inconvertibleErrorCode());
+ auto &SectionIDs = I->second;
+ auto J = SectionIDs.find(SectionName);
+ if (J == SectionIDs.end())
+ return make_error<StringError>("No section named \"" + SectionName +
+ "\" in file " + FileName,
+ inconvertibleErrorCode());
+ return J->second;
+}
+
+// A trivial memory manager that doesn't do anything fancy, just uses the
+// support library allocation routines directly.
+class TrivialMemoryManager : public RTDyldMemoryManager {
+public:
+ struct SectionInfo {
+ SectionInfo(StringRef Name, sys::MemoryBlock MB, unsigned SectionID)
+ : Name(std::string(Name)), MB(std::move(MB)), SectionID(SectionID) {}
+ std::string Name;
+ sys::MemoryBlock MB;
+ unsigned SectionID = ~0U;
+ };
+
+ SmallVector<SectionInfo, 16> FunctionMemory;
+ SmallVector<SectionInfo, 16> DataMemory;
+
+ uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
+ unsigned SectionID,
+ StringRef SectionName) override;
+ uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
+ unsigned SectionID, StringRef SectionName,
+ bool IsReadOnly) override;
+ TrivialMemoryManager::TLSSection
+ allocateTLSSection(uintptr_t Size, unsigned Alignment, unsigned SectionID,
+ StringRef SectionName) override;
+
+ /// If non null, records subsequent Name -> SectionID mappings.
+ void setSectionIDsMap(SectionIDMap *SecIDMap) {
+ this->SecIDMap = SecIDMap;
+ }
+
+ void *getPointerToNamedFunction(const std::string &Name,
+ bool AbortOnFailure = true) override {
+ return nullptr;
+ }
+
+ bool finalizeMemory(std::string *ErrMsg) override { return false; }
+
+ void addDummySymbol(const std::string &Name, uint64_t Addr) {
+ DummyExterns[Name] = Addr;
+ }
+
+ JITSymbol findSymbol(const std::string &Name) override {
+ auto I = DummyExterns.find(Name);
+
+ if (I != DummyExterns.end())
+ return JITSymbol(I->second, JITSymbolFlags::Exported);
+
+ if (auto Sym = RTDyldMemoryManager::findSymbol(Name))
+ return Sym;
+ else if (auto Err = Sym.takeError())
+ ExitOnErr(std::move(Err));
+ else
+ ExitOnErr(make_error<StringError>("Could not find definition for \"" +
+ Name + "\"",
+ inconvertibleErrorCode()));
+ llvm_unreachable("Should have returned or exited by now");
+ }
+
+ void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr,
+ size_t Size) override {}
+ void deregisterEHFrames() override {}
+
+ void preallocateSlab(uint64_t Size) {
+ std::error_code EC;
+ sys::MemoryBlock MB =
+ sys::Memory::allocateMappedMemory(Size, nullptr,
+ sys::Memory::MF_READ |
+ sys::Memory::MF_WRITE,
+ EC);
+ if (!MB.base())
+ report_fatal_error(Twine("Can't allocate enough memory: ") +
+ EC.message());
+
+ PreallocSlab = MB;
+ UsePreallocation = true;
+ SlabSize = Size;
+ }
+
+ uint8_t *allocateFromSlab(uintptr_t Size, unsigned Alignment, bool isCode,
+ StringRef SectionName, unsigned SectionID) {
+ Size = alignTo(Size, Alignment);
+ if (CurrentSlabOffset + Size > SlabSize)
+ report_fatal_error("Can't allocate enough memory. Tune --preallocate");
+
+ uintptr_t OldSlabOffset = CurrentSlabOffset;
+ sys::MemoryBlock MB((void *)OldSlabOffset, Size);
+ if (isCode)
+ FunctionMemory.push_back(SectionInfo(SectionName, MB, SectionID));
+ else
+ DataMemory.push_back(SectionInfo(SectionName, MB, SectionID));
+ CurrentSlabOffset += Size;
+ return (uint8_t*)OldSlabOffset;
+ }
+
+private:
+ std::map<std::string, uint64_t> DummyExterns;
+ sys::MemoryBlock PreallocSlab;
+ bool UsePreallocation = false;
+ uintptr_t SlabSize = 0;
+ uintptr_t CurrentSlabOffset = 0;
+ SectionIDMap *SecIDMap = nullptr;
+#if defined(__x86_64__) && defined(__ELF__) && defined(__linux__)
+ unsigned UsedTLSStorage = 0;
+#endif
+};
+
+uint8_t *TrivialMemoryManager::allocateCodeSection(uintptr_t Size,
+ unsigned Alignment,
+ unsigned SectionID,
+ StringRef SectionName) {
+ if (PrintAllocationRequests)
+ outs() << "allocateCodeSection(Size = " << Size << ", Alignment = "
+ << Alignment << ", SectionName = " << SectionName << ")\n";
+
+ if (SecIDMap)
+ (*SecIDMap)[SectionName] = SectionID;
+
+ if (UsePreallocation)
+ return allocateFromSlab(Size, Alignment, true /* isCode */,
+ SectionName, SectionID);
+
+ std::error_code EC;
+ sys::MemoryBlock MB =
+ sys::Memory::allocateMappedMemory(Size, nullptr,
+ sys::Memory::MF_READ |
+ sys::Memory::MF_WRITE,
+ EC);
+ if (!MB.base())
+ report_fatal_error(Twine("MemoryManager allocation failed: ") +
+ EC.message());
+ FunctionMemory.push_back(SectionInfo(SectionName, MB, SectionID));
+ return (uint8_t*)MB.base();
+}
+
+uint8_t *TrivialMemoryManager::allocateDataSection(uintptr_t Size,
+ unsigned Alignment,
+ unsigned SectionID,
+ StringRef SectionName,
+ bool IsReadOnly) {
+ if (PrintAllocationRequests)
+ outs() << "allocateDataSection(Size = " << Size << ", Alignment = "
+ << Alignment << ", SectionName = " << SectionName << ")\n";
+
+ if (SecIDMap)
+ (*SecIDMap)[SectionName] = SectionID;
+
+ if (UsePreallocation)
+ return allocateFromSlab(Size, Alignment, false /* isCode */, SectionName,
+ SectionID);
+
+ std::error_code EC;
+ sys::MemoryBlock MB =
+ sys::Memory::allocateMappedMemory(Size, nullptr,
+ sys::Memory::MF_READ |
+ sys::Memory::MF_WRITE,
+ EC);
+ if (!MB.base())
+ report_fatal_error(Twine("MemoryManager allocation failed: ") +
+ EC.message());
+ DataMemory.push_back(SectionInfo(SectionName, MB, SectionID));
+ return (uint8_t*)MB.base();
+}
+
+// In case the execution needs TLS storage, we define a very small TLS memory
+// area here that will be used in allocateTLSSection().
+#if defined(__x86_64__) && defined(__ELF__) && defined(__linux__)
+extern "C" {
+alignas(16) __attribute__((visibility("hidden"), tls_model("initial-exec"),
+ used)) thread_local char LLVMRTDyldTLSSpace[16];
+}
+#endif
+
+TrivialMemoryManager::TLSSection
+TrivialMemoryManager::allocateTLSSection(uintptr_t Size, unsigned Alignment,
+ unsigned SectionID,
+ StringRef SectionName) {
+#if defined(__x86_64__) && defined(__ELF__) && defined(__linux__)
+ if (Size + UsedTLSStorage > sizeof(LLVMRTDyldTLSSpace)) {
+ return {};
+ }
+
+ // Get the offset of the TLSSpace in the TLS block by using a tpoff
+ // relocation here.
+ int64_t TLSOffset;
+ asm("leaq LLVMRTDyldTLSSpace@tpoff, %0" : "=r"(TLSOffset));
+
+ TLSSection Section;
+ // We use the storage directly as the initialization image. This means that
+ // when a new thread is spawned after this allocation, it will not be
+ // initialized correctly. This means, llvm-rtdyld will only support TLS in a
+ // single thread.
+ Section.InitializationImage =
+ reinterpret_cast<uint8_t *>(LLVMRTDyldTLSSpace + UsedTLSStorage);
+ Section.Offset = TLSOffset + UsedTLSStorage;
+
+ UsedTLSStorage += Size;
+
+ return Section;
+#else
+ return {};
+#endif
+}
+
+static const char *ProgramName;
+
+static void ErrorAndExit(const Twine &Msg) {
+ errs() << ProgramName << ": error: " << Msg << "\n";
+ exit(1);
+}
+
+static void loadDylibs() {
+ for (const std::string &Dylib : Dylibs) {
+ if (!sys::fs::is_regular_file(Dylib))
+ report_fatal_error(Twine("Dylib not found: '") + Dylib + "'.");
+ std::string ErrMsg;
+ if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg))
+ report_fatal_error(Twine("Error loading '") + Dylib + "': " + ErrMsg);
+ }
+}
+
+/* *** */
+
+static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) {
+ assert(LoadObjects || !UseDebugObj);
+
+ // Load any dylibs requested on the command line.
+ loadDylibs();
+
+ // If we don't have any input files, read from stdin.
+ if (!InputFileList.size())
+ InputFileList.push_back("-");
+ for (auto &File : InputFileList) {
+ // Instantiate a dynamic linker.
+ TrivialMemoryManager MemMgr;
+ RuntimeDyld Dyld(MemMgr, MemMgr);
+
+ // Load the input memory buffer.
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer =
+ MemoryBuffer::getFileOrSTDIN(File);
+ if (std::error_code EC = InputBuffer.getError())
+ ErrorAndExit("unable to read input: '" + EC.message() + "'");
+
+ Expected<std::unique_ptr<ObjectFile>> MaybeObj(
+ ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef()));
+
+ if (!MaybeObj) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(MaybeObj.takeError(), OS);
+ OS.flush();
+ ErrorAndExit("unable to create object file: '" + Buf + "'");
+ }
+
+ ObjectFile &Obj = **MaybeObj;
+
+ OwningBinary<ObjectFile> DebugObj;
+ std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo = nullptr;
+ ObjectFile *SymbolObj = &Obj;
+ if (LoadObjects) {
+ // Load the object file
+ LoadedObjInfo =
+ Dyld.loadObject(Obj);
+
+ if (Dyld.hasError())
+ ErrorAndExit(Dyld.getErrorString());
+
+ // Resolve all the relocations we can.
+ Dyld.resolveRelocations();
+
+ if (UseDebugObj) {
+ DebugObj = LoadedObjInfo->getObjectForDebug(Obj);
+ SymbolObj = DebugObj.getBinary();
+ LoadedObjInfo.reset();
+ }
+ }
+
+ std::unique_ptr<DIContext> Context = DWARFContext::create(
+ *SymbolObj, DWARFContext::ProcessDebugRelocations::Process,
+ LoadedObjInfo.get());
+
+ std::vector<std::pair<SymbolRef, uint64_t>> SymAddr =
+ object::computeSymbolSizes(*SymbolObj);
+
+ // Use symbol info to iterate functions in the object.
+ for (const auto &P : SymAddr) {
+ object::SymbolRef Sym = P.first;
+ Expected<SymbolRef::Type> TypeOrErr = Sym.getType();
+ if (!TypeOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(TypeOrErr.takeError());
+ continue;
+ }
+ SymbolRef::Type Type = *TypeOrErr;
+ if (Type == object::SymbolRef::ST_Function) {
+ Expected<StringRef> Name = Sym.getName();
+ if (!Name) {
+ // TODO: Actually report errors helpfully.
+ consumeError(Name.takeError());
+ continue;
+ }
+ Expected<uint64_t> AddrOrErr = Sym.getAddress();
+ if (!AddrOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(AddrOrErr.takeError());
+ continue;
+ }
+ uint64_t Addr = *AddrOrErr;
+
+ object::SectionedAddress Address;
+
+ uint64_t Size = P.second;
+ // If we're not using the debug object, compute the address of the
+ // symbol in memory (rather than that in the unrelocated object file)
+ // and use that to query the DWARFContext.
+ if (!UseDebugObj && LoadObjects) {
+ auto SecOrErr = Sym.getSection();
+ if (!SecOrErr) {
+ // TODO: Actually report errors helpfully.
+ consumeError(SecOrErr.takeError());
+ continue;
+ }
+ object::section_iterator Sec = *SecOrErr;
+ Address.SectionIndex = Sec->getIndex();
+ uint64_t SectionLoadAddress =
+ LoadedObjInfo->getSectionLoadAddress(*Sec);
+ if (SectionLoadAddress != 0)
+ Addr += SectionLoadAddress - Sec->getAddress();
+ } else if (auto SecOrErr = Sym.getSection())
+ Address.SectionIndex = SecOrErr.get()->getIndex();
+
+ outs() << "Function: " << *Name << ", Size = " << Size
+ << ", Addr = " << Addr << "\n";
+
+ Address.Address = Addr;
+ DILineInfoTable Lines =
+ Context->getLineInfoForAddressRange(Address, Size);
+ for (auto &D : Lines) {
+ outs() << " Line info @ " << D.first - Addr << ": "
+ << D.second.FileName << ", line:" << D.second.Line << "\n";
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void doPreallocation(TrivialMemoryManager &MemMgr) {
+ // Allocate a slab of memory upfront, if required. This is used if
+ // we want to test small code models.
+ if (static_cast<intptr_t>(PreallocMemory) < 0)
+ report_fatal_error("Pre-allocated bytes of memory must be a positive integer.");
+
+ // FIXME: Limit the amount of memory that can be preallocated?
+ if (PreallocMemory != 0)
+ MemMgr.preallocateSlab(PreallocMemory);
+}
+
+static int executeInput() {
+ // Load any dylibs requested on the command line.
+ loadDylibs();
+
+ // Instantiate a dynamic linker.
+ TrivialMemoryManager MemMgr;
+ doPreallocation(MemMgr);
+ RuntimeDyld Dyld(MemMgr, MemMgr);
+
+ // If we don't have any input files, read from stdin.
+ if (!InputFileList.size())
+ InputFileList.push_back("-");
+ {
+ TimeRegion TR(Timers ? &Timers->LoadObjectsTimer : nullptr);
+ for (auto &File : InputFileList) {
+ // Load the input memory buffer.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer =
+ MemoryBuffer::getFileOrSTDIN(File);
+ if (std::error_code EC = InputBuffer.getError())
+ ErrorAndExit("unable to read input: '" + EC.message() + "'");
+ Expected<std::unique_ptr<ObjectFile>> MaybeObj(
+ ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef()));
+
+ if (!MaybeObj) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(MaybeObj.takeError(), OS);
+ OS.flush();
+ ErrorAndExit("unable to create object file: '" + Buf + "'");
+ }
+
+ ObjectFile &Obj = **MaybeObj;
+
+ // Load the object file
+ Dyld.loadObject(Obj);
+ if (Dyld.hasError()) {
+ ErrorAndExit(Dyld.getErrorString());
+ }
+ }
+ }
+
+ {
+ TimeRegion TR(Timers ? &Timers->LinkTimer : nullptr);
+ // Resove all the relocations we can.
+ // FIXME: Error out if there are unresolved relocations.
+ Dyld.resolveRelocations();
+ }
+
+ // Get the address of the entry point (_main by default).
+ void *MainAddress = Dyld.getSymbolLocalAddress(EntryPoint);
+ if (!MainAddress)
+ ErrorAndExit("no definition for '" + EntryPoint + "'");
+
+ // Invalidate the instruction cache for each loaded function.
+ for (auto &FM : MemMgr.FunctionMemory) {
+
+ auto &FM_MB = FM.MB;
+
+ // Make sure the memory is executable.
+ // setExecutable will call InvalidateInstructionCache.
+ if (auto EC = sys::Memory::protectMappedMemory(FM_MB,
+ sys::Memory::MF_READ |
+ sys::Memory::MF_EXEC))
+ ErrorAndExit("unable to mark function executable: '" + EC.message() +
+ "'");
+ }
+
+ // Dispatch to _main().
+ errs() << "loaded '" << EntryPoint << "' at: " << (void*)MainAddress << "\n";
+
+ int (*Main)(int, const char**) =
+ (int(*)(int,const char**)) uintptr_t(MainAddress);
+ std::vector<const char *> Argv;
+ // Use the name of the first input object module as argv[0] for the target.
+ Argv.push_back(InputFileList[0].data());
+ for (auto &Arg : InputArgv)
+ Argv.push_back(Arg.data());
+ Argv.push_back(nullptr);
+ int Result = 0;
+ {
+ TimeRegion TR(Timers ? &Timers->RunTimer : nullptr);
+ Result = Main(Argv.size() - 1, Argv.data());
+ }
+
+ return Result;
+}
+
+static int checkAllExpressions(RuntimeDyldChecker &Checker) {
+ for (const auto& CheckerFileName : CheckFiles) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> CheckerFileBuf =
+ MemoryBuffer::getFileOrSTDIN(CheckerFileName);
+ if (std::error_code EC = CheckerFileBuf.getError())
+ ErrorAndExit("unable to read input '" + CheckerFileName + "': " +
+ EC.message());
+
+ if (!Checker.checkAllRulesInBuffer("# rtdyld-check:",
+ CheckerFileBuf.get().get()))
+ ErrorAndExit("some checks in '" + CheckerFileName + "' failed");
+ }
+ return 0;
+}
+
+void applySpecificSectionMappings(RuntimeDyld &Dyld,
+ const FileToSectionIDMap &FileToSecIDMap) {
+
+ for (StringRef Mapping : SpecificSectionMappings) {
+ size_t EqualsIdx = Mapping.find_first_of("=");
+ std::string SectionIDStr = std::string(Mapping.substr(0, EqualsIdx));
+ size_t ComaIdx = Mapping.find_first_of(",");
+
+ if (ComaIdx == StringRef::npos)
+ report_fatal_error("Invalid section specification '" + Mapping +
+ "'. Should be '<file name>,<section name>=<addr>'");
+
+ std::string FileName = SectionIDStr.substr(0, ComaIdx);
+ std::string SectionName = SectionIDStr.substr(ComaIdx + 1);
+ unsigned SectionID =
+ ExitOnErr(getSectionId(FileToSecIDMap, FileName, SectionName));
+
+ auto* OldAddr = Dyld.getSectionContent(SectionID).data();
+ std::string NewAddrStr = std::string(Mapping.substr(EqualsIdx + 1));
+ uint64_t NewAddr;
+
+ if (StringRef(NewAddrStr).getAsInteger(0, NewAddr))
+ report_fatal_error("Invalid section address in mapping '" + Mapping +
+ "'.");
+
+ Dyld.mapSectionAddress(OldAddr, NewAddr);
+ }
+}
+
+// Scatter sections in all directions!
+// Remaps section addresses for -verify mode. The following command line options
+// can be used to customize the layout of the memory within the phony target's
+// address space:
+// -target-addr-start <s> -- Specify where the phony target address range starts.
+// -target-addr-end <e> -- Specify where the phony target address range ends.
+// -target-section-sep <d> -- Specify how big a gap should be left between the
+// end of one section and the start of the next.
+// Defaults to zero. Set to something big
+// (e.g. 1 << 32) to stress-test stubs, GOTs, etc.
+//
+static void remapSectionsAndSymbols(const llvm::Triple &TargetTriple,
+ RuntimeDyld &Dyld,
+ TrivialMemoryManager &MemMgr) {
+
+ // Set up a work list (section addr/size pairs).
+ typedef std::list<const TrivialMemoryManager::SectionInfo*> WorklistT;
+ WorklistT Worklist;
+
+ for (const auto& CodeSection : MemMgr.FunctionMemory)
+ Worklist.push_back(&CodeSection);
+ for (const auto& DataSection : MemMgr.DataMemory)
+ Worklist.push_back(&DataSection);
+
+ // Keep an "already allocated" mapping of section target addresses to sizes.
+ // Sections whose address mappings aren't specified on the command line will
+ // allocated around the explicitly mapped sections while maintaining the
+ // minimum separation.
+ std::map<uint64_t, uint64_t> AlreadyAllocated;
+
+ // Move the previously applied mappings (whether explicitly specified on the
+ // command line, or implicitly set by RuntimeDyld) into the already-allocated
+ // map.
+ for (WorklistT::iterator I = Worklist.begin(), E = Worklist.end();
+ I != E;) {
+ WorklistT::iterator Tmp = I;
+ ++I;
+
+ auto LoadAddr = Dyld.getSectionLoadAddress((*Tmp)->SectionID);
+
+ if (LoadAddr != static_cast<uint64_t>(
+ reinterpret_cast<uintptr_t>((*Tmp)->MB.base()))) {
+ // 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)->MB.allocatedSize();
+ Worklist.erase(Tmp);
+ }
+ }
+
+ // If the -target-addr-end option wasn't explicitly passed, then set it to a
+ // sensible default based on the target triple.
+ if (TargetAddrEnd.getNumOccurrences() == 0) {
+ if (TargetTriple.isArch16Bit())
+ TargetAddrEnd = (1ULL << 16) - 1;
+ else if (TargetTriple.isArch32Bit())
+ TargetAddrEnd = (1ULL << 32) - 1;
+ // TargetAddrEnd already has a sensible default for 64-bit systems, so
+ // there's nothing to do in the 64-bit case.
+ }
+
+ // Process any elements remaining in the worklist.
+ while (!Worklist.empty()) {
+ auto *CurEntry = Worklist.front();
+ Worklist.pop_front();
+
+ uint64_t NextSectionAddr = TargetAddrStart;
+
+ for (const auto &Alloc : AlreadyAllocated)
+ if (NextSectionAddr + CurEntry->MB.allocatedSize() + TargetSectionSep <=
+ Alloc.first)
+ break;
+ else
+ NextSectionAddr = Alloc.first + Alloc.second + TargetSectionSep;
+
+ Dyld.mapSectionAddress(CurEntry->MB.base(), NextSectionAddr);
+ AlreadyAllocated[NextSectionAddr] = CurEntry->MB.allocatedSize();
+ }
+
+ // Add dummy symbols to the memory manager.
+ for (const auto &Mapping : DummySymbolMappings) {
+ size_t EqualsIdx = Mapping.find_first_of('=');
+
+ if (EqualsIdx == StringRef::npos)
+ report_fatal_error(Twine("Invalid dummy symbol specification '") +
+ Mapping + "'. Should be '<symbol name>=<addr>'");
+
+ std::string Symbol = Mapping.substr(0, EqualsIdx);
+ std::string AddrStr = Mapping.substr(EqualsIdx + 1);
+
+ uint64_t Addr;
+ if (StringRef(AddrStr).getAsInteger(0, Addr))
+ report_fatal_error(Twine("Invalid symbol mapping '") + Mapping + "'.");
+
+ MemMgr.addDummySymbol(Symbol, Addr);
+ }
+}
+
+// Load and link the objects specified on the command line, but do not execute
+// anything. Instead, attach a RuntimeDyldChecker instance and call it to
+// verify the correctness of the linked memory.
+static int linkAndVerify() {
+
+ // Check for missing triple.
+ if (TripleName == "")
+ ErrorAndExit("-triple required when running in -verify mode.");
+
+ // Look up the target and build the disassembler.
+ Triple TheTriple(Triple::normalize(TripleName));
+ std::string ErrorStr;
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget("", TheTriple, ErrorStr);
+ if (!TheTarget)
+ ErrorAndExit("Error accessing target '" + TripleName + "': " + ErrorStr);
+
+ TripleName = TheTriple.getTriple();
+
+ std::unique_ptr<MCSubtargetInfo> STI(
+ TheTarget->createMCSubtargetInfo(TripleName, MCPU, ""));
+ if (!STI)
+ ErrorAndExit("Unable to create subtarget info!");
+
+ std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
+ if (!MRI)
+ ErrorAndExit("Unable to create target register info!");
+
+ MCTargetOptions MCOptions;
+ std::unique_ptr<MCAsmInfo> MAI(
+ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ if (!MAI)
+ ErrorAndExit("Unable to create target asm info!");
+
+ MCContext Ctx(Triple(TripleName), MAI.get(), MRI.get(), STI.get());
+
+ std::unique_ptr<MCDisassembler> Disassembler(
+ TheTarget->createMCDisassembler(*STI, Ctx));
+ if (!Disassembler)
+ ErrorAndExit("Unable to create disassembler!");
+
+ std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
+ if (!MII)
+ ErrorAndExit("Unable to create target instruction info!");
+
+ std::unique_ptr<MCInstPrinter> InstPrinter(
+ TheTarget->createMCInstPrinter(Triple(TripleName), 0, *MAI, *MII, *MRI));
+
+ // Load any dylibs requested on the command line.
+ loadDylibs();
+
+ // Instantiate a dynamic linker.
+ TrivialMemoryManager MemMgr;
+ doPreallocation(MemMgr);
+
+ struct StubID {
+ unsigned SectionID;
+ uint32_t Offset;
+ };
+ using StubInfos = StringMap<StubID>;
+ using StubContainers = StringMap<StubInfos>;
+
+ StubContainers StubMap;
+ RuntimeDyld Dyld(MemMgr, MemMgr);
+ Dyld.setProcessAllSections(true);
+
+ Dyld.setNotifyStubEmitted([&StubMap](StringRef FilePath,
+ StringRef SectionName,
+ StringRef SymbolName, unsigned SectionID,
+ uint32_t StubOffset) {
+ std::string ContainerName =
+ (sys::path::filename(FilePath) + "/" + SectionName).str();
+ StubMap[ContainerName][SymbolName] = {SectionID, StubOffset};
+ });
+
+ auto GetSymbolInfo =
+ [&Dyld, &MemMgr](
+ StringRef Symbol) -> Expected<RuntimeDyldChecker::MemoryRegionInfo> {
+ RuntimeDyldChecker::MemoryRegionInfo SymInfo;
+
+ // First get the target address.
+ if (auto InternalSymbol = Dyld.getSymbol(Symbol))
+ SymInfo.setTargetAddress(InternalSymbol.getAddress());
+ else {
+ // Symbol not found in RuntimeDyld. Fall back to external lookup.
+#ifdef _MSC_VER
+ using ExpectedLookupResult =
+ MSVCPExpected<JITSymbolResolver::LookupResult>;
+#else
+ using ExpectedLookupResult = Expected<JITSymbolResolver::LookupResult>;
+#endif
+
+ auto ResultP = std::make_shared<std::promise<ExpectedLookupResult>>();
+ auto ResultF = ResultP->get_future();
+
+ MemMgr.lookup(JITSymbolResolver::LookupSet({Symbol}),
+ [=](Expected<JITSymbolResolver::LookupResult> Result) {
+ ResultP->set_value(std::move(Result));
+ });
+
+ auto Result = ResultF.get();
+ if (!Result)
+ return Result.takeError();
+
+ auto I = Result->find(Symbol);
+ assert(I != Result->end() &&
+ "Expected symbol address if no error occurred");
+ SymInfo.setTargetAddress(I->second.getAddress());
+ }
+
+ // Now find the symbol content if possible (otherwise leave content as a
+ // default-constructed StringRef).
+ if (auto *SymAddr = Dyld.getSymbolLocalAddress(Symbol)) {
+ unsigned SectionID = Dyld.getSymbolSectionID(Symbol);
+ if (SectionID != ~0U) {
+ char *CSymAddr = static_cast<char *>(SymAddr);
+ StringRef SecContent = Dyld.getSectionContent(SectionID);
+ uint64_t SymSize = SecContent.size() - (CSymAddr - SecContent.data());
+ SymInfo.setContent(ArrayRef<char>(CSymAddr, SymSize));
+ }
+ }
+ return SymInfo;
+ };
+
+ auto IsSymbolValid = [&Dyld, GetSymbolInfo](StringRef Symbol) {
+ if (Dyld.getSymbol(Symbol))
+ return true;
+ auto SymInfo = GetSymbolInfo(Symbol);
+ if (!SymInfo) {
+ logAllUnhandledErrors(SymInfo.takeError(), errs(), "RTDyldChecker: ");
+ return false;
+ }
+ return SymInfo->getTargetAddress() != 0;
+ };
+
+ FileToSectionIDMap FileToSecIDMap;
+
+ auto GetSectionInfo = [&Dyld, &FileToSecIDMap](StringRef FileName,
+ StringRef SectionName)
+ -> Expected<RuntimeDyldChecker::MemoryRegionInfo> {
+ auto SectionID = getSectionId(FileToSecIDMap, FileName, SectionName);
+ if (!SectionID)
+ return SectionID.takeError();
+ RuntimeDyldChecker::MemoryRegionInfo SecInfo;
+ SecInfo.setTargetAddress(Dyld.getSectionLoadAddress(*SectionID));
+ StringRef SecContent = Dyld.getSectionContent(*SectionID);
+ SecInfo.setContent(ArrayRef<char>(SecContent.data(), SecContent.size()));
+ return SecInfo;
+ };
+
+ auto GetStubInfo = [&Dyld, &StubMap](StringRef StubContainer,
+ StringRef SymbolName)
+ -> Expected<RuntimeDyldChecker::MemoryRegionInfo> {
+ if (!StubMap.count(StubContainer))
+ return make_error<StringError>("Stub container not found: " +
+ StubContainer,
+ inconvertibleErrorCode());
+ if (!StubMap[StubContainer].count(SymbolName))
+ return make_error<StringError>("Symbol name " + SymbolName +
+ " in stub container " + StubContainer,
+ inconvertibleErrorCode());
+ auto &SI = StubMap[StubContainer][SymbolName];
+ RuntimeDyldChecker::MemoryRegionInfo StubMemInfo;
+ StubMemInfo.setTargetAddress(Dyld.getSectionLoadAddress(SI.SectionID) +
+ SI.Offset);
+ StringRef SecContent =
+ Dyld.getSectionContent(SI.SectionID).substr(SI.Offset);
+ StubMemInfo.setContent(
+ ArrayRef<char>(SecContent.data(), SecContent.size()));
+ return StubMemInfo;
+ };
+
+ // We will initialize this below once we have the first object file and can
+ // know the endianness.
+ std::unique_ptr<RuntimeDyldChecker> Checker;
+
+ // If we don't have any input files, read from stdin.
+ if (!InputFileList.size())
+ InputFileList.push_back("-");
+ for (auto &InputFile : InputFileList) {
+ // Load the input memory buffer.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer =
+ MemoryBuffer::getFileOrSTDIN(InputFile);
+
+ if (std::error_code EC = InputBuffer.getError())
+ ErrorAndExit("unable to read input: '" + EC.message() + "'");
+
+ Expected<std::unique_ptr<ObjectFile>> MaybeObj(
+ ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef()));
+
+ if (!MaybeObj) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(MaybeObj.takeError(), OS);
+ OS.flush();
+ ErrorAndExit("unable to create object file: '" + Buf + "'");
+ }
+
+ ObjectFile &Obj = **MaybeObj;
+
+ if (!Checker)
+ Checker = std::make_unique<RuntimeDyldChecker>(
+ IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo,
+ GetStubInfo, Obj.isLittleEndian() ? support::little : support::big,
+ Disassembler.get(), InstPrinter.get(), dbgs());
+
+ auto FileName = sys::path::filename(InputFile);
+ MemMgr.setSectionIDsMap(&FileToSecIDMap[FileName]);
+
+ // Load the object file
+ Dyld.loadObject(Obj);
+ if (Dyld.hasError()) {
+ ErrorAndExit(Dyld.getErrorString());
+ }
+ }
+
+ // Re-map the section addresses into the phony target address space and add
+ // dummy symbols.
+ applySpecificSectionMappings(Dyld, FileToSecIDMap);
+ remapSectionsAndSymbols(TheTriple, Dyld, MemMgr);
+
+ // Resolve all the relocations we can.
+ Dyld.resolveRelocations();
+
+ // Register EH frames.
+ Dyld.registerEHFrames();
+
+ int ErrorCode = checkAllExpressions(*Checker);
+ if (Dyld.hasError())
+ ErrorAndExit("RTDyld reported an error applying relocations:\n " +
+ Dyld.getErrorString());
+
+ return ErrorCode;
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ ProgramName = argv[0];
+
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllDisassemblers();
+
+ cl::HideUnrelatedOptions({&RTDyldCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "llvm MC-JIT tool\n");
+
+ ExitOnErr.setBanner(std::string(argv[0]) + ": ");
+
+ Timers = ShowTimes ? std::make_unique<RTDyldTimers>() : nullptr;
+
+ int Result = 0;
+ switch (Action) {
+ case AC_Execute:
+ Result = executeInput();
+ break;
+ case AC_PrintDebugLineInfo:
+ Result =
+ printLineInfoForInput(/* LoadObjects */ true, /* UseDebugObj */ true);
+ break;
+ case AC_PrintLineInfo:
+ Result =
+ printLineInfoForInput(/* LoadObjects */ true, /* UseDebugObj */ false);
+ break;
+ case AC_PrintObjectLineInfo:
+ Result =
+ printLineInfoForInput(/* LoadObjects */ false, /* UseDebugObj */ false);
+ break;
+ case AC_Verify:
+ Result = linkAndVerify();
+ break;
+ }
+ return Result;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-rtdyld/ya.make b/contrib/libs/llvm14/tools/llvm-rtdyld/ya.make
new file mode 100644
index 00000000000..6e1b1f71512
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rtdyld/ya.make
@@ -0,0 +1,59 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/ExecutionEngine
+ contrib/libs/llvm14/lib/ExecutionEngine/RuntimeDyld
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-rtdyld
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-rtdyld.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-size/Opts.td b/contrib/libs/llvm14/tools/llvm-size/Opts.td
new file mode 100644
index 00000000000..edae43f1abd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-size/Opts.td
@@ -0,0 +1,32 @@
+include "llvm/Option/OptParser.td"
+
+class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>;
+class FF<string name, string help> : Flag<["--"], name>, HelpText<help>;
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["--"], name #"=">,
+ HelpText<help>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def common : FF<"common", "Print common symbols in the ELF file. When using Berkeley format, this is added to bss">;
+defm format : Eq<"format", "Specify output format">;
+def help : FF<"help", "Display this help">;
+defm radix : Eq<"radix", "Print size in radix">;
+def totals : FF<"totals", "Print totals of all objects - Berkeley format only">;
+def version : FF<"version", "Display the version">;
+
+// Mach-O specific options.
+def grp_mach_o : OptionGroup<"kind">, HelpText<"OPTIONS (Mach-O specific)">;
+def arch_EQ : Joined<["--"], "arch=">, HelpText<"architecture(s) from a Mach-O file to dump">, Group<grp_mach_o>;
+def : Separate<["--", "-"], "arch">, Alias<arch_EQ>;
+def l : F<"l", "When format is darwin, use long format to include addresses and offsets">, Group<grp_mach_o>;
+
+def : F<"A", "Alias for --format">, Alias<format_EQ>, AliasArgs<["sysv"]>;
+def : F<"B", "Alias for --format">, Alias<format_EQ>, AliasArgs<["berkeley"]>;
+def : F<"d", "Alias for --radix=10">, Alias<radix_EQ>, AliasArgs<["10"]>;
+def : F<"h", "Alias for --help">, Alias<help>;
+def : F<"m", "Alias for --format">, Alias<format_EQ>, AliasArgs<["darwin"]>;
+def : F<"o", "Alias for --radix=8">, Alias<radix_EQ>, AliasArgs<["8"]>;
+def : F<"t", "Alias for --totals">, Alias<totals>;
+def : F<"x", "Alias for --radix=16">, Alias<radix_EQ>, AliasArgs<["16"]>;
diff --git a/contrib/libs/llvm14/tools/llvm-size/llvm-size.cpp b/contrib/libs/llvm14/tools/llvm-size/llvm-size.cpp
new file mode 100644
index 00000000000..ec9a4cde56b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-size/llvm-size.cpp
@@ -0,0 +1,937 @@
+//===-- llvm-size.cpp - Print the size of each object section ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is a utility that works like traditional Unix "size",
+// that is, it prints out the size of each section, and the total size of all
+// sections.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/APInt.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+using namespace object;
+
+namespace {
+using namespace llvm::opt; // for HelpHidden in Opts.inc
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class SizeOptTable : public opt::OptTable {
+public:
+ SizeOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
+};
+
+enum OutputFormatTy { berkeley, sysv, darwin };
+enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 };
+} // namespace
+
+static bool ArchAll = false;
+static std::vector<StringRef> ArchFlags;
+static bool ELFCommons;
+static OutputFormatTy OutputFormat;
+static bool DarwinLongFormat;
+static RadixTy Radix;
+static bool TotalSizes;
+
+static std::vector<std::string> InputFilenames;
+
+static std::string ToolName;
+
+// States
+static bool HadError = false;
+static bool BerkeleyHeaderPrinted = false;
+static bool MoreThanOneFile = false;
+static uint64_t TotalObjectText = 0;
+static uint64_t TotalObjectData = 0;
+static uint64_t TotalObjectBss = 0;
+static uint64_t TotalObjectTotal = 0;
+
+static void error(const Twine &Message, StringRef File = "") {
+ HadError = true;
+ if (File.empty())
+ WithColor::error(errs(), ToolName) << Message << '\n';
+ else
+ WithColor::error(errs(), ToolName)
+ << "'" << File << "': " << Message << '\n';
+}
+
+// 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.
+static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ 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
+ // the index of which archive member this is and or its offset in the
+ // archive instead of "???" as the name.
+ if (!NameOrErr) {
+ consumeError(NameOrErr.takeError());
+ errs() << "(" << "???" << ")";
+ } else
+ errs() << "(" << NameOrErr.get() << ")";
+
+ if (!ArchitectureName.empty())
+ errs() << " (for architecture " << ArchitectureName << ") ";
+
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(std::move(E), OS);
+ OS.flush();
+ errs() << ": " << Buf << "\n";
+}
+
+// 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.
+static void error(llvm::Error E, StringRef FileName,
+ StringRef ArchitectureName = StringRef()) {
+ HadError = true;
+ 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);
+ OS.flush();
+ errs() << ": " << Buf << "\n";
+}
+
+/// Get the length of the string that represents @p num in Radix including the
+/// leading 0x or 0 for hexadecimal and octal respectively.
+static size_t getNumLengthAsString(uint64_t num) {
+ APInt conv(64, num);
+ SmallString<32> result;
+ conv.toString(result, Radix, false, true);
+ return result.size();
+}
+
+/// Return the printing format for the Radix.
+static const char *getRadixFmt() {
+ switch (Radix) {
+ case octal:
+ return PRIo64;
+ case decimal:
+ return PRIu64;
+ case hexadecimal:
+ return PRIx64;
+ }
+ return nullptr;
+}
+
+/// Remove unneeded ELF sections from calculation
+static bool considerForSize(ObjectFile *Obj, SectionRef Section) {
+ if (!Obj->isELF())
+ return true;
+ switch (static_cast<ELFSectionRef>(Section).getType()) {
+ case ELF::SHT_NULL:
+ case ELF::SHT_SYMTAB:
+ return false;
+ case ELF::SHT_STRTAB:
+ case ELF::SHT_REL:
+ case ELF::SHT_RELA:
+ return static_cast<ELFSectionRef>(Section).getFlags() & ELF::SHF_ALLOC;
+ }
+ return true;
+}
+
+/// Total size of all ELF common symbols
+static Expected<uint64_t> getCommonSize(ObjectFile *Obj) {
+ uint64_t TotalCommons = 0;
+ for (auto &Sym : Obj->symbols()) {
+ Expected<uint32_t> SymFlagsOrErr =
+ Obj->getSymbolFlags(Sym.getRawDataRefImpl());
+ if (!SymFlagsOrErr)
+ return SymFlagsOrErr.takeError();
+ if (*SymFlagsOrErr & SymbolRef::SF_Common)
+ TotalCommons += Obj->getCommonSymbolSize(Sym.getRawDataRefImpl());
+ }
+ return TotalCommons;
+}
+
+/// Print the size of each Mach-O segment and section in @p MachO.
+///
+/// This is when used when @c OutputFormat is darwin and produces the same
+/// output as darwin's size(1) -m output.
+static void printDarwinSectionSizes(MachOObjectFile *MachO) {
+ std::string fmtbuf;
+ raw_string_ostream fmt(fmtbuf);
+ const char *radix_fmt = getRadixFmt();
+ if (Radix == hexadecimal)
+ fmt << "0x";
+ fmt << "%" << radix_fmt;
+
+ uint32_t Filetype = MachO->getHeader().filetype;
+
+ uint64_t total = 0;
+ for (const auto &Load : MachO->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load);
+ outs() << "Segment " << Seg.segname << ": "
+ << format(fmt.str().c_str(), Seg.vmsize);
+ if (DarwinLongFormat)
+ outs() << " (vmaddr 0x" << format("%" PRIx64, Seg.vmaddr) << " fileoff "
+ << Seg.fileoff << ")";
+ outs() << "\n";
+ total += Seg.vmsize;
+ uint64_t sec_total = 0;
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section_64 Sec = MachO->getSection64(Load, J);
+ if (Filetype == MachO::MH_OBJECT)
+ outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", "
+ << format("%.16s", &Sec.sectname) << "): ";
+ else
+ outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": ";
+ outs() << format(fmt.str().c_str(), Sec.size);
+ if (DarwinLongFormat)
+ outs() << " (addr 0x" << format("%" PRIx64, Sec.addr) << " offset "
+ << Sec.offset << ")";
+ outs() << "\n";
+ sec_total += Sec.size;
+ }
+ if (Seg.nsects != 0)
+ outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n";
+ } else if (Load.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load);
+ uint64_t Seg_vmsize = Seg.vmsize;
+ outs() << "Segment " << Seg.segname << ": "
+ << format(fmt.str().c_str(), Seg_vmsize);
+ if (DarwinLongFormat)
+ outs() << " (vmaddr 0x" << format("%" PRIx32, Seg.vmaddr) << " fileoff "
+ << Seg.fileoff << ")";
+ outs() << "\n";
+ total += Seg.vmsize;
+ uint64_t sec_total = 0;
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section Sec = MachO->getSection(Load, J);
+ if (Filetype == MachO::MH_OBJECT)
+ outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", "
+ << format("%.16s", &Sec.sectname) << "): ";
+ else
+ outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": ";
+ uint64_t Sec_size = Sec.size;
+ outs() << format(fmt.str().c_str(), Sec_size);
+ if (DarwinLongFormat)
+ outs() << " (addr 0x" << format("%" PRIx32, Sec.addr) << " offset "
+ << Sec.offset << ")";
+ outs() << "\n";
+ sec_total += Sec.size;
+ }
+ if (Seg.nsects != 0)
+ outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n";
+ }
+ }
+ outs() << "total " << format(fmt.str().c_str(), total) << "\n";
+}
+
+/// Print the summary sizes of the standard Mach-O segments in @p MachO.
+///
+/// This is when used when @c OutputFormat is berkeley with a Mach-O file and
+/// produces the same output as darwin's size(1) default output.
+static void printDarwinSegmentSizes(MachOObjectFile *MachO) {
+ uint64_t total_text = 0;
+ uint64_t total_data = 0;
+ uint64_t total_objc = 0;
+ uint64_t total_others = 0;
+ for (const auto &Load : MachO->load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load);
+ if (MachO->getHeader().filetype == MachO::MH_OBJECT) {
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section_64 Sec = MachO->getSection64(Load, J);
+ StringRef SegmentName = StringRef(Sec.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Sec.size;
+ else if (SegmentName == "__DATA")
+ total_data += Sec.size;
+ else if (SegmentName == "__OBJC")
+ total_objc += Sec.size;
+ else
+ total_others += Sec.size;
+ }
+ } else {
+ StringRef SegmentName = StringRef(Seg.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Seg.vmsize;
+ else if (SegmentName == "__DATA")
+ total_data += Seg.vmsize;
+ else if (SegmentName == "__OBJC")
+ total_objc += Seg.vmsize;
+ else
+ total_others += Seg.vmsize;
+ }
+ } else if (Load.C.cmd == MachO::LC_SEGMENT) {
+ MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load);
+ if (MachO->getHeader().filetype == MachO::MH_OBJECT) {
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section Sec = MachO->getSection(Load, J);
+ StringRef SegmentName = StringRef(Sec.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Sec.size;
+ else if (SegmentName == "__DATA")
+ total_data += Sec.size;
+ else if (SegmentName == "__OBJC")
+ total_objc += Sec.size;
+ else
+ total_others += Sec.size;
+ }
+ } else {
+ StringRef SegmentName = StringRef(Seg.segname);
+ if (SegmentName == "__TEXT")
+ total_text += Seg.vmsize;
+ else if (SegmentName == "__DATA")
+ total_data += Seg.vmsize;
+ else if (SegmentName == "__OBJC")
+ total_objc += Seg.vmsize;
+ else
+ total_others += Seg.vmsize;
+ }
+ }
+ }
+ uint64_t total = total_text + total_data + total_objc + total_others;
+
+ if (!BerkeleyHeaderPrinted) {
+ outs() << "__TEXT\t__DATA\t__OBJC\tothers\tdec\thex\n";
+ BerkeleyHeaderPrinted = true;
+ }
+ outs() << total_text << "\t" << total_data << "\t" << total_objc << "\t"
+ << total_others << "\t" << total << "\t" << format("%" PRIx64, total)
+ << "\t";
+}
+
+/// Print the size of each section in @p Obj.
+///
+/// The format used is determined by @c OutputFormat and @c Radix.
+static void printObjectSectionSizes(ObjectFile *Obj) {
+ uint64_t total = 0;
+ std::string fmtbuf;
+ raw_string_ostream fmt(fmtbuf);
+ const char *radix_fmt = getRadixFmt();
+
+ // If OutputFormat is darwin and we have a MachOObjectFile print as darwin's
+ // size(1) -m output, else if OutputFormat is darwin and not a Mach-O object
+ // let it fall through to OutputFormat berkeley.
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Obj);
+ if (OutputFormat == darwin && MachO)
+ printDarwinSectionSizes(MachO);
+ // If we have a MachOObjectFile and the OutputFormat is berkeley print as
+ // darwin's default berkeley format for Mach-O files.
+ else if (MachO && OutputFormat == berkeley)
+ printDarwinSegmentSizes(MachO);
+ else if (OutputFormat == sysv) {
+ // Run two passes over all sections. The first gets the lengths needed for
+ // formatting the output. The second actually does the output.
+ std::size_t max_name_len = strlen("section");
+ std::size_t max_size_len = strlen("size");
+ std::size_t max_addr_len = strlen("addr");
+ for (const SectionRef &Section : Obj->sections()) {
+ if (!considerForSize(Obj, Section))
+ continue;
+ uint64_t size = Section.getSize();
+ total += size;
+
+ Expected<StringRef> name_or_err = Section.getName();
+ if (!name_or_err) {
+ error(name_or_err.takeError(), Obj->getFileName());
+ return;
+ }
+
+ uint64_t addr = Section.getAddress();
+ max_name_len = std::max(max_name_len, name_or_err->size());
+ max_size_len = std::max(max_size_len, getNumLengthAsString(size));
+ max_addr_len = std::max(max_addr_len, getNumLengthAsString(addr));
+ }
+
+ // Add extra padding.
+ max_name_len += 2;
+ max_size_len += 2;
+ max_addr_len += 2;
+
+ // Setup header format.
+ fmt << "%-" << max_name_len << "s "
+ << "%" << max_size_len << "s "
+ << "%" << max_addr_len << "s\n";
+
+ // Print header
+ outs() << format(fmt.str().c_str(), static_cast<const char *>("section"),
+ static_cast<const char *>("size"),
+ static_cast<const char *>("addr"));
+ fmtbuf.clear();
+
+ // Setup per section format.
+ fmt << "%-" << max_name_len << "s "
+ << "%#" << max_size_len << radix_fmt << " "
+ << "%#" << max_addr_len << radix_fmt << "\n";
+
+ // Print each section.
+ for (const SectionRef &Section : Obj->sections()) {
+ if (!considerForSize(Obj, Section))
+ continue;
+
+ Expected<StringRef> name_or_err = Section.getName();
+ if (!name_or_err) {
+ error(name_or_err.takeError(), Obj->getFileName());
+ return;
+ }
+
+ uint64_t size = Section.getSize();
+ uint64_t addr = Section.getAddress();
+ outs() << format(fmt.str().c_str(), name_or_err->str().c_str(), size, addr);
+ }
+
+ if (ELFCommons) {
+ if (Expected<uint64_t> CommonSizeOrErr = getCommonSize(Obj)) {
+ total += *CommonSizeOrErr;
+ outs() << format(fmt.str().c_str(), std::string("*COM*").c_str(),
+ *CommonSizeOrErr, static_cast<uint64_t>(0));
+ } else {
+ error(CommonSizeOrErr.takeError(), Obj->getFileName());
+ return;
+ }
+ }
+
+ // Print total.
+ fmtbuf.clear();
+ fmt << "%-" << max_name_len << "s "
+ << "%#" << max_size_len << radix_fmt << "\n";
+ outs() << format(fmt.str().c_str(), static_cast<const char *>("Total"),
+ total)
+ << "\n\n";
+ } else {
+ // The Berkeley format does not display individual section sizes. It
+ // displays the cumulative size for each section type.
+ uint64_t total_text = 0;
+ uint64_t total_data = 0;
+ uint64_t total_bss = 0;
+
+ // Make one pass over the section table to calculate sizes.
+ for (const SectionRef &Section : Obj->sections()) {
+ uint64_t size = Section.getSize();
+ bool isText = Section.isBerkeleyText();
+ bool isData = Section.isBerkeleyData();
+ bool isBSS = Section.isBSS();
+ if (isText)
+ total_text += size;
+ else if (isData)
+ total_data += size;
+ else if (isBSS)
+ total_bss += size;
+ }
+
+ if (ELFCommons) {
+ if (Expected<uint64_t> CommonSizeOrErr = getCommonSize(Obj))
+ total_bss += *CommonSizeOrErr;
+ else {
+ error(CommonSizeOrErr.takeError(), Obj->getFileName());
+ return;
+ }
+ }
+
+ total = total_text + total_data + total_bss;
+
+ if (TotalSizes) {
+ TotalObjectText += total_text;
+ TotalObjectData += total_data;
+ TotalObjectBss += total_bss;
+ TotalObjectTotal += total;
+ }
+
+ if (!BerkeleyHeaderPrinted) {
+ outs() << " text\t"
+ " data\t"
+ " bss\t"
+ " "
+ << (Radix == octal ? "oct" : "dec")
+ << "\t"
+ " hex\t"
+ "filename\n";
+ BerkeleyHeaderPrinted = true;
+ }
+
+ // Print result.
+ 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) << "\t"
+ << "%7" PRIx64 "\t";
+ outs() << format(fmt.str().c_str(), total, total);
+ }
+}
+
+/// Checks to see if the @p O ObjectFile is a Mach-O file and if it is and there
+/// is a list of architecture flags specified then check to make sure this
+/// Mach-O file is one of those architectures or all architectures was
+/// specificed. If not then an error is generated and this routine returns
+/// false. Else it returns true.
+static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) {
+ auto *MachO = dyn_cast<MachOObjectFile>(O);
+
+ if (!MachO || ArchAll || ArchFlags.empty())
+ return true;
+
+ MachO::mach_header H;
+ MachO::mach_header_64 H_64;
+ Triple T;
+ if (MachO->is64Bit()) {
+ H_64 = MachO->MachOObjectFile::getHeader64();
+ T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype);
+ } else {
+ H = MachO->MachOObjectFile::getHeader();
+ T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype);
+ }
+ if (!is_contained(ArchFlags, T.getArchName())) {
+ error("no architecture specified", Filename);
+ return false;
+ }
+ return true;
+}
+
+/// Print the section sizes for @p file. If @p file is an archive, print the
+/// section sizes for each archive member.
+static void printFileSectionSizes(StringRef file) {
+
+ // Attempt to open the binary.
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
+ if (!BinaryOrErr) {
+ error(BinaryOrErr.takeError(), file);
+ return;
+ }
+ Binary &Bin = *BinaryOrErr.get().getBinary();
+
+ if (Archive *a = dyn_cast<Archive>(&Bin)) {
+ // This is an archive. Iterate over each member and display its sizes.
+ 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()))
+ error(std::move(E), a->getFileName(), C);
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (!checkMachOAndArchFlags(o, file))
+ return;
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << a->getFileName() << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << a->getFileName() << "(" << o->getFileName() << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO)
+ outs() << a->getFileName() << "(" << o->getFileName() << ")\n";
+ else
+ outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), a->getFileName());
+ } else if (MachOUniversalBinary *UB =
+ dyn_cast<MachOUniversalBinary>(&Bin)) {
+ // 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 (ArchFlags[i] == I->getArchFlagName()) {
+ ArchFound = true;
+ Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
+ if (UO) {
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin) {
+ if (MoreThanOneFile || ArchFlags.size() > 1)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << "): \n";
+ }
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile || ArchFlags.size() > 1)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << ")";
+ outs() << "\n";
+ }
+ }
+ } else if (auto E = isNotObjectErrorInvalidFileType(
+ UO.takeError())) {
+ error(std::move(E), file, ArchFlags.size() > 1 ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &UA = *AOrErr;
+ // This is an archive. Iterate over each member and display its
+ // sizes.
+ Error Err = Error::success();
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C,
+ ArchFlags.size() > 1 ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << UA->getFileName() << "(" << o->getFileName()
+ << ")"
+ << " (for architecture " << I->getArchFlagName()
+ << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO) {
+ outs() << UA->getFileName() << "(" << o->getFileName()
+ << ")";
+ if (ArchFlags.size() > 1)
+ outs() << " (for architecture " << I->getArchFlagName()
+ << ")";
+ outs() << "\n";
+ } else
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("mach-o universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a mach-o file or an archive file",
+ file);
+ }
+ }
+ }
+ if (!ArchFound) {
+ error("file does not contain architecture " + ArchFlags[i], file);
+ return;
+ }
+ }
+ return;
+ }
+ // 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();
+ for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
+ E = UB->end_objects();
+ I != E; ++I) {
+ if (HostArchName == I->getArchFlagName()) {
+ Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
+ if (UO) {
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin) {
+ if (MoreThanOneFile)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << "):\n";
+ }
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << ")";
+ outs() << "\n";
+ }
+ }
+ } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
+ error(std::move(E), file);
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &UA = *AOrErr;
+ // This is an archive. Iterate over each member and display its
+ // sizes.
+ Error Err = Error::success();
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C);
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << UA->getFileName() << "(" << o->getFileName() << ")"
+ << " (for architecture " << I->getArchFlagName()
+ << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO)
+ outs() << UA->getFileName() << "(" << o->getFileName()
+ << ")\n";
+ else
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("mach-o universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a mach-o file or an archive file",
+ file);
+ }
+ 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>> UO = I->getAsObjectFile();
+ if (UO) {
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin) {
+ if (MoreThanOneFile || MoreThanOneArch)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << "):";
+ outs() << "\n";
+ }
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile || MoreThanOneArch)
+ outs() << o->getFileName() << " (for architecture "
+ << I->getArchFlagName() << ")";
+ outs() << "\n";
+ }
+ }
+ } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
+ error(std::move(E), file, MoreThanOneArch ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ return;
+ } else if (Expected<std::unique_ptr<Archive>> AOrErr =
+ I->getAsArchive()) {
+ std::unique_ptr<Archive> &UA = *AOrErr;
+ // This is an archive. Iterate over each member and display its sizes.
+ Error Err = Error::success();
+ for (auto &C : UA->children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+ if (!ChildOrErr) {
+ if (auto E = isNotObjectErrorInvalidFileType(
+ ChildOrErr.takeError()))
+ error(std::move(E), UA->getFileName(), C, MoreThanOneArch ?
+ StringRef(I->getArchFlagName()) : StringRef());
+ continue;
+ }
+ if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << "):\n";
+ else if (MachO && OutputFormat == darwin)
+ outs() << UA->getFileName() << "(" << o->getFileName() << ")"
+ << " (for architecture " << I->getArchFlagName() << "):\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (MachO)
+ outs() << UA->getFileName() << "(" << o->getFileName() << ")"
+ << " (for architecture " << I->getArchFlagName()
+ << ")\n";
+ else
+ outs() << o->getFileName() << " (ex " << UA->getFileName()
+ << ")\n";
+ }
+ }
+ }
+ if (Err)
+ error(std::move(Err), UA->getFileName());
+ } else {
+ consumeError(AOrErr.takeError());
+ error("mach-o universal file for architecture " +
+ StringRef(I->getArchFlagName()) +
+ " is not a mach-o file or an archive file",
+ file);
+ }
+ }
+ } else if (ObjectFile *o = dyn_cast<ObjectFile>(&Bin)) {
+ if (!checkMachOAndArchFlags(o, file))
+ return;
+ MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
+ if (OutputFormat == sysv)
+ outs() << o->getFileName() << " :\n";
+ else if (MachO && OutputFormat == darwin && MoreThanOneFile)
+ outs() << o->getFileName() << ":\n";
+ printObjectSectionSizes(o);
+ if (OutputFormat == berkeley) {
+ if (!MachO || MoreThanOneFile)
+ outs() << o->getFileName();
+ outs() << "\n";
+ }
+ } else {
+ error("unsupported file type", file);
+ }
+}
+
+static void printBerkeleyTotals() {
+ std::string fmtbuf;
+ raw_string_ostream fmt(fmtbuf);
+ const char *radix_fmt = getRadixFmt();
+ 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) << "\t"
+ << "%7" PRIx64 "\t";
+ outs() << format(fmt.str().c_str(), TotalObjectTotal, TotalObjectTotal)
+ << "(TOTALS)\n";
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ SizeOptTable Tbl;
+ ToolName = argv[0];
+ opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver,
+ [&](StringRef Msg) { error(Msg); });
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(
+ outs(),
+ (Twine(ToolName) + " [options] <input object files>").str().c_str(),
+ "LLVM object size dumper");
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ return 0;
+ }
+ if (Args.hasArg(OPT_version)) {
+ outs() << ToolName << '\n';
+ cl::PrintVersionMessage();
+ return 0;
+ }
+
+ ELFCommons = Args.hasArg(OPT_common);
+ DarwinLongFormat = Args.hasArg(OPT_l);
+ TotalSizes = Args.hasArg(OPT_totals);
+ StringRef V = Args.getLastArgValue(OPT_format_EQ, "berkeley");
+ if (V == "berkeley")
+ OutputFormat = berkeley;
+ else if (V == "darwin")
+ OutputFormat = darwin;
+ else if (V == "sysv")
+ OutputFormat = sysv;
+ else
+ error("--format value should be one of: 'berkeley', 'darwin', 'sysv'");
+ V = Args.getLastArgValue(OPT_radix_EQ, "10");
+ if (V == "8")
+ Radix = RadixTy::octal;
+ else if (V == "10")
+ Radix = RadixTy::decimal;
+ else if (V == "16")
+ Radix = RadixTy::hexadecimal;
+ else
+ error("--radix value should be one of: 8, 10, 16 ");
+
+ for (const auto *A : Args.filtered(OPT_arch_EQ)) {
+ SmallVector<StringRef, 2> Values;
+ llvm::SplitString(A->getValue(), Values, ",");
+ for (StringRef V : Values) {
+ if (V == "all")
+ ArchAll = true;
+ else if (MachOObjectFile::isValidArch(V))
+ ArchFlags.push_back(V);
+ else {
+ outs() << ToolName << ": for the -arch option: Unknown architecture "
+ << "named '" << V << "'";
+ return 1;
+ }
+ }
+ }
+
+ InputFilenames = Args.getAllArgValues(OPT_INPUT);
+ if (InputFilenames.empty())
+ InputFilenames.push_back("a.out");
+
+ MoreThanOneFile = InputFilenames.size() > 1;
+ llvm::for_each(InputFilenames, printFileSectionSizes);
+ if (OutputFormat == berkeley && TotalSizes)
+ printBerkeleyTotals();
+
+ if (HadError)
+ return 1;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-size/ya.make b/contrib/libs/llvm14/tools/llvm-size/ya.make
new file mode 100644
index 00000000000..2b4c6047513
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-size/ya.make
@@ -0,0 +1,39 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-size
+ contrib/libs/llvm14/tools/llvm-size
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-size.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-split/llvm-split.cpp b/contrib/libs/llvm14/tools/llvm-split/llvm-split.cpp
new file mode 100644
index 00000000000..c6e20e0373c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-split/llvm-split.cpp
@@ -0,0 +1,88 @@
+//===-- llvm-split: command line tool for testing module splitter ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program can be used to test the llvm::SplitModule function.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Transforms/Utils/SplitModule.h"
+
+using namespace llvm;
+
+static cl::OptionCategory SplitCategory("Split Options");
+
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input bitcode file>"),
+ cl::init("-"),
+ cl::value_desc("filename"),
+ cl::cat(SplitCategory));
+
+static cl::opt<std::string> OutputFilename("o",
+ cl::desc("Override output filename"),
+ cl::value_desc("filename"),
+ cl::cat(SplitCategory));
+
+static cl::opt<unsigned> NumOutputs("j", cl::Prefix, cl::init(2),
+ cl::desc("Number of output files"),
+ cl::cat(SplitCategory));
+
+static cl::opt<bool>
+ PreserveLocals("preserve-locals", cl::Prefix, cl::init(false),
+ cl::desc("Split without externalizing locals"),
+ cl::cat(SplitCategory));
+
+int main(int argc, char **argv) {
+ LLVMContext Context;
+ SMDiagnostic Err;
+ cl::HideUnrelatedOptions({&SplitCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "LLVM module splitter\n");
+
+ std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context);
+
+ if (!M) {
+ Err.print(argv[0], errs());
+ return 1;
+ }
+
+ unsigned I = 0;
+ SplitModule(
+ *M, NumOutputs,
+ [&](std::unique_ptr<Module> MPart) {
+ std::error_code EC;
+ std::unique_ptr<ToolOutputFile> Out(new ToolOutputFile(
+ OutputFilename + utostr(I++), EC, sys::fs::OF_None));
+ if (EC) {
+ errs() << EC.message() << '\n';
+ exit(1);
+ }
+
+ if (verifyModule(*MPart, &errs())) {
+ errs() << "Broken module!\n";
+ exit(1);
+ }
+
+ WriteBitcodeToFile(*MPart, Out->os());
+
+ // Declare success.
+ Out->keep();
+ },
+ PreserveLocals);
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-split/ya.make b/contrib/libs/llvm14/tools/llvm-split/ya.make
new file mode 100644
index 00000000000..fd95436766c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-split/ya.make
@@ -0,0 +1,44 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/Utils
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-split
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-split.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-stress/llvm-stress.cpp b/contrib/libs/llvm14/tools/llvm-stress/llvm-stress.cpp
new file mode 100644
index 00000000000..9135d60fdf9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-stress/llvm-stress.cpp
@@ -0,0 +1,778 @@
+//===- llvm-stress.cpp - Generate random LL files to stress-test LLVM -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is a utility that generates random .ll files to stress-test
+// different components in LLVM.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CallingConv.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalValue.h"
+#include "llvm/IR/IRPrintingPasses.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <vector>
+
+namespace llvm {
+
+static cl::OptionCategory StressCategory("Stress Options");
+
+static cl::opt<unsigned> SeedCL("seed", cl::desc("Seed used for randomness"),
+ cl::init(0), cl::cat(StressCategory));
+
+static cl::opt<unsigned> SizeCL(
+ "size",
+ cl::desc("The estimated size of the generated function (# of instrs)"),
+ cl::init(100), cl::cat(StressCategory));
+
+static cl::opt<std::string> OutputFilename("o",
+ cl::desc("Override output filename"),
+ cl::value_desc("filename"),
+ cl::cat(StressCategory));
+
+static LLVMContext Context;
+
+namespace cl {
+
+template <> class parser<Type*> final : public basic_parser<Type*> {
+public:
+ parser(Option &O) : basic_parser(O) {}
+
+ // Parse options as IR types. Return true on error.
+ bool parse(Option &O, StringRef, StringRef Arg, Type *&Value) {
+ if (Arg == "half") Value = Type::getHalfTy(Context);
+ else if (Arg == "fp128") Value = Type::getFP128Ty(Context);
+ else if (Arg == "x86_fp80") Value = Type::getX86_FP80Ty(Context);
+ else if (Arg == "ppc_fp128") Value = Type::getPPC_FP128Ty(Context);
+ else if (Arg == "x86_mmx") Value = Type::getX86_MMXTy(Context);
+ else if (Arg.startswith("i")) {
+ unsigned N = 0;
+ Arg.drop_front().getAsInteger(10, N);
+ if (N > 0)
+ Value = Type::getIntNTy(Context, N);
+ }
+
+ if (!Value)
+ return O.error("Invalid IR scalar type: '" + Arg + "'!");
+ return false;
+ }
+
+ StringRef getValueName() const override { return "IR scalar type"; }
+};
+
+} // end namespace cl
+
+static cl::list<Type*> AdditionalScalarTypes("types", cl::CommaSeparated,
+ cl::desc("Additional IR scalar types "
+ "(always includes i1, i8, i16, i32, i64, float and double)"));
+
+namespace {
+
+/// A utility class to provide a pseudo-random number generator which is
+/// the same across all platforms. This is somewhat close to the libc
+/// implementation. Note: This is not a cryptographically secure pseudorandom
+/// number generator.
+class Random {
+public:
+ /// C'tor
+ Random(unsigned _seed):Seed(_seed) {}
+
+ /// Return a random integer, up to a
+ /// maximum of 2**19 - 1.
+ uint32_t Rand() {
+ uint32_t Val = Seed + 0x000b07a1;
+ Seed = (Val * 0x3c7c0ac1);
+ // Only lowest 19 bits are random-ish.
+ return Seed & 0x7ffff;
+ }
+
+ /// Return a random 64 bit integer.
+ uint64_t Rand64() {
+ uint64_t Val = Rand() & 0xffff;
+ Val |= uint64_t(Rand() & 0xffff) << 16;
+ Val |= uint64_t(Rand() & 0xffff) << 32;
+ Val |= uint64_t(Rand() & 0xffff) << 48;
+ return Val;
+ }
+
+ /// Rand operator for STL algorithms.
+ ptrdiff_t operator()(ptrdiff_t y) {
+ return Rand64() % y;
+ }
+
+ /// Make this like a C++11 random device
+ using result_type = uint32_t ;
+
+ static constexpr result_type min() { return 0; }
+ static constexpr result_type max() { return 0x7ffff; }
+
+ uint32_t operator()() {
+ uint32_t Val = Rand();
+ assert(Val <= max() && "Random value out of range");
+ return Val;
+ }
+
+private:
+ unsigned Seed;
+};
+
+/// Generate an empty function with a default argument list.
+Function *GenEmptyFunction(Module *M) {
+ // Define a few arguments
+ LLVMContext &Context = M->getContext();
+ Type* ArgsTy[] = {
+ Type::getInt8PtrTy(Context),
+ Type::getInt32PtrTy(Context),
+ Type::getInt64PtrTy(Context),
+ Type::getInt32Ty(Context),
+ Type::getInt64Ty(Context),
+ Type::getInt8Ty(Context)
+ };
+
+ auto *FuncTy = FunctionType::get(Type::getVoidTy(Context), ArgsTy, false);
+ // Pick a unique name to describe the input parameters
+ Twine Name = "autogen_SD" + Twine{SeedCL};
+ auto *Func = Function::Create(FuncTy, GlobalValue::ExternalLinkage, Name, M);
+ Func->setCallingConv(CallingConv::C);
+ return Func;
+}
+
+/// A base class, implementing utilities needed for
+/// modifying and adding new random instructions.
+struct Modifier {
+ /// Used to store the randomly generated values.
+ using PieceTable = std::vector<Value *>;
+
+public:
+ /// C'tor
+ Modifier(BasicBlock *Block, PieceTable *PT, Random *R)
+ : BB(Block), PT(PT), Ran(R), Context(BB->getContext()) {}
+
+ /// virtual D'tor to silence warnings.
+ virtual ~Modifier() = default;
+
+ /// Add a new instruction.
+ virtual void Act() = 0;
+
+ /// Add N new instructions,
+ virtual void ActN(unsigned n) {
+ for (unsigned i=0; i<n; ++i)
+ Act();
+ }
+
+protected:
+ /// Return a random integer.
+ uint32_t getRandom() {
+ return Ran->Rand();
+ }
+
+ /// Return a random value from the list of known values.
+ Value *getRandomVal() {
+ assert(PT->size());
+ return PT->at(getRandom() % PT->size());
+ }
+
+ Constant *getRandomConstant(Type *Tp) {
+ if (Tp->isIntegerTy()) {
+ if (getRandom() & 1)
+ return ConstantInt::getAllOnesValue(Tp);
+ return ConstantInt::getNullValue(Tp);
+ } else if (Tp->isFloatingPointTy()) {
+ if (getRandom() & 1)
+ return ConstantFP::getAllOnesValue(Tp);
+ return ConstantFP::getNullValue(Tp);
+ }
+ return UndefValue::get(Tp);
+ }
+
+ /// Return a random value with a known type.
+ Value *getRandomValue(Type *Tp) {
+ unsigned index = getRandom();
+ for (unsigned i=0; i<PT->size(); ++i) {
+ Value *V = PT->at((index + i) % PT->size());
+ if (V->getType() == Tp)
+ return V;
+ }
+
+ // If the requested type was not found, generate a constant value.
+ if (Tp->isIntegerTy()) {
+ if (getRandom() & 1)
+ return ConstantInt::getAllOnesValue(Tp);
+ return ConstantInt::getNullValue(Tp);
+ } else if (Tp->isFloatingPointTy()) {
+ if (getRandom() & 1)
+ return ConstantFP::getAllOnesValue(Tp);
+ return ConstantFP::getNullValue(Tp);
+ } else if (Tp->isVectorTy()) {
+ auto *VTp = cast<FixedVectorType>(Tp);
+
+ std::vector<Constant*> TempValues;
+ TempValues.reserve(VTp->getNumElements());
+ for (unsigned i = 0; i < VTp->getNumElements(); ++i)
+ TempValues.push_back(getRandomConstant(VTp->getScalarType()));
+
+ ArrayRef<Constant*> VectorValue(TempValues);
+ return ConstantVector::get(VectorValue);
+ }
+
+ return UndefValue::get(Tp);
+ }
+
+ /// Return a random value of any pointer type.
+ Value *getRandomPointerValue() {
+ unsigned index = getRandom();
+ for (unsigned i=0; i<PT->size(); ++i) {
+ Value *V = PT->at((index + i) % PT->size());
+ if (V->getType()->isPointerTy())
+ return V;
+ }
+ return UndefValue::get(pickPointerType());
+ }
+
+ /// Return a random value of any vector type.
+ Value *getRandomVectorValue() {
+ unsigned index = getRandom();
+ for (unsigned i=0; i<PT->size(); ++i) {
+ Value *V = PT->at((index + i) % PT->size());
+ if (V->getType()->isVectorTy())
+ return V;
+ }
+ return UndefValue::get(pickVectorType());
+ }
+
+ /// Pick a random type.
+ Type *pickType() {
+ return (getRandom() & 1) ? pickVectorType() : pickScalarType();
+ }
+
+ /// Pick a random pointer type.
+ Type *pickPointerType() {
+ Type *Ty = pickType();
+ return PointerType::get(Ty, 0);
+ }
+
+ /// Pick a random vector type.
+ Type *pickVectorType(unsigned len = (unsigned)-1) {
+ // Pick a random vector width in the range 2**0 to 2**4.
+ // by adding two randoms we are generating a normal-like distribution
+ // around 2**3.
+ unsigned width = 1<<((getRandom() % 3) + (getRandom() % 3));
+ Type *Ty;
+
+ // Vectors of x86mmx are illegal; keep trying till we get something else.
+ do {
+ Ty = pickScalarType();
+ } while (Ty->isX86_MMXTy());
+
+ if (len != (unsigned)-1)
+ width = len;
+ return FixedVectorType::get(Ty, width);
+ }
+
+ /// Pick a random scalar type.
+ Type *pickScalarType() {
+ static std::vector<Type*> ScalarTypes;
+ if (ScalarTypes.empty()) {
+ ScalarTypes.assign({
+ Type::getInt1Ty(Context),
+ Type::getInt8Ty(Context),
+ Type::getInt16Ty(Context),
+ Type::getInt32Ty(Context),
+ Type::getInt64Ty(Context),
+ Type::getFloatTy(Context),
+ Type::getDoubleTy(Context)
+ });
+ llvm::append_range(ScalarTypes, AdditionalScalarTypes);
+ }
+
+ return ScalarTypes[getRandom() % ScalarTypes.size()];
+ }
+
+ /// Basic block to populate
+ BasicBlock *BB;
+
+ /// Value table
+ PieceTable *PT;
+
+ /// Random number generator
+ Random *Ran;
+
+ /// Context
+ LLVMContext &Context;
+};
+
+struct LoadModifier: public Modifier {
+ LoadModifier(BasicBlock *BB, PieceTable *PT, Random *R)
+ : Modifier(BB, PT, R) {}
+
+ void Act() override {
+ // Try to use predefined pointers. If non-exist, use undef pointer value;
+ Value *Ptr = getRandomPointerValue();
+ Value *V = new LoadInst(Ptr->getType()->getPointerElementType(), Ptr, "L",
+ BB->getTerminator());
+ PT->push_back(V);
+ }
+};
+
+struct StoreModifier: public Modifier {
+ StoreModifier(BasicBlock *BB, PieceTable *PT, Random *R)
+ : Modifier(BB, PT, R) {}
+
+ void Act() override {
+ // Try to use predefined pointers. If non-exist, use undef pointer value;
+ Value *Ptr = getRandomPointerValue();
+ Value *Val = getRandomValue(Ptr->getType()->getPointerElementType());
+ Type *ValTy = Val->getType();
+
+ // Do not store vectors of i1s because they are unsupported
+ // by the codegen.
+ if (ValTy->isVectorTy() && ValTy->getScalarSizeInBits() == 1)
+ return;
+
+ new StoreInst(Val, Ptr, BB->getTerminator());
+ }
+};
+
+struct BinModifier: public Modifier {
+ BinModifier(BasicBlock *BB, PieceTable *PT, Random *R)
+ : Modifier(BB, PT, R) {}
+
+ void Act() override {
+ Value *Val0 = getRandomVal();
+ Value *Val1 = getRandomValue(Val0->getType());
+
+ // Don't handle pointer types.
+ if (Val0->getType()->isPointerTy() ||
+ Val1->getType()->isPointerTy())
+ return;
+
+ // Don't handle i1 types.
+ if (Val0->getType()->getScalarSizeInBits() == 1)
+ return;
+
+ bool isFloat = Val0->getType()->getScalarType()->isFloatingPointTy();
+ Instruction* Term = BB->getTerminator();
+ unsigned R = getRandom() % (isFloat ? 7 : 13);
+ Instruction::BinaryOps Op;
+
+ switch (R) {
+ default: llvm_unreachable("Invalid BinOp");
+ case 0:{Op = (isFloat?Instruction::FAdd : Instruction::Add); break; }
+ case 1:{Op = (isFloat?Instruction::FSub : Instruction::Sub); break; }
+ case 2:{Op = (isFloat?Instruction::FMul : Instruction::Mul); break; }
+ case 3:{Op = (isFloat?Instruction::FDiv : Instruction::SDiv); break; }
+ case 4:{Op = (isFloat?Instruction::FDiv : Instruction::UDiv); break; }
+ case 5:{Op = (isFloat?Instruction::FRem : Instruction::SRem); break; }
+ case 6:{Op = (isFloat?Instruction::FRem : Instruction::URem); break; }
+ case 7: {Op = Instruction::Shl; break; }
+ case 8: {Op = Instruction::LShr; break; }
+ case 9: {Op = Instruction::AShr; break; }
+ case 10:{Op = Instruction::And; break; }
+ case 11:{Op = Instruction::Or; break; }
+ case 12:{Op = Instruction::Xor; break; }
+ }
+
+ PT->push_back(BinaryOperator::Create(Op, Val0, Val1, "B", Term));
+ }
+};
+
+/// Generate constant values.
+struct ConstModifier: public Modifier {
+ ConstModifier(BasicBlock *BB, PieceTable *PT, Random *R)
+ : Modifier(BB, PT, R) {}
+
+ void Act() override {
+ Type *Ty = pickType();
+
+ if (Ty->isVectorTy()) {
+ switch (getRandom() % 2) {
+ case 0: if (Ty->isIntOrIntVectorTy())
+ return PT->push_back(ConstantVector::getAllOnesValue(Ty));
+ break;
+ case 1: if (Ty->isIntOrIntVectorTy())
+ return PT->push_back(ConstantVector::getNullValue(Ty));
+ }
+ }
+
+ if (Ty->isFloatingPointTy()) {
+ // Generate 128 random bits, the size of the (currently)
+ // largest floating-point types.
+ uint64_t RandomBits[2];
+ for (unsigned i = 0; i < 2; ++i)
+ RandomBits[i] = Ran->Rand64();
+
+ APInt RandomInt(Ty->getPrimitiveSizeInBits(), makeArrayRef(RandomBits));
+ APFloat RandomFloat(Ty->getFltSemantics(), RandomInt);
+
+ if (getRandom() & 1)
+ return PT->push_back(ConstantFP::getNullValue(Ty));
+ return PT->push_back(ConstantFP::get(Ty->getContext(), RandomFloat));
+ }
+
+ if (Ty->isIntegerTy()) {
+ switch (getRandom() % 7) {
+ case 0:
+ return PT->push_back(ConstantInt::get(
+ Ty, APInt::getAllOnes(Ty->getPrimitiveSizeInBits())));
+ case 1:
+ return PT->push_back(
+ ConstantInt::get(Ty, APInt::getZero(Ty->getPrimitiveSizeInBits())));
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ PT->push_back(ConstantInt::get(Ty, getRandom()));
+ }
+ }
+ }
+};
+
+struct AllocaModifier: public Modifier {
+ AllocaModifier(BasicBlock *BB, PieceTable *PT, Random *R)
+ : Modifier(BB, PT, R) {}
+
+ void Act() override {
+ Type *Tp = pickType();
+ const DataLayout &DL = BB->getModule()->getDataLayout();
+ PT->push_back(new AllocaInst(Tp, DL.getAllocaAddrSpace(),
+ "A", BB->getFirstNonPHI()));
+ }
+};
+
+struct ExtractElementModifier: public Modifier {
+ ExtractElementModifier(BasicBlock *BB, PieceTable *PT, Random *R)
+ : Modifier(BB, PT, R) {}
+
+ void Act() override {
+ Value *Val0 = getRandomVectorValue();
+ Value *V = ExtractElementInst::Create(
+ Val0,
+ ConstantInt::get(
+ Type::getInt32Ty(BB->getContext()),
+ getRandom() %
+ cast<FixedVectorType>(Val0->getType())->getNumElements()),
+ "E", BB->getTerminator());
+ return PT->push_back(V);
+ }
+};
+
+struct ShuffModifier: public Modifier {
+ ShuffModifier(BasicBlock *BB, PieceTable *PT, Random *R)
+ : Modifier(BB, PT, R) {}
+
+ void Act() override {
+ Value *Val0 = getRandomVectorValue();
+ Value *Val1 = getRandomValue(Val0->getType());
+
+ unsigned Width = cast<FixedVectorType>(Val0->getType())->getNumElements();
+ std::vector<Constant*> Idxs;
+
+ Type *I32 = Type::getInt32Ty(BB->getContext());
+ for (unsigned i=0; i<Width; ++i) {
+ Constant *CI = ConstantInt::get(I32, getRandom() % (Width*2));
+ // Pick some undef values.
+ if (!(getRandom() % 5))
+ CI = UndefValue::get(I32);
+ Idxs.push_back(CI);
+ }
+
+ Constant *Mask = ConstantVector::get(Idxs);
+
+ Value *V = new ShuffleVectorInst(Val0, Val1, Mask, "Shuff",
+ BB->getTerminator());
+ PT->push_back(V);
+ }
+};
+
+struct InsertElementModifier: public Modifier {
+ InsertElementModifier(BasicBlock *BB, PieceTable *PT, Random *R)
+ : Modifier(BB, PT, R) {}
+
+ void Act() override {
+ Value *Val0 = getRandomVectorValue();
+ Value *Val1 = getRandomValue(Val0->getType()->getScalarType());
+
+ Value *V = InsertElementInst::Create(
+ Val0, Val1,
+ ConstantInt::get(
+ Type::getInt32Ty(BB->getContext()),
+ getRandom() %
+ cast<FixedVectorType>(Val0->getType())->getNumElements()),
+ "I", BB->getTerminator());
+ return PT->push_back(V);
+ }
+};
+
+struct CastModifier: public Modifier {
+ CastModifier(BasicBlock *BB, PieceTable *PT, Random *R)
+ : Modifier(BB, PT, R) {}
+
+ void Act() override {
+ Value *V = getRandomVal();
+ Type *VTy = V->getType();
+ Type *DestTy = pickScalarType();
+
+ // Handle vector casts vectors.
+ if (VTy->isVectorTy()) {
+ auto *VecTy = cast<FixedVectorType>(VTy);
+ DestTy = pickVectorType(VecTy->getNumElements());
+ }
+
+ // no need to cast.
+ if (VTy == DestTy) return;
+
+ // Pointers:
+ if (VTy->isPointerTy()) {
+ if (!DestTy->isPointerTy())
+ DestTy = PointerType::get(DestTy, 0);
+ return PT->push_back(
+ new BitCastInst(V, DestTy, "PC", BB->getTerminator()));
+ }
+
+ unsigned VSize = VTy->getScalarType()->getPrimitiveSizeInBits();
+ unsigned DestSize = DestTy->getScalarType()->getPrimitiveSizeInBits();
+
+ // Generate lots of bitcasts.
+ if ((getRandom() & 1) && VSize == DestSize) {
+ return PT->push_back(
+ new BitCastInst(V, DestTy, "BC", BB->getTerminator()));
+ }
+
+ // Both types are integers:
+ if (VTy->isIntOrIntVectorTy() && DestTy->isIntOrIntVectorTy()) {
+ if (VSize > DestSize) {
+ return PT->push_back(
+ new TruncInst(V, DestTy, "Tr", BB->getTerminator()));
+ } else {
+ assert(VSize < DestSize && "Different int types with the same size?");
+ if (getRandom() & 1)
+ return PT->push_back(
+ new ZExtInst(V, DestTy, "ZE", BB->getTerminator()));
+ return PT->push_back(new SExtInst(V, DestTy, "Se", BB->getTerminator()));
+ }
+ }
+
+ // Fp to int.
+ if (VTy->isFPOrFPVectorTy() && DestTy->isIntOrIntVectorTy()) {
+ if (getRandom() & 1)
+ return PT->push_back(
+ new FPToSIInst(V, DestTy, "FC", BB->getTerminator()));
+ return PT->push_back(new FPToUIInst(V, DestTy, "FC", BB->getTerminator()));
+ }
+
+ // Int to fp.
+ if (VTy->isIntOrIntVectorTy() && DestTy->isFPOrFPVectorTy()) {
+ if (getRandom() & 1)
+ return PT->push_back(
+ new SIToFPInst(V, DestTy, "FC", BB->getTerminator()));
+ return PT->push_back(new UIToFPInst(V, DestTy, "FC", BB->getTerminator()));
+ }
+
+ // Both floats.
+ if (VTy->isFPOrFPVectorTy() && DestTy->isFPOrFPVectorTy()) {
+ if (VSize > DestSize) {
+ return PT->push_back(
+ new FPTruncInst(V, DestTy, "Tr", BB->getTerminator()));
+ } else if (VSize < DestSize) {
+ return PT->push_back(
+ new FPExtInst(V, DestTy, "ZE", BB->getTerminator()));
+ }
+ // If VSize == DestSize, then the two types must be fp128 and ppc_fp128,
+ // for which there is no defined conversion. So do nothing.
+ }
+ }
+};
+
+struct SelectModifier: public Modifier {
+ SelectModifier(BasicBlock *BB, PieceTable *PT, Random *R)
+ : Modifier(BB, PT, R) {}
+
+ void Act() override {
+ // Try a bunch of different select configuration until a valid one is found.
+ Value *Val0 = getRandomVal();
+ Value *Val1 = getRandomValue(Val0->getType());
+
+ Type *CondTy = Type::getInt1Ty(Context);
+
+ // If the value type is a vector, and we allow vector select, then in 50%
+ // of the cases generate a vector select.
+ if (isa<FixedVectorType>(Val0->getType()) && (getRandom() & 1)) {
+ unsigned NumElem =
+ cast<FixedVectorType>(Val0->getType())->getNumElements();
+ CondTy = FixedVectorType::get(CondTy, NumElem);
+ }
+
+ Value *Cond = getRandomValue(CondTy);
+ Value *V = SelectInst::Create(Cond, Val0, Val1, "Sl", BB->getTerminator());
+ return PT->push_back(V);
+ }
+};
+
+struct CmpModifier: public Modifier {
+ CmpModifier(BasicBlock *BB, PieceTable *PT, Random *R)
+ : Modifier(BB, PT, R) {}
+
+ void Act() override {
+ Value *Val0 = getRandomVal();
+ Value *Val1 = getRandomValue(Val0->getType());
+
+ if (Val0->getType()->isPointerTy()) return;
+ bool fp = Val0->getType()->getScalarType()->isFloatingPointTy();
+
+ int op;
+ if (fp) {
+ op = getRandom() %
+ (CmpInst::LAST_FCMP_PREDICATE - CmpInst::FIRST_FCMP_PREDICATE) +
+ CmpInst::FIRST_FCMP_PREDICATE;
+ } else {
+ op = getRandom() %
+ (CmpInst::LAST_ICMP_PREDICATE - CmpInst::FIRST_ICMP_PREDICATE) +
+ CmpInst::FIRST_ICMP_PREDICATE;
+ }
+
+ Value *V = CmpInst::Create(fp ? Instruction::FCmp : Instruction::ICmp,
+ (CmpInst::Predicate)op, Val0, Val1, "Cmp",
+ BB->getTerminator());
+ return PT->push_back(V);
+ }
+};
+
+} // end anonymous namespace
+
+static void FillFunction(Function *F, Random &R) {
+ // Create a legal entry block.
+ BasicBlock *BB = BasicBlock::Create(F->getContext(), "BB", F);
+ ReturnInst::Create(F->getContext(), BB);
+
+ // Create the value table.
+ Modifier::PieceTable PT;
+
+ // Consider arguments as legal values.
+ for (auto &arg : F->args())
+ PT.push_back(&arg);
+
+ // List of modifiers which add new random instructions.
+ std::vector<std::unique_ptr<Modifier>> Modifiers;
+ Modifiers.emplace_back(new LoadModifier(BB, &PT, &R));
+ Modifiers.emplace_back(new StoreModifier(BB, &PT, &R));
+ auto SM = Modifiers.back().get();
+ Modifiers.emplace_back(new ExtractElementModifier(BB, &PT, &R));
+ Modifiers.emplace_back(new ShuffModifier(BB, &PT, &R));
+ Modifiers.emplace_back(new InsertElementModifier(BB, &PT, &R));
+ Modifiers.emplace_back(new BinModifier(BB, &PT, &R));
+ Modifiers.emplace_back(new CastModifier(BB, &PT, &R));
+ Modifiers.emplace_back(new SelectModifier(BB, &PT, &R));
+ Modifiers.emplace_back(new CmpModifier(BB, &PT, &R));
+
+ // Generate the random instructions
+ AllocaModifier{BB, &PT, &R}.ActN(5); // Throw in a few allocas
+ ConstModifier{BB, &PT, &R}.ActN(40); // Throw in a few constants
+
+ for (unsigned i = 0; i < SizeCL / Modifiers.size(); ++i)
+ for (auto &Mod : Modifiers)
+ Mod->Act();
+
+ SM->ActN(5); // Throw in a few stores.
+}
+
+static void IntroduceControlFlow(Function *F, Random &R) {
+ std::vector<Instruction*> BoolInst;
+ for (auto &Instr : F->front()) {
+ if (Instr.getType() == IntegerType::getInt1Ty(F->getContext()))
+ BoolInst.push_back(&Instr);
+ }
+
+ llvm::shuffle(BoolInst.begin(), BoolInst.end(), R);
+
+ for (auto *Instr : BoolInst) {
+ BasicBlock *Curr = Instr->getParent();
+ BasicBlock::iterator Loc = Instr->getIterator();
+ BasicBlock *Next = Curr->splitBasicBlock(Loc, "CF");
+ Instr->moveBefore(Curr->getTerminator());
+ if (Curr != &F->getEntryBlock()) {
+ BranchInst::Create(Curr, Next, Instr, Curr->getTerminator());
+ Curr->getTerminator()->eraseFromParent();
+ }
+ }
+}
+
+} // end namespace llvm
+
+int main(int argc, char **argv) {
+ using namespace llvm;
+
+ InitLLVM X(argc, argv);
+ cl::HideUnrelatedOptions({&StressCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "llvm codegen stress-tester\n");
+
+ auto M = std::make_unique<Module>("/tmp/autogen.bc", Context);
+ Function *F = GenEmptyFunction(M.get());
+
+ // Pick an initial seed value
+ Random R(SeedCL);
+ // Generate lots of random instructions inside a single basic block.
+ FillFunction(F, R);
+ // Break the basic block into many loops.
+ IntroduceControlFlow(F, R);
+
+ // Figure out what stream we are supposed to write to...
+ std::unique_ptr<ToolOutputFile> Out;
+ // Default to standard output.
+ if (OutputFilename.empty())
+ OutputFilename = "-";
+
+ std::error_code EC;
+ Out.reset(new ToolOutputFile(OutputFilename, EC, sys::fs::OF_None));
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return 1;
+ }
+
+ legacy::PassManager Passes;
+ Passes.add(createVerifierPass());
+ Passes.add(createPrintModulePass(Out->os()));
+ Passes.run(*M.get());
+ Out->keep();
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-stress/ya.make b/contrib/libs/llvm14/tools/llvm-stress/ya.make
new file mode 100644
index 00000000000..b19cd172d7c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-stress/ya.make
@@ -0,0 +1,33 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-stress
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-stress.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-strings/Opts.td b/contrib/libs/llvm14/tools/llvm-strings/Opts.td
new file mode 100644
index 00000000000..2ad77fae6c1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-strings/Opts.td
@@ -0,0 +1,23 @@
+include "llvm/Option/OptParser.td"
+
+class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>;
+class FF<string name, string help> : Flag<["--"], name>, HelpText<help>;
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["--"], name #"=">,
+ HelpText<help>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def all : FF<"all", "Silently ignored. Present for GNU strings compatibility">;
+defm bytes : Eq<"bytes", "Print sequences of the specified length">;
+def help : FF<"help", "Display this help">;
+def print_file_name : Flag<["--"], "print-file-name">, HelpText<"Print the name of the file before each string">;
+defm radix : Eq<"radix", "Print the offset within the file with the specified radix: o (octal), d (decimal), x (hexadecimal)">, MetaVarName<"<radix>">;
+def version : FF<"version", "Display the version">;
+
+def : F<"a", "Alias for --all">, Alias<all>;
+def : F<"f", "Alias for --print-file-name">, Alias<print_file_name>;
+def : F<"h", "Alias for --help">, Alias<help>;
+def : JoinedOrSeparate<["-"], "n">, Alias<bytes_EQ>, HelpText<"Alias for --bytes">;
+def : JoinedOrSeparate<["-"], "t">, Alias<radix_EQ>, HelpText<"Alias for --radix">, MetaVarName<"<radix>">;
diff --git a/contrib/libs/llvm14/tools/llvm-strings/llvm-strings.cpp b/contrib/libs/llvm14/tools/llvm-strings/llvm-strings.cpp
new file mode 100644
index 00000000000..438eed33d28
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-strings/llvm-strings.cpp
@@ -0,0 +1,186 @@
+//===-- llvm-strings.cpp - Printable String dumping utility ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program is a utility that works like binutils "strings", that is, it
+// prints out printable strings in a binary, objdump, or archive file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Opts.inc"
+#include "llvm/Object/Binary.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/WithColor.h"
+#include <cctype>
+#include <string>
+
+using namespace llvm;
+using namespace llvm::object;
+
+namespace {
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class StringsOptTable : public opt::OptTable {
+public:
+ StringsOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); }
+};
+} // namespace
+
+static StringRef ToolName;
+
+static cl::list<std::string> InputFileNames(cl::Positional,
+ cl::desc("<input object files>"),
+ cl::ZeroOrMore);
+
+static int MinLength = 4;
+static bool PrintFileName;
+
+enum radix { none, octal, hexadecimal, decimal };
+static radix Radix;
+
+[[noreturn]] static void reportCmdLineError(const Twine &Message) {
+ WithColor::error(errs(), ToolName) << Message << "\n";
+ exit(1);
+}
+
+template <typename T>
+static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) {
+ if (const opt::Arg *A = Args.getLastArg(ID)) {
+ StringRef V(A->getValue());
+ if (!llvm::to_integer(V, Value, 0) || Value <= 0)
+ reportCmdLineError("expected a positive integer, but got '" + V + "'");
+ }
+}
+
+static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) {
+ auto print = [&OS, FileName](unsigned Offset, StringRef L) {
+ if (L.size() < static_cast<size_t>(MinLength))
+ return;
+ if (PrintFileName)
+ OS << FileName << ": ";
+ switch (Radix) {
+ case none:
+ break;
+ case octal:
+ OS << format("%7o ", Offset);
+ break;
+ case hexadecimal:
+ OS << format("%7x ", Offset);
+ break;
+ case decimal:
+ OS << format("%7u ", Offset);
+ break;
+ }
+ 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 (isPrint(*P) || *P == '\t') {
+ if (S == nullptr)
+ S = P;
+ } else if (S) {
+ print(S - B, StringRef(S, P - S));
+ S = nullptr;
+ }
+ }
+ if (S)
+ print(S - B, StringRef(S, E - S));
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ StringsOptTable Tbl;
+ ToolName = argv[0];
+ opt::InputArgList Args =
+ Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver,
+ [&](StringRef Msg) { reportCmdLineError(Msg); });
+ if (Args.hasArg(OPT_help)) {
+ Tbl.printHelp(
+ outs(),
+ (Twine(ToolName) + " [options] <input object files>").str().c_str(),
+ "llvm string dumper");
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ outs() << "\nPass @FILE as argument to read options from FILE.\n";
+ return 0;
+ }
+ if (Args.hasArg(OPT_version)) {
+ outs() << ToolName << '\n';
+ cl::PrintVersionMessage();
+ return 0;
+ }
+
+ parseIntArg(Args, OPT_bytes_EQ, MinLength);
+ PrintFileName = Args.hasArg(OPT_print_file_name);
+ StringRef R = Args.getLastArgValue(OPT_radix_EQ);
+ if (R.empty())
+ Radix = none;
+ else if (R == "o")
+ Radix = octal;
+ else if (R == "d")
+ Radix = decimal;
+ else if (R == "x")
+ Radix = hexadecimal;
+ else
+ reportCmdLineError("--radix value should be one of: '' (no offset), 'o' "
+ "(octal), 'd' (decimal), 'x' (hexadecimal)");
+
+ if (MinLength == 0) {
+ errs() << "invalid minimum string length 0\n";
+ return EXIT_FAILURE;
+ }
+
+ std::vector<std::string> InputFileNames = Args.getAllArgValues(OPT_INPUT);
+ if (InputFileNames.empty())
+ InputFileNames.push_back("-");
+
+ for (const auto &File : InputFileNames) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
+ MemoryBuffer::getFileOrSTDIN(File);
+ if (std::error_code EC = Buffer.getError())
+ errs() << File << ": " << EC.message() << '\n';
+ else
+ strings(llvm::outs(), File == "-" ? "{standard input}" : File,
+ Buffer.get()->getMemBufferRef().getBuffer());
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-strings/ya.make b/contrib/libs/llvm14/tools/llvm-strings/ya.make
new file mode 100644
index 00000000000..27a1cfd9cdf
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-strings/ya.make
@@ -0,0 +1,32 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-strings
+ contrib/libs/llvm14/tools/llvm-strings
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-strings.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-symbolizer/Opts.td b/contrib/libs/llvm14/tools/llvm-symbolizer/Opts.td
new file mode 100644
index 00000000000..6026e24d6ff
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-symbolizer/Opts.td
@@ -0,0 +1,80 @@
+include "llvm/Option/OptParser.td"
+
+multiclass B<string name, string help1, string help2> {
+ def NAME: Flag<["--"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
+}
+
+multiclass Eq<string name, string help> {
+ def NAME #_EQ : Joined<["--"], name #"=">,
+ HelpText<help>;
+ def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+class F<string name, string help>: Flag<["--"], name>, HelpText<help>;
+
+def grp_mach_o : OptionGroup<"kind">,
+ HelpText<"llvm-symbolizer Mach-O Specific Options">;
+
+def addresses : F<"addresses", "Show address before line information">;
+defm adjust_vma
+ : Eq<"adjust-vma", "Add specified offset to object file addresses">,
+ MetaVarName<"<offset>">;
+def basenames : Flag<["--"], "basenames">, HelpText<"Strip directory names from paths">;
+defm debug_file_directory : Eq<"debug-file-directory", "Path to directory where to look for debug files">, MetaVarName<"<dir>">;
+defm default_arch
+ : Eq<"default-arch", "Default architecture (for multi-arch objects)">,
+ Group<grp_mach_o>;
+defm demangle : B<"demangle", "Demangle function names", "Don't demangle function names">;
+def functions : F<"functions", "Print function name for a given address">;
+def functions_EQ : Joined<["--"], "functions=">, HelpText<"Print function name for a given address">, Values<"none,short,linkage">;
+def help : F<"help", "Display this help">;
+defm dwp : Eq<"dwp", "Path to DWP file to be use for any split CUs">, MetaVarName<"<file>">;
+defm dsym_hint
+ : Eq<"dsym-hint",
+ "Path to .dSYM bundles to search for debug info for the object files">,
+ MetaVarName<"<dir>">,
+ Group<grp_mach_o>;
+defm fallback_debug_path : Eq<"fallback-debug-path", "Fallback path for debug binaries">, MetaVarName<"<dir>">;
+defm inlines : B<"inlines", "Print all inlined frames for a given address",
+ "Do not print inlined frames">;
+defm obj
+ : Eq<"obj", "Path to object file to be symbolized (if not provided, "
+ "object file should be specified for each input line)">, MetaVarName<"<file>">;
+defm output_style
+ : Eq<"output-style", "Specify print style. Supported styles: LLVM, GNU, JSON">,
+ MetaVarName<"style">,
+ Values<"LLVM,GNU,JSON">;
+def pretty_print : F<"pretty-print", "Make the output more human friendly">;
+defm print_source_context_lines : Eq<"print-source-context-lines", "Print N lines of source file context">;
+def relative_address : F<"relative-address", "Interpret addresses as addresses relative to the image base">;
+def relativenames : F<"relativenames", "Strip the compilation directory from paths">;
+defm untag_addresses : B<"untag-addresses", "", "Remove memory tags from addresses before symbolization">;
+def use_dia: F<"dia", "Use the DIA library to access symbols (Windows only)">;
+def verbose : F<"verbose", "Print verbose line info">;
+def version : F<"version", "Display the version">;
+
+def : Flag<["-"], "a">, Alias<addresses>, HelpText<"Alias for --addresses">;
+def : F<"print-address", "Alias for --addresses">, Alias<addresses>;
+def : Flag<["-"], "C">, Alias<demangle>, HelpText<"Alias for --demangle">;
+def : Joined<["--"], "exe=">, Alias<obj_EQ>, HelpText<"Alias for --obj">, MetaVarName<"<file>">;
+def : Separate<["--"], "exe">, Alias<obj_EQ>, HelpText<"Alias for --obj">, MetaVarName<"<file>">;
+def : JoinedOrSeparate<["-"], "e">, Alias<obj_EQ>, HelpText<"Alias for --obj">, MetaVarName<"<file>">;
+def : Joined<["-"], "e=">, Alias<obj_EQ>, HelpText<"Alias for --obj">, MetaVarName<"<file>">;
+def : Flag<["-"], "f">, Alias<functions>, HelpText<"Alias for --functions">;
+def : Joined<["-"], "f=">, Alias<functions_EQ>, HelpText<"Alias for --functions=">;
+def : Flag<["-"], "h">, Alias<help>;
+def : Flag<["-"], "i">, Alias<inlines>, HelpText<"Alias for --inlines">;
+def : F<"inlining", "Alias for --inlines">, Alias<inlines>;
+def : Flag<["-"], "p">, Alias<pretty_print>, HelpText<"Alias for --pretty-print">;
+def : Flag<["-"], "s">, Alias<basenames>, HelpText<"Alias for --basenames">;
+def : Flag<["-"], "v">, Alias<version>, HelpText<"Alias for --version">;
+
+// Compatibility aliases for old asan_symbolize.py and sanitizer binaries (before 2020-08).
+def : Flag<["--"], "inlining=true">, Alias<inlines>, HelpText<"Alias for --inlines">;
+def : Flag<["--"], "inlining=false">, Alias<no_inlines>, HelpText<"Alias for --no-inlines">;
+// Compatibility aliases for pprof's symbolizer.
+def : Flag<["-"], "demangle=true">, Alias<demangle>, HelpText<"Alias for --demangle">;
+def : Flag<["-"], "demangle=false">, Alias<no_demangle>, HelpText<"Alias for --no-demangle">;
+// Compatibility no-op options.
+def : Flag<["--"], "use-symbol-table=true">;
diff --git a/contrib/libs/llvm14/tools/llvm-symbolizer/llvm-symbolizer.cpp b/contrib/libs/llvm14/tools/llvm-symbolizer/llvm-symbolizer.cpp
new file mode 100644
index 00000000000..0315413ea0c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-symbolizer/llvm-symbolizer.cpp
@@ -0,0 +1,369 @@
+//===-- llvm-symbolizer.cpp - Simple addr2line-like symbolizer ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This utility works much like "addr2line". It is able of transforming
+// tuples (module name, module offset) to code locations (function name,
+// file, line number, column number). It is targeted for compiler-rt tools
+// (especially AddressSanitizer and ThreadSanitizer) that can use it
+// to symbolize stack traces in their error reports.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Opts.inc"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Config/config.h"
+#include "llvm/DebugInfo/Symbolize/DIPrinter.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/Debuginfod/DIFetcher.h"
+#include "llvm/Debuginfod/HTTPClient.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/COM.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/StringSaver.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+#include <string>
+
+using namespace llvm;
+using namespace symbolize;
+
+namespace {
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class SymbolizerOptTable : public opt::OptTable {
+public:
+ SymbolizerOptTable() : OptTable(InfoTable) {
+ setGroupedShortOptions(true);
+ }
+};
+} // namespace
+
+template <typename T>
+static void print(const Request &Request, Expected<T> &ResOrErr,
+ DIPrinter &Printer) {
+ if (ResOrErr) {
+ // No error, print the result.
+ Printer.print(Request, *ResOrErr);
+ return;
+ }
+
+ // Handle the error.
+ bool PrintEmpty = true;
+ handleAllErrors(std::move(ResOrErr.takeError()),
+ [&](const ErrorInfoBase &EI) {
+ PrintEmpty = Printer.printError(
+ Request, EI, "LLVMSymbolizer: error reading file: ");
+ });
+
+ if (PrintEmpty)
+ Printer.print(Request, T());
+}
+
+enum class OutputStyle { LLVM, GNU, JSON };
+
+enum class Command {
+ Code,
+ Data,
+ Frame,
+};
+
+static bool parseCommand(StringRef BinaryName, bool IsAddr2Line,
+ StringRef InputString, Command &Cmd,
+ std::string &ModuleName, uint64_t &ModuleOffset) {
+ const char kDelimiters[] = " \n\r";
+ ModuleName = "";
+ if (InputString.consume_front("CODE ")) {
+ Cmd = Command::Code;
+ } else if (InputString.consume_front("DATA ")) {
+ Cmd = Command::Data;
+ } else if (InputString.consume_front("FRAME ")) {
+ Cmd = Command::Frame;
+ } else {
+ // If no cmd, assume it's CODE.
+ Cmd = Command::Code;
+ }
+ const char *Pos = InputString.data();
+ // Skip delimiters and parse input filename (if needed).
+ if (BinaryName.empty()) {
+ Pos += strspn(Pos, kDelimiters);
+ if (*Pos == '"' || *Pos == '\'') {
+ char Quote = *Pos;
+ Pos++;
+ const char *End = strchr(Pos, Quote);
+ if (!End)
+ return false;
+ ModuleName = std::string(Pos, End - Pos);
+ Pos = End + 1;
+ } else {
+ int NameLength = strcspn(Pos, kDelimiters);
+ ModuleName = std::string(Pos, NameLength);
+ Pos += NameLength;
+ }
+ } else {
+ ModuleName = BinaryName.str();
+ }
+ // Skip delimiters and parse module offset.
+ Pos += strspn(Pos, kDelimiters);
+ int OffsetLength = strcspn(Pos, kDelimiters);
+ StringRef Offset(Pos, OffsetLength);
+ // GNU addr2line assumes the offset is hexadecimal and allows a redundant
+ // "0x" or "0X" prefix; do the same for compatibility.
+ if (IsAddr2Line)
+ Offset.consume_front("0x") || Offset.consume_front("0X");
+ return !Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset);
+}
+
+static void symbolizeInput(const opt::InputArgList &Args, uint64_t AdjustVMA,
+ bool IsAddr2Line, OutputStyle Style,
+ StringRef InputString, LLVMSymbolizer &Symbolizer,
+ DIPrinter &Printer) {
+ Command Cmd;
+ std::string ModuleName;
+ uint64_t Offset = 0;
+ if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line,
+ StringRef(InputString), Cmd, ModuleName, Offset)) {
+ Printer.printInvalidCommand({ModuleName, None}, InputString);
+ return;
+ }
+
+ uint64_t AdjustedOffset = Offset - AdjustVMA;
+ if (Cmd == Command::Data) {
+ Expected<DIGlobal> ResOrErr = Symbolizer.symbolizeData(
+ ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection});
+ print({ModuleName, Offset}, ResOrErr, Printer);
+ } else if (Cmd == Command::Frame) {
+ Expected<std::vector<DILocal>> ResOrErr = Symbolizer.symbolizeFrame(
+ ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection});
+ print({ModuleName, Offset}, ResOrErr, Printer);
+ } else if (Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line)) {
+ Expected<DIInliningInfo> ResOrErr = Symbolizer.symbolizeInlinedCode(
+ ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection});
+ print({ModuleName, Offset}, ResOrErr, Printer);
+ } else if (Style == OutputStyle::GNU) {
+ // With PrintFunctions == FunctionNameKind::LinkageName (default)
+ // and UseSymbolTable == true (also default), Symbolizer.symbolizeCode()
+ // may override the name of an inlined function with the name of the topmost
+ // caller function in the inlining chain. This contradicts the existing
+ // behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only
+ // the topmost function, which suits our needs better.
+ Expected<DIInliningInfo> ResOrErr = Symbolizer.symbolizeInlinedCode(
+ ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection});
+ Expected<DILineInfo> Res0OrErr =
+ !ResOrErr
+ ? Expected<DILineInfo>(ResOrErr.takeError())
+ : ((ResOrErr->getNumberOfFrames() == 0) ? DILineInfo()
+ : ResOrErr->getFrame(0));
+ print({ModuleName, Offset}, Res0OrErr, Printer);
+ } else {
+ Expected<DILineInfo> ResOrErr = Symbolizer.symbolizeCode(
+ ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection});
+ print({ModuleName, Offset}, ResOrErr, Printer);
+ }
+}
+
+static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl,
+ raw_ostream &OS) {
+ const char HelpText[] = " [options] addresses...";
+ Tbl.printHelp(OS, (ToolName + HelpText).str().c_str(),
+ ToolName.str().c_str());
+ // TODO Replace this with OptTable API once it adds extrahelp support.
+ OS << "\nPass @FILE as argument to read options from FILE.\n";
+}
+
+static opt::InputArgList parseOptions(int Argc, char *Argv[], bool IsAddr2Line,
+ StringSaver &Saver,
+ SymbolizerOptTable &Tbl) {
+ StringRef ToolName = IsAddr2Line ? "llvm-addr2line" : "llvm-symbolizer";
+ // The environment variable specifies initial options which can be overridden
+ // by commnad line options.
+ Tbl.setInitialOptionsFromEnvironment(IsAddr2Line ? "LLVM_ADDR2LINE_OPTS"
+ : "LLVM_SYMBOLIZER_OPTS");
+ bool HasError = false;
+ opt::InputArgList Args =
+ Tbl.parseArgs(Argc, Argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
+ errs() << ("error: " + Msg + "\n");
+ HasError = true;
+ });
+ if (HasError)
+ exit(1);
+ if (Args.hasArg(OPT_help)) {
+ printHelp(ToolName, Tbl, outs());
+ exit(0);
+ }
+ if (Args.hasArg(OPT_version)) {
+ outs() << ToolName << '\n';
+ cl::PrintVersionMessage();
+ exit(0);
+ }
+
+ return Args;
+}
+
+template <typename T>
+static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) {
+ if (const opt::Arg *A = Args.getLastArg(ID)) {
+ StringRef V(A->getValue());
+ if (!llvm::to_integer(V, Value, 0)) {
+ errs() << A->getSpelling() +
+ ": expected a non-negative integer, but got '" + V + "'";
+ exit(1);
+ }
+ } else {
+ Value = 0;
+ }
+}
+
+static FunctionNameKind decideHowToPrintFunctions(const opt::InputArgList &Args,
+ bool IsAddr2Line) {
+ if (Args.hasArg(OPT_functions))
+ return FunctionNameKind::LinkageName;
+ if (const opt::Arg *A = Args.getLastArg(OPT_functions_EQ))
+ return StringSwitch<FunctionNameKind>(A->getValue())
+ .Case("none", FunctionNameKind::None)
+ .Case("short", FunctionNameKind::ShortName)
+ .Default(FunctionNameKind::LinkageName);
+ return IsAddr2Line ? FunctionNameKind::None : FunctionNameKind::LinkageName;
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ sys::InitializeCOMRAII COM(sys::COMThreadingMode::MultiThreaded);
+
+ bool IsAddr2Line = sys::path::stem(argv[0]).contains("addr2line");
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ SymbolizerOptTable Tbl;
+ opt::InputArgList Args = parseOptions(argc, argv, IsAddr2Line, Saver, Tbl);
+
+ LLVMSymbolizer::Options Opts;
+ uint64_t AdjustVMA;
+ PrinterConfig Config;
+ parseIntArg(Args, OPT_adjust_vma_EQ, AdjustVMA);
+ if (const opt::Arg *A = Args.getLastArg(OPT_basenames, OPT_relativenames)) {
+ Opts.PathStyle =
+ A->getOption().matches(OPT_basenames)
+ ? DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly
+ : DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath;
+ } else {
+ Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath;
+ }
+ Opts.DebugFileDirectory = Args.getAllArgValues(OPT_debug_file_directory_EQ);
+ Opts.DefaultArch = Args.getLastArgValue(OPT_default_arch_EQ).str();
+ Opts.Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, !IsAddr2Line);
+ Opts.DWPName = Args.getLastArgValue(OPT_dwp_EQ).str();
+ Opts.FallbackDebugPath =
+ Args.getLastArgValue(OPT_fallback_debug_path_EQ).str();
+ Opts.PrintFunctions = decideHowToPrintFunctions(Args, IsAddr2Line);
+ parseIntArg(Args, OPT_print_source_context_lines_EQ,
+ Config.SourceContextLines);
+ Opts.RelativeAddresses = Args.hasArg(OPT_relative_address);
+ Opts.UntagAddresses =
+ Args.hasFlag(OPT_untag_addresses, OPT_no_untag_addresses, !IsAddr2Line);
+ Opts.UseDIA = Args.hasArg(OPT_use_dia);
+#if !defined(LLVM_ENABLE_DIA_SDK)
+ if (Opts.UseDIA) {
+ WithColor::warning() << "DIA not available; using native PDB reader\n";
+ Opts.UseDIA = false;
+ }
+#endif
+ Opts.UseSymbolTable = true;
+ Config.PrintAddress = Args.hasArg(OPT_addresses);
+ Config.PrintFunctions = Opts.PrintFunctions != FunctionNameKind::None;
+ Config.Pretty = Args.hasArg(OPT_pretty_print);
+ Config.Verbose = Args.hasArg(OPT_verbose);
+
+ for (const opt::Arg *A : Args.filtered(OPT_dsym_hint_EQ)) {
+ StringRef Hint(A->getValue());
+ if (sys::path::extension(Hint) == ".dSYM") {
+ Opts.DsymHints.emplace_back(Hint);
+ } else {
+ errs() << "Warning: invalid dSYM hint: \"" << Hint
+ << "\" (must have the '.dSYM' extension).\n";
+ }
+ }
+
+ auto Style = IsAddr2Line ? OutputStyle::GNU : OutputStyle::LLVM;
+ if (const opt::Arg *A = Args.getLastArg(OPT_output_style_EQ)) {
+ if (strcmp(A->getValue(), "GNU") == 0)
+ Style = OutputStyle::GNU;
+ else if (strcmp(A->getValue(), "JSON") == 0)
+ Style = OutputStyle::JSON;
+ else
+ Style = OutputStyle::LLVM;
+ }
+
+ LLVMSymbolizer Symbolizer(Opts);
+
+ // Look up symbols using the debuginfod client.
+ Symbolizer.addDIFetcher(std::make_unique<DebuginfodDIFetcher>());
+ // The HTTPClient must be initialized for use by the debuginfod client.
+ HTTPClient::initialize();
+
+ std::unique_ptr<DIPrinter> Printer;
+ if (Style == OutputStyle::GNU)
+ Printer = std::make_unique<GNUPrinter>(outs(), errs(), Config);
+ else if (Style == OutputStyle::JSON)
+ Printer = std::make_unique<JSONPrinter>(outs(), Config);
+ else
+ Printer = std::make_unique<LLVMPrinter>(outs(), errs(), Config);
+
+ std::vector<std::string> InputAddresses = Args.getAllArgValues(OPT_INPUT);
+ if (InputAddresses.empty()) {
+ const int kMaxInputStringLength = 1024;
+ char InputString[kMaxInputStringLength];
+
+ while (fgets(InputString, sizeof(InputString), stdin)) {
+ // Strip newline characters.
+ std::string StrippedInputString(InputString);
+ llvm::erase_if(StrippedInputString,
+ [](char c) { return c == '\r' || c == '\n'; });
+ symbolizeInput(Args, AdjustVMA, IsAddr2Line, Style, StrippedInputString,
+ Symbolizer, *Printer);
+ outs().flush();
+ }
+ } else {
+ Printer->listBegin();
+ for (StringRef Address : InputAddresses)
+ symbolizeInput(Args, AdjustVMA, IsAddr2Line, Style, Address, Symbolizer,
+ *Printer);
+ Printer->listEnd();
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-symbolizer/ya.make b/contrib/libs/llvm14/tools/llvm-symbolizer/ya.make
new file mode 100644
index 00000000000..dbb84e13fc2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-symbolizer/ya.make
@@ -0,0 +1,50 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/DebugInfo/MSF
+ contrib/libs/llvm14/lib/DebugInfo/PDB
+ contrib/libs/llvm14/lib/DebugInfo/Symbolize
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/lib/Debuginfod
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-symbolizer
+ contrib/libs/llvm14/tools/llvm-symbolizer
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCDIR(contrib/libs/llvm14)
+
+SRCS(
+ lib/Debuginfod/DIFetcher.cpp
+ lib/Debuginfod/Debuginfod.cpp
+ lib/Debuginfod/HTTPClient.cpp
+ tools/llvm-symbolizer/llvm-symbolizer.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-undname/llvm-undname.cpp b/contrib/libs/llvm14/tools/llvm-undname/llvm-undname.cpp
new file mode 100644
index 00000000000..cc35cdf4533
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-undname/llvm-undname.cpp
@@ -0,0 +1,146 @@
+//===-- llvm-undname.cpp - Microsoft ABI name undecorator
+//------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This utility works like the windows undname utility. It converts mangled
+// Microsoft symbol names into pretty C/C++ human-readable names.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <string>
+
+using namespace llvm;
+
+cl::OptionCategory UndNameCategory("UndName Options");
+
+cl::opt<bool> DumpBackReferences("backrefs", cl::Optional,
+ cl::desc("dump backreferences"), cl::Hidden,
+ cl::init(false), cl::cat(UndNameCategory));
+cl::opt<bool> NoAccessSpecifier("no-access-specifier", cl::Optional,
+ cl::desc("skip access specifiers"), cl::Hidden,
+ cl::init(false), cl::cat(UndNameCategory));
+cl::opt<bool> NoCallingConvention("no-calling-convention", cl::Optional,
+ cl::desc("skip calling convention"),
+ cl::Hidden, cl::init(false),
+ cl::cat(UndNameCategory));
+cl::opt<bool> NoReturnType("no-return-type", cl::Optional,
+ cl::desc("skip return types"), cl::Hidden,
+ cl::init(false), cl::cat(UndNameCategory));
+cl::opt<bool> NoMemberType("no-member-type", cl::Optional,
+ cl::desc("skip member types"), cl::Hidden,
+ cl::init(false), cl::cat(UndNameCategory));
+cl::opt<bool> NoVariableType("no-variable-type", cl::Optional,
+ cl::desc("skip variable types"), cl::Hidden,
+ cl::init(false), cl::cat(UndNameCategory));
+cl::opt<std::string> RawFile("raw-file", cl::Optional,
+ cl::desc("for fuzzer data"), cl::Hidden,
+ cl::cat(UndNameCategory));
+cl::opt<bool> WarnTrailing("warn-trailing", cl::Optional,
+ cl::desc("warn on trailing characters"), cl::Hidden,
+ cl::init(false), cl::cat(UndNameCategory));
+cl::list<std::string> Symbols(cl::Positional, cl::desc("<input symbols>"),
+ cl::ZeroOrMore, cl::cat(UndNameCategory));
+
+static bool msDemangle(const std::string &S) {
+ int Status;
+ MSDemangleFlags Flags = MSDF_None;
+ if (DumpBackReferences)
+ Flags = MSDemangleFlags(Flags | MSDF_DumpBackrefs);
+ if (NoAccessSpecifier)
+ Flags = MSDemangleFlags(Flags | MSDF_NoAccessSpecifier);
+ if (NoCallingConvention)
+ Flags = MSDemangleFlags(Flags | MSDF_NoCallingConvention);
+ if (NoReturnType)
+ Flags = MSDemangleFlags(Flags | MSDF_NoReturnType);
+ if (NoMemberType)
+ Flags = MSDemangleFlags(Flags | MSDF_NoMemberType);
+ if (NoVariableType)
+ Flags = MSDemangleFlags(Flags | MSDF_NoVariableType);
+
+ size_t NRead;
+ char *ResultBuf =
+ microsoftDemangle(S.c_str(), &NRead, nullptr, nullptr, &Status, Flags);
+ if (Status == llvm::demangle_success) {
+ outs() << ResultBuf << "\n";
+ outs().flush();
+ if (WarnTrailing && NRead < S.size())
+ WithColor::warning() << "trailing characters: " << S.c_str() + NRead
+ << "\n";
+ } else {
+ WithColor::error() << "Invalid mangled name\n";
+ }
+ std::free(ResultBuf);
+ return Status == llvm::demangle_success;
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ cl::HideUnrelatedOptions({&UndNameCategory, &getColorCategory()});
+ cl::ParseCommandLineOptions(argc, argv, "llvm-undname\n");
+
+ if (!RawFile.empty()) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
+ MemoryBuffer::getFileOrSTDIN(RawFile);
+ if (std::error_code EC = FileOrErr.getError()) {
+ WithColor::error() << "Could not open input file \'" << RawFile
+ << "\': " << EC.message() << '\n';
+ return 1;
+ }
+ return msDemangle(std::string(FileOrErr->get()->getBuffer())) ? 0 : 1;
+ }
+
+ bool Success = true;
+ if (Symbols.empty()) {
+ while (true) {
+ std::string LineStr;
+ std::getline(std::cin, LineStr);
+ if (std::cin.eof())
+ break;
+
+ StringRef Line(LineStr);
+ Line = Line.trim();
+ if (Line.empty() || Line.startswith("#") || Line.startswith(";"))
+ continue;
+
+ // If the user is manually typing in these decorated names, don't echo
+ // them to the terminal a second time. If they're coming from redirected
+ // input, however, then we should display the input line so that the
+ // mangled and demangled name can be easily correlated in the output.
+ if (!sys::Process::StandardInIsUserInput()) {
+ outs() << Line << "\n";
+ outs().flush();
+ }
+ if (!msDemangle(std::string(Line)))
+ Success = false;
+ outs() << "\n";
+ }
+ } else {
+ for (StringRef S : Symbols) {
+ outs() << S << "\n";
+ outs().flush();
+ if (!msDemangle(std::string(S)))
+ Success = false;
+ outs() << "\n";
+ }
+ }
+
+ return Success ? 0 : 1;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-undname/ya.make b/contrib/libs/llvm14/tools/llvm-undname/ya.make
new file mode 100644
index 00000000000..10853f467c2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-undname/ya.make
@@ -0,0 +1,27 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-undname
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ llvm-undname.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/llvm-xray/func-id-helper.cpp b/contrib/libs/llvm14/tools/llvm-xray/func-id-helper.cpp
new file mode 100644
index 00000000000..afc912a6398
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/func-id-helper.cpp
@@ -0,0 +1,75 @@
+//===- xray-fc-account.cpp: XRay Function Call Accounting Tool ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of the helper tools dealing with XRay-generated function ids.
+//
+//===----------------------------------------------------------------------===//
+
+#include "func-id-helper.h"
+#include "llvm/Support/Path.h"
+#include <sstream>
+
+using namespace llvm;
+using namespace xray;
+
+std::string FuncIdConversionHelper::SymbolOrNumber(int32_t FuncId) const {
+ auto CacheIt = CachedNames.find(FuncId);
+ if (CacheIt != CachedNames.end())
+ return CacheIt->second;
+
+ std::ostringstream F;
+ auto It = FunctionAddresses.find(FuncId);
+ if (It == FunctionAddresses.end()) {
+ F << "#" << FuncId;
+ return F.str();
+ }
+
+ object::SectionedAddress ModuleAddress;
+ ModuleAddress.Address = It->second;
+ // TODO: set proper section index here.
+ // object::SectionedAddress::UndefSection works for only absolute addresses.
+ ModuleAddress.SectionIndex = object::SectionedAddress::UndefSection;
+ if (auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, ModuleAddress)) {
+ auto &DI = *ResOrErr;
+ if (DI.FunctionName == DILineInfo::BadString)
+ F << "@(" << std::hex << It->second << ")";
+ else
+ F << DI.FunctionName;
+ } else
+ handleAllErrors(ResOrErr.takeError(), [&](const ErrorInfoBase &) {
+ F << "@(" << std::hex << It->second << ")";
+ });
+
+ auto S = F.str();
+ CachedNames[FuncId] = S;
+ return S;
+}
+
+std::string FuncIdConversionHelper::FileLineAndColumn(int32_t FuncId) const {
+ auto It = FunctionAddresses.find(FuncId);
+ if (It == FunctionAddresses.end())
+ return "(unknown)";
+
+ std::ostringstream F;
+ object::SectionedAddress ModuleAddress;
+ ModuleAddress.Address = It->second;
+ // TODO: set proper section index here.
+ // object::SectionedAddress::UndefSection works for only absolute addresses.
+ ModuleAddress.SectionIndex = object::SectionedAddress::UndefSection;
+ auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, ModuleAddress);
+ if (!ResOrErr) {
+ consumeError(ResOrErr.takeError());
+ return "(unknown)";
+ }
+
+ auto &DI = *ResOrErr;
+ F << sys::path::filename(DI.FileName).str() << ":" << DI.Line << ":"
+ << DI.Column;
+
+ return F.str();
+}
diff --git a/contrib/libs/llvm14/tools/llvm-xray/func-id-helper.h b/contrib/libs/llvm14/tools/llvm-xray/func-id-helper.h
new file mode 100644
index 00000000000..c6ce198170d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/func-id-helper.h
@@ -0,0 +1,50 @@
+//===- func-id-helper.h - XRay Function ID Conversion Helpers -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines helper tools dealing with XRay-generated function ids.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_LLVM_XRAY_FUNC_ID_HELPER_H
+#define LLVM_TOOLS_LLVM_XRAY_FUNC_ID_HELPER_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include <unordered_map>
+
+namespace llvm {
+namespace xray {
+
+// This class consolidates common operations related to Function IDs.
+class FuncIdConversionHelper {
+public:
+ using FunctionAddressMap = std::unordered_map<int32_t, uint64_t>;
+
+private:
+ std::string BinaryInstrMap;
+ symbolize::LLVMSymbolizer &Symbolizer;
+ const FunctionAddressMap &FunctionAddresses;
+ mutable llvm::DenseMap<int32_t, std::string> CachedNames;
+
+public:
+ FuncIdConversionHelper(std::string BinaryInstrMap,
+ symbolize::LLVMSymbolizer &Symbolizer,
+ const FunctionAddressMap &FunctionAddresses)
+ : BinaryInstrMap(std::move(BinaryInstrMap)), Symbolizer(Symbolizer),
+ FunctionAddresses(FunctionAddresses) {}
+
+ // Returns the symbol or a string representation of the function id.
+ std::string SymbolOrNumber(int32_t FuncId) const;
+
+ // Returns the file and column from debug info for the given function id.
+ std::string FileLineAndColumn(int32_t FuncId) const;
+};
+
+} // namespace xray
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_XRAY_FUNC_ID_HELPER_H
diff --git a/contrib/libs/llvm14/tools/llvm-xray/llvm-xray.cpp b/contrib/libs/llvm14/tools/llvm-xray/llvm-xray.cpp
new file mode 100644
index 00000000000..9ee653e97b2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/llvm-xray.cpp
@@ -0,0 +1,48 @@
+//===- llvm-xray.cpp: XRay Tool Main Program ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the main entry point for the suite of XRay tools. All
+// additional functionality are implemented as subcommands.
+//
+//===----------------------------------------------------------------------===//
+//
+// Basic usage:
+//
+// llvm-xray [options] <subcommand> [subcommand-specific options]
+//
+#include "xray-registry.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::xray;
+
+int main(int argc, char *argv[]) {
+ cl::ParseCommandLineOptions(argc, argv,
+ "XRay Tools\n\n"
+ " This program consolidates multiple XRay trace "
+ "processing tools for convenient access.\n");
+ for (auto *SC : cl::getRegisteredSubcommands()) {
+ if (*SC) {
+ // If no subcommand was provided, we need to explicitly check if this is
+ // the top-level subcommand.
+ if (SC == &*cl::TopLevelSubCommand) {
+ cl::PrintHelpMessage(false, true);
+ return 0;
+ }
+ if (auto C = dispatch(SC)) {
+ ExitOnError("llvm-xray: ")(C());
+ return 0;
+ }
+ }
+ }
+
+ // If all else fails, we still print the usage message.
+ cl::PrintHelpMessage(false, true);
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-xray/trie-node.h b/contrib/libs/llvm14/tools/llvm-xray/trie-node.h
new file mode 100644
index 00000000000..7bff81473b5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/trie-node.h
@@ -0,0 +1,91 @@
+//===- trie-node.h - XRay Call Stack Data Structure -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides a data structure and routines for working with call stacks
+// of instrumented functions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_XRAY_STACK_TRIE_H
+#define LLVM_TOOLS_LLVM_XRAY_STACK_TRIE_H
+
+#include <forward_list>
+#include <numeric>
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+
+/// A type to represent a trie of invocations. It is useful to construct a
+/// graph of these nodes from reading an XRay trace, such that each function
+/// call can be placed in a larger context.
+///
+/// The template parameter allows users of the template to attach their own
+/// data elements to each node in the invocation graph.
+template <typename AssociatedData> struct TrieNode {
+ /// The function ID.
+ int32_t FuncId;
+
+ /// The caller of this function.
+ TrieNode<AssociatedData> *Parent;
+
+ /// The callees from this function.
+ llvm::SmallVector<TrieNode<AssociatedData> *, 4> Callees;
+
+ /// Additional parameterized data on each node.
+ AssociatedData ExtraData;
+};
+
+/// Merges together two TrieNodes with like function ids, aggregating their
+/// callee lists and durations. The caller must provide storage where new merged
+/// nodes can be allocated in the form of a linked list.
+template <typename T, typename Callable>
+TrieNode<T> *
+mergeTrieNodes(const TrieNode<T> &Left, const TrieNode<T> &Right,
+ /*Non-deduced pointer type for nullptr compatibility*/
+ std::remove_reference_t<TrieNode<T> *> NewParent,
+ std::forward_list<TrieNode<T>> &NodeStore,
+ Callable &&MergeCallable) {
+ llvm::function_ref<T(const T &, const T &)> MergeFn(
+ std::forward<Callable>(MergeCallable));
+ assert(Left.FuncId == Right.FuncId);
+ NodeStore.push_front(TrieNode<T>{
+ Left.FuncId, NewParent, {}, MergeFn(Left.ExtraData, Right.ExtraData)});
+ auto I = NodeStore.begin();
+ auto *Node = &*I;
+
+ // Build a map of callees from the left side.
+ llvm::DenseMap<int32_t, TrieNode<T> *> LeftCalleesByFuncId;
+ for (auto *Callee : Left.Callees) {
+ LeftCalleesByFuncId[Callee->FuncId] = Callee;
+ }
+
+ // Iterate through the right side, either merging with the map values or
+ // directly adding to the Callees vector. The iteration also removes any
+ // merged values from the left side map.
+ // TODO: Unroll into iterative and explicit stack for efficiency.
+ for (auto *Callee : Right.Callees) {
+ auto iter = LeftCalleesByFuncId.find(Callee->FuncId);
+ if (iter != LeftCalleesByFuncId.end()) {
+ Node->Callees.push_back(
+ mergeTrieNodes(*(iter->second), *Callee, Node, NodeStore, MergeFn));
+ LeftCalleesByFuncId.erase(iter);
+ } else {
+ Node->Callees.push_back(Callee);
+ }
+ }
+
+ // Add any callees that weren't found in the right side.
+ for (auto MapPairIter : LeftCalleesByFuncId) {
+ Node->Callees.push_back(MapPairIter.second);
+ }
+
+ return Node;
+}
+
+#endif // LLVM_TOOLS_LLVM_XRAY_STACK_TRIE_H
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-account.cpp b/contrib/libs/llvm14/tools/llvm-xray/xray-account.cpp
new file mode 100644
index 00000000000..659de7507f5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-account.cpp
@@ -0,0 +1,519 @@
+//===- xray-account.h - XRay Function Call Accounting ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements basic function call accounting from an XRay trace.
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+#include <cassert>
+#include <numeric>
+#include <system_error>
+#include <utility>
+
+#include "xray-account.h"
+#include "xray-registry.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/XRay/InstrumentationMap.h"
+#include "llvm/XRay/Trace.h"
+
+using namespace llvm;
+using namespace llvm::xray;
+
+static cl::SubCommand Account("account", "Function call accounting");
+static cl::opt<std::string> AccountInput(cl::Positional,
+ cl::desc("<xray log file>"),
+ cl::Required, cl::sub(Account));
+static cl::opt<bool>
+ AccountKeepGoing("keep-going", cl::desc("Keep going on errors encountered"),
+ cl::sub(Account), cl::init(false));
+static cl::alias AccountKeepGoing2("k", cl::aliasopt(AccountKeepGoing),
+ cl::desc("Alias for -keep_going"));
+static cl::opt<bool> AccountRecursiveCallsOnly(
+ "recursive-calls-only", cl::desc("Only count the calls that are recursive"),
+ cl::sub(Account), cl::init(false));
+static cl::opt<bool> AccountDeduceSiblingCalls(
+ "deduce-sibling-calls",
+ cl::desc("Deduce sibling calls when unrolling function call stacks"),
+ cl::sub(Account), cl::init(false));
+static cl::alias
+ AccountDeduceSiblingCalls2("d", cl::aliasopt(AccountDeduceSiblingCalls),
+ cl::desc("Alias for -deduce_sibling_calls"));
+static cl::opt<std::string>
+ AccountOutput("output", cl::value_desc("output file"), cl::init("-"),
+ cl::desc("output file; use '-' for stdout"),
+ cl::sub(Account));
+static cl::alias AccountOutput2("o", cl::aliasopt(AccountOutput),
+ cl::desc("Alias for -output"));
+enum class AccountOutputFormats { TEXT, CSV };
+static cl::opt<AccountOutputFormats>
+ AccountOutputFormat("format", cl::desc("output format"),
+ cl::values(clEnumValN(AccountOutputFormats::TEXT,
+ "text", "report stats in text"),
+ clEnumValN(AccountOutputFormats::CSV, "csv",
+ "report stats in csv")),
+ cl::sub(Account));
+static cl::alias AccountOutputFormat2("f", cl::desc("Alias of -format"),
+ cl::aliasopt(AccountOutputFormat));
+
+enum class SortField {
+ FUNCID,
+ COUNT,
+ MIN,
+ MED,
+ PCT90,
+ PCT99,
+ MAX,
+ SUM,
+ FUNC,
+};
+
+static cl::opt<SortField> AccountSortOutput(
+ "sort", cl::desc("sort output by this field"), cl::value_desc("field"),
+ cl::sub(Account), cl::init(SortField::FUNCID),
+ cl::values(clEnumValN(SortField::FUNCID, "funcid", "function id"),
+ clEnumValN(SortField::COUNT, "count", "funciton call counts"),
+ clEnumValN(SortField::MIN, "min", "minimum function durations"),
+ clEnumValN(SortField::MED, "med", "median function durations"),
+ clEnumValN(SortField::PCT90, "90p", "90th percentile durations"),
+ clEnumValN(SortField::PCT99, "99p", "99th percentile durations"),
+ clEnumValN(SortField::MAX, "max", "maximum function durations"),
+ clEnumValN(SortField::SUM, "sum", "sum of call durations"),
+ clEnumValN(SortField::FUNC, "func", "function names")));
+static cl::alias AccountSortOutput2("s", cl::aliasopt(AccountSortOutput),
+ cl::desc("Alias for -sort"));
+
+enum class SortDirection {
+ ASCENDING,
+ DESCENDING,
+};
+static cl::opt<SortDirection> AccountSortOrder(
+ "sortorder", cl::desc("sort ordering"), cl::init(SortDirection::ASCENDING),
+ cl::values(clEnumValN(SortDirection::ASCENDING, "asc", "ascending"),
+ clEnumValN(SortDirection::DESCENDING, "dsc", "descending")),
+ cl::sub(Account));
+static cl::alias AccountSortOrder2("r", cl::aliasopt(AccountSortOrder),
+ cl::desc("Alias for -sortorder"));
+
+static cl::opt<int> AccountTop("top", cl::desc("only show the top N results"),
+ cl::value_desc("N"), cl::sub(Account),
+ cl::init(-1));
+static cl::alias AccountTop2("p", cl::desc("Alias for -top"),
+ cl::aliasopt(AccountTop));
+
+static cl::opt<std::string>
+ AccountInstrMap("instr_map",
+ cl::desc("binary with the instrumentation map, or "
+ "a separate instrumentation map"),
+ cl::value_desc("binary with xray_instr_map"),
+ cl::sub(Account), cl::init(""));
+static cl::alias AccountInstrMap2("m", cl::aliasopt(AccountInstrMap),
+ cl::desc("Alias for -instr_map"));
+
+namespace {
+
+template <class T, class U> void setMinMax(std::pair<T, T> &MM, U &&V) {
+ if (MM.first == 0 || MM.second == 0)
+ MM = std::make_pair(std::forward<U>(V), std::forward<U>(V));
+ else
+ MM = std::make_pair(std::min(MM.first, V), std::max(MM.second, V));
+}
+
+template <class T> T diff(T L, T R) { return std::max(L, R) - std::min(L, R); }
+
+} // namespace
+
+using RecursionStatus = LatencyAccountant::FunctionStack::RecursionStatus;
+RecursionStatus &RecursionStatus::operator++() {
+ auto Depth = Bitfield::get<RecursionStatus::Depth>(Storage);
+ assert(Depth >= 0 && Depth < std::numeric_limits<decltype(Depth)>::max());
+ ++Depth;
+ Bitfield::set<RecursionStatus::Depth>(Storage, Depth); // ++Storage
+ // Did this function just (maybe indirectly) call itself the first time?
+ if (!isRecursive() && Depth == 2) // Storage == 2 / Storage s> 1
+ Bitfield::set<RecursionStatus::IsRecursive>(Storage,
+ true); // Storage |= INT_MIN
+ return *this;
+}
+RecursionStatus &RecursionStatus::operator--() {
+ auto Depth = Bitfield::get<RecursionStatus::Depth>(Storage);
+ assert(Depth > 0);
+ --Depth;
+ Bitfield::set<RecursionStatus::Depth>(Storage, Depth); // --Storage
+ // Did we leave a function that previouly (maybe indirectly) called itself?
+ if (isRecursive() && Depth == 0) // Storage == INT_MIN
+ Bitfield::set<RecursionStatus::IsRecursive>(Storage, false); // Storage = 0
+ return *this;
+}
+bool RecursionStatus::isRecursive() const {
+ return Bitfield::get<RecursionStatus::IsRecursive>(Storage); // Storage s< 0
+}
+
+bool LatencyAccountant::accountRecord(const XRayRecord &Record) {
+ setMinMax(PerThreadMinMaxTSC[Record.TId], Record.TSC);
+ setMinMax(PerCPUMinMaxTSC[Record.CPU], Record.TSC);
+
+ if (CurrentMaxTSC == 0)
+ CurrentMaxTSC = Record.TSC;
+
+ if (Record.TSC < CurrentMaxTSC)
+ return false;
+
+ auto &ThreadStack = PerThreadFunctionStack[Record.TId];
+ if (RecursiveCallsOnly && !ThreadStack.RecursionDepth)
+ ThreadStack.RecursionDepth.emplace();
+ 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.Stack.emplace_back(Record.FuncId, Record.TSC);
+ if (ThreadStack.RecursionDepth)
+ ++(*ThreadStack.RecursionDepth)[Record.FuncId];
+ break;
+ }
+ case RecordTypes::EXIT:
+ case RecordTypes::TAIL_EXIT: {
+ if (ThreadStack.Stack.empty())
+ return false;
+
+ if (ThreadStack.Stack.back().first == Record.FuncId) {
+ const auto &Top = ThreadStack.Stack.back();
+ if (!ThreadStack.RecursionDepth ||
+ (*ThreadStack.RecursionDepth)[Top.first].isRecursive())
+ recordLatency(Top.first, diff(Top.second, Record.TSC));
+ if (ThreadStack.RecursionDepth)
+ --(*ThreadStack.RecursionDepth)[Top.first];
+ ThreadStack.Stack.pop_back();
+ break;
+ }
+
+ if (!DeduceSiblingCalls)
+ return false;
+
+ // Look for the parent up the stack.
+ auto Parent =
+ std::find_if(ThreadStack.Stack.rbegin(), ThreadStack.Stack.rend(),
+ [&](const std::pair<const int32_t, uint64_t> &E) {
+ return E.first == Record.FuncId;
+ });
+ if (Parent == ThreadStack.Stack.rend())
+ return false;
+
+ // Account time for this apparently sibling call exit up the stack.
+ // Considering the following case:
+ //
+ // f()
+ // g()
+ // h()
+ //
+ // We might only ever see the following entries:
+ //
+ // -> f()
+ // -> g()
+ // -> h()
+ // <- h()
+ // <- f()
+ //
+ // Now we don't see the exit to g() because some older version of the XRay
+ // runtime wasn't instrumenting tail exits. If we don't deduce tail calls,
+ // we may potentially never account time for g() -- and this code would have
+ // already bailed out, because `<- f()` doesn't match the current "top" of
+ // stack where we're waiting for the exit to `g()` instead. This is not
+ // ideal and brittle -- so instead we provide a potentially inaccurate
+ // accounting of g() instead, computing it from the exit of f().
+ //
+ // While it might be better that we account the time between `-> g()` and
+ // `-> h()` as the proper accounting of time for g() here, this introduces
+ // complexity to do correctly (need to backtrack, etc.).
+ //
+ // FIXME: Potentially implement the more complex deduction algorithm?
+ auto R = make_range(std::next(Parent).base(), ThreadStack.Stack.end());
+ for (auto &E : R) {
+ if (!ThreadStack.RecursionDepth ||
+ (*ThreadStack.RecursionDepth)[E.first].isRecursive())
+ recordLatency(E.first, diff(E.second, Record.TSC));
+ }
+ for (auto &Top : reverse(R)) {
+ if (ThreadStack.RecursionDepth)
+ --(*ThreadStack.RecursionDepth)[Top.first];
+ ThreadStack.Stack.pop_back();
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+namespace {
+
+// We consolidate the data into a struct which we can output in various forms.
+struct ResultRow {
+ uint64_t Count;
+ double Min;
+ double Median;
+ double Pct90;
+ double Pct99;
+ double Max;
+ double Sum;
+ std::string DebugInfo;
+ std::string Function;
+};
+
+ResultRow getStats(MutableArrayRef<uint64_t> Timings) {
+ assert(!Timings.empty());
+ ResultRow R;
+ R.Sum = std::accumulate(Timings.begin(), Timings.end(), 0.0);
+ auto MinMax = std::minmax_element(Timings.begin(), Timings.end());
+ R.Min = *MinMax.first;
+ R.Max = *MinMax.second;
+ R.Count = Timings.size();
+
+ auto MedianOff = Timings.size() / 2;
+ std::nth_element(Timings.begin(), Timings.begin() + MedianOff, Timings.end());
+ R.Median = Timings[MedianOff];
+
+ size_t Pct90Off = std::floor(Timings.size() * 0.9);
+ std::nth_element(Timings.begin(), Timings.begin() + (uint64_t)Pct90Off,
+ Timings.end());
+ R.Pct90 = Timings[Pct90Off];
+
+ size_t Pct99Off = std::floor(Timings.size() * 0.99);
+ std::nth_element(Timings.begin(), Timings.begin() + (uint64_t)Pct99Off,
+ Timings.end());
+ R.Pct99 = Timings[Pct99Off];
+ return R;
+}
+
+} // 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 {
+ std::vector<TupleType> Results;
+ Results.reserve(FunctionLatencies.size());
+ for (auto FT : FunctionLatencies) {
+ const auto &FuncId = FT.first;
+ auto &Timings = FT.second;
+ Results.emplace_back(FuncId, Timings.size(), getStats(Timings));
+ auto &Row = std::get<2>(Results.back());
+ if (Header.CycleFrequency) {
+ double CycleFrequency = Header.CycleFrequency;
+ Row.Min /= CycleFrequency;
+ Row.Median /= CycleFrequency;
+ Row.Pct90 /= CycleFrequency;
+ Row.Pct99 /= CycleFrequency;
+ Row.Max /= CycleFrequency;
+ Row.Sum /= CycleFrequency;
+ }
+
+ Row.Function = FuncIdHelper.SymbolOrNumber(FuncId);
+ Row.DebugInfo = FuncIdHelper.FileLineAndColumn(FuncId);
+ }
+
+ // Sort the data according to user-provided flags.
+ switch (AccountSortOutput) {
+ case SortField::FUNCID:
+ sortByKey(Results, [](const TupleType &X) { return std::get<0>(X); });
+ break;
+ case SortField::COUNT:
+ 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;
+ 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) {
+ 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));
+}
+
+void LatencyAccountant::exportStatsAsText(raw_ostream &OS,
+ const XRayFileHeader &Header) const {
+ OS << "Functions with latencies: " << FunctionLatencies.size() << "\n";
+
+ // We spend some effort to make the text output more readable, so we do the
+ // following formatting decisions for each of the fields:
+ //
+ // - funcid: 32-bit, but we can determine the largest number and be
+ // between
+ // a minimum of 5 characters, up to 9 characters, right aligned.
+ // - count: 64-bit, but we can determine the largest number and be
+ // between
+ // a minimum of 5 characters, up to 9 characters, right aligned.
+ // - min, median, 90pct, 99pct, max: double precision, but we want to keep
+ // the values in seconds, with microsecond precision (0.000'001), so we
+ // have at most 6 significant digits, with the whole number part to be
+ // at
+ // least 1 character. For readability we'll right-align, with full 9
+ // characters each.
+ // - debug info, function name: we format this as a concatenation of the
+ // debug info and the function name.
+ //
+ static constexpr char StatsHeaderFormat[] =
+ "{0,+9} {1,+10} [{2,+9}, {3,+9}, {4,+9}, {5,+9}, {6,+9}] {7,+9}";
+ static constexpr char StatsFormat[] =
+ R"({0,+9} {1,+10} [{2,+9:f6}, {3,+9:f6}, {4,+9:f6}, {5,+9:f6}, {6,+9:f6}] {7,+9:f6})";
+ OS << llvm::formatv(StatsHeaderFormat, "funcid", "count", "min", "med", "90p",
+ "99p", "max", "sum")
+ << llvm::formatv(" {0,-12}\n", "function");
+ exportStats(Header, [&](int32_t FuncId, size_t Count, const ResultRow &Row) {
+ OS << llvm::formatv(StatsFormat, FuncId, Count, Row.Min, Row.Median,
+ Row.Pct90, Row.Pct99, Row.Max, Row.Sum)
+ << " " << Row.DebugInfo << ": " << Row.Function << "\n";
+ });
+}
+
+void LatencyAccountant::exportStatsAsCSV(raw_ostream &OS,
+ const XRayFileHeader &Header) const {
+ OS << "funcid,count,min,median,90%ile,99%ile,max,sum,debug,function\n";
+ exportStats(Header, [&](int32_t FuncId, size_t Count, const ResultRow &Row) {
+ OS << FuncId << ',' << Count << ',' << Row.Min << ',' << Row.Median << ','
+ << Row.Pct90 << ',' << Row.Pct99 << ',' << Row.Max << "," << Row.Sum
+ << ",\"" << Row.DebugInfo << "\",\"" << Row.Function << "\"\n";
+ });
+}
+
+using namespace llvm::xray;
+
+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;
+ case RecordTypes::CUSTOM_EVENT:
+ Stream << "custom-event";
+ break;
+ case RecordTypes::TYPED_EVENT:
+ Stream << "typed-event";
+ break;
+ }
+ }
+};
+} // namespace llvm
+
+static CommandRegistration Unused(&Account, []() -> Error {
+ InstrumentationMap Map;
+ if (!AccountInstrMap.empty()) {
+ auto InstrumentationMapOrError = loadInstrumentationMap(AccountInstrMap);
+ if (!InstrumentationMapOrError)
+ return joinErrors(make_error<StringError>(
+ Twine("Cannot open instrumentation map '") +
+ AccountInstrMap + "'",
+ std::make_error_code(std::errc::invalid_argument)),
+ InstrumentationMapOrError.takeError());
+ Map = std::move(*InstrumentationMapOrError);
+ }
+
+ std::error_code EC;
+ raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF);
+ if (EC)
+ return make_error<StringError>(
+ Twine("Cannot open file '") + AccountOutput + "' for writing.", EC);
+
+ const auto &FunctionAddresses = Map.getFunctionAddresses();
+ symbolize::LLVMSymbolizer Symbolizer;
+ llvm::xray::FuncIdConversionHelper FuncIdHelper(AccountInstrMap, Symbolizer,
+ FunctionAddresses);
+ xray::LatencyAccountant FCA(FuncIdHelper, AccountRecursiveCallsOnly,
+ AccountDeduceSiblingCalls);
+ auto TraceOrErr = loadTraceFile(AccountInput);
+ if (!TraceOrErr)
+ return joinErrors(
+ make_error<StringError>(
+ Twine("Failed loading input file '") + AccountInput + "'",
+ std::make_error_code(std::errc::executable_format_error)),
+ TraceOrErr.takeError());
+
+ auto &T = *TraceOrErr;
+ for (const auto &Record : T) {
+ if (FCA.accountRecord(Record))
+ continue;
+ errs()
+ << "Error processing record: "
+ << llvm::formatv(
+ R"({{type: {0}; cpu: {1}; record-type: {2}; function-id: {3}; tsc: {4}; thread-id: {5}; process-id: {6}}})",
+ Record.RecordType, Record.CPU, Record.Type, Record.FuncId,
+ Record.TSC, Record.TId, Record.PId)
+ << '\n';
+ for (const auto &ThreadStack : FCA.getPerThreadFunctionStack()) {
+ errs() << "Thread ID: " << ThreadStack.first << "\n";
+ if (ThreadStack.second.Stack.empty()) {
+ errs() << " (empty stack)\n";
+ continue;
+ }
+ auto Level = ThreadStack.second.Stack.size();
+ for (const auto &Entry : llvm::reverse(ThreadStack.second.Stack))
+ errs() << " #" << Level-- << "\t"
+ << FuncIdHelper.SymbolOrNumber(Entry.first) << '\n';
+ }
+ if (!AccountKeepGoing)
+ return make_error<StringError>(
+ Twine("Failed accounting function calls in file '") + AccountInput +
+ "'.",
+ std::make_error_code(std::errc::executable_format_error));
+ }
+ switch (AccountOutputFormat) {
+ case AccountOutputFormats::TEXT:
+ FCA.exportStatsAsText(OS, T.getFileHeader());
+ break;
+ case AccountOutputFormats::CSV:
+ FCA.exportStatsAsCSV(OS, T.getFileHeader());
+ break;
+ }
+
+ return Error::success();
+});
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-account.h b/contrib/libs/llvm14/tools/llvm-xray/xray-account.h
new file mode 100644
index 00000000000..371a9cc708e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-account.h
@@ -0,0 +1,115 @@
+//===- xray-account.h - XRay Function Call Accounting ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the interface for performing some basic function call
+// accounting from an XRay trace.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_ACCOUNT_H
+#define LLVM_TOOLS_LLVM_XRAY_XRAY_ACCOUNT_H
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "func-id-helper.h"
+#include "llvm/ADT/Bitfields.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/XRay/XRayRecord.h"
+
+namespace llvm {
+namespace xray {
+
+class LatencyAccountant {
+public:
+ typedef llvm::DenseMap<int32_t, llvm::SmallVector<uint64_t, 0>>
+ FunctionLatencyMap;
+ typedef llvm::DenseMap<uint32_t, std::pair<uint64_t, uint64_t>>
+ PerThreadMinMaxTSCMap;
+ typedef llvm::DenseMap<uint8_t, std::pair<uint64_t, uint64_t>>
+ PerCPUMinMaxTSCMap;
+ struct FunctionStack {
+ llvm::SmallVector<std::pair<int32_t, uint64_t>, 32> Stack;
+ class RecursionStatus {
+ uint32_t Storage = 0;
+ using Depth = Bitfield::Element<int32_t, 0, 31>; // Low 31 bits.
+ using IsRecursive = Bitfield::Element<bool, 31, 1>; // Sign bit.
+ public:
+ RecursionStatus &operator++();
+ RecursionStatus &operator--();
+ bool isRecursive() const;
+ };
+ Optional<llvm::DenseMap<int32_t, RecursionStatus>> RecursionDepth;
+ };
+ typedef llvm::DenseMap<uint32_t, FunctionStack> PerThreadFunctionStackMap;
+
+private:
+ PerThreadFunctionStackMap PerThreadFunctionStack;
+ FunctionLatencyMap FunctionLatencies;
+ PerThreadMinMaxTSCMap PerThreadMinMaxTSC;
+ PerCPUMinMaxTSCMap PerCPUMinMaxTSC;
+ FuncIdConversionHelper &FuncIdHelper;
+
+ bool RecursiveCallsOnly = false;
+ bool DeduceSiblingCalls = false;
+ uint64_t CurrentMaxTSC = 0;
+
+ void recordLatency(int32_t FuncId, uint64_t Latency) {
+ FunctionLatencies[FuncId].push_back(Latency);
+ }
+
+public:
+ explicit LatencyAccountant(FuncIdConversionHelper &FuncIdHelper,
+ bool RecursiveCallsOnly, bool DeduceSiblingCalls)
+ : FuncIdHelper(FuncIdHelper), RecursiveCallsOnly(RecursiveCallsOnly),
+ DeduceSiblingCalls(DeduceSiblingCalls) {}
+
+ const FunctionLatencyMap &getFunctionLatencies() const {
+ return FunctionLatencies;
+ }
+
+ const PerThreadMinMaxTSCMap &getPerThreadMinMaxTSC() const {
+ return PerThreadMinMaxTSC;
+ }
+
+ const PerCPUMinMaxTSCMap &getPerCPUMinMaxTSC() const {
+ return PerCPUMinMaxTSC;
+ }
+
+ /// Returns false in case we fail to account the provided record. This happens
+ /// in the following cases:
+ ///
+ /// - An exit record does not match any entry records for the same function.
+ /// If we've been set to deduce sibling calls, we try walking up the stack
+ /// and recording times for the higher level functions.
+ /// - A record has a TSC that's before the latest TSC that has been
+ /// recorded. We still record the TSC for the min-max.
+ ///
+ bool accountRecord(const XRayRecord &Record);
+
+ const PerThreadFunctionStackMap &getPerThreadFunctionStack() const {
+ return PerThreadFunctionStack;
+ }
+
+ // Output Functions
+ // ================
+
+ void exportStatsAsText(raw_ostream &OS, const XRayFileHeader &Header) const;
+ void exportStatsAsCSV(raw_ostream &OS, const XRayFileHeader &Header) const;
+
+private:
+ // Internal helper to implement common parts of the exportStatsAs...
+ // functions.
+ template <class F> void exportStats(const XRayFileHeader &Header, F fn) const;
+};
+
+} // namespace xray
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_ACCOUNT_H
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-color-helper.cpp b/contrib/libs/llvm14/tools/llvm-xray/xray-color-helper.cpp
new file mode 100644
index 00000000000..b2ed63881bd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-color-helper.cpp
@@ -0,0 +1,222 @@
+//===-- xray-graph.cpp: XRay Function Call Graph Renderer -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A class to get a color from a specified gradient.
+//
+//===----------------------------------------------------------------------===//
+
+#include "xray-color-helper.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cmath>
+
+using namespace llvm;
+using namespace xray;
+
+// Sequential ColorMaps, which are used to represent information
+// from some minimum to some maximum.
+
+const std::tuple<uint8_t, uint8_t, uint8_t> SequentialMaps[][9] = {
+ {// The greys color scheme from http://colorbrewer2.org/
+ std::make_tuple(255, 255, 255), std::make_tuple(240, 240, 240),
+ std::make_tuple(217, 217, 217), std::make_tuple(189, 189, 189),
+ std::make_tuple(150, 150, 150), std::make_tuple(115, 115, 115),
+ std::make_tuple(82, 82, 82), std::make_tuple(37, 37, 37),
+ std::make_tuple(0, 0, 0)},
+ {// The OrRd color scheme from http://colorbrewer2.org/
+ std::make_tuple(255, 247, 236), std::make_tuple(254, 232, 200),
+ std::make_tuple(253, 212, 158), std::make_tuple(253, 187, 132),
+ std::make_tuple(252, 141, 89), std::make_tuple(239, 101, 72),
+ std::make_tuple(215, 48, 31), std::make_tuple(179, 0, 0),
+ std::make_tuple(127, 0, 0)},
+ {// The PuBu color scheme from http://colorbrewer2.org/
+ std::make_tuple(255, 247, 251), std::make_tuple(236, 231, 242),
+ std::make_tuple(208, 209, 230), std::make_tuple(166, 189, 219),
+ std::make_tuple(116, 169, 207), std::make_tuple(54, 144, 192),
+ std::make_tuple(5, 112, 176), std::make_tuple(4, 90, 141),
+ std::make_tuple(2, 56, 88)}};
+
+// Sequential Maps extend the last colors given out of range inputs.
+const std::tuple<uint8_t, uint8_t, uint8_t> SequentialBounds[][2] = {
+ {// The Bounds for the greys color scheme
+ std::make_tuple(255, 255, 255), std::make_tuple(0, 0, 0)},
+ {// The Bounds for the OrRd color Scheme
+ std::make_tuple(255, 247, 236), std::make_tuple(127, 0, 0)},
+ {// The Bounds for the PuBu color Scheme
+ std::make_tuple(255, 247, 251), std::make_tuple(2, 56, 88)}};
+
+ColorHelper::ColorHelper(ColorHelper::SequentialScheme S)
+ : MinIn(0.0), MaxIn(1.0), ColorMap(SequentialMaps[static_cast<int>(S)]),
+ BoundMap(SequentialBounds[static_cast<int>(S)]) {}
+
+// Diverging ColorMaps, which are used to represent information
+// representing differenes, or a range that goes from negative to positive.
+// These take an input in the range [-1,1].
+
+const std::tuple<uint8_t, uint8_t, uint8_t> DivergingCoeffs[][11] = {
+ {// The PiYG color scheme from http://colorbrewer2.org/
+ std::make_tuple(142, 1, 82), std::make_tuple(197, 27, 125),
+ std::make_tuple(222, 119, 174), std::make_tuple(241, 182, 218),
+ std::make_tuple(253, 224, 239), std::make_tuple(247, 247, 247),
+ std::make_tuple(230, 245, 208), std::make_tuple(184, 225, 134),
+ std::make_tuple(127, 188, 65), std::make_tuple(77, 146, 33),
+ std::make_tuple(39, 100, 25)}};
+
+// Diverging maps use out of bounds ranges to show missing data. Missing Right
+// Being below min, and missing left being above max.
+const std::tuple<uint8_t, uint8_t, uint8_t> DivergingBounds[][2] = {
+ {// The PiYG color scheme has green and red for missing right and left
+ // respectively.
+ std::make_tuple(255, 0, 0), std::make_tuple(0, 255, 0)}};
+
+ColorHelper::ColorHelper(ColorHelper::DivergingScheme S)
+ : MinIn(-1.0), MaxIn(1.0), ColorMap(DivergingCoeffs[static_cast<int>(S)]),
+ BoundMap(DivergingBounds[static_cast<int>(S)]) {}
+
+// Takes a tuple of uint8_ts representing a color in RGB and converts them to
+// HSV represented by a tuple of doubles
+static std::tuple<double, double, double>
+convertToHSV(const std::tuple<uint8_t, uint8_t, uint8_t> &Color) {
+ double Scaled[3] = {std::get<0>(Color) / 255.0, std::get<1>(Color) / 255.0,
+ std::get<2>(Color) / 255.0};
+ int Min = 0;
+ int Max = 0;
+ for (int i = 1; i < 3; ++i) {
+ if (Scaled[i] < Scaled[Min])
+ Min = i;
+ if (Scaled[i] > Scaled[Max])
+ Max = i;
+ }
+
+ double C = Scaled[Max] - Scaled[Min];
+
+ double HPrime =
+ (C == 0) ? 0 : (Scaled[(Max + 1) % 3] - Scaled[(Max + 2) % 3]) / C;
+ HPrime = HPrime + 2.0 * Max;
+
+ double H = (HPrime < 0) ? (HPrime + 6.0) * 60
+ : HPrime * 60; // Scale to between 0 and 360
+ double V = Scaled[Max];
+
+ double S = (V == 0.0) ? 0.0 : C / V;
+
+ return std::make_tuple(H, S, V);
+}
+
+// Takes a double precision number, clips it between 0 and 1 and then converts
+// that to an integer between 0x00 and 0xFF with proxpper rounding.
+static uint8_t unitIntervalTo8BitChar(double B) {
+ double n = std::max(std::min(B, 1.0), 0.0);
+ return static_cast<uint8_t>(255 * n + 0.5);
+}
+
+// Takes a typle of doubles representing a color in HSV and converts them to
+// RGB represented as a tuple of uint8_ts
+static std::tuple<uint8_t, uint8_t, uint8_t>
+convertToRGB(const std::tuple<double, double, double> &Color) {
+ const double &H = std::get<0>(Color);
+ const double &S = std::get<1>(Color);
+ const double &V = std::get<2>(Color);
+
+ double C = V * S;
+
+ double HPrime = H / 60;
+ double X = C * (1 - std::abs(std::fmod(HPrime, 2.0) - 1));
+
+ double RGB1[3];
+ int HPrimeInt = static_cast<int>(HPrime);
+ if (HPrimeInt % 2 == 0) {
+ RGB1[(HPrimeInt / 2) % 3] = C;
+ RGB1[(HPrimeInt / 2 + 1) % 3] = X;
+ RGB1[(HPrimeInt / 2 + 2) % 3] = 0.0;
+ } else {
+ RGB1[(HPrimeInt / 2) % 3] = X;
+ RGB1[(HPrimeInt / 2 + 1) % 3] = C;
+ RGB1[(HPrimeInt / 2 + 2) % 3] = 0.0;
+ }
+
+ double Min = V - C;
+ double RGB2[3] = {RGB1[0] + Min, RGB1[1] + Min, RGB1[2] + Min};
+
+ return std::make_tuple(unitIntervalTo8BitChar(RGB2[0]),
+ unitIntervalTo8BitChar(RGB2[1]),
+ unitIntervalTo8BitChar(RGB2[2]));
+}
+
+// The Hue component of the HSV interpolation Routine
+static double interpolateHue(double H0, double H1, double T) {
+ double D = H1 - H0;
+ if (H0 > H1) {
+ std::swap(H0, H1);
+
+ D = -D;
+ T = 1 - T;
+ }
+
+ if (D <= 180) {
+ return H0 + T * (H1 - H0);
+ } else {
+ H0 = H0 + 360;
+ return std::fmod(H0 + T * (H1 - H0) + 720, 360);
+ }
+}
+
+// Interpolates between two HSV Colors both represented as a tuple of doubles
+// Returns an HSV Color represented as a tuple of doubles
+static std::tuple<double, double, double>
+interpolateHSV(const std::tuple<double, double, double> &C0,
+ const std::tuple<double, double, double> &C1, double T) {
+ double H = interpolateHue(std::get<0>(C0), std::get<0>(C1), T);
+ double S = std::get<1>(C0) + T * (std::get<1>(C1) - std::get<1>(C0));
+ double V = std::get<2>(C0) + T * (std::get<2>(C1) - std::get<2>(C0));
+ return std::make_tuple(H, S, V);
+}
+
+// Get the Color as a tuple of uint8_ts
+std::tuple<uint8_t, uint8_t, uint8_t>
+ColorHelper::getColorTuple(double Point) const {
+ assert(!ColorMap.empty() && "ColorMap must not be empty!");
+ assert(!BoundMap.empty() && "BoundMap must not be empty!");
+
+ if (Point < MinIn)
+ return BoundMap[0];
+ if (Point > MaxIn)
+ return BoundMap[1];
+
+ size_t MaxIndex = ColorMap.size() - 1;
+ double IntervalWidth = MaxIn - MinIn;
+ double OffsetP = Point - MinIn;
+ double SectionWidth = IntervalWidth / static_cast<double>(MaxIndex);
+ size_t SectionNo = std::floor(OffsetP / SectionWidth);
+ double T = (OffsetP - SectionNo * SectionWidth) / SectionWidth;
+
+ auto &RGBColor0 = ColorMap[SectionNo];
+ auto &RGBColor1 = ColorMap[std::min(SectionNo + 1, MaxIndex)];
+
+ auto HSVColor0 = convertToHSV(RGBColor0);
+ auto HSVColor1 = convertToHSV(RGBColor1);
+
+ auto InterpolatedHSVColor = interpolateHSV(HSVColor0, HSVColor1, T);
+ return convertToRGB(InterpolatedHSVColor);
+}
+
+// A helper method to convert a color represented as tuple of uint8s to a hex
+// string.
+std::string
+ColorHelper::getColorString(std::tuple<uint8_t, uint8_t, uint8_t> t) {
+ return std::string(llvm::formatv("#{0:X-2}{1:X-2}{2:X-2}", std::get<0>(t),
+ std::get<1>(t), std::get<2>(t)));
+}
+
+// Gets a color in a gradient given a number in the interval [0,1], it does this
+// by evaluating a polynomial which maps [0, 1] -> [0, 1] for each of the R G
+// and B values in the color. It then converts this [0,1] colors to a 24 bit
+// color as a hex string.
+std::string ColorHelper::getColorString(double Point) const {
+ return getColorString(getColorTuple(Point));
+}
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-color-helper.h b/contrib/libs/llvm14/tools/llvm-xray/xray-color-helper.h
new file mode 100644
index 00000000000..3141e90cc89
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-color-helper.h
@@ -0,0 +1,87 @@
+//===-- xray-graph.h - XRay Function Call Graph Renderer --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A class to get a color from a specified gradient.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef XRAY_COLOR_HELPER_H
+#define XRAY_COLOR_HELPER_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include <tuple>
+
+namespace llvm {
+namespace xray {
+
+/// The color helper class it a healper class which allows you to easily get a
+/// color in a gradient. This is used to color-code edges in XRay-Graph tools.
+///
+/// There are two types of color schemes in this class:
+/// - Sequential schemes, which are used to represent information from some
+/// minimum to some maximum. These take an input in the range [0,1]
+/// - Diverging schemes, which are used to represent information representing
+/// differenes, or a range that goes from negative to positive. These take
+/// an input in the range [-1,1].
+/// Usage;
+/// ColorHelper S(ColorHelper::SequentialScheme::OrRd); //Chose a color scheme.
+/// for (double p = 0.0; p <= 1; p += 0.1){
+/// cout() << S.getColor(p) << " \n"; // Sample the gradient at 0.1 intervals
+/// }
+///
+/// ColorHelper D(ColorHelper::DivergingScheme::Spectral); // Choose a color
+/// // scheme.
+/// for (double p= -1; p <= 1 ; p += 0.1){
+/// cout() << D.getColor(p) << " \n"; // sample the gradient at 0.1 intervals
+/// }
+class ColorHelper {
+ double MinIn;
+ double MaxIn;
+
+ ArrayRef<std::tuple<uint8_t, uint8_t, uint8_t>> ColorMap;
+ ArrayRef<std::tuple<uint8_t, uint8_t, uint8_t>> BoundMap;
+
+public:
+ /// Enum of the availible Sequential Color Schemes
+ enum class SequentialScheme {
+ // Schemes based on the ColorBrewer Color schemes of the same name from
+ // http://www.colorbrewer.org/ by Cynthis A Brewer Penn State University.
+ Greys,
+ OrRd,
+ PuBu
+ };
+
+ ColorHelper(SequentialScheme S);
+
+ /// Enum of the availible Diverging Color Schemes
+ enum class DivergingScheme {
+ // Schemes based on the ColorBrewer Color schemes of the same name from
+ // http://www.colorbrewer.org/ by Cynthis A Brewer Penn State University.
+ PiYG
+ };
+
+ ColorHelper(DivergingScheme S);
+
+ // Sample the gradient at the input point.
+ std::tuple<uint8_t, uint8_t, uint8_t> getColorTuple(double Point) const;
+
+ std::string getColorString(double Point) const;
+
+ // Get the Default color, at the moment allways black.
+ std::tuple<uint8_t, uint8_t, uint8_t> getDefaultColorTuple() const {
+ return std::make_tuple(0, 0, 0);
+ }
+
+ std::string getDefaultColorString() const { return "black"; }
+
+ // Convert a tuple to a string
+ static std::string getColorString(std::tuple<uint8_t, uint8_t, uint8_t> t);
+};
+} // namespace xray
+} // namespace llvm
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-converter.cpp b/contrib/libs/llvm14/tools/llvm-xray/xray-converter.cpp
new file mode 100644
index 00000000000..82d0261ec4d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-converter.cpp
@@ -0,0 +1,425 @@
+//===- xray-converter.cpp: XRay Trace Conversion --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the trace conversion functions.
+//
+//===----------------------------------------------------------------------===//
+#include "xray-converter.h"
+
+#include "trie-node.h"
+#include "xray-registry.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/XRay/InstrumentationMap.h"
+#include "llvm/XRay/Trace.h"
+#include "llvm/XRay/YAMLXRayRecord.h"
+
+using namespace llvm;
+using namespace xray;
+
+// llvm-xray convert
+// ----------------------------------------------------------------------------
+static cl::SubCommand Convert("convert", "Trace Format Conversion");
+static cl::opt<std::string> ConvertInput(cl::Positional,
+ cl::desc("<xray log file>"),
+ cl::Required, cl::sub(Convert));
+enum class ConvertFormats { BINARY, YAML, CHROME_TRACE_EVENT };
+static cl::opt<ConvertFormats> ConvertOutputFormat(
+ "output-format", cl::desc("output format"),
+ cl::values(clEnumValN(ConvertFormats::BINARY, "raw", "output in binary"),
+ clEnumValN(ConvertFormats::YAML, "yaml", "output in yaml"),
+ clEnumValN(ConvertFormats::CHROME_TRACE_EVENT, "trace_event",
+ "Output in chrome's trace event format. "
+ "May be visualized with the Catapult trace viewer.")),
+ cl::sub(Convert));
+static cl::alias ConvertOutputFormat2("f", cl::aliasopt(ConvertOutputFormat),
+ cl::desc("Alias for -output-format"));
+static cl::opt<std::string>
+ ConvertOutput("output", cl::value_desc("output file"), cl::init("-"),
+ cl::desc("output file; use '-' for stdout"),
+ cl::sub(Convert));
+static cl::alias ConvertOutput2("o", cl::aliasopt(ConvertOutput),
+ cl::desc("Alias for -output"));
+
+static cl::opt<bool>
+ ConvertSymbolize("symbolize",
+ cl::desc("symbolize function ids from the input log"),
+ cl::init(false), cl::sub(Convert));
+static cl::alias ConvertSymbolize2("y", cl::aliasopt(ConvertSymbolize),
+ cl::desc("Alias for -symbolize"));
+static cl::opt<bool>
+ NoDemangle("no-demangle",
+ cl::desc("determines whether to demangle function name "
+ "when symbolizing function ids from the input log"),
+ cl::init(false), cl::sub(Convert));
+
+static cl::opt<bool> Demangle("demangle",
+ cl::desc("demangle symbols (default)"),
+ cl::sub(Convert));
+
+static cl::opt<std::string>
+ ConvertInstrMap("instr_map",
+ cl::desc("binary with the instrumentation map, or "
+ "a separate instrumentation map"),
+ cl::value_desc("binary with xray_instr_map"),
+ cl::sub(Convert), cl::init(""));
+static cl::alias ConvertInstrMap2("m", cl::aliasopt(ConvertInstrMap),
+ cl::desc("Alias for -instr_map"));
+static cl::opt<bool> ConvertSortInput(
+ "sort",
+ cl::desc("determines whether to sort input log records by timestamp"),
+ cl::sub(Convert), cl::init(true));
+static cl::alias ConvertSortInput2("s", cl::aliasopt(ConvertSortInput),
+ cl::desc("Alias for -sort"));
+
+using llvm::yaml::Output;
+
+void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) {
+ YAMLXRayTrace Trace;
+ const auto &FH = Records.getFileHeader();
+ Trace.Header = {FH.Version, FH.Type, FH.ConstantTSC, FH.NonstopTSC,
+ FH.CycleFrequency};
+ Trace.Records.reserve(Records.size());
+ for (const auto &R : Records) {
+ 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.Data});
+ }
+ Output Out(OS, nullptr, 0);
+ Out.setWriteDefaultValues(false);
+ Out << Trace;
+}
+
+void TraceConverter::exportAsRAWv1(const Trace &Records, raw_ostream &OS) {
+ // First write out the file header, in the correct endian-appropriate format
+ // (XRay assumes currently little endian).
+ support::endian::Writer Writer(OS, support::endianness::little);
+ const auto &FH = Records.getFileHeader();
+ Writer.write(FH.Version);
+ Writer.write(FH.Type);
+ uint32_t Bitfield{0};
+ if (FH.ConstantTSC)
+ Bitfield |= 1uL;
+ if (FH.NonstopTSC)
+ Bitfield |= 1uL << 1;
+ Writer.write(Bitfield);
+ Writer.write(FH.CycleFrequency);
+
+ // There's 16 bytes of padding at the end of the file header.
+ static constexpr uint32_t Padding4B = 0;
+ Writer.write(Padding4B);
+ Writer.write(Padding4B);
+ Writer.write(Padding4B);
+ Writer.write(Padding4B);
+
+ // Then write out the rest of the records, still in an endian-appropriate
+ // format.
+ for (const auto &R : Records) {
+ 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);
+ Writer.write(R.TId);
+
+ if (FH.Version >= 3)
+ Writer.write(R.PId);
+ else
+ Writer.write(Padding4B);
+
+ Writer.write(Padding4B);
+ Writer.write(Padding4B);
+ }
+}
+
+namespace {
+
+// A structure that allows building a dictionary of stack ids for the Chrome
+// trace event format.
+struct StackIdData {
+ // Each Stack of function calls has a unique ID.
+ unsigned id;
+
+ // Bookkeeping so that IDs can be maintained uniquely across threads.
+ // Traversal keeps sibling pointers to other threads stacks. This is helpful
+ // to determine when a thread encounters a new stack and should assign a new
+ // unique ID.
+ SmallVector<TrieNode<StackIdData> *, 4> siblings;
+};
+
+using StackTrieNode = TrieNode<StackIdData>;
+
+// A helper function to find the sibling nodes for an encountered function in a
+// thread of execution. Relies on the invariant that each time a new node is
+// traversed in a thread, sibling bidirectional pointers are maintained.
+SmallVector<StackTrieNode *, 4>
+findSiblings(StackTrieNode *parent, int32_t FnId, uint32_t TId,
+ const DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>>
+ &StackRootsByThreadId) {
+
+ SmallVector<StackTrieNode *, 4> Siblings{};
+
+ if (parent == nullptr) {
+ for (auto map_iter : StackRootsByThreadId) {
+ // Only look for siblings in other threads.
+ if (map_iter.first != TId)
+ for (auto node_iter : map_iter.second) {
+ if (node_iter->FuncId == FnId)
+ Siblings.push_back(node_iter);
+ }
+ }
+ return Siblings;
+ }
+
+ for (auto *ParentSibling : parent->ExtraData.siblings)
+ for (auto node_iter : ParentSibling->Callees)
+ if (node_iter->FuncId == FnId)
+ Siblings.push_back(node_iter);
+
+ return Siblings;
+}
+
+// Given a function being invoked in a thread with id TId, finds and returns the
+// StackTrie representing the function call stack. If no node exists, creates
+// the node. Assigns unique IDs to stacks newly encountered among all threads
+// and keeps sibling links up to when creating new nodes.
+StackTrieNode *findOrCreateStackNode(
+ StackTrieNode *Parent, int32_t FuncId, uint32_t TId,
+ DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> &StackRootsByThreadId,
+ DenseMap<unsigned, StackTrieNode *> &StacksByStackId, unsigned *id_counter,
+ std::forward_list<StackTrieNode> &NodeStore) {
+ SmallVector<StackTrieNode *, 4> &ParentCallees =
+ Parent == nullptr ? StackRootsByThreadId[TId] : Parent->Callees;
+ auto match = find_if(ParentCallees, [FuncId](StackTrieNode *ParentCallee) {
+ return FuncId == ParentCallee->FuncId;
+ });
+ if (match != ParentCallees.end())
+ return *match;
+
+ SmallVector<StackTrieNode *, 4> siblings =
+ findSiblings(Parent, FuncId, TId, StackRootsByThreadId);
+ if (siblings.empty()) {
+ NodeStore.push_front({FuncId, Parent, {}, {(*id_counter)++, {}}});
+ StackTrieNode *CurrentStack = &NodeStore.front();
+ StacksByStackId[*id_counter - 1] = CurrentStack;
+ ParentCallees.push_back(CurrentStack);
+ return CurrentStack;
+ }
+ unsigned stack_id = siblings[0]->ExtraData.id;
+ NodeStore.push_front({FuncId, Parent, {}, {stack_id, std::move(siblings)}});
+ StackTrieNode *CurrentStack = &NodeStore.front();
+ for (auto *sibling : CurrentStack->ExtraData.siblings)
+ sibling->ExtraData.siblings.push_back(CurrentStack);
+ ParentCallees.push_back(CurrentStack);
+ 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,
+ raw_ostream &OS) {
+ const auto &FH = Records.getFileHeader();
+ auto Version = FH.Version;
+ auto CycleFreq = FH.CycleFrequency;
+
+ unsigned id_counter = 0;
+ int NumOutputRecords = 0;
+
+ OS << "{\n \"traceEvents\": [\n";
+ DenseMap<uint32_t, StackTrieNode *> StackCursorByThreadId{};
+ DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> StackRootsByThreadId{};
+ DenseMap<unsigned, StackTrieNode *> StacksByStackId{};
+ std::forward_list<StackTrieNode> NodeStore{};
+ 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
+ // Could lose some precision here by converting the TSC to a double to
+ // multiply by the period in micros. 52 bit mantissa is a good start though.
+ // TODO: Make feature request to Chrome Trace viewer to accept ticks and a
+ // frequency or do some more involved calculation to avoid dangers of
+ // conversion.
+ 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,
+ StackRootsByThreadId, StacksByStackId,
+ &id_counter, NodeStore);
+ // Each record is represented as a json dictionary with function name,
+ // 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.
+ if (NumOutputRecords++ > 0) {
+ OS << ",\n";
+ }
+ writeTraceViewerRecord(Version, OS, R.FuncId, R.TId, R.PId, Symbolize,
+ FuncIdHelper, EventTimestampUs, *StackCursor, "B");
+ break;
+ case RecordTypes::EXIT:
+ case RecordTypes::TAIL_EXIT:
+ // No entries to record end for.
+ if (StackCursor == nullptr)
+ break;
+ // Should we emit an END record anyway or account this condition?
+ // (And/Or in loop termination below)
+ StackTrieNode *PreviousCursor = nullptr;
+ do {
+ if (NumOutputRecords++ > 0) {
+ OS << ",\n";
+ }
+ writeTraceViewerRecord(Version, OS, StackCursor->FuncId, R.TId, R.PId,
+ Symbolize, FuncIdHelper, EventTimestampUs,
+ *StackCursor, "E");
+ 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 << " }";
+ }
+ OS << "\n }\n"; // Close the stack frames map.
+ OS << "}\n"; // Close the JSON entry.
+}
+
+namespace llvm {
+namespace xray {
+
+static CommandRegistration Unused(&Convert, []() -> Error {
+ // FIXME: Support conversion to BINARY when upgrading XRay trace versions.
+ InstrumentationMap Map;
+ if (!ConvertInstrMap.empty()) {
+ auto InstrumentationMapOrError = loadInstrumentationMap(ConvertInstrMap);
+ if (!InstrumentationMapOrError)
+ return joinErrors(make_error<StringError>(
+ Twine("Cannot open instrumentation map '") +
+ ConvertInstrMap + "'",
+ std::make_error_code(std::errc::invalid_argument)),
+ InstrumentationMapOrError.takeError());
+ Map = std::move(*InstrumentationMapOrError);
+ }
+
+ const auto &FunctionAddresses = Map.getFunctionAddresses();
+ symbolize::LLVMSymbolizer::Options SymbolizerOpts;
+ if (Demangle.getPosition() < NoDemangle.getPosition())
+ SymbolizerOpts.Demangle = false;
+ symbolize::LLVMSymbolizer Symbolizer(SymbolizerOpts);
+ llvm::xray::FuncIdConversionHelper FuncIdHelper(ConvertInstrMap, Symbolizer,
+ FunctionAddresses);
+ llvm::xray::TraceConverter TC(FuncIdHelper, ConvertSymbolize);
+ std::error_code EC;
+ raw_fd_ostream OS(ConvertOutput, EC,
+ ConvertOutputFormat == ConvertFormats::BINARY
+ ? sys::fs::OpenFlags::OF_None
+ : sys::fs::OpenFlags::OF_TextWithCRLF);
+ if (EC)
+ return make_error<StringError>(
+ Twine("Cannot open file '") + ConvertOutput + "' for writing.", EC);
+
+ auto TraceOrErr = loadTraceFile(ConvertInput, ConvertSortInput);
+ if (!TraceOrErr)
+ return joinErrors(
+ make_error<StringError>(
+ Twine("Failed loading input file '") + ConvertInput + "'.",
+ std::make_error_code(std::errc::executable_format_error)),
+ TraceOrErr.takeError());
+
+ auto &T = *TraceOrErr;
+ switch (ConvertOutputFormat) {
+ case ConvertFormats::YAML:
+ TC.exportAsYAML(T, OS);
+ break;
+ case ConvertFormats::BINARY:
+ TC.exportAsRAWv1(T, OS);
+ break;
+ case ConvertFormats::CHROME_TRACE_EVENT:
+ TC.exportAsChromeTraceEventFormat(T, OS);
+ break;
+ }
+ return Error::success();
+});
+
+} // namespace xray
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-converter.h b/contrib/libs/llvm14/tools/llvm-xray/xray-converter.h
new file mode 100644
index 00000000000..db6d2b1614e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-converter.h
@@ -0,0 +1,43 @@
+//===- xray-converter.h - XRay Trace Conversion ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the TraceConverter class for turning binary traces into
+// human-readable text and vice versa.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_CONVERTER_H
+#define LLVM_TOOLS_LLVM_XRAY_XRAY_CONVERTER_H
+
+#include "func-id-helper.h"
+#include "llvm/XRay/Trace.h"
+#include "llvm/XRay/XRayRecord.h"
+
+namespace llvm {
+namespace xray {
+
+class TraceConverter {
+ FuncIdConversionHelper &FuncIdHelper;
+ bool Symbolize;
+
+public:
+ TraceConverter(FuncIdConversionHelper &FuncIdHelper, bool Symbolize = false)
+ : FuncIdHelper(FuncIdHelper), Symbolize(Symbolize) {}
+
+ void exportAsYAML(const Trace &Records, raw_ostream &OS);
+ void exportAsRAWv1(const Trace &Records, raw_ostream &OS);
+
+ /// For this conversion, the Function records within each thread are expected
+ /// to be in sorted TSC order. The trace event format encodes stack traces, so
+ /// the linear history is essential for correct output.
+ void exportAsChromeTraceEventFormat(const Trace &Records, raw_ostream &OS);
+};
+
+} // namespace xray
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_CONVERTER_H
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-extract.cpp b/contrib/libs/llvm14/tools/llvm-xray/xray-extract.cpp
new file mode 100644
index 00000000000..52767a00f61
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-extract.cpp
@@ -0,0 +1,101 @@
+//===- xray-extract.cpp: XRay Instrumentation Map Extraction --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of the xray-extract.h interface.
+//
+// FIXME: Support other XRay-instrumented binary formats other than ELF.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "func-id-helper.h"
+#include "xray-registry.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/XRay/InstrumentationMap.h"
+
+using namespace llvm;
+using namespace llvm::xray;
+using namespace llvm::yaml;
+
+// llvm-xray extract
+// ----------------------------------------------------------------------------
+static cl::SubCommand Extract("extract", "Extract instrumentation maps");
+static cl::opt<std::string> ExtractInput(cl::Positional,
+ cl::desc("<input file>"), cl::Required,
+ cl::sub(Extract));
+static cl::opt<std::string>
+ ExtractOutput("output", cl::value_desc("output file"), cl::init("-"),
+ cl::desc("output file; use '-' for stdout"),
+ cl::sub(Extract));
+static cl::alias ExtractOutput2("o", cl::aliasopt(ExtractOutput),
+ cl::desc("Alias for -output"));
+static cl::opt<bool> ExtractSymbolize("symbolize", cl::value_desc("symbolize"),
+ cl::init(false),
+ cl::desc("symbolize functions"),
+ cl::sub(Extract));
+static cl::alias ExtractSymbolize2("s", cl::aliasopt(ExtractSymbolize),
+ cl::desc("alias for -symbolize"));
+static cl::opt<bool> Demangle("demangle",
+ cl::desc("demangle symbols (default)"),
+ cl::sub(Extract));
+static cl::opt<bool> NoDemangle("no-demangle",
+ cl::desc("don't demangle symbols"),
+ cl::sub(Extract));
+
+namespace {
+
+void exportAsYAML(const InstrumentationMap &Map, raw_ostream &OS,
+ FuncIdConversionHelper &FH) {
+ // First we translate the sleds into the YAMLXRaySledEntry objects in a deque.
+ std::vector<YAMLXRaySledEntry> YAMLSleds;
+ auto Sleds = Map.sleds();
+ YAMLSleds.reserve(std::distance(Sleds.begin(), Sleds.end()));
+ for (const auto &Sled : Sleds) {
+ auto FuncId = Map.getFunctionId(Sled.Function);
+ if (!FuncId)
+ return;
+ YAMLSleds.push_back(
+ {*FuncId, Sled.Address, Sled.Function, Sled.Kind, Sled.AlwaysInstrument,
+ ExtractSymbolize ? FH.SymbolOrNumber(*FuncId) : "", Sled.Version});
+ }
+ Output Out(OS, nullptr, 0);
+ Out << YAMLSleds;
+}
+
+} // namespace
+
+static CommandRegistration Unused(&Extract, []() -> Error {
+ auto InstrumentationMapOrError = loadInstrumentationMap(ExtractInput);
+ if (!InstrumentationMapOrError)
+ return joinErrors(make_error<StringError>(
+ Twine("Cannot extract instrumentation map from '") +
+ ExtractInput + "'.",
+ std::make_error_code(std::errc::invalid_argument)),
+ InstrumentationMapOrError.takeError());
+
+ std::error_code EC;
+ raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF);
+ if (EC)
+ return make_error<StringError>(
+ Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC);
+ const auto &FunctionAddresses =
+ InstrumentationMapOrError->getFunctionAddresses();
+ symbolize::LLVMSymbolizer::Options opts;
+ if (Demangle.getPosition() < NoDemangle.getPosition())
+ opts.Demangle = false;
+ symbolize::LLVMSymbolizer Symbolizer(opts);
+ llvm::xray::FuncIdConversionHelper FuncIdHelper(ExtractInput, Symbolizer,
+ FunctionAddresses);
+ exportAsYAML(*InstrumentationMapOrError, OS, FuncIdHelper);
+ return Error::success();
+});
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-fdr-dump.cpp b/contrib/libs/llvm14/tools/llvm-xray/xray-fdr-dump.cpp
new file mode 100644
index 00000000000..295f7a78765
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-fdr-dump.cpp
@@ -0,0 +1,119 @@
+//===- xray-fdr-dump.cpp: XRay FDR Trace Dump Tool ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// 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.
+ auto FDOrErr = sys::fs::openNativeFileForRead(DumpInput);
+ if (!FDOrErr)
+ return FDOrErr.takeError();
+
+ 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(
+ *FDOrErr, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0,
+ EC);
+ sys::fs::closeFile(*FDOrErr);
+
+ DataExtractor DE(StringRef(MappedFile.data(), MappedFile.size()), true, 8);
+ uint64_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/contrib/libs/llvm14/tools/llvm-xray/xray-graph-diff.cpp b/contrib/libs/llvm14/tools/llvm-xray/xray-graph-diff.cpp
new file mode 100644
index 00000000000..f22ea06e053
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-graph-diff.cpp
@@ -0,0 +1,469 @@
+//===-- xray-graph-diff.cpp: XRay Function Call Graph Renderer ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Generate a DOT file to represent the function call graph encountered in
+// the trace.
+//
+//===----------------------------------------------------------------------===//
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <string>
+
+#include "xray-graph-diff.h"
+#include "xray-graph.h"
+#include "xray-registry.h"
+
+#include "xray-color-helper.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/XRay/Trace.h"
+
+using namespace llvm;
+using namespace xray;
+
+static cl::SubCommand GraphDiff("graph-diff",
+ "Generate diff of function-call graphs");
+static cl::opt<std::string> GraphDiffInput1(cl::Positional,
+ cl::desc("<xray log file 1>"),
+ cl::Required, cl::sub(GraphDiff));
+static cl::opt<std::string> GraphDiffInput2(cl::Positional,
+ cl::desc("<xray log file 2>"),
+ cl::Required, cl::sub(GraphDiff));
+
+static cl::opt<bool>
+ GraphDiffKeepGoing("keep-going",
+ cl::desc("Keep going on errors encountered"),
+ cl::sub(GraphDiff), cl::init(false));
+static cl::alias GraphDiffKeepGoingA("k", cl::aliasopt(GraphDiffKeepGoing),
+ cl::desc("Alias for -keep-going"));
+static cl::opt<bool>
+ GraphDiffKeepGoing1("keep-going-1",
+ cl::desc("Keep going on errors encountered in trace 1"),
+ cl::sub(GraphDiff), cl::init(false));
+static cl::alias GraphDiffKeepGoing1A("k1", cl::aliasopt(GraphDiffKeepGoing1),
+ cl::desc("Alias for -keep-going-1"));
+static cl::opt<bool>
+ GraphDiffKeepGoing2("keep-going-2",
+ cl::desc("Keep going on errors encountered in trace 2"),
+ cl::sub(GraphDiff), cl::init(false));
+static cl::alias GraphDiffKeepGoing2A("k2", cl::aliasopt(GraphDiffKeepGoing2),
+ cl::desc("Alias for -keep-going-2"));
+
+static cl::opt<std::string>
+ GraphDiffInstrMap("instr-map",
+ cl::desc("binary with the instrumentation map, or "
+ "a separate instrumentation map for graph"),
+ cl::value_desc("binary with xray_instr_map or yaml"),
+ cl::sub(GraphDiff), cl::init(""));
+static cl::alias GraphDiffInstrMapA("m", cl::aliasopt(GraphDiffInstrMap),
+ cl::desc("Alias for -instr-map"));
+static cl::opt<std::string>
+ GraphDiffInstrMap1("instr-map-1",
+ cl::desc("binary with the instrumentation map, or "
+ "a separate instrumentation map for graph 1"),
+ cl::value_desc("binary with xray_instr_map or yaml"),
+ cl::sub(GraphDiff), cl::init(""));
+static cl::alias GraphDiffInstrMap1A("m1", cl::aliasopt(GraphDiffInstrMap1),
+ cl::desc("Alias for -instr-map-1"));
+static cl::opt<std::string>
+ GraphDiffInstrMap2("instr-map-2",
+ cl::desc("binary with the instrumentation map, or "
+ "a separate instrumentation map for graph 2"),
+ cl::value_desc("binary with xray_instr_map or yaml"),
+ cl::sub(GraphDiff), cl::init(""));
+static cl::alias GraphDiffInstrMap2A("m2", cl::aliasopt(GraphDiffInstrMap2),
+ cl::desc("Alias for -instr-map-2"));
+
+static cl::opt<bool> GraphDiffDeduceSiblingCalls(
+ "deduce-sibling-calls",
+ cl::desc("Deduce sibling calls when unrolling function call stacks"),
+ cl::sub(GraphDiff), cl::init(false));
+static cl::alias
+ GraphDiffDeduceSiblingCallsA("d", cl::aliasopt(GraphDiffDeduceSiblingCalls),
+ cl::desc("Alias for -deduce-sibling-calls"));
+static cl::opt<bool> GraphDiffDeduceSiblingCalls1(
+ "deduce-sibling-calls-1",
+ cl::desc("Deduce sibling calls when unrolling function call stacks"),
+ cl::sub(GraphDiff), cl::init(false));
+static cl::alias GraphDiffDeduceSiblingCalls1A(
+ "d1", cl::aliasopt(GraphDiffDeduceSiblingCalls1),
+ cl::desc("Alias for -deduce-sibling-calls-1"));
+static cl::opt<bool> GraphDiffDeduceSiblingCalls2(
+ "deduce-sibling-calls-2",
+ cl::desc("Deduce sibling calls when unrolling function call stacks"),
+ cl::sub(GraphDiff), cl::init(false));
+static cl::alias GraphDiffDeduceSiblingCalls2A(
+ "d2", cl::aliasopt(GraphDiffDeduceSiblingCalls2),
+ cl::desc("Alias for -deduce-sibling-calls-2"));
+
+static cl::opt<GraphRenderer::StatType> GraphDiffEdgeLabel(
+ "edge-label", cl::desc("Output graphs with edges labeled with this field"),
+ cl::value_desc("field"), cl::sub(GraphDiff),
+ cl::init(GraphRenderer::StatType::NONE),
+ cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
+ "Do not label Edges"),
+ clEnumValN(GraphRenderer::StatType::COUNT, "count",
+ "function call counts"),
+ clEnumValN(GraphRenderer::StatType::MIN, "min",
+ "minimum function durations"),
+ clEnumValN(GraphRenderer::StatType::MED, "med",
+ "median function durations"),
+ clEnumValN(GraphRenderer::StatType::PCT90, "90p",
+ "90th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::PCT99, "99p",
+ "99th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::MAX, "max",
+ "maximum function durations"),
+ clEnumValN(GraphRenderer::StatType::SUM, "sum",
+ "sum of call durations")));
+static cl::alias GraphDiffEdgeLabelA("e", cl::aliasopt(GraphDiffEdgeLabel),
+ cl::desc("Alias for -edge-label"));
+
+static cl::opt<GraphRenderer::StatType> GraphDiffEdgeColor(
+ "edge-color", cl::desc("Output graphs with edges colored by this field"),
+ cl::value_desc("field"), cl::sub(GraphDiff),
+ cl::init(GraphRenderer::StatType::NONE),
+ cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
+ "Do not color Edges"),
+ clEnumValN(GraphRenderer::StatType::COUNT, "count",
+ "function call counts"),
+ clEnumValN(GraphRenderer::StatType::MIN, "min",
+ "minimum function durations"),
+ clEnumValN(GraphRenderer::StatType::MED, "med",
+ "median function durations"),
+ clEnumValN(GraphRenderer::StatType::PCT90, "90p",
+ "90th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::PCT99, "99p",
+ "99th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::MAX, "max",
+ "maximum function durations"),
+ clEnumValN(GraphRenderer::StatType::SUM, "sum",
+ "sum of call durations")));
+static cl::alias GraphDiffEdgeColorA("c", cl::aliasopt(GraphDiffEdgeColor),
+ cl::desc("Alias for -edge-color"));
+
+static cl::opt<GraphRenderer::StatType> GraphDiffVertexLabel(
+ "vertex-label",
+ cl::desc("Output graphs with vertices labeled with this field"),
+ cl::value_desc("field"), cl::sub(GraphDiff),
+ cl::init(GraphRenderer::StatType::NONE),
+ cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
+ "Do not label Vertices"),
+ clEnumValN(GraphRenderer::StatType::COUNT, "count",
+ "function call counts"),
+ clEnumValN(GraphRenderer::StatType::MIN, "min",
+ "minimum function durations"),
+ clEnumValN(GraphRenderer::StatType::MED, "med",
+ "median function durations"),
+ clEnumValN(GraphRenderer::StatType::PCT90, "90p",
+ "90th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::PCT99, "99p",
+ "99th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::MAX, "max",
+ "maximum function durations"),
+ clEnumValN(GraphRenderer::StatType::SUM, "sum",
+ "sum of call durations")));
+static cl::alias GraphDiffVertexLabelA("v", cl::aliasopt(GraphDiffVertexLabel),
+ cl::desc("Alias for -vertex-label"));
+
+static cl::opt<GraphRenderer::StatType> GraphDiffVertexColor(
+ "vertex-color",
+ cl::desc("Output graphs with vertices colored by this field"),
+ cl::value_desc("field"), cl::sub(GraphDiff),
+ cl::init(GraphRenderer::StatType::NONE),
+ cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
+ "Do not color Vertices"),
+ clEnumValN(GraphRenderer::StatType::COUNT, "count",
+ "function call counts"),
+ clEnumValN(GraphRenderer::StatType::MIN, "min",
+ "minimum function durations"),
+ clEnumValN(GraphRenderer::StatType::MED, "med",
+ "median function durations"),
+ clEnumValN(GraphRenderer::StatType::PCT90, "90p",
+ "90th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::PCT99, "99p",
+ "99th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::MAX, "max",
+ "maximum function durations"),
+ clEnumValN(GraphRenderer::StatType::SUM, "sum",
+ "sum of call durations")));
+static cl::alias GraphDiffVertexColorA("b", cl::aliasopt(GraphDiffVertexColor),
+ cl::desc("Alias for -vertex-color"));
+
+static cl::opt<int> GraphDiffVertexLabelTrunc(
+ "vertex-label-trun", cl::desc("What length to truncate vertex labels to "),
+ cl::sub(GraphDiff), cl::init(40));
+static cl::alias
+ GraphDiffVertexLabelTrunc1("t", cl::aliasopt(GraphDiffVertexLabelTrunc),
+ cl::desc("Alias for -vertex-label-trun"));
+
+static cl::opt<std::string>
+ GraphDiffOutput("output", cl::value_desc("Output file"), cl::init("-"),
+ cl::desc("output file; use '-' for stdout"),
+ cl::sub(GraphDiff));
+static cl::alias GraphDiffOutputA("o", cl::aliasopt(GraphDiffOutput),
+ cl::desc("Alias for -output"));
+
+Expected<GraphDiffRenderer> GraphDiffRenderer::Factory::getGraphDiffRenderer() {
+ GraphDiffRenderer R;
+
+ for (int i = 0; i < N; ++i) {
+ const auto &G = this->G[i].get();
+ for (const auto &V : G.vertices()) {
+ const auto &VAttr = V.second;
+ R.G[VAttr.SymbolName].CorrVertexPtr[i] = &V;
+ }
+ for (const auto &E : G.edges()) {
+ auto &EdgeTailID = E.first.first;
+ auto &EdgeHeadID = E.first.second;
+ auto EdgeTailAttrOrErr = G.at(EdgeTailID);
+ auto EdgeHeadAttrOrErr = G.at(EdgeHeadID);
+ if (!EdgeTailAttrOrErr)
+ return EdgeTailAttrOrErr.takeError();
+ if (!EdgeHeadAttrOrErr)
+ return EdgeHeadAttrOrErr.takeError();
+ GraphT::EdgeIdentifier ID{EdgeTailAttrOrErr->SymbolName,
+ EdgeHeadAttrOrErr->SymbolName};
+ R.G[ID].CorrEdgePtr[i] = &E;
+ }
+ }
+
+ return R;
+}
+// Returns the Relative change With respect to LeftStat between LeftStat
+// and RightStat.
+static double statRelDiff(const GraphDiffRenderer::TimeStat &LeftStat,
+ const GraphDiffRenderer::TimeStat &RightStat,
+ GraphDiffRenderer::StatType T) {
+ double LeftAttr = LeftStat.getDouble(T);
+ double RightAttr = RightStat.getDouble(T);
+
+ return RightAttr / LeftAttr - 1.0;
+}
+
+static std::string getColor(const GraphDiffRenderer::GraphT::EdgeValueType &E,
+ const GraphDiffRenderer::GraphT &G, ColorHelper H,
+ GraphDiffRenderer::StatType T) {
+ auto &EdgeAttr = E.second;
+ if (EdgeAttr.CorrEdgePtr[0] == nullptr)
+ return H.getColorString(2.0); // A number greater than 1.0
+ if (EdgeAttr.CorrEdgePtr[1] == nullptr)
+ return H.getColorString(-2.0); // A number less than -1.0
+
+ if (T == GraphDiffRenderer::StatType::NONE)
+ return H.getDefaultColorString();
+
+ const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S;
+ const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S;
+
+ double RelDiff = statRelDiff(LeftStat, RightStat, T);
+ double CappedRelDiff = std::min(1.0, std::max(-1.0, RelDiff));
+
+ return H.getColorString(CappedRelDiff);
+}
+
+static std::string getColor(const GraphDiffRenderer::GraphT::VertexValueType &V,
+ const GraphDiffRenderer::GraphT &G, ColorHelper H,
+ GraphDiffRenderer::StatType T) {
+ auto &VertexAttr = V.second;
+ if (VertexAttr.CorrVertexPtr[0] == nullptr)
+ return H.getColorString(2.0); // A number greater than 1.0
+ if (VertexAttr.CorrVertexPtr[1] == nullptr)
+ return H.getColorString(-2.0); // A number less than -1.0
+
+ if (T == GraphDiffRenderer::StatType::NONE)
+ return H.getDefaultColorString();
+
+ const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S;
+ const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S;
+
+ double RelDiff = statRelDiff(LeftStat, RightStat, T);
+ double CappedRelDiff = std::min(1.0, std::max(-1.0, RelDiff));
+
+ return H.getColorString(CappedRelDiff);
+}
+
+static Twine truncateString(const StringRef &S, size_t n) {
+ return (S.size() > n) ? Twine(S.substr(0, n)) + "..." : Twine(S);
+}
+
+template <typename T> static bool containsNullptr(const T &Collection) {
+ return llvm::is_contained(Collection, nullptr);
+}
+
+static std::string getLabel(const GraphDiffRenderer::GraphT::EdgeValueType &E,
+ GraphDiffRenderer::StatType EL) {
+ auto &EdgeAttr = E.second;
+ switch (EL) {
+ case GraphDiffRenderer::StatType::NONE:
+ return "";
+ default:
+ if (containsNullptr(EdgeAttr.CorrEdgePtr))
+ return "";
+
+ const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S;
+ const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S;
+
+ double RelDiff = statRelDiff(LeftStat, RightStat, EL);
+ return std::string(formatv(R"({0:P})", RelDiff));
+ }
+}
+
+static std::string getLabel(const GraphDiffRenderer::GraphT::VertexValueType &V,
+ GraphDiffRenderer::StatType VL, int TrunLen) {
+ const auto &VertexId = V.first;
+ const auto &VertexAttr = V.second;
+ switch (VL) {
+ case GraphDiffRenderer::StatType::NONE:
+ return std::string(
+ formatv(R"({0})", truncateString(VertexId, TrunLen).str()));
+ default:
+ if (containsNullptr(VertexAttr.CorrVertexPtr))
+ return std::string(
+ formatv(R"({0})", truncateString(VertexId, TrunLen).str()));
+
+ const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S;
+ const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S;
+
+ double RelDiff = statRelDiff(LeftStat, RightStat, VL);
+ return std::string(formatv(
+ R"({{{0}|{1:P}})", truncateString(VertexId, TrunLen).str(), RelDiff));
+ }
+}
+
+static double getLineWidth(const GraphDiffRenderer::GraphT::EdgeValueType &E,
+ GraphDiffRenderer::StatType EL) {
+ auto &EdgeAttr = E.second;
+ switch (EL) {
+ case GraphDiffRenderer::StatType::NONE:
+ return 1.0;
+ default:
+ if (containsNullptr(EdgeAttr.CorrEdgePtr))
+ return 1.0;
+
+ const auto &LeftStat = EdgeAttr.CorrEdgePtr[0]->second.S;
+ const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S;
+
+ double RelDiff = statRelDiff(LeftStat, RightStat, EL);
+ return (RelDiff > 1.0) ? RelDiff : 1.0;
+ }
+}
+
+void GraphDiffRenderer::exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel,
+ StatType EdgeColor,
+ StatType VertexLabel,
+ StatType VertexColor, int TruncLen) {
+ // Get numbering of vertices for dot output.
+ StringMap<int32_t> VertexNo;
+
+ int i = 0;
+ for (const auto &V : G.vertices()) {
+ VertexNo[V.first] = i++;
+ }
+
+ ColorHelper H(ColorHelper::DivergingScheme::PiYG);
+
+ OS << "digraph xrayDiff {\n";
+
+ if (VertexLabel != StatType::NONE)
+ OS << "node [shape=record]\n";
+
+ for (const auto &E : G.edges()) {
+ const auto &HeadId = E.first.first;
+ const auto &TailId = E.first.second;
+ OS << formatv(R"(F{0} -> F{1} [tooltip="{2} -> {3}" label="{4}" )"
+ R"(color="{5}" labelfontcolor="{5}" penwidth={6}])"
+ "\n",
+ VertexNo[HeadId], VertexNo[TailId],
+ (HeadId.equals("")) ? static_cast<StringRef>("F0") : HeadId,
+ TailId, getLabel(E, EdgeLabel), getColor(E, G, H, EdgeColor),
+ getLineWidth(E, EdgeColor));
+ }
+
+ for (const auto &V : G.vertices()) {
+ const auto &VertexId = V.first;
+ if (VertexId.equals("")) {
+ OS << formatv(R"(F{0} [label="F0"])"
+ "\n",
+ VertexNo[VertexId]);
+ continue;
+ }
+ OS << formatv(R"(F{0} [label="{1}" color="{2}"])"
+ "\n",
+ VertexNo[VertexId], getLabel(V, VertexLabel, TruncLen),
+ getColor(V, G, H, VertexColor));
+ }
+
+ OS << "}\n";
+}
+
+template <typename T> static T &ifSpecified(T &A, cl::alias &AA, T &B) {
+ if (A.getPosition() == 0 && AA.getPosition() == 0)
+ return B;
+
+ return A;
+}
+
+static CommandRegistration Unused(&GraphDiff, []() -> Error {
+ std::array<GraphRenderer::Factory, 2> Factories{
+ {{ifSpecified(GraphDiffKeepGoing1, GraphDiffKeepGoing1A,
+ GraphDiffKeepGoing),
+ ifSpecified(GraphDiffDeduceSiblingCalls1, GraphDiffDeduceSiblingCalls1A,
+ GraphDiffDeduceSiblingCalls),
+ ifSpecified(GraphDiffInstrMap1, GraphDiffInstrMap1A, GraphDiffInstrMap),
+ Trace()},
+ {ifSpecified(GraphDiffKeepGoing2, GraphDiffKeepGoing2A,
+ GraphDiffKeepGoing),
+ ifSpecified(GraphDiffDeduceSiblingCalls2, GraphDiffDeduceSiblingCalls2A,
+ GraphDiffDeduceSiblingCalls),
+ ifSpecified(GraphDiffInstrMap2, GraphDiffInstrMap2A, GraphDiffInstrMap),
+ Trace()}}};
+
+ std::array<std::string, 2> Inputs{{GraphDiffInput1, GraphDiffInput2}};
+
+ std::array<GraphRenderer::GraphT, 2> Graphs;
+
+ for (int i = 0; i < 2; i++) {
+ auto TraceOrErr = loadTraceFile(Inputs[i], true);
+ if (!TraceOrErr)
+ return make_error<StringError>(
+ Twine("Failed Loading Input File '") + Inputs[i] + "'",
+ make_error_code(llvm::errc::invalid_argument));
+ Factories[i].Trace = std::move(*TraceOrErr);
+
+ auto GraphRendererOrErr = Factories[i].getGraphRenderer();
+
+ if (!GraphRendererOrErr)
+ return GraphRendererOrErr.takeError();
+
+ auto GraphRenderer = *GraphRendererOrErr;
+
+ Graphs[i] = GraphRenderer.getGraph();
+ }
+
+ GraphDiffRenderer::Factory DGF(Graphs[0], Graphs[1]);
+
+ auto GDROrErr = DGF.getGraphDiffRenderer();
+ if (!GDROrErr)
+ return GDROrErr.takeError();
+
+ auto &GDR = *GDROrErr;
+
+ std::error_code EC;
+ raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF);
+ if (EC)
+ return make_error<StringError>(
+ Twine("Cannot open file '") + GraphDiffOutput + "' for writing.", EC);
+
+ GDR.exportGraphAsDOT(OS, GraphDiffEdgeLabel, GraphDiffEdgeColor,
+ GraphDiffVertexLabel, GraphDiffVertexColor,
+ GraphDiffVertexLabelTrunc);
+
+ return Error::success();
+});
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-graph-diff.h b/contrib/libs/llvm14/tools/llvm-xray/xray-graph-diff.h
new file mode 100644
index 00000000000..5d12c563f47
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-graph-diff.h
@@ -0,0 +1,73 @@
+//===-- xray-graph-diff.h - XRay Graph Diff Renderer ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Generate a DOT file to represent the difference between the function call
+// graph of two differnent traces.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef XRAY_GRAPH_DIFF_H
+#define XRAY_GRAPH_DIFF_H
+
+#include "xray-graph.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/XRay/Graph.h"
+
+namespace llvm {
+namespace xray {
+
+// This class creates a graph representing the difference between two
+// xray-graphs And allows you to print it to a dot file, with optional color
+// coding.
+class GraphDiffRenderer {
+ static const int N = 2;
+
+public:
+ using StatType = GraphRenderer::StatType;
+ using TimeStat = GraphRenderer::TimeStat;
+
+ using GREdgeValueType = GraphRenderer::GraphT::EdgeValueType;
+ using GRVertexValueType = GraphRenderer::GraphT::VertexValueType;
+
+ struct EdgeAttribute {
+ std::array<const GREdgeValueType *, N> CorrEdgePtr = {};
+ };
+
+ struct VertexAttribute {
+ std::array<const GRVertexValueType *, N> CorrVertexPtr = {};
+ };
+
+ using GraphT = Graph<VertexAttribute, EdgeAttribute, StringRef>;
+
+ class Factory {
+ std::array<std::reference_wrapper<const GraphRenderer::GraphT>, N> G;
+
+ public:
+ template <typename... Ts> Factory(Ts &... Args) : G{{Args...}} {}
+
+ Expected<GraphDiffRenderer> getGraphDiffRenderer();
+ };
+
+private:
+ GraphT G;
+
+ GraphDiffRenderer() = default;
+
+public:
+ void exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel = StatType::NONE,
+ StatType EdgeColor = StatType::NONE,
+ StatType VertexLabel = StatType::NONE,
+ StatType VertexColor = StatType::NONE,
+ int TruncLen = 40);
+
+ const GraphT &getGraph() { return G; }
+};
+} // namespace xray
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-graph.cpp b/contrib/libs/llvm14/tools/llvm-xray/xray-graph.cpp
new file mode 100644
index 00000000000..39d2c5c153e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-graph.cpp
@@ -0,0 +1,534 @@
+//===-- xray-graph.cpp: XRay Function Call Graph Renderer -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Generate a DOT file to represent the function call graph encountered in
+// the trace.
+//
+//===----------------------------------------------------------------------===//
+
+#include "xray-graph.h"
+#include "xray-registry.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/XRay/InstrumentationMap.h"
+#include "llvm/XRay/Trace.h"
+
+using namespace llvm;
+using namespace llvm::xray;
+
+// Setup llvm-xray graph subcommand and its options.
+static cl::SubCommand GraphC("graph", "Generate function-call graph");
+static cl::opt<std::string> GraphInput(cl::Positional,
+ cl::desc("<xray log file>"),
+ cl::Required, cl::sub(GraphC));
+
+static cl::opt<bool>
+ GraphKeepGoing("keep-going", cl::desc("Keep going on errors encountered"),
+ cl::sub(GraphC), cl::init(false));
+static cl::alias GraphKeepGoing2("k", cl::aliasopt(GraphKeepGoing),
+ cl::desc("Alias for -keep-going"));
+
+static cl::opt<std::string>
+ GraphOutput("output", cl::value_desc("Output file"), cl::init("-"),
+ cl::desc("output file; use '-' for stdout"), cl::sub(GraphC));
+static cl::alias GraphOutput2("o", cl::aliasopt(GraphOutput),
+ cl::desc("Alias for -output"));
+
+static cl::opt<std::string>
+ GraphInstrMap("instr_map",
+ cl::desc("binary with the instrumrntation map, or "
+ "a separate instrumentation map"),
+ cl::value_desc("binary with xray_instr_map"), cl::sub(GraphC),
+ cl::init(""));
+static cl::alias GraphInstrMap2("m", cl::aliasopt(GraphInstrMap),
+ cl::desc("alias for -instr_map"));
+
+static cl::opt<bool> GraphDeduceSiblingCalls(
+ "deduce-sibling-calls",
+ cl::desc("Deduce sibling calls when unrolling function call stacks"),
+ cl::sub(GraphC), cl::init(false));
+static cl::alias
+ GraphDeduceSiblingCalls2("d", cl::aliasopt(GraphDeduceSiblingCalls),
+ cl::desc("Alias for -deduce-sibling-calls"));
+
+static cl::opt<GraphRenderer::StatType>
+ GraphEdgeLabel("edge-label",
+ cl::desc("Output graphs with edges labeled with this field"),
+ cl::value_desc("field"), cl::sub(GraphC),
+ cl::init(GraphRenderer::StatType::NONE),
+ cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
+ "Do not label Edges"),
+ clEnumValN(GraphRenderer::StatType::COUNT,
+ "count", "function call counts"),
+ clEnumValN(GraphRenderer::StatType::MIN, "min",
+ "minimum function durations"),
+ clEnumValN(GraphRenderer::StatType::MED, "med",
+ "median function durations"),
+ clEnumValN(GraphRenderer::StatType::PCT90, "90p",
+ "90th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::PCT99, "99p",
+ "99th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::MAX, "max",
+ "maximum function durations"),
+ clEnumValN(GraphRenderer::StatType::SUM, "sum",
+ "sum of call durations")));
+static cl::alias GraphEdgeLabel2("e", cl::aliasopt(GraphEdgeLabel),
+ cl::desc("Alias for -edge-label"));
+
+static cl::opt<GraphRenderer::StatType> GraphVertexLabel(
+ "vertex-label",
+ cl::desc("Output graphs with vertices labeled with this field"),
+ cl::value_desc("field"), cl::sub(GraphC),
+ cl::init(GraphRenderer::StatType::NONE),
+ cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
+ "Do not label Vertices"),
+ clEnumValN(GraphRenderer::StatType::COUNT, "count",
+ "function call counts"),
+ clEnumValN(GraphRenderer::StatType::MIN, "min",
+ "minimum function durations"),
+ clEnumValN(GraphRenderer::StatType::MED, "med",
+ "median function durations"),
+ clEnumValN(GraphRenderer::StatType::PCT90, "90p",
+ "90th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::PCT99, "99p",
+ "99th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::MAX, "max",
+ "maximum function durations"),
+ clEnumValN(GraphRenderer::StatType::SUM, "sum",
+ "sum of call durations")));
+static cl::alias GraphVertexLabel2("v", cl::aliasopt(GraphVertexLabel),
+ cl::desc("Alias for -edge-label"));
+
+static cl::opt<GraphRenderer::StatType> GraphEdgeColorType(
+ "color-edges",
+ cl::desc("Output graphs with edge colors determined by this field"),
+ cl::value_desc("field"), cl::sub(GraphC),
+ cl::init(GraphRenderer::StatType::NONE),
+ cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
+ "Do not color Edges"),
+ clEnumValN(GraphRenderer::StatType::COUNT, "count",
+ "function call counts"),
+ clEnumValN(GraphRenderer::StatType::MIN, "min",
+ "minimum function durations"),
+ clEnumValN(GraphRenderer::StatType::MED, "med",
+ "median function durations"),
+ clEnumValN(GraphRenderer::StatType::PCT90, "90p",
+ "90th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::PCT99, "99p",
+ "99th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::MAX, "max",
+ "maximum function durations"),
+ clEnumValN(GraphRenderer::StatType::SUM, "sum",
+ "sum of call durations")));
+static cl::alias GraphEdgeColorType2("c", cl::aliasopt(GraphEdgeColorType),
+ cl::desc("Alias for -color-edges"));
+
+static cl::opt<GraphRenderer::StatType> GraphVertexColorType(
+ "color-vertices",
+ cl::desc("Output graphs with vertex colors determined by this field"),
+ cl::value_desc("field"), cl::sub(GraphC),
+ cl::init(GraphRenderer::StatType::NONE),
+ cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none",
+ "Do not color vertices"),
+ clEnumValN(GraphRenderer::StatType::COUNT, "count",
+ "function call counts"),
+ clEnumValN(GraphRenderer::StatType::MIN, "min",
+ "minimum function durations"),
+ clEnumValN(GraphRenderer::StatType::MED, "med",
+ "median function durations"),
+ clEnumValN(GraphRenderer::StatType::PCT90, "90p",
+ "90th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::PCT99, "99p",
+ "99th percentile durations"),
+ clEnumValN(GraphRenderer::StatType::MAX, "max",
+ "maximum function durations"),
+ clEnumValN(GraphRenderer::StatType::SUM, "sum",
+ "sum of call durations")));
+static cl::alias GraphVertexColorType2("b", cl::aliasopt(GraphVertexColorType),
+ cl::desc("Alias for -edge-label"));
+
+template <class T> T diff(T L, T R) { return std::max(L, R) - std::min(L, R); }
+
+// Updates the statistics for a GraphRenderer::TimeStat
+static void updateStat(GraphRenderer::TimeStat &S, int64_t L) {
+ S.Count++;
+ if (S.Min > L || S.Min == 0)
+ S.Min = L;
+ if (S.Max < L)
+ S.Max = L;
+ S.Sum += L;
+}
+
+// Labels in a DOT graph must be legal XML strings so it's necessary to escape
+// certain characters.
+static std::string escapeString(StringRef Label) {
+ std::string Str;
+ Str.reserve(Label.size());
+ for (const auto C : Label) {
+ switch (C) {
+ case '&':
+ Str.append("&amp;");
+ break;
+ case '<':
+ Str.append("&lt;");
+ break;
+ case '>':
+ Str.append("&gt;");
+ break;
+ default:
+ Str.push_back(C);
+ break;
+ }
+ }
+ return Str;
+}
+
+// Evaluates an XRay record and performs accounting on it.
+//
+// If the record is an ENTER record it pushes the FuncID and TSC onto a
+// structure representing the call stack for that function.
+// If the record is an EXIT record it checks computes computes the ammount of
+// time the function took to complete and then stores that information in an
+// edge of the graph. If there is no matching ENTER record the function tries
+// to recover by assuming that there were EXIT records which were missed, for
+// example caused by tail call elimination and if the option is enabled then
+// then tries to recover from this.
+//
+// This funciton will also error if the records are out of order, as the trace
+// is expected to be sorted.
+//
+// The graph generated has an immaginary root for functions called by no-one at
+// FuncId 0.
+//
+// FIXME: Refactor this and account subcommand to reduce code duplication.
+Error GraphRenderer::accountRecord(const XRayRecord &Record) {
+ using std::make_error_code;
+ using std::errc;
+ if (CurrentMaxTSC == 0)
+ CurrentMaxTSC = Record.TSC;
+
+ if (Record.TSC < CurrentMaxTSC)
+ return make_error<StringError>("Records not in order",
+ make_error_code(errc::invalid_argument));
+
+ auto &ThreadStack = PerThreadFunctionStack[Record.TId];
+ switch (Record.Type) {
+ case RecordTypes::ENTER:
+ case RecordTypes::ENTER_ARG: {
+ if (Record.FuncId != 0 && G.count(Record.FuncId) == 0)
+ G[Record.FuncId].SymbolName = FuncIdHelper.SymbolOrNumber(Record.FuncId);
+ ThreadStack.push_back({Record.FuncId, Record.TSC});
+ break;
+ }
+ case RecordTypes::EXIT:
+ case RecordTypes::TAIL_EXIT: {
+ // FIXME: Refactor this and the account subcommand to reduce code
+ // duplication
+ if (ThreadStack.size() == 0 || ThreadStack.back().FuncId != Record.FuncId) {
+ if (!DeduceSiblingCalls)
+ return make_error<StringError>("No matching ENTRY record",
+ make_error_code(errc::invalid_argument));
+ auto Parent = std::find_if(
+ ThreadStack.rbegin(), ThreadStack.rend(),
+ [&](const FunctionAttr &A) { return A.FuncId == Record.FuncId; });
+ if (Parent == ThreadStack.rend())
+ return make_error<StringError>(
+ "No matching Entry record in stack",
+ make_error_code(errc::invalid_argument)); // There is no matching
+ // Function for this exit.
+ while (ThreadStack.back().FuncId != Record.FuncId) {
+ TimestampT D = diff(ThreadStack.back().TSC, Record.TSC);
+ VertexIdentifier TopFuncId = ThreadStack.back().FuncId;
+ ThreadStack.pop_back();
+ assert(ThreadStack.size() != 0);
+ EdgeIdentifier EI(ThreadStack.back().FuncId, TopFuncId);
+ auto &EA = G[EI];
+ EA.Timings.push_back(D);
+ updateStat(EA.S, D);
+ updateStat(G[TopFuncId].S, D);
+ }
+ }
+ uint64_t D = diff(ThreadStack.back().TSC, Record.TSC);
+ ThreadStack.pop_back();
+ VertexIdentifier VI = ThreadStack.empty() ? 0 : ThreadStack.back().FuncId;
+ EdgeIdentifier EI(VI, Record.FuncId);
+ auto &EA = G[EI];
+ EA.Timings.push_back(D);
+ updateStat(EA.S, D);
+ 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();
+}
+
+template <typename U>
+void GraphRenderer::getStats(U begin, U end, GraphRenderer::TimeStat &S) {
+ if (begin == end) return;
+ std::ptrdiff_t MedianOff = S.Count / 2;
+ std::nth_element(begin, begin + MedianOff, end);
+ S.Median = *(begin + MedianOff);
+ std::ptrdiff_t Pct90Off = (S.Count * 9) / 10;
+ std::nth_element(begin, begin + Pct90Off, end);
+ S.Pct90 = *(begin + Pct90Off);
+ std::ptrdiff_t Pct99Off = (S.Count * 99) / 100;
+ std::nth_element(begin, begin + Pct99Off, end);
+ S.Pct99 = *(begin + Pct99Off);
+}
+
+void GraphRenderer::updateMaxStats(const GraphRenderer::TimeStat &S,
+ GraphRenderer::TimeStat &M) {
+ M.Count = std::max(M.Count, S.Count);
+ M.Min = std::max(M.Min, S.Min);
+ M.Median = std::max(M.Median, S.Median);
+ M.Pct90 = std::max(M.Pct90, S.Pct90);
+ M.Pct99 = std::max(M.Pct99, S.Pct99);
+ M.Max = std::max(M.Max, S.Max);
+ M.Sum = std::max(M.Sum, S.Sum);
+}
+
+void GraphRenderer::calculateEdgeStatistics() {
+ assert(!G.edges().empty());
+ for (auto &E : G.edges()) {
+ auto &A = E.second;
+ assert(!A.Timings.empty());
+ getStats(A.Timings.begin(), A.Timings.end(), A.S);
+ updateMaxStats(A.S, G.GraphEdgeMax);
+ }
+}
+
+void GraphRenderer::calculateVertexStatistics() {
+ std::vector<uint64_t> TempTimings;
+ for (auto &V : G.vertices()) {
+ if (V.first != 0) {
+ for (auto &E : G.inEdges(V.first)) {
+ auto &A = E.second;
+ llvm::append_range(TempTimings, A.Timings);
+ }
+ getStats(TempTimings.begin(), TempTimings.end(), G[V.first].S);
+ updateMaxStats(G[V.first].S, G.GraphVertexMax);
+ TempTimings.clear();
+ }
+ }
+}
+
+// A Helper function for normalizeStatistics which normalises a single
+// TimeStat element.
+static void normalizeTimeStat(GraphRenderer::TimeStat &S,
+ double CycleFrequency) {
+ int64_t OldCount = S.Count;
+ S = S / CycleFrequency;
+ S.Count = OldCount;
+}
+
+// Normalises the statistics in the graph for a given TSC frequency.
+void GraphRenderer::normalizeStatistics(double CycleFrequency) {
+ for (auto &E : G.edges()) {
+ auto &S = E.second.S;
+ normalizeTimeStat(S, CycleFrequency);
+ }
+ for (auto &V : G.vertices()) {
+ auto &S = V.second.S;
+ normalizeTimeStat(S, CycleFrequency);
+ }
+
+ normalizeTimeStat(G.GraphEdgeMax, CycleFrequency);
+ normalizeTimeStat(G.GraphVertexMax, CycleFrequency);
+}
+
+// Returns a string containing the value of statistic field T
+std::string
+GraphRenderer::TimeStat::getString(GraphRenderer::StatType T) const {
+ std::string St;
+ raw_string_ostream S{St};
+ double TimeStat::*DoubleStatPtrs[] = {&TimeStat::Min, &TimeStat::Median,
+ &TimeStat::Pct90, &TimeStat::Pct99,
+ &TimeStat::Max, &TimeStat::Sum};
+ switch (T) {
+ case GraphRenderer::StatType::NONE:
+ break;
+ case GraphRenderer::StatType::COUNT:
+ S << Count;
+ break;
+ default:
+ S << (*this).*
+ DoubleStatPtrs[static_cast<int>(T) -
+ static_cast<int>(GraphRenderer::StatType::MIN)];
+ break;
+ }
+ return S.str();
+}
+
+// Returns the quotient between the property T of this and another TimeStat as
+// a double
+double GraphRenderer::TimeStat::getDouble(StatType T) const {
+ double retval = 0;
+ double TimeStat::*DoubleStatPtrs[] = {&TimeStat::Min, &TimeStat::Median,
+ &TimeStat::Pct90, &TimeStat::Pct99,
+ &TimeStat::Max, &TimeStat::Sum};
+ switch (T) {
+ case GraphRenderer::StatType::NONE:
+ retval = 0.0;
+ break;
+ case GraphRenderer::StatType::COUNT:
+ retval = static_cast<double>(Count);
+ break;
+ default:
+ retval =
+ (*this).*DoubleStatPtrs[static_cast<int>(T) -
+ static_cast<int>(GraphRenderer::StatType::MIN)];
+ break;
+ }
+ return retval;
+}
+
+// Outputs a DOT format version of the Graph embedded in the GraphRenderer
+// object on OS. It does this in the expected way by itterating
+// through all edges then vertices and then outputting them and their
+// annotations.
+//
+// FIXME: output more information, better presented.
+void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, StatType ET, StatType EC,
+ StatType VT, StatType VC) {
+ OS << "digraph xray {\n";
+
+ if (VT != StatType::NONE)
+ OS << "node [shape=record];\n";
+
+ for (const auto &E : G.edges()) {
+ const auto &S = E.second.S;
+ OS << "F" << E.first.first << " -> "
+ << "F" << E.first.second << " [label=\"" << S.getString(ET) << "\"";
+ if (EC != StatType::NONE)
+ OS << " color=\""
+ << CHelper.getColorString(
+ std::sqrt(S.getDouble(EC) / G.GraphEdgeMax.getDouble(EC)))
+ << "\"";
+ OS << "];\n";
+ }
+
+ for (const auto &V : G.vertices()) {
+ const auto &VA = V.second;
+ if (V.first == 0)
+ continue;
+ OS << "F" << V.first << " [label=\"" << (VT != StatType::NONE ? "{" : "")
+ << escapeString(VA.SymbolName.size() > 40
+ ? VA.SymbolName.substr(0, 40) + "..."
+ : VA.SymbolName);
+ if (VT != StatType::NONE)
+ OS << "|" << VA.S.getString(VT) << "}\"";
+ else
+ OS << "\"";
+ if (VC != StatType::NONE)
+ OS << " color=\""
+ << CHelper.getColorString(
+ std::sqrt(VA.S.getDouble(VC) / G.GraphVertexMax.getDouble(VC)))
+ << "\"";
+ OS << "];\n";
+ }
+ OS << "}\n";
+}
+
+Expected<GraphRenderer> GraphRenderer::Factory::getGraphRenderer() {
+ InstrumentationMap Map;
+ if (!GraphInstrMap.empty()) {
+ auto InstrumentationMapOrError = loadInstrumentationMap(GraphInstrMap);
+ if (!InstrumentationMapOrError)
+ return joinErrors(
+ make_error<StringError>(
+ Twine("Cannot open instrumentation map '") + GraphInstrMap + "'",
+ std::make_error_code(std::errc::invalid_argument)),
+ InstrumentationMapOrError.takeError());
+ Map = std::move(*InstrumentationMapOrError);
+ }
+
+ const auto &FunctionAddresses = Map.getFunctionAddresses();
+
+ symbolize::LLVMSymbolizer Symbolizer;
+ const auto &Header = Trace.getFileHeader();
+
+ llvm::xray::FuncIdConversionHelper FuncIdHelper(InstrMap, Symbolizer,
+ FunctionAddresses);
+
+ xray::GraphRenderer GR(FuncIdHelper, DeduceSiblingCalls);
+ for (const auto &Record : Trace) {
+ auto E = GR.accountRecord(Record);
+ if (!E)
+ continue;
+
+ for (const auto &ThreadStack : GR.getPerThreadFunctionStack()) {
+ errs() << "Thread ID: " << ThreadStack.first << "\n";
+ auto Level = ThreadStack.second.size();
+ for (const auto &Entry : llvm::reverse(ThreadStack.second))
+ errs() << "#" << Level-- << "\t"
+ << FuncIdHelper.SymbolOrNumber(Entry.FuncId) << '\n';
+ }
+
+ if (!GraphKeepGoing)
+ return joinErrors(make_error<StringError>(
+ "Error encountered generating the call graph.",
+ std::make_error_code(std::errc::invalid_argument)),
+ std::move(E));
+
+ handleAllErrors(std::move(E),
+ [&](const ErrorInfoBase &E) { E.log(errs()); });
+ }
+
+ GR.G.GraphEdgeMax = {};
+ GR.G.GraphVertexMax = {};
+ GR.calculateEdgeStatistics();
+ GR.calculateVertexStatistics();
+
+ if (Header.CycleFrequency)
+ GR.normalizeStatistics(Header.CycleFrequency);
+
+ return GR;
+}
+
+// Here we register and implement the llvm-xray graph subcommand.
+// The bulk of this code reads in the options, opens the required files, uses
+// those files to create a context for analysing the xray trace, then there is a
+// short loop which actually analyses the trace, generates the graph and then
+// outputs it as a DOT.
+//
+// FIXME: include additional filtering and annalysis passes to provide more
+// specific useful information.
+static CommandRegistration Unused(&GraphC, []() -> Error {
+ GraphRenderer::Factory F;
+
+ F.KeepGoing = GraphKeepGoing;
+ F.DeduceSiblingCalls = GraphDeduceSiblingCalls;
+ F.InstrMap = GraphInstrMap;
+
+ auto TraceOrErr = loadTraceFile(GraphInput, true);
+
+ if (!TraceOrErr)
+ return make_error<StringError>(
+ Twine("Failed loading input file '") + GraphInput + "'",
+ make_error_code(llvm::errc::invalid_argument));
+
+ F.Trace = std::move(*TraceOrErr);
+ auto GROrError = F.getGraphRenderer();
+ if (!GROrError)
+ return GROrError.takeError();
+ auto &GR = *GROrError;
+
+ std::error_code EC;
+ raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF);
+ if (EC)
+ return make_error<StringError>(
+ Twine("Cannot open file '") + GraphOutput + "' for writing.", EC);
+
+ GR.exportGraphAsDOT(OS, GraphEdgeLabel, GraphEdgeColorType, GraphVertexLabel,
+ GraphVertexColorType);
+ return Error::success();
+});
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-graph.h b/contrib/libs/llvm14/tools/llvm-xray/xray-graph.h
new file mode 100644
index 00000000000..23372d40f05
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-graph.h
@@ -0,0 +1,231 @@
+//===-- xray-graph.h - XRay Function Call Graph Renderer --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Generate a DOT file to represent the function call graph encountered in
+// the trace.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef XRAY_GRAPH_H
+#define XRAY_GRAPH_H
+
+#include <string>
+#include <vector>
+
+#include "func-id-helper.h"
+#include "xray-color-helper.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/XRay/Graph.h"
+#include "llvm/XRay/Trace.h"
+#include "llvm/XRay/XRayRecord.h"
+
+namespace llvm {
+namespace xray {
+
+/// A class encapsulating the logic related to analyzing XRay traces, producting
+/// Graphs from them and then exporting those graphs for review.
+class GraphRenderer {
+public:
+ /// An enum for enumerating the various statistics gathered on latencies
+ enum class StatType { NONE, COUNT, MIN, MED, PCT90, PCT99, MAX, SUM };
+
+ /// An inner struct for common timing statistics information
+ struct TimeStat {
+ int64_t Count;
+ double Min;
+ double Median;
+ double Pct90;
+ double Pct99;
+ double Max;
+ double Sum;
+
+ std::string getString(StatType T) const;
+ double getDouble(StatType T) const;
+ };
+ using TimestampT = uint64_t;
+
+ /// An inner struct for storing edge attributes for our graph. Here the
+ /// attributes are mainly function call statistics.
+ ///
+ /// FIXME: expand to contain more information eg call latencies.
+ struct CallStats {
+ TimeStat S;
+ std::vector<TimestampT> Timings;
+ };
+
+ /// An Inner Struct for storing vertex attributes, at the moment just
+ /// SymbolNames, however in future we could store bulk function statistics.
+ ///
+ /// FIXME: Store more attributes based on instrumentation map.
+ struct FunctionStats {
+ std::string SymbolName;
+ TimeStat S = {};
+ };
+
+ struct FunctionAttr {
+ int32_t FuncId;
+ uint64_t TSC;
+ };
+
+ using FunctionStack = SmallVector<FunctionAttr, 4>;
+
+ using PerThreadFunctionStackMap = DenseMap<uint32_t, FunctionStack>;
+
+ class GraphT : public Graph<FunctionStats, CallStats, int32_t> {
+ public:
+ TimeStat GraphEdgeMax = {};
+ TimeStat GraphVertexMax = {};
+ };
+
+ GraphT G;
+ using VertexIdentifier = typename decltype(G)::VertexIdentifier;
+ using EdgeIdentifier = decltype(G)::EdgeIdentifier;
+
+ /// Use a Map to store the Function stack for each thread whilst building the
+ /// graph.
+ ///
+ /// FIXME: Perhaps we can Build this into LatencyAccountant? or vise versa?
+ PerThreadFunctionStackMap PerThreadFunctionStack;
+
+ /// Usefull object for getting human readable Symbol Names.
+ FuncIdConversionHelper FuncIdHelper;
+ bool DeduceSiblingCalls = false;
+ TimestampT CurrentMaxTSC = 0;
+
+ /// A private function to help implement the statistic generation functions;
+ template <typename U>
+ void getStats(U begin, U end, GraphRenderer::TimeStat &S);
+ void updateMaxStats(const TimeStat &S, TimeStat &M);
+
+ /// Calculates latency statistics for each edge and stores the data in the
+ /// Graph
+ void calculateEdgeStatistics();
+
+ /// Calculates latency statistics for each vertex and stores the data in the
+ /// Graph
+ void calculateVertexStatistics();
+
+ /// Normalises latency statistics for each edge and vertex by CycleFrequency;
+ void normalizeStatistics(double CycleFrequency);
+
+ /// An object to color gradients
+ ColorHelper CHelper;
+
+public:
+ /// Takes in a reference to a FuncIdHelper in order to have ready access to
+ /// Symbol names.
+ explicit GraphRenderer(const FuncIdConversionHelper &FuncIdHelper, bool DSC)
+ : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DSC),
+ CHelper(ColorHelper::SequentialScheme::OrRd) {
+ G[0] = {};
+ }
+
+ /// Process an Xray record and expand the graph.
+ ///
+ /// This Function will return true on success, or false if records are not
+ /// presented in per-thread call-tree DFS order. (That is for each thread the
+ /// Records should be in order runtime on an ideal system.)
+ ///
+ /// FIXME: Make this more robust against small irregularities.
+ Error accountRecord(const XRayRecord &Record);
+
+ const PerThreadFunctionStackMap &getPerThreadFunctionStack() const {
+ return PerThreadFunctionStack;
+ }
+
+ class Factory {
+ public:
+ bool KeepGoing;
+ bool DeduceSiblingCalls;
+ std::string InstrMap;
+ ::llvm::xray::Trace Trace;
+ Expected<GraphRenderer> getGraphRenderer();
+ };
+
+ /// Output the Embedded graph in DOT format on \p OS, labeling the edges by
+ /// \p T
+ void exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel = StatType::NONE,
+ StatType EdgeColor = StatType::NONE,
+ StatType VertexLabel = StatType::NONE,
+ StatType VertexColor = StatType::NONE);
+
+ /// Get a reference to the internal graph.
+ const GraphT &getGraph() { return G; }
+};
+
+/// Vector Sum of TimeStats
+inline GraphRenderer::TimeStat operator+(const GraphRenderer::TimeStat &A,
+ const GraphRenderer::TimeStat &B) {
+ return {A.Count + B.Count, A.Min + B.Min, A.Median + B.Median,
+ A.Pct90 + B.Pct90, A.Pct99 + B.Pct99, A.Max + B.Max,
+ A.Sum + B.Sum};
+}
+
+/// Vector Difference of Timestats
+inline GraphRenderer::TimeStat operator-(const GraphRenderer::TimeStat &A,
+ const GraphRenderer::TimeStat &B) {
+
+ return {A.Count - B.Count, A.Min - B.Min, A.Median - B.Median,
+ A.Pct90 - B.Pct90, A.Pct99 - B.Pct99, A.Max - B.Max,
+ A.Sum - B.Sum};
+}
+
+/// Scalar Diference of TimeStat and double
+inline GraphRenderer::TimeStat operator/(const GraphRenderer::TimeStat &A,
+ double B) {
+
+ return {static_cast<int64_t>(A.Count / B),
+ A.Min / B,
+ A.Median / B,
+ A.Pct90 / B,
+ A.Pct99 / B,
+ A.Max / B,
+ A.Sum / B};
+}
+
+/// Scalar product of TimeStat and Double
+inline GraphRenderer::TimeStat operator*(const GraphRenderer::TimeStat &A,
+ double B) {
+ return {static_cast<int64_t>(A.Count * B),
+ A.Min * B,
+ A.Median * B,
+ A.Pct90 * B,
+ A.Pct99 * B,
+ A.Max * B,
+ A.Sum * B};
+}
+
+/// Scalar product of double TimeStat
+inline GraphRenderer::TimeStat operator*(double A,
+ const GraphRenderer::TimeStat &B) {
+ return B * A;
+}
+
+/// Hadamard Product of TimeStats
+inline GraphRenderer::TimeStat operator*(const GraphRenderer::TimeStat &A,
+ const GraphRenderer::TimeStat &B) {
+ return {A.Count * B.Count, A.Min * B.Min, A.Median * B.Median,
+ A.Pct90 * B.Pct90, A.Pct99 * B.Pct99, A.Max * B.Max,
+ A.Sum * B.Sum};
+}
+
+/// Hadamard Division of TimeStats
+inline GraphRenderer::TimeStat operator/(const GraphRenderer::TimeStat &A,
+ const GraphRenderer::TimeStat &B) {
+ return {A.Count / B.Count, A.Min / B.Min, A.Median / B.Median,
+ A.Pct90 / B.Pct90, A.Pct99 / B.Pct99, A.Max / B.Max,
+ A.Sum / B.Sum};
+}
+} // namespace xray
+} // namespace llvm
+
+#endif // XRAY_GRAPH_H
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-registry.cpp b/contrib/libs/llvm14/tools/llvm-xray/xray-registry.cpp
new file mode 100644
index 00000000000..e5c253d2e8f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-registry.cpp
@@ -0,0 +1,40 @@
+//===- xray-registry.cpp: Implement a command registry. -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implement a simple subcommand registry.
+//
+//===----------------------------------------------------------------------===//
+#include "xray-registry.h"
+
+#include "llvm/Support/ManagedStatic.h"
+#include <unordered_map>
+
+namespace llvm {
+namespace xray {
+
+using HandlerType = std::function<Error()>;
+
+ManagedStatic<std::unordered_map<cl::SubCommand *, HandlerType>> Commands;
+
+CommandRegistration::CommandRegistration(cl::SubCommand *SC,
+ HandlerType Command) {
+ assert(Commands->count(SC) == 0 &&
+ "Attempting to overwrite a command handler");
+ assert(Command && "Attempting to register an empty std::function<Error()>");
+ (*Commands)[SC] = Command;
+}
+
+HandlerType dispatch(cl::SubCommand *SC) {
+ auto It = Commands->find(SC);
+ assert(It != Commands->end() &&
+ "Attempting to dispatch on un-registered SubCommand.");
+ return It->second;
+}
+
+} // namespace xray
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-registry.h b/contrib/libs/llvm14/tools/llvm-xray/xray-registry.h
new file mode 100644
index 00000000000..d6fae78ea53
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-registry.h
@@ -0,0 +1,40 @@
+//===- xray-registry.h - Define registry mechanism for commands. ----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implement a simple subcommand registry.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TOOLS_LLVM_XRAY_XRAY_REGISTRY_H
+#define TOOLS_LLVM_XRAY_XRAY_REGISTRY_H
+
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace xray {
+
+// Use |CommandRegistration| as a global initialiser that registers a function
+// and associates it with |SC|. This requires that a command has not been
+// registered to a given |SC|.
+//
+// Usage:
+//
+// // At namespace scope.
+// static CommandRegistration Unused(&MySubCommand, [] { ... });
+//
+struct CommandRegistration {
+ CommandRegistration(cl::SubCommand *SC, std::function<Error()> Command);
+};
+
+// Requires that |SC| is not null and has an associated function to it.
+std::function<Error()> dispatch(cl::SubCommand *SC);
+
+} // namespace xray
+} // namespace llvm
+
+#endif // TOOLS_LLVM_XRAY_XRAY_REGISTRY_H
diff --git a/contrib/libs/llvm14/tools/llvm-xray/xray-stacks.cpp b/contrib/libs/llvm14/tools/llvm-xray/xray-stacks.cpp
new file mode 100644
index 00000000000..d04b998dacc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/xray-stacks.cpp
@@ -0,0 +1,792 @@
+//===- xray-stacks.cpp: XRay Function Call Stack Accounting ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements stack-based accounting. It takes XRay traces, and
+// collates statistics across these traces to show a breakdown of time spent
+// at various points of the stack to provide insight into which functions
+// spend the most time in terms of a call stack. We provide a few
+// sorting/filtering options for zero'ing in on the useful stacks.
+//
+//===----------------------------------------------------------------------===//
+
+#include <forward_list>
+#include <numeric>
+
+#include "func-id-helper.h"
+#include "trie-node.h"
+#include "xray-registry.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/XRay/Graph.h"
+#include "llvm/XRay/InstrumentationMap.h"
+#include "llvm/XRay/Trace.h"
+
+using namespace llvm;
+using namespace llvm::xray;
+
+static cl::SubCommand Stack("stack", "Call stack accounting");
+static cl::list<std::string> StackInputs(cl::Positional,
+ cl::desc("<xray trace>"), cl::Required,
+ cl::sub(Stack), cl::OneOrMore);
+
+static cl::opt<bool>
+ StackKeepGoing("keep-going", cl::desc("Keep going on errors encountered"),
+ cl::sub(Stack), cl::init(false));
+static cl::alias StackKeepGoing2("k", cl::aliasopt(StackKeepGoing),
+ cl::desc("Alias for -keep-going"));
+
+// TODO: Does there need to be an option to deduce tail or sibling calls?
+
+static cl::opt<std::string> StacksInstrMap(
+ "instr_map",
+ cl::desc("instrumentation map used to identify function ids. "
+ "Currently supports elf file instrumentation maps."),
+ cl::sub(Stack), cl::init(""));
+static cl::alias StacksInstrMap2("m", cl::aliasopt(StacksInstrMap),
+ cl::desc("Alias for -instr_map"));
+
+static cl::opt<bool>
+ SeparateThreadStacks("per-thread-stacks",
+ cl::desc("Report top stacks within each thread id"),
+ cl::sub(Stack), cl::init(false));
+
+static cl::opt<bool>
+ AggregateThreads("aggregate-threads",
+ cl::desc("Aggregate stack times across threads"),
+ cl::sub(Stack), cl::init(false));
+
+static cl::opt<bool>
+ DumpAllStacks("all-stacks",
+ cl::desc("Dump sum of timings for all stacks. "
+ "By default separates stacks per-thread."),
+ cl::sub(Stack), cl::init(false));
+static cl::alias DumpAllStacksShort("all", cl::aliasopt(DumpAllStacks),
+ cl::desc("Alias for -all-stacks"));
+
+// TODO(kpw): Add other interesting formats. Perhaps chrome trace viewer format
+// possibly with aggregations or just a linear trace of timings.
+enum StackOutputFormat { HUMAN, FLAMETOOL };
+
+static cl::opt<StackOutputFormat> StacksOutputFormat(
+ "stack-format",
+ cl::desc("The format that output stacks should be "
+ "output in. Only applies with all-stacks."),
+ cl::values(
+ clEnumValN(HUMAN, "human",
+ "Human readable output. Only valid without -all-stacks."),
+ clEnumValN(FLAMETOOL, "flame",
+ "Format consumable by Brendan Gregg's FlameGraph tool. "
+ "Only valid with -all-stacks.")),
+ cl::sub(Stack), cl::init(HUMAN));
+
+// Types of values for each stack in a CallTrie.
+enum class AggregationType {
+ TOTAL_TIME, // The total time spent in a stack and its callees.
+ INVOCATION_COUNT // The number of times the stack was invoked.
+};
+
+static cl::opt<AggregationType> RequestedAggregation(
+ "aggregation-type",
+ cl::desc("The type of aggregation to do on call stacks."),
+ cl::values(
+ clEnumValN(
+ AggregationType::TOTAL_TIME, "time",
+ "Capture the total time spent in an all invocations of a stack."),
+ clEnumValN(AggregationType::INVOCATION_COUNT, "count",
+ "Capture the number of times a stack was invoked. "
+ "In flamegraph mode, this count also includes invocations "
+ "of all callees.")),
+ cl::sub(Stack), cl::init(AggregationType::TOTAL_TIME));
+
+/// A helper struct to work with formatv and XRayRecords. Makes it easier to
+/// use instrumentation map names or addresses in formatted output.
+struct format_xray_record : public FormatAdapter<XRayRecord> {
+ explicit format_xray_record(XRayRecord record,
+ const FuncIdConversionHelper &conv)
+ : FormatAdapter<XRayRecord>(std::move(record)), Converter(&conv) {}
+ void format(raw_ostream &Stream, StringRef Style) override {
+ Stream << formatv(
+ "{FuncId: \"{0}\", ThreadId: \"{1}\", RecordType: \"{2}\"}",
+ Converter->SymbolOrNumber(Item.FuncId), Item.TId,
+ DecodeRecordType(Item.RecordType));
+ }
+
+private:
+ Twine DecodeRecordType(uint16_t recordType) {
+ switch (recordType) {
+ case 0:
+ return Twine("Fn Entry");
+ case 1:
+ return Twine("Fn Exit");
+ default:
+ // TODO: Add Tail exit when it is added to llvm/XRay/XRayRecord.h
+ return Twine("Unknown");
+ }
+ }
+
+ const FuncIdConversionHelper *Converter;
+};
+
+/// The stack command will take a set of XRay traces as arguments, and collects
+/// information about the stacks of instrumented functions that appear in the
+/// traces. We track the following pieces of information:
+///
+/// - Total time: amount of time/cycles accounted for in the traces.
+/// - Stack count: number of times a specific stack appears in the
+/// traces. Only instrumented functions show up in stacks.
+/// - Cumulative stack time: amount of time spent in a stack accumulated
+/// across the invocations in the traces.
+/// - Cumulative local time: amount of time spent in each instrumented
+/// function showing up in a specific stack, accumulated across the traces.
+///
+/// Example output for the kind of data we'd like to provide looks like the
+/// following:
+///
+/// Total time: 3.33234 s
+/// Stack ID: ...
+/// Stack Count: 2093
+/// # Function Local Time (%) Stack Time (%)
+/// 0 main 2.34 ms 0.07% 3.33234 s 100%
+/// 1 foo() 3.30000 s 99.02% 3.33 s 99.92%
+/// 2 bar() 30 ms 0.90% 30 ms 0.90%
+///
+/// We can also show distributions of the function call durations with
+/// statistics at each level of the stack. This works by doing the following
+/// algorithm:
+///
+/// 1. When unwinding, record the duration of each unwound function associated
+/// with the path up to which the unwinding stops. For example:
+///
+/// Step Duration (? means has start time)
+///
+/// push a <start time> a = ?
+/// push b <start time> a = ?, a->b = ?
+/// push c <start time> a = ?, a->b = ?, a->b->c = ?
+/// pop c <end time> a = ?, a->b = ?, emit duration(a->b->c)
+/// pop b <end time> a = ?, emit duration(a->b)
+/// push c <start time> a = ?, a->c = ?
+/// pop c <end time> a = ?, emit duration(a->c)
+/// pop a <end time> emit duration(a)
+///
+/// 2. We then account for the various stacks we've collected, and for each of
+/// them will have measurements that look like the following (continuing
+/// with the above simple example):
+///
+/// c : [<id("a->b->c"), [durations]>, <id("a->c"), [durations]>]
+/// b : [<id("a->b"), [durations]>]
+/// a : [<id("a"), [durations]>]
+///
+/// This allows us to compute, for each stack id, and each function that
+/// shows up in the stack, some important statistics like:
+///
+/// - median
+/// - 99th percentile
+/// - mean + stddev
+/// - count
+///
+/// 3. For cases where we don't have durations for some of the higher levels
+/// of the stack (perhaps instrumentation wasn't activated when the stack was
+/// entered), we can mark them appropriately.
+///
+/// Computing this data also allows us to implement lookup by call stack nodes,
+/// so that we can find functions that show up in multiple stack traces and
+/// show the statistical properties of that function in various contexts. We
+/// can compute information similar to the following:
+///
+/// Function: 'c'
+/// Stacks: 2 / 2
+/// Stack ID: ...
+/// Stack Count: ...
+/// # Function ...
+/// 0 a ...
+/// 1 b ...
+/// 2 c ...
+///
+/// Stack ID: ...
+/// Stack Count: ...
+/// # Function ...
+/// 0 a ...
+/// 1 c ...
+/// ----------------...
+///
+/// Function: 'b'
+/// Stacks: 1 / 2
+/// Stack ID: ...
+/// Stack Count: ...
+/// # Function ...
+/// 0 a ...
+/// 1 b ...
+/// 2 c ...
+///
+///
+/// To do this we require a Trie data structure that will allow us to represent
+/// all the call stacks of instrumented functions in an easily traversible
+/// manner when we do the aggregations and lookups. For instrumented call
+/// sequences like the following:
+///
+/// a()
+/// b()
+/// c()
+/// d()
+/// c()
+///
+/// We will have a representation like so:
+///
+/// a -> b -> c
+/// | |
+/// | +--> d
+/// |
+/// +--> c
+///
+/// We maintain a sequence of durations on the leaves and in the internal nodes
+/// as we go through and process every record from the XRay trace. We also
+/// maintain an index of unique functions, and provide a means of iterating
+/// through all the instrumented call stacks which we know about.
+
+namespace {
+struct StackDuration {
+ llvm::SmallVector<int64_t, 4> TerminalDurations;
+ llvm::SmallVector<int64_t, 4> IntermediateDurations;
+};
+} // namespace
+
+static StackDuration mergeStackDuration(const StackDuration &Left,
+ const StackDuration &Right) {
+ StackDuration Data{};
+ Data.TerminalDurations.reserve(Left.TerminalDurations.size() +
+ Right.TerminalDurations.size());
+ Data.IntermediateDurations.reserve(Left.IntermediateDurations.size() +
+ Right.IntermediateDurations.size());
+ // Aggregate the durations.
+ for (auto duration : Left.TerminalDurations)
+ Data.TerminalDurations.push_back(duration);
+ for (auto duration : Right.TerminalDurations)
+ Data.TerminalDurations.push_back(duration);
+
+ for (auto duration : Left.IntermediateDurations)
+ Data.IntermediateDurations.push_back(duration);
+ for (auto duration : Right.IntermediateDurations)
+ Data.IntermediateDurations.push_back(duration);
+ return Data;
+}
+
+using StackTrieNode = TrieNode<StackDuration>;
+
+template <AggregationType AggType>
+static std::size_t GetValueForStack(const StackTrieNode *Node);
+
+// When computing total time spent in a stack, we're adding the timings from
+// its callees and the timings from when it was a leaf.
+template <>
+std::size_t
+GetValueForStack<AggregationType::TOTAL_TIME>(const StackTrieNode *Node) {
+ auto TopSum = std::accumulate(Node->ExtraData.TerminalDurations.begin(),
+ Node->ExtraData.TerminalDurations.end(), 0uLL);
+ return std::accumulate(Node->ExtraData.IntermediateDurations.begin(),
+ Node->ExtraData.IntermediateDurations.end(), TopSum);
+}
+
+// Calculates how many times a function was invoked.
+// TODO: Hook up option to produce stacks
+template <>
+std::size_t
+GetValueForStack<AggregationType::INVOCATION_COUNT>(const StackTrieNode *Node) {
+ return Node->ExtraData.TerminalDurations.size() +
+ Node->ExtraData.IntermediateDurations.size();
+}
+
+// Make sure there are implementations for each enum value.
+template <AggregationType T> struct DependentFalseType : std::false_type {};
+
+template <AggregationType AggType>
+std::size_t GetValueForStack(const StackTrieNode *Node) {
+ static_assert(DependentFalseType<AggType>::value,
+ "No implementation found for aggregation type provided.");
+ return 0;
+}
+
+class StackTrie {
+ // Avoid the magic number of 4 propagated through the code with an alias.
+ // We use this SmallVector to track the root nodes in a call graph.
+ using RootVector = SmallVector<StackTrieNode *, 4>;
+
+ // We maintain pointers to the roots of the tries we see.
+ DenseMap<uint32_t, RootVector> Roots;
+
+ // We make sure all the nodes are accounted for in this list.
+ std::forward_list<StackTrieNode> NodeStore;
+
+ // A map of thread ids to pairs call stack trie nodes and their start times.
+ DenseMap<uint32_t, SmallVector<std::pair<StackTrieNode *, uint64_t>, 8>>
+ ThreadStackMap;
+
+ StackTrieNode *createTrieNode(uint32_t ThreadId, int32_t FuncId,
+ StackTrieNode *Parent) {
+ NodeStore.push_front(StackTrieNode{FuncId, Parent, {}, {{}, {}}});
+ auto I = NodeStore.begin();
+ auto *Node = &*I;
+ if (!Parent)
+ Roots[ThreadId].push_back(Node);
+ return Node;
+ }
+
+ StackTrieNode *findRootNode(uint32_t ThreadId, int32_t FuncId) {
+ const auto &RootsByThread = Roots[ThreadId];
+ auto I = find_if(RootsByThread,
+ [&](StackTrieNode *N) { return N->FuncId == FuncId; });
+ return (I == RootsByThread.end()) ? nullptr : *I;
+ }
+
+public:
+ enum class AccountRecordStatus {
+ OK, // Successfully processed
+ ENTRY_NOT_FOUND, // An exit record had no matching call stack entry
+ UNKNOWN_RECORD_TYPE
+ };
+
+ struct AccountRecordState {
+ // We keep track of whether the call stack is currently unwinding.
+ bool wasLastRecordExit;
+
+ static AccountRecordState CreateInitialState() { return {false}; }
+ };
+
+ AccountRecordStatus accountRecord(const XRayRecord &R,
+ 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;
+ // When we encounter a new function entry, we want to record the TSC for
+ // that entry, and the function id. Before doing so we check the top of
+ // the stack to see if there are callees that already represent this
+ // function.
+ if (TS.empty()) {
+ auto *Root = findRootNode(R.TId, R.FuncId);
+ TS.emplace_back(Root ? Root : createTrieNode(R.TId, R.FuncId, nullptr),
+ R.TSC);
+ return AccountRecordStatus::OK;
+ }
+
+ auto &Top = TS.back();
+ auto I = find_if(Top.first->Callees,
+ [&](StackTrieNode *N) { return N->FuncId == R.FuncId; });
+ if (I == Top.first->Callees.end()) {
+ // We didn't find the callee in the stack trie, so we're going to
+ // add to the stack then set up the pointers properly.
+ auto N = createTrieNode(R.TId, R.FuncId, Top.first);
+ Top.first->Callees.emplace_back(N);
+
+ // Top may be invalidated after this statement.
+ TS.emplace_back(N, R.TSC);
+ } else {
+ // We found the callee in the stack trie, so we'll use that pointer
+ // instead, add it to the stack associated with the TSC.
+ TS.emplace_back(*I, R.TSC);
+ }
+ return AccountRecordStatus::OK;
+ }
+ case RecordTypes::EXIT:
+ case RecordTypes::TAIL_EXIT: {
+ bool wasLastRecordExit = state->wasLastRecordExit;
+ state->wasLastRecordExit = true;
+ // The exit case is more interesting, since we want to be able to deduce
+ // missing exit records. To do that properly, we need to look up the stack
+ // and see whether the exit record matches any of the entry records. If it
+ // does match, we attempt to record the durations as we pop the stack to
+ // where we see the parent.
+ if (TS.empty()) {
+ // Short circuit, and say we can't find it.
+
+ return AccountRecordStatus::ENTRY_NOT_FOUND;
+ }
+
+ auto FunctionEntryMatch = find_if(
+ reverse(TS), [&](const std::pair<StackTrieNode *, uint64_t> &E) {
+ return E.first->FuncId == R.FuncId;
+ });
+ auto status = AccountRecordStatus::OK;
+ if (FunctionEntryMatch == TS.rend()) {
+ status = AccountRecordStatus::ENTRY_NOT_FOUND;
+ } else {
+ // Account for offset of 1 between reverse and forward iterators. We
+ // want the forward iterator to include the function that is exited.
+ ++FunctionEntryMatch;
+ }
+ auto I = FunctionEntryMatch.base();
+ for (auto &E : make_range(I, TS.end() - 1))
+ E.first->ExtraData.IntermediateDurations.push_back(
+ std::max(E.second, R.TSC) - std::min(E.second, R.TSC));
+ auto &Deepest = TS.back();
+ if (wasLastRecordExit)
+ Deepest.first->ExtraData.IntermediateDurations.push_back(
+ std::max(Deepest.second, R.TSC) - std::min(Deepest.second, R.TSC));
+ else
+ Deepest.first->ExtraData.TerminalDurations.push_back(
+ std::max(Deepest.second, R.TSC) - std::min(Deepest.second, R.TSC));
+ TS.erase(I, TS.end());
+ return status;
+ }
+ }
+ return AccountRecordStatus::UNKNOWN_RECORD_TYPE;
+ }
+
+ bool isEmpty() const { return Roots.empty(); }
+
+ void printStack(raw_ostream &OS, const StackTrieNode *Top,
+ FuncIdConversionHelper &FN) {
+ // Traverse the pointers up to the parent, noting the sums, then print
+ // in reverse order (callers at top, callees down bottom).
+ SmallVector<const StackTrieNode *, 8> CurrentStack;
+ for (auto *F = Top; F != nullptr; F = F->Parent)
+ CurrentStack.push_back(F);
+ int Level = 0;
+ OS << formatv("{0,-5} {1,-60} {2,+12} {3,+16}\n", "lvl", "function",
+ "count", "sum");
+ for (auto *F : reverse(drop_begin(CurrentStack))) {
+ auto Sum = std::accumulate(F->ExtraData.IntermediateDurations.begin(),
+ F->ExtraData.IntermediateDurations.end(), 0LL);
+ auto FuncId = FN.SymbolOrNumber(F->FuncId);
+ OS << formatv("#{0,-4} {1,-60} {2,+12} {3,+16}\n", Level++,
+ FuncId.size() > 60 ? FuncId.substr(0, 57) + "..." : FuncId,
+ F->ExtraData.IntermediateDurations.size(), Sum);
+ }
+ auto *Leaf = *CurrentStack.begin();
+ auto LeafSum =
+ std::accumulate(Leaf->ExtraData.TerminalDurations.begin(),
+ Leaf->ExtraData.TerminalDurations.end(), 0LL);
+ auto LeafFuncId = FN.SymbolOrNumber(Leaf->FuncId);
+ OS << formatv("#{0,-4} {1,-60} {2,+12} {3,+16}\n", Level++,
+ LeafFuncId.size() > 60 ? LeafFuncId.substr(0, 57) + "..."
+ : LeafFuncId,
+ Leaf->ExtraData.TerminalDurations.size(), LeafSum);
+ OS << "\n";
+ }
+
+ /// Prints top stacks for each thread.
+ void printPerThread(raw_ostream &OS, FuncIdConversionHelper &FN) {
+ for (auto iter : Roots) {
+ OS << "Thread " << iter.first << ":\n";
+ print(OS, FN, iter.second);
+ OS << "\n";
+ }
+ }
+
+ /// Prints timing sums for each stack in each threads.
+ template <AggregationType AggType>
+ void printAllPerThread(raw_ostream &OS, FuncIdConversionHelper &FN,
+ StackOutputFormat format) {
+ for (auto iter : Roots) {
+ uint32_t threadId = iter.first;
+ RootVector &perThreadRoots = iter.second;
+ bool reportThreadId = true;
+ printAll<AggType>(OS, FN, perThreadRoots, threadId, reportThreadId);
+ }
+ }
+
+ /// Prints top stacks from looking at all the leaves and ignoring thread IDs.
+ /// Stacks that consist of the same function IDs but were called in different
+ /// thread IDs are not considered unique in this printout.
+ void printIgnoringThreads(raw_ostream &OS, FuncIdConversionHelper &FN) {
+ RootVector RootValues;
+
+ // Function to pull the values out of a map iterator.
+ using RootsType = decltype(Roots.begin())::value_type;
+ auto MapValueFn = [](const RootsType &Value) { return Value.second; };
+
+ for (const auto &RootNodeRange :
+ make_range(map_iterator(Roots.begin(), MapValueFn),
+ map_iterator(Roots.end(), MapValueFn))) {
+ for (auto *RootNode : RootNodeRange)
+ RootValues.push_back(RootNode);
+ }
+
+ print(OS, FN, RootValues);
+ }
+
+ /// Creates a merged list of Tries for unique stacks that disregards their
+ /// thread IDs.
+ RootVector mergeAcrossThreads(std::forward_list<StackTrieNode> &NodeStore) {
+ RootVector MergedByThreadRoots;
+ for (auto MapIter : Roots) {
+ const auto &RootNodeVector = MapIter.second;
+ for (auto *Node : RootNodeVector) {
+ auto MaybeFoundIter =
+ find_if(MergedByThreadRoots, [Node](StackTrieNode *elem) {
+ return Node->FuncId == elem->FuncId;
+ });
+ if (MaybeFoundIter == MergedByThreadRoots.end()) {
+ MergedByThreadRoots.push_back(Node);
+ } else {
+ MergedByThreadRoots.push_back(mergeTrieNodes(
+ **MaybeFoundIter, *Node, nullptr, NodeStore, mergeStackDuration));
+ MergedByThreadRoots.erase(MaybeFoundIter);
+ }
+ }
+ }
+ return MergedByThreadRoots;
+ }
+
+ /// Print timing sums for all stacks merged by Thread ID.
+ template <AggregationType AggType>
+ void printAllAggregatingThreads(raw_ostream &OS, FuncIdConversionHelper &FN,
+ StackOutputFormat format) {
+ std::forward_list<StackTrieNode> AggregatedNodeStore;
+ RootVector MergedByThreadRoots = mergeAcrossThreads(AggregatedNodeStore);
+ bool reportThreadId = false;
+ printAll<AggType>(OS, FN, MergedByThreadRoots,
+ /*threadId*/ 0, reportThreadId);
+ }
+
+ /// Merges the trie by thread id before printing top stacks.
+ void printAggregatingThreads(raw_ostream &OS, FuncIdConversionHelper &FN) {
+ std::forward_list<StackTrieNode> AggregatedNodeStore;
+ RootVector MergedByThreadRoots = mergeAcrossThreads(AggregatedNodeStore);
+ print(OS, FN, MergedByThreadRoots);
+ }
+
+ // TODO: Add a format option when more than one are supported.
+ template <AggregationType AggType>
+ void printAll(raw_ostream &OS, FuncIdConversionHelper &FN,
+ RootVector RootValues, uint32_t ThreadId, bool ReportThread) {
+ SmallVector<const StackTrieNode *, 16> S;
+ for (const auto *N : RootValues) {
+ S.clear();
+ S.push_back(N);
+ while (!S.empty()) {
+ auto *Top = S.pop_back_val();
+ printSingleStack<AggType>(OS, FN, ReportThread, ThreadId, Top);
+ for (const auto *C : Top->Callees)
+ S.push_back(C);
+ }
+ }
+ }
+
+ /// Prints values for stacks in a format consumable for the flamegraph.pl
+ /// tool. This is a line based format that lists each level in the stack
+ /// hierarchy in a semicolon delimited form followed by a space and a numeric
+ /// value. If breaking down by thread, the thread ID will be added as the
+ /// root level of the stack.
+ template <AggregationType AggType>
+ void printSingleStack(raw_ostream &OS, FuncIdConversionHelper &Converter,
+ bool ReportThread, uint32_t ThreadId,
+ const StackTrieNode *Node) {
+ if (ReportThread)
+ OS << "thread_" << ThreadId << ";";
+ SmallVector<const StackTrieNode *, 5> lineage{};
+ lineage.push_back(Node);
+ while (lineage.back()->Parent != nullptr)
+ lineage.push_back(lineage.back()->Parent);
+ while (!lineage.empty()) {
+ OS << Converter.SymbolOrNumber(lineage.back()->FuncId) << ";";
+ lineage.pop_back();
+ }
+ OS << " " << GetValueForStack<AggType>(Node) << "\n";
+ }
+
+ void print(raw_ostream &OS, FuncIdConversionHelper &FN,
+ RootVector RootValues) {
+ // Go through each of the roots, and traverse the call stack, producing the
+ // aggregates as you go along. Remember these aggregates and stacks, and
+ // show summary statistics about:
+ //
+ // - Total number of unique stacks
+ // - Top 10 stacks by count
+ // - Top 10 stacks by aggregate duration
+ SmallVector<std::pair<const StackTrieNode *, uint64_t>, 11>
+ TopStacksByCount;
+ SmallVector<std::pair<const StackTrieNode *, uint64_t>, 11> TopStacksBySum;
+ auto greater_second =
+ [](const std::pair<const StackTrieNode *, uint64_t> &A,
+ const std::pair<const StackTrieNode *, uint64_t> &B) {
+ return A.second > B.second;
+ };
+ uint64_t UniqueStacks = 0;
+ for (const auto *N : RootValues) {
+ SmallVector<const StackTrieNode *, 16> S;
+ S.emplace_back(N);
+
+ while (!S.empty()) {
+ auto *Top = S.pop_back_val();
+
+ // We only start printing the stack (by walking up the parent pointers)
+ // when we get to a leaf function.
+ if (!Top->ExtraData.TerminalDurations.empty()) {
+ ++UniqueStacks;
+ auto TopSum =
+ std::accumulate(Top->ExtraData.TerminalDurations.begin(),
+ Top->ExtraData.TerminalDurations.end(), 0uLL);
+ {
+ auto E = std::make_pair(Top, TopSum);
+ TopStacksBySum.insert(
+ llvm::lower_bound(TopStacksBySum, E, greater_second), E);
+ if (TopStacksBySum.size() == 11)
+ TopStacksBySum.pop_back();
+ }
+ {
+ auto E =
+ std::make_pair(Top, Top->ExtraData.TerminalDurations.size());
+ TopStacksByCount.insert(
+ llvm::lower_bound(TopStacksByCount, E, greater_second), E);
+ if (TopStacksByCount.size() == 11)
+ TopStacksByCount.pop_back();
+ }
+ }
+ for (const auto *C : Top->Callees)
+ S.push_back(C);
+ }
+ }
+
+ // Now print the statistics in the end.
+ OS << "\n";
+ OS << "Unique Stacks: " << UniqueStacks << "\n";
+ OS << "Top 10 Stacks by leaf sum:\n\n";
+ for (const auto &P : TopStacksBySum) {
+ OS << "Sum: " << P.second << "\n";
+ printStack(OS, P.first, FN);
+ }
+ OS << "\n";
+ OS << "Top 10 Stacks by leaf count:\n\n";
+ for (const auto &P : TopStacksByCount) {
+ OS << "Count: " << P.second << "\n";
+ printStack(OS, P.first, FN);
+ }
+ OS << "\n";
+ }
+};
+
+static std::string CreateErrorMessage(StackTrie::AccountRecordStatus Error,
+ const XRayRecord &Record,
+ const FuncIdConversionHelper &Converter) {
+ switch (Error) {
+ case StackTrie::AccountRecordStatus::ENTRY_NOT_FOUND:
+ return std::string(
+ formatv("Found record {0} with no matching function entry\n",
+ format_xray_record(Record, Converter)));
+ default:
+ return std::string(formatv("Unknown error type for record {0}\n",
+ format_xray_record(Record, Converter)));
+ }
+}
+
+static CommandRegistration Unused(&Stack, []() -> Error {
+ // Load each file provided as a command-line argument. For each one of them
+ // account to a single StackTrie, and just print the whole trie for now.
+ StackTrie ST;
+ InstrumentationMap Map;
+ if (!StacksInstrMap.empty()) {
+ auto InstrumentationMapOrError = loadInstrumentationMap(StacksInstrMap);
+ if (!InstrumentationMapOrError)
+ return joinErrors(
+ make_error<StringError>(
+ Twine("Cannot open instrumentation map: ") + StacksInstrMap,
+ std::make_error_code(std::errc::invalid_argument)),
+ InstrumentationMapOrError.takeError());
+ Map = std::move(*InstrumentationMapOrError);
+ }
+
+ if (SeparateThreadStacks && AggregateThreads)
+ return make_error<StringError>(
+ Twine("Can't specify options for per thread reporting and reporting "
+ "that aggregates threads."),
+ std::make_error_code(std::errc::invalid_argument));
+
+ if (!DumpAllStacks && StacksOutputFormat != HUMAN)
+ return make_error<StringError>(
+ Twine("Can't specify a non-human format without -all-stacks."),
+ std::make_error_code(std::errc::invalid_argument));
+
+ if (DumpAllStacks && StacksOutputFormat == HUMAN)
+ return make_error<StringError>(
+ Twine("You must specify a non-human format when reporting with "
+ "-all-stacks."),
+ std::make_error_code(std::errc::invalid_argument));
+
+ symbolize::LLVMSymbolizer Symbolizer;
+ FuncIdConversionHelper FuncIdHelper(StacksInstrMap, Symbolizer,
+ Map.getFunctionAddresses());
+ // TODO: Someday, support output to files instead of just directly to
+ // standard output.
+ for (const auto &Filename : StackInputs) {
+ auto TraceOrErr = loadTraceFile(Filename);
+ if (!TraceOrErr) {
+ if (!StackKeepGoing)
+ return joinErrors(
+ make_error<StringError>(
+ Twine("Failed loading input file '") + Filename + "'",
+ std::make_error_code(std::errc::invalid_argument)),
+ TraceOrErr.takeError());
+ logAllUnhandledErrors(TraceOrErr.takeError(), errs());
+ continue;
+ }
+ auto &T = *TraceOrErr;
+ StackTrie::AccountRecordState AccountRecordState =
+ StackTrie::AccountRecordState::CreateInitialState();
+ for (const auto &Record : T) {
+ auto error = ST.accountRecord(Record, &AccountRecordState);
+ if (error != StackTrie::AccountRecordStatus::OK) {
+ if (!StackKeepGoing)
+ return make_error<StringError>(
+ CreateErrorMessage(error, Record, FuncIdHelper),
+ make_error_code(errc::illegal_byte_sequence));
+ errs() << CreateErrorMessage(error, Record, FuncIdHelper);
+ }
+ }
+ }
+ if (ST.isEmpty()) {
+ return make_error<StringError>(
+ "No instrumented calls were accounted in the input file.",
+ make_error_code(errc::result_out_of_range));
+ }
+
+ // Report the stacks in a long form mode for another tool to analyze.
+ if (DumpAllStacks) {
+ if (AggregateThreads) {
+ switch (RequestedAggregation) {
+ case AggregationType::TOTAL_TIME:
+ ST.printAllAggregatingThreads<AggregationType::TOTAL_TIME>(
+ outs(), FuncIdHelper, StacksOutputFormat);
+ break;
+ case AggregationType::INVOCATION_COUNT:
+ ST.printAllAggregatingThreads<AggregationType::INVOCATION_COUNT>(
+ outs(), FuncIdHelper, StacksOutputFormat);
+ break;
+ }
+ } else {
+ switch (RequestedAggregation) {
+ case AggregationType::TOTAL_TIME:
+ ST.printAllPerThread<AggregationType::TOTAL_TIME>(outs(), FuncIdHelper,
+ StacksOutputFormat);
+ break;
+ case AggregationType::INVOCATION_COUNT:
+ ST.printAllPerThread<AggregationType::INVOCATION_COUNT>(
+ outs(), FuncIdHelper, StacksOutputFormat);
+ break;
+ }
+ }
+ return Error::success();
+ }
+
+ // We're only outputting top stacks.
+ if (AggregateThreads) {
+ ST.printAggregatingThreads(outs(), FuncIdHelper);
+ } else if (SeparateThreadStacks) {
+ ST.printPerThread(outs(), FuncIdHelper);
+ } else {
+ ST.printIgnoringThreads(outs(), FuncIdHelper);
+ }
+ return Error::success();
+});
diff --git a/contrib/libs/llvm14/tools/llvm-xray/ya.make b/contrib/libs/llvm14/tools/llvm-xray/ya.make
new file mode 100644
index 00000000000..f39039465c8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-xray/ya.make
@@ -0,0 +1,52 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/DebugInfo/MSF
+ contrib/libs/llvm14/lib/DebugInfo/PDB
+ contrib/libs/llvm14/lib/DebugInfo/Symbolize
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/XRay
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/llvm-xray
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ func-id-helper.cpp
+ llvm-xray.cpp
+ xray-account.cpp
+ xray-color-helper.cpp
+ xray-converter.cpp
+ xray-extract.cpp
+ xray-fdr-dump.cpp
+ xray-graph-diff.cpp
+ xray-graph.cpp
+ xray-registry.cpp
+ xray-stacks.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/lto/LTODisassembler.cpp b/contrib/libs/llvm14/tools/lto/LTODisassembler.cpp
new file mode 100644
index 00000000000..1c4cc6af29d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/lto/LTODisassembler.cpp
@@ -0,0 +1,25 @@
+//===-- LTODisassembler.cpp - LTO Disassembler interface ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This function provides utility methods used by clients of libLTO that want
+// to use the disassembler.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-c/lto.h"
+#include "llvm/Support/TargetSelect.h"
+
+using namespace llvm;
+
+void lto_initialize_disassembler() {
+ // Initialize targets and assembly printers/parsers.
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmParsers();
+ llvm::InitializeAllDisassemblers();
+}
diff --git a/contrib/libs/llvm14/tools/lto/lto.cpp b/contrib/libs/llvm14/tools/lto/lto.cpp
new file mode 100644
index 00000000000..dffab5f0fac
--- /dev/null
+++ b/contrib/libs/llvm14/tools/lto/lto.cpp
@@ -0,0 +1,707 @@
+//===-lto.cpp - LLVM Link Time Optimizer ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Link Time Optimization library. This library is
+// intended to be used by linker to optimize code at link time.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-c/lto.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/LTO/legacy/LTOCodeGenerator.h"
+#include "llvm/LTO/legacy/LTOModule.h"
+#include "llvm/LTO/legacy/ThinLTOCodeGenerator.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+static codegen::RegisterCodeGenFlags CGF;
+
+// extra command-line flags needed for LTOCodeGenerator
+static cl::opt<char>
+OptLevel("O",
+ cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
+ "(default = '-O2')"),
+ cl::Prefix,
+ cl::ZeroOrMore,
+ cl::init('2'));
+
+static cl::opt<bool> EnableFreestanding(
+ "lto-freestanding", cl::init(false),
+ cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"));
+
+#ifdef NDEBUG
+static bool VerifyByDefault = false;
+#else
+static bool VerifyByDefault = true;
+#endif
+
+static cl::opt<bool> DisableVerify(
+ "disable-llvm-verifier", cl::init(!VerifyByDefault),
+ cl::desc("Don't run the LLVM verifier during the optimization pipeline"));
+
+// Holds most recent error string.
+// *** Not thread safe ***
+static std::string sLastErrorString;
+
+// Holds the initialization state of the LTO module.
+// *** Not thread safe ***
+static bool initialized = false;
+
+// Represent the state of parsing command line debug options.
+static enum class OptParsingState {
+ NotParsed, // Initial state.
+ Early, // After lto_set_debug_options is called.
+ Done // After maybeParseOptions is called.
+} optionParsingState = OptParsingState::NotParsed;
+
+static LLVMContext *LTOContext = nullptr;
+
+struct LTOToolDiagnosticHandler : public DiagnosticHandler {
+ bool handleDiagnostics(const DiagnosticInfo &DI) override {
+ if (DI.getSeverity() != DS_Error) {
+ DiagnosticPrinterRawOStream DP(errs());
+ DI.print(DP);
+ errs() << '\n';
+ return true;
+ }
+ sLastErrorString = "";
+ {
+ raw_string_ostream Stream(sLastErrorString);
+ DiagnosticPrinterRawOStream DP(Stream);
+ DI.print(DP);
+ }
+ return true;
+ }
+};
+
+// Initialize the configured targets if they have not been initialized.
+static void lto_initialize() {
+ if (!initialized) {
+#ifdef _WIN32
+ // Dialog box on crash disabling doesn't work across DLL boundaries, so do
+ // it here.
+ llvm::sys::DisableSystemDialogsOnCrash();
+#endif
+
+ InitializeAllTargetInfos();
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmParsers();
+ InitializeAllAsmPrinters();
+ InitializeAllDisassemblers();
+
+ static LLVMContext Context;
+ LTOContext = &Context;
+ LTOContext->setDiagnosticHandler(
+ std::make_unique<LTOToolDiagnosticHandler>(), true);
+ initialized = true;
+ }
+}
+
+namespace {
+
+static void handleLibLTODiagnostic(lto_codegen_diagnostic_severity_t Severity,
+ const char *Msg, void *) {
+ sLastErrorString = Msg;
+}
+
+// This derived class owns the native object file. This helps implement the
+// libLTO API semantics, which require that the code generator owns the object
+// file.
+struct LibLTOCodeGenerator : LTOCodeGenerator {
+ LibLTOCodeGenerator() : LTOCodeGenerator(*LTOContext) { init(); }
+ LibLTOCodeGenerator(std::unique_ptr<LLVMContext> Context)
+ : LTOCodeGenerator(*Context), OwnedContext(std::move(Context)) {
+ init();
+ }
+
+ // Reset the module first in case MergedModule is created in OwnedContext.
+ // Module must be destructed before its context gets destructed.
+ ~LibLTOCodeGenerator() { resetMergedModule(); }
+
+ void init() { setDiagnosticHandler(handleLibLTODiagnostic, nullptr); }
+
+ std::unique_ptr<MemoryBuffer> NativeObjectFile;
+ std::unique_ptr<LLVMContext> OwnedContext;
+};
+
+}
+
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LibLTOCodeGenerator, lto_code_gen_t)
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ThinLTOCodeGenerator, thinlto_code_gen_t)
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LTOModule, lto_module_t)
+
+// Convert the subtarget features into a string to pass to LTOCodeGenerator.
+static void lto_add_attrs(lto_code_gen_t cg) {
+ LTOCodeGenerator *CG = unwrap(cg);
+ CG->setAttrs(codegen::getMAttrs());
+
+ if (OptLevel < '0' || OptLevel > '3')
+ report_fatal_error("Optimization level must be between 0 and 3");
+ CG->setOptLevel(OptLevel - '0');
+ CG->setFreestanding(EnableFreestanding);
+ CG->setDisableVerify(DisableVerify);
+}
+
+extern const char* lto_get_version() {
+ return LTOCodeGenerator::getVersionString();
+}
+
+const char* lto_get_error_message() {
+ return sLastErrorString.c_str();
+}
+
+bool lto_module_is_object_file(const char* path) {
+ return LTOModule::isBitcodeFile(StringRef(path));
+}
+
+bool lto_module_is_object_file_for_target(const char* path,
+ const char* target_triplet_prefix) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = MemoryBuffer::getFile(path);
+ if (!Buffer)
+ return false;
+ return LTOModule::isBitcodeForTarget(Buffer->get(),
+ StringRef(target_triplet_prefix));
+}
+
+bool lto_module_has_objc_category(const void *mem, size_t length) {
+ std::unique_ptr<MemoryBuffer> Buffer(LTOModule::makeBuffer(mem, length));
+ if (!Buffer)
+ return false;
+ LLVMContext Ctx;
+ ErrorOr<bool> Result = expectedToErrorOrAndEmitErrors(
+ Ctx, llvm::isBitcodeContainingObjCCategory(*Buffer));
+ return Result && *Result;
+}
+
+bool lto_module_is_object_file_in_memory(const void* mem, size_t length) {
+ return LTOModule::isBitcodeFile(mem, length);
+}
+
+bool
+lto_module_is_object_file_in_memory_for_target(const void* mem,
+ size_t length,
+ const char* target_triplet_prefix) {
+ std::unique_ptr<MemoryBuffer> buffer(LTOModule::makeBuffer(mem, length));
+ if (!buffer)
+ return false;
+ return LTOModule::isBitcodeForTarget(buffer.get(),
+ StringRef(target_triplet_prefix));
+}
+
+lto_module_t lto_module_create(const char* path) {
+ lto_initialize();
+ llvm::TargetOptions Options =
+ codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+ ErrorOr<std::unique_ptr<LTOModule>> M =
+ LTOModule::createFromFile(*LTOContext, StringRef(path), Options);
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
+}
+
+lto_module_t lto_module_create_from_fd(int fd, const char *path, size_t size) {
+ lto_initialize();
+ llvm::TargetOptions Options =
+ codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+ ErrorOr<std::unique_ptr<LTOModule>> M = LTOModule::createFromOpenFile(
+ *LTOContext, fd, StringRef(path), size, Options);
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
+}
+
+lto_module_t lto_module_create_from_fd_at_offset(int fd, const char *path,
+ size_t file_size,
+ size_t map_size,
+ off_t offset) {
+ lto_initialize();
+ llvm::TargetOptions Options =
+ codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+ ErrorOr<std::unique_ptr<LTOModule>> M = LTOModule::createFromOpenFileSlice(
+ *LTOContext, fd, StringRef(path), map_size, offset, Options);
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
+}
+
+lto_module_t lto_module_create_from_memory(const void* mem, size_t length) {
+ lto_initialize();
+ llvm::TargetOptions Options =
+ codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+ ErrorOr<std::unique_ptr<LTOModule>> M =
+ LTOModule::createFromBuffer(*LTOContext, mem, length, Options);
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
+}
+
+lto_module_t lto_module_create_from_memory_with_path(const void* mem,
+ size_t length,
+ const char *path) {
+ lto_initialize();
+ llvm::TargetOptions Options =
+ codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+ ErrorOr<std::unique_ptr<LTOModule>> M = LTOModule::createFromBuffer(
+ *LTOContext, mem, length, Options, StringRef(path));
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
+}
+
+lto_module_t lto_module_create_in_local_context(const void *mem, size_t length,
+ const char *path) {
+ lto_initialize();
+ llvm::TargetOptions Options =
+ codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+
+ // Create a local context. Ownership will be transferred to LTOModule.
+ std::unique_ptr<LLVMContext> Context = std::make_unique<LLVMContext>();
+ Context->setDiagnosticHandler(std::make_unique<LTOToolDiagnosticHandler>(),
+ true);
+
+ ErrorOr<std::unique_ptr<LTOModule>> M = LTOModule::createInLocalContext(
+ std::move(Context), mem, length, Options, StringRef(path));
+ if (!M)
+ return nullptr;
+ return wrap(M->release());
+}
+
+lto_module_t lto_module_create_in_codegen_context(const void *mem,
+ size_t length,
+ const char *path,
+ lto_code_gen_t cg) {
+ lto_initialize();
+ llvm::TargetOptions Options =
+ codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+ ErrorOr<std::unique_ptr<LTOModule>> M = LTOModule::createFromBuffer(
+ unwrap(cg)->getContext(), mem, length, Options, StringRef(path));
+ return wrap(M->release());
+}
+
+void lto_module_dispose(lto_module_t mod) { delete unwrap(mod); }
+
+const char* lto_module_get_target_triple(lto_module_t mod) {
+ return unwrap(mod)->getTargetTriple().c_str();
+}
+
+void lto_module_set_target_triple(lto_module_t mod, const char *triple) {
+ return unwrap(mod)->setTargetTriple(StringRef(triple));
+}
+
+unsigned int lto_module_get_num_symbols(lto_module_t mod) {
+ return unwrap(mod)->getSymbolCount();
+}
+
+const char* lto_module_get_symbol_name(lto_module_t mod, unsigned int index) {
+ return unwrap(mod)->getSymbolName(index).data();
+}
+
+lto_symbol_attributes lto_module_get_symbol_attribute(lto_module_t mod,
+ unsigned int index) {
+ return unwrap(mod)->getSymbolAttributes(index);
+}
+
+const char* lto_module_get_linkeropts(lto_module_t mod) {
+ return unwrap(mod)->getLinkerOpts().data();
+}
+
+lto_bool_t lto_module_get_macho_cputype(lto_module_t mod,
+ unsigned int *out_cputype,
+ unsigned int *out_cpusubtype) {
+ LTOModule *M = unwrap(mod);
+ Expected<uint32_t> CPUType = M->getMachOCPUType();
+ if (!CPUType) {
+ sLastErrorString = toString(CPUType.takeError());
+ return true;
+ }
+ *out_cputype = *CPUType;
+
+ Expected<uint32_t> CPUSubType = M->getMachOCPUSubType();
+ if (!CPUSubType) {
+ sLastErrorString = toString(CPUSubType.takeError());
+ return true;
+ }
+ *out_cpusubtype = *CPUSubType;
+
+ return false;
+}
+
+void lto_codegen_set_diagnostic_handler(lto_code_gen_t cg,
+ lto_diagnostic_handler_t diag_handler,
+ void *ctxt) {
+ unwrap(cg)->setDiagnosticHandler(diag_handler, ctxt);
+}
+
+static lto_code_gen_t createCodeGen(bool InLocalContext) {
+ lto_initialize();
+
+ TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+
+ LibLTOCodeGenerator *CodeGen =
+ InLocalContext ? new LibLTOCodeGenerator(std::make_unique<LLVMContext>())
+ : new LibLTOCodeGenerator();
+ CodeGen->setTargetOptions(Options);
+ return wrap(CodeGen);
+}
+
+lto_code_gen_t lto_codegen_create(void) {
+ return createCodeGen(/* InLocalContext */ false);
+}
+
+lto_code_gen_t lto_codegen_create_in_local_context(void) {
+ return createCodeGen(/* InLocalContext */ true);
+}
+
+void lto_codegen_dispose(lto_code_gen_t cg) { delete unwrap(cg); }
+
+bool lto_codegen_add_module(lto_code_gen_t cg, lto_module_t mod) {
+ return !unwrap(cg)->addModule(unwrap(mod));
+}
+
+void lto_codegen_set_module(lto_code_gen_t cg, lto_module_t mod) {
+ unwrap(cg)->setModule(std::unique_ptr<LTOModule>(unwrap(mod)));
+}
+
+bool lto_codegen_set_debug_model(lto_code_gen_t cg, lto_debug_model debug) {
+ unwrap(cg)->setDebugInfo(debug);
+ return false;
+}
+
+bool lto_codegen_set_pic_model(lto_code_gen_t cg, lto_codegen_model model) {
+ switch (model) {
+ case LTO_CODEGEN_PIC_MODEL_STATIC:
+ unwrap(cg)->setCodePICModel(Reloc::Static);
+ return false;
+ case LTO_CODEGEN_PIC_MODEL_DYNAMIC:
+ unwrap(cg)->setCodePICModel(Reloc::PIC_);
+ return false;
+ case LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC:
+ unwrap(cg)->setCodePICModel(Reloc::DynamicNoPIC);
+ return false;
+ case LTO_CODEGEN_PIC_MODEL_DEFAULT:
+ unwrap(cg)->setCodePICModel(None);
+ return false;
+ }
+ sLastErrorString = "Unknown PIC model";
+ return true;
+}
+
+void lto_codegen_set_cpu(lto_code_gen_t cg, const char *cpu) {
+ return unwrap(cg)->setCpu(cpu);
+}
+
+void lto_codegen_set_assembler_path(lto_code_gen_t cg, const char *path) {
+ // In here only for backwards compatibility. We use MC now.
+}
+
+void lto_codegen_set_assembler_args(lto_code_gen_t cg, const char **args,
+ int nargs) {
+ // In here only for backwards compatibility. We use MC now.
+}
+
+void lto_codegen_add_must_preserve_symbol(lto_code_gen_t cg,
+ const char *symbol) {
+ unwrap(cg)->addMustPreserveSymbol(symbol);
+}
+
+static void maybeParseOptions(lto_code_gen_t cg) {
+ if (optionParsingState != OptParsingState::Done) {
+ // Parse options if any were set by the lto_codegen_debug_options* function.
+ unwrap(cg)->parseCodeGenDebugOptions();
+ lto_add_attrs(cg);
+ optionParsingState = OptParsingState::Done;
+ }
+}
+
+bool lto_codegen_write_merged_modules(lto_code_gen_t cg, const char *path) {
+ maybeParseOptions(cg);
+ return !unwrap(cg)->writeMergedModules(path);
+}
+
+const void *lto_codegen_compile(lto_code_gen_t cg, size_t *length) {
+ maybeParseOptions(cg);
+ LibLTOCodeGenerator *CG = unwrap(cg);
+ CG->NativeObjectFile = CG->compile();
+ if (!CG->NativeObjectFile)
+ return nullptr;
+ *length = CG->NativeObjectFile->getBufferSize();
+ return CG->NativeObjectFile->getBufferStart();
+}
+
+bool lto_codegen_optimize(lto_code_gen_t cg) {
+ maybeParseOptions(cg);
+ return !unwrap(cg)->optimize();
+}
+
+const void *lto_codegen_compile_optimized(lto_code_gen_t cg, size_t *length) {
+ maybeParseOptions(cg);
+ LibLTOCodeGenerator *CG = unwrap(cg);
+ CG->NativeObjectFile = CG->compileOptimized();
+ if (!CG->NativeObjectFile)
+ return nullptr;
+ *length = CG->NativeObjectFile->getBufferSize();
+ return CG->NativeObjectFile->getBufferStart();
+}
+
+bool lto_codegen_compile_to_file(lto_code_gen_t cg, const char **name) {
+ maybeParseOptions(cg);
+ return !unwrap(cg)->compile_to_file(name);
+}
+
+void lto_set_debug_options(const char *const *options, int number) {
+ assert(optionParsingState == OptParsingState::NotParsed &&
+ "option processing already happened");
+ // Need to put each suboption in a null-terminated string before passing to
+ // parseCommandLineOptions().
+ std::vector<std::string> Options;
+ for (int i = 0; i < number; ++i)
+ Options.push_back(options[i]);
+
+ llvm::parseCommandLineOptions(Options);
+ optionParsingState = OptParsingState::Early;
+}
+
+void lto_codegen_debug_options(lto_code_gen_t cg, const char *opt) {
+ assert(optionParsingState != OptParsingState::Early &&
+ "early option processing already happened");
+ SmallVector<StringRef, 4> Options;
+ for (std::pair<StringRef, StringRef> o = getToken(opt); !o.first.empty();
+ o = getToken(o.second))
+ Options.push_back(o.first);
+
+ unwrap(cg)->setCodeGenDebugOptions(Options);
+}
+
+void lto_codegen_debug_options_array(lto_code_gen_t cg,
+ const char *const *options, int number) {
+ assert(optionParsingState != OptParsingState::Early &&
+ "early option processing already happened");
+ SmallVector<StringRef, 4> Options;
+ for (int i = 0; i < number; ++i)
+ Options.push_back(options[i]);
+ unwrap(cg)->setCodeGenDebugOptions(makeArrayRef(Options));
+}
+
+unsigned int lto_api_version() { return LTO_API_VERSION; }
+
+void lto_codegen_set_should_internalize(lto_code_gen_t cg,
+ bool ShouldInternalize) {
+ unwrap(cg)->setShouldInternalize(ShouldInternalize);
+}
+
+void lto_codegen_set_should_embed_uselists(lto_code_gen_t cg,
+ lto_bool_t ShouldEmbedUselists) {
+ unwrap(cg)->setShouldEmbedUselists(ShouldEmbedUselists);
+}
+
+lto_bool_t lto_module_has_ctor_dtor(lto_module_t mod) {
+ return unwrap(mod)->hasCtorDtor();
+}
+
+// ThinLTO API below
+
+thinlto_code_gen_t thinlto_create_codegen(void) {
+ lto_initialize();
+ ThinLTOCodeGenerator *CodeGen = new ThinLTOCodeGenerator();
+ CodeGen->setTargetOptions(
+ codegen::InitTargetOptionsFromCodeGenFlags(Triple()));
+ CodeGen->setFreestanding(EnableFreestanding);
+
+ if (OptLevel.getNumOccurrences()) {
+ if (OptLevel < '0' || OptLevel > '3')
+ report_fatal_error("Optimization level must be between 0 and 3");
+ CodeGen->setOptLevel(OptLevel - '0');
+ switch (OptLevel) {
+ case '0':
+ CodeGen->setCodeGenOptLevel(CodeGenOpt::None);
+ break;
+ case '1':
+ CodeGen->setCodeGenOptLevel(CodeGenOpt::Less);
+ break;
+ case '2':
+ CodeGen->setCodeGenOptLevel(CodeGenOpt::Default);
+ break;
+ case '3':
+ CodeGen->setCodeGenOptLevel(CodeGenOpt::Aggressive);
+ break;
+ }
+ }
+ return wrap(CodeGen);
+}
+
+void thinlto_codegen_dispose(thinlto_code_gen_t cg) { delete unwrap(cg); }
+
+void thinlto_codegen_add_module(thinlto_code_gen_t cg, const char *Identifier,
+ const char *Data, int Length) {
+ unwrap(cg)->addModule(Identifier, StringRef(Data, Length));
+}
+
+void thinlto_codegen_process(thinlto_code_gen_t cg) { unwrap(cg)->run(); }
+
+unsigned int thinlto_module_get_num_objects(thinlto_code_gen_t cg) {
+ return unwrap(cg)->getProducedBinaries().size();
+}
+LTOObjectBuffer thinlto_module_get_object(thinlto_code_gen_t cg,
+ unsigned int index) {
+ assert(index < unwrap(cg)->getProducedBinaries().size() && "Index overflow");
+ auto &MemBuffer = unwrap(cg)->getProducedBinaries()[index];
+ return LTOObjectBuffer{MemBuffer->getBufferStart(),
+ MemBuffer->getBufferSize()};
+}
+
+unsigned int thinlto_module_get_num_object_files(thinlto_code_gen_t cg) {
+ return unwrap(cg)->getProducedBinaryFiles().size();
+}
+const char *thinlto_module_get_object_file(thinlto_code_gen_t cg,
+ unsigned int index) {
+ assert(index < unwrap(cg)->getProducedBinaryFiles().size() &&
+ "Index overflow");
+ return unwrap(cg)->getProducedBinaryFiles()[index].c_str();
+}
+
+void thinlto_codegen_disable_codegen(thinlto_code_gen_t cg,
+ lto_bool_t disable) {
+ unwrap(cg)->disableCodeGen(disable);
+}
+
+void thinlto_codegen_set_codegen_only(thinlto_code_gen_t cg,
+ lto_bool_t CodeGenOnly) {
+ unwrap(cg)->setCodeGenOnly(CodeGenOnly);
+}
+
+void thinlto_debug_options(const char *const *options, int number) {
+ // if options were requested, set them
+ if (number && options) {
+ std::vector<const char *> CodegenArgv(1, "libLTO");
+ append_range(CodegenArgv, ArrayRef<const char *>(options, number));
+ cl::ParseCommandLineOptions(CodegenArgv.size(), CodegenArgv.data());
+ }
+}
+
+lto_bool_t lto_module_is_thinlto(lto_module_t mod) {
+ return unwrap(mod)->isThinLTO();
+}
+
+void thinlto_codegen_add_must_preserve_symbol(thinlto_code_gen_t cg,
+ const char *Name, int Length) {
+ unwrap(cg)->preserveSymbol(StringRef(Name, Length));
+}
+
+void thinlto_codegen_add_cross_referenced_symbol(thinlto_code_gen_t cg,
+ const char *Name, int Length) {
+ unwrap(cg)->crossReferenceSymbol(StringRef(Name, Length));
+}
+
+void thinlto_codegen_set_cpu(thinlto_code_gen_t cg, const char *cpu) {
+ return unwrap(cg)->setCpu(cpu);
+}
+
+void thinlto_codegen_set_cache_dir(thinlto_code_gen_t cg,
+ const char *cache_dir) {
+ return unwrap(cg)->setCacheDir(cache_dir);
+}
+
+void thinlto_codegen_set_cache_pruning_interval(thinlto_code_gen_t cg,
+ int interval) {
+ return unwrap(cg)->setCachePruningInterval(interval);
+}
+
+void thinlto_codegen_set_cache_entry_expiration(thinlto_code_gen_t cg,
+ unsigned expiration) {
+ return unwrap(cg)->setCacheEntryExpiration(expiration);
+}
+
+void thinlto_codegen_set_final_cache_size_relative_to_available_space(
+ thinlto_code_gen_t cg, unsigned Percentage) {
+ return unwrap(cg)->setMaxCacheSizeRelativeToAvailableSpace(Percentage);
+}
+
+void thinlto_codegen_set_cache_size_bytes(
+ thinlto_code_gen_t cg, unsigned MaxSizeBytes) {
+ 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);
+}
+
+void thinlto_codegen_set_savetemps_dir(thinlto_code_gen_t cg,
+ const char *save_temps_dir) {
+ return unwrap(cg)->setSaveTempsDir(save_temps_dir);
+}
+
+void thinlto_set_generated_objects_dir(thinlto_code_gen_t cg,
+ const char *save_temps_dir) {
+ unwrap(cg)->setGeneratedObjectsDirectory(save_temps_dir);
+}
+
+lto_bool_t thinlto_codegen_set_pic_model(thinlto_code_gen_t cg,
+ lto_codegen_model model) {
+ switch (model) {
+ case LTO_CODEGEN_PIC_MODEL_STATIC:
+ unwrap(cg)->setCodePICModel(Reloc::Static);
+ return false;
+ case LTO_CODEGEN_PIC_MODEL_DYNAMIC:
+ unwrap(cg)->setCodePICModel(Reloc::PIC_);
+ return false;
+ case LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC:
+ unwrap(cg)->setCodePICModel(Reloc::DynamicNoPIC);
+ return false;
+ case LTO_CODEGEN_PIC_MODEL_DEFAULT:
+ unwrap(cg)->setCodePICModel(None);
+ return false;
+ }
+ sLastErrorString = "Unknown PIC model";
+ return true;
+}
+
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(lto::InputFile, lto_input_t)
+
+lto_input_t lto_input_create(const void *buffer, size_t buffer_size, const char *path) {
+ return wrap(LTOModule::createInputFile(buffer, buffer_size, path, sLastErrorString));
+}
+
+void lto_input_dispose(lto_input_t input) {
+ delete unwrap(input);
+}
+
+extern unsigned lto_input_get_num_dependent_libraries(lto_input_t input) {
+ return LTOModule::getDependentLibraryCount(unwrap(input));
+}
+
+extern const char *lto_input_get_dependent_library(lto_input_t input,
+ size_t index,
+ size_t *size) {
+ return LTOModule::getDependentLibrary(unwrap(input), index, size);
+}
+
+extern const char *const *lto_runtime_lib_symbols_list(size_t *size) {
+ auto symbols = lto::LTO::getRuntimeLibcallSymbols();
+ *size = symbols.size();
+ return symbols.data();
+}
diff --git a/contrib/libs/llvm14/tools/lto/ya.make b/contrib/libs/llvm14/tools/lto/ya.make
new file mode 100644
index 00000000000..96f9dc6f87f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/lto/ya.make
@@ -0,0 +1,63 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/LTO
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/lto
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ LTODisassembler.cpp
+ lto.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/obj2yaml/archive2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/archive2yaml.cpp
new file mode 100644
index 00000000000..c7b0ee48029
--- /dev/null
+++ b/contrib/libs/llvm14/tools/obj2yaml/archive2yaml.cpp
@@ -0,0 +1,114 @@
+//===------ utils/archive2yaml.cpp - obj2yaml conversion tool ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "obj2yaml.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/ObjectYAML/ArchiveYAML.h"
+
+using namespace llvm;
+
+namespace {
+
+class ArchiveDumper {
+public:
+ Expected<ArchYAML::Archive *> dump(MemoryBufferRef Source) {
+ StringRef Buffer = Source.getBuffer();
+ assert(file_magic::archive == identify_magic(Buffer));
+
+ std::unique_ptr<ArchYAML::Archive> Obj =
+ std::make_unique<ArchYAML::Archive>();
+
+ StringRef Magic = "!<arch>\n";
+ if (!Buffer.startswith(Magic))
+ return createStringError(std::errc::not_supported,
+ "only regular archives are supported");
+ Obj->Magic = Magic;
+ Buffer = Buffer.drop_front(Magic.size());
+
+ Obj->Members.emplace();
+ while (!Buffer.empty()) {
+ uint64_t Offset = Buffer.data() - Source.getBuffer().data();
+ if (Buffer.size() < sizeof(ArchiveHeader))
+ return createStringError(
+ std::errc::illegal_byte_sequence,
+ "unable to read the header of a child at offset 0x%" PRIx64,
+ Offset);
+
+ const ArchiveHeader &Hdr =
+ *reinterpret_cast<const ArchiveHeader *>(Buffer.data());
+ Buffer = Buffer.drop_front(sizeof(ArchiveHeader));
+
+ auto ToString = [](ArrayRef<char> V) {
+ // We don't want to dump excessive spaces.
+ return StringRef(V.data(), V.size()).rtrim(' ');
+ };
+
+ ArchYAML::Archive::Child C;
+ C.Fields["Name"].Value = ToString(Hdr.Name);
+ C.Fields["LastModified"].Value = ToString(Hdr.LastModified);
+ C.Fields["UID"].Value = ToString(Hdr.UID);
+ C.Fields["GID"].Value = ToString(Hdr.GID);
+ C.Fields["AccessMode"].Value = ToString(Hdr.AccessMode);
+ StringRef SizeStr = ToString(Hdr.Size);
+ C.Fields["Size"].Value = SizeStr;
+ C.Fields["Terminator"].Value = ToString(Hdr.Terminator);
+
+ uint64_t Size;
+ if (SizeStr.getAsInteger(10, Size))
+ return createStringError(
+ std::errc::illegal_byte_sequence,
+ "unable to read the size of a child at offset 0x%" PRIx64
+ " as integer: \"%s\"",
+ Offset, SizeStr.str().c_str());
+ if (Buffer.size() < Size)
+ return createStringError(
+ std::errc::illegal_byte_sequence,
+ "unable to read the data of a child at offset 0x%" PRIx64
+ " of size %" PRId64 ": the remaining archive size is %zu",
+ Offset, Size, Buffer.size());
+ if (!Buffer.empty())
+ C.Content = arrayRefFromStringRef(Buffer.take_front(Size));
+
+ const bool HasPaddingByte = (Size & 1) && Buffer.size() > Size;
+ if (HasPaddingByte)
+ C.PaddingByte = Buffer[Size];
+
+ Obj->Members->push_back(C);
+ // If the size is odd, consume a padding byte.
+ Buffer = Buffer.drop_front(HasPaddingByte ? Size + 1 : Size);
+ }
+
+ return Obj.release();
+ }
+
+private:
+ struct ArchiveHeader {
+ char Name[16];
+ char LastModified[12];
+ char UID[6];
+ char GID[6];
+ char AccessMode[8];
+ char Size[10];
+ char Terminator[2];
+ };
+};
+
+} // namespace
+
+Error archive2yaml(raw_ostream &Out, MemoryBufferRef Source) {
+ ArchiveDumper Dumper;
+ Expected<ArchYAML::Archive *> YAMLOrErr = Dumper.dump(Source);
+ if (!YAMLOrErr)
+ return YAMLOrErr.takeError();
+
+ std::unique_ptr<ArchYAML::Archive> YAML(YAMLOrErr.get());
+ yaml::Output Yout(Out);
+ Yout << *YAML;
+
+ return Error::success();
+}
diff --git a/contrib/libs/llvm14/tools/obj2yaml/coff2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/coff2yaml.cpp
new file mode 100644
index 00000000000..604799fb273
--- /dev/null
+++ b/contrib/libs/llvm14/tools/obj2yaml/coff2yaml.cpp
@@ -0,0 +1,366 @@
+//===------ utils/obj2yaml.cpp - obj2yaml conversion tool -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#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"
+#include "llvm/Object/COFF.h"
+#include "llvm/ObjectYAML/COFFYAML.h"
+#include "llvm/ObjectYAML/CodeViewYAMLTypes.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace llvm;
+
+namespace {
+
+class COFFDumper {
+ const object::COFFObjectFile &Obj;
+ COFFYAML::Object YAMLObj;
+ template <typename T>
+ void dumpOptionalHeader(T OptionalHeader);
+ void dumpHeader();
+ void dumpSections(unsigned numSections);
+ void dumpSymbols(unsigned numSymbols);
+
+public:
+ COFFDumper(const object::COFFObjectFile &Obj);
+ COFFYAML::Object &getYAMLObj();
+};
+
+}
+
+COFFDumper::COFFDumper(const object::COFFObjectFile &Obj) : Obj(Obj) {
+ if (const object::pe32_header *PE32Header = Obj.getPE32Header())
+ dumpOptionalHeader(PE32Header);
+ else if (const object::pe32plus_header *PE32PlusHeader =
+ Obj.getPE32PlusHeader())
+ dumpOptionalHeader(PE32PlusHeader);
+
+ dumpHeader();
+ dumpSections(Obj.getNumberOfSections());
+ dumpSymbols(Obj.getNumberOfSymbols());
+}
+
+template <typename T> void COFFDumper::dumpOptionalHeader(T OptionalHeader) {
+ YAMLObj.OptionalHeader = COFFYAML::PEHeader();
+ YAMLObj.OptionalHeader->Header.AddressOfEntryPoint =
+ OptionalHeader->AddressOfEntryPoint;
+ YAMLObj.OptionalHeader->Header.ImageBase = OptionalHeader->ImageBase;
+ YAMLObj.OptionalHeader->Header.SectionAlignment =
+ OptionalHeader->SectionAlignment;
+ YAMLObj.OptionalHeader->Header.FileAlignment = OptionalHeader->FileAlignment;
+ YAMLObj.OptionalHeader->Header.MajorOperatingSystemVersion =
+ OptionalHeader->MajorOperatingSystemVersion;
+ YAMLObj.OptionalHeader->Header.MinorOperatingSystemVersion =
+ OptionalHeader->MinorOperatingSystemVersion;
+ YAMLObj.OptionalHeader->Header.MajorImageVersion =
+ OptionalHeader->MajorImageVersion;
+ YAMLObj.OptionalHeader->Header.MinorImageVersion =
+ OptionalHeader->MinorImageVersion;
+ YAMLObj.OptionalHeader->Header.MajorSubsystemVersion =
+ OptionalHeader->MajorSubsystemVersion;
+ YAMLObj.OptionalHeader->Header.MinorSubsystemVersion =
+ OptionalHeader->MinorSubsystemVersion;
+ YAMLObj.OptionalHeader->Header.Subsystem = OptionalHeader->Subsystem;
+ YAMLObj.OptionalHeader->Header.DLLCharacteristics =
+ OptionalHeader->DLLCharacteristics;
+ YAMLObj.OptionalHeader->Header.SizeOfStackReserve =
+ OptionalHeader->SizeOfStackReserve;
+ YAMLObj.OptionalHeader->Header.SizeOfStackCommit =
+ OptionalHeader->SizeOfStackCommit;
+ YAMLObj.OptionalHeader->Header.SizeOfHeapReserve =
+ OptionalHeader->SizeOfHeapReserve;
+ YAMLObj.OptionalHeader->Header.SizeOfHeapCommit =
+ OptionalHeader->SizeOfHeapCommit;
+ YAMLObj.OptionalHeader->Header.NumberOfRvaAndSize =
+ OptionalHeader->NumberOfRvaAndSize;
+ unsigned I = 0;
+ for (auto &DestDD : YAMLObj.OptionalHeader->DataDirectories) {
+ const object::data_directory *DD = Obj.getDataDirectory(I++);
+ if (!DD)
+ continue;
+ DestDD = COFF::DataDirectory();
+ DestDD->RelativeVirtualAddress = DD->RelativeVirtualAddress;
+ DestDD->Size = DD->Size;
+ }
+}
+
+void COFFDumper::dumpHeader() {
+ YAMLObj.Header.Machine = Obj.getMachine();
+ YAMLObj.Header.Characteristics = Obj.getCharacteristics();
+}
+
+static void
+initializeFileAndStringTable(const llvm::object::COFFObjectFile &Obj,
+ codeview::StringsAndChecksumsRef &SC) {
+
+ ExitOnError Err("invalid .debug$S section");
+ // Iterate all .debug$S sections looking for the checksums and string table.
+ // Exit as soon as both sections are found.
+ for (const auto &S : Obj.sections()) {
+ if (SC.hasStrings() && SC.hasChecksums())
+ break;
+
+ Expected<StringRef> SectionNameOrErr = S.getName();
+ if (!SectionNameOrErr) {
+ consumeError(SectionNameOrErr.takeError());
+ continue;
+ }
+
+ ArrayRef<uint8_t> sectionData;
+ if ((*SectionNameOrErr) != ".debug$S")
+ continue;
+
+ const object::coff_section *COFFSection = Obj.getCOFFSection(S);
+
+ cantFail(Obj.getSectionContents(COFFSection, sectionData));
+
+ BinaryStreamReader Reader(sectionData, support::little);
+ uint32_t Magic;
+
+ Err(Reader.readInteger(Magic));
+ assert(Magic == COFF::DEBUG_SECTION_MAGIC && "Invalid .debug$S section!");
+
+ codeview::DebugSubsectionArray Subsections;
+ Err(Reader.readArray(Subsections, Reader.bytesRemaining()));
+
+ SC.initialize(Subsections);
+ }
+}
+
+void COFFDumper::dumpSections(unsigned NumSections) {
+ std::vector<COFFYAML::Section> &YAMLSections = YAMLObj.Sections;
+ codeview::StringsAndChecksumsRef SC;
+ initializeFileAndStringTable(Obj, SC);
+
+ ExitOnError Err("invalid section table");
+ StringMap<bool> SymbolUnique;
+ for (const auto &S : Obj.symbols()) {
+ StringRef Name = Err(Obj.getSymbolName(Obj.getCOFFSymbol(S)));
+ 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;
+
+ if (Expected<StringRef> NameOrErr = ObjSection.getName())
+ NewYAMLSection.Name = *NameOrErr;
+ else
+ consumeError(NameOrErr.takeError());
+
+ NewYAMLSection.Header.Characteristics = COFFSection->Characteristics;
+ NewYAMLSection.Header.VirtualAddress = COFFSection->VirtualAddress;
+ NewYAMLSection.Header.VirtualSize = COFFSection->VirtualSize;
+ NewYAMLSection.Header.NumberOfLineNumbers =
+ COFFSection->NumberOfLinenumbers;
+ NewYAMLSection.Header.NumberOfRelocations =
+ COFFSection->NumberOfRelocations;
+ NewYAMLSection.Header.PointerToLineNumbers =
+ COFFSection->PointerToLinenumbers;
+ NewYAMLSection.Header.PointerToRawData = COFFSection->PointerToRawData;
+ NewYAMLSection.Header.PointerToRelocations =
+ COFFSection->PointerToRelocations;
+ NewYAMLSection.Header.SizeOfRawData = COFFSection->SizeOfRawData;
+ uint32_t Shift = (COFFSection->Characteristics >> 20) & 0xF;
+ NewYAMLSection.Alignment = (1U << Shift) >> 1;
+ assert(NewYAMLSection.Alignment <= 8192);
+
+ ArrayRef<uint8_t> sectionData;
+ if (!ObjSection.isBSS())
+ cantFail(Obj.getSectionContents(COFFSection, sectionData));
+ NewYAMLSection.SectionData = yaml::BinaryRef(sectionData);
+
+ if (NewYAMLSection.Name == ".debug$S")
+ NewYAMLSection.DebugS = CodeViewYAML::fromDebugS(sectionData, SC);
+ else if (NewYAMLSection.Name == ".debug$T")
+ NewYAMLSection.DebugT = CodeViewYAML::fromDebugT(sectionData,
+ NewYAMLSection.Name);
+ else if (NewYAMLSection.Name == ".debug$P")
+ NewYAMLSection.DebugP = CodeViewYAML::fromDebugT(sectionData,
+ NewYAMLSection.Name);
+ else if (NewYAMLSection.Name == ".debug$H")
+ NewYAMLSection.DebugH = CodeViewYAML::fromDebugH(sectionData);
+
+ std::vector<COFFYAML::Relocation> Relocations;
+ for (const auto &Reloc : ObjSection.relocations()) {
+ const object::coff_relocation *reloc = Obj.getCOFFRelocation(Reloc);
+ COFFYAML::Relocation Rel;
+ object::symbol_iterator Sym = Reloc.getSymbol();
+ Expected<StringRef> SymbolNameOrErr = Sym->getName();
+ if (!SymbolNameOrErr) {
+ std::string Buf;
+ raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(SymbolNameOrErr.takeError(), OS);
+ report_fatal_error(Twine(OS.str()));
+ }
+ if (SymbolUnique.lookup(*SymbolNameOrErr))
+ Rel.SymbolName = *SymbolNameOrErr;
+ else
+ Rel.SymbolTableIndex = reloc->SymbolTableIndex;
+ Rel.VirtualAddress = reloc->VirtualAddress;
+ Rel.Type = reloc->Type;
+ Relocations.push_back(Rel);
+ }
+ NewYAMLSection.Relocations = Relocations;
+ YAMLSections.push_back(NewYAMLSection);
+ }
+}
+
+static void
+dumpFunctionDefinition(COFFYAML::Symbol *Sym,
+ const object::coff_aux_function_definition *ObjFD) {
+ COFF::AuxiliaryFunctionDefinition YAMLFD;
+ YAMLFD.TagIndex = ObjFD->TagIndex;
+ YAMLFD.TotalSize = ObjFD->TotalSize;
+ YAMLFD.PointerToLinenumber = ObjFD->PointerToLinenumber;
+ YAMLFD.PointerToNextFunction = ObjFD->PointerToNextFunction;
+
+ Sym->FunctionDefinition = YAMLFD;
+}
+
+static void
+dumpbfAndEfLineInfo(COFFYAML::Symbol *Sym,
+ const object::coff_aux_bf_and_ef_symbol *ObjBES) {
+ COFF::AuxiliarybfAndefSymbol YAMLAAS;
+ YAMLAAS.Linenumber = ObjBES->Linenumber;
+ YAMLAAS.PointerToNextFunction = ObjBES->PointerToNextFunction;
+
+ Sym->bfAndefSymbol = YAMLAAS;
+}
+
+static void dumpWeakExternal(COFFYAML::Symbol *Sym,
+ const object::coff_aux_weak_external *ObjWE) {
+ COFF::AuxiliaryWeakExternal YAMLWE;
+ YAMLWE.TagIndex = ObjWE->TagIndex;
+ YAMLWE.Characteristics = ObjWE->Characteristics;
+
+ Sym->WeakExternal = YAMLWE;
+}
+
+static void
+dumpSectionDefinition(COFFYAML::Symbol *Sym,
+ const object::coff_aux_section_definition *ObjSD,
+ bool IsBigObj) {
+ COFF::AuxiliarySectionDefinition YAMLASD;
+ int32_t AuxNumber = ObjSD->getNumber(IsBigObj);
+ YAMLASD.Length = ObjSD->Length;
+ YAMLASD.NumberOfRelocations = ObjSD->NumberOfRelocations;
+ YAMLASD.NumberOfLinenumbers = ObjSD->NumberOfLinenumbers;
+ YAMLASD.CheckSum = ObjSD->CheckSum;
+ YAMLASD.Number = AuxNumber;
+ YAMLASD.Selection = ObjSD->Selection;
+
+ Sym->SectionDefinition = YAMLASD;
+}
+
+static void
+dumpCLRTokenDefinition(COFFYAML::Symbol *Sym,
+ const object::coff_aux_clr_token *ObjCLRToken) {
+ COFF::AuxiliaryCLRToken YAMLCLRToken;
+ YAMLCLRToken.AuxType = ObjCLRToken->AuxType;
+ YAMLCLRToken.SymbolTableIndex = ObjCLRToken->SymbolTableIndex;
+
+ Sym->CLRToken = YAMLCLRToken;
+}
+
+void COFFDumper::dumpSymbols(unsigned NumSymbols) {
+ ExitOnError Err("invalid symbol table");
+
+ std::vector<COFFYAML::Symbol> &Symbols = YAMLObj.Symbols;
+ for (const auto &S : Obj.symbols()) {
+ object::COFFSymbolRef Symbol = Obj.getCOFFSymbol(S);
+ COFFYAML::Symbol Sym;
+ Sym.Name = Err(Obj.getSymbolName(Symbol));
+ Sym.SimpleType = COFF::SymbolBaseType(Symbol.getBaseType());
+ Sym.ComplexType = COFF::SymbolComplexType(Symbol.getComplexType());
+ Sym.Header.StorageClass = Symbol.getStorageClass();
+ Sym.Header.Value = Symbol.getValue();
+ Sym.Header.SectionNumber = Symbol.getSectionNumber();
+ Sym.Header.NumberOfAuxSymbols = Symbol.getNumberOfAuxSymbols();
+
+ if (Symbol.getNumberOfAuxSymbols() > 0) {
+ ArrayRef<uint8_t> AuxData = Obj.getSymbolAuxData(Symbol);
+ if (Symbol.isFunctionDefinition()) {
+ // This symbol represents a function definition.
+ assert(Symbol.getNumberOfAuxSymbols() == 1 &&
+ "Expected a single aux symbol to describe this function!");
+
+ const object::coff_aux_function_definition *ObjFD =
+ reinterpret_cast<const object::coff_aux_function_definition *>(
+ AuxData.data());
+ dumpFunctionDefinition(&Sym, ObjFD);
+ } else if (Symbol.isFunctionLineInfo()) {
+ // This symbol describes function line number information.
+ assert(Symbol.getNumberOfAuxSymbols() == 1 &&
+ "Expected a single aux symbol to describe this function!");
+
+ const object::coff_aux_bf_and_ef_symbol *ObjBES =
+ reinterpret_cast<const object::coff_aux_bf_and_ef_symbol *>(
+ AuxData.data());
+ dumpbfAndEfLineInfo(&Sym, ObjBES);
+ } else if (Symbol.isAnyUndefined()) {
+ // This symbol represents a weak external definition.
+ assert(Symbol.getNumberOfAuxSymbols() == 1 &&
+ "Expected a single aux symbol to describe this weak symbol!");
+
+ const object::coff_aux_weak_external *ObjWE =
+ reinterpret_cast<const object::coff_aux_weak_external *>(
+ AuxData.data());
+ dumpWeakExternal(&Sym, ObjWE);
+ } else if (Symbol.isFileRecord()) {
+ // This symbol represents a file record.
+ Sym.File = StringRef(reinterpret_cast<const char *>(AuxData.data()),
+ Symbol.getNumberOfAuxSymbols() *
+ Obj.getSymbolTableEntrySize())
+ .rtrim(StringRef("\0", /*length=*/1));
+ } else if (Symbol.isSectionDefinition()) {
+ // This symbol represents a section definition.
+ assert(Symbol.getNumberOfAuxSymbols() == 1 &&
+ "Expected a single aux symbol to describe this section!");
+
+ const object::coff_aux_section_definition *ObjSD =
+ reinterpret_cast<const object::coff_aux_section_definition *>(
+ AuxData.data());
+ dumpSectionDefinition(&Sym, ObjSD, Symbol.isBigObj());
+ } else if (Symbol.isCLRToken()) {
+ // This symbol represents a CLR token definition.
+ assert(Symbol.getNumberOfAuxSymbols() == 1 &&
+ "Expected a single aux symbol to describe this CLR Token!");
+
+ const object::coff_aux_clr_token *ObjCLRToken =
+ reinterpret_cast<const object::coff_aux_clr_token *>(
+ AuxData.data());
+ dumpCLRTokenDefinition(&Sym, ObjCLRToken);
+ } else {
+ llvm_unreachable("Unhandled auxiliary symbol!");
+ }
+ }
+ Symbols.push_back(Sym);
+ }
+}
+
+COFFYAML::Object &COFFDumper::getYAMLObj() {
+ return YAMLObj;
+}
+
+std::error_code coff2yaml(raw_ostream &Out, const object::COFFObjectFile &Obj) {
+ COFFDumper Dumper(Obj);
+
+ yaml::Output Yout(Out);
+ Yout << Dumper.getYAMLObj();
+
+ return std::error_code();
+}
diff --git a/contrib/libs/llvm14/tools/obj2yaml/dwarf2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/dwarf2yaml.cpp
new file mode 100644
index 00000000000..8cae1f3c490
--- /dev/null
+++ b/contrib/libs/llvm14/tools/obj2yaml/dwarf2yaml.cpp
@@ -0,0 +1,463 @@
+//===------ dwarf2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h"
+#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
+#include "llvm/DebugInfo/DWARF/DWARFSection.h"
+#include "llvm/ObjectYAML/DWARFYAML.h"
+
+#include <algorithm>
+
+using namespace llvm;
+
+void dumpDebugAbbrev(DWARFContext &DCtx, DWARFYAML::Data &Y) {
+ auto AbbrevSetPtr = DCtx.getDebugAbbrev();
+ if (AbbrevSetPtr) {
+ uint64_t AbbrevTableID = 0;
+ for (auto AbbrvDeclSet : *AbbrevSetPtr) {
+ Y.DebugAbbrev.emplace_back();
+ Y.DebugAbbrev.back().ID = AbbrevTableID++;
+ for (auto AbbrvDecl : AbbrvDeclSet.second) {
+ DWARFYAML::Abbrev Abbrv;
+ Abbrv.Code = AbbrvDecl.getCode();
+ Abbrv.Tag = AbbrvDecl.getTag();
+ Abbrv.Children = AbbrvDecl.hasChildren() ? dwarf::DW_CHILDREN_yes
+ : dwarf::DW_CHILDREN_no;
+ for (auto Attribute : AbbrvDecl.attributes()) {
+ DWARFYAML::AttributeAbbrev AttAbrv;
+ AttAbrv.Attribute = Attribute.Attr;
+ AttAbrv.Form = Attribute.Form;
+ if (AttAbrv.Form == dwarf::DW_FORM_implicit_const)
+ AttAbrv.Value = Attribute.getImplicitConstValue();
+ Abbrv.Attributes.push_back(AttAbrv);
+ }
+ Y.DebugAbbrev.back().Table.push_back(Abbrv);
+ }
+ }
+ }
+}
+
+Error dumpDebugAddr(DWARFContext &DCtx, DWARFYAML::Data &Y) {
+ DWARFDebugAddrTable AddrTable;
+ DWARFDataExtractor AddrData(DCtx.getDWARFObj(),
+ DCtx.getDWARFObj().getAddrSection(),
+ DCtx.isLittleEndian(), /*AddressSize=*/0);
+ std::vector<DWARFYAML::AddrTableEntry> AddrTables;
+ uint64_t Offset = 0;
+ while (AddrData.isValidOffset(Offset)) {
+ // We ignore any errors that don't prevent parsing the section, since we can
+ // still represent such sections.
+ if (Error Err = AddrTable.extractV5(AddrData, &Offset, /*CUAddrSize=*/0,
+ consumeError))
+ return Err;
+ AddrTables.emplace_back();
+ for (uint64_t Addr : AddrTable.getAddressEntries()) {
+ // Currently, the parser doesn't support parsing an address table with non
+ // linear addresses (segment_selector_size != 0). The segment selectors
+ // are specified to be zero.
+ AddrTables.back().SegAddrPairs.push_back(
+ {/*SegmentSelector=*/0, /*Address=*/Addr});
+ }
+
+ AddrTables.back().Format = AddrTable.getFormat();
+ AddrTables.back().Length = AddrTable.getLength();
+ AddrTables.back().Version = AddrTable.getVersion();
+ AddrTables.back().AddrSize = AddrTable.getAddressSize();
+ AddrTables.back().SegSelectorSize = AddrTable.getSegmentSelectorSize();
+ }
+ Y.DebugAddr = std::move(AddrTables);
+ return Error::success();
+}
+
+Error dumpDebugStrings(DWARFContext &DCtx, DWARFYAML::Data &Y) {
+ DataExtractor StrData = DCtx.getStringExtractor();
+ uint64_t Offset = 0;
+ std::vector<StringRef> DebugStr;
+ Error Err = Error::success();
+ while (StrData.isValidOffset(Offset)) {
+ const char *CStr = StrData.getCStr(&Offset, &Err);
+ if (Err)
+ return Err;
+ DebugStr.push_back(CStr);
+ }
+
+ Y.DebugStrings = DebugStr;
+ return Err;
+}
+
+Error dumpDebugARanges(DWARFContext &DCtx, DWARFYAML::Data &Y) {
+ DWARFDataExtractor ArangesData(DCtx.getDWARFObj().getArangesSection(),
+ DCtx.isLittleEndian(), 0);
+ uint64_t Offset = 0;
+ DWARFDebugArangeSet Set;
+ std::vector<DWARFYAML::ARange> DebugAranges;
+
+ // We ignore any errors that don't prevent parsing the section, since we can
+ // still represent such sections. These errors are recorded via the
+ // WarningHandler parameter of Set.extract().
+ auto DiscardError = [](Error Err) { consumeError(std::move(Err)); };
+
+ while (ArangesData.isValidOffset(Offset)) {
+ if (Error E = Set.extract(ArangesData, &Offset, DiscardError))
+ return E;
+ DWARFYAML::ARange Range;
+ Range.Format = Set.getHeader().Format;
+ Range.Length = Set.getHeader().Length;
+ Range.Version = Set.getHeader().Version;
+ Range.CuOffset = Set.getHeader().CuOffset;
+ Range.AddrSize = Set.getHeader().AddrSize;
+ Range.SegSize = Set.getHeader().SegSize;
+ for (auto Descriptor : Set.descriptors()) {
+ DWARFYAML::ARangeDescriptor Desc;
+ Desc.Address = Descriptor.Address;
+ Desc.Length = Descriptor.Length;
+ Range.Descriptors.push_back(Desc);
+ }
+ DebugAranges.push_back(Range);
+ }
+
+ Y.DebugAranges = DebugAranges;
+ return ErrorSuccess();
+}
+
+Error dumpDebugRanges(DWARFContext &DCtx, DWARFYAML::Data &Y) {
+ // We are assuming all address byte sizes will be consistent across all
+ // compile units.
+ uint8_t AddrSize = 0;
+ for (const auto &CU : DCtx.compile_units()) {
+ const uint8_t CUAddrSize = CU->getAddressByteSize();
+ if (AddrSize == 0)
+ AddrSize = CUAddrSize;
+ else if (CUAddrSize != AddrSize)
+ return createStringError(std::errc::invalid_argument,
+ "address sizes vary in different compile units");
+ }
+
+ DWARFDataExtractor Data(DCtx.getDWARFObj().getRangesSection().Data,
+ DCtx.isLittleEndian(), AddrSize);
+ uint64_t Offset = 0;
+ DWARFDebugRangeList DwarfRanges;
+ std::vector<DWARFYAML::Ranges> DebugRanges;
+
+ while (Data.isValidOffset(Offset)) {
+ DWARFYAML::Ranges YamlRanges;
+ YamlRanges.Offset = Offset;
+ YamlRanges.AddrSize = AddrSize;
+ if (Error E = DwarfRanges.extract(Data, &Offset))
+ return E;
+ for (const auto &RLE : DwarfRanges.getEntries())
+ YamlRanges.Entries.push_back({RLE.StartAddress, RLE.EndAddress});
+ DebugRanges.push_back(std::move(YamlRanges));
+ }
+
+ Y.DebugRanges = DebugRanges;
+ return ErrorSuccess();
+}
+
+static Optional<DWARFYAML::PubSection>
+dumpPubSection(const DWARFContext &DCtx, const DWARFSection &Section,
+ bool IsGNUStyle) {
+ DWARFYAML::PubSection Y;
+ DWARFDataExtractor PubSectionData(DCtx.getDWARFObj(), Section,
+ DCtx.isLittleEndian(), 0);
+ DWARFDebugPubTable Table;
+ // We ignore any errors that don't prevent parsing the section, since we can
+ // still represent such sections.
+ Table.extract(PubSectionData, IsGNUStyle,
+ [](Error Err) { consumeError(std::move(Err)); });
+ ArrayRef<DWARFDebugPubTable::Set> Sets = Table.getData();
+ if (Sets.empty())
+ return None;
+
+ // FIXME: Currently, obj2yaml only supports dumping the first pubtable.
+ Y.Format = Sets[0].Format;
+ Y.Length = Sets[0].Length;
+ Y.Version = Sets[0].Version;
+ Y.UnitOffset = Sets[0].Offset;
+ Y.UnitSize = Sets[0].Size;
+
+ for (const DWARFDebugPubTable::Entry &E : Sets[0].Entries)
+ Y.Entries.push_back(DWARFYAML::PubEntry{(uint32_t)E.SecOffset,
+ E.Descriptor.toBits(), E.Name});
+
+ return Y;
+}
+
+void dumpDebugPubSections(DWARFContext &DCtx, DWARFYAML::Data &Y) {
+ const DWARFObject &D = DCtx.getDWARFObj();
+
+ Y.PubNames =
+ dumpPubSection(DCtx, D.getPubnamesSection(), /*IsGNUStyle=*/false);
+ Y.PubTypes =
+ dumpPubSection(DCtx, D.getPubtypesSection(), /*IsGNUStyle=*/false);
+ // TODO: Test dumping .debug_gnu_pubnames section.
+ Y.GNUPubNames =
+ dumpPubSection(DCtx, D.getGnuPubnamesSection(), /*IsGNUStyle=*/true);
+ // TODO: Test dumping .debug_gnu_pubtypes section.
+ Y.GNUPubTypes =
+ dumpPubSection(DCtx, D.getGnuPubtypesSection(), /*IsGNUStyle=*/true);
+}
+
+void dumpDebugInfo(DWARFContext &DCtx, DWARFYAML::Data &Y) {
+ for (const auto &CU : DCtx.compile_units()) {
+ DWARFYAML::Unit NewUnit;
+ NewUnit.Format = CU->getFormat();
+ NewUnit.Length = CU->getLength();
+ NewUnit.Version = CU->getVersion();
+ if (NewUnit.Version >= 5)
+ NewUnit.Type = (dwarf::UnitType)CU->getUnitType();
+ const DWARFDebugAbbrev *DebugAbbrev = DCtx.getDebugAbbrev();
+ NewUnit.AbbrevTableID = std::distance(
+ DebugAbbrev->begin(),
+ llvm::find_if(
+ *DebugAbbrev,
+ [&](const std::pair<uint64_t, DWARFAbbreviationDeclarationSet> &P) {
+ return P.first == CU->getAbbreviations()->getOffset();
+ }));
+ NewUnit.AbbrOffset = CU->getAbbreviations()->getOffset();
+ NewUnit.AddrSize = CU->getAddressByteSize();
+ for (auto DIE : CU->dies()) {
+ DWARFYAML::Entry NewEntry;
+ DataExtractor EntryData = CU->getDebugInfoExtractor();
+ uint64_t offset = DIE.getOffset();
+
+ assert(EntryData.isValidOffset(offset) && "Invalid DIE Offset");
+ if (!EntryData.isValidOffset(offset))
+ continue;
+
+ NewEntry.AbbrCode = EntryData.getULEB128(&offset);
+
+ auto AbbrevDecl = DIE.getAbbreviationDeclarationPtr();
+ if (AbbrevDecl) {
+ for (const auto &AttrSpec : AbbrevDecl->attributes()) {
+ DWARFYAML::FormValue NewValue;
+ NewValue.Value = 0xDEADBEEFDEADBEEF;
+ DWARFDie DIEWrapper(CU.get(), &DIE);
+ auto FormValue = DIEWrapper.find(AttrSpec.Attr);
+ if (!FormValue)
+ return;
+ auto Form = FormValue.getValue().getForm();
+ bool indirect = false;
+ do {
+ indirect = false;
+ switch (Form) {
+ case dwarf::DW_FORM_addr:
+ case dwarf::DW_FORM_GNU_addr_index:
+ if (auto Val = FormValue.getValue().getAsAddress())
+ NewValue.Value = Val.getValue();
+ break;
+ case dwarf::DW_FORM_ref_addr:
+ case dwarf::DW_FORM_ref1:
+ case dwarf::DW_FORM_ref2:
+ case dwarf::DW_FORM_ref4:
+ case dwarf::DW_FORM_ref8:
+ case dwarf::DW_FORM_ref_udata:
+ case dwarf::DW_FORM_ref_sig8:
+ if (auto Val = FormValue.getValue().getAsReferenceUVal())
+ NewValue.Value = Val.getValue();
+ break;
+ case dwarf::DW_FORM_exprloc:
+ case dwarf::DW_FORM_block:
+ case dwarf::DW_FORM_block1:
+ case dwarf::DW_FORM_block2:
+ case dwarf::DW_FORM_block4:
+ if (auto Val = FormValue.getValue().getAsBlock()) {
+ auto BlockData = Val.getValue();
+ std::copy(BlockData.begin(), BlockData.end(),
+ std::back_inserter(NewValue.BlockData));
+ }
+ NewValue.Value = NewValue.BlockData.size();
+ break;
+ case dwarf::DW_FORM_data1:
+ case dwarf::DW_FORM_flag:
+ case dwarf::DW_FORM_data2:
+ case dwarf::DW_FORM_data4:
+ case dwarf::DW_FORM_data8:
+ case dwarf::DW_FORM_sdata:
+ case dwarf::DW_FORM_udata:
+ case dwarf::DW_FORM_ref_sup4:
+ case dwarf::DW_FORM_ref_sup8:
+ if (auto Val = FormValue.getValue().getAsUnsignedConstant())
+ NewValue.Value = Val.getValue();
+ break;
+ case dwarf::DW_FORM_string:
+ if (auto Val = dwarf::toString(FormValue))
+ NewValue.CStr = *Val;
+ break;
+ case dwarf::DW_FORM_indirect:
+ indirect = true;
+ if (auto Val = FormValue.getValue().getAsUnsignedConstant()) {
+ NewValue.Value = Val.getValue();
+ NewEntry.Values.push_back(NewValue);
+ Form = static_cast<dwarf::Form>(Val.getValue());
+ }
+ break;
+ case dwarf::DW_FORM_strp:
+ case dwarf::DW_FORM_sec_offset:
+ case dwarf::DW_FORM_GNU_ref_alt:
+ case dwarf::DW_FORM_GNU_strp_alt:
+ case dwarf::DW_FORM_line_strp:
+ case dwarf::DW_FORM_strp_sup:
+ case dwarf::DW_FORM_GNU_str_index:
+ case dwarf::DW_FORM_strx:
+ if (auto Val = FormValue.getValue().getAsCStringOffset())
+ NewValue.Value = Val.getValue();
+ break;
+ case dwarf::DW_FORM_flag_present:
+ NewValue.Value = 1;
+ break;
+ default:
+ break;
+ }
+ } while (indirect);
+ NewEntry.Values.push_back(NewValue);
+ }
+ }
+
+ NewUnit.Entries.push_back(NewEntry);
+ }
+ Y.CompileUnits.push_back(NewUnit);
+ }
+}
+
+bool dumpFileEntry(DataExtractor &Data, uint64_t &Offset,
+ DWARFYAML::File &File) {
+ File.Name = Data.getCStr(&Offset);
+ if (File.Name.empty())
+ return false;
+ File.DirIdx = Data.getULEB128(&Offset);
+ File.ModTime = Data.getULEB128(&Offset);
+ File.Length = Data.getULEB128(&Offset);
+ return true;
+}
+
+void dumpDebugLines(DWARFContext &DCtx, DWARFYAML::Data &Y) {
+ for (const auto &CU : DCtx.compile_units()) {
+ auto CUDIE = CU->getUnitDIE();
+ if (!CUDIE)
+ continue;
+ if (auto StmtOffset =
+ dwarf::toSectionOffset(CUDIE.find(dwarf::DW_AT_stmt_list))) {
+ DWARFYAML::LineTable DebugLines;
+ DataExtractor LineData(DCtx.getDWARFObj().getLineSection().Data,
+ DCtx.isLittleEndian(), CU->getAddressByteSize());
+ uint64_t Offset = *StmtOffset;
+ uint64_t LengthOrDWARF64Prefix = LineData.getU32(&Offset);
+ if (LengthOrDWARF64Prefix == dwarf::DW_LENGTH_DWARF64) {
+ DebugLines.Format = dwarf::DWARF64;
+ DebugLines.Length = LineData.getU64(&Offset);
+ } else {
+ DebugLines.Format = dwarf::DWARF32;
+ DebugLines.Length = LengthOrDWARF64Prefix;
+ }
+ assert(DebugLines.Length);
+ uint64_t LineTableLength = *DebugLines.Length;
+ uint64_t SizeOfPrologueLength =
+ DebugLines.Format == dwarf::DWARF64 ? 8 : 4;
+ DebugLines.Version = LineData.getU16(&Offset);
+ DebugLines.PrologueLength =
+ LineData.getUnsigned(&Offset, SizeOfPrologueLength);
+ assert(DebugLines.PrologueLength);
+ const uint64_t EndPrologue = *DebugLines.PrologueLength + Offset;
+
+ DebugLines.MinInstLength = LineData.getU8(&Offset);
+ if (DebugLines.Version >= 4)
+ DebugLines.MaxOpsPerInst = LineData.getU8(&Offset);
+ DebugLines.DefaultIsStmt = LineData.getU8(&Offset);
+ DebugLines.LineBase = LineData.getU8(&Offset);
+ DebugLines.LineRange = LineData.getU8(&Offset);
+ DebugLines.OpcodeBase = LineData.getU8(&Offset);
+
+ DebugLines.StandardOpcodeLengths.emplace();
+ for (uint8_t i = 1; i < DebugLines.OpcodeBase; ++i)
+ DebugLines.StandardOpcodeLengths->push_back(LineData.getU8(&Offset));
+
+ while (Offset < EndPrologue) {
+ StringRef Dir = LineData.getCStr(&Offset);
+ if (!Dir.empty())
+ DebugLines.IncludeDirs.push_back(Dir);
+ else
+ break;
+ }
+
+ while (Offset < EndPrologue) {
+ DWARFYAML::File TmpFile;
+ if (dumpFileEntry(LineData, Offset, TmpFile))
+ DebugLines.Files.push_back(TmpFile);
+ else
+ break;
+ }
+
+ const uint64_t LineEnd =
+ LineTableLength + *StmtOffset + SizeOfPrologueLength;
+ while (Offset < LineEnd) {
+ DWARFYAML::LineTableOpcode NewOp = {};
+ NewOp.Opcode = (dwarf::LineNumberOps)LineData.getU8(&Offset);
+ if (NewOp.Opcode == 0) {
+ auto StartExt = Offset;
+ NewOp.ExtLen = LineData.getULEB128(&Offset);
+ NewOp.SubOpcode =
+ (dwarf::LineNumberExtendedOps)LineData.getU8(&Offset);
+ switch (NewOp.SubOpcode) {
+ case dwarf::DW_LNE_set_address:
+ case dwarf::DW_LNE_set_discriminator:
+ NewOp.Data = LineData.getAddress(&Offset);
+ break;
+ case dwarf::DW_LNE_define_file:
+ dumpFileEntry(LineData, Offset, NewOp.FileEntry);
+ break;
+ case dwarf::DW_LNE_end_sequence:
+ break;
+ default:
+ while (Offset < StartExt + *NewOp.ExtLen)
+ NewOp.UnknownOpcodeData.push_back(LineData.getU8(&Offset));
+ }
+ } else if (NewOp.Opcode < *DebugLines.OpcodeBase) {
+ switch (NewOp.Opcode) {
+ case dwarf::DW_LNS_copy:
+ case dwarf::DW_LNS_negate_stmt:
+ case dwarf::DW_LNS_set_basic_block:
+ case dwarf::DW_LNS_const_add_pc:
+ case dwarf::DW_LNS_set_prologue_end:
+ case dwarf::DW_LNS_set_epilogue_begin:
+ break;
+
+ case dwarf::DW_LNS_advance_pc:
+ case dwarf::DW_LNS_set_file:
+ case dwarf::DW_LNS_set_column:
+ case dwarf::DW_LNS_set_isa:
+ NewOp.Data = LineData.getULEB128(&Offset);
+ break;
+
+ case dwarf::DW_LNS_advance_line:
+ NewOp.SData = LineData.getSLEB128(&Offset);
+ break;
+
+ case dwarf::DW_LNS_fixed_advance_pc:
+ NewOp.Data = LineData.getU16(&Offset);
+ break;
+
+ default:
+ for (uint8_t i = 0;
+ i <
+ DebugLines.StandardOpcodeLengths.getValue()[NewOp.Opcode - 1];
+ ++i)
+ NewOp.StandardOpcodeData.push_back(LineData.getULEB128(&Offset));
+ }
+ }
+ DebugLines.Opcodes.push_back(NewOp);
+ }
+ Y.DebugLines.push_back(DebugLines);
+ }
+ }
+}
diff --git a/contrib/libs/llvm14/tools/obj2yaml/elf2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/elf2yaml.cpp
new file mode 100644
index 00000000000..9d1713c8599
--- /dev/null
+++ b/contrib/libs/llvm14/tools/obj2yaml/elf2yaml.cpp
@@ -0,0 +1,1596 @@
+//===------ utils/elf2yaml.cpp - obj2yaml conversion tool -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "obj2yaml.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/ObjectYAML/DWARFYAML.h"
+#include "llvm/ObjectYAML/ELFYAML.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace llvm;
+
+namespace {
+
+template <class ELFT>
+class ELFDumper {
+ LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
+
+ ArrayRef<Elf_Shdr> Sections;
+ ArrayRef<Elf_Sym> SymTable;
+
+ DenseMap<StringRef, uint32_t> UsedSectionNames;
+ std::vector<std::string> SectionNames;
+ Optional<uint32_t> ShStrTabIndex;
+
+ DenseMap<StringRef, uint32_t> UsedSymbolNames;
+ std::vector<std::string> SymbolNames;
+
+ BumpPtrAllocator StringAllocator;
+
+ Expected<StringRef> getUniquedSectionName(const Elf_Shdr &Sec);
+ Expected<StringRef> getUniquedSymbolName(const Elf_Sym *Sym,
+ StringRef StrTable,
+ const Elf_Shdr *SymTab);
+ Expected<StringRef> getSymbolName(uint32_t SymtabNdx, uint32_t SymbolNdx);
+
+ const object::ELFFile<ELFT> &Obj;
+ std::unique_ptr<DWARFContext> DWARFCtx;
+
+ DenseMap<const Elf_Shdr *, ArrayRef<Elf_Word>> ShndxTables;
+
+ Expected<std::vector<ELFYAML::ProgramHeader>>
+ dumpProgramHeaders(ArrayRef<std::unique_ptr<ELFYAML::Chunk>> Sections);
+
+ Optional<DWARFYAML::Data>
+ dumpDWARFSections(std::vector<std::unique_ptr<ELFYAML::Chunk>> &Sections);
+
+ Error dumpSymbols(const Elf_Shdr *Symtab,
+ Optional<std::vector<ELFYAML::Symbol>> &Symbols);
+ Error dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab,
+ StringRef StrTable, ELFYAML::Symbol &S);
+ Expected<std::vector<std::unique_ptr<ELFYAML::Chunk>>> dumpSections();
+ Error dumpCommonSection(const Elf_Shdr *Shdr, ELFYAML::Section &S);
+ Error dumpCommonRelocationSection(const Elf_Shdr *Shdr,
+ ELFYAML::RelocationSection &S);
+ template <class RelT>
+ Error dumpRelocation(const RelT *Rel, const Elf_Shdr *SymTab,
+ ELFYAML::Relocation &R);
+
+ Expected<ELFYAML::AddrsigSection *> dumpAddrsigSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::LinkerOptionsSection *>
+ dumpLinkerOptionsSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::DependentLibrariesSection *>
+ dumpDependentLibrariesSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::CallGraphProfileSection *>
+ dumpCallGraphProfileSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::DynamicSection *> dumpDynamicSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::RelocationSection *> dumpRelocSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::RelrSection *> dumpRelrSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::RawContentSection *>
+ dumpContentSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::SymtabShndxSection *>
+ dumpSymtabShndxSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::NoBitsSection *> dumpNoBitsSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::HashSection *> dumpHashSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::NoteSection *> dumpNoteSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::GnuHashSection *> dumpGnuHashSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::VerdefSection *> dumpVerdefSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::SymverSection *> dumpSymverSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::VerneedSection *> dumpVerneedSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::GroupSection *> dumpGroupSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::ARMIndexTableSection *>
+ dumpARMIndexTableSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::MipsABIFlags *> dumpMipsABIFlags(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::StackSizesSection *>
+ dumpStackSizesSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::BBAddrMapSection *>
+ dumpBBAddrMapSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::RawContentSection *>
+ dumpPlaceholderSection(const Elf_Shdr *Shdr);
+
+ bool shouldPrintSection(const ELFYAML::Section &S, const Elf_Shdr &SHdr,
+ Optional<DWARFYAML::Data> DWARF);
+
+public:
+ ELFDumper(const object::ELFFile<ELFT> &O, std::unique_ptr<DWARFContext> DCtx);
+ Expected<ELFYAML::Object *> dump();
+};
+
+}
+
+template <class ELFT>
+ELFDumper<ELFT>::ELFDumper(const object::ELFFile<ELFT> &O,
+ std::unique_ptr<DWARFContext> DCtx)
+ : Obj(O), DWARFCtx(std::move(DCtx)) {}
+
+template <class ELFT>
+Expected<StringRef>
+ELFDumper<ELFT>::getUniquedSectionName(const Elf_Shdr &Sec) {
+ unsigned SecIndex = &Sec - &Sections[0];
+ if (!SectionNames[SecIndex].empty())
+ return SectionNames[SecIndex];
+
+ auto NameOrErr = Obj.getSectionName(Sec);
+ if (!NameOrErr)
+ return NameOrErr;
+ StringRef Name = *NameOrErr;
+ // In some specific cases we might have more than one section without a
+ // name (sh_name == 0). It normally doesn't happen, but when we have this case
+ // it doesn't make sense to uniquify their names and add noise to the output.
+ if (Name.empty())
+ return "";
+
+ std::string &Ret = SectionNames[SecIndex];
+
+ auto It = UsedSectionNames.insert({Name, 0});
+ if (!It.second)
+ Ret = ELFYAML::appendUniqueSuffix(Name, Twine(++It.first->second));
+ else
+ Ret = std::string(Name);
+ return Ret;
+}
+
+template <class ELFT>
+Expected<StringRef>
+ELFDumper<ELFT>::getUniquedSymbolName(const Elf_Sym *Sym, StringRef StrTable,
+ const Elf_Shdr *SymTab) {
+ Expected<StringRef> SymbolNameOrErr = Sym->getName(StrTable);
+ if (!SymbolNameOrErr)
+ return SymbolNameOrErr;
+ StringRef Name = *SymbolNameOrErr;
+ if (Name.empty() && Sym->getType() == ELF::STT_SECTION) {
+ Expected<const Elf_Shdr *> ShdrOrErr =
+ Obj.getSection(*Sym, SymTab, ShndxTables.lookup(SymTab));
+ if (!ShdrOrErr)
+ return ShdrOrErr.takeError();
+ // The null section has no name.
+ return (*ShdrOrErr == nullptr) ? "" : getUniquedSectionName(**ShdrOrErr);
+ }
+
+ // Symbols in .symtab can have duplicate names. For example, it is a common
+ // situation for local symbols in a relocatable object. Here we assign unique
+ // suffixes for such symbols so that we can differentiate them.
+ if (SymTab->sh_type == ELF::SHT_SYMTAB) {
+ unsigned Index = Sym - SymTable.data();
+ if (!SymbolNames[Index].empty())
+ return SymbolNames[Index];
+
+ auto It = UsedSymbolNames.insert({Name, 0});
+ if (!It.second)
+ SymbolNames[Index] =
+ ELFYAML::appendUniqueSuffix(Name, Twine(++It.first->second));
+ else
+ SymbolNames[Index] = std::string(Name);
+ return SymbolNames[Index];
+ }
+
+ return Name;
+}
+
+template <class ELFT>
+bool ELFDumper<ELFT>::shouldPrintSection(const ELFYAML::Section &S,
+ const Elf_Shdr &SHdr,
+ Optional<DWARFYAML::Data> DWARF) {
+ // We only print the SHT_NULL section at index 0 when it
+ // has at least one non-null field, because yaml2obj
+ // normally creates the zero section at index 0 implicitly.
+ if (S.Type == ELF::SHT_NULL && (&SHdr == &Sections[0])) {
+ const uint8_t *Begin = reinterpret_cast<const uint8_t *>(&SHdr);
+ const uint8_t *End = Begin + sizeof(Elf_Shdr);
+ return std::any_of(Begin, End, [](uint8_t V) { return V != 0; });
+ }
+
+ // Normally we use "DWARF:" to describe contents of DWARF sections. Sometimes
+ // the content of DWARF sections can be successfully parsed into the "DWARF:"
+ // entry but their section headers may have special flags, entry size, address
+ // alignment, etc. We will preserve the header for them under such
+ // circumstances.
+ StringRef SecName = S.Name.substr(1);
+ if (DWARF && DWARF->getNonEmptySectionNames().count(SecName)) {
+ if (const ELFYAML::RawContentSection *RawSec =
+ dyn_cast<const ELFYAML::RawContentSection>(&S)) {
+ if (RawSec->Type != ELF::SHT_PROGBITS || RawSec->Link || RawSec->Info ||
+ RawSec->AddressAlign != yaml::Hex64{1} || RawSec->Address ||
+ RawSec->EntSize)
+ return true;
+
+ ELFYAML::ELF_SHF ShFlags = RawSec->Flags.getValueOr(ELFYAML::ELF_SHF(0));
+
+ if (SecName == "debug_str")
+ return ShFlags != ELFYAML::ELF_SHF(ELF::SHF_MERGE | ELF::SHF_STRINGS);
+
+ return ShFlags != ELFYAML::ELF_SHF{0};
+ }
+ }
+
+ // Normally we use "Symbols:" and "DynamicSymbols:" to describe contents of
+ // symbol tables. We also build and emit corresponding string tables
+ // implicitly. But sometimes it is important to preserve positions and virtual
+ // addresses of allocatable sections, e.g. for creating program headers.
+ // Generally we are trying to reduce noise in the YAML output. Because
+ // of that we do not print non-allocatable versions of such sections and
+ // assume they are placed at the end.
+ // We also dump symbol tables when the Size field is set. It happens when they
+ // are empty, which should not normally happen.
+ if (S.Type == ELF::SHT_STRTAB || S.Type == ELF::SHT_SYMTAB ||
+ S.Type == ELF::SHT_DYNSYM) {
+ return S.Size || S.Flags.getValueOr(ELFYAML::ELF_SHF(0)) & ELF::SHF_ALLOC;
+ }
+
+ return true;
+}
+
+template <class ELFT>
+static void dumpSectionOffsets(const typename ELFT::Ehdr &Header,
+ ArrayRef<ELFYAML::ProgramHeader> Phdrs,
+ std::vector<std::unique_ptr<ELFYAML::Chunk>> &V,
+ ArrayRef<typename ELFT::Shdr> S) {
+ if (V.empty())
+ return;
+
+ uint64_t ExpectedOffset;
+ if (Header.e_phoff > 0)
+ ExpectedOffset = Header.e_phoff + Header.e_phentsize * Header.e_phnum;
+ else
+ ExpectedOffset = sizeof(typename ELFT::Ehdr);
+
+ for (const std::unique_ptr<ELFYAML::Chunk> &C :
+ makeArrayRef(V).drop_front()) {
+ ELFYAML::Section &Sec = *cast<ELFYAML::Section>(C.get());
+ const typename ELFT::Shdr &SecHdr = S[Sec.OriginalSecNdx];
+
+ ExpectedOffset = alignTo(ExpectedOffset,
+ SecHdr.sh_addralign ? SecHdr.sh_addralign : 1uLL);
+
+ // We only set the "Offset" field when it can't be naturally derived
+ // from the offset and size of the previous section. This reduces
+ // the noise in the YAML output.
+ if (SecHdr.sh_offset != ExpectedOffset)
+ Sec.Offset = (yaml::Hex64)SecHdr.sh_offset;
+
+ if (Sec.Type == ELF::SHT_NOBITS &&
+ !ELFYAML::shouldAllocateFileSpace(Phdrs,
+ *cast<ELFYAML::NoBitsSection>(&Sec)))
+ ExpectedOffset = SecHdr.sh_offset;
+ else
+ ExpectedOffset = SecHdr.sh_offset + SecHdr.sh_size;
+ }
+}
+
+template <class ELFT> Expected<ELFYAML::Object *> ELFDumper<ELFT>::dump() {
+ auto Y = std::make_unique<ELFYAML::Object>();
+
+ // Dump header. We do not dump EPh* and ESh* fields. When not explicitly set,
+ // the values are set by yaml2obj automatically and there is no need to dump
+ // them here.
+ 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;
+ if (Obj.getHeader().e_machine != 0)
+ Y->Header.Machine = ELFYAML::ELF_EM(Obj.getHeader().e_machine);
+ Y->Header.Flags = Obj.getHeader().e_flags;
+ Y->Header.Entry = Obj.getHeader().e_entry;
+
+ // Dump sections
+ auto SectionsOrErr = Obj.sections();
+ if (!SectionsOrErr)
+ return SectionsOrErr.takeError();
+ Sections = *SectionsOrErr;
+ SectionNames.resize(Sections.size());
+
+ if (Sections.size() > 0) {
+ ShStrTabIndex = Obj.getHeader().e_shstrndx;
+ if (*ShStrTabIndex == ELF::SHN_XINDEX)
+ ShStrTabIndex = Sections[0].sh_link;
+ // TODO: Set EShStrndx if the value doesn't represent a real section.
+ }
+
+ // Normally an object that does not have sections has e_shnum == 0.
+ // Also, e_shnum might be 0, when the the number of entries in the section
+ // header table is larger than or equal to SHN_LORESERVE (0xff00). In this
+ // case the real number of entries is held in the sh_size member of the
+ // initial entry. We have a section header table when `e_shoff` is not 0.
+ if (Obj.getHeader().e_shoff != 0 && Obj.getHeader().e_shnum == 0)
+ Y->Header.EShNum = 0;
+
+ // Dump symbols. We need to do this early because other sections might want
+ // to access the deduplicated symbol names that we also create here.
+ const Elf_Shdr *SymTab = nullptr;
+ const Elf_Shdr *DynSymTab = nullptr;
+
+ for (const Elf_Shdr &Sec : Sections) {
+ if (Sec.sh_type == ELF::SHT_SYMTAB) {
+ SymTab = &Sec;
+ } else if (Sec.sh_type == ELF::SHT_DYNSYM) {
+ DynSymTab = &Sec;
+ } else if (Sec.sh_type == ELF::SHT_SYMTAB_SHNDX) {
+ // We need to locate SHT_SYMTAB_SHNDX sections early, because they
+ // might be needed for dumping symbols.
+ if (Expected<ArrayRef<Elf_Word>> TableOrErr = Obj.getSHNDXTable(Sec)) {
+ // The `getSHNDXTable` calls the `getSection` internally when validates
+ // the symbol table section linked to the SHT_SYMTAB_SHNDX section.
+ const Elf_Shdr *LinkedSymTab = cantFail(Obj.getSection(Sec.sh_link));
+ if (!ShndxTables.insert({LinkedSymTab, *TableOrErr}).second)
+ return createStringError(
+ errc::invalid_argument,
+ "multiple SHT_SYMTAB_SHNDX sections are "
+ "linked to the same symbol table with index " +
+ Twine(Sec.sh_link));
+ } else {
+ return createStringError(errc::invalid_argument,
+ "unable to read extended section indexes: " +
+ toString(TableOrErr.takeError()));
+ }
+ }
+ }
+
+ if (SymTab)
+ if (Error E = dumpSymbols(SymTab, Y->Symbols))
+ return std::move(E);
+
+ if (DynSymTab)
+ if (Error E = dumpSymbols(DynSymTab, Y->DynamicSymbols))
+ return std::move(E);
+
+ // We dump all sections first. It is simple and allows us to verify that all
+ // sections are valid and also to generalize the code. But we are not going to
+ // keep all of them in the final output (see comments for
+ // 'shouldPrintSection()'). Undesired chunks will be removed later.
+ Expected<std::vector<std::unique_ptr<ELFYAML::Chunk>>> ChunksOrErr =
+ dumpSections();
+ if (!ChunksOrErr)
+ return ChunksOrErr.takeError();
+ std::vector<std::unique_ptr<ELFYAML::Chunk>> Chunks = std::move(*ChunksOrErr);
+
+ std::vector<ELFYAML::Section *> OriginalOrder;
+ if (!Chunks.empty())
+ for (const std::unique_ptr<ELFYAML::Chunk> &C :
+ makeArrayRef(Chunks).drop_front())
+ OriginalOrder.push_back(cast<ELFYAML::Section>(C.get()));
+
+ // Sometimes the order of sections in the section header table does not match
+ // their actual order. Here we sort sections by the file offset.
+ llvm::stable_sort(Chunks, [&](const std::unique_ptr<ELFYAML::Chunk> &A,
+ const std::unique_ptr<ELFYAML::Chunk> &B) {
+ return Sections[cast<ELFYAML::Section>(A.get())->OriginalSecNdx].sh_offset <
+ Sections[cast<ELFYAML::Section>(B.get())->OriginalSecNdx].sh_offset;
+ });
+
+ // Dump program headers.
+ Expected<std::vector<ELFYAML::ProgramHeader>> PhdrsOrErr =
+ dumpProgramHeaders(Chunks);
+ if (!PhdrsOrErr)
+ return PhdrsOrErr.takeError();
+ Y->ProgramHeaders = std::move(*PhdrsOrErr);
+
+ dumpSectionOffsets<ELFT>(Obj.getHeader(), Y->ProgramHeaders, Chunks,
+ Sections);
+
+ // Dump DWARF sections.
+ Y->DWARF = dumpDWARFSections(Chunks);
+
+ // We emit the "SectionHeaderTable" key when the order of sections in the
+ // sections header table doesn't match the file order.
+ const bool SectionsSorted =
+ llvm::is_sorted(Chunks, [&](const std::unique_ptr<ELFYAML::Chunk> &A,
+ const std::unique_ptr<ELFYAML::Chunk> &B) {
+ return cast<ELFYAML::Section>(A.get())->OriginalSecNdx <
+ cast<ELFYAML::Section>(B.get())->OriginalSecNdx;
+ });
+ if (!SectionsSorted) {
+ std::unique_ptr<ELFYAML::SectionHeaderTable> SHT =
+ std::make_unique<ELFYAML::SectionHeaderTable>(/*IsImplicit=*/false);
+ SHT->Sections.emplace();
+ for (ELFYAML::Section *S : OriginalOrder)
+ SHT->Sections->push_back({S->Name});
+ Chunks.push_back(std::move(SHT));
+ }
+
+ llvm::erase_if(Chunks, [this, &Y](const std::unique_ptr<ELFYAML::Chunk> &C) {
+ if (isa<ELFYAML::SectionHeaderTable>(*C.get()))
+ return false;
+
+ const ELFYAML::Section &S = cast<ELFYAML::Section>(*C.get());
+ return !shouldPrintSection(S, Sections[S.OriginalSecNdx], Y->DWARF);
+ });
+
+ // The section header string table by default is assumed to be called
+ // ".shstrtab" and be in its own unique section. However, it's possible for it
+ // to be called something else and shared with another section. If the name
+ // isn't the default, provide this in the YAML.
+ if (ShStrTabIndex && *ShStrTabIndex != ELF::SHN_UNDEF &&
+ *ShStrTabIndex < Sections.size()) {
+ StringRef ShStrtabName;
+ if (SymTab && SymTab->sh_link == *ShStrTabIndex) {
+ // Section header string table is shared with the symbol table. Use that
+ // section's name (usually .strtab).
+ ShStrtabName = cantFail(Obj.getSectionName(Sections[SymTab->sh_link]));
+ } else if (DynSymTab && DynSymTab->sh_link == *ShStrTabIndex) {
+ // Section header string table is shared with the dynamic symbol table.
+ // Use that section's name (usually .dynstr).
+ ShStrtabName = cantFail(Obj.getSectionName(Sections[DynSymTab->sh_link]));
+ } else {
+ // Otherwise, the section name potentially needs uniquifying.
+ ShStrtabName = cantFail(getUniquedSectionName(Sections[*ShStrTabIndex]));
+ }
+ if (ShStrtabName != ".shstrtab")
+ Y->Header.SectionHeaderStringTable = ShStrtabName;
+ }
+
+ Y->Chunks = std::move(Chunks);
+ return Y.release();
+}
+
+template <class ELFT>
+static bool isInSegment(const ELFYAML::Section &Sec,
+ const typename ELFT::Shdr &SHdr,
+ const typename ELFT::Phdr &Phdr) {
+ if (Sec.Type == ELF::SHT_NULL)
+ return false;
+
+ // A section is within a segment when its location in a file is within the
+ // [p_offset, p_offset + p_filesz] region.
+ bool FileOffsetsMatch =
+ SHdr.sh_offset >= Phdr.p_offset &&
+ (SHdr.sh_offset + SHdr.sh_size <= Phdr.p_offset + Phdr.p_filesz);
+
+ bool VirtualAddressesMatch = SHdr.sh_addr >= Phdr.p_vaddr &&
+ SHdr.sh_addr <= Phdr.p_vaddr + Phdr.p_memsz;
+
+ if (FileOffsetsMatch) {
+ // An empty section on the edges of a program header can be outside of the
+ // virtual address space of the segment. This means it is not included in
+ // the segment and we should ignore it.
+ if (SHdr.sh_size == 0 && (SHdr.sh_offset == Phdr.p_offset ||
+ SHdr.sh_offset == Phdr.p_offset + Phdr.p_filesz))
+ return VirtualAddressesMatch;
+ return true;
+ }
+
+ // SHT_NOBITS sections usually occupy no physical space in a file. Such
+ // sections belong to a segment when they reside in the segment's virtual
+ // address space.
+ if (Sec.Type != ELF::SHT_NOBITS)
+ return false;
+ return VirtualAddressesMatch;
+}
+
+template <class ELFT>
+Expected<std::vector<ELFYAML::ProgramHeader>>
+ELFDumper<ELFT>::dumpProgramHeaders(
+ ArrayRef<std::unique_ptr<ELFYAML::Chunk>> Chunks) {
+ std::vector<ELFYAML::ProgramHeader> Ret;
+ Expected<typename ELFT::PhdrRange> PhdrsOrErr = Obj.program_headers();
+ if (!PhdrsOrErr)
+ return PhdrsOrErr.takeError();
+
+ for (const typename ELFT::Phdr &Phdr : *PhdrsOrErr) {
+ ELFYAML::ProgramHeader PH;
+ PH.Type = Phdr.p_type;
+ PH.Flags = Phdr.p_flags;
+ PH.VAddr = Phdr.p_vaddr;
+ PH.PAddr = Phdr.p_paddr;
+
+ // yaml2obj sets the alignment of a segment to 1 by default.
+ // We do not print the default alignment to reduce noise in the output.
+ if (Phdr.p_align != 1)
+ PH.Align = static_cast<llvm::yaml::Hex64>(Phdr.p_align);
+
+ // Here we match sections with segments.
+ // It is not possible to have a non-Section chunk, because
+ // obj2yaml does not create Fill chunks.
+ for (const std::unique_ptr<ELFYAML::Chunk> &C : Chunks) {
+ ELFYAML::Section &S = cast<ELFYAML::Section>(*C.get());
+ if (isInSegment<ELFT>(S, Sections[S.OriginalSecNdx], Phdr)) {
+ if (!PH.FirstSec)
+ PH.FirstSec = S.Name;
+ PH.LastSec = S.Name;
+ PH.Chunks.push_back(C.get());
+ }
+ }
+
+ Ret.push_back(PH);
+ }
+
+ return Ret;
+}
+
+template <class ELFT>
+Optional<DWARFYAML::Data> ELFDumper<ELFT>::dumpDWARFSections(
+ std::vector<std::unique_ptr<ELFYAML::Chunk>> &Sections) {
+ DWARFYAML::Data DWARF;
+ for (std::unique_ptr<ELFYAML::Chunk> &C : Sections) {
+ if (!C->Name.startswith(".debug_"))
+ continue;
+
+ if (ELFYAML::RawContentSection *RawSec =
+ dyn_cast<ELFYAML::RawContentSection>(C.get())) {
+ // FIXME: The dumpDebug* functions should take the content as stored in
+ // RawSec. Currently, they just use the last section with the matching
+ // name, which defeats this attempt to skip reading a section header
+ // string table with the same name as a DWARF section.
+ if (ShStrTabIndex && RawSec->OriginalSecNdx == *ShStrTabIndex)
+ continue;
+ Error Err = Error::success();
+ cantFail(std::move(Err));
+
+ if (RawSec->Name == ".debug_aranges")
+ Err = dumpDebugARanges(*DWARFCtx.get(), DWARF);
+ else if (RawSec->Name == ".debug_str")
+ Err = dumpDebugStrings(*DWARFCtx.get(), DWARF);
+ else if (RawSec->Name == ".debug_ranges")
+ Err = dumpDebugRanges(*DWARFCtx.get(), DWARF);
+ else if (RawSec->Name == ".debug_addr")
+ Err = dumpDebugAddr(*DWARFCtx.get(), DWARF);
+ else
+ continue;
+
+ // If the DWARF section cannot be successfully parsed, emit raw content
+ // instead of an entry in the DWARF section of the YAML.
+ if (Err)
+ consumeError(std::move(Err));
+ else
+ RawSec->Content.reset();
+ }
+ }
+
+ if (DWARF.getNonEmptySectionNames().empty())
+ return None;
+ return DWARF;
+}
+
+template <class ELFT>
+Expected<ELFYAML::RawContentSection *>
+ELFDumper<ELFT>::dumpPlaceholderSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::RawContentSection>();
+ if (Error E = dumpCommonSection(Shdr, *S.get()))
+ return std::move(E);
+
+ // Normally symbol tables should not be empty. We dump the "Size"
+ // key when they are.
+ if ((Shdr->sh_type == ELF::SHT_SYMTAB || Shdr->sh_type == ELF::SHT_DYNSYM) &&
+ !Shdr->sh_size)
+ S->Size.emplace();
+
+ return S.release();
+}
+
+template <class ELFT>
+Expected<std::vector<std::unique_ptr<ELFYAML::Chunk>>>
+ELFDumper<ELFT>::dumpSections() {
+ std::vector<std::unique_ptr<ELFYAML::Chunk>> Ret;
+ auto Add = [&](Expected<ELFYAML::Chunk *> SecOrErr) -> Error {
+ if (!SecOrErr)
+ return SecOrErr.takeError();
+ Ret.emplace_back(*SecOrErr);
+ return Error::success();
+ };
+
+ auto GetDumper = [this](unsigned Type)
+ -> std::function<Expected<ELFYAML::Chunk *>(const Elf_Shdr *)> {
+ if (Obj.getHeader().e_machine == ELF::EM_ARM && Type == ELF::SHT_ARM_EXIDX)
+ return [this](const Elf_Shdr *S) { return dumpARMIndexTableSection(S); };
+
+ if (Obj.getHeader().e_machine == ELF::EM_MIPS &&
+ Type == ELF::SHT_MIPS_ABIFLAGS)
+ return [this](const Elf_Shdr *S) { return dumpMipsABIFlags(S); };
+
+ switch (Type) {
+ case ELF::SHT_DYNAMIC:
+ return [this](const Elf_Shdr *S) { return dumpDynamicSection(S); };
+ case ELF::SHT_SYMTAB_SHNDX:
+ return [this](const Elf_Shdr *S) { return dumpSymtabShndxSection(S); };
+ case ELF::SHT_REL:
+ case ELF::SHT_RELA:
+ return [this](const Elf_Shdr *S) { return dumpRelocSection(S); };
+ case ELF::SHT_RELR:
+ return [this](const Elf_Shdr *S) { return dumpRelrSection(S); };
+ case ELF::SHT_GROUP:
+ return [this](const Elf_Shdr *S) { return dumpGroupSection(S); };
+ case ELF::SHT_NOBITS:
+ return [this](const Elf_Shdr *S) { return dumpNoBitsSection(S); };
+ case ELF::SHT_NOTE:
+ return [this](const Elf_Shdr *S) { return dumpNoteSection(S); };
+ case ELF::SHT_HASH:
+ return [this](const Elf_Shdr *S) { return dumpHashSection(S); };
+ case ELF::SHT_GNU_HASH:
+ return [this](const Elf_Shdr *S) { return dumpGnuHashSection(S); };
+ case ELF::SHT_GNU_verdef:
+ return [this](const Elf_Shdr *S) { return dumpVerdefSection(S); };
+ case ELF::SHT_GNU_versym:
+ return [this](const Elf_Shdr *S) { return dumpSymverSection(S); };
+ case ELF::SHT_GNU_verneed:
+ return [this](const Elf_Shdr *S) { return dumpVerneedSection(S); };
+ case ELF::SHT_LLVM_ADDRSIG:
+ return [this](const Elf_Shdr *S) { return dumpAddrsigSection(S); };
+ case ELF::SHT_LLVM_LINKER_OPTIONS:
+ return [this](const Elf_Shdr *S) { return dumpLinkerOptionsSection(S); };
+ case ELF::SHT_LLVM_DEPENDENT_LIBRARIES:
+ return [this](const Elf_Shdr *S) {
+ return dumpDependentLibrariesSection(S);
+ };
+ case ELF::SHT_LLVM_CALL_GRAPH_PROFILE:
+ return
+ [this](const Elf_Shdr *S) { return dumpCallGraphProfileSection(S); };
+ case ELF::SHT_LLVM_BB_ADDR_MAP:
+ return [this](const Elf_Shdr *S) { return dumpBBAddrMapSection(S); };
+ case ELF::SHT_STRTAB:
+ case ELF::SHT_SYMTAB:
+ case ELF::SHT_DYNSYM:
+ // The contents of these sections are described by other parts of the YAML
+ // file. But we still want to dump them, because their properties can be
+ // important. See comments for 'shouldPrintSection()' for more details.
+ return [this](const Elf_Shdr *S) { return dumpPlaceholderSection(S); };
+ default:
+ return nullptr;
+ }
+ };
+
+ for (const Elf_Shdr &Sec : Sections) {
+ // We have dedicated dumping functions for most of the section types.
+ // Try to use one of them first.
+ if (std::function<Expected<ELFYAML::Chunk *>(const Elf_Shdr *)> DumpFn =
+ GetDumper(Sec.sh_type)) {
+ if (Error E = Add(DumpFn(&Sec)))
+ return std::move(E);
+ continue;
+ }
+
+ // Recognize some special SHT_PROGBITS sections by name.
+ if (Sec.sh_type == ELF::SHT_PROGBITS) {
+ auto NameOrErr = Obj.getSectionName(Sec);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+
+ if (ELFYAML::StackSizesSection::nameMatches(*NameOrErr)) {
+ if (Error E = Add(dumpStackSizesSection(&Sec)))
+ return std::move(E);
+ continue;
+ }
+ }
+
+ if (Error E = Add(dumpContentSection(&Sec)))
+ return std::move(E);
+ }
+
+ return std::move(Ret);
+}
+
+template <class ELFT>
+Error ELFDumper<ELFT>::dumpSymbols(
+ const Elf_Shdr *Symtab, Optional<std::vector<ELFYAML::Symbol>> &Symbols) {
+ if (!Symtab)
+ return Error::success();
+
+ auto SymtabOrErr = Obj.symbols(Symtab);
+ if (!SymtabOrErr)
+ return SymtabOrErr.takeError();
+
+ if (SymtabOrErr->empty())
+ return Error::success();
+
+ auto StrTableOrErr = Obj.getStringTableForSymtab(*Symtab);
+ if (!StrTableOrErr)
+ return StrTableOrErr.takeError();
+
+ if (Symtab->sh_type == ELF::SHT_SYMTAB) {
+ SymTable = *SymtabOrErr;
+ SymbolNames.resize(SymTable.size());
+ }
+
+ Symbols.emplace();
+ for (const auto &Sym : (*SymtabOrErr).drop_front()) {
+ ELFYAML::Symbol S;
+ if (auto EC = dumpSymbol(&Sym, Symtab, *StrTableOrErr, S))
+ return EC;
+ Symbols->push_back(S);
+ }
+
+ return Error::success();
+}
+
+template <class ELFT>
+Error ELFDumper<ELFT>::dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab,
+ StringRef StrTable, ELFYAML::Symbol &S) {
+ S.Type = Sym->getType();
+ if (Sym->st_value)
+ S.Value = (yaml::Hex64)Sym->st_value;
+ if (Sym->st_size)
+ S.Size = (yaml::Hex64)Sym->st_size;
+ S.Other = Sym->st_other;
+ S.Binding = Sym->getBinding();
+
+ Expected<StringRef> SymbolNameOrErr =
+ getUniquedSymbolName(Sym, StrTable, SymTab);
+ if (!SymbolNameOrErr)
+ return SymbolNameOrErr.takeError();
+ S.Name = SymbolNameOrErr.get();
+
+ if (Sym->st_shndx >= ELF::SHN_LORESERVE) {
+ S.Index = (ELFYAML::ELF_SHN)Sym->st_shndx;
+ return Error::success();
+ }
+
+ auto ShdrOrErr = Obj.getSection(*Sym, SymTab, ShndxTables.lookup(SymTab));
+ if (!ShdrOrErr)
+ return ShdrOrErr.takeError();
+ const Elf_Shdr *Shdr = *ShdrOrErr;
+ if (!Shdr)
+ return Error::success();
+
+ auto NameOrErr = getUniquedSectionName(*Shdr);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ S.Section = NameOrErr.get();
+
+ return Error::success();
+}
+
+template <class ELFT>
+template <class RelT>
+Error ELFDumper<ELFT>::dumpRelocation(const RelT *Rel, const Elf_Shdr *SymTab,
+ ELFYAML::Relocation &R) {
+ R.Type = Rel->getType(Obj.isMips64EL());
+ R.Offset = Rel->r_offset;
+ R.Addend = 0;
+
+ auto SymOrErr = Obj.getRelocationSymbol(*Rel, SymTab);
+ if (!SymOrErr)
+ return SymOrErr.takeError();
+
+ // We have might have a relocation with symbol index 0,
+ // e.g. R_X86_64_NONE or R_X86_64_GOTPC32.
+ const Elf_Sym *Sym = *SymOrErr;
+ if (!Sym)
+ return Error::success();
+
+ auto StrTabSec = Obj.getSection(SymTab->sh_link);
+ if (!StrTabSec)
+ return StrTabSec.takeError();
+ auto StrTabOrErr = Obj.getStringTable(**StrTabSec);
+ if (!StrTabOrErr)
+ return StrTabOrErr.takeError();
+
+ Expected<StringRef> NameOrErr =
+ getUniquedSymbolName(Sym, *StrTabOrErr, SymTab);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ R.Symbol = NameOrErr.get();
+
+ return Error::success();
+}
+
+template <class ELFT>
+Error ELFDumper<ELFT>::dumpCommonSection(const Elf_Shdr *Shdr,
+ ELFYAML::Section &S) {
+ // Dump fields. We do not dump the ShOffset field. When not explicitly
+ // set, the value is set by yaml2obj automatically.
+ S.Type = Shdr->sh_type;
+ if (Shdr->sh_flags)
+ S.Flags = static_cast<ELFYAML::ELF_SHF>(Shdr->sh_flags);
+ if (Shdr->sh_addr)
+ S.Address = static_cast<uint64_t>(Shdr->sh_addr);
+ S.AddressAlign = Shdr->sh_addralign;
+
+ S.OriginalSecNdx = Shdr - &Sections[0];
+
+ Expected<StringRef> NameOrErr = getUniquedSectionName(*Shdr);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ S.Name = NameOrErr.get();
+
+ if (Shdr->sh_entsize != ELFYAML::getDefaultShEntSize<ELFT>(
+ Obj.getHeader().e_machine, S.Type, S.Name))
+ S.EntSize = static_cast<llvm::yaml::Hex64>(Shdr->sh_entsize);
+
+ if (Shdr->sh_link != ELF::SHN_UNDEF) {
+ Expected<const Elf_Shdr *> LinkSection = Obj.getSection(Shdr->sh_link);
+ if (!LinkSection)
+ return make_error<StringError>(
+ "unable to resolve sh_link reference in section '" + S.Name +
+ "': " + toString(LinkSection.takeError()),
+ inconvertibleErrorCode());
+
+ NameOrErr = getUniquedSectionName(**LinkSection);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ S.Link = NameOrErr.get();
+ }
+
+ return Error::success();
+}
+
+template <class ELFT>
+Error ELFDumper<ELFT>::dumpCommonRelocationSection(
+ const Elf_Shdr *Shdr, ELFYAML::RelocationSection &S) {
+ if (Error E = dumpCommonSection(Shdr, S))
+ return E;
+
+ // Having a zero sh_info field is normal: .rela.dyn is a dynamic
+ // relocation section that normally has no value in this field.
+ if (!Shdr->sh_info)
+ return Error::success();
+
+ auto InfoSection = Obj.getSection(Shdr->sh_info);
+ if (!InfoSection)
+ return InfoSection.takeError();
+
+ Expected<StringRef> NameOrErr = getUniquedSectionName(**InfoSection);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ S.RelocatableSec = NameOrErr.get();
+
+ return Error::success();
+}
+
+template <class ELFT>
+Expected<ELFYAML::StackSizesSection *>
+ELFDumper<ELFT>::dumpStackSizesSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::StackSizesSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ DataExtractor Data(Content, Obj.isLE(), ELFT::Is64Bits ? 8 : 4);
+
+ std::vector<ELFYAML::StackSizeEntry> Entries;
+ DataExtractor::Cursor Cur(0);
+ while (Cur && Cur.tell() < Content.size()) {
+ uint64_t Address = Data.getAddress(Cur);
+ uint64_t Size = Data.getULEB128(Cur);
+ Entries.push_back({Address, Size});
+ }
+
+ if (Content.empty() || !Cur) {
+ // If .stack_sizes cannot be decoded, we dump it as an array of bytes.
+ consumeError(Cur.takeError());
+ S->Content = yaml::BinaryRef(Content);
+ } else {
+ S->Entries = std::move(Entries);
+ }
+
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::BBAddrMapSection *>
+ELFDumper<ELFT>::dumpBBAddrMapSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::BBAddrMapSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ if (Content.empty())
+ return S.release();
+
+ DataExtractor Data(Content, Obj.isLE(), ELFT::Is64Bits ? 8 : 4);
+
+ std::vector<ELFYAML::BBAddrMapEntry> Entries;
+ DataExtractor::Cursor Cur(0);
+ while (Cur && Cur.tell() < Content.size()) {
+ uint64_t Address = Data.getAddress(Cur);
+ uint64_t NumBlocks = Data.getULEB128(Cur);
+ std::vector<ELFYAML::BBAddrMapEntry::BBEntry> BBEntries;
+ // Read the specified number of BB entries, or until decoding fails.
+ for (uint64_t BlockID = 0; Cur && BlockID < NumBlocks; ++BlockID) {
+ uint64_t Offset = Data.getULEB128(Cur);
+ uint64_t Size = Data.getULEB128(Cur);
+ uint64_t Metadata = Data.getULEB128(Cur);
+ BBEntries.push_back({Offset, Size, Metadata});
+ }
+ Entries.push_back({Address, /*NumBlocks=*/{}, BBEntries});
+ }
+
+ if (!Cur) {
+ // If the section cannot be decoded, we dump it as an array of bytes.
+ consumeError(Cur.takeError());
+ S->Content = yaml::BinaryRef(Content);
+ } else {
+ S->Entries = std::move(Entries);
+ }
+
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::AddrsigSection *>
+ELFDumper<ELFT>::dumpAddrsigSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::AddrsigSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ DataExtractor::Cursor Cur(0);
+ DataExtractor Data(Content, Obj.isLE(), /*AddressSize=*/0);
+ std::vector<ELFYAML::YAMLFlowString> Symbols;
+ while (Cur && Cur.tell() < Content.size()) {
+ uint64_t SymNdx = Data.getULEB128(Cur);
+ if (!Cur)
+ break;
+
+ Expected<StringRef> SymbolName = getSymbolName(Shdr->sh_link, SymNdx);
+ if (!SymbolName || SymbolName->empty()) {
+ consumeError(SymbolName.takeError());
+ Symbols.emplace_back(
+ StringRef(std::to_string(SymNdx)).copy(StringAllocator));
+ continue;
+ }
+
+ Symbols.emplace_back(*SymbolName);
+ }
+
+ if (Cur) {
+ S->Symbols = std::move(Symbols);
+ return S.release();
+ }
+
+ consumeError(Cur.takeError());
+ S->Content = yaml::BinaryRef(Content);
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::LinkerOptionsSection *>
+ELFDumper<ELFT>::dumpLinkerOptionsSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::LinkerOptionsSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ if (Content.empty() || Content.back() != 0) {
+ S->Content = Content;
+ return S.release();
+ }
+
+ SmallVector<StringRef, 16> Strings;
+ toStringRef(Content.drop_back()).split(Strings, '\0');
+ if (Strings.size() % 2 != 0) {
+ S->Content = Content;
+ return S.release();
+ }
+
+ S->Options.emplace();
+ for (size_t I = 0, E = Strings.size(); I != E; I += 2)
+ S->Options->push_back({Strings[I], Strings[I + 1]});
+
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::DependentLibrariesSection *>
+ELFDumper<ELFT>::dumpDependentLibrariesSection(const Elf_Shdr *Shdr) {
+ auto DL = std::make_unique<ELFYAML::DependentLibrariesSection>();
+ if (Error E = dumpCommonSection(Shdr, *DL))
+ return std::move(E);
+
+ Expected<ArrayRef<uint8_t>> ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ if (!Content.empty() && Content.back() != 0) {
+ DL->Content = Content;
+ return DL.release();
+ }
+
+ DL->Libs.emplace();
+ for (const uint8_t *I = Content.begin(), *E = Content.end(); I < E;) {
+ StringRef Lib((const char *)I);
+ DL->Libs->emplace_back(Lib);
+ I += Lib.size() + 1;
+ }
+
+ return DL.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::CallGraphProfileSection *>
+ELFDumper<ELFT>::dumpCallGraphProfileSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::CallGraphProfileSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ Expected<ArrayRef<uint8_t>> ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ const uint32_t SizeOfEntry = ELFYAML::getDefaultShEntSize<ELFT>(
+ Obj.getHeader().e_machine, S->Type, S->Name);
+ // Dump the section by using the Content key when it is truncated.
+ // There is no need to create either "Content" or "Entries" fields when the
+ // section is empty.
+ if (Content.empty() || Content.size() % SizeOfEntry != 0) {
+ if (!Content.empty())
+ S->Content = yaml::BinaryRef(Content);
+ return S.release();
+ }
+
+ std::vector<ELFYAML::CallGraphEntryWeight> Entries(Content.size() /
+ SizeOfEntry);
+ DataExtractor Data(Content, Obj.isLE(), /*AddressSize=*/0);
+ DataExtractor::Cursor Cur(0);
+ auto ReadEntry = [&](ELFYAML::CallGraphEntryWeight &E) {
+ E.Weight = Data.getU64(Cur);
+ if (!Cur) {
+ consumeError(Cur.takeError());
+ return false;
+ }
+ return true;
+ };
+
+ for (ELFYAML::CallGraphEntryWeight &E : Entries) {
+ if (ReadEntry(E))
+ continue;
+ S->Content = yaml::BinaryRef(Content);
+ return S.release();
+ }
+
+ S->Entries = std::move(Entries);
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::DynamicSection *>
+ELFDumper<ELFT>::dumpDynamicSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::DynamicSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto DynTagsOrErr = Obj.template getSectionContentsAsArray<Elf_Dyn>(*Shdr);
+ if (!DynTagsOrErr)
+ return DynTagsOrErr.takeError();
+
+ S->Entries.emplace();
+ for (const Elf_Dyn &Dyn : *DynTagsOrErr)
+ S->Entries->push_back({(ELFYAML::ELF_DYNTAG)Dyn.getTag(), Dyn.getVal()});
+
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::RelocationSection *>
+ELFDumper<ELFT>::dumpRelocSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::RelocationSection>();
+ if (auto E = dumpCommonRelocationSection(Shdr, *S))
+ return std::move(E);
+
+ auto SymTabOrErr = Obj.getSection(Shdr->sh_link);
+ if (!SymTabOrErr)
+ return SymTabOrErr.takeError();
+
+ if (Shdr->sh_size != 0)
+ S->Relocations.emplace();
+
+ if (Shdr->sh_type == ELF::SHT_REL) {
+ auto Rels = Obj.rels(*Shdr);
+ if (!Rels)
+ return Rels.takeError();
+ for (const Elf_Rel &Rel : *Rels) {
+ ELFYAML::Relocation R;
+ if (Error E = dumpRelocation(&Rel, *SymTabOrErr, R))
+ return std::move(E);
+ S->Relocations->push_back(R);
+ }
+ } else {
+ auto Rels = Obj.relas(*Shdr);
+ if (!Rels)
+ return Rels.takeError();
+ for (const Elf_Rela &Rel : *Rels) {
+ ELFYAML::Relocation R;
+ if (Error E = dumpRelocation(&Rel, *SymTabOrErr, R))
+ return std::move(E);
+ R.Addend = Rel.r_addend;
+ S->Relocations->push_back(R);
+ }
+ }
+
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::RelrSection *>
+ELFDumper<ELFT>::dumpRelrSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::RelrSection>();
+ if (auto E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ if (Expected<ArrayRef<Elf_Relr>> Relrs = Obj.relrs(*Shdr)) {
+ S->Entries.emplace();
+ for (Elf_Relr Rel : *Relrs)
+ S->Entries->emplace_back(Rel);
+ return S.release();
+ } else {
+ // Ignore. We are going to dump the data as raw content below.
+ consumeError(Relrs.takeError());
+ }
+
+ Expected<ArrayRef<uint8_t>> ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+ S->Content = *ContentOrErr;
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::RawContentSection *>
+ELFDumper<ELFT>::dumpContentSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::RawContentSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ unsigned SecIndex = Shdr - &Sections[0];
+ if (SecIndex != 0 || Shdr->sh_type != ELF::SHT_NULL) {
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ if (!Content.empty())
+ S->Content = yaml::BinaryRef(Content);
+ } else {
+ S->Size = static_cast<llvm::yaml::Hex64>(Shdr->sh_size);
+ }
+
+ if (Shdr->sh_info)
+ S->Info = static_cast<llvm::yaml::Hex64>(Shdr->sh_info);
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::SymtabShndxSection *>
+ELFDumper<ELFT>::dumpSymtabShndxSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::SymtabShndxSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto EntriesOrErr = Obj.template getSectionContentsAsArray<Elf_Word>(*Shdr);
+ if (!EntriesOrErr)
+ return EntriesOrErr.takeError();
+
+ S->Entries.emplace();
+ for (const Elf_Word &E : *EntriesOrErr)
+ S->Entries->push_back(E);
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::NoBitsSection *>
+ELFDumper<ELFT>::dumpNoBitsSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::NoBitsSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+ if (Shdr->sh_size)
+ S->Size = static_cast<llvm::yaml::Hex64>(Shdr->sh_size);
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::NoteSection *>
+ELFDumper<ELFT>::dumpNoteSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::NoteSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ std::vector<ELFYAML::NoteEntry> Entries;
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ while (!Content.empty()) {
+ if (Content.size() < sizeof(Elf_Nhdr)) {
+ S->Content = yaml::BinaryRef(*ContentOrErr);
+ return S.release();
+ }
+
+ const Elf_Nhdr *Header = reinterpret_cast<const Elf_Nhdr *>(Content.data());
+ if (Content.size() < Header->getSize()) {
+ S->Content = yaml::BinaryRef(*ContentOrErr);
+ return S.release();
+ }
+
+ Elf_Note Note(*Header);
+ Entries.push_back(
+ {Note.getName(), Note.getDesc(), (ELFYAML::ELF_NT)Note.getType()});
+
+ Content = Content.drop_front(Header->getSize());
+ }
+
+ S->Notes = std::move(Entries);
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::HashSection *>
+ELFDumper<ELFT>::dumpHashSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::HashSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ if (Content.size() % 4 != 0 || Content.size() < 8) {
+ S->Content = yaml::BinaryRef(Content);
+ return S.release();
+ }
+
+ DataExtractor::Cursor Cur(0);
+ DataExtractor Data(Content, Obj.isLE(), /*AddressSize=*/0);
+ uint64_t NBucket = Data.getU32(Cur);
+ uint64_t NChain = Data.getU32(Cur);
+ if (Content.size() != (2 + NBucket + NChain) * 4) {
+ S->Content = yaml::BinaryRef(Content);
+ if (Cur)
+ return S.release();
+ llvm_unreachable("entries were not read correctly");
+ }
+
+ S->Bucket.emplace(NBucket);
+ for (uint32_t &V : *S->Bucket)
+ V = Data.getU32(Cur);
+
+ S->Chain.emplace(NChain);
+ for (uint32_t &V : *S->Chain)
+ V = Data.getU32(Cur);
+
+ if (Cur)
+ return S.release();
+ llvm_unreachable("entries were not read correctly");
+}
+
+template <class ELFT>
+Expected<ELFYAML::GnuHashSection *>
+ELFDumper<ELFT>::dumpGnuHashSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::GnuHashSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ unsigned AddrSize = ELFT::Is64Bits ? 8 : 4;
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ DataExtractor Data(Content, Obj.isLE(), AddrSize);
+
+ ELFYAML::GnuHashHeader Header;
+ DataExtractor::Cursor Cur(0);
+ uint64_t NBuckets = Data.getU32(Cur);
+ Header.SymNdx = Data.getU32(Cur);
+ uint64_t MaskWords = Data.getU32(Cur);
+ Header.Shift2 = Data.getU32(Cur);
+
+ // Set just the raw binary content if we were unable to read the header
+ // or when the section data is truncated or malformed.
+ uint64_t Size = Data.getData().size() - Cur.tell();
+ if (!Cur || (Size < MaskWords * AddrSize + NBuckets * 4) ||
+ (Size % 4 != 0)) {
+ consumeError(Cur.takeError());
+ S->Content = yaml::BinaryRef(Content);
+ return S.release();
+ }
+
+ S->Header = Header;
+
+ S->BloomFilter.emplace(MaskWords);
+ for (llvm::yaml::Hex64 &Val : *S->BloomFilter)
+ Val = Data.getAddress(Cur);
+
+ S->HashBuckets.emplace(NBuckets);
+ for (llvm::yaml::Hex32 &Val : *S->HashBuckets)
+ Val = Data.getU32(Cur);
+
+ S->HashValues.emplace((Data.getData().size() - Cur.tell()) / 4);
+ for (llvm::yaml::Hex32 &Val : *S->HashValues)
+ Val = Data.getU32(Cur);
+
+ if (Cur)
+ return S.release();
+ llvm_unreachable("GnuHashSection was not read correctly");
+}
+
+template <class ELFT>
+Expected<ELFYAML::VerdefSection *>
+ELFDumper<ELFT>::dumpVerdefSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::VerdefSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto StringTableShdrOrErr = Obj.getSection(Shdr->sh_link);
+ if (!StringTableShdrOrErr)
+ return StringTableShdrOrErr.takeError();
+
+ auto StringTableOrErr = Obj.getStringTable(**StringTableShdrOrErr);
+ if (!StringTableOrErr)
+ return StringTableOrErr.takeError();
+
+ auto Contents = Obj.getSectionContents(*Shdr);
+ if (!Contents)
+ return Contents.takeError();
+
+ S->Entries.emplace();
+
+ llvm::ArrayRef<uint8_t> Data = *Contents;
+ const uint8_t *Buf = Data.data();
+ while (Buf) {
+ const Elf_Verdef *Verdef = reinterpret_cast<const Elf_Verdef *>(Buf);
+ ELFYAML::VerdefEntry Entry;
+ if (Verdef->vd_version != 1)
+ return createStringError(errc::invalid_argument,
+ "invalid SHT_GNU_verdef section version: " +
+ Twine(Verdef->vd_version));
+
+ if (Verdef->vd_flags != 0)
+ Entry.Flags = Verdef->vd_flags;
+
+ if (Verdef->vd_ndx != 0)
+ Entry.VersionNdx = Verdef->vd_ndx;
+
+ if (Verdef->vd_hash != 0)
+ Entry.Hash = Verdef->vd_hash;
+
+ const uint8_t *BufAux = Buf + Verdef->vd_aux;
+ while (BufAux) {
+ const Elf_Verdaux *Verdaux =
+ reinterpret_cast<const Elf_Verdaux *>(BufAux);
+ Entry.VerNames.push_back(
+ StringTableOrErr->drop_front(Verdaux->vda_name).data());
+ BufAux = Verdaux->vda_next ? BufAux + Verdaux->vda_next : nullptr;
+ }
+
+ S->Entries->push_back(Entry);
+ Buf = Verdef->vd_next ? Buf + Verdef->vd_next : nullptr;
+ }
+
+ if (Shdr->sh_info != S->Entries->size())
+ S->Info = (llvm::yaml::Hex64)Shdr->sh_info;
+
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::SymverSection *>
+ELFDumper<ELFT>::dumpSymverSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::SymverSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto VersionsOrErr = Obj.template getSectionContentsAsArray<Elf_Half>(*Shdr);
+ if (!VersionsOrErr)
+ return VersionsOrErr.takeError();
+
+ S->Entries.emplace();
+ for (const Elf_Half &E : *VersionsOrErr)
+ S->Entries->push_back(E);
+
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::VerneedSection *>
+ELFDumper<ELFT>::dumpVerneedSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::VerneedSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto Contents = Obj.getSectionContents(*Shdr);
+ if (!Contents)
+ return Contents.takeError();
+
+ auto StringTableShdrOrErr = Obj.getSection(Shdr->sh_link);
+ if (!StringTableShdrOrErr)
+ return StringTableShdrOrErr.takeError();
+
+ auto StringTableOrErr = Obj.getStringTable(**StringTableShdrOrErr);
+ if (!StringTableOrErr)
+ return StringTableOrErr.takeError();
+
+ S->VerneedV.emplace();
+
+ llvm::ArrayRef<uint8_t> Data = *Contents;
+ const uint8_t *Buf = Data.data();
+ while (Buf) {
+ const Elf_Verneed *Verneed = reinterpret_cast<const Elf_Verneed *>(Buf);
+
+ ELFYAML::VerneedEntry Entry;
+ Entry.Version = Verneed->vn_version;
+ Entry.File =
+ StringRef(StringTableOrErr->drop_front(Verneed->vn_file).data());
+
+ const uint8_t *BufAux = Buf + Verneed->vn_aux;
+ while (BufAux) {
+ const Elf_Vernaux *Vernaux =
+ reinterpret_cast<const Elf_Vernaux *>(BufAux);
+
+ ELFYAML::VernauxEntry Aux;
+ Aux.Hash = Vernaux->vna_hash;
+ Aux.Flags = Vernaux->vna_flags;
+ Aux.Other = Vernaux->vna_other;
+ Aux.Name =
+ StringRef(StringTableOrErr->drop_front(Vernaux->vna_name).data());
+
+ Entry.AuxV.push_back(Aux);
+ BufAux = Vernaux->vna_next ? BufAux + Vernaux->vna_next : nullptr;
+ }
+
+ S->VerneedV->push_back(Entry);
+ Buf = Verneed->vn_next ? Buf + Verneed->vn_next : nullptr;
+ }
+
+ if (Shdr->sh_info != S->VerneedV->size())
+ S->Info = (llvm::yaml::Hex64)Shdr->sh_info;
+
+ return S.release();
+}
+
+template <class ELFT>
+Expected<StringRef> ELFDumper<ELFT>::getSymbolName(uint32_t SymtabNdx,
+ uint32_t SymbolNdx) {
+ auto SymtabOrErr = Obj.getSection(SymtabNdx);
+ if (!SymtabOrErr)
+ return SymtabOrErr.takeError();
+
+ const Elf_Shdr *Symtab = *SymtabOrErr;
+ auto SymOrErr = Obj.getSymbol(Symtab, SymbolNdx);
+ if (!SymOrErr)
+ return SymOrErr.takeError();
+
+ auto StrTabOrErr = Obj.getStringTableForSymtab(*Symtab);
+ if (!StrTabOrErr)
+ return StrTabOrErr.takeError();
+ return getUniquedSymbolName(*SymOrErr, *StrTabOrErr, Symtab);
+}
+
+template <class ELFT>
+Expected<ELFYAML::GroupSection *>
+ELFDumper<ELFT>::dumpGroupSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::GroupSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ // Get symbol with index sh_info. This symbol's name is the signature of the group.
+ Expected<StringRef> SymbolName = getSymbolName(Shdr->sh_link, Shdr->sh_info);
+ if (!SymbolName)
+ return SymbolName.takeError();
+ S->Signature = *SymbolName;
+
+ auto MembersOrErr = Obj.template getSectionContentsAsArray<Elf_Word>(*Shdr);
+ if (!MembersOrErr)
+ return MembersOrErr.takeError();
+
+ S->Members.emplace();
+ for (Elf_Word Member : *MembersOrErr) {
+ if (Member == llvm::ELF::GRP_COMDAT) {
+ S->Members->push_back({"GRP_COMDAT"});
+ continue;
+ }
+
+ Expected<const Elf_Shdr *> SHdrOrErr = Obj.getSection(Member);
+ if (!SHdrOrErr)
+ return SHdrOrErr.takeError();
+ Expected<StringRef> NameOrErr = getUniquedSectionName(**SHdrOrErr);
+ if (!NameOrErr)
+ return NameOrErr.takeError();
+ S->Members->push_back({*NameOrErr});
+ }
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::ARMIndexTableSection *>
+ELFDumper<ELFT>::dumpARMIndexTableSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::ARMIndexTableSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ Expected<ArrayRef<uint8_t>> ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ if (ContentOrErr->size() % (sizeof(Elf_Word) * 2) != 0) {
+ S->Content = yaml::BinaryRef(*ContentOrErr);
+ return S.release();
+ }
+
+ ArrayRef<Elf_Word> Words(
+ reinterpret_cast<const Elf_Word *>(ContentOrErr->data()),
+ ContentOrErr->size() / sizeof(Elf_Word));
+
+ S->Entries.emplace();
+ for (size_t I = 0, E = Words.size(); I != E; I += 2)
+ S->Entries->push_back({(yaml::Hex32)Words[I], (yaml::Hex32)Words[I + 1]});
+
+ return S.release();
+}
+
+template <class ELFT>
+Expected<ELFYAML::MipsABIFlags *>
+ELFDumper<ELFT>::dumpMipsABIFlags(const Elf_Shdr *Shdr) {
+ assert(Shdr->sh_type == ELF::SHT_MIPS_ABIFLAGS &&
+ "Section type is not SHT_MIPS_ABIFLAGS");
+ auto S = std::make_unique<ELFYAML::MipsABIFlags>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(*Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ auto *Flags = reinterpret_cast<const object::Elf_Mips_ABIFlags<ELFT> *>(
+ ContentOrErr.get().data());
+ S->Version = Flags->version;
+ S->ISALevel = Flags->isa_level;
+ S->ISARevision = Flags->isa_rev;
+ S->GPRSize = Flags->gpr_size;
+ S->CPR1Size = Flags->cpr1_size;
+ S->CPR2Size = Flags->cpr2_size;
+ S->FpABI = Flags->fp_abi;
+ S->ISAExtension = Flags->isa_ext;
+ S->ASEs = Flags->ases;
+ S->Flags1 = Flags->flags1;
+ S->Flags2 = Flags->flags2;
+ return S.release();
+}
+
+template <class ELFT>
+static Error elf2yaml(raw_ostream &Out, const object::ELFFile<ELFT> &Obj,
+ std::unique_ptr<DWARFContext> DWARFCtx) {
+ ELFDumper<ELFT> Dumper(Obj, std::move(DWARFCtx));
+ Expected<ELFYAML::Object *> YAMLOrErr = Dumper.dump();
+ if (!YAMLOrErr)
+ return YAMLOrErr.takeError();
+
+ std::unique_ptr<ELFYAML::Object> YAML(YAMLOrErr.get());
+ yaml::Output Yout(Out);
+ Yout << *YAML;
+
+ return Error::success();
+}
+
+Error elf2yaml(raw_ostream &Out, const object::ObjectFile &Obj) {
+ std::unique_ptr<DWARFContext> DWARFCtx = DWARFContext::create(Obj);
+ if (const auto *ELFObj = dyn_cast<object::ELF32LEObjectFile>(&Obj))
+ return elf2yaml(Out, ELFObj->getELFFile(), std::move(DWARFCtx));
+
+ if (const auto *ELFObj = dyn_cast<object::ELF32BEObjectFile>(&Obj))
+ return elf2yaml(Out, ELFObj->getELFFile(), std::move(DWARFCtx));
+
+ if (const auto *ELFObj = dyn_cast<object::ELF64LEObjectFile>(&Obj))
+ return elf2yaml(Out, ELFObj->getELFFile(), std::move(DWARFCtx));
+
+ if (const auto *ELFObj = dyn_cast<object::ELF64BEObjectFile>(&Obj))
+ return elf2yaml(Out, ELFObj->getELFFile(), std::move(DWARFCtx));
+
+ llvm_unreachable("unknown ELF file format");
+}
diff --git a/contrib/libs/llvm14/tools/obj2yaml/macho2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/macho2yaml.cpp
new file mode 100644
index 00000000000..a6339b2663e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/obj2yaml/macho2yaml.cpp
@@ -0,0 +1,672 @@
+//===------ macho2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "obj2yaml.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Object/MachOUniversal.h"
+#include "llvm/ObjectYAML/DWARFYAML.h"
+#include "llvm/ObjectYAML/ObjectYAML.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/LEB128.h"
+
+#include <string.h> // for memcpy
+
+using namespace llvm;
+
+class MachODumper {
+
+ template <typename StructType>
+ Expected<const char *> processLoadCommandData(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ MachOYAML::Object &Y);
+
+ const object::MachOObjectFile &Obj;
+ std::unique_ptr<DWARFContext> DWARFCtx;
+ unsigned RawSegment;
+ void dumpHeader(std::unique_ptr<MachOYAML::Object> &Y);
+ Error dumpLoadCommands(std::unique_ptr<MachOYAML::Object> &Y);
+ void dumpLinkEdit(std::unique_ptr<MachOYAML::Object> &Y);
+ void dumpRebaseOpcodes(std::unique_ptr<MachOYAML::Object> &Y);
+ void dumpBindOpcodes(std::vector<MachOYAML::BindOpcode> &BindOpcodes,
+ ArrayRef<uint8_t> OpcodeBuffer, bool Lazy = false);
+ void dumpExportTrie(std::unique_ptr<MachOYAML::Object> &Y);
+ void dumpSymbols(std::unique_ptr<MachOYAML::Object> &Y);
+ void dumpIndirectSymbols(std::unique_ptr<MachOYAML::Object> &Y);
+
+ template <typename SectionType>
+ Expected<MachOYAML::Section> constructSectionCommon(SectionType Sec,
+ size_t SecIndex);
+ template <typename SectionType>
+ Expected<MachOYAML::Section> constructSection(SectionType Sec,
+ size_t SecIndex);
+ template <typename SectionType, typename SegmentType>
+ Expected<const char *>
+ extractSections(const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ std::vector<MachOYAML::Section> &Sections,
+ MachOYAML::Object &Y);
+
+public:
+ MachODumper(const object::MachOObjectFile &O,
+ std::unique_ptr<DWARFContext> DCtx, unsigned RawSegments)
+ : Obj(O), DWARFCtx(std::move(DCtx)), RawSegment(RawSegments) {}
+ Expected<std::unique_ptr<MachOYAML::Object>> dump();
+};
+
+#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
+ case MachO::LCName: \
+ memcpy((void *)&(LC.Data.LCStruct##_data), LoadCmd.Ptr, \
+ sizeof(MachO::LCStruct)); \
+ if (Obj.isLittleEndian() != sys::IsLittleEndianHost) \
+ MachO::swapStruct(LC.Data.LCStruct##_data); \
+ if (Expected<const char *> ExpectedEndPtr = \
+ processLoadCommandData<MachO::LCStruct>(LC, LoadCmd, *Y.get())) \
+ EndPtr = *ExpectedEndPtr; \
+ else \
+ return ExpectedEndPtr.takeError(); \
+ break;
+
+template <typename SectionType>
+Expected<MachOYAML::Section>
+MachODumper::constructSectionCommon(SectionType Sec, size_t SecIndex) {
+ MachOYAML::Section TempSec;
+ memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16);
+ memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16);
+ TempSec.addr = Sec.addr;
+ TempSec.size = Sec.size;
+ TempSec.offset = Sec.offset;
+ TempSec.align = Sec.align;
+ TempSec.reloff = Sec.reloff;
+ TempSec.nreloc = Sec.nreloc;
+ TempSec.flags = Sec.flags;
+ TempSec.reserved1 = Sec.reserved1;
+ TempSec.reserved2 = Sec.reserved2;
+ TempSec.reserved3 = 0;
+ if (!MachO::isVirtualSection(Sec.flags & MachO::SECTION_TYPE))
+ TempSec.content =
+ yaml::BinaryRef(Obj.getSectionContents(Sec.offset, Sec.size));
+
+ if (Expected<object::SectionRef> SecRef = Obj.getSection(SecIndex)) {
+ TempSec.relocations.reserve(TempSec.nreloc);
+ for (const object::RelocationRef &Reloc : SecRef->relocations()) {
+ const object::DataRefImpl Rel = Reloc.getRawDataRefImpl();
+ const MachO::any_relocation_info RE = Obj.getRelocation(Rel);
+ MachOYAML::Relocation R;
+ R.address = Obj.getAnyRelocationAddress(RE);
+ R.is_pcrel = Obj.getAnyRelocationPCRel(RE);
+ R.length = Obj.getAnyRelocationLength(RE);
+ R.type = Obj.getAnyRelocationType(RE);
+ R.is_scattered = Obj.isRelocationScattered(RE);
+ R.symbolnum = (R.is_scattered ? 0 : Obj.getPlainRelocationSymbolNum(RE));
+ R.is_extern =
+ (R.is_scattered ? false : Obj.getPlainRelocationExternal(RE));
+ R.value = (R.is_scattered ? Obj.getScatteredRelocationValue(RE) : 0);
+ TempSec.relocations.push_back(R);
+ }
+ } else {
+ return SecRef.takeError();
+ }
+ return TempSec;
+}
+
+template <>
+Expected<MachOYAML::Section> MachODumper::constructSection(MachO::section Sec,
+ size_t SecIndex) {
+ Expected<MachOYAML::Section> TempSec = constructSectionCommon(Sec, SecIndex);
+ if (TempSec)
+ TempSec->reserved3 = 0;
+ return TempSec;
+}
+
+template <>
+Expected<MachOYAML::Section>
+MachODumper::constructSection(MachO::section_64 Sec, size_t SecIndex) {
+ Expected<MachOYAML::Section> TempSec = constructSectionCommon(Sec, SecIndex);
+ if (TempSec)
+ TempSec->reserved3 = Sec.reserved3;
+ return TempSec;
+}
+
+static Error dumpDebugSection(StringRef SecName, DWARFContext &DCtx,
+ DWARFYAML::Data &DWARF) {
+ if (SecName == "__debug_abbrev") {
+ dumpDebugAbbrev(DCtx, DWARF);
+ return Error::success();
+ }
+ if (SecName == "__debug_aranges")
+ return dumpDebugARanges(DCtx, DWARF);
+ if (SecName == "__debug_info") {
+ dumpDebugInfo(DCtx, DWARF);
+ return Error::success();
+ }
+ if (SecName == "__debug_line") {
+ dumpDebugLines(DCtx, DWARF);
+ return Error::success();
+ }
+ if (SecName.startswith("__debug_pub")) {
+ // FIXME: We should extract pub-section dumpers from this function.
+ dumpDebugPubSections(DCtx, DWARF);
+ return Error::success();
+ }
+ if (SecName == "__debug_ranges")
+ return dumpDebugRanges(DCtx, DWARF);
+ if (SecName == "__debug_str")
+ return dumpDebugStrings(DCtx, DWARF);
+ return createStringError(errc::not_supported,
+ "dumping " + SecName + " section is not supported");
+}
+
+template <typename SectionType, typename SegmentType>
+Expected<const char *> MachODumper::extractSections(
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ std::vector<MachOYAML::Section> &Sections, MachOYAML::Object &Y) {
+ auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize;
+ const SectionType *Curr =
+ reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType));
+ for (; reinterpret_cast<const void *>(Curr) < End; Curr++) {
+ SectionType Sec;
+ memcpy((void *)&Sec, Curr, sizeof(SectionType));
+ if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
+ MachO::swapStruct(Sec);
+ // For MachO section indices start from 1.
+ if (Expected<MachOYAML::Section> S =
+ constructSection(Sec, Sections.size() + 1)) {
+ StringRef SecName(S->sectname);
+
+ // Copy data sections if requested.
+ if ((RawSegment & ::RawSegments::data) &&
+ StringRef(S->segname).startswith("__DATA"))
+ S->content =
+ yaml::BinaryRef(Obj.getSectionContents(Sec.offset, Sec.size));
+
+ if (SecName.startswith("__debug_")) {
+ // If the DWARF section cannot be successfully parsed, emit raw content
+ // instead of an entry in the DWARF section of the YAML.
+ if (Error Err = dumpDebugSection(SecName, *DWARFCtx.get(), Y.DWARF))
+ consumeError(std::move(Err));
+ else
+ S->content.reset();
+ }
+ Sections.push_back(std::move(*S));
+ } else
+ return S.takeError();
+ }
+ return reinterpret_cast<const char *>(Curr);
+}
+
+template <typename StructType>
+Expected<const char *> MachODumper::processLoadCommandData(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ MachOYAML::Object &Y) {
+ return LoadCmd.Ptr + sizeof(StructType);
+}
+
+template <>
+Expected<const char *>
+MachODumper::processLoadCommandData<MachO::segment_command>(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ MachOYAML::Object &Y) {
+ return extractSections<MachO::section, MachO::segment_command>(
+ LoadCmd, LC.Sections, Y);
+}
+
+template <>
+Expected<const char *>
+MachODumper::processLoadCommandData<MachO::segment_command_64>(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ MachOYAML::Object &Y) {
+ return extractSections<MachO::section_64, MachO::segment_command_64>(
+ LoadCmd, LC.Sections, Y);
+}
+
+template <typename StructType>
+const char *
+readString(MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd) {
+ auto Start = LoadCmd.Ptr + sizeof(StructType);
+ auto MaxSize = LoadCmd.C.cmdsize - sizeof(StructType);
+ auto Size = strnlen(Start, MaxSize);
+ LC.Content = StringRef(Start, Size).str();
+ return Start + Size;
+}
+
+template <>
+Expected<const char *>
+MachODumper::processLoadCommandData<MachO::dylib_command>(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ MachOYAML::Object &Y) {
+ return readString<MachO::dylib_command>(LC, LoadCmd);
+}
+
+template <>
+Expected<const char *>
+MachODumper::processLoadCommandData<MachO::dylinker_command>(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ MachOYAML::Object &Y) {
+ return readString<MachO::dylinker_command>(LC, LoadCmd);
+}
+
+template <>
+Expected<const char *>
+MachODumper::processLoadCommandData<MachO::rpath_command>(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ MachOYAML::Object &Y) {
+ return readString<MachO::rpath_command>(LC, LoadCmd);
+}
+
+template <>
+Expected<const char *>
+MachODumper::processLoadCommandData<MachO::build_version_command>(
+ MachOYAML::LoadCommand &LC,
+ const llvm::object::MachOObjectFile::LoadCommandInfo &LoadCmd,
+ MachOYAML::Object &Y) {
+ auto Start = LoadCmd.Ptr + sizeof(MachO::build_version_command);
+ auto NTools = LC.Data.build_version_command_data.ntools;
+ for (unsigned i = 0; i < NTools; ++i) {
+ auto Curr = Start + i * sizeof(MachO::build_tool_version);
+ MachO::build_tool_version BV;
+ memcpy((void *)&BV, Curr, sizeof(MachO::build_tool_version));
+ if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
+ MachO::swapStruct(BV);
+ LC.Tools.push_back(BV);
+ }
+ return Start + NTools * sizeof(MachO::build_tool_version);
+}
+
+Expected<std::unique_ptr<MachOYAML::Object>> MachODumper::dump() {
+ auto Y = std::make_unique<MachOYAML::Object>();
+ Y->IsLittleEndian = Obj.isLittleEndian();
+ dumpHeader(Y);
+ if (Error Err = dumpLoadCommands(Y))
+ return std::move(Err);
+ if (RawSegment & ::RawSegments::linkedit)
+ Y->RawLinkEditSegment =
+ yaml::BinaryRef(Obj.getSegmentContents("__LINKEDIT"));
+ else
+ dumpLinkEdit(Y);
+
+ return std::move(Y);
+}
+
+void MachODumper::dumpHeader(std::unique_ptr<MachOYAML::Object> &Y) {
+ Y->Header.magic = Obj.getHeader().magic;
+ Y->Header.cputype = Obj.getHeader().cputype;
+ Y->Header.cpusubtype = Obj.getHeader().cpusubtype;
+ Y->Header.filetype = Obj.getHeader().filetype;
+ Y->Header.ncmds = Obj.getHeader().ncmds;
+ Y->Header.sizeofcmds = Obj.getHeader().sizeofcmds;
+ Y->Header.flags = Obj.getHeader().flags;
+ Y->Header.reserved = 0;
+}
+
+Error MachODumper::dumpLoadCommands(std::unique_ptr<MachOYAML::Object> &Y) {
+ for (auto LoadCmd : Obj.load_commands()) {
+ MachOYAML::LoadCommand LC;
+ const char *EndPtr = LoadCmd.Ptr;
+ switch (LoadCmd.C.cmd) {
+ default:
+ memcpy((void *)&(LC.Data.load_command_data), LoadCmd.Ptr,
+ sizeof(MachO::load_command));
+ if (Obj.isLittleEndian() != sys::IsLittleEndianHost)
+ MachO::swapStruct(LC.Data.load_command_data);
+ if (Expected<const char *> ExpectedEndPtr =
+ processLoadCommandData<MachO::load_command>(LC, LoadCmd,
+ *Y.get()))
+ EndPtr = *ExpectedEndPtr;
+ else
+ return ExpectedEndPtr.takeError();
+ break;
+#include "llvm/BinaryFormat/MachO.def"
+ }
+ auto RemainingBytes = LoadCmd.C.cmdsize - (EndPtr - LoadCmd.Ptr);
+ if (!std::all_of(EndPtr, &EndPtr[RemainingBytes],
+ [](const char C) { return C == 0; })) {
+ LC.PayloadBytes.insert(LC.PayloadBytes.end(), EndPtr,
+ &EndPtr[RemainingBytes]);
+ RemainingBytes = 0;
+ }
+ LC.ZeroPadBytes = RemainingBytes;
+ Y->LoadCommands.push_back(std::move(LC));
+ }
+ return Error::success();
+}
+
+void MachODumper::dumpLinkEdit(std::unique_ptr<MachOYAML::Object> &Y) {
+ dumpRebaseOpcodes(Y);
+ dumpBindOpcodes(Y->LinkEdit.BindOpcodes, Obj.getDyldInfoBindOpcodes());
+ dumpBindOpcodes(Y->LinkEdit.WeakBindOpcodes,
+ Obj.getDyldInfoWeakBindOpcodes());
+ dumpBindOpcodes(Y->LinkEdit.LazyBindOpcodes, Obj.getDyldInfoLazyBindOpcodes(),
+ true);
+ dumpExportTrie(Y);
+ dumpSymbols(Y);
+ dumpIndirectSymbols(Y);
+}
+
+void MachODumper::dumpRebaseOpcodes(std::unique_ptr<MachOYAML::Object> &Y) {
+ MachOYAML::LinkEditData &LEData = Y->LinkEdit;
+
+ auto RebaseOpcodes = Obj.getDyldInfoRebaseOpcodes();
+ for (auto OpCode = RebaseOpcodes.begin(); OpCode != RebaseOpcodes.end();
+ ++OpCode) {
+ MachOYAML::RebaseOpcode RebaseOp;
+ RebaseOp.Opcode =
+ static_cast<MachO::RebaseOpcode>(*OpCode & MachO::REBASE_OPCODE_MASK);
+ RebaseOp.Imm = *OpCode & MachO::REBASE_IMMEDIATE_MASK;
+
+ unsigned Count;
+ uint64_t ULEB = 0;
+
+ switch (RebaseOp.Opcode) {
+ case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
+
+ ULEB = decodeULEB128(OpCode + 1, &Count);
+ RebaseOp.ExtraData.push_back(ULEB);
+ OpCode += Count;
+ LLVM_FALLTHROUGH;
+ // Intentionally no break here -- This opcode has two ULEB values
+ case MachO::REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ case MachO::REBASE_OPCODE_ADD_ADDR_ULEB:
+ case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+ case MachO::REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+
+ ULEB = decodeULEB128(OpCode + 1, &Count);
+ RebaseOp.ExtraData.push_back(ULEB);
+ OpCode += Count;
+ break;
+ default:
+ break;
+ }
+
+ LEData.RebaseOpcodes.push_back(RebaseOp);
+
+ if (RebaseOp.Opcode == MachO::REBASE_OPCODE_DONE)
+ break;
+ }
+}
+
+StringRef ReadStringRef(const uint8_t *Start) {
+ const uint8_t *Itr = Start;
+ for (; *Itr; ++Itr)
+ ;
+ return StringRef(reinterpret_cast<const char *>(Start), Itr - Start);
+}
+
+void MachODumper::dumpBindOpcodes(
+ std::vector<MachOYAML::BindOpcode> &BindOpcodes,
+ ArrayRef<uint8_t> OpcodeBuffer, bool Lazy) {
+ for (auto OpCode = OpcodeBuffer.begin(); OpCode != OpcodeBuffer.end();
+ ++OpCode) {
+ MachOYAML::BindOpcode BindOp;
+ BindOp.Opcode =
+ static_cast<MachO::BindOpcode>(*OpCode & MachO::BIND_OPCODE_MASK);
+ BindOp.Imm = *OpCode & MachO::BIND_IMMEDIATE_MASK;
+
+ unsigned Count;
+ uint64_t ULEB = 0;
+ int64_t SLEB = 0;
+
+ switch (BindOp.Opcode) {
+ case MachO::BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ ULEB = decodeULEB128(OpCode + 1, &Count);
+ BindOp.ULEBExtraData.push_back(ULEB);
+ OpCode += Count;
+ LLVM_FALLTHROUGH;
+ // Intentionally no break here -- this opcode has two ULEB values
+
+ case MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ case MachO::BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ case MachO::BIND_OPCODE_ADD_ADDR_ULEB:
+ case MachO::BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ ULEB = decodeULEB128(OpCode + 1, &Count);
+ BindOp.ULEBExtraData.push_back(ULEB);
+ OpCode += Count;
+ break;
+
+ case MachO::BIND_OPCODE_SET_ADDEND_SLEB:
+ SLEB = decodeSLEB128(OpCode + 1, &Count);
+ BindOp.SLEBExtraData.push_back(SLEB);
+ OpCode += Count;
+ break;
+
+ case MachO::BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ BindOp.Symbol = ReadStringRef(OpCode + 1);
+ OpCode += BindOp.Symbol.size() + 1;
+ break;
+ default:
+ break;
+ }
+
+ BindOpcodes.push_back(BindOp);
+
+ // Lazy bindings have DONE opcodes between operations, so we need to keep
+ // processing after a DONE.
+ if (!Lazy && BindOp.Opcode == MachO::BIND_OPCODE_DONE)
+ break;
+ }
+}
+
+/*!
+ * /brief processes a node from the export trie, and its children.
+ *
+ * To my knowledge there is no documentation of the encoded format of this data
+ * other than in the heads of the Apple linker engineers. To that end hopefully
+ * this comment and the implementation below can serve to light the way for
+ * anyone crazy enough to come down this path in the future.
+ *
+ * This function reads and preserves the trie structure of the export trie. To
+ * my knowledge there is no code anywhere else that reads the data and preserves
+ * the Trie. LD64 (sources available at opensource.apple.com) has a similar
+ * implementation that parses the export trie into a vector. That code as well
+ * as LLVM's libObject MachO implementation were the basis for this.
+ *
+ * The export trie is an encoded trie. The node serialization is a bit awkward.
+ * The below pseudo-code is the best description I've come up with for it.
+ *
+ * struct SerializedNode {
+ * ULEB128 TerminalSize;
+ * struct TerminalData { <-- This is only present if TerminalSize > 0
+ * ULEB128 Flags;
+ * ULEB128 Address; <-- Present if (! Flags & REEXPORT )
+ * ULEB128 Other; <-- Present if ( Flags & REEXPORT ||
+ * Flags & STUB_AND_RESOLVER )
+ * char[] ImportName; <-- Present if ( Flags & REEXPORT )
+ * }
+ * uint8_t ChildrenCount;
+ * Pair<char[], ULEB128> ChildNameOffsetPair[ChildrenCount];
+ * SerializedNode Children[ChildrenCount]
+ * }
+ *
+ * Terminal nodes are nodes that represent actual exports. They can appear
+ * anywhere in the tree other than at the root; they do not need to be leaf
+ * nodes. When reading the data out of the trie this routine reads it in-order,
+ * but it puts the child names and offsets directly into the child nodes. This
+ * results in looping over the children twice during serialization and
+ * de-serialization, but it makes the YAML representation more human readable.
+ *
+ * Below is an example of the graph from a "Hello World" executable:
+ *
+ * -------
+ * | '' |
+ * -------
+ * |
+ * -------
+ * | '_' |
+ * -------
+ * |
+ * |----------------------------------------|
+ * | |
+ * ------------------------ ---------------------
+ * | '_mh_execute_header' | | 'main' |
+ * | Flags: 0x00000000 | | Flags: 0x00000000 |
+ * | Addr: 0x00000000 | | Addr: 0x00001160 |
+ * ------------------------ ---------------------
+ *
+ * This graph represents the trie for the exports "__mh_execute_header" and
+ * "_main". In the graph only the "_main" and "__mh_execute_header" nodes are
+ * terminal.
+*/
+
+const uint8_t *processExportNode(const uint8_t *CurrPtr,
+ const uint8_t *const End,
+ MachOYAML::ExportEntry &Entry) {
+ if (CurrPtr >= End)
+ return CurrPtr;
+ unsigned Count = 0;
+ Entry.TerminalSize = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ if (Entry.TerminalSize != 0) {
+ Entry.Flags = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) {
+ Entry.Address = 0;
+ Entry.Other = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ Entry.ImportName = std::string(reinterpret_cast<const char *>(CurrPtr));
+ } else {
+ Entry.Address = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) {
+ Entry.Other = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ } else
+ Entry.Other = 0;
+ }
+ }
+ uint8_t childrenCount = *CurrPtr++;
+ if (childrenCount == 0)
+ return CurrPtr;
+
+ Entry.Children.insert(Entry.Children.begin(), (size_t)childrenCount,
+ MachOYAML::ExportEntry());
+ for (auto &Child : Entry.Children) {
+ Child.Name = std::string(reinterpret_cast<const char *>(CurrPtr));
+ CurrPtr += Child.Name.length() + 1;
+ Child.NodeOffset = decodeULEB128(CurrPtr, &Count);
+ CurrPtr += Count;
+ }
+ for (auto &Child : Entry.Children) {
+ CurrPtr = processExportNode(CurrPtr, End, Child);
+ }
+ return CurrPtr;
+}
+
+void MachODumper::dumpExportTrie(std::unique_ptr<MachOYAML::Object> &Y) {
+ MachOYAML::LinkEditData &LEData = Y->LinkEdit;
+ auto ExportsTrie = Obj.getDyldInfoExportsTrie();
+ processExportNode(ExportsTrie.begin(), ExportsTrie.end(), LEData.ExportTrie);
+}
+
+template <typename nlist_t>
+MachOYAML::NListEntry constructNameList(const nlist_t &nlist) {
+ MachOYAML::NListEntry NL;
+ NL.n_strx = nlist.n_strx;
+ NL.n_type = nlist.n_type;
+ NL.n_sect = nlist.n_sect;
+ NL.n_desc = nlist.n_desc;
+ NL.n_value = nlist.n_value;
+ return NL;
+}
+
+void MachODumper::dumpSymbols(std::unique_ptr<MachOYAML::Object> &Y) {
+ MachOYAML::LinkEditData &LEData = Y->LinkEdit;
+
+ for (auto Symbol : Obj.symbols()) {
+ MachOYAML::NListEntry NLE =
+ Obj.is64Bit()
+ ? constructNameList<MachO::nlist_64>(
+ Obj.getSymbol64TableEntry(Symbol.getRawDataRefImpl()))
+ : constructNameList<MachO::nlist>(
+ Obj.getSymbolTableEntry(Symbol.getRawDataRefImpl()));
+ LEData.NameList.push_back(NLE);
+ }
+
+ StringRef RemainingTable = Obj.getStringTableData();
+ while (RemainingTable.size() > 0) {
+ auto SymbolPair = RemainingTable.split('\0');
+ RemainingTable = SymbolPair.second;
+ LEData.StringTable.push_back(SymbolPair.first);
+ }
+}
+
+void MachODumper::dumpIndirectSymbols(std::unique_ptr<MachOYAML::Object> &Y) {
+ MachOYAML::LinkEditData &LEData = Y->LinkEdit;
+
+ MachO::dysymtab_command DLC = Obj.getDysymtabLoadCommand();
+ for (unsigned i = 0; i < DLC.nindirectsyms; ++i)
+ LEData.IndirectSymbols.push_back(Obj.getIndirectSymbolTableEntry(DLC, i));
+}
+
+Error macho2yaml(raw_ostream &Out, const object::MachOObjectFile &Obj,
+ unsigned RawSegments) {
+ std::unique_ptr<DWARFContext> DCtx = DWARFContext::create(Obj);
+ MachODumper Dumper(Obj, std::move(DCtx), RawSegments);
+ Expected<std::unique_ptr<MachOYAML::Object>> YAML = Dumper.dump();
+ if (!YAML)
+ return YAML.takeError();
+
+ yaml::YamlObjectFile YAMLFile;
+ YAMLFile.MachO = std::move(YAML.get());
+
+ yaml::Output Yout(Out);
+ Yout << YAMLFile;
+ return Error::success();
+}
+
+Error macho2yaml(raw_ostream &Out, const object::MachOUniversalBinary &Obj,
+ unsigned RawSegments) {
+ yaml::YamlObjectFile YAMLFile;
+ YAMLFile.FatMachO.reset(new MachOYAML::UniversalBinary());
+ MachOYAML::UniversalBinary &YAML = *YAMLFile.FatMachO;
+ YAML.Header.magic = Obj.getMagic();
+ YAML.Header.nfat_arch = Obj.getNumberOfObjects();
+
+ for (auto Slice : Obj.objects()) {
+ MachOYAML::FatArch arch;
+ arch.cputype = Slice.getCPUType();
+ arch.cpusubtype = Slice.getCPUSubType();
+ arch.offset = Slice.getOffset();
+ arch.size = Slice.getSize();
+ arch.align = Slice.getAlign();
+ arch.reserved = Slice.getReserved();
+ YAML.FatArchs.push_back(arch);
+
+ auto SliceObj = Slice.getAsObjectFile();
+ if (!SliceObj)
+ return SliceObj.takeError();
+
+ std::unique_ptr<DWARFContext> DCtx = DWARFContext::create(*SliceObj.get());
+ MachODumper Dumper(*SliceObj.get(), std::move(DCtx), RawSegments);
+ Expected<std::unique_ptr<MachOYAML::Object>> YAMLObj = Dumper.dump();
+ if (!YAMLObj)
+ return YAMLObj.takeError();
+ YAML.Slices.push_back(*YAMLObj.get());
+ }
+
+ yaml::Output Yout(Out);
+ Yout << YAML;
+ return Error::success();
+}
+
+Error macho2yaml(raw_ostream &Out, const object::Binary &Binary,
+ unsigned RawSegments) {
+ if (const auto *MachOObj = dyn_cast<object::MachOUniversalBinary>(&Binary))
+ return macho2yaml(Out, *MachOObj, RawSegments);
+
+ if (const auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Binary))
+ return macho2yaml(Out, *MachOObj, RawSegments);
+
+ llvm_unreachable("unexpected Mach-O file format");
+}
diff --git a/contrib/libs/llvm14/tools/obj2yaml/minidump2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/minidump2yaml.cpp
new file mode 100644
index 00000000000..7b25b1869c6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/obj2yaml/minidump2yaml.cpp
@@ -0,0 +1,23 @@
+//===- minidump2yaml.cpp - Minidump to yaml conversion tool -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "obj2yaml.h"
+#include "llvm/Object/Minidump.h"
+#include "llvm/ObjectYAML/MinidumpYAML.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace llvm;
+
+Error minidump2yaml(raw_ostream &Out, const object::MinidumpFile &Obj) {
+ auto ExpectedObject = MinidumpYAML::Object::create(Obj);
+ if (!ExpectedObject)
+ return ExpectedObject.takeError();
+ yaml::Output Output(Out);
+ Output << *ExpectedObject;
+ return llvm::Error::success();
+}
diff --git a/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.cpp
new file mode 100644
index 00000000000..9c7a3385850
--- /dev/null
+++ b/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.cpp
@@ -0,0 +1,95 @@
+//===------ utils/obj2yaml.cpp - obj2yaml conversion tool -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "obj2yaml.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/Minidump.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/InitLLVM.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+static cl::opt<std::string>
+ InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));
+static cl::bits<RawSegments> RawSegment(
+ "raw-segment",
+ cl::desc("Mach-O: dump the raw contents of the listed segments instead of "
+ "parsing them:"),
+ cl::values(clEnumVal(data, "__DATA"), clEnumVal(linkedit, "__LINKEDIT")));
+
+static Error dumpObject(const ObjectFile &Obj) {
+ if (Obj.isCOFF())
+ return errorCodeToError(coff2yaml(outs(), cast<COFFObjectFile>(Obj)));
+
+ if (Obj.isXCOFF())
+ return xcoff2yaml(outs(), cast<XCOFFObjectFile>(Obj));
+
+ if (Obj.isELF())
+ return elf2yaml(outs(), Obj);
+
+ if (Obj.isWasm())
+ return errorCodeToError(wasm2yaml(outs(), cast<WasmObjectFile>(Obj)));
+
+ llvm_unreachable("unexpected object file format");
+}
+
+static Error dumpInput(StringRef File) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
+ MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
+ if (std::error_code EC = FileOrErr.getError())
+ return errorCodeToError(EC);
+ std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
+ MemoryBufferRef MemBuf = Buffer->getMemBufferRef();
+ if (file_magic::archive == identify_magic(MemBuf.getBuffer()))
+ return archive2yaml(outs(), MemBuf);
+
+ Expected<std::unique_ptr<Binary>> BinOrErr =
+ createBinary(MemBuf, /*Context=*/nullptr);
+ if (!BinOrErr)
+ return BinOrErr.takeError();
+
+ Binary &Binary = *BinOrErr->get();
+ // Universal MachO is not a subclass of ObjectFile, so it needs to be handled
+ // here with the other binary types.
+ if (Binary.isMachO() || Binary.isMachOUniversalBinary())
+ return macho2yaml(outs(), Binary, RawSegment.getBits());
+ if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary))
+ return dumpObject(*Obj);
+ if (MinidumpFile *Minidump = dyn_cast<MinidumpFile>(&Binary))
+ return minidump2yaml(outs(), *Minidump);
+
+ return Error::success();
+}
+
+static void reportError(StringRef Input, Error Err) {
+ if (Input == "-")
+ Input = "<stdin>";
+ std::string ErrMsg;
+ raw_string_ostream OS(ErrMsg);
+ logAllUnhandledErrors(std::move(Err), OS);
+ OS.flush();
+ errs() << "Error reading file: " << Input << ": " << ErrMsg;
+ errs().flush();
+}
+
+int main(int argc, char *argv[]) {
+ InitLLVM X(argc, argv);
+ cl::ParseCommandLineOptions(argc, argv);
+
+ if (Error Err = dumpInput(InputFilename)) {
+ reportError(InputFilename, std::move(Err));
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.h b/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.h
new file mode 100644
index 00000000000..c026482eaf0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/obj2yaml/obj2yaml.h
@@ -0,0 +1,57 @@
+//===------ utils/obj2yaml.hpp - obj2yaml conversion tool -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// This file declares some helper routines, and also the format-specific
+// writers. To add a new format, add the declaration here, and, in a separate
+// source file, implement it.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJ2YAML_OBJ2YAML_H
+#define LLVM_TOOLS_OBJ2YAML_OBJ2YAML_H
+
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/Minidump.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Object/XCOFFObjectFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/MemoryBufferRef.h"
+#include <system_error>
+
+enum RawSegments : unsigned { none = 0, data = 1, linkedit = 1 << 1 };
+std::error_code coff2yaml(llvm::raw_ostream &Out,
+ const llvm::object::COFFObjectFile &Obj);
+llvm::Error elf2yaml(llvm::raw_ostream &Out,
+ const llvm::object::ObjectFile &Obj);
+llvm::Error macho2yaml(llvm::raw_ostream &Out, const llvm::object::Binary &Obj,
+ unsigned RawSegments);
+llvm::Error minidump2yaml(llvm::raw_ostream &Out,
+ const llvm::object::MinidumpFile &Obj);
+llvm::Error xcoff2yaml(llvm::raw_ostream &Out,
+ const llvm::object::XCOFFObjectFile &Obj);
+std::error_code wasm2yaml(llvm::raw_ostream &Out,
+ const llvm::object::WasmObjectFile &Obj);
+llvm::Error archive2yaml(llvm::raw_ostream &Out, llvm::MemoryBufferRef Source);
+
+// Forward decls for dwarf2yaml
+namespace llvm {
+class DWARFContext;
+namespace DWARFYAML {
+struct Data;
+}
+}
+
+void dumpDebugAbbrev(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y);
+llvm::Error dumpDebugAddr(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y);
+llvm::Error dumpDebugARanges(llvm::DWARFContext &DCtx,
+ llvm::DWARFYAML::Data &Y);
+void dumpDebugPubSections(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y);
+void dumpDebugInfo(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y);
+void dumpDebugLines(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y);
+llvm::Error dumpDebugRanges(llvm::DWARFContext &DCtx, llvm::DWARFYAML::Data &Y);
+llvm::Error dumpDebugStrings(llvm::DWARFContext &DCtx,
+ llvm::DWARFYAML::Data &Y);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/obj2yaml/wasm2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/wasm2yaml.cpp
new file mode 100644
index 00000000000..e4a56524e36
--- /dev/null
+++ b/contrib/libs/llvm14/tools/obj2yaml/wasm2yaml.cpp
@@ -0,0 +1,405 @@
+//===------ utils/wasm2yaml.cpp - obj2yaml conversion tool ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "obj2yaml.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/ObjectYAML/WasmYAML.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace llvm;
+using object::WasmSection;
+
+namespace {
+
+class WasmDumper {
+ const object::WasmObjectFile &Obj;
+
+public:
+ WasmDumper(const object::WasmObjectFile &O) : Obj(O) {}
+
+ ErrorOr<WasmYAML::Object *> dump();
+
+ std::unique_ptr<WasmYAML::CustomSection>
+ dumpCustomSection(const WasmSection &WasmSec);
+};
+
+} // namespace
+
+static WasmYAML::Limits makeLimits(const wasm::WasmLimits &Limits) {
+ WasmYAML::Limits L;
+ L.Flags = Limits.Flags;
+ L.Minimum = Limits.Minimum;
+ L.Maximum = Limits.Maximum;
+ return L;
+}
+
+static WasmYAML::Table makeTable(uint32_t Index,
+ const wasm::WasmTableType &Type) {
+ WasmYAML::Table T;
+ T.Index = Index;
+ T.ElemType = Type.ElemType;
+ T.TableLimits = makeLimits(Type.Limits);
+ return T;
+}
+
+std::unique_ptr<WasmYAML::CustomSection>
+WasmDumper::dumpCustomSection(const WasmSection &WasmSec) {
+ std::unique_ptr<WasmYAML::CustomSection> CustomSec;
+ if (WasmSec.Name == "dylink" || WasmSec.Name == "dylink.0") {
+ std::unique_ptr<WasmYAML::DylinkSection> DylinkSec =
+ std::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;
+ for (const auto &Imp : Info.ImportInfo)
+ DylinkSec->ImportInfo.push_back({Imp.Module, Imp.Field, Imp.Flags});
+ for (const auto &Exp : Info.ExportInfo)
+ DylinkSec->ExportInfo.push_back({Exp.Name, Exp.Flags});
+ CustomSec = std::move(DylinkSec);
+ } else if (WasmSec.Name == "name") {
+ std::unique_ptr<WasmYAML::NameSection> NameSec =
+ std::make_unique<WasmYAML::NameSection>();
+ for (const llvm::wasm::WasmDebugName &Name : Obj.debugNames()) {
+ WasmYAML::NameEntry NameEntry;
+ NameEntry.Name = Name.Name;
+ NameEntry.Index = Name.Index;
+ if (Name.Type == llvm::wasm::NameType::FUNCTION) {
+ NameSec->FunctionNames.push_back(NameEntry);
+ } else if (Name.Type == llvm::wasm::NameType::GLOBAL) {
+ NameSec->GlobalNames.push_back(NameEntry);
+ } else {
+ assert(Name.Type == llvm::wasm::NameType::DATA_SEGMENT);
+ NameSec->DataSegmentNames.push_back(NameEntry);
+ }
+ }
+ CustomSec = std::move(NameSec);
+ } else if (WasmSec.Name == "linking") {
+ std::unique_ptr<WasmYAML::LinkingSection> LinkingSec =
+ std::make_unique<WasmYAML::LinkingSection>();
+ LinkingSec->Version = Obj.linkingData().Version;
+
+ ArrayRef<StringRef> Comdats = Obj.linkingData().Comdats;
+ for (StringRef ComdatName : Comdats)
+ LinkingSec->Comdats.emplace_back(WasmYAML::Comdat{ComdatName, {}});
+ 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});
+ }
+ }
+
+ uint32_t SegmentIndex = 0;
+ for (const object::WasmSegment &Segment : Obj.dataSegments()) {
+ if (!Segment.Data.Name.empty()) {
+ WasmYAML::SegmentInfo SegmentInfo;
+ SegmentInfo.Name = Segment.Data.Name;
+ SegmentInfo.Index = SegmentIndex;
+ SegmentInfo.Alignment = Segment.Data.Alignment;
+ SegmentInfo.Flags = Segment.Data.LinkingFlags;
+ LinkingSec->SegmentInfos.push_back(SegmentInfo);
+ }
+ if (Segment.Data.Comdat != UINT32_MAX) {
+ LinkingSec->Comdats[Segment.Data.Comdat].Entries.emplace_back(
+ WasmYAML::ComdatEntry{wasm::WASM_COMDAT_DATA, SegmentIndex});
+ }
+ SegmentIndex++;
+ }
+ uint32_t SectionIndex = 0;
+ for (const auto &Sec : Obj.sections()) {
+ const WasmSection &WasmSec = Obj.getWasmSection(Sec);
+ if (WasmSec.Comdat != UINT32_MAX)
+ LinkingSec->Comdats[WasmSec.Comdat].Entries.emplace_back(
+ WasmYAML::ComdatEntry{wasm::WASM_COMDAT_SECTION, SectionIndex});
+ SectionIndex++;
+ }
+
+ uint32_t SymbolIndex = 0;
+ for (const wasm::WasmSymbolInfo &Symbol : Obj.linkingData().SymbolTable) {
+ WasmYAML::SymbolInfo Info;
+ Info.Index = SymbolIndex++;
+ Info.Kind = static_cast<uint32_t>(Symbol.Kind);
+ Info.Name = Symbol.Name;
+ Info.Flags = Symbol.Flags;
+ switch (Symbol.Kind) {
+ case wasm::WASM_SYMBOL_TYPE_DATA:
+ Info.DataRef = Symbol.DataRef;
+ break;
+ case wasm::WASM_SYMBOL_TYPE_FUNCTION:
+ case wasm::WASM_SYMBOL_TYPE_GLOBAL:
+ case wasm::WASM_SYMBOL_TYPE_TABLE:
+ case wasm::WASM_SYMBOL_TYPE_TAG:
+ Info.ElementIndex = Symbol.ElementIndex;
+ break;
+ case wasm::WASM_SYMBOL_TYPE_SECTION:
+ Info.ElementIndex = Symbol.ElementIndex;
+ break;
+ }
+ LinkingSec->SymbolTable.emplace_back(Info);
+ }
+
+ for (const wasm::WasmInitFunc &Func : Obj.linkingData().InitFunctions) {
+ WasmYAML::InitFunction F{Func.Priority, Func.Symbol};
+ LinkingSec->InitFunctions.emplace_back(F);
+ }
+
+ CustomSec = std::move(LinkingSec);
+ } else if (WasmSec.Name == "producers") {
+ std::unique_ptr<WasmYAML::ProducersSection> ProducersSec =
+ std::make_unique<WasmYAML::ProducersSection>();
+ const llvm::wasm::WasmProducerInfo &Info = Obj.getProducerInfo();
+ for (auto &E : Info.Languages) {
+ WasmYAML::ProducerEntry Producer;
+ Producer.Name = E.first;
+ Producer.Version = E.second;
+ ProducersSec->Languages.push_back(Producer);
+ }
+ for (auto &E : Info.Tools) {
+ WasmYAML::ProducerEntry Producer;
+ Producer.Name = E.first;
+ Producer.Version = E.second;
+ ProducersSec->Tools.push_back(Producer);
+ }
+ for (auto &E : Info.SDKs) {
+ WasmYAML::ProducerEntry Producer;
+ Producer.Name = E.first;
+ Producer.Version = E.second;
+ ProducersSec->SDKs.push_back(Producer);
+ }
+ CustomSec = std::move(ProducersSec);
+ } else if (WasmSec.Name == "target_features") {
+ std::unique_ptr<WasmYAML::TargetFeaturesSection> TargetFeaturesSec =
+ std::make_unique<WasmYAML::TargetFeaturesSection>();
+ for (auto &E : Obj.getTargetFeatures()) {
+ WasmYAML::FeatureEntry Feature;
+ Feature.Prefix = E.Prefix;
+ Feature.Name = E.Name;
+ TargetFeaturesSec->Features.push_back(Feature);
+ }
+ CustomSec = std::move(TargetFeaturesSec);
+ } else {
+ CustomSec = std::make_unique<WasmYAML::CustomSection>(WasmSec.Name);
+ }
+ CustomSec->Payload = yaml::BinaryRef(WasmSec.Content);
+ return CustomSec;
+}
+
+ErrorOr<WasmYAML::Object *> WasmDumper::dump() {
+ auto Y = std::make_unique<WasmYAML::Object>();
+
+ // Dump header
+ Y->Header.Version = Obj.getHeader().Version;
+
+ // Dump sections
+ for (const auto &Sec : Obj.sections()) {
+ const WasmSection &WasmSec = Obj.getWasmSection(Sec);
+ std::unique_ptr<WasmYAML::Section> S;
+ switch (WasmSec.Type) {
+ case wasm::WASM_SEC_CUSTOM: {
+ if (WasmSec.Name.startswith("reloc.")) {
+ // Relocations are attached the sections they apply to rather than
+ // being represented as a custom section in the YAML output.
+ continue;
+ }
+ S = dumpCustomSection(WasmSec);
+ break;
+ }
+ case wasm::WASM_SEC_TYPE: {
+ auto TypeSec = std::make_unique<WasmYAML::TypeSection>();
+ uint32_t Index = 0;
+ for (const auto &FunctionSig : Obj.types()) {
+ WasmYAML::Signature Sig;
+ Sig.Index = Index++;
+ for (const auto &ParamType : FunctionSig.Params)
+ Sig.ParamTypes.emplace_back(static_cast<uint32_t>(ParamType));
+ for (const auto &ReturnType : FunctionSig.Returns)
+ Sig.ReturnTypes.emplace_back(static_cast<uint32_t>(ReturnType));
+ TypeSec->Signatures.push_back(Sig);
+ }
+ S = std::move(TypeSec);
+ break;
+ }
+ case wasm::WASM_SEC_IMPORT: {
+ auto ImportSec = std::make_unique<WasmYAML::ImportSection>();
+ for (auto &Import : Obj.imports()) {
+ WasmYAML::Import Im;
+ Im.Module = Import.Module;
+ Im.Field = Import.Field;
+ Im.Kind = Import.Kind;
+ switch (Im.Kind) {
+ case wasm::WASM_EXTERNAL_FUNCTION:
+ Im.SigIndex = Import.SigIndex;
+ break;
+ case wasm::WASM_EXTERNAL_GLOBAL:
+ Im.GlobalImport.Type = Import.Global.Type;
+ Im.GlobalImport.Mutable = Import.Global.Mutable;
+ break;
+ case wasm::WASM_EXTERNAL_TAG:
+ Im.SigIndex = Import.SigIndex;
+ break;
+ case wasm::WASM_EXTERNAL_TABLE:
+ // FIXME: Currently we always output an index of 0 for any imported
+ // table.
+ Im.TableImport = makeTable(0, Import.Table);
+ break;
+ case wasm::WASM_EXTERNAL_MEMORY:
+ Im.Memory = makeLimits(Import.Memory);
+ break;
+ }
+ ImportSec->Imports.push_back(Im);
+ }
+ S = std::move(ImportSec);
+ break;
+ }
+ case wasm::WASM_SEC_FUNCTION: {
+ auto FuncSec = std::make_unique<WasmYAML::FunctionSection>();
+ for (const auto &Func : Obj.functions()) {
+ FuncSec->FunctionTypes.push_back(Func.SigIndex);
+ }
+ S = std::move(FuncSec);
+ break;
+ }
+ case wasm::WASM_SEC_TABLE: {
+ auto TableSec = std::make_unique<WasmYAML::TableSection>();
+ for (const wasm::WasmTable &Table : Obj.tables()) {
+ TableSec->Tables.push_back(makeTable(Table.Index, Table.Type));
+ }
+ S = std::move(TableSec);
+ break;
+ }
+ case wasm::WASM_SEC_MEMORY: {
+ auto MemorySec = std::make_unique<WasmYAML::MemorySection>();
+ for (const wasm::WasmLimits &Memory : Obj.memories()) {
+ MemorySec->Memories.push_back(makeLimits(Memory));
+ }
+ S = std::move(MemorySec);
+ break;
+ }
+ case wasm::WASM_SEC_TAG: {
+ auto TagSec = std::make_unique<WasmYAML::TagSection>();
+ for (auto &Tag : Obj.tags()) {
+ TagSec->TagTypes.push_back(Tag.SigIndex);
+ }
+ S = std::move(TagSec);
+ break;
+ }
+ case wasm::WASM_SEC_GLOBAL: {
+ auto GlobalSec = std::make_unique<WasmYAML::GlobalSection>();
+ for (auto &Global : Obj.globals()) {
+ WasmYAML::Global G;
+ G.Index = Global.Index;
+ G.Type = Global.Type.Type;
+ G.Mutable = Global.Type.Mutable;
+ G.InitExpr = Global.InitExpr;
+ GlobalSec->Globals.push_back(G);
+ }
+ S = std::move(GlobalSec);
+ break;
+ }
+ case wasm::WASM_SEC_START: {
+ auto StartSec = std::make_unique<WasmYAML::StartSection>();
+ StartSec->StartFunction = Obj.startFunction();
+ S = std::move(StartSec);
+ break;
+ }
+ case wasm::WASM_SEC_EXPORT: {
+ auto ExportSec = std::make_unique<WasmYAML::ExportSection>();
+ for (auto &Export : Obj.exports()) {
+ WasmYAML::Export Ex;
+ Ex.Name = Export.Name;
+ Ex.Kind = Export.Kind;
+ Ex.Index = Export.Index;
+ ExportSec->Exports.push_back(Ex);
+ }
+ S = std::move(ExportSec);
+ break;
+ }
+ case wasm::WASM_SEC_ELEM: {
+ auto ElemSec = std::make_unique<WasmYAML::ElemSection>();
+ for (auto &Segment : Obj.elements()) {
+ WasmYAML::ElemSegment Seg;
+ Seg.Flags = Segment.Flags;
+ Seg.TableNumber = Segment.TableNumber;
+ Seg.ElemKind = Segment.ElemKind;
+ Seg.Offset = Segment.Offset;
+ append_range(Seg.Functions, Segment.Functions);
+ ElemSec->Segments.push_back(Seg);
+ }
+ S = std::move(ElemSec);
+ break;
+ }
+ case wasm::WASM_SEC_CODE: {
+ auto CodeSec = std::make_unique<WasmYAML::CodeSection>();
+ for (auto &Func : Obj.functions()) {
+ WasmYAML::Function Function;
+ Function.Index = Func.Index;
+ for (auto &Local : Func.Locals) {
+ WasmYAML::LocalDecl LocalDecl;
+ LocalDecl.Type = Local.Type;
+ LocalDecl.Count = Local.Count;
+ Function.Locals.push_back(LocalDecl);
+ }
+ Function.Body = yaml::BinaryRef(Func.Body);
+ CodeSec->Functions.push_back(Function);
+ }
+ S = std::move(CodeSec);
+ break;
+ }
+ case wasm::WASM_SEC_DATA: {
+ auto DataSec = std::make_unique<WasmYAML::DataSection>();
+ for (const object::WasmSegment &Segment : Obj.dataSegments()) {
+ WasmYAML::DataSegment Seg;
+ Seg.SectionOffset = Segment.SectionOffset;
+ Seg.InitFlags = Segment.Data.InitFlags;
+ Seg.MemoryIndex = Segment.Data.MemoryIndex;
+ Seg.Offset = Segment.Data.Offset;
+ Seg.Content = yaml::BinaryRef(Segment.Data.Content);
+ DataSec->Segments.push_back(Seg);
+ }
+ S = std::move(DataSec);
+ break;
+ }
+ case wasm::WASM_SEC_DATACOUNT: {
+ auto DataCountSec = std::make_unique<WasmYAML::DataCountSection>();
+ DataCountSec->Count = Obj.dataSegments().size();
+ S = std::move(DataCountSec);
+ break;
+ }
+ default:
+ llvm_unreachable("Unknown section type");
+ break;
+ }
+ for (const wasm::WasmRelocation &Reloc : WasmSec.Relocations) {
+ WasmYAML::Relocation R;
+ R.Type = Reloc.Type;
+ R.Index = Reloc.Index;
+ R.Offset = Reloc.Offset;
+ R.Addend = Reloc.Addend;
+ S->Relocations.push_back(R);
+ }
+ Y->Sections.push_back(std::move(S));
+ }
+
+ return Y.release();
+}
+
+std::error_code wasm2yaml(raw_ostream &Out, const object::WasmObjectFile &Obj) {
+ WasmDumper Dumper(Obj);
+ ErrorOr<WasmYAML::Object *> YAMLOrErr = Dumper.dump();
+ if (std::error_code EC = YAMLOrErr.getError())
+ return EC;
+
+ std::unique_ptr<WasmYAML::Object> YAML(YAMLOrErr.get());
+ yaml::Output Yout(Out);
+ Yout << *YAML;
+
+ return std::error_code();
+}
diff --git a/contrib/libs/llvm14/tools/obj2yaml/xcoff2yaml.cpp b/contrib/libs/llvm14/tools/obj2yaml/xcoff2yaml.cpp
new file mode 100644
index 00000000000..882c4104960
--- /dev/null
+++ b/contrib/libs/llvm14/tools/obj2yaml/xcoff2yaml.cpp
@@ -0,0 +1,152 @@
+//===------ xcoff2yaml.cpp - XCOFF YAMLIO implementation --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "obj2yaml.h"
+#include "llvm/Object/XCOFFObjectFile.h"
+#include "llvm/ObjectYAML/XCOFFYAML.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace llvm;
+using namespace llvm::object;
+namespace {
+
+class XCOFFDumper {
+ const object::XCOFFObjectFile &Obj;
+ XCOFFYAML::Object YAMLObj;
+ void dumpHeader();
+ Error dumpSections();
+ Error dumpSymbols();
+ template <typename Shdr, typename Reloc>
+ Error dumpSections(ArrayRef<Shdr> Sections);
+
+public:
+ XCOFFDumper(const object::XCOFFObjectFile &obj) : Obj(obj) {}
+ Error dump();
+ XCOFFYAML::Object &getYAMLObj() { return YAMLObj; }
+};
+} // namespace
+
+Error XCOFFDumper::dump() {
+ dumpHeader();
+ if (Error E = dumpSections())
+ return E;
+ return dumpSymbols();
+}
+
+void XCOFFDumper::dumpHeader() {
+ YAMLObj.Header.Magic = Obj.getMagic();
+ YAMLObj.Header.NumberOfSections = Obj.getNumberOfSections();
+ YAMLObj.Header.TimeStamp = Obj.getTimeStamp();
+ YAMLObj.Header.SymbolTableOffset = Obj.is64Bit()
+ ? Obj.getSymbolTableOffset64()
+ : Obj.getSymbolTableOffset32();
+ YAMLObj.Header.NumberOfSymTableEntries =
+ Obj.is64Bit() ? Obj.getNumberOfSymbolTableEntries64()
+ : Obj.getRawNumberOfSymbolTableEntries32();
+ YAMLObj.Header.AuxHeaderSize = Obj.getOptionalHeaderSize();
+ YAMLObj.Header.Flags = Obj.getFlags();
+}
+
+Error XCOFFDumper::dumpSections() {
+ if (Obj.is64Bit())
+ return dumpSections<XCOFFSectionHeader64, XCOFFRelocation64>(
+ Obj.sections64());
+ return dumpSections<XCOFFSectionHeader32, XCOFFRelocation32>(
+ Obj.sections32());
+}
+
+template <typename Shdr, typename Reloc>
+Error XCOFFDumper::dumpSections(ArrayRef<Shdr> Sections) {
+ std::vector<XCOFFYAML::Section> &YamlSections = YAMLObj.Sections;
+ for (const Shdr &S : Sections) {
+ XCOFFYAML::Section YamlSec;
+ YamlSec.SectionName = S.getName();
+ YamlSec.Address = S.PhysicalAddress;
+ YamlSec.Size = S.SectionSize;
+ YamlSec.NumberOfRelocations = S.NumberOfRelocations;
+ YamlSec.NumberOfLineNumbers = S.NumberOfLineNumbers;
+ YamlSec.FileOffsetToData = S.FileOffsetToRawData;
+ YamlSec.FileOffsetToRelocations = S.FileOffsetToRelocationInfo;
+ YamlSec.FileOffsetToLineNumbers = S.FileOffsetToLineNumberInfo;
+ YamlSec.Flags = S.Flags;
+
+ // Dump section data.
+ if (S.FileOffsetToRawData) {
+ DataRefImpl SectionDRI;
+ SectionDRI.p = reinterpret_cast<uintptr_t>(&S);
+ Expected<ArrayRef<uint8_t>> SecDataRefOrErr =
+ Obj.getSectionContents(SectionDRI);
+ if (!SecDataRefOrErr)
+ return SecDataRefOrErr.takeError();
+ YamlSec.SectionData = SecDataRefOrErr.get();
+ }
+
+ // Dump relocations.
+ if (S.NumberOfRelocations) {
+ auto RelRefOrErr = Obj.relocations<Shdr, Reloc>(S);
+ if (!RelRefOrErr)
+ return RelRefOrErr.takeError();
+ for (const Reloc &R : RelRefOrErr.get()) {
+ XCOFFYAML::Relocation YamlRel;
+ YamlRel.Type = R.Type;
+ YamlRel.Info = R.Info;
+ YamlRel.SymbolIndex = R.SymbolIndex;
+ YamlRel.VirtualAddress = R.VirtualAddress;
+ YamlSec.Relocations.push_back(YamlRel);
+ }
+ }
+ YamlSections.push_back(YamlSec);
+ }
+ return Error::success();
+}
+
+Error XCOFFDumper::dumpSymbols() {
+ std::vector<XCOFFYAML::Symbol> &Symbols = YAMLObj.Symbols;
+
+ for (const SymbolRef &S : Obj.symbols()) {
+ DataRefImpl SymbolDRI = S.getRawDataRefImpl();
+ const XCOFFSymbolRef SymbolEntRef = Obj.toSymbolRef(SymbolDRI);
+ XCOFFYAML::Symbol Sym;
+
+ Expected<StringRef> SymNameRefOrErr = Obj.getSymbolName(SymbolDRI);
+ if (!SymNameRefOrErr) {
+ return SymNameRefOrErr.takeError();
+ }
+ Sym.SymbolName = SymNameRefOrErr.get();
+
+ Sym.Value = SymbolEntRef.getValue();
+
+ Expected<StringRef> SectionNameRefOrErr =
+ Obj.getSymbolSectionName(SymbolEntRef);
+ if (!SectionNameRefOrErr)
+ return SectionNameRefOrErr.takeError();
+
+ Sym.SectionName = SectionNameRefOrErr.get();
+
+ Sym.Type = SymbolEntRef.getSymbolType();
+ Sym.StorageClass = SymbolEntRef.getStorageClass();
+ Sym.NumberOfAuxEntries = SymbolEntRef.getNumberOfAuxEntries();
+
+ Symbols.push_back(std::move(Sym));
+ }
+
+ return Error::success();
+}
+
+Error xcoff2yaml(raw_ostream &Out, const object::XCOFFObjectFile &Obj) {
+ XCOFFDumper Dumper(Obj);
+
+ if (Error E = Dumper.dump())
+ return E;
+
+ yaml::Output Yout(Out);
+ Yout << Dumper.getYAMLObj();
+
+ return Error::success();
+}
diff --git a/contrib/libs/llvm14/tools/obj2yaml/ya.make b/contrib/libs/llvm14/tools/obj2yaml/ya.make
new file mode 100644
index 00000000000..01f6a855f77
--- /dev/null
+++ b/contrib/libs/llvm14/tools/obj2yaml/ya.make
@@ -0,0 +1,47 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ObjectYAML
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/obj2yaml
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ archive2yaml.cpp
+ coff2yaml.cpp
+ dwarf2yaml.cpp
+ elf2yaml.cpp
+ macho2yaml.cpp
+ minidump2yaml.cpp
+ obj2yaml.cpp
+ wasm2yaml.cpp
+ xcoff2yaml.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/opt/AnalysisWrappers.cpp b/contrib/libs/llvm14/tools/opt/AnalysisWrappers.cpp
new file mode 100644
index 00000000000..2ae1da84a9a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/opt/AnalysisWrappers.cpp
@@ -0,0 +1,71 @@
+//===- AnalysisWrappers.cpp - Wrappers around non-pass analyses -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines pass wrappers around LLVM analyses that don't make sense to
+// be passes. It provides a nice standard pass interface to these classes so
+// that they can be printed out by analyze.
+//
+// These classes are separated out of analyze.cpp so that it is more clear which
+// code is the integral part of the analyze tool, and which part of the code is
+// just making it so more passes are available.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Analysis/CallGraph.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace llvm;
+
+namespace {
+ /// ExternalFunctionsPassedConstants - This pass prints out call sites to
+ /// external functions that are called with constant arguments. This can be
+ /// useful when looking for standard library functions we should constant fold
+ /// or handle in alias analyses.
+ struct ExternalFunctionsPassedConstants : public ModulePass {
+ static char ID; // Pass ID, replacement for typeid
+ ExternalFunctionsPassedConstants() : ModulePass(ID) {}
+ bool runOnModule(Module &M) override {
+ for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
+ if (!I->isDeclaration()) continue;
+
+ bool PrintedFn = false;
+ for (User *U : I->users()) {
+ Instruction *UI = dyn_cast<Instruction>(U);
+ if (!UI) continue;
+
+ CallBase *CB = dyn_cast<CallBase>(UI);
+ if (!CB)
+ continue;
+
+ for (auto AI = CB->arg_begin(), E = CB->arg_end(); AI != E; ++AI) {
+ if (!isa<Constant>(*AI)) continue;
+
+ if (!PrintedFn) {
+ errs() << "Function '" << I->getName() << "':\n";
+ PrintedFn = true;
+ }
+ errs() << *UI;
+ break;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ }
+ };
+}
+
+char ExternalFunctionsPassedConstants::ID = 0;
+static RegisterPass<ExternalFunctionsPassedConstants>
+ P1("print-externalfnconstants",
+ "Print external fn callsites passed constants");
diff --git a/contrib/libs/llvm14/tools/opt/BreakpointPrinter.cpp b/contrib/libs/llvm14/tools/opt/BreakpointPrinter.cpp
new file mode 100644
index 00000000000..a57a8c43c26
--- /dev/null
+++ b/contrib/libs/llvm14/tools/opt/BreakpointPrinter.cpp
@@ -0,0 +1,71 @@
+//===- BreakpointPrinter.cpp - Breakpoint location printer ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Breakpoint location printer.
+///
+//===----------------------------------------------------------------------===//
+#include "BreakpointPrinter.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace {
+
+struct BreakpointPrinter : public ModulePass {
+ raw_ostream &Out;
+ static char ID;
+
+ BreakpointPrinter(raw_ostream &out) : ModulePass(ID), Out(out) {}
+
+ void getContextName(const DIScope *Context, std::string &N) {
+ if (auto *NS = dyn_cast<DINamespace>(Context)) {
+ if (!NS->getName().empty()) {
+ getContextName(NS->getScope(), N);
+ N = N + NS->getName().str() + "::";
+ }
+ } else if (auto *TY = dyn_cast<DIType>(Context)) {
+ if (!TY->getName().empty()) {
+ getContextName(TY->getScope(), N);
+ N = N + TY->getName().str() + "::";
+ }
+ }
+ }
+
+ bool runOnModule(Module &M) override {
+ StringSet<> Processed;
+ if (NamedMDNode *NMD = M.getNamedMetadata("llvm.dbg.sp"))
+ for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) {
+ std::string Name;
+ auto *SP = cast_or_null<DISubprogram>(NMD->getOperand(i));
+ if (!SP)
+ continue;
+ getContextName(SP->getScope(), Name);
+ Name = Name + SP->getName().str();
+ if (!Name.empty() && Processed.insert(Name).second) {
+ Out << Name << "\n";
+ }
+ }
+ return false;
+ }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ }
+};
+
+char BreakpointPrinter::ID = 0;
+}
+
+ModulePass *llvm::createBreakpointPrinter(raw_ostream &out) {
+ return new BreakpointPrinter(out);
+}
diff --git a/contrib/libs/llvm14/tools/opt/BreakpointPrinter.h b/contrib/libs/llvm14/tools/opt/BreakpointPrinter.h
new file mode 100644
index 00000000000..2877555f852
--- /dev/null
+++ b/contrib/libs/llvm14/tools/opt/BreakpointPrinter.h
@@ -0,0 +1,24 @@
+//===- BreakpointPrinter.h - Breakpoint location printer ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Breakpoint location printer.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_OPT_BREAKPOINTPRINTER_H
+#define LLVM_TOOLS_OPT_BREAKPOINTPRINTER_H
+
+namespace llvm {
+
+class ModulePass;
+class raw_ostream;
+
+ModulePass *createBreakpointPrinter(raw_ostream &out);
+}
+
+#endif // LLVM_TOOLS_OPT_BREAKPOINTPRINTER_H
diff --git a/contrib/libs/llvm14/tools/opt/GraphPrinters.cpp b/contrib/libs/llvm14/tools/opt/GraphPrinters.cpp
new file mode 100644
index 00000000000..611fb20513c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/opt/GraphPrinters.cpp
@@ -0,0 +1,45 @@
+//===- GraphPrinters.cpp - DOT printers for various graph types -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines several printers for various different types of graphs used
+// by the LLVM infrastructure. It uses the generic graph interface to convert
+// the graph into a .dot graph. These graphs can then be processed with the
+// "dot" tool to convert them to postscript or some other suitable format.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/Dominators.h"
+#include "llvm/Pass.h"
+
+using namespace llvm;
+
+//===----------------------------------------------------------------------===//
+// DomInfoPrinter Pass
+//===----------------------------------------------------------------------===//
+
+namespace {
+ class DomInfoPrinter : public FunctionPass {
+ public:
+ static char ID; // Pass identification, replacement for typeid
+ DomInfoPrinter() : FunctionPass(ID) {}
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ AU.addRequired<DominatorTreeWrapperPass>();
+ }
+
+ bool runOnFunction(Function &F) override {
+ getAnalysis<DominatorTreeWrapperPass>().print(dbgs());
+ return false;
+ }
+ };
+}
+
+char DomInfoPrinter::ID = 0;
+static RegisterPass<DomInfoPrinter>
+DIP("print-dom-info", "Dominator Info Printer", true, true);
diff --git a/contrib/libs/llvm14/tools/opt/NewPMDriver.cpp b/contrib/libs/llvm14/tools/opt/NewPMDriver.cpp
new file mode 100644
index 00000000000..af330893944
--- /dev/null
+++ b/contrib/libs/llvm14/tools/opt/NewPMDriver.cpp
@@ -0,0 +1,495 @@
+//===- NewPMDriver.cpp - Driver for opt with new PM -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file is just a split of the code that logically belongs in opt.cpp but
+/// that includes the new pass manager headers.
+///
+//===----------------------------------------------------------------------===//
+
+#include "NewPMDriver.h"
+#include "PassPrinters.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/Analysis/CGSCCPassManager.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Bitcode/BitcodeWriterPass.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRPrintingPasses.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/PassPlugin.h"
+#include "llvm/Passes/StandardInstrumentations.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h"
+#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
+#include "llvm/Transforms/Scalar/LoopPassManager.h"
+#include "llvm/Transforms/Utils/Debugify.h"
+
+using namespace llvm;
+using namespace opt_tool;
+
+namespace llvm {
+cl::opt<bool> DebugifyEach(
+ "debugify-each",
+ cl::desc("Start each pass with debugify and end it with check-debugify"));
+
+cl::opt<std::string>
+ DebugifyExport("debugify-export",
+ cl::desc("Export per-pass debugify statistics to this file"),
+ cl::value_desc("filename"));
+} // namespace llvm
+
+enum class DebugLogging { None, Normal, Verbose, Quiet };
+
+static cl::opt<DebugLogging> DebugPM(
+ "debug-pass-manager", cl::Hidden, cl::ValueOptional,
+ cl::desc("Print pass management debugging information"),
+ cl::init(DebugLogging::None),
+ cl::values(
+ clEnumValN(DebugLogging::Normal, "", ""),
+ clEnumValN(DebugLogging::Quiet, "quiet",
+ "Skip printing info about analyses"),
+ clEnumValN(
+ DebugLogging::Verbose, "verbose",
+ "Print extra information about adaptors and pass managers")));
+
+static cl::list<std::string>
+ PassPlugins("load-pass-plugin",
+ cl::desc("Load passes from plugin library"));
+
+// This flag specifies a textual description of the alias analysis pipeline to
+// use when querying for aliasing information. It only works in concert with
+// the "passes" flag above.
+static cl::opt<std::string>
+ AAPipeline("aa-pipeline",
+ cl::desc("A textual description of the alias analysis "
+ "pipeline for handling managed aliasing queries"),
+ cl::Hidden, cl::init("default"));
+
+/// {{@ These options accept textual pipeline descriptions which will be
+/// inserted into default pipelines at the respective extension points
+static cl::opt<std::string> PeepholeEPPipeline(
+ "passes-ep-peephole",
+ cl::desc("A textual description of the function pass pipeline inserted at "
+ "the Peephole extension points into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> LateLoopOptimizationsEPPipeline(
+ "passes-ep-late-loop-optimizations",
+ cl::desc(
+ "A textual description of the loop pass pipeline inserted at "
+ "the LateLoopOptimizations extension point into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> LoopOptimizerEndEPPipeline(
+ "passes-ep-loop-optimizer-end",
+ cl::desc("A textual description of the loop pass pipeline inserted at "
+ "the LoopOptimizerEnd extension point into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> ScalarOptimizerLateEPPipeline(
+ "passes-ep-scalar-optimizer-late",
+ cl::desc("A textual description of the function pass pipeline inserted at "
+ "the ScalarOptimizerLate extension point into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> CGSCCOptimizerLateEPPipeline(
+ "passes-ep-cgscc-optimizer-late",
+ cl::desc("A textual description of the cgscc pass pipeline inserted at "
+ "the CGSCCOptimizerLate extension point into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> VectorizerStartEPPipeline(
+ "passes-ep-vectorizer-start",
+ cl::desc("A textual description of the function pass pipeline inserted at "
+ "the VectorizerStart extension point into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> PipelineStartEPPipeline(
+ "passes-ep-pipeline-start",
+ cl::desc("A textual description of the module pass pipeline inserted at "
+ "the PipelineStart extension point into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> PipelineEarlySimplificationEPPipeline(
+ "passes-ep-pipeline-early-simplification",
+ cl::desc("A textual description of the module pass pipeline inserted at "
+ "the EarlySimplification extension point into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> OptimizerLastEPPipeline(
+ "passes-ep-optimizer-last",
+ cl::desc("A textual description of the module pass pipeline inserted at "
+ "the OptimizerLast extension point into default pipelines"),
+ cl::Hidden);
+
+// Individual pipeline tuning options.
+extern cl::opt<bool> DisableLoopUnrolling;
+
+namespace llvm {
+extern cl::opt<PGOKind> PGOKindFlag;
+extern cl::opt<std::string> ProfileFile;
+extern cl::opt<CSPGOKind> CSPGOKindFlag;
+extern cl::opt<std::string> CSProfileGenFile;
+extern cl::opt<bool> DisableBasicAA;
+extern cl::opt<bool> PrintPipelinePasses;
+} // namespace llvm
+
+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."));
+static cl::opt<bool> PseudoProbeForProfiling(
+ "new-pm-pseudo-probe-for-profiling", cl::init(false), cl::Hidden,
+ cl::desc("Emit pseudo probes to enable PGO profile generation."));
+/// @}}
+
+template <typename PassManagerT>
+bool tryParsePipelineText(PassBuilder &PB,
+ const cl::opt<std::string> &PipelineOpt) {
+ if (PipelineOpt.empty())
+ return false;
+
+ // Verify the pipeline is parseable:
+ PassManagerT PM;
+ 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
+/// for parsing and inserting the given pipeline
+static void registerEPCallbacks(PassBuilder &PB) {
+ if (tryParsePipelineText<FunctionPassManager>(PB, PeepholeEPPipeline))
+ PB.registerPeepholeEPCallback(
+ [&PB](FunctionPassManager &PM, OptimizationLevel Level) {
+ ExitOnError Err("Unable to parse PeepholeEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, PeepholeEPPipeline));
+ });
+ if (tryParsePipelineText<LoopPassManager>(PB,
+ LateLoopOptimizationsEPPipeline))
+ PB.registerLateLoopOptimizationsEPCallback(
+ [&PB](LoopPassManager &PM, OptimizationLevel Level) {
+ ExitOnError Err("Unable to parse LateLoopOptimizationsEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, LateLoopOptimizationsEPPipeline));
+ });
+ if (tryParsePipelineText<LoopPassManager>(PB, LoopOptimizerEndEPPipeline))
+ PB.registerLoopOptimizerEndEPCallback(
+ [&PB](LoopPassManager &PM, OptimizationLevel Level) {
+ ExitOnError Err("Unable to parse LoopOptimizerEndEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, LoopOptimizerEndEPPipeline));
+ });
+ if (tryParsePipelineText<FunctionPassManager>(PB,
+ ScalarOptimizerLateEPPipeline))
+ PB.registerScalarOptimizerLateEPCallback(
+ [&PB](FunctionPassManager &PM, OptimizationLevel Level) {
+ ExitOnError Err("Unable to parse ScalarOptimizerLateEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, ScalarOptimizerLateEPPipeline));
+ });
+ if (tryParsePipelineText<CGSCCPassManager>(PB, CGSCCOptimizerLateEPPipeline))
+ PB.registerCGSCCOptimizerLateEPCallback(
+ [&PB](CGSCCPassManager &PM, OptimizationLevel Level) {
+ ExitOnError Err("Unable to parse CGSCCOptimizerLateEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, CGSCCOptimizerLateEPPipeline));
+ });
+ if (tryParsePipelineText<FunctionPassManager>(PB, VectorizerStartEPPipeline))
+ PB.registerVectorizerStartEPCallback(
+ [&PB](FunctionPassManager &PM, OptimizationLevel Level) {
+ ExitOnError Err("Unable to parse VectorizerStartEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, VectorizerStartEPPipeline));
+ });
+ if (tryParsePipelineText<ModulePassManager>(PB, PipelineStartEPPipeline))
+ PB.registerPipelineStartEPCallback(
+ [&PB](ModulePassManager &PM, OptimizationLevel) {
+ ExitOnError Err("Unable to parse PipelineStartEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, PipelineStartEPPipeline));
+ });
+ if (tryParsePipelineText<ModulePassManager>(
+ PB, PipelineEarlySimplificationEPPipeline))
+ PB.registerPipelineEarlySimplificationEPCallback(
+ [&PB](ModulePassManager &PM, OptimizationLevel) {
+ ExitOnError Err("Unable to parse EarlySimplification pipeline: ");
+ Err(PB.parsePassPipeline(PM, PipelineEarlySimplificationEPPipeline));
+ });
+ if (tryParsePipelineText<FunctionPassManager>(PB, OptimizerLastEPPipeline))
+ PB.registerOptimizerLastEPCallback(
+ [&PB](ModulePassManager &PM, OptimizationLevel) {
+ ExitOnError Err("Unable to parse OptimizerLastEP pipeline: ");
+ Err(PB.parsePassPipeline(PM, OptimizerLastEPPipeline));
+ });
+}
+
+#define HANDLE_EXTENSION(Ext) \
+ llvm::PassPluginLibraryInfo get##Ext##PluginInfo();
+#include "llvm/Support/Extension.def"
+
+bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
+ TargetLibraryInfoImpl *TLII, ToolOutputFile *Out,
+ ToolOutputFile *ThinLTOLinkOut,
+ ToolOutputFile *OptRemarkFile,
+ StringRef PassPipeline, ArrayRef<StringRef> Passes,
+ OutputKind OK, VerifierKind VK,
+ bool ShouldPreserveAssemblyUseListOrder,
+ bool ShouldPreserveBitcodeUseListOrder,
+ bool EmitSummaryIndex, bool EmitModuleHash,
+ bool EnableDebugify) {
+ bool VerifyEachPass = VK == VK_VerifyEachPass;
+
+ Optional<PGOOptions> P;
+ switch (PGOKindFlag) {
+ case InstrGen:
+ P = PGOOptions(ProfileFile, "", "", PGOOptions::IRInstr);
+ break;
+ case InstrUse:
+ P = PGOOptions(ProfileFile, "", ProfileRemappingFile, PGOOptions::IRUse);
+ break;
+ case SampleUse:
+ P = PGOOptions(ProfileFile, "", ProfileRemappingFile,
+ PGOOptions::SampleUse);
+ break;
+ case NoPGO:
+ if (DebugInfoForProfiling || PseudoProbeForProfiling)
+ P = PGOOptions("", "", "", PGOOptions::NoAction, PGOOptions::NoCSAction,
+ DebugInfoForProfiling, PseudoProbeForProfiling);
+ else
+ P = None;
+ }
+ if (CSPGOKindFlag != NoCSPGO) {
+ if (P && (P->Action == PGOOptions::IRInstr ||
+ P->Action == PGOOptions::SampleUse))
+ errs() << "CSPGOKind cannot be used with IRInstr or SampleUse";
+ if (CSPGOKindFlag == CSInstrGen) {
+ if (CSProfileGenFile.empty())
+ errs() << "CSInstrGen needs to specify CSProfileGenFile";
+ if (P) {
+ P->CSAction = PGOOptions::CSIRInstr;
+ P->CSProfileGenFile = CSProfileGenFile;
+ } else
+ P = PGOOptions("", CSProfileGenFile, ProfileRemappingFile,
+ PGOOptions::NoAction, PGOOptions::CSIRInstr);
+ } else /* CSPGOKindFlag == CSInstrUse */ {
+ if (!P)
+ errs() << "CSInstrUse needs to be together with InstrUse";
+ P->CSAction = PGOOptions::CSIRUse;
+ }
+ }
+ if (TM)
+ TM->setPGOOption(P);
+
+ LoopAnalysisManager LAM;
+ FunctionAnalysisManager FAM;
+ CGSCCAnalysisManager CGAM;
+ ModuleAnalysisManager MAM;
+
+ PassInstrumentationCallbacks PIC;
+ PrintPassOptions PrintPassOpts;
+ PrintPassOpts.Verbose = DebugPM == DebugLogging::Verbose;
+ PrintPassOpts.SkipAnalyses = DebugPM == DebugLogging::Quiet;
+ StandardInstrumentations SI(DebugPM != DebugLogging::None, VerifyEachPass,
+ PrintPassOpts);
+ SI.registerCallbacks(PIC, &FAM);
+ DebugifyEachInstrumentation Debugify;
+ if (DebugifyEach)
+ Debugify.registerCallbacks(PIC);
+
+ PipelineTuningOptions PTO;
+ // LoopUnrolling defaults on to true and DisableLoopUnrolling is initialized
+ // to false above so we shouldn't necessarily need to check whether or not the
+ // option has been enabled.
+ PTO.LoopUnrolling = !DisableLoopUnrolling;
+ PassBuilder PB(TM, PTO, P, &PIC);
+ registerEPCallbacks(PB);
+
+ // Load requested pass plugins and let them register pass builder callbacks
+ for (auto &PluginFN : PassPlugins) {
+ auto PassPlugin = PassPlugin::Load(PluginFN);
+ if (!PassPlugin) {
+ errs() << "Failed to load passes from '" << PluginFN
+ << "'. Request ignored.\n";
+ continue;
+ }
+
+ PassPlugin->registerPassBuilderCallbacks(PB);
+ }
+
+ PB.registerPipelineParsingCallback(
+ [](StringRef Name, ModulePassManager &MPM,
+ ArrayRef<PassBuilder::PipelineElement>) {
+ AddressSanitizerOptions Opts;
+ if (Name == "asan-pipeline") {
+ MPM.addPass(
+ RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>());
+ MPM.addPass(ModuleAddressSanitizerPass(Opts));
+ return true;
+ } else if (Name == "asan-function-pipeline") {
+ MPM.addPass(
+ RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>());
+ MPM.addPass(
+ createModuleToFunctionPassAdaptor(AddressSanitizerPass(Opts)));
+ return true;
+ }
+ return false;
+ });
+
+#define HANDLE_EXTENSION(Ext) \
+ get##Ext##PluginInfo().RegisterPassBuilderCallbacks(PB);
+#include "llvm/Support/Extension.def"
+
+ // Specially handle the alias analysis manager so that we can register
+ // a custom pipeline of AA passes with it.
+ AAManager AA;
+ if (Passes.empty()) {
+ if (auto Err = PB.parseAAPipeline(AA, AAPipeline)) {
+ errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
+ return false;
+ }
+ }
+
+ // For compatibility with the legacy PM AA pipeline.
+ // AAResultsWrapperPass by default provides basic-aa in the legacy PM
+ // unless -disable-basic-aa is specified.
+ // TODO: remove this once tests implicitly requiring basic-aa use -passes= and
+ // -aa-pipeline=basic-aa.
+ if (!Passes.empty() && !DisableBasicAA) {
+ if (auto Err = PB.parseAAPipeline(AA, "basic-aa")) {
+ errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
+ return false;
+ }
+ }
+
+ // For compatibility with legacy pass manager.
+ // Alias analyses are not specially specified when using the legacy PM.
+ for (auto PassName : Passes) {
+ if (PB.isAAPassName(PassName)) {
+ if (auto Err = PB.parseAAPipeline(AA, PassName)) {
+ errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
+ return false;
+ }
+ }
+ }
+
+ // Register the AA manager first so that our version is the one used.
+ FAM.registerPass([&] { return std::move(AA); });
+ // Register our TargetLibraryInfoImpl.
+ FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); });
+
+ // Register all the basic analyses with the managers.
+ PB.registerModuleAnalyses(MAM);
+ PB.registerCGSCCAnalyses(CGAM);
+ PB.registerFunctionAnalyses(FAM);
+ PB.registerLoopAnalyses(LAM);
+ PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+
+ ModulePassManager MPM;
+ if (VK > VK_NoVerifier)
+ MPM.addPass(VerifierPass());
+ if (EnableDebugify)
+ MPM.addPass(NewPMDebugifyPass());
+
+ // Add passes according to the -passes options.
+ if (!PassPipeline.empty()) {
+ assert(Passes.empty() &&
+ "PassPipeline and Passes should not both contain passes");
+ if (auto Err = PB.parsePassPipeline(MPM, PassPipeline)) {
+ errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
+ return false;
+ }
+ }
+ // Add passes specified using the legacy PM syntax (i.e. not using
+ // -passes). This should be removed later when such support has been
+ // deprecated, i.e. when all lit tests running opt (and not using
+ // -enable-new-pm=0) have been updated to use -passes.
+ for (auto PassName : Passes) {
+ std::string ModifiedPassName(PassName.begin(), PassName.end());
+ if (PB.isAnalysisPassName(PassName))
+ ModifiedPassName = "require<" + ModifiedPassName + ">";
+ // FIXME: These translations are supposed to be removed when lit tests that
+ // use these names have been updated to use the -passes syntax (and when the
+ // support for using the old syntax to specify passes is considered as
+ // deprecated for the new PM).
+ if (ModifiedPassName == "early-cse-memssa")
+ ModifiedPassName = "early-cse<memssa>";
+ else if (ModifiedPassName == "post-inline-ee-instrument")
+ ModifiedPassName = "ee-instrument<post-inline>";
+ else if (ModifiedPassName == "loop-extract-single")
+ ModifiedPassName = "loop-extract<single>";
+ else if (ModifiedPassName == "lower-matrix-intrinsics-minimal")
+ ModifiedPassName = "lower-matrix-intrinsics<minimal>";
+ if (auto Err = PB.parsePassPipeline(MPM, ModifiedPassName)) {
+ errs() << Arg0 << ": " << toString(std::move(Err)) << "\n";
+ return false;
+ }
+ }
+
+ if (VK > VK_NoVerifier)
+ MPM.addPass(VerifierPass());
+ if (EnableDebugify)
+ MPM.addPass(NewPMCheckDebugifyPass());
+
+ // Add any relevant output pass at the end of the pipeline.
+ switch (OK) {
+ case OK_NoOutput:
+ break; // No output pass needed.
+ case OK_OutputAssembly:
+ MPM.addPass(
+ PrintModulePass(Out->os(), "", ShouldPreserveAssemblyUseListOrder));
+ break;
+ case OK_OutputBitcode:
+ MPM.addPass(BitcodeWriterPass(Out->os(), ShouldPreserveBitcodeUseListOrder,
+ EmitSummaryIndex, EmitModuleHash));
+ break;
+ case OK_OutputThinLTOBitcode:
+ MPM.addPass(ThinLTOBitcodeWriterPass(
+ Out->os(), ThinLTOLinkOut ? &ThinLTOLinkOut->os() : nullptr));
+ break;
+ }
+
+ // Before executing passes, print the final values of the LLVM options.
+ cl::PrintOptionValues();
+
+ // Print a textual, '-passes=' compatible, representation of pipeline if
+ // requested.
+ if (PrintPipelinePasses) {
+ MPM.printPipeline(outs(), [&PIC](StringRef ClassName) {
+ auto PassName = PIC.getPassNameForClassName(ClassName);
+ return PassName.empty() ? ClassName : PassName;
+ });
+ outs() << "\n";
+ return true;
+ }
+
+ // Now that we have all of the passes ready, run them.
+ MPM.run(M, MAM);
+
+ // Declare success.
+ if (OK != OK_NoOutput) {
+ Out->keep();
+ if (OK == OK_OutputThinLTOBitcode && ThinLTOLinkOut)
+ ThinLTOLinkOut->keep();
+ }
+
+ if (OptRemarkFile)
+ OptRemarkFile->keep();
+
+ if (DebugifyEach && !DebugifyExport.empty())
+ exportDebugifyStats(DebugifyExport, Debugify.StatsMap);
+
+ return true;
+}
+
+void llvm::printPasses(raw_ostream &OS) {
+ PassBuilder PB;
+ PB.printPassNames(OS);
+}
diff --git a/contrib/libs/llvm14/tools/opt/NewPMDriver.h b/contrib/libs/llvm14/tools/opt/NewPMDriver.h
new file mode 100644
index 00000000000..056f7d6a9b8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/opt/NewPMDriver.h
@@ -0,0 +1,79 @@
+//===- NewPMDriver.h - Function to drive opt with the new PM ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// A single function which is called to drive the opt behavior for the new
+/// PassManager.
+///
+/// This is only in a separate TU with a header to avoid including all of the
+/// old pass manager headers and the new pass manager headers into the same
+/// file. Eventually all of the routines here will get folded back into
+/// opt.cpp.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OPT_NEWPMDRIVER_H
+#define LLVM_TOOLS_OPT_NEWPMDRIVER_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/CommandLine.h"
+
+namespace llvm {
+class StringRef;
+class Module;
+class TargetMachine;
+class ToolOutputFile;
+class TargetLibraryInfoImpl;
+
+extern cl::opt<bool> DebugifyEach;
+extern cl::opt<std::string> DebugifyExport;
+
+namespace opt_tool {
+enum OutputKind {
+ OK_NoOutput,
+ OK_OutputAssembly,
+ OK_OutputBitcode,
+ OK_OutputThinLTOBitcode,
+};
+enum VerifierKind {
+ VK_NoVerifier,
+ VK_VerifyInAndOut,
+ VK_VerifyEachPass
+};
+enum PGOKind {
+ NoPGO,
+ InstrGen,
+ InstrUse,
+ SampleUse
+};
+enum CSPGOKind { NoCSPGO, CSInstrGen, CSInstrUse };
+}
+
+void printPasses(raw_ostream &OS);
+
+/// Driver function to run the new pass manager over a module.
+///
+/// This function only exists factored away from opt.cpp in order to prevent
+/// inclusion of the new pass manager headers and the old headers into the same
+/// file. It's interface is consequentially somewhat ad-hoc, but will go away
+/// when the transition finishes.
+///
+/// ThinLTOLinkOut is only used when OK is OK_OutputThinLTOBitcode, and can be
+/// nullptr.
+bool runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
+ TargetLibraryInfoImpl *TLII, ToolOutputFile *Out,
+ ToolOutputFile *ThinLinkOut, ToolOutputFile *OptRemarkFile,
+ StringRef PassPipeline, ArrayRef<StringRef> PassInfos,
+ opt_tool::OutputKind OK, opt_tool::VerifierKind VK,
+ bool ShouldPreserveAssemblyUseListOrder,
+ bool ShouldPreserveBitcodeUseListOrder,
+ bool EmitSummaryIndex, bool EmitModuleHash,
+ bool EnableDebugify);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/opt/PassPrinters.cpp b/contrib/libs/llvm14/tools/opt/PassPrinters.cpp
new file mode 100644
index 00000000000..4e81b5d29c4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/opt/PassPrinters.cpp
@@ -0,0 +1,212 @@
+//===- PassPrinters.cpp - Utilities to print analysis info for passes -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Utilities to print analysis info for various kinds of passes.
+///
+//===----------------------------------------------------------------------===//
+
+#include "PassPrinters.h"
+#include "llvm/Analysis/CallGraph.h"
+#include "llvm/Analysis/CallGraphSCCPass.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/LoopPass.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/RegionPass.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Function.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+
+using namespace llvm;
+
+namespace {
+
+struct FunctionPassPrinter : public FunctionPass {
+ const PassInfo *PassToPrint;
+ raw_ostream &Out;
+ static char ID;
+ std::string PassName;
+
+ FunctionPassPrinter(const PassInfo *PI, raw_ostream &out)
+ : FunctionPass(ID), PassToPrint(PI), Out(out) {
+ std::string PassToPrintName = std::string(PassToPrint->getPassName());
+ PassName = "FunctionPass Printer: " + PassToPrintName;
+ }
+
+ bool runOnFunction(Function &F) override {
+ Out << "Printing analysis '" << PassToPrint->getPassName()
+ << "' for function '" << F.getName() << "':\n";
+
+ // Get and print pass...
+ getAnalysisID<Pass>(PassToPrint->getTypeInfo()).print(Out, F.getParent());
+ return false;
+ }
+
+ StringRef getPassName() const override { return PassName; }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequiredID(PassToPrint->getTypeInfo());
+ AU.setPreservesAll();
+ }
+};
+
+char FunctionPassPrinter::ID = 0;
+
+struct CallGraphSCCPassPrinter : public CallGraphSCCPass {
+ static char ID;
+ const PassInfo *PassToPrint;
+ raw_ostream &Out;
+ std::string PassName;
+
+ CallGraphSCCPassPrinter(const PassInfo *PI, raw_ostream &out)
+ : CallGraphSCCPass(ID), PassToPrint(PI), Out(out) {
+ std::string PassToPrintName = std::string(PassToPrint->getPassName());
+ PassName = "CallGraphSCCPass Printer: " + PassToPrintName;
+ }
+
+ bool runOnSCC(CallGraphSCC &SCC) override {
+ Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n";
+
+ // Get and print pass...
+ for (CallGraphSCC::iterator I = SCC.begin(), E = SCC.end(); I != E; ++I) {
+ Function *F = (*I)->getFunction();
+ if (F)
+ getAnalysisID<Pass>(PassToPrint->getTypeInfo())
+ .print(Out, F->getParent());
+ }
+ return false;
+ }
+
+ StringRef getPassName() const override { return PassName; }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequiredID(PassToPrint->getTypeInfo());
+ AU.setPreservesAll();
+ }
+};
+
+char CallGraphSCCPassPrinter::ID = 0;
+
+struct ModulePassPrinter : public ModulePass {
+ static char ID;
+ const PassInfo *PassToPrint;
+ raw_ostream &Out;
+ std::string PassName;
+
+ ModulePassPrinter(const PassInfo *PI, raw_ostream &out)
+ : ModulePass(ID), PassToPrint(PI), Out(out) {
+ std::string PassToPrintName = std::string(PassToPrint->getPassName());
+ PassName = "ModulePass Printer: " + PassToPrintName;
+ }
+
+ bool runOnModule(Module &M) override {
+ Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n";
+
+ // Get and print pass...
+ getAnalysisID<Pass>(PassToPrint->getTypeInfo()).print(Out, &M);
+ return false;
+ }
+
+ StringRef getPassName() const override { return PassName; }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequiredID(PassToPrint->getTypeInfo());
+ AU.setPreservesAll();
+ }
+};
+
+char ModulePassPrinter::ID = 0;
+
+struct LoopPassPrinter : public LoopPass {
+ static char ID;
+ const PassInfo *PassToPrint;
+ raw_ostream &Out;
+ std::string PassName;
+
+ LoopPassPrinter(const PassInfo *PI, raw_ostream &out)
+ : LoopPass(ID), PassToPrint(PI), Out(out) {
+ std::string PassToPrintName = std::string(PassToPrint->getPassName());
+ PassName = "LoopPass Printer: " + PassToPrintName;
+ }
+
+ bool runOnLoop(Loop *L, LPPassManager &LPM) override {
+ Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n";
+
+ // Get and print pass...
+ getAnalysisID<Pass>(PassToPrint->getTypeInfo())
+ .print(Out, L->getHeader()->getParent()->getParent());
+ return false;
+ }
+
+ StringRef getPassName() const override { return PassName; }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequiredID(PassToPrint->getTypeInfo());
+ AU.setPreservesAll();
+ }
+};
+
+char LoopPassPrinter::ID = 0;
+
+struct RegionPassPrinter : public RegionPass {
+ static char ID;
+ const PassInfo *PassToPrint;
+ raw_ostream &Out;
+ std::string PassName;
+
+ RegionPassPrinter(const PassInfo *PI, raw_ostream &out)
+ : RegionPass(ID), PassToPrint(PI), Out(out) {
+ std::string PassToPrintName = std::string(PassToPrint->getPassName());
+ PassName = "RegionPass Printer: " + PassToPrintName;
+ }
+
+ bool runOnRegion(Region *R, RGPassManager &RGM) override {
+ Out << "Printing analysis '" << PassToPrint->getPassName() << "' for "
+ << "region: '" << R->getNameStr() << "' in function '"
+ << R->getEntry()->getParent()->getName() << "':\n";
+ // Get and print pass...
+ getAnalysisID<Pass>(PassToPrint->getTypeInfo())
+ .print(Out, R->getEntry()->getParent()->getParent());
+ return false;
+ }
+
+ StringRef getPassName() const override { return PassName; }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequiredID(PassToPrint->getTypeInfo());
+ AU.setPreservesAll();
+ }
+};
+
+char RegionPassPrinter::ID = 0;
+
+} // end anonymous namespace
+
+FunctionPass *llvm::createFunctionPassPrinter(const PassInfo *PI,
+ raw_ostream &OS) {
+ return new FunctionPassPrinter(PI, OS);
+}
+
+CallGraphSCCPass *llvm::createCallGraphPassPrinter(const PassInfo *PI,
+ raw_ostream &OS) {
+ return new CallGraphSCCPassPrinter(PI, OS);
+}
+
+ModulePass *llvm::createModulePassPrinter(const PassInfo *PI, raw_ostream &OS) {
+ return new ModulePassPrinter(PI, OS);
+}
+
+LoopPass *llvm::createLoopPassPrinter(const PassInfo *PI, raw_ostream &OS) {
+ return new LoopPassPrinter(PI, OS);
+}
+
+RegionPass *llvm::createRegionPassPrinter(const PassInfo *PI, raw_ostream &OS) {
+ return new RegionPassPrinter(PI, OS);
+}
diff --git a/contrib/libs/llvm14/tools/opt/PassPrinters.h b/contrib/libs/llvm14/tools/opt/PassPrinters.h
new file mode 100644
index 00000000000..a4e1921399f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/opt/PassPrinters.h
@@ -0,0 +1,40 @@
+//=- PassPrinters.h - Utilities to print analysis info for passes -*- C++ -*-=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Utilities to print analysis info for various kinds of passes.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OPT_PASSPRINTERS_H
+#define LLVM_TOOLS_OPT_PASSPRINTERS_H
+
+namespace llvm {
+
+class CallGraphSCCPass;
+class FunctionPass;
+class ModulePass;
+class LoopPass;
+class PassInfo;
+class raw_ostream;
+class RegionPass;
+
+FunctionPass *createFunctionPassPrinter(const PassInfo *PI, raw_ostream &out);
+
+CallGraphSCCPass *createCallGraphPassPrinter(const PassInfo *PI,
+ raw_ostream &out);
+
+ModulePass *createModulePassPrinter(const PassInfo *PI, raw_ostream &out);
+
+LoopPass *createLoopPassPrinter(const PassInfo *PI, raw_ostream &out);
+
+RegionPass *createRegionPassPrinter(const PassInfo *PI, raw_ostream &out);
+
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_OPT_PASSPRINTERS_H
diff --git a/contrib/libs/llvm14/tools/opt/PrintSCC.cpp b/contrib/libs/llvm14/tools/opt/PrintSCC.cpp
new file mode 100644
index 00000000000..1ca52745ff4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/opt/PrintSCC.cpp
@@ -0,0 +1,111 @@
+//===- PrintSCC.cpp - Enumerate SCCs in some key graphs -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides passes to print out SCCs in a CFG or a CallGraph.
+// Normally, you would not use these passes; instead, you would use the
+// scc_iterator directly to enumerate SCCs and process them in some way. These
+// passes serve three purposes:
+//
+// (1) As a reference for how to use the scc_iterator.
+// (2) To print out the SCCs for a CFG or a CallGraph:
+// analyze -print-cfg-sccs to print the SCCs in each CFG of a module.
+// analyze -print-cfg-sccs -stats to print the #SCCs and the maximum SCC size.
+// analyze -print-cfg-sccs -debug > /dev/null to watch the algorithm in action.
+//
+// and similarly:
+// analyze -print-callgraph-sccs [-stats] [-debug] to print SCCs in the CallGraph
+//
+// (3) To test the scc_iterator.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SCCIterator.h"
+#include "llvm/Analysis/CallGraph.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace llvm;
+
+namespace {
+ struct CFGSCC : public FunctionPass {
+ static char ID; // Pass identification, replacement for typeid
+ CFGSCC() : FunctionPass(ID) {}
+ bool runOnFunction(Function& func) override;
+
+ void print(raw_ostream &O, const Module* = nullptr) const override { }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ }
+ };
+
+ struct CallGraphSCC : public ModulePass {
+ static char ID; // Pass identification, replacement for typeid
+ CallGraphSCC() : ModulePass(ID) {}
+
+ // run - Print out SCCs in the call graph for the specified module.
+ bool runOnModule(Module &M) override;
+
+ void print(raw_ostream &O, const Module* = nullptr) const override { }
+
+ // getAnalysisUsage - This pass requires the CallGraph.
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ AU.addRequired<CallGraphWrapperPass>();
+ }
+ };
+}
+
+char CFGSCC::ID = 0;
+static RegisterPass<CFGSCC>
+Y("print-cfg-sccs", "Print SCCs of each function CFG");
+
+char CallGraphSCC::ID = 0;
+static RegisterPass<CallGraphSCC>
+Z("print-callgraph-sccs", "Print SCCs of the Call Graph");
+
+bool CFGSCC::runOnFunction(Function &F) {
+ unsigned sccNum = 0;
+ errs() << "SCCs for Function " << F.getName() << " in PostOrder:";
+ for (scc_iterator<Function*> SCCI = scc_begin(&F); !SCCI.isAtEnd(); ++SCCI) {
+ const std::vector<BasicBlock *> &nextSCC = *SCCI;
+ errs() << "\nSCC #" << ++sccNum << " : ";
+ for (BasicBlock *BB : nextSCC) {
+ BB->printAsOperand(errs(), false);
+ errs() << ", ";
+ }
+ if (nextSCC.size() == 1 && SCCI.hasCycle())
+ errs() << " (Has self-loop).";
+ }
+ errs() << "\n";
+
+ return true;
+}
+
+
+// run - Print out SCCs in the call graph for the specified module.
+bool CallGraphSCC::runOnModule(Module &M) {
+ CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph();
+ unsigned sccNum = 0;
+ errs() << "SCCs for the program in PostOrder:";
+ for (scc_iterator<CallGraph*> SCCI = scc_begin(&CG); !SCCI.isAtEnd();
+ ++SCCI) {
+ const std::vector<CallGraphNode*> &nextSCC = *SCCI;
+ errs() << "\nSCC #" << ++sccNum << " : ";
+ for (std::vector<CallGraphNode*>::const_iterator I = nextSCC.begin(),
+ E = nextSCC.end(); I != E; ++I)
+ errs() << ((*I)->getFunction() ? (*I)->getFunction()->getName()
+ : "external node") << ", ";
+ if (nextSCC.size() == 1 && SCCI.hasCycle())
+ errs() << " (Has self-loop).";
+ }
+ errs() << "\n";
+
+ return true;
+}
diff --git a/contrib/libs/llvm14/tools/opt/opt.cpp b/contrib/libs/llvm14/tools/opt/opt.cpp
new file mode 100644
index 00000000000..7793a547179
--- /dev/null
+++ b/contrib/libs/llvm14/tools/opt/opt.cpp
@@ -0,0 +1,1090 @@
+//===- opt.cpp - The LLVM Modular Optimizer -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Optimizations may be specified an arbitrary number of times on the command
+// line, They are run in the order specified.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BreakpointPrinter.h"
+#include "NewPMDriver.h"
+#include "PassPrinters.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Analysis/CallGraph.h"
+#include "llvm/Analysis/CallGraphSCCPass.h"
+#include "llvm/Analysis/LoopPass.h"
+#include "llvm/Analysis/RegionPass.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LLVMRemarkStreamer.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/LegacyPassNameParser.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/LinkAllIR.h"
+#include "llvm/LinkAllPasses.h"
+#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Remarks/HotnessThresholdParser.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/PluginLoader.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/SystemUtils.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/Coroutines.h"
+#include "llvm/Transforms/IPO/AlwaysInliner.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/IPO/WholeProgramDevirt.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/Debugify.h"
+#include <algorithm>
+#include <memory>
+using namespace llvm;
+using namespace opt_tool;
+
+static codegen::RegisterCodeGenFlags CFG;
+
+// The OptimizationList is automatically populated with registered Passes by the
+// PassNameParser.
+static cl::list<const PassInfo *, bool, PassNameParser> PassList(cl::desc(
+ "Optimizations available (use '-passes=' for the new pass manager)"));
+
+static cl::opt<bool> EnableNewPassManager(
+ "enable-new-pm",
+ cl::desc("Enable the new pass manager, translating "
+ "'opt -foo' to 'opt -passes=foo'. This is strictly for the new PM "
+ "migration, use '-passes=' when possible."),
+ cl::init(LLVM_ENABLE_NEW_PASS_MANAGER));
+
+// This flag specifies a textual description of the optimization pass pipeline
+// to run over the module. This flag switches opt to use the new pass manager
+// infrastructure, completely disabling all of the flags specific to the old
+// pass management.
+static cl::opt<std::string> PassPipeline(
+ "passes",
+ cl::desc(
+ "A textual description of the pass pipeline. To have analysis passes "
+ "available before a certain pass, add 'require<foo-analysis>'."));
+
+static cl::opt<bool> PrintPasses("print-passes",
+ cl::desc("Print available passes that can be "
+ "specified in -passes=foo and exit"));
+
+static cl::opt<std::string>
+InputFilename(cl::Positional, cl::desc("<input bitcode file>"),
+ cl::init("-"), cl::value_desc("filename"));
+
+static cl::opt<std::string>
+OutputFilename("o", cl::desc("Override output filename"),
+ cl::value_desc("filename"));
+
+static cl::opt<bool>
+Force("f", cl::desc("Enable binary output on terminals"));
+
+static cl::opt<bool>
+NoOutput("disable-output",
+ cl::desc("Do not write result bitcode file"), cl::Hidden);
+
+static cl::opt<bool>
+OutputAssembly("S", cl::desc("Write output as LLVM assembly"));
+
+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(
+ "A file in which to write minimized bitcode for the thin link only"));
+
+static cl::opt<bool>
+NoVerify("disable-verify", cl::desc("Do not run the verifier"), cl::Hidden);
+
+static cl::opt<bool> NoUpgradeDebugInfo("disable-upgrade-debug-info",
+ cl::desc("Generate invalid output"),
+ cl::ReallyHidden);
+
+static cl::opt<bool> VerifyEach("verify-each",
+ cl::desc("Verify after each transform"));
+
+static cl::opt<bool>
+ DisableDITypeMap("disable-debug-info-type-map",
+ cl::desc("Don't use a uniquing type map for debug info"));
+
+static cl::opt<bool>
+StripDebug("strip-debug",
+ cl::desc("Strip debugger symbol info from translation unit"));
+
+static cl::opt<bool>
+ StripNamedMetadata("strip-named-metadata",
+ cl::desc("Strip module-level named metadata"));
+
+
+
+static cl::opt<bool>
+ OptLevelO0("O0", cl::desc("Optimization level 0. Similar to clang -O0. "
+ "Use -passes='default<O0>' for the new PM"));
+
+static cl::opt<bool>
+ OptLevelO1("O1", cl::desc("Optimization level 1. Similar to clang -O1. "
+ "Use -passes='default<O1>' for the new PM"));
+
+static cl::opt<bool>
+ OptLevelO2("O2", cl::desc("Optimization level 2. Similar to clang -O2. "
+ "Use -passes='default<O2>' for the new PM"));
+
+static cl::opt<bool>
+ OptLevelOs("Os", cl::desc("Like -O2 but size-conscious. Similar to clang "
+ "-Os. Use -passes='default<Os>' for the new PM"));
+
+static cl::opt<bool> OptLevelOz(
+ "Oz",
+ cl::desc("Like -O2 but optimize for code size above all else. Similar to "
+ "clang -Oz. Use -passes='default<Oz>' for the new PM"));
+
+static cl::opt<bool>
+ OptLevelO3("O3", cl::desc("Optimization level 3. Similar to clang -O3. "
+ "Use -passes='default<O3>' for the new PM"));
+
+static cl::opt<unsigned> CodeGenOptLevel(
+ "codegen-opt-level",
+ cl::desc("Override optimization level for codegen hooks, legacy PM only"));
+
+static cl::opt<std::string>
+TargetTriple("mtriple", cl::desc("Override target triple for module"));
+
+cl::opt<bool> DisableLoopUnrolling(
+ "disable-loop-unrolling",
+ cl::desc("Disable loop unrolling in all relevant passes"), cl::init(false));
+
+static cl::opt<bool> EmitSummaryIndex("module-summary",
+ cl::desc("Emit module summary index"),
+ cl::init(false));
+
+static cl::opt<bool> EmitModuleHash("module-hash", cl::desc("Emit module hash"),
+ cl::init(false));
+
+static cl::opt<bool>
+DisableSimplifyLibCalls("disable-simplify-libcalls",
+ cl::desc("Disable simplify-libcalls"));
+
+static cl::list<std::string>
+DisableBuiltins("disable-builtin",
+ cl::desc("Disable specific target library builtin function"),
+ cl::ZeroOrMore);
+
+static cl::opt<bool>
+ AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization. "
+ "Legacy pass manager only."));
+
+static cl::opt<bool> EnableDebugify(
+ "enable-debugify",
+ cl::desc(
+ "Start the pipeline with debugify and end it with check-debugify"));
+
+static cl::opt<bool> VerifyDebugInfoPreserve(
+ "verify-debuginfo-preserve",
+ cl::desc("Start the pipeline with collecting and end it with checking of "
+ "debug info preservation."));
+
+static cl::opt<bool> VerifyEachDebugInfoPreserve(
+ "verify-each-debuginfo-preserve",
+ cl::desc("Start each pass with collecting and end it with checking of "
+ "debug info preservation."));
+
+static cl::opt<std::string>
+ VerifyDIPreserveExport("verify-di-preserve-export",
+ cl::desc("Export debug info preservation failures into "
+ "specified (JSON) file (should be abs path as we use"
+ " append mode to insert new JSON objects)"),
+ cl::value_desc("filename"), cl::init(""));
+
+static cl::opt<bool>
+PrintBreakpoints("print-breakpoints-for-testing",
+ cl::desc("Print select breakpoints location for testing"));
+
+static cl::opt<std::string> ClDataLayout("data-layout",
+ cl::desc("data layout string to use"),
+ cl::value_desc("layout-string"),
+ cl::init(""));
+
+static cl::opt<bool> PreserveBitcodeUseListOrder(
+ "preserve-bc-uselistorder",
+ cl::desc("Preserve use-list order when writing LLVM bitcode."),
+ cl::init(true), cl::Hidden);
+
+static cl::opt<bool> PreserveAssemblyUseListOrder(
+ "preserve-ll-uselistorder",
+ cl::desc("Preserve use-list order when writing LLVM assembly."),
+ cl::init(false), cl::Hidden);
+
+static cl::opt<bool> RunTwice("run-twice",
+ cl::desc("Run all passes twice, re-using the "
+ "same pass manager (legacy PM only)."),
+ cl::init(false), cl::Hidden);
+
+static cl::opt<bool> DiscardValueNames(
+ "discard-value-names",
+ cl::desc("Discard names from Value (other than GlobalValue)."),
+ cl::init(false), cl::Hidden);
+
+static cl::opt<bool> Coroutines(
+ "enable-coroutines",
+ cl::desc("Enable coroutine passes."),
+ cl::init(false), cl::Hidden);
+
+static cl::opt<bool> TimeTrace(
+ "time-trace",
+ cl::desc("Record time trace"));
+
+static cl::opt<unsigned> TimeTraceGranularity(
+ "time-trace-granularity",
+ cl::desc("Minimum time granularity (in microseconds) traced by time profiler"),
+ cl::init(500), cl::Hidden);
+
+static cl::opt<std::string>
+ TimeTraceFile("time-trace-file",
+ cl::desc("Specify time trace file destination"),
+ cl::value_desc("filename"));
+
+static cl::opt<bool> RemarksWithHotness(
+ "pass-remarks-with-hotness",
+ cl::desc("With PGO, include profile count in optimization remarks"),
+ cl::Hidden);
+
+static cl::opt<Optional<uint64_t>, false, remarks::HotnessThresholdParser>
+ RemarksHotnessThreshold(
+ "pass-remarks-hotness-threshold",
+ cl::desc("Minimum profile count required for "
+ "an optimization remark to be output. "
+ "Use 'auto' to apply the threshold from profile summary."),
+ cl::value_desc("N or 'auto'"), cl::init(0), cl::Hidden);
+
+static cl::opt<std::string>
+ RemarksFilename("pass-remarks-output",
+ cl::desc("Output filename for pass remarks"),
+ cl::value_desc("filename"));
+
+static cl::opt<std::string>
+ RemarksPasses("pass-remarks-filter",
+ cl::desc("Only record optimization remarks from passes whose "
+ "names match the given regular expression"),
+ cl::value_desc("regex"));
+
+static cl::opt<std::string> RemarksFormat(
+ "pass-remarks-format",
+ cl::desc("The format used for serializing remarks (default: YAML)"),
+ cl::value_desc("format"), cl::init("yaml"));
+
+namespace llvm {
+cl::opt<PGOKind>
+ PGOKindFlag("pgo-kind", cl::init(NoPGO), cl::Hidden,
+ cl::desc("The kind of profile guided optimization"),
+ cl::values(clEnumValN(NoPGO, "nopgo", "Do not use PGO."),
+ clEnumValN(InstrGen, "pgo-instr-gen-pipeline",
+ "Instrument the IR to generate profile."),
+ clEnumValN(InstrUse, "pgo-instr-use-pipeline",
+ "Use instrumented profile to guide PGO."),
+ clEnumValN(SampleUse, "pgo-sample-use-pipeline",
+ "Use sampled profile to guide PGO.")));
+cl::opt<std::string> ProfileFile("profile-file",
+ cl::desc("Path to the profile."), cl::Hidden);
+
+cl::opt<CSPGOKind> CSPGOKindFlag(
+ "cspgo-kind", cl::init(NoCSPGO), cl::Hidden,
+ cl::desc("The kind of context sensitive profile guided optimization"),
+ cl::values(
+ clEnumValN(NoCSPGO, "nocspgo", "Do not use CSPGO."),
+ clEnumValN(
+ CSInstrGen, "cspgo-instr-gen-pipeline",
+ "Instrument (context sensitive) the IR to generate profile."),
+ clEnumValN(
+ CSInstrUse, "cspgo-instr-use-pipeline",
+ "Use instrumented (context sensitive) profile to guide PGO.")));
+cl::opt<std::string> CSProfileGenFile(
+ "cs-profilegen-file",
+ cl::desc("Path to the instrumented context sensitive profile."),
+ cl::Hidden);
+} // namespace llvm
+
+static inline void addPass(legacy::PassManagerBase &PM, Pass *P) {
+ // Add the pass to the pass manager...
+ PM.add(P);
+
+ // If we are verifying all of the intermediate steps, add the verifier...
+ if (VerifyEach)
+ PM.add(createVerifierPass());
+}
+
+/// This routine adds optimization passes based on selected optimization level,
+/// OptLevel.
+///
+/// OptLevel - Optimization Level
+static void AddOptimizationPasses(legacy::PassManagerBase &MPM,
+ legacy::FunctionPassManager &FPM,
+ TargetMachine *TM, unsigned OptLevel,
+ unsigned SizeLevel) {
+ if (!NoVerify || VerifyEach)
+ FPM.add(createVerifierPass()); // Verify that input is correct
+
+ PassManagerBuilder Builder;
+ Builder.OptLevel = OptLevel;
+ Builder.SizeLevel = SizeLevel;
+
+ if (OptLevel > 1) {
+ Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel, false);
+ } else {
+ Builder.Inliner = createAlwaysInlinerLegacyPass();
+ }
+ Builder.DisableUnrollLoops = (DisableLoopUnrolling.getNumOccurrences() > 0) ?
+ DisableLoopUnrolling : OptLevel == 0;
+
+ Builder.LoopVectorize = OptLevel > 1 && SizeLevel < 2;
+
+ Builder.SLPVectorize = OptLevel > 1 && SizeLevel < 2;
+
+ if (TM)
+ TM->adjustPassManager(Builder);
+
+ if (Coroutines)
+ addCoroutinePassesToExtensionPoints(Builder);
+
+ switch (PGOKindFlag) {
+ case InstrGen:
+ Builder.EnablePGOInstrGen = true;
+ Builder.PGOInstrGen = ProfileFile;
+ break;
+ case InstrUse:
+ Builder.PGOInstrUse = ProfileFile;
+ break;
+ case SampleUse:
+ Builder.PGOSampleUse = ProfileFile;
+ break;
+ default:
+ break;
+ }
+
+ switch (CSPGOKindFlag) {
+ case CSInstrGen:
+ Builder.EnablePGOCSInstrGen = true;
+ break;
+ case CSInstrUse:
+ Builder.EnablePGOCSInstrUse = true;
+ break;
+ default:
+ break;
+ }
+
+ Builder.populateFunctionPassManager(FPM);
+ Builder.populateModulePassManager(MPM);
+}
+
+//===----------------------------------------------------------------------===//
+// CodeGen-related helper functions.
+//
+
+static CodeGenOpt::Level GetCodeGenOptLevel() {
+ if (CodeGenOptLevel.getNumOccurrences())
+ return static_cast<CodeGenOpt::Level>(unsigned(CodeGenOptLevel));
+ if (OptLevelO1)
+ return CodeGenOpt::Less;
+ if (OptLevelO2)
+ return CodeGenOpt::Default;
+ if (OptLevelO3)
+ return CodeGenOpt::Aggressive;
+ return CodeGenOpt::None;
+}
+
+// Returns the TargetMachine instance or zero if no triple is provided.
+static TargetMachine* GetTargetMachine(Triple TheTriple, StringRef CPUStr,
+ StringRef FeaturesStr,
+ const TargetOptions &Options) {
+ std::string Error;
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error);
+ // Some modules don't specify a triple, and this is okay.
+ if (!TheTarget) {
+ return nullptr;
+ }
+
+ return TheTarget->createTargetMachine(
+ TheTriple.getTriple(), codegen::getCPUStr(), codegen::getFeaturesStr(),
+ Options, codegen::getExplicitRelocModel(),
+ codegen::getExplicitCodeModel(), GetCodeGenOptLevel());
+}
+
+#ifdef BUILD_EXAMPLES
+void initializeExampleIRTransforms(llvm::PassRegistry &Registry);
+#endif
+
+struct TimeTracerRAII {
+ TimeTracerRAII(StringRef ProgramName) {
+ if (TimeTrace)
+ timeTraceProfilerInitialize(TimeTraceGranularity, ProgramName);
+ }
+ ~TimeTracerRAII() {
+ if (TimeTrace) {
+ if (auto E = timeTraceProfilerWrite(TimeTraceFile, OutputFilename)) {
+ handleAllErrors(std::move(E), [&](const StringError &SE) {
+ errs() << SE.getMessage() << "\n";
+ });
+ return;
+ }
+ timeTraceProfilerCleanup();
+ }
+ }
+};
+
+// For use in NPM transition. Currently this contains most codegen-specific
+// passes. Remove passes from here when porting to the NPM.
+// TODO: use a codegen version of PassRegistry.def/PassBuilder::is*Pass() once
+// it exists.
+static bool shouldPinPassToLegacyPM(StringRef Pass) {
+ std::vector<StringRef> PassNameExactToIgnore = {
+ "nvvm-reflect",
+ "nvvm-intr-range",
+ "amdgpu-simplifylib",
+ "amdgpu-usenative",
+ "amdgpu-promote-alloca",
+ "amdgpu-promote-alloca-to-vector",
+ "amdgpu-lower-kernel-attributes",
+ "amdgpu-propagate-attributes-early",
+ "amdgpu-propagate-attributes-late",
+ "amdgpu-unify-metadata",
+ "amdgpu-printf-runtime-binding",
+ "amdgpu-always-inline"};
+ if (llvm::is_contained(PassNameExactToIgnore, Pass))
+ return false;
+
+ std::vector<StringRef> PassNamePrefix = {
+ "x86-", "xcore-", "wasm-", "systemz-", "ppc-", "nvvm-",
+ "nvptx-", "mips-", "lanai-", "hexagon-", "bpf-", "avr-",
+ "thumb2-", "arm-", "si-", "gcn-", "amdgpu-", "aarch64-",
+ "amdgcn-", "polly-", "riscv-"};
+ std::vector<StringRef> PassNameContain = {"ehprepare"};
+ std::vector<StringRef> PassNameExact = {
+ "safe-stack", "cost-model",
+ "codegenprepare", "interleaved-load-combine",
+ "unreachableblockelim", "verify-safepoint-ir",
+ "atomic-expand", "expandvp",
+ "hardware-loops", "type-promotion",
+ "mve-tail-predication", "interleaved-access",
+ "global-merge", "pre-isel-intrinsic-lowering",
+ "expand-reductions", "indirectbr-expand",
+ "generic-to-nvvm", "expandmemcmp",
+ "loop-reduce", "lower-amx-type",
+ "pre-amx-config", "lower-amx-intrinsics",
+ "polyhedral-info", "replace-with-veclib"};
+ for (const auto &P : PassNamePrefix)
+ if (Pass.startswith(P))
+ return true;
+ for (const auto &P : PassNameContain)
+ if (Pass.contains(P))
+ return true;
+ return llvm::is_contained(PassNameExact, Pass);
+}
+
+// For use in NPM transition.
+static bool shouldForceLegacyPM() {
+ for (const auto &P : PassList) {
+ StringRef Arg = P->getPassArgument();
+ if (shouldPinPassToLegacyPM(Arg))
+ return true;
+ }
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// main for opt
+//
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ // Enable debug stream buffering.
+ EnableDebugBuffering = true;
+
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmPrinters();
+ InitializeAllAsmParsers();
+
+ // Initialize passes
+ PassRegistry &Registry = *PassRegistry::getPassRegistry();
+ initializeCore(Registry);
+ initializeCoroutines(Registry);
+ initializeScalarOpts(Registry);
+ initializeObjCARCOpts(Registry);
+ initializeVectorization(Registry);
+ initializeIPO(Registry);
+ initializeAnalysis(Registry);
+ initializeTransformUtils(Registry);
+ initializeInstCombine(Registry);
+ initializeAggressiveInstCombine(Registry);
+ initializeInstrumentation(Registry);
+ initializeTarget(Registry);
+ // For codegen passes, only passes that do IR to IR transformation are
+ // supported.
+ initializeExpandMemCmpPassPass(Registry);
+ initializeScalarizeMaskedMemIntrinLegacyPassPass(Registry);
+ initializeCodeGenPreparePass(Registry);
+ initializeAtomicExpandPass(Registry);
+ initializeRewriteSymbolsLegacyPassPass(Registry);
+ initializeWinEHPreparePass(Registry);
+ initializeDwarfEHPrepareLegacyPassPass(Registry);
+ initializeSafeStackLegacyPassPass(Registry);
+ initializeSjLjEHPreparePass(Registry);
+ initializePreISelIntrinsicLoweringLegacyPassPass(Registry);
+ initializeGlobalMergePass(Registry);
+ initializeIndirectBrExpandPassPass(Registry);
+ initializeInterleavedLoadCombinePass(Registry);
+ initializeInterleavedAccessPass(Registry);
+ initializeEntryExitInstrumenterPass(Registry);
+ initializePostInlineEntryExitInstrumenterPass(Registry);
+ initializeUnreachableBlockElimLegacyPassPass(Registry);
+ initializeExpandReductionsPass(Registry);
+ initializeExpandVectorPredicationPass(Registry);
+ initializeWasmEHPreparePass(Registry);
+ initializeWriteBitcodePassPass(Registry);
+ initializeHardwareLoopsPass(Registry);
+ initializeTypePromotionPass(Registry);
+ initializeReplaceWithVeclibLegacyPass(Registry);
+
+#ifdef BUILD_EXAMPLES
+ initializeExampleIRTransforms(Registry);
+#endif
+
+ cl::ParseCommandLineOptions(argc, argv,
+ "llvm .bc -> .bc modular optimizer and analysis printer\n");
+
+ LLVMContext Context;
+
+ if (AnalyzeOnly && NoOutput) {
+ errs() << argv[0] << ": analyze mode conflicts with no-output mode.\n";
+ return 1;
+ }
+
+ // FIXME: once the legacy PM code is deleted, move runPassPipeline() here and
+ // construct the PassBuilder before parsing IR so we can reuse the same
+ // PassBuilder for print passes.
+ if (PrintPasses) {
+ printPasses(outs());
+ return 0;
+ }
+
+ TimeTracerRAII TimeTracer(argv[0]);
+
+ SMDiagnostic Err;
+
+ Context.setDiscardValueNames(DiscardValueNames);
+ if (!DisableDITypeMap)
+ Context.enableDebugTypeODRUniquing();
+
+ Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr =
+ setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses,
+ RemarksFormat, RemarksWithHotness,
+ RemarksHotnessThreshold);
+ if (Error E = RemarksFileOrErr.takeError()) {
+ errs() << toString(std::move(E)) << '\n';
+ return 1;
+ }
+ std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr);
+
+ // Load the input module...
+ auto SetDataLayout = [](StringRef) -> Optional<std::string> {
+ if (ClDataLayout.empty())
+ return None;
+ return ClDataLayout;
+ };
+ std::unique_ptr<Module> M;
+ if (NoUpgradeDebugInfo)
+ M = parseAssemblyFileWithIndexNoUpgradeDebugInfo(
+ InputFilename, Err, Context, nullptr, SetDataLayout)
+ .Mod;
+ else
+ M = parseIRFile(InputFilename, Err, Context, SetDataLayout);
+
+ if (!M) {
+ Err.print(argv[0], errs());
+ return 1;
+ }
+
+ // Strip debug info before running the verifier.
+ if (StripDebug)
+ StripDebugInfo(*M);
+
+ // Erase module-level named metadata, if requested.
+ if (StripNamedMetadata) {
+ while (!M->named_metadata_empty()) {
+ NamedMDNode *NMD = &*M->named_metadata_begin();
+ M->eraseNamedMetadata(NMD);
+ }
+ }
+
+ // If we are supposed to override the target triple or data layout, do so now.
+ if (!TargetTriple.empty())
+ M->setTargetTriple(Triple::normalize(TargetTriple));
+
+ // Immediately run the verifier to catch any problems before starting up the
+ // pass pipelines. Otherwise we can crash on broken code during
+ // doInitialization().
+ if (!NoVerify && verifyModule(*M, &errs())) {
+ errs() << argv[0] << ": " << InputFilename
+ << ": error: input module is broken!\n";
+ return 1;
+ }
+
+ // Enable testing of whole program devirtualization on this module by invoking
+ // the facility for updating public visibility to linkage unit visibility when
+ // specified by an internal option. This is normally done during LTO which is
+ // not performed via opt.
+ updateVCallVisibilityInModule(*M,
+ /* WholeProgramVisibilityEnabledInLTO */ false,
+ /* DynamicExportSymbols */ {});
+
+ // Figure out what stream we are supposed to write to...
+ std::unique_ptr<ToolOutputFile> Out;
+ std::unique_ptr<ToolOutputFile> ThinLinkOut;
+ if (NoOutput) {
+ if (!OutputFilename.empty())
+ errs() << "WARNING: The -o (output filename) option is ignored when\n"
+ "the --disable-output option is used.\n";
+ } else {
+ // Default to standard output.
+ if (OutputFilename.empty())
+ OutputFilename = "-";
+
+ std::error_code EC;
+ sys::fs::OpenFlags Flags =
+ OutputAssembly ? sys::fs::OF_TextWithCRLF : sys::fs::OF_None;
+ Out.reset(new ToolOutputFile(OutputFilename, EC, Flags));
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return 1;
+ }
+
+ if (!ThinLinkBitcodeFile.empty()) {
+ ThinLinkOut.reset(
+ new ToolOutputFile(ThinLinkBitcodeFile, EC, sys::fs::OF_None));
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return 1;
+ }
+ }
+ }
+
+ Triple ModuleTriple(M->getTargetTriple());
+ std::string CPUStr, FeaturesStr;
+ TargetMachine *Machine = nullptr;
+ const TargetOptions Options =
+ codegen::InitTargetOptionsFromCodeGenFlags(ModuleTriple);
+
+ if (ModuleTriple.getArch()) {
+ CPUStr = codegen::getCPUStr();
+ FeaturesStr = codegen::getFeaturesStr();
+ Machine = GetTargetMachine(ModuleTriple, CPUStr, FeaturesStr, Options);
+ } else if (ModuleTriple.getArchName() != "unknown" &&
+ ModuleTriple.getArchName() != "") {
+ errs() << argv[0] << ": unrecognized architecture '"
+ << ModuleTriple.getArchName() << "' provided.\n";
+ return 1;
+ }
+
+ std::unique_ptr<TargetMachine> TM(Machine);
+
+ // Override function attributes based on CPUStr, FeaturesStr, and command line
+ // flags.
+ codegen::setFunctionAttributes(CPUStr, FeaturesStr, *M);
+
+ // If the output is set to be emitted to standard out, and standard out is a
+ // console, print out a warning message and refuse to do it. We don't
+ // impress anyone by spewing tons of binary goo to a terminal.
+ if (!Force && !NoOutput && !AnalyzeOnly && !OutputAssembly)
+ if (CheckBitcodeOutputToConsole(Out->os()))
+ NoOutput = true;
+
+ if (OutputThinLTOBC)
+ M->addModuleFlag(Module::Error, "EnableSplitLTOUnit", SplitLTOUnit);
+
+ // Add an appropriate TargetLibraryInfo pass for the module's triple.
+ TargetLibraryInfoImpl TLII(ModuleTriple);
+
+ // The -disable-simplify-libcalls flag actually disables all builtin optzns.
+ if (DisableSimplifyLibCalls)
+ TLII.disableAllFunctions();
+ else {
+ // Disable individual builtin functions in TargetLibraryInfo.
+ LibFunc F;
+ for (auto &FuncName : DisableBuiltins)
+ if (TLII.getLibFunc(FuncName, F))
+ TLII.setUnavailable(F);
+ else {
+ errs() << argv[0] << ": cannot disable nonexistent builtin function "
+ << FuncName << '\n';
+ return 1;
+ }
+ }
+
+ // If `-passes=` is specified, use NPM.
+ // If `-enable-new-pm` is specified and there are no codegen passes, use NPM.
+ // e.g. `-enable-new-pm -sroa` will use NPM.
+ // but `-enable-new-pm -codegenprepare` will still revert to legacy PM.
+ if ((EnableNewPassManager && !shouldForceLegacyPM()) ||
+ PassPipeline.getNumOccurrences() > 0) {
+ if (AnalyzeOnly) {
+ errs() << "Cannot specify -analyze under new pass manager, either "
+ "specify '-enable-new-pm=0', or use the corresponding new pass "
+ "manager pass, e.g. '-passes=print<scalar-evolution>'. For a "
+ "full list of passes, see the '--print-passes' flag.\n";
+ return 1;
+ }
+ if (legacy::debugPassSpecified()) {
+ errs()
+ << "-debug-pass does not work with the new PM, either use "
+ "-debug-pass-manager, or use the legacy PM (-enable-new-pm=0)\n";
+ return 1;
+ }
+ if (PassPipeline.getNumOccurrences() > 0 && PassList.size() > 0) {
+ errs()
+ << "Cannot specify passes via both -foo-pass and --passes=foo-pass\n";
+ return 1;
+ }
+ auto NumOLevel = OptLevelO0 + OptLevelO1 + OptLevelO2 + OptLevelO3 +
+ OptLevelOs + OptLevelOz;
+ if (NumOLevel > 1) {
+ errs() << "Cannot specify multiple -O#\n";
+ return 1;
+ }
+ if (NumOLevel > 0 && PassPipeline.getNumOccurrences() > 0) {
+ errs() << "Cannot specify -O# and --passes=, use "
+ "-passes='default<O#>,other-pass'\n";
+ return 1;
+ }
+ std::string Pipeline = PassPipeline;
+
+ SmallVector<StringRef, 4> Passes;
+ if (OptLevelO0)
+ Pipeline = "default<O0>";
+ if (OptLevelO1)
+ Pipeline = "default<O1>";
+ if (OptLevelO2)
+ Pipeline = "default<O2>";
+ if (OptLevelO3)
+ Pipeline = "default<O3>";
+ if (OptLevelOs)
+ Pipeline = "default<Os>";
+ if (OptLevelOz)
+ Pipeline = "default<Oz>";
+ for (const auto &P : PassList)
+ Passes.push_back(P->getPassArgument());
+ OutputKind OK = OK_NoOutput;
+ if (!NoOutput)
+ OK = OutputAssembly
+ ? OK_OutputAssembly
+ : (OutputThinLTOBC ? OK_OutputThinLTOBitcode : OK_OutputBitcode);
+
+ VerifierKind VK = VK_VerifyInAndOut;
+ if (NoVerify)
+ VK = VK_NoVerifier;
+ else if (VerifyEach)
+ VK = VK_VerifyEachPass;
+
+ // The user has asked to use the new pass manager and provided a pipeline
+ // string. Hand off the rest of the functionality to the new code for that
+ // layer.
+ return runPassPipeline(argv[0], *M, TM.get(), &TLII, Out.get(),
+ ThinLinkOut.get(), RemarksFile.get(), Pipeline,
+ Passes, OK, VK, PreserveAssemblyUseListOrder,
+ PreserveBitcodeUseListOrder, EmitSummaryIndex,
+ EmitModuleHash, EnableDebugify)
+ ? 0
+ : 1;
+ }
+
+ // Create a PassManager to hold and optimize the collection of passes we are
+ // about to build. If the -debugify-each option is set, wrap each pass with
+ // the (-check)-debugify passes.
+ DebugifyCustomPassManager Passes;
+ DebugifyStatsMap DIStatsMap;
+ DebugInfoPerPassMap DIPreservationMap;
+ if (DebugifyEach) {
+ Passes.setDebugifyMode(DebugifyMode::SyntheticDebugInfo);
+ Passes.setDIStatsMap(DIStatsMap);
+ } else if (VerifyEachDebugInfoPreserve) {
+ Passes.setDebugifyMode(DebugifyMode::OriginalDebugInfo);
+ Passes.setDIPreservationMap(DIPreservationMap);
+ if (!VerifyDIPreserveExport.empty())
+ Passes.setOrigDIVerifyBugsReportFilePath(VerifyDIPreserveExport);
+ }
+
+ bool AddOneTimeDebugifyPasses =
+ (EnableDebugify && !DebugifyEach) ||
+ (VerifyDebugInfoPreserve && !VerifyEachDebugInfoPreserve);
+
+ Passes.add(new TargetLibraryInfoWrapperPass(TLII));
+
+ // Add internal analysis passes from the target machine.
+ Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis()
+ : TargetIRAnalysis()));
+
+ if (AddOneTimeDebugifyPasses) {
+ if (EnableDebugify) {
+ Passes.setDIStatsMap(DIStatsMap);
+ Passes.add(createDebugifyModulePass());
+ } else if (VerifyDebugInfoPreserve) {
+ Passes.setDIPreservationMap(DIPreservationMap);
+ Passes.add(createDebugifyModulePass(
+ DebugifyMode::OriginalDebugInfo, "",
+ &(Passes.getDebugInfoPerPassMap())));
+ }
+ }
+
+ std::unique_ptr<legacy::FunctionPassManager> FPasses;
+ if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz ||
+ OptLevelO3) {
+ FPasses.reset(new legacy::FunctionPassManager(M.get()));
+ FPasses->add(createTargetTransformInfoWrapperPass(
+ TM ? TM->getTargetIRAnalysis() : TargetIRAnalysis()));
+ }
+
+ if (PrintBreakpoints) {
+ // Default to standard output.
+ if (!Out) {
+ if (OutputFilename.empty())
+ OutputFilename = "-";
+
+ std::error_code EC;
+ Out = std::make_unique<ToolOutputFile>(OutputFilename, EC,
+ sys::fs::OF_None);
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return 1;
+ }
+ }
+ Passes.add(createBreakpointPrinter(Out->os()));
+ NoOutput = true;
+ }
+
+ if (TM) {
+ // FIXME: We should dyn_cast this when supported.
+ auto &LTM = static_cast<LLVMTargetMachine &>(*TM);
+ Pass *TPC = LTM.createPassConfig(Passes);
+ Passes.add(TPC);
+ }
+
+ // Create a new optimization pass for each one specified on the command line
+ for (unsigned i = 0; i < PassList.size(); ++i) {
+ if (OptLevelO0 && OptLevelO0.getPosition() < PassList.getPosition(i)) {
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 0, 0);
+ OptLevelO0 = false;
+ }
+
+ if (OptLevelO1 && OptLevelO1.getPosition() < PassList.getPosition(i)) {
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 1, 0);
+ OptLevelO1 = false;
+ }
+
+ if (OptLevelO2 && OptLevelO2.getPosition() < PassList.getPosition(i)) {
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 0);
+ OptLevelO2 = false;
+ }
+
+ if (OptLevelOs && OptLevelOs.getPosition() < PassList.getPosition(i)) {
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 1);
+ OptLevelOs = false;
+ }
+
+ if (OptLevelOz && OptLevelOz.getPosition() < PassList.getPosition(i)) {
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 2);
+ OptLevelOz = false;
+ }
+
+ if (OptLevelO3 && OptLevelO3.getPosition() < PassList.getPosition(i)) {
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 3, 0);
+ OptLevelO3 = false;
+ }
+
+ const PassInfo *PassInf = PassList[i];
+ Pass *P = nullptr;
+ if (PassInf->getNormalCtor())
+ P = PassInf->getNormalCtor()();
+ else
+ errs() << argv[0] << ": cannot create pass: "
+ << PassInf->getPassName() << "\n";
+ if (P) {
+ PassKind Kind = P->getPassKind();
+ addPass(Passes, P);
+
+ if (AnalyzeOnly) {
+ switch (Kind) {
+ case PT_Region:
+ Passes.add(createRegionPassPrinter(PassInf, Out->os()));
+ break;
+ case PT_Loop:
+ Passes.add(createLoopPassPrinter(PassInf, Out->os()));
+ break;
+ case PT_Function:
+ Passes.add(createFunctionPassPrinter(PassInf, Out->os()));
+ break;
+ case PT_CallGraphSCC:
+ Passes.add(createCallGraphPassPrinter(PassInf, Out->os()));
+ break;
+ default:
+ Passes.add(createModulePassPrinter(PassInf, Out->os()));
+ break;
+ }
+ }
+ }
+ }
+
+ if (OptLevelO0)
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 0, 0);
+
+ if (OptLevelO1)
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 1, 0);
+
+ if (OptLevelO2)
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 0);
+
+ if (OptLevelOs)
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 1);
+
+ if (OptLevelOz)
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 2, 2);
+
+ if (OptLevelO3)
+ AddOptimizationPasses(Passes, *FPasses, TM.get(), 3, 0);
+
+ if (FPasses) {
+ FPasses->doInitialization();
+ for (Function &F : *M)
+ FPasses->run(F);
+ FPasses->doFinalization();
+ }
+
+ // Check that the module is well formed on completion of optimization
+ if (!NoVerify && !VerifyEach)
+ Passes.add(createVerifierPass());
+
+ if (AddOneTimeDebugifyPasses) {
+ if (EnableDebugify)
+ Passes.add(createCheckDebugifyModulePass(false));
+ else if (VerifyDebugInfoPreserve) {
+ if (!VerifyDIPreserveExport.empty())
+ Passes.setOrigDIVerifyBugsReportFilePath(VerifyDIPreserveExport);
+ Passes.add(createCheckDebugifyModulePass(
+ false, "", nullptr, DebugifyMode::OriginalDebugInfo,
+ &(Passes.getDebugInfoPerPassMap()), VerifyDIPreserveExport));
+ }
+ }
+
+ // In run twice mode, we want to make sure the output is bit-by-bit
+ // equivalent if we run the pass manager again, so setup two buffers and
+ // a stream to write to them. Note that llc does something similar and it
+ // may be worth to abstract this out in the future.
+ SmallVector<char, 0> Buffer;
+ SmallVector<char, 0> FirstRunBuffer;
+ std::unique_ptr<raw_svector_ostream> BOS;
+ raw_ostream *OS = nullptr;
+
+ const bool ShouldEmitOutput = !NoOutput && !AnalyzeOnly;
+
+ // Write bitcode or assembly to the output as the last step...
+ if (ShouldEmitOutput || RunTwice) {
+ assert(Out);
+ OS = &Out->os();
+ if (RunTwice) {
+ BOS = std::make_unique<raw_svector_ostream>(Buffer);
+ OS = BOS.get();
+ }
+ if (OutputAssembly) {
+ if (EmitSummaryIndex)
+ report_fatal_error("Text output is incompatible with -module-summary");
+ if (EmitModuleHash)
+ report_fatal_error("Text output is incompatible with -module-hash");
+ Passes.add(createPrintModulePass(*OS, "", PreserveAssemblyUseListOrder));
+ } else if (OutputThinLTOBC)
+ Passes.add(createWriteThinLTOBitcodePass(
+ *OS, ThinLinkOut ? &ThinLinkOut->os() : nullptr));
+ else
+ Passes.add(createBitcodeWriterPass(*OS, PreserveBitcodeUseListOrder,
+ EmitSummaryIndex, EmitModuleHash));
+ }
+
+ // Before executing passes, print the final values of the LLVM options.
+ cl::PrintOptionValues();
+
+ if (!RunTwice) {
+ // Now that we have all of the passes ready, run them.
+ Passes.run(*M);
+ } else {
+ // If requested, run all passes twice with the same pass manager to catch
+ // bugs caused by persistent state in the passes.
+ std::unique_ptr<Module> M2(CloneModule(*M));
+ // Run all passes on the original module first, so the second run processes
+ // the clone to catch CloneModule bugs.
+ Passes.run(*M);
+ FirstRunBuffer = Buffer;
+ Buffer.clear();
+
+ Passes.run(*M2);
+
+ // Compare the two outputs and make sure they're the same
+ assert(Out);
+ if (Buffer.size() != FirstRunBuffer.size() ||
+ (memcmp(Buffer.data(), FirstRunBuffer.data(), Buffer.size()) != 0)) {
+ errs()
+ << "Running the pass manager twice changed the output.\n"
+ "Writing the result of the second run to the specified output.\n"
+ "To generate the one-run comparison binary, just run without\n"
+ "the compile-twice option\n";
+ if (ShouldEmitOutput) {
+ Out->os() << BOS->str();
+ Out->keep();
+ }
+ if (RemarksFile)
+ RemarksFile->keep();
+ return 1;
+ }
+ if (ShouldEmitOutput)
+ Out->os() << BOS->str();
+ }
+
+ if (DebugifyEach && !DebugifyExport.empty())
+ exportDebugifyStats(DebugifyExport, Passes.getDebugifyStatsMap());
+
+ // Declare success.
+ if (!NoOutput || PrintBreakpoints)
+ Out->keep();
+
+ if (RemarksFile)
+ RemarksFile->keep();
+
+ if (ThinLinkOut)
+ ThinLinkOut->keep();
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/opt/ya.make b/contrib/libs/llvm14/tools/opt/ya.make
new file mode 100644
index 00000000000..83fdf2510e7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/opt/ya.make
@@ -0,0 +1,98 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/CodeGen
+ contrib/libs/llvm14/lib/CodeGen/AsmPrinter
+ contrib/libs/llvm14/lib/CodeGen/GlobalISel
+ contrib/libs/llvm14/lib/CodeGen/SelectionDAG
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Extensions
+ contrib/libs/llvm14/lib/Frontend/OpenMP
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Passes
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/AArch64
+ contrib/libs/llvm14/lib/Target/AArch64/AsmParser
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM
+ contrib/libs/llvm14/lib/Target/ARM/AsmParser
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF
+ contrib/libs/llvm14/lib/Target/BPF/AsmParser
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC
+ contrib/libs/llvm14/lib/Target/PowerPC/AsmParser
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86
+ contrib/libs/llvm14/lib/Target/X86/AsmParser
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+ contrib/libs/llvm14/lib/Transforms/AggressiveInstCombine
+ contrib/libs/llvm14/lib/Transforms/CFGuard
+ contrib/libs/llvm14/lib/Transforms/Coroutines
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Instrumentation
+ contrib/libs/llvm14/lib/Transforms/ObjCARC
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+ contrib/libs/llvm14/tools/polly/lib
+ contrib/libs/llvm14/tools/polly/lib/External/isl
+ contrib/libs/llvm14/tools/polly/lib/External/ppcg
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/opt
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ AnalysisWrappers.cpp
+ BreakpointPrinter.cpp
+ GraphPrinters.cpp
+ NewPMDriver.cpp
+ PassPrinters.cpp
+ PrintSCC.cpp
+ opt.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/polly/CREDITS.txt b/contrib/libs/llvm14/tools/polly/CREDITS.txt
new file mode 100644
index 00000000000..98fb9cd671a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/CREDITS.txt
@@ -0,0 +1,42 @@
+This file is a partial list of people who have contributed to Polly.
+If you have contributed a patch or made some other contribution to
+Polly, please submit a patch to this file to add yourself, and it will be
+done!
+
+The list is sorted by surname and formatted to allow easy grepping and
+beautification by scripts. The fields are: name (N), email (E), web-address
+(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
+(S).
+
+N: Raghesh Aloor
+E: raghesh.a@gmail.com
+D: OpenMP code generation
+D: Google Summer of Code student 2011
+
+N: Tobias Grosser
+E: tobias@grosser.es
+W: http://www.grosser.es
+D: Co-founder, design of the overall architecture
+
+N: Yabin Hu
+E: yabin.hwu@gmail.com
+D: GPGPU code generation
+D: Google Summer of Code student 2012, 2014
+
+N: Andreas Simbuerger
+E: simbuerg@fim.uni-passau.de
+W: http://www.infosun.fim.uni-passau.de/cl/staff/simbuerger/
+D: Profiling infrastructure
+
+N: Hongbin Zheng
+E: etherzhhb@gmail.com
+D: Co-founder
+D: scop detection, automake/cmake infrastructure, scopinfo, scoppasses, ...
+D: Google Summer of Code student 2010
+
+N: Johannes Doerfert
+E: doerfert@cs.uni-saarland.de
+E: jdoerfert@codeaurora.org
+W: http://www.cdl.uni-saarland.de/people/doerfert/
+P: http://www.cdl.uni-saarland.de/people/doerfert/JohannesDoerfert.asc
+D: reductions, general infrastructure
diff --git a/contrib/libs/llvm14/tools/polly/LICENSE.TXT b/contrib/libs/llvm14/tools/polly/LICENSE.TXT
new file mode 100644
index 00000000000..51f1de3f0b0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/LICENSE.TXT
@@ -0,0 +1,273 @@
+==============================================================================
+The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
+==============================================================================
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+---- LLVM Exceptions to the Apache 2.0 License ----
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into an Object form of such source code, you
+may redistribute such embedded portions in such Object form without complying
+with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
+
+In addition, if you combine or link compiled forms of this Software with
+software that is licensed under the GPLv2 ("Combined Software") and if a
+court of competent jurisdiction determines that the patent provision (Section
+3), the indemnity provision (Section 9) or other Section of the License
+conflicts with the conditions of the GPLv2, you may retroactively and
+prospectively choose to deem waived or otherwise exclude such Section(s) of
+the License, but only in their entirety and only with respect to the Combined
+Software.
+
+==============================================================================
+Software from third parties included in the LLVM Project:
+==============================================================================
+The LLVM Project contains third party software which is under different license
+terms. All such code will be identified clearly using at least one of two
+mechanisms:
+1) It will be in a separate directory tree with its own `LICENSE.txt` or
+ `LICENSE` file at the top containing the specific license and restrictions
+ which apply to that software, or
+2) It will contain specific license and restriction terms at the top of every
+ file.
+
+==============================================================================
+Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
+==============================================================================
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2009-2019 Polly Team
+All rights reserved.
+
+Developed by:
+
+ Polly Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimers.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimers in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the names of the Polly Team, copyright holders, nor the names of
+ its contributors may be used to endorse or promote products derived from
+ this Software without specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
diff --git a/contrib/libs/llvm14/tools/polly/README b/contrib/libs/llvm14/tools/polly/README
new file mode 100644
index 00000000000..09a4a546188
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/README
@@ -0,0 +1,12 @@
+Polly - Polyhedral optimizations for LLVM
+-----------------------------------------
+http://polly.llvm.org/
+
+Polly uses a mathematical representation, the polyhedral model, to represent and
+transform loops and other control flow structures. Using an abstract
+representation it is possible to reason about transformations in a more general
+way and to use highly optimized linear programming libraries to figure out the
+optimal loop structure. These transformations can be used to do constant
+propagation through arrays, remove dead loop iterations, optimize loops for
+cache locality, optimize arrays, apply advanced automatic parallelization, drive
+vectorization, or they can be used to do software pipelining.
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Canonicalization.h b/contrib/libs/llvm14/tools/polly/include/polly/Canonicalization.h
new file mode 100644
index 00000000000..03f277e4e91
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Canonicalization.h
@@ -0,0 +1,37 @@
+//===--- Canonicalization.h - Set of canonicalization passes ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_CANONICALIZATION_H
+#define POLLY_CANONICALIZATION_H
+
+#include "llvm/Passes/PassBuilder.h"
+
+namespace llvm {
+namespace legacy {
+class PassManagerBase;
+}
+} // namespace llvm
+
+namespace polly {
+
+/// Schedule a set of canonicalization passes to prepare for Polly.
+///
+/// The set of optimization passes was partially taken/copied from the
+/// set of default optimization passes in LLVM. It is used to bring the code
+/// into a canonical form that simplifies the analysis and optimization passes
+/// of Polly. The set of optimization passes scheduled here is probably not yet
+/// optimal. TODO: Optimize the set of canonicalization passes.
+void registerCanonicalicationPasses(llvm::legacy::PassManagerBase &PM);
+
+llvm::FunctionPassManager
+buildCanonicalicationPassesForNPM(llvm::ModulePassManager &MPM,
+ llvm::OptimizationLevel Level);
+
+} // namespace polly
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/BlockGenerators.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/BlockGenerators.h
new file mode 100644
index 00000000000..9c9fa60fba6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/BlockGenerators.h
@@ -0,0 +1,996 @@
+//===-BlockGenerators.h - Helper to generate code for statements-*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the BlockGenerator and VectorBlockGenerator classes, which
+// generate sequential code and vectorized code for a polyhedral statement,
+// respectively.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_BLOCK_GENERATORS_H
+#define POLLY_BLOCK_GENERATORS_H
+
+#include "polly/CodeGen/IRBuilder.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "isl/isl-noexceptions.h"
+
+namespace polly {
+using llvm::AllocaInst;
+using llvm::ArrayRef;
+using llvm::AssertingVH;
+using llvm::BasicBlock;
+using llvm::BinaryOperator;
+using llvm::CmpInst;
+using llvm::DataLayout;
+using llvm::DenseMap;
+using llvm::DominatorTree;
+using llvm::Function;
+using llvm::Instruction;
+using llvm::LoadInst;
+using llvm::Loop;
+using llvm::LoopInfo;
+using llvm::LoopToScevMapT;
+using llvm::MapVector;
+using llvm::PHINode;
+using llvm::ScalarEvolution;
+using llvm::SetVector;
+using llvm::SmallVector;
+using llvm::StoreInst;
+using llvm::StringRef;
+using llvm::Type;
+using llvm::UnaryInstruction;
+using llvm::Value;
+
+class MemoryAccess;
+class ScopArrayInfo;
+class IslExprBuilder;
+
+/// Generate a new basic block for a polyhedral statement.
+class BlockGenerator {
+public:
+ typedef llvm::SmallVector<ValueMapT, 8> VectorValueMapT;
+
+ /// Map types to resolve scalar dependences.
+ ///
+ ///@{
+ using AllocaMapTy = DenseMap<const ScopArrayInfo *, AssertingVH<AllocaInst>>;
+
+ /// Simple vector of instructions to store escape users.
+ using EscapeUserVectorTy = SmallVector<Instruction *, 4>;
+
+ /// Map type to resolve escaping users for scalar instructions.
+ ///
+ /// @see The EscapeMap member.
+ using EscapeUsersAllocaMapTy =
+ MapVector<Instruction *,
+ std::pair<AssertingVH<Value>, EscapeUserVectorTy>>;
+
+ ///@}
+
+ /// Create a generator for basic blocks.
+ ///
+ /// @param Builder The LLVM-IR Builder used to generate the statement. The
+ /// code is generated at the location, the Builder points
+ /// to.
+ /// @param LI The loop info for the current function
+ /// @param SE The scalar evolution info for the current function
+ /// @param DT The dominator tree of this function.
+ /// @param ScalarMap Map from scalars to their demoted location.
+ /// @param EscapeMap Map from scalars to their escape users and locations.
+ /// @param GlobalMap A mapping from llvm::Values used in the original scop
+ /// region to a new set of llvm::Values. Each reference to
+ /// an original value appearing in this mapping is replaced
+ /// with the new value it is mapped to.
+ /// @param ExprBuilder An expression builder to generate new access functions.
+ /// @param StartBlock The first basic block after the RTC.
+ BlockGenerator(PollyIRBuilder &Builder, LoopInfo &LI, ScalarEvolution &SE,
+ DominatorTree &DT, AllocaMapTy &ScalarMap,
+ EscapeUsersAllocaMapTy &EscapeMap, ValueMapT &GlobalMap,
+ IslExprBuilder *ExprBuilder, BasicBlock *StartBlock);
+
+ /// Copy the basic block.
+ ///
+ /// This copies the entire basic block and updates references to old values
+ /// with references to new values, as defined by GlobalMap.
+ ///
+ /// @param Stmt The block statement to code generate.
+ /// @param LTS A map from old loops to new induction variables as
+ /// SCEVs.
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ void copyStmt(ScopStmt &Stmt, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses);
+
+ /// Remove a ScopArrayInfo's allocation from the ScalarMap.
+ ///
+ /// This function allows to remove values from the ScalarMap. This is useful
+ /// if the corresponding alloca instruction will be deleted (or moved into
+ /// another module), as without removing these values the underlying
+ /// AssertingVH will trigger due to us still keeping reference to this
+ /// scalar.
+ ///
+ /// @param Array The array for which the alloca was generated.
+ void freeScalarAlloc(ScopArrayInfo *Array) { ScalarMap.erase(Array); }
+
+ /// Return the alloca for @p Access.
+ ///
+ /// If no alloca was mapped for @p Access a new one is created.
+ ///
+ /// @param Access The memory access for which to generate the alloca.
+ ///
+ /// @returns The alloca for @p Access or a replacement value taken from
+ /// GlobalMap.
+ Value *getOrCreateAlloca(const MemoryAccess &Access);
+
+ /// Return the alloca for @p Array.
+ ///
+ /// If no alloca was mapped for @p Array a new one is created.
+ ///
+ /// @param Array The array for which to generate the alloca.
+ ///
+ /// @returns The alloca for @p Array or a replacement value taken from
+ /// GlobalMap.
+ Value *getOrCreateAlloca(const ScopArrayInfo *Array);
+
+ /// Finalize the code generation for the SCoP @p S.
+ ///
+ /// This will initialize and finalize the scalar variables we demoted during
+ /// the code generation.
+ ///
+ /// @see createScalarInitialization(Scop &)
+ /// @see createScalarFinalization(Region &)
+ void finalizeSCoP(Scop &S);
+
+ /// An empty destructor
+ virtual ~BlockGenerator() {}
+
+ BlockGenerator(const BlockGenerator &) = default;
+
+protected:
+ PollyIRBuilder &Builder;
+ LoopInfo &LI;
+ ScalarEvolution &SE;
+ IslExprBuilder *ExprBuilder;
+
+ /// The dominator tree of this function.
+ DominatorTree &DT;
+
+ /// The entry block of the current function.
+ BasicBlock *EntryBB;
+
+ /// Map to resolve scalar dependences for PHI operands and scalars.
+ ///
+ /// When translating code that contains scalar dependences as they result from
+ /// inter-block scalar dependences (including the use of data carrying PHI
+ /// nodes), we do not directly regenerate in-register SSA code, but instead
+ /// allocate some stack memory through which these scalar values are passed.
+ /// Only a later pass of -mem2reg will then (re)introduce in-register
+ /// computations.
+ ///
+ /// To keep track of the memory location(s) used to store the data computed by
+ /// a given SSA instruction, we use the map 'ScalarMap'. ScalarMap maps a
+ /// given ScopArrayInfo to the junk of stack allocated memory, that is
+ /// used for code generation.
+ ///
+ /// Up to two different ScopArrayInfo objects are associated with each
+ /// llvm::Value:
+ ///
+ /// MemoryType::Value objects are used for normal scalar dependences that go
+ /// from a scalar definition to its use. Such dependences are lowered by
+ /// directly writing the value an instruction computes into the corresponding
+ /// chunk of memory and reading it back from this chunk of memory right before
+ /// every use of this original scalar value. The memory allocations for
+ /// MemoryType::Value objects end with '.s2a'.
+ ///
+ /// MemoryType::PHI (and MemoryType::ExitPHI) objects are used to model PHI
+ /// nodes. For each PHI nodes we introduce, besides the Array of type
+ /// MemoryType::Value, a second chunk of memory into which we write at the end
+ /// of each basic block preceding the PHI instruction the value passed
+ /// through this basic block. At the place where the PHI node is executed, we
+ /// replace the PHI node with a load from the corresponding MemoryType::PHI
+ /// memory location. The memory allocations for MemoryType::PHI end with
+ /// '.phiops'.
+ ///
+ /// Example:
+ ///
+ /// Input C Code
+ /// ============
+ ///
+ /// S1: x1 = ...
+ /// for (i=0...N) {
+ /// S2: x2 = phi(x1, add)
+ /// S3: add = x2 + 42;
+ /// }
+ /// S4: print(x1)
+ /// print(x2)
+ /// print(add)
+ ///
+ ///
+ /// Unmodified IR IR After expansion
+ /// ============= ==================
+ ///
+ /// S1: x1 = ... S1: x1 = ...
+ /// x1.s2a = s1
+ /// x2.phiops = s1
+ /// | |
+ /// | <--<--<--<--< | <--<--<--<--<
+ /// | / \ | / \ .
+ /// V V \ V V \ .
+ /// S2: x2 = phi (x1, add) | S2: x2 = x2.phiops |
+ /// | x2.s2a = x2 |
+ /// | |
+ /// S3: add = x2 + 42 | S3: add = x2 + 42 |
+ /// | add.s2a = add |
+ /// | x2.phiops = add |
+ /// | \ / | \ /
+ /// | \ / | \ /
+ /// | >-->-->-->--> | >-->-->-->-->
+ /// V V
+ ///
+ /// S4: x1 = x1.s2a
+ /// S4: ... = x1 ... = x1
+ /// x2 = x2.s2a
+ /// ... = x2 ... = x2
+ /// add = add.s2a
+ /// ... = add ... = add
+ ///
+ /// ScalarMap = { x1:Value -> x1.s2a, x2:Value -> x2.s2a,
+ /// add:Value -> add.s2a, x2:PHI -> x2.phiops }
+ ///
+ /// ??? Why does a PHI-node require two memory chunks ???
+ ///
+ /// One may wonder why a PHI node requires two memory chunks and not just
+ /// all data is stored in a single location. The following example tries
+ /// to store all data in .s2a and drops the .phiops location:
+ ///
+ /// S1: x1 = ...
+ /// x1.s2a = s1
+ /// x2.s2a = s1 // use .s2a instead of .phiops
+ /// |
+ /// | <--<--<--<--<
+ /// | / \ .
+ /// V V \ .
+ /// S2: x2 = x2.s2a | // value is same as above, but read
+ /// | // from .s2a
+ /// |
+ /// x2.s2a = x2 | // store into .s2a as normal
+ /// |
+ /// S3: add = x2 + 42 |
+ /// add.s2a = add |
+ /// x2.s2a = add | // use s2a instead of .phiops
+ /// | \ / // !!! This is wrong, as x2.s2a now
+ /// | >-->-->-->--> // contains add instead of x2.
+ /// V
+ ///
+ /// S4: x1 = x1.s2a
+ /// ... = x1
+ /// x2 = x2.s2a // !!! We now read 'add' instead of
+ /// ... = x2 // 'x2'
+ /// add = add.s2a
+ /// ... = add
+ ///
+ /// As visible in the example, the SSA value of the PHI node may still be
+ /// needed _after_ the basic block, which could conceptually branch to the
+ /// PHI node, has been run and has overwritten the PHI's old value. Hence, a
+ /// single memory location is not enough to code-generate a PHI node.
+ ///
+ /// Memory locations used for the special PHI node modeling.
+ AllocaMapTy &ScalarMap;
+
+ /// Map from instructions to their escape users as well as the alloca.
+ EscapeUsersAllocaMapTy &EscapeMap;
+
+ /// A map from llvm::Values referenced in the old code to a new set of
+ /// llvm::Values, which is used to replace these old values during
+ /// code generation.
+ ValueMapT &GlobalMap;
+
+ /// The first basic block after the RTC.
+ BasicBlock *StartBlock;
+
+ /// Split @p BB to create a new one we can use to clone @p BB in.
+ BasicBlock *splitBB(BasicBlock *BB);
+
+ /// Copy the given basic block.
+ ///
+ /// @param Stmt The statement to code generate.
+ /// @param BB The basic block to code generate.
+ /// @param BBMap A mapping from old values to their new values in this
+ /// block.
+ /// @param LTS A map from old loops to new induction variables as
+ /// SCEVs.
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ ///
+ /// @returns The copy of the basic block.
+ BasicBlock *copyBB(ScopStmt &Stmt, BasicBlock *BB, ValueMapT &BBMap,
+ LoopToScevMapT &LTS, isl_id_to_ast_expr *NewAccesses);
+
+ /// Copy the given basic block.
+ ///
+ /// @param Stmt The statement to code generate.
+ /// @param BB The basic block to code generate.
+ /// @param BBCopy The new basic block to generate code in.
+ /// @param BBMap A mapping from old values to their new values in this
+ /// block.
+ /// @param LTS A map from old loops to new induction variables as
+ /// SCEVs.
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ void copyBB(ScopStmt &Stmt, BasicBlock *BB, BasicBlock *BBCopy,
+ ValueMapT &BBMap, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses);
+
+ /// Generate reload of scalars demoted to memory and needed by @p Stmt.
+ ///
+ /// @param Stmt The statement we generate code for.
+ /// @param LTS A mapping from loops virtual canonical induction
+ /// variable to their new values.
+ /// @param BBMap A mapping from old values to their new values in this block.
+ /// @param NewAccesses A map from memory access ids to new ast expressions.
+ void generateScalarLoads(ScopStmt &Stmt, LoopToScevMapT &LTS,
+ ValueMapT &BBMap,
+ __isl_keep isl_id_to_ast_expr *NewAccesses);
+
+ /// When statement tracing is enabled, build the print instructions for
+ /// printing the current statement instance.
+ ///
+ /// The printed output looks like:
+ ///
+ /// Stmt1(0)
+ ///
+ /// If printing of scalars is enabled, it also appends the value of each
+ /// scalar to the line:
+ ///
+ /// Stmt1(0) %i=1 %sum=5
+ ///
+ /// @param Stmt The statement we generate code for.
+ /// @param LTS A mapping from loops virtual canonical induction
+ /// variable to their new values.
+ /// @param BBMap A mapping from old values to their new values in this block.
+ void generateBeginStmtTrace(ScopStmt &Stmt, LoopToScevMapT &LTS,
+ ValueMapT &BBMap);
+
+ /// Generate instructions that compute whether one instance of @p Set is
+ /// executed.
+ ///
+ /// @param Stmt The statement we generate code for.
+ /// @param Subdomain A set in the space of @p Stmt's domain. Elements not in
+ /// @p Stmt's domain are ignored.
+ ///
+ /// @return An expression of type i1, generated into the current builder
+ /// position, that evaluates to 1 if the executed instance is part of
+ /// @p Set.
+ Value *buildContainsCondition(ScopStmt &Stmt, const isl::set &Subdomain);
+
+ /// Generate code that executes in a subset of @p Stmt's domain.
+ ///
+ /// @param Stmt The statement we generate code for.
+ /// @param Subdomain The condition for some code to be executed.
+ /// @param Subject A name for the code that is executed
+ /// conditionally. Used to name new basic blocks and
+ /// instructions.
+ /// @param GenThenFunc Callback which generates the code to be executed
+ /// when the current executed instance is in @p Set. The
+ /// IRBuilder's position is moved to within the block that
+ /// executes conditionally for this callback.
+ void generateConditionalExecution(ScopStmt &Stmt, const isl::set &Subdomain,
+ StringRef Subject,
+ const std::function<void()> &GenThenFunc);
+
+ /// Generate the scalar stores for the given statement.
+ ///
+ /// After the statement @p Stmt was copied all inner-SCoP scalar dependences
+ /// starting in @p Stmt (hence all scalar write accesses in @p Stmt) need to
+ /// be demoted to memory.
+ ///
+ /// @param Stmt The statement we generate code for.
+ /// @param LTS A mapping from loops virtual canonical induction
+ /// variable to their new values
+ /// (for values recalculated in the new ScoP, but not
+ /// within this basic block)
+ /// @param BBMap A mapping from old values to their new values in this block.
+ /// @param NewAccesses A map from memory access ids to new ast expressions.
+ virtual void generateScalarStores(ScopStmt &Stmt, LoopToScevMapT &LTS,
+ ValueMapT &BBMap,
+ __isl_keep isl_id_to_ast_expr *NewAccesses);
+
+ /// Handle users of @p Array outside the SCoP.
+ ///
+ /// @param S The current SCoP.
+ /// @param Inst The ScopArrayInfo to handle.
+ void handleOutsideUsers(const Scop &S, ScopArrayInfo *Array);
+
+ /// Find scalar statements that have outside users.
+ ///
+ /// We register these scalar values to later update subsequent scalar uses of
+ /// these values to either use the newly computed value from within the scop
+ /// (if the scop was executed) or the unchanged original code (if the run-time
+ /// check failed).
+ ///
+ /// @param S The scop for which to find the outside users.
+ void findOutsideUsers(Scop &S);
+
+ /// Initialize the memory of demoted scalars.
+ ///
+ /// @param S The scop for which to generate the scalar initializers.
+ void createScalarInitialization(Scop &S);
+
+ /// Create exit PHI node merges for PHI nodes with more than two edges
+ /// from inside the scop.
+ ///
+ /// For scops which have a PHI node in the exit block that has more than two
+ /// incoming edges from inside the scop region, we require some special
+ /// handling to understand which of the possible values will be passed to the
+ /// PHI node from inside the optimized version of the scop. To do so ScopInfo
+ /// models the possible incoming values as write accesses of the ScopStmts.
+ ///
+ /// This function creates corresponding code to reload the computed outgoing
+ /// value from the stack slot it has been stored into and to pass it on to the
+ /// PHI node in the original exit block.
+ ///
+ /// @param S The scop for which to generate the exiting PHI nodes.
+ void createExitPHINodeMerges(Scop &S);
+
+ /// Promote the values of demoted scalars after the SCoP.
+ ///
+ /// If a scalar value was used outside the SCoP we need to promote the value
+ /// stored in the memory cell allocated for that scalar and combine it with
+ /// the original value in the non-optimized SCoP.
+ void createScalarFinalization(Scop &S);
+
+ /// Try to synthesize a new value
+ ///
+ /// Given an old value, we try to synthesize it in a new context from its
+ /// original SCEV expression. We start from the original SCEV expression,
+ /// then replace outdated parameter and loop references, and finally
+ /// expand it to code that computes this updated expression.
+ ///
+ /// @param Stmt The statement to code generate
+ /// @param Old The old Value
+ /// @param BBMap A mapping from old values to their new values
+ /// (for values recalculated within this basic block)
+ /// @param LTS A mapping from loops virtual canonical induction
+ /// variable to their new values
+ /// (for values recalculated in the new ScoP, but not
+ /// within this basic block)
+ /// @param L The loop that surrounded the instruction that referenced
+ /// this value in the original code. This loop is used to
+ /// evaluate the scalar evolution at the right scope.
+ ///
+ /// @returns o A newly synthesized value.
+ /// o NULL, if synthesizing the value failed.
+ Value *trySynthesizeNewValue(ScopStmt &Stmt, Value *Old, ValueMapT &BBMap,
+ LoopToScevMapT &LTS, Loop *L) const;
+
+ /// Get the new version of a value.
+ ///
+ /// Given an old value, we first check if a new version of this value is
+ /// available in the BBMap or GlobalMap. In case it is not and the value can
+ /// be recomputed using SCEV, we do so. If we can not recompute a value
+ /// using SCEV, but we understand that the value is constant within the scop,
+ /// we return the old value. If the value can still not be derived, this
+ /// function will assert.
+ ///
+ /// @param Stmt The statement to code generate.
+ /// @param Old The old Value.
+ /// @param BBMap A mapping from old values to their new values
+ /// (for values recalculated within this basic block).
+ /// @param LTS A mapping from loops virtual canonical induction
+ /// variable to their new values
+ /// (for values recalculated in the new ScoP, but not
+ /// within this basic block).
+ /// @param L The loop that surrounded the instruction that referenced
+ /// this value in the original code. This loop is used to
+ /// evaluate the scalar evolution at the right scope.
+ ///
+ /// @returns o The old value, if it is still valid.
+ /// o The new value, if available.
+ /// o NULL, if no value is found.
+ Value *getNewValue(ScopStmt &Stmt, Value *Old, ValueMapT &BBMap,
+ LoopToScevMapT &LTS, Loop *L) const;
+
+ void copyInstScalar(ScopStmt &Stmt, Instruction *Inst, ValueMapT &BBMap,
+ LoopToScevMapT &LTS);
+
+ /// Get the innermost loop that surrounds the statement @p Stmt.
+ Loop *getLoopForStmt(const ScopStmt &Stmt) const;
+
+ /// Generate the operand address
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ Value *generateLocationAccessed(ScopStmt &Stmt, MemAccInst Inst,
+ ValueMapT &BBMap, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses);
+
+ /// Generate the operand address.
+ ///
+ /// @param Stmt The statement to generate code for.
+ /// @param L The innermost loop that surrounds the statement.
+ /// @param Pointer If the access expression is not changed (ie. not found
+ /// in @p LTS), use this Pointer from the original code
+ /// instead.
+ /// @param BBMap A mapping from old values to their new values.
+ /// @param LTS A mapping from loops virtual canonical induction
+ /// variable to their new values.
+ /// @param NewAccesses Ahead-of-time generated access expressions.
+ /// @param Id Identifier of the MemoryAccess to generate.
+ /// @param ExpectedType The type the returned value should have.
+ ///
+ /// @return The generated address.
+ Value *generateLocationAccessed(ScopStmt &Stmt, Loop *L, Value *Pointer,
+ ValueMapT &BBMap, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses,
+ __isl_take isl_id *Id, Type *ExpectedType);
+
+ /// Generate the pointer value that is accesses by @p Access.
+ ///
+ /// For write accesses, generate the target address. For read accesses,
+ /// generate the source address.
+ /// The access can be either an array access or a scalar access. In the first
+ /// case, the returned address will point to an element into that array. In
+ /// the scalar case, an alloca is used.
+ /// If a new AccessRelation is set for the MemoryAccess, the new relation will
+ /// be used.
+ ///
+ /// @param Access The access to generate a pointer for.
+ /// @param L The innermost loop that surrounds the statement.
+ /// @param LTS A mapping from loops virtual canonical induction
+ /// variable to their new values.
+ /// @param BBMap A mapping from old values to their new values.
+ /// @param NewAccesses A map from memory access ids to new ast expressions.
+ ///
+ /// @return The generated address.
+ Value *getImplicitAddress(MemoryAccess &Access, Loop *L, LoopToScevMapT &LTS,
+ ValueMapT &BBMap,
+ __isl_keep isl_id_to_ast_expr *NewAccesses);
+
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ Value *generateArrayLoad(ScopStmt &Stmt, LoadInst *load, ValueMapT &BBMap,
+ LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses);
+
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ void generateArrayStore(ScopStmt &Stmt, StoreInst *store, ValueMapT &BBMap,
+ LoopToScevMapT &LTS, isl_id_to_ast_expr *NewAccesses);
+
+ /// Copy a single PHI instruction.
+ ///
+ /// The implementation in the BlockGenerator is trivial, however it allows
+ /// subclasses to handle PHIs different.
+ virtual void copyPHIInstruction(ScopStmt &, PHINode *, ValueMapT &,
+ LoopToScevMapT &) {}
+
+ /// Copy a single Instruction.
+ ///
+ /// This copies a single Instruction and updates references to old values
+ /// with references to new values, as defined by GlobalMap and BBMap.
+ ///
+ /// @param Stmt The statement to code generate.
+ /// @param Inst The instruction to copy.
+ /// @param BBMap A mapping from old values to their new values
+ /// (for values recalculated within this basic block).
+ /// @param GlobalMap A mapping from old values to their new values
+ /// (for values recalculated in the new ScoP, but not
+ /// within this basic block).
+ /// @param LTS A mapping from loops virtual canonical induction
+ /// variable to their new values
+ /// (for values recalculated in the new ScoP, but not
+ /// within this basic block).
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ void copyInstruction(ScopStmt &Stmt, Instruction *Inst, ValueMapT &BBMap,
+ LoopToScevMapT &LTS, isl_id_to_ast_expr *NewAccesses);
+
+ /// Helper to determine if @p Inst can be synthesized in @p Stmt.
+ ///
+ /// @returns false, iff @p Inst can be synthesized in @p Stmt.
+ bool canSyntheziseInStmt(ScopStmt &Stmt, Instruction *Inst);
+
+ /// Remove dead instructions generated for BB
+ ///
+ /// @param BB The basic block code for which code has been generated.
+ /// @param BBMap A local map from old to new instructions.
+ void removeDeadInstructions(BasicBlock *BB, ValueMapT &BBMap);
+
+ /// Invalidate the scalar evolution expressions for a scop.
+ ///
+ /// This function invalidates the scalar evolution results for all
+ /// instructions that are part of a given scop, and the loops
+ /// surrounding the users of merge blocks. This is necessary to ensure that
+ /// later scops do not obtain scalar evolution expressions that reference
+ /// values that earlier dominated the later scop, but have been moved in the
+ /// conditional part of an earlier scop and consequently do not any more
+ /// dominate the later scop.
+ ///
+ /// @param S The scop to invalidate.
+ void invalidateScalarEvolution(Scop &S);
+};
+
+/// Generate a new vector basic block for a polyhedral statement.
+///
+/// The only public function exposed is generate().
+class VectorBlockGenerator : BlockGenerator {
+public:
+ /// Generate a new vector basic block for a ScoPStmt.
+ ///
+ /// This code generation is similar to the normal, scalar code generation,
+ /// except that each instruction is code generated for several vector lanes
+ /// at a time. If possible instructions are issued as actual vector
+ /// instructions, but e.g. for address calculation instructions we currently
+ /// generate scalar instructions for each vector lane.
+ ///
+ /// @param BlockGen A block generator object used as parent.
+ /// @param Stmt The statement to code generate.
+ /// @param VLTS A mapping from loops virtual canonical induction
+ /// variable to their new values
+ /// (for values recalculated in the new ScoP, but not
+ /// within this basic block), one for each lane.
+ /// @param Schedule A map from the statement to a schedule where the
+ /// innermost dimension is the dimension of the innermost
+ /// loop containing the statement.
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ static void generate(BlockGenerator &BlockGen, ScopStmt &Stmt,
+ std::vector<LoopToScevMapT> &VLTS,
+ __isl_keep isl_map *Schedule,
+ __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ VectorBlockGenerator Generator(BlockGen, VLTS, Schedule);
+ Generator.copyStmt(Stmt, NewAccesses);
+ }
+
+private:
+ // This is a vector of loop->scev maps. The first map is used for the first
+ // vector lane, ...
+ // Each map, contains information about Instructions in the old ScoP, which
+ // are recalculated in the new SCoP. When copying the basic block, we replace
+ // all references to the old instructions with their recalculated values.
+ //
+ // For example, when the code generator produces this AST:
+ //
+ // for (int c1 = 0; c1 <= 1023; c1 += 1)
+ // for (int c2 = 0; c2 <= 1023; c2 += VF)
+ // for (int lane = 0; lane <= VF; lane += 1)
+ // Stmt(c2 + lane + 3, c1);
+ //
+ // VLTS[lane] contains a map:
+ // "outer loop in the old loop nest" -> SCEV("c2 + lane + 3"),
+ // "inner loop in the old loop nest" -> SCEV("c1").
+ std::vector<LoopToScevMapT> &VLTS;
+
+ // A map from the statement to a schedule where the innermost dimension is the
+ // dimension of the innermost loop containing the statement.
+ isl_map *Schedule;
+
+ VectorBlockGenerator(BlockGenerator &BlockGen,
+ std::vector<LoopToScevMapT> &VLTS,
+ __isl_keep isl_map *Schedule);
+
+ int getVectorWidth();
+
+ Value *getVectorValue(ScopStmt &Stmt, Value *Old, ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps, Loop *L);
+
+ /// Load a vector from a set of adjacent scalars
+ ///
+ /// In case a set of scalars is known to be next to each other in memory,
+ /// create a vector load that loads those scalars
+ ///
+ /// %vector_ptr= bitcast double* %p to <4 x double>*
+ /// %vec_full = load <4 x double>* %vector_ptr
+ ///
+ /// @param Stmt The statement to code generate.
+ /// @param NegativeStride This is used to indicate a -1 stride. In such
+ /// a case we load the end of a base address and
+ /// shuffle the accesses in reverse order into the
+ /// vector. By default we would do only positive
+ /// strides.
+ ///
+ /// @param NewAccesses A map from memory access ids to new ast
+ /// expressions, which may contain new access
+ /// expressions for certain memory accesses.
+ Value *generateStrideOneLoad(ScopStmt &Stmt, LoadInst *Load,
+ VectorValueMapT &ScalarMaps,
+ __isl_keep isl_id_to_ast_expr *NewAccesses,
+ bool NegativeStride);
+
+ /// Load a vector initialized from a single scalar in memory
+ ///
+ /// In case all elements of a vector are initialized to the same
+ /// scalar value, this value is loaded and shuffled into all elements
+ /// of the vector.
+ ///
+ /// %splat_one = load <1 x double>* %p
+ /// %splat = shufflevector <1 x double> %splat_one, <1 x
+ /// double> %splat_one, <4 x i32> zeroinitializer
+ ///
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ Value *generateStrideZeroLoad(ScopStmt &Stmt, LoadInst *Load,
+ ValueMapT &BBMap,
+ __isl_keep isl_id_to_ast_expr *NewAccesses);
+
+ /// Load a vector from scalars distributed in memory
+ ///
+ /// In case some scalars a distributed randomly in memory. Create a vector
+ /// by loading each scalar and by inserting one after the other into the
+ /// vector.
+ ///
+ /// %scalar_1= load double* %p_1
+ /// %vec_1 = insertelement <2 x double> undef, double %scalar_1, i32 0
+ /// %scalar 2 = load double* %p_2
+ /// %vec_2 = insertelement <2 x double> %vec_1, double %scalar_1, i32 1
+ ///
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ Value *generateUnknownStrideLoad(ScopStmt &Stmt, LoadInst *Load,
+ VectorValueMapT &ScalarMaps,
+ __isl_keep isl_id_to_ast_expr *NewAccesses);
+
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ void generateLoad(ScopStmt &Stmt, LoadInst *Load, ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps,
+ __isl_keep isl_id_to_ast_expr *NewAccesses);
+
+ void copyUnaryInst(ScopStmt &Stmt, UnaryInstruction *Inst,
+ ValueMapT &VectorMap, VectorValueMapT &ScalarMaps);
+
+ void copyBinaryInst(ScopStmt &Stmt, BinaryOperator *Inst,
+ ValueMapT &VectorMap, VectorValueMapT &ScalarMaps);
+
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ void copyStore(ScopStmt &Stmt, StoreInst *Store, ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps,
+ __isl_keep isl_id_to_ast_expr *NewAccesses);
+
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ void copyInstScalarized(ScopStmt &Stmt, Instruction *Inst,
+ ValueMapT &VectorMap, VectorValueMapT &ScalarMaps,
+ __isl_keep isl_id_to_ast_expr *NewAccesses);
+
+ bool extractScalarValues(const Instruction *Inst, ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps);
+
+ bool hasVectorOperands(const Instruction *Inst, ValueMapT &VectorMap);
+
+ /// Generate vector loads for scalars.
+ ///
+ /// @param Stmt The scop statement for which to generate the loads.
+ /// @param VectorBlockMap A map that will be updated to relate the original
+ /// values with the newly generated vector loads.
+ void generateScalarVectorLoads(ScopStmt &Stmt, ValueMapT &VectorBlockMap);
+
+ /// Verify absence of scalar stores.
+ ///
+ /// @param Stmt The scop statement to check for scalar stores.
+ void verifyNoScalarStores(ScopStmt &Stmt);
+
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ void copyInstruction(ScopStmt &Stmt, Instruction *Inst, ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps,
+ __isl_keep isl_id_to_ast_expr *NewAccesses);
+
+ /// @param NewAccesses A map from memory access ids to new ast expressions,
+ /// which may contain new access expressions for certain
+ /// memory accesses.
+ void copyStmt(ScopStmt &Stmt, __isl_keep isl_id_to_ast_expr *NewAccesses);
+};
+
+/// Generator for new versions of polyhedral region statements.
+class RegionGenerator : public BlockGenerator {
+public:
+ /// Create a generator for regions.
+ ///
+ /// @param BlockGen A generator for basic blocks.
+ RegionGenerator(BlockGenerator &BlockGen) : BlockGenerator(BlockGen) {}
+
+ virtual ~RegionGenerator() {}
+
+ /// Copy the region statement @p Stmt.
+ ///
+ /// This copies the entire region represented by @p Stmt and updates
+ /// references to old values with references to new values, as defined by
+ /// GlobalMap.
+ ///
+ /// @param Stmt The statement to code generate.
+ /// @param LTS A map from old loops to new induction variables as SCEVs.
+ void copyStmt(ScopStmt &Stmt, LoopToScevMapT &LTS,
+ __isl_keep isl_id_to_ast_expr *IdToAstExp);
+
+private:
+ /// A map from old to the first new block in the region, that was created to
+ /// model the old basic block.
+ DenseMap<BasicBlock *, BasicBlock *> StartBlockMap;
+
+ /// A map from old to the last new block in the region, that was created to
+ /// model the old basic block.
+ DenseMap<BasicBlock *, BasicBlock *> EndBlockMap;
+
+ /// The "BBMaps" for the whole region (one for each block). In case a basic
+ /// block is code generated to multiple basic blocks (e.g., for partial
+ /// writes), the StartBasic is used as index for the RegionMap.
+ DenseMap<BasicBlock *, ValueMapT> RegionMaps;
+
+ /// Mapping to remember PHI nodes that still need incoming values.
+ using PHINodePairTy = std::pair<PHINode *, PHINode *>;
+ DenseMap<BasicBlock *, SmallVector<PHINodePairTy, 4>> IncompletePHINodeMap;
+
+ /// Repair the dominance tree after we created a copy block for @p BB.
+ ///
+ /// @returns The immediate dominator in the DT for @p BBCopy if in the region.
+ BasicBlock *repairDominance(BasicBlock *BB, BasicBlock *BBCopy);
+
+ /// Add the new operand from the copy of @p IncomingBB to @p PHICopy.
+ ///
+ /// PHI nodes, which may have (multiple) edges that enter from outside the
+ /// non-affine subregion and even from outside the scop, are code generated as
+ /// follows:
+ ///
+ /// # Original
+ ///
+ /// Region: %A-> %exit
+ /// NonAffine Stmt: %nonaffB -> %D (includes %nonaffB, %nonaffC)
+ ///
+ /// pre:
+ /// %val = add i64 1, 1
+ ///
+ /// A:
+ /// br label %nonaff
+ ///
+ /// nonaffB:
+ /// %phi = phi i64 [%val, %A], [%valC, %nonAffC], [%valD, %D]
+ /// %cmp = <nonaff>
+ /// br i1 %cmp, label %C, label %nonaffC
+ ///
+ /// nonaffC:
+ /// %valC = add i64 1, 1
+ /// br i1 undef, label %D, label %nonaffB
+ ///
+ /// D:
+ /// %valD = ...
+ /// %exit_cond = <loopexit>
+ /// br i1 %exit_cond, label %nonaffB, label %exit
+ ///
+ /// exit:
+ /// ...
+ ///
+ /// - %start and %C enter from outside the non-affine region.
+ /// - %nonaffC enters from within the non-affine region.
+ ///
+ /// # New
+ ///
+ /// polly.A:
+ /// store i64 %val, i64* %phi.phiops
+ /// br label %polly.nonaffA.entry
+ ///
+ /// polly.nonaffB.entry:
+ /// %phi.phiops.reload = load i64, i64* %phi.phiops
+ /// br label %nonaffB
+ ///
+ /// polly.nonaffB:
+ /// %polly.phi = [%phi.phiops.reload, %nonaffB.entry],
+ /// [%p.valC, %polly.nonaffC]
+ ///
+ /// polly.nonaffC:
+ /// %p.valC = add i64 1, 1
+ /// br i1 undef, label %polly.D, label %polly.nonaffB
+ ///
+ /// polly.D:
+ /// %p.valD = ...
+ /// store i64 %p.valD, i64* %phi.phiops
+ /// %p.exit_cond = <loopexit>
+ /// br i1 %p.exit_cond, label %polly.nonaffB, label %exit
+ ///
+ /// Values that enter the PHI from outside the non-affine region are stored
+ /// into the stack slot %phi.phiops by statements %polly.A and %polly.D and
+ /// reloaded in %polly.nonaffB.entry, a basic block generated before the
+ /// actual non-affine region.
+ ///
+ /// When generating the PHI node of the non-affine region in %polly.nonaffB,
+ /// incoming edges from outside the region are combined into a single branch
+ /// from %polly.nonaffB.entry which has as incoming value the value reloaded
+ /// from the %phi.phiops stack slot. Incoming edges from within the region
+ /// refer to the copied instructions (%p.valC) and basic blocks
+ /// (%polly.nonaffC) of the non-affine region.
+ ///
+ /// @param Stmt The statement to code generate.
+ /// @param PHI The original PHI we copy.
+ /// @param PHICopy The copy of @p PHI.
+ /// @param IncomingBB An incoming block of @p PHI.
+ /// @param LTS A map from old loops to new induction variables as
+ /// SCEVs.
+ void addOperandToPHI(ScopStmt &Stmt, PHINode *PHI, PHINode *PHICopy,
+ BasicBlock *IncomingBB, LoopToScevMapT &LTS);
+
+ /// Create a PHI that combines the incoming values from all incoming blocks
+ /// that are in the subregion.
+ ///
+ /// PHIs in the subregion's exit block can have incoming edges from within and
+ /// outside the subregion. This function combines the incoming values from
+ /// within the subregion to appear as if there is only one incoming edge from
+ /// the subregion (an additional exit block is created by RegionGenerator).
+ /// This is to avoid that a value is written to the .phiops location without
+ /// leaving the subregion because the exiting block as an edge back into the
+ /// subregion.
+ ///
+ /// @param MA The WRITE of MemoryKind::PHI/MemoryKind::ExitPHI for a PHI in
+ /// the subregion's exit block.
+ /// @param LTS Virtual induction variable mapping.
+ /// @param BBMap A mapping from old values to their new values in this block.
+ /// @param L Loop surrounding this region statement.
+ ///
+ /// @returns The constructed PHI node.
+ PHINode *buildExitPHI(MemoryAccess *MA, LoopToScevMapT &LTS, ValueMapT &BBMap,
+ Loop *L);
+
+ /// @param Return the new value of a scalar write, creating a PHINode if
+ /// necessary.
+ ///
+ /// @param MA A scalar WRITE MemoryAccess.
+ /// @param LTS Virtual induction variable mapping.
+ /// @param BBMap A mapping from old values to their new values in this block.
+ ///
+ /// @returns The effective value of @p MA's written value when leaving the
+ /// subregion.
+ /// @see buildExitPHI
+ Value *getExitScalar(MemoryAccess *MA, LoopToScevMapT &LTS, ValueMapT &BBMap);
+
+ /// Generate the scalar stores for the given statement.
+ ///
+ /// After the statement @p Stmt was copied all inner-SCoP scalar dependences
+ /// starting in @p Stmt (hence all scalar write accesses in @p Stmt) need to
+ /// be demoted to memory.
+ ///
+ /// @param Stmt The statement we generate code for.
+ /// @param LTS A mapping from loops virtual canonical induction variable to
+ /// their new values (for values recalculated in the new ScoP,
+ /// but not within this basic block)
+ /// @param BBMap A mapping from old values to their new values in this block.
+ /// @param LTS A mapping from loops virtual canonical induction variable to
+ /// their new values.
+ virtual void
+ generateScalarStores(ScopStmt &Stmt, LoopToScevMapT &LTS, ValueMapT &BBMAp,
+ __isl_keep isl_id_to_ast_expr *NewAccesses) override;
+
+ /// Copy a single PHI instruction.
+ ///
+ /// This copies a single PHI instruction and updates references to old values
+ /// with references to new values, as defined by GlobalMap and BBMap.
+ ///
+ /// @param Stmt The statement to code generate.
+ /// @param PHI The PHI instruction to copy.
+ /// @param BBMap A mapping from old values to their new values
+ /// (for values recalculated within this basic block).
+ /// @param LTS A map from old loops to new induction variables as SCEVs.
+ virtual void copyPHIInstruction(ScopStmt &Stmt, PHINode *Inst,
+ ValueMapT &BBMap,
+ LoopToScevMapT &LTS) override;
+};
+} // namespace polly
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/CodeGeneration.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/CodeGeneration.h
new file mode 100644
index 00000000000..b32f31299c6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/CodeGeneration.h
@@ -0,0 +1,39 @@
+//===- polly/CodeGeneration.h - The Polly code generator --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_CODEGENERATION_H
+#define POLLY_CODEGENERATION_H
+
+#include "polly/CodeGen/IRBuilder.h"
+#include "polly/ScopPass.h"
+#include "llvm/IR/PassManager.h"
+
+namespace polly {
+
+enum VectorizerChoice {
+ VECTORIZER_NONE,
+ VECTORIZER_STRIPMINE,
+ VECTORIZER_POLLY,
+};
+extern VectorizerChoice PollyVectorizerChoice;
+
+/// Mark a basic block unreachable.
+///
+/// Marks the basic block @p Block unreachable by equipping it with an
+/// UnreachableInst.
+void markBlockUnreachable(BasicBlock &Block, PollyIRBuilder &Builder);
+
+struct CodeGenerationPass : public PassInfoMixin<CodeGenerationPass> {
+ PreservedAnalyses run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &AR, SPMUpdater &U);
+};
+
+extern bool PerfMonitoring;
+} // namespace polly
+
+#endif // POLLY_CODEGENERATION_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/CodegenCleanup.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/CodegenCleanup.h
new file mode 100644
index 00000000000..a1fd6805dfe
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/CodegenCleanup.h
@@ -0,0 +1,17 @@
+#ifndef POLLY_CODEGENCLEANUP_H
+#define POLLY_CODEGENCLEANUP_H
+
+namespace llvm {
+class FunctionPass;
+class PassRegistry;
+} // namespace llvm
+
+namespace polly {
+llvm::FunctionPass *createCodegenCleanupPass();
+} // namespace polly
+
+namespace llvm {
+void initializeCodegenCleanupPass(llvm::PassRegistry &);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IRBuilder.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IRBuilder.h
new file mode 100644
index 00000000000..8d9473ce2be
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IRBuilder.h
@@ -0,0 +1,144 @@
+//===- Codegen/IRBuilder.h - The IR builder used by Polly -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The Polly IRBuilder file contains Polly specific extensions for the IRBuilder
+// that are used e.g. to emit the llvm.loop.parallel metadata.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_CODEGEN_IRBUILDER_H
+#define POLLY_CODEGEN_IRBUILDER_H
+
+#include "llvm/ADT/MapVector.h"
+#include "llvm/IR/IRBuilder.h"
+
+namespace llvm {
+class Loop;
+class SCEV;
+class ScalarEvolution;
+} // namespace llvm
+
+namespace polly {
+class Scop;
+struct BandAttr;
+
+/// Helper class to annotate newly generated SCoPs with metadata.
+///
+/// The annotations are twofold:
+/// 1) Loops are stored in a stack-like structure in the order they are
+/// constructed and the LoopID metadata node is added to the backedge.
+/// Contained memory instructions and loop headers are annotated according
+/// to all parallel surrounding loops.
+/// 2) The new SCoP is assumed alias free (either due to the result of
+/// AliasAnalysis queries or runtime alias checks). We annotate therefore
+/// all memory instruction with alias scopes to indicate that fact to
+/// later optimizations.
+/// These alias scopes live in a new alias domain only used in this SCoP.
+/// Each base pointer has its own alias scope and is annotated to not
+/// alias with any access to different base pointers.
+class ScopAnnotator {
+public:
+ ScopAnnotator();
+ ~ScopAnnotator();
+
+ /// Build all alias scopes for the given SCoP.
+ void buildAliasScopes(Scop &S);
+
+ /// Add a new loop @p L which is parallel if @p IsParallel is true.
+ void pushLoop(llvm::Loop *L, bool IsParallel);
+
+ /// Remove the last added loop.
+ void popLoop(bool isParallel);
+
+ /// Annotate the new instruction @p I for all parallel loops.
+ void annotate(llvm::Instruction *I);
+
+ /// Annotate the loop latch @p B wrt. @p L.
+ void annotateLoopLatch(llvm::BranchInst *B, llvm::Loop *L, bool IsParallel,
+ bool IsLoopVectorizerDisabled) const;
+
+ /// Add alternative alias based pointers
+ ///
+ /// When annotating instructions with alias scope metadata, the right metadata
+ /// is identified through the base pointer of the memory access. In some cases
+ /// (e.g. OpenMP code generation), the base pointer of the memory accesses is
+ /// not the original base pointer, but was changed when passing the original
+ /// base pointer over a function boundary. This function allows to provide a
+ /// map that maps from these new base pointers to the original base pointers
+ /// to allow the ScopAnnotator to still find the right alias scop annotations.
+ ///
+ /// @param NewMap A map from new base pointers to original base pointers.
+ void addAlternativeAliasBases(
+ llvm::DenseMap<llvm::AssertingVH<llvm::Value>,
+ llvm::AssertingVH<llvm::Value>> &NewMap) {
+ AlternativeAliasBases.insert(NewMap.begin(), NewMap.end());
+ }
+
+ /// Delete the set of alternative alias bases
+ void resetAlternativeAliasBases() { AlternativeAliasBases.clear(); }
+
+ /// Stack for surrounding BandAttr annotations.
+ llvm::SmallVector<BandAttr *, 8> LoopAttrEnv;
+ BandAttr *&getStagingAttrEnv() { return LoopAttrEnv.back(); }
+ BandAttr *getActiveAttrEnv() const {
+ return LoopAttrEnv[LoopAttrEnv.size() - 2];
+ }
+
+private:
+ /// The ScalarEvolution analysis we use to find base pointers.
+ llvm::ScalarEvolution *SE;
+
+ /// All loops currently under construction.
+ llvm::SmallVector<llvm::Loop *, 8> ActiveLoops;
+
+ /// Access groups for the parallel loops currently under construction.
+ llvm::SmallVector<llvm::MDNode *, 8> ParallelLoops;
+
+ /// The alias scope domain for the current SCoP.
+ llvm::MDNode *AliasScopeDomain;
+
+ /// A map from base pointers to its alias scope.
+ llvm::MapVector<llvm::AssertingVH<llvm::Value>, llvm::MDNode *> AliasScopeMap;
+
+ /// A map from base pointers to an alias scope list of other pointers.
+ llvm::DenseMap<llvm::AssertingVH<llvm::Value>, llvm::MDNode *>
+ OtherAliasScopeListMap;
+
+ llvm::DenseMap<llvm::AssertingVH<llvm::Value>, llvm::AssertingVH<llvm::Value>>
+ AlternativeAliasBases;
+};
+
+/// Add Polly specifics when running IRBuilder.
+///
+/// This is used to add additional items such as e.g. the llvm.loop.parallel
+/// metadata.
+class IRInserter final : public llvm::IRBuilderDefaultInserter {
+public:
+ IRInserter() = default;
+ IRInserter(class ScopAnnotator &A) : Annotator(&A) {}
+
+ void InsertHelper(llvm::Instruction *I, const llvm::Twine &Name,
+ llvm::BasicBlock *BB,
+ llvm::BasicBlock::iterator InsertPt) const override {
+ llvm::IRBuilderDefaultInserter::InsertHelper(I, Name, BB, InsertPt);
+ if (Annotator)
+ Annotator->annotate(I);
+ }
+
+private:
+ class ScopAnnotator *Annotator = nullptr;
+};
+
+// TODO: We should not name instructions in NDEBUG builds.
+//
+// We currently always name instructions, as the polly test suite currently
+// matches for certain names.
+typedef llvm::IRBuilder<llvm::ConstantFolder, IRInserter> PollyIRBuilder;
+
+} // namespace polly
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslAst.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslAst.h
new file mode 100644
index 00000000000..bab4fce864a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslAst.h
@@ -0,0 +1,212 @@
+//===- IslAst.h - Interface to the isl code generator -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The isl code generator interface takes a Scop and generates a isl_ast. This
+// ist_ast can either be returned directly or it can be pretty printed to
+// stdout.
+//
+// A typical isl_ast output looks like this:
+//
+// for (c2 = max(0, ceild(n + m, 2); c2 <= min(511, floord(5 * n, 3)); c2++) {
+// bb2(c2);
+// }
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_ISLAST_H
+#define POLLY_ISLAST_H
+
+#include "polly/ScopPass.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/IR/PassManager.h"
+#include "isl/ctx.h"
+
+namespace polly {
+using llvm::SmallPtrSet;
+
+struct Dependences;
+
+class IslAst {
+public:
+ IslAst(const IslAst &) = delete;
+ IslAst &operator=(const IslAst &) = delete;
+ IslAst(IslAst &&);
+ IslAst &operator=(IslAst &&) = delete;
+
+ static IslAst create(Scop &Scop, const Dependences &D);
+
+ /// Print a source code representation of the program.
+ void pprint(raw_ostream &OS);
+
+ isl::ast_node getAst();
+
+ const std::shared_ptr<isl_ctx> getSharedIslCtx() const { return Ctx; }
+
+ /// Get the run-time conditions for the Scop.
+ isl::ast_expr getRunCondition();
+
+ /// Build run-time condition for scop.
+ ///
+ /// @param S The scop to build the condition for.
+ /// @param Build The isl_build object to use to build the condition.
+ ///
+ /// @returns An ast expression that describes the necessary run-time check.
+ static isl::ast_expr buildRunCondition(Scop &S, const isl::ast_build &Build);
+
+private:
+ Scop &S;
+ std::shared_ptr<isl_ctx> Ctx;
+ isl::ast_expr RunCondition;
+ isl::ast_node Root;
+
+ IslAst(Scop &Scop);
+
+ void init(const Dependences &D);
+};
+
+class IslAstInfo {
+public:
+ using MemoryAccessSet = SmallPtrSet<MemoryAccess *, 4>;
+
+ /// Payload information used to annotate an AST node.
+ struct IslAstUserPayload {
+ /// Construct and initialize the payload.
+ IslAstUserPayload() = default;
+
+ /// Does the dependence analysis determine that there are no loop-carried
+ /// dependencies?
+ bool IsParallel = false;
+
+ /// Flag to mark innermost loops.
+ bool IsInnermost = false;
+
+ /// Flag to mark innermost parallel loops.
+ bool IsInnermostParallel = false;
+
+ /// Flag to mark outermost parallel loops.
+ bool IsOutermostParallel = false;
+
+ /// Flag to mark parallel loops which break reductions.
+ bool IsReductionParallel = false;
+
+ /// The minimal dependence distance for non parallel loops.
+ isl::pw_aff MinimalDependenceDistance;
+
+ /// The build environment at the time this node was constructed.
+ isl::ast_build Build;
+
+ /// Set of accesses which break reduction dependences.
+ MemoryAccessSet BrokenReductions;
+ };
+
+private:
+ Scop &S;
+ IslAst Ast;
+
+public:
+ IslAstInfo(Scop &S, const Dependences &D) : S(S), Ast(IslAst::create(S, D)) {}
+
+ /// Return the isl AST computed by this IslAstInfo.
+ IslAst &getIslAst() { return Ast; }
+
+ /// Return a copy of the AST root node.
+ isl::ast_node getAst();
+
+ /// Get the run condition.
+ ///
+ /// Only if the run condition evaluates at run-time to a non-zero value, the
+ /// assumptions that have been taken hold. If the run condition evaluates to
+ /// zero/false some assumptions do not hold and the original code needs to
+ /// be executed.
+ isl::ast_expr getRunCondition();
+
+ void print(raw_ostream &O);
+
+ /// @name Extract information attached to an isl ast (for) node.
+ ///
+ ///{
+ /// Get the complete payload attached to @p Node.
+ static IslAstUserPayload *getNodePayload(const isl::ast_node &Node);
+
+ /// Is this loop an innermost loop?
+ static bool isInnermost(const isl::ast_node &Node);
+
+ /// Is this loop a parallel loop?
+ static bool isParallel(const isl::ast_node &Node);
+
+ /// Is this loop an outermost parallel loop?
+ static bool isOutermostParallel(const isl::ast_node &Node);
+
+ /// Is this loop an innermost parallel loop?
+ static bool isInnermostParallel(const isl::ast_node &Node);
+
+ /// Is this loop a reduction parallel loop?
+ static bool isReductionParallel(const isl::ast_node &Node);
+
+ /// Will the loop be run as thread parallel?
+ static bool isExecutedInParallel(const isl::ast_node &Node);
+
+ /// Get the nodes schedule or a nullptr if not available.
+ static isl::union_map getSchedule(const isl::ast_node &Node);
+
+ /// Get minimal dependence distance or nullptr if not available.
+ static isl::pw_aff getMinimalDependenceDistance(const isl::ast_node &Node);
+
+ /// Get the nodes broken reductions or a nullptr if not available.
+ static MemoryAccessSet *getBrokenReductions(const isl::ast_node &Node);
+
+ /// Get the nodes build context or a nullptr if not available.
+ static isl::ast_build getBuild(const isl::ast_node &Node);
+
+ ///}
+};
+
+struct IslAstAnalysis : public AnalysisInfoMixin<IslAstAnalysis> {
+ static AnalysisKey Key;
+
+ using Result = IslAstInfo;
+
+ IslAstInfo run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR);
+};
+
+class IslAstInfoWrapperPass : public ScopPass {
+ std::unique_ptr<IslAstInfo> Ast;
+
+public:
+ static char ID;
+
+ IslAstInfoWrapperPass() : ScopPass(ID) {}
+
+ IslAstInfo &getAI() { return *Ast; }
+ const IslAstInfo &getAI() const { return *Ast; }
+
+ /// Build the AST for the given SCoP @p S.
+ bool runOnScop(Scop &S) override;
+
+ /// Register all analyses and transformation required.
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+
+ /// Release the internal memory.
+ void releaseMemory() override;
+
+ /// Print a source code representation of the program.
+ void printScop(raw_ostream &OS, Scop &S) const override;
+};
+
+struct IslAstPrinterPass : public PassInfoMixin<IslAstPrinterPass> {
+ IslAstPrinterPass(raw_ostream &OS) : OS(OS) {}
+
+ PreservedAnalyses run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &, SPMUpdater &U);
+
+ raw_ostream &OS;
+};
+} // namespace polly
+
+#endif // POLLY_ISLAST_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslExprBuilder.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslExprBuilder.h
new file mode 100644
index 00000000000..05772464052
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslExprBuilder.h
@@ -0,0 +1,269 @@
+//===-IslExprBuilder.h - Helper to generate code for isl AST expressions --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_ISL_EXPR_BUILDER_H
+#define POLLY_ISL_EXPR_BUILDER_H
+
+#include "polly/CodeGen/IRBuilder.h"
+#include "polly/Support/ScopHelper.h"
+#include "isl/isl-noexceptions.h"
+
+namespace llvm {
+// Provide PointerLikeTypeTraits for isl_id.
+template <> struct PointerLikeTypeTraits<isl_id *> {
+
+public:
+ static inline const void *getAsVoidPointer(isl_id *P) { return (void *)P; }
+ static inline const Region *getFromVoidPointer(void *P) {
+ return (Region *)P;
+ }
+ static constexpr int NumLowBitsAvailable = 0;
+};
+} // namespace llvm
+
+namespace polly {
+class ScopArrayInfo;
+
+/// LLVM-IR generator for isl_ast_expr[essions]
+///
+/// This generator generates LLVM-IR that performs the computation described by
+/// an isl_ast_expr[ession].
+///
+/// Example:
+///
+/// An isl_ast_expr[ession] can look like this:
+///
+/// (N + M) + 10
+///
+/// The IslExprBuilder could create the following LLVM-IR:
+///
+/// %tmp1 = add nsw i64 %N
+/// %tmp2 = add nsw i64 %tmp1, %M
+/// %tmp3 = add nsw i64 %tmp2, 10
+///
+/// The implementation of this class is mostly a mapping from isl_ast_expr
+/// constructs to the corresponding LLVM-IR constructs.
+///
+/// The following decisions may need some explanation:
+///
+/// 1) Which data-type to choose
+///
+/// isl_ast_expr[essions] are untyped expressions that assume arbitrary
+/// precision integer computations. LLVM-IR instead has fixed size integers.
+/// When lowering to LLVM-IR we need to chose both the size of the data type and
+/// the sign of the operations we use.
+///
+/// At the moment, we hardcode i64 bit signed computations. Our experience has
+/// shown that 64 bit are generally large enough for the loop bounds that appear
+/// in the wild. Signed computations are needed, as loop bounds may become
+/// negative.
+///
+/// It is possible to track overflows that occurred in the generated IR. See the
+/// description of @see OverflowState for more information.
+///
+/// FIXME: Hardcoding sizes can cause issues:
+///
+/// - On embedded systems and especially for high-level-synthesis 64 bit
+/// computations are very costly.
+///
+/// The right approach is to compute the minimal necessary bitwidth and
+/// signedness for each subexpression during in the isl AST generation and
+/// to use this information in our IslAstGenerator. Preliminary patches are
+/// available, but have not been committed yet.
+///
+class IslExprBuilder {
+public:
+ /// A map from isl_ids to llvm::Values.
+ typedef llvm::MapVector<isl_id *, llvm::AssertingVH<llvm::Value>> IDToValueTy;
+
+ typedef llvm::MapVector<isl_id *, const ScopArrayInfo *> IDToScopArrayInfoTy;
+
+ /// A map from isl_ids to ScopArrayInfo objects.
+ ///
+ /// This map is used to obtain ScopArrayInfo objects for isl_ids which do not
+ /// carry a ScopArrayInfo object in their user pointer. This is useful if the
+ /// construction of ScopArrayInfo objects happens only after references (e.g.
+ /// in an AST) to an isl_id are generated and the user pointer of the isl_id
+ /// can not be changed any more.
+ ///
+ /// This is useful for external users who just use the IslExprBuilder for
+ /// code generation.
+ IDToScopArrayInfoTy *IDToSAI = nullptr;
+
+ /// Set the isl_id to ScopArrayInfo map.
+ ///
+ /// @param NewIDToSAI The new isl_id to ScopArrayInfo map to use.
+ void setIDToSAI(IDToScopArrayInfoTy *NewIDToSAI) { IDToSAI = NewIDToSAI; }
+
+ /// Construct an IslExprBuilder.
+ ///
+ /// @param Builder The IRBuilder used to construct the
+ /// isl_ast_expr[ession]. The insert location of this
+ /// IRBuilder defines WHERE the corresponding LLVM-IR
+ /// is generated.
+ /// @param IDToValue The isl_ast_expr[ession] may reference parameters or
+ /// variables (identified by an isl_id). The IDTOValue map
+ /// specifies the LLVM-IR Values that correspond to these
+ /// parameters and variables.
+ /// @param GlobalMap A mapping from llvm::Values used in the original scop
+ /// region to a new set of llvm::Values.
+ /// @param DL DataLayout for the current Module.
+ /// @param SE ScalarEvolution analysis for the current function.
+ /// @param DT DominatorTree analysis for the current function.
+ /// @param LI LoopInfo analysis for the current function.
+ /// @param StartBlock The first basic block after the RTC.
+ IslExprBuilder(Scop &S, PollyIRBuilder &Builder, IDToValueTy &IDToValue,
+ ValueMapT &GlobalMap, const llvm::DataLayout &DL,
+ llvm::ScalarEvolution &SE, llvm::DominatorTree &DT,
+ llvm::LoopInfo &LI, llvm::BasicBlock *StartBlock);
+
+ /// Create LLVM-IR for an isl_ast_expr[ession].
+ ///
+ /// @param Expr The ast expression for which we generate LLVM-IR.
+ ///
+ /// @return The llvm::Value* containing the result of the computation.
+ llvm::Value *create(__isl_take isl_ast_expr *Expr);
+
+ /// Return the largest of two types.
+ ///
+ /// @param T1 The first type.
+ /// @param T2 The second type.
+ ///
+ /// @return The largest of the two types.
+ llvm::Type *getWidestType(llvm::Type *T1, llvm::Type *T2);
+
+ /// Return the type with which this expression should be computed.
+ ///
+ /// The type needs to be large enough to hold all possible input and all
+ /// possible output values.
+ ///
+ /// @param Expr The expression for which to find the type.
+ /// @return The type with which the expression should be computed.
+ llvm::IntegerType *getType(__isl_keep isl_ast_expr *Expr);
+
+ /// Change if runtime overflows are tracked or not.
+ ///
+ /// @param Enable Flag to enable/disable the tracking.
+ ///
+ /// Note that this will reset the tracking state and that tracking is only
+ /// allowed if the last tracked expression dominates the current insert point.
+ void setTrackOverflow(bool Enable);
+
+ /// Return the current overflow status or nullptr if it is not tracked.
+ ///
+ /// @return A nullptr if tracking is disabled or otherwise an i1 that has the
+ /// value of "0" if and only if no overflow happened since tracking
+ /// was enabled.
+ llvm::Value *getOverflowState() const;
+
+ /// Create LLVM-IR that computes the memory location of an access expression.
+ ///
+ /// For a given isl_ast_expr[ession] of type isl_ast_op_access this function
+ /// creates IR that computes the address the access expression refers to.
+ ///
+ /// @param Expr The ast expression of type isl_ast_op_access
+ /// for which we generate LLVM-IR.
+ ///
+ /// @return A pair of the llvm::Value* containing the result of the
+ /// computation and the llvm::Type* it points to.
+ std::pair<llvm::Value *, llvm::Type *>
+ createAccessAddress(__isl_take isl_ast_expr *Expr);
+
+ /// Check if an @p Expr contains integer constants larger than 64 bit.
+ ///
+ /// @param Expr The expression to check.
+ ///
+ /// @return True if the ast expression is larger than 64 bit.
+ bool hasLargeInts(isl::ast_expr Expr);
+
+private:
+ Scop &S;
+
+ /// Flag that will be set if an overflow occurred at runtime.
+ ///
+ /// Note that this flag is by default a nullptr and if it is a nullptr
+ /// we will not record overflows but simply perform the computations.
+ /// The intended usage is as follows:
+ /// - If overflows in [an] expression[s] should be tracked, call
+ /// the setTrackOverflow(true) function.
+ /// - Use create(...) for all expressions that should be checked.
+ /// - Call getOverflowState() to get the value representing the current
+ /// state of the overflow flag.
+ /// - To stop tracking call setTrackOverflow(false).
+ llvm::Value *OverflowState;
+
+ PollyIRBuilder &Builder;
+ IDToValueTy &IDToValue;
+ ValueMapT &GlobalMap;
+
+ const llvm::DataLayout &DL;
+ llvm::ScalarEvolution &SE;
+ llvm::DominatorTree &DT;
+ llvm::LoopInfo &LI;
+ llvm::BasicBlock *StartBlock;
+
+ llvm::Value *createOp(__isl_take isl_ast_expr *Expr);
+ llvm::Value *createOpUnary(__isl_take isl_ast_expr *Expr);
+ llvm::Value *createOpAccess(__isl_take isl_ast_expr *Expr);
+ llvm::Value *createOpBin(__isl_take isl_ast_expr *Expr);
+ llvm::Value *createOpNAry(__isl_take isl_ast_expr *Expr);
+ llvm::Value *createOpSelect(__isl_take isl_ast_expr *Expr);
+ llvm::Value *createOpICmp(__isl_take isl_ast_expr *Expr);
+ llvm::Value *createOpBoolean(__isl_take isl_ast_expr *Expr);
+ llvm::Value *createOpBooleanConditional(__isl_take isl_ast_expr *Expr);
+ llvm::Value *createId(__isl_take isl_ast_expr *Expr);
+ llvm::Value *createInt(__isl_take isl_ast_expr *Expr);
+ llvm::Value *createOpAddressOf(__isl_take isl_ast_expr *Expr);
+
+ /// Create a binary operation @p Opc and track overflows if requested.
+ ///
+ /// @param OpC The binary operation that should be performed [Add/Sub/Mul].
+ /// @param LHS The left operand.
+ /// @param RHS The right operand.
+ /// @param Name The (base) name of the new IR operations.
+ ///
+ /// @return A value that represents the result of the binary operation.
+ llvm::Value *createBinOp(llvm::BinaryOperator::BinaryOps Opc,
+ llvm::Value *LHS, llvm::Value *RHS,
+ const llvm::Twine &Name);
+
+ /// Create an addition and track overflows if requested.
+ ///
+ /// @param LHS The left operand.
+ /// @param RHS The right operand.
+ /// @param Name The (base) name of the new IR operations.
+ ///
+ /// @return A value that represents the result of the addition.
+ llvm::Value *createAdd(llvm::Value *LHS, llvm::Value *RHS,
+ const llvm::Twine &Name = "");
+
+ /// Create a subtraction and track overflows if requested.
+ ///
+ /// @param LHS The left operand.
+ /// @param RHS The right operand.
+ /// @param Name The (base) name of the new IR operations.
+ ///
+ /// @return A value that represents the result of the subtraction.
+ llvm::Value *createSub(llvm::Value *LHS, llvm::Value *RHS,
+ const llvm::Twine &Name = "");
+
+ /// Create a multiplication and track overflows if requested.
+ ///
+ /// @param LHS The left operand.
+ /// @param RHS The right operand.
+ /// @param Name The (base) name of the new IR operations.
+ ///
+ /// @return A value that represents the result of the multiplication.
+ llvm::Value *createMul(llvm::Value *LHS, llvm::Value *RHS,
+ const llvm::Twine &Name = "");
+};
+} // namespace polly
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslNodeBuilder.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslNodeBuilder.h
new file mode 100644
index 00000000000..2dc7f019e84
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/IslNodeBuilder.h
@@ -0,0 +1,425 @@
+//=- IslNodeBuilder.cpp - Translate an isl AST into a LLVM-IR AST -*- C++ -*-=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the IslNodeBuilder, a class to translate an isl AST into
+// a LLVM-IR AST.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_ISLNODEBUILDER_H
+#define POLLY_ISLNODEBUILDER_H
+
+#include "polly/CodeGen/BlockGenerators.h"
+#include "polly/CodeGen/IslExprBuilder.h"
+#include "polly/ScopDetectionDiagnostic.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/IR/InstrTypes.h"
+#include "isl/ctx.h"
+#include "isl/isl-noexceptions.h"
+
+namespace polly {
+using llvm::LoopInfo;
+using llvm::SmallSet;
+
+struct InvariantEquivClassTy;
+
+struct SubtreeReferences {
+ LoopInfo &LI;
+ ScalarEvolution &SE;
+ Scop &S;
+ ValueMapT &GlobalMap;
+ SetVector<Value *> &Values;
+ SetVector<const SCEV *> &SCEVs;
+ BlockGenerator &BlockGen;
+ // In case an (optional) parameter space location is provided, parameter space
+ // information is collected as well.
+ isl::space *ParamSpace;
+};
+
+/// Extract the out-of-scop values and SCEVs referenced from a ScopStmt.
+///
+/// This includes the SCEVUnknowns referenced by the SCEVs used in the
+/// statement and the base pointers of the memory accesses. For scalar
+/// statements we force the generation of alloca memory locations and list
+/// these locations in the set of out-of-scop values as well.
+///
+/// We also collect an isl::space that includes all parameter dimensions
+/// used in the statement's memory accesses, in case the ParamSpace pointer
+/// is non-null.
+///
+/// @param Stmt The statement for which to extract the information.
+/// @param UserPtr A void pointer that can be casted to a
+/// SubtreeReferences structure.
+/// @param CreateScalarRefs Should the result include allocas of scalar
+/// references?
+void addReferencesFromStmt(ScopStmt *Stmt, void *UserPtr,
+ bool CreateScalarRefs = true);
+
+class IslNodeBuilder {
+public:
+ IslNodeBuilder(PollyIRBuilder &Builder, ScopAnnotator &Annotator,
+ const DataLayout &DL, LoopInfo &LI, ScalarEvolution &SE,
+ DominatorTree &DT, Scop &S, BasicBlock *StartBlock)
+ : S(S), Builder(Builder), Annotator(Annotator),
+ ExprBuilder(S, Builder, IDToValue, ValueMap, DL, SE, DT, LI,
+ StartBlock),
+ BlockGen(Builder, LI, SE, DT, ScalarMap, EscapeMap, ValueMap,
+ &ExprBuilder, StartBlock),
+ RegionGen(BlockGen), DL(DL), LI(LI), SE(SE), DT(DT),
+ StartBlock(StartBlock) {}
+
+ virtual ~IslNodeBuilder() = default;
+
+ void addParameters(__isl_take isl_set *Context);
+
+ /// Generate code that evaluates @p Condition at run-time.
+ ///
+ /// This function is typically called to generate the LLVM-IR for the
+ /// run-time condition of the scop, that verifies that all the optimistic
+ /// assumptions we have taken during scop modeling and transformation
+ /// hold at run-time.
+ ///
+ /// @param Condition The condition to evaluate
+ ///
+ /// @result An llvm::Value that is true if the condition holds and false
+ /// otherwise.
+ Value *createRTC(isl_ast_expr *Condition);
+
+ void create(__isl_take isl_ast_node *Node);
+
+ /// Allocate memory for all new arrays created by Polly.
+ void allocateNewArrays(BBPair StartExitBlocks);
+
+ /// Preload all memory loads that are invariant.
+ bool preloadInvariantLoads();
+
+ /// Finalize code generation.
+ ///
+ /// @see BlockGenerator::finalizeSCoP(Scop &S)
+ virtual void finalize() { BlockGen.finalizeSCoP(S); }
+
+ IslExprBuilder &getExprBuilder() { return ExprBuilder; }
+
+ /// Get the associated block generator.
+ ///
+ /// @return A reference to the associated block generator.
+ BlockGenerator &getBlockGenerator() { return BlockGen; }
+
+ /// Return the parallel subfunctions that have been created.
+ const ArrayRef<Function *> getParallelSubfunctions() const {
+ return ParallelSubfunctions;
+ }
+
+protected:
+ Scop &S;
+ PollyIRBuilder &Builder;
+ ScopAnnotator &Annotator;
+
+ IslExprBuilder ExprBuilder;
+
+ /// Maps used by the block and region generator to demote scalars.
+ ///
+ ///@{
+
+ /// See BlockGenerator::ScalarMap.
+ BlockGenerator::AllocaMapTy ScalarMap;
+
+ /// See BlockGenerator::EscapeMap.
+ BlockGenerator::EscapeUsersAllocaMapTy EscapeMap;
+
+ ///@}
+
+ /// The generator used to copy a basic block.
+ BlockGenerator BlockGen;
+
+ /// The generator used to copy a non-affine region.
+ RegionGenerator RegionGen;
+
+ const DataLayout &DL;
+ LoopInfo &LI;
+ ScalarEvolution &SE;
+ DominatorTree &DT;
+ BasicBlock *StartBlock;
+
+ /// The current iteration of out-of-scop loops
+ ///
+ /// This map provides for a given loop a llvm::Value that contains the current
+ /// loop iteration.
+ MapVector<const Loop *, const SCEV *> OutsideLoopIterations;
+
+ // This maps an isl_id* to the Value* it has in the generated program. For now
+ // on, the only isl_ids that are stored here are the newly calculated loop
+ // ivs.
+ IslExprBuilder::IDToValueTy IDToValue;
+
+ /// A collection of all parallel subfunctions that have been created.
+ SmallVector<Function *, 8> ParallelSubfunctions;
+
+ /// Generate code for a given SCEV*
+ ///
+ /// This function generates code for a given SCEV expression. It generated
+ /// code is emitted at the end of the basic block our Builder currently
+ /// points to and the resulting value is returned.
+ ///
+ /// @param Expr The expression to code generate.
+ Value *generateSCEV(const SCEV *Expr);
+
+ /// A set of Value -> Value remappings to apply when generating new code.
+ ///
+ /// When generating new code for a ScopStmt this map is used to map certain
+ /// llvm::Values to new llvm::Values.
+ ValueMapT ValueMap;
+
+ /// Materialize code for @p Id if it was not done before.
+ ///
+ /// @returns False, iff a problem occurred and the value was not materialized.
+ bool materializeValue(__isl_take isl_id *Id);
+
+ /// Materialize parameters of @p Set.
+ ///
+ /// @returns False, iff a problem occurred and the value was not materialized.
+ bool materializeParameters(__isl_take isl_set *Set);
+
+ /// Materialize all parameters in the current scop.
+ ///
+ /// @returns False, iff a problem occurred and the value was not materialized.
+ bool materializeParameters();
+
+ // Extract the upper bound of this loop
+ //
+ // The isl code generation can generate arbitrary expressions to check if the
+ // upper bound of a loop is reached, but it provides an option to enforce
+ // 'atomic' upper bounds. An 'atomic upper bound is always of the form
+ // iv <= expr, where expr is an (arbitrary) expression not containing iv.
+ //
+ // This function extracts 'atomic' upper bounds. Polly, in general, requires
+ // atomic upper bounds for the following reasons:
+ //
+ // 1. An atomic upper bound is loop invariant
+ //
+ // It must not be calculated at each loop iteration and can often even be
+ // hoisted out further by the loop invariant code motion.
+ //
+ // 2. OpenMP needs a loop invariant upper bound to calculate the number
+ // of loop iterations.
+ //
+ // 3. With the existing code, upper bounds have been easier to implement.
+ isl::ast_expr getUpperBound(isl::ast_node_for For,
+ CmpInst::Predicate &Predicate);
+
+ /// Return non-negative number of iterations in case of the following form
+ /// of a loop and -1 otherwise.
+ ///
+ /// for (i = 0; i <= NumIter; i++) {
+ /// loop body;
+ /// }
+ ///
+ /// NumIter is a non-negative integer value. Condition can have
+ /// isl_ast_op_lt type.
+ int getNumberOfIterations(isl::ast_node_for For);
+
+ /// Compute the values and loops referenced in this subtree.
+ ///
+ /// This function looks at all ScopStmts scheduled below the provided For node
+ /// and finds the llvm::Value[s] and llvm::Loops[s] which are referenced but
+ /// not locally defined.
+ ///
+ /// Values that can be synthesized or that are available as globals are
+ /// considered locally defined.
+ ///
+ /// Loops that contain the scop or that are part of the scop are considered
+ /// locally defined. Loops that are before the scop, but do not contain the
+ /// scop itself are considered not locally defined.
+ ///
+ /// @param For The node defining the subtree.
+ /// @param Values A vector that will be filled with the Values referenced in
+ /// this subtree.
+ /// @param Loops A vector that will be filled with the Loops referenced in
+ /// this subtree.
+ void getReferencesInSubtree(const isl::ast_node &For,
+ SetVector<Value *> &Values,
+ SetVector<const Loop *> &Loops);
+
+ /// Change the llvm::Value(s) used for code generation.
+ ///
+ /// When generating code certain values (e.g., references to induction
+ /// variables or array base pointers) in the original code may be replaced by
+ /// new values. This function allows to (partially) update the set of values
+ /// used. A typical use case for this function is the case when we continue
+ /// code generation in a subfunction/kernel function and need to explicitly
+ /// pass down certain values.
+ ///
+ /// @param NewValues A map that maps certain llvm::Values to new llvm::Values.
+ void updateValues(ValueMapT &NewValues);
+
+ /// Return the most up-to-date version of the llvm::Value for code generation.
+ /// @param Original The Value to check for an up to date version.
+ /// @returns A remapped `Value` from ValueMap, or `Original` if no mapping
+ /// exists.
+ /// @see IslNodeBuilder::updateValues
+ /// @see IslNodeBuilder::ValueMap
+ Value *getLatestValue(Value *Original) const;
+
+ /// Generate code for a marker now.
+ ///
+ /// For mark nodes with an unknown name, we just forward the code generation
+ /// to its child. This is currently the only behavior implemented, as there is
+ /// currently not special handling for marker nodes implemented.
+ ///
+ /// @param Mark The node we generate code for.
+ virtual void createMark(__isl_take isl_ast_node *Marker);
+
+ virtual void createFor(__isl_take isl_ast_node *For);
+
+ /// Set to remember materialized invariant loads.
+ ///
+ /// An invariant load is identified by its pointer (the SCEV) and its type.
+ SmallSet<std::pair<const SCEV *, Type *>, 16> PreloadedPtrs;
+
+ /// Preload the memory access at @p AccessRange with @p Build.
+ ///
+ /// @returns The preloaded value casted to type @p Ty
+ Value *preloadUnconditionally(__isl_take isl_set *AccessRange,
+ isl_ast_build *Build, Instruction *AccInst);
+
+ /// Preload the memory load access @p MA.
+ ///
+ /// If @p MA is not always executed it will be conditionally loaded and
+ /// merged with undef from the same type. Hence, if @p MA is executed only
+ /// under condition C then the preload code will look like this:
+ ///
+ /// MA_preload = undef;
+ /// if (C)
+ /// MA_preload = load MA;
+ /// use MA_preload
+ Value *preloadInvariantLoad(const MemoryAccess &MA,
+ __isl_take isl_set *Domain);
+
+ /// Preload the invariant access equivalence class @p IAClass
+ ///
+ /// This function will preload the representing load from @p IAClass and
+ /// map all members of @p IAClass to that preloaded value, potentially casted
+ /// to the required type.
+ ///
+ /// @returns False, iff a problem occurred and the load was not preloaded.
+ bool preloadInvariantEquivClass(InvariantEquivClassTy &IAClass);
+
+ void createForVector(__isl_take isl_ast_node *For, int VectorWidth);
+ void createForSequential(isl::ast_node_for For, bool MarkParallel);
+
+ /// Create LLVM-IR that executes a for node thread parallel.
+ ///
+ /// @param For The FOR isl_ast_node for which code is generated.
+ void createForParallel(__isl_take isl_ast_node *For);
+
+ /// Create new access functions for modified memory accesses.
+ ///
+ /// In case the access function of one of the memory references in the Stmt
+ /// has been modified, we generate a new isl_ast_expr that reflects the
+ /// newly modified access function and return a map that maps from the
+ /// individual memory references in the statement (identified by their id)
+ /// to these newly generated ast expressions.
+ ///
+ /// @param Stmt The statement for which to (possibly) generate new access
+ /// functions.
+ /// @param Node The ast node corresponding to the statement for us to extract
+ /// the local schedule from.
+ /// @return A new hash table that contains remappings from memory ids to new
+ /// access expressions.
+ __isl_give isl_id_to_ast_expr *
+ createNewAccesses(ScopStmt *Stmt, __isl_keep isl_ast_node *Node);
+
+ /// Generate LLVM-IR that computes the values of the original induction
+ /// variables in function of the newly generated loop induction variables.
+ ///
+ /// Example:
+ ///
+ /// // Original
+ /// for i
+ /// for j
+ /// S(i)
+ ///
+ /// Schedule: [i,j] -> [i+j, j]
+ ///
+ /// // New
+ /// for c0
+ /// for c1
+ /// S(c0 - c1, c1)
+ ///
+ /// Assuming the original code consists of two loops which are
+ /// transformed according to a schedule [i,j] -> [c0=i+j,c1=j]. The resulting
+ /// ast models the original statement as a call expression where each argument
+ /// is an expression that computes the old induction variables from the new
+ /// ones, ordered such that the first argument computes the value of induction
+ /// variable that was outermost in the original code.
+ ///
+ /// @param Expr The call expression that represents the statement.
+ /// @param Stmt The statement that is called.
+ /// @param LTS The loop to SCEV map in which the mapping from the original
+ /// loop to a SCEV representing the new loop iv is added. This
+ /// mapping does not require an explicit induction variable.
+ /// Instead, we think in terms of an implicit induction variable
+ /// that counts the number of times a loop is executed. For each
+ /// original loop this count, expressed in function of the new
+ /// induction variables, is added to the LTS map.
+ void createSubstitutions(__isl_take isl_ast_expr *Expr, ScopStmt *Stmt,
+ LoopToScevMapT &LTS);
+ void createSubstitutionsVector(__isl_take isl_ast_expr *Expr, ScopStmt *Stmt,
+ std::vector<LoopToScevMapT> &VLTS,
+ std::vector<Value *> &IVS,
+ __isl_take isl_id *IteratorID);
+ virtual void createIf(__isl_take isl_ast_node *If);
+ void createUserVector(__isl_take isl_ast_node *User,
+ std::vector<Value *> &IVS,
+ __isl_take isl_id *IteratorID,
+ __isl_take isl_union_map *Schedule);
+ virtual void createUser(__isl_take isl_ast_node *User);
+ virtual void createBlock(__isl_take isl_ast_node *Block);
+
+ /// Get the schedule for a given AST node.
+ ///
+ /// This information is used to reason about parallelism of loops or the
+ /// locality of memory accesses under a given schedule.
+ ///
+ /// @param Node The node we want to obtain the schedule for.
+ /// @return Return an isl_union_map that maps from the statements executed
+ /// below this ast node to the scheduling vectors used to enumerate
+ /// them.
+ ///
+ virtual isl::union_map getScheduleForAstNode(const isl::ast_node &Node);
+
+private:
+ /// Create code for a copy statement.
+ ///
+ /// A copy statement is expected to have one read memory access and one write
+ /// memory access (in this very order). Data is loaded from the location
+ /// described by the read memory access and written to the location described
+ /// by the write memory access. @p NewAccesses contains for each access
+ /// the isl ast expression that describes the location accessed.
+ ///
+ /// @param Stmt The copy statement that contains the accesses.
+ /// @param NewAccesses The hash table that contains remappings from memory
+ /// ids to new access expressions.
+ void generateCopyStmt(ScopStmt *Stmt,
+ __isl_keep isl_id_to_ast_expr *NewAccesses);
+
+ /// Materialize a canonical loop induction variable for `L`, which is a loop
+ /// that is *not* present in the Scop.
+ ///
+ /// Note that this is materialized at the point where the `Builder` is
+ /// currently pointing.
+ /// We also populate the `OutsideLoopIterations` map with `L`s SCEV to keep
+ /// track of the induction variable.
+ /// See [Code generation of induction variables of loops outside Scops]
+ Value *materializeNonScopLoopInductionVariable(const Loop *L);
+};
+
+} // namespace polly
+
+#endif // POLLY_ISLNODEBUILDER_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGenerators.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGenerators.h
new file mode 100644
index 00000000000..17207b8ea13
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGenerators.h
@@ -0,0 +1,228 @@
+//===- LoopGenerators.h - IR helper to create loops -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains functions to create scalar and OpenMP parallel loops
+// as LLVM-IR.
+//
+//===----------------------------------------------------------------------===//
+#ifndef POLLY_LOOP_GENERATORS_H
+#define POLLY_LOOP_GENERATORS_H
+
+#include "polly/CodeGen/IRBuilder.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/ADT/SetVector.h"
+
+namespace polly {
+using llvm::AllocaInst;
+using llvm::BasicBlock;
+using llvm::DataLayout;
+using llvm::DominatorTree;
+using llvm::Function;
+using llvm::ICmpInst;
+using llvm::LoopInfo;
+using llvm::Module;
+using llvm::SetVector;
+using llvm::Type;
+using llvm::Value;
+
+/// General scheduling types of parallel OpenMP for loops.
+/// Initialization values taken from OpenMP's enum in kmp.h: sched_type.
+/// Currently, only 'static' scheduling may change from chunked to non-chunked.
+enum class OMPGeneralSchedulingType {
+ StaticChunked = 33,
+ StaticNonChunked = 34,
+ Dynamic = 35,
+ Guided = 36,
+ Runtime = 37
+};
+
+extern int PollyNumThreads;
+extern OMPGeneralSchedulingType PollyScheduling;
+extern int PollyChunkSize;
+
+/// Create a scalar do/for-style loop.
+///
+/// @param LowerBound The starting value of the induction variable.
+/// @param UpperBound The upper bound of the induction variable.
+/// @param Stride The value by which the induction variable
+/// is incremented.
+///
+/// @param Builder The builder used to create the loop.
+/// @param P A pointer to the pass that uses this function.
+/// It is used to update analysis information.
+/// @param LI The loop info for the current function
+/// @param DT The dominator tree we need to update
+/// @param ExitBlock The block the loop will exit to.
+/// @param Predicate The predicate used to generate the upper loop
+/// bound.
+/// @param Annotator This function can (optionally) take
+/// a ScopAnnotator which
+/// annotates loops and alias information in the SCoP.
+/// @param Parallel If this loop should be marked parallel in
+/// the Annotator.
+/// @param UseGuard Create a guard in front of the header to check if
+/// the loop is executed at least once, otherwise just
+/// assume it.
+/// @param LoopVectDisabled If the Loop vectorizer should be disabled for this
+/// loop.
+///
+/// @return Value* The newly created induction variable for this loop.
+Value *createLoop(Value *LowerBound, Value *UpperBound, Value *Stride,
+ PollyIRBuilder &Builder, LoopInfo &LI, DominatorTree &DT,
+ BasicBlock *&ExitBlock, ICmpInst::Predicate Predicate,
+ ScopAnnotator *Annotator = nullptr, bool Parallel = false,
+ bool UseGuard = true, bool LoopVectDisabled = false);
+
+/// The ParallelLoopGenerator allows to create parallelized loops
+///
+/// To parallelize a loop, we perform the following steps:
+/// o Generate a subfunction which will hold the loop body.
+/// o Create a struct to hold all outer values needed in the loop body.
+/// o Create calls to a runtime library to achieve the actual parallelism.
+/// These calls will spawn and join threads, define how the work (here the
+/// iterations) are distributed between them and make sure each has access
+/// to the struct holding all needed values.
+///
+/// At the moment we support only one parallel runtime, OpenMP.
+///
+/// If we parallelize the outer loop of the following loop nest,
+///
+/// S0;
+/// for (int i = 0; i < N; i++)
+/// for (int j = 0; j < M; j++)
+/// S1(i, j);
+/// S2;
+///
+/// we will generate the following code (with different runtime function names):
+///
+/// S0;
+/// auto *values = storeValuesIntoStruct();
+/// // Execute subfunction with multiple threads
+/// spawn_threads(subfunction, values);
+/// join_threads();
+/// S2;
+///
+/// // This function is executed in parallel by different threads
+/// void subfunction(values) {
+/// while (auto *WorkItem = getWorkItem()) {
+/// int LB = WorkItem.begin();
+/// int UB = WorkItem.end();
+/// for (int i = LB; i < UB; i++)
+/// for (int j = 0; j < M; j++)
+/// S1(i, j);
+/// }
+/// cleanup_thread();
+/// }
+class ParallelLoopGenerator {
+public:
+ /// Create a parallel loop generator for the current function.
+ ParallelLoopGenerator(PollyIRBuilder &Builder, LoopInfo &LI,
+ DominatorTree &DT, const DataLayout &DL)
+ : Builder(Builder), LI(LI), DT(DT),
+ LongType(
+ Type::getIntNTy(Builder.getContext(), DL.getPointerSizeInBits())),
+ M(Builder.GetInsertBlock()->getParent()->getParent()) {}
+
+ virtual ~ParallelLoopGenerator() {}
+
+ /// Create a parallel loop.
+ ///
+ /// This function is the main function to automatically generate a parallel
+ /// loop with all its components.
+ ///
+ /// @param LB The lower bound for the loop we parallelize.
+ /// @param UB The upper bound for the loop we parallelize.
+ /// @param Stride The stride of the loop we parallelize.
+ /// @param Values A set of LLVM-IR Values that should be available in
+ /// the new loop body.
+ /// @param VMap A map to allow outside access to the new versions of
+ /// the values in @p Values.
+ /// @param LoopBody A pointer to an iterator that is set to point to the
+ /// body of the created loop. It should be used to insert
+ /// instructions that form the actual loop body.
+ ///
+ /// @return The newly created induction variable for this loop.
+ Value *createParallelLoop(Value *LB, Value *UB, Value *Stride,
+ SetVector<Value *> &Values, ValueMapT &VMap,
+ BasicBlock::iterator *LoopBody);
+
+protected:
+ /// The IR builder we use to create instructions.
+ PollyIRBuilder &Builder;
+
+ /// The loop info of the current function we need to update.
+ LoopInfo &LI;
+
+ /// The dominance tree of the current function we need to update.
+ DominatorTree &DT;
+
+ /// The type of a "long" on this hardware used for backend calls.
+ Type *LongType;
+
+ /// The current module
+ Module *M;
+
+public:
+ /// Create a struct for all @p Values and store them in there.
+ ///
+ /// @param Values The values which should be stored in the struct.
+ ///
+ /// @return The created struct.
+ AllocaInst *storeValuesIntoStruct(SetVector<Value *> &Values);
+
+ /// Extract all values from the @p Struct and construct the mapping.
+ ///
+ /// @param Values The values which were stored in the struct.
+ /// @param Struct The struct holding all the values in @p Values.
+ /// @param VMap A map to associate every element of @p Values with the
+ /// new llvm value loaded from the @p Struct.
+ void extractValuesFromStruct(SetVector<Value *> Values, Type *Ty,
+ Value *Struct, ValueMapT &VMap);
+
+ /// Create the definition of the parallel subfunction.
+ ///
+ /// @return A pointer to the subfunction.
+ Function *createSubFnDefinition();
+
+ /// Create the runtime library calls for spawn and join of the worker threads.
+ /// Additionally, places a call to the specified subfunction.
+ ///
+ /// @param SubFn The subfunction which holds the loop body.
+ /// @param SubFnParam The parameter for the subfunction (basically the struct
+ /// filled with the outside values).
+ /// @param LB The lower bound for the loop we parallelize.
+ /// @param UB The upper bound for the loop we parallelize.
+ /// @param Stride The stride of the loop we parallelize.
+ virtual void deployParallelExecution(Function *SubFn, Value *SubFnParam,
+ Value *LB, Value *UB, Value *Stride) = 0;
+
+ /// Prepare the definition of the parallel subfunction.
+ /// Creates the argument list and names them (as well as the subfunction).
+ ///
+ /// @param F A pointer to the (parallel) subfunction's parent function.
+ ///
+ /// @return The pointer to the (parallel) subfunction.
+ virtual Function *prepareSubFnDefinition(Function *F) const = 0;
+
+ /// Create the parallel subfunction.
+ ///
+ /// @param Stride The induction variable increment.
+ /// @param Struct A struct holding all values in @p Values.
+ /// @param Values A set of LLVM-IR Values that should be available in
+ /// the new loop body.
+ /// @param VMap A map to allow outside access to the new versions of
+ /// the values in @p Values.
+ /// @param SubFn The newly created subfunction is returned here.
+ ///
+ /// @return The newly created induction variable.
+ virtual std::tuple<Value *, Function *>
+ createSubFn(Value *Stride, AllocaInst *Struct, SetVector<Value *> UsedValues,
+ ValueMapT &VMap) = 0;
+};
+} // end namespace polly
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGeneratorsGOMP.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGeneratorsGOMP.h
new file mode 100644
index 00000000000..1f47d38f016
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGeneratorsGOMP.h
@@ -0,0 +1,74 @@
+//===- LoopGeneratorsGOMP.h - IR helper to create loops ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains functions to create scalar and OpenMP parallel loops
+// as LLVM-IR.
+//
+//===----------------------------------------------------------------------===//
+#ifndef POLLY_LOOP_GENERATORS_GOMP_H
+#define POLLY_LOOP_GENERATORS_GOMP_H
+
+#include "polly/CodeGen/IRBuilder.h"
+#include "polly/CodeGen/LoopGenerators.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/ADT/SetVector.h"
+
+namespace polly {
+
+/// This ParallelLoopGenerator subclass handles the generation of parallelized
+/// code, utilizing the GNU OpenMP library.
+class ParallelLoopGeneratorGOMP : public ParallelLoopGenerator {
+public:
+ /// Create a parallel loop generator for the current function.
+ ParallelLoopGeneratorGOMP(PollyIRBuilder &Builder, LoopInfo &LI,
+ DominatorTree &DT, const DataLayout &DL)
+ : ParallelLoopGenerator(Builder, LI, DT, DL) {}
+
+ // The functions below may be used if one does not want to generate a
+ // specific OpenMP parallel loop, but generate individual parts of it
+ // (e.g. the subfunction definition).
+
+ /// Create a runtime library call to spawn the worker threads.
+ ///
+ /// @param SubFn The subfunction which holds the loop body.
+ /// @param SubFnParam The parameter for the subfunction (basically the struct
+ /// filled with the outside values).
+ /// @param LB The lower bound for the loop we parallelize.
+ /// @param UB The upper bound for the loop we parallelize.
+ /// @param Stride The stride of the loop we parallelize.
+ void createCallSpawnThreads(Value *SubFn, Value *SubFnParam, Value *LB,
+ Value *UB, Value *Stride);
+
+ void deployParallelExecution(Function *SubFn, Value *SubFnParam, Value *LB,
+ Value *UB, Value *Stride) override;
+
+ virtual Function *prepareSubFnDefinition(Function *F) const override;
+
+ std::tuple<Value *, Function *> createSubFn(Value *Stride, AllocaInst *Struct,
+ SetVector<Value *> UsedValues,
+ ValueMapT &VMap) override;
+
+ /// Create a runtime library call to join the worker threads.
+ void createCallJoinThreads();
+
+ /// Create a runtime library call to get the next work item.
+ ///
+ /// @param LBPtr A pointer value to store the work item begin in.
+ /// @param UBPtr A pointer value to store the work item end in.
+ ///
+ /// @returns A true value if the work item is not empty.
+ Value *createCallGetWorkItem(Value *LBPtr, Value *UBPtr);
+
+ /// Create a runtime library call to allow cleanup of the thread.
+ ///
+ /// @note This function is called right before the thread will exit the
+ /// subfunction and only if the runtime system depends on it.
+ void createCallCleanupThread();
+};
+} // end namespace polly
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGeneratorsKMP.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGeneratorsKMP.h
new file mode 100644
index 00000000000..c4921aced76
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/LoopGeneratorsKMP.h
@@ -0,0 +1,145 @@
+//===- LoopGeneratorsKMP.h - IR helper to create loops ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains functions to create scalar and OpenMP parallel loops
+// as LLVM-IR.
+//
+//===----------------------------------------------------------------------===//
+#ifndef POLLY_LOOP_GENERATORS_KMP_H
+#define POLLY_LOOP_GENERATORS_KMP_H
+
+#include "polly/CodeGen/IRBuilder.h"
+#include "polly/CodeGen/LoopGenerators.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/ADT/SetVector.h"
+
+namespace polly {
+using llvm::GlobalValue;
+using llvm::GlobalVariable;
+
+/// This ParallelLoopGenerator subclass handles the generation of parallelized
+/// code, utilizing the LLVM OpenMP library.
+class ParallelLoopGeneratorKMP : public ParallelLoopGenerator {
+public:
+ /// Create a parallel loop generator for the current function.
+ ParallelLoopGeneratorKMP(PollyIRBuilder &Builder, LoopInfo &LI,
+ DominatorTree &DT, const DataLayout &DL)
+ : ParallelLoopGenerator(Builder, LI, DT, DL) {
+ SourceLocationInfo = createSourceLocation();
+ }
+
+protected:
+ /// The source location struct of this loop.
+ /// ident_t = type { i32, i32, i32, i32, i8* }
+ GlobalValue *SourceLocationInfo;
+
+ /// Convert the combination of given chunk size and scheduling type (which
+ /// might have been set via the command line) into the corresponding
+ /// scheduling type. This may result (e.g.) in a 'change' from
+ /// "static chunked" scheduling to "static non-chunked" (regarding the
+ /// provided and returned scheduling types).
+ ///
+ /// @param ChunkSize The chunk size, set via command line or its default.
+ /// @param Scheduling The scheduling, set via command line or its default.
+ ///
+ /// @return The corresponding OMPGeneralSchedulingType.
+ OMPGeneralSchedulingType
+ getSchedType(int ChunkSize, OMPGeneralSchedulingType Scheduling) const;
+
+ /// Returns True if 'LongType' is 64bit wide, otherwise: False.
+ bool is64BitArch();
+
+public:
+ // The functions below may be used if one does not want to generate a
+ // specific OpenMP parallel loop, but generate individual parts of it
+ // (e.g. the subfunction definition).
+
+ /// Create a runtime library call to spawn the worker threads.
+ ///
+ /// @param SubFn The subfunction which holds the loop body.
+ /// @param SubFnParam The parameter for the subfunction (basically the struct
+ /// filled with the outside values).
+ /// @param LB The lower bound for the loop we parallelize.
+ /// @param UB The upper bound for the loop we parallelize.
+ /// @param Stride The stride of the loop we parallelize.
+ void createCallSpawnThreads(Value *SubFn, Value *SubFnParam, Value *LB,
+ Value *UB, Value *Stride);
+
+ void deployParallelExecution(Function *SubFn, Value *SubFnParam, Value *LB,
+ Value *UB, Value *Stride) override;
+
+ virtual Function *prepareSubFnDefinition(Function *F) const override;
+
+ std::tuple<Value *, Function *> createSubFn(Value *Stride, AllocaInst *Struct,
+ SetVector<Value *> UsedValues,
+ ValueMapT &VMap) override;
+
+ /// Create a runtime library call to get the current global thread number.
+ ///
+ /// @return A Value ref which holds the current global thread number.
+ Value *createCallGlobalThreadNum();
+
+ /// Create a runtime library call to request a number of threads.
+ /// Which will be used in the next OpenMP section (by the next fork).
+ ///
+ /// @param GlobalThreadID The global thread ID.
+ /// @param NumThreads The number of threads to use.
+ void createCallPushNumThreads(Value *GlobalThreadID, Value *NumThreads);
+
+ /// Create a runtime library call to prepare the OpenMP runtime.
+ /// For dynamically scheduled loops, saving the loop arguments.
+ ///
+ /// @param GlobalThreadID The global thread ID.
+ /// @param LB The loop's lower bound.
+ /// @param UB The loop's upper bound.
+ /// @param Inc The loop increment.
+ /// @param ChunkSize The chunk size of the parallel loop.
+ void createCallDispatchInit(Value *GlobalThreadID, Value *LB, Value *UB,
+ Value *Inc, Value *ChunkSize);
+
+ /// Create a runtime library call to retrieve the next (dynamically)
+ /// allocated chunk of work for this thread.
+ ///
+ /// @param GlobalThreadID The global thread ID.
+ /// @param IsLastPtr Pointer to a flag, which is set to 1 if this is
+ /// the last chunk of work, or 0 otherwise.
+ /// @param LBPtr Pointer to the lower bound for the next chunk.
+ /// @param UBPtr Pointer to the upper bound for the next chunk.
+ /// @param StridePtr Pointer to the stride for the next chunk.
+ ///
+ /// @return A Value which holds 1 if there is work to be done, 0 otherwise.
+ Value *createCallDispatchNext(Value *GlobalThreadID, Value *IsLastPtr,
+ Value *LBPtr, Value *UBPtr, Value *StridePtr);
+
+ /// Create a runtime library call to prepare the OpenMP runtime.
+ /// For statically scheduled loops, saving the loop arguments.
+ ///
+ /// @param GlobalThreadID The global thread ID.
+ /// @param IsLastPtr Pointer to a flag, which is set to 1 if this is
+ /// the last chunk of work, or 0 otherwise.
+ /// @param LBPtr Pointer to the lower bound for the next chunk.
+ /// @param UBPtr Pointer to the upper bound for the next chunk.
+ /// @param StridePtr Pointer to the stride for the next chunk.
+ /// @param ChunkSize The chunk size of the parallel loop.
+ void createCallStaticInit(Value *GlobalThreadID, Value *IsLastPtr,
+ Value *LBPtr, Value *UBPtr, Value *StridePtr,
+ Value *ChunkSize);
+
+ /// Create a runtime library call to mark the end of
+ /// a statically scheduled loop.
+ ///
+ /// @param GlobalThreadID The global thread ID.
+ void createCallStaticFini(Value *GlobalThreadID);
+
+ /// Create the current source location.
+ ///
+ /// TODO: Generates only(!) dummy values.
+ GlobalVariable *createSourceLocation();
+};
+} // end namespace polly
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/PPCGCodeGeneration.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/PPCGCodeGeneration.h
new file mode 100644
index 00000000000..9a6c596e5f4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/PPCGCodeGeneration.h
@@ -0,0 +1,33 @@
+//===--- polly/PPCGCodeGeneration.h - Polly Accelerator Code Generation. --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Take a scop created by ScopInfo and map it to GPU code using the ppcg
+// GPU mapping strategy.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_PPCGCODEGENERATION_H
+#define POLLY_PPCGCODEGENERATION_H
+
+/// The GPU Architecture to target.
+enum GPUArch { NVPTX64, SPIR32, SPIR64 };
+
+/// The GPU Runtime implementation to use.
+enum GPURuntime { CUDA, OpenCL };
+
+namespace polly {
+extern bool PollyManagedMemory;
+
+/// Use for pass instantiation defaults.
+/// @{
+extern GPURuntime GPURuntimeChoice;
+extern GPUArch GPUArchChoice;
+/// @}
+} // namespace polly
+
+#endif // POLLY_PPCGCODEGENERATION_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/PerfMonitor.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/PerfMonitor.h
new file mode 100644
index 00000000000..81aa4c7ee44
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/PerfMonitor.h
@@ -0,0 +1,142 @@
+//===--- PerfMonitor.h --- Monitor time spent in scops --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef PERF_MONITOR_H
+#define PERF_MONITOR_H
+
+#include "polly/CodeGen/IRBuilder.h"
+
+namespace polly {
+
+class PerfMonitor {
+public:
+ /// Create a new performance monitor.
+ ///
+ /// @param S The scop for which to generate fine-grained performance
+ /// monitoring information.
+ /// @param M The module for which to generate the performance monitor.
+ PerfMonitor(const Scop &S, llvm::Module *M);
+
+ /// Initialize the performance monitor.
+ ///
+ /// Ensure that all global variables, functions, and callbacks needed to
+ /// manage the performance monitor are initialized and registered.
+ void initialize();
+
+ /// Mark the beginning of a timing region.
+ ///
+ /// @param InsertBefore The instruction before which the timing region starts.
+ void insertRegionStart(llvm::Instruction *InsertBefore);
+
+ /// Mark the end of a timing region.
+ ///
+ /// @param InsertBefore The instruction before which the timing region starts.
+ void insertRegionEnd(llvm::Instruction *InsertBefore);
+
+private:
+ llvm::Module *M;
+ PollyIRBuilder Builder;
+
+ // The scop to profile against.
+ const Scop &S;
+
+ /// Indicates if performance profiling is supported on this architecture.
+ bool Supported;
+
+ /// The cycle counter at the beginning of the program execution.
+ llvm::Value *CyclesTotalStartPtr;
+
+ /// The total number of cycles spent in the current scop S.
+ llvm::Value *CyclesInCurrentScopPtr;
+
+ /// The total number of times the current scop S is executed.
+ llvm::Value *TripCountForCurrentScopPtr;
+
+ /// The total number of cycles spent within scops.
+ llvm::Value *CyclesInScopsPtr;
+
+ /// The value of the cycle counter at the beginning of the last scop.
+ llvm::Value *CyclesInScopStartPtr;
+
+ /// A global variable, that keeps track if the performance monitor
+ /// initialization has already been run.
+ llvm::Value *AlreadyInitializedPtr;
+
+ llvm::Function *insertInitFunction(llvm::Function *FinalReporting);
+
+ /// Add Function @p to list of global constructors
+ ///
+ /// If no global constructors are available in this current module, insert
+ /// a new list of global constructors containing @p Fn as only global
+ /// constructor. Otherwise, append @p Fn to the list of global constructors.
+ ///
+ /// All functions listed as global constructors are executed before the
+ /// main() function is called.
+ ///
+ /// @param Fn Function to add to global constructors
+ void addToGlobalConstructors(llvm::Function *Fn);
+
+ /// Add global variables to module.
+ ///
+ /// Insert a set of global variables that are used to track performance,
+ /// into the module (or obtain references to them if they already exist).
+ void addGlobalVariables();
+
+ /// Add per-scop tracking to module.
+ ///
+ /// Insert the global variable which is used to track the number of cycles
+ /// this scop runs.
+ void addScopCounter();
+
+ /// Get a reference to the intrinsic "{ i64, i32 } @llvm.x86.rdtscp()".
+ ///
+ /// The rdtscp function returns the current value of the processor's
+ /// time-stamp counter as well as the current CPU identifier. On modern x86
+ /// systems, the returned value is independent of the dynamic clock frequency
+ /// and consistent across multiple cores. It can consequently be used to get
+ /// accurate and low-overhead timing information. Even though the counter is
+ /// wrapping, it can be reliably used even for measuring longer time
+ /// intervals, as on a 1 GHz processor the counter only wraps every 545 years.
+ ///
+ /// The RDTSCP instruction is "pseudo" serializing:
+ ///
+ /// "“The RDTSCP instruction waits until all previous instructions have been
+ /// executed before reading the counter. However, subsequent instructions may
+ /// begin execution before the read operation is performed.”
+ ///
+ /// To ensure that no later instructions are scheduled before the RDTSCP
+ /// instruction it is often recommended to schedule a cpuid call after the
+ /// RDTSCP instruction. We do not do this yet, trading some imprecision in
+ /// our timing for a reduced overhead in our timing.
+ ///
+ /// @returns A reference to the declaration of @llvm.x86.rdtscp.
+ llvm::Function *getRDTSCP();
+
+ /// Get a reference to "int atexit(void (*function)(void))" function.
+ ///
+ /// This function allows to register function pointers that must be executed
+ /// when the program is terminated.
+ ///
+ /// @returns A reference to @atexit().
+ llvm::Function *getAtExit();
+
+ /// Create function "__polly_perf_final_reporting".
+ ///
+ /// This function finalizes the performance measurements and prints the
+ /// results to stdout. It is expected to be registered with 'atexit()'.
+ llvm::Function *insertFinalReporting();
+
+ /// Append Scop reporting data to "__polly_perf_final_reporting".
+ ///
+ /// This function appends the current scop (S)'s information to the final
+ /// printing function.
+ void AppendScopReporting();
+};
+} // namespace polly
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/RuntimeDebugBuilder.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/RuntimeDebugBuilder.h
new file mode 100644
index 00000000000..c40b53c1b54
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/RuntimeDebugBuilder.h
@@ -0,0 +1,169 @@
+//===--- RuntimeDebugBuilder.h --- Helper to insert prints into LLVM-IR ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef RUNTIME_DEBUG_BUILDER_H
+#define RUNTIME_DEBUG_BUILDER_H
+
+#include "polly/CodeGen/IRBuilder.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include <vector>
+
+namespace llvm {
+class Value;
+class Function;
+} // namespace llvm
+
+namespace polly {
+
+/// Insert function calls that print certain LLVM values at run time.
+///
+/// This class inserts libc function calls to print certain LLVM values at
+/// run time.
+struct RuntimeDebugBuilder {
+
+ /// Generate a constant string into the builder's llvm::Module which can be
+ /// passed to createGPUPrinter() or createGPUPrinter().
+ ///
+ /// @param Builder The builder used to emit the printer calls.
+ /// @param Str The string to be printed.
+
+ /// @return A global containing @p Str.
+ static llvm::Value *getPrintableString(PollyIRBuilder &Builder,
+ llvm::StringRef Str) {
+ // TODO: Get rid of magic number 4. It it NVPTX's constant address space and
+ // works on X86 (CPU) only because its backend ignores the address space.
+ return Builder.CreateGlobalStringPtr(Str, "", 4);
+ }
+
+ /// Return whether an llvm::Value of the type @p Ty is printable for
+ /// debugging.
+ ///
+ /// That is, whether such a value can be passed to createGPUPrinter() or
+ /// createGPUPrinter() to be dumped as runtime. If false is returned, those
+ /// functions will fail.
+ static bool isPrintable(llvm::Type *Ty);
+
+ /// Print a set of LLVM-IR Values or StringRefs via printf
+ ///
+ /// This function emits a call to printf that will print the given arguments.
+ /// It is useful for debugging CPU programs. All arguments given in this list
+ /// will be automatically concatenated and the resulting string will be
+ /// printed atomically. We also support ArrayRef arguments, which can be used
+ /// to provide of id values.
+ ///
+ /// @param Builder The builder used to emit the printer calls.
+ /// @param Args The list of values to print.
+ template <typename... Args>
+ static void createCPUPrinter(PollyIRBuilder &Builder, Args... args) {
+ std::vector<llvm::Value *> Vector;
+ createPrinter(Builder, /* CPU */ false, Vector, args...);
+ }
+
+ /// Print a set of LLVM-IR Values or StringRefs on an NVIDIA GPU.
+ ///
+ /// This function emits a call to vprintf that will print the given
+ /// arguments from within a kernel thread. It is useful for debugging
+ /// CUDA program kernels. All arguments given in this list will be
+ /// automatically concatenated and the resulting string will be printed
+ /// atomically. We also support ArrayRef arguments, which can be used to
+ /// provide for example a list of thread-id values.
+ ///
+ /// @param Builder The builder used to emit the printer calls.
+ /// @param Args The list of values to print.
+ template <typename... Args>
+ static void createGPUPrinter(PollyIRBuilder &Builder, Args... args) {
+ std::vector<llvm::Value *> Vector;
+ createPrinter(Builder, /* GPU */ true, Vector, args...);
+ }
+
+private:
+ /// Handle Values.
+ template <typename... Args>
+ static void createPrinter(PollyIRBuilder &Builder, bool UseGPU,
+ std::vector<llvm::Value *> &Values,
+ llvm::Value *Value, Args... args) {
+ Values.push_back(Value);
+ createPrinter(Builder, UseGPU, Values, args...);
+ }
+
+ /// Handle StringRefs.
+ template <typename... Args>
+ static void createPrinter(PollyIRBuilder &Builder, bool UseGPU,
+ std::vector<llvm::Value *> &Values,
+ llvm::StringRef String, Args... args) {
+ Values.push_back(getPrintableString(Builder, String));
+ createPrinter(Builder, UseGPU, Values, args...);
+ }
+
+ /// Handle ArrayRefs.
+ template <typename... Args>
+ static void createPrinter(PollyIRBuilder &Builder, bool UseGPU,
+ std::vector<llvm::Value *> &Values,
+ llvm::ArrayRef<llvm::Value *> Array, Args... args) {
+ Values.insert(Values.end(), Array.begin(), Array.end());
+ createPrinter(Builder, UseGPU, Values, args...);
+ }
+
+ /// Print a list of Values.
+ static void createPrinter(PollyIRBuilder &Builder, bool UseGPU,
+ llvm::ArrayRef<llvm::Value *> Values);
+
+ /// Print a list of Values on a GPU.
+ static void createGPUPrinterT(PollyIRBuilder &Builder,
+ llvm::ArrayRef<llvm::Value *> Values);
+
+ /// Print a list of Values on a CPU.
+ static void createCPUPrinterT(PollyIRBuilder &Builder,
+ llvm::ArrayRef<llvm::Value *> Values);
+
+ /// Get a reference to the 'printf' function.
+ ///
+ /// If the current module does not yet contain a reference to printf, we
+ /// insert a reference to it. Otherwise the existing reference is returned.
+ static llvm::Function *getPrintF(PollyIRBuilder &Builder);
+
+ /// Call printf
+ ///
+ /// @param Builder The builder used to insert the code.
+ /// @param Format The format string.
+ /// @param Values The set of values to print.
+ static void createPrintF(PollyIRBuilder &Builder, std::string Format,
+ llvm::ArrayRef<llvm::Value *> Values);
+
+ /// Get (and possibly insert) a vprintf declaration into the module.
+ static llvm::Function *getVPrintF(PollyIRBuilder &Builder);
+
+ /// Call fflush
+ ///
+ /// @parma Builder The builder used to insert the code.
+ static void createFlush(PollyIRBuilder &Builder);
+
+ /// Get (and possibly insert) a NVIDIA address space cast call.
+ static llvm::Function *getAddressSpaceCast(PollyIRBuilder &Builder,
+ unsigned Src, unsigned Dst,
+ unsigned SrcBits = 8,
+ unsigned DstBits = 8);
+
+ /// Get identifiers that describe the currently executed GPU thread.
+ ///
+ /// The result will be a vector that if passed to the GPU printer will result
+ /// into a string (initialized to values corresponding to the printing
+ /// thread):
+ ///
+ /// "> block-id: bidx bid1y bidz | thread-id: tidx tidy tidz "
+ static std::vector<llvm::Value *>
+ getGPUThreadIdentifiers(PollyIRBuilder &Builder);
+};
+} // namespace polly
+
+extern bool PollyDebugPrinting;
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/Utils.h b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/Utils.h
new file mode 100644
index 00000000000..0678e34b870
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodeGen/Utils.h
@@ -0,0 +1,72 @@
+//===- Utils.h - Utility functions for code generation ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains utility functions for the code generation.
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_CODEGEN_UTILS_H
+#define POLLY_CODEGEN_UTILS_H
+
+#include <utility>
+
+namespace llvm {
+class Pass;
+class Value;
+class BasicBlock;
+class DominatorTree;
+class RegionInfo;
+class LoopInfo;
+class BranchInst;
+} // namespace llvm
+
+namespace polly {
+
+class Scop;
+
+using BBPair = std::pair<llvm::BasicBlock *, llvm::BasicBlock *>;
+/// Execute a Scop conditionally wrt @p RTC.
+///
+/// In the CFG the optimized code of the Scop is generated next to the
+/// original code. Both the new and the original version of the code remain
+/// in the CFG. A branch statement decides which version is executed based on
+/// the runtime value of @p RTC.
+///
+/// Before transformation:
+///
+/// bb0
+/// |
+/// orig_scop
+/// |
+/// bb1
+///
+/// After transformation:
+/// bb0
+/// |
+/// polly.splitBlock
+/// / \.
+/// | startBlock
+/// | |
+/// orig_scop new_scop
+/// \ /
+/// \ /
+/// bb1 (joinBlock)
+///
+/// @param S The Scop to execute conditionally.
+/// @param P A reference to the pass calling this function.
+/// @param RTC The runtime condition checked before executing the new SCoP.
+///
+/// @return An std::pair:
+/// - The first element is a BBPair of (StartBlock, EndBlock).
+/// - The second element is the BranchInst which conditionally
+/// branches to the SCoP based on the RTC.
+///
+std::pair<BBPair, llvm::BranchInst *>
+executeScopConditionally(Scop &S, llvm::Value *RTC, llvm::DominatorTree &DT,
+ llvm::RegionInfo &RI, llvm::LoopInfo &LI);
+} // namespace polly
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/CodePreparation.h b/contrib/libs/llvm14/tools/polly/include/polly/CodePreparation.h
new file mode 100644
index 00000000000..fb1942b550e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/CodePreparation.h
@@ -0,0 +1,25 @@
+//===- polly/ScopPreparation.h - Code preparation pass ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Prepare the Function for polyhedral codegeneration.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_CODEPREPARATION_H
+#define POLLY_CODEPREPARATION_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace polly {
+struct CodePreparationPass : public llvm::PassInfoMixin<CodePreparationPass> {
+ llvm::PreservedAnalyses run(llvm::Function &F,
+ llvm::FunctionAnalysisManager &FAM);
+};
+} // namespace polly
+
+#endif /* POLLY_CODEPREPARATION_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Config/config.h b/contrib/libs/llvm14/tools/polly/include/polly/Config/config.h
new file mode 100644
index 00000000000..72d6d98581a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Config/config.h
@@ -0,0 +1,18 @@
+//===- polly/Config.h ------------ Configuration of Polly -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Configuration of Polly.
+//
+//===----------------------------------------------------------------------===//
+#ifndef POLLY_CONFIG_H
+#define POLLY_CONFIG_H
+
+/* #undef CUDA_FOUND */
+#define GPU_CODEGEN
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/DeLICM.h b/contrib/libs/llvm14/tools/polly/include/polly/DeLICM.h
new file mode 100644
index 00000000000..3b6928baa5d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/DeLICM.h
@@ -0,0 +1,67 @@
+//===------ DeLICM.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Undo the effect of Loop Invariant Code Motion (LICM) and
+// GVN Partial Redundancy Elimination (PRE) on SCoP-level.
+//
+// Namely, remove register/scalar dependencies by mapping them back to array
+// elements.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_DELICM_H
+#define POLLY_DELICM_H
+
+#include "polly/ScopPass.h"
+#include "isl/isl-noexceptions.h"
+
+namespace llvm {
+class PassRegistry;
+class Pass;
+class raw_ostream;
+} // namespace llvm
+
+namespace polly {
+/// Create a new DeLICM pass instance.
+llvm::Pass *createDeLICMWrapperPass();
+
+struct DeLICMPass : llvm::PassInfoMixin<DeLICMPass> {
+ DeLICMPass() {}
+
+ llvm::PreservedAnalyses run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U);
+};
+
+struct DeLICMPrinterPass : llvm::PassInfoMixin<DeLICMPrinterPass> {
+ DeLICMPrinterPass(raw_ostream &OS) : OS(OS) {}
+
+ PreservedAnalyses run(Scop &S, ScopAnalysisManager &,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &);
+
+private:
+ llvm::raw_ostream &OS;
+};
+
+/// Determine whether two lifetimes are conflicting.
+///
+/// Used by unittesting.
+bool isConflicting(isl::union_set ExistingOccupied,
+ isl::union_set ExistingUnused, isl::union_map ExistingKnown,
+ isl::union_map ExistingWrites,
+ isl::union_set ProposedOccupied,
+ isl::union_set ProposedUnused, isl::union_map ProposedKnown,
+ isl::union_map ProposedWrites,
+ llvm::raw_ostream *OS = nullptr, unsigned Indent = 0);
+
+} // namespace polly
+
+namespace llvm {
+void initializeDeLICMWrapperPassPass(llvm::PassRegistry &);
+} // namespace llvm
+
+#endif /* POLLY_DELICM_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/DeadCodeElimination.h b/contrib/libs/llvm14/tools/polly/include/polly/DeadCodeElimination.h
new file mode 100644
index 00000000000..044bc2ae84e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/DeadCodeElimination.h
@@ -0,0 +1,40 @@
+//===- DeadCodeElimination.h ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Eliminate dead iterations.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_DEADCODEELIMINATION_H
+#define POLLY_DEADCODEELIMINATION_H
+
+#include "polly/ScopPass.h"
+
+namespace llvm {
+class PassRegistry;
+class Pass;
+class raw_ostream;
+} // namespace llvm
+
+namespace polly {
+llvm::Pass *createDeadCodeElimWrapperPass();
+
+struct DeadCodeElimPass : llvm::PassInfoMixin<DeadCodeElimPass> {
+ DeadCodeElimPass() {}
+
+ llvm::PreservedAnalyses run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U);
+};
+
+} // namespace polly
+
+namespace llvm {
+void initializeDeadCodeElimWrapperPassPass(llvm::PassRegistry &);
+} // namespace llvm
+
+#endif /* POLLY_DEADCODEELIMINATION_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/DependenceInfo.h b/contrib/libs/llvm14/tools/polly/include/polly/DependenceInfo.h
new file mode 100644
index 00000000000..3d70ea2b74e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/DependenceInfo.h
@@ -0,0 +1,311 @@
+//===--- polly/DependenceInfo.h - Polyhedral dependency analysis *- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Calculate the data dependency relations for a Scop using ISL.
+//
+// The integer set library (ISL) from Sven has an integrated dependency analysis
+// to calculate data dependences. This pass takes advantage of this and
+// calculates those dependences of a Scop.
+//
+// The dependences in this pass are exact in terms that for a specific read
+// statement instance only the last write statement instance is returned. In
+// case of may-writes, a set of possible write instances is returned. This
+// analysis will never produce redundant dependences.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_DEPENDENCE_INFO_H
+#define POLLY_DEPENDENCE_INFO_H
+
+#include "polly/ScopPass.h"
+#include "isl/ctx.h"
+#include "isl/isl-noexceptions.h"
+
+namespace polly {
+
+/// The accumulated dependence information for a SCoP.
+///
+/// The Dependences struct holds all dependence information we collect and
+/// compute for one SCoP. It also offers an interface that allows users to
+/// query only specific parts.
+struct Dependences {
+ // Granularities of the current dependence analysis
+ enum AnalysisLevel {
+ AL_Statement = 0,
+ // Distinguish accessed memory references in the same statement
+ AL_Reference,
+ // Distinguish memory access instances in the same statement
+ AL_Access,
+
+ NumAnalysisLevels
+ };
+
+ /// Map type for reduction dependences.
+ using ReductionDependencesMapTy = DenseMap<MemoryAccess *, isl_map *>;
+
+ /// Map type to associate statements with schedules.
+ using StatementToIslMapTy = DenseMap<ScopStmt *, isl::map>;
+
+ /// The type of the dependences.
+ ///
+ /// Reduction dependences are separated from RAW/WAW/WAR dependences because
+ /// we can ignore them during the scheduling. That's because the order
+ /// in which the reduction statements are executed does not matter. However,
+ /// if they are executed in parallel we need to take additional measures
+ /// (e.g, privatization) to ensure a correct result. The (reverse) transitive
+ /// closure of the reduction dependences are used to check for parallel
+ /// executed reduction statements during code generation. These dependences
+ /// connect all instances of a reduction with each other, they are therefore
+ /// cyclic and possibly "reversed".
+ enum Type {
+ // Write after read
+ TYPE_WAR = 1 << 0,
+
+ // Read after write
+ TYPE_RAW = 1 << 1,
+
+ // Write after write
+ TYPE_WAW = 1 << 2,
+
+ // Reduction dependences
+ TYPE_RED = 1 << 3,
+
+ // Transitive closure of the reduction dependences (& the reverse)
+ TYPE_TC_RED = 1 << 4,
+ };
+
+ const std::shared_ptr<isl_ctx> &getSharedIslCtx() const { return IslCtx; }
+
+ /// Get the dependences of type @p Kinds.
+ ///
+ /// @param Kinds This integer defines the different kinds of dependences
+ /// that will be returned. To return more than one kind, the
+ /// different kinds are 'ored' together.
+ isl::union_map getDependences(int Kinds) const;
+
+ /// Report if valid dependences are available.
+ bool hasValidDependences() const;
+
+ /// Return the reduction dependences caused by @p MA.
+ ///
+ /// @return The reduction dependences caused by @p MA or nullptr if none.
+ __isl_give isl_map *getReductionDependences(MemoryAccess *MA) const;
+
+ /// Return all reduction dependences.
+ const ReductionDependencesMapTy &getReductionDependences() const {
+ return ReductionDependences;
+ }
+
+ /// Check if a partial schedule is parallel wrt to @p Deps.
+ ///
+ /// @param Schedule The subset of the schedule space that we want to
+ /// check.
+ /// @param Deps The dependences @p Schedule needs to respect.
+ /// @param MinDistancePtr If not nullptr, the minimal dependence distance will
+ /// be returned at the address of that pointer
+ ///
+ /// @return Returns true, if executing parallel the outermost dimension of
+ /// @p Schedule is valid according to the dependences @p Deps.
+ bool isParallel(__isl_keep isl_union_map *Schedule,
+ __isl_take isl_union_map *Deps,
+ __isl_give isl_pw_aff **MinDistancePtr = nullptr) const;
+
+ /// Check if a new schedule is valid.
+ ///
+ /// @param S The current SCoP.
+ /// @param NewSchedules The new schedules
+ ///
+ /// @return True if the new schedule is valid, false if it reverses
+ /// dependences.
+ bool isValidSchedule(Scop &S, const StatementToIslMapTy &NewSchedules) const;
+
+ /// Return true of the schedule @p NewSched is a schedule for @S that does not
+ /// violate any dependences.
+ bool isValidSchedule(Scop &S, isl::schedule NewSched) const;
+
+ /// Print the stored dependence information.
+ void print(llvm::raw_ostream &OS) const;
+
+ /// Dump the dependence information stored to the dbgs stream.
+ void dump() const;
+
+ /// Return the granularity of this dependence analysis.
+ AnalysisLevel getDependenceLevel() { return Level; }
+
+ /// Allow the DependenceInfo access to private members and methods.
+ ///
+ /// To restrict access to the internal state, only the DependenceInfo class
+ /// is able to call or modify a Dependences struct.
+ friend struct DependenceAnalysis;
+ friend struct DependenceInfoPrinterPass;
+ friend class DependenceInfo;
+ friend class DependenceInfoWrapperPass;
+
+ /// Destructor that will free internal objects.
+ ~Dependences() { releaseMemory(); }
+
+private:
+ /// Create an empty dependences struct.
+ explicit Dependences(const std::shared_ptr<isl_ctx> &IslCtx,
+ AnalysisLevel Level)
+ : RAW(nullptr), WAR(nullptr), WAW(nullptr), RED(nullptr), TC_RED(nullptr),
+ IslCtx(IslCtx), Level(Level) {}
+
+ /// Calculate and add at the privatization dependences.
+ void addPrivatizationDependences();
+
+ /// Calculate the dependences for a certain SCoP @p S.
+ void calculateDependences(Scop &S);
+
+ /// Set the reduction dependences for @p MA to @p Deps.
+ void setReductionDependences(MemoryAccess *MA, __isl_take isl_map *Deps);
+
+ /// Free the objects associated with this Dependences struct.
+ ///
+ /// The Dependences struct will again be "empty" afterwards.
+ void releaseMemory();
+
+ /// The different basic kinds of dependences we calculate.
+ isl_union_map *RAW;
+ isl_union_map *WAR;
+ isl_union_map *WAW;
+
+ /// The special reduction dependences.
+ isl_union_map *RED;
+
+ /// The (reverse) transitive closure of reduction dependences.
+ isl_union_map *TC_RED;
+
+ /// Mapping from memory accesses to their reduction dependences.
+ ReductionDependencesMapTy ReductionDependences;
+
+ /// Isl context from the SCoP.
+ std::shared_ptr<isl_ctx> IslCtx;
+
+ /// Granularity of this dependence analysis.
+ const AnalysisLevel Level;
+};
+
+struct DependenceAnalysis : public AnalysisInfoMixin<DependenceAnalysis> {
+ static AnalysisKey Key;
+ struct Result {
+ Scop &S;
+ std::unique_ptr<Dependences> D[Dependences::NumAnalysisLevels];
+
+ /// Return the dependence information for the current SCoP.
+ ///
+ /// @param Level The granularity of dependence analysis result.
+ ///
+ /// @return The dependence analysis result
+ ///
+ const Dependences &getDependences(Dependences::AnalysisLevel Level);
+
+ /// Recompute dependences from schedule and memory accesses.
+ const Dependences &recomputeDependences(Dependences::AnalysisLevel Level);
+ };
+ Result run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR);
+};
+
+struct DependenceInfoPrinterPass
+ : public PassInfoMixin<DependenceInfoPrinterPass> {
+ DependenceInfoPrinterPass(raw_ostream &OS) : OS(OS) {}
+
+ PreservedAnalyses run(Scop &S, ScopAnalysisManager &,
+ ScopStandardAnalysisResults &, SPMUpdater &);
+
+ raw_ostream &OS;
+};
+
+class DependenceInfo : public ScopPass {
+public:
+ static char ID;
+
+ /// Construct a new DependenceInfo pass.
+ DependenceInfo() : ScopPass(ID) {}
+
+ /// Return the dependence information for the current SCoP.
+ ///
+ /// @param Level The granularity of dependence analysis result.
+ ///
+ /// @return The dependence analysis result
+ ///
+ const Dependences &getDependences(Dependences::AnalysisLevel Level);
+
+ /// Recompute dependences from schedule and memory accesses.
+ const Dependences &recomputeDependences(Dependences::AnalysisLevel Level);
+
+ /// Compute the dependence information for the SCoP @p S.
+ bool runOnScop(Scop &S) override;
+
+ /// Print the dependences for the given SCoP to @p OS.
+ void printScop(raw_ostream &OS, Scop &) const override;
+
+ /// Release the internal memory.
+ void releaseMemory() override {
+ for (auto &d : D)
+ d.reset();
+ }
+
+ /// Register all analyses and transformation required.
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+
+private:
+ Scop *S;
+
+ /// Dependences struct for the current SCoP.
+ std::unique_ptr<Dependences> D[Dependences::NumAnalysisLevels];
+};
+
+/// Construct a new DependenceInfoWrapper pass.
+class DependenceInfoWrapperPass : public FunctionPass {
+public:
+ static char ID;
+
+ /// Construct a new DependenceInfoWrapper pass.
+ DependenceInfoWrapperPass() : FunctionPass(ID) {}
+
+ /// Return the dependence information for the given SCoP.
+ ///
+ /// @param S SCoP object.
+ /// @param Level The granularity of dependence analysis result.
+ ///
+ /// @return The dependence analysis result
+ ///
+ const Dependences &getDependences(Scop *S, Dependences::AnalysisLevel Level);
+
+ /// Recompute dependences from schedule and memory accesses.
+ const Dependences &recomputeDependences(Scop *S,
+ Dependences::AnalysisLevel Level);
+
+ /// Compute the dependence information on-the-fly for the function.
+ bool runOnFunction(Function &F) override;
+
+ /// Print the dependences for the current function to @p OS.
+ void print(raw_ostream &OS, const Module *M = nullptr) const override;
+
+ /// Release the internal memory.
+ void releaseMemory() override { ScopToDepsMap.clear(); }
+
+ /// Register all analyses and transformation required.
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+
+private:
+ using ScopToDepsMapTy = DenseMap<Scop *, std::unique_ptr<Dependences>>;
+
+ /// Scop to Dependence map for the current function.
+ ScopToDepsMapTy ScopToDepsMap;
+};
+} // namespace polly
+
+namespace llvm {
+void initializeDependenceInfoPass(llvm::PassRegistry &);
+void initializeDependenceInfoWrapperPassPass(llvm::PassRegistry &);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/FlattenAlgo.h b/contrib/libs/llvm14/tools/polly/include/polly/FlattenAlgo.h
new file mode 100644
index 00000000000..6fdca8f08eb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/FlattenAlgo.h
@@ -0,0 +1,37 @@
+//===------ FlattenAlgo.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Main algorithm of the FlattenSchedulePass. This is a separate file to avoid
+// the unittest for this requiring linking against LLVM.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_FLATTENALGO_H
+#define POLLY_FLATTENALGO_H
+
+#include "isl/isl-noexceptions.h"
+
+namespace polly {
+/// Recursively flatten a schedule.
+///
+/// Reduce the number of scatter dimensions as much as possible without changing
+/// the relative order of instances in a schedule. Ideally, this results in a
+/// single scatter dimension, but it may not always be possible to combine
+/// dimensions, eg. if a dimension is unbounded. In worst case, the original
+/// schedule is returned.
+///
+/// Schedules with fewer dimensions may be easier to understand for humans, but
+/// it should make no difference to the computer.
+///
+/// @param Schedule The input schedule.
+///
+/// @return The flattened schedule.
+isl::union_map flattenSchedule(isl::union_map Schedule);
+} // namespace polly
+
+#endif /* POLLY_FLATTENALGO_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/FlattenSchedule.h b/contrib/libs/llvm14/tools/polly/include/polly/FlattenSchedule.h
new file mode 100644
index 00000000000..2b9fa4fd485
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/FlattenSchedule.h
@@ -0,0 +1,31 @@
+//===------ FlattenSchedule.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Try to reduce the number of scatter dimension. Useful to make isl_union_map
+// schedules more understandable. This is only intended for debugging and
+// unittests, not for optimizations themselves.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_FLATTENSCHEDULE_H
+#define POLLY_FLATTENSCHEDULE_H
+
+namespace llvm {
+class PassRegistry;
+class Pass;
+} // namespace llvm
+
+namespace polly {
+llvm::Pass *createFlattenSchedulePass();
+} // namespace polly
+
+namespace llvm {
+void initializeFlattenSchedulePass(llvm::PassRegistry &);
+} // namespace llvm
+
+#endif /* POLLY_FLATTENSCHEDULE_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/ForwardOpTree.h b/contrib/libs/llvm14/tools/polly/include/polly/ForwardOpTree.h
new file mode 100644
index 00000000000..72c77c398ad
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/ForwardOpTree.h
@@ -0,0 +1,47 @@
+//===- ForwardOpTree.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Move instructions between statements.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_FORWARDOPTREE_H
+#define POLLY_FORWARDOPTREE_H
+
+#include "polly/ScopPass.h"
+
+namespace llvm {
+class PassRegistry;
+
+void initializeForwardOpTreeWrapperPassPass(PassRegistry &);
+} // namespace llvm
+
+namespace polly {
+llvm::Pass *createForwardOpTreeWrapperPass();
+
+struct ForwardOpTreePass : llvm::PassInfoMixin<ForwardOpTreePass> {
+ ForwardOpTreePass() {}
+
+ llvm::PreservedAnalyses run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U);
+};
+
+struct ForwardOpTreePrinterPass
+ : llvm::PassInfoMixin<ForwardOpTreePrinterPass> {
+ ForwardOpTreePrinterPass(raw_ostream &OS) : OS(OS) {}
+
+ PreservedAnalyses run(Scop &S, ScopAnalysisManager &,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &);
+
+private:
+ llvm::raw_ostream &OS;
+};
+
+} // namespace polly
+
+#endif // POLLY_FORWARDOPTREE_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/JSONExporter.h b/contrib/libs/llvm14/tools/polly/include/polly/JSONExporter.h
new file mode 100644
index 00000000000..92aaaccf3b7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/JSONExporter.h
@@ -0,0 +1,31 @@
+//===- polly/JSONExporter.h - Import/Export to/from jscop files.-*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_JSONEXPORTER_H
+#define POLLY_JSONEXPORTER_H
+
+#include "polly/ScopPass.h"
+#include "llvm/IR/PassManager.h"
+
+namespace polly {
+/// This pass exports a scop to a jscop file. The filename is generated from the
+/// concatenation of the function and scop name.
+struct JSONExportPass : public llvm::PassInfoMixin<JSONExportPass> {
+ llvm::PreservedAnalyses run(Scop &, ScopAnalysisManager &,
+ ScopStandardAnalysisResults &, SPMUpdater &);
+};
+
+/// This pass imports a scop from a jscop file. The filename is deduced from the
+/// concatenation of the function and scop name.
+struct JSONImportPass : public llvm::PassInfoMixin<JSONExportPass> {
+ llvm::PreservedAnalyses run(Scop &, ScopAnalysisManager &,
+ ScopStandardAnalysisResults &, SPMUpdater &);
+};
+} // namespace polly
+
+#endif /* POLLY_JSONEXPORTER_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/LinkAllPasses.h b/contrib/libs/llvm14/tools/polly/include/polly/LinkAllPasses.h
new file mode 100644
index 00000000000..4f57e60d7ca
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/LinkAllPasses.h
@@ -0,0 +1,132 @@
+//===- polly/LinkAllPasses.h ----------- Reference All Passes ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This header file pulls in all transformation and analysis passes for tools
+// like opt and bugpoint that need this functionality.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_LINKALLPASSES_H
+#define POLLY_LINKALLPASSES_H
+
+#include "polly/CodeGen/PPCGCodeGeneration.h"
+#include "polly/Config/config.h"
+#include "polly/Support/DumpFunctionPass.h"
+#include "polly/Support/DumpModulePass.h"
+#include "llvm/ADT/StringRef.h"
+#include <cstdlib>
+
+namespace llvm {
+class Pass;
+class PassRegistry;
+} // namespace llvm
+
+namespace polly {
+llvm::Pass *createCodePreparationPass();
+llvm::Pass *createScopInlinerPass();
+llvm::Pass *createDeadCodeElimWrapperPass();
+llvm::Pass *createDependenceInfoPass();
+llvm::Pass *createDependenceInfoWrapperPassPass();
+llvm::Pass *createDOTOnlyPrinterPass();
+llvm::Pass *createDOTOnlyViewerPass();
+llvm::Pass *createDOTPrinterPass();
+llvm::Pass *createDOTViewerPass();
+llvm::Pass *createJSONExporterPass();
+llvm::Pass *createJSONImporterPass();
+llvm::Pass *createPollyCanonicalizePass();
+llvm::Pass *createPolyhedralInfoPass();
+llvm::Pass *createScopDetectionWrapperPassPass();
+llvm::Pass *createScopInfoRegionPassPass();
+llvm::Pass *createScopInfoWrapperPassPass();
+llvm::Pass *createIslAstInfoWrapperPassPass();
+llvm::Pass *createCodeGenerationPass();
+#ifdef GPU_CODEGEN
+llvm::Pass *createPPCGCodeGenerationPass(GPUArch Arch = GPUArch::NVPTX64,
+ GPURuntime Runtime = GPURuntime::CUDA);
+
+llvm::Pass *
+createManagedMemoryRewritePassPass(GPUArch Arch = GPUArch::NVPTX64,
+ GPURuntime Runtime = GPURuntime::CUDA);
+#endif
+llvm::Pass *createIslScheduleOptimizerWrapperPass();
+llvm::Pass *createFlattenSchedulePass();
+llvm::Pass *createForwardOpTreeWrapperPass();
+llvm::Pass *createDeLICMWrapperPass();
+llvm::Pass *createMaximalStaticExpansionPass();
+llvm::Pass *createSimplifyWrapperPass(int);
+llvm::Pass *createPruneUnprofitableWrapperPass();
+
+extern char &CodePreparationID;
+} // namespace polly
+
+namespace {
+struct PollyForcePassLinking {
+ PollyForcePassLinking() {
+ // We must reference the passes in such a way that compilers will not
+ // delete it all as dead code, even with whole program optimization,
+ // yet is effectively a NO-OP. As the compiler isn't smart enough
+ // to know that getenv() never returns -1, this will do the job.
+ if (std::getenv("bar") != (char *)-1)
+ return;
+
+ polly::createCodePreparationPass();
+ polly::createDeadCodeElimWrapperPass();
+ polly::createDependenceInfoPass();
+ polly::createDOTOnlyPrinterPass();
+ polly::createDOTOnlyViewerPass();
+ polly::createDOTPrinterPass();
+ polly::createDOTViewerPass();
+ polly::createJSONExporterPass();
+ polly::createJSONImporterPass();
+ polly::createScopDetectionWrapperPassPass();
+ polly::createScopInfoRegionPassPass();
+ polly::createPollyCanonicalizePass();
+ polly::createPolyhedralInfoPass();
+ polly::createIslAstInfoWrapperPassPass();
+ polly::createCodeGenerationPass();
+#ifdef GPU_CODEGEN
+ polly::createPPCGCodeGenerationPass();
+ polly::createManagedMemoryRewritePassPass();
+#endif
+ polly::createIslScheduleOptimizerWrapperPass();
+ polly::createMaximalStaticExpansionPass();
+ polly::createFlattenSchedulePass();
+ polly::createForwardOpTreeWrapperPass();
+ polly::createDeLICMWrapperPass();
+ polly::createDumpModuleWrapperPass("", true);
+ polly::createDumpFunctionWrapperPass("");
+ polly::createSimplifyWrapperPass(0);
+ polly::createPruneUnprofitableWrapperPass();
+ }
+} PollyForcePassLinking; // Force link by creating a global definition.
+} // namespace
+
+namespace llvm {
+class PassRegistry;
+void initializeCodePreparationPass(llvm::PassRegistry &);
+void initializeScopInlinerPass(llvm::PassRegistry &);
+void initializeDeadCodeElimWrapperPassPass(llvm::PassRegistry &);
+void initializeJSONExporterPass(llvm::PassRegistry &);
+void initializeJSONImporterPass(llvm::PassRegistry &);
+void initializeIslAstInfoWrapperPassPass(llvm::PassRegistry &);
+void initializeCodeGenerationPass(llvm::PassRegistry &);
+#ifdef GPU_CODEGEN
+void initializePPCGCodeGenerationPass(llvm::PassRegistry &);
+void initializeManagedMemoryRewritePassPass(llvm::PassRegistry &);
+#endif
+void initializeIslScheduleOptimizerWrapperPassPass(llvm::PassRegistry &);
+void initializeMaximalStaticExpanderPass(llvm::PassRegistry &);
+void initializePollyCanonicalizePass(llvm::PassRegistry &);
+void initializeFlattenSchedulePass(llvm::PassRegistry &);
+void initializeForwardOpTreeWrapperPassPass(llvm::PassRegistry &);
+void initializeDeLICMWrapperPassPass(llvm::PassRegistry &);
+void initializeSimplifyWrapperPassPass(llvm::PassRegistry &);
+void initializePruneUnprofitableWrapperPassPass(llvm::PassRegistry &);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/ManualOptimizer.h b/contrib/libs/llvm14/tools/polly/include/polly/ManualOptimizer.h
new file mode 100644
index 00000000000..988926334eb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/ManualOptimizer.h
@@ -0,0 +1,43 @@
+//===------ ManualOptimizer.h ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Handle pragma/metadata-directed transformations.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_MANUALOPTIMIZER_H
+#define POLLY_MANUALOPTIMIZER_H
+
+#include "isl/isl-noexceptions.h"
+
+namespace llvm {
+class OptimizationRemarkEmitter;
+}
+
+namespace polly {
+class Scop;
+struct Dependences;
+
+/// Apply loop-transformation metadata.
+///
+/// The loop metadata are taken from mark-nodes in @sched. These nodes have been
+/// added by ScopBuilder when creating a schedule for a loop with an attach
+/// LoopID.
+///
+/// @param S The SCoP for @p Sched.
+/// @param Sched The input schedule to apply the directives on.
+///
+/// @return The transformed schedule with all mark-nodes with loop
+/// transformations applied. Returns NULL in case of an error or @p
+/// Sched itself if no transformation has been applied.
+isl::schedule applyManualTransformations(Scop *S, isl::schedule Sched,
+ const Dependences &D,
+ llvm::OptimizationRemarkEmitter *ORE);
+} // namespace polly
+
+#endif /* POLLY_MANUALOPTIMIZER_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/MatmulOptimizer.h b/contrib/libs/llvm14/tools/polly/include/polly/MatmulOptimizer.h
new file mode 100644
index 00000000000..e00003d95b2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/MatmulOptimizer.h
@@ -0,0 +1,74 @@
+//===- MatmulOptimizer.h -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_MATMULOPTIMIZER_H
+#define POLLY_MATMULOPTIMIZER_H
+
+#include "isl/isl-noexceptions.h"
+
+namespace llvm {
+class TargetTransformInfo;
+}
+
+namespace polly {
+struct Dependences;
+
+/// Apply the BLIS matmul optimization pattern if possible.
+///
+/// Make the loops containing the matrix multiplication be the innermost
+/// loops and apply the BLIS matmul optimization pattern. BLIS implements
+/// gemm as three nested loops around a macro-kernel, plus two packing
+/// routines. The macro-kernel is implemented in terms of two additional
+/// loops around a micro-kernel. The micro-kernel is a loop around a rank-1
+/// (i.e., outer product) update.
+///
+/// For a detailed description please see [1].
+///
+/// The order of the loops defines the data reused in the BLIS implementation
+/// of gemm ([1]). In particular, elements of the matrix B, the second
+/// operand of matrix multiplication, are reused between iterations of the
+/// innermost loop. To keep the reused data in cache, only elements of matrix
+/// A, the first operand of matrix multiplication, should be evicted during
+/// an iteration of the innermost loop. To provide such a cache replacement
+/// policy, elements of the matrix A can, in particular, be loaded first and,
+/// consequently, be least-recently-used.
+///
+/// In our case matrices are stored in row-major order instead of
+/// column-major order used in the BLIS implementation ([1]). It affects only
+/// on the form of the BLIS micro kernel and the computation of its
+/// parameters. In particular, reused elements of the matrix B are
+/// successively multiplied by specific elements of the matrix A.
+///
+/// Refs.:
+/// [1] - Analytical Modeling is Enough for High Performance BLIS
+/// Tze Meng Low, Francisco D Igual, Tyler M Smith, Enrique S Quintana-Orti
+/// Technical Report, 2014
+/// http://www.cs.utexas.edu/users/flame/pubs/TOMS-BLIS-Analytical.pdf
+///
+/// @see ScheduleTreeOptimizer::createMicroKernel
+/// @see ScheduleTreeOptimizer::createMacroKernel
+/// @see getMicroKernelParams
+/// @see getMacroKernelParams
+///
+/// TODO: Implement the packing transformation.
+///
+/// @param Node The node that contains a band to be optimized. The node
+/// is required to successfully pass
+/// ScheduleTreeOptimizer::isMatrMultPattern.
+/// @param TTI Target Transform Info.
+/// @param D The dependencies.
+///
+/// @returns The transformed schedule or nullptr if the optimization
+/// cannot be applied.
+isl::schedule_node
+tryOptimizeMatMulPattern(isl::schedule_node Node,
+ const llvm::TargetTransformInfo *TTI,
+ const Dependences *D);
+
+} // namespace polly
+#endif // POLLY_MATMULOPTIMIZER_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Options.h b/contrib/libs/llvm14/tools/polly/include/polly/Options.h
new file mode 100644
index 00000000000..91a3a84def2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Options.h
@@ -0,0 +1,19 @@
+//===--------------- polly/Options.h - The Polly option category *- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Introduce an option category for Polly.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_OPTIONS_H
+#define POLLY_OPTIONS_H
+
+#include "llvm/Support/CommandLine.h"
+
+extern llvm::cl::OptionCategory PollyCategory;
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/PolyhedralInfo.h b/contrib/libs/llvm14/tools/polly/include/polly/PolyhedralInfo.h
new file mode 100644
index 00000000000..cec288d7cd0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/PolyhedralInfo.h
@@ -0,0 +1,101 @@
+//===- polly/PolyhedralInfo.h - PolyhedralInfo class definition -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// This file contains the declaration of the PolyhedralInfo class, which will
+/// provide an interface to expose polyhedral analysis information of Polly.
+///
+/// This is work in progress. We will add more API's as and when deemed
+/// required.
+//===----------------------------------------------------------------------===///
+
+#ifndef POLLY_POLYHEDRAL_INFO_H
+#define POLLY_POLYHEDRAL_INFO_H
+
+#include "llvm/Pass.h"
+#include "isl/aff_type.h"
+#include "isl/ctx.h"
+#include "isl/union_map_type.h"
+
+namespace llvm {
+class Loop;
+} // namespace llvm
+
+namespace polly {
+
+class Scop;
+class ScopInfo;
+class DependenceInfoWrapperPass;
+
+class PolyhedralInfo : public llvm::FunctionPass {
+public:
+ static char ID; // Pass identification, replacement for typeid
+
+ /// Construct a new PolyhedralInfo pass.
+ PolyhedralInfo() : FunctionPass(ID) {}
+ ~PolyhedralInfo() {}
+
+ /// Check if a given loop is parallel.
+ ///
+ /// @param L The loop.
+ ///
+ /// @return Returns true, if loop is parallel false otherwise.
+ bool isParallel(llvm::Loop *L) const;
+
+ /// Return the SCoP containing the @p L loop.
+ ///
+ /// @param L The loop.
+ ///
+ /// @return Returns the SCoP containing the given loop.
+ /// Returns null if the loop is not contained in any SCoP.
+ const Scop *getScopContainingLoop(llvm::Loop *L) const;
+
+ /// Computes the partial schedule for the given @p L loop.
+ ///
+ /// @param S The SCoP containing the given loop
+ /// @param L The loop.
+ ///
+ /// @return Returns the partial schedule for the given loop
+ __isl_give isl_union_map *getScheduleForLoop(const Scop *S,
+ llvm::Loop *L) const;
+
+ /// Get the SCoP and dependence analysis information for @p F.
+ bool runOnFunction(llvm::Function &F) override;
+
+ /// Release the internal memory.
+ void releaseMemory() override {}
+
+ /// Print to @p OS if each dimension of a loop nest is parallel or not.
+ void print(llvm::raw_ostream &OS,
+ const llvm::Module *M = nullptr) const override;
+
+ /// Register all analyses and transformation required.
+ void getAnalysisUsage(llvm::AnalysisUsage &AU) const override;
+
+private:
+ /// Check if a given loop is parallel or vectorizable.
+ ///
+ /// @param L The loop.
+ /// @param MinDepDistPtr If not nullptr, the minimal dependence distance will
+ /// be returned at the address of that pointer
+ ///
+ /// @return Returns true if loop is parallel or vectorizable, false
+ /// otherwise.
+ bool checkParallel(llvm::Loop *L,
+ __isl_give isl_pw_aff **MinDepDistPtr = nullptr) const;
+
+ ScopInfo *SI;
+ DependenceInfoWrapperPass *DI;
+};
+} // end namespace polly
+
+namespace llvm {
+class PassRegistry;
+void initializePolyhedralInfoPass(llvm::PassRegistry &);
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/PruneUnprofitable.h b/contrib/libs/llvm14/tools/polly/include/polly/PruneUnprofitable.h
new file mode 100644
index 00000000000..6335708fae8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/PruneUnprofitable.h
@@ -0,0 +1,38 @@
+//===- PruneUnprofitable.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Mark a SCoP as unfeasible if not deemed profitable to optimize.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_PRUNEUNPROFITABLE_H
+#define POLLY_PRUNEUNPROFITABLE_H
+
+#include "polly/ScopPass.h"
+
+namespace llvm {
+class Pass;
+class PassRegistry;
+} // namespace llvm
+
+namespace polly {
+llvm::Pass *createPruneUnprofitableWrapperPass();
+
+struct PruneUnprofitablePass : llvm::PassInfoMixin<PruneUnprofitablePass> {
+ PruneUnprofitablePass() {}
+
+ llvm::PreservedAnalyses run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U);
+};
+} // namespace polly
+
+namespace llvm {
+void initializePruneUnprofitableWrapperPassPass(PassRegistry &);
+}
+
+#endif // POLLY_PRUNEUNPROFITABLE_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/RegisterPasses.h b/contrib/libs/llvm14/tools/polly/include/polly/RegisterPasses.h
new file mode 100644
index 00000000000..3a81e1ba748
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/RegisterPasses.h
@@ -0,0 +1,32 @@
+//===------ polly/RegisterPasses.h - Register the Polly passes *- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Functions to register the Polly passes in a LLVM pass manager.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_REGISTER_PASSES_H
+#define POLLY_REGISTER_PASSES_H
+
+namespace llvm {
+class PassRegistry;
+class PassBuilder;
+struct PassPluginLibraryInfo;
+namespace legacy {
+class PassManagerBase;
+} // namespace legacy
+} // namespace llvm
+
+namespace polly {
+void initializePollyPasses(llvm::PassRegistry &Registry);
+void registerPollyPasses(llvm::PassBuilder &PB);
+} // namespace polly
+
+llvm::PassPluginLibraryInfo getPollyPluginInfo();
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/ScheduleOptimizer.h b/contrib/libs/llvm14/tools/polly/include/polly/ScheduleOptimizer.h
new file mode 100644
index 00000000000..8c326f6ac34
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/ScheduleOptimizer.h
@@ -0,0 +1,46 @@
+//===- polly/ScheduleOptimizer.h - The Schedule Optimizer -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SCHEDULEOPTIMIZER_H
+#define POLLY_SCHEDULEOPTIMIZER_H
+
+#include "polly/ScopPass.h"
+
+namespace llvm {
+class Pass;
+class PassRegistry;
+} // namespace llvm
+
+namespace polly {
+llvm::Pass *createIslScheduleOptimizerWrapperPass();
+
+struct IslScheduleOptimizerPass
+ : llvm::PassInfoMixin<IslScheduleOptimizerPass> {
+ IslScheduleOptimizerPass() {}
+
+ llvm::PreservedAnalyses run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U);
+};
+
+struct IslScheduleOptimizerPrinterPass
+ : llvm::PassInfoMixin<IslScheduleOptimizerPrinterPass> {
+ IslScheduleOptimizerPrinterPass(raw_ostream &OS) : OS(OS) {}
+
+ PreservedAnalyses run(Scop &S, ScopAnalysisManager &,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &);
+
+private:
+ llvm::raw_ostream &OS;
+};
+} // namespace polly
+
+namespace llvm {
+void initializeIslScheduleOptimizerWrapperPassPass(llvm::PassRegistry &);
+}
+
+#endif // POLLY_SCHEDULEOPTIMIZER_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/ScheduleTreeTransform.h b/contrib/libs/llvm14/tools/polly/include/polly/ScheduleTreeTransform.h
new file mode 100644
index 00000000000..b35da47f59c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/ScheduleTreeTransform.h
@@ -0,0 +1,286 @@
+//===- polly/ScheduleTreeTransform.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Make changes to isl's schedule tree data structure.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SCHEDULETREETRANSFORM_H
+#define POLLY_SCHEDULETREETRANSFORM_H
+
+#include "polly/Support/ISLTools.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "isl/isl-noexceptions.h"
+#include <cassert>
+
+namespace polly {
+struct BandAttr;
+
+/// This class defines a simple visitor class that may be used for
+/// various schedule tree analysis purposes.
+template <typename Derived, typename RetTy = void, typename... Args>
+struct ScheduleTreeVisitor {
+ Derived &getDerived() { return *static_cast<Derived *>(this); }
+ const Derived &getDerived() const {
+ return *static_cast<const Derived *>(this);
+ }
+
+ RetTy visit(isl::schedule_node Node, Args... args) {
+ assert(!Node.is_null());
+ switch (isl_schedule_node_get_type(Node.get())) {
+ case isl_schedule_node_domain:
+ assert(isl_schedule_node_n_children(Node.get()) == 1);
+ return getDerived().visitDomain(Node.as<isl::schedule_node_domain>(),
+ std::forward<Args>(args)...);
+ case isl_schedule_node_band:
+ assert(isl_schedule_node_n_children(Node.get()) == 1);
+ return getDerived().visitBand(Node.as<isl::schedule_node_band>(),
+ std::forward<Args>(args)...);
+ case isl_schedule_node_sequence:
+ assert(isl_schedule_node_n_children(Node.get()) >= 2);
+ return getDerived().visitSequence(Node.as<isl::schedule_node_sequence>(),
+ std::forward<Args>(args)...);
+ case isl_schedule_node_set:
+ return getDerived().visitSet(Node.as<isl::schedule_node_set>(),
+ std::forward<Args>(args)...);
+ assert(isl_schedule_node_n_children(Node.get()) >= 2);
+ case isl_schedule_node_leaf:
+ assert(isl_schedule_node_n_children(Node.get()) == 0);
+ return getDerived().visitLeaf(Node.as<isl::schedule_node_leaf>(),
+ std::forward<Args>(args)...);
+ case isl_schedule_node_mark:
+ assert(isl_schedule_node_n_children(Node.get()) == 1);
+ return getDerived().visitMark(Node.as<isl::schedule_node_mark>(),
+ std::forward<Args>(args)...);
+ case isl_schedule_node_extension:
+ assert(isl_schedule_node_n_children(Node.get()) == 1);
+ return getDerived().visitExtension(
+ Node.as<isl::schedule_node_extension>(), std::forward<Args>(args)...);
+ case isl_schedule_node_filter:
+ assert(isl_schedule_node_n_children(Node.get()) == 1);
+ return getDerived().visitFilter(Node.as<isl::schedule_node_filter>(),
+ std::forward<Args>(args)...);
+ default:
+ llvm_unreachable("unimplemented schedule node type");
+ }
+ }
+
+ RetTy visitDomain(isl::schedule_node_domain Domain, Args... args) {
+ return getDerived().visitSingleChild(std::move(Domain),
+ std::forward<Args>(args)...);
+ }
+
+ RetTy visitBand(isl::schedule_node_band Band, Args... args) {
+ return getDerived().visitSingleChild(std::move(Band),
+ std::forward<Args>(args)...);
+ }
+
+ RetTy visitSequence(isl::schedule_node_sequence Sequence, Args... args) {
+ return getDerived().visitMultiChild(std::move(Sequence),
+ std::forward<Args>(args)...);
+ }
+
+ RetTy visitSet(isl::schedule_node_set Set, Args... args) {
+ return getDerived().visitMultiChild(std::move(Set),
+ std::forward<Args>(args)...);
+ }
+
+ RetTy visitLeaf(isl::schedule_node_leaf Leaf, Args... args) {
+ return getDerived().visitNode(std::move(Leaf), std::forward<Args>(args)...);
+ }
+
+ RetTy visitMark(isl::schedule_node_mark Mark, Args... args) {
+ return getDerived().visitSingleChild(std::move(Mark),
+ std::forward<Args>(args)...);
+ }
+
+ RetTy visitExtension(isl::schedule_node_extension Extension, Args... args) {
+ return getDerived().visitSingleChild(std::move(Extension),
+ std::forward<Args>(args)...);
+ }
+
+ RetTy visitFilter(isl::schedule_node_filter Filter, Args... args) {
+ return getDerived().visitSingleChild(std::move(Filter),
+ std::forward<Args>(args)...);
+ }
+
+ RetTy visitSingleChild(isl::schedule_node Node, Args... args) {
+ return getDerived().visitNode(std::move(Node), std::forward<Args>(args)...);
+ }
+
+ RetTy visitMultiChild(isl::schedule_node Node, Args... args) {
+ return getDerived().visitNode(std::move(Node), std::forward<Args>(args)...);
+ }
+
+ RetTy visitNode(isl::schedule_node Node, Args... args) {
+ llvm_unreachable("Unimplemented other");
+ }
+};
+
+/// Recursively visit all nodes of a schedule tree.
+template <typename Derived, typename RetTy = void, typename... Args>
+struct RecursiveScheduleTreeVisitor
+ : public ScheduleTreeVisitor<Derived, RetTy, Args...> {
+ using BaseTy = ScheduleTreeVisitor<Derived, RetTy, Args...>;
+ BaseTy &getBase() { return *this; }
+ const BaseTy &getBase() const { return *this; }
+ Derived &getDerived() { return *static_cast<Derived *>(this); }
+ const Derived &getDerived() const {
+ return *static_cast<const Derived *>(this);
+ }
+
+ /// When visiting an entire schedule tree, start at its root node.
+ RetTy visit(isl::schedule Schedule, Args... args) {
+ return getDerived().visit(Schedule.get_root(), std::forward<Args>(args)...);
+ }
+
+ // Necessary to allow overload resolution with the added visit(isl::schedule)
+ // overload.
+ RetTy visit(isl::schedule_node Node, Args... args) {
+ return getBase().visit(Node, std::forward<Args>(args)...);
+ }
+
+ /// By default, recursively visit the child nodes.
+ RetTy visitNode(isl::schedule_node Node, Args... args) {
+ for (unsigned i : rangeIslSize(0, Node.n_children()))
+ getDerived().visit(Node.child(i), std::forward<Args>(args)...);
+ return RetTy();
+ }
+};
+
+/// Recursively visit all nodes of a schedule tree while allowing changes.
+///
+/// The visit methods return an isl::schedule_node that is used to continue
+/// visiting the tree. Structural changes such as returning a different node
+/// will confuse the visitor.
+template <typename Derived, typename... Args>
+struct ScheduleNodeRewriter
+ : public RecursiveScheduleTreeVisitor<Derived, isl::schedule_node,
+ Args...> {
+ Derived &getDerived() { return *static_cast<Derived *>(this); }
+ const Derived &getDerived() const {
+ return *static_cast<const Derived *>(this);
+ }
+
+ isl::schedule_node visitNode(isl::schedule_node Node, Args... args) {
+ return getDerived().visitChildren(Node);
+ }
+
+ isl::schedule_node visitChildren(isl::schedule_node Node, Args... args) {
+ if (!Node.has_children())
+ return Node;
+
+ isl::schedule_node It = Node.first_child();
+ while (true) {
+ It = getDerived().visit(It, std::forward<Args>(args)...);
+ if (!It.has_next_sibling())
+ break;
+ It = It.next_sibling();
+ }
+ return It.parent();
+ }
+};
+
+/// Is this node the marker for its parent band?
+bool isBandMark(const isl::schedule_node &Node);
+
+/// Extract the BandAttr from a band's wrapping marker. Can also pass the band
+/// itself and this methods will try to find its wrapping mark. Returns nullptr
+/// if the band has not BandAttr.
+BandAttr *getBandAttr(isl::schedule_node MarkOrBand);
+
+/// Hoist all domains from extension into the root domain node, such that there
+/// are no more extension nodes (which isl does not support for some
+/// operations). This assumes that domains added by to extension nodes do not
+/// overlap.
+isl::schedule hoistExtensionNodes(isl::schedule Sched);
+
+/// Replace the AST band @p BandToUnroll by a sequence of all its iterations.
+///
+/// The implementation enumerates all points in the partial schedule and creates
+/// an ISL sequence node for each point. The number of iterations must be a
+/// constant.
+isl::schedule applyFullUnroll(isl::schedule_node BandToUnroll);
+
+/// Replace the AST band @p BandToUnroll by a partially unrolled equivalent.
+isl::schedule applyPartialUnroll(isl::schedule_node BandToUnroll, int Factor);
+
+/// Loop-distribute the band @p BandToFission as much as possible.
+isl::schedule applyMaxFission(isl::schedule_node BandToFission);
+
+/// Build the desired set of partial tile prefixes.
+///
+/// We build a set of partial tile prefixes, which are prefixes of the vector
+/// loop that have exactly VectorWidth iterations.
+///
+/// 1. Drop all constraints involving the dimension that represents the
+/// vector loop.
+/// 2. Constrain the last dimension to get a set, which has exactly VectorWidth
+/// iterations.
+/// 3. Subtract loop domain from it, project out the vector loop dimension and
+/// get a set that contains prefixes, which do not have exactly VectorWidth
+/// iterations.
+/// 4. Project out the vector loop dimension of the set that was build on the
+/// first step and subtract the set built on the previous step to get the
+/// desired set of prefixes.
+///
+/// @param ScheduleRange A range of a map, which describes a prefix schedule
+/// relation.
+isl::set getPartialTilePrefixes(isl::set ScheduleRange, int VectorWidth);
+
+/// Create an isl::union_set, which describes the isolate option based on
+/// IsolateDomain.
+///
+/// @param IsolateDomain An isl::set whose @p OutDimsNum last dimensions should
+/// belong to the current band node.
+/// @param OutDimsNum A number of dimensions that should belong to
+/// the current band node.
+isl::union_set getIsolateOptions(isl::set IsolateDomain, unsigned OutDimsNum);
+
+/// Create an isl::union_set, which describes the specified option for the
+/// dimension of the current node.
+///
+/// @param Ctx An isl::ctx, which is used to create the isl::union_set.
+/// @param Option The name of the option.
+isl::union_set getDimOptions(isl::ctx Ctx, const char *Option);
+
+/// Tile a schedule node.
+///
+/// @param Node The node to tile.
+/// @param Identifier An name that identifies this kind of tiling and
+/// that is used to mark the tiled loops in the
+/// generated AST.
+/// @param TileSizes A vector of tile sizes that should be used for
+/// tiling.
+/// @param DefaultTileSize A default tile size that is used for dimensions
+/// that are not covered by the TileSizes vector.
+isl::schedule_node tileNode(isl::schedule_node Node, const char *Identifier,
+ llvm::ArrayRef<int> TileSizes, int DefaultTileSize);
+
+/// Tile a schedule node and unroll point loops.
+///
+/// @param Node The node to register tile.
+/// @param TileSizes A vector of tile sizes that should be used for
+/// tiling.
+/// @param DefaultTileSize A default tile size that is used for dimensions
+isl::schedule_node applyRegisterTiling(isl::schedule_node Node,
+ llvm::ArrayRef<int> TileSizes,
+ int DefaultTileSize);
+
+/// Apply greedy fusion. That is, fuse any loop that is possible to be fused
+/// top-down.
+///
+/// @param Sched Sched tree to fuse all the loops in.
+/// @param Deps Validity constraints that must be preserved.
+isl::schedule applyGreedyFusion(isl::schedule Sched,
+ const isl::union_map &Deps);
+
+} // namespace polly
+
+#endif // POLLY_SCHEDULETREETRANSFORM_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/ScopBuilder.h b/contrib/libs/llvm14/tools/polly/include/polly/ScopBuilder.h
new file mode 100644
index 00000000000..dbfc3dcff12
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/ScopBuilder.h
@@ -0,0 +1,782 @@
+//===- polly/ScopBuilder.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Create a polyhedral description for a static control flow region.
+//
+// The pass creates a polyhedral description of the Scops detected by the SCoP
+// detection derived from their LLVM-IR code.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SCOPBUILDER_H
+#define POLLY_SCOPBUILDER_H
+
+#include "polly/ScopInfo.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SetVector.h"
+
+namespace polly {
+using llvm::SmallSetVector;
+
+class ScopDetection;
+
+/// Command line switch whether to model read-only accesses.
+extern bool ModelReadOnlyScalars;
+
+/// Build the Polly IR (Scop and ScopStmt) on a Region.
+class ScopBuilder {
+
+ /// The AAResults to build AliasSetTracker.
+ AAResults &AA;
+
+ /// Target data for element size computing.
+ const DataLayout &DL;
+
+ /// DominatorTree to reason about guaranteed execution.
+ DominatorTree &DT;
+
+ /// LoopInfo for information about loops.
+ LoopInfo &LI;
+
+ /// Valid Regions for Scop
+ ScopDetection &SD;
+
+ /// The ScalarEvolution to help building Scop.
+ ScalarEvolution &SE;
+
+ /// An optimization diagnostic interface to add optimization remarks.
+ OptimizationRemarkEmitter &ORE;
+
+ /// Set of instructions that might read any memory location.
+ SmallVector<std::pair<ScopStmt *, Instruction *>, 16> GlobalReads;
+
+ /// Set of all accessed array base pointers.
+ SmallSetVector<Value *, 16> ArrayBasePointers;
+
+ // The Scop
+ std::unique_ptr<Scop> scop;
+
+ /// Collection to hold taken assumptions.
+ ///
+ /// There are two reasons why we want to record assumptions first before we
+ /// add them to the assumed/invalid context:
+ /// 1) If the SCoP is not profitable or otherwise invalid without the
+ /// assumed/invalid context we do not have to compute it.
+ /// 2) Information about the context are gathered rather late in the SCoP
+ /// construction (basically after we know all parameters), thus the user
+ /// might see overly complicated assumptions to be taken while they will
+ /// only be simplified later on.
+ RecordedAssumptionsTy RecordedAssumptions;
+
+ // Build the SCoP for Region @p R.
+ void buildScop(Region &R, AssumptionCache &AC);
+
+ /// Adjust the dimensions of @p Dom that was constructed for @p OldL
+ /// to be compatible to domains constructed for loop @p NewL.
+ ///
+ /// This function assumes @p NewL and @p OldL are equal or there is a CFG
+ /// edge from @p OldL to @p NewL.
+ isl::set adjustDomainDimensions(isl::set Dom, Loop *OldL, Loop *NewL);
+
+ /// Compute the domain for each basic block in @p R.
+ ///
+ /// @param R The region we currently traverse.
+ /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current
+ /// region.
+ ///
+ /// @returns True if there was no problem and false otherwise.
+ bool buildDomains(Region *R,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap);
+
+ /// Compute the branching constraints for each basic block in @p R.
+ ///
+ /// @param R The region we currently build branching conditions
+ /// for.
+ /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current
+ /// region.
+ ///
+ /// @returns True if there was no problem and false otherwise.
+ bool buildDomainsWithBranchConstraints(
+ Region *R, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap);
+
+ /// Build the conditions sets for the terminator @p TI in the @p Domain.
+ ///
+ /// This will fill @p ConditionSets with the conditions under which control
+ /// will be moved from @p TI to its successors. Hence, @p ConditionSets will
+ /// have as many elements as @p TI has successors.
+ bool buildConditionSets(BasicBlock *BB, Instruction *TI, Loop *L,
+ __isl_keep isl_set *Domain,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ SmallVectorImpl<__isl_give isl_set *> &ConditionSets);
+
+ /// Build the conditions sets for the branch condition @p Condition in
+ /// the @p Domain.
+ ///
+ /// This will fill @p ConditionSets with the conditions under which control
+ /// will be moved from @p TI to its successors. Hence, @p ConditionSets will
+ /// have as many elements as @p TI has successors. If @p TI is nullptr the
+ /// context under which @p Condition is true/false will be returned as the
+ /// new elements of @p ConditionSets.
+ bool buildConditionSets(BasicBlock *BB, Value *Condition, Instruction *TI,
+ Loop *L, __isl_keep isl_set *Domain,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ SmallVectorImpl<__isl_give isl_set *> &ConditionSets);
+
+ /// Build the conditions sets for the switch @p SI in the @p Domain.
+ ///
+ /// This will fill @p ConditionSets with the conditions under which control
+ /// will be moved from @p SI to its successors. Hence, @p ConditionSets will
+ /// have as many elements as @p SI has successors.
+ bool buildConditionSets(BasicBlock *BB, SwitchInst *SI, Loop *L,
+ __isl_keep isl_set *Domain,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ SmallVectorImpl<__isl_give isl_set *> &ConditionSets);
+
+ /// Build condition sets for unsigned ICmpInst(s).
+ /// Special handling is required for unsigned operands to ensure that if
+ /// MSB (aka the Sign bit) is set for an operands in an unsigned ICmpInst
+ /// it should wrap around.
+ ///
+ /// @param IsStrictUpperBound holds information on the predicate relation
+ /// between TestVal and UpperBound, i.e,
+ /// TestVal < UpperBound OR TestVal <= UpperBound
+ __isl_give isl_set *buildUnsignedConditionSets(
+ BasicBlock *BB, Value *Condition, __isl_keep isl_set *Domain,
+ const SCEV *SCEV_TestVal, const SCEV *SCEV_UpperBound,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ bool IsStrictUpperBound);
+
+ /// Propagate the domain constraints through the region @p R.
+ ///
+ /// @param R The region we currently build branching
+ /// conditions for.
+ /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current
+ /// region.
+ ///
+ /// @returns True if there was no problem and false otherwise.
+ bool propagateDomainConstraints(
+ Region *R, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap);
+
+ /// Propagate domains that are known due to graph properties.
+ ///
+ /// As a CFG is mostly structured we use the graph properties to propagate
+ /// domains without the need to compute all path conditions. In particular,
+ /// if a block A dominates a block B and B post-dominates A we know that the
+ /// domain of B is a superset of the domain of A. As we do not have
+ /// post-dominator information available here we use the less precise region
+ /// information. Given a region R, we know that the exit is always executed
+ /// if the entry was executed, thus the domain of the exit is a superset of
+ /// the domain of the entry. In case the exit can only be reached from
+ /// within the region the domains are in fact equal. This function will use
+ /// this property to avoid the generation of condition constraints that
+ /// determine when a branch is taken. If @p BB is a region entry block we
+ /// will propagate its domain to the region exit block. Additionally, we put
+ /// the region exit block in the @p FinishedExitBlocks set so we can later
+ /// skip edges from within the region to that block.
+ ///
+ /// @param BB The block for which the domain is currently
+ /// propagated.
+ /// @param BBLoop The innermost affine loop surrounding @p BB.
+ /// @param FinishedExitBlocks Set of region exits the domain was set for.
+ /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current
+ /// region.
+ void propagateDomainConstraintsToRegionExit(
+ BasicBlock *BB, Loop *BBLoop,
+ SmallPtrSetImpl<BasicBlock *> &FinishedExitBlocks,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap);
+
+ /// Propagate invalid domains of statements through @p R.
+ ///
+ /// This method will propagate invalid statement domains through @p R and at
+ /// the same time add error block domains to them. Additionally, the domains
+ /// of error statements and those only reachable via error statements will
+ /// be replaced by an empty set. Later those will be removed completely.
+ ///
+ /// @param R The currently traversed region.
+ /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current
+ /// region.
+ //
+ /// @returns True if there was no problem and false otherwise.
+ bool propagateInvalidStmtDomains(
+ Region *R, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap);
+
+ /// Compute the union of predecessor domains for @p BB.
+ ///
+ /// To compute the union of all domains of predecessors of @p BB this
+ /// function applies similar reasoning on the CFG structure as described for
+ /// @see propagateDomainConstraintsToRegionExit
+ ///
+ /// @param BB The block for which the predecessor domains are collected.
+ /// @param Domain The domain under which BB is executed.
+ ///
+ /// @returns The domain under which @p BB is executed.
+ isl::set getPredecessorDomainConstraints(BasicBlock *BB, isl::set Domain);
+
+ /// Add loop carried constraints to the header block of the loop @p L.
+ ///
+ /// @param L The loop to process.
+ /// @param InvalidDomainMap BB to InvalidDomain map for the BB of current
+ /// region.
+ ///
+ /// @returns True if there was no problem and false otherwise.
+ bool addLoopBoundsToHeaderDomain(
+ Loop *L, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap);
+
+ /// Compute the isl representation for the SCEV @p E in this BB.
+ ///
+ /// @param BB The BB for which isl representation is to be
+ /// computed.
+ /// @param InvalidDomainMap A map of BB to their invalid domains.
+ /// @param E The SCEV that should be translated.
+ /// @param NonNegative Flag to indicate the @p E has to be
+ /// non-negative.
+ ///
+ /// Note that this function will also adjust the invalid context
+ /// accordingly.
+ __isl_give isl_pw_aff *
+ getPwAff(BasicBlock *BB, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ const SCEV *E, bool NonNegative = false);
+
+ /// Create equivalence classes for required invariant accesses.
+ ///
+ /// These classes will consolidate multiple required invariant loads from the
+ /// same address in order to keep the number of dimensions in the SCoP
+ /// description small. For each such class equivalence class only one
+ /// representing element, hence one required invariant load, will be chosen
+ /// and modeled as parameter. The method
+ /// Scop::getRepresentingInvariantLoadSCEV() will replace each element from an
+ /// equivalence class with the representing element that is modeled. As a
+ /// consequence Scop::getIdForParam() will only return an id for the
+ /// representing element of each equivalence class, thus for each required
+ /// invariant location.
+ void buildInvariantEquivalenceClasses();
+
+ /// Try to build a multi-dimensional fixed sized MemoryAccess from the
+ /// Load/Store instruction.
+ ///
+ /// @param Inst The Load/Store instruction that access the memory
+ /// @param Stmt The parent statement of the instruction
+ ///
+ /// @returns True if the access could be built, False otherwise.
+ bool buildAccessMultiDimFixed(MemAccInst Inst, ScopStmt *Stmt);
+
+ /// Try to build a multi-dimensional parametric sized MemoryAccess.
+ /// from the Load/Store instruction.
+ ///
+ /// @param Inst The Load/Store instruction that access the memory
+ /// @param Stmt The parent statement of the instruction
+ ///
+ /// @returns True if the access could be built, False otherwise.
+ bool buildAccessMultiDimParam(MemAccInst Inst, ScopStmt *Stmt);
+
+ /// Try to build a MemoryAccess for a memory intrinsic.
+ ///
+ /// @param Inst The instruction that access the memory
+ /// @param Stmt The parent statement of the instruction
+ ///
+ /// @returns True if the access could be built, False otherwise.
+ bool buildAccessMemIntrinsic(MemAccInst Inst, ScopStmt *Stmt);
+
+ /// Try to build a MemoryAccess for a call instruction.
+ ///
+ /// @param Inst The call instruction that access the memory
+ /// @param Stmt The parent statement of the instruction
+ ///
+ /// @returns True if the access could be built, False otherwise.
+ bool buildAccessCallInst(MemAccInst Inst, ScopStmt *Stmt);
+
+ /// Build a single-dimensional parametric sized MemoryAccess
+ /// from the Load/Store instruction.
+ ///
+ /// @param Inst The Load/Store instruction that access the memory
+ /// @param Stmt The parent statement of the instruction
+ void buildAccessSingleDim(MemAccInst Inst, ScopStmt *Stmt);
+
+ /// Finalize all access relations.
+ ///
+ /// When building up access relations, temporary access relations that
+ /// correctly represent each individual access are constructed. However, these
+ /// access relations can be inconsistent or non-optimal when looking at the
+ /// set of accesses as a whole. This function finalizes the memory accesses
+ /// and constructs a globally consistent state.
+ void finalizeAccesses();
+
+ /// Update access dimensionalities.
+ ///
+ /// When detecting memory accesses different accesses to the same array may
+ /// have built with different dimensionality, as outer zero-values dimensions
+ /// may not have been recognized as separate dimensions. This function goes
+ /// again over all memory accesses and updates their dimensionality to match
+ /// the dimensionality of the underlying ScopArrayInfo object.
+ void updateAccessDimensionality();
+
+ /// Fold size constants to the right.
+ ///
+ /// In case all memory accesses in a given dimension are multiplied with a
+ /// common constant, we can remove this constant from the individual access
+ /// functions and move it to the size of the memory access. We do this as this
+ /// increases the size of the innermost dimension, consequently widens the
+ /// valid range the array subscript in this dimension can evaluate to, and
+ /// as a result increases the likelihood that our delinearization is
+ /// correct.
+ ///
+ /// Example:
+ ///
+ /// A[][n]
+ /// S[i,j] -> A[2i][2j+1]
+ /// S[i,j] -> A[2i][2j]
+ ///
+ /// =>
+ ///
+ /// A[][2n]
+ /// S[i,j] -> A[i][2j+1]
+ /// S[i,j] -> A[i][2j]
+ ///
+ /// Constants in outer dimensions can arise when the elements of a parametric
+ /// multi-dimensional array are not elementary data types, but e.g.,
+ /// structures.
+ void foldSizeConstantsToRight();
+
+ /// Fold memory accesses to handle parametric offset.
+ ///
+ /// As a post-processing step, we 'fold' memory accesses to parametric
+ /// offsets in the access functions. @see MemoryAccess::foldAccess for
+ /// details.
+ void foldAccessRelations();
+
+ /// Assume that all memory accesses are within bounds.
+ ///
+ /// After we have built a model of all memory accesses, we need to assume
+ /// that the model we built matches reality -- aka. all modeled memory
+ /// accesses always remain within bounds. We do this as last step, after
+ /// all memory accesses have been modeled and canonicalized.
+ void assumeNoOutOfBounds();
+
+ /// Build the alias checks for this SCoP.
+ bool buildAliasChecks();
+
+ /// A vector of memory accesses that belong to an alias group.
+ using AliasGroupTy = SmallVector<MemoryAccess *, 4>;
+
+ /// A vector of alias groups.
+ using AliasGroupVectorTy = SmallVector<AliasGroupTy, 4>;
+
+ /// Build a given alias group and its access data.
+ ///
+ /// @param AliasGroup The alias group to build.
+ /// @param HasWriteAccess A set of arrays through which memory is not only
+ /// read, but also written.
+ //
+ /// @returns True if __no__ error occurred, false otherwise.
+ bool buildAliasGroup(AliasGroupTy &AliasGroup,
+ DenseSet<const ScopArrayInfo *> HasWriteAccess);
+
+ /// Build all alias groups for this SCoP.
+ ///
+ /// @returns True if __no__ error occurred, false otherwise.
+ bool buildAliasGroups();
+
+ /// Build alias groups for all memory accesses in the Scop.
+ ///
+ /// Using the alias analysis and an alias set tracker we build alias sets
+ /// for all memory accesses inside the Scop. For each alias set we then map
+ /// the aliasing pointers back to the memory accesses we know, thus obtain
+ /// groups of memory accesses which might alias. We also collect the set of
+ /// arrays through which memory is written.
+ ///
+ /// @returns A pair consistent of a vector of alias groups and a set of arrays
+ /// through which memory is written.
+ std::tuple<AliasGroupVectorTy, DenseSet<const ScopArrayInfo *>>
+ buildAliasGroupsForAccesses();
+
+ /// Split alias groups by iteration domains.
+ ///
+ /// We split each group based on the domains of the minimal/maximal accesses.
+ /// That means two minimal/maximal accesses are only in a group if their
+ /// access domains intersect. Otherwise, they are in different groups.
+ ///
+ /// @param AliasGroups The alias groups to split
+ void splitAliasGroupsByDomain(AliasGroupVectorTy &AliasGroups);
+
+ /// Build an instance of MemoryAccess from the Load/Store instruction.
+ ///
+ /// @param Inst The Load/Store instruction that access the memory
+ /// @param Stmt The parent statement of the instruction
+ void buildMemoryAccess(MemAccInst Inst, ScopStmt *Stmt);
+
+ /// Analyze and extract the cross-BB scalar dependences (or, dataflow
+ /// dependencies) of an instruction.
+ ///
+ /// @param UserStmt The statement @p Inst resides in.
+ /// @param Inst The instruction to be analyzed.
+ void buildScalarDependences(ScopStmt *UserStmt, Instruction *Inst);
+
+ /// Build the escaping dependences for @p Inst.
+ ///
+ /// Search for uses of the llvm::Value defined by @p Inst that are not
+ /// within the SCoP. If there is such use, add a SCALAR WRITE such that
+ /// it is available after the SCoP as escaping value.
+ ///
+ /// @param Inst The instruction to be analyzed.
+ void buildEscapingDependences(Instruction *Inst);
+
+ /// Create MemoryAccesses for the given PHI node in the given region.
+ ///
+ /// @param PHIStmt The statement @p PHI resides in.
+ /// @param PHI The PHI node to be handled
+ /// @param NonAffineSubRegion The non affine sub-region @p PHI is in.
+ /// @param IsExitBlock Flag to indicate that @p PHI is in the exit BB.
+ void buildPHIAccesses(ScopStmt *PHIStmt, PHINode *PHI,
+ Region *NonAffineSubRegion, bool IsExitBlock = false);
+
+ /// Build the access functions for the subregion @p SR.
+ void buildAccessFunctions();
+
+ /// Should an instruction be modeled in a ScopStmt.
+ ///
+ /// @param Inst The instruction to check.
+ /// @param L The loop in which context the instruction is looked at.
+ ///
+ /// @returns True if the instruction should be modeled.
+ bool shouldModelInst(Instruction *Inst, Loop *L);
+
+ /// Create one or more ScopStmts for @p BB.
+ ///
+ /// Consecutive instructions are associated to the same statement until a
+ /// separator is found.
+ void buildSequentialBlockStmts(BasicBlock *BB, bool SplitOnStore = false);
+
+ /// Create one or more ScopStmts for @p BB using equivalence classes.
+ ///
+ /// Instructions of a basic block that belong to the same equivalence class
+ /// are added to the same statement.
+ void buildEqivClassBlockStmts(BasicBlock *BB);
+
+ /// Create ScopStmt for all BBs and non-affine subregions of @p SR.
+ ///
+ /// @param SR A subregion of @p R.
+ ///
+ /// Some of the statements might be optimized away later when they do not
+ /// access any memory and thus have no effect.
+ void buildStmts(Region &SR);
+
+ /// Build the access functions for the statement @p Stmt in or represented by
+ /// @p BB.
+ ///
+ /// @param Stmt Statement to add MemoryAccesses to.
+ /// @param BB A basic block in @p R.
+ /// @param NonAffineSubRegion The non affine sub-region @p BB is in.
+ void buildAccessFunctions(ScopStmt *Stmt, BasicBlock &BB,
+ Region *NonAffineSubRegion = nullptr);
+
+ /// Create a new MemoryAccess object and add it to #AccFuncMap.
+ ///
+ /// @param Stmt The statement where the access takes place.
+ /// @param Inst The instruction doing the access. It is not necessarily
+ /// inside @p BB.
+ /// @param AccType The kind of access.
+ /// @param BaseAddress The accessed array's base address.
+ /// @param ElemType The type of the accessed array elements.
+ /// @param Affine Whether all subscripts are affine expressions.
+ /// @param AccessValue Value read or written.
+ /// @param Subscripts Access subscripts per dimension.
+ /// @param Sizes The array dimension's sizes.
+ /// @param Kind The kind of memory accessed.
+ ///
+ /// @return The created MemoryAccess, or nullptr if the access is not within
+ /// the SCoP.
+ MemoryAccess *addMemoryAccess(ScopStmt *Stmt, Instruction *Inst,
+ MemoryAccess::AccessType AccType,
+ Value *BaseAddress, Type *ElemType, bool Affine,
+ Value *AccessValue,
+ ArrayRef<const SCEV *> Subscripts,
+ ArrayRef<const SCEV *> Sizes, MemoryKind Kind);
+
+ /// Create a MemoryAccess that represents either a LoadInst or
+ /// StoreInst.
+ ///
+ /// @param Stmt The statement to add the MemoryAccess to.
+ /// @param MemAccInst The LoadInst or StoreInst.
+ /// @param AccType The kind of access.
+ /// @param BaseAddress The accessed array's base address.
+ /// @param ElemType The type of the accessed array elements.
+ /// @param IsAffine Whether all subscripts are affine expressions.
+ /// @param Subscripts Access subscripts per dimension.
+ /// @param Sizes The array dimension's sizes.
+ /// @param AccessValue Value read or written.
+ ///
+ /// @see MemoryKind
+ void addArrayAccess(ScopStmt *Stmt, MemAccInst MemAccInst,
+ MemoryAccess::AccessType AccType, Value *BaseAddress,
+ Type *ElemType, bool IsAffine,
+ ArrayRef<const SCEV *> Subscripts,
+ ArrayRef<const SCEV *> Sizes, Value *AccessValue);
+
+ /// Create a MemoryAccess for writing an llvm::Instruction.
+ ///
+ /// The access will be created at the position of @p Inst.
+ ///
+ /// @param Inst The instruction to be written.
+ ///
+ /// @see ensureValueRead()
+ /// @see MemoryKind
+ void ensureValueWrite(Instruction *Inst);
+
+ /// Ensure an llvm::Value is available in the BB's statement, creating a
+ /// MemoryAccess for reloading it if necessary.
+ ///
+ /// @param V The value expected to be loaded.
+ /// @param UserStmt Where to reload the value.
+ ///
+ /// @see ensureValueStore()
+ /// @see MemoryKind
+ void ensureValueRead(Value *V, ScopStmt *UserStmt);
+
+ /// Create a write MemoryAccess for the incoming block of a phi node.
+ ///
+ /// Each of the incoming blocks write their incoming value to be picked in the
+ /// phi's block.
+ ///
+ /// @param PHI PHINode under consideration.
+ /// @param IncomingStmt The statement to add the MemoryAccess to.
+ /// @param IncomingBlock Some predecessor block.
+ /// @param IncomingValue @p PHI's value when coming from @p IncomingBlock.
+ /// @param IsExitBlock When true, uses the .s2a alloca instead of the
+ /// .phiops one. Required for values escaping through a
+ /// PHINode in the SCoP region's exit block.
+ /// @see addPHIReadAccess()
+ /// @see MemoryKind
+ void ensurePHIWrite(PHINode *PHI, ScopStmt *IncomintStmt,
+ BasicBlock *IncomingBlock, Value *IncomingValue,
+ bool IsExitBlock);
+
+ /// Add user provided parameter constraints to context (command line).
+ void addUserContext();
+
+ /// Add user provided parameter constraints to context (source code).
+ void addUserAssumptions(AssumptionCache &AC,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap);
+
+ /// Add all recorded assumptions to the assumed context.
+ void addRecordedAssumptions();
+
+ /// Create a MemoryAccess for reading the value of a phi.
+ ///
+ /// The modeling assumes that all incoming blocks write their incoming value
+ /// to the same location. Thus, this access will read the incoming block's
+ /// value as instructed by this @p PHI.
+ ///
+ /// @param PHIStmt Statement @p PHI resides in.
+ /// @param PHI PHINode under consideration; the READ access will be added
+ /// here.
+ ///
+ /// @see ensurePHIWrite()
+ /// @see MemoryKind
+ void addPHIReadAccess(ScopStmt *PHIStmt, PHINode *PHI);
+
+ /// Wrapper function to calculate minimal/maximal accesses to each array.
+ bool calculateMinMaxAccess(AliasGroupTy AliasGroup,
+ Scop::MinMaxVectorTy &MinMaxAccesses);
+ /// Build the domain of @p Stmt.
+ void buildDomain(ScopStmt &Stmt);
+
+ /// Fill NestLoops with loops surrounding @p Stmt.
+ void collectSurroundingLoops(ScopStmt &Stmt);
+
+ /// Check for reductions in @p Stmt.
+ ///
+ /// Iterate over all store memory accesses and check for valid binary
+ /// reduction like chains. For all candidates we check if they have the same
+ /// base address and there are no other accesses which overlap with them. The
+ /// base address check rules out impossible reductions candidates early. The
+ /// overlap check, together with the "only one user" check in
+ /// collectCandidateReductionLoads, guarantees that none of the intermediate
+ /// results will escape during execution of the loop nest. We basically check
+ /// here that no other memory access can access the same memory as the
+ /// potential reduction.
+ void checkForReductions(ScopStmt &Stmt);
+
+ /// Verify that all required invariant loads have been hoisted.
+ ///
+ /// Invariant load hoisting is not guaranteed to hoist all loads that were
+ /// assumed to be scop invariant during scop detection. This function checks
+ /// for cases where the hoisting failed, but where it would have been
+ /// necessary for our scop modeling to be correct. In case of insufficient
+ /// hoisting the scop is marked as invalid.
+ ///
+ /// In the example below Bound[1] is required to be invariant:
+ ///
+ /// for (int i = 1; i < Bound[0]; i++)
+ /// for (int j = 1; j < Bound[1]; j++)
+ /// ...
+ void verifyInvariantLoads();
+
+ /// Hoist invariant memory loads and check for required ones.
+ ///
+ /// We first identify "common" invariant loads, thus loads that are invariant
+ /// and can be hoisted. Then we check if all required invariant loads have
+ /// been identified as (common) invariant. A load is a required invariant load
+ /// if it was assumed to be invariant during SCoP detection, e.g., to assume
+ /// loop bounds to be affine or runtime alias checks to be placeable. In case
+ /// a required invariant load was not identified as (common) invariant we will
+ /// drop this SCoP. An example for both "common" as well as required invariant
+ /// loads is given below:
+ ///
+ /// for (int i = 1; i < *LB[0]; i++)
+ /// for (int j = 1; j < *LB[1]; j++)
+ /// A[i][j] += A[0][0] + (*V);
+ ///
+ /// Common inv. loads: V, A[0][0], LB[0], LB[1]
+ /// Required inv. loads: LB[0], LB[1], (V, if it may alias with A or LB)
+ void hoistInvariantLoads();
+
+ /// Add invariant loads listed in @p InvMAs with the domain of @p Stmt.
+ void addInvariantLoads(ScopStmt &Stmt, InvariantAccessesTy &InvMAs);
+
+ /// Check if @p MA can always be hoisted without execution context.
+ bool canAlwaysBeHoisted(MemoryAccess *MA, bool StmtInvalidCtxIsEmpty,
+ bool MAInvalidCtxIsEmpty,
+ bool NonHoistableCtxIsEmpty);
+
+ /// Return true if and only if @p LI is a required invariant load.
+ bool isRequiredInvariantLoad(LoadInst *LI) const {
+ return scop->getRequiredInvariantLoads().count(LI);
+ }
+
+ /// Check if the base ptr of @p MA is in the SCoP but not hoistable.
+ bool hasNonHoistableBasePtrInScop(MemoryAccess *MA, isl::union_map Writes);
+
+ /// Return the context under which the access cannot be hoisted.
+ ///
+ /// @param Access The access to check.
+ /// @param Writes The set of all memory writes in the scop.
+ ///
+ /// @return Return the context under which the access cannot be hoisted or a
+ /// nullptr if it cannot be hoisted at all.
+ isl::set getNonHoistableCtx(MemoryAccess *Access, isl::union_map Writes);
+
+ /// Collect loads which might form a reduction chain with @p StoreMA.
+ ///
+ /// Check if the stored value for @p StoreMA is a binary operator with one or
+ /// two loads as operands. If the binary operand is commutative & associative,
+ /// used only once (by @p StoreMA) and its load operands are also used only
+ /// once, we have found a possible reduction chain. It starts at an operand
+ /// load and includes the binary operator and @p StoreMA.
+ ///
+ /// Note: We allow only one use to ensure the load and binary operator cannot
+ /// escape this block or into any other store except @p StoreMA.
+ void collectCandidateReductionLoads(MemoryAccess *StoreMA,
+ SmallVectorImpl<MemoryAccess *> &Loads);
+
+ /// Build the access relation of all memory accesses of @p Stmt.
+ void buildAccessRelations(ScopStmt &Stmt);
+
+ /// Canonicalize arrays with base pointers from the same equivalence class.
+ ///
+ /// Some context: in our normal model we assume that each base pointer is
+ /// related to a single specific memory region, where memory regions
+ /// associated with different base pointers are disjoint. Consequently we do
+ /// not need to compute additional data dependences that model possible
+ /// overlaps of these memory regions. To verify our assumption we compute
+ /// alias checks that verify that modeled arrays indeed do not overlap. In
+ /// case an overlap is detected the runtime check fails and we fall back to
+ /// the original code.
+ ///
+ /// In case of arrays where the base pointers are know to be identical,
+ /// because they are dynamically loaded by accesses that are in the same
+ /// invariant load equivalence class, such run-time alias check would always
+ /// be false.
+ ///
+ /// This function makes sure that we do not generate consistently failing
+ /// run-time checks for code that contains distinct arrays with known
+ /// equivalent base pointers. It identifies for each invariant load
+ /// equivalence class a single canonical array and canonicalizes all memory
+ /// accesses that reference arrays that have base pointers that are known to
+ /// be equal to the base pointer of such a canonical array to this canonical
+ /// array.
+ ///
+ /// We currently do not canonicalize arrays for which certain memory accesses
+ /// have been hoisted as loop invariant.
+ void canonicalizeDynamicBasePtrs();
+
+ /// Construct the schedule of this SCoP.
+ void buildSchedule();
+
+ /// A loop stack element to keep track of per-loop information during
+ /// schedule construction.
+ using LoopStackElementTy = struct LoopStackElement {
+ // The loop for which we keep information.
+ Loop *L;
+
+ // The (possibly incomplete) schedule for this loop.
+ isl::schedule Schedule;
+
+ // The number of basic blocks in the current loop, for which a schedule has
+ // already been constructed.
+ unsigned NumBlocksProcessed;
+
+ LoopStackElement(Loop *L, isl::schedule S, unsigned NumBlocksProcessed)
+ : L(L), Schedule(S), NumBlocksProcessed(NumBlocksProcessed) {}
+ };
+
+ /// The loop stack used for schedule construction.
+ ///
+ /// The loop stack keeps track of schedule information for a set of nested
+ /// loops as well as an (optional) 'nullptr' loop that models the outermost
+ /// schedule dimension. The loops in a loop stack always have a parent-child
+ /// relation where the loop at position n is the parent of the loop at
+ /// position n + 1.
+ using LoopStackTy = SmallVector<LoopStackElementTy, 4>;
+
+ /// Construct schedule information for a given Region and add the
+ /// derived information to @p LoopStack.
+ ///
+ /// Given a Region we derive schedule information for all RegionNodes
+ /// contained in this region ensuring that the assigned execution times
+ /// correctly model the existing control flow relations.
+ ///
+ /// @param R The region which to process.
+ /// @param LoopStack A stack of loops that are currently under
+ /// construction.
+ void buildSchedule(Region *R, LoopStackTy &LoopStack);
+
+ /// Build Schedule for the region node @p RN and add the derived
+ /// information to @p LoopStack.
+ ///
+ /// In case @p RN is a BasicBlock or a non-affine Region, we construct the
+ /// schedule for this @p RN and also finalize loop schedules in case the
+ /// current @p RN completes the loop.
+ ///
+ /// In case @p RN is a not-non-affine Region, we delegate the construction to
+ /// buildSchedule(Region *R, ...).
+ ///
+ /// @param RN The RegionNode region traversed.
+ /// @param LoopStack A stack of loops that are currently under
+ /// construction.
+ void buildSchedule(RegionNode *RN, LoopStackTy &LoopStack);
+
+public:
+ explicit ScopBuilder(Region *R, AssumptionCache &AC, AAResults &AA,
+ const DataLayout &DL, DominatorTree &DT, LoopInfo &LI,
+ ScopDetection &SD, ScalarEvolution &SE,
+ OptimizationRemarkEmitter &ORE);
+ ScopBuilder(const ScopBuilder &) = delete;
+ ScopBuilder &operator=(const ScopBuilder &) = delete;
+ ~ScopBuilder() = default;
+
+ /// Try to build the Polly IR of static control part on the current
+ /// SESE-Region.
+ ///
+ /// @return Give up the ownership of the scop object or static control part
+ /// for the region
+ std::unique_ptr<Scop> getScop() { return std::move(scop); }
+};
+} // end namespace polly
+
+#endif // POLLY_SCOPBUILDER_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/ScopDetection.h b/contrib/libs/llvm14/tools/polly/include/polly/ScopDetection.h
new file mode 100644
index 00000000000..e1195c92b96
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/ScopDetection.h
@@ -0,0 +1,681 @@
+//===- ScopDetection.h - Detect Scops ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Detect the maximal Scops of a function.
+//
+// A static control part (Scop) is a subgraph of the control flow graph (CFG)
+// that only has statically known control flow and can therefore be described
+// within the polyhedral model.
+//
+// Every Scop fulfills these restrictions:
+//
+// * It is a single entry single exit region
+//
+// * Only affine linear bounds in the loops
+//
+// Every natural loop in a Scop must have a number of loop iterations that can
+// be described as an affine linear function in surrounding loop iterators or
+// parameters. (A parameter is a scalar that does not change its value during
+// execution of the Scop).
+//
+// * Only comparisons of affine linear expressions in conditions
+//
+// * All loops and conditions perfectly nested
+//
+// The control flow needs to be structured such that it could be written using
+// just 'for' and 'if' statements, without the need for any 'goto', 'break' or
+// 'continue'.
+//
+// * Side effect free functions call
+//
+// Only function calls and intrinsics that do not have side effects are allowed
+// (readnone).
+//
+// The Scop detection finds the largest Scops by checking if the largest
+// region is a Scop. If this is not the case, its canonical subregions are
+// checked until a region is a Scop. It is now tried to extend this Scop by
+// creating a larger non canonical region.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SCOPDETECTION_H
+#define POLLY_SCOPDETECTION_H
+
+#include "polly/ScopDetectionDiagnostic.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/Analysis/AliasSetTracker.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "llvm/Pass.h"
+#include <set>
+
+namespace llvm {
+class AAResults;
+
+void initializeScopDetectionWrapperPassPass(PassRegistry &);
+} // namespace llvm
+
+namespace polly {
+using llvm::AAResults;
+using llvm::AliasSetTracker;
+using llvm::AnalysisInfoMixin;
+using llvm::AnalysisKey;
+using llvm::AnalysisUsage;
+using llvm::BranchInst;
+using llvm::CallInst;
+using llvm::DenseMap;
+using llvm::DominatorTree;
+using llvm::Function;
+using llvm::FunctionAnalysisManager;
+using llvm::FunctionPass;
+using llvm::IntrinsicInst;
+using llvm::LoopInfo;
+using llvm::Module;
+using llvm::OptimizationRemarkEmitter;
+using llvm::PassInfoMixin;
+using llvm::PreservedAnalyses;
+using llvm::RegionInfo;
+using llvm::ScalarEvolution;
+using llvm::SCEVUnknown;
+using llvm::SetVector;
+using llvm::SmallSetVector;
+using llvm::SmallVectorImpl;
+using llvm::StringRef;
+using llvm::SwitchInst;
+
+using ParamSetType = std::set<const SCEV *>;
+
+// Description of the shape of an array.
+struct ArrayShape {
+ // Base pointer identifying all accesses to this array.
+ const SCEVUnknown *BasePointer;
+
+ // Sizes of each delinearized dimension.
+ SmallVector<const SCEV *, 4> DelinearizedSizes;
+
+ ArrayShape(const SCEVUnknown *B) : BasePointer(B) {}
+};
+
+struct MemAcc {
+ const Instruction *Insn;
+
+ // A pointer to the shape description of the array.
+ std::shared_ptr<ArrayShape> Shape;
+
+ // Subscripts computed by delinearization.
+ SmallVector<const SCEV *, 4> DelinearizedSubscripts;
+
+ MemAcc(const Instruction *I, std::shared_ptr<ArrayShape> S)
+ : Insn(I), Shape(S) {}
+};
+
+using MapInsnToMemAcc = std::map<const Instruction *, MemAcc>;
+using PairInstSCEV = std::pair<const Instruction *, const SCEV *>;
+using AFs = std::vector<PairInstSCEV>;
+using BaseToAFs = std::map<const SCEVUnknown *, AFs>;
+using BaseToElSize = std::map<const SCEVUnknown *, const SCEV *>;
+
+extern bool PollyTrackFailures;
+extern bool PollyDelinearize;
+extern bool PollyUseRuntimeAliasChecks;
+extern bool PollyProcessUnprofitable;
+extern bool PollyInvariantLoadHoisting;
+extern bool PollyAllowUnsignedOperations;
+extern bool PollyAllowFullFunction;
+
+/// A function attribute which will cause Polly to skip the function
+extern StringRef PollySkipFnAttr;
+
+//===----------------------------------------------------------------------===//
+/// Pass to detect the maximal static control parts (Scops) of a
+/// function.
+class ScopDetection {
+public:
+ using RegionSet = SetVector<const Region *>;
+
+ // Remember the valid regions
+ RegionSet ValidRegions;
+
+ /// Context variables for SCoP detection.
+ struct DetectionContext {
+ Region &CurRegion; // The region to check.
+ AliasSetTracker AST; // The AliasSetTracker to hold the alias information.
+ bool Verifying; // If we are in the verification phase?
+
+ /// Container to remember rejection reasons for this region.
+ RejectLog Log;
+
+ /// Map a base pointer to all access functions accessing it.
+ ///
+ /// This map is indexed by the base pointer. Each element of the map
+ /// is a list of memory accesses that reference this base pointer.
+ BaseToAFs Accesses;
+
+ /// The set of base pointers with non-affine accesses.
+ ///
+ /// This set contains all base pointers and the locations where they are
+ /// used for memory accesses that can not be detected as affine accesses.
+ llvm::SetVector<std::pair<const SCEVUnknown *, Loop *>> NonAffineAccesses;
+ BaseToElSize ElementSize;
+
+ /// The region has at least one load instruction.
+ bool hasLoads = false;
+
+ /// The region has at least one store instruction.
+ bool hasStores = false;
+
+ /// Flag to indicate the region has at least one unknown access.
+ bool HasUnknownAccess = false;
+
+ /// The set of non-affine subregions in the region we analyze.
+ RegionSet NonAffineSubRegionSet;
+
+ /// The set of loops contained in non-affine regions.
+ BoxedLoopsSetTy BoxedLoopsSet;
+
+ /// Loads that need to be invariant during execution.
+ InvariantLoadsSetTy RequiredILS;
+
+ /// Map to memory access description for the corresponding LLVM
+ /// instructions.
+ MapInsnToMemAcc InsnToMemAcc;
+
+ /// Initialize a DetectionContext from scratch.
+ DetectionContext(Region &R, AAResults &AA, bool Verify)
+ : CurRegion(R), AST(AA), Verifying(Verify), Log(&R) {}
+ };
+
+ /// Helper data structure to collect statistics about loop counts.
+ struct LoopStats {
+ int NumLoops;
+ int MaxDepth;
+ };
+
+ int NextScopID = 0;
+ int getNextID() { return NextScopID++; }
+
+private:
+ //===--------------------------------------------------------------------===//
+
+ /// Analyses used
+ //@{
+ const DominatorTree &DT;
+ ScalarEvolution &SE;
+ LoopInfo &LI;
+ RegionInfo &RI;
+ AAResults &AA;
+ //@}
+
+ /// Map to remember detection contexts for all regions.
+ using DetectionContextMapTy =
+ DenseMap<BBPair, std::unique_ptr<DetectionContext>>;
+ DetectionContextMapTy DetectionContextMap;
+
+ /// Cache for the isErrorBlock function.
+ DenseMap<std::tuple<const BasicBlock *, const Region *>, bool>
+ ErrorBlockCache;
+
+ /// Remove cached results for @p R.
+ void removeCachedResults(const Region &R);
+
+ /// Remove cached results for the children of @p R recursively.
+ void removeCachedResultsRecursively(const Region &R);
+
+ /// Check if @p S0 and @p S1 do contain multiple possibly aliasing pointers.
+ ///
+ /// @param S0 A expression to check.
+ /// @param S1 Another expression to check or nullptr.
+ /// @param Scope The loop/scope the expressions are checked in.
+ ///
+ /// @returns True, if multiple possibly aliasing pointers are used in @p S0
+ /// (and @p S1 if given).
+ bool involvesMultiplePtrs(const SCEV *S0, const SCEV *S1, Loop *Scope) const;
+
+ /// Add the region @p AR as over approximated sub-region in @p Context.
+ ///
+ /// @param AR The non-affine subregion.
+ /// @param Context The current detection context.
+ ///
+ /// @returns True if the subregion can be over approximated, false otherwise.
+ bool addOverApproximatedRegion(Region *AR, DetectionContext &Context) const;
+
+ /// Find for a given base pointer terms that hint towards dimension
+ /// sizes of a multi-dimensional array.
+ ///
+ /// @param Context The current detection context.
+ /// @param BasePointer A base pointer indicating the virtual array we are
+ /// interested in.
+ SmallVector<const SCEV *, 4>
+ getDelinearizationTerms(DetectionContext &Context,
+ const SCEVUnknown *BasePointer) const;
+
+ /// Check if the dimension size of a delinearized array is valid.
+ ///
+ /// @param Context The current detection context.
+ /// @param Sizes The sizes of the different array dimensions.
+ /// @param BasePointer The base pointer we are interested in.
+ /// @param Scope The location where @p BasePointer is being used.
+ /// @returns True if one or more array sizes could be derived - meaning: we
+ /// see this array as multi-dimensional.
+ bool hasValidArraySizes(DetectionContext &Context,
+ SmallVectorImpl<const SCEV *> &Sizes,
+ const SCEVUnknown *BasePointer, Loop *Scope) const;
+
+ /// Derive access functions for a given base pointer.
+ ///
+ /// @param Context The current detection context.
+ /// @param Sizes The sizes of the different array dimensions.
+ /// @param BasePointer The base pointer of all the array for which to compute
+ /// access functions.
+ /// @param Shape The shape that describes the derived array sizes and
+ /// which should be filled with newly computed access
+ /// functions.
+ /// @returns True if a set of affine access functions could be derived.
+ bool computeAccessFunctions(DetectionContext &Context,
+ const SCEVUnknown *BasePointer,
+ std::shared_ptr<ArrayShape> Shape) const;
+
+ /// Check if all accesses to a given BasePointer are affine.
+ ///
+ /// @param Context The current detection context.
+ /// @param BasePointer the base pointer we are interested in.
+ /// @param Scope The location where @p BasePointer is being used.
+ /// @param True if consistent (multi-dimensional) array accesses could be
+ /// derived for this array.
+ bool hasBaseAffineAccesses(DetectionContext &Context,
+ const SCEVUnknown *BasePointer, Loop *Scope) const;
+
+ // Delinearize all non affine memory accesses and return false when there
+ // exists a non affine memory access that cannot be delinearized. Return true
+ // when all array accesses are affine after delinearization.
+ bool hasAffineMemoryAccesses(DetectionContext &Context) const;
+
+ // Try to expand the region R. If R can be expanded return the expanded
+ // region, NULL otherwise.
+ Region *expandRegion(Region &R);
+
+ /// Find the Scops in this region tree.
+ ///
+ /// @param The region tree to scan for scops.
+ void findScops(Region &R);
+
+ /// Check if all basic block in the region are valid.
+ ///
+ /// @param Context The context of scop detection.
+ ///
+ /// @return True if all blocks in R are valid, false otherwise.
+ bool allBlocksValid(DetectionContext &Context);
+
+ /// Check if a region has sufficient compute instructions.
+ ///
+ /// This function checks if a region has a non-trivial number of instructions
+ /// in each loop. This can be used as an indicator whether a loop is worth
+ /// optimizing.
+ ///
+ /// @param Context The context of scop detection.
+ /// @param NumLoops The number of loops in the region.
+ ///
+ /// @return True if region is has sufficient compute instructions,
+ /// false otherwise.
+ bool hasSufficientCompute(DetectionContext &Context,
+ int NumAffineLoops) const;
+
+ /// Check if the unique affine loop might be amendable to distribution.
+ ///
+ /// This function checks if the number of non-trivial blocks in the unique
+ /// affine loop in Context.CurRegion is at least two, thus if the loop might
+ /// be amendable to distribution.
+ ///
+ /// @param Context The context of scop detection.
+ ///
+ /// @return True only if the affine loop might be amendable to distributable.
+ bool hasPossiblyDistributableLoop(DetectionContext &Context) const;
+
+ /// Check if a region is profitable to optimize.
+ ///
+ /// Regions that are unlikely to expose interesting optimization opportunities
+ /// are called 'unprofitable' and may be skipped during scop detection.
+ ///
+ /// @param Context The context of scop detection.
+ ///
+ /// @return True if region is profitable to optimize, false otherwise.
+ bool isProfitableRegion(DetectionContext &Context) const;
+
+ /// Check if a region is a Scop.
+ ///
+ /// @param Context The context of scop detection.
+ ///
+ /// @return True if R is a Scop, false otherwise.
+ bool isValidRegion(DetectionContext &Context);
+
+ /// Check if an intrinsic call can be part of a Scop.
+ ///
+ /// @param II The intrinsic call instruction to check.
+ /// @param Context The current detection context.
+ ///
+ /// @return True if the call instruction is valid, false otherwise.
+ bool isValidIntrinsicInst(IntrinsicInst &II, DetectionContext &Context) const;
+
+ /// Check if a call instruction can be part of a Scop.
+ ///
+ /// @param CI The call instruction to check.
+ /// @param Context The current detection context.
+ ///
+ /// @return True if the call instruction is valid, false otherwise.
+ bool isValidCallInst(CallInst &CI, DetectionContext &Context) const;
+
+ /// Check if the given loads could be invariant and can be hoisted.
+ ///
+ /// If true is returned the loads are added to the required invariant loads
+ /// contained in the @p Context.
+ ///
+ /// @param RequiredILS The loads to check.
+ /// @param Context The current detection context.
+ ///
+ /// @return True if all loads can be assumed invariant.
+ bool onlyValidRequiredInvariantLoads(InvariantLoadsSetTy &RequiredILS,
+ DetectionContext &Context) const;
+
+ /// Check if a value is invariant in the region Reg.
+ ///
+ /// @param Val Value to check for invariance.
+ /// @param Reg The region to consider for the invariance of Val.
+ /// @param Ctx The current detection context.
+ ///
+ /// @return True if the value represented by Val is invariant in the region
+ /// identified by Reg.
+ bool isInvariant(Value &Val, const Region &Reg, DetectionContext &Ctx) const;
+
+ /// Check if the memory access caused by @p Inst is valid.
+ ///
+ /// @param Inst The access instruction.
+ /// @param AF The access function.
+ /// @param BP The access base pointer.
+ /// @param Context The current detection context.
+ bool isValidAccess(Instruction *Inst, const SCEV *AF, const SCEVUnknown *BP,
+ DetectionContext &Context) const;
+
+ /// Check if a memory access can be part of a Scop.
+ ///
+ /// @param Inst The instruction accessing the memory.
+ /// @param Context The context of scop detection.
+ ///
+ /// @return True if the memory access is valid, false otherwise.
+ bool isValidMemoryAccess(MemAccInst Inst, DetectionContext &Context) const;
+
+ /// Check if an instruction has any non trivial scalar dependencies as part of
+ /// a Scop.
+ ///
+ /// @param Inst The instruction to check.
+ /// @param RefRegion The region in respect to which we check the access
+ /// function.
+ ///
+ /// @return True if the instruction has scalar dependences, false otherwise.
+ bool hasScalarDependency(Instruction &Inst, Region &RefRegion) const;
+
+ /// Check if an instruction can be part of a Scop.
+ ///
+ /// @param Inst The instruction to check.
+ /// @param Context The context of scop detection.
+ ///
+ /// @return True if the instruction is valid, false otherwise.
+ bool isValidInstruction(Instruction &Inst, DetectionContext &Context);
+
+ /// Check if the switch @p SI with condition @p Condition is valid.
+ ///
+ /// @param BB The block to check.
+ /// @param SI The switch to check.
+ /// @param Condition The switch condition.
+ /// @param IsLoopBranch Flag to indicate the branch is a loop exit/latch.
+ /// @param Context The context of scop detection.
+ ///
+ /// @return True if the branch @p BI is valid.
+ bool isValidSwitch(BasicBlock &BB, SwitchInst *SI, Value *Condition,
+ bool IsLoopBranch, DetectionContext &Context) const;
+
+ /// Check if the branch @p BI with condition @p Condition is valid.
+ ///
+ /// @param BB The block to check.
+ /// @param BI The branch to check.
+ /// @param Condition The branch condition.
+ /// @param IsLoopBranch Flag to indicate the branch is a loop exit/latch.
+ /// @param Context The context of scop detection.
+ ///
+ /// @return True if the branch @p BI is valid.
+ bool isValidBranch(BasicBlock &BB, BranchInst *BI, Value *Condition,
+ bool IsLoopBranch, DetectionContext &Context);
+
+ /// Check if the SCEV @p S is affine in the current @p Context.
+ ///
+ /// This will also use a heuristic to decide if we want to require loads to be
+ /// invariant to make the expression affine or if we want to treat is as
+ /// non-affine.
+ ///
+ /// @param S The expression to be checked.
+ /// @param Scope The loop nest in which @p S is used.
+ /// @param Context The context of scop detection.
+ bool isAffine(const SCEV *S, Loop *Scope, DetectionContext &Context) const;
+
+ /// Check if the control flow in a basic block is valid.
+ ///
+ /// This function checks if a certain basic block is terminated by a
+ /// Terminator instruction we can handle or, if this is not the case,
+ /// registers this basic block as the start of a non-affine region.
+ ///
+ /// This function optionally allows unreachable statements.
+ ///
+ /// @param BB The BB to check the control flow.
+ /// @param IsLoopBranch Flag to indicate the branch is a loop exit/latch.
+ // @param AllowUnreachable Allow unreachable statements.
+ /// @param Context The context of scop detection.
+ ///
+ /// @return True if the BB contains only valid control flow.
+ bool isValidCFG(BasicBlock &BB, bool IsLoopBranch, bool AllowUnreachable,
+ DetectionContext &Context);
+
+ /// Is a loop valid with respect to a given region.
+ ///
+ /// @param L The loop to check.
+ /// @param Context The context of scop detection.
+ ///
+ /// @return True if the loop is valid in the region.
+ bool isValidLoop(Loop *L, DetectionContext &Context);
+
+ /// Count the number of loops and the maximal loop depth in @p L.
+ ///
+ /// @param L The loop to check.
+ /// @param SE The scalar evolution analysis.
+ /// @param MinProfitableTrips The minimum number of trip counts from which
+ /// a loop is assumed to be profitable and
+ /// consequently is counted.
+ /// returns A tuple of number of loops and their maximal depth.
+ static ScopDetection::LoopStats
+ countBeneficialSubLoops(Loop *L, ScalarEvolution &SE,
+ unsigned MinProfitableTrips);
+
+ /// Check if the function @p F is marked as invalid.
+ ///
+ /// @note An OpenMP subfunction will be marked as invalid.
+ static bool isValidFunction(Function &F);
+
+ /// Can ISL compute the trip count of a loop.
+ ///
+ /// @param L The loop to check.
+ /// @param Context The context of scop detection.
+ ///
+ /// @return True if ISL can compute the trip count of the loop.
+ bool canUseISLTripCount(Loop *L, DetectionContext &Context);
+
+ /// Print the locations of all detected scops.
+ void printLocations(Function &F);
+
+ /// Check if a region is reducible or not.
+ ///
+ /// @param Region The region to check.
+ /// @param DbgLoc Parameter to save the location of instruction that
+ /// causes irregular control flow if the region is irreducible.
+ ///
+ /// @return True if R is reducible, false otherwise.
+ bool isReducibleRegion(Region &R, DebugLoc &DbgLoc) const;
+
+ /// Track diagnostics for invalid scops.
+ ///
+ /// @param Context The context of scop detection.
+ /// @param Assert Throw an assert in verify mode or not.
+ /// @param Args Argument list that gets passed to the constructor of RR.
+ template <class RR, typename... Args>
+ inline bool invalid(DetectionContext &Context, bool Assert,
+ Args &&...Arguments) const;
+
+public:
+ ScopDetection(const DominatorTree &DT, ScalarEvolution &SE, LoopInfo &LI,
+ RegionInfo &RI, AAResults &AA, OptimizationRemarkEmitter &ORE);
+
+ void detect(Function &F);
+
+ /// Get the RegionInfo stored in this pass.
+ ///
+ /// This was added to give the DOT printer easy access to this information.
+ RegionInfo *getRI() const { return &RI; }
+
+ /// Get the LoopInfo stored in this pass.
+ LoopInfo *getLI() const { return &LI; }
+
+ /// Is the region is the maximum region of a Scop?
+ ///
+ /// @param R The Region to test if it is maximum.
+ /// @param Verify Rerun the scop detection to verify SCoP was not invalidated
+ /// meanwhile. Do not use if the region's DetectionContect is
+ /// referenced by a Scop that is still to be processed.
+ ///
+ /// @return Return true if R is the maximum Region in a Scop, false otherwise.
+ bool isMaxRegionInScop(const Region &R, bool Verify = true);
+
+ /// Return the detection context for @p R, nullptr if @p R was invalid.
+ DetectionContext *getDetectionContext(const Region *R) const;
+
+ /// Return the set of rejection causes for @p R.
+ const RejectLog *lookupRejectionLog(const Region *R) const;
+
+ /// Return true if @p SubR is a non-affine subregion in @p ScopR.
+ bool isNonAffineSubRegion(const Region *SubR, const Region *ScopR) const;
+
+ /// Get a message why a region is invalid
+ ///
+ /// @param R The region for which we get the error message
+ ///
+ /// @return The error or "" if no error appeared.
+ std::string regionIsInvalidBecause(const Region *R) const;
+
+ /// @name Maximum Region In Scops Iterators
+ ///
+ /// These iterators iterator over all maximum region in Scops of this
+ /// function.
+ //@{
+ using iterator = RegionSet::iterator;
+ using const_iterator = RegionSet::const_iterator;
+
+ iterator begin() { return ValidRegions.begin(); }
+ iterator end() { return ValidRegions.end(); }
+
+ const_iterator begin() const { return ValidRegions.begin(); }
+ const_iterator end() const { return ValidRegions.end(); }
+ //@}
+
+ /// Emit rejection remarks for all rejected regions.
+ ///
+ /// @param F The function to emit remarks for.
+ void emitMissedRemarks(const Function &F);
+
+ /// Mark the function as invalid so we will not extract any scop from
+ /// the function.
+ ///
+ /// @param F The function to mark as invalid.
+ static void markFunctionAsInvalid(Function *F);
+
+ /// Verify if all valid Regions in this Function are still valid
+ /// after some transformations.
+ void verifyAnalysis();
+
+ /// Verify if R is still a valid part of Scop after some transformations.
+ ///
+ /// @param R The Region to verify.
+ void verifyRegion(const Region &R);
+
+ /// Count the number of loops and the maximal loop depth in @p R.
+ ///
+ /// @param R The region to check
+ /// @param SE The scalar evolution analysis.
+ /// @param MinProfitableTrips The minimum number of trip counts from which
+ /// a loop is assumed to be profitable and
+ /// consequently is counted.
+ /// returns A tuple of number of loops and their maximal depth.
+ static ScopDetection::LoopStats
+ countBeneficialLoops(Region *R, ScalarEvolution &SE, LoopInfo &LI,
+ unsigned MinProfitableTrips);
+
+ /// Check if the block is a error block.
+ ///
+ /// A error block is currently any block that fulfills at least one of
+ /// the following conditions:
+ ///
+ /// - It is terminated by an unreachable instruction
+ /// - It contains a call to a non-pure function that is not immediately
+ /// dominated by a loop header and that does not dominate the region exit.
+ /// This is a heuristic to pick only error blocks that are conditionally
+ /// executed and can be assumed to be not executed at all without the
+ /// domains being available.
+ ///
+ /// @param BB The block to check.
+ /// @param R The analyzed region.
+ ///
+ /// @return True if the block is a error block, false otherwise.
+ bool isErrorBlock(llvm::BasicBlock &BB, const llvm::Region &R);
+
+private:
+ /// OptimizationRemarkEmitter object used to emit diagnostic remarks
+ OptimizationRemarkEmitter &ORE;
+};
+
+struct ScopAnalysis : public AnalysisInfoMixin<ScopAnalysis> {
+ static AnalysisKey Key;
+
+ using Result = ScopDetection;
+
+ ScopAnalysis();
+
+ Result run(Function &F, FunctionAnalysisManager &FAM);
+};
+
+struct ScopAnalysisPrinterPass : public PassInfoMixin<ScopAnalysisPrinterPass> {
+ ScopAnalysisPrinterPass(raw_ostream &OS) : OS(OS) {}
+
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);
+
+ raw_ostream &OS;
+};
+
+struct ScopDetectionWrapperPass : public FunctionPass {
+ static char ID;
+ std::unique_ptr<ScopDetection> Result;
+
+ ScopDetectionWrapperPass();
+
+ /// @name FunctionPass interface
+ //@{
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+ void releaseMemory() override;
+ bool runOnFunction(Function &F) override;
+ void print(raw_ostream &OS, const Module *) const override;
+ //@}
+
+ ScopDetection &getSD() const { return *Result; }
+};
+} // namespace polly
+
+#endif // POLLY_SCOPDETECTION_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/ScopDetectionDiagnostic.h b/contrib/libs/llvm14/tools/polly/include/polly/ScopDetectionDiagnostic.h
new file mode 100644
index 00000000000..f4c2ee466a3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/ScopDetectionDiagnostic.h
@@ -0,0 +1,890 @@
+//===- ScopDetectionDiagnostic.h - Diagnostic for ScopDetection -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Small set of diagnostic helper classes to encapsulate any errors occurred
+// during the detection of Scops.
+//
+// The ScopDetection defines a set of error classes (via Statistic variables)
+// that groups a number of individual errors into a group, e.g. non-affinity
+// related errors.
+// On error we generate an object that carries enough additional information
+// to diagnose the error and generate a helpful error message.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SCOPDETECTIONDIAGNOSTIC_H
+#define POLLY_SCOPDETECTIONDIAGNOSTIC_H
+
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/IR/DebugLoc.h"
+#include "llvm/IR/Instruction.h"
+#include <cstddef>
+
+namespace llvm {
+class AliasSet;
+class BasicBlock;
+class OptimizationRemarkEmitter;
+class Region;
+class SCEV;
+} // namespace llvm
+
+namespace polly {
+using llvm::AliasSet;
+using llvm::BasicBlock;
+using llvm::DebugLoc;
+using llvm::Instruction;
+using llvm::Loop;
+using llvm::OptimizationRemarkEmitter;
+using llvm::raw_ostream;
+using llvm::Region;
+using llvm::SCEV;
+using llvm::SmallVector;
+using llvm::Value;
+
+/// Type to hold region delimiters (entry & exit block).
+using BBPair = std::pair<BasicBlock *, BasicBlock *>;
+
+/// Return the region delimiters (entry & exit block) of @p R.
+BBPair getBBPairForRegion(const Region *R);
+
+/// Set the begin and end source location for the region limited by @p P.
+void getDebugLocations(const BBPair &P, DebugLoc &Begin, DebugLoc &End);
+
+class RejectLog;
+
+/// Emit optimization remarks about the rejected regions to the user.
+///
+/// This emits the content of the reject log as optimization remarks.
+/// Remember to at least track failures (-polly-detect-track-failures).
+/// @param P The region delimiters (entry & exit) we emit remarks for.
+/// @param Log The error log containing all messages being emitted as remark.
+void emitRejectionRemarks(const BBPair &P, const RejectLog &Log,
+ OptimizationRemarkEmitter &ORE);
+
+// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
+enum class RejectReasonKind {
+ // CFG Category
+ CFG,
+ InvalidTerminator,
+ IrreducibleRegion,
+ UnreachableInExit,
+ IndirectPredecessor,
+ LastCFG,
+
+ // Non-Affinity
+ AffFunc,
+ UndefCond,
+ InvalidCond,
+ UndefOperand,
+ NonAffBranch,
+ NoBasePtr,
+ UndefBasePtr,
+ VariantBasePtr,
+ NonAffineAccess,
+ DifferentElementSize,
+ LastAffFunc,
+
+ LoopBound,
+ LoopHasNoExit,
+ LoopHasMultipleExits,
+ LoopOnlySomeLatches,
+
+ FuncCall,
+ NonSimpleMemoryAccess,
+
+ Alias,
+
+ // Other
+ Other,
+ IntToPtr,
+ Alloca,
+ UnknownInst,
+ Entry,
+ Unprofitable,
+ LastOther
+};
+
+//===----------------------------------------------------------------------===//
+/// Base class of all reject reasons found during Scop detection.
+///
+/// Subclasses of RejectReason should provide means to capture enough
+/// diagnostic information to help clients figure out what and where something
+/// went wrong in the Scop detection.
+class RejectReason {
+private:
+ const RejectReasonKind Kind;
+
+protected:
+ static const DebugLoc Unknown;
+
+public:
+ RejectReason(RejectReasonKind K);
+
+ virtual ~RejectReason() = default;
+
+ RejectReasonKind getKind() const { return Kind; }
+
+ /// Generate the remark name to identify this remark.
+ ///
+ /// @return A short string that identifies the error.
+ virtual std::string getRemarkName() const = 0;
+
+ /// Get the Basic Block containing this remark.
+ ///
+ /// @return The Basic Block containing this remark.
+ virtual const Value *getRemarkBB() const = 0;
+
+ /// Generate a reasonable diagnostic message describing this error.
+ ///
+ /// @return A debug message representing this error.
+ virtual std::string getMessage() const = 0;
+
+ /// Generate a message for the end-user describing this error.
+ ///
+ /// The message provided has to be suitable for the end-user. So it should
+ /// not reference any LLVM internal data structures or terminology.
+ /// Ideally, the message helps the end-user to increase the size of the
+ /// regions amenable to Polly.
+ ///
+ /// @return A short message representing this error.
+ virtual std::string getEndUserMessage() const { return "Unspecified error."; }
+
+ /// Get the source location of this error.
+ ///
+ /// @return The debug location for this error.
+ virtual const DebugLoc &getDebugLoc() const;
+};
+
+using RejectReasonPtr = std::shared_ptr<RejectReason>;
+
+/// Stores all errors that occurred during the detection.
+class RejectLog {
+ Region *R;
+ SmallVector<RejectReasonPtr, 1> ErrorReports;
+
+public:
+ explicit RejectLog(Region *R) : R(R) {}
+
+ using iterator = SmallVector<RejectReasonPtr, 1>::const_iterator;
+
+ iterator begin() const { return ErrorReports.begin(); }
+ iterator end() const { return ErrorReports.end(); }
+ size_t size() const { return ErrorReports.size(); }
+
+ /// Returns true, if we store at least one error.
+ ///
+ /// @return true, if we store at least one error.
+ bool hasErrors() const { return size() > 0; }
+
+ void print(raw_ostream &OS, int level = 0) const;
+
+ const Region *region() const { return R; }
+ void report(RejectReasonPtr Reject) { ErrorReports.push_back(Reject); }
+};
+
+//===----------------------------------------------------------------------===//
+/// Base class for CFG related reject reasons.
+///
+/// Scop candidates that violate structural restrictions can be grouped under
+/// this reject reason class.
+class ReportCFG : public RejectReason {
+public:
+ ReportCFG(const RejectReasonKind K);
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures bad terminator within a Scop candidate.
+class ReportInvalidTerminator : public ReportCFG {
+ BasicBlock *BB;
+
+public:
+ ReportInvalidTerminator(BasicBlock *BB)
+ : ReportCFG(RejectReasonKind::InvalidTerminator), BB(BB) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures irreducible regions in CFG.
+class ReportIrreducibleRegion : public ReportCFG {
+ Region *R;
+ DebugLoc DbgLoc;
+
+public:
+ ReportIrreducibleRegion(Region *R, DebugLoc DbgLoc)
+ : ReportCFG(RejectReasonKind::IrreducibleRegion), R(R), DbgLoc(DbgLoc) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ std::string getEndUserMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures regions with an unreachable in the exit block.
+class ReportUnreachableInExit : public ReportCFG {
+ BasicBlock *BB;
+ DebugLoc DbgLoc;
+
+public:
+ ReportUnreachableInExit(BasicBlock *BB, DebugLoc DbgLoc)
+ : ReportCFG(RejectReasonKind::UnreachableInExit), BB(BB), DbgLoc(DbgLoc) {
+ }
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ std::string getEndUserMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures regions with an IndirectBr predecessor.
+class ReportIndirectPredecessor : public ReportCFG {
+ Instruction *Inst;
+ DebugLoc DbgLoc;
+
+public:
+ ReportIndirectPredecessor(Instruction *Inst, DebugLoc DbgLoc)
+ : ReportCFG(RejectReasonKind::IndirectPredecessor), Inst(Inst),
+ DbgLoc(DbgLoc) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ std::string getEndUserMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Base class for non-affine reject reasons.
+///
+/// Scop candidates that violate restrictions to affinity are reported under
+/// this class.
+class ReportAffFunc : public RejectReason {
+protected:
+ // The instruction that caused non-affinity to occur.
+ const Instruction *Inst;
+
+public:
+ ReportAffFunc(const RejectReasonKind K, const Instruction *Inst);
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ const DebugLoc &getDebugLoc() const override { return Inst->getDebugLoc(); }
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures a condition that is based on an 'undef' value.
+class ReportUndefCond : public ReportAffFunc {
+ // The BasicBlock we found the broken condition in.
+ BasicBlock *BB;
+
+public:
+ ReportUndefCond(const Instruction *Inst, BasicBlock *BB)
+ : ReportAffFunc(RejectReasonKind::UndefCond, Inst), BB(BB) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures an invalid condition
+///
+/// Conditions have to be either constants or icmp instructions.
+class ReportInvalidCond : public ReportAffFunc {
+ // The BasicBlock we found the broken condition in.
+ BasicBlock *BB;
+
+public:
+ ReportInvalidCond(const Instruction *Inst, BasicBlock *BB)
+ : ReportAffFunc(RejectReasonKind::InvalidCond, Inst), BB(BB) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures an undefined operand.
+class ReportUndefOperand : public ReportAffFunc {
+ // The BasicBlock we found the undefined operand in.
+ BasicBlock *BB;
+
+public:
+ ReportUndefOperand(BasicBlock *BB, const Instruction *Inst)
+ : ReportAffFunc(RejectReasonKind::UndefOperand, Inst), BB(BB) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures a non-affine branch.
+class ReportNonAffBranch : public ReportAffFunc {
+ // The BasicBlock we found the non-affine branch in.
+ BasicBlock *BB;
+
+ /// LHS & RHS of the failed condition.
+ //@{
+ const SCEV *LHS;
+ const SCEV *RHS;
+ //@}
+
+public:
+ ReportNonAffBranch(BasicBlock *BB, const SCEV *LHS, const SCEV *RHS,
+ const Instruction *Inst)
+ : ReportAffFunc(RejectReasonKind::NonAffBranch, Inst), BB(BB), LHS(LHS),
+ RHS(RHS) {}
+
+ const SCEV *lhs() { return LHS; }
+ const SCEV *rhs() { return RHS; }
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures a missing base pointer.
+class ReportNoBasePtr : public ReportAffFunc {
+public:
+ ReportNoBasePtr(const Instruction *Inst)
+ : ReportAffFunc(RejectReasonKind::NoBasePtr, Inst) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures an undefined base pointer.
+class ReportUndefBasePtr : public ReportAffFunc {
+public:
+ ReportUndefBasePtr(const Instruction *Inst)
+ : ReportAffFunc(RejectReasonKind::UndefBasePtr, Inst) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures a base pointer that is not invariant in the region.
+class ReportVariantBasePtr : public ReportAffFunc {
+ // The variant base pointer.
+ Value *BaseValue;
+
+public:
+ ReportVariantBasePtr(Value *BaseValue, const Instruction *Inst)
+ : ReportAffFunc(RejectReasonKind::VariantBasePtr, Inst),
+ BaseValue(BaseValue) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ std::string getEndUserMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures a non-affine access function.
+class ReportNonAffineAccess : public ReportAffFunc {
+ // The non-affine access function.
+ const SCEV *AccessFunction;
+
+ // The base pointer of the memory access.
+ const Value *BaseValue;
+
+public:
+ ReportNonAffineAccess(const SCEV *AccessFunction, const Instruction *Inst,
+ const Value *V)
+ : ReportAffFunc(RejectReasonKind::NonAffineAccess, Inst),
+ AccessFunction(AccessFunction), BaseValue(V) {}
+
+ const SCEV *get() { return AccessFunction; }
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ std::string getEndUserMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Report array accesses with differing element size.
+class ReportDifferentArrayElementSize : public ReportAffFunc {
+ // The base pointer of the memory access.
+ const Value *BaseValue;
+
+public:
+ ReportDifferentArrayElementSize(const Instruction *Inst, const Value *V)
+ : ReportAffFunc(RejectReasonKind::DifferentElementSize, Inst),
+ BaseValue(V) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ std::string getEndUserMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures errors with non affine loop bounds.
+class ReportLoopBound : public RejectReason {
+ // The offending loop.
+ Loop *L;
+
+ // The non-affine loop bound.
+ const SCEV *LoopCount;
+
+ // A copy of the offending loop's debug location.
+ const DebugLoc Loc;
+
+public:
+ ReportLoopBound(Loop *L, const SCEV *LoopCount);
+
+ const SCEV *loopCount() { return LoopCount; }
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ std::string getEndUserMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures errors when loop has no exit.
+class ReportLoopHasNoExit : public RejectReason {
+ /// The loop that has no exit.
+ Loop *L;
+
+ const DebugLoc Loc;
+
+public:
+ ReportLoopHasNoExit(Loop *L)
+ : RejectReason(RejectReasonKind::LoopHasNoExit), L(L),
+ Loc(L->getStartLoc()) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ std::string getEndUserMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures errors when a loop has multiple exists.
+class ReportLoopHasMultipleExits : public RejectReason {
+ /// The loop that has multiple exits.
+ Loop *L;
+
+ const DebugLoc Loc;
+
+public:
+ ReportLoopHasMultipleExits(Loop *L)
+ : RejectReason(RejectReasonKind::LoopHasMultipleExits), L(L),
+ Loc(L->getStartLoc()) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ std::string getEndUserMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures errors when not all loop latches are part of the scop.
+class ReportLoopOnlySomeLatches : public RejectReason {
+ /// The loop for which not all loop latches are part of the scop.
+ Loop *L;
+
+ const DebugLoc Loc;
+
+public:
+ ReportLoopOnlySomeLatches(Loop *L)
+ : RejectReason(RejectReasonKind::LoopOnlySomeLatches), L(L),
+ Loc(L->getStartLoc()) {}
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ std::string getEndUserMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures errors with non-side-effect-known function calls.
+class ReportFuncCall : public RejectReason {
+ // The offending call instruction.
+ Instruction *Inst;
+
+public:
+ ReportFuncCall(Instruction *Inst);
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ std::string getEndUserMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures errors with aliasing.
+class ReportAlias : public RejectReason {
+public:
+ using PointerSnapshotTy = std::vector<const Value *>;
+
+private:
+ /// Format an invalid alias set.
+ ///
+ // @param Prefix A prefix string to put before the list of aliasing pointers.
+ // @param Suffix A suffix string to put after the list of aliasing pointers.
+ std::string formatInvalidAlias(std::string Prefix = "",
+ std::string Suffix = "") const;
+
+ Instruction *Inst;
+
+ // A snapshot of the llvm values that took part in the aliasing error.
+ mutable PointerSnapshotTy Pointers;
+
+public:
+ ReportAlias(Instruction *Inst, AliasSet &AS);
+
+ const PointerSnapshotTy &getPointers() const { return Pointers; }
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ std::string getEndUserMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Base class for otherwise ungrouped reject reasons.
+class ReportOther : public RejectReason {
+public:
+ ReportOther(const RejectReasonKind K);
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ std::string getMessage() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures errors with bad IntToPtr instructions.
+class ReportIntToPtr : public ReportOther {
+ // The offending base value.
+ Instruction *BaseValue;
+
+public:
+ ReportIntToPtr(Instruction *BaseValue);
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures errors with alloca instructions.
+class ReportAlloca : public ReportOther {
+ Instruction *Inst;
+
+public:
+ ReportAlloca(Instruction *Inst);
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures errors with unknown instructions.
+class ReportUnknownInst : public ReportOther {
+ Instruction *Inst;
+
+public:
+ ReportUnknownInst(Instruction *Inst);
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures errors with regions containing the function entry block.
+class ReportEntry : public ReportOther {
+ BasicBlock *BB;
+
+public:
+ ReportEntry(BasicBlock *BB);
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ std::string getEndUserMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Report regions that seem not profitable to be optimized.
+class ReportUnprofitable : public ReportOther {
+ Region *R;
+
+public:
+ ReportUnprofitable(Region *R);
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ std::string getEndUserMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ //@}
+};
+
+//===----------------------------------------------------------------------===//
+/// Captures errors with non-simple memory accesses.
+class ReportNonSimpleMemoryAccess : public ReportOther {
+ // The offending call instruction.
+ Instruction *Inst;
+
+public:
+ ReportNonSimpleMemoryAccess(Instruction *Inst);
+
+ /// @name LLVM-RTTI interface
+ //@{
+ static bool classof(const RejectReason *RR);
+ //@}
+
+ /// @name RejectReason interface
+ //@{
+ std::string getRemarkName() const override;
+ const Value *getRemarkBB() const override;
+ std::string getMessage() const override;
+ const DebugLoc &getDebugLoc() const override;
+ std::string getEndUserMessage() const override;
+ //@}
+};
+} // namespace polly
+
+#endif // POLLY_SCOPDETECTIONDIAGNOSTIC_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/ScopInfo.h b/contrib/libs/llvm14/tools/polly/include/polly/ScopInfo.h
new file mode 100644
index 00000000000..9739e3310da
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/ScopInfo.h
@@ -0,0 +1,2869 @@
+//===- polly/ScopInfo.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Store the polyhedral model representation of a static control flow region,
+// also called SCoP (Static Control Part).
+//
+// This representation is shared among several tools in the polyhedral
+// community, which are e.g. CLooG, Pluto, Loopo, Graphite.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SCOPINFO_H
+#define POLLY_SCOPINFO_H
+
+#include "polly/ScopDetection.h"
+#include "polly/Support/SCEVAffinator.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/Analysis/RegionPass.h"
+#include "llvm/IR/DebugLoc.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/ValueHandle.h"
+#include "llvm/Pass.h"
+#include "isl/isl-noexceptions.h"
+#include <cassert>
+#include <cstddef>
+#include <forward_list>
+
+namespace llvm {
+void initializeScopInfoRegionPassPass(PassRegistry &);
+void initializeScopInfoWrapperPassPass(PassRegistry &);
+} // end namespace llvm
+
+namespace polly {
+using llvm::AnalysisInfoMixin;
+using llvm::ArrayRef;
+using llvm::AssertingVH;
+using llvm::AssumptionCache;
+using llvm::cast;
+using llvm::DataLayout;
+using llvm::DenseMap;
+using llvm::DenseSet;
+using llvm::function_ref;
+using llvm::isa;
+using llvm::iterator_range;
+using llvm::LoadInst;
+using llvm::make_range;
+using llvm::MapVector;
+using llvm::MemIntrinsic;
+using llvm::Optional;
+using llvm::PassInfoMixin;
+using llvm::PHINode;
+using llvm::RegionNode;
+using llvm::RegionPass;
+using llvm::RGPassManager;
+using llvm::SetVector;
+using llvm::SmallPtrSetImpl;
+using llvm::SmallVector;
+using llvm::SmallVectorImpl;
+using llvm::StringMap;
+using llvm::Type;
+using llvm::Use;
+using llvm::Value;
+using llvm::ValueToValueMap;
+
+class MemoryAccess;
+
+//===---------------------------------------------------------------------===//
+
+extern bool UseInstructionNames;
+
+// The maximal number of basic sets we allow during domain construction to
+// be created. More complex scops will result in very high compile time and
+// are also unlikely to result in good code.
+extern unsigned const MaxDisjunctsInDomain;
+
+/// The different memory kinds used in Polly.
+///
+/// We distinguish between arrays and various scalar memory objects. We use
+/// the term ``array'' to describe memory objects that consist of a set of
+/// individual data elements arranged in a multi-dimensional grid. A scalar
+/// memory object describes an individual data element and is used to model
+/// the definition and uses of llvm::Values.
+///
+/// The polyhedral model does traditionally not reason about SSA values. To
+/// reason about llvm::Values we model them "as if" they were zero-dimensional
+/// memory objects, even though they were not actually allocated in (main)
+/// memory. Memory for such objects is only alloca[ed] at CodeGeneration
+/// time. To relate the memory slots used during code generation with the
+/// llvm::Values they belong to the new names for these corresponding stack
+/// slots are derived by appending suffixes (currently ".s2a" and ".phiops")
+/// to the name of the original llvm::Value. To describe how def/uses are
+/// modeled exactly we use these suffixes here as well.
+///
+/// There are currently four different kinds of memory objects:
+enum class MemoryKind {
+ /// MemoryKind::Array: Models a one or multi-dimensional array
+ ///
+ /// A memory object that can be described by a multi-dimensional array.
+ /// Memory objects of this type are used to model actual multi-dimensional
+ /// arrays as they exist in LLVM-IR, but they are also used to describe
+ /// other objects:
+ /// - A single data element allocated on the stack using 'alloca' is
+ /// modeled as a one-dimensional, single-element array.
+ /// - A single data element allocated as a global variable is modeled as
+ /// one-dimensional, single-element array.
+ /// - Certain multi-dimensional arrays with variable size, which in
+ /// LLVM-IR are commonly expressed as a single-dimensional access with a
+ /// complicated access function, are modeled as multi-dimensional
+ /// memory objects (grep for "delinearization").
+ Array,
+
+ /// MemoryKind::Value: Models an llvm::Value
+ ///
+ /// Memory objects of type MemoryKind::Value are used to model the data flow
+ /// induced by llvm::Values. For each llvm::Value that is used across
+ /// BasicBlocks, one ScopArrayInfo object is created. A single memory WRITE
+ /// stores the llvm::Value at its definition into the memory object and at
+ /// each use of the llvm::Value (ignoring trivial intra-block uses) a
+ /// corresponding READ is added. For instance, the use/def chain of a
+ /// llvm::Value %V depicted below
+ /// ______________________
+ /// |DefBB: |
+ /// | %V = float op ... |
+ /// ----------------------
+ /// | |
+ /// _________________ _________________
+ /// |UseBB1: | |UseBB2: |
+ /// | use float %V | | use float %V |
+ /// ----------------- -----------------
+ ///
+ /// is modeled as if the following memory accesses occurred:
+ ///
+ /// __________________________
+ /// |entry: |
+ /// | %V.s2a = alloca float |
+ /// --------------------------
+ /// |
+ /// ___________________________________
+ /// |DefBB: |
+ /// | store %float %V, float* %V.s2a |
+ /// -----------------------------------
+ /// | |
+ /// ____________________________________ ___________________________________
+ /// |UseBB1: | |UseBB2: |
+ /// | %V.reload1 = load float* %V.s2a | | %V.reload2 = load float* %V.s2a|
+ /// | use float %V.reload1 | | use float %V.reload2 |
+ /// ------------------------------------ -----------------------------------
+ ///
+ Value,
+
+ /// MemoryKind::PHI: Models PHI nodes within the SCoP
+ ///
+ /// Besides the MemoryKind::Value memory object used to model the normal
+ /// llvm::Value dependences described above, PHI nodes require an additional
+ /// memory object of type MemoryKind::PHI to describe the forwarding of values
+ /// to
+ /// the PHI node.
+ ///
+ /// As an example, a PHIInst instructions
+ ///
+ /// %PHI = phi float [ %Val1, %IncomingBlock1 ], [ %Val2, %IncomingBlock2 ]
+ ///
+ /// is modeled as if the accesses occurred this way:
+ ///
+ /// _______________________________
+ /// |entry: |
+ /// | %PHI.phiops = alloca float |
+ /// -------------------------------
+ /// | |
+ /// __________________________________ __________________________________
+ /// |IncomingBlock1: | |IncomingBlock2: |
+ /// | ... | | ... |
+ /// | store float %Val1 %PHI.phiops | | store float %Val2 %PHI.phiops |
+ /// | br label % JoinBlock | | br label %JoinBlock |
+ /// ---------------------------------- ----------------------------------
+ /// \ /
+ /// \ /
+ /// _________________________________________
+ /// |JoinBlock: |
+ /// | %PHI = load float, float* PHI.phiops |
+ /// -----------------------------------------
+ ///
+ /// Note that there can also be a scalar write access for %PHI if used in a
+ /// different BasicBlock, i.e. there can be a memory object %PHI.phiops as
+ /// well as a memory object %PHI.s2a.
+ PHI,
+
+ /// MemoryKind::ExitPHI: Models PHI nodes in the SCoP's exit block
+ ///
+ /// For PHI nodes in the Scop's exit block a special memory object kind is
+ /// used. The modeling used is identical to MemoryKind::PHI, with the
+ /// exception
+ /// that there are no READs from these memory objects. The PHINode's
+ /// llvm::Value is treated as a value escaping the SCoP. WRITE accesses
+ /// write directly to the escaping value's ".s2a" alloca.
+ ExitPHI
+};
+
+/// Maps from a loop to the affine function expressing its backedge taken count.
+/// The backedge taken count already enough to express iteration domain as we
+/// only allow loops with canonical induction variable.
+/// A canonical induction variable is:
+/// an integer recurrence that starts at 0 and increments by one each time
+/// through the loop.
+using LoopBoundMapType = std::map<const Loop *, const SCEV *>;
+
+using AccFuncVector = std::vector<std::unique_ptr<MemoryAccess>>;
+
+/// A class to store information about arrays in the SCoP.
+///
+/// Objects are accessible via the ScoP, MemoryAccess or the id associated with
+/// the MemoryAccess access function.
+///
+class ScopArrayInfo {
+public:
+ /// Construct a ScopArrayInfo object.
+ ///
+ /// @param BasePtr The array base pointer.
+ /// @param ElementType The type of the elements stored in the array.
+ /// @param IslCtx The isl context used to create the base pointer id.
+ /// @param DimensionSizes A vector containing the size of each dimension.
+ /// @param Kind The kind of the array object.
+ /// @param DL The data layout of the module.
+ /// @param S The scop this array object belongs to.
+ /// @param BaseName The optional name of this memory reference.
+ ScopArrayInfo(Value *BasePtr, Type *ElementType, isl::ctx IslCtx,
+ ArrayRef<const SCEV *> DimensionSizes, MemoryKind Kind,
+ const DataLayout &DL, Scop *S, const char *BaseName = nullptr);
+
+ /// Destructor to free the isl id of the base pointer.
+ ~ScopArrayInfo();
+
+ /// Update the element type of the ScopArrayInfo object.
+ ///
+ /// Memory accesses referencing this ScopArrayInfo object may use
+ /// different element sizes. This function ensures the canonical element type
+ /// stored is small enough to model accesses to the current element type as
+ /// well as to @p NewElementType.
+ ///
+ /// @param NewElementType An element type that is used to access this array.
+ void updateElementType(Type *NewElementType);
+
+ /// Update the sizes of the ScopArrayInfo object.
+ ///
+ /// A ScopArrayInfo object may be created without all outer dimensions being
+ /// available. This function is called when new memory accesses are added for
+ /// this ScopArrayInfo object. It verifies that sizes are compatible and adds
+ /// additional outer array dimensions, if needed.
+ ///
+ /// @param Sizes A vector of array sizes where the rightmost array
+ /// sizes need to match the innermost array sizes already
+ /// defined in SAI.
+ /// @param CheckConsistency Update sizes, even if new sizes are inconsistent
+ /// with old sizes
+ bool updateSizes(ArrayRef<const SCEV *> Sizes, bool CheckConsistency = true);
+
+ /// Set the base pointer to @p BP.
+ void setBasePtr(Value *BP) { BasePtr = BP; }
+
+ /// Return the base pointer.
+ Value *getBasePtr() const { return BasePtr; }
+
+ // Set IsOnHeap to the value in parameter.
+ void setIsOnHeap(bool value) { IsOnHeap = value; }
+
+ /// For indirect accesses return the origin SAI of the BP, else null.
+ const ScopArrayInfo *getBasePtrOriginSAI() const { return BasePtrOriginSAI; }
+
+ /// The set of derived indirect SAIs for this origin SAI.
+ const SmallSetVector<ScopArrayInfo *, 2> &getDerivedSAIs() const {
+ return DerivedSAIs;
+ }
+
+ /// Return the number of dimensions.
+ unsigned getNumberOfDimensions() const {
+ if (Kind == MemoryKind::PHI || Kind == MemoryKind::ExitPHI ||
+ Kind == MemoryKind::Value)
+ return 0;
+ return DimensionSizes.size();
+ }
+
+ /// Return the size of dimension @p dim as SCEV*.
+ //
+ // Scalars do not have array dimensions and the first dimension of
+ // a (possibly multi-dimensional) array also does not carry any size
+ // information, in case the array is not newly created.
+ const SCEV *getDimensionSize(unsigned Dim) const {
+ assert(Dim < getNumberOfDimensions() && "Invalid dimension");
+ return DimensionSizes[Dim];
+ }
+
+ /// Return the size of dimension @p dim as isl::pw_aff.
+ //
+ // Scalars do not have array dimensions and the first dimension of
+ // a (possibly multi-dimensional) array also does not carry any size
+ // information, in case the array is not newly created.
+ isl::pw_aff getDimensionSizePw(unsigned Dim) const {
+ assert(Dim < getNumberOfDimensions() && "Invalid dimension");
+ return DimensionSizesPw[Dim];
+ }
+
+ /// Get the canonical element type of this array.
+ ///
+ /// @returns The canonical element type of this array.
+ Type *getElementType() const { return ElementType; }
+
+ /// Get element size in bytes.
+ int getElemSizeInBytes() const;
+
+ /// Get the name of this memory reference.
+ std::string getName() const;
+
+ /// Return the isl id for the base pointer.
+ isl::id getBasePtrId() const;
+
+ /// Return what kind of memory this represents.
+ MemoryKind getKind() const { return Kind; }
+
+ /// Is this array info modeling an llvm::Value?
+ bool isValueKind() const { return Kind == MemoryKind::Value; }
+
+ /// Is this array info modeling special PHI node memory?
+ ///
+ /// During code generation of PHI nodes, there is a need for two kinds of
+ /// virtual storage. The normal one as it is used for all scalar dependences,
+ /// where the result of the PHI node is stored and later loaded from as well
+ /// as a second one where the incoming values of the PHI nodes are stored
+ /// into and reloaded when the PHI is executed. As both memories use the
+ /// original PHI node as virtual base pointer, we have this additional
+ /// attribute to distinguish the PHI node specific array modeling from the
+ /// normal scalar array modeling.
+ bool isPHIKind() const { return Kind == MemoryKind::PHI; }
+
+ /// Is this array info modeling an MemoryKind::ExitPHI?
+ bool isExitPHIKind() const { return Kind == MemoryKind::ExitPHI; }
+
+ /// Is this array info modeling an array?
+ bool isArrayKind() const { return Kind == MemoryKind::Array; }
+
+ /// Is this array allocated on heap
+ ///
+ /// This property is only relevant if the array is allocated by Polly instead
+ /// of pre-existing. If false, it is allocated using alloca instead malloca.
+ bool isOnHeap() const { return IsOnHeap; }
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ /// Dump a readable representation to stderr.
+ void dump() const;
+#endif
+
+ /// Print a readable representation to @p OS.
+ ///
+ /// @param SizeAsPwAff Print the size as isl::pw_aff
+ void print(raw_ostream &OS, bool SizeAsPwAff = false) const;
+
+ /// Access the ScopArrayInfo associated with an access function.
+ static const ScopArrayInfo *getFromAccessFunction(isl::pw_multi_aff PMA);
+
+ /// Access the ScopArrayInfo associated with an isl Id.
+ static const ScopArrayInfo *getFromId(isl::id Id);
+
+ /// Get the space of this array access.
+ isl::space getSpace() const;
+
+ /// If the array is read only
+ bool isReadOnly();
+
+ /// Verify that @p Array is compatible to this ScopArrayInfo.
+ ///
+ /// Two arrays are compatible if their dimensionality, the sizes of their
+ /// dimensions, and their element sizes match.
+ ///
+ /// @param Array The array to compare against.
+ ///
+ /// @returns True, if the arrays are compatible, False otherwise.
+ bool isCompatibleWith(const ScopArrayInfo *Array) const;
+
+private:
+ void addDerivedSAI(ScopArrayInfo *DerivedSAI) {
+ DerivedSAIs.insert(DerivedSAI);
+ }
+
+ /// For indirect accesses this is the SAI of the BP origin.
+ const ScopArrayInfo *BasePtrOriginSAI;
+
+ /// For origin SAIs the set of derived indirect SAIs.
+ SmallSetVector<ScopArrayInfo *, 2> DerivedSAIs;
+
+ /// The base pointer.
+ AssertingVH<Value> BasePtr;
+
+ /// The canonical element type of this array.
+ ///
+ /// The canonical element type describes the minimal accessible element in
+ /// this array. Not all elements accessed, need to be of the very same type,
+ /// but the allocation size of the type of the elements loaded/stored from/to
+ /// this array needs to be a multiple of the allocation size of the canonical
+ /// type.
+ Type *ElementType;
+
+ /// The isl id for the base pointer.
+ isl::id Id;
+
+ /// True if the newly allocated array is on heap.
+ bool IsOnHeap = false;
+
+ /// The sizes of each dimension as SCEV*.
+ SmallVector<const SCEV *, 4> DimensionSizes;
+
+ /// The sizes of each dimension as isl::pw_aff.
+ SmallVector<isl::pw_aff, 4> DimensionSizesPw;
+
+ /// The type of this scop array info object.
+ ///
+ /// We distinguish between SCALAR, PHI and ARRAY objects.
+ MemoryKind Kind;
+
+ /// The data layout of the module.
+ const DataLayout &DL;
+
+ /// The scop this SAI object belongs to.
+ Scop &S;
+};
+
+/// Represent memory accesses in statements.
+class MemoryAccess {
+ friend class Scop;
+ friend class ScopStmt;
+ friend class ScopBuilder;
+
+public:
+ /// The access type of a memory access
+ ///
+ /// There are three kind of access types:
+ ///
+ /// * A read access
+ ///
+ /// A certain set of memory locations are read and may be used for internal
+ /// calculations.
+ ///
+ /// * A must-write access
+ ///
+ /// A certain set of memory locations is definitely written. The old value is
+ /// replaced by a newly calculated value. The old value is not read or used at
+ /// all.
+ ///
+ /// * A may-write access
+ ///
+ /// A certain set of memory locations may be written. The memory location may
+ /// contain a new value if there is actually a write or the old value may
+ /// remain, if no write happens.
+ enum AccessType {
+ READ = 0x1,
+ MUST_WRITE = 0x2,
+ MAY_WRITE = 0x3,
+ };
+
+ /// Reduction access type
+ ///
+ /// Commutative and associative binary operations suitable for reductions
+ enum ReductionType {
+ RT_NONE, ///< Indicate no reduction at all
+ RT_ADD, ///< Addition
+ RT_MUL, ///< Multiplication
+ RT_BOR, ///< Bitwise Or
+ RT_BXOR, ///< Bitwise XOr
+ RT_BAND, ///< Bitwise And
+ };
+
+ using SubscriptsTy = SmallVector<const SCEV *, 4>;
+
+private:
+ /// A unique identifier for this memory access.
+ ///
+ /// The identifier is unique between all memory accesses belonging to the same
+ /// scop statement.
+ isl::id Id;
+
+ /// What is modeled by this MemoryAccess.
+ /// @see MemoryKind
+ MemoryKind Kind;
+
+ /// Whether it a reading or writing access, and if writing, whether it
+ /// is conditional (MAY_WRITE).
+ enum AccessType AccType;
+
+ /// Reduction type for reduction like accesses, RT_NONE otherwise
+ ///
+ /// An access is reduction like if it is part of a load-store chain in which
+ /// both access the same memory location (use the same LLVM-IR value
+ /// as pointer reference). Furthermore, between the load and the store there
+ /// is exactly one binary operator which is known to be associative and
+ /// commutative.
+ ///
+ /// TODO:
+ ///
+ /// We can later lift the constraint that the same LLVM-IR value defines the
+ /// memory location to handle scops such as the following:
+ ///
+ /// for i
+ /// for j
+ /// sum[i+j] = sum[i] + 3;
+ ///
+ /// Here not all iterations access the same memory location, but iterations
+ /// for which j = 0 holds do. After lifting the equality check in ScopBuilder,
+ /// subsequent transformations do not only need check if a statement is
+ /// reduction like, but they also need to verify that that the reduction
+ /// property is only exploited for statement instances that load from and
+ /// store to the same data location. Doing so at dependence analysis time
+ /// could allow us to handle the above example.
+ ReductionType RedType = RT_NONE;
+
+ /// Parent ScopStmt of this access.
+ ScopStmt *Statement;
+
+ /// The domain under which this access is not modeled precisely.
+ ///
+ /// The invalid domain for an access describes all parameter combinations
+ /// under which the statement looks to be executed but is in fact not because
+ /// some assumption/restriction makes the access invalid.
+ isl::set InvalidDomain;
+
+ // Properties describing the accessed array.
+ // TODO: It might be possible to move them to ScopArrayInfo.
+ // @{
+
+ /// The base address (e.g., A for A[i+j]).
+ ///
+ /// The #BaseAddr of a memory access of kind MemoryKind::Array is the base
+ /// pointer of the memory access.
+ /// The #BaseAddr of a memory access of kind MemoryKind::PHI or
+ /// MemoryKind::ExitPHI is the PHI node itself.
+ /// The #BaseAddr of a memory access of kind MemoryKind::Value is the
+ /// instruction defining the value.
+ AssertingVH<Value> BaseAddr;
+
+ /// Type a single array element wrt. this access.
+ Type *ElementType;
+
+ /// Size of each dimension of the accessed array.
+ SmallVector<const SCEV *, 4> Sizes;
+ // @}
+
+ // Properties describing the accessed element.
+ // @{
+
+ /// The access instruction of this memory access.
+ ///
+ /// For memory accesses of kind MemoryKind::Array the access instruction is
+ /// the Load or Store instruction performing the access.
+ ///
+ /// For memory accesses of kind MemoryKind::PHI or MemoryKind::ExitPHI the
+ /// access instruction of a load access is the PHI instruction. The access
+ /// instruction of a PHI-store is the incoming's block's terminator
+ /// instruction.
+ ///
+ /// For memory accesses of kind MemoryKind::Value the access instruction of a
+ /// load access is nullptr because generally there can be multiple
+ /// instructions in the statement using the same llvm::Value. The access
+ /// instruction of a write access is the instruction that defines the
+ /// llvm::Value.
+ Instruction *AccessInstruction = nullptr;
+
+ /// Incoming block and value of a PHINode.
+ SmallVector<std::pair<BasicBlock *, Value *>, 4> Incoming;
+
+ /// The value associated with this memory access.
+ ///
+ /// - For array memory accesses (MemoryKind::Array) it is the loaded result
+ /// or the stored value. If the access instruction is a memory intrinsic it
+ /// the access value is also the memory intrinsic.
+ /// - For accesses of kind MemoryKind::Value it is the access instruction
+ /// itself.
+ /// - For accesses of kind MemoryKind::PHI or MemoryKind::ExitPHI it is the
+ /// PHI node itself (for both, READ and WRITE accesses).
+ ///
+ AssertingVH<Value> AccessValue;
+
+ /// Are all the subscripts affine expression?
+ bool IsAffine = true;
+
+ /// Subscript expression for each dimension.
+ SubscriptsTy Subscripts;
+
+ /// Relation from statement instances to the accessed array elements.
+ ///
+ /// In the common case this relation is a function that maps a set of loop
+ /// indices to the memory address from which a value is loaded/stored:
+ ///
+ /// for i
+ /// for j
+ /// S: A[i + 3 j] = ...
+ ///
+ /// => { S[i,j] -> A[i + 3j] }
+ ///
+ /// In case the exact access function is not known, the access relation may
+ /// also be a one to all mapping { S[i,j] -> A[o] } describing that any
+ /// element accessible through A might be accessed.
+ ///
+ /// In case of an access to a larger element belonging to an array that also
+ /// contains smaller elements, the access relation models the larger access
+ /// with multiple smaller accesses of the size of the minimal array element
+ /// type:
+ ///
+ /// short *A;
+ ///
+ /// for i
+ /// S: A[i] = *((double*)&A[4 * i]);
+ ///
+ /// => { S[i] -> A[i]; S[i] -> A[o] : 4i <= o <= 4i + 3 }
+ isl::map AccessRelation;
+
+ /// Updated access relation read from JSCOP file.
+ isl::map NewAccessRelation;
+ // @}
+
+ isl::basic_map createBasicAccessMap(ScopStmt *Statement);
+
+ isl::set assumeNoOutOfBound();
+
+ /// Compute bounds on an over approximated access relation.
+ ///
+ /// @param ElementSize The size of one element accessed.
+ void computeBoundsOnAccessRelation(unsigned ElementSize);
+
+ /// Get the original access function as read from IR.
+ isl::map getOriginalAccessRelation() const;
+
+ /// Return the space in which the access relation lives in.
+ isl::space getOriginalAccessRelationSpace() const;
+
+ /// Get the new access function imported or set by a pass
+ isl::map getNewAccessRelation() const;
+
+ /// Fold the memory access to consider parametric offsets
+ ///
+ /// To recover memory accesses with array size parameters in the subscript
+ /// expression we post-process the delinearization results.
+ ///
+ /// We would normally recover from an access A[exp0(i) * N + exp1(i)] into an
+ /// array A[][N] the 2D access A[exp0(i)][exp1(i)]. However, another valid
+ /// delinearization is A[exp0(i) - 1][exp1(i) + N] which - depending on the
+ /// range of exp1(i) - may be preferable. Specifically, for cases where we
+ /// know exp1(i) is negative, we want to choose the latter expression.
+ ///
+ /// As we commonly do not have any information about the range of exp1(i),
+ /// we do not choose one of the two options, but instead create a piecewise
+ /// access function that adds the (-1, N) offsets as soon as exp1(i) becomes
+ /// negative. For a 2D array such an access function is created by applying
+ /// the piecewise map:
+ ///
+ /// [i,j] -> [i, j] : j >= 0
+ /// [i,j] -> [i-1, j+N] : j < 0
+ ///
+ /// We can generalize this mapping to arbitrary dimensions by applying this
+ /// piecewise mapping pairwise from the rightmost to the leftmost access
+ /// dimension. It would also be possible to cover a wider range by introducing
+ /// more cases and adding multiple of Ns to these cases. However, this has
+ /// not yet been necessary.
+ /// The introduction of different cases necessarily complicates the memory
+ /// access function, but cases that can be statically proven to not happen
+ /// will be eliminated later on.
+ void foldAccessRelation();
+
+ /// Create the access relation for the underlying memory intrinsic.
+ void buildMemIntrinsicAccessRelation();
+
+ /// Assemble the access relation from all available information.
+ ///
+ /// In particular, used the information passes in the constructor and the
+ /// parent ScopStmt set by setStatment().
+ ///
+ /// @param SAI Info object for the accessed array.
+ void buildAccessRelation(const ScopArrayInfo *SAI);
+
+ /// Carry index overflows of dimensions with constant size to the next higher
+ /// dimension.
+ ///
+ /// For dimensions that have constant size, modulo the index by the size and
+ /// add up the carry (floored division) to the next higher dimension. This is
+ /// how overflow is defined in row-major order.
+ /// It happens e.g. when ScalarEvolution computes the offset to the base
+ /// pointer and would algebraically sum up all lower dimensions' indices of
+ /// constant size.
+ ///
+ /// Example:
+ /// float (*A)[4];
+ /// A[1][6] -> A[2][2]
+ void wrapConstantDimensions();
+
+public:
+ /// Create a new MemoryAccess.
+ ///
+ /// @param Stmt The parent statement.
+ /// @param AccessInst The instruction doing the access.
+ /// @param BaseAddr The accessed array's address.
+ /// @param ElemType The type of the accessed array elements.
+ /// @param AccType Whether read or write access.
+ /// @param IsAffine Whether the subscripts are affine expressions.
+ /// @param Kind The kind of memory accessed.
+ /// @param Subscripts Subscript expressions
+ /// @param Sizes Dimension lengths of the accessed array.
+ MemoryAccess(ScopStmt *Stmt, Instruction *AccessInst, AccessType AccType,
+ Value *BaseAddress, Type *ElemType, bool Affine,
+ ArrayRef<const SCEV *> Subscripts, ArrayRef<const SCEV *> Sizes,
+ Value *AccessValue, MemoryKind Kind);
+
+ /// Create a new MemoryAccess that corresponds to @p AccRel.
+ ///
+ /// Along with @p Stmt and @p AccType it uses information about dimension
+ /// lengths of the accessed array, the type of the accessed array elements,
+ /// the name of the accessed array that is derived from the object accessible
+ /// via @p AccRel.
+ ///
+ /// @param Stmt The parent statement.
+ /// @param AccType Whether read or write access.
+ /// @param AccRel The access relation that describes the memory access.
+ MemoryAccess(ScopStmt *Stmt, AccessType AccType, isl::map AccRel);
+
+ MemoryAccess(const MemoryAccess &) = delete;
+ MemoryAccess &operator=(const MemoryAccess &) = delete;
+ ~MemoryAccess();
+
+ /// Add a new incoming block/value pairs for this PHI/ExitPHI access.
+ ///
+ /// @param IncomingBlock The PHI's incoming block.
+ /// @param IncomingValue The value when reaching the PHI from the @p
+ /// IncomingBlock.
+ void addIncoming(BasicBlock *IncomingBlock, Value *IncomingValue) {
+ assert(!isRead());
+ assert(isAnyPHIKind());
+ Incoming.emplace_back(std::make_pair(IncomingBlock, IncomingValue));
+ }
+
+ /// Return the list of possible PHI/ExitPHI values.
+ ///
+ /// After code generation moves some PHIs around during region simplification,
+ /// we cannot reliably locate the original PHI node and its incoming values
+ /// anymore. For this reason we remember these explicitly for all PHI-kind
+ /// accesses.
+ ArrayRef<std::pair<BasicBlock *, Value *>> getIncoming() const {
+ assert(isAnyPHIKind());
+ return Incoming;
+ }
+
+ /// Get the type of a memory access.
+ enum AccessType getType() { return AccType; }
+
+ /// Is this a reduction like access?
+ bool isReductionLike() const { return RedType != RT_NONE; }
+
+ /// Is this a read memory access?
+ bool isRead() const { return AccType == MemoryAccess::READ; }
+
+ /// Is this a must-write memory access?
+ bool isMustWrite() const { return AccType == MemoryAccess::MUST_WRITE; }
+
+ /// Is this a may-write memory access?
+ bool isMayWrite() const { return AccType == MemoryAccess::MAY_WRITE; }
+
+ /// Is this a write memory access?
+ bool isWrite() const { return isMustWrite() || isMayWrite(); }
+
+ /// Is this a memory intrinsic access (memcpy, memset, memmove)?
+ bool isMemoryIntrinsic() const {
+ return isa<MemIntrinsic>(getAccessInstruction());
+ }
+
+ /// Check if a new access relation was imported or set by a pass.
+ bool hasNewAccessRelation() const { return !NewAccessRelation.is_null(); }
+
+ /// Return the newest access relation of this access.
+ ///
+ /// There are two possibilities:
+ /// 1) The original access relation read from the LLVM-IR.
+ /// 2) A new access relation imported from a json file or set by another
+ /// pass (e.g., for privatization).
+ ///
+ /// As 2) is by construction "newer" than 1) we return the new access
+ /// relation if present.
+ ///
+ isl::map getLatestAccessRelation() const {
+ return hasNewAccessRelation() ? getNewAccessRelation()
+ : getOriginalAccessRelation();
+ }
+
+ /// Old name of getLatestAccessRelation().
+ isl::map getAccessRelation() const { return getLatestAccessRelation(); }
+
+ /// Get an isl map describing the memory address accessed.
+ ///
+ /// In most cases the memory address accessed is well described by the access
+ /// relation obtained with getAccessRelation. However, in case of arrays
+ /// accessed with types of different size the access relation maps one access
+ /// to multiple smaller address locations. This method returns an isl map that
+ /// relates each dynamic statement instance to the unique memory location
+ /// that is loaded from / stored to.
+ ///
+ /// For an access relation { S[i] -> A[o] : 4i <= o <= 4i + 3 } this method
+ /// will return the address function { S[i] -> A[4i] }.
+ ///
+ /// @returns The address function for this memory access.
+ isl::map getAddressFunction() const;
+
+ /// Return the access relation after the schedule was applied.
+ isl::pw_multi_aff
+ applyScheduleToAccessRelation(isl::union_map Schedule) const;
+
+ /// Get an isl string representing the access function read from IR.
+ std::string getOriginalAccessRelationStr() const;
+
+ /// Get an isl string representing a new access function, if available.
+ std::string getNewAccessRelationStr() const;
+
+ /// Get an isl string representing the latest access relation.
+ std::string getAccessRelationStr() const;
+
+ /// Get the original base address of this access (e.g. A for A[i+j]) when
+ /// detected.
+ ///
+ /// This address may differ from the base address referenced by the original
+ /// ScopArrayInfo to which this array belongs, as this memory access may
+ /// have been canonicalized to a ScopArrayInfo which has a different but
+ /// identically-valued base pointer in case invariant load hoisting is
+ /// enabled.
+ Value *getOriginalBaseAddr() const { return BaseAddr; }
+
+ /// Get the detection-time base array isl::id for this access.
+ isl::id getOriginalArrayId() const;
+
+ /// Get the base array isl::id for this access, modifiable through
+ /// setNewAccessRelation().
+ isl::id getLatestArrayId() const;
+
+ /// Old name of getOriginalArrayId().
+ isl::id getArrayId() const { return getOriginalArrayId(); }
+
+ /// Get the detection-time ScopArrayInfo object for the base address.
+ const ScopArrayInfo *getOriginalScopArrayInfo() const;
+
+ /// Get the ScopArrayInfo object for the base address, or the one set
+ /// by setNewAccessRelation().
+ const ScopArrayInfo *getLatestScopArrayInfo() const;
+
+ /// Legacy name of getOriginalScopArrayInfo().
+ const ScopArrayInfo *getScopArrayInfo() const {
+ return getOriginalScopArrayInfo();
+ }
+
+ /// Return a string representation of the access's reduction type.
+ const std::string getReductionOperatorStr() const;
+
+ /// Return a string representation of the reduction type @p RT.
+ static const std::string getReductionOperatorStr(ReductionType RT);
+
+ /// Return the element type of the accessed array wrt. this access.
+ Type *getElementType() const { return ElementType; }
+
+ /// Return the access value of this memory access.
+ Value *getAccessValue() const { return AccessValue; }
+
+ /// Return llvm::Value that is stored by this access, if available.
+ ///
+ /// PHI nodes may not have a unique value available that is stored, as in
+ /// case of region statements one out of possibly several llvm::Values
+ /// might be stored. In this case nullptr is returned.
+ Value *tryGetValueStored() {
+ assert(isWrite() && "Only write statement store values");
+ if (isAnyPHIKind()) {
+ if (Incoming.size() == 1)
+ return Incoming[0].second;
+ return nullptr;
+ }
+ return AccessValue;
+ }
+
+ /// Return the access instruction of this memory access.
+ Instruction *getAccessInstruction() const { return AccessInstruction; }
+
+ /// Return an iterator range containing the subscripts.
+ iterator_range<SubscriptsTy::const_iterator> subscripts() const {
+ return make_range(Subscripts.begin(), Subscripts.end());
+ }
+
+ /// Return the number of access function subscript.
+ unsigned getNumSubscripts() const { return Subscripts.size(); }
+
+ /// Return the access function subscript in the dimension @p Dim.
+ const SCEV *getSubscript(unsigned Dim) const { return Subscripts[Dim]; }
+
+ /// Compute the isl representation for the SCEV @p E wrt. this access.
+ ///
+ /// Note that this function will also adjust the invalid context accordingly.
+ isl::pw_aff getPwAff(const SCEV *E);
+
+ /// Get the invalid domain for this access.
+ isl::set getInvalidDomain() const { return InvalidDomain; }
+
+ /// Get the invalid context for this access.
+ isl::set getInvalidContext() const { return getInvalidDomain().params(); }
+
+ /// Get the stride of this memory access in the specified Schedule. Schedule
+ /// is a map from the statement to a schedule where the innermost dimension is
+ /// the dimension of the innermost loop containing the statement.
+ isl::set getStride(isl::map Schedule) const;
+
+ /// Is the stride of the access equal to a certain width? Schedule is a map
+ /// from the statement to a schedule where the innermost dimension is the
+ /// dimension of the innermost loop containing the statement.
+ bool isStrideX(isl::map Schedule, int StrideWidth) const;
+
+ /// Is consecutive memory accessed for a given statement instance set?
+ /// Schedule is a map from the statement to a schedule where the innermost
+ /// dimension is the dimension of the innermost loop containing the
+ /// statement.
+ bool isStrideOne(isl::map Schedule) const;
+
+ /// Is always the same memory accessed for a given statement instance set?
+ /// Schedule is a map from the statement to a schedule where the innermost
+ /// dimension is the dimension of the innermost loop containing the
+ /// statement.
+ bool isStrideZero(isl::map Schedule) const;
+
+ /// Return the kind when this access was first detected.
+ MemoryKind getOriginalKind() const {
+ assert(!getOriginalScopArrayInfo() /* not yet initialized */ ||
+ getOriginalScopArrayInfo()->getKind() == Kind);
+ return Kind;
+ }
+
+ /// Return the kind considering a potential setNewAccessRelation.
+ MemoryKind getLatestKind() const {
+ return getLatestScopArrayInfo()->getKind();
+ }
+
+ /// Whether this is an access of an explicit load or store in the IR.
+ bool isOriginalArrayKind() const {
+ return getOriginalKind() == MemoryKind::Array;
+ }
+
+ /// Whether storage memory is either an custom .s2a/.phiops alloca
+ /// (false) or an existing pointer into an array (true).
+ bool isLatestArrayKind() const {
+ return getLatestKind() == MemoryKind::Array;
+ }
+
+ /// Old name of isOriginalArrayKind.
+ bool isArrayKind() const { return isOriginalArrayKind(); }
+
+ /// Whether this access is an array to a scalar memory object, without
+ /// considering changes by setNewAccessRelation.
+ ///
+ /// Scalar accesses are accesses to MemoryKind::Value, MemoryKind::PHI or
+ /// MemoryKind::ExitPHI.
+ bool isOriginalScalarKind() const {
+ return getOriginalKind() != MemoryKind::Array;
+ }
+
+ /// Whether this access is an array to a scalar memory object, also
+ /// considering changes by setNewAccessRelation.
+ bool isLatestScalarKind() const {
+ return getLatestKind() != MemoryKind::Array;
+ }
+
+ /// Old name of isOriginalScalarKind.
+ bool isScalarKind() const { return isOriginalScalarKind(); }
+
+ /// Was this MemoryAccess detected as a scalar dependences?
+ bool isOriginalValueKind() const {
+ return getOriginalKind() == MemoryKind::Value;
+ }
+
+ /// Is this MemoryAccess currently modeling scalar dependences?
+ bool isLatestValueKind() const {
+ return getLatestKind() == MemoryKind::Value;
+ }
+
+ /// Old name of isOriginalValueKind().
+ bool isValueKind() const { return isOriginalValueKind(); }
+
+ /// Was this MemoryAccess detected as a special PHI node access?
+ bool isOriginalPHIKind() const {
+ return getOriginalKind() == MemoryKind::PHI;
+ }
+
+ /// Is this MemoryAccess modeling special PHI node accesses, also
+ /// considering a potential change by setNewAccessRelation?
+ bool isLatestPHIKind() const { return getLatestKind() == MemoryKind::PHI; }
+
+ /// Old name of isOriginalPHIKind.
+ bool isPHIKind() const { return isOriginalPHIKind(); }
+
+ /// Was this MemoryAccess detected as the accesses of a PHI node in the
+ /// SCoP's exit block?
+ bool isOriginalExitPHIKind() const {
+ return getOriginalKind() == MemoryKind::ExitPHI;
+ }
+
+ /// Is this MemoryAccess modeling the accesses of a PHI node in the
+ /// SCoP's exit block? Can be changed to an array access using
+ /// setNewAccessRelation().
+ bool isLatestExitPHIKind() const {
+ return getLatestKind() == MemoryKind::ExitPHI;
+ }
+
+ /// Old name of isOriginalExitPHIKind().
+ bool isExitPHIKind() const { return isOriginalExitPHIKind(); }
+
+ /// Was this access detected as one of the two PHI types?
+ bool isOriginalAnyPHIKind() const {
+ return isOriginalPHIKind() || isOriginalExitPHIKind();
+ }
+
+ /// Does this access originate from one of the two PHI types? Can be
+ /// changed to an array access using setNewAccessRelation().
+ bool isLatestAnyPHIKind() const {
+ return isLatestPHIKind() || isLatestExitPHIKind();
+ }
+
+ /// Old name of isOriginalAnyPHIKind().
+ bool isAnyPHIKind() const { return isOriginalAnyPHIKind(); }
+
+ /// Get the statement that contains this memory access.
+ ScopStmt *getStatement() const { return Statement; }
+
+ /// Get the reduction type of this access
+ ReductionType getReductionType() const { return RedType; }
+
+ /// Update the original access relation.
+ ///
+ /// We need to update the original access relation during scop construction,
+ /// when unifying the memory accesses that access the same scop array info
+ /// object. After the scop has been constructed, the original access relation
+ /// should not be changed any more. Instead setNewAccessRelation should
+ /// be called.
+ void setAccessRelation(isl::map AccessRelation);
+
+ /// Set the updated access relation read from JSCOP file.
+ void setNewAccessRelation(isl::map NewAccessRelation);
+
+ /// Return whether the MemoryyAccess is a partial access. That is, the access
+ /// is not executed in some instances of the parent statement's domain.
+ bool isLatestPartialAccess() const;
+
+ /// Mark this a reduction like access
+ void markAsReductionLike(ReductionType RT) { RedType = RT; }
+
+ /// Align the parameters in the access relation to the scop context
+ void realignParams();
+
+ /// Update the dimensionality of the memory access.
+ ///
+ /// During scop construction some memory accesses may not be constructed with
+ /// their full dimensionality, but outer dimensions may have been omitted if
+ /// they took the value 'zero'. By updating the dimensionality of the
+ /// statement we add additional zero-valued dimensions to match the
+ /// dimensionality of the ScopArrayInfo object that belongs to this memory
+ /// access.
+ void updateDimensionality();
+
+ /// Get identifier for the memory access.
+ ///
+ /// This identifier is unique for all accesses that belong to the same scop
+ /// statement.
+ isl::id getId() const;
+
+ /// Print the MemoryAccess.
+ ///
+ /// @param OS The output stream the MemoryAccess is printed to.
+ void print(raw_ostream &OS) const;
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ /// Print the MemoryAccess to stderr.
+ void dump() const;
+#endif
+
+ /// Is the memory access affine?
+ bool isAffine() const { return IsAffine; }
+};
+
+raw_ostream &operator<<(raw_ostream &OS, MemoryAccess::ReductionType RT);
+
+/// Ordered list type to hold accesses.
+using MemoryAccessList = std::forward_list<MemoryAccess *>;
+
+/// Helper structure for invariant memory accesses.
+struct InvariantAccess {
+ /// The memory access that is (partially) invariant.
+ MemoryAccess *MA;
+
+ /// The context under which the access is not invariant.
+ isl::set NonHoistableCtx;
+};
+
+/// Ordered container type to hold invariant accesses.
+using InvariantAccessesTy = SmallVector<InvariantAccess, 8>;
+
+/// Type for equivalent invariant accesses and their domain context.
+struct InvariantEquivClassTy {
+ /// The pointer that identifies this equivalence class
+ const SCEV *IdentifyingPointer;
+
+ /// Memory accesses now treated invariant
+ ///
+ /// These memory accesses access the pointer location that identifies
+ /// this equivalence class. They are treated as invariant and hoisted during
+ /// code generation.
+ MemoryAccessList InvariantAccesses;
+
+ /// The execution context under which the memory location is accessed
+ ///
+ /// It is the union of the execution domains of the memory accesses in the
+ /// InvariantAccesses list.
+ isl::set ExecutionContext;
+
+ /// The type of the invariant access
+ ///
+ /// It is used to differentiate between differently typed invariant loads from
+ /// the same location.
+ Type *AccessType;
+};
+
+/// Type for invariant accesses equivalence classes.
+using InvariantEquivClassesTy = SmallVector<InvariantEquivClassTy, 8>;
+
+/// Statement of the Scop
+///
+/// A Scop statement represents an instruction in the Scop.
+///
+/// It is further described by its iteration domain, its schedule and its data
+/// accesses.
+/// At the moment every statement represents a single basic block of LLVM-IR.
+class ScopStmt {
+ friend class ScopBuilder;
+
+public:
+ /// Create the ScopStmt from a BasicBlock.
+ ScopStmt(Scop &parent, BasicBlock &bb, StringRef Name, Loop *SurroundingLoop,
+ std::vector<Instruction *> Instructions);
+
+ /// Create an overapproximating ScopStmt for the region @p R.
+ ///
+ /// @param EntryBlockInstructions The list of instructions that belong to the
+ /// entry block of the region statement.
+ /// Instructions are only tracked for entry
+ /// blocks for now. We currently do not allow
+ /// to modify the instructions of blocks later
+ /// in the region statement.
+ ScopStmt(Scop &parent, Region &R, StringRef Name, Loop *SurroundingLoop,
+ std::vector<Instruction *> EntryBlockInstructions);
+
+ /// Create a copy statement.
+ ///
+ /// @param Stmt The parent statement.
+ /// @param SourceRel The source location.
+ /// @param TargetRel The target location.
+ /// @param Domain The original domain under which the copy statement would
+ /// be executed.
+ ScopStmt(Scop &parent, isl::map SourceRel, isl::map TargetRel,
+ isl::set Domain);
+
+ ScopStmt(const ScopStmt &) = delete;
+ const ScopStmt &operator=(const ScopStmt &) = delete;
+ ~ScopStmt();
+
+private:
+ /// Polyhedral description
+ //@{
+
+ /// The Scop containing this ScopStmt.
+ Scop &Parent;
+
+ /// The domain under which this statement is not modeled precisely.
+ ///
+ /// The invalid domain for a statement describes all parameter combinations
+ /// under which the statement looks to be executed but is in fact not because
+ /// some assumption/restriction makes the statement/scop invalid.
+ isl::set InvalidDomain;
+
+ /// The iteration domain describes the set of iterations for which this
+ /// statement is executed.
+ ///
+ /// Example:
+ /// for (i = 0; i < 100 + b; ++i)
+ /// for (j = 0; j < i; ++j)
+ /// S(i,j);
+ ///
+ /// 'S' is executed for different values of i and j. A vector of all
+ /// induction variables around S (i, j) is called iteration vector.
+ /// The domain describes the set of possible iteration vectors.
+ ///
+ /// In this case it is:
+ ///
+ /// Domain: 0 <= i <= 100 + b
+ /// 0 <= j <= i
+ ///
+ /// A pair of statement and iteration vector (S, (5,3)) is called statement
+ /// instance.
+ isl::set Domain;
+
+ /// The memory accesses of this statement.
+ ///
+ /// The only side effects of a statement are its memory accesses.
+ using MemoryAccessVec = llvm::SmallVector<MemoryAccess *, 8>;
+ MemoryAccessVec MemAccs;
+
+ /// Mapping from instructions to (scalar) memory accesses.
+ DenseMap<const Instruction *, MemoryAccessList> InstructionToAccess;
+
+ /// The set of values defined elsewhere required in this ScopStmt and
+ /// their MemoryKind::Value READ MemoryAccesses.
+ DenseMap<Value *, MemoryAccess *> ValueReads;
+
+ /// The set of values defined in this ScopStmt that are required
+ /// elsewhere, mapped to their MemoryKind::Value WRITE MemoryAccesses.
+ DenseMap<Instruction *, MemoryAccess *> ValueWrites;
+
+ /// Map from PHI nodes to its incoming value when coming from this
+ /// statement.
+ ///
+ /// Non-affine subregions can have multiple exiting blocks that are incoming
+ /// blocks of the PHI nodes. This map ensures that there is only one write
+ /// operation for the complete subregion. A PHI selecting the relevant value
+ /// will be inserted.
+ DenseMap<PHINode *, MemoryAccess *> PHIWrites;
+
+ /// Map from PHI nodes to its read access in this statement.
+ DenseMap<PHINode *, MemoryAccess *> PHIReads;
+
+ //@}
+
+ /// A SCoP statement represents either a basic block (affine/precise case) or
+ /// a whole region (non-affine case).
+ ///
+ /// Only one of the following two members will therefore be set and indicate
+ /// which kind of statement this is.
+ ///
+ ///{
+
+ /// The BasicBlock represented by this statement (in the affine case).
+ BasicBlock *BB = nullptr;
+
+ /// The region represented by this statement (in the non-affine case).
+ Region *R = nullptr;
+
+ ///}
+
+ /// The isl AST build for the new generated AST.
+ isl::ast_build Build;
+
+ SmallVector<Loop *, 4> NestLoops;
+
+ std::string BaseName;
+
+ /// The closest loop that contains this statement.
+ Loop *SurroundingLoop;
+
+ /// Vector for Instructions in this statement.
+ std::vector<Instruction *> Instructions;
+
+ /// Remove @p MA from dictionaries pointing to them.
+ void removeAccessData(MemoryAccess *MA);
+
+public:
+ /// Get an isl_ctx pointer.
+ isl::ctx getIslCtx() const;
+
+ /// Get the iteration domain of this ScopStmt.
+ ///
+ /// @return The iteration domain of this ScopStmt.
+ isl::set getDomain() const;
+
+ /// Get the space of the iteration domain
+ ///
+ /// @return The space of the iteration domain
+ isl::space getDomainSpace() const;
+
+ /// Get the id of the iteration domain space
+ ///
+ /// @return The id of the iteration domain space
+ isl::id getDomainId() const;
+
+ /// Get an isl string representing this domain.
+ std::string getDomainStr() const;
+
+ /// Get the schedule function of this ScopStmt.
+ ///
+ /// @return The schedule function of this ScopStmt, if it does not contain
+ /// extension nodes, and nullptr, otherwise.
+ isl::map getSchedule() const;
+
+ /// Get an isl string representing this schedule.
+ ///
+ /// @return An isl string representing this schedule, if it does not contain
+ /// extension nodes, and an empty string, otherwise.
+ std::string getScheduleStr() const;
+
+ /// Get the invalid domain for this statement.
+ isl::set getInvalidDomain() const { return InvalidDomain; }
+
+ /// Get the invalid context for this statement.
+ isl::set getInvalidContext() const { return getInvalidDomain().params(); }
+
+ /// Set the invalid context for this statement to @p ID.
+ void setInvalidDomain(isl::set ID);
+
+ /// Get the BasicBlock represented by this ScopStmt (if any).
+ ///
+ /// @return The BasicBlock represented by this ScopStmt, or null if the
+ /// statement represents a region.
+ BasicBlock *getBasicBlock() const { return BB; }
+
+ /// Return true if this statement represents a single basic block.
+ bool isBlockStmt() const { return BB != nullptr; }
+
+ /// Return true if this is a copy statement.
+ bool isCopyStmt() const { return BB == nullptr && R == nullptr; }
+
+ /// Get the region represented by this ScopStmt (if any).
+ ///
+ /// @return The region represented by this ScopStmt, or null if the statement
+ /// represents a basic block.
+ Region *getRegion() const { return R; }
+
+ /// Return true if this statement represents a whole region.
+ bool isRegionStmt() const { return R != nullptr; }
+
+ /// Return a BasicBlock from this statement.
+ ///
+ /// For block statements, it returns the BasicBlock itself. For subregion
+ /// statements, return its entry block.
+ BasicBlock *getEntryBlock() const;
+
+ /// Return whether @p L is boxed within this statement.
+ bool contains(const Loop *L) const {
+ // Block statements never contain loops.
+ if (isBlockStmt())
+ return false;
+
+ return getRegion()->contains(L);
+ }
+
+ /// Return whether this statement represents @p BB.
+ bool represents(BasicBlock *BB) const {
+ if (isCopyStmt())
+ return false;
+ if (isBlockStmt())
+ return BB == getBasicBlock();
+ return getRegion()->contains(BB);
+ }
+
+ /// Return whether this statement contains @p Inst.
+ bool contains(Instruction *Inst) const {
+ if (!Inst)
+ return false;
+ if (isBlockStmt())
+ return std::find(Instructions.begin(), Instructions.end(), Inst) !=
+ Instructions.end();
+ return represents(Inst->getParent());
+ }
+
+ /// Return the closest innermost loop that contains this statement, but is not
+ /// contained in it.
+ ///
+ /// For block statement, this is just the loop that contains the block. Region
+ /// statements can contain boxed loops, so getting the loop of one of the
+ /// region's BBs might return such an inner loop. For instance, the region's
+ /// entry could be a header of a loop, but the region might extend to BBs
+ /// after the loop exit. Similarly, the region might only contain parts of the
+ /// loop body and still include the loop header.
+ ///
+ /// Most of the time the surrounding loop is the top element of #NestLoops,
+ /// except when it is empty. In that case it return the loop that the whole
+ /// SCoP is contained in. That can be nullptr if there is no such loop.
+ Loop *getSurroundingLoop() const {
+ assert(!isCopyStmt() &&
+ "No surrounding loop for artificially created statements");
+ return SurroundingLoop;
+ }
+
+ /// Return true if this statement does not contain any accesses.
+ bool isEmpty() const { return MemAccs.empty(); }
+
+ /// Find all array accesses for @p Inst.
+ ///
+ /// @param Inst The instruction accessing an array.
+ ///
+ /// @return A list of array accesses (MemoryKind::Array) accessed by @p Inst.
+ /// If there is no such access, it returns nullptr.
+ const MemoryAccessList *
+ lookupArrayAccessesFor(const Instruction *Inst) const {
+ auto It = InstructionToAccess.find(Inst);
+ if (It == InstructionToAccess.end())
+ return nullptr;
+ if (It->second.empty())
+ return nullptr;
+ return &It->second;
+ }
+
+ /// Return the only array access for @p Inst, if existing.
+ ///
+ /// @param Inst The instruction for which to look up the access.
+ /// @returns The unique array memory access related to Inst or nullptr if
+ /// no array access exists
+ MemoryAccess *getArrayAccessOrNULLFor(const Instruction *Inst) const {
+ auto It = InstructionToAccess.find(Inst);
+ if (It == InstructionToAccess.end())
+ return nullptr;
+
+ MemoryAccess *ArrayAccess = nullptr;
+
+ for (auto Access : It->getSecond()) {
+ if (!Access->isArrayKind())
+ continue;
+
+ assert(!ArrayAccess && "More then one array access for instruction");
+
+ ArrayAccess = Access;
+ }
+
+ return ArrayAccess;
+ }
+
+ /// Return the only array access for @p Inst.
+ ///
+ /// @param Inst The instruction for which to look up the access.
+ /// @returns The unique array memory access related to Inst.
+ MemoryAccess &getArrayAccessFor(const Instruction *Inst) const {
+ MemoryAccess *ArrayAccess = getArrayAccessOrNULLFor(Inst);
+
+ assert(ArrayAccess && "No array access found for instruction!");
+ return *ArrayAccess;
+ }
+
+ /// Return the MemoryAccess that writes the value of an instruction
+ /// defined in this statement, or nullptr if not existing, respectively
+ /// not yet added.
+ MemoryAccess *lookupValueWriteOf(Instruction *Inst) const {
+ assert((isRegionStmt() && R->contains(Inst)) ||
+ (!isRegionStmt() && Inst->getParent() == BB));
+ return ValueWrites.lookup(Inst);
+ }
+
+ /// Return the MemoryAccess that reloads a value, or nullptr if not
+ /// existing, respectively not yet added.
+ MemoryAccess *lookupValueReadOf(Value *Inst) const {
+ return ValueReads.lookup(Inst);
+ }
+
+ /// Return the MemoryAccess that loads a PHINode value, or nullptr if not
+ /// existing, respectively not yet added.
+ MemoryAccess *lookupPHIReadOf(PHINode *PHI) const {
+ return PHIReads.lookup(PHI);
+ }
+
+ /// Return the PHI write MemoryAccess for the incoming values from any
+ /// basic block in this ScopStmt, or nullptr if not existing,
+ /// respectively not yet added.
+ MemoryAccess *lookupPHIWriteOf(PHINode *PHI) const {
+ assert(isBlockStmt() || R->getExit() == PHI->getParent());
+ return PHIWrites.lookup(PHI);
+ }
+
+ /// Return the input access of the value, or null if no such MemoryAccess
+ /// exists.
+ ///
+ /// The input access is the MemoryAccess that makes an inter-statement value
+ /// available in this statement by reading it at the start of this statement.
+ /// This can be a MemoryKind::Value if defined in another statement or a
+ /// MemoryKind::PHI if the value is a PHINode in this statement.
+ MemoryAccess *lookupInputAccessOf(Value *Val) const {
+ if (isa<PHINode>(Val))
+ if (auto InputMA = lookupPHIReadOf(cast<PHINode>(Val))) {
+ assert(!lookupValueReadOf(Val) && "input accesses must be unique; a "
+ "statement cannot read a .s2a and "
+ ".phiops simultaneously");
+ return InputMA;
+ }
+
+ if (auto *InputMA = lookupValueReadOf(Val))
+ return InputMA;
+
+ return nullptr;
+ }
+
+ /// Add @p Access to this statement's list of accesses.
+ ///
+ /// @param Access The access to add.
+ /// @param Prepend If true, will add @p Access before all other instructions
+ /// (instead of appending it).
+ void addAccess(MemoryAccess *Access, bool Preprend = false);
+
+ /// Remove a MemoryAccess from this statement.
+ ///
+ /// Note that scalar accesses that are caused by MA will
+ /// be eliminated too.
+ void removeMemoryAccess(MemoryAccess *MA);
+
+ /// Remove @p MA from this statement.
+ ///
+ /// In contrast to removeMemoryAccess(), no other access will be eliminated.
+ ///
+ /// @param MA The MemoryAccess to be removed.
+ /// @param AfterHoisting If true, also remove from data access lists.
+ /// These lists are filled during
+ /// ScopBuilder::buildAccessRelations. Therefore, if this
+ /// method is called before buildAccessRelations, false
+ /// must be passed.
+ void removeSingleMemoryAccess(MemoryAccess *MA, bool AfterHoisting = true);
+
+ using iterator = MemoryAccessVec::iterator;
+ using const_iterator = MemoryAccessVec::const_iterator;
+
+ iterator begin() { return MemAccs.begin(); }
+ iterator end() { return MemAccs.end(); }
+ const_iterator begin() const { return MemAccs.begin(); }
+ const_iterator end() const { return MemAccs.end(); }
+ size_t size() const { return MemAccs.size(); }
+
+ unsigned getNumIterators() const;
+
+ Scop *getParent() { return &Parent; }
+ const Scop *getParent() const { return &Parent; }
+
+ const std::vector<Instruction *> &getInstructions() const {
+ return Instructions;
+ }
+
+ /// Set the list of instructions for this statement. It replaces the current
+ /// list.
+ void setInstructions(ArrayRef<Instruction *> Range) {
+ Instructions.assign(Range.begin(), Range.end());
+ }
+
+ std::vector<Instruction *>::const_iterator insts_begin() const {
+ return Instructions.begin();
+ }
+
+ std::vector<Instruction *>::const_iterator insts_end() const {
+ return Instructions.end();
+ }
+
+ /// The range of instructions in this statement.
+ iterator_range<std::vector<Instruction *>::const_iterator> insts() const {
+ return {insts_begin(), insts_end()};
+ }
+
+ /// Insert an instruction before all other instructions in this statement.
+ void prependInstruction(Instruction *Inst) {
+ Instructions.insert(Instructions.begin(), Inst);
+ }
+
+ const char *getBaseName() const;
+
+ /// Set the isl AST build.
+ void setAstBuild(isl::ast_build B) { Build = B; }
+
+ /// Get the isl AST build.
+ isl::ast_build getAstBuild() const { return Build; }
+
+ /// Restrict the domain of the statement.
+ ///
+ /// @param NewDomain The new statement domain.
+ void restrictDomain(isl::set NewDomain);
+
+ /// Get the loop for a dimension.
+ ///
+ /// @param Dimension The dimension of the induction variable
+ /// @return The loop at a certain dimension.
+ Loop *getLoopForDimension(unsigned Dimension) const;
+
+ /// Align the parameters in the statement to the scop context
+ void realignParams();
+
+ /// Print the ScopStmt.
+ ///
+ /// @param OS The output stream the ScopStmt is printed to.
+ /// @param PrintInstructions Whether to print the statement's instructions as
+ /// well.
+ void print(raw_ostream &OS, bool PrintInstructions) const;
+
+ /// Print the instructions in ScopStmt.
+ ///
+ void printInstructions(raw_ostream &OS) const;
+
+ /// Check whether there is a value read access for @p V in this statement, and
+ /// if not, create one.
+ ///
+ /// This allows to add MemoryAccesses after the initial creation of the Scop
+ /// by ScopBuilder.
+ ///
+ /// @return The already existing or newly created MemoryKind::Value READ
+ /// MemoryAccess.
+ ///
+ /// @see ScopBuilder::ensureValueRead(Value*,ScopStmt*)
+ MemoryAccess *ensureValueRead(Value *V);
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ /// Print the ScopStmt to stderr.
+ void dump() const;
+#endif
+};
+
+/// Print ScopStmt S to raw_ostream OS.
+raw_ostream &operator<<(raw_ostream &OS, const ScopStmt &S);
+
+/// Build the conditions sets for the branch condition @p Condition in
+/// the @p Domain.
+///
+/// This will fill @p ConditionSets with the conditions under which control
+/// will be moved from @p TI to its successors. Hence, @p ConditionSets will
+/// have as many elements as @p TI has successors. If @p TI is nullptr the
+/// context under which @p Condition is true/false will be returned as the
+/// new elements of @p ConditionSets.
+bool buildConditionSets(Scop &S, BasicBlock *BB, Value *Condition,
+ Instruction *TI, Loop *L, __isl_keep isl_set *Domain,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ SmallVectorImpl<__isl_give isl_set *> &ConditionSets);
+
+/// Build condition sets for unsigned ICmpInst(s).
+/// Special handling is required for unsigned operands to ensure that if
+/// MSB (aka the Sign bit) is set for an operands in an unsigned ICmpInst
+/// it should wrap around.
+///
+/// @param IsStrictUpperBound holds information on the predicate relation
+/// between TestVal and UpperBound, i.e,
+/// TestVal < UpperBound OR TestVal <= UpperBound
+__isl_give isl_set *
+buildUnsignedConditionSets(Scop &S, BasicBlock *BB, Value *Condition,
+ __isl_keep isl_set *Domain, const SCEV *SCEV_TestVal,
+ const SCEV *SCEV_UpperBound,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ bool IsStrictUpperBound);
+
+/// Build the conditions sets for the terminator @p TI in the @p Domain.
+///
+/// This will fill @p ConditionSets with the conditions under which control
+/// will be moved from @p TI to its successors. Hence, @p ConditionSets will
+/// have as many elements as @p TI has successors.
+bool buildConditionSets(Scop &S, BasicBlock *BB, Instruction *TI, Loop *L,
+ __isl_keep isl_set *Domain,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ SmallVectorImpl<__isl_give isl_set *> &ConditionSets);
+
+/// Static Control Part
+///
+/// A Scop is the polyhedral representation of a control flow region detected
+/// by the Scop detection. It is generated by translating the LLVM-IR and
+/// abstracting its effects.
+///
+/// A Scop consists of a set of:
+///
+/// * A set of statements executed in the Scop.
+///
+/// * A set of global parameters
+/// Those parameters are scalar integer values, which are constant during
+/// execution.
+///
+/// * A context
+/// This context contains information about the values the parameters
+/// can take and relations between different parameters.
+class Scop {
+public:
+ /// Type to represent a pair of minimal/maximal access to an array.
+ using MinMaxAccessTy = std::pair<isl::pw_multi_aff, isl::pw_multi_aff>;
+
+ /// Vector of minimal/maximal accesses to different arrays.
+ using MinMaxVectorTy = SmallVector<MinMaxAccessTy, 4>;
+
+ /// Pair of minimal/maximal access vectors representing
+ /// read write and read only accesses
+ using MinMaxVectorPairTy = std::pair<MinMaxVectorTy, MinMaxVectorTy>;
+
+ /// Vector of pair of minimal/maximal access vectors representing
+ /// non read only and read only accesses for each alias group.
+ using MinMaxVectorPairVectorTy = SmallVector<MinMaxVectorPairTy, 4>;
+
+private:
+ friend class ScopBuilder;
+
+ /// Isl context.
+ ///
+ /// We need a shared_ptr with reference counter to delete the context when all
+ /// isl objects are deleted. We will distribute the shared_ptr to all objects
+ /// that use the context to create isl objects, and increase the reference
+ /// counter. By doing this, we guarantee that the context is deleted when we
+ /// delete the last object that creates isl objects with the context. This
+ /// declaration needs to be the first in class to gracefully destroy all isl
+ /// objects before the context.
+ std::shared_ptr<isl_ctx> IslCtx;
+
+ ScalarEvolution *SE;
+ DominatorTree *DT;
+
+ /// The underlying Region.
+ Region &R;
+
+ /// The name of the SCoP (identical to the regions name)
+ Optional<std::string> name;
+
+ // Access functions of the SCoP.
+ //
+ // This owns all the MemoryAccess objects of the Scop created in this pass.
+ AccFuncVector AccessFunctions;
+
+ /// Flag to indicate that the scheduler actually optimized the SCoP.
+ bool IsOptimized = false;
+
+ /// True if the underlying region has a single exiting block.
+ bool HasSingleExitEdge;
+
+ /// Flag to remember if the SCoP contained an error block or not.
+ bool HasErrorBlock = false;
+
+ /// Max loop depth.
+ unsigned MaxLoopDepth = 0;
+
+ /// Number of copy statements.
+ unsigned CopyStmtsNum = 0;
+
+ /// Flag to indicate if the Scop is to be skipped.
+ bool SkipScop = false;
+
+ using StmtSet = std::list<ScopStmt>;
+
+ /// The statements in this Scop.
+ StmtSet Stmts;
+
+ /// Parameters of this Scop
+ ParameterSetTy Parameters;
+
+ /// Mapping from parameters to their ids.
+ DenseMap<const SCEV *, isl::id> ParameterIds;
+
+ /// The context of the SCoP created during SCoP detection.
+ ScopDetection::DetectionContext &DC;
+
+ /// OptimizationRemarkEmitter object for displaying diagnostic remarks
+ OptimizationRemarkEmitter &ORE;
+
+ /// A map from basic blocks to vector of SCoP statements. Currently this
+ /// vector comprises only of a single statement.
+ DenseMap<BasicBlock *, std::vector<ScopStmt *>> StmtMap;
+
+ /// A map from instructions to SCoP statements.
+ DenseMap<Instruction *, ScopStmt *> InstStmtMap;
+
+ /// A map from basic blocks to their domains.
+ DenseMap<BasicBlock *, isl::set> DomainMap;
+
+ /// Constraints on parameters.
+ isl::set Context;
+
+ /// The affinator used to translate SCEVs to isl expressions.
+ SCEVAffinator Affinator;
+
+ using ArrayInfoMapTy =
+ std::map<std::pair<AssertingVH<const Value>, MemoryKind>,
+ std::unique_ptr<ScopArrayInfo>>;
+
+ using ArrayNameMapTy = StringMap<std::unique_ptr<ScopArrayInfo>>;
+
+ using ArrayInfoSetTy = SetVector<ScopArrayInfo *>;
+
+ /// A map to remember ScopArrayInfo objects for all base pointers.
+ ///
+ /// As PHI nodes may have two array info objects associated, we add a flag
+ /// that distinguishes between the PHI node specific ArrayInfo object
+ /// and the normal one.
+ ArrayInfoMapTy ScopArrayInfoMap;
+
+ /// A map to remember ScopArrayInfo objects for all names of memory
+ /// references.
+ ArrayNameMapTy ScopArrayNameMap;
+
+ /// A set to remember ScopArrayInfo objects.
+ /// @see Scop::ScopArrayInfoMap
+ ArrayInfoSetTy ScopArrayInfoSet;
+
+ /// The assumptions under which this scop was built.
+ ///
+ /// When constructing a scop sometimes the exact representation of a statement
+ /// or condition would be very complex, but there is a common case which is a
+ /// lot simpler, but which is only valid under certain assumptions. The
+ /// assumed context records the assumptions taken during the construction of
+ /// this scop and that need to be code generated as a run-time test.
+ isl::set AssumedContext;
+
+ /// The restrictions under which this SCoP was built.
+ ///
+ /// The invalid context is similar to the assumed context as it contains
+ /// constraints over the parameters. However, while we need the constraints
+ /// in the assumed context to be "true" the constraints in the invalid context
+ /// need to be "false". Otherwise they behave the same.
+ isl::set InvalidContext;
+
+ /// The context under which the SCoP must have defined behavior. Optimizer and
+ /// code generator can assume that the SCoP will only be executed with
+ /// parameter values within this context. This might be either because we can
+ /// prove that other values are impossible or explicitly have undefined
+ /// behavior, such as due to no-wrap flags. If this becomes too complex, can
+ /// also be nullptr.
+ ///
+ /// In contrast to Scop::AssumedContext and Scop::InvalidContext, these do not
+ /// need to be checked at runtime.
+ ///
+ /// Scop::Context on the other side is an overapproximation and does not
+ /// include all requirements, but is always defined. However, there is still
+ /// no guarantee that there is no undefined behavior in
+ /// DefinedBehaviorContext.
+ isl::set DefinedBehaviorContext;
+
+ /// The schedule of the SCoP
+ ///
+ /// The schedule of the SCoP describes the execution order of the statements
+ /// in the scop by assigning each statement instance a possibly
+ /// multi-dimensional execution time. The schedule is stored as a tree of
+ /// schedule nodes.
+ ///
+ /// The most common nodes in a schedule tree are so-called band nodes. Band
+ /// nodes map statement instances into a multi dimensional schedule space.
+ /// This space can be seen as a multi-dimensional clock.
+ ///
+ /// Example:
+ ///
+ /// <S,(5,4)> may be mapped to (5,4) by this schedule:
+ ///
+ /// s0 = i (Year of execution)
+ /// s1 = j (Day of execution)
+ ///
+ /// or to (9, 20) by this schedule:
+ ///
+ /// s0 = i + j (Year of execution)
+ /// s1 = 20 (Day of execution)
+ ///
+ /// The order statement instances are executed is defined by the
+ /// schedule vectors they are mapped to. A statement instance
+ /// <A, (i, j, ..)> is executed before a statement instance <B, (i', ..)>, if
+ /// the schedule vector of A is lexicographic smaller than the schedule
+ /// vector of B.
+ ///
+ /// Besides band nodes, schedule trees contain additional nodes that specify
+ /// a textual ordering between two subtrees or filter nodes that filter the
+ /// set of statement instances that will be scheduled in a subtree. There
+ /// are also several other nodes. A full description of the different nodes
+ /// in a schedule tree is given in the isl manual.
+ isl::schedule Schedule;
+
+ /// Is this Scop marked as not to be transformed by an optimization heuristic?
+ bool HasDisableHeuristicsHint = false;
+
+ /// Whether the schedule has been modified after derived from the CFG by
+ /// ScopBuilder.
+ bool ScheduleModified = false;
+
+ /// The set of minimal/maximal accesses for each alias group.
+ ///
+ /// When building runtime alias checks we look at all memory instructions and
+ /// build so called alias groups. Each group contains a set of accesses to
+ /// different base arrays which might alias with each other. However, between
+ /// alias groups there is no aliasing possible.
+ ///
+ /// In a program with int and float pointers annotated with tbaa information
+ /// we would probably generate two alias groups, one for the int pointers and
+ /// one for the float pointers.
+ ///
+ /// During code generation we will create a runtime alias check for each alias
+ /// group to ensure the SCoP is executed in an alias free environment.
+ MinMaxVectorPairVectorTy MinMaxAliasGroups;
+
+ /// Mapping from invariant loads to the representing invariant load of
+ /// their equivalence class.
+ ValueToValueMap InvEquivClassVMap;
+
+ /// List of invariant accesses.
+ InvariantEquivClassesTy InvariantEquivClasses;
+
+ /// The smallest array index not yet assigned.
+ long ArrayIdx = 0;
+
+ /// The smallest statement index not yet assigned.
+ long StmtIdx = 0;
+
+ /// A number that uniquely represents a Scop within its function
+ const int ID;
+
+ /// Map of values to the MemoryAccess that writes its definition.
+ ///
+ /// There must be at most one definition per llvm::Instruction in a SCoP.
+ DenseMap<Value *, MemoryAccess *> ValueDefAccs;
+
+ /// Map of values to the MemoryAccess that reads a PHI.
+ DenseMap<PHINode *, MemoryAccess *> PHIReadAccs;
+
+ /// List of all uses (i.e. read MemoryAccesses) for a MemoryKind::Value
+ /// scalar.
+ DenseMap<const ScopArrayInfo *, SmallVector<MemoryAccess *, 4>> ValueUseAccs;
+
+ /// List of all incoming values (write MemoryAccess) of a MemoryKind::PHI or
+ /// MemoryKind::ExitPHI scalar.
+ DenseMap<const ScopArrayInfo *, SmallVector<MemoryAccess *, 4>>
+ PHIIncomingAccs;
+
+ /// Scop constructor; invoked from ScopBuilder::buildScop.
+ Scop(Region &R, ScalarEvolution &SE, LoopInfo &LI, DominatorTree &DT,
+ ScopDetection::DetectionContext &DC, OptimizationRemarkEmitter &ORE,
+ int ID);
+
+ //@}
+
+ /// Initialize this ScopBuilder.
+ void init(AAResults &AA, AssumptionCache &AC, DominatorTree &DT,
+ LoopInfo &LI);
+
+ /// Return the access for the base ptr of @p MA if any.
+ MemoryAccess *lookupBasePtrAccess(MemoryAccess *MA);
+
+ /// Create an id for @p Param and store it in the ParameterIds map.
+ void createParameterId(const SCEV *Param);
+
+ /// Build the Context of the Scop.
+ void buildContext();
+
+ /// Add the bounds of the parameters to the context.
+ void addParameterBounds();
+
+ /// Simplify the assumed and invalid context.
+ void simplifyContexts();
+
+ /// Create a new SCoP statement for @p BB.
+ ///
+ /// A new statement for @p BB will be created and added to the statement
+ /// vector
+ /// and map.
+ ///
+ /// @param BB The basic block we build the statement for.
+ /// @param Name The name of the new statement.
+ /// @param SurroundingLoop The loop the created statement is contained in.
+ /// @param Instructions The instructions in the statement.
+ void addScopStmt(BasicBlock *BB, StringRef Name, Loop *SurroundingLoop,
+ std::vector<Instruction *> Instructions);
+
+ /// Create a new SCoP statement for @p R.
+ ///
+ /// A new statement for @p R will be created and added to the statement vector
+ /// and map.
+ ///
+ /// @param R The region we build the statement for.
+ /// @param Name The name of the new statement.
+ /// @param SurroundingLoop The loop the created statement is contained
+ /// in.
+ /// @param EntryBlockInstructions The (interesting) instructions in the
+ /// entry block of the region statement.
+ void addScopStmt(Region *R, StringRef Name, Loop *SurroundingLoop,
+ std::vector<Instruction *> EntryBlockInstructions);
+
+ /// Removes @p Stmt from the StmtMap.
+ void removeFromStmtMap(ScopStmt &Stmt);
+
+ /// Removes all statements where the entry block of the statement does not
+ /// have a corresponding domain in the domain map (or it is empty).
+ void removeStmtNotInDomainMap();
+
+ /// Collect all memory access relations of a given type.
+ ///
+ /// @param Predicate A predicate function that returns true if an access is
+ /// of a given type.
+ ///
+ /// @returns The set of memory accesses in the scop that match the predicate.
+ isl::union_map
+ getAccessesOfType(std::function<bool(MemoryAccess &)> Predicate);
+
+ /// @name Helper functions for printing the Scop.
+ ///
+ //@{
+ void printContext(raw_ostream &OS) const;
+ void printArrayInfo(raw_ostream &OS) const;
+ void printStatements(raw_ostream &OS, bool PrintInstructions) const;
+ void printAliasAssumptions(raw_ostream &OS) const;
+ //@}
+
+public:
+ Scop(const Scop &) = delete;
+ Scop &operator=(const Scop &) = delete;
+ ~Scop();
+
+ /// Increment actual number of aliasing assumptions taken
+ ///
+ /// @param Step Number of new aliasing assumptions which should be added to
+ /// the number of already taken assumptions.
+ static void incrementNumberOfAliasingAssumptions(unsigned Step);
+
+ /// Get the count of copy statements added to this Scop.
+ ///
+ /// @return The count of copy statements added to this Scop.
+ unsigned getCopyStmtsNum() { return CopyStmtsNum; }
+
+ /// Create a new copy statement.
+ ///
+ /// A new statement will be created and added to the statement vector.
+ ///
+ /// @param SourceRel The source location.
+ /// @param TargetRel The target location.
+ /// @param Domain The original domain under which the copy statement would
+ /// be executed.
+ ScopStmt *addScopStmt(isl::map SourceRel, isl::map TargetRel,
+ isl::set Domain);
+
+ /// Add the access function to all MemoryAccess objects of the Scop
+ /// created in this pass.
+ void addAccessFunction(MemoryAccess *Access) {
+ AccessFunctions.emplace_back(Access);
+
+ // Register value definitions.
+ if (Access->isWrite() && Access->isOriginalValueKind()) {
+ assert(!ValueDefAccs.count(Access->getAccessValue()) &&
+ "there can be just one definition per value");
+ ValueDefAccs[Access->getAccessValue()] = Access;
+ } else if (Access->isRead() && Access->isOriginalPHIKind()) {
+ PHINode *PHI = cast<PHINode>(Access->getAccessInstruction());
+ assert(!PHIReadAccs.count(PHI) &&
+ "there can be just one PHI read per PHINode");
+ PHIReadAccs[PHI] = Access;
+ }
+ }
+
+ /// Add metadata for @p Access.
+ void addAccessData(MemoryAccess *Access);
+
+ /// Add new invariant access equivalence class
+ void
+ addInvariantEquivClass(const InvariantEquivClassTy &InvariantEquivClass) {
+ InvariantEquivClasses.emplace_back(InvariantEquivClass);
+ }
+
+ /// Add mapping from invariant loads to the representing invariant load of
+ /// their equivalence class.
+ void addInvariantLoadMapping(const Value *LoadInst, Value *ClassRep) {
+ InvEquivClassVMap[LoadInst] = ClassRep;
+ }
+
+ /// Remove the metadata stored for @p Access.
+ void removeAccessData(MemoryAccess *Access);
+
+ /// Return the scalar evolution.
+ ScalarEvolution *getSE() const;
+
+ /// Return the dominator tree.
+ DominatorTree *getDT() const { return DT; }
+
+ /// Return the LoopInfo used for this Scop.
+ LoopInfo *getLI() const { return Affinator.getLI(); }
+
+ /// Get the count of parameters used in this Scop.
+ ///
+ /// @return The count of parameters used in this Scop.
+ size_t getNumParams() const { return Parameters.size(); }
+
+ /// Return whether given SCEV is used as the parameter in this Scop.
+ bool isParam(const SCEV *Param) const { return Parameters.count(Param); }
+
+ /// Take a list of parameters and add the new ones to the scop.
+ void addParams(const ParameterSetTy &NewParameters);
+
+ /// Return an iterator range containing the scop parameters.
+ iterator_range<ParameterSetTy::iterator> parameters() const {
+ return make_range(Parameters.begin(), Parameters.end());
+ }
+
+ /// Return an iterator range containing invariant accesses.
+ iterator_range<InvariantEquivClassesTy::iterator> invariantEquivClasses() {
+ return make_range(InvariantEquivClasses.begin(),
+ InvariantEquivClasses.end());
+ }
+
+ /// Return an iterator range containing all the MemoryAccess objects of the
+ /// Scop.
+ iterator_range<AccFuncVector::iterator> access_functions() {
+ return make_range(AccessFunctions.begin(), AccessFunctions.end());
+ }
+
+ /// Return whether this scop is empty, i.e. contains no statements that
+ /// could be executed.
+ bool isEmpty() const { return Stmts.empty(); }
+
+ StringRef getName() {
+ if (!name)
+ name = R.getNameStr();
+ return *name;
+ }
+
+ using array_iterator = ArrayInfoSetTy::iterator;
+ using const_array_iterator = ArrayInfoSetTy::const_iterator;
+ using array_range = iterator_range<ArrayInfoSetTy::iterator>;
+ using const_array_range = iterator_range<ArrayInfoSetTy::const_iterator>;
+
+ inline array_iterator array_begin() { return ScopArrayInfoSet.begin(); }
+
+ inline array_iterator array_end() { return ScopArrayInfoSet.end(); }
+
+ inline const_array_iterator array_begin() const {
+ return ScopArrayInfoSet.begin();
+ }
+
+ inline const_array_iterator array_end() const {
+ return ScopArrayInfoSet.end();
+ }
+
+ inline array_range arrays() {
+ return array_range(array_begin(), array_end());
+ }
+
+ inline const_array_range arrays() const {
+ return const_array_range(array_begin(), array_end());
+ }
+
+ /// Return the isl_id that represents a certain parameter.
+ ///
+ /// @param Parameter A SCEV that was recognized as a Parameter.
+ ///
+ /// @return The corresponding isl_id or NULL otherwise.
+ isl::id getIdForParam(const SCEV *Parameter) const;
+
+ /// Get the maximum region of this static control part.
+ ///
+ /// @return The maximum region of this static control part.
+ inline const Region &getRegion() const { return R; }
+ inline Region &getRegion() { return R; }
+
+ /// Return the function this SCoP is in.
+ Function &getFunction() const { return *R.getEntry()->getParent(); }
+
+ /// Check if @p L is contained in the SCoP.
+ bool contains(const Loop *L) const { return R.contains(L); }
+
+ /// Check if @p BB is contained in the SCoP.
+ bool contains(const BasicBlock *BB) const { return R.contains(BB); }
+
+ /// Check if @p I is contained in the SCoP.
+ bool contains(const Instruction *I) const { return R.contains(I); }
+
+ /// Return the unique exit block of the SCoP.
+ BasicBlock *getExit() const { return R.getExit(); }
+
+ /// Return the unique exiting block of the SCoP if any.
+ BasicBlock *getExitingBlock() const { return R.getExitingBlock(); }
+
+ /// Return the unique entry block of the SCoP.
+ BasicBlock *getEntry() const { return R.getEntry(); }
+
+ /// Return the unique entering block of the SCoP if any.
+ BasicBlock *getEnteringBlock() const { return R.getEnteringBlock(); }
+
+ /// Return true if @p BB is the exit block of the SCoP.
+ bool isExit(BasicBlock *BB) const { return getExit() == BB; }
+
+ /// Return a range of all basic blocks in the SCoP.
+ Region::block_range blocks() const { return R.blocks(); }
+
+ /// Return true if and only if @p BB dominates the SCoP.
+ bool isDominatedBy(const DominatorTree &DT, BasicBlock *BB) const;
+
+ /// Get the maximum depth of the loop.
+ ///
+ /// @return The maximum depth of the loop.
+ inline unsigned getMaxLoopDepth() const { return MaxLoopDepth; }
+
+ /// Return the invariant equivalence class for @p Val if any.
+ InvariantEquivClassTy *lookupInvariantEquivClass(Value *Val);
+
+ /// Return the set of invariant accesses.
+ InvariantEquivClassesTy &getInvariantAccesses() {
+ return InvariantEquivClasses;
+ }
+
+ /// Check if the scop has any invariant access.
+ bool hasInvariantAccesses() { return !InvariantEquivClasses.empty(); }
+
+ /// Mark the SCoP as optimized by the scheduler.
+ void markAsOptimized() { IsOptimized = true; }
+
+ /// Check if the SCoP has been optimized by the scheduler.
+ bool isOptimized() const { return IsOptimized; }
+
+ /// Mark the SCoP to be skipped by ScopPass passes.
+ void markAsToBeSkipped() { SkipScop = true; }
+
+ /// Check if the SCoP is to be skipped by ScopPass passes.
+ bool isToBeSkipped() const { return SkipScop; }
+
+ /// Return the ID of the Scop
+ int getID() const { return ID; }
+
+ /// Get the name of the entry and exit blocks of this Scop.
+ ///
+ /// These along with the function name can uniquely identify a Scop.
+ ///
+ /// @return std::pair whose first element is the entry name & second element
+ /// is the exit name.
+ std::pair<std::string, std::string> getEntryExitStr() const;
+
+ /// Get the name of this Scop.
+ std::string getNameStr() const;
+
+ /// Get the constraint on parameter of this Scop.
+ ///
+ /// @return The constraint on parameter of this Scop.
+ isl::set getContext() const;
+
+ /// Return the context where execution behavior is defined. Might return
+ /// nullptr.
+ isl::set getDefinedBehaviorContext() const { return DefinedBehaviorContext; }
+
+ /// Return the define behavior context, or if not available, its approximation
+ /// from all other contexts.
+ isl::set getBestKnownDefinedBehaviorContext() const {
+ if (!DefinedBehaviorContext.is_null())
+ return DefinedBehaviorContext;
+
+ return Context.intersect_params(AssumedContext).subtract(InvalidContext);
+ }
+
+ /// Return space of isl context parameters.
+ ///
+ /// Returns the set of context parameters that are currently constrained. In
+ /// case the full set of parameters is needed, see @getFullParamSpace.
+ isl::space getParamSpace() const;
+
+ /// Return the full space of parameters.
+ ///
+ /// getParamSpace will only return the parameters of the context that are
+ /// actually constrained, whereas getFullParamSpace will return all
+ // parameters. This is useful in cases, where we need to ensure all
+ // parameters are available, as certain isl functions will abort if this is
+ // not the case.
+ isl::space getFullParamSpace() const;
+
+ /// Get the assumed context for this Scop.
+ ///
+ /// @return The assumed context of this Scop.
+ isl::set getAssumedContext() const;
+
+ /// Return true if the optimized SCoP can be executed.
+ ///
+ /// In addition to the runtime check context this will also utilize the domain
+ /// constraints to decide it the optimized version can actually be executed.
+ ///
+ /// @returns True if the optimized SCoP can be executed.
+ bool hasFeasibleRuntimeContext() const;
+
+ /// Check if the assumption in @p Set is trivial or not.
+ ///
+ /// @param Set The relations between parameters that are assumed to hold.
+ /// @param Sign Enum to indicate if the assumptions in @p Set are positive
+ /// (needed/assumptions) or negative (invalid/restrictions).
+ ///
+ /// @returns True if the assumption @p Set is not trivial.
+ bool isEffectiveAssumption(isl::set Set, AssumptionSign Sign);
+
+ /// Track and report an assumption.
+ ///
+ /// Use 'clang -Rpass-analysis=polly-scops' or 'opt
+ /// -pass-remarks-analysis=polly-scops' to output the assumptions.
+ ///
+ /// @param Kind The assumption kind describing the underlying cause.
+ /// @param Set The relations between parameters that are assumed to hold.
+ /// @param Loc The location in the source that caused this assumption.
+ /// @param Sign Enum to indicate if the assumptions in @p Set are positive
+ /// (needed/assumptions) or negative (invalid/restrictions).
+ /// @param BB The block in which this assumption was taken. Used to
+ /// calculate hotness when emitting remark.
+ ///
+ /// @returns True if the assumption is not trivial.
+ bool trackAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
+ AssumptionSign Sign, BasicBlock *BB);
+
+ /// Add the conditions from @p Set (or subtract them if @p Sign is
+ /// AS_RESTRICTION) to the defined behaviour context.
+ void intersectDefinedBehavior(isl::set Set, AssumptionSign Sign);
+
+ /// Add assumptions to assumed context.
+ ///
+ /// The assumptions added will be assumed to hold during the execution of the
+ /// scop. However, as they are generally not statically provable, at code
+ /// generation time run-time checks will be generated that ensure the
+ /// assumptions hold.
+ ///
+ /// WARNING: We currently exploit in simplifyAssumedContext the knowledge
+ /// that assumptions do not change the set of statement instances
+ /// executed.
+ ///
+ /// @param Kind The assumption kind describing the underlying cause.
+ /// @param Set The relations between parameters that are assumed to hold.
+ /// @param Loc The location in the source that caused this assumption.
+ /// @param Sign Enum to indicate if the assumptions in @p Set are positive
+ /// (needed/assumptions) or negative (invalid/restrictions).
+ /// @param BB The block in which this assumption was taken. Used to
+ /// calculate hotness when emitting remark.
+ /// @param RTC Does the assumption require a runtime check?
+ void addAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
+ AssumptionSign Sign, BasicBlock *BB, bool RTC = true);
+
+ /// Mark the scop as invalid.
+ ///
+ /// This method adds an assumption to the scop that is always invalid. As a
+ /// result, the scop will not be optimized later on. This function is commonly
+ /// called when a condition makes it impossible (or too compile time
+ /// expensive) to process this scop any further.
+ ///
+ /// @param Kind The assumption kind describing the underlying cause.
+ /// @param Loc The location in the source that triggered .
+ /// @param BB The BasicBlock where it was triggered.
+ void invalidate(AssumptionKind Kind, DebugLoc Loc, BasicBlock *BB = nullptr);
+
+ /// Get the invalid context for this Scop.
+ ///
+ /// @return The invalid context of this Scop.
+ isl::set getInvalidContext() const;
+
+ /// Return true if and only if the InvalidContext is trivial (=empty).
+ bool hasTrivialInvalidContext() const { return InvalidContext.is_empty(); }
+
+ /// Return all alias groups for this SCoP.
+ const MinMaxVectorPairVectorTy &getAliasGroups() const {
+ return MinMaxAliasGroups;
+ }
+
+ void addAliasGroup(MinMaxVectorTy &MinMaxAccessesReadWrite,
+ MinMaxVectorTy &MinMaxAccessesReadOnly) {
+ MinMaxAliasGroups.emplace_back();
+ MinMaxAliasGroups.back().first = MinMaxAccessesReadWrite;
+ MinMaxAliasGroups.back().second = MinMaxAccessesReadOnly;
+ }
+
+ /// Remove statements from the list of scop statements.
+ ///
+ /// @param ShouldDelete A function that returns true if the statement passed
+ /// to it should be deleted.
+ /// @param AfterHoisting If true, also remove from data access lists.
+ /// These lists are filled during
+ /// ScopBuilder::buildAccessRelations. Therefore, if this
+ /// method is called before buildAccessRelations, false
+ /// must be passed.
+ void removeStmts(function_ref<bool(ScopStmt &)> ShouldDelete,
+ bool AfterHoisting = true);
+
+ /// Get an isl string representing the context.
+ std::string getContextStr() const;
+
+ /// Get an isl string representing the assumed context.
+ std::string getAssumedContextStr() const;
+
+ /// Get an isl string representing the invalid context.
+ std::string getInvalidContextStr() const;
+
+ /// Return the list of ScopStmts that represent the given @p BB.
+ ArrayRef<ScopStmt *> getStmtListFor(BasicBlock *BB) const;
+
+ /// Get the statement to put a PHI WRITE into.
+ ///
+ /// @param U The operand of a PHINode.
+ ScopStmt *getIncomingStmtFor(const Use &U) const;
+
+ /// Return the last statement representing @p BB.
+ ///
+ /// Of the sequence of statements that represent a @p BB, this is the last one
+ /// to be executed. It is typically used to determine which instruction to add
+ /// a MemoryKind::PHI WRITE to. For this purpose, it is not strictly required
+ /// to be executed last, only that the incoming value is available in it.
+ ScopStmt *getLastStmtFor(BasicBlock *BB) const;
+
+ /// Return the ScopStmts that represents the Region @p R, or nullptr if
+ /// it is not represented by any statement in this Scop.
+ ArrayRef<ScopStmt *> getStmtListFor(Region *R) const;
+
+ /// Return the ScopStmts that represents @p RN; can return nullptr if
+ /// the RegionNode is not within the SCoP or has been removed due to
+ /// simplifications.
+ ArrayRef<ScopStmt *> getStmtListFor(RegionNode *RN) const;
+
+ /// Return the ScopStmt an instruction belongs to, or nullptr if it
+ /// does not belong to any statement in this Scop.
+ ScopStmt *getStmtFor(Instruction *Inst) const {
+ return InstStmtMap.lookup(Inst);
+ }
+
+ /// Return the number of statements in the SCoP.
+ size_t getSize() const { return Stmts.size(); }
+
+ /// @name Statements Iterators
+ ///
+ /// These iterators iterate over all statements of this Scop.
+ //@{
+ using iterator = StmtSet::iterator;
+ using const_iterator = StmtSet::const_iterator;
+
+ iterator begin() { return Stmts.begin(); }
+ iterator end() { return Stmts.end(); }
+ const_iterator begin() const { return Stmts.begin(); }
+ const_iterator end() const { return Stmts.end(); }
+
+ using reverse_iterator = StmtSet::reverse_iterator;
+ using const_reverse_iterator = StmtSet::const_reverse_iterator;
+
+ reverse_iterator rbegin() { return Stmts.rbegin(); }
+ reverse_iterator rend() { return Stmts.rend(); }
+ const_reverse_iterator rbegin() const { return Stmts.rbegin(); }
+ const_reverse_iterator rend() const { return Stmts.rend(); }
+ //@}
+
+ /// Return the set of required invariant loads.
+ const InvariantLoadsSetTy &getRequiredInvariantLoads() const {
+ return DC.RequiredILS;
+ }
+
+ /// Add @p LI to the set of required invariant loads.
+ void addRequiredInvariantLoad(LoadInst *LI) { DC.RequiredILS.insert(LI); }
+
+ /// Return the set of boxed (thus overapproximated) loops.
+ const BoxedLoopsSetTy &getBoxedLoops() const { return DC.BoxedLoopsSet; }
+
+ /// Return true if and only if @p R is a non-affine subregion.
+ bool isNonAffineSubRegion(const Region *R) {
+ return DC.NonAffineSubRegionSet.count(R);
+ }
+
+ const MapInsnToMemAcc &getInsnToMemAccMap() const { return DC.InsnToMemAcc; }
+
+ /// Return the (possibly new) ScopArrayInfo object for @p Access.
+ ///
+ /// @param ElementType The type of the elements stored in this array.
+ /// @param Kind The kind of the array info object.
+ /// @param BaseName The optional name of this memory reference.
+ ScopArrayInfo *getOrCreateScopArrayInfo(Value *BasePtr, Type *ElementType,
+ ArrayRef<const SCEV *> Sizes,
+ MemoryKind Kind,
+ const char *BaseName = nullptr);
+
+ /// Create an array and return the corresponding ScopArrayInfo object.
+ ///
+ /// @param ElementType The type of the elements stored in this array.
+ /// @param BaseName The name of this memory reference.
+ /// @param Sizes The sizes of dimensions.
+ ScopArrayInfo *createScopArrayInfo(Type *ElementType,
+ const std::string &BaseName,
+ const std::vector<unsigned> &Sizes);
+
+ /// Return the cached ScopArrayInfo object for @p BasePtr.
+ ///
+ /// @param BasePtr The base pointer the object has been stored for.
+ /// @param Kind The kind of array info object.
+ ///
+ /// @returns The ScopArrayInfo pointer or NULL if no such pointer is
+ /// available.
+ ScopArrayInfo *getScopArrayInfoOrNull(Value *BasePtr, MemoryKind Kind);
+
+ /// Return the cached ScopArrayInfo object for @p BasePtr.
+ ///
+ /// @param BasePtr The base pointer the object has been stored for.
+ /// @param Kind The kind of array info object.
+ ///
+ /// @returns The ScopArrayInfo pointer (may assert if no such pointer is
+ /// available).
+ ScopArrayInfo *getScopArrayInfo(Value *BasePtr, MemoryKind Kind);
+
+ /// Invalidate ScopArrayInfo object for base address.
+ ///
+ /// @param BasePtr The base pointer of the ScopArrayInfo object to invalidate.
+ /// @param Kind The Kind of the ScopArrayInfo object.
+ void invalidateScopArrayInfo(Value *BasePtr, MemoryKind Kind) {
+ auto It = ScopArrayInfoMap.find(std::make_pair(BasePtr, Kind));
+ if (It == ScopArrayInfoMap.end())
+ return;
+ ScopArrayInfoSet.remove(It->second.get());
+ ScopArrayInfoMap.erase(It);
+ }
+
+ /// Set new isl context.
+ void setContext(isl::set NewContext);
+
+ /// Update maximal loop depth. If @p Depth is smaller than current value,
+ /// then maximal loop depth is not updated.
+ void updateMaxLoopDepth(unsigned Depth) {
+ MaxLoopDepth = std::max(MaxLoopDepth, Depth);
+ }
+
+ /// Align the parameters in the statement to the scop context
+ void realignParams();
+
+ /// Return true if this SCoP can be profitably optimized.
+ ///
+ /// @param ScalarsAreUnprofitable Never consider statements with scalar writes
+ /// as profitably optimizable.
+ ///
+ /// @return Whether this SCoP can be profitably optimized.
+ bool isProfitable(bool ScalarsAreUnprofitable) const;
+
+ /// Return true if the SCoP contained at least one error block.
+ bool hasErrorBlock() const { return HasErrorBlock; }
+
+ /// Notify SCoP that it contains an error block
+ void notifyErrorBlock() { HasErrorBlock = true; }
+
+ /// Return true if the underlying region has a single exiting block.
+ bool hasSingleExitEdge() const { return HasSingleExitEdge; }
+
+ /// Print the static control part.
+ ///
+ /// @param OS The output stream the static control part is printed to.
+ /// @param PrintInstructions Whether to print the statement's instructions as
+ /// well.
+ void print(raw_ostream &OS, bool PrintInstructions) const;
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ /// Print the ScopStmt to stderr.
+ void dump() const;
+#endif
+
+ /// Get the isl context of this static control part.
+ ///
+ /// @return The isl context of this static control part.
+ isl::ctx getIslCtx() const;
+
+ /// Directly return the shared_ptr of the context.
+ const std::shared_ptr<isl_ctx> &getSharedIslCtx() const { return IslCtx; }
+
+ /// Compute the isl representation for the SCEV @p E
+ ///
+ /// @param E The SCEV that should be translated.
+ /// @param BB An (optional) basic block in which the isl_pw_aff is computed.
+ /// SCEVs known to not reference any loops in the SCoP can be
+ /// passed without a @p BB.
+ /// @param NonNegative Flag to indicate the @p E has to be non-negative.
+ ///
+ /// Note that this function will always return a valid isl_pw_aff. However, if
+ /// the translation of @p E was deemed to complex the SCoP is invalidated and
+ /// a dummy value of appropriate dimension is returned. This allows to bail
+ /// for complex cases without "error handling code" needed on the users side.
+ PWACtx getPwAff(const SCEV *E, BasicBlock *BB = nullptr,
+ bool NonNegative = false,
+ RecordedAssumptionsTy *RecordedAssumptions = nullptr);
+
+ /// Compute the isl representation for the SCEV @p E
+ ///
+ /// This function is like @see Scop::getPwAff() but strips away the invalid
+ /// domain part associated with the piecewise affine function.
+ isl::pw_aff
+ getPwAffOnly(const SCEV *E, BasicBlock *BB = nullptr,
+ RecordedAssumptionsTy *RecordedAssumptions = nullptr);
+
+ /// Check if an <nsw> AddRec for the loop L is cached.
+ bool hasNSWAddRecForLoop(Loop *L) { return Affinator.hasNSWAddRecForLoop(L); }
+
+ /// Return the domain of @p Stmt.
+ ///
+ /// @param Stmt The statement for which the conditions should be returned.
+ isl::set getDomainConditions(const ScopStmt *Stmt) const;
+
+ /// Return the domain of @p BB.
+ ///
+ /// @param BB The block for which the conditions should be returned.
+ isl::set getDomainConditions(BasicBlock *BB) const;
+
+ /// Return the domain of @p BB. If it does not exist, create an empty one.
+ isl::set &getOrInitEmptyDomain(BasicBlock *BB) { return DomainMap[BB]; }
+
+ /// Check if domain is determined for @p BB.
+ bool isDomainDefined(BasicBlock *BB) const { return DomainMap.count(BB) > 0; }
+
+ /// Set domain for @p BB.
+ void setDomain(BasicBlock *BB, isl::set &Domain) { DomainMap[BB] = Domain; }
+
+ /// Get a union set containing the iteration domains of all statements.
+ isl::union_set getDomains() const;
+
+ /// Get a union map of all may-writes performed in the SCoP.
+ isl::union_map getMayWrites();
+
+ /// Get a union map of all must-writes performed in the SCoP.
+ isl::union_map getMustWrites();
+
+ /// Get a union map of all writes performed in the SCoP.
+ isl::union_map getWrites();
+
+ /// Get a union map of all reads performed in the SCoP.
+ isl::union_map getReads();
+
+ /// Get a union map of all memory accesses performed in the SCoP.
+ isl::union_map getAccesses();
+
+ /// Get a union map of all memory accesses performed in the SCoP.
+ ///
+ /// @param Array The array to which the accesses should belong.
+ isl::union_map getAccesses(ScopArrayInfo *Array);
+
+ /// Get the schedule of all the statements in the SCoP.
+ ///
+ /// @return The schedule of all the statements in the SCoP, if the schedule of
+ /// the Scop does not contain extension nodes, and nullptr, otherwise.
+ isl::union_map getSchedule() const;
+
+ /// Get a schedule tree describing the schedule of all statements.
+ isl::schedule getScheduleTree() const;
+
+ /// Update the current schedule
+ ///
+ /// NewSchedule The new schedule (given as a flat union-map).
+ void setSchedule(isl::union_map NewSchedule);
+
+ /// Update the current schedule
+ ///
+ /// NewSchedule The new schedule (given as schedule tree).
+ void setScheduleTree(isl::schedule NewSchedule);
+
+ /// Whether the schedule is the original schedule as derived from the CFG by
+ /// ScopBuilder.
+ bool isOriginalSchedule() const { return !ScheduleModified; }
+
+ /// Intersects the domains of all statements in the SCoP.
+ ///
+ /// @return true if a change was made
+ bool restrictDomains(isl::union_set Domain);
+
+ /// Get the depth of a loop relative to the outermost loop in the Scop.
+ ///
+ /// This will return
+ /// 0 if @p L is an outermost loop in the SCoP
+ /// >0 for other loops in the SCoP
+ /// -1 if @p L is nullptr or there is no outermost loop in the SCoP
+ int getRelativeLoopDepth(const Loop *L) const;
+
+ /// Find the ScopArrayInfo associated with an isl Id
+ /// that has name @p Name.
+ ScopArrayInfo *getArrayInfoByName(const std::string BaseName);
+
+ /// Simplify the SCoP representation.
+ ///
+ /// @param AfterHoisting Whether it is called after invariant load hoisting.
+ /// When true, also removes statements without
+ /// side-effects.
+ void simplifySCoP(bool AfterHoisting);
+
+ /// Get the next free array index.
+ ///
+ /// This function returns a unique index which can be used to identify an
+ /// array.
+ long getNextArrayIdx() { return ArrayIdx++; }
+
+ /// Get the next free statement index.
+ ///
+ /// This function returns a unique index which can be used to identify a
+ /// statement.
+ long getNextStmtIdx() { return StmtIdx++; }
+
+ /// Get the representing SCEV for @p S if applicable, otherwise @p S.
+ ///
+ /// Invariant loads of the same location are put in an equivalence class and
+ /// only one of them is chosen as a representing element that will be
+ /// modeled as a parameter. The others have to be normalized, i.e.,
+ /// replaced by the representing element of their equivalence class, in order
+ /// to get the correct parameter value, e.g., in the SCEVAffinator.
+ ///
+ /// @param S The SCEV to normalize.
+ ///
+ /// @return The representing SCEV for invariant loads or @p S if none.
+ const SCEV *getRepresentingInvariantLoadSCEV(const SCEV *S) const;
+
+ /// Return the MemoryAccess that writes an llvm::Value, represented by a
+ /// ScopArrayInfo.
+ ///
+ /// There can be at most one such MemoryAccess per llvm::Value in the SCoP.
+ /// Zero is possible for read-only values.
+ MemoryAccess *getValueDef(const ScopArrayInfo *SAI) const;
+
+ /// Return all MemoryAccesses that us an llvm::Value, represented by a
+ /// ScopArrayInfo.
+ ArrayRef<MemoryAccess *> getValueUses(const ScopArrayInfo *SAI) const;
+
+ /// Return the MemoryAccess that represents an llvm::PHINode.
+ ///
+ /// ExitPHIs's PHINode is not within the SCoPs. This function returns nullptr
+ /// for them.
+ MemoryAccess *getPHIRead(const ScopArrayInfo *SAI) const;
+
+ /// Return all MemoryAccesses for all incoming statements of a PHINode,
+ /// represented by a ScopArrayInfo.
+ ArrayRef<MemoryAccess *> getPHIIncomings(const ScopArrayInfo *SAI) const;
+
+ /// Return whether @p Inst has a use outside of this SCoP.
+ bool isEscaping(Instruction *Inst);
+
+ struct ScopStatistics {
+ int NumAffineLoops = 0;
+ int NumBoxedLoops = 0;
+
+ int NumValueWrites = 0;
+ int NumValueWritesInLoops = 0;
+ int NumPHIWrites = 0;
+ int NumPHIWritesInLoops = 0;
+ int NumSingletonWrites = 0;
+ int NumSingletonWritesInLoops = 0;
+ };
+
+ /// Collect statistic about this SCoP.
+ ///
+ /// These are most commonly used for LLVM's static counters (Statistic.h) in
+ /// various places. If statistics are disabled, only zeros are returned to
+ /// avoid the overhead.
+ ScopStatistics getStatistics() const;
+
+ /// Is this Scop marked as not to be transformed by an optimization heuristic?
+ /// In this case, only user-directed transformations are allowed.
+ bool hasDisableHeuristicsHint() const { return HasDisableHeuristicsHint; }
+
+ /// Mark this Scop to not apply an optimization heuristic.
+ void markDisableHeuristics() { HasDisableHeuristicsHint = true; }
+};
+
+/// Print Scop scop to raw_ostream OS.
+raw_ostream &operator<<(raw_ostream &OS, const Scop &scop);
+
+/// The legacy pass manager's analysis pass to compute scop information
+/// for a region.
+class ScopInfoRegionPass : public RegionPass {
+ /// The Scop pointer which is used to construct a Scop.
+ std::unique_ptr<Scop> S;
+
+public:
+ static char ID; // Pass identification, replacement for typeid
+
+ ScopInfoRegionPass() : RegionPass(ID) {}
+ ~ScopInfoRegionPass() override = default;
+
+ /// Build Scop object, the Polly IR of static control
+ /// part for the current SESE-Region.
+ ///
+ /// @return If the current region is a valid for a static control part,
+ /// return the Polly IR representing this static control part,
+ /// return null otherwise.
+ Scop *getScop() { return S.get(); }
+ const Scop *getScop() const { return S.get(); }
+
+ /// Calculate the polyhedral scop information for a given Region.
+ bool runOnRegion(Region *R, RGPassManager &RGM) override;
+
+ void releaseMemory() override { S.reset(); }
+
+ void print(raw_ostream &O, const Module *M = nullptr) const override;
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+};
+
+class ScopInfo {
+public:
+ using RegionToScopMapTy = MapVector<Region *, std::unique_ptr<Scop>>;
+ using reverse_iterator = RegionToScopMapTy::reverse_iterator;
+ using const_reverse_iterator = RegionToScopMapTy::const_reverse_iterator;
+ using iterator = RegionToScopMapTy::iterator;
+ using const_iterator = RegionToScopMapTy::const_iterator;
+
+private:
+ /// A map of Region to its Scop object containing
+ /// Polly IR of static control part.
+ RegionToScopMapTy RegionToScopMap;
+ const DataLayout &DL;
+ ScopDetection &SD;
+ ScalarEvolution &SE;
+ LoopInfo &LI;
+ AAResults &AA;
+ DominatorTree &DT;
+ AssumptionCache &AC;
+ OptimizationRemarkEmitter &ORE;
+
+public:
+ ScopInfo(const DataLayout &DL, ScopDetection &SD, ScalarEvolution &SE,
+ LoopInfo &LI, AAResults &AA, DominatorTree &DT, AssumptionCache &AC,
+ OptimizationRemarkEmitter &ORE);
+
+ /// Get the Scop object for the given Region.
+ ///
+ /// @return If the given region is the maximal region within a scop, return
+ /// the scop object. If the given region is a subregion, return a
+ /// nullptr. Top level region containing the entry block of a function
+ /// is not considered in the scop creation.
+ Scop *getScop(Region *R) const {
+ auto MapIt = RegionToScopMap.find(R);
+ if (MapIt != RegionToScopMap.end())
+ return MapIt->second.get();
+ return nullptr;
+ }
+
+ /// Recompute the Scop-Information for a function.
+ ///
+ /// This invalidates any iterators.
+ void recompute();
+
+ /// Handle invalidation explicitly
+ bool invalidate(Function &F, const PreservedAnalyses &PA,
+ FunctionAnalysisManager::Invalidator &Inv);
+
+ iterator begin() { return RegionToScopMap.begin(); }
+ iterator end() { return RegionToScopMap.end(); }
+ const_iterator begin() const { return RegionToScopMap.begin(); }
+ const_iterator end() const { return RegionToScopMap.end(); }
+ reverse_iterator rbegin() { return RegionToScopMap.rbegin(); }
+ reverse_iterator rend() { return RegionToScopMap.rend(); }
+ const_reverse_iterator rbegin() const { return RegionToScopMap.rbegin(); }
+ const_reverse_iterator rend() const { return RegionToScopMap.rend(); }
+ bool empty() const { return RegionToScopMap.empty(); }
+};
+
+struct ScopInfoAnalysis : public AnalysisInfoMixin<ScopInfoAnalysis> {
+ static AnalysisKey Key;
+
+ using Result = ScopInfo;
+
+ Result run(Function &, FunctionAnalysisManager &);
+};
+
+struct ScopInfoPrinterPass : public PassInfoMixin<ScopInfoPrinterPass> {
+ ScopInfoPrinterPass(raw_ostream &OS) : Stream(OS) {}
+
+ PreservedAnalyses run(Function &, FunctionAnalysisManager &);
+
+ raw_ostream &Stream;
+};
+
+//===----------------------------------------------------------------------===//
+/// The legacy pass manager's analysis pass to compute scop information
+/// for the whole function.
+///
+/// This pass will maintain a map of the maximal region within a scop to its
+/// scop object for all the feasible scops present in a function.
+/// This pass is an alternative to the ScopInfoRegionPass in order to avoid a
+/// region pass manager.
+class ScopInfoWrapperPass : public FunctionPass {
+ std::unique_ptr<ScopInfo> Result;
+
+public:
+ ScopInfoWrapperPass() : FunctionPass(ID) {}
+ ~ScopInfoWrapperPass() override = default;
+
+ static char ID; // Pass identification, replacement for typeid
+
+ ScopInfo *getSI() { return Result.get(); }
+ const ScopInfo *getSI() const { return Result.get(); }
+
+ /// Calculate all the polyhedral scops for a given function.
+ bool runOnFunction(Function &F) override;
+
+ void releaseMemory() override { Result.reset(); }
+
+ void print(raw_ostream &O, const Module *M = nullptr) const override;
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+};
+} // end namespace polly
+
+#endif // POLLY_SCOPINFO_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/ScopPass.h b/contrib/libs/llvm14/tools/polly/include/polly/ScopPass.h
new file mode 100644
index 00000000000..db910572bcc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/ScopPass.h
@@ -0,0 +1,293 @@
+//===--------- ScopPass.h - Pass for Static Control Parts --------*-C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the ScopPass class. ScopPasses are just RegionPasses,
+// except they operate on Polly IR (Scop and ScopStmt) built by ScopInfo Pass.
+// Because they operate on Polly IR, not the LLVM IR, ScopPasses are not allowed
+// to modify the LLVM IR. Due to this limitation, the ScopPass class takes
+// care of declaring that no LLVM passes are invalidated.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SCOP_PASS_H
+#define POLLY_SCOP_PASS_H
+
+#include "polly/ScopInfo.h"
+#include "llvm/ADT/PriorityWorklist.h"
+#include "llvm/Analysis/RegionPass.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/PassManagerImpl.h"
+
+namespace polly {
+using llvm::AllAnalysesOn;
+using llvm::AnalysisManager;
+using llvm::DominatorTreeAnalysis;
+using llvm::InnerAnalysisManagerProxy;
+using llvm::LoopAnalysis;
+using llvm::OuterAnalysisManagerProxy;
+using llvm::PassManager;
+using llvm::RegionInfoAnalysis;
+using llvm::ScalarEvolutionAnalysis;
+using llvm::SmallPriorityWorklist;
+using llvm::TargetIRAnalysis;
+using llvm::TargetTransformInfo;
+
+class Scop;
+class SPMUpdater;
+struct ScopStandardAnalysisResults;
+
+using ScopAnalysisManager =
+ AnalysisManager<Scop, ScopStandardAnalysisResults &>;
+using ScopAnalysisManagerFunctionProxy =
+ InnerAnalysisManagerProxy<ScopAnalysisManager, Function>;
+using FunctionAnalysisManagerScopProxy =
+ OuterAnalysisManagerProxy<FunctionAnalysisManager, Scop,
+ ScopStandardAnalysisResults &>;
+} // namespace polly
+
+namespace llvm {
+using polly::Scop;
+using polly::ScopAnalysisManager;
+using polly::ScopAnalysisManagerFunctionProxy;
+using polly::ScopInfo;
+using polly::ScopStandardAnalysisResults;
+using polly::SPMUpdater;
+
+template <>
+class InnerAnalysisManagerProxy<ScopAnalysisManager, Function>::Result {
+public:
+ explicit Result(ScopAnalysisManager &InnerAM, ScopInfo &SI)
+ : InnerAM(&InnerAM), SI(&SI) {}
+ Result(Result &&R) : InnerAM(std::move(R.InnerAM)), SI(R.SI) {
+ R.InnerAM = nullptr;
+ }
+ Result &operator=(Result &&RHS) {
+ InnerAM = RHS.InnerAM;
+ SI = RHS.SI;
+ RHS.InnerAM = nullptr;
+ return *this;
+ }
+ ~Result() {
+ if (!InnerAM)
+ return;
+ InnerAM->clear();
+ }
+
+ ScopAnalysisManager &getManager() { return *InnerAM; }
+
+ bool invalidate(Function &F, const PreservedAnalyses &PA,
+ FunctionAnalysisManager::Invalidator &Inv);
+
+private:
+ ScopAnalysisManager *InnerAM;
+ ScopInfo *SI;
+};
+
+// A partial specialization of the require analysis template pass to handle
+// extra parameters
+template <typename AnalysisT>
+struct RequireAnalysisPass<AnalysisT, Scop, ScopAnalysisManager,
+ ScopStandardAnalysisResults &, SPMUpdater &>
+ : PassInfoMixin<
+ RequireAnalysisPass<AnalysisT, Scop, ScopAnalysisManager,
+ ScopStandardAnalysisResults &, SPMUpdater &>> {
+ PreservedAnalyses run(Scop &L, ScopAnalysisManager &AM,
+ ScopStandardAnalysisResults &AR, SPMUpdater &) {
+ (void)AM.template getResult<AnalysisT>(L, AR);
+ return PreservedAnalyses::all();
+ }
+};
+
+template <>
+InnerAnalysisManagerProxy<ScopAnalysisManager, Function>::Result
+InnerAnalysisManagerProxy<ScopAnalysisManager, Function>::run(
+ Function &F, FunctionAnalysisManager &FAM);
+
+template <>
+PreservedAnalyses
+PassManager<Scop, ScopAnalysisManager, ScopStandardAnalysisResults &,
+ SPMUpdater &>::run(Scop &InitialS, ScopAnalysisManager &AM,
+ ScopStandardAnalysisResults &, SPMUpdater &);
+extern template class PassManager<Scop, ScopAnalysisManager,
+ ScopStandardAnalysisResults &, SPMUpdater &>;
+extern template class InnerAnalysisManagerProxy<ScopAnalysisManager, Function>;
+extern template class OuterAnalysisManagerProxy<FunctionAnalysisManager, Scop,
+ ScopStandardAnalysisResults &>;
+} // namespace llvm
+
+namespace polly {
+
+template <typename AnalysisManagerT, typename IRUnitT, typename... ExtraArgTs>
+class OwningInnerAnalysisManagerProxy
+ : public InnerAnalysisManagerProxy<AnalysisManagerT, IRUnitT> {
+public:
+ OwningInnerAnalysisManagerProxy()
+ : InnerAnalysisManagerProxy<AnalysisManagerT, IRUnitT>(InnerAM) {}
+ using Result = typename InnerAnalysisManagerProxy<AnalysisManagerT, IRUnitT,
+ ExtraArgTs...>::Result;
+ Result run(IRUnitT &IR, AnalysisManager<IRUnitT, ExtraArgTs...> &AM,
+ ExtraArgTs...) {
+ return Result(InnerAM);
+ }
+
+ AnalysisManagerT &getManager() { return InnerAM; }
+
+private:
+ AnalysisManagerT InnerAM;
+};
+
+template <>
+OwningInnerAnalysisManagerProxy<ScopAnalysisManager, Function>::Result
+OwningInnerAnalysisManagerProxy<ScopAnalysisManager, Function>::run(
+ Function &F, FunctionAnalysisManager &FAM);
+extern template class OwningInnerAnalysisManagerProxy<ScopAnalysisManager,
+ Function>;
+
+using OwningScopAnalysisManagerFunctionProxy =
+ OwningInnerAnalysisManagerProxy<ScopAnalysisManager, Function>;
+using ScopPassManager =
+ PassManager<Scop, ScopAnalysisManager, ScopStandardAnalysisResults &,
+ SPMUpdater &>;
+
+/// ScopPass - This class adapts the RegionPass interface to allow convenient
+/// creation of passes that operate on the Polly IR. Instead of overriding
+/// runOnRegion, subclasses override runOnScop.
+class ScopPass : public RegionPass {
+ Scop *S;
+
+protected:
+ explicit ScopPass(char &ID) : RegionPass(ID), S(nullptr) {}
+
+ /// runOnScop - This method must be overloaded to perform the
+ /// desired Polyhedral transformation or analysis.
+ ///
+ virtual bool runOnScop(Scop &S) = 0;
+
+ /// Print method for SCoPs.
+ virtual void printScop(raw_ostream &OS, Scop &S) const {}
+
+ /// getAnalysisUsage - Subclasses that override getAnalysisUsage
+ /// must call this.
+ ///
+ virtual void getAnalysisUsage(AnalysisUsage &AU) const override;
+
+private:
+ bool runOnRegion(Region *R, RGPassManager &RGM) override;
+ void print(raw_ostream &OS, const Module *) const override;
+};
+
+struct ScopStandardAnalysisResults {
+ DominatorTree &DT;
+ ScopInfo &SI;
+ ScalarEvolution &SE;
+ LoopInfo &LI;
+ RegionInfo &RI;
+ TargetTransformInfo &TTI;
+};
+
+class SPMUpdater {
+public:
+ SPMUpdater(SmallPriorityWorklist<Region *, 4> &Worklist,
+ ScopAnalysisManager &SAM)
+ : InvalidateCurrentScop(false), Worklist(Worklist), SAM(SAM) {}
+
+ bool invalidateCurrentScop() const { return InvalidateCurrentScop; }
+
+ void invalidateScop(Scop &S) {
+ if (&S == CurrentScop)
+ InvalidateCurrentScop = true;
+
+ Worklist.erase(&S.getRegion());
+ SAM.clear(S, S.getName());
+ }
+
+private:
+ Scop *CurrentScop;
+ bool InvalidateCurrentScop;
+ SmallPriorityWorklist<Region *, 4> &Worklist;
+ ScopAnalysisManager &SAM;
+ template <typename ScopPassT> friend class FunctionToScopPassAdaptor;
+};
+
+template <typename ScopPassT>
+class FunctionToScopPassAdaptor
+ : public PassInfoMixin<FunctionToScopPassAdaptor<ScopPassT>> {
+public:
+ explicit FunctionToScopPassAdaptor(ScopPassT Pass) : Pass(std::move(Pass)) {}
+
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
+ ScopDetection &SD = AM.getResult<ScopAnalysis>(F);
+ ScopInfo &SI = AM.getResult<ScopInfoAnalysis>(F);
+ if (SI.empty()) {
+ // With no scops having been detected, no IR changes have been made and
+ // therefore all analyses are preserved. However, we must still free the
+ // Scop analysis results which may hold AssertingVH that cause an error
+ // if its value is destroyed.
+ PreservedAnalyses PA = PreservedAnalyses::all();
+ PA.abandon<ScopInfoAnalysis>();
+ PA.abandon<ScopAnalysis>();
+ AM.invalidate(F, PA);
+ return PreservedAnalyses::all();
+ }
+
+ SmallPriorityWorklist<Region *, 4> Worklist;
+ for (auto &S : SI)
+ if (S.second)
+ Worklist.insert(S.first);
+
+ ScopStandardAnalysisResults AR = {AM.getResult<DominatorTreeAnalysis>(F),
+ AM.getResult<ScopInfoAnalysis>(F),
+ AM.getResult<ScalarEvolutionAnalysis>(F),
+ AM.getResult<LoopAnalysis>(F),
+ AM.getResult<RegionInfoAnalysis>(F),
+ AM.getResult<TargetIRAnalysis>(F)};
+
+ ScopAnalysisManager &SAM =
+ AM.getResult<ScopAnalysisManagerFunctionProxy>(F).getManager();
+
+ SPMUpdater Updater{Worklist, SAM};
+
+ while (!Worklist.empty()) {
+ Region *R = Worklist.pop_back_val();
+ if (!SD.isMaxRegionInScop(*R, /*Verify=*/false))
+ continue;
+ Scop *scop = SI.getScop(R);
+ if (!scop)
+ continue;
+ Updater.CurrentScop = scop;
+ Updater.InvalidateCurrentScop = false;
+ PreservedAnalyses PassPA = Pass.run(*scop, SAM, AR, Updater);
+
+ SAM.invalidate(*scop, PassPA);
+ if (Updater.invalidateCurrentScop())
+ SI.recompute();
+ };
+
+ // FIXME: For the same reason as we add a BarrierNoopPass in the legacy pass
+ // manager, do not preserve any analyses. While CodeGeneration may preserve
+ // IR analyses sufficiently to process another Scop in the same function (it
+ // has to, otherwise the ScopDetection result itself would need to be
+ // invalidated), it is not sufficient for other purposes. For instance,
+ // CodeGeneration does not inform LoopInfo about new loops in the
+ // Polly-generated IR.
+ return PreservedAnalyses::none();
+ }
+
+private:
+ ScopPassT Pass;
+};
+
+template <typename ScopPassT>
+FunctionToScopPassAdaptor<ScopPassT>
+createFunctionToScopPassAdaptor(ScopPassT Pass) {
+ return FunctionToScopPassAdaptor<ScopPassT>(std::move(Pass));
+}
+} // namespace polly
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Simplify.h b/contrib/libs/llvm14/tools/polly/include/polly/Simplify.h
new file mode 100644
index 00000000000..76099916b14
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Simplify.h
@@ -0,0 +1,81 @@
+//===------ Simplify.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Simplify a SCoP by removing unnecessary statements and accesses.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_TRANSFORM_SIMPLIFY_H
+#define POLLY_TRANSFORM_SIMPLIFY_H
+
+#include "polly/ScopPass.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace llvm {
+class PassRegistry;
+class Pass;
+} // namespace llvm
+
+namespace polly {
+class MemoryAccess;
+class ScopStmt;
+
+/// Return a vector that contains MemoryAccesses in the order in
+/// which they are executed.
+///
+/// The order is:
+/// - Implicit reads (BlockGenerator::generateScalarLoads)
+/// - Explicit reads and writes (BlockGenerator::generateArrayLoad,
+/// BlockGenerator::generateArrayStore)
+/// - In block statements, the accesses are in order in which their
+/// instructions are executed.
+/// - In region statements, that order of execution is not predictable at
+/// compile-time.
+/// - Implicit writes (BlockGenerator::generateScalarStores)
+/// The order in which implicit writes are executed relative to each other is
+/// undefined.
+llvm::SmallVector<MemoryAccess *, 32> getAccessesInOrder(ScopStmt &Stmt);
+
+/// Create a Simplify pass
+///
+/// @param CallNo Disambiguates this instance for when there are multiple
+/// instances of this pass in the pass manager. It is used only to
+/// keep the statistics apart and has no influence on the
+/// simplification itself.
+///
+/// @return The Simplify pass.
+llvm::Pass *createSimplifyWrapperPass(int CallNo = 0);
+
+struct SimplifyPass : public PassInfoMixin<SimplifyPass> {
+ SimplifyPass(int CallNo = 0) : CallNo(CallNo) {}
+
+ llvm::PreservedAnalyses run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &AR, SPMUpdater &U);
+
+private:
+ int CallNo;
+};
+
+struct SimplifyPrinterPass : public PassInfoMixin<SimplifyPrinterPass> {
+ SimplifyPrinterPass(raw_ostream &OS, int CallNo = 0)
+ : OS(OS), CallNo(CallNo) {}
+
+ PreservedAnalyses run(Scop &S, ScopAnalysisManager &,
+ ScopStandardAnalysisResults &, SPMUpdater &);
+
+private:
+ raw_ostream &OS;
+ int CallNo;
+};
+} // namespace polly
+
+namespace llvm {
+void initializeSimplifyWrapperPassPass(llvm::PassRegistry &);
+} // namespace llvm
+
+#endif /* POLLY_TRANSFORM_SIMPLIFY_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Support/DumpFunctionPass.h b/contrib/libs/llvm14/tools/polly/include/polly/Support/DumpFunctionPass.h
new file mode 100644
index 00000000000..2a803ef17ce
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Support/DumpFunctionPass.h
@@ -0,0 +1,44 @@
+//===------ DumpFunctionPass.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Write a function to a file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SUPPORT_DUMPFUNCTIONPASS_H
+#define POLLY_SUPPORT_DUMPFUNCTIONPASS_H
+
+#include "llvm/IR/PassManager.h"
+#include <string>
+
+namespace llvm {
+class FunctionPass;
+class ModulePass;
+} // namespace llvm
+
+namespace polly {
+llvm::FunctionPass *createDumpFunctionWrapperPass(std::string Suffix);
+
+/// A pass that isolates a function into a new Module and writes it into a file.
+struct DumpFunctionPass : llvm::PassInfoMixin<DumpFunctionPass> {
+ std::string Suffix;
+
+ DumpFunctionPass(std::string Suffix) : Suffix(std::move(Suffix)) {}
+
+ llvm::PreservedAnalyses run(llvm::Function &F,
+ llvm::FunctionAnalysisManager &AM);
+};
+
+} // namespace polly
+
+namespace llvm {
+class PassRegistry;
+void initializeDumpFunctionWrapperPassPass(llvm::PassRegistry &);
+} // namespace llvm
+
+#endif /* POLLY_SUPPORT_DUMPFUNCTIONPASS_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Support/DumpModulePass.h b/contrib/libs/llvm14/tools/polly/include/polly/Support/DumpModulePass.h
new file mode 100644
index 00000000000..851ba6401d0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Support/DumpModulePass.h
@@ -0,0 +1,54 @@
+//===------ DumpModulePass.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Write a module to a file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SUPPORT_DUMPMODULEPASS_H
+#define POLLY_SUPPORT_DUMPMODULEPASS_H
+
+#include "llvm/IR/PassManager.h"
+#include <string>
+
+namespace llvm {
+class ModulePass;
+} // namespace llvm
+
+namespace polly {
+/// Create a pass that prints the module into a file.
+///
+/// The meaning of @p Filename depends on @p IsSuffix. If IsSuffix==false, then
+/// the module is written to the @p Filename. If it is true, the filename is
+/// generated from the module's name, @p Filename with an '.ll' extension.
+///
+/// The intent of IsSuffix is to avoid the file being overwritten when
+/// processing multiple modules and/or with multiple dump passes in the
+/// pipeline.
+llvm::ModulePass *createDumpModuleWrapperPass(std::string Filename,
+ bool IsSuffix);
+
+/// A pass that prints the module into a file.
+struct DumpModulePass : llvm::PassInfoMixin<DumpModulePass> {
+ std::string Filename;
+ bool IsSuffix;
+
+ DumpModulePass(std::string Filename, bool IsSuffix)
+ : Filename(std::move(Filename)), IsSuffix(IsSuffix) {}
+
+ llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM);
+};
+
+} // namespace polly
+
+namespace llvm {
+class PassRegistry;
+void initializeDumpModuleWrapperPassPass(llvm::PassRegistry &);
+} // namespace llvm
+
+#endif /* POLLY_SUPPORT_DUMPMODULEPASS_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Support/GICHelper.h b/contrib/libs/llvm14/tools/polly/include/polly/Support/GICHelper.h
new file mode 100644
index 00000000000..67be60cbb3a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Support/GICHelper.h
@@ -0,0 +1,492 @@
+//===- Support/GICHelper.h -- Helper functions for ISL --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Helper functions for isl objects.
+//
+//===----------------------------------------------------------------------===//
+//
+#ifndef POLLY_SUPPORT_GIC_HELPER_H
+#define POLLY_SUPPORT_GIC_HELPER_H
+
+#include "llvm/ADT/APInt.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/Support/raw_ostream.h"
+#include "isl/ctx.h"
+#include "isl/isl-noexceptions.h"
+#include "isl/options.h"
+
+namespace polly {
+
+/// Translate an llvm::APInt to an isl_val.
+///
+/// Translate the bitsequence without sign information as provided by APInt into
+/// a signed isl_val type. Depending on the value of @p IsSigned @p Int is
+/// interpreted as unsigned value or as signed value in two's complement
+/// representation.
+///
+/// Input IsSigned Output
+///
+/// 0 0 -> 0
+/// 1 0 -> 1
+/// 00 0 -> 0
+/// 01 0 -> 1
+/// 10 0 -> 2
+/// 11 0 -> 3
+///
+/// 0 1 -> 0
+/// 1 1 -> -1
+/// 00 1 -> 0
+/// 01 1 -> 1
+/// 10 1 -> -2
+/// 11 1 -> -1
+///
+/// @param Ctx The isl_ctx to create the isl_val in.
+/// @param Int The integer value to translate.
+/// @param IsSigned If the APInt should be interpreted as signed or unsigned
+/// value.
+///
+/// @return The isl_val corresponding to @p Int.
+__isl_give isl_val *isl_valFromAPInt(isl_ctx *Ctx, const llvm::APInt Int,
+ bool IsSigned);
+
+/// Translate an llvm::APInt to an isl::val.
+///
+/// Translate the bitsequence without sign information as provided by APInt into
+/// a signed isl::val type. Depending on the value of @p IsSigned @p Int is
+/// interpreted as unsigned value or as signed value in two's complement
+/// representation.
+///
+/// Input IsSigned Output
+///
+/// 0 0 -> 0
+/// 1 0 -> 1
+/// 00 0 -> 0
+/// 01 0 -> 1
+/// 10 0 -> 2
+/// 11 0 -> 3
+///
+/// 0 1 -> 0
+/// 1 1 -> -1
+/// 00 1 -> 0
+/// 01 1 -> 1
+/// 10 1 -> -2
+/// 11 1 -> -1
+///
+/// @param Ctx The isl_ctx to create the isl::val in.
+/// @param Int The integer value to translate.
+/// @param IsSigned If the APInt should be interpreted as signed or unsigned
+/// value.
+///
+/// @return The isl::val corresponding to @p Int.
+inline isl::val valFromAPInt(isl_ctx *Ctx, const llvm::APInt Int,
+ bool IsSigned) {
+ return isl::manage(isl_valFromAPInt(Ctx, Int, IsSigned));
+}
+
+/// Translate isl_val to llvm::APInt.
+///
+/// This function can only be called on isl_val values which are integers.
+/// Calling this function with a non-integral rational, NaN or infinity value
+/// is not allowed.
+///
+/// As the input isl_val may be negative, the APInt that this function returns
+/// must always be interpreted as signed two's complement value. The bitwidth of
+/// the generated APInt is always the minimal bitwidth necessary to model the
+/// provided integer when interpreting the bit pattern as signed value.
+///
+/// Some example conversions are:
+///
+/// Input Bits Signed Bitwidth
+/// 0 -> 0 0 1
+/// -1 -> 1 -1 1
+/// 1 -> 01 1 2
+/// -2 -> 10 -2 2
+/// 2 -> 010 2 3
+/// -3 -> 101 -3 3
+/// 3 -> 011 3 3
+/// -4 -> 100 -4 3
+/// 4 -> 0100 4 4
+///
+/// @param Val The isl val to translate.
+///
+/// @return The APInt value corresponding to @p Val.
+llvm::APInt APIntFromVal(__isl_take isl_val *Val);
+
+/// Translate isl::val to llvm::APInt.
+///
+/// This function can only be called on isl::val values which are integers.
+/// Calling this function with a non-integral rational, NaN or infinity value
+/// is not allowed.
+///
+/// As the input isl::val may be negative, the APInt that this function returns
+/// must always be interpreted as signed two's complement value. The bitwidth of
+/// the generated APInt is always the minimal bitwidth necessary to model the
+/// provided integer when interpreting the bit pattern as signed value.
+///
+/// Some example conversions are:
+///
+/// Input Bits Signed Bitwidth
+/// 0 -> 0 0 1
+/// -1 -> 1 -1 1
+/// 1 -> 01 1 2
+/// -2 -> 10 -2 2
+/// 2 -> 010 2 3
+/// -3 -> 101 -3 3
+/// 3 -> 011 3 3
+/// -4 -> 100 -4 3
+/// 4 -> 0100 4 4
+///
+/// @param Val The isl val to translate.
+///
+/// @return The APInt value corresponding to @p Val.
+inline llvm::APInt APIntFromVal(isl::val V) {
+ return APIntFromVal(V.release());
+}
+
+/// Get c++ string from Isl objects.
+//@{
+#define ISL_CPP_OBJECT_TO_STRING(name) \
+ inline std::string stringFromIslObj(const name &Obj, \
+ std::string DefaultValue = "") { \
+ return stringFromIslObj(Obj.get(), DefaultValue); \
+ }
+
+#define ISL_OBJECT_TO_STRING(name) \
+ std::string stringFromIslObj(__isl_keep isl_##name *Obj, \
+ std::string DefaultValue = ""); \
+ ISL_CPP_OBJECT_TO_STRING(isl::name)
+
+ISL_OBJECT_TO_STRING(aff)
+ISL_OBJECT_TO_STRING(ast_expr)
+ISL_OBJECT_TO_STRING(ast_node)
+ISL_OBJECT_TO_STRING(basic_map)
+ISL_OBJECT_TO_STRING(basic_set)
+ISL_OBJECT_TO_STRING(map)
+ISL_OBJECT_TO_STRING(set)
+ISL_OBJECT_TO_STRING(id)
+ISL_OBJECT_TO_STRING(multi_aff)
+ISL_OBJECT_TO_STRING(multi_pw_aff)
+ISL_OBJECT_TO_STRING(multi_union_pw_aff)
+ISL_OBJECT_TO_STRING(point)
+ISL_OBJECT_TO_STRING(pw_aff)
+ISL_OBJECT_TO_STRING(pw_multi_aff)
+ISL_OBJECT_TO_STRING(schedule)
+ISL_OBJECT_TO_STRING(schedule_node)
+ISL_OBJECT_TO_STRING(space)
+ISL_OBJECT_TO_STRING(union_access_info)
+ISL_OBJECT_TO_STRING(union_flow)
+ISL_OBJECT_TO_STRING(union_set)
+ISL_OBJECT_TO_STRING(union_map)
+ISL_OBJECT_TO_STRING(union_pw_aff)
+ISL_OBJECT_TO_STRING(union_pw_multi_aff)
+//@}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+/// C++ wrapper for isl_*_dump() functions.
+//@{
+
+#define ISL_DUMP_OBJECT(name) \
+ void dumpIslObj(const isl::name &Obj); \
+ void dumpIslObj(isl_##name *Obj);
+
+ISL_DUMP_OBJECT(aff)
+ISL_DUMP_OBJECT(aff_list)
+ISL_DUMP_OBJECT(ast_expr)
+ISL_DUMP_OBJECT(ast_node)
+ISL_DUMP_OBJECT(ast_node_list)
+ISL_DUMP_OBJECT(basic_map)
+ISL_DUMP_OBJECT(basic_map_list)
+ISL_DUMP_OBJECT(basic_set)
+ISL_DUMP_OBJECT(basic_set_list)
+ISL_DUMP_OBJECT(constraint)
+ISL_DUMP_OBJECT(id)
+ISL_DUMP_OBJECT(id_list)
+ISL_DUMP_OBJECT(id_to_ast_expr)
+ISL_DUMP_OBJECT(local_space)
+ISL_DUMP_OBJECT(map)
+ISL_DUMP_OBJECT(map_list)
+ISL_DUMP_OBJECT(multi_aff)
+ISL_DUMP_OBJECT(multi_pw_aff)
+ISL_DUMP_OBJECT(multi_union_pw_aff)
+ISL_DUMP_OBJECT(multi_val)
+ISL_DUMP_OBJECT(point)
+ISL_DUMP_OBJECT(pw_aff)
+ISL_DUMP_OBJECT(pw_aff_list)
+ISL_DUMP_OBJECT(pw_multi_aff)
+ISL_DUMP_OBJECT(schedule)
+ISL_DUMP_OBJECT(schedule_constraints)
+ISL_DUMP_OBJECT(schedule_node)
+ISL_DUMP_OBJECT(set)
+ISL_DUMP_OBJECT(set_list)
+ISL_DUMP_OBJECT(space)
+ISL_DUMP_OBJECT(union_map)
+ISL_DUMP_OBJECT(union_pw_aff)
+ISL_DUMP_OBJECT(union_pw_aff_list)
+ISL_DUMP_OBJECT(union_pw_multi_aff)
+ISL_DUMP_OBJECT(union_set)
+ISL_DUMP_OBJECT(union_set_list)
+ISL_DUMP_OBJECT(val)
+ISL_DUMP_OBJECT(val_list)
+//@}
+
+/// Emit the equivaltent of the isl_*_dump output into a raw_ostream.
+/// @{
+void dumpIslObj(const isl::schedule_node &Node, llvm::raw_ostream &OS);
+void dumpIslObj(__isl_keep isl_schedule_node *node, llvm::raw_ostream &OS);
+/// @}
+#endif
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ __isl_keep isl_union_map *Map) {
+ OS << polly::stringFromIslObj(Map, "null");
+ return OS;
+}
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ __isl_keep isl_map *Map) {
+ OS << polly::stringFromIslObj(Map, "null");
+ return OS;
+}
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ __isl_keep isl_set *Set) {
+ OS << polly::stringFromIslObj(Set, "null");
+ return OS;
+}
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ __isl_keep isl_pw_aff *Map) {
+ OS << polly::stringFromIslObj(Map, "null");
+ return OS;
+}
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ __isl_keep isl_pw_multi_aff *PMA) {
+ OS << polly::stringFromIslObj(PMA, "null");
+ return OS;
+}
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ __isl_keep isl_multi_aff *MA) {
+ OS << polly::stringFromIslObj(MA, "null");
+ return OS;
+}
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ __isl_keep isl_union_pw_multi_aff *UPMA) {
+ OS << polly::stringFromIslObj(UPMA, "null");
+ return OS;
+}
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ __isl_keep isl_schedule *Schedule) {
+ OS << polly::stringFromIslObj(Schedule, "null");
+ return OS;
+}
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ __isl_keep isl_space *Space) {
+ OS << polly::stringFromIslObj(Space, "null");
+ return OS;
+}
+
+/// Combine Prefix, Val (or Number) and Suffix to an isl-compatible name.
+///
+/// In case @p UseInstructionNames is set, this function returns:
+///
+/// @p Prefix + "_" + @p Val->getName() + @p Suffix
+///
+/// otherwise
+///
+/// @p Prefix + to_string(Number) + @p Suffix
+///
+/// We ignore the value names by default, as they may change between release
+/// and debug mode and can consequently not be used when aiming for reproducible
+/// builds. However, for debugging named statements are often helpful, hence
+/// we allow their optional use.
+std::string getIslCompatibleName(const std::string &Prefix,
+ const llvm::Value *Val, long Number,
+ const std::string &Suffix,
+ bool UseInstructionNames);
+
+/// Combine Prefix, Name (or Number) and Suffix to an isl-compatible name.
+///
+/// In case @p UseInstructionNames is set, this function returns:
+///
+/// @p Prefix + "_" + Name + @p Suffix
+///
+/// otherwise
+///
+/// @p Prefix + to_string(Number) + @p Suffix
+///
+/// We ignore @p Name by default, as they may change between release
+/// and debug mode and can consequently not be used when aiming for reproducible
+/// builds. However, for debugging named statements are often helpful, hence
+/// we allow their optional use.
+std::string getIslCompatibleName(const std::string &Prefix,
+ const std::string &Middle, long Number,
+ const std::string &Suffix,
+ bool UseInstructionNames);
+
+std::string getIslCompatibleName(const std::string &Prefix,
+ const std::string &Middle,
+ const std::string &Suffix);
+
+inline llvm::DiagnosticInfoOptimizationBase &
+operator<<(llvm::DiagnosticInfoOptimizationBase &OS,
+ const isl::union_map &Obj) {
+ OS << stringFromIslObj(Obj);
+ return OS;
+}
+
+/// Scope guard for code that allows arbitrary isl function to return an error
+/// if the max-operations quota exceeds.
+///
+/// This allows to opt-in code sections that have known long executions times.
+/// code not in a hot path can continue to assume that no unexpected error
+/// occurs.
+///
+/// This is typically used inside a nested IslMaxOperationsGuard scope. The
+/// IslMaxOperationsGuard defines the number of allowed base operations for some
+/// code, IslQuotaScope defines where it is allowed to return an error result.
+class IslQuotaScope {
+ isl_ctx *IslCtx;
+ int OldOnError;
+
+public:
+ IslQuotaScope() : IslCtx(nullptr) {}
+ IslQuotaScope(const IslQuotaScope &) = delete;
+ IslQuotaScope(IslQuotaScope &&Other)
+ : IslCtx(Other.IslCtx), OldOnError(Other.OldOnError) {
+ Other.IslCtx = nullptr;
+ }
+ const IslQuotaScope &operator=(IslQuotaScope &&Other) {
+ std::swap(this->IslCtx, Other.IslCtx);
+ std::swap(this->OldOnError, Other.OldOnError);
+ return *this;
+ }
+
+ /// Enter a quota-aware scope.
+ ///
+ /// Should not be used directly. Use IslMaxOperationsGuard::enter() instead.
+ explicit IslQuotaScope(isl_ctx *IslCtx, unsigned long LocalMaxOps)
+ : IslCtx(IslCtx) {
+ assert(IslCtx);
+ assert(isl_ctx_get_max_operations(IslCtx) == 0 && "Incorrect nesting");
+ if (LocalMaxOps == 0) {
+ this->IslCtx = nullptr;
+ return;
+ }
+
+ OldOnError = isl_options_get_on_error(IslCtx);
+ isl_options_set_on_error(IslCtx, ISL_ON_ERROR_CONTINUE);
+ isl_ctx_reset_error(IslCtx);
+ isl_ctx_set_max_operations(IslCtx, LocalMaxOps);
+ }
+
+ ~IslQuotaScope() {
+ if (!IslCtx)
+ return;
+
+ assert(isl_ctx_get_max_operations(IslCtx) > 0 && "Incorrect nesting");
+ assert(isl_options_get_on_error(IslCtx) == ISL_ON_ERROR_CONTINUE &&
+ "Incorrect nesting");
+ isl_ctx_set_max_operations(IslCtx, 0);
+ isl_options_set_on_error(IslCtx, OldOnError);
+ }
+
+ /// Return whether the current quota has exceeded.
+ bool hasQuotaExceeded() const {
+ if (!IslCtx)
+ return false;
+
+ return isl_ctx_last_error(IslCtx) == isl_error_quota;
+ }
+};
+
+/// Scoped limit of ISL operations.
+///
+/// Limits the number of ISL operations during the lifetime of this object. The
+/// idea is to use this as an RAII guard for the scope where the code is aware
+/// that ISL can return errors even when all input is valid. After leaving the
+/// scope, it will return to the error setting as it was before. That also means
+/// that the error setting should not be changed while in that scope.
+///
+/// Such scopes are not allowed to be nested because the previous operations
+/// counter cannot be reset to the previous state, or one that adds the
+/// operations while being in the nested scope. Use therefore is only allowed
+/// while currently a no operations-limit is active.
+class IslMaxOperationsGuard {
+private:
+ /// The ISL context to set the operations limit.
+ ///
+ /// If set to nullptr, there is no need for any action at the end of the
+ /// scope.
+ isl_ctx *IslCtx;
+
+ /// Maximum number of operations for the scope.
+ unsigned long LocalMaxOps;
+
+ /// When AutoEnter is enabled, holds the IslQuotaScope object.
+ IslQuotaScope TopLevelScope;
+
+public:
+ /// Enter a max operations scope.
+ ///
+ /// @param IslCtx The ISL context to set the operations limit for.
+ /// @param LocalMaxOps Maximum number of operations allowed in the
+ /// scope. If set to zero, no operations limit is enforced.
+ /// @param AutoEnter If true, automatically enters an IslQuotaScope such
+ /// that isl operations may return quota errors
+ /// immediately. If false, only starts the operations
+ /// counter, but isl does not return quota errors before
+ /// calling enter().
+ IslMaxOperationsGuard(isl_ctx *IslCtx, unsigned long LocalMaxOps,
+ bool AutoEnter = true)
+ : IslCtx(IslCtx), LocalMaxOps(LocalMaxOps) {
+ assert(IslCtx);
+ assert(isl_ctx_get_max_operations(IslCtx) == 0 &&
+ "Nested max operations not supported");
+
+ // Users of this guard may check whether the last error was isl_error_quota.
+ // Reset the last error such that a previous out-of-quota error is not
+ // mistaken to have occurred in the in this quota, even if the max number of
+ // operations is set to infinite (LocalMaxOps == 0).
+ isl_ctx_reset_error(IslCtx);
+
+ if (LocalMaxOps == 0) {
+ // No limit on operations; also disable restoring on_error/max_operations.
+ this->IslCtx = nullptr;
+ return;
+ }
+
+ isl_ctx_reset_operations(IslCtx);
+ TopLevelScope = enter(AutoEnter);
+ }
+
+ /// Enter a scope that can handle out-of-quota errors.
+ ///
+ /// @param AllowReturnNull Whether the scoped code can handle out-of-quota
+ /// errors. If false, returns a dummy scope object that
+ /// does nothing.
+ IslQuotaScope enter(bool AllowReturnNull = true) {
+ return AllowReturnNull && IslCtx ? IslQuotaScope(IslCtx, LocalMaxOps)
+ : IslQuotaScope();
+ }
+
+ /// Return whether the current quota has exceeded.
+ bool hasQuotaExceeded() const {
+ if (!IslCtx)
+ return false;
+
+ return isl_ctx_last_error(IslCtx) == isl_error_quota;
+ }
+};
+} // end namespace polly
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Support/ISLOStream.h b/contrib/libs/llvm14/tools/polly/include/polly/Support/ISLOStream.h
new file mode 100644
index 00000000000..5f058507c60
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Support/ISLOStream.h
@@ -0,0 +1,48 @@
+//===------ IslOstream.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// raw_ostream printers for isl C++ objects.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/Support/GICHelper.h"
+#include "llvm/Support/raw_ostream.h"
+#include "isl/isl-noexceptions.h"
+namespace polly {
+
+#define ADD_OSTREAM_PRINTER(name) \
+ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, \
+ const name &Obj) { \
+ OS << stringFromIslObj(Obj); \
+ return OS; \
+ }
+
+ADD_OSTREAM_PRINTER(isl::aff)
+ADD_OSTREAM_PRINTER(isl::ast_expr)
+ADD_OSTREAM_PRINTER(isl::ast_node)
+ADD_OSTREAM_PRINTER(isl::basic_map)
+ADD_OSTREAM_PRINTER(isl::basic_set)
+ADD_OSTREAM_PRINTER(isl::map)
+ADD_OSTREAM_PRINTER(isl::set)
+ADD_OSTREAM_PRINTER(isl::id)
+ADD_OSTREAM_PRINTER(isl::multi_aff)
+ADD_OSTREAM_PRINTER(isl::multi_pw_aff)
+ADD_OSTREAM_PRINTER(isl::multi_union_pw_aff)
+ADD_OSTREAM_PRINTER(isl::point)
+ADD_OSTREAM_PRINTER(isl::pw_aff)
+ADD_OSTREAM_PRINTER(isl::pw_multi_aff)
+ADD_OSTREAM_PRINTER(isl::schedule)
+ADD_OSTREAM_PRINTER(isl::schedule_node)
+ADD_OSTREAM_PRINTER(isl::space)
+ADD_OSTREAM_PRINTER(isl::union_access_info)
+ADD_OSTREAM_PRINTER(isl::union_flow)
+ADD_OSTREAM_PRINTER(isl::union_set)
+ADD_OSTREAM_PRINTER(isl::union_map)
+ADD_OSTREAM_PRINTER(isl::union_pw_aff)
+ADD_OSTREAM_PRINTER(isl::union_pw_multi_aff)
+} // namespace polly
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Support/ISLTools.h b/contrib/libs/llvm14/tools/polly/include/polly/Support/ISLTools.h
new file mode 100644
index 00000000000..790f7b00253
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Support/ISLTools.h
@@ -0,0 +1,640 @@
+//===------ ISLTools.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Tools, utilities, helpers and extensions useful in conjunction with the
+// Integer Set Library (isl).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_ISLTOOLS_H
+#define POLLY_ISLTOOLS_H
+
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/iterator.h"
+#include "isl/isl-noexceptions.h"
+#include <cassert>
+
+/// In debug builds assert that the @p Size is valid, in non-debug builds
+/// disable the mandatory state checking but do not enforce the error checking.
+inline void islAssert(const isl::size &Size) {
+#ifdef NDEBUG
+ // Calling is_error() marks that the error status has been checked which
+ // disables the error-status-not-checked errors that would otherwise occur
+ // when using the value.
+ (void)Size.is_error();
+#else
+ // Assert on error in debug builds.
+ assert(!Size.is_error());
+#endif
+}
+
+/// Check that @p Size is valid (only on debug builds) and cast it to unsigned.
+/// Cast the @p Size to unsigned. If the @p Size is not valid (Size.is_error()
+/// == true) then an assert and an abort are triggered.
+inline unsigned unsignedFromIslSize(const isl::size &Size) {
+ islAssert(Size);
+ return static_cast<unsigned>(Size);
+}
+
+namespace isl {
+inline namespace noexceptions {
+
+template <typename ListT>
+using list_element_type = decltype(std::declval<ListT>().get_at(0));
+
+template <typename ListT>
+struct isl_iterator
+ : public llvm::iterator_facade_base<isl_iterator<ListT>,
+ std::forward_iterator_tag,
+ list_element_type<ListT>> {
+
+ using ElementT = list_element_type<ListT>;
+
+ explicit isl_iterator(const ListT &List)
+ : List(&List), Position(std::max(List.size().release(), 0)) {}
+ isl_iterator(const ListT &List, int Position)
+ : List(&List), Position(Position) {}
+
+ bool operator==(const isl_iterator &O) const {
+ return List == O.List && Position == O.Position;
+ }
+
+ isl_iterator &operator++() {
+ ++Position;
+ return *this;
+ }
+
+ isl_iterator operator++(int) {
+ isl_iterator Copy{*this};
+ ++Position;
+ return Copy;
+ }
+
+ ElementT operator*() const { return List->get_at(this->Position); }
+
+protected:
+ const ListT *List;
+ int Position = 0;
+};
+
+template <typename T> isl_iterator<T> begin(const T &t) {
+ return isl_iterator<T>(t, 0);
+}
+template <typename T> isl_iterator<T> end(const T &t) {
+ return isl_iterator<T>(t);
+}
+
+} // namespace noexceptions
+} // namespace isl
+
+namespace polly {
+
+/// Return the range elements that are lexicographically smaller.
+///
+/// @param Map { Space[] -> Scatter[] }
+/// @param Strict True for strictly lexicographically smaller elements (exclude
+/// same timepoints from the result).
+///
+/// @return { Space[] -> Scatter[] }
+/// A map to all timepoints that happen before the timepoints the input
+/// mapped to.
+isl::map beforeScatter(isl::map Map, bool Strict);
+
+/// Piecewise beforeScatter(isl::map,bool).
+isl::union_map beforeScatter(isl::union_map UMap, bool Strict);
+
+/// Return the range elements that are lexicographically larger.
+///
+/// @param Map { Space[] -> Scatter[] }
+/// @param Strict True for strictly lexicographically larger elements (exclude
+/// same timepoints from the result).
+///
+/// @return { Space[] -> Scatter[] }
+/// A map to all timepoints that happen after the timepoints the input
+/// map originally mapped to.
+isl::map afterScatter(isl::map Map, bool Strict);
+
+/// Piecewise afterScatter(isl::map,bool).
+isl::union_map afterScatter(const isl::union_map &UMap, bool Strict);
+
+/// Construct a range of timepoints between two timepoints.
+///
+/// Example:
+/// From := { A[] -> [0]; B[] -> [0] }
+/// To := { B[] -> [10]; C[] -> [20] }
+///
+/// Result:
+/// { B[] -> [i] : 0 < i < 10 }
+///
+/// Note that A[] and C[] are not in the result because they do not have a start
+/// or end timepoint. If a start (or end) timepoint is not unique, the first
+/// (respectively last) is chosen.
+///
+/// @param From { Space[] -> Scatter[] }
+/// Map to start timepoints.
+/// @param To { Space[] -> Scatter[] }
+/// Map to end timepoints.
+/// @param InclFrom Whether to include the start timepoints in the result. In
+/// the example, this would add { B[] -> [0] }
+/// @param InclTo Whether to include the end timepoints in the result. In this
+/// example, this would add { B[] -> [10] }
+///
+/// @return { Space[] -> Scatter[] }
+/// A map for each domain element of timepoints between two extreme
+/// points, or nullptr if @p From or @p To is nullptr, or the isl max
+/// operations is exceeded.
+isl::map betweenScatter(isl::map From, isl::map To, bool InclFrom, bool InclTo);
+
+/// Piecewise betweenScatter(isl::map,isl::map,bool,bool).
+isl::union_map betweenScatter(isl::union_map From, isl::union_map To,
+ bool InclFrom, bool InclTo);
+
+/// If by construction a union map is known to contain only a single map, return
+/// it.
+///
+/// This function combines isl_map_from_union_map() and
+/// isl_union_map_extract_map(). isl_map_from_union_map() fails if the map is
+/// empty because it does not know which space it would be in.
+/// isl_union_map_extract_map() on the other hand does not check whether there
+/// is (at most) one isl_map in the union, i.e. how it has been constructed is
+/// probably wrong.
+isl::map singleton(isl::union_map UMap, isl::space ExpectedSpace);
+
+/// If by construction an isl_union_set is known to contain only a single
+/// isl_set, return it.
+///
+/// This function combines isl_set_from_union_set() and
+/// isl_union_set_extract_set(). isl_map_from_union_set() fails if the set is
+/// empty because it does not know which space it would be in.
+/// isl_union_set_extract_set() on the other hand does not check whether there
+/// is (at most) one isl_set in the union, i.e. how it has been constructed is
+/// probably wrong.
+isl::set singleton(isl::union_set USet, isl::space ExpectedSpace);
+
+/// Determine how many dimensions the scatter space of @p Schedule has.
+///
+/// The schedule must not be empty and have equal number of dimensions of any
+/// subspace it contains.
+///
+/// The implementation currently returns the maximum number of dimensions it
+/// encounters, if different, and 0 if none is encountered. However, most other
+/// code will most likely fail if one of these happen.
+unsigned getNumScatterDims(const isl::union_map &Schedule);
+
+/// Return the scatter space of a @p Schedule.
+///
+/// This is basically the range space of the schedule map, but harder to
+/// determine because it is an isl_union_map.
+isl::space getScatterSpace(const isl::union_map &Schedule);
+
+/// Construct an identity map for the given domain values.
+///
+/// @param USet { Space[] }
+/// The returned map's domain and range.
+/// @param RestrictDomain If true, the returned map only maps elements contained
+/// in @p Set and no other. If false, it returns an
+/// overapproximation with the identity maps of any space
+/// in @p Set, not just the elements in it.
+///
+/// @return { Space[] -> Space[] }
+/// A map that maps each value of @p Set to itself.
+isl::map makeIdentityMap(const isl::set &Set, bool RestrictDomain);
+
+/// Construct an identity map for the given domain values.
+///
+/// There is no type resembling isl_union_space, hence we have to pass an
+/// isl_union_set as the map's domain and range space.
+///
+/// @param USet { Space[] }
+/// The returned map's domain and range.
+/// @param RestrictDomain If true, the returned map only maps elements contained
+/// in @p USet and no other. If false, it returns an
+/// overapproximation with the identity maps of any space
+/// in @p USet, not just the elements in it.
+///
+/// @return { Space[] -> Space[] }
+/// A map that maps each value of @p USet to itself.
+isl::union_map makeIdentityMap(const isl::union_set &USet, bool RestrictDomain);
+
+/// Reverse the nested map tuple in @p Map's domain.
+///
+/// @param Map { [Space1[] -> Space2[]] -> Space3[] }
+///
+/// @return { [Space2[] -> Space1[]] -> Space3[] }
+isl::map reverseDomain(isl::map Map);
+
+/// Piecewise reverseDomain(isl::map).
+isl::union_map reverseDomain(const isl::union_map &UMap);
+
+/// Add a constant to one dimension of a set.
+///
+/// @param Map The set to shift a dimension in.
+/// @param Pos The dimension to shift. If negative, the dimensions are
+/// counted from the end instead from the beginning. E.g. -1 is
+/// the last dimension in the tuple.
+/// @param Amount The offset to add to the specified dimension.
+///
+/// @return The modified set.
+isl::set shiftDim(isl::set Set, int Pos, int Amount);
+
+/// Piecewise shiftDim(isl::set,int,int).
+isl::union_set shiftDim(isl::union_set USet, int Pos, int Amount);
+
+/// Add a constant to one dimension of a map.
+///
+/// @param Map The map to shift a dimension in.
+/// @param Type A tuple of @p Map which contains the dimension to shift.
+/// @param Pos The dimension to shift. If negative, the dimensions are
+/// counted from the end instead from the beginning. Eg. -1 is the last
+/// dimension in the tuple.
+/// @param Amount The offset to add to the specified dimension.
+///
+/// @return The modified map.
+isl::map shiftDim(isl::map Map, isl::dim Dim, int Pos, int Amount);
+
+/// Add a constant to one dimension of a each map in a union map.
+///
+/// @param UMap The maps to shift a dimension in.
+/// @param Type The tuple which contains the dimension to shift.
+/// @param Pos The dimension to shift. If negative, the dimensions are
+/// counted from the ends of each map of union instead from their
+/// beginning. E.g. -1 is the last dimension of any map.
+/// @param Amount The offset to add to the specified dimension.
+///
+/// @return The union of all modified maps.
+isl::union_map shiftDim(isl::union_map UMap, isl::dim Dim, int Pos, int Amount);
+
+/// Simplify a set inplace.
+void simplify(isl::set &Set);
+
+/// Simplify a union set inplace.
+void simplify(isl::union_set &USet);
+
+/// Simplify a map inplace.
+void simplify(isl::map &Map);
+
+/// Simplify a union map inplace.
+void simplify(isl::union_map &UMap);
+
+/// Compute the reaching definition statement or the next overwrite for each
+/// definition of an array element.
+///
+/// The reaching definition of an array element at a specific timepoint is the
+/// statement instance that has written the current element's content.
+/// Alternatively, this function determines for each timepoint and element which
+/// write is going to overwrite an element at a future timepoint. This can be
+/// seen as "reaching definition in reverse" where definitions are found in the
+/// past.
+///
+/// For example:
+///
+/// Schedule := { Write[] -> [0]; Overwrite[] -> [10] }
+/// Defs := { Write[] -> A[5]; Overwrite[] -> A[5] }
+///
+/// If index 5 of array A is written at timepoint 0 and 10, the resulting
+/// reaching definitions are:
+///
+/// { [A[5] -> [i]] -> Write[] : 0 < i < 10;
+/// [A[5] -> [i]] -> Overwrite[] : 10 < i }
+///
+/// Between timepoint 0 (Write[]) and timepoint 10 (Overwrite[]), the
+/// content of A[5] is written by statement instance Write[] and after
+/// timepoint 10 by Overwrite[]. Values not defined in the map have no known
+/// definition. This includes the statement instance timepoints themselves,
+/// because reads at those timepoints could either read the old or the new
+/// value, defined only by the statement itself. But this can be changed by @p
+/// InclPrevDef and @p InclNextDef. InclPrevDef=false and InclNextDef=true
+/// returns a zone. Unless @p InclPrevDef and @p InclNextDef are both true,
+/// there is only one unique definition per element and timepoint.
+///
+/// @param Schedule { DomainWrite[] -> Scatter[] }
+/// Schedule of (at least) all array writes. Instances not in
+/// @p Writes are ignored.
+/// @param Writes { DomainWrite[] -> Element[] }
+/// Elements written to by the statement instances.
+/// @param Reverse If true, look for definitions in the future. That is,
+/// find the write that is overwrites the current value.
+/// @param InclPrevDef Include the definition's timepoint to the set of
+/// well-defined elements (any load at that timepoint happen
+/// at the writes). In the example, enabling this option adds
+/// {[A[5] -> [0]] -> Write[]; [A[5] -> [10]] -> Overwrite[]}
+/// to the result.
+/// @param InclNextDef Whether to assume that at the timepoint where an element
+/// is overwritten, it still contains the old value (any load
+/// at that timepoint would happen before the overwrite). In
+/// this example, enabling this adds
+/// { [A[] -> [10]] -> Write[] } to the result.
+///
+/// @return { [Element[] -> Scatter[]] -> DomainWrite[] }
+/// The reaching definitions or future overwrite as described above, or
+/// nullptr if either @p Schedule or @p Writes is nullptr, or the isl
+/// max operations count has exceeded.
+isl::union_map computeReachingWrite(isl::union_map Schedule,
+ isl::union_map Writes, bool Reverse,
+ bool InclPrevDef, bool InclNextDef);
+
+/// Compute the timepoints where the contents of an array element are not used.
+///
+/// An element is unused at a timepoint when the element is overwritten in
+/// the future, but it is not read in between. Another way to express this: the
+/// time from when the element is written, to the most recent read before it, or
+/// infinitely into the past if there is no read before. Such unused elements
+/// can be overwritten by any value without changing the scop's semantics. An
+/// example:
+///
+/// Schedule := { Read[] -> [0]; Write[] -> [10]; Def[] -> [20] }
+/// Writes := { Write[] -> A[5]; Def[] -> A[6] }
+/// Reads := { Read[] -> A[5] }
+///
+/// The result is:
+///
+/// { A[5] -> [i] : 0 < i < 10;
+/// A[6] -> [i] : i < 20 }
+///
+/// That is, A[5] is unused between timepoint 0 (the read) and timepoint 10 (the
+/// write). A[6] is unused before timepoint 20, but might be used after the
+/// scop's execution (A[5] and any other A[i] as well). Use InclLastRead=false
+/// and InclWrite=true to interpret the result as zone.
+///
+/// @param Schedule { Domain[] -> Scatter[] }
+/// The schedule of (at least) all statement instances
+/// occurring in @p Writes or @p Reads. All other
+/// instances are ignored.
+/// @param Writes { DomainWrite[] -> Element[] }
+/// Elements written to by the statement instances.
+/// @param Reads { DomainRead[] -> Element[] }
+/// Elements read from by the statement instances.
+/// @param ReadEltInSameInst Whether a load reads the value from a write
+/// that is scheduled at the same timepoint (Writes
+/// happen before reads). Otherwise, loads use the
+/// value of an element that it had before the
+/// timepoint (Reads before writes). For example:
+/// { Read[] -> [0]; Write[] -> [0] }
+/// With ReadEltInSameInst=false it is assumed that the
+/// read happens before the write, such that the
+/// element is never unused, or just at timepoint 0,
+/// depending on InclLastRead/InclWrite.
+/// With ReadEltInSameInst=false it assumes that the
+/// value just written is used. Anything before
+/// timepoint 0 is considered unused.
+/// @param InclLastRead Whether a timepoint where an element is last read
+/// counts as unused (the read happens at the beginning
+/// of its timepoint, and nothing (else) can use it
+/// during the timepoint). In the example, this option
+/// adds { A[5] -> [0] } to the result.
+/// @param InclWrite Whether the timepoint where an element is written
+/// itself counts as unused (the write happens at the
+/// end of its timepoint; no (other) operations uses
+/// the element during the timepoint). In this example,
+/// this adds
+/// { A[5] -> [10]; A[6] -> [20] } to the result.
+///
+/// @return { Element[] -> Scatter[] }
+/// The unused timepoints as defined above, or nullptr if either @p
+/// Schedule, @p Writes are @p Reads is nullptr, or the ISL max
+/// operations count is exceeded.
+isl::union_map computeArrayUnused(isl::union_map Schedule,
+ isl::union_map Writes, isl::union_map Reads,
+ bool ReadEltInSameInst, bool InclLastRead,
+ bool InclWrite);
+
+/// Convert a zone (range between timepoints) to timepoints.
+///
+/// A zone represents the time between (integer) timepoints, but not the
+/// timepoints themselves. This function can be used to determine whether a
+/// timepoint lies within a zone.
+///
+/// For instance, the range (1,3), representing the time between 1 and 3, is
+/// represented by the zone
+///
+/// { [i] : 1 < i <= 3 }
+///
+/// The set of timepoints that lie completely within this range is
+///
+/// { [i] : 1 < i < 3 }
+///
+/// A typical use-case is the range in which a value written by a store is
+/// available until it is overwritten by another value. If the write is at
+/// timepoint 1 and its value is overwritten by another value at timepoint 3,
+/// the value is available between those timepoints: timepoint 2 in this
+/// example.
+///
+///
+/// When InclStart is true, the range is interpreted left-inclusive, i.e. adds
+/// the timepoint 1 to the result:
+///
+/// { [i] : 1 <= i < 3 }
+///
+/// In the use-case mentioned above that means that the value written at
+/// timepoint 1 is already available in timepoint 1 (write takes place before
+/// any read of it even if executed at the same timepoint)
+///
+/// When InclEnd is true, the range is interpreted right-inclusive, i.e. adds
+/// the timepoint 3 to the result:
+///
+/// { [i] : 1 < i <= 3 }
+///
+/// In the use-case mentioned above that means that although the value is
+/// overwritten in timepoint 3, the old value is still available at timepoint 3
+/// (write takes place after any read even if executed at the same timepoint)
+///
+/// @param Zone { Zone[] }
+/// @param InclStart Include timepoints adjacent to the beginning of a zone.
+/// @param InclEnd Include timepoints adjacent to the ending of a zone.
+///
+/// @return { Scatter[] }
+isl::union_set convertZoneToTimepoints(isl::union_set Zone, bool InclStart,
+ bool InclEnd);
+
+/// Like convertZoneToTimepoints(isl::union_set,InclStart,InclEnd), but convert
+/// either the domain or the range of a map.
+isl::union_map convertZoneToTimepoints(isl::union_map Zone, isl::dim Dim,
+ bool InclStart, bool InclEnd);
+
+/// Overload of convertZoneToTimepoints(isl::map,InclStart,InclEnd) to process
+/// only a single map.
+isl::map convertZoneToTimepoints(isl::map Zone, isl::dim Dim, bool InclStart,
+ bool InclEnd);
+
+/// Distribute the domain to the tuples of a wrapped range map.
+///
+/// @param Map { Domain[] -> [Range1[] -> Range2[]] }
+///
+/// @return { [Domain[] -> Range1[]] -> [Domain[] -> Range2[]] }
+isl::map distributeDomain(isl::map Map);
+
+/// Apply distributeDomain(isl::map) to each map in the union.
+isl::union_map distributeDomain(isl::union_map UMap);
+
+/// Prepend a space to the tuples of a map.
+///
+/// @param UMap { Domain[] -> Range[] }
+/// @param Factor { Factor[] }
+///
+/// @return { [Factor[] -> Domain[]] -> [Factor[] -> Range[]] }
+isl::union_map liftDomains(isl::union_map UMap, isl::union_set Factor);
+
+/// Apply a map to the 'middle' of another relation.
+///
+/// @param UMap { [DomainDomain[] -> DomainRange[]] -> Range[] }
+/// @param Func { DomainRange[] -> NewDomainRange[] }
+///
+/// @return { [DomainDomain[] -> NewDomainRange[]] -> Range[] }
+isl::union_map applyDomainRange(isl::union_map UMap, isl::union_map Func);
+
+/// Intersect the range of @p Map with @p Range.
+///
+/// Since @p Map is an isl::map, the result will be a single space, even though
+/// @p Range is an isl::union_set. This is the only difference to
+/// isl::map::intersect_range and isl::union_map::interset_range.
+///
+/// @param Map { Domain[] -> Range[] }
+/// @param Range { Range[] }
+///
+/// @return { Domain[] -> Range[] }
+isl::map intersectRange(isl::map Map, isl::union_set Range);
+
+/// Subtract the parameter space @p Params from @p Map.
+/// This is akin to isl::map::intersect_params.
+///
+/// Example:
+/// subtractParams(
+/// { [i] -> [i] },
+/// [x] -> { : x < 0 }
+/// ) = [x] -> { [i] -> [i] : x >= 0 }
+///
+/// @param Map Remove the conditions of @p Params from this map.
+/// @param Params Parameter set to subtract.
+///
+/// @param The map with the parameter conditions removed.
+isl::map subtractParams(isl::map Map, isl::set Params);
+
+/// Subtract the parameter space @p Params from @p Set.
+isl::set subtractParams(isl::set Set, isl::set Params);
+
+/// If @p PwAff maps to a constant, return said constant. If @p Max/@p Min, it
+/// can also be a piecewise constant and it would return the minimum/maximum
+/// value. Otherwise, return NaN.
+isl::val getConstant(isl::pw_aff PwAff, bool Max, bool Min);
+
+/// Check that @p End is valid and return an iterator from @p Begin to @p End
+///
+/// Use case example:
+/// for (unsigned i : rangeIslSize(0, Map.domain_tuple_dim()))
+/// // do stuff
+llvm::iota_range<unsigned> rangeIslSize(unsigned Begin, isl::size End);
+
+/// Dump a description of the argument to llvm::errs().
+///
+/// In contrast to isl's dump function, there are a few differences:
+/// - Each polyhedron (pieces) is written on its own line.
+/// - Spaces are sorted by structure. E.g. maps with same domain space are
+/// grouped. Isl sorts them according to the space's hash function.
+/// - Pieces of the same space are sorted using their lower bound.
+/// - A more compact to_str representation is used instead of Isl's dump
+/// functions that try to show the internal representation.
+///
+/// The goal is to get a better understandable representation that is also
+/// useful to compare two sets. As all dump() functions, its intended use is to
+/// be called in a debugger only.
+///
+/// isl_map_dump example:
+/// [p_0, p_1, p_2] -> { Stmt0[i0] -> [o0, o1] : (o0 = i0 and o1 = 0 and i0 > 0
+/// and i0 <= 5 - p_2) or (i0 = 0 and o0 = 0 and o1 = 0); Stmt3[i0] -> [o0, o1]
+/// : (o0 = i0 and o1 = 3 and i0 > 0 and i0 <= 5 - p_2) or (i0 = 0 and o0 = 0
+/// and o1 = 3); Stmt2[i0] -> [o0, o1] : (o0 = i0 and o1 = 1 and i0 >= 3 + p_0 -
+/// p_1 and i0 > 0 and i0 <= 5 - p_2) or (o0 = i0 and o1 = 1 and i0 > 0 and i0
+/// <= 5 - p_2 and i0 < p_0 - p_1) or (i0 = 0 and o0 = 0 and o1 = 1 and p_1 >= 3
+/// + p_0) or (i0 = 0 and o0 = 0 and o1 = 1 and p_1 < p_0) or (p_0 = 0 and i0 =
+/// 2 - p_1 and o0 = 2 - p_1 and o1 = 1 and p_2 <= 3 + p_1 and p_1 <= 1) or (p_1
+/// = 1 + p_0 and i0 = 0 and o0 = 0 and o1 = 1) or (p_0 = 0 and p_1 = 2 and i0 =
+/// 0 and o0 = 0 and o1 = 1) or (p_0 = -1 and p_1 = -1 and i0 = 0 and o0 = 0 and
+/// o1 = 1); Stmt1[i0] -> [o0, o1] : (p_0 = -1 and i0 = 1 - p_1 and o0 = 1 - p_1
+/// and o1 = 2 and p_2 <= 4 + p_1 and p_1 <= 0) or (p_0 = 0 and i0 = -p_1 and o0
+/// = -p_1 and o1 = 2 and p_2 <= 5 + p_1 and p_1 < 0) or (p_0 = -1 and p_1 = 1
+/// and i0 = 0 and o0 = 0 and o1 = 2) or (p_0 = 0 and p_1 = 0 and i0 = 0 and o0
+/// = 0 and o1 = 2) }
+///
+/// dumpPw example (same set):
+/// [p_0, p_1, p_2] -> {
+/// Stmt0[0] -> [0, 0];
+/// Stmt0[i0] -> [i0, 0] : 0 < i0 <= 5 - p_2;
+/// Stmt1[0] -> [0, 2] : p_1 = 1 and p_0 = -1;
+/// Stmt1[0] -> [0, 2] : p_1 = 0 and p_0 = 0;
+/// Stmt1[1 - p_1] -> [1 - p_1, 2] : p_0 = -1 and p_1 <= 0 and p_2 <= 4 + p_1;
+/// Stmt1[-p_1] -> [-p_1, 2] : p_0 = 0 and p_1 < 0 and p_2 <= 5 + p_1;
+/// Stmt2[0] -> [0, 1] : p_1 >= 3 + p_0;
+/// Stmt2[0] -> [0, 1] : p_1 < p_0;
+/// Stmt2[0] -> [0, 1] : p_1 = 1 + p_0;
+/// Stmt2[0] -> [0, 1] : p_1 = 2 and p_0 = 0;
+/// Stmt2[0] -> [0, 1] : p_1 = -1 and p_0 = -1;
+/// Stmt2[i0] -> [i0, 1] : i0 >= 3 + p_0 - p_1 and 0 < i0 <= 5 - p_2;
+/// Stmt2[i0] -> [i0, 1] : 0 < i0 <= 5 - p_2 and i0 < p_0 - p_1;
+/// Stmt2[2 - p_1] -> [2 - p_1, 1] : p_0 = 0 and p_1 <= 1 and p_2 <= 3 + p_1;
+/// Stmt3[0] -> [0, 3];
+/// Stmt3[i0] -> [i0, 3] : 0 < i0 <= 5 - p_2
+/// }
+/// @{
+void dumpPw(const isl::set &Set);
+void dumpPw(const isl::map &Map);
+void dumpPw(const isl::union_set &USet);
+void dumpPw(const isl::union_map &UMap);
+void dumpPw(__isl_keep isl_set *Set);
+void dumpPw(__isl_keep isl_map *Map);
+void dumpPw(__isl_keep isl_union_set *USet);
+void dumpPw(__isl_keep isl_union_map *UMap);
+/// @}
+
+/// Dump all points of the argument to llvm::errs().
+///
+/// Before being printed by dumpPw(), the argument's pieces are expanded to
+/// contain only single points. If a dimension is unbounded, it keeps its
+/// representation.
+///
+/// This is useful for debugging reduced cases where parameters are set to
+/// constants to keep the example simple. Such sets can still contain
+/// existential dimensions which makes the polyhedral hard to compare.
+///
+/// Example:
+/// { [MemRef_A[i0] -> [i1]] : (exists (e0 = floor((1 + i1)/3): i0 = 1 and 3e0
+/// <= i1 and 3e0 >= -1 + i1 and i1 >= 15 and i1 <= 25)) or (exists (e0 =
+/// floor((i1)/3): i0 = 0 and 3e0 < i1 and 3e0 >= -2 + i1 and i1 > 0 and i1 <=
+/// 11)) }
+///
+/// dumpExpanded:
+/// {
+/// [MemRef_A[0] ->[1]];
+/// [MemRef_A[0] ->[2]];
+/// [MemRef_A[0] ->[4]];
+/// [MemRef_A[0] ->[5]];
+/// [MemRef_A[0] ->[7]];
+/// [MemRef_A[0] ->[8]];
+/// [MemRef_A[0] ->[10]];
+/// [MemRef_A[0] ->[11]];
+/// [MemRef_A[1] ->[15]];
+/// [MemRef_A[1] ->[16]];
+/// [MemRef_A[1] ->[18]];
+/// [MemRef_A[1] ->[19]];
+/// [MemRef_A[1] ->[21]];
+/// [MemRef_A[1] ->[22]];
+/// [MemRef_A[1] ->[24]];
+/// [MemRef_A[1] ->[25]]
+/// }
+/// @{
+void dumpExpanded(const isl::set &Set);
+void dumpExpanded(const isl::map &Map);
+void dumpExpanded(const isl::union_set &USet);
+void dumpExpanded(const isl::union_map &UMap);
+void dumpExpanded(__isl_keep isl_set *Set);
+void dumpExpanded(__isl_keep isl_map *Map);
+void dumpExpanded(__isl_keep isl_union_set *USet);
+void dumpExpanded(__isl_keep isl_union_map *UMap);
+/// @}
+} // namespace polly
+
+#endif /* POLLY_ISLTOOLS_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Support/SCEVAffinator.h b/contrib/libs/llvm14/tools/polly/include/polly/Support/SCEVAffinator.h
new file mode 100644
index 00000000000..a555f6c3426
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Support/SCEVAffinator.h
@@ -0,0 +1,124 @@
+//===------ polly/SCEVAffinator.h - Create isl expressions from SCEVs -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Create a polyhedral description for a SCEV value.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SCEV_AFFINATOR_H
+#define POLLY_SCEV_AFFINATOR_H
+
+#include "polly/Support/ScopHelper.h"
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "isl/isl-noexceptions.h"
+
+namespace polly {
+class Scop;
+
+/// The result type of the SCEVAffinator.
+///
+/// The first element of the pair is the isl representation of the SCEV, the
+/// second is the domain under which it is __invalid__.
+typedef std::pair<isl::pw_aff, isl::set> PWACtx;
+
+/// Translate a SCEV to an isl::pw_aff and the domain on which it is invalid.
+struct SCEVAffinator : public llvm::SCEVVisitor<SCEVAffinator, PWACtx> {
+public:
+ SCEVAffinator(Scop *S, llvm::LoopInfo &LI);
+
+ /// Translate a SCEV to an isl::pw_aff.
+ ///
+ /// @param E he expression that is translated.
+ /// @param BB The block in which @p E is executed.
+ ///
+ /// @returns The isl representation of the SCEV @p E in @p Domain.
+ PWACtx getPwAff(const llvm::SCEV *E, llvm::BasicBlock *BB = nullptr,
+ RecordedAssumptionsTy *RecordedAssumptions = nullptr);
+
+ /// Take the assumption that @p PWAC is non-negative.
+ void takeNonNegativeAssumption(
+ PWACtx &PWAC, RecordedAssumptionsTy *RecordedAssumptions = nullptr);
+
+ /// Interpret the PWA in @p PWAC as an unsigned value.
+ void interpretAsUnsigned(PWACtx &PWAC, unsigned Width);
+
+ /// Check an <nsw> AddRec for the loop @p L is cached.
+ bool hasNSWAddRecForLoop(llvm::Loop *L) const;
+
+ /// Return the LoopInfo used by thi object.
+ llvm::LoopInfo *getLI() const { return &LI; }
+
+private:
+ /// Key to identify cached expressions.
+ using CacheKey = std::pair<const llvm::SCEV *, llvm::BasicBlock *>;
+
+ /// Map to remembered cached expressions.
+ llvm::DenseMap<CacheKey, PWACtx> CachedExpressions;
+
+ Scop *S;
+ isl::ctx Ctx;
+ unsigned NumIterators;
+ llvm::ScalarEvolution &SE;
+ llvm::LoopInfo &LI;
+ llvm::BasicBlock *BB;
+ RecordedAssumptionsTy *RecordedAssumptions = nullptr;
+
+ /// Target data for element size computing.
+ const llvm::DataLayout &TD;
+
+ /// Return the loop for the current block if any.
+ llvm::Loop *getScope();
+
+ /// Return a PWACtx for @p PWA that is always valid.
+ PWACtx getPWACtxFromPWA(isl::pw_aff PWA);
+
+ /// Compute the non-wrapping version of @p PWA for type @p ExprType.
+ ///
+ /// @param PWA The piece-wise affine function that might wrap.
+ /// @param Type The type of the SCEV that was translated to @p PWA.
+ ///
+ /// @returns The expr @p PWA modulo the size constraints of @p ExprType.
+ isl::pw_aff addModuloSemantic(isl::pw_aff PWA, llvm::Type *ExprType) const;
+
+ /// If @p Expr might cause an integer wrap record an assumption.
+ ///
+ /// @param Expr The SCEV expression that might wrap.
+ /// @param PWAC The isl representation of @p Expr with the invalid domain.
+ ///
+ /// @returns The isl representation @p PWAC with a possibly adjusted domain.
+ PWACtx checkForWrapping(const llvm::SCEV *Expr, PWACtx PWAC) const;
+
+ /// Whether to track the value of this expression precisely, rather than
+ /// assuming it won't wrap.
+ bool computeModuloForExpr(const llvm::SCEV *Expr);
+
+ PWACtx visit(const llvm::SCEV *E);
+ PWACtx visitConstant(const llvm::SCEVConstant *E);
+ PWACtx visitPtrToIntExpr(const llvm::SCEVPtrToIntExpr *E);
+ PWACtx visitTruncateExpr(const llvm::SCEVTruncateExpr *E);
+ PWACtx visitZeroExtendExpr(const llvm::SCEVZeroExtendExpr *E);
+ PWACtx visitSignExtendExpr(const llvm::SCEVSignExtendExpr *E);
+ PWACtx visitAddExpr(const llvm::SCEVAddExpr *E);
+ PWACtx visitMulExpr(const llvm::SCEVMulExpr *E);
+ PWACtx visitUDivExpr(const llvm::SCEVUDivExpr *E);
+ PWACtx visitAddRecExpr(const llvm::SCEVAddRecExpr *E);
+ PWACtx visitSMaxExpr(const llvm::SCEVSMaxExpr *E);
+ PWACtx visitSMinExpr(const llvm::SCEVSMinExpr *E);
+ PWACtx visitUMaxExpr(const llvm::SCEVUMaxExpr *E);
+ PWACtx visitUMinExpr(const llvm::SCEVUMinExpr *E);
+ PWACtx visitSequentialUMinExpr(const llvm::SCEVSequentialUMinExpr *E);
+ PWACtx visitUnknown(const llvm::SCEVUnknown *E);
+ PWACtx visitSDivInstruction(llvm::Instruction *SDiv);
+ PWACtx visitSRemInstruction(llvm::Instruction *SRem);
+ PWACtx complexityBailout();
+
+ friend struct llvm::SCEVVisitor<SCEVAffinator, PWACtx>;
+};
+} // namespace polly
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Support/SCEVValidator.h b/contrib/libs/llvm14/tools/polly/include/polly/Support/SCEVValidator.h
new file mode 100644
index 00000000000..5e816aa533b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Support/SCEVValidator.h
@@ -0,0 +1,90 @@
+//===--- SCEVValidator.h - Detect Scops -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// Checks if a SCEV expression represents a valid affine expression.
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SCEV_VALIDATOR_H
+#define POLLY_SCEV_VALIDATOR_H
+
+#include "polly/Support/ScopHelper.h"
+
+namespace llvm {
+class SCEVConstant;
+} // namespace llvm
+
+namespace polly {
+class ScopDetection;
+
+/// Find the loops referenced from a SCEV expression.
+///
+/// @param Expr The SCEV expression to scan for loops.
+/// @param Loops A vector into which the found loops are inserted.
+void findLoops(const llvm::SCEV *Expr,
+ llvm::SetVector<const llvm::Loop *> &Loops);
+
+/// Find the values referenced by SCEVUnknowns in a given SCEV
+/// expression.
+///
+/// @param Expr The SCEV expression to scan for SCEVUnknowns.
+/// @param SE The ScalarEvolution analysis for this function.
+/// @param Values A vector into which the found values are inserted.
+void findValues(const llvm::SCEV *Expr, llvm::ScalarEvolution &SE,
+ llvm::SetVector<llvm::Value *> &Values);
+
+/// Returns true when the SCEV contains references to instructions within the
+/// region.
+///
+/// @param Expr The SCEV to analyze.
+/// @param R The region in which we look for dependences.
+/// @param Scope Location where the value is needed.
+/// @param AllowLoops Whether loop recurrences outside the loop that are in the
+/// region count as dependence.
+bool hasScalarDepsInsideRegion(const llvm::SCEV *Expr, const llvm::Region *R,
+ llvm::Loop *Scope, bool AllowLoops,
+ const InvariantLoadsSetTy &ILS);
+bool isAffineExpr(const llvm::Region *R, llvm::Loop *Scope,
+ const llvm::SCEV *Expression, llvm::ScalarEvolution &SE,
+ InvariantLoadsSetTy *ILS = nullptr);
+
+/// Check if @p V describes an affine constraint in @p R.
+bool isAffineConstraint(llvm::Value *V, const llvm::Region *R,
+ llvm::Loop *Scope, llvm::ScalarEvolution &SE,
+ ParameterSetTy &Params, bool OrExpr = false);
+
+ParameterSetTy getParamsInAffineExpr(const llvm::Region *R, llvm::Loop *Scope,
+ const llvm::SCEV *Expression,
+ llvm::ScalarEvolution &SE);
+
+/// Extract the constant factors from the multiplication @p M.
+///
+/// @param M A potential SCEV multiplication.
+/// @param SE The ScalarEvolution analysis to create new SCEVs.
+///
+/// @returns The constant factor in @p M and the rest of @p M.
+std::pair<const llvm::SCEVConstant *, const llvm::SCEV *>
+extractConstantFactor(const llvm::SCEV *M, llvm::ScalarEvolution &SE);
+
+/// Try to look through PHI nodes, where some incoming edges come from error
+/// blocks.
+///
+/// In case a PHI node follows an error block we can assume that the incoming
+/// value can only come from the node that is not an error block. As a result,
+/// conditions that seemed non-affine before are now in fact affine.
+const llvm::SCEV *tryForwardThroughPHI(const llvm::SCEV *Expr, llvm::Region &R,
+ llvm::ScalarEvolution &SE,
+ ScopDetection *SD);
+
+/// Return a unique non-error block incoming value for @p PHI if available.
+///
+/// @param R The region to run our code on.
+/// @param SD The ScopDetection
+llvm::Value *getUniqueNonErrorValue(llvm::PHINode *PHI, llvm::Region *R,
+ ScopDetection *SD);
+} // namespace polly
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Support/ScopHelper.h b/contrib/libs/llvm14/tools/polly/include/polly/Support/ScopHelper.h
new file mode 100644
index 00000000000..1219b7a97d4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Support/ScopHelper.h
@@ -0,0 +1,602 @@
+//===------ Support/ScopHelper.h -- Some Helper Functions for Scop. -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Small functions that help with LLVM-IR.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SUPPORT_IRHELPER_H
+#define POLLY_SUPPORT_IRHELPER_H
+
+#include "llvm/ADT/SetVector.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/ValueHandle.h"
+#include "isl/isl-noexceptions.h"
+
+namespace llvm {
+class LoopInfo;
+class Loop;
+class ScalarEvolution;
+class SCEV;
+class Region;
+class Pass;
+class DominatorTree;
+class RegionInfo;
+class RegionNode;
+} // namespace llvm
+
+namespace polly {
+class Scop;
+class ScopStmt;
+
+/// Enumeration of assumptions Polly can take.
+enum AssumptionKind {
+ ALIASING,
+ INBOUNDS,
+ WRAPPING,
+ UNSIGNED,
+ PROFITABLE,
+ ERRORBLOCK,
+ COMPLEXITY,
+ INFINITELOOP,
+ INVARIANTLOAD,
+ DELINEARIZATION,
+};
+
+/// Enum to distinguish between assumptions and restrictions.
+enum AssumptionSign { AS_ASSUMPTION, AS_RESTRICTION };
+
+/// Helper struct to remember assumptions.
+struct Assumption {
+ /// The kind of the assumption (e.g., WRAPPING).
+ AssumptionKind Kind;
+
+ /// Flag to distinguish assumptions and restrictions.
+ AssumptionSign Sign;
+
+ /// The valid/invalid context if this is an assumption/restriction.
+ isl::set Set;
+
+ /// The location that caused this assumption.
+ llvm::DebugLoc Loc;
+
+ /// An optional block whose domain can simplify the assumption.
+ llvm::BasicBlock *BB;
+
+ // Whether the assumption must be checked at runtime.
+ bool RequiresRTC;
+};
+
+using RecordedAssumptionsTy = llvm::SmallVector<Assumption, 8>;
+
+/// Record an assumption for later addition to the assumed context.
+///
+/// This function will add the assumption to the RecordedAssumptions. This
+/// collection will be added (@see addAssumption) to the assumed context once
+/// all paramaters are known and the context is fully built.
+///
+/// @param RecordedAssumption container which keeps all recorded assumptions.
+/// @param Kind The assumption kind describing the underlying cause.
+/// @param Set The relations between parameters that are assumed to hold.
+/// @param Loc The location in the source that caused this assumption.
+/// @param Sign Enum to indicate if the assumptions in @p Set are positive
+/// (needed/assumptions) or negative (invalid/restrictions).
+/// @param BB The block in which this assumption was taken. If it is
+/// set, the domain of that block will be used to simplify the
+/// actual assumption in @p Set once it is added. This is useful
+/// if the assumption was created prior to the domain.
+/// @param RTC Does the assumption require a runtime check?
+void recordAssumption(RecordedAssumptionsTy *RecordedAssumptions,
+ AssumptionKind Kind, isl::set Set, llvm::DebugLoc Loc,
+ AssumptionSign Sign, llvm::BasicBlock *BB = nullptr,
+ bool RTC = true);
+
+/// Type to remap values.
+using ValueMapT = llvm::DenseMap<llvm::AssertingVH<llvm::Value>,
+ llvm::AssertingVH<llvm::Value>>;
+
+/// Type for a set of invariant loads.
+using InvariantLoadsSetTy = llvm::SetVector<llvm::AssertingVH<llvm::LoadInst>>;
+
+/// Set type for parameters.
+using ParameterSetTy = llvm::SetVector<const llvm::SCEV *>;
+
+/// Set of loops (used to remember loops in non-affine subregions).
+using BoxedLoopsSetTy = llvm::SetVector<const llvm::Loop *>;
+
+/// Utility proxy to wrap the common members of LoadInst and StoreInst.
+///
+/// This works like the LLVM utility class CallSite, ie. it forwards all calls
+/// to either a LoadInst, StoreInst, MemIntrinsic or MemTransferInst.
+/// It is similar to LLVM's utility classes IntrinsicInst, MemIntrinsic,
+/// MemTransferInst, etc. in that it offers a common interface, but does not act
+/// as a fake base class.
+/// It is similar to StringRef and ArrayRef in that it holds a pointer to the
+/// referenced object and should be passed by-value as it is small enough.
+///
+/// This proxy can either represent a LoadInst instance, a StoreInst instance,
+/// a MemIntrinsic instance (memset, memmove, memcpy), a CallInst instance or a
+/// nullptr (only creatable using the default constructor); never an Instruction
+/// that is neither of the above mentioned. When representing a nullptr, only
+/// the following methods are defined:
+/// isNull(), isInstruction(), isLoad(), isStore(), ..., isMemTransferInst(),
+/// operator bool(), operator!()
+///
+/// The functions isa, cast, cast_or_null, dyn_cast are modeled te resemble
+/// those from llvm/Support/Casting.h. Partial template function specialization
+/// is currently not supported in C++ such that those cannot be used directly.
+/// (llvm::isa could, but then llvm:cast etc. would not have the expected
+/// behavior)
+class MemAccInst {
+private:
+ llvm::Instruction *I;
+
+public:
+ MemAccInst() : I(nullptr) {}
+ MemAccInst(const MemAccInst &Inst) : I(Inst.I) {}
+ /* implicit */ MemAccInst(llvm::LoadInst &LI) : I(&LI) {}
+ /* implicit */ MemAccInst(llvm::LoadInst *LI) : I(LI) {}
+ /* implicit */ MemAccInst(llvm::StoreInst &SI) : I(&SI) {}
+ /* implicit */ MemAccInst(llvm::StoreInst *SI) : I(SI) {}
+ /* implicit */ MemAccInst(llvm::MemIntrinsic *MI) : I(MI) {}
+ /* implicit */ MemAccInst(llvm::CallInst *CI) : I(CI) {}
+ explicit MemAccInst(llvm::Instruction &I) : I(&I) { assert(isa(I)); }
+ explicit MemAccInst(llvm::Instruction *I) : I(I) { assert(isa(I)); }
+
+ static bool isa(const llvm::Value &V) {
+ return llvm::isa<llvm::LoadInst>(V) || llvm::isa<llvm::StoreInst>(V) ||
+ llvm::isa<llvm::CallInst>(V) || llvm::isa<llvm::MemIntrinsic>(V);
+ }
+ static bool isa(const llvm::Value *V) {
+ return llvm::isa<llvm::LoadInst>(V) || llvm::isa<llvm::StoreInst>(V) ||
+ llvm::isa<llvm::CallInst>(V) || llvm::isa<llvm::MemIntrinsic>(V);
+ }
+ static MemAccInst cast(llvm::Value &V) {
+ return MemAccInst(llvm::cast<llvm::Instruction>(V));
+ }
+ static MemAccInst cast(llvm::Value *V) {
+ return MemAccInst(llvm::cast<llvm::Instruction>(V));
+ }
+ static MemAccInst cast_or_null(llvm::Value &V) {
+ return MemAccInst(llvm::cast<llvm::Instruction>(V));
+ }
+ static MemAccInst cast_or_null(llvm::Value *V) {
+ if (!V)
+ return MemAccInst();
+ return MemAccInst(llvm::cast<llvm::Instruction>(V));
+ }
+ static MemAccInst dyn_cast(llvm::Value &V) {
+ if (isa(V))
+ return MemAccInst(llvm::cast<llvm::Instruction>(V));
+ return MemAccInst();
+ }
+ static MemAccInst dyn_cast(llvm::Value *V) {
+ assert(V);
+ if (isa(V))
+ return MemAccInst(llvm::cast<llvm::Instruction>(V));
+ return MemAccInst();
+ }
+
+ MemAccInst &operator=(const MemAccInst &Inst) {
+ I = Inst.I;
+ return *this;
+ }
+ MemAccInst &operator=(llvm::LoadInst &LI) {
+ I = &LI;
+ return *this;
+ }
+ MemAccInst &operator=(llvm::LoadInst *LI) {
+ I = LI;
+ return *this;
+ }
+ MemAccInst &operator=(llvm::StoreInst &SI) {
+ I = &SI;
+ return *this;
+ }
+ MemAccInst &operator=(llvm::StoreInst *SI) {
+ I = SI;
+ return *this;
+ }
+ MemAccInst &operator=(llvm::MemIntrinsic &MI) {
+ I = &MI;
+ return *this;
+ }
+ MemAccInst &operator=(llvm::MemIntrinsic *MI) {
+ I = MI;
+ return *this;
+ }
+ MemAccInst &operator=(llvm::CallInst &CI) {
+ I = &CI;
+ return *this;
+ }
+ MemAccInst &operator=(llvm::CallInst *CI) {
+ I = CI;
+ return *this;
+ }
+
+ llvm::Instruction *get() const {
+ assert(I && "Unexpected nullptr!");
+ return I;
+ }
+ operator llvm::Instruction *() const { return asInstruction(); }
+ llvm::Instruction *operator->() const { return get(); }
+
+ explicit operator bool() const { return isInstruction(); }
+ bool operator!() const { return isNull(); }
+
+ llvm::Value *getValueOperand() const {
+ if (isLoad())
+ return asLoad();
+ if (isStore())
+ return asStore()->getValueOperand();
+ if (isMemIntrinsic())
+ return nullptr;
+ if (isCallInst())
+ return nullptr;
+ llvm_unreachable("Operation not supported on nullptr");
+ }
+ llvm::Value *getPointerOperand() const {
+ if (isLoad())
+ return asLoad()->getPointerOperand();
+ if (isStore())
+ return asStore()->getPointerOperand();
+ if (isMemIntrinsic())
+ return asMemIntrinsic()->getRawDest();
+ if (isCallInst())
+ return nullptr;
+ llvm_unreachable("Operation not supported on nullptr");
+ }
+
+ unsigned getAlignment() const {
+ if (isLoad())
+ return asLoad()->getAlignment();
+ if (isStore())
+ return asStore()->getAlignment();
+ if (isMemTransferInst())
+ return std::min(asMemTransferInst()->getDestAlignment(),
+ asMemTransferInst()->getSourceAlignment());
+ if (isMemIntrinsic())
+ return asMemIntrinsic()->getDestAlignment();
+ if (isCallInst())
+ return 0;
+ llvm_unreachable("Operation not supported on nullptr");
+ }
+ bool isVolatile() const {
+ if (isLoad())
+ return asLoad()->isVolatile();
+ if (isStore())
+ return asStore()->isVolatile();
+ if (isMemIntrinsic())
+ return asMemIntrinsic()->isVolatile();
+ if (isCallInst())
+ return false;
+ llvm_unreachable("Operation not supported on nullptr");
+ }
+ bool isSimple() const {
+ if (isLoad())
+ return asLoad()->isSimple();
+ if (isStore())
+ return asStore()->isSimple();
+ if (isMemIntrinsic())
+ return !asMemIntrinsic()->isVolatile();
+ if (isCallInst())
+ return true;
+ llvm_unreachable("Operation not supported on nullptr");
+ }
+ llvm::AtomicOrdering getOrdering() const {
+ if (isLoad())
+ return asLoad()->getOrdering();
+ if (isStore())
+ return asStore()->getOrdering();
+ if (isMemIntrinsic())
+ return llvm::AtomicOrdering::NotAtomic;
+ if (isCallInst())
+ return llvm::AtomicOrdering::NotAtomic;
+ llvm_unreachable("Operation not supported on nullptr");
+ }
+ bool isUnordered() const {
+ if (isLoad())
+ return asLoad()->isUnordered();
+ if (isStore())
+ return asStore()->isUnordered();
+ // Copied from the Load/Store implementation of isUnordered:
+ if (isMemIntrinsic())
+ return !asMemIntrinsic()->isVolatile();
+ if (isCallInst())
+ return true;
+ llvm_unreachable("Operation not supported on nullptr");
+ }
+
+ bool isNull() const { return !I; }
+ bool isInstruction() const { return I; }
+
+ llvm::Instruction *asInstruction() const { return I; }
+
+private:
+ bool isLoad() const { return I && llvm::isa<llvm::LoadInst>(I); }
+ bool isStore() const { return I && llvm::isa<llvm::StoreInst>(I); }
+ bool isCallInst() const { return I && llvm::isa<llvm::CallInst>(I); }
+ bool isMemIntrinsic() const { return I && llvm::isa<llvm::MemIntrinsic>(I); }
+ bool isMemSetInst() const { return I && llvm::isa<llvm::MemSetInst>(I); }
+ bool isMemTransferInst() const {
+ return I && llvm::isa<llvm::MemTransferInst>(I);
+ }
+
+ llvm::LoadInst *asLoad() const { return llvm::cast<llvm::LoadInst>(I); }
+ llvm::StoreInst *asStore() const { return llvm::cast<llvm::StoreInst>(I); }
+ llvm::CallInst *asCallInst() const { return llvm::cast<llvm::CallInst>(I); }
+ llvm::MemIntrinsic *asMemIntrinsic() const {
+ return llvm::cast<llvm::MemIntrinsic>(I);
+ }
+ llvm::MemSetInst *asMemSetInst() const {
+ return llvm::cast<llvm::MemSetInst>(I);
+ }
+ llvm::MemTransferInst *asMemTransferInst() const {
+ return llvm::cast<llvm::MemTransferInst>(I);
+ }
+};
+} // namespace polly
+
+namespace llvm {
+/// Specialize simplify_type for MemAccInst to enable dyn_cast and cast
+/// from a MemAccInst object.
+template <> struct simplify_type<polly::MemAccInst> {
+ typedef Instruction *SimpleType;
+ static SimpleType getSimplifiedValue(polly::MemAccInst &I) {
+ return I.asInstruction();
+ }
+};
+} // namespace llvm
+
+namespace polly {
+
+/// Simplify the region to have a single unconditional entry edge and a
+/// single exit edge.
+///
+/// Although this function allows DT and RI to be null, regions only work
+/// properly if the DominatorTree (for Region::contains) and RegionInfo are kept
+/// up-to-date.
+///
+/// @param R The region to be simplified
+/// @param DT DominatorTree to be updated.
+/// @param LI LoopInfo to be updated.
+/// @param RI RegionInfo to be updated.
+void simplifyRegion(llvm::Region *R, llvm::DominatorTree *DT,
+ llvm::LoopInfo *LI, llvm::RegionInfo *RI);
+
+/// Split the entry block of a function to store the newly inserted
+/// allocations outside of all Scops.
+///
+/// @param EntryBlock The entry block of the current function.
+/// @param P The pass that currently running.
+///
+void splitEntryBlockForAlloca(llvm::BasicBlock *EntryBlock, llvm::Pass *P);
+
+/// Split the entry block of a function to store the newly inserted
+/// allocations outside of all Scops.
+///
+/// @param DT DominatorTree to be updated.
+/// @param LI LoopInfo to be updated.
+/// @param RI RegionInfo to be updated.
+void splitEntryBlockForAlloca(llvm::BasicBlock *EntryBlock,
+ llvm::DominatorTree *DT, llvm::LoopInfo *LI,
+ llvm::RegionInfo *RI);
+
+/// Wrapper for SCEVExpander extended to all Polly features.
+///
+/// This wrapper will internally call the SCEVExpander but also makes sure that
+/// all additional features not represented in SCEV (e.g., SDiv/SRem are not
+/// black boxes but can be part of the function) will be expanded correctly.
+///
+/// The parameters are the same as for the creation of a SCEVExpander as well
+/// as the call to SCEVExpander::expandCodeFor:
+///
+/// @param S The current Scop.
+/// @param SE The Scalar Evolution pass.
+/// @param DL The module data layout.
+/// @param Name The suffix added to the new instruction names.
+/// @param E The expression for which code is actually generated.
+/// @param Ty The type of the resulting code.
+/// @param IP The insertion point for the new code.
+/// @param VMap A remapping of values used in @p E.
+/// @param RTCBB The last block of the RTC. Used to insert loop-invariant
+/// instructions in rare cases.
+llvm::Value *expandCodeFor(Scop &S, llvm::ScalarEvolution &SE,
+ const llvm::DataLayout &DL, const char *Name,
+ const llvm::SCEV *E, llvm::Type *Ty,
+ llvm::Instruction *IP, ValueMapT *VMap,
+ llvm::BasicBlock *RTCBB);
+
+/// Return the condition for the terminator @p TI.
+///
+/// For unconditional branches the "i1 true" condition will be returned.
+///
+/// @param TI The terminator to get the condition from.
+///
+/// @return The condition of @p TI and nullptr if none could be extracted.
+llvm::Value *getConditionFromTerminator(llvm::Instruction *TI);
+
+/// Get the smallest loop that contains @p S but is not in @p S.
+llvm::Loop *getLoopSurroundingScop(Scop &S, llvm::LoopInfo &LI);
+
+/// Get the number of blocks in @p L.
+///
+/// The number of blocks in a loop are the number of basic blocks actually
+/// belonging to the loop, as well as all single basic blocks that the loop
+/// exits to and which terminate in an unreachable instruction. We do not
+/// allow such basic blocks in the exit of a scop, hence they belong to the
+/// scop and represent run-time conditions which we want to model and
+/// subsequently speculate away.
+///
+/// @see getRegionNodeLoop for additional details.
+unsigned getNumBlocksInLoop(llvm::Loop *L);
+
+/// Get the number of blocks in @p RN.
+unsigned getNumBlocksInRegionNode(llvm::RegionNode *RN);
+
+/// Return the smallest loop surrounding @p RN.
+llvm::Loop *getRegionNodeLoop(llvm::RegionNode *RN, llvm::LoopInfo &LI);
+
+/// Check if @p LInst can be hoisted in @p R.
+///
+/// @param LInst The load to check.
+/// @param R The analyzed region.
+/// @param LI The loop info.
+/// @param SE The scalar evolution analysis.
+/// @param DT The dominator tree of the function.
+/// @param KnownInvariantLoads The invariant load set.
+///
+/// @return True if @p LInst can be hoisted in @p R.
+bool isHoistableLoad(llvm::LoadInst *LInst, llvm::Region &R, llvm::LoopInfo &LI,
+ llvm::ScalarEvolution &SE, const llvm::DominatorTree &DT,
+ const InvariantLoadsSetTy &KnownInvariantLoads);
+
+/// Return true iff @p V is an intrinsic that we ignore during code
+/// generation.
+bool isIgnoredIntrinsic(const llvm::Value *V);
+
+/// Check whether a value an be synthesized by the code generator.
+///
+/// Some value will be recalculated only from information that is code generated
+/// from the polyhedral representation. For such instructions we do not need to
+/// ensure that their operands are available during code generation.
+///
+/// @param V The value to check.
+/// @param S The current SCoP.
+/// @param SE The scalar evolution database.
+/// @param Scope Location where the value would by synthesized.
+/// @return If the instruction I can be regenerated from its
+/// scalar evolution representation, return true,
+/// otherwise return false.
+bool canSynthesize(const llvm::Value *V, const Scop &S,
+ llvm::ScalarEvolution *SE, llvm::Loop *Scope);
+
+/// Return the block in which a value is used.
+///
+/// For normal instructions, this is the instruction's parent block. For PHI
+/// nodes, this is the incoming block of that use, because this is where the
+/// operand must be defined (i.e. its definition dominates this block).
+/// Non-instructions do not use operands at a specific point such that in this
+/// case this function returns nullptr.
+llvm::BasicBlock *getUseBlock(const llvm::Use &U);
+
+// If the loop is nonaffine/boxed, return the first non-boxed surrounding loop
+// for Polly. If the loop is affine, return the loop itself.
+//
+// @param L Pointer to the Loop object to analyze.
+// @param LI Reference to the LoopInfo.
+// @param BoxedLoops Set of Boxed Loops we get from the SCoP.
+llvm::Loop *getFirstNonBoxedLoopFor(llvm::Loop *L, llvm::LoopInfo &LI,
+ const BoxedLoopsSetTy &BoxedLoops);
+
+// If the Basic Block belongs to a loop that is nonaffine/boxed, return the
+// first non-boxed surrounding loop for Polly. If the loop is affine, return
+// the loop itself.
+//
+// @param BB Pointer to the Basic Block to analyze.
+// @param LI Reference to the LoopInfo.
+// @param BoxedLoops Set of Boxed Loops we get from the SCoP.
+llvm::Loop *getFirstNonBoxedLoopFor(llvm::BasicBlock *BB, llvm::LoopInfo &LI,
+ const BoxedLoopsSetTy &BoxedLoops);
+
+/// Is the given instruction a call to a debug function?
+///
+/// A debug function can be used to insert output in Polly-optimized code which
+/// normally does not allow function calls with side-effects. For instance, a
+/// printf can be inserted to check whether a value still has the expected value
+/// after Polly generated code:
+///
+/// int sum = 0;
+/// for (int i = 0; i < 16; i+=1) {
+/// sum += i;
+/// printf("The value of sum at i=%d is %d\n", sum, i);
+/// }
+bool isDebugCall(llvm::Instruction *Inst);
+
+/// Does the statement contain a call to a debug function?
+///
+/// Such a statement must not be removed, even if has no side-effects.
+bool hasDebugCall(ScopStmt *Stmt);
+
+/// Find a property value in a LoopID.
+///
+/// Generally, a property MDNode has the format
+///
+/// !{ !"Name", value }
+///
+/// In which case the value is returned.
+///
+/// If the property is just
+///
+/// !{ !"Name" }
+///
+/// Then `nullptr` is set to mark the property is existing, but does not carry
+/// any value. If the property does not exist, `None` is returned.
+llvm::Optional<llvm::Metadata *> findMetadataOperand(llvm::MDNode *LoopMD,
+ llvm::StringRef Name);
+
+/// Find a boolean property value in a LoopID. The value not being defined is
+/// interpreted as a false value.
+bool getBooleanLoopAttribute(llvm::MDNode *LoopID, llvm::StringRef Name);
+
+/// Find an integers property value in a LoopID.
+llvm::Optional<int> getOptionalIntLoopAttribute(llvm::MDNode *LoopID,
+ llvm::StringRef Name);
+
+/// Does the loop's LoopID contain a 'llvm.loop.disable_heuristics' property?
+///
+/// This is equivalent to llvm::hasDisableAllTransformsHint(Loop*), but
+/// including the LoopUtils.h header indirectly also declares llvm::MemoryAccess
+/// which clashes with polly::MemoryAccess. Declaring this alias here avoid
+/// having to include LoopUtils.h in other files.
+bool hasDisableAllTransformsHint(llvm::Loop *L);
+bool hasDisableAllTransformsHint(llvm::MDNode *LoopID);
+
+/// Represent the attributes of a loop.
+struct BandAttr {
+ /// LoopID which stores the properties of the loop, such as transformations to
+ /// apply and the metadata of followup-loops.
+ ///
+ /// Cannot be used to identify a loop. Two different loops can have the same
+ /// metadata.
+ llvm::MDNode *Metadata = nullptr;
+
+ /// The LoopInfo reference for this loop.
+ ///
+ /// Only loops from the original IR are represented by LoopInfo. Loops that
+ /// were generated by Polly are not tracked by LoopInfo.
+ llvm::Loop *OriginalLoop = nullptr;
+};
+
+/// Get an isl::id representing a loop.
+///
+/// This takes the ownership of the BandAttr and will be free'd when the
+/// returned isl::Id is free'd.
+isl::id getIslLoopAttr(isl::ctx Ctx, BandAttr *Attr);
+
+/// Create an isl::id that identifies an original loop.
+///
+/// Return nullptr if the loop does not need a BandAttr (i.e. has no
+/// properties);
+///
+/// This creates a BandAttr which must be unique per loop and therefore this
+/// must not be called multiple times on the same loop as their id would be
+/// different.
+isl::id createIslLoopAttr(isl::ctx Ctx, llvm::Loop *L);
+
+/// Is @p Id representing a loop?
+///
+/// Such ids contain a polly::BandAttr as its user pointer.
+bool isLoopAttr(const isl::id &Id);
+
+/// Return the BandAttr of a loop's isl::id.
+BandAttr *getLoopAttr(const isl::id &Id);
+
+} // namespace polly
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Support/ScopLocation.h b/contrib/libs/llvm14/tools/polly/include/polly/Support/ScopLocation.h
new file mode 100644
index 00000000000..a6d577948f9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Support/ScopLocation.h
@@ -0,0 +1,34 @@
+//=== ScopLocation.h -- Debug location helper for ScopDetection -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Helper function for extracting region debug information.
+//
+//===----------------------------------------------------------------------===//
+//
+#ifndef POLLY_SCOP_LOCATION_H
+#define POLLY_SCOP_LOCATION_H
+
+#include <string>
+
+namespace llvm {
+class Region;
+} // namespace llvm
+
+namespace polly {
+
+/// Get the location of a region from the debug info.
+///
+/// @param R The region to get debug info for.
+/// @param LineBegin The first line in the region.
+/// @param LineEnd The last line in the region.
+/// @param FileName The filename where the region was defined.
+void getDebugLocation(const llvm::Region *R, unsigned &LineBegin,
+ unsigned &LineEnd, std::string &FileName);
+} // namespace polly
+
+#endif // POLLY_SCOP_LOCATION_H
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/Support/VirtualInstruction.h b/contrib/libs/llvm14/tools/polly/include/polly/Support/VirtualInstruction.h
new file mode 100644
index 00000000000..c9746d82d17
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/Support/VirtualInstruction.h
@@ -0,0 +1,346 @@
+//===------ VirtualInstruction.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Tools for determining which instructions are within a statement and the
+// nature of their operands.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_SUPPORT_VIRTUALINSTRUCTION_H
+#define POLLY_SUPPORT_VIRTUALINSTRUCTION_H
+
+#include "polly/ScopInfo.h"
+
+namespace polly {
+using llvm::User;
+
+/// Determine the nature of a value's use within a statement.
+///
+/// These are not always representable by llvm::Use. For instance, scalar write
+/// MemoryAccesses do use a value, but are not associated with an instruction's
+/// argument.
+///
+/// Despite its name it is not tied to virtual instructions (although it works
+/// fine with them), but to promote consistent handling of values used in
+/// statements.
+class VirtualUse {
+public:
+ /// The different types of uses. Handling usually differentiates a lot between
+ /// these; one can use a switch to handle each case (and get warned by the
+ /// compiler if one is not handled).
+ enum UseKind {
+ // An llvm::Constant.
+ Constant,
+
+ // An llvm::BasicBlock.
+ Block,
+
+ // A value that can be generated using ScopExpander.
+ Synthesizable,
+
+ // A load that always reads the same value throughout the SCoP (address and
+ // the value located there a SCoP-invariant) and has been hoisted in front
+ // of the SCoP.
+ Hoisted,
+
+ // Definition before the SCoP and not synthesizable. Can be an instruction
+ // outside the SCoP, a function argument or a global value. Whether there is
+ // a scalar MemoryAccess in this statement for reading it depends on the
+ // -polly-analyze-read-only-scalars switch.
+ ReadOnly,
+
+ // A definition within the same statement. No MemoryAccess between
+ // definition and use are necessary.
+ Intra,
+
+ // Definition in another statement. There is a scalar MemoryAccess that
+ // makes it available in this statement.
+ Inter
+ };
+
+private:
+ /// The statement where a value is used.
+ ScopStmt *User;
+
+ /// The value that is used.
+ Value *Val;
+
+ /// The type of value use.
+ UseKind Kind;
+
+ /// The value represented as llvm::SCEV expression.
+ const SCEV *ScevExpr;
+
+ /// If this is an inter-statement (or read-only) use, contains the
+ /// MemoryAccess that makes the value available in this statement. In case of
+ /// intra-statement uses, can contain a MemoryKind::Array access. In all other
+ /// cases, it is a nullptr.
+ MemoryAccess *InputMA;
+
+ VirtualUse(ScopStmt *User, Value *Val, UseKind Kind, const SCEV *ScevExpr,
+ MemoryAccess *InputMA)
+ : User(User), Val(Val), Kind(Kind), ScevExpr(ScevExpr), InputMA(InputMA) {
+ }
+
+public:
+ /// Get a VirtualUse for an llvm::Use.
+ ///
+ /// @param S The Scop object.
+ /// @param U The llvm::Use the get information for.
+ /// @param LI The LoopInfo analysis. Needed to determine whether the
+ /// value is synthesizable.
+ /// @param Virtual Whether to ignore existing MemoryAcccess.
+ ///
+ /// @return The VirtualUse representing the same use as @p U.
+ static VirtualUse create(Scop *S, const Use &U, LoopInfo *LI, bool Virtual);
+
+ /// Get a VirtualUse for uses within statements.
+ ///
+ /// It is assumed that the user is not a PHINode. Such uses are always
+ /// VirtualUse::Inter unless in a regions statement.
+ ///
+ /// @param S The Scop object.
+ /// @param UserStmt The statement in which @p Val is used. Can be nullptr, in
+ /// which case it assumed that the statement has been
+ /// removed, which is only possible if no instruction in it
+ /// had side-effects or computes a value used by another
+ /// statement.
+ /// @param UserScope Loop scope in which the value is used. Needed to
+ /// determine whether the value is synthesizable.
+ /// @param Val The value being used.
+ /// @param Virtual Whether to use (and prioritize over instruction location)
+ /// information about MemoryAccesses.
+ ///
+ /// @return A VirtualUse object that gives information about @p Val's use in
+ /// @p UserStmt.
+ static VirtualUse create(Scop *S, ScopStmt *UserStmt, Loop *UserScope,
+ Value *Val, bool Virtual);
+
+ static VirtualUse create(ScopStmt *UserStmt, Loop *UserScope, Value *Val,
+ bool Virtual) {
+ return create(UserStmt->getParent(), UserStmt, UserScope, Val, Virtual);
+ }
+
+ bool isConstant() const { return Kind == Constant; }
+ bool isBlock() const { return Kind == Block; }
+ bool isSynthesizable() const { return Kind == Synthesizable; }
+ bool isHoisted() const { return Kind == Hoisted; }
+ bool isReadOnly() const { return Kind == ReadOnly; }
+ bool isIntra() const { return Kind == Intra; }
+ bool isInter() const { return Kind == Inter; }
+
+ /// Return user statement.
+ ScopStmt *getUser() const { return User; }
+
+ /// Return the used value.
+ llvm::Value *getValue() const { return Val; }
+
+ /// Return the type of use.
+ UseKind getKind() const { return Kind; }
+
+ /// Return the ScalarEvolution representation of @p Val.
+ const SCEV *getScevExpr() const { return ScevExpr; }
+
+ /// Return the MemoryAccess that makes the value available in this statement,
+ /// if any.
+ MemoryAccess *getMemoryAccess() const { return InputMA; }
+
+ /// Print a description of this object.
+ ///
+ /// @param OS Stream to print to.
+ /// @param Reproducible If true, ensures that the output is stable between
+ /// runs and is suitable to check in regression tests.
+ /// This excludes printing e.g. pointer values. If false,
+ /// the output should not be used for regression tests,
+ /// but may contain more information useful in debugger
+ /// sessions.
+ void print(raw_ostream &OS, bool Reproducible = true) const;
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ void dump() const;
+#endif
+};
+
+/// An iterator for virtual operands.
+class VirtualOperandIterator {
+ friend class VirtualInstruction;
+ friend class VirtualUse;
+
+ using Self = VirtualOperandIterator;
+
+ ScopStmt *User;
+ User::op_iterator U;
+
+ VirtualOperandIterator(ScopStmt *User, User::op_iterator U)
+ : User(User), U(U) {}
+
+public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = VirtualUse;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type *;
+ using reference = value_type &;
+
+ inline bool operator==(const Self &that) const {
+ assert(this->User == that.User);
+ return this->U == that.U;
+ }
+
+ inline bool operator!=(const Self &that) const {
+ assert(this->User == that.User);
+ return this->U != that.U;
+ }
+
+ VirtualUse operator*() const {
+ return VirtualUse::create(User, User->getSurroundingLoop(), U->get(), true);
+ }
+
+ Use *operator->() const { return U; }
+
+ Self &operator++() {
+ U++;
+ return *this;
+ }
+
+ Self operator++(int) {
+ Self tmp = *this;
+ ++*this;
+ return tmp;
+ }
+};
+
+/// This class represents a "virtual instruction", an instruction in a ScopStmt,
+/// effectively a ScopStmt/Instruction-pair.
+///
+/// An instructions can be moved between statements (e.g. to avoid a scalar
+/// dependency) and even can be contained in multiple statements (for instance,
+/// to recompute a value instead of transferring it), hence 'virtual'. This
+/// class is required to represent such instructions that are not in their
+/// 'physical' location anymore.
+///
+/// A statement can currently not contain the same instructions multiple times
+/// (that is, from different loop iterations). Therefore, a
+/// ScopStmt/Instruction-pair uniquely identifies a virtual instructions.
+/// ScopStmt::getInstruction() can contain the same instruction multiple times,
+/// but they necessarily compute the same value.
+class VirtualInstruction {
+ friend class VirtualOperandIterator;
+ friend struct llvm::DenseMapInfo<VirtualInstruction>;
+
+private:
+ /// The statement this virtual instruction is in.
+ ScopStmt *Stmt = nullptr;
+
+ /// The instruction of a statement.
+ Instruction *Inst = nullptr;
+
+public:
+ VirtualInstruction() {}
+
+ /// Create a new virtual instruction of an instruction @p Inst in @p Stmt.
+ VirtualInstruction(ScopStmt *Stmt, Instruction *Inst)
+ : Stmt(Stmt), Inst(Inst) {
+ assert(Stmt && Inst);
+ }
+
+ VirtualOperandIterator operand_begin() const {
+ return VirtualOperandIterator(Stmt, Inst->op_begin());
+ }
+
+ VirtualOperandIterator operand_end() const {
+ return VirtualOperandIterator(Stmt, Inst->op_end());
+ }
+
+ /// Returns a list of virtual operands.
+ ///
+ /// Virtual operands, like virtual instructions, need to encode the ScopStmt
+ /// they are in.
+ llvm::iterator_range<VirtualOperandIterator> operands() const {
+ return {operand_begin(), operand_end()};
+ }
+
+ /// Return the SCoP everything is contained in.
+ Scop *getScop() const { return Stmt->getParent(); }
+
+ /// Return the ScopStmt this virtual instruction is in.
+ ScopStmt *getStmt() const { return Stmt; }
+
+ /// Return the instruction in the statement.
+ Instruction *getInstruction() const { return Inst; }
+
+ /// Print a description of this object.
+ ///
+ /// @param OS Stream to print to.
+ /// @param Reproducible If true, ensures that the output is stable between
+ /// runs and is suitable for checks in regression tests.
+ /// This excludes printing e.g., pointer values. If false,
+ /// the output should not be used for regression tests,
+ /// but may contain more information useful in debugger
+ /// sessions.
+ void print(raw_ostream &OS, bool Reproducible = true) const;
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ void dump() const;
+#endif
+};
+
+static inline bool operator==(VirtualInstruction LHS, VirtualInstruction RHS) {
+ return LHS.getStmt() == RHS.getStmt() &&
+ LHS.getInstruction() == RHS.getInstruction();
+}
+
+/// Find all reachable instructions and accesses.
+///
+/// @param S The SCoP to find everything reachable in.
+/// @param LI LoopInfo required for analysis.
+/// @param UsedInsts[out] Receives all reachable instructions.
+/// @param UsedAccs[out] Receives all reachable accesses.
+/// @param OnlyLocal If non-nullptr, activates local mode: The SCoP is
+/// assumed to consist only of this statement and is
+/// conservatively correct. Does not require walking the
+/// whole SCoP.
+void markReachable(Scop *S, LoopInfo *LI,
+ DenseSet<VirtualInstruction> &UsedInsts,
+ DenseSet<MemoryAccess *> &UsedAccs,
+ ScopStmt *OnlyLocal = nullptr);
+} // namespace polly
+
+namespace llvm {
+/// Support VirtualInstructions in llvm::DenseMaps.
+template <> struct DenseMapInfo<polly::VirtualInstruction> {
+public:
+ static bool isEqual(polly::VirtualInstruction LHS,
+ polly::VirtualInstruction RHS) {
+ return DenseMapInfo<polly::ScopStmt *>::isEqual(LHS.getStmt(),
+ RHS.getStmt()) &&
+ DenseMapInfo<Instruction *>::isEqual(LHS.getInstruction(),
+ RHS.getInstruction());
+ }
+
+ static polly::VirtualInstruction getTombstoneKey() {
+ polly::VirtualInstruction TombstoneKey;
+ TombstoneKey.Stmt = DenseMapInfo<polly::ScopStmt *>::getTombstoneKey();
+ TombstoneKey.Inst = DenseMapInfo<Instruction *>::getTombstoneKey();
+ return TombstoneKey;
+ }
+
+ static polly::VirtualInstruction getEmptyKey() {
+ polly::VirtualInstruction EmptyKey;
+ EmptyKey.Stmt = DenseMapInfo<polly::ScopStmt *>::getEmptyKey();
+ EmptyKey.Inst = DenseMapInfo<Instruction *>::getEmptyKey();
+ return EmptyKey;
+ }
+
+ static unsigned getHashValue(polly::VirtualInstruction Val) {
+ return DenseMapInfo<std::pair<polly::ScopStmt *, Instruction *>>::
+ getHashValue(std::make_pair(Val.getStmt(), Val.getInstruction()));
+ }
+};
+} // namespace llvm
+
+#endif /* POLLY_SUPPORT_VIRTUALINSTRUCTION_H */
diff --git a/contrib/libs/llvm14/tools/polly/include/polly/ZoneAlgo.h b/contrib/libs/llvm14/tools/polly/include/polly/ZoneAlgo.h
new file mode 100644
index 00000000000..a93868d91c7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/include/polly/ZoneAlgo.h
@@ -0,0 +1,419 @@
+//===------ ZoneAlgo.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Derive information about array elements between statements ("Zones").
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_ZONEALGO_H
+#define POLLY_ZONEALGO_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "isl/isl-noexceptions.h"
+#include <memory>
+
+namespace llvm {
+class Value;
+class LoopInfo;
+class Loop;
+class PHINode;
+class raw_ostream;
+} // namespace llvm
+
+namespace polly {
+class Scop;
+class ScopStmt;
+class MemoryAccess;
+class ScopArrayInfo;
+
+/// Return only the mappings that map to known values.
+///
+/// @param UMap { [] -> ValInst[] }
+///
+/// @return { [] -> ValInst[] }
+isl::union_map filterKnownValInst(const isl::union_map &UMap);
+
+/// Base class for algorithms based on zones, like DeLICM.
+class ZoneAlgorithm {
+protected:
+ /// The name of the pass this is used from. Used for optimization remarks.
+ const char *PassName;
+
+ /// Hold a reference to the isl_ctx to avoid it being freed before we released
+ /// all of the isl objects.
+ ///
+ /// This must be declared before any other member that holds an isl object.
+ /// This guarantees that the shared_ptr and its isl_ctx is destructed last,
+ /// after all other members free'd the isl objects they were holding.
+ std::shared_ptr<isl_ctx> IslCtx;
+
+ /// Cached reaching definitions for each ScopStmt.
+ ///
+ /// Use getScalarReachingDefinition() to get its contents.
+ llvm::DenseMap<ScopStmt *, isl::map> ScalarReachDefZone;
+
+ /// The analyzed Scop.
+ Scop *S;
+
+ /// LoopInfo analysis used to determine whether values are synthesizable.
+ llvm::LoopInfo *LI;
+
+ /// Parameter space that does not need realignment.
+ isl::space ParamSpace;
+
+ /// Space the schedule maps to.
+ isl::space ScatterSpace;
+
+ /// Cached version of the schedule and domains.
+ isl::union_map Schedule;
+
+ /// Combined access relations of all MemoryKind::Array READ accesses.
+ /// { DomainRead[] -> Element[] }
+ isl::union_map AllReads;
+
+ /// The loaded values (llvm::LoadInst) of all reads.
+ /// { [Element[] -> DomainRead[]] -> ValInst[] }
+ isl::union_map AllReadValInst;
+
+ /// Combined access relations of all MemoryKind::Array, MAY_WRITE accesses.
+ /// { DomainMayWrite[] -> Element[] }
+ isl::union_map AllMayWrites;
+
+ /// Combined access relations of all MemoryKind::Array, MUST_WRITE accesses.
+ /// { DomainMustWrite[] -> Element[] }
+ isl::union_map AllMustWrites;
+
+ /// Combined access relations of all MK_Array write accesses (union of
+ /// AllMayWrites and AllMustWrites).
+ /// { DomainWrite[] -> Element[] }
+ isl::union_map AllWrites;
+
+ /// The value instances written to array elements of all write accesses.
+ /// { [Element[] -> DomainWrite[]] -> ValInst[] }
+ isl::union_map AllWriteValInst;
+
+ /// All reaching definitions for MemoryKind::Array writes.
+ /// { [Element[] -> Zone[]] -> DomainWrite[] }
+ isl::union_map WriteReachDefZone;
+
+ /// Map llvm::Values to an isl identifier.
+ /// Used with -polly-use-llvm-names=false as an alternative method to get
+ /// unique ids that do not depend on pointer values.
+ llvm::DenseMap<llvm::Value *, isl::id> ValueIds;
+
+ /// Set of array elements that can be reliably used for zone analysis.
+ /// { Element[] }
+ isl::union_set CompatibleElts;
+
+ /// List of PHIs that may transitively refer to themselves.
+ ///
+ /// Computing them would require a polyhedral transitive closure operation,
+ /// for which isl may only return an approximation. For correctness, we always
+ /// require an exact result. Hence, we exclude such PHIs.
+ llvm::SmallPtrSet<llvm::PHINode *, 4> RecursivePHIs;
+
+ /// PHIs that have been computed.
+ ///
+ /// Computed PHIs are replaced by their incoming values using #NormalizeMap.
+ llvm::DenseSet<llvm::PHINode *> ComputedPHIs;
+
+ /// For computed PHIs, contains the ValInst they stand for.
+ ///
+ /// To show an example, assume the following PHINode:
+ ///
+ /// Stmt:
+ /// %phi = phi double [%val1, %bb1], [%val2, %bb2]
+ ///
+ /// It's ValInst is:
+ ///
+ /// { [Stmt[i] -> phi[]] }
+ ///
+ /// The value %phi will be either %val1 or %val2, depending on whether in
+ /// iteration i %bb1 or %bb2 has been executed before. In SCoPs, this can be
+ /// determined at compile-time, and the result stored in #NormalizeMap. For
+ /// the previous example, it could be:
+ ///
+ /// { [Stmt[i] -> phi[]] -> [Stmt[0] -> val1[]];
+ /// [Stmt[i] -> phi[]] -> [Stmt[i] -> val2[]] : i > 0 }
+ ///
+ /// Only ValInsts in #ComputedPHIs are present in this map. Other values are
+ /// assumed to represent themselves. This is to avoid adding lots of identity
+ /// entries to this map.
+ ///
+ /// { PHIValInst[] -> IncomingValInst[] }
+ isl::union_map NormalizeMap;
+
+ /// Cache for computePerPHI(const ScopArrayInfo *)
+ llvm::SmallDenseMap<llvm::PHINode *, isl::union_map> PerPHIMaps;
+
+ /// A cache for getDefToTarget().
+ llvm::DenseMap<std::pair<ScopStmt *, ScopStmt *>, isl::map> DefToTargetCache;
+
+ /// Prepare the object before computing the zones of @p S.
+ ///
+ /// @param PassName Name of the pass using this analysis.
+ /// @param S The SCoP to process.
+ /// @param LI LoopInfo analysis used to determine synthesizable values.
+ ZoneAlgorithm(const char *PassName, Scop *S, llvm::LoopInfo *LI);
+
+private:
+ /// Find the array elements that violate the zone analysis assumptions.
+ ///
+ /// What violates our assumptions:
+ /// - A load after a write of the same location; we assume that all reads
+ /// occur before the writes.
+ /// - Two writes to the same location; we cannot model the order in which
+ /// these occur.
+ ///
+ /// Scalar reads implicitly always occur before other accesses therefore never
+ /// violate the first condition. There is also at most one write to a scalar,
+ /// satisfying the second condition.
+ ///
+ /// @param Stmt The statement to be analyzed.
+ /// @param[out] IncompatibleElts Receives the elements that are not
+ /// zone-analysis compatible.
+ /// @param[out] AllElts receives all encountered elements.
+ void collectIncompatibleElts(ScopStmt *Stmt, isl::union_set &IncompatibleElts,
+ isl::union_set &AllElts);
+
+ void addArrayReadAccess(MemoryAccess *MA);
+
+ /// Return the ValInst write by a (must-)write access. Returns the 'unknown'
+ /// ValInst if there is no single ValInst[] the array element written to will
+ /// have.
+ ///
+ /// @return { ValInst[] }
+ isl::union_map getWrittenValue(MemoryAccess *MA, isl::map AccRel);
+
+ void addArrayWriteAccess(MemoryAccess *MA);
+
+ /// For an llvm::Value defined in @p DefStmt, compute the RAW dependency for a
+ /// use in every instance of @p UseStmt.
+ ///
+ /// @param UseStmt Statement a scalar is used in.
+ /// @param DefStmt Statement a scalar is defined in.
+ ///
+ /// @return { DomainUse[] -> DomainDef[] }
+ isl::map computeUseToDefFlowDependency(ScopStmt *UseStmt, ScopStmt *DefStmt);
+
+protected:
+ isl::union_set makeEmptyUnionSet() const;
+
+ isl::union_map makeEmptyUnionMap() const;
+
+ /// For each 'execution' of a PHINode, get the incoming block that was
+ /// executed before.
+ ///
+ /// For each PHI instance we can directly determine which was the incoming
+ /// block, and hence derive which value the PHI has.
+ ///
+ /// @param SAI The ScopArrayInfo representing the PHI's storage.
+ ///
+ /// @return { DomainPHIRead[] -> DomainPHIWrite[] }
+ isl::union_map computePerPHI(const polly::ScopArrayInfo *SAI);
+
+ /// Find the array elements that can be used for zone analysis.
+ void collectCompatibleElts();
+
+ /// Get the schedule for @p Stmt.
+ ///
+ /// The domain of the result is as narrow as possible.
+ isl::map getScatterFor(ScopStmt *Stmt) const;
+
+ /// Get the schedule of @p MA's parent statement.
+ isl::map getScatterFor(MemoryAccess *MA) const;
+
+ /// Get the schedule for the statement instances of @p Domain.
+ isl::union_map getScatterFor(isl::union_set Domain) const;
+
+ /// Get the schedule for the statement instances of @p Domain.
+ isl::map getScatterFor(isl::set Domain) const;
+
+ /// Get the domain of @p Stmt.
+ isl::set getDomainFor(ScopStmt *Stmt) const;
+
+ /// Get the domain @p MA's parent statement.
+ isl::set getDomainFor(MemoryAccess *MA) const;
+
+ /// Get the access relation of @p MA.
+ ///
+ /// The domain of the result is as narrow as possible.
+ isl::map getAccessRelationFor(MemoryAccess *MA) const;
+
+ /// Get a domain translation map from a (scalar) definition to the statement
+ /// where the definition is being moved to.
+ ///
+ /// @p TargetStmt can also be seen at an llvm::Use of an llvm::Value in
+ /// @p DefStmt. In addition, we allow transitive uses:
+ ///
+ /// DefStmt -> MiddleStmt -> TargetStmt
+ ///
+ /// where an operand tree of instructions in DefStmt and MiddleStmt are to be
+ /// moved to TargetStmt. To be generally correct, we also need to know all the
+ /// intermediate statements. However, we make use of the fact that
+ /// ForwardOpTree currently does not support a move from a loop body across
+ /// its header such that only the first definition and the target statement
+ /// are relevant.
+ ///
+ /// @param DefStmt Statement from where a definition might be moved from.
+ /// @param TargetStmt Statement where the definition is potentially being
+ /// moved to (should contain a use of that definition).
+ ///
+ /// @return { DomainDef[] -> DomainTarget[] }
+ isl::map getDefToTarget(ScopStmt *DefStmt, ScopStmt *TargetStmt);
+
+ /// Get the reaching definition of a scalar defined in @p Stmt.
+ ///
+ /// Note that this does not depend on the llvm::Instruction, only on the
+ /// statement it is defined in. Therefore the same computation can be reused.
+ ///
+ /// @param Stmt The statement in which a scalar is defined.
+ ///
+ /// @return { Scatter[] -> DomainDef[] }
+ isl::map getScalarReachingDefinition(ScopStmt *Stmt);
+
+ /// Get the reaching definition of a scalar defined in @p DefDomain.
+ ///
+ /// @param DomainDef { DomainDef[] }
+ /// The write statements to get the reaching definition for.
+ ///
+ /// @return { Scatter[] -> DomainDef[] }
+ isl::map getScalarReachingDefinition(isl::set DomainDef);
+
+ /// Create a statement-to-unknown value mapping.
+ ///
+ /// @param Stmt The statement whose instances are mapped to unknown.
+ ///
+ /// @return { Domain[] -> ValInst[] }
+ isl::map makeUnknownForDomain(ScopStmt *Stmt) const;
+
+ /// Create an isl_id that represents @p V.
+ isl::id makeValueId(llvm::Value *V);
+
+ /// Create the space for an llvm::Value that is available everywhere.
+ isl::space makeValueSpace(llvm::Value *V);
+
+ /// Create a set with the llvm::Value @p V which is available everywhere.
+ isl::set makeValueSet(llvm::Value *V);
+
+ /// Create a mapping from a statement instance to the instance of an
+ /// llvm::Value that can be used in there.
+ ///
+ /// Although LLVM IR uses single static assignment, llvm::Values can have
+ /// different contents in loops, when they get redefined in the last
+ /// iteration. This function tries to get the statement instance of the
+ /// previous definition, relative to a user.
+ ///
+ /// Example:
+ /// for (int i = 0; i < N; i += 1) {
+ /// DEF:
+ /// int v = A[i];
+ /// USE:
+ /// use(v);
+ /// }
+ ///
+ /// The value instance used by statement instance USE[i] is DEF[i]. Hence,
+ /// makeValInst returns:
+ ///
+ /// { USE[i] -> [DEF[i] -> v[]] : 0 <= i < N }
+ ///
+ /// @param Val The value to get the instance of.
+ /// @param UserStmt The statement that uses @p Val. Can be nullptr.
+ /// @param Scope Loop the using instruction resides in.
+ /// @param IsCertain Pass true if the definition of @p Val is a
+ /// MUST_WRITE or false if the write is conditional.
+ ///
+ /// @return { DomainUse[] -> ValInst[] }
+ isl::map makeValInst(llvm::Value *Val, ScopStmt *UserStmt, llvm::Loop *Scope,
+ bool IsCertain = true);
+
+ /// Create and normalize a ValInst.
+ ///
+ /// @see makeValInst
+ /// @see normalizeValInst
+ /// @see #NormalizedPHI
+ isl::union_map makeNormalizedValInst(llvm::Value *Val, ScopStmt *UserStmt,
+ llvm::Loop *Scope,
+ bool IsCertain = true);
+
+ /// Return whether @p MA can be used for transformations (e.g. OpTree load
+ /// forwarding, DeLICM mapping).
+ bool isCompatibleAccess(MemoryAccess *MA);
+
+ /// Compute the different zones.
+ void computeCommon();
+
+ /// Compute the normalization map that replaces PHIs by their incoming
+ /// values.
+ ///
+ /// @see #NormalizeMap
+ void computeNormalizedPHIs();
+
+ /// Print the current state of all MemoryAccesses to @p.
+ void printAccesses(llvm::raw_ostream &OS, int Indent = 0) const;
+
+ /// Is @p MA a PHI READ access that can be normalized?
+ ///
+ /// @see #NormalizeMap
+ bool isNormalizable(MemoryAccess *MA);
+
+ /// @{
+ /// Determine whether the argument does not map to any computed PHI. Those
+ /// should have been replaced by their incoming values.
+ ///
+ /// @see #NormalizedPHI
+ isl::boolean isNormalized(isl::map Map);
+ isl::boolean isNormalized(isl::union_map Map);
+ /// @}
+
+public:
+ /// Return the SCoP this object is analyzing.
+ Scop *getScop() const { return S; }
+
+ /// A reaching definition zone is known to have the definition's written value
+ /// if the definition is a MUST_WRITE.
+ ///
+ /// @return { [Element[] -> Zone[]] -> ValInst[] }
+ isl::union_map computeKnownFromMustWrites() const;
+
+ /// A reaching definition zone is known to be the same value as any load that
+ /// reads from that array element in that period.
+ ///
+ /// @return { [Element[] -> Zone[]] -> ValInst[] }
+ isl::union_map computeKnownFromLoad() const;
+
+ /// Compute which value an array element stores at every instant.
+ ///
+ /// @param FromWrite Use stores as source of information.
+ /// @param FromRead Use loads as source of information.
+ ///
+ /// @return { [Element[] -> Zone[]] -> ValInst[] }
+ isl::union_map computeKnown(bool FromWrite, bool FromRead) const;
+};
+
+/// Create a domain-to-unknown value mapping.
+///
+/// Value instances that do not represent a specific value are represented by an
+/// unnamed tuple of 0 dimensions. Its meaning depends on the context. It can
+/// either mean a specific but unknown value which cannot be represented by
+/// other means. It conflicts with itself because those two unknown ValInsts may
+/// have different concrete values at runtime.
+///
+/// The other meaning is an arbitrary or wildcard value that can be chosen
+/// freely, like LLVM's undef. If matched with an unknown ValInst, there is no
+/// conflict.
+///
+/// @param Domain { Domain[] }
+///
+/// @return { Domain[] -> ValInst[] }
+isl::union_map makeUnknownForDomain(isl::union_set Domain);
+} // namespace polly
+
+#endif /* POLLY_ZONEALGO_H */
diff --git a/contrib/libs/llvm14/tools/polly/lib/Analysis/DependenceInfo.cpp b/contrib/libs/llvm14/tools/polly/lib/Analysis/DependenceInfo.cpp
new file mode 100644
index 00000000000..b8798a1c761
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Analysis/DependenceInfo.cpp
@@ -0,0 +1,983 @@
+//===- DependenceInfo.cpp - Calculate dependency information for a Scop. --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Calculate the data dependency relations for a Scop using ISL.
+//
+// The integer set library (ISL) from Sven, has a integrated dependency analysis
+// to calculate data dependences. This pass takes advantage of this and
+// calculate those dependences a Scop.
+//
+// The dependences in this pass are exact in terms that for a specific read
+// statement instance only the last write statement instance is returned. In
+// case of may writes a set of possible write instances is returned. This
+// analysis will never produce redundant dependences.
+//
+//===----------------------------------------------------------------------===//
+//
+#include "polly/DependenceInfo.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/GICHelper.h"
+#include "polly/Support/ISLTools.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/Support/Debug.h"
+#include "isl/aff.h"
+#include "isl/ctx.h"
+#include "isl/flow.h"
+#include "isl/map.h"
+#include "isl/schedule.h"
+#include "isl/set.h"
+#include "isl/union_map.h"
+#include "isl/union_set.h"
+
+using namespace polly;
+using namespace llvm;
+
+#define DEBUG_TYPE "polly-dependence"
+
+static cl::opt<int> OptComputeOut(
+ "polly-dependences-computeout",
+ cl::desc("Bound the dependence analysis by a maximal amount of "
+ "computational steps (0 means no bound)"),
+ cl::Hidden, cl::init(500000), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> LegalityCheckDisabled(
+ "disable-polly-legality", cl::desc("Disable polly legality check"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ UseReductions("polly-dependences-use-reductions",
+ cl::desc("Exploit reductions in dependence analysis"),
+ cl::Hidden, cl::init(true), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+enum AnalysisType { VALUE_BASED_ANALYSIS, MEMORY_BASED_ANALYSIS };
+
+static cl::opt<enum AnalysisType> OptAnalysisType(
+ "polly-dependences-analysis-type",
+ cl::desc("The kind of dependence analysis to use"),
+ cl::values(clEnumValN(VALUE_BASED_ANALYSIS, "value-based",
+ "Exact dependences without transitive dependences"),
+ clEnumValN(MEMORY_BASED_ANALYSIS, "memory-based",
+ "Overapproximation of dependences")),
+ cl::Hidden, cl::init(VALUE_BASED_ANALYSIS), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<Dependences::AnalysisLevel> OptAnalysisLevel(
+ "polly-dependences-analysis-level",
+ cl::desc("The level of dependence analysis"),
+ cl::values(clEnumValN(Dependences::AL_Statement, "statement-wise",
+ "Statement-level analysis"),
+ clEnumValN(Dependences::AL_Reference, "reference-wise",
+ "Memory reference level analysis that distinguish"
+ " accessed references in the same statement"),
+ clEnumValN(Dependences::AL_Access, "access-wise",
+ "Memory reference level analysis that distinguish"
+ " access instructions in the same statement")),
+ cl::Hidden, cl::init(Dependences::AL_Statement), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+//===----------------------------------------------------------------------===//
+
+/// Tag the @p Relation domain with @p TagId
+static __isl_give isl_map *tag(__isl_take isl_map *Relation,
+ __isl_take isl_id *TagId) {
+ isl_space *Space = isl_map_get_space(Relation);
+ Space = isl_space_drop_dims(Space, isl_dim_out, 0,
+ isl_map_dim(Relation, isl_dim_out));
+ Space = isl_space_set_tuple_id(Space, isl_dim_out, TagId);
+ isl_multi_aff *Tag = isl_multi_aff_domain_map(Space);
+ Relation = isl_map_preimage_domain_multi_aff(Relation, Tag);
+ return Relation;
+}
+
+/// Tag the @p Relation domain with either MA->getArrayId() or
+/// MA->getId() based on @p TagLevel
+static __isl_give isl_map *tag(__isl_take isl_map *Relation, MemoryAccess *MA,
+ Dependences::AnalysisLevel TagLevel) {
+ if (TagLevel == Dependences::AL_Reference)
+ return tag(Relation, MA->getArrayId().release());
+
+ if (TagLevel == Dependences::AL_Access)
+ return tag(Relation, MA->getId().release());
+
+ // No need to tag at the statement level.
+ return Relation;
+}
+
+/// Collect information about the SCoP @p S.
+static void collectInfo(Scop &S, isl_union_map *&Read,
+ isl_union_map *&MustWrite, isl_union_map *&MayWrite,
+ isl_union_map *&ReductionTagMap,
+ isl_union_set *&TaggedStmtDomain,
+ Dependences::AnalysisLevel Level) {
+ isl_space *Space = S.getParamSpace().release();
+ Read = isl_union_map_empty(isl_space_copy(Space));
+ MustWrite = isl_union_map_empty(isl_space_copy(Space));
+ MayWrite = isl_union_map_empty(isl_space_copy(Space));
+ ReductionTagMap = isl_union_map_empty(isl_space_copy(Space));
+ isl_union_map *StmtSchedule = isl_union_map_empty(Space);
+
+ SmallPtrSet<const ScopArrayInfo *, 8> ReductionArrays;
+ if (UseReductions)
+ for (ScopStmt &Stmt : S)
+ for (MemoryAccess *MA : Stmt)
+ if (MA->isReductionLike())
+ ReductionArrays.insert(MA->getScopArrayInfo());
+
+ for (ScopStmt &Stmt : S) {
+ for (MemoryAccess *MA : Stmt) {
+ isl_set *domcp = Stmt.getDomain().release();
+ isl_map *accdom = MA->getAccessRelation().release();
+
+ accdom = isl_map_intersect_domain(accdom, domcp);
+
+ if (ReductionArrays.count(MA->getScopArrayInfo())) {
+ // Wrap the access domain and adjust the schedule accordingly.
+ //
+ // An access domain like
+ // Stmt[i0, i1] -> MemAcc_A[i0 + i1]
+ // will be transformed into
+ // [Stmt[i0, i1] -> MemAcc_A[i0 + i1]] -> MemAcc_A[i0 + i1]
+ //
+ // We collect all the access domains in the ReductionTagMap.
+ // This is used in Dependences::calculateDependences to create
+ // a tagged Schedule tree.
+
+ ReductionTagMap =
+ isl_union_map_add_map(ReductionTagMap, isl_map_copy(accdom));
+ accdom = isl_map_range_map(accdom);
+ } else {
+ accdom = tag(accdom, MA, Level);
+ if (Level > Dependences::AL_Statement) {
+ isl_map *StmtScheduleMap = Stmt.getSchedule().release();
+ assert(StmtScheduleMap &&
+ "Schedules that contain extension nodes require special "
+ "handling.");
+ isl_map *Schedule = tag(StmtScheduleMap, MA, Level);
+ StmtSchedule = isl_union_map_add_map(StmtSchedule, Schedule);
+ }
+ }
+
+ if (MA->isRead())
+ Read = isl_union_map_add_map(Read, accdom);
+ else if (MA->isMayWrite())
+ MayWrite = isl_union_map_add_map(MayWrite, accdom);
+ else
+ MustWrite = isl_union_map_add_map(MustWrite, accdom);
+ }
+
+ if (!ReductionArrays.empty() && Level == Dependences::AL_Statement)
+ StmtSchedule =
+ isl_union_map_add_map(StmtSchedule, Stmt.getSchedule().release());
+ }
+
+ StmtSchedule = isl_union_map_intersect_params(
+ StmtSchedule, S.getAssumedContext().release());
+ TaggedStmtDomain = isl_union_map_domain(StmtSchedule);
+
+ ReductionTagMap = isl_union_map_coalesce(ReductionTagMap);
+ Read = isl_union_map_coalesce(Read);
+ MustWrite = isl_union_map_coalesce(MustWrite);
+ MayWrite = isl_union_map_coalesce(MayWrite);
+}
+
+/// Fix all dimension of @p Zero to 0 and add it to @p user
+static void fixSetToZero(isl::set Zero, isl::union_set *User) {
+ for (auto i : rangeIslSize(0, Zero.tuple_dim()))
+ Zero = Zero.fix_si(isl::dim::set, i, 0);
+ *User = User->unite(Zero);
+}
+
+/// Compute the privatization dependences for a given dependency @p Map
+///
+/// Privatization dependences are widened original dependences which originate
+/// or end in a reduction access. To compute them we apply the transitive close
+/// of the reduction dependences (which maps each iteration of a reduction
+/// statement to all following ones) on the RAW/WAR/WAW dependences. The
+/// dependences which start or end at a reduction statement will be extended to
+/// depend on all following reduction statement iterations as well.
+/// Note: "Following" here means according to the reduction dependences.
+///
+/// For the input:
+///
+/// S0: *sum = 0;
+/// for (int i = 0; i < 1024; i++)
+/// S1: *sum += i;
+/// S2: *sum = *sum * 3;
+///
+/// we have the following dependences before we add privatization dependences:
+///
+/// RAW:
+/// { S0[] -> S1[0]; S1[1023] -> S2[] }
+/// WAR:
+/// { }
+/// WAW:
+/// { S0[] -> S1[0]; S1[1024] -> S2[] }
+/// RED:
+/// { S1[i0] -> S1[1 + i0] : i0 >= 0 and i0 <= 1022 }
+///
+/// and afterwards:
+///
+/// RAW:
+/// { S0[] -> S1[i0] : i0 >= 0 and i0 <= 1023;
+/// S1[i0] -> S2[] : i0 >= 0 and i0 <= 1023}
+/// WAR:
+/// { }
+/// WAW:
+/// { S0[] -> S1[i0] : i0 >= 0 and i0 <= 1023;
+/// S1[i0] -> S2[] : i0 >= 0 and i0 <= 1023}
+/// RED:
+/// { S1[i0] -> S1[1 + i0] : i0 >= 0 and i0 <= 1022 }
+///
+/// Note: This function also computes the (reverse) transitive closure of the
+/// reduction dependences.
+void Dependences::addPrivatizationDependences() {
+ isl_union_map *PrivRAW, *PrivWAW, *PrivWAR;
+
+ // The transitive closure might be over approximated, thus could lead to
+ // dependency cycles in the privatization dependences. To make sure this
+ // will not happen we remove all negative dependences after we computed
+ // the transitive closure.
+ TC_RED = isl_union_map_transitive_closure(isl_union_map_copy(RED), nullptr);
+
+ // FIXME: Apply the current schedule instead of assuming the identity schedule
+ // here. The current approach is only valid as long as we compute the
+ // dependences only with the initial (identity schedule). Any other
+ // schedule could change "the direction of the backward dependences" we
+ // want to eliminate here.
+ isl_union_set *UDeltas = isl_union_map_deltas(isl_union_map_copy(TC_RED));
+ isl_union_set *Universe = isl_union_set_universe(isl_union_set_copy(UDeltas));
+ isl::union_set Zero =
+ isl::manage(isl_union_set_empty(isl_union_set_get_space(Universe)));
+
+ for (isl::set Set : isl::manage_copy(Universe).get_set_list())
+ fixSetToZero(Set, &Zero);
+
+ isl_union_map *NonPositive =
+ isl_union_set_lex_le_union_set(UDeltas, Zero.release());
+
+ TC_RED = isl_union_map_subtract(TC_RED, NonPositive);
+
+ TC_RED = isl_union_map_union(
+ TC_RED, isl_union_map_reverse(isl_union_map_copy(TC_RED)));
+ TC_RED = isl_union_map_coalesce(TC_RED);
+
+ isl_union_map **Maps[] = {&RAW, &WAW, &WAR};
+ isl_union_map **PrivMaps[] = {&PrivRAW, &PrivWAW, &PrivWAR};
+ for (unsigned u = 0; u < 3; u++) {
+ isl_union_map **Map = Maps[u], **PrivMap = PrivMaps[u];
+
+ *PrivMap = isl_union_map_apply_range(isl_union_map_copy(*Map),
+ isl_union_map_copy(TC_RED));
+ *PrivMap = isl_union_map_union(
+ *PrivMap, isl_union_map_apply_range(isl_union_map_copy(TC_RED),
+ isl_union_map_copy(*Map)));
+
+ *Map = isl_union_map_union(*Map, *PrivMap);
+ }
+
+ isl_union_set_free(Universe);
+}
+
+static __isl_give isl_union_flow *buildFlow(__isl_keep isl_union_map *Snk,
+ __isl_keep isl_union_map *Src,
+ __isl_keep isl_union_map *MaySrc,
+ __isl_keep isl_union_map *Kill,
+ __isl_keep isl_schedule *Schedule) {
+ isl_union_access_info *AI;
+
+ AI = isl_union_access_info_from_sink(isl_union_map_copy(Snk));
+ if (MaySrc)
+ AI = isl_union_access_info_set_may_source(AI, isl_union_map_copy(MaySrc));
+ if (Src)
+ AI = isl_union_access_info_set_must_source(AI, isl_union_map_copy(Src));
+ if (Kill)
+ AI = isl_union_access_info_set_kill(AI, isl_union_map_copy(Kill));
+ AI = isl_union_access_info_set_schedule(AI, isl_schedule_copy(Schedule));
+ auto Flow = isl_union_access_info_compute_flow(AI);
+ LLVM_DEBUG(if (!Flow) dbgs()
+ << "last error: "
+ << isl_ctx_last_error(isl_schedule_get_ctx(Schedule))
+ << '\n';);
+ return Flow;
+}
+
+void Dependences::calculateDependences(Scop &S) {
+ isl_union_map *Read, *MustWrite, *MayWrite, *ReductionTagMap;
+ isl_schedule *Schedule;
+ isl_union_set *TaggedStmtDomain;
+
+ LLVM_DEBUG(dbgs() << "Scop: \n" << S << "\n");
+
+ collectInfo(S, Read, MustWrite, MayWrite, ReductionTagMap, TaggedStmtDomain,
+ Level);
+
+ bool HasReductions = !isl_union_map_is_empty(ReductionTagMap);
+
+ LLVM_DEBUG(dbgs() << "Read: " << Read << '\n';
+ dbgs() << "MustWrite: " << MustWrite << '\n';
+ dbgs() << "MayWrite: " << MayWrite << '\n';
+ dbgs() << "ReductionTagMap: " << ReductionTagMap << '\n';
+ dbgs() << "TaggedStmtDomain: " << TaggedStmtDomain << '\n';);
+
+ Schedule = S.getScheduleTree().release();
+
+ if (!HasReductions) {
+ isl_union_map_free(ReductionTagMap);
+ // Tag the schedule tree if we want fine-grain dependence info
+ if (Level > AL_Statement) {
+ auto TaggedMap =
+ isl_union_set_unwrap(isl_union_set_copy(TaggedStmtDomain));
+ auto Tags = isl_union_map_domain_map_union_pw_multi_aff(TaggedMap);
+ Schedule = isl_schedule_pullback_union_pw_multi_aff(Schedule, Tags);
+ }
+ } else {
+ isl_union_map *IdentityMap;
+ isl_union_pw_multi_aff *ReductionTags, *IdentityTags, *Tags;
+
+ // Extract Reduction tags from the combined access domains in the given
+ // SCoP. The result is a map that maps each tagged element in the domain to
+ // the memory location it accesses. ReductionTags = {[Stmt[i] ->
+ // Array[f(i)]] -> Stmt[i] }
+ ReductionTags =
+ isl_union_map_domain_map_union_pw_multi_aff(ReductionTagMap);
+
+ // Compute an identity map from each statement in domain to itself.
+ // IdentityTags = { [Stmt[i] -> Stmt[i] }
+ IdentityMap = isl_union_set_identity(isl_union_set_copy(TaggedStmtDomain));
+ IdentityTags = isl_union_pw_multi_aff_from_union_map(IdentityMap);
+
+ Tags = isl_union_pw_multi_aff_union_add(ReductionTags, IdentityTags);
+
+ // By pulling back Tags from Schedule, we have a schedule tree that can
+ // be used to compute normal dependences, as well as 'tagged' reduction
+ // dependences.
+ Schedule = isl_schedule_pullback_union_pw_multi_aff(Schedule, Tags);
+ }
+
+ LLVM_DEBUG(dbgs() << "Read: " << Read << "\n";
+ dbgs() << "MustWrite: " << MustWrite << "\n";
+ dbgs() << "MayWrite: " << MayWrite << "\n";
+ dbgs() << "Schedule: " << Schedule << "\n");
+
+ isl_union_map *StrictWAW = nullptr;
+ {
+ IslMaxOperationsGuard MaxOpGuard(IslCtx.get(), OptComputeOut);
+
+ RAW = WAW = WAR = RED = nullptr;
+ isl_union_map *Write = isl_union_map_union(isl_union_map_copy(MustWrite),
+ isl_union_map_copy(MayWrite));
+
+ // We are interested in detecting reductions that do not have intermediate
+ // computations that are captured by other statements.
+ //
+ // Example:
+ // void f(int *A, int *B) {
+ // for(int i = 0; i <= 100; i++) {
+ //
+ // *-WAR (S0[i] -> S0[i + 1] 0 <= i <= 100)------------*
+ // | |
+ // *-WAW (S0[i] -> S0[i + 1] 0 <= i <= 100)------------*
+ // | |
+ // v |
+ // S0: *A += i; >------------------*-----------------------*
+ // |
+ // if (i >= 98) { WAR (S0[i] -> S1[i]) 98 <= i <= 100
+ // |
+ // S1: *B = *A; <--------------*
+ // }
+ // }
+ // }
+ //
+ // S0[0 <= i <= 100] has a reduction. However, the values in
+ // S0[98 <= i <= 100] is captured in S1[98 <= i <= 100].
+ // Since we allow free reordering on our reduction dependences, we need to
+ // remove all instances of a reduction statement that have data dependences
+ // originating from them.
+ // In the case of the example, we need to remove S0[98 <= i <= 100] from
+ // our reduction dependences.
+ //
+ // When we build up the WAW dependences that are used to detect reductions,
+ // we consider only **Writes that have no intermediate Reads**.
+ //
+ // `isl_union_flow_get_must_dependence` gives us dependences of the form:
+ // (sink <- must_source).
+ //
+ // It *will not give* dependences of the form:
+ // 1. (sink <- ... <- may_source <- ... <- must_source)
+ // 2. (sink <- ... <- must_source <- ... <- must_source)
+ //
+ // For a detailed reference on ISL's flow analysis, see:
+ // "Presburger Formulas and Polyhedral Compilation" - Approximate Dataflow
+ // Analysis.
+ //
+ // Since we set "Write" as a must-source, "Read" as a may-source, and ask
+ // for must dependences, we get all Writes to Writes that **do not flow
+ // through a Read**.
+ //
+ // ScopInfo::checkForReductions makes sure that if something captures
+ // the reduction variable in the same basic block, then it is rejected
+ // before it is even handed here. This makes sure that there is exactly
+ // one read and one write to a reduction variable in a Statement.
+ // Example:
+ // void f(int *sum, int A[N], int B[N]) {
+ // for (int i = 0; i < N; i++) {
+ // *sum += A[i]; < the store and the load is not tagged as a
+ // B[i] = *sum; < reduction-like access due to the overlap.
+ // }
+ // }
+
+ isl_union_flow *Flow = buildFlow(Write, Write, Read, nullptr, Schedule);
+ StrictWAW = isl_union_flow_get_must_dependence(Flow);
+ isl_union_flow_free(Flow);
+
+ if (OptAnalysisType == VALUE_BASED_ANALYSIS) {
+ Flow = buildFlow(Read, MustWrite, MayWrite, nullptr, Schedule);
+ RAW = isl_union_flow_get_may_dependence(Flow);
+ isl_union_flow_free(Flow);
+
+ Flow = buildFlow(Write, MustWrite, MayWrite, nullptr, Schedule);
+ WAW = isl_union_flow_get_may_dependence(Flow);
+ isl_union_flow_free(Flow);
+
+ // ISL now supports "kills" in approximate dataflow analysis, we can
+ // specify the MustWrite as kills, Read as source and Write as sink.
+ Flow = buildFlow(Write, nullptr, Read, MustWrite, Schedule);
+ WAR = isl_union_flow_get_may_dependence(Flow);
+ isl_union_flow_free(Flow);
+ } else {
+ Flow = buildFlow(Read, nullptr, Write, nullptr, Schedule);
+ RAW = isl_union_flow_get_may_dependence(Flow);
+ isl_union_flow_free(Flow);
+
+ Flow = buildFlow(Write, nullptr, Read, nullptr, Schedule);
+ WAR = isl_union_flow_get_may_dependence(Flow);
+ isl_union_flow_free(Flow);
+
+ Flow = buildFlow(Write, nullptr, Write, nullptr, Schedule);
+ WAW = isl_union_flow_get_may_dependence(Flow);
+ isl_union_flow_free(Flow);
+ }
+
+ isl_union_map_free(Write);
+ isl_union_map_free(MustWrite);
+ isl_union_map_free(MayWrite);
+ isl_union_map_free(Read);
+ isl_schedule_free(Schedule);
+
+ RAW = isl_union_map_coalesce(RAW);
+ WAW = isl_union_map_coalesce(WAW);
+ WAR = isl_union_map_coalesce(WAR);
+
+ // End of max_operations scope.
+ }
+
+ if (isl_ctx_last_error(IslCtx.get()) == isl_error_quota) {
+ isl_union_map_free(RAW);
+ isl_union_map_free(WAW);
+ isl_union_map_free(WAR);
+ isl_union_map_free(StrictWAW);
+ RAW = WAW = WAR = StrictWAW = nullptr;
+ isl_ctx_reset_error(IslCtx.get());
+ }
+
+ // Drop out early, as the remaining computations are only needed for
+ // reduction dependences or dependences that are finer than statement
+ // level dependences.
+ if (!HasReductions && Level == AL_Statement) {
+ RED = isl_union_map_empty(isl_union_map_get_space(RAW));
+ TC_RED = isl_union_map_empty(isl_union_set_get_space(TaggedStmtDomain));
+ isl_union_set_free(TaggedStmtDomain);
+ isl_union_map_free(StrictWAW);
+ return;
+ }
+
+ isl_union_map *STMT_RAW, *STMT_WAW, *STMT_WAR;
+ STMT_RAW = isl_union_map_intersect_domain(
+ isl_union_map_copy(RAW), isl_union_set_copy(TaggedStmtDomain));
+ STMT_WAW = isl_union_map_intersect_domain(
+ isl_union_map_copy(WAW), isl_union_set_copy(TaggedStmtDomain));
+ STMT_WAR =
+ isl_union_map_intersect_domain(isl_union_map_copy(WAR), TaggedStmtDomain);
+ LLVM_DEBUG({
+ dbgs() << "Wrapped Dependences:\n";
+ dump();
+ dbgs() << "\n";
+ });
+
+ // To handle reduction dependences we proceed as follows:
+ // 1) Aggregate all possible reduction dependences, namely all self
+ // dependences on reduction like statements.
+ // 2) Intersect them with the actual RAW & WAW dependences to the get the
+ // actual reduction dependences. This will ensure the load/store memory
+ // addresses were __identical__ in the two iterations of the statement.
+ // 3) Relax the original RAW, WAW and WAR dependences by subtracting the
+ // actual reduction dependences. Binary reductions (sum += A[i]) cause
+ // the same, RAW, WAW and WAR dependences.
+ // 4) Add the privatization dependences which are widened versions of
+ // already present dependences. They model the effect of manual
+ // privatization at the outermost possible place (namely after the last
+ // write and before the first access to a reduction location).
+
+ // Step 1)
+ RED = isl_union_map_empty(isl_union_map_get_space(RAW));
+ for (ScopStmt &Stmt : S) {
+ for (MemoryAccess *MA : Stmt) {
+ if (!MA->isReductionLike())
+ continue;
+ isl_set *AccDomW = isl_map_wrap(MA->getAccessRelation().release());
+ isl_map *Identity =
+ isl_map_from_domain_and_range(isl_set_copy(AccDomW), AccDomW);
+ RED = isl_union_map_add_map(RED, Identity);
+ }
+ }
+
+ // Step 2)
+ RED = isl_union_map_intersect(RED, isl_union_map_copy(RAW));
+ RED = isl_union_map_intersect(RED, StrictWAW);
+
+ if (!isl_union_map_is_empty(RED)) {
+
+ // Step 3)
+ RAW = isl_union_map_subtract(RAW, isl_union_map_copy(RED));
+ WAW = isl_union_map_subtract(WAW, isl_union_map_copy(RED));
+ WAR = isl_union_map_subtract(WAR, isl_union_map_copy(RED));
+
+ // Step 4)
+ addPrivatizationDependences();
+ } else
+ TC_RED = isl_union_map_empty(isl_union_map_get_space(RED));
+
+ LLVM_DEBUG({
+ dbgs() << "Final Wrapped Dependences:\n";
+ dump();
+ dbgs() << "\n";
+ });
+
+ // RED_SIN is used to collect all reduction dependences again after we
+ // split them according to the causing memory accesses. The current assumption
+ // is that our method of splitting will not have any leftovers. In the end
+ // we validate this assumption until we have more confidence in this method.
+ isl_union_map *RED_SIN = isl_union_map_empty(isl_union_map_get_space(RAW));
+
+ // For each reduction like memory access, check if there are reduction
+ // dependences with the access relation of the memory access as a domain
+ // (wrapped space!). If so these dependences are caused by this memory access.
+ // We then move this portion of reduction dependences back to the statement ->
+ // statement space and add a mapping from the memory access to these
+ // dependences.
+ for (ScopStmt &Stmt : S) {
+ for (MemoryAccess *MA : Stmt) {
+ if (!MA->isReductionLike())
+ continue;
+
+ isl_set *AccDomW = isl_map_wrap(MA->getAccessRelation().release());
+ isl_union_map *AccRedDepU = isl_union_map_intersect_domain(
+ isl_union_map_copy(TC_RED), isl_union_set_from_set(AccDomW));
+ if (isl_union_map_is_empty(AccRedDepU)) {
+ isl_union_map_free(AccRedDepU);
+ continue;
+ }
+
+ isl_map *AccRedDep = isl_map_from_union_map(AccRedDepU);
+ RED_SIN = isl_union_map_add_map(RED_SIN, isl_map_copy(AccRedDep));
+ AccRedDep = isl_map_zip(AccRedDep);
+ AccRedDep = isl_set_unwrap(isl_map_domain(AccRedDep));
+ setReductionDependences(MA, AccRedDep);
+ }
+ }
+
+ assert(isl_union_map_is_equal(RED_SIN, TC_RED) &&
+ "Intersecting the reduction dependence domain with the wrapped access "
+ "relation is not enough, we need to loosen the access relation also");
+ isl_union_map_free(RED_SIN);
+
+ RAW = isl_union_map_zip(RAW);
+ WAW = isl_union_map_zip(WAW);
+ WAR = isl_union_map_zip(WAR);
+ RED = isl_union_map_zip(RED);
+ TC_RED = isl_union_map_zip(TC_RED);
+
+ LLVM_DEBUG({
+ dbgs() << "Zipped Dependences:\n";
+ dump();
+ dbgs() << "\n";
+ });
+
+ RAW = isl_union_set_unwrap(isl_union_map_domain(RAW));
+ WAW = isl_union_set_unwrap(isl_union_map_domain(WAW));
+ WAR = isl_union_set_unwrap(isl_union_map_domain(WAR));
+ RED = isl_union_set_unwrap(isl_union_map_domain(RED));
+ TC_RED = isl_union_set_unwrap(isl_union_map_domain(TC_RED));
+
+ LLVM_DEBUG({
+ dbgs() << "Unwrapped Dependences:\n";
+ dump();
+ dbgs() << "\n";
+ });
+
+ RAW = isl_union_map_union(RAW, STMT_RAW);
+ WAW = isl_union_map_union(WAW, STMT_WAW);
+ WAR = isl_union_map_union(WAR, STMT_WAR);
+
+ RAW = isl_union_map_coalesce(RAW);
+ WAW = isl_union_map_coalesce(WAW);
+ WAR = isl_union_map_coalesce(WAR);
+ RED = isl_union_map_coalesce(RED);
+ TC_RED = isl_union_map_coalesce(TC_RED);
+
+ LLVM_DEBUG(dump());
+}
+
+bool Dependences::isValidSchedule(Scop &S, isl::schedule NewSched) const {
+ // TODO: Also check permutable/coincident flags as well.
+
+ StatementToIslMapTy NewSchedules;
+ for (auto NewMap : NewSched.get_map().get_map_list()) {
+ auto Stmt = reinterpret_cast<ScopStmt *>(
+ NewMap.get_tuple_id(isl::dim::in).get_user());
+ NewSchedules[Stmt] = NewMap;
+ }
+
+ return isValidSchedule(S, NewSchedules);
+}
+
+bool Dependences::isValidSchedule(
+ Scop &S, const StatementToIslMapTy &NewSchedule) const {
+ if (LegalityCheckDisabled)
+ return true;
+
+ isl::union_map Dependences = getDependences(TYPE_RAW | TYPE_WAW | TYPE_WAR);
+ isl::union_map Schedule = isl::union_map::empty(S.getIslCtx());
+
+ isl::space ScheduleSpace;
+
+ for (ScopStmt &Stmt : S) {
+ isl::map StmtScat;
+
+ auto Lookup = NewSchedule.find(&Stmt);
+ if (Lookup == NewSchedule.end())
+ StmtScat = Stmt.getSchedule();
+ else
+ StmtScat = Lookup->second;
+ assert(!StmtScat.is_null() &&
+ "Schedules that contain extension nodes require special handling.");
+
+ if (ScheduleSpace.is_null())
+ ScheduleSpace = StmtScat.get_space().range();
+
+ Schedule = Schedule.unite(StmtScat);
+ }
+
+ Dependences = Dependences.apply_domain(Schedule);
+ Dependences = Dependences.apply_range(Schedule);
+
+ isl::set Zero = isl::set::universe(ScheduleSpace);
+ for (auto i : rangeIslSize(0, Zero.tuple_dim()))
+ Zero = Zero.fix_si(isl::dim::set, i, 0);
+
+ isl::union_set UDeltas = Dependences.deltas();
+ isl::set Deltas = singleton(UDeltas, ScheduleSpace);
+
+ isl::space Space = Deltas.get_space();
+ isl::map NonPositive = isl::map::universe(Space.map_from_set());
+ NonPositive =
+ NonPositive.lex_le_at(isl::multi_pw_aff::identity_on_domain(Space));
+ NonPositive = NonPositive.intersect_domain(Deltas);
+ NonPositive = NonPositive.intersect_range(Zero);
+
+ return NonPositive.is_empty();
+}
+
+// Check if the current scheduling dimension is parallel.
+//
+// We check for parallelism by verifying that the loop does not carry any
+// dependences.
+//
+// Parallelism test: if the distance is zero in all outer dimensions, then it
+// has to be zero in the current dimension as well.
+//
+// Implementation: first, translate dependences into time space, then force
+// outer dimensions to be equal. If the distance is zero in the current
+// dimension, then the loop is parallel. The distance is zero in the current
+// dimension if it is a subset of a map with equal values for the current
+// dimension.
+bool Dependences::isParallel(isl_union_map *Schedule, isl_union_map *Deps,
+ isl_pw_aff **MinDistancePtr) const {
+ isl_set *Deltas, *Distance;
+ isl_map *ScheduleDeps;
+ unsigned Dimension;
+ bool IsParallel;
+
+ Deps = isl_union_map_apply_range(Deps, isl_union_map_copy(Schedule));
+ Deps = isl_union_map_apply_domain(Deps, isl_union_map_copy(Schedule));
+
+ if (isl_union_map_is_empty(Deps)) {
+ isl_union_map_free(Deps);
+ return true;
+ }
+
+ ScheduleDeps = isl_map_from_union_map(Deps);
+ Dimension = isl_map_dim(ScheduleDeps, isl_dim_out) - 1;
+
+ for (unsigned i = 0; i < Dimension; i++)
+ ScheduleDeps = isl_map_equate(ScheduleDeps, isl_dim_out, i, isl_dim_in, i);
+
+ Deltas = isl_map_deltas(ScheduleDeps);
+ Distance = isl_set_universe(isl_set_get_space(Deltas));
+
+ // [0, ..., 0, +] - All zeros and last dimension larger than zero
+ for (unsigned i = 0; i < Dimension; i++)
+ Distance = isl_set_fix_si(Distance, isl_dim_set, i, 0);
+
+ Distance = isl_set_lower_bound_si(Distance, isl_dim_set, Dimension, 1);
+ Distance = isl_set_intersect(Distance, Deltas);
+
+ IsParallel = isl_set_is_empty(Distance);
+ if (IsParallel || !MinDistancePtr) {
+ isl_set_free(Distance);
+ return IsParallel;
+ }
+
+ Distance = isl_set_project_out(Distance, isl_dim_set, 0, Dimension);
+ Distance = isl_set_coalesce(Distance);
+
+ // This last step will compute a expression for the minimal value in the
+ // distance polyhedron Distance with regards to the first (outer most)
+ // dimension.
+ *MinDistancePtr = isl_pw_aff_coalesce(isl_set_dim_min(Distance, 0));
+
+ return false;
+}
+
+static void printDependencyMap(raw_ostream &OS, __isl_keep isl_union_map *DM) {
+ if (DM)
+ OS << DM << "\n";
+ else
+ OS << "n/a\n";
+}
+
+void Dependences::print(raw_ostream &OS) const {
+ OS << "\tRAW dependences:\n\t\t";
+ printDependencyMap(OS, RAW);
+ OS << "\tWAR dependences:\n\t\t";
+ printDependencyMap(OS, WAR);
+ OS << "\tWAW dependences:\n\t\t";
+ printDependencyMap(OS, WAW);
+ OS << "\tReduction dependences:\n\t\t";
+ printDependencyMap(OS, RED);
+ OS << "\tTransitive closure of reduction dependences:\n\t\t";
+ printDependencyMap(OS, TC_RED);
+}
+
+void Dependences::dump() const { print(dbgs()); }
+
+void Dependences::releaseMemory() {
+ isl_union_map_free(RAW);
+ isl_union_map_free(WAR);
+ isl_union_map_free(WAW);
+ isl_union_map_free(RED);
+ isl_union_map_free(TC_RED);
+
+ RED = RAW = WAR = WAW = TC_RED = nullptr;
+
+ for (auto &ReductionDeps : ReductionDependences)
+ isl_map_free(ReductionDeps.second);
+ ReductionDependences.clear();
+}
+
+isl::union_map Dependences::getDependences(int Kinds) const {
+ assert(hasValidDependences() && "No valid dependences available");
+ isl::space Space = isl::manage_copy(RAW).get_space();
+ isl::union_map Deps = Deps.empty(Space.ctx());
+
+ if (Kinds & TYPE_RAW)
+ Deps = Deps.unite(isl::manage_copy(RAW));
+
+ if (Kinds & TYPE_WAR)
+ Deps = Deps.unite(isl::manage_copy(WAR));
+
+ if (Kinds & TYPE_WAW)
+ Deps = Deps.unite(isl::manage_copy(WAW));
+
+ if (Kinds & TYPE_RED)
+ Deps = Deps.unite(isl::manage_copy(RED));
+
+ if (Kinds & TYPE_TC_RED)
+ Deps = Deps.unite(isl::manage_copy(TC_RED));
+
+ Deps = Deps.coalesce();
+ Deps = Deps.detect_equalities();
+ return Deps;
+}
+
+bool Dependences::hasValidDependences() const {
+ return (RAW != nullptr) && (WAR != nullptr) && (WAW != nullptr);
+}
+
+__isl_give isl_map *
+Dependences::getReductionDependences(MemoryAccess *MA) const {
+ return isl_map_copy(ReductionDependences.lookup(MA));
+}
+
+void Dependences::setReductionDependences(MemoryAccess *MA, isl_map *D) {
+ assert(ReductionDependences.count(MA) == 0 &&
+ "Reduction dependences set twice!");
+ ReductionDependences[MA] = D;
+}
+
+const Dependences &
+DependenceAnalysis::Result::getDependences(Dependences::AnalysisLevel Level) {
+ if (Dependences *d = D[Level].get())
+ return *d;
+
+ return recomputeDependences(Level);
+}
+
+const Dependences &DependenceAnalysis::Result::recomputeDependences(
+ Dependences::AnalysisLevel Level) {
+ D[Level].reset(new Dependences(S.getSharedIslCtx(), Level));
+ D[Level]->calculateDependences(S);
+ return *D[Level];
+}
+
+DependenceAnalysis::Result
+DependenceAnalysis::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR) {
+ return {S, {}};
+}
+
+AnalysisKey DependenceAnalysis::Key;
+
+PreservedAnalyses
+DependenceInfoPrinterPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &U) {
+ auto &DI = SAM.getResult<DependenceAnalysis>(S, SAR);
+
+ if (auto d = DI.D[OptAnalysisLevel].get()) {
+ d->print(OS);
+ return PreservedAnalyses::all();
+ }
+
+ // Otherwise create the dependences on-the-fly and print them
+ Dependences D(S.getSharedIslCtx(), OptAnalysisLevel);
+ D.calculateDependences(S);
+ D.print(OS);
+
+ return PreservedAnalyses::all();
+}
+
+const Dependences &
+DependenceInfo::getDependences(Dependences::AnalysisLevel Level) {
+ if (Dependences *d = D[Level].get())
+ return *d;
+
+ return recomputeDependences(Level);
+}
+
+const Dependences &
+DependenceInfo::recomputeDependences(Dependences::AnalysisLevel Level) {
+ D[Level].reset(new Dependences(S->getSharedIslCtx(), Level));
+ D[Level]->calculateDependences(*S);
+ return *D[Level];
+}
+
+bool DependenceInfo::runOnScop(Scop &ScopVar) {
+ S = &ScopVar;
+ return false;
+}
+
+/// Print the dependences for the given SCoP to @p OS.
+
+void polly::DependenceInfo::printScop(raw_ostream &OS, Scop &S) const {
+ if (auto d = D[OptAnalysisLevel].get()) {
+ d->print(OS);
+ return;
+ }
+
+ // Otherwise create the dependences on-the-fly and print it
+ Dependences D(S.getSharedIslCtx(), OptAnalysisLevel);
+ D.calculateDependences(S);
+ D.print(OS);
+}
+
+void DependenceInfo::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequiredTransitive<ScopInfoRegionPass>();
+ AU.setPreservesAll();
+}
+
+char DependenceInfo::ID = 0;
+
+Pass *polly::createDependenceInfoPass() { return new DependenceInfo(); }
+
+INITIALIZE_PASS_BEGIN(DependenceInfo, "polly-dependences",
+ "Polly - Calculate dependences", false, false);
+INITIALIZE_PASS_DEPENDENCY(ScopInfoRegionPass);
+INITIALIZE_PASS_END(DependenceInfo, "polly-dependences",
+ "Polly - Calculate dependences", false, false)
+
+//===----------------------------------------------------------------------===//
+const Dependences &
+DependenceInfoWrapperPass::getDependences(Scop *S,
+ Dependences::AnalysisLevel Level) {
+ auto It = ScopToDepsMap.find(S);
+ if (It != ScopToDepsMap.end())
+ if (It->second) {
+ if (It->second->getDependenceLevel() == Level)
+ return *It->second.get();
+ }
+ return recomputeDependences(S, Level);
+}
+
+const Dependences &DependenceInfoWrapperPass::recomputeDependences(
+ Scop *S, Dependences::AnalysisLevel Level) {
+ std::unique_ptr<Dependences> D(new Dependences(S->getSharedIslCtx(), Level));
+ D->calculateDependences(*S);
+ auto Inserted = ScopToDepsMap.insert(std::make_pair(S, std::move(D)));
+ return *Inserted.first->second;
+}
+
+bool DependenceInfoWrapperPass::runOnFunction(Function &F) {
+ auto &SI = *getAnalysis<ScopInfoWrapperPass>().getSI();
+ for (auto &It : SI) {
+ assert(It.second && "Invalid SCoP object!");
+ recomputeDependences(It.second.get(), Dependences::AL_Access);
+ }
+ return false;
+}
+
+void DependenceInfoWrapperPass::print(raw_ostream &OS, const Module *M) const {
+ for (auto &It : ScopToDepsMap) {
+ assert((It.first && It.second) && "Invalid Scop or Dependence object!\n");
+ It.second->print(OS);
+ }
+}
+
+void DependenceInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequiredTransitive<ScopInfoWrapperPass>();
+ AU.setPreservesAll();
+}
+
+char DependenceInfoWrapperPass::ID = 0;
+
+Pass *polly::createDependenceInfoWrapperPassPass() {
+ return new DependenceInfoWrapperPass();
+}
+
+INITIALIZE_PASS_BEGIN(
+ DependenceInfoWrapperPass, "polly-function-dependences",
+ "Polly - Calculate dependences for all the SCoPs of a function", false,
+ false)
+INITIALIZE_PASS_DEPENDENCY(ScopInfoWrapperPass);
+INITIALIZE_PASS_END(
+ DependenceInfoWrapperPass, "polly-function-dependences",
+ "Polly - Calculate dependences for all the SCoPs of a function", false,
+ false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Analysis/PolyhedralInfo.cpp b/contrib/libs/llvm14/tools/polly/lib/Analysis/PolyhedralInfo.cpp
new file mode 100644
index 00000000000..d5c258bb526
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Analysis/PolyhedralInfo.cpp
@@ -0,0 +1,167 @@
+//===--------- PolyhedralInfo.cpp - Create Scops from LLVM IR-------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// An interface to the Polyhedral analysis engine(Polly) of LLVM.
+//
+// This pass provides an interface to the polyhedral analysis performed by
+// Polly.
+//
+// This interface provides basic interface like isParallel, isVectorizable
+// that can be used in LLVM transformation passes.
+//
+// Work in progress, this file is subject to change.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/PolyhedralInfo.h"
+#include "polly/DependenceInfo.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/GICHelper.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/Debug.h"
+#include "isl/union_map.h"
+
+using namespace llvm;
+using namespace polly;
+
+#define DEBUG_TYPE "polyhedral-info"
+
+static cl::opt<bool> CheckParallel("polly-check-parallel",
+ cl::desc("Check for parallel loops"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool> CheckVectorizable("polly-check-vectorizable",
+ cl::desc("Check for vectorizable loops"),
+ cl::Hidden, cl::init(false),
+ cl::ZeroOrMore, cl::cat(PollyCategory));
+
+void PolyhedralInfo::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequiredTransitive<DependenceInfoWrapperPass>();
+ AU.addRequired<LoopInfoWrapperPass>();
+ AU.addRequiredTransitive<ScopInfoWrapperPass>();
+ AU.setPreservesAll();
+}
+
+bool PolyhedralInfo::runOnFunction(Function &F) {
+ DI = &getAnalysis<DependenceInfoWrapperPass>();
+ SI = getAnalysis<ScopInfoWrapperPass>().getSI();
+ return false;
+}
+
+void PolyhedralInfo::print(raw_ostream &OS, const Module *) const {
+ auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+ for (auto *TopLevelLoop : LI) {
+ for (auto *L : depth_first(TopLevelLoop)) {
+ OS.indent(2) << L->getHeader()->getName() << ":\t";
+ if (CheckParallel && isParallel(L))
+ OS << "Loop is parallel.\n";
+ else if (CheckParallel)
+ OS << "Loop is not parallel.\n";
+ }
+ }
+}
+
+bool PolyhedralInfo::checkParallel(Loop *L, isl_pw_aff **MinDepDistPtr) const {
+ bool IsParallel;
+ const Scop *S = getScopContainingLoop(L);
+ if (!S)
+ return false;
+ const Dependences &D =
+ DI->getDependences(const_cast<Scop *>(S), Dependences::AL_Access);
+ if (!D.hasValidDependences())
+ return false;
+ LLVM_DEBUG(dbgs() << "Loop :\t" << L->getHeader()->getName() << ":\n");
+
+ isl_union_map *Deps =
+ D.getDependences(Dependences::TYPE_RAW | Dependences::TYPE_WAW |
+ Dependences::TYPE_WAR | Dependences::TYPE_RED)
+ .release();
+
+ LLVM_DEBUG(dbgs() << "Dependences :\t" << stringFromIslObj(Deps, "null")
+ << "\n");
+
+ isl_union_map *Schedule = getScheduleForLoop(S, L);
+ LLVM_DEBUG(dbgs() << "Schedule: \t" << stringFromIslObj(Schedule, "null")
+ << "\n");
+
+ IsParallel = D.isParallel(Schedule, Deps, MinDepDistPtr);
+ isl_union_map_free(Schedule);
+ return IsParallel;
+}
+
+bool PolyhedralInfo::isParallel(Loop *L) const { return checkParallel(L); }
+
+const Scop *PolyhedralInfo::getScopContainingLoop(Loop *L) const {
+ assert((SI) && "ScopInfoWrapperPass is required by PolyhedralInfo pass!\n");
+ for (auto &It : *SI) {
+ Region *R = It.first;
+ if (R->contains(L))
+ return It.second.get();
+ }
+ return nullptr;
+}
+
+// Given a Loop and the containing SCoP, we compute the partial schedule
+// by taking union of individual schedules of each ScopStmt within the loop
+// and projecting out the inner dimensions from the range of the schedule.
+// for (i = 0; i < n; i++)
+// for (j = 0; j < n; j++)
+// A[j] = 1; //Stmt
+//
+// The original schedule will be
+// Stmt[i0, i1] -> [i0, i1]
+// The schedule for the outer loop will be
+// Stmt[i0, i1] -> [i0]
+// The schedule for the inner loop will be
+// Stmt[i0, i1] -> [i0, i1]
+__isl_give isl_union_map *PolyhedralInfo::getScheduleForLoop(const Scop *S,
+ Loop *L) const {
+ isl_union_map *Schedule = isl_union_map_empty(S->getParamSpace().release());
+ int CurrDim = S->getRelativeLoopDepth(L);
+ LLVM_DEBUG(dbgs() << "Relative loop depth:\t" << CurrDim << "\n");
+ assert(CurrDim >= 0 && "Loop in region should have at least depth one");
+
+ for (auto &SS : *S) {
+ if (L->contains(SS.getSurroundingLoop())) {
+
+ unsigned int MaxDim = SS.getNumIterators();
+ LLVM_DEBUG(dbgs() << "Maximum depth of Stmt:\t" << MaxDim << "\n");
+ isl_map *ScheduleMap = SS.getSchedule().release();
+ assert(
+ ScheduleMap &&
+ "Schedules that contain extension nodes require special handling.");
+
+ ScheduleMap = isl_map_project_out(ScheduleMap, isl_dim_out, CurrDim + 1,
+ MaxDim - CurrDim - 1);
+ ScheduleMap = isl_map_set_tuple_id(ScheduleMap, isl_dim_in,
+ SS.getDomainId().release());
+ Schedule =
+ isl_union_map_union(Schedule, isl_union_map_from_map(ScheduleMap));
+ }
+ }
+ Schedule = isl_union_map_coalesce(Schedule);
+ return Schedule;
+}
+
+char PolyhedralInfo::ID = 0;
+
+Pass *polly::createPolyhedralInfoPass() { return new PolyhedralInfo(); }
+
+INITIALIZE_PASS_BEGIN(PolyhedralInfo, "polyhedral-info",
+ "Polly - Interface to polyhedral analysis engine", false,
+ false);
+INITIALIZE_PASS_DEPENDENCY(DependenceInfoWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(ScopInfoWrapperPass);
+INITIALIZE_PASS_END(PolyhedralInfo, "polyhedral-info",
+ "Polly - Interface to polyhedral analysis engine", false,
+ false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Analysis/PruneUnprofitable.cpp b/contrib/libs/llvm14/tools/polly/lib/Analysis/PruneUnprofitable.cpp
new file mode 100644
index 00000000000..abcbf3d809d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Analysis/PruneUnprofitable.cpp
@@ -0,0 +1,123 @@
+//===- PruneUnprofitable.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Mark a SCoP as unfeasible if not deemed profitable to optimize.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/PruneUnprofitable.h"
+#include "polly/ScopDetection.h"
+#include "polly/ScopInfo.h"
+#include "polly/ScopPass.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/IR/DebugLoc.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace polly;
+
+#define DEBUG_TYPE "polly-prune-unprofitable"
+
+namespace {
+
+STATISTIC(ScopsProcessed,
+ "Number of SCoPs considered for unprofitability pruning");
+STATISTIC(ScopsPruned, "Number of pruned SCoPs because it they cannot be "
+ "optimized in a significant way");
+STATISTIC(ScopsSurvived, "Number of SCoPs after pruning");
+
+STATISTIC(NumPrunedLoops, "Number of pruned loops");
+STATISTIC(NumPrunedBoxedLoops, "Number of pruned boxed loops");
+STATISTIC(NumPrunedAffineLoops, "Number of pruned affine loops");
+
+STATISTIC(NumLoopsInScop, "Number of loops in scops after pruning");
+STATISTIC(NumBoxedLoops, "Number of boxed loops in SCoPs after pruning");
+STATISTIC(NumAffineLoops, "Number of affine loops in SCoPs after pruning");
+
+static void updateStatistics(Scop &S, bool Pruned) {
+ Scop::ScopStatistics ScopStats = S.getStatistics();
+ if (Pruned) {
+ ScopsPruned++;
+ NumPrunedLoops += ScopStats.NumAffineLoops + ScopStats.NumBoxedLoops;
+ NumPrunedBoxedLoops += ScopStats.NumBoxedLoops;
+ NumPrunedAffineLoops += ScopStats.NumAffineLoops;
+ } else {
+ ScopsSurvived++;
+ NumLoopsInScop += ScopStats.NumAffineLoops + ScopStats.NumBoxedLoops;
+ NumBoxedLoops += ScopStats.NumBoxedLoops;
+ NumAffineLoops += ScopStats.NumAffineLoops;
+ }
+}
+
+static bool runPruneUnprofitable(Scop &S) {
+ if (PollyProcessUnprofitable) {
+ LLVM_DEBUG(
+ dbgs() << "NOTE: -polly-process-unprofitable active, won't prune "
+ "anything\n");
+ return false;
+ }
+
+ ScopsProcessed++;
+
+ if (!S.isProfitable(true)) {
+ LLVM_DEBUG(
+ dbgs() << "SCoP pruned because it probably cannot be optimized in "
+ "a significant way\n");
+ S.invalidate(PROFITABLE, DebugLoc());
+ updateStatistics(S, true);
+ } else {
+ updateStatistics(S, false);
+ }
+
+ return false;
+}
+
+class PruneUnprofitableWrapperPass : public ScopPass {
+public:
+ static char ID;
+
+ explicit PruneUnprofitableWrapperPass() : ScopPass(ID) {}
+ PruneUnprofitableWrapperPass(const PruneUnprofitableWrapperPass &) = delete;
+ PruneUnprofitableWrapperPass &
+ operator=(const PruneUnprofitableWrapperPass &) = delete;
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequired<ScopInfoRegionPass>();
+ AU.setPreservesAll();
+ }
+
+ bool runOnScop(Scop &S) override { return runPruneUnprofitable(S); }
+};
+} // namespace
+
+char PruneUnprofitableWrapperPass::ID;
+
+Pass *polly::createPruneUnprofitableWrapperPass() {
+ return new PruneUnprofitableWrapperPass();
+}
+
+INITIALIZE_PASS_BEGIN(PruneUnprofitableWrapperPass, "polly-prune-unprofitable",
+ "Polly - Prune unprofitable SCoPs", false, false)
+INITIALIZE_PASS_END(PruneUnprofitableWrapperPass, "polly-prune-unprofitable",
+ "Polly - Prune unprofitable SCoPs", false, false)
+
+llvm::PreservedAnalyses
+PruneUnprofitablePass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U) {
+ bool Changed = runPruneUnprofitable(S);
+
+ if (!Changed)
+ return PreservedAnalyses::all();
+
+ PreservedAnalyses PA;
+ PA.preserveSet<AllAnalysesOn<Module>>();
+ PA.preserveSet<AllAnalysesOn<Function>>();
+ PA.preserveSet<AllAnalysesOn<Loop>>();
+ return PA;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopBuilder.cpp b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopBuilder.cpp
new file mode 100644
index 00000000000..d06d5b72b71
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopBuilder.cpp
@@ -0,0 +1,3650 @@
+//===- ScopBuilder.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Create a polyhedral description for a static control flow region.
+//
+// The pass creates a polyhedral description of the Scops detected by the SCoP
+// detection derived from their LLVM-IR code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/ScopBuilder.h"
+#include "polly/Options.h"
+#include "polly/ScopDetection.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/GICHelper.h"
+#include "polly/Support/ISLTools.h"
+#include "polly/Support/SCEVValidator.h"
+#include "polly/Support/ScopHelper.h"
+#include "polly/Support/VirtualInstruction.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/EquivalenceClasses.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/Analysis/AssumptionCache.h"
+#include "llvm/Analysis/Delinearization.h"
+#include "llvm/Analysis/Loads.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/RegionIterator.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DebugLoc.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Use.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+
+using namespace llvm;
+using namespace polly;
+
+#define DEBUG_TYPE "polly-scops"
+
+STATISTIC(ScopFound, "Number of valid Scops");
+STATISTIC(RichScopFound, "Number of Scops containing a loop");
+STATISTIC(InfeasibleScops,
+ "Number of SCoPs with statically infeasible context.");
+
+bool polly::ModelReadOnlyScalars;
+
+// The maximal number of dimensions we allow during invariant load construction.
+// More complex access ranges will result in very high compile time and are also
+// unlikely to result in good code. This value is very high and should only
+// trigger for corner cases (e.g., the "dct_luma" function in h264, SPEC2006).
+static unsigned const MaxDimensionsInAccessRange = 9;
+
+static cl::opt<bool, true> XModelReadOnlyScalars(
+ "polly-analyze-read-only-scalars",
+ cl::desc("Model read-only scalar values in the scop description"),
+ cl::location(ModelReadOnlyScalars), cl::Hidden, cl::ZeroOrMore,
+ cl::init(true), cl::cat(PollyCategory));
+
+static cl::opt<int>
+ OptComputeOut("polly-analysis-computeout",
+ cl::desc("Bound the scop analysis by a maximal amount of "
+ "computational steps (0 means no bound)"),
+ cl::Hidden, cl::init(800000), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool> PollyAllowDereferenceOfAllFunctionParams(
+ "polly-allow-dereference-of-all-function-parameters",
+ cl::desc(
+ "Treat all parameters to functions that are pointers as dereferencible."
+ " This is useful for invariant load hoisting, since we can generate"
+ " less runtime checks. This is only valid if all pointers to functions"
+ " are always initialized, so that Polly can choose to hoist"
+ " their loads. "),
+ cl::Hidden, cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ PollyIgnoreInbounds("polly-ignore-inbounds",
+ cl::desc("Do not take inbounds assumptions at all"),
+ cl::Hidden, cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<unsigned> RunTimeChecksMaxArraysPerGroup(
+ "polly-rtc-max-arrays-per-group",
+ cl::desc("The maximal number of arrays to compare in each alias group."),
+ cl::Hidden, cl::ZeroOrMore, cl::init(20), cl::cat(PollyCategory));
+
+static cl::opt<unsigned> RunTimeChecksMaxAccessDisjuncts(
+ "polly-rtc-max-array-disjuncts",
+ cl::desc("The maximal number of disjunts allowed in memory accesses to "
+ "to build RTCs."),
+ cl::Hidden, cl::ZeroOrMore, cl::init(8), cl::cat(PollyCategory));
+
+static cl::opt<unsigned> RunTimeChecksMaxParameters(
+ "polly-rtc-max-parameters",
+ cl::desc("The maximal number of parameters allowed in RTCs."), cl::Hidden,
+ cl::ZeroOrMore, cl::init(8), cl::cat(PollyCategory));
+
+static cl::opt<bool> UnprofitableScalarAccs(
+ "polly-unprofitable-scalar-accs",
+ cl::desc("Count statements with scalar accesses as not optimizable"),
+ cl::Hidden, cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<std::string> UserContextStr(
+ "polly-context", cl::value_desc("isl parameter set"),
+ cl::desc("Provide additional constraints on the context parameters"),
+ cl::init(""), cl::cat(PollyCategory));
+
+static cl::opt<bool> DetectReductions("polly-detect-reductions",
+ cl::desc("Detect and exploit reductions"),
+ cl::Hidden, cl::ZeroOrMore,
+ cl::init(true), cl::cat(PollyCategory));
+
+// Multiplicative reductions can be disabled separately as these kind of
+// operations can overflow easily. Additive reductions and bit operations
+// are in contrast pretty stable.
+static cl::opt<bool> DisableMultiplicativeReductions(
+ "polly-disable-multiplicative-reductions",
+ cl::desc("Disable multiplicative reductions"), cl::Hidden, cl::ZeroOrMore,
+ cl::init(false), cl::cat(PollyCategory));
+
+enum class GranularityChoice { BasicBlocks, ScalarIndependence, Stores };
+
+static cl::opt<GranularityChoice> StmtGranularity(
+ "polly-stmt-granularity",
+ cl::desc(
+ "Algorithm to use for splitting basic blocks into multiple statements"),
+ cl::values(clEnumValN(GranularityChoice::BasicBlocks, "bb",
+ "One statement per basic block"),
+ clEnumValN(GranularityChoice::ScalarIndependence, "scalar-indep",
+ "Scalar independence heuristic"),
+ clEnumValN(GranularityChoice::Stores, "store",
+ "Store-level granularity")),
+ cl::init(GranularityChoice::ScalarIndependence), cl::cat(PollyCategory));
+
+/// Helper to treat non-affine regions and basic blocks the same.
+///
+///{
+
+/// Return the block that is the representing block for @p RN.
+static inline BasicBlock *getRegionNodeBasicBlock(RegionNode *RN) {
+ return RN->isSubRegion() ? RN->getNodeAs<Region>()->getEntry()
+ : RN->getNodeAs<BasicBlock>();
+}
+
+/// Return the @p idx'th block that is executed after @p RN.
+static inline BasicBlock *
+getRegionNodeSuccessor(RegionNode *RN, Instruction *TI, unsigned idx) {
+ if (RN->isSubRegion()) {
+ assert(idx == 0);
+ return RN->getNodeAs<Region>()->getExit();
+ }
+ return TI->getSuccessor(idx);
+}
+
+static bool containsErrorBlock(RegionNode *RN, const Region &R,
+ ScopDetection *SD) {
+ if (!RN->isSubRegion())
+ return SD->isErrorBlock(*RN->getNodeAs<BasicBlock>(), R);
+ for (BasicBlock *BB : RN->getNodeAs<Region>()->blocks())
+ if (SD->isErrorBlock(*BB, R))
+ return true;
+ return false;
+}
+
+///}
+
+/// Create a map to map from a given iteration to a subsequent iteration.
+///
+/// This map maps from SetSpace -> SetSpace where the dimensions @p Dim
+/// is incremented by one and all other dimensions are equal, e.g.,
+/// [i0, i1, i2, i3] -> [i0, i1, i2 + 1, i3]
+///
+/// if @p Dim is 2 and @p SetSpace has 4 dimensions.
+static isl::map createNextIterationMap(isl::space SetSpace, unsigned Dim) {
+ isl::space MapSpace = SetSpace.map_from_set();
+ isl::map NextIterationMap = isl::map::universe(MapSpace);
+ for (unsigned u : rangeIslSize(0, NextIterationMap.domain_tuple_dim()))
+ if (u != Dim)
+ NextIterationMap =
+ NextIterationMap.equate(isl::dim::in, u, isl::dim::out, u);
+ isl::constraint C =
+ isl::constraint::alloc_equality(isl::local_space(MapSpace));
+ C = C.set_constant_si(1);
+ C = C.set_coefficient_si(isl::dim::in, Dim, 1);
+ C = C.set_coefficient_si(isl::dim::out, Dim, -1);
+ NextIterationMap = NextIterationMap.add_constraint(C);
+ return NextIterationMap;
+}
+
+/// Add @p BSet to set @p BoundedParts if @p BSet is bounded.
+static isl::set collectBoundedParts(isl::set S) {
+ isl::set BoundedParts = isl::set::empty(S.get_space());
+ for (isl::basic_set BSet : S.get_basic_set_list())
+ if (BSet.is_bounded())
+ BoundedParts = BoundedParts.unite(isl::set(BSet));
+ return BoundedParts;
+}
+
+/// Compute the (un)bounded parts of @p S wrt. to dimension @p Dim.
+///
+/// @returns A separation of @p S into first an unbounded then a bounded subset,
+/// both with regards to the dimension @p Dim.
+static std::pair<isl::set, isl::set> partitionSetParts(isl::set S,
+ unsigned Dim) {
+ for (unsigned u : rangeIslSize(0, S.tuple_dim()))
+ S = S.lower_bound_si(isl::dim::set, u, 0);
+
+ unsigned NumDimsS = unsignedFromIslSize(S.tuple_dim());
+ isl::set OnlyDimS = S;
+
+ // Remove dimensions that are greater than Dim as they are not interesting.
+ assert(NumDimsS >= Dim + 1);
+ OnlyDimS = OnlyDimS.project_out(isl::dim::set, Dim + 1, NumDimsS - Dim - 1);
+
+ // Create artificial parametric upper bounds for dimensions smaller than Dim
+ // as we are not interested in them.
+ OnlyDimS = OnlyDimS.insert_dims(isl::dim::param, 0, Dim);
+
+ for (unsigned u = 0; u < Dim; u++) {
+ isl::constraint C = isl::constraint::alloc_inequality(
+ isl::local_space(OnlyDimS.get_space()));
+ C = C.set_coefficient_si(isl::dim::param, u, 1);
+ C = C.set_coefficient_si(isl::dim::set, u, -1);
+ OnlyDimS = OnlyDimS.add_constraint(C);
+ }
+
+ // Collect all bounded parts of OnlyDimS.
+ isl::set BoundedParts = collectBoundedParts(OnlyDimS);
+
+ // Create the dimensions greater than Dim again.
+ BoundedParts =
+ BoundedParts.insert_dims(isl::dim::set, Dim + 1, NumDimsS - Dim - 1);
+
+ // Remove the artificial upper bound parameters again.
+ BoundedParts = BoundedParts.remove_dims(isl::dim::param, 0, Dim);
+
+ isl::set UnboundedParts = S.subtract(BoundedParts);
+ return std::make_pair(UnboundedParts, BoundedParts);
+}
+
+/// Create the conditions under which @p L @p Pred @p R is true.
+static isl::set buildConditionSet(ICmpInst::Predicate Pred, isl::pw_aff L,
+ isl::pw_aff R) {
+ switch (Pred) {
+ case ICmpInst::ICMP_EQ:
+ return L.eq_set(R);
+ case ICmpInst::ICMP_NE:
+ return L.ne_set(R);
+ case ICmpInst::ICMP_SLT:
+ return L.lt_set(R);
+ case ICmpInst::ICMP_SLE:
+ return L.le_set(R);
+ case ICmpInst::ICMP_SGT:
+ return L.gt_set(R);
+ case ICmpInst::ICMP_SGE:
+ return L.ge_set(R);
+ case ICmpInst::ICMP_ULT:
+ return L.lt_set(R);
+ case ICmpInst::ICMP_UGT:
+ return L.gt_set(R);
+ case ICmpInst::ICMP_ULE:
+ return L.le_set(R);
+ case ICmpInst::ICMP_UGE:
+ return L.ge_set(R);
+ default:
+ llvm_unreachable("Non integer predicate not supported");
+ }
+}
+
+isl::set ScopBuilder::adjustDomainDimensions(isl::set Dom, Loop *OldL,
+ Loop *NewL) {
+ // If the loops are the same there is nothing to do.
+ if (NewL == OldL)
+ return Dom;
+
+ int OldDepth = scop->getRelativeLoopDepth(OldL);
+ int NewDepth = scop->getRelativeLoopDepth(NewL);
+ // If both loops are non-affine loops there is nothing to do.
+ if (OldDepth == -1 && NewDepth == -1)
+ return Dom;
+
+ // Distinguish three cases:
+ // 1) The depth is the same but the loops are not.
+ // => One loop was left one was entered.
+ // 2) The depth increased from OldL to NewL.
+ // => One loop was entered, none was left.
+ // 3) The depth decreased from OldL to NewL.
+ // => Loops were left were difference of the depths defines how many.
+ if (OldDepth == NewDepth) {
+ assert(OldL->getParentLoop() == NewL->getParentLoop());
+ Dom = Dom.project_out(isl::dim::set, NewDepth, 1);
+ Dom = Dom.add_dims(isl::dim::set, 1);
+ } else if (OldDepth < NewDepth) {
+ assert(OldDepth + 1 == NewDepth);
+ auto &R = scop->getRegion();
+ (void)R;
+ assert(NewL->getParentLoop() == OldL ||
+ ((!OldL || !R.contains(OldL)) && R.contains(NewL)));
+ Dom = Dom.add_dims(isl::dim::set, 1);
+ } else {
+ assert(OldDepth > NewDepth);
+ unsigned Diff = OldDepth - NewDepth;
+ unsigned NumDim = unsignedFromIslSize(Dom.tuple_dim());
+ assert(NumDim >= Diff);
+ Dom = Dom.project_out(isl::dim::set, NumDim - Diff, Diff);
+ }
+
+ return Dom;
+}
+
+/// Compute the isl representation for the SCEV @p E in this BB.
+///
+/// @param BB The BB for which isl representation is to be
+/// computed.
+/// @param InvalidDomainMap A map of BB to their invalid domains.
+/// @param E The SCEV that should be translated.
+/// @param NonNegative Flag to indicate the @p E has to be non-negative.
+///
+/// Note that this function will also adjust the invalid context accordingly.
+
+__isl_give isl_pw_aff *
+ScopBuilder::getPwAff(BasicBlock *BB,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ const SCEV *E, bool NonNegative) {
+ PWACtx PWAC = scop->getPwAff(E, BB, NonNegative, &RecordedAssumptions);
+ InvalidDomainMap[BB] = InvalidDomainMap[BB].unite(PWAC.second);
+ return PWAC.first.release();
+}
+
+/// Build condition sets for unsigned ICmpInst(s).
+/// Special handling is required for unsigned operands to ensure that if
+/// MSB (aka the Sign bit) is set for an operands in an unsigned ICmpInst
+/// it should wrap around.
+///
+/// @param IsStrictUpperBound holds information on the predicate relation
+/// between TestVal and UpperBound, i.e,
+/// TestVal < UpperBound OR TestVal <= UpperBound
+__isl_give isl_set *ScopBuilder::buildUnsignedConditionSets(
+ BasicBlock *BB, Value *Condition, __isl_keep isl_set *Domain,
+ const SCEV *SCEV_TestVal, const SCEV *SCEV_UpperBound,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ bool IsStrictUpperBound) {
+ // Do not take NonNeg assumption on TestVal
+ // as it might have MSB (Sign bit) set.
+ isl_pw_aff *TestVal = getPwAff(BB, InvalidDomainMap, SCEV_TestVal, false);
+ // Take NonNeg assumption on UpperBound.
+ isl_pw_aff *UpperBound =
+ getPwAff(BB, InvalidDomainMap, SCEV_UpperBound, true);
+
+ // 0 <= TestVal
+ isl_set *First =
+ isl_pw_aff_le_set(isl_pw_aff_zero_on_domain(isl_local_space_from_space(
+ isl_pw_aff_get_domain_space(TestVal))),
+ isl_pw_aff_copy(TestVal));
+
+ isl_set *Second;
+ if (IsStrictUpperBound)
+ // TestVal < UpperBound
+ Second = isl_pw_aff_lt_set(TestVal, UpperBound);
+ else
+ // TestVal <= UpperBound
+ Second = isl_pw_aff_le_set(TestVal, UpperBound);
+
+ isl_set *ConsequenceCondSet = isl_set_intersect(First, Second);
+ return ConsequenceCondSet;
+}
+
+bool ScopBuilder::buildConditionSets(
+ BasicBlock *BB, SwitchInst *SI, Loop *L, __isl_keep isl_set *Domain,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ SmallVectorImpl<__isl_give isl_set *> &ConditionSets) {
+ Value *Condition = getConditionFromTerminator(SI);
+ assert(Condition && "No condition for switch");
+
+ isl_pw_aff *LHS, *RHS;
+ LHS = getPwAff(BB, InvalidDomainMap, SE.getSCEVAtScope(Condition, L));
+
+ unsigned NumSuccessors = SI->getNumSuccessors();
+ ConditionSets.resize(NumSuccessors);
+ for (auto &Case : SI->cases()) {
+ unsigned Idx = Case.getSuccessorIndex();
+ ConstantInt *CaseValue = Case.getCaseValue();
+
+ RHS = getPwAff(BB, InvalidDomainMap, SE.getSCEV(CaseValue));
+ isl_set *CaseConditionSet =
+ buildConditionSet(ICmpInst::ICMP_EQ, isl::manage_copy(LHS),
+ isl::manage(RHS))
+ .release();
+ ConditionSets[Idx] = isl_set_coalesce(
+ isl_set_intersect(CaseConditionSet, isl_set_copy(Domain)));
+ }
+
+ assert(ConditionSets[0] == nullptr && "Default condition set was set");
+ isl_set *ConditionSetUnion = isl_set_copy(ConditionSets[1]);
+ for (unsigned u = 2; u < NumSuccessors; u++)
+ ConditionSetUnion =
+ isl_set_union(ConditionSetUnion, isl_set_copy(ConditionSets[u]));
+ ConditionSets[0] = isl_set_subtract(isl_set_copy(Domain), ConditionSetUnion);
+
+ isl_pw_aff_free(LHS);
+
+ return true;
+}
+
+bool ScopBuilder::buildConditionSets(
+ BasicBlock *BB, Value *Condition, Instruction *TI, Loop *L,
+ __isl_keep isl_set *Domain,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ SmallVectorImpl<__isl_give isl_set *> &ConditionSets) {
+ isl_set *ConsequenceCondSet = nullptr;
+
+ if (auto Load = dyn_cast<LoadInst>(Condition)) {
+ const SCEV *LHSSCEV = SE.getSCEVAtScope(Load, L);
+ const SCEV *RHSSCEV = SE.getZero(LHSSCEV->getType());
+ bool NonNeg = false;
+ isl_pw_aff *LHS = getPwAff(BB, InvalidDomainMap, LHSSCEV, NonNeg);
+ isl_pw_aff *RHS = getPwAff(BB, InvalidDomainMap, RHSSCEV, NonNeg);
+ ConsequenceCondSet = buildConditionSet(ICmpInst::ICMP_SLE, isl::manage(LHS),
+ isl::manage(RHS))
+ .release();
+ } else if (auto *PHI = dyn_cast<PHINode>(Condition)) {
+ auto *Unique = dyn_cast<ConstantInt>(
+ getUniqueNonErrorValue(PHI, &scop->getRegion(), &SD));
+ assert(Unique &&
+ "A PHINode condition should only be accepted by ScopDetection if "
+ "getUniqueNonErrorValue returns non-NULL");
+
+ if (Unique->isZero())
+ ConsequenceCondSet = isl_set_empty(isl_set_get_space(Domain));
+ else
+ ConsequenceCondSet = isl_set_universe(isl_set_get_space(Domain));
+ } else if (auto *CCond = dyn_cast<ConstantInt>(Condition)) {
+ if (CCond->isZero())
+ ConsequenceCondSet = isl_set_empty(isl_set_get_space(Domain));
+ else
+ ConsequenceCondSet = isl_set_universe(isl_set_get_space(Domain));
+ } else if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(Condition)) {
+ auto Opcode = BinOp->getOpcode();
+ assert(Opcode == Instruction::And || Opcode == Instruction::Or);
+
+ bool Valid = buildConditionSets(BB, BinOp->getOperand(0), TI, L, Domain,
+ InvalidDomainMap, ConditionSets) &&
+ buildConditionSets(BB, BinOp->getOperand(1), TI, L, Domain,
+ InvalidDomainMap, ConditionSets);
+ if (!Valid) {
+ while (!ConditionSets.empty())
+ isl_set_free(ConditionSets.pop_back_val());
+ return false;
+ }
+
+ isl_set_free(ConditionSets.pop_back_val());
+ isl_set *ConsCondPart0 = ConditionSets.pop_back_val();
+ isl_set_free(ConditionSets.pop_back_val());
+ isl_set *ConsCondPart1 = ConditionSets.pop_back_val();
+
+ if (Opcode == Instruction::And)
+ ConsequenceCondSet = isl_set_intersect(ConsCondPart0, ConsCondPart1);
+ else
+ ConsequenceCondSet = isl_set_union(ConsCondPart0, ConsCondPart1);
+ } else {
+ auto *ICond = dyn_cast<ICmpInst>(Condition);
+ assert(ICond &&
+ "Condition of exiting branch was neither constant nor ICmp!");
+
+ Region &R = scop->getRegion();
+
+ isl_pw_aff *LHS, *RHS;
+ // For unsigned comparisons we assumed the signed bit of neither operand
+ // to be set. The comparison is equal to a signed comparison under this
+ // assumption.
+ bool NonNeg = ICond->isUnsigned();
+ const SCEV *LeftOperand = SE.getSCEVAtScope(ICond->getOperand(0), L),
+ *RightOperand = SE.getSCEVAtScope(ICond->getOperand(1), L);
+
+ LeftOperand = tryForwardThroughPHI(LeftOperand, R, SE, &SD);
+ RightOperand = tryForwardThroughPHI(RightOperand, R, SE, &SD);
+
+ switch (ICond->getPredicate()) {
+ case ICmpInst::ICMP_ULT:
+ ConsequenceCondSet =
+ buildUnsignedConditionSets(BB, Condition, Domain, LeftOperand,
+ RightOperand, InvalidDomainMap, true);
+ break;
+ case ICmpInst::ICMP_ULE:
+ ConsequenceCondSet =
+ buildUnsignedConditionSets(BB, Condition, Domain, LeftOperand,
+ RightOperand, InvalidDomainMap, false);
+ break;
+ case ICmpInst::ICMP_UGT:
+ ConsequenceCondSet =
+ buildUnsignedConditionSets(BB, Condition, Domain, RightOperand,
+ LeftOperand, InvalidDomainMap, true);
+ break;
+ case ICmpInst::ICMP_UGE:
+ ConsequenceCondSet =
+ buildUnsignedConditionSets(BB, Condition, Domain, RightOperand,
+ LeftOperand, InvalidDomainMap, false);
+ break;
+ default:
+ LHS = getPwAff(BB, InvalidDomainMap, LeftOperand, NonNeg);
+ RHS = getPwAff(BB, InvalidDomainMap, RightOperand, NonNeg);
+ ConsequenceCondSet = buildConditionSet(ICond->getPredicate(),
+ isl::manage(LHS), isl::manage(RHS))
+ .release();
+ break;
+ }
+ }
+
+ // If no terminator was given we are only looking for parameter constraints
+ // under which @p Condition is true/false.
+ if (!TI)
+ ConsequenceCondSet = isl_set_params(ConsequenceCondSet);
+ assert(ConsequenceCondSet);
+ ConsequenceCondSet = isl_set_coalesce(
+ isl_set_intersect(ConsequenceCondSet, isl_set_copy(Domain)));
+
+ isl_set *AlternativeCondSet = nullptr;
+ bool TooComplex =
+ isl_set_n_basic_set(ConsequenceCondSet) >= (int)MaxDisjunctsInDomain;
+
+ if (!TooComplex) {
+ AlternativeCondSet = isl_set_subtract(isl_set_copy(Domain),
+ isl_set_copy(ConsequenceCondSet));
+ TooComplex =
+ isl_set_n_basic_set(AlternativeCondSet) >= (int)MaxDisjunctsInDomain;
+ }
+
+ if (TooComplex) {
+ scop->invalidate(COMPLEXITY, TI ? TI->getDebugLoc() : DebugLoc(),
+ TI ? TI->getParent() : nullptr /* BasicBlock */);
+ isl_set_free(AlternativeCondSet);
+ isl_set_free(ConsequenceCondSet);
+ return false;
+ }
+
+ ConditionSets.push_back(ConsequenceCondSet);
+ ConditionSets.push_back(isl_set_coalesce(AlternativeCondSet));
+
+ return true;
+}
+
+bool ScopBuilder::buildConditionSets(
+ BasicBlock *BB, Instruction *TI, Loop *L, __isl_keep isl_set *Domain,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap,
+ SmallVectorImpl<__isl_give isl_set *> &ConditionSets) {
+ if (SwitchInst *SI = dyn_cast<SwitchInst>(TI))
+ return buildConditionSets(BB, SI, L, Domain, InvalidDomainMap,
+ ConditionSets);
+
+ assert(isa<BranchInst>(TI) && "Terminator was neither branch nor switch.");
+
+ if (TI->getNumSuccessors() == 1) {
+ ConditionSets.push_back(isl_set_copy(Domain));
+ return true;
+ }
+
+ Value *Condition = getConditionFromTerminator(TI);
+ assert(Condition && "No condition for Terminator");
+
+ return buildConditionSets(BB, Condition, TI, L, Domain, InvalidDomainMap,
+ ConditionSets);
+}
+
+bool ScopBuilder::propagateDomainConstraints(
+ Region *R, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
+ // Iterate over the region R and propagate the domain constrains from the
+ // predecessors to the current node. In contrast to the
+ // buildDomainsWithBranchConstraints function, this one will pull the domain
+ // information from the predecessors instead of pushing it to the successors.
+ // Additionally, we assume the domains to be already present in the domain
+ // map here. However, we iterate again in reverse post order so we know all
+ // predecessors have been visited before a block or non-affine subregion is
+ // visited.
+
+ ReversePostOrderTraversal<Region *> RTraversal(R);
+ for (auto *RN : RTraversal) {
+ // Recurse for affine subregions but go on for basic blocks and non-affine
+ // subregions.
+ if (RN->isSubRegion()) {
+ Region *SubRegion = RN->getNodeAs<Region>();
+ if (!scop->isNonAffineSubRegion(SubRegion)) {
+ if (!propagateDomainConstraints(SubRegion, InvalidDomainMap))
+ return false;
+ continue;
+ }
+ }
+
+ BasicBlock *BB = getRegionNodeBasicBlock(RN);
+ isl::set &Domain = scop->getOrInitEmptyDomain(BB);
+ assert(!Domain.is_null());
+
+ // Under the union of all predecessor conditions we can reach this block.
+ isl::set PredDom = getPredecessorDomainConstraints(BB, Domain);
+ Domain = Domain.intersect(PredDom).coalesce();
+ Domain = Domain.align_params(scop->getParamSpace());
+
+ Loop *BBLoop = getRegionNodeLoop(RN, LI);
+ if (BBLoop && BBLoop->getHeader() == BB && scop->contains(BBLoop))
+ if (!addLoopBoundsToHeaderDomain(BBLoop, InvalidDomainMap))
+ return false;
+ }
+
+ return true;
+}
+
+void ScopBuilder::propagateDomainConstraintsToRegionExit(
+ BasicBlock *BB, Loop *BBLoop,
+ SmallPtrSetImpl<BasicBlock *> &FinishedExitBlocks,
+ DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
+ // Check if the block @p BB is the entry of a region. If so we propagate it's
+ // domain to the exit block of the region. Otherwise we are done.
+ auto *RI = scop->getRegion().getRegionInfo();
+ auto *BBReg = RI ? RI->getRegionFor(BB) : nullptr;
+ auto *ExitBB = BBReg ? BBReg->getExit() : nullptr;
+ if (!BBReg || BBReg->getEntry() != BB || !scop->contains(ExitBB))
+ return;
+
+ // Do not propagate the domain if there is a loop backedge inside the region
+ // that would prevent the exit block from being executed.
+ auto *L = BBLoop;
+ while (L && scop->contains(L)) {
+ SmallVector<BasicBlock *, 4> LatchBBs;
+ BBLoop->getLoopLatches(LatchBBs);
+ for (auto *LatchBB : LatchBBs)
+ if (BB != LatchBB && BBReg->contains(LatchBB))
+ return;
+ L = L->getParentLoop();
+ }
+
+ isl::set Domain = scop->getOrInitEmptyDomain(BB);
+ assert(!Domain.is_null() && "Cannot propagate a nullptr");
+
+ Loop *ExitBBLoop = getFirstNonBoxedLoopFor(ExitBB, LI, scop->getBoxedLoops());
+
+ // Since the dimensions of @p BB and @p ExitBB might be different we have to
+ // adjust the domain before we can propagate it.
+ isl::set AdjustedDomain = adjustDomainDimensions(Domain, BBLoop, ExitBBLoop);
+ isl::set &ExitDomain = scop->getOrInitEmptyDomain(ExitBB);
+
+ // If the exit domain is not yet created we set it otherwise we "add" the
+ // current domain.
+ ExitDomain =
+ !ExitDomain.is_null() ? AdjustedDomain.unite(ExitDomain) : AdjustedDomain;
+
+ // Initialize the invalid domain.
+ InvalidDomainMap[ExitBB] = ExitDomain.empty(ExitDomain.get_space());
+
+ FinishedExitBlocks.insert(ExitBB);
+}
+
+isl::set ScopBuilder::getPredecessorDomainConstraints(BasicBlock *BB,
+ isl::set Domain) {
+ // If @p BB is the ScopEntry we are done
+ if (scop->getRegion().getEntry() == BB)
+ return isl::set::universe(Domain.get_space());
+
+ // The region info of this function.
+ auto &RI = *scop->getRegion().getRegionInfo();
+
+ Loop *BBLoop = getFirstNonBoxedLoopFor(BB, LI, scop->getBoxedLoops());
+
+ // A domain to collect all predecessor domains, thus all conditions under
+ // which the block is executed. To this end we start with the empty domain.
+ isl::set PredDom = isl::set::empty(Domain.get_space());
+
+ // Set of regions of which the entry block domain has been propagated to BB.
+ // all predecessors inside any of the regions can be skipped.
+ SmallSet<Region *, 8> PropagatedRegions;
+
+ for (auto *PredBB : predecessors(BB)) {
+ // Skip backedges.
+ if (DT.dominates(BB, PredBB))
+ continue;
+
+ // If the predecessor is in a region we used for propagation we can skip it.
+ auto PredBBInRegion = [PredBB](Region *PR) { return PR->contains(PredBB); };
+ if (std::any_of(PropagatedRegions.begin(), PropagatedRegions.end(),
+ PredBBInRegion)) {
+ continue;
+ }
+
+ // Check if there is a valid region we can use for propagation, thus look
+ // for a region that contains the predecessor and has @p BB as exit block.
+ // FIXME: This was an side-effect-free (and possibly infinite) loop when
+ // committed and seems not to be needed.
+ auto *PredR = RI.getRegionFor(PredBB);
+ while (PredR->getExit() != BB && !PredR->contains(BB))
+ PredR = PredR->getParent();
+
+ // If a valid region for propagation was found use the entry of that region
+ // for propagation, otherwise the PredBB directly.
+ if (PredR->getExit() == BB) {
+ PredBB = PredR->getEntry();
+ PropagatedRegions.insert(PredR);
+ }
+
+ isl::set PredBBDom = scop->getDomainConditions(PredBB);
+ Loop *PredBBLoop =
+ getFirstNonBoxedLoopFor(PredBB, LI, scop->getBoxedLoops());
+ PredBBDom = adjustDomainDimensions(PredBBDom, PredBBLoop, BBLoop);
+ PredDom = PredDom.unite(PredBBDom);
+ }
+
+ return PredDom;
+}
+
+bool ScopBuilder::addLoopBoundsToHeaderDomain(
+ Loop *L, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
+ int LoopDepth = scop->getRelativeLoopDepth(L);
+ assert(LoopDepth >= 0 && "Loop in region should have at least depth one");
+
+ BasicBlock *HeaderBB = L->getHeader();
+ assert(scop->isDomainDefined(HeaderBB));
+ isl::set &HeaderBBDom = scop->getOrInitEmptyDomain(HeaderBB);
+
+ isl::map NextIterationMap =
+ createNextIterationMap(HeaderBBDom.get_space(), LoopDepth);
+
+ isl::set UnionBackedgeCondition = HeaderBBDom.empty(HeaderBBDom.get_space());
+
+ SmallVector<BasicBlock *, 4> LatchBlocks;
+ L->getLoopLatches(LatchBlocks);
+
+ for (BasicBlock *LatchBB : LatchBlocks) {
+ // If the latch is only reachable via error statements we skip it.
+ if (!scop->isDomainDefined(LatchBB))
+ continue;
+
+ isl::set LatchBBDom = scop->getDomainConditions(LatchBB);
+
+ isl::set BackedgeCondition;
+
+ Instruction *TI = LatchBB->getTerminator();
+ BranchInst *BI = dyn_cast<BranchInst>(TI);
+ assert(BI && "Only branch instructions allowed in loop latches");
+
+ if (BI->isUnconditional())
+ BackedgeCondition = LatchBBDom;
+ else {
+ SmallVector<isl_set *, 8> ConditionSets;
+ int idx = BI->getSuccessor(0) != HeaderBB;
+ if (!buildConditionSets(LatchBB, TI, L, LatchBBDom.get(),
+ InvalidDomainMap, ConditionSets))
+ return false;
+
+ // Free the non back edge condition set as we do not need it.
+ isl_set_free(ConditionSets[1 - idx]);
+
+ BackedgeCondition = isl::manage(ConditionSets[idx]);
+ }
+
+ int LatchLoopDepth = scop->getRelativeLoopDepth(LI.getLoopFor(LatchBB));
+ assert(LatchLoopDepth >= LoopDepth);
+ BackedgeCondition = BackedgeCondition.project_out(
+ isl::dim::set, LoopDepth + 1, LatchLoopDepth - LoopDepth);
+ UnionBackedgeCondition = UnionBackedgeCondition.unite(BackedgeCondition);
+ }
+
+ isl::map ForwardMap = ForwardMap.lex_le(HeaderBBDom.get_space());
+ for (int i = 0; i < LoopDepth; i++)
+ ForwardMap = ForwardMap.equate(isl::dim::in, i, isl::dim::out, i);
+
+ isl::set UnionBackedgeConditionComplement =
+ UnionBackedgeCondition.complement();
+ UnionBackedgeConditionComplement =
+ UnionBackedgeConditionComplement.lower_bound_si(isl::dim::set, LoopDepth,
+ 0);
+ UnionBackedgeConditionComplement =
+ UnionBackedgeConditionComplement.apply(ForwardMap);
+ HeaderBBDom = HeaderBBDom.subtract(UnionBackedgeConditionComplement);
+ HeaderBBDom = HeaderBBDom.apply(NextIterationMap);
+
+ auto Parts = partitionSetParts(HeaderBBDom, LoopDepth);
+ HeaderBBDom = Parts.second;
+
+ // Check if there is a <nsw> tagged AddRec for this loop and if so do not
+ // require a runtime check. The assumption is already implied by the <nsw>
+ // tag.
+ bool RequiresRTC = !scop->hasNSWAddRecForLoop(L);
+
+ isl::set UnboundedCtx = Parts.first.params();
+ recordAssumption(&RecordedAssumptions, INFINITELOOP, UnboundedCtx,
+ HeaderBB->getTerminator()->getDebugLoc(), AS_RESTRICTION,
+ nullptr, RequiresRTC);
+ return true;
+}
+
+void ScopBuilder::buildInvariantEquivalenceClasses() {
+ DenseMap<std::pair<const SCEV *, Type *>, LoadInst *> EquivClasses;
+
+ const InvariantLoadsSetTy &RIL = scop->getRequiredInvariantLoads();
+ for (LoadInst *LInst : RIL) {
+ const SCEV *PointerSCEV = SE.getSCEV(LInst->getPointerOperand());
+
+ Type *Ty = LInst->getType();
+ LoadInst *&ClassRep = EquivClasses[std::make_pair(PointerSCEV, Ty)];
+ if (ClassRep) {
+ scop->addInvariantLoadMapping(LInst, ClassRep);
+ continue;
+ }
+
+ ClassRep = LInst;
+ scop->addInvariantEquivClass(
+ InvariantEquivClassTy{PointerSCEV, MemoryAccessList(), {}, Ty});
+ }
+}
+
+bool ScopBuilder::buildDomains(
+ Region *R, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
+ bool IsOnlyNonAffineRegion = scop->isNonAffineSubRegion(R);
+ auto *EntryBB = R->getEntry();
+ auto *L = IsOnlyNonAffineRegion ? nullptr : LI.getLoopFor(EntryBB);
+ int LD = scop->getRelativeLoopDepth(L);
+ auto *S =
+ isl_set_universe(isl_space_set_alloc(scop->getIslCtx().get(), 0, LD + 1));
+
+ InvalidDomainMap[EntryBB] = isl::manage(isl_set_empty(isl_set_get_space(S)));
+ isl::set Domain = isl::manage(S);
+ scop->setDomain(EntryBB, Domain);
+
+ if (IsOnlyNonAffineRegion)
+ return !containsErrorBlock(R->getNode(), *R, &SD);
+
+ if (!buildDomainsWithBranchConstraints(R, InvalidDomainMap))
+ return false;
+
+ if (!propagateDomainConstraints(R, InvalidDomainMap))
+ return false;
+
+ // Error blocks and blocks dominated by them have been assumed to never be
+ // executed. Representing them in the Scop does not add any value. In fact,
+ // it is likely to cause issues during construction of the ScopStmts. The
+ // contents of error blocks have not been verified to be expressible and
+ // will cause problems when building up a ScopStmt for them.
+ // Furthermore, basic blocks dominated by error blocks may reference
+ // instructions in the error block which, if the error block is not modeled,
+ // can themselves not be constructed properly. To this end we will replace
+ // the domains of error blocks and those only reachable via error blocks
+ // with an empty set. Additionally, we will record for each block under which
+ // parameter combination it would be reached via an error block in its
+ // InvalidDomain. This information is needed during load hoisting.
+ if (!propagateInvalidStmtDomains(R, InvalidDomainMap))
+ return false;
+
+ return true;
+}
+
+bool ScopBuilder::buildDomainsWithBranchConstraints(
+ Region *R, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
+ // To create the domain for each block in R we iterate over all blocks and
+ // subregions in R and propagate the conditions under which the current region
+ // element is executed. To this end we iterate in reverse post order over R as
+ // it ensures that we first visit all predecessors of a region node (either a
+ // basic block or a subregion) before we visit the region node itself.
+ // Initially, only the domain for the SCoP region entry block is set and from
+ // there we propagate the current domain to all successors, however we add the
+ // condition that the successor is actually executed next.
+ // As we are only interested in non-loop carried constraints here we can
+ // simply skip loop back edges.
+
+ SmallPtrSet<BasicBlock *, 8> FinishedExitBlocks;
+ ReversePostOrderTraversal<Region *> RTraversal(R);
+ for (auto *RN : RTraversal) {
+ // Recurse for affine subregions but go on for basic blocks and non-affine
+ // subregions.
+ if (RN->isSubRegion()) {
+ Region *SubRegion = RN->getNodeAs<Region>();
+ if (!scop->isNonAffineSubRegion(SubRegion)) {
+ if (!buildDomainsWithBranchConstraints(SubRegion, InvalidDomainMap))
+ return false;
+ continue;
+ }
+ }
+
+ if (containsErrorBlock(RN, scop->getRegion(), &SD))
+ scop->notifyErrorBlock();
+ ;
+
+ BasicBlock *BB = getRegionNodeBasicBlock(RN);
+ Instruction *TI = BB->getTerminator();
+
+ if (isa<UnreachableInst>(TI))
+ continue;
+
+ if (!scop->isDomainDefined(BB))
+ continue;
+ isl::set Domain = scop->getDomainConditions(BB);
+
+ scop->updateMaxLoopDepth(unsignedFromIslSize(Domain.tuple_dim()));
+
+ auto *BBLoop = getRegionNodeLoop(RN, LI);
+ // Propagate the domain from BB directly to blocks that have a superset
+ // domain, at the moment only region exit nodes of regions that start in BB.
+ propagateDomainConstraintsToRegionExit(BB, BBLoop, FinishedExitBlocks,
+ InvalidDomainMap);
+
+ // If all successors of BB have been set a domain through the propagation
+ // above we do not need to build condition sets but can just skip this
+ // block. However, it is important to note that this is a local property
+ // with regards to the region @p R. To this end FinishedExitBlocks is a
+ // local variable.
+ auto IsFinishedRegionExit = [&FinishedExitBlocks](BasicBlock *SuccBB) {
+ return FinishedExitBlocks.count(SuccBB);
+ };
+ if (std::all_of(succ_begin(BB), succ_end(BB), IsFinishedRegionExit))
+ continue;
+
+ // Build the condition sets for the successor nodes of the current region
+ // node. If it is a non-affine subregion we will always execute the single
+ // exit node, hence the single entry node domain is the condition set. For
+ // basic blocks we use the helper function buildConditionSets.
+ SmallVector<isl_set *, 8> ConditionSets;
+ if (RN->isSubRegion())
+ ConditionSets.push_back(Domain.copy());
+ else if (!buildConditionSets(BB, TI, BBLoop, Domain.get(), InvalidDomainMap,
+ ConditionSets))
+ return false;
+
+ // Now iterate over the successors and set their initial domain based on
+ // their condition set. We skip back edges here and have to be careful when
+ // we leave a loop not to keep constraints over a dimension that doesn't
+ // exist anymore.
+ assert(RN->isSubRegion() || TI->getNumSuccessors() == ConditionSets.size());
+ for (unsigned u = 0, e = ConditionSets.size(); u < e; u++) {
+ isl::set CondSet = isl::manage(ConditionSets[u]);
+ BasicBlock *SuccBB = getRegionNodeSuccessor(RN, TI, u);
+
+ // Skip blocks outside the region.
+ if (!scop->contains(SuccBB))
+ continue;
+
+ // If we propagate the domain of some block to "SuccBB" we do not have to
+ // adjust the domain.
+ if (FinishedExitBlocks.count(SuccBB))
+ continue;
+
+ // Skip back edges.
+ if (DT.dominates(SuccBB, BB))
+ continue;
+
+ Loop *SuccBBLoop =
+ getFirstNonBoxedLoopFor(SuccBB, LI, scop->getBoxedLoops());
+
+ CondSet = adjustDomainDimensions(CondSet, BBLoop, SuccBBLoop);
+
+ // Set the domain for the successor or merge it with an existing domain in
+ // case there are multiple paths (without loop back edges) to the
+ // successor block.
+ isl::set &SuccDomain = scop->getOrInitEmptyDomain(SuccBB);
+
+ if (!SuccDomain.is_null()) {
+ SuccDomain = SuccDomain.unite(CondSet).coalesce();
+ } else {
+ // Initialize the invalid domain.
+ InvalidDomainMap[SuccBB] = CondSet.empty(CondSet.get_space());
+ SuccDomain = CondSet;
+ }
+
+ SuccDomain = SuccDomain.detect_equalities();
+
+ // Check if the maximal number of domain disjunctions was reached.
+ // In case this happens we will clean up and bail.
+ if (unsignedFromIslSize(SuccDomain.n_basic_set()) < MaxDisjunctsInDomain)
+ continue;
+
+ scop->invalidate(COMPLEXITY, DebugLoc());
+ while (++u < ConditionSets.size())
+ isl_set_free(ConditionSets[u]);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ScopBuilder::propagateInvalidStmtDomains(
+ Region *R, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
+ ReversePostOrderTraversal<Region *> RTraversal(R);
+ for (auto *RN : RTraversal) {
+
+ // Recurse for affine subregions but go on for basic blocks and non-affine
+ // subregions.
+ if (RN->isSubRegion()) {
+ Region *SubRegion = RN->getNodeAs<Region>();
+ if (!scop->isNonAffineSubRegion(SubRegion)) {
+ propagateInvalidStmtDomains(SubRegion, InvalidDomainMap);
+ continue;
+ }
+ }
+
+ bool ContainsErrorBlock = containsErrorBlock(RN, scop->getRegion(), &SD);
+ BasicBlock *BB = getRegionNodeBasicBlock(RN);
+ isl::set &Domain = scop->getOrInitEmptyDomain(BB);
+ assert(!Domain.is_null() && "Cannot propagate a nullptr");
+
+ isl::set InvalidDomain = InvalidDomainMap[BB];
+
+ bool IsInvalidBlock = ContainsErrorBlock || Domain.is_subset(InvalidDomain);
+
+ if (!IsInvalidBlock) {
+ InvalidDomain = InvalidDomain.intersect(Domain);
+ } else {
+ InvalidDomain = Domain;
+ isl::set DomPar = Domain.params();
+ recordAssumption(&RecordedAssumptions, ERRORBLOCK, DomPar,
+ BB->getTerminator()->getDebugLoc(), AS_RESTRICTION);
+ Domain = isl::set::empty(Domain.get_space());
+ }
+
+ if (InvalidDomain.is_empty()) {
+ InvalidDomainMap[BB] = InvalidDomain;
+ continue;
+ }
+
+ auto *BBLoop = getRegionNodeLoop(RN, LI);
+ auto *TI = BB->getTerminator();
+ unsigned NumSuccs = RN->isSubRegion() ? 1 : TI->getNumSuccessors();
+ for (unsigned u = 0; u < NumSuccs; u++) {
+ auto *SuccBB = getRegionNodeSuccessor(RN, TI, u);
+
+ // Skip successors outside the SCoP.
+ if (!scop->contains(SuccBB))
+ continue;
+
+ // Skip backedges.
+ if (DT.dominates(SuccBB, BB))
+ continue;
+
+ Loop *SuccBBLoop =
+ getFirstNonBoxedLoopFor(SuccBB, LI, scop->getBoxedLoops());
+
+ auto AdjustedInvalidDomain =
+ adjustDomainDimensions(InvalidDomain, BBLoop, SuccBBLoop);
+
+ isl::set SuccInvalidDomain = InvalidDomainMap[SuccBB];
+ SuccInvalidDomain = SuccInvalidDomain.unite(AdjustedInvalidDomain);
+ SuccInvalidDomain = SuccInvalidDomain.coalesce();
+
+ InvalidDomainMap[SuccBB] = SuccInvalidDomain;
+
+ // Check if the maximal number of domain disjunctions was reached.
+ // In case this happens we will bail.
+ if (unsignedFromIslSize(SuccInvalidDomain.n_basic_set()) <
+ MaxDisjunctsInDomain)
+ continue;
+
+ InvalidDomainMap.erase(BB);
+ scop->invalidate(COMPLEXITY, TI->getDebugLoc(), TI->getParent());
+ return false;
+ }
+
+ InvalidDomainMap[BB] = InvalidDomain;
+ }
+
+ return true;
+}
+
+void ScopBuilder::buildPHIAccesses(ScopStmt *PHIStmt, PHINode *PHI,
+ Region *NonAffineSubRegion,
+ bool IsExitBlock) {
+ // PHI nodes that are in the exit block of the region, hence if IsExitBlock is
+ // true, are not modeled as ordinary PHI nodes as they are not part of the
+ // region. However, we model the operands in the predecessor blocks that are
+ // part of the region as regular scalar accesses.
+
+ // If we can synthesize a PHI we can skip it, however only if it is in
+ // the region. If it is not it can only be in the exit block of the region.
+ // In this case we model the operands but not the PHI itself.
+ auto *Scope = LI.getLoopFor(PHI->getParent());
+ if (!IsExitBlock && canSynthesize(PHI, *scop, &SE, Scope))
+ return;
+
+ // PHI nodes are modeled as if they had been demoted prior to the SCoP
+ // detection. Hence, the PHI is a load of a new memory location in which the
+ // incoming value was written at the end of the incoming basic block.
+ bool OnlyNonAffineSubRegionOperands = true;
+ for (unsigned u = 0; u < PHI->getNumIncomingValues(); u++) {
+ Value *Op = PHI->getIncomingValue(u);
+ BasicBlock *OpBB = PHI->getIncomingBlock(u);
+ ScopStmt *OpStmt = scop->getIncomingStmtFor(PHI->getOperandUse(u));
+
+ // Do not build PHI dependences inside a non-affine subregion, but make
+ // sure that the necessary scalar values are still made available.
+ if (NonAffineSubRegion && NonAffineSubRegion->contains(OpBB)) {
+ auto *OpInst = dyn_cast<Instruction>(Op);
+ if (!OpInst || !NonAffineSubRegion->contains(OpInst))
+ ensureValueRead(Op, OpStmt);
+ continue;
+ }
+
+ OnlyNonAffineSubRegionOperands = false;
+ ensurePHIWrite(PHI, OpStmt, OpBB, Op, IsExitBlock);
+ }
+
+ if (!OnlyNonAffineSubRegionOperands && !IsExitBlock) {
+ addPHIReadAccess(PHIStmt, PHI);
+ }
+}
+
+void ScopBuilder::buildScalarDependences(ScopStmt *UserStmt,
+ Instruction *Inst) {
+ assert(!isa<PHINode>(Inst));
+
+ // Pull-in required operands.
+ for (Use &Op : Inst->operands())
+ ensureValueRead(Op.get(), UserStmt);
+}
+
+// Create a sequence of two schedules. Either argument may be null and is
+// interpreted as the empty schedule. Can also return null if both schedules are
+// empty.
+static isl::schedule combineInSequence(isl::schedule Prev, isl::schedule Succ) {
+ if (Prev.is_null())
+ return Succ;
+ if (Succ.is_null())
+ return Prev;
+
+ return Prev.sequence(Succ);
+}
+
+// Create an isl_multi_union_aff that defines an identity mapping from the
+// elements of USet to their N-th dimension.
+//
+// # Example:
+//
+// Domain: { A[i,j]; B[i,j,k] }
+// N: 1
+//
+// Resulting Mapping: { {A[i,j] -> [(j)]; B[i,j,k] -> [(j)] }
+//
+// @param USet A union set describing the elements for which to generate a
+// mapping.
+// @param N The dimension to map to.
+// @returns A mapping from USet to its N-th dimension.
+static isl::multi_union_pw_aff mapToDimension(isl::union_set USet, unsigned N) {
+ assert(!USet.is_null());
+ assert(!USet.is_empty());
+
+ auto Result = isl::union_pw_multi_aff::empty(USet.get_space());
+
+ for (isl::set S : USet.get_set_list()) {
+ unsigned Dim = unsignedFromIslSize(S.tuple_dim());
+ assert(Dim >= N);
+ auto PMA = isl::pw_multi_aff::project_out_map(S.get_space(), isl::dim::set,
+ N, Dim - N);
+ if (N > 1)
+ PMA = PMA.drop_dims(isl::dim::out, 0, N - 1);
+
+ Result = Result.add_pw_multi_aff(PMA);
+ }
+
+ return isl::multi_union_pw_aff(isl::union_pw_multi_aff(Result));
+}
+
+void ScopBuilder::buildSchedule() {
+ Loop *L = getLoopSurroundingScop(*scop, LI);
+ LoopStackTy LoopStack({LoopStackElementTy(L, {}, 0)});
+ buildSchedule(scop->getRegion().getNode(), LoopStack);
+ assert(LoopStack.size() == 1 && LoopStack.back().L == L);
+ scop->setScheduleTree(LoopStack[0].Schedule);
+}
+
+/// To generate a schedule for the elements in a Region we traverse the Region
+/// in reverse-post-order and add the contained RegionNodes in traversal order
+/// to the schedule of the loop that is currently at the top of the LoopStack.
+/// For loop-free codes, this results in a correct sequential ordering.
+///
+/// Example:
+/// bb1(0)
+/// / \.
+/// bb2(1) bb3(2)
+/// \ / \.
+/// bb4(3) bb5(4)
+/// \ /
+/// bb6(5)
+///
+/// Including loops requires additional processing. Whenever a loop header is
+/// encountered, the corresponding loop is added to the @p LoopStack. Starting
+/// from an empty schedule, we first process all RegionNodes that are within
+/// this loop and complete the sequential schedule at this loop-level before
+/// processing about any other nodes. To implement this
+/// loop-nodes-first-processing, the reverse post-order traversal is
+/// insufficient. Hence, we additionally check if the traversal yields
+/// sub-regions or blocks that are outside the last loop on the @p LoopStack.
+/// These region-nodes are then queue and only traverse after the all nodes
+/// within the current loop have been processed.
+void ScopBuilder::buildSchedule(Region *R, LoopStackTy &LoopStack) {
+ Loop *OuterScopLoop = getLoopSurroundingScop(*scop, LI);
+
+ ReversePostOrderTraversal<Region *> RTraversal(R);
+ std::deque<RegionNode *> WorkList(RTraversal.begin(), RTraversal.end());
+ std::deque<RegionNode *> DelayList;
+ bool LastRNWaiting = false;
+
+ // Iterate over the region @p R in reverse post-order but queue
+ // sub-regions/blocks iff they are not part of the last encountered but not
+ // completely traversed loop. The variable LastRNWaiting is a flag to indicate
+ // that we queued the last sub-region/block from the reverse post-order
+ // iterator. If it is set we have to explore the next sub-region/block from
+ // the iterator (if any) to guarantee progress. If it is not set we first try
+ // the next queued sub-region/blocks.
+ while (!WorkList.empty() || !DelayList.empty()) {
+ RegionNode *RN;
+
+ if ((LastRNWaiting && !WorkList.empty()) || DelayList.empty()) {
+ RN = WorkList.front();
+ WorkList.pop_front();
+ LastRNWaiting = false;
+ } else {
+ RN = DelayList.front();
+ DelayList.pop_front();
+ }
+
+ Loop *L = getRegionNodeLoop(RN, LI);
+ if (!scop->contains(L))
+ L = OuterScopLoop;
+
+ Loop *LastLoop = LoopStack.back().L;
+ if (LastLoop != L) {
+ if (LastLoop && !LastLoop->contains(L)) {
+ LastRNWaiting = true;
+ DelayList.push_back(RN);
+ continue;
+ }
+ LoopStack.push_back({L, {}, 0});
+ }
+ buildSchedule(RN, LoopStack);
+ }
+}
+
+void ScopBuilder::buildSchedule(RegionNode *RN, LoopStackTy &LoopStack) {
+ if (RN->isSubRegion()) {
+ auto *LocalRegion = RN->getNodeAs<Region>();
+ if (!scop->isNonAffineSubRegion(LocalRegion)) {
+ buildSchedule(LocalRegion, LoopStack);
+ return;
+ }
+ }
+
+ assert(LoopStack.rbegin() != LoopStack.rend());
+ auto LoopData = LoopStack.rbegin();
+ LoopData->NumBlocksProcessed += getNumBlocksInRegionNode(RN);
+
+ for (auto *Stmt : scop->getStmtListFor(RN)) {
+ isl::union_set UDomain{Stmt->getDomain()};
+ auto StmtSchedule = isl::schedule::from_domain(UDomain);
+ LoopData->Schedule = combineInSequence(LoopData->Schedule, StmtSchedule);
+ }
+
+ // Check if we just processed the last node in this loop. If we did, finalize
+ // the loop by:
+ //
+ // - adding new schedule dimensions
+ // - folding the resulting schedule into the parent loop schedule
+ // - dropping the loop schedule from the LoopStack.
+ //
+ // Then continue to check surrounding loops, which might also have been
+ // completed by this node.
+ size_t Dimension = LoopStack.size();
+ while (LoopData->L &&
+ LoopData->NumBlocksProcessed == getNumBlocksInLoop(LoopData->L)) {
+ isl::schedule Schedule = LoopData->Schedule;
+ auto NumBlocksProcessed = LoopData->NumBlocksProcessed;
+
+ assert(std::next(LoopData) != LoopStack.rend());
+ Loop *L = LoopData->L;
+ ++LoopData;
+ --Dimension;
+
+ if (!Schedule.is_null()) {
+ isl::union_set Domain = Schedule.get_domain();
+ isl::multi_union_pw_aff MUPA = mapToDimension(Domain, Dimension);
+ Schedule = Schedule.insert_partial_schedule(MUPA);
+
+ if (hasDisableAllTransformsHint(L)) {
+ /// If any of the loops has a disable_nonforced heuristic, mark the
+ /// entire SCoP as such. The ISL rescheduler can only reschedule the
+ /// SCoP in its entirety.
+ /// TODO: ScopDetection could avoid including such loops or warp them as
+ /// boxed loop. It still needs to pass-through loop with user-defined
+ /// metadata.
+ scop->markDisableHeuristics();
+ }
+
+ // It is easier to insert the marks here that do it retroactively.
+ isl::id IslLoopId = createIslLoopAttr(scop->getIslCtx(), L);
+ if (!IslLoopId.is_null())
+ Schedule =
+ Schedule.get_root().child(0).insert_mark(IslLoopId).get_schedule();
+
+ LoopData->Schedule = combineInSequence(LoopData->Schedule, Schedule);
+ }
+
+ LoopData->NumBlocksProcessed += NumBlocksProcessed;
+ }
+ // Now pop all loops processed up there from the LoopStack
+ LoopStack.erase(LoopStack.begin() + Dimension, LoopStack.end());
+}
+
+void ScopBuilder::buildEscapingDependences(Instruction *Inst) {
+ // Check for uses of this instruction outside the scop. Because we do not
+ // iterate over such instructions and therefore did not "ensure" the existence
+ // of a write, we must determine such use here.
+ if (scop->isEscaping(Inst))
+ ensureValueWrite(Inst);
+}
+
+void ScopBuilder::addRecordedAssumptions() {
+ for (auto &AS : llvm::reverse(RecordedAssumptions)) {
+
+ if (!AS.BB) {
+ scop->addAssumption(AS.Kind, AS.Set, AS.Loc, AS.Sign,
+ nullptr /* BasicBlock */, AS.RequiresRTC);
+ continue;
+ }
+
+ // If the domain was deleted the assumptions are void.
+ isl_set *Dom = scop->getDomainConditions(AS.BB).release();
+ if (!Dom)
+ continue;
+
+ // If a basic block was given use its domain to simplify the assumption.
+ // In case of restrictions we know they only have to hold on the domain,
+ // thus we can intersect them with the domain of the block. However, for
+ // assumptions the domain has to imply them, thus:
+ // _ _____
+ // Dom => S <==> A v B <==> A - B
+ //
+ // To avoid the complement we will register A - B as a restriction not an
+ // assumption.
+ isl_set *S = AS.Set.copy();
+ if (AS.Sign == AS_RESTRICTION)
+ S = isl_set_params(isl_set_intersect(S, Dom));
+ else /* (AS.Sign == AS_ASSUMPTION) */
+ S = isl_set_params(isl_set_subtract(Dom, S));
+
+ scop->addAssumption(AS.Kind, isl::manage(S), AS.Loc, AS_RESTRICTION, AS.BB,
+ AS.RequiresRTC);
+ }
+}
+
+void ScopBuilder::addUserAssumptions(
+ AssumptionCache &AC, DenseMap<BasicBlock *, isl::set> &InvalidDomainMap) {
+ for (auto &Assumption : AC.assumptions()) {
+ auto *CI = dyn_cast_or_null<CallInst>(Assumption);
+ if (!CI || CI->arg_size() != 1)
+ continue;
+
+ bool InScop = scop->contains(CI);
+ if (!InScop && !scop->isDominatedBy(DT, CI->getParent()))
+ continue;
+
+ auto *L = LI.getLoopFor(CI->getParent());
+ auto *Val = CI->getArgOperand(0);
+ ParameterSetTy DetectedParams;
+ auto &R = scop->getRegion();
+ if (!isAffineConstraint(Val, &R, L, SE, DetectedParams)) {
+ ORE.emit(
+ OptimizationRemarkAnalysis(DEBUG_TYPE, "IgnoreUserAssumption", CI)
+ << "Non-affine user assumption ignored.");
+ continue;
+ }
+
+ // Collect all newly introduced parameters.
+ ParameterSetTy NewParams;
+ for (auto *Param : DetectedParams) {
+ Param = extractConstantFactor(Param, SE).second;
+ Param = scop->getRepresentingInvariantLoadSCEV(Param);
+ if (scop->isParam(Param))
+ continue;
+ NewParams.insert(Param);
+ }
+
+ SmallVector<isl_set *, 2> ConditionSets;
+ auto *TI = InScop ? CI->getParent()->getTerminator() : nullptr;
+ BasicBlock *BB = InScop ? CI->getParent() : R.getEntry();
+ auto *Dom = InScop ? isl_set_copy(scop->getDomainConditions(BB).get())
+ : isl_set_copy(scop->getContext().get());
+ assert(Dom && "Cannot propagate a nullptr.");
+ bool Valid = buildConditionSets(BB, Val, TI, L, Dom, InvalidDomainMap,
+ ConditionSets);
+ isl_set_free(Dom);
+
+ if (!Valid)
+ continue;
+
+ isl_set *AssumptionCtx = nullptr;
+ if (InScop) {
+ AssumptionCtx = isl_set_complement(isl_set_params(ConditionSets[1]));
+ isl_set_free(ConditionSets[0]);
+ } else {
+ AssumptionCtx = isl_set_complement(ConditionSets[1]);
+ AssumptionCtx = isl_set_intersect(AssumptionCtx, ConditionSets[0]);
+ }
+
+ // Project out newly introduced parameters as they are not otherwise useful.
+ if (!NewParams.empty()) {
+ for (isl_size u = 0; u < isl_set_n_param(AssumptionCtx); u++) {
+ auto *Id = isl_set_get_dim_id(AssumptionCtx, isl_dim_param, u);
+ auto *Param = static_cast<const SCEV *>(isl_id_get_user(Id));
+ isl_id_free(Id);
+
+ if (!NewParams.count(Param))
+ continue;
+
+ AssumptionCtx =
+ isl_set_project_out(AssumptionCtx, isl_dim_param, u--, 1);
+ }
+ }
+ ORE.emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "UserAssumption", CI)
+ << "Use user assumption: "
+ << stringFromIslObj(AssumptionCtx, "null"));
+ isl::set newContext =
+ scop->getContext().intersect(isl::manage(AssumptionCtx));
+ scop->setContext(newContext);
+ }
+}
+
+bool ScopBuilder::buildAccessMultiDimFixed(MemAccInst Inst, ScopStmt *Stmt) {
+ Value *Val = Inst.getValueOperand();
+ Type *ElementType = Val->getType();
+ Value *Address = Inst.getPointerOperand();
+ const SCEV *AccessFunction =
+ SE.getSCEVAtScope(Address, LI.getLoopFor(Inst->getParent()));
+ const SCEVUnknown *BasePointer =
+ dyn_cast<SCEVUnknown>(SE.getPointerBase(AccessFunction));
+ enum MemoryAccess::AccessType AccType =
+ isa<LoadInst>(Inst) ? MemoryAccess::READ : MemoryAccess::MUST_WRITE;
+
+ if (auto *BitCast = dyn_cast<BitCastInst>(Address)) {
+ auto *Src = BitCast->getOperand(0);
+ auto *SrcTy = Src->getType();
+ auto *DstTy = BitCast->getType();
+ // Do not try to delinearize non-sized (opaque) pointers.
+ if ((SrcTy->isPointerTy() && !SrcTy->getPointerElementType()->isSized()) ||
+ (DstTy->isPointerTy() && !DstTy->getPointerElementType()->isSized())) {
+ return false;
+ }
+ if (SrcTy->isPointerTy() && DstTy->isPointerTy() &&
+ DL.getTypeAllocSize(SrcTy->getPointerElementType()) ==
+ DL.getTypeAllocSize(DstTy->getPointerElementType()))
+ Address = Src;
+ }
+
+ auto *GEP = dyn_cast<GetElementPtrInst>(Address);
+ if (!GEP)
+ return false;
+
+ SmallVector<const SCEV *, 4> Subscripts;
+ SmallVector<int, 4> Sizes;
+ getIndexExpressionsFromGEP(SE, GEP, Subscripts, Sizes);
+ auto *BasePtr = GEP->getOperand(0);
+
+ if (auto *BasePtrCast = dyn_cast<BitCastInst>(BasePtr))
+ BasePtr = BasePtrCast->getOperand(0);
+
+ // Check for identical base pointers to ensure that we do not miss index
+ // offsets that have been added before this GEP is applied.
+ if (BasePtr != BasePointer->getValue())
+ return false;
+
+ std::vector<const SCEV *> SizesSCEV;
+
+ const InvariantLoadsSetTy &ScopRIL = scop->getRequiredInvariantLoads();
+
+ Loop *SurroundingLoop = Stmt->getSurroundingLoop();
+ for (auto *Subscript : Subscripts) {
+ InvariantLoadsSetTy AccessILS;
+ if (!isAffineExpr(&scop->getRegion(), SurroundingLoop, Subscript, SE,
+ &AccessILS))
+ return false;
+
+ for (LoadInst *LInst : AccessILS)
+ if (!ScopRIL.count(LInst))
+ return false;
+ }
+
+ if (Sizes.empty())
+ return false;
+
+ SizesSCEV.push_back(nullptr);
+
+ for (auto V : Sizes)
+ SizesSCEV.push_back(SE.getSCEV(
+ ConstantInt::get(IntegerType::getInt64Ty(BasePtr->getContext()), V)));
+
+ addArrayAccess(Stmt, Inst, AccType, BasePointer->getValue(), ElementType,
+ true, Subscripts, SizesSCEV, Val);
+ return true;
+}
+
+bool ScopBuilder::buildAccessMultiDimParam(MemAccInst Inst, ScopStmt *Stmt) {
+ if (!PollyDelinearize)
+ return false;
+
+ Value *Address = Inst.getPointerOperand();
+ Value *Val = Inst.getValueOperand();
+ Type *ElementType = Val->getType();
+ unsigned ElementSize = DL.getTypeAllocSize(ElementType);
+ enum MemoryAccess::AccessType AccType =
+ isa<LoadInst>(Inst) ? MemoryAccess::READ : MemoryAccess::MUST_WRITE;
+
+ const SCEV *AccessFunction =
+ SE.getSCEVAtScope(Address, LI.getLoopFor(Inst->getParent()));
+ const SCEVUnknown *BasePointer =
+ dyn_cast<SCEVUnknown>(SE.getPointerBase(AccessFunction));
+
+ assert(BasePointer && "Could not find base pointer");
+
+ auto &InsnToMemAcc = scop->getInsnToMemAccMap();
+ auto AccItr = InsnToMemAcc.find(Inst);
+ if (AccItr == InsnToMemAcc.end())
+ return false;
+
+ std::vector<const SCEV *> Sizes = {nullptr};
+
+ Sizes.insert(Sizes.end(), AccItr->second.Shape->DelinearizedSizes.begin(),
+ AccItr->second.Shape->DelinearizedSizes.end());
+
+ // In case only the element size is contained in the 'Sizes' array, the
+ // access does not access a real multi-dimensional array. Hence, we allow
+ // the normal single-dimensional access construction to handle this.
+ if (Sizes.size() == 1)
+ return false;
+
+ // Remove the element size. This information is already provided by the
+ // ElementSize parameter. In case the element size of this access and the
+ // element size used for delinearization differs the delinearization is
+ // incorrect. Hence, we invalidate the scop.
+ //
+ // TODO: Handle delinearization with differing element sizes.
+ auto DelinearizedSize =
+ cast<SCEVConstant>(Sizes.back())->getAPInt().getSExtValue();
+ Sizes.pop_back();
+ if (ElementSize != DelinearizedSize)
+ scop->invalidate(DELINEARIZATION, Inst->getDebugLoc(), Inst->getParent());
+
+ addArrayAccess(Stmt, Inst, AccType, BasePointer->getValue(), ElementType,
+ true, AccItr->second.DelinearizedSubscripts, Sizes, Val);
+ return true;
+}
+
+bool ScopBuilder::buildAccessMemIntrinsic(MemAccInst Inst, ScopStmt *Stmt) {
+ auto *MemIntr = dyn_cast_or_null<MemIntrinsic>(Inst);
+
+ if (MemIntr == nullptr)
+ return false;
+
+ auto *L = LI.getLoopFor(Inst->getParent());
+ auto *LengthVal = SE.getSCEVAtScope(MemIntr->getLength(), L);
+ assert(LengthVal);
+
+ // Check if the length val is actually affine or if we overapproximate it
+ InvariantLoadsSetTy AccessILS;
+ const InvariantLoadsSetTy &ScopRIL = scop->getRequiredInvariantLoads();
+
+ Loop *SurroundingLoop = Stmt->getSurroundingLoop();
+ bool LengthIsAffine = isAffineExpr(&scop->getRegion(), SurroundingLoop,
+ LengthVal, SE, &AccessILS);
+ for (LoadInst *LInst : AccessILS)
+ if (!ScopRIL.count(LInst))
+ LengthIsAffine = false;
+ if (!LengthIsAffine)
+ LengthVal = nullptr;
+
+ auto *DestPtrVal = MemIntr->getDest();
+ assert(DestPtrVal);
+
+ auto *DestAccFunc = SE.getSCEVAtScope(DestPtrVal, L);
+ assert(DestAccFunc);
+ // Ignore accesses to "NULL".
+ // TODO: We could use this to optimize the region further, e.g., intersect
+ // the context with
+ // isl_set_complement(isl_set_params(getDomain()))
+ // as we know it would be undefined to execute this instruction anyway.
+ if (DestAccFunc->isZero())
+ return true;
+
+ if (auto *U = dyn_cast<SCEVUnknown>(DestAccFunc)) {
+ if (isa<ConstantPointerNull>(U->getValue()))
+ return true;
+ }
+
+ auto *DestPtrSCEV = dyn_cast<SCEVUnknown>(SE.getPointerBase(DestAccFunc));
+ assert(DestPtrSCEV);
+ DestAccFunc = SE.getMinusSCEV(DestAccFunc, DestPtrSCEV);
+ addArrayAccess(Stmt, Inst, MemoryAccess::MUST_WRITE, DestPtrSCEV->getValue(),
+ IntegerType::getInt8Ty(DestPtrVal->getContext()),
+ LengthIsAffine, {DestAccFunc, LengthVal}, {nullptr},
+ Inst.getValueOperand());
+
+ auto *MemTrans = dyn_cast<MemTransferInst>(MemIntr);
+ if (!MemTrans)
+ return true;
+
+ auto *SrcPtrVal = MemTrans->getSource();
+ assert(SrcPtrVal);
+
+ auto *SrcAccFunc = SE.getSCEVAtScope(SrcPtrVal, L);
+ assert(SrcAccFunc);
+ // Ignore accesses to "NULL".
+ // TODO: See above TODO
+ if (SrcAccFunc->isZero())
+ return true;
+
+ auto *SrcPtrSCEV = dyn_cast<SCEVUnknown>(SE.getPointerBase(SrcAccFunc));
+ assert(SrcPtrSCEV);
+ SrcAccFunc = SE.getMinusSCEV(SrcAccFunc, SrcPtrSCEV);
+ addArrayAccess(Stmt, Inst, MemoryAccess::READ, SrcPtrSCEV->getValue(),
+ IntegerType::getInt8Ty(SrcPtrVal->getContext()),
+ LengthIsAffine, {SrcAccFunc, LengthVal}, {nullptr},
+ Inst.getValueOperand());
+
+ return true;
+}
+
+bool ScopBuilder::buildAccessCallInst(MemAccInst Inst, ScopStmt *Stmt) {
+ auto *CI = dyn_cast_or_null<CallInst>(Inst);
+
+ if (CI == nullptr)
+ return false;
+
+ if (CI->doesNotAccessMemory() || isIgnoredIntrinsic(CI) || isDebugCall(CI))
+ return true;
+
+ bool ReadOnly = false;
+ auto *AF = SE.getConstant(IntegerType::getInt64Ty(CI->getContext()), 0);
+ auto *CalledFunction = CI->getCalledFunction();
+ switch (AA.getModRefBehavior(CalledFunction)) {
+ case FMRB_UnknownModRefBehavior:
+ llvm_unreachable("Unknown mod ref behaviour cannot be represented.");
+ case FMRB_DoesNotAccessMemory:
+ return true;
+ case FMRB_OnlyWritesMemory:
+ case FMRB_OnlyWritesInaccessibleMem:
+ case FMRB_OnlyWritesInaccessibleOrArgMem:
+ case FMRB_OnlyAccessesInaccessibleMem:
+ case FMRB_OnlyAccessesInaccessibleOrArgMem:
+ return false;
+ case FMRB_OnlyReadsMemory:
+ case FMRB_OnlyReadsInaccessibleMem:
+ case FMRB_OnlyReadsInaccessibleOrArgMem:
+ GlobalReads.emplace_back(Stmt, CI);
+ return true;
+ case FMRB_OnlyReadsArgumentPointees:
+ ReadOnly = true;
+ LLVM_FALLTHROUGH;
+ case FMRB_OnlyWritesArgumentPointees:
+ case FMRB_OnlyAccessesArgumentPointees: {
+ auto AccType = ReadOnly ? MemoryAccess::READ : MemoryAccess::MAY_WRITE;
+ Loop *L = LI.getLoopFor(Inst->getParent());
+ for (const auto &Arg : CI->args()) {
+ if (!Arg->getType()->isPointerTy())
+ continue;
+
+ auto *ArgSCEV = SE.getSCEVAtScope(Arg, L);
+ if (ArgSCEV->isZero())
+ continue;
+
+ if (auto *U = dyn_cast<SCEVUnknown>(ArgSCEV)) {
+ if (isa<ConstantPointerNull>(U->getValue()))
+ return true;
+ }
+
+ auto *ArgBasePtr = cast<SCEVUnknown>(SE.getPointerBase(ArgSCEV));
+ addArrayAccess(Stmt, Inst, AccType, ArgBasePtr->getValue(),
+ ArgBasePtr->getType(), false, {AF}, {nullptr}, CI);
+ }
+ return true;
+ }
+ }
+
+ return true;
+}
+
+void ScopBuilder::buildAccessSingleDim(MemAccInst Inst, ScopStmt *Stmt) {
+ Value *Address = Inst.getPointerOperand();
+ Value *Val = Inst.getValueOperand();
+ Type *ElementType = Val->getType();
+ enum MemoryAccess::AccessType AccType =
+ isa<LoadInst>(Inst) ? MemoryAccess::READ : MemoryAccess::MUST_WRITE;
+
+ const SCEV *AccessFunction =
+ SE.getSCEVAtScope(Address, LI.getLoopFor(Inst->getParent()));
+ const SCEVUnknown *BasePointer =
+ dyn_cast<SCEVUnknown>(SE.getPointerBase(AccessFunction));
+
+ assert(BasePointer && "Could not find base pointer");
+ AccessFunction = SE.getMinusSCEV(AccessFunction, BasePointer);
+
+ // Check if the access depends on a loop contained in a non-affine subregion.
+ bool isVariantInNonAffineLoop = false;
+ SetVector<const Loop *> Loops;
+ findLoops(AccessFunction, Loops);
+ for (const Loop *L : Loops)
+ if (Stmt->contains(L)) {
+ isVariantInNonAffineLoop = true;
+ break;
+ }
+
+ InvariantLoadsSetTy AccessILS;
+
+ Loop *SurroundingLoop = Stmt->getSurroundingLoop();
+ bool IsAffine = !isVariantInNonAffineLoop &&
+ isAffineExpr(&scop->getRegion(), SurroundingLoop,
+ AccessFunction, SE, &AccessILS);
+
+ const InvariantLoadsSetTy &ScopRIL = scop->getRequiredInvariantLoads();
+ for (LoadInst *LInst : AccessILS)
+ if (!ScopRIL.count(LInst))
+ IsAffine = false;
+
+ if (!IsAffine && AccType == MemoryAccess::MUST_WRITE)
+ AccType = MemoryAccess::MAY_WRITE;
+
+ addArrayAccess(Stmt, Inst, AccType, BasePointer->getValue(), ElementType,
+ IsAffine, {AccessFunction}, {nullptr}, Val);
+}
+
+void ScopBuilder::buildMemoryAccess(MemAccInst Inst, ScopStmt *Stmt) {
+ if (buildAccessMemIntrinsic(Inst, Stmt))
+ return;
+
+ if (buildAccessCallInst(Inst, Stmt))
+ return;
+
+ if (buildAccessMultiDimFixed(Inst, Stmt))
+ return;
+
+ if (buildAccessMultiDimParam(Inst, Stmt))
+ return;
+
+ buildAccessSingleDim(Inst, Stmt);
+}
+
+void ScopBuilder::buildAccessFunctions() {
+ for (auto &Stmt : *scop) {
+ if (Stmt.isBlockStmt()) {
+ buildAccessFunctions(&Stmt, *Stmt.getBasicBlock());
+ continue;
+ }
+
+ Region *R = Stmt.getRegion();
+ for (BasicBlock *BB : R->blocks())
+ buildAccessFunctions(&Stmt, *BB, R);
+ }
+
+ // Build write accesses for values that are used after the SCoP.
+ // The instructions defining them might be synthesizable and therefore not
+ // contained in any statement, hence we iterate over the original instructions
+ // to identify all escaping values.
+ for (BasicBlock *BB : scop->getRegion().blocks()) {
+ for (Instruction &Inst : *BB)
+ buildEscapingDependences(&Inst);
+ }
+}
+
+bool ScopBuilder::shouldModelInst(Instruction *Inst, Loop *L) {
+ return !Inst->isTerminator() && !isIgnoredIntrinsic(Inst) &&
+ !canSynthesize(Inst, *scop, &SE, L);
+}
+
+/// Generate a name for a statement.
+///
+/// @param BB The basic block the statement will represent.
+/// @param BBIdx The index of the @p BB relative to other BBs/regions.
+/// @param Count The index of the created statement in @p BB.
+/// @param IsMain Whether this is the main of all statement for @p BB. If true,
+/// no suffix will be added.
+/// @param IsLast Uses a special indicator for the last statement of a BB.
+static std::string makeStmtName(BasicBlock *BB, long BBIdx, int Count,
+ bool IsMain, bool IsLast = false) {
+ std::string Suffix;
+ if (!IsMain) {
+ if (UseInstructionNames)
+ Suffix = '_';
+ if (IsLast)
+ Suffix += "last";
+ else if (Count < 26)
+ Suffix += 'a' + Count;
+ else
+ Suffix += std::to_string(Count);
+ }
+ return getIslCompatibleName("Stmt", BB, BBIdx, Suffix, UseInstructionNames);
+}
+
+/// Generate a name for a statement that represents a non-affine subregion.
+///
+/// @param R The region the statement will represent.
+/// @param RIdx The index of the @p R relative to other BBs/regions.
+static std::string makeStmtName(Region *R, long RIdx) {
+ return getIslCompatibleName("Stmt", R->getNameStr(), RIdx, "",
+ UseInstructionNames);
+}
+
+void ScopBuilder::buildSequentialBlockStmts(BasicBlock *BB, bool SplitOnStore) {
+ Loop *SurroundingLoop = LI.getLoopFor(BB);
+
+ int Count = 0;
+ long BBIdx = scop->getNextStmtIdx();
+ std::vector<Instruction *> Instructions;
+ for (Instruction &Inst : *BB) {
+ if (shouldModelInst(&Inst, SurroundingLoop))
+ Instructions.push_back(&Inst);
+ if (Inst.getMetadata("polly_split_after") ||
+ (SplitOnStore && isa<StoreInst>(Inst))) {
+ std::string Name = makeStmtName(BB, BBIdx, Count, Count == 0);
+ scop->addScopStmt(BB, Name, SurroundingLoop, Instructions);
+ Count++;
+ Instructions.clear();
+ }
+ }
+
+ std::string Name = makeStmtName(BB, BBIdx, Count, Count == 0);
+ scop->addScopStmt(BB, Name, SurroundingLoop, Instructions);
+}
+
+/// Is @p Inst an ordered instruction?
+///
+/// An unordered instruction is an instruction, such that a sequence of
+/// unordered instructions can be permuted without changing semantics. Any
+/// instruction for which this is not always the case is ordered.
+static bool isOrderedInstruction(Instruction *Inst) {
+ return Inst->mayHaveSideEffects() || Inst->mayReadOrWriteMemory();
+}
+
+/// Join instructions to the same statement if one uses the scalar result of the
+/// other.
+static void joinOperandTree(EquivalenceClasses<Instruction *> &UnionFind,
+ ArrayRef<Instruction *> ModeledInsts) {
+ for (Instruction *Inst : ModeledInsts) {
+ if (isa<PHINode>(Inst))
+ continue;
+
+ for (Use &Op : Inst->operands()) {
+ Instruction *OpInst = dyn_cast<Instruction>(Op.get());
+ if (!OpInst)
+ continue;
+
+ // Check if OpInst is in the BB and is a modeled instruction.
+ auto OpVal = UnionFind.findValue(OpInst);
+ if (OpVal == UnionFind.end())
+ continue;
+
+ UnionFind.unionSets(Inst, OpInst);
+ }
+ }
+}
+
+/// Ensure that the order of ordered instructions does not change.
+///
+/// If we encounter an ordered instruction enclosed in instructions belonging to
+/// a different statement (which might as well contain ordered instructions, but
+/// this is not tested here), join them.
+static void
+joinOrderedInstructions(EquivalenceClasses<Instruction *> &UnionFind,
+ ArrayRef<Instruction *> ModeledInsts) {
+ SetVector<Instruction *> SeenLeaders;
+ for (Instruction *Inst : ModeledInsts) {
+ if (!isOrderedInstruction(Inst))
+ continue;
+
+ Instruction *Leader = UnionFind.getLeaderValue(Inst);
+ // Since previous iterations might have merged sets, some items in
+ // SeenLeaders are not leaders anymore. However, The new leader of
+ // previously merged instructions must be one of the former leaders of
+ // these merged instructions.
+ bool Inserted = SeenLeaders.insert(Leader);
+ if (Inserted)
+ continue;
+
+ // Merge statements to close holes. Say, we have already seen statements A
+ // and B, in this order. Then we see an instruction of A again and we would
+ // see the pattern "A B A". This function joins all statements until the
+ // only seen occurrence of A.
+ for (Instruction *Prev : reverse(SeenLeaders)) {
+ // We are backtracking from the last element until we see Inst's leader
+ // in SeenLeaders and merge all into one set. Although leaders of
+ // instructions change during the execution of this loop, it's irrelevant
+ // as we are just searching for the element that we already confirmed is
+ // in the list.
+ if (Prev == Leader)
+ break;
+ UnionFind.unionSets(Prev, Leader);
+ }
+ }
+}
+
+/// If the BasicBlock has an edge from itself, ensure that the PHI WRITEs for
+/// the incoming values from this block are executed after the PHI READ.
+///
+/// Otherwise it could overwrite the incoming value from before the BB with the
+/// value for the next execution. This can happen if the PHI WRITE is added to
+/// the statement with the instruction that defines the incoming value (instead
+/// of the last statement of the same BB). To ensure that the PHI READ and WRITE
+/// are in order, we put both into the statement. PHI WRITEs are always executed
+/// after PHI READs when they are in the same statement.
+///
+/// TODO: This is an overpessimization. We only have to ensure that the PHI
+/// WRITE is not put into a statement containing the PHI itself. That could also
+/// be done by
+/// - having all (strongly connected) PHIs in a single statement,
+/// - unite only the PHIs in the operand tree of the PHI WRITE (because it only
+/// has a chance of being lifted before a PHI by being in a statement with a
+/// PHI that comes before in the basic block), or
+/// - when uniting statements, ensure that no (relevant) PHIs are overtaken.
+static void joinOrderedPHIs(EquivalenceClasses<Instruction *> &UnionFind,
+ ArrayRef<Instruction *> ModeledInsts) {
+ for (Instruction *Inst : ModeledInsts) {
+ PHINode *PHI = dyn_cast<PHINode>(Inst);
+ if (!PHI)
+ continue;
+
+ int Idx = PHI->getBasicBlockIndex(PHI->getParent());
+ if (Idx < 0)
+ continue;
+
+ Instruction *IncomingVal =
+ dyn_cast<Instruction>(PHI->getIncomingValue(Idx));
+ if (!IncomingVal)
+ continue;
+
+ UnionFind.unionSets(PHI, IncomingVal);
+ }
+}
+
+void ScopBuilder::buildEqivClassBlockStmts(BasicBlock *BB) {
+ Loop *L = LI.getLoopFor(BB);
+
+ // Extracting out modeled instructions saves us from checking
+ // shouldModelInst() repeatedly.
+ SmallVector<Instruction *, 32> ModeledInsts;
+ EquivalenceClasses<Instruction *> UnionFind;
+ Instruction *MainInst = nullptr, *MainLeader = nullptr;
+ for (Instruction &Inst : *BB) {
+ if (!shouldModelInst(&Inst, L))
+ continue;
+ ModeledInsts.push_back(&Inst);
+ UnionFind.insert(&Inst);
+
+ // When a BB is split into multiple statements, the main statement is the
+ // one containing the 'main' instruction. We select the first instruction
+ // that is unlikely to be removed (because it has side-effects) as the main
+ // one. It is used to ensure that at least one statement from the bb has the
+ // same name as with -polly-stmt-granularity=bb.
+ if (!MainInst && (isa<StoreInst>(Inst) ||
+ (isa<CallInst>(Inst) && !isa<IntrinsicInst>(Inst))))
+ MainInst = &Inst;
+ }
+
+ joinOperandTree(UnionFind, ModeledInsts);
+ joinOrderedInstructions(UnionFind, ModeledInsts);
+ joinOrderedPHIs(UnionFind, ModeledInsts);
+
+ // The list of instructions for statement (statement represented by the leader
+ // instruction).
+ MapVector<Instruction *, std::vector<Instruction *>> LeaderToInstList;
+
+ // The order of statements must be preserved w.r.t. their ordered
+ // instructions. Without this explicit scan, we would also use non-ordered
+ // instructions (whose order is arbitrary) to determine statement order.
+ for (Instruction *Inst : ModeledInsts) {
+ if (!isOrderedInstruction(Inst))
+ continue;
+
+ auto LeaderIt = UnionFind.findLeader(Inst);
+ if (LeaderIt == UnionFind.member_end())
+ continue;
+
+ // Insert element for the leader instruction.
+ (void)LeaderToInstList[*LeaderIt];
+ }
+
+ // Collect the instructions of all leaders. UnionFind's member iterator
+ // unfortunately are not in any specific order.
+ for (Instruction *Inst : ModeledInsts) {
+ auto LeaderIt = UnionFind.findLeader(Inst);
+ if (LeaderIt == UnionFind.member_end())
+ continue;
+
+ if (Inst == MainInst)
+ MainLeader = *LeaderIt;
+ std::vector<Instruction *> &InstList = LeaderToInstList[*LeaderIt];
+ InstList.push_back(Inst);
+ }
+
+ // Finally build the statements.
+ int Count = 0;
+ long BBIdx = scop->getNextStmtIdx();
+ for (auto &Instructions : LeaderToInstList) {
+ std::vector<Instruction *> &InstList = Instructions.second;
+
+ // If there is no main instruction, make the first statement the main.
+ bool IsMain = (MainInst ? MainLeader == Instructions.first : Count == 0);
+
+ std::string Name = makeStmtName(BB, BBIdx, Count, IsMain);
+ scop->addScopStmt(BB, Name, L, std::move(InstList));
+ Count += 1;
+ }
+
+ // Unconditionally add an epilogue (last statement). It contains no
+ // instructions, but holds the PHI write accesses for successor basic blocks,
+ // if the incoming value is not defined in another statement if the same BB.
+ // The epilogue becomes the main statement only if there is no other
+ // statement that could become main.
+ // The epilogue will be removed if no PHIWrite is added to it.
+ std::string EpilogueName = makeStmtName(BB, BBIdx, Count, Count == 0, true);
+ scop->addScopStmt(BB, EpilogueName, L, {});
+}
+
+void ScopBuilder::buildStmts(Region &SR) {
+ if (scop->isNonAffineSubRegion(&SR)) {
+ std::vector<Instruction *> Instructions;
+ Loop *SurroundingLoop =
+ getFirstNonBoxedLoopFor(SR.getEntry(), LI, scop->getBoxedLoops());
+ for (Instruction &Inst : *SR.getEntry())
+ if (shouldModelInst(&Inst, SurroundingLoop))
+ Instructions.push_back(&Inst);
+ long RIdx = scop->getNextStmtIdx();
+ std::string Name = makeStmtName(&SR, RIdx);
+ scop->addScopStmt(&SR, Name, SurroundingLoop, Instructions);
+ return;
+ }
+
+ for (auto I = SR.element_begin(), E = SR.element_end(); I != E; ++I)
+ if (I->isSubRegion())
+ buildStmts(*I->getNodeAs<Region>());
+ else {
+ BasicBlock *BB = I->getNodeAs<BasicBlock>();
+ switch (StmtGranularity) {
+ case GranularityChoice::BasicBlocks:
+ buildSequentialBlockStmts(BB);
+ break;
+ case GranularityChoice::ScalarIndependence:
+ buildEqivClassBlockStmts(BB);
+ break;
+ case GranularityChoice::Stores:
+ buildSequentialBlockStmts(BB, true);
+ break;
+ }
+ }
+}
+
+void ScopBuilder::buildAccessFunctions(ScopStmt *Stmt, BasicBlock &BB,
+ Region *NonAffineSubRegion) {
+ assert(
+ Stmt &&
+ "The exit BB is the only one that cannot be represented by a statement");
+ assert(Stmt->represents(&BB));
+
+ // We do not build access functions for error blocks, as they may contain
+ // instructions we can not model.
+ if (SD.isErrorBlock(BB, scop->getRegion()))
+ return;
+
+ auto BuildAccessesForInst = [this, Stmt,
+ NonAffineSubRegion](Instruction *Inst) {
+ PHINode *PHI = dyn_cast<PHINode>(Inst);
+ if (PHI)
+ buildPHIAccesses(Stmt, PHI, NonAffineSubRegion, false);
+
+ if (auto MemInst = MemAccInst::dyn_cast(*Inst)) {
+ assert(Stmt && "Cannot build access function in non-existing statement");
+ buildMemoryAccess(MemInst, Stmt);
+ }
+
+ // PHI nodes have already been modeled above and terminators that are
+ // not part of a non-affine subregion are fully modeled and regenerated
+ // from the polyhedral domains. Hence, they do not need to be modeled as
+ // explicit data dependences.
+ if (!PHI)
+ buildScalarDependences(Stmt, Inst);
+ };
+
+ const InvariantLoadsSetTy &RIL = scop->getRequiredInvariantLoads();
+ bool IsEntryBlock = (Stmt->getEntryBlock() == &BB);
+ if (IsEntryBlock) {
+ for (Instruction *Inst : Stmt->getInstructions())
+ BuildAccessesForInst(Inst);
+ if (Stmt->isRegionStmt())
+ BuildAccessesForInst(BB.getTerminator());
+ } else {
+ for (Instruction &Inst : BB) {
+ if (isIgnoredIntrinsic(&Inst))
+ continue;
+
+ // Invariant loads already have been processed.
+ if (isa<LoadInst>(Inst) && RIL.count(cast<LoadInst>(&Inst)))
+ continue;
+
+ BuildAccessesForInst(&Inst);
+ }
+ }
+}
+
+MemoryAccess *ScopBuilder::addMemoryAccess(
+ ScopStmt *Stmt, Instruction *Inst, MemoryAccess::AccessType AccType,
+ Value *BaseAddress, Type *ElementType, bool Affine, Value *AccessValue,
+ ArrayRef<const SCEV *> Subscripts, ArrayRef<const SCEV *> Sizes,
+ MemoryKind Kind) {
+ bool isKnownMustAccess = false;
+
+ // Accesses in single-basic block statements are always executed.
+ if (Stmt->isBlockStmt())
+ isKnownMustAccess = true;
+
+ if (Stmt->isRegionStmt()) {
+ // Accesses that dominate the exit block of a non-affine region are always
+ // executed. In non-affine regions there may exist MemoryKind::Values that
+ // do not dominate the exit. MemoryKind::Values will always dominate the
+ // exit and MemoryKind::PHIs only if there is at most one PHI_WRITE in the
+ // non-affine region.
+ if (Inst && DT.dominates(Inst->getParent(), Stmt->getRegion()->getExit()))
+ isKnownMustAccess = true;
+ }
+
+ // Non-affine PHI writes do not "happen" at a particular instruction, but
+ // after exiting the statement. Therefore they are guaranteed to execute and
+ // overwrite the old value.
+ if (Kind == MemoryKind::PHI || Kind == MemoryKind::ExitPHI)
+ isKnownMustAccess = true;
+
+ if (!isKnownMustAccess && AccType == MemoryAccess::MUST_WRITE)
+ AccType = MemoryAccess::MAY_WRITE;
+
+ auto *Access = new MemoryAccess(Stmt, Inst, AccType, BaseAddress, ElementType,
+ Affine, Subscripts, Sizes, AccessValue, Kind);
+
+ scop->addAccessFunction(Access);
+ Stmt->addAccess(Access);
+ return Access;
+}
+
+void ScopBuilder::addArrayAccess(ScopStmt *Stmt, MemAccInst MemAccInst,
+ MemoryAccess::AccessType AccType,
+ Value *BaseAddress, Type *ElementType,
+ bool IsAffine,
+ ArrayRef<const SCEV *> Subscripts,
+ ArrayRef<const SCEV *> Sizes,
+ Value *AccessValue) {
+ ArrayBasePointers.insert(BaseAddress);
+ addMemoryAccess(Stmt, MemAccInst, AccType, BaseAddress, ElementType, IsAffine,
+ AccessValue, Subscripts, Sizes, MemoryKind::Array);
+}
+
+/// Check if @p Expr is divisible by @p Size.
+static bool isDivisible(const SCEV *Expr, unsigned Size, ScalarEvolution &SE) {
+ assert(Size != 0);
+ if (Size == 1)
+ return true;
+
+ // Only one factor needs to be divisible.
+ if (auto *MulExpr = dyn_cast<SCEVMulExpr>(Expr)) {
+ for (auto *FactorExpr : MulExpr->operands())
+ if (isDivisible(FactorExpr, Size, SE))
+ return true;
+ return false;
+ }
+
+ // For other n-ary expressions (Add, AddRec, Max,...) all operands need
+ // to be divisible.
+ if (auto *NAryExpr = dyn_cast<SCEVNAryExpr>(Expr)) {
+ for (auto *OpExpr : NAryExpr->operands())
+ if (!isDivisible(OpExpr, Size, SE))
+ return false;
+ return true;
+ }
+
+ auto *SizeSCEV = SE.getConstant(Expr->getType(), Size);
+ auto *UDivSCEV = SE.getUDivExpr(Expr, SizeSCEV);
+ auto *MulSCEV = SE.getMulExpr(UDivSCEV, SizeSCEV);
+ return MulSCEV == Expr;
+}
+
+void ScopBuilder::foldSizeConstantsToRight() {
+ isl::union_set Accessed = scop->getAccesses().range();
+
+ for (auto Array : scop->arrays()) {
+ if (Array->getNumberOfDimensions() <= 1)
+ continue;
+
+ isl::space Space = Array->getSpace();
+ Space = Space.align_params(Accessed.get_space());
+
+ if (!Accessed.contains(Space))
+ continue;
+
+ isl::set Elements = Accessed.extract_set(Space);
+ isl::map Transform = isl::map::universe(Array->getSpace().map_from_set());
+
+ std::vector<int> Int;
+ unsigned Dims = unsignedFromIslSize(Elements.tuple_dim());
+ for (unsigned i = 0; i < Dims; i++) {
+ isl::set DimOnly = isl::set(Elements).project_out(isl::dim::set, 0, i);
+ DimOnly = DimOnly.project_out(isl::dim::set, 1, Dims - i - 1);
+ DimOnly = DimOnly.lower_bound_si(isl::dim::set, 0, 0);
+
+ isl::basic_set DimHull = DimOnly.affine_hull();
+
+ if (i == Dims - 1) {
+ Int.push_back(1);
+ Transform = Transform.equate(isl::dim::in, i, isl::dim::out, i);
+ continue;
+ }
+
+ if (unsignedFromIslSize(DimHull.dim(isl::dim::div)) == 1) {
+ isl::aff Diff = DimHull.get_div(0);
+ isl::val Val = Diff.get_denominator_val();
+
+ int ValInt = 1;
+ if (Val.is_int()) {
+ auto ValAPInt = APIntFromVal(Val);
+ if (ValAPInt.isSignedIntN(32))
+ ValInt = ValAPInt.getSExtValue();
+ } else {
+ }
+
+ Int.push_back(ValInt);
+ isl::constraint C = isl::constraint::alloc_equality(
+ isl::local_space(Transform.get_space()));
+ C = C.set_coefficient_si(isl::dim::out, i, ValInt);
+ C = C.set_coefficient_si(isl::dim::in, i, -1);
+ Transform = Transform.add_constraint(C);
+ continue;
+ }
+
+ isl::basic_set ZeroSet = isl::basic_set(DimHull);
+ ZeroSet = ZeroSet.fix_si(isl::dim::set, 0, 0);
+
+ int ValInt = 1;
+ if (ZeroSet.is_equal(DimHull)) {
+ ValInt = 0;
+ }
+
+ Int.push_back(ValInt);
+ Transform = Transform.equate(isl::dim::in, i, isl::dim::out, i);
+ }
+
+ isl::set MappedElements = isl::map(Transform).domain();
+ if (!Elements.is_subset(MappedElements))
+ continue;
+
+ bool CanFold = true;
+ if (Int[0] <= 1)
+ CanFold = false;
+
+ unsigned NumDims = Array->getNumberOfDimensions();
+ for (unsigned i = 1; i < NumDims - 1; i++)
+ if (Int[0] != Int[i] && Int[i])
+ CanFold = false;
+
+ if (!CanFold)
+ continue;
+
+ for (auto &Access : scop->access_functions())
+ if (Access->getScopArrayInfo() == Array)
+ Access->setAccessRelation(
+ Access->getAccessRelation().apply_range(Transform));
+
+ std::vector<const SCEV *> Sizes;
+ for (unsigned i = 0; i < NumDims; i++) {
+ auto Size = Array->getDimensionSize(i);
+
+ if (i == NumDims - 1)
+ Size = SE.getMulExpr(Size, SE.getConstant(Size->getType(), Int[0]));
+ Sizes.push_back(Size);
+ }
+
+ Array->updateSizes(Sizes, false /* CheckConsistency */);
+ }
+}
+
+void ScopBuilder::finalizeAccesses() {
+ updateAccessDimensionality();
+ foldSizeConstantsToRight();
+ foldAccessRelations();
+ assumeNoOutOfBounds();
+}
+
+void ScopBuilder::updateAccessDimensionality() {
+ // Check all array accesses for each base pointer and find a (virtual) element
+ // size for the base pointer that divides all access functions.
+ for (ScopStmt &Stmt : *scop)
+ for (MemoryAccess *Access : Stmt) {
+ if (!Access->isArrayKind())
+ continue;
+ ScopArrayInfo *Array =
+ const_cast<ScopArrayInfo *>(Access->getScopArrayInfo());
+
+ if (Array->getNumberOfDimensions() != 1)
+ continue;
+ unsigned DivisibleSize = Array->getElemSizeInBytes();
+ const SCEV *Subscript = Access->getSubscript(0);
+ while (!isDivisible(Subscript, DivisibleSize, SE))
+ DivisibleSize /= 2;
+ auto *Ty = IntegerType::get(SE.getContext(), DivisibleSize * 8);
+ Array->updateElementType(Ty);
+ }
+
+ for (auto &Stmt : *scop)
+ for (auto &Access : Stmt)
+ Access->updateDimensionality();
+}
+
+void ScopBuilder::foldAccessRelations() {
+ for (auto &Stmt : *scop)
+ for (auto &Access : Stmt)
+ Access->foldAccessRelation();
+}
+
+void ScopBuilder::assumeNoOutOfBounds() {
+ if (PollyIgnoreInbounds)
+ return;
+ for (auto &Stmt : *scop)
+ for (auto &Access : Stmt) {
+ isl::set Outside = Access->assumeNoOutOfBound();
+ const auto &Loc = Access->getAccessInstruction()
+ ? Access->getAccessInstruction()->getDebugLoc()
+ : DebugLoc();
+ recordAssumption(&RecordedAssumptions, INBOUNDS, Outside, Loc,
+ AS_ASSUMPTION);
+ }
+}
+
+void ScopBuilder::ensureValueWrite(Instruction *Inst) {
+ // Find the statement that defines the value of Inst. That statement has to
+ // write the value to make it available to those statements that read it.
+ ScopStmt *Stmt = scop->getStmtFor(Inst);
+
+ // It is possible that the value is synthesizable within a loop (such that it
+ // is not part of any statement), but not after the loop (where you need the
+ // number of loop round-trips to synthesize it). In LCSSA-form a PHI node will
+ // avoid this. In case the IR has no such PHI, use the last statement (where
+ // the value is synthesizable) to write the value.
+ if (!Stmt)
+ Stmt = scop->getLastStmtFor(Inst->getParent());
+
+ // Inst not defined within this SCoP.
+ if (!Stmt)
+ return;
+
+ // Do not process further if the instruction is already written.
+ if (Stmt->lookupValueWriteOf(Inst))
+ return;
+
+ addMemoryAccess(Stmt, Inst, MemoryAccess::MUST_WRITE, Inst, Inst->getType(),
+ true, Inst, ArrayRef<const SCEV *>(),
+ ArrayRef<const SCEV *>(), MemoryKind::Value);
+}
+
+void ScopBuilder::ensureValueRead(Value *V, ScopStmt *UserStmt) {
+ // TODO: Make ScopStmt::ensureValueRead(Value*) offer the same functionality
+ // to be able to replace this one. Currently, there is a split responsibility.
+ // In a first step, the MemoryAccess is created, but without the
+ // AccessRelation. In the second step by ScopStmt::buildAccessRelations(), the
+ // AccessRelation is created. At least for scalar accesses, there is no new
+ // information available at ScopStmt::buildAccessRelations(), so we could
+ // create the AccessRelation right away. This is what
+ // ScopStmt::ensureValueRead(Value*) does.
+
+ auto *Scope = UserStmt->getSurroundingLoop();
+ auto VUse = VirtualUse::create(scop.get(), UserStmt, Scope, V, false);
+ switch (VUse.getKind()) {
+ case VirtualUse::Constant:
+ case VirtualUse::Block:
+ case VirtualUse::Synthesizable:
+ case VirtualUse::Hoisted:
+ case VirtualUse::Intra:
+ // Uses of these kinds do not need a MemoryAccess.
+ break;
+
+ case VirtualUse::ReadOnly:
+ // Add MemoryAccess for invariant values only if requested.
+ if (!ModelReadOnlyScalars)
+ break;
+
+ LLVM_FALLTHROUGH;
+ case VirtualUse::Inter:
+
+ // Do not create another MemoryAccess for reloading the value if one already
+ // exists.
+ if (UserStmt->lookupValueReadOf(V))
+ break;
+
+ addMemoryAccess(UserStmt, nullptr, MemoryAccess::READ, V, V->getType(),
+ true, V, ArrayRef<const SCEV *>(), ArrayRef<const SCEV *>(),
+ MemoryKind::Value);
+
+ // Inter-statement uses need to write the value in their defining statement.
+ if (VUse.isInter())
+ ensureValueWrite(cast<Instruction>(V));
+ break;
+ }
+}
+
+void ScopBuilder::ensurePHIWrite(PHINode *PHI, ScopStmt *IncomingStmt,
+ BasicBlock *IncomingBlock,
+ Value *IncomingValue, bool IsExitBlock) {
+ // As the incoming block might turn out to be an error statement ensure we
+ // will create an exit PHI SAI object. It is needed during code generation
+ // and would be created later anyway.
+ if (IsExitBlock)
+ scop->getOrCreateScopArrayInfo(PHI, PHI->getType(), {},
+ MemoryKind::ExitPHI);
+
+ // This is possible if PHI is in the SCoP's entry block. The incoming blocks
+ // from outside the SCoP's region have no statement representation.
+ if (!IncomingStmt)
+ return;
+
+ // Take care for the incoming value being available in the incoming block.
+ // This must be done before the check for multiple PHI writes because multiple
+ // exiting edges from subregion each can be the effective written value of the
+ // subregion. As such, all of them must be made available in the subregion
+ // statement.
+ ensureValueRead(IncomingValue, IncomingStmt);
+
+ // Do not add more than one MemoryAccess per PHINode and ScopStmt.
+ if (MemoryAccess *Acc = IncomingStmt->lookupPHIWriteOf(PHI)) {
+ assert(Acc->getAccessInstruction() == PHI);
+ Acc->addIncoming(IncomingBlock, IncomingValue);
+ return;
+ }
+
+ MemoryAccess *Acc = addMemoryAccess(
+ IncomingStmt, PHI, MemoryAccess::MUST_WRITE, PHI, PHI->getType(), true,
+ PHI, ArrayRef<const SCEV *>(), ArrayRef<const SCEV *>(),
+ IsExitBlock ? MemoryKind::ExitPHI : MemoryKind::PHI);
+ assert(Acc);
+ Acc->addIncoming(IncomingBlock, IncomingValue);
+}
+
+void ScopBuilder::addPHIReadAccess(ScopStmt *PHIStmt, PHINode *PHI) {
+ addMemoryAccess(PHIStmt, PHI, MemoryAccess::READ, PHI, PHI->getType(), true,
+ PHI, ArrayRef<const SCEV *>(), ArrayRef<const SCEV *>(),
+ MemoryKind::PHI);
+}
+
+void ScopBuilder::buildDomain(ScopStmt &Stmt) {
+ isl::id Id = isl::id::alloc(scop->getIslCtx(), Stmt.getBaseName(), &Stmt);
+
+ Stmt.Domain = scop->getDomainConditions(&Stmt);
+ Stmt.Domain = Stmt.Domain.set_tuple_id(Id);
+}
+
+void ScopBuilder::collectSurroundingLoops(ScopStmt &Stmt) {
+ isl::set Domain = Stmt.getDomain();
+ BasicBlock *BB = Stmt.getEntryBlock();
+
+ Loop *L = LI.getLoopFor(BB);
+
+ while (L && Stmt.isRegionStmt() && Stmt.getRegion()->contains(L))
+ L = L->getParentLoop();
+
+ SmallVector<llvm::Loop *, 8> Loops;
+
+ while (L && Stmt.getParent()->getRegion().contains(L)) {
+ Loops.push_back(L);
+ L = L->getParentLoop();
+ }
+
+ Stmt.NestLoops.insert(Stmt.NestLoops.begin(), Loops.rbegin(), Loops.rend());
+}
+
+/// Return the reduction type for a given binary operator.
+static MemoryAccess::ReductionType getReductionType(const BinaryOperator *BinOp,
+ const Instruction *Load) {
+ if (!BinOp)
+ return MemoryAccess::RT_NONE;
+ switch (BinOp->getOpcode()) {
+ case Instruction::FAdd:
+ if (!BinOp->isFast())
+ return MemoryAccess::RT_NONE;
+ LLVM_FALLTHROUGH;
+ case Instruction::Add:
+ return MemoryAccess::RT_ADD;
+ case Instruction::Or:
+ return MemoryAccess::RT_BOR;
+ case Instruction::Xor:
+ return MemoryAccess::RT_BXOR;
+ case Instruction::And:
+ return MemoryAccess::RT_BAND;
+ case Instruction::FMul:
+ if (!BinOp->isFast())
+ return MemoryAccess::RT_NONE;
+ LLVM_FALLTHROUGH;
+ case Instruction::Mul:
+ if (DisableMultiplicativeReductions)
+ return MemoryAccess::RT_NONE;
+ return MemoryAccess::RT_MUL;
+ default:
+ return MemoryAccess::RT_NONE;
+ }
+}
+
+void ScopBuilder::checkForReductions(ScopStmt &Stmt) {
+ SmallVector<MemoryAccess *, 2> Loads;
+ SmallVector<std::pair<MemoryAccess *, MemoryAccess *>, 4> Candidates;
+
+ // First collect candidate load-store reduction chains by iterating over all
+ // stores and collecting possible reduction loads.
+ for (MemoryAccess *StoreMA : Stmt) {
+ if (StoreMA->isRead())
+ continue;
+
+ Loads.clear();
+ collectCandidateReductionLoads(StoreMA, Loads);
+ for (MemoryAccess *LoadMA : Loads)
+ Candidates.push_back(std::make_pair(LoadMA, StoreMA));
+ }
+
+ // Then check each possible candidate pair.
+ for (const auto &CandidatePair : Candidates) {
+ bool Valid = true;
+ isl::map LoadAccs = CandidatePair.first->getAccessRelation();
+ isl::map StoreAccs = CandidatePair.second->getAccessRelation();
+
+ // Skip those with obviously unequal base addresses.
+ if (!LoadAccs.has_equal_space(StoreAccs)) {
+ continue;
+ }
+
+ // And check if the remaining for overlap with other memory accesses.
+ isl::map AllAccsRel = LoadAccs.unite(StoreAccs);
+ AllAccsRel = AllAccsRel.intersect_domain(Stmt.getDomain());
+ isl::set AllAccs = AllAccsRel.range();
+
+ for (MemoryAccess *MA : Stmt) {
+ if (MA == CandidatePair.first || MA == CandidatePair.second)
+ continue;
+
+ isl::map AccRel =
+ MA->getAccessRelation().intersect_domain(Stmt.getDomain());
+ isl::set Accs = AccRel.range();
+
+ if (AllAccs.has_equal_space(Accs)) {
+ isl::set OverlapAccs = Accs.intersect(AllAccs);
+ Valid = Valid && OverlapAccs.is_empty();
+ }
+ }
+
+ if (!Valid)
+ continue;
+
+ const LoadInst *Load =
+ dyn_cast<const LoadInst>(CandidatePair.first->getAccessInstruction());
+ MemoryAccess::ReductionType RT =
+ getReductionType(dyn_cast<BinaryOperator>(Load->user_back()), Load);
+
+ // If no overlapping access was found we mark the load and store as
+ // reduction like.
+ CandidatePair.first->markAsReductionLike(RT);
+ CandidatePair.second->markAsReductionLike(RT);
+ }
+}
+
+void ScopBuilder::verifyInvariantLoads() {
+ auto &RIL = scop->getRequiredInvariantLoads();
+ for (LoadInst *LI : RIL) {
+ assert(LI && scop->contains(LI));
+ // If there exists a statement in the scop which has a memory access for
+ // @p LI, then mark this scop as infeasible for optimization.
+ for (ScopStmt &Stmt : *scop)
+ if (Stmt.getArrayAccessOrNULLFor(LI)) {
+ scop->invalidate(INVARIANTLOAD, LI->getDebugLoc(), LI->getParent());
+ return;
+ }
+ }
+}
+
+void ScopBuilder::hoistInvariantLoads() {
+ if (!PollyInvariantLoadHoisting)
+ return;
+
+ isl::union_map Writes = scop->getWrites();
+ for (ScopStmt &Stmt : *scop) {
+ InvariantAccessesTy InvariantAccesses;
+
+ for (MemoryAccess *Access : Stmt) {
+ isl::set NHCtx = getNonHoistableCtx(Access, Writes);
+ if (!NHCtx.is_null())
+ InvariantAccesses.push_back({Access, NHCtx});
+ }
+
+ // Transfer the memory access from the statement to the SCoP.
+ for (auto InvMA : InvariantAccesses)
+ Stmt.removeMemoryAccess(InvMA.MA);
+ addInvariantLoads(Stmt, InvariantAccesses);
+ }
+}
+
+/// Check if an access range is too complex.
+///
+/// An access range is too complex, if it contains either many disjuncts or
+/// very complex expressions. As a simple heuristic, we assume if a set to
+/// be too complex if the sum of existentially quantified dimensions and
+/// set dimensions is larger than a threshold. This reliably detects both
+/// sets with many disjuncts as well as sets with many divisions as they
+/// arise in h264.
+///
+/// @param AccessRange The range to check for complexity.
+///
+/// @returns True if the access range is too complex.
+static bool isAccessRangeTooComplex(isl::set AccessRange) {
+ unsigned NumTotalDims = 0;
+
+ for (isl::basic_set BSet : AccessRange.get_basic_set_list()) {
+ NumTotalDims += unsignedFromIslSize(BSet.dim(isl::dim::div));
+ NumTotalDims += unsignedFromIslSize(BSet.dim(isl::dim::set));
+ }
+
+ if (NumTotalDims > MaxDimensionsInAccessRange)
+ return true;
+
+ return false;
+}
+
+bool ScopBuilder::hasNonHoistableBasePtrInScop(MemoryAccess *MA,
+ isl::union_map Writes) {
+ if (auto *BasePtrMA = scop->lookupBasePtrAccess(MA)) {
+ return getNonHoistableCtx(BasePtrMA, Writes).is_null();
+ }
+
+ Value *BaseAddr = MA->getOriginalBaseAddr();
+ if (auto *BasePtrInst = dyn_cast<Instruction>(BaseAddr))
+ if (!isa<LoadInst>(BasePtrInst))
+ return scop->contains(BasePtrInst);
+
+ return false;
+}
+
+void ScopBuilder::addUserContext() {
+ if (UserContextStr.empty())
+ return;
+
+ isl::set UserContext = isl::set(scop->getIslCtx(), UserContextStr.c_str());
+ isl::space Space = scop->getParamSpace();
+ isl::size SpaceParams = Space.dim(isl::dim::param);
+ if (unsignedFromIslSize(SpaceParams) !=
+ unsignedFromIslSize(UserContext.dim(isl::dim::param))) {
+ std::string SpaceStr = stringFromIslObj(Space, "null");
+ errs() << "Error: the context provided in -polly-context has not the same "
+ << "number of dimensions than the computed context. Due to this "
+ << "mismatch, the -polly-context option is ignored. Please provide "
+ << "the context in the parameter space: " << SpaceStr << ".\n";
+ return;
+ }
+
+ for (auto i : rangeIslSize(0, SpaceParams)) {
+ std::string NameContext =
+ scop->getContext().get_dim_name(isl::dim::param, i);
+ std::string NameUserContext = UserContext.get_dim_name(isl::dim::param, i);
+
+ if (NameContext != NameUserContext) {
+ std::string SpaceStr = stringFromIslObj(Space, "null");
+ errs() << "Error: the name of dimension " << i
+ << " provided in -polly-context "
+ << "is '" << NameUserContext << "', but the name in the computed "
+ << "context is '" << NameContext
+ << "'. Due to this name mismatch, "
+ << "the -polly-context option is ignored. Please provide "
+ << "the context in the parameter space: " << SpaceStr << ".\n";
+ return;
+ }
+
+ UserContext = UserContext.set_dim_id(isl::dim::param, i,
+ Space.get_dim_id(isl::dim::param, i));
+ }
+ isl::set newContext = scop->getContext().intersect(UserContext);
+ scop->setContext(newContext);
+}
+
+isl::set ScopBuilder::getNonHoistableCtx(MemoryAccess *Access,
+ isl::union_map Writes) {
+ // TODO: Loads that are not loop carried, hence are in a statement with
+ // zero iterators, are by construction invariant, though we
+ // currently "hoist" them anyway. This is necessary because we allow
+ // them to be treated as parameters (e.g., in conditions) and our code
+ // generation would otherwise use the old value.
+
+ auto &Stmt = *Access->getStatement();
+ BasicBlock *BB = Stmt.getEntryBlock();
+
+ if (Access->isScalarKind() || Access->isWrite() || !Access->isAffine() ||
+ Access->isMemoryIntrinsic())
+ return {};
+
+ // Skip accesses that have an invariant base pointer which is defined but
+ // not loaded inside the SCoP. This can happened e.g., if a readnone call
+ // returns a pointer that is used as a base address. However, as we want
+ // to hoist indirect pointers, we allow the base pointer to be defined in
+ // the region if it is also a memory access. Each ScopArrayInfo object
+ // that has a base pointer origin has a base pointer that is loaded and
+ // that it is invariant, thus it will be hoisted too. However, if there is
+ // no base pointer origin we check that the base pointer is defined
+ // outside the region.
+ auto *LI = cast<LoadInst>(Access->getAccessInstruction());
+ if (hasNonHoistableBasePtrInScop(Access, Writes))
+ return {};
+
+ isl::map AccessRelation = Access->getAccessRelation();
+ assert(!AccessRelation.is_empty());
+
+ if (AccessRelation.involves_dims(isl::dim::in, 0, Stmt.getNumIterators()))
+ return {};
+
+ AccessRelation = AccessRelation.intersect_domain(Stmt.getDomain());
+ isl::set SafeToLoad;
+
+ auto &DL = scop->getFunction().getParent()->getDataLayout();
+ if (isSafeToLoadUnconditionally(LI->getPointerOperand(), LI->getType(),
+ LI->getAlign(), DL)) {
+ SafeToLoad = isl::set::universe(AccessRelation.get_space().range());
+ } else if (BB != LI->getParent()) {
+ // Skip accesses in non-affine subregions as they might not be executed
+ // under the same condition as the entry of the non-affine subregion.
+ return {};
+ } else {
+ SafeToLoad = AccessRelation.range();
+ }
+
+ if (isAccessRangeTooComplex(AccessRelation.range()))
+ return {};
+
+ isl::union_map Written = Writes.intersect_range(SafeToLoad);
+ isl::set WrittenCtx = Written.params();
+ bool IsWritten = !WrittenCtx.is_empty();
+
+ if (!IsWritten)
+ return WrittenCtx;
+
+ WrittenCtx = WrittenCtx.remove_divs();
+ bool TooComplex =
+ unsignedFromIslSize(WrittenCtx.n_basic_set()) >= MaxDisjunctsInDomain;
+ if (TooComplex || !isRequiredInvariantLoad(LI))
+ return {};
+
+ scop->addAssumption(INVARIANTLOAD, WrittenCtx, LI->getDebugLoc(),
+ AS_RESTRICTION, LI->getParent());
+ return WrittenCtx;
+}
+
+static bool isAParameter(llvm::Value *maybeParam, const Function &F) {
+ for (const llvm::Argument &Arg : F.args())
+ if (&Arg == maybeParam)
+ return true;
+
+ return false;
+}
+
+bool ScopBuilder::canAlwaysBeHoisted(MemoryAccess *MA,
+ bool StmtInvalidCtxIsEmpty,
+ bool MAInvalidCtxIsEmpty,
+ bool NonHoistableCtxIsEmpty) {
+ LoadInst *LInst = cast<LoadInst>(MA->getAccessInstruction());
+ const DataLayout &DL = LInst->getParent()->getModule()->getDataLayout();
+ if (PollyAllowDereferenceOfAllFunctionParams &&
+ isAParameter(LInst->getPointerOperand(), scop->getFunction()))
+ return true;
+
+ // TODO: We can provide more information for better but more expensive
+ // results.
+ if (!isDereferenceableAndAlignedPointer(
+ LInst->getPointerOperand(), LInst->getType(), LInst->getAlign(), DL))
+ return false;
+
+ // If the location might be overwritten we do not hoist it unconditionally.
+ //
+ // TODO: This is probably too conservative.
+ if (!NonHoistableCtxIsEmpty)
+ return false;
+
+ // If a dereferenceable load is in a statement that is modeled precisely we
+ // can hoist it.
+ if (StmtInvalidCtxIsEmpty && MAInvalidCtxIsEmpty)
+ return true;
+
+ // Even if the statement is not modeled precisely we can hoist the load if it
+ // does not involve any parameters that might have been specialized by the
+ // statement domain.
+ for (const SCEV *Subscript : MA->subscripts())
+ if (!isa<SCEVConstant>(Subscript))
+ return false;
+ return true;
+}
+
+void ScopBuilder::addInvariantLoads(ScopStmt &Stmt,
+ InvariantAccessesTy &InvMAs) {
+ if (InvMAs.empty())
+ return;
+
+ isl::set StmtInvalidCtx = Stmt.getInvalidContext();
+ bool StmtInvalidCtxIsEmpty = StmtInvalidCtx.is_empty();
+
+ // Get the context under which the statement is executed but remove the error
+ // context under which this statement is reached.
+ isl::set DomainCtx = Stmt.getDomain().params();
+ DomainCtx = DomainCtx.subtract(StmtInvalidCtx);
+
+ if (unsignedFromIslSize(DomainCtx.n_basic_set()) >= MaxDisjunctsInDomain) {
+ auto *AccInst = InvMAs.front().MA->getAccessInstruction();
+ scop->invalidate(COMPLEXITY, AccInst->getDebugLoc(), AccInst->getParent());
+ return;
+ }
+
+ // Project out all parameters that relate to loads in the statement. Otherwise
+ // we could have cyclic dependences on the constraints under which the
+ // hoisted loads are executed and we could not determine an order in which to
+ // pre-load them. This happens because not only lower bounds are part of the
+ // domain but also upper bounds.
+ for (auto &InvMA : InvMAs) {
+ auto *MA = InvMA.MA;
+ Instruction *AccInst = MA->getAccessInstruction();
+ if (SE.isSCEVable(AccInst->getType())) {
+ SetVector<Value *> Values;
+ for (const SCEV *Parameter : scop->parameters()) {
+ Values.clear();
+ findValues(Parameter, SE, Values);
+ if (!Values.count(AccInst))
+ continue;
+
+ isl::id ParamId = scop->getIdForParam(Parameter);
+ if (!ParamId.is_null()) {
+ int Dim = DomainCtx.find_dim_by_id(isl::dim::param, ParamId);
+ if (Dim >= 0)
+ DomainCtx = DomainCtx.eliminate(isl::dim::param, Dim, 1);
+ }
+ }
+ }
+ }
+
+ for (auto &InvMA : InvMAs) {
+ auto *MA = InvMA.MA;
+ isl::set NHCtx = InvMA.NonHoistableCtx;
+
+ // Check for another invariant access that accesses the same location as
+ // MA and if found consolidate them. Otherwise create a new equivalence
+ // class at the end of InvariantEquivClasses.
+ LoadInst *LInst = cast<LoadInst>(MA->getAccessInstruction());
+ Type *Ty = LInst->getType();
+ const SCEV *PointerSCEV = SE.getSCEV(LInst->getPointerOperand());
+
+ isl::set MAInvalidCtx = MA->getInvalidContext();
+ bool NonHoistableCtxIsEmpty = NHCtx.is_empty();
+ bool MAInvalidCtxIsEmpty = MAInvalidCtx.is_empty();
+
+ isl::set MACtx;
+ // Check if we know that this pointer can be speculatively accessed.
+ if (canAlwaysBeHoisted(MA, StmtInvalidCtxIsEmpty, MAInvalidCtxIsEmpty,
+ NonHoistableCtxIsEmpty)) {
+ MACtx = isl::set::universe(DomainCtx.get_space());
+ } else {
+ MACtx = DomainCtx;
+ MACtx = MACtx.subtract(MAInvalidCtx.unite(NHCtx));
+ MACtx = MACtx.gist_params(scop->getContext());
+ }
+
+ bool Consolidated = false;
+ for (auto &IAClass : scop->invariantEquivClasses()) {
+ if (PointerSCEV != IAClass.IdentifyingPointer || Ty != IAClass.AccessType)
+ continue;
+
+ // If the pointer and the type is equal check if the access function wrt.
+ // to the domain is equal too. It can happen that the domain fixes
+ // parameter values and these can be different for distinct part of the
+ // SCoP. If this happens we cannot consolidate the loads but need to
+ // create a new invariant load equivalence class.
+ auto &MAs = IAClass.InvariantAccesses;
+ if (!MAs.empty()) {
+ auto *LastMA = MAs.front();
+
+ isl::set AR = MA->getAccessRelation().range();
+ isl::set LastAR = LastMA->getAccessRelation().range();
+ bool SameAR = AR.is_equal(LastAR);
+
+ if (!SameAR)
+ continue;
+ }
+
+ // Add MA to the list of accesses that are in this class.
+ MAs.push_front(MA);
+
+ Consolidated = true;
+
+ // Unify the execution context of the class and this statement.
+ isl::set IAClassDomainCtx = IAClass.ExecutionContext;
+ if (!IAClassDomainCtx.is_null())
+ IAClassDomainCtx = IAClassDomainCtx.unite(MACtx).coalesce();
+ else
+ IAClassDomainCtx = MACtx;
+ IAClass.ExecutionContext = IAClassDomainCtx;
+ break;
+ }
+
+ if (Consolidated)
+ continue;
+
+ MACtx = MACtx.coalesce();
+
+ // If we did not consolidate MA, thus did not find an equivalence class
+ // for it, we create a new one.
+ scop->addInvariantEquivClass(
+ InvariantEquivClassTy{PointerSCEV, MemoryAccessList{MA}, MACtx, Ty});
+ }
+}
+
+void ScopBuilder::collectCandidateReductionLoads(
+ MemoryAccess *StoreMA, SmallVectorImpl<MemoryAccess *> &Loads) {
+ ScopStmt *Stmt = StoreMA->getStatement();
+
+ auto *Store = dyn_cast<StoreInst>(StoreMA->getAccessInstruction());
+ if (!Store)
+ return;
+
+ // Skip if there is not one binary operator between the load and the store
+ auto *BinOp = dyn_cast<BinaryOperator>(Store->getValueOperand());
+ if (!BinOp)
+ return;
+
+ // Skip if the binary operators has multiple uses
+ if (BinOp->getNumUses() != 1)
+ return;
+
+ // Skip if the opcode of the binary operator is not commutative/associative
+ if (!BinOp->isCommutative() || !BinOp->isAssociative())
+ return;
+
+ // Skip if the binary operator is outside the current SCoP
+ if (BinOp->getParent() != Store->getParent())
+ return;
+
+ // Skip if it is a multiplicative reduction and we disabled them
+ if (DisableMultiplicativeReductions &&
+ (BinOp->getOpcode() == Instruction::Mul ||
+ BinOp->getOpcode() == Instruction::FMul))
+ return;
+
+ // Check the binary operator operands for a candidate load
+ auto *PossibleLoad0 = dyn_cast<LoadInst>(BinOp->getOperand(0));
+ auto *PossibleLoad1 = dyn_cast<LoadInst>(BinOp->getOperand(1));
+ if (!PossibleLoad0 && !PossibleLoad1)
+ return;
+
+ // A load is only a candidate if it cannot escape (thus has only this use)
+ if (PossibleLoad0 && PossibleLoad0->getNumUses() == 1)
+ if (PossibleLoad0->getParent() == Store->getParent())
+ Loads.push_back(&Stmt->getArrayAccessFor(PossibleLoad0));
+ if (PossibleLoad1 && PossibleLoad1->getNumUses() == 1)
+ if (PossibleLoad1->getParent() == Store->getParent())
+ Loads.push_back(&Stmt->getArrayAccessFor(PossibleLoad1));
+}
+
+/// Find the canonical scop array info object for a set of invariant load
+/// hoisted loads. The canonical array is the one that corresponds to the
+/// first load in the list of accesses which is used as base pointer of a
+/// scop array.
+static const ScopArrayInfo *findCanonicalArray(Scop &S,
+ MemoryAccessList &Accesses) {
+ for (MemoryAccess *Access : Accesses) {
+ const ScopArrayInfo *CanonicalArray = S.getScopArrayInfoOrNull(
+ Access->getAccessInstruction(), MemoryKind::Array);
+ if (CanonicalArray)
+ return CanonicalArray;
+ }
+ return nullptr;
+}
+
+/// Check if @p Array severs as base array in an invariant load.
+static bool isUsedForIndirectHoistedLoad(Scop &S, const ScopArrayInfo *Array) {
+ for (InvariantEquivClassTy &EqClass2 : S.getInvariantAccesses())
+ for (MemoryAccess *Access2 : EqClass2.InvariantAccesses)
+ if (Access2->getScopArrayInfo() == Array)
+ return true;
+ return false;
+}
+
+/// Replace the base pointer arrays in all memory accesses referencing @p Old,
+/// with a reference to @p New.
+static void replaceBasePtrArrays(Scop &S, const ScopArrayInfo *Old,
+ const ScopArrayInfo *New) {
+ for (ScopStmt &Stmt : S)
+ for (MemoryAccess *Access : Stmt) {
+ if (Access->getLatestScopArrayInfo() != Old)
+ continue;
+
+ isl::id Id = New->getBasePtrId();
+ isl::map Map = Access->getAccessRelation();
+ Map = Map.set_tuple_id(isl::dim::out, Id);
+ Access->setAccessRelation(Map);
+ }
+}
+
+void ScopBuilder::canonicalizeDynamicBasePtrs() {
+ for (InvariantEquivClassTy &EqClass : scop->InvariantEquivClasses) {
+ MemoryAccessList &BasePtrAccesses = EqClass.InvariantAccesses;
+
+ const ScopArrayInfo *CanonicalBasePtrSAI =
+ findCanonicalArray(*scop, BasePtrAccesses);
+
+ if (!CanonicalBasePtrSAI)
+ continue;
+
+ for (MemoryAccess *BasePtrAccess : BasePtrAccesses) {
+ const ScopArrayInfo *BasePtrSAI = scop->getScopArrayInfoOrNull(
+ BasePtrAccess->getAccessInstruction(), MemoryKind::Array);
+ if (!BasePtrSAI || BasePtrSAI == CanonicalBasePtrSAI ||
+ !BasePtrSAI->isCompatibleWith(CanonicalBasePtrSAI))
+ continue;
+
+ // we currently do not canonicalize arrays where some accesses are
+ // hoisted as invariant loads. If we would, we need to update the access
+ // function of the invariant loads as well. However, as this is not a
+ // very common situation, we leave this for now to avoid further
+ // complexity increases.
+ if (isUsedForIndirectHoistedLoad(*scop, BasePtrSAI))
+ continue;
+
+ replaceBasePtrArrays(*scop, BasePtrSAI, CanonicalBasePtrSAI);
+ }
+ }
+}
+
+void ScopBuilder::buildAccessRelations(ScopStmt &Stmt) {
+ for (MemoryAccess *Access : Stmt.MemAccs) {
+ Type *ElementType = Access->getElementType();
+
+ MemoryKind Ty;
+ if (Access->isPHIKind())
+ Ty = MemoryKind::PHI;
+ else if (Access->isExitPHIKind())
+ Ty = MemoryKind::ExitPHI;
+ else if (Access->isValueKind())
+ Ty = MemoryKind::Value;
+ else
+ Ty = MemoryKind::Array;
+
+ // Create isl::pw_aff for SCEVs which describe sizes. Collect all
+ // assumptions which are taken. isl::pw_aff objects are cached internally
+ // and they are used later by scop.
+ for (const SCEV *Size : Access->Sizes) {
+ if (!Size)
+ continue;
+ scop->getPwAff(Size, nullptr, false, &RecordedAssumptions);
+ }
+ auto *SAI = scop->getOrCreateScopArrayInfo(Access->getOriginalBaseAddr(),
+ ElementType, Access->Sizes, Ty);
+
+ // Create isl::pw_aff for SCEVs which describe subscripts. Collect all
+ // assumptions which are taken. isl::pw_aff objects are cached internally
+ // and they are used later by scop.
+ for (const SCEV *Subscript : Access->subscripts()) {
+ if (!Access->isAffine() || !Subscript)
+ continue;
+ scop->getPwAff(Subscript, Stmt.getEntryBlock(), false,
+ &RecordedAssumptions);
+ }
+ Access->buildAccessRelation(SAI);
+ scop->addAccessData(Access);
+ }
+}
+
+/// Add the minimal/maximal access in @p Set to @p User.
+///
+/// @return True if more accesses should be added, false if we reached the
+/// maximal number of run-time checks to be generated.
+static bool buildMinMaxAccess(isl::set Set,
+ Scop::MinMaxVectorTy &MinMaxAccesses, Scop &S) {
+ isl::pw_multi_aff MinPMA, MaxPMA;
+ isl::pw_aff LastDimAff;
+ isl::aff OneAff;
+ unsigned Pos;
+
+ Set = Set.remove_divs();
+ polly::simplify(Set);
+
+ if (unsignedFromIslSize(Set.n_basic_set()) > RunTimeChecksMaxAccessDisjuncts)
+ Set = Set.simple_hull();
+
+ // Restrict the number of parameters involved in the access as the lexmin/
+ // lexmax computation will take too long if this number is high.
+ //
+ // Experiments with a simple test case using an i7 4800MQ:
+ //
+ // #Parameters involved | Time (in sec)
+ // 6 | 0.01
+ // 7 | 0.04
+ // 8 | 0.12
+ // 9 | 0.40
+ // 10 | 1.54
+ // 11 | 6.78
+ // 12 | 30.38
+ //
+ if (isl_set_n_param(Set.get()) >
+ static_cast<isl_size>(RunTimeChecksMaxParameters)) {
+ unsigned InvolvedParams = 0;
+ for (unsigned u = 0, e = isl_set_n_param(Set.get()); u < e; u++)
+ if (Set.involves_dims(isl::dim::param, u, 1))
+ InvolvedParams++;
+
+ if (InvolvedParams > RunTimeChecksMaxParameters)
+ return false;
+ }
+
+ MinPMA = Set.lexmin_pw_multi_aff();
+ MaxPMA = Set.lexmax_pw_multi_aff();
+
+ MinPMA = MinPMA.coalesce();
+ MaxPMA = MaxPMA.coalesce();
+
+ if (MaxPMA.is_null())
+ return false;
+
+ unsigned MaxOutputSize = unsignedFromIslSize(MaxPMA.dim(isl::dim::out));
+
+ // Adjust the last dimension of the maximal access by one as we want to
+ // enclose the accessed memory region by MinPMA and MaxPMA. The pointer
+ // we test during code generation might now point after the end of the
+ // allocated array but we will never dereference it anyway.
+ assert(MaxOutputSize >= 1 && "Assumed at least one output dimension");
+
+ Pos = MaxOutputSize - 1;
+ LastDimAff = MaxPMA.at(Pos);
+ OneAff = isl::aff(isl::local_space(LastDimAff.get_domain_space()));
+ OneAff = OneAff.add_constant_si(1);
+ LastDimAff = LastDimAff.add(OneAff);
+ MaxPMA = MaxPMA.set_pw_aff(Pos, LastDimAff);
+
+ if (MinPMA.is_null() || MaxPMA.is_null())
+ return false;
+
+ MinMaxAccesses.push_back(std::make_pair(MinPMA, MaxPMA));
+
+ return true;
+}
+
+/// Wrapper function to calculate minimal/maximal accesses to each array.
+bool ScopBuilder::calculateMinMaxAccess(AliasGroupTy AliasGroup,
+ Scop::MinMaxVectorTy &MinMaxAccesses) {
+ MinMaxAccesses.reserve(AliasGroup.size());
+
+ isl::union_set Domains = scop->getDomains();
+ isl::union_map Accesses = isl::union_map::empty(scop->getIslCtx());
+
+ for (MemoryAccess *MA : AliasGroup)
+ Accesses = Accesses.unite(MA->getAccessRelation());
+
+ Accesses = Accesses.intersect_domain(Domains);
+ isl::union_set Locations = Accesses.range();
+
+ bool LimitReached = false;
+ for (isl::set Set : Locations.get_set_list()) {
+ LimitReached |= !buildMinMaxAccess(Set, MinMaxAccesses, *scop);
+ if (LimitReached)
+ break;
+ }
+
+ return !LimitReached;
+}
+
+static isl::set getAccessDomain(MemoryAccess *MA) {
+ isl::set Domain = MA->getStatement()->getDomain();
+ Domain = Domain.project_out(isl::dim::set, 0,
+ unsignedFromIslSize(Domain.tuple_dim()));
+ return Domain.reset_tuple_id();
+}
+
+bool ScopBuilder::buildAliasChecks() {
+ if (!PollyUseRuntimeAliasChecks)
+ return true;
+
+ if (buildAliasGroups()) {
+ // Aliasing assumptions do not go through addAssumption but we still want to
+ // collect statistics so we do it here explicitly.
+ if (scop->getAliasGroups().size())
+ Scop::incrementNumberOfAliasingAssumptions(1);
+ return true;
+ }
+
+ // If a problem occurs while building the alias groups we need to delete
+ // this SCoP and pretend it wasn't valid in the first place. To this end
+ // we make the assumed context infeasible.
+ scop->invalidate(ALIASING, DebugLoc());
+
+ LLVM_DEBUG(dbgs() << "\n\nNOTE: Run time checks for " << scop->getNameStr()
+ << " could not be created. This SCoP has been dismissed.");
+ return false;
+}
+
+std::tuple<ScopBuilder::AliasGroupVectorTy, DenseSet<const ScopArrayInfo *>>
+ScopBuilder::buildAliasGroupsForAccesses() {
+ AliasSetTracker AST(AA);
+
+ DenseMap<Value *, MemoryAccess *> PtrToAcc;
+ DenseSet<const ScopArrayInfo *> HasWriteAccess;
+ for (ScopStmt &Stmt : *scop) {
+
+ isl::set StmtDomain = Stmt.getDomain();
+ bool StmtDomainEmpty = StmtDomain.is_empty();
+
+ // Statements with an empty domain will never be executed.
+ if (StmtDomainEmpty)
+ continue;
+
+ for (MemoryAccess *MA : Stmt) {
+ if (MA->isScalarKind())
+ continue;
+ if (!MA->isRead())
+ HasWriteAccess.insert(MA->getScopArrayInfo());
+ MemAccInst Acc(MA->getAccessInstruction());
+ if (MA->isRead() && isa<MemTransferInst>(Acc))
+ PtrToAcc[cast<MemTransferInst>(Acc)->getRawSource()] = MA;
+ else
+ PtrToAcc[Acc.getPointerOperand()] = MA;
+ AST.add(Acc);
+ }
+ }
+
+ AliasGroupVectorTy AliasGroups;
+ for (AliasSet &AS : AST) {
+ if (AS.isMustAlias() || AS.isForwardingAliasSet())
+ continue;
+ AliasGroupTy AG;
+ for (auto &PR : AS)
+ AG.push_back(PtrToAcc[PR.getValue()]);
+ if (AG.size() < 2)
+ continue;
+ AliasGroups.push_back(std::move(AG));
+ }
+
+ return std::make_tuple(AliasGroups, HasWriteAccess);
+}
+
+bool ScopBuilder::buildAliasGroups() {
+ // To create sound alias checks we perform the following steps:
+ // o) We partition each group into read only and non read only accesses.
+ // o) For each group with more than one base pointer we then compute minimal
+ // and maximal accesses to each array of a group in read only and non
+ // read only partitions separately.
+ AliasGroupVectorTy AliasGroups;
+ DenseSet<const ScopArrayInfo *> HasWriteAccess;
+
+ std::tie(AliasGroups, HasWriteAccess) = buildAliasGroupsForAccesses();
+
+ splitAliasGroupsByDomain(AliasGroups);
+
+ for (AliasGroupTy &AG : AliasGroups) {
+ if (!scop->hasFeasibleRuntimeContext())
+ return false;
+
+ {
+ IslMaxOperationsGuard MaxOpGuard(scop->getIslCtx().get(), OptComputeOut);
+ bool Valid = buildAliasGroup(AG, HasWriteAccess);
+ if (!Valid)
+ return false;
+ }
+ if (isl_ctx_last_error(scop->getIslCtx().get()) == isl_error_quota) {
+ scop->invalidate(COMPLEXITY, DebugLoc());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ScopBuilder::buildAliasGroup(
+ AliasGroupTy &AliasGroup, DenseSet<const ScopArrayInfo *> HasWriteAccess) {
+ AliasGroupTy ReadOnlyAccesses;
+ AliasGroupTy ReadWriteAccesses;
+ SmallPtrSet<const ScopArrayInfo *, 4> ReadWriteArrays;
+ SmallPtrSet<const ScopArrayInfo *, 4> ReadOnlyArrays;
+
+ if (AliasGroup.size() < 2)
+ return true;
+
+ for (MemoryAccess *Access : AliasGroup) {
+ ORE.emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "PossibleAlias",
+ Access->getAccessInstruction())
+ << "Possibly aliasing pointer, use restrict keyword.");
+ const ScopArrayInfo *Array = Access->getScopArrayInfo();
+ if (HasWriteAccess.count(Array)) {
+ ReadWriteArrays.insert(Array);
+ ReadWriteAccesses.push_back(Access);
+ } else {
+ ReadOnlyArrays.insert(Array);
+ ReadOnlyAccesses.push_back(Access);
+ }
+ }
+
+ // If there are no read-only pointers, and less than two read-write pointers,
+ // no alias check is needed.
+ if (ReadOnlyAccesses.empty() && ReadWriteArrays.size() <= 1)
+ return true;
+
+ // If there is no read-write pointer, no alias check is needed.
+ if (ReadWriteArrays.empty())
+ return true;
+
+ // For non-affine accesses, no alias check can be generated as we cannot
+ // compute a sufficiently tight lower and upper bound: bail out.
+ for (MemoryAccess *MA : AliasGroup) {
+ if (!MA->isAffine()) {
+ scop->invalidate(ALIASING, MA->getAccessInstruction()->getDebugLoc(),
+ MA->getAccessInstruction()->getParent());
+ return false;
+ }
+ }
+
+ // Ensure that for all memory accesses for which we generate alias checks,
+ // their base pointers are available.
+ for (MemoryAccess *MA : AliasGroup) {
+ if (MemoryAccess *BasePtrMA = scop->lookupBasePtrAccess(MA))
+ scop->addRequiredInvariantLoad(
+ cast<LoadInst>(BasePtrMA->getAccessInstruction()));
+ }
+
+ // scop->getAliasGroups().emplace_back();
+ // Scop::MinMaxVectorPairTy &pair = scop->getAliasGroups().back();
+ Scop::MinMaxVectorTy MinMaxAccessesReadWrite;
+ Scop::MinMaxVectorTy MinMaxAccessesReadOnly;
+
+ bool Valid;
+
+ Valid = calculateMinMaxAccess(ReadWriteAccesses, MinMaxAccessesReadWrite);
+
+ if (!Valid)
+ return false;
+
+ // Bail out if the number of values we need to compare is too large.
+ // This is important as the number of comparisons grows quadratically with
+ // the number of values we need to compare.
+ if (MinMaxAccessesReadWrite.size() + ReadOnlyArrays.size() >
+ RunTimeChecksMaxArraysPerGroup)
+ return false;
+
+ Valid = calculateMinMaxAccess(ReadOnlyAccesses, MinMaxAccessesReadOnly);
+
+ scop->addAliasGroup(MinMaxAccessesReadWrite, MinMaxAccessesReadOnly);
+ if (!Valid)
+ return false;
+
+ return true;
+}
+
+void ScopBuilder::splitAliasGroupsByDomain(AliasGroupVectorTy &AliasGroups) {
+ for (unsigned u = 0; u < AliasGroups.size(); u++) {
+ AliasGroupTy NewAG;
+ AliasGroupTy &AG = AliasGroups[u];
+ AliasGroupTy::iterator AGI = AG.begin();
+ isl::set AGDomain = getAccessDomain(*AGI);
+ while (AGI != AG.end()) {
+ MemoryAccess *MA = *AGI;
+ isl::set MADomain = getAccessDomain(MA);
+ if (AGDomain.is_disjoint(MADomain)) {
+ NewAG.push_back(MA);
+ AGI = AG.erase(AGI);
+ } else {
+ AGDomain = AGDomain.unite(MADomain);
+ AGI++;
+ }
+ }
+ if (NewAG.size() > 1)
+ AliasGroups.push_back(std::move(NewAG));
+ }
+}
+
+#ifndef NDEBUG
+static void verifyUse(Scop *S, Use &Op, LoopInfo &LI) {
+ auto PhysUse = VirtualUse::create(S, Op, &LI, false);
+ auto VirtUse = VirtualUse::create(S, Op, &LI, true);
+ assert(PhysUse.getKind() == VirtUse.getKind());
+}
+
+/// Check the consistency of every statement's MemoryAccesses.
+///
+/// The check is carried out by expecting the "physical" kind of use (derived
+/// from the BasicBlocks instructions resides in) to be same as the "virtual"
+/// kind of use (derived from a statement's MemoryAccess).
+///
+/// The "physical" uses are taken by ensureValueRead to determine whether to
+/// create MemoryAccesses. When done, the kind of scalar access should be the
+/// same no matter which way it was derived.
+///
+/// The MemoryAccesses might be changed by later SCoP-modifying passes and hence
+/// can intentionally influence on the kind of uses (not corresponding to the
+/// "physical" anymore, hence called "virtual"). The CodeGenerator therefore has
+/// to pick up the virtual uses. But here in the code generator, this has not
+/// happened yet, such that virtual and physical uses are equivalent.
+static void verifyUses(Scop *S, LoopInfo &LI, DominatorTree &DT) {
+ for (auto *BB : S->getRegion().blocks()) {
+ for (auto &Inst : *BB) {
+ auto *Stmt = S->getStmtFor(&Inst);
+ if (!Stmt)
+ continue;
+
+ if (isIgnoredIntrinsic(&Inst))
+ continue;
+
+ // Branch conditions are encoded in the statement domains.
+ if (Inst.isTerminator() && Stmt->isBlockStmt())
+ continue;
+
+ // Verify all uses.
+ for (auto &Op : Inst.operands())
+ verifyUse(S, Op, LI);
+
+ // Stores do not produce values used by other statements.
+ if (isa<StoreInst>(Inst))
+ continue;
+
+ // For every value defined in the block, also check that a use of that
+ // value in the same statement would not be an inter-statement use. It can
+ // still be synthesizable or load-hoisted, but these kind of instructions
+ // are not directly copied in code-generation.
+ auto VirtDef =
+ VirtualUse::create(S, Stmt, Stmt->getSurroundingLoop(), &Inst, true);
+ assert(VirtDef.getKind() == VirtualUse::Synthesizable ||
+ VirtDef.getKind() == VirtualUse::Intra ||
+ VirtDef.getKind() == VirtualUse::Hoisted);
+ }
+ }
+
+ if (S->hasSingleExitEdge())
+ return;
+
+ // PHINodes in the SCoP region's exit block are also uses to be checked.
+ if (!S->getRegion().isTopLevelRegion()) {
+ for (auto &Inst : *S->getRegion().getExit()) {
+ if (!isa<PHINode>(Inst))
+ break;
+
+ for (auto &Op : Inst.operands())
+ verifyUse(S, Op, LI);
+ }
+ }
+}
+#endif
+
+void ScopBuilder::buildScop(Region &R, AssumptionCache &AC) {
+ scop.reset(new Scop(R, SE, LI, DT, *SD.getDetectionContext(&R), ORE,
+ SD.getNextID()));
+
+ buildStmts(R);
+
+ // Create all invariant load instructions first. These are categorized as
+ // 'synthesizable', therefore are not part of any ScopStmt but need to be
+ // created somewhere.
+ const InvariantLoadsSetTy &RIL = scop->getRequiredInvariantLoads();
+ for (BasicBlock *BB : scop->getRegion().blocks()) {
+ if (SD.isErrorBlock(*BB, scop->getRegion()))
+ continue;
+
+ for (Instruction &Inst : *BB) {
+ LoadInst *Load = dyn_cast<LoadInst>(&Inst);
+ if (!Load)
+ continue;
+
+ if (!RIL.count(Load))
+ continue;
+
+ // Invariant loads require a MemoryAccess to be created in some statement.
+ // It is not important to which statement the MemoryAccess is added
+ // because it will later be removed from the ScopStmt again. We chose the
+ // first statement of the basic block the LoadInst is in.
+ ArrayRef<ScopStmt *> List = scop->getStmtListFor(BB);
+ assert(!List.empty());
+ ScopStmt *RILStmt = List.front();
+ buildMemoryAccess(Load, RILStmt);
+ }
+ }
+ buildAccessFunctions();
+
+ // In case the region does not have an exiting block we will later (during
+ // code generation) split the exit block. This will move potential PHI nodes
+ // from the current exit block into the new region exiting block. Hence, PHI
+ // nodes that are at this point not part of the region will be.
+ // To handle these PHI nodes later we will now model their operands as scalar
+ // accesses. Note that we do not model anything in the exit block if we have
+ // an exiting block in the region, as there will not be any splitting later.
+ if (!R.isTopLevelRegion() && !scop->hasSingleExitEdge()) {
+ for (Instruction &Inst : *R.getExit()) {
+ PHINode *PHI = dyn_cast<PHINode>(&Inst);
+ if (!PHI)
+ break;
+
+ buildPHIAccesses(nullptr, PHI, nullptr, true);
+ }
+ }
+
+ // Create memory accesses for global reads since all arrays are now known.
+ auto *AF = SE.getConstant(IntegerType::getInt64Ty(SE.getContext()), 0);
+ for (auto GlobalReadPair : GlobalReads) {
+ ScopStmt *GlobalReadStmt = GlobalReadPair.first;
+ Instruction *GlobalRead = GlobalReadPair.second;
+ for (auto *BP : ArrayBasePointers)
+ addArrayAccess(GlobalReadStmt, MemAccInst(GlobalRead), MemoryAccess::READ,
+ BP, BP->getType(), false, {AF}, {nullptr}, GlobalRead);
+ }
+
+ buildInvariantEquivalenceClasses();
+
+ /// A map from basic blocks to their invalid domains.
+ DenseMap<BasicBlock *, isl::set> InvalidDomainMap;
+
+ if (!buildDomains(&R, InvalidDomainMap)) {
+ LLVM_DEBUG(
+ dbgs() << "Bailing-out because buildDomains encountered problems\n");
+ return;
+ }
+
+ addUserAssumptions(AC, InvalidDomainMap);
+
+ // Initialize the invalid domain.
+ for (ScopStmt &Stmt : scop->Stmts)
+ if (Stmt.isBlockStmt())
+ Stmt.setInvalidDomain(InvalidDomainMap[Stmt.getEntryBlock()]);
+ else
+ Stmt.setInvalidDomain(InvalidDomainMap[getRegionNodeBasicBlock(
+ Stmt.getRegion()->getNode())]);
+
+ // Remove empty statements.
+ // Exit early in case there are no executable statements left in this scop.
+ scop->removeStmtNotInDomainMap();
+ scop->simplifySCoP(false);
+ if (scop->isEmpty()) {
+ LLVM_DEBUG(dbgs() << "Bailing-out because SCoP is empty\n");
+ return;
+ }
+
+ // The ScopStmts now have enough information to initialize themselves.
+ for (ScopStmt &Stmt : *scop) {
+ collectSurroundingLoops(Stmt);
+
+ buildDomain(Stmt);
+ buildAccessRelations(Stmt);
+
+ if (DetectReductions)
+ checkForReductions(Stmt);
+ }
+
+ // Check early for a feasible runtime context.
+ if (!scop->hasFeasibleRuntimeContext()) {
+ LLVM_DEBUG(dbgs() << "Bailing-out because of unfeasible context (early)\n");
+ return;
+ }
+
+ // Check early for profitability. Afterwards it cannot change anymore,
+ // only the runtime context could become infeasible.
+ if (!scop->isProfitable(UnprofitableScalarAccs)) {
+ scop->invalidate(PROFITABLE, DebugLoc());
+ LLVM_DEBUG(
+ dbgs() << "Bailing-out because SCoP is not considered profitable\n");
+ return;
+ }
+
+ buildSchedule();
+
+ finalizeAccesses();
+
+ scop->realignParams();
+ addUserContext();
+
+ // After the context was fully constructed, thus all our knowledge about
+ // the parameters is in there, we add all recorded assumptions to the
+ // assumed/invalid context.
+ addRecordedAssumptions();
+
+ scop->simplifyContexts();
+ if (!buildAliasChecks()) {
+ LLVM_DEBUG(dbgs() << "Bailing-out because could not build alias checks\n");
+ return;
+ }
+
+ hoistInvariantLoads();
+ canonicalizeDynamicBasePtrs();
+ verifyInvariantLoads();
+ scop->simplifySCoP(true);
+
+ // Check late for a feasible runtime context because profitability did not
+ // change.
+ if (!scop->hasFeasibleRuntimeContext()) {
+ LLVM_DEBUG(dbgs() << "Bailing-out because of unfeasible context (late)\n");
+ return;
+ }
+
+#ifndef NDEBUG
+ verifyUses(scop.get(), LI, DT);
+#endif
+}
+
+ScopBuilder::ScopBuilder(Region *R, AssumptionCache &AC, AliasAnalysis &AA,
+ const DataLayout &DL, DominatorTree &DT, LoopInfo &LI,
+ ScopDetection &SD, ScalarEvolution &SE,
+ OptimizationRemarkEmitter &ORE)
+ : AA(AA), DL(DL), DT(DT), LI(LI), SD(SD), SE(SE), ORE(ORE) {
+ DebugLoc Beg, End;
+ auto P = getBBPairForRegion(R);
+ getDebugLocations(P, Beg, End);
+
+ std::string Msg = "SCoP begins here.";
+ ORE.emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "ScopEntry", Beg, P.first)
+ << Msg);
+
+ buildScop(*R, AC);
+
+ LLVM_DEBUG(dbgs() << *scop);
+
+ if (!scop->hasFeasibleRuntimeContext()) {
+ InfeasibleScops++;
+ Msg = "SCoP ends here but was dismissed.";
+ LLVM_DEBUG(dbgs() << "SCoP detected but dismissed\n");
+ RecordedAssumptions.clear();
+ scop.reset();
+ } else {
+ Msg = "SCoP ends here.";
+ ++ScopFound;
+ if (scop->getMaxLoopDepth() > 0)
+ ++RichScopFound;
+ }
+
+ if (R->isTopLevelRegion())
+ ORE.emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "ScopEnd", End, P.first)
+ << Msg);
+ else
+ ORE.emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "ScopEnd", End, P.second)
+ << Msg);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopDetection.cpp b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopDetection.cpp
new file mode 100644
index 00000000000..8b3af67a596
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopDetection.cpp
@@ -0,0 +1,2044 @@
+//===- ScopDetection.cpp - Detect Scops -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Detect the maximal Scops of a function.
+//
+// A static control part (Scop) is a subgraph of the control flow graph (CFG)
+// that only has statically known control flow and can therefore be described
+// within the polyhedral model.
+//
+// Every Scop fulfills these restrictions:
+//
+// * It is a single entry single exit region
+//
+// * Only affine linear bounds in the loops
+//
+// Every natural loop in a Scop must have a number of loop iterations that can
+// be described as an affine linear function in surrounding loop iterators or
+// parameters. (A parameter is a scalar that does not change its value during
+// execution of the Scop).
+//
+// * Only comparisons of affine linear expressions in conditions
+//
+// * All loops and conditions perfectly nested
+//
+// The control flow needs to be structured such that it could be written using
+// just 'for' and 'if' statements, without the need for any 'goto', 'break' or
+// 'continue'.
+//
+// * Side effect free functions call
+//
+// Function calls and intrinsics that do not have side effects (readnone)
+// or memory intrinsics (memset, memcpy, memmove) are allowed.
+//
+// The Scop detection finds the largest Scops by checking if the largest
+// region is a Scop. If this is not the case, its canonical subregions are
+// checked until a region is a Scop. It is now tried to extend this Scop by
+// creating a larger non canonical region.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/ScopDetection.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "polly/ScopDetectionDiagnostic.h"
+#include "polly/Support/SCEVValidator.h"
+#include "polly/Support/ScopHelper.h"
+#include "polly/Support/ScopLocation.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/Analysis/Delinearization.h"
+#include "llvm/Analysis/Loads.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/DebugLoc.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/Value.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <memory>
+#include <stack>
+#include <string>
+#include <utility>
+#include <vector>
+
+using namespace llvm;
+using namespace polly;
+
+#define DEBUG_TYPE "polly-detect"
+
+// This option is set to a very high value, as analyzing such loops increases
+// compile time on several cases. For experiments that enable this option,
+// a value of around 40 has been working to avoid run-time regressions with
+// Polly while still exposing interesting optimization opportunities.
+static cl::opt<int> ProfitabilityMinPerLoopInstructions(
+ "polly-detect-profitability-min-per-loop-insts",
+ cl::desc("The minimal number of per-loop instructions before a single loop "
+ "region is considered profitable"),
+ cl::Hidden, cl::ValueRequired, cl::init(100000000), cl::cat(PollyCategory));
+
+bool polly::PollyProcessUnprofitable;
+
+static cl::opt<bool, true> XPollyProcessUnprofitable(
+ "polly-process-unprofitable",
+ cl::desc(
+ "Process scops that are unlikely to benefit from Polly optimizations."),
+ cl::location(PollyProcessUnprofitable), cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::list<std::string> OnlyFunctions(
+ "polly-only-func",
+ cl::desc("Only run on functions that match a regex. "
+ "Multiple regexes can be comma separated. "
+ "Scop detection will run on all functions that match "
+ "ANY of the regexes provided."),
+ cl::ZeroOrMore, cl::CommaSeparated, cl::cat(PollyCategory));
+
+static cl::list<std::string> IgnoredFunctions(
+ "polly-ignore-func",
+ cl::desc("Ignore functions that match a regex. "
+ "Multiple regexes can be comma separated. "
+ "Scop detection will ignore all functions that match "
+ "ANY of the regexes provided."),
+ cl::ZeroOrMore, cl::CommaSeparated, cl::cat(PollyCategory));
+
+bool polly::PollyAllowFullFunction;
+
+static cl::opt<bool, true>
+ XAllowFullFunction("polly-detect-full-functions",
+ cl::desc("Allow the detection of full functions"),
+ cl::location(polly::PollyAllowFullFunction),
+ cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<std::string> OnlyRegion(
+ "polly-only-region",
+ cl::desc("Only run on certain regions (The provided identifier must "
+ "appear in the name of the region's entry block"),
+ cl::value_desc("identifier"), cl::ValueRequired, cl::init(""),
+ cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ IgnoreAliasing("polly-ignore-aliasing",
+ cl::desc("Ignore possible aliasing of the array bases"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+bool polly::PollyAllowUnsignedOperations;
+
+static cl::opt<bool, true> XPollyAllowUnsignedOperations(
+ "polly-allow-unsigned-operations",
+ cl::desc("Allow unsigned operations such as comparisons or zero-extends."),
+ cl::location(PollyAllowUnsignedOperations), cl::Hidden, cl::ZeroOrMore,
+ cl::init(true), cl::cat(PollyCategory));
+
+bool polly::PollyUseRuntimeAliasChecks;
+
+static cl::opt<bool, true> XPollyUseRuntimeAliasChecks(
+ "polly-use-runtime-alias-checks",
+ cl::desc("Use runtime alias checks to resolve possible aliasing."),
+ cl::location(PollyUseRuntimeAliasChecks), cl::Hidden, cl::ZeroOrMore,
+ cl::init(true), cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ ReportLevel("polly-report",
+ cl::desc("Print information about the activities of Polly"),
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> AllowDifferentTypes(
+ "polly-allow-differing-element-types",
+ cl::desc("Allow different element types for array accesses"), cl::Hidden,
+ cl::init(true), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ AllowNonAffine("polly-allow-nonaffine",
+ cl::desc("Allow non affine access functions in arrays"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ AllowModrefCall("polly-allow-modref-calls",
+ cl::desc("Allow functions with known modref behavior"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool> AllowNonAffineSubRegions(
+ "polly-allow-nonaffine-branches",
+ cl::desc("Allow non affine conditions for branches"), cl::Hidden,
+ cl::init(true), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ AllowNonAffineSubLoops("polly-allow-nonaffine-loops",
+ cl::desc("Allow non affine conditions for loops"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool, true>
+ TrackFailures("polly-detect-track-failures",
+ cl::desc("Track failure strings in detecting scop regions"),
+ cl::location(PollyTrackFailures), cl::Hidden, cl::ZeroOrMore,
+ cl::init(true), cl::cat(PollyCategory));
+
+static cl::opt<bool> KeepGoing("polly-detect-keep-going",
+ cl::desc("Do not fail on the first error."),
+ cl::Hidden, cl::ZeroOrMore, cl::init(false),
+ cl::cat(PollyCategory));
+
+static cl::opt<bool, true>
+ PollyDelinearizeX("polly-delinearize",
+ cl::desc("Delinearize array access functions"),
+ cl::location(PollyDelinearize), cl::Hidden,
+ cl::ZeroOrMore, cl::init(true), cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ VerifyScops("polly-detect-verify",
+ cl::desc("Verify the detected SCoPs after each transformation"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+bool polly::PollyInvariantLoadHoisting;
+
+static cl::opt<bool, true> XPollyInvariantLoadHoisting(
+ "polly-invariant-load-hoisting", cl::desc("Hoist invariant loads."),
+ cl::location(PollyInvariantLoadHoisting), cl::Hidden, cl::ZeroOrMore,
+ cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<bool> PollyAllowErrorBlocks(
+ "polly-allow-error-blocks",
+ cl::desc("Allow to speculate on the execution of 'error blocks'."),
+ cl::Hidden, cl::init(true), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+/// The minimal trip count under which loops are considered unprofitable.
+static const unsigned MIN_LOOP_TRIP_COUNT = 8;
+
+bool polly::PollyTrackFailures = false;
+bool polly::PollyDelinearize = false;
+StringRef polly::PollySkipFnAttr = "polly.skip.fn";
+
+//===----------------------------------------------------------------------===//
+// Statistics.
+
+STATISTIC(NumScopRegions, "Number of scops");
+STATISTIC(NumLoopsInScop, "Number of loops in scops");
+STATISTIC(NumScopsDepthZero, "Number of scops with maximal loop depth 0");
+STATISTIC(NumScopsDepthOne, "Number of scops with maximal loop depth 1");
+STATISTIC(NumScopsDepthTwo, "Number of scops with maximal loop depth 2");
+STATISTIC(NumScopsDepthThree, "Number of scops with maximal loop depth 3");
+STATISTIC(NumScopsDepthFour, "Number of scops with maximal loop depth 4");
+STATISTIC(NumScopsDepthFive, "Number of scops with maximal loop depth 5");
+STATISTIC(NumScopsDepthLarger,
+ "Number of scops with maximal loop depth 6 and larger");
+STATISTIC(NumProfScopRegions, "Number of scops (profitable scops only)");
+STATISTIC(NumLoopsInProfScop,
+ "Number of loops in scops (profitable scops only)");
+STATISTIC(NumLoopsOverall, "Number of total loops");
+STATISTIC(NumProfScopsDepthZero,
+ "Number of scops with maximal loop depth 0 (profitable scops only)");
+STATISTIC(NumProfScopsDepthOne,
+ "Number of scops with maximal loop depth 1 (profitable scops only)");
+STATISTIC(NumProfScopsDepthTwo,
+ "Number of scops with maximal loop depth 2 (profitable scops only)");
+STATISTIC(NumProfScopsDepthThree,
+ "Number of scops with maximal loop depth 3 (profitable scops only)");
+STATISTIC(NumProfScopsDepthFour,
+ "Number of scops with maximal loop depth 4 (profitable scops only)");
+STATISTIC(NumProfScopsDepthFive,
+ "Number of scops with maximal loop depth 5 (profitable scops only)");
+STATISTIC(NumProfScopsDepthLarger,
+ "Number of scops with maximal loop depth 6 and larger "
+ "(profitable scops only)");
+STATISTIC(MaxNumLoopsInScop, "Maximal number of loops in scops");
+STATISTIC(MaxNumLoopsInProfScop,
+ "Maximal number of loops in scops (profitable scops only)");
+
+static void updateLoopCountStatistic(ScopDetection::LoopStats Stats,
+ bool OnlyProfitable);
+
+namespace {
+
+class DiagnosticScopFound : public DiagnosticInfo {
+private:
+ static int PluginDiagnosticKind;
+
+ Function &F;
+ std::string FileName;
+ unsigned EntryLine, ExitLine;
+
+public:
+ DiagnosticScopFound(Function &F, std::string FileName, unsigned EntryLine,
+ unsigned ExitLine)
+ : DiagnosticInfo(PluginDiagnosticKind, DS_Note), F(F), FileName(FileName),
+ EntryLine(EntryLine), ExitLine(ExitLine) {}
+
+ void print(DiagnosticPrinter &DP) const override;
+
+ static bool classof(const DiagnosticInfo *DI) {
+ return DI->getKind() == PluginDiagnosticKind;
+ }
+};
+} // namespace
+
+int DiagnosticScopFound::PluginDiagnosticKind =
+ getNextAvailablePluginDiagnosticKind();
+
+void DiagnosticScopFound::print(DiagnosticPrinter &DP) const {
+ DP << "Polly detected an optimizable loop region (scop) in function '" << F
+ << "'\n";
+
+ if (FileName.empty()) {
+ DP << "Scop location is unknown. Compile with debug info "
+ "(-g) to get more precise information. ";
+ return;
+ }
+
+ DP << FileName << ":" << EntryLine << ": Start of scop\n";
+ DP << FileName << ":" << ExitLine << ": End of scop";
+}
+
+/// Check if a string matches any regex in a list of regexes.
+/// @param Str the input string to match against.
+/// @param RegexList a list of strings that are regular expressions.
+static bool doesStringMatchAnyRegex(StringRef Str,
+ const cl::list<std::string> &RegexList) {
+ for (auto RegexStr : RegexList) {
+ Regex R(RegexStr);
+
+ std::string Err;
+ if (!R.isValid(Err))
+ report_fatal_error(Twine("invalid regex given as input to polly: ") + Err,
+ true);
+
+ if (R.match(Str))
+ return true;
+ }
+ return false;
+}
+//===----------------------------------------------------------------------===//
+// ScopDetection.
+
+ScopDetection::ScopDetection(const DominatorTree &DT, ScalarEvolution &SE,
+ LoopInfo &LI, RegionInfo &RI, AliasAnalysis &AA,
+ OptimizationRemarkEmitter &ORE)
+ : DT(DT), SE(SE), LI(LI), RI(RI), AA(AA), ORE(ORE) {}
+
+void ScopDetection::detect(Function &F) {
+ assert(ValidRegions.empty() && "Detection must run only once");
+
+ if (!PollyProcessUnprofitable && LI.empty())
+ return;
+
+ Region *TopRegion = RI.getTopLevelRegion();
+
+ if (!OnlyFunctions.empty() &&
+ !doesStringMatchAnyRegex(F.getName(), OnlyFunctions))
+ return;
+
+ if (doesStringMatchAnyRegex(F.getName(), IgnoredFunctions))
+ return;
+
+ if (!isValidFunction(F))
+ return;
+
+ findScops(*TopRegion);
+
+ NumScopRegions += ValidRegions.size();
+
+ // Prune non-profitable regions.
+ for (auto &DIt : DetectionContextMap) {
+ DetectionContext &DC = *DIt.getSecond().get();
+ if (DC.Log.hasErrors())
+ continue;
+ if (!ValidRegions.count(&DC.CurRegion))
+ continue;
+ LoopStats Stats = countBeneficialLoops(&DC.CurRegion, SE, LI, 0);
+ updateLoopCountStatistic(Stats, false /* OnlyProfitable */);
+ if (isProfitableRegion(DC)) {
+ updateLoopCountStatistic(Stats, true /* OnlyProfitable */);
+ continue;
+ }
+
+ ValidRegions.remove(&DC.CurRegion);
+ }
+
+ NumProfScopRegions += ValidRegions.size();
+ NumLoopsOverall += countBeneficialLoops(TopRegion, SE, LI, 0).NumLoops;
+
+ // Only makes sense when we tracked errors.
+ if (PollyTrackFailures)
+ emitMissedRemarks(F);
+
+ if (ReportLevel)
+ printLocations(F);
+
+ assert(ValidRegions.size() <= DetectionContextMap.size() &&
+ "Cached more results than valid regions");
+}
+
+template <class RR, typename... Args>
+inline bool ScopDetection::invalid(DetectionContext &Context, bool Assert,
+ Args &&...Arguments) const {
+ if (!Context.Verifying) {
+ RejectLog &Log = Context.Log;
+ std::shared_ptr<RR> RejectReason = std::make_shared<RR>(Arguments...);
+
+ if (PollyTrackFailures)
+ Log.report(RejectReason);
+
+ LLVM_DEBUG(dbgs() << RejectReason->getMessage());
+ LLVM_DEBUG(dbgs() << "\n");
+ } else {
+ assert(!Assert && "Verification of detected scop failed");
+ }
+
+ return false;
+}
+
+bool ScopDetection::isMaxRegionInScop(const Region &R, bool Verify) {
+ if (!ValidRegions.count(&R))
+ return false;
+
+ if (Verify) {
+ BBPair P = getBBPairForRegion(&R);
+ std::unique_ptr<DetectionContext> &Entry = DetectionContextMap[P];
+
+ // Free previous DetectionContext for the region and create and verify a new
+ // one. Be sure that the DetectionContext is not still used by a ScopInfop.
+ // Due to changes but CodeGeneration of another Scop, the Region object and
+ // the BBPair might not match anymore.
+ Entry = std::make_unique<DetectionContext>(const_cast<Region &>(R), AA,
+ /*Verifying=*/false);
+
+ return isValidRegion(*Entry.get());
+ }
+
+ return true;
+}
+
+std::string ScopDetection::regionIsInvalidBecause(const Region *R) const {
+ // Get the first error we found. Even in keep-going mode, this is the first
+ // reason that caused the candidate to be rejected.
+ auto *Log = lookupRejectionLog(R);
+
+ // This can happen when we marked a region invalid, but didn't track
+ // an error for it.
+ if (!Log || !Log->hasErrors())
+ return "";
+
+ RejectReasonPtr RR = *Log->begin();
+ return RR->getMessage();
+}
+
+bool ScopDetection::addOverApproximatedRegion(Region *AR,
+ DetectionContext &Context) const {
+ // If we already know about Ar we can exit.
+ if (!Context.NonAffineSubRegionSet.insert(AR))
+ return true;
+
+ // All loops in the region have to be overapproximated too if there
+ // are accesses that depend on the iteration count.
+
+ for (BasicBlock *BB : AR->blocks()) {
+ Loop *L = LI.getLoopFor(BB);
+ if (AR->contains(L))
+ Context.BoxedLoopsSet.insert(L);
+ }
+
+ return (AllowNonAffineSubLoops || Context.BoxedLoopsSet.empty());
+}
+
+bool ScopDetection::onlyValidRequiredInvariantLoads(
+ InvariantLoadsSetTy &RequiredILS, DetectionContext &Context) const {
+ Region &CurRegion = Context.CurRegion;
+ const DataLayout &DL = CurRegion.getEntry()->getModule()->getDataLayout();
+
+ if (!PollyInvariantLoadHoisting && !RequiredILS.empty())
+ return false;
+
+ for (LoadInst *Load : RequiredILS) {
+ // If we already know a load has been accepted as required invariant, we
+ // already run the validation below once and consequently don't need to
+ // run it again. Hence, we return early. For certain test cases (e.g.,
+ // COSMO this avoids us spending 50% of scop-detection time in this
+ // very function (and its children).
+ if (Context.RequiredILS.count(Load))
+ continue;
+ if (!isHoistableLoad(Load, CurRegion, LI, SE, DT, Context.RequiredILS))
+ return false;
+
+ for (auto NonAffineRegion : Context.NonAffineSubRegionSet) {
+ if (isSafeToLoadUnconditionally(Load->getPointerOperand(),
+ Load->getType(), Load->getAlign(), DL))
+ continue;
+
+ if (NonAffineRegion->contains(Load) &&
+ Load->getParent() != NonAffineRegion->getEntry())
+ return false;
+ }
+ }
+
+ Context.RequiredILS.insert(RequiredILS.begin(), RequiredILS.end());
+
+ return true;
+}
+
+bool ScopDetection::involvesMultiplePtrs(const SCEV *S0, const SCEV *S1,
+ Loop *Scope) const {
+ SetVector<Value *> Values;
+ findValues(S0, SE, Values);
+ if (S1)
+ findValues(S1, SE, Values);
+
+ SmallPtrSet<Value *, 8> PtrVals;
+ for (auto *V : Values) {
+ if (auto *P2I = dyn_cast<PtrToIntInst>(V))
+ V = P2I->getOperand(0);
+
+ if (!V->getType()->isPointerTy())
+ continue;
+
+ auto *PtrSCEV = SE.getSCEVAtScope(V, Scope);
+ if (isa<SCEVConstant>(PtrSCEV))
+ continue;
+
+ auto *BasePtr = dyn_cast<SCEVUnknown>(SE.getPointerBase(PtrSCEV));
+ if (!BasePtr)
+ return true;
+
+ auto *BasePtrVal = BasePtr->getValue();
+ if (PtrVals.insert(BasePtrVal).second) {
+ for (auto *PtrVal : PtrVals)
+ if (PtrVal != BasePtrVal && !AA.isNoAlias(PtrVal, BasePtrVal))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ScopDetection::isAffine(const SCEV *S, Loop *Scope,
+ DetectionContext &Context) const {
+ InvariantLoadsSetTy AccessILS;
+ if (!isAffineExpr(&Context.CurRegion, Scope, S, SE, &AccessILS))
+ return false;
+
+ if (!onlyValidRequiredInvariantLoads(AccessILS, Context))
+ return false;
+
+ return true;
+}
+
+bool ScopDetection::isValidSwitch(BasicBlock &BB, SwitchInst *SI,
+ Value *Condition, bool IsLoopBranch,
+ DetectionContext &Context) const {
+ Loop *L = LI.getLoopFor(&BB);
+ const SCEV *ConditionSCEV = SE.getSCEVAtScope(Condition, L);
+
+ if (IsLoopBranch && L->isLoopLatch(&BB))
+ return false;
+
+ // Check for invalid usage of different pointers in one expression.
+ if (involvesMultiplePtrs(ConditionSCEV, nullptr, L))
+ return false;
+
+ if (isAffine(ConditionSCEV, L, Context))
+ return true;
+
+ if (AllowNonAffineSubRegions &&
+ addOverApproximatedRegion(RI.getRegionFor(&BB), Context))
+ return true;
+
+ return invalid<ReportNonAffBranch>(Context, /*Assert=*/true, &BB,
+ ConditionSCEV, ConditionSCEV, SI);
+}
+
+bool ScopDetection::isValidBranch(BasicBlock &BB, BranchInst *BI,
+ Value *Condition, bool IsLoopBranch,
+ DetectionContext &Context) {
+ // Constant integer conditions are always affine.
+ if (isa<ConstantInt>(Condition))
+ return true;
+
+ if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(Condition)) {
+ auto Opcode = BinOp->getOpcode();
+ if (Opcode == Instruction::And || Opcode == Instruction::Or) {
+ Value *Op0 = BinOp->getOperand(0);
+ Value *Op1 = BinOp->getOperand(1);
+ return isValidBranch(BB, BI, Op0, IsLoopBranch, Context) &&
+ isValidBranch(BB, BI, Op1, IsLoopBranch, Context);
+ }
+ }
+
+ if (auto PHI = dyn_cast<PHINode>(Condition)) {
+ auto *Unique = dyn_cast_or_null<ConstantInt>(
+ getUniqueNonErrorValue(PHI, &Context.CurRegion, this));
+ if (Unique && (Unique->isZero() || Unique->isOne()))
+ return true;
+ }
+
+ if (auto Load = dyn_cast<LoadInst>(Condition))
+ if (!IsLoopBranch && Context.CurRegion.contains(Load)) {
+ Context.RequiredILS.insert(Load);
+ return true;
+ }
+
+ // Non constant conditions of branches need to be ICmpInst.
+ if (!isa<ICmpInst>(Condition)) {
+ if (!IsLoopBranch && AllowNonAffineSubRegions &&
+ addOverApproximatedRegion(RI.getRegionFor(&BB), Context))
+ return true;
+ return invalid<ReportInvalidCond>(Context, /*Assert=*/true, BI, &BB);
+ }
+
+ ICmpInst *ICmp = cast<ICmpInst>(Condition);
+
+ // Are both operands of the ICmp affine?
+ if (isa<UndefValue>(ICmp->getOperand(0)) ||
+ isa<UndefValue>(ICmp->getOperand(1)))
+ return invalid<ReportUndefOperand>(Context, /*Assert=*/true, &BB, ICmp);
+
+ Loop *L = LI.getLoopFor(&BB);
+ const SCEV *LHS = SE.getSCEVAtScope(ICmp->getOperand(0), L);
+ const SCEV *RHS = SE.getSCEVAtScope(ICmp->getOperand(1), L);
+
+ LHS = tryForwardThroughPHI(LHS, Context.CurRegion, SE, this);
+ RHS = tryForwardThroughPHI(RHS, Context.CurRegion, SE, this);
+
+ // If unsigned operations are not allowed try to approximate the region.
+ if (ICmp->isUnsigned() && !PollyAllowUnsignedOperations)
+ return !IsLoopBranch && AllowNonAffineSubRegions &&
+ addOverApproximatedRegion(RI.getRegionFor(&BB), Context);
+
+ // Check for invalid usage of different pointers in one expression.
+ if (ICmp->isEquality() && involvesMultiplePtrs(LHS, nullptr, L) &&
+ involvesMultiplePtrs(RHS, nullptr, L))
+ return false;
+
+ // Check for invalid usage of different pointers in a relational comparison.
+ if (ICmp->isRelational() && involvesMultiplePtrs(LHS, RHS, L))
+ return false;
+
+ if (isAffine(LHS, L, Context) && isAffine(RHS, L, Context))
+ return true;
+
+ if (!IsLoopBranch && AllowNonAffineSubRegions &&
+ addOverApproximatedRegion(RI.getRegionFor(&BB), Context))
+ return true;
+
+ if (IsLoopBranch)
+ return false;
+
+ return invalid<ReportNonAffBranch>(Context, /*Assert=*/true, &BB, LHS, RHS,
+ ICmp);
+}
+
+bool ScopDetection::isValidCFG(BasicBlock &BB, bool IsLoopBranch,
+ bool AllowUnreachable,
+ DetectionContext &Context) {
+ Region &CurRegion = Context.CurRegion;
+
+ Instruction *TI = BB.getTerminator();
+
+ if (AllowUnreachable && isa<UnreachableInst>(TI))
+ return true;
+
+ // Return instructions are only valid if the region is the top level region.
+ if (isa<ReturnInst>(TI) && CurRegion.isTopLevelRegion())
+ return true;
+
+ Value *Condition = getConditionFromTerminator(TI);
+
+ if (!Condition)
+ return invalid<ReportInvalidTerminator>(Context, /*Assert=*/true, &BB);
+
+ // UndefValue is not allowed as condition.
+ if (isa<UndefValue>(Condition))
+ return invalid<ReportUndefCond>(Context, /*Assert=*/true, TI, &BB);
+
+ if (BranchInst *BI = dyn_cast<BranchInst>(TI))
+ return isValidBranch(BB, BI, Condition, IsLoopBranch, Context);
+
+ SwitchInst *SI = dyn_cast<SwitchInst>(TI);
+ assert(SI && "Terminator was neither branch nor switch");
+
+ return isValidSwitch(BB, SI, Condition, IsLoopBranch, Context);
+}
+
+bool ScopDetection::isValidCallInst(CallInst &CI,
+ DetectionContext &Context) const {
+ if (CI.doesNotReturn())
+ return false;
+
+ if (CI.doesNotAccessMemory())
+ return true;
+
+ if (auto *II = dyn_cast<IntrinsicInst>(&CI))
+ if (isValidIntrinsicInst(*II, Context))
+ return true;
+
+ Function *CalledFunction = CI.getCalledFunction();
+
+ // Indirect calls are not supported.
+ if (CalledFunction == nullptr)
+ return false;
+
+ if (isDebugCall(&CI)) {
+ LLVM_DEBUG(dbgs() << "Allow call to debug function: "
+ << CalledFunction->getName() << '\n');
+ return true;
+ }
+
+ if (AllowModrefCall) {
+ switch (AA.getModRefBehavior(CalledFunction)) {
+ case FMRB_UnknownModRefBehavior:
+ return false;
+ case FMRB_DoesNotAccessMemory:
+ case FMRB_OnlyReadsMemory:
+ case FMRB_OnlyReadsInaccessibleMem:
+ case FMRB_OnlyReadsInaccessibleOrArgMem:
+ // Implicitly disable delinearization since we have an unknown
+ // accesses with an unknown access function.
+ Context.HasUnknownAccess = true;
+ // Explicitly use addUnknown so we don't put a loop-variant
+ // pointer into the alias set.
+ Context.AST.addUnknown(&CI);
+ return true;
+ case FMRB_OnlyReadsArgumentPointees:
+ case FMRB_OnlyAccessesArgumentPointees:
+ case FMRB_OnlyWritesArgumentPointees:
+ for (const auto &Arg : CI.args()) {
+ if (!Arg->getType()->isPointerTy())
+ continue;
+
+ // Bail if a pointer argument has a base address not known to
+ // ScalarEvolution. Note that a zero pointer is acceptable.
+ auto *ArgSCEV = SE.getSCEVAtScope(Arg, LI.getLoopFor(CI.getParent()));
+ if (ArgSCEV->isZero())
+ continue;
+
+ auto *BP = dyn_cast<SCEVUnknown>(SE.getPointerBase(ArgSCEV));
+ if (!BP)
+ return false;
+
+ // Implicitly disable delinearization since we have an unknown
+ // accesses with an unknown access function.
+ Context.HasUnknownAccess = true;
+ }
+
+ // Explicitly use addUnknown so we don't put a loop-variant
+ // pointer into the alias set.
+ Context.AST.addUnknown(&CI);
+ return true;
+ case FMRB_OnlyWritesMemory:
+ case FMRB_OnlyWritesInaccessibleMem:
+ case FMRB_OnlyWritesInaccessibleOrArgMem:
+ case FMRB_OnlyAccessesInaccessibleMem:
+ case FMRB_OnlyAccessesInaccessibleOrArgMem:
+ return false;
+ }
+ }
+
+ return false;
+}
+
+bool ScopDetection::isValidIntrinsicInst(IntrinsicInst &II,
+ DetectionContext &Context) const {
+ if (isIgnoredIntrinsic(&II))
+ return true;
+
+ // The closest loop surrounding the call instruction.
+ Loop *L = LI.getLoopFor(II.getParent());
+
+ // The access function and base pointer for memory intrinsics.
+ const SCEV *AF;
+ const SCEVUnknown *BP;
+
+ switch (II.getIntrinsicID()) {
+ // Memory intrinsics that can be represented are supported.
+ case Intrinsic::memmove:
+ case Intrinsic::memcpy:
+ AF = SE.getSCEVAtScope(cast<MemTransferInst>(II).getSource(), L);
+ if (!AF->isZero()) {
+ BP = dyn_cast<SCEVUnknown>(SE.getPointerBase(AF));
+ // Bail if the source pointer is not valid.
+ if (!isValidAccess(&II, AF, BP, Context))
+ return false;
+ }
+ LLVM_FALLTHROUGH;
+ case Intrinsic::memset:
+ AF = SE.getSCEVAtScope(cast<MemIntrinsic>(II).getDest(), L);
+ if (!AF->isZero()) {
+ BP = dyn_cast<SCEVUnknown>(SE.getPointerBase(AF));
+ // Bail if the destination pointer is not valid.
+ if (!isValidAccess(&II, AF, BP, Context))
+ return false;
+ }
+
+ // Bail if the length is not affine.
+ if (!isAffine(SE.getSCEVAtScope(cast<MemIntrinsic>(II).getLength(), L), L,
+ Context))
+ return false;
+
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool ScopDetection::isInvariant(Value &Val, const Region &Reg,
+ DetectionContext &Ctx) const {
+ // A reference to function argument or constant value is invariant.
+ if (isa<Argument>(Val) || isa<Constant>(Val))
+ return true;
+
+ Instruction *I = dyn_cast<Instruction>(&Val);
+ if (!I)
+ return false;
+
+ if (!Reg.contains(I))
+ return true;
+
+ // Loads within the SCoP may read arbitrary values, need to hoist them. If it
+ // is not hoistable, it will be rejected later, but here we assume it is and
+ // that makes the value invariant.
+ if (auto LI = dyn_cast<LoadInst>(I)) {
+ Ctx.RequiredILS.insert(LI);
+ return true;
+ }
+
+ return false;
+}
+
+namespace {
+
+/// Remove smax of smax(0, size) expressions from a SCEV expression and
+/// register the '...' components.
+///
+/// Array access expressions as they are generated by GFortran contain smax(0,
+/// size) expressions that confuse the 'normal' delinearization algorithm.
+/// However, if we extract such expressions before the normal delinearization
+/// takes place they can actually help to identify array size expressions in
+/// Fortran accesses. For the subsequently following delinearization the smax(0,
+/// size) component can be replaced by just 'size'. This is correct as we will
+/// always add and verify the assumption that for all subscript expressions
+/// 'exp' the inequality 0 <= exp < size holds. Hence, we will also verify
+/// that 0 <= size, which means smax(0, size) == size.
+class SCEVRemoveMax : public SCEVRewriteVisitor<SCEVRemoveMax> {
+public:
+ SCEVRemoveMax(ScalarEvolution &SE, std::vector<const SCEV *> *Terms)
+ : SCEVRewriteVisitor(SE), Terms(Terms) {}
+
+ static const SCEV *rewrite(const SCEV *Scev, ScalarEvolution &SE,
+ std::vector<const SCEV *> *Terms = nullptr) {
+ SCEVRemoveMax Rewriter(SE, Terms);
+ return Rewriter.visit(Scev);
+ }
+
+ const SCEV *visitSMaxExpr(const SCEVSMaxExpr *Expr) {
+ if ((Expr->getNumOperands() == 2) && Expr->getOperand(0)->isZero()) {
+ auto Res = visit(Expr->getOperand(1));
+ if (Terms)
+ (*Terms).push_back(Res);
+ return Res;
+ }
+
+ return Expr;
+ }
+
+private:
+ std::vector<const SCEV *> *Terms;
+};
+} // namespace
+
+SmallVector<const SCEV *, 4>
+ScopDetection::getDelinearizationTerms(DetectionContext &Context,
+ const SCEVUnknown *BasePointer) const {
+ SmallVector<const SCEV *, 4> Terms;
+ for (const auto &Pair : Context.Accesses[BasePointer]) {
+ std::vector<const SCEV *> MaxTerms;
+ SCEVRemoveMax::rewrite(Pair.second, SE, &MaxTerms);
+ if (!MaxTerms.empty()) {
+ Terms.insert(Terms.begin(), MaxTerms.begin(), MaxTerms.end());
+ continue;
+ }
+ // In case the outermost expression is a plain add, we check if any of its
+ // terms has the form 4 * %inst * %param * %param ..., aka a term that
+ // contains a product between a parameter and an instruction that is
+ // inside the scop. Such instructions, if allowed at all, are instructions
+ // SCEV can not represent, but Polly is still looking through. As a
+ // result, these instructions can depend on induction variables and are
+ // most likely no array sizes. However, terms that are multiplied with
+ // them are likely candidates for array sizes.
+ if (auto *AF = dyn_cast<SCEVAddExpr>(Pair.second)) {
+ for (auto Op : AF->operands()) {
+ if (auto *AF2 = dyn_cast<SCEVAddRecExpr>(Op))
+ collectParametricTerms(SE, AF2, Terms);
+ if (auto *AF2 = dyn_cast<SCEVMulExpr>(Op)) {
+ SmallVector<const SCEV *, 0> Operands;
+
+ for (auto *MulOp : AF2->operands()) {
+ if (auto *Const = dyn_cast<SCEVConstant>(MulOp))
+ Operands.push_back(Const);
+ if (auto *Unknown = dyn_cast<SCEVUnknown>(MulOp)) {
+ if (auto *Inst = dyn_cast<Instruction>(Unknown->getValue())) {
+ if (!Context.CurRegion.contains(Inst))
+ Operands.push_back(MulOp);
+
+ } else {
+ Operands.push_back(MulOp);
+ }
+ }
+ }
+ if (Operands.size())
+ Terms.push_back(SE.getMulExpr(Operands));
+ }
+ }
+ }
+ if (Terms.empty())
+ collectParametricTerms(SE, Pair.second, Terms);
+ }
+ return Terms;
+}
+
+bool ScopDetection::hasValidArraySizes(DetectionContext &Context,
+ SmallVectorImpl<const SCEV *> &Sizes,
+ const SCEVUnknown *BasePointer,
+ Loop *Scope) const {
+ // If no sizes were found, all sizes are trivially valid. We allow this case
+ // to make it possible to pass known-affine accesses to the delinearization to
+ // try to recover some interesting multi-dimensional accesses, but to still
+ // allow the already known to be affine access in case the delinearization
+ // fails. In such situations, the delinearization will just return a Sizes
+ // array of size zero.
+ if (Sizes.size() == 0)
+ return true;
+
+ Value *BaseValue = BasePointer->getValue();
+ Region &CurRegion = Context.CurRegion;
+ for (const SCEV *DelinearizedSize : Sizes) {
+ // Don't pass down the scope to isAfffine; array dimensions must be
+ // invariant across the entire scop.
+ if (!isAffine(DelinearizedSize, nullptr, Context)) {
+ Sizes.clear();
+ break;
+ }
+ if (auto *Unknown = dyn_cast<SCEVUnknown>(DelinearizedSize)) {
+ auto *V = dyn_cast<Value>(Unknown->getValue());
+ if (auto *Load = dyn_cast<LoadInst>(V)) {
+ if (Context.CurRegion.contains(Load) &&
+ isHoistableLoad(Load, CurRegion, LI, SE, DT, Context.RequiredILS))
+ Context.RequiredILS.insert(Load);
+ continue;
+ }
+ }
+ if (hasScalarDepsInsideRegion(DelinearizedSize, &CurRegion, Scope, false,
+ Context.RequiredILS))
+ return invalid<ReportNonAffineAccess>(
+ Context, /*Assert=*/true, DelinearizedSize,
+ Context.Accesses[BasePointer].front().first, BaseValue);
+ }
+
+ // No array shape derived.
+ if (Sizes.empty()) {
+ if (AllowNonAffine)
+ return true;
+
+ for (const auto &Pair : Context.Accesses[BasePointer]) {
+ const Instruction *Insn = Pair.first;
+ const SCEV *AF = Pair.second;
+
+ if (!isAffine(AF, Scope, Context)) {
+ invalid<ReportNonAffineAccess>(Context, /*Assert=*/true, AF, Insn,
+ BaseValue);
+ if (!KeepGoing)
+ return false;
+ }
+ }
+ return false;
+ }
+ return true;
+}
+
+// We first store the resulting memory accesses in TempMemoryAccesses. Only
+// if the access functions for all memory accesses have been successfully
+// delinearized we continue. Otherwise, we either report a failure or, if
+// non-affine accesses are allowed, we drop the information. In case the
+// information is dropped the memory accesses need to be overapproximated
+// when translated to a polyhedral representation.
+bool ScopDetection::computeAccessFunctions(
+ DetectionContext &Context, const SCEVUnknown *BasePointer,
+ std::shared_ptr<ArrayShape> Shape) const {
+ Value *BaseValue = BasePointer->getValue();
+ bool BasePtrHasNonAffine = false;
+ MapInsnToMemAcc TempMemoryAccesses;
+ for (const auto &Pair : Context.Accesses[BasePointer]) {
+ const Instruction *Insn = Pair.first;
+ auto *AF = Pair.second;
+ AF = SCEVRemoveMax::rewrite(AF, SE);
+ bool IsNonAffine = false;
+ TempMemoryAccesses.insert(std::make_pair(Insn, MemAcc(Insn, Shape)));
+ MemAcc *Acc = &TempMemoryAccesses.find(Insn)->second;
+ auto *Scope = LI.getLoopFor(Insn->getParent());
+
+ if (!AF) {
+ if (isAffine(Pair.second, Scope, Context))
+ Acc->DelinearizedSubscripts.push_back(Pair.second);
+ else
+ IsNonAffine = true;
+ } else {
+ if (Shape->DelinearizedSizes.size() == 0) {
+ Acc->DelinearizedSubscripts.push_back(AF);
+ } else {
+ llvm::computeAccessFunctions(SE, AF, Acc->DelinearizedSubscripts,
+ Shape->DelinearizedSizes);
+ if (Acc->DelinearizedSubscripts.size() == 0)
+ IsNonAffine = true;
+ }
+ for (const SCEV *S : Acc->DelinearizedSubscripts)
+ if (!isAffine(S, Scope, Context))
+ IsNonAffine = true;
+ }
+
+ // (Possibly) report non affine access
+ if (IsNonAffine) {
+ BasePtrHasNonAffine = true;
+ if (!AllowNonAffine)
+ invalid<ReportNonAffineAccess>(Context, /*Assert=*/true, Pair.second,
+ Insn, BaseValue);
+ if (!KeepGoing && !AllowNonAffine)
+ return false;
+ }
+ }
+
+ if (!BasePtrHasNonAffine)
+ Context.InsnToMemAcc.insert(TempMemoryAccesses.begin(),
+ TempMemoryAccesses.end());
+
+ return true;
+}
+
+bool ScopDetection::hasBaseAffineAccesses(DetectionContext &Context,
+ const SCEVUnknown *BasePointer,
+ Loop *Scope) const {
+ auto Shape = std::shared_ptr<ArrayShape>(new ArrayShape(BasePointer));
+
+ auto Terms = getDelinearizationTerms(Context, BasePointer);
+
+ findArrayDimensions(SE, Terms, Shape->DelinearizedSizes,
+ Context.ElementSize[BasePointer]);
+
+ if (!hasValidArraySizes(Context, Shape->DelinearizedSizes, BasePointer,
+ Scope))
+ return false;
+
+ return computeAccessFunctions(Context, BasePointer, Shape);
+}
+
+bool ScopDetection::hasAffineMemoryAccesses(DetectionContext &Context) const {
+ // TODO: If we have an unknown access and other non-affine accesses we do
+ // not try to delinearize them for now.
+ if (Context.HasUnknownAccess && !Context.NonAffineAccesses.empty())
+ return AllowNonAffine;
+
+ for (auto &Pair : Context.NonAffineAccesses) {
+ auto *BasePointer = Pair.first;
+ auto *Scope = Pair.second;
+ if (!hasBaseAffineAccesses(Context, BasePointer, Scope)) {
+ if (KeepGoing)
+ continue;
+ else
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ScopDetection::isValidAccess(Instruction *Inst, const SCEV *AF,
+ const SCEVUnknown *BP,
+ DetectionContext &Context) const {
+
+ if (!BP)
+ return invalid<ReportNoBasePtr>(Context, /*Assert=*/true, Inst);
+
+ auto *BV = BP->getValue();
+ if (isa<UndefValue>(BV))
+ return invalid<ReportUndefBasePtr>(Context, /*Assert=*/true, Inst);
+
+ // FIXME: Think about allowing IntToPtrInst
+ if (IntToPtrInst *Inst = dyn_cast<IntToPtrInst>(BV))
+ return invalid<ReportIntToPtr>(Context, /*Assert=*/true, Inst);
+
+ // Check that the base address of the access is invariant in the current
+ // region.
+ if (!isInvariant(*BV, Context.CurRegion, Context))
+ return invalid<ReportVariantBasePtr>(Context, /*Assert=*/true, BV, Inst);
+
+ AF = SE.getMinusSCEV(AF, BP);
+
+ const SCEV *Size;
+ if (!isa<MemIntrinsic>(Inst)) {
+ Size = SE.getElementSize(Inst);
+ } else {
+ auto *SizeTy =
+ SE.getEffectiveSCEVType(PointerType::getInt8PtrTy(SE.getContext()));
+ Size = SE.getConstant(SizeTy, 8);
+ }
+
+ if (Context.ElementSize[BP]) {
+ if (!AllowDifferentTypes && Context.ElementSize[BP] != Size)
+ return invalid<ReportDifferentArrayElementSize>(Context, /*Assert=*/true,
+ Inst, BV);
+
+ Context.ElementSize[BP] = SE.getSMinExpr(Size, Context.ElementSize[BP]);
+ } else {
+ Context.ElementSize[BP] = Size;
+ }
+
+ bool IsVariantInNonAffineLoop = false;
+ SetVector<const Loop *> Loops;
+ findLoops(AF, Loops);
+ for (const Loop *L : Loops)
+ if (Context.BoxedLoopsSet.count(L))
+ IsVariantInNonAffineLoop = true;
+
+ auto *Scope = LI.getLoopFor(Inst->getParent());
+ bool IsAffine = !IsVariantInNonAffineLoop && isAffine(AF, Scope, Context);
+ // Do not try to delinearize memory intrinsics and force them to be affine.
+ if (isa<MemIntrinsic>(Inst) && !IsAffine) {
+ return invalid<ReportNonAffineAccess>(Context, /*Assert=*/true, AF, Inst,
+ BV);
+ } else if (PollyDelinearize && !IsVariantInNonAffineLoop) {
+ Context.Accesses[BP].push_back({Inst, AF});
+
+ if (!IsAffine)
+ Context.NonAffineAccesses.insert(
+ std::make_pair(BP, LI.getLoopFor(Inst->getParent())));
+ } else if (!AllowNonAffine && !IsAffine) {
+ return invalid<ReportNonAffineAccess>(Context, /*Assert=*/true, AF, Inst,
+ BV);
+ }
+
+ if (IgnoreAliasing)
+ return true;
+
+ // Check if the base pointer of the memory access does alias with
+ // any other pointer. This cannot be handled at the moment.
+ AAMDNodes AATags = Inst->getAAMetadata();
+ AliasSet &AS = Context.AST.getAliasSetFor(
+ MemoryLocation::getBeforeOrAfter(BP->getValue(), AATags));
+
+ if (!AS.isMustAlias()) {
+ if (PollyUseRuntimeAliasChecks) {
+ bool CanBuildRunTimeCheck = true;
+ // The run-time alias check places code that involves the base pointer at
+ // the beginning of the SCoP. This breaks if the base pointer is defined
+ // inside the scop. Hence, we can only create a run-time check if we are
+ // sure the base pointer is not an instruction defined inside the scop.
+ // However, we can ignore loads that will be hoisted.
+
+ InvariantLoadsSetTy VariantLS, InvariantLS;
+ // In order to detect loads which are dependent on other invariant loads
+ // as invariant, we use fixed-point iteration method here i.e we iterate
+ // over the alias set for arbitrary number of times until it is safe to
+ // assume that all the invariant loads have been detected
+ while (true) {
+ const unsigned int VariantSize = VariantLS.size(),
+ InvariantSize = InvariantLS.size();
+
+ for (const auto &Ptr : AS) {
+ Instruction *Inst = dyn_cast<Instruction>(Ptr.getValue());
+ if (Inst && Context.CurRegion.contains(Inst)) {
+ auto *Load = dyn_cast<LoadInst>(Inst);
+ if (Load && InvariantLS.count(Load))
+ continue;
+ if (Load && isHoistableLoad(Load, Context.CurRegion, LI, SE, DT,
+ InvariantLS)) {
+ if (VariantLS.count(Load))
+ VariantLS.remove(Load);
+ Context.RequiredILS.insert(Load);
+ InvariantLS.insert(Load);
+ } else {
+ CanBuildRunTimeCheck = false;
+ VariantLS.insert(Load);
+ }
+ }
+ }
+
+ if (InvariantSize == InvariantLS.size() &&
+ VariantSize == VariantLS.size())
+ break;
+ }
+
+ if (CanBuildRunTimeCheck)
+ return true;
+ }
+ return invalid<ReportAlias>(Context, /*Assert=*/true, Inst, AS);
+ }
+
+ return true;
+}
+
+bool ScopDetection::isValidMemoryAccess(MemAccInst Inst,
+ DetectionContext &Context) const {
+ Value *Ptr = Inst.getPointerOperand();
+ Loop *L = LI.getLoopFor(Inst->getParent());
+ const SCEV *AccessFunction = SE.getSCEVAtScope(Ptr, L);
+ const SCEVUnknown *BasePointer;
+
+ BasePointer = dyn_cast<SCEVUnknown>(SE.getPointerBase(AccessFunction));
+
+ return isValidAccess(Inst, AccessFunction, BasePointer, Context);
+}
+
+bool ScopDetection::isValidInstruction(Instruction &Inst,
+ DetectionContext &Context) {
+ for (auto &Op : Inst.operands()) {
+ auto *OpInst = dyn_cast<Instruction>(&Op);
+
+ if (!OpInst)
+ continue;
+
+ if (isErrorBlock(*OpInst->getParent(), Context.CurRegion)) {
+ auto *PHI = dyn_cast<PHINode>(OpInst);
+ if (PHI) {
+ for (User *U : PHI->users()) {
+ auto *UI = dyn_cast<Instruction>(U);
+ if (!UI || !UI->isTerminator())
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ if (isa<LandingPadInst>(&Inst) || isa<ResumeInst>(&Inst))
+ return false;
+
+ // We only check the call instruction but not invoke instruction.
+ if (CallInst *CI = dyn_cast<CallInst>(&Inst)) {
+ if (isValidCallInst(*CI, Context))
+ return true;
+
+ return invalid<ReportFuncCall>(Context, /*Assert=*/true, &Inst);
+ }
+
+ if (!Inst.mayReadOrWriteMemory()) {
+ if (!isa<AllocaInst>(Inst))
+ return true;
+
+ return invalid<ReportAlloca>(Context, /*Assert=*/true, &Inst);
+ }
+
+ // Check the access function.
+ if (auto MemInst = MemAccInst::dyn_cast(Inst)) {
+ Context.hasStores |= isa<StoreInst>(MemInst);
+ Context.hasLoads |= isa<LoadInst>(MemInst);
+ if (!MemInst.isSimple())
+ return invalid<ReportNonSimpleMemoryAccess>(Context, /*Assert=*/true,
+ &Inst);
+
+ return isValidMemoryAccess(MemInst, Context);
+ }
+
+ // We do not know this instruction, therefore we assume it is invalid.
+ return invalid<ReportUnknownInst>(Context, /*Assert=*/true, &Inst);
+}
+
+/// Check whether @p L has exiting blocks.
+///
+/// @param L The loop of interest
+///
+/// @return True if the loop has exiting blocks, false otherwise.
+static bool hasExitingBlocks(Loop *L) {
+ SmallVector<BasicBlock *, 4> ExitingBlocks;
+ L->getExitingBlocks(ExitingBlocks);
+ return !ExitingBlocks.empty();
+}
+
+bool ScopDetection::canUseISLTripCount(Loop *L, DetectionContext &Context) {
+ // Ensure the loop has valid exiting blocks as well as latches, otherwise we
+ // need to overapproximate it as a boxed loop.
+ SmallVector<BasicBlock *, 4> LoopControlBlocks;
+ L->getExitingBlocks(LoopControlBlocks);
+ L->getLoopLatches(LoopControlBlocks);
+ for (BasicBlock *ControlBB : LoopControlBlocks) {
+ if (!isValidCFG(*ControlBB, true, false, Context))
+ return false;
+ }
+
+ // We can use ISL to compute the trip count of L.
+ return true;
+}
+
+bool ScopDetection::isValidLoop(Loop *L, DetectionContext &Context) {
+ // Loops that contain part but not all of the blocks of a region cannot be
+ // handled by the schedule generation. Such loop constructs can happen
+ // because a region can contain BBs that have no path to the exit block
+ // (Infinite loops, UnreachableInst), but such blocks are never part of a
+ // loop.
+ //
+ // _______________
+ // | Loop Header | <-----------.
+ // --------------- |
+ // | |
+ // _______________ ______________
+ // | RegionEntry |-----> | RegionExit |----->
+ // --------------- --------------
+ // |
+ // _______________
+ // | EndlessLoop | <--.
+ // --------------- |
+ // | |
+ // \------------/
+ //
+ // In the example above, the loop (LoopHeader,RegionEntry,RegionExit) is
+ // neither entirely contained in the region RegionEntry->RegionExit
+ // (containing RegionEntry,EndlessLoop) nor is the region entirely contained
+ // in the loop.
+ // The block EndlessLoop is contained in the region because Region::contains
+ // tests whether it is not dominated by RegionExit. This is probably to not
+ // having to query the PostdominatorTree. Instead of an endless loop, a dead
+ // end can also be formed by an UnreachableInst. This case is already caught
+ // by isErrorBlock(). We hence only have to reject endless loops here.
+ if (!hasExitingBlocks(L))
+ return invalid<ReportLoopHasNoExit>(Context, /*Assert=*/true, L);
+
+ // The algorithm for domain construction assumes that loops has only a single
+ // exit block (and hence corresponds to a subregion). Note that we cannot use
+ // L->getExitBlock() because it does not check whether all exiting edges point
+ // to the same BB.
+ SmallVector<BasicBlock *, 4> ExitBlocks;
+ L->getExitBlocks(ExitBlocks);
+ BasicBlock *TheExitBlock = ExitBlocks[0];
+ for (BasicBlock *ExitBB : ExitBlocks) {
+ if (TheExitBlock != ExitBB)
+ return invalid<ReportLoopHasMultipleExits>(Context, /*Assert=*/true, L);
+ }
+
+ if (canUseISLTripCount(L, Context))
+ return true;
+
+ if (AllowNonAffineSubLoops && AllowNonAffineSubRegions) {
+ Region *R = RI.getRegionFor(L->getHeader());
+ while (R != &Context.CurRegion && !R->contains(L))
+ R = R->getParent();
+
+ if (addOverApproximatedRegion(R, Context))
+ return true;
+ }
+
+ const SCEV *LoopCount = SE.getBackedgeTakenCount(L);
+ return invalid<ReportLoopBound>(Context, /*Assert=*/true, L, LoopCount);
+}
+
+/// Return the number of loops in @p L (incl. @p L) that have a trip
+/// count that is not known to be less than @MinProfitableTrips.
+ScopDetection::LoopStats
+ScopDetection::countBeneficialSubLoops(Loop *L, ScalarEvolution &SE,
+ unsigned MinProfitableTrips) {
+ auto *TripCount = SE.getBackedgeTakenCount(L);
+
+ int NumLoops = 1;
+ int MaxLoopDepth = 1;
+ if (MinProfitableTrips > 0)
+ if (auto *TripCountC = dyn_cast<SCEVConstant>(TripCount))
+ if (TripCountC->getType()->getScalarSizeInBits() <= 64)
+ if (TripCountC->getValue()->getZExtValue() <= MinProfitableTrips)
+ NumLoops -= 1;
+
+ for (auto &SubLoop : *L) {
+ LoopStats Stats = countBeneficialSubLoops(SubLoop, SE, MinProfitableTrips);
+ NumLoops += Stats.NumLoops;
+ MaxLoopDepth = std::max(MaxLoopDepth, Stats.MaxDepth + 1);
+ }
+
+ return {NumLoops, MaxLoopDepth};
+}
+
+ScopDetection::LoopStats
+ScopDetection::countBeneficialLoops(Region *R, ScalarEvolution &SE,
+ LoopInfo &LI, unsigned MinProfitableTrips) {
+ int LoopNum = 0;
+ int MaxLoopDepth = 0;
+
+ auto L = LI.getLoopFor(R->getEntry());
+
+ // If L is fully contained in R, move to first loop surrounding R. Otherwise,
+ // L is either nullptr or already surrounding R.
+ if (L && R->contains(L)) {
+ L = R->outermostLoopInRegion(L);
+ L = L->getParentLoop();
+ }
+
+ auto SubLoops =
+ L ? L->getSubLoopsVector() : std::vector<Loop *>(LI.begin(), LI.end());
+
+ for (auto &SubLoop : SubLoops)
+ if (R->contains(SubLoop)) {
+ LoopStats Stats =
+ countBeneficialSubLoops(SubLoop, SE, MinProfitableTrips);
+ LoopNum += Stats.NumLoops;
+ MaxLoopDepth = std::max(MaxLoopDepth, Stats.MaxDepth);
+ }
+
+ return {LoopNum, MaxLoopDepth};
+}
+
+static bool isErrorBlockImpl(BasicBlock &BB, const Region &R, LoopInfo &LI,
+ const DominatorTree &DT) {
+ if (isa<UnreachableInst>(BB.getTerminator()))
+ return true;
+
+ if (LI.isLoopHeader(&BB))
+ return false;
+
+ // Don't consider something outside the SCoP as error block. It will precede
+ // the code versioning runtime check.
+ if (!R.contains(&BB))
+ return false;
+
+ // Basic blocks that are always executed are not considered error blocks,
+ // as their execution can not be a rare event.
+ bool DominatesAllPredecessors = true;
+ if (R.isTopLevelRegion()) {
+ for (BasicBlock &I : *R.getEntry()->getParent()) {
+ if (isa<ReturnInst>(I.getTerminator()) && !DT.dominates(&BB, &I)) {
+ DominatesAllPredecessors = false;
+ break;
+ }
+ }
+ } else {
+ for (auto Pred : predecessors(R.getExit())) {
+ if (R.contains(Pred) && !DT.dominates(&BB, Pred)) {
+ DominatesAllPredecessors = false;
+ break;
+ }
+ }
+ }
+
+ if (DominatesAllPredecessors)
+ return false;
+
+ for (Instruction &Inst : BB)
+ if (CallInst *CI = dyn_cast<CallInst>(&Inst)) {
+ if (isDebugCall(CI))
+ continue;
+
+ if (isIgnoredIntrinsic(CI))
+ continue;
+
+ // memset, memcpy and memmove are modeled intrinsics.
+ if (isa<MemSetInst>(CI) || isa<MemTransferInst>(CI))
+ continue;
+
+ if (!CI->doesNotAccessMemory())
+ return true;
+ if (CI->doesNotReturn())
+ return true;
+ }
+
+ return false;
+}
+
+bool ScopDetection::isErrorBlock(llvm::BasicBlock &BB, const llvm::Region &R) {
+ if (!PollyAllowErrorBlocks)
+ return false;
+
+ auto It = ErrorBlockCache.insert({std::make_pair(&BB, &R), false});
+ if (!It.second)
+ return It.first->getSecond();
+
+ bool Result = isErrorBlockImpl(BB, R, LI, DT);
+ It.first->second = Result;
+ return Result;
+}
+
+Region *ScopDetection::expandRegion(Region &R) {
+ // Initial no valid region was found (greater than R)
+ std::unique_ptr<Region> LastValidRegion;
+ auto ExpandedRegion = std::unique_ptr<Region>(R.getExpandedRegion());
+
+ LLVM_DEBUG(dbgs() << "\tExpanding " << R.getNameStr() << "\n");
+
+ while (ExpandedRegion) {
+ BBPair P = getBBPairForRegion(ExpandedRegion.get());
+ std::unique_ptr<DetectionContext> &Entry = DetectionContextMap[P];
+ Entry = std::make_unique<DetectionContext>(*ExpandedRegion, AA,
+ /*Verifying=*/false);
+ DetectionContext &Context = *Entry.get();
+
+ LLVM_DEBUG(dbgs() << "\t\tTrying " << ExpandedRegion->getNameStr() << "\n");
+ // Only expand when we did not collect errors.
+
+ if (!Context.Log.hasErrors()) {
+ // If the exit is valid check all blocks
+ // - if true, a valid region was found => store it + keep expanding
+ // - if false, .tbd. => stop (should this really end the loop?)
+ if (!allBlocksValid(Context) || Context.Log.hasErrors()) {
+ removeCachedResults(*ExpandedRegion);
+ DetectionContextMap.erase(P);
+ break;
+ }
+
+ // Store this region, because it is the greatest valid (encountered so
+ // far).
+ if (LastValidRegion) {
+ removeCachedResults(*LastValidRegion);
+ DetectionContextMap.erase(P);
+ }
+ LastValidRegion = std::move(ExpandedRegion);
+
+ // Create and test the next greater region (if any)
+ ExpandedRegion =
+ std::unique_ptr<Region>(LastValidRegion->getExpandedRegion());
+
+ } else {
+ // Create and test the next greater region (if any)
+ removeCachedResults(*ExpandedRegion);
+ DetectionContextMap.erase(P);
+ ExpandedRegion =
+ std::unique_ptr<Region>(ExpandedRegion->getExpandedRegion());
+ }
+ }
+
+ LLVM_DEBUG({
+ if (LastValidRegion)
+ dbgs() << "\tto " << LastValidRegion->getNameStr() << "\n";
+ else
+ dbgs() << "\tExpanding " << R.getNameStr() << " failed\n";
+ });
+
+ return LastValidRegion.release();
+}
+
+static bool regionWithoutLoops(Region &R, LoopInfo &LI) {
+ for (const BasicBlock *BB : R.blocks())
+ if (R.contains(LI.getLoopFor(BB)))
+ return false;
+
+ return true;
+}
+
+void ScopDetection::removeCachedResultsRecursively(const Region &R) {
+ for (auto &SubRegion : R) {
+ if (ValidRegions.count(SubRegion.get())) {
+ removeCachedResults(*SubRegion.get());
+ } else
+ removeCachedResultsRecursively(*SubRegion);
+ }
+}
+
+void ScopDetection::removeCachedResults(const Region &R) {
+ ValidRegions.remove(&R);
+}
+
+void ScopDetection::findScops(Region &R) {
+ std::unique_ptr<DetectionContext> &Entry =
+ DetectionContextMap[getBBPairForRegion(&R)];
+ Entry = std::make_unique<DetectionContext>(R, AA, /*Verifying=*/false);
+ DetectionContext &Context = *Entry.get();
+
+ bool RegionIsValid = false;
+ if (!PollyProcessUnprofitable && regionWithoutLoops(R, LI))
+ invalid<ReportUnprofitable>(Context, /*Assert=*/true, &R);
+ else
+ RegionIsValid = isValidRegion(Context);
+
+ bool HasErrors = !RegionIsValid || Context.Log.size() > 0;
+
+ if (HasErrors) {
+ removeCachedResults(R);
+ } else {
+ ValidRegions.insert(&R);
+ return;
+ }
+
+ for (auto &SubRegion : R)
+ findScops(*SubRegion);
+
+ // Try to expand regions.
+ //
+ // As the region tree normally only contains canonical regions, non canonical
+ // regions that form a Scop are not found. Therefore, those non canonical
+ // regions are checked by expanding the canonical ones.
+
+ std::vector<Region *> ToExpand;
+
+ for (auto &SubRegion : R)
+ ToExpand.push_back(SubRegion.get());
+
+ for (Region *CurrentRegion : ToExpand) {
+ // Skip invalid regions. Regions may become invalid, if they are element of
+ // an already expanded region.
+ if (!ValidRegions.count(CurrentRegion))
+ continue;
+
+ // Skip regions that had errors.
+ bool HadErrors = lookupRejectionLog(CurrentRegion)->hasErrors();
+ if (HadErrors)
+ continue;
+
+ Region *ExpandedR = expandRegion(*CurrentRegion);
+
+ if (!ExpandedR)
+ continue;
+
+ R.addSubRegion(ExpandedR, true);
+ ValidRegions.insert(ExpandedR);
+ removeCachedResults(*CurrentRegion);
+ removeCachedResultsRecursively(*ExpandedR);
+ }
+}
+
+bool ScopDetection::allBlocksValid(DetectionContext &Context) {
+ Region &CurRegion = Context.CurRegion;
+
+ for (const BasicBlock *BB : CurRegion.blocks()) {
+ Loop *L = LI.getLoopFor(BB);
+ if (L && L->getHeader() == BB) {
+ if (CurRegion.contains(L)) {
+ if (!isValidLoop(L, Context) && !KeepGoing)
+ return false;
+ } else {
+ SmallVector<BasicBlock *, 1> Latches;
+ L->getLoopLatches(Latches);
+ for (BasicBlock *Latch : Latches)
+ if (CurRegion.contains(Latch))
+ return invalid<ReportLoopOnlySomeLatches>(Context, /*Assert=*/true,
+ L);
+ }
+ }
+ }
+
+ for (BasicBlock *BB : CurRegion.blocks()) {
+ bool IsErrorBlock = isErrorBlock(*BB, CurRegion);
+
+ // Also check exception blocks (and possibly register them as non-affine
+ // regions). Even though exception blocks are not modeled, we use them
+ // to forward-propagate domain constraints during ScopInfo construction.
+ if (!isValidCFG(*BB, false, IsErrorBlock, Context) && !KeepGoing)
+ return false;
+
+ if (IsErrorBlock)
+ continue;
+
+ for (BasicBlock::iterator I = BB->begin(), E = --BB->end(); I != E; ++I)
+ if (!isValidInstruction(*I, Context) && !KeepGoing)
+ return false;
+ }
+
+ if (!hasAffineMemoryAccesses(Context))
+ return false;
+
+ return true;
+}
+
+bool ScopDetection::hasSufficientCompute(DetectionContext &Context,
+ int NumLoops) const {
+ int InstCount = 0;
+
+ if (NumLoops == 0)
+ return false;
+
+ for (auto *BB : Context.CurRegion.blocks())
+ if (Context.CurRegion.contains(LI.getLoopFor(BB)))
+ InstCount += BB->size();
+
+ InstCount = InstCount / NumLoops;
+
+ return InstCount >= ProfitabilityMinPerLoopInstructions;
+}
+
+bool ScopDetection::hasPossiblyDistributableLoop(
+ DetectionContext &Context) const {
+ for (auto *BB : Context.CurRegion.blocks()) {
+ auto *L = LI.getLoopFor(BB);
+ if (!Context.CurRegion.contains(L))
+ continue;
+ if (Context.BoxedLoopsSet.count(L))
+ continue;
+ unsigned StmtsWithStoresInLoops = 0;
+ for (auto *LBB : L->blocks()) {
+ bool MemStore = false;
+ for (auto &I : *LBB)
+ MemStore |= isa<StoreInst>(&I);
+ StmtsWithStoresInLoops += MemStore;
+ }
+ return (StmtsWithStoresInLoops > 1);
+ }
+ return false;
+}
+
+bool ScopDetection::isProfitableRegion(DetectionContext &Context) const {
+ Region &CurRegion = Context.CurRegion;
+
+ if (PollyProcessUnprofitable)
+ return true;
+
+ // We can probably not do a lot on scops that only write or only read
+ // data.
+ if (!Context.hasStores || !Context.hasLoads)
+ return invalid<ReportUnprofitable>(Context, /*Assert=*/true, &CurRegion);
+
+ int NumLoops =
+ countBeneficialLoops(&CurRegion, SE, LI, MIN_LOOP_TRIP_COUNT).NumLoops;
+ int NumAffineLoops = NumLoops - Context.BoxedLoopsSet.size();
+
+ // Scops with at least two loops may allow either loop fusion or tiling and
+ // are consequently interesting to look at.
+ if (NumAffineLoops >= 2)
+ return true;
+
+ // A loop with multiple non-trivial blocks might be amendable to distribution.
+ if (NumAffineLoops == 1 && hasPossiblyDistributableLoop(Context))
+ return true;
+
+ // Scops that contain a loop with a non-trivial amount of computation per
+ // loop-iteration are interesting as we may be able to parallelize such
+ // loops. Individual loops that have only a small amount of computation
+ // per-iteration are performance-wise very fragile as any change to the
+ // loop induction variables may affect performance. To not cause spurious
+ // performance regressions, we do not consider such loops.
+ if (NumAffineLoops == 1 && hasSufficientCompute(Context, NumLoops))
+ return true;
+
+ return invalid<ReportUnprofitable>(Context, /*Assert=*/true, &CurRegion);
+}
+
+bool ScopDetection::isValidRegion(DetectionContext &Context) {
+ Region &CurRegion = Context.CurRegion;
+
+ LLVM_DEBUG(dbgs() << "Checking region: " << CurRegion.getNameStr() << "\n\t");
+
+ if (!PollyAllowFullFunction && CurRegion.isTopLevelRegion()) {
+ LLVM_DEBUG(dbgs() << "Top level region is invalid\n");
+ return false;
+ }
+
+ DebugLoc DbgLoc;
+ if (CurRegion.getExit() &&
+ isa<UnreachableInst>(CurRegion.getExit()->getTerminator())) {
+ LLVM_DEBUG(dbgs() << "Unreachable in exit\n");
+ return invalid<ReportUnreachableInExit>(Context, /*Assert=*/true,
+ CurRegion.getExit(), DbgLoc);
+ }
+
+ if (!OnlyRegion.empty() &&
+ !CurRegion.getEntry()->getName().count(OnlyRegion)) {
+ LLVM_DEBUG({
+ dbgs() << "Region entry does not match -polly-only-region";
+ dbgs() << "\n";
+ });
+ return false;
+ }
+
+ for (BasicBlock *Pred : predecessors(CurRegion.getEntry())) {
+ Instruction *PredTerm = Pred->getTerminator();
+ if (isa<IndirectBrInst>(PredTerm) || isa<CallBrInst>(PredTerm))
+ return invalid<ReportIndirectPredecessor>(
+ Context, /*Assert=*/true, PredTerm, PredTerm->getDebugLoc());
+ }
+
+ // SCoP cannot contain the entry block of the function, because we need
+ // to insert alloca instruction there when translate scalar to array.
+ if (!PollyAllowFullFunction &&
+ CurRegion.getEntry() ==
+ &(CurRegion.getEntry()->getParent()->getEntryBlock()))
+ return invalid<ReportEntry>(Context, /*Assert=*/true, CurRegion.getEntry());
+
+ if (!allBlocksValid(Context))
+ return false;
+
+ if (!isReducibleRegion(CurRegion, DbgLoc))
+ return invalid<ReportIrreducibleRegion>(Context, /*Assert=*/true,
+ &CurRegion, DbgLoc);
+
+ LLVM_DEBUG(dbgs() << "OK\n");
+ return true;
+}
+
+void ScopDetection::markFunctionAsInvalid(Function *F) {
+ F->addFnAttr(PollySkipFnAttr);
+}
+
+bool ScopDetection::isValidFunction(Function &F) {
+ return !F.hasFnAttribute(PollySkipFnAttr);
+}
+
+void ScopDetection::printLocations(Function &F) {
+ for (const Region *R : *this) {
+ unsigned LineEntry, LineExit;
+ std::string FileName;
+
+ getDebugLocation(R, LineEntry, LineExit, FileName);
+ DiagnosticScopFound Diagnostic(F, FileName, LineEntry, LineExit);
+ F.getContext().diagnose(Diagnostic);
+ }
+}
+
+void ScopDetection::emitMissedRemarks(const Function &F) {
+ for (auto &DIt : DetectionContextMap) {
+ DetectionContext &DC = *DIt.getSecond().get();
+ if (DC.Log.hasErrors())
+ emitRejectionRemarks(DIt.getFirst(), DC.Log, ORE);
+ }
+}
+
+bool ScopDetection::isReducibleRegion(Region &R, DebugLoc &DbgLoc) const {
+ /// Enum for coloring BBs in Region.
+ ///
+ /// WHITE - Unvisited BB in DFS walk.
+ /// GREY - BBs which are currently on the DFS stack for processing.
+ /// BLACK - Visited and completely processed BB.
+ enum Color { WHITE, GREY, BLACK };
+
+ BasicBlock *REntry = R.getEntry();
+ BasicBlock *RExit = R.getExit();
+ // Map to match the color of a BasicBlock during the DFS walk.
+ DenseMap<const BasicBlock *, Color> BBColorMap;
+ // Stack keeping track of current BB and index of next child to be processed.
+ std::stack<std::pair<BasicBlock *, unsigned>> DFSStack;
+
+ unsigned AdjacentBlockIndex = 0;
+ BasicBlock *CurrBB, *SuccBB;
+ CurrBB = REntry;
+
+ // Initialize the map for all BB with WHITE color.
+ for (auto *BB : R.blocks())
+ BBColorMap[BB] = WHITE;
+
+ // Process the entry block of the Region.
+ BBColorMap[CurrBB] = GREY;
+ DFSStack.push(std::make_pair(CurrBB, 0));
+
+ while (!DFSStack.empty()) {
+ // Get next BB on stack to be processed.
+ CurrBB = DFSStack.top().first;
+ AdjacentBlockIndex = DFSStack.top().second;
+ DFSStack.pop();
+
+ // Loop to iterate over the successors of current BB.
+ const Instruction *TInst = CurrBB->getTerminator();
+ unsigned NSucc = TInst->getNumSuccessors();
+ for (unsigned I = AdjacentBlockIndex; I < NSucc;
+ ++I, ++AdjacentBlockIndex) {
+ SuccBB = TInst->getSuccessor(I);
+
+ // Checks for region exit block and self-loops in BB.
+ if (SuccBB == RExit || SuccBB == CurrBB)
+ continue;
+
+ // WHITE indicates an unvisited BB in DFS walk.
+ if (BBColorMap[SuccBB] == WHITE) {
+ // Push the current BB and the index of the next child to be visited.
+ DFSStack.push(std::make_pair(CurrBB, I + 1));
+ // Push the next BB to be processed.
+ DFSStack.push(std::make_pair(SuccBB, 0));
+ // First time the BB is being processed.
+ BBColorMap[SuccBB] = GREY;
+ break;
+ } else if (BBColorMap[SuccBB] == GREY) {
+ // GREY indicates a loop in the control flow.
+ // If the destination dominates the source, it is a natural loop
+ // else, an irreducible control flow in the region is detected.
+ if (!DT.dominates(SuccBB, CurrBB)) {
+ // Get debug info of instruction which causes irregular control flow.
+ DbgLoc = TInst->getDebugLoc();
+ return false;
+ }
+ }
+ }
+
+ // If all children of current BB have been processed,
+ // then mark that BB as fully processed.
+ if (AdjacentBlockIndex == NSucc)
+ BBColorMap[CurrBB] = BLACK;
+ }
+
+ return true;
+}
+
+static void updateLoopCountStatistic(ScopDetection::LoopStats Stats,
+ bool OnlyProfitable) {
+ if (!OnlyProfitable) {
+ NumLoopsInScop += Stats.NumLoops;
+ MaxNumLoopsInScop =
+ std::max(MaxNumLoopsInScop.getValue(), (unsigned)Stats.NumLoops);
+ if (Stats.MaxDepth == 0)
+ NumScopsDepthZero++;
+ else if (Stats.MaxDepth == 1)
+ NumScopsDepthOne++;
+ else if (Stats.MaxDepth == 2)
+ NumScopsDepthTwo++;
+ else if (Stats.MaxDepth == 3)
+ NumScopsDepthThree++;
+ else if (Stats.MaxDepth == 4)
+ NumScopsDepthFour++;
+ else if (Stats.MaxDepth == 5)
+ NumScopsDepthFive++;
+ else
+ NumScopsDepthLarger++;
+ } else {
+ NumLoopsInProfScop += Stats.NumLoops;
+ MaxNumLoopsInProfScop =
+ std::max(MaxNumLoopsInProfScop.getValue(), (unsigned)Stats.NumLoops);
+ if (Stats.MaxDepth == 0)
+ NumProfScopsDepthZero++;
+ else if (Stats.MaxDepth == 1)
+ NumProfScopsDepthOne++;
+ else if (Stats.MaxDepth == 2)
+ NumProfScopsDepthTwo++;
+ else if (Stats.MaxDepth == 3)
+ NumProfScopsDepthThree++;
+ else if (Stats.MaxDepth == 4)
+ NumProfScopsDepthFour++;
+ else if (Stats.MaxDepth == 5)
+ NumProfScopsDepthFive++;
+ else
+ NumProfScopsDepthLarger++;
+ }
+}
+
+ScopDetection::DetectionContext *
+ScopDetection::getDetectionContext(const Region *R) const {
+ auto DCMIt = DetectionContextMap.find(getBBPairForRegion(R));
+ if (DCMIt == DetectionContextMap.end())
+ return nullptr;
+ return DCMIt->second.get();
+}
+
+const RejectLog *ScopDetection::lookupRejectionLog(const Region *R) const {
+ const DetectionContext *DC = getDetectionContext(R);
+ return DC ? &DC->Log : nullptr;
+}
+
+void ScopDetection::verifyRegion(const Region &R) {
+ assert(isMaxRegionInScop(R) && "Expect R is a valid region.");
+
+ DetectionContext Context(const_cast<Region &>(R), AA, true /*verifying*/);
+ isValidRegion(Context);
+}
+
+void ScopDetection::verifyAnalysis() {
+ if (!VerifyScops)
+ return;
+
+ for (const Region *R : ValidRegions)
+ verifyRegion(*R);
+}
+
+bool ScopDetectionWrapperPass::runOnFunction(Function &F) {
+ auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+ auto &RI = getAnalysis<RegionInfoPass>().getRegionInfo();
+ auto &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();
+ auto &SE = getAnalysis<ScalarEvolutionWrapperPass>().getSE();
+ auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
+ auto &ORE = getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE();
+
+ Result = std::make_unique<ScopDetection>(DT, SE, LI, RI, AA, ORE);
+ Result->detect(F);
+ return false;
+}
+
+void ScopDetectionWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequired<LoopInfoWrapperPass>();
+ AU.addRequiredTransitive<ScalarEvolutionWrapperPass>();
+ AU.addRequired<DominatorTreeWrapperPass>();
+ AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
+ // We also need AA and RegionInfo when we are verifying analysis.
+ AU.addRequiredTransitive<AAResultsWrapperPass>();
+ AU.addRequiredTransitive<RegionInfoPass>();
+ AU.setPreservesAll();
+}
+
+void ScopDetectionWrapperPass::print(raw_ostream &OS, const Module *) const {
+ for (const Region *R : Result->ValidRegions)
+ OS << "Valid Region for Scop: " << R->getNameStr() << '\n';
+
+ OS << "\n";
+}
+
+ScopDetectionWrapperPass::ScopDetectionWrapperPass() : FunctionPass(ID) {
+ // Disable runtime alias checks if we ignore aliasing all together.
+ if (IgnoreAliasing)
+ PollyUseRuntimeAliasChecks = false;
+}
+
+ScopAnalysis::ScopAnalysis() {
+ // Disable runtime alias checks if we ignore aliasing all together.
+ if (IgnoreAliasing)
+ PollyUseRuntimeAliasChecks = false;
+}
+
+void ScopDetectionWrapperPass::releaseMemory() { Result.reset(); }
+
+char ScopDetectionWrapperPass::ID;
+
+AnalysisKey ScopAnalysis::Key;
+
+ScopDetection ScopAnalysis::run(Function &F, FunctionAnalysisManager &FAM) {
+ auto &LI = FAM.getResult<LoopAnalysis>(F);
+ auto &RI = FAM.getResult<RegionInfoAnalysis>(F);
+ auto &AA = FAM.getResult<AAManager>(F);
+ auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);
+ auto &DT = FAM.getResult<DominatorTreeAnalysis>(F);
+ auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
+
+ ScopDetection Result(DT, SE, LI, RI, AA, ORE);
+ Result.detect(F);
+ return Result;
+}
+
+PreservedAnalyses ScopAnalysisPrinterPass::run(Function &F,
+ FunctionAnalysisManager &FAM) {
+ OS << "Detected Scops in Function " << F.getName() << "\n";
+ auto &SD = FAM.getResult<ScopAnalysis>(F);
+ for (const Region *R : SD.ValidRegions)
+ OS << "Valid Region for Scop: " << R->getNameStr() << '\n';
+
+ OS << "\n";
+ return PreservedAnalyses::all();
+}
+
+Pass *polly::createScopDetectionWrapperPassPass() {
+ return new ScopDetectionWrapperPass();
+}
+
+INITIALIZE_PASS_BEGIN(ScopDetectionWrapperPass, "polly-detect",
+ "Polly - Detect static control parts (SCoPs)", false,
+ false);
+INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(RegionInfoPass);
+INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass);
+INITIALIZE_PASS_END(ScopDetectionWrapperPass, "polly-detect",
+ "Polly - Detect static control parts (SCoPs)", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopDetectionDiagnostic.cpp b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopDetectionDiagnostic.cpp
new file mode 100644
index 00000000000..0bac2cfd155
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopDetectionDiagnostic.cpp
@@ -0,0 +1,834 @@
+//===- ScopDetectionDiagnostic.cpp - Error diagnostics --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Small set of diagnostic helper classes to encapsulate any errors occurred
+// during the detection of Scops.
+//
+// The ScopDetection defines a set of error classes (via Statistic variables)
+// that groups a number of individual errors into a group, e.g. non-affinity
+// related errors.
+// On error we generate an object that carries enough additional information
+// to diagnose the error and generate a helpful error message.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/ScopDetectionDiagnostic.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Analysis/AliasSetTracker.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/DebugLoc.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <string>
+#include <utility>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "polly-detect"
+
+#define SCOP_STAT(NAME, DESC) \
+ { "polly-detect", "NAME", "Number of rejected regions: " DESC }
+
+static Statistic RejectStatistics[] = {
+ SCOP_STAT(CFG, ""),
+ SCOP_STAT(InvalidTerminator, "Unsupported terminator instruction"),
+ SCOP_STAT(IrreducibleRegion, "Irreducible loops"),
+ SCOP_STAT(UnreachableInExit, "Unreachable in exit block"),
+ SCOP_STAT(IndirectPredecessor, "Branch from indirect terminator"),
+ SCOP_STAT(LastCFG, ""),
+ SCOP_STAT(AffFunc, ""),
+ SCOP_STAT(UndefCond, "Undefined branch condition"),
+ SCOP_STAT(InvalidCond, "Non-integer branch condition"),
+ SCOP_STAT(UndefOperand, "Undefined operands in comparison"),
+ SCOP_STAT(NonAffBranch, "Non-affine branch condition"),
+ SCOP_STAT(NoBasePtr, "No base pointer"),
+ SCOP_STAT(UndefBasePtr, "Undefined base pointer"),
+ SCOP_STAT(VariantBasePtr, "Variant base pointer"),
+ SCOP_STAT(NonAffineAccess, "Non-affine memory accesses"),
+ SCOP_STAT(DifferentElementSize, "Accesses with differing sizes"),
+ SCOP_STAT(LastAffFunc, ""),
+ SCOP_STAT(LoopBound, "Uncomputable loop bounds"),
+ SCOP_STAT(LoopHasNoExit, "Loop without exit"),
+ SCOP_STAT(LoopHasMultipleExits, "Loop with multiple exits"),
+ SCOP_STAT(LoopOnlySomeLatches, "Not all loop latches in scop"),
+ SCOP_STAT(FuncCall, "Function call with side effects"),
+ SCOP_STAT(NonSimpleMemoryAccess,
+ "Compilated access semantics (volatile or atomic)"),
+ SCOP_STAT(Alias, "Base address aliasing"),
+ SCOP_STAT(Other, ""),
+ SCOP_STAT(IntToPtr, "Integer to pointer conversions"),
+ SCOP_STAT(Alloca, "Stack allocations"),
+ SCOP_STAT(UnknownInst, "Unknown Instructions"),
+ SCOP_STAT(Entry, "Contains entry block"),
+ SCOP_STAT(Unprofitable, "Assumed to be unprofitable"),
+ SCOP_STAT(LastOther, ""),
+};
+
+namespace polly {
+
+/// Small string conversion via raw_string_stream.
+template <typename T> std::string operator+(Twine LHS, const T &RHS) {
+ std::string Buf;
+ raw_string_ostream fmt(Buf);
+ fmt << RHS;
+ fmt.flush();
+
+ return LHS.concat(Buf).str();
+}
+} // namespace polly
+
+namespace llvm {
+
+// Lexicographic order on (line, col) of our debug locations.
+static bool operator<(const DebugLoc &LHS, const DebugLoc &RHS) {
+ return LHS.getLine() < RHS.getLine() ||
+ (LHS.getLine() == RHS.getLine() && LHS.getCol() < RHS.getCol());
+}
+} // namespace llvm
+
+namespace polly {
+
+BBPair getBBPairForRegion(const Region *R) {
+ return std::make_pair(R->getEntry(), R->getExit());
+}
+
+void getDebugLocations(const BBPair &P, DebugLoc &Begin, DebugLoc &End) {
+ SmallPtrSet<BasicBlock *, 32> Seen;
+ SmallVector<BasicBlock *, 32> Todo;
+ Todo.push_back(P.first);
+ while (!Todo.empty()) {
+ auto *BB = Todo.pop_back_val();
+ if (BB == P.second)
+ continue;
+ if (!Seen.insert(BB).second)
+ continue;
+ Todo.append(succ_begin(BB), succ_end(BB));
+ for (const Instruction &Inst : *BB) {
+ DebugLoc DL = Inst.getDebugLoc();
+ if (!DL)
+ continue;
+
+ Begin = Begin ? std::min(Begin, DL) : DL;
+ End = End ? std::max(End, DL) : DL;
+ }
+ }
+}
+
+void emitRejectionRemarks(const BBPair &P, const RejectLog &Log,
+ OptimizationRemarkEmitter &ORE) {
+ DebugLoc Begin, End;
+ getDebugLocations(P, Begin, End);
+
+ ORE.emit(
+ OptimizationRemarkMissed(DEBUG_TYPE, "RejectionErrors", Begin, P.first)
+ << "The following errors keep this region from being a Scop.");
+
+ for (RejectReasonPtr RR : Log) {
+
+ if (const DebugLoc &Loc = RR->getDebugLoc())
+ ORE.emit(OptimizationRemarkMissed(DEBUG_TYPE, RR->getRemarkName(), Loc,
+ RR->getRemarkBB())
+ << RR->getEndUserMessage());
+ else
+ ORE.emit(OptimizationRemarkMissed(DEBUG_TYPE, RR->getRemarkName(), Begin,
+ RR->getRemarkBB())
+ << RR->getEndUserMessage());
+ }
+
+ /* Check to see if Region is a top level region, getExit = NULL*/
+ if (P.second)
+ ORE.emit(
+ OptimizationRemarkMissed(DEBUG_TYPE, "InvalidScopEnd", End, P.second)
+ << "Invalid Scop candidate ends here.");
+ else
+ ORE.emit(
+ OptimizationRemarkMissed(DEBUG_TYPE, "InvalidScopEnd", End, P.first)
+ << "Invalid Scop candidate ends here.");
+}
+
+//===----------------------------------------------------------------------===//
+// RejectReason.
+
+RejectReason::RejectReason(RejectReasonKind K) : Kind(K) {
+ RejectStatistics[static_cast<int>(K)]++;
+}
+
+const DebugLoc RejectReason::Unknown = DebugLoc();
+
+const DebugLoc &RejectReason::getDebugLoc() const {
+ // Allocate an empty DebugLoc and return it a reference to it.
+ return Unknown;
+}
+
+// RejectLog.
+void RejectLog::print(raw_ostream &OS, int level) const {
+ int j = 0;
+ for (auto Reason : ErrorReports)
+ OS.indent(level) << "[" << j++ << "] " << Reason->getMessage() << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// ReportCFG.
+
+ReportCFG::ReportCFG(const RejectReasonKind K) : RejectReason(K) {}
+
+bool ReportCFG::classof(const RejectReason *RR) {
+ return RR->getKind() >= RejectReasonKind::CFG &&
+ RR->getKind() <= RejectReasonKind::LastCFG;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportInvalidTerminator.
+
+std::string ReportInvalidTerminator::getRemarkName() const {
+ return "InvalidTerminator";
+}
+
+const Value *ReportInvalidTerminator::getRemarkBB() const { return BB; }
+
+std::string ReportInvalidTerminator::getMessage() const {
+ return ("Invalid instruction terminates BB: " + BB->getName()).str();
+}
+
+const DebugLoc &ReportInvalidTerminator::getDebugLoc() const {
+ return BB->getTerminator()->getDebugLoc();
+}
+
+bool ReportInvalidTerminator::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::InvalidTerminator;
+}
+
+//===----------------------------------------------------------------------===//
+// UnreachableInExit.
+
+std::string ReportUnreachableInExit::getRemarkName() const {
+ return "UnreachableInExit";
+}
+
+const Value *ReportUnreachableInExit::getRemarkBB() const { return BB; }
+
+std::string ReportUnreachableInExit::getMessage() const {
+ std::string BBName = BB->getName().str();
+ return "Unreachable in exit block" + BBName;
+}
+
+const DebugLoc &ReportUnreachableInExit::getDebugLoc() const { return DbgLoc; }
+
+std::string ReportUnreachableInExit::getEndUserMessage() const {
+ return "Unreachable in exit block.";
+}
+
+bool ReportUnreachableInExit::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::UnreachableInExit;
+}
+
+//===----------------------------------------------------------------------===//
+// IndirectPredecessor.
+
+std::string ReportIndirectPredecessor::getRemarkName() const {
+ return "IndirectPredecessor";
+}
+
+const Value *ReportIndirectPredecessor::getRemarkBB() const {
+ if (Inst)
+ return Inst->getParent();
+ return nullptr;
+}
+
+std::string ReportIndirectPredecessor::getMessage() const {
+ if (Inst)
+ return "Branch from indirect terminator: " + *Inst;
+ return getEndUserMessage();
+}
+
+const DebugLoc &ReportIndirectPredecessor::getDebugLoc() const {
+ return DbgLoc;
+}
+
+std::string ReportIndirectPredecessor::getEndUserMessage() const {
+ return "Branch from indirect terminator.";
+}
+
+bool ReportIndirectPredecessor::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::IndirectPredecessor;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportIrreducibleRegion.
+
+std::string ReportIrreducibleRegion::getRemarkName() const {
+ return "IrreducibleRegion";
+}
+
+const Value *ReportIrreducibleRegion::getRemarkBB() const {
+ return R->getEntry();
+}
+
+std::string ReportIrreducibleRegion::getMessage() const {
+ return "Irreducible region encountered: " + R->getNameStr();
+}
+
+const DebugLoc &ReportIrreducibleRegion::getDebugLoc() const { return DbgLoc; }
+
+std::string ReportIrreducibleRegion::getEndUserMessage() const {
+ return "Irreducible region encountered in control flow.";
+}
+
+bool ReportIrreducibleRegion::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::IrreducibleRegion;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportAffFunc.
+
+ReportAffFunc::ReportAffFunc(const RejectReasonKind K, const Instruction *Inst)
+ : RejectReason(K), Inst(Inst) {}
+
+bool ReportAffFunc::classof(const RejectReason *RR) {
+ return RR->getKind() >= RejectReasonKind::AffFunc &&
+ RR->getKind() <= RejectReasonKind::LastAffFunc;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportUndefCond.
+
+std::string ReportUndefCond::getRemarkName() const { return "UndefCond"; }
+
+const Value *ReportUndefCond::getRemarkBB() const { return BB; }
+
+std::string ReportUndefCond::getMessage() const {
+ return ("Condition based on 'undef' value in BB: " + BB->getName()).str();
+}
+
+bool ReportUndefCond::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::UndefCond;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportInvalidCond.
+
+std::string ReportInvalidCond::getRemarkName() const { return "InvalidCond"; }
+
+const Value *ReportInvalidCond::getRemarkBB() const { return BB; }
+
+std::string ReportInvalidCond::getMessage() const {
+ return ("Condition in BB '" + BB->getName()).str() +
+ "' neither constant nor an icmp instruction";
+}
+
+bool ReportInvalidCond::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::InvalidCond;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportUndefOperand.
+
+std::string ReportUndefOperand::getRemarkName() const { return "UndefOperand"; }
+
+const Value *ReportUndefOperand::getRemarkBB() const { return BB; }
+
+std::string ReportUndefOperand::getMessage() const {
+ return ("undef operand in branch at BB: " + BB->getName()).str();
+}
+
+bool ReportUndefOperand::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::UndefOperand;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportNonAffBranch.
+
+std::string ReportNonAffBranch::getRemarkName() const { return "NonAffBranch"; }
+
+const Value *ReportNonAffBranch::getRemarkBB() const { return BB; }
+
+std::string ReportNonAffBranch::getMessage() const {
+ return ("Non affine branch in BB '" + BB->getName()).str() +
+ "' with LHS: " + *LHS + " and RHS: " + *RHS;
+}
+
+bool ReportNonAffBranch::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::NonAffBranch;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportNoBasePtr.
+
+std::string ReportNoBasePtr::getRemarkName() const { return "NoBasePtr"; }
+
+const Value *ReportNoBasePtr::getRemarkBB() const { return Inst->getParent(); }
+
+std::string ReportNoBasePtr::getMessage() const { return "No base pointer"; }
+
+bool ReportNoBasePtr::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::NoBasePtr;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportUndefBasePtr.
+
+std::string ReportUndefBasePtr::getRemarkName() const { return "UndefBasePtr"; }
+
+const Value *ReportUndefBasePtr::getRemarkBB() const {
+ return Inst->getParent();
+}
+
+std::string ReportUndefBasePtr::getMessage() const {
+ return "Undefined base pointer";
+}
+
+bool ReportUndefBasePtr::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::UndefBasePtr;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportVariantBasePtr.
+
+std::string ReportVariantBasePtr::getRemarkName() const {
+ return "VariantBasePtr";
+}
+
+const Value *ReportVariantBasePtr::getRemarkBB() const {
+ return Inst->getParent();
+}
+
+std::string ReportVariantBasePtr::getMessage() const {
+ return "Base address not invariant in current region:" + *BaseValue;
+}
+
+std::string ReportVariantBasePtr::getEndUserMessage() const {
+ return "The base address of this array is not invariant inside the loop";
+}
+
+bool ReportVariantBasePtr::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::VariantBasePtr;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportDifferentArrayElementSize
+
+std::string ReportDifferentArrayElementSize::getRemarkName() const {
+ return "DifferentArrayElementSize";
+}
+
+const Value *ReportDifferentArrayElementSize::getRemarkBB() const {
+ return Inst->getParent();
+}
+
+std::string ReportDifferentArrayElementSize::getMessage() const {
+ return "Access to one array through data types of different size";
+}
+
+bool ReportDifferentArrayElementSize::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::DifferentElementSize;
+}
+
+std::string ReportDifferentArrayElementSize::getEndUserMessage() const {
+ StringRef BaseName = BaseValue->getName();
+ std::string Name = BaseName.empty() ? "UNKNOWN" : BaseName.str();
+ return "The array \"" + Name +
+ "\" is accessed through elements that differ "
+ "in size";
+}
+
+//===----------------------------------------------------------------------===//
+// ReportNonAffineAccess.
+
+std::string ReportNonAffineAccess::getRemarkName() const {
+ return "NonAffineAccess";
+}
+
+const Value *ReportNonAffineAccess::getRemarkBB() const {
+ return Inst->getParent();
+}
+
+std::string ReportNonAffineAccess::getMessage() const {
+ return "Non affine access function: " + *AccessFunction;
+}
+
+bool ReportNonAffineAccess::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::NonAffineAccess;
+}
+
+std::string ReportNonAffineAccess::getEndUserMessage() const {
+ StringRef BaseName = BaseValue->getName();
+ std::string Name = BaseName.empty() ? "UNKNOWN" : BaseName.str();
+ return "The array subscript of \"" + Name + "\" is not affine";
+}
+
+//===----------------------------------------------------------------------===//
+// ReportLoopBound.
+
+ReportLoopBound::ReportLoopBound(Loop *L, const SCEV *LoopCount)
+ : RejectReason(RejectReasonKind::LoopBound), L(L), LoopCount(LoopCount),
+ Loc(L->getStartLoc()) {}
+
+std::string ReportLoopBound::getRemarkName() const { return "LoopBound"; }
+
+const Value *ReportLoopBound::getRemarkBB() const { return L->getHeader(); }
+
+std::string ReportLoopBound::getMessage() const {
+ return "Non affine loop bound '" + *LoopCount +
+ "' in loop: " + L->getHeader()->getName();
+}
+
+const DebugLoc &ReportLoopBound::getDebugLoc() const { return Loc; }
+
+bool ReportLoopBound::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::LoopBound;
+}
+
+std::string ReportLoopBound::getEndUserMessage() const {
+ return "Failed to derive an affine function from the loop bounds.";
+}
+
+//===----------------------------------------------------------------------===//
+// ReportLoopHasNoExit.
+
+std::string ReportLoopHasNoExit::getRemarkName() const {
+ return "LoopHasNoExit";
+}
+
+const Value *ReportLoopHasNoExit::getRemarkBB() const { return L->getHeader(); }
+
+std::string ReportLoopHasNoExit::getMessage() const {
+ return "Loop " + L->getHeader()->getName() + " has no exit.";
+}
+
+bool ReportLoopHasNoExit::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::LoopHasNoExit;
+}
+
+const DebugLoc &ReportLoopHasNoExit::getDebugLoc() const { return Loc; }
+
+std::string ReportLoopHasNoExit::getEndUserMessage() const {
+ return "Loop cannot be handled because it has no exit.";
+}
+
+//===----------------------------------------------------------------------===//
+// ReportLoopHasMultipleExits.
+
+std::string ReportLoopHasMultipleExits::getRemarkName() const {
+ return "ReportLoopHasMultipleExits";
+}
+
+const Value *ReportLoopHasMultipleExits::getRemarkBB() const {
+ return L->getHeader();
+}
+
+std::string ReportLoopHasMultipleExits::getMessage() const {
+ return "Loop " + L->getHeader()->getName() + " has multiple exits.";
+}
+
+bool ReportLoopHasMultipleExits::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::LoopHasMultipleExits;
+}
+
+const DebugLoc &ReportLoopHasMultipleExits::getDebugLoc() const { return Loc; }
+
+std::string ReportLoopHasMultipleExits::getEndUserMessage() const {
+ return "Loop cannot be handled because it has multiple exits.";
+}
+
+//===----------------------------------------------------------------------===//
+// ReportLoopOnlySomeLatches
+
+std::string ReportLoopOnlySomeLatches::getRemarkName() const {
+ return "LoopHasNoExit";
+}
+
+const Value *ReportLoopOnlySomeLatches::getRemarkBB() const {
+ return L->getHeader();
+}
+
+std::string ReportLoopOnlySomeLatches::getMessage() const {
+ return "Not all latches of loop " + L->getHeader()->getName() +
+ " part of scop.";
+}
+
+bool ReportLoopOnlySomeLatches::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::LoopHasNoExit;
+}
+
+const DebugLoc &ReportLoopOnlySomeLatches::getDebugLoc() const { return Loc; }
+
+std::string ReportLoopOnlySomeLatches::getEndUserMessage() const {
+ return "Loop cannot be handled because not all latches are part of loop "
+ "region.";
+}
+
+//===----------------------------------------------------------------------===//
+// ReportFuncCall.
+
+ReportFuncCall::ReportFuncCall(Instruction *Inst)
+ : RejectReason(RejectReasonKind::FuncCall), Inst(Inst) {}
+
+std::string ReportFuncCall::getRemarkName() const { return "FuncCall"; }
+
+const Value *ReportFuncCall::getRemarkBB() const { return Inst->getParent(); }
+
+std::string ReportFuncCall::getMessage() const {
+ return "Call instruction: " + *Inst;
+}
+
+const DebugLoc &ReportFuncCall::getDebugLoc() const {
+ return Inst->getDebugLoc();
+}
+
+std::string ReportFuncCall::getEndUserMessage() const {
+ return "This function call cannot be handled. "
+ "Try to inline it.";
+}
+
+bool ReportFuncCall::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::FuncCall;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportNonSimpleMemoryAccess
+
+ReportNonSimpleMemoryAccess::ReportNonSimpleMemoryAccess(Instruction *Inst)
+ : ReportOther(RejectReasonKind::NonSimpleMemoryAccess), Inst(Inst) {}
+
+std::string ReportNonSimpleMemoryAccess::getRemarkName() const {
+ return "NonSimpleMemoryAccess";
+}
+
+const Value *ReportNonSimpleMemoryAccess::getRemarkBB() const {
+ return Inst->getParent();
+}
+
+std::string ReportNonSimpleMemoryAccess::getMessage() const {
+ return "Non-simple memory access: " + *Inst;
+}
+
+const DebugLoc &ReportNonSimpleMemoryAccess::getDebugLoc() const {
+ return Inst->getDebugLoc();
+}
+
+std::string ReportNonSimpleMemoryAccess::getEndUserMessage() const {
+ return "Volatile memory accesses or memory accesses for atomic types "
+ "are not supported.";
+}
+
+bool ReportNonSimpleMemoryAccess::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::NonSimpleMemoryAccess;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportAlias.
+
+ReportAlias::ReportAlias(Instruction *Inst, AliasSet &AS)
+ : RejectReason(RejectReasonKind::Alias), Inst(Inst) {
+ for (const auto &I : AS)
+ Pointers.push_back(I.getValue());
+}
+
+std::string ReportAlias::formatInvalidAlias(std::string Prefix,
+ std::string Suffix) const {
+ std::string Message;
+ raw_string_ostream OS(Message);
+
+ OS << Prefix;
+
+ for (PointerSnapshotTy::const_iterator PI = Pointers.begin(),
+ PE = Pointers.end();
+ ;) {
+ const Value *V = *PI;
+ assert(V && "Diagnostic info does not match found LLVM-IR anymore.");
+
+ if (V->getName().empty())
+ OS << "\" <unknown> \"";
+ else
+ OS << "\"" << V->getName() << "\"";
+
+ ++PI;
+
+ if (PI != PE)
+ OS << ", ";
+ else
+ break;
+ }
+
+ OS << Suffix;
+
+ return OS.str();
+}
+
+std::string ReportAlias::getRemarkName() const { return "Alias"; }
+
+const Value *ReportAlias::getRemarkBB() const { return Inst->getParent(); }
+
+std::string ReportAlias::getMessage() const {
+ return formatInvalidAlias("Possible aliasing: ");
+}
+
+std::string ReportAlias::getEndUserMessage() const {
+ return formatInvalidAlias("Accesses to the arrays ",
+ " may access the same memory.");
+}
+
+const DebugLoc &ReportAlias::getDebugLoc() const { return Inst->getDebugLoc(); }
+
+bool ReportAlias::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::Alias;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportOther.
+
+std::string ReportOther::getRemarkName() const { return "UnknownRejectReason"; }
+
+std::string ReportOther::getMessage() const { return "Unknown reject reason"; }
+
+ReportOther::ReportOther(const RejectReasonKind K) : RejectReason(K) {}
+
+bool ReportOther::classof(const RejectReason *RR) {
+ return RR->getKind() >= RejectReasonKind::Other &&
+ RR->getKind() <= RejectReasonKind::LastOther;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportIntToPtr.
+ReportIntToPtr::ReportIntToPtr(Instruction *BaseValue)
+ : ReportOther(RejectReasonKind::IntToPtr), BaseValue(BaseValue) {}
+
+std::string ReportIntToPtr::getRemarkName() const { return "IntToPtr"; }
+
+const Value *ReportIntToPtr::getRemarkBB() const {
+ return BaseValue->getParent();
+}
+
+std::string ReportIntToPtr::getMessage() const {
+ return "Find bad intToptr prt: " + *BaseValue;
+}
+
+const DebugLoc &ReportIntToPtr::getDebugLoc() const {
+ return BaseValue->getDebugLoc();
+}
+
+bool ReportIntToPtr::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::IntToPtr;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportAlloca.
+
+ReportAlloca::ReportAlloca(Instruction *Inst)
+ : ReportOther(RejectReasonKind::Alloca), Inst(Inst) {}
+
+std::string ReportAlloca::getRemarkName() const { return "Alloca"; }
+
+const Value *ReportAlloca::getRemarkBB() const { return Inst->getParent(); }
+
+std::string ReportAlloca::getMessage() const {
+ return "Alloca instruction: " + *Inst;
+}
+
+const DebugLoc &ReportAlloca::getDebugLoc() const {
+ return Inst->getDebugLoc();
+}
+
+bool ReportAlloca::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::Alloca;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportUnknownInst.
+
+ReportUnknownInst::ReportUnknownInst(Instruction *Inst)
+ : ReportOther(RejectReasonKind::UnknownInst), Inst(Inst) {}
+
+std::string ReportUnknownInst::getRemarkName() const { return "UnknownInst"; }
+
+const Value *ReportUnknownInst::getRemarkBB() const {
+ return Inst->getParent();
+}
+
+std::string ReportUnknownInst::getMessage() const {
+ return "Unknown instruction: " + *Inst;
+}
+
+const DebugLoc &ReportUnknownInst::getDebugLoc() const {
+ return Inst->getDebugLoc();
+}
+
+bool ReportUnknownInst::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::UnknownInst;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportEntry.
+
+ReportEntry::ReportEntry(BasicBlock *BB)
+ : ReportOther(RejectReasonKind::Entry), BB(BB) {}
+
+std::string ReportEntry::getRemarkName() const { return "Entry"; }
+
+const Value *ReportEntry::getRemarkBB() const { return BB; }
+
+std::string ReportEntry::getMessage() const {
+ return "Region containing entry block of function is invalid!";
+}
+
+std::string ReportEntry::getEndUserMessage() const {
+ return "Scop contains function entry (not yet supported).";
+}
+
+const DebugLoc &ReportEntry::getDebugLoc() const {
+ return BB->getTerminator()->getDebugLoc();
+}
+
+bool ReportEntry::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::Entry;
+}
+
+//===----------------------------------------------------------------------===//
+// ReportUnprofitable.
+
+ReportUnprofitable::ReportUnprofitable(Region *R)
+ : ReportOther(RejectReasonKind::Unprofitable), R(R) {}
+
+std::string ReportUnprofitable::getRemarkName() const { return "Unprofitable"; }
+
+const Value *ReportUnprofitable::getRemarkBB() const { return R->getEntry(); }
+
+std::string ReportUnprofitable::getMessage() const {
+ return "Region can not profitably be optimized!";
+}
+
+std::string ReportUnprofitable::getEndUserMessage() const {
+ return "No profitable polyhedral optimization found";
+}
+
+const DebugLoc &ReportUnprofitable::getDebugLoc() const {
+ for (const BasicBlock *BB : R->blocks())
+ for (const Instruction &Inst : *BB)
+ if (const DebugLoc &DL = Inst.getDebugLoc())
+ return DL;
+
+ return R->getEntry()->getTerminator()->getDebugLoc();
+}
+
+bool ReportUnprofitable::classof(const RejectReason *RR) {
+ return RR->getKind() == RejectReasonKind::Unprofitable;
+}
+} // namespace polly
diff --git a/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopGraphPrinter.cpp b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopGraphPrinter.cpp
new file mode 100644
index 00000000000..1092d5aa4a4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopGraphPrinter.cpp
@@ -0,0 +1,263 @@
+//===- GraphPrinter.cpp - Create a DOT output describing the Scop. --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Create a DOT output describing the Scop.
+//
+// For each function a dot file is created that shows the control flow graph of
+// the function and highlights the detected Scops.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/LinkAllPasses.h"
+#include "polly/ScopDetection.h"
+#include "polly/Support/ScopLocation.h"
+#include "llvm/Analysis/DOTGraphTraitsPass.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/RegionIterator.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace polly;
+using namespace llvm;
+static cl::opt<std::string>
+ ViewFilter("polly-view-only",
+ cl::desc("Only view functions that match this pattern"),
+ cl::Hidden, cl::init(""), cl::ZeroOrMore);
+
+static cl::opt<bool> ViewAll("polly-view-all",
+ cl::desc("Also show functions without any scops"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore);
+
+namespace llvm {
+template <>
+struct GraphTraits<ScopDetection *> : public GraphTraits<RegionInfo *> {
+ static NodeRef getEntryNode(ScopDetection *SD) {
+ return GraphTraits<RegionInfo *>::getEntryNode(SD->getRI());
+ }
+ static nodes_iterator nodes_begin(ScopDetection *SD) {
+ return nodes_iterator::begin(getEntryNode(SD));
+ }
+ static nodes_iterator nodes_end(ScopDetection *SD) {
+ return nodes_iterator::end(getEntryNode(SD));
+ }
+};
+
+template <>
+struct GraphTraits<ScopDetectionWrapperPass *>
+ : public GraphTraits<ScopDetection *> {
+ static NodeRef getEntryNode(ScopDetectionWrapperPass *P) {
+ return GraphTraits<ScopDetection *>::getEntryNode(&P->getSD());
+ }
+ static nodes_iterator nodes_begin(ScopDetectionWrapperPass *P) {
+ return nodes_iterator::begin(getEntryNode(P));
+ }
+ static nodes_iterator nodes_end(ScopDetectionWrapperPass *P) {
+ return nodes_iterator::end(getEntryNode(P));
+ }
+};
+
+template <> struct DOTGraphTraits<RegionNode *> : public DefaultDOTGraphTraits {
+ DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
+
+ std::string getNodeLabel(RegionNode *Node, RegionNode *Graph) {
+ if (!Node->isSubRegion()) {
+ BasicBlock *BB = Node->getNodeAs<BasicBlock>();
+
+ if (isSimple())
+ return DOTGraphTraits<DOTFuncInfo *>::getSimpleNodeLabel(BB, nullptr);
+
+ else
+ return DOTGraphTraits<DOTFuncInfo *>::getCompleteNodeLabel(BB, nullptr);
+ }
+
+ return "Not implemented";
+ }
+};
+
+template <>
+struct DOTGraphTraits<ScopDetectionWrapperPass *>
+ : public DOTGraphTraits<RegionNode *> {
+ DOTGraphTraits(bool isSimple = false)
+ : DOTGraphTraits<RegionNode *>(isSimple) {}
+ static std::string getGraphName(ScopDetectionWrapperPass *SD) {
+ return "Scop Graph";
+ }
+
+ std::string getEdgeAttributes(RegionNode *srcNode,
+ GraphTraits<RegionInfo *>::ChildIteratorType CI,
+ ScopDetectionWrapperPass *P) {
+ RegionNode *destNode = *CI;
+ auto *SD = &P->getSD();
+
+ if (srcNode->isSubRegion() || destNode->isSubRegion())
+ return "";
+
+ // In case of a backedge, do not use it to define the layout of the nodes.
+ BasicBlock *srcBB = srcNode->getNodeAs<BasicBlock>();
+ BasicBlock *destBB = destNode->getNodeAs<BasicBlock>();
+
+ RegionInfo *RI = SD->getRI();
+ Region *R = RI->getRegionFor(destBB);
+
+ while (R && R->getParent())
+ if (R->getParent()->getEntry() == destBB)
+ R = R->getParent();
+ else
+ break;
+
+ if (R && R->getEntry() == destBB && R->contains(srcBB))
+ return "constraint=false";
+
+ return "";
+ }
+
+ std::string getNodeLabel(RegionNode *Node, ScopDetectionWrapperPass *P) {
+ return DOTGraphTraits<RegionNode *>::getNodeLabel(
+ Node, reinterpret_cast<RegionNode *>(
+ P->getSD().getRI()->getTopLevelRegion()));
+ }
+
+ static std::string escapeString(std::string String) {
+ std::string Escaped;
+
+ for (const auto &C : String) {
+ if (C == '"')
+ Escaped += '\\';
+
+ Escaped += C;
+ }
+ return Escaped;
+ }
+
+ // Print the cluster of the subregions. This groups the single basic blocks
+ // and adds a different background color for each group.
+ static void printRegionCluster(ScopDetection *SD, const Region *R,
+ raw_ostream &O, unsigned depth = 0) {
+ O.indent(2 * depth) << "subgraph cluster_" << static_cast<const void *>(R)
+ << " {\n";
+ unsigned LineBegin, LineEnd;
+ std::string FileName;
+
+ getDebugLocation(R, LineBegin, LineEnd, FileName);
+
+ std::string Location;
+ if (LineBegin != (unsigned)-1) {
+ Location = escapeString(FileName + ":" + std::to_string(LineBegin) + "-" +
+ std::to_string(LineEnd) + "\n");
+ }
+
+ std::string ErrorMessage = SD->regionIsInvalidBecause(R);
+ ErrorMessage = escapeString(ErrorMessage);
+ O.indent(2 * (depth + 1))
+ << "label = \"" << Location << ErrorMessage << "\";\n";
+
+ if (SD->isMaxRegionInScop(*R)) {
+ O.indent(2 * (depth + 1)) << "style = filled;\n";
+
+ // Set color to green.
+ O.indent(2 * (depth + 1)) << "color = 3";
+ } else {
+ O.indent(2 * (depth + 1)) << "style = solid;\n";
+
+ int color = (R->getDepth() * 2 % 12) + 1;
+
+ // We do not want green again.
+ if (color == 3)
+ color = 6;
+
+ O.indent(2 * (depth + 1)) << "color = " << color << "\n";
+ }
+
+ for (const auto &SubRegion : *R)
+ printRegionCluster(SD, SubRegion.get(), O, depth + 1);
+
+ RegionInfo *RI = R->getRegionInfo();
+
+ for (BasicBlock *BB : R->blocks())
+ if (RI->getRegionFor(BB) == R)
+ O.indent(2 * (depth + 1))
+ << "Node"
+ << static_cast<void *>(RI->getTopLevelRegion()->getBBNode(BB))
+ << ";\n";
+
+ O.indent(2 * depth) << "}\n";
+ }
+ static void
+ addCustomGraphFeatures(const ScopDetectionWrapperPass *SD,
+ GraphWriter<ScopDetectionWrapperPass *> &GW) {
+ raw_ostream &O = GW.getOStream();
+ O << "\tcolorscheme = \"paired12\"\n";
+ printRegionCluster(&SD->getSD(), SD->getSD().getRI()->getTopLevelRegion(),
+ O, 4);
+ }
+};
+} // end namespace llvm
+
+struct ScopViewer
+ : public DOTGraphTraitsViewer<ScopDetectionWrapperPass, false> {
+ static char ID;
+ ScopViewer()
+ : DOTGraphTraitsViewer<ScopDetectionWrapperPass, false>("scops", ID) {}
+ bool processFunction(Function &F, ScopDetectionWrapperPass &SD) override {
+ if (ViewFilter != "" && !F.getName().count(ViewFilter))
+ return false;
+
+ if (ViewAll)
+ return true;
+
+ // Check that at least one scop was detected.
+ return std::distance(SD.getSD().begin(), SD.getSD().end()) > 0;
+ }
+};
+char ScopViewer::ID = 0;
+
+struct ScopOnlyViewer
+ : public DOTGraphTraitsViewer<ScopDetectionWrapperPass, true> {
+ static char ID;
+ ScopOnlyViewer()
+ : DOTGraphTraitsViewer<ScopDetectionWrapperPass, true>("scopsonly", ID) {}
+};
+char ScopOnlyViewer::ID = 0;
+
+struct ScopPrinter
+ : public DOTGraphTraitsPrinter<ScopDetectionWrapperPass, false> {
+ static char ID;
+ ScopPrinter()
+ : DOTGraphTraitsPrinter<ScopDetectionWrapperPass, false>("scops", ID) {}
+};
+char ScopPrinter::ID = 0;
+
+struct ScopOnlyPrinter
+ : public DOTGraphTraitsPrinter<ScopDetectionWrapperPass, true> {
+ static char ID;
+ ScopOnlyPrinter()
+ : DOTGraphTraitsPrinter<ScopDetectionWrapperPass, true>("scopsonly", ID) {
+ }
+};
+char ScopOnlyPrinter::ID = 0;
+
+static RegisterPass<ScopViewer> X("view-scops",
+ "Polly - View Scops of function");
+
+static RegisterPass<ScopOnlyViewer>
+ Y("view-scops-only",
+ "Polly - View Scops of function (with no function bodies)");
+
+static RegisterPass<ScopPrinter> M("dot-scops",
+ "Polly - Print Scops of function");
+
+static RegisterPass<ScopOnlyPrinter>
+ N("dot-scops-only",
+ "Polly - Print Scops of function (with no function bodies)");
+
+Pass *polly::createDOTViewerPass() { return new ScopViewer(); }
+
+Pass *polly::createDOTOnlyViewerPass() { return new ScopOnlyViewer(); }
+
+Pass *polly::createDOTPrinterPass() { return new ScopPrinter(); }
+
+Pass *polly::createDOTOnlyPrinterPass() { return new ScopOnlyPrinter(); }
diff --git a/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopInfo.cpp b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopInfo.cpp
new file mode 100644
index 00000000000..1115832a452
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopInfo.cpp
@@ -0,0 +1,2775 @@
+//===- ScopInfo.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Create a polyhedral description for a static control flow region.
+//
+// The pass creates a polyhedral description of the Scops detected by the Scop
+// detection derived from their LLVM-IR code.
+//
+// This representation is shared among several tools in the polyhedral
+// community, which are e.g. Cloog, Pluto, Loopo, Graphite.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/ScopInfo.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "polly/ScopBuilder.h"
+#include "polly/ScopDetection.h"
+#include "polly/Support/GICHelper.h"
+#include "polly/Support/ISLOStream.h"
+#include "polly/Support/ISLTools.h"
+#include "polly/Support/SCEVAffinator.h"
+#include "polly/Support/SCEVValidator.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/Analysis/AssumptionCache.h"
+#include "llvm/Analysis/Loads.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/RegionIterator.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/ConstantRange.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DebugLoc.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include "isl/aff.h"
+#include "isl/local_space.h"
+#include "isl/map.h"
+#include "isl/options.h"
+#include "isl/set.h"
+#include <cassert>
+
+using namespace llvm;
+using namespace polly;
+
+#define DEBUG_TYPE "polly-scops"
+
+STATISTIC(AssumptionsAliasing, "Number of aliasing assumptions taken.");
+STATISTIC(AssumptionsInbounds, "Number of inbounds assumptions taken.");
+STATISTIC(AssumptionsWrapping, "Number of wrapping assumptions taken.");
+STATISTIC(AssumptionsUnsigned, "Number of unsigned assumptions taken.");
+STATISTIC(AssumptionsComplexity, "Number of too complex SCoPs.");
+STATISTIC(AssumptionsUnprofitable, "Number of unprofitable SCoPs.");
+STATISTIC(AssumptionsErrorBlock, "Number of error block assumptions taken.");
+STATISTIC(AssumptionsInfiniteLoop, "Number of bounded loop assumptions taken.");
+STATISTIC(AssumptionsInvariantLoad,
+ "Number of invariant loads assumptions taken.");
+STATISTIC(AssumptionsDelinearization,
+ "Number of delinearization assumptions taken.");
+
+STATISTIC(NumScops, "Number of feasible SCoPs after ScopInfo");
+STATISTIC(NumLoopsInScop, "Number of loops in scops");
+STATISTIC(NumBoxedLoops, "Number of boxed loops in SCoPs after ScopInfo");
+STATISTIC(NumAffineLoops, "Number of affine loops in SCoPs after ScopInfo");
+
+STATISTIC(NumScopsDepthZero, "Number of scops with maximal loop depth 0");
+STATISTIC(NumScopsDepthOne, "Number of scops with maximal loop depth 1");
+STATISTIC(NumScopsDepthTwo, "Number of scops with maximal loop depth 2");
+STATISTIC(NumScopsDepthThree, "Number of scops with maximal loop depth 3");
+STATISTIC(NumScopsDepthFour, "Number of scops with maximal loop depth 4");
+STATISTIC(NumScopsDepthFive, "Number of scops with maximal loop depth 5");
+STATISTIC(NumScopsDepthLarger,
+ "Number of scops with maximal loop depth 6 and larger");
+STATISTIC(MaxNumLoopsInScop, "Maximal number of loops in scops");
+
+STATISTIC(NumValueWrites, "Number of scalar value writes after ScopInfo");
+STATISTIC(
+ NumValueWritesInLoops,
+ "Number of scalar value writes nested in affine loops after ScopInfo");
+STATISTIC(NumPHIWrites, "Number of scalar phi writes after ScopInfo");
+STATISTIC(NumPHIWritesInLoops,
+ "Number of scalar phi writes nested in affine loops after ScopInfo");
+STATISTIC(NumSingletonWrites, "Number of singleton writes after ScopInfo");
+STATISTIC(NumSingletonWritesInLoops,
+ "Number of singleton writes nested in affine loops after ScopInfo");
+
+unsigned const polly::MaxDisjunctsInDomain = 20;
+
+// The number of disjunct in the context after which we stop to add more
+// disjuncts. This parameter is there to avoid exponential growth in the
+// number of disjunct when adding non-convex sets to the context.
+static int const MaxDisjunctsInContext = 4;
+
+// Be a bit more generous for the defined behavior context which is used less
+// often.
+static int const MaxDisjunktsInDefinedBehaviourContext = 8;
+
+static cl::opt<bool> PollyRemarksMinimal(
+ "polly-remarks-minimal",
+ cl::desc("Do not emit remarks about assumptions that are known"),
+ cl::Hidden, cl::ZeroOrMore, cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ IslOnErrorAbort("polly-on-isl-error-abort",
+ cl::desc("Abort if an isl error is encountered"),
+ cl::init(true), cl::cat(PollyCategory));
+
+static cl::opt<bool> PollyPreciseInbounds(
+ "polly-precise-inbounds",
+ cl::desc("Take more precise inbounds assumptions (do not scale well)"),
+ cl::Hidden, cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<bool> PollyIgnoreParamBounds(
+ "polly-ignore-parameter-bounds",
+ cl::desc(
+ "Do not add parameter bounds and do no gist simplify sets accordingly"),
+ cl::Hidden, cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<bool> PollyPreciseFoldAccesses(
+ "polly-precise-fold-accesses",
+ cl::desc("Fold memory accesses to model more possible delinearizations "
+ "(does not scale well)"),
+ cl::Hidden, cl::init(false), cl::cat(PollyCategory));
+
+bool polly::UseInstructionNames;
+
+static cl::opt<bool, true> XUseInstructionNames(
+ "polly-use-llvm-names",
+ cl::desc("Use LLVM-IR names when deriving statement names"),
+ cl::location(UseInstructionNames), cl::Hidden, cl::init(false),
+ cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> PollyPrintInstructions(
+ "polly-print-instructions", cl::desc("Output instructions per ScopStmt"),
+ cl::Hidden, cl::Optional, cl::init(false), cl::cat(PollyCategory));
+
+static cl::list<std::string> IslArgs("polly-isl-arg",
+ cl::value_desc("argument"),
+ cl::desc("Option passed to ISL"),
+ cl::ZeroOrMore, cl::cat(PollyCategory));
+
+//===----------------------------------------------------------------------===//
+
+static isl::set addRangeBoundsToSet(isl::set S, const ConstantRange &Range,
+ int dim, isl::dim type) {
+ isl::val V;
+ isl::ctx Ctx = S.ctx();
+
+ // The upper and lower bound for a parameter value is derived either from
+ // the data type of the parameter or from the - possibly more restrictive -
+ // range metadata.
+ V = valFromAPInt(Ctx.get(), Range.getSignedMin(), true);
+ S = S.lower_bound_val(type, dim, V);
+ V = valFromAPInt(Ctx.get(), Range.getSignedMax(), true);
+ S = S.upper_bound_val(type, dim, V);
+
+ if (Range.isFullSet())
+ return S;
+
+ if (S.n_basic_set().release() > MaxDisjunctsInContext)
+ return S;
+
+ // In case of signed wrapping, we can refine the set of valid values by
+ // excluding the part not covered by the wrapping range.
+ if (Range.isSignWrappedSet()) {
+ V = valFromAPInt(Ctx.get(), Range.getLower(), true);
+ isl::set SLB = S.lower_bound_val(type, dim, V);
+
+ V = valFromAPInt(Ctx.get(), Range.getUpper(), true);
+ V = V.sub(1);
+ isl::set SUB = S.upper_bound_val(type, dim, V);
+ S = SLB.unite(SUB);
+ }
+
+ return S;
+}
+
+static const ScopArrayInfo *identifyBasePtrOriginSAI(Scop *S, Value *BasePtr) {
+ LoadInst *BasePtrLI = dyn_cast<LoadInst>(BasePtr);
+ if (!BasePtrLI)
+ return nullptr;
+
+ if (!S->contains(BasePtrLI))
+ return nullptr;
+
+ ScalarEvolution &SE = *S->getSE();
+
+ auto *OriginBaseSCEV =
+ SE.getPointerBase(SE.getSCEV(BasePtrLI->getPointerOperand()));
+ if (!OriginBaseSCEV)
+ return nullptr;
+
+ auto *OriginBaseSCEVUnknown = dyn_cast<SCEVUnknown>(OriginBaseSCEV);
+ if (!OriginBaseSCEVUnknown)
+ return nullptr;
+
+ return S->getScopArrayInfo(OriginBaseSCEVUnknown->getValue(),
+ MemoryKind::Array);
+}
+
+ScopArrayInfo::ScopArrayInfo(Value *BasePtr, Type *ElementType, isl::ctx Ctx,
+ ArrayRef<const SCEV *> Sizes, MemoryKind Kind,
+ const DataLayout &DL, Scop *S,
+ const char *BaseName)
+ : BasePtr(BasePtr), ElementType(ElementType), Kind(Kind), DL(DL), S(*S) {
+ std::string BasePtrName =
+ BaseName ? BaseName
+ : getIslCompatibleName("MemRef", BasePtr, S->getNextArrayIdx(),
+ Kind == MemoryKind::PHI ? "__phi" : "",
+ UseInstructionNames);
+ Id = isl::id::alloc(Ctx, BasePtrName, this);
+
+ updateSizes(Sizes);
+
+ if (!BasePtr || Kind != MemoryKind::Array) {
+ BasePtrOriginSAI = nullptr;
+ return;
+ }
+
+ BasePtrOriginSAI = identifyBasePtrOriginSAI(S, BasePtr);
+ if (BasePtrOriginSAI)
+ const_cast<ScopArrayInfo *>(BasePtrOriginSAI)->addDerivedSAI(this);
+}
+
+ScopArrayInfo::~ScopArrayInfo() = default;
+
+isl::space ScopArrayInfo::getSpace() const {
+ auto Space = isl::space(Id.ctx(), 0, getNumberOfDimensions());
+ Space = Space.set_tuple_id(isl::dim::set, Id);
+ return Space;
+}
+
+bool ScopArrayInfo::isReadOnly() {
+ isl::union_set WriteSet = S.getWrites().range();
+ isl::space Space = getSpace();
+ WriteSet = WriteSet.extract_set(Space);
+
+ return bool(WriteSet.is_empty());
+}
+
+bool ScopArrayInfo::isCompatibleWith(const ScopArrayInfo *Array) const {
+ if (Array->getElementType() != getElementType())
+ return false;
+
+ if (Array->getNumberOfDimensions() != getNumberOfDimensions())
+ return false;
+
+ for (unsigned i = 0; i < getNumberOfDimensions(); i++)
+ if (Array->getDimensionSize(i) != getDimensionSize(i))
+ return false;
+
+ return true;
+}
+
+void ScopArrayInfo::updateElementType(Type *NewElementType) {
+ if (NewElementType == ElementType)
+ return;
+
+ auto OldElementSize = DL.getTypeAllocSizeInBits(ElementType);
+ auto NewElementSize = DL.getTypeAllocSizeInBits(NewElementType);
+
+ if (NewElementSize == OldElementSize || NewElementSize == 0)
+ return;
+
+ if (NewElementSize % OldElementSize == 0 && NewElementSize < OldElementSize) {
+ ElementType = NewElementType;
+ } else {
+ auto GCD = GreatestCommonDivisor64(NewElementSize, OldElementSize);
+ ElementType = IntegerType::get(ElementType->getContext(), GCD);
+ }
+}
+
+bool ScopArrayInfo::updateSizes(ArrayRef<const SCEV *> NewSizes,
+ bool CheckConsistency) {
+ int SharedDims = std::min(NewSizes.size(), DimensionSizes.size());
+ int ExtraDimsNew = NewSizes.size() - SharedDims;
+ int ExtraDimsOld = DimensionSizes.size() - SharedDims;
+
+ if (CheckConsistency) {
+ for (int i = 0; i < SharedDims; i++) {
+ auto *NewSize = NewSizes[i + ExtraDimsNew];
+ auto *KnownSize = DimensionSizes[i + ExtraDimsOld];
+ if (NewSize && KnownSize && NewSize != KnownSize)
+ return false;
+ }
+
+ if (DimensionSizes.size() >= NewSizes.size())
+ return true;
+ }
+
+ DimensionSizes.clear();
+ DimensionSizes.insert(DimensionSizes.begin(), NewSizes.begin(),
+ NewSizes.end());
+ DimensionSizesPw.clear();
+ for (const SCEV *Expr : DimensionSizes) {
+ if (!Expr) {
+ DimensionSizesPw.push_back(isl::pw_aff());
+ continue;
+ }
+ isl::pw_aff Size = S.getPwAffOnly(Expr);
+ DimensionSizesPw.push_back(Size);
+ }
+ return true;
+}
+
+std::string ScopArrayInfo::getName() const { return Id.get_name(); }
+
+int ScopArrayInfo::getElemSizeInBytes() const {
+ return DL.getTypeAllocSize(ElementType);
+}
+
+isl::id ScopArrayInfo::getBasePtrId() const { return Id; }
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void ScopArrayInfo::dump() const { print(errs()); }
+#endif
+
+void ScopArrayInfo::print(raw_ostream &OS, bool SizeAsPwAff) const {
+ OS.indent(8) << *getElementType() << " " << getName();
+ unsigned u = 0;
+
+ if (getNumberOfDimensions() > 0 && !getDimensionSize(0)) {
+ OS << "[*]";
+ u++;
+ }
+ for (; u < getNumberOfDimensions(); u++) {
+ OS << "[";
+
+ if (SizeAsPwAff) {
+ isl::pw_aff Size = getDimensionSizePw(u);
+ OS << " " << Size << " ";
+ } else {
+ OS << *getDimensionSize(u);
+ }
+
+ OS << "]";
+ }
+
+ OS << ";";
+
+ if (BasePtrOriginSAI)
+ OS << " [BasePtrOrigin: " << BasePtrOriginSAI->getName() << "]";
+
+ OS << " // Element size " << getElemSizeInBytes() << "\n";
+}
+
+const ScopArrayInfo *
+ScopArrayInfo::getFromAccessFunction(isl::pw_multi_aff PMA) {
+ isl::id Id = PMA.get_tuple_id(isl::dim::out);
+ assert(!Id.is_null() && "Output dimension didn't have an ID");
+ return getFromId(Id);
+}
+
+const ScopArrayInfo *ScopArrayInfo::getFromId(isl::id Id) {
+ void *User = Id.get_user();
+ const ScopArrayInfo *SAI = static_cast<ScopArrayInfo *>(User);
+ return SAI;
+}
+
+void MemoryAccess::wrapConstantDimensions() {
+ auto *SAI = getScopArrayInfo();
+ isl::space ArraySpace = SAI->getSpace();
+ isl::ctx Ctx = ArraySpace.ctx();
+ unsigned DimsArray = SAI->getNumberOfDimensions();
+
+ isl::multi_aff DivModAff = isl::multi_aff::identity(
+ ArraySpace.map_from_domain_and_range(ArraySpace));
+ isl::local_space LArraySpace = isl::local_space(ArraySpace);
+
+ // Begin with last dimension, to iteratively carry into higher dimensions.
+ for (int i = DimsArray - 1; i > 0; i--) {
+ auto *DimSize = SAI->getDimensionSize(i);
+ auto *DimSizeCst = dyn_cast<SCEVConstant>(DimSize);
+
+ // This transformation is not applicable to dimensions with dynamic size.
+ if (!DimSizeCst)
+ continue;
+
+ // This transformation is not applicable to dimensions of size zero.
+ if (DimSize->isZero())
+ continue;
+
+ isl::val DimSizeVal =
+ valFromAPInt(Ctx.get(), DimSizeCst->getAPInt(), false);
+ isl::aff Var = isl::aff::var_on_domain(LArraySpace, isl::dim::set, i);
+ isl::aff PrevVar =
+ isl::aff::var_on_domain(LArraySpace, isl::dim::set, i - 1);
+
+ // Compute: index % size
+ // Modulo must apply in the divide of the previous iteration, if any.
+ isl::aff Modulo = Var.mod(DimSizeVal);
+ Modulo = Modulo.pullback(DivModAff);
+
+ // Compute: floor(index / size)
+ isl::aff Divide = Var.div(isl::aff(LArraySpace, DimSizeVal));
+ Divide = Divide.floor();
+ Divide = Divide.add(PrevVar);
+ Divide = Divide.pullback(DivModAff);
+
+ // Apply Modulo and Divide.
+ DivModAff = DivModAff.set_aff(i, Modulo);
+ DivModAff = DivModAff.set_aff(i - 1, Divide);
+ }
+
+ // Apply all modulo/divides on the accesses.
+ isl::map Relation = AccessRelation;
+ Relation = Relation.apply_range(isl::map::from_multi_aff(DivModAff));
+ Relation = Relation.detect_equalities();
+ AccessRelation = Relation;
+}
+
+void MemoryAccess::updateDimensionality() {
+ auto *SAI = getScopArrayInfo();
+ isl::space ArraySpace = SAI->getSpace();
+ isl::space AccessSpace = AccessRelation.get_space().range();
+ isl::ctx Ctx = ArraySpace.ctx();
+
+ unsigned DimsArray = unsignedFromIslSize(ArraySpace.dim(isl::dim::set));
+ unsigned DimsAccess = unsignedFromIslSize(AccessSpace.dim(isl::dim::set));
+ assert(DimsArray >= DimsAccess);
+ unsigned DimsMissing = DimsArray - DimsAccess;
+
+ auto *BB = getStatement()->getEntryBlock();
+ auto &DL = BB->getModule()->getDataLayout();
+ unsigned ArrayElemSize = SAI->getElemSizeInBytes();
+ unsigned ElemBytes = DL.getTypeAllocSize(getElementType());
+
+ isl::map Map = isl::map::from_domain_and_range(
+ isl::set::universe(AccessSpace), isl::set::universe(ArraySpace));
+
+ for (auto i : seq<unsigned>(0, DimsMissing))
+ Map = Map.fix_si(isl::dim::out, i, 0);
+
+ for (auto i : seq<unsigned>(DimsMissing, DimsArray))
+ Map = Map.equate(isl::dim::in, i - DimsMissing, isl::dim::out, i);
+
+ AccessRelation = AccessRelation.apply_range(Map);
+
+ // For the non delinearized arrays, divide the access function of the last
+ // subscript by the size of the elements in the array.
+ //
+ // A stride one array access in C expressed as A[i] is expressed in
+ // LLVM-IR as something like A[i * elementsize]. This hides the fact that
+ // two subsequent values of 'i' index two values that are stored next to
+ // each other in memory. By this division we make this characteristic
+ // obvious again. If the base pointer was accessed with offsets not divisible
+ // by the accesses element size, we will have chosen a smaller ArrayElemSize
+ // that divides the offsets of all accesses to this base pointer.
+ if (DimsAccess == 1) {
+ isl::val V = isl::val(Ctx, ArrayElemSize);
+ AccessRelation = AccessRelation.floordiv_val(V);
+ }
+
+ // We currently do this only if we added at least one dimension, which means
+ // some dimension's indices have not been specified, an indicator that some
+ // index values have been added together.
+ // TODO: Investigate general usefulness; Effect on unit tests is to make index
+ // expressions more complicated.
+ if (DimsMissing)
+ wrapConstantDimensions();
+
+ if (!isAffine())
+ computeBoundsOnAccessRelation(ArrayElemSize);
+
+ // Introduce multi-element accesses in case the type loaded by this memory
+ // access is larger than the canonical element type of the array.
+ //
+ // An access ((float *)A)[i] to an array char *A is modeled as
+ // {[i] -> A[o] : 4 i <= o <= 4 i + 3
+ if (ElemBytes > ArrayElemSize) {
+ assert(ElemBytes % ArrayElemSize == 0 &&
+ "Loaded element size should be multiple of canonical element size");
+ assert(DimsArray >= 1);
+ isl::map Map = isl::map::from_domain_and_range(
+ isl::set::universe(ArraySpace), isl::set::universe(ArraySpace));
+ for (auto i : seq<unsigned>(0, DimsArray - 1))
+ Map = Map.equate(isl::dim::in, i, isl::dim::out, i);
+
+ isl::constraint C;
+ isl::local_space LS;
+
+ LS = isl::local_space(Map.get_space());
+ int Num = ElemBytes / getScopArrayInfo()->getElemSizeInBytes();
+
+ C = isl::constraint::alloc_inequality(LS);
+ C = C.set_constant_val(isl::val(Ctx, Num - 1));
+ C = C.set_coefficient_si(isl::dim::in, DimsArray - 1, 1);
+ C = C.set_coefficient_si(isl::dim::out, DimsArray - 1, -1);
+ Map = Map.add_constraint(C);
+
+ C = isl::constraint::alloc_inequality(LS);
+ C = C.set_coefficient_si(isl::dim::in, DimsArray - 1, -1);
+ C = C.set_coefficient_si(isl::dim::out, DimsArray - 1, 1);
+ C = C.set_constant_val(isl::val(Ctx, 0));
+ Map = Map.add_constraint(C);
+ AccessRelation = AccessRelation.apply_range(Map);
+ }
+}
+
+const std::string
+MemoryAccess::getReductionOperatorStr(MemoryAccess::ReductionType RT) {
+ switch (RT) {
+ case MemoryAccess::RT_NONE:
+ llvm_unreachable("Requested a reduction operator string for a memory "
+ "access which isn't a reduction");
+ case MemoryAccess::RT_ADD:
+ return "+";
+ case MemoryAccess::RT_MUL:
+ return "*";
+ case MemoryAccess::RT_BOR:
+ return "|";
+ case MemoryAccess::RT_BXOR:
+ return "^";
+ case MemoryAccess::RT_BAND:
+ return "&";
+ }
+ llvm_unreachable("Unknown reduction type");
+}
+
+const ScopArrayInfo *MemoryAccess::getOriginalScopArrayInfo() const {
+ isl::id ArrayId = getArrayId();
+ void *User = ArrayId.get_user();
+ const ScopArrayInfo *SAI = static_cast<ScopArrayInfo *>(User);
+ return SAI;
+}
+
+const ScopArrayInfo *MemoryAccess::getLatestScopArrayInfo() const {
+ isl::id ArrayId = getLatestArrayId();
+ void *User = ArrayId.get_user();
+ const ScopArrayInfo *SAI = static_cast<ScopArrayInfo *>(User);
+ return SAI;
+}
+
+isl::id MemoryAccess::getOriginalArrayId() const {
+ return AccessRelation.get_tuple_id(isl::dim::out);
+}
+
+isl::id MemoryAccess::getLatestArrayId() const {
+ if (!hasNewAccessRelation())
+ return getOriginalArrayId();
+ return NewAccessRelation.get_tuple_id(isl::dim::out);
+}
+
+isl::map MemoryAccess::getAddressFunction() const {
+ return getAccessRelation().lexmin();
+}
+
+isl::pw_multi_aff
+MemoryAccess::applyScheduleToAccessRelation(isl::union_map USchedule) const {
+ isl::map Schedule, ScheduledAccRel;
+ isl::union_set UDomain;
+
+ UDomain = getStatement()->getDomain();
+ USchedule = USchedule.intersect_domain(UDomain);
+ Schedule = isl::map::from_union_map(USchedule);
+ ScheduledAccRel = getAddressFunction().apply_domain(Schedule);
+ return isl::pw_multi_aff::from_map(ScheduledAccRel);
+}
+
+isl::map MemoryAccess::getOriginalAccessRelation() const {
+ return AccessRelation;
+}
+
+std::string MemoryAccess::getOriginalAccessRelationStr() const {
+ return stringFromIslObj(AccessRelation);
+}
+
+isl::space MemoryAccess::getOriginalAccessRelationSpace() const {
+ return AccessRelation.get_space();
+}
+
+isl::map MemoryAccess::getNewAccessRelation() const {
+ return NewAccessRelation;
+}
+
+std::string MemoryAccess::getNewAccessRelationStr() const {
+ return stringFromIslObj(NewAccessRelation);
+}
+
+std::string MemoryAccess::getAccessRelationStr() const {
+ return stringFromIslObj(getAccessRelation());
+}
+
+isl::basic_map MemoryAccess::createBasicAccessMap(ScopStmt *Statement) {
+ isl::space Space = isl::space(Statement->getIslCtx(), 0, 1);
+ Space = Space.align_params(Statement->getDomainSpace());
+
+ return isl::basic_map::from_domain_and_range(
+ isl::basic_set::universe(Statement->getDomainSpace()),
+ isl::basic_set::universe(Space));
+}
+
+// Formalize no out-of-bound access assumption
+//
+// When delinearizing array accesses we optimistically assume that the
+// delinearized accesses do not access out of bound locations (the subscript
+// expression of each array evaluates for each statement instance that is
+// executed to a value that is larger than zero and strictly smaller than the
+// size of the corresponding dimension). The only exception is the outermost
+// dimension for which we do not need to assume any upper bound. At this point
+// we formalize this assumption to ensure that at code generation time the
+// relevant run-time checks can be generated.
+//
+// To find the set of constraints necessary to avoid out of bound accesses, we
+// first build the set of data locations that are not within array bounds. We
+// then apply the reverse access relation to obtain the set of iterations that
+// may contain invalid accesses and reduce this set of iterations to the ones
+// that are actually executed by intersecting them with the domain of the
+// statement. If we now project out all loop dimensions, we obtain a set of
+// parameters that may cause statement instances to be executed that may
+// possibly yield out of bound memory accesses. The complement of these
+// constraints is the set of constraints that needs to be assumed to ensure such
+// statement instances are never executed.
+isl::set MemoryAccess::assumeNoOutOfBound() {
+ auto *SAI = getScopArrayInfo();
+ isl::space Space = getOriginalAccessRelationSpace().range();
+ isl::set Outside = isl::set::empty(Space);
+ for (int i = 1, Size = Space.dim(isl::dim::set).release(); i < Size; ++i) {
+ isl::local_space LS(Space);
+ isl::pw_aff Var = isl::pw_aff::var_on_domain(LS, isl::dim::set, i);
+ isl::pw_aff Zero = isl::pw_aff(LS);
+
+ isl::set DimOutside = Var.lt_set(Zero);
+ isl::pw_aff SizeE = SAI->getDimensionSizePw(i);
+ SizeE = SizeE.add_dims(isl::dim::in, Space.dim(isl::dim::set).release());
+ SizeE = SizeE.set_tuple_id(isl::dim::in, Space.get_tuple_id(isl::dim::set));
+ DimOutside = DimOutside.unite(SizeE.le_set(Var));
+
+ Outside = Outside.unite(DimOutside);
+ }
+
+ Outside = Outside.apply(getAccessRelation().reverse());
+ Outside = Outside.intersect(Statement->getDomain());
+ Outside = Outside.params();
+
+ // Remove divs to avoid the construction of overly complicated assumptions.
+ // Doing so increases the set of parameter combinations that are assumed to
+ // not appear. This is always save, but may make the resulting run-time check
+ // bail out more often than strictly necessary.
+ Outside = Outside.remove_divs();
+ Outside = Outside.complement();
+
+ if (!PollyPreciseInbounds)
+ Outside = Outside.gist_params(Statement->getDomain().params());
+ return Outside;
+}
+
+void MemoryAccess::buildMemIntrinsicAccessRelation() {
+ assert(isMemoryIntrinsic());
+ assert(Subscripts.size() == 2 && Sizes.size() == 1);
+
+ isl::pw_aff SubscriptPWA = getPwAff(Subscripts[0]);
+ isl::map SubscriptMap = isl::map::from_pw_aff(SubscriptPWA);
+
+ isl::map LengthMap;
+ if (Subscripts[1] == nullptr) {
+ LengthMap = isl::map::universe(SubscriptMap.get_space());
+ } else {
+ isl::pw_aff LengthPWA = getPwAff(Subscripts[1]);
+ LengthMap = isl::map::from_pw_aff(LengthPWA);
+ isl::space RangeSpace = LengthMap.get_space().range();
+ LengthMap = LengthMap.apply_range(isl::map::lex_gt(RangeSpace));
+ }
+ LengthMap = LengthMap.lower_bound_si(isl::dim::out, 0, 0);
+ LengthMap = LengthMap.align_params(SubscriptMap.get_space());
+ SubscriptMap = SubscriptMap.align_params(LengthMap.get_space());
+ LengthMap = LengthMap.sum(SubscriptMap);
+ AccessRelation =
+ LengthMap.set_tuple_id(isl::dim::in, getStatement()->getDomainId());
+}
+
+void MemoryAccess::computeBoundsOnAccessRelation(unsigned ElementSize) {
+ ScalarEvolution *SE = Statement->getParent()->getSE();
+
+ auto MAI = MemAccInst(getAccessInstruction());
+ if (isa<MemIntrinsic>(MAI))
+ return;
+
+ Value *Ptr = MAI.getPointerOperand();
+ if (!Ptr || !SE->isSCEVable(Ptr->getType()))
+ return;
+
+ auto *PtrSCEV = SE->getSCEV(Ptr);
+ if (isa<SCEVCouldNotCompute>(PtrSCEV))
+ return;
+
+ auto *BasePtrSCEV = SE->getPointerBase(PtrSCEV);
+ if (BasePtrSCEV && !isa<SCEVCouldNotCompute>(BasePtrSCEV))
+ PtrSCEV = SE->getMinusSCEV(PtrSCEV, BasePtrSCEV);
+
+ const ConstantRange &Range = SE->getSignedRange(PtrSCEV);
+ if (Range.isFullSet())
+ return;
+
+ if (Range.isUpperWrapped() || Range.isSignWrappedSet())
+ return;
+
+ bool isWrapping = Range.isSignWrappedSet();
+
+ unsigned BW = Range.getBitWidth();
+ const auto One = APInt(BW, 1);
+ const auto LB = isWrapping ? Range.getLower() : Range.getSignedMin();
+ const auto UB = isWrapping ? (Range.getUpper() - One) : Range.getSignedMax();
+
+ auto Min = LB.sdiv(APInt(BW, ElementSize));
+ auto Max = UB.sdiv(APInt(BW, ElementSize)) + One;
+
+ assert(Min.sle(Max) && "Minimum expected to be less or equal than max");
+
+ isl::map Relation = AccessRelation;
+ isl::set AccessRange = Relation.range();
+ AccessRange = addRangeBoundsToSet(AccessRange, ConstantRange(Min, Max), 0,
+ isl::dim::set);
+ AccessRelation = Relation.intersect_range(AccessRange);
+}
+
+void MemoryAccess::foldAccessRelation() {
+ if (Sizes.size() < 2 || isa<SCEVConstant>(Sizes[1]))
+ return;
+
+ int Size = Subscripts.size();
+
+ isl::map NewAccessRelation = AccessRelation;
+
+ for (int i = Size - 2; i >= 0; --i) {
+ isl::space Space;
+ isl::map MapOne, MapTwo;
+ isl::pw_aff DimSize = getPwAff(Sizes[i + 1]);
+
+ isl::space SpaceSize = DimSize.get_space();
+ isl::id ParamId = SpaceSize.get_dim_id(isl::dim::param, 0);
+
+ Space = AccessRelation.get_space();
+ Space = Space.range().map_from_set();
+ Space = Space.align_params(SpaceSize);
+
+ int ParamLocation = Space.find_dim_by_id(isl::dim::param, ParamId);
+
+ MapOne = isl::map::universe(Space);
+ for (int j = 0; j < Size; ++j)
+ MapOne = MapOne.equate(isl::dim::in, j, isl::dim::out, j);
+ MapOne = MapOne.lower_bound_si(isl::dim::in, i + 1, 0);
+
+ MapTwo = isl::map::universe(Space);
+ for (int j = 0; j < Size; ++j)
+ if (j < i || j > i + 1)
+ MapTwo = MapTwo.equate(isl::dim::in, j, isl::dim::out, j);
+
+ isl::local_space LS(Space);
+ isl::constraint C;
+ C = isl::constraint::alloc_equality(LS);
+ C = C.set_constant_si(-1);
+ C = C.set_coefficient_si(isl::dim::in, i, 1);
+ C = C.set_coefficient_si(isl::dim::out, i, -1);
+ MapTwo = MapTwo.add_constraint(C);
+ C = isl::constraint::alloc_equality(LS);
+ C = C.set_coefficient_si(isl::dim::in, i + 1, 1);
+ C = C.set_coefficient_si(isl::dim::out, i + 1, -1);
+ C = C.set_coefficient_si(isl::dim::param, ParamLocation, 1);
+ MapTwo = MapTwo.add_constraint(C);
+ MapTwo = MapTwo.upper_bound_si(isl::dim::in, i + 1, -1);
+
+ MapOne = MapOne.unite(MapTwo);
+ NewAccessRelation = NewAccessRelation.apply_range(MapOne);
+ }
+
+ isl::id BaseAddrId = getScopArrayInfo()->getBasePtrId();
+ isl::space Space = Statement->getDomainSpace();
+ NewAccessRelation = NewAccessRelation.set_tuple_id(
+ isl::dim::in, Space.get_tuple_id(isl::dim::set));
+ NewAccessRelation = NewAccessRelation.set_tuple_id(isl::dim::out, BaseAddrId);
+ NewAccessRelation = NewAccessRelation.gist_domain(Statement->getDomain());
+
+ // Access dimension folding might in certain cases increase the number of
+ // disjuncts in the memory access, which can possibly complicate the generated
+ // run-time checks and can lead to costly compilation.
+ if (!PollyPreciseFoldAccesses && NewAccessRelation.n_basic_map().release() >
+ AccessRelation.n_basic_map().release()) {
+ } else {
+ AccessRelation = NewAccessRelation;
+ }
+}
+
+void MemoryAccess::buildAccessRelation(const ScopArrayInfo *SAI) {
+ assert(AccessRelation.is_null() && "AccessRelation already built");
+
+ // Initialize the invalid domain which describes all iterations for which the
+ // access relation is not modeled correctly.
+ isl::set StmtInvalidDomain = getStatement()->getInvalidDomain();
+ InvalidDomain = isl::set::empty(StmtInvalidDomain.get_space());
+
+ isl::ctx Ctx = Id.ctx();
+ isl::id BaseAddrId = SAI->getBasePtrId();
+
+ if (getAccessInstruction() && isa<MemIntrinsic>(getAccessInstruction())) {
+ buildMemIntrinsicAccessRelation();
+ AccessRelation = AccessRelation.set_tuple_id(isl::dim::out, BaseAddrId);
+ return;
+ }
+
+ if (!isAffine()) {
+ // We overapproximate non-affine accesses with a possible access to the
+ // whole array. For read accesses it does not make a difference, if an
+ // access must or may happen. However, for write accesses it is important to
+ // differentiate between writes that must happen and writes that may happen.
+ if (AccessRelation.is_null())
+ AccessRelation = createBasicAccessMap(Statement);
+
+ AccessRelation = AccessRelation.set_tuple_id(isl::dim::out, BaseAddrId);
+ return;
+ }
+
+ isl::space Space = isl::space(Ctx, 0, Statement->getNumIterators(), 0);
+ AccessRelation = isl::map::universe(Space);
+
+ for (int i = 0, Size = Subscripts.size(); i < Size; ++i) {
+ isl::pw_aff Affine = getPwAff(Subscripts[i]);
+ isl::map SubscriptMap = isl::map::from_pw_aff(Affine);
+ AccessRelation = AccessRelation.flat_range_product(SubscriptMap);
+ }
+
+ Space = Statement->getDomainSpace();
+ AccessRelation = AccessRelation.set_tuple_id(
+ isl::dim::in, Space.get_tuple_id(isl::dim::set));
+ AccessRelation = AccessRelation.set_tuple_id(isl::dim::out, BaseAddrId);
+
+ AccessRelation = AccessRelation.gist_domain(Statement->getDomain());
+}
+
+MemoryAccess::MemoryAccess(ScopStmt *Stmt, Instruction *AccessInst,
+ AccessType AccType, Value *BaseAddress,
+ Type *ElementType, bool Affine,
+ ArrayRef<const SCEV *> Subscripts,
+ ArrayRef<const SCEV *> Sizes, Value *AccessValue,
+ MemoryKind Kind)
+ : Kind(Kind), AccType(AccType), Statement(Stmt), InvalidDomain(),
+ BaseAddr(BaseAddress), ElementType(ElementType),
+ Sizes(Sizes.begin(), Sizes.end()), AccessInstruction(AccessInst),
+ AccessValue(AccessValue), IsAffine(Affine),
+ Subscripts(Subscripts.begin(), Subscripts.end()), AccessRelation(),
+ NewAccessRelation() {
+ static const std::string TypeStrings[] = {"", "_Read", "_Write", "_MayWrite"};
+ const std::string Access = TypeStrings[AccType] + utostr(Stmt->size());
+
+ std::string IdName = Stmt->getBaseName() + Access;
+ Id = isl::id::alloc(Stmt->getParent()->getIslCtx(), IdName, this);
+}
+
+MemoryAccess::MemoryAccess(ScopStmt *Stmt, AccessType AccType, isl::map AccRel)
+ : Kind(MemoryKind::Array), AccType(AccType), Statement(Stmt),
+ InvalidDomain(), AccessRelation(), NewAccessRelation(AccRel) {
+ isl::id ArrayInfoId = NewAccessRelation.get_tuple_id(isl::dim::out);
+ auto *SAI = ScopArrayInfo::getFromId(ArrayInfoId);
+ Sizes.push_back(nullptr);
+ for (unsigned i = 1; i < SAI->getNumberOfDimensions(); i++)
+ Sizes.push_back(SAI->getDimensionSize(i));
+ ElementType = SAI->getElementType();
+ BaseAddr = SAI->getBasePtr();
+ static const std::string TypeStrings[] = {"", "_Read", "_Write", "_MayWrite"};
+ const std::string Access = TypeStrings[AccType] + utostr(Stmt->size());
+
+ std::string IdName = Stmt->getBaseName() + Access;
+ Id = isl::id::alloc(Stmt->getParent()->getIslCtx(), IdName, this);
+}
+
+MemoryAccess::~MemoryAccess() = default;
+
+void MemoryAccess::realignParams() {
+ isl::set Ctx = Statement->getParent()->getContext();
+ InvalidDomain = InvalidDomain.gist_params(Ctx);
+ AccessRelation = AccessRelation.gist_params(Ctx);
+
+ // Predictable parameter order is required for JSON imports. Ensure alignment
+ // by explicitly calling align_params.
+ isl::space CtxSpace = Ctx.get_space();
+ InvalidDomain = InvalidDomain.align_params(CtxSpace);
+ AccessRelation = AccessRelation.align_params(CtxSpace);
+}
+
+const std::string MemoryAccess::getReductionOperatorStr() const {
+ return MemoryAccess::getReductionOperatorStr(getReductionType());
+}
+
+isl::id MemoryAccess::getId() const { return Id; }
+
+raw_ostream &polly::operator<<(raw_ostream &OS,
+ MemoryAccess::ReductionType RT) {
+ if (RT == MemoryAccess::RT_NONE)
+ OS << "NONE";
+ else
+ OS << MemoryAccess::getReductionOperatorStr(RT);
+ return OS;
+}
+
+void MemoryAccess::print(raw_ostream &OS) const {
+ switch (AccType) {
+ case READ:
+ OS.indent(12) << "ReadAccess :=\t";
+ break;
+ case MUST_WRITE:
+ OS.indent(12) << "MustWriteAccess :=\t";
+ break;
+ case MAY_WRITE:
+ OS.indent(12) << "MayWriteAccess :=\t";
+ break;
+ }
+
+ OS << "[Reduction Type: " << getReductionType() << "] ";
+
+ OS << "[Scalar: " << isScalarKind() << "]\n";
+ OS.indent(16) << getOriginalAccessRelationStr() << ";\n";
+ if (hasNewAccessRelation())
+ OS.indent(11) << "new: " << getNewAccessRelationStr() << ";\n";
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void MemoryAccess::dump() const { print(errs()); }
+#endif
+
+isl::pw_aff MemoryAccess::getPwAff(const SCEV *E) {
+ auto *Stmt = getStatement();
+ PWACtx PWAC = Stmt->getParent()->getPwAff(E, Stmt->getEntryBlock());
+ isl::set StmtDom = getStatement()->getDomain();
+ StmtDom = StmtDom.reset_tuple_id();
+ isl::set NewInvalidDom = StmtDom.intersect(PWAC.second);
+ InvalidDomain = InvalidDomain.unite(NewInvalidDom);
+ return PWAC.first;
+}
+
+// Create a map in the size of the provided set domain, that maps from the
+// one element of the provided set domain to another element of the provided
+// set domain.
+// The mapping is limited to all points that are equal in all but the last
+// dimension and for which the last dimension of the input is strict smaller
+// than the last dimension of the output.
+//
+// getEqualAndLarger(set[i0, i1, ..., iX]):
+//
+// set[i0, i1, ..., iX] -> set[o0, o1, ..., oX]
+// : i0 = o0, i1 = o1, ..., i(X-1) = o(X-1), iX < oX
+//
+static isl::map getEqualAndLarger(isl::space SetDomain) {
+ isl::space Space = SetDomain.map_from_set();
+ isl::map Map = isl::map::universe(Space);
+ unsigned lastDimension = Map.domain_tuple_dim().release() - 1;
+
+ // Set all but the last dimension to be equal for the input and output
+ //
+ // input[i0, i1, ..., iX] -> output[o0, o1, ..., oX]
+ // : i0 = o0, i1 = o1, ..., i(X-1) = o(X-1)
+ for (unsigned i = 0; i < lastDimension; ++i)
+ Map = Map.equate(isl::dim::in, i, isl::dim::out, i);
+
+ // Set the last dimension of the input to be strict smaller than the
+ // last dimension of the output.
+ //
+ // input[?,?,?,...,iX] -> output[?,?,?,...,oX] : iX < oX
+ Map = Map.order_lt(isl::dim::in, lastDimension, isl::dim::out, lastDimension);
+ return Map;
+}
+
+isl::set MemoryAccess::getStride(isl::map Schedule) const {
+ isl::map AccessRelation = getAccessRelation();
+ isl::space Space = Schedule.get_space().range();
+ isl::map NextScatt = getEqualAndLarger(Space);
+
+ Schedule = Schedule.reverse();
+ NextScatt = NextScatt.lexmin();
+
+ NextScatt = NextScatt.apply_range(Schedule);
+ NextScatt = NextScatt.apply_range(AccessRelation);
+ NextScatt = NextScatt.apply_domain(Schedule);
+ NextScatt = NextScatt.apply_domain(AccessRelation);
+
+ isl::set Deltas = NextScatt.deltas();
+ return Deltas;
+}
+
+bool MemoryAccess::isStrideX(isl::map Schedule, int StrideWidth) const {
+ isl::set Stride, StrideX;
+ bool IsStrideX;
+
+ Stride = getStride(Schedule);
+ StrideX = isl::set::universe(Stride.get_space());
+ int Size = unsignedFromIslSize(StrideX.tuple_dim());
+ for (auto i : seq<int>(0, Size - 1))
+ StrideX = StrideX.fix_si(isl::dim::set, i, 0);
+ StrideX = StrideX.fix_si(isl::dim::set, Size - 1, StrideWidth);
+ IsStrideX = Stride.is_subset(StrideX);
+
+ return IsStrideX;
+}
+
+bool MemoryAccess::isStrideZero(isl::map Schedule) const {
+ return isStrideX(Schedule, 0);
+}
+
+bool MemoryAccess::isStrideOne(isl::map Schedule) const {
+ return isStrideX(Schedule, 1);
+}
+
+void MemoryAccess::setAccessRelation(isl::map NewAccess) {
+ AccessRelation = NewAccess;
+}
+
+void MemoryAccess::setNewAccessRelation(isl::map NewAccess) {
+ assert(!NewAccess.is_null());
+
+#ifndef NDEBUG
+ // Check domain space compatibility.
+ isl::space NewSpace = NewAccess.get_space();
+ isl::space NewDomainSpace = NewSpace.domain();
+ isl::space OriginalDomainSpace = getStatement()->getDomainSpace();
+ assert(OriginalDomainSpace.has_equal_tuples(NewDomainSpace));
+
+ // Reads must be executed unconditionally. Writes might be executed in a
+ // subdomain only.
+ if (isRead()) {
+ // Check whether there is an access for every statement instance.
+ isl::set StmtDomain = getStatement()->getDomain();
+ isl::set DefinedContext =
+ getStatement()->getParent()->getBestKnownDefinedBehaviorContext();
+ StmtDomain = StmtDomain.intersect_params(DefinedContext);
+ isl::set NewDomain = NewAccess.domain();
+ assert(!StmtDomain.is_subset(NewDomain).is_false() &&
+ "Partial READ accesses not supported");
+ }
+
+ isl::space NewAccessSpace = NewAccess.get_space();
+ assert(NewAccessSpace.has_tuple_id(isl::dim::set) &&
+ "Must specify the array that is accessed");
+ isl::id NewArrayId = NewAccessSpace.get_tuple_id(isl::dim::set);
+ auto *SAI = static_cast<ScopArrayInfo *>(NewArrayId.get_user());
+ assert(SAI && "Must set a ScopArrayInfo");
+
+ if (SAI->isArrayKind() && SAI->getBasePtrOriginSAI()) {
+ InvariantEquivClassTy *EqClass =
+ getStatement()->getParent()->lookupInvariantEquivClass(
+ SAI->getBasePtr());
+ assert(EqClass &&
+ "Access functions to indirect arrays must have an invariant and "
+ "hoisted base pointer");
+ }
+
+ // Check whether access dimensions correspond to number of dimensions of the
+ // accesses array.
+ unsigned Dims = SAI->getNumberOfDimensions();
+ unsigned SpaceSize = unsignedFromIslSize(NewAccessSpace.dim(isl::dim::set));
+ assert(SpaceSize == Dims && "Access dims must match array dims");
+#endif
+
+ NewAccess = NewAccess.gist_params(getStatement()->getParent()->getContext());
+ NewAccess = NewAccess.gist_domain(getStatement()->getDomain());
+ NewAccessRelation = NewAccess;
+}
+
+bool MemoryAccess::isLatestPartialAccess() const {
+ isl::set StmtDom = getStatement()->getDomain();
+ isl::set AccDom = getLatestAccessRelation().domain();
+
+ return !StmtDom.is_subset(AccDom);
+}
+
+//===----------------------------------------------------------------------===//
+
+isl::map ScopStmt::getSchedule() const {
+ isl::set Domain = getDomain();
+ if (Domain.is_empty())
+ return isl::map::from_aff(isl::aff(isl::local_space(getDomainSpace())));
+ auto Schedule = getParent()->getSchedule();
+ if (Schedule.is_null())
+ return {};
+ Schedule = Schedule.intersect_domain(isl::union_set(Domain));
+ if (Schedule.is_empty())
+ return isl::map::from_aff(isl::aff(isl::local_space(getDomainSpace())));
+ isl::map M = M.from_union_map(Schedule);
+ M = M.coalesce();
+ M = M.gist_domain(Domain);
+ M = M.coalesce();
+ return M;
+}
+
+void ScopStmt::restrictDomain(isl::set NewDomain) {
+ assert(NewDomain.is_subset(Domain) &&
+ "New domain is not a subset of old domain!");
+ Domain = NewDomain;
+}
+
+void ScopStmt::addAccess(MemoryAccess *Access, bool Prepend) {
+ Instruction *AccessInst = Access->getAccessInstruction();
+
+ if (Access->isArrayKind()) {
+ MemoryAccessList &MAL = InstructionToAccess[AccessInst];
+ MAL.emplace_front(Access);
+ } else if (Access->isValueKind() && Access->isWrite()) {
+ Instruction *AccessVal = cast<Instruction>(Access->getAccessValue());
+ assert(!ValueWrites.lookup(AccessVal));
+
+ ValueWrites[AccessVal] = Access;
+ } else if (Access->isValueKind() && Access->isRead()) {
+ Value *AccessVal = Access->getAccessValue();
+ assert(!ValueReads.lookup(AccessVal));
+
+ ValueReads[AccessVal] = Access;
+ } else if (Access->isAnyPHIKind() && Access->isWrite()) {
+ PHINode *PHI = cast<PHINode>(Access->getAccessValue());
+ assert(!PHIWrites.lookup(PHI));
+
+ PHIWrites[PHI] = Access;
+ } else if (Access->isAnyPHIKind() && Access->isRead()) {
+ PHINode *PHI = cast<PHINode>(Access->getAccessValue());
+ assert(!PHIReads.lookup(PHI));
+
+ PHIReads[PHI] = Access;
+ }
+
+ if (Prepend) {
+ MemAccs.insert(MemAccs.begin(), Access);
+ return;
+ }
+ MemAccs.push_back(Access);
+}
+
+void ScopStmt::realignParams() {
+ for (MemoryAccess *MA : *this)
+ MA->realignParams();
+
+ simplify(InvalidDomain);
+ simplify(Domain);
+
+ isl::set Ctx = Parent.getContext();
+ InvalidDomain = InvalidDomain.gist_params(Ctx);
+ Domain = Domain.gist_params(Ctx);
+
+ // Predictable parameter order is required for JSON imports. Ensure alignment
+ // by explicitly calling align_params.
+ isl::space CtxSpace = Ctx.get_space();
+ InvalidDomain = InvalidDomain.align_params(CtxSpace);
+ Domain = Domain.align_params(CtxSpace);
+}
+
+ScopStmt::ScopStmt(Scop &parent, Region &R, StringRef Name,
+ Loop *SurroundingLoop,
+ std::vector<Instruction *> EntryBlockInstructions)
+ : Parent(parent), InvalidDomain(), Domain(), R(&R), Build(), BaseName(Name),
+ SurroundingLoop(SurroundingLoop), Instructions(EntryBlockInstructions) {}
+
+ScopStmt::ScopStmt(Scop &parent, BasicBlock &bb, StringRef Name,
+ Loop *SurroundingLoop,
+ std::vector<Instruction *> Instructions)
+ : Parent(parent), InvalidDomain(), Domain(), BB(&bb), Build(),
+ BaseName(Name), SurroundingLoop(SurroundingLoop),
+ Instructions(Instructions) {}
+
+ScopStmt::ScopStmt(Scop &parent, isl::map SourceRel, isl::map TargetRel,
+ isl::set NewDomain)
+ : Parent(parent), InvalidDomain(), Domain(NewDomain), Build() {
+ BaseName = getIslCompatibleName("CopyStmt_", "",
+ std::to_string(parent.getCopyStmtsNum()));
+ isl::id Id = isl::id::alloc(getIslCtx(), getBaseName(), this);
+ Domain = Domain.set_tuple_id(Id);
+ TargetRel = TargetRel.set_tuple_id(isl::dim::in, Id);
+ auto *Access =
+ new MemoryAccess(this, MemoryAccess::AccessType::MUST_WRITE, TargetRel);
+ parent.addAccessFunction(Access);
+ addAccess(Access);
+ SourceRel = SourceRel.set_tuple_id(isl::dim::in, Id);
+ Access = new MemoryAccess(this, MemoryAccess::AccessType::READ, SourceRel);
+ parent.addAccessFunction(Access);
+ addAccess(Access);
+}
+
+ScopStmt::~ScopStmt() = default;
+
+std::string ScopStmt::getDomainStr() const { return stringFromIslObj(Domain); }
+
+std::string ScopStmt::getScheduleStr() const {
+ return stringFromIslObj(getSchedule());
+}
+
+void ScopStmt::setInvalidDomain(isl::set ID) { InvalidDomain = ID; }
+
+BasicBlock *ScopStmt::getEntryBlock() const {
+ if (isBlockStmt())
+ return getBasicBlock();
+ return getRegion()->getEntry();
+}
+
+unsigned ScopStmt::getNumIterators() const { return NestLoops.size(); }
+
+const char *ScopStmt::getBaseName() const { return BaseName.c_str(); }
+
+Loop *ScopStmt::getLoopForDimension(unsigned Dimension) const {
+ return NestLoops[Dimension];
+}
+
+isl::ctx ScopStmt::getIslCtx() const { return Parent.getIslCtx(); }
+
+isl::set ScopStmt::getDomain() const { return Domain; }
+
+isl::space ScopStmt::getDomainSpace() const { return Domain.get_space(); }
+
+isl::id ScopStmt::getDomainId() const { return Domain.get_tuple_id(); }
+
+void ScopStmt::printInstructions(raw_ostream &OS) const {
+ OS << "Instructions {\n";
+
+ for (Instruction *Inst : Instructions)
+ OS.indent(16) << *Inst << "\n";
+
+ OS.indent(12) << "}\n";
+}
+
+void ScopStmt::print(raw_ostream &OS, bool PrintInstructions) const {
+ OS << "\t" << getBaseName() << "\n";
+ OS.indent(12) << "Domain :=\n";
+
+ if (!Domain.is_null()) {
+ OS.indent(16) << getDomainStr() << ";\n";
+ } else
+ OS.indent(16) << "n/a\n";
+
+ OS.indent(12) << "Schedule :=\n";
+
+ if (!Domain.is_null()) {
+ OS.indent(16) << getScheduleStr() << ";\n";
+ } else
+ OS.indent(16) << "n/a\n";
+
+ for (MemoryAccess *Access : MemAccs)
+ Access->print(OS);
+
+ if (PrintInstructions)
+ printInstructions(OS.indent(12));
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void ScopStmt::dump() const { print(dbgs(), true); }
+#endif
+
+void ScopStmt::removeAccessData(MemoryAccess *MA) {
+ if (MA->isRead() && MA->isOriginalValueKind()) {
+ bool Found = ValueReads.erase(MA->getAccessValue());
+ (void)Found;
+ assert(Found && "Expected access data not found");
+ }
+ if (MA->isWrite() && MA->isOriginalValueKind()) {
+ bool Found = ValueWrites.erase(cast<Instruction>(MA->getAccessValue()));
+ (void)Found;
+ assert(Found && "Expected access data not found");
+ }
+ if (MA->isWrite() && MA->isOriginalAnyPHIKind()) {
+ bool Found = PHIWrites.erase(cast<PHINode>(MA->getAccessInstruction()));
+ (void)Found;
+ assert(Found && "Expected access data not found");
+ }
+ if (MA->isRead() && MA->isOriginalAnyPHIKind()) {
+ bool Found = PHIReads.erase(cast<PHINode>(MA->getAccessInstruction()));
+ (void)Found;
+ assert(Found && "Expected access data not found");
+ }
+}
+
+void ScopStmt::removeMemoryAccess(MemoryAccess *MA) {
+ // Remove the memory accesses from this statement together with all scalar
+ // accesses that were caused by it. MemoryKind::Value READs have no access
+ // instruction, hence would not be removed by this function. However, it is
+ // only used for invariant LoadInst accesses, its arguments are always affine,
+ // hence synthesizable, and therefore there are no MemoryKind::Value READ
+ // accesses to be removed.
+ auto Predicate = [&](MemoryAccess *Acc) {
+ return Acc->getAccessInstruction() == MA->getAccessInstruction();
+ };
+ for (auto *MA : MemAccs) {
+ if (Predicate(MA)) {
+ removeAccessData(MA);
+ Parent.removeAccessData(MA);
+ }
+ }
+ llvm::erase_if(MemAccs, Predicate);
+ InstructionToAccess.erase(MA->getAccessInstruction());
+}
+
+void ScopStmt::removeSingleMemoryAccess(MemoryAccess *MA, bool AfterHoisting) {
+ if (AfterHoisting) {
+ auto MAIt = std::find(MemAccs.begin(), MemAccs.end(), MA);
+ assert(MAIt != MemAccs.end());
+ MemAccs.erase(MAIt);
+
+ removeAccessData(MA);
+ Parent.removeAccessData(MA);
+ }
+
+ auto It = InstructionToAccess.find(MA->getAccessInstruction());
+ if (It != InstructionToAccess.end()) {
+ It->second.remove(MA);
+ if (It->second.empty())
+ InstructionToAccess.erase(MA->getAccessInstruction());
+ }
+}
+
+MemoryAccess *ScopStmt::ensureValueRead(Value *V) {
+ MemoryAccess *Access = lookupInputAccessOf(V);
+ if (Access)
+ return Access;
+
+ ScopArrayInfo *SAI =
+ Parent.getOrCreateScopArrayInfo(V, V->getType(), {}, MemoryKind::Value);
+ Access = new MemoryAccess(this, nullptr, MemoryAccess::READ, V, V->getType(),
+ true, {}, {}, V, MemoryKind::Value);
+ Parent.addAccessFunction(Access);
+ Access->buildAccessRelation(SAI);
+ addAccess(Access);
+ Parent.addAccessData(Access);
+ return Access;
+}
+
+raw_ostream &polly::operator<<(raw_ostream &OS, const ScopStmt &S) {
+ S.print(OS, PollyPrintInstructions);
+ return OS;
+}
+
+//===----------------------------------------------------------------------===//
+/// Scop class implement
+
+void Scop::setContext(isl::set NewContext) {
+ Context = NewContext.align_params(Context.get_space());
+}
+
+namespace {
+
+/// Remap parameter values but keep AddRecs valid wrt. invariant loads.
+struct SCEVSensitiveParameterRewriter
+ : public SCEVRewriteVisitor<SCEVSensitiveParameterRewriter> {
+ const ValueToValueMap &VMap;
+
+public:
+ SCEVSensitiveParameterRewriter(const ValueToValueMap &VMap,
+ ScalarEvolution &SE)
+ : SCEVRewriteVisitor(SE), VMap(VMap) {}
+
+ static const SCEV *rewrite(const SCEV *E, ScalarEvolution &SE,
+ const ValueToValueMap &VMap) {
+ SCEVSensitiveParameterRewriter SSPR(VMap, SE);
+ return SSPR.visit(E);
+ }
+
+ const SCEV *visitAddRecExpr(const SCEVAddRecExpr *E) {
+ auto *Start = visit(E->getStart());
+ auto *AddRec = SE.getAddRecExpr(SE.getConstant(E->getType(), 0),
+ visit(E->getStepRecurrence(SE)),
+ E->getLoop(), SCEV::FlagAnyWrap);
+ return SE.getAddExpr(Start, AddRec);
+ }
+
+ const SCEV *visitUnknown(const SCEVUnknown *E) {
+ if (auto *NewValue = VMap.lookup(E->getValue()))
+ return SE.getUnknown(NewValue);
+ return E;
+ }
+};
+
+/// Check whether we should remap a SCEV expression.
+struct SCEVFindInsideScop : public SCEVTraversal<SCEVFindInsideScop> {
+ const ValueToValueMap &VMap;
+ bool FoundInside = false;
+ const Scop *S;
+
+public:
+ SCEVFindInsideScop(const ValueToValueMap &VMap, ScalarEvolution &SE,
+ const Scop *S)
+ : SCEVTraversal(*this), VMap(VMap), S(S) {}
+
+ static bool hasVariant(const SCEV *E, ScalarEvolution &SE,
+ const ValueToValueMap &VMap, const Scop *S) {
+ SCEVFindInsideScop SFIS(VMap, SE, S);
+ SFIS.visitAll(E);
+ return SFIS.FoundInside;
+ }
+
+ bool follow(const SCEV *E) {
+ if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(E)) {
+ FoundInside |= S->getRegion().contains(AddRec->getLoop());
+ } else if (auto *Unknown = dyn_cast<SCEVUnknown>(E)) {
+ if (Instruction *I = dyn_cast<Instruction>(Unknown->getValue()))
+ FoundInside |= S->getRegion().contains(I) && !VMap.count(I);
+ }
+ return !FoundInside;
+ }
+
+ bool isDone() { return FoundInside; }
+};
+} // end anonymous namespace
+
+const SCEV *Scop::getRepresentingInvariantLoadSCEV(const SCEV *E) const {
+ // Check whether it makes sense to rewrite the SCEV. (ScalarEvolution
+ // doesn't like addition between an AddRec and an expression that
+ // doesn't have a dominance relationship with it.)
+ if (SCEVFindInsideScop::hasVariant(E, *SE, InvEquivClassVMap, this))
+ return E;
+
+ // Rewrite SCEV.
+ return SCEVSensitiveParameterRewriter::rewrite(E, *SE, InvEquivClassVMap);
+}
+
+void Scop::createParameterId(const SCEV *Parameter) {
+ assert(Parameters.count(Parameter));
+ assert(!ParameterIds.count(Parameter));
+
+ std::string ParameterName = "p_" + std::to_string(getNumParams() - 1);
+
+ if (const SCEVUnknown *ValueParameter = dyn_cast<SCEVUnknown>(Parameter)) {
+ Value *Val = ValueParameter->getValue();
+
+ if (UseInstructionNames) {
+ // If this parameter references a specific Value and this value has a name
+ // we use this name as it is likely to be unique and more useful than just
+ // a number.
+ if (Val->hasName())
+ ParameterName = Val->getName().str();
+ else if (LoadInst *LI = dyn_cast<LoadInst>(Val)) {
+ auto *LoadOrigin = LI->getPointerOperand()->stripInBoundsOffsets();
+ if (LoadOrigin->hasName()) {
+ ParameterName += "_loaded_from_";
+ ParameterName +=
+ LI->getPointerOperand()->stripInBoundsOffsets()->getName();
+ }
+ }
+ }
+
+ ParameterName = getIslCompatibleName("", ParameterName, "");
+ }
+
+ isl::id Id = isl::id::alloc(getIslCtx(), ParameterName,
+ const_cast<void *>((const void *)Parameter));
+ ParameterIds[Parameter] = Id;
+}
+
+void Scop::addParams(const ParameterSetTy &NewParameters) {
+ for (const SCEV *Parameter : NewParameters) {
+ // Normalize the SCEV to get the representing element for an invariant load.
+ Parameter = extractConstantFactor(Parameter, *SE).second;
+ Parameter = getRepresentingInvariantLoadSCEV(Parameter);
+
+ if (Parameters.insert(Parameter))
+ createParameterId(Parameter);
+ }
+}
+
+isl::id Scop::getIdForParam(const SCEV *Parameter) const {
+ // Normalize the SCEV to get the representing element for an invariant load.
+ Parameter = getRepresentingInvariantLoadSCEV(Parameter);
+ return ParameterIds.lookup(Parameter);
+}
+
+bool Scop::isDominatedBy(const DominatorTree &DT, BasicBlock *BB) const {
+ return DT.dominates(BB, getEntry());
+}
+
+void Scop::buildContext() {
+ isl::space Space = isl::space::params_alloc(getIslCtx(), 0);
+ Context = isl::set::universe(Space);
+ InvalidContext = isl::set::empty(Space);
+ AssumedContext = isl::set::universe(Space);
+ DefinedBehaviorContext = isl::set::universe(Space);
+}
+
+void Scop::addParameterBounds() {
+ unsigned PDim = 0;
+ for (auto *Parameter : Parameters) {
+ ConstantRange SRange = SE->getSignedRange(Parameter);
+ Context = addRangeBoundsToSet(Context, SRange, PDim++, isl::dim::param);
+ }
+ intersectDefinedBehavior(Context, AS_ASSUMPTION);
+}
+
+void Scop::realignParams() {
+ if (PollyIgnoreParamBounds)
+ return;
+
+ // Add all parameters into a common model.
+ isl::space Space = getFullParamSpace();
+
+ // Align the parameters of all data structures to the model.
+ Context = Context.align_params(Space);
+ AssumedContext = AssumedContext.align_params(Space);
+ InvalidContext = InvalidContext.align_params(Space);
+
+ // As all parameters are known add bounds to them.
+ addParameterBounds();
+
+ for (ScopStmt &Stmt : *this)
+ Stmt.realignParams();
+ // Simplify the schedule according to the context too.
+ Schedule = Schedule.gist_domain_params(getContext());
+
+ // Predictable parameter order is required for JSON imports. Ensure alignment
+ // by explicitly calling align_params.
+ Schedule = Schedule.align_params(Space);
+}
+
+static isl::set simplifyAssumptionContext(isl::set AssumptionContext,
+ const Scop &S) {
+ // If we have modeled all blocks in the SCoP that have side effects we can
+ // simplify the context with the constraints that are needed for anything to
+ // be executed at all. However, if we have error blocks in the SCoP we already
+ // assumed some parameter combinations cannot occur and removed them from the
+ // domains, thus we cannot use the remaining domain to simplify the
+ // assumptions.
+ if (!S.hasErrorBlock()) {
+ auto DomainParameters = S.getDomains().params();
+ AssumptionContext = AssumptionContext.gist_params(DomainParameters);
+ }
+
+ AssumptionContext = AssumptionContext.gist_params(S.getContext());
+ return AssumptionContext;
+}
+
+void Scop::simplifyContexts() {
+ // The parameter constraints of the iteration domains give us a set of
+ // constraints that need to hold for all cases where at least a single
+ // statement iteration is executed in the whole scop. We now simplify the
+ // assumed context under the assumption that such constraints hold and at
+ // least a single statement iteration is executed. For cases where no
+ // statement instances are executed, the assumptions we have taken about
+ // the executed code do not matter and can be changed.
+ //
+ // WARNING: This only holds if the assumptions we have taken do not reduce
+ // the set of statement instances that are executed. Otherwise we
+ // may run into a case where the iteration domains suggest that
+ // for a certain set of parameter constraints no code is executed,
+ // but in the original program some computation would have been
+ // performed. In such a case, modifying the run-time conditions and
+ // possibly influencing the run-time check may cause certain scops
+ // to not be executed.
+ //
+ // Example:
+ //
+ // When delinearizing the following code:
+ //
+ // for (long i = 0; i < 100; i++)
+ // for (long j = 0; j < m; j++)
+ // A[i+p][j] = 1.0;
+ //
+ // we assume that the condition m <= 0 or (m >= 1 and p >= 0) holds as
+ // otherwise we would access out of bound data. Now, knowing that code is
+ // only executed for the case m >= 0, it is sufficient to assume p >= 0.
+ AssumedContext = simplifyAssumptionContext(AssumedContext, *this);
+ InvalidContext = InvalidContext.align_params(getParamSpace());
+ simplify(DefinedBehaviorContext);
+ DefinedBehaviorContext = DefinedBehaviorContext.align_params(getParamSpace());
+}
+
+isl::set Scop::getDomainConditions(const ScopStmt *Stmt) const {
+ return getDomainConditions(Stmt->getEntryBlock());
+}
+
+isl::set Scop::getDomainConditions(BasicBlock *BB) const {
+ auto DIt = DomainMap.find(BB);
+ if (DIt != DomainMap.end())
+ return DIt->getSecond();
+
+ auto &RI = *R.getRegionInfo();
+ auto *BBR = RI.getRegionFor(BB);
+ while (BBR->getEntry() == BB)
+ BBR = BBR->getParent();
+ return getDomainConditions(BBR->getEntry());
+}
+
+Scop::Scop(Region &R, ScalarEvolution &ScalarEvolution, LoopInfo &LI,
+ DominatorTree &DT, ScopDetection::DetectionContext &DC,
+ OptimizationRemarkEmitter &ORE, int ID)
+ : IslCtx(isl_ctx_alloc(), isl_ctx_free), SE(&ScalarEvolution), DT(&DT),
+ R(R), name(None), HasSingleExitEdge(R.getExitingBlock()), DC(DC),
+ ORE(ORE), Affinator(this, LI), ID(ID) {
+
+ // Options defaults that are different from ISL's.
+ isl_options_set_schedule_serialize_sccs(IslCtx.get(), true);
+
+ SmallVector<char *, 8> IslArgv;
+ IslArgv.reserve(1 + IslArgs.size());
+
+ // Substitute for program name.
+ IslArgv.push_back(const_cast<char *>("-polly-isl-arg"));
+
+ for (std::string &Arg : IslArgs)
+ IslArgv.push_back(const_cast<char *>(Arg.c_str()));
+
+ // Abort if unknown argument is passed.
+ // Note that "-V" (print isl version) will always call exit(0), so we cannot
+ // avoid ISL aborting the program at this point.
+ unsigned IslParseFlags = ISL_ARG_ALL;
+
+ isl_ctx_parse_options(IslCtx.get(), IslArgv.size(), IslArgv.data(),
+ IslParseFlags);
+
+ if (IslOnErrorAbort)
+ isl_options_set_on_error(getIslCtx().get(), ISL_ON_ERROR_ABORT);
+ buildContext();
+}
+
+Scop::~Scop() = default;
+
+void Scop::removeFromStmtMap(ScopStmt &Stmt) {
+ for (Instruction *Inst : Stmt.getInstructions())
+ InstStmtMap.erase(Inst);
+
+ if (Stmt.isRegionStmt()) {
+ for (BasicBlock *BB : Stmt.getRegion()->blocks()) {
+ StmtMap.erase(BB);
+ // Skip entry basic block, as its instructions are already deleted as
+ // part of the statement's instruction list.
+ if (BB == Stmt.getEntryBlock())
+ continue;
+ for (Instruction &Inst : *BB)
+ InstStmtMap.erase(&Inst);
+ }
+ } else {
+ auto StmtMapIt = StmtMap.find(Stmt.getBasicBlock());
+ if (StmtMapIt != StmtMap.end())
+ StmtMapIt->second.erase(std::remove(StmtMapIt->second.begin(),
+ StmtMapIt->second.end(), &Stmt),
+ StmtMapIt->second.end());
+ for (Instruction *Inst : Stmt.getInstructions())
+ InstStmtMap.erase(Inst);
+ }
+}
+
+void Scop::removeStmts(function_ref<bool(ScopStmt &)> ShouldDelete,
+ bool AfterHoisting) {
+ for (auto StmtIt = Stmts.begin(), StmtEnd = Stmts.end(); StmtIt != StmtEnd;) {
+ if (!ShouldDelete(*StmtIt)) {
+ StmtIt++;
+ continue;
+ }
+
+ // Start with removing all of the statement's accesses including erasing it
+ // from all maps that are pointing to them.
+ // Make a temporary copy because removing MAs invalidates the iterator.
+ SmallVector<MemoryAccess *, 16> MAList(StmtIt->begin(), StmtIt->end());
+ for (MemoryAccess *MA : MAList)
+ StmtIt->removeSingleMemoryAccess(MA, AfterHoisting);
+
+ removeFromStmtMap(*StmtIt);
+ StmtIt = Stmts.erase(StmtIt);
+ }
+}
+
+void Scop::removeStmtNotInDomainMap() {
+ removeStmts([this](ScopStmt &Stmt) -> bool {
+ isl::set Domain = DomainMap.lookup(Stmt.getEntryBlock());
+ if (Domain.is_null())
+ return true;
+ return Domain.is_empty();
+ });
+}
+
+void Scop::simplifySCoP(bool AfterHoisting) {
+ removeStmts(
+ [AfterHoisting](ScopStmt &Stmt) -> bool {
+ // Never delete statements that contain calls to debug functions.
+ if (hasDebugCall(&Stmt))
+ return false;
+
+ bool RemoveStmt = Stmt.isEmpty();
+
+ // Remove read only statements only after invariant load hoisting.
+ if (!RemoveStmt && AfterHoisting) {
+ bool OnlyRead = true;
+ for (MemoryAccess *MA : Stmt) {
+ if (MA->isRead())
+ continue;
+
+ OnlyRead = false;
+ break;
+ }
+
+ RemoveStmt = OnlyRead;
+ }
+ return RemoveStmt;
+ },
+ AfterHoisting);
+}
+
+InvariantEquivClassTy *Scop::lookupInvariantEquivClass(Value *Val) {
+ LoadInst *LInst = dyn_cast<LoadInst>(Val);
+ if (!LInst)
+ return nullptr;
+
+ if (Value *Rep = InvEquivClassVMap.lookup(LInst))
+ LInst = cast<LoadInst>(Rep);
+
+ Type *Ty = LInst->getType();
+ const SCEV *PointerSCEV = SE->getSCEV(LInst->getPointerOperand());
+ for (auto &IAClass : InvariantEquivClasses) {
+ if (PointerSCEV != IAClass.IdentifyingPointer || Ty != IAClass.AccessType)
+ continue;
+
+ auto &MAs = IAClass.InvariantAccesses;
+ for (auto *MA : MAs)
+ if (MA->getAccessInstruction() == Val)
+ return &IAClass;
+ }
+
+ return nullptr;
+}
+
+ScopArrayInfo *Scop::getOrCreateScopArrayInfo(Value *BasePtr, Type *ElementType,
+ ArrayRef<const SCEV *> Sizes,
+ MemoryKind Kind,
+ const char *BaseName) {
+ assert((BasePtr || BaseName) &&
+ "BasePtr and BaseName can not be nullptr at the same time.");
+ assert(!(BasePtr && BaseName) && "BaseName is redundant.");
+ auto &SAI = BasePtr ? ScopArrayInfoMap[std::make_pair(BasePtr, Kind)]
+ : ScopArrayNameMap[BaseName];
+ if (!SAI) {
+ auto &DL = getFunction().getParent()->getDataLayout();
+ SAI.reset(new ScopArrayInfo(BasePtr, ElementType, getIslCtx(), Sizes, Kind,
+ DL, this, BaseName));
+ ScopArrayInfoSet.insert(SAI.get());
+ } else {
+ SAI->updateElementType(ElementType);
+ // In case of mismatching array sizes, we bail out by setting the run-time
+ // context to false.
+ if (!SAI->updateSizes(Sizes))
+ invalidate(DELINEARIZATION, DebugLoc());
+ }
+ return SAI.get();
+}
+
+ScopArrayInfo *Scop::createScopArrayInfo(Type *ElementType,
+ const std::string &BaseName,
+ const std::vector<unsigned> &Sizes) {
+ auto *DimSizeType = Type::getInt64Ty(getSE()->getContext());
+ std::vector<const SCEV *> SCEVSizes;
+
+ for (auto size : Sizes)
+ if (size)
+ SCEVSizes.push_back(getSE()->getConstant(DimSizeType, size, false));
+ else
+ SCEVSizes.push_back(nullptr);
+
+ auto *SAI = getOrCreateScopArrayInfo(nullptr, ElementType, SCEVSizes,
+ MemoryKind::Array, BaseName.c_str());
+ return SAI;
+}
+
+ScopArrayInfo *Scop::getScopArrayInfoOrNull(Value *BasePtr, MemoryKind Kind) {
+ auto *SAI = ScopArrayInfoMap[std::make_pair(BasePtr, Kind)].get();
+ return SAI;
+}
+
+ScopArrayInfo *Scop::getScopArrayInfo(Value *BasePtr, MemoryKind Kind) {
+ auto *SAI = getScopArrayInfoOrNull(BasePtr, Kind);
+ assert(SAI && "No ScopArrayInfo available for this base pointer");
+ return SAI;
+}
+
+std::string Scop::getContextStr() const {
+ return stringFromIslObj(getContext());
+}
+
+std::string Scop::getAssumedContextStr() const {
+ assert(!AssumedContext.is_null() && "Assumed context not yet built");
+ return stringFromIslObj(AssumedContext);
+}
+
+std::string Scop::getInvalidContextStr() const {
+ return stringFromIslObj(InvalidContext);
+}
+
+std::string Scop::getNameStr() const {
+ std::string ExitName, EntryName;
+ std::tie(EntryName, ExitName) = getEntryExitStr();
+ return EntryName + "---" + ExitName;
+}
+
+std::pair<std::string, std::string> Scop::getEntryExitStr() const {
+ std::string ExitName, EntryName;
+ raw_string_ostream ExitStr(ExitName);
+ raw_string_ostream EntryStr(EntryName);
+
+ R.getEntry()->printAsOperand(EntryStr, false);
+ EntryStr.str();
+
+ if (R.getExit()) {
+ R.getExit()->printAsOperand(ExitStr, false);
+ ExitStr.str();
+ } else
+ ExitName = "FunctionExit";
+
+ return std::make_pair(EntryName, ExitName);
+}
+
+isl::set Scop::getContext() const { return Context; }
+
+isl::space Scop::getParamSpace() const { return getContext().get_space(); }
+
+isl::space Scop::getFullParamSpace() const {
+
+ isl::space Space = isl::space::params_alloc(getIslCtx(), ParameterIds.size());
+
+ unsigned PDim = 0;
+ for (const SCEV *Parameter : Parameters) {
+ isl::id Id = getIdForParam(Parameter);
+ Space = Space.set_dim_id(isl::dim::param, PDim++, Id);
+ }
+
+ return Space;
+}
+
+isl::set Scop::getAssumedContext() const {
+ assert(!AssumedContext.is_null() && "Assumed context not yet built");
+ return AssumedContext;
+}
+
+bool Scop::isProfitable(bool ScalarsAreUnprofitable) const {
+ if (PollyProcessUnprofitable)
+ return true;
+
+ if (isEmpty())
+ return false;
+
+ unsigned OptimizableStmtsOrLoops = 0;
+ for (auto &Stmt : *this) {
+ if (Stmt.getNumIterators() == 0)
+ continue;
+
+ bool ContainsArrayAccs = false;
+ bool ContainsScalarAccs = false;
+ for (auto *MA : Stmt) {
+ if (MA->isRead())
+ continue;
+ ContainsArrayAccs |= MA->isLatestArrayKind();
+ ContainsScalarAccs |= MA->isLatestScalarKind();
+ }
+
+ if (!ScalarsAreUnprofitable || (ContainsArrayAccs && !ContainsScalarAccs))
+ OptimizableStmtsOrLoops += Stmt.getNumIterators();
+ }
+
+ return OptimizableStmtsOrLoops > 1;
+}
+
+bool Scop::hasFeasibleRuntimeContext() const {
+ if (Stmts.empty())
+ return false;
+
+ isl::set PositiveContext = getAssumedContext();
+ isl::set NegativeContext = getInvalidContext();
+ PositiveContext = PositiveContext.intersect_params(Context);
+ PositiveContext = PositiveContext.intersect_params(getDomains().params());
+ return PositiveContext.is_empty().is_false() &&
+ PositiveContext.is_subset(NegativeContext).is_false();
+}
+
+MemoryAccess *Scop::lookupBasePtrAccess(MemoryAccess *MA) {
+ Value *PointerBase = MA->getOriginalBaseAddr();
+
+ auto *PointerBaseInst = dyn_cast<Instruction>(PointerBase);
+ if (!PointerBaseInst)
+ return nullptr;
+
+ auto *BasePtrStmt = getStmtFor(PointerBaseInst);
+ if (!BasePtrStmt)
+ return nullptr;
+
+ return BasePtrStmt->getArrayAccessOrNULLFor(PointerBaseInst);
+}
+
+static std::string toString(AssumptionKind Kind) {
+ switch (Kind) {
+ case ALIASING:
+ return "No-aliasing";
+ case INBOUNDS:
+ return "Inbounds";
+ case WRAPPING:
+ return "No-overflows";
+ case UNSIGNED:
+ return "Signed-unsigned";
+ case COMPLEXITY:
+ return "Low complexity";
+ case PROFITABLE:
+ return "Profitable";
+ case ERRORBLOCK:
+ return "No-error";
+ case INFINITELOOP:
+ return "Finite loop";
+ case INVARIANTLOAD:
+ return "Invariant load";
+ case DELINEARIZATION:
+ return "Delinearization";
+ }
+ llvm_unreachable("Unknown AssumptionKind!");
+}
+
+bool Scop::isEffectiveAssumption(isl::set Set, AssumptionSign Sign) {
+ if (Sign == AS_ASSUMPTION) {
+ if (Context.is_subset(Set))
+ return false;
+
+ if (AssumedContext.is_subset(Set))
+ return false;
+ } else {
+ if (Set.is_disjoint(Context))
+ return false;
+
+ if (Set.is_subset(InvalidContext))
+ return false;
+ }
+ return true;
+}
+
+bool Scop::trackAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
+ AssumptionSign Sign, BasicBlock *BB) {
+ if (PollyRemarksMinimal && !isEffectiveAssumption(Set, Sign))
+ return false;
+
+ // Do never emit trivial assumptions as they only clutter the output.
+ if (!PollyRemarksMinimal) {
+ isl::set Univ;
+ if (Sign == AS_ASSUMPTION)
+ Univ = isl::set::universe(Set.get_space());
+
+ bool IsTrivial = (Sign == AS_RESTRICTION && Set.is_empty()) ||
+ (Sign == AS_ASSUMPTION && Univ.is_equal(Set));
+
+ if (IsTrivial)
+ return false;
+ }
+
+ switch (Kind) {
+ case ALIASING:
+ AssumptionsAliasing++;
+ break;
+ case INBOUNDS:
+ AssumptionsInbounds++;
+ break;
+ case WRAPPING:
+ AssumptionsWrapping++;
+ break;
+ case UNSIGNED:
+ AssumptionsUnsigned++;
+ break;
+ case COMPLEXITY:
+ AssumptionsComplexity++;
+ break;
+ case PROFITABLE:
+ AssumptionsUnprofitable++;
+ break;
+ case ERRORBLOCK:
+ AssumptionsErrorBlock++;
+ break;
+ case INFINITELOOP:
+ AssumptionsInfiniteLoop++;
+ break;
+ case INVARIANTLOAD:
+ AssumptionsInvariantLoad++;
+ break;
+ case DELINEARIZATION:
+ AssumptionsDelinearization++;
+ break;
+ }
+
+ auto Suffix = Sign == AS_ASSUMPTION ? " assumption:\t" : " restriction:\t";
+ std::string Msg = toString(Kind) + Suffix + stringFromIslObj(Set);
+ if (BB)
+ ORE.emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "AssumpRestrict", Loc, BB)
+ << Msg);
+ else
+ ORE.emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "AssumpRestrict", Loc,
+ R.getEntry())
+ << Msg);
+ return true;
+}
+
+void Scop::addAssumption(AssumptionKind Kind, isl::set Set, DebugLoc Loc,
+ AssumptionSign Sign, BasicBlock *BB,
+ bool RequiresRTC) {
+ // Simplify the assumptions/restrictions first.
+ Set = Set.gist_params(getContext());
+ intersectDefinedBehavior(Set, Sign);
+
+ if (!RequiresRTC)
+ return;
+
+ if (!trackAssumption(Kind, Set, Loc, Sign, BB))
+ return;
+
+ if (Sign == AS_ASSUMPTION)
+ AssumedContext = AssumedContext.intersect(Set).coalesce();
+ else
+ InvalidContext = InvalidContext.unite(Set).coalesce();
+}
+
+void Scop::intersectDefinedBehavior(isl::set Set, AssumptionSign Sign) {
+ if (DefinedBehaviorContext.is_null())
+ return;
+
+ if (Sign == AS_ASSUMPTION)
+ DefinedBehaviorContext = DefinedBehaviorContext.intersect(Set);
+ else
+ DefinedBehaviorContext = DefinedBehaviorContext.subtract(Set);
+
+ // Limit the complexity of the context. If complexity is exceeded, simplify
+ // the set and check again.
+ if (DefinedBehaviorContext.n_basic_set().release() >
+ MaxDisjunktsInDefinedBehaviourContext) {
+ simplify(DefinedBehaviorContext);
+ if (DefinedBehaviorContext.n_basic_set().release() >
+ MaxDisjunktsInDefinedBehaviourContext)
+ DefinedBehaviorContext = {};
+ }
+}
+
+void Scop::invalidate(AssumptionKind Kind, DebugLoc Loc, BasicBlock *BB) {
+ LLVM_DEBUG(dbgs() << "Invalidate SCoP because of reason " << Kind << "\n");
+ addAssumption(Kind, isl::set::empty(getParamSpace()), Loc, AS_ASSUMPTION, BB);
+}
+
+isl::set Scop::getInvalidContext() const { return InvalidContext; }
+
+void Scop::printContext(raw_ostream &OS) const {
+ OS << "Context:\n";
+ OS.indent(4) << Context << "\n";
+
+ OS.indent(4) << "Assumed Context:\n";
+ OS.indent(4) << AssumedContext << "\n";
+
+ OS.indent(4) << "Invalid Context:\n";
+ OS.indent(4) << InvalidContext << "\n";
+
+ OS.indent(4) << "Defined Behavior Context:\n";
+ if (!DefinedBehaviorContext.is_null())
+ OS.indent(4) << DefinedBehaviorContext << "\n";
+ else
+ OS.indent(4) << "<unavailable>\n";
+
+ unsigned Dim = 0;
+ for (const SCEV *Parameter : Parameters)
+ OS.indent(4) << "p" << Dim++ << ": " << *Parameter << "\n";
+}
+
+void Scop::printAliasAssumptions(raw_ostream &OS) const {
+ int noOfGroups = 0;
+ for (const MinMaxVectorPairTy &Pair : MinMaxAliasGroups) {
+ if (Pair.second.size() == 0)
+ noOfGroups += 1;
+ else
+ noOfGroups += Pair.second.size();
+ }
+
+ OS.indent(4) << "Alias Groups (" << noOfGroups << "):\n";
+ if (MinMaxAliasGroups.empty()) {
+ OS.indent(8) << "n/a\n";
+ return;
+ }
+
+ for (const MinMaxVectorPairTy &Pair : MinMaxAliasGroups) {
+
+ // If the group has no read only accesses print the write accesses.
+ if (Pair.second.empty()) {
+ OS.indent(8) << "[[";
+ for (const MinMaxAccessTy &MMANonReadOnly : Pair.first) {
+ OS << " <" << MMANonReadOnly.first << ", " << MMANonReadOnly.second
+ << ">";
+ }
+ OS << " ]]\n";
+ }
+
+ for (const MinMaxAccessTy &MMAReadOnly : Pair.second) {
+ OS.indent(8) << "[[";
+ OS << " <" << MMAReadOnly.first << ", " << MMAReadOnly.second << ">";
+ for (const MinMaxAccessTy &MMANonReadOnly : Pair.first) {
+ OS << " <" << MMANonReadOnly.first << ", " << MMANonReadOnly.second
+ << ">";
+ }
+ OS << " ]]\n";
+ }
+ }
+}
+
+void Scop::printStatements(raw_ostream &OS, bool PrintInstructions) const {
+ OS << "Statements {\n";
+
+ for (const ScopStmt &Stmt : *this) {
+ OS.indent(4);
+ Stmt.print(OS, PrintInstructions);
+ }
+
+ OS.indent(4) << "}\n";
+}
+
+void Scop::printArrayInfo(raw_ostream &OS) const {
+ OS << "Arrays {\n";
+
+ for (auto &Array : arrays())
+ Array->print(OS);
+
+ OS.indent(4) << "}\n";
+
+ OS.indent(4) << "Arrays (Bounds as pw_affs) {\n";
+
+ for (auto &Array : arrays())
+ Array->print(OS, /* SizeAsPwAff */ true);
+
+ OS.indent(4) << "}\n";
+}
+
+void Scop::print(raw_ostream &OS, bool PrintInstructions) const {
+ OS.indent(4) << "Function: " << getFunction().getName() << "\n";
+ OS.indent(4) << "Region: " << getNameStr() << "\n";
+ OS.indent(4) << "Max Loop Depth: " << getMaxLoopDepth() << "\n";
+ OS.indent(4) << "Invariant Accesses: {\n";
+ for (const auto &IAClass : InvariantEquivClasses) {
+ const auto &MAs = IAClass.InvariantAccesses;
+ if (MAs.empty()) {
+ OS.indent(12) << "Class Pointer: " << *IAClass.IdentifyingPointer << "\n";
+ } else {
+ MAs.front()->print(OS);
+ OS.indent(12) << "Execution Context: " << IAClass.ExecutionContext
+ << "\n";
+ }
+ }
+ OS.indent(4) << "}\n";
+ printContext(OS.indent(4));
+ printArrayInfo(OS.indent(4));
+ printAliasAssumptions(OS);
+ printStatements(OS.indent(4), PrintInstructions);
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void Scop::dump() const { print(dbgs(), true); }
+#endif
+
+isl::ctx Scop::getIslCtx() const { return IslCtx.get(); }
+
+__isl_give PWACtx Scop::getPwAff(const SCEV *E, BasicBlock *BB,
+ bool NonNegative,
+ RecordedAssumptionsTy *RecordedAssumptions) {
+ // First try to use the SCEVAffinator to generate a piecewise defined
+ // affine function from @p E in the context of @p BB. If that tasks becomes to
+ // complex the affinator might return a nullptr. In such a case we invalidate
+ // the SCoP and return a dummy value. This way we do not need to add error
+ // handling code to all users of this function.
+ auto PWAC = Affinator.getPwAff(E, BB, RecordedAssumptions);
+ if (!PWAC.first.is_null()) {
+ // TODO: We could use a heuristic and either use:
+ // SCEVAffinator::takeNonNegativeAssumption
+ // or
+ // SCEVAffinator::interpretAsUnsigned
+ // to deal with unsigned or "NonNegative" SCEVs.
+ if (NonNegative)
+ Affinator.takeNonNegativeAssumption(PWAC, RecordedAssumptions);
+ return PWAC;
+ }
+
+ auto DL = BB ? BB->getTerminator()->getDebugLoc() : DebugLoc();
+ invalidate(COMPLEXITY, DL, BB);
+ return Affinator.getPwAff(SE->getZero(E->getType()), BB, RecordedAssumptions);
+}
+
+isl::union_set Scop::getDomains() const {
+ isl_space *EmptySpace = isl_space_params_alloc(getIslCtx().get(), 0);
+ isl_union_set *Domain = isl_union_set_empty(EmptySpace);
+
+ for (const ScopStmt &Stmt : *this)
+ Domain = isl_union_set_add_set(Domain, Stmt.getDomain().release());
+
+ return isl::manage(Domain);
+}
+
+isl::pw_aff Scop::getPwAffOnly(const SCEV *E, BasicBlock *BB,
+ RecordedAssumptionsTy *RecordedAssumptions) {
+ PWACtx PWAC = getPwAff(E, BB, RecordedAssumptions);
+ return PWAC.first;
+}
+
+isl::union_map
+Scop::getAccessesOfType(std::function<bool(MemoryAccess &)> Predicate) {
+ isl::union_map Accesses = isl::union_map::empty(getIslCtx());
+
+ for (ScopStmt &Stmt : *this) {
+ for (MemoryAccess *MA : Stmt) {
+ if (!Predicate(*MA))
+ continue;
+
+ isl::set Domain = Stmt.getDomain();
+ isl::map AccessDomain = MA->getAccessRelation();
+ AccessDomain = AccessDomain.intersect_domain(Domain);
+ Accesses = Accesses.unite(AccessDomain);
+ }
+ }
+
+ return Accesses.coalesce();
+}
+
+isl::union_map Scop::getMustWrites() {
+ return getAccessesOfType([](MemoryAccess &MA) { return MA.isMustWrite(); });
+}
+
+isl::union_map Scop::getMayWrites() {
+ return getAccessesOfType([](MemoryAccess &MA) { return MA.isMayWrite(); });
+}
+
+isl::union_map Scop::getWrites() {
+ return getAccessesOfType([](MemoryAccess &MA) { return MA.isWrite(); });
+}
+
+isl::union_map Scop::getReads() {
+ return getAccessesOfType([](MemoryAccess &MA) { return MA.isRead(); });
+}
+
+isl::union_map Scop::getAccesses() {
+ return getAccessesOfType([](MemoryAccess &MA) { return true; });
+}
+
+isl::union_map Scop::getAccesses(ScopArrayInfo *Array) {
+ return getAccessesOfType(
+ [Array](MemoryAccess &MA) { return MA.getScopArrayInfo() == Array; });
+}
+
+isl::union_map Scop::getSchedule() const {
+ auto Tree = getScheduleTree();
+ return Tree.get_map();
+}
+
+isl::schedule Scop::getScheduleTree() const {
+ return Schedule.intersect_domain(getDomains());
+}
+
+void Scop::setSchedule(isl::union_map NewSchedule) {
+ auto S = isl::schedule::from_domain(getDomains());
+ Schedule = S.insert_partial_schedule(
+ isl::multi_union_pw_aff::from_union_map(NewSchedule));
+ ScheduleModified = true;
+}
+
+void Scop::setScheduleTree(isl::schedule NewSchedule) {
+ Schedule = NewSchedule;
+ ScheduleModified = true;
+}
+
+bool Scop::restrictDomains(isl::union_set Domain) {
+ bool Changed = false;
+ for (ScopStmt &Stmt : *this) {
+ isl::union_set StmtDomain = isl::union_set(Stmt.getDomain());
+ isl::union_set NewStmtDomain = StmtDomain.intersect(Domain);
+
+ if (StmtDomain.is_subset(NewStmtDomain))
+ continue;
+
+ Changed = true;
+
+ NewStmtDomain = NewStmtDomain.coalesce();
+
+ if (NewStmtDomain.is_empty())
+ Stmt.restrictDomain(isl::set::empty(Stmt.getDomainSpace()));
+ else
+ Stmt.restrictDomain(isl::set(NewStmtDomain));
+ }
+ return Changed;
+}
+
+ScalarEvolution *Scop::getSE() const { return SE; }
+
+void Scop::addScopStmt(BasicBlock *BB, StringRef Name, Loop *SurroundingLoop,
+ std::vector<Instruction *> Instructions) {
+ assert(BB && "Unexpected nullptr!");
+ Stmts.emplace_back(*this, *BB, Name, SurroundingLoop, Instructions);
+ auto *Stmt = &Stmts.back();
+ StmtMap[BB].push_back(Stmt);
+ for (Instruction *Inst : Instructions) {
+ assert(!InstStmtMap.count(Inst) &&
+ "Unexpected statement corresponding to the instruction.");
+ InstStmtMap[Inst] = Stmt;
+ }
+}
+
+void Scop::addScopStmt(Region *R, StringRef Name, Loop *SurroundingLoop,
+ std::vector<Instruction *> Instructions) {
+ assert(R && "Unexpected nullptr!");
+ Stmts.emplace_back(*this, *R, Name, SurroundingLoop, Instructions);
+ auto *Stmt = &Stmts.back();
+
+ for (Instruction *Inst : Instructions) {
+ assert(!InstStmtMap.count(Inst) &&
+ "Unexpected statement corresponding to the instruction.");
+ InstStmtMap[Inst] = Stmt;
+ }
+
+ for (BasicBlock *BB : R->blocks()) {
+ StmtMap[BB].push_back(Stmt);
+ if (BB == R->getEntry())
+ continue;
+ for (Instruction &Inst : *BB) {
+ assert(!InstStmtMap.count(&Inst) &&
+ "Unexpected statement corresponding to the instruction.");
+ InstStmtMap[&Inst] = Stmt;
+ }
+ }
+}
+
+ScopStmt *Scop::addScopStmt(isl::map SourceRel, isl::map TargetRel,
+ isl::set Domain) {
+#ifndef NDEBUG
+ isl::set SourceDomain = SourceRel.domain();
+ isl::set TargetDomain = TargetRel.domain();
+ assert(Domain.is_subset(TargetDomain) &&
+ "Target access not defined for complete statement domain");
+ assert(Domain.is_subset(SourceDomain) &&
+ "Source access not defined for complete statement domain");
+#endif
+ Stmts.emplace_back(*this, SourceRel, TargetRel, Domain);
+ CopyStmtsNum++;
+ return &(Stmts.back());
+}
+
+ArrayRef<ScopStmt *> Scop::getStmtListFor(BasicBlock *BB) const {
+ auto StmtMapIt = StmtMap.find(BB);
+ if (StmtMapIt == StmtMap.end())
+ return {};
+ return StmtMapIt->second;
+}
+
+ScopStmt *Scop::getIncomingStmtFor(const Use &U) const {
+ auto *PHI = cast<PHINode>(U.getUser());
+ BasicBlock *IncomingBB = PHI->getIncomingBlock(U);
+
+ // If the value is a non-synthesizable from the incoming block, use the
+ // statement that contains it as user statement.
+ if (auto *IncomingInst = dyn_cast<Instruction>(U.get())) {
+ if (IncomingInst->getParent() == IncomingBB) {
+ if (ScopStmt *IncomingStmt = getStmtFor(IncomingInst))
+ return IncomingStmt;
+ }
+ }
+
+ // Otherwise, use the epilogue/last statement.
+ return getLastStmtFor(IncomingBB);
+}
+
+ScopStmt *Scop::getLastStmtFor(BasicBlock *BB) const {
+ ArrayRef<ScopStmt *> StmtList = getStmtListFor(BB);
+ if (!StmtList.empty())
+ return StmtList.back();
+ return nullptr;
+}
+
+ArrayRef<ScopStmt *> Scop::getStmtListFor(RegionNode *RN) const {
+ if (RN->isSubRegion())
+ return getStmtListFor(RN->getNodeAs<Region>());
+ return getStmtListFor(RN->getNodeAs<BasicBlock>());
+}
+
+ArrayRef<ScopStmt *> Scop::getStmtListFor(Region *R) const {
+ return getStmtListFor(R->getEntry());
+}
+
+int Scop::getRelativeLoopDepth(const Loop *L) const {
+ if (!L || !R.contains(L))
+ return -1;
+ // outermostLoopInRegion always returns nullptr for top level regions
+ if (R.isTopLevelRegion()) {
+ // LoopInfo's depths start at 1, we start at 0
+ return L->getLoopDepth() - 1;
+ } else {
+ Loop *OuterLoop = R.outermostLoopInRegion(const_cast<Loop *>(L));
+ assert(OuterLoop);
+ return L->getLoopDepth() - OuterLoop->getLoopDepth();
+ }
+}
+
+ScopArrayInfo *Scop::getArrayInfoByName(const std::string BaseName) {
+ for (auto &SAI : arrays()) {
+ if (SAI->getName() == BaseName)
+ return SAI;
+ }
+ return nullptr;
+}
+
+void Scop::addAccessData(MemoryAccess *Access) {
+ const ScopArrayInfo *SAI = Access->getOriginalScopArrayInfo();
+ assert(SAI && "can only use after access relations have been constructed");
+
+ if (Access->isOriginalValueKind() && Access->isRead())
+ ValueUseAccs[SAI].push_back(Access);
+ else if (Access->isOriginalAnyPHIKind() && Access->isWrite())
+ PHIIncomingAccs[SAI].push_back(Access);
+}
+
+void Scop::removeAccessData(MemoryAccess *Access) {
+ if (Access->isOriginalValueKind() && Access->isWrite()) {
+ ValueDefAccs.erase(Access->getAccessValue());
+ } else if (Access->isOriginalValueKind() && Access->isRead()) {
+ auto &Uses = ValueUseAccs[Access->getScopArrayInfo()];
+ auto NewEnd = std::remove(Uses.begin(), Uses.end(), Access);
+ Uses.erase(NewEnd, Uses.end());
+ } else if (Access->isOriginalPHIKind() && Access->isRead()) {
+ PHINode *PHI = cast<PHINode>(Access->getAccessInstruction());
+ PHIReadAccs.erase(PHI);
+ } else if (Access->isOriginalAnyPHIKind() && Access->isWrite()) {
+ auto &Incomings = PHIIncomingAccs[Access->getScopArrayInfo()];
+ auto NewEnd = std::remove(Incomings.begin(), Incomings.end(), Access);
+ Incomings.erase(NewEnd, Incomings.end());
+ }
+}
+
+MemoryAccess *Scop::getValueDef(const ScopArrayInfo *SAI) const {
+ assert(SAI->isValueKind());
+
+ Instruction *Val = dyn_cast<Instruction>(SAI->getBasePtr());
+ if (!Val)
+ return nullptr;
+
+ return ValueDefAccs.lookup(Val);
+}
+
+ArrayRef<MemoryAccess *> Scop::getValueUses(const ScopArrayInfo *SAI) const {
+ assert(SAI->isValueKind());
+ auto It = ValueUseAccs.find(SAI);
+ if (It == ValueUseAccs.end())
+ return {};
+ return It->second;
+}
+
+MemoryAccess *Scop::getPHIRead(const ScopArrayInfo *SAI) const {
+ assert(SAI->isPHIKind() || SAI->isExitPHIKind());
+
+ if (SAI->isExitPHIKind())
+ return nullptr;
+
+ PHINode *PHI = cast<PHINode>(SAI->getBasePtr());
+ return PHIReadAccs.lookup(PHI);
+}
+
+ArrayRef<MemoryAccess *> Scop::getPHIIncomings(const ScopArrayInfo *SAI) const {
+ assert(SAI->isPHIKind() || SAI->isExitPHIKind());
+ auto It = PHIIncomingAccs.find(SAI);
+ if (It == PHIIncomingAccs.end())
+ return {};
+ return It->second;
+}
+
+bool Scop::isEscaping(Instruction *Inst) {
+ assert(contains(Inst) && "The concept of escaping makes only sense for "
+ "values defined inside the SCoP");
+
+ for (Use &Use : Inst->uses()) {
+ BasicBlock *UserBB = getUseBlock(Use);
+ if (!contains(UserBB))
+ return true;
+
+ // When the SCoP region exit needs to be simplified, PHIs in the region exit
+ // move to a new basic block such that its incoming blocks are not in the
+ // SCoP anymore.
+ if (hasSingleExitEdge() && isa<PHINode>(Use.getUser()) &&
+ isExit(cast<PHINode>(Use.getUser())->getParent()))
+ return true;
+ }
+ return false;
+}
+
+void Scop::incrementNumberOfAliasingAssumptions(unsigned step) {
+ AssumptionsAliasing += step;
+}
+
+Scop::ScopStatistics Scop::getStatistics() const {
+ ScopStatistics Result;
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_STATS)
+ auto LoopStat = ScopDetection::countBeneficialLoops(&R, *SE, *getLI(), 0);
+
+ int NumTotalLoops = LoopStat.NumLoops;
+ Result.NumBoxedLoops = getBoxedLoops().size();
+ Result.NumAffineLoops = NumTotalLoops - Result.NumBoxedLoops;
+
+ for (const ScopStmt &Stmt : *this) {
+ isl::set Domain = Stmt.getDomain().intersect_params(getContext());
+ bool IsInLoop = Stmt.getNumIterators() >= 1;
+ for (MemoryAccess *MA : Stmt) {
+ if (!MA->isWrite())
+ continue;
+
+ if (MA->isLatestValueKind()) {
+ Result.NumValueWrites += 1;
+ if (IsInLoop)
+ Result.NumValueWritesInLoops += 1;
+ }
+
+ if (MA->isLatestAnyPHIKind()) {
+ Result.NumPHIWrites += 1;
+ if (IsInLoop)
+ Result.NumPHIWritesInLoops += 1;
+ }
+
+ isl::set AccSet =
+ MA->getAccessRelation().intersect_domain(Domain).range();
+ if (AccSet.is_singleton()) {
+ Result.NumSingletonWrites += 1;
+ if (IsInLoop)
+ Result.NumSingletonWritesInLoops += 1;
+ }
+ }
+ }
+#endif
+ return Result;
+}
+
+raw_ostream &polly::operator<<(raw_ostream &OS, const Scop &scop) {
+ scop.print(OS, PollyPrintInstructions);
+ return OS;
+}
+
+//===----------------------------------------------------------------------===//
+void ScopInfoRegionPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequired<LoopInfoWrapperPass>();
+ AU.addRequired<RegionInfoPass>();
+ AU.addRequired<DominatorTreeWrapperPass>();
+ AU.addRequiredTransitive<ScalarEvolutionWrapperPass>();
+ AU.addRequiredTransitive<ScopDetectionWrapperPass>();
+ AU.addRequired<AAResultsWrapperPass>();
+ AU.addRequired<AssumptionCacheTracker>();
+ AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
+ AU.setPreservesAll();
+}
+
+void updateLoopCountStatistic(ScopDetection::LoopStats Stats,
+ Scop::ScopStatistics ScopStats) {
+ assert(Stats.NumLoops == ScopStats.NumAffineLoops + ScopStats.NumBoxedLoops);
+
+ NumScops++;
+ NumLoopsInScop += Stats.NumLoops;
+ MaxNumLoopsInScop =
+ std::max(MaxNumLoopsInScop.getValue(), (unsigned)Stats.NumLoops);
+
+ if (Stats.MaxDepth == 0)
+ NumScopsDepthZero++;
+ else if (Stats.MaxDepth == 1)
+ NumScopsDepthOne++;
+ else if (Stats.MaxDepth == 2)
+ NumScopsDepthTwo++;
+ else if (Stats.MaxDepth == 3)
+ NumScopsDepthThree++;
+ else if (Stats.MaxDepth == 4)
+ NumScopsDepthFour++;
+ else if (Stats.MaxDepth == 5)
+ NumScopsDepthFive++;
+ else
+ NumScopsDepthLarger++;
+
+ NumAffineLoops += ScopStats.NumAffineLoops;
+ NumBoxedLoops += ScopStats.NumBoxedLoops;
+
+ NumValueWrites += ScopStats.NumValueWrites;
+ NumValueWritesInLoops += ScopStats.NumValueWritesInLoops;
+ NumPHIWrites += ScopStats.NumPHIWrites;
+ NumPHIWritesInLoops += ScopStats.NumPHIWritesInLoops;
+ NumSingletonWrites += ScopStats.NumSingletonWrites;
+ NumSingletonWritesInLoops += ScopStats.NumSingletonWritesInLoops;
+}
+
+bool ScopInfoRegionPass::runOnRegion(Region *R, RGPassManager &RGM) {
+ auto &SD = getAnalysis<ScopDetectionWrapperPass>().getSD();
+
+ if (!SD.isMaxRegionInScop(*R))
+ return false;
+
+ Function *F = R->getEntry()->getParent();
+ auto &SE = getAnalysis<ScalarEvolutionWrapperPass>().getSE();
+ auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+ auto &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();
+ auto const &DL = F->getParent()->getDataLayout();
+ auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
+ auto &AC = getAnalysis<AssumptionCacheTracker>().getAssumptionCache(*F);
+ auto &ORE = getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE();
+
+ ScopBuilder SB(R, AC, AA, DL, DT, LI, SD, SE, ORE);
+ S = SB.getScop(); // take ownership of scop object
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_STATS)
+ if (S) {
+ ScopDetection::LoopStats Stats =
+ ScopDetection::countBeneficialLoops(&S->getRegion(), SE, LI, 0);
+ updateLoopCountStatistic(Stats, S->getStatistics());
+ }
+#endif
+
+ return false;
+}
+
+void ScopInfoRegionPass::print(raw_ostream &OS, const Module *) const {
+ if (S)
+ S->print(OS, PollyPrintInstructions);
+ else
+ OS << "Invalid Scop!\n";
+}
+
+char ScopInfoRegionPass::ID = 0;
+
+Pass *polly::createScopInfoRegionPassPass() { return new ScopInfoRegionPass(); }
+
+INITIALIZE_PASS_BEGIN(ScopInfoRegionPass, "polly-scops",
+ "Polly - Create polyhedral description of Scops", false,
+ false);
+INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker);
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(RegionInfoPass);
+INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(ScopDetectionWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass);
+INITIALIZE_PASS_END(ScopInfoRegionPass, "polly-scops",
+ "Polly - Create polyhedral description of Scops", false,
+ false)
+
+//===----------------------------------------------------------------------===//
+ScopInfo::ScopInfo(const DataLayout &DL, ScopDetection &SD, ScalarEvolution &SE,
+ LoopInfo &LI, AliasAnalysis &AA, DominatorTree &DT,
+ AssumptionCache &AC, OptimizationRemarkEmitter &ORE)
+ : DL(DL), SD(SD), SE(SE), LI(LI), AA(AA), DT(DT), AC(AC), ORE(ORE) {
+ recompute();
+}
+
+void ScopInfo::recompute() {
+ RegionToScopMap.clear();
+ /// Create polyhedral description of scops for all the valid regions of a
+ /// function.
+ for (auto &It : SD) {
+ Region *R = const_cast<Region *>(It);
+ if (!SD.isMaxRegionInScop(*R))
+ continue;
+
+ ScopBuilder SB(R, AC, AA, DL, DT, LI, SD, SE, ORE);
+ std::unique_ptr<Scop> S = SB.getScop();
+ if (!S)
+ continue;
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_STATS)
+ ScopDetection::LoopStats Stats =
+ ScopDetection::countBeneficialLoops(&S->getRegion(), SE, LI, 0);
+ updateLoopCountStatistic(Stats, S->getStatistics());
+#endif
+ bool Inserted = RegionToScopMap.insert({R, std::move(S)}).second;
+ assert(Inserted && "Building Scop for the same region twice!");
+ (void)Inserted;
+ }
+}
+
+bool ScopInfo::invalidate(Function &F, const PreservedAnalyses &PA,
+ FunctionAnalysisManager::Invalidator &Inv) {
+ // Check whether the analysis, all analyses on functions have been preserved
+ // or anything we're holding references to is being invalidated
+ auto PAC = PA.getChecker<ScopInfoAnalysis>();
+ return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>()) ||
+ Inv.invalidate<ScopAnalysis>(F, PA) ||
+ Inv.invalidate<ScalarEvolutionAnalysis>(F, PA) ||
+ Inv.invalidate<LoopAnalysis>(F, PA) ||
+ Inv.invalidate<AAManager>(F, PA) ||
+ Inv.invalidate<DominatorTreeAnalysis>(F, PA) ||
+ Inv.invalidate<AssumptionAnalysis>(F, PA);
+}
+
+AnalysisKey ScopInfoAnalysis::Key;
+
+ScopInfoAnalysis::Result ScopInfoAnalysis::run(Function &F,
+ FunctionAnalysisManager &FAM) {
+ auto &SD = FAM.getResult<ScopAnalysis>(F);
+ auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);
+ auto &LI = FAM.getResult<LoopAnalysis>(F);
+ auto &AA = FAM.getResult<AAManager>(F);
+ auto &DT = FAM.getResult<DominatorTreeAnalysis>(F);
+ auto &AC = FAM.getResult<AssumptionAnalysis>(F);
+ auto &DL = F.getParent()->getDataLayout();
+ auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
+ return {DL, SD, SE, LI, AA, DT, AC, ORE};
+}
+
+PreservedAnalyses ScopInfoPrinterPass::run(Function &F,
+ FunctionAnalysisManager &FAM) {
+ auto &SI = FAM.getResult<ScopInfoAnalysis>(F);
+ // Since the legacy PM processes Scops in bottom up, we print them in reverse
+ // order here to keep the output persistent
+ for (auto &It : reverse(SI)) {
+ if (It.second)
+ It.second->print(Stream, PollyPrintInstructions);
+ else
+ Stream << "Invalid Scop!\n";
+ }
+ return PreservedAnalyses::all();
+}
+
+void ScopInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequired<LoopInfoWrapperPass>();
+ AU.addRequired<RegionInfoPass>();
+ AU.addRequired<DominatorTreeWrapperPass>();
+ AU.addRequiredTransitive<ScalarEvolutionWrapperPass>();
+ AU.addRequiredTransitive<ScopDetectionWrapperPass>();
+ AU.addRequired<AAResultsWrapperPass>();
+ AU.addRequired<AssumptionCacheTracker>();
+ AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
+ AU.setPreservesAll();
+}
+
+bool ScopInfoWrapperPass::runOnFunction(Function &F) {
+ auto &SD = getAnalysis<ScopDetectionWrapperPass>().getSD();
+ auto &SE = getAnalysis<ScalarEvolutionWrapperPass>().getSE();
+ auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+ auto &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();
+ auto const &DL = F.getParent()->getDataLayout();
+ auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
+ auto &AC = getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
+ auto &ORE = getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE();
+
+ Result.reset(new ScopInfo{DL, SD, SE, LI, AA, DT, AC, ORE});
+ return false;
+}
+
+void ScopInfoWrapperPass::print(raw_ostream &OS, const Module *) const {
+ for (auto &It : *Result) {
+ if (It.second)
+ It.second->print(OS, PollyPrintInstructions);
+ else
+ OS << "Invalid Scop!\n";
+ }
+}
+
+char ScopInfoWrapperPass::ID = 0;
+
+Pass *polly::createScopInfoWrapperPassPass() {
+ return new ScopInfoWrapperPass();
+}
+
+INITIALIZE_PASS_BEGIN(
+ ScopInfoWrapperPass, "polly-function-scops",
+ "Polly - Create polyhedral description of all Scops of a function", false,
+ false);
+INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker);
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(RegionInfoPass);
+INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(ScopDetectionWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass);
+INITIALIZE_PASS_END(
+ ScopInfoWrapperPass, "polly-function-scops",
+ "Polly - Create polyhedral description of all Scops of a function", false,
+ false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopPass.cpp b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopPass.cpp
new file mode 100644
index 00000000000..9f8b5c23358
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Analysis/ScopPass.cpp
@@ -0,0 +1,169 @@
+//===- ScopPass.cpp - The base class of Passes that operate on Polly IR ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the definitions of the ScopPass members.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/ScopPass.h"
+#include "polly/ScopInfo.h"
+#include "llvm/Analysis/BasicAliasAnalysis.h"
+#include "llvm/Analysis/GlobalsModRef.h"
+#include "llvm/Analysis/LazyBlockFrequencyInfo.h"
+#include "llvm/Analysis/LazyBranchProbabilityInfo.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/Analysis/ScalarEvolutionAliasAnalysis.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+
+using namespace llvm;
+using namespace polly;
+
+bool ScopPass::runOnRegion(Region *R, RGPassManager &RGM) {
+ S = nullptr;
+
+ if (skipRegion(*R))
+ return false;
+
+ if ((S = getAnalysis<ScopInfoRegionPass>().getScop()))
+ return runOnScop(*S);
+
+ return false;
+}
+
+void ScopPass::print(raw_ostream &OS, const Module *M) const {
+ if (S)
+ printScop(OS, *S);
+}
+
+void ScopPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequired<ScopInfoRegionPass>();
+
+ AU.addPreserved<AAResultsWrapperPass>();
+ AU.addPreserved<BasicAAWrapperPass>();
+ AU.addPreserved<LoopInfoWrapperPass>();
+ AU.addPreserved<DominatorTreeWrapperPass>();
+ AU.addPreserved<GlobalsAAWrapperPass>();
+ AU.addPreserved<ScopDetectionWrapperPass>();
+ AU.addPreserved<ScalarEvolutionWrapperPass>();
+ AU.addPreserved<SCEVAAWrapperPass>();
+ AU.addPreserved<OptimizationRemarkEmitterWrapperPass>();
+ AU.addPreserved<LazyBlockFrequencyInfoPass>();
+ AU.addPreserved<LazyBranchProbabilityInfoPass>();
+ AU.addPreserved<RegionInfoPass>();
+ AU.addPreserved<ScopInfoRegionPass>();
+ AU.addPreserved<TargetTransformInfoWrapperPass>();
+}
+
+namespace polly {
+template class OwningInnerAnalysisManagerProxy<ScopAnalysisManager, Function>;
+}
+
+namespace llvm {
+
+template class PassManager<Scop, ScopAnalysisManager,
+ ScopStandardAnalysisResults &, SPMUpdater &>;
+template class InnerAnalysisManagerProxy<ScopAnalysisManager, Function>;
+template class OuterAnalysisManagerProxy<FunctionAnalysisManager, Scop,
+ ScopStandardAnalysisResults &>;
+
+template <>
+PreservedAnalyses
+PassManager<Scop, ScopAnalysisManager, ScopStandardAnalysisResults &,
+ SPMUpdater &>::run(Scop &S, ScopAnalysisManager &AM,
+ ScopStandardAnalysisResults &AR, SPMUpdater &U) {
+ auto PA = PreservedAnalyses::all();
+ for (auto &Pass : Passes) {
+ auto PassPA = Pass->run(S, AM, AR, U);
+
+ AM.invalidate(S, PassPA);
+ PA.intersect(std::move(PassPA));
+ }
+
+ // All analyses for 'this' Scop have been invalidated above.
+ // If ScopPasses affect break other scops they have to propagate this
+ // information through the updater
+ PA.preserveSet<AllAnalysesOn<Scop>>();
+ return PA;
+}
+
+bool ScopAnalysisManagerFunctionProxy::Result::invalidate(
+ Function &F, const PreservedAnalyses &PA,
+ FunctionAnalysisManager::Invalidator &Inv) {
+
+ // First, check whether our ScopInfo is about to be invalidated
+ auto PAC = PA.getChecker<ScopAnalysisManagerFunctionProxy>();
+ if (!(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>()) ||
+ Inv.invalidate<ScopInfoAnalysis>(F, PA) ||
+ Inv.invalidate<ScalarEvolutionAnalysis>(F, PA) ||
+ Inv.invalidate<LoopAnalysis>(F, PA) ||
+ Inv.invalidate<DominatorTreeAnalysis>(F, PA)) {
+
+ // As everything depends on ScopInfo, we must drop all existing results
+ for (auto &S : *SI)
+ if (auto *scop = S.second.get())
+ if (InnerAM)
+ InnerAM->clear(*scop, scop->getName());
+
+ InnerAM = nullptr;
+ return true; // Invalidate the proxy result as well.
+ }
+
+ bool allPreserved = PA.allAnalysesInSetPreserved<AllAnalysesOn<Scop>>();
+
+ // Invalidate all non-preserved analyses
+ // Even if all analyses were preserved, we still need to run deferred
+ // invalidation
+ for (auto &S : *SI) {
+ Optional<PreservedAnalyses> InnerPA;
+ auto *scop = S.second.get();
+ if (!scop)
+ continue;
+
+ if (auto *OuterProxy =
+ InnerAM->getCachedResult<FunctionAnalysisManagerScopProxy>(*scop)) {
+ for (const auto &InvPair : OuterProxy->getOuterInvalidations()) {
+ auto *OuterAnalysisID = InvPair.first;
+ const auto &InnerAnalysisIDs = InvPair.second;
+
+ if (Inv.invalidate(OuterAnalysisID, F, PA)) {
+ if (!InnerPA)
+ InnerPA = PA;
+ for (auto *InnerAnalysisID : InnerAnalysisIDs)
+ InnerPA->abandon(InnerAnalysisID);
+ }
+ }
+
+ if (InnerPA) {
+ InnerAM->invalidate(*scop, *InnerPA);
+ continue;
+ }
+ }
+
+ if (!allPreserved)
+ InnerAM->invalidate(*scop, PA);
+ }
+
+ return false; // This proxy is still valid
+}
+
+template <>
+ScopAnalysisManagerFunctionProxy::Result
+ScopAnalysisManagerFunctionProxy::run(Function &F,
+ FunctionAnalysisManager &FAM) {
+ return Result(*InnerAM, FAM.getResult<ScopInfoAnalysis>(F));
+}
+} // namespace llvm
+
+namespace polly {
+template <>
+OwningScopAnalysisManagerFunctionProxy::Result
+OwningScopAnalysisManagerFunctionProxy::run(Function &F,
+ FunctionAnalysisManager &FAM) {
+ return Result(InnerAM, FAM.getResult<ScopInfoAnalysis>(F));
+}
+} // namespace polly
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/BlockGenerators.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/BlockGenerators.cpp
new file mode 100644
index 00000000000..e946c7ef960
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/BlockGenerators.cpp
@@ -0,0 +1,1795 @@
+//===--- BlockGenerators.cpp - Generate code for statements -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the BlockGenerator and VectorBlockGenerator classes,
+// which generate sequential code and vectorized code for a polyhedral
+// statement, respectively.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/BlockGenerators.h"
+#include "polly/CodeGen/IslExprBuilder.h"
+#include "polly/CodeGen/RuntimeDebugBuilder.h"
+#include "polly/Options.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/ISLTools.h"
+#include "polly/Support/ScopHelper.h"
+#include "polly/Support/VirtualInstruction.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/Local.h"
+#include "isl/ast.h"
+#include <deque>
+
+using namespace llvm;
+using namespace polly;
+
+static cl::opt<bool> Aligned("enable-polly-aligned",
+ cl::desc("Assumed aligned memory accesses."),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+bool PollyDebugPrinting;
+static cl::opt<bool, true> DebugPrintingX(
+ "polly-codegen-add-debug-printing",
+ cl::desc("Add printf calls that show the values loaded/stored."),
+ cl::location(PollyDebugPrinting), cl::Hidden, cl::init(false),
+ cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> TraceStmts(
+ "polly-codegen-trace-stmts",
+ cl::desc("Add printf calls that print the statement being executed"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> TraceScalars(
+ "polly-codegen-trace-scalars",
+ cl::desc("Add printf calls that print the values of all scalar values "
+ "used in a statement. Requires -polly-codegen-trace-stmts."),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+BlockGenerator::BlockGenerator(
+ PollyIRBuilder &B, LoopInfo &LI, ScalarEvolution &SE, DominatorTree &DT,
+ AllocaMapTy &ScalarMap, EscapeUsersAllocaMapTy &EscapeMap,
+ ValueMapT &GlobalMap, IslExprBuilder *ExprBuilder, BasicBlock *StartBlock)
+ : Builder(B), LI(LI), SE(SE), ExprBuilder(ExprBuilder), DT(DT),
+ EntryBB(nullptr), ScalarMap(ScalarMap), EscapeMap(EscapeMap),
+ GlobalMap(GlobalMap), StartBlock(StartBlock) {}
+
+Value *BlockGenerator::trySynthesizeNewValue(ScopStmt &Stmt, Value *Old,
+ ValueMapT &BBMap,
+ LoopToScevMapT &LTS,
+ Loop *L) const {
+ if (!SE.isSCEVable(Old->getType()))
+ return nullptr;
+
+ const SCEV *Scev = SE.getSCEVAtScope(Old, L);
+ if (!Scev)
+ return nullptr;
+
+ if (isa<SCEVCouldNotCompute>(Scev))
+ return nullptr;
+
+ const SCEV *NewScev = SCEVLoopAddRecRewriter::rewrite(Scev, LTS, SE);
+ ValueMapT VTV;
+ VTV.insert(BBMap.begin(), BBMap.end());
+ VTV.insert(GlobalMap.begin(), GlobalMap.end());
+
+ Scop &S = *Stmt.getParent();
+ const DataLayout &DL = S.getFunction().getParent()->getDataLayout();
+ auto IP = Builder.GetInsertPoint();
+
+ assert(IP != Builder.GetInsertBlock()->end() &&
+ "Only instructions can be insert points for SCEVExpander");
+ Value *Expanded =
+ expandCodeFor(S, SE, DL, "polly", NewScev, Old->getType(), &*IP, &VTV,
+ StartBlock->getSinglePredecessor());
+
+ BBMap[Old] = Expanded;
+ return Expanded;
+}
+
+Value *BlockGenerator::getNewValue(ScopStmt &Stmt, Value *Old, ValueMapT &BBMap,
+ LoopToScevMapT &LTS, Loop *L) const {
+
+ auto lookupGlobally = [this](Value *Old) -> Value * {
+ Value *New = GlobalMap.lookup(Old);
+ if (!New)
+ return nullptr;
+
+ // Required by:
+ // * Isl/CodeGen/OpenMP/invariant_base_pointer_preloaded.ll
+ // * Isl/CodeGen/OpenMP/invariant_base_pointer_preloaded_different_bb.ll
+ // * Isl/CodeGen/OpenMP/invariant_base_pointer_preloaded_pass_only_needed.ll
+ // * Isl/CodeGen/OpenMP/invariant_base_pointers_preloaded.ll
+ // * Isl/CodeGen/OpenMP/loop-body-references-outer-values-3.ll
+ // * Isl/CodeGen/OpenMP/single_loop_with_loop_invariant_baseptr.ll
+ // GlobalMap should be a mapping from (value in original SCoP) to (copied
+ // value in generated SCoP), without intermediate mappings, which might
+ // easily require transitiveness as well.
+ if (Value *NewRemapped = GlobalMap.lookup(New))
+ New = NewRemapped;
+
+ // No test case for this code.
+ if (Old->getType()->getScalarSizeInBits() <
+ New->getType()->getScalarSizeInBits())
+ New = Builder.CreateTruncOrBitCast(New, Old->getType());
+
+ return New;
+ };
+
+ Value *New = nullptr;
+ auto VUse = VirtualUse::create(&Stmt, L, Old, true);
+ switch (VUse.getKind()) {
+ case VirtualUse::Block:
+ // BasicBlock are constants, but the BlockGenerator copies them.
+ New = BBMap.lookup(Old);
+ break;
+
+ case VirtualUse::Constant:
+ // Used by:
+ // * Isl/CodeGen/OpenMP/reference-argument-from-non-affine-region.ll
+ // Constants should not be redefined. In this case, the GlobalMap just
+ // contains a mapping to the same constant, which is unnecessary, but
+ // harmless.
+ if ((New = lookupGlobally(Old)))
+ break;
+
+ assert(!BBMap.count(Old));
+ New = Old;
+ break;
+
+ case VirtualUse::ReadOnly:
+ assert(!GlobalMap.count(Old));
+
+ // Required for:
+ // * Isl/CodeGen/MemAccess/create_arrays.ll
+ // * Isl/CodeGen/read-only-scalars.ll
+ // * ScheduleOptimizer/pattern-matching-based-opts_10.ll
+ // For some reason these reload a read-only value. The reloaded value ends
+ // up in BBMap, buts its value should be identical.
+ //
+ // Required for:
+ // * Isl/CodeGen/OpenMP/single_loop_with_param.ll
+ // The parallel subfunctions need to reference the read-only value from the
+ // parent function, this is done by reloading them locally.
+ if ((New = BBMap.lookup(Old)))
+ break;
+
+ New = Old;
+ break;
+
+ case VirtualUse::Synthesizable:
+ // Used by:
+ // * Isl/CodeGen/OpenMP/loop-body-references-outer-values-3.ll
+ // * Isl/CodeGen/OpenMP/recomputed-srem.ll
+ // * Isl/CodeGen/OpenMP/reference-other-bb.ll
+ // * Isl/CodeGen/OpenMP/two-parallel-loops-reference-outer-indvar.ll
+ // For some reason synthesizable values end up in GlobalMap. Their values
+ // are the same as trySynthesizeNewValue would return. The legacy
+ // implementation prioritized GlobalMap, so this is what we do here as well.
+ // Ideally, synthesizable values should not end up in GlobalMap.
+ if ((New = lookupGlobally(Old)))
+ break;
+
+ // Required for:
+ // * Isl/CodeGen/RuntimeDebugBuilder/combine_different_values.ll
+ // * Isl/CodeGen/getNumberOfIterations.ll
+ // * Isl/CodeGen/non_affine_float_compare.ll
+ // * ScheduleOptimizer/pattern-matching-based-opts_10.ll
+ // Ideally, synthesizable values are synthesized by trySynthesizeNewValue,
+ // not precomputed (SCEVExpander has its own caching mechanism).
+ // These tests fail without this, but I think trySynthesizeNewValue would
+ // just re-synthesize the same instructions.
+ if ((New = BBMap.lookup(Old)))
+ break;
+
+ New = trySynthesizeNewValue(Stmt, Old, BBMap, LTS, L);
+ break;
+
+ case VirtualUse::Hoisted:
+ // TODO: Hoisted invariant loads should be found in GlobalMap only, but not
+ // redefined locally (which will be ignored anyway). That is, the following
+ // assertion should apply: assert(!BBMap.count(Old))
+
+ New = lookupGlobally(Old);
+ break;
+
+ case VirtualUse::Intra:
+ case VirtualUse::Inter:
+ assert(!GlobalMap.count(Old) &&
+ "Intra and inter-stmt values are never global");
+ New = BBMap.lookup(Old);
+ break;
+ }
+ assert(New && "Unexpected scalar dependence in region!");
+ return New;
+}
+
+void BlockGenerator::copyInstScalar(ScopStmt &Stmt, Instruction *Inst,
+ ValueMapT &BBMap, LoopToScevMapT &LTS) {
+ // We do not generate debug intrinsics as we did not investigate how to
+ // copy them correctly. At the current state, they just crash the code
+ // generation as the meta-data operands are not correctly copied.
+ if (isa<DbgInfoIntrinsic>(Inst))
+ return;
+
+ Instruction *NewInst = Inst->clone();
+
+ // Replace old operands with the new ones.
+ for (Value *OldOperand : Inst->operands()) {
+ Value *NewOperand =
+ getNewValue(Stmt, OldOperand, BBMap, LTS, getLoopForStmt(Stmt));
+
+ if (!NewOperand) {
+ assert(!isa<StoreInst>(NewInst) &&
+ "Store instructions are always needed!");
+ NewInst->deleteValue();
+ return;
+ }
+
+ NewInst->replaceUsesOfWith(OldOperand, NewOperand);
+ }
+
+ Builder.Insert(NewInst);
+ BBMap[Inst] = NewInst;
+
+ // When copying the instruction onto the Module meant for the GPU,
+ // debug metadata attached to an instruction causes all related
+ // metadata to be pulled into the Module. This includes the DICompileUnit,
+ // which will not be listed in llvm.dbg.cu of the Module since the Module
+ // doesn't contain one. This fails the verification of the Module and the
+ // subsequent generation of the ASM string.
+ if (NewInst->getModule() != Inst->getModule())
+ NewInst->setDebugLoc(llvm::DebugLoc());
+
+ if (!NewInst->getType()->isVoidTy())
+ NewInst->setName("p_" + Inst->getName());
+}
+
+Value *
+BlockGenerator::generateLocationAccessed(ScopStmt &Stmt, MemAccInst Inst,
+ ValueMapT &BBMap, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses) {
+ const MemoryAccess &MA = Stmt.getArrayAccessFor(Inst);
+ return generateLocationAccessed(
+ Stmt, getLoopForStmt(Stmt),
+ Inst.isNull() ? nullptr : Inst.getPointerOperand(), BBMap, LTS,
+ NewAccesses, MA.getId().release(), MA.getAccessValue()->getType());
+}
+
+Value *BlockGenerator::generateLocationAccessed(
+ ScopStmt &Stmt, Loop *L, Value *Pointer, ValueMapT &BBMap,
+ LoopToScevMapT &LTS, isl_id_to_ast_expr *NewAccesses, __isl_take isl_id *Id,
+ Type *ExpectedType) {
+ isl_ast_expr *AccessExpr = isl_id_to_ast_expr_get(NewAccesses, Id);
+
+ if (AccessExpr) {
+ AccessExpr = isl_ast_expr_address_of(AccessExpr);
+ auto Address = ExprBuilder->create(AccessExpr);
+
+ // Cast the address of this memory access to a pointer type that has the
+ // same element type as the original access, but uses the address space of
+ // the newly generated pointer.
+ auto OldPtrTy = ExpectedType->getPointerTo();
+ auto NewPtrTy = Address->getType();
+ OldPtrTy = PointerType::getWithSamePointeeType(
+ OldPtrTy, NewPtrTy->getPointerAddressSpace());
+
+ if (OldPtrTy != NewPtrTy)
+ Address = Builder.CreateBitOrPointerCast(Address, OldPtrTy);
+ return Address;
+ }
+ assert(
+ Pointer &&
+ "If expression was not generated, must use the original pointer value");
+ return getNewValue(Stmt, Pointer, BBMap, LTS, L);
+}
+
+Value *
+BlockGenerator::getImplicitAddress(MemoryAccess &Access, Loop *L,
+ LoopToScevMapT &LTS, ValueMapT &BBMap,
+ __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ if (Access.isLatestArrayKind())
+ return generateLocationAccessed(*Access.getStatement(), L, nullptr, BBMap,
+ LTS, NewAccesses, Access.getId().release(),
+ Access.getAccessValue()->getType());
+
+ return getOrCreateAlloca(Access);
+}
+
+Loop *BlockGenerator::getLoopForStmt(const ScopStmt &Stmt) const {
+ auto *StmtBB = Stmt.getEntryBlock();
+ return LI.getLoopFor(StmtBB);
+}
+
+Value *BlockGenerator::generateArrayLoad(ScopStmt &Stmt, LoadInst *Load,
+ ValueMapT &BBMap, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses) {
+ if (Value *PreloadLoad = GlobalMap.lookup(Load))
+ return PreloadLoad;
+
+ Value *NewPointer =
+ generateLocationAccessed(Stmt, Load, BBMap, LTS, NewAccesses);
+ Value *ScalarLoad =
+ Builder.CreateAlignedLoad(Load->getType(), NewPointer, Load->getAlign(),
+ Load->getName() + "_p_scalar_");
+
+ if (PollyDebugPrinting)
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "Load from ", NewPointer,
+ ": ", ScalarLoad, "\n");
+
+ return ScalarLoad;
+}
+
+void BlockGenerator::generateArrayStore(ScopStmt &Stmt, StoreInst *Store,
+ ValueMapT &BBMap, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses) {
+ MemoryAccess &MA = Stmt.getArrayAccessFor(Store);
+ isl::set AccDom = MA.getAccessRelation().domain();
+ std::string Subject = MA.getId().get_name();
+
+ generateConditionalExecution(Stmt, AccDom, Subject.c_str(), [&, this]() {
+ Value *NewPointer =
+ generateLocationAccessed(Stmt, Store, BBMap, LTS, NewAccesses);
+ Value *ValueOperand = getNewValue(Stmt, Store->getValueOperand(), BBMap,
+ LTS, getLoopForStmt(Stmt));
+
+ if (PollyDebugPrinting)
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "Store to ", NewPointer,
+ ": ", ValueOperand, "\n");
+
+ Builder.CreateAlignedStore(ValueOperand, NewPointer, Store->getAlign());
+ });
+}
+
+bool BlockGenerator::canSyntheziseInStmt(ScopStmt &Stmt, Instruction *Inst) {
+ Loop *L = getLoopForStmt(Stmt);
+ return (Stmt.isBlockStmt() || !Stmt.getRegion()->contains(L)) &&
+ canSynthesize(Inst, *Stmt.getParent(), &SE, L);
+}
+
+void BlockGenerator::copyInstruction(ScopStmt &Stmt, Instruction *Inst,
+ ValueMapT &BBMap, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses) {
+ // Terminator instructions control the control flow. They are explicitly
+ // expressed in the clast and do not need to be copied.
+ if (Inst->isTerminator())
+ return;
+
+ // Synthesizable statements will be generated on-demand.
+ if (canSyntheziseInStmt(Stmt, Inst))
+ return;
+
+ if (auto *Load = dyn_cast<LoadInst>(Inst)) {
+ Value *NewLoad = generateArrayLoad(Stmt, Load, BBMap, LTS, NewAccesses);
+ // Compute NewLoad before its insertion in BBMap to make the insertion
+ // deterministic.
+ BBMap[Load] = NewLoad;
+ return;
+ }
+
+ if (auto *Store = dyn_cast<StoreInst>(Inst)) {
+ // Identified as redundant by -polly-simplify.
+ if (!Stmt.getArrayAccessOrNULLFor(Store))
+ return;
+
+ generateArrayStore(Stmt, Store, BBMap, LTS, NewAccesses);
+ return;
+ }
+
+ if (auto *PHI = dyn_cast<PHINode>(Inst)) {
+ copyPHIInstruction(Stmt, PHI, BBMap, LTS);
+ return;
+ }
+
+ // Skip some special intrinsics for which we do not adjust the semantics to
+ // the new schedule. All others are handled like every other instruction.
+ if (isIgnoredIntrinsic(Inst))
+ return;
+
+ copyInstScalar(Stmt, Inst, BBMap, LTS);
+}
+
+void BlockGenerator::removeDeadInstructions(BasicBlock *BB, ValueMapT &BBMap) {
+ auto NewBB = Builder.GetInsertBlock();
+ for (auto I = NewBB->rbegin(); I != NewBB->rend(); I++) {
+ Instruction *NewInst = &*I;
+
+ if (!isInstructionTriviallyDead(NewInst))
+ continue;
+
+ for (auto Pair : BBMap)
+ if (Pair.second == NewInst) {
+ BBMap.erase(Pair.first);
+ }
+
+ NewInst->eraseFromParent();
+ I = NewBB->rbegin();
+ }
+}
+
+void BlockGenerator::copyStmt(ScopStmt &Stmt, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses) {
+ assert(Stmt.isBlockStmt() &&
+ "Only block statements can be copied by the block generator");
+
+ ValueMapT BBMap;
+
+ BasicBlock *BB = Stmt.getBasicBlock();
+ copyBB(Stmt, BB, BBMap, LTS, NewAccesses);
+ removeDeadInstructions(BB, BBMap);
+}
+
+BasicBlock *BlockGenerator::splitBB(BasicBlock *BB) {
+ BasicBlock *CopyBB = SplitBlock(Builder.GetInsertBlock(),
+ &*Builder.GetInsertPoint(), &DT, &LI);
+ CopyBB->setName("polly.stmt." + BB->getName());
+ return CopyBB;
+}
+
+BasicBlock *BlockGenerator::copyBB(ScopStmt &Stmt, BasicBlock *BB,
+ ValueMapT &BBMap, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses) {
+ BasicBlock *CopyBB = splitBB(BB);
+ Builder.SetInsertPoint(&CopyBB->front());
+ generateScalarLoads(Stmt, LTS, BBMap, NewAccesses);
+ generateBeginStmtTrace(Stmt, LTS, BBMap);
+
+ copyBB(Stmt, BB, CopyBB, BBMap, LTS, NewAccesses);
+
+ // After a basic block was copied store all scalars that escape this block in
+ // their alloca.
+ generateScalarStores(Stmt, LTS, BBMap, NewAccesses);
+ return CopyBB;
+}
+
+void BlockGenerator::copyBB(ScopStmt &Stmt, BasicBlock *BB, BasicBlock *CopyBB,
+ ValueMapT &BBMap, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *NewAccesses) {
+ EntryBB = &CopyBB->getParent()->getEntryBlock();
+
+ // Block statements and the entry blocks of region statement are code
+ // generated from instruction lists. This allow us to optimize the
+ // instructions that belong to a certain scop statement. As the code
+ // structure of region statements might be arbitrary complex, optimizing the
+ // instruction list is not yet supported.
+ if (Stmt.isBlockStmt() || (Stmt.isRegionStmt() && Stmt.getEntryBlock() == BB))
+ for (Instruction *Inst : Stmt.getInstructions())
+ copyInstruction(Stmt, Inst, BBMap, LTS, NewAccesses);
+ else
+ for (Instruction &Inst : *BB)
+ copyInstruction(Stmt, &Inst, BBMap, LTS, NewAccesses);
+}
+
+Value *BlockGenerator::getOrCreateAlloca(const MemoryAccess &Access) {
+ assert(!Access.isLatestArrayKind() && "Trying to get alloca for array kind");
+
+ return getOrCreateAlloca(Access.getLatestScopArrayInfo());
+}
+
+Value *BlockGenerator::getOrCreateAlloca(const ScopArrayInfo *Array) {
+ assert(!Array->isArrayKind() && "Trying to get alloca for array kind");
+
+ auto &Addr = ScalarMap[Array];
+
+ if (Addr) {
+ // Allow allocas to be (temporarily) redirected once by adding a new
+ // old-alloca-addr to new-addr mapping to GlobalMap. This functionality
+ // is used for example by the OpenMP code generation where a first use
+ // of a scalar while still in the host code allocates a normal alloca with
+ // getOrCreateAlloca. When the values of this scalar are accessed during
+ // the generation of the parallel subfunction, these values are copied over
+ // to the parallel subfunction and each request for a scalar alloca slot
+ // must be forwarded to the temporary in-subfunction slot. This mapping is
+ // removed when the subfunction has been generated and again normal host
+ // code is generated. Due to the following reasons it is not possible to
+ // perform the GlobalMap lookup right after creating the alloca below, but
+ // instead we need to check GlobalMap at each call to getOrCreateAlloca:
+ //
+ // 1) GlobalMap may be changed multiple times (for each parallel loop),
+ // 2) The temporary mapping is commonly only known after the initial
+ // alloca has already been generated, and
+ // 3) The original alloca value must be restored after leaving the
+ // sub-function.
+ if (Value *NewAddr = GlobalMap.lookup(&*Addr))
+ return NewAddr;
+ return Addr;
+ }
+
+ Type *Ty = Array->getElementType();
+ Value *ScalarBase = Array->getBasePtr();
+ std::string NameExt;
+ if (Array->isPHIKind())
+ NameExt = ".phiops";
+ else
+ NameExt = ".s2a";
+
+ const DataLayout &DL = Builder.GetInsertBlock()->getModule()->getDataLayout();
+
+ Addr =
+ new AllocaInst(Ty, DL.getAllocaAddrSpace(), nullptr,
+ DL.getPrefTypeAlign(Ty), ScalarBase->getName() + NameExt);
+ EntryBB = &Builder.GetInsertBlock()->getParent()->getEntryBlock();
+ Addr->insertBefore(&*EntryBB->getFirstInsertionPt());
+
+ return Addr;
+}
+
+void BlockGenerator::handleOutsideUsers(const Scop &S, ScopArrayInfo *Array) {
+ Instruction *Inst = cast<Instruction>(Array->getBasePtr());
+
+ // If there are escape users we get the alloca for this instruction and put it
+ // in the EscapeMap for later finalization. Lastly, if the instruction was
+ // copied multiple times we already did this and can exit.
+ if (EscapeMap.count(Inst))
+ return;
+
+ EscapeUserVectorTy EscapeUsers;
+ for (User *U : Inst->users()) {
+
+ // Non-instruction user will never escape.
+ Instruction *UI = dyn_cast<Instruction>(U);
+ if (!UI)
+ continue;
+
+ if (S.contains(UI))
+ continue;
+
+ EscapeUsers.push_back(UI);
+ }
+
+ // Exit if no escape uses were found.
+ if (EscapeUsers.empty())
+ return;
+
+ // Get or create an escape alloca for this instruction.
+ auto *ScalarAddr = getOrCreateAlloca(Array);
+
+ // Remember that this instruction has escape uses and the escape alloca.
+ EscapeMap[Inst] = std::make_pair(ScalarAddr, std::move(EscapeUsers));
+}
+
+void BlockGenerator::generateScalarLoads(
+ ScopStmt &Stmt, LoopToScevMapT &LTS, ValueMapT &BBMap,
+ __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ for (MemoryAccess *MA : Stmt) {
+ if (MA->isOriginalArrayKind() || MA->isWrite())
+ continue;
+
+#ifndef NDEBUG
+ auto StmtDom =
+ Stmt.getDomain().intersect_params(Stmt.getParent()->getContext());
+ auto AccDom = MA->getAccessRelation().domain();
+ assert(!StmtDom.is_subset(AccDom).is_false() &&
+ "Scalar must be loaded in all statement instances");
+#endif
+
+ auto *Address =
+ getImplicitAddress(*MA, getLoopForStmt(Stmt), LTS, BBMap, NewAccesses);
+ assert((!isa<Instruction>(Address) ||
+ DT.dominates(cast<Instruction>(Address)->getParent(),
+ Builder.GetInsertBlock())) &&
+ "Domination violation");
+ BBMap[MA->getAccessValue()] = Builder.CreateLoad(
+ MA->getElementType(), Address, Address->getName() + ".reload");
+ }
+}
+
+Value *BlockGenerator::buildContainsCondition(ScopStmt &Stmt,
+ const isl::set &Subdomain) {
+ isl::ast_build AstBuild = Stmt.getAstBuild();
+ isl::set Domain = Stmt.getDomain();
+
+ isl::union_map USchedule = AstBuild.get_schedule();
+ USchedule = USchedule.intersect_domain(Domain);
+
+ assert(!USchedule.is_empty());
+ isl::map Schedule = isl::map::from_union_map(USchedule);
+
+ isl::set ScheduledDomain = Schedule.range();
+ isl::set ScheduledSet = Subdomain.apply(Schedule);
+
+ isl::ast_build RestrictedBuild = AstBuild.restrict(ScheduledDomain);
+
+ isl::ast_expr IsInSet = RestrictedBuild.expr_from(ScheduledSet);
+ Value *IsInSetExpr = ExprBuilder->create(IsInSet.copy());
+ IsInSetExpr = Builder.CreateICmpNE(
+ IsInSetExpr, ConstantInt::get(IsInSetExpr->getType(), 0));
+
+ return IsInSetExpr;
+}
+
+void BlockGenerator::generateConditionalExecution(
+ ScopStmt &Stmt, const isl::set &Subdomain, StringRef Subject,
+ const std::function<void()> &GenThenFunc) {
+ isl::set StmtDom = Stmt.getDomain();
+
+ // If the condition is a tautology, don't generate a condition around the
+ // code.
+ bool IsPartialWrite =
+ !StmtDom.intersect_params(Stmt.getParent()->getContext())
+ .is_subset(Subdomain);
+ if (!IsPartialWrite) {
+ GenThenFunc();
+ return;
+ }
+
+ // Generate the condition.
+ Value *Cond = buildContainsCondition(Stmt, Subdomain);
+
+ // Don't call GenThenFunc if it is never executed. An ast index expression
+ // might not be defined in this case.
+ if (auto *Const = dyn_cast<ConstantInt>(Cond))
+ if (Const->isZero())
+ return;
+
+ BasicBlock *HeadBlock = Builder.GetInsertBlock();
+ StringRef BlockName = HeadBlock->getName();
+
+ // Generate the conditional block.
+ SplitBlockAndInsertIfThen(Cond, &*Builder.GetInsertPoint(), false, nullptr,
+ &DT, &LI);
+ BranchInst *Branch = cast<BranchInst>(HeadBlock->getTerminator());
+ BasicBlock *ThenBlock = Branch->getSuccessor(0);
+ BasicBlock *TailBlock = Branch->getSuccessor(1);
+
+ // Assign descriptive names.
+ if (auto *CondInst = dyn_cast<Instruction>(Cond))
+ CondInst->setName("polly." + Subject + ".cond");
+ ThenBlock->setName(BlockName + "." + Subject + ".partial");
+ TailBlock->setName(BlockName + ".cont");
+
+ // Put the client code into the conditional block and continue in the merge
+ // block afterwards.
+ Builder.SetInsertPoint(ThenBlock, ThenBlock->getFirstInsertionPt());
+ GenThenFunc();
+ Builder.SetInsertPoint(TailBlock, TailBlock->getFirstInsertionPt());
+}
+
+static std::string getInstName(Value *Val) {
+ std::string Result;
+ raw_string_ostream OS(Result);
+ Val->printAsOperand(OS, false);
+ return OS.str();
+}
+
+void BlockGenerator::generateBeginStmtTrace(ScopStmt &Stmt, LoopToScevMapT &LTS,
+ ValueMapT &BBMap) {
+ if (!TraceStmts)
+ return;
+
+ Scop *S = Stmt.getParent();
+ const char *BaseName = Stmt.getBaseName();
+
+ isl::ast_build AstBuild = Stmt.getAstBuild();
+ isl::set Domain = Stmt.getDomain();
+
+ isl::union_map USchedule = AstBuild.get_schedule().intersect_domain(Domain);
+ isl::map Schedule = isl::map::from_union_map(USchedule);
+ assert(Schedule.is_empty().is_false() &&
+ "The stmt must have a valid instance");
+
+ isl::multi_pw_aff ScheduleMultiPwAff =
+ isl::pw_multi_aff::from_map(Schedule.reverse());
+ isl::ast_build RestrictedBuild = AstBuild.restrict(Schedule.range());
+
+ // Sequence of strings to print.
+ SmallVector<llvm::Value *, 8> Values;
+
+ // Print the name of the statement.
+ // TODO: Indent by the depth of the statement instance in the schedule tree.
+ Values.push_back(RuntimeDebugBuilder::getPrintableString(Builder, BaseName));
+ Values.push_back(RuntimeDebugBuilder::getPrintableString(Builder, "("));
+
+ // Add the coordinate of the statement instance.
+ for (unsigned i : rangeIslSize(0, ScheduleMultiPwAff.dim(isl::dim::out))) {
+ if (i > 0)
+ Values.push_back(RuntimeDebugBuilder::getPrintableString(Builder, ","));
+
+ isl::ast_expr IsInSet = RestrictedBuild.expr_from(ScheduleMultiPwAff.at(i));
+ Values.push_back(ExprBuilder->create(IsInSet.copy()));
+ }
+
+ if (TraceScalars) {
+ Values.push_back(RuntimeDebugBuilder::getPrintableString(Builder, ")"));
+ DenseSet<Instruction *> Encountered;
+
+ // Add the value of each scalar (and the result of PHIs) used in the
+ // statement.
+ // TODO: Values used in region-statements.
+ for (Instruction *Inst : Stmt.insts()) {
+ if (!RuntimeDebugBuilder::isPrintable(Inst->getType()))
+ continue;
+
+ if (isa<PHINode>(Inst)) {
+ Values.push_back(RuntimeDebugBuilder::getPrintableString(Builder, " "));
+ Values.push_back(RuntimeDebugBuilder::getPrintableString(
+ Builder, getInstName(Inst)));
+ Values.push_back(RuntimeDebugBuilder::getPrintableString(Builder, "="));
+ Values.push_back(getNewValue(Stmt, Inst, BBMap, LTS,
+ LI.getLoopFor(Inst->getParent())));
+ } else {
+ for (Value *Op : Inst->operand_values()) {
+ // Do not print values that cannot change during the execution of the
+ // SCoP.
+ auto *OpInst = dyn_cast<Instruction>(Op);
+ if (!OpInst)
+ continue;
+ if (!S->contains(OpInst))
+ continue;
+
+ // Print each scalar at most once, and exclude values defined in the
+ // statement itself.
+ if (Encountered.count(OpInst))
+ continue;
+
+ Values.push_back(
+ RuntimeDebugBuilder::getPrintableString(Builder, " "));
+ Values.push_back(RuntimeDebugBuilder::getPrintableString(
+ Builder, getInstName(OpInst)));
+ Values.push_back(
+ RuntimeDebugBuilder::getPrintableString(Builder, "="));
+ Values.push_back(getNewValue(Stmt, OpInst, BBMap, LTS,
+ LI.getLoopFor(Inst->getParent())));
+ Encountered.insert(OpInst);
+ }
+ }
+
+ Encountered.insert(Inst);
+ }
+
+ Values.push_back(RuntimeDebugBuilder::getPrintableString(Builder, "\n"));
+ } else {
+ Values.push_back(RuntimeDebugBuilder::getPrintableString(Builder, ")\n"));
+ }
+
+ RuntimeDebugBuilder::createCPUPrinter(Builder, ArrayRef<Value *>(Values));
+}
+
+void BlockGenerator::generateScalarStores(
+ ScopStmt &Stmt, LoopToScevMapT &LTS, ValueMapT &BBMap,
+ __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ Loop *L = LI.getLoopFor(Stmt.getBasicBlock());
+
+ assert(Stmt.isBlockStmt() &&
+ "Region statements need to use the generateScalarStores() function in "
+ "the RegionGenerator");
+
+ for (MemoryAccess *MA : Stmt) {
+ if (MA->isOriginalArrayKind() || MA->isRead())
+ continue;
+
+ isl::set AccDom = MA->getAccessRelation().domain();
+ std::string Subject = MA->getId().get_name();
+
+ generateConditionalExecution(
+ Stmt, AccDom, Subject.c_str(), [&, this, MA]() {
+ Value *Val = MA->getAccessValue();
+ if (MA->isAnyPHIKind()) {
+ assert(MA->getIncoming().size() >= 1 &&
+ "Block statements have exactly one exiting block, or "
+ "multiple but "
+ "with same incoming block and value");
+ assert(std::all_of(MA->getIncoming().begin(),
+ MA->getIncoming().end(),
+ [&](std::pair<BasicBlock *, Value *> p) -> bool {
+ return p.first == Stmt.getBasicBlock();
+ }) &&
+ "Incoming block must be statement's block");
+ Val = MA->getIncoming()[0].second;
+ }
+ auto Address = getImplicitAddress(*MA, getLoopForStmt(Stmt), LTS,
+ BBMap, NewAccesses);
+
+ Val = getNewValue(Stmt, Val, BBMap, LTS, L);
+ assert((!isa<Instruction>(Val) ||
+ DT.dominates(cast<Instruction>(Val)->getParent(),
+ Builder.GetInsertBlock())) &&
+ "Domination violation");
+ assert((!isa<Instruction>(Address) ||
+ DT.dominates(cast<Instruction>(Address)->getParent(),
+ Builder.GetInsertBlock())) &&
+ "Domination violation");
+
+ // The new Val might have a different type than the old Val due to
+ // ScalarEvolution looking through bitcasts.
+ Address = Builder.CreateBitOrPointerCast(
+ Address, Val->getType()->getPointerTo(
+ Address->getType()->getPointerAddressSpace()));
+
+ Builder.CreateStore(Val, Address);
+ });
+ }
+}
+
+void BlockGenerator::createScalarInitialization(Scop &S) {
+ BasicBlock *ExitBB = S.getExit();
+ BasicBlock *PreEntryBB = S.getEnteringBlock();
+
+ Builder.SetInsertPoint(&*StartBlock->begin());
+
+ for (auto &Array : S.arrays()) {
+ if (Array->getNumberOfDimensions() != 0)
+ continue;
+ if (Array->isPHIKind()) {
+ // For PHI nodes, the only values we need to store are the ones that
+ // reach the PHI node from outside the region. In general there should
+ // only be one such incoming edge and this edge should enter through
+ // 'PreEntryBB'.
+ auto PHI = cast<PHINode>(Array->getBasePtr());
+
+ for (auto BI = PHI->block_begin(), BE = PHI->block_end(); BI != BE; BI++)
+ if (!S.contains(*BI) && *BI != PreEntryBB)
+ llvm_unreachable("Incoming edges from outside the scop should always "
+ "come from PreEntryBB");
+
+ int Idx = PHI->getBasicBlockIndex(PreEntryBB);
+ if (Idx < 0)
+ continue;
+
+ Value *ScalarValue = PHI->getIncomingValue(Idx);
+
+ Builder.CreateStore(ScalarValue, getOrCreateAlloca(Array));
+ continue;
+ }
+
+ auto *Inst = dyn_cast<Instruction>(Array->getBasePtr());
+
+ if (Inst && S.contains(Inst))
+ continue;
+
+ // PHI nodes that are not marked as such in their SAI object are either exit
+ // PHI nodes we model as common scalars but without initialization, or
+ // incoming phi nodes that need to be initialized. Check if the first is the
+ // case for Inst and do not create and initialize memory if so.
+ if (auto *PHI = dyn_cast_or_null<PHINode>(Inst))
+ if (!S.hasSingleExitEdge() && PHI->getBasicBlockIndex(ExitBB) >= 0)
+ continue;
+
+ Builder.CreateStore(Array->getBasePtr(), getOrCreateAlloca(Array));
+ }
+}
+
+void BlockGenerator::createScalarFinalization(Scop &S) {
+ // The exit block of the __unoptimized__ region.
+ BasicBlock *ExitBB = S.getExitingBlock();
+ // The merge block __just after__ the region and the optimized region.
+ BasicBlock *MergeBB = S.getExit();
+
+ // The exit block of the __optimized__ region.
+ BasicBlock *OptExitBB = *(pred_begin(MergeBB));
+ if (OptExitBB == ExitBB)
+ OptExitBB = *(++pred_begin(MergeBB));
+
+ Builder.SetInsertPoint(OptExitBB->getTerminator());
+ for (const auto &EscapeMapping : EscapeMap) {
+ // Extract the escaping instruction and the escaping users as well as the
+ // alloca the instruction was demoted to.
+ Instruction *EscapeInst = EscapeMapping.first;
+ const auto &EscapeMappingValue = EscapeMapping.second;
+ const EscapeUserVectorTy &EscapeUsers = EscapeMappingValue.second;
+ auto *ScalarAddr = cast<AllocaInst>(&*EscapeMappingValue.first);
+
+ // Reload the demoted instruction in the optimized version of the SCoP.
+ Value *EscapeInstReload =
+ Builder.CreateLoad(ScalarAddr->getAllocatedType(), ScalarAddr,
+ EscapeInst->getName() + ".final_reload");
+ EscapeInstReload =
+ Builder.CreateBitOrPointerCast(EscapeInstReload, EscapeInst->getType());
+
+ // Create the merge PHI that merges the optimized and unoptimized version.
+ PHINode *MergePHI = PHINode::Create(EscapeInst->getType(), 2,
+ EscapeInst->getName() + ".merge");
+ MergePHI->insertBefore(&*MergeBB->getFirstInsertionPt());
+
+ // Add the respective values to the merge PHI.
+ MergePHI->addIncoming(EscapeInstReload, OptExitBB);
+ MergePHI->addIncoming(EscapeInst, ExitBB);
+
+ // The information of scalar evolution about the escaping instruction needs
+ // to be revoked so the new merged instruction will be used.
+ if (SE.isSCEVable(EscapeInst->getType()))
+ SE.forgetValue(EscapeInst);
+
+ // Replace all uses of the demoted instruction with the merge PHI.
+ for (Instruction *EUser : EscapeUsers)
+ EUser->replaceUsesOfWith(EscapeInst, MergePHI);
+ }
+}
+
+void BlockGenerator::findOutsideUsers(Scop &S) {
+ for (auto &Array : S.arrays()) {
+
+ if (Array->getNumberOfDimensions() != 0)
+ continue;
+
+ if (Array->isPHIKind())
+ continue;
+
+ auto *Inst = dyn_cast<Instruction>(Array->getBasePtr());
+
+ if (!Inst)
+ continue;
+
+ // Scop invariant hoisting moves some of the base pointers out of the scop.
+ // We can ignore these, as the invariant load hoisting already registers the
+ // relevant outside users.
+ if (!S.contains(Inst))
+ continue;
+
+ handleOutsideUsers(S, Array);
+ }
+}
+
+void BlockGenerator::createExitPHINodeMerges(Scop &S) {
+ if (S.hasSingleExitEdge())
+ return;
+
+ auto *ExitBB = S.getExitingBlock();
+ auto *MergeBB = S.getExit();
+ auto *AfterMergeBB = MergeBB->getSingleSuccessor();
+ BasicBlock *OptExitBB = *(pred_begin(MergeBB));
+ if (OptExitBB == ExitBB)
+ OptExitBB = *(++pred_begin(MergeBB));
+
+ Builder.SetInsertPoint(OptExitBB->getTerminator());
+
+ for (auto &SAI : S.arrays()) {
+ auto *Val = SAI->getBasePtr();
+
+ // Only Value-like scalars need a merge PHI. Exit block PHIs receive either
+ // the original PHI's value or the reloaded incoming values from the
+ // generated code. An llvm::Value is merged between the original code's
+ // value or the generated one.
+ if (!SAI->isExitPHIKind())
+ continue;
+
+ PHINode *PHI = dyn_cast<PHINode>(Val);
+ if (!PHI)
+ continue;
+
+ if (PHI->getParent() != AfterMergeBB)
+ continue;
+
+ std::string Name = PHI->getName().str();
+ Value *ScalarAddr = getOrCreateAlloca(SAI);
+ Value *Reload = Builder.CreateLoad(SAI->getElementType(), ScalarAddr,
+ Name + ".ph.final_reload");
+ Reload = Builder.CreateBitOrPointerCast(Reload, PHI->getType());
+ Value *OriginalValue = PHI->getIncomingValueForBlock(MergeBB);
+ assert((!isa<Instruction>(OriginalValue) ||
+ cast<Instruction>(OriginalValue)->getParent() != MergeBB) &&
+ "Original value must no be one we just generated.");
+ auto *MergePHI = PHINode::Create(PHI->getType(), 2, Name + ".ph.merge");
+ MergePHI->insertBefore(&*MergeBB->getFirstInsertionPt());
+ MergePHI->addIncoming(Reload, OptExitBB);
+ MergePHI->addIncoming(OriginalValue, ExitBB);
+ int Idx = PHI->getBasicBlockIndex(MergeBB);
+ PHI->setIncomingValue(Idx, MergePHI);
+ }
+}
+
+void BlockGenerator::invalidateScalarEvolution(Scop &S) {
+ for (auto &Stmt : S)
+ if (Stmt.isCopyStmt())
+ continue;
+ else if (Stmt.isBlockStmt())
+ for (auto &Inst : *Stmt.getBasicBlock())
+ SE.forgetValue(&Inst);
+ else if (Stmt.isRegionStmt())
+ for (auto *BB : Stmt.getRegion()->blocks())
+ for (auto &Inst : *BB)
+ SE.forgetValue(&Inst);
+ else
+ llvm_unreachable("Unexpected statement type found");
+
+ // Invalidate SCEV of loops surrounding the EscapeUsers.
+ for (const auto &EscapeMapping : EscapeMap) {
+ const EscapeUserVectorTy &EscapeUsers = EscapeMapping.second.second;
+ for (Instruction *EUser : EscapeUsers) {
+ if (Loop *L = LI.getLoopFor(EUser->getParent()))
+ while (L) {
+ SE.forgetLoop(L);
+ L = L->getParentLoop();
+ }
+ }
+ }
+}
+
+void BlockGenerator::finalizeSCoP(Scop &S) {
+ findOutsideUsers(S);
+ createScalarInitialization(S);
+ createExitPHINodeMerges(S);
+ createScalarFinalization(S);
+ invalidateScalarEvolution(S);
+}
+
+VectorBlockGenerator::VectorBlockGenerator(BlockGenerator &BlockGen,
+ std::vector<LoopToScevMapT> &VLTS,
+ isl_map *Schedule)
+ : BlockGenerator(BlockGen), VLTS(VLTS), Schedule(Schedule) {
+ assert(Schedule && "No statement domain provided");
+}
+
+Value *VectorBlockGenerator::getVectorValue(ScopStmt &Stmt, Value *Old,
+ ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps,
+ Loop *L) {
+ if (Value *NewValue = VectorMap.lookup(Old))
+ return NewValue;
+
+ int Width = getVectorWidth();
+
+ Value *Vector = UndefValue::get(FixedVectorType::get(Old->getType(), Width));
+
+ for (int Lane = 0; Lane < Width; Lane++)
+ Vector = Builder.CreateInsertElement(
+ Vector, getNewValue(Stmt, Old, ScalarMaps[Lane], VLTS[Lane], L),
+ Builder.getInt32(Lane));
+
+ VectorMap[Old] = Vector;
+
+ return Vector;
+}
+
+Value *VectorBlockGenerator::generateStrideOneLoad(
+ ScopStmt &Stmt, LoadInst *Load, VectorValueMapT &ScalarMaps,
+ __isl_keep isl_id_to_ast_expr *NewAccesses, bool NegativeStride = false) {
+ unsigned VectorWidth = getVectorWidth();
+ Type *VectorType = FixedVectorType::get(Load->getType(), VectorWidth);
+ Type *VectorPtrType =
+ PointerType::get(VectorType, Load->getPointerAddressSpace());
+ unsigned Offset = NegativeStride ? VectorWidth - 1 : 0;
+
+ Value *NewPointer = generateLocationAccessed(Stmt, Load, ScalarMaps[Offset],
+ VLTS[Offset], NewAccesses);
+ Value *VectorPtr =
+ Builder.CreateBitCast(NewPointer, VectorPtrType, "vector_ptr");
+ LoadInst *VecLoad = Builder.CreateLoad(VectorType, VectorPtr,
+ Load->getName() + "_p_vec_full");
+ if (!Aligned)
+ VecLoad->setAlignment(Align(8));
+
+ if (NegativeStride) {
+ SmallVector<Constant *, 16> Indices;
+ for (int i = VectorWidth - 1; i >= 0; i--)
+ Indices.push_back(ConstantInt::get(Builder.getInt32Ty(), i));
+ Constant *SV = llvm::ConstantVector::get(Indices);
+ Value *RevVecLoad = Builder.CreateShuffleVector(
+ VecLoad, VecLoad, SV, Load->getName() + "_reverse");
+ return RevVecLoad;
+ }
+
+ return VecLoad;
+}
+
+Value *VectorBlockGenerator::generateStrideZeroLoad(
+ ScopStmt &Stmt, LoadInst *Load, ValueMapT &BBMap,
+ __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ Type *VectorType = FixedVectorType::get(Load->getType(), 1);
+ Type *VectorPtrType =
+ PointerType::get(VectorType, Load->getPointerAddressSpace());
+ Value *NewPointer =
+ generateLocationAccessed(Stmt, Load, BBMap, VLTS[0], NewAccesses);
+ Value *VectorPtr = Builder.CreateBitCast(NewPointer, VectorPtrType,
+ Load->getName() + "_p_vec_p");
+ LoadInst *ScalarLoad = Builder.CreateLoad(VectorType, VectorPtr,
+ Load->getName() + "_p_splat_one");
+
+ if (!Aligned)
+ ScalarLoad->setAlignment(Align(8));
+
+ Constant *SplatVector = Constant::getNullValue(
+ FixedVectorType::get(Builder.getInt32Ty(), getVectorWidth()));
+
+ Value *VectorLoad = Builder.CreateShuffleVector(
+ ScalarLoad, ScalarLoad, SplatVector, Load->getName() + "_p_splat");
+ return VectorLoad;
+}
+
+Value *VectorBlockGenerator::generateUnknownStrideLoad(
+ ScopStmt &Stmt, LoadInst *Load, VectorValueMapT &ScalarMaps,
+ __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ int VectorWidth = getVectorWidth();
+ Type *ElemTy = Load->getType();
+ auto *FVTy = FixedVectorType::get(ElemTy, VectorWidth);
+
+ Value *Vector = UndefValue::get(FVTy);
+
+ for (int i = 0; i < VectorWidth; i++) {
+ Value *NewPointer = generateLocationAccessed(Stmt, Load, ScalarMaps[i],
+ VLTS[i], NewAccesses);
+ Value *ScalarLoad =
+ Builder.CreateLoad(ElemTy, NewPointer, Load->getName() + "_p_scalar_");
+ Vector = Builder.CreateInsertElement(
+ Vector, ScalarLoad, Builder.getInt32(i), Load->getName() + "_p_vec_");
+ }
+
+ return Vector;
+}
+
+void VectorBlockGenerator::generateLoad(
+ ScopStmt &Stmt, LoadInst *Load, ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps, __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ if (Value *PreloadLoad = GlobalMap.lookup(Load)) {
+ VectorMap[Load] = Builder.CreateVectorSplat(getVectorWidth(), PreloadLoad,
+ Load->getName() + "_p");
+ return;
+ }
+
+ if (!VectorType::isValidElementType(Load->getType())) {
+ for (int i = 0; i < getVectorWidth(); i++)
+ ScalarMaps[i][Load] =
+ generateArrayLoad(Stmt, Load, ScalarMaps[i], VLTS[i], NewAccesses);
+ return;
+ }
+
+ const MemoryAccess &Access = Stmt.getArrayAccessFor(Load);
+
+ // Make sure we have scalar values available to access the pointer to
+ // the data location.
+ extractScalarValues(Load, VectorMap, ScalarMaps);
+
+ Value *NewLoad;
+ if (Access.isStrideZero(isl::manage_copy(Schedule)))
+ NewLoad = generateStrideZeroLoad(Stmt, Load, ScalarMaps[0], NewAccesses);
+ else if (Access.isStrideOne(isl::manage_copy(Schedule)))
+ NewLoad = generateStrideOneLoad(Stmt, Load, ScalarMaps, NewAccesses);
+ else if (Access.isStrideX(isl::manage_copy(Schedule), -1))
+ NewLoad = generateStrideOneLoad(Stmt, Load, ScalarMaps, NewAccesses, true);
+ else
+ NewLoad = generateUnknownStrideLoad(Stmt, Load, ScalarMaps, NewAccesses);
+
+ VectorMap[Load] = NewLoad;
+}
+
+void VectorBlockGenerator::copyUnaryInst(ScopStmt &Stmt, UnaryInstruction *Inst,
+ ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps) {
+ int VectorWidth = getVectorWidth();
+ Value *NewOperand = getVectorValue(Stmt, Inst->getOperand(0), VectorMap,
+ ScalarMaps, getLoopForStmt(Stmt));
+
+ assert(isa<CastInst>(Inst) && "Can not generate vector code for instruction");
+
+ const CastInst *Cast = dyn_cast<CastInst>(Inst);
+ auto *DestType = FixedVectorType::get(Inst->getType(), VectorWidth);
+ VectorMap[Inst] = Builder.CreateCast(Cast->getOpcode(), NewOperand, DestType);
+}
+
+void VectorBlockGenerator::copyBinaryInst(ScopStmt &Stmt, BinaryOperator *Inst,
+ ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps) {
+ Loop *L = getLoopForStmt(Stmt);
+ Value *OpZero = Inst->getOperand(0);
+ Value *OpOne = Inst->getOperand(1);
+
+ Value *NewOpZero, *NewOpOne;
+ NewOpZero = getVectorValue(Stmt, OpZero, VectorMap, ScalarMaps, L);
+ NewOpOne = getVectorValue(Stmt, OpOne, VectorMap, ScalarMaps, L);
+
+ Value *NewInst = Builder.CreateBinOp(Inst->getOpcode(), NewOpZero, NewOpOne,
+ Inst->getName() + "p_vec");
+ VectorMap[Inst] = NewInst;
+}
+
+void VectorBlockGenerator::copyStore(
+ ScopStmt &Stmt, StoreInst *Store, ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps, __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ const MemoryAccess &Access = Stmt.getArrayAccessFor(Store);
+
+ Value *Vector = getVectorValue(Stmt, Store->getValueOperand(), VectorMap,
+ ScalarMaps, getLoopForStmt(Stmt));
+
+ // Make sure we have scalar values available to access the pointer to
+ // the data location.
+ extractScalarValues(Store, VectorMap, ScalarMaps);
+
+ if (Access.isStrideOne(isl::manage_copy(Schedule))) {
+ Type *VectorType = FixedVectorType::get(Store->getValueOperand()->getType(),
+ getVectorWidth());
+ Type *VectorPtrType =
+ PointerType::get(VectorType, Store->getPointerAddressSpace());
+ Value *NewPointer = generateLocationAccessed(Stmt, Store, ScalarMaps[0],
+ VLTS[0], NewAccesses);
+
+ Value *VectorPtr =
+ Builder.CreateBitCast(NewPointer, VectorPtrType, "vector_ptr");
+ StoreInst *Store = Builder.CreateStore(Vector, VectorPtr);
+
+ if (!Aligned)
+ Store->setAlignment(Align(8));
+ } else {
+ for (unsigned i = 0; i < ScalarMaps.size(); i++) {
+ Value *Scalar = Builder.CreateExtractElement(Vector, Builder.getInt32(i));
+ Value *NewPointer = generateLocationAccessed(Stmt, Store, ScalarMaps[i],
+ VLTS[i], NewAccesses);
+ Builder.CreateStore(Scalar, NewPointer);
+ }
+ }
+}
+
+bool VectorBlockGenerator::hasVectorOperands(const Instruction *Inst,
+ ValueMapT &VectorMap) {
+ for (Value *Operand : Inst->operands())
+ if (VectorMap.count(Operand))
+ return true;
+ return false;
+}
+
+bool VectorBlockGenerator::extractScalarValues(const Instruction *Inst,
+ ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps) {
+ bool HasVectorOperand = false;
+ int VectorWidth = getVectorWidth();
+
+ for (Value *Operand : Inst->operands()) {
+ ValueMapT::iterator VecOp = VectorMap.find(Operand);
+
+ if (VecOp == VectorMap.end())
+ continue;
+
+ HasVectorOperand = true;
+ Value *NewVector = VecOp->second;
+
+ for (int i = 0; i < VectorWidth; ++i) {
+ ValueMapT &SM = ScalarMaps[i];
+
+ // If there is one scalar extracted, all scalar elements should have
+ // already been extracted by the code here. So no need to check for the
+ // existence of all of them.
+ if (SM.count(Operand))
+ break;
+
+ SM[Operand] =
+ Builder.CreateExtractElement(NewVector, Builder.getInt32(i));
+ }
+ }
+
+ return HasVectorOperand;
+}
+
+void VectorBlockGenerator::copyInstScalarized(
+ ScopStmt &Stmt, Instruction *Inst, ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps, __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ bool HasVectorOperand;
+ int VectorWidth = getVectorWidth();
+
+ HasVectorOperand = extractScalarValues(Inst, VectorMap, ScalarMaps);
+
+ for (int VectorLane = 0; VectorLane < getVectorWidth(); VectorLane++)
+ BlockGenerator::copyInstruction(Stmt, Inst, ScalarMaps[VectorLane],
+ VLTS[VectorLane], NewAccesses);
+
+ if (!VectorType::isValidElementType(Inst->getType()) || !HasVectorOperand)
+ return;
+
+ // Make the result available as vector value.
+ auto *FVTy = FixedVectorType::get(Inst->getType(), VectorWidth);
+ Value *Vector = UndefValue::get(FVTy);
+
+ for (int i = 0; i < VectorWidth; i++)
+ Vector = Builder.CreateInsertElement(Vector, ScalarMaps[i][Inst],
+ Builder.getInt32(i));
+
+ VectorMap[Inst] = Vector;
+}
+
+int VectorBlockGenerator::getVectorWidth() { return VLTS.size(); }
+
+void VectorBlockGenerator::copyInstruction(
+ ScopStmt &Stmt, Instruction *Inst, ValueMapT &VectorMap,
+ VectorValueMapT &ScalarMaps, __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ // Terminator instructions control the control flow. They are explicitly
+ // expressed in the clast and do not need to be copied.
+ if (Inst->isTerminator())
+ return;
+
+ if (canSyntheziseInStmt(Stmt, Inst))
+ return;
+
+ if (auto *Load = dyn_cast<LoadInst>(Inst)) {
+ generateLoad(Stmt, Load, VectorMap, ScalarMaps, NewAccesses);
+ return;
+ }
+
+ if (hasVectorOperands(Inst, VectorMap)) {
+ if (auto *Store = dyn_cast<StoreInst>(Inst)) {
+ // Identified as redundant by -polly-simplify.
+ if (!Stmt.getArrayAccessOrNULLFor(Store))
+ return;
+
+ copyStore(Stmt, Store, VectorMap, ScalarMaps, NewAccesses);
+ return;
+ }
+
+ if (auto *Unary = dyn_cast<UnaryInstruction>(Inst)) {
+ copyUnaryInst(Stmt, Unary, VectorMap, ScalarMaps);
+ return;
+ }
+
+ if (auto *Binary = dyn_cast<BinaryOperator>(Inst)) {
+ copyBinaryInst(Stmt, Binary, VectorMap, ScalarMaps);
+ return;
+ }
+
+ // Fallthrough: We generate scalar instructions, if we don't know how to
+ // generate vector code.
+ }
+
+ copyInstScalarized(Stmt, Inst, VectorMap, ScalarMaps, NewAccesses);
+}
+
+void VectorBlockGenerator::generateScalarVectorLoads(
+ ScopStmt &Stmt, ValueMapT &VectorBlockMap) {
+ for (MemoryAccess *MA : Stmt) {
+ if (MA->isArrayKind() || MA->isWrite())
+ continue;
+
+ auto *Address = getOrCreateAlloca(*MA);
+ Type *VectorType = FixedVectorType::get(MA->getElementType(), 1);
+ Type *VectorPtrType = PointerType::get(
+ VectorType, Address->getType()->getPointerAddressSpace());
+ Value *VectorPtr = Builder.CreateBitCast(Address, VectorPtrType,
+ Address->getName() + "_p_vec_p");
+ auto *Val = Builder.CreateLoad(VectorType, VectorPtr,
+ Address->getName() + ".reload");
+ Constant *SplatVector = Constant::getNullValue(
+ FixedVectorType::get(Builder.getInt32Ty(), getVectorWidth()));
+
+ Value *VectorVal = Builder.CreateShuffleVector(
+ Val, Val, SplatVector, Address->getName() + "_p_splat");
+ VectorBlockMap[MA->getAccessValue()] = VectorVal;
+ }
+}
+
+void VectorBlockGenerator::verifyNoScalarStores(ScopStmt &Stmt) {
+ for (MemoryAccess *MA : Stmt) {
+ if (MA->isArrayKind() || MA->isRead())
+ continue;
+
+ llvm_unreachable("Scalar stores not expected in vector loop");
+ }
+}
+
+void VectorBlockGenerator::copyStmt(
+ ScopStmt &Stmt, __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ assert(Stmt.isBlockStmt() &&
+ "TODO: Only block statements can be copied by the vector block "
+ "generator");
+
+ BasicBlock *BB = Stmt.getBasicBlock();
+ BasicBlock *CopyBB = SplitBlock(Builder.GetInsertBlock(),
+ &*Builder.GetInsertPoint(), &DT, &LI);
+ CopyBB->setName("polly.stmt." + BB->getName());
+ Builder.SetInsertPoint(&CopyBB->front());
+
+ // Create two maps that store the mapping from the original instructions of
+ // the old basic block to their copies in the new basic block. Those maps
+ // are basic block local.
+ //
+ // As vector code generation is supported there is one map for scalar values
+ // and one for vector values.
+ //
+ // In case we just do scalar code generation, the vectorMap is not used and
+ // the scalarMap has just one dimension, which contains the mapping.
+ //
+ // In case vector code generation is done, an instruction may either appear
+ // in the vector map once (as it is calculating >vectorwidth< values at a
+ // time. Or (if the values are calculated using scalar operations), it
+ // appears once in every dimension of the scalarMap.
+ VectorValueMapT ScalarBlockMap(getVectorWidth());
+ ValueMapT VectorBlockMap;
+
+ generateScalarVectorLoads(Stmt, VectorBlockMap);
+
+ for (Instruction *Inst : Stmt.getInstructions())
+ copyInstruction(Stmt, Inst, VectorBlockMap, ScalarBlockMap, NewAccesses);
+
+ verifyNoScalarStores(Stmt);
+}
+
+BasicBlock *RegionGenerator::repairDominance(BasicBlock *BB,
+ BasicBlock *BBCopy) {
+
+ BasicBlock *BBIDom = DT.getNode(BB)->getIDom()->getBlock();
+ BasicBlock *BBCopyIDom = EndBlockMap.lookup(BBIDom);
+
+ if (BBCopyIDom)
+ DT.changeImmediateDominator(BBCopy, BBCopyIDom);
+
+ return StartBlockMap.lookup(BBIDom);
+}
+
+// This is to determine whether an llvm::Value (defined in @p BB) is usable when
+// leaving a subregion. The straight-forward DT.dominates(BB, R->getExitBlock())
+// does not work in cases where the exit block has edges from outside the
+// region. In that case the llvm::Value would never be usable in in the exit
+// block. The RegionGenerator however creates an new exit block ('ExitBBCopy')
+// for the subregion's exiting edges only. We need to determine whether an
+// llvm::Value is usable in there. We do this by checking whether it dominates
+// all exiting blocks individually.
+static bool isDominatingSubregionExit(const DominatorTree &DT, Region *R,
+ BasicBlock *BB) {
+ for (auto ExitingBB : predecessors(R->getExit())) {
+ // Check for non-subregion incoming edges.
+ if (!R->contains(ExitingBB))
+ continue;
+
+ if (!DT.dominates(BB, ExitingBB))
+ return false;
+ }
+
+ return true;
+}
+
+// Find the direct dominator of the subregion's exit block if the subregion was
+// simplified.
+static BasicBlock *findExitDominator(DominatorTree &DT, Region *R) {
+ BasicBlock *Common = nullptr;
+ for (auto ExitingBB : predecessors(R->getExit())) {
+ // Check for non-subregion incoming edges.
+ if (!R->contains(ExitingBB))
+ continue;
+
+ // First exiting edge.
+ if (!Common) {
+ Common = ExitingBB;
+ continue;
+ }
+
+ Common = DT.findNearestCommonDominator(Common, ExitingBB);
+ }
+
+ assert(Common && R->contains(Common));
+ return Common;
+}
+
+void RegionGenerator::copyStmt(ScopStmt &Stmt, LoopToScevMapT &LTS,
+ isl_id_to_ast_expr *IdToAstExp) {
+ assert(Stmt.isRegionStmt() &&
+ "Only region statements can be copied by the region generator");
+
+ // Forget all old mappings.
+ StartBlockMap.clear();
+ EndBlockMap.clear();
+ RegionMaps.clear();
+ IncompletePHINodeMap.clear();
+
+ // Collection of all values related to this subregion.
+ ValueMapT ValueMap;
+
+ // The region represented by the statement.
+ Region *R = Stmt.getRegion();
+
+ // Create a dedicated entry for the region where we can reload all demoted
+ // inputs.
+ BasicBlock *EntryBB = R->getEntry();
+ BasicBlock *EntryBBCopy = SplitBlock(Builder.GetInsertBlock(),
+ &*Builder.GetInsertPoint(), &DT, &LI);
+ EntryBBCopy->setName("polly.stmt." + EntryBB->getName() + ".entry");
+ Builder.SetInsertPoint(&EntryBBCopy->front());
+
+ ValueMapT &EntryBBMap = RegionMaps[EntryBBCopy];
+ generateScalarLoads(Stmt, LTS, EntryBBMap, IdToAstExp);
+ generateBeginStmtTrace(Stmt, LTS, EntryBBMap);
+
+ for (auto PI = pred_begin(EntryBB), PE = pred_end(EntryBB); PI != PE; ++PI)
+ if (!R->contains(*PI)) {
+ StartBlockMap[*PI] = EntryBBCopy;
+ EndBlockMap[*PI] = EntryBBCopy;
+ }
+
+ // Iterate over all blocks in the region in a breadth-first search.
+ std::deque<BasicBlock *> Blocks;
+ SmallSetVector<BasicBlock *, 8> SeenBlocks;
+ Blocks.push_back(EntryBB);
+ SeenBlocks.insert(EntryBB);
+
+ while (!Blocks.empty()) {
+ BasicBlock *BB = Blocks.front();
+ Blocks.pop_front();
+
+ // First split the block and update dominance information.
+ BasicBlock *BBCopy = splitBB(BB);
+ BasicBlock *BBCopyIDom = repairDominance(BB, BBCopy);
+
+ // Get the mapping for this block and initialize it with either the scalar
+ // loads from the generated entering block (which dominates all blocks of
+ // this subregion) or the maps of the immediate dominator, if part of the
+ // subregion. The latter necessarily includes the former.
+ ValueMapT *InitBBMap;
+ if (BBCopyIDom) {
+ assert(RegionMaps.count(BBCopyIDom));
+ InitBBMap = &RegionMaps[BBCopyIDom];
+ } else
+ InitBBMap = &EntryBBMap;
+ auto Inserted = RegionMaps.insert(std::make_pair(BBCopy, *InitBBMap));
+ ValueMapT &RegionMap = Inserted.first->second;
+
+ // Copy the block with the BlockGenerator.
+ Builder.SetInsertPoint(&BBCopy->front());
+ copyBB(Stmt, BB, BBCopy, RegionMap, LTS, IdToAstExp);
+
+ // In order to remap PHI nodes we store also basic block mappings.
+ StartBlockMap[BB] = BBCopy;
+ EndBlockMap[BB] = Builder.GetInsertBlock();
+
+ // Add values to incomplete PHI nodes waiting for this block to be copied.
+ for (const PHINodePairTy &PHINodePair : IncompletePHINodeMap[BB])
+ addOperandToPHI(Stmt, PHINodePair.first, PHINodePair.second, BB, LTS);
+ IncompletePHINodeMap[BB].clear();
+
+ // And continue with new successors inside the region.
+ for (auto SI = succ_begin(BB), SE = succ_end(BB); SI != SE; SI++)
+ if (R->contains(*SI) && SeenBlocks.insert(*SI))
+ Blocks.push_back(*SI);
+
+ // Remember value in case it is visible after this subregion.
+ if (isDominatingSubregionExit(DT, R, BB))
+ ValueMap.insert(RegionMap.begin(), RegionMap.end());
+ }
+
+ // Now create a new dedicated region exit block and add it to the region map.
+ BasicBlock *ExitBBCopy = SplitBlock(Builder.GetInsertBlock(),
+ &*Builder.GetInsertPoint(), &DT, &LI);
+ ExitBBCopy->setName("polly.stmt." + R->getExit()->getName() + ".exit");
+ StartBlockMap[R->getExit()] = ExitBBCopy;
+ EndBlockMap[R->getExit()] = ExitBBCopy;
+
+ BasicBlock *ExitDomBBCopy = EndBlockMap.lookup(findExitDominator(DT, R));
+ assert(ExitDomBBCopy &&
+ "Common exit dominator must be within region; at least the entry node "
+ "must match");
+ DT.changeImmediateDominator(ExitBBCopy, ExitDomBBCopy);
+
+ // As the block generator doesn't handle control flow we need to add the
+ // region control flow by hand after all blocks have been copied.
+ for (BasicBlock *BB : SeenBlocks) {
+
+ BasicBlock *BBCopyStart = StartBlockMap[BB];
+ BasicBlock *BBCopyEnd = EndBlockMap[BB];
+ Instruction *TI = BB->getTerminator();
+ if (isa<UnreachableInst>(TI)) {
+ while (!BBCopyEnd->empty())
+ BBCopyEnd->begin()->eraseFromParent();
+ new UnreachableInst(BBCopyEnd->getContext(), BBCopyEnd);
+ continue;
+ }
+
+ Instruction *BICopy = BBCopyEnd->getTerminator();
+
+ ValueMapT &RegionMap = RegionMaps[BBCopyStart];
+ RegionMap.insert(StartBlockMap.begin(), StartBlockMap.end());
+
+ Builder.SetInsertPoint(BICopy);
+ copyInstScalar(Stmt, TI, RegionMap, LTS);
+ BICopy->eraseFromParent();
+ }
+
+ // Add counting PHI nodes to all loops in the region that can be used as
+ // replacement for SCEVs referring to the old loop.
+ for (BasicBlock *BB : SeenBlocks) {
+ Loop *L = LI.getLoopFor(BB);
+ if (L == nullptr || L->getHeader() != BB || !R->contains(L))
+ continue;
+
+ BasicBlock *BBCopy = StartBlockMap[BB];
+ Value *NullVal = Builder.getInt32(0);
+ PHINode *LoopPHI =
+ PHINode::Create(Builder.getInt32Ty(), 2, "polly.subregion.iv");
+ Instruction *LoopPHIInc = BinaryOperator::CreateAdd(
+ LoopPHI, Builder.getInt32(1), "polly.subregion.iv.inc");
+ LoopPHI->insertBefore(&BBCopy->front());
+ LoopPHIInc->insertBefore(BBCopy->getTerminator());
+
+ for (auto *PredBB : make_range(pred_begin(BB), pred_end(BB))) {
+ if (!R->contains(PredBB))
+ continue;
+ if (L->contains(PredBB))
+ LoopPHI->addIncoming(LoopPHIInc, EndBlockMap[PredBB]);
+ else
+ LoopPHI->addIncoming(NullVal, EndBlockMap[PredBB]);
+ }
+
+ for (auto *PredBBCopy : make_range(pred_begin(BBCopy), pred_end(BBCopy)))
+ if (LoopPHI->getBasicBlockIndex(PredBBCopy) < 0)
+ LoopPHI->addIncoming(NullVal, PredBBCopy);
+
+ LTS[L] = SE.getUnknown(LoopPHI);
+ }
+
+ // Continue generating code in the exit block.
+ Builder.SetInsertPoint(&*ExitBBCopy->getFirstInsertionPt());
+
+ // Write values visible to other statements.
+ generateScalarStores(Stmt, LTS, ValueMap, IdToAstExp);
+ StartBlockMap.clear();
+ EndBlockMap.clear();
+ RegionMaps.clear();
+ IncompletePHINodeMap.clear();
+}
+
+PHINode *RegionGenerator::buildExitPHI(MemoryAccess *MA, LoopToScevMapT &LTS,
+ ValueMapT &BBMap, Loop *L) {
+ ScopStmt *Stmt = MA->getStatement();
+ Region *SubR = Stmt->getRegion();
+ auto Incoming = MA->getIncoming();
+
+ PollyIRBuilder::InsertPointGuard IPGuard(Builder);
+ PHINode *OrigPHI = cast<PHINode>(MA->getAccessInstruction());
+ BasicBlock *NewSubregionExit = Builder.GetInsertBlock();
+
+ // This can happen if the subregion is simplified after the ScopStmts
+ // have been created; simplification happens as part of CodeGeneration.
+ if (OrigPHI->getParent() != SubR->getExit()) {
+ BasicBlock *FormerExit = SubR->getExitingBlock();
+ if (FormerExit)
+ NewSubregionExit = StartBlockMap.lookup(FormerExit);
+ }
+
+ PHINode *NewPHI = PHINode::Create(OrigPHI->getType(), Incoming.size(),
+ "polly." + OrigPHI->getName(),
+ NewSubregionExit->getFirstNonPHI());
+
+ // Add the incoming values to the PHI.
+ for (auto &Pair : Incoming) {
+ BasicBlock *OrigIncomingBlock = Pair.first;
+ BasicBlock *NewIncomingBlockStart = StartBlockMap.lookup(OrigIncomingBlock);
+ BasicBlock *NewIncomingBlockEnd = EndBlockMap.lookup(OrigIncomingBlock);
+ Builder.SetInsertPoint(NewIncomingBlockEnd->getTerminator());
+ assert(RegionMaps.count(NewIncomingBlockStart));
+ assert(RegionMaps.count(NewIncomingBlockEnd));
+ ValueMapT *LocalBBMap = &RegionMaps[NewIncomingBlockStart];
+
+ Value *OrigIncomingValue = Pair.second;
+ Value *NewIncomingValue =
+ getNewValue(*Stmt, OrigIncomingValue, *LocalBBMap, LTS, L);
+ NewPHI->addIncoming(NewIncomingValue, NewIncomingBlockEnd);
+ }
+
+ return NewPHI;
+}
+
+Value *RegionGenerator::getExitScalar(MemoryAccess *MA, LoopToScevMapT &LTS,
+ ValueMapT &BBMap) {
+ ScopStmt *Stmt = MA->getStatement();
+
+ // TODO: Add some test cases that ensure this is really the right choice.
+ Loop *L = LI.getLoopFor(Stmt->getRegion()->getExit());
+
+ if (MA->isAnyPHIKind()) {
+ auto Incoming = MA->getIncoming();
+ assert(!Incoming.empty() &&
+ "PHI WRITEs must have originate from at least one incoming block");
+
+ // If there is only one incoming value, we do not need to create a PHI.
+ if (Incoming.size() == 1) {
+ Value *OldVal = Incoming[0].second;
+ return getNewValue(*Stmt, OldVal, BBMap, LTS, L);
+ }
+
+ return buildExitPHI(MA, LTS, BBMap, L);
+ }
+
+ // MemoryKind::Value accesses leaving the subregion must dominate the exit
+ // block; just pass the copied value.
+ Value *OldVal = MA->getAccessValue();
+ return getNewValue(*Stmt, OldVal, BBMap, LTS, L);
+}
+
+void RegionGenerator::generateScalarStores(
+ ScopStmt &Stmt, LoopToScevMapT &LTS, ValueMapT &BBMap,
+ __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ assert(Stmt.getRegion() &&
+ "Block statements need to use the generateScalarStores() "
+ "function in the BlockGenerator");
+
+ // Get the exit scalar values before generating the writes.
+ // This is necessary because RegionGenerator::getExitScalar may insert
+ // PHINodes that depend on the region's exiting blocks. But
+ // BlockGenerator::generateConditionalExecution may insert a new basic block
+ // such that the current basic block is not a direct successor of the exiting
+ // blocks anymore. Hence, build the PHINodes while the current block is still
+ // the direct successor.
+ SmallDenseMap<MemoryAccess *, Value *> NewExitScalars;
+ for (MemoryAccess *MA : Stmt) {
+ if (MA->isOriginalArrayKind() || MA->isRead())
+ continue;
+
+ Value *NewVal = getExitScalar(MA, LTS, BBMap);
+ NewExitScalars[MA] = NewVal;
+ }
+
+ for (MemoryAccess *MA : Stmt) {
+ if (MA->isOriginalArrayKind() || MA->isRead())
+ continue;
+
+ isl::set AccDom = MA->getAccessRelation().domain();
+ std::string Subject = MA->getId().get_name();
+ generateConditionalExecution(
+ Stmt, AccDom, Subject.c_str(), [&, this, MA]() {
+ Value *NewVal = NewExitScalars.lookup(MA);
+ assert(NewVal && "The exit scalar must be determined before");
+ Value *Address = getImplicitAddress(*MA, getLoopForStmt(Stmt), LTS,
+ BBMap, NewAccesses);
+ assert((!isa<Instruction>(NewVal) ||
+ DT.dominates(cast<Instruction>(NewVal)->getParent(),
+ Builder.GetInsertBlock())) &&
+ "Domination violation");
+ assert((!isa<Instruction>(Address) ||
+ DT.dominates(cast<Instruction>(Address)->getParent(),
+ Builder.GetInsertBlock())) &&
+ "Domination violation");
+ Builder.CreateStore(NewVal, Address);
+ });
+ }
+}
+
+void RegionGenerator::addOperandToPHI(ScopStmt &Stmt, PHINode *PHI,
+ PHINode *PHICopy, BasicBlock *IncomingBB,
+ LoopToScevMapT &LTS) {
+ // If the incoming block was not yet copied mark this PHI as incomplete.
+ // Once the block will be copied the incoming value will be added.
+ BasicBlock *BBCopyStart = StartBlockMap[IncomingBB];
+ BasicBlock *BBCopyEnd = EndBlockMap[IncomingBB];
+ if (!BBCopyStart) {
+ assert(!BBCopyEnd);
+ assert(Stmt.represents(IncomingBB) &&
+ "Bad incoming block for PHI in non-affine region");
+ IncompletePHINodeMap[IncomingBB].push_back(std::make_pair(PHI, PHICopy));
+ return;
+ }
+
+ assert(RegionMaps.count(BBCopyStart) &&
+ "Incoming PHI block did not have a BBMap");
+ ValueMapT &BBCopyMap = RegionMaps[BBCopyStart];
+
+ Value *OpCopy = nullptr;
+
+ if (Stmt.represents(IncomingBB)) {
+ Value *Op = PHI->getIncomingValueForBlock(IncomingBB);
+
+ // If the current insert block is different from the PHIs incoming block
+ // change it, otherwise do not.
+ auto IP = Builder.GetInsertPoint();
+ if (IP->getParent() != BBCopyEnd)
+ Builder.SetInsertPoint(BBCopyEnd->getTerminator());
+ OpCopy = getNewValue(Stmt, Op, BBCopyMap, LTS, getLoopForStmt(Stmt));
+ if (IP->getParent() != BBCopyEnd)
+ Builder.SetInsertPoint(&*IP);
+ } else {
+ // All edges from outside the non-affine region become a single edge
+ // in the new copy of the non-affine region. Make sure to only add the
+ // corresponding edge the first time we encounter a basic block from
+ // outside the non-affine region.
+ if (PHICopy->getBasicBlockIndex(BBCopyEnd) >= 0)
+ return;
+
+ // Get the reloaded value.
+ OpCopy = getNewValue(Stmt, PHI, BBCopyMap, LTS, getLoopForStmt(Stmt));
+ }
+
+ assert(OpCopy && "Incoming PHI value was not copied properly");
+ PHICopy->addIncoming(OpCopy, BBCopyEnd);
+}
+
+void RegionGenerator::copyPHIInstruction(ScopStmt &Stmt, PHINode *PHI,
+ ValueMapT &BBMap,
+ LoopToScevMapT &LTS) {
+ unsigned NumIncoming = PHI->getNumIncomingValues();
+ PHINode *PHICopy =
+ Builder.CreatePHI(PHI->getType(), NumIncoming, "polly." + PHI->getName());
+ PHICopy->moveBefore(PHICopy->getParent()->getFirstNonPHI());
+ BBMap[PHI] = PHICopy;
+
+ for (BasicBlock *IncomingBB : PHI->blocks())
+ addOperandToPHI(Stmt, PHI, PHICopy, IncomingBB, LTS);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/CodeGeneration.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/CodeGeneration.cpp
new file mode 100644
index 00000000000..31143eae752
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/CodeGeneration.cpp
@@ -0,0 +1,386 @@
+//===- CodeGeneration.cpp - Code generate the Scops using ISL. ---------======//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The CodeGeneration pass takes a Scop created by ScopInfo and translates it
+// back to LLVM-IR using the ISL code generator.
+//
+// The Scop describes the high level memory behavior of a control flow region.
+// Transformation passes can update the schedule (execution order) of statements
+// in the Scop. ISL is used to generate an abstract syntax tree that reflects
+// the updated execution order. This clast is used to create new LLVM-IR that is
+// computationally equivalent to the original control flow region, but executes
+// its code in the new execution order defined by the changed schedule.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/CodeGeneration.h"
+#include "polly/CodeGen/IRBuilder.h"
+#include "polly/CodeGen/IslAst.h"
+#include "polly/CodeGen/IslNodeBuilder.h"
+#include "polly/CodeGen/PerfMonitor.h"
+#include "polly/CodeGen/Utils.h"
+#include "polly/DependenceInfo.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include "isl/ast.h"
+#include <cassert>
+
+using namespace llvm;
+using namespace polly;
+
+#define DEBUG_TYPE "polly-codegen"
+
+static cl::opt<bool> Verify("polly-codegen-verify",
+ cl::desc("Verify the function generated by Polly"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+bool polly::PerfMonitoring;
+
+static cl::opt<bool, true>
+ XPerfMonitoring("polly-codegen-perf-monitoring",
+ cl::desc("Add run-time performance monitoring"), cl::Hidden,
+ cl::location(polly::PerfMonitoring), cl::init(false),
+ cl::ZeroOrMore, cl::cat(PollyCategory));
+
+STATISTIC(ScopsProcessed, "Number of SCoP processed");
+STATISTIC(CodegenedScops, "Number of successfully generated SCoPs");
+STATISTIC(CodegenedAffineLoops,
+ "Number of original affine loops in SCoPs that have been generated");
+STATISTIC(CodegenedBoxedLoops,
+ "Number of original boxed loops in SCoPs that have been generated");
+
+namespace polly {
+
+/// Mark a basic block unreachable.
+///
+/// Marks the basic block @p Block unreachable by equipping it with an
+/// UnreachableInst.
+void markBlockUnreachable(BasicBlock &Block, PollyIRBuilder &Builder) {
+ auto *OrigTerminator = Block.getTerminator();
+ Builder.SetInsertPoint(OrigTerminator);
+ Builder.CreateUnreachable();
+ OrigTerminator->eraseFromParent();
+}
+} // namespace polly
+
+static void verifyGeneratedFunction(Scop &S, Function &F, IslAstInfo &AI) {
+ if (!Verify || !verifyFunction(F, &errs()))
+ return;
+
+ LLVM_DEBUG({
+ errs() << "== ISL Codegen created an invalid function ==\n\n== The "
+ "SCoP ==\n";
+ errs() << S;
+ errs() << "\n== The isl AST ==\n";
+ AI.print(errs());
+ errs() << "\n== The invalid function ==\n";
+ F.print(errs());
+ });
+
+ llvm_unreachable("Polly generated function could not be verified. Add "
+ "-polly-codegen-verify=false to disable this assertion.");
+}
+
+// CodeGeneration adds a lot of BBs without updating the RegionInfo
+// We make all created BBs belong to the scop's parent region without any
+// nested structure to keep the RegionInfo verifier happy.
+static void fixRegionInfo(Function &F, Region &ParentRegion, RegionInfo &RI) {
+ for (BasicBlock &BB : F) {
+ if (RI.getRegionFor(&BB))
+ continue;
+
+ RI.setRegionFor(&BB, &ParentRegion);
+ }
+}
+
+/// Remove all lifetime markers (llvm.lifetime.start, llvm.lifetime.end) from
+/// @R.
+///
+/// CodeGeneration does not copy lifetime markers into the optimized SCoP,
+/// which would leave the them only in the original path. This can transform
+/// code such as
+///
+/// llvm.lifetime.start(%p)
+/// llvm.lifetime.end(%p)
+///
+/// into
+///
+/// if (RTC) {
+/// // generated code
+/// } else {
+/// // original code
+/// llvm.lifetime.start(%p)
+/// }
+/// llvm.lifetime.end(%p)
+///
+/// The current StackColoring algorithm cannot handle if some, but not all,
+/// paths from the end marker to the entry block cross the start marker. Same
+/// for start markers that do not always cross the end markers. We avoid any
+/// issues by removing all lifetime markers, even from the original code.
+///
+/// A better solution could be to hoist all llvm.lifetime.start to the split
+/// node and all llvm.lifetime.end to the merge node, which should be
+/// conservatively correct.
+static void removeLifetimeMarkers(Region *R) {
+ for (auto *BB : R->blocks()) {
+ auto InstIt = BB->begin();
+ auto InstEnd = BB->end();
+
+ while (InstIt != InstEnd) {
+ auto NextIt = InstIt;
+ ++NextIt;
+
+ if (auto *IT = dyn_cast<IntrinsicInst>(&*InstIt)) {
+ switch (IT->getIntrinsicID()) {
+ case Intrinsic::lifetime_start:
+ case Intrinsic::lifetime_end:
+ BB->getInstList().erase(InstIt);
+ break;
+ default:
+ break;
+ }
+ }
+
+ InstIt = NextIt;
+ }
+ }
+}
+
+static bool generateCode(Scop &S, IslAstInfo &AI, LoopInfo &LI,
+ DominatorTree &DT, ScalarEvolution &SE,
+ RegionInfo &RI) {
+ // Check whether IslAstInfo uses the same isl_ctx. Since -polly-codegen
+ // reports itself to preserve DependenceInfo and IslAstInfo, we might get
+ // those analysis that were computed by a different ScopInfo for a different
+ // Scop structure. When the ScopInfo/Scop object is freed, there is a high
+ // probability that the new ScopInfo/Scop object will be created at the same
+ // heap position with the same address. Comparing whether the Scop or ScopInfo
+ // address is the expected therefore is unreliable.
+ // Instead, we compare the address of the isl_ctx object. Both, DependenceInfo
+ // and IslAstInfo must hold a reference to the isl_ctx object to ensure it is
+ // not freed before the destruction of those analyses which might happen after
+ // the destruction of the Scop/ScopInfo they refer to. Hence, the isl_ctx
+ // will not be freed and its space not reused as long there is a
+ // DependenceInfo or IslAstInfo around.
+ IslAst &Ast = AI.getIslAst();
+ if (Ast.getSharedIslCtx() != S.getSharedIslCtx()) {
+ LLVM_DEBUG(dbgs() << "Got an IstAst for a different Scop/isl_ctx\n");
+ return false;
+ }
+
+ // Check if we created an isl_ast root node, otherwise exit.
+ isl::ast_node AstRoot = Ast.getAst();
+ if (AstRoot.is_null())
+ return false;
+
+ // Collect statistics. Do it before we modify the IR to avoid having it any
+ // influence on the result.
+ auto ScopStats = S.getStatistics();
+ ScopsProcessed++;
+
+ auto &DL = S.getFunction().getParent()->getDataLayout();
+ Region *R = &S.getRegion();
+ assert(!R->isTopLevelRegion() && "Top level regions are not supported");
+
+ ScopAnnotator Annotator;
+
+ simplifyRegion(R, &DT, &LI, &RI);
+ assert(R->isSimple());
+ BasicBlock *EnteringBB = S.getEnteringBlock();
+ assert(EnteringBB);
+ PollyIRBuilder Builder(EnteringBB->getContext(), ConstantFolder(),
+ IRInserter(Annotator));
+ Builder.SetInsertPoint(EnteringBB->getTerminator());
+
+ // Only build the run-time condition and parameters _after_ having
+ // introduced the conditional branch. This is important as the conditional
+ // branch will guard the original scop from new induction variables that
+ // the SCEVExpander may introduce while code generating the parameters and
+ // which may introduce scalar dependences that prevent us from correctly
+ // code generating this scop.
+ BBPair StartExitBlocks =
+ std::get<0>(executeScopConditionally(S, Builder.getTrue(), DT, RI, LI));
+ BasicBlock *StartBlock = std::get<0>(StartExitBlocks);
+ BasicBlock *ExitBlock = std::get<1>(StartExitBlocks);
+
+ removeLifetimeMarkers(R);
+ auto *SplitBlock = StartBlock->getSinglePredecessor();
+
+ IslNodeBuilder NodeBuilder(Builder, Annotator, DL, LI, SE, DT, S, StartBlock);
+
+ // All arrays must have their base pointers known before
+ // ScopAnnotator::buildAliasScopes.
+ NodeBuilder.allocateNewArrays(StartExitBlocks);
+ Annotator.buildAliasScopes(S);
+
+ if (PerfMonitoring) {
+ PerfMonitor P(S, EnteringBB->getParent()->getParent());
+ P.initialize();
+ P.insertRegionStart(SplitBlock->getTerminator());
+
+ BasicBlock *MergeBlock = ExitBlock->getUniqueSuccessor();
+ P.insertRegionEnd(MergeBlock->getTerminator());
+ }
+
+ // First generate code for the hoisted invariant loads and transitively the
+ // parameters they reference. Afterwards, for the remaining parameters that
+ // might reference the hoisted loads. Finally, build the runtime check
+ // that might reference both hoisted loads as well as parameters.
+ // If the hoisting fails we have to bail and execute the original code.
+ Builder.SetInsertPoint(SplitBlock->getTerminator());
+ if (!NodeBuilder.preloadInvariantLoads()) {
+ // Patch the introduced branch condition to ensure that we always execute
+ // the original SCoP.
+ auto *FalseI1 = Builder.getFalse();
+ auto *SplitBBTerm = Builder.GetInsertBlock()->getTerminator();
+ SplitBBTerm->setOperand(0, FalseI1);
+
+ // Since the other branch is hence ignored we mark it as unreachable and
+ // adjust the dominator tree accordingly.
+ auto *ExitingBlock = StartBlock->getUniqueSuccessor();
+ assert(ExitingBlock);
+ auto *MergeBlock = ExitingBlock->getUniqueSuccessor();
+ assert(MergeBlock);
+ markBlockUnreachable(*StartBlock, Builder);
+ markBlockUnreachable(*ExitingBlock, Builder);
+ auto *ExitingBB = S.getExitingBlock();
+ assert(ExitingBB);
+ DT.changeImmediateDominator(MergeBlock, ExitingBB);
+ DT.eraseNode(ExitingBlock);
+ } else {
+ NodeBuilder.addParameters(S.getContext().release());
+ Value *RTC = NodeBuilder.createRTC(AI.getRunCondition().release());
+
+ Builder.GetInsertBlock()->getTerminator()->setOperand(0, RTC);
+
+ // Explicitly set the insert point to the end of the block to avoid that a
+ // split at the builder's current
+ // insert position would move the malloc calls to the wrong BasicBlock.
+ // Ideally we would just split the block during allocation of the new
+ // arrays, but this would break the assumption that there are no blocks
+ // between polly.start and polly.exiting (at this point).
+ Builder.SetInsertPoint(StartBlock->getTerminator());
+
+ NodeBuilder.create(AstRoot.release());
+ NodeBuilder.finalize();
+ fixRegionInfo(*EnteringBB->getParent(), *R->getParent(), RI);
+
+ CodegenedScops++;
+ CodegenedAffineLoops += ScopStats.NumAffineLoops;
+ CodegenedBoxedLoops += ScopStats.NumBoxedLoops;
+ }
+
+ Function *F = EnteringBB->getParent();
+ verifyGeneratedFunction(S, *F, AI);
+ for (auto *SubF : NodeBuilder.getParallelSubfunctions())
+ verifyGeneratedFunction(S, *SubF, AI);
+
+ // Mark the function such that we run additional cleanup passes on this
+ // function (e.g. mem2reg to rediscover phi nodes).
+ F->addFnAttr("polly-optimized");
+ return true;
+}
+
+namespace {
+
+class CodeGeneration : public ScopPass {
+public:
+ static char ID;
+
+ /// The data layout used.
+ const DataLayout *DL;
+
+ /// @name The analysis passes we need to generate code.
+ ///
+ ///{
+ LoopInfo *LI;
+ IslAstInfo *AI;
+ DominatorTree *DT;
+ ScalarEvolution *SE;
+ RegionInfo *RI;
+ ///}
+
+ CodeGeneration() : ScopPass(ID) {}
+
+ /// Generate LLVM-IR for the SCoP @p S.
+ bool runOnScop(Scop &S) override {
+ // Skip SCoPs in case they're already code-generated by PPCGCodeGeneration.
+ if (S.isToBeSkipped())
+ return false;
+
+ AI = &getAnalysis<IslAstInfoWrapperPass>().getAI();
+ LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+ DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
+ SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
+ DL = &S.getFunction().getParent()->getDataLayout();
+ RI = &getAnalysis<RegionInfoPass>().getRegionInfo();
+ return generateCode(S, *AI, *LI, *DT, *SE, *RI);
+ }
+
+ /// Register all analyses and transformation required.
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ ScopPass::getAnalysisUsage(AU);
+
+ AU.addRequired<DominatorTreeWrapperPass>();
+ AU.addRequired<IslAstInfoWrapperPass>();
+ AU.addRequired<RegionInfoPass>();
+ AU.addRequired<ScalarEvolutionWrapperPass>();
+ AU.addRequired<ScopDetectionWrapperPass>();
+ AU.addRequired<ScopInfoRegionPass>();
+ AU.addRequired<LoopInfoWrapperPass>();
+
+ AU.addPreserved<DependenceInfo>();
+ AU.addPreserved<IslAstInfoWrapperPass>();
+
+ // FIXME: We do not yet add regions for the newly generated code to the
+ // region tree.
+ }
+};
+} // namespace
+
+PreservedAnalyses CodeGenerationPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &AR,
+ SPMUpdater &U) {
+ auto &AI = SAM.getResult<IslAstAnalysis>(S, AR);
+ if (generateCode(S, AI, AR.LI, AR.DT, AR.SE, AR.RI)) {
+ U.invalidateScop(S);
+ return PreservedAnalyses::none();
+ }
+
+ return PreservedAnalyses::all();
+}
+
+char CodeGeneration::ID = 1;
+
+Pass *polly::createCodeGenerationPass() { return new CodeGeneration(); }
+
+INITIALIZE_PASS_BEGIN(CodeGeneration, "polly-codegen",
+ "Polly - Create LLVM-IR from SCoPs", false, false);
+INITIALIZE_PASS_DEPENDENCY(DependenceInfo);
+INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(RegionInfoPass);
+INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(ScopDetectionWrapperPass);
+INITIALIZE_PASS_END(CodeGeneration, "polly-codegen",
+ "Polly - Create LLVM-IR from SCoPs", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/CodegenCleanup.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/CodegenCleanup.cpp
new file mode 100644
index 00000000000..11fef4af49a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/CodegenCleanup.cpp
@@ -0,0 +1,139 @@
+//===- CodegenCleanup.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/CodegenCleanup.h"
+
+#include "llvm/Analysis/ScopedNoAliasAA.h"
+#include "llvm/Analysis/TypeBasedAliasAnalysis.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Transforms/InstCombine/InstCombine.h"
+#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/Scalar/GVN.h"
+#include "llvm/Transforms/Utils.h"
+
+#define DEBUG_TYPE "polly-cleanup"
+
+using namespace llvm;
+using namespace polly;
+
+namespace {
+
+class CodegenCleanup : public FunctionPass {
+private:
+ CodegenCleanup(const CodegenCleanup &) = delete;
+ const CodegenCleanup &operator=(const CodegenCleanup &) = delete;
+
+ llvm::legacy::FunctionPassManager *FPM;
+
+public:
+ static char ID;
+ explicit CodegenCleanup() : FunctionPass(ID), FPM(nullptr) {}
+
+ /// @name FunctionPass interface
+ //@{
+ virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {}
+
+ virtual bool doInitialization(Module &M) override {
+ assert(!FPM);
+
+ FPM = new llvm::legacy::FunctionPassManager(&M);
+
+ // TODO: How to make parent passes discoverable?
+ // TODO: Should be sensitive to compiler options in PassManagerBuilder, to
+ // which we do not have access here.
+ FPM->add(createScopedNoAliasAAWrapperPass());
+ FPM->add(createTypeBasedAAWrapperPass());
+ FPM->add(createAAResultsWrapperPass());
+
+ // TODO: These are non-conditional passes that run between
+ // EP_ModuleOptimizerEarly and EP_VectorizerStart just to ensure we do not
+ // miss any optimization that would have run after Polly with
+ // -polly-position=early. This can probably be reduced to a more compact set
+ // of passes.
+ FPM->add(createCFGSimplificationPass());
+ FPM->add(createSROAPass());
+ FPM->add(createEarlyCSEPass());
+
+ FPM->add(createPromoteMemoryToRegisterPass());
+ FPM->add(createInstructionCombiningPass(true));
+ FPM->add(createCFGSimplificationPass());
+ FPM->add(createSROAPass());
+ FPM->add(createEarlyCSEPass(true));
+ FPM->add(createSpeculativeExecutionIfHasBranchDivergencePass());
+ FPM->add(createJumpThreadingPass());
+ FPM->add(createCorrelatedValuePropagationPass());
+ FPM->add(createCFGSimplificationPass());
+ FPM->add(createInstructionCombiningPass(true));
+ FPM->add(createLibCallsShrinkWrapPass());
+ FPM->add(createTailCallEliminationPass());
+ FPM->add(createCFGSimplificationPass());
+ FPM->add(createReassociatePass());
+ FPM->add(createLoopRotatePass(-1));
+ FPM->add(createGVNPass());
+ FPM->add(createLICMPass());
+ FPM->add(createLoopUnswitchPass());
+ FPM->add(createCFGSimplificationPass());
+ FPM->add(createInstructionCombiningPass(true));
+ FPM->add(createIndVarSimplifyPass());
+ FPM->add(createLoopIdiomPass());
+ FPM->add(createLoopDeletionPass());
+ FPM->add(createCFGSimplificationPass());
+ FPM->add(createSimpleLoopUnrollPass(3));
+ FPM->add(createMergedLoadStoreMotionPass());
+ FPM->add(createGVNPass());
+ FPM->add(createMemCpyOptPass());
+ FPM->add(createSCCPPass());
+ FPM->add(createBitTrackingDCEPass());
+ FPM->add(createInstructionCombiningPass(true));
+ FPM->add(createJumpThreadingPass());
+ FPM->add(createCorrelatedValuePropagationPass());
+ FPM->add(createDeadStoreEliminationPass());
+ FPM->add(createLICMPass());
+ FPM->add(createAggressiveDCEPass());
+ FPM->add(createCFGSimplificationPass());
+ FPM->add(createInstructionCombiningPass(true));
+ FPM->add(createFloat2IntPass());
+
+ return FPM->doInitialization();
+ }
+
+ virtual bool doFinalization(Module &M) override {
+ bool Result = FPM->doFinalization();
+
+ delete FPM;
+ FPM = nullptr;
+
+ return Result;
+ }
+
+ virtual bool runOnFunction(llvm::Function &F) override {
+ if (!F.hasFnAttribute("polly-optimized")) {
+ LLVM_DEBUG(
+ dbgs() << F.getName()
+ << ": Skipping cleanup because Polly did not optimize it.");
+ return false;
+ }
+
+ LLVM_DEBUG(dbgs() << F.getName() << ": Running codegen cleanup...");
+ return FPM->run(F);
+ }
+ //@}
+};
+
+char CodegenCleanup::ID;
+} // namespace
+
+FunctionPass *polly::createCodegenCleanupPass() { return new CodegenCleanup(); }
+
+INITIALIZE_PASS_BEGIN(CodegenCleanup, "polly-cleanup",
+ "Polly - Cleanup after code generation", false, false)
+INITIALIZE_PASS_END(CodegenCleanup, "polly-cleanup",
+ "Polly - Cleanup after code generation", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/IRBuilder.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/IRBuilder.cpp
new file mode 100644
index 00000000000..2285c746912
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/IRBuilder.cpp
@@ -0,0 +1,270 @@
+//===------ PollyIRBuilder.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The Polly IRBuilder file contains Polly specific extensions for the IRBuilder
+// that are used e.g. to emit the llvm.loop.parallel metadata.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/IRBuilder.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/Metadata.h"
+
+using namespace llvm;
+using namespace polly;
+
+static const int MaxArraysInAliasScops = 10;
+
+/// Get a self referencing id metadata node.
+///
+/// The MDNode looks like this (if arg0/arg1 are not null):
+///
+/// '!n = distinct !{!n, arg0, arg1}'
+///
+/// @return The self referencing id metadata node.
+static MDNode *getID(LLVMContext &Ctx, Metadata *arg0 = nullptr,
+ Metadata *arg1 = nullptr) {
+ MDNode *ID;
+ SmallVector<Metadata *, 3> Args;
+ // Reserve operand 0 for loop id self reference.
+ Args.push_back(nullptr);
+
+ if (arg0)
+ Args.push_back(arg0);
+ if (arg1)
+ Args.push_back(arg1);
+
+ ID = MDNode::getDistinct(Ctx, Args);
+ ID->replaceOperandWith(0, ID);
+ return ID;
+}
+
+ScopAnnotator::ScopAnnotator() : SE(nullptr), AliasScopeDomain(nullptr) {
+ // Push an empty staging BandAttr.
+ LoopAttrEnv.emplace_back();
+}
+
+ScopAnnotator::~ScopAnnotator() {
+ assert(LoopAttrEnv.size() == 1 && "Loop stack imbalance");
+ assert(!getStagingAttrEnv() && "Forgot to clear staging attr env");
+}
+
+void ScopAnnotator::buildAliasScopes(Scop &S) {
+ SE = S.getSE();
+
+ LLVMContext &Ctx = SE->getContext();
+ AliasScopeDomain = getID(Ctx, MDString::get(Ctx, "polly.alias.scope.domain"));
+
+ AliasScopeMap.clear();
+ OtherAliasScopeListMap.clear();
+
+ // We are only interested in arrays, but no scalar references. Scalars should
+ // be handled easily by basicaa.
+ SmallVector<ScopArrayInfo *, 10> Arrays;
+ for (ScopArrayInfo *Array : S.arrays())
+ if (Array->isArrayKind())
+ Arrays.push_back(Array);
+
+ // The construction of alias scopes is quadratic in the number of arrays
+ // involved. In case of too many arrays, skip the construction of alias
+ // information to avoid quadratic increases in compile time and code size.
+ if (Arrays.size() > MaxArraysInAliasScops)
+ return;
+
+ std::string AliasScopeStr = "polly.alias.scope.";
+ for (const ScopArrayInfo *Array : Arrays) {
+ assert(Array->getBasePtr() && "Base pointer must be present");
+ AliasScopeMap[Array->getBasePtr()] =
+ getID(Ctx, AliasScopeDomain,
+ MDString::get(Ctx, (AliasScopeStr + Array->getName()).c_str()));
+ }
+
+ for (const ScopArrayInfo *Array : Arrays) {
+ MDNode *AliasScopeList = MDNode::get(Ctx, {});
+ for (const auto &AliasScopePair : AliasScopeMap) {
+ if (Array->getBasePtr() == AliasScopePair.first)
+ continue;
+
+ Metadata *Args = {AliasScopePair.second};
+ AliasScopeList =
+ MDNode::concatenate(AliasScopeList, MDNode::get(Ctx, Args));
+ }
+
+ OtherAliasScopeListMap[Array->getBasePtr()] = AliasScopeList;
+ }
+}
+
+void ScopAnnotator::pushLoop(Loop *L, bool IsParallel) {
+ ActiveLoops.push_back(L);
+
+ if (IsParallel) {
+ LLVMContext &Ctx = SE->getContext();
+ MDNode *AccessGroup = MDNode::getDistinct(Ctx, {});
+ ParallelLoops.push_back(AccessGroup);
+ }
+
+ // Open an empty BandAttr context for loops nested in this one.
+ LoopAttrEnv.emplace_back();
+}
+
+void ScopAnnotator::popLoop(bool IsParallel) {
+ ActiveLoops.pop_back();
+
+ if (IsParallel) {
+ assert(!ParallelLoops.empty() && "Expected a parallel loop to pop");
+ ParallelLoops.pop_back();
+ }
+
+ // Exit the subloop context.
+ assert(!getStagingAttrEnv() && "Forgot to clear staging attr env");
+ assert(LoopAttrEnv.size() >= 2 && "Popped too many");
+ LoopAttrEnv.pop_back();
+}
+
+void ScopAnnotator::annotateLoopLatch(BranchInst *B, Loop *L, bool IsParallel,
+ bool IsLoopVectorizerDisabled) const {
+ LLVMContext &Ctx = SE->getContext();
+ SmallVector<Metadata *, 3> Args;
+
+ // For the LoopID self-reference.
+ Args.push_back(nullptr);
+
+ // Add the user-defined loop properties to the annotation, if any. Any
+ // additional properties are appended.
+ // FIXME: What to do if these conflict?
+ MDNode *MData = nullptr;
+ if (BandAttr *AttrEnv = getActiveAttrEnv()) {
+ MData = AttrEnv->Metadata;
+ if (MData)
+ llvm::append_range(Args, drop_begin(MData->operands(), 1));
+ }
+
+ if (IsLoopVectorizerDisabled) {
+ MDString *PropName = MDString::get(Ctx, "llvm.loop.vectorize.enable");
+ ConstantInt *FalseValue = ConstantInt::get(Type::getInt1Ty(Ctx), 0);
+ ValueAsMetadata *PropValue = ValueAsMetadata::get(FalseValue);
+ Args.push_back(MDNode::get(Ctx, {PropName, PropValue}));
+ }
+
+ if (IsParallel) {
+ MDString *PropName = MDString::get(Ctx, "llvm.loop.parallel_accesses");
+ MDNode *AccGroup = ParallelLoops.back();
+ Args.push_back(MDNode::get(Ctx, {PropName, AccGroup}));
+ }
+
+ // No metadata to annotate.
+ if (!MData && Args.size() <= 1)
+ return;
+
+ // Reuse the MData node if possible, this will avoid having to create another
+ // one that cannot be merged because LoopIDs are 'distinct'. However, we have
+ // to create a new one if we add properties.
+ if (!MData || Args.size() > MData->getNumOperands()) {
+ MData = MDNode::getDistinct(Ctx, Args);
+ MData->replaceOperandWith(0, MData);
+ }
+ B->setMetadata(LLVMContext::MD_loop, MData);
+}
+
+/// Get the pointer operand
+///
+/// @param Inst The instruction to be analyzed.
+/// @return the pointer operand in case @p Inst is a memory access
+/// instruction and nullptr otherwise.
+static llvm::Value *getMemAccInstPointerOperand(Instruction *Inst) {
+ auto MemInst = MemAccInst::dyn_cast(Inst);
+ if (!MemInst)
+ return nullptr;
+
+ return MemInst.getPointerOperand();
+}
+
+/// Find the base pointer of an array access.
+///
+/// This should be equivalent to ScalarEvolution::getPointerBase, which we
+/// cannot use here the IR is still under construction which ScalarEvolution
+/// assumes to not be modified.
+static Value *findBasePtr(Value *Val) {
+ while (true) {
+ if (auto *Gep = dyn_cast<GEPOperator>(Val)) {
+ Val = Gep->getPointerOperand();
+ continue;
+ }
+ if (auto *Cast = dyn_cast<BitCastOperator>(Val)) {
+ Val = Cast->getOperand(0);
+ continue;
+ }
+
+ break;
+ }
+
+ return Val;
+}
+
+void ScopAnnotator::annotate(Instruction *Inst) {
+ if (!Inst->mayReadOrWriteMemory())
+ return;
+
+ switch (ParallelLoops.size()) {
+ case 0:
+ // Not parallel to anything: no access group needed.
+ break;
+ case 1:
+ // Single parallel loop: use directly.
+ Inst->setMetadata(LLVMContext::MD_access_group,
+ cast<MDNode>(ParallelLoops.front()));
+ break;
+ default:
+ // Parallel to multiple loops: refer to list of access groups.
+ Inst->setMetadata(LLVMContext::MD_access_group,
+ MDNode::get(SE->getContext(),
+ ArrayRef<Metadata *>(
+ (Metadata *const *)ParallelLoops.data(),
+ ParallelLoops.size())));
+ break;
+ }
+
+ // TODO: Use the ScopArrayInfo once available here.
+ if (!AliasScopeDomain)
+ return;
+
+ // Do not apply annotations on memory operations that take more than one
+ // pointer. It would be ambiguous to which pointer the annotation applies.
+ // FIXME: How can we specify annotations for all pointer arguments?
+ if (isa<CallInst>(Inst) && !isa<MemSetInst>(Inst))
+ return;
+
+ auto *Ptr = getMemAccInstPointerOperand(Inst);
+ if (!Ptr)
+ return;
+
+ Value *BasePtr = findBasePtr(Ptr);
+ if (!BasePtr)
+ return;
+
+ auto AliasScope = AliasScopeMap.lookup(BasePtr);
+
+ if (!AliasScope) {
+ BasePtr = AlternativeAliasBases.lookup(BasePtr);
+ if (!BasePtr)
+ return;
+
+ AliasScope = AliasScopeMap.lookup(BasePtr);
+ if (!AliasScope)
+ return;
+ }
+
+ assert(OtherAliasScopeListMap.count(BasePtr) &&
+ "BasePtr either expected in AliasScopeMap and OtherAlias...Map");
+ auto *OtherAliasScopeList = OtherAliasScopeListMap[BasePtr];
+
+ Inst->setMetadata("alias.scope", MDNode::get(SE->getContext(), AliasScope));
+ Inst->setMetadata("noalias", OtherAliasScopeList);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/IslAst.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/IslAst.cpp
new file mode 100644
index 00000000000..6497275df61
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/IslAst.cpp
@@ -0,0 +1,825 @@
+//===- IslAst.cpp - isl code generator interface --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The isl code generator interface takes a Scop and generates an isl_ast. This
+// ist_ast can either be returned directly or it can be pretty printed to
+// stdout.
+//
+// A typical isl_ast output looks like this:
+//
+// for (c2 = max(0, ceild(n + m, 2); c2 <= min(511, floord(5 * n, 3)); c2++) {
+// bb2(c2);
+// }
+//
+// An in-depth discussion of our AST generation approach can be found in:
+//
+// Polyhedral AST generation is more than scanning polyhedra
+// Tobias Grosser, Sven Verdoolaege, Albert Cohen
+// ACM Transactions on Programming Languages and Systems (TOPLAS),
+// 37(4), July 2015
+// http://www.grosser.es/#pub-polyhedral-AST-generation
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/IslAst.h"
+#include "polly/CodeGen/CodeGeneration.h"
+#include "polly/DependenceInfo.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "polly/ScopDetection.h"
+#include "polly/ScopInfo.h"
+#include "polly/ScopPass.h"
+#include "polly/Support/GICHelper.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/IR/Function.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+#include "isl/aff.h"
+#include "isl/ast.h"
+#include "isl/ast_build.h"
+#include "isl/id.h"
+#include "isl/isl-noexceptions.h"
+#include "isl/printer.h"
+#include "isl/schedule.h"
+#include "isl/set.h"
+#include "isl/union_map.h"
+#include "isl/val.h"
+#include <cassert>
+#include <cstdlib>
+
+#define DEBUG_TYPE "polly-ast"
+
+using namespace llvm;
+using namespace polly;
+
+using IslAstUserPayload = IslAstInfo::IslAstUserPayload;
+
+static cl::opt<bool>
+ PollyParallel("polly-parallel",
+ cl::desc("Generate thread parallel code (isl codegen only)"),
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> PrintAccesses("polly-ast-print-accesses",
+ cl::desc("Print memory access functions"),
+ cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool> PollyParallelForce(
+ "polly-parallel-force",
+ cl::desc(
+ "Force generation of thread parallel code ignoring any cost model"),
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> UseContext("polly-ast-use-context",
+ cl::desc("Use context"), cl::Hidden,
+ cl::init(true), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool> DetectParallel("polly-ast-detect-parallel",
+ cl::desc("Detect parallelism"), cl::Hidden,
+ cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+STATISTIC(ScopsProcessed, "Number of SCoPs processed");
+STATISTIC(ScopsBeneficial, "Number of beneficial SCoPs");
+STATISTIC(BeneficialAffineLoops, "Number of beneficial affine loops");
+STATISTIC(BeneficialBoxedLoops, "Number of beneficial boxed loops");
+
+STATISTIC(NumForLoops, "Number of for-loops");
+STATISTIC(NumParallel, "Number of parallel for-loops");
+STATISTIC(NumInnermostParallel, "Number of innermost parallel for-loops");
+STATISTIC(NumOutermostParallel, "Number of outermost parallel for-loops");
+STATISTIC(NumReductionParallel, "Number of reduction-parallel for-loops");
+STATISTIC(NumExecutedInParallel, "Number of for-loops executed in parallel");
+STATISTIC(NumIfConditions, "Number of if-conditions");
+
+namespace polly {
+
+/// Temporary information used when building the ast.
+struct AstBuildUserInfo {
+ /// Construct and initialize the helper struct for AST creation.
+ AstBuildUserInfo() = default;
+
+ /// The dependence information used for the parallelism check.
+ const Dependences *Deps = nullptr;
+
+ /// Flag to indicate that we are inside a parallel for node.
+ bool InParallelFor = false;
+
+ /// Flag to indicate that we are inside an SIMD node.
+ bool InSIMD = false;
+
+ /// The last iterator id created for the current SCoP.
+ isl_id *LastForNodeId = nullptr;
+};
+} // namespace polly
+
+/// Free an IslAstUserPayload object pointed to by @p Ptr.
+static void freeIslAstUserPayload(void *Ptr) {
+ delete ((IslAstInfo::IslAstUserPayload *)Ptr);
+}
+
+/// Print a string @p str in a single line using @p Printer.
+static isl_printer *printLine(__isl_take isl_printer *Printer,
+ const std::string &str,
+ __isl_keep isl_pw_aff *PWA = nullptr) {
+ Printer = isl_printer_start_line(Printer);
+ Printer = isl_printer_print_str(Printer, str.c_str());
+ if (PWA)
+ Printer = isl_printer_print_pw_aff(Printer, PWA);
+ return isl_printer_end_line(Printer);
+}
+
+/// Return all broken reductions as a string of clauses (OpenMP style).
+static const std::string getBrokenReductionsStr(const isl::ast_node &Node) {
+ IslAstInfo::MemoryAccessSet *BrokenReductions;
+ std::string str;
+
+ BrokenReductions = IslAstInfo::getBrokenReductions(Node);
+ if (!BrokenReductions || BrokenReductions->empty())
+ return "";
+
+ // Map each type of reduction to a comma separated list of the base addresses.
+ std::map<MemoryAccess::ReductionType, std::string> Clauses;
+ for (MemoryAccess *MA : *BrokenReductions)
+ if (MA->isWrite())
+ Clauses[MA->getReductionType()] +=
+ ", " + MA->getScopArrayInfo()->getName();
+
+ // Now print the reductions sorted by type. Each type will cause a clause
+ // like: reduction (+ : sum0, sum1, sum2)
+ for (const auto &ReductionClause : Clauses) {
+ str += " reduction (";
+ str += MemoryAccess::getReductionOperatorStr(ReductionClause.first);
+ // Remove the first two symbols (", ") to make the output look pretty.
+ str += " : " + ReductionClause.second.substr(2) + ")";
+ }
+
+ return str;
+}
+
+/// Callback executed for each for node in the ast in order to print it.
+static isl_printer *cbPrintFor(__isl_take isl_printer *Printer,
+ __isl_take isl_ast_print_options *Options,
+ __isl_keep isl_ast_node *Node, void *) {
+ isl::pw_aff DD =
+ IslAstInfo::getMinimalDependenceDistance(isl::manage_copy(Node));
+ const std::string BrokenReductionsStr =
+ getBrokenReductionsStr(isl::manage_copy(Node));
+ const std::string KnownParallelStr = "#pragma known-parallel";
+ const std::string DepDisPragmaStr = "#pragma minimal dependence distance: ";
+ const std::string SimdPragmaStr = "#pragma simd";
+ const std::string OmpPragmaStr = "#pragma omp parallel for";
+
+ if (!DD.is_null())
+ Printer = printLine(Printer, DepDisPragmaStr, DD.get());
+
+ if (IslAstInfo::isInnermostParallel(isl::manage_copy(Node)))
+ Printer = printLine(Printer, SimdPragmaStr + BrokenReductionsStr);
+
+ if (IslAstInfo::isExecutedInParallel(isl::manage_copy(Node)))
+ Printer = printLine(Printer, OmpPragmaStr);
+ else if (IslAstInfo::isOutermostParallel(isl::manage_copy(Node)))
+ Printer = printLine(Printer, KnownParallelStr + BrokenReductionsStr);
+
+ return isl_ast_node_for_print(Node, Printer, Options);
+}
+
+/// Check if the current scheduling dimension is parallel.
+///
+/// In case the dimension is parallel we also check if any reduction
+/// dependences is broken when we exploit this parallelism. If so,
+/// @p IsReductionParallel will be set to true. The reduction dependences we use
+/// to check are actually the union of the transitive closure of the initial
+/// reduction dependences together with their reversal. Even though these
+/// dependences connect all iterations with each other (thus they are cyclic)
+/// we can perform the parallelism check as we are only interested in a zero
+/// (or non-zero) dependence distance on the dimension in question.
+static bool astScheduleDimIsParallel(const isl::ast_build &Build,
+ const Dependences *D,
+ IslAstUserPayload *NodeInfo) {
+ if (!D->hasValidDependences())
+ return false;
+
+ isl::union_map Schedule = Build.get_schedule();
+ isl::union_map Dep = D->getDependences(
+ Dependences::TYPE_RAW | Dependences::TYPE_WAW | Dependences::TYPE_WAR);
+
+ if (!D->isParallel(Schedule.get(), Dep.release())) {
+ isl::union_map DepsAll =
+ D->getDependences(Dependences::TYPE_RAW | Dependences::TYPE_WAW |
+ Dependences::TYPE_WAR | Dependences::TYPE_TC_RED);
+ // TODO: We will need to change isParallel to stop the unwrapping
+ isl_pw_aff *MinimalDependenceDistanceIsl = nullptr;
+ D->isParallel(Schedule.get(), DepsAll.release(),
+ &MinimalDependenceDistanceIsl);
+ NodeInfo->MinimalDependenceDistance =
+ isl::manage(MinimalDependenceDistanceIsl);
+ return false;
+ }
+
+ isl::union_map RedDeps = D->getDependences(Dependences::TYPE_TC_RED);
+ if (!D->isParallel(Schedule.get(), RedDeps.release()))
+ NodeInfo->IsReductionParallel = true;
+
+ if (!NodeInfo->IsReductionParallel)
+ return true;
+
+ for (const auto &MaRedPair : D->getReductionDependences()) {
+ if (!MaRedPair.second)
+ continue;
+ isl::union_map MaRedDeps = isl::manage_copy(MaRedPair.second);
+ if (!D->isParallel(Schedule.get(), MaRedDeps.release()))
+ NodeInfo->BrokenReductions.insert(MaRedPair.first);
+ }
+ return true;
+}
+
+// This method is executed before the construction of a for node. It creates
+// an isl_id that is used to annotate the subsequently generated ast for nodes.
+//
+// In this function we also run the following analyses:
+//
+// - Detection of openmp parallel loops
+//
+static __isl_give isl_id *astBuildBeforeFor(__isl_keep isl_ast_build *Build,
+ void *User) {
+ AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User;
+ IslAstUserPayload *Payload = new IslAstUserPayload();
+ isl_id *Id = isl_id_alloc(isl_ast_build_get_ctx(Build), "", Payload);
+ Id = isl_id_set_free_user(Id, freeIslAstUserPayload);
+ BuildInfo->LastForNodeId = Id;
+
+ Payload->IsParallel = astScheduleDimIsParallel(isl::manage_copy(Build),
+ BuildInfo->Deps, Payload);
+
+ // Test for parallelism only if we are not already inside a parallel loop
+ if (!BuildInfo->InParallelFor && !BuildInfo->InSIMD)
+ BuildInfo->InParallelFor = Payload->IsOutermostParallel =
+ Payload->IsParallel;
+
+ return Id;
+}
+
+// This method is executed after the construction of a for node.
+//
+// It performs the following actions:
+//
+// - Reset the 'InParallelFor' flag, as soon as we leave a for node,
+// that is marked as openmp parallel.
+//
+static __isl_give isl_ast_node *
+astBuildAfterFor(__isl_take isl_ast_node *Node, __isl_keep isl_ast_build *Build,
+ void *User) {
+ isl_id *Id = isl_ast_node_get_annotation(Node);
+ assert(Id && "Post order visit assumes annotated for nodes");
+ IslAstUserPayload *Payload = (IslAstUserPayload *)isl_id_get_user(Id);
+ assert(Payload && "Post order visit assumes annotated for nodes");
+
+ AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User;
+ assert(Payload->Build.is_null() && "Build environment already set");
+ Payload->Build = isl::manage_copy(Build);
+ Payload->IsInnermost = (Id == BuildInfo->LastForNodeId);
+
+ Payload->IsInnermostParallel =
+ Payload->IsInnermost && (BuildInfo->InSIMD || Payload->IsParallel);
+ if (Payload->IsOutermostParallel)
+ BuildInfo->InParallelFor = false;
+
+ isl_id_free(Id);
+ return Node;
+}
+
+static isl_stat astBuildBeforeMark(__isl_keep isl_id *MarkId,
+ __isl_keep isl_ast_build *Build,
+ void *User) {
+ if (!MarkId)
+ return isl_stat_error;
+
+ AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User;
+ if (strcmp(isl_id_get_name(MarkId), "SIMD") == 0)
+ BuildInfo->InSIMD = true;
+
+ return isl_stat_ok;
+}
+
+static __isl_give isl_ast_node *
+astBuildAfterMark(__isl_take isl_ast_node *Node,
+ __isl_keep isl_ast_build *Build, void *User) {
+ assert(isl_ast_node_get_type(Node) == isl_ast_node_mark);
+ AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User;
+ auto *Id = isl_ast_node_mark_get_id(Node);
+ if (strcmp(isl_id_get_name(Id), "SIMD") == 0)
+ BuildInfo->InSIMD = false;
+ isl_id_free(Id);
+ return Node;
+}
+
+static __isl_give isl_ast_node *AtEachDomain(__isl_take isl_ast_node *Node,
+ __isl_keep isl_ast_build *Build,
+ void *User) {
+ assert(!isl_ast_node_get_annotation(Node) && "Node already annotated");
+
+ IslAstUserPayload *Payload = new IslAstUserPayload();
+ isl_id *Id = isl_id_alloc(isl_ast_build_get_ctx(Build), "", Payload);
+ Id = isl_id_set_free_user(Id, freeIslAstUserPayload);
+
+ Payload->Build = isl::manage_copy(Build);
+
+ return isl_ast_node_set_annotation(Node, Id);
+}
+
+// Build alias check condition given a pair of minimal/maximal access.
+static isl::ast_expr buildCondition(Scop &S, isl::ast_build Build,
+ const Scop::MinMaxAccessTy *It0,
+ const Scop::MinMaxAccessTy *It1) {
+
+ isl::pw_multi_aff AFirst = It0->first;
+ isl::pw_multi_aff ASecond = It0->second;
+ isl::pw_multi_aff BFirst = It1->first;
+ isl::pw_multi_aff BSecond = It1->second;
+
+ isl::id Left = AFirst.get_tuple_id(isl::dim::set);
+ isl::id Right = BFirst.get_tuple_id(isl::dim::set);
+
+ isl::ast_expr True =
+ isl::ast_expr::from_val(isl::val::int_from_ui(Build.ctx(), 1));
+ isl::ast_expr False =
+ isl::ast_expr::from_val(isl::val::int_from_ui(Build.ctx(), 0));
+
+ const ScopArrayInfo *BaseLeft =
+ ScopArrayInfo::getFromId(Left)->getBasePtrOriginSAI();
+ const ScopArrayInfo *BaseRight =
+ ScopArrayInfo::getFromId(Right)->getBasePtrOriginSAI();
+ if (BaseLeft && BaseLeft == BaseRight)
+ return True;
+
+ isl::set Params = S.getContext();
+
+ isl::ast_expr NonAliasGroup, MinExpr, MaxExpr;
+
+ // In the following, we first check if any accesses will be empty under
+ // the execution context of the scop and do not code generate them if this
+ // is the case as isl will fail to derive valid AST expressions for such
+ // accesses.
+
+ if (!AFirst.intersect_params(Params).domain().is_empty() &&
+ !BSecond.intersect_params(Params).domain().is_empty()) {
+ MinExpr = Build.access_from(AFirst).address_of();
+ MaxExpr = Build.access_from(BSecond).address_of();
+ NonAliasGroup = MaxExpr.le(MinExpr);
+ }
+
+ if (!BFirst.intersect_params(Params).domain().is_empty() &&
+ !ASecond.intersect_params(Params).domain().is_empty()) {
+ MinExpr = Build.access_from(BFirst).address_of();
+ MaxExpr = Build.access_from(ASecond).address_of();
+
+ isl::ast_expr Result = MaxExpr.le(MinExpr);
+ if (!NonAliasGroup.is_null())
+ NonAliasGroup = isl::manage(
+ isl_ast_expr_or(NonAliasGroup.release(), Result.release()));
+ else
+ NonAliasGroup = Result;
+ }
+
+ if (NonAliasGroup.is_null())
+ NonAliasGroup = True;
+
+ return NonAliasGroup;
+}
+
+isl::ast_expr IslAst::buildRunCondition(Scop &S, const isl::ast_build &Build) {
+ isl::ast_expr RunCondition;
+
+ // The conditions that need to be checked at run-time for this scop are
+ // available as an isl_set in the runtime check context from which we can
+ // directly derive a run-time condition.
+ auto PosCond = Build.expr_from(S.getAssumedContext());
+ if (S.hasTrivialInvalidContext()) {
+ RunCondition = std::move(PosCond);
+ } else {
+ auto ZeroV = isl::val::zero(Build.ctx());
+ auto NegCond = Build.expr_from(S.getInvalidContext());
+ auto NotNegCond =
+ isl::ast_expr::from_val(std::move(ZeroV)).eq(std::move(NegCond));
+ RunCondition =
+ isl::manage(isl_ast_expr_and(PosCond.release(), NotNegCond.release()));
+ }
+
+ // Create the alias checks from the minimal/maximal accesses in each alias
+ // group which consists of read only and non read only (read write) accesses.
+ // This operation is by construction quadratic in the read-write pointers and
+ // linear in the read only pointers in each alias group.
+ for (const Scop::MinMaxVectorPairTy &MinMaxAccessPair : S.getAliasGroups()) {
+ auto &MinMaxReadWrite = MinMaxAccessPair.first;
+ auto &MinMaxReadOnly = MinMaxAccessPair.second;
+ auto RWAccEnd = MinMaxReadWrite.end();
+
+ for (auto RWAccIt0 = MinMaxReadWrite.begin(); RWAccIt0 != RWAccEnd;
+ ++RWAccIt0) {
+ for (auto RWAccIt1 = RWAccIt0 + 1; RWAccIt1 != RWAccEnd; ++RWAccIt1)
+ RunCondition = isl::manage(isl_ast_expr_and(
+ RunCondition.release(),
+ buildCondition(S, Build, RWAccIt0, RWAccIt1).release()));
+ for (const Scop::MinMaxAccessTy &ROAccIt : MinMaxReadOnly)
+ RunCondition = isl::manage(isl_ast_expr_and(
+ RunCondition.release(),
+ buildCondition(S, Build, RWAccIt0, &ROAccIt).release()));
+ }
+ }
+
+ return RunCondition;
+}
+
+/// Simple cost analysis for a given SCoP.
+///
+/// TODO: Improve this analysis and extract it to make it usable in other
+/// places too.
+/// In order to improve the cost model we could either keep track of
+/// performed optimizations (e.g., tiling) or compute properties on the
+/// original as well as optimized SCoP (e.g., #stride-one-accesses).
+static bool benefitsFromPolly(Scop &Scop, bool PerformParallelTest) {
+ if (PollyProcessUnprofitable)
+ return true;
+
+ // Check if nothing interesting happened.
+ if (!PerformParallelTest && !Scop.isOptimized() &&
+ Scop.getAliasGroups().empty())
+ return false;
+
+ // The default assumption is that Polly improves the code.
+ return true;
+}
+
+/// Collect statistics for the syntax tree rooted at @p Ast.
+static void walkAstForStatistics(const isl::ast_node &Ast) {
+ assert(!Ast.is_null());
+ isl_ast_node_foreach_descendant_top_down(
+ Ast.get(),
+ [](__isl_keep isl_ast_node *Node, void *User) -> isl_bool {
+ switch (isl_ast_node_get_type(Node)) {
+ case isl_ast_node_for:
+ NumForLoops++;
+ if (IslAstInfo::isParallel(isl::manage_copy(Node)))
+ NumParallel++;
+ if (IslAstInfo::isInnermostParallel(isl::manage_copy(Node)))
+ NumInnermostParallel++;
+ if (IslAstInfo::isOutermostParallel(isl::manage_copy(Node)))
+ NumOutermostParallel++;
+ if (IslAstInfo::isReductionParallel(isl::manage_copy(Node)))
+ NumReductionParallel++;
+ if (IslAstInfo::isExecutedInParallel(isl::manage_copy(Node)))
+ NumExecutedInParallel++;
+ break;
+
+ case isl_ast_node_if:
+ NumIfConditions++;
+ break;
+
+ default:
+ break;
+ }
+
+ // Continue traversing subtrees.
+ return isl_bool_true;
+ },
+ nullptr);
+}
+
+IslAst::IslAst(Scop &Scop) : S(Scop), Ctx(Scop.getSharedIslCtx()) {}
+
+IslAst::IslAst(IslAst &&O)
+ : S(O.S), Ctx(O.Ctx), RunCondition(std::move(O.RunCondition)),
+ Root(std::move(O.Root)) {}
+
+void IslAst::init(const Dependences &D) {
+ bool PerformParallelTest = PollyParallel || DetectParallel ||
+ PollyVectorizerChoice != VECTORIZER_NONE;
+ auto ScheduleTree = S.getScheduleTree();
+
+ // Skip AST and code generation if there was no benefit achieved.
+ if (!benefitsFromPolly(S, PerformParallelTest))
+ return;
+
+ auto ScopStats = S.getStatistics();
+ ScopsBeneficial++;
+ BeneficialAffineLoops += ScopStats.NumAffineLoops;
+ BeneficialBoxedLoops += ScopStats.NumBoxedLoops;
+
+ auto Ctx = S.getIslCtx();
+ isl_options_set_ast_build_atomic_upper_bound(Ctx.get(), true);
+ isl_options_set_ast_build_detect_min_max(Ctx.get(), true);
+ isl_ast_build *Build;
+ AstBuildUserInfo BuildInfo;
+
+ if (UseContext)
+ Build = isl_ast_build_from_context(S.getContext().release());
+ else
+ Build = isl_ast_build_from_context(
+ isl_set_universe(S.getParamSpace().release()));
+
+ Build = isl_ast_build_set_at_each_domain(Build, AtEachDomain, nullptr);
+
+ if (PerformParallelTest) {
+ BuildInfo.Deps = &D;
+ BuildInfo.InParallelFor = false;
+ BuildInfo.InSIMD = false;
+
+ Build = isl_ast_build_set_before_each_for(Build, &astBuildBeforeFor,
+ &BuildInfo);
+ Build =
+ isl_ast_build_set_after_each_for(Build, &astBuildAfterFor, &BuildInfo);
+
+ Build = isl_ast_build_set_before_each_mark(Build, &astBuildBeforeMark,
+ &BuildInfo);
+
+ Build = isl_ast_build_set_after_each_mark(Build, &astBuildAfterMark,
+ &BuildInfo);
+ }
+
+ RunCondition = buildRunCondition(S, isl::manage_copy(Build));
+
+ Root = isl::manage(
+ isl_ast_build_node_from_schedule(Build, S.getScheduleTree().release()));
+ walkAstForStatistics(Root);
+
+ isl_ast_build_free(Build);
+}
+
+IslAst IslAst::create(Scop &Scop, const Dependences &D) {
+ IslAst Ast{Scop};
+ Ast.init(D);
+ return Ast;
+}
+
+isl::ast_node IslAst::getAst() { return Root; }
+isl::ast_expr IslAst::getRunCondition() { return RunCondition; }
+
+isl::ast_node IslAstInfo::getAst() { return Ast.getAst(); }
+isl::ast_expr IslAstInfo::getRunCondition() { return Ast.getRunCondition(); }
+
+IslAstUserPayload *IslAstInfo::getNodePayload(const isl::ast_node &Node) {
+ isl::id Id = Node.get_annotation();
+ if (Id.is_null())
+ return nullptr;
+ IslAstUserPayload *Payload = (IslAstUserPayload *)Id.get_user();
+ return Payload;
+}
+
+bool IslAstInfo::isInnermost(const isl::ast_node &Node) {
+ IslAstUserPayload *Payload = getNodePayload(Node);
+ return Payload && Payload->IsInnermost;
+}
+
+bool IslAstInfo::isParallel(const isl::ast_node &Node) {
+ return IslAstInfo::isInnermostParallel(Node) ||
+ IslAstInfo::isOutermostParallel(Node);
+}
+
+bool IslAstInfo::isInnermostParallel(const isl::ast_node &Node) {
+ IslAstUserPayload *Payload = getNodePayload(Node);
+ return Payload && Payload->IsInnermostParallel;
+}
+
+bool IslAstInfo::isOutermostParallel(const isl::ast_node &Node) {
+ IslAstUserPayload *Payload = getNodePayload(Node);
+ return Payload && Payload->IsOutermostParallel;
+}
+
+bool IslAstInfo::isReductionParallel(const isl::ast_node &Node) {
+ IslAstUserPayload *Payload = getNodePayload(Node);
+ return Payload && Payload->IsReductionParallel;
+}
+
+bool IslAstInfo::isExecutedInParallel(const isl::ast_node &Node) {
+ if (!PollyParallel)
+ return false;
+
+ // Do not parallelize innermost loops.
+ //
+ // Parallelizing innermost loops is often not profitable, especially if
+ // they have a low number of iterations.
+ //
+ // TODO: Decide this based on the number of loop iterations that will be
+ // executed. This can possibly require run-time checks, which again
+ // raises the question of both run-time check overhead and code size
+ // costs.
+ if (!PollyParallelForce && isInnermost(Node))
+ return false;
+
+ return isOutermostParallel(Node) && !isReductionParallel(Node);
+}
+
+isl::union_map IslAstInfo::getSchedule(const isl::ast_node &Node) {
+ IslAstUserPayload *Payload = getNodePayload(Node);
+ return Payload ? Payload->Build.get_schedule() : isl::union_map();
+}
+
+isl::pw_aff
+IslAstInfo::getMinimalDependenceDistance(const isl::ast_node &Node) {
+ IslAstUserPayload *Payload = getNodePayload(Node);
+ return Payload ? Payload->MinimalDependenceDistance : isl::pw_aff();
+}
+
+IslAstInfo::MemoryAccessSet *
+IslAstInfo::getBrokenReductions(const isl::ast_node &Node) {
+ IslAstUserPayload *Payload = getNodePayload(Node);
+ return Payload ? &Payload->BrokenReductions : nullptr;
+}
+
+isl::ast_build IslAstInfo::getBuild(const isl::ast_node &Node) {
+ IslAstUserPayload *Payload = getNodePayload(Node);
+ return Payload ? Payload->Build : isl::ast_build();
+}
+
+static std::unique_ptr<IslAstInfo> runIslAst(
+ Scop &Scop,
+ function_ref<const Dependences &(Dependences::AnalysisLevel)> GetDeps) {
+ // Skip SCoPs in case they're already handled by PPCGCodeGeneration.
+ if (Scop.isToBeSkipped())
+ return {};
+
+ ScopsProcessed++;
+
+ const Dependences &D = GetDeps(Dependences::AL_Statement);
+
+ if (D.getSharedIslCtx() != Scop.getSharedIslCtx()) {
+ LLVM_DEBUG(
+ dbgs() << "Got dependence analysis for different SCoP/isl_ctx\n");
+ return {};
+ }
+
+ std::unique_ptr<IslAstInfo> Ast = std::make_unique<IslAstInfo>(Scop, D);
+
+ LLVM_DEBUG({
+ if (Ast)
+ Ast->print(dbgs());
+ });
+
+ return Ast;
+}
+
+IslAstInfo IslAstAnalysis::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR) {
+ auto GetDeps = [&](Dependences::AnalysisLevel Lvl) -> const Dependences & {
+ return SAM.getResult<DependenceAnalysis>(S, SAR).getDependences(Lvl);
+ };
+
+ return std::move(*runIslAst(S, GetDeps));
+}
+
+static __isl_give isl_printer *cbPrintUser(__isl_take isl_printer *P,
+ __isl_take isl_ast_print_options *O,
+ __isl_keep isl_ast_node *Node,
+ void *User) {
+ isl::ast_node_user AstNode = isl::manage_copy(Node).as<isl::ast_node_user>();
+ isl::ast_expr NodeExpr = AstNode.expr();
+ isl::ast_expr CallExpr = NodeExpr.get_op_arg(0);
+ isl::id CallExprId = CallExpr.get_id();
+ ScopStmt *AccessStmt = (ScopStmt *)CallExprId.get_user();
+
+ P = isl_printer_start_line(P);
+ P = isl_printer_print_str(P, AccessStmt->getBaseName());
+ P = isl_printer_print_str(P, "(");
+ P = isl_printer_end_line(P);
+ P = isl_printer_indent(P, 2);
+
+ for (MemoryAccess *MemAcc : *AccessStmt) {
+ P = isl_printer_start_line(P);
+
+ if (MemAcc->isRead())
+ P = isl_printer_print_str(P, "/* read */ &");
+ else
+ P = isl_printer_print_str(P, "/* write */ ");
+
+ isl::ast_build Build = IslAstInfo::getBuild(isl::manage_copy(Node));
+ if (MemAcc->isAffine()) {
+ isl_pw_multi_aff *PwmaPtr =
+ MemAcc->applyScheduleToAccessRelation(Build.get_schedule()).release();
+ isl::pw_multi_aff Pwma = isl::manage(PwmaPtr);
+ isl::ast_expr AccessExpr = Build.access_from(Pwma);
+ P = isl_printer_print_ast_expr(P, AccessExpr.get());
+ } else {
+ P = isl_printer_print_str(
+ P, MemAcc->getLatestScopArrayInfo()->getName().c_str());
+ P = isl_printer_print_str(P, "[*]");
+ }
+ P = isl_printer_end_line(P);
+ }
+
+ P = isl_printer_indent(P, -2);
+ P = isl_printer_start_line(P);
+ P = isl_printer_print_str(P, ");");
+ P = isl_printer_end_line(P);
+
+ isl_ast_print_options_free(O);
+ return P;
+}
+
+void IslAstInfo::print(raw_ostream &OS) {
+ isl_ast_print_options *Options;
+ isl::ast_node RootNode = Ast.getAst();
+ Function &F = S.getFunction();
+
+ OS << ":: isl ast :: " << F.getName() << " :: " << S.getNameStr() << "\n";
+
+ if (RootNode.is_null()) {
+ OS << ":: isl ast generation and code generation was skipped!\n\n";
+ OS << ":: This is either because no useful optimizations could be applied "
+ "(use -polly-process-unprofitable to enforce code generation) or "
+ "because earlier passes such as dependence analysis timed out (use "
+ "-polly-dependences-computeout=0 to set dependence analysis timeout "
+ "to infinity)\n\n";
+ return;
+ }
+
+ isl::ast_expr RunCondition = Ast.getRunCondition();
+ char *RtCStr, *AstStr;
+
+ Options = isl_ast_print_options_alloc(S.getIslCtx().get());
+
+ if (PrintAccesses)
+ Options =
+ isl_ast_print_options_set_print_user(Options, cbPrintUser, nullptr);
+ Options = isl_ast_print_options_set_print_for(Options, cbPrintFor, nullptr);
+
+ isl_printer *P = isl_printer_to_str(S.getIslCtx().get());
+ P = isl_printer_set_output_format(P, ISL_FORMAT_C);
+ P = isl_printer_print_ast_expr(P, RunCondition.get());
+ RtCStr = isl_printer_get_str(P);
+ P = isl_printer_flush(P);
+ P = isl_printer_indent(P, 4);
+ P = isl_ast_node_print(RootNode.get(), P, Options);
+ AstStr = isl_printer_get_str(P);
+
+ LLVM_DEBUG({
+ dbgs() << S.getContextStr() << "\n";
+ dbgs() << stringFromIslObj(S.getScheduleTree(), "null");
+ });
+ OS << "\nif (" << RtCStr << ")\n\n";
+ OS << AstStr << "\n";
+ OS << "else\n";
+ OS << " { /* original code */ }\n\n";
+
+ free(RtCStr);
+ free(AstStr);
+
+ isl_printer_free(P);
+}
+
+AnalysisKey IslAstAnalysis::Key;
+PreservedAnalyses IslAstPrinterPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &U) {
+ auto &Ast = SAM.getResult<IslAstAnalysis>(S, SAR);
+ Ast.print(OS);
+ return PreservedAnalyses::all();
+}
+
+void IslAstInfoWrapperPass::releaseMemory() { Ast.reset(); }
+
+bool IslAstInfoWrapperPass::runOnScop(Scop &Scop) {
+ auto GetDeps = [this](Dependences::AnalysisLevel Lvl) -> const Dependences & {
+ return getAnalysis<DependenceInfo>().getDependences(Lvl);
+ };
+
+ Ast = runIslAst(Scop, GetDeps);
+
+ return false;
+}
+
+void IslAstInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ // Get the Common analysis usage of ScopPasses.
+ ScopPass::getAnalysisUsage(AU);
+ AU.addRequiredTransitive<ScopInfoRegionPass>();
+ AU.addRequired<DependenceInfo>();
+
+ AU.addPreserved<DependenceInfo>();
+}
+
+void IslAstInfoWrapperPass::printScop(raw_ostream &OS, Scop &S) const {
+ OS << "Printing analysis 'Polly - Generate an AST of the SCoP (isl)'"
+ << S.getName() << "' in function '" << S.getFunction().getName() << "':\n";
+ if (Ast)
+ Ast->print(OS);
+}
+
+char IslAstInfoWrapperPass::ID = 0;
+
+Pass *polly::createIslAstInfoWrapperPassPass() {
+ return new IslAstInfoWrapperPass();
+}
+
+INITIALIZE_PASS_BEGIN(IslAstInfoWrapperPass, "polly-ast",
+ "Polly - Generate an AST of the SCoP (isl)", false,
+ false);
+INITIALIZE_PASS_DEPENDENCY(ScopInfoRegionPass);
+INITIALIZE_PASS_DEPENDENCY(DependenceInfo);
+INITIALIZE_PASS_END(IslAstInfoWrapperPass, "polly-ast",
+ "Polly - Generate an AST from the SCoP (isl)", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/IslExprBuilder.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/IslExprBuilder.cpp
new file mode 100644
index 00000000000..4d1094cf3ef
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/IslExprBuilder.cpp
@@ -0,0 +1,788 @@
+//===------ IslExprBuilder.cpp ----- Code generate isl AST expressions ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/IslExprBuilder.h"
+#include "polly/CodeGen/RuntimeDebugBuilder.h"
+#include "polly/Options.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/GICHelper.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+
+using namespace llvm;
+using namespace polly;
+
+/// Different overflow tracking modes.
+enum OverflowTrackingChoice {
+ OT_NEVER, ///< Never tack potential overflows.
+ OT_REQUEST, ///< Track potential overflows if requested.
+ OT_ALWAYS ///< Always track potential overflows.
+};
+
+static cl::opt<OverflowTrackingChoice> OTMode(
+ "polly-overflow-tracking",
+ cl::desc("Define where potential integer overflows in generated "
+ "expressions should be tracked."),
+ cl::values(clEnumValN(OT_NEVER, "never", "Never track the overflow bit."),
+ clEnumValN(OT_REQUEST, "request",
+ "Track the overflow bit if requested."),
+ clEnumValN(OT_ALWAYS, "always",
+ "Always track the overflow bit.")),
+ cl::Hidden, cl::init(OT_REQUEST), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+IslExprBuilder::IslExprBuilder(Scop &S, PollyIRBuilder &Builder,
+ IDToValueTy &IDToValue, ValueMapT &GlobalMap,
+ const DataLayout &DL, ScalarEvolution &SE,
+ DominatorTree &DT, LoopInfo &LI,
+ BasicBlock *StartBlock)
+ : S(S), Builder(Builder), IDToValue(IDToValue), GlobalMap(GlobalMap),
+ DL(DL), SE(SE), DT(DT), LI(LI), StartBlock(StartBlock) {
+ OverflowState = (OTMode == OT_ALWAYS) ? Builder.getFalse() : nullptr;
+}
+
+void IslExprBuilder::setTrackOverflow(bool Enable) {
+ // If potential overflows are tracked always or never we ignore requests
+ // to change the behavior.
+ if (OTMode != OT_REQUEST)
+ return;
+
+ if (Enable) {
+ // If tracking should be enabled initialize the OverflowState.
+ OverflowState = Builder.getFalse();
+ } else {
+ // If tracking should be disabled just unset the OverflowState.
+ OverflowState = nullptr;
+ }
+}
+
+Value *IslExprBuilder::getOverflowState() const {
+ // If the overflow tracking was requested but it is disabled we avoid the
+ // additional nullptr checks at the call sides but instead provide a
+ // meaningful result.
+ if (OTMode == OT_NEVER)
+ return Builder.getFalse();
+ return OverflowState;
+}
+
+bool IslExprBuilder::hasLargeInts(isl::ast_expr Expr) {
+ enum isl_ast_expr_type Type = isl_ast_expr_get_type(Expr.get());
+
+ if (Type == isl_ast_expr_id)
+ return false;
+
+ if (Type == isl_ast_expr_int) {
+ isl::val Val = Expr.get_val();
+ APInt APValue = APIntFromVal(Val);
+ auto BitWidth = APValue.getBitWidth();
+ return BitWidth >= 64;
+ }
+
+ assert(Type == isl_ast_expr_op && "Expected isl_ast_expr of type operation");
+
+ int NumArgs = isl_ast_expr_get_op_n_arg(Expr.get());
+
+ for (int i = 0; i < NumArgs; i++) {
+ isl::ast_expr Operand = Expr.get_op_arg(i);
+ if (hasLargeInts(Operand))
+ return true;
+ }
+
+ return false;
+}
+
+Value *IslExprBuilder::createBinOp(BinaryOperator::BinaryOps Opc, Value *LHS,
+ Value *RHS, const Twine &Name) {
+ // Handle the plain operation (without overflow tracking) first.
+ if (!OverflowState) {
+ switch (Opc) {
+ case Instruction::Add:
+ return Builder.CreateNSWAdd(LHS, RHS, Name);
+ case Instruction::Sub:
+ return Builder.CreateNSWSub(LHS, RHS, Name);
+ case Instruction::Mul:
+ return Builder.CreateNSWMul(LHS, RHS, Name);
+ default:
+ llvm_unreachable("Unknown binary operator!");
+ }
+ }
+
+ Function *F = nullptr;
+ Module *M = Builder.GetInsertBlock()->getModule();
+ switch (Opc) {
+ case Instruction::Add:
+ F = Intrinsic::getDeclaration(M, Intrinsic::sadd_with_overflow,
+ {LHS->getType()});
+ break;
+ case Instruction::Sub:
+ F = Intrinsic::getDeclaration(M, Intrinsic::ssub_with_overflow,
+ {LHS->getType()});
+ break;
+ case Instruction::Mul:
+ F = Intrinsic::getDeclaration(M, Intrinsic::smul_with_overflow,
+ {LHS->getType()});
+ break;
+ default:
+ llvm_unreachable("No overflow intrinsic for binary operator found!");
+ }
+
+ auto *ResultStruct = Builder.CreateCall(F, {LHS, RHS}, Name);
+ assert(ResultStruct->getType()->isStructTy());
+
+ auto *OverflowFlag =
+ Builder.CreateExtractValue(ResultStruct, 1, Name + ".obit");
+
+ // If all overflows are tracked we do not combine the results as this could
+ // cause dominance problems. Instead we will always keep the last overflow
+ // flag as current state.
+ if (OTMode == OT_ALWAYS)
+ OverflowState = OverflowFlag;
+ else
+ OverflowState =
+ Builder.CreateOr(OverflowState, OverflowFlag, "polly.overflow.state");
+
+ return Builder.CreateExtractValue(ResultStruct, 0, Name + ".res");
+}
+
+Value *IslExprBuilder::createAdd(Value *LHS, Value *RHS, const Twine &Name) {
+ return createBinOp(Instruction::Add, LHS, RHS, Name);
+}
+
+Value *IslExprBuilder::createSub(Value *LHS, Value *RHS, const Twine &Name) {
+ return createBinOp(Instruction::Sub, LHS, RHS, Name);
+}
+
+Value *IslExprBuilder::createMul(Value *LHS, Value *RHS, const Twine &Name) {
+ return createBinOp(Instruction::Mul, LHS, RHS, Name);
+}
+
+Type *IslExprBuilder::getWidestType(Type *T1, Type *T2) {
+ assert(isa<IntegerType>(T1) && isa<IntegerType>(T2));
+
+ if (T1->getPrimitiveSizeInBits() < T2->getPrimitiveSizeInBits())
+ return T2;
+ else
+ return T1;
+}
+
+Value *IslExprBuilder::createOpUnary(__isl_take isl_ast_expr *Expr) {
+ assert(isl_ast_expr_get_op_type(Expr) == isl_ast_op_minus &&
+ "Unsupported unary operation");
+
+ Value *V;
+ Type *MaxType = getType(Expr);
+ assert(MaxType->isIntegerTy() &&
+ "Unary expressions can only be created for integer types");
+
+ V = create(isl_ast_expr_get_op_arg(Expr, 0));
+ MaxType = getWidestType(MaxType, V->getType());
+
+ if (MaxType != V->getType())
+ V = Builder.CreateSExt(V, MaxType);
+
+ isl_ast_expr_free(Expr);
+ return createSub(ConstantInt::getNullValue(MaxType), V);
+}
+
+Value *IslExprBuilder::createOpNAry(__isl_take isl_ast_expr *Expr) {
+ assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op &&
+ "isl ast expression not of type isl_ast_op");
+ assert(isl_ast_expr_get_op_n_arg(Expr) >= 2 &&
+ "We need at least two operands in an n-ary operation");
+
+ CmpInst::Predicate Pred;
+ switch (isl_ast_expr_get_op_type(Expr)) {
+ default:
+ llvm_unreachable("This is not a an n-ary isl ast expression");
+ case isl_ast_op_max:
+ Pred = CmpInst::ICMP_SGT;
+ break;
+ case isl_ast_op_min:
+ Pred = CmpInst::ICMP_SLT;
+ break;
+ }
+
+ Value *V = create(isl_ast_expr_get_op_arg(Expr, 0));
+
+ for (int i = 1; i < isl_ast_expr_get_op_n_arg(Expr); ++i) {
+ Value *OpV = create(isl_ast_expr_get_op_arg(Expr, i));
+ Type *Ty = getWidestType(V->getType(), OpV->getType());
+
+ if (Ty != OpV->getType())
+ OpV = Builder.CreateSExt(OpV, Ty);
+
+ if (Ty != V->getType())
+ V = Builder.CreateSExt(V, Ty);
+
+ Value *Cmp = Builder.CreateICmp(Pred, V, OpV);
+ V = Builder.CreateSelect(Cmp, V, OpV);
+ }
+
+ // TODO: We can truncate the result, if it fits into a smaller type. This can
+ // help in cases where we have larger operands (e.g. i67) but the result is
+ // known to fit into i64. Without the truncation, the larger i67 type may
+ // force all subsequent operations to be performed on a non-native type.
+ isl_ast_expr_free(Expr);
+ return V;
+}
+
+std::pair<Value *, Type *>
+IslExprBuilder::createAccessAddress(isl_ast_expr *Expr) {
+ assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op &&
+ "isl ast expression not of type isl_ast_op");
+ assert(isl_ast_expr_get_op_type(Expr) == isl_ast_op_access &&
+ "not an access isl ast expression");
+ assert(isl_ast_expr_get_op_n_arg(Expr) >= 1 &&
+ "We need at least two operands to create a member access.");
+
+ Value *Base, *IndexOp, *Access;
+ isl_ast_expr *BaseExpr;
+ isl_id *BaseId;
+
+ BaseExpr = isl_ast_expr_get_op_arg(Expr, 0);
+ BaseId = isl_ast_expr_get_id(BaseExpr);
+ isl_ast_expr_free(BaseExpr);
+
+ const ScopArrayInfo *SAI = nullptr;
+
+ if (PollyDebugPrinting)
+ RuntimeDebugBuilder::createCPUPrinter(Builder, isl_id_get_name(BaseId));
+
+ if (IDToSAI)
+ SAI = (*IDToSAI)[BaseId];
+
+ if (!SAI)
+ SAI = ScopArrayInfo::getFromId(isl::manage(BaseId));
+ else
+ isl_id_free(BaseId);
+
+ assert(SAI && "No ScopArrayInfo found for this isl_id.");
+
+ Base = SAI->getBasePtr();
+
+ if (auto NewBase = GlobalMap.lookup(Base))
+ Base = NewBase;
+
+ assert(Base->getType()->isPointerTy() && "Access base should be a pointer");
+ StringRef BaseName = Base->getName();
+
+ auto PointerTy = PointerType::get(SAI->getElementType(),
+ Base->getType()->getPointerAddressSpace());
+ if (Base->getType() != PointerTy) {
+ Base =
+ Builder.CreateBitCast(Base, PointerTy, "polly.access.cast." + BaseName);
+ }
+
+ if (isl_ast_expr_get_op_n_arg(Expr) == 1) {
+ isl_ast_expr_free(Expr);
+ if (PollyDebugPrinting)
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "\n");
+ return {Base, SAI->getElementType()};
+ }
+
+ IndexOp = nullptr;
+ for (unsigned u = 1, e = isl_ast_expr_get_op_n_arg(Expr); u < e; u++) {
+ Value *NextIndex = create(isl_ast_expr_get_op_arg(Expr, u));
+ assert(NextIndex->getType()->isIntegerTy() &&
+ "Access index should be an integer");
+
+ if (PollyDebugPrinting)
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "[", NextIndex, "]");
+
+ if (!IndexOp) {
+ IndexOp = NextIndex;
+ } else {
+ Type *Ty = getWidestType(NextIndex->getType(), IndexOp->getType());
+
+ if (Ty != NextIndex->getType())
+ NextIndex = Builder.CreateIntCast(NextIndex, Ty, true);
+ if (Ty != IndexOp->getType())
+ IndexOp = Builder.CreateIntCast(IndexOp, Ty, true);
+
+ IndexOp = createAdd(IndexOp, NextIndex, "polly.access.add." + BaseName);
+ }
+
+ // For every but the last dimension multiply the size, for the last
+ // dimension we can exit the loop.
+ if (u + 1 >= e)
+ break;
+
+ const SCEV *DimSCEV = SAI->getDimensionSize(u);
+
+ llvm::ValueToSCEVMapTy Map;
+ for (auto &KV : GlobalMap)
+ Map[KV.first] = SE.getSCEV(KV.second);
+ DimSCEV = SCEVParameterRewriter::rewrite(DimSCEV, SE, Map);
+ Value *DimSize =
+ expandCodeFor(S, SE, DL, "polly", DimSCEV, DimSCEV->getType(),
+ &*Builder.GetInsertPoint(), nullptr,
+ StartBlock->getSinglePredecessor());
+
+ Type *Ty = getWidestType(DimSize->getType(), IndexOp->getType());
+
+ if (Ty != IndexOp->getType())
+ IndexOp = Builder.CreateSExtOrTrunc(IndexOp, Ty,
+ "polly.access.sext." + BaseName);
+ if (Ty != DimSize->getType())
+ DimSize = Builder.CreateSExtOrTrunc(DimSize, Ty,
+ "polly.access.sext." + BaseName);
+ IndexOp = createMul(IndexOp, DimSize, "polly.access.mul." + BaseName);
+ }
+
+ Access = Builder.CreateGEP(SAI->getElementType(), Base, IndexOp,
+ "polly.access." + BaseName);
+
+ if (PollyDebugPrinting)
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "\n");
+ isl_ast_expr_free(Expr);
+ return {Access, SAI->getElementType()};
+}
+
+Value *IslExprBuilder::createOpAccess(isl_ast_expr *Expr) {
+ auto Info = createAccessAddress(Expr);
+ assert(Info.first && "Could not create op access address");
+ return Builder.CreateLoad(Info.second, Info.first,
+ Info.first->getName() + ".load");
+}
+
+Value *IslExprBuilder::createOpBin(__isl_take isl_ast_expr *Expr) {
+ Value *LHS, *RHS, *Res;
+ Type *MaxType;
+ isl_ast_op_type OpType;
+
+ assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op &&
+ "isl ast expression not of type isl_ast_op");
+ assert(isl_ast_expr_get_op_n_arg(Expr) == 2 &&
+ "not a binary isl ast expression");
+
+ OpType = isl_ast_expr_get_op_type(Expr);
+
+ LHS = create(isl_ast_expr_get_op_arg(Expr, 0));
+ RHS = create(isl_ast_expr_get_op_arg(Expr, 1));
+
+ Type *LHSType = LHS->getType();
+ Type *RHSType = RHS->getType();
+
+ MaxType = getWidestType(LHSType, RHSType);
+
+ // Take the result into account when calculating the widest type.
+ //
+ // For operations such as '+' the result may require a type larger than
+ // the type of the individual operands. For other operations such as '/', the
+ // result type cannot be larger than the type of the individual operand. isl
+ // does not calculate correct types for these operations and we consequently
+ // exclude those operations here.
+ switch (OpType) {
+ case isl_ast_op_pdiv_q:
+ case isl_ast_op_pdiv_r:
+ case isl_ast_op_div:
+ case isl_ast_op_fdiv_q:
+ case isl_ast_op_zdiv_r:
+ // Do nothing
+ break;
+ case isl_ast_op_add:
+ case isl_ast_op_sub:
+ case isl_ast_op_mul:
+ MaxType = getWidestType(MaxType, getType(Expr));
+ break;
+ default:
+ llvm_unreachable("This is no binary isl ast expression");
+ }
+
+ if (MaxType != RHS->getType())
+ RHS = Builder.CreateSExt(RHS, MaxType);
+
+ if (MaxType != LHS->getType())
+ LHS = Builder.CreateSExt(LHS, MaxType);
+
+ switch (OpType) {
+ default:
+ llvm_unreachable("This is no binary isl ast expression");
+ case isl_ast_op_add:
+ Res = createAdd(LHS, RHS);
+ break;
+ case isl_ast_op_sub:
+ Res = createSub(LHS, RHS);
+ break;
+ case isl_ast_op_mul:
+ Res = createMul(LHS, RHS);
+ break;
+ case isl_ast_op_div:
+ Res = Builder.CreateSDiv(LHS, RHS, "pexp.div", true);
+ break;
+ case isl_ast_op_pdiv_q: // Dividend is non-negative
+ Res = Builder.CreateUDiv(LHS, RHS, "pexp.p_div_q");
+ break;
+ case isl_ast_op_fdiv_q: { // Round towards -infty
+ if (auto *Const = dyn_cast<ConstantInt>(RHS)) {
+ auto &Val = Const->getValue();
+ if (Val.isPowerOf2() && Val.isNonNegative()) {
+ Res = Builder.CreateAShr(LHS, Val.ceilLogBase2(), "polly.fdiv_q.shr");
+ break;
+ }
+ }
+ // TODO: Review code and check that this calculation does not yield
+ // incorrect overflow in some edge cases.
+ //
+ // floord(n,d) ((n < 0) ? (n - d + 1) : n) / d
+ Value *One = ConstantInt::get(MaxType, 1);
+ Value *Zero = ConstantInt::get(MaxType, 0);
+ Value *Sum1 = createSub(LHS, RHS, "pexp.fdiv_q.0");
+ Value *Sum2 = createAdd(Sum1, One, "pexp.fdiv_q.1");
+ Value *isNegative = Builder.CreateICmpSLT(LHS, Zero, "pexp.fdiv_q.2");
+ Value *Dividend =
+ Builder.CreateSelect(isNegative, Sum2, LHS, "pexp.fdiv_q.3");
+ Res = Builder.CreateSDiv(Dividend, RHS, "pexp.fdiv_q.4");
+ break;
+ }
+ case isl_ast_op_pdiv_r: // Dividend is non-negative
+ Res = Builder.CreateURem(LHS, RHS, "pexp.pdiv_r");
+ break;
+
+ case isl_ast_op_zdiv_r: // Result only compared against zero
+ Res = Builder.CreateSRem(LHS, RHS, "pexp.zdiv_r");
+ break;
+ }
+
+ // TODO: We can truncate the result, if it fits into a smaller type. This can
+ // help in cases where we have larger operands (e.g. i67) but the result is
+ // known to fit into i64. Without the truncation, the larger i67 type may
+ // force all subsequent operations to be performed on a non-native type.
+ isl_ast_expr_free(Expr);
+ return Res;
+}
+
+Value *IslExprBuilder::createOpSelect(__isl_take isl_ast_expr *Expr) {
+ assert(isl_ast_expr_get_op_type(Expr) == isl_ast_op_select &&
+ "Unsupported unary isl ast expression");
+ Value *LHS, *RHS, *Cond;
+ Type *MaxType = getType(Expr);
+
+ Cond = create(isl_ast_expr_get_op_arg(Expr, 0));
+ if (!Cond->getType()->isIntegerTy(1))
+ Cond = Builder.CreateIsNotNull(Cond);
+
+ LHS = create(isl_ast_expr_get_op_arg(Expr, 1));
+ RHS = create(isl_ast_expr_get_op_arg(Expr, 2));
+
+ MaxType = getWidestType(MaxType, LHS->getType());
+ MaxType = getWidestType(MaxType, RHS->getType());
+
+ if (MaxType != RHS->getType())
+ RHS = Builder.CreateSExt(RHS, MaxType);
+
+ if (MaxType != LHS->getType())
+ LHS = Builder.CreateSExt(LHS, MaxType);
+
+ // TODO: Do we want to truncate the result?
+ isl_ast_expr_free(Expr);
+ return Builder.CreateSelect(Cond, LHS, RHS);
+}
+
+Value *IslExprBuilder::createOpICmp(__isl_take isl_ast_expr *Expr) {
+ assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op &&
+ "Expected an isl_ast_expr_op expression");
+
+ Value *LHS, *RHS, *Res;
+
+ auto *Op0 = isl_ast_expr_get_op_arg(Expr, 0);
+ auto *Op1 = isl_ast_expr_get_op_arg(Expr, 1);
+ bool HasNonAddressOfOperand =
+ isl_ast_expr_get_type(Op0) != isl_ast_expr_op ||
+ isl_ast_expr_get_type(Op1) != isl_ast_expr_op ||
+ isl_ast_expr_get_op_type(Op0) != isl_ast_op_address_of ||
+ isl_ast_expr_get_op_type(Op1) != isl_ast_op_address_of;
+
+ LHS = create(Op0);
+ RHS = create(Op1);
+
+ auto *LHSTy = LHS->getType();
+ auto *RHSTy = RHS->getType();
+ bool IsPtrType = LHSTy->isPointerTy() || RHSTy->isPointerTy();
+ bool UseUnsignedCmp = IsPtrType && !HasNonAddressOfOperand;
+
+ auto *PtrAsIntTy = Builder.getIntNTy(DL.getPointerSizeInBits());
+ if (LHSTy->isPointerTy())
+ LHS = Builder.CreatePtrToInt(LHS, PtrAsIntTy);
+ if (RHSTy->isPointerTy())
+ RHS = Builder.CreatePtrToInt(RHS, PtrAsIntTy);
+
+ if (LHS->getType() != RHS->getType()) {
+ Type *MaxType = LHS->getType();
+ MaxType = getWidestType(MaxType, RHS->getType());
+
+ if (MaxType != RHS->getType())
+ RHS = Builder.CreateSExt(RHS, MaxType);
+
+ if (MaxType != LHS->getType())
+ LHS = Builder.CreateSExt(LHS, MaxType);
+ }
+
+ isl_ast_op_type OpType = isl_ast_expr_get_op_type(Expr);
+ assert(OpType >= isl_ast_op_eq && OpType <= isl_ast_op_gt &&
+ "Unsupported ICmp isl ast expression");
+ static_assert(isl_ast_op_eq + 4 == isl_ast_op_gt,
+ "Isl ast op type interface changed");
+
+ CmpInst::Predicate Predicates[5][2] = {
+ {CmpInst::ICMP_EQ, CmpInst::ICMP_EQ},
+ {CmpInst::ICMP_SLE, CmpInst::ICMP_ULE},
+ {CmpInst::ICMP_SLT, CmpInst::ICMP_ULT},
+ {CmpInst::ICMP_SGE, CmpInst::ICMP_UGE},
+ {CmpInst::ICMP_SGT, CmpInst::ICMP_UGT},
+ };
+
+ Res = Builder.CreateICmp(Predicates[OpType - isl_ast_op_eq][UseUnsignedCmp],
+ LHS, RHS);
+
+ isl_ast_expr_free(Expr);
+ return Res;
+}
+
+Value *IslExprBuilder::createOpBoolean(__isl_take isl_ast_expr *Expr) {
+ assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op &&
+ "Expected an isl_ast_expr_op expression");
+
+ Value *LHS, *RHS, *Res;
+ isl_ast_op_type OpType;
+
+ OpType = isl_ast_expr_get_op_type(Expr);
+
+ assert((OpType == isl_ast_op_and || OpType == isl_ast_op_or) &&
+ "Unsupported isl_ast_op_type");
+
+ LHS = create(isl_ast_expr_get_op_arg(Expr, 0));
+ RHS = create(isl_ast_expr_get_op_arg(Expr, 1));
+
+ // Even though the isl pretty printer prints the expressions as 'exp && exp'
+ // or 'exp || exp', we actually code generate the bitwise expressions
+ // 'exp & exp' or 'exp | exp'. This forces the evaluation of both branches,
+ // but it is, due to the use of i1 types, otherwise equivalent. The reason
+ // to go for bitwise operations is, that we assume the reduced control flow
+ // will outweigh the overhead introduced by evaluating unneeded expressions.
+ // The isl code generation currently does not take advantage of the fact that
+ // the expression after an '||' or '&&' is in some cases not evaluated.
+ // Evaluating it anyways does not cause any undefined behaviour.
+ //
+ // TODO: Document in isl itself, that the unconditionally evaluating the
+ // second part of '||' or '&&' expressions is safe.
+ if (!LHS->getType()->isIntegerTy(1))
+ LHS = Builder.CreateIsNotNull(LHS);
+ if (!RHS->getType()->isIntegerTy(1))
+ RHS = Builder.CreateIsNotNull(RHS);
+
+ switch (OpType) {
+ default:
+ llvm_unreachable("Unsupported boolean expression");
+ case isl_ast_op_and:
+ Res = Builder.CreateAnd(LHS, RHS);
+ break;
+ case isl_ast_op_or:
+ Res = Builder.CreateOr(LHS, RHS);
+ break;
+ }
+
+ isl_ast_expr_free(Expr);
+ return Res;
+}
+
+Value *
+IslExprBuilder::createOpBooleanConditional(__isl_take isl_ast_expr *Expr) {
+ assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op &&
+ "Expected an isl_ast_expr_op expression");
+
+ Value *LHS, *RHS;
+ isl_ast_op_type OpType;
+
+ Function *F = Builder.GetInsertBlock()->getParent();
+ LLVMContext &Context = F->getContext();
+
+ OpType = isl_ast_expr_get_op_type(Expr);
+
+ assert((OpType == isl_ast_op_and_then || OpType == isl_ast_op_or_else) &&
+ "Unsupported isl_ast_op_type");
+
+ auto InsertBB = Builder.GetInsertBlock();
+ auto InsertPoint = Builder.GetInsertPoint();
+ auto NextBB = SplitBlock(InsertBB, &*InsertPoint, &DT, &LI);
+ BasicBlock *CondBB = BasicBlock::Create(Context, "polly.cond", F);
+ LI.changeLoopFor(CondBB, LI.getLoopFor(InsertBB));
+ DT.addNewBlock(CondBB, InsertBB);
+
+ InsertBB->getTerminator()->eraseFromParent();
+ Builder.SetInsertPoint(InsertBB);
+ auto BR = Builder.CreateCondBr(Builder.getTrue(), NextBB, CondBB);
+
+ Builder.SetInsertPoint(CondBB);
+ Builder.CreateBr(NextBB);
+
+ Builder.SetInsertPoint(InsertBB->getTerminator());
+
+ LHS = create(isl_ast_expr_get_op_arg(Expr, 0));
+ if (!LHS->getType()->isIntegerTy(1))
+ LHS = Builder.CreateIsNotNull(LHS);
+ auto LeftBB = Builder.GetInsertBlock();
+
+ if (OpType == isl_ast_op_and || OpType == isl_ast_op_and_then)
+ BR->setCondition(Builder.CreateNeg(LHS));
+ else
+ BR->setCondition(LHS);
+
+ Builder.SetInsertPoint(CondBB->getTerminator());
+ RHS = create(isl_ast_expr_get_op_arg(Expr, 1));
+ if (!RHS->getType()->isIntegerTy(1))
+ RHS = Builder.CreateIsNotNull(RHS);
+ auto RightBB = Builder.GetInsertBlock();
+
+ Builder.SetInsertPoint(NextBB->getTerminator());
+ auto PHI = Builder.CreatePHI(Builder.getInt1Ty(), 2);
+ PHI->addIncoming(OpType == isl_ast_op_and_then ? Builder.getFalse()
+ : Builder.getTrue(),
+ LeftBB);
+ PHI->addIncoming(RHS, RightBB);
+
+ isl_ast_expr_free(Expr);
+ return PHI;
+}
+
+Value *IslExprBuilder::createOp(__isl_take isl_ast_expr *Expr) {
+ assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op &&
+ "Expression not of type isl_ast_expr_op");
+ switch (isl_ast_expr_get_op_type(Expr)) {
+ case isl_ast_op_error:
+ case isl_ast_op_cond:
+ case isl_ast_op_call:
+ case isl_ast_op_member:
+ llvm_unreachable("Unsupported isl ast expression");
+ case isl_ast_op_access:
+ return createOpAccess(Expr);
+ case isl_ast_op_max:
+ case isl_ast_op_min:
+ return createOpNAry(Expr);
+ case isl_ast_op_add:
+ case isl_ast_op_sub:
+ case isl_ast_op_mul:
+ case isl_ast_op_div:
+ case isl_ast_op_fdiv_q: // Round towards -infty
+ case isl_ast_op_pdiv_q: // Dividend is non-negative
+ case isl_ast_op_pdiv_r: // Dividend is non-negative
+ case isl_ast_op_zdiv_r: // Result only compared against zero
+ return createOpBin(Expr);
+ case isl_ast_op_minus:
+ return createOpUnary(Expr);
+ case isl_ast_op_select:
+ return createOpSelect(Expr);
+ case isl_ast_op_and:
+ case isl_ast_op_or:
+ return createOpBoolean(Expr);
+ case isl_ast_op_and_then:
+ case isl_ast_op_or_else:
+ return createOpBooleanConditional(Expr);
+ case isl_ast_op_eq:
+ case isl_ast_op_le:
+ case isl_ast_op_lt:
+ case isl_ast_op_ge:
+ case isl_ast_op_gt:
+ return createOpICmp(Expr);
+ case isl_ast_op_address_of:
+ return createOpAddressOf(Expr);
+ }
+
+ llvm_unreachable("Unsupported isl_ast_expr_op kind.");
+}
+
+Value *IslExprBuilder::createOpAddressOf(__isl_take isl_ast_expr *Expr) {
+ assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op &&
+ "Expected an isl_ast_expr_op expression.");
+ assert(isl_ast_expr_get_op_n_arg(Expr) == 1 && "Address of should be unary.");
+
+ isl_ast_expr *Op = isl_ast_expr_get_op_arg(Expr, 0);
+ assert(isl_ast_expr_get_type(Op) == isl_ast_expr_op &&
+ "Expected address of operator to be an isl_ast_expr_op expression.");
+ assert(isl_ast_expr_get_op_type(Op) == isl_ast_op_access &&
+ "Expected address of operator to be an access expression.");
+
+ Value *V = createAccessAddress(Op).first;
+
+ isl_ast_expr_free(Expr);
+
+ return V;
+}
+
+Value *IslExprBuilder::createId(__isl_take isl_ast_expr *Expr) {
+ assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_id &&
+ "Expression not of type isl_ast_expr_ident");
+
+ isl_id *Id;
+ Value *V;
+
+ Id = isl_ast_expr_get_id(Expr);
+
+ assert(IDToValue.count(Id) && "Identifier not found");
+
+ V = IDToValue[Id];
+ if (!V)
+ V = UndefValue::get(getType(Expr));
+
+ if (V->getType()->isPointerTy())
+ V = Builder.CreatePtrToInt(V, Builder.getIntNTy(DL.getPointerSizeInBits()));
+
+ assert(V && "Unknown parameter id found");
+
+ isl_id_free(Id);
+ isl_ast_expr_free(Expr);
+
+ return V;
+}
+
+IntegerType *IslExprBuilder::getType(__isl_keep isl_ast_expr *Expr) {
+ // XXX: We assume i64 is large enough. This is often true, but in general
+ // incorrect. Also, on 32bit architectures, it would be beneficial to
+ // use a smaller type. We can and should directly derive this information
+ // during code generation.
+ return IntegerType::get(Builder.getContext(), 64);
+}
+
+Value *IslExprBuilder::createInt(__isl_take isl_ast_expr *Expr) {
+ assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_int &&
+ "Expression not of type isl_ast_expr_int");
+ isl_val *Val;
+ Value *V;
+ APInt APValue;
+ IntegerType *T;
+
+ Val = isl_ast_expr_get_val(Expr);
+ APValue = APIntFromVal(Val);
+
+ auto BitWidth = APValue.getBitWidth();
+ if (BitWidth <= 64)
+ T = getType(Expr);
+ else
+ T = Builder.getIntNTy(BitWidth);
+
+ APValue = APValue.sextOrSelf(T->getBitWidth());
+ V = ConstantInt::get(T, APValue);
+
+ isl_ast_expr_free(Expr);
+ return V;
+}
+
+Value *IslExprBuilder::create(__isl_take isl_ast_expr *Expr) {
+ switch (isl_ast_expr_get_type(Expr)) {
+ case isl_ast_expr_error:
+ llvm_unreachable("Code generation error");
+ case isl_ast_expr_op:
+ return createOp(Expr);
+ case isl_ast_expr_id:
+ return createId(Expr);
+ case isl_ast_expr_int:
+ return createInt(Expr);
+ }
+
+ llvm_unreachable("Unexpected enum value");
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/IslNodeBuilder.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/IslNodeBuilder.cpp
new file mode 100644
index 00000000000..77c6cd4cadb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/IslNodeBuilder.cpp
@@ -0,0 +1,1559 @@
+//===- IslNodeBuilder.cpp - Translate an isl AST into a LLVM-IR AST -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the IslNodeBuilder, a class to translate an isl AST into
+// a LLVM-IR AST.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/IslNodeBuilder.h"
+#include "polly/CodeGen/BlockGenerators.h"
+#include "polly/CodeGen/CodeGeneration.h"
+#include "polly/CodeGen/IslAst.h"
+#include "polly/CodeGen/IslExprBuilder.h"
+#include "polly/CodeGen/LoopGeneratorsGOMP.h"
+#include "polly/CodeGen/LoopGeneratorsKMP.h"
+#include "polly/CodeGen/RuntimeDebugBuilder.h"
+#include "polly/Options.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/ISLTools.h"
+#include "polly/Support/SCEVValidator.h"
+#include "polly/Support/ScopHelper.h"
+#include "polly/Support/VirtualInstruction.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Constant.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "isl/aff.h"
+#include "isl/aff_type.h"
+#include "isl/ast.h"
+#include "isl/ast_build.h"
+#include "isl/isl-noexceptions.h"
+#include "isl/map.h"
+#include "isl/set.h"
+#include "isl/union_map.h"
+#include "isl/union_set.h"
+#include "isl/val.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <utility>
+#include <vector>
+
+using namespace llvm;
+using namespace polly;
+
+#define DEBUG_TYPE "polly-codegen"
+
+STATISTIC(VersionedScops, "Number of SCoPs that required versioning.");
+
+STATISTIC(SequentialLoops, "Number of generated sequential for-loops");
+STATISTIC(ParallelLoops, "Number of generated parallel for-loops");
+STATISTIC(VectorLoops, "Number of generated vector for-loops");
+STATISTIC(IfConditions, "Number of generated if-conditions");
+
+/// OpenMP backend options
+enum class OpenMPBackend { GNU, LLVM };
+
+static cl::opt<bool> PollyGenerateRTCPrint(
+ "polly-codegen-emit-rtc-print",
+ cl::desc("Emit code that prints the runtime check result dynamically."),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+// If this option is set we always use the isl AST generator to regenerate
+// memory accesses. Without this option set we regenerate expressions using the
+// original SCEV expressions and only generate new expressions in case the
+// access relation has been changed and consequently must be regenerated.
+static cl::opt<bool> PollyGenerateExpressions(
+ "polly-codegen-generate-expressions",
+ cl::desc("Generate AST expressions for unmodified and modified accesses"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> PollyTargetFirstLevelCacheLineSize(
+ "polly-target-first-level-cache-line-size",
+ cl::desc("The size of the first level cache line size specified in bytes."),
+ cl::Hidden, cl::init(64), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<OpenMPBackend> PollyOmpBackend(
+ "polly-omp-backend", cl::desc("Choose the OpenMP library to use:"),
+ cl::values(clEnumValN(OpenMPBackend::GNU, "GNU", "GNU OpenMP"),
+ clEnumValN(OpenMPBackend::LLVM, "LLVM", "LLVM OpenMP")),
+ cl::Hidden, cl::init(OpenMPBackend::GNU), cl::cat(PollyCategory));
+
+isl::ast_expr IslNodeBuilder::getUpperBound(isl::ast_node_for For,
+ ICmpInst::Predicate &Predicate) {
+ isl::ast_expr Cond = For.cond();
+ isl::ast_expr Iterator = For.iterator();
+ assert(isl_ast_expr_get_type(Cond.get()) == isl_ast_expr_op &&
+ "conditional expression is not an atomic upper bound");
+
+ isl_ast_op_type OpType = isl_ast_expr_get_op_type(Cond.get());
+
+ switch (OpType) {
+ case isl_ast_op_le:
+ Predicate = ICmpInst::ICMP_SLE;
+ break;
+ case isl_ast_op_lt:
+ Predicate = ICmpInst::ICMP_SLT;
+ break;
+ default:
+ llvm_unreachable("Unexpected comparison type in loop condition");
+ }
+
+ isl::ast_expr Arg0 = Cond.get_op_arg(0);
+
+ assert(isl_ast_expr_get_type(Arg0.get()) == isl_ast_expr_id &&
+ "conditional expression is not an atomic upper bound");
+
+ isl::id UBID = Arg0.get_id();
+
+ assert(isl_ast_expr_get_type(Iterator.get()) == isl_ast_expr_id &&
+ "Could not get the iterator");
+
+ isl::id IteratorID = Iterator.get_id();
+
+ assert(UBID.get() == IteratorID.get() &&
+ "conditional expression is not an atomic upper bound");
+
+ return Cond.get_op_arg(1);
+}
+
+int IslNodeBuilder::getNumberOfIterations(isl::ast_node_for For) {
+ assert(isl_ast_node_get_type(For.get()) == isl_ast_node_for);
+ isl::ast_node Body = For.body();
+
+ // First, check if we can actually handle this code.
+ switch (isl_ast_node_get_type(Body.get())) {
+ case isl_ast_node_user:
+ break;
+ case isl_ast_node_block: {
+ isl::ast_node_block BodyBlock = Body.as<isl::ast_node_block>();
+ isl::ast_node_list List = BodyBlock.children();
+ for (isl::ast_node Node : List) {
+ isl_ast_node_type NodeType = isl_ast_node_get_type(Node.get());
+ if (NodeType != isl_ast_node_user)
+ return -1;
+ }
+ break;
+ }
+ default:
+ return -1;
+ }
+
+ isl::ast_expr Init = For.init();
+ if (!Init.isa<isl::ast_expr_int>() || !Init.val().is_zero())
+ return -1;
+ isl::ast_expr Inc = For.inc();
+ if (!Inc.isa<isl::ast_expr_int>() || !Inc.val().is_one())
+ return -1;
+ CmpInst::Predicate Predicate;
+ isl::ast_expr UB = getUpperBound(For, Predicate);
+ if (!UB.isa<isl::ast_expr_int>())
+ return -1;
+ isl::val UpVal = UB.get_val();
+ int NumberIterations = UpVal.get_num_si();
+ if (NumberIterations < 0)
+ return -1;
+ if (Predicate == CmpInst::ICMP_SLT)
+ return NumberIterations;
+ else
+ return NumberIterations + 1;
+}
+
+static void findReferencesByUse(Value *SrcVal, ScopStmt *UserStmt,
+ Loop *UserScope, const ValueMapT &GlobalMap,
+ SetVector<Value *> &Values,
+ SetVector<const SCEV *> &SCEVs) {
+ VirtualUse VUse = VirtualUse::create(UserStmt, UserScope, SrcVal, true);
+ switch (VUse.getKind()) {
+ case VirtualUse::Constant:
+ // When accelerator-offloading, GlobalValue is a host address whose content
+ // must still be transferred to the GPU.
+ if (isa<GlobalValue>(SrcVal))
+ Values.insert(SrcVal);
+ break;
+
+ case VirtualUse::Synthesizable:
+ SCEVs.insert(VUse.getScevExpr());
+ return;
+
+ case VirtualUse::Block:
+ case VirtualUse::ReadOnly:
+ case VirtualUse::Hoisted:
+ case VirtualUse::Intra:
+ case VirtualUse::Inter:
+ break;
+ }
+
+ if (Value *NewVal = GlobalMap.lookup(SrcVal))
+ Values.insert(NewVal);
+}
+
+static void findReferencesInInst(Instruction *Inst, ScopStmt *UserStmt,
+ Loop *UserScope, const ValueMapT &GlobalMap,
+ SetVector<Value *> &Values,
+ SetVector<const SCEV *> &SCEVs) {
+ for (Use &U : Inst->operands())
+ findReferencesByUse(U.get(), UserStmt, UserScope, GlobalMap, Values, SCEVs);
+}
+
+static void findReferencesInStmt(ScopStmt *Stmt, SetVector<Value *> &Values,
+ ValueMapT &GlobalMap,
+ SetVector<const SCEV *> &SCEVs) {
+ LoopInfo *LI = Stmt->getParent()->getLI();
+
+ BasicBlock *BB = Stmt->getBasicBlock();
+ Loop *Scope = LI->getLoopFor(BB);
+ for (Instruction *Inst : Stmt->getInstructions())
+ findReferencesInInst(Inst, Stmt, Scope, GlobalMap, Values, SCEVs);
+
+ if (Stmt->isRegionStmt()) {
+ for (BasicBlock *BB : Stmt->getRegion()->blocks()) {
+ Loop *Scope = LI->getLoopFor(BB);
+ for (Instruction &Inst : *BB)
+ findReferencesInInst(&Inst, Stmt, Scope, GlobalMap, Values, SCEVs);
+ }
+ }
+}
+
+void polly::addReferencesFromStmt(ScopStmt *Stmt, void *UserPtr,
+ bool CreateScalarRefs) {
+ auto &References = *static_cast<struct SubtreeReferences *>(UserPtr);
+
+ findReferencesInStmt(Stmt, References.Values, References.GlobalMap,
+ References.SCEVs);
+
+ for (auto &Access : *Stmt) {
+ if (References.ParamSpace) {
+ isl::space ParamSpace = Access->getLatestAccessRelation().get_space();
+ (*References.ParamSpace) =
+ References.ParamSpace->align_params(ParamSpace);
+ }
+
+ if (Access->isLatestArrayKind()) {
+ auto *BasePtr = Access->getLatestScopArrayInfo()->getBasePtr();
+ if (Instruction *OpInst = dyn_cast<Instruction>(BasePtr))
+ if (Stmt->getParent()->contains(OpInst))
+ continue;
+
+ References.Values.insert(BasePtr);
+ continue;
+ }
+
+ if (CreateScalarRefs)
+ References.Values.insert(References.BlockGen.getOrCreateAlloca(*Access));
+ }
+}
+
+/// Extract the out-of-scop values and SCEVs referenced from a set describing
+/// a ScopStmt.
+///
+/// This includes the SCEVUnknowns referenced by the SCEVs used in the
+/// statement and the base pointers of the memory accesses. For scalar
+/// statements we force the generation of alloca memory locations and list
+/// these locations in the set of out-of-scop values as well.
+///
+/// @param Set A set which references the ScopStmt we are interested in.
+/// @param UserPtr A void pointer that can be casted to a SubtreeReferences
+/// structure.
+static void addReferencesFromStmtSet(isl::set Set,
+ struct SubtreeReferences *UserPtr) {
+ isl::id Id = Set.get_tuple_id();
+ auto *Stmt = static_cast<ScopStmt *>(Id.get_user());
+ addReferencesFromStmt(Stmt, UserPtr);
+}
+
+/// Extract the out-of-scop values and SCEVs referenced from a union set
+/// referencing multiple ScopStmts.
+///
+/// This includes the SCEVUnknowns referenced by the SCEVs used in the
+/// statement and the base pointers of the memory accesses. For scalar
+/// statements we force the generation of alloca memory locations and list
+/// these locations in the set of out-of-scop values as well.
+///
+/// @param USet A union set referencing the ScopStmts we are interested
+/// in.
+/// @param References The SubtreeReferences data structure through which
+/// results are returned and further information is
+/// provided.
+static void
+addReferencesFromStmtUnionSet(isl::union_set USet,
+ struct SubtreeReferences &References) {
+
+ for (isl::set Set : USet.get_set_list())
+ addReferencesFromStmtSet(Set, &References);
+}
+
+isl::union_map
+IslNodeBuilder::getScheduleForAstNode(const isl::ast_node &Node) {
+ return IslAstInfo::getSchedule(Node);
+}
+
+void IslNodeBuilder::getReferencesInSubtree(const isl::ast_node &For,
+ SetVector<Value *> &Values,
+ SetVector<const Loop *> &Loops) {
+ SetVector<const SCEV *> SCEVs;
+ struct SubtreeReferences References = {
+ LI, SE, S, ValueMap, Values, SCEVs, getBlockGenerator(), nullptr};
+
+ for (const auto &I : IDToValue)
+ Values.insert(I.second);
+
+ // NOTE: this is populated in IslNodeBuilder::addParameters
+ for (const auto &I : OutsideLoopIterations)
+ Values.insert(cast<SCEVUnknown>(I.second)->getValue());
+
+ isl::union_set Schedule = getScheduleForAstNode(For).domain();
+ addReferencesFromStmtUnionSet(Schedule, References);
+
+ for (const SCEV *Expr : SCEVs) {
+ findValues(Expr, SE, Values);
+ findLoops(Expr, Loops);
+ }
+
+ Values.remove_if([](const Value *V) { return isa<GlobalValue>(V); });
+
+ /// Note: Code generation of induction variables of loops outside Scops
+ ///
+ /// Remove loops that contain the scop or that are part of the scop, as they
+ /// are considered local. This leaves only loops that are before the scop, but
+ /// do not contain the scop itself.
+ /// We ignore loops perfectly contained in the Scop because these are already
+ /// generated at `IslNodeBuilder::addParameters`. These `Loops` are loops
+ /// whose induction variables are referred to by the Scop, but the Scop is not
+ /// fully contained in these Loops. Since there can be many of these,
+ /// we choose to codegen these on-demand.
+ /// @see IslNodeBuilder::materializeNonScopLoopInductionVariable.
+ Loops.remove_if([this](const Loop *L) {
+ return S.contains(L) || L->contains(S.getEntry());
+ });
+
+ // Contains Values that may need to be replaced with other values
+ // due to replacements from the ValueMap. We should make sure
+ // that we return correctly remapped values.
+ // NOTE: this code path is tested by:
+ // 1. test/Isl/CodeGen/OpenMP/single_loop_with_loop_invariant_baseptr.ll
+ // 2. test/Isl/CodeGen/OpenMP/loop-body-references-outer-values-3.ll
+ SetVector<Value *> ReplacedValues;
+ for (Value *V : Values) {
+ ReplacedValues.insert(getLatestValue(V));
+ }
+ Values = ReplacedValues;
+}
+
+void IslNodeBuilder::updateValues(ValueMapT &NewValues) {
+ SmallPtrSet<Value *, 5> Inserted;
+
+ for (const auto &I : IDToValue) {
+ IDToValue[I.first] = NewValues[I.second];
+ Inserted.insert(I.second);
+ }
+
+ for (const auto &I : NewValues) {
+ if (Inserted.count(I.first))
+ continue;
+
+ ValueMap[I.first] = I.second;
+ }
+}
+
+Value *IslNodeBuilder::getLatestValue(Value *Original) const {
+ auto It = ValueMap.find(Original);
+ if (It == ValueMap.end())
+ return Original;
+ return It->second;
+}
+
+void IslNodeBuilder::createUserVector(__isl_take isl_ast_node *User,
+ std::vector<Value *> &IVS,
+ __isl_take isl_id *IteratorID,
+ __isl_take isl_union_map *Schedule) {
+ isl_ast_expr *Expr = isl_ast_node_user_get_expr(User);
+ isl_ast_expr *StmtExpr = isl_ast_expr_get_op_arg(Expr, 0);
+ isl_id *Id = isl_ast_expr_get_id(StmtExpr);
+ isl_ast_expr_free(StmtExpr);
+ ScopStmt *Stmt = (ScopStmt *)isl_id_get_user(Id);
+ std::vector<LoopToScevMapT> VLTS(IVS.size());
+
+ isl_union_set *Domain = isl_union_set_from_set(Stmt->getDomain().release());
+ Schedule = isl_union_map_intersect_domain(Schedule, Domain);
+ isl_map *S = isl_map_from_union_map(Schedule);
+
+ auto *NewAccesses = createNewAccesses(Stmt, User);
+ createSubstitutionsVector(Expr, Stmt, VLTS, IVS, IteratorID);
+ VectorBlockGenerator::generate(BlockGen, *Stmt, VLTS, S, NewAccesses);
+ isl_id_to_ast_expr_free(NewAccesses);
+ isl_map_free(S);
+ isl_id_free(Id);
+ isl_ast_node_free(User);
+}
+
+void IslNodeBuilder::createMark(__isl_take isl_ast_node *Node) {
+ auto *Id = isl_ast_node_mark_get_id(Node);
+ auto Child = isl_ast_node_mark_get_node(Node);
+ isl_ast_node_free(Node);
+ // If a child node of a 'SIMD mark' is a loop that has a single iteration,
+ // it will be optimized away and we should skip it.
+ if (strcmp(isl_id_get_name(Id), "SIMD") == 0 &&
+ isl_ast_node_get_type(Child) == isl_ast_node_for) {
+ bool Vector = PollyVectorizerChoice == VECTORIZER_POLLY;
+ int VectorWidth =
+ getNumberOfIterations(isl::manage_copy(Child).as<isl::ast_node_for>());
+ if (Vector && 1 < VectorWidth && VectorWidth <= 16)
+ createForVector(Child, VectorWidth);
+ else
+ createForSequential(isl::manage(Child).as<isl::ast_node_for>(), true);
+ isl_id_free(Id);
+ return;
+ }
+
+ BandAttr *ChildLoopAttr = getLoopAttr(isl::manage_copy(Id));
+ BandAttr *AncestorLoopAttr;
+ if (ChildLoopAttr) {
+ // Save current LoopAttr environment to restore again when leaving this
+ // subtree. This means there was no loop between the ancestor LoopAttr and
+ // this mark, i.e. the ancestor LoopAttr did not directly mark a loop. This
+ // can happen e.g. if the AST build peeled or unrolled the loop.
+ AncestorLoopAttr = Annotator.getStagingAttrEnv();
+
+ Annotator.getStagingAttrEnv() = ChildLoopAttr;
+ }
+
+ create(Child);
+
+ if (ChildLoopAttr) {
+ assert(Annotator.getStagingAttrEnv() == ChildLoopAttr &&
+ "Nest must not overwrite loop attr environment");
+ Annotator.getStagingAttrEnv() = AncestorLoopAttr;
+ }
+
+ isl_id_free(Id);
+}
+
+void IslNodeBuilder::createForVector(__isl_take isl_ast_node *For,
+ int VectorWidth) {
+ isl_ast_node *Body = isl_ast_node_for_get_body(For);
+ isl_ast_expr *Init = isl_ast_node_for_get_init(For);
+ isl_ast_expr *Inc = isl_ast_node_for_get_inc(For);
+ isl_ast_expr *Iterator = isl_ast_node_for_get_iterator(For);
+ isl_id *IteratorID = isl_ast_expr_get_id(Iterator);
+
+ Value *ValueLB = ExprBuilder.create(Init);
+ Value *ValueInc = ExprBuilder.create(Inc);
+
+ Type *MaxType = ExprBuilder.getType(Iterator);
+ MaxType = ExprBuilder.getWidestType(MaxType, ValueLB->getType());
+ MaxType = ExprBuilder.getWidestType(MaxType, ValueInc->getType());
+
+ if (MaxType != ValueLB->getType())
+ ValueLB = Builder.CreateSExt(ValueLB, MaxType);
+ if (MaxType != ValueInc->getType())
+ ValueInc = Builder.CreateSExt(ValueInc, MaxType);
+
+ std::vector<Value *> IVS(VectorWidth);
+ IVS[0] = ValueLB;
+
+ for (int i = 1; i < VectorWidth; i++)
+ IVS[i] = Builder.CreateAdd(IVS[i - 1], ValueInc, "p_vector_iv");
+
+ isl::union_map Schedule = getScheduleForAstNode(isl::manage_copy(For));
+ assert(!Schedule.is_null() &&
+ "For statement annotation does not contain its schedule");
+
+ IDToValue[IteratorID] = ValueLB;
+
+ switch (isl_ast_node_get_type(Body)) {
+ case isl_ast_node_user:
+ createUserVector(Body, IVS, isl_id_copy(IteratorID), Schedule.copy());
+ break;
+ case isl_ast_node_block: {
+ isl_ast_node_list *List = isl_ast_node_block_get_children(Body);
+
+ for (int i = 0; i < isl_ast_node_list_n_ast_node(List); ++i)
+ createUserVector(isl_ast_node_list_get_ast_node(List, i), IVS,
+ isl_id_copy(IteratorID), Schedule.copy());
+
+ isl_ast_node_free(Body);
+ isl_ast_node_list_free(List);
+ break;
+ }
+ default:
+ isl_ast_node_dump(Body);
+ llvm_unreachable("Unhandled isl_ast_node in vectorizer");
+ }
+
+ IDToValue.erase(IDToValue.find(IteratorID));
+ isl_id_free(IteratorID);
+
+ isl_ast_node_free(For);
+ isl_ast_expr_free(Iterator);
+
+ VectorLoops++;
+}
+
+/// Restore the initial ordering of dimensions of the band node
+///
+/// In case the band node represents all the dimensions of the iteration
+/// domain, recreate the band node to restore the initial ordering of the
+/// dimensions.
+///
+/// @param Node The band node to be modified.
+/// @return The modified schedule node.
+static bool IsLoopVectorizerDisabled(isl::ast_node_for Node) {
+ assert(isl_ast_node_get_type(Node.get()) == isl_ast_node_for);
+ isl::ast_node Body = Node.body();
+ if (isl_ast_node_get_type(Body.get()) != isl_ast_node_mark)
+ return false;
+
+ isl::ast_node_mark BodyMark = Body.as<isl::ast_node_mark>();
+ auto Id = BodyMark.id();
+ if (strcmp(Id.get_name().c_str(), "Loop Vectorizer Disabled") == 0)
+ return true;
+ return false;
+}
+
+void IslNodeBuilder::createForSequential(isl::ast_node_for For,
+ bool MarkParallel) {
+ Value *ValueLB, *ValueUB, *ValueInc;
+ Type *MaxType;
+ BasicBlock *ExitBlock;
+ Value *IV;
+ CmpInst::Predicate Predicate;
+
+ bool LoopVectorizerDisabled = IsLoopVectorizerDisabled(For);
+
+ isl::ast_node Body = For.body();
+
+ // isl_ast_node_for_is_degenerate(For)
+ //
+ // TODO: For degenerated loops we could generate a plain assignment.
+ // However, for now we just reuse the logic for normal loops, which will
+ // create a loop with a single iteration.
+
+ isl::ast_expr Init = For.init();
+ isl::ast_expr Inc = For.inc();
+ isl::ast_expr Iterator = For.iterator();
+ isl::id IteratorID = Iterator.get_id();
+ isl::ast_expr UB = getUpperBound(For, Predicate);
+
+ ValueLB = ExprBuilder.create(Init.release());
+ ValueUB = ExprBuilder.create(UB.release());
+ ValueInc = ExprBuilder.create(Inc.release());
+
+ MaxType = ExprBuilder.getType(Iterator.get());
+ MaxType = ExprBuilder.getWidestType(MaxType, ValueLB->getType());
+ MaxType = ExprBuilder.getWidestType(MaxType, ValueUB->getType());
+ MaxType = ExprBuilder.getWidestType(MaxType, ValueInc->getType());
+
+ if (MaxType != ValueLB->getType())
+ ValueLB = Builder.CreateSExt(ValueLB, MaxType);
+ if (MaxType != ValueUB->getType())
+ ValueUB = Builder.CreateSExt(ValueUB, MaxType);
+ if (MaxType != ValueInc->getType())
+ ValueInc = Builder.CreateSExt(ValueInc, MaxType);
+
+ // If we can show that LB <Predicate> UB holds at least once, we can
+ // omit the GuardBB in front of the loop.
+ bool UseGuardBB =
+ !SE.isKnownPredicate(Predicate, SE.getSCEV(ValueLB), SE.getSCEV(ValueUB));
+ IV = createLoop(ValueLB, ValueUB, ValueInc, Builder, LI, DT, ExitBlock,
+ Predicate, &Annotator, MarkParallel, UseGuardBB,
+ LoopVectorizerDisabled);
+ IDToValue[IteratorID.get()] = IV;
+
+ create(Body.release());
+
+ Annotator.popLoop(MarkParallel);
+
+ IDToValue.erase(IDToValue.find(IteratorID.get()));
+
+ Builder.SetInsertPoint(&ExitBlock->front());
+
+ SequentialLoops++;
+}
+
+/// Remove the BBs contained in a (sub)function from the dominator tree.
+///
+/// This function removes the basic blocks that are part of a subfunction from
+/// the dominator tree. Specifically, when generating code it may happen that at
+/// some point the code generation continues in a new sub-function (e.g., when
+/// generating OpenMP code). The basic blocks that are created in this
+/// sub-function are then still part of the dominator tree of the original
+/// function, such that the dominator tree reaches over function boundaries.
+/// This is not only incorrect, but also causes crashes. This function now
+/// removes from the dominator tree all basic blocks that are dominated (and
+/// consequently reachable) from the entry block of this (sub)function.
+///
+/// FIXME: A LLVM (function or region) pass should not touch anything outside of
+/// the function/region it runs on. Hence, the pure need for this function shows
+/// that we do not comply to this rule. At the moment, this does not cause any
+/// issues, but we should be aware that such issues may appear. Unfortunately
+/// the current LLVM pass infrastructure does not allow to make Polly a module
+/// or call-graph pass to solve this issue, as such a pass would not have access
+/// to the per-function analyses passes needed by Polly. A future pass manager
+/// infrastructure is supposed to enable such kind of access possibly allowing
+/// us to create a cleaner solution here.
+///
+/// FIXME: Instead of adding the dominance information and then dropping it
+/// later on, we should try to just not add it in the first place. This requires
+/// some careful testing to make sure this does not break in interaction with
+/// the SCEVBuilder and SplitBlock which may rely on the dominator tree or
+/// which may try to update it.
+///
+/// @param F The function which contains the BBs to removed.
+/// @param DT The dominator tree from which to remove the BBs.
+static void removeSubFuncFromDomTree(Function *F, DominatorTree &DT) {
+ DomTreeNode *N = DT.getNode(&F->getEntryBlock());
+ std::vector<BasicBlock *> Nodes;
+
+ // We can only remove an element from the dominator tree, if all its children
+ // have been removed. To ensure this we obtain the list of nodes to remove
+ // using a post-order tree traversal.
+ for (po_iterator<DomTreeNode *> I = po_begin(N), E = po_end(N); I != E; ++I)
+ Nodes.push_back(I->getBlock());
+
+ for (BasicBlock *BB : Nodes)
+ DT.eraseNode(BB);
+}
+
+void IslNodeBuilder::createForParallel(__isl_take isl_ast_node *For) {
+ isl_ast_node *Body;
+ isl_ast_expr *Init, *Inc, *Iterator, *UB;
+ isl_id *IteratorID;
+ Value *ValueLB, *ValueUB, *ValueInc;
+ Type *MaxType;
+ Value *IV;
+ CmpInst::Predicate Predicate;
+
+ // The preamble of parallel code interacts different than normal code with
+ // e.g., scalar initialization. Therefore, we ensure the parallel code is
+ // separated from the last basic block.
+ BasicBlock *ParBB = SplitBlock(Builder.GetInsertBlock(),
+ &*Builder.GetInsertPoint(), &DT, &LI);
+ ParBB->setName("polly.parallel.for");
+ Builder.SetInsertPoint(&ParBB->front());
+
+ Body = isl_ast_node_for_get_body(For);
+ Init = isl_ast_node_for_get_init(For);
+ Inc = isl_ast_node_for_get_inc(For);
+ Iterator = isl_ast_node_for_get_iterator(For);
+ IteratorID = isl_ast_expr_get_id(Iterator);
+ UB = getUpperBound(isl::manage_copy(For).as<isl::ast_node_for>(), Predicate)
+ .release();
+
+ ValueLB = ExprBuilder.create(Init);
+ ValueUB = ExprBuilder.create(UB);
+ ValueInc = ExprBuilder.create(Inc);
+
+ // OpenMP always uses SLE. In case the isl generated AST uses a SLT
+ // expression, we need to adjust the loop bound by one.
+ if (Predicate == CmpInst::ICMP_SLT)
+ ValueUB = Builder.CreateAdd(
+ ValueUB, Builder.CreateSExt(Builder.getTrue(), ValueUB->getType()));
+
+ MaxType = ExprBuilder.getType(Iterator);
+ MaxType = ExprBuilder.getWidestType(MaxType, ValueLB->getType());
+ MaxType = ExprBuilder.getWidestType(MaxType, ValueUB->getType());
+ MaxType = ExprBuilder.getWidestType(MaxType, ValueInc->getType());
+
+ if (MaxType != ValueLB->getType())
+ ValueLB = Builder.CreateSExt(ValueLB, MaxType);
+ if (MaxType != ValueUB->getType())
+ ValueUB = Builder.CreateSExt(ValueUB, MaxType);
+ if (MaxType != ValueInc->getType())
+ ValueInc = Builder.CreateSExt(ValueInc, MaxType);
+
+ BasicBlock::iterator LoopBody;
+
+ SetVector<Value *> SubtreeValues;
+ SetVector<const Loop *> Loops;
+
+ getReferencesInSubtree(isl::manage_copy(For), SubtreeValues, Loops);
+
+ // Create for all loops we depend on values that contain the current loop
+ // iteration. These values are necessary to generate code for SCEVs that
+ // depend on such loops. As a result we need to pass them to the subfunction.
+ // See [Code generation of induction variables of loops outside Scops]
+ for (const Loop *L : Loops) {
+ Value *LoopInductionVar = materializeNonScopLoopInductionVariable(L);
+ SubtreeValues.insert(LoopInductionVar);
+ }
+
+ ValueMapT NewValues;
+
+ std::unique_ptr<ParallelLoopGenerator> ParallelLoopGenPtr;
+
+ switch (PollyOmpBackend) {
+ case OpenMPBackend::GNU:
+ ParallelLoopGenPtr.reset(
+ new ParallelLoopGeneratorGOMP(Builder, LI, DT, DL));
+ break;
+ case OpenMPBackend::LLVM:
+ ParallelLoopGenPtr.reset(new ParallelLoopGeneratorKMP(Builder, LI, DT, DL));
+ break;
+ }
+
+ IV = ParallelLoopGenPtr->createParallelLoop(
+ ValueLB, ValueUB, ValueInc, SubtreeValues, NewValues, &LoopBody);
+ BasicBlock::iterator AfterLoop = Builder.GetInsertPoint();
+ Builder.SetInsertPoint(&*LoopBody);
+
+ // Remember the parallel subfunction
+ ParallelSubfunctions.push_back(LoopBody->getFunction());
+
+ // Save the current values.
+ auto ValueMapCopy = ValueMap;
+ IslExprBuilder::IDToValueTy IDToValueCopy = IDToValue;
+
+ updateValues(NewValues);
+ IDToValue[IteratorID] = IV;
+
+ ValueMapT NewValuesReverse;
+
+ for (auto P : NewValues)
+ NewValuesReverse[P.second] = P.first;
+
+ Annotator.addAlternativeAliasBases(NewValuesReverse);
+
+ create(Body);
+
+ Annotator.resetAlternativeAliasBases();
+ // Restore the original values.
+ ValueMap = ValueMapCopy;
+ IDToValue = IDToValueCopy;
+
+ Builder.SetInsertPoint(&*AfterLoop);
+ removeSubFuncFromDomTree((*LoopBody).getParent()->getParent(), DT);
+
+ for (const Loop *L : Loops)
+ OutsideLoopIterations.erase(L);
+
+ isl_ast_node_free(For);
+ isl_ast_expr_free(Iterator);
+ isl_id_free(IteratorID);
+
+ ParallelLoops++;
+}
+
+/// Return whether any of @p Node's statements contain partial accesses.
+///
+/// Partial accesses are not supported by Polly's vector code generator.
+static bool hasPartialAccesses(__isl_take isl_ast_node *Node) {
+ return isl_ast_node_foreach_descendant_top_down(
+ Node,
+ [](isl_ast_node *Node, void *User) -> isl_bool {
+ if (isl_ast_node_get_type(Node) != isl_ast_node_user)
+ return isl_bool_true;
+
+ isl::ast_expr Expr =
+ isl::manage(isl_ast_node_user_get_expr(Node));
+ isl::ast_expr StmtExpr = Expr.get_op_arg(0);
+ isl::id Id = StmtExpr.get_id();
+
+ ScopStmt *Stmt =
+ static_cast<ScopStmt *>(isl_id_get_user(Id.get()));
+ isl::set StmtDom = Stmt->getDomain();
+ for (auto *MA : *Stmt) {
+ if (MA->isLatestPartialAccess())
+ return isl_bool_error;
+ }
+ return isl_bool_true;
+ },
+ nullptr) == isl_stat_error;
+}
+
+void IslNodeBuilder::createFor(__isl_take isl_ast_node *For) {
+ bool Vector = PollyVectorizerChoice == VECTORIZER_POLLY;
+
+ if (Vector && IslAstInfo::isInnermostParallel(isl::manage_copy(For)) &&
+ !IslAstInfo::isReductionParallel(isl::manage_copy(For))) {
+ int VectorWidth =
+ getNumberOfIterations(isl::manage_copy(For).as<isl::ast_node_for>());
+ if (1 < VectorWidth && VectorWidth <= 16 && !hasPartialAccesses(For)) {
+ createForVector(For, VectorWidth);
+ return;
+ }
+ }
+
+ if (IslAstInfo::isExecutedInParallel(isl::manage_copy(For))) {
+ createForParallel(For);
+ return;
+ }
+ bool Parallel = (IslAstInfo::isParallel(isl::manage_copy(For)) &&
+ !IslAstInfo::isReductionParallel(isl::manage_copy(For)));
+ createForSequential(isl::manage(For).as<isl::ast_node_for>(), Parallel);
+}
+
+void IslNodeBuilder::createIf(__isl_take isl_ast_node *If) {
+ isl_ast_expr *Cond = isl_ast_node_if_get_cond(If);
+
+ Function *F = Builder.GetInsertBlock()->getParent();
+ LLVMContext &Context = F->getContext();
+
+ BasicBlock *CondBB = SplitBlock(Builder.GetInsertBlock(),
+ &*Builder.GetInsertPoint(), &DT, &LI);
+ CondBB->setName("polly.cond");
+ BasicBlock *MergeBB = SplitBlock(CondBB, &CondBB->front(), &DT, &LI);
+ MergeBB->setName("polly.merge");
+ BasicBlock *ThenBB = BasicBlock::Create(Context, "polly.then", F);
+ BasicBlock *ElseBB = BasicBlock::Create(Context, "polly.else", F);
+
+ DT.addNewBlock(ThenBB, CondBB);
+ DT.addNewBlock(ElseBB, CondBB);
+ DT.changeImmediateDominator(MergeBB, CondBB);
+
+ Loop *L = LI.getLoopFor(CondBB);
+ if (L) {
+ L->addBasicBlockToLoop(ThenBB, LI);
+ L->addBasicBlockToLoop(ElseBB, LI);
+ }
+
+ CondBB->getTerminator()->eraseFromParent();
+
+ Builder.SetInsertPoint(CondBB);
+ Value *Predicate = ExprBuilder.create(Cond);
+ Builder.CreateCondBr(Predicate, ThenBB, ElseBB);
+ Builder.SetInsertPoint(ThenBB);
+ Builder.CreateBr(MergeBB);
+ Builder.SetInsertPoint(ElseBB);
+ Builder.CreateBr(MergeBB);
+ Builder.SetInsertPoint(&ThenBB->front());
+
+ create(isl_ast_node_if_get_then(If));
+
+ Builder.SetInsertPoint(&ElseBB->front());
+
+ if (isl_ast_node_if_has_else(If))
+ create(isl_ast_node_if_get_else(If));
+
+ Builder.SetInsertPoint(&MergeBB->front());
+
+ isl_ast_node_free(If);
+
+ IfConditions++;
+}
+
+__isl_give isl_id_to_ast_expr *
+IslNodeBuilder::createNewAccesses(ScopStmt *Stmt,
+ __isl_keep isl_ast_node *Node) {
+ isl::id_to_ast_expr NewAccesses =
+ isl::id_to_ast_expr::alloc(Stmt->getParent()->getIslCtx(), 0);
+
+ isl::ast_build Build = IslAstInfo::getBuild(isl::manage_copy(Node));
+ assert(!Build.is_null() && "Could not obtain isl_ast_build from user node");
+ Stmt->setAstBuild(Build);
+
+ for (auto *MA : *Stmt) {
+ if (!MA->hasNewAccessRelation()) {
+ if (PollyGenerateExpressions) {
+ if (!MA->isAffine())
+ continue;
+ if (MA->getLatestScopArrayInfo()->getBasePtrOriginSAI())
+ continue;
+
+ auto *BasePtr =
+ dyn_cast<Instruction>(MA->getLatestScopArrayInfo()->getBasePtr());
+ if (BasePtr && Stmt->getParent()->getRegion().contains(BasePtr))
+ continue;
+ } else {
+ continue;
+ }
+ }
+ assert(MA->isAffine() &&
+ "Only affine memory accesses can be code generated");
+
+ isl::union_map Schedule = Build.get_schedule();
+
+#ifndef NDEBUG
+ if (MA->isRead()) {
+ auto Dom = Stmt->getDomain().release();
+ auto SchedDom = isl_set_from_union_set(Schedule.domain().release());
+ auto AccDom = isl_map_domain(MA->getAccessRelation().release());
+ Dom = isl_set_intersect_params(Dom,
+ Stmt->getParent()->getContext().release());
+ SchedDom = isl_set_intersect_params(
+ SchedDom, Stmt->getParent()->getContext().release());
+ assert(isl_set_is_subset(SchedDom, AccDom) &&
+ "Access relation not defined on full schedule domain");
+ assert(isl_set_is_subset(Dom, AccDom) &&
+ "Access relation not defined on full domain");
+ isl_set_free(AccDom);
+ isl_set_free(SchedDom);
+ isl_set_free(Dom);
+ }
+#endif
+
+ isl::pw_multi_aff PWAccRel = MA->applyScheduleToAccessRelation(Schedule);
+
+ // isl cannot generate an index expression for access-nothing accesses.
+ isl::set AccDomain = PWAccRel.domain();
+ isl::set Context = S.getContext();
+ AccDomain = AccDomain.intersect_params(Context);
+ if (AccDomain.is_empty())
+ continue;
+
+ isl::ast_expr AccessExpr = Build.access_from(PWAccRel);
+ NewAccesses = NewAccesses.set(MA->getId(), AccessExpr);
+ }
+
+ return NewAccesses.release();
+}
+
+void IslNodeBuilder::createSubstitutions(__isl_take isl_ast_expr *Expr,
+ ScopStmt *Stmt, LoopToScevMapT &LTS) {
+ assert(isl_ast_expr_get_type(Expr) == isl_ast_expr_op &&
+ "Expression of type 'op' expected");
+ assert(isl_ast_expr_get_op_type(Expr) == isl_ast_op_call &&
+ "Operation of type 'call' expected");
+ for (int i = 0; i < isl_ast_expr_get_op_n_arg(Expr) - 1; ++i) {
+ isl_ast_expr *SubExpr;
+ Value *V;
+
+ SubExpr = isl_ast_expr_get_op_arg(Expr, i + 1);
+ V = ExprBuilder.create(SubExpr);
+ ScalarEvolution *SE = Stmt->getParent()->getSE();
+ LTS[Stmt->getLoopForDimension(i)] = SE->getUnknown(V);
+ }
+
+ isl_ast_expr_free(Expr);
+}
+
+void IslNodeBuilder::createSubstitutionsVector(
+ __isl_take isl_ast_expr *Expr, ScopStmt *Stmt,
+ std::vector<LoopToScevMapT> &VLTS, std::vector<Value *> &IVS,
+ __isl_take isl_id *IteratorID) {
+ int i = 0;
+
+ Value *OldValue = IDToValue[IteratorID];
+ for (Value *IV : IVS) {
+ IDToValue[IteratorID] = IV;
+ createSubstitutions(isl_ast_expr_copy(Expr), Stmt, VLTS[i]);
+ i++;
+ }
+
+ IDToValue[IteratorID] = OldValue;
+ isl_id_free(IteratorID);
+ isl_ast_expr_free(Expr);
+}
+
+void IslNodeBuilder::generateCopyStmt(
+ ScopStmt *Stmt, __isl_keep isl_id_to_ast_expr *NewAccesses) {
+ assert(Stmt->size() == 2);
+ auto ReadAccess = Stmt->begin();
+ auto WriteAccess = ReadAccess++;
+ assert((*ReadAccess)->isRead() && (*WriteAccess)->isMustWrite());
+ assert((*ReadAccess)->getElementType() == (*WriteAccess)->getElementType() &&
+ "Accesses use the same data type");
+ assert((*ReadAccess)->isArrayKind() && (*WriteAccess)->isArrayKind());
+ auto *AccessExpr =
+ isl_id_to_ast_expr_get(NewAccesses, (*ReadAccess)->getId().release());
+ auto *LoadValue = ExprBuilder.create(AccessExpr);
+ AccessExpr =
+ isl_id_to_ast_expr_get(NewAccesses, (*WriteAccess)->getId().release());
+ auto *StoreAddr = ExprBuilder.createAccessAddress(AccessExpr).first;
+ Builder.CreateStore(LoadValue, StoreAddr);
+}
+
+Value *IslNodeBuilder::materializeNonScopLoopInductionVariable(const Loop *L) {
+ assert(OutsideLoopIterations.find(L) == OutsideLoopIterations.end() &&
+ "trying to materialize loop induction variable twice");
+ const SCEV *OuterLIV = SE.getAddRecExpr(SE.getUnknown(Builder.getInt64(0)),
+ SE.getUnknown(Builder.getInt64(1)), L,
+ SCEV::FlagAnyWrap);
+ Value *V = generateSCEV(OuterLIV);
+ OutsideLoopIterations[L] = SE.getUnknown(V);
+ return V;
+}
+
+void IslNodeBuilder::createUser(__isl_take isl_ast_node *User) {
+ LoopToScevMapT LTS;
+ isl_id *Id;
+ ScopStmt *Stmt;
+
+ isl_ast_expr *Expr = isl_ast_node_user_get_expr(User);
+ isl_ast_expr *StmtExpr = isl_ast_expr_get_op_arg(Expr, 0);
+ Id = isl_ast_expr_get_id(StmtExpr);
+ isl_ast_expr_free(StmtExpr);
+
+ LTS.insert(OutsideLoopIterations.begin(), OutsideLoopIterations.end());
+
+ Stmt = (ScopStmt *)isl_id_get_user(Id);
+ auto *NewAccesses = createNewAccesses(Stmt, User);
+ if (Stmt->isCopyStmt()) {
+ generateCopyStmt(Stmt, NewAccesses);
+ isl_ast_expr_free(Expr);
+ } else {
+ createSubstitutions(Expr, Stmt, LTS);
+
+ if (Stmt->isBlockStmt())
+ BlockGen.copyStmt(*Stmt, LTS, NewAccesses);
+ else
+ RegionGen.copyStmt(*Stmt, LTS, NewAccesses);
+ }
+
+ isl_id_to_ast_expr_free(NewAccesses);
+ isl_ast_node_free(User);
+ isl_id_free(Id);
+}
+
+void IslNodeBuilder::createBlock(__isl_take isl_ast_node *Block) {
+ isl_ast_node_list *List = isl_ast_node_block_get_children(Block);
+
+ for (int i = 0; i < isl_ast_node_list_n_ast_node(List); ++i)
+ create(isl_ast_node_list_get_ast_node(List, i));
+
+ isl_ast_node_free(Block);
+ isl_ast_node_list_free(List);
+}
+
+void IslNodeBuilder::create(__isl_take isl_ast_node *Node) {
+ switch (isl_ast_node_get_type(Node)) {
+ case isl_ast_node_error:
+ llvm_unreachable("code generation error");
+ case isl_ast_node_mark:
+ createMark(Node);
+ return;
+ case isl_ast_node_for:
+ createFor(Node);
+ return;
+ case isl_ast_node_if:
+ createIf(Node);
+ return;
+ case isl_ast_node_user:
+ createUser(Node);
+ return;
+ case isl_ast_node_block:
+ createBlock(Node);
+ return;
+ }
+
+ llvm_unreachable("Unknown isl_ast_node type");
+}
+
+bool IslNodeBuilder::materializeValue(isl_id *Id) {
+ // If the Id is already mapped, skip it.
+ if (!IDToValue.count(Id)) {
+ auto *ParamSCEV = (const SCEV *)isl_id_get_user(Id);
+ Value *V = nullptr;
+
+ // Parameters could refer to invariant loads that need to be
+ // preloaded before we can generate code for the parameter. Thus,
+ // check if any value referred to in ParamSCEV is an invariant load
+ // and if so make sure its equivalence class is preloaded.
+ SetVector<Value *> Values;
+ findValues(ParamSCEV, SE, Values);
+ for (auto *Val : Values) {
+ // Check if the value is an instruction in a dead block within the SCoP
+ // and if so do not code generate it.
+ if (auto *Inst = dyn_cast<Instruction>(Val)) {
+ if (S.contains(Inst)) {
+ bool IsDead = true;
+
+ // Check for "undef" loads first, then if there is a statement for
+ // the parent of Inst and lastly if the parent of Inst has an empty
+ // domain. In the first and last case the instruction is dead but if
+ // there is a statement or the domain is not empty Inst is not dead.
+ auto MemInst = MemAccInst::dyn_cast(Inst);
+ auto Address = MemInst ? MemInst.getPointerOperand() : nullptr;
+ if (Address && SE.getUnknown(UndefValue::get(Address->getType())) ==
+ SE.getPointerBase(SE.getSCEV(Address))) {
+ } else if (S.getStmtFor(Inst)) {
+ IsDead = false;
+ } else {
+ auto *Domain = S.getDomainConditions(Inst->getParent()).release();
+ IsDead = isl_set_is_empty(Domain);
+ isl_set_free(Domain);
+ }
+
+ if (IsDead) {
+ V = UndefValue::get(ParamSCEV->getType());
+ break;
+ }
+ }
+ }
+
+ if (auto *IAClass = S.lookupInvariantEquivClass(Val)) {
+ // Check if this invariant access class is empty, hence if we never
+ // actually added a loads instruction to it. In that case it has no
+ // (meaningful) users and we should not try to code generate it.
+ if (IAClass->InvariantAccesses.empty())
+ V = UndefValue::get(ParamSCEV->getType());
+
+ if (!preloadInvariantEquivClass(*IAClass)) {
+ isl_id_free(Id);
+ return false;
+ }
+ }
+ }
+
+ V = V ? V : generateSCEV(ParamSCEV);
+ IDToValue[Id] = V;
+ }
+
+ isl_id_free(Id);
+ return true;
+}
+
+bool IslNodeBuilder::materializeParameters(isl_set *Set) {
+ for (unsigned i = 0, e = isl_set_dim(Set, isl_dim_param); i < e; ++i) {
+ if (!isl_set_involves_dims(Set, isl_dim_param, i, 1))
+ continue;
+ isl_id *Id = isl_set_get_dim_id(Set, isl_dim_param, i);
+ if (!materializeValue(Id))
+ return false;
+ }
+ return true;
+}
+
+bool IslNodeBuilder::materializeParameters() {
+ for (const SCEV *Param : S.parameters()) {
+ isl_id *Id = S.getIdForParam(Param).release();
+ if (!materializeValue(Id))
+ return false;
+ }
+ return true;
+}
+
+Value *IslNodeBuilder::preloadUnconditionally(isl_set *AccessRange,
+ isl_ast_build *Build,
+ Instruction *AccInst) {
+ isl_pw_multi_aff *PWAccRel = isl_pw_multi_aff_from_set(AccessRange);
+ isl_ast_expr *Access =
+ isl_ast_build_access_from_pw_multi_aff(Build, PWAccRel);
+ auto *Address = isl_ast_expr_address_of(Access);
+ auto *AddressValue = ExprBuilder.create(Address);
+ Value *PreloadVal;
+
+ // Correct the type as the SAI might have a different type than the user
+ // expects, especially if the base pointer is a struct.
+ Type *Ty = AccInst->getType();
+
+ auto *Ptr = AddressValue;
+ auto Name = Ptr->getName();
+ auto AS = Ptr->getType()->getPointerAddressSpace();
+ Ptr = Builder.CreatePointerCast(Ptr, Ty->getPointerTo(AS), Name + ".cast");
+ PreloadVal = Builder.CreateLoad(Ty, Ptr, Name + ".load");
+ if (LoadInst *PreloadInst = dyn_cast<LoadInst>(PreloadVal))
+ PreloadInst->setAlignment(cast<LoadInst>(AccInst)->getAlign());
+
+ // TODO: This is only a hot fix for SCoP sequences that use the same load
+ // instruction contained and hoisted by one of the SCoPs.
+ if (SE.isSCEVable(Ty))
+ SE.forgetValue(AccInst);
+
+ return PreloadVal;
+}
+
+Value *IslNodeBuilder::preloadInvariantLoad(const MemoryAccess &MA,
+ isl_set *Domain) {
+ isl_set *AccessRange = isl_map_range(MA.getAddressFunction().release());
+ AccessRange = isl_set_gist_params(AccessRange, S.getContext().release());
+
+ if (!materializeParameters(AccessRange)) {
+ isl_set_free(AccessRange);
+ isl_set_free(Domain);
+ return nullptr;
+ }
+
+ auto *Build =
+ isl_ast_build_from_context(isl_set_universe(S.getParamSpace().release()));
+ isl_set *Universe = isl_set_universe(isl_set_get_space(Domain));
+ bool AlwaysExecuted = isl_set_is_equal(Domain, Universe);
+ isl_set_free(Universe);
+
+ Instruction *AccInst = MA.getAccessInstruction();
+ Type *AccInstTy = AccInst->getType();
+
+ Value *PreloadVal = nullptr;
+ if (AlwaysExecuted) {
+ PreloadVal = preloadUnconditionally(AccessRange, Build, AccInst);
+ isl_ast_build_free(Build);
+ isl_set_free(Domain);
+ return PreloadVal;
+ }
+
+ if (!materializeParameters(Domain)) {
+ isl_ast_build_free(Build);
+ isl_set_free(AccessRange);
+ isl_set_free(Domain);
+ return nullptr;
+ }
+
+ isl_ast_expr *DomainCond = isl_ast_build_expr_from_set(Build, Domain);
+ Domain = nullptr;
+
+ ExprBuilder.setTrackOverflow(true);
+ Value *Cond = ExprBuilder.create(DomainCond);
+ Value *OverflowHappened = Builder.CreateNot(ExprBuilder.getOverflowState(),
+ "polly.preload.cond.overflown");
+ Cond = Builder.CreateAnd(Cond, OverflowHappened, "polly.preload.cond.result");
+ ExprBuilder.setTrackOverflow(false);
+
+ if (!Cond->getType()->isIntegerTy(1))
+ Cond = Builder.CreateIsNotNull(Cond);
+
+ BasicBlock *CondBB = SplitBlock(Builder.GetInsertBlock(),
+ &*Builder.GetInsertPoint(), &DT, &LI);
+ CondBB->setName("polly.preload.cond");
+
+ BasicBlock *MergeBB = SplitBlock(CondBB, &CondBB->front(), &DT, &LI);
+ MergeBB->setName("polly.preload.merge");
+
+ Function *F = Builder.GetInsertBlock()->getParent();
+ LLVMContext &Context = F->getContext();
+ BasicBlock *ExecBB = BasicBlock::Create(Context, "polly.preload.exec", F);
+
+ DT.addNewBlock(ExecBB, CondBB);
+ if (Loop *L = LI.getLoopFor(CondBB))
+ L->addBasicBlockToLoop(ExecBB, LI);
+
+ auto *CondBBTerminator = CondBB->getTerminator();
+ Builder.SetInsertPoint(CondBBTerminator);
+ Builder.CreateCondBr(Cond, ExecBB, MergeBB);
+ CondBBTerminator->eraseFromParent();
+
+ Builder.SetInsertPoint(ExecBB);
+ Builder.CreateBr(MergeBB);
+
+ Builder.SetInsertPoint(ExecBB->getTerminator());
+ Value *PreAccInst = preloadUnconditionally(AccessRange, Build, AccInst);
+ Builder.SetInsertPoint(MergeBB->getTerminator());
+ auto *MergePHI = Builder.CreatePHI(
+ AccInstTy, 2, "polly.preload." + AccInst->getName() + ".merge");
+ PreloadVal = MergePHI;
+
+ if (!PreAccInst) {
+ PreloadVal = nullptr;
+ PreAccInst = UndefValue::get(AccInstTy);
+ }
+
+ MergePHI->addIncoming(PreAccInst, ExecBB);
+ MergePHI->addIncoming(Constant::getNullValue(AccInstTy), CondBB);
+
+ isl_ast_build_free(Build);
+ return PreloadVal;
+}
+
+bool IslNodeBuilder::preloadInvariantEquivClass(
+ InvariantEquivClassTy &IAClass) {
+ // For an equivalence class of invariant loads we pre-load the representing
+ // element with the unified execution context. However, we have to map all
+ // elements of the class to the one preloaded load as they are referenced
+ // during the code generation and therefor need to be mapped.
+ const MemoryAccessList &MAs = IAClass.InvariantAccesses;
+ if (MAs.empty())
+ return true;
+
+ MemoryAccess *MA = MAs.front();
+ assert(MA->isArrayKind() && MA->isRead());
+
+ // If the access function was already mapped, the preload of this equivalence
+ // class was triggered earlier already and doesn't need to be done again.
+ if (ValueMap.count(MA->getAccessInstruction()))
+ return true;
+
+ // Check for recursion which can be caused by additional constraints, e.g.,
+ // non-finite loop constraints. In such a case we have to bail out and insert
+ // a "false" runtime check that will cause the original code to be executed.
+ auto PtrId = std::make_pair(IAClass.IdentifyingPointer, IAClass.AccessType);
+ if (!PreloadedPtrs.insert(PtrId).second)
+ return false;
+
+ // The execution context of the IAClass.
+ isl::set &ExecutionCtx = IAClass.ExecutionContext;
+
+ // If the base pointer of this class is dependent on another one we have to
+ // make sure it was preloaded already.
+ auto *SAI = MA->getScopArrayInfo();
+ if (auto *BaseIAClass = S.lookupInvariantEquivClass(SAI->getBasePtr())) {
+ if (!preloadInvariantEquivClass(*BaseIAClass))
+ return false;
+
+ // After we preloaded the BaseIAClass we adjusted the BaseExecutionCtx and
+ // we need to refine the ExecutionCtx.
+ isl::set BaseExecutionCtx = BaseIAClass->ExecutionContext;
+ ExecutionCtx = ExecutionCtx.intersect(BaseExecutionCtx);
+ }
+
+ // If the size of a dimension is dependent on another class, make sure it is
+ // preloaded.
+ for (unsigned i = 1, e = SAI->getNumberOfDimensions(); i < e; ++i) {
+ const SCEV *Dim = SAI->getDimensionSize(i);
+ SetVector<Value *> Values;
+ findValues(Dim, SE, Values);
+ for (auto *Val : Values) {
+ if (auto *BaseIAClass = S.lookupInvariantEquivClass(Val)) {
+ if (!preloadInvariantEquivClass(*BaseIAClass))
+ return false;
+
+ // After we preloaded the BaseIAClass we adjusted the BaseExecutionCtx
+ // and we need to refine the ExecutionCtx.
+ isl::set BaseExecutionCtx = BaseIAClass->ExecutionContext;
+ ExecutionCtx = ExecutionCtx.intersect(BaseExecutionCtx);
+ }
+ }
+ }
+
+ Instruction *AccInst = MA->getAccessInstruction();
+ Type *AccInstTy = AccInst->getType();
+
+ Value *PreloadVal = preloadInvariantLoad(*MA, ExecutionCtx.copy());
+ if (!PreloadVal)
+ return false;
+
+ for (const MemoryAccess *MA : MAs) {
+ Instruction *MAAccInst = MA->getAccessInstruction();
+ assert(PreloadVal->getType() == MAAccInst->getType());
+ ValueMap[MAAccInst] = PreloadVal;
+ }
+
+ if (SE.isSCEVable(AccInstTy)) {
+ isl_id *ParamId = S.getIdForParam(SE.getSCEV(AccInst)).release();
+ if (ParamId)
+ IDToValue[ParamId] = PreloadVal;
+ isl_id_free(ParamId);
+ }
+
+ BasicBlock *EntryBB = &Builder.GetInsertBlock()->getParent()->getEntryBlock();
+ auto *Alloca = new AllocaInst(AccInstTy, DL.getAllocaAddrSpace(),
+ AccInst->getName() + ".preload.s2a",
+ &*EntryBB->getFirstInsertionPt());
+ Builder.CreateStore(PreloadVal, Alloca);
+ ValueMapT PreloadedPointer;
+ PreloadedPointer[PreloadVal] = AccInst;
+ Annotator.addAlternativeAliasBases(PreloadedPointer);
+
+ for (auto *DerivedSAI : SAI->getDerivedSAIs()) {
+ Value *BasePtr = DerivedSAI->getBasePtr();
+
+ for (const MemoryAccess *MA : MAs) {
+ // As the derived SAI information is quite coarse, any load from the
+ // current SAI could be the base pointer of the derived SAI, however we
+ // should only change the base pointer of the derived SAI if we actually
+ // preloaded it.
+ if (BasePtr == MA->getOriginalBaseAddr()) {
+ assert(BasePtr->getType() == PreloadVal->getType());
+ DerivedSAI->setBasePtr(PreloadVal);
+ }
+
+ // For scalar derived SAIs we remap the alloca used for the derived value.
+ if (BasePtr == MA->getAccessInstruction())
+ ScalarMap[DerivedSAI] = Alloca;
+ }
+ }
+
+ for (const MemoryAccess *MA : MAs) {
+ Instruction *MAAccInst = MA->getAccessInstruction();
+ // Use the escape system to get the correct value to users outside the SCoP.
+ BlockGenerator::EscapeUserVectorTy EscapeUsers;
+ for (auto *U : MAAccInst->users())
+ if (Instruction *UI = dyn_cast<Instruction>(U))
+ if (!S.contains(UI))
+ EscapeUsers.push_back(UI);
+
+ if (EscapeUsers.empty())
+ continue;
+
+ EscapeMap[MA->getAccessInstruction()] =
+ std::make_pair(Alloca, std::move(EscapeUsers));
+ }
+
+ return true;
+}
+
+void IslNodeBuilder::allocateNewArrays(BBPair StartExitBlocks) {
+ for (auto &SAI : S.arrays()) {
+ if (SAI->getBasePtr())
+ continue;
+
+ assert(SAI->getNumberOfDimensions() > 0 && SAI->getDimensionSize(0) &&
+ "The size of the outermost dimension is used to declare newly "
+ "created arrays that require memory allocation.");
+
+ Type *NewArrayType = nullptr;
+
+ // Get the size of the array = size(dim_1)*...*size(dim_n)
+ uint64_t ArraySizeInt = 1;
+ for (int i = SAI->getNumberOfDimensions() - 1; i >= 0; i--) {
+ auto *DimSize = SAI->getDimensionSize(i);
+ unsigned UnsignedDimSize = static_cast<const SCEVConstant *>(DimSize)
+ ->getAPInt()
+ .getLimitedValue();
+
+ if (!NewArrayType)
+ NewArrayType = SAI->getElementType();
+
+ NewArrayType = ArrayType::get(NewArrayType, UnsignedDimSize);
+ ArraySizeInt *= UnsignedDimSize;
+ }
+
+ if (SAI->isOnHeap()) {
+ LLVMContext &Ctx = NewArrayType->getContext();
+
+ // Get the IntPtrTy from the Datalayout
+ auto IntPtrTy = DL.getIntPtrType(Ctx);
+
+ // Get the size of the element type in bits
+ unsigned Size = SAI->getElemSizeInBytes();
+
+ // Insert the malloc call at polly.start
+ auto InstIt = std::get<0>(StartExitBlocks)->getTerminator();
+ auto *CreatedArray = CallInst::CreateMalloc(
+ &*InstIt, IntPtrTy, SAI->getElementType(),
+ ConstantInt::get(Type::getInt64Ty(Ctx), Size),
+ ConstantInt::get(Type::getInt64Ty(Ctx), ArraySizeInt), nullptr,
+ SAI->getName());
+
+ SAI->setBasePtr(CreatedArray);
+
+ // Insert the free call at polly.exiting
+ CallInst::CreateFree(CreatedArray,
+ std::get<1>(StartExitBlocks)->getTerminator());
+ } else {
+ auto InstIt = Builder.GetInsertBlock()
+ ->getParent()
+ ->getEntryBlock()
+ .getTerminator();
+
+ auto *CreatedArray = new AllocaInst(NewArrayType, DL.getAllocaAddrSpace(),
+ SAI->getName(), &*InstIt);
+ if (PollyTargetFirstLevelCacheLineSize)
+ CreatedArray->setAlignment(Align(PollyTargetFirstLevelCacheLineSize));
+ SAI->setBasePtr(CreatedArray);
+ }
+ }
+}
+
+bool IslNodeBuilder::preloadInvariantLoads() {
+ auto &InvariantEquivClasses = S.getInvariantAccesses();
+ if (InvariantEquivClasses.empty())
+ return true;
+
+ BasicBlock *PreLoadBB = SplitBlock(Builder.GetInsertBlock(),
+ &*Builder.GetInsertPoint(), &DT, &LI);
+ PreLoadBB->setName("polly.preload.begin");
+ Builder.SetInsertPoint(&PreLoadBB->front());
+
+ for (auto &IAClass : InvariantEquivClasses)
+ if (!preloadInvariantEquivClass(IAClass))
+ return false;
+
+ return true;
+}
+
+void IslNodeBuilder::addParameters(__isl_take isl_set *Context) {
+ // Materialize values for the parameters of the SCoP.
+ materializeParameters();
+
+ // Generate values for the current loop iteration for all surrounding loops.
+ //
+ // We may also reference loops outside of the scop which do not contain the
+ // scop itself, but as the number of such scops may be arbitrarily large we do
+ // not generate code for them here, but only at the point of code generation
+ // where these values are needed.
+ Loop *L = LI.getLoopFor(S.getEntry());
+
+ while (L != nullptr && S.contains(L))
+ L = L->getParentLoop();
+
+ while (L != nullptr) {
+ materializeNonScopLoopInductionVariable(L);
+ L = L->getParentLoop();
+ }
+
+ isl_set_free(Context);
+}
+
+Value *IslNodeBuilder::generateSCEV(const SCEV *Expr) {
+ /// We pass the insert location of our Builder, as Polly ensures during IR
+ /// generation that there is always a valid CFG into which instructions are
+ /// inserted. As a result, the insertpoint is known to be always followed by a
+ /// terminator instruction. This means the insert point may be specified by a
+ /// terminator instruction, but it can never point to an ->end() iterator
+ /// which does not have a corresponding instruction. Hence, dereferencing
+ /// the insertpoint to obtain an instruction is known to be save.
+ ///
+ /// We also do not need to update the Builder here, as new instructions are
+ /// always inserted _before_ the given InsertLocation. As a result, the
+ /// insert location remains valid.
+ assert(Builder.GetInsertBlock()->end() != Builder.GetInsertPoint() &&
+ "Insert location points after last valid instruction");
+ Instruction *InsertLocation = &*Builder.GetInsertPoint();
+ return expandCodeFor(S, SE, DL, "polly", Expr, Expr->getType(),
+ InsertLocation, &ValueMap,
+ StartBlock->getSinglePredecessor());
+}
+
+/// The AST expression we generate to perform the run-time check assumes
+/// computations on integer types of infinite size. As we only use 64-bit
+/// arithmetic we check for overflows, in case of which we set the result
+/// of this run-time check to false to be conservatively correct,
+Value *IslNodeBuilder::createRTC(isl_ast_expr *Condition) {
+ auto ExprBuilder = getExprBuilder();
+
+ // In case the AST expression has integers larger than 64 bit, bail out. The
+ // resulting LLVM-IR will contain operations on types that use more than 64
+ // bits. These are -- in case wrapping intrinsics are used -- translated to
+ // runtime library calls that are not available on all systems (e.g., Android)
+ // and consequently will result in linker errors.
+ if (ExprBuilder.hasLargeInts(isl::manage_copy(Condition))) {
+ isl_ast_expr_free(Condition);
+ return Builder.getFalse();
+ }
+
+ ExprBuilder.setTrackOverflow(true);
+ Value *RTC = ExprBuilder.create(Condition);
+ if (!RTC->getType()->isIntegerTy(1))
+ RTC = Builder.CreateIsNotNull(RTC);
+ Value *OverflowHappened =
+ Builder.CreateNot(ExprBuilder.getOverflowState(), "polly.rtc.overflown");
+
+ if (PollyGenerateRTCPrint) {
+ auto *F = Builder.GetInsertBlock()->getParent();
+ RuntimeDebugBuilder::createCPUPrinter(
+ Builder,
+ "F: " + F->getName().str() + " R: " + S.getRegion().getNameStr() +
+ "RTC: ",
+ RTC, " Overflow: ", OverflowHappened,
+ "\n"
+ " (0 failed, -1 succeeded)\n"
+ " (if one or both are 0 falling back to original code, if both are -1 "
+ "executing Polly code)\n");
+ }
+
+ RTC = Builder.CreateAnd(RTC, OverflowHappened, "polly.rtc.result");
+ ExprBuilder.setTrackOverflow(false);
+
+ if (!isa<ConstantInt>(RTC))
+ VersionedScops++;
+
+ return RTC;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGenerators.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGenerators.cpp
new file mode 100644
index 00000000000..70250390870
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGenerators.cpp
@@ -0,0 +1,253 @@
+//===------ LoopGenerators.cpp - IR helper to create loops ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains functions to create scalar loops and orchestrate the
+// creation of parallel loops as LLVM-IR.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/LoopGenerators.h"
+#include "polly/Options.h"
+#include "polly/ScopDetection.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+
+using namespace llvm;
+using namespace polly;
+
+int polly::PollyNumThreads;
+OMPGeneralSchedulingType polly::PollyScheduling;
+int polly::PollyChunkSize;
+
+static cl::opt<int, true>
+ XPollyNumThreads("polly-num-threads",
+ cl::desc("Number of threads to use (0 = auto)"),
+ cl::Hidden, cl::location(polly::PollyNumThreads),
+ cl::init(0), cl::cat(PollyCategory));
+
+static cl::opt<OMPGeneralSchedulingType, true> XPollyScheduling(
+ "polly-scheduling",
+ cl::desc("Scheduling type of parallel OpenMP for loops"),
+ cl::values(clEnumValN(OMPGeneralSchedulingType::StaticChunked, "static",
+ "Static scheduling"),
+ clEnumValN(OMPGeneralSchedulingType::Dynamic, "dynamic",
+ "Dynamic scheduling"),
+ clEnumValN(OMPGeneralSchedulingType::Guided, "guided",
+ "Guided scheduling"),
+ clEnumValN(OMPGeneralSchedulingType::Runtime, "runtime",
+ "Runtime determined (OMP_SCHEDULE)")),
+ cl::Hidden, cl::location(polly::PollyScheduling),
+ cl::init(OMPGeneralSchedulingType::Runtime), cl::Optional,
+ cl::cat(PollyCategory));
+
+static cl::opt<int, true>
+ XPollyChunkSize("polly-scheduling-chunksize",
+ cl::desc("Chunksize to use by the OpenMP runtime calls"),
+ cl::Hidden, cl::location(polly::PollyChunkSize),
+ cl::init(0), cl::Optional, cl::cat(PollyCategory));
+
+// We generate a loop of either of the following structures:
+//
+// BeforeBB BeforeBB
+// | |
+// v v
+// GuardBB PreHeaderBB
+// / | | _____
+// __ PreHeaderBB | v \/ |
+// / \ / | HeaderBB latch
+// latch HeaderBB | |\ |
+// \ / \ / | \------/
+// < \ / |
+// \ / v
+// ExitBB ExitBB
+//
+// depending on whether or not we know that it is executed at least once. If
+// not, GuardBB checks if the loop is executed at least once. If this is the
+// case we branch to PreHeaderBB and subsequently to the HeaderBB, which
+// contains the loop iv 'polly.indvar', the incremented loop iv
+// 'polly.indvar_next' as well as the condition to check if we execute another
+// iteration of the loop. After the loop has finished, we branch to ExitBB.
+// We expect the type of UB, LB, UB+Stride to be large enough for values that
+// UB may take throughout the execution of the loop, including the computation
+// of indvar + Stride before the final abort.
+Value *polly::createLoop(Value *LB, Value *UB, Value *Stride,
+ PollyIRBuilder &Builder, LoopInfo &LI,
+ DominatorTree &DT, BasicBlock *&ExitBB,
+ ICmpInst::Predicate Predicate,
+ ScopAnnotator *Annotator, bool Parallel, bool UseGuard,
+ bool LoopVectDisabled) {
+ Function *F = Builder.GetInsertBlock()->getParent();
+ LLVMContext &Context = F->getContext();
+
+ assert(LB->getType() == UB->getType() && "Types of loop bounds do not match");
+ IntegerType *LoopIVType = dyn_cast<IntegerType>(UB->getType());
+ assert(LoopIVType && "UB is not integer?");
+
+ BasicBlock *BeforeBB = Builder.GetInsertBlock();
+ BasicBlock *GuardBB =
+ UseGuard ? BasicBlock::Create(Context, "polly.loop_if", F) : nullptr;
+ BasicBlock *HeaderBB = BasicBlock::Create(Context, "polly.loop_header", F);
+ BasicBlock *PreHeaderBB =
+ BasicBlock::Create(Context, "polly.loop_preheader", F);
+
+ // Update LoopInfo
+ Loop *OuterLoop = LI.getLoopFor(BeforeBB);
+ Loop *NewLoop = LI.AllocateLoop();
+
+ if (OuterLoop)
+ OuterLoop->addChildLoop(NewLoop);
+ else
+ LI.addTopLevelLoop(NewLoop);
+
+ if (OuterLoop) {
+ if (GuardBB)
+ OuterLoop->addBasicBlockToLoop(GuardBB, LI);
+ OuterLoop->addBasicBlockToLoop(PreHeaderBB, LI);
+ }
+
+ NewLoop->addBasicBlockToLoop(HeaderBB, LI);
+
+ // Notify the annotator (if present) that we have a new loop, but only
+ // after the header block is set.
+ if (Annotator)
+ Annotator->pushLoop(NewLoop, Parallel);
+
+ // ExitBB
+ ExitBB = SplitBlock(BeforeBB, &*Builder.GetInsertPoint(), &DT, &LI);
+ ExitBB->setName("polly.loop_exit");
+
+ // BeforeBB
+ if (GuardBB) {
+ BeforeBB->getTerminator()->setSuccessor(0, GuardBB);
+ DT.addNewBlock(GuardBB, BeforeBB);
+
+ // GuardBB
+ Builder.SetInsertPoint(GuardBB);
+ Value *LoopGuard;
+ LoopGuard = Builder.CreateICmp(Predicate, LB, UB);
+ LoopGuard->setName("polly.loop_guard");
+ Builder.CreateCondBr(LoopGuard, PreHeaderBB, ExitBB);
+ DT.addNewBlock(PreHeaderBB, GuardBB);
+ } else {
+ BeforeBB->getTerminator()->setSuccessor(0, PreHeaderBB);
+ DT.addNewBlock(PreHeaderBB, BeforeBB);
+ }
+
+ // PreHeaderBB
+ Builder.SetInsertPoint(PreHeaderBB);
+ Builder.CreateBr(HeaderBB);
+
+ // HeaderBB
+ DT.addNewBlock(HeaderBB, PreHeaderBB);
+ Builder.SetInsertPoint(HeaderBB);
+ PHINode *IV = Builder.CreatePHI(LoopIVType, 2, "polly.indvar");
+ IV->addIncoming(LB, PreHeaderBB);
+ Stride = Builder.CreateZExtOrBitCast(Stride, LoopIVType);
+ Value *IncrementedIV = Builder.CreateNSWAdd(IV, Stride, "polly.indvar_next");
+ Value *LoopCondition =
+ Builder.CreateICmp(Predicate, IncrementedIV, UB, "polly.loop_cond");
+
+ // Create the loop latch and annotate it as such.
+ BranchInst *B = Builder.CreateCondBr(LoopCondition, HeaderBB, ExitBB);
+ if (Annotator)
+ Annotator->annotateLoopLatch(B, NewLoop, Parallel, LoopVectDisabled);
+
+ IV->addIncoming(IncrementedIV, HeaderBB);
+ if (GuardBB)
+ DT.changeImmediateDominator(ExitBB, GuardBB);
+ else
+ DT.changeImmediateDominator(ExitBB, HeaderBB);
+
+ // The loop body should be added here.
+ Builder.SetInsertPoint(HeaderBB->getFirstNonPHI());
+ return IV;
+}
+
+Value *ParallelLoopGenerator::createParallelLoop(
+ Value *LB, Value *UB, Value *Stride, SetVector<Value *> &UsedValues,
+ ValueMapT &Map, BasicBlock::iterator *LoopBody) {
+
+ AllocaInst *Struct = storeValuesIntoStruct(UsedValues);
+ BasicBlock::iterator BeforeLoop = Builder.GetInsertPoint();
+
+ Value *IV;
+ Function *SubFn;
+ std::tie(IV, SubFn) = createSubFn(Stride, Struct, UsedValues, Map);
+ *LoopBody = Builder.GetInsertPoint();
+ Builder.SetInsertPoint(&*BeforeLoop);
+
+ Value *SubFnParam = Builder.CreateBitCast(Struct, Builder.getInt8PtrTy(),
+ "polly.par.userContext");
+
+ // Add one as the upper bound provided by OpenMP is a < comparison
+ // whereas the codegenForSequential function creates a <= comparison.
+ UB = Builder.CreateAdd(UB, ConstantInt::get(LongType, 1));
+
+ // Execute the prepared subfunction in parallel.
+ deployParallelExecution(SubFn, SubFnParam, LB, UB, Stride);
+
+ return IV;
+}
+
+Function *ParallelLoopGenerator::createSubFnDefinition() {
+ Function *F = Builder.GetInsertBlock()->getParent();
+ Function *SubFn = prepareSubFnDefinition(F);
+
+ // Certain backends (e.g., NVPTX) do not support '.'s in function names.
+ // Hence, we ensure that all '.'s are replaced by '_'s.
+ std::string FunctionName = SubFn->getName().str();
+ std::replace(FunctionName.begin(), FunctionName.end(), '.', '_');
+ SubFn->setName(FunctionName);
+
+ // Do not run any polly pass on the new function.
+ SubFn->addFnAttr(PollySkipFnAttr);
+
+ return SubFn;
+}
+
+AllocaInst *
+ParallelLoopGenerator::storeValuesIntoStruct(SetVector<Value *> &Values) {
+ SmallVector<Type *, 8> Members;
+
+ for (Value *V : Values)
+ Members.push_back(V->getType());
+
+ const DataLayout &DL = Builder.GetInsertBlock()->getModule()->getDataLayout();
+
+ // We do not want to allocate the alloca inside any loop, thus we allocate it
+ // in the entry block of the function and use annotations to denote the actual
+ // live span (similar to clang).
+ BasicBlock &EntryBB = Builder.GetInsertBlock()->getParent()->getEntryBlock();
+ Instruction *IP = &*EntryBB.getFirstInsertionPt();
+ StructType *Ty = StructType::get(Builder.getContext(), Members);
+ AllocaInst *Struct = new AllocaInst(Ty, DL.getAllocaAddrSpace(), nullptr,
+ "polly.par.userContext", IP);
+
+ for (unsigned i = 0; i < Values.size(); i++) {
+ Value *Address = Builder.CreateStructGEP(Ty, Struct, i);
+ Address->setName("polly.subfn.storeaddr." + Values[i]->getName());
+ Builder.CreateStore(Values[i], Address);
+ }
+
+ return Struct;
+}
+
+void ParallelLoopGenerator::extractValuesFromStruct(
+ SetVector<Value *> OldValues, Type *Ty, Value *Struct, ValueMapT &Map) {
+ for (unsigned i = 0; i < OldValues.size(); i++) {
+ Value *Address = Builder.CreateStructGEP(Ty, Struct, i);
+ Type *ElemTy = cast<GetElementPtrInst>(Address)->getResultElementType();
+ Value *NewValue = Builder.CreateLoad(ElemTy, Address);
+ NewValue->setName("polly.subfunc.arg." + OldValues[i]->getName());
+ Map[OldValues[i]] = NewValue;
+ }
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGeneratorsGOMP.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGeneratorsGOMP.cpp
new file mode 100644
index 00000000000..294d91f2f39
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGeneratorsGOMP.cpp
@@ -0,0 +1,223 @@
+//===------ LoopGeneratorsGOMP.cpp - IR helper to create loops ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains functions to create parallel loops as LLVM-IR.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/LoopGeneratorsGOMP.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Module.h"
+
+using namespace llvm;
+using namespace polly;
+
+void ParallelLoopGeneratorGOMP::createCallSpawnThreads(Value *SubFn,
+ Value *SubFnParam,
+ Value *LB, Value *UB,
+ Value *Stride) {
+ const std::string Name = "GOMP_parallel_loop_runtime_start";
+
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+
+ Type *Params[] = {PointerType::getUnqual(FunctionType::get(
+ Builder.getVoidTy(), Builder.getInt8PtrTy(), false)),
+ Builder.getInt8PtrTy(),
+ Builder.getInt32Ty(),
+ LongType,
+ LongType,
+ LongType};
+
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Value *Args[] = {SubFn, SubFnParam, Builder.getInt32(PollyNumThreads),
+ LB, UB, Stride};
+
+ Builder.CreateCall(F, Args);
+}
+
+void ParallelLoopGeneratorGOMP::deployParallelExecution(Function *SubFn,
+ Value *SubFnParam,
+ Value *LB, Value *UB,
+ Value *Stride) {
+ // Tell the runtime we start a parallel loop
+ createCallSpawnThreads(SubFn, SubFnParam, LB, UB, Stride);
+ Builder.CreateCall(SubFn, SubFnParam);
+ createCallJoinThreads();
+}
+
+Function *ParallelLoopGeneratorGOMP::prepareSubFnDefinition(Function *F) const {
+ FunctionType *FT =
+ FunctionType::get(Builder.getVoidTy(), {Builder.getInt8PtrTy()}, false);
+ Function *SubFn = Function::Create(FT, Function::InternalLinkage,
+ F->getName() + "_polly_subfn", M);
+ // Name the function's arguments
+ SubFn->arg_begin()->setName("polly.par.userContext");
+ return SubFn;
+}
+
+// Create a subfunction of the following (preliminary) structure:
+//
+// PrevBB
+// |
+// v
+// HeaderBB
+// | _____
+// v v |
+// CheckNextBB PreHeaderBB
+// |\ |
+// | \______/
+// |
+// v
+// ExitBB
+//
+// HeaderBB will hold allocations and loading of variables.
+// CheckNextBB will check for more work.
+// If there is more work to do: go to PreHeaderBB, otherwise go to ExitBB.
+// PreHeaderBB loads the new boundaries (& will lead to the loop body later on).
+// ExitBB marks the end of the parallel execution.
+std::tuple<Value *, Function *>
+ParallelLoopGeneratorGOMP::createSubFn(Value *Stride, AllocaInst *StructData,
+ SetVector<Value *> Data,
+ ValueMapT &Map) {
+ if (PollyScheduling != OMPGeneralSchedulingType::Runtime) {
+ // User tried to influence the scheduling type (currently not supported)
+ errs() << "warning: Polly's GNU OpenMP backend solely "
+ "supports the scheduling type 'runtime'.\n";
+ }
+
+ if (PollyChunkSize != 0) {
+ // User tried to influence the chunk size (currently not supported)
+ errs() << "warning: Polly's GNU OpenMP backend solely "
+ "supports the default chunk size.\n";
+ }
+
+ Function *SubFn = createSubFnDefinition();
+ LLVMContext &Context = SubFn->getContext();
+
+ // Store the previous basic block.
+ BasicBlock *PrevBB = Builder.GetInsertBlock();
+
+ // Create basic blocks.
+ BasicBlock *HeaderBB = BasicBlock::Create(Context, "polly.par.setup", SubFn);
+ BasicBlock *ExitBB = BasicBlock::Create(Context, "polly.par.exit", SubFn);
+ BasicBlock *CheckNextBB =
+ BasicBlock::Create(Context, "polly.par.checkNext", SubFn);
+ BasicBlock *PreHeaderBB =
+ BasicBlock::Create(Context, "polly.par.loadIVBounds", SubFn);
+
+ DT.addNewBlock(HeaderBB, PrevBB);
+ DT.addNewBlock(ExitBB, HeaderBB);
+ DT.addNewBlock(CheckNextBB, HeaderBB);
+ DT.addNewBlock(PreHeaderBB, HeaderBB);
+
+ // Fill up basic block HeaderBB.
+ Builder.SetInsertPoint(HeaderBB);
+ Value *LBPtr = Builder.CreateAlloca(LongType, nullptr, "polly.par.LBPtr");
+ Value *UBPtr = Builder.CreateAlloca(LongType, nullptr, "polly.par.UBPtr");
+ Value *UserContext = Builder.CreateBitCast(
+ &*SubFn->arg_begin(), StructData->getType(), "polly.par.userContext");
+
+ extractValuesFromStruct(Data, StructData->getAllocatedType(), UserContext,
+ Map);
+ Builder.CreateBr(CheckNextBB);
+
+ // Add code to check if another set of iterations will be executed.
+ Builder.SetInsertPoint(CheckNextBB);
+ Value *Next = createCallGetWorkItem(LBPtr, UBPtr);
+ Value *HasNextSchedule = Builder.CreateTrunc(
+ Next, Builder.getInt1Ty(), "polly.par.hasNextScheduleBlock");
+ Builder.CreateCondBr(HasNextSchedule, PreHeaderBB, ExitBB);
+
+ // Add code to load the iv bounds for this set of iterations.
+ Builder.SetInsertPoint(PreHeaderBB);
+ Value *LB = Builder.CreateLoad(LongType, LBPtr, "polly.par.LB");
+ Value *UB = Builder.CreateLoad(LongType, UBPtr, "polly.par.UB");
+
+ // Subtract one as the upper bound provided by OpenMP is a < comparison
+ // whereas the codegenForSequential function creates a <= comparison.
+ UB = Builder.CreateSub(UB, ConstantInt::get(LongType, 1),
+ "polly.par.UBAdjusted");
+
+ Builder.CreateBr(CheckNextBB);
+ Builder.SetInsertPoint(&*--Builder.GetInsertPoint());
+ BasicBlock *AfterBB;
+ Value *IV =
+ createLoop(LB, UB, Stride, Builder, LI, DT, AfterBB, ICmpInst::ICMP_SLE,
+ nullptr, true, /* UseGuard */ false);
+
+ BasicBlock::iterator LoopBody = Builder.GetInsertPoint();
+
+ // Add code to terminate this subfunction.
+ Builder.SetInsertPoint(ExitBB);
+ createCallCleanupThread();
+ Builder.CreateRetVoid();
+
+ Builder.SetInsertPoint(&*LoopBody);
+
+ return std::make_tuple(IV, SubFn);
+}
+
+Value *ParallelLoopGeneratorGOMP::createCallGetWorkItem(Value *LBPtr,
+ Value *UBPtr) {
+ const std::string Name = "GOMP_loop_runtime_next";
+
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ Type *Params[] = {LongType->getPointerTo(), LongType->getPointerTo()};
+ FunctionType *Ty = FunctionType::get(Builder.getInt8Ty(), Params, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Value *Args[] = {LBPtr, UBPtr};
+ Value *Return = Builder.CreateCall(F, Args);
+ Return = Builder.CreateICmpNE(
+ Return, Builder.CreateZExt(Builder.getFalse(), Return->getType()));
+ return Return;
+}
+
+void ParallelLoopGeneratorGOMP::createCallJoinThreads() {
+ const std::string Name = "GOMP_parallel_end";
+
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Builder.CreateCall(F, {});
+}
+
+void ParallelLoopGeneratorGOMP::createCallCleanupThread() {
+ const std::string Name = "GOMP_loop_end_nowait";
+
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Builder.CreateCall(F, {});
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGeneratorsKMP.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGeneratorsKMP.cpp
new file mode 100644
index 00000000000..335fc400762
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/LoopGeneratorsKMP.cpp
@@ -0,0 +1,558 @@
+//===------ LoopGeneratorsKMP.cpp - IR helper to create loops -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains functions to create parallel loops as LLVM-IR.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/LoopGeneratorsKMP.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/Module.h"
+
+using namespace llvm;
+using namespace polly;
+
+void ParallelLoopGeneratorKMP::createCallSpawnThreads(Value *SubFn,
+ Value *SubFnParam,
+ Value *LB, Value *UB,
+ Value *Stride) {
+ const std::string Name = "__kmpc_fork_call";
+ Function *F = M->getFunction(Name);
+ Type *KMPCMicroTy = StructType::getTypeByName(M->getContext(), "kmpc_micro");
+
+ if (!KMPCMicroTy) {
+ // void (*kmpc_micro)(kmp_int32 *global_tid, kmp_int32 *bound_tid, ...)
+ Type *MicroParams[] = {Builder.getInt32Ty()->getPointerTo(),
+ Builder.getInt32Ty()->getPointerTo()};
+
+ KMPCMicroTy = FunctionType::get(Builder.getVoidTy(), MicroParams, true);
+ }
+
+ // If F is not available, declare it.
+ if (!F) {
+ StructType *IdentTy =
+ StructType::getTypeByName(M->getContext(), "struct.ident_t");
+
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ Type *Params[] = {IdentTy->getPointerTo(), Builder.getInt32Ty(),
+ KMPCMicroTy->getPointerTo()};
+
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, true);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Value *Task = Builder.CreatePointerBitCastOrAddrSpaceCast(
+ SubFn, KMPCMicroTy->getPointerTo());
+
+ Value *Args[] = {SourceLocationInfo,
+ Builder.getInt32(4) /* Number of arguments (w/o Task) */,
+ Task,
+ LB,
+ UB,
+ Stride,
+ SubFnParam};
+
+ Builder.CreateCall(F, Args);
+}
+
+void ParallelLoopGeneratorKMP::deployParallelExecution(Function *SubFn,
+ Value *SubFnParam,
+ Value *LB, Value *UB,
+ Value *Stride) {
+ // Inform OpenMP runtime about the number of threads if greater than zero
+ if (PollyNumThreads > 0) {
+ Value *GlobalThreadID = createCallGlobalThreadNum();
+ createCallPushNumThreads(GlobalThreadID, Builder.getInt32(PollyNumThreads));
+ }
+
+ // Tell the runtime we start a parallel loop
+ createCallSpawnThreads(SubFn, SubFnParam, LB, UB, Stride);
+}
+
+Function *ParallelLoopGeneratorKMP::prepareSubFnDefinition(Function *F) const {
+ std::vector<Type *> Arguments = {Builder.getInt32Ty()->getPointerTo(),
+ Builder.getInt32Ty()->getPointerTo(),
+ LongType,
+ LongType,
+ LongType,
+ Builder.getInt8PtrTy()};
+
+ FunctionType *FT = FunctionType::get(Builder.getVoidTy(), Arguments, false);
+ Function *SubFn = Function::Create(FT, Function::InternalLinkage,
+ F->getName() + "_polly_subfn", M);
+ // Name the function's arguments
+ Function::arg_iterator AI = SubFn->arg_begin();
+ AI->setName("polly.kmpc.global_tid");
+ std::advance(AI, 1);
+ AI->setName("polly.kmpc.bound_tid");
+ std::advance(AI, 1);
+ AI->setName("polly.kmpc.lb");
+ std::advance(AI, 1);
+ AI->setName("polly.kmpc.ub");
+ std::advance(AI, 1);
+ AI->setName("polly.kmpc.inc");
+ std::advance(AI, 1);
+ AI->setName("polly.kmpc.shared");
+
+ return SubFn;
+}
+
+// Create a subfunction of the following (preliminary) structure:
+//
+// PrevBB
+// |
+// v
+// HeaderBB
+// / | _____
+// / v v |
+// / PreHeaderBB |
+// | | |
+// | v |
+// | CheckNextBB |
+// \ | \_____/
+// \ |
+// v v
+// ExitBB
+//
+// HeaderBB will hold allocations, loading of variables and kmp-init calls.
+// CheckNextBB will check for more work (dynamic / static chunked) or will be
+// empty (static non chunked).
+// If there is more work to do: go to PreHeaderBB, otherwise go to ExitBB.
+// PreHeaderBB loads the new boundaries (& will lead to the loop body later on).
+// Just like CheckNextBB: PreHeaderBB is (preliminary) empty in the static non
+// chunked scheduling case. ExitBB marks the end of the parallel execution.
+// The possibly empty BasicBlocks will automatically be removed.
+std::tuple<Value *, Function *>
+ParallelLoopGeneratorKMP::createSubFn(Value *SequentialLoopStride,
+ AllocaInst *StructData,
+ SetVector<Value *> Data, ValueMapT &Map) {
+ Function *SubFn = createSubFnDefinition();
+ LLVMContext &Context = SubFn->getContext();
+
+ // Store the previous basic block.
+ BasicBlock *PrevBB = Builder.GetInsertBlock();
+
+ // Create basic blocks.
+ BasicBlock *HeaderBB = BasicBlock::Create(Context, "polly.par.setup", SubFn);
+ BasicBlock *ExitBB = BasicBlock::Create(Context, "polly.par.exit", SubFn);
+ BasicBlock *CheckNextBB =
+ BasicBlock::Create(Context, "polly.par.checkNext", SubFn);
+ BasicBlock *PreHeaderBB =
+ BasicBlock::Create(Context, "polly.par.loadIVBounds", SubFn);
+
+ DT.addNewBlock(HeaderBB, PrevBB);
+ DT.addNewBlock(ExitBB, HeaderBB);
+ DT.addNewBlock(CheckNextBB, HeaderBB);
+ DT.addNewBlock(PreHeaderBB, HeaderBB);
+
+ // Fill up basic block HeaderBB.
+ Builder.SetInsertPoint(HeaderBB);
+ Value *LBPtr = Builder.CreateAlloca(LongType, nullptr, "polly.par.LBPtr");
+ Value *UBPtr = Builder.CreateAlloca(LongType, nullptr, "polly.par.UBPtr");
+ Value *IsLastPtr = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr,
+ "polly.par.lastIterPtr");
+ Value *StridePtr =
+ Builder.CreateAlloca(LongType, nullptr, "polly.par.StridePtr");
+
+ // Get iterator for retrieving the previously defined parameters.
+ Function::arg_iterator AI = SubFn->arg_begin();
+ // First argument holds "global thread ID".
+ Value *IDPtr = &*AI;
+ // Skip "bound thread ID" since it is not used (but had to be defined).
+ std::advance(AI, 2);
+ // Move iterator to: LB, UB, Stride, Shared variable struct.
+ Value *LB = &*AI;
+ std::advance(AI, 1);
+ Value *UB = &*AI;
+ std::advance(AI, 1);
+ Value *Stride = &*AI;
+ std::advance(AI, 1);
+ Value *Shared = &*AI;
+
+ Value *UserContext = Builder.CreateBitCast(Shared, StructData->getType(),
+ "polly.par.userContext");
+
+ extractValuesFromStruct(Data, StructData->getAllocatedType(), UserContext,
+ Map);
+
+ const auto Alignment = llvm::Align(is64BitArch() ? 8 : 4);
+ Value *ID = Builder.CreateAlignedLoad(Builder.getInt32Ty(), IDPtr, Alignment,
+ "polly.par.global_tid");
+
+ Builder.CreateAlignedStore(LB, LBPtr, Alignment);
+ Builder.CreateAlignedStore(UB, UBPtr, Alignment);
+ Builder.CreateAlignedStore(Builder.getInt32(0), IsLastPtr, Alignment);
+ Builder.CreateAlignedStore(Stride, StridePtr, Alignment);
+
+ // Subtract one as the upper bound provided by openmp is a < comparison
+ // whereas the codegenForSequential function creates a <= comparison.
+ Value *AdjustedUB = Builder.CreateAdd(UB, ConstantInt::get(LongType, -1),
+ "polly.indvar.UBAdjusted");
+
+ Value *ChunkSize =
+ ConstantInt::get(LongType, std::max<int>(PollyChunkSize, 1));
+
+ OMPGeneralSchedulingType Scheduling =
+ getSchedType(PollyChunkSize, PollyScheduling);
+
+ switch (Scheduling) {
+ case OMPGeneralSchedulingType::Dynamic:
+ case OMPGeneralSchedulingType::Guided:
+ case OMPGeneralSchedulingType::Runtime:
+ // "DYNAMIC" scheduling types are handled below (including 'runtime')
+ {
+ UB = AdjustedUB;
+ createCallDispatchInit(ID, LB, UB, Stride, ChunkSize);
+ Value *HasWork =
+ createCallDispatchNext(ID, IsLastPtr, LBPtr, UBPtr, StridePtr);
+ Value *HasIteration =
+ Builder.CreateICmp(llvm::CmpInst::Predicate::ICMP_EQ, HasWork,
+ Builder.getInt32(1), "polly.hasIteration");
+ Builder.CreateCondBr(HasIteration, PreHeaderBB, ExitBB);
+
+ Builder.SetInsertPoint(CheckNextBB);
+ HasWork = createCallDispatchNext(ID, IsLastPtr, LBPtr, UBPtr, StridePtr);
+ HasIteration =
+ Builder.CreateICmp(llvm::CmpInst::Predicate::ICMP_EQ, HasWork,
+ Builder.getInt32(1), "polly.hasWork");
+ Builder.CreateCondBr(HasIteration, PreHeaderBB, ExitBB);
+
+ Builder.SetInsertPoint(PreHeaderBB);
+ LB = Builder.CreateAlignedLoad(LongType, LBPtr, Alignment,
+ "polly.indvar.LB");
+ UB = Builder.CreateAlignedLoad(LongType, UBPtr, Alignment,
+ "polly.indvar.UB");
+ }
+ break;
+ case OMPGeneralSchedulingType::StaticChunked:
+ case OMPGeneralSchedulingType::StaticNonChunked:
+ // "STATIC" scheduling types are handled below
+ {
+ Builder.CreateAlignedStore(AdjustedUB, UBPtr, Alignment);
+ createCallStaticInit(ID, IsLastPtr, LBPtr, UBPtr, StridePtr, ChunkSize);
+
+ Value *ChunkedStride = Builder.CreateAlignedLoad(
+ LongType, StridePtr, Alignment, "polly.kmpc.stride");
+
+ LB = Builder.CreateAlignedLoad(LongType, LBPtr, Alignment,
+ "polly.indvar.LB");
+ UB = Builder.CreateAlignedLoad(LongType, UBPtr, Alignment,
+ "polly.indvar.UB.temp");
+
+ Value *UBInRange =
+ Builder.CreateICmp(llvm::CmpInst::Predicate::ICMP_SLE, UB, AdjustedUB,
+ "polly.indvar.UB.inRange");
+ UB = Builder.CreateSelect(UBInRange, UB, AdjustedUB, "polly.indvar.UB");
+ Builder.CreateAlignedStore(UB, UBPtr, Alignment);
+
+ Value *HasIteration = Builder.CreateICmp(
+ llvm::CmpInst::Predicate::ICMP_SLE, LB, UB, "polly.hasIteration");
+ Builder.CreateCondBr(HasIteration, PreHeaderBB, ExitBB);
+
+ if (Scheduling == OMPGeneralSchedulingType::StaticChunked) {
+ Builder.SetInsertPoint(PreHeaderBB);
+ LB = Builder.CreateAlignedLoad(LongType, LBPtr, Alignment,
+ "polly.indvar.LB.entry");
+ UB = Builder.CreateAlignedLoad(LongType, UBPtr, Alignment,
+ "polly.indvar.UB.entry");
+ }
+
+ Builder.SetInsertPoint(CheckNextBB);
+
+ if (Scheduling == OMPGeneralSchedulingType::StaticChunked) {
+ Value *NextLB =
+ Builder.CreateAdd(LB, ChunkedStride, "polly.indvar.nextLB");
+ Value *NextUB = Builder.CreateAdd(UB, ChunkedStride);
+
+ Value *NextUBOutOfBounds =
+ Builder.CreateICmp(llvm::CmpInst::Predicate::ICMP_SGT, NextUB,
+ AdjustedUB, "polly.indvar.nextUB.outOfBounds");
+ NextUB = Builder.CreateSelect(NextUBOutOfBounds, AdjustedUB, NextUB,
+ "polly.indvar.nextUB");
+
+ Builder.CreateAlignedStore(NextLB, LBPtr, Alignment);
+ Builder.CreateAlignedStore(NextUB, UBPtr, Alignment);
+
+ Value *HasWork =
+ Builder.CreateICmp(llvm::CmpInst::Predicate::ICMP_SLE, NextLB,
+ AdjustedUB, "polly.hasWork");
+ Builder.CreateCondBr(HasWork, PreHeaderBB, ExitBB);
+ } else {
+ Builder.CreateBr(ExitBB);
+ }
+
+ Builder.SetInsertPoint(PreHeaderBB);
+ }
+ break;
+ }
+
+ Builder.CreateBr(CheckNextBB);
+ Builder.SetInsertPoint(&*--Builder.GetInsertPoint());
+ BasicBlock *AfterBB;
+ Value *IV = createLoop(LB, UB, SequentialLoopStride, Builder, LI, DT, AfterBB,
+ ICmpInst::ICMP_SLE, nullptr, true,
+ /* UseGuard */ false);
+
+ BasicBlock::iterator LoopBody = Builder.GetInsertPoint();
+
+ // Add code to terminate this subfunction.
+ Builder.SetInsertPoint(ExitBB);
+ // Static (i.e. non-dynamic) scheduling types, are terminated with a fini-call
+ if (Scheduling == OMPGeneralSchedulingType::StaticChunked ||
+ Scheduling == OMPGeneralSchedulingType::StaticNonChunked) {
+ createCallStaticFini(ID);
+ }
+ Builder.CreateRetVoid();
+ Builder.SetInsertPoint(&*LoopBody);
+
+ return std::make_tuple(IV, SubFn);
+}
+
+Value *ParallelLoopGeneratorKMP::createCallGlobalThreadNum() {
+ const std::string Name = "__kmpc_global_thread_num";
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ StructType *IdentTy =
+ StructType::getTypeByName(M->getContext(), "struct.ident_t");
+
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ Type *Params[] = {IdentTy->getPointerTo()};
+
+ FunctionType *Ty = FunctionType::get(Builder.getInt32Ty(), Params, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ return Builder.CreateCall(F, {SourceLocationInfo});
+}
+
+void ParallelLoopGeneratorKMP::createCallPushNumThreads(Value *GlobalThreadID,
+ Value *NumThreads) {
+ const std::string Name = "__kmpc_push_num_threads";
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ StructType *IdentTy =
+ StructType::getTypeByName(M->getContext(), "struct.ident_t");
+
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ Type *Params[] = {IdentTy->getPointerTo(), Builder.getInt32Ty(),
+ Builder.getInt32Ty()};
+
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Value *Args[] = {SourceLocationInfo, GlobalThreadID, NumThreads};
+
+ Builder.CreateCall(F, Args);
+}
+
+void ParallelLoopGeneratorKMP::createCallStaticInit(Value *GlobalThreadID,
+ Value *IsLastPtr,
+ Value *LBPtr, Value *UBPtr,
+ Value *StridePtr,
+ Value *ChunkSize) {
+ const std::string Name =
+ is64BitArch() ? "__kmpc_for_static_init_8" : "__kmpc_for_static_init_4";
+ Function *F = M->getFunction(Name);
+ StructType *IdentTy =
+ StructType::getTypeByName(M->getContext(), "struct.ident_t");
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+
+ Type *Params[] = {IdentTy->getPointerTo(),
+ Builder.getInt32Ty(),
+ Builder.getInt32Ty(),
+ Builder.getInt32Ty()->getPointerTo(),
+ LongType->getPointerTo(),
+ LongType->getPointerTo(),
+ LongType->getPointerTo(),
+ LongType,
+ LongType};
+
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ // The parameter 'ChunkSize' will hold strictly positive integer values,
+ // regardless of PollyChunkSize's value
+ Value *Args[] = {
+ SourceLocationInfo,
+ GlobalThreadID,
+ Builder.getInt32(int(getSchedType(PollyChunkSize, PollyScheduling))),
+ IsLastPtr,
+ LBPtr,
+ UBPtr,
+ StridePtr,
+ ConstantInt::get(LongType, 1),
+ ChunkSize};
+
+ Builder.CreateCall(F, Args);
+}
+
+void ParallelLoopGeneratorKMP::createCallStaticFini(Value *GlobalThreadID) {
+ const std::string Name = "__kmpc_for_static_fini";
+ Function *F = M->getFunction(Name);
+ StructType *IdentTy =
+ StructType::getTypeByName(M->getContext(), "struct.ident_t");
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ Type *Params[] = {IdentTy->getPointerTo(), Builder.getInt32Ty()};
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Value *Args[] = {SourceLocationInfo, GlobalThreadID};
+
+ Builder.CreateCall(F, Args);
+}
+
+void ParallelLoopGeneratorKMP::createCallDispatchInit(Value *GlobalThreadID,
+ Value *LB, Value *UB,
+ Value *Inc,
+ Value *ChunkSize) {
+ const std::string Name =
+ is64BitArch() ? "__kmpc_dispatch_init_8" : "__kmpc_dispatch_init_4";
+ Function *F = M->getFunction(Name);
+ StructType *IdentTy =
+ StructType::getTypeByName(M->getContext(), "struct.ident_t");
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+
+ Type *Params[] = {IdentTy->getPointerTo(),
+ Builder.getInt32Ty(),
+ Builder.getInt32Ty(),
+ LongType,
+ LongType,
+ LongType,
+ LongType};
+
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ // The parameter 'ChunkSize' will hold strictly positive integer values,
+ // regardless of PollyChunkSize's value
+ Value *Args[] = {
+ SourceLocationInfo,
+ GlobalThreadID,
+ Builder.getInt32(int(getSchedType(PollyChunkSize, PollyScheduling))),
+ LB,
+ UB,
+ Inc,
+ ChunkSize};
+
+ Builder.CreateCall(F, Args);
+}
+
+Value *ParallelLoopGeneratorKMP::createCallDispatchNext(Value *GlobalThreadID,
+ Value *IsLastPtr,
+ Value *LBPtr,
+ Value *UBPtr,
+ Value *StridePtr) {
+ const std::string Name =
+ is64BitArch() ? "__kmpc_dispatch_next_8" : "__kmpc_dispatch_next_4";
+ Function *F = M->getFunction(Name);
+ StructType *IdentTy =
+ StructType::getTypeByName(M->getContext(), "struct.ident_t");
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+
+ Type *Params[] = {IdentTy->getPointerTo(),
+ Builder.getInt32Ty(),
+ Builder.getInt32Ty()->getPointerTo(),
+ LongType->getPointerTo(),
+ LongType->getPointerTo(),
+ LongType->getPointerTo()};
+
+ FunctionType *Ty = FunctionType::get(Builder.getInt32Ty(), Params, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Value *Args[] = {SourceLocationInfo, GlobalThreadID, IsLastPtr, LBPtr, UBPtr,
+ StridePtr};
+
+ return Builder.CreateCall(F, Args);
+}
+
+// TODO: This function currently creates a source location dummy. It might be
+// necessary to (actually) provide information, in the future.
+GlobalVariable *ParallelLoopGeneratorKMP::createSourceLocation() {
+ const std::string LocName = ".loc.dummy";
+ GlobalVariable *SourceLocDummy = M->getGlobalVariable(LocName);
+
+ if (SourceLocDummy == nullptr) {
+ const std::string StructName = "struct.ident_t";
+ StructType *IdentTy =
+ StructType::getTypeByName(M->getContext(), StructName);
+
+ // If the ident_t StructType is not available, declare it.
+ // in LLVM-IR: ident_t = type { i32, i32, i32, i32, i8* }
+ if (!IdentTy) {
+ Type *LocMembers[] = {Builder.getInt32Ty(), Builder.getInt32Ty(),
+ Builder.getInt32Ty(), Builder.getInt32Ty(),
+ Builder.getInt8PtrTy()};
+
+ IdentTy =
+ StructType::create(M->getContext(), LocMembers, StructName, false);
+ }
+
+ const auto ArrayType =
+ llvm::ArrayType::get(Builder.getInt8Ty(), /* Length */ 23);
+
+ // Global Variable Definitions
+ GlobalVariable *StrVar =
+ new GlobalVariable(*M, ArrayType, true, GlobalValue::PrivateLinkage,
+ nullptr, ".str.ident");
+ StrVar->setAlignment(llvm::Align(1));
+
+ SourceLocDummy = new GlobalVariable(
+ *M, IdentTy, true, GlobalValue::PrivateLinkage, nullptr, LocName);
+ SourceLocDummy->setAlignment(llvm::Align(8));
+
+ // Constant Definitions
+ Constant *InitStr = ConstantDataArray::getString(
+ M->getContext(), "Source location dummy.", true);
+
+ Constant *StrPtr = static_cast<Constant *>(Builder.CreateInBoundsGEP(
+ ArrayType, StrVar, {Builder.getInt32(0), Builder.getInt32(0)}));
+
+ Constant *LocInitStruct = ConstantStruct::get(
+ IdentTy, {Builder.getInt32(0), Builder.getInt32(0), Builder.getInt32(0),
+ Builder.getInt32(0), StrPtr});
+
+ // Initialize variables
+ StrVar->setInitializer(InitStr);
+ SourceLocDummy->setInitializer(LocInitStruct);
+ }
+
+ return SourceLocDummy;
+}
+
+bool ParallelLoopGeneratorKMP::is64BitArch() {
+ return (LongType->getIntegerBitWidth() == 64);
+}
+
+OMPGeneralSchedulingType ParallelLoopGeneratorKMP::getSchedType(
+ int ChunkSize, OMPGeneralSchedulingType Scheduling) const {
+ if (ChunkSize == 0 && Scheduling == OMPGeneralSchedulingType::StaticChunked)
+ return OMPGeneralSchedulingType::StaticNonChunked;
+
+ return Scheduling;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/ManagedMemoryRewrite.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/ManagedMemoryRewrite.cpp
new file mode 100644
index 00000000000..35456388e56
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/ManagedMemoryRewrite.cpp
@@ -0,0 +1,428 @@
+//===---- ManagedMemoryRewrite.cpp - Rewrite global & malloc'd memory -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Take a module and rewrite:
+// 1. `malloc` -> `polly_mallocManaged`
+// 2. `free` -> `polly_freeManaged`
+// 3. global arrays with initializers -> global arrays that are initialized
+// with a constructor call to
+// `polly_mallocManaged`.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/IRBuilder.h"
+#include "polly/CodeGen/PPCGCodeGeneration.h"
+#include "polly/DependenceInfo.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "polly/ScopDetection.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Analysis/CaptureTracking.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+
+using namespace llvm;
+using namespace polly;
+
+static cl::opt<bool> RewriteAllocas(
+ "polly-acc-rewrite-allocas",
+ cl::desc(
+ "Ask the managed memory rewriter to also rewrite alloca instructions"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> IgnoreLinkageForGlobals(
+ "polly-acc-rewrite-ignore-linkage-for-globals",
+ cl::desc(
+ "By default, we only rewrite globals with internal linkage. This flag "
+ "enables rewriting of globals regardless of linkage"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+#define DEBUG_TYPE "polly-acc-rewrite-managed-memory"
+namespace {
+
+static llvm::Function *getOrCreatePollyMallocManaged(Module &M) {
+ const char *Name = "polly_mallocManaged";
+ Function *F = M.getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ PollyIRBuilder Builder(M.getContext());
+ // TODO: How do I get `size_t`? I assume from DataLayout?
+ FunctionType *Ty = FunctionType::get(Builder.getInt8PtrTy(),
+ {Builder.getInt64Ty()}, false);
+ F = Function::Create(Ty, Linkage, Name, &M);
+ }
+
+ return F;
+}
+
+static llvm::Function *getOrCreatePollyFreeManaged(Module &M) {
+ const char *Name = "polly_freeManaged";
+ Function *F = M.getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ PollyIRBuilder Builder(M.getContext());
+ // TODO: How do I get `size_t`? I assume from DataLayout?
+ FunctionType *Ty =
+ FunctionType::get(Builder.getVoidTy(), {Builder.getInt8PtrTy()}, false);
+ F = Function::Create(Ty, Linkage, Name, &M);
+ }
+
+ return F;
+}
+
+// Expand a constant expression `Cur`, which is used at instruction `Parent`
+// at index `index`.
+// Since a constant expression can expand to multiple instructions, store all
+// the expands into a set called `Expands`.
+// Note that this goes inorder on the constant expression tree.
+// A * ((B * D) + C)
+// will be processed with first A, then B * D, then B, then D, and then C.
+// Though ConstantExprs are not treated as "trees" but as DAGs, since you can
+// have something like this:
+// *
+// / \
+// \ /
+// (D)
+//
+// For the purposes of this expansion, we expand the two occurences of D
+// separately. Therefore, we expand the DAG into the tree:
+// *
+// / \
+// D D
+// TODO: We don't _have_to do this, but this is the simplest solution.
+// We can write a solution that keeps track of which constants have been
+// already expanded.
+static void expandConstantExpr(ConstantExpr *Cur, PollyIRBuilder &Builder,
+ Instruction *Parent, int index,
+ SmallPtrSet<Instruction *, 4> &Expands) {
+ assert(Cur && "invalid constant expression passed");
+ Instruction *I = Cur->getAsInstruction();
+ assert(I && "unable to convert ConstantExpr to Instruction");
+
+ LLVM_DEBUG(dbgs() << "Expanding ConstantExpression: (" << *Cur
+ << ") in Instruction: (" << *I << ")\n";);
+
+ // Invalidate `Cur` so that no one after this point uses `Cur`. Rather,
+ // they should mutate `I`.
+ Cur = nullptr;
+
+ Expands.insert(I);
+ Parent->setOperand(index, I);
+
+ // The things that `Parent` uses (its operands) should be created
+ // before `Parent`.
+ Builder.SetInsertPoint(Parent);
+ Builder.Insert(I);
+
+ for (unsigned i = 0; i < I->getNumOperands(); i++) {
+ Value *Op = I->getOperand(i);
+ assert(isa<Constant>(Op) && "constant must have a constant operand");
+
+ if (ConstantExpr *CExprOp = dyn_cast<ConstantExpr>(Op))
+ expandConstantExpr(CExprOp, Builder, I, i, Expands);
+ }
+}
+
+// Edit all uses of `OldVal` to NewVal` in `Inst`. This will rewrite
+// `ConstantExpr`s that are used in the `Inst`.
+// Note that `replaceAllUsesWith` is insufficient for this purpose because it
+// does not rewrite values in `ConstantExpr`s.
+static void rewriteOldValToNew(Instruction *Inst, Value *OldVal, Value *NewVal,
+ PollyIRBuilder &Builder) {
+
+ // This contains a set of instructions in which OldVal must be replaced.
+ // We start with `Inst`, and we fill it up with the expanded `ConstantExpr`s
+ // from `Inst`s arguments.
+ // We need to go through this process because `replaceAllUsesWith` does not
+ // actually edit `ConstantExpr`s.
+ SmallPtrSet<Instruction *, 4> InstsToVisit = {Inst};
+
+ // Expand all `ConstantExpr`s and place it in `InstsToVisit`.
+ for (unsigned i = 0; i < Inst->getNumOperands(); i++) {
+ Value *Operand = Inst->getOperand(i);
+ if (ConstantExpr *ValueConstExpr = dyn_cast<ConstantExpr>(Operand))
+ expandConstantExpr(ValueConstExpr, Builder, Inst, i, InstsToVisit);
+ }
+
+ // Now visit each instruction and use `replaceUsesOfWith`. We know that
+ // will work because `I` cannot have any `ConstantExpr` within it.
+ for (Instruction *I : InstsToVisit)
+ I->replaceUsesOfWith(OldVal, NewVal);
+}
+
+// Given a value `Current`, return all Instructions that may contain `Current`
+// in an expression.
+// We need this auxiliary function, because if we have a
+// `Constant` that is a user of `V`, we need to recurse into the
+// `Constant`s uses to gather the root instruciton.
+static void getInstructionUsersOfValue(Value *V,
+ SmallVector<Instruction *, 4> &Owners) {
+ if (auto *I = dyn_cast<Instruction>(V)) {
+ Owners.push_back(I);
+ } else {
+ // Anything that is a `User` must be a constant or an instruction.
+ auto *C = cast<Constant>(V);
+ for (Use &CUse : C->uses())
+ getInstructionUsersOfValue(CUse.getUser(), Owners);
+ }
+}
+
+static void
+replaceGlobalArray(Module &M, const DataLayout &DL, GlobalVariable &Array,
+ SmallPtrSet<GlobalVariable *, 4> &ReplacedGlobals) {
+ // We only want arrays.
+ ArrayType *ArrayTy = dyn_cast<ArrayType>(Array.getType()->getElementType());
+ if (!ArrayTy)
+ return;
+ Type *ElemTy = ArrayTy->getElementType();
+ PointerType *ElemPtrTy = ElemTy->getPointerTo();
+
+ // We only wish to replace arrays that are visible in the module they
+ // inhabit. Otherwise, our type edit from [T] to T* would be illegal across
+ // modules.
+ const bool OnlyVisibleInsideModule = Array.hasPrivateLinkage() ||
+ Array.hasInternalLinkage() ||
+ IgnoreLinkageForGlobals;
+ if (!OnlyVisibleInsideModule) {
+ LLVM_DEBUG(
+ dbgs() << "Not rewriting (" << Array
+ << ") to managed memory "
+ "because it could be visible externally. To force rewrite, "
+ "use -polly-acc-rewrite-ignore-linkage-for-globals.\n");
+ return;
+ }
+
+ if (!Array.hasInitializer() ||
+ !isa<ConstantAggregateZero>(Array.getInitializer())) {
+ LLVM_DEBUG(dbgs() << "Not rewriting (" << Array
+ << ") to managed memory "
+ "because it has an initializer which is "
+ "not a zeroinitializer.\n");
+ return;
+ }
+
+ // At this point, we have committed to replacing this array.
+ ReplacedGlobals.insert(&Array);
+
+ std::string NewName = Array.getName().str();
+ NewName += ".toptr";
+ GlobalVariable *ReplacementToArr =
+ cast<GlobalVariable>(M.getOrInsertGlobal(NewName, ElemPtrTy));
+ ReplacementToArr->setInitializer(ConstantPointerNull::get(ElemPtrTy));
+
+ Function *PollyMallocManaged = getOrCreatePollyMallocManaged(M);
+ std::string FnName = Array.getName().str();
+ FnName += ".constructor";
+ PollyIRBuilder Builder(M.getContext());
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), false);
+ const GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ Function *F = Function::Create(Ty, Linkage, FnName, &M);
+ BasicBlock *Start = BasicBlock::Create(M.getContext(), "entry", F);
+ Builder.SetInsertPoint(Start);
+
+ const uint64_t ArraySizeInt = DL.getTypeAllocSize(ArrayTy);
+ Value *ArraySize = Builder.getInt64(ArraySizeInt);
+ ArraySize->setName("array.size");
+
+ Value *AllocatedMemRaw =
+ Builder.CreateCall(PollyMallocManaged, {ArraySize}, "mem.raw");
+ Value *AllocatedMemTyped =
+ Builder.CreatePointerCast(AllocatedMemRaw, ElemPtrTy, "mem.typed");
+ Builder.CreateStore(AllocatedMemTyped, ReplacementToArr);
+ Builder.CreateRetVoid();
+
+ const int Priority = 0;
+ appendToGlobalCtors(M, F, Priority, ReplacementToArr);
+
+ SmallVector<Instruction *, 4> ArrayUserInstructions;
+ // Get all instructions that use array. We need to do this weird thing
+ // because `Constant`s that contain this array neeed to be expanded into
+ // instructions so that we can replace their parameters. `Constant`s cannot
+ // be edited easily, so we choose to convert all `Constant`s to
+ // `Instruction`s and handle all of the uses of `Array` uniformly.
+ for (Use &ArrayUse : Array.uses())
+ getInstructionUsersOfValue(ArrayUse.getUser(), ArrayUserInstructions);
+
+ for (Instruction *UserOfArrayInst : ArrayUserInstructions) {
+
+ Builder.SetInsertPoint(UserOfArrayInst);
+ // <ty>** -> <ty>*
+ Value *ArrPtrLoaded =
+ Builder.CreateLoad(ElemPtrTy, ReplacementToArr, "arrptr.load");
+ // <ty>* -> [ty]*
+ Value *ArrPtrLoadedBitcasted = Builder.CreateBitCast(
+ ArrPtrLoaded, ArrayTy->getPointerTo(), "arrptr.bitcast");
+ rewriteOldValToNew(UserOfArrayInst, &Array, ArrPtrLoadedBitcasted, Builder);
+ }
+}
+
+// We return all `allocas` that may need to be converted to a call to
+// cudaMallocManaged.
+static void getAllocasToBeManaged(Function &F,
+ SmallSet<AllocaInst *, 4> &Allocas) {
+ for (BasicBlock &BB : F) {
+ for (Instruction &I : BB) {
+ auto *Alloca = dyn_cast<AllocaInst>(&I);
+ if (!Alloca)
+ continue;
+ LLVM_DEBUG(dbgs() << "Checking if (" << *Alloca << ") may be captured: ");
+
+ if (PointerMayBeCaptured(Alloca, /* ReturnCaptures */ false,
+ /* StoreCaptures */ true)) {
+ Allocas.insert(Alloca);
+ LLVM_DEBUG(dbgs() << "YES (captured).\n");
+ } else {
+ LLVM_DEBUG(dbgs() << "NO (not captured).\n");
+ }
+ }
+ }
+}
+
+static void rewriteAllocaAsManagedMemory(AllocaInst *Alloca,
+ const DataLayout &DL) {
+ LLVM_DEBUG(dbgs() << "rewriting: (" << *Alloca << ") to managed mem.\n");
+ Module *M = Alloca->getModule();
+ assert(M && "Alloca does not have a module");
+
+ PollyIRBuilder Builder(M->getContext());
+ Builder.SetInsertPoint(Alloca);
+
+ Function *MallocManagedFn =
+ getOrCreatePollyMallocManaged(*Alloca->getModule());
+ const uint64_t Size =
+ DL.getTypeAllocSize(Alloca->getType()->getElementType());
+ Value *SizeVal = Builder.getInt64(Size);
+ Value *RawManagedMem = Builder.CreateCall(MallocManagedFn, {SizeVal});
+ Value *Bitcasted = Builder.CreateBitCast(RawManagedMem, Alloca->getType());
+
+ Function *F = Alloca->getFunction();
+ assert(F && "Alloca has invalid function");
+
+ Bitcasted->takeName(Alloca);
+ Alloca->replaceAllUsesWith(Bitcasted);
+ Alloca->eraseFromParent();
+
+ for (BasicBlock &BB : *F) {
+ ReturnInst *Return = dyn_cast<ReturnInst>(BB.getTerminator());
+ if (!Return)
+ continue;
+ Builder.SetInsertPoint(Return);
+
+ Function *FreeManagedFn = getOrCreatePollyFreeManaged(*M);
+ Builder.CreateCall(FreeManagedFn, {RawManagedMem});
+ }
+}
+
+// Replace all uses of `Old` with `New`, even inside `ConstantExpr`.
+//
+// `replaceAllUsesWith` does replace values in `ConstantExpr`. This function
+// actually does replace it in `ConstantExpr`. The caveat is that if there is
+// a use that is *outside* a function (say, at global declarations), we fail.
+// So, this is meant to be used on values which we know will only be used
+// within functions.
+//
+// This process works by looking through the uses of `Old`. If it finds a
+// `ConstantExpr`, it recursively looks for the owning instruction.
+// Then, it expands all the `ConstantExpr` to instructions and replaces
+// `Old` with `New` in the expanded instructions.
+static void replaceAllUsesAndConstantUses(Value *Old, Value *New,
+ PollyIRBuilder &Builder) {
+ SmallVector<Instruction *, 4> UserInstructions;
+ // Get all instructions that use array. We need to do this weird thing
+ // because `Constant`s that contain this array neeed to be expanded into
+ // instructions so that we can replace their parameters. `Constant`s cannot
+ // be edited easily, so we choose to convert all `Constant`s to
+ // `Instruction`s and handle all of the uses of `Array` uniformly.
+ for (Use &ArrayUse : Old->uses())
+ getInstructionUsersOfValue(ArrayUse.getUser(), UserInstructions);
+
+ for (Instruction *I : UserInstructions)
+ rewriteOldValToNew(I, Old, New, Builder);
+}
+
+class ManagedMemoryRewritePass : public ModulePass {
+public:
+ static char ID;
+ GPUArch Architecture;
+ GPURuntime Runtime;
+
+ ManagedMemoryRewritePass() : ModulePass(ID) {}
+ bool runOnModule(Module &M) override {
+ const DataLayout &DL = M.getDataLayout();
+
+ Function *Malloc = M.getFunction("malloc");
+
+ if (Malloc) {
+ PollyIRBuilder Builder(M.getContext());
+ Function *PollyMallocManaged = getOrCreatePollyMallocManaged(M);
+ assert(PollyMallocManaged && "unable to create polly_mallocManaged");
+
+ replaceAllUsesAndConstantUses(Malloc, PollyMallocManaged, Builder);
+ Malloc->eraseFromParent();
+ }
+
+ Function *Free = M.getFunction("free");
+
+ if (Free) {
+ PollyIRBuilder Builder(M.getContext());
+ Function *PollyFreeManaged = getOrCreatePollyFreeManaged(M);
+ assert(PollyFreeManaged && "unable to create polly_freeManaged");
+
+ replaceAllUsesAndConstantUses(Free, PollyFreeManaged, Builder);
+ Free->eraseFromParent();
+ }
+
+ SmallPtrSet<GlobalVariable *, 4> GlobalsToErase;
+ for (GlobalVariable &Global : M.globals())
+ replaceGlobalArray(M, DL, Global, GlobalsToErase);
+ for (GlobalVariable *G : GlobalsToErase)
+ G->eraseFromParent();
+
+ // Rewrite allocas to cudaMallocs if we are asked to do so.
+ if (RewriteAllocas) {
+ SmallSet<AllocaInst *, 4> AllocasToBeManaged;
+ for (Function &F : M.functions())
+ getAllocasToBeManaged(F, AllocasToBeManaged);
+
+ for (AllocaInst *Alloca : AllocasToBeManaged)
+ rewriteAllocaAsManagedMemory(Alloca, DL);
+ }
+
+ return true;
+ }
+};
+} // namespace
+char ManagedMemoryRewritePass::ID = 42;
+
+Pass *polly::createManagedMemoryRewritePassPass(GPUArch Arch,
+ GPURuntime Runtime) {
+ ManagedMemoryRewritePass *pass = new ManagedMemoryRewritePass();
+ pass->Runtime = Runtime;
+ pass->Architecture = Arch;
+ return pass;
+}
+
+INITIALIZE_PASS_BEGIN(
+ ManagedMemoryRewritePass, "polly-acc-rewrite-managed-memory",
+ "Polly - Rewrite all allocations in heap & data section to managed memory",
+ false, false)
+INITIALIZE_PASS_DEPENDENCY(PPCGCodeGeneration);
+INITIALIZE_PASS_DEPENDENCY(DependenceInfo);
+INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(RegionInfoPass);
+INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(ScopDetectionWrapperPass);
+INITIALIZE_PASS_END(
+ ManagedMemoryRewritePass, "polly-acc-rewrite-managed-memory",
+ "Polly - Rewrite all allocations in heap & data section to managed memory",
+ false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/PPCGCodeGeneration.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/PPCGCodeGeneration.cpp
new file mode 100644
index 00000000000..a10a5312b60
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/PPCGCodeGeneration.cpp
@@ -0,0 +1,3666 @@
+//===------ PPCGCodeGeneration.cpp - Polly Accelerator Code Generation. ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Take a scop created by ScopInfo and map it to GPU code using the ppcg
+// GPU mapping strategy.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/PPCGCodeGeneration.h"
+#include "polly/CodeGen/CodeGeneration.h"
+#include "polly/CodeGen/IslAst.h"
+#include "polly/CodeGen/IslNodeBuilder.h"
+#include "polly/CodeGen/PerfMonitor.h"
+#include "polly/CodeGen/Utils.h"
+#include "polly/DependenceInfo.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "polly/ScopDetection.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/ISLTools.h"
+#include "polly/Support/SCEVValidator.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/IR/IntrinsicsNVPTX.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Linker/Linker.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "isl/union_map.h"
+#include <algorithm>
+
+extern "C" {
+#include "ppcg/cuda.h"
+#include "ppcg/gpu.h"
+#include "ppcg/ppcg.h"
+}
+
+#include "llvm/Support/Debug.h"
+
+using namespace polly;
+using namespace llvm;
+
+#define DEBUG_TYPE "polly-codegen-ppcg"
+
+static cl::opt<bool> DumpSchedule("polly-acc-dump-schedule",
+ cl::desc("Dump the computed GPU Schedule"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ DumpCode("polly-acc-dump-code",
+ cl::desc("Dump C code describing the GPU mapping"), cl::Hidden,
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> DumpKernelIR("polly-acc-dump-kernel-ir",
+ cl::desc("Dump the kernel LLVM-IR"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool> DumpKernelASM("polly-acc-dump-kernel-asm",
+ cl::desc("Dump the kernel assembly code"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool> FastMath("polly-acc-fastmath",
+ cl::desc("Allow unsafe math optimizations"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+static cl::opt<bool> SharedMemory("polly-acc-use-shared",
+ cl::desc("Use shared memory"), cl::Hidden,
+ cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+static cl::opt<bool> PrivateMemory("polly-acc-use-private",
+ cl::desc("Use private memory"), cl::Hidden,
+ cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+bool polly::PollyManagedMemory;
+static cl::opt<bool, true>
+ XManagedMemory("polly-acc-codegen-managed-memory",
+ cl::desc("Generate Host kernel code assuming"
+ " that all memory has been"
+ " declared as managed memory"),
+ cl::location(PollyManagedMemory), cl::Hidden,
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ FailOnVerifyModuleFailure("polly-acc-fail-on-verify-module-failure",
+ cl::desc("Fail and generate a backtrace if"
+ " verifyModule fails on the GPU "
+ " kernel module."),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<std::string> CUDALibDevice(
+ "polly-acc-libdevice", cl::desc("Path to CUDA libdevice"), cl::Hidden,
+ cl::init("/usr/local/cuda/nvvm/libdevice/libdevice.compute_20.10.ll"),
+ cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<std::string>
+ CudaVersion("polly-acc-cuda-version",
+ cl::desc("The CUDA version to compile for"), cl::Hidden,
+ cl::init("sm_30"), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int>
+ MinCompute("polly-acc-mincompute",
+ cl::desc("Minimal number of compute statements to run on GPU."),
+ cl::Hidden, cl::init(10 * 512 * 512));
+
+GPURuntime polly::GPURuntimeChoice;
+static cl::opt<GPURuntime, true> XGPURuntimeChoice(
+ "polly-gpu-runtime", cl::desc("The GPU Runtime API to target"),
+ cl::values(clEnumValN(GPURuntime::CUDA, "libcudart",
+ "use the CUDA Runtime API"),
+ clEnumValN(GPURuntime::OpenCL, "libopencl",
+ "use the OpenCL Runtime API")),
+ cl::location(polly::GPURuntimeChoice), cl::init(GPURuntime::CUDA),
+ cl::ZeroOrMore, cl::cat(PollyCategory));
+
+GPUArch polly::GPUArchChoice;
+static cl::opt<GPUArch, true>
+ XGPUArchChoice("polly-gpu-arch", cl::desc("The GPU Architecture to target"),
+ cl::values(clEnumValN(GPUArch::NVPTX64, "nvptx64",
+ "target NVIDIA 64-bit architecture"),
+ clEnumValN(GPUArch::SPIR32, "spir32",
+ "target SPIR 32-bit architecture"),
+ clEnumValN(GPUArch::SPIR64, "spir64",
+ "target SPIR 64-bit architecture")),
+ cl::location(polly::GPUArchChoice),
+ cl::init(GPUArch::NVPTX64), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+extern bool polly::PerfMonitoring;
+
+/// Return a unique name for a Scop, which is the scop region with the
+/// function name.
+std::string getUniqueScopName(const Scop *S) {
+ return "Scop Region: " + S->getNameStr() +
+ " | Function: " + std::string(S->getFunction().getName());
+}
+
+/// Used to store information PPCG wants for kills. This information is
+/// used by live range reordering.
+///
+/// @see computeLiveRangeReordering
+/// @see GPUNodeBuilder::createPPCGScop
+/// @see GPUNodeBuilder::createPPCGProg
+struct MustKillsInfo {
+ /// Collection of all kill statements that will be sequenced at the end of
+ /// PPCGScop->schedule.
+ ///
+ /// The nodes in `KillsSchedule` will be merged using `isl_schedule_set`
+ /// which merges schedules in *arbitrary* order.
+ /// (we don't care about the order of the kills anyway).
+ isl::schedule KillsSchedule;
+ /// Map from kill statement instances to scalars that need to be
+ /// killed.
+ ///
+ /// We currently derive kill information for:
+ /// 1. phi nodes. PHI nodes are not alive outside the scop and can
+ /// consequently all be killed.
+ /// 2. Scalar arrays that are not used outside the Scop. This is
+ /// checked by `isScalarUsesContainedInScop`.
+ /// [params] -> { [Stmt_phantom[] -> ref_phantom[]] -> scalar_to_kill[] }
+ isl::union_map TaggedMustKills;
+
+ /// Tagged must kills stripped of the tags.
+ /// [params] -> { Stmt_phantom[] -> scalar_to_kill[] }
+ isl::union_map MustKills;
+
+ MustKillsInfo() : KillsSchedule() {}
+};
+
+/// Check if SAI's uses are entirely contained within Scop S.
+/// If a scalar is used only with a Scop, we are free to kill it, as no data
+/// can flow in/out of the value any more.
+/// @see computeMustKillsInfo
+static bool isScalarUsesContainedInScop(const Scop &S,
+ const ScopArrayInfo *SAI) {
+ assert(SAI->isValueKind() && "this function only deals with scalars."
+ " Dealing with arrays required alias analysis");
+
+ const Region &R = S.getRegion();
+ for (User *U : SAI->getBasePtr()->users()) {
+ Instruction *I = dyn_cast<Instruction>(U);
+ assert(I && "invalid user of scop array info");
+ if (!R.contains(I))
+ return false;
+ }
+ return true;
+}
+
+/// Compute must-kills needed to enable live range reordering with PPCG.
+///
+/// @params S The Scop to compute live range reordering information
+/// @returns live range reordering information that can be used to setup
+/// PPCG.
+static MustKillsInfo computeMustKillsInfo(const Scop &S) {
+ const isl::space ParamSpace = S.getParamSpace();
+ MustKillsInfo Info;
+
+ // 1. Collect all ScopArrayInfo that satisfy *any* of the criteria:
+ // 1.1 phi nodes in scop.
+ // 1.2 scalars that are only used within the scop
+ SmallVector<isl::id, 4> KillMemIds;
+ for (ScopArrayInfo *SAI : S.arrays()) {
+ if (SAI->isPHIKind() ||
+ (SAI->isValueKind() && isScalarUsesContainedInScop(S, SAI)))
+ KillMemIds.push_back(isl::manage(SAI->getBasePtrId().release()));
+ }
+
+ Info.TaggedMustKills = isl::union_map::empty(ParamSpace.ctx());
+ Info.MustKills = isl::union_map::empty(ParamSpace.ctx());
+
+ // Initialising KillsSchedule to `isl_set_empty` creates an empty node in the
+ // schedule:
+ // - filter: "[control] -> { }"
+ // So, we choose to not create this to keep the output a little nicer,
+ // at the cost of some code complexity.
+ Info.KillsSchedule = {};
+
+ for (isl::id &ToKillId : KillMemIds) {
+ isl::id KillStmtId = isl::id::alloc(
+ S.getIslCtx(),
+ std::string("SKill_phantom_").append(ToKillId.get_name()), nullptr);
+
+ // NOTE: construction of tagged_must_kill:
+ // 2. We need to construct a map:
+ // [param] -> { [Stmt_phantom[] -> ref_phantom[]] -> scalar_to_kill[] }
+ // To construct this, we use `isl_map_domain_product` on 2 maps`:
+ // 2a. StmtToScalar:
+ // [param] -> { Stmt_phantom[] -> scalar_to_kill[] }
+ // 2b. PhantomRefToScalar:
+ // [param] -> { ref_phantom[] -> scalar_to_kill[] }
+ //
+ // Combining these with `isl_map_domain_product` gives us
+ // TaggedMustKill:
+ // [param] -> { [Stmt[] -> phantom_ref[]] -> scalar_to_kill[] }
+
+ // 2a. [param] -> { Stmt[] -> scalar_to_kill[] }
+ isl::map StmtToScalar = isl::map::universe(ParamSpace);
+ StmtToScalar = StmtToScalar.set_tuple_id(isl::dim::in, isl::id(KillStmtId));
+ StmtToScalar = StmtToScalar.set_tuple_id(isl::dim::out, isl::id(ToKillId));
+
+ isl::id PhantomRefId = isl::id::alloc(
+ S.getIslCtx(), std::string("ref_phantom") + ToKillId.get_name(),
+ nullptr);
+
+ // 2b. [param] -> { phantom_ref[] -> scalar_to_kill[] }
+ isl::map PhantomRefToScalar = isl::map::universe(ParamSpace);
+ PhantomRefToScalar =
+ PhantomRefToScalar.set_tuple_id(isl::dim::in, PhantomRefId);
+ PhantomRefToScalar =
+ PhantomRefToScalar.set_tuple_id(isl::dim::out, ToKillId);
+
+ // 2. [param] -> { [Stmt[] -> phantom_ref[]] -> scalar_to_kill[] }
+ isl::map TaggedMustKill = StmtToScalar.domain_product(PhantomRefToScalar);
+ Info.TaggedMustKills = Info.TaggedMustKills.unite(TaggedMustKill);
+
+ // 2. [param] -> { Stmt[] -> scalar_to_kill[] }
+ Info.MustKills = Info.TaggedMustKills.domain_factor_domain();
+
+ // 3. Create the kill schedule of the form:
+ // "[param] -> { Stmt_phantom[] }"
+ // Then add this to Info.KillsSchedule.
+ isl::space KillStmtSpace = ParamSpace;
+ KillStmtSpace = KillStmtSpace.set_tuple_id(isl::dim::set, KillStmtId);
+ isl::union_set KillStmtDomain = isl::set::universe(KillStmtSpace);
+
+ isl::schedule KillSchedule = isl::schedule::from_domain(KillStmtDomain);
+ if (!Info.KillsSchedule.is_null())
+ Info.KillsSchedule = isl::manage(
+ isl_schedule_set(Info.KillsSchedule.release(), KillSchedule.copy()));
+ else
+ Info.KillsSchedule = KillSchedule;
+ }
+
+ return Info;
+}
+
+/// Create the ast expressions for a ScopStmt.
+///
+/// This function is a callback for to generate the ast expressions for each
+/// of the scheduled ScopStmts.
+static __isl_give isl_id_to_ast_expr *pollyBuildAstExprForStmt(
+ void *StmtT, __isl_take isl_ast_build *Build_C,
+ isl_multi_pw_aff *(*FunctionIndex)(__isl_take isl_multi_pw_aff *MPA,
+ isl_id *Id, void *User),
+ void *UserIndex,
+ isl_ast_expr *(*FunctionExpr)(isl_ast_expr *Expr, isl_id *Id, void *User),
+ void *UserExpr) {
+
+ ScopStmt *Stmt = (ScopStmt *)StmtT;
+
+ if (!Stmt || !Build_C)
+ return NULL;
+
+ isl::ast_build Build = isl::manage_copy(Build_C);
+ isl::ctx Ctx = Build.ctx();
+ isl::id_to_ast_expr RefToExpr = isl::id_to_ast_expr::alloc(Ctx, 0);
+
+ Stmt->setAstBuild(Build);
+
+ for (MemoryAccess *Acc : *Stmt) {
+ isl::map AddrFunc = Acc->getAddressFunction();
+ AddrFunc = AddrFunc.intersect_domain(Stmt->getDomain());
+
+ isl::id RefId = Acc->getId();
+ isl::pw_multi_aff PMA = isl::pw_multi_aff::from_map(AddrFunc);
+
+ isl::multi_pw_aff MPA = isl::multi_pw_aff(PMA);
+ MPA = MPA.coalesce();
+ MPA = isl::manage(FunctionIndex(MPA.release(), RefId.get(), UserIndex));
+
+ isl::ast_expr Access = Build.access_from(MPA);
+ Access = isl::manage(FunctionExpr(Access.release(), RefId.get(), UserExpr));
+ RefToExpr = RefToExpr.set(RefId, Access);
+ }
+
+ return RefToExpr.release();
+}
+
+/// Given a LLVM Type, compute its size in bytes,
+static int computeSizeInBytes(const Type *T) {
+ int bytes = T->getPrimitiveSizeInBits() / 8;
+ if (bytes == 0)
+ bytes = T->getScalarSizeInBits() / 8;
+ return bytes;
+}
+
+/// Generate code for a GPU specific isl AST.
+///
+/// The GPUNodeBuilder augments the general existing IslNodeBuilder, which
+/// generates code for general-purpose AST nodes, with special functionality
+/// for generating GPU specific user nodes.
+///
+/// @see GPUNodeBuilder::createUser
+class GPUNodeBuilder : public IslNodeBuilder {
+public:
+ GPUNodeBuilder(PollyIRBuilder &Builder, ScopAnnotator &Annotator,
+ const DataLayout &DL, LoopInfo &LI, ScalarEvolution &SE,
+ DominatorTree &DT, Scop &S, BasicBlock *StartBlock,
+ gpu_prog *Prog, GPURuntime Runtime, GPUArch Arch)
+ : IslNodeBuilder(Builder, Annotator, DL, LI, SE, DT, S, StartBlock),
+ Prog(Prog), Runtime(Runtime), Arch(Arch) {
+ getExprBuilder().setIDToSAI(&IDToSAI);
+ }
+
+ /// Create after-run-time-check initialization code.
+ void initializeAfterRTH();
+
+ /// Finalize the generated scop.
+ void finalize() override;
+
+ /// Track if the full build process was successful.
+ ///
+ /// This value is set to false, if throughout the build process an error
+ /// occurred which prevents us from generating valid GPU code.
+ bool BuildSuccessful = true;
+
+ /// The maximal number of loops surrounding a sequential kernel.
+ unsigned DeepestSequential = 0;
+
+ /// The maximal number of loops surrounding a parallel kernel.
+ unsigned DeepestParallel = 0;
+
+ /// Return the name to set for the ptx_kernel.
+ std::string getKernelFuncName(int Kernel_id);
+
+private:
+ /// A vector of array base pointers for which a new ScopArrayInfo was created.
+ ///
+ /// This vector is used to delete the ScopArrayInfo when it is not needed any
+ /// more.
+ std::vector<Value *> LocalArrays;
+
+ /// A map from ScopArrays to their corresponding device allocations.
+ std::map<ScopArrayInfo *, Value *> DeviceAllocations;
+
+ /// The current GPU context.
+ Value *GPUContext;
+
+ /// The set of isl_ids allocated in the kernel
+ std::vector<isl_id *> KernelIds;
+
+ /// A module containing GPU code.
+ ///
+ /// This pointer is only set in case we are currently generating GPU code.
+ std::unique_ptr<Module> GPUModule;
+
+ /// The GPU program we generate code for.
+ gpu_prog *Prog;
+
+ /// The GPU Runtime implementation to use (OpenCL or CUDA).
+ GPURuntime Runtime;
+
+ /// The GPU Architecture to target.
+ GPUArch Arch;
+
+ /// Class to free isl_ids.
+ class IslIdDeleter {
+ public:
+ void operator()(__isl_take isl_id *Id) { isl_id_free(Id); };
+ };
+
+ /// A set containing all isl_ids allocated in a GPU kernel.
+ ///
+ /// By releasing this set all isl_ids will be freed.
+ std::set<std::unique_ptr<isl_id, IslIdDeleter>> KernelIDs;
+
+ IslExprBuilder::IDToScopArrayInfoTy IDToSAI;
+
+ /// Create code for user-defined AST nodes.
+ ///
+ /// These AST nodes can be of type:
+ ///
+ /// - ScopStmt: A computational statement (TODO)
+ /// - Kernel: A GPU kernel call (TODO)
+ /// - Data-Transfer: A GPU <-> CPU data-transfer
+ /// - In-kernel synchronization
+ /// - In-kernel memory copy statement
+ ///
+ /// @param UserStmt The ast node to generate code for.
+ void createUser(__isl_take isl_ast_node *UserStmt) override;
+
+ void createFor(__isl_take isl_ast_node *Node) override;
+
+ enum DataDirection { HOST_TO_DEVICE, DEVICE_TO_HOST };
+
+ /// Create code for a data transfer statement
+ ///
+ /// @param TransferStmt The data transfer statement.
+ /// @param Direction The direction in which to transfer data.
+ void createDataTransfer(__isl_take isl_ast_node *TransferStmt,
+ enum DataDirection Direction);
+
+ /// Find llvm::Values referenced in GPU kernel.
+ ///
+ /// @param Kernel The kernel to scan for llvm::Values
+ ///
+ /// @returns A tuple, whose:
+ /// - First element contains the set of values referenced by the
+ /// kernel
+ /// - Second element contains the set of functions referenced by the
+ /// kernel. All functions in the set satisfy
+ /// `isValidFunctionInKernel`.
+ /// - Third element contains loops that have induction variables
+ /// which are used in the kernel, *and* these loops are *neither*
+ /// in the scop, nor do they immediately surroung the Scop.
+ /// See [Code generation of induction variables of loops outside
+ /// Scops]
+ std::tuple<SetVector<Value *>, SetVector<Function *>, SetVector<const Loop *>,
+ isl::space>
+ getReferencesInKernel(ppcg_kernel *Kernel);
+
+ /// Compute the sizes of the execution grid for a given kernel.
+ ///
+ /// @param Kernel The kernel to compute grid sizes for.
+ ///
+ /// @returns A tuple with grid sizes for X and Y dimension
+ std::tuple<Value *, Value *> getGridSizes(ppcg_kernel *Kernel);
+
+ /// Get the managed array pointer for sending host pointers to the device.
+ /// \note
+ /// This is to be used only with managed memory
+ Value *getManagedDeviceArray(gpu_array_info *Array, ScopArrayInfo *ArrayInfo);
+
+ /// Compute the sizes of the thread blocks for a given kernel.
+ ///
+ /// @param Kernel The kernel to compute thread block sizes for.
+ ///
+ /// @returns A tuple with thread block sizes for X, Y, and Z dimensions.
+ std::tuple<Value *, Value *, Value *> getBlockSizes(ppcg_kernel *Kernel);
+
+ /// Store a specific kernel launch parameter in the array of kernel launch
+ /// parameters.
+ ///
+ /// @param Parameters The list of parameters in which to store.
+ /// @param Param The kernel launch parameter to store.
+ /// @param Index The index in the parameter list, at which to store the
+ /// parameter.
+ void insertStoreParameter(Instruction *Parameters, Instruction *Param,
+ int Index);
+
+ /// Create kernel launch parameters.
+ ///
+ /// @param Kernel The kernel to create parameters for.
+ /// @param F The kernel function that has been created.
+ /// @param SubtreeValues The set of llvm::Values referenced by this kernel.
+ ///
+ /// @returns A stack allocated array with pointers to the parameter
+ /// values that are passed to the kernel.
+ Value *createLaunchParameters(ppcg_kernel *Kernel, Function *F,
+ SetVector<Value *> SubtreeValues);
+
+ /// Create declarations for kernel variable.
+ ///
+ /// This includes shared memory declarations.
+ ///
+ /// @param Kernel The kernel definition to create variables for.
+ /// @param FN The function into which to generate the variables.
+ void createKernelVariables(ppcg_kernel *Kernel, Function *FN);
+
+ /// Add CUDA annotations to module.
+ ///
+ /// Add a set of CUDA annotations that declares the maximal block dimensions
+ /// that will be used to execute the CUDA kernel. This allows the NVIDIA
+ /// PTX compiler to bound the number of allocated registers to ensure the
+ /// resulting kernel is known to run with up to as many block dimensions
+ /// as specified here.
+ ///
+ /// @param M The module to add the annotations to.
+ /// @param BlockDimX The size of block dimension X.
+ /// @param BlockDimY The size of block dimension Y.
+ /// @param BlockDimZ The size of block dimension Z.
+ void addCUDAAnnotations(Module *M, Value *BlockDimX, Value *BlockDimY,
+ Value *BlockDimZ);
+
+ /// Create GPU kernel.
+ ///
+ /// Code generate the kernel described by @p KernelStmt.
+ ///
+ /// @param KernelStmt The ast node to generate kernel code for.
+ void createKernel(__isl_take isl_ast_node *KernelStmt);
+
+ /// Generate code that computes the size of an array.
+ ///
+ /// @param Array The array for which to compute a size.
+ Value *getArraySize(gpu_array_info *Array);
+
+ /// Generate code to compute the minimal offset at which an array is accessed.
+ ///
+ /// The offset of an array is the minimal array location accessed in a scop.
+ ///
+ /// Example:
+ ///
+ /// for (long i = 0; i < 100; i++)
+ /// A[i + 42] += ...
+ ///
+ /// getArrayOffset(A) results in 42.
+ ///
+ /// @param Array The array for which to compute the offset.
+ /// @returns An llvm::Value that contains the offset of the array.
+ Value *getArrayOffset(gpu_array_info *Array);
+
+ /// Prepare the kernel arguments for kernel code generation
+ ///
+ /// @param Kernel The kernel to generate code for.
+ /// @param FN The function created for the kernel.
+ void prepareKernelArguments(ppcg_kernel *Kernel, Function *FN);
+
+ /// Create kernel function.
+ ///
+ /// Create a kernel function located in a newly created module that can serve
+ /// as target for device code generation. Set the Builder to point to the
+ /// start block of this newly created function.
+ ///
+ /// @param Kernel The kernel to generate code for.
+ /// @param SubtreeValues The set of llvm::Values referenced by this kernel.
+ /// @param SubtreeFunctions The set of llvm::Functions referenced by this
+ /// kernel.
+ void createKernelFunction(ppcg_kernel *Kernel,
+ SetVector<Value *> &SubtreeValues,
+ SetVector<Function *> &SubtreeFunctions);
+
+ /// Create the declaration of a kernel function.
+ ///
+ /// The kernel function takes as arguments:
+ ///
+ /// - One i8 pointer for each external array reference used in the kernel.
+ /// - Host iterators
+ /// - Parameters
+ /// - Other LLVM Value references (TODO)
+ ///
+ /// @param Kernel The kernel to generate the function declaration for.
+ /// @param SubtreeValues The set of llvm::Values referenced by this kernel.
+ ///
+ /// @returns The newly declared function.
+ Function *createKernelFunctionDecl(ppcg_kernel *Kernel,
+ SetVector<Value *> &SubtreeValues);
+
+ /// Insert intrinsic functions to obtain thread and block ids.
+ ///
+ /// @param The kernel to generate the intrinsic functions for.
+ void insertKernelIntrinsics(ppcg_kernel *Kernel);
+
+ /// Insert function calls to retrieve the SPIR group/local ids.
+ ///
+ /// @param Kernel The kernel to generate the function calls for.
+ /// @param SizeTypeIs64Bit Whether size_t of the openCl device is 64bit.
+ void insertKernelCallsSPIR(ppcg_kernel *Kernel, bool SizeTypeIs64bit);
+
+ /// Setup the creation of functions referenced by the GPU kernel.
+ ///
+ /// 1. Create new function declarations in GPUModule which are the same as
+ /// SubtreeFunctions.
+ ///
+ /// 2. Populate IslNodeBuilder::ValueMap with mappings from
+ /// old functions (that come from the original module) to new functions
+ /// (that are created within GPUModule). That way, we generate references
+ /// to the correct function (in GPUModule) in BlockGenerator.
+ ///
+ /// @see IslNodeBuilder::ValueMap
+ /// @see BlockGenerator::GlobalMap
+ /// @see BlockGenerator::getNewValue
+ /// @see GPUNodeBuilder::getReferencesInKernel.
+ ///
+ /// @param SubtreeFunctions The set of llvm::Functions referenced by
+ /// this kernel.
+ void setupKernelSubtreeFunctions(SetVector<Function *> SubtreeFunctions);
+
+ /// Create a global-to-shared or shared-to-global copy statement.
+ ///
+ /// @param CopyStmt The copy statement to generate code for
+ void createKernelCopy(ppcg_kernel_stmt *CopyStmt);
+
+ /// Create code for a ScopStmt called in @p Expr.
+ ///
+ /// @param Expr The expression containing the call.
+ /// @param KernelStmt The kernel statement referenced in the call.
+ void createScopStmt(isl_ast_expr *Expr, ppcg_kernel_stmt *KernelStmt);
+
+ /// Create an in-kernel synchronization call.
+ void createKernelSync();
+
+ /// Create a PTX assembly string for the current GPU kernel.
+ ///
+ /// @returns A string containing the corresponding PTX assembly code.
+ std::string createKernelASM();
+
+ /// Remove references from the dominator tree to the kernel function @p F.
+ ///
+ /// @param F The function to remove references to.
+ void clearDominators(Function *F);
+
+ /// Remove references from scalar evolution to the kernel function @p F.
+ ///
+ /// @param F The function to remove references to.
+ void clearScalarEvolution(Function *F);
+
+ /// Remove references from loop info to the kernel function @p F.
+ ///
+ /// @param F The function to remove references to.
+ void clearLoops(Function *F);
+
+ /// Check if the scop requires to be linked with CUDA's libdevice.
+ bool requiresCUDALibDevice();
+
+ /// Link with the NVIDIA libdevice library (if needed and available).
+ void addCUDALibDevice();
+
+ /// Finalize the generation of the kernel function.
+ ///
+ /// Free the LLVM-IR module corresponding to the kernel and -- if requested --
+ /// dump its IR to stderr.
+ ///
+ /// @returns The Assembly string of the kernel.
+ std::string finalizeKernelFunction();
+
+ /// Finalize the generation of the kernel arguments.
+ ///
+ /// This function ensures that not-read-only scalars used in a kernel are
+ /// stored back to the global memory location they are backed with before
+ /// the kernel terminates.
+ ///
+ /// @params Kernel The kernel to finalize kernel arguments for.
+ void finalizeKernelArguments(ppcg_kernel *Kernel);
+
+ /// Create code that allocates memory to store arrays on device.
+ void allocateDeviceArrays();
+
+ /// Create code to prepare the managed device pointers.
+ void prepareManagedDeviceArrays();
+
+ /// Free all allocated device arrays.
+ void freeDeviceArrays();
+
+ /// Create a call to initialize the GPU context.
+ ///
+ /// @returns A pointer to the newly initialized context.
+ Value *createCallInitContext();
+
+ /// Create a call to get the device pointer for a kernel allocation.
+ ///
+ /// @param Allocation The Polly GPU allocation
+ ///
+ /// @returns The device parameter corresponding to this allocation.
+ Value *createCallGetDevicePtr(Value *Allocation);
+
+ /// Create a call to free the GPU context.
+ ///
+ /// @param Context A pointer to an initialized GPU context.
+ void createCallFreeContext(Value *Context);
+
+ /// Create a call to allocate memory on the device.
+ ///
+ /// @param Size The size of memory to allocate
+ ///
+ /// @returns A pointer that identifies this allocation.
+ Value *createCallAllocateMemoryForDevice(Value *Size);
+
+ /// Create a call to free a device array.
+ ///
+ /// @param Array The device array to free.
+ void createCallFreeDeviceMemory(Value *Array);
+
+ /// Create a call to copy data from host to device.
+ ///
+ /// @param HostPtr A pointer to the host data that should be copied.
+ /// @param DevicePtr A device pointer specifying the location to copy to.
+ void createCallCopyFromHostToDevice(Value *HostPtr, Value *DevicePtr,
+ Value *Size);
+
+ /// Create a call to copy data from device to host.
+ ///
+ /// @param DevicePtr A pointer to the device data that should be copied.
+ /// @param HostPtr A host pointer specifying the location to copy to.
+ void createCallCopyFromDeviceToHost(Value *DevicePtr, Value *HostPtr,
+ Value *Size);
+
+ /// Create a call to synchronize Host & Device.
+ /// \note
+ /// This is to be used only with managed memory.
+ void createCallSynchronizeDevice();
+
+ /// Create a call to get a kernel from an assembly string.
+ ///
+ /// @param Buffer The string describing the kernel.
+ /// @param Entry The name of the kernel function to call.
+ ///
+ /// @returns A pointer to a kernel object
+ Value *createCallGetKernel(Value *Buffer, Value *Entry);
+
+ /// Create a call to free a GPU kernel.
+ ///
+ /// @param GPUKernel THe kernel to free.
+ void createCallFreeKernel(Value *GPUKernel);
+
+ /// Create a call to launch a GPU kernel.
+ ///
+ /// @param GPUKernel The kernel to launch.
+ /// @param GridDimX The size of the first grid dimension.
+ /// @param GridDimY The size of the second grid dimension.
+ /// @param GridBlockX The size of the first block dimension.
+ /// @param GridBlockY The size of the second block dimension.
+ /// @param GridBlockZ The size of the third block dimension.
+ /// @param Parameters A pointer to an array that contains itself pointers to
+ /// the parameter values passed for each kernel argument.
+ void createCallLaunchKernel(Value *GPUKernel, Value *GridDimX,
+ Value *GridDimY, Value *BlockDimX,
+ Value *BlockDimY, Value *BlockDimZ,
+ Value *Parameters);
+};
+
+std::string GPUNodeBuilder::getKernelFuncName(int Kernel_id) {
+ return "FUNC_" + S.getFunction().getName().str() + "_SCOP_" +
+ std::to_string(S.getID()) + "_KERNEL_" + std::to_string(Kernel_id);
+}
+
+void GPUNodeBuilder::initializeAfterRTH() {
+ BasicBlock *NewBB = SplitBlock(Builder.GetInsertBlock(),
+ &*Builder.GetInsertPoint(), &DT, &LI);
+ NewBB->setName("polly.acc.initialize");
+ Builder.SetInsertPoint(&NewBB->front());
+
+ GPUContext = createCallInitContext();
+
+ if (!PollyManagedMemory)
+ allocateDeviceArrays();
+ else
+ prepareManagedDeviceArrays();
+}
+
+void GPUNodeBuilder::finalize() {
+ if (!PollyManagedMemory)
+ freeDeviceArrays();
+
+ createCallFreeContext(GPUContext);
+ IslNodeBuilder::finalize();
+}
+
+void GPUNodeBuilder::allocateDeviceArrays() {
+ assert(!PollyManagedMemory &&
+ "Managed memory will directly send host pointers "
+ "to the kernel. There is no need for device arrays");
+ isl_ast_build *Build = isl_ast_build_from_context(S.getContext().release());
+
+ for (int i = 0; i < Prog->n_array; ++i) {
+ gpu_array_info *Array = &Prog->array[i];
+ auto *ScopArray = (ScopArrayInfo *)Array->user;
+ std::string DevArrayName("p_dev_array_");
+ DevArrayName.append(Array->name);
+
+ Value *ArraySize = getArraySize(Array);
+ Value *Offset = getArrayOffset(Array);
+ if (Offset)
+ ArraySize = Builder.CreateSub(
+ ArraySize,
+ Builder.CreateMul(Offset,
+ Builder.getInt64(ScopArray->getElemSizeInBytes())));
+ const SCEV *SizeSCEV = SE.getSCEV(ArraySize);
+ // It makes no sense to have an array of size 0. The CUDA API will
+ // throw an error anyway if we invoke `cuMallocManaged` with size `0`. We
+ // choose to be defensive and catch this at the compile phase. It is
+ // most likely that we are doing something wrong with size computation.
+ if (SizeSCEV->isZero()) {
+ errs() << getUniqueScopName(&S)
+ << " has computed array size 0: " << *ArraySize
+ << " | for array: " << *(ScopArray->getBasePtr())
+ << ". This is illegal, exiting.\n";
+ report_fatal_error("array size was computed to be 0");
+ }
+
+ Value *DevArray = createCallAllocateMemoryForDevice(ArraySize);
+ DevArray->setName(DevArrayName);
+ DeviceAllocations[ScopArray] = DevArray;
+ }
+
+ isl_ast_build_free(Build);
+}
+
+void GPUNodeBuilder::prepareManagedDeviceArrays() {
+ assert(PollyManagedMemory &&
+ "Device array most only be prepared in managed-memory mode");
+ for (int i = 0; i < Prog->n_array; ++i) {
+ gpu_array_info *Array = &Prog->array[i];
+ ScopArrayInfo *ScopArray = (ScopArrayInfo *)Array->user;
+ Value *HostPtr;
+
+ if (gpu_array_is_scalar(Array))
+ HostPtr = BlockGen.getOrCreateAlloca(ScopArray);
+ else
+ HostPtr = ScopArray->getBasePtr();
+ HostPtr = getLatestValue(HostPtr);
+
+ Value *Offset = getArrayOffset(Array);
+ if (Offset) {
+ HostPtr = Builder.CreatePointerCast(
+ HostPtr, ScopArray->getElementType()->getPointerTo());
+ HostPtr = Builder.CreateGEP(ScopArray->getElementType(), HostPtr, Offset);
+ }
+
+ HostPtr = Builder.CreatePointerCast(HostPtr, Builder.getInt8PtrTy());
+ DeviceAllocations[ScopArray] = HostPtr;
+ }
+}
+
+void GPUNodeBuilder::addCUDAAnnotations(Module *M, Value *BlockDimX,
+ Value *BlockDimY, Value *BlockDimZ) {
+ auto AnnotationNode = M->getOrInsertNamedMetadata("nvvm.annotations");
+
+ for (auto &F : *M) {
+ if (F.getCallingConv() != CallingConv::PTX_Kernel)
+ continue;
+
+ Value *V[] = {BlockDimX, BlockDimY, BlockDimZ};
+
+ Metadata *Elements[] = {
+ ValueAsMetadata::get(&F), MDString::get(M->getContext(), "maxntidx"),
+ ValueAsMetadata::get(V[0]), MDString::get(M->getContext(), "maxntidy"),
+ ValueAsMetadata::get(V[1]), MDString::get(M->getContext(), "maxntidz"),
+ ValueAsMetadata::get(V[2]),
+ };
+ MDNode *Node = MDNode::get(M->getContext(), Elements);
+ AnnotationNode->addOperand(Node);
+ }
+}
+
+void GPUNodeBuilder::freeDeviceArrays() {
+ assert(!PollyManagedMemory && "Managed memory does not use device arrays");
+ for (auto &Array : DeviceAllocations)
+ createCallFreeDeviceMemory(Array.second);
+}
+
+Value *GPUNodeBuilder::createCallGetKernel(Value *Buffer, Value *Entry) {
+ const char *Name = "polly_getKernel";
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ Args.push_back(Builder.getInt8PtrTy());
+ Args.push_back(Builder.getInt8PtrTy());
+ FunctionType *Ty = FunctionType::get(Builder.getInt8PtrTy(), Args, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ return Builder.CreateCall(F, {Buffer, Entry});
+}
+
+Value *GPUNodeBuilder::createCallGetDevicePtr(Value *Allocation) {
+ const char *Name = "polly_getDevicePtr";
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ Args.push_back(Builder.getInt8PtrTy());
+ FunctionType *Ty = FunctionType::get(Builder.getInt8PtrTy(), Args, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ return Builder.CreateCall(F, {Allocation});
+}
+
+void GPUNodeBuilder::createCallLaunchKernel(Value *GPUKernel, Value *GridDimX,
+ Value *GridDimY, Value *BlockDimX,
+ Value *BlockDimY, Value *BlockDimZ,
+ Value *Parameters) {
+ const char *Name = "polly_launchKernel";
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ Args.push_back(Builder.getInt8PtrTy());
+ Args.push_back(Builder.getInt32Ty());
+ Args.push_back(Builder.getInt32Ty());
+ Args.push_back(Builder.getInt32Ty());
+ Args.push_back(Builder.getInt32Ty());
+ Args.push_back(Builder.getInt32Ty());
+ Args.push_back(Builder.getInt8PtrTy());
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Builder.CreateCall(F, {GPUKernel, GridDimX, GridDimY, BlockDimX, BlockDimY,
+ BlockDimZ, Parameters});
+}
+
+void GPUNodeBuilder::createCallFreeKernel(Value *GPUKernel) {
+ const char *Name = "polly_freeKernel";
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ Args.push_back(Builder.getInt8PtrTy());
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Builder.CreateCall(F, {GPUKernel});
+}
+
+void GPUNodeBuilder::createCallFreeDeviceMemory(Value *Array) {
+ assert(!PollyManagedMemory &&
+ "Managed memory does not allocate or free memory "
+ "for device");
+ const char *Name = "polly_freeDeviceMemory";
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ Args.push_back(Builder.getInt8PtrTy());
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Builder.CreateCall(F, {Array});
+}
+
+Value *GPUNodeBuilder::createCallAllocateMemoryForDevice(Value *Size) {
+ assert(!PollyManagedMemory &&
+ "Managed memory does not allocate or free memory "
+ "for device");
+ const char *Name = "polly_allocateMemoryForDevice";
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ Args.push_back(Builder.getInt64Ty());
+ FunctionType *Ty = FunctionType::get(Builder.getInt8PtrTy(), Args, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ return Builder.CreateCall(F, {Size});
+}
+
+void GPUNodeBuilder::createCallCopyFromHostToDevice(Value *HostData,
+ Value *DeviceData,
+ Value *Size) {
+ assert(!PollyManagedMemory &&
+ "Managed memory does not transfer memory between "
+ "device and host");
+ const char *Name = "polly_copyFromHostToDevice";
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ Args.push_back(Builder.getInt8PtrTy());
+ Args.push_back(Builder.getInt8PtrTy());
+ Args.push_back(Builder.getInt64Ty());
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Builder.CreateCall(F, {HostData, DeviceData, Size});
+}
+
+void GPUNodeBuilder::createCallCopyFromDeviceToHost(Value *DeviceData,
+ Value *HostData,
+ Value *Size) {
+ assert(!PollyManagedMemory &&
+ "Managed memory does not transfer memory between "
+ "device and host");
+ const char *Name = "polly_copyFromDeviceToHost";
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ Args.push_back(Builder.getInt8PtrTy());
+ Args.push_back(Builder.getInt8PtrTy());
+ Args.push_back(Builder.getInt64Ty());
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Builder.CreateCall(F, {DeviceData, HostData, Size});
+}
+
+void GPUNodeBuilder::createCallSynchronizeDevice() {
+ assert(PollyManagedMemory && "explicit synchronization is only necessary for "
+ "managed memory");
+ const char *Name = "polly_synchronizeDevice";
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Builder.CreateCall(F);
+}
+
+Value *GPUNodeBuilder::createCallInitContext() {
+ const char *Name;
+
+ switch (Runtime) {
+ case GPURuntime::CUDA:
+ Name = "polly_initContextCUDA";
+ break;
+ case GPURuntime::OpenCL:
+ Name = "polly_initContextCL";
+ break;
+ }
+
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ FunctionType *Ty = FunctionType::get(Builder.getInt8PtrTy(), Args, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ return Builder.CreateCall(F, {});
+}
+
+void GPUNodeBuilder::createCallFreeContext(Value *Context) {
+ const char *Name = "polly_freeContext";
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *F = M->getFunction(Name);
+
+ // If F is not available, declare it.
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ Args.push_back(Builder.getInt8PtrTy());
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ Builder.CreateCall(F, {Context});
+}
+
+/// Check if one string is a prefix of another.
+///
+/// @param String The string in which to look for the prefix.
+/// @param Prefix The prefix to look for.
+static bool isPrefix(std::string String, std::string Prefix) {
+ return String.find(Prefix) == 0;
+}
+
+Value *GPUNodeBuilder::getArraySize(gpu_array_info *Array) {
+ isl::ast_build Build = isl::ast_build::from_context(S.getContext());
+ Value *ArraySize = ConstantInt::get(Builder.getInt64Ty(), Array->size);
+
+ if (!gpu_array_is_scalar(Array)) {
+ isl::multi_pw_aff ArrayBound = isl::manage_copy(Array->bound);
+
+ isl::pw_aff OffsetDimZero = ArrayBound.at(0);
+ isl::ast_expr Res = Build.expr_from(OffsetDimZero);
+
+ for (unsigned int i = 1; i < Array->n_index; i++) {
+ isl::pw_aff Bound_I = ArrayBound.at(i);
+ isl::ast_expr Expr = Build.expr_from(Bound_I);
+ Res = Res.mul(Expr);
+ }
+
+ Value *NumElements = ExprBuilder.create(Res.release());
+ if (NumElements->getType() != ArraySize->getType())
+ NumElements = Builder.CreateSExt(NumElements, ArraySize->getType());
+ ArraySize = Builder.CreateMul(ArraySize, NumElements);
+ }
+ return ArraySize;
+}
+
+Value *GPUNodeBuilder::getArrayOffset(gpu_array_info *Array) {
+ if (gpu_array_is_scalar(Array))
+ return nullptr;
+
+ isl::ast_build Build = isl::ast_build::from_context(S.getContext());
+
+ isl::set Min = isl::manage_copy(Array->extent).lexmin();
+
+ isl::set ZeroSet = isl::set::universe(Min.get_space());
+
+ for (unsigned i : rangeIslSize(0, Min.tuple_dim()))
+ ZeroSet = ZeroSet.fix_si(isl::dim::set, i, 0);
+
+ if (Min.is_subset(ZeroSet)) {
+ return nullptr;
+ }
+
+ isl::ast_expr Result = isl::ast_expr::from_val(isl::val(Min.ctx(), 0));
+
+ for (unsigned i : rangeIslSize(0, Min.tuple_dim())) {
+ if (i > 0) {
+ isl::pw_aff Bound_I =
+ isl::manage(isl_multi_pw_aff_get_pw_aff(Array->bound, i - 1));
+ isl::ast_expr BExpr = Build.expr_from(Bound_I);
+ Result = Result.mul(BExpr);
+ }
+ isl::pw_aff DimMin = Min.dim_min(i);
+ isl::ast_expr MExpr = Build.expr_from(DimMin);
+ Result = Result.add(MExpr);
+ }
+
+ return ExprBuilder.create(Result.release());
+}
+
+Value *GPUNodeBuilder::getManagedDeviceArray(gpu_array_info *Array,
+ ScopArrayInfo *ArrayInfo) {
+ assert(PollyManagedMemory && "Only used when you wish to get a host "
+ "pointer for sending data to the kernel, "
+ "with managed memory");
+ std::map<ScopArrayInfo *, Value *>::iterator it;
+ it = DeviceAllocations.find(ArrayInfo);
+ assert(it != DeviceAllocations.end() &&
+ "Device array expected to be available");
+ return it->second;
+}
+
+void GPUNodeBuilder::createDataTransfer(__isl_take isl_ast_node *TransferStmt,
+ enum DataDirection Direction) {
+ assert(!PollyManagedMemory && "Managed memory needs no data transfers");
+ isl_ast_expr *Expr = isl_ast_node_user_get_expr(TransferStmt);
+ isl_ast_expr *Arg = isl_ast_expr_get_op_arg(Expr, 0);
+ isl_id *Id = isl_ast_expr_get_id(Arg);
+ auto Array = (gpu_array_info *)isl_id_get_user(Id);
+ auto ScopArray = (ScopArrayInfo *)(Array->user);
+
+ Value *Size = getArraySize(Array);
+ Value *Offset = getArrayOffset(Array);
+ Value *DevPtr = DeviceAllocations[ScopArray];
+
+ Value *HostPtr;
+
+ if (gpu_array_is_scalar(Array))
+ HostPtr = BlockGen.getOrCreateAlloca(ScopArray);
+ else
+ HostPtr = ScopArray->getBasePtr();
+ HostPtr = getLatestValue(HostPtr);
+
+ if (Offset) {
+ HostPtr = Builder.CreatePointerCast(
+ HostPtr, ScopArray->getElementType()->getPointerTo());
+ HostPtr = Builder.CreateGEP(ScopArray->getElementType(), HostPtr, Offset);
+ }
+
+ HostPtr = Builder.CreatePointerCast(HostPtr, Builder.getInt8PtrTy());
+
+ if (Offset) {
+ Size = Builder.CreateSub(
+ Size, Builder.CreateMul(
+ Offset, Builder.getInt64(ScopArray->getElemSizeInBytes())));
+ }
+
+ if (Direction == HOST_TO_DEVICE)
+ createCallCopyFromHostToDevice(HostPtr, DevPtr, Size);
+ else
+ createCallCopyFromDeviceToHost(DevPtr, HostPtr, Size);
+
+ isl_id_free(Id);
+ isl_ast_expr_free(Arg);
+ isl_ast_expr_free(Expr);
+ isl_ast_node_free(TransferStmt);
+}
+
+void GPUNodeBuilder::createUser(__isl_take isl_ast_node *UserStmt) {
+ isl_ast_expr *Expr = isl_ast_node_user_get_expr(UserStmt);
+ isl_ast_expr *StmtExpr = isl_ast_expr_get_op_arg(Expr, 0);
+ isl_id *Id = isl_ast_expr_get_id(StmtExpr);
+ isl_id_free(Id);
+ isl_ast_expr_free(StmtExpr);
+
+ const char *Str = isl_id_get_name(Id);
+ if (!strcmp(Str, "kernel")) {
+ createKernel(UserStmt);
+ if (PollyManagedMemory)
+ createCallSynchronizeDevice();
+ isl_ast_expr_free(Expr);
+ return;
+ }
+ if (!strcmp(Str, "init_device")) {
+ initializeAfterRTH();
+ isl_ast_node_free(UserStmt);
+ isl_ast_expr_free(Expr);
+ return;
+ }
+ if (!strcmp(Str, "clear_device")) {
+ finalize();
+ isl_ast_node_free(UserStmt);
+ isl_ast_expr_free(Expr);
+ return;
+ }
+ if (isPrefix(Str, "to_device")) {
+ if (!PollyManagedMemory)
+ createDataTransfer(UserStmt, HOST_TO_DEVICE);
+ else
+ isl_ast_node_free(UserStmt);
+
+ isl_ast_expr_free(Expr);
+ return;
+ }
+
+ if (isPrefix(Str, "from_device")) {
+ if (!PollyManagedMemory) {
+ createDataTransfer(UserStmt, DEVICE_TO_HOST);
+ } else {
+ isl_ast_node_free(UserStmt);
+ }
+ isl_ast_expr_free(Expr);
+ return;
+ }
+
+ isl_id *Anno = isl_ast_node_get_annotation(UserStmt);
+ struct ppcg_kernel_stmt *KernelStmt =
+ (struct ppcg_kernel_stmt *)isl_id_get_user(Anno);
+ isl_id_free(Anno);
+
+ switch (KernelStmt->type) {
+ case ppcg_kernel_domain:
+ createScopStmt(Expr, KernelStmt);
+ isl_ast_node_free(UserStmt);
+ return;
+ case ppcg_kernel_copy:
+ createKernelCopy(KernelStmt);
+ isl_ast_expr_free(Expr);
+ isl_ast_node_free(UserStmt);
+ return;
+ case ppcg_kernel_sync:
+ createKernelSync();
+ isl_ast_expr_free(Expr);
+ isl_ast_node_free(UserStmt);
+ return;
+ }
+
+ isl_ast_expr_free(Expr);
+ isl_ast_node_free(UserStmt);
+}
+
+void GPUNodeBuilder::createFor(__isl_take isl_ast_node *Node) {
+ createForSequential(isl::manage(Node).as<isl::ast_node_for>(), false);
+}
+
+void GPUNodeBuilder::createKernelCopy(ppcg_kernel_stmt *KernelStmt) {
+ isl_ast_expr *LocalIndex = isl_ast_expr_copy(KernelStmt->u.c.local_index);
+ LocalIndex = isl_ast_expr_address_of(LocalIndex);
+ Value *LocalAddr = ExprBuilder.create(LocalIndex);
+ isl_ast_expr *Index = isl_ast_expr_copy(KernelStmt->u.c.index);
+ Index = isl_ast_expr_address_of(Index);
+ Value *GlobalAddr = ExprBuilder.create(Index);
+ Type *IndexTy = cast<PointerType>(GlobalAddr->getType())->getElementType();
+
+ if (KernelStmt->u.c.read) {
+ LoadInst *Load = Builder.CreateLoad(IndexTy, GlobalAddr, "shared.read");
+ Builder.CreateStore(Load, LocalAddr);
+ } else {
+ LoadInst *Load = Builder.CreateLoad(IndexTy, LocalAddr, "shared.write");
+ Builder.CreateStore(Load, GlobalAddr);
+ }
+}
+
+void GPUNodeBuilder::createScopStmt(isl_ast_expr *Expr,
+ ppcg_kernel_stmt *KernelStmt) {
+ auto Stmt = (ScopStmt *)KernelStmt->u.d.stmt->stmt;
+ isl_id_to_ast_expr *Indexes = KernelStmt->u.d.ref2expr;
+
+ LoopToScevMapT LTS;
+ LTS.insert(OutsideLoopIterations.begin(), OutsideLoopIterations.end());
+
+ createSubstitutions(Expr, Stmt, LTS);
+
+ if (Stmt->isBlockStmt())
+ BlockGen.copyStmt(*Stmt, LTS, Indexes);
+ else
+ RegionGen.copyStmt(*Stmt, LTS, Indexes);
+}
+
+void GPUNodeBuilder::createKernelSync() {
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ const char *SpirName = "__gen_ocl_barrier_global";
+
+ Function *Sync;
+
+ switch (Arch) {
+ case GPUArch::SPIR64:
+ case GPUArch::SPIR32:
+ Sync = M->getFunction(SpirName);
+
+ // If Sync is not available, declare it.
+ if (!Sync) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Args, false);
+ Sync = Function::Create(Ty, Linkage, SpirName, M);
+ Sync->setCallingConv(CallingConv::SPIR_FUNC);
+ }
+ break;
+ case GPUArch::NVPTX64:
+ Sync = Intrinsic::getDeclaration(M, Intrinsic::nvvm_barrier0);
+ break;
+ }
+
+ Builder.CreateCall(Sync, {});
+}
+
+/// Collect llvm::Values referenced from @p Node
+///
+/// This function only applies to isl_ast_nodes that are user_nodes referring
+/// to a ScopStmt. All other node types are ignore.
+///
+/// @param Node The node to collect references for.
+/// @param User A user pointer used as storage for the data that is collected.
+///
+/// @returns isl_bool_true if data could be collected successfully.
+isl_bool collectReferencesInGPUStmt(__isl_keep isl_ast_node *Node, void *User) {
+ if (isl_ast_node_get_type(Node) != isl_ast_node_user)
+ return isl_bool_true;
+
+ isl_ast_expr *Expr = isl_ast_node_user_get_expr(Node);
+ isl_ast_expr *StmtExpr = isl_ast_expr_get_op_arg(Expr, 0);
+ isl_id *Id = isl_ast_expr_get_id(StmtExpr);
+ const char *Str = isl_id_get_name(Id);
+ isl_id_free(Id);
+ isl_ast_expr_free(StmtExpr);
+ isl_ast_expr_free(Expr);
+
+ if (!isPrefix(Str, "Stmt"))
+ return isl_bool_true;
+
+ Id = isl_ast_node_get_annotation(Node);
+ auto *KernelStmt = (ppcg_kernel_stmt *)isl_id_get_user(Id);
+ auto Stmt = (ScopStmt *)KernelStmt->u.d.stmt->stmt;
+ isl_id_free(Id);
+
+ addReferencesFromStmt(Stmt, User, false /* CreateScalarRefs */);
+
+ return isl_bool_true;
+}
+
+/// A list of functions that are available in NVIDIA's libdevice.
+const std::set<std::string> CUDALibDeviceFunctions = {
+ "exp", "expf", "expl", "cos", "cosf", "sqrt", "sqrtf",
+ "copysign", "copysignf", "copysignl", "log", "logf", "powi", "powif"};
+
+// A map from intrinsics to their corresponding libdevice functions.
+const std::map<std::string, std::string> IntrinsicToLibdeviceFunc = {
+ {"llvm.exp.f64", "exp"},
+ {"llvm.exp.f32", "expf"},
+ {"llvm.powi.f64.i32", "powi"},
+ {"llvm.powi.f32.i32", "powif"}};
+
+/// Return the corresponding CUDA libdevice function name @p Name.
+/// Note that this function will try to convert instrinsics in the list
+/// IntrinsicToLibdeviceFunc into libdevice functions.
+/// This is because some intrinsics such as `exp`
+/// are not supported by the NVPTX backend.
+/// If this restriction of the backend is lifted, we should refactor our code
+/// so that we use intrinsics whenever possible.
+///
+/// Return "" if we are not compiling for CUDA.
+std::string getCUDALibDeviceFuntion(StringRef NameRef) {
+ std::string Name = NameRef.str();
+ auto It = IntrinsicToLibdeviceFunc.find(Name);
+ if (It != IntrinsicToLibdeviceFunc.end())
+ return getCUDALibDeviceFuntion(It->second);
+
+ if (CUDALibDeviceFunctions.count(Name))
+ return ("__nv_" + Name);
+
+ return "";
+}
+
+/// Check if F is a function that we can code-generate in a GPU kernel.
+static bool isValidFunctionInKernel(llvm::Function *F, bool AllowLibDevice) {
+ assert(F && "F is an invalid pointer");
+ // We string compare against the name of the function to allow
+ // all variants of the intrinsic "llvm.sqrt.*", "llvm.fabs", and
+ // "llvm.copysign".
+ const StringRef Name = F->getName();
+
+ if (AllowLibDevice && getCUDALibDeviceFuntion(Name).length() > 0)
+ return true;
+
+ return F->isIntrinsic() &&
+ (Name.startswith("llvm.sqrt") || Name.startswith("llvm.fabs") ||
+ Name.startswith("llvm.copysign"));
+}
+
+/// Do not take `Function` as a subtree value.
+///
+/// We try to take the reference of all subtree values and pass them along
+/// to the kernel from the host. Taking an address of any function and
+/// trying to pass along is nonsensical. Only allow `Value`s that are not
+/// `Function`s.
+static bool isValidSubtreeValue(llvm::Value *V) { return !isa<Function>(V); }
+
+/// Return `Function`s from `RawSubtreeValues`.
+static SetVector<Function *>
+getFunctionsFromRawSubtreeValues(SetVector<Value *> RawSubtreeValues,
+ bool AllowCUDALibDevice) {
+ SetVector<Function *> SubtreeFunctions;
+ for (Value *It : RawSubtreeValues) {
+ Function *F = dyn_cast<Function>(It);
+ if (F) {
+ assert(isValidFunctionInKernel(F, AllowCUDALibDevice) &&
+ "Code should have bailed out by "
+ "this point if an invalid function "
+ "were present in a kernel.");
+ SubtreeFunctions.insert(F);
+ }
+ }
+ return SubtreeFunctions;
+}
+
+std::tuple<SetVector<Value *>, SetVector<Function *>, SetVector<const Loop *>,
+ isl::space>
+GPUNodeBuilder::getReferencesInKernel(ppcg_kernel *Kernel) {
+ SetVector<Value *> SubtreeValues;
+ SetVector<const SCEV *> SCEVs;
+ SetVector<const Loop *> Loops;
+ isl::space ParamSpace = isl::space(S.getIslCtx(), 0, 0).params();
+ SubtreeReferences References = {
+ LI, SE, S, ValueMap, SubtreeValues, SCEVs, getBlockGenerator(),
+ &ParamSpace};
+
+ for (const auto &I : IDToValue)
+ SubtreeValues.insert(I.second);
+
+ // NOTE: this is populated in IslNodeBuilder::addParameters
+ // See [Code generation of induction variables of loops outside Scops].
+ for (const auto &I : OutsideLoopIterations)
+ SubtreeValues.insert(cast<SCEVUnknown>(I.second)->getValue());
+
+ isl_ast_node_foreach_descendant_top_down(
+ Kernel->tree, collectReferencesInGPUStmt, &References);
+
+ for (const SCEV *Expr : SCEVs) {
+ findValues(Expr, SE, SubtreeValues);
+ findLoops(Expr, Loops);
+ }
+
+ Loops.remove_if([this](const Loop *L) {
+ return S.contains(L) || L->contains(S.getEntry());
+ });
+
+ for (auto &SAI : S.arrays())
+ SubtreeValues.remove(SAI->getBasePtr());
+
+ isl_space *Space = S.getParamSpace().release();
+ for (long i = 0, n = isl_space_dim(Space, isl_dim_param); i < n; i++) {
+ isl_id *Id = isl_space_get_dim_id(Space, isl_dim_param, i);
+ assert(IDToValue.count(Id));
+ Value *Val = IDToValue[Id];
+ SubtreeValues.remove(Val);
+ isl_id_free(Id);
+ }
+ isl_space_free(Space);
+
+ for (long i = 0, n = isl_space_dim(Kernel->space, isl_dim_set); i < n; i++) {
+ isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_set, i);
+ assert(IDToValue.count(Id));
+ Value *Val = IDToValue[Id];
+ SubtreeValues.remove(Val);
+ isl_id_free(Id);
+ }
+
+ // Note: { ValidSubtreeValues, ValidSubtreeFunctions } partitions
+ // SubtreeValues. This is important, because we should not lose any
+ // SubtreeValues in the process of constructing the
+ // "ValidSubtree{Values, Functions} sets. Nor should the set
+ // ValidSubtree{Values, Functions} have any common element.
+ auto ValidSubtreeValuesIt =
+ make_filter_range(SubtreeValues, isValidSubtreeValue);
+ SetVector<Value *> ValidSubtreeValues(ValidSubtreeValuesIt.begin(),
+ ValidSubtreeValuesIt.end());
+
+ bool AllowCUDALibDevice = Arch == GPUArch::NVPTX64;
+
+ SetVector<Function *> ValidSubtreeFunctions(
+ getFunctionsFromRawSubtreeValues(SubtreeValues, AllowCUDALibDevice));
+
+ // @see IslNodeBuilder::getReferencesInSubtree
+ SetVector<Value *> ReplacedValues;
+ for (Value *V : ValidSubtreeValues) {
+ auto It = ValueMap.find(V);
+ if (It == ValueMap.end())
+ ReplacedValues.insert(V);
+ else
+ ReplacedValues.insert(It->second);
+ }
+ return std::make_tuple(ReplacedValues, ValidSubtreeFunctions, Loops,
+ ParamSpace);
+}
+
+void GPUNodeBuilder::clearDominators(Function *F) {
+ DomTreeNode *N = DT.getNode(&F->getEntryBlock());
+ std::vector<BasicBlock *> Nodes;
+ for (po_iterator<DomTreeNode *> I = po_begin(N), E = po_end(N); I != E; ++I)
+ Nodes.push_back(I->getBlock());
+
+ for (BasicBlock *BB : Nodes)
+ DT.eraseNode(BB);
+}
+
+void GPUNodeBuilder::clearScalarEvolution(Function *F) {
+ for (BasicBlock &BB : *F) {
+ Loop *L = LI.getLoopFor(&BB);
+ if (L)
+ SE.forgetLoop(L);
+ }
+}
+
+void GPUNodeBuilder::clearLoops(Function *F) {
+ SmallSet<Loop *, 1> WorkList;
+ for (BasicBlock &BB : *F) {
+ Loop *L = LI.getLoopFor(&BB);
+ if (L)
+ WorkList.insert(L);
+ }
+ for (auto *L : WorkList)
+ LI.erase(L);
+}
+
+std::tuple<Value *, Value *> GPUNodeBuilder::getGridSizes(ppcg_kernel *Kernel) {
+ std::vector<Value *> Sizes;
+ isl::ast_build Context = isl::ast_build::from_context(S.getContext());
+
+ isl::multi_pw_aff GridSizePwAffs = isl::manage_copy(Kernel->grid_size);
+ for (long i = 0; i < Kernel->n_grid; i++) {
+ isl::pw_aff Size = GridSizePwAffs.at(i);
+ isl::ast_expr GridSize = Context.expr_from(Size);
+ Value *Res = ExprBuilder.create(GridSize.release());
+ Res = Builder.CreateTrunc(Res, Builder.getInt32Ty());
+ Sizes.push_back(Res);
+ }
+
+ for (long i = Kernel->n_grid; i < 3; i++)
+ Sizes.push_back(ConstantInt::get(Builder.getInt32Ty(), 1));
+
+ return std::make_tuple(Sizes[0], Sizes[1]);
+}
+
+std::tuple<Value *, Value *, Value *>
+GPUNodeBuilder::getBlockSizes(ppcg_kernel *Kernel) {
+ std::vector<Value *> Sizes;
+
+ for (long i = 0; i < Kernel->n_block; i++) {
+ Value *Res = ConstantInt::get(Builder.getInt32Ty(), Kernel->block_dim[i]);
+ Sizes.push_back(Res);
+ }
+
+ for (long i = Kernel->n_block; i < 3; i++)
+ Sizes.push_back(ConstantInt::get(Builder.getInt32Ty(), 1));
+
+ return std::make_tuple(Sizes[0], Sizes[1], Sizes[2]);
+}
+
+void GPUNodeBuilder::insertStoreParameter(Instruction *Parameters,
+ Instruction *Param, int Index) {
+ Value *Slot = Builder.CreateGEP(
+ Parameters->getType()->getPointerElementType(), Parameters,
+ {Builder.getInt64(0), Builder.getInt64(Index)});
+ Value *ParamTyped = Builder.CreatePointerCast(Param, Builder.getInt8PtrTy());
+ Builder.CreateStore(ParamTyped, Slot);
+}
+
+Value *
+GPUNodeBuilder::createLaunchParameters(ppcg_kernel *Kernel, Function *F,
+ SetVector<Value *> SubtreeValues) {
+ const int NumArgs = F->arg_size();
+ std::vector<int> ArgSizes(NumArgs);
+
+ // If we are using the OpenCL Runtime, we need to add the kernel argument
+ // sizes to the end of the launch-parameter list, so OpenCL can determine
+ // how big the respective kernel arguments are.
+ // Here we need to reserve adequate space for that.
+ Type *ArrayTy;
+ if (Runtime == GPURuntime::OpenCL)
+ ArrayTy = ArrayType::get(Builder.getInt8PtrTy(), 2 * NumArgs);
+ else
+ ArrayTy = ArrayType::get(Builder.getInt8PtrTy(), NumArgs);
+
+ BasicBlock *EntryBlock =
+ &Builder.GetInsertBlock()->getParent()->getEntryBlock();
+ auto AddressSpace = F->getParent()->getDataLayout().getAllocaAddrSpace();
+ std::string Launch = "polly_launch_" + std::to_string(Kernel->id);
+ Instruction *Parameters = new AllocaInst(
+ ArrayTy, AddressSpace, Launch + "_params", EntryBlock->getTerminator());
+
+ int Index = 0;
+ for (long i = 0; i < Prog->n_array; i++) {
+ if (!ppcg_kernel_requires_array_argument(Kernel, i))
+ continue;
+
+ isl_id *Id = isl_space_get_tuple_id(Prog->array[i].space, isl_dim_set);
+ const ScopArrayInfo *SAI = ScopArrayInfo::getFromId(isl::manage(Id));
+
+ if (Runtime == GPURuntime::OpenCL)
+ ArgSizes[Index] = SAI->getElemSizeInBytes();
+
+ Value *DevArray = nullptr;
+ if (PollyManagedMemory) {
+ DevArray = getManagedDeviceArray(&Prog->array[i],
+ const_cast<ScopArrayInfo *>(SAI));
+ } else {
+ DevArray = DeviceAllocations[const_cast<ScopArrayInfo *>(SAI)];
+ DevArray = createCallGetDevicePtr(DevArray);
+ }
+ assert(DevArray != nullptr && "Array to be offloaded to device not "
+ "initialized");
+ Value *Offset = getArrayOffset(&Prog->array[i]);
+
+ if (Offset) {
+ DevArray = Builder.CreatePointerCast(
+ DevArray, SAI->getElementType()->getPointerTo());
+ DevArray = Builder.CreateGEP(SAI->getElementType(), DevArray,
+ Builder.CreateNeg(Offset));
+ DevArray = Builder.CreatePointerCast(DevArray, Builder.getInt8PtrTy());
+ }
+ Value *Slot = Builder.CreateGEP(
+ ArrayTy, Parameters, {Builder.getInt64(0), Builder.getInt64(Index)});
+
+ if (gpu_array_is_read_only_scalar(&Prog->array[i])) {
+ Value *ValPtr = nullptr;
+ if (PollyManagedMemory)
+ ValPtr = DevArray;
+ else
+ ValPtr = BlockGen.getOrCreateAlloca(SAI);
+
+ assert(ValPtr != nullptr && "ValPtr that should point to a valid object"
+ " to be stored into Parameters");
+ Value *ValPtrCast =
+ Builder.CreatePointerCast(ValPtr, Builder.getInt8PtrTy());
+ Builder.CreateStore(ValPtrCast, Slot);
+ } else {
+ Instruction *Param =
+ new AllocaInst(Builder.getInt8PtrTy(), AddressSpace,
+ Launch + "_param_" + std::to_string(Index),
+ EntryBlock->getTerminator());
+ Builder.CreateStore(DevArray, Param);
+ Value *ParamTyped =
+ Builder.CreatePointerCast(Param, Builder.getInt8PtrTy());
+ Builder.CreateStore(ParamTyped, Slot);
+ }
+ Index++;
+ }
+
+ int NumHostIters = isl_space_dim(Kernel->space, isl_dim_set);
+
+ for (long i = 0; i < NumHostIters; i++) {
+ isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_set, i);
+ Value *Val = IDToValue[Id];
+ isl_id_free(Id);
+
+ if (Runtime == GPURuntime::OpenCL)
+ ArgSizes[Index] = computeSizeInBytes(Val->getType());
+
+ Instruction *Param =
+ new AllocaInst(Val->getType(), AddressSpace,
+ Launch + "_param_" + std::to_string(Index),
+ EntryBlock->getTerminator());
+ Builder.CreateStore(Val, Param);
+ insertStoreParameter(Parameters, Param, Index);
+ Index++;
+ }
+
+ int NumVars = isl_space_dim(Kernel->space, isl_dim_param);
+
+ for (long i = 0; i < NumVars; i++) {
+ isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_param, i);
+ Value *Val = IDToValue[Id];
+ if (ValueMap.count(Val))
+ Val = ValueMap[Val];
+ isl_id_free(Id);
+
+ if (Runtime == GPURuntime::OpenCL)
+ ArgSizes[Index] = computeSizeInBytes(Val->getType());
+
+ Instruction *Param =
+ new AllocaInst(Val->getType(), AddressSpace,
+ Launch + "_param_" + std::to_string(Index),
+ EntryBlock->getTerminator());
+ Builder.CreateStore(Val, Param);
+ insertStoreParameter(Parameters, Param, Index);
+ Index++;
+ }
+
+ for (auto Val : SubtreeValues) {
+ if (Runtime == GPURuntime::OpenCL)
+ ArgSizes[Index] = computeSizeInBytes(Val->getType());
+
+ Instruction *Param =
+ new AllocaInst(Val->getType(), AddressSpace,
+ Launch + "_param_" + std::to_string(Index),
+ EntryBlock->getTerminator());
+ Builder.CreateStore(Val, Param);
+ insertStoreParameter(Parameters, Param, Index);
+ Index++;
+ }
+
+ if (Runtime == GPURuntime::OpenCL) {
+ for (int i = 0; i < NumArgs; i++) {
+ Value *Val = ConstantInt::get(Builder.getInt32Ty(), ArgSizes[i]);
+ Instruction *Param =
+ new AllocaInst(Builder.getInt32Ty(), AddressSpace,
+ Launch + "_param_size_" + std::to_string(i),
+ EntryBlock->getTerminator());
+ Builder.CreateStore(Val, Param);
+ insertStoreParameter(Parameters, Param, Index);
+ Index++;
+ }
+ }
+
+ auto Location = EntryBlock->getTerminator();
+ return new BitCastInst(Parameters, Builder.getInt8PtrTy(),
+ Launch + "_params_i8ptr", Location);
+}
+
+void GPUNodeBuilder::setupKernelSubtreeFunctions(
+ SetVector<Function *> SubtreeFunctions) {
+ for (auto Fn : SubtreeFunctions) {
+ const std::string ClonedFnName = Fn->getName().str();
+ Function *Clone = GPUModule->getFunction(ClonedFnName);
+ if (!Clone)
+ Clone =
+ Function::Create(Fn->getFunctionType(), GlobalValue::ExternalLinkage,
+ ClonedFnName, GPUModule.get());
+ assert(Clone && "Expected cloned function to be initialized.");
+ assert(ValueMap.find(Fn) == ValueMap.end() &&
+ "Fn already present in ValueMap");
+ ValueMap[Fn] = Clone;
+ }
+}
+void GPUNodeBuilder::createKernel(__isl_take isl_ast_node *KernelStmt) {
+ isl_id *Id = isl_ast_node_get_annotation(KernelStmt);
+ ppcg_kernel *Kernel = (ppcg_kernel *)isl_id_get_user(Id);
+ isl_id_free(Id);
+ isl_ast_node_free(KernelStmt);
+
+ if (Kernel->n_grid > 1)
+ DeepestParallel = std::max(
+ DeepestParallel, (unsigned)isl_space_dim(Kernel->space, isl_dim_set));
+ else
+ DeepestSequential = std::max(
+ DeepestSequential, (unsigned)isl_space_dim(Kernel->space, isl_dim_set));
+
+ Value *BlockDimX, *BlockDimY, *BlockDimZ;
+ std::tie(BlockDimX, BlockDimY, BlockDimZ) = getBlockSizes(Kernel);
+
+ SetVector<Value *> SubtreeValues;
+ SetVector<Function *> SubtreeFunctions;
+ SetVector<const Loop *> Loops;
+ isl::space ParamSpace;
+ std::tie(SubtreeValues, SubtreeFunctions, Loops, ParamSpace) =
+ getReferencesInKernel(Kernel);
+
+ // Add parameters that appear only in the access function to the kernel
+ // space. This is important to make sure that all isl_ids are passed as
+ // parameters to the kernel, even though we may not have all parameters
+ // in the context to improve compile time.
+ Kernel->space = isl_space_align_params(Kernel->space, ParamSpace.release());
+
+ assert(Kernel->tree && "Device AST of kernel node is empty");
+
+ Instruction &HostInsertPoint = *Builder.GetInsertPoint();
+ IslExprBuilder::IDToValueTy HostIDs = IDToValue;
+ ValueMapT HostValueMap = ValueMap;
+ BlockGenerator::AllocaMapTy HostScalarMap = ScalarMap;
+ ScalarMap.clear();
+ BlockGenerator::EscapeUsersAllocaMapTy HostEscapeMap = EscapeMap;
+ EscapeMap.clear();
+
+ // Create for all loops we depend on values that contain the current loop
+ // iteration. These values are necessary to generate code for SCEVs that
+ // depend on such loops. As a result we need to pass them to the subfunction.
+ for (const Loop *L : Loops) {
+ const SCEV *OuterLIV = SE.getAddRecExpr(SE.getUnknown(Builder.getInt64(0)),
+ SE.getUnknown(Builder.getInt64(1)),
+ L, SCEV::FlagAnyWrap);
+ Value *V = generateSCEV(OuterLIV);
+ OutsideLoopIterations[L] = SE.getUnknown(V);
+ SubtreeValues.insert(V);
+ }
+
+ createKernelFunction(Kernel, SubtreeValues, SubtreeFunctions);
+ setupKernelSubtreeFunctions(SubtreeFunctions);
+
+ create(isl_ast_node_copy(Kernel->tree));
+
+ finalizeKernelArguments(Kernel);
+ Function *F = Builder.GetInsertBlock()->getParent();
+ if (Arch == GPUArch::NVPTX64)
+ addCUDAAnnotations(F->getParent(), BlockDimX, BlockDimY, BlockDimZ);
+ clearDominators(F);
+ clearScalarEvolution(F);
+ clearLoops(F);
+
+ IDToValue = HostIDs;
+
+ ValueMap = std::move(HostValueMap);
+ ScalarMap = std::move(HostScalarMap);
+ EscapeMap = std::move(HostEscapeMap);
+ IDToSAI.clear();
+ Annotator.resetAlternativeAliasBases();
+ for (auto &BasePtr : LocalArrays)
+ S.invalidateScopArrayInfo(BasePtr, MemoryKind::Array);
+ LocalArrays.clear();
+
+ std::string ASMString = finalizeKernelFunction();
+ Builder.SetInsertPoint(&HostInsertPoint);
+ Value *Parameters = createLaunchParameters(Kernel, F, SubtreeValues);
+
+ std::string Name = getKernelFuncName(Kernel->id);
+ Value *KernelString = Builder.CreateGlobalStringPtr(ASMString, Name);
+ Value *NameString = Builder.CreateGlobalStringPtr(Name, Name + "_name");
+ Value *GPUKernel = createCallGetKernel(KernelString, NameString);
+
+ Value *GridDimX, *GridDimY;
+ std::tie(GridDimX, GridDimY) = getGridSizes(Kernel);
+
+ createCallLaunchKernel(GPUKernel, GridDimX, GridDimY, BlockDimX, BlockDimY,
+ BlockDimZ, Parameters);
+ createCallFreeKernel(GPUKernel);
+
+ for (auto Id : KernelIds)
+ isl_id_free(Id);
+
+ KernelIds.clear();
+}
+
+/// Compute the DataLayout string for the NVPTX backend.
+///
+/// @param is64Bit Are we looking for a 64 bit architecture?
+static std::string computeNVPTXDataLayout(bool is64Bit) {
+ std::string Ret = "";
+
+ if (!is64Bit) {
+ Ret += "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:"
+ "64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:"
+ "64-v128:128:128-n16:32:64";
+ } else {
+ Ret += "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:"
+ "64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:"
+ "64-v128:128:128-n16:32:64";
+ }
+
+ return Ret;
+}
+
+/// Compute the DataLayout string for a SPIR kernel.
+///
+/// @param is64Bit Are we looking for a 64 bit architecture?
+static std::string computeSPIRDataLayout(bool is64Bit) {
+ std::string Ret = "";
+
+ if (!is64Bit) {
+ Ret += "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:"
+ "64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:"
+ "32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:"
+ "256:256-v256:256:256-v512:512:512-v1024:1024:1024";
+ } else {
+ Ret += "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:"
+ "64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:"
+ "32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:"
+ "256:256-v256:256:256-v512:512:512-v1024:1024:1024";
+ }
+
+ return Ret;
+}
+
+Function *
+GPUNodeBuilder::createKernelFunctionDecl(ppcg_kernel *Kernel,
+ SetVector<Value *> &SubtreeValues) {
+ std::vector<Type *> Args;
+ std::string Identifier = getKernelFuncName(Kernel->id);
+
+ std::vector<Metadata *> MemoryType;
+
+ for (long i = 0; i < Prog->n_array; i++) {
+ if (!ppcg_kernel_requires_array_argument(Kernel, i))
+ continue;
+
+ if (gpu_array_is_read_only_scalar(&Prog->array[i])) {
+ isl_id *Id = isl_space_get_tuple_id(Prog->array[i].space, isl_dim_set);
+ const ScopArrayInfo *SAI = ScopArrayInfo::getFromId(isl::manage(Id));
+ Args.push_back(SAI->getElementType());
+ MemoryType.push_back(
+ ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), 0)));
+ } else {
+ static const int UseGlobalMemory = 1;
+ Args.push_back(Builder.getInt8PtrTy(UseGlobalMemory));
+ MemoryType.push_back(
+ ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), 1)));
+ }
+ }
+
+ int NumHostIters = isl_space_dim(Kernel->space, isl_dim_set);
+
+ for (long i = 0; i < NumHostIters; i++) {
+ Args.push_back(Builder.getInt64Ty());
+ MemoryType.push_back(
+ ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), 0)));
+ }
+
+ int NumVars = isl_space_dim(Kernel->space, isl_dim_param);
+
+ for (long i = 0; i < NumVars; i++) {
+ isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_param, i);
+ Value *Val = IDToValue[Id];
+ isl_id_free(Id);
+ Args.push_back(Val->getType());
+ MemoryType.push_back(
+ ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), 0)));
+ }
+
+ for (auto *V : SubtreeValues) {
+ Args.push_back(V->getType());
+ MemoryType.push_back(
+ ConstantAsMetadata::get(ConstantInt::get(Builder.getInt32Ty(), 0)));
+ }
+
+ auto *FT = FunctionType::get(Builder.getVoidTy(), Args, false);
+ auto *FN = Function::Create(FT, Function::ExternalLinkage, Identifier,
+ GPUModule.get());
+
+ std::vector<Metadata *> EmptyStrings;
+
+ for (unsigned int i = 0; i < MemoryType.size(); i++) {
+ EmptyStrings.push_back(MDString::get(FN->getContext(), ""));
+ }
+
+ if (Arch == GPUArch::SPIR32 || Arch == GPUArch::SPIR64) {
+ FN->setMetadata("kernel_arg_addr_space",
+ MDNode::get(FN->getContext(), MemoryType));
+ FN->setMetadata("kernel_arg_name",
+ MDNode::get(FN->getContext(), EmptyStrings));
+ FN->setMetadata("kernel_arg_access_qual",
+ MDNode::get(FN->getContext(), EmptyStrings));
+ FN->setMetadata("kernel_arg_type",
+ MDNode::get(FN->getContext(), EmptyStrings));
+ FN->setMetadata("kernel_arg_type_qual",
+ MDNode::get(FN->getContext(), EmptyStrings));
+ FN->setMetadata("kernel_arg_base_type",
+ MDNode::get(FN->getContext(), EmptyStrings));
+ }
+
+ switch (Arch) {
+ case GPUArch::NVPTX64:
+ FN->setCallingConv(CallingConv::PTX_Kernel);
+ break;
+ case GPUArch::SPIR32:
+ case GPUArch::SPIR64:
+ FN->setCallingConv(CallingConv::SPIR_KERNEL);
+ break;
+ }
+
+ auto Arg = FN->arg_begin();
+ for (long i = 0; i < Kernel->n_array; i++) {
+ if (!ppcg_kernel_requires_array_argument(Kernel, i))
+ continue;
+
+ Arg->setName(Kernel->array[i].array->name);
+
+ isl_id *Id = isl_space_get_tuple_id(Prog->array[i].space, isl_dim_set);
+ const ScopArrayInfo *SAI = ScopArrayInfo::getFromId(isl::manage_copy(Id));
+ Type *EleTy = SAI->getElementType();
+ Value *Val = &*Arg;
+ SmallVector<const SCEV *, 4> Sizes;
+ isl_ast_build *Build =
+ isl_ast_build_from_context(isl_set_copy(Prog->context));
+ Sizes.push_back(nullptr);
+ for (long j = 1, n = Kernel->array[i].array->n_index; j < n; j++) {
+ isl_ast_expr *DimSize = isl_ast_build_expr_from_pw_aff(
+ Build, isl_multi_pw_aff_get_pw_aff(Kernel->array[i].array->bound, j));
+ auto V = ExprBuilder.create(DimSize);
+ Sizes.push_back(SE.getSCEV(V));
+ }
+ const ScopArrayInfo *SAIRep =
+ S.getOrCreateScopArrayInfo(Val, EleTy, Sizes, MemoryKind::Array);
+ LocalArrays.push_back(Val);
+
+ isl_ast_build_free(Build);
+ KernelIds.push_back(Id);
+ IDToSAI[Id] = SAIRep;
+ Arg++;
+ }
+
+ for (long i = 0; i < NumHostIters; i++) {
+ isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_set, i);
+ Arg->setName(isl_id_get_name(Id));
+ IDToValue[Id] = &*Arg;
+ KernelIDs.insert(std::unique_ptr<isl_id, IslIdDeleter>(Id));
+ Arg++;
+ }
+
+ for (long i = 0; i < NumVars; i++) {
+ isl_id *Id = isl_space_get_dim_id(Kernel->space, isl_dim_param, i);
+ Arg->setName(isl_id_get_name(Id));
+ Value *Val = IDToValue[Id];
+ ValueMap[Val] = &*Arg;
+ IDToValue[Id] = &*Arg;
+ KernelIDs.insert(std::unique_ptr<isl_id, IslIdDeleter>(Id));
+ Arg++;
+ }
+
+ for (auto *V : SubtreeValues) {
+ Arg->setName(V->getName());
+ ValueMap[V] = &*Arg;
+ Arg++;
+ }
+
+ return FN;
+}
+
+void GPUNodeBuilder::insertKernelIntrinsics(ppcg_kernel *Kernel) {
+ Intrinsic::ID IntrinsicsBID[2];
+ Intrinsic::ID IntrinsicsTID[3];
+
+ switch (Arch) {
+ case GPUArch::SPIR64:
+ case GPUArch::SPIR32:
+ llvm_unreachable("Cannot generate NVVM intrinsics for SPIR");
+ case GPUArch::NVPTX64:
+ IntrinsicsBID[0] = Intrinsic::nvvm_read_ptx_sreg_ctaid_x;
+ IntrinsicsBID[1] = Intrinsic::nvvm_read_ptx_sreg_ctaid_y;
+
+ IntrinsicsTID[0] = Intrinsic::nvvm_read_ptx_sreg_tid_x;
+ IntrinsicsTID[1] = Intrinsic::nvvm_read_ptx_sreg_tid_y;
+ IntrinsicsTID[2] = Intrinsic::nvvm_read_ptx_sreg_tid_z;
+ break;
+ }
+
+ auto addId = [this](__isl_take isl_id *Id, Intrinsic::ID Intr) mutable {
+ std::string Name = isl_id_get_name(Id);
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *IntrinsicFn = Intrinsic::getDeclaration(M, Intr);
+ Value *Val = Builder.CreateCall(IntrinsicFn, {});
+ Val = Builder.CreateIntCast(Val, Builder.getInt64Ty(), false, Name);
+ IDToValue[Id] = Val;
+ KernelIDs.insert(std::unique_ptr<isl_id, IslIdDeleter>(Id));
+ };
+
+ for (int i = 0; i < Kernel->n_grid; ++i) {
+ isl_id *Id = isl_id_list_get_id(Kernel->block_ids, i);
+ addId(Id, IntrinsicsBID[i]);
+ }
+
+ for (int i = 0; i < Kernel->n_block; ++i) {
+ isl_id *Id = isl_id_list_get_id(Kernel->thread_ids, i);
+ addId(Id, IntrinsicsTID[i]);
+ }
+}
+
+void GPUNodeBuilder::insertKernelCallsSPIR(ppcg_kernel *Kernel,
+ bool SizeTypeIs64bit) {
+ const char *GroupName[3] = {"__gen_ocl_get_group_id0",
+ "__gen_ocl_get_group_id1",
+ "__gen_ocl_get_group_id2"};
+
+ const char *LocalName[3] = {"__gen_ocl_get_local_id0",
+ "__gen_ocl_get_local_id1",
+ "__gen_ocl_get_local_id2"};
+ IntegerType *SizeT =
+ SizeTypeIs64bit ? Builder.getInt64Ty() : Builder.getInt32Ty();
+
+ auto createFunc = [this](const char *Name, __isl_take isl_id *Id,
+ IntegerType *SizeT) mutable {
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ Function *FN = M->getFunction(Name);
+
+ // If FN is not available, declare it.
+ if (!FN) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ std::vector<Type *> Args;
+ FunctionType *Ty = FunctionType::get(SizeT, Args, false);
+ FN = Function::Create(Ty, Linkage, Name, M);
+ FN->setCallingConv(CallingConv::SPIR_FUNC);
+ }
+
+ Value *Val = Builder.CreateCall(FN, {});
+ if (SizeT == Builder.getInt32Ty())
+ Val = Builder.CreateIntCast(Val, Builder.getInt64Ty(), false, Name);
+ IDToValue[Id] = Val;
+ KernelIDs.insert(std::unique_ptr<isl_id, IslIdDeleter>(Id));
+ };
+
+ for (int i = 0; i < Kernel->n_grid; ++i)
+ createFunc(GroupName[i], isl_id_list_get_id(Kernel->block_ids, i), SizeT);
+
+ for (int i = 0; i < Kernel->n_block; ++i)
+ createFunc(LocalName[i], isl_id_list_get_id(Kernel->thread_ids, i), SizeT);
+}
+
+void GPUNodeBuilder::prepareKernelArguments(ppcg_kernel *Kernel, Function *FN) {
+ auto Arg = FN->arg_begin();
+ for (long i = 0; i < Kernel->n_array; i++) {
+ if (!ppcg_kernel_requires_array_argument(Kernel, i))
+ continue;
+
+ isl_id *Id = isl_space_get_tuple_id(Prog->array[i].space, isl_dim_set);
+ const ScopArrayInfo *SAI = ScopArrayInfo::getFromId(isl::manage_copy(Id));
+ isl_id_free(Id);
+
+ if (SAI->getNumberOfDimensions() > 0) {
+ Arg++;
+ continue;
+ }
+
+ Value *Val = &*Arg;
+
+ if (!gpu_array_is_read_only_scalar(&Prog->array[i])) {
+ Type *TypePtr = SAI->getElementType()->getPointerTo();
+ Value *TypedArgPtr = Builder.CreatePointerCast(Val, TypePtr);
+ Val = Builder.CreateLoad(SAI->getElementType(), TypedArgPtr);
+ }
+
+ Value *Alloca = BlockGen.getOrCreateAlloca(SAI);
+ Builder.CreateStore(Val, Alloca);
+
+ Arg++;
+ }
+}
+
+void GPUNodeBuilder::finalizeKernelArguments(ppcg_kernel *Kernel) {
+ auto *FN = Builder.GetInsertBlock()->getParent();
+ auto Arg = FN->arg_begin();
+
+ bool StoredScalar = false;
+ for (long i = 0; i < Kernel->n_array; i++) {
+ if (!ppcg_kernel_requires_array_argument(Kernel, i))
+ continue;
+
+ isl_id *Id = isl_space_get_tuple_id(Prog->array[i].space, isl_dim_set);
+ const ScopArrayInfo *SAI = ScopArrayInfo::getFromId(isl::manage_copy(Id));
+ isl_id_free(Id);
+
+ if (SAI->getNumberOfDimensions() > 0) {
+ Arg++;
+ continue;
+ }
+
+ if (gpu_array_is_read_only_scalar(&Prog->array[i])) {
+ Arg++;
+ continue;
+ }
+
+ Value *Alloca = BlockGen.getOrCreateAlloca(SAI);
+ Value *ArgPtr = &*Arg;
+ Type *TypePtr = SAI->getElementType()->getPointerTo();
+ Value *TypedArgPtr = Builder.CreatePointerCast(ArgPtr, TypePtr);
+ Value *Val = Builder.CreateLoad(SAI->getElementType(), Alloca);
+ Builder.CreateStore(Val, TypedArgPtr);
+ StoredScalar = true;
+
+ Arg++;
+ }
+
+ if (StoredScalar) {
+ /// In case more than one thread contains scalar stores, the generated
+ /// code might be incorrect, if we only store at the end of the kernel.
+ /// To support this case we need to store these scalars back at each
+ /// memory store or at least before each kernel barrier.
+ if (Kernel->n_block != 0 || Kernel->n_grid != 0) {
+ BuildSuccessful = 0;
+ LLVM_DEBUG(
+ dbgs() << getUniqueScopName(&S)
+ << " has a store to a scalar value that"
+ " would be undefined to run in parallel. Bailing out.\n";);
+ }
+ }
+}
+
+void GPUNodeBuilder::createKernelVariables(ppcg_kernel *Kernel, Function *FN) {
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+
+ for (int i = 0; i < Kernel->n_var; ++i) {
+ struct ppcg_kernel_var &Var = Kernel->var[i];
+ isl_id *Id = isl_space_get_tuple_id(Var.array->space, isl_dim_set);
+ Type *EleTy = ScopArrayInfo::getFromId(isl::manage(Id))->getElementType();
+
+ Type *ArrayTy = EleTy;
+ SmallVector<const SCEV *, 4> Sizes;
+
+ Sizes.push_back(nullptr);
+ for (unsigned int j = 1; j < Var.array->n_index; ++j) {
+ isl_val *Val = isl_vec_get_element_val(Var.size, j);
+ long Bound = isl_val_get_num_si(Val);
+ isl_val_free(Val);
+ Sizes.push_back(S.getSE()->getConstant(Builder.getInt64Ty(), Bound));
+ }
+
+ for (int j = Var.array->n_index - 1; j >= 0; --j) {
+ isl_val *Val = isl_vec_get_element_val(Var.size, j);
+ long Bound = isl_val_get_num_si(Val);
+ isl_val_free(Val);
+ ArrayTy = ArrayType::get(ArrayTy, Bound);
+ }
+
+ const ScopArrayInfo *SAI;
+ Value *Allocation;
+ if (Var.type == ppcg_access_shared) {
+ auto GlobalVar = new GlobalVariable(
+ *M, ArrayTy, false, GlobalValue::InternalLinkage, 0, Var.name,
+ nullptr, GlobalValue::ThreadLocalMode::NotThreadLocal, 3);
+ GlobalVar->setAlignment(llvm::Align(EleTy->getPrimitiveSizeInBits() / 8));
+ GlobalVar->setInitializer(Constant::getNullValue(ArrayTy));
+
+ Allocation = GlobalVar;
+ } else if (Var.type == ppcg_access_private) {
+ Allocation = Builder.CreateAlloca(ArrayTy, 0, "private_array");
+ } else {
+ llvm_unreachable("unknown variable type");
+ }
+ SAI =
+ S.getOrCreateScopArrayInfo(Allocation, EleTy, Sizes, MemoryKind::Array);
+ Id = isl_id_alloc(S.getIslCtx().get(), Var.name, nullptr);
+ IDToValue[Id] = Allocation;
+ LocalArrays.push_back(Allocation);
+ KernelIds.push_back(Id);
+ IDToSAI[Id] = SAI;
+ }
+}
+
+void GPUNodeBuilder::createKernelFunction(
+ ppcg_kernel *Kernel, SetVector<Value *> &SubtreeValues,
+ SetVector<Function *> &SubtreeFunctions) {
+ std::string Identifier = getKernelFuncName(Kernel->id);
+ GPUModule.reset(new Module(Identifier, Builder.getContext()));
+
+ switch (Arch) {
+ case GPUArch::NVPTX64:
+ if (Runtime == GPURuntime::CUDA)
+ GPUModule->setTargetTriple(Triple::normalize("nvptx64-nvidia-cuda"));
+ else if (Runtime == GPURuntime::OpenCL)
+ GPUModule->setTargetTriple(Triple::normalize("nvptx64-nvidia-nvcl"));
+ GPUModule->setDataLayout(computeNVPTXDataLayout(true /* is64Bit */));
+ break;
+ case GPUArch::SPIR32:
+ GPUModule->setTargetTriple(Triple::normalize("spir-unknown-unknown"));
+ GPUModule->setDataLayout(computeSPIRDataLayout(false /* is64Bit */));
+ break;
+ case GPUArch::SPIR64:
+ GPUModule->setTargetTriple(Triple::normalize("spir64-unknown-unknown"));
+ GPUModule->setDataLayout(computeSPIRDataLayout(true /* is64Bit */));
+ break;
+ }
+
+ Function *FN = createKernelFunctionDecl(Kernel, SubtreeValues);
+
+ BasicBlock *PrevBlock = Builder.GetInsertBlock();
+ auto EntryBlock = BasicBlock::Create(Builder.getContext(), "entry", FN);
+
+ DT.addNewBlock(EntryBlock, PrevBlock);
+
+ Builder.SetInsertPoint(EntryBlock);
+ Builder.CreateRetVoid();
+ Builder.SetInsertPoint(EntryBlock, EntryBlock->begin());
+
+ ScopDetection::markFunctionAsInvalid(FN);
+
+ prepareKernelArguments(Kernel, FN);
+ createKernelVariables(Kernel, FN);
+
+ switch (Arch) {
+ case GPUArch::NVPTX64:
+ insertKernelIntrinsics(Kernel);
+ break;
+ case GPUArch::SPIR32:
+ insertKernelCallsSPIR(Kernel, false);
+ break;
+ case GPUArch::SPIR64:
+ insertKernelCallsSPIR(Kernel, true);
+ break;
+ }
+}
+
+std::string GPUNodeBuilder::createKernelASM() {
+ llvm::Triple GPUTriple;
+
+ switch (Arch) {
+ case GPUArch::NVPTX64:
+ switch (Runtime) {
+ case GPURuntime::CUDA:
+ GPUTriple = llvm::Triple(Triple::normalize("nvptx64-nvidia-cuda"));
+ break;
+ case GPURuntime::OpenCL:
+ GPUTriple = llvm::Triple(Triple::normalize("nvptx64-nvidia-nvcl"));
+ break;
+ }
+ break;
+ case GPUArch::SPIR64:
+ case GPUArch::SPIR32:
+ std::string SPIRAssembly;
+ raw_string_ostream IROstream(SPIRAssembly);
+ IROstream << *GPUModule;
+ IROstream.flush();
+ return SPIRAssembly;
+ }
+
+ std::string ErrMsg;
+ auto GPUTarget = TargetRegistry::lookupTarget(GPUTriple.getTriple(), ErrMsg);
+
+ if (!GPUTarget) {
+ errs() << ErrMsg << "\n";
+ return "";
+ }
+
+ TargetOptions Options;
+ Options.UnsafeFPMath = FastMath;
+
+ std::string subtarget;
+
+ switch (Arch) {
+ case GPUArch::NVPTX64:
+ subtarget = CudaVersion;
+ break;
+ case GPUArch::SPIR32:
+ case GPUArch::SPIR64:
+ llvm_unreachable("No subtarget for SPIR architecture");
+ }
+
+ std::unique_ptr<TargetMachine> TargetM(GPUTarget->createTargetMachine(
+ GPUTriple.getTriple(), subtarget, "", Options, Optional<Reloc::Model>()));
+
+ SmallString<0> ASMString;
+ raw_svector_ostream ASMStream(ASMString);
+ llvm::legacy::PassManager PM;
+
+ PM.add(createTargetTransformInfoWrapperPass(TargetM->getTargetIRAnalysis()));
+
+ if (TargetM->addPassesToEmitFile(PM, ASMStream, nullptr, CGFT_AssemblyFile,
+ true /* verify */)) {
+ errs() << "The target does not support generation of this file type!\n";
+ return "";
+ }
+
+ PM.run(*GPUModule);
+
+ return ASMStream.str().str();
+}
+
+bool GPUNodeBuilder::requiresCUDALibDevice() {
+ bool RequiresLibDevice = false;
+ for (Function &F : GPUModule->functions()) {
+ if (!F.isDeclaration())
+ continue;
+
+ const std::string CUDALibDeviceFunc = getCUDALibDeviceFuntion(F.getName());
+ if (CUDALibDeviceFunc.length() != 0) {
+ // We need to handle the case where a module looks like this:
+ // @expf(..)
+ // @llvm.exp.f64(..)
+ // Both of these functions would be renamed to `__nv_expf`.
+ //
+ // So, we must first check for the existence of the libdevice function.
+ // If this exists, we replace our current function with it.
+ //
+ // If it does not exist, we rename the current function to the
+ // libdevice functiono name.
+ if (Function *Replacement = F.getParent()->getFunction(CUDALibDeviceFunc))
+ F.replaceAllUsesWith(Replacement);
+ else
+ F.setName(CUDALibDeviceFunc);
+ RequiresLibDevice = true;
+ }
+ }
+
+ return RequiresLibDevice;
+}
+
+void GPUNodeBuilder::addCUDALibDevice() {
+ if (Arch != GPUArch::NVPTX64)
+ return;
+
+ if (requiresCUDALibDevice()) {
+ SMDiagnostic Error;
+
+ errs() << CUDALibDevice << "\n";
+ auto LibDeviceModule =
+ parseIRFile(CUDALibDevice, Error, GPUModule->getContext());
+
+ if (!LibDeviceModule) {
+ BuildSuccessful = false;
+ report_fatal_error("Could not find or load libdevice. Skipping GPU "
+ "kernel generation. Please set -polly-acc-libdevice "
+ "accordingly.\n");
+ return;
+ }
+
+ Linker L(*GPUModule);
+
+ // Set an nvptx64 target triple to avoid linker warnings. The original
+ // triple of the libdevice files are nvptx-unknown-unknown.
+ LibDeviceModule->setTargetTriple(Triple::normalize("nvptx64-nvidia-cuda"));
+ L.linkInModule(std::move(LibDeviceModule), Linker::LinkOnlyNeeded);
+ }
+}
+
+std::string GPUNodeBuilder::finalizeKernelFunction() {
+
+ if (verifyModule(*GPUModule)) {
+ LLVM_DEBUG(dbgs() << "verifyModule failed on module:\n";
+ GPUModule->print(dbgs(), nullptr); dbgs() << "\n";);
+ LLVM_DEBUG(dbgs() << "verifyModule Error:\n";
+ verifyModule(*GPUModule, &dbgs()););
+
+ if (FailOnVerifyModuleFailure)
+ llvm_unreachable("VerifyModule failed.");
+
+ BuildSuccessful = false;
+ return "";
+ }
+
+ addCUDALibDevice();
+
+ if (DumpKernelIR)
+ outs() << *GPUModule << "\n";
+
+ if (Arch != GPUArch::SPIR32 && Arch != GPUArch::SPIR64) {
+ // Optimize module.
+ llvm::legacy::PassManager OptPasses;
+ PassManagerBuilder PassBuilder;
+ PassBuilder.OptLevel = 3;
+ PassBuilder.SizeLevel = 0;
+ PassBuilder.populateModulePassManager(OptPasses);
+ OptPasses.run(*GPUModule);
+ }
+
+ std::string Assembly = createKernelASM();
+
+ if (DumpKernelASM)
+ outs() << Assembly << "\n";
+
+ GPUModule.release();
+ KernelIDs.clear();
+
+ return Assembly;
+}
+/// Construct an `isl_pw_aff_list` from a vector of `isl_pw_aff`
+/// @param PwAffs The list of piecewise affine functions to create an
+/// `isl_pw_aff_list` from. We expect an rvalue ref because
+/// all the isl_pw_aff are used up by this function.
+///
+/// @returns The `isl_pw_aff_list`.
+__isl_give isl_pw_aff_list *
+createPwAffList(isl_ctx *Context,
+ const std::vector<__isl_take isl_pw_aff *> &&PwAffs) {
+ isl_pw_aff_list *List = isl_pw_aff_list_alloc(Context, PwAffs.size());
+
+ for (unsigned i = 0; i < PwAffs.size(); i++) {
+ List = isl_pw_aff_list_insert(List, i, PwAffs[i]);
+ }
+ return List;
+}
+
+/// Align all the `PwAffs` such that they have the same parameter dimensions.
+///
+/// We loop over all `pw_aff` and align all of their spaces together to
+/// create a common space for all the `pw_aff`. This common space is the
+/// `AlignSpace`. We then align all the `pw_aff` to this space. We start
+/// with the given `SeedSpace`.
+/// @param PwAffs The list of piecewise affine functions we want to align.
+/// This is an rvalue reference because the entire vector is
+/// used up by the end of the operation.
+/// @param SeedSpace The space to start the alignment process with.
+/// @returns A std::pair, whose first element is the aligned space,
+/// whose second element is the vector of aligned piecewise
+/// affines.
+static std::pair<__isl_give isl_space *, std::vector<__isl_give isl_pw_aff *>>
+alignPwAffs(const std::vector<__isl_take isl_pw_aff *> &&PwAffs,
+ __isl_take isl_space *SeedSpace) {
+ assert(SeedSpace && "Invalid seed space given.");
+
+ isl_space *AlignSpace = SeedSpace;
+ for (isl_pw_aff *PwAff : PwAffs) {
+ isl_space *PwAffSpace = isl_pw_aff_get_domain_space(PwAff);
+ AlignSpace = isl_space_align_params(AlignSpace, PwAffSpace);
+ }
+ std::vector<isl_pw_aff *> AdjustedPwAffs;
+
+ for (unsigned i = 0; i < PwAffs.size(); i++) {
+ isl_pw_aff *Adjusted = PwAffs[i];
+ assert(Adjusted && "Invalid pw_aff given.");
+ Adjusted = isl_pw_aff_align_params(Adjusted, isl_space_copy(AlignSpace));
+ AdjustedPwAffs.push_back(Adjusted);
+ }
+ return std::make_pair(AlignSpace, AdjustedPwAffs);
+}
+
+namespace {
+class PPCGCodeGeneration : public ScopPass {
+public:
+ static char ID;
+
+ GPURuntime Runtime = GPURuntime::CUDA;
+
+ GPUArch Architecture = GPUArch::NVPTX64;
+
+ /// The scop that is currently processed.
+ Scop *S;
+
+ LoopInfo *LI;
+ DominatorTree *DT;
+ ScalarEvolution *SE;
+ const DataLayout *DL;
+ RegionInfo *RI;
+
+ PPCGCodeGeneration() : ScopPass(ID) {
+ // Apply defaults.
+ Runtime = GPURuntimeChoice;
+ Architecture = GPUArchChoice;
+ }
+
+ /// Construct compilation options for PPCG.
+ ///
+ /// @returns The compilation options.
+ ppcg_options *createPPCGOptions() {
+ auto DebugOptions =
+ (ppcg_debug_options *)malloc(sizeof(ppcg_debug_options));
+ auto Options = (ppcg_options *)malloc(sizeof(ppcg_options));
+
+ DebugOptions->dump_schedule_constraints = false;
+ DebugOptions->dump_schedule = false;
+ DebugOptions->dump_final_schedule = false;
+ DebugOptions->dump_sizes = false;
+ DebugOptions->verbose = false;
+
+ Options->debug = DebugOptions;
+
+ Options->group_chains = false;
+ Options->reschedule = true;
+ Options->scale_tile_loops = false;
+ Options->wrap = false;
+
+ Options->non_negative_parameters = false;
+ Options->ctx = nullptr;
+ Options->sizes = nullptr;
+
+ Options->tile = true;
+ Options->tile_size = 32;
+
+ Options->isolate_full_tiles = false;
+
+ Options->use_private_memory = PrivateMemory;
+ Options->use_shared_memory = SharedMemory;
+ Options->max_shared_memory = 48 * 1024;
+
+ Options->target = PPCG_TARGET_CUDA;
+ Options->openmp = false;
+ Options->linearize_device_arrays = true;
+ Options->allow_gnu_extensions = false;
+
+ Options->unroll_copy_shared = false;
+ Options->unroll_gpu_tile = false;
+ Options->live_range_reordering = true;
+
+ Options->live_range_reordering = true;
+ Options->hybrid = false;
+ Options->opencl_compiler_options = nullptr;
+ Options->opencl_use_gpu = false;
+ Options->opencl_n_include_file = 0;
+ Options->opencl_include_files = nullptr;
+ Options->opencl_print_kernel_types = false;
+ Options->opencl_embed_kernel_code = false;
+
+ Options->save_schedule_file = nullptr;
+ Options->load_schedule_file = nullptr;
+
+ return Options;
+ }
+
+ /// Get a tagged access relation containing all accesses of type @p AccessTy.
+ ///
+ /// Instead of a normal access of the form:
+ ///
+ /// Stmt[i,j,k] -> Array[f_0(i,j,k), f_1(i,j,k)]
+ ///
+ /// a tagged access has the form
+ ///
+ /// [Stmt[i,j,k] -> id[]] -> Array[f_0(i,j,k), f_1(i,j,k)]
+ ///
+ /// where 'id' is an additional space that references the memory access that
+ /// triggered the access.
+ ///
+ /// @param AccessTy The type of the memory accesses to collect.
+ ///
+ /// @return The relation describing all tagged memory accesses.
+ isl_union_map *getTaggedAccesses(enum MemoryAccess::AccessType AccessTy) {
+ isl_union_map *Accesses = isl_union_map_empty(S->getParamSpace().release());
+
+ for (auto &Stmt : *S)
+ for (auto &Acc : Stmt)
+ if (Acc->getType() == AccessTy) {
+ isl_map *Relation = Acc->getAccessRelation().release();
+ Relation =
+ isl_map_intersect_domain(Relation, Stmt.getDomain().release());
+
+ isl_space *Space = isl_map_get_space(Relation);
+ Space = isl_space_range(Space);
+ Space = isl_space_from_range(Space);
+ Space =
+ isl_space_set_tuple_id(Space, isl_dim_in, Acc->getId().release());
+ isl_map *Universe = isl_map_universe(Space);
+ Relation = isl_map_domain_product(Relation, Universe);
+ Accesses = isl_union_map_add_map(Accesses, Relation);
+ }
+
+ return Accesses;
+ }
+
+ /// Get the set of all read accesses, tagged with the access id.
+ ///
+ /// @see getTaggedAccesses
+ isl_union_map *getTaggedReads() {
+ return getTaggedAccesses(MemoryAccess::READ);
+ }
+
+ /// Get the set of all may (and must) accesses, tagged with the access id.
+ ///
+ /// @see getTaggedAccesses
+ isl_union_map *getTaggedMayWrites() {
+ return isl_union_map_union(getTaggedAccesses(MemoryAccess::MAY_WRITE),
+ getTaggedAccesses(MemoryAccess::MUST_WRITE));
+ }
+
+ /// Get the set of all must accesses, tagged with the access id.
+ ///
+ /// @see getTaggedAccesses
+ isl_union_map *getTaggedMustWrites() {
+ return getTaggedAccesses(MemoryAccess::MUST_WRITE);
+ }
+
+ /// Collect parameter and array names as isl_ids.
+ ///
+ /// To reason about the different parameters and arrays used, ppcg requires
+ /// a list of all isl_ids in use. As PPCG traditionally performs
+ /// source-to-source compilation each of these isl_ids is mapped to the
+ /// expression that represents it. As we do not have a corresponding
+ /// expression in Polly, we just map each id to a 'zero' expression to match
+ /// the data format that ppcg expects.
+ ///
+ /// @returns Retun a map from collected ids to 'zero' ast expressions.
+ __isl_give isl_id_to_ast_expr *getNames() {
+ auto *Names = isl_id_to_ast_expr_alloc(
+ S->getIslCtx().get(),
+ S->getNumParams() + std::distance(S->array_begin(), S->array_end()));
+ auto *Zero = isl_ast_expr_from_val(isl_val_zero(S->getIslCtx().get()));
+
+ for (const SCEV *P : S->parameters()) {
+ isl_id *Id = S->getIdForParam(P).release();
+ Names = isl_id_to_ast_expr_set(Names, Id, isl_ast_expr_copy(Zero));
+ }
+
+ for (auto &Array : S->arrays()) {
+ auto Id = Array->getBasePtrId().release();
+ Names = isl_id_to_ast_expr_set(Names, Id, isl_ast_expr_copy(Zero));
+ }
+
+ isl_ast_expr_free(Zero);
+
+ return Names;
+ }
+
+ /// Create a new PPCG scop from the current scop.
+ ///
+ /// The PPCG scop is initialized with data from the current polly::Scop. From
+ /// this initial data, the data-dependences in the PPCG scop are initialized.
+ /// We do not use Polly's dependence analysis for now, to ensure we match
+ /// the PPCG default behaviour more closely.
+ ///
+ /// @returns A new ppcg scop.
+ ppcg_scop *createPPCGScop() {
+ MustKillsInfo KillsInfo = computeMustKillsInfo(*S);
+
+ auto PPCGScop = (ppcg_scop *)malloc(sizeof(ppcg_scop));
+
+ PPCGScop->options = createPPCGOptions();
+ // enable live range reordering
+ PPCGScop->options->live_range_reordering = 1;
+
+ PPCGScop->start = 0;
+ PPCGScop->end = 0;
+
+ PPCGScop->context = S->getContext().release();
+ PPCGScop->domain = S->getDomains().release();
+ // TODO: investigate this further. PPCG calls collect_call_domains.
+ PPCGScop->call = isl_union_set_from_set(S->getContext().release());
+ PPCGScop->tagged_reads = getTaggedReads();
+ PPCGScop->reads = S->getReads().release();
+ PPCGScop->live_in = nullptr;
+ PPCGScop->tagged_may_writes = getTaggedMayWrites();
+ PPCGScop->may_writes = S->getWrites().release();
+ PPCGScop->tagged_must_writes = getTaggedMustWrites();
+ PPCGScop->must_writes = S->getMustWrites().release();
+ PPCGScop->live_out = nullptr;
+ PPCGScop->tagged_must_kills = KillsInfo.TaggedMustKills.release();
+ PPCGScop->must_kills = KillsInfo.MustKills.release();
+
+ PPCGScop->tagger = nullptr;
+ PPCGScop->independence =
+ isl_union_map_empty(isl_set_get_space(PPCGScop->context));
+ PPCGScop->dep_flow = nullptr;
+ PPCGScop->tagged_dep_flow = nullptr;
+ PPCGScop->dep_false = nullptr;
+ PPCGScop->dep_forced = nullptr;
+ PPCGScop->dep_order = nullptr;
+ PPCGScop->tagged_dep_order = nullptr;
+
+ PPCGScop->schedule = S->getScheduleTree().release();
+ // If we have something non-trivial to kill, add it to the schedule
+ if (KillsInfo.KillsSchedule.get())
+ PPCGScop->schedule = isl_schedule_sequence(
+ PPCGScop->schedule, KillsInfo.KillsSchedule.release());
+
+ PPCGScop->names = getNames();
+ PPCGScop->pet = nullptr;
+
+ compute_tagger(PPCGScop);
+ compute_dependences(PPCGScop);
+ eliminate_dead_code(PPCGScop);
+
+ return PPCGScop;
+ }
+
+ /// Collect the array accesses in a statement.
+ ///
+ /// @param Stmt The statement for which to collect the accesses.
+ ///
+ /// @returns A list of array accesses.
+ gpu_stmt_access *getStmtAccesses(ScopStmt &Stmt) {
+ gpu_stmt_access *Accesses = nullptr;
+
+ for (MemoryAccess *Acc : Stmt) {
+ auto Access =
+ isl_alloc_type(S->getIslCtx().get(), struct gpu_stmt_access);
+ Access->read = Acc->isRead();
+ Access->write = Acc->isWrite();
+ Access->access = Acc->getAccessRelation().release();
+ isl_space *Space = isl_map_get_space(Access->access);
+ Space = isl_space_range(Space);
+ Space = isl_space_from_range(Space);
+ Space = isl_space_set_tuple_id(Space, isl_dim_in, Acc->getId().release());
+ isl_map *Universe = isl_map_universe(Space);
+ Access->tagged_access =
+ isl_map_domain_product(Acc->getAccessRelation().release(), Universe);
+ Access->exact_write = !Acc->isMayWrite();
+ Access->ref_id = Acc->getId().release();
+ Access->next = Accesses;
+ Access->n_index = Acc->getScopArrayInfo()->getNumberOfDimensions();
+ // TODO: Also mark one-element accesses to arrays as fixed-element.
+ Access->fixed_element =
+ Acc->isLatestScalarKind() ? isl_bool_true : isl_bool_false;
+ Accesses = Access;
+ }
+
+ return Accesses;
+ }
+
+ /// Collect the list of GPU statements.
+ ///
+ /// Each statement has an id, a pointer to the underlying data structure,
+ /// as well as a list with all memory accesses.
+ ///
+ /// TODO: Initialize the list of memory accesses.
+ ///
+ /// @returns A linked-list of statements.
+ gpu_stmt *getStatements() {
+ gpu_stmt *Stmts = isl_calloc_array(S->getIslCtx().get(), struct gpu_stmt,
+ std::distance(S->begin(), S->end()));
+
+ int i = 0;
+ for (auto &Stmt : *S) {
+ gpu_stmt *GPUStmt = &Stmts[i];
+
+ GPUStmt->id = Stmt.getDomainId().release();
+
+ // We use the pet stmt pointer to keep track of the Polly statements.
+ GPUStmt->stmt = (pet_stmt *)&Stmt;
+ GPUStmt->accesses = getStmtAccesses(Stmt);
+ i++;
+ }
+
+ return Stmts;
+ }
+
+ /// Derive the extent of an array.
+ ///
+ /// The extent of an array is the set of elements that are within the
+ /// accessed array. For the inner dimensions, the extent constraints are
+ /// 0 and the size of the corresponding array dimension. For the first
+ /// (outermost) dimension, the extent constraints are the minimal and maximal
+ /// subscript value for the first dimension.
+ ///
+ /// @param Array The array to derive the extent for.
+ ///
+ /// @returns An isl_set describing the extent of the array.
+ isl::set getExtent(ScopArrayInfo *Array) {
+ unsigned NumDims = Array->getNumberOfDimensions();
+
+ if (Array->getNumberOfDimensions() == 0)
+ return isl::set::universe(Array->getSpace());
+
+ isl::union_map Accesses = S->getAccesses(Array);
+ isl::union_set AccessUSet = Accesses.range();
+ AccessUSet = AccessUSet.coalesce();
+ AccessUSet = AccessUSet.detect_equalities();
+ AccessUSet = AccessUSet.coalesce();
+
+ if (AccessUSet.is_empty())
+ return isl::set::empty(Array->getSpace());
+
+ isl::set AccessSet = AccessUSet.extract_set(Array->getSpace());
+
+ isl::local_space LS = isl::local_space(Array->getSpace());
+
+ isl::pw_aff Val = isl::aff::var_on_domain(LS, isl::dim::set, 0);
+ isl::pw_aff OuterMin = AccessSet.dim_min(0);
+ isl::pw_aff OuterMax = AccessSet.dim_max(0);
+ OuterMin = OuterMin.add_dims(isl::dim::in,
+ unsignedFromIslSize(Val.dim(isl::dim::in)));
+ OuterMax = OuterMax.add_dims(isl::dim::in,
+ unsignedFromIslSize(Val.dim(isl::dim::in)));
+ OuterMin = OuterMin.set_tuple_id(isl::dim::in, Array->getBasePtrId());
+ OuterMax = OuterMax.set_tuple_id(isl::dim::in, Array->getBasePtrId());
+
+ isl::set Extent = isl::set::universe(Array->getSpace());
+
+ Extent = Extent.intersect(OuterMin.le_set(Val));
+ Extent = Extent.intersect(OuterMax.ge_set(Val));
+
+ for (unsigned i = 1; i < NumDims; ++i)
+ Extent = Extent.lower_bound_si(isl::dim::set, i, 0);
+
+ for (unsigned i = 0; i < NumDims; ++i) {
+ isl::pw_aff PwAff = Array->getDimensionSizePw(i);
+
+ // isl_pw_aff can be NULL for zero dimension. Only in the case of a
+ // Fortran array will we have a legitimate dimension.
+ if (PwAff.is_null()) {
+ assert(i == 0 && "invalid dimension isl_pw_aff for nonzero dimension");
+ continue;
+ }
+
+ isl::pw_aff Val = isl::aff::var_on_domain(
+ isl::local_space(Array->getSpace()), isl::dim::set, i);
+ PwAff = PwAff.add_dims(isl::dim::in,
+ unsignedFromIslSize(Val.dim(isl::dim::in)));
+ PwAff = PwAff.set_tuple_id(isl::dim::in, Val.get_tuple_id(isl::dim::in));
+ isl::set Set = PwAff.gt_set(Val);
+ Extent = Set.intersect(Extent);
+ }
+
+ return Extent;
+ }
+
+ /// Derive the bounds of an array.
+ ///
+ /// For the first dimension we derive the bound of the array from the extent
+ /// of this dimension. For inner dimensions we obtain their size directly from
+ /// ScopArrayInfo.
+ ///
+ /// @param PPCGArray The array to compute bounds for.
+ /// @param Array The polly array from which to take the information.
+ void setArrayBounds(gpu_array_info &PPCGArray, ScopArrayInfo *Array) {
+ std::vector<isl_pw_aff *> Bounds;
+
+ if (PPCGArray.n_index > 0) {
+ if (isl_set_is_empty(PPCGArray.extent)) {
+ isl_set *Dom = isl_set_copy(PPCGArray.extent);
+ isl_local_space *LS = isl_local_space_from_space(
+ isl_space_params(isl_set_get_space(Dom)));
+ isl_set_free(Dom);
+ isl_pw_aff *Zero = isl_pw_aff_from_aff(isl_aff_zero_on_domain(LS));
+ Bounds.push_back(Zero);
+ } else {
+ isl_set *Dom = isl_set_copy(PPCGArray.extent);
+ Dom = isl_set_project_out(Dom, isl_dim_set, 1, PPCGArray.n_index - 1);
+ isl_pw_aff *Bound = isl_set_dim_max(isl_set_copy(Dom), 0);
+ isl_set_free(Dom);
+ Dom = isl_pw_aff_domain(isl_pw_aff_copy(Bound));
+ isl_local_space *LS =
+ isl_local_space_from_space(isl_set_get_space(Dom));
+ isl_aff *One = isl_aff_zero_on_domain(LS);
+ One = isl_aff_add_constant_si(One, 1);
+ Bound = isl_pw_aff_add(Bound, isl_pw_aff_alloc(Dom, One));
+ Bound = isl_pw_aff_gist(Bound, S->getContext().release());
+ Bounds.push_back(Bound);
+ }
+ }
+
+ for (unsigned i = 1; i < PPCGArray.n_index; ++i) {
+ isl_pw_aff *Bound = Array->getDimensionSizePw(i).release();
+ auto LS = isl_pw_aff_get_domain_space(Bound);
+ auto Aff = isl_multi_aff_zero(LS);
+
+ // We need types to work out, which is why we perform this weird dance
+ // with `Aff` and `Bound`. Consider this example:
+
+ // LS: [p] -> { [] }
+ // Zero: [p] -> { [] } | Implicitly, is [p] -> { ~ -> [] }.
+ // This `~` is used to denote a "null space" (which is different from
+ // a *zero dimensional* space), which is something that ISL does not
+ // show you when pretty printing.
+
+ // Bound: [p] -> { [] -> [(10p)] } | Here, the [] is a *zero dimensional*
+ // space, not a "null space" which does not exist at all.
+
+ // When we pullback (precompose) `Bound` with `Zero`, we get:
+ // Bound . Zero =
+ // ([p] -> { [] -> [(10p)] }) . ([p] -> {~ -> [] }) =
+ // [p] -> { ~ -> [(10p)] } =
+ // [p] -> [(10p)] (as ISL pretty prints it)
+ // Bound Pullback: [p] -> { [(10p)] }
+
+ // We want this kind of an expression for Bound, without a
+ // zero dimensional input, but with a "null space" input for the types
+ // to work out later on, as far as I (Siddharth Bhat) understand.
+ // I was unable to find a reference to this in the ISL manual.
+ // References: Tobias Grosser.
+
+ Bound = isl_pw_aff_pullback_multi_aff(Bound, Aff);
+ Bounds.push_back(Bound);
+ }
+
+ /// To construct a `isl_multi_pw_aff`, we need all the indivisual `pw_aff`
+ /// to have the same parameter dimensions. So, we need to align them to an
+ /// appropriate space.
+ /// Scop::Context is _not_ an appropriate space, because when we have
+ /// `-polly-ignore-parameter-bounds` enabled, the Scop::Context does not
+ /// contain all parameter dimensions.
+ /// So, use the helper `alignPwAffs` to align all the `isl_pw_aff` together.
+ isl_space *SeedAlignSpace = S->getParamSpace().release();
+ SeedAlignSpace = isl_space_add_dims(SeedAlignSpace, isl_dim_set, 1);
+
+ isl_space *AlignSpace = nullptr;
+ std::vector<isl_pw_aff *> AlignedBounds;
+ std::tie(AlignSpace, AlignedBounds) =
+ alignPwAffs(std::move(Bounds), SeedAlignSpace);
+
+ assert(AlignSpace && "alignPwAffs did not initialise AlignSpace");
+
+ isl_pw_aff_list *BoundsList =
+ createPwAffList(S->getIslCtx().get(), std::move(AlignedBounds));
+
+ isl_space *BoundsSpace = isl_set_get_space(PPCGArray.extent);
+ BoundsSpace = isl_space_align_params(BoundsSpace, AlignSpace);
+
+ assert(BoundsSpace && "Unable to access space of array.");
+ assert(BoundsList && "Unable to access list of bounds.");
+
+ PPCGArray.bound =
+ isl_multi_pw_aff_from_pw_aff_list(BoundsSpace, BoundsList);
+ assert(PPCGArray.bound && "PPCGArray.bound was not constructed correctly.");
+ }
+
+ /// Create the arrays for @p PPCGProg.
+ ///
+ /// @param PPCGProg The program to compute the arrays for.
+ void createArrays(gpu_prog *PPCGProg,
+ const SmallVector<ScopArrayInfo *, 4> &ValidSAIs) {
+ int i = 0;
+ for (auto &Array : ValidSAIs) {
+ std::string TypeName;
+ raw_string_ostream OS(TypeName);
+
+ OS << *Array->getElementType();
+ TypeName = OS.str();
+
+ gpu_array_info &PPCGArray = PPCGProg->array[i];
+
+ PPCGArray.space = Array->getSpace().release();
+ PPCGArray.type = strdup(TypeName.c_str());
+ PPCGArray.size = DL->getTypeAllocSize(Array->getElementType());
+ PPCGArray.name = strdup(Array->getName().c_str());
+ PPCGArray.extent = nullptr;
+ PPCGArray.n_index = Array->getNumberOfDimensions();
+ PPCGArray.extent = getExtent(Array).release();
+ PPCGArray.n_ref = 0;
+ PPCGArray.refs = nullptr;
+ PPCGArray.accessed = true;
+ PPCGArray.read_only_scalar =
+ Array->isReadOnly() && Array->getNumberOfDimensions() == 0;
+ PPCGArray.has_compound_element = false;
+ PPCGArray.local = false;
+ PPCGArray.declare_local = false;
+ PPCGArray.global = false;
+ PPCGArray.linearize = false;
+ PPCGArray.dep_order = nullptr;
+ PPCGArray.user = Array;
+
+ PPCGArray.bound = nullptr;
+ setArrayBounds(PPCGArray, Array);
+ i++;
+
+ collect_references(PPCGProg, &PPCGArray);
+ PPCGArray.only_fixed_element = only_fixed_element_accessed(&PPCGArray);
+ }
+ }
+
+ /// Create an identity map between the arrays in the scop.
+ ///
+ /// @returns An identity map between the arrays in the scop.
+ isl_union_map *getArrayIdentity() {
+ isl_union_map *Maps = isl_union_map_empty(S->getParamSpace().release());
+
+ for (auto &Array : S->arrays()) {
+ isl_space *Space = Array->getSpace().release();
+ Space = isl_space_map_from_set(Space);
+ isl_map *Identity = isl_map_identity(Space);
+ Maps = isl_union_map_add_map(Maps, Identity);
+ }
+
+ return Maps;
+ }
+
+ /// Create a default-initialized PPCG GPU program.
+ ///
+ /// @returns A new gpu program description.
+ gpu_prog *createPPCGProg(ppcg_scop *PPCGScop) {
+
+ if (!PPCGScop)
+ return nullptr;
+
+ auto PPCGProg = isl_calloc_type(S->getIslCtx().get(), struct gpu_prog);
+
+ PPCGProg->ctx = S->getIslCtx().get();
+ PPCGProg->scop = PPCGScop;
+ PPCGProg->context = isl_set_copy(PPCGScop->context);
+ PPCGProg->read = isl_union_map_copy(PPCGScop->reads);
+ PPCGProg->may_write = isl_union_map_copy(PPCGScop->may_writes);
+ PPCGProg->must_write = isl_union_map_copy(PPCGScop->must_writes);
+ PPCGProg->tagged_must_kill =
+ isl_union_map_copy(PPCGScop->tagged_must_kills);
+ PPCGProg->to_inner = getArrayIdentity();
+ PPCGProg->to_outer = getArrayIdentity();
+ // TODO: verify that this assignment is correct.
+ PPCGProg->any_to_outer = nullptr;
+ PPCGProg->n_stmts = std::distance(S->begin(), S->end());
+ PPCGProg->stmts = getStatements();
+
+ // Only consider arrays that have a non-empty extent.
+ // Otherwise, this will cause us to consider the following kinds of
+ // empty arrays:
+ // 1. Invariant loads that are represented by SAI objects.
+ // 2. Arrays with statically known zero size.
+ auto ValidSAIsRange =
+ make_filter_range(S->arrays(), [this](ScopArrayInfo *SAI) -> bool {
+ return !getExtent(SAI).is_empty();
+ });
+ SmallVector<ScopArrayInfo *, 4> ValidSAIs(ValidSAIsRange.begin(),
+ ValidSAIsRange.end());
+
+ PPCGProg->n_array =
+ ValidSAIs.size(); // std::distance(S->array_begin(), S->array_end());
+ PPCGProg->array = isl_calloc_array(
+ S->getIslCtx().get(), struct gpu_array_info, PPCGProg->n_array);
+
+ createArrays(PPCGProg, ValidSAIs);
+
+ PPCGProg->array_order = nullptr;
+ collect_order_dependences(PPCGProg);
+
+ PPCGProg->may_persist = compute_may_persist(PPCGProg);
+ return PPCGProg;
+ }
+
+ struct PrintGPUUserData {
+ struct cuda_info *CudaInfo;
+ struct gpu_prog *PPCGProg;
+ std::vector<ppcg_kernel *> Kernels;
+ };
+
+ /// Print a user statement node in the host code.
+ ///
+ /// We use ppcg's printing facilities to print the actual statement and
+ /// additionally build up a list of all kernels that are encountered in the
+ /// host ast.
+ ///
+ /// @param P The printer to print to
+ /// @param Options The printing options to use
+ /// @param Node The node to print
+ /// @param User A user pointer to carry additional data. This pointer is
+ /// expected to be of type PrintGPUUserData.
+ ///
+ /// @returns A printer to which the output has been printed.
+ static __isl_give isl_printer *
+ printHostUser(__isl_take isl_printer *P,
+ __isl_take isl_ast_print_options *Options,
+ __isl_take isl_ast_node *Node, void *User) {
+ auto Data = (struct PrintGPUUserData *)User;
+ auto Id = isl_ast_node_get_annotation(Node);
+
+ if (Id) {
+ bool IsUser = !strcmp(isl_id_get_name(Id), "user");
+
+ // If this is a user statement, format it ourselves as ppcg would
+ // otherwise try to call pet functionality that is not available in
+ // Polly.
+ if (IsUser) {
+ P = isl_printer_start_line(P);
+ P = isl_printer_print_ast_node(P, Node);
+ P = isl_printer_end_line(P);
+ isl_id_free(Id);
+ isl_ast_print_options_free(Options);
+ return P;
+ }
+
+ auto Kernel = (struct ppcg_kernel *)isl_id_get_user(Id);
+ isl_id_free(Id);
+ Data->Kernels.push_back(Kernel);
+ }
+
+ return print_host_user(P, Options, Node, User);
+ }
+
+ /// Print C code corresponding to the control flow in @p Kernel.
+ ///
+ /// @param Kernel The kernel to print
+ void printKernel(ppcg_kernel *Kernel) {
+ auto *P = isl_printer_to_str(S->getIslCtx().get());
+ P = isl_printer_set_output_format(P, ISL_FORMAT_C);
+ auto *Options = isl_ast_print_options_alloc(S->getIslCtx().get());
+ P = isl_ast_node_print(Kernel->tree, P, Options);
+ char *String = isl_printer_get_str(P);
+ outs() << String << "\n";
+ free(String);
+ isl_printer_free(P);
+ }
+
+ /// Print C code corresponding to the GPU code described by @p Tree.
+ ///
+ /// @param Tree An AST describing GPU code
+ /// @param PPCGProg The PPCG program from which @Tree has been constructed.
+ void printGPUTree(isl_ast_node *Tree, gpu_prog *PPCGProg) {
+ auto *P = isl_printer_to_str(S->getIslCtx().get());
+ P = isl_printer_set_output_format(P, ISL_FORMAT_C);
+
+ PrintGPUUserData Data;
+ Data.PPCGProg = PPCGProg;
+
+ auto *Options = isl_ast_print_options_alloc(S->getIslCtx().get());
+ Options =
+ isl_ast_print_options_set_print_user(Options, printHostUser, &Data);
+ P = isl_ast_node_print(Tree, P, Options);
+ char *String = isl_printer_get_str(P);
+ outs() << "# host\n";
+ outs() << String << "\n";
+ free(String);
+ isl_printer_free(P);
+
+ for (auto Kernel : Data.Kernels) {
+ outs() << "# kernel" << Kernel->id << "\n";
+ printKernel(Kernel);
+ }
+ }
+
+ // Generate a GPU program using PPCG.
+ //
+ // GPU mapping consists of multiple steps:
+ //
+ // 1) Compute new schedule for the program.
+ // 2) Map schedule to GPU (TODO)
+ // 3) Generate code for new schedule (TODO)
+ //
+ // We do not use here the Polly ScheduleOptimizer, as the schedule optimizer
+ // is mostly CPU specific. Instead, we use PPCG's GPU code generation
+ // strategy directly from this pass.
+ gpu_gen *generateGPU(ppcg_scop *PPCGScop, gpu_prog *PPCGProg) {
+
+ auto PPCGGen = isl_calloc_type(S->getIslCtx().get(), struct gpu_gen);
+
+ PPCGGen->ctx = S->getIslCtx().get();
+ PPCGGen->options = PPCGScop->options;
+ PPCGGen->print = nullptr;
+ PPCGGen->print_user = nullptr;
+ PPCGGen->build_ast_expr = &pollyBuildAstExprForStmt;
+ PPCGGen->prog = PPCGProg;
+ PPCGGen->tree = nullptr;
+ PPCGGen->types.n = 0;
+ PPCGGen->types.name = nullptr;
+ PPCGGen->sizes = nullptr;
+ PPCGGen->used_sizes = nullptr;
+ PPCGGen->kernel_id = 0;
+
+ // Set scheduling strategy to same strategy PPCG is using.
+ isl_options_set_schedule_serialize_sccs(PPCGGen->ctx, false);
+ isl_options_set_schedule_outer_coincidence(PPCGGen->ctx, true);
+ isl_options_set_schedule_maximize_band_depth(PPCGGen->ctx, true);
+ isl_options_set_schedule_whole_component(PPCGGen->ctx, false);
+
+ isl_schedule *Schedule = get_schedule(PPCGGen);
+
+ int has_permutable = has_any_permutable_node(Schedule);
+
+ Schedule =
+ isl_schedule_align_params(Schedule, S->getFullParamSpace().release());
+
+ if (!has_permutable || has_permutable < 0) {
+ Schedule = isl_schedule_free(Schedule);
+ LLVM_DEBUG(dbgs() << getUniqueScopName(S)
+ << " does not have permutable bands. Bailing out\n";);
+ } else {
+ const bool CreateTransferToFromDevice = !PollyManagedMemory;
+ Schedule = map_to_device(PPCGGen, Schedule, CreateTransferToFromDevice);
+ PPCGGen->tree = generate_code(PPCGGen, isl_schedule_copy(Schedule));
+ }
+
+ if (DumpSchedule) {
+ isl_printer *P = isl_printer_to_str(S->getIslCtx().get());
+ P = isl_printer_set_yaml_style(P, ISL_YAML_STYLE_BLOCK);
+ P = isl_printer_print_str(P, "Schedule\n");
+ P = isl_printer_print_str(P, "========\n");
+ if (Schedule)
+ P = isl_printer_print_schedule(P, Schedule);
+ else
+ P = isl_printer_print_str(P, "No schedule found\n");
+
+ outs() << isl_printer_get_str(P) << "\n";
+ isl_printer_free(P);
+ }
+
+ if (DumpCode) {
+ outs() << "Code\n";
+ outs() << "====\n";
+ if (PPCGGen->tree)
+ printGPUTree(PPCGGen->tree, PPCGProg);
+ else
+ outs() << "No code generated\n";
+ }
+
+ isl_schedule_free(Schedule);
+
+ return PPCGGen;
+ }
+
+ /// Free gpu_gen structure.
+ ///
+ /// @param PPCGGen The ppcg_gen object to free.
+ void freePPCGGen(gpu_gen *PPCGGen) {
+ isl_ast_node_free(PPCGGen->tree);
+ isl_union_map_free(PPCGGen->sizes);
+ isl_union_map_free(PPCGGen->used_sizes);
+ free(PPCGGen);
+ }
+
+ /// Free the options in the ppcg scop structure.
+ ///
+ /// ppcg is not freeing these options for us. To avoid leaks we do this
+ /// ourselves.
+ ///
+ /// @param PPCGScop The scop referencing the options to free.
+ void freeOptions(ppcg_scop *PPCGScop) {
+ free(PPCGScop->options->debug);
+ PPCGScop->options->debug = nullptr;
+ free(PPCGScop->options);
+ PPCGScop->options = nullptr;
+ }
+
+ /// Approximate the number of points in the set.
+ ///
+ /// This function returns an ast expression that overapproximates the number
+ /// of points in an isl set through the rectangular hull surrounding this set.
+ ///
+ /// @param Set The set to count.
+ /// @param Build The isl ast build object to use for creating the ast
+ /// expression.
+ ///
+ /// @returns An approximation of the number of points in the set.
+ __isl_give isl_ast_expr *approxPointsInSet(__isl_take isl_set *Set,
+ __isl_keep isl_ast_build *Build) {
+
+ isl_val *One = isl_val_int_from_si(isl_set_get_ctx(Set), 1);
+ auto *Expr = isl_ast_expr_from_val(isl_val_copy(One));
+
+ isl_space *Space = isl_set_get_space(Set);
+ Space = isl_space_params(Space);
+ auto *Univ = isl_set_universe(Space);
+ isl_pw_aff *OneAff = isl_pw_aff_val_on_domain(Univ, One);
+
+ for (long i = 0, n = isl_set_dim(Set, isl_dim_set); i < n; i++) {
+ isl_pw_aff *Max = isl_set_dim_max(isl_set_copy(Set), i);
+ isl_pw_aff *Min = isl_set_dim_min(isl_set_copy(Set), i);
+ isl_pw_aff *DimSize = isl_pw_aff_sub(Max, Min);
+ DimSize = isl_pw_aff_add(DimSize, isl_pw_aff_copy(OneAff));
+ auto DimSizeExpr = isl_ast_build_expr_from_pw_aff(Build, DimSize);
+ Expr = isl_ast_expr_mul(Expr, DimSizeExpr);
+ }
+
+ isl_set_free(Set);
+ isl_pw_aff_free(OneAff);
+
+ return Expr;
+ }
+
+ /// Approximate a number of dynamic instructions executed by a given
+ /// statement.
+ ///
+ /// @param Stmt The statement for which to compute the number of dynamic
+ /// instructions.
+ /// @param Build The isl ast build object to use for creating the ast
+ /// expression.
+ /// @returns An approximation of the number of dynamic instructions executed
+ /// by @p Stmt.
+ __isl_give isl_ast_expr *approxDynamicInst(ScopStmt &Stmt,
+ __isl_keep isl_ast_build *Build) {
+ auto Iterations = approxPointsInSet(Stmt.getDomain().release(), Build);
+
+ long InstCount = 0;
+
+ if (Stmt.isBlockStmt()) {
+ auto *BB = Stmt.getBasicBlock();
+ InstCount = std::distance(BB->begin(), BB->end());
+ } else {
+ auto *R = Stmt.getRegion();
+
+ for (auto *BB : R->blocks()) {
+ InstCount += std::distance(BB->begin(), BB->end());
+ }
+ }
+
+ isl_val *InstVal = isl_val_int_from_si(S->getIslCtx().get(), InstCount);
+ auto *InstExpr = isl_ast_expr_from_val(InstVal);
+ return isl_ast_expr_mul(InstExpr, Iterations);
+ }
+
+ /// Approximate dynamic instructions executed in scop.
+ ///
+ /// @param S The scop for which to approximate dynamic instructions.
+ /// @param Build The isl ast build object to use for creating the ast
+ /// expression.
+ /// @returns An approximation of the number of dynamic instructions executed
+ /// in @p S.
+ __isl_give isl_ast_expr *
+ getNumberOfIterations(Scop &S, __isl_keep isl_ast_build *Build) {
+ isl_ast_expr *Instructions;
+
+ isl_val *Zero = isl_val_int_from_si(S.getIslCtx().get(), 0);
+ Instructions = isl_ast_expr_from_val(Zero);
+
+ for (ScopStmt &Stmt : S) {
+ isl_ast_expr *StmtInstructions = approxDynamicInst(Stmt, Build);
+ Instructions = isl_ast_expr_add(Instructions, StmtInstructions);
+ }
+ return Instructions;
+ }
+
+ /// Create a check that ensures sufficient compute in scop.
+ ///
+ /// @param S The scop for which to ensure sufficient compute.
+ /// @param Build The isl ast build object to use for creating the ast
+ /// expression.
+ /// @returns An expression that evaluates to TRUE in case of sufficient
+ /// compute and to FALSE, otherwise.
+ __isl_give isl_ast_expr *
+ createSufficientComputeCheck(Scop &S, __isl_keep isl_ast_build *Build) {
+ auto Iterations = getNumberOfIterations(S, Build);
+ auto *MinComputeVal = isl_val_int_from_si(S.getIslCtx().get(), MinCompute);
+ auto *MinComputeExpr = isl_ast_expr_from_val(MinComputeVal);
+ return isl_ast_expr_ge(Iterations, MinComputeExpr);
+ }
+
+ /// Check if the basic block contains a function we cannot codegen for GPU
+ /// kernels.
+ ///
+ /// If this basic block does something with a `Function` other than calling
+ /// a function that we support in a kernel, return true.
+ bool containsInvalidKernelFunctionInBlock(const BasicBlock *BB,
+ bool AllowCUDALibDevice) {
+ for (const Instruction &Inst : *BB) {
+ const CallInst *Call = dyn_cast<CallInst>(&Inst);
+ if (Call && isValidFunctionInKernel(Call->getCalledFunction(),
+ AllowCUDALibDevice))
+ continue;
+
+ for (Value *Op : Inst.operands())
+ // Look for (<func-type>*) among operands of Inst
+ if (auto PtrTy = dyn_cast<PointerType>(Op->getType())) {
+ if (isa<FunctionType>(PtrTy->getElementType())) {
+ LLVM_DEBUG(dbgs()
+ << Inst << " has illegal use of function in kernel.\n");
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /// Return whether the Scop S uses functions in a way that we do not support.
+ bool containsInvalidKernelFunction(const Scop &S, bool AllowCUDALibDevice) {
+ for (auto &Stmt : S) {
+ if (Stmt.isBlockStmt()) {
+ if (containsInvalidKernelFunctionInBlock(Stmt.getBasicBlock(),
+ AllowCUDALibDevice))
+ return true;
+ } else {
+ assert(Stmt.isRegionStmt() &&
+ "Stmt was neither block nor region statement");
+ for (const BasicBlock *BB : Stmt.getRegion()->blocks())
+ if (containsInvalidKernelFunctionInBlock(BB, AllowCUDALibDevice))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Generate code for a given GPU AST described by @p Root.
+ ///
+ /// @param Root An isl_ast_node pointing to the root of the GPU AST.
+ /// @param Prog The GPU Program to generate code for.
+ void generateCode(__isl_take isl_ast_node *Root, gpu_prog *Prog) {
+ ScopAnnotator Annotator;
+ Annotator.buildAliasScopes(*S);
+
+ Region *R = &S->getRegion();
+
+ simplifyRegion(R, DT, LI, RI);
+
+ BasicBlock *EnteringBB = R->getEnteringBlock();
+
+ PollyIRBuilder Builder(EnteringBB->getContext(), ConstantFolder(),
+ IRInserter(Annotator));
+ Builder.SetInsertPoint(EnteringBB->getTerminator());
+
+ // Only build the run-time condition and parameters _after_ having
+ // introduced the conditional branch. This is important as the conditional
+ // branch will guard the original scop from new induction variables that
+ // the SCEVExpander may introduce while code generating the parameters and
+ // which may introduce scalar dependences that prevent us from correctly
+ // code generating this scop.
+ BBPair StartExitBlocks;
+ BranchInst *CondBr = nullptr;
+ std::tie(StartExitBlocks, CondBr) =
+ executeScopConditionally(*S, Builder.getTrue(), *DT, *RI, *LI);
+ BasicBlock *StartBlock = std::get<0>(StartExitBlocks);
+
+ assert(CondBr && "CondBr not initialized by executeScopConditionally");
+
+ GPUNodeBuilder NodeBuilder(Builder, Annotator, *DL, *LI, *SE, *DT, *S,
+ StartBlock, Prog, Runtime, Architecture);
+
+ // TODO: Handle LICM
+ auto SplitBlock = StartBlock->getSinglePredecessor();
+ Builder.SetInsertPoint(SplitBlock->getTerminator());
+
+ isl_ast_build *Build = isl_ast_build_alloc(S->getIslCtx().get());
+ isl::ast_expr Condition =
+ IslAst::buildRunCondition(*S, isl::manage_copy(Build));
+ isl_ast_expr *SufficientCompute = createSufficientComputeCheck(*S, Build);
+ Condition =
+ isl::manage(isl_ast_expr_and(Condition.release(), SufficientCompute));
+ isl_ast_build_free(Build);
+
+ // preload invariant loads. Note: This should happen before the RTC
+ // because the RTC may depend on values that are invariant load hoisted.
+ if (!NodeBuilder.preloadInvariantLoads()) {
+ // Patch the introduced branch condition to ensure that we always execute
+ // the original SCoP.
+ auto *FalseI1 = Builder.getFalse();
+ auto *SplitBBTerm = Builder.GetInsertBlock()->getTerminator();
+ SplitBBTerm->setOperand(0, FalseI1);
+
+ LLVM_DEBUG(dbgs() << "preloading invariant loads failed in function: " +
+ S->getFunction().getName() +
+ " | Scop Region: " + S->getNameStr());
+ // adjust the dominator tree accordingly.
+ auto *ExitingBlock = StartBlock->getUniqueSuccessor();
+ assert(ExitingBlock);
+ auto *MergeBlock = ExitingBlock->getUniqueSuccessor();
+ assert(MergeBlock);
+ polly::markBlockUnreachable(*StartBlock, Builder);
+ polly::markBlockUnreachable(*ExitingBlock, Builder);
+ auto *ExitingBB = S->getExitingBlock();
+ assert(ExitingBB);
+
+ DT->changeImmediateDominator(MergeBlock, ExitingBB);
+ DT->eraseNode(ExitingBlock);
+ isl_ast_node_free(Root);
+ } else {
+
+ if (polly::PerfMonitoring) {
+ PerfMonitor P(*S, EnteringBB->getParent()->getParent());
+ P.initialize();
+ P.insertRegionStart(SplitBlock->getTerminator());
+
+ // TODO: actually think if this is the correct exiting block to place
+ // the `end` performance marker. Invariant load hoisting changes
+ // the CFG in a way that I do not precisely understand, so I
+ // (Siddharth<siddu.druid@gmail.com>) should come back to this and
+ // think about which exiting block to use.
+ auto *ExitingBlock = StartBlock->getUniqueSuccessor();
+ assert(ExitingBlock);
+ BasicBlock *MergeBlock = ExitingBlock->getUniqueSuccessor();
+ P.insertRegionEnd(MergeBlock->getTerminator());
+ }
+
+ NodeBuilder.addParameters(S->getContext().release());
+ Value *RTC = NodeBuilder.createRTC(Condition.release());
+ Builder.GetInsertBlock()->getTerminator()->setOperand(0, RTC);
+
+ Builder.SetInsertPoint(&*StartBlock->begin());
+
+ NodeBuilder.create(Root);
+ }
+
+ /// In case a sequential kernel has more surrounding loops as any parallel
+ /// kernel, the SCoP is probably mostly sequential. Hence, there is no
+ /// point in running it on a GPU.
+ if (NodeBuilder.DeepestSequential > NodeBuilder.DeepestParallel)
+ CondBr->setOperand(0, Builder.getFalse());
+
+ if (!NodeBuilder.BuildSuccessful)
+ CondBr->setOperand(0, Builder.getFalse());
+ }
+
+ bool runOnScop(Scop &CurrentScop) override {
+ S = &CurrentScop;
+ LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+ DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
+ SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
+ DL = &S->getRegion().getEntry()->getModule()->getDataLayout();
+ RI = &getAnalysis<RegionInfoPass>().getRegionInfo();
+
+ LLVM_DEBUG(dbgs() << "PPCGCodeGen running on : " << getUniqueScopName(S)
+ << " | loop depth: " << S->getMaxLoopDepth() << "\n");
+
+ // We currently do not support functions other than intrinsics inside
+ // kernels, as code generation will need to offload function calls to the
+ // kernel. This may lead to a kernel trying to call a function on the host.
+ // This also allows us to prevent codegen from trying to take the
+ // address of an intrinsic function to send to the kernel.
+ if (containsInvalidKernelFunction(CurrentScop,
+ Architecture == GPUArch::NVPTX64)) {
+ LLVM_DEBUG(
+ dbgs() << getUniqueScopName(S)
+ << " contains function which cannot be materialised in a GPU "
+ "kernel. Bailing out.\n";);
+ return false;
+ }
+
+ auto PPCGScop = createPPCGScop();
+ auto PPCGProg = createPPCGProg(PPCGScop);
+ auto PPCGGen = generateGPU(PPCGScop, PPCGProg);
+
+ if (PPCGGen->tree) {
+ generateCode(isl_ast_node_copy(PPCGGen->tree), PPCGProg);
+ CurrentScop.markAsToBeSkipped();
+ } else {
+ LLVM_DEBUG(dbgs() << getUniqueScopName(S)
+ << " has empty PPCGGen->tree. Bailing out.\n");
+ }
+
+ freeOptions(PPCGScop);
+ freePPCGGen(PPCGGen);
+ gpu_prog_free(PPCGProg);
+ ppcg_scop_free(PPCGScop);
+
+ return true;
+ }
+
+ void printScop(raw_ostream &, Scop &) const override {}
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ ScopPass::getAnalysisUsage(AU);
+
+ AU.addRequired<DominatorTreeWrapperPass>();
+ AU.addRequired<RegionInfoPass>();
+ AU.addRequired<ScalarEvolutionWrapperPass>();
+ AU.addRequired<ScopDetectionWrapperPass>();
+ AU.addRequired<ScopInfoRegionPass>();
+ AU.addRequired<LoopInfoWrapperPass>();
+
+ // FIXME: We do not yet add regions for the newly generated code to the
+ // region tree.
+ }
+};
+} // namespace
+
+char PPCGCodeGeneration::ID = 1;
+
+Pass *polly::createPPCGCodeGenerationPass(GPUArch Arch, GPURuntime Runtime) {
+ PPCGCodeGeneration *generator = new PPCGCodeGeneration();
+ generator->Runtime = Runtime;
+ generator->Architecture = Arch;
+ return generator;
+}
+
+INITIALIZE_PASS_BEGIN(PPCGCodeGeneration, "polly-codegen-ppcg",
+ "Polly - Apply PPCG translation to SCOP", false, false)
+INITIALIZE_PASS_DEPENDENCY(DependenceInfo);
+INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(RegionInfoPass);
+INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(ScopDetectionWrapperPass);
+INITIALIZE_PASS_END(PPCGCodeGeneration, "polly-codegen-ppcg",
+ "Polly - Apply PPCG translation to SCOP", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/PerfMonitor.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/PerfMonitor.cpp
new file mode 100644
index 00000000000..ccd8990283e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/PerfMonitor.cpp
@@ -0,0 +1,303 @@
+//===------ PerfMonitor.cpp - Generate a run-time performance monitor. -======//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/PerfMonitor.h"
+#include "polly/CodeGen/RuntimeDebugBuilder.h"
+#include "polly/ScopInfo.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/IR/IntrinsicsX86.h"
+
+using namespace llvm;
+using namespace polly;
+
+Function *PerfMonitor::getAtExit() {
+ const char *Name = "atexit";
+ Function *F = M->getFunction(Name);
+
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ FunctionType *Ty = FunctionType::get(Builder.getInt32Ty(),
+ {Builder.getInt8PtrTy()}, false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ return F;
+}
+
+void PerfMonitor::addToGlobalConstructors(Function *Fn) {
+ const char *Name = "llvm.global_ctors";
+ GlobalVariable *GV = M->getGlobalVariable(Name);
+ std::vector<Constant *> V;
+
+ if (GV) {
+ Constant *Array = GV->getInitializer();
+ for (Value *X : Array->operand_values())
+ V.push_back(cast<Constant>(X));
+ GV->eraseFromParent();
+ }
+
+ StructType *ST = StructType::get(Builder.getInt32Ty(), Fn->getType(),
+ Builder.getInt8PtrTy());
+
+ V.push_back(
+ ConstantStruct::get(ST, Builder.getInt32(10), Fn,
+ ConstantPointerNull::get(Builder.getInt8PtrTy())));
+ ArrayType *Ty = ArrayType::get(ST, V.size());
+
+ GV = new GlobalVariable(*M, Ty, true, GlobalValue::AppendingLinkage,
+ ConstantArray::get(Ty, V), Name, nullptr,
+ GlobalVariable::NotThreadLocal);
+}
+
+Function *PerfMonitor::getRDTSCP() {
+ return Intrinsic::getDeclaration(M, Intrinsic::x86_rdtscp);
+}
+
+PerfMonitor::PerfMonitor(const Scop &S, Module *M)
+ : M(M), Builder(M->getContext()), S(S) {
+ if (Triple(M->getTargetTriple()).getArch() == llvm::Triple::x86_64)
+ Supported = true;
+ else
+ Supported = false;
+}
+
+static void TryRegisterGlobal(Module *M, const char *Name,
+ Constant *InitialValue, Value **Location) {
+ *Location = M->getGlobalVariable(Name);
+
+ if (!*Location)
+ *Location = new GlobalVariable(
+ *M, InitialValue->getType(), true, GlobalValue::WeakAnyLinkage,
+ InitialValue, Name, nullptr, GlobalVariable::InitialExecTLSModel);
+}
+
+// Generate a unique name that is usable as a LLVM name for a scop to name its
+// performance counter.
+static std::string GetScopUniqueVarname(const Scop &S) {
+ std::string EntryString, ExitString;
+ std::tie(EntryString, ExitString) = S.getEntryExitStr();
+
+ return (Twine("__polly_perf_in_") + S.getFunction().getName() + "_from__" +
+ EntryString + "__to__" + ExitString)
+ .str();
+}
+
+void PerfMonitor::addScopCounter() {
+ const std::string varname = GetScopUniqueVarname(S);
+ TryRegisterGlobal(M, (varname + "_cycles").c_str(), Builder.getInt64(0),
+ &CyclesInCurrentScopPtr);
+
+ TryRegisterGlobal(M, (varname + "_trip_count").c_str(), Builder.getInt64(0),
+ &TripCountForCurrentScopPtr);
+}
+
+void PerfMonitor::addGlobalVariables() {
+ TryRegisterGlobal(M, "__polly_perf_cycles_total_start", Builder.getInt64(0),
+ &CyclesTotalStartPtr);
+
+ TryRegisterGlobal(M, "__polly_perf_initialized", Builder.getInt1(false),
+ &AlreadyInitializedPtr);
+
+ TryRegisterGlobal(M, "__polly_perf_cycles_in_scops", Builder.getInt64(0),
+ &CyclesInScopsPtr);
+
+ TryRegisterGlobal(M, "__polly_perf_cycles_in_scop_start", Builder.getInt64(0),
+ &CyclesInScopStartPtr);
+}
+
+static const char *InitFunctionName = "__polly_perf_init";
+static const char *FinalReportingFunctionName = "__polly_perf_final";
+
+static BasicBlock *FinalStartBB = nullptr;
+static ReturnInst *ReturnFromFinal = nullptr;
+
+Function *PerfMonitor::insertFinalReporting() {
+ // Create new function.
+ GlobalValue::LinkageTypes Linkage = Function::WeakODRLinkage;
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), {}, false);
+ Function *ExitFn =
+ Function::Create(Ty, Linkage, FinalReportingFunctionName, M);
+ FinalStartBB = BasicBlock::Create(M->getContext(), "start", ExitFn);
+ Builder.SetInsertPoint(FinalStartBB);
+
+ if (!Supported) {
+ RuntimeDebugBuilder::createCPUPrinter(
+ Builder, "Polly runtime information generation not supported\n");
+ Builder.CreateRetVoid();
+ return ExitFn;
+ }
+
+ // Measure current cycles and compute final timings.
+ Function *RDTSCPFn = getRDTSCP();
+
+ Type *Int64Ty = Builder.getInt64Ty();
+ Value *CurrentCycles =
+ Builder.CreateExtractValue(Builder.CreateCall(RDTSCPFn), {0});
+ Value *CyclesStart = Builder.CreateLoad(Int64Ty, CyclesTotalStartPtr, true);
+ Value *CyclesTotal = Builder.CreateSub(CurrentCycles, CyclesStart);
+ Value *CyclesInScops = Builder.CreateLoad(Int64Ty, CyclesInScopsPtr, true);
+
+ // Print the runtime information.
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "Polly runtime information\n");
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "-------------------------\n");
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "Total: ", CyclesTotal, "\n");
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "Scops: ", CyclesInScops,
+ "\n");
+
+ // Print the preamble for per-scop information.
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "\n");
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "Per SCoP information\n");
+ RuntimeDebugBuilder::createCPUPrinter(Builder, "--------------------\n");
+
+ RuntimeDebugBuilder::createCPUPrinter(
+ Builder, "scop function, "
+ "entry block name, exit block name, total time, trip count\n");
+ ReturnFromFinal = Builder.CreateRetVoid();
+ return ExitFn;
+}
+
+void PerfMonitor::AppendScopReporting() {
+ if (!Supported)
+ return;
+
+ assert(FinalStartBB && "Expected FinalStartBB to be initialized by "
+ "PerfMonitor::insertFinalReporting.");
+ assert(ReturnFromFinal && "Expected ReturnFromFinal to be initialized by "
+ "PerfMonitor::insertFinalReporting.");
+
+ Builder.SetInsertPoint(FinalStartBB);
+ ReturnFromFinal->eraseFromParent();
+
+ Type *Int64Ty = Builder.getInt64Ty();
+ Value *CyclesInCurrentScop =
+ Builder.CreateLoad(Int64Ty, this->CyclesInCurrentScopPtr, true);
+
+ Value *TripCountForCurrentScop =
+ Builder.CreateLoad(Int64Ty, this->TripCountForCurrentScopPtr, true);
+
+ std::string EntryName, ExitName;
+ std::tie(EntryName, ExitName) = S.getEntryExitStr();
+
+ // print in CSV for easy parsing with other tools.
+ RuntimeDebugBuilder::createCPUPrinter(
+ Builder, S.getFunction().getName(), ", ", EntryName, ", ", ExitName, ", ",
+ CyclesInCurrentScop, ", ", TripCountForCurrentScop, "\n");
+
+ ReturnFromFinal = Builder.CreateRetVoid();
+}
+
+static Function *FinalReporting = nullptr;
+
+void PerfMonitor::initialize() {
+ addGlobalVariables();
+ addScopCounter();
+
+ // Ensure that we only add the final reporting function once.
+ // On later invocations, append to the reporting function.
+ if (!FinalReporting) {
+ FinalReporting = insertFinalReporting();
+
+ Function *InitFn = insertInitFunction(FinalReporting);
+ addToGlobalConstructors(InitFn);
+ }
+
+ AppendScopReporting();
+}
+
+Function *PerfMonitor::insertInitFunction(Function *FinalReporting) {
+ // Insert function definition and BBs.
+ GlobalValue::LinkageTypes Linkage = Function::WeakODRLinkage;
+ FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), {}, false);
+ Function *InitFn = Function::Create(Ty, Linkage, InitFunctionName, M);
+ BasicBlock *Start = BasicBlock::Create(M->getContext(), "start", InitFn);
+ BasicBlock *EarlyReturn =
+ BasicBlock::Create(M->getContext(), "earlyreturn", InitFn);
+ BasicBlock *InitBB = BasicBlock::Create(M->getContext(), "initbb", InitFn);
+
+ Builder.SetInsertPoint(Start);
+
+ // Check if this function was already run. If yes, return.
+ //
+ // In case profiling has been enabled in multiple translation units, the
+ // initializer function will be added to the global constructors list of
+ // each translation unit. When merging translation units, the global
+ // constructor lists are just appended, such that the initializer will appear
+ // multiple times. To avoid initializations being run multiple times (and
+ // especially to avoid that atExitFn is called more than once), we bail
+ // out if the initializer is run more than once.
+ Value *HasRunBefore =
+ Builder.CreateLoad(Builder.getInt1Ty(), AlreadyInitializedPtr);
+ Builder.CreateCondBr(HasRunBefore, EarlyReturn, InitBB);
+ Builder.SetInsertPoint(EarlyReturn);
+ Builder.CreateRetVoid();
+
+ // Keep track that this function has been run once.
+ Builder.SetInsertPoint(InitBB);
+ Value *True = Builder.getInt1(true);
+ Builder.CreateStore(True, AlreadyInitializedPtr);
+
+ // Register the final reporting function with atexit().
+ Value *FinalReportingPtr =
+ Builder.CreatePointerCast(FinalReporting, Builder.getInt8PtrTy());
+ Function *AtExitFn = getAtExit();
+ Builder.CreateCall(AtExitFn, {FinalReportingPtr});
+
+ if (Supported) {
+ // Read the currently cycle counter and store the result for later.
+ Function *RDTSCPFn = getRDTSCP();
+ Value *CurrentCycles =
+ Builder.CreateExtractValue(Builder.CreateCall(RDTSCPFn), {0});
+ Builder.CreateStore(CurrentCycles, CyclesTotalStartPtr, true);
+ }
+ Builder.CreateRetVoid();
+
+ return InitFn;
+}
+
+void PerfMonitor::insertRegionStart(Instruction *InsertBefore) {
+ if (!Supported)
+ return;
+
+ Builder.SetInsertPoint(InsertBefore);
+ Function *RDTSCPFn = getRDTSCP();
+ Value *CurrentCycles =
+ Builder.CreateExtractValue(Builder.CreateCall(RDTSCPFn), {0});
+ Builder.CreateStore(CurrentCycles, CyclesInScopStartPtr, true);
+}
+
+void PerfMonitor::insertRegionEnd(Instruction *InsertBefore) {
+ if (!Supported)
+ return;
+
+ Builder.SetInsertPoint(InsertBefore);
+ Function *RDTSCPFn = getRDTSCP();
+ Type *Int64Ty = Builder.getInt64Ty();
+ LoadInst *CyclesStart =
+ Builder.CreateLoad(Int64Ty, CyclesInScopStartPtr, true);
+ Value *CurrentCycles =
+ Builder.CreateExtractValue(Builder.CreateCall(RDTSCPFn), {0});
+ Value *CyclesInScop = Builder.CreateSub(CurrentCycles, CyclesStart);
+ Value *CyclesInScops = Builder.CreateLoad(Int64Ty, CyclesInScopsPtr, true);
+ CyclesInScops = Builder.CreateAdd(CyclesInScops, CyclesInScop);
+ Builder.CreateStore(CyclesInScops, CyclesInScopsPtr, true);
+
+ Value *CyclesInCurrentScop =
+ Builder.CreateLoad(Int64Ty, CyclesInCurrentScopPtr, true);
+ CyclesInCurrentScop = Builder.CreateAdd(CyclesInCurrentScop, CyclesInScop);
+ Builder.CreateStore(CyclesInCurrentScop, CyclesInCurrentScopPtr, true);
+
+ Value *TripCountForCurrentScop =
+ Builder.CreateLoad(Int64Ty, TripCountForCurrentScopPtr, true);
+ TripCountForCurrentScop =
+ Builder.CreateAdd(TripCountForCurrentScop, Builder.getInt64(1));
+ Builder.CreateStore(TripCountForCurrentScop, TripCountForCurrentScopPtr,
+ true);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/RuntimeDebugBuilder.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/RuntimeDebugBuilder.cpp
new file mode 100644
index 00000000000..05e47e1a485
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/RuntimeDebugBuilder.cpp
@@ -0,0 +1,290 @@
+//===--- RuntimeDebugBuilder.cpp - Helper to insert prints into LLVM-IR ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/RuntimeDebugBuilder.h"
+#include "llvm/IR/IntrinsicsNVPTX.h"
+#include "llvm/IR/Module.h"
+#include <string>
+#include <vector>
+
+using namespace llvm;
+using namespace polly;
+
+Function *RuntimeDebugBuilder::getVPrintF(PollyIRBuilder &Builder) {
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ const char *Name = "vprintf";
+ Function *F = M->getFunction(Name);
+
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ FunctionType *Ty = FunctionType::get(
+ Builder.getInt32Ty(), {Builder.getInt8PtrTy(), Builder.getInt8PtrTy()},
+ false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ return F;
+}
+
+Function *RuntimeDebugBuilder::getAddressSpaceCast(PollyIRBuilder &Builder,
+ unsigned Src, unsigned Dst,
+ unsigned SrcBits,
+ unsigned DstBits) {
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ auto Name = std::string("llvm.nvvm.ptr.constant.to.gen.p") +
+ std::to_string(Dst) + "i" + std::to_string(DstBits) + ".p" +
+ std::to_string(Src) + "i" + std::to_string(SrcBits);
+ Function *F = M->getFunction(Name);
+
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ FunctionType *Ty = FunctionType::get(
+ PointerType::get(Builder.getIntNTy(DstBits), Dst),
+ PointerType::get(Builder.getIntNTy(SrcBits), Src), false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ return F;
+}
+
+std::vector<Value *>
+RuntimeDebugBuilder::getGPUThreadIdentifiers(PollyIRBuilder &Builder) {
+ std::vector<Value *> Identifiers;
+
+ auto M = Builder.GetInsertBlock()->getParent()->getParent();
+
+ std::vector<Function *> BlockIDs = {
+ Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_ctaid_x),
+ Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_ctaid_y),
+ Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_ctaid_z),
+ };
+
+ Identifiers.push_back(Builder.CreateGlobalStringPtr("> block-id: ", "", 4));
+ for (auto GetID : BlockIDs) {
+ Value *Id = Builder.CreateCall(GetID, {});
+ Id = Builder.CreateIntCast(Id, Builder.getInt64Ty(), false);
+ Identifiers.push_back(Id);
+ Identifiers.push_back(Builder.CreateGlobalStringPtr(" ", "", 4));
+ }
+
+ Identifiers.push_back(Builder.CreateGlobalStringPtr("| ", "", 4));
+
+ std::vector<Function *> ThreadIDs = {
+ Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_tid_x),
+ Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_tid_y),
+ Intrinsic::getDeclaration(M, Intrinsic::nvvm_read_ptx_sreg_tid_z),
+ };
+
+ Identifiers.push_back(Builder.CreateGlobalStringPtr("thread-id: ", "", 4));
+ for (auto GetId : ThreadIDs) {
+ Value *Id = Builder.CreateCall(GetId, {});
+ Id = Builder.CreateIntCast(Id, Builder.getInt64Ty(), false);
+ Identifiers.push_back(Id);
+ Identifiers.push_back(Builder.CreateGlobalStringPtr(" ", "", 4));
+ }
+
+ return Identifiers;
+}
+
+void RuntimeDebugBuilder::createPrinter(PollyIRBuilder &Builder, bool IsGPU,
+ ArrayRef<Value *> Values) {
+ if (IsGPU)
+ createGPUPrinterT(Builder, Values);
+ else
+ createCPUPrinterT(Builder, Values);
+}
+
+bool RuntimeDebugBuilder::isPrintable(Type *Ty) {
+ if (Ty->isFloatingPointTy())
+ return true;
+
+ if (Ty->isIntegerTy())
+ return Ty->getIntegerBitWidth() <= 64;
+
+ if (isa<PointerType>(Ty))
+ return true;
+
+ return false;
+}
+
+static std::tuple<std::string, std::vector<Value *>>
+prepareValuesForPrinting(PollyIRBuilder &Builder, ArrayRef<Value *> Values) {
+ std::string FormatString;
+ std::vector<Value *> ValuesToPrint;
+
+ for (auto Val : Values) {
+ Type *Ty = Val->getType();
+
+ if (Ty->isFloatingPointTy()) {
+ if (!Ty->isDoubleTy())
+ Val = Builder.CreateFPExt(Val, Builder.getDoubleTy());
+ } else if (Ty->isIntegerTy()) {
+ if (Ty->getIntegerBitWidth() < 64)
+ Val = Builder.CreateSExt(Val, Builder.getInt64Ty());
+ else
+ assert(Ty->getIntegerBitWidth() &&
+ "Integer types larger 64 bit not supported");
+ } else if (isa<PointerType>(Ty)) {
+ if (Ty->getPointerElementType() == Builder.getInt8Ty() &&
+ Ty->getPointerAddressSpace() == 4) {
+ Val = Builder.CreateGEP(Builder.getInt8Ty(), Val, Builder.getInt64(0));
+ } else {
+ Val = Builder.CreatePtrToInt(Val, Builder.getInt64Ty());
+ }
+ } else {
+ llvm_unreachable("Unknown type");
+ }
+
+ Ty = Val->getType();
+
+ if (Ty->isFloatingPointTy())
+ FormatString += "%f";
+ else if (Ty->isIntegerTy())
+ FormatString += "%ld";
+ else
+ FormatString += "%s";
+
+ ValuesToPrint.push_back(Val);
+ }
+
+ return std::make_tuple(FormatString, ValuesToPrint);
+}
+
+void RuntimeDebugBuilder::createCPUPrinterT(PollyIRBuilder &Builder,
+ ArrayRef<Value *> Values) {
+
+ std::string FormatString;
+ std::vector<Value *> ValuesToPrint;
+
+ std::tie(FormatString, ValuesToPrint) =
+ prepareValuesForPrinting(Builder, Values);
+
+ createPrintF(Builder, FormatString, ValuesToPrint);
+ createFlush(Builder);
+}
+
+void RuntimeDebugBuilder::createGPUPrinterT(PollyIRBuilder &Builder,
+ ArrayRef<Value *> Values) {
+ std::string str;
+
+ auto *Zero = Builder.getInt64(0);
+
+ auto ToPrint = getGPUThreadIdentifiers(Builder);
+
+ ToPrint.push_back(Builder.CreateGlobalStringPtr("\n ", "", 4));
+ ToPrint.insert(ToPrint.end(), Values.begin(), Values.end());
+
+ const DataLayout &DL = Builder.GetInsertBlock()->getModule()->getDataLayout();
+
+ // Allocate print buffer (assuming 2*32 bit per element)
+ auto T = ArrayType::get(Builder.getInt32Ty(), ToPrint.size() * 2);
+ Value *Data = new AllocaInst(
+ T, DL.getAllocaAddrSpace(), "polly.vprint.buffer",
+ &Builder.GetInsertBlock()->getParent()->getEntryBlock().front());
+ auto *DataPtr = Builder.CreateGEP(T, Data, {Zero, Zero});
+
+ int Offset = 0;
+ for (auto Val : ToPrint) {
+ auto Ptr = Builder.CreateGEP(Builder.getInt32Ty(), DataPtr,
+ Builder.getInt64(Offset));
+ Type *Ty = Val->getType();
+
+ if (Ty->isFloatingPointTy()) {
+ if (!Ty->isDoubleTy())
+ Val = Builder.CreateFPExt(Val, Builder.getDoubleTy());
+ } else if (Ty->isIntegerTy()) {
+ if (Ty->getIntegerBitWidth() < 64) {
+ Val = Builder.CreateSExt(Val, Builder.getInt64Ty());
+ } else {
+ assert(Ty->getIntegerBitWidth() == 64 &&
+ "Integer types larger 64 bit not supported");
+ // fallthrough
+ }
+ } else if (auto PtTy = dyn_cast<PointerType>(Ty)) {
+ if (PtTy->getAddressSpace() == 4) {
+ // Pointers in constant address space are printed as strings
+ Val = Builder.CreateGEP(Ty->getPointerElementType(), Val,
+ Builder.getInt64(0));
+ auto F = RuntimeDebugBuilder::getAddressSpaceCast(Builder, 4, 0);
+ Val = Builder.CreateCall(F, Val);
+ } else {
+ Val = Builder.CreatePtrToInt(Val, Builder.getInt64Ty());
+ }
+ } else {
+ llvm_unreachable("Unknown type");
+ }
+
+ Ty = Val->getType();
+ Ptr = Builder.CreatePointerBitCastOrAddrSpaceCast(Ptr, Ty->getPointerTo(5));
+ Builder.CreateAlignedStore(Val, Ptr, Align(4));
+
+ if (Ty->isFloatingPointTy())
+ str += "%f";
+ else if (Ty->isIntegerTy())
+ str += "%ld";
+ else
+ str += "%s";
+
+ Offset += 2;
+ }
+
+ Value *Format = Builder.CreateGlobalStringPtr(str, "polly.vprintf.buffer", 4);
+ Format = Builder.CreateCall(getAddressSpaceCast(Builder, 4, 0), Format);
+
+ Data = Builder.CreateBitCast(Data, Builder.getInt8PtrTy());
+
+ Builder.CreateCall(getVPrintF(Builder), {Format, Data});
+}
+
+Function *RuntimeDebugBuilder::getPrintF(PollyIRBuilder &Builder) {
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ const char *Name = "printf";
+ Function *F = M->getFunction(Name);
+
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ FunctionType *Ty = FunctionType::get(Builder.getInt32Ty(), true);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ return F;
+}
+
+void RuntimeDebugBuilder::createPrintF(PollyIRBuilder &Builder,
+ std::string Format,
+ ArrayRef<Value *> Values) {
+ Value *FormatString = Builder.CreateGlobalStringPtr(Format);
+ std::vector<Value *> Arguments;
+
+ Arguments.push_back(FormatString);
+ Arguments.insert(Arguments.end(), Values.begin(), Values.end());
+ Builder.CreateCall(getPrintF(Builder), Arguments);
+}
+
+void RuntimeDebugBuilder::createFlush(PollyIRBuilder &Builder) {
+ Module *M = Builder.GetInsertBlock()->getParent()->getParent();
+ const char *Name = "fflush";
+ Function *F = M->getFunction(Name);
+
+ if (!F) {
+ GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
+ FunctionType *Ty =
+ FunctionType::get(Builder.getInt32Ty(), Builder.getInt8PtrTy(), false);
+ F = Function::Create(Ty, Linkage, Name, M);
+ }
+
+ // fflush(NULL) flushes _all_ open output streams.
+ //
+ // fflush is declared as 'int fflush(FILE *stream)'. As we only pass on a NULL
+ // pointer, the type we point to does conceptually not matter. However, if
+ // fflush is already declared in this translation unit, we use the very same
+ // type to ensure that LLVM does not complain about mismatching types.
+ Builder.CreateCall(F, Constant::getNullValue(F->arg_begin()->getType()));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/CodeGen/Utils.cpp b/contrib/libs/llvm14/tools/polly/lib/CodeGen/Utils.cpp
new file mode 100644
index 00000000000..3afb2e58088
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/CodeGen/Utils.cpp
@@ -0,0 +1,219 @@
+//===--- Utils.cpp - Utility functions for the code generation --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains utility functions for the code generation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodeGen/Utils.h"
+#include "polly/CodeGen/IRBuilder.h"
+#include "polly/ScopInfo.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+
+using namespace llvm;
+
+// Alternative to llvm::SplitCriticalEdge.
+//
+// Creates a new block which branches to Succ. The edge to split is redirected
+// to the new block.
+//
+// The issue with llvm::SplitCriticalEdge is that it does nothing if the edge is
+// not critical.
+// The issue with llvm::SplitEdge is that it does not always create the middle
+// block, but reuses Prev/Succ if it can. We always want a new middle block.
+static BasicBlock *splitEdge(BasicBlock *Prev, BasicBlock *Succ,
+ const char *Suffix, DominatorTree *DT,
+ LoopInfo *LI, RegionInfo *RI) {
+ assert(Prev && Succ);
+
+ // Before:
+ // \ / / //
+ // Prev / //
+ // | \___/ //
+ // | ___ //
+ // | / \ //
+ // Succ \ //
+ // / \ \ //
+
+ // The algorithm to update DominatorTree and LoopInfo of
+ // llvm::SplitCriticalEdge is more efficient than
+ // llvm::SplitBlockPredecessors, which is more general. In the future we might
+ // either modify llvm::SplitCriticalEdge to allow skipping the critical edge
+ // check; or Copy&Pase it here.
+ BasicBlock *MiddleBlock = SplitBlockPredecessors(
+ Succ, ArrayRef<BasicBlock *>(Prev), Suffix, DT, LI);
+
+ if (RI) {
+ Region *PrevRegion = RI->getRegionFor(Prev);
+ Region *SuccRegion = RI->getRegionFor(Succ);
+ if (PrevRegion->contains(MiddleBlock)) {
+ RI->setRegionFor(MiddleBlock, PrevRegion);
+ } else {
+ RI->setRegionFor(MiddleBlock, SuccRegion);
+ }
+ }
+
+ // After:
+ // \ / / //
+ // Prev / //
+ // | \___/ //
+ // | //
+ // MiddleBlock //
+ // | ___ //
+ // | / \ //
+ // Succ \ //
+ // / \ \ //
+
+ return MiddleBlock;
+}
+
+std::pair<polly::BBPair, BranchInst *>
+polly::executeScopConditionally(Scop &S, Value *RTC, DominatorTree &DT,
+ RegionInfo &RI, LoopInfo &LI) {
+ Region &R = S.getRegion();
+ PollyIRBuilder Builder(S.getEntry());
+
+ // Before:
+ //
+ // \ / //
+ // EnteringBB //
+ // _____|_____ //
+ // / EntryBB \ //
+ // | (region) | //
+ // \_ExitingBB_/ //
+ // | //
+ // ExitBB //
+ // / \ //
+
+ // Create a fork block.
+ BasicBlock *EnteringBB = S.getEnteringBlock();
+ BasicBlock *EntryBB = S.getEntry();
+ assert(EnteringBB && "Must be a simple region");
+ BasicBlock *SplitBlock =
+ splitEdge(EnteringBB, EntryBB, ".split_new_and_old", &DT, &LI, &RI);
+ SplitBlock->setName("polly.split_new_and_old");
+
+ // If EntryBB is the exit block of the region that includes Prev, exclude
+ // SplitBlock from that region by making it itself the exit block. This is
+ // trivially possible because there is just one edge to EnteringBB.
+ // This is necessary because we will add an outgoing edge from SplitBlock,
+ // which would violate the single exit block requirement of PrevRegion.
+ Region *PrevRegion = RI.getRegionFor(EnteringBB);
+ while (PrevRegion->getExit() == EntryBB) {
+ PrevRegion->replaceExit(SplitBlock);
+ PrevRegion = PrevRegion->getParent();
+ }
+ RI.setRegionFor(SplitBlock, PrevRegion);
+
+ // Create a join block
+ BasicBlock *ExitingBB = S.getExitingBlock();
+ BasicBlock *ExitBB = S.getExit();
+ assert(ExitingBB && "Must be a simple region");
+ BasicBlock *MergeBlock =
+ splitEdge(ExitingBB, ExitBB, ".merge_new_and_old", &DT, &LI, &RI);
+ MergeBlock->setName("polly.merge_new_and_old");
+
+ // Exclude the join block from the region.
+ R.replaceExitRecursive(MergeBlock);
+ RI.setRegionFor(MergeBlock, R.getParent());
+
+ // \ / //
+ // EnteringBB //
+ // | //
+ // SplitBlock //
+ // _____|_____ //
+ // / EntryBB \ //
+ // | (region) | //
+ // \_ExitingBB_/ //
+ // | //
+ // MergeBlock //
+ // | //
+ // ExitBB //
+ // / \ //
+
+ // Create the start and exiting block.
+ Function *F = SplitBlock->getParent();
+ BasicBlock *StartBlock =
+ BasicBlock::Create(F->getContext(), "polly.start", F);
+ BasicBlock *ExitingBlock =
+ BasicBlock::Create(F->getContext(), "polly.exiting", F);
+ SplitBlock->getTerminator()->eraseFromParent();
+ Builder.SetInsertPoint(SplitBlock);
+ BranchInst *CondBr = Builder.CreateCondBr(RTC, StartBlock, S.getEntry());
+
+ if (Loop *L = LI.getLoopFor(SplitBlock)) {
+ L->addBasicBlockToLoop(StartBlock, LI);
+ L->addBasicBlockToLoop(ExitingBlock, LI);
+ }
+ DT.addNewBlock(StartBlock, SplitBlock);
+ DT.addNewBlock(ExitingBlock, StartBlock);
+ RI.setRegionFor(StartBlock, RI.getRegionFor(SplitBlock));
+ RI.setRegionFor(ExitingBlock, RI.getRegionFor(SplitBlock));
+
+ // \ / //
+ // EnteringBB //
+ // | //
+ // SplitBlock---------\ //
+ // _____|_____ | //
+ // / EntryBB \ StartBlock //
+ // | (region) | | //
+ // \_ExitingBB_/ ExitingBlock //
+ // | //
+ // MergeBlock //
+ // | //
+ // ExitBB //
+ // / \ //
+
+ // Connect start block to exiting block.
+ Builder.SetInsertPoint(StartBlock);
+ Builder.CreateBr(ExitingBlock);
+ DT.changeImmediateDominator(ExitingBlock, StartBlock);
+
+ // Connect exiting block to join block.
+ Builder.SetInsertPoint(ExitingBlock);
+ Builder.CreateBr(MergeBlock);
+ DT.changeImmediateDominator(MergeBlock, SplitBlock);
+
+ // \ / //
+ // EnteringBB //
+ // | //
+ // SplitBlock---------\ //
+ // _____|_____ | //
+ // / EntryBB \ StartBlock //
+ // | (region) | | //
+ // \_ExitingBB_/ ExitingBlock //
+ // | | //
+ // MergeBlock---------/ //
+ // | //
+ // ExitBB //
+ // / \ //
+ //
+
+ // Split the edge between SplitBlock and EntryBB, to avoid a critical edge.
+ splitEdge(SplitBlock, EntryBB, ".pre_entry_bb", &DT, &LI, &RI);
+
+ // \ / //
+ // EnteringBB //
+ // | //
+ // SplitBlock---------\ //
+ // | | //
+ // PreEntryBB | //
+ // _____|_____ | //
+ // / EntryBB \ StartBlock //
+ // | (region) | | //
+ // \_ExitingBB_/ ExitingBlock //
+ // | | //
+ // MergeBlock---------/ //
+ // | //
+ // ExitBB //
+ // / \ //
+
+ return std::make_pair(std::make_pair(StartBlock, ExitingBlock), CondBr);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Exchange/JSONExporter.cpp b/contrib/libs/llvm14/tools/polly/lib/Exchange/JSONExporter.cpp
new file mode 100644
index 00000000000..507caa1d897
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Exchange/JSONExporter.cpp
@@ -0,0 +1,835 @@
+//===-- JSONExporter.cpp - Export Scops as JSON -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Export the Scops build by ScopInfo pass as a JSON file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/JSONExporter.h"
+#include "polly/DependenceInfo.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "polly/ScopInfo.h"
+#include "polly/ScopPass.h"
+#include "polly/Support/ISLTools.h"
+#include "polly/Support/ScopLocation.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include "isl/map.h"
+#include "isl/set.h"
+#include <memory>
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+using namespace polly;
+
+#define DEBUG_TYPE "polly-import-jscop"
+
+STATISTIC(NewAccessMapFound, "Number of updated access functions");
+
+namespace {
+static cl::opt<std::string>
+ ImportDir("polly-import-jscop-dir",
+ cl::desc("The directory to import the .jscop files from."),
+ cl::Hidden, cl::value_desc("Directory path"), cl::ValueRequired,
+ cl::init("."), cl::cat(PollyCategory));
+
+static cl::opt<std::string>
+ ImportPostfix("polly-import-jscop-postfix",
+ cl::desc("Postfix to append to the import .jsop files."),
+ cl::Hidden, cl::value_desc("File postfix"), cl::ValueRequired,
+ cl::init(""), cl::cat(PollyCategory));
+
+struct JSONExporter : public ScopPass {
+ static char ID;
+ explicit JSONExporter() : ScopPass(ID) {}
+
+ /// Export the SCoP @p S to a JSON file.
+ bool runOnScop(Scop &S) override;
+
+ /// Print the SCoP @p S as it is exported.
+ void printScop(raw_ostream &OS, Scop &S) const override;
+
+ /// Register all analyses and transformation required.
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+};
+
+struct JSONImporter : public ScopPass {
+ static char ID;
+ std::vector<std::string> NewAccessStrings;
+ explicit JSONImporter() : ScopPass(ID) {}
+ /// Import new access functions for SCoP @p S from a JSON file.
+ bool runOnScop(Scop &S) override;
+
+ /// Print the SCoP @p S and the imported access functions.
+ void printScop(raw_ostream &OS, Scop &S) const override;
+
+ /// Register all analyses and transformation required.
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+};
+} // namespace
+
+static std::string getFileName(Scop &S, StringRef Suffix = "") {
+ std::string FunctionName = S.getFunction().getName().str();
+ std::string FileName = FunctionName + "___" + S.getNameStr() + ".jscop";
+
+ if (Suffix != "")
+ FileName += "." + Suffix.str();
+
+ return FileName;
+}
+
+/// Export all arrays from the Scop.
+///
+/// @param S The Scop containing the arrays.
+///
+/// @returns Json::Value containing the arrays.
+static json::Array exportArrays(const Scop &S) {
+ json::Array Arrays;
+ std::string Buffer;
+ llvm::raw_string_ostream RawStringOstream(Buffer);
+
+ for (auto &SAI : S.arrays()) {
+ if (!SAI->isArrayKind())
+ continue;
+
+ json::Object Array;
+ json::Array Sizes;
+ Array["name"] = SAI->getName();
+ unsigned i = 0;
+ if (!SAI->getDimensionSize(i)) {
+ Sizes.push_back("*");
+ i++;
+ }
+ for (; i < SAI->getNumberOfDimensions(); i++) {
+ SAI->getDimensionSize(i)->print(RawStringOstream);
+ Sizes.push_back(RawStringOstream.str());
+ Buffer.clear();
+ }
+ Array["sizes"] = std::move(Sizes);
+ SAI->getElementType()->print(RawStringOstream);
+ Array["type"] = RawStringOstream.str();
+ Buffer.clear();
+ Arrays.push_back(std::move(Array));
+ }
+ return Arrays;
+}
+
+static json::Value getJSON(Scop &S) {
+ json::Object root;
+ unsigned LineBegin, LineEnd;
+ std::string FileName;
+
+ getDebugLocation(&S.getRegion(), LineBegin, LineEnd, FileName);
+ std::string Location;
+ if (LineBegin != (unsigned)-1)
+ Location = FileName + ":" + std::to_string(LineBegin) + "-" +
+ std::to_string(LineEnd);
+
+ root["name"] = S.getNameStr();
+ root["context"] = S.getContextStr();
+ if (LineBegin != (unsigned)-1)
+ root["location"] = Location;
+
+ root["arrays"] = exportArrays(S);
+
+ root["statements"];
+
+ json::Array Statements;
+ for (ScopStmt &Stmt : S) {
+ json::Object statement;
+
+ statement["name"] = Stmt.getBaseName();
+ statement["domain"] = Stmt.getDomainStr();
+ statement["schedule"] = Stmt.getScheduleStr();
+
+ json::Array Accesses;
+ for (MemoryAccess *MA : Stmt) {
+ json::Object access;
+
+ access["kind"] = MA->isRead() ? "read" : "write";
+ access["relation"] = MA->getAccessRelationStr();
+
+ Accesses.push_back(std::move(access));
+ }
+ statement["accesses"] = std::move(Accesses);
+
+ Statements.push_back(std::move(statement));
+ }
+
+ root["statements"] = std::move(Statements);
+ return json::Value(std::move(root));
+}
+
+static void exportScop(Scop &S) {
+ std::string FileName = ImportDir + "/" + getFileName(S);
+
+ json::Value jscop = getJSON(S);
+
+ // Write to file.
+ std::error_code EC;
+ ToolOutputFile F(FileName, EC, llvm::sys::fs::OF_TextWithCRLF);
+
+ std::string FunctionName = S.getFunction().getName().str();
+ errs() << "Writing JScop '" << S.getNameStr() << "' in function '"
+ << FunctionName << "' to '" << FileName << "'.\n";
+
+ if (!EC) {
+ F.os() << formatv("{0:3}", jscop);
+ F.os().close();
+ if (!F.os().has_error()) {
+ errs() << "\n";
+ F.keep();
+ return;
+ }
+ }
+
+ errs() << " error opening file for writing!\n";
+ F.os().clear_error();
+}
+
+typedef Dependences::StatementToIslMapTy StatementToIslMapTy;
+
+/// Import a new context from JScop.
+///
+/// @param S The scop to update.
+/// @param JScop The JScop file describing the new schedule.
+///
+/// @returns True if the import succeeded, otherwise False.
+static bool importContext(Scop &S, const json::Object &JScop) {
+ isl::set OldContext = S.getContext();
+
+ // Check if key 'context' is present.
+ if (!JScop.get("context")) {
+ errs() << "JScop file has no key named 'context'.\n";
+ return false;
+ }
+
+ isl::set NewContext = isl::set{S.getIslCtx().get(),
+ JScop.getString("context").getValue().str()};
+
+ // Check whether the context was parsed successfully.
+ if (NewContext.is_null()) {
+ errs() << "The context was not parsed successfully by ISL.\n";
+ return false;
+ }
+
+ // Check if the isl_set is a parameter set.
+ if (!NewContext.is_params()) {
+ errs() << "The isl_set is not a parameter set.\n";
+ return false;
+ }
+
+ unsigned OldContextDim = unsignedFromIslSize(OldContext.dim(isl::dim::param));
+ unsigned NewContextDim = unsignedFromIslSize(NewContext.dim(isl::dim::param));
+
+ // Check if the imported context has the right number of parameters.
+ if (OldContextDim != NewContextDim) {
+ errs() << "Imported context has the wrong number of parameters : "
+ << "Found " << NewContextDim << " Expected " << OldContextDim
+ << "\n";
+ return false;
+ }
+
+ for (unsigned i = 0; i < OldContextDim; i++) {
+ isl::id Id = OldContext.get_dim_id(isl::dim::param, i);
+ NewContext = NewContext.set_dim_id(isl::dim::param, i, Id);
+ }
+
+ S.setContext(NewContext);
+ return true;
+}
+
+/// Import a new schedule from JScop.
+///
+/// ... and verify that the new schedule does preserve existing data
+/// dependences.
+///
+/// @param S The scop to update.
+/// @param JScop The JScop file describing the new schedule.
+/// @param D The data dependences of the @p S.
+///
+/// @returns True if the import succeeded, otherwise False.
+static bool importSchedule(Scop &S, const json::Object &JScop,
+ const Dependences &D) {
+ StatementToIslMapTy NewSchedule;
+
+ // Check if key 'statements' is present.
+ if (!JScop.get("statements")) {
+ errs() << "JScop file has no key name 'statements'.\n";
+ return false;
+ }
+
+ const json::Array &statements = *JScop.getArray("statements");
+
+ // Check whether the number of indices equals the number of statements
+ if (statements.size() != S.getSize()) {
+ errs() << "The number of indices and the number of statements differ.\n";
+ return false;
+ }
+
+ int Index = 0;
+ for (ScopStmt &Stmt : S) {
+ // Check if key 'schedule' is present.
+ if (!statements[Index].getAsObject()->get("schedule")) {
+ errs() << "Statement " << Index << " has no 'schedule' key.\n";
+ return false;
+ }
+ Optional<StringRef> Schedule =
+ statements[Index].getAsObject()->getString("schedule");
+ assert(Schedule.hasValue() &&
+ "Schedules that contain extension nodes require special handling.");
+ isl_map *Map = isl_map_read_from_str(S.getIslCtx().get(),
+ Schedule.getValue().str().c_str());
+
+ // Check whether the schedule was parsed successfully
+ if (!Map) {
+ errs() << "The schedule was not parsed successfully (index = " << Index
+ << ").\n";
+ return false;
+ }
+
+ isl_space *Space = Stmt.getDomainSpace().release();
+
+ // Copy the old tuple id. This is necessary to retain the user pointer,
+ // that stores the reference to the ScopStmt this schedule belongs to.
+ Map = isl_map_set_tuple_id(Map, isl_dim_in,
+ isl_space_get_tuple_id(Space, isl_dim_set));
+ for (isl_size i = 0; i < isl_space_dim(Space, isl_dim_param); i++) {
+ isl_id *Id = isl_space_get_dim_id(Space, isl_dim_param, i);
+ Map = isl_map_set_dim_id(Map, isl_dim_param, i, Id);
+ }
+ isl_space_free(Space);
+ NewSchedule[&Stmt] = isl::manage(Map);
+ Index++;
+ }
+
+ // Check whether the new schedule is valid or not.
+ if (!D.isValidSchedule(S, NewSchedule)) {
+ errs() << "JScop file contains a schedule that changes the "
+ << "dependences. Use -disable-polly-legality to continue anyways\n";
+ return false;
+ }
+
+ auto ScheduleMap = isl::union_map::empty(S.getIslCtx());
+ for (ScopStmt &Stmt : S) {
+ if (NewSchedule.find(&Stmt) != NewSchedule.end())
+ ScheduleMap = ScheduleMap.unite(NewSchedule[&Stmt]);
+ else
+ ScheduleMap = ScheduleMap.unite(Stmt.getSchedule());
+ }
+
+ S.setSchedule(ScheduleMap);
+
+ return true;
+}
+
+/// Import new memory accesses from JScop.
+///
+/// @param S The scop to update.
+/// @param JScop The JScop file describing the new schedule.
+/// @param DL The data layout to assume.
+/// @param NewAccessStrings optionally record the imported access strings
+///
+/// @returns True if the import succeeded, otherwise False.
+static bool
+importAccesses(Scop &S, const json::Object &JScop, const DataLayout &DL,
+ std::vector<std::string> *NewAccessStrings = nullptr) {
+ int StatementIdx = 0;
+
+ // Check if key 'statements' is present.
+ if (!JScop.get("statements")) {
+ errs() << "JScop file has no key name 'statements'.\n";
+ return false;
+ }
+ const json::Array &statements = *JScop.getArray("statements");
+
+ // Check whether the number of indices equals the number of statements
+ if (statements.size() != S.getSize()) {
+ errs() << "The number of indices and the number of statements differ.\n";
+ return false;
+ }
+
+ for (ScopStmt &Stmt : S) {
+ int MemoryAccessIdx = 0;
+ const json::Object *Statement = statements[StatementIdx].getAsObject();
+ assert(Statement);
+
+ // Check if key 'accesses' is present.
+ if (!Statement->get("accesses")) {
+ errs()
+ << "Statement from JScop file has no key name 'accesses' for index "
+ << StatementIdx << ".\n";
+ return false;
+ }
+ const json::Array &JsonAccesses = *Statement->getArray("accesses");
+
+ // Check whether the number of indices equals the number of memory
+ // accesses
+ if (Stmt.size() != JsonAccesses.size()) {
+ errs() << "The number of memory accesses in the JSop file and the number "
+ "of memory accesses differ for index "
+ << StatementIdx << ".\n";
+ return false;
+ }
+
+ for (MemoryAccess *MA : Stmt) {
+ // Check if key 'relation' is present.
+ const json::Object *JsonMemoryAccess =
+ JsonAccesses[MemoryAccessIdx].getAsObject();
+ assert(JsonMemoryAccess);
+ if (!JsonMemoryAccess->get("relation")) {
+ errs() << "Memory access number " << MemoryAccessIdx
+ << " has no key name 'relation' for statement number "
+ << StatementIdx << ".\n";
+ return false;
+ }
+ StringRef Accesses = JsonMemoryAccess->getString("relation").getValue();
+ isl_map *NewAccessMap =
+ isl_map_read_from_str(S.getIslCtx().get(), Accesses.str().c_str());
+
+ // Check whether the access was parsed successfully
+ if (!NewAccessMap) {
+ errs() << "The access was not parsed successfully by ISL.\n";
+ return false;
+ }
+ isl_map *CurrentAccessMap = MA->getAccessRelation().release();
+
+ // Check if the number of parameter change
+ if (isl_map_dim(NewAccessMap, isl_dim_param) !=
+ isl_map_dim(CurrentAccessMap, isl_dim_param)) {
+ errs() << "JScop file changes the number of parameter dimensions.\n";
+ isl_map_free(CurrentAccessMap);
+ isl_map_free(NewAccessMap);
+ return false;
+ }
+
+ isl_id *NewOutId;
+
+ // If the NewAccessMap has zero dimensions, it is the scalar access; it
+ // must be the same as before.
+ // If it has at least one dimension, it's an array access; search for
+ // its ScopArrayInfo.
+ if (isl_map_dim(NewAccessMap, isl_dim_out) >= 1) {
+ NewOutId = isl_map_get_tuple_id(NewAccessMap, isl_dim_out);
+ auto *SAI = S.getArrayInfoByName(isl_id_get_name(NewOutId));
+ isl_id *OutId = isl_map_get_tuple_id(CurrentAccessMap, isl_dim_out);
+ auto *OutSAI = ScopArrayInfo::getFromId(isl::manage(OutId));
+ if (!SAI || SAI->getElementType() != OutSAI->getElementType()) {
+ errs() << "JScop file contains access function with undeclared "
+ "ScopArrayInfo\n";
+ isl_map_free(CurrentAccessMap);
+ isl_map_free(NewAccessMap);
+ isl_id_free(NewOutId);
+ return false;
+ }
+ isl_id_free(NewOutId);
+ NewOutId = SAI->getBasePtrId().release();
+ } else {
+ NewOutId = isl_map_get_tuple_id(CurrentAccessMap, isl_dim_out);
+ }
+
+ NewAccessMap = isl_map_set_tuple_id(NewAccessMap, isl_dim_out, NewOutId);
+
+ if (MA->isArrayKind()) {
+ // We keep the old alignment, thus we cannot allow accesses to memory
+ // locations that were not accessed before if the alignment of the
+ // access is not the default alignment.
+ bool SpecialAlignment = true;
+ if (LoadInst *LoadI = dyn_cast<LoadInst>(MA->getAccessInstruction())) {
+ SpecialAlignment =
+ LoadI->getAlignment() &&
+ DL.getABITypeAlignment(LoadI->getType()) != LoadI->getAlignment();
+ } else if (StoreInst *StoreI =
+ dyn_cast<StoreInst>(MA->getAccessInstruction())) {
+ SpecialAlignment =
+ StoreI->getAlignment() &&
+ DL.getABITypeAlignment(StoreI->getValueOperand()->getType()) !=
+ StoreI->getAlignment();
+ }
+
+ if (SpecialAlignment) {
+ isl_set *NewAccessSet = isl_map_range(isl_map_copy(NewAccessMap));
+ isl_set *CurrentAccessSet =
+ isl_map_range(isl_map_copy(CurrentAccessMap));
+ bool IsSubset = isl_set_is_subset(NewAccessSet, CurrentAccessSet);
+ isl_set_free(NewAccessSet);
+ isl_set_free(CurrentAccessSet);
+
+ // Check if the JScop file changes the accessed memory.
+ if (!IsSubset) {
+ errs() << "JScop file changes the accessed memory\n";
+ isl_map_free(CurrentAccessMap);
+ isl_map_free(NewAccessMap);
+ return false;
+ }
+ }
+ }
+
+ // We need to copy the isl_ids for the parameter dimensions to the new
+ // map. Without doing this the current map would have different
+ // ids then the new one, even though both are named identically.
+ for (isl_size i = 0; i < isl_map_dim(CurrentAccessMap, isl_dim_param);
+ i++) {
+ isl_id *Id = isl_map_get_dim_id(CurrentAccessMap, isl_dim_param, i);
+ NewAccessMap = isl_map_set_dim_id(NewAccessMap, isl_dim_param, i, Id);
+ }
+
+ // Copy the old tuple id. This is necessary to retain the user pointer,
+ // that stores the reference to the ScopStmt this access belongs to.
+ isl_id *Id = isl_map_get_tuple_id(CurrentAccessMap, isl_dim_in);
+ NewAccessMap = isl_map_set_tuple_id(NewAccessMap, isl_dim_in, Id);
+
+ auto NewAccessDomain = isl_map_domain(isl_map_copy(NewAccessMap));
+ auto CurrentAccessDomain = isl_map_domain(isl_map_copy(CurrentAccessMap));
+
+ if (!isl_set_has_equal_space(NewAccessDomain, CurrentAccessDomain)) {
+ errs() << "JScop file contains access function with incompatible "
+ << "dimensions\n";
+ isl_map_free(CurrentAccessMap);
+ isl_map_free(NewAccessMap);
+ isl_set_free(NewAccessDomain);
+ isl_set_free(CurrentAccessDomain);
+ return false;
+ }
+
+ NewAccessDomain =
+ isl_set_intersect_params(NewAccessDomain, S.getContext().release());
+ CurrentAccessDomain = isl_set_intersect_params(CurrentAccessDomain,
+ S.getContext().release());
+ CurrentAccessDomain =
+ isl_set_intersect(CurrentAccessDomain, Stmt.getDomain().release());
+
+ if (MA->isRead() &&
+ isl_set_is_subset(CurrentAccessDomain, NewAccessDomain) ==
+ isl_bool_false) {
+ errs() << "Mapping not defined for all iteration domain elements\n";
+ isl_set_free(CurrentAccessDomain);
+ isl_set_free(NewAccessDomain);
+ isl_map_free(CurrentAccessMap);
+ isl_map_free(NewAccessMap);
+ return false;
+ }
+
+ isl_set_free(CurrentAccessDomain);
+ isl_set_free(NewAccessDomain);
+
+ if (!isl_map_is_equal(NewAccessMap, CurrentAccessMap)) {
+ // Statistics.
+ ++NewAccessMapFound;
+ if (NewAccessStrings)
+ NewAccessStrings->push_back(Accesses.str());
+ MA->setNewAccessRelation(isl::manage(NewAccessMap));
+ } else {
+ isl_map_free(NewAccessMap);
+ }
+ isl_map_free(CurrentAccessMap);
+ MemoryAccessIdx++;
+ }
+ StatementIdx++;
+ }
+
+ return true;
+}
+
+/// Check whether @p SAI and @p Array represent the same array.
+static bool areArraysEqual(ScopArrayInfo *SAI, const json::Object &Array) {
+ std::string Buffer;
+ llvm::raw_string_ostream RawStringOstream(Buffer);
+
+ // Check if key 'type' is present.
+ if (!Array.get("type")) {
+ errs() << "Array has no key 'type'.\n";
+ return false;
+ }
+
+ // Check if key 'sizes' is present.
+ if (!Array.get("sizes")) {
+ errs() << "Array has no key 'sizes'.\n";
+ return false;
+ }
+
+ // Check if key 'name' is present.
+ if (!Array.get("name")) {
+ errs() << "Array has no key 'name'.\n";
+ return false;
+ }
+
+ if (SAI->getName() != Array.getString("name").getValue())
+ return false;
+
+ if (SAI->getNumberOfDimensions() != Array.getArray("sizes")->size())
+ return false;
+
+ for (unsigned i = 1; i < Array.getArray("sizes")->size(); i++) {
+ SAI->getDimensionSize(i)->print(RawStringOstream);
+ const json::Array &SizesArray = *Array.getArray("sizes");
+ if (RawStringOstream.str() != SizesArray[i].getAsString().getValue())
+ return false;
+ Buffer.clear();
+ }
+
+ // Check if key 'type' differs from the current one or is not valid.
+ SAI->getElementType()->print(RawStringOstream);
+ if (RawStringOstream.str() != Array.getString("type").getValue()) {
+ errs() << "Array has not a valid type.\n";
+ return false;
+ }
+
+ return true;
+}
+
+/// Get the accepted primitive type from its textual representation
+/// @p TypeTextRepresentation.
+///
+/// @param TypeTextRepresentation The textual representation of the type.
+/// @return The pointer to the primitive type, if this type is accepted
+/// or nullptr otherwise.
+static Type *parseTextType(const std::string &TypeTextRepresentation,
+ LLVMContext &LLVMContext) {
+ std::map<std::string, Type *> MapStrToType = {
+ {"void", Type::getVoidTy(LLVMContext)},
+ {"half", Type::getHalfTy(LLVMContext)},
+ {"float", Type::getFloatTy(LLVMContext)},
+ {"double", Type::getDoubleTy(LLVMContext)},
+ {"x86_fp80", Type::getX86_FP80Ty(LLVMContext)},
+ {"fp128", Type::getFP128Ty(LLVMContext)},
+ {"ppc_fp128", Type::getPPC_FP128Ty(LLVMContext)},
+ {"i1", Type::getInt1Ty(LLVMContext)},
+ {"i8", Type::getInt8Ty(LLVMContext)},
+ {"i16", Type::getInt16Ty(LLVMContext)},
+ {"i32", Type::getInt32Ty(LLVMContext)},
+ {"i64", Type::getInt64Ty(LLVMContext)},
+ {"i128", Type::getInt128Ty(LLVMContext)}};
+
+ auto It = MapStrToType.find(TypeTextRepresentation);
+ if (It != MapStrToType.end())
+ return It->second;
+
+ errs() << "Textual representation can not be parsed: "
+ << TypeTextRepresentation << "\n";
+ return nullptr;
+}
+
+/// Import new arrays from JScop.
+///
+/// @param S The scop to update.
+/// @param JScop The JScop file describing new arrays.
+///
+/// @returns True if the import succeeded, otherwise False.
+static bool importArrays(Scop &S, const json::Object &JScop) {
+ if (!JScop.get("arrays"))
+ return true;
+ const json::Array &Arrays = *JScop.getArray("arrays");
+ if (Arrays.size() == 0)
+ return true;
+
+ unsigned ArrayIdx = 0;
+ for (auto &SAI : S.arrays()) {
+ if (!SAI->isArrayKind())
+ continue;
+ if (ArrayIdx + 1 > Arrays.size()) {
+ errs() << "Not enough array entries in JScop file.\n";
+ return false;
+ }
+ if (!areArraysEqual(SAI, *Arrays[ArrayIdx].getAsObject())) {
+ errs() << "No match for array '" << SAI->getName() << "' in JScop.\n";
+ return false;
+ }
+ ArrayIdx++;
+ }
+
+ for (; ArrayIdx < Arrays.size(); ArrayIdx++) {
+ const json::Object &Array = *Arrays[ArrayIdx].getAsObject();
+ auto *ElementType =
+ parseTextType(Array.get("type")->getAsString().getValue().str(),
+ S.getSE()->getContext());
+ if (!ElementType) {
+ errs() << "Error while parsing element type for new array.\n";
+ return false;
+ }
+ const json::Array &SizesArray = *Array.getArray("sizes");
+ std::vector<unsigned> DimSizes;
+ for (unsigned i = 0; i < SizesArray.size(); i++) {
+ auto Size = std::stoi(SizesArray[i].getAsString().getValue().str());
+
+ // Check if the size if positive.
+ if (Size <= 0) {
+ errs() << "The size at index " << i << " is =< 0.\n";
+ return false;
+ }
+
+ DimSizes.push_back(Size);
+ }
+
+ auto NewSAI = S.createScopArrayInfo(
+ ElementType, Array.getString("name").getValue().str(), DimSizes);
+
+ if (Array.get("allocation")) {
+ NewSAI->setIsOnHeap(Array.getString("allocation").getValue() == "heap");
+ }
+ }
+
+ return true;
+}
+
+/// Import a Scop from a JSCOP file
+/// @param S The scop to be modified
+/// @param D Dependence Info
+/// @param DL The DataLayout of the function
+/// @param NewAccessStrings Optionally record the imported access strings
+///
+/// @returns true on success, false otherwise. Beware that if this returns
+/// false, the Scop may still have been modified. In this case the Scop contains
+/// invalid information.
+static bool importScop(Scop &S, const Dependences &D, const DataLayout &DL,
+ std::vector<std::string> *NewAccessStrings = nullptr) {
+ std::string FileName = ImportDir + "/" + getFileName(S, ImportPostfix);
+
+ std::string FunctionName = S.getFunction().getName().str();
+ errs() << "Reading JScop '" << S.getNameStr() << "' in function '"
+ << FunctionName << "' from '" << FileName << "'.\n";
+ ErrorOr<std::unique_ptr<MemoryBuffer>> result =
+ MemoryBuffer::getFile(FileName);
+ std::error_code ec = result.getError();
+
+ if (ec) {
+ errs() << "File could not be read: " << ec.message() << "\n";
+ return false;
+ }
+
+ Expected<json::Value> ParseResult =
+ json::parse(result.get().get()->getBuffer());
+
+ if (Error E = ParseResult.takeError()) {
+ errs() << "JSCoP file could not be parsed\n";
+ errs() << E << "\n";
+ consumeError(std::move(E));
+ return false;
+ }
+ json::Object &jscop = *ParseResult.get().getAsObject();
+
+ bool Success = importContext(S, jscop);
+
+ if (!Success)
+ return false;
+
+ Success = importSchedule(S, jscop, D);
+
+ if (!Success)
+ return false;
+
+ Success = importArrays(S, jscop);
+
+ if (!Success)
+ return false;
+
+ Success = importAccesses(S, jscop, DL, NewAccessStrings);
+
+ if (!Success)
+ return false;
+ return true;
+}
+
+char JSONExporter::ID = 0;
+void JSONExporter::printScop(raw_ostream &OS, Scop &S) const { OS << S; }
+
+bool JSONExporter::runOnScop(Scop &S) {
+ exportScop(S);
+ return false;
+}
+
+void JSONExporter::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.setPreservesAll();
+ AU.addRequired<ScopInfoRegionPass>();
+}
+
+Pass *polly::createJSONExporterPass() { return new JSONExporter(); }
+
+PreservedAnalyses JSONExportPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &) {
+ exportScop(S);
+ return PreservedAnalyses::all();
+}
+
+char JSONImporter::ID = 0;
+
+void JSONImporter::printScop(raw_ostream &OS, Scop &S) const {
+ OS << S;
+ for (std::vector<std::string>::const_iterator I = NewAccessStrings.begin(),
+ E = NewAccessStrings.end();
+ I != E; I++)
+ OS << "New access function '" << *I << "' detected in JSCOP file\n";
+}
+
+bool JSONImporter::runOnScop(Scop &S) {
+ const Dependences &D =
+ getAnalysis<DependenceInfo>().getDependences(Dependences::AL_Statement);
+ const DataLayout &DL = S.getFunction().getParent()->getDataLayout();
+
+ if (!importScop(S, D, DL, &NewAccessStrings))
+ report_fatal_error("Tried to import a malformed jscop file.");
+
+ return false;
+}
+
+void JSONImporter::getAnalysisUsage(AnalysisUsage &AU) const {
+ ScopPass::getAnalysisUsage(AU);
+ AU.addRequired<DependenceInfo>();
+
+ // TODO: JSONImporter should throw away DependenceInfo.
+ AU.addPreserved<DependenceInfo>();
+}
+
+Pass *polly::createJSONImporterPass() { return new JSONImporter(); }
+
+PreservedAnalyses JSONImportPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &) {
+ const Dependences &D =
+ SAM.getResult<DependenceAnalysis>(S, SAR).getDependences(
+ Dependences::AL_Statement);
+ const DataLayout &DL = S.getFunction().getParent()->getDataLayout();
+
+ if (!importScop(S, D, DL))
+ report_fatal_error("Tried to import a malformed jscop file.");
+
+ // This invalidates all analyses on Scop.
+ PreservedAnalyses PA;
+ PA.preserveSet<AllAnalysesOn<Module>>();
+ PA.preserveSet<AllAnalysesOn<Function>>();
+ PA.preserveSet<AllAnalysesOn<Loop>>();
+ return PA;
+}
+
+INITIALIZE_PASS_BEGIN(JSONExporter, "polly-export-jscop",
+ "Polly - Export Scops as JSON"
+ " (Writes a .jscop file for each Scop)",
+ false, false);
+INITIALIZE_PASS_DEPENDENCY(DependenceInfo)
+INITIALIZE_PASS_END(JSONExporter, "polly-export-jscop",
+ "Polly - Export Scops as JSON"
+ " (Writes a .jscop file for each Scop)",
+ false, false)
+
+INITIALIZE_PASS_BEGIN(JSONImporter, "polly-import-jscop",
+ "Polly - Import Scops from JSON"
+ " (Reads a .jscop file for each Scop)",
+ false, false);
+INITIALIZE_PASS_DEPENDENCY(DependenceInfo)
+INITIALIZE_PASS_END(JSONImporter, "polly-import-jscop",
+ "Polly - Import Scops from JSON"
+ " (Reads a .jscop file for each Scop)",
+ false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/README.txt b/contrib/libs/llvm14/tools/polly/lib/External/README.txt
new file mode 100644
index 00000000000..c99eda31842
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/README.txt
@@ -0,0 +1,18 @@
+The libraries in this directory are mirrored from external projects.
+
+Patches to them should first be contributed upstream and then return to Polly
+as normal (re)imports of these updated libraries.
+
+We currently have the following external libraries.
+
+# isl
+License: MIT-STYLE
+Details: isl/LICENSE
+
+# imath
+License: MIT-STYLE
+Details: isl/imath/LICENSE
+
+To update these libraries run 'autoreconf -i && ./configure && make dist' in
+the isl git directory and move the resulting files into lib/External/isl.
+Alternatively, run the update-isl.sh script.
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/AUTHORS b/contrib/libs/llvm14/tools/polly/lib/External/isl/AUTHORS
new file mode 100644
index 00000000000..c6aa5cdad94
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/AUTHORS
@@ -0,0 +1,64 @@
+isl was written by
+
+ Sven Verdoolaege
+2006-2007 Leiden Institute of Advanced Computer Science
+ Universiteit Leiden
+ Niels Bohrweg 1
+ 2333 CA Leiden
+ The Netherlands
+2008-2009 K.U.Leuven
+ Departement Computerwetenschappen
+ Celestijnenlaan 200A
+ B-3001 Leuven
+ Belgium
+2010-2011 INRIA Saclay - Ile-de-France
+ Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod
+ 91893 Orsay
+ France
+2011-2012 consultant for Leiden Institute of Advanced Computer Science
+2012-2014 Ecole Normale Superieure
+ 45 rue d'Ulm, 75230 Paris
+ France
+2014-2015 INRIA Rocquencourt
+ Domaine de Voluceau - Rocquencourt, B.P. 105
+ 78153 Le Chesnay
+ France
+2015-2020 Polly Labs
+2018-2020 Cerebras Systems
+ 175 S San Antonio Rd
+ Los Altos, CA
+ USA
+
+Contributions by
+
+Mythri Alle
+Riyadh Baghdadi
+Serge Belyshev
+Albert Cohen
+Ray Donnelly
+Johannes Doerfert
+Andi Drebes
+Ron Estrin
+Clement Foyer
+Armin Groesslinger
+Tobias Grosser
+Frederik Harwath
+Alexandre Isoard
+Andreas Kloeckner
+Michael Kruse
+Manjunath Kudlur
+Alexander Matz
+Chielo Newctle
+Sebastian Pop
+Louis-Noel Pouchet
+Benoit Pradelle
+Uday Bondhugula
+Andreas Simbuerger
+Tianjiao Sun
+Malhar Thakkar
+Sergei Trofimovich
+Miheer Vaidya
+Sven van Haastregt
+Oleksandr Zinenko
+
+The merge sort implementation was written by Jeffrey Stedfast.
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/ChangeLog b/contrib/libs/llvm14/tools/polly/lib/External/isl/ChangeLog
new file mode 100644
index 00000000000..44acc426307
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/ChangeLog
@@ -0,0 +1,250 @@
+version: 0.24
+date: Sun 25 Apr 2021 03:56:37 PM CEST
+changes:
+ - improved (C++) bindings (inherit methods, renamed exports)
+ - initial templated C++ bindings
+ - detect bounds on constant polynomials as tight
+---
+version: 0.23
+date: Sun 01 Nov 2020 02:41:20 PM CET
+changes:
+ - minor improvements to coalescing
+ - use build compiler to build extract_interface
+ - add some convenience functions
+ - ignore parameters in isl_union_* hash tables
+---
+version: 0.22.1
+date: Sun Jan 12 10:48:18 CET 2020
+changes:
+ - fix error handling
+---
+version: 0.22
+date: Fri Nov 1 18:39:30 CET 2019
+changes:
+ - require C++11 to generate bindings
+ - improved bindings
+ - scheduler fix involving fixed dimensions
+ - accept ranges in tuples during parsing
+ - add some convenience functions
+---
+version: 0.21
+date: Sat Mar 9 15:25:29 CET 2019
+changes:
+ - preliminary C++ bindings
+ - use incremental scheduler by default
+ - introduce isl_size type
+ - rename isl_ast_op_type to isl_ast_expr_op_type
+ - fix coalescing bugs
+ - use isl_bool to return extra boolean argument
+---
+version: 0.20
+date: Sat Jul 21 18:10:08 CEST 2018
+changes:
+ - keep track of domain in 0D isl_multi_pw_aff and isl_multi_union_pw_aff
+ - add isl_aff_eval and isl_pw_aff_eval
+ - add fixed-size rectangular box hull
+---
+version: 0.19
+date: Sat Mar 3 10:44:49 CET 2018
+changes:
+ - minor improvements to coalescing
+ - minor improvement to parametric integer programming
+ - try harder to avoid large coefficients in scheduler
+ - support kill accesses in dependence analysis
+ - drop deprecated isl_int
+ - drop deprecated band forests
+ - drop deprecated functions
+---
+version: 0.18
+date: Sun Dec 18 11:01:58 CET 2016
+changes:
+ - improve elimination of redundant existentially quantified variables
+ - improve coalescing
+ - improve parametric integer programming
+ - preserve isolate option in isl_schedule_node_band_split
+ - print AST nodes in YAML format
+ - minor improvements to Python bindings
+---
+version: 0.17.1
+date: Fri May 6 12:02:48 CEST 2016
+changes:
+ - fix bug in coalescing treatment
+---
+version: 0.17
+date: Tue May 3 14:26:43 CEST 2016
+changes:
+ - optionally combine SCCs incrementally in scheduler
+ - optionally maximize coincidence in scheduler
+ - optionally avoid loop coalescing in scheduler
+ - fix handling of nested integer divisions
+ - optionally detect min/max expressions during AST generation
+ - minor AST generator improvements
+ - simplify stride constraints
+ - improve support for expansions in schedule trees
+---
+version: 0.16.1
+date: Thu Jan 14 18:08:06 CET 2016
+changes:
+ - fix bug in simplification
+---
+version: 0.16
+date: Tue Jan 12 09:56:16 CET 2016
+changes:
+ - add 32 bit integer optimization for IMath
+ - minor AST generator improvements
+ - add isl_union_flow_get_full_{may,must}_dependence
+ - minor improvements to Python bindings
+ - minor improvements to set and map printing
+---
+version: 0.15
+date: Thu Jun 11 12:45:33 CEST 2015
+changes:
+ - improve coalescing
+ - add isl_union_access_info_compute_flow
+ - add mark nodes in AST
+ - add isl_union_pw_aff and isl_multi_union_pw_aff
+ - add schedule trees
+ - deprecate band forests
+ - deprecate separation_class AST generation option
+ - introduce isl_bool and isl_stat types
+---
+version: 0.14.1
+date: Thu Apr 9 12:57:23 CEST 2015
+changes:
+ - fix bug in affine expression normalization
+ - fix handling of conditional validity constraints
+---
+version: 0.14
+date: Sat Oct 25 16:08:47 CEST 2014
+changes:
+ - support IMath as an optional replacement for GMP
+ - minor AST generator improvements
+---
+version: 0.13
+date: Mon Apr 14 11:08:45 CEST 2014
+changes:
+ - deprecate isl_int
+ - improved support for multi piecewise quasi-affine expressions
+ - allow the user to impose a bound on the number of low-level operations
+ - add isl_id_to_ast_expr and isl_id_to_pw_aff
+ - add isl_schedule_constraints
+ - hide internal structure of isl_vec
+ - remove support for piplib
+---
+version: 0.12.2
+date: Sun Jan 12 12:09:46 CET 2014
+changes:
+ - MinGW-w64 build fix
+ - fix simplification bug
+---
+version: 0.12.1
+date: Wed Jul 24 12:54:46 CEST 2013
+changes:
+ - handle malloc returning NULL on zero-size allocation
+ - fix regression in AST generator
+---
+version: 0.12
+date: Sun Jun 23 20:23:05 CEST 2013
+changes:
+ - add isl_val abstraction
+---
+version: 0.11.2
+date: Tue Apr 9 18:45:10 CEST 2013
+changes:
+ - make code generation output the same on Solaris
+ - fix some hard to trigger bugs
+---
+version: 0.11.1
+date: Mon Dec 10 11:55:30 CET 2012
+changes:
+ - add LICENSE file to distribution
+ - make code generation output independent of endianness
+---
+version: 0.11
+date: Mon Dec 3 08:17:18 CET 2012
+changes:
+ - change license from LGPL 2.1 to MIT
+ - add support for multi piecewise quasi-affine expressions
+ - add code generation
+ - various minor bug fixes
+---
+version: 0.10
+date: Sun Jun 3 18:00:16 CEST 2012
+changes:
+ - support for interaction with dependence analysis
+ - add public API for vectors
+ - improved support for (piecewise) multi quasi-affine expressions
+ - various minor bug fixes
+---
+version: 0.09
+date: Sat Dec 17 18:19:26 CET 2011
+changes:
+ - improved argument parsing
+ - hide internal structure of isl_options
+ - improved support for parameter sets
+ - configurable scheduling
+---
+version: 0.08
+date: Fri Oct 21 12:36:20 CEST 2011
+changes:
+ - improved parsing
+ - drop isl_div abstraction
+ - rename isl_dim to isl_space
+ - |-
+ explicitly differentiate between spaces of maps,
+ sets and parameter sets
+ - add support for identifiers
+ - add support for (piecewise) multi quasi-affine expressions
+ - preliminary Python bindings
+---
+version: 0.07
+date: Tue Jul 12 19:34:51 CEST 2011
+changes:
+ - hide internal structures of isl_div and isl_constraint
+ - preliminary scheduling
+ - add support for local spaces and (piecewise) quasi-affine expressions
+---
+version: 0.06
+date: Fri Mar 18 15:59:16 CET 2011
+changes:
+ - improved parsing
+ - consistency changes in API
+ - hide internal structure of isl_ctx
+---
+version: 0.05.1
+date: Wed Jan 5 10:21:42 CET 2011
+changes:
+ - fix simple symmetry detection in parametric integer programming
+---
+version: 0.05
+date: Thu Dec 23 17:03:14 CET 2010
+changes:
+ - rename header files from isl_header.h to isl/header.h
+ - add higher level interface for dependence analysis
+ - improved argument parsing
+ - optionally triangulate domains during Bernstein expansion
+ - support extended PolyLib format
+ - hide internal structure of some data types
+ - improved coalescing
+ - add simple symmetry detection in parametric integer programming
+---
+version: 0.04
+date: Fri Sep 10 12:57:50 CEST 2010
+changes:
+ - rename isl_pw_qpolynomial_fold_add
+ - add isl_map_apply_pw_qpolynomial_fold
+ - support named and nested spaces
+ - support union sets and maps
+ - add public API for matrices
+---
+version: 0.03
+date: Tue Jun 29 13:16:46 CEST 2010
+changes:
+ - new printing functions
+ - support for "may" accesses in dependence analysis
+ - improved coalescing
+ - improved transitive closure
+ - fix several hard to trigger bugs
+ - improved argument parsing
+ - support parametric vertex enumeration for barvinok
+ - optionally use Bernstein expansion to compute bounds
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/LICENSE b/contrib/libs/llvm14/tools/polly/lib/External/isl/LICENSE
new file mode 100644
index 00000000000..e93f5973e00
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/LICENSE
@@ -0,0 +1,19 @@
+MIT License (MIT)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/README b/contrib/libs/llvm14/tools/polly/lib/External/isl/README
new file mode 100644
index 00000000000..e7a5f7d20c7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/README
@@ -0,0 +1,53 @@
+isl is a thread-safe C library for manipulating sets and relations
+of integer points bounded by affine constraints. The descriptions of
+the sets and relations may involve both parameters and existentially
+quantified variables. All computations are performed in exact integer
+arithmetic using GMP.
+
+isl is released under the MIT license, but depends on the LGPL GMP
+library.
+
+Minimal compilation instructions:
+
+ ./configure
+ make
+ make install
+
+If you are taking the source from the git repository, then you first
+need to do
+
+ git clone git://repo.or.cz/isl.git
+ ./autogen.sh
+
+For more information, see doc/user.pod or the generated documentation.
+
+New releases are announced on http://groups.google.com/group/isl-announce
+
+If you use isl, you can let me know by stacking
+https://www.openhub.net/p/isl on Open Hub.
+
+For bug reports, feature requests and questions,
+contact http://groups.google.com/group/isl-development
+
+Whenever you report a bug, please mention the exact version of isl
+that you are using (output of "./isl_cat --version"). If you are unable
+to compile isl, then report the git version (output of "git describe")
+or the version included in the name of the tarball.
+
+If you use isl for your research, you are invited do cite
+the following paper and/or the paper(s) describing the specific
+operations you use.
+
+@incollection{Verdoolaege2010isl,
+ author = {Verdoolaege, Sven},
+ title = {isl: An Integer Set Library for the Polyhedral Model},
+ booktitle = {Mathematical Software - ICMS 2010},
+ series = {Lecture Notes in Computer Science},
+ editor = {Fukuda, Komei and Hoeven, Joris and Joswig, Michael and
+ Takayama, Nobuki},
+ publisher = {Springer},
+ isbn = {978-3-642-15581-9},
+ pages = {299-302},
+ volume = {6327},
+ year = {2010}
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/basis_reduction_tab.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/basis_reduction_tab.c
new file mode 100644
index 00000000000..b42f677312b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/basis_reduction_tab.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <assert.h>
+#include <isl_map_private.h>
+#include <isl_seq.h>
+#include "isl_tab.h"
+#include <isl_int.h>
+#include <isl_config.h>
+
+struct tab_lp {
+ struct isl_ctx *ctx;
+ struct isl_vec *row;
+ struct isl_tab *tab;
+ struct isl_tab_undo **stack;
+ isl_int *obj;
+ isl_int opt;
+ isl_int opt_denom;
+ isl_int tmp;
+ isl_int tmp2;
+ int neq;
+ unsigned dim;
+ /* number of constraints in initial product tableau */
+ int con_offset;
+ /* objective function has fixed or no integer value */
+ int is_fixed;
+};
+
+#ifdef USE_GMP_FOR_MP
+#define GBR_type mpq_t
+#define GBR_init(v) mpq_init(v)
+#define GBR_clear(v) mpq_clear(v)
+#define GBR_set(a,b) mpq_set(a,b)
+#define GBR_set_ui(a,b) mpq_set_ui(a,b,1)
+#define GBR_mul(a,b,c) mpq_mul(a,b,c)
+#define GBR_lt(a,b) (mpq_cmp(a,b) < 0)
+#define GBR_is_zero(a) (mpq_sgn(a) == 0)
+#define GBR_numref(a) mpq_numref(a)
+#define GBR_denref(a) mpq_denref(a)
+#define GBR_floor(a,b) mpz_fdiv_q(a,GBR_numref(b),GBR_denref(b))
+#define GBR_ceil(a,b) mpz_cdiv_q(a,GBR_numref(b),GBR_denref(b))
+#define GBR_set_num_neg(a, b) mpz_neg(GBR_numref(*a), b);
+#define GBR_set_den(a, b) mpz_set(GBR_denref(*a), b);
+#endif /* USE_GMP_FOR_MP */
+
+#ifdef USE_IMATH_FOR_MP
+#include <imrat.h>
+
+#define GBR_type mp_rat
+#define GBR_init(v) v = mp_rat_alloc()
+#define GBR_clear(v) mp_rat_free(v)
+#define GBR_set(a,b) mp_rat_copy(b,a)
+#define GBR_set_ui(a,b) mp_rat_set_uvalue(a,b,1)
+#define GBR_mul(a,b,c) mp_rat_mul(b,c,a)
+#define GBR_lt(a,b) (mp_rat_compare(a,b) < 0)
+#define GBR_is_zero(a) (mp_rat_compare_zero(a) == 0)
+#ifdef USE_SMALL_INT_OPT
+#define GBR_numref(a) isl_sioimath_encode_big(mp_rat_numer_ref(a))
+#define GBR_denref(a) isl_sioimath_encode_big(mp_rat_denom_ref(a))
+#define GBR_floor(a, b) isl_sioimath_fdiv_q((a), GBR_numref(b), GBR_denref(b))
+#define GBR_ceil(a, b) isl_sioimath_cdiv_q((a), GBR_numref(b), GBR_denref(b))
+#define GBR_set_num_neg(a, b) \
+ do { \
+ isl_sioimath_scratchspace_t scratch; \
+ impz_neg(mp_rat_numer_ref(*a), \
+ isl_sioimath_bigarg_src(*b, &scratch));\
+ } while (0)
+#define GBR_set_den(a, b) \
+ do { \
+ isl_sioimath_scratchspace_t scratch; \
+ impz_set(mp_rat_denom_ref(*a), \
+ isl_sioimath_bigarg_src(*b, &scratch));\
+ } while (0)
+#else /* USE_SMALL_INT_OPT */
+#define GBR_numref(a) mp_rat_numer_ref(a)
+#define GBR_denref(a) mp_rat_denom_ref(a)
+#define GBR_floor(a,b) impz_fdiv_q(a,GBR_numref(b),GBR_denref(b))
+#define GBR_ceil(a,b) impz_cdiv_q(a,GBR_numref(b),GBR_denref(b))
+#define GBR_set_num_neg(a, b) impz_neg(GBR_numref(*a), b)
+#define GBR_set_den(a, b) impz_set(GBR_denref(*a), b)
+#endif /* USE_SMALL_INT_OPT */
+#endif /* USE_IMATH_FOR_MP */
+
+static struct tab_lp *init_lp(struct isl_tab *tab);
+static void set_lp_obj(struct tab_lp *lp, isl_int *row, int dim);
+static int solve_lp(struct tab_lp *lp);
+static void get_obj_val(struct tab_lp* lp, GBR_type *F);
+static void delete_lp(struct tab_lp *lp);
+static int add_lp_row(struct tab_lp *lp, isl_int *row, int dim);
+static void get_alpha(struct tab_lp* lp, int row, GBR_type *alpha);
+static int del_lp_row(struct tab_lp *lp) WARN_UNUSED;
+static int cut_lp_to_hyperplane(struct tab_lp *lp, isl_int *row);
+
+#define GBR_LP struct tab_lp
+#define GBR_lp_init(P) init_lp(P)
+#define GBR_lp_set_obj(lp, obj, dim) set_lp_obj(lp, obj, dim)
+#define GBR_lp_solve(lp) solve_lp(lp)
+#define GBR_lp_get_obj_val(lp, F) get_obj_val(lp, F)
+#define GBR_lp_delete(lp) delete_lp(lp)
+#define GBR_lp_next_row(lp) lp->neq
+#define GBR_lp_add_row(lp, row, dim) add_lp_row(lp, row, dim)
+#define GBR_lp_get_alpha(lp, row, alpha) get_alpha(lp, row, alpha)
+#define GBR_lp_del_row(lp) del_lp_row(lp)
+#define GBR_lp_is_fixed(lp) (lp)->is_fixed
+#define GBR_lp_cut(lp, obj) cut_lp_to_hyperplane(lp, obj)
+#include "basis_reduction_templ.c"
+
+/* Set up a tableau for the Cartesian product of bset with itself.
+ * This could be optimized by first setting up a tableau for bset
+ * and then performing the Cartesian product on the tableau.
+ */
+static struct isl_tab *gbr_tab(struct isl_tab *tab, struct isl_vec *row)
+{
+ unsigned dim;
+ struct isl_tab *prod;
+
+ if (!tab || !row)
+ return NULL;
+
+ dim = tab->n_var;
+ prod = isl_tab_product(tab, tab);
+ if (isl_tab_extend_cons(prod, 3 * dim + 1) < 0) {
+ isl_tab_free(prod);
+ return NULL;
+ }
+ return prod;
+}
+
+static struct tab_lp *init_lp(struct isl_tab *tab)
+{
+ struct tab_lp *lp = NULL;
+
+ if (!tab)
+ return NULL;
+
+ lp = isl_calloc_type(tab->mat->ctx, struct tab_lp);
+ if (!lp)
+ return NULL;
+
+ isl_int_init(lp->opt);
+ isl_int_init(lp->opt_denom);
+ isl_int_init(lp->tmp);
+ isl_int_init(lp->tmp2);
+
+ lp->dim = tab->n_var;
+
+ lp->ctx = tab->mat->ctx;
+ isl_ctx_ref(lp->ctx);
+
+ lp->stack = isl_alloc_array(lp->ctx, struct isl_tab_undo *, lp->dim);
+
+ lp->row = isl_vec_alloc(lp->ctx, 1 + 2 * lp->dim);
+ if (!lp->row)
+ goto error;
+ lp->tab = gbr_tab(tab, lp->row);
+ if (!lp->tab)
+ goto error;
+ lp->con_offset = lp->tab->n_con;
+ lp->obj = NULL;
+ lp->neq = 0;
+
+ return lp;
+error:
+ delete_lp(lp);
+ return NULL;
+}
+
+static void set_lp_obj(struct tab_lp *lp, isl_int *row, int dim)
+{
+ lp->obj = row;
+}
+
+static int solve_lp(struct tab_lp *lp)
+{
+ enum isl_lp_result res;
+ unsigned flags = 0;
+
+ lp->is_fixed = 0;
+
+ isl_int_set_si(lp->row->el[0], 0);
+ isl_seq_cpy(lp->row->el + 1, lp->obj, lp->dim);
+ isl_seq_neg(lp->row->el + 1 + lp->dim, lp->obj, lp->dim);
+ if (lp->neq)
+ flags = ISL_TAB_SAVE_DUAL;
+ res = isl_tab_min(lp->tab, lp->row->el, lp->ctx->one,
+ &lp->opt, &lp->opt_denom, flags);
+ isl_int_mul_ui(lp->opt_denom, lp->opt_denom, 2);
+ if (isl_int_abs_lt(lp->opt, lp->opt_denom)) {
+ struct isl_vec *sample = isl_tab_get_sample_value(lp->tab);
+ if (!sample)
+ return -1;
+ isl_seq_inner_product(lp->obj, sample->el + 1, lp->dim, &lp->tmp);
+ isl_seq_inner_product(lp->obj, sample->el + 1 + lp->dim, lp->dim, &lp->tmp2);
+ isl_int_cdiv_q(lp->tmp, lp->tmp, sample->el[0]);
+ isl_int_fdiv_q(lp->tmp2, lp->tmp2, sample->el[0]);
+ if (isl_int_ge(lp->tmp, lp->tmp2))
+ lp->is_fixed = 1;
+ isl_vec_free(sample);
+ }
+ isl_int_divexact_ui(lp->opt_denom, lp->opt_denom, 2);
+ if (res < 0)
+ return -1;
+ if (res != isl_lp_ok)
+ isl_die(lp->ctx, isl_error_internal,
+ "unexpected missing (bounded) solution", return -1);
+ return 0;
+}
+
+/* The current objective function has a fixed (or no) integer value.
+ * Cut the tableau to the hyperplane that fixes this value in
+ * both halves of the tableau.
+ * Return 1 if the resulting tableau is empty.
+ */
+static int cut_lp_to_hyperplane(struct tab_lp *lp, isl_int *row)
+{
+ enum isl_lp_result res;
+
+ isl_int_set_si(lp->row->el[0], 0);
+ isl_seq_cpy(lp->row->el + 1, row, lp->dim);
+ isl_seq_clr(lp->row->el + 1 + lp->dim, lp->dim);
+ res = isl_tab_min(lp->tab, lp->row->el, lp->ctx->one,
+ &lp->tmp, NULL, 0);
+ if (res != isl_lp_ok)
+ return -1;
+
+ isl_int_neg(lp->row->el[0], lp->tmp);
+ if (isl_tab_add_eq(lp->tab, lp->row->el) < 0)
+ return -1;
+
+ isl_seq_cpy(lp->row->el + 1 + lp->dim, row, lp->dim);
+ isl_seq_clr(lp->row->el + 1, lp->dim);
+ if (isl_tab_add_eq(lp->tab, lp->row->el) < 0)
+ return -1;
+
+ lp->con_offset += 2;
+
+ return lp->tab->empty;
+}
+
+static void get_obj_val(struct tab_lp* lp, GBR_type *F)
+{
+ GBR_set_num_neg(F, lp->opt);
+ GBR_set_den(F, lp->opt_denom);
+}
+
+static void delete_lp(struct tab_lp *lp)
+{
+ if (!lp)
+ return;
+
+ isl_int_clear(lp->opt);
+ isl_int_clear(lp->opt_denom);
+ isl_int_clear(lp->tmp);
+ isl_int_clear(lp->tmp2);
+ isl_vec_free(lp->row);
+ free(lp->stack);
+ isl_tab_free(lp->tab);
+ isl_ctx_deref(lp->ctx);
+ free(lp);
+}
+
+static int add_lp_row(struct tab_lp *lp, isl_int *row, int dim)
+{
+ lp->stack[lp->neq] = isl_tab_snap(lp->tab);
+
+ isl_int_set_si(lp->row->el[0], 0);
+ isl_seq_cpy(lp->row->el + 1, row, lp->dim);
+ isl_seq_neg(lp->row->el + 1 + lp->dim, row, lp->dim);
+
+ if (isl_tab_add_valid_eq(lp->tab, lp->row->el) < 0)
+ return -1;
+
+ return lp->neq++;
+}
+
+static void get_alpha(struct tab_lp* lp, int row, GBR_type *alpha)
+{
+ row += lp->con_offset;
+ GBR_set_num_neg(alpha, lp->tab->dual->el[1 + row]);
+ GBR_set_den(alpha, lp->tab->dual->el[0]);
+}
+
+static int del_lp_row(struct tab_lp *lp)
+{
+ lp->neq--;
+ return isl_tab_rollback(lp->tab, lp->stack[lp->neq]);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/basis_reduction_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/basis_reduction_templ.c
new file mode 100644
index 00000000000..5c03eab18f3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/basis_reduction_templ.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2006-2007 Universiteit Leiden
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, Leiden Institute of Advanced Computer Science,
+ * Universiteit Leiden, Niels Bohrweg 1, 2333 CA Leiden, The Netherlands
+ * and K.U.Leuven, Departement Computerwetenschappen, Celestijnenlaan 200A,
+ * B-3001 Leuven, Belgium
+ */
+
+#include <stdlib.h>
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl_vec_private.h>
+#include <isl_options_private.h>
+#include "isl_basis_reduction.h"
+
+static void save_alpha(GBR_LP *lp, int first, int n, GBR_type *alpha)
+{
+ int i;
+
+ for (i = 0; i < n; ++i)
+ GBR_lp_get_alpha(lp, first + i, &alpha[i]);
+}
+
+/* Compute a reduced basis for the set represented by the tableau "tab".
+ * tab->basis, which must be initialized by the calling function to an affine
+ * unimodular basis, is updated to reflect the reduced basis.
+ * The first tab->n_zero rows of the basis (ignoring the constant row)
+ * are assumed to correspond to equalities and are left untouched.
+ * tab->n_zero is updated to reflect any additional equalities that
+ * have been detected in the first rows of the new basis.
+ * The final tab->n_unbounded rows of the basis are assumed to correspond
+ * to unbounded directions and are also left untouched.
+ * In particular this means that the remaining rows are assumed to
+ * correspond to bounded directions.
+ *
+ * This function implements the algorithm described in
+ * "An Implementation of the Generalized Basis Reduction Algorithm
+ * for Integer Programming" of Cook el al. to compute a reduced basis.
+ * We use \epsilon = 1/4.
+ *
+ * If ctx->opt->gbr_only_first is set, the user is only interested
+ * in the first direction. In this case we stop the basis reduction when
+ * the width in the first direction becomes smaller than 2.
+ */
+struct isl_tab *isl_tab_compute_reduced_basis(struct isl_tab *tab)
+{
+ unsigned dim;
+ struct isl_ctx *ctx;
+ struct isl_mat *B;
+ int i;
+ GBR_LP *lp = NULL;
+ GBR_type F_old, alpha, F_new;
+ int row;
+ isl_int tmp;
+ struct isl_vec *b_tmp;
+ GBR_type *F = NULL;
+ GBR_type *alpha_buffer[2] = { NULL, NULL };
+ GBR_type *alpha_saved;
+ GBR_type F_saved;
+ int use_saved = 0;
+ isl_int mu[2];
+ GBR_type mu_F[2];
+ GBR_type two;
+ GBR_type one;
+ int empty = 0;
+ int fixed = 0;
+ int fixed_saved = 0;
+ int mu_fixed[2];
+ int n_bounded;
+ int gbr_only_first;
+
+ if (!tab)
+ return NULL;
+
+ if (tab->empty)
+ return tab;
+
+ ctx = tab->mat->ctx;
+ gbr_only_first = ctx->opt->gbr_only_first;
+ dim = tab->n_var;
+ B = tab->basis;
+ if (!B)
+ return tab;
+
+ n_bounded = dim - tab->n_unbounded;
+ if (n_bounded <= tab->n_zero + 1)
+ return tab;
+
+ isl_int_init(tmp);
+ isl_int_init(mu[0]);
+ isl_int_init(mu[1]);
+
+ GBR_init(alpha);
+ GBR_init(F_old);
+ GBR_init(F_new);
+ GBR_init(F_saved);
+ GBR_init(mu_F[0]);
+ GBR_init(mu_F[1]);
+ GBR_init(two);
+ GBR_init(one);
+
+ b_tmp = isl_vec_alloc(ctx, dim);
+ if (!b_tmp)
+ goto error;
+
+ F = isl_alloc_array(ctx, GBR_type, n_bounded);
+ alpha_buffer[0] = isl_alloc_array(ctx, GBR_type, n_bounded);
+ alpha_buffer[1] = isl_alloc_array(ctx, GBR_type, n_bounded);
+ alpha_saved = alpha_buffer[0];
+
+ if (!F || !alpha_buffer[0] || !alpha_buffer[1])
+ goto error;
+
+ for (i = 0; i < n_bounded; ++i) {
+ GBR_init(F[i]);
+ GBR_init(alpha_buffer[0][i]);
+ GBR_init(alpha_buffer[1][i]);
+ }
+
+ GBR_set_ui(two, 2);
+ GBR_set_ui(one, 1);
+
+ lp = GBR_lp_init(tab);
+ if (!lp)
+ goto error;
+
+ i = tab->n_zero;
+
+ GBR_lp_set_obj(lp, B->row[1+i]+1, dim);
+ ctx->stats->gbr_solved_lps++;
+ if (GBR_lp_solve(lp) < 0)
+ goto error;
+ GBR_lp_get_obj_val(lp, &F[i]);
+
+ if (GBR_lt(F[i], one)) {
+ if (!GBR_is_zero(F[i])) {
+ empty = GBR_lp_cut(lp, B->row[1+i]+1);
+ if (empty)
+ goto done;
+ GBR_set_ui(F[i], 0);
+ }
+ tab->n_zero++;
+ }
+
+ do {
+ if (i+1 == tab->n_zero) {
+ GBR_lp_set_obj(lp, B->row[1+i+1]+1, dim);
+ ctx->stats->gbr_solved_lps++;
+ if (GBR_lp_solve(lp) < 0)
+ goto error;
+ GBR_lp_get_obj_val(lp, &F_new);
+ fixed = GBR_lp_is_fixed(lp);
+ GBR_set_ui(alpha, 0);
+ } else
+ if (use_saved) {
+ row = GBR_lp_next_row(lp);
+ GBR_set(F_new, F_saved);
+ fixed = fixed_saved;
+ GBR_set(alpha, alpha_saved[i]);
+ } else {
+ row = GBR_lp_add_row(lp, B->row[1+i]+1, dim);
+ GBR_lp_set_obj(lp, B->row[1+i+1]+1, dim);
+ ctx->stats->gbr_solved_lps++;
+ if (GBR_lp_solve(lp) < 0)
+ goto error;
+ GBR_lp_get_obj_val(lp, &F_new);
+ fixed = GBR_lp_is_fixed(lp);
+
+ GBR_lp_get_alpha(lp, row, &alpha);
+
+ if (i > 0)
+ save_alpha(lp, row-i, i, alpha_saved);
+
+ if (GBR_lp_del_row(lp) < 0)
+ goto error;
+ }
+ GBR_set(F[i+1], F_new);
+
+ GBR_floor(mu[0], alpha);
+ GBR_ceil(mu[1], alpha);
+
+ if (isl_int_eq(mu[0], mu[1]))
+ isl_int_set(tmp, mu[0]);
+ else {
+ int j;
+
+ for (j = 0; j <= 1; ++j) {
+ isl_int_set(tmp, mu[j]);
+ isl_seq_combine(b_tmp->el,
+ ctx->one, B->row[1+i+1]+1,
+ tmp, B->row[1+i]+1, dim);
+ GBR_lp_set_obj(lp, b_tmp->el, dim);
+ ctx->stats->gbr_solved_lps++;
+ if (GBR_lp_solve(lp) < 0)
+ goto error;
+ GBR_lp_get_obj_val(lp, &mu_F[j]);
+ mu_fixed[j] = GBR_lp_is_fixed(lp);
+ if (i > 0)
+ save_alpha(lp, row-i, i, alpha_buffer[j]);
+ }
+
+ if (GBR_lt(mu_F[0], mu_F[1]))
+ j = 0;
+ else
+ j = 1;
+
+ isl_int_set(tmp, mu[j]);
+ GBR_set(F_new, mu_F[j]);
+ fixed = mu_fixed[j];
+ alpha_saved = alpha_buffer[j];
+ }
+ isl_seq_combine(B->row[1+i+1]+1, ctx->one, B->row[1+i+1]+1,
+ tmp, B->row[1+i]+1, dim);
+
+ if (i+1 == tab->n_zero && fixed) {
+ if (!GBR_is_zero(F[i+1])) {
+ empty = GBR_lp_cut(lp, B->row[1+i+1]+1);
+ if (empty)
+ goto done;
+ GBR_set_ui(F[i+1], 0);
+ }
+ tab->n_zero++;
+ }
+
+ GBR_set(F_old, F[i]);
+
+ use_saved = 0;
+ /* mu_F[0] = 4 * F_new; mu_F[1] = 3 * F_old */
+ GBR_set_ui(mu_F[0], 4);
+ GBR_mul(mu_F[0], mu_F[0], F_new);
+ GBR_set_ui(mu_F[1], 3);
+ GBR_mul(mu_F[1], mu_F[1], F_old);
+ if (GBR_lt(mu_F[0], mu_F[1])) {
+ B = isl_mat_swap_rows(B, 1 + i, 1 + i + 1);
+ if (i > tab->n_zero) {
+ use_saved = 1;
+ GBR_set(F_saved, F_new);
+ fixed_saved = fixed;
+ if (GBR_lp_del_row(lp) < 0)
+ goto error;
+ --i;
+ } else {
+ GBR_set(F[tab->n_zero], F_new);
+ if (gbr_only_first && GBR_lt(F[tab->n_zero], two))
+ break;
+
+ if (fixed) {
+ if (!GBR_is_zero(F[tab->n_zero])) {
+ empty = GBR_lp_cut(lp, B->row[1+tab->n_zero]+1);
+ if (empty)
+ goto done;
+ GBR_set_ui(F[tab->n_zero], 0);
+ }
+ tab->n_zero++;
+ }
+ }
+ } else {
+ GBR_lp_add_row(lp, B->row[1+i]+1, dim);
+ ++i;
+ }
+ } while (i < n_bounded - 1);
+
+ if (0) {
+done:
+ if (empty < 0) {
+error:
+ isl_mat_free(B);
+ B = NULL;
+ }
+ }
+
+ GBR_lp_delete(lp);
+
+ if (alpha_buffer[1])
+ for (i = 0; i < n_bounded; ++i) {
+ GBR_clear(F[i]);
+ GBR_clear(alpha_buffer[0][i]);
+ GBR_clear(alpha_buffer[1][i]);
+ }
+ free(F);
+ free(alpha_buffer[0]);
+ free(alpha_buffer[1]);
+
+ isl_vec_free(b_tmp);
+
+ GBR_clear(alpha);
+ GBR_clear(F_old);
+ GBR_clear(F_new);
+ GBR_clear(F_saved);
+ GBR_clear(mu_F[0]);
+ GBR_clear(mu_F[1]);
+ GBR_clear(two);
+ GBR_clear(one);
+
+ isl_int_clear(tmp);
+ isl_int_clear(mu[0]);
+ isl_int_clear(mu[1]);
+
+ tab->basis = B;
+
+ return tab;
+}
+
+/* Compute an affine form of a reduced basis of the given basic
+ * non-parametric set, which is assumed to be bounded and not
+ * include any integer divisions.
+ * The first column and the first row correspond to the constant term.
+ *
+ * If the input contains any equalities, we first create an initial
+ * basis with the equalities first. Otherwise, we start off with
+ * the identity matrix.
+ */
+__isl_give isl_mat *isl_basic_set_reduced_basis(__isl_keep isl_basic_set *bset)
+{
+ struct isl_mat *basis;
+ struct isl_tab *tab;
+
+ if (isl_basic_set_check_no_locals(bset) < 0 ||
+ isl_basic_set_check_no_params(bset) < 0)
+ return NULL;
+
+ tab = isl_tab_from_basic_set(bset, 0);
+ if (!tab)
+ return NULL;
+
+ if (bset->n_eq == 0)
+ tab->basis = isl_mat_identity(bset->ctx, 1 + tab->n_var);
+ else {
+ isl_mat *eq;
+ isl_size nvar = isl_basic_set_dim(bset, isl_dim_all);
+ if (nvar < 0)
+ goto error;
+ eq = isl_mat_sub_alloc6(bset->ctx, bset->eq, 0, bset->n_eq,
+ 1, nvar);
+ eq = isl_mat_left_hermite(eq, 0, NULL, &tab->basis);
+ tab->basis = isl_mat_lin_to_aff(tab->basis);
+ tab->n_zero = bset->n_eq;
+ isl_mat_free(eq);
+ }
+ tab = isl_tab_compute_reduced_basis(tab);
+ if (!tab)
+ return NULL;
+
+ basis = isl_mat_copy(tab->basis);
+
+ isl_tab_free(tab);
+
+ return basis;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/bset_from_bmap.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/bset_from_bmap.c
new file mode 100644
index 00000000000..958508031d9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/bset_from_bmap.c
@@ -0,0 +1,8 @@
+#include <isl/map_type.h>
+
+/* Return the basic set that was treated as the basic map "bmap".
+ */
+static __isl_give isl_basic_set *bset_from_bmap(__isl_take isl_basic_map *bmap)
+{
+ return (isl_basic_set *) bmap;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/bset_to_bmap.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/bset_to_bmap.c
new file mode 100644
index 00000000000..874fe7107c4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/bset_to_bmap.c
@@ -0,0 +1,10 @@
+#include <isl/map_type.h>
+
+/* Treat "bset" as a basic map.
+ * Internally, isl_basic_set is defined to isl_basic_map, so in practice,
+ * this function performs a redundant cast.
+ */
+static __isl_give isl_basic_map *bset_to_bmap(__isl_take isl_basic_set *bset)
+{
+ return (isl_basic_map *) bset;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/check_single_reference_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/check_single_reference_templ.c
new file mode 100644
index 00000000000..20970b4aa75
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/check_single_reference_templ.c
@@ -0,0 +1,19 @@
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Check that "obj" has a single reference.
+ * That is, check that "obj" can be changed inplace.
+ */
+isl_stat FN(TYPE,check_single_reference)(__isl_keep TYPE *obj)
+{
+ isl_bool single;
+
+ single = FN(TYPE,has_single_reference)(obj);
+ if (single < 0)
+ return isl_stat_error;
+ if (!single)
+ isl_die(FN(TYPE,get_ctx)(obj), isl_error_invalid,
+ "object should have a single reference",
+ return isl_stat_error);
+ return isl_stat_ok;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/check_type_range_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/check_type_range_templ.c
new file mode 100644
index 00000000000..4e4c1e2fc36
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/check_type_range_templ.c
@@ -0,0 +1,20 @@
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Check that there are "n" dimensions of type "type" starting at "first"
+ * in "obj".
+ */
+isl_stat FN(TYPE,check_range)(__isl_keep TYPE *obj,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ isl_size dim;
+
+ dim = FN(TYPE,dim)(obj, type);
+ if (dim < 0)
+ return isl_stat_error;
+ if (first + n > dim || first + n < first)
+ isl_die(FN(TYPE,get_ctx)(obj), isl_error_invalid,
+ "position or range out of bounds",
+ return isl_stat_error);
+ return isl_stat_ok;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/extract_key.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/extract_key.c
new file mode 100644
index 00000000000..070c1f6265f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/extract_key.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <string.h>
+
+/* Extract a mapping key from the token "tok".
+ * Return KEY_ERROR on error, i.e., if "tok" does not
+ * correspond to any known key.
+ */
+static KEY extract_key(__isl_keep isl_stream *s, struct isl_token *tok)
+{
+ int type;
+ char *name;
+ KEY key;
+ isl_ctx *ctx;
+
+ if (!tok)
+ return KEY_ERROR;
+ type = isl_token_get_type(tok);
+ if (type != ISL_TOKEN_IDENT && type != ISL_TOKEN_STRING) {
+ isl_stream_error(s, tok, "expecting key");
+ return KEY_ERROR;
+ }
+
+ ctx = isl_stream_get_ctx(s);
+ name = isl_token_get_str(ctx, tok);
+ if (!name)
+ return KEY_ERROR;
+
+ for (key = 0; key < KEY_END; ++key) {
+ if (!strcmp(name, key_str[key]))
+ break;
+ }
+ free(name);
+
+ if (key >= KEY_END)
+ isl_die(ctx, isl_error_invalid, "unknown key",
+ return KEY_ERROR);
+ return key;
+}
+
+/* Read a key from "s" and return the corresponding enum.
+ * Return KEY_ERROR on error, i.e., if the first token
+ * on the stream does not correspond to any known key.
+ */
+static KEY get_key(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ KEY key;
+
+ tok = isl_stream_next_token(s);
+ key = extract_key(s, tok);
+ isl_token_free(tok);
+
+ return key;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/gitversion.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/gitversion.h
new file mode 100644
index 00000000000..99427f1ff94
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/gitversion.h
@@ -0,0 +1 @@
+#define GIT_HEAD_ID "isl-0.24-69-g54aac5ac" \ No newline at end of file
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/has_single_reference_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/has_single_reference_templ.c
new file mode 100644
index 00000000000..332f6b22e24
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/has_single_reference_templ.c
@@ -0,0 +1,12 @@
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Does "obj" have a single reference?
+ * That is, can "obj" be changed inplace?
+ */
+isl_bool FN(TYPE,has_single_reference)(__isl_keep TYPE *obj)
+{
+ if (!obj)
+ return isl_bool_error;
+ return obj->ref == 1;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/ChangeLog b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/ChangeLog
new file mode 100644
index 00000000000..294bd2a2289
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/ChangeLog
@@ -0,0 +1,563 @@
+1.0.1
+ First released version.
+
+1.0.2
+ Fixed a bug in mp_int_div() which would yield incorrect quotients
+ when the divisor was very close in value to a prefix of the
+ dividend. This is now fixed, and there are regression tests in
+ the tests directory.
+
+ Added recursive multiplication and squaring (Karatsuba-Ofman) for
+ large input values. Integrated these with the existing code for
+ exponentiation, too. See the code for s_kmul() and s_ksqr() in
+ imath.c. Tests added and verified against GNU bc.
+
+ Added documentation on mp_get_multiply_threshold() and the reason
+ why it exists.
+
+1.0.3
+ Fixed a couple of bugs in pi.c that were causing incorrect values
+ to be computed for > 30 digits or so. Added a pi-computation test
+ to the default test suite (make test), checked against a static
+ file computed by bc (set scale=1024, compute 4 * atan(1)). Added
+ command line option to specify output radix for pi.
+
+ Cleaned up a sign-related bug in mp_int_gcd(), which would cause
+ the sign of gcd(0, x) to be incorrect when x < 0. Test cases
+ added for future regression.
+
+ Fixed a bug in s_reduce() which would give incorrect results for
+ powers of 2 in certain circumstances. Added tests to drive this
+ case for future regression.
+
+ Added mp_int_exptmod_evalue() and mp_int_exptmod_bvalue() to make
+ it easier to work with small bases and small exponents.
+
+ Set default recursive multiplication threshold to 50 digits, since
+ this seems to work best for the platforms I've tested so far.
+
+ Added iprime.h and iprime.c to the distribution.
+
+1.0.4
+ Added `findsizes.pl' to the distribution.
+
+ Revised the type declarations in imath.h to use 32/64 bit
+ operations where the "long long" type is supported.
+
+ Fixed a sign-related bug in mp_int_invmod().
+
+ Fixed several small bugs related to shifting which affect the use
+ of 32-bit digits. Many architectures cannot shift by 32 bits at a
+ time (e.g., MIPS), so I split each of these cases into two shifts
+ of half the size, which should scale properly for both the smaller
+ and larger cases.
+
+ Fixed several arithmetic issues with 32-bit digits that arose due
+ to missing type-casts on the right-hand sides of assignments.
+
+ Fixed s_print() and s_print_buf() to handle the sizes of digits
+ transparently.
+
+1.0.5
+ Updated the Makefile to include the _GNU_SOURCE macro. For many
+ GCC systems, this is necessary to get the correct definition of
+ the ULLONG_MAX macro in <limits.h>. Also, you may now build with
+ the make option DEBUG=Y to enable debugging, e.g.:
+
+ make DEBUG=Y imtest
+
+ By default, the Makefile builds with the optimizer enabled.
+
+ Cleaned up the definitions triggered by USE_LONG_LONG in imath.h,
+ and added an #error instruction in case the build is unable to
+ find a definition of ULLONG_MAX or ULONG_LONG_MAX in <limits.h>.
+ Also added the mp_int_to_unsigned(), mp_int_read_unsigned(), and
+ mp_int_unsigned_len() prototypes.
+
+ Fixed a bug in s_qmul() [imath.c:2493] that would grow the value
+ being multiplied even if there was room in the existing digits to
+ hold the result. This was driving an (apparent) bug in the more
+ general mp_int_read_binary() routine. Added the routines
+ mentioned in the previous paragraph, and factored some common
+ code out into a static s_tobin().
+
+ Added reset_registers() to imdrover.{h,c}. Added new test
+ driver functions test_to_uns() and test_read_uns(). Renamed
+ test_read_bin to test_read_binary().
+
+ Silenced a sign-related warning in pi.c (related to printf).
+
+ Added many new test vectors to tests/conv.t, including the
+ original bug proof-of-concept from Tom Wu, and a series of new
+ tests for the unsigned conversion routines.
+
+ Updated `doc.txt' to reflect the changes described above.
+
+1.0.6
+ Updated copyright notices, added LICENSE file explaining the
+ license I am using. This is basically the BSD license, so
+ you should have no trouble incorporating this code into other
+ open source projects.
+
+ No new functionality in this release.
+
+1.0.7
+ The mp_int_invmod(a, m, c) function would compute a negative value
+ for c when given a < 0. I added some code to insure that the value
+ returned is always the least non-negative member of the congruence
+ class, if the inverse exists. A test for this was added to invmod.t.
+
+1.0.8
+ Fixed a small buffer-overrun in s_qmul(). Because it only
+ allocates an extra digit if it absolutely has to, the test for
+ whether it needs to carry a shift out into the "spare" digit had
+ to be written carefully; I missed a subtlety, which is now
+ fixed. Along the way, I fixed a minor performance-related bug in
+ the same routine.
+
+ Added mp_int_error_string(), which converts mp_result values
+ into descriptive strings. These are statically allocated, so
+ you don't have to free them.
+
+ This version also adds an "examples" subdirectory. Currently,
+ there is only one program there, but I will add more examples as
+ time permits me. You have to read the source to understand them
+ anyway, so I won't explain them here.
+
+1.1.0
+ Added imrat.h and imrat.c, containing routines for rational number
+ arithmetic at arbitrary precision. Added support to the test driver,
+ in imath.c and included various tests in the tests/ subdirectory.
+
+ Fixed a sign-of-zero bug in mp_int_mul(). Tests added to mul.t to
+ regress this fix.
+
+1.1.2
+ Fixed a bug with leading zeroes after the decimal point in the
+ mp_rat_read_decimal() function (imrat.c). Along the way, I also
+ found a sign-related bug, in which -0.5 would be treated as if it
+ were positive, because the sign of zero is implicitly positive,
+ and the denominator is treated as unsigned always.
+
+ Thanks to Eric Silva for pointing out the leading zeroes bug.
+ The solution isn't the most efficient possible.
+
+1.1.3
+ Rewrote mp_int_to_decimal() to support new rounding modes. The
+ modes are documented in doc.txt. Some of the code sucked anyway,
+ so I rewrote pretty much the entire function.
+
+ Added new rounding mode constants.
+
+1.1.4
+ Added mixed rational/integer operations:
+ mp_rat_add_int, mp_rat_sub_int, mp_rat_mul_int, mp_rat_div_int
+ Added rational exponentiation (with integer exponents):
+ mp_rat_expt
+
+ Tests for same were added to the tests/ subdirectory.
+
+1.1.5
+ Added mp_rat_read_cdecimal() and mp_rat_read_ustring()
+ Updated the input.c example.
+
+1.1.6
+ Fixed a bug in mp_int_read_cstring() which would read the string
+ "-0" with incorrect sign (MP_NEG instead of MP_ZPOS). This would
+ violate an invariant that zero is always signed with positives.
+
+ Added some tests to tests/neg.t to catch this case.
+
+1.1.7
+ Fixed a bug in s_udiv(), internal to imath.c, which caused
+ division to fail in some corner cases masked by the use of long
+ long as a word type. As a result, s_udiv() has now been wholly
+ rewritten. I also fixed a few lingering buffer-length errors in
+ s_kmul(), and added a "const" qualifier to the input buffers for
+ the mp_int_read_string() and mp_int_read_cstring() functions,
+ and their analogs in imrat.c.
+
+1.1.8
+ Added mp_int_alloc() and mp_int_free().
+
+1.1.9
+ Added mp_rat_alloc() and mp_rat_free(). Fixed a couple of minor
+ bugs in the doc.txt file. Added mp_int_sqrt() to imath.{h,c} and
+ doc.txt.
+
+1.2
+ Dropped bugfix component of revision number. Fixed rsakey.c
+ example program to be complete and work faster.
+
+1.3
+ Replaced findsizes.pl with findsizes.py. Fixed two bugs in the
+ rsakey tool that were leading to incorrect output.
+
+1.4
+ Fixed a bug in mp_int_alloc(), it was not returning NULL when out
+ of memory, but rather failing in assert() instead. Also, updated
+ the documentation to have better language about the return values
+ in various error conditions.
+
+1.5
+ Changed the API for rational rounding. Removed the two functions
+ mp_rat_set_rounding() and mp_rat_get_rounding(), along with the
+ round_output global variable. Redefined the MP_ROUND_* constants
+ as an enumeration type (mp_round_mode). Added a new parameter to
+ the mp_rat_to_decimal() function to accept a rounding mode. Unit
+ tests and doc.txt updated suitably.
+
+ This release also incorporates a small patch submitted by Jonathan
+ Shapiro to support compilation in C++.
+
+1.6
+ Defined default_precision and multiply_threshold to be constant
+ and static. If IMATH_TEST is defined at compile time, these are
+ made global, and can be modified by the caller (the imtimer tool
+ makes use of this ability, for example).
+
+ Implemented a single-digit optimization suggested by J. Shapiro.
+ Documentation updated.
+
+1.7
+ Fixed a subtle casting problem in the use of the ctype macros that
+ would permit negative signed character values to produce illogical
+ behaviour in some configurations (e.g., NetBSD). Removed a dead
+ "return" statement.
+
+ Added the -pedantic switch for gcc, to get more aggressive
+ warnings; to permit the nonstandard "long long" type to still be
+ used, I also added -Wno-long-long when building with long long
+ enabled (the standard configuration).
+
+ Fixed a bug found by the Samba team running Valgrind on the
+ Heimdal project, and reported by Love Hörnquist Âstrand: One of
+ the intermediate values used during modular exponentiation could
+ be overflowed during recursive multiplication. Fixed by taking a
+ more conservative approach to buffer sizing.
+
+ Added a "contrib" subdirectory, whose first entry is a Makefile
+ to build IMath with the MSVC++ "nmake" program, contributed by
+ Matus Horvath.
+
+1.8
+ Fixed a bug in s_udiv() affecting the computation of quotient
+ digits. Thanks to Love Âstrand for isolating this bug. Also in
+ this release, defining USELLONG=Y or USELLONG=N on the command
+ line for make will switch support for the "long long" data type on
+ or off without having to edit the Makefile. The default is still
+ to permit use of "long long", even though the type is not standard
+ ANSI C90.
+
+1.9
+ Increased the number of small primes used for primality testing to
+ 100 from 32. Removed an unwanted #define from imath.c, left over
+ from testing; added "static" to the declaration of the s_embar()
+ internal function since it is not used outside imath.c. Reduced
+ the quantity of feedback generated by rsakey.c during the prime
+ finding stage of key generation.
+
+1.10
+ All primes less than 1000 are now used in iprime.c for preliminary
+ testing of prime candidates. Removed declaration of s_pad() from
+ rsakey.c example. Added imcalc.c example.
+
+ Beginning with this release, defining the DEBUG preprocessor macro
+ when compiling imath.c causes all the normally-static helper
+ functions to be exported. This makes it easier to troubleshoot
+ bugs in the back end functions without manually editing the source
+ till you have found where the bug actually is.
+
+ Fixed a memory leak in the test driver (imtest.c) where the input
+ buffers allocated for test specs were not released before being
+ released. No impact on the core routines, but nevertheless not a
+ good thing.
+
+ Fixed several uninitialized memory reads and one subtle read past
+ the end of a buffer in s_kmul(), found during a run of Purify.
+ Thanks to Love Hörnquist Âstrand for finding this one, and
+ providing a good test case so I could isolate the problem. Also
+ fixed a buglet in s_kmul(), in which free() was being called
+ instead of s_free(), which would break if you provided a custom
+ version of s_alloc() and s_free() for your application.
+
+1.11
+ Those functions which take int parameters to supply one or more of
+ the arithmetic values of the function have been converted to use a
+ typedef "mp_small". This is defined in imath.h, along with some
+ supporting macros.
+
+ Added mp_int_to_uint() and mp_int_lcm() in imath.{h,c}, based on a
+ patch contributed by Hal Finkel. Added LCM tests as as well as
+ some more GCD tests in tests/lcm.t and tests/gcd.t
+
+ Also at Hal Finkel's request, added mp_int_root() to compute the
+ integer nth root, i.e., \lfloor a^{1/b}\rfloor; replaced the old
+ mp_int_sqrt() function with a call to mp_int_root() via a macro.
+ The new implementation is probably slightly less efficient for
+ square roots, but more general. Added tests/root.t and moved the
+ sqrt tests there, also.
+
+1.12
+ Added a new global constant MP_MINERR which is the value of the
+ smallest error code defined by IMath itself. This can be used by
+ clients who wish to define and use additional error codes, so that
+ those codes will not conflict with the existing set.
+
+ Extended the imcalc example to include memory.
+
+ Fixed a bug in mp_int_add() in which -1 + 1 = -0 (the sign of zero
+ was recorded incorrectly). Added tests to the regression suite
+ for this fix.
+
+1.13
+ Cosmetic change -- updated all the files with my new web address.
+
+ Fixed a buglet caught by Love Hörnquist Âstrand using the LLVM
+ static checker tools, in which a mp_int_copy() failure would be
+ silently ignored and cause an extra copy to be generated.
+
+ Fixed a bug in the testing suite while building on MinGW. The pi
+ generation tests compare to static files and these tests fail if
+ CR/LF is output instead of just LF. The test script now strips
+ all CR and LF from the output and compares to files lacking them.
+ Reported by Chris Cole <cjcole@gmail.com>.
+
+1.14
+ Instead of using the preprocessor to delete "static", the static
+ definitions in imath.c now use an explicit STATIC macro, that is
+ made null when DEBUG is defined. This avoids a subtle problem
+ with static variables defined inside functions (although no bugs
+ actually arose from it).
+
+ Fixed a bug in s_udiv() while building on MinGW. When building
+ with short type digits, the routine was incorrectly discarding
+ overflow when computing the next quotient digit.
+ Reported by Paul DeMarco <pdemarco@ppg.com>.
+
+1.15
+ Fixed a bug in the definition of MP_DIGIT_MAX that caused errors
+ when IMath is built under 64-bit Linux. Reported by
+ Klaus Stengel <klaus.stengel@informatik.stud.uni-erlangen.de>.
+
+ Unpacked the macro definitions in imath.c a bit, to make them more
+ readable.
+
+ Added mp_int_expt_full() by request of Andrea Barberio
+ <insomniac@slackware.it>.
+
+1.16
+ Fixed a bug in mp_int_to_uint() which was causing incorrect MP_RANGE
+ errors during small integer conversion.
+ Reported by Andrea Barberio <insomniac@slackware.it>
+
+ Added mp_int_compare_uvalue().
+ Added some new testing hooks in imtest.c, new unit tests.
+
+ Made some code style changes that do not affect functionality.
+
+1.17
+ Fixed a bug in mp_int_swap() where mpz_t structures using their single
+ field as storage would not get swapped correctly.
+ Reported by Andres Navarro <canavarro82@gmail.com>
+
+ Added regression test for this and some hooks for future
+ regressions in the tests/test.sh script.
+
+1.18
+ Made mp_int_rat() use mp_int_init() to initialize numerator and
+ denominator instead of mp_int_init_size().
+ Some minor code cleanup inside the testing code (imdrover.c).
+
+ Fixed an off-by-one bug in s_udiv() which could cause the quotient
+ guessing loop to spin. Reported by Andres Navarro. Added
+ triggering example to div.t as a regression test.
+
+1.19
+ Fix signedness error in compile. Reported by Paweł Sikora.
+
+1.20
+ Fix broken comments, apparently from a previous bad merge.
+ Remove emacs folding-mode comments throughout.
+ Some minor Makefile cleanup to make clang happier.
+
+1.21
+ Fix a syntax error. TODO: Run tests before merging, or better
+ still set up CI someplace.
+ Remove dead division code.
+ Restore a missing comparison.
+ Drop dead flags from the Makefile.
+
+1.22
+ Remove MP_USMALL_MIN, which was unused by anything in imath.
+ Rework doc.txt into Markdown.
+ Restore imath-test.scm and imath.py, dropped during import.
+
+1.23
+ Portability fixes from PostgreSQL (#8), from nmisch.
+
+1.24
+ A cosmetic update, consisting mainly of readability updates,
+ documentation fixes, and warning fixes. There are not intended to
+ be any functional changes in this update, but a fair bit of code
+ and the Makefile have been modified, so I'm adding a tag.
+
+ My intent is to keep the source formatted with clang-format going
+ forward, though I will need to set up some presubmit checks to
+ enforce that. For now it is still a manual step via "make format".
+
+ 7e45d6a Remove a doc reference to MP_USMALL_MIN.
+ 544687d Fix the spelling of mp_error_string in doc.md.
+ 592d4a0 Fix some mis-converted section breaks in doc.md.
+ df9fe8e Format source files with clang-format.
+ fcb4e21 Build with 64-bit words by default.
+ 1579b70 Minor simplifications to the Makefile.
+ 0fbe8e6 Style cleanup: More invasive manual changes.
+ 1d28177 Add -Wextra and -Wno-unused-parameter to default build flags.
+ 15ba02a Fix warnings for signed/unsigned comparisons.
+ 3556984 Style cleanup: Insert missing brackets.
+
+1.25
+ This version fixes several issues found by clang static analysis.
+ It also includes some additional follow-on cleanup tasks from the
+ previous release.
+
+ b5a73c4 Cleanup: Use .tc for test files instead of .t.
+ dc307ae Cleanup: Remove dead author URLs, SVN markers.
+ 389a1be bug: Fix a memory leak in test_meta.
+ 8fb98f7 bug: Fix a use of an uninitalized pointer.
+ fe0757a bug: Fix reads of uninitalized fields in free_test.
+ 08fe765 bug: Fix a reachable null pointer indirection.
+ 7b10453 bug: Remove a redundant initialization.
+ cebce44 bug: Fix various dead assignments.
+ ef36352 Remove the findsizes.py script.
+ eebfb85 Fix some more comparison-sign mismatches.
+ 9abcf66 Cleanup: Update a stale reference to doc.txt in the README.
+ 8ec696f Cleanup: Consolidate the USE_32BIT_WORDS macro sections.
+
+1.26
+ Another round of fixes. Notably the gmp-compat-test again works on
+ macOS, fixing https://github.com/creachadair/imath/issues/26.
+ Also, this release cleans up some more warnings and fixes some
+ missing #include paths.
+
+ 2ea0fff gmp_compat: Fix warnings for mixed-sign comparisons.
+ 2a41bae Fix DLL loading.
+ 56c40f4 Make gmp-compat-test work again on macOS.
+ f163906 Comment out a vacuously true assertion.
+ 667d90e gmp_compat: Ensure a definition of ssize_t is available.
+ 6c6fdd8 Fix a vacuously meaningless comparison.
+ 4dac16f Silence a warning about an uninitalized variable.
+ c6119c4 Include strings.h for strcasecmp.
+
+1.27
+ Another round of cleanup and bug fixes. This release includes a
+ Dockerfile to support basic Linux testing, which I found useful as
+ I do most of my work on macOS.
+
+ This release also addresses most of issue #29 (Switching from C90
+ to C99). Part of that change removes most function-like macros
+ from the API headers, replacing them with static functions. Code
+ that used the macros as lvalues will no longer work, but can and
+ should be easily updated to access the members of mpz_t directly.
+
+ Fixed: #34.
+
+ 899e202 Add a docker config for basic Linux testing.
+ 40e8887 Move imath-test.scm to the tests directory.
+ 6f01c9f Add .dockerignore to the release tarball.
+ 1dab081 Fix the spelling of __abs__ in imath.py.
+ 8f0a00c Enable source formatting for Python.
+ 99e27c8 Format all Python source with yapf.
+ bf289f0 gmp-compat-test: Remove dependency on Python 3.
+ 9269d57 Clean up the Linux test image.
+ 61ca691 Include stdbool.h and use the bool type where appropriate.
+ d4760ee Replace macros with static inline functions.
+ 8241977 linux test: Layer the image for better caching.
+ 46bb578 imath: Replace accessor macros with inline functions.
+ 50c6cc8 imrat: Replace accessor macros with static functions.
+ 0c5cec9 gmp_compat: Fix lvalue uses of MP_USED.
+ 89c72f2 Remove CHECK and NRCHECK macros.
+ dbe9f50 imath: Replace macros with static functions.
+ 0006998 imath: Replace ROUND_PREC macro with a function.
+ b628a0c Move local variable declarations closer to first use.
+ 54d51da Remove obsolete division spec.
+ 796776f iprime: Move and scope variables closer to first use.
+ 8fd5236 iprime: Use a sentinel instead of a length.
+ ce89180 Include getopt.h explicitly where it is required.
+ e6fc170 Make libimath.so build correctly under GCC.
+ b54d8c9 Use Bourne in preference to Bash.
+ 8f88c01 Makefile: Export CC and CFLAGS for submakes.
+ 58f4392 Use the inherited settings in the gmp-compat-tests.
+ 8a181cd Make the Linux docker test run "make check".
+ 28617f2 gmp_compat: Fix overflow in the uint conversion adapter.
+
+1.28
+ Another round of cleanup, and some more invasive API changes.
+ I removed more macros, and added an API surface for setting the
+ default precision and recursive-multiply threshold.
+ The compile-time switchable statics are now strictly static.
+ The handling of temporary values was greatly reworked to make it
+ less brittle.
+
+ ba44b37 Add unit tests for mp_int_is_prime.
+ 6f10877 imath: Remove lvalue uses of access macros, and the macros.
+ f4939db Fix formatting.
+ 85137fa docs: Remove macro implementation comments.
+ 37f046e Rework the handling of temporaries.
+ cc8ac74 imtimer: Fix a lingering lvalue use of MP_USED.
+ 9736a8b imath: Drop switchable statics and stdio dependency.
+ 5445ad8 Add functions to set default precision and multiply threshold.
+ 58f2d6e Use alpine:latest rather than a fixed version.
+
+1.29
+ The Documentation Edition. The main improvement here is that the
+ API documentation is now unified in the header files, and doc.md
+ is now generated from a template that includes the text from the
+ headers. The automation for this is still unsatisfactory, but it
+ is better than it was.
+
+ d239b2e Remove imath.py.
+ e43b0f5 imath: Clean up extraneous whitespace.
+ fbbbbad Remove the mpz struct tag.
+ 718fef2 imath: Add documentation comments to the header.
+ 02600e5 imath: Make radix bound checks into requirements.
+ c21f49d imrat: Add documentation comments to the header.
+ ea5398f Remove the mpq struct tag.
+ c1549c7 Move tools and tests into subdirectories.
+ 7187c49 Remove extraneous whitespace from declarations.
+ afa715c Comment cleanup on Aisle 2.
+ cbf9a03 Add tools/mkdoc.py.
+ 58672fc Remove the "dist" target from Makefile.
+ 894bb90 Move rtest.c into the tests directory.
+ d4cfc69 Add a doc.md.in template file.
+ bd929aa Add a make rule for doc.md from doc.md.in.
+ 6dea44e Update doc.md using the new generator.
+ 56ef9a0 doc: Include mp_int_set_uvalue.
+ 13618b3 doc: Explain the comparator terminology.
+ 9990b2e Make the clean and distclean make-rules equivalent.
+ 13df978 doc: Update the explanation of temp handling macros.
+ b80bd8a doc: Emit one generated comment for the whole file.
+ 3cde6b8 doc: Remove the markdown disclaimer.
+ 045a2a6 doc: Point my address to github instead of e-mail.
+ 08f2efd doc: Add headings for general API functions.
+ 77159d9 mkdoc.py: Link back to source lines.
+ aec8587 doc: Include links back to the source.
+ f8c9f6c imath: Document a constraint on mp_int_redux_const.
+
+1.30
+ Improve test automation; no functional changes to the library.
+
+ fc7846a imtest: Ensure the exit code is non-zero when tests fail.
+ 87edcbe test.sh: Exit non-zero if any unit tests fail.
+ 276d1f9 imtest: Make test output easier to read.
+ c8c90c4 Make the Linux test protocol less brittle.
+ f68ba5b Add a .gitattributes file.
+ 33c2843 Add a docker-test target to the Makefile.
+
+1.31
+ Improvements to build and test automation; add CI configuration.
+
+ d419633 Add a Travis CI configuration for imath.
+ 3305c4a Ensure the Makefile respects a $CC set in the environment.
+ d2da4b6 Update instructions for reporting bugs.
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/LICENSE b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/LICENSE
new file mode 100644
index 00000000000..087fb612583
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/LICENSE
@@ -0,0 +1,20 @@
+IMath is Copyright © 2002-2009 Michael J. Fromberger
+You may use it subject to the following Licensing Terms:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/README.md b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/README.md
new file mode 100644
index 00000000000..a6d4d2b96b4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/README.md
@@ -0,0 +1,107 @@
+IMath
+=====
+
+Arbitrary precision integer and rational arithmetic library.
+
+IMath is an open-source ANSI C arbitrary precision integer and rational
+arithmetic library.
+
+IMath is copyright &copy; 2002-2009 Michael J. Fromberger.
+
+> Permission is hereby granted, free of charge, to any person obtaining a copy
+> of this software and associated documentation files (the "Software"), to deal
+> in the Software without restriction, including without limitation the rights
+> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+> copies of the Software, and to permit persons to whom the Software is
+> furnished to do so, subject to the following conditions:
+>
+> The above copyright notice and this permission notice shall be included in
+> all copies or substantial portions of the Software.
+>
+> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+> SOFTWARE.
+
+
+About IMath
+-----------
+
+IMath is a library written in portable ANSI C that allows you to perform
+arithmetic on integers and rational numbers of arbitrary precision. While many
+programming languages, including Java, Perl, and Python provide arbitrary
+precision numbers as a standard library or language feature, C does not.
+
+IMath was designed to be small, self-contained, easy to understand and use, and
+as portable as possible across various platforms. The API is simple, and the
+code should be comparatively easy to modify or extend. Simplicity and
+portability are useful goals for some applications&#8212;however, IMath does
+not attempt to break performance records. If you need the fastest possible
+implementation, you might consider some other libraries, such as GNU MP (GMP),
+MIRACL, or the bignum library from OpenSSL.
+
+Programming with IMath
+----------------------
+
+Detailed descriptions of the IMath API can be found in [doc.md](doc.md).
+However, the following is a brief synopsis of how to get started with some
+simple tasks.
+
+To do basic integer arithmetic, you must declare variables of type `mpz_t` in
+your program, and call the functions defined in `imath.h` to operate on them.
+Here is a simple example that reads one base-10 integer from the command line,
+multiplies it by another (fixed) value, and prints the result to the standard
+output in base-10 notation:
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include "imath.h"
+
+ int main(int argc, char *argv[])
+ {
+ mpz_t a, b;
+ char *buf;
+ int len;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: testprogram <integer>\n");
+ return 1;
+ }
+
+ /* Initialize a new zero-valued mpz_t structure */
+ mp_int_init(&a);
+
+ /* Initialize a new mpz_t with a small integer value */
+ mp_int_init_value(&b, 25101);
+
+ /* Read a string value in the specified radix */
+ mp_int_read_string(&a, 10, argv[1]);
+
+ /* Multiply the two together... */
+ mp_int_mul(&a, &b, &a);
+
+ /* Print out the result */
+ len = mp_int_string_len(&a, 10);
+ buf = calloc(len, sizeof(*buf));
+ mp_int_to_string(&a, 10, buf, len);
+ printf("result = %s\n", buf);
+ free(buf);
+
+ /* Release memory occupied by mpz_t structures when finished */
+ mp_int_clear(&b);
+ mp_int_clear(&a);
+
+ return 0;
+ }
+
+This simple example program does not do any error checking, but all the IMath
+API functions return an `mp_result` value which can be used to detect various
+problems like range errors, running out of memory, and undefined results.
+
+The IMath API also supports operations on arbitrary precision rational numbers.
+The functions for creating and manipulating rational values (type `mpq_t`) are
+defined in `imrat.h`, so that you need only include them in your project if you
+wish to.
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/gmp_compat.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/gmp_compat.c
new file mode 100644
index 00000000000..620c4ca9a97
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/gmp_compat.c
@@ -0,0 +1,823 @@
+/*
+ Name: gmp_compat.c
+ Purpose: Provide GMP compatiable routines for imath library
+ Author: David Peixotto
+
+ Copyright (c) 2012 Qualcomm Innovation Center, Inc. All rights reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+#include "gmp_compat.h"
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(_MSC_VER)
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+#else
+#include <sys/types.h>
+#endif
+
+#ifdef NDEBUG
+#define CHECK(res) (res)
+#else
+#define CHECK(res) assert(((res) == MP_OK) && "expected MP_OK")
+#endif
+
+/* *(signed char *)&endian_test will thus either be:
+ * 0b00000001 = 1 on big-endian
+ * 0b11111111 = -1 on little-endian */
+static const uint16_t endian_test = 0x1FF;
+#define HOST_ENDIAN (*(signed char *)&endian_test)
+
+/*************************************************************************
+ *
+ * Functions with direct translations
+ *
+ *************************************************************************/
+/* gmp: mpq_clear */
+void GMPQAPI(clear)(mp_rat x) { mp_rat_clear(x); }
+
+/* gmp: mpq_cmp */
+int GMPQAPI(cmp)(mp_rat op1, mp_rat op2) { return mp_rat_compare(op1, op2); }
+
+/* gmp: mpq_init */
+void GMPQAPI(init)(mp_rat x) { CHECK(mp_rat_init(x)); }
+
+/* gmp: mpq_mul */
+void GMPQAPI(mul)(mp_rat product, mp_rat multiplier, mp_rat multiplicand) {
+ CHECK(mp_rat_mul(multiplier, multiplicand, product));
+}
+
+/* gmp: mpq_set */
+void GMPQAPI(set)(mp_rat rop, mp_rat op) { CHECK(mp_rat_copy(op, rop)); }
+
+/* gmp: mpz_abs */
+void GMPZAPI(abs)(mp_int rop, mp_int op) { CHECK(mp_int_abs(op, rop)); }
+
+/* gmp: mpz_add */
+void GMPZAPI(add)(mp_int rop, mp_int op1, mp_int op2) {
+ CHECK(mp_int_add(op1, op2, rop));
+}
+
+/* gmp: mpz_clear */
+void GMPZAPI(clear)(mp_int x) { mp_int_clear(x); }
+
+/* gmp: mpz_cmp_si */
+int GMPZAPI(cmp_si)(mp_int op1, long op2) {
+ return mp_int_compare_value(op1, op2);
+}
+
+/* gmp: mpz_cmpabs */
+int GMPZAPI(cmpabs)(mp_int op1, mp_int op2) {
+ return mp_int_compare_unsigned(op1, op2);
+}
+
+/* gmp: mpz_cmp */
+int GMPZAPI(cmp)(mp_int op1, mp_int op2) { return mp_int_compare(op1, op2); }
+
+/* gmp: mpz_init */
+void GMPZAPI(init)(mp_int x) { CHECK(mp_int_init(x)); }
+
+/* gmp: mpz_mul */
+void GMPZAPI(mul)(mp_int rop, mp_int op1, mp_int op2) {
+ CHECK(mp_int_mul(op1, op2, rop));
+}
+
+/* gmp: mpz_neg */
+void GMPZAPI(neg)(mp_int rop, mp_int op) { CHECK(mp_int_neg(op, rop)); }
+
+/* gmp: mpz_set_si */
+void GMPZAPI(set_si)(mp_int rop, long op) { CHECK(mp_int_set_value(rop, op)); }
+
+/* gmp: mpz_set */
+void GMPZAPI(set)(mp_int rop, mp_int op) { CHECK(mp_int_copy(op, rop)); }
+
+/* gmp: mpz_sub */
+void GMPZAPI(sub)(mp_int rop, mp_int op1, mp_int op2) {
+ CHECK(mp_int_sub(op1, op2, rop));
+}
+
+/* gmp: mpz_swap */
+void GMPZAPI(swap)(mp_int rop1, mp_int rop2) { mp_int_swap(rop1, rop2); }
+
+/* gmp: mpq_sgn */
+int GMPQAPI(sgn)(mp_rat op) { return mp_rat_compare_zero(op); }
+
+/* gmp: mpz_sgn */
+int GMPZAPI(sgn)(mp_int op) { return mp_int_compare_zero(op); }
+
+/* gmp: mpq_set_ui */
+void GMPQAPI(set_ui)(mp_rat rop, unsigned long op1, unsigned long op2) {
+ CHECK(mp_rat_set_uvalue(rop, op1, op2));
+}
+
+/* gmp: mpz_set_ui */
+void GMPZAPI(set_ui)(mp_int rop, unsigned long op) {
+ CHECK(mp_int_set_uvalue(rop, op));
+}
+
+/* gmp: mpq_den_ref */
+mp_int GMPQAPI(denref)(mp_rat op) { return mp_rat_denom_ref(op); }
+
+/* gmp: mpq_num_ref */
+mp_int GMPQAPI(numref)(mp_rat op) { return mp_rat_numer_ref(op); }
+
+/* gmp: mpq_canonicalize */
+void GMPQAPI(canonicalize)(mp_rat op) { CHECK(mp_rat_reduce(op)); }
+
+/*
+ * Functions that can be implemented as a combination of imath functions
+ */
+
+/* gmp: mpz_addmul */
+/* gmp: rop = rop + (op1 * op2) */
+void GMPZAPI(addmul)(mp_int rop, mp_int op1, mp_int op2) {
+ mpz_t tempz;
+ mp_int temp = &tempz;
+ mp_int_init(temp);
+
+ CHECK(mp_int_mul(op1, op2, temp));
+ CHECK(mp_int_add(rop, temp, rop));
+ mp_int_clear(temp);
+}
+
+/* gmp: mpz_divexact */
+/* gmp: only produces correct results when d divides n */
+void GMPZAPI(divexact)(mp_int q, mp_int n, mp_int d) {
+ CHECK(mp_int_div(n, d, q, NULL));
+}
+
+/* gmp: mpz_divisible_p */
+/* gmp: return 1 if d divides n, 0 otherwise */
+/* gmp: 0 is considered to divide only 0 */
+int GMPZAPI(divisible_p)(mp_int n, mp_int d) {
+ /* variables to hold remainder */
+ mpz_t rz;
+ mp_int r = &rz;
+ int r_is_zero;
+
+ /* check for d = 0 */
+ int n_is_zero = mp_int_compare_zero(n) == 0;
+ int d_is_zero = mp_int_compare_zero(d) == 0;
+ if (d_is_zero) return n_is_zero;
+
+ /* return true if remainder is 0 */
+ CHECK(mp_int_init(r));
+ CHECK(mp_int_div(n, d, NULL, r));
+ r_is_zero = mp_int_compare_zero(r) == 0;
+ mp_int_clear(r);
+
+ return r_is_zero;
+}
+
+/* gmp: mpz_submul */
+/* gmp: rop = rop - (op1 * op2) */
+void GMPZAPI(submul)(mp_int rop, mp_int op1, mp_int op2) {
+ mpz_t tempz;
+ mp_int temp = &tempz;
+ mp_int_init(temp);
+
+ CHECK(mp_int_mul(op1, op2, temp));
+ CHECK(mp_int_sub(rop, temp, rop));
+
+ mp_int_clear(temp);
+}
+
+/* gmp: mpz_add_ui */
+void GMPZAPI(add_ui)(mp_int rop, mp_int op1, unsigned long op2) {
+ mpz_t tempz;
+ mp_int temp = &tempz;
+ CHECK(mp_int_init_uvalue(temp, op2));
+
+ CHECK(mp_int_add(op1, temp, rop));
+
+ mp_int_clear(temp);
+}
+
+/* gmp: mpz_divexact_ui */
+/* gmp: only produces correct results when d divides n */
+void GMPZAPI(divexact_ui)(mp_int q, mp_int n, unsigned long d) {
+ mpz_t tempz;
+ mp_int temp = &tempz;
+ CHECK(mp_int_init_uvalue(temp, d));
+
+ CHECK(mp_int_div(n, temp, q, NULL));
+
+ mp_int_clear(temp);
+}
+
+/* gmp: mpz_mul_ui */
+void GMPZAPI(mul_ui)(mp_int rop, mp_int op1, unsigned long op2) {
+ mpz_t tempz;
+ mp_int temp = &tempz;
+ CHECK(mp_int_init_uvalue(temp, op2));
+
+ CHECK(mp_int_mul(op1, temp, rop));
+
+ mp_int_clear(temp);
+}
+
+/* gmp: mpz_pow_ui */
+/* gmp: 0^0 = 1 */
+void GMPZAPI(pow_ui)(mp_int rop, mp_int base, unsigned long exp) {
+ mpz_t tempz;
+ mp_int temp = &tempz;
+
+ /* check for 0^0 */
+ if (exp == 0 && mp_int_compare_zero(base) == 0) {
+ CHECK(mp_int_set_value(rop, 1));
+ return;
+ }
+
+ /* rop = base^exp */
+ CHECK(mp_int_init_uvalue(temp, exp));
+ CHECK(mp_int_expt_full(base, temp, rop));
+ mp_int_clear(temp);
+}
+
+/* gmp: mpz_sub_ui */
+void GMPZAPI(sub_ui)(mp_int rop, mp_int op1, unsigned long op2) {
+ mpz_t tempz;
+ mp_int temp = &tempz;
+ CHECK(mp_int_init_uvalue(temp, op2));
+
+ CHECK(mp_int_sub(op1, temp, rop));
+
+ mp_int_clear(temp);
+}
+
+/*************************************************************************
+ *
+ * Functions with different behavior in corner cases
+ *
+ *************************************************************************/
+
+/* gmp: mpz_gcd */
+void GMPZAPI(gcd)(mp_int rop, mp_int op1, mp_int op2) {
+ int op1_is_zero = mp_int_compare_zero(op1) == 0;
+ int op2_is_zero = mp_int_compare_zero(op2) == 0;
+
+ if (op1_is_zero && op2_is_zero) {
+ mp_int_zero(rop);
+ return;
+ }
+
+ CHECK(mp_int_gcd(op1, op2, rop));
+}
+
+/* gmp: mpz_get_str */
+char *GMPZAPI(get_str)(char *str, int radix, mp_int op) {
+ int i, r, len;
+
+ /* Support negative radix like gmp */
+ r = radix;
+ if (r < 0) r = -r;
+
+ /* Compute the length of the string needed to hold the int */
+ len = mp_int_string_len(op, r);
+ if (str == NULL) {
+ str = malloc(len);
+ }
+
+ /* Convert to string using imath function */
+ CHECK(mp_int_to_string(op, r, str, len));
+
+ /* Change case to match gmp */
+ for (i = 0; i < len - 1; i++) {
+ if (radix < 0) {
+ str[i] = toupper(str[i]);
+ } else {
+ str[i] = tolower(str[i]);
+ }
+ }
+ return str;
+}
+
+/* gmp: mpq_get_str */
+char *GMPQAPI(get_str)(char *str, int radix, mp_rat op) {
+ int i, r, len;
+
+ /* Only print numerator if it is a whole number */
+ if (mp_int_compare_value(mp_rat_denom_ref(op), 1) == 0)
+ return GMPZAPI(get_str)(str, radix, mp_rat_numer_ref(op));
+
+ /* Support negative radix like gmp */
+ r = radix;
+ if (r < 0) r = -r;
+
+ /* Compute the length of the string needed to hold the int */
+ len = mp_rat_string_len(op, r);
+ if (str == NULL) {
+ str = malloc(len);
+ }
+
+ /* Convert to string using imath function */
+ CHECK(mp_rat_to_string(op, r, str, len));
+
+ /* Change case to match gmp */
+ for (i = 0; i < len; i++) {
+ if (radix < 0) {
+ str[i] = toupper(str[i]);
+ } else {
+ str[i] = tolower(str[i]);
+ }
+ }
+
+ return str;
+}
+
+/* gmp: mpz_set_str */
+int GMPZAPI(set_str)(mp_int rop, char *str, int base) {
+ mp_result res = mp_int_read_string(rop, base, str);
+ return ((res == MP_OK) ? 0 : -1);
+}
+
+/* gmp: mpq_set_str */
+int GMPQAPI(set_str)(mp_rat rop, char *s, int base) {
+ char *slash;
+ char *str;
+ mp_result resN;
+ mp_result resD;
+ int res = 0;
+
+ /* Copy string to temporary storage so we can modify it below */
+ str = malloc(strlen(s) + 1);
+ strcpy(str, s);
+
+ /* Properly format the string as an int by terminating at the / */
+ slash = strchr(str, '/');
+ if (slash) *slash = '\0';
+
+ /* Parse numerator */
+ resN = mp_int_read_string(mp_rat_numer_ref(rop), base, str);
+
+ /* Parse denominator if given or set to 1 if not */
+ if (slash) {
+ resD = mp_int_read_string(mp_rat_denom_ref(rop), base, slash + 1);
+ } else {
+ resD = mp_int_set_uvalue(mp_rat_denom_ref(rop), 1);
+ }
+
+ /* Return failure if either parse failed */
+ if (resN != MP_OK || resD != MP_OK) {
+ res = -1;
+ }
+
+ free(str);
+ return res;
+}
+
+static unsigned long get_long_bits(mp_int op) {
+ /* Deal with integer that does not fit into unsigned long. We want to grab
+ * the least significant digits that will fit into the long. Read the digits
+ * into the long starting at the most significant digit that fits into a
+ * long. The long is shifted over by MP_DIGIT_BIT before each digit is added.
+ *
+ * The shift is decomposed into two steps (following the pattern used in the
+ * rest of the imath library) to accommodate architectures that don't deal
+ * well with 32-bit shifts.
+ */
+ mp_size digits_to_copy =
+ (sizeof(unsigned long) + sizeof(mp_digit) - 1) / sizeof(mp_digit);
+ if (digits_to_copy > MP_USED(op)) {
+ digits_to_copy = MP_USED(op);
+ }
+
+ mp_digit *digits = MP_DIGITS(op);
+ unsigned long out = 0;
+
+ for (int i = digits_to_copy - 1; i >= 0; i--) {
+ out <<= (MP_DIGIT_BIT / 2);
+ out <<= (MP_DIGIT_BIT / 2);
+ out |= digits[i];
+ }
+
+ return out;
+}
+
+/* gmp: mpz_get_ui */
+unsigned long GMPZAPI(get_ui)(mp_int op) {
+ unsigned long out;
+
+ /* Try a standard conversion that fits into an unsigned long */
+ mp_result res = mp_int_to_uint(op, &out);
+ if (res == MP_OK) return out;
+
+ /* Abort the try if we don't have a range error in the conversion.
+ * The range error indicates that the value cannot fit into a long. */
+ CHECK(res == MP_RANGE ? MP_OK : MP_RANGE);
+ if (res != MP_RANGE) return 0;
+
+ return get_long_bits(op);
+}
+
+/* gmp: mpz_get_si */
+long GMPZAPI(get_si)(mp_int op) {
+ long out;
+ unsigned long uout;
+ int long_msb;
+
+ /* Try a standard conversion that fits into a long */
+ mp_result res = mp_int_to_int(op, &out);
+ if (res == MP_OK) return out;
+
+ /* Abort the try if we don't have a range error in the conversion.
+ * The range error indicates that the value cannot fit into a long. */
+ CHECK(res == MP_RANGE ? MP_OK : MP_RANGE);
+ if (res != MP_RANGE) return 0;
+
+ /* get least significant bits into an unsigned long */
+ uout = get_long_bits(op);
+
+ /* clear the top bit */
+ long_msb = (sizeof(unsigned long) * 8) - 1;
+ uout &= (~(1UL << long_msb));
+
+ /* convert to negative if needed based on sign of op */
+ if (MP_SIGN(op) == MP_NEG) {
+ uout = 0 - uout;
+ }
+
+ out = (long)uout;
+ return out;
+}
+
+/* gmp: mpz_lcm */
+void GMPZAPI(lcm)(mp_int rop, mp_int op1, mp_int op2) {
+ int op1_is_zero = mp_int_compare_zero(op1) == 0;
+ int op2_is_zero = mp_int_compare_zero(op2) == 0;
+
+ if (op1_is_zero || op2_is_zero) {
+ mp_int_zero(rop);
+ return;
+ }
+
+ CHECK(mp_int_lcm(op1, op2, rop));
+ CHECK(mp_int_abs(rop, rop));
+}
+
+/* gmp: mpz_mul_2exp */
+/* gmp: allow big values for op2 when op1 == 0 */
+void GMPZAPI(mul_2exp)(mp_int rop, mp_int op1, unsigned long op2) {
+ if (mp_int_compare_zero(op1) == 0)
+ mp_int_zero(rop);
+ else
+ CHECK(mp_int_mul_pow2(op1, op2, rop));
+}
+
+/*
+ * Functions needing expanded functionality
+ */
+/* [Note]Overview of division implementation
+
+ All division operations (N / D) compute q and r such that
+
+ N = q * D + r, with 0 <= abs(r) < abs(d)
+
+ The q and r values are not uniquely specified by N and D. To specify which q
+ and r values should be used, GMP implements three different rounding modes
+ for integer division:
+
+ ceiling - round q twords +infinity, r has opposite sign as d
+ floor - round q twords -infinity, r has same sign as d
+ truncate - round q twords zero, r has same sign as n
+
+ The imath library only supports truncate as a rounding mode. We need to
+ implement the other rounding modes in terms of truncating division. We first
+ perform the division in trucate mode and then adjust q accordingly. Once we
+ know q, we can easily compute the correct r according the the formula above
+ by computing:
+
+ r = N - q * D
+
+ The main task is to compute q. We can compute the correct q from a trucated
+ version as follows.
+
+ For ceiling rounding mode, if q is less than 0 then the truncated rounding
+ mode is the same as the ceiling rounding mode. If q is greater than zero
+ then we need to round q up by one because the truncated version was rounded
+ down to zero. If q equals zero then check to see if the result of the
+ divison is positive. A positive result needs to increment q to one.
+
+ For floor rounding mode, if q is greater than 0 then the trucated rounding
+ mode is the same as the floor rounding mode. If q is less than zero then we
+ need to round q down by one because the trucated mode rounded q up by one
+ twords zero. If q is zero then we need to check to see if the result of the
+ division is negative. A negative result needs to decrement q to negative
+ one.
+ */
+
+/* gmp: mpz_cdiv_q */
+void GMPZAPI(cdiv_q)(mp_int q, mp_int n, mp_int d) {
+ mpz_t rz;
+ mp_int r = &rz;
+ int qsign, rsign, nsign, dsign;
+ CHECK(mp_int_init(r));
+
+ /* save signs before division because q can alias with n or d */
+ nsign = mp_int_compare_zero(n);
+ dsign = mp_int_compare_zero(d);
+
+ /* truncating division */
+ CHECK(mp_int_div(n, d, q, r));
+
+ /* see: [Note]Overview of division implementation */
+ qsign = mp_int_compare_zero(q);
+ rsign = mp_int_compare_zero(r);
+ if (qsign > 0) { /* q > 0 */
+ if (rsign != 0) { /* r != 0 */
+ CHECK(mp_int_add_value(q, 1, q));
+ }
+ } else if (qsign == 0) { /* q == 0 */
+ if (rsign != 0) { /* r != 0 */
+ if ((nsign > 0 && dsign > 0) || (nsign < 0 && dsign < 0)) {
+ CHECK(mp_int_set_value(q, 1));
+ }
+ }
+ }
+ mp_int_clear(r);
+}
+
+/* gmp: mpz_fdiv_q */
+void GMPZAPI(fdiv_q)(mp_int q, mp_int n, mp_int d) {
+ mpz_t rz;
+ mp_int r = &rz;
+ int qsign, rsign, nsign, dsign;
+ CHECK(mp_int_init(r));
+
+ /* save signs before division because q can alias with n or d */
+ nsign = mp_int_compare_zero(n);
+ dsign = mp_int_compare_zero(d);
+
+ /* truncating division */
+ CHECK(mp_int_div(n, d, q, r));
+
+ /* see: [Note]Overview of division implementation */
+ qsign = mp_int_compare_zero(q);
+ rsign = mp_int_compare_zero(r);
+ if (qsign < 0) { /* q < 0 */
+ if (rsign != 0) { /* r != 0 */
+ CHECK(mp_int_sub_value(q, 1, q));
+ }
+ } else if (qsign == 0) { /* q == 0 */
+ if (rsign != 0) { /* r != 0 */
+ if ((nsign < 0 && dsign > 0) || (nsign > 0 && dsign < 0)) {
+ CHECK(mp_int_set_value(q, -1));
+ }
+ }
+ }
+ mp_int_clear(r);
+}
+
+/* gmp: mpz_fdiv_r */
+void GMPZAPI(fdiv_r)(mp_int r, mp_int n, mp_int d) {
+ mpz_t qz;
+ mpz_t tempz;
+ mpz_t orig_dz;
+ mpz_t orig_nz;
+ mp_int q = &qz;
+ mp_int temp = &tempz;
+ mp_int orig_d = &orig_dz;
+ mp_int orig_n = &orig_nz;
+ CHECK(mp_int_init(q));
+ CHECK(mp_int_init(temp));
+ /* Make a copy of n in case n and d in case they overlap with q */
+ CHECK(mp_int_init_copy(orig_d, d));
+ CHECK(mp_int_init_copy(orig_n, n));
+
+ /* floor division */
+ GMPZAPI(fdiv_q)(q, n, d);
+
+ /* see: [Note]Overview of division implementation */
+ /* n = q * d + r ==> r = n - q * d */
+ mp_int_mul(q, orig_d, temp);
+ mp_int_sub(orig_n, temp, r);
+
+ mp_int_clear(q);
+ mp_int_clear(temp);
+ mp_int_clear(orig_d);
+ mp_int_clear(orig_n);
+}
+
+/* gmp: mpz_tdiv_q */
+void GMPZAPI(tdiv_q)(mp_int q, mp_int n, mp_int d) {
+ /* truncating division*/
+ CHECK(mp_int_div(n, d, q, NULL));
+}
+
+/* gmp: mpz_fdiv_q_ui */
+unsigned long GMPZAPI(fdiv_q_ui)(mp_int q, mp_int n, unsigned long d) {
+ mpz_t tempz;
+ mp_int temp = &tempz;
+ mpz_t rz;
+ mp_int r = &rz;
+ mpz_t orig_nz;
+ mp_int orig_n = &orig_nz;
+ unsigned long rl;
+ CHECK(mp_int_init_uvalue(temp, d));
+ CHECK(mp_int_init(r));
+ /* Make a copy of n in case n and q overlap */
+ CHECK(mp_int_init_copy(orig_n, n));
+
+ /* use floor division mode to compute q and r */
+ GMPZAPI(fdiv_q)(q, n, temp);
+ GMPZAPI(fdiv_r)(r, orig_n, temp);
+ CHECK(mp_int_to_uint(r, &rl));
+
+ mp_int_clear(temp);
+ mp_int_clear(r);
+ mp_int_clear(orig_n);
+
+ return rl;
+}
+
+/* gmp: mpz_export */
+void *GMPZAPI(export)(void *rop, size_t *countp, int order, size_t size,
+ int endian, size_t nails, mp_int op) {
+ size_t i, j;
+ size_t num_used_bytes;
+ size_t num_words, num_missing_bytes;
+ ssize_t word_offset;
+ unsigned char *dst;
+ mp_digit *src;
+ int src_bits;
+
+ /* We do not have a complete implementation. Assert to ensure our
+ * restrictions are in place.
+ */
+ assert(nails == 0 && "Do not support non-full words");
+ assert(endian == 1 || endian == 0 || endian == -1);
+ assert(order == 1 || order == -1);
+
+ /* Test for zero */
+ if (mp_int_compare_zero(op) == 0) {
+ if (countp) *countp = 0;
+ return rop;
+ }
+
+ /* Calculate how many words we need */
+ num_used_bytes = mp_int_unsigned_len(op);
+ num_words = (num_used_bytes + (size - 1)) / size; /* ceil division */
+ assert(num_used_bytes > 0);
+
+ /* Check to see if we will have missing bytes in the last word.
+
+ Missing bytes can only occur when the size of words we output is
+ greater than the size of words used internally by imath. The number of
+ missing bytes is the number of bytes needed to fill out the last word. If
+ this number is greater than the size of a single mp_digit, then we need to
+ pad the word with extra zeros. Otherwise, the missing bytes can be filled
+ directly from the zeros in the last digit in the number.
+ */
+ num_missing_bytes = (size * num_words) - num_used_bytes;
+ assert(num_missing_bytes < size);
+
+ /* Allocate space for the result if needed */
+ if (rop == NULL) {
+ rop = malloc(num_words * size);
+ }
+
+ if (endian == 0) {
+ endian = HOST_ENDIAN;
+ }
+
+ /* Initialize dst and src pointers */
+ dst = (unsigned char *)rop + (order >= 0 ? (num_words - 1) * size : 0) +
+ (endian >= 0 ? size - 1 : 0);
+ src = MP_DIGITS(op);
+ src_bits = MP_DIGIT_BIT;
+
+ word_offset = (endian >= 0 ? size : -size) + (order < 0 ? size : -size);
+
+ for (i = 0; i < num_words; i++) {
+ for (j = 0; j < size && i * size + j < num_used_bytes; j++) {
+ if (src_bits == 0) {
+ ++src;
+ src_bits = MP_DIGIT_BIT;
+ }
+ *dst = (*src >> (MP_DIGIT_BIT - src_bits)) & 0xFF;
+ src_bits -= 8;
+ dst -= endian;
+ }
+ for (; j < size; j++) {
+ *dst = 0;
+ dst -= endian;
+ }
+ dst += word_offset;
+ }
+
+ if (countp) *countp = num_words;
+ return rop;
+}
+
+/* gmp: mpz_import */
+void GMPZAPI(import)(mp_int rop, size_t count, int order, size_t size,
+ int endian, size_t nails, const void *op) {
+ mpz_t tmpz;
+ mp_int tmp = &tmpz;
+ size_t total_size;
+ size_t num_digits;
+ ssize_t word_offset;
+ const unsigned char *src;
+ mp_digit *dst;
+ int dst_bits;
+ size_t i, j;
+ if (count == 0 || op == NULL) return;
+
+ /* We do not have a complete implementation. Assert to ensure our
+ * restrictions are in place. */
+ assert(nails == 0 && "Do not support non-full words");
+ assert(endian == 1 || endian == 0 || endian == -1);
+ assert(order == 1 || order == -1);
+
+ if (endian == 0) {
+ endian = HOST_ENDIAN;
+ }
+
+ /* Compute number of needed digits by ceil division */
+ total_size = count * size;
+ num_digits = (total_size + sizeof(mp_digit) - 1) / sizeof(mp_digit);
+
+ /* Init temporary */
+ mp_int_init_size(tmp, num_digits);
+ for (i = 0; i < num_digits; i++) tmp->digits[i] = 0;
+
+ /* Copy bytes */
+ src = (const unsigned char *)op + (order >= 0 ? (count - 1) * size : 0) +
+ (endian >= 0 ? size - 1 : 0);
+ dst = MP_DIGITS(tmp);
+ dst_bits = 0;
+
+ word_offset = (endian >= 0 ? size : -size) + (order < 0 ? size : -size);
+
+ for (i = 0; i < count; i++) {
+ for (j = 0; j < size; j++) {
+ if (dst_bits == MP_DIGIT_BIT) {
+ ++dst;
+ dst_bits = 0;
+ }
+ *dst |= ((mp_digit)*src) << dst_bits;
+ dst_bits += 8;
+ src -= endian;
+ }
+ src += word_offset;
+ }
+
+ tmp->used = num_digits;
+
+ /* Remove leading zeros from number */
+ {
+ mp_size uz_ = tmp->used;
+ mp_digit *dz_ = MP_DIGITS(tmp) + uz_ - 1;
+ while (uz_ > 1 && (*dz_-- == 0)) --uz_;
+ tmp->used = uz_;
+ }
+
+ /* Copy to destination */
+ mp_int_copy(tmp, rop);
+ mp_int_clear(tmp);
+}
+
+/* gmp: mpz_sizeinbase */
+size_t GMPZAPI(sizeinbase)(mp_int op, int base) {
+ mp_result res;
+ size_t size;
+
+ /* If op == 0, return 1 */
+ if (mp_int_compare_zero(op) == 0) return 1;
+
+ /* Compute string length in base */
+ res = mp_int_string_len(op, base);
+ CHECK((res > 0) == MP_OK);
+
+ /* Now adjust the final size by getting rid of string artifacts */
+ size = res;
+
+ /* subtract one for the null terminator */
+ size -= 1;
+
+ /* subtract one for the negative sign */
+ if (mp_int_compare_zero(op) < 0) size -= 1;
+
+ return size;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/gmp_compat.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/gmp_compat.h
new file mode 100644
index 00000000000..949e377e979
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/gmp_compat.h
@@ -0,0 +1,229 @@
+/*
+ Name: gmp_compat.h
+ Purpose: Provide GMP compatiable routines for imath library
+ Author: David Peixotto
+
+ Copyright (c) 2012 Qualcomm Innovation Center, Inc. All rights reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+#ifndef IMATH_GMP_COMPAT_H_
+#define IMATH_GMP_COMPAT_H_
+#include "imath.h"
+#include "imrat.h"
+#include <stddef.h>
+
+#define GMPZAPI(fun) impz_ ## fun
+#define GMPQAPI(fun) impq_ ## fun
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*************************************************************************
+ *
+ * Functions with direct translations
+ *
+ *************************************************************************/
+/* gmp: mpq_clear */
+void GMPQAPI(clear)(mp_rat x);
+
+/* gmp: mpq_cmp */
+int GMPQAPI(cmp)(mp_rat op1, mp_rat op2);
+
+/* gmp: mpq_init */
+void GMPQAPI(init)(mp_rat x);
+
+/* gmp: mpq_mul */
+void GMPQAPI(mul)(mp_rat product, mp_rat multiplier, mp_rat multiplicand);
+
+/* gmp: mpq_set */
+void GMPQAPI(set)(mp_rat rop, mp_rat op);
+
+/* gmp: mpz_abs */
+void GMPZAPI(abs)(mp_int rop, mp_int op);
+
+/* gmp: mpz_add */
+void GMPZAPI(add)(mp_int rop, mp_int op1, mp_int op2);
+
+/* gmp: mpz_clear */
+void GMPZAPI(clear)(mp_int x);
+
+/* gmp: mpz_cmp_si */
+int GMPZAPI(cmp_si)(mp_int op1, long op2);
+
+/* gmp: mpz_cmpabs */
+int GMPZAPI(cmpabs)(mp_int op1, mp_int op2);
+
+/* gmp: mpz_cmp */
+int GMPZAPI(cmp)(mp_int op1, mp_int op2);
+
+/* gmp: mpz_init */
+void GMPZAPI(init)(mp_int x);
+
+/* gmp: mpz_mul */
+void GMPZAPI(mul)(mp_int rop, mp_int op1, mp_int op2);
+
+/* gmp: mpz_neg */
+void GMPZAPI(neg)(mp_int rop, mp_int op);
+
+/* gmp: mpz_set_si */
+void GMPZAPI(set_si)(mp_int rop, long op);
+
+/* gmp: mpz_set */
+void GMPZAPI(set)(mp_int rop, mp_int op);
+
+/* gmp: mpz_sub */
+void GMPZAPI(sub)(mp_int rop, mp_int op1, mp_int op2);
+
+/* gmp: mpz_swap */
+void GMPZAPI(swap)(mp_int rop1, mp_int rop2);
+
+/* gmp: mpq_sgn */
+int GMPQAPI(sgn)(mp_rat op);
+
+/* gmp: mpz_sgn */
+int GMPZAPI(sgn)(mp_int op);
+
+/* gmp: mpq_set_ui */
+void GMPQAPI(set_ui)(mp_rat rop, unsigned long op1, unsigned long op2);
+
+/* gmp: mpz_set_ui */
+void GMPZAPI(set_ui)(mp_int rop, unsigned long op);
+
+/* gmp: mpq_den_ref */
+mp_int GMPQAPI(denref)(mp_rat op);
+
+/* gmp: mpq_num_ref */
+mp_int GMPQAPI(numref)(mp_rat op);
+
+/* gmp: mpq_canonicalize */
+void GMPQAPI(canonicalize)(mp_rat op);
+
+/*************************************************************************
+ *
+ * Functions that can be implemented as a combination of imath functions
+ *
+ *************************************************************************/
+/* gmp: mpz_addmul */
+void GMPZAPI(addmul)(mp_int rop, mp_int op1, mp_int op2);
+
+/* gmp: mpz_divexact */
+void GMPZAPI(divexact)(mp_int q, mp_int n, mp_int d);
+
+/* gmp: mpz_divisible_p */
+int GMPZAPI(divisible_p)(mp_int n, mp_int d);
+
+/* gmp: mpz_submul */
+void GMPZAPI(submul)(mp_int rop, mp_int op1, mp_int op2);
+
+/* gmp: mpz_add_ui */
+void GMPZAPI(add_ui)(mp_int rop, mp_int op1, unsigned long op2);
+
+/* gmp: mpz_divexact_ui */
+void GMPZAPI(divexact_ui)(mp_int q, mp_int n, unsigned long d);
+
+/* gmp: mpz_mul_ui */
+void GMPZAPI(mul_ui)(mp_int rop, mp_int op1, unsigned long op2);
+
+/* gmp: mpz_pow_ui */
+void GMPZAPI(pow_ui)(mp_int rop, mp_int base, unsigned long exp);
+
+/* gmp: mpz_sub_ui */
+void GMPZAPI(sub_ui)(mp_int rop, mp_int op1, unsigned long op2);
+
+/* gmp: mpz_fdiv_q_ui */
+unsigned long GMPZAPI(fdiv_q_ui)(mp_int q, mp_int n, unsigned long d);
+
+/* gmp: mpz_sizeinbase */
+size_t GMPZAPI(sizeinbase)(mp_int op, int base);
+
+/*************************************************************************
+ *
+ * Functions with different behavior in corner cases
+ *
+ *************************************************************************/
+/* gmp: mpz_gcd */
+/* gmp: When op1 = 0 and op2 = 0, return 0.*/
+void GMPZAPI(gcd)(mp_int rop, mp_int op1, mp_int op2);
+
+/* gmp: mpz_get_str */
+/* gmp: If str is NULL then allocate space using the default allocator. */
+char* GMPZAPI(get_str)(char *str, int radix, mp_int op);
+
+/* gmp: mpq_get_str */
+/* gmp: If str is NULL then allocate space using the default allocator. */
+/* gmp: If value is a whole number do not print denomenator. */
+/* TODO: Need to handle 0 values better. GMP prints 0/4 instead of 0.*/
+char* GMPQAPI(get_str)(char *str, int radix, mp_rat op);
+
+/* gmp: mpz_set_str */
+/* gmp: Allow and ignore spaces in string. */
+int GMPZAPI(set_str)(mp_int rop, char *str, int base);
+
+/* gmp: mpq_set_str */
+int GMPQAPI(set_str)(mp_rat rop, char *str, int base);
+
+/* gmp: mpz_get_ui */
+/* gmp: Return least significant bits if value is too big for a long. */
+unsigned long GMPZAPI(get_ui)(mp_int op);
+
+/* gmp: mpz_get_si */
+/* gmp: Return least significant bits if value is too bit for a long. */
+/* gmp: If value is too big for long, return the least significant
+ (8*sizeof(long)-1) bits from the op and set the sign bit according to
+ the sign of the op. */
+long GMPZAPI(get_si)(mp_int op);
+
+/* gmp: mpz_lcm */
+/* gmp: When op1 = 0 or op2 = 0, return 0.*/
+/* gmp: The resutl of lcm(a,b) is always positive. */
+void GMPZAPI(lcm)(mp_int rop, mp_int op1, mp_int op2);
+
+/* gmp: mpz_mul_2exp */
+/* gmp: allow big values for op2 when op1 == 0 */
+void GMPZAPI(mul_2exp)(mp_int rop, mp_int op1, unsigned long op2);
+
+/*************************************************************************
+ *
+ * Functions needing expanded functionality
+ *
+ *************************************************************************/
+/* gmp: mpz_cdiv_q */
+void GMPZAPI(cdiv_q)(mp_int q, mp_int n, mp_int d);
+
+/* gmp: mpz_fdiv_q */
+void GMPZAPI(fdiv_q)(mp_int q, mp_int n, mp_int d);
+
+/* gmp: mpz_fdiv_r */
+void GMPZAPI(fdiv_r)(mp_int r, mp_int n, mp_int d);
+
+/* gmp: mpz_tdiv_q */
+void GMPZAPI(tdiv_q)(mp_int q, mp_int n, mp_int d);
+
+/* gmp: mpz_export */
+void* GMPZAPI(export)(void *rop, size_t *countp, int order, size_t size, int endian, size_t nails, mp_int op);
+
+/* gmp: mpz_import */
+void GMPZAPI(import)(mp_int rop, size_t count, int order, size_t size, int endian, size_t nails, const void* op);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* end IMATH_GMP_COMPAT_H_ */
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imath.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imath.c
new file mode 100644
index 00000000000..26dc91f35e4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imath.c
@@ -0,0 +1,2780 @@
+/*
+ Name: imath.c
+ Purpose: Arbitrary precision integer arithmetic routines.
+ Author: M. J. Fromberger
+
+ Copyright (C) 2002-2007 Michael J. Fromberger, All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+#include "imath.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+const mp_result MP_OK = 0; /* no error, all is well */
+const mp_result MP_FALSE = 0; /* boolean false */
+const mp_result MP_TRUE = -1; /* boolean true */
+const mp_result MP_MEMORY = -2; /* out of memory */
+const mp_result MP_RANGE = -3; /* argument out of range */
+const mp_result MP_UNDEF = -4; /* result undefined */
+const mp_result MP_TRUNC = -5; /* output truncated */
+const mp_result MP_BADARG = -6; /* invalid null argument */
+const mp_result MP_MINERR = -6;
+
+const mp_sign MP_NEG = 1; /* value is strictly negative */
+const mp_sign MP_ZPOS = 0; /* value is non-negative */
+
+static const char *s_unknown_err = "unknown result code";
+static const char *s_error_msg[] = {"error code 0", "boolean true",
+ "out of memory", "argument out of range",
+ "result undefined", "output truncated",
+ "invalid argument", NULL};
+
+/* The ith entry of this table gives the value of log_i(2).
+
+ An integer value n requires ceil(log_i(n)) digits to be represented
+ in base i. Since it is easy to compute lg(n), by counting bits, we
+ can compute log_i(n) = lg(n) * log_i(2).
+
+ The use of this table eliminates a dependency upon linkage against
+ the standard math libraries.
+
+ If MP_MAX_RADIX is increased, this table should be expanded too.
+ */
+static const double s_log2[] = {
+ 0.000000000, 0.000000000, 1.000000000, 0.630929754, /* (D)(D) 2 3 */
+ 0.500000000, 0.430676558, 0.386852807, 0.356207187, /* 4 5 6 7 */
+ 0.333333333, 0.315464877, 0.301029996, 0.289064826, /* 8 9 10 11 */
+ 0.278942946, 0.270238154, 0.262649535, 0.255958025, /* 12 13 14 15 */
+ 0.250000000, 0.244650542, 0.239812467, 0.235408913, /* 16 17 18 19 */
+ 0.231378213, 0.227670249, 0.224243824, 0.221064729, /* 20 21 22 23 */
+ 0.218104292, 0.215338279, 0.212746054, 0.210309918, /* 24 25 26 27 */
+ 0.208014598, 0.205846832, 0.203795047, 0.201849087, /* 28 29 30 31 */
+ 0.200000000, 0.198239863, 0.196561632, 0.194959022, /* 32 33 34 35 */
+ 0.193426404, /* 36 */
+};
+
+/* Return the number of digits needed to represent a static value */
+#define MP_VALUE_DIGITS(V) \
+ ((sizeof(V) + (sizeof(mp_digit) - 1)) / sizeof(mp_digit))
+
+/* Round precision P to nearest word boundary */
+static inline mp_size s_round_prec(mp_size P) { return 2 * ((P + 1) / 2); }
+
+/* Set array P of S digits to zero */
+static inline void ZERO(mp_digit *P, mp_size S) {
+ mp_size i__ = S * sizeof(mp_digit);
+ mp_digit *p__ = P;
+ memset(p__, 0, i__);
+}
+
+/* Copy S digits from array P to array Q */
+static inline void COPY(mp_digit *P, mp_digit *Q, mp_size S) {
+ mp_size i__ = S * sizeof(mp_digit);
+ mp_digit *p__ = P;
+ mp_digit *q__ = Q;
+ memcpy(q__, p__, i__);
+}
+
+/* Reverse N elements of unsigned char in A. */
+static inline void REV(unsigned char *A, int N) {
+ unsigned char *u_ = A;
+ unsigned char *v_ = u_ + N - 1;
+ while (u_ < v_) {
+ unsigned char xch = *u_;
+ *u_++ = *v_;
+ *v_-- = xch;
+ }
+}
+
+/* Strip leading zeroes from z_ in-place. */
+static inline void CLAMP(mp_int z_) {
+ mp_size uz_ = MP_USED(z_);
+ mp_digit *dz_ = MP_DIGITS(z_) + uz_ - 1;
+ while (uz_ > 1 && (*dz_-- == 0)) --uz_;
+ z_->used = uz_;
+}
+
+/* Select min/max. */
+static inline int MIN(int A, int B) { return (B < A ? B : A); }
+static inline mp_size MAX(mp_size A, mp_size B) { return (B > A ? B : A); }
+
+/* Exchange lvalues A and B of type T, e.g.
+ SWAP(int, x, y) where x and y are variables of type int. */
+#define SWAP(T, A, B) \
+ do { \
+ T t_ = (A); \
+ A = (B); \
+ B = t_; \
+ } while (0)
+
+/* Declare a block of N temporary mpz_t values.
+ These values are initialized to zero.
+ You must add CLEANUP_TEMP() at the end of the function.
+ Use TEMP(i) to access a pointer to the ith value.
+ */
+#define DECLARE_TEMP(N) \
+ struct { \
+ mpz_t value[(N)]; \
+ int len; \
+ mp_result err; \
+ } temp_ = { \
+ .len = (N), \
+ .err = MP_OK, \
+ }; \
+ do { \
+ for (int i = 0; i < temp_.len; i++) { \
+ mp_int_init(TEMP(i)); \
+ } \
+ } while (0)
+
+/* Clear all allocated temp values. */
+#define CLEANUP_TEMP() \
+ CLEANUP: \
+ do { \
+ for (int i = 0; i < temp_.len; i++) { \
+ mp_int_clear(TEMP(i)); \
+ } \
+ if (temp_.err != MP_OK) { \
+ return temp_.err; \
+ } \
+ } while (0)
+
+/* A pointer to the kth temp value. */
+#define TEMP(K) (temp_.value + (K))
+
+/* Evaluate E, an expression of type mp_result expected to return MP_OK. If
+ the value is not MP_OK, the error is cached and control resumes at the
+ cleanup handler, which returns it.
+*/
+#define REQUIRE(E) \
+ do { \
+ temp_.err = (E); \
+ if (temp_.err != MP_OK) goto CLEANUP; \
+ } while (0)
+
+/* Compare value to zero. */
+static inline int CMPZ(mp_int Z) {
+ if (Z->used == 1 && Z->digits[0] == 0) return 0;
+ return (Z->sign == MP_NEG) ? -1 : 1;
+}
+
+static inline mp_word UPPER_HALF(mp_word W) { return (W >> MP_DIGIT_BIT); }
+static inline mp_digit LOWER_HALF(mp_word W) { return (mp_digit)(W); }
+
+/* Report whether the highest-order bit of W is 1. */
+static inline bool HIGH_BIT_SET(mp_word W) {
+ return (W >> (MP_WORD_BIT - 1)) != 0;
+}
+
+/* Report whether adding W + V will carry out. */
+static inline bool ADD_WILL_OVERFLOW(mp_word W, mp_word V) {
+ return ((MP_WORD_MAX - V) < W);
+}
+
+/* Default number of digits allocated to a new mp_int */
+static mp_size default_precision = 8;
+
+void mp_int_default_precision(mp_size size) {
+ assert(size > 0);
+ default_precision = size;
+}
+
+/* Minimum number of digits to invoke recursive multiply */
+static mp_size multiply_threshold = 32;
+
+void mp_int_multiply_threshold(mp_size thresh) {
+ assert(thresh >= sizeof(mp_word));
+ multiply_threshold = thresh;
+}
+
+/* Allocate a buffer of (at least) num digits, or return
+ NULL if that couldn't be done. */
+static mp_digit *s_alloc(mp_size num);
+
+/* Release a buffer of digits allocated by s_alloc(). */
+static void s_free(void *ptr);
+
+/* Insure that z has at least min digits allocated, resizing if
+ necessary. Returns true if successful, false if out of memory. */
+static bool s_pad(mp_int z, mp_size min);
+
+/* Ensure Z has at least N digits allocated. */
+static inline mp_result GROW(mp_int Z, mp_size N) {
+ return s_pad(Z, N) ? MP_OK : MP_MEMORY;
+}
+
+/* Fill in a "fake" mp_int on the stack with a given value */
+static void s_fake(mp_int z, mp_small value, mp_digit vbuf[]);
+static void s_ufake(mp_int z, mp_usmall value, mp_digit vbuf[]);
+
+/* Compare two runs of digits of given length, returns <0, 0, >0 */
+static int s_cdig(mp_digit *da, mp_digit *db, mp_size len);
+
+/* Pack the unsigned digits of v into array t */
+static int s_uvpack(mp_usmall v, mp_digit t[]);
+
+/* Compare magnitudes of a and b, returns <0, 0, >0 */
+static int s_ucmp(mp_int a, mp_int b);
+
+/* Compare magnitudes of a and v, returns <0, 0, >0 */
+static int s_vcmp(mp_int a, mp_small v);
+static int s_uvcmp(mp_int a, mp_usmall uv);
+
+/* Unsigned magnitude addition; assumes dc is big enough.
+ Carry out is returned (no memory allocated). */
+static mp_digit s_uadd(mp_digit *da, mp_digit *db, mp_digit *dc, mp_size size_a,
+ mp_size size_b);
+
+/* Unsigned magnitude subtraction. Assumes dc is big enough. */
+static void s_usub(mp_digit *da, mp_digit *db, mp_digit *dc, mp_size size_a,
+ mp_size size_b);
+
+/* Unsigned recursive multiplication. Assumes dc is big enough. */
+static int s_kmul(mp_digit *da, mp_digit *db, mp_digit *dc, mp_size size_a,
+ mp_size size_b);
+
+/* Unsigned magnitude multiplication. Assumes dc is big enough. */
+static void s_umul(mp_digit *da, mp_digit *db, mp_digit *dc, mp_size size_a,
+ mp_size size_b);
+
+/* Unsigned recursive squaring. Assumes dc is big enough. */
+static int s_ksqr(mp_digit *da, mp_digit *dc, mp_size size_a);
+
+/* Unsigned magnitude squaring. Assumes dc is big enough. */
+static void s_usqr(mp_digit *da, mp_digit *dc, mp_size size_a);
+
+/* Single digit addition. Assumes a is big enough. */
+static void s_dadd(mp_int a, mp_digit b);
+
+/* Single digit multiplication. Assumes a is big enough. */
+static void s_dmul(mp_int a, mp_digit b);
+
+/* Single digit multiplication on buffers; assumes dc is big enough. */
+static void s_dbmul(mp_digit *da, mp_digit b, mp_digit *dc, mp_size size_a);
+
+/* Single digit division. Replaces a with the quotient,
+ returns the remainder. */
+static mp_digit s_ddiv(mp_int a, mp_digit b);
+
+/* Quick division by a power of 2, replaces z (no allocation) */
+static void s_qdiv(mp_int z, mp_size p2);
+
+/* Quick remainder by a power of 2, replaces z (no allocation) */
+static void s_qmod(mp_int z, mp_size p2);
+
+/* Quick multiplication by a power of 2, replaces z.
+ Allocates if necessary; returns false in case this fails. */
+static int s_qmul(mp_int z, mp_size p2);
+
+/* Quick subtraction from a power of 2, replaces z.
+ Allocates if necessary; returns false in case this fails. */
+static int s_qsub(mp_int z, mp_size p2);
+
+/* Return maximum k such that 2^k divides z. */
+static int s_dp2k(mp_int z);
+
+/* Return k >= 0 such that z = 2^k, or -1 if there is no such k. */
+static int s_isp2(mp_int z);
+
+/* Set z to 2^k. May allocate; returns false in case this fails. */
+static int s_2expt(mp_int z, mp_small k);
+
+/* Normalize a and b for division, returns normalization constant */
+static int s_norm(mp_int a, mp_int b);
+
+/* Compute constant mu for Barrett reduction, given modulus m, result
+ replaces z, m is untouched. */
+static mp_result s_brmu(mp_int z, mp_int m);
+
+/* Reduce a modulo m, using Barrett's algorithm. */
+static int s_reduce(mp_int x, mp_int m, mp_int mu, mp_int q1, mp_int q2);
+
+/* Modular exponentiation, using Barrett reduction */
+static mp_result s_embar(mp_int a, mp_int b, mp_int m, mp_int mu, mp_int c);
+
+/* Unsigned magnitude division. Assumes |a| > |b|. Allocates temporaries;
+ overwrites a with quotient, b with remainder. */
+static mp_result s_udiv_knuth(mp_int a, mp_int b);
+
+/* Compute the number of digits in radix r required to represent the given
+ value. Does not account for sign flags, terminators, etc. */
+static int s_outlen(mp_int z, mp_size r);
+
+/* Guess how many digits of precision will be needed to represent a radix r
+ value of the specified number of digits. Returns a value guaranteed to be
+ no smaller than the actual number required. */
+static mp_size s_inlen(int len, mp_size r);
+
+/* Convert a character to a digit value in radix r, or
+ -1 if out of range */
+static int s_ch2val(char c, int r);
+
+/* Convert a digit value to a character */
+static char s_val2ch(int v, int caps);
+
+/* Take 2's complement of a buffer in place */
+static void s_2comp(unsigned char *buf, int len);
+
+/* Convert a value to binary, ignoring sign. On input, *limpos is the bound on
+ how many bytes should be written to buf; on output, *limpos is set to the
+ number of bytes actually written. */
+static mp_result s_tobin(mp_int z, unsigned char *buf, int *limpos, int pad);
+
+/* Multiply X by Y into Z, ignoring signs. Requires that Z have enough storage
+ preallocated to hold the result. */
+static inline void UMUL(mp_int X, mp_int Y, mp_int Z) {
+ mp_size ua_ = MP_USED(X);
+ mp_size ub_ = MP_USED(Y);
+ mp_size o_ = ua_ + ub_;
+ ZERO(MP_DIGITS(Z), o_);
+ (void)s_kmul(MP_DIGITS(X), MP_DIGITS(Y), MP_DIGITS(Z), ua_, ub_);
+ Z->used = o_;
+ CLAMP(Z);
+}
+
+/* Square X into Z. Requires that Z have enough storage to hold the result. */
+static inline void USQR(mp_int X, mp_int Z) {
+ mp_size ua_ = MP_USED(X);
+ mp_size o_ = ua_ + ua_;
+ ZERO(MP_DIGITS(Z), o_);
+ (void)s_ksqr(MP_DIGITS(X), MP_DIGITS(Z), ua_);
+ Z->used = o_;
+ CLAMP(Z);
+}
+
+mp_result mp_int_init(mp_int z) {
+ if (z == NULL) return MP_BADARG;
+
+ z->single = 0;
+ z->digits = &(z->single);
+ z->alloc = 1;
+ z->used = 1;
+ z->sign = MP_ZPOS;
+
+ return MP_OK;
+}
+
+mp_int mp_int_alloc(void) {
+ mp_int out = malloc(sizeof(mpz_t));
+
+ if (out != NULL) mp_int_init(out);
+
+ return out;
+}
+
+mp_result mp_int_init_size(mp_int z, mp_size prec) {
+ assert(z != NULL);
+
+ if (prec == 0) {
+ prec = default_precision;
+ } else if (prec == 1) {
+ return mp_int_init(z);
+ } else {
+ prec = s_round_prec(prec);
+ }
+
+ z->digits = s_alloc(prec);
+ if (MP_DIGITS(z) == NULL) return MP_MEMORY;
+
+ z->digits[0] = 0;
+ z->used = 1;
+ z->alloc = prec;
+ z->sign = MP_ZPOS;
+
+ return MP_OK;
+}
+
+mp_result mp_int_init_copy(mp_int z, mp_int old) {
+ assert(z != NULL && old != NULL);
+
+ mp_size uold = MP_USED(old);
+ if (uold == 1) {
+ mp_int_init(z);
+ } else {
+ mp_size target = MAX(uold, default_precision);
+ mp_result res = mp_int_init_size(z, target);
+ if (res != MP_OK) return res;
+ }
+
+ z->used = uold;
+ z->sign = old->sign;
+ COPY(MP_DIGITS(old), MP_DIGITS(z), uold);
+
+ return MP_OK;
+}
+
+mp_result mp_int_init_value(mp_int z, mp_small value) {
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+ return mp_int_init_copy(z, &vtmp);
+}
+
+mp_result mp_int_init_uvalue(mp_int z, mp_usmall uvalue) {
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(uvalue)];
+
+ s_ufake(&vtmp, uvalue, vbuf);
+ return mp_int_init_copy(z, &vtmp);
+}
+
+mp_result mp_int_set_value(mp_int z, mp_small value) {
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+ return mp_int_copy(&vtmp, z);
+}
+
+mp_result mp_int_set_uvalue(mp_int z, mp_usmall uvalue) {
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(uvalue)];
+
+ s_ufake(&vtmp, uvalue, vbuf);
+ return mp_int_copy(&vtmp, z);
+}
+
+void mp_int_clear(mp_int z) {
+ if (z == NULL) return;
+
+ if (MP_DIGITS(z) != NULL) {
+ if (MP_DIGITS(z) != &(z->single)) s_free(MP_DIGITS(z));
+
+ z->digits = NULL;
+ }
+}
+
+void mp_int_free(mp_int z) {
+ assert(z != NULL);
+
+ mp_int_clear(z);
+ free(z); /* note: NOT s_free() */
+}
+
+mp_result mp_int_copy(mp_int a, mp_int c) {
+ assert(a != NULL && c != NULL);
+
+ if (a != c) {
+ mp_size ua = MP_USED(a);
+ mp_digit *da, *dc;
+
+ if (!s_pad(c, ua)) return MP_MEMORY;
+
+ da = MP_DIGITS(a);
+ dc = MP_DIGITS(c);
+ COPY(da, dc, ua);
+
+ c->used = ua;
+ c->sign = a->sign;
+ }
+
+ return MP_OK;
+}
+
+void mp_int_swap(mp_int a, mp_int c) {
+ if (a != c) {
+ mpz_t tmp = *a;
+
+ *a = *c;
+ *c = tmp;
+
+ if (MP_DIGITS(a) == &(c->single)) a->digits = &(a->single);
+ if (MP_DIGITS(c) == &(a->single)) c->digits = &(c->single);
+ }
+}
+
+void mp_int_zero(mp_int z) {
+ assert(z != NULL);
+
+ z->digits[0] = 0;
+ z->used = 1;
+ z->sign = MP_ZPOS;
+}
+
+mp_result mp_int_abs(mp_int a, mp_int c) {
+ assert(a != NULL && c != NULL);
+
+ mp_result res;
+ if ((res = mp_int_copy(a, c)) != MP_OK) return res;
+
+ c->sign = MP_ZPOS;
+ return MP_OK;
+}
+
+mp_result mp_int_neg(mp_int a, mp_int c) {
+ assert(a != NULL && c != NULL);
+
+ mp_result res;
+ if ((res = mp_int_copy(a, c)) != MP_OK) return res;
+
+ if (CMPZ(c) != 0) c->sign = 1 - MP_SIGN(a);
+
+ return MP_OK;
+}
+
+mp_result mp_int_add(mp_int a, mp_int b, mp_int c) {
+ assert(a != NULL && b != NULL && c != NULL);
+
+ mp_size ua = MP_USED(a);
+ mp_size ub = MP_USED(b);
+ mp_size max = MAX(ua, ub);
+
+ if (MP_SIGN(a) == MP_SIGN(b)) {
+ /* Same sign -- add magnitudes, preserve sign of addends */
+ if (!s_pad(c, max)) return MP_MEMORY;
+
+ mp_digit carry = s_uadd(MP_DIGITS(a), MP_DIGITS(b), MP_DIGITS(c), ua, ub);
+ mp_size uc = max;
+
+ if (carry) {
+ if (!s_pad(c, max + 1)) return MP_MEMORY;
+
+ c->digits[max] = carry;
+ ++uc;
+ }
+
+ c->used = uc;
+ c->sign = a->sign;
+
+ } else {
+ /* Different signs -- subtract magnitudes, preserve sign of greater */
+ int cmp = s_ucmp(a, b); /* magnitude comparison, sign ignored */
+
+ /* Set x to max(a, b), y to min(a, b) to simplify later code.
+ A special case yields zero for equal magnitudes.
+ */
+ mp_int x, y;
+ if (cmp == 0) {
+ mp_int_zero(c);
+ return MP_OK;
+ } else if (cmp < 0) {
+ x = b;
+ y = a;
+ } else {
+ x = a;
+ y = b;
+ }
+
+ if (!s_pad(c, MP_USED(x))) return MP_MEMORY;
+
+ /* Subtract smaller from larger */
+ s_usub(MP_DIGITS(x), MP_DIGITS(y), MP_DIGITS(c), MP_USED(x), MP_USED(y));
+ c->used = x->used;
+ CLAMP(c);
+
+ /* Give result the sign of the larger */
+ c->sign = x->sign;
+ }
+
+ return MP_OK;
+}
+
+mp_result mp_int_add_value(mp_int a, mp_small value, mp_int c) {
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+
+ return mp_int_add(a, &vtmp, c);
+}
+
+mp_result mp_int_sub(mp_int a, mp_int b, mp_int c) {
+ assert(a != NULL && b != NULL && c != NULL);
+
+ mp_size ua = MP_USED(a);
+ mp_size ub = MP_USED(b);
+ mp_size max = MAX(ua, ub);
+
+ if (MP_SIGN(a) != MP_SIGN(b)) {
+ /* Different signs -- add magnitudes and keep sign of a */
+ if (!s_pad(c, max)) return MP_MEMORY;
+
+ mp_digit carry = s_uadd(MP_DIGITS(a), MP_DIGITS(b), MP_DIGITS(c), ua, ub);
+ mp_size uc = max;
+
+ if (carry) {
+ if (!s_pad(c, max + 1)) return MP_MEMORY;
+
+ c->digits[max] = carry;
+ ++uc;
+ }
+
+ c->used = uc;
+ c->sign = a->sign;
+
+ } else {
+ /* Same signs -- subtract magnitudes */
+ if (!s_pad(c, max)) return MP_MEMORY;
+ mp_int x, y;
+ mp_sign osign;
+
+ int cmp = s_ucmp(a, b);
+ if (cmp >= 0) {
+ x = a;
+ y = b;
+ osign = MP_ZPOS;
+ } else {
+ x = b;
+ y = a;
+ osign = MP_NEG;
+ }
+
+ if (MP_SIGN(a) == MP_NEG && cmp != 0) osign = 1 - osign;
+
+ s_usub(MP_DIGITS(x), MP_DIGITS(y), MP_DIGITS(c), MP_USED(x), MP_USED(y));
+ c->used = x->used;
+ CLAMP(c);
+
+ c->sign = osign;
+ }
+
+ return MP_OK;
+}
+
+mp_result mp_int_sub_value(mp_int a, mp_small value, mp_int c) {
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+
+ return mp_int_sub(a, &vtmp, c);
+}
+
+mp_result mp_int_mul(mp_int a, mp_int b, mp_int c) {
+ assert(a != NULL && b != NULL && c != NULL);
+
+ /* If either input is zero, we can shortcut multiplication */
+ if (mp_int_compare_zero(a) == 0 || mp_int_compare_zero(b) == 0) {
+ mp_int_zero(c);
+ return MP_OK;
+ }
+
+ /* Output is positive if inputs have same sign, otherwise negative */
+ mp_sign osign = (MP_SIGN(a) == MP_SIGN(b)) ? MP_ZPOS : MP_NEG;
+
+ /* If the output is not identical to any of the inputs, we'll write the
+ results directly; otherwise, allocate a temporary space. */
+ mp_size ua = MP_USED(a);
+ mp_size ub = MP_USED(b);
+ mp_size osize = MAX(ua, ub);
+ osize = 4 * ((osize + 1) / 2);
+
+ mp_digit *out;
+ mp_size p = 0;
+ if (c == a || c == b) {
+ p = MAX(s_round_prec(osize), default_precision);
+
+ if ((out = s_alloc(p)) == NULL) return MP_MEMORY;
+ } else {
+ if (!s_pad(c, osize)) return MP_MEMORY;
+
+ out = MP_DIGITS(c);
+ }
+ ZERO(out, osize);
+
+ if (!s_kmul(MP_DIGITS(a), MP_DIGITS(b), out, ua, ub)) return MP_MEMORY;
+
+ /* If we allocated a new buffer, get rid of whatever memory c was already
+ using, and fix up its fields to reflect that.
+ */
+ if (out != MP_DIGITS(c)) {
+ if ((void *)MP_DIGITS(c) != (void *)c) s_free(MP_DIGITS(c));
+ c->digits = out;
+ c->alloc = p;
+ }
+
+ c->used = osize; /* might not be true, but we'll fix it ... */
+ CLAMP(c); /* ... right here */
+ c->sign = osign;
+
+ return MP_OK;
+}
+
+mp_result mp_int_mul_value(mp_int a, mp_small value, mp_int c) {
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+
+ return mp_int_mul(a, &vtmp, c);
+}
+
+mp_result mp_int_mul_pow2(mp_int a, mp_small p2, mp_int c) {
+ assert(a != NULL && c != NULL && p2 >= 0);
+
+ mp_result res = mp_int_copy(a, c);
+ if (res != MP_OK) return res;
+
+ if (s_qmul(c, (mp_size)p2)) {
+ return MP_OK;
+ } else {
+ return MP_MEMORY;
+ }
+}
+
+mp_result mp_int_sqr(mp_int a, mp_int c) {
+ assert(a != NULL && c != NULL);
+
+ /* Get a temporary buffer big enough to hold the result */
+ mp_size osize = (mp_size)4 * ((MP_USED(a) + 1) / 2);
+ mp_size p = 0;
+ mp_digit *out;
+ if (a == c) {
+ p = s_round_prec(osize);
+ p = MAX(p, default_precision);
+
+ if ((out = s_alloc(p)) == NULL) return MP_MEMORY;
+ } else {
+ if (!s_pad(c, osize)) return MP_MEMORY;
+
+ out = MP_DIGITS(c);
+ }
+ ZERO(out, osize);
+
+ s_ksqr(MP_DIGITS(a), out, MP_USED(a));
+
+ /* Get rid of whatever memory c was already using, and fix up its fields to
+ reflect the new digit array it's using
+ */
+ if (out != MP_DIGITS(c)) {
+ if ((void *)MP_DIGITS(c) != (void *)c) s_free(MP_DIGITS(c));
+ c->digits = out;
+ c->alloc = p;
+ }
+
+ c->used = osize; /* might not be true, but we'll fix it ... */
+ CLAMP(c); /* ... right here */
+ c->sign = MP_ZPOS;
+
+ return MP_OK;
+}
+
+mp_result mp_int_div(mp_int a, mp_int b, mp_int q, mp_int r) {
+ assert(a != NULL && b != NULL && q != r);
+
+ int cmp;
+ mp_result res = MP_OK;
+ mp_int qout, rout;
+ mp_sign sa = MP_SIGN(a);
+ mp_sign sb = MP_SIGN(b);
+ if (CMPZ(b) == 0) {
+ return MP_UNDEF;
+ } else if ((cmp = s_ucmp(a, b)) < 0) {
+ /* If |a| < |b|, no division is required:
+ q = 0, r = a
+ */
+ if (r && (res = mp_int_copy(a, r)) != MP_OK) return res;
+
+ if (q) mp_int_zero(q);
+
+ return MP_OK;
+ } else if (cmp == 0) {
+ /* If |a| = |b|, no division is required:
+ q = 1 or -1, r = 0
+ */
+ if (r) mp_int_zero(r);
+
+ if (q) {
+ mp_int_zero(q);
+ q->digits[0] = 1;
+
+ if (sa != sb) q->sign = MP_NEG;
+ }
+
+ return MP_OK;
+ }
+
+ /* When |a| > |b|, real division is required. We need someplace to store
+ quotient and remainder, but q and r are allowed to be NULL or to overlap
+ with the inputs.
+ */
+ DECLARE_TEMP(2);
+ int lg;
+ if ((lg = s_isp2(b)) < 0) {
+ if (q && b != q) {
+ REQUIRE(mp_int_copy(a, q));
+ qout = q;
+ } else {
+ REQUIRE(mp_int_copy(a, TEMP(0)));
+ qout = TEMP(0);
+ }
+
+ if (r && a != r) {
+ REQUIRE(mp_int_copy(b, r));
+ rout = r;
+ } else {
+ REQUIRE(mp_int_copy(b, TEMP(1)));
+ rout = TEMP(1);
+ }
+
+ REQUIRE(s_udiv_knuth(qout, rout));
+ } else {
+ if (q) REQUIRE(mp_int_copy(a, q));
+ if (r) REQUIRE(mp_int_copy(a, r));
+
+ if (q) s_qdiv(q, (mp_size)lg);
+ qout = q;
+ if (r) s_qmod(r, (mp_size)lg);
+ rout = r;
+ }
+
+ /* Recompute signs for output */
+ if (rout) {
+ rout->sign = sa;
+ if (CMPZ(rout) == 0) rout->sign = MP_ZPOS;
+ }
+ if (qout) {
+ qout->sign = (sa == sb) ? MP_ZPOS : MP_NEG;
+ if (CMPZ(qout) == 0) qout->sign = MP_ZPOS;
+ }
+
+ if (q) REQUIRE(mp_int_copy(qout, q));
+ if (r) REQUIRE(mp_int_copy(rout, r));
+ CLEANUP_TEMP();
+ return res;
+}
+
+mp_result mp_int_mod(mp_int a, mp_int m, mp_int c) {
+ DECLARE_TEMP(1);
+ mp_int out = (m == c) ? TEMP(0) : c;
+ REQUIRE(mp_int_div(a, m, NULL, out));
+ if (CMPZ(out) < 0) {
+ REQUIRE(mp_int_add(out, m, c));
+ } else {
+ REQUIRE(mp_int_copy(out, c));
+ }
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+mp_result mp_int_div_value(mp_int a, mp_small value, mp_int q, mp_small *r) {
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+ s_fake(&vtmp, value, vbuf);
+
+ DECLARE_TEMP(1);
+ REQUIRE(mp_int_div(a, &vtmp, q, TEMP(0)));
+
+ if (r) (void)mp_int_to_int(TEMP(0), r); /* can't fail */
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+mp_result mp_int_div_pow2(mp_int a, mp_small p2, mp_int q, mp_int r) {
+ assert(a != NULL && p2 >= 0 && q != r);
+
+ mp_result res = MP_OK;
+ if (q != NULL && (res = mp_int_copy(a, q)) == MP_OK) {
+ s_qdiv(q, (mp_size)p2);
+ }
+
+ if (res == MP_OK && r != NULL && (res = mp_int_copy(a, r)) == MP_OK) {
+ s_qmod(r, (mp_size)p2);
+ }
+
+ return res;
+}
+
+mp_result mp_int_expt(mp_int a, mp_small b, mp_int c) {
+ assert(c != NULL);
+ if (b < 0) return MP_RANGE;
+
+ DECLARE_TEMP(1);
+ REQUIRE(mp_int_copy(a, TEMP(0)));
+
+ (void)mp_int_set_value(c, 1);
+ unsigned int v = labs(b);
+ while (v != 0) {
+ if (v & 1) {
+ REQUIRE(mp_int_mul(c, TEMP(0), c));
+ }
+
+ v >>= 1;
+ if (v == 0) break;
+
+ REQUIRE(mp_int_sqr(TEMP(0), TEMP(0)));
+ }
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+mp_result mp_int_expt_value(mp_small a, mp_small b, mp_int c) {
+ assert(c != NULL);
+ if (b < 0) return MP_RANGE;
+
+ DECLARE_TEMP(1);
+ REQUIRE(mp_int_set_value(TEMP(0), a));
+
+ (void)mp_int_set_value(c, 1);
+ unsigned int v = labs(b);
+ while (v != 0) {
+ if (v & 1) {
+ REQUIRE(mp_int_mul(c, TEMP(0), c));
+ }
+
+ v >>= 1;
+ if (v == 0) break;
+
+ REQUIRE(mp_int_sqr(TEMP(0), TEMP(0)));
+ }
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+mp_result mp_int_expt_full(mp_int a, mp_int b, mp_int c) {
+ assert(a != NULL && b != NULL && c != NULL);
+ if (MP_SIGN(b) == MP_NEG) return MP_RANGE;
+
+ DECLARE_TEMP(1);
+ REQUIRE(mp_int_copy(a, TEMP(0)));
+
+ (void)mp_int_set_value(c, 1);
+ for (unsigned ix = 0; ix < MP_USED(b); ++ix) {
+ mp_digit d = b->digits[ix];
+
+ for (unsigned jx = 0; jx < MP_DIGIT_BIT; ++jx) {
+ if (d & 1) {
+ REQUIRE(mp_int_mul(c, TEMP(0), c));
+ }
+
+ d >>= 1;
+ if (d == 0 && ix + 1 == MP_USED(b)) break;
+ REQUIRE(mp_int_sqr(TEMP(0), TEMP(0)));
+ }
+ }
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+int mp_int_compare(mp_int a, mp_int b) {
+ assert(a != NULL && b != NULL);
+
+ mp_sign sa = MP_SIGN(a);
+ if (sa == MP_SIGN(b)) {
+ int cmp = s_ucmp(a, b);
+
+ /* If they're both zero or positive, the normal comparison applies; if both
+ negative, the sense is reversed. */
+ if (sa == MP_ZPOS) {
+ return cmp;
+ } else {
+ return -cmp;
+ }
+ } else if (sa == MP_ZPOS) {
+ return 1;
+ } else {
+ return -1;
+ }
+}
+
+int mp_int_compare_unsigned(mp_int a, mp_int b) {
+ assert(a != NULL && b != NULL);
+
+ return s_ucmp(a, b);
+}
+
+int mp_int_compare_zero(mp_int z) {
+ assert(z != NULL);
+
+ if (MP_USED(z) == 1 && z->digits[0] == 0) {
+ return 0;
+ } else if (MP_SIGN(z) == MP_ZPOS) {
+ return 1;
+ } else {
+ return -1;
+ }
+}
+
+int mp_int_compare_value(mp_int z, mp_small value) {
+ assert(z != NULL);
+
+ mp_sign vsign = (value < 0) ? MP_NEG : MP_ZPOS;
+ if (vsign == MP_SIGN(z)) {
+ int cmp = s_vcmp(z, value);
+
+ return (vsign == MP_ZPOS) ? cmp : -cmp;
+ } else {
+ return (value < 0) ? 1 : -1;
+ }
+}
+
+int mp_int_compare_uvalue(mp_int z, mp_usmall uv) {
+ assert(z != NULL);
+
+ if (MP_SIGN(z) == MP_NEG) {
+ return -1;
+ } else {
+ return s_uvcmp(z, uv);
+ }
+}
+
+mp_result mp_int_exptmod(mp_int a, mp_int b, mp_int m, mp_int c) {
+ assert(a != NULL && b != NULL && c != NULL && m != NULL);
+
+ /* Zero moduli and negative exponents are not considered. */
+ if (CMPZ(m) == 0) return MP_UNDEF;
+ if (CMPZ(b) < 0) return MP_RANGE;
+
+ mp_size um = MP_USED(m);
+ DECLARE_TEMP(3);
+ REQUIRE(GROW(TEMP(0), 2 * um));
+ REQUIRE(GROW(TEMP(1), 2 * um));
+
+ mp_int s;
+ if (c == b || c == m) {
+ REQUIRE(GROW(TEMP(2), 2 * um));
+ s = TEMP(2);
+ } else {
+ s = c;
+ }
+
+ REQUIRE(mp_int_mod(a, m, TEMP(0)));
+ REQUIRE(s_brmu(TEMP(1), m));
+ REQUIRE(s_embar(TEMP(0), b, m, TEMP(1), s));
+ REQUIRE(mp_int_copy(s, c));
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+mp_result mp_int_exptmod_evalue(mp_int a, mp_small value, mp_int m, mp_int c) {
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+
+ return mp_int_exptmod(a, &vtmp, m, c);
+}
+
+mp_result mp_int_exptmod_bvalue(mp_small value, mp_int b, mp_int m, mp_int c) {
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+
+ return mp_int_exptmod(&vtmp, b, m, c);
+}
+
+mp_result mp_int_exptmod_known(mp_int a, mp_int b, mp_int m, mp_int mu,
+ mp_int c) {
+ assert(a && b && m && c);
+
+ /* Zero moduli and negative exponents are not considered. */
+ if (CMPZ(m) == 0) return MP_UNDEF;
+ if (CMPZ(b) < 0) return MP_RANGE;
+
+ DECLARE_TEMP(2);
+ mp_size um = MP_USED(m);
+ REQUIRE(GROW(TEMP(0), 2 * um));
+
+ mp_int s;
+ if (c == b || c == m) {
+ REQUIRE(GROW(TEMP(1), 2 * um));
+ s = TEMP(1);
+ } else {
+ s = c;
+ }
+
+ REQUIRE(mp_int_mod(a, m, TEMP(0)));
+ REQUIRE(s_embar(TEMP(0), b, m, mu, s));
+ REQUIRE(mp_int_copy(s, c));
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+mp_result mp_int_redux_const(mp_int m, mp_int c) {
+ assert(m != NULL && c != NULL && m != c);
+
+ return s_brmu(c, m);
+}
+
+mp_result mp_int_invmod(mp_int a, mp_int m, mp_int c) {
+ assert(a != NULL && m != NULL && c != NULL);
+
+ if (CMPZ(a) == 0 || CMPZ(m) <= 0) return MP_RANGE;
+
+ DECLARE_TEMP(2);
+
+ REQUIRE(mp_int_egcd(a, m, TEMP(0), TEMP(1), NULL));
+
+ if (mp_int_compare_value(TEMP(0), 1) != 0) {
+ REQUIRE(MP_UNDEF);
+ }
+
+ /* It is first necessary to constrain the value to the proper range */
+ REQUIRE(mp_int_mod(TEMP(1), m, TEMP(1)));
+
+ /* Now, if 'a' was originally negative, the value we have is actually the
+ magnitude of the negative representative; to get the positive value we
+ have to subtract from the modulus. Otherwise, the value is okay as it
+ stands.
+ */
+ if (MP_SIGN(a) == MP_NEG) {
+ REQUIRE(mp_int_sub(m, TEMP(1), c));
+ } else {
+ REQUIRE(mp_int_copy(TEMP(1), c));
+ }
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+/* Binary GCD algorithm due to Josef Stein, 1961 */
+mp_result mp_int_gcd(mp_int a, mp_int b, mp_int c) {
+ assert(a != NULL && b != NULL && c != NULL);
+
+ int ca = CMPZ(a);
+ int cb = CMPZ(b);
+ if (ca == 0 && cb == 0) {
+ return MP_UNDEF;
+ } else if (ca == 0) {
+ return mp_int_abs(b, c);
+ } else if (cb == 0) {
+ return mp_int_abs(a, c);
+ }
+
+ DECLARE_TEMP(3);
+ REQUIRE(mp_int_copy(a, TEMP(0)));
+ REQUIRE(mp_int_copy(b, TEMP(1)));
+
+ TEMP(0)->sign = MP_ZPOS;
+ TEMP(1)->sign = MP_ZPOS;
+
+ int k = 0;
+ { /* Divide out common factors of 2 from u and v */
+ int div2_u = s_dp2k(TEMP(0));
+ int div2_v = s_dp2k(TEMP(1));
+
+ k = MIN(div2_u, div2_v);
+ s_qdiv(TEMP(0), (mp_size)k);
+ s_qdiv(TEMP(1), (mp_size)k);
+ }
+
+ if (mp_int_is_odd(TEMP(0))) {
+ REQUIRE(mp_int_neg(TEMP(1), TEMP(2)));
+ } else {
+ REQUIRE(mp_int_copy(TEMP(0), TEMP(2)));
+ }
+
+ for (;;) {
+ s_qdiv(TEMP(2), s_dp2k(TEMP(2)));
+
+ if (CMPZ(TEMP(2)) > 0) {
+ REQUIRE(mp_int_copy(TEMP(2), TEMP(0)));
+ } else {
+ REQUIRE(mp_int_neg(TEMP(2), TEMP(1)));
+ }
+
+ REQUIRE(mp_int_sub(TEMP(0), TEMP(1), TEMP(2)));
+
+ if (CMPZ(TEMP(2)) == 0) break;
+ }
+
+ REQUIRE(mp_int_abs(TEMP(0), c));
+ if (!s_qmul(c, (mp_size)k)) REQUIRE(MP_MEMORY);
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+/* This is the binary GCD algorithm again, but this time we keep track of the
+ elementary matrix operations as we go, so we can get values x and y
+ satisfying c = ax + by.
+ */
+mp_result mp_int_egcd(mp_int a, mp_int b, mp_int c, mp_int x, mp_int y) {
+ assert(a != NULL && b != NULL && c != NULL && (x != NULL || y != NULL));
+
+ mp_result res = MP_OK;
+ int ca = CMPZ(a);
+ int cb = CMPZ(b);
+ if (ca == 0 && cb == 0) {
+ return MP_UNDEF;
+ } else if (ca == 0) {
+ if ((res = mp_int_abs(b, c)) != MP_OK) return res;
+ mp_int_zero(x);
+ (void)mp_int_set_value(y, 1);
+ return MP_OK;
+ } else if (cb == 0) {
+ if ((res = mp_int_abs(a, c)) != MP_OK) return res;
+ (void)mp_int_set_value(x, 1);
+ mp_int_zero(y);
+ return MP_OK;
+ }
+
+ /* Initialize temporaries:
+ A:0, B:1, C:2, D:3, u:4, v:5, ou:6, ov:7 */
+ DECLARE_TEMP(8);
+ REQUIRE(mp_int_set_value(TEMP(0), 1));
+ REQUIRE(mp_int_set_value(TEMP(3), 1));
+ REQUIRE(mp_int_copy(a, TEMP(4)));
+ REQUIRE(mp_int_copy(b, TEMP(5)));
+
+ /* We will work with absolute values here */
+ TEMP(4)->sign = MP_ZPOS;
+ TEMP(5)->sign = MP_ZPOS;
+
+ int k = 0;
+ { /* Divide out common factors of 2 from u and v */
+ int div2_u = s_dp2k(TEMP(4)), div2_v = s_dp2k(TEMP(5));
+
+ k = MIN(div2_u, div2_v);
+ s_qdiv(TEMP(4), k);
+ s_qdiv(TEMP(5), k);
+ }
+
+ REQUIRE(mp_int_copy(TEMP(4), TEMP(6)));
+ REQUIRE(mp_int_copy(TEMP(5), TEMP(7)));
+
+ for (;;) {
+ while (mp_int_is_even(TEMP(4))) {
+ s_qdiv(TEMP(4), 1);
+
+ if (mp_int_is_odd(TEMP(0)) || mp_int_is_odd(TEMP(1))) {
+ REQUIRE(mp_int_add(TEMP(0), TEMP(7), TEMP(0)));
+ REQUIRE(mp_int_sub(TEMP(1), TEMP(6), TEMP(1)));
+ }
+
+ s_qdiv(TEMP(0), 1);
+ s_qdiv(TEMP(1), 1);
+ }
+
+ while (mp_int_is_even(TEMP(5))) {
+ s_qdiv(TEMP(5), 1);
+
+ if (mp_int_is_odd(TEMP(2)) || mp_int_is_odd(TEMP(3))) {
+ REQUIRE(mp_int_add(TEMP(2), TEMP(7), TEMP(2)));
+ REQUIRE(mp_int_sub(TEMP(3), TEMP(6), TEMP(3)));
+ }
+
+ s_qdiv(TEMP(2), 1);
+ s_qdiv(TEMP(3), 1);
+ }
+
+ if (mp_int_compare(TEMP(4), TEMP(5)) >= 0) {
+ REQUIRE(mp_int_sub(TEMP(4), TEMP(5), TEMP(4)));
+ REQUIRE(mp_int_sub(TEMP(0), TEMP(2), TEMP(0)));
+ REQUIRE(mp_int_sub(TEMP(1), TEMP(3), TEMP(1)));
+ } else {
+ REQUIRE(mp_int_sub(TEMP(5), TEMP(4), TEMP(5)));
+ REQUIRE(mp_int_sub(TEMP(2), TEMP(0), TEMP(2)));
+ REQUIRE(mp_int_sub(TEMP(3), TEMP(1), TEMP(3)));
+ }
+
+ if (CMPZ(TEMP(4)) == 0) {
+ if (x) REQUIRE(mp_int_copy(TEMP(2), x));
+ if (y) REQUIRE(mp_int_copy(TEMP(3), y));
+ if (c) {
+ if (!s_qmul(TEMP(5), k)) {
+ REQUIRE(MP_MEMORY);
+ }
+ REQUIRE(mp_int_copy(TEMP(5), c));
+ }
+
+ break;
+ }
+ }
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+mp_result mp_int_lcm(mp_int a, mp_int b, mp_int c) {
+ assert(a != NULL && b != NULL && c != NULL);
+
+ /* Since a * b = gcd(a, b) * lcm(a, b), we can compute
+ lcm(a, b) = (a / gcd(a, b)) * b.
+
+ This formulation insures everything works even if the input
+ variables share space.
+ */
+ DECLARE_TEMP(1);
+ REQUIRE(mp_int_gcd(a, b, TEMP(0)));
+ REQUIRE(mp_int_div(a, TEMP(0), TEMP(0), NULL));
+ REQUIRE(mp_int_mul(TEMP(0), b, TEMP(0)));
+ REQUIRE(mp_int_copy(TEMP(0), c));
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+bool mp_int_divisible_value(mp_int a, mp_small v) {
+ mp_small rem = 0;
+
+ if (mp_int_div_value(a, v, NULL, &rem) != MP_OK) {
+ return false;
+ }
+ return rem == 0;
+}
+
+int mp_int_is_pow2(mp_int z) {
+ assert(z != NULL);
+
+ return s_isp2(z);
+}
+
+/* Implementation of Newton's root finding method, based loosely on a patch
+ contributed by Hal Finkel <half@halssoftware.com>
+ modified by M. J. Fromberger.
+ */
+mp_result mp_int_root(mp_int a, mp_small b, mp_int c) {
+ assert(a != NULL && c != NULL && b > 0);
+
+ if (b == 1) {
+ return mp_int_copy(a, c);
+ }
+ bool flips = false;
+ if (MP_SIGN(a) == MP_NEG) {
+ if (b % 2 == 0) {
+ return MP_UNDEF; /* root does not exist for negative a with even b */
+ } else {
+ flips = true;
+ }
+ }
+
+ DECLARE_TEMP(5);
+ REQUIRE(mp_int_copy(a, TEMP(0)));
+ REQUIRE(mp_int_copy(a, TEMP(1)));
+ TEMP(0)->sign = MP_ZPOS;
+ TEMP(1)->sign = MP_ZPOS;
+
+ for (;;) {
+ REQUIRE(mp_int_expt(TEMP(1), b, TEMP(2)));
+
+ if (mp_int_compare_unsigned(TEMP(2), TEMP(0)) <= 0) break;
+
+ REQUIRE(mp_int_sub(TEMP(2), TEMP(0), TEMP(2)));
+ REQUIRE(mp_int_expt(TEMP(1), b - 1, TEMP(3)));
+ REQUIRE(mp_int_mul_value(TEMP(3), b, TEMP(3)));
+ REQUIRE(mp_int_div(TEMP(2), TEMP(3), TEMP(4), NULL));
+ REQUIRE(mp_int_sub(TEMP(1), TEMP(4), TEMP(4)));
+
+ if (mp_int_compare_unsigned(TEMP(1), TEMP(4)) == 0) {
+ REQUIRE(mp_int_sub_value(TEMP(4), 1, TEMP(4)));
+ }
+ REQUIRE(mp_int_copy(TEMP(4), TEMP(1)));
+ }
+
+ REQUIRE(mp_int_copy(TEMP(1), c));
+
+ /* If the original value of a was negative, flip the output sign. */
+ if (flips) (void)mp_int_neg(c, c); /* cannot fail */
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+mp_result mp_int_to_int(mp_int z, mp_small *out) {
+ assert(z != NULL);
+
+ /* Make sure the value is representable as a small integer */
+ mp_sign sz = MP_SIGN(z);
+ if ((sz == MP_ZPOS && mp_int_compare_value(z, MP_SMALL_MAX) > 0) ||
+ mp_int_compare_value(z, MP_SMALL_MIN) < 0) {
+ return MP_RANGE;
+ }
+
+ mp_usmall uz = MP_USED(z);
+ mp_digit *dz = MP_DIGITS(z) + uz - 1;
+ mp_small uv = 0;
+ while (uz > 0) {
+ uv <<= MP_DIGIT_BIT / 2;
+ uv = (uv << (MP_DIGIT_BIT / 2)) | *dz--;
+ --uz;
+ }
+
+ if (out) *out = (mp_small)((sz == MP_NEG) ? -uv : uv);
+
+ return MP_OK;
+}
+
+mp_result mp_int_to_uint(mp_int z, mp_usmall *out) {
+ assert(z != NULL);
+
+ /* Make sure the value is representable as an unsigned small integer */
+ mp_size sz = MP_SIGN(z);
+ if (sz == MP_NEG || mp_int_compare_uvalue(z, MP_USMALL_MAX) > 0) {
+ return MP_RANGE;
+ }
+
+ mp_size uz = MP_USED(z);
+ mp_digit *dz = MP_DIGITS(z) + uz - 1;
+ mp_usmall uv = 0;
+
+ while (uz > 0) {
+ uv <<= MP_DIGIT_BIT / 2;
+ uv = (uv << (MP_DIGIT_BIT / 2)) | *dz--;
+ --uz;
+ }
+
+ if (out) *out = uv;
+
+ return MP_OK;
+}
+
+mp_result mp_int_to_string(mp_int z, mp_size radix, char *str, int limit) {
+ assert(z != NULL && str != NULL && limit >= 2);
+ assert(radix >= MP_MIN_RADIX && radix <= MP_MAX_RADIX);
+
+ int cmp = 0;
+ if (CMPZ(z) == 0) {
+ *str++ = s_val2ch(0, 1);
+ } else {
+ mp_result res;
+ mpz_t tmp;
+ char *h, *t;
+
+ if ((res = mp_int_init_copy(&tmp, z)) != MP_OK) return res;
+
+ if (MP_SIGN(z) == MP_NEG) {
+ *str++ = '-';
+ --limit;
+ }
+ h = str;
+
+ /* Generate digits in reverse order until finished or limit reached */
+ for (/* */; limit > 0; --limit) {
+ mp_digit d;
+
+ if ((cmp = CMPZ(&tmp)) == 0) break;
+
+ d = s_ddiv(&tmp, (mp_digit)radix);
+ *str++ = s_val2ch(d, 1);
+ }
+ t = str - 1;
+
+ /* Put digits back in correct output order */
+ while (h < t) {
+ char tc = *h;
+ *h++ = *t;
+ *t-- = tc;
+ }
+
+ mp_int_clear(&tmp);
+ }
+
+ *str = '\0';
+ if (cmp == 0) {
+ return MP_OK;
+ } else {
+ return MP_TRUNC;
+ }
+}
+
+mp_result mp_int_string_len(mp_int z, mp_size radix) {
+ assert(z != NULL);
+ assert(radix >= MP_MIN_RADIX && radix <= MP_MAX_RADIX);
+
+ int len = s_outlen(z, radix) + 1; /* for terminator */
+
+ /* Allow for sign marker on negatives */
+ if (MP_SIGN(z) == MP_NEG) len += 1;
+
+ return len;
+}
+
+/* Read zero-terminated string into z */
+mp_result mp_int_read_string(mp_int z, mp_size radix, const char *str) {
+ return mp_int_read_cstring(z, radix, str, NULL);
+}
+
+mp_result mp_int_read_cstring(mp_int z, mp_size radix, const char *str,
+ char **end) {
+ assert(z != NULL && str != NULL);
+ assert(radix >= MP_MIN_RADIX && radix <= MP_MAX_RADIX);
+
+ /* Skip leading whitespace */
+ while (isspace((unsigned char)*str)) ++str;
+
+ /* Handle leading sign tag (+/-, positive default) */
+ switch (*str) {
+ case '-':
+ z->sign = MP_NEG;
+ ++str;
+ break;
+ case '+':
+ ++str; /* fallthrough */
+ default:
+ z->sign = MP_ZPOS;
+ break;
+ }
+
+ /* Skip leading zeroes */
+ int ch;
+ while ((ch = s_ch2val(*str, radix)) == 0) ++str;
+
+ /* Make sure there is enough space for the value */
+ if (!s_pad(z, s_inlen(strlen(str), radix))) return MP_MEMORY;
+
+ z->used = 1;
+ z->digits[0] = 0;
+
+ while (*str != '\0' && ((ch = s_ch2val(*str, radix)) >= 0)) {
+ s_dmul(z, (mp_digit)radix);
+ s_dadd(z, (mp_digit)ch);
+ ++str;
+ }
+
+ CLAMP(z);
+
+ /* Override sign for zero, even if negative specified. */
+ if (CMPZ(z) == 0) z->sign = MP_ZPOS;
+
+ if (end != NULL) *end = (char *)str;
+
+ /* Return a truncation error if the string has unprocessed characters
+ remaining, so the caller can tell if the whole string was done */
+ if (*str != '\0') {
+ return MP_TRUNC;
+ } else {
+ return MP_OK;
+ }
+}
+
+mp_result mp_int_count_bits(mp_int z) {
+ assert(z != NULL);
+
+ mp_size uz = MP_USED(z);
+ if (uz == 1 && z->digits[0] == 0) return 1;
+
+ --uz;
+ mp_size nbits = uz * MP_DIGIT_BIT;
+ mp_digit d = z->digits[uz];
+
+ while (d != 0) {
+ d >>= 1;
+ ++nbits;
+ }
+
+ return nbits;
+}
+
+mp_result mp_int_to_binary(mp_int z, unsigned char *buf, int limit) {
+ static const int PAD_FOR_2C = 1;
+
+ assert(z != NULL && buf != NULL);
+
+ int limpos = limit;
+ mp_result res = s_tobin(z, buf, &limpos, PAD_FOR_2C);
+
+ if (MP_SIGN(z) == MP_NEG) s_2comp(buf, limpos);
+
+ return res;
+}
+
+mp_result mp_int_read_binary(mp_int z, unsigned char *buf, int len) {
+ assert(z != NULL && buf != NULL && len > 0);
+
+ /* Figure out how many digits are needed to represent this value */
+ mp_size need = ((len * CHAR_BIT) + (MP_DIGIT_BIT - 1)) / MP_DIGIT_BIT;
+ if (!s_pad(z, need)) return MP_MEMORY;
+
+ mp_int_zero(z);
+
+ /* If the high-order bit is set, take the 2's complement before reading the
+ value (it will be restored afterward) */
+ if (buf[0] >> (CHAR_BIT - 1)) {
+ z->sign = MP_NEG;
+ s_2comp(buf, len);
+ }
+
+ mp_digit *dz = MP_DIGITS(z);
+ unsigned char *tmp = buf;
+ for (int i = len; i > 0; --i, ++tmp) {
+ s_qmul(z, (mp_size)CHAR_BIT);
+ *dz |= *tmp;
+ }
+
+ /* Restore 2's complement if we took it before */
+ if (MP_SIGN(z) == MP_NEG) s_2comp(buf, len);
+
+ return MP_OK;
+}
+
+mp_result mp_int_binary_len(mp_int z) {
+ mp_result res = mp_int_count_bits(z);
+ if (res <= 0) return res;
+
+ int bytes = mp_int_unsigned_len(z);
+
+ /* If the highest-order bit falls exactly on a byte boundary, we need to pad
+ with an extra byte so that the sign will be read correctly when reading it
+ back in. */
+ if (bytes * CHAR_BIT == res) ++bytes;
+
+ return bytes;
+}
+
+mp_result mp_int_to_unsigned(mp_int z, unsigned char *buf, int limit) {
+ static const int NO_PADDING = 0;
+
+ assert(z != NULL && buf != NULL);
+
+ return s_tobin(z, buf, &limit, NO_PADDING);
+}
+
+mp_result mp_int_read_unsigned(mp_int z, unsigned char *buf, int len) {
+ assert(z != NULL && buf != NULL && len > 0);
+
+ /* Figure out how many digits are needed to represent this value */
+ mp_size need = ((len * CHAR_BIT) + (MP_DIGIT_BIT - 1)) / MP_DIGIT_BIT;
+ if (!s_pad(z, need)) return MP_MEMORY;
+
+ mp_int_zero(z);
+
+ unsigned char *tmp = buf;
+ for (int i = len; i > 0; --i, ++tmp) {
+ (void)s_qmul(z, CHAR_BIT);
+ *MP_DIGITS(z) |= *tmp;
+ }
+
+ return MP_OK;
+}
+
+mp_result mp_int_unsigned_len(mp_int z) {
+ mp_result res = mp_int_count_bits(z);
+ if (res <= 0) return res;
+
+ int bytes = (res + (CHAR_BIT - 1)) / CHAR_BIT;
+ return bytes;
+}
+
+const char *mp_error_string(mp_result res) {
+ if (res > 0) return s_unknown_err;
+
+ res = -res;
+ int ix;
+ for (ix = 0; ix < res && s_error_msg[ix] != NULL; ++ix)
+ ;
+
+ if (s_error_msg[ix] != NULL) {
+ return s_error_msg[ix];
+ } else {
+ return s_unknown_err;
+ }
+}
+
+/*------------------------------------------------------------------------*/
+/* Private functions for internal use. These make assumptions. */
+
+#if DEBUG
+static const mp_digit fill = (mp_digit)0xdeadbeefabad1dea;
+#endif
+
+static mp_digit *s_alloc(mp_size num) {
+ mp_digit *out = malloc(num * sizeof(mp_digit));
+ assert(out != NULL);
+
+#if DEBUG
+ for (mp_size ix = 0; ix < num; ++ix) out[ix] = fill;
+#endif
+ return out;
+}
+
+static mp_digit *s_realloc(mp_digit *old, mp_size osize, mp_size nsize) {
+#if DEBUG
+ mp_digit *new = s_alloc(nsize);
+ assert(new != NULL);
+
+ for (mp_size ix = 0; ix < nsize; ++ix) new[ix] = fill;
+ memcpy(new, old, osize * sizeof(mp_digit));
+#else
+ mp_digit *new = realloc(old, nsize * sizeof(mp_digit));
+ assert(new != NULL);
+#endif
+
+ return new;
+}
+
+static void s_free(void *ptr) { free(ptr); }
+
+static bool s_pad(mp_int z, mp_size min) {
+ if (MP_ALLOC(z) < min) {
+ mp_size nsize = s_round_prec(min);
+ mp_digit *tmp;
+
+ if (z->digits == &(z->single)) {
+ if ((tmp = s_alloc(nsize)) == NULL) return false;
+ tmp[0] = z->single;
+ } else if ((tmp = s_realloc(MP_DIGITS(z), MP_ALLOC(z), nsize)) == NULL) {
+ return false;
+ }
+
+ z->digits = tmp;
+ z->alloc = nsize;
+ }
+
+ return true;
+}
+
+/* Note: This will not work correctly when value == MP_SMALL_MIN */
+static void s_fake(mp_int z, mp_small value, mp_digit vbuf[]) {
+ mp_usmall uv = (mp_usmall)(value < 0) ? -value : value;
+ s_ufake(z, uv, vbuf);
+ if (value < 0) z->sign = MP_NEG;
+}
+
+static void s_ufake(mp_int z, mp_usmall value, mp_digit vbuf[]) {
+ mp_size ndig = (mp_size)s_uvpack(value, vbuf);
+
+ z->used = ndig;
+ z->alloc = MP_VALUE_DIGITS(value);
+ z->sign = MP_ZPOS;
+ z->digits = vbuf;
+}
+
+static int s_cdig(mp_digit *da, mp_digit *db, mp_size len) {
+ mp_digit *dat = da + len - 1, *dbt = db + len - 1;
+
+ for (/* */; len != 0; --len, --dat, --dbt) {
+ if (*dat > *dbt) {
+ return 1;
+ } else if (*dat < *dbt) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int s_uvpack(mp_usmall uv, mp_digit t[]) {
+ int ndig = 0;
+
+ if (uv == 0)
+ t[ndig++] = 0;
+ else {
+ while (uv != 0) {
+ t[ndig++] = (mp_digit)uv;
+ uv >>= MP_DIGIT_BIT / 2;
+ uv >>= MP_DIGIT_BIT / 2;
+ }
+ }
+
+ return ndig;
+}
+
+static int s_ucmp(mp_int a, mp_int b) {
+ mp_size ua = MP_USED(a), ub = MP_USED(b);
+
+ if (ua > ub) {
+ return 1;
+ } else if (ub > ua) {
+ return -1;
+ } else {
+ return s_cdig(MP_DIGITS(a), MP_DIGITS(b), ua);
+ }
+}
+
+static int s_vcmp(mp_int a, mp_small v) {
+ mp_usmall uv = (v < 0) ? -(mp_usmall)v : (mp_usmall)v;
+ return s_uvcmp(a, uv);
+}
+
+static int s_uvcmp(mp_int a, mp_usmall uv) {
+ mpz_t vtmp;
+ mp_digit vdig[MP_VALUE_DIGITS(uv)];
+
+ s_ufake(&vtmp, uv, vdig);
+ return s_ucmp(a, &vtmp);
+}
+
+static mp_digit s_uadd(mp_digit *da, mp_digit *db, mp_digit *dc, mp_size size_a,
+ mp_size size_b) {
+ mp_size pos;
+ mp_word w = 0;
+
+ /* Insure that da is the longer of the two to simplify later code */
+ if (size_b > size_a) {
+ SWAP(mp_digit *, da, db);
+ SWAP(mp_size, size_a, size_b);
+ }
+
+ /* Add corresponding digits until the shorter number runs out */
+ for (pos = 0; pos < size_b; ++pos, ++da, ++db, ++dc) {
+ w = w + (mp_word)*da + (mp_word)*db;
+ *dc = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ }
+
+ /* Propagate carries as far as necessary */
+ for (/* */; pos < size_a; ++pos, ++da, ++dc) {
+ w = w + *da;
+
+ *dc = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ }
+
+ /* Return carry out */
+ return (mp_digit)w;
+}
+
+static void s_usub(mp_digit *da, mp_digit *db, mp_digit *dc, mp_size size_a,
+ mp_size size_b) {
+ mp_size pos;
+ mp_word w = 0;
+
+ /* We assume that |a| >= |b| so this should definitely hold */
+ assert(size_a >= size_b);
+
+ /* Subtract corresponding digits and propagate borrow */
+ for (pos = 0; pos < size_b; ++pos, ++da, ++db, ++dc) {
+ w = ((mp_word)MP_DIGIT_MAX + 1 + /* MP_RADIX */
+ (mp_word)*da) -
+ w - (mp_word)*db;
+
+ *dc = LOWER_HALF(w);
+ w = (UPPER_HALF(w) == 0);
+ }
+
+ /* Finish the subtraction for remaining upper digits of da */
+ for (/* */; pos < size_a; ++pos, ++da, ++dc) {
+ w = ((mp_word)MP_DIGIT_MAX + 1 + /* MP_RADIX */
+ (mp_word)*da) -
+ w;
+
+ *dc = LOWER_HALF(w);
+ w = (UPPER_HALF(w) == 0);
+ }
+
+ /* If there is a borrow out at the end, it violates the precondition */
+ assert(w == 0);
+}
+
+static int s_kmul(mp_digit *da, mp_digit *db, mp_digit *dc, mp_size size_a,
+ mp_size size_b) {
+ mp_size bot_size;
+
+ /* Make sure b is the smaller of the two input values */
+ if (size_b > size_a) {
+ SWAP(mp_digit *, da, db);
+ SWAP(mp_size, size_a, size_b);
+ }
+
+ /* Insure that the bottom is the larger half in an odd-length split; the code
+ below relies on this being true.
+ */
+ bot_size = (size_a + 1) / 2;
+
+ /* If the values are big enough to bother with recursion, use the Karatsuba
+ algorithm to compute the product; otherwise use the normal multiplication
+ algorithm
+ */
+ if (multiply_threshold && size_a >= multiply_threshold && size_b > bot_size) {
+ mp_digit *t1, *t2, *t3, carry;
+
+ mp_digit *a_top = da + bot_size;
+ mp_digit *b_top = db + bot_size;
+
+ mp_size at_size = size_a - bot_size;
+ mp_size bt_size = size_b - bot_size;
+ mp_size buf_size = 2 * bot_size;
+
+ /* Do a single allocation for all three temporary buffers needed; each
+ buffer must be big enough to hold the product of two bottom halves, and
+ one buffer needs space for the completed product; twice the space is
+ plenty.
+ */
+ if ((t1 = s_alloc(4 * buf_size)) == NULL) return 0;
+ t2 = t1 + buf_size;
+ t3 = t2 + buf_size;
+ ZERO(t1, 4 * buf_size);
+
+ /* t1 and t2 are initially used as temporaries to compute the inner product
+ (a1 + a0)(b1 + b0) = a1b1 + a1b0 + a0b1 + a0b0
+ */
+ carry = s_uadd(da, a_top, t1, bot_size, at_size); /* t1 = a1 + a0 */
+ t1[bot_size] = carry;
+
+ carry = s_uadd(db, b_top, t2, bot_size, bt_size); /* t2 = b1 + b0 */
+ t2[bot_size] = carry;
+
+ (void)s_kmul(t1, t2, t3, bot_size + 1, bot_size + 1); /* t3 = t1 * t2 */
+
+ /* Now we'll get t1 = a0b0 and t2 = a1b1, and subtract them out so that
+ we're left with only the pieces we want: t3 = a1b0 + a0b1
+ */
+ ZERO(t1, buf_size);
+ ZERO(t2, buf_size);
+ (void)s_kmul(da, db, t1, bot_size, bot_size); /* t1 = a0 * b0 */
+ (void)s_kmul(a_top, b_top, t2, at_size, bt_size); /* t2 = a1 * b1 */
+
+ /* Subtract out t1 and t2 to get the inner product */
+ s_usub(t3, t1, t3, buf_size + 2, buf_size);
+ s_usub(t3, t2, t3, buf_size + 2, buf_size);
+
+ /* Assemble the output value */
+ COPY(t1, dc, buf_size);
+ carry = s_uadd(t3, dc + bot_size, dc + bot_size, buf_size + 1, buf_size);
+ assert(carry == 0);
+
+ carry =
+ s_uadd(t2, dc + 2 * bot_size, dc + 2 * bot_size, buf_size, buf_size);
+ assert(carry == 0);
+
+ s_free(t1); /* note t2 and t3 are just internal pointers to t1 */
+ } else {
+ s_umul(da, db, dc, size_a, size_b);
+ }
+
+ return 1;
+}
+
+static void s_umul(mp_digit *da, mp_digit *db, mp_digit *dc, mp_size size_a,
+ mp_size size_b) {
+ mp_size a, b;
+ mp_word w;
+
+ for (a = 0; a < size_a; ++a, ++dc, ++da) {
+ mp_digit *dct = dc;
+ mp_digit *dbt = db;
+
+ if (*da == 0) continue;
+
+ w = 0;
+ for (b = 0; b < size_b; ++b, ++dbt, ++dct) {
+ w = (mp_word)*da * (mp_word)*dbt + w + (mp_word)*dct;
+
+ *dct = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ }
+
+ *dct = (mp_digit)w;
+ }
+}
+
+static int s_ksqr(mp_digit *da, mp_digit *dc, mp_size size_a) {
+ if (multiply_threshold && size_a > multiply_threshold) {
+ mp_size bot_size = (size_a + 1) / 2;
+ mp_digit *a_top = da + bot_size;
+ mp_digit *t1, *t2, *t3, carry;
+ mp_size at_size = size_a - bot_size;
+ mp_size buf_size = 2 * bot_size;
+
+ if ((t1 = s_alloc(4 * buf_size)) == NULL) return 0;
+ t2 = t1 + buf_size;
+ t3 = t2 + buf_size;
+ ZERO(t1, 4 * buf_size);
+
+ (void)s_ksqr(da, t1, bot_size); /* t1 = a0 ^ 2 */
+ (void)s_ksqr(a_top, t2, at_size); /* t2 = a1 ^ 2 */
+
+ (void)s_kmul(da, a_top, t3, bot_size, at_size); /* t3 = a0 * a1 */
+
+ /* Quick multiply t3 by 2, shifting left (can't overflow) */
+ {
+ int i, top = bot_size + at_size;
+ mp_word w, save = 0;
+
+ for (i = 0; i < top; ++i) {
+ w = t3[i];
+ w = (w << 1) | save;
+ t3[i] = LOWER_HALF(w);
+ save = UPPER_HALF(w);
+ }
+ t3[i] = LOWER_HALF(save);
+ }
+
+ /* Assemble the output value */
+ COPY(t1, dc, 2 * bot_size);
+ carry = s_uadd(t3, dc + bot_size, dc + bot_size, buf_size + 1, buf_size);
+ assert(carry == 0);
+
+ carry =
+ s_uadd(t2, dc + 2 * bot_size, dc + 2 * bot_size, buf_size, buf_size);
+ assert(carry == 0);
+
+ s_free(t1); /* note that t2 and t2 are internal pointers only */
+
+ } else {
+ s_usqr(da, dc, size_a);
+ }
+
+ return 1;
+}
+
+static void s_usqr(mp_digit *da, mp_digit *dc, mp_size size_a) {
+ mp_size i, j;
+ mp_word w;
+
+ for (i = 0; i < size_a; ++i, dc += 2, ++da) {
+ mp_digit *dct = dc, *dat = da;
+
+ if (*da == 0) continue;
+
+ /* Take care of the first digit, no rollover */
+ w = (mp_word)*dat * (mp_word)*dat + (mp_word)*dct;
+ *dct = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ ++dat;
+ ++dct;
+
+ for (j = i + 1; j < size_a; ++j, ++dat, ++dct) {
+ mp_word t = (mp_word)*da * (mp_word)*dat;
+ mp_word u = w + (mp_word)*dct, ov = 0;
+
+ /* Check if doubling t will overflow a word */
+ if (HIGH_BIT_SET(t)) ov = 1;
+
+ w = t + t;
+
+ /* Check if adding u to w will overflow a word */
+ if (ADD_WILL_OVERFLOW(w, u)) ov = 1;
+
+ w += u;
+
+ *dct = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ if (ov) {
+ w += MP_DIGIT_MAX; /* MP_RADIX */
+ ++w;
+ }
+ }
+
+ w = w + *dct;
+ *dct = (mp_digit)w;
+ while ((w = UPPER_HALF(w)) != 0) {
+ ++dct;
+ w = w + *dct;
+ *dct = LOWER_HALF(w);
+ }
+
+ assert(w == 0);
+ }
+}
+
+static void s_dadd(mp_int a, mp_digit b) {
+ mp_word w = 0;
+ mp_digit *da = MP_DIGITS(a);
+ mp_size ua = MP_USED(a);
+
+ w = (mp_word)*da + b;
+ *da++ = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+
+ for (ua -= 1; ua > 0; --ua, ++da) {
+ w = (mp_word)*da + w;
+
+ *da = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ }
+
+ if (w) {
+ *da = (mp_digit)w;
+ a->used += 1;
+ }
+}
+
+static void s_dmul(mp_int a, mp_digit b) {
+ mp_word w = 0;
+ mp_digit *da = MP_DIGITS(a);
+ mp_size ua = MP_USED(a);
+
+ while (ua > 0) {
+ w = (mp_word)*da * b + w;
+ *da++ = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ --ua;
+ }
+
+ if (w) {
+ *da = (mp_digit)w;
+ a->used += 1;
+ }
+}
+
+static void s_dbmul(mp_digit *da, mp_digit b, mp_digit *dc, mp_size size_a) {
+ mp_word w = 0;
+
+ while (size_a > 0) {
+ w = (mp_word)*da++ * (mp_word)b + w;
+
+ *dc++ = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ --size_a;
+ }
+
+ if (w) *dc = LOWER_HALF(w);
+}
+
+static mp_digit s_ddiv(mp_int a, mp_digit b) {
+ mp_word w = 0, qdigit;
+ mp_size ua = MP_USED(a);
+ mp_digit *da = MP_DIGITS(a) + ua - 1;
+
+ for (/* */; ua > 0; --ua, --da) {
+ w = (w << MP_DIGIT_BIT) | *da;
+
+ if (w >= b) {
+ qdigit = w / b;
+ w = w % b;
+ } else {
+ qdigit = 0;
+ }
+
+ *da = (mp_digit)qdigit;
+ }
+
+ CLAMP(a);
+ return (mp_digit)w;
+}
+
+static void s_qdiv(mp_int z, mp_size p2) {
+ mp_size ndig = p2 / MP_DIGIT_BIT, nbits = p2 % MP_DIGIT_BIT;
+ mp_size uz = MP_USED(z);
+
+ if (ndig) {
+ mp_size mark;
+ mp_digit *to, *from;
+
+ if (ndig >= uz) {
+ mp_int_zero(z);
+ return;
+ }
+
+ to = MP_DIGITS(z);
+ from = to + ndig;
+
+ for (mark = ndig; mark < uz; ++mark) {
+ *to++ = *from++;
+ }
+
+ z->used = uz - ndig;
+ }
+
+ if (nbits) {
+ mp_digit d = 0, *dz, save;
+ mp_size up = MP_DIGIT_BIT - nbits;
+
+ uz = MP_USED(z);
+ dz = MP_DIGITS(z) + uz - 1;
+
+ for (/* */; uz > 0; --uz, --dz) {
+ save = *dz;
+
+ *dz = (*dz >> nbits) | (d << up);
+ d = save;
+ }
+
+ CLAMP(z);
+ }
+
+ if (MP_USED(z) == 1 && z->digits[0] == 0) z->sign = MP_ZPOS;
+}
+
+static void s_qmod(mp_int z, mp_size p2) {
+ mp_size start = p2 / MP_DIGIT_BIT + 1, rest = p2 % MP_DIGIT_BIT;
+ mp_size uz = MP_USED(z);
+ mp_digit mask = (1u << rest) - 1;
+
+ if (start <= uz) {
+ z->used = start;
+ z->digits[start - 1] &= mask;
+ CLAMP(z);
+ }
+}
+
+static int s_qmul(mp_int z, mp_size p2) {
+ mp_size uz, need, rest, extra, i;
+ mp_digit *from, *to, d;
+
+ if (p2 == 0) return 1;
+
+ uz = MP_USED(z);
+ need = p2 / MP_DIGIT_BIT;
+ rest = p2 % MP_DIGIT_BIT;
+
+ /* Figure out if we need an extra digit at the top end; this occurs if the
+ topmost `rest' bits of the high-order digit of z are not zero, meaning
+ they will be shifted off the end if not preserved */
+ extra = 0;
+ if (rest != 0) {
+ mp_digit *dz = MP_DIGITS(z) + uz - 1;
+
+ if ((*dz >> (MP_DIGIT_BIT - rest)) != 0) extra = 1;
+ }
+
+ if (!s_pad(z, uz + need + extra)) return 0;
+
+ /* If we need to shift by whole digits, do that in one pass, then
+ to back and shift by partial digits.
+ */
+ if (need > 0) {
+ from = MP_DIGITS(z) + uz - 1;
+ to = from + need;
+
+ for (i = 0; i < uz; ++i) *to-- = *from--;
+
+ ZERO(MP_DIGITS(z), need);
+ uz += need;
+ }
+
+ if (rest) {
+ d = 0;
+ for (i = need, from = MP_DIGITS(z) + need; i < uz; ++i, ++from) {
+ mp_digit save = *from;
+
+ *from = (*from << rest) | (d >> (MP_DIGIT_BIT - rest));
+ d = save;
+ }
+
+ d >>= (MP_DIGIT_BIT - rest);
+ if (d != 0) {
+ *from = d;
+ uz += extra;
+ }
+ }
+
+ z->used = uz;
+ CLAMP(z);
+
+ return 1;
+}
+
+/* Compute z = 2^p2 - |z|; requires that 2^p2 >= |z|
+ The sign of the result is always zero/positive.
+ */
+static int s_qsub(mp_int z, mp_size p2) {
+ mp_digit hi = (1u << (p2 % MP_DIGIT_BIT)), *zp;
+ mp_size tdig = (p2 / MP_DIGIT_BIT), pos;
+ mp_word w = 0;
+
+ if (!s_pad(z, tdig + 1)) return 0;
+
+ for (pos = 0, zp = MP_DIGITS(z); pos < tdig; ++pos, ++zp) {
+ w = ((mp_word)MP_DIGIT_MAX + 1) - w - (mp_word)*zp;
+
+ *zp = LOWER_HALF(w);
+ w = UPPER_HALF(w) ? 0 : 1;
+ }
+
+ w = ((mp_word)MP_DIGIT_MAX + 1 + hi) - w - (mp_word)*zp;
+ *zp = LOWER_HALF(w);
+
+ assert(UPPER_HALF(w) != 0); /* no borrow out should be possible */
+
+ z->sign = MP_ZPOS;
+ CLAMP(z);
+
+ return 1;
+}
+
+static int s_dp2k(mp_int z) {
+ int k = 0;
+ mp_digit *dp = MP_DIGITS(z), d;
+
+ if (MP_USED(z) == 1 && *dp == 0) return 1;
+
+ while (*dp == 0) {
+ k += MP_DIGIT_BIT;
+ ++dp;
+ }
+
+ d = *dp;
+ while ((d & 1) == 0) {
+ d >>= 1;
+ ++k;
+ }
+
+ return k;
+}
+
+static int s_isp2(mp_int z) {
+ mp_size uz = MP_USED(z), k = 0;
+ mp_digit *dz = MP_DIGITS(z), d;
+
+ while (uz > 1) {
+ if (*dz++ != 0) return -1;
+ k += MP_DIGIT_BIT;
+ --uz;
+ }
+
+ d = *dz;
+ while (d > 1) {
+ if (d & 1) return -1;
+ ++k;
+ d >>= 1;
+ }
+
+ return (int)k;
+}
+
+static int s_2expt(mp_int z, mp_small k) {
+ mp_size ndig, rest;
+ mp_digit *dz;
+
+ ndig = (k + MP_DIGIT_BIT) / MP_DIGIT_BIT;
+ rest = k % MP_DIGIT_BIT;
+
+ if (!s_pad(z, ndig)) return 0;
+
+ dz = MP_DIGITS(z);
+ ZERO(dz, ndig);
+ *(dz + ndig - 1) = (1u << rest);
+ z->used = ndig;
+
+ return 1;
+}
+
+static int s_norm(mp_int a, mp_int b) {
+ mp_digit d = b->digits[MP_USED(b) - 1];
+ int k = 0;
+
+ while (d < (1u << (mp_digit)(MP_DIGIT_BIT - 1))) { /* d < (MP_RADIX / 2) */
+ d <<= 1;
+ ++k;
+ }
+
+ /* These multiplications can't fail */
+ if (k != 0) {
+ (void)s_qmul(a, (mp_size)k);
+ (void)s_qmul(b, (mp_size)k);
+ }
+
+ return k;
+}
+
+static mp_result s_brmu(mp_int z, mp_int m) {
+ mp_size um = MP_USED(m) * 2;
+
+ if (!s_pad(z, um)) return MP_MEMORY;
+
+ s_2expt(z, MP_DIGIT_BIT * um);
+ return mp_int_div(z, m, z, NULL);
+}
+
+static int s_reduce(mp_int x, mp_int m, mp_int mu, mp_int q1, mp_int q2) {
+ mp_size um = MP_USED(m), umb_p1, umb_m1;
+
+ umb_p1 = (um + 1) * MP_DIGIT_BIT;
+ umb_m1 = (um - 1) * MP_DIGIT_BIT;
+
+ if (mp_int_copy(x, q1) != MP_OK) return 0;
+
+ /* Compute q2 = floor((floor(x / b^(k-1)) * mu) / b^(k+1)) */
+ s_qdiv(q1, umb_m1);
+ UMUL(q1, mu, q2);
+ s_qdiv(q2, umb_p1);
+
+ /* Set x = x mod b^(k+1) */
+ s_qmod(x, umb_p1);
+
+ /* Now, q is a guess for the quotient a / m.
+ Compute x - q * m mod b^(k+1), replacing x. This may be off
+ by a factor of 2m, but no more than that.
+ */
+ UMUL(q2, m, q1);
+ s_qmod(q1, umb_p1);
+ (void)mp_int_sub(x, q1, x); /* can't fail */
+
+ /* The result may be < 0; if it is, add b^(k+1) to pin it in the proper
+ range. */
+ if ((CMPZ(x) < 0) && !s_qsub(x, umb_p1)) return 0;
+
+ /* If x > m, we need to back it off until it is in range. This will be
+ required at most twice. */
+ if (mp_int_compare(x, m) >= 0) {
+ (void)mp_int_sub(x, m, x);
+ if (mp_int_compare(x, m) >= 0) {
+ (void)mp_int_sub(x, m, x);
+ }
+ }
+
+ /* At this point, x has been properly reduced. */
+ return 1;
+}
+
+/* Perform modular exponentiation using Barrett's method, where mu is the
+ reduction constant for m. Assumes a < m, b > 0. */
+static mp_result s_embar(mp_int a, mp_int b, mp_int m, mp_int mu, mp_int c) {
+ mp_digit umu = MP_USED(mu);
+ mp_digit *db = MP_DIGITS(b);
+ mp_digit *dbt = db + MP_USED(b) - 1;
+
+ DECLARE_TEMP(3);
+ REQUIRE(GROW(TEMP(0), 4 * umu));
+ REQUIRE(GROW(TEMP(1), 4 * umu));
+ REQUIRE(GROW(TEMP(2), 4 * umu));
+ ZERO(TEMP(0)->digits, TEMP(0)->alloc);
+ ZERO(TEMP(1)->digits, TEMP(1)->alloc);
+ ZERO(TEMP(2)->digits, TEMP(2)->alloc);
+
+ (void)mp_int_set_value(c, 1);
+
+ /* Take care of low-order digits */
+ while (db < dbt) {
+ mp_digit d = *db;
+
+ for (int i = MP_DIGIT_BIT; i > 0; --i, d >>= 1) {
+ if (d & 1) {
+ /* The use of a second temporary avoids allocation */
+ UMUL(c, a, TEMP(0));
+ if (!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) {
+ REQUIRE(MP_MEMORY);
+ }
+ mp_int_copy(TEMP(0), c);
+ }
+
+ USQR(a, TEMP(0));
+ assert(MP_SIGN(TEMP(0)) == MP_ZPOS);
+ if (!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) {
+ REQUIRE(MP_MEMORY);
+ }
+ assert(MP_SIGN(TEMP(0)) == MP_ZPOS);
+ mp_int_copy(TEMP(0), a);
+ }
+
+ ++db;
+ }
+
+ /* Take care of highest-order digit */
+ mp_digit d = *dbt;
+ for (;;) {
+ if (d & 1) {
+ UMUL(c, a, TEMP(0));
+ if (!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) {
+ REQUIRE(MP_MEMORY);
+ }
+ mp_int_copy(TEMP(0), c);
+ }
+
+ d >>= 1;
+ if (!d) break;
+
+ USQR(a, TEMP(0));
+ if (!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) {
+ REQUIRE(MP_MEMORY);
+ }
+ (void)mp_int_copy(TEMP(0), a);
+ }
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+/* Division of nonnegative integers
+
+ This function implements division algorithm for unsigned multi-precision
+ integers. The algorithm is based on Algorithm D from Knuth's "The Art of
+ Computer Programming", 3rd ed. 1998, pg 272-273.
+
+ We diverge from Knuth's algorithm in that we do not perform the subtraction
+ from the remainder until we have determined that we have the correct
+ quotient digit. This makes our algorithm less efficient that Knuth because
+ we might have to perform multiple multiplication and comparison steps before
+ the subtraction. The advantage is that it is easy to implement and ensure
+ correctness without worrying about underflow from the subtraction.
+
+ inputs: u a n+m digit integer in base b (b is 2^MP_DIGIT_BIT)
+ v a n digit integer in base b (b is 2^MP_DIGIT_BIT)
+ n >= 1
+ m >= 0
+ outputs: u / v stored in u
+ u % v stored in v
+ */
+static mp_result s_udiv_knuth(mp_int u, mp_int v) {
+ /* Force signs to positive */
+ u->sign = MP_ZPOS;
+ v->sign = MP_ZPOS;
+
+ /* Use simple division algorithm when v is only one digit long */
+ if (MP_USED(v) == 1) {
+ mp_digit d, rem;
+ d = v->digits[0];
+ rem = s_ddiv(u, d);
+ mp_int_set_value(v, rem);
+ return MP_OK;
+ }
+
+ /* Algorithm D
+
+ The n and m variables are defined as used by Knuth.
+ u is an n digit number with digits u_{n-1}..u_0.
+ v is an n+m digit number with digits from v_{m+n-1}..v_0.
+ We require that n > 1 and m >= 0
+ */
+ mp_size n = MP_USED(v);
+ mp_size m = MP_USED(u) - n;
+ assert(n > 1);
+ /* assert(m >= 0) follows because m is unsigned. */
+
+ /* D1: Normalize.
+ The normalization step provides the necessary condition for Theorem B,
+ which states that the quotient estimate for q_j, call it qhat
+
+ qhat = u_{j+n}u_{j+n-1} / v_{n-1}
+
+ is bounded by
+
+ qhat - 2 <= q_j <= qhat.
+
+ That is, qhat is always greater than the actual quotient digit q,
+ and it is never more than two larger than the actual quotient digit.
+ */
+ int k = s_norm(u, v);
+
+ /* Extend size of u by one if needed.
+
+ The algorithm begins with a value of u that has one more digit of input.
+ The normalization step sets u_{m+n}..u_0 = 2^k * u_{m+n-1}..u_0. If the
+ multiplication did not increase the number of digits of u, we need to add
+ a leading zero here.
+ */
+ if (k == 0 || MP_USED(u) != m + n + 1) {
+ if (!s_pad(u, m + n + 1)) return MP_MEMORY;
+ u->digits[m + n] = 0;
+ u->used = m + n + 1;
+ }
+
+ /* Add a leading 0 to v.
+
+ The multiplication in step D4 multiplies qhat * 0v_{n-1}..v_0. We need to
+ add the leading zero to v here to ensure that the multiplication will
+ produce the full n+1 digit result.
+ */
+ if (!s_pad(v, n + 1)) return MP_MEMORY;
+ v->digits[n] = 0;
+
+ /* Initialize temporary variables q and t.
+ q allocates space for m+1 digits to store the quotient digits
+ t allocates space for n+1 digits to hold the result of q_j*v
+ */
+ DECLARE_TEMP(2);
+ REQUIRE(GROW(TEMP(0), m + 1));
+ REQUIRE(GROW(TEMP(1), n + 1));
+
+ /* D2: Initialize j */
+ int j = m;
+ mpz_t r;
+ r.digits = MP_DIGITS(u) + j; /* The contents of r are shared with u */
+ r.used = n + 1;
+ r.sign = MP_ZPOS;
+ r.alloc = MP_ALLOC(u);
+ ZERO(TEMP(1)->digits, TEMP(1)->alloc);
+
+ /* Calculate the m+1 digits of the quotient result */
+ for (; j >= 0; j--) {
+ /* D3: Calculate q' */
+ /* r->digits is aligned to position j of the number u */
+ mp_word pfx, qhat;
+ pfx = r.digits[n];
+ pfx <<= MP_DIGIT_BIT / 2;
+ pfx <<= MP_DIGIT_BIT / 2;
+ pfx |= r.digits[n - 1]; /* pfx = u_{j+n}{j+n-1} */
+
+ qhat = pfx / v->digits[n - 1];
+ /* Check to see if qhat > b, and decrease qhat if so.
+ Theorem B guarantess that qhat is at most 2 larger than the
+ actual value, so it is possible that qhat is greater than
+ the maximum value that will fit in a digit */
+ if (qhat > MP_DIGIT_MAX) qhat = MP_DIGIT_MAX;
+
+ /* D4,D5,D6: Multiply qhat * v and test for a correct value of q
+
+ We proceed a bit different than the way described by Knuth. This way is
+ simpler but less efficent. Instead of doing the multiply and subtract
+ then checking for underflow, we first do the multiply of qhat * v and
+ see if it is larger than the current remainder r. If it is larger, we
+ decrease qhat by one and try again. We may need to decrease qhat one
+ more time before we get a value that is smaller than r.
+
+ This way is less efficent than Knuth because we do more multiplies, but
+ we do not need to worry about underflow this way.
+ */
+ /* t = qhat * v */
+ s_dbmul(MP_DIGITS(v), (mp_digit)qhat, TEMP(1)->digits, n + 1);
+ TEMP(1)->used = n + 1;
+ CLAMP(TEMP(1));
+
+ /* Clamp r for the comparison. Comparisons do not like leading zeros. */
+ CLAMP(&r);
+ if (s_ucmp(TEMP(1), &r) > 0) { /* would the remainder be negative? */
+ qhat -= 1; /* try a smaller q */
+ s_dbmul(MP_DIGITS(v), (mp_digit)qhat, TEMP(1)->digits, n + 1);
+ TEMP(1)->used = n + 1;
+ CLAMP(TEMP(1));
+ if (s_ucmp(TEMP(1), &r) > 0) { /* would the remainder be negative? */
+ assert(qhat > 0);
+ qhat -= 1; /* try a smaller q */
+ s_dbmul(MP_DIGITS(v), (mp_digit)qhat, TEMP(1)->digits, n + 1);
+ TEMP(1)->used = n + 1;
+ CLAMP(TEMP(1));
+ }
+ assert(s_ucmp(TEMP(1), &r) <= 0 && "The mathematics failed us.");
+ }
+ /* Unclamp r. The D algorithm expects r = u_{j+n}..u_j to always be n+1
+ digits long. */
+ r.used = n + 1;
+
+ /* D4: Multiply and subtract
+
+ Note: The multiply was completed above so we only need to subtract here.
+ */
+ s_usub(r.digits, TEMP(1)->digits, r.digits, r.used, TEMP(1)->used);
+
+ /* D5: Test remainder
+
+ Note: Not needed because we always check that qhat is the correct value
+ before performing the subtract. Value cast to mp_digit to prevent
+ warning, qhat has been clamped to MP_DIGIT_MAX
+ */
+ TEMP(0)->digits[j] = (mp_digit)qhat;
+
+ /* D6: Add back
+ Note: Not needed because we always check that qhat is the correct value
+ before performing the subtract.
+ */
+
+ /* D7: Loop on j */
+ r.digits--;
+ ZERO(TEMP(1)->digits, TEMP(1)->alloc);
+ }
+
+ /* Get rid of leading zeros in q */
+ TEMP(0)->used = m + 1;
+ CLAMP(TEMP(0));
+
+ /* Denormalize the remainder */
+ CLAMP(u); /* use u here because the r.digits pointer is off-by-one */
+ if (k != 0) s_qdiv(u, k);
+
+ mp_int_copy(u, v); /* ok: 0 <= r < v */
+ mp_int_copy(TEMP(0), u); /* ok: q <= u */
+
+ CLEANUP_TEMP();
+ return MP_OK;
+}
+
+static int s_outlen(mp_int z, mp_size r) {
+ assert(r >= MP_MIN_RADIX && r <= MP_MAX_RADIX);
+
+ mp_result bits = mp_int_count_bits(z);
+ double raw = (double)bits * s_log2[r];
+
+ return (int)(raw + 0.999999);
+}
+
+static mp_size s_inlen(int len, mp_size r) {
+ double raw = (double)len / s_log2[r];
+ mp_size bits = (mp_size)(raw + 0.5);
+
+ return (mp_size)((bits + (MP_DIGIT_BIT - 1)) / MP_DIGIT_BIT) + 1;
+}
+
+static int s_ch2val(char c, int r) {
+ int out;
+
+ /*
+ * In some locales, isalpha() accepts characters outside the range A-Z,
+ * producing out<0 or out>=36. The "out >= r" check will always catch
+ * out>=36. Though nothing explicitly catches out<0, our caller reacts the
+ * same way to every negative return value.
+ */
+ if (isdigit((unsigned char)c))
+ out = c - '0';
+ else if (r > 10 && isalpha((unsigned char)c))
+ out = toupper((unsigned char)c) - 'A' + 10;
+ else
+ return -1;
+
+ return (out >= r) ? -1 : out;
+}
+
+static char s_val2ch(int v, int caps) {
+ assert(v >= 0);
+
+ if (v < 10) {
+ return v + '0';
+ } else {
+ char out = (v - 10) + 'a';
+
+ if (caps) {
+ return toupper((unsigned char)out);
+ } else {
+ return out;
+ }
+ }
+}
+
+static void s_2comp(unsigned char *buf, int len) {
+ unsigned short s = 1;
+
+ for (int i = len - 1; i >= 0; --i) {
+ unsigned char c = ~buf[i];
+
+ s = c + s;
+ c = s & UCHAR_MAX;
+ s >>= CHAR_BIT;
+
+ buf[i] = c;
+ }
+
+ /* last carry out is ignored */
+}
+
+static mp_result s_tobin(mp_int z, unsigned char *buf, int *limpos, int pad) {
+ int pos = 0, limit = *limpos;
+ mp_size uz = MP_USED(z);
+ mp_digit *dz = MP_DIGITS(z);
+
+ while (uz > 0 && pos < limit) {
+ mp_digit d = *dz++;
+ int i;
+
+ for (i = sizeof(mp_digit); i > 0 && pos < limit; --i) {
+ buf[pos++] = (unsigned char)d;
+ d >>= CHAR_BIT;
+
+ /* Don't write leading zeroes */
+ if (d == 0 && uz == 1) i = 0; /* exit loop without signaling truncation */
+ }
+
+ /* Detect truncation (loop exited with pos >= limit) */
+ if (i > 0) break;
+
+ --uz;
+ }
+
+ if (pad != 0 && (buf[pos - 1] >> (CHAR_BIT - 1))) {
+ if (pos < limit) {
+ buf[pos++] = 0;
+ } else {
+ uz = 1;
+ }
+ }
+
+ /* Digits are in reverse order, fix that */
+ REV(buf, pos);
+
+ /* Return the number of bytes actually written */
+ *limpos = pos;
+
+ return (uz == 0) ? MP_OK : MP_TRUNC;
+}
+
+/* Here there be dragons */
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imath.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imath.h
new file mode 100644
index 00000000000..7d304870cf0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imath.h
@@ -0,0 +1,421 @@
+/*
+ Name: imath.h
+ Purpose: Arbitrary precision integer arithmetic routines.
+ Author: M. J. Fromberger
+
+ Copyright (C) 2002-2007 Michael J. Fromberger, All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+#ifndef IMATH_H_
+#define IMATH_H_
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned char mp_sign;
+typedef unsigned int mp_size;
+typedef int mp_result;
+typedef long mp_small; /* must be a signed type */
+typedef unsigned long mp_usmall; /* must be an unsigned type */
+
+
+/* Build with words as uint64_t by default. */
+#ifdef USE_32BIT_WORDS
+typedef uint16_t mp_digit;
+typedef uint32_t mp_word;
+# define MP_DIGIT_MAX (UINT16_MAX * 1UL)
+# define MP_WORD_MAX (UINT32_MAX * 1UL)
+#else
+typedef uint32_t mp_digit;
+typedef uint64_t mp_word;
+# define MP_DIGIT_MAX (UINT32_MAX * UINT64_C(1))
+# define MP_WORD_MAX (UINT64_MAX)
+#endif
+
+typedef struct {
+ mp_digit single;
+ mp_digit* digits;
+ mp_size alloc;
+ mp_size used;
+ mp_sign sign;
+} mpz_t, *mp_int;
+
+static inline mp_digit* MP_DIGITS(mp_int Z) { return Z->digits; }
+static inline mp_size MP_ALLOC(mp_int Z) { return Z->alloc; }
+static inline mp_size MP_USED(mp_int Z) { return Z->used; }
+static inline mp_sign MP_SIGN(mp_int Z) { return Z->sign; }
+
+extern const mp_result MP_OK;
+extern const mp_result MP_FALSE;
+extern const mp_result MP_TRUE;
+extern const mp_result MP_MEMORY;
+extern const mp_result MP_RANGE;
+extern const mp_result MP_UNDEF;
+extern const mp_result MP_TRUNC;
+extern const mp_result MP_BADARG;
+extern const mp_result MP_MINERR;
+
+#define MP_DIGIT_BIT (sizeof(mp_digit) * CHAR_BIT)
+#define MP_WORD_BIT (sizeof(mp_word) * CHAR_BIT)
+#define MP_SMALL_MIN LONG_MIN
+#define MP_SMALL_MAX LONG_MAX
+#define MP_USMALL_MAX ULONG_MAX
+
+#define MP_MIN_RADIX 2
+#define MP_MAX_RADIX 36
+
+/** Sets the default number of digits allocated to an `mp_int` constructed by
+ `mp_int_init_size()` with `prec == 0`. Allocations are rounded up to
+ multiples of this value. `MP_DEFAULT_PREC` is the default value. Requires
+ `ndigits > 0`. */
+void mp_int_default_precision(mp_size ndigits);
+
+/** Sets the number of digits below which multiplication will use the standard
+ quadratic "schoolbook" multiplication algorithm rather than Karatsuba-Ofman.
+ Requires `ndigits >= sizeof(mp_word)`. */
+void mp_int_multiply_threshold(mp_size ndigits);
+
+/** A sign indicating a (strictly) negative value. */
+extern const mp_sign MP_NEG;
+
+/** A sign indicating a zero or positive value. */
+extern const mp_sign MP_ZPOS;
+
+/** Reports whether `z` is odd, having remainder 1 when divided by 2. */
+static inline bool mp_int_is_odd(mp_int z) { return (z->digits[0] & 1) != 0; }
+
+/** Reports whether `z` is even, having remainder 0 when divided by 2. */
+static inline bool mp_int_is_even(mp_int z) { return (z->digits[0] & 1) == 0; }
+
+/** Initializes `z` with 1-digit precision and sets it to zero. This function
+ cannot fail unless `z == NULL`. */
+mp_result mp_int_init(mp_int z);
+
+/** Allocates a fresh zero-valued `mpz_t` on the heap, returning NULL in case
+ of error. The only possible error is out-of-memory. */
+mp_int mp_int_alloc(void);
+
+/** Initializes `z` with at least `prec` digits of storage, and sets it to
+ zero. If `prec` is zero, the default precision is used. In either case the
+ size is rounded up to the nearest multiple of the word size. */
+mp_result mp_int_init_size(mp_int z, mp_size prec);
+
+/** Initializes `z` to be a copy of an already-initialized value in `old`. The
+ new copy does not share storage with the original. */
+mp_result mp_int_init_copy(mp_int z, mp_int old);
+
+/** Initializes `z` to the specified signed `value` at default precision. */
+mp_result mp_int_init_value(mp_int z, mp_small value);
+
+/** Initializes `z` to the specified unsigned `value` at default precision. */
+mp_result mp_int_init_uvalue(mp_int z, mp_usmall uvalue);
+
+/** Sets `z` to the value of the specified signed `value`. */
+mp_result mp_int_set_value(mp_int z, mp_small value);
+
+/** Sets `z` to the value of the specified unsigned `value`. */
+mp_result mp_int_set_uvalue(mp_int z, mp_usmall uvalue);
+
+/** Releases the storage used by `z`. */
+void mp_int_clear(mp_int z);
+
+/** Releases the storage used by `z` and also `z` itself.
+ This should only be used for `z` allocated by `mp_int_alloc()`. */
+void mp_int_free(mp_int z);
+
+/** Replaces the value of `c` with a copy of the value of `a`. No new memory is
+ allocated unless `a` has more significant digits than `c` has allocated. */
+mp_result mp_int_copy(mp_int a, mp_int c);
+
+/** Swaps the values and storage between `a` and `c`. */
+void mp_int_swap(mp_int a, mp_int c);
+
+/** Sets `z` to zero. The allocated storage of `z` is not changed. */
+void mp_int_zero(mp_int z);
+
+/** Sets `c` to the absolute value of `a`. */
+mp_result mp_int_abs(mp_int a, mp_int c);
+
+/** Sets `c` to the additive inverse (negation) of `a`. */
+mp_result mp_int_neg(mp_int a, mp_int c);
+
+/** Sets `c` to the sum of `a` and `b`. */
+mp_result mp_int_add(mp_int a, mp_int b, mp_int c);
+
+/** Sets `c` to the sum of `a` and `value`. */
+mp_result mp_int_add_value(mp_int a, mp_small value, mp_int c);
+
+/** Sets `c` to the difference of `a` less `b`. */
+mp_result mp_int_sub(mp_int a, mp_int b, mp_int c);
+
+/** Sets `c` to the difference of `a` less `value`. */
+mp_result mp_int_sub_value(mp_int a, mp_small value, mp_int c);
+
+/** Sets `c` to the product of `a` and `b`. */
+mp_result mp_int_mul(mp_int a, mp_int b, mp_int c);
+
+/** Sets `c` to the product of `a` and `value`. */
+mp_result mp_int_mul_value(mp_int a, mp_small value, mp_int c);
+
+/** Sets `c` to the product of `a` and `2^p2`. Requires `p2 >= 0`. */
+mp_result mp_int_mul_pow2(mp_int a, mp_small p2, mp_int c);
+
+/** Sets `c` to the square of `a`. */
+mp_result mp_int_sqr(mp_int a, mp_int c);
+
+/** Sets `q` and `r` to the quotent and remainder of `a / b`. Division by
+ powers of 2 is detected and handled efficiently. The remainder is pinned
+ to `0 <= r < b`.
+
+ Either of `q` or `r` may be NULL, but not both, and `q` and `r` may not
+ point to the same value. */
+mp_result mp_int_div(mp_int a, mp_int b, mp_int q, mp_int r);
+
+/** Sets `q` and `*r` to the quotent and remainder of `a / value`. Division by
+ powers of 2 is detected and handled efficiently. The remainder is pinned to
+ `0 <= *r < b`. Either of `q` or `r` may be NULL. */
+mp_result mp_int_div_value(mp_int a, mp_small value, mp_int q, mp_small *r);
+
+/** Sets `q` and `r` to the quotient and remainder of `a / 2^p2`. This is a
+ special case for division by powers of two that is more efficient than
+ using ordinary division. Note that `mp_int_div()` will automatically handle
+ this case, this function is for cases where you have only the exponent. */
+mp_result mp_int_div_pow2(mp_int a, mp_small p2, mp_int q, mp_int r);
+
+/** Sets `c` to the remainder of `a / m`.
+ The remainder is pinned to `0 <= c < m`. */
+mp_result mp_int_mod(mp_int a, mp_int m, mp_int c);
+
+/** Sets `c` to the value of `a` raised to the `b` power.
+ It returns `MP_RANGE` if `b < 0`. */
+mp_result mp_int_expt(mp_int a, mp_small b, mp_int c);
+
+/** Sets `c` to the value of `a` raised to the `b` power.
+ It returns `MP_RANGE` if `b < 0`. */
+mp_result mp_int_expt_value(mp_small a, mp_small b, mp_int c);
+
+/** Sets `c` to the value of `a` raised to the `b` power.
+ It returns `MP_RANGE`) if `b < 0`. */
+mp_result mp_int_expt_full(mp_int a, mp_int b, mp_int c);
+
+/** Sets `*r` to the remainder of `a / value`.
+ The remainder is pinned to `0 <= r < value`. */
+static inline
+mp_result mp_int_mod_value(mp_int a, mp_small value, mp_small* r) {
+ return mp_int_div_value(a, value, 0, r);
+}
+
+/** Returns the comparator of `a` and `b`. */
+int mp_int_compare(mp_int a, mp_int b);
+
+/** Returns the comparator of the magnitudes of `a` and `b`, disregarding their
+ signs. Neither `a` nor `b` is modified by the comparison. */
+int mp_int_compare_unsigned(mp_int a, mp_int b);
+
+/** Returns the comparator of `z` and zero. */
+int mp_int_compare_zero(mp_int z);
+
+/** Returns the comparator of `z` and the signed value `v`. */
+int mp_int_compare_value(mp_int z, mp_small v);
+
+/** Returns the comparator of `z` and the unsigned value `uv`. */
+int mp_int_compare_uvalue(mp_int z, mp_usmall uv);
+
+/** Reports whether `a` is divisible by `v`. */
+bool mp_int_divisible_value(mp_int a, mp_small v);
+
+/** Returns `k >= 0` such that `z` is `2^k`, if such a `k` exists. If no such
+ `k` exists, the function returns -1. */
+int mp_int_is_pow2(mp_int z);
+
+/** Sets `c` to the value of `a` raised to the `b` power, reduced modulo `m`.
+ It returns `MP_RANGE` if `b < 0` or `MP_UNDEF` if `m == 0`. */
+mp_result mp_int_exptmod(mp_int a, mp_int b, mp_int m, mp_int c);
+
+/** Sets `c` to the value of `a` raised to the `value` power, modulo `m`.
+ It returns `MP_RANGE` if `value < 0` or `MP_UNDEF` if `m == 0`. */
+mp_result mp_int_exptmod_evalue(mp_int a, mp_small value, mp_int m, mp_int c);
+
+/** Sets `c` to the value of `value` raised to the `b` power, modulo `m`.
+ It returns `MP_RANGE` if `b < 0` or `MP_UNDEF` if `m == 0`. */
+mp_result mp_int_exptmod_bvalue(mp_small value, mp_int b, mp_int m, mp_int c);
+
+/** Sets `c` to the value of `a` raised to the `b` power, reduced modulo `m`,
+ given a precomputed reduction constant `mu` defined for Barrett's modular
+ reduction algorithm.
+
+ It returns `MP_RANGE` if `b < 0` or `MP_UNDEF` if `m == 0`. */
+mp_result mp_int_exptmod_known(mp_int a, mp_int b, mp_int m, mp_int mu, mp_int c);
+
+/** Sets `c` to the reduction constant for Barrett reduction by modulus `m`.
+ Requires that `c` and `m` point to distinct locations. */
+mp_result mp_int_redux_const(mp_int m, mp_int c);
+
+/** Sets `c` to the multiplicative inverse of `a` modulo `m`, if it exists.
+ The least non-negative representative of the congruence class is computed.
+
+ It returns `MP_UNDEF` if the inverse does not exist, or `MP_RANGE` if `a ==
+ 0` or `m <= 0`. */
+mp_result mp_int_invmod(mp_int a, mp_int m, mp_int c);
+
+/** Sets `c` to the greatest common divisor of `a` and `b`.
+
+ It returns `MP_UNDEF` if the GCD is undefined, such as for example if `a`
+ and `b` are both zero. */
+mp_result mp_int_gcd(mp_int a, mp_int b, mp_int c);
+
+/** Sets `c` to the greatest common divisor of `a` and `b`, and sets `x` and
+ `y` to values satisfying Bezout's identity `gcd(a, b) = ax + by`.
+
+ It returns `MP_UNDEF` if the GCD is undefined, such as for example if `a`
+ and `b` are both zero. */
+mp_result mp_int_egcd(mp_int a, mp_int b, mp_int c, mp_int x, mp_int y);
+
+/** Sets `c` to the least common multiple of `a` and `b`.
+
+ It returns `MP_UNDEF` if the LCM is undefined, such as for example if `a`
+ and `b` are both zero. */
+mp_result mp_int_lcm(mp_int a, mp_int b, mp_int c);
+
+/** Sets `c` to the greatest integer not less than the `b`th root of `a`,
+ using Newton's root-finding algorithm.
+ It returns `MP_UNDEF` if `a < 0` and `b` is even. */
+mp_result mp_int_root(mp_int a, mp_small b, mp_int c);
+
+/** Sets `c` to the greatest integer not less than the square root of `a`.
+ This is a special case of `mp_int_root()`. */
+static inline
+mp_result mp_int_sqrt(mp_int a, mp_int c) { return mp_int_root(a, 2, c); }
+
+/** Returns `MP_OK` if `z` is representable as `mp_small`, else `MP_RANGE`.
+ If `out` is not NULL, `*out` is set to the value of `z` when `MP_OK`. */
+mp_result mp_int_to_int(mp_int z, mp_small *out);
+
+/** Returns `MP_OK` if `z` is representable as `mp_usmall`, or `MP_RANGE`.
+ If `out` is not NULL, `*out` is set to the value of `z` when `MP_OK`. */
+mp_result mp_int_to_uint(mp_int z, mp_usmall *out);
+
+/** Converts `z` to a zero-terminated string of characters in the specified
+ `radix`, writing at most `limit` characters to `str` including the
+ terminating NUL value. A leading `-` is used to indicate a negative value.
+
+ Returns `MP_TRUNC` if `limit` was to small to write all of `z`.
+ Requires `MP_MIN_RADIX <= radix <= MP_MAX_RADIX`. */
+mp_result mp_int_to_string(mp_int z, mp_size radix, char *str, int limit);
+
+/** Reports the minimum number of characters required to represent `z` as a
+ zero-terminated string in the given `radix`.
+ Requires `MP_MIN_RADIX <= radix <= MP_MAX_RADIX`. */
+mp_result mp_int_string_len(mp_int z, mp_size radix);
+
+/** Reads a string of ASCII digits in the specified `radix` from the zero
+ terminated `str` provided into `z`. For values of `radix > 10`, the letters
+ `A`..`Z` or `a`..`z` are accepted. Letters are interpreted without respect
+ to case.
+
+ Leading whitespace is ignored, and a leading `+` or `-` is interpreted as a
+ sign flag. Processing stops when a NUL or any other character out of range
+ for a digit in the given radix is encountered.
+
+ If the whole string was consumed, `MP_OK` is returned; otherwise
+ `MP_TRUNC`. is returned.
+
+ Requires `MP_MIN_RADIX <= radix <= MP_MAX_RADIX`. */
+mp_result mp_int_read_string(mp_int z, mp_size radix, const char *str);
+
+/** Reads a string of ASCII digits in the specified `radix` from the zero
+ terminated `str` provided into `z`. For values of `radix > 10`, the letters
+ `A`..`Z` or `a`..`z` are accepted. Letters are interpreted without respect
+ to case.
+
+ Leading whitespace is ignored, and a leading `+` or `-` is interpreted as a
+ sign flag. Processing stops when a NUL or any other character out of range
+ for a digit in the given radix is encountered.
+
+ If the whole string was consumed, `MP_OK` is returned; otherwise
+ `MP_TRUNC`. is returned. If `end` is not NULL, `*end` is set to point to
+ the first unconsumed byte of the input string (the NUL byte if the whole
+ string was consumed). This emulates the behavior of the standard C
+ `strtol()` function.
+
+ Requires `MP_MIN_RADIX <= radix <= MP_MAX_RADIX`. */
+mp_result mp_int_read_cstring(mp_int z, mp_size radix, const char *str, char **end);
+
+/** Returns the number of significant bits in `z`. */
+mp_result mp_int_count_bits(mp_int z);
+
+/** Converts `z` to 2's complement binary, writing at most `limit` bytes into
+ the given `buf`. Returns `MP_TRUNC` if the buffer limit was too small to
+ contain the whole value. If this occurs, the contents of buf will be
+ effectively garbage, as the function uses the buffer as scratch space.
+
+ The binary representation of `z` is in base-256 with digits ordered from
+ most significant to least significant (network byte ordering). The
+ high-order bit of the first byte is set for negative values, clear for
+ non-negative values.
+
+ As a result, non-negative values will be padded with a leading zero byte if
+ the high-order byte of the base-256 magnitude is set. This extra byte is
+ accounted for by the `mp_int_binary_len()` function. */
+mp_result mp_int_to_binary(mp_int z, unsigned char *buf, int limit);
+
+/** Reads a 2's complement binary value from `buf` into `z`, where `len` is the
+ length of the buffer. The contents of `buf` may be overwritten during
+ processing, although they will be restored when the function returns. */
+mp_result mp_int_read_binary(mp_int z, unsigned char *buf, int len);
+
+/** Returns the number of bytes to represent `z` in 2's complement binary. */
+mp_result mp_int_binary_len(mp_int z);
+
+/** Converts the magnitude of `z` to unsigned binary, writing at most `limit`
+ bytes into the given `buf`. The sign of `z` is ignored, but `z` is not
+ modified. Returns `MP_TRUNC` if the buffer limit was too small to contain
+ the whole value. If this occurs, the contents of `buf` will be effectively
+ garbage, as the function uses the buffer as scratch space during
+ conversion.
+
+ The binary representation of `z` is in base-256 with digits ordered from
+ most significant to least significant (network byte ordering). */
+mp_result mp_int_to_unsigned(mp_int z, unsigned char *buf, int limit);
+
+/** Reads an unsigned binary value from `buf` into `z`, where `len` is the
+ length of the buffer. The contents of `buf` are not modified during
+ processing. */
+mp_result mp_int_read_unsigned(mp_int z, unsigned char *buf, int len);
+
+/** Returns the number of bytes required to represent `z` as an unsigned binary
+ value in base 256. */
+mp_result mp_int_unsigned_len(mp_int z);
+
+/** Returns a pointer to a brief, human-readable, zero-terminated string
+ describing `res`. The returned string is statically allocated and must not
+ be freed by the caller. */
+const char *mp_error_string(mp_result res);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* end IMATH_H_ */
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imrat.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imrat.c
new file mode 100644
index 00000000000..afcfdaf3a78
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imrat.c
@@ -0,0 +1,940 @@
+/*
+ Name: imrat.c
+ Purpose: Arbitrary precision rational arithmetic routines.
+ Author: M. J. Fromberger
+
+ Copyright (C) 2002-2007 Michael J. Fromberger, All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+#include "imrat.h"
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MP_NUMER_SIGN(Q) (MP_NUMER_P(Q)->sign)
+#define MP_DENOM_SIGN(Q) (MP_DENOM_P(Q)->sign)
+
+#define TEMP(K) (temp + (K))
+#define SETUP(E, C) \
+ do { \
+ if ((res = (E)) != MP_OK) goto CLEANUP; \
+ ++(C); \
+ } while (0)
+
+/* Reduce the given rational, in place, to lowest terms and canonical form.
+ Zero is represented as 0/1, one as 1/1. Signs are adjusted so that the sign
+ of the numerator is definitive. */
+static mp_result s_rat_reduce(mp_rat r);
+
+/* Common code for addition and subtraction operations on rationals. */
+static mp_result s_rat_combine(mp_rat a, mp_rat b, mp_rat c,
+ mp_result (*comb_f)(mp_int, mp_int, mp_int));
+
+mp_result mp_rat_init(mp_rat r) {
+ mp_result res;
+
+ if ((res = mp_int_init(MP_NUMER_P(r))) != MP_OK) return res;
+ if ((res = mp_int_init(MP_DENOM_P(r))) != MP_OK) {
+ mp_int_clear(MP_NUMER_P(r));
+ return res;
+ }
+
+ return mp_int_set_value(MP_DENOM_P(r), 1);
+}
+
+mp_rat mp_rat_alloc(void) {
+ mp_rat out = malloc(sizeof(*out));
+
+ if (out != NULL) {
+ if (mp_rat_init(out) != MP_OK) {
+ free(out);
+ return NULL;
+ }
+ }
+
+ return out;
+}
+
+mp_result mp_rat_reduce(mp_rat r) { return s_rat_reduce(r); }
+
+mp_result mp_rat_init_size(mp_rat r, mp_size n_prec, mp_size d_prec) {
+ mp_result res;
+
+ if ((res = mp_int_init_size(MP_NUMER_P(r), n_prec)) != MP_OK) {
+ return res;
+ }
+ if ((res = mp_int_init_size(MP_DENOM_P(r), d_prec)) != MP_OK) {
+ mp_int_clear(MP_NUMER_P(r));
+ return res;
+ }
+
+ return mp_int_set_value(MP_DENOM_P(r), 1);
+}
+
+mp_result mp_rat_init_copy(mp_rat r, mp_rat old) {
+ mp_result res;
+
+ if ((res = mp_int_init_copy(MP_NUMER_P(r), MP_NUMER_P(old))) != MP_OK) {
+ return res;
+ }
+ if ((res = mp_int_init_copy(MP_DENOM_P(r), MP_DENOM_P(old))) != MP_OK)
+ mp_int_clear(MP_NUMER_P(r));
+
+ return res;
+}
+
+mp_result mp_rat_set_value(mp_rat r, mp_small numer, mp_small denom) {
+ mp_result res;
+
+ if (denom == 0) return MP_UNDEF;
+
+ if ((res = mp_int_set_value(MP_NUMER_P(r), numer)) != MP_OK) {
+ return res;
+ }
+ if ((res = mp_int_set_value(MP_DENOM_P(r), denom)) != MP_OK) {
+ return res;
+ }
+
+ return s_rat_reduce(r);
+}
+
+mp_result mp_rat_set_uvalue(mp_rat r, mp_usmall numer, mp_usmall denom) {
+ mp_result res;
+
+ if (denom == 0) return MP_UNDEF;
+
+ if ((res = mp_int_set_uvalue(MP_NUMER_P(r), numer)) != MP_OK) {
+ return res;
+ }
+ if ((res = mp_int_set_uvalue(MP_DENOM_P(r), denom)) != MP_OK) {
+ return res;
+ }
+
+ return s_rat_reduce(r);
+}
+
+void mp_rat_clear(mp_rat r) {
+ mp_int_clear(MP_NUMER_P(r));
+ mp_int_clear(MP_DENOM_P(r));
+}
+
+void mp_rat_free(mp_rat r) {
+ assert(r != NULL);
+
+ if (r->num.digits != NULL) mp_rat_clear(r);
+
+ free(r);
+}
+
+mp_result mp_rat_numer(mp_rat r, mp_int z) {
+ return mp_int_copy(MP_NUMER_P(r), z);
+}
+
+mp_int mp_rat_numer_ref(mp_rat r) { return MP_NUMER_P(r); }
+
+mp_result mp_rat_denom(mp_rat r, mp_int z) {
+ return mp_int_copy(MP_DENOM_P(r), z);
+}
+
+mp_int mp_rat_denom_ref(mp_rat r) { return MP_DENOM_P(r); }
+
+mp_sign mp_rat_sign(mp_rat r) { return MP_NUMER_SIGN(r); }
+
+mp_result mp_rat_copy(mp_rat a, mp_rat c) {
+ mp_result res;
+
+ if ((res = mp_int_copy(MP_NUMER_P(a), MP_NUMER_P(c))) != MP_OK) {
+ return res;
+ }
+
+ res = mp_int_copy(MP_DENOM_P(a), MP_DENOM_P(c));
+ return res;
+}
+
+void mp_rat_zero(mp_rat r) {
+ mp_int_zero(MP_NUMER_P(r));
+ mp_int_set_value(MP_DENOM_P(r), 1);
+}
+
+mp_result mp_rat_abs(mp_rat a, mp_rat c) {
+ mp_result res;
+
+ if ((res = mp_int_abs(MP_NUMER_P(a), MP_NUMER_P(c))) != MP_OK) {
+ return res;
+ }
+
+ res = mp_int_abs(MP_DENOM_P(a), MP_DENOM_P(c));
+ return res;
+}
+
+mp_result mp_rat_neg(mp_rat a, mp_rat c) {
+ mp_result res;
+
+ if ((res = mp_int_neg(MP_NUMER_P(a), MP_NUMER_P(c))) != MP_OK) {
+ return res;
+ }
+
+ res = mp_int_copy(MP_DENOM_P(a), MP_DENOM_P(c));
+ return res;
+}
+
+mp_result mp_rat_recip(mp_rat a, mp_rat c) {
+ mp_result res;
+
+ if (mp_rat_compare_zero(a) == 0) return MP_UNDEF;
+
+ if ((res = mp_rat_copy(a, c)) != MP_OK) return res;
+
+ mp_int_swap(MP_NUMER_P(c), MP_DENOM_P(c));
+
+ /* Restore the signs of the swapped elements */
+ {
+ mp_sign tmp = MP_NUMER_SIGN(c);
+
+ MP_NUMER_SIGN(c) = MP_DENOM_SIGN(c);
+ MP_DENOM_SIGN(c) = tmp;
+ }
+
+ return MP_OK;
+}
+
+mp_result mp_rat_add(mp_rat a, mp_rat b, mp_rat c) {
+ return s_rat_combine(a, b, c, mp_int_add);
+}
+
+mp_result mp_rat_sub(mp_rat a, mp_rat b, mp_rat c) {
+ return s_rat_combine(a, b, c, mp_int_sub);
+}
+
+mp_result mp_rat_mul(mp_rat a, mp_rat b, mp_rat c) {
+ mp_result res;
+
+ if ((res = mp_int_mul(MP_NUMER_P(a), MP_NUMER_P(b), MP_NUMER_P(c))) != MP_OK)
+ return res;
+
+ if (mp_int_compare_zero(MP_NUMER_P(c)) != 0) {
+ res = mp_int_mul(MP_DENOM_P(a), MP_DENOM_P(b), MP_DENOM_P(c));
+ if (res != MP_OK) {
+ return res;
+ }
+ }
+
+ return s_rat_reduce(c);
+}
+
+mp_result mp_rat_div(mp_rat a, mp_rat b, mp_rat c) {
+ mp_result res = MP_OK;
+
+ if (mp_rat_compare_zero(b) == 0) return MP_UNDEF;
+
+ if (c == a || c == b) {
+ mpz_t tmp;
+
+ if ((res = mp_int_init(&tmp)) != MP_OK) return res;
+ if ((res = mp_int_mul(MP_NUMER_P(a), MP_DENOM_P(b), &tmp)) != MP_OK) {
+ goto CLEANUP;
+ }
+ if ((res = mp_int_mul(MP_DENOM_P(a), MP_NUMER_P(b), MP_DENOM_P(c))) !=
+ MP_OK) {
+ goto CLEANUP;
+ }
+ res = mp_int_copy(&tmp, MP_NUMER_P(c));
+
+ CLEANUP:
+ mp_int_clear(&tmp);
+ } else {
+ if ((res = mp_int_mul(MP_NUMER_P(a), MP_DENOM_P(b), MP_NUMER_P(c))) !=
+ MP_OK) {
+ return res;
+ }
+ if ((res = mp_int_mul(MP_DENOM_P(a), MP_NUMER_P(b), MP_DENOM_P(c))) !=
+ MP_OK) {
+ return res;
+ }
+ }
+
+ if (res != MP_OK) {
+ return res;
+ } else {
+ return s_rat_reduce(c);
+ }
+}
+
+mp_result mp_rat_add_int(mp_rat a, mp_int b, mp_rat c) {
+ mpz_t tmp;
+ mp_result res;
+
+ if ((res = mp_int_init_copy(&tmp, b)) != MP_OK) {
+ return res;
+ }
+ if ((res = mp_int_mul(&tmp, MP_DENOM_P(a), &tmp)) != MP_OK) {
+ goto CLEANUP;
+ }
+ if ((res = mp_rat_copy(a, c)) != MP_OK) {
+ goto CLEANUP;
+ }
+ if ((res = mp_int_add(MP_NUMER_P(c), &tmp, MP_NUMER_P(c))) != MP_OK) {
+ goto CLEANUP;
+ }
+
+ res = s_rat_reduce(c);
+
+CLEANUP:
+ mp_int_clear(&tmp);
+ return res;
+}
+
+mp_result mp_rat_sub_int(mp_rat a, mp_int b, mp_rat c) {
+ mpz_t tmp;
+ mp_result res;
+
+ if ((res = mp_int_init_copy(&tmp, b)) != MP_OK) {
+ return res;
+ }
+ if ((res = mp_int_mul(&tmp, MP_DENOM_P(a), &tmp)) != MP_OK) {
+ goto CLEANUP;
+ }
+ if ((res = mp_rat_copy(a, c)) != MP_OK) {
+ goto CLEANUP;
+ }
+ if ((res = mp_int_sub(MP_NUMER_P(c), &tmp, MP_NUMER_P(c))) != MP_OK) {
+ goto CLEANUP;
+ }
+
+ res = s_rat_reduce(c);
+
+CLEANUP:
+ mp_int_clear(&tmp);
+ return res;
+}
+
+mp_result mp_rat_mul_int(mp_rat a, mp_int b, mp_rat c) {
+ mp_result res;
+
+ if ((res = mp_rat_copy(a, c)) != MP_OK) {
+ return res;
+ }
+ if ((res = mp_int_mul(MP_NUMER_P(c), b, MP_NUMER_P(c))) != MP_OK) {
+ return res;
+ }
+
+ return s_rat_reduce(c);
+}
+
+mp_result mp_rat_div_int(mp_rat a, mp_int b, mp_rat c) {
+ mp_result res;
+
+ if (mp_int_compare_zero(b) == 0) {
+ return MP_UNDEF;
+ }
+ if ((res = mp_rat_copy(a, c)) != MP_OK) {
+ return res;
+ }
+ if ((res = mp_int_mul(MP_DENOM_P(c), b, MP_DENOM_P(c))) != MP_OK) {
+ return res;
+ }
+
+ return s_rat_reduce(c);
+}
+
+mp_result mp_rat_expt(mp_rat a, mp_small b, mp_rat c) {
+ mp_result res;
+
+ /* Special cases for easy powers. */
+ if (b == 0) {
+ return mp_rat_set_value(c, 1, 1);
+ } else if (b == 1) {
+ return mp_rat_copy(a, c);
+ }
+
+ /* Since rationals are always stored in lowest terms, it is not necessary to
+ reduce again when raising to an integer power. */
+ if ((res = mp_int_expt(MP_NUMER_P(a), b, MP_NUMER_P(c))) != MP_OK) {
+ return res;
+ }
+
+ return mp_int_expt(MP_DENOM_P(a), b, MP_DENOM_P(c));
+}
+
+int mp_rat_compare(mp_rat a, mp_rat b) {
+ /* Quick check for opposite signs. Works because the sign of the numerator
+ is always definitive. */
+ if (MP_NUMER_SIGN(a) != MP_NUMER_SIGN(b)) {
+ if (MP_NUMER_SIGN(a) == MP_ZPOS) {
+ return 1;
+ } else {
+ return -1;
+ }
+ } else {
+ /* Compare absolute magnitudes; if both are positive, the answer stands,
+ otherwise it needs to be reflected about zero. */
+ int cmp = mp_rat_compare_unsigned(a, b);
+
+ if (MP_NUMER_SIGN(a) == MP_ZPOS) {
+ return cmp;
+ } else {
+ return -cmp;
+ }
+ }
+}
+
+int mp_rat_compare_unsigned(mp_rat a, mp_rat b) {
+ /* If the denominators are equal, we can quickly compare numerators without
+ multiplying. Otherwise, we actually have to do some work. */
+ if (mp_int_compare_unsigned(MP_DENOM_P(a), MP_DENOM_P(b)) == 0) {
+ return mp_int_compare_unsigned(MP_NUMER_P(a), MP_NUMER_P(b));
+ }
+
+ else {
+ mpz_t temp[2];
+ mp_result res;
+ int cmp = INT_MAX, last = 0;
+
+ /* t0 = num(a) * den(b), t1 = num(b) * den(a) */
+ SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(a)), last);
+ SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(b)), last);
+
+ if ((res = mp_int_mul(TEMP(0), MP_DENOM_P(b), TEMP(0))) != MP_OK ||
+ (res = mp_int_mul(TEMP(1), MP_DENOM_P(a), TEMP(1))) != MP_OK) {
+ goto CLEANUP;
+ }
+
+ cmp = mp_int_compare_unsigned(TEMP(0), TEMP(1));
+
+ CLEANUP:
+ while (--last >= 0) {
+ mp_int_clear(TEMP(last));
+ }
+
+ return cmp;
+ }
+}
+
+int mp_rat_compare_zero(mp_rat r) { return mp_int_compare_zero(MP_NUMER_P(r)); }
+
+int mp_rat_compare_value(mp_rat r, mp_small n, mp_small d) {
+ mpq_t tmp;
+ mp_result res;
+ int out = INT_MAX;
+
+ if ((res = mp_rat_init(&tmp)) != MP_OK) {
+ return out;
+ }
+ if ((res = mp_rat_set_value(&tmp, n, d)) != MP_OK) {
+ goto CLEANUP;
+ }
+
+ out = mp_rat_compare(r, &tmp);
+
+CLEANUP:
+ mp_rat_clear(&tmp);
+ return out;
+}
+
+bool mp_rat_is_integer(mp_rat r) {
+ return (mp_int_compare_value(MP_DENOM_P(r), 1) == 0);
+}
+
+mp_result mp_rat_to_ints(mp_rat r, mp_small *num, mp_small *den) {
+ mp_result res;
+
+ if ((res = mp_int_to_int(MP_NUMER_P(r), num)) != MP_OK) {
+ return res;
+ }
+
+ res = mp_int_to_int(MP_DENOM_P(r), den);
+ return res;
+}
+
+mp_result mp_rat_to_string(mp_rat r, mp_size radix, char *str, int limit) {
+ /* Write the numerator. The sign of the rational number is written by the
+ underlying integer implementation. */
+ mp_result res;
+ if ((res = mp_int_to_string(MP_NUMER_P(r), radix, str, limit)) != MP_OK) {
+ return res;
+ }
+
+ /* If the value is zero, don't bother writing any denominator */
+ if (mp_int_compare_zero(MP_NUMER_P(r)) == 0) {
+ return MP_OK;
+ }
+
+ /* Locate the end of the numerator, and make sure we are not going to exceed
+ the limit by writing a slash. */
+ int len = strlen(str);
+ char *start = str + len;
+ limit -= len;
+ if (limit == 0) return MP_TRUNC;
+
+ *start++ = '/';
+ limit -= 1;
+
+ return mp_int_to_string(MP_DENOM_P(r), radix, start, limit);
+}
+
+mp_result mp_rat_to_decimal(mp_rat r, mp_size radix, mp_size prec,
+ mp_round_mode round, char *str, int limit) {
+ mpz_t temp[3];
+ mp_result res;
+ int last = 0;
+
+ SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(r)), last);
+ SETUP(mp_int_init(TEMP(last)), last);
+ SETUP(mp_int_init(TEMP(last)), last);
+
+ /* Get the unsigned integer part by dividing denominator into the absolute
+ value of the numerator. */
+ mp_int_abs(TEMP(0), TEMP(0));
+ if ((res = mp_int_div(TEMP(0), MP_DENOM_P(r), TEMP(0), TEMP(1))) != MP_OK) {
+ goto CLEANUP;
+ }
+
+ /* Now: T0 = integer portion, unsigned;
+ T1 = remainder, from which fractional part is computed. */
+
+ /* Count up leading zeroes after the radix point. */
+ int zprec = (int)prec;
+ int lead_0;
+ for (lead_0 = 0; lead_0 < zprec && mp_int_compare(TEMP(1), MP_DENOM_P(r)) < 0;
+ ++lead_0) {
+ if ((res = mp_int_mul_value(TEMP(1), radix, TEMP(1))) != MP_OK) {
+ goto CLEANUP;
+ }
+ }
+
+ /* Multiply remainder by a power of the radix sufficient to get the right
+ number of significant figures. */
+ if (zprec > lead_0) {
+ if ((res = mp_int_expt_value(radix, zprec - lead_0, TEMP(2))) != MP_OK) {
+ goto CLEANUP;
+ }
+ if ((res = mp_int_mul(TEMP(1), TEMP(2), TEMP(1))) != MP_OK) {
+ goto CLEANUP;
+ }
+ }
+ if ((res = mp_int_div(TEMP(1), MP_DENOM_P(r), TEMP(1), TEMP(2))) != MP_OK) {
+ goto CLEANUP;
+ }
+
+ /* Now: T1 = significant digits of fractional part;
+ T2 = leftovers, to use for rounding.
+
+ At this point, what we do depends on the rounding mode. The default is
+ MP_ROUND_DOWN, for which everything is as it should be already.
+ */
+ switch (round) {
+ int cmp;
+
+ case MP_ROUND_UP:
+ if (mp_int_compare_zero(TEMP(2)) != 0) {
+ if (prec == 0) {
+ res = mp_int_add_value(TEMP(0), 1, TEMP(0));
+ } else {
+ res = mp_int_add_value(TEMP(1), 1, TEMP(1));
+ }
+ }
+ break;
+
+ case MP_ROUND_HALF_UP:
+ case MP_ROUND_HALF_DOWN:
+ if ((res = mp_int_mul_pow2(TEMP(2), 1, TEMP(2))) != MP_OK) {
+ goto CLEANUP;
+ }
+
+ cmp = mp_int_compare(TEMP(2), MP_DENOM_P(r));
+
+ if (round == MP_ROUND_HALF_UP) cmp += 1;
+
+ if (cmp > 0) {
+ if (prec == 0) {
+ res = mp_int_add_value(TEMP(0), 1, TEMP(0));
+ } else {
+ res = mp_int_add_value(TEMP(1), 1, TEMP(1));
+ }
+ }
+ break;
+
+ case MP_ROUND_DOWN:
+ break; /* No action required */
+
+ default:
+ return MP_BADARG; /* Invalid rounding specifier */
+ }
+ if (res != MP_OK) {
+ goto CLEANUP;
+ }
+
+ /* The sign of the output should be the sign of the numerator, but if all the
+ displayed digits will be zero due to the precision, a negative shouldn't
+ be shown. */
+ char *start = str;
+ int left = limit;
+ if (MP_NUMER_SIGN(r) == MP_NEG && (mp_int_compare_zero(TEMP(0)) != 0 ||
+ mp_int_compare_zero(TEMP(1)) != 0)) {
+ *start++ = '-';
+ left -= 1;
+ }
+
+ if ((res = mp_int_to_string(TEMP(0), radix, start, left)) != MP_OK) {
+ goto CLEANUP;
+ }
+
+ int len = strlen(start);
+ start += len;
+ left -= len;
+
+ if (prec == 0) goto CLEANUP;
+
+ *start++ = '.';
+ left -= 1;
+
+ if (left < zprec + 1) {
+ res = MP_TRUNC;
+ goto CLEANUP;
+ }
+
+ memset(start, '0', lead_0 - 1);
+ left -= lead_0;
+ start += lead_0 - 1;
+
+ res = mp_int_to_string(TEMP(1), radix, start, left);
+
+CLEANUP:
+ while (--last >= 0) mp_int_clear(TEMP(last));
+
+ return res;
+}
+
+mp_result mp_rat_string_len(mp_rat r, mp_size radix) {
+ mp_result d_len = 0;
+ mp_result n_len = mp_int_string_len(MP_NUMER_P(r), radix);
+
+ if (mp_int_compare_zero(MP_NUMER_P(r)) != 0) {
+ d_len = mp_int_string_len(MP_DENOM_P(r), radix);
+ }
+
+ /* Though simplistic, this formula is correct. Space for the sign flag is
+ included in n_len, and the space for the NUL that is counted in n_len
+ counts for the separator here. The space for the NUL counted in d_len
+ counts for the final terminator here. */
+
+ return n_len + d_len;
+}
+
+mp_result mp_rat_decimal_len(mp_rat r, mp_size radix, mp_size prec) {
+ int f_len;
+ int z_len = mp_int_string_len(MP_NUMER_P(r), radix);
+
+ if (prec == 0) {
+ f_len = 1; /* terminator only */
+ } else {
+ f_len = 1 + prec + 1; /* decimal point, digits, terminator */
+ }
+
+ return z_len + f_len;
+}
+
+mp_result mp_rat_read_string(mp_rat r, mp_size radix, const char *str) {
+ return mp_rat_read_cstring(r, radix, str, NULL);
+}
+
+mp_result mp_rat_read_cstring(mp_rat r, mp_size radix, const char *str,
+ char **end) {
+ mp_result res;
+ char *endp;
+
+ if ((res = mp_int_read_cstring(MP_NUMER_P(r), radix, str, &endp)) != MP_OK &&
+ (res != MP_TRUNC)) {
+ return res;
+ }
+
+ /* Skip whitespace between numerator and (possible) separator */
+ while (isspace((unsigned char)*endp)) {
+ ++endp;
+ }
+
+ /* If there is no separator, we will stop reading at this point. */
+ if (*endp != '/') {
+ mp_int_set_value(MP_DENOM_P(r), 1);
+ if (end != NULL) *end = endp;
+ return res;
+ }
+
+ ++endp; /* skip separator */
+ if ((res = mp_int_read_cstring(MP_DENOM_P(r), radix, endp, end)) != MP_OK) {
+ return res;
+ }
+
+ /* Make sure the value is well-defined */
+ if (mp_int_compare_zero(MP_DENOM_P(r)) == 0) {
+ return MP_UNDEF;
+ }
+
+ /* Reduce to lowest terms */
+ return s_rat_reduce(r);
+}
+
+/* Read a string and figure out what format it's in. The radix may be supplied
+ as zero to use "default" behaviour.
+
+ This function will accept either a/b notation or decimal notation.
+ */
+mp_result mp_rat_read_ustring(mp_rat r, mp_size radix, const char *str,
+ char **end) {
+ char *endp = "";
+ mp_result res;
+
+ if (radix == 0) radix = 10; /* default to decimal input */
+
+ res = mp_rat_read_cstring(r, radix, str, &endp);
+ if (res == MP_TRUNC && *endp == '.') {
+ res = mp_rat_read_cdecimal(r, radix, str, &endp);
+ }
+ if (end != NULL) *end = endp;
+ return res;
+}
+
+mp_result mp_rat_read_decimal(mp_rat r, mp_size radix, const char *str) {
+ return mp_rat_read_cdecimal(r, radix, str, NULL);
+}
+
+mp_result mp_rat_read_cdecimal(mp_rat r, mp_size radix, const char *str,
+ char **end) {
+ mp_result res;
+ mp_sign osign;
+ char *endp;
+
+ while (isspace((unsigned char)*str)) ++str;
+
+ switch (*str) {
+ case '-':
+ osign = MP_NEG;
+ break;
+ default:
+ osign = MP_ZPOS;
+ }
+
+ if ((res = mp_int_read_cstring(MP_NUMER_P(r), radix, str, &endp)) != MP_OK &&
+ (res != MP_TRUNC)) {
+ return res;
+ }
+
+ /* This needs to be here. */
+ (void)mp_int_set_value(MP_DENOM_P(r), 1);
+
+ if (*endp != '.') {
+ if (end != NULL) *end = endp;
+ return res;
+ }
+
+ /* If the character following the decimal point is whitespace or a sign flag,
+ we will consider this a truncated value. This special case is because
+ mp_int_read_string() will consider whitespace or sign flags to be valid
+ starting characters for a value, and we do not want them following the
+ decimal point.
+
+ Once we have done this check, it is safe to read in the value of the
+ fractional piece as a regular old integer.
+ */
+ ++endp;
+ if (*endp == '\0') {
+ if (end != NULL) *end = endp;
+ return MP_OK;
+ } else if (isspace((unsigned char)*endp) || *endp == '-' || *endp == '+') {
+ return MP_TRUNC;
+ } else {
+ mpz_t frac;
+ mp_result save_res;
+ char *save = endp;
+ int num_lz = 0;
+
+ /* Make a temporary to hold the part after the decimal point. */
+ if ((res = mp_int_init(&frac)) != MP_OK) {
+ return res;
+ }
+
+ if ((res = mp_int_read_cstring(&frac, radix, endp, &endp)) != MP_OK &&
+ (res != MP_TRUNC)) {
+ goto CLEANUP;
+ }
+
+ /* Save this response for later. */
+ save_res = res;
+
+ if (mp_int_compare_zero(&frac) == 0) goto FINISHED;
+
+ /* Discard trailing zeroes (somewhat inefficiently) */
+ while (mp_int_divisible_value(&frac, radix)) {
+ if ((res = mp_int_div_value(&frac, radix, &frac, NULL)) != MP_OK) {
+ goto CLEANUP;
+ }
+ }
+
+ /* Count leading zeros after the decimal point */
+ while (save[num_lz] == '0') {
+ ++num_lz;
+ }
+
+ /* Find the least power of the radix that is at least as large as the
+ significant value of the fractional part, ignoring leading zeroes. */
+ (void)mp_int_set_value(MP_DENOM_P(r), radix);
+
+ while (mp_int_compare(MP_DENOM_P(r), &frac) < 0) {
+ if ((res = mp_int_mul_value(MP_DENOM_P(r), radix, MP_DENOM_P(r))) !=
+ MP_OK) {
+ goto CLEANUP;
+ }
+ }
+
+ /* Also shift by enough to account for leading zeroes */
+ while (num_lz > 0) {
+ if ((res = mp_int_mul_value(MP_DENOM_P(r), radix, MP_DENOM_P(r))) !=
+ MP_OK) {
+ goto CLEANUP;
+ }
+
+ --num_lz;
+ }
+
+ /* Having found this power, shift the numerator leftward that many, digits,
+ and add the nonzero significant digits of the fractional part to get the
+ result. */
+ if ((res = mp_int_mul(MP_NUMER_P(r), MP_DENOM_P(r), MP_NUMER_P(r))) !=
+ MP_OK) {
+ goto CLEANUP;
+ }
+
+ { /* This addition needs to be unsigned. */
+ MP_NUMER_SIGN(r) = MP_ZPOS;
+ if ((res = mp_int_add(MP_NUMER_P(r), &frac, MP_NUMER_P(r))) != MP_OK) {
+ goto CLEANUP;
+ }
+
+ MP_NUMER_SIGN(r) = osign;
+ }
+ if ((res = s_rat_reduce(r)) != MP_OK) goto CLEANUP;
+
+ /* At this point, what we return depends on whether reading the fractional
+ part was truncated or not. That information is saved from when we
+ called mp_int_read_string() above. */
+ FINISHED:
+ res = save_res;
+ if (end != NULL) *end = endp;
+
+ CLEANUP:
+ mp_int_clear(&frac);
+
+ return res;
+ }
+}
+
+/* Private functions for internal use. Make unchecked assumptions about format
+ and validity of inputs. */
+
+static mp_result s_rat_reduce(mp_rat r) {
+ mpz_t gcd;
+ mp_result res = MP_OK;
+
+ if (mp_int_compare_zero(MP_NUMER_P(r)) == 0) {
+ mp_int_set_value(MP_DENOM_P(r), 1);
+ return MP_OK;
+ }
+
+ /* If the greatest common divisor of the numerator and denominator is greater
+ than 1, divide it out. */
+ if ((res = mp_int_init(&gcd)) != MP_OK) return res;
+
+ if ((res = mp_int_gcd(MP_NUMER_P(r), MP_DENOM_P(r), &gcd)) != MP_OK) {
+ goto CLEANUP;
+ }
+
+ if (mp_int_compare_value(&gcd, 1) != 0) {
+ if ((res = mp_int_div(MP_NUMER_P(r), &gcd, MP_NUMER_P(r), NULL)) != MP_OK) {
+ goto CLEANUP;
+ }
+ if ((res = mp_int_div(MP_DENOM_P(r), &gcd, MP_DENOM_P(r), NULL)) != MP_OK) {
+ goto CLEANUP;
+ }
+ }
+
+ /* Fix up the signs of numerator and denominator */
+ if (MP_NUMER_SIGN(r) == MP_DENOM_SIGN(r)) {
+ MP_NUMER_SIGN(r) = MP_DENOM_SIGN(r) = MP_ZPOS;
+ } else {
+ MP_NUMER_SIGN(r) = MP_NEG;
+ MP_DENOM_SIGN(r) = MP_ZPOS;
+ }
+
+CLEANUP:
+ mp_int_clear(&gcd);
+
+ return res;
+}
+
+static mp_result s_rat_combine(mp_rat a, mp_rat b, mp_rat c,
+ mp_result (*comb_f)(mp_int, mp_int, mp_int)) {
+ mp_result res;
+
+ /* Shortcut when denominators are already common */
+ if (mp_int_compare(MP_DENOM_P(a), MP_DENOM_P(b)) == 0) {
+ if ((res = (comb_f)(MP_NUMER_P(a), MP_NUMER_P(b), MP_NUMER_P(c))) !=
+ MP_OK) {
+ return res;
+ }
+ if ((res = mp_int_copy(MP_DENOM_P(a), MP_DENOM_P(c))) != MP_OK) {
+ return res;
+ }
+
+ return s_rat_reduce(c);
+ } else {
+ mpz_t temp[2];
+ int last = 0;
+
+ SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(a)), last);
+ SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(b)), last);
+
+ if ((res = mp_int_mul(TEMP(0), MP_DENOM_P(b), TEMP(0))) != MP_OK) {
+ goto CLEANUP;
+ }
+ if ((res = mp_int_mul(TEMP(1), MP_DENOM_P(a), TEMP(1))) != MP_OK) {
+ goto CLEANUP;
+ }
+ if ((res = (comb_f)(TEMP(0), TEMP(1), MP_NUMER_P(c))) != MP_OK) {
+ goto CLEANUP;
+ }
+
+ res = mp_int_mul(MP_DENOM_P(a), MP_DENOM_P(b), MP_DENOM_P(c));
+
+ CLEANUP:
+ while (--last >= 0) {
+ mp_int_clear(TEMP(last));
+ }
+
+ if (res == MP_OK) {
+ return s_rat_reduce(c);
+ } else {
+ return res;
+ }
+ }
+}
+
+/* Here there be dragons */
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imrat.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imrat.h
new file mode 100644
index 00000000000..87e167fe772
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/imath/imrat.h
@@ -0,0 +1,270 @@
+/*
+ Name: imrat.h
+ Purpose: Arbitrary precision rational arithmetic routines.
+ Author: M. J. Fromberger
+
+ Copyright (C) 2002-2007 Michael J. Fromberger, All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+#ifndef IMRAT_H_
+#define IMRAT_H_
+
+#include <stdbool.h>
+
+#include "imath.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ mpz_t num; /* Numerator */
+ mpz_t den; /* Denominator, <> 0 */
+} mpq_t, *mp_rat;
+
+/* Return a pointer to the numerator. */
+static inline mp_int MP_NUMER_P(mp_rat Q) { return &(Q->num); }
+
+/* Return a pointer to the denominator. */
+static inline mp_int MP_DENOM_P(mp_rat Q) { return &(Q->den); }
+
+/* Rounding constants */
+typedef enum {
+ MP_ROUND_DOWN,
+ MP_ROUND_HALF_UP,
+ MP_ROUND_UP,
+ MP_ROUND_HALF_DOWN
+} mp_round_mode;
+
+/** Initializes `r` with 1-digit precision and sets it to zero. This function
+ cannot fail unless `r` is NULL. */
+mp_result mp_rat_init(mp_rat r);
+
+/** Allocates a fresh zero-valued `mpq_t` on the heap, returning NULL in case
+ of error. The only possible error is out-of-memory. */
+mp_rat mp_rat_alloc(void);
+
+/** Reduces `r` in-place to lowest terms and canonical form.
+
+ Zero is represented as 0/1, one as 1/1, and signs are adjusted so that the
+ sign of the value is carried by the numerator. */
+mp_result mp_rat_reduce(mp_rat r);
+
+/** Initializes `r` with at least `n_prec` digits of storage for the numerator
+ and `d_prec` digits of storage for the denominator, and value zero.
+
+ If either precision is zero, the default precision is used, rounded up to
+ the nearest word size. */
+mp_result mp_rat_init_size(mp_rat r, mp_size n_prec, mp_size d_prec);
+
+/** Initializes `r` to be a copy of an already-initialized value in `old`. The
+ new copy does not share storage with the original. */
+mp_result mp_rat_init_copy(mp_rat r, mp_rat old);
+
+/** Sets the value of `r` to the ratio of signed `numer` to signed `denom`. It
+ returns `MP_UNDEF` if `denom` is zero. */
+mp_result mp_rat_set_value(mp_rat r, mp_small numer, mp_small denom);
+
+/** Sets the value of `r` to the ratio of unsigned `numer` to unsigned
+ `denom`. It returns `MP_UNDEF` if `denom` is zero. */
+mp_result mp_rat_set_uvalue(mp_rat r, mp_usmall numer, mp_usmall denom);
+
+/** Releases the storage used by `r`. */
+void mp_rat_clear(mp_rat r);
+
+/** Releases the storage used by `r` and also `r` itself.
+ This should only be used for `r` allocated by `mp_rat_alloc()`. */
+void mp_rat_free(mp_rat r);
+
+/** Sets `z` to a copy of the numerator of `r`. */
+mp_result mp_rat_numer(mp_rat r, mp_int z);
+
+/** Returns a pointer to the numerator of `r`. */
+mp_int mp_rat_numer_ref(mp_rat r);
+
+/** Sets `z` to a copy of the denominator of `r`. */
+mp_result mp_rat_denom(mp_rat r, mp_int z);
+
+/** Returns a pointer to the denominator of `r`. */
+mp_int mp_rat_denom_ref(mp_rat r);
+
+/** Reports the sign of `r`. */
+mp_sign mp_rat_sign(mp_rat r);
+
+/** Sets `c` to a copy of the value of `a`. No new memory is allocated unless a
+ term of `a` has more significant digits than the corresponding term of `c`
+ has allocated. */
+mp_result mp_rat_copy(mp_rat a, mp_rat c);
+
+/** Sets `r` to zero. The allocated storage of `r` is not changed. */
+void mp_rat_zero(mp_rat r);
+
+/** Sets `c` to the absolute value of `a`. */
+mp_result mp_rat_abs(mp_rat a, mp_rat c);
+
+/** Sets `c` to the absolute value of `a`. */
+mp_result mp_rat_neg(mp_rat a, mp_rat c);
+
+/** Sets `c` to the reciprocal of `a` if the reciprocal is defined.
+ It returns `MP_UNDEF` if `a` is zero. */
+mp_result mp_rat_recip(mp_rat a, mp_rat c);
+
+/** Sets `c` to the sum of `a` and `b`. */
+mp_result mp_rat_add(mp_rat a, mp_rat b, mp_rat c);
+
+/** Sets `c` to the difference of `a` less `b`. */
+mp_result mp_rat_sub(mp_rat a, mp_rat b, mp_rat c);
+
+/** Sets `c` to the product of `a` and `b`. */
+mp_result mp_rat_mul(mp_rat a, mp_rat b, mp_rat c);
+
+/** Sets `c` to the ratio `a / b` if that ratio is defined.
+ It returns `MP_UNDEF` if `b` is zero. */
+mp_result mp_rat_div(mp_rat a, mp_rat b, mp_rat c);
+
+/** Sets `c` to the sum of `a` and integer `b`. */
+mp_result mp_rat_add_int(mp_rat a, mp_int b, mp_rat c);
+
+/** Sets `c` to the difference of `a` less integer `b`. */
+mp_result mp_rat_sub_int(mp_rat a, mp_int b, mp_rat c);
+
+/** Sets `c` to the product of `a` and integer `b`. */
+mp_result mp_rat_mul_int(mp_rat a, mp_int b, mp_rat c);
+
+/** Sets `c` to the ratio `a / b` if that ratio is defined.
+ It returns `MP_UNDEF` if `b` is zero. */
+mp_result mp_rat_div_int(mp_rat a, mp_int b, mp_rat c);
+
+/** Sets `c` to the value of `a` raised to the `b` power.
+ It returns `MP_RANGE` if `b < 0`. */
+mp_result mp_rat_expt(mp_rat a, mp_small b, mp_rat c);
+
+/** Returns the comparator of `a` and `b`. */
+int mp_rat_compare(mp_rat a, mp_rat b);
+
+/** Returns the comparator of the magnitudes of `a` and `b`, disregarding their
+ signs. Neither `a` nor `b` is modified by the comparison. */
+int mp_rat_compare_unsigned(mp_rat a, mp_rat b);
+
+/** Returns the comparator of `r` and zero. */
+int mp_rat_compare_zero(mp_rat r);
+
+/** Returns the comparator of `r` and the signed ratio `n / d`.
+ It returns `MP_UNDEF` if `d` is zero. */
+int mp_rat_compare_value(mp_rat r, mp_small n, mp_small d);
+
+/** Reports whether `r` is an integer, having canonical denominator 1. */
+bool mp_rat_is_integer(mp_rat r);
+
+/** Reports whether the numerator and denominator of `r` can be represented as
+ small signed integers, and if so stores the corresponding values to `num`
+ and `den`. It returns `MP_RANGE` if either cannot be so represented. */
+mp_result mp_rat_to_ints(mp_rat r, mp_small *num, mp_small *den);
+
+/** Converts `r` to a zero-terminated string of the format `"n/d"` with `n` and
+ `d` in the specified radix and writing no more than `limit` bytes to the
+ given output buffer `str`. The output of the numerator includes a sign flag
+ if `r` is negative. Requires `MP_MIN_RADIX <= radix <= MP_MAX_RADIX`. */
+mp_result mp_rat_to_string(mp_rat r, mp_size radix, char *str, int limit);
+
+/** Converts the value of `r` to a string in decimal-point notation with the
+ specified radix, writing no more than `limit` bytes of data to the given
+ output buffer. It generates `prec` digits of precision, and requires
+ `MP_MIN_RADIX <= radix <= MP_MAX_RADIX`.
+
+ Ratios usually must be rounded when they are being converted for output as
+ a decimal value. There are four rounding modes currently supported:
+
+ MP_ROUND_DOWN
+ Truncates the value toward zero.
+ Example: 12.009 to 2dp becomes 12.00
+
+ MP_ROUND_UP
+ Rounds the value away from zero:
+ Example: 12.001 to 2dp becomes 12.01, but
+ 12.000 to 2dp remains 12.00
+
+ MP_ROUND_HALF_DOWN
+ Rounds the value to nearest digit, half goes toward zero.
+ Example: 12.005 to 2dp becomes 12.00, but
+ 12.006 to 2dp becomes 12.01
+
+ MP_ROUND_HALF_UP
+ Rounds the value to nearest digit, half rounds upward.
+ Example: 12.005 to 2dp becomes 12.01, but
+ 12.004 to 2dp becomes 12.00
+*/
+mp_result mp_rat_to_decimal(mp_rat r, mp_size radix, mp_size prec,
+ mp_round_mode round, char *str, int limit);
+
+/** Reports the minimum number of characters required to represent `r` as a
+ zero-terminated string in the given `radix`.
+ Requires `MP_MIN_RADIX <= radix <= MP_MAX_RADIX`. */
+mp_result mp_rat_string_len(mp_rat r, mp_size radix);
+
+/** Reports the length in bytes of the buffer needed to convert `r` using the
+ `mp_rat_to_decimal()` function with the specified `radix` and `prec`. The
+ buffer size estimate may slightly exceed the actual required capacity. */
+mp_result mp_rat_decimal_len(mp_rat r, mp_size radix, mp_size prec);
+
+/** Sets `r` to the value represented by a zero-terminated string `str` in the
+ format `"n/d"` including a sign flag. It returns `MP_UNDEF` if the encoded
+ denominator has value zero. */
+mp_result mp_rat_read_string(mp_rat r, mp_size radix, const char *str);
+
+/** Sets `r` to the value represented by a zero-terminated string `str` in the
+ format `"n/d"` including a sign flag. It returns `MP_UNDEF` if the encoded
+ denominator has value zero. If `end` is not NULL then `*end` is set to
+ point to the first unconsumed character in the string, after parsing.
+*/
+mp_result mp_rat_read_cstring(mp_rat r, mp_size radix, const char *str,
+ char **end);
+
+/** Sets `r` to the value represented by a zero-terminated string `str` having
+ one of the following formats, each with an optional leading sign flag:
+
+ n : integer format, e.g. "123"
+ n/d : ratio format, e.g., "-12/5"
+ z.ffff : decimal format, e.g., "1.627"
+
+ It returns `MP_UNDEF` if the effective denominator is zero. If `end` is not
+ NULL then `*end` is set to point to the first unconsumed character in the
+ string, after parsing.
+*/
+mp_result mp_rat_read_ustring(mp_rat r, mp_size radix, const char *str,
+ char **end);
+
+/** Sets `r` to the value represented by a zero-terminated string `str` in the
+ format `"z.ffff"` including a sign flag. It returns `MP_UNDEF` if the
+ effective denominator. */
+mp_result mp_rat_read_decimal(mp_rat r, mp_size radix, const char *str);
+
+/** Sets `r` to the value represented by a zero-terminated string `str` in the
+ format `"z.ffff"` including a sign flag. It returns `MP_UNDEF` if the
+ effective denominator. If `end` is not NULL then `*end` is set to point to
+ the first unconsumed character in the string, after parsing. */
+mp_result mp_rat_read_cdecimal(mp_rat r, mp_size radix, const char *str,
+ char **end);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* IMRAT_H_ */
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/aff.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/aff.h
new file mode 100644
index 00000000000..c417be16542
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/aff.h
@@ -0,0 +1,1476 @@
+#ifndef ISL_AFF_H
+#define ISL_AFF_H
+
+#include <isl/stdint.h>
+#include <isl/local_space.h>
+#include <isl/printer.h>
+#include <isl/id_type.h>
+#include <isl/set_type.h>
+#include <isl/aff_type.h>
+#include <isl/list.h>
+#include <isl/multi.h>
+#include <isl/union_set_type.h>
+#include <isl/val_type.h>
+#include <isl/point.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+__isl_overload
+__isl_give isl_aff *isl_aff_zero_on_domain_space(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_aff *isl_space_zero_aff_on_domain(__isl_take isl_space *space);
+__isl_give isl_aff *isl_aff_zero_on_domain(__isl_take isl_local_space *ls);
+__isl_give isl_aff *isl_aff_val_on_domain_space(__isl_take isl_space *space,
+ __isl_take isl_val *val);
+__isl_give isl_aff *isl_aff_val_on_domain(__isl_take isl_local_space *ls,
+ __isl_take isl_val *val);
+__isl_give isl_aff *isl_aff_var_on_domain(__isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_aff *isl_aff_nan_on_domain_space(__isl_take isl_space *space);
+__isl_give isl_aff *isl_aff_nan_on_domain(__isl_take isl_local_space *ls);
+__isl_give isl_aff *isl_aff_param_on_domain_space_id(
+ __isl_take isl_space *space, __isl_take isl_id *id);
+__isl_overload
+__isl_give isl_aff *isl_space_param_aff_on_domain_id(
+ __isl_take isl_space *space, __isl_take isl_id *id);
+
+__isl_give isl_aff *isl_aff_copy(__isl_keep isl_aff *aff);
+__isl_null isl_aff *isl_aff_free(__isl_take isl_aff *aff);
+
+isl_ctx *isl_aff_get_ctx(__isl_keep isl_aff *aff);
+uint32_t isl_aff_get_hash(__isl_keep isl_aff *aff);
+
+isl_bool isl_aff_involves_locals(__isl_keep isl_aff *aff);
+
+isl_size isl_aff_dim(__isl_keep isl_aff *aff, enum isl_dim_type type);
+isl_bool isl_aff_involves_dims(__isl_keep isl_aff *aff,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+__isl_give isl_space *isl_aff_get_domain_space(__isl_keep isl_aff *aff);
+__isl_give isl_space *isl_aff_get_space(__isl_keep isl_aff *aff);
+__isl_give isl_local_space *isl_aff_get_domain_local_space(
+ __isl_keep isl_aff *aff);
+__isl_give isl_local_space *isl_aff_get_local_space(__isl_keep isl_aff *aff);
+
+const char *isl_aff_get_dim_name(__isl_keep isl_aff *aff,
+ enum isl_dim_type type, unsigned pos);
+__isl_export
+__isl_give isl_val *isl_aff_get_constant_val(__isl_keep isl_aff *aff);
+__isl_give isl_val *isl_aff_get_coefficient_val(__isl_keep isl_aff *aff,
+ enum isl_dim_type type, int pos);
+int isl_aff_coefficient_sgn(__isl_keep isl_aff *aff,
+ enum isl_dim_type type, int pos);
+__isl_give isl_val *isl_aff_get_denominator_val(__isl_keep isl_aff *aff);
+__isl_give isl_aff *isl_aff_set_constant_si(__isl_take isl_aff *aff, int v);
+__isl_give isl_aff *isl_aff_set_constant_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *v);
+__isl_give isl_aff *isl_aff_set_coefficient_si(__isl_take isl_aff *aff,
+ enum isl_dim_type type, int pos, int v);
+__isl_give isl_aff *isl_aff_set_coefficient_val(__isl_take isl_aff *aff,
+ enum isl_dim_type type, int pos, __isl_take isl_val *v);
+__isl_give isl_aff *isl_aff_add_constant_si(__isl_take isl_aff *aff, int v);
+__isl_overload
+__isl_give isl_aff *isl_aff_add_constant_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *v);
+__isl_give isl_aff *isl_aff_add_constant_num_si(__isl_take isl_aff *aff, int v);
+__isl_give isl_aff *isl_aff_add_coefficient_si(__isl_take isl_aff *aff,
+ enum isl_dim_type type, int pos, int v);
+__isl_give isl_aff *isl_aff_add_coefficient_val(__isl_take isl_aff *aff,
+ enum isl_dim_type type, int pos, __isl_take isl_val *v);
+
+__isl_export
+isl_bool isl_aff_is_cst(__isl_keep isl_aff *aff);
+
+__isl_give isl_aff *isl_aff_set_tuple_id(__isl_take isl_aff *aff,
+ enum isl_dim_type type, __isl_take isl_id *id);
+__isl_give isl_aff *isl_aff_set_dim_name(__isl_take isl_aff *aff,
+ enum isl_dim_type type, unsigned pos, const char *s);
+__isl_give isl_aff *isl_aff_set_dim_id(__isl_take isl_aff *aff,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id);
+
+int isl_aff_find_dim_by_name(__isl_keep isl_aff *aff, enum isl_dim_type type,
+ const char *name);
+
+isl_bool isl_aff_plain_is_equal(__isl_keep isl_aff *aff1,
+ __isl_keep isl_aff *aff2);
+isl_bool isl_aff_plain_is_zero(__isl_keep isl_aff *aff);
+isl_bool isl_aff_is_nan(__isl_keep isl_aff *aff);
+
+__isl_give isl_aff *isl_aff_get_div(__isl_keep isl_aff *aff, int pos);
+
+__isl_give isl_aff *isl_aff_from_range(__isl_take isl_aff *aff);
+
+__isl_export
+__isl_give isl_aff *isl_aff_neg(__isl_take isl_aff *aff);
+__isl_export
+__isl_give isl_aff *isl_aff_ceil(__isl_take isl_aff *aff);
+__isl_export
+__isl_give isl_aff *isl_aff_floor(__isl_take isl_aff *aff);
+__isl_overload
+__isl_give isl_aff *isl_aff_mod_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *mod);
+
+__isl_export
+__isl_give isl_aff *isl_aff_mul(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_export
+__isl_give isl_aff *isl_aff_div(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_export
+__isl_give isl_aff *isl_aff_add(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_export
+__isl_give isl_aff *isl_aff_sub(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+
+__isl_overload
+__isl_give isl_aff *isl_aff_scale_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *v);
+__isl_give isl_aff *isl_aff_scale_down_ui(__isl_take isl_aff *aff, unsigned f);
+__isl_overload
+__isl_give isl_aff *isl_aff_scale_down_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *v);
+
+__isl_give isl_aff *isl_aff_insert_dims(__isl_take isl_aff *aff,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_aff *isl_aff_add_dims(__isl_take isl_aff *aff,
+ enum isl_dim_type type, unsigned n);
+__isl_give isl_aff *isl_aff_move_dims(__isl_take isl_aff *aff,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+__isl_give isl_aff *isl_aff_drop_dims(__isl_take isl_aff *aff,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_aff *isl_aff_project_domain_on_params(__isl_take isl_aff *aff);
+__isl_export
+__isl_give isl_aff *isl_aff_unbind_params_insert_domain(
+ __isl_take isl_aff *aff, __isl_take isl_multi_id *domain);
+
+__isl_give isl_aff *isl_aff_align_params(__isl_take isl_aff *aff,
+ __isl_take isl_space *model);
+
+__isl_export
+__isl_give isl_aff *isl_aff_gist(__isl_take isl_aff *aff,
+ __isl_take isl_set *context);
+__isl_give isl_aff *isl_aff_gist_params(__isl_take isl_aff *aff,
+ __isl_take isl_set *context);
+
+__isl_export
+__isl_give isl_val *isl_aff_eval(__isl_take isl_aff *aff,
+ __isl_take isl_point *pnt);
+
+__isl_give isl_aff *isl_aff_pullback_aff(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_overload
+__isl_give isl_aff *isl_aff_pullback_multi_aff(__isl_take isl_aff *aff,
+ __isl_take isl_multi_aff *ma);
+
+__isl_give isl_basic_set *isl_aff_zero_basic_set(__isl_take isl_aff *aff);
+__isl_give isl_basic_set *isl_aff_neg_basic_set(__isl_take isl_aff *aff);
+
+__isl_give isl_basic_set *isl_aff_eq_basic_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_export
+__isl_give isl_set *isl_aff_eq_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_export
+__isl_give isl_set *isl_aff_ne_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_give isl_basic_set *isl_aff_le_basic_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_export
+__isl_give isl_set *isl_aff_le_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_give isl_basic_set *isl_aff_lt_basic_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_export
+__isl_give isl_set *isl_aff_lt_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_give isl_basic_set *isl_aff_ge_basic_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_export
+__isl_give isl_set *isl_aff_ge_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_give isl_basic_set *isl_aff_gt_basic_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+__isl_export
+__isl_give isl_set *isl_aff_gt_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2);
+
+__isl_overload
+__isl_give isl_basic_set *isl_aff_bind_id(__isl_take isl_aff *aff,
+ __isl_take isl_id *id);
+
+__isl_constructor
+__isl_give isl_aff *isl_aff_read_from_str(isl_ctx *ctx, const char *str);
+__isl_give char *isl_aff_to_str(__isl_keep isl_aff *aff);
+__isl_give isl_printer *isl_printer_print_aff(__isl_take isl_printer *p,
+ __isl_keep isl_aff *aff);
+void isl_aff_dump(__isl_keep isl_aff *aff);
+
+isl_ctx *isl_pw_aff_get_ctx(__isl_keep isl_pw_aff *pwaff);
+uint32_t isl_pw_aff_get_hash(__isl_keep isl_pw_aff *pa);
+__isl_give isl_space *isl_pw_aff_get_domain_space(__isl_keep isl_pw_aff *pwaff);
+__isl_export
+__isl_give isl_space *isl_pw_aff_get_space(__isl_keep isl_pw_aff *pwaff);
+
+__isl_constructor
+__isl_give isl_pw_aff *isl_pw_aff_from_aff(__isl_take isl_aff *aff);
+__isl_give isl_pw_aff *isl_pw_aff_empty(__isl_take isl_space *space);
+__isl_give isl_pw_aff *isl_pw_aff_alloc(__isl_take isl_set *set,
+ __isl_take isl_aff *aff);
+__isl_give isl_pw_aff *isl_pw_aff_zero_on_domain(
+ __isl_take isl_local_space *ls);
+__isl_give isl_pw_aff *isl_pw_aff_var_on_domain(__isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_pw_aff *isl_pw_aff_nan_on_domain_space(
+ __isl_take isl_space *space);
+__isl_give isl_pw_aff *isl_pw_aff_nan_on_domain(__isl_take isl_local_space *ls);
+__isl_give isl_pw_aff *isl_pw_aff_val_on_domain(__isl_take isl_set *domain,
+ __isl_take isl_val *v);
+__isl_overload
+__isl_give isl_pw_aff *isl_pw_aff_param_on_domain_id(
+ __isl_take isl_set *domain, __isl_take isl_id *id);
+
+__isl_export
+__isl_give isl_pw_aff *isl_set_indicator_function(__isl_take isl_set *set);
+
+const char *isl_pw_aff_get_dim_name(__isl_keep isl_pw_aff *pa,
+ enum isl_dim_type type, unsigned pos);
+isl_bool isl_pw_aff_has_dim_id(__isl_keep isl_pw_aff *pa,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_id *isl_pw_aff_get_dim_id(__isl_keep isl_pw_aff *pa,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_pw_aff *isl_pw_aff_set_dim_id(__isl_take isl_pw_aff *pma,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id);
+
+int isl_pw_aff_find_dim_by_name(__isl_keep isl_pw_aff *pa,
+ enum isl_dim_type type, const char *name);
+
+isl_bool isl_pw_aff_is_empty(__isl_keep isl_pw_aff *pwaff);
+isl_bool isl_pw_aff_involves_nan(__isl_keep isl_pw_aff *pa);
+int isl_pw_aff_plain_cmp(__isl_keep isl_pw_aff *pa1,
+ __isl_keep isl_pw_aff *pa2);
+isl_bool isl_pw_aff_plain_is_equal(__isl_keep isl_pw_aff *pwaff1,
+ __isl_keep isl_pw_aff *pwaff2);
+isl_bool isl_pw_aff_is_equal(__isl_keep isl_pw_aff *pa1,
+ __isl_keep isl_pw_aff *pa2);
+
+__isl_give isl_pw_aff *isl_pw_aff_union_min(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_give isl_pw_aff *isl_pw_aff_union_max(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_union_add(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+
+__isl_give isl_pw_aff *isl_pw_aff_copy(__isl_keep isl_pw_aff *pwaff);
+__isl_null isl_pw_aff *isl_pw_aff_free(__isl_take isl_pw_aff *pwaff);
+
+isl_size isl_pw_aff_dim(__isl_keep isl_pw_aff *pwaff, enum isl_dim_type type);
+isl_bool isl_pw_aff_involves_param_id(__isl_keep isl_pw_aff *pa,
+ __isl_keep isl_id *id);
+isl_bool isl_pw_aff_involves_dims(__isl_keep isl_pw_aff *pwaff,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+isl_bool isl_pw_aff_is_cst(__isl_keep isl_pw_aff *pwaff);
+
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_insert_domain(__isl_take isl_pw_aff *pa,
+ __isl_take isl_space *domain);
+__isl_give isl_pw_aff *isl_pw_aff_project_domain_on_params(
+ __isl_take isl_pw_aff *pa);
+
+__isl_give isl_pw_aff *isl_pw_aff_align_params(__isl_take isl_pw_aff *pwaff,
+ __isl_take isl_space *model);
+__isl_give isl_pw_aff *isl_pw_aff_drop_unused_params(
+ __isl_take isl_pw_aff *pa);
+
+isl_bool isl_pw_aff_has_tuple_id(__isl_keep isl_pw_aff *pa,
+ enum isl_dim_type type);
+__isl_give isl_id *isl_pw_aff_get_tuple_id(__isl_keep isl_pw_aff *pa,
+ enum isl_dim_type type);
+__isl_give isl_pw_aff *isl_pw_aff_set_tuple_id(__isl_take isl_pw_aff *pwaff,
+ enum isl_dim_type type, __isl_take isl_id *id);
+__isl_give isl_pw_aff *isl_pw_aff_reset_tuple_id(__isl_take isl_pw_aff *pa,
+ enum isl_dim_type type);
+__isl_give isl_pw_aff *isl_pw_aff_reset_user(__isl_take isl_pw_aff *pa);
+
+__isl_give isl_set *isl_pw_aff_params(__isl_take isl_pw_aff *pwa);
+__isl_export
+__isl_give isl_set *isl_pw_aff_domain(__isl_take isl_pw_aff *pwaff);
+__isl_give isl_pw_aff *isl_pw_aff_from_range(__isl_take isl_pw_aff *pwa);
+
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_min(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_max(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_mul(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_div(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_add(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_sub(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_neg(__isl_take isl_pw_aff *pwaff);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_ceil(__isl_take isl_pw_aff *pwaff);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_floor(__isl_take isl_pw_aff *pwaff);
+__isl_overload
+__isl_give isl_pw_aff *isl_pw_aff_mod_val(__isl_take isl_pw_aff *pa,
+ __isl_take isl_val *mod);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_tdiv_q(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_tdiv_r(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2);
+
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_intersect_params(__isl_take isl_pw_aff *pa,
+ __isl_take isl_set *set);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_intersect_domain(__isl_take isl_pw_aff *pa,
+ __isl_take isl_set *set);
+__isl_give isl_pw_aff *isl_pw_aff_intersect_domain_wrapped_domain(
+ __isl_take isl_pw_aff *pa, __isl_take isl_set *set);
+__isl_give isl_pw_aff *isl_pw_aff_intersect_domain_wrapped_range(
+ __isl_take isl_pw_aff *pa, __isl_take isl_set *set);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_subtract_domain(__isl_take isl_pw_aff *pa,
+ __isl_take isl_set *set);
+
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_cond(__isl_take isl_pw_aff *cond,
+ __isl_take isl_pw_aff *pwaff_true, __isl_take isl_pw_aff *pwaff_false);
+
+__isl_overload
+__isl_give isl_pw_aff *isl_pw_aff_add_constant_val(__isl_take isl_pw_aff *pa,
+ __isl_take isl_val *v);
+__isl_overload
+__isl_give isl_pw_aff *isl_pw_aff_scale_val(__isl_take isl_pw_aff *pa,
+ __isl_take isl_val *v);
+__isl_overload
+__isl_give isl_pw_aff *isl_pw_aff_scale_down_val(__isl_take isl_pw_aff *pa,
+ __isl_take isl_val *f);
+
+__isl_give isl_pw_aff *isl_pw_aff_insert_dims(__isl_take isl_pw_aff *pwaff,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_pw_aff *isl_pw_aff_add_dims(__isl_take isl_pw_aff *pwaff,
+ enum isl_dim_type type, unsigned n);
+__isl_give isl_pw_aff *isl_pw_aff_move_dims(__isl_take isl_pw_aff *pa,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+__isl_give isl_pw_aff *isl_pw_aff_drop_dims(__isl_take isl_pw_aff *pwaff,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_coalesce(__isl_take isl_pw_aff *pa);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_gist(__isl_take isl_pw_aff *pwaff,
+ __isl_take isl_set *context);
+__isl_give isl_pw_aff *isl_pw_aff_gist_params(__isl_take isl_pw_aff *pwaff,
+ __isl_take isl_set *context);
+
+__isl_export
+__isl_give isl_val *isl_pw_aff_eval(__isl_take isl_pw_aff *pa,
+ __isl_take isl_point *pnt);
+
+__isl_overload
+__isl_give isl_pw_aff *isl_pw_aff_pullback_multi_aff(
+ __isl_take isl_pw_aff *pa, __isl_take isl_multi_aff *ma);
+__isl_overload
+__isl_give isl_pw_aff *isl_pw_aff_pullback_pw_multi_aff(
+ __isl_take isl_pw_aff *pa, __isl_take isl_pw_multi_aff *pma);
+__isl_overload
+__isl_give isl_pw_aff *isl_pw_aff_pullback_multi_pw_aff(
+ __isl_take isl_pw_aff *pa, __isl_take isl_multi_pw_aff *mpa);
+
+isl_size isl_pw_aff_n_piece(__isl_keep isl_pw_aff *pwaff);
+isl_stat isl_pw_aff_foreach_piece(__isl_keep isl_pw_aff *pwaff,
+ isl_stat (*fn)(__isl_take isl_set *set, __isl_take isl_aff *aff,
+ void *user), void *user);
+isl_bool isl_pw_aff_every_piece(__isl_keep isl_pw_aff *pa,
+ isl_bool (*test)(__isl_keep isl_set *set, __isl_keep isl_aff *aff,
+ void *user), void *user);
+__isl_export
+isl_bool isl_pw_aff_isa_aff(__isl_keep isl_pw_aff *pa);
+__isl_export
+__isl_give isl_aff *isl_pw_aff_as_aff(__isl_take isl_pw_aff *pa);
+
+__isl_export
+__isl_give isl_map *isl_pw_aff_as_map(__isl_take isl_pw_aff *pa);
+__isl_give isl_set *isl_set_from_pw_aff(__isl_take isl_pw_aff *pwaff);
+__isl_give isl_map *isl_map_from_pw_aff(__isl_take isl_pw_aff *pwaff);
+
+__isl_give isl_set *isl_pw_aff_pos_set(__isl_take isl_pw_aff *pa);
+__isl_give isl_set *isl_pw_aff_nonneg_set(__isl_take isl_pw_aff *pwaff);
+__isl_give isl_set *isl_pw_aff_zero_set(__isl_take isl_pw_aff *pwaff);
+__isl_give isl_set *isl_pw_aff_non_zero_set(__isl_take isl_pw_aff *pwaff);
+
+__isl_export
+__isl_give isl_set *isl_pw_aff_eq_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_export
+__isl_give isl_set *isl_pw_aff_ne_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_export
+__isl_give isl_set *isl_pw_aff_le_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_export
+__isl_give isl_set *isl_pw_aff_lt_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_export
+__isl_give isl_set *isl_pw_aff_ge_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+__isl_export
+__isl_give isl_set *isl_pw_aff_gt_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2);
+
+__isl_give isl_map *isl_pw_aff_eq_map(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2);
+__isl_give isl_map *isl_pw_aff_le_map(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2);
+__isl_give isl_map *isl_pw_aff_lt_map(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2);
+__isl_give isl_map *isl_pw_aff_ge_map(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2);
+__isl_give isl_map *isl_pw_aff_gt_map(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2);
+
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_bind_domain(__isl_take isl_pw_aff *pa,
+ __isl_take isl_multi_id *tuple);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_aff_bind_domain_wrapped_domain(
+ __isl_take isl_pw_aff *pa, __isl_take isl_multi_id *tuple);
+__isl_overload
+__isl_give isl_set *isl_pw_aff_bind_id(__isl_take isl_pw_aff *pa,
+ __isl_take isl_id *id);
+
+__isl_constructor
+__isl_give isl_pw_aff *isl_pw_aff_read_from_str(isl_ctx *ctx, const char *str);
+__isl_give char *isl_pw_aff_to_str(__isl_keep isl_pw_aff *pa);
+__isl_give isl_printer *isl_printer_print_pw_aff(__isl_take isl_printer *p,
+ __isl_keep isl_pw_aff *pwaff);
+void isl_pw_aff_dump(__isl_keep isl_pw_aff *pwaff);
+
+__isl_give isl_pw_aff *isl_pw_aff_list_min(__isl_take isl_pw_aff_list *list);
+__isl_give isl_pw_aff *isl_pw_aff_list_max(__isl_take isl_pw_aff_list *list);
+
+__isl_give isl_set *isl_pw_aff_list_eq_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2);
+__isl_give isl_set *isl_pw_aff_list_ne_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2);
+__isl_give isl_set *isl_pw_aff_list_le_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2);
+__isl_give isl_set *isl_pw_aff_list_lt_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2);
+__isl_give isl_set *isl_pw_aff_list_ge_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2);
+__isl_give isl_set *isl_pw_aff_list_gt_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2);
+
+ISL_DECLARE_MULTI(aff)
+ISL_DECLARE_MULTI_IDENTITY(aff)
+ISL_DECLARE_MULTI_CMP(aff)
+ISL_DECLARE_MULTI_ARITH(aff)
+ISL_DECLARE_MULTI_ADD_CONSTANT(aff)
+ISL_DECLARE_MULTI_ZERO(aff)
+ISL_DECLARE_MULTI_NAN(aff)
+ISL_DECLARE_MULTI_DIMS(aff)
+ISL_DECLARE_MULTI_INSERT_DOMAIN(aff)
+ISL_DECLARE_MULTI_LOCALS(aff)
+ISL_DECLARE_MULTI_DIM_ID(aff)
+ISL_DECLARE_MULTI_TUPLE_ID(aff)
+ISL_DECLARE_MULTI_WITH_DOMAIN(aff)
+ISL_DECLARE_MULTI_BIND_DOMAIN(aff)
+ISL_DECLARE_MULTI_UNBIND_PARAMS(aff)
+
+__isl_constructor
+__isl_give isl_multi_aff *isl_multi_aff_from_aff(__isl_take isl_aff *aff);
+__isl_export
+__isl_give isl_multi_aff *isl_multi_aff_domain_map(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_multi_aff *isl_space_domain_map_multi_aff(
+ __isl_take isl_space *space);
+__isl_export
+__isl_give isl_multi_aff *isl_multi_aff_range_map(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_multi_aff *isl_space_range_map_multi_aff(
+ __isl_take isl_space *space);
+__isl_give isl_multi_aff *isl_multi_aff_project_out_map(
+ __isl_take isl_space *space, enum isl_dim_type type,
+ unsigned first, unsigned n);
+
+__isl_overload
+__isl_give isl_multi_aff *isl_multi_aff_multi_val_on_domain_space(
+ __isl_take isl_space *space, __isl_take isl_multi_val *mv);
+__isl_overload
+__isl_give isl_multi_aff *isl_space_multi_aff_on_domain_multi_val(
+ __isl_take isl_space *space, __isl_take isl_multi_val *mv);
+__isl_give isl_multi_aff *isl_multi_aff_multi_val_on_space(
+ __isl_take isl_space *space, __isl_take isl_multi_val *mv);
+
+__isl_export
+__isl_give isl_multi_val *isl_multi_aff_get_constant_multi_val(
+ __isl_keep isl_multi_aff *ma);
+
+__isl_export
+__isl_give isl_multi_aff *isl_multi_aff_floor(__isl_take isl_multi_aff *ma);
+
+__isl_give isl_multi_aff *isl_multi_aff_gist_params(
+ __isl_take isl_multi_aff *maff, __isl_take isl_set *context);
+__isl_export
+__isl_give isl_multi_aff *isl_multi_aff_gist(__isl_take isl_multi_aff *maff,
+ __isl_take isl_set *context);
+
+__isl_give isl_multi_aff *isl_multi_aff_lift(__isl_take isl_multi_aff *maff,
+ __isl_give isl_local_space **ls);
+
+__isl_overload
+__isl_give isl_multi_aff *isl_multi_aff_pullback_multi_aff(
+ __isl_take isl_multi_aff *ma1, __isl_take isl_multi_aff *ma2);
+
+__isl_give isl_multi_aff *isl_multi_aff_move_dims(__isl_take isl_multi_aff *ma,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+
+__isl_give isl_set *isl_multi_aff_lex_lt_set(__isl_take isl_multi_aff *ma1,
+ __isl_take isl_multi_aff *ma2);
+__isl_give isl_set *isl_multi_aff_lex_le_set(__isl_take isl_multi_aff *ma1,
+ __isl_take isl_multi_aff *ma2);
+__isl_give isl_set *isl_multi_aff_lex_gt_set(__isl_take isl_multi_aff *ma1,
+ __isl_take isl_multi_aff *ma2);
+__isl_give isl_set *isl_multi_aff_lex_ge_set(__isl_take isl_multi_aff *ma1,
+ __isl_take isl_multi_aff *ma2);
+
+__isl_export
+__isl_give isl_basic_set *isl_multi_aff_bind(__isl_take isl_multi_aff *ma,
+ __isl_take isl_multi_id *tuple);
+
+__isl_give char *isl_multi_aff_to_str(__isl_keep isl_multi_aff *ma);
+__isl_give isl_printer *isl_printer_print_multi_aff(__isl_take isl_printer *p,
+ __isl_keep isl_multi_aff *maff);
+
+__isl_constructor
+__isl_give isl_multi_aff *isl_multi_aff_read_from_str(isl_ctx *ctx,
+ const char *str);
+void isl_multi_aff_dump(__isl_keep isl_multi_aff *maff);
+
+ISL_DECLARE_MULTI(pw_aff)
+ISL_DECLARE_MULTI_IDENTITY(pw_aff)
+ISL_DECLARE_MULTI_ARITH(pw_aff)
+ISL_DECLARE_MULTI_MIN_MAX(pw_aff)
+ISL_DECLARE_MULTI_ADD_CONSTANT(pw_aff)
+ISL_DECLARE_MULTI_ZERO(pw_aff)
+ISL_DECLARE_MULTI_NAN(pw_aff)
+ISL_DECLARE_MULTI_DIMS(pw_aff)
+ISL_DECLARE_MULTI_DIM_ID(pw_aff)
+ISL_DECLARE_MULTI_INSERT_DOMAIN(pw_aff)
+ISL_DECLARE_MULTI_TUPLE_ID(pw_aff)
+ISL_DECLARE_MULTI_WITH_DOMAIN(pw_aff)
+ISL_DECLARE_MULTI_BIND_DOMAIN(pw_aff)
+ISL_DECLARE_MULTI_PARAM(pw_aff)
+ISL_DECLARE_MULTI_UNBIND_PARAMS(pw_aff)
+
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_zero(__isl_take isl_space *space);
+__isl_overload
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_identity_on_domain_space(
+ __isl_take isl_space *space);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_space_identity_pw_multi_aff_on_domain(
+ __isl_take isl_space *space);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_identity(
+ __isl_take isl_space *space);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_domain_map(
+ __isl_take isl_space *space);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_space_domain_map_pw_multi_aff(
+ __isl_take isl_space *space);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_map(
+ __isl_take isl_space *space);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_space_range_map_pw_multi_aff(
+ __isl_take isl_space *space);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_project_out_map(
+ __isl_take isl_space *space, enum isl_dim_type type,
+ unsigned first, unsigned n);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_multi_aff_to_pw_multi_aff(
+ __isl_take isl_multi_aff *ma);
+__isl_constructor
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_multi_aff(
+ __isl_take isl_multi_aff *ma);
+__isl_constructor
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_pw_aff(
+ __isl_take isl_pw_aff *pa);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_alloc(__isl_take isl_set *set,
+ __isl_take isl_multi_aff *maff);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_copy(
+ __isl_keep isl_pw_multi_aff *pma);
+__isl_null isl_pw_multi_aff *isl_pw_multi_aff_free(
+ __isl_take isl_pw_multi_aff *pma);
+
+isl_size isl_pw_multi_aff_dim(__isl_keep isl_pw_multi_aff *pma,
+ enum isl_dim_type type);
+__isl_export
+isl_bool isl_pw_multi_aff_involves_locals(__isl_keep isl_pw_multi_aff *pma);
+isl_bool isl_pw_multi_aff_involves_param_id(__isl_keep isl_pw_multi_aff *pma,
+ __isl_keep isl_id *id);
+isl_bool isl_pw_multi_aff_involves_dims(__isl_keep isl_pw_multi_aff *pma,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_export
+__isl_give isl_pw_aff *isl_pw_multi_aff_get_at(
+ __isl_keep isl_pw_multi_aff *pma, int pos);
+__isl_give isl_pw_aff *isl_pw_multi_aff_get_pw_aff(
+ __isl_keep isl_pw_multi_aff *pma, int pos);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_set_pw_aff(
+ __isl_take isl_pw_multi_aff *pma, unsigned pos,
+ __isl_take isl_pw_aff *pa);
+
+isl_ctx *isl_pw_multi_aff_get_ctx(__isl_keep isl_pw_multi_aff *pma);
+__isl_give isl_space *isl_pw_multi_aff_get_domain_space(
+ __isl_keep isl_pw_multi_aff *pma);
+__isl_export
+__isl_give isl_space *isl_pw_multi_aff_get_space(
+ __isl_keep isl_pw_multi_aff *pma);
+isl_bool isl_pw_multi_aff_has_tuple_name(__isl_keep isl_pw_multi_aff *pma,
+ enum isl_dim_type type);
+const char *isl_pw_multi_aff_get_tuple_name(__isl_keep isl_pw_multi_aff *pma,
+ enum isl_dim_type type);
+__isl_export
+__isl_give isl_id *isl_pw_multi_aff_get_range_tuple_id(
+ __isl_keep isl_pw_multi_aff *pma);
+__isl_give isl_id *isl_pw_multi_aff_get_tuple_id(
+ __isl_keep isl_pw_multi_aff *pma, enum isl_dim_type type);
+__isl_export
+isl_bool isl_pw_multi_aff_has_range_tuple_id(__isl_keep isl_pw_multi_aff *pma);
+isl_bool isl_pw_multi_aff_has_tuple_id(__isl_keep isl_pw_multi_aff *pma,
+ enum isl_dim_type type);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_set_tuple_id(
+ __isl_take isl_pw_multi_aff *pma,
+ enum isl_dim_type type, __isl_take isl_id *id);
+__isl_overload
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_set_range_tuple_id(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_id *id);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_reset_tuple_id(
+ __isl_take isl_pw_multi_aff *pma, enum isl_dim_type type);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_reset_user(
+ __isl_take isl_pw_multi_aff *pma);
+
+int isl_pw_multi_aff_find_dim_by_name(__isl_keep isl_pw_multi_aff *pma,
+ enum isl_dim_type type, const char *name);
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_drop_dims(
+ __isl_take isl_pw_multi_aff *pma,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+__isl_export
+__isl_give isl_set *isl_pw_multi_aff_domain(__isl_take isl_pw_multi_aff *pma);
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_empty(__isl_take isl_space *space);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_domain(
+ __isl_take isl_set *set);
+
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_multi_val_on_domain(
+ __isl_take isl_set *domain, __isl_take isl_multi_val *mv);
+__isl_overload
+__isl_give isl_pw_multi_aff *isl_set_pw_multi_aff_on_domain_multi_val(
+ __isl_take isl_set *domain, __isl_take isl_multi_val *mv);
+
+const char *isl_pw_multi_aff_get_dim_name(__isl_keep isl_pw_multi_aff *pma,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_id *isl_pw_multi_aff_get_dim_id(
+ __isl_keep isl_pw_multi_aff *pma, enum isl_dim_type type,
+ unsigned pos);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_set_dim_id(
+ __isl_take isl_pw_multi_aff *pma,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id);
+
+isl_bool isl_pw_multi_aff_involves_nan(__isl_keep isl_pw_multi_aff *pma);
+isl_bool isl_pw_multi_aff_plain_is_equal(__isl_keep isl_pw_multi_aff *pma1,
+ __isl_keep isl_pw_multi_aff *pma2);
+isl_bool isl_pw_multi_aff_is_equal(__isl_keep isl_pw_multi_aff *pma1,
+ __isl_keep isl_pw_multi_aff *pma2);
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_fix_si(
+ __isl_take isl_pw_multi_aff *pma, enum isl_dim_type type,
+ unsigned pos, int value);
+
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_add(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2);
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_neg(
+ __isl_take isl_pw_multi_aff *pma);
+
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_add(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_sub(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2);
+
+__isl_overload
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_add_constant_val(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_val *v);
+__isl_overload
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_add_constant_multi_val(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_multi_val *mv);
+__isl_overload
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_scale_val(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_val *v);
+__isl_overload
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_scale_down_val(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_val *v);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_scale_multi_val(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_multi_val *mv);
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_lexmin(
+ __isl_take isl_pw_multi_aff *pma1,
+ __isl_take isl_pw_multi_aff *pma2);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_lexmax(
+ __isl_take isl_pw_multi_aff *pma1,
+ __isl_take isl_pw_multi_aff *pma2);
+
+__isl_give isl_multi_aff *isl_multi_aff_flatten_domain(
+ __isl_take isl_multi_aff *ma);
+
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_product(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_flat_range_product(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_product(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_factor_domain(
+ __isl_take isl_pw_multi_aff *pma);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_factor_range(
+ __isl_take isl_pw_multi_aff *pma);
+
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_intersect_params(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_intersect_domain(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_intersect_domain_wrapped_domain(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_intersect_domain_wrapped_range(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_subtract_domain(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set);
+
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_insert_domain(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_space *domain);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_project_domain_on_params(
+ __isl_take isl_pw_multi_aff *pma);
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_align_params(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_space *model);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_drop_unused_params(
+ __isl_take isl_pw_multi_aff *pma);
+
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_coalesce(
+ __isl_take isl_pw_multi_aff *pma);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_gist_params(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_gist(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_set *set);
+
+__isl_overload
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_pullback_multi_aff(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_multi_aff *ma);
+__isl_overload
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_pullback_pw_multi_aff(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2);
+__isl_overload
+__isl_give isl_pw_multi_aff *
+isl_pw_multi_aff_preimage_domain_wrapped_domain_pw_multi_aff(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2);
+
+__isl_export
+isl_size isl_pw_multi_aff_n_piece(__isl_keep isl_pw_multi_aff *pma);
+__isl_export
+isl_stat isl_pw_multi_aff_foreach_piece(__isl_keep isl_pw_multi_aff *pma,
+ isl_stat (*fn)(__isl_take isl_set *set, __isl_take isl_multi_aff *maff,
+ void *user), void *user);
+isl_bool isl_pw_multi_aff_every_piece(__isl_keep isl_pw_multi_aff *pma,
+ isl_bool (*test)(__isl_keep isl_set *set, __isl_keep isl_multi_aff *ma,
+ void *user), void *user);
+__isl_export
+isl_bool isl_pw_multi_aff_isa_multi_aff(__isl_keep isl_pw_multi_aff *pma);
+__isl_export
+__isl_give isl_multi_aff *isl_pw_multi_aff_as_multi_aff(
+ __isl_take isl_pw_multi_aff *pma);
+
+__isl_export
+__isl_give isl_map *isl_pw_multi_aff_as_map(__isl_take isl_pw_multi_aff *pma);
+__isl_give isl_map *isl_map_from_pw_multi_aff(__isl_take isl_pw_multi_aff *pma);
+__isl_export
+__isl_give isl_set *isl_pw_multi_aff_as_set(__isl_take isl_pw_multi_aff *pma);
+__isl_give isl_set *isl_set_from_pw_multi_aff(__isl_take isl_pw_multi_aff *pma);
+
+__isl_give char *isl_pw_multi_aff_to_str(__isl_keep isl_pw_multi_aff *pma);
+__isl_give isl_printer *isl_printer_print_pw_multi_aff(__isl_take isl_printer *p,
+ __isl_keep isl_pw_multi_aff *pma);
+
+__isl_export
+__isl_give isl_pw_multi_aff *isl_set_as_pw_multi_aff(__isl_take isl_set *set);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_set(__isl_take isl_set *set);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_map_as_pw_multi_aff(__isl_take isl_map *map);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_map(__isl_take isl_map *map);
+
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_bind_domain(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_multi_id *tuple);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_bind_domain_wrapped_domain(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_multi_id *tuple);
+
+__isl_constructor
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_read_from_str(isl_ctx *ctx,
+ const char *str);
+void isl_pw_multi_aff_dump(__isl_keep isl_pw_multi_aff *pma);
+
+
+__isl_overload
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_empty_ctx(
+ isl_ctx *ctx);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_empty_space(
+ __isl_take isl_space *space);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_empty(
+ __isl_take isl_space *space);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_aff(
+ __isl_take isl_aff *aff);
+__isl_constructor
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_multi_aff(
+ __isl_take isl_multi_aff *ma);
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_pw_multi_aff_to_union_pw_multi_aff(
+ __isl_take isl_pw_multi_aff *pma);
+__isl_constructor
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_pw_multi_aff(
+ __isl_take isl_pw_multi_aff *pma);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_domain(
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_multi_val_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_multi_val *mv);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_param_on_domain_id(
+ __isl_take isl_union_set *domain, __isl_take isl_id *id);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_copy(
+ __isl_keep isl_union_pw_multi_aff *upma);
+__isl_null isl_union_pw_multi_aff *isl_union_pw_multi_aff_free(
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_give isl_union_pw_multi_aff *isl_union_set_identity_union_pw_multi_aff(
+ __isl_take isl_union_set *uset);
+
+__isl_give isl_union_pw_aff *isl_union_pw_multi_aff_get_union_pw_aff(
+ __isl_keep isl_union_pw_multi_aff *upma, int pos);
+
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_add_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma,
+ __isl_take isl_pw_multi_aff *pma);
+
+isl_ctx *isl_union_pw_multi_aff_get_ctx(
+ __isl_keep isl_union_pw_multi_aff *upma);
+__isl_export
+__isl_give isl_space *isl_union_pw_multi_aff_get_space(
+ __isl_keep isl_union_pw_multi_aff *upma);
+__isl_export
+__isl_give isl_pw_multi_aff_list *isl_union_pw_multi_aff_get_pw_multi_aff_list(
+ __isl_keep isl_union_pw_multi_aff *upma);
+
+isl_size isl_union_pw_multi_aff_dim(__isl_keep isl_union_pw_multi_aff *upma,
+ enum isl_dim_type type);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_set_dim_name(
+ __isl_take isl_union_pw_multi_aff *upma,
+ enum isl_dim_type type, unsigned pos, const char *s);
+
+int isl_union_pw_multi_aff_find_dim_by_name(
+ __isl_keep isl_union_pw_multi_aff *upma, enum isl_dim_type type,
+ const char *name);
+
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_drop_dims(
+ __isl_take isl_union_pw_multi_aff *upma,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_reset_user(
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_coalesce(
+ __isl_take isl_union_pw_multi_aff *upma);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_gist_params(
+ __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_set *context);
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_gist(
+ __isl_take isl_union_pw_multi_aff *upma,
+ __isl_take isl_union_set *context);
+
+__isl_overload
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_pullback_union_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2);
+__isl_overload
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_apply_union_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2);
+__isl_overload
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_preimage_domain_wrapped_domain_union_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2);
+
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_align_params(
+ __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_space *model);
+
+isl_size isl_union_pw_multi_aff_n_pw_multi_aff(
+ __isl_keep isl_union_pw_multi_aff *upma);
+
+isl_stat isl_union_pw_multi_aff_foreach_pw_multi_aff(
+ __isl_keep isl_union_pw_multi_aff *upma,
+ isl_stat (*fn)(__isl_take isl_pw_multi_aff *pma, void *user),
+ void *user);
+isl_bool isl_union_pw_multi_aff_every_pw_multi_aff(
+ __isl_keep isl_union_pw_multi_aff *upma,
+ isl_bool (*test)(__isl_keep isl_pw_multi_aff *pma, void *user),
+ void *user);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_union_pw_multi_aff_extract_pw_multi_aff(
+ __isl_keep isl_union_pw_multi_aff *upma, __isl_take isl_space *space);
+__isl_export
+isl_bool isl_union_pw_multi_aff_isa_pw_multi_aff(
+ __isl_keep isl_union_pw_multi_aff *upma);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_union_pw_multi_aff_as_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_export
+isl_bool isl_union_pw_multi_aff_plain_is_empty(
+ __isl_keep isl_union_pw_multi_aff *upma);
+__isl_export
+isl_bool isl_union_pw_multi_aff_involves_locals(
+ __isl_keep isl_union_pw_multi_aff *upma);
+isl_bool isl_union_pw_multi_aff_involves_nan(
+ __isl_keep isl_union_pw_multi_aff *upma);
+isl_bool isl_union_pw_multi_aff_plain_is_equal(
+ __isl_keep isl_union_pw_multi_aff *upma1,
+ __isl_keep isl_union_pw_multi_aff *upma2);
+
+__isl_export
+__isl_give isl_union_set *isl_union_pw_multi_aff_domain(
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_neg(
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_add(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2);
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_union_add(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2);
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_sub(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2);
+
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_scale_val(
+ __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_val *val);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_scale_down_val(
+ __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_val *val);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_scale_multi_val(
+ __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_multi_val *mv);
+
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_range_product(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2);
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_flat_range_product(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2);
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_range_factor_domain(
+ __isl_take isl_union_pw_multi_aff *upma);
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_range_factor_range(
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_intersect_params(
+ __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_set *set);
+__isl_overload
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_intersect_domain_union_set(
+ __isl_take isl_union_pw_multi_aff *upma,
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_intersect_domain(
+ __isl_take isl_union_pw_multi_aff *upma,
+ __isl_take isl_union_set *uset);
+__isl_overload
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_intersect_domain_space(
+ __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_space *space);
+__isl_export
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_intersect_domain_wrapped_domain(
+ __isl_take isl_union_pw_multi_aff *upma,
+ __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_intersect_domain_wrapped_range(
+ __isl_take isl_union_pw_multi_aff *upma,
+ __isl_take isl_union_set *uset);
+__isl_overload
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_subtract_domain_union_set(
+ __isl_take isl_union_pw_multi_aff *upma,
+ __isl_take isl_union_set *uset);
+__isl_overload
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_subtract_domain_space(
+ __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_space *space);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_subtract_domain(
+ __isl_take isl_union_pw_multi_aff *upma,
+ __isl_take isl_union_set *uset);
+
+__isl_export
+__isl_give isl_union_map *isl_union_pw_multi_aff_as_union_map(
+ __isl_take isl_union_pw_multi_aff *upma);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_from_union_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_give isl_printer *isl_printer_print_union_pw_multi_aff(
+ __isl_take isl_printer *p, __isl_keep isl_union_pw_multi_aff *upma);
+
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_set(
+ __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_map_as_union_pw_multi_aff(
+ __isl_take isl_union_map *umap);
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_map(
+ __isl_take isl_union_map *umap);
+
+__isl_constructor
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_read_from_str(
+ isl_ctx *ctx, const char *str);
+void isl_union_pw_multi_aff_dump(__isl_keep isl_union_pw_multi_aff *upma);
+__isl_give char *isl_union_pw_multi_aff_to_str(
+ __isl_keep isl_union_pw_multi_aff *upma);
+
+uint32_t isl_multi_pw_aff_get_hash(__isl_keep isl_multi_pw_aff *mpa);
+
+__isl_constructor
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_aff(__isl_take isl_aff *aff);
+__isl_export
+__isl_give isl_multi_pw_aff *isl_multi_aff_to_multi_pw_aff(
+ __isl_take isl_multi_aff *ma);
+__isl_constructor
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_multi_aff(
+ __isl_take isl_multi_aff *ma);
+__isl_constructor
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_pw_aff(
+ __isl_take isl_pw_aff *pa);
+__isl_export
+__isl_give isl_set *isl_multi_pw_aff_domain(__isl_take isl_multi_pw_aff *mpa);
+__isl_export
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_intersect_params(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_set *set);
+__isl_export
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_intersect_domain(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_set *domain);
+
+__isl_export
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_coalesce(
+ __isl_take isl_multi_pw_aff *mpa);
+__isl_export
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_gist(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_set *set);
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_gist_params(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_set *set);
+
+isl_bool isl_multi_pw_aff_is_cst(__isl_keep isl_multi_pw_aff *mpa);
+isl_bool isl_multi_pw_aff_is_equal(__isl_keep isl_multi_pw_aff *mpa1,
+ __isl_keep isl_multi_pw_aff *mpa2);
+
+__isl_overload
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_aff(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_multi_aff *ma);
+__isl_overload
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_pw_multi_aff(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_pw_multi_aff *pma);
+__isl_overload
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_pw_aff(
+ __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2);
+
+__isl_export
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_union_add(
+ __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2);
+
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_move_dims(
+ __isl_take isl_multi_pw_aff *pma,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+
+__isl_export
+isl_bool isl_multi_pw_aff_isa_multi_aff(__isl_keep isl_multi_pw_aff *mpa);
+__isl_export
+__isl_give isl_multi_aff *isl_multi_pw_aff_as_multi_aff(
+ __isl_take isl_multi_pw_aff *mpa);
+
+__isl_export
+__isl_give isl_set *isl_multi_pw_aff_as_set(__isl_take isl_multi_pw_aff *mpa);
+__isl_give isl_set *isl_set_from_multi_pw_aff(__isl_take isl_multi_pw_aff *mpa);
+__isl_export
+__isl_give isl_map *isl_multi_pw_aff_as_map(__isl_take isl_multi_pw_aff *mpa);
+__isl_give isl_map *isl_map_from_multi_pw_aff(__isl_take isl_multi_pw_aff *mpa);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_multi_pw_aff(
+ __isl_take isl_multi_pw_aff *mpa);
+__isl_export
+__isl_give isl_multi_pw_aff *isl_pw_multi_aff_to_multi_pw_aff(
+ __isl_take isl_pw_multi_aff *pma);
+__isl_constructor
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_pw_multi_aff(
+ __isl_take isl_pw_multi_aff *pma);
+
+__isl_give isl_map *isl_multi_pw_aff_eq_map(__isl_take isl_multi_pw_aff *mpa1,
+ __isl_take isl_multi_pw_aff *mpa2);
+__isl_give isl_map *isl_multi_pw_aff_lex_le_map(
+ __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2);
+__isl_give isl_map *isl_multi_pw_aff_lex_lt_map(
+ __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2);
+__isl_give isl_map *isl_multi_pw_aff_lex_ge_map(
+ __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2);
+__isl_give isl_map *isl_multi_pw_aff_lex_gt_map(
+ __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2);
+
+__isl_export
+__isl_give isl_set *isl_multi_pw_aff_bind(__isl_take isl_multi_pw_aff *mpa,
+ __isl_take isl_multi_id *tuple);
+
+__isl_constructor
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_read_from_str(isl_ctx *ctx,
+ const char *str);
+__isl_give char *isl_multi_pw_aff_to_str(__isl_keep isl_multi_pw_aff *mpa);
+__isl_give isl_printer *isl_printer_print_multi_pw_aff(
+ __isl_take isl_printer *p, __isl_keep isl_multi_pw_aff *mpa);
+void isl_multi_pw_aff_dump(__isl_keep isl_multi_pw_aff *mpa);
+
+__isl_give isl_union_pw_aff *isl_union_pw_aff_copy(
+ __isl_keep isl_union_pw_aff *upa);
+__isl_null isl_union_pw_aff *isl_union_pw_aff_free(
+ __isl_take isl_union_pw_aff *upa);
+
+isl_ctx *isl_union_pw_aff_get_ctx(__isl_keep isl_union_pw_aff *upa);
+__isl_export
+__isl_give isl_space *isl_union_pw_aff_get_space(
+ __isl_keep isl_union_pw_aff *upa);
+__isl_give isl_pw_aff_list *isl_union_pw_aff_get_pw_aff_list(
+ __isl_keep isl_union_pw_aff *upa);
+
+isl_size isl_union_pw_aff_dim(__isl_keep isl_union_pw_aff *upa,
+ enum isl_dim_type type);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_set_dim_name(
+ __isl_take isl_union_pw_aff *upa, enum isl_dim_type type,
+ unsigned pos, const char *s);
+
+int isl_union_pw_aff_find_dim_by_name(__isl_keep isl_union_pw_aff *upa,
+ enum isl_dim_type type, const char *name);
+
+__isl_give isl_union_pw_aff *isl_union_pw_aff_drop_dims(
+ __isl_take isl_union_pw_aff *upa,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_reset_user(
+ __isl_take isl_union_pw_aff *upa);
+
+__isl_give isl_union_pw_aff *isl_union_pw_aff_empty_ctx(isl_ctx *ctx);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_empty_space(
+ __isl_take isl_space *space);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_empty(
+ __isl_take isl_space *space);
+__isl_constructor
+__isl_give isl_union_pw_aff *isl_union_pw_aff_from_aff(__isl_take isl_aff *aff);
+__isl_export
+__isl_give isl_union_pw_aff *isl_pw_aff_to_union_pw_aff(
+ __isl_take isl_pw_aff *pa);
+__isl_constructor
+__isl_give isl_union_pw_aff *isl_union_pw_aff_from_pw_aff(
+ __isl_take isl_pw_aff *pa);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_val_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_val *v);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_aff_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_aff *aff);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_pw_aff_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_pw_aff *pa);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_add_pw_aff(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_pw_aff *pa);
+
+__isl_constructor
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_pw_aff(
+ __isl_take isl_union_pw_aff *upa);
+
+isl_size isl_union_pw_aff_n_pw_aff(__isl_keep isl_union_pw_aff *upa);
+
+isl_stat isl_union_pw_aff_foreach_pw_aff(__isl_keep isl_union_pw_aff *upa,
+ isl_stat (*fn)(__isl_take isl_pw_aff *pa, void *user), void *user);
+isl_bool isl_union_pw_aff_every_pw_aff(__isl_keep isl_union_pw_aff *upa,
+ isl_bool (*test)(__isl_keep isl_pw_aff *pa, void *user), void *user);
+__isl_give isl_pw_aff *isl_union_pw_aff_extract_pw_aff(
+ __isl_keep isl_union_pw_aff *upa, __isl_take isl_space *space);
+
+isl_bool isl_union_pw_aff_involves_nan(__isl_keep isl_union_pw_aff *upa);
+isl_bool isl_union_pw_aff_plain_is_equal(__isl_keep isl_union_pw_aff *upa1,
+ __isl_keep isl_union_pw_aff *upa2);
+
+__isl_export
+__isl_give isl_union_set *isl_union_pw_aff_domain(
+ __isl_take isl_union_pw_aff *upa);
+
+__isl_give isl_union_pw_aff *isl_union_pw_aff_neg(
+ __isl_take isl_union_pw_aff *upa);
+
+__isl_export
+__isl_give isl_union_pw_aff *isl_union_pw_aff_add(
+ __isl_take isl_union_pw_aff *upa1, __isl_take isl_union_pw_aff *upa2);
+__isl_export
+__isl_give isl_union_pw_aff *isl_union_pw_aff_union_add(
+ __isl_take isl_union_pw_aff *upa1, __isl_take isl_union_pw_aff *upa2);
+__isl_export
+__isl_give isl_union_pw_aff *isl_union_pw_aff_sub(
+ __isl_take isl_union_pw_aff *upa1, __isl_take isl_union_pw_aff *upa2);
+
+__isl_export
+__isl_give isl_union_pw_aff *isl_union_pw_aff_coalesce(
+ __isl_take isl_union_pw_aff *upa);
+__isl_export
+__isl_give isl_union_pw_aff *isl_union_pw_aff_gist(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_union_set *context);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_gist_params(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_set *context);
+
+__isl_overload
+__isl_give isl_union_pw_aff *isl_union_pw_aff_pullback_union_pw_multi_aff(
+ __isl_take isl_union_pw_aff *upa,
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_give isl_union_pw_aff *isl_union_pw_aff_floor(
+ __isl_take isl_union_pw_aff *upa);
+
+__isl_give isl_union_pw_aff *isl_union_pw_aff_scale_val(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_val *v);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_scale_down_val(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_val *v);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_mod_val(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_val *f);
+
+__isl_give isl_union_pw_aff *isl_union_pw_aff_align_params(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_space *model);
+
+__isl_export
+__isl_give isl_union_pw_aff *isl_union_pw_aff_intersect_params(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_set *set);
+__isl_overload
+__isl_give isl_union_pw_aff *isl_union_pw_aff_intersect_domain_space(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_space *space);
+__isl_overload
+__isl_give isl_union_pw_aff *isl_union_pw_aff_intersect_domain_union_set(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_intersect_domain(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_pw_aff *isl_union_pw_aff_intersect_domain_wrapped_domain(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_pw_aff *isl_union_pw_aff_intersect_domain_wrapped_range(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_union_set *uset);
+__isl_overload
+__isl_give isl_union_pw_aff *isl_union_pw_aff_subtract_domain_union_set(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_union_set *uset);
+__isl_overload
+__isl_give isl_union_pw_aff *isl_union_pw_aff_subtract_domain_space(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_space *space);
+__isl_give isl_union_pw_aff *isl_union_pw_aff_subtract_domain(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_union_set *uset);
+
+__isl_give isl_union_pw_aff *isl_union_pw_aff_set_dim_name(
+ __isl_take isl_union_pw_aff *upa,
+ enum isl_dim_type type, unsigned pos, const char *s);
+
+__isl_give isl_union_set *isl_union_pw_aff_zero_union_set(
+ __isl_take isl_union_pw_aff *upa);
+
+__isl_give isl_union_map *isl_union_map_from_union_pw_aff(
+ __isl_take isl_union_pw_aff *upa);
+
+__isl_overload
+__isl_give isl_union_set *isl_union_pw_aff_bind_id(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_id *id);
+
+__isl_constructor
+__isl_give isl_union_pw_aff *isl_union_pw_aff_read_from_str(isl_ctx *ctx,
+ const char *str);
+__isl_give char *isl_union_pw_aff_to_str(__isl_keep isl_union_pw_aff *upa);
+__isl_give isl_printer *isl_printer_print_union_pw_aff(
+ __isl_take isl_printer *p, __isl_keep isl_union_pw_aff *upa);
+void isl_union_pw_aff_dump(__isl_keep isl_union_pw_aff *upa);
+
+ISL_DECLARE_MULTI(union_pw_aff)
+ISL_DECLARE_MULTI_ARITH(union_pw_aff)
+ISL_DECLARE_MULTI_ZERO(union_pw_aff)
+ISL_DECLARE_MULTI_NAN(union_pw_aff)
+ISL_DECLARE_MULTI_DROP_DIMS(union_pw_aff)
+ISL_DECLARE_MULTI_DIM_ID(union_pw_aff)
+ISL_DECLARE_MULTI_TUPLE_ID(union_pw_aff)
+
+__isl_export
+__isl_give isl_multi_union_pw_aff *isl_multi_aff_to_multi_union_pw_aff(
+ __isl_take isl_multi_aff *ma);
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_multi_aff(
+ __isl_take isl_multi_aff *ma);
+__isl_constructor
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_union_pw_aff(
+ __isl_take isl_union_pw_aff *upa);
+__isl_constructor
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_multi_pw_aff(
+ __isl_take isl_multi_pw_aff *mpa);
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_multi_val_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_multi_val *mv);
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_multi_aff_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_multi_aff *ma);
+__isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_pw_multi_aff_on_domain(__isl_take isl_union_set *domain,
+ __isl_take isl_pw_multi_aff *pma);
+
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_floor(
+ __isl_take isl_multi_union_pw_aff *mupa);
+
+__isl_export
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_intersect_domain(
+ __isl_take isl_multi_union_pw_aff *mupa,
+ __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_intersect_params(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_set *params);
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_intersect_range(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_set *set);
+
+__isl_export
+__isl_give isl_union_set *isl_multi_union_pw_aff_domain(
+ __isl_take isl_multi_union_pw_aff *mupa);
+
+__isl_export
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_coalesce(
+ __isl_take isl_multi_union_pw_aff *mupa);
+__isl_export
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_gist(
+ __isl_take isl_multi_union_pw_aff *mupa,
+ __isl_take isl_union_set *context);
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_gist_params(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_set *context);
+
+__isl_give isl_union_pw_aff *isl_multi_union_pw_aff_apply_aff(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_aff *aff);
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_multi_aff(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_multi_aff *ma);
+__isl_give isl_union_pw_aff *isl_multi_union_pw_aff_apply_pw_aff(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_pw_aff *pa);
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_pw_multi_aff(
+ __isl_take isl_multi_union_pw_aff *mupa,
+ __isl_take isl_pw_multi_aff *pma);
+
+__isl_overload
+__isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_pullback_union_pw_multi_aff(
+ __isl_take isl_multi_union_pw_aff *mupa,
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_from_multi_union_pw_aff(
+ __isl_take isl_multi_union_pw_aff *mupa);
+
+__isl_export
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_union_add(
+ __isl_take isl_multi_union_pw_aff *mupa1,
+ __isl_take isl_multi_union_pw_aff *mupa2);
+
+__isl_export
+__isl_give isl_multi_union_pw_aff *
+isl_union_pw_multi_aff_as_multi_union_pw_aff(
+ __isl_take isl_union_pw_multi_aff *upma);
+__isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_from_union_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_export
+__isl_give isl_multi_union_pw_aff *isl_union_map_as_multi_union_pw_aff(
+ __isl_take isl_union_map *umap);
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_union_map(
+ __isl_take isl_union_map *umap);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_from_multi_union_pw_aff(
+ __isl_take isl_multi_union_pw_aff *mupa);
+
+__isl_give isl_union_set *isl_multi_union_pw_aff_zero_union_set(
+ __isl_take isl_multi_union_pw_aff *mupa);
+__isl_export
+__isl_give isl_union_set *isl_multi_union_pw_aff_bind(
+ __isl_take isl_multi_union_pw_aff *mupa,
+ __isl_take isl_multi_id *tuple);
+
+__isl_give isl_multi_pw_aff *isl_multi_union_pw_aff_extract_multi_pw_aff(
+ __isl_keep isl_multi_union_pw_aff *mupa, __isl_take isl_space *space);
+
+__isl_constructor
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_read_from_str(
+ isl_ctx *ctx, const char *str);
+__isl_give char *isl_multi_union_pw_aff_to_str(
+ __isl_keep isl_multi_union_pw_aff *mupa);
+__isl_give isl_printer *isl_printer_print_multi_union_pw_aff(
+ __isl_take isl_printer *p, __isl_keep isl_multi_union_pw_aff *mupa);
+void isl_multi_union_pw_aff_dump(__isl_keep isl_multi_union_pw_aff *mupa);
+
+ISL_DECLARE_EXPORTED_LIST_FN(aff)
+ISL_DECLARE_EXPORTED_LIST_FN_READ(aff)
+ISL_DECLARE_EXPORTED_LIST_FN(pw_aff)
+ISL_DECLARE_EXPORTED_LIST_FN_READ(pw_aff)
+ISL_DECLARE_EXPORTED_LIST_FN(pw_multi_aff)
+ISL_DECLARE_EXPORTED_LIST_FN_READ(pw_multi_aff)
+ISL_DECLARE_EXPORTED_LIST_FN(union_pw_aff)
+ISL_DECLARE_EXPORTED_LIST_FN_READ(union_pw_aff)
+ISL_DECLARE_LIST_FN(union_pw_multi_aff)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/aff_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/aff_type.h
new file mode 100644
index 00000000000..271f978b8ce
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/aff_type.h
@@ -0,0 +1,52 @@
+#ifndef ISL_AFF_TYPE_H
+#define ISL_AFF_TYPE_H
+
+#include <isl/list.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct __isl_subclass(isl_multi_aff) __isl_subclass(isl_pw_aff) isl_aff;
+typedef struct isl_aff isl_aff;
+
+ISL_DECLARE_EXPORTED_LIST_TYPE(aff)
+
+struct __isl_subclass(isl_multi_pw_aff) __isl_subclass(isl_pw_multi_aff)
+ __isl_subclass(isl_union_pw_aff) isl_pw_aff;
+typedef struct isl_pw_aff isl_pw_aff;
+
+ISL_DECLARE_EXPORTED_LIST_TYPE(pw_aff)
+
+struct __isl_subclass(isl_multi_union_pw_aff)
+ __isl_subclass(isl_union_pw_multi_aff) isl_union_pw_aff;
+typedef struct isl_union_pw_aff isl_union_pw_aff;
+
+ISL_DECLARE_EXPORTED_LIST_TYPE(union_pw_aff)
+
+struct __isl_subclass(isl_multi_pw_aff) __isl_subclass(isl_pw_multi_aff)
+ isl_multi_aff;
+typedef struct isl_multi_aff isl_multi_aff;
+
+struct __isl_subclass(isl_multi_pw_aff) __isl_subclass(isl_union_pw_multi_aff)
+ isl_pw_multi_aff;
+typedef struct isl_pw_multi_aff isl_pw_multi_aff;
+
+ISL_DECLARE_EXPORTED_LIST_TYPE(pw_multi_aff)
+
+struct __isl_export isl_union_pw_multi_aff;
+typedef struct isl_union_pw_multi_aff isl_union_pw_multi_aff;
+
+ISL_DECLARE_LIST_TYPE(union_pw_multi_aff)
+
+struct __isl_subclass(isl_multi_union_pw_aff) isl_multi_pw_aff;
+typedef struct isl_multi_pw_aff isl_multi_pw_aff;
+
+struct __isl_export isl_multi_union_pw_aff;
+typedef struct isl_multi_union_pw_aff isl_multi_union_pw_aff;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/arg.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/arg.h
new file mode 100644
index 00000000000..edbd81e12dd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/arg.h
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_ARG_H
+#define ISL_ARG_H
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_arg_choice {
+ const char *name;
+ unsigned value;
+};
+
+struct isl_arg_flags {
+ const char *name;
+ unsigned mask;
+ unsigned value;
+};
+
+enum isl_arg_type {
+ isl_arg_end,
+ isl_arg_alias,
+ isl_arg_arg,
+ isl_arg_bool,
+ isl_arg_child,
+ isl_arg_choice,
+ isl_arg_flags,
+ isl_arg_footer,
+ isl_arg_int,
+ isl_arg_user,
+ isl_arg_long,
+ isl_arg_ulong,
+ isl_arg_str,
+ isl_arg_str_list,
+ isl_arg_version
+};
+
+struct isl_args;
+
+struct isl_arg {
+ enum isl_arg_type type;
+ char short_name;
+ const char *long_name;
+ const char *argument_name;
+#define ISL_ARG_OFFSET_NONE ((size_t) -1)
+ size_t offset;
+ const char *help_msg;
+#define ISL_ARG_SINGLE_DASH (1 << 0)
+#define ISL_ARG_BOOL_ARG (1 << 1)
+#define ISL_ARG_HIDDEN (1 << 2)
+ unsigned flags;
+ union {
+ struct {
+ struct isl_arg_choice *choice;
+ unsigned default_value;
+ unsigned default_selected;
+ int (*set)(void *opt, unsigned val);
+ } choice;
+ struct {
+ struct isl_arg_flags *flags;
+ unsigned default_value;
+ } flags;
+ struct {
+ unsigned default_value;
+ int (*set)(void *opt, unsigned val);
+ } b;
+ struct {
+ int default_value;
+ } i;
+ struct {
+ long default_value;
+ long default_selected;
+ int (*set)(void *opt, long val);
+ } l;
+ struct {
+ unsigned long default_value;
+ } ul;
+ struct {
+ const char *default_value;
+ } str;
+ struct {
+ size_t offset_n;
+ } str_list;
+ struct {
+ struct isl_args *child;
+ } child;
+ struct {
+ void (*print_version)(void);
+ } version;
+ struct {
+ int (*init)(void*);
+ void (*clear)(void*);
+ } user;
+ } u;
+};
+
+struct isl_args {
+ size_t options_size;
+ struct isl_arg *args;
+};
+
+#define ISL_ARGS_START(s,name) \
+ struct isl_arg name ## LIST[]; \
+ struct isl_args name = { sizeof(s), name ## LIST }; \
+ struct isl_arg name ## LIST[] = {
+#define ISL_ARGS_END \
+ { isl_arg_end } };
+
+#define ISL_ARG_ALIAS(l) { \
+ .type = isl_arg_alias, \
+ .long_name = l, \
+},
+#define ISL_ARG_ARG(st,f,a,d) { \
+ .type = isl_arg_arg, \
+ .argument_name = a, \
+ .offset = offsetof(st, f), \
+ .u = { .str = { .default_value = d } } \
+},
+#define ISL_ARG_FOOTER(h) { \
+ .type = isl_arg_footer, \
+ .help_msg = h, \
+},
+#define ISL_ARG_CHOICE(st,f,s,l,c,d,h) { \
+ .type = isl_arg_choice, \
+ .short_name = s, \
+ .long_name = l, \
+ .offset = offsetof(st, f), \
+ .help_msg = h, \
+ .u = { .choice = { .choice = c, .default_value = d, \
+ .default_selected = d, .set = NULL } } \
+},
+#define ISL_ARG_OPT_CHOICE(st,f,s,l,c,d,ds,h) { \
+ .type = isl_arg_choice, \
+ .short_name = s, \
+ .long_name = l, \
+ .offset = offsetof(st, f), \
+ .help_msg = h, \
+ .u = { .choice = { .choice = c, .default_value = d, \
+ .default_selected = ds, .set = NULL } } \
+},
+#define ISL_ARG_PHANTOM_USER_CHOICE_F(s,l,c,setter,d,h,fl) { \
+ .type = isl_arg_choice, \
+ .short_name = s, \
+ .long_name = l, \
+ .offset = ISL_ARG_OFFSET_NONE, \
+ .help_msg = h, \
+ .flags = fl, \
+ .u = { .choice = { .choice = c, .default_value = d, \
+ .default_selected = d, .set = setter } } \
+},
+#define ISL_ARG_USER_OPT_CHOICE(st,f,s,l,c,setter,d,ds,h) { \
+ .type = isl_arg_choice, \
+ .short_name = s, \
+ .long_name = l, \
+ .offset = offsetof(st, f), \
+ .help_msg = h, \
+ .u = { .choice = { .choice = c, .default_value = d, \
+ .default_selected = ds, .set = setter } } \
+},
+#define _ISL_ARG_BOOL_F(o,s,l,setter,d,h,fl) { \
+ .type = isl_arg_bool, \
+ .short_name = s, \
+ .long_name = l, \
+ .offset = o, \
+ .help_msg = h, \
+ .flags = fl, \
+ .u = { .b = { .default_value = d, .set = setter } } \
+},
+#define ISL_ARG_BOOL_F(st,f,s,l,d,h,fl) \
+ _ISL_ARG_BOOL_F(offsetof(st, f),s,l,NULL,d,h,fl)
+#define ISL_ARG_BOOL(st,f,s,l,d,h) \
+ ISL_ARG_BOOL_F(st,f,s,l,d,h,0)
+#define ISL_ARG_PHANTOM_BOOL_F(s,l,setter,h,fl) \
+ _ISL_ARG_BOOL_F(ISL_ARG_OFFSET_NONE,s,l,setter,0,h,fl)
+#define ISL_ARG_PHANTOM_BOOL(s,l,setter,h) \
+ ISL_ARG_PHANTOM_BOOL_F(s,l,setter,h,0)
+#define ISL_ARG_INT_F(st,f,s,l,a,d,h,fl) { \
+ .type = isl_arg_int, \
+ .short_name = s, \
+ .long_name = l, \
+ .argument_name = a, \
+ .offset = offsetof(st, f), \
+ .help_msg = h, \
+ .flags = fl, \
+ .u = { .i = { .default_value = d } } \
+},
+#define ISL_ARG_INT(st,f,s,l,a,d,h) \
+ ISL_ARG_INT_F(st,f,s,l,a,d,h,0)
+#define ISL_ARG_LONG(st,f,s,lo,d,h) { \
+ .type = isl_arg_long, \
+ .short_name = s, \
+ .long_name = lo, \
+ .offset = offsetof(st, f), \
+ .help_msg = h, \
+ .u = { .l = { .default_value = d, .default_selected = d, \
+ .set = NULL } } \
+},
+#define ISL_ARG_USER_LONG(st,f,s,lo,setter,d,h) { \
+ .type = isl_arg_long, \
+ .short_name = s, \
+ .long_name = lo, \
+ .offset = offsetof(st, f), \
+ .help_msg = h, \
+ .u = { .l = { .default_value = d, .default_selected = d, \
+ .set = setter } } \
+},
+#define ISL_ARG_OPT_LONG(st,f,s,lo,d,ds,h) { \
+ .type = isl_arg_long, \
+ .short_name = s, \
+ .long_name = lo, \
+ .offset = offsetof(st, f), \
+ .help_msg = h, \
+ .u = { .l = { .default_value = d, .default_selected = ds, \
+ .set = NULL } } \
+},
+#define ISL_ARG_ULONG(st,f,s,l,d,h) { \
+ .type = isl_arg_ulong, \
+ .short_name = s, \
+ .long_name = l, \
+ .offset = offsetof(st, f), \
+ .help_msg = h, \
+ .u = { .ul = { .default_value = d } } \
+},
+#define ISL_ARG_STR_F(st,f,s,l,a,d,h,fl) { \
+ .type = isl_arg_str, \
+ .short_name = s, \
+ .long_name = l, \
+ .argument_name = a, \
+ .offset = offsetof(st, f), \
+ .help_msg = h, \
+ .flags = fl, \
+ .u = { .str = { .default_value = d } } \
+},
+#define ISL_ARG_STR(st,f,s,l,a,d,h) \
+ ISL_ARG_STR_F(st,f,s,l,a,d,h,0)
+#define ISL_ARG_STR_LIST(st,f_n,f_l,s,l,a,h) { \
+ .type = isl_arg_str_list, \
+ .short_name = s, \
+ .long_name = l, \
+ .argument_name = a, \
+ .offset = offsetof(st, f_l), \
+ .help_msg = h, \
+ .u = { .str_list = { .offset_n = offsetof(st, f_n) } } \
+},
+#define _ISL_ARG_CHILD(o,l,c,h,fl) { \
+ .type = isl_arg_child, \
+ .long_name = l, \
+ .offset = o, \
+ .help_msg = h, \
+ .flags = fl, \
+ .u = { .child = { .child = c } } \
+},
+#define ISL_ARG_CHILD(st,f,l,c,h) \
+ _ISL_ARG_CHILD(offsetof(st, f),l,c,h,0)
+#define ISL_ARG_GROUP_F(l,c,h,fl) \
+ _ISL_ARG_CHILD(ISL_ARG_OFFSET_NONE,l,c,h,fl)
+#define ISL_ARG_GROUP(l,c,h) \
+ ISL_ARG_GROUP_F(l,c,h,0)
+#define ISL_ARG_FLAGS(st,f,s,l,c,d,h) { \
+ .type = isl_arg_flags, \
+ .short_name = s, \
+ .long_name = l, \
+ .offset = offsetof(st, f), \
+ .help_msg = h, \
+ .u = { .flags = { .flags = c, .default_value = d } } \
+},
+#define ISL_ARG_USER(st,f,i,c) { \
+ .type = isl_arg_user, \
+ .offset = offsetof(st, f), \
+ .u = { .user = { .init = i, .clear = c} } \
+},
+#define ISL_ARG_VERSION(print) { \
+ .type = isl_arg_version, \
+ .u = { .version = { .print_version = print } } \
+},
+
+#define ISL_ARG_ALL (1 << 0)
+#define ISL_ARG_SKIP_HELP (1 << 1)
+
+void isl_args_set_defaults(struct isl_args *args, void *opt);
+void isl_args_free(struct isl_args *args, void *opt);
+int isl_args_parse(struct isl_args *args, int argc, char **argv, void *opt,
+ unsigned flags);
+
+#define ISL_ARG_DECL(prefix,st,args) \
+extern struct isl_args args; \
+st *prefix ## _new_with_defaults(void); \
+void prefix ## _free(st *opt); \
+int prefix ## _parse(st *opt, int argc, char **argv, unsigned flags);
+
+#define ISL_ARG_DEF(prefix,st,args) \
+st *prefix ## _new_with_defaults() \
+{ \
+ st *opt = (st *)calloc(1, sizeof(st)); \
+ if (opt) \
+ isl_args_set_defaults(&(args), opt); \
+ return opt; \
+} \
+ \
+void prefix ## _free(st *opt) \
+{ \
+ isl_args_free(&(args), opt); \
+} \
+ \
+int prefix ## _parse(st *opt, int argc, char **argv, unsigned flags) \
+{ \
+ return isl_args_parse(&(args), argc, argv, opt, flags); \
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast.h
new file mode 100644
index 00000000000..570c48a092b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast.h
@@ -0,0 +1,242 @@
+#ifndef ISL_AST_H
+#define ISL_AST_H
+
+#include <isl/ctx.h>
+#include <isl/ast_type.h>
+#include <isl/id_type.h>
+#include <isl/id_to_ast_expr.h>
+#include <isl/val_type.h>
+#include <isl/list.h>
+#include <isl/printer.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+isl_stat isl_options_set_ast_iterator_type(isl_ctx *ctx, const char *val);
+const char *isl_options_get_ast_iterator_type(isl_ctx *ctx);
+
+isl_stat isl_options_set_ast_always_print_block(isl_ctx *ctx, int val);
+int isl_options_get_ast_always_print_block(isl_ctx *ctx);
+
+isl_stat isl_options_set_ast_print_outermost_block(isl_ctx *ctx, int val);
+int isl_options_get_ast_print_outermost_block(isl_ctx *ctx);
+
+__isl_give isl_ast_expr *isl_ast_expr_from_val(__isl_take isl_val *v);
+__isl_give isl_ast_expr *isl_ast_expr_from_id(__isl_take isl_id *id);
+__isl_give isl_ast_expr *isl_ast_expr_neg(__isl_take isl_ast_expr *expr);
+__isl_give isl_ast_expr *isl_ast_expr_add(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_sub(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_mul(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_div(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_pdiv_q(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_pdiv_r(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_and(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_and_then(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_or(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_or_else(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_le(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_lt(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_ge(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_gt(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_eq(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2);
+__isl_give isl_ast_expr *isl_ast_expr_access(__isl_take isl_ast_expr *array,
+ __isl_take isl_ast_expr_list *indices);
+__isl_give isl_ast_expr *isl_ast_expr_call(__isl_take isl_ast_expr *function,
+ __isl_take isl_ast_expr_list *arguments);
+__isl_give isl_ast_expr *isl_ast_expr_address_of(__isl_take isl_ast_expr *expr);
+
+__isl_give isl_ast_expr *isl_ast_expr_copy(__isl_keep isl_ast_expr *expr);
+__isl_null isl_ast_expr *isl_ast_expr_free(__isl_take isl_ast_expr *expr);
+
+isl_ctx *isl_ast_expr_get_ctx(__isl_keep isl_ast_expr *expr);
+__isl_subclass(isl_ast_expr)
+enum isl_ast_expr_type isl_ast_expr_get_type(__isl_keep isl_ast_expr *expr);
+__isl_export
+__isl_give isl_val *isl_ast_expr_int_get_val(__isl_keep isl_ast_expr *expr);
+__isl_give isl_val *isl_ast_expr_get_val(__isl_keep isl_ast_expr *expr);
+__isl_export
+__isl_give isl_id *isl_ast_expr_id_get_id(__isl_keep isl_ast_expr *expr);
+__isl_give isl_id *isl_ast_expr_get_id(__isl_keep isl_ast_expr *expr);
+
+__isl_subclass(isl_ast_expr_op)
+enum isl_ast_expr_op_type isl_ast_expr_op_get_type(
+ __isl_keep isl_ast_expr *expr);
+enum isl_ast_expr_op_type isl_ast_expr_get_op_type(
+ __isl_keep isl_ast_expr *expr);
+__isl_export
+isl_size isl_ast_expr_op_get_n_arg(__isl_keep isl_ast_expr *expr);
+isl_size isl_ast_expr_get_op_n_arg(__isl_keep isl_ast_expr *expr);
+__isl_export
+__isl_give isl_ast_expr *isl_ast_expr_op_get_arg(__isl_keep isl_ast_expr *expr,
+ int pos);
+__isl_give isl_ast_expr *isl_ast_expr_get_op_arg(__isl_keep isl_ast_expr *expr,
+ int pos);
+__isl_give isl_ast_expr *isl_ast_expr_set_op_arg(__isl_take isl_ast_expr *expr,
+ int pos, __isl_take isl_ast_expr *arg);
+
+isl_bool isl_ast_expr_is_equal(__isl_keep isl_ast_expr *expr1,
+ __isl_keep isl_ast_expr *expr2);
+
+__isl_give isl_ast_expr *isl_ast_expr_substitute_ids(
+ __isl_take isl_ast_expr *expr, __isl_take isl_id_to_ast_expr *id2expr);
+
+__isl_give isl_printer *isl_printer_print_ast_expr(__isl_take isl_printer *p,
+ __isl_keep isl_ast_expr *expr);
+void isl_ast_expr_dump(__isl_keep isl_ast_expr *expr);
+__isl_give char *isl_ast_expr_to_str(__isl_keep isl_ast_expr *expr);
+__isl_export
+__isl_give char *isl_ast_expr_to_C_str(__isl_keep isl_ast_expr *expr);
+
+__isl_give isl_ast_node *isl_ast_node_alloc_user(__isl_take isl_ast_expr *expr);
+__isl_give isl_ast_node *isl_ast_node_copy(__isl_keep isl_ast_node *node);
+__isl_null isl_ast_node *isl_ast_node_free(__isl_take isl_ast_node *node);
+
+isl_ctx *isl_ast_node_get_ctx(__isl_keep isl_ast_node *node);
+__isl_subclass(isl_ast_node)
+enum isl_ast_node_type isl_ast_node_get_type(__isl_keep isl_ast_node *node);
+
+__isl_give isl_ast_node *isl_ast_node_set_annotation(
+ __isl_take isl_ast_node *node, __isl_take isl_id *annotation);
+__isl_give isl_id *isl_ast_node_get_annotation(__isl_keep isl_ast_node *node);
+
+__isl_export
+__isl_give isl_ast_expr *isl_ast_node_for_get_iterator(
+ __isl_keep isl_ast_node *node);
+__isl_export
+__isl_give isl_ast_expr *isl_ast_node_for_get_init(
+ __isl_keep isl_ast_node *node);
+__isl_export
+__isl_give isl_ast_expr *isl_ast_node_for_get_cond(
+ __isl_keep isl_ast_node *node);
+__isl_export
+__isl_give isl_ast_expr *isl_ast_node_for_get_inc(
+ __isl_keep isl_ast_node *node);
+__isl_export
+__isl_give isl_ast_node *isl_ast_node_for_get_body(
+ __isl_keep isl_ast_node *node);
+__isl_export
+isl_bool isl_ast_node_for_is_degenerate(__isl_keep isl_ast_node *node);
+
+__isl_export
+__isl_give isl_ast_expr *isl_ast_node_if_get_cond(
+ __isl_keep isl_ast_node *node);
+__isl_export
+__isl_give isl_ast_node *isl_ast_node_if_get_then_node(
+ __isl_keep isl_ast_node *node);
+__isl_give isl_ast_node *isl_ast_node_if_get_then(
+ __isl_keep isl_ast_node *node);
+__isl_export
+isl_bool isl_ast_node_if_has_else_node(__isl_keep isl_ast_node *node);
+isl_bool isl_ast_node_if_has_else(__isl_keep isl_ast_node *node);
+__isl_export
+__isl_give isl_ast_node *isl_ast_node_if_get_else_node(
+ __isl_keep isl_ast_node *node);
+__isl_give isl_ast_node *isl_ast_node_if_get_else(
+ __isl_keep isl_ast_node *node);
+
+__isl_export
+__isl_give isl_ast_node_list *isl_ast_node_block_get_children(
+ __isl_keep isl_ast_node *node);
+
+__isl_export
+__isl_give isl_id *isl_ast_node_mark_get_id(__isl_keep isl_ast_node *node);
+__isl_export
+__isl_give isl_ast_node *isl_ast_node_mark_get_node(
+ __isl_keep isl_ast_node *node);
+
+__isl_export
+__isl_give isl_ast_expr *isl_ast_node_user_get_expr(
+ __isl_keep isl_ast_node *node);
+
+isl_stat isl_ast_node_foreach_descendant_top_down(
+ __isl_keep isl_ast_node *node,
+ isl_bool (*fn)(__isl_keep isl_ast_node *node, void *user), void *user);
+
+__isl_give isl_printer *isl_printer_print_ast_node(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node);
+void isl_ast_node_dump(__isl_keep isl_ast_node *node);
+__isl_give char *isl_ast_node_to_str(__isl_keep isl_ast_node *node);
+
+__isl_give isl_ast_print_options *isl_ast_print_options_alloc(isl_ctx *ctx);
+__isl_give isl_ast_print_options *isl_ast_print_options_copy(
+ __isl_keep isl_ast_print_options *options);
+__isl_null isl_ast_print_options *isl_ast_print_options_free(
+ __isl_take isl_ast_print_options *options);
+isl_ctx *isl_ast_print_options_get_ctx(
+ __isl_keep isl_ast_print_options *options);
+
+__isl_give isl_ast_print_options *isl_ast_print_options_set_print_user(
+ __isl_take isl_ast_print_options *options,
+ __isl_give isl_printer *(*print_user)(__isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *options,
+ __isl_keep isl_ast_node *node, void *user),
+ void *user);
+__isl_give isl_ast_print_options *isl_ast_print_options_set_print_for(
+ __isl_take isl_ast_print_options *options,
+ __isl_give isl_printer *(*print_for)(__isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *options,
+ __isl_keep isl_ast_node *node, void *user),
+ void *user);
+
+isl_stat isl_options_set_ast_print_macro_once(isl_ctx *ctx, int val);
+int isl_options_get_ast_print_macro_once(isl_ctx *ctx);
+
+isl_stat isl_ast_expr_foreach_ast_expr_op_type(__isl_keep isl_ast_expr *expr,
+ isl_stat (*fn)(enum isl_ast_expr_op_type type, void *user), void *user);
+isl_stat isl_ast_expr_foreach_ast_op_type(__isl_keep isl_ast_expr *expr,
+ isl_stat (*fn)(enum isl_ast_expr_op_type type, void *user), void *user);
+isl_stat isl_ast_node_foreach_ast_expr_op_type(__isl_keep isl_ast_node *node,
+ isl_stat (*fn)(enum isl_ast_expr_op_type type, void *user), void *user);
+isl_stat isl_ast_node_foreach_ast_op_type(__isl_keep isl_ast_node *node,
+ isl_stat (*fn)(enum isl_ast_expr_op_type type, void *user), void *user);
+__isl_give isl_printer *isl_ast_expr_op_type_set_print_name(
+ __isl_take isl_printer *p, enum isl_ast_expr_op_type type,
+ __isl_keep const char *name);
+__isl_give isl_printer *isl_ast_op_type_set_print_name(
+ __isl_take isl_printer *p, enum isl_ast_expr_op_type type,
+ __isl_keep const char *name);
+__isl_give isl_printer *isl_ast_expr_op_type_print_macro(
+ enum isl_ast_expr_op_type type, __isl_take isl_printer *p);
+__isl_give isl_printer *isl_ast_op_type_print_macro(
+ enum isl_ast_expr_op_type type, __isl_take isl_printer *p);
+__isl_give isl_printer *isl_ast_expr_print_macros(
+ __isl_keep isl_ast_expr *expr, __isl_take isl_printer *p);
+__isl_give isl_printer *isl_ast_node_print_macros(
+ __isl_keep isl_ast_node *node, __isl_take isl_printer *p);
+__isl_give isl_printer *isl_ast_node_print(__isl_keep isl_ast_node *node,
+ __isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *options);
+__isl_give isl_printer *isl_ast_node_for_print(__isl_keep isl_ast_node *node,
+ __isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *options);
+__isl_give isl_printer *isl_ast_node_if_print(__isl_keep isl_ast_node *node,
+ __isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *options);
+
+__isl_export
+__isl_give char *isl_ast_node_to_C_str(__isl_keep isl_ast_node *node);
+
+ISL_DECLARE_LIST_FN(ast_expr)
+ISL_DECLARE_EXPORTED_LIST_FN(ast_node)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast_build.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast_build.h
new file mode 100644
index 00000000000..0029cd5bec7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast_build.h
@@ -0,0 +1,131 @@
+#ifndef ISL_AST_BUILD_H
+#define ISL_AST_BUILD_H
+
+#include <isl/ctx.h>
+#include <isl/set.h>
+#include <isl/ast.h>
+#include <isl/schedule.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct __isl_export isl_ast_build;
+typedef struct isl_ast_build isl_ast_build;
+
+
+isl_stat isl_options_set_ast_build_atomic_upper_bound(isl_ctx *ctx, int val);
+int isl_options_get_ast_build_atomic_upper_bound(isl_ctx *ctx);
+
+isl_stat isl_options_set_ast_build_prefer_pdiv(isl_ctx *ctx, int val);
+int isl_options_get_ast_build_prefer_pdiv(isl_ctx *ctx);
+
+isl_stat isl_options_set_ast_build_detect_min_max(isl_ctx *ctx, int val);
+int isl_options_get_ast_build_detect_min_max(isl_ctx *ctx);
+
+isl_stat isl_options_set_ast_build_exploit_nested_bounds(isl_ctx *ctx, int val);
+int isl_options_get_ast_build_exploit_nested_bounds(isl_ctx *ctx);
+
+isl_stat isl_options_set_ast_build_group_coscheduled(isl_ctx *ctx, int val);
+int isl_options_get_ast_build_group_coscheduled(isl_ctx *ctx);
+
+#define ISL_AST_BUILD_SEPARATION_BOUNDS_EXPLICIT 0
+#define ISL_AST_BUILD_SEPARATION_BOUNDS_IMPLICIT 1
+isl_stat isl_options_set_ast_build_separation_bounds(isl_ctx *ctx, int val);
+int isl_options_get_ast_build_separation_bounds(isl_ctx *ctx);
+
+isl_stat isl_options_set_ast_build_scale_strides(isl_ctx *ctx, int val);
+int isl_options_get_ast_build_scale_strides(isl_ctx *ctx);
+
+isl_stat isl_options_set_ast_build_allow_else(isl_ctx *ctx, int val);
+int isl_options_get_ast_build_allow_else(isl_ctx *ctx);
+
+isl_stat isl_options_set_ast_build_allow_or(isl_ctx *ctx, int val);
+int isl_options_get_ast_build_allow_or(isl_ctx *ctx);
+
+isl_ctx *isl_ast_build_get_ctx(__isl_keep isl_ast_build *build);
+
+__isl_constructor
+__isl_give isl_ast_build *isl_ast_build_alloc(isl_ctx *ctx);
+__isl_export
+__isl_give isl_ast_build *isl_ast_build_from_context(__isl_take isl_set *set);
+
+__isl_give isl_space *isl_ast_build_get_schedule_space(
+ __isl_keep isl_ast_build *build);
+__isl_export
+__isl_give isl_union_map *isl_ast_build_get_schedule(
+ __isl_keep isl_ast_build *build);
+
+__isl_give isl_ast_build *isl_ast_build_restrict(
+ __isl_take isl_ast_build *build, __isl_take isl_set *set);
+
+__isl_give isl_ast_build *isl_ast_build_copy(
+ __isl_keep isl_ast_build *build);
+__isl_null isl_ast_build *isl_ast_build_free(
+ __isl_take isl_ast_build *build);
+
+__isl_give isl_ast_build *isl_ast_build_set_options(
+ __isl_take isl_ast_build *build,
+ __isl_take isl_union_map *options);
+__isl_give isl_ast_build *isl_ast_build_set_iterators(
+ __isl_take isl_ast_build *build,
+ __isl_take isl_id_list *iterators);
+__isl_export
+__isl_give isl_ast_build *isl_ast_build_set_at_each_domain(
+ __isl_take isl_ast_build *build,
+ __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build, void *user), void *user);
+__isl_give isl_ast_build *isl_ast_build_set_before_each_for(
+ __isl_take isl_ast_build *build,
+ __isl_give isl_id *(*fn)(__isl_keep isl_ast_build *build,
+ void *user), void *user);
+__isl_give isl_ast_build *isl_ast_build_set_after_each_for(
+ __isl_take isl_ast_build *build,
+ __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build, void *user), void *user);
+__isl_give isl_ast_build *isl_ast_build_set_before_each_mark(
+ __isl_take isl_ast_build *build,
+ isl_stat (*fn)(__isl_keep isl_id *mark, __isl_keep isl_ast_build *build,
+ void *user), void *user);
+__isl_give isl_ast_build *isl_ast_build_set_after_each_mark(
+ __isl_take isl_ast_build *build,
+ __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build, void *user), void *user);
+__isl_give isl_ast_build *isl_ast_build_set_create_leaf(
+ __isl_take isl_ast_build *build,
+ __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_build *build,
+ void *user), void *user);
+
+__isl_overload
+__isl_give isl_ast_expr *isl_ast_build_expr_from_set(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set);
+__isl_overload
+__isl_give isl_ast_expr *isl_ast_build_expr_from_pw_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa);
+__isl_overload
+__isl_give isl_ast_expr *isl_ast_build_access_from_pw_multi_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma);
+__isl_overload
+__isl_give isl_ast_expr *isl_ast_build_access_from_multi_pw_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa);
+__isl_overload
+__isl_give isl_ast_expr *isl_ast_build_call_from_pw_multi_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma);
+__isl_overload
+__isl_give isl_ast_expr *isl_ast_build_call_from_multi_pw_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa);
+
+__isl_overload
+__isl_give isl_ast_node *isl_ast_build_node_from_schedule(
+ __isl_keep isl_ast_build *build, __isl_take isl_schedule *schedule);
+__isl_export
+__isl_give isl_ast_node *isl_ast_build_node_from_schedule_map(
+ __isl_keep isl_ast_build *build, __isl_take isl_union_map *schedule);
+__isl_give isl_ast_node *isl_ast_build_ast_from_schedule(
+ __isl_keep isl_ast_build *build, __isl_take isl_union_map *schedule);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast_type.h
new file mode 100644
index 00000000000..2877b8f8551
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ast_type.h
@@ -0,0 +1,109 @@
+#ifndef ISL_AST_TYPE_H
+#define ISL_AST_TYPE_H
+
+#include <isl/list.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct __isl_export isl_ast_expr;
+typedef struct isl_ast_expr isl_ast_expr;
+
+struct __isl_export isl_ast_node;
+typedef struct isl_ast_node isl_ast_node;
+
+enum isl_ast_expr_op_type {
+ isl_ast_expr_op_error = -1,
+ isl_ast_expr_op_and,
+ isl_ast_expr_op_and_then,
+ isl_ast_expr_op_or,
+ isl_ast_expr_op_or_else,
+ isl_ast_expr_op_max,
+ isl_ast_expr_op_min,
+ isl_ast_expr_op_minus,
+ isl_ast_expr_op_add,
+ isl_ast_expr_op_sub,
+ isl_ast_expr_op_mul,
+ isl_ast_expr_op_div,
+ isl_ast_expr_op_fdiv_q, /* Round towards -infty */
+ isl_ast_expr_op_pdiv_q, /* Dividend is non-negative */
+ isl_ast_expr_op_pdiv_r, /* Dividend is non-negative */
+ isl_ast_expr_op_zdiv_r, /* Result only compared against zero */
+ isl_ast_expr_op_cond,
+ isl_ast_expr_op_select,
+ isl_ast_expr_op_eq,
+ isl_ast_expr_op_le,
+ isl_ast_expr_op_lt,
+ isl_ast_expr_op_ge,
+ isl_ast_expr_op_gt,
+ isl_ast_expr_op_call,
+ isl_ast_expr_op_access,
+ isl_ast_expr_op_member,
+ isl_ast_expr_op_address_of
+};
+
+#define isl_ast_op_type isl_ast_expr_op_type
+#define isl_ast_op_error isl_ast_expr_op_error
+#define isl_ast_op_and isl_ast_expr_op_and
+#define isl_ast_op_and_then isl_ast_expr_op_and_then
+#define isl_ast_op_or isl_ast_expr_op_or
+#define isl_ast_op_or_else isl_ast_expr_op_or_else
+#define isl_ast_op_max isl_ast_expr_op_max
+#define isl_ast_op_min isl_ast_expr_op_min
+#define isl_ast_op_minus isl_ast_expr_op_minus
+#define isl_ast_op_add isl_ast_expr_op_add
+#define isl_ast_op_sub isl_ast_expr_op_sub
+#define isl_ast_op_mul isl_ast_expr_op_mul
+#define isl_ast_op_div isl_ast_expr_op_div
+#define isl_ast_op_fdiv_q isl_ast_expr_op_fdiv_q
+#define isl_ast_op_pdiv_q isl_ast_expr_op_pdiv_q
+#define isl_ast_op_pdiv_r isl_ast_expr_op_pdiv_r
+#define isl_ast_op_zdiv_r isl_ast_expr_op_zdiv_r
+#define isl_ast_op_cond isl_ast_expr_op_cond
+#define isl_ast_op_select isl_ast_expr_op_select
+#define isl_ast_op_eq isl_ast_expr_op_eq
+#define isl_ast_op_le isl_ast_expr_op_le
+#define isl_ast_op_lt isl_ast_expr_op_lt
+#define isl_ast_op_ge isl_ast_expr_op_ge
+#define isl_ast_op_gt isl_ast_expr_op_gt
+#define isl_ast_op_call isl_ast_expr_op_call
+#define isl_ast_op_access isl_ast_expr_op_access
+#define isl_ast_op_member isl_ast_expr_op_member
+#define isl_ast_op_address_of isl_ast_expr_op_address_of
+
+enum isl_ast_expr_type {
+ isl_ast_expr_error = -1,
+ isl_ast_expr_op,
+ isl_ast_expr_id,
+ isl_ast_expr_int
+};
+
+enum isl_ast_node_type {
+ isl_ast_node_error = -1,
+ isl_ast_node_for = 1,
+ isl_ast_node_if,
+ isl_ast_node_block,
+ isl_ast_node_mark,
+ isl_ast_node_user
+};
+
+enum isl_ast_loop_type {
+ isl_ast_loop_error = -1,
+ isl_ast_loop_default = 0,
+ isl_ast_loop_atomic,
+ isl_ast_loop_unroll,
+ isl_ast_loop_separate
+};
+
+struct isl_ast_print_options;
+typedef struct isl_ast_print_options isl_ast_print_options;
+
+ISL_DECLARE_LIST_TYPE(ast_expr)
+ISL_DECLARE_EXPORTED_LIST_TYPE(ast_node)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/constraint.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/constraint.h
new file mode 100644
index 00000000000..a8b1611220f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/constraint.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_CONSTRAINT_H
+#define ISL_CONSTRAINT_H
+
+#include <isl/local_space.h>
+#include <isl/space_type.h>
+#include <isl/aff_type.h>
+#include <isl/set_type.h>
+#include <isl/list.h>
+#include <isl/val_type.h>
+#include <isl/printer.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_constraint;
+typedef struct isl_constraint isl_constraint;
+
+ISL_DECLARE_LIST(constraint)
+
+isl_ctx *isl_constraint_get_ctx(__isl_keep isl_constraint *c);
+
+__isl_give isl_constraint *isl_constraint_alloc_equality(
+ __isl_take isl_local_space *ls);
+__isl_give isl_constraint *isl_constraint_alloc_inequality(
+ __isl_take isl_local_space *ls);
+__isl_give isl_constraint *isl_equality_alloc(__isl_take isl_local_space *ls);
+__isl_give isl_constraint *isl_inequality_alloc(__isl_take isl_local_space *ls);
+
+__isl_give isl_constraint *isl_constraint_copy(__isl_keep isl_constraint *c);
+__isl_null isl_constraint *isl_constraint_free(__isl_take isl_constraint *c);
+
+isl_size isl_basic_map_n_constraint(__isl_keep isl_basic_map *bmap);
+isl_size isl_basic_set_n_constraint(__isl_keep isl_basic_set *bset);
+isl_stat isl_basic_map_foreach_constraint(__isl_keep isl_basic_map *bmap,
+ isl_stat (*fn)(__isl_take isl_constraint *c, void *user), void *user);
+isl_stat isl_basic_set_foreach_constraint(__isl_keep isl_basic_set *bset,
+ isl_stat (*fn)(__isl_take isl_constraint *c, void *user), void *user);
+__isl_give isl_constraint_list *isl_basic_map_get_constraint_list(
+ __isl_keep isl_basic_map *bmap);
+__isl_give isl_constraint_list *isl_basic_set_get_constraint_list(
+ __isl_keep isl_basic_set *bset);
+int isl_constraint_is_equal(__isl_keep isl_constraint *constraint1,
+ __isl_keep isl_constraint *constraint2);
+
+isl_stat isl_basic_set_foreach_bound_pair(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos,
+ isl_stat (*fn)(__isl_take isl_constraint *lower,
+ __isl_take isl_constraint *upper,
+ __isl_take isl_basic_set *bset, void *user), void *user);
+
+__isl_give isl_basic_map *isl_basic_map_add_constraint(
+ __isl_take isl_basic_map *bmap, __isl_take isl_constraint *constraint);
+__isl_give isl_basic_set *isl_basic_set_add_constraint(
+ __isl_take isl_basic_set *bset, __isl_take isl_constraint *constraint);
+__isl_give isl_map *isl_map_add_constraint(__isl_take isl_map *map,
+ __isl_take isl_constraint *constraint);
+__isl_give isl_set *isl_set_add_constraint(__isl_take isl_set *set,
+ __isl_take isl_constraint *constraint);
+
+isl_bool isl_basic_map_has_defining_equality(
+ __isl_keep isl_basic_map *bmap, enum isl_dim_type type, int pos,
+ __isl_give isl_constraint **c);
+isl_bool isl_basic_set_has_defining_equality(
+ struct isl_basic_set *bset, enum isl_dim_type type, int pos,
+ struct isl_constraint **constraint);
+isl_bool isl_basic_set_has_defining_inequalities(
+ struct isl_basic_set *bset, enum isl_dim_type type, int pos,
+ struct isl_constraint **lower,
+ struct isl_constraint **upper);
+
+__isl_give isl_space *isl_constraint_get_space(
+ __isl_keep isl_constraint *constraint);
+__isl_give isl_local_space *isl_constraint_get_local_space(
+ __isl_keep isl_constraint *constraint);
+isl_size isl_constraint_dim(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type);
+
+isl_bool isl_constraint_involves_dims(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+const char *isl_constraint_get_dim_name(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_val *isl_constraint_get_constant_val(
+ __isl_keep isl_constraint *constraint);
+__isl_give isl_val *isl_constraint_get_coefficient_val(
+ __isl_keep isl_constraint *constraint, enum isl_dim_type type, int pos);
+__isl_give isl_constraint *isl_constraint_set_constant_si(
+ __isl_take isl_constraint *constraint, int v);
+__isl_give isl_constraint *isl_constraint_set_constant_val(
+ __isl_take isl_constraint *constraint, __isl_take isl_val *v);
+__isl_give isl_constraint *isl_constraint_set_coefficient_si(
+ __isl_take isl_constraint *constraint,
+ enum isl_dim_type type, int pos, int v);
+__isl_give isl_constraint *isl_constraint_set_coefficient_val(
+ __isl_take isl_constraint *constraint,
+ enum isl_dim_type type, int pos, __isl_take isl_val *v);
+
+__isl_give isl_aff *isl_constraint_get_div(__isl_keep isl_constraint *constraint,
+ int pos);
+
+__isl_give isl_constraint *isl_constraint_negate(
+ __isl_take isl_constraint *constraint);
+
+isl_bool isl_constraint_is_equality(__isl_keep isl_constraint *constraint);
+isl_bool isl_constraint_is_div_constraint(
+ __isl_keep isl_constraint *constraint);
+
+isl_bool isl_constraint_is_lower_bound(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type, unsigned pos);
+isl_bool isl_constraint_is_upper_bound(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type, unsigned pos);
+
+__isl_give isl_basic_map *isl_basic_map_from_constraint(
+ __isl_take isl_constraint *constraint);
+__isl_give isl_basic_set *isl_basic_set_from_constraint(
+ __isl_take isl_constraint *constraint);
+
+__isl_give isl_aff *isl_constraint_get_bound(
+ __isl_keep isl_constraint *constraint, enum isl_dim_type type, int pos);
+__isl_give isl_aff *isl_constraint_get_aff(
+ __isl_keep isl_constraint *constraint);
+__isl_give isl_constraint *isl_equality_from_aff(__isl_take isl_aff *aff);
+__isl_give isl_constraint *isl_inequality_from_aff(__isl_take isl_aff *aff);
+
+int isl_constraint_plain_cmp(__isl_keep isl_constraint *c1,
+ __isl_keep isl_constraint *c2);
+int isl_constraint_cmp_last_non_zero(__isl_keep isl_constraint *c1,
+ __isl_keep isl_constraint *c2);
+
+__isl_give isl_printer *isl_printer_print_constraint(__isl_take isl_printer *p,
+ __isl_keep isl_constraint *c);
+void isl_constraint_dump(__isl_keep isl_constraint *c);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ctx.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ctx.h
new file mode 100644
index 00000000000..ecd9272e4ef
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ctx.h
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_CTX_H
+#define ISL_CTX_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <isl/arg.h>
+
+#ifndef __isl_give
+#define __isl_give
+#endif
+#ifndef __isl_take
+#define __isl_take
+#endif
+#ifndef __isl_keep
+#define __isl_keep
+#endif
+#ifndef __isl_null
+#define __isl_null
+#endif
+#ifndef __isl_export
+#define __isl_export
+#endif
+#ifndef __isl_overload
+#define __isl_overload
+#endif
+#ifndef __isl_constructor
+#define __isl_constructor
+#endif
+#ifndef __isl_subclass
+#define __isl_subclass(super)
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Nearly all isa functions require a struct isl_ctx allocated using
+ * isl_ctx_alloc. This ctx contains (or will contain) options that
+ * control the behavior of the library and some caches.
+ *
+ * An object allocated within a given ctx should never be used inside
+ * another ctx. Functions for moving objects from one ctx to another
+ * will be added as the need arises.
+ *
+ * A given context should only be used inside a single thread.
+ * A global context for synchronization between different threads
+ * as well as functions for moving a context to a different thread
+ * will be added as the need arises.
+ *
+ * If anything goes wrong (out of memory, failed assertion), then
+ * the library will currently simply abort. This will be made
+ * configurable in the future.
+ * Users of the library should expect functions that return
+ * a pointer to a structure, to return NULL, indicating failure.
+ * Any function accepting a pointer to a structure will treat
+ * a NULL argument as a failure, resulting in the function freeing
+ * the remaining structures (if any) and returning NULL itself
+ * (in case of pointer return type).
+ * The only exception is the isl_ctx argument, which should never be NULL.
+ */
+struct isl_stats {
+ long gbr_solved_lps;
+};
+enum isl_error {
+ isl_error_none = 0,
+ isl_error_abort,
+ isl_error_alloc,
+ isl_error_unknown,
+ isl_error_internal,
+ isl_error_invalid,
+ isl_error_quota,
+ isl_error_unsupported
+};
+typedef enum {
+ isl_stat_error = -1,
+ isl_stat_ok = 0
+} isl_stat;
+isl_stat isl_stat_non_null(void *obj);
+typedef enum {
+ isl_bool_error = -1,
+ isl_bool_false = 0,
+ isl_bool_true = 1
+} isl_bool;
+isl_bool isl_bool_not(isl_bool b);
+isl_bool isl_bool_ok(int b);
+typedef int isl_size;
+#define isl_size_error ((int) -1)
+struct isl_ctx;
+typedef struct isl_ctx isl_ctx;
+
+/* Some helper macros */
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+#define ISL_DEPRECATED __attribute__((__deprecated__))
+#else
+#define ISL_DEPRECATED
+#endif
+
+#define ISL_FL_INIT(l, f) (l) = (f) /* Specific flags location. */
+#define ISL_FL_SET(l, f) ((l) |= (f))
+#define ISL_FL_CLR(l, f) ((l) &= ~(f))
+#define ISL_FL_ISSET(l, f) (!!((l) & (f)))
+
+#define ISL_F_INIT(p, f) ISL_FL_INIT((p)->flags, f) /* Structure element flags. */
+#define ISL_F_SET(p, f) ISL_FL_SET((p)->flags, f)
+#define ISL_F_CLR(p, f) ISL_FL_CLR((p)->flags, f)
+#define ISL_F_ISSET(p, f) ISL_FL_ISSET((p)->flags, f)
+
+void *isl_malloc_or_die(isl_ctx *ctx, size_t size);
+void *isl_calloc_or_die(isl_ctx *ctx, size_t nmemb, size_t size);
+void *isl_realloc_or_die(isl_ctx *ctx, void *ptr, size_t size);
+
+#define isl_alloc(ctx,type,size) ((type *)isl_malloc_or_die(ctx, size))
+#define isl_calloc(ctx,type,size) ((type *)isl_calloc_or_die(ctx,\
+ 1, size))
+#define isl_realloc(ctx,ptr,type,size) ((type *)isl_realloc_or_die(ctx,\
+ ptr, size))
+#define isl_alloc_type(ctx,type) isl_alloc(ctx,type,sizeof(type))
+#define isl_calloc_type(ctx,type) isl_calloc(ctx,type,sizeof(type))
+#define isl_realloc_type(ctx,ptr,type) isl_realloc(ctx,ptr,type,sizeof(type))
+#define isl_alloc_array(ctx,type,n) isl_alloc(ctx,type,(n)*sizeof(type))
+#define isl_calloc_array(ctx,type,n) ((type *)isl_calloc_or_die(ctx,\
+ n, sizeof(type)))
+#define isl_realloc_array(ctx,ptr,type,n) \
+ isl_realloc(ctx,ptr,type,(n)*sizeof(type))
+
+#define isl_die(ctx,errno,msg,code) \
+ do { \
+ isl_handle_error(ctx, errno, msg, __FILE__, __LINE__); \
+ code; \
+ } while (0)
+
+void isl_handle_error(isl_ctx *ctx, enum isl_error error, const char *msg,
+ const char *file, int line);
+
+#define isl_assert4(ctx,test,code,errno) \
+ do { \
+ if (test) \
+ break; \
+ isl_die(ctx, errno, "Assertion \"" #test "\" failed", code); \
+ } while (0)
+#define isl_assert(ctx,test,code) \
+ isl_assert4(ctx,test,code,isl_error_unknown)
+
+#define isl_min(a,b) ((a < b) ? (a) : (b))
+
+/* struct isl_ctx functions */
+
+struct isl_options *isl_ctx_options(isl_ctx *ctx);
+
+isl_ctx *isl_ctx_alloc_with_options(struct isl_args *args,
+ __isl_take void *opt);
+isl_ctx *isl_ctx_alloc(void);
+void *isl_ctx_peek_options(isl_ctx *ctx, struct isl_args *args);
+int isl_ctx_parse_options(isl_ctx *ctx, int argc, char **argv, unsigned flags);
+void isl_ctx_ref(struct isl_ctx *ctx);
+void isl_ctx_deref(struct isl_ctx *ctx);
+void isl_ctx_free(isl_ctx *ctx);
+
+void isl_ctx_abort(isl_ctx *ctx);
+void isl_ctx_resume(isl_ctx *ctx);
+int isl_ctx_aborted(isl_ctx *ctx);
+
+void isl_ctx_set_max_operations(isl_ctx *ctx, unsigned long max_operations);
+unsigned long isl_ctx_get_max_operations(isl_ctx *ctx);
+void isl_ctx_reset_operations(isl_ctx *ctx);
+
+#define ISL_ARG_CTX_DECL(prefix,st,args) \
+st *isl_ctx_peek_ ## prefix(isl_ctx *ctx);
+
+#define ISL_ARG_CTX_DEF(prefix,st,args) \
+st *isl_ctx_peek_ ## prefix(isl_ctx *ctx) \
+{ \
+ return (st *)isl_ctx_peek_options(ctx, &(args)); \
+}
+
+#define ISL_CTX_GET_INT_DEF(prefix,st,args,field) \
+int prefix ## _get_ ## field(isl_ctx *ctx) \
+{ \
+ st *options; \
+ options = isl_ctx_peek_ ## prefix(ctx); \
+ if (!options) \
+ isl_die(ctx, isl_error_invalid, \
+ "isl_ctx does not reference " #prefix, \
+ return -1); \
+ return options->field; \
+}
+
+#define ISL_CTX_SET_INT_DEF(prefix,st,args,field) \
+isl_stat prefix ## _set_ ## field(isl_ctx *ctx, int val) \
+{ \
+ st *options; \
+ options = isl_ctx_peek_ ## prefix(ctx); \
+ if (!options) \
+ isl_die(ctx, isl_error_invalid, \
+ "isl_ctx does not reference " #prefix, \
+ return isl_stat_error); \
+ options->field = val; \
+ return isl_stat_ok; \
+}
+
+#define ISL_CTX_GET_STR_DEF(prefix,st,args,field) \
+const char *prefix ## _get_ ## field(isl_ctx *ctx) \
+{ \
+ st *options; \
+ options = isl_ctx_peek_ ## prefix(ctx); \
+ if (!options) \
+ isl_die(ctx, isl_error_invalid, \
+ "isl_ctx does not reference " #prefix, \
+ return NULL); \
+ return options->field; \
+}
+
+#define ISL_CTX_SET_STR_DEF(prefix,st,args,field) \
+isl_stat prefix ## _set_ ## field(isl_ctx *ctx, const char *val) \
+{ \
+ st *options; \
+ options = isl_ctx_peek_ ## prefix(ctx); \
+ if (!options) \
+ isl_die(ctx, isl_error_invalid, \
+ "isl_ctx does not reference " #prefix, \
+ return isl_stat_error); \
+ if (!val) \
+ return isl_stat_error; \
+ free(options->field); \
+ options->field = strdup(val); \
+ if (!options->field) \
+ return isl_stat_error; \
+ return isl_stat_ok; \
+}
+
+#define ISL_CTX_GET_BOOL_DEF(prefix,st,args,field) \
+ ISL_CTX_GET_INT_DEF(prefix,st,args,field)
+
+#define ISL_CTX_SET_BOOL_DEF(prefix,st,args,field) \
+ ISL_CTX_SET_INT_DEF(prefix,st,args,field)
+
+#define ISL_CTX_GET_CHOICE_DEF(prefix,st,args,field) \
+ ISL_CTX_GET_INT_DEF(prefix,st,args,field)
+
+#define ISL_CTX_SET_CHOICE_DEF(prefix,st,args,field) \
+ ISL_CTX_SET_INT_DEF(prefix,st,args,field)
+
+enum isl_error isl_ctx_last_error(isl_ctx *ctx);
+const char *isl_ctx_last_error_msg(isl_ctx *ctx);
+const char *isl_ctx_last_error_file(isl_ctx *ctx);
+int isl_ctx_last_error_line(isl_ctx *ctx);
+void isl_ctx_reset_error(isl_ctx *ctx);
+void isl_ctx_set_error(isl_ctx *ctx, enum isl_error error);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/fixed_box.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/fixed_box.h
new file mode 100644
index 00000000000..d8c78e323bf
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/fixed_box.h
@@ -0,0 +1,43 @@
+/*
+ * Use of this software is governed by the MIT license
+ */
+
+#ifndef ISL_FIXED_BOX_H
+#define ISL_FIXED_BOX_H
+
+#include <isl/ctx.h>
+#include <isl/val_type.h>
+#include <isl/space_type.h>
+#include <isl/aff_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct __isl_export isl_fixed_box;
+typedef struct isl_fixed_box isl_fixed_box;
+
+isl_ctx *isl_fixed_box_get_ctx(__isl_keep isl_fixed_box *box);
+__isl_export
+__isl_give isl_space *isl_fixed_box_get_space(__isl_keep isl_fixed_box *box);
+__isl_export
+isl_bool isl_fixed_box_is_valid(__isl_keep isl_fixed_box *box);
+__isl_export
+__isl_give isl_multi_aff *isl_fixed_box_get_offset(
+ __isl_keep isl_fixed_box *box);
+__isl_export
+__isl_give isl_multi_val *isl_fixed_box_get_size(__isl_keep isl_fixed_box *box);
+
+__isl_give isl_fixed_box *isl_fixed_box_copy(__isl_keep isl_fixed_box *box);
+__isl_null isl_fixed_box *isl_fixed_box_free(__isl_take isl_fixed_box *box);
+
+__isl_give isl_printer *isl_printer_print_fixed_box(
+ __isl_take isl_printer *p, __isl_keep isl_fixed_box *box);
+__isl_give char *isl_fixed_box_to_str(__isl_keep isl_fixed_box *box);
+void isl_fixed_box_dump(__isl_keep isl_fixed_box *box);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/flow.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/flow.h
new file mode 100644
index 00000000000..be2f6c577f1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/flow.h
@@ -0,0 +1,156 @@
+#ifndef ISL_FLOW_H
+#define ISL_FLOW_H
+
+#include <stdio.h>
+
+#include <isl/set_type.h>
+#include <isl/map_type.h>
+#include <isl/union_set_type.h>
+#include <isl/union_map_type.h>
+#include <isl/schedule.h>
+#include <isl/printer.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Let n (>= 0) be the number of iterators shared by first and second.
+ * If first precedes second textually return 2 * n + 1,
+ * otherwise return 2 * n.
+ */
+typedef int (*isl_access_level_before)(void *first, void *second);
+
+struct isl_restriction;
+typedef struct isl_restriction isl_restriction;
+
+__isl_null isl_restriction *isl_restriction_free(
+ __isl_take isl_restriction *restr);
+__isl_give isl_restriction *isl_restriction_empty(
+ __isl_take isl_map *source_map);
+__isl_give isl_restriction *isl_restriction_none(
+ __isl_take isl_map *source_map);
+__isl_give isl_restriction *isl_restriction_input(
+ __isl_take isl_set *source_restr, __isl_take isl_set *sink_restr);
+__isl_give isl_restriction *isl_restriction_output(
+ __isl_take isl_set *source_restr);
+
+isl_ctx *isl_restriction_get_ctx(__isl_keep isl_restriction *restr);
+
+typedef __isl_give isl_restriction *(*isl_access_restrict)(
+ __isl_keep isl_map *source_map, __isl_keep isl_set *sink,
+ void *source_user, void *user);
+
+struct isl_access_info;
+typedef struct isl_access_info isl_access_info;
+struct isl_flow;
+typedef struct isl_flow isl_flow;
+
+__isl_give isl_access_info *isl_access_info_alloc(__isl_take isl_map *sink,
+ void *sink_user, isl_access_level_before fn, int max_source);
+__isl_give isl_access_info *isl_access_info_set_restrict(
+ __isl_take isl_access_info *acc, isl_access_restrict fn, void *user);
+__isl_give isl_access_info *isl_access_info_add_source(
+ __isl_take isl_access_info *acc, __isl_take isl_map *source,
+ int must, void *source_user);
+__isl_null isl_access_info *isl_access_info_free(
+ __isl_take isl_access_info *acc);
+
+isl_ctx *isl_access_info_get_ctx(__isl_keep isl_access_info *acc);
+
+__isl_give isl_flow *isl_access_info_compute_flow(__isl_take isl_access_info *acc);
+isl_stat isl_flow_foreach(__isl_keep isl_flow *deps,
+ isl_stat (*fn)(__isl_take isl_map *dep, int must, void *dep_user,
+ void *user),
+ void *user);
+__isl_give isl_map *isl_flow_get_no_source(__isl_keep isl_flow *deps, int must);
+__isl_null isl_flow *isl_flow_free(__isl_take isl_flow *deps);
+
+isl_ctx *isl_flow_get_ctx(__isl_keep isl_flow *deps);
+
+struct __isl_export isl_union_access_info;
+typedef struct isl_union_access_info isl_union_access_info;
+struct __isl_export isl_union_flow;
+typedef struct isl_union_flow isl_union_flow;
+
+__isl_constructor
+__isl_give isl_union_access_info *isl_union_access_info_from_sink(
+ __isl_take isl_union_map *sink);
+__isl_export
+__isl_give isl_union_access_info *isl_union_access_info_set_must_source(
+ __isl_take isl_union_access_info *access,
+ __isl_take isl_union_map *must_source);
+__isl_export
+__isl_give isl_union_access_info *isl_union_access_info_set_may_source(
+ __isl_take isl_union_access_info *access,
+ __isl_take isl_union_map *may_source);
+__isl_export
+__isl_give isl_union_access_info *isl_union_access_info_set_kill(
+ __isl_take isl_union_access_info *access,
+ __isl_take isl_union_map *kill);
+__isl_export
+__isl_give isl_union_access_info *isl_union_access_info_set_schedule(
+ __isl_take isl_union_access_info *access,
+ __isl_take isl_schedule *schedule);
+__isl_export
+__isl_give isl_union_access_info *isl_union_access_info_set_schedule_map(
+ __isl_take isl_union_access_info *access,
+ __isl_take isl_union_map *schedule_map);
+__isl_give isl_union_access_info *isl_union_access_info_copy(
+ __isl_keep isl_union_access_info *access);
+__isl_null isl_union_access_info *isl_union_access_info_free(
+ __isl_take isl_union_access_info *access);
+
+isl_ctx *isl_union_access_info_get_ctx(
+ __isl_keep isl_union_access_info *access);
+
+__isl_give isl_union_access_info *isl_union_access_info_read_from_file(
+ isl_ctx *ctx, FILE *input);
+__isl_give isl_printer *isl_printer_print_union_access_info(
+ __isl_take isl_printer *p, __isl_keep isl_union_access_info *access);
+__isl_give char *isl_union_access_info_to_str(
+ __isl_keep isl_union_access_info *access);
+
+__isl_export
+__isl_give isl_union_flow *isl_union_access_info_compute_flow(
+ __isl_take isl_union_access_info *access);
+
+isl_ctx *isl_union_flow_get_ctx(__isl_keep isl_union_flow *flow);
+__isl_give isl_union_flow *isl_union_flow_copy(
+ __isl_keep isl_union_flow *flow);
+__isl_export
+__isl_give isl_union_map *isl_union_flow_get_must_dependence(
+ __isl_keep isl_union_flow *flow);
+__isl_export
+__isl_give isl_union_map *isl_union_flow_get_may_dependence(
+ __isl_keep isl_union_flow *flow);
+__isl_export
+__isl_give isl_union_map *isl_union_flow_get_full_must_dependence(
+ __isl_keep isl_union_flow *flow);
+__isl_export
+__isl_give isl_union_map *isl_union_flow_get_full_may_dependence(
+ __isl_keep isl_union_flow *flow);
+__isl_export
+__isl_give isl_union_map *isl_union_flow_get_must_no_source(
+ __isl_keep isl_union_flow *flow);
+__isl_export
+__isl_give isl_union_map *isl_union_flow_get_may_no_source(
+ __isl_keep isl_union_flow *flow);
+__isl_null isl_union_flow *isl_union_flow_free(__isl_take isl_union_flow *flow);
+
+__isl_give isl_printer *isl_printer_print_union_flow(
+ __isl_take isl_printer *p, __isl_keep isl_union_flow *flow);
+__isl_give char *isl_union_flow_to_str(__isl_keep isl_union_flow *flow);
+
+int isl_union_map_compute_flow(__isl_take isl_union_map *sink,
+ __isl_take isl_union_map *must_source,
+ __isl_take isl_union_map *may_source,
+ __isl_take isl_union_map *schedule,
+ __isl_give isl_union_map **must_dep, __isl_give isl_union_map **may_dep,
+ __isl_give isl_union_map **must_no_source,
+ __isl_give isl_union_map **may_no_source);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hash.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hash.h
new file mode 100644
index 00000000000..7ef3d2e03e0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hash.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_HASH_H
+#define ISL_HASH_H
+
+#include <stdlib.h>
+#include <isl/stdint.h>
+#include <isl/ctx.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define isl_hash_init() (2166136261u)
+#define isl_hash_byte(h,b) do { \
+ h *= 16777619; \
+ h ^= b; \
+ } while(0)
+#define isl_hash_hash(h,h2) \
+ do { \
+ isl_hash_byte(h, (h2) & 0xFF); \
+ isl_hash_byte(h, ((h2) >> 8) & 0xFF); \
+ isl_hash_byte(h, ((h2) >> 16) & 0xFF); \
+ isl_hash_byte(h, ((h2) >> 24) & 0xFF); \
+ } while(0)
+#define isl_hash_bits(h,bits) \
+ ((bits) == 32) ? (h) : \
+ ((bits) >= 16) ? \
+ ((h) >> (bits)) ^ ((h) & (((uint32_t)1 << (bits)) - 1)) : \
+ (((h) >> (bits)) ^ (h)) & (((uint32_t)1 << (bits)) - 1)
+
+uint32_t isl_hash_string(uint32_t hash, const char *s);
+uint32_t isl_hash_mem(uint32_t hash, const void *p, size_t len);
+
+#define isl_hash_builtin(h,l) isl_hash_mem(h, &l, sizeof(l))
+
+struct isl_hash_table_entry
+{
+ uint32_t hash;
+ void *data;
+};
+
+struct isl_hash_table {
+ int bits;
+ int n;
+ struct isl_hash_table_entry *entries;
+};
+
+struct isl_hash_table *isl_hash_table_alloc(struct isl_ctx *ctx, int min_size);
+void isl_hash_table_free(struct isl_ctx *ctx, struct isl_hash_table *table);
+
+int isl_hash_table_init(struct isl_ctx *ctx, struct isl_hash_table *table,
+ int min_size);
+void isl_hash_table_clear(struct isl_hash_table *table);
+extern struct isl_hash_table_entry *isl_hash_table_entry_none;
+struct isl_hash_table_entry *isl_hash_table_find(struct isl_ctx *ctx,
+ struct isl_hash_table *table,
+ uint32_t key_hash,
+ isl_bool (*eq)(const void *entry, const void *val),
+ const void *val, int reserve);
+isl_stat isl_hash_table_foreach(isl_ctx *ctx, struct isl_hash_table *table,
+ isl_stat (*fn)(void **entry, void *user), void *user);
+void isl_hash_table_remove(struct isl_ctx *ctx,
+ struct isl_hash_table *table,
+ struct isl_hash_table_entry *entry);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hmap.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hmap.h
new file mode 100644
index 00000000000..21612217032
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hmap.h
@@ -0,0 +1,55 @@
+#include <isl/ctx.h>
+#include <isl/maybe.h>
+#include <isl/printer.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define ISL_xCAT(A,B) A ## B
+#define ISL_CAT(A,B) ISL_xCAT(A,B)
+#define ISL_xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define ISL_FN(TYPE,NAME) ISL_xFN(TYPE,NAME)
+
+struct ISL_HMAP;
+typedef struct ISL_HMAP ISL_HMAP;
+
+__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,alloc)(isl_ctx *ctx, int min_size);
+__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,copy)(__isl_keep ISL_HMAP *hmap);
+__isl_null ISL_HMAP *ISL_FN(ISL_HMAP,free)(__isl_take ISL_HMAP *hmap);
+
+isl_ctx *ISL_FN(ISL_HMAP,get_ctx)(__isl_keep ISL_HMAP *hmap);
+
+__isl_give ISL_MAYBE(ISL_VAL) ISL_FN(ISL_HMAP,try_get)(
+ __isl_keep ISL_HMAP *hmap, __isl_keep ISL_KEY *key);
+isl_bool ISL_FN(ISL_HMAP,has)(__isl_keep ISL_HMAP *hmap,
+ __isl_keep ISL_KEY *key);
+__isl_give ISL_VAL *ISL_FN(ISL_HMAP,get)(__isl_keep ISL_HMAP *hmap,
+ __isl_take ISL_KEY *key);
+__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,set)(__isl_take ISL_HMAP *hmap,
+ __isl_take ISL_KEY *key, __isl_take ISL_VAL *val);
+__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,drop)(__isl_take ISL_HMAP *hmap,
+ __isl_take ISL_KEY *key);
+
+isl_stat ISL_FN(ISL_HMAP,foreach)(__isl_keep ISL_HMAP *hmap,
+ isl_stat (*fn)(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val,
+ void *user),
+ void *user);
+
+__isl_give isl_printer *ISL_FN(isl_printer_print,ISL_HMAP_SUFFIX)(
+ __isl_take isl_printer *p, __isl_keep ISL_HMAP *hmap);
+void ISL_FN(ISL_HMAP,dump)(__isl_keep ISL_HMAP *hmap);
+
+#undef ISL_xCAT
+#undef ISL_CAT
+#undef ISL_KEY
+#undef ISL_VAL
+#undef ISL_xFN
+#undef ISL_FN
+#undef ISL_xHMAP
+#undef ISL_yHMAP
+#undef ISL_HMAP
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hmap_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hmap_templ.c
new file mode 100644
index 00000000000..ceb52bd6937
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/hmap_templ.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright 2011 INRIA Saclay
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <isl/ctx.h>
+#include <isl/hash.h>
+
+#define ISL_xCAT(A,B) A ## B
+#define ISL_CAT(A,B) ISL_xCAT(A,B)
+#define ISL_xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define ISL_FN(TYPE,NAME) ISL_xFN(TYPE,NAME)
+#define ISL_xS(TYPE1,TYPE2,NAME) struct isl_ ## TYPE1 ## _ ## TYPE2 ## _ ## NAME
+#define ISL_yS(TYPE1,TYPE2,NAME) ISL_xS(TYPE1,TYPE2,NAME)
+#define ISL_S(NAME) ISL_yS(ISL_KEY,ISL_VAL,NAME)
+
+struct ISL_HMAP {
+ int ref;
+ isl_ctx *ctx;
+ struct isl_hash_table table;
+};
+
+ISL_S(pair) {
+ ISL_KEY *key;
+ ISL_VAL *val;
+};
+
+__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,alloc)(isl_ctx *ctx, int min_size)
+{
+ ISL_HMAP *hmap;
+
+ hmap = isl_calloc_type(ctx, ISL_HMAP);
+ if (!hmap)
+ return NULL;
+
+ hmap->ctx = ctx;
+ isl_ctx_ref(ctx);
+ hmap->ref = 1;
+
+ if (isl_hash_table_init(ctx, &hmap->table, min_size) < 0)
+ return ISL_FN(ISL_HMAP,free)(hmap);
+
+ return hmap;
+}
+
+static isl_stat free_pair(void **entry, void *user)
+{
+ ISL_S(pair) *pair = *entry;
+ ISL_FN(ISL_KEY,free)(pair->key);
+ ISL_FN(ISL_VAL,free)(pair->val);
+ free(pair);
+ *entry = NULL;
+ return isl_stat_ok;
+}
+
+__isl_null ISL_HMAP *ISL_FN(ISL_HMAP,free)(__isl_take ISL_HMAP *hmap)
+{
+ if (!hmap)
+ return NULL;
+ if (--hmap->ref > 0)
+ return NULL;
+ isl_hash_table_foreach(hmap->ctx, &hmap->table, &free_pair, NULL);
+ isl_hash_table_clear(&hmap->table);
+ isl_ctx_deref(hmap->ctx);
+ free(hmap);
+ return NULL;
+}
+
+isl_ctx *ISL_FN(ISL_HMAP,get_ctx)(__isl_keep ISL_HMAP *hmap)
+{
+ return hmap ? hmap->ctx : NULL;
+}
+
+/* Add a mapping from "key" to "val" to the associative array
+ * pointed to by user.
+ */
+static isl_stat add_key_val(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val,
+ void *user)
+{
+ ISL_HMAP **hmap = (ISL_HMAP **) user;
+
+ *hmap = ISL_FN(ISL_HMAP,set)(*hmap, key, val);
+
+ if (!*hmap)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,dup)(__isl_keep ISL_HMAP *hmap)
+{
+ ISL_HMAP *dup;
+
+ if (!hmap)
+ return NULL;
+
+ dup = ISL_FN(ISL_HMAP,alloc)(hmap->ctx, hmap->table.n);
+ if (ISL_FN(ISL_HMAP,foreach)(hmap, &add_key_val, &dup) < 0)
+ return ISL_FN(ISL_HMAP,free)(dup);
+
+ return dup;
+}
+
+__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,cow)(__isl_take ISL_HMAP *hmap)
+{
+ if (!hmap)
+ return NULL;
+
+ if (hmap->ref == 1)
+ return hmap;
+ hmap->ref--;
+ return ISL_FN(ISL_HMAP,dup)(hmap);
+}
+
+__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,copy)(__isl_keep ISL_HMAP *hmap)
+{
+ if (!hmap)
+ return NULL;
+
+ hmap->ref++;
+ return hmap;
+}
+
+static isl_bool has_key(const void *entry, const void *c_key)
+{
+ const ISL_S(pair) *pair = entry;
+ ISL_KEY *key = (ISL_KEY *) c_key;
+
+ return ISL_KEY_IS_EQUAL(pair->key, key);
+}
+
+/* If "hmap" contains a value associated to "key", then return
+ * (isl_bool_true, copy of value).
+ * Otherwise, return
+ * (isl_bool_false, NULL).
+ * If an error occurs, then return
+ * (isl_bool_error, NULL).
+ */
+__isl_give ISL_MAYBE(ISL_VAL) ISL_FN(ISL_HMAP,try_get)(
+ __isl_keep ISL_HMAP *hmap, __isl_keep ISL_KEY *key)
+{
+ struct isl_hash_table_entry *entry;
+ ISL_S(pair) *pair;
+ uint32_t hash;
+ ISL_MAYBE(ISL_VAL) res = { isl_bool_false, NULL };
+
+ if (!hmap || !key)
+ goto error;
+
+ hash = ISL_FN(ISL_KEY,get_hash)(key);
+ entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash,
+ &has_key, key, 0);
+
+ if (!entry)
+ goto error;
+ if (entry == isl_hash_table_entry_none)
+ return res;
+
+ pair = entry->data;
+
+ res.valid = isl_bool_true;
+ res.value = ISL_FN(ISL_VAL,copy)(pair->val);
+ if (!res.value)
+ res.valid = isl_bool_error;
+ return res;
+error:
+ res.valid = isl_bool_error;
+ res.value = NULL;
+ return res;
+}
+
+/* If "hmap" contains a value associated to "key", then return
+ * isl_bool_true. Otherwise, return isl_bool_false.
+ * Return isl_bool_error on error.
+ */
+isl_bool ISL_FN(ISL_HMAP,has)(__isl_keep ISL_HMAP *hmap,
+ __isl_keep ISL_KEY *key)
+{
+ ISL_MAYBE(ISL_VAL) res;
+
+ res = ISL_FN(ISL_HMAP,try_get)(hmap, key);
+ ISL_FN(ISL_VAL,free)(res.value);
+
+ return res.valid;
+}
+
+/* If "hmap" contains a value associated to "key", then return
+ * a copy of that value. Otherwise, return NULL.
+ * Return NULL on error.
+ */
+__isl_give ISL_VAL *ISL_FN(ISL_HMAP,get)(__isl_keep ISL_HMAP *hmap,
+ __isl_take ISL_KEY *key)
+{
+ ISL_VAL *res;
+
+ res = ISL_FN(ISL_HMAP,try_get)(hmap, key).value;
+ ISL_FN(ISL_KEY,free)(key);
+ return res;
+}
+
+/* Remove the mapping between "key" and its associated value (if any)
+ * from "hmap".
+ *
+ * If "key" is not mapped to anything, then we leave "hmap" untouched"
+ */
+__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,drop)(__isl_take ISL_HMAP *hmap,
+ __isl_take ISL_KEY *key)
+{
+ struct isl_hash_table_entry *entry;
+ ISL_S(pair) *pair;
+ uint32_t hash;
+
+ if (!hmap || !key)
+ goto error;
+
+ hash = ISL_FN(ISL_KEY,get_hash)(key);
+ entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash,
+ &has_key, key, 0);
+ if (!entry)
+ goto error;
+ if (entry == isl_hash_table_entry_none) {
+ ISL_FN(ISL_KEY,free)(key);
+ return hmap;
+ }
+
+ hmap = ISL_FN(ISL_HMAP,cow)(hmap);
+ if (!hmap)
+ goto error;
+ entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash,
+ &has_key, key, 0);
+ ISL_FN(ISL_KEY,free)(key);
+
+ if (!entry)
+ return ISL_FN(ISL_HMAP,free)(hmap);
+ if (entry == isl_hash_table_entry_none)
+ isl_die(hmap->ctx, isl_error_internal,
+ "missing entry" , return ISL_FN(ISL_HMAP,free)(hmap));
+
+ pair = entry->data;
+ isl_hash_table_remove(hmap->ctx, &hmap->table, entry);
+ ISL_FN(ISL_KEY,free)(pair->key);
+ ISL_FN(ISL_VAL,free)(pair->val);
+ free(pair);
+
+ return hmap;
+error:
+ ISL_FN(ISL_KEY,free)(key);
+ ISL_FN(ISL_HMAP,free)(hmap);
+ return NULL;
+}
+
+/* Add a mapping from "key" to "val" to "hmap".
+ * If "key" was already mapped to something else, then that mapping
+ * is replaced.
+ * If key happened to be mapped to "val" already, then we leave
+ * "hmap" untouched.
+ */
+__isl_give ISL_HMAP *ISL_FN(ISL_HMAP,set)(__isl_take ISL_HMAP *hmap,
+ __isl_take ISL_KEY *key, __isl_take ISL_VAL *val)
+{
+ struct isl_hash_table_entry *entry;
+ ISL_S(pair) *pair;
+ uint32_t hash;
+
+ if (!hmap || !key || !val)
+ goto error;
+
+ hash = ISL_FN(ISL_KEY,get_hash)(key);
+ entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash,
+ &has_key, key, 0);
+ if (!entry)
+ goto error;
+ if (entry != isl_hash_table_entry_none) {
+ isl_bool equal;
+ pair = entry->data;
+ equal = ISL_VAL_IS_EQUAL(pair->val, val);
+ if (equal < 0)
+ goto error;
+ if (equal) {
+ ISL_FN(ISL_KEY,free)(key);
+ ISL_FN(ISL_VAL,free)(val);
+ return hmap;
+ }
+ }
+
+ hmap = ISL_FN(ISL_HMAP,cow)(hmap);
+ if (!hmap)
+ goto error;
+
+ entry = isl_hash_table_find(hmap->ctx, &hmap->table, hash,
+ &has_key, key, 1);
+
+ if (!entry)
+ goto error;
+
+ if (entry->data) {
+ pair = entry->data;
+ ISL_FN(ISL_VAL,free)(pair->val);
+ pair->val = val;
+ ISL_FN(ISL_KEY,free)(key);
+ return hmap;
+ }
+
+ pair = isl_alloc_type(hmap->ctx, ISL_S(pair));
+ if (!pair)
+ goto error;
+
+ entry->data = pair;
+ pair->key = key;
+ pair->val = val;
+ return hmap;
+error:
+ ISL_FN(ISL_KEY,free)(key);
+ ISL_FN(ISL_VAL,free)(val);
+ return ISL_FN(ISL_HMAP,free)(hmap);
+}
+
+/* Internal data structure for isl_map_to_basic_set_foreach.
+ *
+ * fn is the function that should be called on each entry.
+ * user is the user-specified final argument to fn.
+ */
+ISL_S(foreach_data) {
+ isl_stat (*fn)(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val,
+ void *user);
+ void *user;
+};
+
+/* Call data->fn on a copy of the key and value in *entry.
+ */
+static isl_stat call_on_copy(void **entry, void *user)
+{
+ ISL_S(pair) *pair = *entry;
+ ISL_S(foreach_data) *data = (ISL_S(foreach_data) *) user;
+
+ return data->fn(ISL_FN(ISL_KEY,copy)(pair->key),
+ ISL_FN(ISL_VAL,copy)(pair->val), data->user);
+}
+
+/* Call "fn" on each pair of key and value in "hmap".
+ */
+isl_stat ISL_FN(ISL_HMAP,foreach)(__isl_keep ISL_HMAP *hmap,
+ isl_stat (*fn)(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val,
+ void *user),
+ void *user)
+{
+ ISL_S(foreach_data) data = { fn, user };
+
+ if (!hmap)
+ return isl_stat_error;
+
+ return isl_hash_table_foreach(hmap->ctx, &hmap->table,
+ &call_on_copy, &data);
+}
+
+/* Internal data structure for print_pair.
+ *
+ * p is the printer on which the associative array is being printed.
+ * first is set if the current key-value pair is the first to be printed.
+ */
+ISL_S(print_data) {
+ isl_printer *p;
+ int first;
+};
+
+/* Print the given key-value pair to data->p.
+ */
+static isl_stat print_pair(__isl_take ISL_KEY *key, __isl_take ISL_VAL *val,
+ void *user)
+{
+ ISL_S(print_data) *data = user;
+
+ if (!data->first)
+ data->p = isl_printer_print_str(data->p, ", ");
+ data->p = ISL_KEY_PRINT(data->p, key);
+ data->p = isl_printer_print_str(data->p, ": ");
+ data->p = ISL_VAL_PRINT(data->p, val);
+ data->first = 0;
+
+ ISL_FN(ISL_KEY,free)(key);
+ ISL_FN(ISL_VAL,free)(val);
+ return isl_stat_ok;
+}
+
+/* Print the associative array to "p".
+ */
+__isl_give isl_printer *ISL_FN(isl_printer_print,ISL_HMAP_SUFFIX)(
+ __isl_take isl_printer *p, __isl_keep ISL_HMAP *hmap)
+{
+ ISL_S(print_data) data;
+
+ if (!p || !hmap)
+ return isl_printer_free(p);
+
+ p = isl_printer_print_str(p, "{");
+ data.p = p;
+ data.first = 1;
+ if (ISL_FN(ISL_HMAP,foreach)(hmap, &print_pair, &data) < 0)
+ data.p = isl_printer_free(data.p);
+ p = data.p;
+ p = isl_printer_print_str(p, "}");
+
+ return p;
+}
+
+void ISL_FN(ISL_HMAP,dump)(__isl_keep ISL_HMAP *hmap)
+{
+ isl_printer *printer;
+
+ if (!hmap)
+ return;
+
+ printer = isl_printer_to_file(ISL_FN(ISL_HMAP,get_ctx)(hmap), stderr);
+ printer = ISL_FN(isl_printer_print,ISL_HMAP_SUFFIX)(printer, hmap);
+ printer = isl_printer_end_line(printer);
+
+ isl_printer_free(printer);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id.h
new file mode 100644
index 00000000000..3e71cb007cd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id.h
@@ -0,0 +1,54 @@
+#ifndef ISL_ID_H
+#define ISL_ID_H
+
+#include <isl/ctx.h>
+#include <isl/id_type.h>
+#include <isl/list.h>
+#include <isl/multi.h>
+#include <isl/printer_type.h>
+#include <isl/stdint.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+ISL_DECLARE_EXPORTED_LIST_FN(id)
+ISL_DECLARE_EXPORTED_LIST_FN_READ(id)
+
+ISL_DECLARE_MULTI(id)
+
+isl_ctx *isl_id_get_ctx(__isl_keep isl_id *id);
+uint32_t isl_id_get_hash(__isl_keep isl_id *id);
+
+__isl_give isl_id *isl_id_alloc(isl_ctx *ctx,
+ __isl_keep const char *name, void *user);
+__isl_give isl_id *isl_id_copy(isl_id *id);
+__isl_null isl_id *isl_id_free(__isl_take isl_id *id);
+
+void *isl_id_get_user(__isl_keep isl_id *id);
+__isl_export
+__isl_keep const char *isl_id_get_name(__isl_keep isl_id *id);
+
+__isl_give isl_id *isl_id_set_free_user(__isl_take isl_id *id,
+ void (*free_user)(void *user));
+
+__isl_constructor
+__isl_give isl_id *isl_id_read_from_str(isl_ctx *ctx, const char *str);
+__isl_give char *isl_id_to_str(__isl_keep isl_id *id);
+__isl_give isl_printer *isl_printer_print_id(__isl_take isl_printer *p,
+ __isl_keep isl_id *id);
+void isl_id_dump(__isl_keep isl_id *id);
+
+__isl_constructor
+__isl_give isl_multi_id *isl_multi_id_read_from_str(isl_ctx *ctx,
+ const char *str);
+__isl_give isl_printer *isl_printer_print_multi_id(__isl_take isl_printer *p,
+ __isl_keep isl_multi_id *mi);
+void isl_multi_id_dump(__isl_keep isl_multi_id *mi);
+__isl_give char *isl_multi_id_to_str(__isl_keep isl_multi_id *mi);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_ast_expr.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_ast_expr.h
new file mode 100644
index 00000000000..5822241e3f4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_ast_expr.h
@@ -0,0 +1,18 @@
+#ifndef ISL_ID_TO_AST_EXPR_H
+#define ISL_ID_TO_AST_EXPR_H
+
+#include <isl/id_type.h>
+#include <isl/ast_type.h>
+#include <isl/maybe_ast_expr.h>
+
+#define ISL_KEY isl_id
+#define ISL_VAL isl_ast_expr
+#define ISL_HMAP_SUFFIX id_to_ast_expr
+#define ISL_HMAP isl_id_to_ast_expr
+#include <isl/hmap.h>
+#undef ISL_KEY
+#undef ISL_VAL
+#undef ISL_HMAP_SUFFIX
+#undef ISL_HMAP
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_id.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_id.h
new file mode 100644
index 00000000000..309011577d1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_id.h
@@ -0,0 +1,17 @@
+#ifndef ISL_ID_TO_ID_H
+#define ISL_ID_TO_ID_H
+
+#include <isl/id_type.h>
+#include <isl/maybe_id.h>
+
+#define ISL_KEY isl_id
+#define ISL_VAL isl_id
+#define ISL_HMAP_SUFFIX id_to_id
+#define ISL_HMAP isl_id_to_id
+#include <isl/hmap.h>
+#undef ISL_KEY
+#undef ISL_VAL
+#undef ISL_HMAP_SUFFIX
+#undef ISL_HMAP
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_pw_aff.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_pw_aff.h
new file mode 100644
index 00000000000..bcbea660d79
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_to_pw_aff.h
@@ -0,0 +1,18 @@
+#ifndef ISL_ID_TO_PW_AFF_H
+#define ISL_ID_TO_PW_AFF_H
+
+#include <isl/id_type.h>
+#include <isl/aff_type.h>
+#include <isl/maybe_pw_aff.h>
+
+#define ISL_KEY isl_id
+#define ISL_VAL isl_pw_aff
+#define ISL_HMAP_SUFFIX id_to_pw_aff
+#define ISL_HMAP isl_id_to_pw_aff
+#include <isl/hmap.h>
+#undef ISL_KEY
+#undef ISL_VAL
+#undef ISL_HMAP_SUFFIX
+#undef ISL_HMAP
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_type.h
new file mode 100644
index 00000000000..0cb3c1c43b2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/id_type.h
@@ -0,0 +1,22 @@
+#ifndef ISL_ID_TYPE_H
+#define ISL_ID_TYPE_H
+
+#include <isl/list.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct __isl_export isl_id;
+typedef struct isl_id isl_id;
+
+ISL_DECLARE_EXPORTED_LIST_TYPE(id)
+
+struct __isl_export isl_multi_id;
+typedef struct isl_multi_id isl_multi_id;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ilp.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ilp.h
new file mode 100644
index 00000000000..a2be08e5c0e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/ilp.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_ILP_H
+#define ISL_ILP_H
+
+#include <isl/aff_type.h>
+#include <isl/set_type.h>
+#include <isl/union_set_type.h>
+#include <isl/val_type.h>
+#include <isl/vec.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+__isl_give isl_val *isl_basic_set_max_val(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_aff *obj);
+__isl_export
+__isl_give isl_val *isl_set_min_val(__isl_keep isl_set *set,
+ __isl_keep isl_aff *obj);
+__isl_export
+__isl_give isl_val *isl_set_max_val(__isl_keep isl_set *set,
+ __isl_keep isl_aff *obj);
+__isl_give isl_multi_val *isl_union_set_min_multi_union_pw_aff(
+ __isl_keep isl_union_set *uset, __isl_keep isl_multi_union_pw_aff *obj);
+
+__isl_export
+__isl_give isl_multi_val *isl_pw_multi_aff_min_multi_val(
+ __isl_take isl_pw_multi_aff *pma);
+__isl_export
+__isl_give isl_multi_val *isl_pw_multi_aff_max_multi_val(
+ __isl_take isl_pw_multi_aff *pma);
+__isl_export
+__isl_give isl_multi_val *isl_multi_pw_aff_min_multi_val(
+ __isl_take isl_multi_pw_aff *mpa);
+__isl_export
+__isl_give isl_multi_val *isl_multi_pw_aff_max_multi_val(
+ __isl_take isl_multi_pw_aff *mpa);
+
+__isl_give isl_val *isl_union_pw_aff_min_val(__isl_take isl_union_pw_aff *upa);
+__isl_give isl_val *isl_union_pw_aff_max_val(__isl_take isl_union_pw_aff *upa);
+
+__isl_give isl_multi_val *isl_multi_union_pw_aff_min_multi_val(
+ __isl_take isl_multi_union_pw_aff *mupa);
+__isl_give isl_multi_val *isl_multi_union_pw_aff_max_multi_val(
+ __isl_take isl_multi_union_pw_aff *mupa);
+
+__isl_export
+__isl_give isl_val *isl_basic_set_dim_max_val(__isl_take isl_basic_set *bset,
+ int pos);
+__isl_export
+__isl_give isl_val *isl_set_dim_min_val(__isl_take isl_set *set, int pos);
+__isl_export
+__isl_give isl_val *isl_set_dim_max_val(__isl_take isl_set *set, int pos);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/isl-noexceptions.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/isl-noexceptions.h
new file mode 100644
index 00000000000..e5727532b2c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/isl-noexceptions.h
@@ -0,0 +1,22973 @@
+/// These are automatically generated checked C++ bindings for isl.
+///
+/// isl is a library for computing with integer sets and maps described by
+/// Presburger formulas. On top of this, isl provides various tools for
+/// polyhedral compilation, ranging from dependence analysis over scheduling
+/// to AST generation.
+
+// clang-format off
+
+#ifndef ISL_CPP_CHECKED
+#define ISL_CPP_CHECKED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <isl/set.h>
+
+#include <functional>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <type_traits>
+
+namespace isl {
+
+#define ISLPP_STRINGIZE_(X) #X
+#define ISLPP_STRINGIZE(X) ISLPP_STRINGIZE_(X)
+
+#define ISLPP_ASSERT(test, message) \
+ do { \
+ if (test) \
+ break; \
+ fputs("Assertion \"" #test "\" failed at " __FILE__ \
+ ":" ISLPP_STRINGIZE(__LINE__) "\n " message "\n", \
+ stderr); \
+ abort(); \
+ } while (0)
+
+/* Class used to check that isl::checked::boolean,
+ * isl::checked::stat and isl::checked::size values are checked for errors.
+ */
+struct checker {
+ bool checked = false;
+ ~checker() {
+ //ISLPP_ASSERT(checked, "IMPLEMENTATION ERROR: Unchecked state");
+ }
+};
+
+class boolean {
+private:
+ mutable std::shared_ptr<checker> check = std::make_shared<checker>();
+ isl_bool val;
+
+ friend boolean manage(isl_bool val);
+ boolean(isl_bool val): val(val) {}
+public:
+ static boolean error() {
+ return boolean(isl_bool_error);
+ }
+ boolean()
+ : val(isl_bool_error) {}
+
+ /* implicit */ boolean(bool val)
+ : val(val ? isl_bool_true : isl_bool_false) {}
+
+ isl_bool release() {
+ auto tmp = val;
+ val = isl_bool_error;
+ check->checked = true;
+ return tmp;
+ }
+
+ bool is_error() const { check->checked = true; return val == isl_bool_error; }
+ bool is_false() const { check->checked = true; return val == isl_bool_false; }
+ bool is_true() const { check->checked = true; return val == isl_bool_true; }
+
+ operator bool() const {
+ //ISLPP_ASSERT(check->checked, "IMPLEMENTATION ERROR: Unchecked error state");
+ ISLPP_ASSERT(!is_error(), "IMPLEMENTATION ERROR: Unhandled error state");
+ return is_true();
+ }
+
+ boolean negate() {
+ if (val == isl_bool_true)
+ val = isl_bool_false;
+ else if (val == isl_bool_false)
+ val = isl_bool_true;
+ return *this;
+ }
+
+ boolean operator!() const {
+ return boolean(*this).negate();
+ }
+};
+
+inline boolean manage(isl_bool val) {
+ return boolean(val);
+}
+
+class ctx {
+ isl_ctx *ptr;
+public:
+ /* implicit */ ctx(isl_ctx *ctx)
+ : ptr(ctx) {}
+ isl_ctx *release() {
+ auto tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+ }
+ isl_ctx *get() {
+ return ptr;
+ }
+};
+
+/* Class encapsulating an isl_stat value.
+ */
+class stat {
+private:
+ mutable std::shared_ptr<checker> check = std::make_shared<checker>();
+ isl_stat val;
+
+ friend stat manage(isl_stat val);
+ stat(isl_stat val) : val(val) {}
+public:
+ static stat ok() {
+ return stat(isl_stat_ok);
+ }
+ static stat error() {
+ return stat(isl_stat_error);
+ }
+ stat() : val(isl_stat_error) {}
+
+ isl_stat release() {
+ check->checked = true;
+ return val;
+ }
+
+ bool is_error() const {
+ check->checked = true;
+ return val == isl_stat_error;
+ }
+ bool is_ok() const {
+ check->checked = true;
+ return val == isl_stat_ok;
+ }
+};
+
+inline stat manage(isl_stat val)
+{
+ return stat(val);
+}
+
+/* Class encapsulating an isl_size value.
+ */
+class size {
+private:
+ mutable std::shared_ptr<checker> check = std::make_shared<checker>();
+ isl_size val;
+
+ friend size manage(isl_size val);
+ size(isl_size val) : val(val) {}
+public:
+ size() : val(isl_size_error) {}
+
+ isl_size release() {
+ auto tmp = val;
+ val = isl_size_error;
+ check->checked = true;
+ return tmp;
+ }
+
+ bool is_error() const {
+ check->checked = true;
+ return val == isl_size_error;
+ }
+
+ explicit operator unsigned() const {
+ ISLPP_ASSERT(check->checked,
+ "IMPLEMENTATION ERROR: Unchecked error state");
+ ISLPP_ASSERT(!is_error(),
+ "IMPLEMENTATION ERROR: Unhandled error state");
+ return val;
+ }
+};
+
+inline size manage(isl_size val)
+{
+ return size(val);
+}
+
+enum class dim {
+ cst = isl_dim_cst,
+ param = isl_dim_param,
+ in = isl_dim_in,
+ out = isl_dim_out,
+ set = isl_dim_set,
+ div = isl_dim_div,
+ all = isl_dim_all
+};
+
+} // namespace isl
+#include <isl/id.h>
+#include <isl/space.h>
+#include <isl/val.h>
+#include <isl/aff.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl/ilp.h>
+#include <isl/constraint.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl/flow.h>
+#include <isl/schedule.h>
+#include <isl/schedule_node.h>
+#include <isl/ast_build.h>
+#include <isl/fixed_box.h>
+
+namespace isl {
+
+// forward declarations
+class aff;
+class aff_list;
+class ast_build;
+class ast_expr;
+class ast_expr_id;
+class ast_expr_int;
+class ast_expr_op;
+class ast_expr_op_access;
+class ast_expr_op_add;
+class ast_expr_op_address_of;
+class ast_expr_op_and;
+class ast_expr_op_and_then;
+class ast_expr_op_call;
+class ast_expr_op_cond;
+class ast_expr_op_div;
+class ast_expr_op_eq;
+class ast_expr_op_fdiv_q;
+class ast_expr_op_ge;
+class ast_expr_op_gt;
+class ast_expr_op_le;
+class ast_expr_op_lt;
+class ast_expr_op_max;
+class ast_expr_op_member;
+class ast_expr_op_min;
+class ast_expr_op_minus;
+class ast_expr_op_mul;
+class ast_expr_op_or;
+class ast_expr_op_or_else;
+class ast_expr_op_pdiv_q;
+class ast_expr_op_pdiv_r;
+class ast_expr_op_select;
+class ast_expr_op_sub;
+class ast_expr_op_zdiv_r;
+class ast_node;
+class ast_node_block;
+class ast_node_for;
+class ast_node_if;
+class ast_node_list;
+class ast_node_mark;
+class ast_node_user;
+class basic_map;
+class basic_map_list;
+class basic_set;
+class basic_set_list;
+class constraint;
+class fixed_box;
+class id;
+class id_list;
+class id_to_ast_expr;
+class local_space;
+class map;
+class map_list;
+class multi_aff;
+class multi_id;
+class multi_pw_aff;
+class multi_union_pw_aff;
+class multi_val;
+class point;
+class pw_aff;
+class pw_aff_list;
+class pw_multi_aff;
+class pw_multi_aff_list;
+class schedule;
+class schedule_constraints;
+class schedule_node;
+class schedule_node_band;
+class schedule_node_context;
+class schedule_node_domain;
+class schedule_node_expansion;
+class schedule_node_extension;
+class schedule_node_filter;
+class schedule_node_guard;
+class schedule_node_leaf;
+class schedule_node_mark;
+class schedule_node_sequence;
+class schedule_node_set;
+class set;
+class set_list;
+class space;
+class union_access_info;
+class union_flow;
+class union_map;
+class union_pw_aff;
+class union_pw_aff_list;
+class union_pw_multi_aff;
+class union_set;
+class union_set_list;
+class val;
+class val_list;
+
+// declarations for isl::aff
+inline aff manage(__isl_take isl_aff *ptr);
+inline aff manage_copy(__isl_keep isl_aff *ptr);
+
+class aff {
+ friend inline aff manage(__isl_take isl_aff *ptr);
+ friend inline aff manage_copy(__isl_keep isl_aff *ptr);
+
+protected:
+ isl_aff *ptr = nullptr;
+
+ inline explicit aff(__isl_take isl_aff *ptr);
+
+public:
+ inline /* implicit */ aff();
+ inline /* implicit */ aff(const aff &obj);
+ inline explicit aff(isl::ctx ctx, const std::string &str);
+ inline explicit aff(isl::local_space ls, isl::val val);
+ inline explicit aff(isl::local_space ls);
+ inline aff &operator=(aff obj);
+ inline ~aff();
+ inline __isl_give isl_aff *copy() const &;
+ inline __isl_give isl_aff *copy() && = delete;
+ inline __isl_keep isl_aff *get() const;
+ inline __isl_give isl_aff *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::aff add(isl::aff aff2) const;
+ inline isl::multi_aff add(const isl::multi_aff &multi2) const;
+ inline isl::multi_pw_aff add(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff add(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_aff add(const isl::pw_aff &pwaff2) const;
+ inline isl::pw_multi_aff add(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_aff add(const isl::union_pw_aff &upa2) const;
+ inline isl::union_pw_multi_aff add(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::aff add_constant(isl::val v) const;
+ inline isl::aff add_constant(long v) const;
+ inline isl::multi_aff add_constant(const isl::multi_val &mv) const;
+ inline isl::aff add_constant_si(int v) const;
+ inline isl::pw_aff add_dims(isl::dim type, unsigned int n) const;
+ inline isl::union_pw_multi_aff add_pw_multi_aff(const isl::pw_multi_aff &pma) const;
+ inline isl::union_pw_multi_aff apply(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::aff as_aff() const;
+ inline isl::map as_map() const;
+ inline isl::multi_aff as_multi_aff() const;
+ inline isl::multi_union_pw_aff as_multi_union_pw_aff() const;
+ inline isl::pw_multi_aff as_pw_multi_aff() const;
+ inline isl::set as_set() const;
+ inline isl::union_map as_union_map() const;
+ inline isl::aff at(int pos) const;
+ inline isl::basic_set bind(isl::id id) const;
+ inline isl::basic_set bind(const std::string &id) const;
+ inline isl::basic_set bind(const isl::multi_id &tuple) const;
+ inline isl::pw_aff bind_domain(const isl::multi_id &tuple) const;
+ inline isl::pw_aff bind_domain_wrapped_domain(const isl::multi_id &tuple) const;
+ inline isl::aff ceil() const;
+ inline isl::pw_aff coalesce() const;
+ inline isl::pw_aff cond(const isl::pw_aff &pwaff_true, const isl::pw_aff &pwaff_false) const;
+ inline isl::multi_val constant_multi_val() const;
+ inline isl::val constant_val() const;
+ inline isl::val get_constant_val() const;
+ inline isl::val denominator_val() const;
+ inline isl::val get_denominator_val() const;
+ inline class size dim(isl::dim type) const;
+ inline isl::id dim_id(isl::dim type, unsigned int pos) const;
+ inline isl::aff div(isl::aff aff2) const;
+ inline isl::pw_aff div(const isl::pw_aff &pa2) const;
+ inline isl::set domain() const;
+ inline isl::space domain_space() const;
+ inline isl::pw_multi_aff drop_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::set eq_set(isl::aff aff2) const;
+ inline isl::set eq_set(const isl::pw_aff &pwaff2) const;
+ inline isl::val eval(isl::point pnt) const;
+ inline isl::pw_multi_aff extract_pw_multi_aff(const isl::space &space) const;
+ inline isl::multi_aff flat_range_product(const isl::multi_aff &multi2) const;
+ inline isl::multi_pw_aff flat_range_product(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff flat_range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff flat_range_product(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff flat_range_product(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::aff floor() const;
+ inline stat foreach_piece(const std::function<stat(isl::set, isl::aff)> &fn) const;
+ inline stat foreach_piece(const std::function<stat(isl::set, isl::multi_aff)> &fn) const;
+ inline stat foreach_pw_aff(const std::function<stat(isl::pw_aff)> &fn) const;
+ inline isl::set ge_set(isl::aff aff2) const;
+ inline isl::set ge_set(const isl::pw_aff &pwaff2) const;
+ inline isl::aff gist(isl::set context) const;
+ inline isl::union_pw_aff gist(const isl::union_set &context) const;
+ inline isl::aff gist(const isl::basic_set &context) const;
+ inline isl::aff gist(const isl::point &context) const;
+ inline isl::set gt_set(isl::aff aff2) const;
+ inline isl::set gt_set(const isl::pw_aff &pwaff2) const;
+ inline boolean has_range_tuple_id() const;
+ inline isl::multi_aff identity() const;
+ inline isl::pw_aff insert_domain(const isl::space &domain) const;
+ inline isl::pw_aff intersect_domain(const isl::set &set) const;
+ inline isl::union_pw_aff intersect_domain(const isl::space &space) const;
+ inline isl::union_pw_aff intersect_domain(const isl::union_set &uset) const;
+ inline isl::union_pw_aff intersect_domain_wrapped_domain(const isl::union_set &uset) const;
+ inline isl::union_pw_aff intersect_domain_wrapped_range(const isl::union_set &uset) const;
+ inline isl::pw_aff intersect_params(const isl::set &set) const;
+ inline boolean involves_locals() const;
+ inline boolean involves_nan() const;
+ inline boolean involves_param(const isl::id &id) const;
+ inline boolean involves_param(const std::string &id) const;
+ inline boolean involves_param(const isl::id_list &list) const;
+ inline boolean is_cst() const;
+ inline boolean is_equal(const isl::pw_aff &pa2) const;
+ inline boolean isa_aff() const;
+ inline boolean isa_multi_aff() const;
+ inline boolean isa_pw_multi_aff() const;
+ inline isl::set le_set(isl::aff aff2) const;
+ inline isl::set le_set(const isl::pw_aff &pwaff2) const;
+ inline isl::aff_list list() const;
+ inline isl::set lt_set(isl::aff aff2) const;
+ inline isl::set lt_set(const isl::pw_aff &pwaff2) const;
+ inline isl::multi_pw_aff max(const isl::multi_pw_aff &multi2) const;
+ inline isl::pw_aff max(const isl::pw_aff &pwaff2) const;
+ inline isl::multi_val max_multi_val() const;
+ inline isl::multi_pw_aff min(const isl::multi_pw_aff &multi2) const;
+ inline isl::pw_aff min(const isl::pw_aff &pwaff2) const;
+ inline isl::multi_val min_multi_val() const;
+ inline isl::aff mod(isl::val mod) const;
+ inline isl::aff mod(long mod) const;
+ inline isl::aff mul(isl::aff aff2) const;
+ inline isl::pw_aff mul(const isl::pw_aff &pwaff2) const;
+ inline class size n_piece() const;
+ inline isl::set ne_set(isl::aff aff2) const;
+ inline isl::set ne_set(const isl::pw_aff &pwaff2) const;
+ inline isl::aff neg() const;
+ inline boolean plain_is_empty() const;
+ inline boolean plain_is_equal(const isl::multi_aff &multi2) const;
+ inline boolean plain_is_equal(const isl::multi_pw_aff &multi2) const;
+ inline boolean plain_is_equal(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff preimage_domain_wrapped_domain(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff preimage_domain_wrapped_domain(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::multi_aff product(const isl::multi_aff &multi2) const;
+ inline isl::multi_pw_aff product(const isl::multi_pw_aff &multi2) const;
+ inline isl::pw_multi_aff product(const isl::pw_multi_aff &pma2) const;
+ inline isl::aff pullback(isl::multi_aff ma) const;
+ inline isl::pw_aff pullback(const isl::multi_pw_aff &mpa) const;
+ inline isl::pw_aff pullback(const isl::pw_multi_aff &pma) const;
+ inline isl::union_pw_aff pullback(const isl::union_pw_multi_aff &upma) const;
+ inline isl::aff pullback(const isl::aff &ma) const;
+ inline isl::pw_multi_aff_list pw_multi_aff_list() const;
+ inline isl::pw_multi_aff range_factor_domain() const;
+ inline isl::pw_multi_aff range_factor_range() const;
+ inline isl::multi_aff range_product(const isl::multi_aff &multi2) const;
+ inline isl::multi_pw_aff range_product(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff range_product(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff range_product(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::id range_tuple_id() const;
+ inline isl::multi_aff reset_range_tuple_id() const;
+ inline isl::multi_aff reset_tuple_id(isl::dim type) const;
+ inline isl::aff scale(isl::val v) const;
+ inline isl::aff scale(long v) const;
+ inline isl::multi_aff scale(const isl::multi_val &mv) const;
+ inline isl::aff scale_down(isl::val v) const;
+ inline isl::aff scale_down(long v) const;
+ inline isl::multi_aff scale_down(const isl::multi_val &mv) const;
+ inline isl::multi_aff set_aff(int pos, const isl::aff &el) const;
+ inline isl::multi_aff set_at(int pos, const isl::aff &el) const;
+ inline isl::multi_pw_aff set_at(int pos, const isl::pw_aff &el) const;
+ inline isl::multi_union_pw_aff set_at(int pos, const isl::union_pw_aff &el) const;
+ inline isl::aff set_constant_si(int v) const;
+ inline isl::multi_pw_aff set_pw_aff(int pos, const isl::pw_aff &el) const;
+ inline isl::pw_multi_aff set_pw_aff(unsigned int pos, const isl::pw_aff &pa) const;
+ inline isl::multi_aff set_range_tuple(const isl::id &id) const;
+ inline isl::multi_aff set_range_tuple(const std::string &id) const;
+ inline isl::pw_aff set_tuple_id(isl::dim type, const isl::id &id) const;
+ inline isl::pw_aff set_tuple_id(isl::dim type, const std::string &id) const;
+ inline isl::multi_union_pw_aff set_union_pw_aff(int pos, const isl::union_pw_aff &el) const;
+ inline class size size() const;
+ inline isl::space space() const;
+ inline isl::aff sub(isl::aff aff2) const;
+ inline isl::multi_aff sub(const isl::multi_aff &multi2) const;
+ inline isl::multi_pw_aff sub(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff sub(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_aff sub(const isl::pw_aff &pwaff2) const;
+ inline isl::pw_multi_aff sub(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_aff sub(const isl::union_pw_aff &upa2) const;
+ inline isl::union_pw_multi_aff sub(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_aff subtract_domain(const isl::set &set) const;
+ inline isl::union_pw_aff subtract_domain(const isl::space &space) const;
+ inline isl::union_pw_aff subtract_domain(const isl::union_set &uset) const;
+ inline isl::pw_aff tdiv_q(const isl::pw_aff &pa2) const;
+ inline isl::pw_aff tdiv_r(const isl::pw_aff &pa2) const;
+ inline isl::aff_list to_list() const;
+ inline isl::multi_pw_aff to_multi_pw_aff() const;
+ inline isl::multi_union_pw_aff to_multi_union_pw_aff() const;
+ inline isl::pw_multi_aff to_pw_multi_aff() const;
+ inline isl::union_pw_aff to_union_pw_aff() const;
+ inline isl::union_pw_multi_aff to_union_pw_multi_aff() const;
+ inline isl::id tuple_id(isl::dim type) const;
+ inline isl::aff unbind_params_insert_domain(isl::multi_id domain) const;
+ inline isl::multi_pw_aff union_add(const isl::multi_pw_aff &mpa2) const;
+ inline isl::multi_union_pw_aff union_add(const isl::multi_union_pw_aff &mupa2) const;
+ inline isl::pw_aff union_add(const isl::pw_aff &pwaff2) const;
+ inline isl::pw_multi_aff union_add(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_aff union_add(const isl::union_pw_aff &upa2) const;
+ inline isl::union_pw_multi_aff union_add(const isl::union_pw_multi_aff &upma2) const;
+ static inline isl::aff var_on_domain(isl::local_space ls, isl::dim type, unsigned int pos);
+ static inline isl::aff zero_on_domain(isl::space space);
+};
+
+// declarations for isl::aff_list
+inline aff_list manage(__isl_take isl_aff_list *ptr);
+inline aff_list manage_copy(__isl_keep isl_aff_list *ptr);
+
+class aff_list {
+ friend inline aff_list manage(__isl_take isl_aff_list *ptr);
+ friend inline aff_list manage_copy(__isl_keep isl_aff_list *ptr);
+
+protected:
+ isl_aff_list *ptr = nullptr;
+
+ inline explicit aff_list(__isl_take isl_aff_list *ptr);
+
+public:
+ inline /* implicit */ aff_list();
+ inline /* implicit */ aff_list(const aff_list &obj);
+ inline explicit aff_list(isl::ctx ctx, int n);
+ inline explicit aff_list(isl::aff el);
+ inline explicit aff_list(isl::ctx ctx, const std::string &str);
+ inline aff_list &operator=(aff_list obj);
+ inline ~aff_list();
+ inline __isl_give isl_aff_list *copy() const &;
+ inline __isl_give isl_aff_list *copy() && = delete;
+ inline __isl_keep isl_aff_list *get() const;
+ inline __isl_give isl_aff_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::aff_list add(isl::aff el) const;
+ inline isl::aff at(int index) const;
+ inline isl::aff get_at(int index) const;
+ inline isl::aff_list clear() const;
+ inline isl::aff_list concat(isl::aff_list list2) const;
+ inline isl::aff_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::aff)> &fn) const;
+ inline isl::aff_list insert(unsigned int pos, isl::aff el) const;
+ inline class size size() const;
+};
+
+// declarations for isl::ast_build
+inline ast_build manage(__isl_take isl_ast_build *ptr);
+inline ast_build manage_copy(__isl_keep isl_ast_build *ptr);
+
+class ast_build {
+ friend inline ast_build manage(__isl_take isl_ast_build *ptr);
+ friend inline ast_build manage_copy(__isl_keep isl_ast_build *ptr);
+
+protected:
+ isl_ast_build *ptr = nullptr;
+
+ inline explicit ast_build(__isl_take isl_ast_build *ptr);
+
+public:
+ inline /* implicit */ ast_build();
+ inline /* implicit */ ast_build(const ast_build &obj);
+ inline explicit ast_build(isl::ctx ctx);
+ inline ast_build &operator=(ast_build obj);
+ inline ~ast_build();
+ inline __isl_give isl_ast_build *copy() const &;
+ inline __isl_give isl_ast_build *copy() && = delete;
+ inline __isl_keep isl_ast_build *get() const;
+ inline __isl_give isl_ast_build *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+private:
+ inline ast_build &copy_callbacks(const ast_build &obj);
+ struct at_each_domain_data {
+ std::function<isl::ast_node(isl::ast_node, isl::ast_build)> func;
+ };
+ std::shared_ptr<at_each_domain_data> at_each_domain_data;
+ static inline isl_ast_node *at_each_domain(isl_ast_node *arg_0, isl_ast_build *arg_1, void *arg_2);
+ inline void set_at_each_domain_data(const std::function<isl::ast_node(isl::ast_node, isl::ast_build)> &fn);
+public:
+ inline isl::ast_build set_at_each_domain(const std::function<isl::ast_node(isl::ast_node, isl::ast_build)> &fn) const;
+ inline isl::ast_expr access_from(isl::multi_pw_aff mpa) const;
+ inline isl::ast_expr access_from(isl::pw_multi_aff pma) const;
+ inline isl::ast_expr call_from(isl::multi_pw_aff mpa) const;
+ inline isl::ast_expr call_from(isl::pw_multi_aff pma) const;
+ inline isl::ast_expr expr_from(isl::pw_aff pa) const;
+ inline isl::ast_expr expr_from(isl::set set) const;
+ static inline isl::ast_build from_context(isl::set set);
+ inline isl::ast_node node_from(isl::schedule schedule) const;
+ inline isl::ast_node node_from_schedule_map(isl::union_map schedule) const;
+ inline isl::ast_build restrict(isl::set set) const;
+ inline isl::union_map schedule() const;
+ inline isl::union_map get_schedule() const;
+};
+
+// declarations for isl::ast_expr
+inline ast_expr manage(__isl_take isl_ast_expr *ptr);
+inline ast_expr manage_copy(__isl_keep isl_ast_expr *ptr);
+
+class ast_expr {
+ friend inline ast_expr manage(__isl_take isl_ast_expr *ptr);
+ friend inline ast_expr manage_copy(__isl_keep isl_ast_expr *ptr);
+
+protected:
+ isl_ast_expr *ptr = nullptr;
+
+ inline explicit ast_expr(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr();
+ inline /* implicit */ ast_expr(const ast_expr &obj);
+ inline ast_expr &operator=(ast_expr obj);
+ inline ~ast_expr();
+ inline __isl_give isl_ast_expr *copy() const &;
+ inline __isl_give isl_ast_expr *copy() && = delete;
+ inline __isl_keep isl_ast_expr *get() const;
+ inline __isl_give isl_ast_expr *release();
+ inline bool is_null() const;
+private:
+ template <typename T,
+ typename = typename std::enable_if<std::is_same<
+ const decltype(isl_ast_expr_get_type(nullptr)),
+ const T>::value>::type>
+ inline boolean isa_type(T subtype) const;
+public:
+ template <class T> inline boolean isa() const;
+ template <class T> inline T as() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::ast_expr add(isl::ast_expr expr2) const;
+ inline isl::ast_expr address_of() const;
+ inline isl::ast_expr eq(isl::ast_expr expr2) const;
+ static inline isl::ast_expr from_val(isl::val v);
+ inline isl::id id() const;
+ inline isl::id get_id() const;
+ inline isl::ast_expr le(isl::ast_expr expr2) const;
+ inline isl::ast_expr mul(isl::ast_expr expr2) const;
+ inline isl::ast_expr op_arg(int pos) const;
+ inline isl::ast_expr get_op_arg(int pos) const;
+ inline std::string to_C_str() const;
+ inline isl::val val() const;
+ inline isl::val get_val() const;
+};
+
+// declarations for isl::ast_expr_id
+
+class ast_expr_id : public ast_expr {
+ template <class T>
+ friend boolean ast_expr::isa() const;
+ friend ast_expr_id ast_expr::as<ast_expr_id>() const;
+ static const auto type = isl_ast_expr_id;
+
+protected:
+ inline explicit ast_expr_id(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_id();
+ inline /* implicit */ ast_expr_id(const ast_expr_id &obj);
+ inline ast_expr_id &operator=(ast_expr_id obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::id id() const;
+ inline isl::id get_id() const;
+};
+
+// declarations for isl::ast_expr_int
+
+class ast_expr_int : public ast_expr {
+ template <class T>
+ friend boolean ast_expr::isa() const;
+ friend ast_expr_int ast_expr::as<ast_expr_int>() const;
+ static const auto type = isl_ast_expr_int;
+
+protected:
+ inline explicit ast_expr_int(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_int();
+ inline /* implicit */ ast_expr_int(const ast_expr_int &obj);
+ inline ast_expr_int &operator=(ast_expr_int obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::val val() const;
+ inline isl::val get_val() const;
+};
+
+// declarations for isl::ast_expr_op
+
+class ast_expr_op : public ast_expr {
+ template <class T>
+ friend boolean ast_expr::isa() const;
+ friend ast_expr_op ast_expr::as<ast_expr_op>() const;
+ static const auto type = isl_ast_expr_op;
+
+protected:
+ inline explicit ast_expr_op(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op();
+ inline /* implicit */ ast_expr_op(const ast_expr_op &obj);
+ inline ast_expr_op &operator=(ast_expr_op obj);
+private:
+ template <typename T,
+ typename = typename std::enable_if<std::is_same<
+ const decltype(isl_ast_expr_op_get_type(nullptr)),
+ const T>::value>::type>
+ inline boolean isa_type(T subtype) const;
+public:
+ template <class T> inline boolean isa() const;
+ template <class T> inline T as() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::ast_expr arg(int pos) const;
+ inline isl::ast_expr get_arg(int pos) const;
+ inline class size n_arg() const;
+ inline class size get_n_arg() const;
+};
+
+// declarations for isl::ast_expr_op_access
+
+class ast_expr_op_access : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_access ast_expr_op::as<ast_expr_op_access>() const;
+ static const auto type = isl_ast_expr_op_access;
+
+protected:
+ inline explicit ast_expr_op_access(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_access();
+ inline /* implicit */ ast_expr_op_access(const ast_expr_op_access &obj);
+ inline ast_expr_op_access &operator=(ast_expr_op_access obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_add
+
+class ast_expr_op_add : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_add ast_expr_op::as<ast_expr_op_add>() const;
+ static const auto type = isl_ast_expr_op_add;
+
+protected:
+ inline explicit ast_expr_op_add(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_add();
+ inline /* implicit */ ast_expr_op_add(const ast_expr_op_add &obj);
+ inline ast_expr_op_add &operator=(ast_expr_op_add obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_address_of
+
+class ast_expr_op_address_of : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_address_of ast_expr_op::as<ast_expr_op_address_of>() const;
+ static const auto type = isl_ast_expr_op_address_of;
+
+protected:
+ inline explicit ast_expr_op_address_of(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_address_of();
+ inline /* implicit */ ast_expr_op_address_of(const ast_expr_op_address_of &obj);
+ inline ast_expr_op_address_of &operator=(ast_expr_op_address_of obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_and
+
+class ast_expr_op_and : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_and ast_expr_op::as<ast_expr_op_and>() const;
+ static const auto type = isl_ast_expr_op_and;
+
+protected:
+ inline explicit ast_expr_op_and(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_and();
+ inline /* implicit */ ast_expr_op_and(const ast_expr_op_and &obj);
+ inline ast_expr_op_and &operator=(ast_expr_op_and obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_and_then
+
+class ast_expr_op_and_then : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_and_then ast_expr_op::as<ast_expr_op_and_then>() const;
+ static const auto type = isl_ast_expr_op_and_then;
+
+protected:
+ inline explicit ast_expr_op_and_then(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_and_then();
+ inline /* implicit */ ast_expr_op_and_then(const ast_expr_op_and_then &obj);
+ inline ast_expr_op_and_then &operator=(ast_expr_op_and_then obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_call
+
+class ast_expr_op_call : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_call ast_expr_op::as<ast_expr_op_call>() const;
+ static const auto type = isl_ast_expr_op_call;
+
+protected:
+ inline explicit ast_expr_op_call(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_call();
+ inline /* implicit */ ast_expr_op_call(const ast_expr_op_call &obj);
+ inline ast_expr_op_call &operator=(ast_expr_op_call obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_cond
+
+class ast_expr_op_cond : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_cond ast_expr_op::as<ast_expr_op_cond>() const;
+ static const auto type = isl_ast_expr_op_cond;
+
+protected:
+ inline explicit ast_expr_op_cond(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_cond();
+ inline /* implicit */ ast_expr_op_cond(const ast_expr_op_cond &obj);
+ inline ast_expr_op_cond &operator=(ast_expr_op_cond obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_div
+
+class ast_expr_op_div : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_div ast_expr_op::as<ast_expr_op_div>() const;
+ static const auto type = isl_ast_expr_op_div;
+
+protected:
+ inline explicit ast_expr_op_div(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_div();
+ inline /* implicit */ ast_expr_op_div(const ast_expr_op_div &obj);
+ inline ast_expr_op_div &operator=(ast_expr_op_div obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_eq
+
+class ast_expr_op_eq : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_eq ast_expr_op::as<ast_expr_op_eq>() const;
+ static const auto type = isl_ast_expr_op_eq;
+
+protected:
+ inline explicit ast_expr_op_eq(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_eq();
+ inline /* implicit */ ast_expr_op_eq(const ast_expr_op_eq &obj);
+ inline ast_expr_op_eq &operator=(ast_expr_op_eq obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_fdiv_q
+
+class ast_expr_op_fdiv_q : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_fdiv_q ast_expr_op::as<ast_expr_op_fdiv_q>() const;
+ static const auto type = isl_ast_expr_op_fdiv_q;
+
+protected:
+ inline explicit ast_expr_op_fdiv_q(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_fdiv_q();
+ inline /* implicit */ ast_expr_op_fdiv_q(const ast_expr_op_fdiv_q &obj);
+ inline ast_expr_op_fdiv_q &operator=(ast_expr_op_fdiv_q obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_ge
+
+class ast_expr_op_ge : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_ge ast_expr_op::as<ast_expr_op_ge>() const;
+ static const auto type = isl_ast_expr_op_ge;
+
+protected:
+ inline explicit ast_expr_op_ge(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_ge();
+ inline /* implicit */ ast_expr_op_ge(const ast_expr_op_ge &obj);
+ inline ast_expr_op_ge &operator=(ast_expr_op_ge obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_gt
+
+class ast_expr_op_gt : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_gt ast_expr_op::as<ast_expr_op_gt>() const;
+ static const auto type = isl_ast_expr_op_gt;
+
+protected:
+ inline explicit ast_expr_op_gt(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_gt();
+ inline /* implicit */ ast_expr_op_gt(const ast_expr_op_gt &obj);
+ inline ast_expr_op_gt &operator=(ast_expr_op_gt obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_le
+
+class ast_expr_op_le : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_le ast_expr_op::as<ast_expr_op_le>() const;
+ static const auto type = isl_ast_expr_op_le;
+
+protected:
+ inline explicit ast_expr_op_le(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_le();
+ inline /* implicit */ ast_expr_op_le(const ast_expr_op_le &obj);
+ inline ast_expr_op_le &operator=(ast_expr_op_le obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_lt
+
+class ast_expr_op_lt : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_lt ast_expr_op::as<ast_expr_op_lt>() const;
+ static const auto type = isl_ast_expr_op_lt;
+
+protected:
+ inline explicit ast_expr_op_lt(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_lt();
+ inline /* implicit */ ast_expr_op_lt(const ast_expr_op_lt &obj);
+ inline ast_expr_op_lt &operator=(ast_expr_op_lt obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_max
+
+class ast_expr_op_max : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_max ast_expr_op::as<ast_expr_op_max>() const;
+ static const auto type = isl_ast_expr_op_max;
+
+protected:
+ inline explicit ast_expr_op_max(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_max();
+ inline /* implicit */ ast_expr_op_max(const ast_expr_op_max &obj);
+ inline ast_expr_op_max &operator=(ast_expr_op_max obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_member
+
+class ast_expr_op_member : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_member ast_expr_op::as<ast_expr_op_member>() const;
+ static const auto type = isl_ast_expr_op_member;
+
+protected:
+ inline explicit ast_expr_op_member(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_member();
+ inline /* implicit */ ast_expr_op_member(const ast_expr_op_member &obj);
+ inline ast_expr_op_member &operator=(ast_expr_op_member obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_min
+
+class ast_expr_op_min : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_min ast_expr_op::as<ast_expr_op_min>() const;
+ static const auto type = isl_ast_expr_op_min;
+
+protected:
+ inline explicit ast_expr_op_min(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_min();
+ inline /* implicit */ ast_expr_op_min(const ast_expr_op_min &obj);
+ inline ast_expr_op_min &operator=(ast_expr_op_min obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_minus
+
+class ast_expr_op_minus : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_minus ast_expr_op::as<ast_expr_op_minus>() const;
+ static const auto type = isl_ast_expr_op_minus;
+
+protected:
+ inline explicit ast_expr_op_minus(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_minus();
+ inline /* implicit */ ast_expr_op_minus(const ast_expr_op_minus &obj);
+ inline ast_expr_op_minus &operator=(ast_expr_op_minus obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_mul
+
+class ast_expr_op_mul : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_mul ast_expr_op::as<ast_expr_op_mul>() const;
+ static const auto type = isl_ast_expr_op_mul;
+
+protected:
+ inline explicit ast_expr_op_mul(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_mul();
+ inline /* implicit */ ast_expr_op_mul(const ast_expr_op_mul &obj);
+ inline ast_expr_op_mul &operator=(ast_expr_op_mul obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_or
+
+class ast_expr_op_or : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_or ast_expr_op::as<ast_expr_op_or>() const;
+ static const auto type = isl_ast_expr_op_or;
+
+protected:
+ inline explicit ast_expr_op_or(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_or();
+ inline /* implicit */ ast_expr_op_or(const ast_expr_op_or &obj);
+ inline ast_expr_op_or &operator=(ast_expr_op_or obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_or_else
+
+class ast_expr_op_or_else : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_or_else ast_expr_op::as<ast_expr_op_or_else>() const;
+ static const auto type = isl_ast_expr_op_or_else;
+
+protected:
+ inline explicit ast_expr_op_or_else(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_or_else();
+ inline /* implicit */ ast_expr_op_or_else(const ast_expr_op_or_else &obj);
+ inline ast_expr_op_or_else &operator=(ast_expr_op_or_else obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_pdiv_q
+
+class ast_expr_op_pdiv_q : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_pdiv_q ast_expr_op::as<ast_expr_op_pdiv_q>() const;
+ static const auto type = isl_ast_expr_op_pdiv_q;
+
+protected:
+ inline explicit ast_expr_op_pdiv_q(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_pdiv_q();
+ inline /* implicit */ ast_expr_op_pdiv_q(const ast_expr_op_pdiv_q &obj);
+ inline ast_expr_op_pdiv_q &operator=(ast_expr_op_pdiv_q obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_pdiv_r
+
+class ast_expr_op_pdiv_r : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_pdiv_r ast_expr_op::as<ast_expr_op_pdiv_r>() const;
+ static const auto type = isl_ast_expr_op_pdiv_r;
+
+protected:
+ inline explicit ast_expr_op_pdiv_r(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_pdiv_r();
+ inline /* implicit */ ast_expr_op_pdiv_r(const ast_expr_op_pdiv_r &obj);
+ inline ast_expr_op_pdiv_r &operator=(ast_expr_op_pdiv_r obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_select
+
+class ast_expr_op_select : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_select ast_expr_op::as<ast_expr_op_select>() const;
+ static const auto type = isl_ast_expr_op_select;
+
+protected:
+ inline explicit ast_expr_op_select(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_select();
+ inline /* implicit */ ast_expr_op_select(const ast_expr_op_select &obj);
+ inline ast_expr_op_select &operator=(ast_expr_op_select obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_sub
+
+class ast_expr_op_sub : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_sub ast_expr_op::as<ast_expr_op_sub>() const;
+ static const auto type = isl_ast_expr_op_sub;
+
+protected:
+ inline explicit ast_expr_op_sub(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_sub();
+ inline /* implicit */ ast_expr_op_sub(const ast_expr_op_sub &obj);
+ inline ast_expr_op_sub &operator=(ast_expr_op_sub obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_expr_op_zdiv_r
+
+class ast_expr_op_zdiv_r : public ast_expr_op {
+ template <class T>
+ friend boolean ast_expr_op::isa() const;
+ friend ast_expr_op_zdiv_r ast_expr_op::as<ast_expr_op_zdiv_r>() const;
+ static const auto type = isl_ast_expr_op_zdiv_r;
+
+protected:
+ inline explicit ast_expr_op_zdiv_r(__isl_take isl_ast_expr *ptr);
+
+public:
+ inline /* implicit */ ast_expr_op_zdiv_r();
+ inline /* implicit */ ast_expr_op_zdiv_r(const ast_expr_op_zdiv_r &obj);
+ inline ast_expr_op_zdiv_r &operator=(ast_expr_op_zdiv_r obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::ast_node
+inline ast_node manage(__isl_take isl_ast_node *ptr);
+inline ast_node manage_copy(__isl_keep isl_ast_node *ptr);
+
+class ast_node {
+ friend inline ast_node manage(__isl_take isl_ast_node *ptr);
+ friend inline ast_node manage_copy(__isl_keep isl_ast_node *ptr);
+
+protected:
+ isl_ast_node *ptr = nullptr;
+
+ inline explicit ast_node(__isl_take isl_ast_node *ptr);
+
+public:
+ inline /* implicit */ ast_node();
+ inline /* implicit */ ast_node(const ast_node &obj);
+ inline ast_node &operator=(ast_node obj);
+ inline ~ast_node();
+ inline __isl_give isl_ast_node *copy() const &;
+ inline __isl_give isl_ast_node *copy() && = delete;
+ inline __isl_keep isl_ast_node *get() const;
+ inline __isl_give isl_ast_node *release();
+ inline bool is_null() const;
+private:
+ template <typename T,
+ typename = typename std::enable_if<std::is_same<
+ const decltype(isl_ast_node_get_type(nullptr)),
+ const T>::value>::type>
+ inline boolean isa_type(T subtype) const;
+public:
+ template <class T> inline boolean isa() const;
+ template <class T> inline T as() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::id annotation() const;
+ inline isl::id get_annotation() const;
+ inline std::string to_C_str() const;
+ inline isl::ast_node_list to_list() const;
+};
+
+// declarations for isl::ast_node_block
+
+class ast_node_block : public ast_node {
+ template <class T>
+ friend boolean ast_node::isa() const;
+ friend ast_node_block ast_node::as<ast_node_block>() const;
+ static const auto type = isl_ast_node_block;
+
+protected:
+ inline explicit ast_node_block(__isl_take isl_ast_node *ptr);
+
+public:
+ inline /* implicit */ ast_node_block();
+ inline /* implicit */ ast_node_block(const ast_node_block &obj);
+ inline ast_node_block &operator=(ast_node_block obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::ast_node_list children() const;
+ inline isl::ast_node_list get_children() const;
+};
+
+// declarations for isl::ast_node_for
+
+class ast_node_for : public ast_node {
+ template <class T>
+ friend boolean ast_node::isa() const;
+ friend ast_node_for ast_node::as<ast_node_for>() const;
+ static const auto type = isl_ast_node_for;
+
+protected:
+ inline explicit ast_node_for(__isl_take isl_ast_node *ptr);
+
+public:
+ inline /* implicit */ ast_node_for();
+ inline /* implicit */ ast_node_for(const ast_node_for &obj);
+ inline ast_node_for &operator=(ast_node_for obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::ast_node body() const;
+ inline isl::ast_node get_body() const;
+ inline isl::ast_expr cond() const;
+ inline isl::ast_expr get_cond() const;
+ inline isl::ast_expr inc() const;
+ inline isl::ast_expr get_inc() const;
+ inline isl::ast_expr init() const;
+ inline isl::ast_expr get_init() const;
+ inline boolean is_degenerate() const;
+ inline isl::ast_expr iterator() const;
+ inline isl::ast_expr get_iterator() const;
+};
+
+// declarations for isl::ast_node_if
+
+class ast_node_if : public ast_node {
+ template <class T>
+ friend boolean ast_node::isa() const;
+ friend ast_node_if ast_node::as<ast_node_if>() const;
+ static const auto type = isl_ast_node_if;
+
+protected:
+ inline explicit ast_node_if(__isl_take isl_ast_node *ptr);
+
+public:
+ inline /* implicit */ ast_node_if();
+ inline /* implicit */ ast_node_if(const ast_node_if &obj);
+ inline ast_node_if &operator=(ast_node_if obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::ast_expr cond() const;
+ inline isl::ast_expr get_cond() const;
+ inline isl::ast_node else_node() const;
+ inline isl::ast_node get_else_node() const;
+ inline boolean has_else_node() const;
+ inline isl::ast_node then_node() const;
+ inline isl::ast_node get_then_node() const;
+};
+
+// declarations for isl::ast_node_list
+inline ast_node_list manage(__isl_take isl_ast_node_list *ptr);
+inline ast_node_list manage_copy(__isl_keep isl_ast_node_list *ptr);
+
+class ast_node_list {
+ friend inline ast_node_list manage(__isl_take isl_ast_node_list *ptr);
+ friend inline ast_node_list manage_copy(__isl_keep isl_ast_node_list *ptr);
+
+protected:
+ isl_ast_node_list *ptr = nullptr;
+
+ inline explicit ast_node_list(__isl_take isl_ast_node_list *ptr);
+
+public:
+ inline /* implicit */ ast_node_list();
+ inline /* implicit */ ast_node_list(const ast_node_list &obj);
+ inline explicit ast_node_list(isl::ctx ctx, int n);
+ inline explicit ast_node_list(isl::ast_node el);
+ inline ast_node_list &operator=(ast_node_list obj);
+ inline ~ast_node_list();
+ inline __isl_give isl_ast_node_list *copy() const &;
+ inline __isl_give isl_ast_node_list *copy() && = delete;
+ inline __isl_keep isl_ast_node_list *get() const;
+ inline __isl_give isl_ast_node_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::ast_node_list add(isl::ast_node el) const;
+ inline isl::ast_node at(int index) const;
+ inline isl::ast_node get_at(int index) const;
+ inline isl::ast_node_list clear() const;
+ inline isl::ast_node_list concat(isl::ast_node_list list2) const;
+ inline isl::ast_node_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::ast_node)> &fn) const;
+ inline isl::ast_node_list insert(unsigned int pos, isl::ast_node el) const;
+ inline class size size() const;
+};
+
+// declarations for isl::ast_node_mark
+
+class ast_node_mark : public ast_node {
+ template <class T>
+ friend boolean ast_node::isa() const;
+ friend ast_node_mark ast_node::as<ast_node_mark>() const;
+ static const auto type = isl_ast_node_mark;
+
+protected:
+ inline explicit ast_node_mark(__isl_take isl_ast_node *ptr);
+
+public:
+ inline /* implicit */ ast_node_mark();
+ inline /* implicit */ ast_node_mark(const ast_node_mark &obj);
+ inline ast_node_mark &operator=(ast_node_mark obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::id id() const;
+ inline isl::id get_id() const;
+ inline isl::ast_node node() const;
+ inline isl::ast_node get_node() const;
+};
+
+// declarations for isl::ast_node_user
+
+class ast_node_user : public ast_node {
+ template <class T>
+ friend boolean ast_node::isa() const;
+ friend ast_node_user ast_node::as<ast_node_user>() const;
+ static const auto type = isl_ast_node_user;
+
+protected:
+ inline explicit ast_node_user(__isl_take isl_ast_node *ptr);
+
+public:
+ inline /* implicit */ ast_node_user();
+ inline /* implicit */ ast_node_user(const ast_node_user &obj);
+ inline ast_node_user &operator=(ast_node_user obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::ast_expr expr() const;
+ inline isl::ast_expr get_expr() const;
+};
+
+// declarations for isl::basic_map
+inline basic_map manage(__isl_take isl_basic_map *ptr);
+inline basic_map manage_copy(__isl_keep isl_basic_map *ptr);
+
+class basic_map {
+ friend inline basic_map manage(__isl_take isl_basic_map *ptr);
+ friend inline basic_map manage_copy(__isl_keep isl_basic_map *ptr);
+
+protected:
+ isl_basic_map *ptr = nullptr;
+
+ inline explicit basic_map(__isl_take isl_basic_map *ptr);
+
+public:
+ inline /* implicit */ basic_map();
+ inline /* implicit */ basic_map(const basic_map &obj);
+ inline explicit basic_map(isl::ctx ctx, const std::string &str);
+ inline basic_map &operator=(basic_map obj);
+ inline ~basic_map();
+ inline __isl_give isl_basic_map *copy() const &;
+ inline __isl_give isl_basic_map *copy() && = delete;
+ inline __isl_keep isl_basic_map *get() const;
+ inline __isl_give isl_basic_map *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::map add_constraint(const isl::constraint &constraint) const;
+ inline isl::map add_dims(isl::dim type, unsigned int n) const;
+ inline isl::basic_map affine_hull() const;
+ inline isl::map align_params(const isl::space &model) const;
+ inline isl::basic_map apply_domain(isl::basic_map bmap2) const;
+ inline isl::map apply_domain(const isl::map &map2) const;
+ inline isl::union_map apply_domain(const isl::union_map &umap2) const;
+ inline isl::basic_map apply_range(isl::basic_map bmap2) const;
+ inline isl::map apply_range(const isl::map &map2) const;
+ inline isl::union_map apply_range(const isl::union_map &umap2) const;
+ inline isl::map as_map() const;
+ inline isl::multi_union_pw_aff as_multi_union_pw_aff() const;
+ inline isl::pw_multi_aff as_pw_multi_aff() const;
+ inline isl::union_pw_multi_aff as_union_pw_multi_aff() const;
+ inline isl::basic_map_list basic_map_list() const;
+ inline isl::set bind_domain(const isl::multi_id &tuple) const;
+ inline isl::set bind_range(const isl::multi_id &tuple) const;
+ inline boolean can_curry() const;
+ inline isl::map coalesce() const;
+ inline isl::map complement() const;
+ inline isl::union_map compute_divs() const;
+ inline isl::map curry() const;
+ inline isl::basic_set deltas() const;
+ inline isl::basic_map detect_equalities() const;
+ inline class size dim(isl::dim type) const;
+ inline isl::pw_aff dim_max(int pos) const;
+ inline isl::pw_aff dim_min(int pos) const;
+ inline isl::basic_set domain() const;
+ inline isl::map domain_factor_domain() const;
+ inline isl::map domain_factor_range() const;
+ inline isl::map domain_map() const;
+ inline isl::union_pw_multi_aff domain_map_union_pw_multi_aff() const;
+ inline isl::map domain_product(const isl::map &map2) const;
+ inline isl::union_map domain_product(const isl::union_map &umap2) const;
+ inline class size domain_tuple_dim() const;
+ inline isl::id domain_tuple_id() const;
+ inline isl::map eq_at(const isl::multi_pw_aff &mpa) const;
+ inline isl::union_map eq_at(const isl::multi_union_pw_aff &mupa) const;
+ static inline isl::basic_map equal(isl::space space, unsigned int n_equal);
+ inline isl::basic_map equate(isl::dim type1, int pos1, isl::dim type2, int pos2) const;
+ inline boolean every_map(const std::function<boolean(isl::map)> &test) const;
+ inline isl::map extract_map(const isl::space &space) const;
+ inline isl::map factor_domain() const;
+ inline isl::map factor_range() const;
+ inline isl::basic_map fix_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::basic_map fix_val(isl::dim type, unsigned int pos, isl::val v) const;
+ inline isl::basic_map fix_val(isl::dim type, unsigned int pos, long v) const;
+ inline isl::union_map fixed_power(const isl::val &exp) const;
+ inline isl::union_map fixed_power(long exp) const;
+ inline isl::map flat_range_product(const isl::map &map2) const;
+ inline isl::union_map flat_range_product(const isl::union_map &umap2) const;
+ inline isl::basic_map flatten() const;
+ inline isl::basic_map flatten_domain() const;
+ inline isl::basic_map flatten_range() const;
+ inline isl::map floordiv_val(const isl::val &d) const;
+ inline isl::map floordiv_val(long d) const;
+ inline stat foreach_basic_map(const std::function<stat(isl::basic_map)> &fn) const;
+ inline stat foreach_map(const std::function<stat(isl::map)> &fn) const;
+ static inline isl::basic_map from_aff(isl::aff aff);
+ static inline isl::basic_map from_domain_and_range(isl::basic_set domain, isl::basic_set range);
+ inline isl::basic_map gist(isl::basic_map context) const;
+ inline isl::map gist(const isl::map &context) const;
+ inline isl::union_map gist(const isl::union_map &context) const;
+ inline isl::map gist_domain(const isl::set &context) const;
+ inline isl::union_map gist_domain(const isl::union_set &uset) const;
+ inline isl::map gist_params(const isl::set &context) const;
+ inline isl::union_map gist_range(const isl::union_set &uset) const;
+ inline boolean has_domain_tuple_id() const;
+ inline boolean has_equal_space(const isl::map &map2) const;
+ inline boolean has_range_tuple_id() const;
+ inline boolean has_tuple_id(isl::dim type) const;
+ inline boolean has_tuple_name(isl::dim type) const;
+ inline isl::basic_map intersect(isl::basic_map bmap2) const;
+ inline isl::map intersect(const isl::map &map2) const;
+ inline isl::union_map intersect(const isl::union_map &umap2) const;
+ inline isl::basic_map intersect_domain(isl::basic_set bset) const;
+ inline isl::map intersect_domain(const isl::set &set) const;
+ inline isl::union_map intersect_domain(const isl::space &space) const;
+ inline isl::union_map intersect_domain(const isl::union_set &uset) const;
+ inline isl::basic_map intersect_domain(const isl::point &bset) const;
+ inline isl::map intersect_domain_factor_domain(const isl::map &factor) const;
+ inline isl::union_map intersect_domain_factor_domain(const isl::union_map &factor) const;
+ inline isl::map intersect_domain_factor_range(const isl::map &factor) const;
+ inline isl::union_map intersect_domain_factor_range(const isl::union_map &factor) const;
+ inline isl::map intersect_params(const isl::set &params) const;
+ inline isl::basic_map intersect_range(isl::basic_set bset) const;
+ inline isl::map intersect_range(const isl::set &set) const;
+ inline isl::union_map intersect_range(const isl::space &space) const;
+ inline isl::union_map intersect_range(const isl::union_set &uset) const;
+ inline isl::basic_map intersect_range(const isl::point &bset) const;
+ inline isl::map intersect_range_factor_domain(const isl::map &factor) const;
+ inline isl::union_map intersect_range_factor_domain(const isl::union_map &factor) const;
+ inline isl::map intersect_range_factor_range(const isl::map &factor) const;
+ inline isl::union_map intersect_range_factor_range(const isl::union_map &factor) const;
+ inline boolean involves_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline boolean is_bijective() const;
+ inline boolean is_disjoint(const isl::map &map2) const;
+ inline boolean is_disjoint(const isl::union_map &umap2) const;
+ inline boolean is_empty() const;
+ inline boolean is_equal(const isl::basic_map &bmap2) const;
+ inline boolean is_equal(const isl::map &map2) const;
+ inline boolean is_equal(const isl::union_map &umap2) const;
+ inline boolean is_injective() const;
+ inline boolean is_single_valued() const;
+ inline boolean is_strict_subset(const isl::map &map2) const;
+ inline boolean is_strict_subset(const isl::union_map &umap2) const;
+ inline boolean is_subset(const isl::basic_map &bmap2) const;
+ inline boolean is_subset(const isl::map &map2) const;
+ inline boolean is_subset(const isl::union_map &umap2) const;
+ inline boolean isa_map() const;
+ inline isl::map lex_ge_at(const isl::multi_pw_aff &mpa) const;
+ inline isl::map lex_gt_at(const isl::multi_pw_aff &mpa) const;
+ inline isl::map lex_le_at(const isl::multi_pw_aff &mpa) const;
+ inline isl::map lex_lt_at(const isl::multi_pw_aff &mpa) const;
+ inline isl::map lexmax() const;
+ inline isl::pw_multi_aff lexmax_pw_multi_aff() const;
+ inline isl::map lexmin() const;
+ inline isl::pw_multi_aff lexmin_pw_multi_aff() const;
+ inline isl::map lower_bound(const isl::multi_pw_aff &lower) const;
+ inline isl::map lower_bound_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::map_list map_list() const;
+ inline isl::multi_pw_aff max_multi_pw_aff() const;
+ inline isl::multi_pw_aff min_multi_pw_aff() const;
+ inline isl::map move_dims(isl::dim dst_type, unsigned int dst_pos, isl::dim src_type, unsigned int src_pos, unsigned int n) const;
+ inline class size n_basic_map() const;
+ inline isl::map order_lt(isl::dim type1, int pos1, isl::dim type2, int pos2) const;
+ inline isl::set params() const;
+ inline isl::val plain_get_val_if_fixed(isl::dim type, unsigned int pos) const;
+ inline isl::basic_map polyhedral_hull() const;
+ inline isl::map preimage_domain(const isl::multi_aff &ma) const;
+ inline isl::map preimage_domain(const isl::multi_pw_aff &mpa) const;
+ inline isl::map preimage_domain(const isl::pw_multi_aff &pma) const;
+ inline isl::union_map preimage_domain(const isl::union_pw_multi_aff &upma) const;
+ inline isl::map preimage_range(const isl::multi_aff &ma) const;
+ inline isl::map preimage_range(const isl::pw_multi_aff &pma) const;
+ inline isl::union_map preimage_range(const isl::union_pw_multi_aff &upma) const;
+ inline isl::map product(const isl::map &map2) const;
+ inline isl::union_map product(const isl::union_map &umap2) const;
+ inline isl::map project_out(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::map project_out_all_params() const;
+ inline isl::set range() const;
+ inline isl::map range_factor_domain() const;
+ inline isl::map range_factor_range() const;
+ inline isl::fixed_box range_lattice_tile() const;
+ inline isl::map range_map() const;
+ inline isl::map range_product(const isl::map &map2) const;
+ inline isl::union_map range_product(const isl::union_map &umap2) const;
+ inline isl::map range_reverse() const;
+ inline isl::fixed_box range_simple_fixed_box_hull() const;
+ inline class size range_tuple_dim() const;
+ inline isl::id range_tuple_id() const;
+ inline isl::basic_map reverse() const;
+ inline isl::basic_map sample() const;
+ inline isl::map set_domain_tuple(const isl::id &id) const;
+ inline isl::map set_domain_tuple(const std::string &id) const;
+ inline isl::map set_range_tuple(const isl::id &id) const;
+ inline isl::map set_range_tuple(const std::string &id) const;
+ inline isl::map set_tuple_id(isl::dim type, const isl::id &id) const;
+ inline isl::map set_tuple_id(isl::dim type, const std::string &id) const;
+ inline isl::space space() const;
+ inline isl::map subtract(const isl::map &map2) const;
+ inline isl::union_map subtract(const isl::union_map &umap2) const;
+ inline isl::union_map subtract_domain(const isl::union_set &dom) const;
+ inline isl::union_map subtract_range(const isl::union_set &dom) const;
+ inline isl::map sum(const isl::map &map2) const;
+ inline isl::basic_map_list to_list() const;
+ inline isl::union_map to_union_map() const;
+ inline isl::id tuple_id(isl::dim type) const;
+ inline isl::map uncurry() const;
+ inline isl::map unite(isl::basic_map bmap2) const;
+ inline isl::map unite(const isl::map &map2) const;
+ inline isl::union_map unite(const isl::union_map &umap2) const;
+ static inline isl::basic_map universe(isl::space space);
+ inline isl::basic_map unshifted_simple_hull() const;
+ inline isl::map upper_bound(const isl::multi_pw_aff &upper) const;
+ inline isl::map upper_bound_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::set wrap() const;
+ inline isl::map zip() const;
+};
+
+// declarations for isl::basic_map_list
+inline basic_map_list manage(__isl_take isl_basic_map_list *ptr);
+inline basic_map_list manage_copy(__isl_keep isl_basic_map_list *ptr);
+
+class basic_map_list {
+ friend inline basic_map_list manage(__isl_take isl_basic_map_list *ptr);
+ friend inline basic_map_list manage_copy(__isl_keep isl_basic_map_list *ptr);
+
+protected:
+ isl_basic_map_list *ptr = nullptr;
+
+ inline explicit basic_map_list(__isl_take isl_basic_map_list *ptr);
+
+public:
+ inline /* implicit */ basic_map_list();
+ inline /* implicit */ basic_map_list(const basic_map_list &obj);
+ inline explicit basic_map_list(isl::ctx ctx, int n);
+ inline explicit basic_map_list(isl::basic_map el);
+ inline basic_map_list &operator=(basic_map_list obj);
+ inline ~basic_map_list();
+ inline __isl_give isl_basic_map_list *copy() const &;
+ inline __isl_give isl_basic_map_list *copy() && = delete;
+ inline __isl_keep isl_basic_map_list *get() const;
+ inline __isl_give isl_basic_map_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::basic_map_list add(isl::basic_map el) const;
+ inline isl::basic_map at(int index) const;
+ inline isl::basic_map get_at(int index) const;
+ inline isl::basic_map_list clear() const;
+ inline isl::basic_map_list concat(isl::basic_map_list list2) const;
+ inline isl::basic_map_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::basic_map)> &fn) const;
+ inline isl::basic_map_list insert(unsigned int pos, isl::basic_map el) const;
+ inline class size size() const;
+};
+
+// declarations for isl::basic_set
+inline basic_set manage(__isl_take isl_basic_set *ptr);
+inline basic_set manage_copy(__isl_keep isl_basic_set *ptr);
+
+class basic_set {
+ friend inline basic_set manage(__isl_take isl_basic_set *ptr);
+ friend inline basic_set manage_copy(__isl_keep isl_basic_set *ptr);
+
+protected:
+ isl_basic_set *ptr = nullptr;
+
+ inline explicit basic_set(__isl_take isl_basic_set *ptr);
+
+public:
+ inline /* implicit */ basic_set();
+ inline /* implicit */ basic_set(const basic_set &obj);
+ inline /* implicit */ basic_set(isl::point pnt);
+ inline explicit basic_set(isl::ctx ctx, const std::string &str);
+ inline basic_set &operator=(basic_set obj);
+ inline ~basic_set();
+ inline __isl_give isl_basic_set *copy() const &;
+ inline __isl_give isl_basic_set *copy() && = delete;
+ inline __isl_keep isl_basic_set *get() const;
+ inline __isl_give isl_basic_set *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::set add_constraint(const isl::constraint &constraint) const;
+ inline isl::set add_dims(isl::dim type, unsigned int n) const;
+ inline isl::basic_set affine_hull() const;
+ inline isl::set align_params(const isl::space &model) const;
+ inline isl::basic_set apply(isl::basic_map bmap) const;
+ inline isl::set apply(const isl::map &map) const;
+ inline isl::union_set apply(const isl::union_map &umap) const;
+ inline isl::pw_multi_aff as_pw_multi_aff() const;
+ inline isl::set as_set() const;
+ inline isl::basic_set_list basic_set_list() const;
+ inline isl::set bind(const isl::multi_id &tuple) const;
+ inline isl::set coalesce() const;
+ inline isl::set complement() const;
+ inline isl::union_set compute_divs() const;
+ inline boolean contains(const isl::space &space) const;
+ inline isl::basic_set convex_hull() const;
+ inline isl::basic_set detect_equalities() const;
+ inline class size dim(isl::dim type) const;
+ inline boolean dim_has_any_lower_bound(isl::dim type, unsigned int pos) const;
+ inline isl::id dim_id(isl::dim type, unsigned int pos) const;
+ inline isl::pw_aff dim_max(int pos) const;
+ inline isl::val dim_max_val(int pos) const;
+ inline isl::pw_aff dim_min(int pos) const;
+ inline isl::val dim_min_val(int pos) const;
+ inline std::string dim_name(isl::dim type, unsigned int pos) const;
+ inline isl::aff div(int pos) const;
+ inline isl::aff get_div(int pos) const;
+ inline isl::set drop_constraints_involving_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::set eliminate(isl::dim type, unsigned int first, unsigned int n) const;
+ inline boolean every_set(const std::function<boolean(isl::set)> &test) const;
+ inline isl::set extract_set(const isl::space &space) const;
+ inline int find_dim_by_id(isl::dim type, const isl::id &id) const;
+ inline int find_dim_by_id(isl::dim type, const std::string &id) const;
+ inline isl::basic_set fix_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::basic_set fix_val(isl::dim type, unsigned int pos, isl::val v) const;
+ inline isl::basic_set fix_val(isl::dim type, unsigned int pos, long v) const;
+ inline isl::basic_set flatten() const;
+ inline stat foreach_basic_set(const std::function<stat(isl::basic_set)> &fn) const;
+ inline stat foreach_point(const std::function<stat(isl::point)> &fn) const;
+ inline stat foreach_set(const std::function<stat(isl::set)> &fn) const;
+ inline isl::basic_set gist(isl::basic_set context) const;
+ inline isl::set gist(const isl::set &context) const;
+ inline isl::union_set gist(const isl::union_set &context) const;
+ inline isl::basic_set gist(const isl::point &context) const;
+ inline isl::set gist_params(const isl::set &context) const;
+ inline boolean has_equal_space(const isl::set &set2) const;
+ inline isl::map identity() const;
+ inline isl::union_pw_multi_aff identity_union_pw_multi_aff() const;
+ inline isl::pw_aff indicator_function() const;
+ inline isl::set insert_dims(isl::dim type, unsigned int pos, unsigned int n) const;
+ inline isl::map insert_domain(const isl::space &domain) const;
+ inline isl::basic_set intersect(isl::basic_set bset2) const;
+ inline isl::set intersect(const isl::set &set2) const;
+ inline isl::union_set intersect(const isl::union_set &uset2) const;
+ inline isl::basic_set intersect(const isl::point &bset2) const;
+ inline isl::basic_set intersect_params(isl::basic_set bset2) const;
+ inline isl::set intersect_params(const isl::set &params) const;
+ inline isl::basic_set intersect_params(const isl::point &bset2) const;
+ inline boolean involves_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline boolean involves_locals() const;
+ inline boolean is_bounded() const;
+ inline boolean is_disjoint(const isl::set &set2) const;
+ inline boolean is_disjoint(const isl::union_set &uset2) const;
+ inline boolean is_empty() const;
+ inline boolean is_equal(const isl::basic_set &bset2) const;
+ inline boolean is_equal(const isl::set &set2) const;
+ inline boolean is_equal(const isl::union_set &uset2) const;
+ inline boolean is_equal(const isl::point &bset2) const;
+ inline boolean is_params() const;
+ inline boolean is_singleton() const;
+ inline boolean is_strict_subset(const isl::set &set2) const;
+ inline boolean is_strict_subset(const isl::union_set &uset2) const;
+ inline boolean is_subset(const isl::basic_set &bset2) const;
+ inline boolean is_subset(const isl::set &set2) const;
+ inline boolean is_subset(const isl::union_set &uset2) const;
+ inline boolean is_subset(const isl::point &bset2) const;
+ inline boolean is_wrapping() const;
+ inline boolean isa_set() const;
+ inline isl::set lexmax() const;
+ inline isl::pw_multi_aff lexmax_pw_multi_aff() const;
+ inline isl::set lexmin() const;
+ inline isl::pw_multi_aff lexmin_pw_multi_aff() const;
+ inline isl::set lower_bound(const isl::multi_pw_aff &lower) const;
+ inline isl::set lower_bound(const isl::multi_val &lower) const;
+ inline isl::set lower_bound_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::set lower_bound_val(isl::dim type, unsigned int pos, const isl::val &value) const;
+ inline isl::set lower_bound_val(isl::dim type, unsigned int pos, long value) const;
+ inline isl::multi_pw_aff max_multi_pw_aff() const;
+ inline isl::val max_val(const isl::aff &obj) const;
+ inline isl::multi_pw_aff min_multi_pw_aff() const;
+ inline isl::val min_val(const isl::aff &obj) const;
+ inline class size n_basic_set() const;
+ inline isl::basic_set params() const;
+ inline isl::val plain_get_val_if_fixed(isl::dim type, unsigned int pos) const;
+ inline isl::multi_val plain_multi_val_if_fixed() const;
+ inline isl::basic_set polyhedral_hull() const;
+ inline isl::set preimage(const isl::multi_aff &ma) const;
+ inline isl::set preimage(const isl::multi_pw_aff &mpa) const;
+ inline isl::set preimage(const isl::pw_multi_aff &pma) const;
+ inline isl::union_set preimage(const isl::union_pw_multi_aff &upma) const;
+ inline isl::set product(const isl::set &set2) const;
+ inline isl::basic_set project_out(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::set project_out_all_params() const;
+ inline isl::set project_out_param(const isl::id &id) const;
+ inline isl::set project_out_param(const std::string &id) const;
+ inline isl::set project_out_param(const isl::id_list &list) const;
+ inline isl::pw_multi_aff pw_multi_aff_on_domain(const isl::multi_val &mv) const;
+ inline isl::set remove_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::set remove_divs() const;
+ inline isl::set remove_redundancies() const;
+ inline isl::set reset_tuple_id() const;
+ inline isl::basic_set sample() const;
+ inline isl::point sample_point() const;
+ inline isl::set set_dim_id(isl::dim type, unsigned int pos, const isl::id &id) const;
+ inline isl::set set_dim_id(isl::dim type, unsigned int pos, const std::string &id) const;
+ inline isl::set_list set_list() const;
+ inline isl::set set_tuple_id(const isl::id &id) const;
+ inline isl::set set_tuple_id(const std::string &id) const;
+ inline isl::fixed_box simple_fixed_box_hull() const;
+ inline isl::basic_set simple_hull() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::val stride(int pos) const;
+ inline isl::set subtract(const isl::set &set2) const;
+ inline isl::union_set subtract(const isl::union_set &uset2) const;
+ inline isl::basic_set_list to_list() const;
+ inline isl::set to_set() const;
+ inline isl::union_set to_union_set() const;
+ inline isl::map translation() const;
+ inline class size tuple_dim() const;
+ inline isl::id tuple_id() const;
+ inline std::string tuple_name() const;
+ inline isl::set unbind_params(const isl::multi_id &tuple) const;
+ inline isl::map unbind_params_insert_domain(const isl::multi_id &domain) const;
+ inline isl::set unite(isl::basic_set bset2) const;
+ inline isl::set unite(const isl::set &set2) const;
+ inline isl::union_set unite(const isl::union_set &uset2) const;
+ inline isl::set unite(const isl::point &bset2) const;
+ static inline isl::basic_set universe(isl::space space);
+ inline isl::basic_set unshifted_simple_hull() const;
+ inline isl::map unwrap() const;
+ inline isl::set upper_bound(const isl::multi_pw_aff &upper) const;
+ inline isl::set upper_bound(const isl::multi_val &upper) const;
+ inline isl::set upper_bound_val(isl::dim type, unsigned int pos, const isl::val &value) const;
+ inline isl::set upper_bound_val(isl::dim type, unsigned int pos, long value) const;
+};
+
+// declarations for isl::basic_set_list
+inline basic_set_list manage(__isl_take isl_basic_set_list *ptr);
+inline basic_set_list manage_copy(__isl_keep isl_basic_set_list *ptr);
+
+class basic_set_list {
+ friend inline basic_set_list manage(__isl_take isl_basic_set_list *ptr);
+ friend inline basic_set_list manage_copy(__isl_keep isl_basic_set_list *ptr);
+
+protected:
+ isl_basic_set_list *ptr = nullptr;
+
+ inline explicit basic_set_list(__isl_take isl_basic_set_list *ptr);
+
+public:
+ inline /* implicit */ basic_set_list();
+ inline /* implicit */ basic_set_list(const basic_set_list &obj);
+ inline explicit basic_set_list(isl::ctx ctx, int n);
+ inline explicit basic_set_list(isl::basic_set el);
+ inline basic_set_list &operator=(basic_set_list obj);
+ inline ~basic_set_list();
+ inline __isl_give isl_basic_set_list *copy() const &;
+ inline __isl_give isl_basic_set_list *copy() && = delete;
+ inline __isl_keep isl_basic_set_list *get() const;
+ inline __isl_give isl_basic_set_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::basic_set_list add(isl::basic_set el) const;
+ inline isl::basic_set at(int index) const;
+ inline isl::basic_set get_at(int index) const;
+ inline isl::basic_set_list clear() const;
+ inline isl::basic_set_list concat(isl::basic_set_list list2) const;
+ inline isl::basic_set_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::basic_set)> &fn) const;
+ inline isl::basic_set_list insert(unsigned int pos, isl::basic_set el) const;
+ inline class size size() const;
+};
+
+// declarations for isl::constraint
+inline constraint manage(__isl_take isl_constraint *ptr);
+inline constraint manage_copy(__isl_keep isl_constraint *ptr);
+
+class constraint {
+ friend inline constraint manage(__isl_take isl_constraint *ptr);
+ friend inline constraint manage_copy(__isl_keep isl_constraint *ptr);
+
+protected:
+ isl_constraint *ptr = nullptr;
+
+ inline explicit constraint(__isl_take isl_constraint *ptr);
+
+public:
+ inline /* implicit */ constraint();
+ inline /* implicit */ constraint(const constraint &obj);
+ inline constraint &operator=(constraint obj);
+ inline ~constraint();
+ inline __isl_give isl_constraint *copy() const &;
+ inline __isl_give isl_constraint *copy() && = delete;
+ inline __isl_keep isl_constraint *get() const;
+ inline __isl_give isl_constraint *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ static inline isl::constraint alloc_equality(isl::local_space ls);
+ static inline isl::constraint alloc_inequality(isl::local_space ls);
+ inline isl::constraint set_coefficient_si(isl::dim type, int pos, int v) const;
+ inline isl::constraint set_constant_si(int v) const;
+ inline isl::constraint set_constant_val(isl::val v) const;
+ inline isl::constraint set_constant_val(long v) const;
+};
+
+// declarations for isl::fixed_box
+inline fixed_box manage(__isl_take isl_fixed_box *ptr);
+inline fixed_box manage_copy(__isl_keep isl_fixed_box *ptr);
+
+class fixed_box {
+ friend inline fixed_box manage(__isl_take isl_fixed_box *ptr);
+ friend inline fixed_box manage_copy(__isl_keep isl_fixed_box *ptr);
+
+protected:
+ isl_fixed_box *ptr = nullptr;
+
+ inline explicit fixed_box(__isl_take isl_fixed_box *ptr);
+
+public:
+ inline /* implicit */ fixed_box();
+ inline /* implicit */ fixed_box(const fixed_box &obj);
+ inline fixed_box &operator=(fixed_box obj);
+ inline ~fixed_box();
+ inline __isl_give isl_fixed_box *copy() const &;
+ inline __isl_give isl_fixed_box *copy() && = delete;
+ inline __isl_keep isl_fixed_box *get() const;
+ inline __isl_give isl_fixed_box *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline boolean is_valid() const;
+ inline isl::multi_aff offset() const;
+ inline isl::multi_aff get_offset() const;
+ inline isl::multi_val size() const;
+ inline isl::multi_val get_size() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+};
+
+// declarations for isl::id
+inline id manage(__isl_take isl_id *ptr);
+inline id manage_copy(__isl_keep isl_id *ptr);
+
+class id {
+ friend inline id manage(__isl_take isl_id *ptr);
+ friend inline id manage_copy(__isl_keep isl_id *ptr);
+
+protected:
+ isl_id *ptr = nullptr;
+
+ inline explicit id(__isl_take isl_id *ptr);
+
+public:
+ inline /* implicit */ id();
+ inline /* implicit */ id(const id &obj);
+ inline explicit id(isl::ctx ctx, const std::string &str);
+ inline id &operator=(id obj);
+ inline ~id();
+ inline __isl_give isl_id *copy() const &;
+ inline __isl_give isl_id *copy() && = delete;
+ inline __isl_keep isl_id *get() const;
+ inline __isl_give isl_id *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ static inline isl::id alloc(isl::ctx ctx, const std::string &name, void * user);
+ inline std::string name() const;
+ inline std::string get_name() const;
+ inline isl::id_list to_list() const;
+ inline void * user() const;
+ inline void * get_user() const;
+};
+
+// declarations for isl::id_list
+inline id_list manage(__isl_take isl_id_list *ptr);
+inline id_list manage_copy(__isl_keep isl_id_list *ptr);
+
+class id_list {
+ friend inline id_list manage(__isl_take isl_id_list *ptr);
+ friend inline id_list manage_copy(__isl_keep isl_id_list *ptr);
+
+protected:
+ isl_id_list *ptr = nullptr;
+
+ inline explicit id_list(__isl_take isl_id_list *ptr);
+
+public:
+ inline /* implicit */ id_list();
+ inline /* implicit */ id_list(const id_list &obj);
+ inline explicit id_list(isl::ctx ctx, int n);
+ inline explicit id_list(isl::id el);
+ inline explicit id_list(isl::ctx ctx, const std::string &str);
+ inline id_list &operator=(id_list obj);
+ inline ~id_list();
+ inline __isl_give isl_id_list *copy() const &;
+ inline __isl_give isl_id_list *copy() && = delete;
+ inline __isl_keep isl_id_list *get() const;
+ inline __isl_give isl_id_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::id_list add(isl::id el) const;
+ inline isl::id_list add(const std::string &el) const;
+ inline isl::id at(int index) const;
+ inline isl::id get_at(int index) const;
+ inline isl::id_list clear() const;
+ inline isl::id_list concat(isl::id_list list2) const;
+ inline isl::id_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::id)> &fn) const;
+ inline isl::id_list insert(unsigned int pos, isl::id el) const;
+ inline isl::id_list insert(unsigned int pos, const std::string &el) const;
+ inline class size size() const;
+};
+
+// declarations for isl::id_to_ast_expr
+inline id_to_ast_expr manage(__isl_take isl_id_to_ast_expr *ptr);
+inline id_to_ast_expr manage_copy(__isl_keep isl_id_to_ast_expr *ptr);
+
+class id_to_ast_expr {
+ friend inline id_to_ast_expr manage(__isl_take isl_id_to_ast_expr *ptr);
+ friend inline id_to_ast_expr manage_copy(__isl_keep isl_id_to_ast_expr *ptr);
+
+protected:
+ isl_id_to_ast_expr *ptr = nullptr;
+
+ inline explicit id_to_ast_expr(__isl_take isl_id_to_ast_expr *ptr);
+
+public:
+ inline /* implicit */ id_to_ast_expr();
+ inline /* implicit */ id_to_ast_expr(const id_to_ast_expr &obj);
+ inline id_to_ast_expr &operator=(id_to_ast_expr obj);
+ inline ~id_to_ast_expr();
+ inline __isl_give isl_id_to_ast_expr *copy() const &;
+ inline __isl_give isl_id_to_ast_expr *copy() && = delete;
+ inline __isl_keep isl_id_to_ast_expr *get() const;
+ inline __isl_give isl_id_to_ast_expr *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ static inline isl::id_to_ast_expr alloc(isl::ctx ctx, int min_size);
+ inline isl::id_to_ast_expr set(isl::id key, isl::ast_expr val) const;
+ inline isl::id_to_ast_expr set(const std::string &key, const isl::ast_expr &val) const;
+};
+
+// declarations for isl::local_space
+inline local_space manage(__isl_take isl_local_space *ptr);
+inline local_space manage_copy(__isl_keep isl_local_space *ptr);
+
+class local_space {
+ friend inline local_space manage(__isl_take isl_local_space *ptr);
+ friend inline local_space manage_copy(__isl_keep isl_local_space *ptr);
+
+protected:
+ isl_local_space *ptr = nullptr;
+
+ inline explicit local_space(__isl_take isl_local_space *ptr);
+
+public:
+ inline /* implicit */ local_space();
+ inline /* implicit */ local_space(const local_space &obj);
+ inline explicit local_space(isl::space space);
+ inline local_space &operator=(local_space obj);
+ inline ~local_space();
+ inline __isl_give isl_local_space *copy() const &;
+ inline __isl_give isl_local_space *copy() && = delete;
+ inline __isl_keep isl_local_space *get() const;
+ inline __isl_give isl_local_space *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::map
+inline map manage(__isl_take isl_map *ptr);
+inline map manage_copy(__isl_keep isl_map *ptr);
+
+class map {
+ friend inline map manage(__isl_take isl_map *ptr);
+ friend inline map manage_copy(__isl_keep isl_map *ptr);
+
+protected:
+ isl_map *ptr = nullptr;
+
+ inline explicit map(__isl_take isl_map *ptr);
+
+public:
+ inline /* implicit */ map();
+ inline /* implicit */ map(const map &obj);
+ inline /* implicit */ map(isl::basic_map bmap);
+ inline explicit map(isl::ctx ctx, const std::string &str);
+ inline map &operator=(map obj);
+ inline ~map();
+ inline __isl_give isl_map *copy() const &;
+ inline __isl_give isl_map *copy() && = delete;
+ inline __isl_keep isl_map *get() const;
+ inline __isl_give isl_map *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::map add_constraint(isl::constraint constraint) const;
+ inline isl::map add_dims(isl::dim type, unsigned int n) const;
+ inline isl::basic_map affine_hull() const;
+ inline isl::map align_params(isl::space model) const;
+ inline isl::map apply_domain(isl::map map2) const;
+ inline isl::union_map apply_domain(const isl::union_map &umap2) const;
+ inline isl::map apply_domain(const isl::basic_map &map2) const;
+ inline isl::map apply_range(isl::map map2) const;
+ inline isl::union_map apply_range(const isl::union_map &umap2) const;
+ inline isl::map apply_range(const isl::basic_map &map2) const;
+ inline isl::map as_map() const;
+ inline isl::multi_union_pw_aff as_multi_union_pw_aff() const;
+ inline isl::pw_multi_aff as_pw_multi_aff() const;
+ inline isl::union_pw_multi_aff as_union_pw_multi_aff() const;
+ inline isl::basic_map_list basic_map_list() const;
+ inline isl::basic_map_list get_basic_map_list() const;
+ inline isl::set bind_domain(isl::multi_id tuple) const;
+ inline isl::set bind_range(isl::multi_id tuple) const;
+ inline boolean can_curry() const;
+ inline isl::map coalesce() const;
+ inline isl::map complement() const;
+ inline isl::union_map compute_divs() const;
+ inline isl::map curry() const;
+ inline isl::set deltas() const;
+ inline isl::map detect_equalities() const;
+ inline class size dim(isl::dim type) const;
+ inline isl::pw_aff dim_max(int pos) const;
+ inline isl::pw_aff dim_min(int pos) const;
+ inline isl::set domain() const;
+ inline isl::map domain_factor_domain() const;
+ inline isl::map domain_factor_range() const;
+ inline isl::map domain_map() const;
+ inline isl::union_pw_multi_aff domain_map_union_pw_multi_aff() const;
+ inline isl::map domain_product(isl::map map2) const;
+ inline isl::union_map domain_product(const isl::union_map &umap2) const;
+ inline isl::map domain_product(const isl::basic_map &map2) const;
+ inline class size domain_tuple_dim() const;
+ inline isl::id domain_tuple_id() const;
+ inline isl::id get_domain_tuple_id() const;
+ static inline isl::map empty(isl::space space);
+ inline isl::map eq_at(isl::multi_pw_aff mpa) const;
+ inline isl::union_map eq_at(const isl::multi_union_pw_aff &mupa) const;
+ inline isl::map eq_at(const isl::aff &mpa) const;
+ inline isl::map eq_at(const isl::multi_aff &mpa) const;
+ inline isl::map eq_at(const isl::pw_aff &mpa) const;
+ inline isl::map eq_at(const isl::pw_multi_aff &mpa) const;
+ inline isl::map equate(isl::dim type1, int pos1, isl::dim type2, int pos2) const;
+ inline boolean every_map(const std::function<boolean(isl::map)> &test) const;
+ inline isl::map extract_map(const isl::space &space) const;
+ inline isl::map factor_domain() const;
+ inline isl::map factor_range() const;
+ inline isl::map fix_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::union_map fixed_power(const isl::val &exp) const;
+ inline isl::union_map fixed_power(long exp) const;
+ inline isl::map flat_range_product(isl::map map2) const;
+ inline isl::union_map flat_range_product(const isl::union_map &umap2) const;
+ inline isl::map flat_range_product(const isl::basic_map &map2) const;
+ inline isl::map flatten() const;
+ inline isl::map flatten_domain() const;
+ inline isl::map flatten_range() const;
+ inline isl::map floordiv_val(isl::val d) const;
+ inline isl::map floordiv_val(long d) const;
+ inline stat foreach_basic_map(const std::function<stat(isl::basic_map)> &fn) const;
+ inline stat foreach_map(const std::function<stat(isl::map)> &fn) const;
+ static inline isl::map from_aff(isl::aff aff);
+ static inline isl::map from_domain(isl::set set);
+ static inline isl::map from_domain_and_range(isl::set domain, isl::set range);
+ static inline isl::map from_multi_aff(isl::multi_aff maff);
+ static inline isl::map from_pw_aff(isl::pw_aff pwaff);
+ static inline isl::map from_range(isl::set set);
+ static inline isl::map from_union_map(isl::union_map umap);
+ inline isl::map gist(isl::map context) const;
+ inline isl::union_map gist(const isl::union_map &context) const;
+ inline isl::map gist(const isl::basic_map &context) const;
+ inline isl::map gist_domain(isl::set context) const;
+ inline isl::union_map gist_domain(const isl::union_set &uset) const;
+ inline isl::map gist_domain(const isl::basic_set &context) const;
+ inline isl::map gist_domain(const isl::point &context) const;
+ inline isl::map gist_params(isl::set context) const;
+ inline isl::union_map gist_range(const isl::union_set &uset) const;
+ inline boolean has_domain_tuple_id() const;
+ inline boolean has_equal_space(const isl::map &map2) const;
+ inline boolean has_range_tuple_id() const;
+ inline boolean has_tuple_id(isl::dim type) const;
+ inline boolean has_tuple_name(isl::dim type) const;
+ static inline isl::map identity(isl::space space);
+ inline isl::map intersect(isl::map map2) const;
+ inline isl::union_map intersect(const isl::union_map &umap2) const;
+ inline isl::map intersect(const isl::basic_map &map2) const;
+ inline isl::map intersect_domain(isl::set set) const;
+ inline isl::union_map intersect_domain(const isl::space &space) const;
+ inline isl::union_map intersect_domain(const isl::union_set &uset) const;
+ inline isl::map intersect_domain(const isl::basic_set &set) const;
+ inline isl::map intersect_domain(const isl::point &set) const;
+ inline isl::map intersect_domain_factor_domain(isl::map factor) const;
+ inline isl::union_map intersect_domain_factor_domain(const isl::union_map &factor) const;
+ inline isl::map intersect_domain_factor_domain(const isl::basic_map &factor) const;
+ inline isl::map intersect_domain_factor_range(isl::map factor) const;
+ inline isl::union_map intersect_domain_factor_range(const isl::union_map &factor) const;
+ inline isl::map intersect_domain_factor_range(const isl::basic_map &factor) const;
+ inline isl::map intersect_params(isl::set params) const;
+ inline isl::map intersect_range(isl::set set) const;
+ inline isl::union_map intersect_range(const isl::space &space) const;
+ inline isl::union_map intersect_range(const isl::union_set &uset) const;
+ inline isl::map intersect_range(const isl::basic_set &set) const;
+ inline isl::map intersect_range(const isl::point &set) const;
+ inline isl::map intersect_range_factor_domain(isl::map factor) const;
+ inline isl::union_map intersect_range_factor_domain(const isl::union_map &factor) const;
+ inline isl::map intersect_range_factor_domain(const isl::basic_map &factor) const;
+ inline isl::map intersect_range_factor_range(isl::map factor) const;
+ inline isl::union_map intersect_range_factor_range(const isl::union_map &factor) const;
+ inline isl::map intersect_range_factor_range(const isl::basic_map &factor) const;
+ inline boolean involves_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline boolean is_bijective() const;
+ inline boolean is_disjoint(const isl::map &map2) const;
+ inline boolean is_disjoint(const isl::union_map &umap2) const;
+ inline boolean is_disjoint(const isl::basic_map &map2) const;
+ inline boolean is_empty() const;
+ inline boolean is_equal(const isl::map &map2) const;
+ inline boolean is_equal(const isl::union_map &umap2) const;
+ inline boolean is_equal(const isl::basic_map &map2) const;
+ inline boolean is_injective() const;
+ inline boolean is_single_valued() const;
+ inline boolean is_strict_subset(const isl::map &map2) const;
+ inline boolean is_strict_subset(const isl::union_map &umap2) const;
+ inline boolean is_strict_subset(const isl::basic_map &map2) const;
+ inline boolean is_subset(const isl::map &map2) const;
+ inline boolean is_subset(const isl::union_map &umap2) const;
+ inline boolean is_subset(const isl::basic_map &map2) const;
+ inline boolean isa_map() const;
+ static inline isl::map lex_ge(isl::space set_space);
+ inline isl::map lex_ge_at(isl::multi_pw_aff mpa) const;
+ static inline isl::map lex_gt(isl::space set_space);
+ inline isl::map lex_gt_at(isl::multi_pw_aff mpa) const;
+ static inline isl::map lex_le(isl::space set_space);
+ inline isl::map lex_le_at(isl::multi_pw_aff mpa) const;
+ static inline isl::map lex_lt(isl::space set_space);
+ inline isl::map lex_lt_at(isl::multi_pw_aff mpa) const;
+ inline isl::map lexmax() const;
+ inline isl::pw_multi_aff lexmax_pw_multi_aff() const;
+ inline isl::map lexmin() const;
+ inline isl::pw_multi_aff lexmin_pw_multi_aff() const;
+ inline isl::map lower_bound(isl::multi_pw_aff lower) const;
+ inline isl::map lower_bound_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::map_list map_list() const;
+ inline isl::multi_pw_aff max_multi_pw_aff() const;
+ inline isl::multi_pw_aff min_multi_pw_aff() const;
+ inline isl::map move_dims(isl::dim dst_type, unsigned int dst_pos, isl::dim src_type, unsigned int src_pos, unsigned int n) const;
+ inline class size n_basic_map() const;
+ inline isl::map order_lt(isl::dim type1, int pos1, isl::dim type2, int pos2) const;
+ inline isl::set params() const;
+ inline isl::basic_map polyhedral_hull() const;
+ inline isl::map preimage_domain(isl::multi_aff ma) const;
+ inline isl::map preimage_domain(isl::multi_pw_aff mpa) const;
+ inline isl::map preimage_domain(isl::pw_multi_aff pma) const;
+ inline isl::union_map preimage_domain(const isl::union_pw_multi_aff &upma) const;
+ inline isl::map preimage_range(isl::multi_aff ma) const;
+ inline isl::map preimage_range(isl::pw_multi_aff pma) const;
+ inline isl::union_map preimage_range(const isl::union_pw_multi_aff &upma) const;
+ inline isl::map product(isl::map map2) const;
+ inline isl::union_map product(const isl::union_map &umap2) const;
+ inline isl::map product(const isl::basic_map &map2) const;
+ inline isl::map project_out(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::map project_out_all_params() const;
+ inline isl::set range() const;
+ inline isl::map range_factor_domain() const;
+ inline isl::map range_factor_range() const;
+ inline isl::fixed_box range_lattice_tile() const;
+ inline isl::fixed_box get_range_lattice_tile() const;
+ inline isl::map range_map() const;
+ inline isl::map range_product(isl::map map2) const;
+ inline isl::union_map range_product(const isl::union_map &umap2) const;
+ inline isl::map range_product(const isl::basic_map &map2) const;
+ inline isl::map range_reverse() const;
+ inline isl::fixed_box range_simple_fixed_box_hull() const;
+ inline isl::fixed_box get_range_simple_fixed_box_hull() const;
+ inline class size range_tuple_dim() const;
+ inline isl::id range_tuple_id() const;
+ inline isl::id get_range_tuple_id() const;
+ inline isl::map reverse() const;
+ inline isl::basic_map sample() const;
+ inline isl::map set_domain_tuple(isl::id id) const;
+ inline isl::map set_domain_tuple(const std::string &id) const;
+ inline isl::map set_range_tuple(isl::id id) const;
+ inline isl::map set_range_tuple(const std::string &id) const;
+ inline isl::map set_tuple_id(isl::dim type, isl::id id) const;
+ inline isl::map set_tuple_id(isl::dim type, const std::string &id) const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::map subtract(isl::map map2) const;
+ inline isl::union_map subtract(const isl::union_map &umap2) const;
+ inline isl::map subtract(const isl::basic_map &map2) const;
+ inline isl::union_map subtract_domain(const isl::union_set &dom) const;
+ inline isl::union_map subtract_range(const isl::union_set &dom) const;
+ inline isl::map sum(isl::map map2) const;
+ inline isl::map_list to_list() const;
+ inline isl::union_map to_union_map() const;
+ inline isl::id tuple_id(isl::dim type) const;
+ inline isl::id get_tuple_id(isl::dim type) const;
+ inline isl::map uncurry() const;
+ inline isl::map unite(isl::map map2) const;
+ inline isl::union_map unite(const isl::union_map &umap2) const;
+ inline isl::map unite(const isl::basic_map &map2) const;
+ static inline isl::map universe(isl::space space);
+ inline isl::basic_map unshifted_simple_hull() const;
+ inline isl::map upper_bound(isl::multi_pw_aff upper) const;
+ inline isl::map upper_bound_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::set wrap() const;
+ inline isl::map zip() const;
+};
+
+// declarations for isl::map_list
+inline map_list manage(__isl_take isl_map_list *ptr);
+inline map_list manage_copy(__isl_keep isl_map_list *ptr);
+
+class map_list {
+ friend inline map_list manage(__isl_take isl_map_list *ptr);
+ friend inline map_list manage_copy(__isl_keep isl_map_list *ptr);
+
+protected:
+ isl_map_list *ptr = nullptr;
+
+ inline explicit map_list(__isl_take isl_map_list *ptr);
+
+public:
+ inline /* implicit */ map_list();
+ inline /* implicit */ map_list(const map_list &obj);
+ inline explicit map_list(isl::ctx ctx, int n);
+ inline explicit map_list(isl::map el);
+ inline explicit map_list(isl::ctx ctx, const std::string &str);
+ inline map_list &operator=(map_list obj);
+ inline ~map_list();
+ inline __isl_give isl_map_list *copy() const &;
+ inline __isl_give isl_map_list *copy() && = delete;
+ inline __isl_keep isl_map_list *get() const;
+ inline __isl_give isl_map_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::map_list add(isl::map el) const;
+ inline isl::map at(int index) const;
+ inline isl::map get_at(int index) const;
+ inline isl::map_list clear() const;
+ inline isl::map_list concat(isl::map_list list2) const;
+ inline isl::map_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::map)> &fn) const;
+ inline isl::map_list insert(unsigned int pos, isl::map el) const;
+ inline class size size() const;
+};
+
+// declarations for isl::multi_aff
+inline multi_aff manage(__isl_take isl_multi_aff *ptr);
+inline multi_aff manage_copy(__isl_keep isl_multi_aff *ptr);
+
+class multi_aff {
+ friend inline multi_aff manage(__isl_take isl_multi_aff *ptr);
+ friend inline multi_aff manage_copy(__isl_keep isl_multi_aff *ptr);
+
+protected:
+ isl_multi_aff *ptr = nullptr;
+
+ inline explicit multi_aff(__isl_take isl_multi_aff *ptr);
+
+public:
+ inline /* implicit */ multi_aff();
+ inline /* implicit */ multi_aff(const multi_aff &obj);
+ inline /* implicit */ multi_aff(isl::aff aff);
+ inline explicit multi_aff(isl::space space, isl::aff_list list);
+ inline explicit multi_aff(isl::ctx ctx, const std::string &str);
+ inline multi_aff &operator=(multi_aff obj);
+ inline ~multi_aff();
+ inline __isl_give isl_multi_aff *copy() const &;
+ inline __isl_give isl_multi_aff *copy() && = delete;
+ inline __isl_keep isl_multi_aff *get() const;
+ inline __isl_give isl_multi_aff *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::multi_aff add(isl::multi_aff multi2) const;
+ inline isl::multi_pw_aff add(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff add(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff add(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff add(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::multi_aff add(const isl::aff &multi2) const;
+ inline isl::multi_aff add_constant(isl::multi_val mv) const;
+ inline isl::multi_aff add_constant(isl::val v) const;
+ inline isl::multi_aff add_constant(long v) const;
+ inline isl::union_pw_multi_aff add_pw_multi_aff(const isl::pw_multi_aff &pma) const;
+ inline isl::union_pw_multi_aff apply(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::map as_map() const;
+ inline isl::multi_aff as_multi_aff() const;
+ inline isl::multi_union_pw_aff as_multi_union_pw_aff() const;
+ inline isl::pw_multi_aff as_pw_multi_aff() const;
+ inline isl::set as_set() const;
+ inline isl::union_map as_union_map() const;
+ inline isl::aff at(int pos) const;
+ inline isl::aff get_at(int pos) const;
+ inline isl::basic_set bind(isl::multi_id tuple) const;
+ inline isl::multi_aff bind_domain(isl::multi_id tuple) const;
+ inline isl::multi_aff bind_domain_wrapped_domain(isl::multi_id tuple) const;
+ inline isl::pw_multi_aff coalesce() const;
+ inline isl::multi_val constant_multi_val() const;
+ inline isl::multi_val get_constant_multi_val() const;
+ inline class size dim(isl::dim type) const;
+ inline isl::set domain() const;
+ static inline isl::multi_aff domain_map(isl::space space);
+ inline isl::pw_multi_aff drop_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::pw_multi_aff extract_pw_multi_aff(const isl::space &space) const;
+ inline isl::multi_aff flat_range_product(isl::multi_aff multi2) const;
+ inline isl::multi_pw_aff flat_range_product(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff flat_range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff flat_range_product(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff flat_range_product(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::multi_aff flat_range_product(const isl::aff &multi2) const;
+ inline isl::multi_aff floor() const;
+ inline stat foreach_piece(const std::function<stat(isl::set, isl::multi_aff)> &fn) const;
+ inline isl::multi_aff gist(isl::set context) const;
+ inline isl::union_pw_multi_aff gist(const isl::union_set &context) const;
+ inline isl::multi_aff gist(const isl::basic_set &context) const;
+ inline isl::multi_aff gist(const isl::point &context) const;
+ inline boolean has_range_tuple_id() const;
+ static inline isl::multi_aff identity(isl::space space);
+ inline isl::multi_aff identity() const;
+ static inline isl::multi_aff identity_on_domain(isl::space space);
+ inline isl::multi_aff insert_domain(isl::space domain) const;
+ inline isl::pw_multi_aff intersect_domain(const isl::set &set) const;
+ inline isl::union_pw_multi_aff intersect_domain(const isl::space &space) const;
+ inline isl::union_pw_multi_aff intersect_domain(const isl::union_set &uset) const;
+ inline isl::union_pw_multi_aff intersect_domain_wrapped_domain(const isl::union_set &uset) const;
+ inline isl::union_pw_multi_aff intersect_domain_wrapped_range(const isl::union_set &uset) const;
+ inline isl::pw_multi_aff intersect_params(const isl::set &set) const;
+ inline boolean involves_locals() const;
+ inline boolean involves_nan() const;
+ inline boolean involves_param(const isl::id &id) const;
+ inline boolean involves_param(const std::string &id) const;
+ inline boolean involves_param(const isl::id_list &list) const;
+ inline boolean isa_multi_aff() const;
+ inline boolean isa_pw_multi_aff() const;
+ inline isl::aff_list list() const;
+ inline isl::aff_list get_list() const;
+ inline isl::multi_pw_aff max(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_val max_multi_val() const;
+ inline isl::multi_pw_aff min(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_val min_multi_val() const;
+ static inline isl::multi_aff multi_val_on_domain(isl::space space, isl::multi_val mv);
+ inline class size n_piece() const;
+ inline isl::multi_aff neg() const;
+ inline boolean plain_is_empty() const;
+ inline boolean plain_is_equal(const isl::multi_aff &multi2) const;
+ inline boolean plain_is_equal(const isl::multi_pw_aff &multi2) const;
+ inline boolean plain_is_equal(const isl::multi_union_pw_aff &multi2) const;
+ inline boolean plain_is_equal(const isl::aff &multi2) const;
+ inline isl::pw_multi_aff preimage_domain_wrapped_domain(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff preimage_domain_wrapped_domain(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::multi_aff product(isl::multi_aff multi2) const;
+ inline isl::multi_pw_aff product(const isl::multi_pw_aff &multi2) const;
+ inline isl::pw_multi_aff product(const isl::pw_multi_aff &pma2) const;
+ inline isl::multi_aff product(const isl::aff &multi2) const;
+ inline isl::multi_aff pullback(isl::multi_aff ma2) const;
+ inline isl::multi_pw_aff pullback(const isl::multi_pw_aff &mpa2) const;
+ inline isl::pw_multi_aff pullback(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff pullback(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::multi_aff pullback(const isl::aff &ma2) const;
+ inline isl::pw_multi_aff_list pw_multi_aff_list() const;
+ inline isl::pw_multi_aff range_factor_domain() const;
+ inline isl::pw_multi_aff range_factor_range() const;
+ static inline isl::multi_aff range_map(isl::space space);
+ inline isl::multi_aff range_product(isl::multi_aff multi2) const;
+ inline isl::multi_pw_aff range_product(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff range_product(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff range_product(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::multi_aff range_product(const isl::aff &multi2) const;
+ inline isl::id range_tuple_id() const;
+ inline isl::id get_range_tuple_id() const;
+ inline isl::multi_aff reset_range_tuple_id() const;
+ inline isl::multi_aff reset_tuple_id(isl::dim type) const;
+ inline isl::multi_aff scale(isl::multi_val mv) const;
+ inline isl::multi_aff scale(isl::val v) const;
+ inline isl::multi_aff scale(long v) const;
+ inline isl::multi_aff scale_down(isl::multi_val mv) const;
+ inline isl::multi_aff scale_down(isl::val v) const;
+ inline isl::multi_aff scale_down(long v) const;
+ inline isl::multi_aff set_aff(int pos, isl::aff el) const;
+ inline isl::multi_aff set_at(int pos, isl::aff el) const;
+ inline isl::multi_pw_aff set_at(int pos, const isl::pw_aff &el) const;
+ inline isl::multi_union_pw_aff set_at(int pos, const isl::union_pw_aff &el) const;
+ inline isl::multi_pw_aff set_pw_aff(int pos, const isl::pw_aff &el) const;
+ inline isl::pw_multi_aff set_pw_aff(unsigned int pos, const isl::pw_aff &pa) const;
+ inline isl::multi_aff set_range_tuple(isl::id id) const;
+ inline isl::multi_aff set_range_tuple(const std::string &id) const;
+ inline isl::multi_union_pw_aff set_union_pw_aff(int pos, const isl::union_pw_aff &el) const;
+ inline class size size() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::multi_aff sub(isl::multi_aff multi2) const;
+ inline isl::multi_pw_aff sub(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff sub(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff sub(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff sub(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::multi_aff sub(const isl::aff &multi2) const;
+ inline isl::pw_multi_aff subtract_domain(const isl::set &set) const;
+ inline isl::union_pw_multi_aff subtract_domain(const isl::space &space) const;
+ inline isl::union_pw_multi_aff subtract_domain(const isl::union_set &uset) const;
+ inline isl::pw_multi_aff_list to_list() const;
+ inline isl::multi_pw_aff to_multi_pw_aff() const;
+ inline isl::multi_union_pw_aff to_multi_union_pw_aff() const;
+ inline isl::pw_multi_aff to_pw_multi_aff() const;
+ inline isl::union_pw_multi_aff to_union_pw_multi_aff() const;
+ inline isl::id tuple_id(isl::dim type) const;
+ inline isl::multi_aff unbind_params_insert_domain(isl::multi_id domain) const;
+ inline isl::multi_pw_aff union_add(const isl::multi_pw_aff &mpa2) const;
+ inline isl::multi_union_pw_aff union_add(const isl::multi_union_pw_aff &mupa2) const;
+ inline isl::pw_multi_aff union_add(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff union_add(const isl::union_pw_multi_aff &upma2) const;
+ static inline isl::multi_aff zero(isl::space space);
+};
+
+// declarations for isl::multi_id
+inline multi_id manage(__isl_take isl_multi_id *ptr);
+inline multi_id manage_copy(__isl_keep isl_multi_id *ptr);
+
+class multi_id {
+ friend inline multi_id manage(__isl_take isl_multi_id *ptr);
+ friend inline multi_id manage_copy(__isl_keep isl_multi_id *ptr);
+
+protected:
+ isl_multi_id *ptr = nullptr;
+
+ inline explicit multi_id(__isl_take isl_multi_id *ptr);
+
+public:
+ inline /* implicit */ multi_id();
+ inline /* implicit */ multi_id(const multi_id &obj);
+ inline explicit multi_id(isl::space space, isl::id_list list);
+ inline explicit multi_id(isl::ctx ctx, const std::string &str);
+ inline multi_id &operator=(multi_id obj);
+ inline ~multi_id();
+ inline __isl_give isl_multi_id *copy() const &;
+ inline __isl_give isl_multi_id *copy() && = delete;
+ inline __isl_keep isl_multi_id *get() const;
+ inline __isl_give isl_multi_id *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::id at(int pos) const;
+ inline isl::id get_at(int pos) const;
+ inline isl::multi_id flat_range_product(isl::multi_id multi2) const;
+ inline isl::id_list list() const;
+ inline isl::id_list get_list() const;
+ inline boolean plain_is_equal(const isl::multi_id &multi2) const;
+ inline isl::multi_id range_product(isl::multi_id multi2) const;
+ inline isl::multi_id set_at(int pos, isl::id el) const;
+ inline isl::multi_id set_at(int pos, const std::string &el) const;
+ inline isl::multi_id set_id(int pos, isl::id el) const;
+ inline isl::multi_id set_id(int pos, const std::string &el) const;
+ inline class size size() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+};
+
+// declarations for isl::multi_pw_aff
+inline multi_pw_aff manage(__isl_take isl_multi_pw_aff *ptr);
+inline multi_pw_aff manage_copy(__isl_keep isl_multi_pw_aff *ptr);
+
+class multi_pw_aff {
+ friend inline multi_pw_aff manage(__isl_take isl_multi_pw_aff *ptr);
+ friend inline multi_pw_aff manage_copy(__isl_keep isl_multi_pw_aff *ptr);
+
+protected:
+ isl_multi_pw_aff *ptr = nullptr;
+
+ inline explicit multi_pw_aff(__isl_take isl_multi_pw_aff *ptr);
+
+public:
+ inline /* implicit */ multi_pw_aff();
+ inline /* implicit */ multi_pw_aff(const multi_pw_aff &obj);
+ inline /* implicit */ multi_pw_aff(isl::aff aff);
+ inline /* implicit */ multi_pw_aff(isl::multi_aff ma);
+ inline /* implicit */ multi_pw_aff(isl::pw_aff pa);
+ inline explicit multi_pw_aff(isl::space space, isl::pw_aff_list list);
+ inline /* implicit */ multi_pw_aff(isl::pw_multi_aff pma);
+ inline explicit multi_pw_aff(isl::ctx ctx, const std::string &str);
+ inline multi_pw_aff &operator=(multi_pw_aff obj);
+ inline ~multi_pw_aff();
+ inline __isl_give isl_multi_pw_aff *copy() const &;
+ inline __isl_give isl_multi_pw_aff *copy() && = delete;
+ inline __isl_keep isl_multi_pw_aff *get() const;
+ inline __isl_give isl_multi_pw_aff *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::multi_pw_aff add(isl::multi_pw_aff multi2) const;
+ inline isl::multi_union_pw_aff add(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::multi_pw_aff add(const isl::aff &multi2) const;
+ inline isl::multi_pw_aff add(const isl::multi_aff &multi2) const;
+ inline isl::multi_pw_aff add(const isl::pw_aff &multi2) const;
+ inline isl::multi_pw_aff add(const isl::pw_multi_aff &multi2) const;
+ inline isl::multi_pw_aff add_constant(isl::multi_val mv) const;
+ inline isl::multi_pw_aff add_constant(isl::val v) const;
+ inline isl::multi_pw_aff add_constant(long v) const;
+ inline isl::map as_map() const;
+ inline isl::multi_aff as_multi_aff() const;
+ inline isl::set as_set() const;
+ inline isl::pw_aff at(int pos) const;
+ inline isl::pw_aff get_at(int pos) const;
+ inline isl::set bind(isl::multi_id tuple) const;
+ inline isl::multi_pw_aff bind_domain(isl::multi_id tuple) const;
+ inline isl::multi_pw_aff bind_domain_wrapped_domain(isl::multi_id tuple) const;
+ inline isl::multi_pw_aff coalesce() const;
+ inline class size dim(isl::dim type) const;
+ inline isl::set domain() const;
+ inline isl::multi_pw_aff flat_range_product(isl::multi_pw_aff multi2) const;
+ inline isl::multi_union_pw_aff flat_range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::multi_pw_aff flat_range_product(const isl::aff &multi2) const;
+ inline isl::multi_pw_aff flat_range_product(const isl::multi_aff &multi2) const;
+ inline isl::multi_pw_aff flat_range_product(const isl::pw_aff &multi2) const;
+ inline isl::multi_pw_aff flat_range_product(const isl::pw_multi_aff &multi2) const;
+ inline isl::multi_pw_aff gist(isl::set set) const;
+ inline isl::multi_union_pw_aff gist(const isl::union_set &context) const;
+ inline isl::multi_pw_aff gist(const isl::basic_set &set) const;
+ inline isl::multi_pw_aff gist(const isl::point &set) const;
+ inline boolean has_range_tuple_id() const;
+ static inline isl::multi_pw_aff identity(isl::space space);
+ inline isl::multi_pw_aff identity() const;
+ static inline isl::multi_pw_aff identity_on_domain(isl::space space);
+ inline isl::multi_pw_aff insert_domain(isl::space domain) const;
+ inline isl::multi_pw_aff intersect_domain(isl::set domain) const;
+ inline isl::multi_union_pw_aff intersect_domain(const isl::union_set &uset) const;
+ inline isl::multi_pw_aff intersect_domain(const isl::basic_set &domain) const;
+ inline isl::multi_pw_aff intersect_domain(const isl::point &domain) const;
+ inline isl::multi_pw_aff intersect_params(isl::set set) const;
+ inline boolean involves_nan() const;
+ inline boolean involves_param(const isl::id &id) const;
+ inline boolean involves_param(const std::string &id) const;
+ inline boolean involves_param(const isl::id_list &list) const;
+ inline boolean isa_multi_aff() const;
+ inline isl::pw_aff_list list() const;
+ inline isl::pw_aff_list get_list() const;
+ inline isl::multi_pw_aff max(isl::multi_pw_aff multi2) const;
+ inline isl::multi_val max_multi_val() const;
+ inline isl::multi_pw_aff min(isl::multi_pw_aff multi2) const;
+ inline isl::multi_val min_multi_val() const;
+ inline isl::multi_pw_aff neg() const;
+ inline boolean plain_is_equal(const isl::multi_pw_aff &multi2) const;
+ inline boolean plain_is_equal(const isl::multi_union_pw_aff &multi2) const;
+ inline boolean plain_is_equal(const isl::aff &multi2) const;
+ inline boolean plain_is_equal(const isl::multi_aff &multi2) const;
+ inline boolean plain_is_equal(const isl::pw_aff &multi2) const;
+ inline boolean plain_is_equal(const isl::pw_multi_aff &multi2) const;
+ inline isl::multi_pw_aff product(isl::multi_pw_aff multi2) const;
+ inline isl::multi_pw_aff pullback(isl::multi_aff ma) const;
+ inline isl::multi_pw_aff pullback(isl::multi_pw_aff mpa2) const;
+ inline isl::multi_pw_aff pullback(isl::pw_multi_aff pma) const;
+ inline isl::multi_union_pw_aff pullback(const isl::union_pw_multi_aff &upma) const;
+ inline isl::multi_pw_aff range_product(isl::multi_pw_aff multi2) const;
+ inline isl::multi_union_pw_aff range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::multi_pw_aff range_product(const isl::aff &multi2) const;
+ inline isl::multi_pw_aff range_product(const isl::multi_aff &multi2) const;
+ inline isl::multi_pw_aff range_product(const isl::pw_aff &multi2) const;
+ inline isl::multi_pw_aff range_product(const isl::pw_multi_aff &multi2) const;
+ inline isl::id range_tuple_id() const;
+ inline isl::id get_range_tuple_id() const;
+ inline isl::multi_pw_aff reset_range_tuple_id() const;
+ inline isl::multi_pw_aff reset_tuple_id(isl::dim type) const;
+ inline isl::multi_pw_aff scale(isl::multi_val mv) const;
+ inline isl::multi_pw_aff scale(isl::val v) const;
+ inline isl::multi_pw_aff scale(long v) const;
+ inline isl::multi_pw_aff scale_down(isl::multi_val mv) const;
+ inline isl::multi_pw_aff scale_down(isl::val v) const;
+ inline isl::multi_pw_aff scale_down(long v) const;
+ inline isl::multi_pw_aff set_at(int pos, isl::pw_aff el) const;
+ inline isl::multi_union_pw_aff set_at(int pos, const isl::union_pw_aff &el) const;
+ inline isl::multi_pw_aff set_pw_aff(int pos, isl::pw_aff el) const;
+ inline isl::multi_pw_aff set_range_tuple(isl::id id) const;
+ inline isl::multi_pw_aff set_range_tuple(const std::string &id) const;
+ inline isl::multi_union_pw_aff set_union_pw_aff(int pos, const isl::union_pw_aff &el) const;
+ inline class size size() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::multi_pw_aff sub(isl::multi_pw_aff multi2) const;
+ inline isl::multi_union_pw_aff sub(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::multi_pw_aff sub(const isl::aff &multi2) const;
+ inline isl::multi_pw_aff sub(const isl::multi_aff &multi2) const;
+ inline isl::multi_pw_aff sub(const isl::pw_aff &multi2) const;
+ inline isl::multi_pw_aff sub(const isl::pw_multi_aff &multi2) const;
+ inline isl::multi_pw_aff unbind_params_insert_domain(isl::multi_id domain) const;
+ inline isl::multi_pw_aff union_add(isl::multi_pw_aff mpa2) const;
+ inline isl::multi_union_pw_aff union_add(const isl::multi_union_pw_aff &mupa2) const;
+ inline isl::multi_pw_aff union_add(const isl::aff &mpa2) const;
+ inline isl::multi_pw_aff union_add(const isl::multi_aff &mpa2) const;
+ inline isl::multi_pw_aff union_add(const isl::pw_aff &mpa2) const;
+ inline isl::multi_pw_aff union_add(const isl::pw_multi_aff &mpa2) const;
+ static inline isl::multi_pw_aff zero(isl::space space);
+};
+
+// declarations for isl::multi_union_pw_aff
+inline multi_union_pw_aff manage(__isl_take isl_multi_union_pw_aff *ptr);
+inline multi_union_pw_aff manage_copy(__isl_keep isl_multi_union_pw_aff *ptr);
+
+class multi_union_pw_aff {
+ friend inline multi_union_pw_aff manage(__isl_take isl_multi_union_pw_aff *ptr);
+ friend inline multi_union_pw_aff manage_copy(__isl_keep isl_multi_union_pw_aff *ptr);
+
+protected:
+ isl_multi_union_pw_aff *ptr = nullptr;
+
+ inline explicit multi_union_pw_aff(__isl_take isl_multi_union_pw_aff *ptr);
+
+public:
+ inline /* implicit */ multi_union_pw_aff();
+ inline /* implicit */ multi_union_pw_aff(const multi_union_pw_aff &obj);
+ inline /* implicit */ multi_union_pw_aff(isl::multi_pw_aff mpa);
+ inline /* implicit */ multi_union_pw_aff(isl::union_pw_aff upa);
+ inline explicit multi_union_pw_aff(isl::space space, isl::union_pw_aff_list list);
+ inline explicit multi_union_pw_aff(isl::union_pw_multi_aff upma);
+ inline explicit multi_union_pw_aff(isl::ctx ctx, const std::string &str);
+ inline multi_union_pw_aff &operator=(multi_union_pw_aff obj);
+ inline ~multi_union_pw_aff();
+ inline __isl_give isl_multi_union_pw_aff *copy() const &;
+ inline __isl_give isl_multi_union_pw_aff *copy() && = delete;
+ inline __isl_keep isl_multi_union_pw_aff *get() const;
+ inline __isl_give isl_multi_union_pw_aff *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::multi_union_pw_aff add(isl::multi_union_pw_aff multi2) const;
+ inline isl::union_pw_aff at(int pos) const;
+ inline isl::union_pw_aff get_at(int pos) const;
+ inline isl::union_set bind(isl::multi_id tuple) const;
+ inline isl::multi_union_pw_aff coalesce() const;
+ inline class size dim(isl::dim type) const;
+ inline isl::union_set domain() const;
+ inline isl::multi_union_pw_aff flat_range_product(isl::multi_union_pw_aff multi2) const;
+ static inline isl::multi_union_pw_aff from_union_map(isl::union_map umap);
+ inline isl::multi_union_pw_aff gist(isl::union_set context) const;
+ inline boolean has_range_tuple_id() const;
+ inline isl::multi_union_pw_aff intersect_domain(isl::union_set uset) const;
+ inline isl::multi_union_pw_aff intersect_params(isl::set params) const;
+ inline boolean involves_nan() const;
+ inline isl::union_pw_aff_list list() const;
+ inline isl::union_pw_aff_list get_list() const;
+ inline isl::multi_union_pw_aff neg() const;
+ inline boolean plain_is_equal(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff pullback(isl::union_pw_multi_aff upma) const;
+ inline isl::multi_union_pw_aff range_product(isl::multi_union_pw_aff multi2) const;
+ inline isl::id range_tuple_id() const;
+ inline isl::id get_range_tuple_id() const;
+ inline isl::multi_union_pw_aff reset_range_tuple_id() const;
+ inline isl::multi_union_pw_aff reset_tuple_id(isl::dim type) const;
+ inline isl::multi_union_pw_aff scale(isl::multi_val mv) const;
+ inline isl::multi_union_pw_aff scale(isl::val v) const;
+ inline isl::multi_union_pw_aff scale(long v) const;
+ inline isl::multi_union_pw_aff scale_down(isl::multi_val mv) const;
+ inline isl::multi_union_pw_aff scale_down(isl::val v) const;
+ inline isl::multi_union_pw_aff scale_down(long v) const;
+ inline isl::multi_union_pw_aff set_at(int pos, isl::union_pw_aff el) const;
+ inline isl::multi_union_pw_aff set_range_tuple(isl::id id) const;
+ inline isl::multi_union_pw_aff set_range_tuple(const std::string &id) const;
+ inline isl::multi_union_pw_aff set_union_pw_aff(int pos, isl::union_pw_aff el) const;
+ inline class size size() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::multi_union_pw_aff sub(isl::multi_union_pw_aff multi2) const;
+ inline isl::multi_union_pw_aff union_add(isl::multi_union_pw_aff mupa2) const;
+ static inline isl::multi_union_pw_aff zero(isl::space space);
+};
+
+// declarations for isl::multi_val
+inline multi_val manage(__isl_take isl_multi_val *ptr);
+inline multi_val manage_copy(__isl_keep isl_multi_val *ptr);
+
+class multi_val {
+ friend inline multi_val manage(__isl_take isl_multi_val *ptr);
+ friend inline multi_val manage_copy(__isl_keep isl_multi_val *ptr);
+
+protected:
+ isl_multi_val *ptr = nullptr;
+
+ inline explicit multi_val(__isl_take isl_multi_val *ptr);
+
+public:
+ inline /* implicit */ multi_val();
+ inline /* implicit */ multi_val(const multi_val &obj);
+ inline explicit multi_val(isl::space space, isl::val_list list);
+ inline explicit multi_val(isl::ctx ctx, const std::string &str);
+ inline multi_val &operator=(multi_val obj);
+ inline ~multi_val();
+ inline __isl_give isl_multi_val *copy() const &;
+ inline __isl_give isl_multi_val *copy() && = delete;
+ inline __isl_keep isl_multi_val *get() const;
+ inline __isl_give isl_multi_val *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::multi_val add(isl::multi_val multi2) const;
+ inline isl::multi_val add(isl::val v) const;
+ inline isl::multi_val add(long v) const;
+ inline isl::val at(int pos) const;
+ inline isl::val get_at(int pos) const;
+ inline class size dim(isl::dim type) const;
+ inline isl::multi_val flat_range_product(isl::multi_val multi2) const;
+ inline boolean has_range_tuple_id() const;
+ inline boolean involves_nan() const;
+ inline isl::val_list list() const;
+ inline isl::val_list get_list() const;
+ inline isl::multi_val max(isl::multi_val multi2) const;
+ inline isl::multi_val min(isl::multi_val multi2) const;
+ inline isl::multi_val neg() const;
+ inline boolean plain_is_equal(const isl::multi_val &multi2) const;
+ inline isl::multi_val product(isl::multi_val multi2) const;
+ inline isl::multi_val range_product(isl::multi_val multi2) const;
+ inline isl::id range_tuple_id() const;
+ inline isl::id get_range_tuple_id() const;
+ inline isl::multi_val reset_range_tuple_id() const;
+ inline isl::multi_val reset_tuple_id(isl::dim type) const;
+ inline isl::multi_val scale(isl::multi_val mv) const;
+ inline isl::multi_val scale(isl::val v) const;
+ inline isl::multi_val scale(long v) const;
+ inline isl::multi_val scale_down(isl::multi_val mv) const;
+ inline isl::multi_val scale_down(isl::val v) const;
+ inline isl::multi_val scale_down(long v) const;
+ inline isl::multi_val set_at(int pos, isl::val el) const;
+ inline isl::multi_val set_at(int pos, long el) const;
+ inline isl::multi_val set_range_tuple(isl::id id) const;
+ inline isl::multi_val set_range_tuple(const std::string &id) const;
+ inline isl::multi_val set_val(int pos, isl::val el) const;
+ inline isl::multi_val set_val(int pos, long el) const;
+ inline class size size() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::multi_val sub(isl::multi_val multi2) const;
+ static inline isl::multi_val zero(isl::space space);
+};
+
+// declarations for isl::point
+inline point manage(__isl_take isl_point *ptr);
+inline point manage_copy(__isl_keep isl_point *ptr);
+
+class point {
+ friend inline point manage(__isl_take isl_point *ptr);
+ friend inline point manage_copy(__isl_keep isl_point *ptr);
+
+protected:
+ isl_point *ptr = nullptr;
+
+ inline explicit point(__isl_take isl_point *ptr);
+
+public:
+ inline /* implicit */ point();
+ inline /* implicit */ point(const point &obj);
+ inline explicit point(isl::space space);
+ inline point &operator=(point obj);
+ inline ~point();
+ inline __isl_give isl_point *copy() const &;
+ inline __isl_give isl_point *copy() && = delete;
+ inline __isl_keep isl_point *get() const;
+ inline __isl_give isl_point *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::set add_constraint(const isl::constraint &constraint) const;
+ inline isl::set add_dims(isl::dim type, unsigned int n) const;
+ inline isl::basic_set affine_hull() const;
+ inline isl::set align_params(const isl::space &model) const;
+ inline isl::basic_set apply(const isl::basic_map &bmap) const;
+ inline isl::set apply(const isl::map &map) const;
+ inline isl::union_set apply(const isl::union_map &umap) const;
+ inline isl::pw_multi_aff as_pw_multi_aff() const;
+ inline isl::set as_set() const;
+ inline isl::basic_set_list basic_set_list() const;
+ inline isl::set bind(const isl::multi_id &tuple) const;
+ inline isl::set coalesce() const;
+ inline isl::set complement() const;
+ inline isl::union_set compute_divs() const;
+ inline boolean contains(const isl::space &space) const;
+ inline isl::basic_set convex_hull() const;
+ inline isl::val coordinate_val(isl::dim type, int pos) const;
+ inline isl::val get_coordinate_val(isl::dim type, int pos) const;
+ inline isl::basic_set detect_equalities() const;
+ inline class size dim(isl::dim type) const;
+ inline boolean dim_has_any_lower_bound(isl::dim type, unsigned int pos) const;
+ inline isl::id dim_id(isl::dim type, unsigned int pos) const;
+ inline isl::pw_aff dim_max(int pos) const;
+ inline isl::val dim_max_val(int pos) const;
+ inline isl::pw_aff dim_min(int pos) const;
+ inline isl::val dim_min_val(int pos) const;
+ inline std::string dim_name(isl::dim type, unsigned int pos) const;
+ inline isl::aff div(int pos) const;
+ inline isl::set drop_constraints_involving_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::set eliminate(isl::dim type, unsigned int first, unsigned int n) const;
+ inline boolean every_set(const std::function<boolean(isl::set)> &test) const;
+ inline isl::set extract_set(const isl::space &space) const;
+ inline int find_dim_by_id(isl::dim type, const isl::id &id) const;
+ inline int find_dim_by_id(isl::dim type, const std::string &id) const;
+ inline isl::basic_set fix_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::basic_set fix_val(isl::dim type, unsigned int pos, const isl::val &v) const;
+ inline isl::basic_set fix_val(isl::dim type, unsigned int pos, long v) const;
+ inline isl::basic_set flatten() const;
+ inline stat foreach_basic_set(const std::function<stat(isl::basic_set)> &fn) const;
+ inline stat foreach_point(const std::function<stat(isl::point)> &fn) const;
+ inline stat foreach_set(const std::function<stat(isl::set)> &fn) const;
+ inline isl::basic_set gist(const isl::basic_set &context) const;
+ inline isl::set gist(const isl::set &context) const;
+ inline isl::union_set gist(const isl::union_set &context) const;
+ inline isl::set gist_params(const isl::set &context) const;
+ inline boolean has_equal_space(const isl::set &set2) const;
+ inline isl::map identity() const;
+ inline isl::union_pw_multi_aff identity_union_pw_multi_aff() const;
+ inline isl::pw_aff indicator_function() const;
+ inline isl::set insert_dims(isl::dim type, unsigned int pos, unsigned int n) const;
+ inline isl::map insert_domain(const isl::space &domain) const;
+ inline isl::basic_set intersect(const isl::basic_set &bset2) const;
+ inline isl::set intersect(const isl::set &set2) const;
+ inline isl::union_set intersect(const isl::union_set &uset2) const;
+ inline isl::basic_set intersect_params(const isl::basic_set &bset2) const;
+ inline isl::set intersect_params(const isl::set &params) const;
+ inline boolean involves_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline boolean involves_locals() const;
+ inline boolean is_bounded() const;
+ inline boolean is_disjoint(const isl::set &set2) const;
+ inline boolean is_disjoint(const isl::union_set &uset2) const;
+ inline boolean is_empty() const;
+ inline boolean is_equal(const isl::basic_set &bset2) const;
+ inline boolean is_equal(const isl::set &set2) const;
+ inline boolean is_equal(const isl::union_set &uset2) const;
+ inline boolean is_params() const;
+ inline boolean is_singleton() const;
+ inline boolean is_strict_subset(const isl::set &set2) const;
+ inline boolean is_strict_subset(const isl::union_set &uset2) const;
+ inline boolean is_subset(const isl::basic_set &bset2) const;
+ inline boolean is_subset(const isl::set &set2) const;
+ inline boolean is_subset(const isl::union_set &uset2) const;
+ inline boolean is_wrapping() const;
+ inline boolean isa_set() const;
+ inline isl::set lexmax() const;
+ inline isl::pw_multi_aff lexmax_pw_multi_aff() const;
+ inline isl::set lexmin() const;
+ inline isl::pw_multi_aff lexmin_pw_multi_aff() const;
+ inline isl::set lower_bound(const isl::multi_pw_aff &lower) const;
+ inline isl::set lower_bound(const isl::multi_val &lower) const;
+ inline isl::set lower_bound_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::set lower_bound_val(isl::dim type, unsigned int pos, const isl::val &value) const;
+ inline isl::set lower_bound_val(isl::dim type, unsigned int pos, long value) const;
+ inline isl::multi_pw_aff max_multi_pw_aff() const;
+ inline isl::val max_val(const isl::aff &obj) const;
+ inline isl::multi_pw_aff min_multi_pw_aff() const;
+ inline isl::val min_val(const isl::aff &obj) const;
+ inline isl::multi_val multi_val() const;
+ inline isl::multi_val get_multi_val() const;
+ inline class size n_basic_set() const;
+ inline isl::basic_set params() const;
+ inline isl::val plain_get_val_if_fixed(isl::dim type, unsigned int pos) const;
+ inline isl::multi_val plain_multi_val_if_fixed() const;
+ inline isl::basic_set polyhedral_hull() const;
+ inline isl::set preimage(const isl::multi_aff &ma) const;
+ inline isl::set preimage(const isl::multi_pw_aff &mpa) const;
+ inline isl::set preimage(const isl::pw_multi_aff &pma) const;
+ inline isl::union_set preimage(const isl::union_pw_multi_aff &upma) const;
+ inline isl::set product(const isl::set &set2) const;
+ inline isl::basic_set project_out(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::set project_out_all_params() const;
+ inline isl::set project_out_param(const isl::id &id) const;
+ inline isl::set project_out_param(const std::string &id) const;
+ inline isl::set project_out_param(const isl::id_list &list) const;
+ inline isl::pw_multi_aff pw_multi_aff_on_domain(const isl::multi_val &mv) const;
+ inline isl::set remove_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::set remove_divs() const;
+ inline isl::set remove_redundancies() const;
+ inline isl::set reset_tuple_id() const;
+ inline isl::basic_set sample() const;
+ inline isl::point sample_point() const;
+ inline isl::set set_dim_id(isl::dim type, unsigned int pos, const isl::id &id) const;
+ inline isl::set set_dim_id(isl::dim type, unsigned int pos, const std::string &id) const;
+ inline isl::set_list set_list() const;
+ inline isl::set set_tuple_id(const isl::id &id) const;
+ inline isl::set set_tuple_id(const std::string &id) const;
+ inline isl::fixed_box simple_fixed_box_hull() const;
+ inline isl::basic_set simple_hull() const;
+ inline isl::space space() const;
+ inline isl::val stride(int pos) const;
+ inline isl::set subtract(const isl::set &set2) const;
+ inline isl::union_set subtract(const isl::union_set &uset2) const;
+ inline isl::basic_set_list to_list() const;
+ inline isl::set to_set() const;
+ inline isl::union_set to_union_set() const;
+ inline isl::map translation() const;
+ inline class size tuple_dim() const;
+ inline isl::id tuple_id() const;
+ inline std::string tuple_name() const;
+ inline isl::set unbind_params(const isl::multi_id &tuple) const;
+ inline isl::map unbind_params_insert_domain(const isl::multi_id &domain) const;
+ inline isl::set unite(const isl::basic_set &bset2) const;
+ inline isl::set unite(const isl::set &set2) const;
+ inline isl::union_set unite(const isl::union_set &uset2) const;
+ inline isl::basic_set unshifted_simple_hull() const;
+ inline isl::map unwrap() const;
+ inline isl::set upper_bound(const isl::multi_pw_aff &upper) const;
+ inline isl::set upper_bound(const isl::multi_val &upper) const;
+ inline isl::set upper_bound_val(isl::dim type, unsigned int pos, const isl::val &value) const;
+ inline isl::set upper_bound_val(isl::dim type, unsigned int pos, long value) const;
+};
+
+// declarations for isl::pw_aff
+inline pw_aff manage(__isl_take isl_pw_aff *ptr);
+inline pw_aff manage_copy(__isl_keep isl_pw_aff *ptr);
+
+class pw_aff {
+ friend inline pw_aff manage(__isl_take isl_pw_aff *ptr);
+ friend inline pw_aff manage_copy(__isl_keep isl_pw_aff *ptr);
+
+protected:
+ isl_pw_aff *ptr = nullptr;
+
+ inline explicit pw_aff(__isl_take isl_pw_aff *ptr);
+
+public:
+ inline /* implicit */ pw_aff();
+ inline /* implicit */ pw_aff(const pw_aff &obj);
+ inline /* implicit */ pw_aff(isl::aff aff);
+ inline explicit pw_aff(isl::ctx ctx, const std::string &str);
+ inline explicit pw_aff(isl::set domain, isl::val v);
+ inline explicit pw_aff(isl::local_space ls);
+ inline pw_aff &operator=(pw_aff obj);
+ inline ~pw_aff();
+ inline __isl_give isl_pw_aff *copy() const &;
+ inline __isl_give isl_pw_aff *copy() && = delete;
+ inline __isl_keep isl_pw_aff *get() const;
+ inline __isl_give isl_pw_aff *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::multi_pw_aff add(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff add(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_aff add(isl::pw_aff pwaff2) const;
+ inline isl::pw_multi_aff add(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_aff add(const isl::union_pw_aff &upa2) const;
+ inline isl::union_pw_multi_aff add(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_aff add(const isl::aff &pwaff2) const;
+ inline isl::pw_aff add_constant(isl::val v) const;
+ inline isl::pw_aff add_constant(long v) const;
+ inline isl::pw_multi_aff add_constant(const isl::multi_val &mv) const;
+ inline isl::pw_aff add_dims(isl::dim type, unsigned int n) const;
+ inline isl::union_pw_multi_aff add_pw_multi_aff(const isl::pw_multi_aff &pma) const;
+ inline isl::union_pw_multi_aff apply(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::aff as_aff() const;
+ inline isl::map as_map() const;
+ inline isl::multi_aff as_multi_aff() const;
+ inline isl::multi_union_pw_aff as_multi_union_pw_aff() const;
+ inline isl::pw_multi_aff as_pw_multi_aff() const;
+ inline isl::set as_set() const;
+ inline isl::union_map as_union_map() const;
+ inline isl::pw_aff at(int pos) const;
+ inline isl::set bind(const isl::multi_id &tuple) const;
+ inline isl::set bind(isl::id id) const;
+ inline isl::set bind(const std::string &id) const;
+ inline isl::pw_aff bind_domain(isl::multi_id tuple) const;
+ inline isl::pw_aff bind_domain_wrapped_domain(isl::multi_id tuple) const;
+ inline isl::pw_aff ceil() const;
+ inline isl::pw_aff coalesce() const;
+ inline isl::pw_aff cond(isl::pw_aff pwaff_true, isl::pw_aff pwaff_false) const;
+ inline class size dim(isl::dim type) const;
+ inline isl::id dim_id(isl::dim type, unsigned int pos) const;
+ inline isl::id get_dim_id(isl::dim type, unsigned int pos) const;
+ inline isl::pw_aff div(isl::pw_aff pa2) const;
+ inline isl::set domain() const;
+ inline isl::space domain_space() const;
+ inline isl::space get_domain_space() const;
+ inline isl::pw_multi_aff drop_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::set eq_set(isl::pw_aff pwaff2) const;
+ inline isl::val eval(isl::point pnt) const;
+ inline isl::pw_multi_aff extract_pw_multi_aff(const isl::space &space) const;
+ inline isl::multi_pw_aff flat_range_product(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff flat_range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff flat_range_product(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff flat_range_product(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_aff floor() const;
+ inline stat foreach_piece(const std::function<stat(isl::set, isl::aff)> &fn) const;
+ inline stat foreach_piece(const std::function<stat(isl::set, isl::multi_aff)> &fn) const;
+ inline stat foreach_pw_aff(const std::function<stat(isl::pw_aff)> &fn) const;
+ inline isl::set ge_set(isl::pw_aff pwaff2) const;
+ inline isl::pw_aff gist(isl::set context) const;
+ inline isl::union_pw_aff gist(const isl::union_set &context) const;
+ inline isl::pw_aff gist(const isl::basic_set &context) const;
+ inline isl::pw_aff gist(const isl::point &context) const;
+ inline isl::set gt_set(isl::pw_aff pwaff2) const;
+ inline boolean has_range_tuple_id() const;
+ inline isl::multi_pw_aff identity() const;
+ inline isl::pw_aff insert_domain(isl::space domain) const;
+ inline isl::pw_aff intersect_domain(isl::set set) const;
+ inline isl::union_pw_aff intersect_domain(const isl::space &space) const;
+ inline isl::union_pw_aff intersect_domain(const isl::union_set &uset) const;
+ inline isl::pw_aff intersect_domain(const isl::basic_set &set) const;
+ inline isl::pw_aff intersect_domain(const isl::point &set) const;
+ inline isl::union_pw_aff intersect_domain_wrapped_domain(const isl::union_set &uset) const;
+ inline isl::union_pw_aff intersect_domain_wrapped_range(const isl::union_set &uset) const;
+ inline isl::pw_aff intersect_params(isl::set set) const;
+ inline boolean involves_locals() const;
+ inline boolean involves_nan() const;
+ inline boolean involves_param(const isl::id &id) const;
+ inline boolean involves_param(const std::string &id) const;
+ inline boolean involves_param(const isl::id_list &list) const;
+ inline boolean is_cst() const;
+ inline boolean is_equal(const isl::pw_aff &pa2) const;
+ inline boolean isa_aff() const;
+ inline boolean isa_multi_aff() const;
+ inline boolean isa_pw_multi_aff() const;
+ inline isl::set le_set(isl::pw_aff pwaff2) const;
+ inline isl::pw_aff_list list() const;
+ inline isl::set lt_set(isl::pw_aff pwaff2) const;
+ inline isl::multi_pw_aff max(const isl::multi_pw_aff &multi2) const;
+ inline isl::pw_aff max(isl::pw_aff pwaff2) const;
+ inline isl::pw_aff max(const isl::aff &pwaff2) const;
+ inline isl::multi_val max_multi_val() const;
+ inline isl::multi_pw_aff min(const isl::multi_pw_aff &multi2) const;
+ inline isl::pw_aff min(isl::pw_aff pwaff2) const;
+ inline isl::pw_aff min(const isl::aff &pwaff2) const;
+ inline isl::multi_val min_multi_val() const;
+ inline isl::pw_aff mod(isl::val mod) const;
+ inline isl::pw_aff mod(long mod) const;
+ inline isl::pw_aff mul(isl::pw_aff pwaff2) const;
+ inline class size n_piece() const;
+ inline isl::set ne_set(isl::pw_aff pwaff2) const;
+ inline isl::pw_aff neg() const;
+ static inline isl::pw_aff param_on_domain(isl::set domain, isl::id id);
+ inline boolean plain_is_empty() const;
+ inline boolean plain_is_equal(const isl::multi_pw_aff &multi2) const;
+ inline boolean plain_is_equal(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff preimage_domain_wrapped_domain(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff preimage_domain_wrapped_domain(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::multi_pw_aff product(const isl::multi_pw_aff &multi2) const;
+ inline isl::pw_multi_aff product(const isl::pw_multi_aff &pma2) const;
+ inline isl::pw_aff pullback(isl::multi_aff ma) const;
+ inline isl::pw_aff pullback(isl::multi_pw_aff mpa) const;
+ inline isl::pw_aff pullback(isl::pw_multi_aff pma) const;
+ inline isl::union_pw_aff pullback(const isl::union_pw_multi_aff &upma) const;
+ inline isl::pw_multi_aff_list pw_multi_aff_list() const;
+ inline isl::pw_multi_aff range_factor_domain() const;
+ inline isl::pw_multi_aff range_factor_range() const;
+ inline isl::multi_pw_aff range_product(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff range_product(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_multi_aff range_product(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::id range_tuple_id() const;
+ inline isl::multi_pw_aff reset_range_tuple_id() const;
+ inline isl::multi_pw_aff reset_tuple_id(isl::dim type) const;
+ inline isl::multi_pw_aff scale(const isl::multi_val &mv) const;
+ inline isl::pw_aff scale(isl::val v) const;
+ inline isl::pw_aff scale(long v) const;
+ inline isl::multi_pw_aff scale_down(const isl::multi_val &mv) const;
+ inline isl::pw_aff scale_down(isl::val f) const;
+ inline isl::pw_aff scale_down(long f) const;
+ inline isl::multi_pw_aff set_at(int pos, const isl::pw_aff &el) const;
+ inline isl::multi_union_pw_aff set_at(int pos, const isl::union_pw_aff &el) const;
+ inline isl::multi_pw_aff set_pw_aff(int pos, const isl::pw_aff &el) const;
+ inline isl::pw_multi_aff set_pw_aff(unsigned int pos, const isl::pw_aff &pa) const;
+ inline isl::pw_multi_aff set_range_tuple(const isl::id &id) const;
+ inline isl::pw_multi_aff set_range_tuple(const std::string &id) const;
+ inline isl::pw_aff set_tuple_id(isl::dim type, isl::id id) const;
+ inline isl::pw_aff set_tuple_id(isl::dim type, const std::string &id) const;
+ inline isl::multi_union_pw_aff set_union_pw_aff(int pos, const isl::union_pw_aff &el) const;
+ inline class size size() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::multi_pw_aff sub(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff sub(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_aff sub(isl::pw_aff pwaff2) const;
+ inline isl::pw_multi_aff sub(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_aff sub(const isl::union_pw_aff &upa2) const;
+ inline isl::union_pw_multi_aff sub(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_aff sub(const isl::aff &pwaff2) const;
+ inline isl::pw_aff subtract_domain(isl::set set) const;
+ inline isl::union_pw_aff subtract_domain(const isl::space &space) const;
+ inline isl::union_pw_aff subtract_domain(const isl::union_set &uset) const;
+ inline isl::pw_aff subtract_domain(const isl::basic_set &set) const;
+ inline isl::pw_aff subtract_domain(const isl::point &set) const;
+ inline isl::pw_aff tdiv_q(isl::pw_aff pa2) const;
+ inline isl::pw_aff tdiv_r(isl::pw_aff pa2) const;
+ inline isl::pw_aff_list to_list() const;
+ inline isl::multi_pw_aff to_multi_pw_aff() const;
+ inline isl::union_pw_aff to_union_pw_aff() const;
+ inline isl::union_pw_multi_aff to_union_pw_multi_aff() const;
+ inline isl::id tuple_id(isl::dim type) const;
+ inline isl::id get_tuple_id(isl::dim type) const;
+ inline isl::multi_pw_aff unbind_params_insert_domain(const isl::multi_id &domain) const;
+ inline isl::multi_pw_aff union_add(const isl::multi_pw_aff &mpa2) const;
+ inline isl::multi_union_pw_aff union_add(const isl::multi_union_pw_aff &mupa2) const;
+ inline isl::pw_aff union_add(isl::pw_aff pwaff2) const;
+ inline isl::pw_multi_aff union_add(const isl::pw_multi_aff &pma2) const;
+ inline isl::union_pw_aff union_add(const isl::union_pw_aff &upa2) const;
+ inline isl::union_pw_multi_aff union_add(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_aff union_add(const isl::aff &pwaff2) const;
+ static inline isl::pw_aff var_on_domain(isl::local_space ls, isl::dim type, unsigned int pos);
+};
+
+// declarations for isl::pw_aff_list
+inline pw_aff_list manage(__isl_take isl_pw_aff_list *ptr);
+inline pw_aff_list manage_copy(__isl_keep isl_pw_aff_list *ptr);
+
+class pw_aff_list {
+ friend inline pw_aff_list manage(__isl_take isl_pw_aff_list *ptr);
+ friend inline pw_aff_list manage_copy(__isl_keep isl_pw_aff_list *ptr);
+
+protected:
+ isl_pw_aff_list *ptr = nullptr;
+
+ inline explicit pw_aff_list(__isl_take isl_pw_aff_list *ptr);
+
+public:
+ inline /* implicit */ pw_aff_list();
+ inline /* implicit */ pw_aff_list(const pw_aff_list &obj);
+ inline explicit pw_aff_list(isl::ctx ctx, int n);
+ inline explicit pw_aff_list(isl::pw_aff el);
+ inline explicit pw_aff_list(isl::ctx ctx, const std::string &str);
+ inline pw_aff_list &operator=(pw_aff_list obj);
+ inline ~pw_aff_list();
+ inline __isl_give isl_pw_aff_list *copy() const &;
+ inline __isl_give isl_pw_aff_list *copy() && = delete;
+ inline __isl_keep isl_pw_aff_list *get() const;
+ inline __isl_give isl_pw_aff_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::pw_aff_list add(isl::pw_aff el) const;
+ inline isl::pw_aff at(int index) const;
+ inline isl::pw_aff get_at(int index) const;
+ inline isl::pw_aff_list clear() const;
+ inline isl::pw_aff_list concat(isl::pw_aff_list list2) const;
+ inline isl::pw_aff_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::pw_aff)> &fn) const;
+ inline isl::pw_aff_list insert(unsigned int pos, isl::pw_aff el) const;
+ inline class size size() const;
+};
+
+// declarations for isl::pw_multi_aff
+inline pw_multi_aff manage(__isl_take isl_pw_multi_aff *ptr);
+inline pw_multi_aff manage_copy(__isl_keep isl_pw_multi_aff *ptr);
+
+class pw_multi_aff {
+ friend inline pw_multi_aff manage(__isl_take isl_pw_multi_aff *ptr);
+ friend inline pw_multi_aff manage_copy(__isl_keep isl_pw_multi_aff *ptr);
+
+protected:
+ isl_pw_multi_aff *ptr = nullptr;
+
+ inline explicit pw_multi_aff(__isl_take isl_pw_multi_aff *ptr);
+
+public:
+ inline /* implicit */ pw_multi_aff();
+ inline /* implicit */ pw_multi_aff(const pw_multi_aff &obj);
+ inline /* implicit */ pw_multi_aff(isl::multi_aff ma);
+ inline /* implicit */ pw_multi_aff(isl::pw_aff pa);
+ inline explicit pw_multi_aff(isl::ctx ctx, const std::string &str);
+ inline pw_multi_aff &operator=(pw_multi_aff obj);
+ inline ~pw_multi_aff();
+ inline __isl_give isl_pw_multi_aff *copy() const &;
+ inline __isl_give isl_pw_multi_aff *copy() && = delete;
+ inline __isl_keep isl_pw_multi_aff *get() const;
+ inline __isl_give isl_pw_multi_aff *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::multi_pw_aff add(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff add(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff add(isl::pw_multi_aff pma2) const;
+ inline isl::union_pw_multi_aff add(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_multi_aff add(const isl::multi_aff &pma2) const;
+ inline isl::pw_multi_aff add(const isl::pw_aff &pma2) const;
+ inline isl::pw_multi_aff add_constant(isl::multi_val mv) const;
+ inline isl::pw_multi_aff add_constant(isl::val v) const;
+ inline isl::pw_multi_aff add_constant(long v) const;
+ inline isl::union_pw_multi_aff add_pw_multi_aff(const isl::pw_multi_aff &pma) const;
+ inline isl::union_pw_multi_aff apply(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::map as_map() const;
+ inline isl::multi_aff as_multi_aff() const;
+ inline isl::multi_union_pw_aff as_multi_union_pw_aff() const;
+ inline isl::pw_multi_aff as_pw_multi_aff() const;
+ inline isl::set as_set() const;
+ inline isl::union_map as_union_map() const;
+ inline isl::pw_aff at(int pos) const;
+ inline isl::pw_aff get_at(int pos) const;
+ inline isl::set bind(const isl::multi_id &tuple) const;
+ inline isl::pw_multi_aff bind_domain(isl::multi_id tuple) const;
+ inline isl::pw_multi_aff bind_domain_wrapped_domain(isl::multi_id tuple) const;
+ inline isl::pw_multi_aff coalesce() const;
+ inline class size dim(isl::dim type) const;
+ inline isl::set domain() const;
+ static inline isl::pw_multi_aff domain_map(isl::space space);
+ inline isl::pw_multi_aff drop_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::pw_multi_aff extract_pw_multi_aff(const isl::space &space) const;
+ inline isl::multi_pw_aff flat_range_product(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff flat_range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff flat_range_product(isl::pw_multi_aff pma2) const;
+ inline isl::union_pw_multi_aff flat_range_product(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_multi_aff flat_range_product(const isl::multi_aff &pma2) const;
+ inline isl::pw_multi_aff flat_range_product(const isl::pw_aff &pma2) const;
+ inline stat foreach_piece(const std::function<stat(isl::set, isl::multi_aff)> &fn) const;
+ static inline isl::pw_multi_aff from_map(isl::map map);
+ inline isl::pw_multi_aff gist(isl::set set) const;
+ inline isl::union_pw_multi_aff gist(const isl::union_set &context) const;
+ inline isl::pw_multi_aff gist(const isl::basic_set &set) const;
+ inline isl::pw_multi_aff gist(const isl::point &set) const;
+ inline boolean has_range_tuple_id() const;
+ inline isl::multi_pw_aff identity() const;
+ static inline isl::pw_multi_aff identity_on_domain(isl::space space);
+ inline isl::pw_multi_aff insert_domain(isl::space domain) const;
+ inline isl::pw_multi_aff intersect_domain(isl::set set) const;
+ inline isl::union_pw_multi_aff intersect_domain(const isl::space &space) const;
+ inline isl::union_pw_multi_aff intersect_domain(const isl::union_set &uset) const;
+ inline isl::pw_multi_aff intersect_domain(const isl::basic_set &set) const;
+ inline isl::pw_multi_aff intersect_domain(const isl::point &set) const;
+ inline isl::union_pw_multi_aff intersect_domain_wrapped_domain(const isl::union_set &uset) const;
+ inline isl::union_pw_multi_aff intersect_domain_wrapped_range(const isl::union_set &uset) const;
+ inline isl::pw_multi_aff intersect_params(isl::set set) const;
+ inline boolean involves_locals() const;
+ inline boolean involves_nan() const;
+ inline boolean involves_param(const isl::id &id) const;
+ inline boolean involves_param(const std::string &id) const;
+ inline boolean involves_param(const isl::id_list &list) const;
+ inline boolean isa_multi_aff() const;
+ inline boolean isa_pw_multi_aff() const;
+ inline isl::pw_aff_list list() const;
+ inline isl::multi_pw_aff max(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_val max_multi_val() const;
+ inline isl::multi_pw_aff min(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_val min_multi_val() const;
+ static inline isl::pw_multi_aff multi_val_on_domain(isl::set domain, isl::multi_val mv);
+ inline class size n_piece() const;
+ inline isl::multi_pw_aff neg() const;
+ inline boolean plain_is_empty() const;
+ inline boolean plain_is_equal(const isl::multi_pw_aff &multi2) const;
+ inline boolean plain_is_equal(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff preimage_domain_wrapped_domain(isl::pw_multi_aff pma2) const;
+ inline isl::union_pw_multi_aff preimage_domain_wrapped_domain(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_multi_aff preimage_domain_wrapped_domain(const isl::multi_aff &pma2) const;
+ inline isl::pw_multi_aff preimage_domain_wrapped_domain(const isl::pw_aff &pma2) const;
+ inline isl::multi_pw_aff product(const isl::multi_pw_aff &multi2) const;
+ inline isl::pw_multi_aff product(isl::pw_multi_aff pma2) const;
+ inline isl::pw_multi_aff product(const isl::multi_aff &pma2) const;
+ inline isl::pw_multi_aff product(const isl::pw_aff &pma2) const;
+ static inline isl::pw_multi_aff project_out_map(isl::space space, isl::dim type, unsigned int first, unsigned int n);
+ inline isl::multi_pw_aff pullback(const isl::multi_pw_aff &mpa2) const;
+ inline isl::pw_multi_aff pullback(isl::multi_aff ma) const;
+ inline isl::pw_multi_aff pullback(isl::pw_multi_aff pma2) const;
+ inline isl::union_pw_multi_aff pullback(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_multi_aff_list pw_multi_aff_list() const;
+ inline isl::pw_multi_aff range_factor_domain() const;
+ inline isl::pw_multi_aff range_factor_range() const;
+ static inline isl::pw_multi_aff range_map(isl::space space);
+ inline isl::multi_pw_aff range_product(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff range_product(isl::pw_multi_aff pma2) const;
+ inline isl::union_pw_multi_aff range_product(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_multi_aff range_product(const isl::multi_aff &pma2) const;
+ inline isl::pw_multi_aff range_product(const isl::pw_aff &pma2) const;
+ inline isl::id range_tuple_id() const;
+ inline isl::id get_range_tuple_id() const;
+ inline isl::multi_pw_aff reset_range_tuple_id() const;
+ inline isl::multi_pw_aff reset_tuple_id(isl::dim type) const;
+ inline isl::multi_pw_aff scale(const isl::multi_val &mv) const;
+ inline isl::pw_multi_aff scale(isl::val v) const;
+ inline isl::pw_multi_aff scale(long v) const;
+ inline isl::multi_pw_aff scale_down(const isl::multi_val &mv) const;
+ inline isl::pw_multi_aff scale_down(isl::val v) const;
+ inline isl::pw_multi_aff scale_down(long v) const;
+ inline isl::multi_pw_aff set_at(int pos, const isl::pw_aff &el) const;
+ inline isl::multi_union_pw_aff set_at(int pos, const isl::union_pw_aff &el) const;
+ inline isl::multi_pw_aff set_pw_aff(int pos, const isl::pw_aff &el) const;
+ inline isl::pw_multi_aff set_pw_aff(unsigned int pos, isl::pw_aff pa) const;
+ inline isl::pw_multi_aff set_range_tuple(isl::id id) const;
+ inline isl::pw_multi_aff set_range_tuple(const std::string &id) const;
+ inline isl::multi_union_pw_aff set_union_pw_aff(int pos, const isl::union_pw_aff &el) const;
+ inline class size size() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::multi_pw_aff sub(const isl::multi_pw_aff &multi2) const;
+ inline isl::multi_union_pw_aff sub(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::pw_multi_aff sub(isl::pw_multi_aff pma2) const;
+ inline isl::union_pw_multi_aff sub(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_multi_aff sub(const isl::multi_aff &pma2) const;
+ inline isl::pw_multi_aff sub(const isl::pw_aff &pma2) const;
+ inline isl::pw_multi_aff subtract_domain(isl::set set) const;
+ inline isl::union_pw_multi_aff subtract_domain(const isl::space &space) const;
+ inline isl::union_pw_multi_aff subtract_domain(const isl::union_set &uset) const;
+ inline isl::pw_multi_aff subtract_domain(const isl::basic_set &set) const;
+ inline isl::pw_multi_aff subtract_domain(const isl::point &set) const;
+ inline isl::pw_multi_aff_list to_list() const;
+ inline isl::multi_pw_aff to_multi_pw_aff() const;
+ inline isl::union_pw_multi_aff to_union_pw_multi_aff() const;
+ inline isl::id tuple_id(isl::dim type) const;
+ inline isl::id get_tuple_id(isl::dim type) const;
+ inline isl::multi_pw_aff unbind_params_insert_domain(const isl::multi_id &domain) const;
+ inline isl::multi_pw_aff union_add(const isl::multi_pw_aff &mpa2) const;
+ inline isl::multi_union_pw_aff union_add(const isl::multi_union_pw_aff &mupa2) const;
+ inline isl::pw_multi_aff union_add(isl::pw_multi_aff pma2) const;
+ inline isl::union_pw_multi_aff union_add(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::pw_multi_aff union_add(const isl::multi_aff &pma2) const;
+ inline isl::pw_multi_aff union_add(const isl::pw_aff &pma2) const;
+ static inline isl::pw_multi_aff zero(isl::space space);
+};
+
+// declarations for isl::pw_multi_aff_list
+inline pw_multi_aff_list manage(__isl_take isl_pw_multi_aff_list *ptr);
+inline pw_multi_aff_list manage_copy(__isl_keep isl_pw_multi_aff_list *ptr);
+
+class pw_multi_aff_list {
+ friend inline pw_multi_aff_list manage(__isl_take isl_pw_multi_aff_list *ptr);
+ friend inline pw_multi_aff_list manage_copy(__isl_keep isl_pw_multi_aff_list *ptr);
+
+protected:
+ isl_pw_multi_aff_list *ptr = nullptr;
+
+ inline explicit pw_multi_aff_list(__isl_take isl_pw_multi_aff_list *ptr);
+
+public:
+ inline /* implicit */ pw_multi_aff_list();
+ inline /* implicit */ pw_multi_aff_list(const pw_multi_aff_list &obj);
+ inline explicit pw_multi_aff_list(isl::ctx ctx, int n);
+ inline explicit pw_multi_aff_list(isl::pw_multi_aff el);
+ inline explicit pw_multi_aff_list(isl::ctx ctx, const std::string &str);
+ inline pw_multi_aff_list &operator=(pw_multi_aff_list obj);
+ inline ~pw_multi_aff_list();
+ inline __isl_give isl_pw_multi_aff_list *copy() const &;
+ inline __isl_give isl_pw_multi_aff_list *copy() && = delete;
+ inline __isl_keep isl_pw_multi_aff_list *get() const;
+ inline __isl_give isl_pw_multi_aff_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::pw_multi_aff_list add(isl::pw_multi_aff el) const;
+ inline isl::pw_multi_aff at(int index) const;
+ inline isl::pw_multi_aff get_at(int index) const;
+ inline isl::pw_multi_aff_list clear() const;
+ inline isl::pw_multi_aff_list concat(isl::pw_multi_aff_list list2) const;
+ inline isl::pw_multi_aff_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::pw_multi_aff)> &fn) const;
+ inline isl::pw_multi_aff_list insert(unsigned int pos, isl::pw_multi_aff el) const;
+ inline class size size() const;
+};
+
+// declarations for isl::schedule
+inline schedule manage(__isl_take isl_schedule *ptr);
+inline schedule manage_copy(__isl_keep isl_schedule *ptr);
+
+class schedule {
+ friend inline schedule manage(__isl_take isl_schedule *ptr);
+ friend inline schedule manage_copy(__isl_keep isl_schedule *ptr);
+
+protected:
+ isl_schedule *ptr = nullptr;
+
+ inline explicit schedule(__isl_take isl_schedule *ptr);
+
+public:
+ inline /* implicit */ schedule();
+ inline /* implicit */ schedule(const schedule &obj);
+ inline explicit schedule(isl::ctx ctx, const std::string &str);
+ inline schedule &operator=(schedule obj);
+ inline ~schedule();
+ inline __isl_give isl_schedule *copy() const &;
+ inline __isl_give isl_schedule *copy() && = delete;
+ inline __isl_keep isl_schedule *get() const;
+ inline __isl_give isl_schedule *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::schedule align_params(isl::space space) const;
+ inline isl::union_set domain() const;
+ inline isl::union_set get_domain() const;
+ static inline isl::schedule from_domain(isl::union_set domain);
+ inline isl::schedule gist_domain_params(isl::set context) const;
+ inline isl::schedule insert_partial_schedule(isl::multi_union_pw_aff partial) const;
+ inline isl::schedule intersect_domain(isl::union_set domain) const;
+ inline isl::union_map map() const;
+ inline isl::union_map get_map() const;
+ inline isl::schedule pullback(isl::union_pw_multi_aff upma) const;
+ inline isl::schedule_node root() const;
+ inline isl::schedule_node get_root() const;
+ inline isl::schedule sequence(isl::schedule schedule2) const;
+};
+
+// declarations for isl::schedule_constraints
+inline schedule_constraints manage(__isl_take isl_schedule_constraints *ptr);
+inline schedule_constraints manage_copy(__isl_keep isl_schedule_constraints *ptr);
+
+class schedule_constraints {
+ friend inline schedule_constraints manage(__isl_take isl_schedule_constraints *ptr);
+ friend inline schedule_constraints manage_copy(__isl_keep isl_schedule_constraints *ptr);
+
+protected:
+ isl_schedule_constraints *ptr = nullptr;
+
+ inline explicit schedule_constraints(__isl_take isl_schedule_constraints *ptr);
+
+public:
+ inline /* implicit */ schedule_constraints();
+ inline /* implicit */ schedule_constraints(const schedule_constraints &obj);
+ inline explicit schedule_constraints(isl::ctx ctx, const std::string &str);
+ inline schedule_constraints &operator=(schedule_constraints obj);
+ inline ~schedule_constraints();
+ inline __isl_give isl_schedule_constraints *copy() const &;
+ inline __isl_give isl_schedule_constraints *copy() && = delete;
+ inline __isl_keep isl_schedule_constraints *get() const;
+ inline __isl_give isl_schedule_constraints *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::union_map coincidence() const;
+ inline isl::union_map get_coincidence() const;
+ inline isl::schedule compute_schedule() const;
+ inline isl::union_map conditional_validity() const;
+ inline isl::union_map get_conditional_validity() const;
+ inline isl::union_map conditional_validity_condition() const;
+ inline isl::union_map get_conditional_validity_condition() const;
+ inline isl::set context() const;
+ inline isl::set get_context() const;
+ inline isl::union_set domain() const;
+ inline isl::union_set get_domain() const;
+ static inline isl::schedule_constraints on_domain(isl::union_set domain);
+ inline isl::union_map proximity() const;
+ inline isl::union_map get_proximity() const;
+ inline isl::schedule_constraints set_coincidence(isl::union_map coincidence) const;
+ inline isl::schedule_constraints set_conditional_validity(isl::union_map condition, isl::union_map validity) const;
+ inline isl::schedule_constraints set_context(isl::set context) const;
+ inline isl::schedule_constraints set_proximity(isl::union_map proximity) const;
+ inline isl::schedule_constraints set_validity(isl::union_map validity) const;
+ inline isl::union_map validity() const;
+ inline isl::union_map get_validity() const;
+};
+
+// declarations for isl::schedule_node
+inline schedule_node manage(__isl_take isl_schedule_node *ptr);
+inline schedule_node manage_copy(__isl_keep isl_schedule_node *ptr);
+
+class schedule_node {
+ friend inline schedule_node manage(__isl_take isl_schedule_node *ptr);
+ friend inline schedule_node manage_copy(__isl_keep isl_schedule_node *ptr);
+
+protected:
+ isl_schedule_node *ptr = nullptr;
+
+ inline explicit schedule_node(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node();
+ inline /* implicit */ schedule_node(const schedule_node &obj);
+ inline schedule_node &operator=(schedule_node obj);
+ inline ~schedule_node();
+ inline __isl_give isl_schedule_node *copy() const &;
+ inline __isl_give isl_schedule_node *copy() && = delete;
+ inline __isl_keep isl_schedule_node *get() const;
+ inline __isl_give isl_schedule_node *release();
+ inline bool is_null() const;
+private:
+ template <typename T,
+ typename = typename std::enable_if<std::is_same<
+ const decltype(isl_schedule_node_get_type(nullptr)),
+ const T>::value>::type>
+ inline boolean isa_type(T subtype) const;
+public:
+ template <class T> inline boolean isa() const;
+ template <class T> inline T as() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::schedule_node ancestor(int generation) const;
+ inline class size ancestor_child_position(const isl::schedule_node &ancestor) const;
+ inline class size get_ancestor_child_position(const isl::schedule_node &ancestor) const;
+ inline isl::schedule_node child(int pos) const;
+ inline class size child_position() const;
+ inline class size get_child_position() const;
+ inline isl::union_set domain() const;
+ inline isl::union_set get_domain() const;
+ inline boolean every_descendant(const std::function<boolean(isl::schedule_node)> &test) const;
+ inline isl::schedule_node first_child() const;
+ inline stat foreach_ancestor_top_down(const std::function<stat(isl::schedule_node)> &fn) const;
+ inline stat foreach_descendant_top_down(const std::function<boolean(isl::schedule_node)> &fn) const;
+ static inline isl::schedule_node from_domain(isl::union_set domain);
+ static inline isl::schedule_node from_extension(isl::union_map extension);
+ inline isl::schedule_node graft_after(isl::schedule_node graft) const;
+ inline isl::schedule_node graft_before(isl::schedule_node graft) const;
+ inline boolean has_children() const;
+ inline boolean has_next_sibling() const;
+ inline boolean has_parent() const;
+ inline boolean has_previous_sibling() const;
+ inline isl::schedule_node insert_context(isl::set context) const;
+ inline isl::schedule_node insert_filter(isl::union_set filter) const;
+ inline isl::schedule_node insert_guard(isl::set context) const;
+ inline isl::schedule_node insert_mark(isl::id mark) const;
+ inline isl::schedule_node insert_mark(const std::string &mark) const;
+ inline isl::schedule_node insert_partial_schedule(isl::multi_union_pw_aff schedule) const;
+ inline isl::schedule_node insert_sequence(isl::union_set_list filters) const;
+ inline isl::schedule_node insert_set(isl::union_set_list filters) const;
+ inline boolean is_equal(const isl::schedule_node &node2) const;
+ inline boolean is_subtree_anchored() const;
+ inline isl::schedule_node map_descendant_bottom_up(const std::function<isl::schedule_node(isl::schedule_node)> &fn) const;
+ inline class size n_children() const;
+ inline isl::schedule_node next_sibling() const;
+ inline isl::schedule_node order_after(isl::union_set filter) const;
+ inline isl::schedule_node order_before(isl::union_set filter) const;
+ inline isl::schedule_node parent() const;
+ inline isl::multi_union_pw_aff prefix_schedule_multi_union_pw_aff() const;
+ inline isl::multi_union_pw_aff get_prefix_schedule_multi_union_pw_aff() const;
+ inline isl::union_map prefix_schedule_relation() const;
+ inline isl::union_map get_prefix_schedule_relation() const;
+ inline isl::union_map prefix_schedule_union_map() const;
+ inline isl::union_map get_prefix_schedule_union_map() const;
+ inline isl::union_pw_multi_aff prefix_schedule_union_pw_multi_aff() const;
+ inline isl::union_pw_multi_aff get_prefix_schedule_union_pw_multi_aff() const;
+ inline isl::schedule_node previous_sibling() const;
+ inline isl::schedule_node root() const;
+ inline isl::schedule schedule() const;
+ inline isl::schedule get_schedule() const;
+ inline class size schedule_depth() const;
+ inline class size get_schedule_depth() const;
+ inline isl::schedule_node shared_ancestor(const isl::schedule_node &node2) const;
+ inline isl::schedule_node get_shared_ancestor(const isl::schedule_node &node2) const;
+ inline class size tree_depth() const;
+ inline class size get_tree_depth() const;
+ inline isl::union_set universe_domain() const;
+ inline isl::union_set get_universe_domain() const;
+};
+
+// declarations for isl::schedule_node_band
+
+class schedule_node_band : public schedule_node {
+ template <class T>
+ friend boolean schedule_node::isa() const;
+ friend schedule_node_band schedule_node::as<schedule_node_band>() const;
+ static const auto type = isl_schedule_node_band;
+
+protected:
+ inline explicit schedule_node_band(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node_band();
+ inline /* implicit */ schedule_node_band(const schedule_node_band &obj);
+ inline schedule_node_band &operator=(schedule_node_band obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::union_set ast_build_options() const;
+ inline isl::union_set get_ast_build_options() const;
+ inline isl::set ast_isolate_option() const;
+ inline isl::set get_ast_isolate_option() const;
+ inline boolean member_get_coincident(int pos) const;
+ inline schedule_node_band member_set_coincident(int pos, int coincident) const;
+ inline schedule_node_band mod(isl::multi_val mv) const;
+ inline class size n_member() const;
+ inline isl::multi_union_pw_aff partial_schedule() const;
+ inline isl::multi_union_pw_aff get_partial_schedule() const;
+ inline boolean permutable() const;
+ inline boolean get_permutable() const;
+ inline schedule_node_band scale(isl::multi_val mv) const;
+ inline schedule_node_band scale_down(isl::multi_val mv) const;
+ inline schedule_node_band set_ast_build_options(isl::union_set options) const;
+ inline schedule_node_band set_permutable(int permutable) const;
+ inline schedule_node_band shift(isl::multi_union_pw_aff shift) const;
+ inline schedule_node_band split(int pos) const;
+ inline schedule_node_band tile(isl::multi_val sizes) const;
+ inline schedule_node_band member_set_ast_loop_default(int pos) const;
+ inline schedule_node_band member_set_ast_loop_atomic(int pos) const;
+ inline schedule_node_band member_set_ast_loop_unroll(int pos) const;
+ inline schedule_node_band member_set_ast_loop_separate(int pos) const;
+};
+
+// declarations for isl::schedule_node_context
+
+class schedule_node_context : public schedule_node {
+ template <class T>
+ friend boolean schedule_node::isa() const;
+ friend schedule_node_context schedule_node::as<schedule_node_context>() const;
+ static const auto type = isl_schedule_node_context;
+
+protected:
+ inline explicit schedule_node_context(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node_context();
+ inline /* implicit */ schedule_node_context(const schedule_node_context &obj);
+ inline schedule_node_context &operator=(schedule_node_context obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::set context() const;
+ inline isl::set get_context() const;
+};
+
+// declarations for isl::schedule_node_domain
+
+class schedule_node_domain : public schedule_node {
+ template <class T>
+ friend boolean schedule_node::isa() const;
+ friend schedule_node_domain schedule_node::as<schedule_node_domain>() const;
+ static const auto type = isl_schedule_node_domain;
+
+protected:
+ inline explicit schedule_node_domain(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node_domain();
+ inline /* implicit */ schedule_node_domain(const schedule_node_domain &obj);
+ inline schedule_node_domain &operator=(schedule_node_domain obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::union_set domain() const;
+ inline isl::union_set get_domain() const;
+};
+
+// declarations for isl::schedule_node_expansion
+
+class schedule_node_expansion : public schedule_node {
+ template <class T>
+ friend boolean schedule_node::isa() const;
+ friend schedule_node_expansion schedule_node::as<schedule_node_expansion>() const;
+ static const auto type = isl_schedule_node_expansion;
+
+protected:
+ inline explicit schedule_node_expansion(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node_expansion();
+ inline /* implicit */ schedule_node_expansion(const schedule_node_expansion &obj);
+ inline schedule_node_expansion &operator=(schedule_node_expansion obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::union_pw_multi_aff contraction() const;
+ inline isl::union_pw_multi_aff get_contraction() const;
+ inline isl::union_map expansion() const;
+ inline isl::union_map get_expansion() const;
+};
+
+// declarations for isl::schedule_node_extension
+
+class schedule_node_extension : public schedule_node {
+ template <class T>
+ friend boolean schedule_node::isa() const;
+ friend schedule_node_extension schedule_node::as<schedule_node_extension>() const;
+ static const auto type = isl_schedule_node_extension;
+
+protected:
+ inline explicit schedule_node_extension(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node_extension();
+ inline /* implicit */ schedule_node_extension(const schedule_node_extension &obj);
+ inline schedule_node_extension &operator=(schedule_node_extension obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::union_map extension() const;
+ inline isl::union_map get_extension() const;
+};
+
+// declarations for isl::schedule_node_filter
+
+class schedule_node_filter : public schedule_node {
+ template <class T>
+ friend boolean schedule_node::isa() const;
+ friend schedule_node_filter schedule_node::as<schedule_node_filter>() const;
+ static const auto type = isl_schedule_node_filter;
+
+protected:
+ inline explicit schedule_node_filter(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node_filter();
+ inline /* implicit */ schedule_node_filter(const schedule_node_filter &obj);
+ inline schedule_node_filter &operator=(schedule_node_filter obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::union_set filter() const;
+ inline isl::union_set get_filter() const;
+};
+
+// declarations for isl::schedule_node_guard
+
+class schedule_node_guard : public schedule_node {
+ template <class T>
+ friend boolean schedule_node::isa() const;
+ friend schedule_node_guard schedule_node::as<schedule_node_guard>() const;
+ static const auto type = isl_schedule_node_guard;
+
+protected:
+ inline explicit schedule_node_guard(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node_guard();
+ inline /* implicit */ schedule_node_guard(const schedule_node_guard &obj);
+ inline schedule_node_guard &operator=(schedule_node_guard obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::set guard() const;
+ inline isl::set get_guard() const;
+};
+
+// declarations for isl::schedule_node_leaf
+
+class schedule_node_leaf : public schedule_node {
+ template <class T>
+ friend boolean schedule_node::isa() const;
+ friend schedule_node_leaf schedule_node::as<schedule_node_leaf>() const;
+ static const auto type = isl_schedule_node_leaf;
+
+protected:
+ inline explicit schedule_node_leaf(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node_leaf();
+ inline /* implicit */ schedule_node_leaf(const schedule_node_leaf &obj);
+ inline schedule_node_leaf &operator=(schedule_node_leaf obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::schedule_node_mark
+
+class schedule_node_mark : public schedule_node {
+ template <class T>
+ friend boolean schedule_node::isa() const;
+ friend schedule_node_mark schedule_node::as<schedule_node_mark>() const;
+ static const auto type = isl_schedule_node_mark;
+
+protected:
+ inline explicit schedule_node_mark(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node_mark();
+ inline /* implicit */ schedule_node_mark(const schedule_node_mark &obj);
+ inline schedule_node_mark &operator=(schedule_node_mark obj);
+ inline isl::ctx ctx() const;
+
+ inline isl::id id() const;
+ inline isl::id get_id() const;
+};
+
+// declarations for isl::schedule_node_sequence
+
+class schedule_node_sequence : public schedule_node {
+ template <class T>
+ friend boolean schedule_node::isa() const;
+ friend schedule_node_sequence schedule_node::as<schedule_node_sequence>() const;
+ static const auto type = isl_schedule_node_sequence;
+
+protected:
+ inline explicit schedule_node_sequence(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node_sequence();
+ inline /* implicit */ schedule_node_sequence(const schedule_node_sequence &obj);
+ inline schedule_node_sequence &operator=(schedule_node_sequence obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::schedule_node_set
+
+class schedule_node_set : public schedule_node {
+ template <class T>
+ friend boolean schedule_node::isa() const;
+ friend schedule_node_set schedule_node::as<schedule_node_set>() const;
+ static const auto type = isl_schedule_node_set;
+
+protected:
+ inline explicit schedule_node_set(__isl_take isl_schedule_node *ptr);
+
+public:
+ inline /* implicit */ schedule_node_set();
+ inline /* implicit */ schedule_node_set(const schedule_node_set &obj);
+ inline schedule_node_set &operator=(schedule_node_set obj);
+ inline isl::ctx ctx() const;
+
+};
+
+// declarations for isl::set
+inline set manage(__isl_take isl_set *ptr);
+inline set manage_copy(__isl_keep isl_set *ptr);
+
+class set {
+ friend inline set manage(__isl_take isl_set *ptr);
+ friend inline set manage_copy(__isl_keep isl_set *ptr);
+
+protected:
+ isl_set *ptr = nullptr;
+
+ inline explicit set(__isl_take isl_set *ptr);
+
+public:
+ inline /* implicit */ set();
+ inline /* implicit */ set(const set &obj);
+ inline /* implicit */ set(isl::basic_set bset);
+ inline /* implicit */ set(isl::point pnt);
+ inline explicit set(isl::union_set uset);
+ inline explicit set(isl::ctx ctx, const std::string &str);
+ inline set &operator=(set obj);
+ inline ~set();
+ inline __isl_give isl_set *copy() const &;
+ inline __isl_give isl_set *copy() && = delete;
+ inline __isl_keep isl_set *get() const;
+ inline __isl_give isl_set *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::set add_constraint(isl::constraint constraint) const;
+ inline isl::set add_dims(isl::dim type, unsigned int n) const;
+ inline isl::basic_set affine_hull() const;
+ inline isl::set align_params(isl::space model) const;
+ inline isl::set apply(isl::map map) const;
+ inline isl::union_set apply(const isl::union_map &umap) const;
+ inline isl::set apply(const isl::basic_map &map) const;
+ inline isl::pw_multi_aff as_pw_multi_aff() const;
+ inline isl::set as_set() const;
+ inline isl::basic_set_list basic_set_list() const;
+ inline isl::basic_set_list get_basic_set_list() const;
+ inline isl::set bind(isl::multi_id tuple) const;
+ inline isl::set coalesce() const;
+ inline isl::set complement() const;
+ inline isl::union_set compute_divs() const;
+ inline boolean contains(const isl::space &space) const;
+ inline isl::basic_set convex_hull() const;
+ inline isl::set detect_equalities() const;
+ inline class size dim(isl::dim type) const;
+ inline boolean dim_has_any_lower_bound(isl::dim type, unsigned int pos) const;
+ inline isl::id dim_id(isl::dim type, unsigned int pos) const;
+ inline isl::id get_dim_id(isl::dim type, unsigned int pos) const;
+ inline isl::pw_aff dim_max(int pos) const;
+ inline isl::val dim_max_val(int pos) const;
+ inline isl::pw_aff dim_min(int pos) const;
+ inline isl::val dim_min_val(int pos) const;
+ inline std::string dim_name(isl::dim type, unsigned int pos) const;
+ inline std::string get_dim_name(isl::dim type, unsigned int pos) const;
+ inline isl::set drop_constraints_involving_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::set eliminate(isl::dim type, unsigned int first, unsigned int n) const;
+ static inline isl::set empty(isl::space space);
+ inline boolean every_set(const std::function<boolean(isl::set)> &test) const;
+ inline isl::set extract_set(const isl::space &space) const;
+ inline int find_dim_by_id(isl::dim type, const isl::id &id) const;
+ inline int find_dim_by_id(isl::dim type, const std::string &id) const;
+ inline isl::set fix_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::set flatten() const;
+ inline stat foreach_basic_set(const std::function<stat(isl::basic_set)> &fn) const;
+ inline stat foreach_point(const std::function<stat(isl::point)> &fn) const;
+ inline stat foreach_set(const std::function<stat(isl::set)> &fn) const;
+ inline isl::set gist(isl::set context) const;
+ inline isl::union_set gist(const isl::union_set &context) const;
+ inline isl::set gist(const isl::basic_set &context) const;
+ inline isl::set gist(const isl::point &context) const;
+ inline isl::set gist_params(isl::set context) const;
+ inline boolean has_equal_space(const isl::set &set2) const;
+ inline isl::map identity() const;
+ inline isl::union_pw_multi_aff identity_union_pw_multi_aff() const;
+ inline isl::pw_aff indicator_function() const;
+ inline isl::set insert_dims(isl::dim type, unsigned int pos, unsigned int n) const;
+ inline isl::map insert_domain(isl::space domain) const;
+ inline isl::set intersect(isl::set set2) const;
+ inline isl::union_set intersect(const isl::union_set &uset2) const;
+ inline isl::set intersect(const isl::basic_set &set2) const;
+ inline isl::set intersect(const isl::point &set2) const;
+ inline isl::set intersect_params(isl::set params) const;
+ inline boolean involves_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline boolean involves_locals() const;
+ inline boolean is_bounded() const;
+ inline boolean is_disjoint(const isl::set &set2) const;
+ inline boolean is_disjoint(const isl::union_set &uset2) const;
+ inline boolean is_disjoint(const isl::basic_set &set2) const;
+ inline boolean is_disjoint(const isl::point &set2) const;
+ inline boolean is_empty() const;
+ inline boolean is_equal(const isl::set &set2) const;
+ inline boolean is_equal(const isl::union_set &uset2) const;
+ inline boolean is_equal(const isl::basic_set &set2) const;
+ inline boolean is_equal(const isl::point &set2) const;
+ inline boolean is_params() const;
+ inline boolean is_singleton() const;
+ inline boolean is_strict_subset(const isl::set &set2) const;
+ inline boolean is_strict_subset(const isl::union_set &uset2) const;
+ inline boolean is_strict_subset(const isl::basic_set &set2) const;
+ inline boolean is_strict_subset(const isl::point &set2) const;
+ inline boolean is_subset(const isl::set &set2) const;
+ inline boolean is_subset(const isl::union_set &uset2) const;
+ inline boolean is_subset(const isl::basic_set &set2) const;
+ inline boolean is_subset(const isl::point &set2) const;
+ inline boolean is_wrapping() const;
+ inline boolean isa_set() const;
+ inline isl::set lexmax() const;
+ inline isl::pw_multi_aff lexmax_pw_multi_aff() const;
+ inline isl::set lexmin() const;
+ inline isl::pw_multi_aff lexmin_pw_multi_aff() const;
+ inline isl::set lower_bound(isl::multi_pw_aff lower) const;
+ inline isl::set lower_bound(isl::multi_val lower) const;
+ inline isl::set lower_bound_si(isl::dim type, unsigned int pos, int value) const;
+ inline isl::set lower_bound_val(isl::dim type, unsigned int pos, isl::val value) const;
+ inline isl::set lower_bound_val(isl::dim type, unsigned int pos, long value) const;
+ inline isl::multi_pw_aff max_multi_pw_aff() const;
+ inline isl::val max_val(const isl::aff &obj) const;
+ inline isl::multi_pw_aff min_multi_pw_aff() const;
+ inline isl::val min_val(const isl::aff &obj) const;
+ inline class size n_basic_set() const;
+ inline isl::set params() const;
+ inline isl::val plain_get_val_if_fixed(isl::dim type, unsigned int pos) const;
+ inline isl::multi_val plain_multi_val_if_fixed() const;
+ inline isl::multi_val get_plain_multi_val_if_fixed() const;
+ inline isl::basic_set polyhedral_hull() const;
+ inline isl::set preimage(isl::multi_aff ma) const;
+ inline isl::set preimage(isl::multi_pw_aff mpa) const;
+ inline isl::set preimage(isl::pw_multi_aff pma) const;
+ inline isl::union_set preimage(const isl::union_pw_multi_aff &upma) const;
+ inline isl::set product(isl::set set2) const;
+ inline isl::set project_out(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::set project_out_all_params() const;
+ inline isl::set project_out_param(isl::id id) const;
+ inline isl::set project_out_param(const std::string &id) const;
+ inline isl::set project_out_param(isl::id_list list) const;
+ inline isl::pw_multi_aff pw_multi_aff_on_domain(isl::multi_val mv) const;
+ inline isl::set remove_dims(isl::dim type, unsigned int first, unsigned int n) const;
+ inline isl::set remove_divs() const;
+ inline isl::set remove_redundancies() const;
+ inline isl::set reset_tuple_id() const;
+ inline isl::basic_set sample() const;
+ inline isl::point sample_point() const;
+ inline isl::set set_dim_id(isl::dim type, unsigned int pos, isl::id id) const;
+ inline isl::set set_dim_id(isl::dim type, unsigned int pos, const std::string &id) const;
+ inline isl::set_list set_list() const;
+ inline isl::set set_tuple_id(isl::id id) const;
+ inline isl::set set_tuple_id(const std::string &id) const;
+ inline isl::fixed_box simple_fixed_box_hull() const;
+ inline isl::fixed_box get_simple_fixed_box_hull() const;
+ inline isl::basic_set simple_hull() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::val stride(int pos) const;
+ inline isl::val get_stride(int pos) const;
+ inline isl::set subtract(isl::set set2) const;
+ inline isl::union_set subtract(const isl::union_set &uset2) const;
+ inline isl::set subtract(const isl::basic_set &set2) const;
+ inline isl::set subtract(const isl::point &set2) const;
+ inline isl::set_list to_list() const;
+ inline isl::union_set to_union_set() const;
+ inline isl::map translation() const;
+ inline class size tuple_dim() const;
+ inline isl::id tuple_id() const;
+ inline isl::id get_tuple_id() const;
+ inline std::string tuple_name() const;
+ inline std::string get_tuple_name() const;
+ inline isl::set unbind_params(isl::multi_id tuple) const;
+ inline isl::map unbind_params_insert_domain(isl::multi_id domain) const;
+ inline isl::set unite(isl::set set2) const;
+ inline isl::union_set unite(const isl::union_set &uset2) const;
+ inline isl::set unite(const isl::basic_set &set2) const;
+ inline isl::set unite(const isl::point &set2) const;
+ static inline isl::set universe(isl::space space);
+ inline isl::basic_set unshifted_simple_hull() const;
+ inline isl::map unwrap() const;
+ inline isl::set upper_bound(isl::multi_pw_aff upper) const;
+ inline isl::set upper_bound(isl::multi_val upper) const;
+ inline isl::set upper_bound_val(isl::dim type, unsigned int pos, isl::val value) const;
+ inline isl::set upper_bound_val(isl::dim type, unsigned int pos, long value) const;
+};
+
+// declarations for isl::set_list
+inline set_list manage(__isl_take isl_set_list *ptr);
+inline set_list manage_copy(__isl_keep isl_set_list *ptr);
+
+class set_list {
+ friend inline set_list manage(__isl_take isl_set_list *ptr);
+ friend inline set_list manage_copy(__isl_keep isl_set_list *ptr);
+
+protected:
+ isl_set_list *ptr = nullptr;
+
+ inline explicit set_list(__isl_take isl_set_list *ptr);
+
+public:
+ inline /* implicit */ set_list();
+ inline /* implicit */ set_list(const set_list &obj);
+ inline explicit set_list(isl::ctx ctx, int n);
+ inline explicit set_list(isl::set el);
+ inline explicit set_list(isl::ctx ctx, const std::string &str);
+ inline set_list &operator=(set_list obj);
+ inline ~set_list();
+ inline __isl_give isl_set_list *copy() const &;
+ inline __isl_give isl_set_list *copy() && = delete;
+ inline __isl_keep isl_set_list *get() const;
+ inline __isl_give isl_set_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::set_list add(isl::set el) const;
+ inline isl::set at(int index) const;
+ inline isl::set get_at(int index) const;
+ inline isl::set_list clear() const;
+ inline isl::set_list concat(isl::set_list list2) const;
+ inline isl::set_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::set)> &fn) const;
+ inline isl::set_list insert(unsigned int pos, isl::set el) const;
+ inline class size size() const;
+};
+
+// declarations for isl::space
+inline space manage(__isl_take isl_space *ptr);
+inline space manage_copy(__isl_keep isl_space *ptr);
+
+class space {
+ friend inline space manage(__isl_take isl_space *ptr);
+ friend inline space manage_copy(__isl_keep isl_space *ptr);
+
+protected:
+ isl_space *ptr = nullptr;
+
+ inline explicit space(__isl_take isl_space *ptr);
+
+public:
+ inline /* implicit */ space();
+ inline /* implicit */ space(const space &obj);
+ inline explicit space(isl::ctx ctx, unsigned int nparam, unsigned int n_in, unsigned int n_out);
+ inline explicit space(isl::ctx ctx, unsigned int nparam, unsigned int dim);
+ inline space &operator=(space obj);
+ inline ~space();
+ inline __isl_give isl_space *copy() const &;
+ inline __isl_give isl_space *copy() && = delete;
+ inline __isl_keep isl_space *get() const;
+ inline __isl_give isl_space *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::space add_dims(isl::dim type, unsigned int n) const;
+ inline isl::space add_named_tuple(isl::id tuple_id, unsigned int dim) const;
+ inline isl::space add_named_tuple(const std::string &tuple_id, unsigned int dim) const;
+ inline isl::space add_param(isl::id id) const;
+ inline isl::space add_param(const std::string &id) const;
+ inline isl::space add_unnamed_tuple(unsigned int dim) const;
+ inline isl::space align_params(isl::space space2) const;
+ inline isl::space curry() const;
+ inline class size dim(isl::dim type) const;
+ inline isl::id dim_id(isl::dim type, unsigned int pos) const;
+ inline isl::id get_dim_id(isl::dim type, unsigned int pos) const;
+ inline isl::space domain() const;
+ inline isl::multi_aff domain_map_multi_aff() const;
+ inline isl::pw_multi_aff domain_map_pw_multi_aff() const;
+ inline isl::id domain_tuple_id() const;
+ inline isl::id get_domain_tuple_id() const;
+ inline isl::space drop_dims(isl::dim type, unsigned int first, unsigned int num) const;
+ inline int find_dim_by_id(isl::dim type, const isl::id &id) const;
+ inline int find_dim_by_id(isl::dim type, const std::string &id) const;
+ inline isl::space flatten_domain() const;
+ inline isl::space flatten_range() const;
+ inline boolean has_domain_tuple_id() const;
+ inline boolean has_equal_tuples(const isl::space &space2) const;
+ inline boolean has_range_tuple_id() const;
+ inline boolean has_tuple_id(isl::dim type) const;
+ inline boolean has_tuple_name(isl::dim type) const;
+ inline isl::multi_aff identity_multi_aff_on_domain() const;
+ inline isl::multi_pw_aff identity_multi_pw_aff_on_domain() const;
+ inline isl::pw_multi_aff identity_pw_multi_aff_on_domain() const;
+ inline boolean is_equal(const isl::space &space2) const;
+ inline boolean is_params() const;
+ inline boolean is_set() const;
+ inline boolean is_wrapping() const;
+ inline isl::space map_from_domain_and_range(isl::space range) const;
+ inline isl::space map_from_set() const;
+ inline isl::multi_aff multi_aff(isl::aff_list list) const;
+ inline isl::multi_aff multi_aff_on_domain(isl::multi_val mv) const;
+ inline isl::multi_id multi_id(isl::id_list list) const;
+ inline isl::multi_pw_aff multi_pw_aff(isl::pw_aff_list list) const;
+ inline isl::multi_union_pw_aff multi_union_pw_aff(isl::union_pw_aff_list list) const;
+ inline isl::multi_val multi_val(isl::val_list list) const;
+ inline isl::aff param_aff_on_domain(isl::id id) const;
+ inline isl::aff param_aff_on_domain(const std::string &id) const;
+ inline isl::space params() const;
+ static inline isl::space params_alloc(isl::ctx ctx, unsigned int nparam);
+ inline isl::space product(isl::space right) const;
+ inline isl::space range() const;
+ inline isl::multi_aff range_map_multi_aff() const;
+ inline isl::pw_multi_aff range_map_pw_multi_aff() const;
+ inline isl::space range_reverse() const;
+ inline isl::id range_tuple_id() const;
+ inline isl::id get_range_tuple_id() const;
+ inline isl::space reverse() const;
+ inline isl::space set_dim_id(isl::dim type, unsigned int pos, isl::id id) const;
+ inline isl::space set_dim_id(isl::dim type, unsigned int pos, const std::string &id) const;
+ inline isl::space set_domain_tuple(isl::id id) const;
+ inline isl::space set_domain_tuple(const std::string &id) const;
+ inline isl::space set_from_params() const;
+ inline isl::space set_range_tuple(isl::id id) const;
+ inline isl::space set_range_tuple(const std::string &id) const;
+ inline isl::space set_tuple_id(isl::dim type, isl::id id) const;
+ inline isl::space set_tuple_id(isl::dim type, const std::string &id) const;
+ inline isl::id tuple_id(isl::dim type) const;
+ inline isl::id get_tuple_id(isl::dim type) const;
+ inline std::string tuple_name(isl::dim type) const;
+ inline std::string get_tuple_name(isl::dim type) const;
+ inline isl::space uncurry() const;
+ static inline isl::space unit(isl::ctx ctx);
+ inline isl::map universe_map() const;
+ inline isl::set universe_set() const;
+ inline isl::space unwrap() const;
+ inline isl::space wrap() const;
+ inline isl::aff zero_aff_on_domain() const;
+ inline isl::multi_aff zero_multi_aff() const;
+ inline isl::multi_pw_aff zero_multi_pw_aff() const;
+ inline isl::multi_union_pw_aff zero_multi_union_pw_aff() const;
+ inline isl::multi_val zero_multi_val() const;
+};
+
+// declarations for isl::union_access_info
+inline union_access_info manage(__isl_take isl_union_access_info *ptr);
+inline union_access_info manage_copy(__isl_keep isl_union_access_info *ptr);
+
+class union_access_info {
+ friend inline union_access_info manage(__isl_take isl_union_access_info *ptr);
+ friend inline union_access_info manage_copy(__isl_keep isl_union_access_info *ptr);
+
+protected:
+ isl_union_access_info *ptr = nullptr;
+
+ inline explicit union_access_info(__isl_take isl_union_access_info *ptr);
+
+public:
+ inline /* implicit */ union_access_info();
+ inline /* implicit */ union_access_info(const union_access_info &obj);
+ inline explicit union_access_info(isl::union_map sink);
+ inline union_access_info &operator=(union_access_info obj);
+ inline ~union_access_info();
+ inline __isl_give isl_union_access_info *copy() const &;
+ inline __isl_give isl_union_access_info *copy() && = delete;
+ inline __isl_keep isl_union_access_info *get() const;
+ inline __isl_give isl_union_access_info *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::union_flow compute_flow() const;
+ inline isl::union_access_info set_kill(isl::union_map kill) const;
+ inline isl::union_access_info set_may_source(isl::union_map may_source) const;
+ inline isl::union_access_info set_must_source(isl::union_map must_source) const;
+ inline isl::union_access_info set_schedule(isl::schedule schedule) const;
+ inline isl::union_access_info set_schedule_map(isl::union_map schedule_map) const;
+};
+
+// declarations for isl::union_flow
+inline union_flow manage(__isl_take isl_union_flow *ptr);
+inline union_flow manage_copy(__isl_keep isl_union_flow *ptr);
+
+class union_flow {
+ friend inline union_flow manage(__isl_take isl_union_flow *ptr);
+ friend inline union_flow manage_copy(__isl_keep isl_union_flow *ptr);
+
+protected:
+ isl_union_flow *ptr = nullptr;
+
+ inline explicit union_flow(__isl_take isl_union_flow *ptr);
+
+public:
+ inline /* implicit */ union_flow();
+ inline /* implicit */ union_flow(const union_flow &obj);
+ inline union_flow &operator=(union_flow obj);
+ inline ~union_flow();
+ inline __isl_give isl_union_flow *copy() const &;
+ inline __isl_give isl_union_flow *copy() && = delete;
+ inline __isl_keep isl_union_flow *get() const;
+ inline __isl_give isl_union_flow *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::union_map full_may_dependence() const;
+ inline isl::union_map get_full_may_dependence() const;
+ inline isl::union_map full_must_dependence() const;
+ inline isl::union_map get_full_must_dependence() const;
+ inline isl::union_map may_dependence() const;
+ inline isl::union_map get_may_dependence() const;
+ inline isl::union_map may_no_source() const;
+ inline isl::union_map get_may_no_source() const;
+ inline isl::union_map must_dependence() const;
+ inline isl::union_map get_must_dependence() const;
+ inline isl::union_map must_no_source() const;
+ inline isl::union_map get_must_no_source() const;
+};
+
+// declarations for isl::union_map
+inline union_map manage(__isl_take isl_union_map *ptr);
+inline union_map manage_copy(__isl_keep isl_union_map *ptr);
+
+class union_map {
+ friend inline union_map manage(__isl_take isl_union_map *ptr);
+ friend inline union_map manage_copy(__isl_keep isl_union_map *ptr);
+
+protected:
+ isl_union_map *ptr = nullptr;
+
+ inline explicit union_map(__isl_take isl_union_map *ptr);
+
+public:
+ inline /* implicit */ union_map();
+ inline /* implicit */ union_map(const union_map &obj);
+ inline /* implicit */ union_map(isl::basic_map bmap);
+ inline /* implicit */ union_map(isl::map map);
+ inline explicit union_map(isl::ctx ctx, const std::string &str);
+ inline union_map &operator=(union_map obj);
+ inline ~union_map();
+ inline __isl_give isl_union_map *copy() const &;
+ inline __isl_give isl_union_map *copy() && = delete;
+ inline __isl_keep isl_union_map *get() const;
+ inline __isl_give isl_union_map *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::union_map affine_hull() const;
+ inline isl::union_map apply_domain(isl::union_map umap2) const;
+ inline isl::union_map apply_range(isl::union_map umap2) const;
+ inline isl::map as_map() const;
+ inline isl::multi_union_pw_aff as_multi_union_pw_aff() const;
+ inline isl::union_pw_multi_aff as_union_pw_multi_aff() const;
+ inline isl::union_set bind_range(isl::multi_id tuple) const;
+ inline isl::union_map coalesce() const;
+ inline isl::union_map compute_divs() const;
+ inline isl::union_map curry() const;
+ inline isl::union_set deltas() const;
+ inline isl::union_map detect_equalities() const;
+ inline isl::union_set domain() const;
+ inline isl::union_map domain_factor_domain() const;
+ inline isl::union_map domain_factor_range() const;
+ inline isl::union_map domain_map() const;
+ inline isl::union_pw_multi_aff domain_map_union_pw_multi_aff() const;
+ inline isl::union_map domain_product(isl::union_map umap2) const;
+ static inline isl::union_map empty(isl::ctx ctx);
+ inline isl::union_map eq_at(isl::multi_union_pw_aff mupa) const;
+ inline boolean every_map(const std::function<boolean(isl::map)> &test) const;
+ inline isl::map extract_map(isl::space space) const;
+ inline isl::union_map factor_domain() const;
+ inline isl::union_map factor_range() const;
+ inline isl::union_map fixed_power(isl::val exp) const;
+ inline isl::union_map fixed_power(long exp) const;
+ inline isl::union_map flat_range_product(isl::union_map umap2) const;
+ inline stat foreach_map(const std::function<stat(isl::map)> &fn) const;
+ static inline isl::union_map from(isl::multi_union_pw_aff mupa);
+ static inline isl::union_map from(isl::union_pw_multi_aff upma);
+ static inline isl::union_map from_domain(isl::union_set uset);
+ static inline isl::union_map from_domain_and_range(isl::union_set domain, isl::union_set range);
+ static inline isl::union_map from_range(isl::union_set uset);
+ inline isl::union_map gist(isl::union_map context) const;
+ inline isl::union_map gist_domain(isl::union_set uset) const;
+ inline isl::union_map gist_params(isl::set set) const;
+ inline isl::union_map gist_range(isl::union_set uset) const;
+ inline isl::union_map intersect(isl::union_map umap2) const;
+ inline isl::union_map intersect_domain(isl::space space) const;
+ inline isl::union_map intersect_domain(isl::union_set uset) const;
+ inline isl::union_map intersect_domain_factor_domain(isl::union_map factor) const;
+ inline isl::union_map intersect_domain_factor_range(isl::union_map factor) const;
+ inline isl::union_map intersect_params(isl::set set) const;
+ inline isl::union_map intersect_range(isl::space space) const;
+ inline isl::union_map intersect_range(isl::union_set uset) const;
+ inline isl::union_map intersect_range_factor_domain(isl::union_map factor) const;
+ inline isl::union_map intersect_range_factor_range(isl::union_map factor) const;
+ inline boolean is_bijective() const;
+ inline boolean is_disjoint(const isl::union_map &umap2) const;
+ inline boolean is_empty() const;
+ inline boolean is_equal(const isl::union_map &umap2) const;
+ inline boolean is_injective() const;
+ inline boolean is_single_valued() const;
+ inline boolean is_strict_subset(const isl::union_map &umap2) const;
+ inline boolean is_subset(const isl::union_map &umap2) const;
+ inline boolean isa_map() const;
+ inline isl::union_map lexmax() const;
+ inline isl::union_map lexmin() const;
+ inline isl::map_list map_list() const;
+ inline isl::map_list get_map_list() const;
+ inline isl::set params() const;
+ inline isl::union_map polyhedral_hull() const;
+ inline isl::union_map preimage_domain(isl::multi_aff ma) const;
+ inline isl::union_map preimage_domain(isl::multi_pw_aff mpa) const;
+ inline isl::union_map preimage_domain(isl::pw_multi_aff pma) const;
+ inline isl::union_map preimage_domain(isl::union_pw_multi_aff upma) const;
+ inline isl::union_map preimage_range(isl::multi_aff ma) const;
+ inline isl::union_map preimage_range(isl::pw_multi_aff pma) const;
+ inline isl::union_map preimage_range(isl::union_pw_multi_aff upma) const;
+ inline isl::union_map product(isl::union_map umap2) const;
+ inline isl::union_map project_out_all_params() const;
+ inline isl::union_set range() const;
+ inline isl::union_map range_factor_domain() const;
+ inline isl::union_map range_factor_range() const;
+ inline isl::union_map range_map() const;
+ inline isl::union_map range_product(isl::union_map umap2) const;
+ inline isl::union_map range_reverse() const;
+ inline isl::union_map reverse() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::union_map subtract(isl::union_map umap2) const;
+ inline isl::union_map subtract_domain(isl::union_set dom) const;
+ inline isl::union_map subtract_range(isl::union_set dom) const;
+ inline isl::union_map uncurry() const;
+ inline isl::union_map unite(isl::union_map umap2) const;
+ inline isl::union_map universe() const;
+ inline isl::union_set wrap() const;
+ inline isl::union_map zip() const;
+};
+
+// declarations for isl::union_pw_aff
+inline union_pw_aff manage(__isl_take isl_union_pw_aff *ptr);
+inline union_pw_aff manage_copy(__isl_keep isl_union_pw_aff *ptr);
+
+class union_pw_aff {
+ friend inline union_pw_aff manage(__isl_take isl_union_pw_aff *ptr);
+ friend inline union_pw_aff manage_copy(__isl_keep isl_union_pw_aff *ptr);
+
+protected:
+ isl_union_pw_aff *ptr = nullptr;
+
+ inline explicit union_pw_aff(__isl_take isl_union_pw_aff *ptr);
+
+public:
+ inline /* implicit */ union_pw_aff();
+ inline /* implicit */ union_pw_aff(const union_pw_aff &obj);
+ inline /* implicit */ union_pw_aff(isl::aff aff);
+ inline /* implicit */ union_pw_aff(isl::pw_aff pa);
+ inline explicit union_pw_aff(isl::ctx ctx, const std::string &str);
+ inline explicit union_pw_aff(isl::union_set domain, isl::val v);
+ inline union_pw_aff &operator=(union_pw_aff obj);
+ inline ~union_pw_aff();
+ inline __isl_give isl_union_pw_aff *copy() const &;
+ inline __isl_give isl_union_pw_aff *copy() && = delete;
+ inline __isl_keep isl_union_pw_aff *get() const;
+ inline __isl_give isl_union_pw_aff *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::multi_union_pw_aff add(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::union_pw_aff add(isl::union_pw_aff upa2) const;
+ inline isl::union_pw_multi_aff add(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::union_pw_aff add(const isl::aff &upa2) const;
+ inline isl::union_pw_aff add(const isl::pw_aff &upa2) const;
+ inline isl::union_pw_multi_aff add_pw_multi_aff(const isl::pw_multi_aff &pma) const;
+ inline isl::union_pw_multi_aff apply(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::multi_union_pw_aff as_multi_union_pw_aff() const;
+ inline isl::pw_multi_aff as_pw_multi_aff() const;
+ inline isl::union_map as_union_map() const;
+ inline isl::union_pw_aff at(int pos) const;
+ inline isl::union_set bind(const isl::multi_id &tuple) const;
+ inline isl::union_set bind(isl::id id) const;
+ inline isl::union_set bind(const std::string &id) const;
+ inline isl::union_pw_aff coalesce() const;
+ inline class size dim(isl::dim type) const;
+ inline isl::union_set domain() const;
+ static inline isl::union_pw_aff empty(isl::space space);
+ inline isl::pw_multi_aff extract_pw_multi_aff(const isl::space &space) const;
+ inline isl::multi_union_pw_aff flat_range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::union_pw_multi_aff flat_range_product(const isl::union_pw_multi_aff &upma2) const;
+ inline stat foreach_pw_aff(const std::function<stat(isl::pw_aff)> &fn) const;
+ inline isl::union_pw_aff gist(isl::union_set context) const;
+ inline boolean has_range_tuple_id() const;
+ inline isl::union_pw_aff intersect_domain(isl::space space) const;
+ inline isl::union_pw_aff intersect_domain(isl::union_set uset) const;
+ inline isl::union_pw_aff intersect_domain_wrapped_domain(isl::union_set uset) const;
+ inline isl::union_pw_aff intersect_domain_wrapped_range(isl::union_set uset) const;
+ inline isl::union_pw_aff intersect_params(isl::set set) const;
+ inline boolean involves_locals() const;
+ inline boolean involves_nan() const;
+ inline boolean isa_pw_multi_aff() const;
+ inline isl::union_pw_aff_list list() const;
+ inline isl::multi_union_pw_aff neg() const;
+ inline boolean plain_is_empty() const;
+ inline boolean plain_is_equal(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::union_pw_multi_aff preimage_domain_wrapped_domain(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::union_pw_aff pullback(isl::union_pw_multi_aff upma) const;
+ inline isl::pw_multi_aff_list pw_multi_aff_list() const;
+ inline isl::union_pw_multi_aff range_factor_domain() const;
+ inline isl::union_pw_multi_aff range_factor_range() const;
+ inline isl::multi_union_pw_aff range_product(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::union_pw_multi_aff range_product(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::id range_tuple_id() const;
+ inline isl::multi_union_pw_aff reset_range_tuple_id() const;
+ inline isl::multi_union_pw_aff reset_tuple_id(isl::dim type) const;
+ inline isl::multi_union_pw_aff scale(const isl::multi_val &mv) const;
+ inline isl::multi_union_pw_aff scale(const isl::val &v) const;
+ inline isl::multi_union_pw_aff scale(long v) const;
+ inline isl::multi_union_pw_aff scale_down(const isl::multi_val &mv) const;
+ inline isl::multi_union_pw_aff scale_down(const isl::val &v) const;
+ inline isl::multi_union_pw_aff scale_down(long v) const;
+ inline isl::multi_union_pw_aff set_at(int pos, const isl::union_pw_aff &el) const;
+ inline isl::multi_union_pw_aff set_range_tuple(const isl::id &id) const;
+ inline isl::multi_union_pw_aff set_range_tuple(const std::string &id) const;
+ inline isl::multi_union_pw_aff set_union_pw_aff(int pos, const isl::union_pw_aff &el) const;
+ inline class size size() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::multi_union_pw_aff sub(const isl::multi_union_pw_aff &multi2) const;
+ inline isl::union_pw_aff sub(isl::union_pw_aff upa2) const;
+ inline isl::union_pw_multi_aff sub(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::union_pw_aff sub(const isl::aff &upa2) const;
+ inline isl::union_pw_aff sub(const isl::pw_aff &upa2) const;
+ inline isl::union_pw_aff subtract_domain(isl::space space) const;
+ inline isl::union_pw_aff subtract_domain(isl::union_set uset) const;
+ inline isl::union_pw_aff_list to_list() const;
+ inline isl::multi_union_pw_aff union_add(const isl::multi_union_pw_aff &mupa2) const;
+ inline isl::union_pw_aff union_add(isl::union_pw_aff upa2) const;
+ inline isl::union_pw_multi_aff union_add(const isl::union_pw_multi_aff &upma2) const;
+ inline isl::union_pw_aff union_add(const isl::aff &upa2) const;
+ inline isl::union_pw_aff union_add(const isl::pw_aff &upa2) const;
+};
+
+// declarations for isl::union_pw_aff_list
+inline union_pw_aff_list manage(__isl_take isl_union_pw_aff_list *ptr);
+inline union_pw_aff_list manage_copy(__isl_keep isl_union_pw_aff_list *ptr);
+
+class union_pw_aff_list {
+ friend inline union_pw_aff_list manage(__isl_take isl_union_pw_aff_list *ptr);
+ friend inline union_pw_aff_list manage_copy(__isl_keep isl_union_pw_aff_list *ptr);
+
+protected:
+ isl_union_pw_aff_list *ptr = nullptr;
+
+ inline explicit union_pw_aff_list(__isl_take isl_union_pw_aff_list *ptr);
+
+public:
+ inline /* implicit */ union_pw_aff_list();
+ inline /* implicit */ union_pw_aff_list(const union_pw_aff_list &obj);
+ inline explicit union_pw_aff_list(isl::ctx ctx, int n);
+ inline explicit union_pw_aff_list(isl::union_pw_aff el);
+ inline explicit union_pw_aff_list(isl::ctx ctx, const std::string &str);
+ inline union_pw_aff_list &operator=(union_pw_aff_list obj);
+ inline ~union_pw_aff_list();
+ inline __isl_give isl_union_pw_aff_list *copy() const &;
+ inline __isl_give isl_union_pw_aff_list *copy() && = delete;
+ inline __isl_keep isl_union_pw_aff_list *get() const;
+ inline __isl_give isl_union_pw_aff_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::union_pw_aff_list add(isl::union_pw_aff el) const;
+ inline isl::union_pw_aff at(int index) const;
+ inline isl::union_pw_aff get_at(int index) const;
+ inline isl::union_pw_aff_list clear() const;
+ inline isl::union_pw_aff_list concat(isl::union_pw_aff_list list2) const;
+ inline isl::union_pw_aff_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::union_pw_aff)> &fn) const;
+ inline isl::union_pw_aff_list insert(unsigned int pos, isl::union_pw_aff el) const;
+ inline class size size() const;
+};
+
+// declarations for isl::union_pw_multi_aff
+inline union_pw_multi_aff manage(__isl_take isl_union_pw_multi_aff *ptr);
+inline union_pw_multi_aff manage_copy(__isl_keep isl_union_pw_multi_aff *ptr);
+
+class union_pw_multi_aff {
+ friend inline union_pw_multi_aff manage(__isl_take isl_union_pw_multi_aff *ptr);
+ friend inline union_pw_multi_aff manage_copy(__isl_keep isl_union_pw_multi_aff *ptr);
+
+protected:
+ isl_union_pw_multi_aff *ptr = nullptr;
+
+ inline explicit union_pw_multi_aff(__isl_take isl_union_pw_multi_aff *ptr);
+
+public:
+ inline /* implicit */ union_pw_multi_aff();
+ inline /* implicit */ union_pw_multi_aff(const union_pw_multi_aff &obj);
+ inline explicit union_pw_multi_aff(isl::union_set uset);
+ inline /* implicit */ union_pw_multi_aff(isl::multi_aff ma);
+ inline /* implicit */ union_pw_multi_aff(isl::pw_multi_aff pma);
+ inline explicit union_pw_multi_aff(isl::union_map umap);
+ inline /* implicit */ union_pw_multi_aff(isl::union_pw_aff upa);
+ inline explicit union_pw_multi_aff(isl::ctx ctx, const std::string &str);
+ inline union_pw_multi_aff &operator=(union_pw_multi_aff obj);
+ inline ~union_pw_multi_aff();
+ inline __isl_give isl_union_pw_multi_aff *copy() const &;
+ inline __isl_give isl_union_pw_multi_aff *copy() && = delete;
+ inline __isl_keep isl_union_pw_multi_aff *get() const;
+ inline __isl_give isl_union_pw_multi_aff *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::union_pw_multi_aff add(isl::union_pw_multi_aff upma2) const;
+ inline isl::union_pw_multi_aff add_pw_multi_aff(isl::pw_multi_aff pma) const;
+ inline isl::union_pw_multi_aff apply(isl::union_pw_multi_aff upma2) const;
+ inline isl::multi_union_pw_aff as_multi_union_pw_aff() const;
+ inline isl::pw_multi_aff as_pw_multi_aff() const;
+ inline isl::union_map as_union_map() const;
+ inline isl::union_pw_multi_aff coalesce() const;
+ inline isl::union_set domain() const;
+ static inline isl::union_pw_multi_aff empty(isl::space space);
+ static inline isl::union_pw_multi_aff empty(isl::ctx ctx);
+ inline isl::pw_multi_aff extract_pw_multi_aff(isl::space space) const;
+ inline isl::union_pw_multi_aff flat_range_product(isl::union_pw_multi_aff upma2) const;
+ inline isl::union_pw_multi_aff gist(isl::union_set context) const;
+ inline isl::union_pw_multi_aff intersect_domain(isl::space space) const;
+ inline isl::union_pw_multi_aff intersect_domain(isl::union_set uset) const;
+ inline isl::union_pw_multi_aff intersect_domain_wrapped_domain(isl::union_set uset) const;
+ inline isl::union_pw_multi_aff intersect_domain_wrapped_range(isl::union_set uset) const;
+ inline isl::union_pw_multi_aff intersect_params(isl::set set) const;
+ inline boolean involves_locals() const;
+ inline boolean isa_pw_multi_aff() const;
+ inline boolean plain_is_empty() const;
+ inline isl::union_pw_multi_aff preimage_domain_wrapped_domain(isl::union_pw_multi_aff upma2) const;
+ inline isl::union_pw_multi_aff pullback(isl::union_pw_multi_aff upma2) const;
+ inline isl::pw_multi_aff_list pw_multi_aff_list() const;
+ inline isl::pw_multi_aff_list get_pw_multi_aff_list() const;
+ inline isl::union_pw_multi_aff range_factor_domain() const;
+ inline isl::union_pw_multi_aff range_factor_range() const;
+ inline isl::union_pw_multi_aff range_product(isl::union_pw_multi_aff upma2) const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::union_pw_multi_aff sub(isl::union_pw_multi_aff upma2) const;
+ inline isl::union_pw_multi_aff subtract_domain(isl::space space) const;
+ inline isl::union_pw_multi_aff subtract_domain(isl::union_set uset) const;
+ inline isl::union_pw_multi_aff union_add(isl::union_pw_multi_aff upma2) const;
+};
+
+// declarations for isl::union_set
+inline union_set manage(__isl_take isl_union_set *ptr);
+inline union_set manage_copy(__isl_keep isl_union_set *ptr);
+
+class union_set {
+ friend inline union_set manage(__isl_take isl_union_set *ptr);
+ friend inline union_set manage_copy(__isl_keep isl_union_set *ptr);
+
+protected:
+ isl_union_set *ptr = nullptr;
+
+ inline explicit union_set(__isl_take isl_union_set *ptr);
+
+public:
+ inline /* implicit */ union_set();
+ inline /* implicit */ union_set(const union_set &obj);
+ inline /* implicit */ union_set(isl::basic_set bset);
+ inline /* implicit */ union_set(isl::point pnt);
+ inline /* implicit */ union_set(isl::set set);
+ inline explicit union_set(isl::ctx ctx, const std::string &str);
+ inline union_set &operator=(union_set obj);
+ inline ~union_set();
+ inline __isl_give isl_union_set *copy() const &;
+ inline __isl_give isl_union_set *copy() && = delete;
+ inline __isl_keep isl_union_set *get() const;
+ inline __isl_give isl_union_set *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::union_set affine_hull() const;
+ inline isl::union_set apply(isl::union_map umap) const;
+ inline isl::set as_set() const;
+ inline isl::union_set coalesce() const;
+ inline isl::union_set compute_divs() const;
+ inline boolean contains(const isl::space &space) const;
+ inline isl::union_set detect_equalities() const;
+ static inline isl::union_set empty(isl::ctx ctx);
+ inline boolean every_set(const std::function<boolean(isl::set)> &test) const;
+ inline isl::set extract_set(isl::space space) const;
+ inline stat foreach_point(const std::function<stat(isl::point)> &fn) const;
+ inline stat foreach_set(const std::function<stat(isl::set)> &fn) const;
+ inline isl::union_set gist(isl::union_set context) const;
+ inline isl::union_set gist_params(isl::set set) const;
+ inline isl::union_map identity() const;
+ inline isl::union_pw_multi_aff identity_union_pw_multi_aff() const;
+ inline isl::union_set intersect(isl::union_set uset2) const;
+ inline isl::union_set intersect_params(isl::set set) const;
+ inline boolean is_disjoint(const isl::union_set &uset2) const;
+ inline boolean is_empty() const;
+ inline boolean is_equal(const isl::union_set &uset2) const;
+ inline boolean is_strict_subset(const isl::union_set &uset2) const;
+ inline boolean is_subset(const isl::union_set &uset2) const;
+ inline boolean isa_set() const;
+ inline isl::union_set lexmax() const;
+ inline isl::union_set lexmin() const;
+ inline isl::set params() const;
+ inline isl::union_set polyhedral_hull() const;
+ inline isl::union_set preimage(isl::multi_aff ma) const;
+ inline isl::union_set preimage(isl::pw_multi_aff pma) const;
+ inline isl::union_set preimage(isl::union_pw_multi_aff upma) const;
+ inline isl::point sample_point() const;
+ inline isl::set_list set_list() const;
+ inline isl::set_list get_set_list() const;
+ inline isl::space space() const;
+ inline isl::space get_space() const;
+ inline isl::union_set subtract(isl::union_set uset2) const;
+ inline isl::union_set_list to_list() const;
+ inline isl::union_set unite(isl::union_set uset2) const;
+ inline isl::union_set universe() const;
+ inline isl::union_map unwrap() const;
+};
+
+// declarations for isl::union_set_list
+inline union_set_list manage(__isl_take isl_union_set_list *ptr);
+inline union_set_list manage_copy(__isl_keep isl_union_set_list *ptr);
+
+class union_set_list {
+ friend inline union_set_list manage(__isl_take isl_union_set_list *ptr);
+ friend inline union_set_list manage_copy(__isl_keep isl_union_set_list *ptr);
+
+protected:
+ isl_union_set_list *ptr = nullptr;
+
+ inline explicit union_set_list(__isl_take isl_union_set_list *ptr);
+
+public:
+ inline /* implicit */ union_set_list();
+ inline /* implicit */ union_set_list(const union_set_list &obj);
+ inline explicit union_set_list(isl::ctx ctx, int n);
+ inline explicit union_set_list(isl::union_set el);
+ inline explicit union_set_list(isl::ctx ctx, const std::string &str);
+ inline union_set_list &operator=(union_set_list obj);
+ inline ~union_set_list();
+ inline __isl_give isl_union_set_list *copy() const &;
+ inline __isl_give isl_union_set_list *copy() && = delete;
+ inline __isl_keep isl_union_set_list *get() const;
+ inline __isl_give isl_union_set_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::union_set_list add(isl::union_set el) const;
+ inline isl::union_set at(int index) const;
+ inline isl::union_set get_at(int index) const;
+ inline isl::union_set_list clear() const;
+ inline isl::union_set_list concat(isl::union_set_list list2) const;
+ inline isl::union_set_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::union_set)> &fn) const;
+ inline isl::union_set_list insert(unsigned int pos, isl::union_set el) const;
+ inline class size size() const;
+};
+
+// declarations for isl::val
+inline val manage(__isl_take isl_val *ptr);
+inline val manage_copy(__isl_keep isl_val *ptr);
+
+class val {
+ friend inline val manage(__isl_take isl_val *ptr);
+ friend inline val manage_copy(__isl_keep isl_val *ptr);
+
+protected:
+ isl_val *ptr = nullptr;
+
+ inline explicit val(__isl_take isl_val *ptr);
+
+public:
+ inline /* implicit */ val();
+ inline /* implicit */ val(const val &obj);
+ inline explicit val(isl::ctx ctx, long i);
+ inline explicit val(isl::ctx ctx, const std::string &str);
+ inline val &operator=(val obj);
+ inline ~val();
+ inline __isl_give isl_val *copy() const &;
+ inline __isl_give isl_val *copy() && = delete;
+ inline __isl_keep isl_val *get() const;
+ inline __isl_give isl_val *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::val abs() const;
+ inline boolean abs_eq(const isl::val &v2) const;
+ inline boolean abs_eq(long v2) const;
+ inline isl::val add(isl::val v2) const;
+ inline isl::val add(long v2) const;
+ inline isl::val ceil() const;
+ inline int cmp_si(long i) const;
+ inline long den_si() const;
+ inline long get_den_si() const;
+ inline isl::val div(isl::val v2) const;
+ inline isl::val div(long v2) const;
+ inline boolean eq(const isl::val &v2) const;
+ inline boolean eq(long v2) const;
+ inline isl::val floor() const;
+ inline isl::val gcd(isl::val v2) const;
+ inline isl::val gcd(long v2) const;
+ inline boolean ge(const isl::val &v2) const;
+ inline boolean ge(long v2) const;
+ inline boolean gt(const isl::val &v2) const;
+ inline boolean gt(long v2) const;
+ static inline isl::val infty(isl::ctx ctx);
+ static inline isl::val int_from_ui(isl::ctx ctx, unsigned long u);
+ inline isl::val inv() const;
+ inline boolean is_divisible_by(const isl::val &v2) const;
+ inline boolean is_divisible_by(long v2) const;
+ inline boolean is_infty() const;
+ inline boolean is_int() const;
+ inline boolean is_nan() const;
+ inline boolean is_neg() const;
+ inline boolean is_neginfty() const;
+ inline boolean is_negone() const;
+ inline boolean is_nonneg() const;
+ inline boolean is_nonpos() const;
+ inline boolean is_one() const;
+ inline boolean is_pos() const;
+ inline boolean is_rat() const;
+ inline boolean is_zero() const;
+ inline boolean le(const isl::val &v2) const;
+ inline boolean le(long v2) const;
+ inline boolean lt(const isl::val &v2) const;
+ inline boolean lt(long v2) const;
+ inline isl::val max(isl::val v2) const;
+ inline isl::val max(long v2) const;
+ inline isl::val min(isl::val v2) const;
+ inline isl::val min(long v2) const;
+ inline isl::val mod(isl::val v2) const;
+ inline isl::val mod(long v2) const;
+ inline isl::val mul(isl::val v2) const;
+ inline isl::val mul(long v2) const;
+ static inline isl::val nan(isl::ctx ctx);
+ inline boolean ne(const isl::val &v2) const;
+ inline boolean ne(long v2) const;
+ inline isl::val neg() const;
+ static inline isl::val neginfty(isl::ctx ctx);
+ static inline isl::val negone(isl::ctx ctx);
+ inline long num_si() const;
+ inline long get_num_si() const;
+ static inline isl::val one(isl::ctx ctx);
+ inline isl::val pow2() const;
+ inline int sgn() const;
+ inline isl::val sub(isl::val v2) const;
+ inline isl::val sub(long v2) const;
+ inline isl::val_list to_list() const;
+ inline isl::val trunc() const;
+ static inline isl::val zero(isl::ctx ctx);
+};
+
+// declarations for isl::val_list
+inline val_list manage(__isl_take isl_val_list *ptr);
+inline val_list manage_copy(__isl_keep isl_val_list *ptr);
+
+class val_list {
+ friend inline val_list manage(__isl_take isl_val_list *ptr);
+ friend inline val_list manage_copy(__isl_keep isl_val_list *ptr);
+
+protected:
+ isl_val_list *ptr = nullptr;
+
+ inline explicit val_list(__isl_take isl_val_list *ptr);
+
+public:
+ inline /* implicit */ val_list();
+ inline /* implicit */ val_list(const val_list &obj);
+ inline explicit val_list(isl::ctx ctx, int n);
+ inline explicit val_list(isl::val el);
+ inline explicit val_list(isl::ctx ctx, const std::string &str);
+ inline val_list &operator=(val_list obj);
+ inline ~val_list();
+ inline __isl_give isl_val_list *copy() const &;
+ inline __isl_give isl_val_list *copy() && = delete;
+ inline __isl_keep isl_val_list *get() const;
+ inline __isl_give isl_val_list *release();
+ inline bool is_null() const;
+ inline isl::ctx ctx() const;
+
+ inline isl::val_list add(isl::val el) const;
+ inline isl::val_list add(long el) const;
+ inline isl::val at(int index) const;
+ inline isl::val get_at(int index) const;
+ inline isl::val_list clear() const;
+ inline isl::val_list concat(isl::val_list list2) const;
+ inline isl::val_list drop(unsigned int first, unsigned int n) const;
+ inline stat foreach(const std::function<stat(isl::val)> &fn) const;
+ inline isl::val_list insert(unsigned int pos, isl::val el) const;
+ inline isl::val_list insert(unsigned int pos, long el) const;
+ inline class size size() const;
+};
+
+// implementations for isl::aff
+aff manage(__isl_take isl_aff *ptr) {
+ return aff(ptr);
+}
+aff manage_copy(__isl_keep isl_aff *ptr) {
+ ptr = isl_aff_copy(ptr);
+ return aff(ptr);
+}
+
+aff::aff()
+ : ptr(nullptr) {}
+
+aff::aff(const aff &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+aff::aff(__isl_take isl_aff *ptr)
+ : ptr(ptr) {}
+
+aff::aff(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_aff_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+aff::aff(isl::local_space ls, isl::val val)
+{
+ auto res = isl_aff_val_on_domain(ls.release(), val.release());
+ ptr = res;
+}
+
+aff::aff(isl::local_space ls)
+{
+ auto res = isl_aff_zero_on_domain(ls.release());
+ ptr = res;
+}
+
+aff &aff::operator=(aff obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+aff::~aff() {
+ if (ptr)
+ isl_aff_free(ptr);
+}
+
+__isl_give isl_aff *aff::copy() const & {
+ return isl_aff_copy(ptr);
+}
+
+__isl_keep isl_aff *aff::get() const {
+ return ptr;
+}
+
+__isl_give isl_aff *aff::release() {
+ isl_aff *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool aff::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx aff::ctx() const {
+ return isl::ctx(isl_aff_get_ctx(ptr));
+}
+
+isl::aff aff::add(isl::aff aff2) const
+{
+ auto res = isl_aff_add(copy(), aff2.release());
+ return manage(res);
+}
+
+isl::multi_aff aff::add(const isl::multi_aff &multi2) const
+{
+ return isl::multi_aff(*this).add(multi2);
+}
+
+isl::multi_pw_aff aff::add(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).add(multi2);
+}
+
+isl::multi_union_pw_aff aff::add(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).add(multi2);
+}
+
+isl::pw_aff aff::add(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).add(pwaff2);
+}
+
+isl::pw_multi_aff aff::add(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_aff(*this).add(pma2);
+}
+
+isl::union_pw_aff aff::add(const isl::union_pw_aff &upa2) const
+{
+ return isl::pw_aff(*this).add(upa2);
+}
+
+isl::union_pw_multi_aff aff::add(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_aff(*this).add(upma2);
+}
+
+isl::aff aff::add_constant(isl::val v) const
+{
+ auto res = isl_aff_add_constant_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::aff aff::add_constant(long v) const
+{
+ return this->add_constant(isl::val(ctx(), v));
+}
+
+isl::multi_aff aff::add_constant(const isl::multi_val &mv) const
+{
+ return isl::multi_aff(*this).add_constant(mv);
+}
+
+isl::aff aff::add_constant_si(int v) const
+{
+ auto res = isl_aff_add_constant_si(copy(), v);
+ return manage(res);
+}
+
+isl::pw_aff aff::add_dims(isl::dim type, unsigned int n) const
+{
+ return isl::pw_aff(*this).add_dims(type, n);
+}
+
+isl::union_pw_multi_aff aff::add_pw_multi_aff(const isl::pw_multi_aff &pma) const
+{
+ return isl::pw_aff(*this).add_pw_multi_aff(pma);
+}
+
+isl::union_pw_multi_aff aff::apply(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_aff(*this).apply(upma2);
+}
+
+isl::aff aff::as_aff() const
+{
+ return isl::pw_aff(*this).as_aff();
+}
+
+isl::map aff::as_map() const
+{
+ return isl::pw_aff(*this).as_map();
+}
+
+isl::multi_aff aff::as_multi_aff() const
+{
+ return isl::pw_aff(*this).as_multi_aff();
+}
+
+isl::multi_union_pw_aff aff::as_multi_union_pw_aff() const
+{
+ return isl::pw_aff(*this).as_multi_union_pw_aff();
+}
+
+isl::pw_multi_aff aff::as_pw_multi_aff() const
+{
+ return isl::pw_aff(*this).as_pw_multi_aff();
+}
+
+isl::set aff::as_set() const
+{
+ return isl::multi_aff(*this).as_set();
+}
+
+isl::union_map aff::as_union_map() const
+{
+ return isl::pw_aff(*this).as_union_map();
+}
+
+isl::aff aff::at(int pos) const
+{
+ return isl::multi_aff(*this).at(pos);
+}
+
+isl::basic_set aff::bind(isl::id id) const
+{
+ auto res = isl_aff_bind_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::basic_set aff::bind(const std::string &id) const
+{
+ return this->bind(isl::id(ctx(), id));
+}
+
+isl::basic_set aff::bind(const isl::multi_id &tuple) const
+{
+ return isl::multi_aff(*this).bind(tuple);
+}
+
+isl::pw_aff aff::bind_domain(const isl::multi_id &tuple) const
+{
+ return isl::pw_aff(*this).bind_domain(tuple);
+}
+
+isl::pw_aff aff::bind_domain_wrapped_domain(const isl::multi_id &tuple) const
+{
+ return isl::pw_aff(*this).bind_domain_wrapped_domain(tuple);
+}
+
+isl::aff aff::ceil() const
+{
+ auto res = isl_aff_ceil(copy());
+ return manage(res);
+}
+
+isl::pw_aff aff::coalesce() const
+{
+ return isl::pw_aff(*this).coalesce();
+}
+
+isl::pw_aff aff::cond(const isl::pw_aff &pwaff_true, const isl::pw_aff &pwaff_false) const
+{
+ return isl::pw_aff(*this).cond(pwaff_true, pwaff_false);
+}
+
+isl::multi_val aff::constant_multi_val() const
+{
+ return isl::multi_aff(*this).constant_multi_val();
+}
+
+isl::val aff::constant_val() const
+{
+ auto res = isl_aff_get_constant_val(get());
+ return manage(res);
+}
+
+isl::val aff::get_constant_val() const
+{
+ return constant_val();
+}
+
+isl::val aff::denominator_val() const
+{
+ auto res = isl_aff_get_denominator_val(get());
+ return manage(res);
+}
+
+isl::val aff::get_denominator_val() const
+{
+ return denominator_val();
+}
+
+class size aff::dim(isl::dim type) const
+{
+ return isl::multi_aff(*this).dim(type);
+}
+
+isl::id aff::dim_id(isl::dim type, unsigned int pos) const
+{
+ return isl::pw_aff(*this).dim_id(type, pos);
+}
+
+isl::aff aff::div(isl::aff aff2) const
+{
+ auto res = isl_aff_div(copy(), aff2.release());
+ return manage(res);
+}
+
+isl::pw_aff aff::div(const isl::pw_aff &pa2) const
+{
+ return isl::pw_aff(*this).div(pa2);
+}
+
+isl::set aff::domain() const
+{
+ return isl::pw_aff(*this).domain();
+}
+
+isl::space aff::domain_space() const
+{
+ return isl::pw_aff(*this).domain_space();
+}
+
+isl::pw_multi_aff aff::drop_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::pw_aff(*this).drop_dims(type, first, n);
+}
+
+isl::set aff::eq_set(isl::aff aff2) const
+{
+ auto res = isl_aff_eq_set(copy(), aff2.release());
+ return manage(res);
+}
+
+isl::set aff::eq_set(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).eq_set(pwaff2);
+}
+
+isl::val aff::eval(isl::point pnt) const
+{
+ auto res = isl_aff_eval(copy(), pnt.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff aff::extract_pw_multi_aff(const isl::space &space) const
+{
+ return isl::pw_aff(*this).extract_pw_multi_aff(space);
+}
+
+isl::multi_aff aff::flat_range_product(const isl::multi_aff &multi2) const
+{
+ return isl::multi_aff(*this).flat_range_product(multi2);
+}
+
+isl::multi_pw_aff aff::flat_range_product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).flat_range_product(multi2);
+}
+
+isl::multi_union_pw_aff aff::flat_range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).flat_range_product(multi2);
+}
+
+isl::pw_multi_aff aff::flat_range_product(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_aff(*this).flat_range_product(pma2);
+}
+
+isl::union_pw_multi_aff aff::flat_range_product(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_aff(*this).flat_range_product(upma2);
+}
+
+isl::aff aff::floor() const
+{
+ auto res = isl_aff_floor(copy());
+ return manage(res);
+}
+
+stat aff::foreach_piece(const std::function<stat(isl::set, isl::aff)> &fn) const
+{
+ return isl::pw_aff(*this).foreach_piece(fn);
+}
+
+stat aff::foreach_piece(const std::function<stat(isl::set, isl::multi_aff)> &fn) const
+{
+ return isl::pw_aff(*this).foreach_piece(fn);
+}
+
+stat aff::foreach_pw_aff(const std::function<stat(isl::pw_aff)> &fn) const
+{
+ return isl::pw_aff(*this).foreach_pw_aff(fn);
+}
+
+isl::set aff::ge_set(isl::aff aff2) const
+{
+ auto res = isl_aff_ge_set(copy(), aff2.release());
+ return manage(res);
+}
+
+isl::set aff::ge_set(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).ge_set(pwaff2);
+}
+
+isl::aff aff::gist(isl::set context) const
+{
+ auto res = isl_aff_gist(copy(), context.release());
+ return manage(res);
+}
+
+isl::union_pw_aff aff::gist(const isl::union_set &context) const
+{
+ return isl::pw_aff(*this).gist(context);
+}
+
+isl::aff aff::gist(const isl::basic_set &context) const
+{
+ return this->gist(isl::set(context));
+}
+
+isl::aff aff::gist(const isl::point &context) const
+{
+ return this->gist(isl::set(context));
+}
+
+isl::set aff::gt_set(isl::aff aff2) const
+{
+ auto res = isl_aff_gt_set(copy(), aff2.release());
+ return manage(res);
+}
+
+isl::set aff::gt_set(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).gt_set(pwaff2);
+}
+
+boolean aff::has_range_tuple_id() const
+{
+ return isl::multi_aff(*this).has_range_tuple_id();
+}
+
+isl::multi_aff aff::identity() const
+{
+ return isl::multi_aff(*this).identity();
+}
+
+isl::pw_aff aff::insert_domain(const isl::space &domain) const
+{
+ return isl::pw_aff(*this).insert_domain(domain);
+}
+
+isl::pw_aff aff::intersect_domain(const isl::set &set) const
+{
+ return isl::pw_aff(*this).intersect_domain(set);
+}
+
+isl::union_pw_aff aff::intersect_domain(const isl::space &space) const
+{
+ return isl::pw_aff(*this).intersect_domain(space);
+}
+
+isl::union_pw_aff aff::intersect_domain(const isl::union_set &uset) const
+{
+ return isl::pw_aff(*this).intersect_domain(uset);
+}
+
+isl::union_pw_aff aff::intersect_domain_wrapped_domain(const isl::union_set &uset) const
+{
+ return isl::pw_aff(*this).intersect_domain_wrapped_domain(uset);
+}
+
+isl::union_pw_aff aff::intersect_domain_wrapped_range(const isl::union_set &uset) const
+{
+ return isl::pw_aff(*this).intersect_domain_wrapped_range(uset);
+}
+
+isl::pw_aff aff::intersect_params(const isl::set &set) const
+{
+ return isl::pw_aff(*this).intersect_params(set);
+}
+
+boolean aff::involves_locals() const
+{
+ return isl::multi_aff(*this).involves_locals();
+}
+
+boolean aff::involves_nan() const
+{
+ return isl::multi_aff(*this).involves_nan();
+}
+
+boolean aff::involves_param(const isl::id &id) const
+{
+ return isl::pw_aff(*this).involves_param(id);
+}
+
+boolean aff::involves_param(const std::string &id) const
+{
+ return this->involves_param(isl::id(ctx(), id));
+}
+
+boolean aff::involves_param(const isl::id_list &list) const
+{
+ return isl::pw_aff(*this).involves_param(list);
+}
+
+boolean aff::is_cst() const
+{
+ auto res = isl_aff_is_cst(get());
+ return manage(res);
+}
+
+boolean aff::is_equal(const isl::pw_aff &pa2) const
+{
+ return isl::pw_aff(*this).is_equal(pa2);
+}
+
+boolean aff::isa_aff() const
+{
+ return isl::pw_aff(*this).isa_aff();
+}
+
+boolean aff::isa_multi_aff() const
+{
+ return isl::pw_aff(*this).isa_multi_aff();
+}
+
+boolean aff::isa_pw_multi_aff() const
+{
+ return isl::pw_aff(*this).isa_pw_multi_aff();
+}
+
+isl::set aff::le_set(isl::aff aff2) const
+{
+ auto res = isl_aff_le_set(copy(), aff2.release());
+ return manage(res);
+}
+
+isl::set aff::le_set(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).le_set(pwaff2);
+}
+
+isl::aff_list aff::list() const
+{
+ return isl::multi_aff(*this).list();
+}
+
+isl::set aff::lt_set(isl::aff aff2) const
+{
+ auto res = isl_aff_lt_set(copy(), aff2.release());
+ return manage(res);
+}
+
+isl::set aff::lt_set(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).lt_set(pwaff2);
+}
+
+isl::multi_pw_aff aff::max(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).max(multi2);
+}
+
+isl::pw_aff aff::max(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).max(pwaff2);
+}
+
+isl::multi_val aff::max_multi_val() const
+{
+ return isl::pw_aff(*this).max_multi_val();
+}
+
+isl::multi_pw_aff aff::min(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).min(multi2);
+}
+
+isl::pw_aff aff::min(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).min(pwaff2);
+}
+
+isl::multi_val aff::min_multi_val() const
+{
+ return isl::pw_aff(*this).min_multi_val();
+}
+
+isl::aff aff::mod(isl::val mod) const
+{
+ auto res = isl_aff_mod_val(copy(), mod.release());
+ return manage(res);
+}
+
+isl::aff aff::mod(long mod) const
+{
+ return this->mod(isl::val(ctx(), mod));
+}
+
+isl::aff aff::mul(isl::aff aff2) const
+{
+ auto res = isl_aff_mul(copy(), aff2.release());
+ return manage(res);
+}
+
+isl::pw_aff aff::mul(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).mul(pwaff2);
+}
+
+class size aff::n_piece() const
+{
+ return isl::pw_aff(*this).n_piece();
+}
+
+isl::set aff::ne_set(isl::aff aff2) const
+{
+ auto res = isl_aff_ne_set(copy(), aff2.release());
+ return manage(res);
+}
+
+isl::set aff::ne_set(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).ne_set(pwaff2);
+}
+
+isl::aff aff::neg() const
+{
+ auto res = isl_aff_neg(copy());
+ return manage(res);
+}
+
+boolean aff::plain_is_empty() const
+{
+ return isl::pw_aff(*this).plain_is_empty();
+}
+
+boolean aff::plain_is_equal(const isl::multi_aff &multi2) const
+{
+ return isl::multi_aff(*this).plain_is_equal(multi2);
+}
+
+boolean aff::plain_is_equal(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).plain_is_equal(multi2);
+}
+
+boolean aff::plain_is_equal(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).plain_is_equal(multi2);
+}
+
+isl::pw_multi_aff aff::preimage_domain_wrapped_domain(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_aff(*this).preimage_domain_wrapped_domain(pma2);
+}
+
+isl::union_pw_multi_aff aff::preimage_domain_wrapped_domain(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_aff(*this).preimage_domain_wrapped_domain(upma2);
+}
+
+isl::multi_aff aff::product(const isl::multi_aff &multi2) const
+{
+ return isl::multi_aff(*this).product(multi2);
+}
+
+isl::multi_pw_aff aff::product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).product(multi2);
+}
+
+isl::pw_multi_aff aff::product(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_aff(*this).product(pma2);
+}
+
+isl::aff aff::pullback(isl::multi_aff ma) const
+{
+ auto res = isl_aff_pullback_multi_aff(copy(), ma.release());
+ return manage(res);
+}
+
+isl::pw_aff aff::pullback(const isl::multi_pw_aff &mpa) const
+{
+ return isl::pw_aff(*this).pullback(mpa);
+}
+
+isl::pw_aff aff::pullback(const isl::pw_multi_aff &pma) const
+{
+ return isl::pw_aff(*this).pullback(pma);
+}
+
+isl::union_pw_aff aff::pullback(const isl::union_pw_multi_aff &upma) const
+{
+ return isl::pw_aff(*this).pullback(upma);
+}
+
+isl::aff aff::pullback(const isl::aff &ma) const
+{
+ return this->pullback(isl::multi_aff(ma));
+}
+
+isl::pw_multi_aff_list aff::pw_multi_aff_list() const
+{
+ return isl::pw_aff(*this).pw_multi_aff_list();
+}
+
+isl::pw_multi_aff aff::range_factor_domain() const
+{
+ return isl::pw_aff(*this).range_factor_domain();
+}
+
+isl::pw_multi_aff aff::range_factor_range() const
+{
+ return isl::pw_aff(*this).range_factor_range();
+}
+
+isl::multi_aff aff::range_product(const isl::multi_aff &multi2) const
+{
+ return isl::multi_aff(*this).range_product(multi2);
+}
+
+isl::multi_pw_aff aff::range_product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).range_product(multi2);
+}
+
+isl::multi_union_pw_aff aff::range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).range_product(multi2);
+}
+
+isl::pw_multi_aff aff::range_product(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_aff(*this).range_product(pma2);
+}
+
+isl::union_pw_multi_aff aff::range_product(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_aff(*this).range_product(upma2);
+}
+
+isl::id aff::range_tuple_id() const
+{
+ return isl::multi_aff(*this).range_tuple_id();
+}
+
+isl::multi_aff aff::reset_range_tuple_id() const
+{
+ return isl::multi_aff(*this).reset_range_tuple_id();
+}
+
+isl::multi_aff aff::reset_tuple_id(isl::dim type) const
+{
+ return isl::multi_aff(*this).reset_tuple_id(type);
+}
+
+isl::aff aff::scale(isl::val v) const
+{
+ auto res = isl_aff_scale_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::aff aff::scale(long v) const
+{
+ return this->scale(isl::val(ctx(), v));
+}
+
+isl::multi_aff aff::scale(const isl::multi_val &mv) const
+{
+ return isl::multi_aff(*this).scale(mv);
+}
+
+isl::aff aff::scale_down(isl::val v) const
+{
+ auto res = isl_aff_scale_down_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::aff aff::scale_down(long v) const
+{
+ return this->scale_down(isl::val(ctx(), v));
+}
+
+isl::multi_aff aff::scale_down(const isl::multi_val &mv) const
+{
+ return isl::multi_aff(*this).scale_down(mv);
+}
+
+isl::multi_aff aff::set_aff(int pos, const isl::aff &el) const
+{
+ return isl::multi_aff(*this).set_aff(pos, el);
+}
+
+isl::multi_aff aff::set_at(int pos, const isl::aff &el) const
+{
+ return isl::multi_aff(*this).set_at(pos, el);
+}
+
+isl::multi_pw_aff aff::set_at(int pos, const isl::pw_aff &el) const
+{
+ return isl::pw_aff(*this).set_at(pos, el);
+}
+
+isl::multi_union_pw_aff aff::set_at(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::pw_aff(*this).set_at(pos, el);
+}
+
+isl::aff aff::set_constant_si(int v) const
+{
+ auto res = isl_aff_set_constant_si(copy(), v);
+ return manage(res);
+}
+
+isl::multi_pw_aff aff::set_pw_aff(int pos, const isl::pw_aff &el) const
+{
+ return isl::pw_aff(*this).set_pw_aff(pos, el);
+}
+
+isl::pw_multi_aff aff::set_pw_aff(unsigned int pos, const isl::pw_aff &pa) const
+{
+ return isl::pw_aff(*this).set_pw_aff(pos, pa);
+}
+
+isl::multi_aff aff::set_range_tuple(const isl::id &id) const
+{
+ return isl::multi_aff(*this).set_range_tuple(id);
+}
+
+isl::multi_aff aff::set_range_tuple(const std::string &id) const
+{
+ return this->set_range_tuple(isl::id(ctx(), id));
+}
+
+isl::pw_aff aff::set_tuple_id(isl::dim type, const isl::id &id) const
+{
+ return isl::pw_aff(*this).set_tuple_id(type, id);
+}
+
+isl::pw_aff aff::set_tuple_id(isl::dim type, const std::string &id) const
+{
+ return this->set_tuple_id(type, isl::id(ctx(), id));
+}
+
+isl::multi_union_pw_aff aff::set_union_pw_aff(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::pw_aff(*this).set_union_pw_aff(pos, el);
+}
+
+class size aff::size() const
+{
+ return isl::multi_aff(*this).size();
+}
+
+isl::space aff::space() const
+{
+ return isl::pw_aff(*this).space();
+}
+
+isl::aff aff::sub(isl::aff aff2) const
+{
+ auto res = isl_aff_sub(copy(), aff2.release());
+ return manage(res);
+}
+
+isl::multi_aff aff::sub(const isl::multi_aff &multi2) const
+{
+ return isl::multi_aff(*this).sub(multi2);
+}
+
+isl::multi_pw_aff aff::sub(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).sub(multi2);
+}
+
+isl::multi_union_pw_aff aff::sub(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::pw_aff(*this).sub(multi2);
+}
+
+isl::pw_aff aff::sub(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).sub(pwaff2);
+}
+
+isl::pw_multi_aff aff::sub(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_aff(*this).sub(pma2);
+}
+
+isl::union_pw_aff aff::sub(const isl::union_pw_aff &upa2) const
+{
+ return isl::pw_aff(*this).sub(upa2);
+}
+
+isl::union_pw_multi_aff aff::sub(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_aff(*this).sub(upma2);
+}
+
+isl::pw_aff aff::subtract_domain(const isl::set &set) const
+{
+ return isl::pw_aff(*this).subtract_domain(set);
+}
+
+isl::union_pw_aff aff::subtract_domain(const isl::space &space) const
+{
+ return isl::pw_aff(*this).subtract_domain(space);
+}
+
+isl::union_pw_aff aff::subtract_domain(const isl::union_set &uset) const
+{
+ return isl::pw_aff(*this).subtract_domain(uset);
+}
+
+isl::pw_aff aff::tdiv_q(const isl::pw_aff &pa2) const
+{
+ return isl::pw_aff(*this).tdiv_q(pa2);
+}
+
+isl::pw_aff aff::tdiv_r(const isl::pw_aff &pa2) const
+{
+ return isl::pw_aff(*this).tdiv_r(pa2);
+}
+
+isl::aff_list aff::to_list() const
+{
+ auto res = isl_aff_to_list(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff aff::to_multi_pw_aff() const
+{
+ return isl::multi_aff(*this).to_multi_pw_aff();
+}
+
+isl::multi_union_pw_aff aff::to_multi_union_pw_aff() const
+{
+ return isl::multi_aff(*this).to_multi_union_pw_aff();
+}
+
+isl::pw_multi_aff aff::to_pw_multi_aff() const
+{
+ return isl::multi_aff(*this).to_pw_multi_aff();
+}
+
+isl::union_pw_aff aff::to_union_pw_aff() const
+{
+ return isl::pw_aff(*this).to_union_pw_aff();
+}
+
+isl::union_pw_multi_aff aff::to_union_pw_multi_aff() const
+{
+ return isl::pw_aff(*this).to_union_pw_multi_aff();
+}
+
+isl::id aff::tuple_id(isl::dim type) const
+{
+ return isl::pw_aff(*this).tuple_id(type);
+}
+
+isl::aff aff::unbind_params_insert_domain(isl::multi_id domain) const
+{
+ auto res = isl_aff_unbind_params_insert_domain(copy(), domain.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff aff::union_add(const isl::multi_pw_aff &mpa2) const
+{
+ return isl::pw_aff(*this).union_add(mpa2);
+}
+
+isl::multi_union_pw_aff aff::union_add(const isl::multi_union_pw_aff &mupa2) const
+{
+ return isl::pw_aff(*this).union_add(mupa2);
+}
+
+isl::pw_aff aff::union_add(const isl::pw_aff &pwaff2) const
+{
+ return isl::pw_aff(*this).union_add(pwaff2);
+}
+
+isl::pw_multi_aff aff::union_add(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_aff(*this).union_add(pma2);
+}
+
+isl::union_pw_aff aff::union_add(const isl::union_pw_aff &upa2) const
+{
+ return isl::pw_aff(*this).union_add(upa2);
+}
+
+isl::union_pw_multi_aff aff::union_add(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_aff(*this).union_add(upma2);
+}
+
+isl::aff aff::var_on_domain(isl::local_space ls, isl::dim type, unsigned int pos)
+{
+ auto res = isl_aff_var_on_domain(ls.release(), static_cast<enum isl_dim_type>(type), pos);
+ return manage(res);
+}
+
+isl::aff aff::zero_on_domain(isl::space space)
+{
+ auto res = isl_aff_zero_on_domain_space(space.release());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const aff &obj)
+{
+ char *str = isl_aff_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::aff_list
+aff_list manage(__isl_take isl_aff_list *ptr) {
+ return aff_list(ptr);
+}
+aff_list manage_copy(__isl_keep isl_aff_list *ptr) {
+ ptr = isl_aff_list_copy(ptr);
+ return aff_list(ptr);
+}
+
+aff_list::aff_list()
+ : ptr(nullptr) {}
+
+aff_list::aff_list(const aff_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+aff_list::aff_list(__isl_take isl_aff_list *ptr)
+ : ptr(ptr) {}
+
+aff_list::aff_list(isl::ctx ctx, int n)
+{
+ auto res = isl_aff_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+aff_list::aff_list(isl::aff el)
+{
+ auto res = isl_aff_list_from_aff(el.release());
+ ptr = res;
+}
+
+aff_list::aff_list(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_aff_list_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+aff_list &aff_list::operator=(aff_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+aff_list::~aff_list() {
+ if (ptr)
+ isl_aff_list_free(ptr);
+}
+
+__isl_give isl_aff_list *aff_list::copy() const & {
+ return isl_aff_list_copy(ptr);
+}
+
+__isl_keep isl_aff_list *aff_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_aff_list *aff_list::release() {
+ isl_aff_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool aff_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx aff_list::ctx() const {
+ return isl::ctx(isl_aff_list_get_ctx(ptr));
+}
+
+isl::aff_list aff_list::add(isl::aff el) const
+{
+ auto res = isl_aff_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::aff aff_list::at(int index) const
+{
+ auto res = isl_aff_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::aff aff_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::aff_list aff_list::clear() const
+{
+ auto res = isl_aff_list_clear(copy());
+ return manage(res);
+}
+
+isl::aff_list aff_list::concat(isl::aff_list list2) const
+{
+ auto res = isl_aff_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::aff_list aff_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_aff_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat aff_list::foreach(const std::function<stat(isl::aff)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::aff)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_aff *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_aff_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::aff_list aff_list::insert(unsigned int pos, isl::aff el) const
+{
+ auto res = isl_aff_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+class size aff_list::size() const
+{
+ auto res = isl_aff_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const aff_list &obj)
+{
+ char *str = isl_aff_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_build
+ast_build manage(__isl_take isl_ast_build *ptr) {
+ return ast_build(ptr);
+}
+ast_build manage_copy(__isl_keep isl_ast_build *ptr) {
+ ptr = isl_ast_build_copy(ptr);
+ return ast_build(ptr);
+}
+
+ast_build::ast_build()
+ : ptr(nullptr) {}
+
+ast_build::ast_build(const ast_build &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+ copy_callbacks(obj);
+}
+
+ast_build::ast_build(__isl_take isl_ast_build *ptr)
+ : ptr(ptr) {}
+
+ast_build::ast_build(isl::ctx ctx)
+{
+ auto res = isl_ast_build_alloc(ctx.release());
+ ptr = res;
+}
+
+ast_build &ast_build::operator=(ast_build obj) {
+ std::swap(this->ptr, obj.ptr);
+ copy_callbacks(obj);
+ return *this;
+}
+
+ast_build::~ast_build() {
+ if (ptr)
+ isl_ast_build_free(ptr);
+}
+
+__isl_give isl_ast_build *ast_build::copy() const & {
+ return isl_ast_build_copy(ptr);
+}
+
+__isl_keep isl_ast_build *ast_build::get() const {
+ return ptr;
+}
+
+__isl_give isl_ast_build *ast_build::release() {
+ if (at_each_domain_data)
+ isl_die(ctx().get(), isl_error_invalid, "cannot release object with persistent callbacks", return nullptr);
+ isl_ast_build *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool ast_build::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx ast_build::ctx() const {
+ return isl::ctx(isl_ast_build_get_ctx(ptr));
+}
+
+ast_build &ast_build::copy_callbacks(const ast_build &obj)
+{
+ at_each_domain_data = obj.at_each_domain_data;
+ return *this;
+}
+
+isl_ast_node *ast_build::at_each_domain(isl_ast_node *arg_0, isl_ast_build *arg_1, void *arg_2)
+{
+ auto *data = static_cast<struct at_each_domain_data *>(arg_2);
+ auto ret = (data->func)(manage(arg_0), manage_copy(arg_1));
+ return ret.release();
+}
+
+void ast_build::set_at_each_domain_data(const std::function<isl::ast_node(isl::ast_node, isl::ast_build)> &fn)
+{
+ at_each_domain_data = std::make_shared<struct at_each_domain_data>();
+ at_each_domain_data->func = fn;
+ ptr = isl_ast_build_set_at_each_domain(ptr, &at_each_domain, at_each_domain_data.get());
+}
+
+isl::ast_build ast_build::set_at_each_domain(const std::function<isl::ast_node(isl::ast_node, isl::ast_build)> &fn) const
+{
+ auto copy = *this;
+ copy.set_at_each_domain_data(fn);
+ return copy;
+}
+
+isl::ast_expr ast_build::access_from(isl::multi_pw_aff mpa) const
+{
+ auto res = isl_ast_build_access_from_multi_pw_aff(get(), mpa.release());
+ return manage(res);
+}
+
+isl::ast_expr ast_build::access_from(isl::pw_multi_aff pma) const
+{
+ auto res = isl_ast_build_access_from_pw_multi_aff(get(), pma.release());
+ return manage(res);
+}
+
+isl::ast_expr ast_build::call_from(isl::multi_pw_aff mpa) const
+{
+ auto res = isl_ast_build_call_from_multi_pw_aff(get(), mpa.release());
+ return manage(res);
+}
+
+isl::ast_expr ast_build::call_from(isl::pw_multi_aff pma) const
+{
+ auto res = isl_ast_build_call_from_pw_multi_aff(get(), pma.release());
+ return manage(res);
+}
+
+isl::ast_expr ast_build::expr_from(isl::pw_aff pa) const
+{
+ auto res = isl_ast_build_expr_from_pw_aff(get(), pa.release());
+ return manage(res);
+}
+
+isl::ast_expr ast_build::expr_from(isl::set set) const
+{
+ auto res = isl_ast_build_expr_from_set(get(), set.release());
+ return manage(res);
+}
+
+isl::ast_build ast_build::from_context(isl::set set)
+{
+ auto res = isl_ast_build_from_context(set.release());
+ return manage(res);
+}
+
+isl::ast_node ast_build::node_from(isl::schedule schedule) const
+{
+ auto res = isl_ast_build_node_from_schedule(get(), schedule.release());
+ return manage(res);
+}
+
+isl::ast_node ast_build::node_from_schedule_map(isl::union_map schedule) const
+{
+ auto res = isl_ast_build_node_from_schedule_map(get(), schedule.release());
+ return manage(res);
+}
+
+isl::ast_build ast_build::restrict(isl::set set) const
+{
+ auto res = isl_ast_build_restrict(copy(), set.release());
+ return manage(res).copy_callbacks(*this);
+}
+
+isl::union_map ast_build::schedule() const
+{
+ auto res = isl_ast_build_get_schedule(get());
+ return manage(res);
+}
+
+isl::union_map ast_build::get_schedule() const
+{
+ return schedule();
+}
+
+// implementations for isl::ast_expr
+ast_expr manage(__isl_take isl_ast_expr *ptr) {
+ return ast_expr(ptr);
+}
+ast_expr manage_copy(__isl_keep isl_ast_expr *ptr) {
+ ptr = isl_ast_expr_copy(ptr);
+ return ast_expr(ptr);
+}
+
+ast_expr::ast_expr()
+ : ptr(nullptr) {}
+
+ast_expr::ast_expr(const ast_expr &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+ast_expr::ast_expr(__isl_take isl_ast_expr *ptr)
+ : ptr(ptr) {}
+
+ast_expr &ast_expr::operator=(ast_expr obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+ast_expr::~ast_expr() {
+ if (ptr)
+ isl_ast_expr_free(ptr);
+}
+
+__isl_give isl_ast_expr *ast_expr::copy() const & {
+ return isl_ast_expr_copy(ptr);
+}
+
+__isl_keep isl_ast_expr *ast_expr::get() const {
+ return ptr;
+}
+
+__isl_give isl_ast_expr *ast_expr::release() {
+ isl_ast_expr *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool ast_expr::is_null() const {
+ return ptr == nullptr;
+}
+
+template <typename T, typename>
+boolean ast_expr::isa_type(T subtype) const
+{
+ if (is_null())
+ return boolean();
+ return isl_ast_expr_get_type(get()) == subtype;
+}
+template <class T>
+boolean ast_expr::isa() const
+{
+ return isa_type<decltype(T::type)>(T::type);
+}
+template <class T>
+T ast_expr::as() const
+{
+ if (isa<T>().is_false())
+ isl_die(ctx().get(), isl_error_invalid, "not an object of the requested subtype", return T());
+ return T(copy());
+}
+
+isl::ctx ast_expr::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+isl::ast_expr ast_expr::add(isl::ast_expr expr2) const
+{
+ auto res = isl_ast_expr_add(copy(), expr2.release());
+ return manage(res);
+}
+
+isl::ast_expr ast_expr::address_of() const
+{
+ auto res = isl_ast_expr_address_of(copy());
+ return manage(res);
+}
+
+isl::ast_expr ast_expr::eq(isl::ast_expr expr2) const
+{
+ auto res = isl_ast_expr_eq(copy(), expr2.release());
+ return manage(res);
+}
+
+isl::ast_expr ast_expr::from_val(isl::val v)
+{
+ auto res = isl_ast_expr_from_val(v.release());
+ return manage(res);
+}
+
+isl::id ast_expr::id() const
+{
+ auto res = isl_ast_expr_get_id(get());
+ return manage(res);
+}
+
+isl::id ast_expr::get_id() const
+{
+ return id();
+}
+
+isl::ast_expr ast_expr::le(isl::ast_expr expr2) const
+{
+ auto res = isl_ast_expr_le(copy(), expr2.release());
+ return manage(res);
+}
+
+isl::ast_expr ast_expr::mul(isl::ast_expr expr2) const
+{
+ auto res = isl_ast_expr_mul(copy(), expr2.release());
+ return manage(res);
+}
+
+isl::ast_expr ast_expr::op_arg(int pos) const
+{
+ auto res = isl_ast_expr_get_op_arg(get(), pos);
+ return manage(res);
+}
+
+isl::ast_expr ast_expr::get_op_arg(int pos) const
+{
+ return op_arg(pos);
+}
+
+std::string ast_expr::to_C_str() const
+{
+ auto res = isl_ast_expr_to_C_str(get());
+ std::string tmp(res);
+ free(res);
+ return tmp;
+}
+
+isl::val ast_expr::val() const
+{
+ auto res = isl_ast_expr_get_val(get());
+ return manage(res);
+}
+
+isl::val ast_expr::get_val() const
+{
+ return val();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_id
+ast_expr_id::ast_expr_id()
+ : ast_expr() {}
+
+ast_expr_id::ast_expr_id(const ast_expr_id &obj)
+ : ast_expr(obj)
+{
+}
+
+ast_expr_id::ast_expr_id(__isl_take isl_ast_expr *ptr)
+ : ast_expr(ptr) {}
+
+ast_expr_id &ast_expr_id::operator=(ast_expr_id obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_id::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+isl::id ast_expr_id::id() const
+{
+ auto res = isl_ast_expr_id_get_id(get());
+ return manage(res);
+}
+
+isl::id ast_expr_id::get_id() const
+{
+ return id();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_id &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_int
+ast_expr_int::ast_expr_int()
+ : ast_expr() {}
+
+ast_expr_int::ast_expr_int(const ast_expr_int &obj)
+ : ast_expr(obj)
+{
+}
+
+ast_expr_int::ast_expr_int(__isl_take isl_ast_expr *ptr)
+ : ast_expr(ptr) {}
+
+ast_expr_int &ast_expr_int::operator=(ast_expr_int obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_int::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+isl::val ast_expr_int::val() const
+{
+ auto res = isl_ast_expr_int_get_val(get());
+ return manage(res);
+}
+
+isl::val ast_expr_int::get_val() const
+{
+ return val();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_int &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op
+ast_expr_op::ast_expr_op()
+ : ast_expr() {}
+
+ast_expr_op::ast_expr_op(const ast_expr_op &obj)
+ : ast_expr(obj)
+{
+}
+
+ast_expr_op::ast_expr_op(__isl_take isl_ast_expr *ptr)
+ : ast_expr(ptr) {}
+
+ast_expr_op &ast_expr_op::operator=(ast_expr_op obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+template <typename T, typename>
+boolean ast_expr_op::isa_type(T subtype) const
+{
+ if (is_null())
+ return boolean();
+ return isl_ast_expr_op_get_type(get()) == subtype;
+}
+template <class T>
+boolean ast_expr_op::isa() const
+{
+ return isa_type<decltype(T::type)>(T::type);
+}
+template <class T>
+T ast_expr_op::as() const
+{
+ if (isa<T>().is_false())
+ isl_die(ctx().get(), isl_error_invalid, "not an object of the requested subtype", return T());
+ return T(copy());
+}
+
+isl::ctx ast_expr_op::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+isl::ast_expr ast_expr_op::arg(int pos) const
+{
+ auto res = isl_ast_expr_op_get_arg(get(), pos);
+ return manage(res);
+}
+
+isl::ast_expr ast_expr_op::get_arg(int pos) const
+{
+ return arg(pos);
+}
+
+class size ast_expr_op::n_arg() const
+{
+ auto res = isl_ast_expr_op_get_n_arg(get());
+ return manage(res);
+}
+
+class size ast_expr_op::get_n_arg() const
+{
+ return n_arg();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_access
+ast_expr_op_access::ast_expr_op_access()
+ : ast_expr_op() {}
+
+ast_expr_op_access::ast_expr_op_access(const ast_expr_op_access &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_access::ast_expr_op_access(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_access &ast_expr_op_access::operator=(ast_expr_op_access obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_access::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_access &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_add
+ast_expr_op_add::ast_expr_op_add()
+ : ast_expr_op() {}
+
+ast_expr_op_add::ast_expr_op_add(const ast_expr_op_add &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_add::ast_expr_op_add(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_add &ast_expr_op_add::operator=(ast_expr_op_add obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_add::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_add &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_address_of
+ast_expr_op_address_of::ast_expr_op_address_of()
+ : ast_expr_op() {}
+
+ast_expr_op_address_of::ast_expr_op_address_of(const ast_expr_op_address_of &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_address_of::ast_expr_op_address_of(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_address_of &ast_expr_op_address_of::operator=(ast_expr_op_address_of obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_address_of::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_address_of &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_and
+ast_expr_op_and::ast_expr_op_and()
+ : ast_expr_op() {}
+
+ast_expr_op_and::ast_expr_op_and(const ast_expr_op_and &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_and::ast_expr_op_and(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_and &ast_expr_op_and::operator=(ast_expr_op_and obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_and::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_and &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_and_then
+ast_expr_op_and_then::ast_expr_op_and_then()
+ : ast_expr_op() {}
+
+ast_expr_op_and_then::ast_expr_op_and_then(const ast_expr_op_and_then &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_and_then::ast_expr_op_and_then(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_and_then &ast_expr_op_and_then::operator=(ast_expr_op_and_then obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_and_then::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_and_then &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_call
+ast_expr_op_call::ast_expr_op_call()
+ : ast_expr_op() {}
+
+ast_expr_op_call::ast_expr_op_call(const ast_expr_op_call &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_call::ast_expr_op_call(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_call &ast_expr_op_call::operator=(ast_expr_op_call obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_call::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_call &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_cond
+ast_expr_op_cond::ast_expr_op_cond()
+ : ast_expr_op() {}
+
+ast_expr_op_cond::ast_expr_op_cond(const ast_expr_op_cond &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_cond::ast_expr_op_cond(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_cond &ast_expr_op_cond::operator=(ast_expr_op_cond obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_cond::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_cond &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_div
+ast_expr_op_div::ast_expr_op_div()
+ : ast_expr_op() {}
+
+ast_expr_op_div::ast_expr_op_div(const ast_expr_op_div &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_div::ast_expr_op_div(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_div &ast_expr_op_div::operator=(ast_expr_op_div obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_div::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_div &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_eq
+ast_expr_op_eq::ast_expr_op_eq()
+ : ast_expr_op() {}
+
+ast_expr_op_eq::ast_expr_op_eq(const ast_expr_op_eq &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_eq::ast_expr_op_eq(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_eq &ast_expr_op_eq::operator=(ast_expr_op_eq obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_eq::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_eq &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_fdiv_q
+ast_expr_op_fdiv_q::ast_expr_op_fdiv_q()
+ : ast_expr_op() {}
+
+ast_expr_op_fdiv_q::ast_expr_op_fdiv_q(const ast_expr_op_fdiv_q &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_fdiv_q::ast_expr_op_fdiv_q(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_fdiv_q &ast_expr_op_fdiv_q::operator=(ast_expr_op_fdiv_q obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_fdiv_q::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_fdiv_q &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_ge
+ast_expr_op_ge::ast_expr_op_ge()
+ : ast_expr_op() {}
+
+ast_expr_op_ge::ast_expr_op_ge(const ast_expr_op_ge &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_ge::ast_expr_op_ge(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_ge &ast_expr_op_ge::operator=(ast_expr_op_ge obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_ge::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_ge &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_gt
+ast_expr_op_gt::ast_expr_op_gt()
+ : ast_expr_op() {}
+
+ast_expr_op_gt::ast_expr_op_gt(const ast_expr_op_gt &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_gt::ast_expr_op_gt(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_gt &ast_expr_op_gt::operator=(ast_expr_op_gt obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_gt::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_gt &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_le
+ast_expr_op_le::ast_expr_op_le()
+ : ast_expr_op() {}
+
+ast_expr_op_le::ast_expr_op_le(const ast_expr_op_le &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_le::ast_expr_op_le(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_le &ast_expr_op_le::operator=(ast_expr_op_le obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_le::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_le &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_lt
+ast_expr_op_lt::ast_expr_op_lt()
+ : ast_expr_op() {}
+
+ast_expr_op_lt::ast_expr_op_lt(const ast_expr_op_lt &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_lt::ast_expr_op_lt(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_lt &ast_expr_op_lt::operator=(ast_expr_op_lt obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_lt::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_lt &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_max
+ast_expr_op_max::ast_expr_op_max()
+ : ast_expr_op() {}
+
+ast_expr_op_max::ast_expr_op_max(const ast_expr_op_max &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_max::ast_expr_op_max(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_max &ast_expr_op_max::operator=(ast_expr_op_max obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_max::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_max &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_member
+ast_expr_op_member::ast_expr_op_member()
+ : ast_expr_op() {}
+
+ast_expr_op_member::ast_expr_op_member(const ast_expr_op_member &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_member::ast_expr_op_member(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_member &ast_expr_op_member::operator=(ast_expr_op_member obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_member::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_member &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_min
+ast_expr_op_min::ast_expr_op_min()
+ : ast_expr_op() {}
+
+ast_expr_op_min::ast_expr_op_min(const ast_expr_op_min &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_min::ast_expr_op_min(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_min &ast_expr_op_min::operator=(ast_expr_op_min obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_min::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_min &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_minus
+ast_expr_op_minus::ast_expr_op_minus()
+ : ast_expr_op() {}
+
+ast_expr_op_minus::ast_expr_op_minus(const ast_expr_op_minus &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_minus::ast_expr_op_minus(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_minus &ast_expr_op_minus::operator=(ast_expr_op_minus obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_minus::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_minus &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_mul
+ast_expr_op_mul::ast_expr_op_mul()
+ : ast_expr_op() {}
+
+ast_expr_op_mul::ast_expr_op_mul(const ast_expr_op_mul &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_mul::ast_expr_op_mul(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_mul &ast_expr_op_mul::operator=(ast_expr_op_mul obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_mul::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_mul &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_or
+ast_expr_op_or::ast_expr_op_or()
+ : ast_expr_op() {}
+
+ast_expr_op_or::ast_expr_op_or(const ast_expr_op_or &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_or::ast_expr_op_or(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_or &ast_expr_op_or::operator=(ast_expr_op_or obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_or::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_or &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_or_else
+ast_expr_op_or_else::ast_expr_op_or_else()
+ : ast_expr_op() {}
+
+ast_expr_op_or_else::ast_expr_op_or_else(const ast_expr_op_or_else &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_or_else::ast_expr_op_or_else(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_or_else &ast_expr_op_or_else::operator=(ast_expr_op_or_else obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_or_else::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_or_else &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_pdiv_q
+ast_expr_op_pdiv_q::ast_expr_op_pdiv_q()
+ : ast_expr_op() {}
+
+ast_expr_op_pdiv_q::ast_expr_op_pdiv_q(const ast_expr_op_pdiv_q &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_pdiv_q::ast_expr_op_pdiv_q(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_pdiv_q &ast_expr_op_pdiv_q::operator=(ast_expr_op_pdiv_q obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_pdiv_q::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_pdiv_q &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_pdiv_r
+ast_expr_op_pdiv_r::ast_expr_op_pdiv_r()
+ : ast_expr_op() {}
+
+ast_expr_op_pdiv_r::ast_expr_op_pdiv_r(const ast_expr_op_pdiv_r &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_pdiv_r::ast_expr_op_pdiv_r(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_pdiv_r &ast_expr_op_pdiv_r::operator=(ast_expr_op_pdiv_r obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_pdiv_r::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_pdiv_r &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_select
+ast_expr_op_select::ast_expr_op_select()
+ : ast_expr_op() {}
+
+ast_expr_op_select::ast_expr_op_select(const ast_expr_op_select &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_select::ast_expr_op_select(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_select &ast_expr_op_select::operator=(ast_expr_op_select obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_select::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_select &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_sub
+ast_expr_op_sub::ast_expr_op_sub()
+ : ast_expr_op() {}
+
+ast_expr_op_sub::ast_expr_op_sub(const ast_expr_op_sub &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_sub::ast_expr_op_sub(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_sub &ast_expr_op_sub::operator=(ast_expr_op_sub obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_sub::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_sub &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_expr_op_zdiv_r
+ast_expr_op_zdiv_r::ast_expr_op_zdiv_r()
+ : ast_expr_op() {}
+
+ast_expr_op_zdiv_r::ast_expr_op_zdiv_r(const ast_expr_op_zdiv_r &obj)
+ : ast_expr_op(obj)
+{
+}
+
+ast_expr_op_zdiv_r::ast_expr_op_zdiv_r(__isl_take isl_ast_expr *ptr)
+ : ast_expr_op(ptr) {}
+
+ast_expr_op_zdiv_r &ast_expr_op_zdiv_r::operator=(ast_expr_op_zdiv_r obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_expr_op_zdiv_r::ctx() const {
+ return isl::ctx(isl_ast_expr_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_expr_op_zdiv_r &obj)
+{
+ char *str = isl_ast_expr_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_node
+ast_node manage(__isl_take isl_ast_node *ptr) {
+ return ast_node(ptr);
+}
+ast_node manage_copy(__isl_keep isl_ast_node *ptr) {
+ ptr = isl_ast_node_copy(ptr);
+ return ast_node(ptr);
+}
+
+ast_node::ast_node()
+ : ptr(nullptr) {}
+
+ast_node::ast_node(const ast_node &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+ast_node::ast_node(__isl_take isl_ast_node *ptr)
+ : ptr(ptr) {}
+
+ast_node &ast_node::operator=(ast_node obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+ast_node::~ast_node() {
+ if (ptr)
+ isl_ast_node_free(ptr);
+}
+
+__isl_give isl_ast_node *ast_node::copy() const & {
+ return isl_ast_node_copy(ptr);
+}
+
+__isl_keep isl_ast_node *ast_node::get() const {
+ return ptr;
+}
+
+__isl_give isl_ast_node *ast_node::release() {
+ isl_ast_node *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool ast_node::is_null() const {
+ return ptr == nullptr;
+}
+
+template <typename T, typename>
+boolean ast_node::isa_type(T subtype) const
+{
+ if (is_null())
+ return boolean();
+ return isl_ast_node_get_type(get()) == subtype;
+}
+template <class T>
+boolean ast_node::isa() const
+{
+ return isa_type<decltype(T::type)>(T::type);
+}
+template <class T>
+T ast_node::as() const
+{
+ if (isa<T>().is_false())
+ isl_die(ctx().get(), isl_error_invalid, "not an object of the requested subtype", return T());
+ return T(copy());
+}
+
+isl::ctx ast_node::ctx() const {
+ return isl::ctx(isl_ast_node_get_ctx(ptr));
+}
+
+isl::id ast_node::annotation() const
+{
+ auto res = isl_ast_node_get_annotation(get());
+ return manage(res);
+}
+
+isl::id ast_node::get_annotation() const
+{
+ return annotation();
+}
+
+std::string ast_node::to_C_str() const
+{
+ auto res = isl_ast_node_to_C_str(get());
+ std::string tmp(res);
+ free(res);
+ return tmp;
+}
+
+isl::ast_node_list ast_node::to_list() const
+{
+ auto res = isl_ast_node_to_list(copy());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_node &obj)
+{
+ char *str = isl_ast_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_node_block
+ast_node_block::ast_node_block()
+ : ast_node() {}
+
+ast_node_block::ast_node_block(const ast_node_block &obj)
+ : ast_node(obj)
+{
+}
+
+ast_node_block::ast_node_block(__isl_take isl_ast_node *ptr)
+ : ast_node(ptr) {}
+
+ast_node_block &ast_node_block::operator=(ast_node_block obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_node_block::ctx() const {
+ return isl::ctx(isl_ast_node_get_ctx(ptr));
+}
+
+isl::ast_node_list ast_node_block::children() const
+{
+ auto res = isl_ast_node_block_get_children(get());
+ return manage(res);
+}
+
+isl::ast_node_list ast_node_block::get_children() const
+{
+ return children();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_node_block &obj)
+{
+ char *str = isl_ast_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_node_for
+ast_node_for::ast_node_for()
+ : ast_node() {}
+
+ast_node_for::ast_node_for(const ast_node_for &obj)
+ : ast_node(obj)
+{
+}
+
+ast_node_for::ast_node_for(__isl_take isl_ast_node *ptr)
+ : ast_node(ptr) {}
+
+ast_node_for &ast_node_for::operator=(ast_node_for obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_node_for::ctx() const {
+ return isl::ctx(isl_ast_node_get_ctx(ptr));
+}
+
+isl::ast_node ast_node_for::body() const
+{
+ auto res = isl_ast_node_for_get_body(get());
+ return manage(res);
+}
+
+isl::ast_node ast_node_for::get_body() const
+{
+ return body();
+}
+
+isl::ast_expr ast_node_for::cond() const
+{
+ auto res = isl_ast_node_for_get_cond(get());
+ return manage(res);
+}
+
+isl::ast_expr ast_node_for::get_cond() const
+{
+ return cond();
+}
+
+isl::ast_expr ast_node_for::inc() const
+{
+ auto res = isl_ast_node_for_get_inc(get());
+ return manage(res);
+}
+
+isl::ast_expr ast_node_for::get_inc() const
+{
+ return inc();
+}
+
+isl::ast_expr ast_node_for::init() const
+{
+ auto res = isl_ast_node_for_get_init(get());
+ return manage(res);
+}
+
+isl::ast_expr ast_node_for::get_init() const
+{
+ return init();
+}
+
+boolean ast_node_for::is_degenerate() const
+{
+ auto res = isl_ast_node_for_is_degenerate(get());
+ return manage(res);
+}
+
+isl::ast_expr ast_node_for::iterator() const
+{
+ auto res = isl_ast_node_for_get_iterator(get());
+ return manage(res);
+}
+
+isl::ast_expr ast_node_for::get_iterator() const
+{
+ return iterator();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_node_for &obj)
+{
+ char *str = isl_ast_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_node_if
+ast_node_if::ast_node_if()
+ : ast_node() {}
+
+ast_node_if::ast_node_if(const ast_node_if &obj)
+ : ast_node(obj)
+{
+}
+
+ast_node_if::ast_node_if(__isl_take isl_ast_node *ptr)
+ : ast_node(ptr) {}
+
+ast_node_if &ast_node_if::operator=(ast_node_if obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_node_if::ctx() const {
+ return isl::ctx(isl_ast_node_get_ctx(ptr));
+}
+
+isl::ast_expr ast_node_if::cond() const
+{
+ auto res = isl_ast_node_if_get_cond(get());
+ return manage(res);
+}
+
+isl::ast_expr ast_node_if::get_cond() const
+{
+ return cond();
+}
+
+isl::ast_node ast_node_if::else_node() const
+{
+ auto res = isl_ast_node_if_get_else_node(get());
+ return manage(res);
+}
+
+isl::ast_node ast_node_if::get_else_node() const
+{
+ return else_node();
+}
+
+boolean ast_node_if::has_else_node() const
+{
+ auto res = isl_ast_node_if_has_else_node(get());
+ return manage(res);
+}
+
+isl::ast_node ast_node_if::then_node() const
+{
+ auto res = isl_ast_node_if_get_then_node(get());
+ return manage(res);
+}
+
+isl::ast_node ast_node_if::get_then_node() const
+{
+ return then_node();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_node_if &obj)
+{
+ char *str = isl_ast_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_node_list
+ast_node_list manage(__isl_take isl_ast_node_list *ptr) {
+ return ast_node_list(ptr);
+}
+ast_node_list manage_copy(__isl_keep isl_ast_node_list *ptr) {
+ ptr = isl_ast_node_list_copy(ptr);
+ return ast_node_list(ptr);
+}
+
+ast_node_list::ast_node_list()
+ : ptr(nullptr) {}
+
+ast_node_list::ast_node_list(const ast_node_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+ast_node_list::ast_node_list(__isl_take isl_ast_node_list *ptr)
+ : ptr(ptr) {}
+
+ast_node_list::ast_node_list(isl::ctx ctx, int n)
+{
+ auto res = isl_ast_node_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+ast_node_list::ast_node_list(isl::ast_node el)
+{
+ auto res = isl_ast_node_list_from_ast_node(el.release());
+ ptr = res;
+}
+
+ast_node_list &ast_node_list::operator=(ast_node_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+ast_node_list::~ast_node_list() {
+ if (ptr)
+ isl_ast_node_list_free(ptr);
+}
+
+__isl_give isl_ast_node_list *ast_node_list::copy() const & {
+ return isl_ast_node_list_copy(ptr);
+}
+
+__isl_keep isl_ast_node_list *ast_node_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_ast_node_list *ast_node_list::release() {
+ isl_ast_node_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool ast_node_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx ast_node_list::ctx() const {
+ return isl::ctx(isl_ast_node_list_get_ctx(ptr));
+}
+
+isl::ast_node_list ast_node_list::add(isl::ast_node el) const
+{
+ auto res = isl_ast_node_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::ast_node ast_node_list::at(int index) const
+{
+ auto res = isl_ast_node_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::ast_node ast_node_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::ast_node_list ast_node_list::clear() const
+{
+ auto res = isl_ast_node_list_clear(copy());
+ return manage(res);
+}
+
+isl::ast_node_list ast_node_list::concat(isl::ast_node_list list2) const
+{
+ auto res = isl_ast_node_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::ast_node_list ast_node_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_ast_node_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat ast_node_list::foreach(const std::function<stat(isl::ast_node)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::ast_node)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_ast_node *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_ast_node_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::ast_node_list ast_node_list::insert(unsigned int pos, isl::ast_node el) const
+{
+ auto res = isl_ast_node_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+class size ast_node_list::size() const
+{
+ auto res = isl_ast_node_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_node_list &obj)
+{
+ char *str = isl_ast_node_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_node_mark
+ast_node_mark::ast_node_mark()
+ : ast_node() {}
+
+ast_node_mark::ast_node_mark(const ast_node_mark &obj)
+ : ast_node(obj)
+{
+}
+
+ast_node_mark::ast_node_mark(__isl_take isl_ast_node *ptr)
+ : ast_node(ptr) {}
+
+ast_node_mark &ast_node_mark::operator=(ast_node_mark obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_node_mark::ctx() const {
+ return isl::ctx(isl_ast_node_get_ctx(ptr));
+}
+
+isl::id ast_node_mark::id() const
+{
+ auto res = isl_ast_node_mark_get_id(get());
+ return manage(res);
+}
+
+isl::id ast_node_mark::get_id() const
+{
+ return id();
+}
+
+isl::ast_node ast_node_mark::node() const
+{
+ auto res = isl_ast_node_mark_get_node(get());
+ return manage(res);
+}
+
+isl::ast_node ast_node_mark::get_node() const
+{
+ return node();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_node_mark &obj)
+{
+ char *str = isl_ast_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::ast_node_user
+ast_node_user::ast_node_user()
+ : ast_node() {}
+
+ast_node_user::ast_node_user(const ast_node_user &obj)
+ : ast_node(obj)
+{
+}
+
+ast_node_user::ast_node_user(__isl_take isl_ast_node *ptr)
+ : ast_node(ptr) {}
+
+ast_node_user &ast_node_user::operator=(ast_node_user obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx ast_node_user::ctx() const {
+ return isl::ctx(isl_ast_node_get_ctx(ptr));
+}
+
+isl::ast_expr ast_node_user::expr() const
+{
+ auto res = isl_ast_node_user_get_expr(get());
+ return manage(res);
+}
+
+isl::ast_expr ast_node_user::get_expr() const
+{
+ return expr();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const ast_node_user &obj)
+{
+ char *str = isl_ast_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::basic_map
+basic_map manage(__isl_take isl_basic_map *ptr) {
+ return basic_map(ptr);
+}
+basic_map manage_copy(__isl_keep isl_basic_map *ptr) {
+ ptr = isl_basic_map_copy(ptr);
+ return basic_map(ptr);
+}
+
+basic_map::basic_map()
+ : ptr(nullptr) {}
+
+basic_map::basic_map(const basic_map &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+basic_map::basic_map(__isl_take isl_basic_map *ptr)
+ : ptr(ptr) {}
+
+basic_map::basic_map(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_basic_map_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+basic_map &basic_map::operator=(basic_map obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+basic_map::~basic_map() {
+ if (ptr)
+ isl_basic_map_free(ptr);
+}
+
+__isl_give isl_basic_map *basic_map::copy() const & {
+ return isl_basic_map_copy(ptr);
+}
+
+__isl_keep isl_basic_map *basic_map::get() const {
+ return ptr;
+}
+
+__isl_give isl_basic_map *basic_map::release() {
+ isl_basic_map *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool basic_map::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx basic_map::ctx() const {
+ return isl::ctx(isl_basic_map_get_ctx(ptr));
+}
+
+isl::map basic_map::add_constraint(const isl::constraint &constraint) const
+{
+ return isl::map(*this).add_constraint(constraint);
+}
+
+isl::map basic_map::add_dims(isl::dim type, unsigned int n) const
+{
+ return isl::map(*this).add_dims(type, n);
+}
+
+isl::basic_map basic_map::affine_hull() const
+{
+ auto res = isl_basic_map_affine_hull(copy());
+ return manage(res);
+}
+
+isl::map basic_map::align_params(const isl::space &model) const
+{
+ return isl::map(*this).align_params(model);
+}
+
+isl::basic_map basic_map::apply_domain(isl::basic_map bmap2) const
+{
+ auto res = isl_basic_map_apply_domain(copy(), bmap2.release());
+ return manage(res);
+}
+
+isl::map basic_map::apply_domain(const isl::map &map2) const
+{
+ return isl::map(*this).apply_domain(map2);
+}
+
+isl::union_map basic_map::apply_domain(const isl::union_map &umap2) const
+{
+ return isl::map(*this).apply_domain(umap2);
+}
+
+isl::basic_map basic_map::apply_range(isl::basic_map bmap2) const
+{
+ auto res = isl_basic_map_apply_range(copy(), bmap2.release());
+ return manage(res);
+}
+
+isl::map basic_map::apply_range(const isl::map &map2) const
+{
+ return isl::map(*this).apply_range(map2);
+}
+
+isl::union_map basic_map::apply_range(const isl::union_map &umap2) const
+{
+ return isl::map(*this).apply_range(umap2);
+}
+
+isl::map basic_map::as_map() const
+{
+ return isl::map(*this).as_map();
+}
+
+isl::multi_union_pw_aff basic_map::as_multi_union_pw_aff() const
+{
+ return isl::map(*this).as_multi_union_pw_aff();
+}
+
+isl::pw_multi_aff basic_map::as_pw_multi_aff() const
+{
+ return isl::map(*this).as_pw_multi_aff();
+}
+
+isl::union_pw_multi_aff basic_map::as_union_pw_multi_aff() const
+{
+ return isl::map(*this).as_union_pw_multi_aff();
+}
+
+isl::basic_map_list basic_map::basic_map_list() const
+{
+ return isl::map(*this).basic_map_list();
+}
+
+isl::set basic_map::bind_domain(const isl::multi_id &tuple) const
+{
+ return isl::map(*this).bind_domain(tuple);
+}
+
+isl::set basic_map::bind_range(const isl::multi_id &tuple) const
+{
+ return isl::map(*this).bind_range(tuple);
+}
+
+boolean basic_map::can_curry() const
+{
+ return isl::map(*this).can_curry();
+}
+
+isl::map basic_map::coalesce() const
+{
+ return isl::map(*this).coalesce();
+}
+
+isl::map basic_map::complement() const
+{
+ return isl::map(*this).complement();
+}
+
+isl::union_map basic_map::compute_divs() const
+{
+ return isl::map(*this).compute_divs();
+}
+
+isl::map basic_map::curry() const
+{
+ return isl::map(*this).curry();
+}
+
+isl::basic_set basic_map::deltas() const
+{
+ auto res = isl_basic_map_deltas(copy());
+ return manage(res);
+}
+
+isl::basic_map basic_map::detect_equalities() const
+{
+ auto res = isl_basic_map_detect_equalities(copy());
+ return manage(res);
+}
+
+class size basic_map::dim(isl::dim type) const
+{
+ return isl::map(*this).dim(type);
+}
+
+isl::pw_aff basic_map::dim_max(int pos) const
+{
+ return isl::map(*this).dim_max(pos);
+}
+
+isl::pw_aff basic_map::dim_min(int pos) const
+{
+ return isl::map(*this).dim_min(pos);
+}
+
+isl::basic_set basic_map::domain() const
+{
+ auto res = isl_basic_map_domain(copy());
+ return manage(res);
+}
+
+isl::map basic_map::domain_factor_domain() const
+{
+ return isl::map(*this).domain_factor_domain();
+}
+
+isl::map basic_map::domain_factor_range() const
+{
+ return isl::map(*this).domain_factor_range();
+}
+
+isl::map basic_map::domain_map() const
+{
+ return isl::map(*this).domain_map();
+}
+
+isl::union_pw_multi_aff basic_map::domain_map_union_pw_multi_aff() const
+{
+ return isl::map(*this).domain_map_union_pw_multi_aff();
+}
+
+isl::map basic_map::domain_product(const isl::map &map2) const
+{
+ return isl::map(*this).domain_product(map2);
+}
+
+isl::union_map basic_map::domain_product(const isl::union_map &umap2) const
+{
+ return isl::map(*this).domain_product(umap2);
+}
+
+class size basic_map::domain_tuple_dim() const
+{
+ return isl::map(*this).domain_tuple_dim();
+}
+
+isl::id basic_map::domain_tuple_id() const
+{
+ return isl::map(*this).domain_tuple_id();
+}
+
+isl::map basic_map::eq_at(const isl::multi_pw_aff &mpa) const
+{
+ return isl::map(*this).eq_at(mpa);
+}
+
+isl::union_map basic_map::eq_at(const isl::multi_union_pw_aff &mupa) const
+{
+ return isl::map(*this).eq_at(mupa);
+}
+
+isl::basic_map basic_map::equal(isl::space space, unsigned int n_equal)
+{
+ auto res = isl_basic_map_equal(space.release(), n_equal);
+ return manage(res);
+}
+
+isl::basic_map basic_map::equate(isl::dim type1, int pos1, isl::dim type2, int pos2) const
+{
+ auto res = isl_basic_map_equate(copy(), static_cast<enum isl_dim_type>(type1), pos1, static_cast<enum isl_dim_type>(type2), pos2);
+ return manage(res);
+}
+
+boolean basic_map::every_map(const std::function<boolean(isl::map)> &test) const
+{
+ return isl::map(*this).every_map(test);
+}
+
+isl::map basic_map::extract_map(const isl::space &space) const
+{
+ return isl::map(*this).extract_map(space);
+}
+
+isl::map basic_map::factor_domain() const
+{
+ return isl::map(*this).factor_domain();
+}
+
+isl::map basic_map::factor_range() const
+{
+ return isl::map(*this).factor_range();
+}
+
+isl::basic_map basic_map::fix_si(isl::dim type, unsigned int pos, int value) const
+{
+ auto res = isl_basic_map_fix_si(copy(), static_cast<enum isl_dim_type>(type), pos, value);
+ return manage(res);
+}
+
+isl::basic_map basic_map::fix_val(isl::dim type, unsigned int pos, isl::val v) const
+{
+ auto res = isl_basic_map_fix_val(copy(), static_cast<enum isl_dim_type>(type), pos, v.release());
+ return manage(res);
+}
+
+isl::basic_map basic_map::fix_val(isl::dim type, unsigned int pos, long v) const
+{
+ return this->fix_val(type, pos, isl::val(ctx(), v));
+}
+
+isl::union_map basic_map::fixed_power(const isl::val &exp) const
+{
+ return isl::map(*this).fixed_power(exp);
+}
+
+isl::union_map basic_map::fixed_power(long exp) const
+{
+ return this->fixed_power(isl::val(ctx(), exp));
+}
+
+isl::map basic_map::flat_range_product(const isl::map &map2) const
+{
+ return isl::map(*this).flat_range_product(map2);
+}
+
+isl::union_map basic_map::flat_range_product(const isl::union_map &umap2) const
+{
+ return isl::map(*this).flat_range_product(umap2);
+}
+
+isl::basic_map basic_map::flatten() const
+{
+ auto res = isl_basic_map_flatten(copy());
+ return manage(res);
+}
+
+isl::basic_map basic_map::flatten_domain() const
+{
+ auto res = isl_basic_map_flatten_domain(copy());
+ return manage(res);
+}
+
+isl::basic_map basic_map::flatten_range() const
+{
+ auto res = isl_basic_map_flatten_range(copy());
+ return manage(res);
+}
+
+isl::map basic_map::floordiv_val(const isl::val &d) const
+{
+ return isl::map(*this).floordiv_val(d);
+}
+
+isl::map basic_map::floordiv_val(long d) const
+{
+ return this->floordiv_val(isl::val(ctx(), d));
+}
+
+stat basic_map::foreach_basic_map(const std::function<stat(isl::basic_map)> &fn) const
+{
+ return isl::map(*this).foreach_basic_map(fn);
+}
+
+stat basic_map::foreach_map(const std::function<stat(isl::map)> &fn) const
+{
+ return isl::map(*this).foreach_map(fn);
+}
+
+isl::basic_map basic_map::from_aff(isl::aff aff)
+{
+ auto res = isl_basic_map_from_aff(aff.release());
+ return manage(res);
+}
+
+isl::basic_map basic_map::from_domain_and_range(isl::basic_set domain, isl::basic_set range)
+{
+ auto res = isl_basic_map_from_domain_and_range(domain.release(), range.release());
+ return manage(res);
+}
+
+isl::basic_map basic_map::gist(isl::basic_map context) const
+{
+ auto res = isl_basic_map_gist(copy(), context.release());
+ return manage(res);
+}
+
+isl::map basic_map::gist(const isl::map &context) const
+{
+ return isl::map(*this).gist(context);
+}
+
+isl::union_map basic_map::gist(const isl::union_map &context) const
+{
+ return isl::map(*this).gist(context);
+}
+
+isl::map basic_map::gist_domain(const isl::set &context) const
+{
+ return isl::map(*this).gist_domain(context);
+}
+
+isl::union_map basic_map::gist_domain(const isl::union_set &uset) const
+{
+ return isl::map(*this).gist_domain(uset);
+}
+
+isl::map basic_map::gist_params(const isl::set &context) const
+{
+ return isl::map(*this).gist_params(context);
+}
+
+isl::union_map basic_map::gist_range(const isl::union_set &uset) const
+{
+ return isl::map(*this).gist_range(uset);
+}
+
+boolean basic_map::has_domain_tuple_id() const
+{
+ return isl::map(*this).has_domain_tuple_id();
+}
+
+boolean basic_map::has_equal_space(const isl::map &map2) const
+{
+ return isl::map(*this).has_equal_space(map2);
+}
+
+boolean basic_map::has_range_tuple_id() const
+{
+ return isl::map(*this).has_range_tuple_id();
+}
+
+boolean basic_map::has_tuple_id(isl::dim type) const
+{
+ return isl::map(*this).has_tuple_id(type);
+}
+
+boolean basic_map::has_tuple_name(isl::dim type) const
+{
+ return isl::map(*this).has_tuple_name(type);
+}
+
+isl::basic_map basic_map::intersect(isl::basic_map bmap2) const
+{
+ auto res = isl_basic_map_intersect(copy(), bmap2.release());
+ return manage(res);
+}
+
+isl::map basic_map::intersect(const isl::map &map2) const
+{
+ return isl::map(*this).intersect(map2);
+}
+
+isl::union_map basic_map::intersect(const isl::union_map &umap2) const
+{
+ return isl::map(*this).intersect(umap2);
+}
+
+isl::basic_map basic_map::intersect_domain(isl::basic_set bset) const
+{
+ auto res = isl_basic_map_intersect_domain(copy(), bset.release());
+ return manage(res);
+}
+
+isl::map basic_map::intersect_domain(const isl::set &set) const
+{
+ return isl::map(*this).intersect_domain(set);
+}
+
+isl::union_map basic_map::intersect_domain(const isl::space &space) const
+{
+ return isl::map(*this).intersect_domain(space);
+}
+
+isl::union_map basic_map::intersect_domain(const isl::union_set &uset) const
+{
+ return isl::map(*this).intersect_domain(uset);
+}
+
+isl::basic_map basic_map::intersect_domain(const isl::point &bset) const
+{
+ return this->intersect_domain(isl::basic_set(bset));
+}
+
+isl::map basic_map::intersect_domain_factor_domain(const isl::map &factor) const
+{
+ return isl::map(*this).intersect_domain_factor_domain(factor);
+}
+
+isl::union_map basic_map::intersect_domain_factor_domain(const isl::union_map &factor) const
+{
+ return isl::map(*this).intersect_domain_factor_domain(factor);
+}
+
+isl::map basic_map::intersect_domain_factor_range(const isl::map &factor) const
+{
+ return isl::map(*this).intersect_domain_factor_range(factor);
+}
+
+isl::union_map basic_map::intersect_domain_factor_range(const isl::union_map &factor) const
+{
+ return isl::map(*this).intersect_domain_factor_range(factor);
+}
+
+isl::map basic_map::intersect_params(const isl::set &params) const
+{
+ return isl::map(*this).intersect_params(params);
+}
+
+isl::basic_map basic_map::intersect_range(isl::basic_set bset) const
+{
+ auto res = isl_basic_map_intersect_range(copy(), bset.release());
+ return manage(res);
+}
+
+isl::map basic_map::intersect_range(const isl::set &set) const
+{
+ return isl::map(*this).intersect_range(set);
+}
+
+isl::union_map basic_map::intersect_range(const isl::space &space) const
+{
+ return isl::map(*this).intersect_range(space);
+}
+
+isl::union_map basic_map::intersect_range(const isl::union_set &uset) const
+{
+ return isl::map(*this).intersect_range(uset);
+}
+
+isl::basic_map basic_map::intersect_range(const isl::point &bset) const
+{
+ return this->intersect_range(isl::basic_set(bset));
+}
+
+isl::map basic_map::intersect_range_factor_domain(const isl::map &factor) const
+{
+ return isl::map(*this).intersect_range_factor_domain(factor);
+}
+
+isl::union_map basic_map::intersect_range_factor_domain(const isl::union_map &factor) const
+{
+ return isl::map(*this).intersect_range_factor_domain(factor);
+}
+
+isl::map basic_map::intersect_range_factor_range(const isl::map &factor) const
+{
+ return isl::map(*this).intersect_range_factor_range(factor);
+}
+
+isl::union_map basic_map::intersect_range_factor_range(const isl::union_map &factor) const
+{
+ return isl::map(*this).intersect_range_factor_range(factor);
+}
+
+boolean basic_map::involves_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::map(*this).involves_dims(type, first, n);
+}
+
+boolean basic_map::is_bijective() const
+{
+ return isl::map(*this).is_bijective();
+}
+
+boolean basic_map::is_disjoint(const isl::map &map2) const
+{
+ return isl::map(*this).is_disjoint(map2);
+}
+
+boolean basic_map::is_disjoint(const isl::union_map &umap2) const
+{
+ return isl::map(*this).is_disjoint(umap2);
+}
+
+boolean basic_map::is_empty() const
+{
+ auto res = isl_basic_map_is_empty(get());
+ return manage(res);
+}
+
+boolean basic_map::is_equal(const isl::basic_map &bmap2) const
+{
+ auto res = isl_basic_map_is_equal(get(), bmap2.get());
+ return manage(res);
+}
+
+boolean basic_map::is_equal(const isl::map &map2) const
+{
+ return isl::map(*this).is_equal(map2);
+}
+
+boolean basic_map::is_equal(const isl::union_map &umap2) const
+{
+ return isl::map(*this).is_equal(umap2);
+}
+
+boolean basic_map::is_injective() const
+{
+ return isl::map(*this).is_injective();
+}
+
+boolean basic_map::is_single_valued() const
+{
+ return isl::map(*this).is_single_valued();
+}
+
+boolean basic_map::is_strict_subset(const isl::map &map2) const
+{
+ return isl::map(*this).is_strict_subset(map2);
+}
+
+boolean basic_map::is_strict_subset(const isl::union_map &umap2) const
+{
+ return isl::map(*this).is_strict_subset(umap2);
+}
+
+boolean basic_map::is_subset(const isl::basic_map &bmap2) const
+{
+ auto res = isl_basic_map_is_subset(get(), bmap2.get());
+ return manage(res);
+}
+
+boolean basic_map::is_subset(const isl::map &map2) const
+{
+ return isl::map(*this).is_subset(map2);
+}
+
+boolean basic_map::is_subset(const isl::union_map &umap2) const
+{
+ return isl::map(*this).is_subset(umap2);
+}
+
+boolean basic_map::isa_map() const
+{
+ return isl::map(*this).isa_map();
+}
+
+isl::map basic_map::lex_ge_at(const isl::multi_pw_aff &mpa) const
+{
+ return isl::map(*this).lex_ge_at(mpa);
+}
+
+isl::map basic_map::lex_gt_at(const isl::multi_pw_aff &mpa) const
+{
+ return isl::map(*this).lex_gt_at(mpa);
+}
+
+isl::map basic_map::lex_le_at(const isl::multi_pw_aff &mpa) const
+{
+ return isl::map(*this).lex_le_at(mpa);
+}
+
+isl::map basic_map::lex_lt_at(const isl::multi_pw_aff &mpa) const
+{
+ return isl::map(*this).lex_lt_at(mpa);
+}
+
+isl::map basic_map::lexmax() const
+{
+ auto res = isl_basic_map_lexmax(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff basic_map::lexmax_pw_multi_aff() const
+{
+ return isl::map(*this).lexmax_pw_multi_aff();
+}
+
+isl::map basic_map::lexmin() const
+{
+ auto res = isl_basic_map_lexmin(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff basic_map::lexmin_pw_multi_aff() const
+{
+ return isl::map(*this).lexmin_pw_multi_aff();
+}
+
+isl::map basic_map::lower_bound(const isl::multi_pw_aff &lower) const
+{
+ return isl::map(*this).lower_bound(lower);
+}
+
+isl::map basic_map::lower_bound_si(isl::dim type, unsigned int pos, int value) const
+{
+ return isl::map(*this).lower_bound_si(type, pos, value);
+}
+
+isl::map_list basic_map::map_list() const
+{
+ return isl::map(*this).map_list();
+}
+
+isl::multi_pw_aff basic_map::max_multi_pw_aff() const
+{
+ return isl::map(*this).max_multi_pw_aff();
+}
+
+isl::multi_pw_aff basic_map::min_multi_pw_aff() const
+{
+ return isl::map(*this).min_multi_pw_aff();
+}
+
+isl::map basic_map::move_dims(isl::dim dst_type, unsigned int dst_pos, isl::dim src_type, unsigned int src_pos, unsigned int n) const
+{
+ return isl::map(*this).move_dims(dst_type, dst_pos, src_type, src_pos, n);
+}
+
+class size basic_map::n_basic_map() const
+{
+ return isl::map(*this).n_basic_map();
+}
+
+isl::map basic_map::order_lt(isl::dim type1, int pos1, isl::dim type2, int pos2) const
+{
+ return isl::map(*this).order_lt(type1, pos1, type2, pos2);
+}
+
+isl::set basic_map::params() const
+{
+ return isl::map(*this).params();
+}
+
+isl::val basic_map::plain_get_val_if_fixed(isl::dim type, unsigned int pos) const
+{
+ auto res = isl_basic_map_plain_get_val_if_fixed(get(), static_cast<enum isl_dim_type>(type), pos);
+ return manage(res);
+}
+
+isl::basic_map basic_map::polyhedral_hull() const
+{
+ return isl::map(*this).polyhedral_hull();
+}
+
+isl::map basic_map::preimage_domain(const isl::multi_aff &ma) const
+{
+ return isl::map(*this).preimage_domain(ma);
+}
+
+isl::map basic_map::preimage_domain(const isl::multi_pw_aff &mpa) const
+{
+ return isl::map(*this).preimage_domain(mpa);
+}
+
+isl::map basic_map::preimage_domain(const isl::pw_multi_aff &pma) const
+{
+ return isl::map(*this).preimage_domain(pma);
+}
+
+isl::union_map basic_map::preimage_domain(const isl::union_pw_multi_aff &upma) const
+{
+ return isl::map(*this).preimage_domain(upma);
+}
+
+isl::map basic_map::preimage_range(const isl::multi_aff &ma) const
+{
+ return isl::map(*this).preimage_range(ma);
+}
+
+isl::map basic_map::preimage_range(const isl::pw_multi_aff &pma) const
+{
+ return isl::map(*this).preimage_range(pma);
+}
+
+isl::union_map basic_map::preimage_range(const isl::union_pw_multi_aff &upma) const
+{
+ return isl::map(*this).preimage_range(upma);
+}
+
+isl::map basic_map::product(const isl::map &map2) const
+{
+ return isl::map(*this).product(map2);
+}
+
+isl::union_map basic_map::product(const isl::union_map &umap2) const
+{
+ return isl::map(*this).product(umap2);
+}
+
+isl::map basic_map::project_out(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::map(*this).project_out(type, first, n);
+}
+
+isl::map basic_map::project_out_all_params() const
+{
+ return isl::map(*this).project_out_all_params();
+}
+
+isl::set basic_map::range() const
+{
+ return isl::map(*this).range();
+}
+
+isl::map basic_map::range_factor_domain() const
+{
+ return isl::map(*this).range_factor_domain();
+}
+
+isl::map basic_map::range_factor_range() const
+{
+ return isl::map(*this).range_factor_range();
+}
+
+isl::fixed_box basic_map::range_lattice_tile() const
+{
+ return isl::map(*this).range_lattice_tile();
+}
+
+isl::map basic_map::range_map() const
+{
+ return isl::map(*this).range_map();
+}
+
+isl::map basic_map::range_product(const isl::map &map2) const
+{
+ return isl::map(*this).range_product(map2);
+}
+
+isl::union_map basic_map::range_product(const isl::union_map &umap2) const
+{
+ return isl::map(*this).range_product(umap2);
+}
+
+isl::map basic_map::range_reverse() const
+{
+ return isl::map(*this).range_reverse();
+}
+
+isl::fixed_box basic_map::range_simple_fixed_box_hull() const
+{
+ return isl::map(*this).range_simple_fixed_box_hull();
+}
+
+class size basic_map::range_tuple_dim() const
+{
+ return isl::map(*this).range_tuple_dim();
+}
+
+isl::id basic_map::range_tuple_id() const
+{
+ return isl::map(*this).range_tuple_id();
+}
+
+isl::basic_map basic_map::reverse() const
+{
+ auto res = isl_basic_map_reverse(copy());
+ return manage(res);
+}
+
+isl::basic_map basic_map::sample() const
+{
+ auto res = isl_basic_map_sample(copy());
+ return manage(res);
+}
+
+isl::map basic_map::set_domain_tuple(const isl::id &id) const
+{
+ return isl::map(*this).set_domain_tuple(id);
+}
+
+isl::map basic_map::set_domain_tuple(const std::string &id) const
+{
+ return this->set_domain_tuple(isl::id(ctx(), id));
+}
+
+isl::map basic_map::set_range_tuple(const isl::id &id) const
+{
+ return isl::map(*this).set_range_tuple(id);
+}
+
+isl::map basic_map::set_range_tuple(const std::string &id) const
+{
+ return this->set_range_tuple(isl::id(ctx(), id));
+}
+
+isl::map basic_map::set_tuple_id(isl::dim type, const isl::id &id) const
+{
+ return isl::map(*this).set_tuple_id(type, id);
+}
+
+isl::map basic_map::set_tuple_id(isl::dim type, const std::string &id) const
+{
+ return this->set_tuple_id(type, isl::id(ctx(), id));
+}
+
+isl::space basic_map::space() const
+{
+ return isl::map(*this).space();
+}
+
+isl::map basic_map::subtract(const isl::map &map2) const
+{
+ return isl::map(*this).subtract(map2);
+}
+
+isl::union_map basic_map::subtract(const isl::union_map &umap2) const
+{
+ return isl::map(*this).subtract(umap2);
+}
+
+isl::union_map basic_map::subtract_domain(const isl::union_set &dom) const
+{
+ return isl::map(*this).subtract_domain(dom);
+}
+
+isl::union_map basic_map::subtract_range(const isl::union_set &dom) const
+{
+ return isl::map(*this).subtract_range(dom);
+}
+
+isl::map basic_map::sum(const isl::map &map2) const
+{
+ return isl::map(*this).sum(map2);
+}
+
+isl::basic_map_list basic_map::to_list() const
+{
+ auto res = isl_basic_map_to_list(copy());
+ return manage(res);
+}
+
+isl::union_map basic_map::to_union_map() const
+{
+ return isl::map(*this).to_union_map();
+}
+
+isl::id basic_map::tuple_id(isl::dim type) const
+{
+ return isl::map(*this).tuple_id(type);
+}
+
+isl::map basic_map::uncurry() const
+{
+ return isl::map(*this).uncurry();
+}
+
+isl::map basic_map::unite(isl::basic_map bmap2) const
+{
+ auto res = isl_basic_map_union(copy(), bmap2.release());
+ return manage(res);
+}
+
+isl::map basic_map::unite(const isl::map &map2) const
+{
+ return isl::map(*this).unite(map2);
+}
+
+isl::union_map basic_map::unite(const isl::union_map &umap2) const
+{
+ return isl::map(*this).unite(umap2);
+}
+
+isl::basic_map basic_map::universe(isl::space space)
+{
+ auto res = isl_basic_map_universe(space.release());
+ return manage(res);
+}
+
+isl::basic_map basic_map::unshifted_simple_hull() const
+{
+ return isl::map(*this).unshifted_simple_hull();
+}
+
+isl::map basic_map::upper_bound(const isl::multi_pw_aff &upper) const
+{
+ return isl::map(*this).upper_bound(upper);
+}
+
+isl::map basic_map::upper_bound_si(isl::dim type, unsigned int pos, int value) const
+{
+ return isl::map(*this).upper_bound_si(type, pos, value);
+}
+
+isl::set basic_map::wrap() const
+{
+ return isl::map(*this).wrap();
+}
+
+isl::map basic_map::zip() const
+{
+ return isl::map(*this).zip();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const basic_map &obj)
+{
+ char *str = isl_basic_map_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::basic_map_list
+basic_map_list manage(__isl_take isl_basic_map_list *ptr) {
+ return basic_map_list(ptr);
+}
+basic_map_list manage_copy(__isl_keep isl_basic_map_list *ptr) {
+ ptr = isl_basic_map_list_copy(ptr);
+ return basic_map_list(ptr);
+}
+
+basic_map_list::basic_map_list()
+ : ptr(nullptr) {}
+
+basic_map_list::basic_map_list(const basic_map_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+basic_map_list::basic_map_list(__isl_take isl_basic_map_list *ptr)
+ : ptr(ptr) {}
+
+basic_map_list::basic_map_list(isl::ctx ctx, int n)
+{
+ auto res = isl_basic_map_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+basic_map_list::basic_map_list(isl::basic_map el)
+{
+ auto res = isl_basic_map_list_from_basic_map(el.release());
+ ptr = res;
+}
+
+basic_map_list &basic_map_list::operator=(basic_map_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+basic_map_list::~basic_map_list() {
+ if (ptr)
+ isl_basic_map_list_free(ptr);
+}
+
+__isl_give isl_basic_map_list *basic_map_list::copy() const & {
+ return isl_basic_map_list_copy(ptr);
+}
+
+__isl_keep isl_basic_map_list *basic_map_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_basic_map_list *basic_map_list::release() {
+ isl_basic_map_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool basic_map_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx basic_map_list::ctx() const {
+ return isl::ctx(isl_basic_map_list_get_ctx(ptr));
+}
+
+isl::basic_map_list basic_map_list::add(isl::basic_map el) const
+{
+ auto res = isl_basic_map_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::basic_map basic_map_list::at(int index) const
+{
+ auto res = isl_basic_map_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::basic_map basic_map_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::basic_map_list basic_map_list::clear() const
+{
+ auto res = isl_basic_map_list_clear(copy());
+ return manage(res);
+}
+
+isl::basic_map_list basic_map_list::concat(isl::basic_map_list list2) const
+{
+ auto res = isl_basic_map_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::basic_map_list basic_map_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_basic_map_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat basic_map_list::foreach(const std::function<stat(isl::basic_map)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::basic_map)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_basic_map *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_basic_map_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::basic_map_list basic_map_list::insert(unsigned int pos, isl::basic_map el) const
+{
+ auto res = isl_basic_map_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+class size basic_map_list::size() const
+{
+ auto res = isl_basic_map_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const basic_map_list &obj)
+{
+ char *str = isl_basic_map_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::basic_set
+basic_set manage(__isl_take isl_basic_set *ptr) {
+ return basic_set(ptr);
+}
+basic_set manage_copy(__isl_keep isl_basic_set *ptr) {
+ ptr = isl_basic_set_copy(ptr);
+ return basic_set(ptr);
+}
+
+basic_set::basic_set()
+ : ptr(nullptr) {}
+
+basic_set::basic_set(const basic_set &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+basic_set::basic_set(__isl_take isl_basic_set *ptr)
+ : ptr(ptr) {}
+
+basic_set::basic_set(isl::point pnt)
+{
+ auto res = isl_basic_set_from_point(pnt.release());
+ ptr = res;
+}
+
+basic_set::basic_set(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_basic_set_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+basic_set &basic_set::operator=(basic_set obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+basic_set::~basic_set() {
+ if (ptr)
+ isl_basic_set_free(ptr);
+}
+
+__isl_give isl_basic_set *basic_set::copy() const & {
+ return isl_basic_set_copy(ptr);
+}
+
+__isl_keep isl_basic_set *basic_set::get() const {
+ return ptr;
+}
+
+__isl_give isl_basic_set *basic_set::release() {
+ isl_basic_set *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool basic_set::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx basic_set::ctx() const {
+ return isl::ctx(isl_basic_set_get_ctx(ptr));
+}
+
+isl::set basic_set::add_constraint(const isl::constraint &constraint) const
+{
+ return isl::set(*this).add_constraint(constraint);
+}
+
+isl::set basic_set::add_dims(isl::dim type, unsigned int n) const
+{
+ return isl::set(*this).add_dims(type, n);
+}
+
+isl::basic_set basic_set::affine_hull() const
+{
+ auto res = isl_basic_set_affine_hull(copy());
+ return manage(res);
+}
+
+isl::set basic_set::align_params(const isl::space &model) const
+{
+ return isl::set(*this).align_params(model);
+}
+
+isl::basic_set basic_set::apply(isl::basic_map bmap) const
+{
+ auto res = isl_basic_set_apply(copy(), bmap.release());
+ return manage(res);
+}
+
+isl::set basic_set::apply(const isl::map &map) const
+{
+ return isl::set(*this).apply(map);
+}
+
+isl::union_set basic_set::apply(const isl::union_map &umap) const
+{
+ return isl::set(*this).apply(umap);
+}
+
+isl::pw_multi_aff basic_set::as_pw_multi_aff() const
+{
+ return isl::set(*this).as_pw_multi_aff();
+}
+
+isl::set basic_set::as_set() const
+{
+ return isl::set(*this).as_set();
+}
+
+isl::basic_set_list basic_set::basic_set_list() const
+{
+ return isl::set(*this).basic_set_list();
+}
+
+isl::set basic_set::bind(const isl::multi_id &tuple) const
+{
+ return isl::set(*this).bind(tuple);
+}
+
+isl::set basic_set::coalesce() const
+{
+ return isl::set(*this).coalesce();
+}
+
+isl::set basic_set::complement() const
+{
+ return isl::set(*this).complement();
+}
+
+isl::union_set basic_set::compute_divs() const
+{
+ return isl::set(*this).compute_divs();
+}
+
+boolean basic_set::contains(const isl::space &space) const
+{
+ return isl::set(*this).contains(space);
+}
+
+isl::basic_set basic_set::convex_hull() const
+{
+ return isl::set(*this).convex_hull();
+}
+
+isl::basic_set basic_set::detect_equalities() const
+{
+ auto res = isl_basic_set_detect_equalities(copy());
+ return manage(res);
+}
+
+class size basic_set::dim(isl::dim type) const
+{
+ auto res = isl_basic_set_dim(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+boolean basic_set::dim_has_any_lower_bound(isl::dim type, unsigned int pos) const
+{
+ return isl::set(*this).dim_has_any_lower_bound(type, pos);
+}
+
+isl::id basic_set::dim_id(isl::dim type, unsigned int pos) const
+{
+ return isl::set(*this).dim_id(type, pos);
+}
+
+isl::pw_aff basic_set::dim_max(int pos) const
+{
+ return isl::set(*this).dim_max(pos);
+}
+
+isl::val basic_set::dim_max_val(int pos) const
+{
+ auto res = isl_basic_set_dim_max_val(copy(), pos);
+ return manage(res);
+}
+
+isl::pw_aff basic_set::dim_min(int pos) const
+{
+ return isl::set(*this).dim_min(pos);
+}
+
+isl::val basic_set::dim_min_val(int pos) const
+{
+ return isl::set(*this).dim_min_val(pos);
+}
+
+std::string basic_set::dim_name(isl::dim type, unsigned int pos) const
+{
+ return isl::set(*this).dim_name(type, pos);
+}
+
+isl::aff basic_set::div(int pos) const
+{
+ auto res = isl_basic_set_get_div(get(), pos);
+ return manage(res);
+}
+
+isl::aff basic_set::get_div(int pos) const
+{
+ return div(pos);
+}
+
+isl::set basic_set::drop_constraints_involving_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::set(*this).drop_constraints_involving_dims(type, first, n);
+}
+
+isl::set basic_set::eliminate(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::set(*this).eliminate(type, first, n);
+}
+
+boolean basic_set::every_set(const std::function<boolean(isl::set)> &test) const
+{
+ return isl::set(*this).every_set(test);
+}
+
+isl::set basic_set::extract_set(const isl::space &space) const
+{
+ return isl::set(*this).extract_set(space);
+}
+
+int basic_set::find_dim_by_id(isl::dim type, const isl::id &id) const
+{
+ return isl::set(*this).find_dim_by_id(type, id);
+}
+
+int basic_set::find_dim_by_id(isl::dim type, const std::string &id) const
+{
+ return this->find_dim_by_id(type, isl::id(ctx(), id));
+}
+
+isl::basic_set basic_set::fix_si(isl::dim type, unsigned int pos, int value) const
+{
+ auto res = isl_basic_set_fix_si(copy(), static_cast<enum isl_dim_type>(type), pos, value);
+ return manage(res);
+}
+
+isl::basic_set basic_set::fix_val(isl::dim type, unsigned int pos, isl::val v) const
+{
+ auto res = isl_basic_set_fix_val(copy(), static_cast<enum isl_dim_type>(type), pos, v.release());
+ return manage(res);
+}
+
+isl::basic_set basic_set::fix_val(isl::dim type, unsigned int pos, long v) const
+{
+ return this->fix_val(type, pos, isl::val(ctx(), v));
+}
+
+isl::basic_set basic_set::flatten() const
+{
+ auto res = isl_basic_set_flatten(copy());
+ return manage(res);
+}
+
+stat basic_set::foreach_basic_set(const std::function<stat(isl::basic_set)> &fn) const
+{
+ return isl::set(*this).foreach_basic_set(fn);
+}
+
+stat basic_set::foreach_point(const std::function<stat(isl::point)> &fn) const
+{
+ return isl::set(*this).foreach_point(fn);
+}
+
+stat basic_set::foreach_set(const std::function<stat(isl::set)> &fn) const
+{
+ return isl::set(*this).foreach_set(fn);
+}
+
+isl::basic_set basic_set::gist(isl::basic_set context) const
+{
+ auto res = isl_basic_set_gist(copy(), context.release());
+ return manage(res);
+}
+
+isl::set basic_set::gist(const isl::set &context) const
+{
+ return isl::set(*this).gist(context);
+}
+
+isl::union_set basic_set::gist(const isl::union_set &context) const
+{
+ return isl::set(*this).gist(context);
+}
+
+isl::basic_set basic_set::gist(const isl::point &context) const
+{
+ return this->gist(isl::basic_set(context));
+}
+
+isl::set basic_set::gist_params(const isl::set &context) const
+{
+ return isl::set(*this).gist_params(context);
+}
+
+boolean basic_set::has_equal_space(const isl::set &set2) const
+{
+ return isl::set(*this).has_equal_space(set2);
+}
+
+isl::map basic_set::identity() const
+{
+ return isl::set(*this).identity();
+}
+
+isl::union_pw_multi_aff basic_set::identity_union_pw_multi_aff() const
+{
+ return isl::set(*this).identity_union_pw_multi_aff();
+}
+
+isl::pw_aff basic_set::indicator_function() const
+{
+ return isl::set(*this).indicator_function();
+}
+
+isl::set basic_set::insert_dims(isl::dim type, unsigned int pos, unsigned int n) const
+{
+ return isl::set(*this).insert_dims(type, pos, n);
+}
+
+isl::map basic_set::insert_domain(const isl::space &domain) const
+{
+ return isl::set(*this).insert_domain(domain);
+}
+
+isl::basic_set basic_set::intersect(isl::basic_set bset2) const
+{
+ auto res = isl_basic_set_intersect(copy(), bset2.release());
+ return manage(res);
+}
+
+isl::set basic_set::intersect(const isl::set &set2) const
+{
+ return isl::set(*this).intersect(set2);
+}
+
+isl::union_set basic_set::intersect(const isl::union_set &uset2) const
+{
+ return isl::set(*this).intersect(uset2);
+}
+
+isl::basic_set basic_set::intersect(const isl::point &bset2) const
+{
+ return this->intersect(isl::basic_set(bset2));
+}
+
+isl::basic_set basic_set::intersect_params(isl::basic_set bset2) const
+{
+ auto res = isl_basic_set_intersect_params(copy(), bset2.release());
+ return manage(res);
+}
+
+isl::set basic_set::intersect_params(const isl::set &params) const
+{
+ return isl::set(*this).intersect_params(params);
+}
+
+isl::basic_set basic_set::intersect_params(const isl::point &bset2) const
+{
+ return this->intersect_params(isl::basic_set(bset2));
+}
+
+boolean basic_set::involves_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::set(*this).involves_dims(type, first, n);
+}
+
+boolean basic_set::involves_locals() const
+{
+ return isl::set(*this).involves_locals();
+}
+
+boolean basic_set::is_bounded() const
+{
+ auto res = isl_basic_set_is_bounded(get());
+ return manage(res);
+}
+
+boolean basic_set::is_disjoint(const isl::set &set2) const
+{
+ return isl::set(*this).is_disjoint(set2);
+}
+
+boolean basic_set::is_disjoint(const isl::union_set &uset2) const
+{
+ return isl::set(*this).is_disjoint(uset2);
+}
+
+boolean basic_set::is_empty() const
+{
+ auto res = isl_basic_set_is_empty(get());
+ return manage(res);
+}
+
+boolean basic_set::is_equal(const isl::basic_set &bset2) const
+{
+ auto res = isl_basic_set_is_equal(get(), bset2.get());
+ return manage(res);
+}
+
+boolean basic_set::is_equal(const isl::set &set2) const
+{
+ return isl::set(*this).is_equal(set2);
+}
+
+boolean basic_set::is_equal(const isl::union_set &uset2) const
+{
+ return isl::set(*this).is_equal(uset2);
+}
+
+boolean basic_set::is_equal(const isl::point &bset2) const
+{
+ return this->is_equal(isl::basic_set(bset2));
+}
+
+boolean basic_set::is_params() const
+{
+ return isl::set(*this).is_params();
+}
+
+boolean basic_set::is_singleton() const
+{
+ return isl::set(*this).is_singleton();
+}
+
+boolean basic_set::is_strict_subset(const isl::set &set2) const
+{
+ return isl::set(*this).is_strict_subset(set2);
+}
+
+boolean basic_set::is_strict_subset(const isl::union_set &uset2) const
+{
+ return isl::set(*this).is_strict_subset(uset2);
+}
+
+boolean basic_set::is_subset(const isl::basic_set &bset2) const
+{
+ auto res = isl_basic_set_is_subset(get(), bset2.get());
+ return manage(res);
+}
+
+boolean basic_set::is_subset(const isl::set &set2) const
+{
+ return isl::set(*this).is_subset(set2);
+}
+
+boolean basic_set::is_subset(const isl::union_set &uset2) const
+{
+ return isl::set(*this).is_subset(uset2);
+}
+
+boolean basic_set::is_subset(const isl::point &bset2) const
+{
+ return this->is_subset(isl::basic_set(bset2));
+}
+
+boolean basic_set::is_wrapping() const
+{
+ auto res = isl_basic_set_is_wrapping(get());
+ return manage(res);
+}
+
+boolean basic_set::isa_set() const
+{
+ return isl::set(*this).isa_set();
+}
+
+isl::set basic_set::lexmax() const
+{
+ auto res = isl_basic_set_lexmax(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff basic_set::lexmax_pw_multi_aff() const
+{
+ return isl::set(*this).lexmax_pw_multi_aff();
+}
+
+isl::set basic_set::lexmin() const
+{
+ auto res = isl_basic_set_lexmin(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff basic_set::lexmin_pw_multi_aff() const
+{
+ return isl::set(*this).lexmin_pw_multi_aff();
+}
+
+isl::set basic_set::lower_bound(const isl::multi_pw_aff &lower) const
+{
+ return isl::set(*this).lower_bound(lower);
+}
+
+isl::set basic_set::lower_bound(const isl::multi_val &lower) const
+{
+ return isl::set(*this).lower_bound(lower);
+}
+
+isl::set basic_set::lower_bound_si(isl::dim type, unsigned int pos, int value) const
+{
+ return isl::set(*this).lower_bound_si(type, pos, value);
+}
+
+isl::set basic_set::lower_bound_val(isl::dim type, unsigned int pos, const isl::val &value) const
+{
+ return isl::set(*this).lower_bound_val(type, pos, value);
+}
+
+isl::set basic_set::lower_bound_val(isl::dim type, unsigned int pos, long value) const
+{
+ return this->lower_bound_val(type, pos, isl::val(ctx(), value));
+}
+
+isl::multi_pw_aff basic_set::max_multi_pw_aff() const
+{
+ return isl::set(*this).max_multi_pw_aff();
+}
+
+isl::val basic_set::max_val(const isl::aff &obj) const
+{
+ return isl::set(*this).max_val(obj);
+}
+
+isl::multi_pw_aff basic_set::min_multi_pw_aff() const
+{
+ return isl::set(*this).min_multi_pw_aff();
+}
+
+isl::val basic_set::min_val(const isl::aff &obj) const
+{
+ return isl::set(*this).min_val(obj);
+}
+
+class size basic_set::n_basic_set() const
+{
+ return isl::set(*this).n_basic_set();
+}
+
+isl::basic_set basic_set::params() const
+{
+ auto res = isl_basic_set_params(copy());
+ return manage(res);
+}
+
+isl::val basic_set::plain_get_val_if_fixed(isl::dim type, unsigned int pos) const
+{
+ return isl::set(*this).plain_get_val_if_fixed(type, pos);
+}
+
+isl::multi_val basic_set::plain_multi_val_if_fixed() const
+{
+ return isl::set(*this).plain_multi_val_if_fixed();
+}
+
+isl::basic_set basic_set::polyhedral_hull() const
+{
+ return isl::set(*this).polyhedral_hull();
+}
+
+isl::set basic_set::preimage(const isl::multi_aff &ma) const
+{
+ return isl::set(*this).preimage(ma);
+}
+
+isl::set basic_set::preimage(const isl::multi_pw_aff &mpa) const
+{
+ return isl::set(*this).preimage(mpa);
+}
+
+isl::set basic_set::preimage(const isl::pw_multi_aff &pma) const
+{
+ return isl::set(*this).preimage(pma);
+}
+
+isl::union_set basic_set::preimage(const isl::union_pw_multi_aff &upma) const
+{
+ return isl::set(*this).preimage(upma);
+}
+
+isl::set basic_set::product(const isl::set &set2) const
+{
+ return isl::set(*this).product(set2);
+}
+
+isl::basic_set basic_set::project_out(isl::dim type, unsigned int first, unsigned int n) const
+{
+ auto res = isl_basic_set_project_out(copy(), static_cast<enum isl_dim_type>(type), first, n);
+ return manage(res);
+}
+
+isl::set basic_set::project_out_all_params() const
+{
+ return isl::set(*this).project_out_all_params();
+}
+
+isl::set basic_set::project_out_param(const isl::id &id) const
+{
+ return isl::set(*this).project_out_param(id);
+}
+
+isl::set basic_set::project_out_param(const std::string &id) const
+{
+ return this->project_out_param(isl::id(ctx(), id));
+}
+
+isl::set basic_set::project_out_param(const isl::id_list &list) const
+{
+ return isl::set(*this).project_out_param(list);
+}
+
+isl::pw_multi_aff basic_set::pw_multi_aff_on_domain(const isl::multi_val &mv) const
+{
+ return isl::set(*this).pw_multi_aff_on_domain(mv);
+}
+
+isl::set basic_set::remove_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::set(*this).remove_dims(type, first, n);
+}
+
+isl::set basic_set::remove_divs() const
+{
+ return isl::set(*this).remove_divs();
+}
+
+isl::set basic_set::remove_redundancies() const
+{
+ return isl::set(*this).remove_redundancies();
+}
+
+isl::set basic_set::reset_tuple_id() const
+{
+ return isl::set(*this).reset_tuple_id();
+}
+
+isl::basic_set basic_set::sample() const
+{
+ auto res = isl_basic_set_sample(copy());
+ return manage(res);
+}
+
+isl::point basic_set::sample_point() const
+{
+ auto res = isl_basic_set_sample_point(copy());
+ return manage(res);
+}
+
+isl::set basic_set::set_dim_id(isl::dim type, unsigned int pos, const isl::id &id) const
+{
+ return isl::set(*this).set_dim_id(type, pos, id);
+}
+
+isl::set basic_set::set_dim_id(isl::dim type, unsigned int pos, const std::string &id) const
+{
+ return this->set_dim_id(type, pos, isl::id(ctx(), id));
+}
+
+isl::set_list basic_set::set_list() const
+{
+ return isl::set(*this).set_list();
+}
+
+isl::set basic_set::set_tuple_id(const isl::id &id) const
+{
+ return isl::set(*this).set_tuple_id(id);
+}
+
+isl::set basic_set::set_tuple_id(const std::string &id) const
+{
+ return this->set_tuple_id(isl::id(ctx(), id));
+}
+
+isl::fixed_box basic_set::simple_fixed_box_hull() const
+{
+ return isl::set(*this).simple_fixed_box_hull();
+}
+
+isl::basic_set basic_set::simple_hull() const
+{
+ return isl::set(*this).simple_hull();
+}
+
+isl::space basic_set::space() const
+{
+ auto res = isl_basic_set_get_space(get());
+ return manage(res);
+}
+
+isl::space basic_set::get_space() const
+{
+ return space();
+}
+
+isl::val basic_set::stride(int pos) const
+{
+ return isl::set(*this).stride(pos);
+}
+
+isl::set basic_set::subtract(const isl::set &set2) const
+{
+ return isl::set(*this).subtract(set2);
+}
+
+isl::union_set basic_set::subtract(const isl::union_set &uset2) const
+{
+ return isl::set(*this).subtract(uset2);
+}
+
+isl::basic_set_list basic_set::to_list() const
+{
+ auto res = isl_basic_set_to_list(copy());
+ return manage(res);
+}
+
+isl::set basic_set::to_set() const
+{
+ auto res = isl_basic_set_to_set(copy());
+ return manage(res);
+}
+
+isl::union_set basic_set::to_union_set() const
+{
+ return isl::set(*this).to_union_set();
+}
+
+isl::map basic_set::translation() const
+{
+ return isl::set(*this).translation();
+}
+
+class size basic_set::tuple_dim() const
+{
+ return isl::set(*this).tuple_dim();
+}
+
+isl::id basic_set::tuple_id() const
+{
+ return isl::set(*this).tuple_id();
+}
+
+std::string basic_set::tuple_name() const
+{
+ return isl::set(*this).tuple_name();
+}
+
+isl::set basic_set::unbind_params(const isl::multi_id &tuple) const
+{
+ return isl::set(*this).unbind_params(tuple);
+}
+
+isl::map basic_set::unbind_params_insert_domain(const isl::multi_id &domain) const
+{
+ return isl::set(*this).unbind_params_insert_domain(domain);
+}
+
+isl::set basic_set::unite(isl::basic_set bset2) const
+{
+ auto res = isl_basic_set_union(copy(), bset2.release());
+ return manage(res);
+}
+
+isl::set basic_set::unite(const isl::set &set2) const
+{
+ return isl::set(*this).unite(set2);
+}
+
+isl::union_set basic_set::unite(const isl::union_set &uset2) const
+{
+ return isl::set(*this).unite(uset2);
+}
+
+isl::set basic_set::unite(const isl::point &bset2) const
+{
+ return this->unite(isl::basic_set(bset2));
+}
+
+isl::basic_set basic_set::universe(isl::space space)
+{
+ auto res = isl_basic_set_universe(space.release());
+ return manage(res);
+}
+
+isl::basic_set basic_set::unshifted_simple_hull() const
+{
+ return isl::set(*this).unshifted_simple_hull();
+}
+
+isl::map basic_set::unwrap() const
+{
+ return isl::set(*this).unwrap();
+}
+
+isl::set basic_set::upper_bound(const isl::multi_pw_aff &upper) const
+{
+ return isl::set(*this).upper_bound(upper);
+}
+
+isl::set basic_set::upper_bound(const isl::multi_val &upper) const
+{
+ return isl::set(*this).upper_bound(upper);
+}
+
+isl::set basic_set::upper_bound_val(isl::dim type, unsigned int pos, const isl::val &value) const
+{
+ return isl::set(*this).upper_bound_val(type, pos, value);
+}
+
+isl::set basic_set::upper_bound_val(isl::dim type, unsigned int pos, long value) const
+{
+ return this->upper_bound_val(type, pos, isl::val(ctx(), value));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const basic_set &obj)
+{
+ char *str = isl_basic_set_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::basic_set_list
+basic_set_list manage(__isl_take isl_basic_set_list *ptr) {
+ return basic_set_list(ptr);
+}
+basic_set_list manage_copy(__isl_keep isl_basic_set_list *ptr) {
+ ptr = isl_basic_set_list_copy(ptr);
+ return basic_set_list(ptr);
+}
+
+basic_set_list::basic_set_list()
+ : ptr(nullptr) {}
+
+basic_set_list::basic_set_list(const basic_set_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+basic_set_list::basic_set_list(__isl_take isl_basic_set_list *ptr)
+ : ptr(ptr) {}
+
+basic_set_list::basic_set_list(isl::ctx ctx, int n)
+{
+ auto res = isl_basic_set_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+basic_set_list::basic_set_list(isl::basic_set el)
+{
+ auto res = isl_basic_set_list_from_basic_set(el.release());
+ ptr = res;
+}
+
+basic_set_list &basic_set_list::operator=(basic_set_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+basic_set_list::~basic_set_list() {
+ if (ptr)
+ isl_basic_set_list_free(ptr);
+}
+
+__isl_give isl_basic_set_list *basic_set_list::copy() const & {
+ return isl_basic_set_list_copy(ptr);
+}
+
+__isl_keep isl_basic_set_list *basic_set_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_basic_set_list *basic_set_list::release() {
+ isl_basic_set_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool basic_set_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx basic_set_list::ctx() const {
+ return isl::ctx(isl_basic_set_list_get_ctx(ptr));
+}
+
+isl::basic_set_list basic_set_list::add(isl::basic_set el) const
+{
+ auto res = isl_basic_set_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::basic_set basic_set_list::at(int index) const
+{
+ auto res = isl_basic_set_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::basic_set basic_set_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::basic_set_list basic_set_list::clear() const
+{
+ auto res = isl_basic_set_list_clear(copy());
+ return manage(res);
+}
+
+isl::basic_set_list basic_set_list::concat(isl::basic_set_list list2) const
+{
+ auto res = isl_basic_set_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::basic_set_list basic_set_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_basic_set_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat basic_set_list::foreach(const std::function<stat(isl::basic_set)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::basic_set)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_basic_set *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_basic_set_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::basic_set_list basic_set_list::insert(unsigned int pos, isl::basic_set el) const
+{
+ auto res = isl_basic_set_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+class size basic_set_list::size() const
+{
+ auto res = isl_basic_set_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const basic_set_list &obj)
+{
+ char *str = isl_basic_set_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::constraint
+constraint manage(__isl_take isl_constraint *ptr) {
+ return constraint(ptr);
+}
+constraint manage_copy(__isl_keep isl_constraint *ptr) {
+ ptr = isl_constraint_copy(ptr);
+ return constraint(ptr);
+}
+
+constraint::constraint()
+ : ptr(nullptr) {}
+
+constraint::constraint(const constraint &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+constraint::constraint(__isl_take isl_constraint *ptr)
+ : ptr(ptr) {}
+
+constraint &constraint::operator=(constraint obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+constraint::~constraint() {
+ if (ptr)
+ isl_constraint_free(ptr);
+}
+
+__isl_give isl_constraint *constraint::copy() const & {
+ return isl_constraint_copy(ptr);
+}
+
+__isl_keep isl_constraint *constraint::get() const {
+ return ptr;
+}
+
+__isl_give isl_constraint *constraint::release() {
+ isl_constraint *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool constraint::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx constraint::ctx() const {
+ return isl::ctx(isl_constraint_get_ctx(ptr));
+}
+
+isl::constraint constraint::alloc_equality(isl::local_space ls)
+{
+ auto res = isl_constraint_alloc_equality(ls.release());
+ return manage(res);
+}
+
+isl::constraint constraint::alloc_inequality(isl::local_space ls)
+{
+ auto res = isl_constraint_alloc_inequality(ls.release());
+ return manage(res);
+}
+
+isl::constraint constraint::set_coefficient_si(isl::dim type, int pos, int v) const
+{
+ auto res = isl_constraint_set_coefficient_si(copy(), static_cast<enum isl_dim_type>(type), pos, v);
+ return manage(res);
+}
+
+isl::constraint constraint::set_constant_si(int v) const
+{
+ auto res = isl_constraint_set_constant_si(copy(), v);
+ return manage(res);
+}
+
+isl::constraint constraint::set_constant_val(isl::val v) const
+{
+ auto res = isl_constraint_set_constant_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::constraint constraint::set_constant_val(long v) const
+{
+ return this->set_constant_val(isl::val(ctx(), v));
+}
+
+// implementations for isl::fixed_box
+fixed_box manage(__isl_take isl_fixed_box *ptr) {
+ return fixed_box(ptr);
+}
+fixed_box manage_copy(__isl_keep isl_fixed_box *ptr) {
+ ptr = isl_fixed_box_copy(ptr);
+ return fixed_box(ptr);
+}
+
+fixed_box::fixed_box()
+ : ptr(nullptr) {}
+
+fixed_box::fixed_box(const fixed_box &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+fixed_box::fixed_box(__isl_take isl_fixed_box *ptr)
+ : ptr(ptr) {}
+
+fixed_box &fixed_box::operator=(fixed_box obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+fixed_box::~fixed_box() {
+ if (ptr)
+ isl_fixed_box_free(ptr);
+}
+
+__isl_give isl_fixed_box *fixed_box::copy() const & {
+ return isl_fixed_box_copy(ptr);
+}
+
+__isl_keep isl_fixed_box *fixed_box::get() const {
+ return ptr;
+}
+
+__isl_give isl_fixed_box *fixed_box::release() {
+ isl_fixed_box *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool fixed_box::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx fixed_box::ctx() const {
+ return isl::ctx(isl_fixed_box_get_ctx(ptr));
+}
+
+boolean fixed_box::is_valid() const
+{
+ auto res = isl_fixed_box_is_valid(get());
+ return manage(res);
+}
+
+isl::multi_aff fixed_box::offset() const
+{
+ auto res = isl_fixed_box_get_offset(get());
+ return manage(res);
+}
+
+isl::multi_aff fixed_box::get_offset() const
+{
+ return offset();
+}
+
+isl::multi_val fixed_box::size() const
+{
+ auto res = isl_fixed_box_get_size(get());
+ return manage(res);
+}
+
+isl::multi_val fixed_box::get_size() const
+{
+ return size();
+}
+
+isl::space fixed_box::space() const
+{
+ auto res = isl_fixed_box_get_space(get());
+ return manage(res);
+}
+
+isl::space fixed_box::get_space() const
+{
+ return space();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const fixed_box &obj)
+{
+ char *str = isl_fixed_box_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::id
+id manage(__isl_take isl_id *ptr) {
+ return id(ptr);
+}
+id manage_copy(__isl_keep isl_id *ptr) {
+ ptr = isl_id_copy(ptr);
+ return id(ptr);
+}
+
+id::id()
+ : ptr(nullptr) {}
+
+id::id(const id &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+id::id(__isl_take isl_id *ptr)
+ : ptr(ptr) {}
+
+id::id(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_id_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+id &id::operator=(id obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+id::~id() {
+ if (ptr)
+ isl_id_free(ptr);
+}
+
+__isl_give isl_id *id::copy() const & {
+ return isl_id_copy(ptr);
+}
+
+__isl_keep isl_id *id::get() const {
+ return ptr;
+}
+
+__isl_give isl_id *id::release() {
+ isl_id *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool id::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx id::ctx() const {
+ return isl::ctx(isl_id_get_ctx(ptr));
+}
+
+isl::id id::alloc(isl::ctx ctx, const std::string &name, void * user)
+{
+ auto res = isl_id_alloc(ctx.release(), name.c_str(), user);
+ return manage(res);
+}
+
+std::string id::name() const
+{
+ auto res = isl_id_get_name(get());
+ std::string tmp(res);
+ return tmp;
+}
+
+std::string id::get_name() const
+{
+ return name();
+}
+
+isl::id_list id::to_list() const
+{
+ auto res = isl_id_to_list(copy());
+ return manage(res);
+}
+
+void * id::user() const
+{
+ auto res = isl_id_get_user(get());
+ return res;
+}
+
+void * id::get_user() const
+{
+ return user();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const id &obj)
+{
+ char *str = isl_id_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::id_list
+id_list manage(__isl_take isl_id_list *ptr) {
+ return id_list(ptr);
+}
+id_list manage_copy(__isl_keep isl_id_list *ptr) {
+ ptr = isl_id_list_copy(ptr);
+ return id_list(ptr);
+}
+
+id_list::id_list()
+ : ptr(nullptr) {}
+
+id_list::id_list(const id_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+id_list::id_list(__isl_take isl_id_list *ptr)
+ : ptr(ptr) {}
+
+id_list::id_list(isl::ctx ctx, int n)
+{
+ auto res = isl_id_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+id_list::id_list(isl::id el)
+{
+ auto res = isl_id_list_from_id(el.release());
+ ptr = res;
+}
+
+id_list::id_list(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_id_list_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+id_list &id_list::operator=(id_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+id_list::~id_list() {
+ if (ptr)
+ isl_id_list_free(ptr);
+}
+
+__isl_give isl_id_list *id_list::copy() const & {
+ return isl_id_list_copy(ptr);
+}
+
+__isl_keep isl_id_list *id_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_id_list *id_list::release() {
+ isl_id_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool id_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx id_list::ctx() const {
+ return isl::ctx(isl_id_list_get_ctx(ptr));
+}
+
+isl::id_list id_list::add(isl::id el) const
+{
+ auto res = isl_id_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::id_list id_list::add(const std::string &el) const
+{
+ return this->add(isl::id(ctx(), el));
+}
+
+isl::id id_list::at(int index) const
+{
+ auto res = isl_id_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::id id_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::id_list id_list::clear() const
+{
+ auto res = isl_id_list_clear(copy());
+ return manage(res);
+}
+
+isl::id_list id_list::concat(isl::id_list list2) const
+{
+ auto res = isl_id_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::id_list id_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_id_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat id_list::foreach(const std::function<stat(isl::id)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::id)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_id *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_id_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::id_list id_list::insert(unsigned int pos, isl::id el) const
+{
+ auto res = isl_id_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+isl::id_list id_list::insert(unsigned int pos, const std::string &el) const
+{
+ return this->insert(pos, isl::id(ctx(), el));
+}
+
+class size id_list::size() const
+{
+ auto res = isl_id_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const id_list &obj)
+{
+ char *str = isl_id_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::id_to_ast_expr
+id_to_ast_expr manage(__isl_take isl_id_to_ast_expr *ptr) {
+ return id_to_ast_expr(ptr);
+}
+id_to_ast_expr manage_copy(__isl_keep isl_id_to_ast_expr *ptr) {
+ ptr = isl_id_to_ast_expr_copy(ptr);
+ return id_to_ast_expr(ptr);
+}
+
+id_to_ast_expr::id_to_ast_expr()
+ : ptr(nullptr) {}
+
+id_to_ast_expr::id_to_ast_expr(const id_to_ast_expr &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+id_to_ast_expr::id_to_ast_expr(__isl_take isl_id_to_ast_expr *ptr)
+ : ptr(ptr) {}
+
+id_to_ast_expr &id_to_ast_expr::operator=(id_to_ast_expr obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+id_to_ast_expr::~id_to_ast_expr() {
+ if (ptr)
+ isl_id_to_ast_expr_free(ptr);
+}
+
+__isl_give isl_id_to_ast_expr *id_to_ast_expr::copy() const & {
+ return isl_id_to_ast_expr_copy(ptr);
+}
+
+__isl_keep isl_id_to_ast_expr *id_to_ast_expr::get() const {
+ return ptr;
+}
+
+__isl_give isl_id_to_ast_expr *id_to_ast_expr::release() {
+ isl_id_to_ast_expr *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool id_to_ast_expr::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx id_to_ast_expr::ctx() const {
+ return isl::ctx(isl_id_to_ast_expr_get_ctx(ptr));
+}
+
+isl::id_to_ast_expr id_to_ast_expr::alloc(isl::ctx ctx, int min_size)
+{
+ auto res = isl_id_to_ast_expr_alloc(ctx.release(), min_size);
+ return manage(res);
+}
+
+isl::id_to_ast_expr id_to_ast_expr::set(isl::id key, isl::ast_expr val) const
+{
+ auto res = isl_id_to_ast_expr_set(copy(), key.release(), val.release());
+ return manage(res);
+}
+
+isl::id_to_ast_expr id_to_ast_expr::set(const std::string &key, const isl::ast_expr &val) const
+{
+ return this->set(isl::id(ctx(), key), val);
+}
+
+// implementations for isl::local_space
+local_space manage(__isl_take isl_local_space *ptr) {
+ return local_space(ptr);
+}
+local_space manage_copy(__isl_keep isl_local_space *ptr) {
+ ptr = isl_local_space_copy(ptr);
+ return local_space(ptr);
+}
+
+local_space::local_space()
+ : ptr(nullptr) {}
+
+local_space::local_space(const local_space &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+local_space::local_space(__isl_take isl_local_space *ptr)
+ : ptr(ptr) {}
+
+local_space::local_space(isl::space space)
+{
+ auto res = isl_local_space_from_space(space.release());
+ ptr = res;
+}
+
+local_space &local_space::operator=(local_space obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+local_space::~local_space() {
+ if (ptr)
+ isl_local_space_free(ptr);
+}
+
+__isl_give isl_local_space *local_space::copy() const & {
+ return isl_local_space_copy(ptr);
+}
+
+__isl_keep isl_local_space *local_space::get() const {
+ return ptr;
+}
+
+__isl_give isl_local_space *local_space::release() {
+ isl_local_space *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool local_space::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx local_space::ctx() const {
+ return isl::ctx(isl_local_space_get_ctx(ptr));
+}
+
+// implementations for isl::map
+map manage(__isl_take isl_map *ptr) {
+ return map(ptr);
+}
+map manage_copy(__isl_keep isl_map *ptr) {
+ ptr = isl_map_copy(ptr);
+ return map(ptr);
+}
+
+map::map()
+ : ptr(nullptr) {}
+
+map::map(const map &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+map::map(__isl_take isl_map *ptr)
+ : ptr(ptr) {}
+
+map::map(isl::basic_map bmap)
+{
+ auto res = isl_map_from_basic_map(bmap.release());
+ ptr = res;
+}
+
+map::map(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_map_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+map &map::operator=(map obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+map::~map() {
+ if (ptr)
+ isl_map_free(ptr);
+}
+
+__isl_give isl_map *map::copy() const & {
+ return isl_map_copy(ptr);
+}
+
+__isl_keep isl_map *map::get() const {
+ return ptr;
+}
+
+__isl_give isl_map *map::release() {
+ isl_map *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool map::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx map::ctx() const {
+ return isl::ctx(isl_map_get_ctx(ptr));
+}
+
+isl::map map::add_constraint(isl::constraint constraint) const
+{
+ auto res = isl_map_add_constraint(copy(), constraint.release());
+ return manage(res);
+}
+
+isl::map map::add_dims(isl::dim type, unsigned int n) const
+{
+ auto res = isl_map_add_dims(copy(), static_cast<enum isl_dim_type>(type), n);
+ return manage(res);
+}
+
+isl::basic_map map::affine_hull() const
+{
+ auto res = isl_map_affine_hull(copy());
+ return manage(res);
+}
+
+isl::map map::align_params(isl::space model) const
+{
+ auto res = isl_map_align_params(copy(), model.release());
+ return manage(res);
+}
+
+isl::map map::apply_domain(isl::map map2) const
+{
+ auto res = isl_map_apply_domain(copy(), map2.release());
+ return manage(res);
+}
+
+isl::union_map map::apply_domain(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).apply_domain(umap2);
+}
+
+isl::map map::apply_domain(const isl::basic_map &map2) const
+{
+ return this->apply_domain(isl::map(map2));
+}
+
+isl::map map::apply_range(isl::map map2) const
+{
+ auto res = isl_map_apply_range(copy(), map2.release());
+ return manage(res);
+}
+
+isl::union_map map::apply_range(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).apply_range(umap2);
+}
+
+isl::map map::apply_range(const isl::basic_map &map2) const
+{
+ return this->apply_range(isl::map(map2));
+}
+
+isl::map map::as_map() const
+{
+ return isl::union_map(*this).as_map();
+}
+
+isl::multi_union_pw_aff map::as_multi_union_pw_aff() const
+{
+ return isl::union_map(*this).as_multi_union_pw_aff();
+}
+
+isl::pw_multi_aff map::as_pw_multi_aff() const
+{
+ auto res = isl_map_as_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff map::as_union_pw_multi_aff() const
+{
+ return isl::union_map(*this).as_union_pw_multi_aff();
+}
+
+isl::basic_map_list map::basic_map_list() const
+{
+ auto res = isl_map_get_basic_map_list(get());
+ return manage(res);
+}
+
+isl::basic_map_list map::get_basic_map_list() const
+{
+ return basic_map_list();
+}
+
+isl::set map::bind_domain(isl::multi_id tuple) const
+{
+ auto res = isl_map_bind_domain(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::set map::bind_range(isl::multi_id tuple) const
+{
+ auto res = isl_map_bind_range(copy(), tuple.release());
+ return manage(res);
+}
+
+boolean map::can_curry() const
+{
+ auto res = isl_map_can_curry(get());
+ return manage(res);
+}
+
+isl::map map::coalesce() const
+{
+ auto res = isl_map_coalesce(copy());
+ return manage(res);
+}
+
+isl::map map::complement() const
+{
+ auto res = isl_map_complement(copy());
+ return manage(res);
+}
+
+isl::union_map map::compute_divs() const
+{
+ return isl::union_map(*this).compute_divs();
+}
+
+isl::map map::curry() const
+{
+ auto res = isl_map_curry(copy());
+ return manage(res);
+}
+
+isl::set map::deltas() const
+{
+ auto res = isl_map_deltas(copy());
+ return manage(res);
+}
+
+isl::map map::detect_equalities() const
+{
+ auto res = isl_map_detect_equalities(copy());
+ return manage(res);
+}
+
+class size map::dim(isl::dim type) const
+{
+ auto res = isl_map_dim(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::pw_aff map::dim_max(int pos) const
+{
+ auto res = isl_map_dim_max(copy(), pos);
+ return manage(res);
+}
+
+isl::pw_aff map::dim_min(int pos) const
+{
+ auto res = isl_map_dim_min(copy(), pos);
+ return manage(res);
+}
+
+isl::set map::domain() const
+{
+ auto res = isl_map_domain(copy());
+ return manage(res);
+}
+
+isl::map map::domain_factor_domain() const
+{
+ auto res = isl_map_domain_factor_domain(copy());
+ return manage(res);
+}
+
+isl::map map::domain_factor_range() const
+{
+ auto res = isl_map_domain_factor_range(copy());
+ return manage(res);
+}
+
+isl::map map::domain_map() const
+{
+ auto res = isl_map_domain_map(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff map::domain_map_union_pw_multi_aff() const
+{
+ return isl::union_map(*this).domain_map_union_pw_multi_aff();
+}
+
+isl::map map::domain_product(isl::map map2) const
+{
+ auto res = isl_map_domain_product(copy(), map2.release());
+ return manage(res);
+}
+
+isl::union_map map::domain_product(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).domain_product(umap2);
+}
+
+isl::map map::domain_product(const isl::basic_map &map2) const
+{
+ return this->domain_product(isl::map(map2));
+}
+
+class size map::domain_tuple_dim() const
+{
+ auto res = isl_map_domain_tuple_dim(get());
+ return manage(res);
+}
+
+isl::id map::domain_tuple_id() const
+{
+ auto res = isl_map_get_domain_tuple_id(get());
+ return manage(res);
+}
+
+isl::id map::get_domain_tuple_id() const
+{
+ return domain_tuple_id();
+}
+
+isl::map map::empty(isl::space space)
+{
+ auto res = isl_map_empty(space.release());
+ return manage(res);
+}
+
+isl::map map::eq_at(isl::multi_pw_aff mpa) const
+{
+ auto res = isl_map_eq_at_multi_pw_aff(copy(), mpa.release());
+ return manage(res);
+}
+
+isl::union_map map::eq_at(const isl::multi_union_pw_aff &mupa) const
+{
+ return isl::union_map(*this).eq_at(mupa);
+}
+
+isl::map map::eq_at(const isl::aff &mpa) const
+{
+ return this->eq_at(isl::multi_pw_aff(mpa));
+}
+
+isl::map map::eq_at(const isl::multi_aff &mpa) const
+{
+ return this->eq_at(isl::multi_pw_aff(mpa));
+}
+
+isl::map map::eq_at(const isl::pw_aff &mpa) const
+{
+ return this->eq_at(isl::multi_pw_aff(mpa));
+}
+
+isl::map map::eq_at(const isl::pw_multi_aff &mpa) const
+{
+ return this->eq_at(isl::multi_pw_aff(mpa));
+}
+
+isl::map map::equate(isl::dim type1, int pos1, isl::dim type2, int pos2) const
+{
+ auto res = isl_map_equate(copy(), static_cast<enum isl_dim_type>(type1), pos1, static_cast<enum isl_dim_type>(type2), pos2);
+ return manage(res);
+}
+
+boolean map::every_map(const std::function<boolean(isl::map)> &test) const
+{
+ return isl::union_map(*this).every_map(test);
+}
+
+isl::map map::extract_map(const isl::space &space) const
+{
+ return isl::union_map(*this).extract_map(space);
+}
+
+isl::map map::factor_domain() const
+{
+ auto res = isl_map_factor_domain(copy());
+ return manage(res);
+}
+
+isl::map map::factor_range() const
+{
+ auto res = isl_map_factor_range(copy());
+ return manage(res);
+}
+
+isl::map map::fix_si(isl::dim type, unsigned int pos, int value) const
+{
+ auto res = isl_map_fix_si(copy(), static_cast<enum isl_dim_type>(type), pos, value);
+ return manage(res);
+}
+
+isl::union_map map::fixed_power(const isl::val &exp) const
+{
+ return isl::union_map(*this).fixed_power(exp);
+}
+
+isl::union_map map::fixed_power(long exp) const
+{
+ return this->fixed_power(isl::val(ctx(), exp));
+}
+
+isl::map map::flat_range_product(isl::map map2) const
+{
+ auto res = isl_map_flat_range_product(copy(), map2.release());
+ return manage(res);
+}
+
+isl::union_map map::flat_range_product(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).flat_range_product(umap2);
+}
+
+isl::map map::flat_range_product(const isl::basic_map &map2) const
+{
+ return this->flat_range_product(isl::map(map2));
+}
+
+isl::map map::flatten() const
+{
+ auto res = isl_map_flatten(copy());
+ return manage(res);
+}
+
+isl::map map::flatten_domain() const
+{
+ auto res = isl_map_flatten_domain(copy());
+ return manage(res);
+}
+
+isl::map map::flatten_range() const
+{
+ auto res = isl_map_flatten_range(copy());
+ return manage(res);
+}
+
+isl::map map::floordiv_val(isl::val d) const
+{
+ auto res = isl_map_floordiv_val(copy(), d.release());
+ return manage(res);
+}
+
+isl::map map::floordiv_val(long d) const
+{
+ return this->floordiv_val(isl::val(ctx(), d));
+}
+
+stat map::foreach_basic_map(const std::function<stat(isl::basic_map)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::basic_map)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_basic_map *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_map_foreach_basic_map(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+stat map::foreach_map(const std::function<stat(isl::map)> &fn) const
+{
+ return isl::union_map(*this).foreach_map(fn);
+}
+
+isl::map map::from_aff(isl::aff aff)
+{
+ auto res = isl_map_from_aff(aff.release());
+ return manage(res);
+}
+
+isl::map map::from_domain(isl::set set)
+{
+ auto res = isl_map_from_domain(set.release());
+ return manage(res);
+}
+
+isl::map map::from_domain_and_range(isl::set domain, isl::set range)
+{
+ auto res = isl_map_from_domain_and_range(domain.release(), range.release());
+ return manage(res);
+}
+
+isl::map map::from_multi_aff(isl::multi_aff maff)
+{
+ auto res = isl_map_from_multi_aff(maff.release());
+ return manage(res);
+}
+
+isl::map map::from_pw_aff(isl::pw_aff pwaff)
+{
+ auto res = isl_map_from_pw_aff(pwaff.release());
+ return manage(res);
+}
+
+isl::map map::from_range(isl::set set)
+{
+ auto res = isl_map_from_range(set.release());
+ return manage(res);
+}
+
+isl::map map::from_union_map(isl::union_map umap)
+{
+ auto res = isl_map_from_union_map(umap.release());
+ return manage(res);
+}
+
+isl::map map::gist(isl::map context) const
+{
+ auto res = isl_map_gist(copy(), context.release());
+ return manage(res);
+}
+
+isl::union_map map::gist(const isl::union_map &context) const
+{
+ return isl::union_map(*this).gist(context);
+}
+
+isl::map map::gist(const isl::basic_map &context) const
+{
+ return this->gist(isl::map(context));
+}
+
+isl::map map::gist_domain(isl::set context) const
+{
+ auto res = isl_map_gist_domain(copy(), context.release());
+ return manage(res);
+}
+
+isl::union_map map::gist_domain(const isl::union_set &uset) const
+{
+ return isl::union_map(*this).gist_domain(uset);
+}
+
+isl::map map::gist_domain(const isl::basic_set &context) const
+{
+ return this->gist_domain(isl::set(context));
+}
+
+isl::map map::gist_domain(const isl::point &context) const
+{
+ return this->gist_domain(isl::set(context));
+}
+
+isl::map map::gist_params(isl::set context) const
+{
+ auto res = isl_map_gist_params(copy(), context.release());
+ return manage(res);
+}
+
+isl::union_map map::gist_range(const isl::union_set &uset) const
+{
+ return isl::union_map(*this).gist_range(uset);
+}
+
+boolean map::has_domain_tuple_id() const
+{
+ auto res = isl_map_has_domain_tuple_id(get());
+ return manage(res);
+}
+
+boolean map::has_equal_space(const isl::map &map2) const
+{
+ auto res = isl_map_has_equal_space(get(), map2.get());
+ return manage(res);
+}
+
+boolean map::has_range_tuple_id() const
+{
+ auto res = isl_map_has_range_tuple_id(get());
+ return manage(res);
+}
+
+boolean map::has_tuple_id(isl::dim type) const
+{
+ auto res = isl_map_has_tuple_id(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+boolean map::has_tuple_name(isl::dim type) const
+{
+ auto res = isl_map_has_tuple_name(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::map map::identity(isl::space space)
+{
+ auto res = isl_map_identity(space.release());
+ return manage(res);
+}
+
+isl::map map::intersect(isl::map map2) const
+{
+ auto res = isl_map_intersect(copy(), map2.release());
+ return manage(res);
+}
+
+isl::union_map map::intersect(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).intersect(umap2);
+}
+
+isl::map map::intersect(const isl::basic_map &map2) const
+{
+ return this->intersect(isl::map(map2));
+}
+
+isl::map map::intersect_domain(isl::set set) const
+{
+ auto res = isl_map_intersect_domain(copy(), set.release());
+ return manage(res);
+}
+
+isl::union_map map::intersect_domain(const isl::space &space) const
+{
+ return isl::union_map(*this).intersect_domain(space);
+}
+
+isl::union_map map::intersect_domain(const isl::union_set &uset) const
+{
+ return isl::union_map(*this).intersect_domain(uset);
+}
+
+isl::map map::intersect_domain(const isl::basic_set &set) const
+{
+ return this->intersect_domain(isl::set(set));
+}
+
+isl::map map::intersect_domain(const isl::point &set) const
+{
+ return this->intersect_domain(isl::set(set));
+}
+
+isl::map map::intersect_domain_factor_domain(isl::map factor) const
+{
+ auto res = isl_map_intersect_domain_factor_domain(copy(), factor.release());
+ return manage(res);
+}
+
+isl::union_map map::intersect_domain_factor_domain(const isl::union_map &factor) const
+{
+ return isl::union_map(*this).intersect_domain_factor_domain(factor);
+}
+
+isl::map map::intersect_domain_factor_domain(const isl::basic_map &factor) const
+{
+ return this->intersect_domain_factor_domain(isl::map(factor));
+}
+
+isl::map map::intersect_domain_factor_range(isl::map factor) const
+{
+ auto res = isl_map_intersect_domain_factor_range(copy(), factor.release());
+ return manage(res);
+}
+
+isl::union_map map::intersect_domain_factor_range(const isl::union_map &factor) const
+{
+ return isl::union_map(*this).intersect_domain_factor_range(factor);
+}
+
+isl::map map::intersect_domain_factor_range(const isl::basic_map &factor) const
+{
+ return this->intersect_domain_factor_range(isl::map(factor));
+}
+
+isl::map map::intersect_params(isl::set params) const
+{
+ auto res = isl_map_intersect_params(copy(), params.release());
+ return manage(res);
+}
+
+isl::map map::intersect_range(isl::set set) const
+{
+ auto res = isl_map_intersect_range(copy(), set.release());
+ return manage(res);
+}
+
+isl::union_map map::intersect_range(const isl::space &space) const
+{
+ return isl::union_map(*this).intersect_range(space);
+}
+
+isl::union_map map::intersect_range(const isl::union_set &uset) const
+{
+ return isl::union_map(*this).intersect_range(uset);
+}
+
+isl::map map::intersect_range(const isl::basic_set &set) const
+{
+ return this->intersect_range(isl::set(set));
+}
+
+isl::map map::intersect_range(const isl::point &set) const
+{
+ return this->intersect_range(isl::set(set));
+}
+
+isl::map map::intersect_range_factor_domain(isl::map factor) const
+{
+ auto res = isl_map_intersect_range_factor_domain(copy(), factor.release());
+ return manage(res);
+}
+
+isl::union_map map::intersect_range_factor_domain(const isl::union_map &factor) const
+{
+ return isl::union_map(*this).intersect_range_factor_domain(factor);
+}
+
+isl::map map::intersect_range_factor_domain(const isl::basic_map &factor) const
+{
+ return this->intersect_range_factor_domain(isl::map(factor));
+}
+
+isl::map map::intersect_range_factor_range(isl::map factor) const
+{
+ auto res = isl_map_intersect_range_factor_range(copy(), factor.release());
+ return manage(res);
+}
+
+isl::union_map map::intersect_range_factor_range(const isl::union_map &factor) const
+{
+ return isl::union_map(*this).intersect_range_factor_range(factor);
+}
+
+isl::map map::intersect_range_factor_range(const isl::basic_map &factor) const
+{
+ return this->intersect_range_factor_range(isl::map(factor));
+}
+
+boolean map::involves_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ auto res = isl_map_involves_dims(get(), static_cast<enum isl_dim_type>(type), first, n);
+ return manage(res);
+}
+
+boolean map::is_bijective() const
+{
+ auto res = isl_map_is_bijective(get());
+ return manage(res);
+}
+
+boolean map::is_disjoint(const isl::map &map2) const
+{
+ auto res = isl_map_is_disjoint(get(), map2.get());
+ return manage(res);
+}
+
+boolean map::is_disjoint(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).is_disjoint(umap2);
+}
+
+boolean map::is_disjoint(const isl::basic_map &map2) const
+{
+ return this->is_disjoint(isl::map(map2));
+}
+
+boolean map::is_empty() const
+{
+ auto res = isl_map_is_empty(get());
+ return manage(res);
+}
+
+boolean map::is_equal(const isl::map &map2) const
+{
+ auto res = isl_map_is_equal(get(), map2.get());
+ return manage(res);
+}
+
+boolean map::is_equal(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).is_equal(umap2);
+}
+
+boolean map::is_equal(const isl::basic_map &map2) const
+{
+ return this->is_equal(isl::map(map2));
+}
+
+boolean map::is_injective() const
+{
+ auto res = isl_map_is_injective(get());
+ return manage(res);
+}
+
+boolean map::is_single_valued() const
+{
+ auto res = isl_map_is_single_valued(get());
+ return manage(res);
+}
+
+boolean map::is_strict_subset(const isl::map &map2) const
+{
+ auto res = isl_map_is_strict_subset(get(), map2.get());
+ return manage(res);
+}
+
+boolean map::is_strict_subset(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).is_strict_subset(umap2);
+}
+
+boolean map::is_strict_subset(const isl::basic_map &map2) const
+{
+ return this->is_strict_subset(isl::map(map2));
+}
+
+boolean map::is_subset(const isl::map &map2) const
+{
+ auto res = isl_map_is_subset(get(), map2.get());
+ return manage(res);
+}
+
+boolean map::is_subset(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).is_subset(umap2);
+}
+
+boolean map::is_subset(const isl::basic_map &map2) const
+{
+ return this->is_subset(isl::map(map2));
+}
+
+boolean map::isa_map() const
+{
+ return isl::union_map(*this).isa_map();
+}
+
+isl::map map::lex_ge(isl::space set_space)
+{
+ auto res = isl_map_lex_ge(set_space.release());
+ return manage(res);
+}
+
+isl::map map::lex_ge_at(isl::multi_pw_aff mpa) const
+{
+ auto res = isl_map_lex_ge_at_multi_pw_aff(copy(), mpa.release());
+ return manage(res);
+}
+
+isl::map map::lex_gt(isl::space set_space)
+{
+ auto res = isl_map_lex_gt(set_space.release());
+ return manage(res);
+}
+
+isl::map map::lex_gt_at(isl::multi_pw_aff mpa) const
+{
+ auto res = isl_map_lex_gt_at_multi_pw_aff(copy(), mpa.release());
+ return manage(res);
+}
+
+isl::map map::lex_le(isl::space set_space)
+{
+ auto res = isl_map_lex_le(set_space.release());
+ return manage(res);
+}
+
+isl::map map::lex_le_at(isl::multi_pw_aff mpa) const
+{
+ auto res = isl_map_lex_le_at_multi_pw_aff(copy(), mpa.release());
+ return manage(res);
+}
+
+isl::map map::lex_lt(isl::space set_space)
+{
+ auto res = isl_map_lex_lt(set_space.release());
+ return manage(res);
+}
+
+isl::map map::lex_lt_at(isl::multi_pw_aff mpa) const
+{
+ auto res = isl_map_lex_lt_at_multi_pw_aff(copy(), mpa.release());
+ return manage(res);
+}
+
+isl::map map::lexmax() const
+{
+ auto res = isl_map_lexmax(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff map::lexmax_pw_multi_aff() const
+{
+ auto res = isl_map_lexmax_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::map map::lexmin() const
+{
+ auto res = isl_map_lexmin(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff map::lexmin_pw_multi_aff() const
+{
+ auto res = isl_map_lexmin_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::map map::lower_bound(isl::multi_pw_aff lower) const
+{
+ auto res = isl_map_lower_bound_multi_pw_aff(copy(), lower.release());
+ return manage(res);
+}
+
+isl::map map::lower_bound_si(isl::dim type, unsigned int pos, int value) const
+{
+ auto res = isl_map_lower_bound_si(copy(), static_cast<enum isl_dim_type>(type), pos, value);
+ return manage(res);
+}
+
+isl::map_list map::map_list() const
+{
+ return isl::union_map(*this).map_list();
+}
+
+isl::multi_pw_aff map::max_multi_pw_aff() const
+{
+ auto res = isl_map_max_multi_pw_aff(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff map::min_multi_pw_aff() const
+{
+ auto res = isl_map_min_multi_pw_aff(copy());
+ return manage(res);
+}
+
+isl::map map::move_dims(isl::dim dst_type, unsigned int dst_pos, isl::dim src_type, unsigned int src_pos, unsigned int n) const
+{
+ auto res = isl_map_move_dims(copy(), static_cast<enum isl_dim_type>(dst_type), dst_pos, static_cast<enum isl_dim_type>(src_type), src_pos, n);
+ return manage(res);
+}
+
+class size map::n_basic_map() const
+{
+ auto res = isl_map_n_basic_map(get());
+ return manage(res);
+}
+
+isl::map map::order_lt(isl::dim type1, int pos1, isl::dim type2, int pos2) const
+{
+ auto res = isl_map_order_lt(copy(), static_cast<enum isl_dim_type>(type1), pos1, static_cast<enum isl_dim_type>(type2), pos2);
+ return manage(res);
+}
+
+isl::set map::params() const
+{
+ return isl::union_map(*this).params();
+}
+
+isl::basic_map map::polyhedral_hull() const
+{
+ auto res = isl_map_polyhedral_hull(copy());
+ return manage(res);
+}
+
+isl::map map::preimage_domain(isl::multi_aff ma) const
+{
+ auto res = isl_map_preimage_domain_multi_aff(copy(), ma.release());
+ return manage(res);
+}
+
+isl::map map::preimage_domain(isl::multi_pw_aff mpa) const
+{
+ auto res = isl_map_preimage_domain_multi_pw_aff(copy(), mpa.release());
+ return manage(res);
+}
+
+isl::map map::preimage_domain(isl::pw_multi_aff pma) const
+{
+ auto res = isl_map_preimage_domain_pw_multi_aff(copy(), pma.release());
+ return manage(res);
+}
+
+isl::union_map map::preimage_domain(const isl::union_pw_multi_aff &upma) const
+{
+ return isl::union_map(*this).preimage_domain(upma);
+}
+
+isl::map map::preimage_range(isl::multi_aff ma) const
+{
+ auto res = isl_map_preimage_range_multi_aff(copy(), ma.release());
+ return manage(res);
+}
+
+isl::map map::preimage_range(isl::pw_multi_aff pma) const
+{
+ auto res = isl_map_preimage_range_pw_multi_aff(copy(), pma.release());
+ return manage(res);
+}
+
+isl::union_map map::preimage_range(const isl::union_pw_multi_aff &upma) const
+{
+ return isl::union_map(*this).preimage_range(upma);
+}
+
+isl::map map::product(isl::map map2) const
+{
+ auto res = isl_map_product(copy(), map2.release());
+ return manage(res);
+}
+
+isl::union_map map::product(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).product(umap2);
+}
+
+isl::map map::product(const isl::basic_map &map2) const
+{
+ return this->product(isl::map(map2));
+}
+
+isl::map map::project_out(isl::dim type, unsigned int first, unsigned int n) const
+{
+ auto res = isl_map_project_out(copy(), static_cast<enum isl_dim_type>(type), first, n);
+ return manage(res);
+}
+
+isl::map map::project_out_all_params() const
+{
+ auto res = isl_map_project_out_all_params(copy());
+ return manage(res);
+}
+
+isl::set map::range() const
+{
+ auto res = isl_map_range(copy());
+ return manage(res);
+}
+
+isl::map map::range_factor_domain() const
+{
+ auto res = isl_map_range_factor_domain(copy());
+ return manage(res);
+}
+
+isl::map map::range_factor_range() const
+{
+ auto res = isl_map_range_factor_range(copy());
+ return manage(res);
+}
+
+isl::fixed_box map::range_lattice_tile() const
+{
+ auto res = isl_map_get_range_lattice_tile(get());
+ return manage(res);
+}
+
+isl::fixed_box map::get_range_lattice_tile() const
+{
+ return range_lattice_tile();
+}
+
+isl::map map::range_map() const
+{
+ auto res = isl_map_range_map(copy());
+ return manage(res);
+}
+
+isl::map map::range_product(isl::map map2) const
+{
+ auto res = isl_map_range_product(copy(), map2.release());
+ return manage(res);
+}
+
+isl::union_map map::range_product(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).range_product(umap2);
+}
+
+isl::map map::range_product(const isl::basic_map &map2) const
+{
+ return this->range_product(isl::map(map2));
+}
+
+isl::map map::range_reverse() const
+{
+ auto res = isl_map_range_reverse(copy());
+ return manage(res);
+}
+
+isl::fixed_box map::range_simple_fixed_box_hull() const
+{
+ auto res = isl_map_get_range_simple_fixed_box_hull(get());
+ return manage(res);
+}
+
+isl::fixed_box map::get_range_simple_fixed_box_hull() const
+{
+ return range_simple_fixed_box_hull();
+}
+
+class size map::range_tuple_dim() const
+{
+ auto res = isl_map_range_tuple_dim(get());
+ return manage(res);
+}
+
+isl::id map::range_tuple_id() const
+{
+ auto res = isl_map_get_range_tuple_id(get());
+ return manage(res);
+}
+
+isl::id map::get_range_tuple_id() const
+{
+ return range_tuple_id();
+}
+
+isl::map map::reverse() const
+{
+ auto res = isl_map_reverse(copy());
+ return manage(res);
+}
+
+isl::basic_map map::sample() const
+{
+ auto res = isl_map_sample(copy());
+ return manage(res);
+}
+
+isl::map map::set_domain_tuple(isl::id id) const
+{
+ auto res = isl_map_set_domain_tuple_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::map map::set_domain_tuple(const std::string &id) const
+{
+ return this->set_domain_tuple(isl::id(ctx(), id));
+}
+
+isl::map map::set_range_tuple(isl::id id) const
+{
+ auto res = isl_map_set_range_tuple_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::map map::set_range_tuple(const std::string &id) const
+{
+ return this->set_range_tuple(isl::id(ctx(), id));
+}
+
+isl::map map::set_tuple_id(isl::dim type, isl::id id) const
+{
+ auto res = isl_map_set_tuple_id(copy(), static_cast<enum isl_dim_type>(type), id.release());
+ return manage(res);
+}
+
+isl::map map::set_tuple_id(isl::dim type, const std::string &id) const
+{
+ return this->set_tuple_id(type, isl::id(ctx(), id));
+}
+
+isl::space map::space() const
+{
+ auto res = isl_map_get_space(get());
+ return manage(res);
+}
+
+isl::space map::get_space() const
+{
+ return space();
+}
+
+isl::map map::subtract(isl::map map2) const
+{
+ auto res = isl_map_subtract(copy(), map2.release());
+ return manage(res);
+}
+
+isl::union_map map::subtract(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).subtract(umap2);
+}
+
+isl::map map::subtract(const isl::basic_map &map2) const
+{
+ return this->subtract(isl::map(map2));
+}
+
+isl::union_map map::subtract_domain(const isl::union_set &dom) const
+{
+ return isl::union_map(*this).subtract_domain(dom);
+}
+
+isl::union_map map::subtract_range(const isl::union_set &dom) const
+{
+ return isl::union_map(*this).subtract_range(dom);
+}
+
+isl::map map::sum(isl::map map2) const
+{
+ auto res = isl_map_sum(copy(), map2.release());
+ return manage(res);
+}
+
+isl::map_list map::to_list() const
+{
+ auto res = isl_map_to_list(copy());
+ return manage(res);
+}
+
+isl::union_map map::to_union_map() const
+{
+ auto res = isl_map_to_union_map(copy());
+ return manage(res);
+}
+
+isl::id map::tuple_id(isl::dim type) const
+{
+ auto res = isl_map_get_tuple_id(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::id map::get_tuple_id(isl::dim type) const
+{
+ return tuple_id(type);
+}
+
+isl::map map::uncurry() const
+{
+ auto res = isl_map_uncurry(copy());
+ return manage(res);
+}
+
+isl::map map::unite(isl::map map2) const
+{
+ auto res = isl_map_union(copy(), map2.release());
+ return manage(res);
+}
+
+isl::union_map map::unite(const isl::union_map &umap2) const
+{
+ return isl::union_map(*this).unite(umap2);
+}
+
+isl::map map::unite(const isl::basic_map &map2) const
+{
+ return this->unite(isl::map(map2));
+}
+
+isl::map map::universe(isl::space space)
+{
+ auto res = isl_map_universe(space.release());
+ return manage(res);
+}
+
+isl::basic_map map::unshifted_simple_hull() const
+{
+ auto res = isl_map_unshifted_simple_hull(copy());
+ return manage(res);
+}
+
+isl::map map::upper_bound(isl::multi_pw_aff upper) const
+{
+ auto res = isl_map_upper_bound_multi_pw_aff(copy(), upper.release());
+ return manage(res);
+}
+
+isl::map map::upper_bound_si(isl::dim type, unsigned int pos, int value) const
+{
+ auto res = isl_map_upper_bound_si(copy(), static_cast<enum isl_dim_type>(type), pos, value);
+ return manage(res);
+}
+
+isl::set map::wrap() const
+{
+ auto res = isl_map_wrap(copy());
+ return manage(res);
+}
+
+isl::map map::zip() const
+{
+ auto res = isl_map_zip(copy());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const map &obj)
+{
+ char *str = isl_map_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::map_list
+map_list manage(__isl_take isl_map_list *ptr) {
+ return map_list(ptr);
+}
+map_list manage_copy(__isl_keep isl_map_list *ptr) {
+ ptr = isl_map_list_copy(ptr);
+ return map_list(ptr);
+}
+
+map_list::map_list()
+ : ptr(nullptr) {}
+
+map_list::map_list(const map_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+map_list::map_list(__isl_take isl_map_list *ptr)
+ : ptr(ptr) {}
+
+map_list::map_list(isl::ctx ctx, int n)
+{
+ auto res = isl_map_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+map_list::map_list(isl::map el)
+{
+ auto res = isl_map_list_from_map(el.release());
+ ptr = res;
+}
+
+map_list::map_list(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_map_list_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+map_list &map_list::operator=(map_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+map_list::~map_list() {
+ if (ptr)
+ isl_map_list_free(ptr);
+}
+
+__isl_give isl_map_list *map_list::copy() const & {
+ return isl_map_list_copy(ptr);
+}
+
+__isl_keep isl_map_list *map_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_map_list *map_list::release() {
+ isl_map_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool map_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx map_list::ctx() const {
+ return isl::ctx(isl_map_list_get_ctx(ptr));
+}
+
+isl::map_list map_list::add(isl::map el) const
+{
+ auto res = isl_map_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::map map_list::at(int index) const
+{
+ auto res = isl_map_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::map map_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::map_list map_list::clear() const
+{
+ auto res = isl_map_list_clear(copy());
+ return manage(res);
+}
+
+isl::map_list map_list::concat(isl::map_list list2) const
+{
+ auto res = isl_map_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::map_list map_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_map_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat map_list::foreach(const std::function<stat(isl::map)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::map)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_map *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_map_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::map_list map_list::insert(unsigned int pos, isl::map el) const
+{
+ auto res = isl_map_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+class size map_list::size() const
+{
+ auto res = isl_map_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const map_list &obj)
+{
+ char *str = isl_map_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::multi_aff
+multi_aff manage(__isl_take isl_multi_aff *ptr) {
+ return multi_aff(ptr);
+}
+multi_aff manage_copy(__isl_keep isl_multi_aff *ptr) {
+ ptr = isl_multi_aff_copy(ptr);
+ return multi_aff(ptr);
+}
+
+multi_aff::multi_aff()
+ : ptr(nullptr) {}
+
+multi_aff::multi_aff(const multi_aff &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+multi_aff::multi_aff(__isl_take isl_multi_aff *ptr)
+ : ptr(ptr) {}
+
+multi_aff::multi_aff(isl::aff aff)
+{
+ auto res = isl_multi_aff_from_aff(aff.release());
+ ptr = res;
+}
+
+multi_aff::multi_aff(isl::space space, isl::aff_list list)
+{
+ auto res = isl_multi_aff_from_aff_list(space.release(), list.release());
+ ptr = res;
+}
+
+multi_aff::multi_aff(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_multi_aff_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+multi_aff &multi_aff::operator=(multi_aff obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+multi_aff::~multi_aff() {
+ if (ptr)
+ isl_multi_aff_free(ptr);
+}
+
+__isl_give isl_multi_aff *multi_aff::copy() const & {
+ return isl_multi_aff_copy(ptr);
+}
+
+__isl_keep isl_multi_aff *multi_aff::get() const {
+ return ptr;
+}
+
+__isl_give isl_multi_aff *multi_aff::release() {
+ isl_multi_aff *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool multi_aff::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx multi_aff::ctx() const {
+ return isl::ctx(isl_multi_aff_get_ctx(ptr));
+}
+
+isl::multi_aff multi_aff::add(isl::multi_aff multi2) const
+{
+ auto res = isl_multi_aff_add(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_aff::add(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).add(multi2);
+}
+
+isl::multi_union_pw_aff multi_aff::add(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).add(multi2);
+}
+
+isl::pw_multi_aff multi_aff::add(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).add(pma2);
+}
+
+isl::union_pw_multi_aff multi_aff::add(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_multi_aff(*this).add(upma2);
+}
+
+isl::multi_aff multi_aff::add(const isl::aff &multi2) const
+{
+ return this->add(isl::multi_aff(multi2));
+}
+
+isl::multi_aff multi_aff::add_constant(isl::multi_val mv) const
+{
+ auto res = isl_multi_aff_add_constant_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::add_constant(isl::val v) const
+{
+ auto res = isl_multi_aff_add_constant_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::add_constant(long v) const
+{
+ return this->add_constant(isl::val(ctx(), v));
+}
+
+isl::union_pw_multi_aff multi_aff::add_pw_multi_aff(const isl::pw_multi_aff &pma) const
+{
+ return isl::pw_multi_aff(*this).add_pw_multi_aff(pma);
+}
+
+isl::union_pw_multi_aff multi_aff::apply(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_multi_aff(*this).apply(upma2);
+}
+
+isl::map multi_aff::as_map() const
+{
+ auto res = isl_multi_aff_as_map(copy());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::as_multi_aff() const
+{
+ return isl::pw_multi_aff(*this).as_multi_aff();
+}
+
+isl::multi_union_pw_aff multi_aff::as_multi_union_pw_aff() const
+{
+ return isl::pw_multi_aff(*this).as_multi_union_pw_aff();
+}
+
+isl::pw_multi_aff multi_aff::as_pw_multi_aff() const
+{
+ return isl::pw_multi_aff(*this).as_pw_multi_aff();
+}
+
+isl::set multi_aff::as_set() const
+{
+ auto res = isl_multi_aff_as_set(copy());
+ return manage(res);
+}
+
+isl::union_map multi_aff::as_union_map() const
+{
+ return isl::pw_multi_aff(*this).as_union_map();
+}
+
+isl::aff multi_aff::at(int pos) const
+{
+ auto res = isl_multi_aff_get_at(get(), pos);
+ return manage(res);
+}
+
+isl::aff multi_aff::get_at(int pos) const
+{
+ return at(pos);
+}
+
+isl::basic_set multi_aff::bind(isl::multi_id tuple) const
+{
+ auto res = isl_multi_aff_bind(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::bind_domain(isl::multi_id tuple) const
+{
+ auto res = isl_multi_aff_bind_domain(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::bind_domain_wrapped_domain(isl::multi_id tuple) const
+{
+ auto res = isl_multi_aff_bind_domain_wrapped_domain(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff multi_aff::coalesce() const
+{
+ return isl::pw_multi_aff(*this).coalesce();
+}
+
+isl::multi_val multi_aff::constant_multi_val() const
+{
+ auto res = isl_multi_aff_get_constant_multi_val(get());
+ return manage(res);
+}
+
+isl::multi_val multi_aff::get_constant_multi_val() const
+{
+ return constant_multi_val();
+}
+
+class size multi_aff::dim(isl::dim type) const
+{
+ auto res = isl_multi_aff_dim(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::set multi_aff::domain() const
+{
+ return isl::pw_multi_aff(*this).domain();
+}
+
+isl::multi_aff multi_aff::domain_map(isl::space space)
+{
+ auto res = isl_multi_aff_domain_map(space.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff multi_aff::drop_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::pw_multi_aff(*this).drop_dims(type, first, n);
+}
+
+isl::pw_multi_aff multi_aff::extract_pw_multi_aff(const isl::space &space) const
+{
+ return isl::pw_multi_aff(*this).extract_pw_multi_aff(space);
+}
+
+isl::multi_aff multi_aff::flat_range_product(isl::multi_aff multi2) const
+{
+ auto res = isl_multi_aff_flat_range_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_aff::flat_range_product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).flat_range_product(multi2);
+}
+
+isl::multi_union_pw_aff multi_aff::flat_range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).flat_range_product(multi2);
+}
+
+isl::pw_multi_aff multi_aff::flat_range_product(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).flat_range_product(pma2);
+}
+
+isl::union_pw_multi_aff multi_aff::flat_range_product(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_multi_aff(*this).flat_range_product(upma2);
+}
+
+isl::multi_aff multi_aff::flat_range_product(const isl::aff &multi2) const
+{
+ return this->flat_range_product(isl::multi_aff(multi2));
+}
+
+isl::multi_aff multi_aff::floor() const
+{
+ auto res = isl_multi_aff_floor(copy());
+ return manage(res);
+}
+
+stat multi_aff::foreach_piece(const std::function<stat(isl::set, isl::multi_aff)> &fn) const
+{
+ return isl::pw_multi_aff(*this).foreach_piece(fn);
+}
+
+isl::multi_aff multi_aff::gist(isl::set context) const
+{
+ auto res = isl_multi_aff_gist(copy(), context.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff multi_aff::gist(const isl::union_set &context) const
+{
+ return isl::pw_multi_aff(*this).gist(context);
+}
+
+isl::multi_aff multi_aff::gist(const isl::basic_set &context) const
+{
+ return this->gist(isl::set(context));
+}
+
+isl::multi_aff multi_aff::gist(const isl::point &context) const
+{
+ return this->gist(isl::set(context));
+}
+
+boolean multi_aff::has_range_tuple_id() const
+{
+ auto res = isl_multi_aff_has_range_tuple_id(get());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::identity(isl::space space)
+{
+ auto res = isl_multi_aff_identity(space.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::identity() const
+{
+ auto res = isl_multi_aff_identity_multi_aff(copy());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::identity_on_domain(isl::space space)
+{
+ auto res = isl_multi_aff_identity_on_domain_space(space.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::insert_domain(isl::space domain) const
+{
+ auto res = isl_multi_aff_insert_domain(copy(), domain.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff multi_aff::intersect_domain(const isl::set &set) const
+{
+ return isl::pw_multi_aff(*this).intersect_domain(set);
+}
+
+isl::union_pw_multi_aff multi_aff::intersect_domain(const isl::space &space) const
+{
+ return isl::pw_multi_aff(*this).intersect_domain(space);
+}
+
+isl::union_pw_multi_aff multi_aff::intersect_domain(const isl::union_set &uset) const
+{
+ return isl::pw_multi_aff(*this).intersect_domain(uset);
+}
+
+isl::union_pw_multi_aff multi_aff::intersect_domain_wrapped_domain(const isl::union_set &uset) const
+{
+ return isl::pw_multi_aff(*this).intersect_domain_wrapped_domain(uset);
+}
+
+isl::union_pw_multi_aff multi_aff::intersect_domain_wrapped_range(const isl::union_set &uset) const
+{
+ return isl::pw_multi_aff(*this).intersect_domain_wrapped_range(uset);
+}
+
+isl::pw_multi_aff multi_aff::intersect_params(const isl::set &set) const
+{
+ return isl::pw_multi_aff(*this).intersect_params(set);
+}
+
+boolean multi_aff::involves_locals() const
+{
+ auto res = isl_multi_aff_involves_locals(get());
+ return manage(res);
+}
+
+boolean multi_aff::involves_nan() const
+{
+ auto res = isl_multi_aff_involves_nan(get());
+ return manage(res);
+}
+
+boolean multi_aff::involves_param(const isl::id &id) const
+{
+ return isl::pw_multi_aff(*this).involves_param(id);
+}
+
+boolean multi_aff::involves_param(const std::string &id) const
+{
+ return this->involves_param(isl::id(ctx(), id));
+}
+
+boolean multi_aff::involves_param(const isl::id_list &list) const
+{
+ return isl::pw_multi_aff(*this).involves_param(list);
+}
+
+boolean multi_aff::isa_multi_aff() const
+{
+ return isl::pw_multi_aff(*this).isa_multi_aff();
+}
+
+boolean multi_aff::isa_pw_multi_aff() const
+{
+ return isl::pw_multi_aff(*this).isa_pw_multi_aff();
+}
+
+isl::aff_list multi_aff::list() const
+{
+ auto res = isl_multi_aff_get_list(get());
+ return manage(res);
+}
+
+isl::aff_list multi_aff::get_list() const
+{
+ return list();
+}
+
+isl::multi_pw_aff multi_aff::max(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).max(multi2);
+}
+
+isl::multi_val multi_aff::max_multi_val() const
+{
+ return isl::pw_multi_aff(*this).max_multi_val();
+}
+
+isl::multi_pw_aff multi_aff::min(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).min(multi2);
+}
+
+isl::multi_val multi_aff::min_multi_val() const
+{
+ return isl::pw_multi_aff(*this).min_multi_val();
+}
+
+isl::multi_aff multi_aff::multi_val_on_domain(isl::space space, isl::multi_val mv)
+{
+ auto res = isl_multi_aff_multi_val_on_domain_space(space.release(), mv.release());
+ return manage(res);
+}
+
+class size multi_aff::n_piece() const
+{
+ return isl::pw_multi_aff(*this).n_piece();
+}
+
+isl::multi_aff multi_aff::neg() const
+{
+ auto res = isl_multi_aff_neg(copy());
+ return manage(res);
+}
+
+boolean multi_aff::plain_is_empty() const
+{
+ return isl::pw_multi_aff(*this).plain_is_empty();
+}
+
+boolean multi_aff::plain_is_equal(const isl::multi_aff &multi2) const
+{
+ auto res = isl_multi_aff_plain_is_equal(get(), multi2.get());
+ return manage(res);
+}
+
+boolean multi_aff::plain_is_equal(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).plain_is_equal(multi2);
+}
+
+boolean multi_aff::plain_is_equal(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).plain_is_equal(multi2);
+}
+
+boolean multi_aff::plain_is_equal(const isl::aff &multi2) const
+{
+ return this->plain_is_equal(isl::multi_aff(multi2));
+}
+
+isl::pw_multi_aff multi_aff::preimage_domain_wrapped_domain(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).preimage_domain_wrapped_domain(pma2);
+}
+
+isl::union_pw_multi_aff multi_aff::preimage_domain_wrapped_domain(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_multi_aff(*this).preimage_domain_wrapped_domain(upma2);
+}
+
+isl::multi_aff multi_aff::product(isl::multi_aff multi2) const
+{
+ auto res = isl_multi_aff_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_aff::product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).product(multi2);
+}
+
+isl::pw_multi_aff multi_aff::product(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).product(pma2);
+}
+
+isl::multi_aff multi_aff::product(const isl::aff &multi2) const
+{
+ return this->product(isl::multi_aff(multi2));
+}
+
+isl::multi_aff multi_aff::pullback(isl::multi_aff ma2) const
+{
+ auto res = isl_multi_aff_pullback_multi_aff(copy(), ma2.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_aff::pullback(const isl::multi_pw_aff &mpa2) const
+{
+ return isl::pw_multi_aff(*this).pullback(mpa2);
+}
+
+isl::pw_multi_aff multi_aff::pullback(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).pullback(pma2);
+}
+
+isl::union_pw_multi_aff multi_aff::pullback(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_multi_aff(*this).pullback(upma2);
+}
+
+isl::multi_aff multi_aff::pullback(const isl::aff &ma2) const
+{
+ return this->pullback(isl::multi_aff(ma2));
+}
+
+isl::pw_multi_aff_list multi_aff::pw_multi_aff_list() const
+{
+ return isl::pw_multi_aff(*this).pw_multi_aff_list();
+}
+
+isl::pw_multi_aff multi_aff::range_factor_domain() const
+{
+ return isl::pw_multi_aff(*this).range_factor_domain();
+}
+
+isl::pw_multi_aff multi_aff::range_factor_range() const
+{
+ return isl::pw_multi_aff(*this).range_factor_range();
+}
+
+isl::multi_aff multi_aff::range_map(isl::space space)
+{
+ auto res = isl_multi_aff_range_map(space.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::range_product(isl::multi_aff multi2) const
+{
+ auto res = isl_multi_aff_range_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_aff::range_product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).range_product(multi2);
+}
+
+isl::multi_union_pw_aff multi_aff::range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).range_product(multi2);
+}
+
+isl::pw_multi_aff multi_aff::range_product(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).range_product(pma2);
+}
+
+isl::union_pw_multi_aff multi_aff::range_product(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_multi_aff(*this).range_product(upma2);
+}
+
+isl::multi_aff multi_aff::range_product(const isl::aff &multi2) const
+{
+ return this->range_product(isl::multi_aff(multi2));
+}
+
+isl::id multi_aff::range_tuple_id() const
+{
+ auto res = isl_multi_aff_get_range_tuple_id(get());
+ return manage(res);
+}
+
+isl::id multi_aff::get_range_tuple_id() const
+{
+ return range_tuple_id();
+}
+
+isl::multi_aff multi_aff::reset_range_tuple_id() const
+{
+ auto res = isl_multi_aff_reset_range_tuple_id(copy());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::reset_tuple_id(isl::dim type) const
+{
+ auto res = isl_multi_aff_reset_tuple_id(copy(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::scale(isl::multi_val mv) const
+{
+ auto res = isl_multi_aff_scale_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::scale(isl::val v) const
+{
+ auto res = isl_multi_aff_scale_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::scale(long v) const
+{
+ return this->scale(isl::val(ctx(), v));
+}
+
+isl::multi_aff multi_aff::scale_down(isl::multi_val mv) const
+{
+ auto res = isl_multi_aff_scale_down_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::scale_down(isl::val v) const
+{
+ auto res = isl_multi_aff_scale_down_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::scale_down(long v) const
+{
+ return this->scale_down(isl::val(ctx(), v));
+}
+
+isl::multi_aff multi_aff::set_aff(int pos, isl::aff el) const
+{
+ auto res = isl_multi_aff_set_aff(copy(), pos, el.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::set_at(int pos, isl::aff el) const
+{
+ auto res = isl_multi_aff_set_at(copy(), pos, el.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_aff::set_at(int pos, const isl::pw_aff &el) const
+{
+ return isl::pw_multi_aff(*this).set_at(pos, el);
+}
+
+isl::multi_union_pw_aff multi_aff::set_at(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::pw_multi_aff(*this).set_at(pos, el);
+}
+
+isl::multi_pw_aff multi_aff::set_pw_aff(int pos, const isl::pw_aff &el) const
+{
+ return isl::pw_multi_aff(*this).set_pw_aff(pos, el);
+}
+
+isl::pw_multi_aff multi_aff::set_pw_aff(unsigned int pos, const isl::pw_aff &pa) const
+{
+ return isl::pw_multi_aff(*this).set_pw_aff(pos, pa);
+}
+
+isl::multi_aff multi_aff::set_range_tuple(isl::id id) const
+{
+ auto res = isl_multi_aff_set_range_tuple_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::multi_aff multi_aff::set_range_tuple(const std::string &id) const
+{
+ return this->set_range_tuple(isl::id(ctx(), id));
+}
+
+isl::multi_union_pw_aff multi_aff::set_union_pw_aff(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::pw_multi_aff(*this).set_union_pw_aff(pos, el);
+}
+
+class size multi_aff::size() const
+{
+ auto res = isl_multi_aff_size(get());
+ return manage(res);
+}
+
+isl::space multi_aff::space() const
+{
+ auto res = isl_multi_aff_get_space(get());
+ return manage(res);
+}
+
+isl::space multi_aff::get_space() const
+{
+ return space();
+}
+
+isl::multi_aff multi_aff::sub(isl::multi_aff multi2) const
+{
+ auto res = isl_multi_aff_sub(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_aff::sub(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).sub(multi2);
+}
+
+isl::multi_union_pw_aff multi_aff::sub(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).sub(multi2);
+}
+
+isl::pw_multi_aff multi_aff::sub(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).sub(pma2);
+}
+
+isl::union_pw_multi_aff multi_aff::sub(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_multi_aff(*this).sub(upma2);
+}
+
+isl::multi_aff multi_aff::sub(const isl::aff &multi2) const
+{
+ return this->sub(isl::multi_aff(multi2));
+}
+
+isl::pw_multi_aff multi_aff::subtract_domain(const isl::set &set) const
+{
+ return isl::pw_multi_aff(*this).subtract_domain(set);
+}
+
+isl::union_pw_multi_aff multi_aff::subtract_domain(const isl::space &space) const
+{
+ return isl::pw_multi_aff(*this).subtract_domain(space);
+}
+
+isl::union_pw_multi_aff multi_aff::subtract_domain(const isl::union_set &uset) const
+{
+ return isl::pw_multi_aff(*this).subtract_domain(uset);
+}
+
+isl::pw_multi_aff_list multi_aff::to_list() const
+{
+ return isl::pw_multi_aff(*this).to_list();
+}
+
+isl::multi_pw_aff multi_aff::to_multi_pw_aff() const
+{
+ auto res = isl_multi_aff_to_multi_pw_aff(copy());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_aff::to_multi_union_pw_aff() const
+{
+ auto res = isl_multi_aff_to_multi_union_pw_aff(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff multi_aff::to_pw_multi_aff() const
+{
+ auto res = isl_multi_aff_to_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff multi_aff::to_union_pw_multi_aff() const
+{
+ return isl::pw_multi_aff(*this).to_union_pw_multi_aff();
+}
+
+isl::id multi_aff::tuple_id(isl::dim type) const
+{
+ return isl::pw_multi_aff(*this).tuple_id(type);
+}
+
+isl::multi_aff multi_aff::unbind_params_insert_domain(isl::multi_id domain) const
+{
+ auto res = isl_multi_aff_unbind_params_insert_domain(copy(), domain.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_aff::union_add(const isl::multi_pw_aff &mpa2) const
+{
+ return isl::pw_multi_aff(*this).union_add(mpa2);
+}
+
+isl::multi_union_pw_aff multi_aff::union_add(const isl::multi_union_pw_aff &mupa2) const
+{
+ return isl::pw_multi_aff(*this).union_add(mupa2);
+}
+
+isl::pw_multi_aff multi_aff::union_add(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).union_add(pma2);
+}
+
+isl::union_pw_multi_aff multi_aff::union_add(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::pw_multi_aff(*this).union_add(upma2);
+}
+
+isl::multi_aff multi_aff::zero(isl::space space)
+{
+ auto res = isl_multi_aff_zero(space.release());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const multi_aff &obj)
+{
+ char *str = isl_multi_aff_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::multi_id
+multi_id manage(__isl_take isl_multi_id *ptr) {
+ return multi_id(ptr);
+}
+multi_id manage_copy(__isl_keep isl_multi_id *ptr) {
+ ptr = isl_multi_id_copy(ptr);
+ return multi_id(ptr);
+}
+
+multi_id::multi_id()
+ : ptr(nullptr) {}
+
+multi_id::multi_id(const multi_id &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+multi_id::multi_id(__isl_take isl_multi_id *ptr)
+ : ptr(ptr) {}
+
+multi_id::multi_id(isl::space space, isl::id_list list)
+{
+ auto res = isl_multi_id_from_id_list(space.release(), list.release());
+ ptr = res;
+}
+
+multi_id::multi_id(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_multi_id_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+multi_id &multi_id::operator=(multi_id obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+multi_id::~multi_id() {
+ if (ptr)
+ isl_multi_id_free(ptr);
+}
+
+__isl_give isl_multi_id *multi_id::copy() const & {
+ return isl_multi_id_copy(ptr);
+}
+
+__isl_keep isl_multi_id *multi_id::get() const {
+ return ptr;
+}
+
+__isl_give isl_multi_id *multi_id::release() {
+ isl_multi_id *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool multi_id::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx multi_id::ctx() const {
+ return isl::ctx(isl_multi_id_get_ctx(ptr));
+}
+
+isl::id multi_id::at(int pos) const
+{
+ auto res = isl_multi_id_get_at(get(), pos);
+ return manage(res);
+}
+
+isl::id multi_id::get_at(int pos) const
+{
+ return at(pos);
+}
+
+isl::multi_id multi_id::flat_range_product(isl::multi_id multi2) const
+{
+ auto res = isl_multi_id_flat_range_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::id_list multi_id::list() const
+{
+ auto res = isl_multi_id_get_list(get());
+ return manage(res);
+}
+
+isl::id_list multi_id::get_list() const
+{
+ return list();
+}
+
+boolean multi_id::plain_is_equal(const isl::multi_id &multi2) const
+{
+ auto res = isl_multi_id_plain_is_equal(get(), multi2.get());
+ return manage(res);
+}
+
+isl::multi_id multi_id::range_product(isl::multi_id multi2) const
+{
+ auto res = isl_multi_id_range_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_id multi_id::set_at(int pos, isl::id el) const
+{
+ auto res = isl_multi_id_set_at(copy(), pos, el.release());
+ return manage(res);
+}
+
+isl::multi_id multi_id::set_at(int pos, const std::string &el) const
+{
+ return this->set_at(pos, isl::id(ctx(), el));
+}
+
+isl::multi_id multi_id::set_id(int pos, isl::id el) const
+{
+ auto res = isl_multi_id_set_id(copy(), pos, el.release());
+ return manage(res);
+}
+
+isl::multi_id multi_id::set_id(int pos, const std::string &el) const
+{
+ return this->set_id(pos, isl::id(ctx(), el));
+}
+
+class size multi_id::size() const
+{
+ auto res = isl_multi_id_size(get());
+ return manage(res);
+}
+
+isl::space multi_id::space() const
+{
+ auto res = isl_multi_id_get_space(get());
+ return manage(res);
+}
+
+isl::space multi_id::get_space() const
+{
+ return space();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const multi_id &obj)
+{
+ char *str = isl_multi_id_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::multi_pw_aff
+multi_pw_aff manage(__isl_take isl_multi_pw_aff *ptr) {
+ return multi_pw_aff(ptr);
+}
+multi_pw_aff manage_copy(__isl_keep isl_multi_pw_aff *ptr) {
+ ptr = isl_multi_pw_aff_copy(ptr);
+ return multi_pw_aff(ptr);
+}
+
+multi_pw_aff::multi_pw_aff()
+ : ptr(nullptr) {}
+
+multi_pw_aff::multi_pw_aff(const multi_pw_aff &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+multi_pw_aff::multi_pw_aff(__isl_take isl_multi_pw_aff *ptr)
+ : ptr(ptr) {}
+
+multi_pw_aff::multi_pw_aff(isl::aff aff)
+{
+ auto res = isl_multi_pw_aff_from_aff(aff.release());
+ ptr = res;
+}
+
+multi_pw_aff::multi_pw_aff(isl::multi_aff ma)
+{
+ auto res = isl_multi_pw_aff_from_multi_aff(ma.release());
+ ptr = res;
+}
+
+multi_pw_aff::multi_pw_aff(isl::pw_aff pa)
+{
+ auto res = isl_multi_pw_aff_from_pw_aff(pa.release());
+ ptr = res;
+}
+
+multi_pw_aff::multi_pw_aff(isl::space space, isl::pw_aff_list list)
+{
+ auto res = isl_multi_pw_aff_from_pw_aff_list(space.release(), list.release());
+ ptr = res;
+}
+
+multi_pw_aff::multi_pw_aff(isl::pw_multi_aff pma)
+{
+ auto res = isl_multi_pw_aff_from_pw_multi_aff(pma.release());
+ ptr = res;
+}
+
+multi_pw_aff::multi_pw_aff(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_multi_pw_aff_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+multi_pw_aff &multi_pw_aff::operator=(multi_pw_aff obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+multi_pw_aff::~multi_pw_aff() {
+ if (ptr)
+ isl_multi_pw_aff_free(ptr);
+}
+
+__isl_give isl_multi_pw_aff *multi_pw_aff::copy() const & {
+ return isl_multi_pw_aff_copy(ptr);
+}
+
+__isl_keep isl_multi_pw_aff *multi_pw_aff::get() const {
+ return ptr;
+}
+
+__isl_give isl_multi_pw_aff *multi_pw_aff::release() {
+ isl_multi_pw_aff *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool multi_pw_aff::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx multi_pw_aff::ctx() const {
+ return isl::ctx(isl_multi_pw_aff_get_ctx(ptr));
+}
+
+isl::multi_pw_aff multi_pw_aff::add(isl::multi_pw_aff multi2) const
+{
+ auto res = isl_multi_pw_aff_add(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_pw_aff::add(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_union_pw_aff(*this).add(multi2);
+}
+
+isl::multi_pw_aff multi_pw_aff::add(const isl::aff &multi2) const
+{
+ return this->add(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::add(const isl::multi_aff &multi2) const
+{
+ return this->add(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::add(const isl::pw_aff &multi2) const
+{
+ return this->add(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::add(const isl::pw_multi_aff &multi2) const
+{
+ return this->add(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::add_constant(isl::multi_val mv) const
+{
+ auto res = isl_multi_pw_aff_add_constant_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::add_constant(isl::val v) const
+{
+ auto res = isl_multi_pw_aff_add_constant_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::add_constant(long v) const
+{
+ return this->add_constant(isl::val(ctx(), v));
+}
+
+isl::map multi_pw_aff::as_map() const
+{
+ auto res = isl_multi_pw_aff_as_map(copy());
+ return manage(res);
+}
+
+isl::multi_aff multi_pw_aff::as_multi_aff() const
+{
+ auto res = isl_multi_pw_aff_as_multi_aff(copy());
+ return manage(res);
+}
+
+isl::set multi_pw_aff::as_set() const
+{
+ auto res = isl_multi_pw_aff_as_set(copy());
+ return manage(res);
+}
+
+isl::pw_aff multi_pw_aff::at(int pos) const
+{
+ auto res = isl_multi_pw_aff_get_at(get(), pos);
+ return manage(res);
+}
+
+isl::pw_aff multi_pw_aff::get_at(int pos) const
+{
+ return at(pos);
+}
+
+isl::set multi_pw_aff::bind(isl::multi_id tuple) const
+{
+ auto res = isl_multi_pw_aff_bind(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::bind_domain(isl::multi_id tuple) const
+{
+ auto res = isl_multi_pw_aff_bind_domain(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::bind_domain_wrapped_domain(isl::multi_id tuple) const
+{
+ auto res = isl_multi_pw_aff_bind_domain_wrapped_domain(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::coalesce() const
+{
+ auto res = isl_multi_pw_aff_coalesce(copy());
+ return manage(res);
+}
+
+class size multi_pw_aff::dim(isl::dim type) const
+{
+ auto res = isl_multi_pw_aff_dim(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::set multi_pw_aff::domain() const
+{
+ auto res = isl_multi_pw_aff_domain(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::flat_range_product(isl::multi_pw_aff multi2) const
+{
+ auto res = isl_multi_pw_aff_flat_range_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_pw_aff::flat_range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_union_pw_aff(*this).flat_range_product(multi2);
+}
+
+isl::multi_pw_aff multi_pw_aff::flat_range_product(const isl::aff &multi2) const
+{
+ return this->flat_range_product(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::flat_range_product(const isl::multi_aff &multi2) const
+{
+ return this->flat_range_product(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::flat_range_product(const isl::pw_aff &multi2) const
+{
+ return this->flat_range_product(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::flat_range_product(const isl::pw_multi_aff &multi2) const
+{
+ return this->flat_range_product(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::gist(isl::set set) const
+{
+ auto res = isl_multi_pw_aff_gist(copy(), set.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_pw_aff::gist(const isl::union_set &context) const
+{
+ return isl::multi_union_pw_aff(*this).gist(context);
+}
+
+isl::multi_pw_aff multi_pw_aff::gist(const isl::basic_set &set) const
+{
+ return this->gist(isl::set(set));
+}
+
+isl::multi_pw_aff multi_pw_aff::gist(const isl::point &set) const
+{
+ return this->gist(isl::set(set));
+}
+
+boolean multi_pw_aff::has_range_tuple_id() const
+{
+ auto res = isl_multi_pw_aff_has_range_tuple_id(get());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::identity(isl::space space)
+{
+ auto res = isl_multi_pw_aff_identity(space.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::identity() const
+{
+ auto res = isl_multi_pw_aff_identity_multi_pw_aff(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::identity_on_domain(isl::space space)
+{
+ auto res = isl_multi_pw_aff_identity_on_domain_space(space.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::insert_domain(isl::space domain) const
+{
+ auto res = isl_multi_pw_aff_insert_domain(copy(), domain.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::intersect_domain(isl::set domain) const
+{
+ auto res = isl_multi_pw_aff_intersect_domain(copy(), domain.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_pw_aff::intersect_domain(const isl::union_set &uset) const
+{
+ return isl::multi_union_pw_aff(*this).intersect_domain(uset);
+}
+
+isl::multi_pw_aff multi_pw_aff::intersect_domain(const isl::basic_set &domain) const
+{
+ return this->intersect_domain(isl::set(domain));
+}
+
+isl::multi_pw_aff multi_pw_aff::intersect_domain(const isl::point &domain) const
+{
+ return this->intersect_domain(isl::set(domain));
+}
+
+isl::multi_pw_aff multi_pw_aff::intersect_params(isl::set set) const
+{
+ auto res = isl_multi_pw_aff_intersect_params(copy(), set.release());
+ return manage(res);
+}
+
+boolean multi_pw_aff::involves_nan() const
+{
+ auto res = isl_multi_pw_aff_involves_nan(get());
+ return manage(res);
+}
+
+boolean multi_pw_aff::involves_param(const isl::id &id) const
+{
+ auto res = isl_multi_pw_aff_involves_param_id(get(), id.get());
+ return manage(res);
+}
+
+boolean multi_pw_aff::involves_param(const std::string &id) const
+{
+ return this->involves_param(isl::id(ctx(), id));
+}
+
+boolean multi_pw_aff::involves_param(const isl::id_list &list) const
+{
+ auto res = isl_multi_pw_aff_involves_param_id_list(get(), list.get());
+ return manage(res);
+}
+
+boolean multi_pw_aff::isa_multi_aff() const
+{
+ auto res = isl_multi_pw_aff_isa_multi_aff(get());
+ return manage(res);
+}
+
+isl::pw_aff_list multi_pw_aff::list() const
+{
+ auto res = isl_multi_pw_aff_get_list(get());
+ return manage(res);
+}
+
+isl::pw_aff_list multi_pw_aff::get_list() const
+{
+ return list();
+}
+
+isl::multi_pw_aff multi_pw_aff::max(isl::multi_pw_aff multi2) const
+{
+ auto res = isl_multi_pw_aff_max(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_val multi_pw_aff::max_multi_val() const
+{
+ auto res = isl_multi_pw_aff_max_multi_val(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::min(isl::multi_pw_aff multi2) const
+{
+ auto res = isl_multi_pw_aff_min(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_val multi_pw_aff::min_multi_val() const
+{
+ auto res = isl_multi_pw_aff_min_multi_val(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::neg() const
+{
+ auto res = isl_multi_pw_aff_neg(copy());
+ return manage(res);
+}
+
+boolean multi_pw_aff::plain_is_equal(const isl::multi_pw_aff &multi2) const
+{
+ auto res = isl_multi_pw_aff_plain_is_equal(get(), multi2.get());
+ return manage(res);
+}
+
+boolean multi_pw_aff::plain_is_equal(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_union_pw_aff(*this).plain_is_equal(multi2);
+}
+
+boolean multi_pw_aff::plain_is_equal(const isl::aff &multi2) const
+{
+ return this->plain_is_equal(isl::multi_pw_aff(multi2));
+}
+
+boolean multi_pw_aff::plain_is_equal(const isl::multi_aff &multi2) const
+{
+ return this->plain_is_equal(isl::multi_pw_aff(multi2));
+}
+
+boolean multi_pw_aff::plain_is_equal(const isl::pw_aff &multi2) const
+{
+ return this->plain_is_equal(isl::multi_pw_aff(multi2));
+}
+
+boolean multi_pw_aff::plain_is_equal(const isl::pw_multi_aff &multi2) const
+{
+ return this->plain_is_equal(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::product(isl::multi_pw_aff multi2) const
+{
+ auto res = isl_multi_pw_aff_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::pullback(isl::multi_aff ma) const
+{
+ auto res = isl_multi_pw_aff_pullback_multi_aff(copy(), ma.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::pullback(isl::multi_pw_aff mpa2) const
+{
+ auto res = isl_multi_pw_aff_pullback_multi_pw_aff(copy(), mpa2.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::pullback(isl::pw_multi_aff pma) const
+{
+ auto res = isl_multi_pw_aff_pullback_pw_multi_aff(copy(), pma.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_pw_aff::pullback(const isl::union_pw_multi_aff &upma) const
+{
+ return isl::multi_union_pw_aff(*this).pullback(upma);
+}
+
+isl::multi_pw_aff multi_pw_aff::range_product(isl::multi_pw_aff multi2) const
+{
+ auto res = isl_multi_pw_aff_range_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_pw_aff::range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_union_pw_aff(*this).range_product(multi2);
+}
+
+isl::multi_pw_aff multi_pw_aff::range_product(const isl::aff &multi2) const
+{
+ return this->range_product(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::range_product(const isl::multi_aff &multi2) const
+{
+ return this->range_product(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::range_product(const isl::pw_aff &multi2) const
+{
+ return this->range_product(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::range_product(const isl::pw_multi_aff &multi2) const
+{
+ return this->range_product(isl::multi_pw_aff(multi2));
+}
+
+isl::id multi_pw_aff::range_tuple_id() const
+{
+ auto res = isl_multi_pw_aff_get_range_tuple_id(get());
+ return manage(res);
+}
+
+isl::id multi_pw_aff::get_range_tuple_id() const
+{
+ return range_tuple_id();
+}
+
+isl::multi_pw_aff multi_pw_aff::reset_range_tuple_id() const
+{
+ auto res = isl_multi_pw_aff_reset_range_tuple_id(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::reset_tuple_id(isl::dim type) const
+{
+ auto res = isl_multi_pw_aff_reset_tuple_id(copy(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::scale(isl::multi_val mv) const
+{
+ auto res = isl_multi_pw_aff_scale_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::scale(isl::val v) const
+{
+ auto res = isl_multi_pw_aff_scale_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::scale(long v) const
+{
+ return this->scale(isl::val(ctx(), v));
+}
+
+isl::multi_pw_aff multi_pw_aff::scale_down(isl::multi_val mv) const
+{
+ auto res = isl_multi_pw_aff_scale_down_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::scale_down(isl::val v) const
+{
+ auto res = isl_multi_pw_aff_scale_down_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::scale_down(long v) const
+{
+ return this->scale_down(isl::val(ctx(), v));
+}
+
+isl::multi_pw_aff multi_pw_aff::set_at(int pos, isl::pw_aff el) const
+{
+ auto res = isl_multi_pw_aff_set_at(copy(), pos, el.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_pw_aff::set_at(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::multi_union_pw_aff(*this).set_at(pos, el);
+}
+
+isl::multi_pw_aff multi_pw_aff::set_pw_aff(int pos, isl::pw_aff el) const
+{
+ auto res = isl_multi_pw_aff_set_pw_aff(copy(), pos, el.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::set_range_tuple(isl::id id) const
+{
+ auto res = isl_multi_pw_aff_set_range_tuple_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::set_range_tuple(const std::string &id) const
+{
+ return this->set_range_tuple(isl::id(ctx(), id));
+}
+
+isl::multi_union_pw_aff multi_pw_aff::set_union_pw_aff(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::multi_union_pw_aff(*this).set_union_pw_aff(pos, el);
+}
+
+class size multi_pw_aff::size() const
+{
+ auto res = isl_multi_pw_aff_size(get());
+ return manage(res);
+}
+
+isl::space multi_pw_aff::space() const
+{
+ auto res = isl_multi_pw_aff_get_space(get());
+ return manage(res);
+}
+
+isl::space multi_pw_aff::get_space() const
+{
+ return space();
+}
+
+isl::multi_pw_aff multi_pw_aff::sub(isl::multi_pw_aff multi2) const
+{
+ auto res = isl_multi_pw_aff_sub(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_pw_aff::sub(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_union_pw_aff(*this).sub(multi2);
+}
+
+isl::multi_pw_aff multi_pw_aff::sub(const isl::aff &multi2) const
+{
+ return this->sub(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::sub(const isl::multi_aff &multi2) const
+{
+ return this->sub(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::sub(const isl::pw_aff &multi2) const
+{
+ return this->sub(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::sub(const isl::pw_multi_aff &multi2) const
+{
+ return this->sub(isl::multi_pw_aff(multi2));
+}
+
+isl::multi_pw_aff multi_pw_aff::unbind_params_insert_domain(isl::multi_id domain) const
+{
+ auto res = isl_multi_pw_aff_unbind_params_insert_domain(copy(), domain.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff multi_pw_aff::union_add(isl::multi_pw_aff mpa2) const
+{
+ auto res = isl_multi_pw_aff_union_add(copy(), mpa2.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_pw_aff::union_add(const isl::multi_union_pw_aff &mupa2) const
+{
+ return isl::multi_union_pw_aff(*this).union_add(mupa2);
+}
+
+isl::multi_pw_aff multi_pw_aff::union_add(const isl::aff &mpa2) const
+{
+ return this->union_add(isl::multi_pw_aff(mpa2));
+}
+
+isl::multi_pw_aff multi_pw_aff::union_add(const isl::multi_aff &mpa2) const
+{
+ return this->union_add(isl::multi_pw_aff(mpa2));
+}
+
+isl::multi_pw_aff multi_pw_aff::union_add(const isl::pw_aff &mpa2) const
+{
+ return this->union_add(isl::multi_pw_aff(mpa2));
+}
+
+isl::multi_pw_aff multi_pw_aff::union_add(const isl::pw_multi_aff &mpa2) const
+{
+ return this->union_add(isl::multi_pw_aff(mpa2));
+}
+
+isl::multi_pw_aff multi_pw_aff::zero(isl::space space)
+{
+ auto res = isl_multi_pw_aff_zero(space.release());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const multi_pw_aff &obj)
+{
+ char *str = isl_multi_pw_aff_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::multi_union_pw_aff
+multi_union_pw_aff manage(__isl_take isl_multi_union_pw_aff *ptr) {
+ return multi_union_pw_aff(ptr);
+}
+multi_union_pw_aff manage_copy(__isl_keep isl_multi_union_pw_aff *ptr) {
+ ptr = isl_multi_union_pw_aff_copy(ptr);
+ return multi_union_pw_aff(ptr);
+}
+
+multi_union_pw_aff::multi_union_pw_aff()
+ : ptr(nullptr) {}
+
+multi_union_pw_aff::multi_union_pw_aff(const multi_union_pw_aff &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+multi_union_pw_aff::multi_union_pw_aff(__isl_take isl_multi_union_pw_aff *ptr)
+ : ptr(ptr) {}
+
+multi_union_pw_aff::multi_union_pw_aff(isl::multi_pw_aff mpa)
+{
+ auto res = isl_multi_union_pw_aff_from_multi_pw_aff(mpa.release());
+ ptr = res;
+}
+
+multi_union_pw_aff::multi_union_pw_aff(isl::union_pw_aff upa)
+{
+ auto res = isl_multi_union_pw_aff_from_union_pw_aff(upa.release());
+ ptr = res;
+}
+
+multi_union_pw_aff::multi_union_pw_aff(isl::space space, isl::union_pw_aff_list list)
+{
+ auto res = isl_multi_union_pw_aff_from_union_pw_aff_list(space.release(), list.release());
+ ptr = res;
+}
+
+multi_union_pw_aff::multi_union_pw_aff(isl::union_pw_multi_aff upma)
+{
+ auto res = isl_multi_union_pw_aff_from_union_pw_multi_aff(upma.release());
+ ptr = res;
+}
+
+multi_union_pw_aff::multi_union_pw_aff(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_multi_union_pw_aff_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+multi_union_pw_aff &multi_union_pw_aff::operator=(multi_union_pw_aff obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+multi_union_pw_aff::~multi_union_pw_aff() {
+ if (ptr)
+ isl_multi_union_pw_aff_free(ptr);
+}
+
+__isl_give isl_multi_union_pw_aff *multi_union_pw_aff::copy() const & {
+ return isl_multi_union_pw_aff_copy(ptr);
+}
+
+__isl_keep isl_multi_union_pw_aff *multi_union_pw_aff::get() const {
+ return ptr;
+}
+
+__isl_give isl_multi_union_pw_aff *multi_union_pw_aff::release() {
+ isl_multi_union_pw_aff *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool multi_union_pw_aff::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx multi_union_pw_aff::ctx() const {
+ return isl::ctx(isl_multi_union_pw_aff_get_ctx(ptr));
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::add(isl::multi_union_pw_aff multi2) const
+{
+ auto res = isl_multi_union_pw_aff_add(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::union_pw_aff multi_union_pw_aff::at(int pos) const
+{
+ auto res = isl_multi_union_pw_aff_get_at(get(), pos);
+ return manage(res);
+}
+
+isl::union_pw_aff multi_union_pw_aff::get_at(int pos) const
+{
+ return at(pos);
+}
+
+isl::union_set multi_union_pw_aff::bind(isl::multi_id tuple) const
+{
+ auto res = isl_multi_union_pw_aff_bind(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::coalesce() const
+{
+ auto res = isl_multi_union_pw_aff_coalesce(copy());
+ return manage(res);
+}
+
+class size multi_union_pw_aff::dim(isl::dim type) const
+{
+ auto res = isl_multi_union_pw_aff_dim(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::union_set multi_union_pw_aff::domain() const
+{
+ auto res = isl_multi_union_pw_aff_domain(copy());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::flat_range_product(isl::multi_union_pw_aff multi2) const
+{
+ auto res = isl_multi_union_pw_aff_flat_range_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::from_union_map(isl::union_map umap)
+{
+ auto res = isl_multi_union_pw_aff_from_union_map(umap.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::gist(isl::union_set context) const
+{
+ auto res = isl_multi_union_pw_aff_gist(copy(), context.release());
+ return manage(res);
+}
+
+boolean multi_union_pw_aff::has_range_tuple_id() const
+{
+ auto res = isl_multi_union_pw_aff_has_range_tuple_id(get());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::intersect_domain(isl::union_set uset) const
+{
+ auto res = isl_multi_union_pw_aff_intersect_domain(copy(), uset.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::intersect_params(isl::set params) const
+{
+ auto res = isl_multi_union_pw_aff_intersect_params(copy(), params.release());
+ return manage(res);
+}
+
+boolean multi_union_pw_aff::involves_nan() const
+{
+ auto res = isl_multi_union_pw_aff_involves_nan(get());
+ return manage(res);
+}
+
+isl::union_pw_aff_list multi_union_pw_aff::list() const
+{
+ auto res = isl_multi_union_pw_aff_get_list(get());
+ return manage(res);
+}
+
+isl::union_pw_aff_list multi_union_pw_aff::get_list() const
+{
+ return list();
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::neg() const
+{
+ auto res = isl_multi_union_pw_aff_neg(copy());
+ return manage(res);
+}
+
+boolean multi_union_pw_aff::plain_is_equal(const isl::multi_union_pw_aff &multi2) const
+{
+ auto res = isl_multi_union_pw_aff_plain_is_equal(get(), multi2.get());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::pullback(isl::union_pw_multi_aff upma) const
+{
+ auto res = isl_multi_union_pw_aff_pullback_union_pw_multi_aff(copy(), upma.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::range_product(isl::multi_union_pw_aff multi2) const
+{
+ auto res = isl_multi_union_pw_aff_range_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::id multi_union_pw_aff::range_tuple_id() const
+{
+ auto res = isl_multi_union_pw_aff_get_range_tuple_id(get());
+ return manage(res);
+}
+
+isl::id multi_union_pw_aff::get_range_tuple_id() const
+{
+ return range_tuple_id();
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::reset_range_tuple_id() const
+{
+ auto res = isl_multi_union_pw_aff_reset_range_tuple_id(copy());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::reset_tuple_id(isl::dim type) const
+{
+ auto res = isl_multi_union_pw_aff_reset_tuple_id(copy(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::scale(isl::multi_val mv) const
+{
+ auto res = isl_multi_union_pw_aff_scale_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::scale(isl::val v) const
+{
+ auto res = isl_multi_union_pw_aff_scale_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::scale(long v) const
+{
+ return this->scale(isl::val(ctx(), v));
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::scale_down(isl::multi_val mv) const
+{
+ auto res = isl_multi_union_pw_aff_scale_down_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::scale_down(isl::val v) const
+{
+ auto res = isl_multi_union_pw_aff_scale_down_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::scale_down(long v) const
+{
+ return this->scale_down(isl::val(ctx(), v));
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::set_at(int pos, isl::union_pw_aff el) const
+{
+ auto res = isl_multi_union_pw_aff_set_at(copy(), pos, el.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::set_range_tuple(isl::id id) const
+{
+ auto res = isl_multi_union_pw_aff_set_range_tuple_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::set_range_tuple(const std::string &id) const
+{
+ return this->set_range_tuple(isl::id(ctx(), id));
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::set_union_pw_aff(int pos, isl::union_pw_aff el) const
+{
+ auto res = isl_multi_union_pw_aff_set_union_pw_aff(copy(), pos, el.release());
+ return manage(res);
+}
+
+class size multi_union_pw_aff::size() const
+{
+ auto res = isl_multi_union_pw_aff_size(get());
+ return manage(res);
+}
+
+isl::space multi_union_pw_aff::space() const
+{
+ auto res = isl_multi_union_pw_aff_get_space(get());
+ return manage(res);
+}
+
+isl::space multi_union_pw_aff::get_space() const
+{
+ return space();
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::sub(isl::multi_union_pw_aff multi2) const
+{
+ auto res = isl_multi_union_pw_aff_sub(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::union_add(isl::multi_union_pw_aff mupa2) const
+{
+ auto res = isl_multi_union_pw_aff_union_add(copy(), mupa2.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff multi_union_pw_aff::zero(isl::space space)
+{
+ auto res = isl_multi_union_pw_aff_zero(space.release());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const multi_union_pw_aff &obj)
+{
+ char *str = isl_multi_union_pw_aff_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::multi_val
+multi_val manage(__isl_take isl_multi_val *ptr) {
+ return multi_val(ptr);
+}
+multi_val manage_copy(__isl_keep isl_multi_val *ptr) {
+ ptr = isl_multi_val_copy(ptr);
+ return multi_val(ptr);
+}
+
+multi_val::multi_val()
+ : ptr(nullptr) {}
+
+multi_val::multi_val(const multi_val &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+multi_val::multi_val(__isl_take isl_multi_val *ptr)
+ : ptr(ptr) {}
+
+multi_val::multi_val(isl::space space, isl::val_list list)
+{
+ auto res = isl_multi_val_from_val_list(space.release(), list.release());
+ ptr = res;
+}
+
+multi_val::multi_val(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_multi_val_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+multi_val &multi_val::operator=(multi_val obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+multi_val::~multi_val() {
+ if (ptr)
+ isl_multi_val_free(ptr);
+}
+
+__isl_give isl_multi_val *multi_val::copy() const & {
+ return isl_multi_val_copy(ptr);
+}
+
+__isl_keep isl_multi_val *multi_val::get() const {
+ return ptr;
+}
+
+__isl_give isl_multi_val *multi_val::release() {
+ isl_multi_val *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool multi_val::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx multi_val::ctx() const {
+ return isl::ctx(isl_multi_val_get_ctx(ptr));
+}
+
+isl::multi_val multi_val::add(isl::multi_val multi2) const
+{
+ auto res = isl_multi_val_add(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::add(isl::val v) const
+{
+ auto res = isl_multi_val_add_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::add(long v) const
+{
+ return this->add(isl::val(ctx(), v));
+}
+
+isl::val multi_val::at(int pos) const
+{
+ auto res = isl_multi_val_get_at(get(), pos);
+ return manage(res);
+}
+
+isl::val multi_val::get_at(int pos) const
+{
+ return at(pos);
+}
+
+class size multi_val::dim(isl::dim type) const
+{
+ auto res = isl_multi_val_dim(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::multi_val multi_val::flat_range_product(isl::multi_val multi2) const
+{
+ auto res = isl_multi_val_flat_range_product(copy(), multi2.release());
+ return manage(res);
+}
+
+boolean multi_val::has_range_tuple_id() const
+{
+ auto res = isl_multi_val_has_range_tuple_id(get());
+ return manage(res);
+}
+
+boolean multi_val::involves_nan() const
+{
+ auto res = isl_multi_val_involves_nan(get());
+ return manage(res);
+}
+
+isl::val_list multi_val::list() const
+{
+ auto res = isl_multi_val_get_list(get());
+ return manage(res);
+}
+
+isl::val_list multi_val::get_list() const
+{
+ return list();
+}
+
+isl::multi_val multi_val::max(isl::multi_val multi2) const
+{
+ auto res = isl_multi_val_max(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::min(isl::multi_val multi2) const
+{
+ auto res = isl_multi_val_min(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::neg() const
+{
+ auto res = isl_multi_val_neg(copy());
+ return manage(res);
+}
+
+boolean multi_val::plain_is_equal(const isl::multi_val &multi2) const
+{
+ auto res = isl_multi_val_plain_is_equal(get(), multi2.get());
+ return manage(res);
+}
+
+isl::multi_val multi_val::product(isl::multi_val multi2) const
+{
+ auto res = isl_multi_val_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::range_product(isl::multi_val multi2) const
+{
+ auto res = isl_multi_val_range_product(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::id multi_val::range_tuple_id() const
+{
+ auto res = isl_multi_val_get_range_tuple_id(get());
+ return manage(res);
+}
+
+isl::id multi_val::get_range_tuple_id() const
+{
+ return range_tuple_id();
+}
+
+isl::multi_val multi_val::reset_range_tuple_id() const
+{
+ auto res = isl_multi_val_reset_range_tuple_id(copy());
+ return manage(res);
+}
+
+isl::multi_val multi_val::reset_tuple_id(isl::dim type) const
+{
+ auto res = isl_multi_val_reset_tuple_id(copy(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::multi_val multi_val::scale(isl::multi_val mv) const
+{
+ auto res = isl_multi_val_scale_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::scale(isl::val v) const
+{
+ auto res = isl_multi_val_scale_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::scale(long v) const
+{
+ return this->scale(isl::val(ctx(), v));
+}
+
+isl::multi_val multi_val::scale_down(isl::multi_val mv) const
+{
+ auto res = isl_multi_val_scale_down_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::scale_down(isl::val v) const
+{
+ auto res = isl_multi_val_scale_down_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::scale_down(long v) const
+{
+ return this->scale_down(isl::val(ctx(), v));
+}
+
+isl::multi_val multi_val::set_at(int pos, isl::val el) const
+{
+ auto res = isl_multi_val_set_at(copy(), pos, el.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::set_at(int pos, long el) const
+{
+ return this->set_at(pos, isl::val(ctx(), el));
+}
+
+isl::multi_val multi_val::set_range_tuple(isl::id id) const
+{
+ auto res = isl_multi_val_set_range_tuple_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::set_range_tuple(const std::string &id) const
+{
+ return this->set_range_tuple(isl::id(ctx(), id));
+}
+
+isl::multi_val multi_val::set_val(int pos, isl::val el) const
+{
+ auto res = isl_multi_val_set_val(copy(), pos, el.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::set_val(int pos, long el) const
+{
+ return this->set_val(pos, isl::val(ctx(), el));
+}
+
+class size multi_val::size() const
+{
+ auto res = isl_multi_val_size(get());
+ return manage(res);
+}
+
+isl::space multi_val::space() const
+{
+ auto res = isl_multi_val_get_space(get());
+ return manage(res);
+}
+
+isl::space multi_val::get_space() const
+{
+ return space();
+}
+
+isl::multi_val multi_val::sub(isl::multi_val multi2) const
+{
+ auto res = isl_multi_val_sub(copy(), multi2.release());
+ return manage(res);
+}
+
+isl::multi_val multi_val::zero(isl::space space)
+{
+ auto res = isl_multi_val_zero(space.release());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const multi_val &obj)
+{
+ char *str = isl_multi_val_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::point
+point manage(__isl_take isl_point *ptr) {
+ return point(ptr);
+}
+point manage_copy(__isl_keep isl_point *ptr) {
+ ptr = isl_point_copy(ptr);
+ return point(ptr);
+}
+
+point::point()
+ : ptr(nullptr) {}
+
+point::point(const point &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+point::point(__isl_take isl_point *ptr)
+ : ptr(ptr) {}
+
+point::point(isl::space space)
+{
+ auto res = isl_point_zero(space.release());
+ ptr = res;
+}
+
+point &point::operator=(point obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+point::~point() {
+ if (ptr)
+ isl_point_free(ptr);
+}
+
+__isl_give isl_point *point::copy() const & {
+ return isl_point_copy(ptr);
+}
+
+__isl_keep isl_point *point::get() const {
+ return ptr;
+}
+
+__isl_give isl_point *point::release() {
+ isl_point *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool point::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx point::ctx() const {
+ return isl::ctx(isl_point_get_ctx(ptr));
+}
+
+isl::set point::add_constraint(const isl::constraint &constraint) const
+{
+ return isl::basic_set(*this).add_constraint(constraint);
+}
+
+isl::set point::add_dims(isl::dim type, unsigned int n) const
+{
+ return isl::basic_set(*this).add_dims(type, n);
+}
+
+isl::basic_set point::affine_hull() const
+{
+ return isl::basic_set(*this).affine_hull();
+}
+
+isl::set point::align_params(const isl::space &model) const
+{
+ return isl::basic_set(*this).align_params(model);
+}
+
+isl::basic_set point::apply(const isl::basic_map &bmap) const
+{
+ return isl::basic_set(*this).apply(bmap);
+}
+
+isl::set point::apply(const isl::map &map) const
+{
+ return isl::basic_set(*this).apply(map);
+}
+
+isl::union_set point::apply(const isl::union_map &umap) const
+{
+ return isl::basic_set(*this).apply(umap);
+}
+
+isl::pw_multi_aff point::as_pw_multi_aff() const
+{
+ return isl::basic_set(*this).as_pw_multi_aff();
+}
+
+isl::set point::as_set() const
+{
+ return isl::basic_set(*this).as_set();
+}
+
+isl::basic_set_list point::basic_set_list() const
+{
+ return isl::basic_set(*this).basic_set_list();
+}
+
+isl::set point::bind(const isl::multi_id &tuple) const
+{
+ return isl::basic_set(*this).bind(tuple);
+}
+
+isl::set point::coalesce() const
+{
+ return isl::basic_set(*this).coalesce();
+}
+
+isl::set point::complement() const
+{
+ return isl::basic_set(*this).complement();
+}
+
+isl::union_set point::compute_divs() const
+{
+ return isl::basic_set(*this).compute_divs();
+}
+
+boolean point::contains(const isl::space &space) const
+{
+ return isl::basic_set(*this).contains(space);
+}
+
+isl::basic_set point::convex_hull() const
+{
+ return isl::basic_set(*this).convex_hull();
+}
+
+isl::val point::coordinate_val(isl::dim type, int pos) const
+{
+ auto res = isl_point_get_coordinate_val(get(), static_cast<enum isl_dim_type>(type), pos);
+ return manage(res);
+}
+
+isl::val point::get_coordinate_val(isl::dim type, int pos) const
+{
+ return coordinate_val(type, pos);
+}
+
+isl::basic_set point::detect_equalities() const
+{
+ return isl::basic_set(*this).detect_equalities();
+}
+
+class size point::dim(isl::dim type) const
+{
+ return isl::basic_set(*this).dim(type);
+}
+
+boolean point::dim_has_any_lower_bound(isl::dim type, unsigned int pos) const
+{
+ return isl::basic_set(*this).dim_has_any_lower_bound(type, pos);
+}
+
+isl::id point::dim_id(isl::dim type, unsigned int pos) const
+{
+ return isl::basic_set(*this).dim_id(type, pos);
+}
+
+isl::pw_aff point::dim_max(int pos) const
+{
+ return isl::basic_set(*this).dim_max(pos);
+}
+
+isl::val point::dim_max_val(int pos) const
+{
+ return isl::basic_set(*this).dim_max_val(pos);
+}
+
+isl::pw_aff point::dim_min(int pos) const
+{
+ return isl::basic_set(*this).dim_min(pos);
+}
+
+isl::val point::dim_min_val(int pos) const
+{
+ return isl::basic_set(*this).dim_min_val(pos);
+}
+
+std::string point::dim_name(isl::dim type, unsigned int pos) const
+{
+ return isl::basic_set(*this).dim_name(type, pos);
+}
+
+isl::aff point::div(int pos) const
+{
+ return isl::basic_set(*this).div(pos);
+}
+
+isl::set point::drop_constraints_involving_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::basic_set(*this).drop_constraints_involving_dims(type, first, n);
+}
+
+isl::set point::eliminate(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::basic_set(*this).eliminate(type, first, n);
+}
+
+boolean point::every_set(const std::function<boolean(isl::set)> &test) const
+{
+ return isl::basic_set(*this).every_set(test);
+}
+
+isl::set point::extract_set(const isl::space &space) const
+{
+ return isl::basic_set(*this).extract_set(space);
+}
+
+int point::find_dim_by_id(isl::dim type, const isl::id &id) const
+{
+ return isl::basic_set(*this).find_dim_by_id(type, id);
+}
+
+int point::find_dim_by_id(isl::dim type, const std::string &id) const
+{
+ return this->find_dim_by_id(type, isl::id(ctx(), id));
+}
+
+isl::basic_set point::fix_si(isl::dim type, unsigned int pos, int value) const
+{
+ return isl::basic_set(*this).fix_si(type, pos, value);
+}
+
+isl::basic_set point::fix_val(isl::dim type, unsigned int pos, const isl::val &v) const
+{
+ return isl::basic_set(*this).fix_val(type, pos, v);
+}
+
+isl::basic_set point::fix_val(isl::dim type, unsigned int pos, long v) const
+{
+ return this->fix_val(type, pos, isl::val(ctx(), v));
+}
+
+isl::basic_set point::flatten() const
+{
+ return isl::basic_set(*this).flatten();
+}
+
+stat point::foreach_basic_set(const std::function<stat(isl::basic_set)> &fn) const
+{
+ return isl::basic_set(*this).foreach_basic_set(fn);
+}
+
+stat point::foreach_point(const std::function<stat(isl::point)> &fn) const
+{
+ return isl::basic_set(*this).foreach_point(fn);
+}
+
+stat point::foreach_set(const std::function<stat(isl::set)> &fn) const
+{
+ return isl::basic_set(*this).foreach_set(fn);
+}
+
+isl::basic_set point::gist(const isl::basic_set &context) const
+{
+ return isl::basic_set(*this).gist(context);
+}
+
+isl::set point::gist(const isl::set &context) const
+{
+ return isl::basic_set(*this).gist(context);
+}
+
+isl::union_set point::gist(const isl::union_set &context) const
+{
+ return isl::basic_set(*this).gist(context);
+}
+
+isl::set point::gist_params(const isl::set &context) const
+{
+ return isl::basic_set(*this).gist_params(context);
+}
+
+boolean point::has_equal_space(const isl::set &set2) const
+{
+ return isl::basic_set(*this).has_equal_space(set2);
+}
+
+isl::map point::identity() const
+{
+ return isl::basic_set(*this).identity();
+}
+
+isl::union_pw_multi_aff point::identity_union_pw_multi_aff() const
+{
+ return isl::basic_set(*this).identity_union_pw_multi_aff();
+}
+
+isl::pw_aff point::indicator_function() const
+{
+ return isl::basic_set(*this).indicator_function();
+}
+
+isl::set point::insert_dims(isl::dim type, unsigned int pos, unsigned int n) const
+{
+ return isl::basic_set(*this).insert_dims(type, pos, n);
+}
+
+isl::map point::insert_domain(const isl::space &domain) const
+{
+ return isl::basic_set(*this).insert_domain(domain);
+}
+
+isl::basic_set point::intersect(const isl::basic_set &bset2) const
+{
+ return isl::basic_set(*this).intersect(bset2);
+}
+
+isl::set point::intersect(const isl::set &set2) const
+{
+ return isl::basic_set(*this).intersect(set2);
+}
+
+isl::union_set point::intersect(const isl::union_set &uset2) const
+{
+ return isl::basic_set(*this).intersect(uset2);
+}
+
+isl::basic_set point::intersect_params(const isl::basic_set &bset2) const
+{
+ return isl::basic_set(*this).intersect_params(bset2);
+}
+
+isl::set point::intersect_params(const isl::set &params) const
+{
+ return isl::basic_set(*this).intersect_params(params);
+}
+
+boolean point::involves_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::basic_set(*this).involves_dims(type, first, n);
+}
+
+boolean point::involves_locals() const
+{
+ return isl::basic_set(*this).involves_locals();
+}
+
+boolean point::is_bounded() const
+{
+ return isl::basic_set(*this).is_bounded();
+}
+
+boolean point::is_disjoint(const isl::set &set2) const
+{
+ return isl::basic_set(*this).is_disjoint(set2);
+}
+
+boolean point::is_disjoint(const isl::union_set &uset2) const
+{
+ return isl::basic_set(*this).is_disjoint(uset2);
+}
+
+boolean point::is_empty() const
+{
+ return isl::basic_set(*this).is_empty();
+}
+
+boolean point::is_equal(const isl::basic_set &bset2) const
+{
+ return isl::basic_set(*this).is_equal(bset2);
+}
+
+boolean point::is_equal(const isl::set &set2) const
+{
+ return isl::basic_set(*this).is_equal(set2);
+}
+
+boolean point::is_equal(const isl::union_set &uset2) const
+{
+ return isl::basic_set(*this).is_equal(uset2);
+}
+
+boolean point::is_params() const
+{
+ return isl::basic_set(*this).is_params();
+}
+
+boolean point::is_singleton() const
+{
+ return isl::basic_set(*this).is_singleton();
+}
+
+boolean point::is_strict_subset(const isl::set &set2) const
+{
+ return isl::basic_set(*this).is_strict_subset(set2);
+}
+
+boolean point::is_strict_subset(const isl::union_set &uset2) const
+{
+ return isl::basic_set(*this).is_strict_subset(uset2);
+}
+
+boolean point::is_subset(const isl::basic_set &bset2) const
+{
+ return isl::basic_set(*this).is_subset(bset2);
+}
+
+boolean point::is_subset(const isl::set &set2) const
+{
+ return isl::basic_set(*this).is_subset(set2);
+}
+
+boolean point::is_subset(const isl::union_set &uset2) const
+{
+ return isl::basic_set(*this).is_subset(uset2);
+}
+
+boolean point::is_wrapping() const
+{
+ return isl::basic_set(*this).is_wrapping();
+}
+
+boolean point::isa_set() const
+{
+ return isl::basic_set(*this).isa_set();
+}
+
+isl::set point::lexmax() const
+{
+ return isl::basic_set(*this).lexmax();
+}
+
+isl::pw_multi_aff point::lexmax_pw_multi_aff() const
+{
+ return isl::basic_set(*this).lexmax_pw_multi_aff();
+}
+
+isl::set point::lexmin() const
+{
+ return isl::basic_set(*this).lexmin();
+}
+
+isl::pw_multi_aff point::lexmin_pw_multi_aff() const
+{
+ return isl::basic_set(*this).lexmin_pw_multi_aff();
+}
+
+isl::set point::lower_bound(const isl::multi_pw_aff &lower) const
+{
+ return isl::basic_set(*this).lower_bound(lower);
+}
+
+isl::set point::lower_bound(const isl::multi_val &lower) const
+{
+ return isl::basic_set(*this).lower_bound(lower);
+}
+
+isl::set point::lower_bound_si(isl::dim type, unsigned int pos, int value) const
+{
+ return isl::basic_set(*this).lower_bound_si(type, pos, value);
+}
+
+isl::set point::lower_bound_val(isl::dim type, unsigned int pos, const isl::val &value) const
+{
+ return isl::basic_set(*this).lower_bound_val(type, pos, value);
+}
+
+isl::set point::lower_bound_val(isl::dim type, unsigned int pos, long value) const
+{
+ return this->lower_bound_val(type, pos, isl::val(ctx(), value));
+}
+
+isl::multi_pw_aff point::max_multi_pw_aff() const
+{
+ return isl::basic_set(*this).max_multi_pw_aff();
+}
+
+isl::val point::max_val(const isl::aff &obj) const
+{
+ return isl::basic_set(*this).max_val(obj);
+}
+
+isl::multi_pw_aff point::min_multi_pw_aff() const
+{
+ return isl::basic_set(*this).min_multi_pw_aff();
+}
+
+isl::val point::min_val(const isl::aff &obj) const
+{
+ return isl::basic_set(*this).min_val(obj);
+}
+
+isl::multi_val point::multi_val() const
+{
+ auto res = isl_point_get_multi_val(get());
+ return manage(res);
+}
+
+isl::multi_val point::get_multi_val() const
+{
+ return multi_val();
+}
+
+class size point::n_basic_set() const
+{
+ return isl::basic_set(*this).n_basic_set();
+}
+
+isl::basic_set point::params() const
+{
+ return isl::basic_set(*this).params();
+}
+
+isl::val point::plain_get_val_if_fixed(isl::dim type, unsigned int pos) const
+{
+ return isl::basic_set(*this).plain_get_val_if_fixed(type, pos);
+}
+
+isl::multi_val point::plain_multi_val_if_fixed() const
+{
+ return isl::basic_set(*this).plain_multi_val_if_fixed();
+}
+
+isl::basic_set point::polyhedral_hull() const
+{
+ return isl::basic_set(*this).polyhedral_hull();
+}
+
+isl::set point::preimage(const isl::multi_aff &ma) const
+{
+ return isl::basic_set(*this).preimage(ma);
+}
+
+isl::set point::preimage(const isl::multi_pw_aff &mpa) const
+{
+ return isl::basic_set(*this).preimage(mpa);
+}
+
+isl::set point::preimage(const isl::pw_multi_aff &pma) const
+{
+ return isl::basic_set(*this).preimage(pma);
+}
+
+isl::union_set point::preimage(const isl::union_pw_multi_aff &upma) const
+{
+ return isl::basic_set(*this).preimage(upma);
+}
+
+isl::set point::product(const isl::set &set2) const
+{
+ return isl::basic_set(*this).product(set2);
+}
+
+isl::basic_set point::project_out(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::basic_set(*this).project_out(type, first, n);
+}
+
+isl::set point::project_out_all_params() const
+{
+ return isl::basic_set(*this).project_out_all_params();
+}
+
+isl::set point::project_out_param(const isl::id &id) const
+{
+ return isl::basic_set(*this).project_out_param(id);
+}
+
+isl::set point::project_out_param(const std::string &id) const
+{
+ return this->project_out_param(isl::id(ctx(), id));
+}
+
+isl::set point::project_out_param(const isl::id_list &list) const
+{
+ return isl::basic_set(*this).project_out_param(list);
+}
+
+isl::pw_multi_aff point::pw_multi_aff_on_domain(const isl::multi_val &mv) const
+{
+ return isl::basic_set(*this).pw_multi_aff_on_domain(mv);
+}
+
+isl::set point::remove_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::basic_set(*this).remove_dims(type, first, n);
+}
+
+isl::set point::remove_divs() const
+{
+ return isl::basic_set(*this).remove_divs();
+}
+
+isl::set point::remove_redundancies() const
+{
+ return isl::basic_set(*this).remove_redundancies();
+}
+
+isl::set point::reset_tuple_id() const
+{
+ return isl::basic_set(*this).reset_tuple_id();
+}
+
+isl::basic_set point::sample() const
+{
+ return isl::basic_set(*this).sample();
+}
+
+isl::point point::sample_point() const
+{
+ return isl::basic_set(*this).sample_point();
+}
+
+isl::set point::set_dim_id(isl::dim type, unsigned int pos, const isl::id &id) const
+{
+ return isl::basic_set(*this).set_dim_id(type, pos, id);
+}
+
+isl::set point::set_dim_id(isl::dim type, unsigned int pos, const std::string &id) const
+{
+ return this->set_dim_id(type, pos, isl::id(ctx(), id));
+}
+
+isl::set_list point::set_list() const
+{
+ return isl::basic_set(*this).set_list();
+}
+
+isl::set point::set_tuple_id(const isl::id &id) const
+{
+ return isl::basic_set(*this).set_tuple_id(id);
+}
+
+isl::set point::set_tuple_id(const std::string &id) const
+{
+ return this->set_tuple_id(isl::id(ctx(), id));
+}
+
+isl::fixed_box point::simple_fixed_box_hull() const
+{
+ return isl::basic_set(*this).simple_fixed_box_hull();
+}
+
+isl::basic_set point::simple_hull() const
+{
+ return isl::basic_set(*this).simple_hull();
+}
+
+isl::space point::space() const
+{
+ return isl::basic_set(*this).space();
+}
+
+isl::val point::stride(int pos) const
+{
+ return isl::basic_set(*this).stride(pos);
+}
+
+isl::set point::subtract(const isl::set &set2) const
+{
+ return isl::basic_set(*this).subtract(set2);
+}
+
+isl::union_set point::subtract(const isl::union_set &uset2) const
+{
+ return isl::basic_set(*this).subtract(uset2);
+}
+
+isl::basic_set_list point::to_list() const
+{
+ return isl::basic_set(*this).to_list();
+}
+
+isl::set point::to_set() const
+{
+ auto res = isl_point_to_set(copy());
+ return manage(res);
+}
+
+isl::union_set point::to_union_set() const
+{
+ return isl::basic_set(*this).to_union_set();
+}
+
+isl::map point::translation() const
+{
+ return isl::basic_set(*this).translation();
+}
+
+class size point::tuple_dim() const
+{
+ return isl::basic_set(*this).tuple_dim();
+}
+
+isl::id point::tuple_id() const
+{
+ return isl::basic_set(*this).tuple_id();
+}
+
+std::string point::tuple_name() const
+{
+ return isl::basic_set(*this).tuple_name();
+}
+
+isl::set point::unbind_params(const isl::multi_id &tuple) const
+{
+ return isl::basic_set(*this).unbind_params(tuple);
+}
+
+isl::map point::unbind_params_insert_domain(const isl::multi_id &domain) const
+{
+ return isl::basic_set(*this).unbind_params_insert_domain(domain);
+}
+
+isl::set point::unite(const isl::basic_set &bset2) const
+{
+ return isl::basic_set(*this).unite(bset2);
+}
+
+isl::set point::unite(const isl::set &set2) const
+{
+ return isl::basic_set(*this).unite(set2);
+}
+
+isl::union_set point::unite(const isl::union_set &uset2) const
+{
+ return isl::basic_set(*this).unite(uset2);
+}
+
+isl::basic_set point::unshifted_simple_hull() const
+{
+ return isl::basic_set(*this).unshifted_simple_hull();
+}
+
+isl::map point::unwrap() const
+{
+ return isl::basic_set(*this).unwrap();
+}
+
+isl::set point::upper_bound(const isl::multi_pw_aff &upper) const
+{
+ return isl::basic_set(*this).upper_bound(upper);
+}
+
+isl::set point::upper_bound(const isl::multi_val &upper) const
+{
+ return isl::basic_set(*this).upper_bound(upper);
+}
+
+isl::set point::upper_bound_val(isl::dim type, unsigned int pos, const isl::val &value) const
+{
+ return isl::basic_set(*this).upper_bound_val(type, pos, value);
+}
+
+isl::set point::upper_bound_val(isl::dim type, unsigned int pos, long value) const
+{
+ return this->upper_bound_val(type, pos, isl::val(ctx(), value));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const point &obj)
+{
+ char *str = isl_point_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::pw_aff
+pw_aff manage(__isl_take isl_pw_aff *ptr) {
+ return pw_aff(ptr);
+}
+pw_aff manage_copy(__isl_keep isl_pw_aff *ptr) {
+ ptr = isl_pw_aff_copy(ptr);
+ return pw_aff(ptr);
+}
+
+pw_aff::pw_aff()
+ : ptr(nullptr) {}
+
+pw_aff::pw_aff(const pw_aff &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+pw_aff::pw_aff(__isl_take isl_pw_aff *ptr)
+ : ptr(ptr) {}
+
+pw_aff::pw_aff(isl::aff aff)
+{
+ auto res = isl_pw_aff_from_aff(aff.release());
+ ptr = res;
+}
+
+pw_aff::pw_aff(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_pw_aff_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+pw_aff::pw_aff(isl::set domain, isl::val v)
+{
+ auto res = isl_pw_aff_val_on_domain(domain.release(), v.release());
+ ptr = res;
+}
+
+pw_aff::pw_aff(isl::local_space ls)
+{
+ auto res = isl_pw_aff_zero_on_domain(ls.release());
+ ptr = res;
+}
+
+pw_aff &pw_aff::operator=(pw_aff obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+pw_aff::~pw_aff() {
+ if (ptr)
+ isl_pw_aff_free(ptr);
+}
+
+__isl_give isl_pw_aff *pw_aff::copy() const & {
+ return isl_pw_aff_copy(ptr);
+}
+
+__isl_keep isl_pw_aff *pw_aff::get() const {
+ return ptr;
+}
+
+__isl_give isl_pw_aff *pw_aff::release() {
+ isl_pw_aff *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool pw_aff::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx pw_aff::ctx() const {
+ return isl::ctx(isl_pw_aff_get_ctx(ptr));
+}
+
+isl::multi_pw_aff pw_aff::add(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).add(multi2);
+}
+
+isl::multi_union_pw_aff pw_aff::add(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::union_pw_aff(*this).add(multi2);
+}
+
+isl::pw_aff pw_aff::add(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_add(copy(), pwaff2.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_aff::add(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).add(pma2);
+}
+
+isl::union_pw_aff pw_aff::add(const isl::union_pw_aff &upa2) const
+{
+ return isl::union_pw_aff(*this).add(upa2);
+}
+
+isl::union_pw_multi_aff pw_aff::add(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_aff(*this).add(upma2);
+}
+
+isl::pw_aff pw_aff::add(const isl::aff &pwaff2) const
+{
+ return this->add(isl::pw_aff(pwaff2));
+}
+
+isl::pw_aff pw_aff::add_constant(isl::val v) const
+{
+ auto res = isl_pw_aff_add_constant_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::add_constant(long v) const
+{
+ return this->add_constant(isl::val(ctx(), v));
+}
+
+isl::pw_multi_aff pw_aff::add_constant(const isl::multi_val &mv) const
+{
+ return isl::pw_multi_aff(*this).add_constant(mv);
+}
+
+isl::pw_aff pw_aff::add_dims(isl::dim type, unsigned int n) const
+{
+ auto res = isl_pw_aff_add_dims(copy(), static_cast<enum isl_dim_type>(type), n);
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_aff::add_pw_multi_aff(const isl::pw_multi_aff &pma) const
+{
+ return isl::union_pw_aff(*this).add_pw_multi_aff(pma);
+}
+
+isl::union_pw_multi_aff pw_aff::apply(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_aff(*this).apply(upma2);
+}
+
+isl::aff pw_aff::as_aff() const
+{
+ auto res = isl_pw_aff_as_aff(copy());
+ return manage(res);
+}
+
+isl::map pw_aff::as_map() const
+{
+ auto res = isl_pw_aff_as_map(copy());
+ return manage(res);
+}
+
+isl::multi_aff pw_aff::as_multi_aff() const
+{
+ return isl::pw_multi_aff(*this).as_multi_aff();
+}
+
+isl::multi_union_pw_aff pw_aff::as_multi_union_pw_aff() const
+{
+ return isl::union_pw_aff(*this).as_multi_union_pw_aff();
+}
+
+isl::pw_multi_aff pw_aff::as_pw_multi_aff() const
+{
+ return isl::union_pw_aff(*this).as_pw_multi_aff();
+}
+
+isl::set pw_aff::as_set() const
+{
+ return isl::pw_multi_aff(*this).as_set();
+}
+
+isl::union_map pw_aff::as_union_map() const
+{
+ return isl::union_pw_aff(*this).as_union_map();
+}
+
+isl::pw_aff pw_aff::at(int pos) const
+{
+ return isl::pw_multi_aff(*this).at(pos);
+}
+
+isl::set pw_aff::bind(const isl::multi_id &tuple) const
+{
+ return isl::multi_pw_aff(*this).bind(tuple);
+}
+
+isl::set pw_aff::bind(isl::id id) const
+{
+ auto res = isl_pw_aff_bind_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::set pw_aff::bind(const std::string &id) const
+{
+ return this->bind(isl::id(ctx(), id));
+}
+
+isl::pw_aff pw_aff::bind_domain(isl::multi_id tuple) const
+{
+ auto res = isl_pw_aff_bind_domain(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::bind_domain_wrapped_domain(isl::multi_id tuple) const
+{
+ auto res = isl_pw_aff_bind_domain_wrapped_domain(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::ceil() const
+{
+ auto res = isl_pw_aff_ceil(copy());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::coalesce() const
+{
+ auto res = isl_pw_aff_coalesce(copy());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::cond(isl::pw_aff pwaff_true, isl::pw_aff pwaff_false) const
+{
+ auto res = isl_pw_aff_cond(copy(), pwaff_true.release(), pwaff_false.release());
+ return manage(res);
+}
+
+class size pw_aff::dim(isl::dim type) const
+{
+ return isl::pw_multi_aff(*this).dim(type);
+}
+
+isl::id pw_aff::dim_id(isl::dim type, unsigned int pos) const
+{
+ auto res = isl_pw_aff_get_dim_id(get(), static_cast<enum isl_dim_type>(type), pos);
+ return manage(res);
+}
+
+isl::id pw_aff::get_dim_id(isl::dim type, unsigned int pos) const
+{
+ return dim_id(type, pos);
+}
+
+isl::pw_aff pw_aff::div(isl::pw_aff pa2) const
+{
+ auto res = isl_pw_aff_div(copy(), pa2.release());
+ return manage(res);
+}
+
+isl::set pw_aff::domain() const
+{
+ auto res = isl_pw_aff_domain(copy());
+ return manage(res);
+}
+
+isl::space pw_aff::domain_space() const
+{
+ auto res = isl_pw_aff_get_domain_space(get());
+ return manage(res);
+}
+
+isl::space pw_aff::get_domain_space() const
+{
+ return domain_space();
+}
+
+isl::pw_multi_aff pw_aff::drop_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ return isl::pw_multi_aff(*this).drop_dims(type, first, n);
+}
+
+isl::set pw_aff::eq_set(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_eq_set(copy(), pwaff2.release());
+ return manage(res);
+}
+
+isl::val pw_aff::eval(isl::point pnt) const
+{
+ auto res = isl_pw_aff_eval(copy(), pnt.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_aff::extract_pw_multi_aff(const isl::space &space) const
+{
+ return isl::union_pw_aff(*this).extract_pw_multi_aff(space);
+}
+
+isl::multi_pw_aff pw_aff::flat_range_product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).flat_range_product(multi2);
+}
+
+isl::multi_union_pw_aff pw_aff::flat_range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::union_pw_aff(*this).flat_range_product(multi2);
+}
+
+isl::pw_multi_aff pw_aff::flat_range_product(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).flat_range_product(pma2);
+}
+
+isl::union_pw_multi_aff pw_aff::flat_range_product(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_aff(*this).flat_range_product(upma2);
+}
+
+isl::pw_aff pw_aff::floor() const
+{
+ auto res = isl_pw_aff_floor(copy());
+ return manage(res);
+}
+
+stat pw_aff::foreach_piece(const std::function<stat(isl::set, isl::aff)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::set, isl::aff)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_set *arg_0, isl_aff *arg_1, void *arg_2) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_2);
+ auto ret = (data->func)(manage(arg_0), manage(arg_1));
+ return ret.release();
+ };
+ auto res = isl_pw_aff_foreach_piece(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+stat pw_aff::foreach_piece(const std::function<stat(isl::set, isl::multi_aff)> &fn) const
+{
+ return isl::pw_multi_aff(*this).foreach_piece(fn);
+}
+
+stat pw_aff::foreach_pw_aff(const std::function<stat(isl::pw_aff)> &fn) const
+{
+ return isl::union_pw_aff(*this).foreach_pw_aff(fn);
+}
+
+isl::set pw_aff::ge_set(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_ge_set(copy(), pwaff2.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::gist(isl::set context) const
+{
+ auto res = isl_pw_aff_gist(copy(), context.release());
+ return manage(res);
+}
+
+isl::union_pw_aff pw_aff::gist(const isl::union_set &context) const
+{
+ return isl::union_pw_aff(*this).gist(context);
+}
+
+isl::pw_aff pw_aff::gist(const isl::basic_set &context) const
+{
+ return this->gist(isl::set(context));
+}
+
+isl::pw_aff pw_aff::gist(const isl::point &context) const
+{
+ return this->gist(isl::set(context));
+}
+
+isl::set pw_aff::gt_set(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_gt_set(copy(), pwaff2.release());
+ return manage(res);
+}
+
+boolean pw_aff::has_range_tuple_id() const
+{
+ return isl::pw_multi_aff(*this).has_range_tuple_id();
+}
+
+isl::multi_pw_aff pw_aff::identity() const
+{
+ return isl::pw_multi_aff(*this).identity();
+}
+
+isl::pw_aff pw_aff::insert_domain(isl::space domain) const
+{
+ auto res = isl_pw_aff_insert_domain(copy(), domain.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::intersect_domain(isl::set set) const
+{
+ auto res = isl_pw_aff_intersect_domain(copy(), set.release());
+ return manage(res);
+}
+
+isl::union_pw_aff pw_aff::intersect_domain(const isl::space &space) const
+{
+ return isl::union_pw_aff(*this).intersect_domain(space);
+}
+
+isl::union_pw_aff pw_aff::intersect_domain(const isl::union_set &uset) const
+{
+ return isl::union_pw_aff(*this).intersect_domain(uset);
+}
+
+isl::pw_aff pw_aff::intersect_domain(const isl::basic_set &set) const
+{
+ return this->intersect_domain(isl::set(set));
+}
+
+isl::pw_aff pw_aff::intersect_domain(const isl::point &set) const
+{
+ return this->intersect_domain(isl::set(set));
+}
+
+isl::union_pw_aff pw_aff::intersect_domain_wrapped_domain(const isl::union_set &uset) const
+{
+ return isl::union_pw_aff(*this).intersect_domain_wrapped_domain(uset);
+}
+
+isl::union_pw_aff pw_aff::intersect_domain_wrapped_range(const isl::union_set &uset) const
+{
+ return isl::union_pw_aff(*this).intersect_domain_wrapped_range(uset);
+}
+
+isl::pw_aff pw_aff::intersect_params(isl::set set) const
+{
+ auto res = isl_pw_aff_intersect_params(copy(), set.release());
+ return manage(res);
+}
+
+boolean pw_aff::involves_locals() const
+{
+ return isl::pw_multi_aff(*this).involves_locals();
+}
+
+boolean pw_aff::involves_nan() const
+{
+ return isl::multi_pw_aff(*this).involves_nan();
+}
+
+boolean pw_aff::involves_param(const isl::id &id) const
+{
+ return isl::pw_multi_aff(*this).involves_param(id);
+}
+
+boolean pw_aff::involves_param(const std::string &id) const
+{
+ return this->involves_param(isl::id(ctx(), id));
+}
+
+boolean pw_aff::involves_param(const isl::id_list &list) const
+{
+ return isl::pw_multi_aff(*this).involves_param(list);
+}
+
+boolean pw_aff::is_cst() const
+{
+ auto res = isl_pw_aff_is_cst(get());
+ return manage(res);
+}
+
+boolean pw_aff::is_equal(const isl::pw_aff &pa2) const
+{
+ auto res = isl_pw_aff_is_equal(get(), pa2.get());
+ return manage(res);
+}
+
+boolean pw_aff::isa_aff() const
+{
+ auto res = isl_pw_aff_isa_aff(get());
+ return manage(res);
+}
+
+boolean pw_aff::isa_multi_aff() const
+{
+ return isl::pw_multi_aff(*this).isa_multi_aff();
+}
+
+boolean pw_aff::isa_pw_multi_aff() const
+{
+ return isl::union_pw_aff(*this).isa_pw_multi_aff();
+}
+
+isl::set pw_aff::le_set(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_le_set(copy(), pwaff2.release());
+ return manage(res);
+}
+
+isl::pw_aff_list pw_aff::list() const
+{
+ return isl::multi_pw_aff(*this).list();
+}
+
+isl::set pw_aff::lt_set(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_lt_set(copy(), pwaff2.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff pw_aff::max(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).max(multi2);
+}
+
+isl::pw_aff pw_aff::max(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_max(copy(), pwaff2.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::max(const isl::aff &pwaff2) const
+{
+ return this->max(isl::pw_aff(pwaff2));
+}
+
+isl::multi_val pw_aff::max_multi_val() const
+{
+ return isl::pw_multi_aff(*this).max_multi_val();
+}
+
+isl::multi_pw_aff pw_aff::min(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).min(multi2);
+}
+
+isl::pw_aff pw_aff::min(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_min(copy(), pwaff2.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::min(const isl::aff &pwaff2) const
+{
+ return this->min(isl::pw_aff(pwaff2));
+}
+
+isl::multi_val pw_aff::min_multi_val() const
+{
+ return isl::pw_multi_aff(*this).min_multi_val();
+}
+
+isl::pw_aff pw_aff::mod(isl::val mod) const
+{
+ auto res = isl_pw_aff_mod_val(copy(), mod.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::mod(long mod) const
+{
+ return this->mod(isl::val(ctx(), mod));
+}
+
+isl::pw_aff pw_aff::mul(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_mul(copy(), pwaff2.release());
+ return manage(res);
+}
+
+class size pw_aff::n_piece() const
+{
+ return isl::pw_multi_aff(*this).n_piece();
+}
+
+isl::set pw_aff::ne_set(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_ne_set(copy(), pwaff2.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::neg() const
+{
+ auto res = isl_pw_aff_neg(copy());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::param_on_domain(isl::set domain, isl::id id)
+{
+ auto res = isl_pw_aff_param_on_domain_id(domain.release(), id.release());
+ return manage(res);
+}
+
+boolean pw_aff::plain_is_empty() const
+{
+ return isl::union_pw_aff(*this).plain_is_empty();
+}
+
+boolean pw_aff::plain_is_equal(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).plain_is_equal(multi2);
+}
+
+boolean pw_aff::plain_is_equal(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::union_pw_aff(*this).plain_is_equal(multi2);
+}
+
+isl::pw_multi_aff pw_aff::preimage_domain_wrapped_domain(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).preimage_domain_wrapped_domain(pma2);
+}
+
+isl::union_pw_multi_aff pw_aff::preimage_domain_wrapped_domain(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_aff(*this).preimage_domain_wrapped_domain(upma2);
+}
+
+isl::multi_pw_aff pw_aff::product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).product(multi2);
+}
+
+isl::pw_multi_aff pw_aff::product(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).product(pma2);
+}
+
+isl::pw_aff pw_aff::pullback(isl::multi_aff ma) const
+{
+ auto res = isl_pw_aff_pullback_multi_aff(copy(), ma.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::pullback(isl::multi_pw_aff mpa) const
+{
+ auto res = isl_pw_aff_pullback_multi_pw_aff(copy(), mpa.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::pullback(isl::pw_multi_aff pma) const
+{
+ auto res = isl_pw_aff_pullback_pw_multi_aff(copy(), pma.release());
+ return manage(res);
+}
+
+isl::union_pw_aff pw_aff::pullback(const isl::union_pw_multi_aff &upma) const
+{
+ return isl::union_pw_aff(*this).pullback(upma);
+}
+
+isl::pw_multi_aff_list pw_aff::pw_multi_aff_list() const
+{
+ return isl::union_pw_aff(*this).pw_multi_aff_list();
+}
+
+isl::pw_multi_aff pw_aff::range_factor_domain() const
+{
+ return isl::pw_multi_aff(*this).range_factor_domain();
+}
+
+isl::pw_multi_aff pw_aff::range_factor_range() const
+{
+ return isl::pw_multi_aff(*this).range_factor_range();
+}
+
+isl::multi_pw_aff pw_aff::range_product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).range_product(multi2);
+}
+
+isl::multi_union_pw_aff pw_aff::range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::union_pw_aff(*this).range_product(multi2);
+}
+
+isl::pw_multi_aff pw_aff::range_product(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).range_product(pma2);
+}
+
+isl::union_pw_multi_aff pw_aff::range_product(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_aff(*this).range_product(upma2);
+}
+
+isl::id pw_aff::range_tuple_id() const
+{
+ return isl::pw_multi_aff(*this).range_tuple_id();
+}
+
+isl::multi_pw_aff pw_aff::reset_range_tuple_id() const
+{
+ return isl::multi_pw_aff(*this).reset_range_tuple_id();
+}
+
+isl::multi_pw_aff pw_aff::reset_tuple_id(isl::dim type) const
+{
+ return isl::multi_pw_aff(*this).reset_tuple_id(type);
+}
+
+isl::multi_pw_aff pw_aff::scale(const isl::multi_val &mv) const
+{
+ return isl::multi_pw_aff(*this).scale(mv);
+}
+
+isl::pw_aff pw_aff::scale(isl::val v) const
+{
+ auto res = isl_pw_aff_scale_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::scale(long v) const
+{
+ return this->scale(isl::val(ctx(), v));
+}
+
+isl::multi_pw_aff pw_aff::scale_down(const isl::multi_val &mv) const
+{
+ return isl::multi_pw_aff(*this).scale_down(mv);
+}
+
+isl::pw_aff pw_aff::scale_down(isl::val f) const
+{
+ auto res = isl_pw_aff_scale_down_val(copy(), f.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::scale_down(long f) const
+{
+ return this->scale_down(isl::val(ctx(), f));
+}
+
+isl::multi_pw_aff pw_aff::set_at(int pos, const isl::pw_aff &el) const
+{
+ return isl::pw_multi_aff(*this).set_at(pos, el);
+}
+
+isl::multi_union_pw_aff pw_aff::set_at(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::union_pw_aff(*this).set_at(pos, el);
+}
+
+isl::multi_pw_aff pw_aff::set_pw_aff(int pos, const isl::pw_aff &el) const
+{
+ return isl::pw_multi_aff(*this).set_pw_aff(pos, el);
+}
+
+isl::pw_multi_aff pw_aff::set_pw_aff(unsigned int pos, const isl::pw_aff &pa) const
+{
+ return isl::pw_multi_aff(*this).set_pw_aff(pos, pa);
+}
+
+isl::pw_multi_aff pw_aff::set_range_tuple(const isl::id &id) const
+{
+ return isl::pw_multi_aff(*this).set_range_tuple(id);
+}
+
+isl::pw_multi_aff pw_aff::set_range_tuple(const std::string &id) const
+{
+ return this->set_range_tuple(isl::id(ctx(), id));
+}
+
+isl::pw_aff pw_aff::set_tuple_id(isl::dim type, isl::id id) const
+{
+ auto res = isl_pw_aff_set_tuple_id(copy(), static_cast<enum isl_dim_type>(type), id.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::set_tuple_id(isl::dim type, const std::string &id) const
+{
+ return this->set_tuple_id(type, isl::id(ctx(), id));
+}
+
+isl::multi_union_pw_aff pw_aff::set_union_pw_aff(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::union_pw_aff(*this).set_union_pw_aff(pos, el);
+}
+
+class size pw_aff::size() const
+{
+ return isl::multi_pw_aff(*this).size();
+}
+
+isl::space pw_aff::space() const
+{
+ auto res = isl_pw_aff_get_space(get());
+ return manage(res);
+}
+
+isl::space pw_aff::get_space() const
+{
+ return space();
+}
+
+isl::multi_pw_aff pw_aff::sub(const isl::multi_pw_aff &multi2) const
+{
+ return isl::pw_multi_aff(*this).sub(multi2);
+}
+
+isl::multi_union_pw_aff pw_aff::sub(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::union_pw_aff(*this).sub(multi2);
+}
+
+isl::pw_aff pw_aff::sub(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_sub(copy(), pwaff2.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_aff::sub(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).sub(pma2);
+}
+
+isl::union_pw_aff pw_aff::sub(const isl::union_pw_aff &upa2) const
+{
+ return isl::union_pw_aff(*this).sub(upa2);
+}
+
+isl::union_pw_multi_aff pw_aff::sub(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_aff(*this).sub(upma2);
+}
+
+isl::pw_aff pw_aff::sub(const isl::aff &pwaff2) const
+{
+ return this->sub(isl::pw_aff(pwaff2));
+}
+
+isl::pw_aff pw_aff::subtract_domain(isl::set set) const
+{
+ auto res = isl_pw_aff_subtract_domain(copy(), set.release());
+ return manage(res);
+}
+
+isl::union_pw_aff pw_aff::subtract_domain(const isl::space &space) const
+{
+ return isl::union_pw_aff(*this).subtract_domain(space);
+}
+
+isl::union_pw_aff pw_aff::subtract_domain(const isl::union_set &uset) const
+{
+ return isl::union_pw_aff(*this).subtract_domain(uset);
+}
+
+isl::pw_aff pw_aff::subtract_domain(const isl::basic_set &set) const
+{
+ return this->subtract_domain(isl::set(set));
+}
+
+isl::pw_aff pw_aff::subtract_domain(const isl::point &set) const
+{
+ return this->subtract_domain(isl::set(set));
+}
+
+isl::pw_aff pw_aff::tdiv_q(isl::pw_aff pa2) const
+{
+ auto res = isl_pw_aff_tdiv_q(copy(), pa2.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff::tdiv_r(isl::pw_aff pa2) const
+{
+ auto res = isl_pw_aff_tdiv_r(copy(), pa2.release());
+ return manage(res);
+}
+
+isl::pw_aff_list pw_aff::to_list() const
+{
+ auto res = isl_pw_aff_to_list(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff pw_aff::to_multi_pw_aff() const
+{
+ return isl::pw_multi_aff(*this).to_multi_pw_aff();
+}
+
+isl::union_pw_aff pw_aff::to_union_pw_aff() const
+{
+ auto res = isl_pw_aff_to_union_pw_aff(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_aff::to_union_pw_multi_aff() const
+{
+ return isl::pw_multi_aff(*this).to_union_pw_multi_aff();
+}
+
+isl::id pw_aff::tuple_id(isl::dim type) const
+{
+ auto res = isl_pw_aff_get_tuple_id(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::id pw_aff::get_tuple_id(isl::dim type) const
+{
+ return tuple_id(type);
+}
+
+isl::multi_pw_aff pw_aff::unbind_params_insert_domain(const isl::multi_id &domain) const
+{
+ return isl::pw_multi_aff(*this).unbind_params_insert_domain(domain);
+}
+
+isl::multi_pw_aff pw_aff::union_add(const isl::multi_pw_aff &mpa2) const
+{
+ return isl::pw_multi_aff(*this).union_add(mpa2);
+}
+
+isl::multi_union_pw_aff pw_aff::union_add(const isl::multi_union_pw_aff &mupa2) const
+{
+ return isl::union_pw_aff(*this).union_add(mupa2);
+}
+
+isl::pw_aff pw_aff::union_add(isl::pw_aff pwaff2) const
+{
+ auto res = isl_pw_aff_union_add(copy(), pwaff2.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_aff::union_add(const isl::pw_multi_aff &pma2) const
+{
+ return isl::pw_multi_aff(*this).union_add(pma2);
+}
+
+isl::union_pw_aff pw_aff::union_add(const isl::union_pw_aff &upa2) const
+{
+ return isl::union_pw_aff(*this).union_add(upa2);
+}
+
+isl::union_pw_multi_aff pw_aff::union_add(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_aff(*this).union_add(upma2);
+}
+
+isl::pw_aff pw_aff::union_add(const isl::aff &pwaff2) const
+{
+ return this->union_add(isl::pw_aff(pwaff2));
+}
+
+isl::pw_aff pw_aff::var_on_domain(isl::local_space ls, isl::dim type, unsigned int pos)
+{
+ auto res = isl_pw_aff_var_on_domain(ls.release(), static_cast<enum isl_dim_type>(type), pos);
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const pw_aff &obj)
+{
+ char *str = isl_pw_aff_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::pw_aff_list
+pw_aff_list manage(__isl_take isl_pw_aff_list *ptr) {
+ return pw_aff_list(ptr);
+}
+pw_aff_list manage_copy(__isl_keep isl_pw_aff_list *ptr) {
+ ptr = isl_pw_aff_list_copy(ptr);
+ return pw_aff_list(ptr);
+}
+
+pw_aff_list::pw_aff_list()
+ : ptr(nullptr) {}
+
+pw_aff_list::pw_aff_list(const pw_aff_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+pw_aff_list::pw_aff_list(__isl_take isl_pw_aff_list *ptr)
+ : ptr(ptr) {}
+
+pw_aff_list::pw_aff_list(isl::ctx ctx, int n)
+{
+ auto res = isl_pw_aff_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+pw_aff_list::pw_aff_list(isl::pw_aff el)
+{
+ auto res = isl_pw_aff_list_from_pw_aff(el.release());
+ ptr = res;
+}
+
+pw_aff_list::pw_aff_list(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_pw_aff_list_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+pw_aff_list &pw_aff_list::operator=(pw_aff_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+pw_aff_list::~pw_aff_list() {
+ if (ptr)
+ isl_pw_aff_list_free(ptr);
+}
+
+__isl_give isl_pw_aff_list *pw_aff_list::copy() const & {
+ return isl_pw_aff_list_copy(ptr);
+}
+
+__isl_keep isl_pw_aff_list *pw_aff_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_pw_aff_list *pw_aff_list::release() {
+ isl_pw_aff_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool pw_aff_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx pw_aff_list::ctx() const {
+ return isl::ctx(isl_pw_aff_list_get_ctx(ptr));
+}
+
+isl::pw_aff_list pw_aff_list::add(isl::pw_aff el) const
+{
+ auto res = isl_pw_aff_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::pw_aff pw_aff_list::at(int index) const
+{
+ auto res = isl_pw_aff_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::pw_aff pw_aff_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::pw_aff_list pw_aff_list::clear() const
+{
+ auto res = isl_pw_aff_list_clear(copy());
+ return manage(res);
+}
+
+isl::pw_aff_list pw_aff_list::concat(isl::pw_aff_list list2) const
+{
+ auto res = isl_pw_aff_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::pw_aff_list pw_aff_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_pw_aff_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat pw_aff_list::foreach(const std::function<stat(isl::pw_aff)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::pw_aff)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_pw_aff *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_pw_aff_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::pw_aff_list pw_aff_list::insert(unsigned int pos, isl::pw_aff el) const
+{
+ auto res = isl_pw_aff_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+class size pw_aff_list::size() const
+{
+ auto res = isl_pw_aff_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const pw_aff_list &obj)
+{
+ char *str = isl_pw_aff_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::pw_multi_aff
+pw_multi_aff manage(__isl_take isl_pw_multi_aff *ptr) {
+ return pw_multi_aff(ptr);
+}
+pw_multi_aff manage_copy(__isl_keep isl_pw_multi_aff *ptr) {
+ ptr = isl_pw_multi_aff_copy(ptr);
+ return pw_multi_aff(ptr);
+}
+
+pw_multi_aff::pw_multi_aff()
+ : ptr(nullptr) {}
+
+pw_multi_aff::pw_multi_aff(const pw_multi_aff &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+pw_multi_aff::pw_multi_aff(__isl_take isl_pw_multi_aff *ptr)
+ : ptr(ptr) {}
+
+pw_multi_aff::pw_multi_aff(isl::multi_aff ma)
+{
+ auto res = isl_pw_multi_aff_from_multi_aff(ma.release());
+ ptr = res;
+}
+
+pw_multi_aff::pw_multi_aff(isl::pw_aff pa)
+{
+ auto res = isl_pw_multi_aff_from_pw_aff(pa.release());
+ ptr = res;
+}
+
+pw_multi_aff::pw_multi_aff(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_pw_multi_aff_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+pw_multi_aff &pw_multi_aff::operator=(pw_multi_aff obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+pw_multi_aff::~pw_multi_aff() {
+ if (ptr)
+ isl_pw_multi_aff_free(ptr);
+}
+
+__isl_give isl_pw_multi_aff *pw_multi_aff::copy() const & {
+ return isl_pw_multi_aff_copy(ptr);
+}
+
+__isl_keep isl_pw_multi_aff *pw_multi_aff::get() const {
+ return ptr;
+}
+
+__isl_give isl_pw_multi_aff *pw_multi_aff::release() {
+ isl_pw_multi_aff *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool pw_multi_aff::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx pw_multi_aff::ctx() const {
+ return isl::ctx(isl_pw_multi_aff_get_ctx(ptr));
+}
+
+isl::multi_pw_aff pw_multi_aff::add(const isl::multi_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).add(multi2);
+}
+
+isl::multi_union_pw_aff pw_multi_aff::add(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).add(multi2);
+}
+
+isl::pw_multi_aff pw_multi_aff::add(isl::pw_multi_aff pma2) const
+{
+ auto res = isl_pw_multi_aff_add(copy(), pma2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::add(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).add(upma2);
+}
+
+isl::pw_multi_aff pw_multi_aff::add(const isl::multi_aff &pma2) const
+{
+ return this->add(isl::pw_multi_aff(pma2));
+}
+
+isl::pw_multi_aff pw_multi_aff::add(const isl::pw_aff &pma2) const
+{
+ return this->add(isl::pw_multi_aff(pma2));
+}
+
+isl::pw_multi_aff pw_multi_aff::add_constant(isl::multi_val mv) const
+{
+ auto res = isl_pw_multi_aff_add_constant_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::add_constant(isl::val v) const
+{
+ auto res = isl_pw_multi_aff_add_constant_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::add_constant(long v) const
+{
+ return this->add_constant(isl::val(ctx(), v));
+}
+
+isl::union_pw_multi_aff pw_multi_aff::add_pw_multi_aff(const isl::pw_multi_aff &pma) const
+{
+ return isl::union_pw_multi_aff(*this).add_pw_multi_aff(pma);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::apply(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).apply(upma2);
+}
+
+isl::map pw_multi_aff::as_map() const
+{
+ auto res = isl_pw_multi_aff_as_map(copy());
+ return manage(res);
+}
+
+isl::multi_aff pw_multi_aff::as_multi_aff() const
+{
+ auto res = isl_pw_multi_aff_as_multi_aff(copy());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff pw_multi_aff::as_multi_union_pw_aff() const
+{
+ return isl::union_pw_multi_aff(*this).as_multi_union_pw_aff();
+}
+
+isl::pw_multi_aff pw_multi_aff::as_pw_multi_aff() const
+{
+ return isl::union_pw_multi_aff(*this).as_pw_multi_aff();
+}
+
+isl::set pw_multi_aff::as_set() const
+{
+ auto res = isl_pw_multi_aff_as_set(copy());
+ return manage(res);
+}
+
+isl::union_map pw_multi_aff::as_union_map() const
+{
+ return isl::union_pw_multi_aff(*this).as_union_map();
+}
+
+isl::pw_aff pw_multi_aff::at(int pos) const
+{
+ auto res = isl_pw_multi_aff_get_at(get(), pos);
+ return manage(res);
+}
+
+isl::pw_aff pw_multi_aff::get_at(int pos) const
+{
+ return at(pos);
+}
+
+isl::set pw_multi_aff::bind(const isl::multi_id &tuple) const
+{
+ return isl::multi_pw_aff(*this).bind(tuple);
+}
+
+isl::pw_multi_aff pw_multi_aff::bind_domain(isl::multi_id tuple) const
+{
+ auto res = isl_pw_multi_aff_bind_domain(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::bind_domain_wrapped_domain(isl::multi_id tuple) const
+{
+ auto res = isl_pw_multi_aff_bind_domain_wrapped_domain(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::coalesce() const
+{
+ auto res = isl_pw_multi_aff_coalesce(copy());
+ return manage(res);
+}
+
+class size pw_multi_aff::dim(isl::dim type) const
+{
+ auto res = isl_pw_multi_aff_dim(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::set pw_multi_aff::domain() const
+{
+ auto res = isl_pw_multi_aff_domain(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::domain_map(isl::space space)
+{
+ auto res = isl_pw_multi_aff_domain_map(space.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::drop_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ auto res = isl_pw_multi_aff_drop_dims(copy(), static_cast<enum isl_dim_type>(type), first, n);
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::extract_pw_multi_aff(const isl::space &space) const
+{
+ return isl::union_pw_multi_aff(*this).extract_pw_multi_aff(space);
+}
+
+isl::multi_pw_aff pw_multi_aff::flat_range_product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).flat_range_product(multi2);
+}
+
+isl::multi_union_pw_aff pw_multi_aff::flat_range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).flat_range_product(multi2);
+}
+
+isl::pw_multi_aff pw_multi_aff::flat_range_product(isl::pw_multi_aff pma2) const
+{
+ auto res = isl_pw_multi_aff_flat_range_product(copy(), pma2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::flat_range_product(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).flat_range_product(upma2);
+}
+
+isl::pw_multi_aff pw_multi_aff::flat_range_product(const isl::multi_aff &pma2) const
+{
+ return this->flat_range_product(isl::pw_multi_aff(pma2));
+}
+
+isl::pw_multi_aff pw_multi_aff::flat_range_product(const isl::pw_aff &pma2) const
+{
+ return this->flat_range_product(isl::pw_multi_aff(pma2));
+}
+
+stat pw_multi_aff::foreach_piece(const std::function<stat(isl::set, isl::multi_aff)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::set, isl::multi_aff)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_set *arg_0, isl_multi_aff *arg_1, void *arg_2) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_2);
+ auto ret = (data->func)(manage(arg_0), manage(arg_1));
+ return ret.release();
+ };
+ auto res = isl_pw_multi_aff_foreach_piece(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::from_map(isl::map map)
+{
+ auto res = isl_pw_multi_aff_from_map(map.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::gist(isl::set set) const
+{
+ auto res = isl_pw_multi_aff_gist(copy(), set.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::gist(const isl::union_set &context) const
+{
+ return isl::union_pw_multi_aff(*this).gist(context);
+}
+
+isl::pw_multi_aff pw_multi_aff::gist(const isl::basic_set &set) const
+{
+ return this->gist(isl::set(set));
+}
+
+isl::pw_multi_aff pw_multi_aff::gist(const isl::point &set) const
+{
+ return this->gist(isl::set(set));
+}
+
+boolean pw_multi_aff::has_range_tuple_id() const
+{
+ auto res = isl_pw_multi_aff_has_range_tuple_id(get());
+ return manage(res);
+}
+
+isl::multi_pw_aff pw_multi_aff::identity() const
+{
+ return isl::multi_pw_aff(*this).identity();
+}
+
+isl::pw_multi_aff pw_multi_aff::identity_on_domain(isl::space space)
+{
+ auto res = isl_pw_multi_aff_identity_on_domain_space(space.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::insert_domain(isl::space domain) const
+{
+ auto res = isl_pw_multi_aff_insert_domain(copy(), domain.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::intersect_domain(isl::set set) const
+{
+ auto res = isl_pw_multi_aff_intersect_domain(copy(), set.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::intersect_domain(const isl::space &space) const
+{
+ return isl::union_pw_multi_aff(*this).intersect_domain(space);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::intersect_domain(const isl::union_set &uset) const
+{
+ return isl::union_pw_multi_aff(*this).intersect_domain(uset);
+}
+
+isl::pw_multi_aff pw_multi_aff::intersect_domain(const isl::basic_set &set) const
+{
+ return this->intersect_domain(isl::set(set));
+}
+
+isl::pw_multi_aff pw_multi_aff::intersect_domain(const isl::point &set) const
+{
+ return this->intersect_domain(isl::set(set));
+}
+
+isl::union_pw_multi_aff pw_multi_aff::intersect_domain_wrapped_domain(const isl::union_set &uset) const
+{
+ return isl::union_pw_multi_aff(*this).intersect_domain_wrapped_domain(uset);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::intersect_domain_wrapped_range(const isl::union_set &uset) const
+{
+ return isl::union_pw_multi_aff(*this).intersect_domain_wrapped_range(uset);
+}
+
+isl::pw_multi_aff pw_multi_aff::intersect_params(isl::set set) const
+{
+ auto res = isl_pw_multi_aff_intersect_params(copy(), set.release());
+ return manage(res);
+}
+
+boolean pw_multi_aff::involves_locals() const
+{
+ auto res = isl_pw_multi_aff_involves_locals(get());
+ return manage(res);
+}
+
+boolean pw_multi_aff::involves_nan() const
+{
+ return isl::multi_pw_aff(*this).involves_nan();
+}
+
+boolean pw_multi_aff::involves_param(const isl::id &id) const
+{
+ return isl::multi_pw_aff(*this).involves_param(id);
+}
+
+boolean pw_multi_aff::involves_param(const std::string &id) const
+{
+ return this->involves_param(isl::id(ctx(), id));
+}
+
+boolean pw_multi_aff::involves_param(const isl::id_list &list) const
+{
+ return isl::multi_pw_aff(*this).involves_param(list);
+}
+
+boolean pw_multi_aff::isa_multi_aff() const
+{
+ auto res = isl_pw_multi_aff_isa_multi_aff(get());
+ return manage(res);
+}
+
+boolean pw_multi_aff::isa_pw_multi_aff() const
+{
+ return isl::union_pw_multi_aff(*this).isa_pw_multi_aff();
+}
+
+isl::pw_aff_list pw_multi_aff::list() const
+{
+ return isl::multi_pw_aff(*this).list();
+}
+
+isl::multi_pw_aff pw_multi_aff::max(const isl::multi_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).max(multi2);
+}
+
+isl::multi_val pw_multi_aff::max_multi_val() const
+{
+ auto res = isl_pw_multi_aff_max_multi_val(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff pw_multi_aff::min(const isl::multi_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).min(multi2);
+}
+
+isl::multi_val pw_multi_aff::min_multi_val() const
+{
+ auto res = isl_pw_multi_aff_min_multi_val(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::multi_val_on_domain(isl::set domain, isl::multi_val mv)
+{
+ auto res = isl_pw_multi_aff_multi_val_on_domain(domain.release(), mv.release());
+ return manage(res);
+}
+
+class size pw_multi_aff::n_piece() const
+{
+ auto res = isl_pw_multi_aff_n_piece(get());
+ return manage(res);
+}
+
+isl::multi_pw_aff pw_multi_aff::neg() const
+{
+ return isl::multi_pw_aff(*this).neg();
+}
+
+boolean pw_multi_aff::plain_is_empty() const
+{
+ return isl::union_pw_multi_aff(*this).plain_is_empty();
+}
+
+boolean pw_multi_aff::plain_is_equal(const isl::multi_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).plain_is_equal(multi2);
+}
+
+boolean pw_multi_aff::plain_is_equal(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).plain_is_equal(multi2);
+}
+
+isl::pw_multi_aff pw_multi_aff::preimage_domain_wrapped_domain(isl::pw_multi_aff pma2) const
+{
+ auto res = isl_pw_multi_aff_preimage_domain_wrapped_domain_pw_multi_aff(copy(), pma2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::preimage_domain_wrapped_domain(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).preimage_domain_wrapped_domain(upma2);
+}
+
+isl::pw_multi_aff pw_multi_aff::preimage_domain_wrapped_domain(const isl::multi_aff &pma2) const
+{
+ return this->preimage_domain_wrapped_domain(isl::pw_multi_aff(pma2));
+}
+
+isl::pw_multi_aff pw_multi_aff::preimage_domain_wrapped_domain(const isl::pw_aff &pma2) const
+{
+ return this->preimage_domain_wrapped_domain(isl::pw_multi_aff(pma2));
+}
+
+isl::multi_pw_aff pw_multi_aff::product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).product(multi2);
+}
+
+isl::pw_multi_aff pw_multi_aff::product(isl::pw_multi_aff pma2) const
+{
+ auto res = isl_pw_multi_aff_product(copy(), pma2.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::product(const isl::multi_aff &pma2) const
+{
+ return this->product(isl::pw_multi_aff(pma2));
+}
+
+isl::pw_multi_aff pw_multi_aff::product(const isl::pw_aff &pma2) const
+{
+ return this->product(isl::pw_multi_aff(pma2));
+}
+
+isl::pw_multi_aff pw_multi_aff::project_out_map(isl::space space, isl::dim type, unsigned int first, unsigned int n)
+{
+ auto res = isl_pw_multi_aff_project_out_map(space.release(), static_cast<enum isl_dim_type>(type), first, n);
+ return manage(res);
+}
+
+isl::multi_pw_aff pw_multi_aff::pullback(const isl::multi_pw_aff &mpa2) const
+{
+ return isl::multi_pw_aff(*this).pullback(mpa2);
+}
+
+isl::pw_multi_aff pw_multi_aff::pullback(isl::multi_aff ma) const
+{
+ auto res = isl_pw_multi_aff_pullback_multi_aff(copy(), ma.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::pullback(isl::pw_multi_aff pma2) const
+{
+ auto res = isl_pw_multi_aff_pullback_pw_multi_aff(copy(), pma2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::pullback(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).pullback(upma2);
+}
+
+isl::pw_multi_aff_list pw_multi_aff::pw_multi_aff_list() const
+{
+ return isl::union_pw_multi_aff(*this).pw_multi_aff_list();
+}
+
+isl::pw_multi_aff pw_multi_aff::range_factor_domain() const
+{
+ auto res = isl_pw_multi_aff_range_factor_domain(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::range_factor_range() const
+{
+ auto res = isl_pw_multi_aff_range_factor_range(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::range_map(isl::space space)
+{
+ auto res = isl_pw_multi_aff_range_map(space.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff pw_multi_aff::range_product(const isl::multi_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).range_product(multi2);
+}
+
+isl::multi_union_pw_aff pw_multi_aff::range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).range_product(multi2);
+}
+
+isl::pw_multi_aff pw_multi_aff::range_product(isl::pw_multi_aff pma2) const
+{
+ auto res = isl_pw_multi_aff_range_product(copy(), pma2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::range_product(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).range_product(upma2);
+}
+
+isl::pw_multi_aff pw_multi_aff::range_product(const isl::multi_aff &pma2) const
+{
+ return this->range_product(isl::pw_multi_aff(pma2));
+}
+
+isl::pw_multi_aff pw_multi_aff::range_product(const isl::pw_aff &pma2) const
+{
+ return this->range_product(isl::pw_multi_aff(pma2));
+}
+
+isl::id pw_multi_aff::range_tuple_id() const
+{
+ auto res = isl_pw_multi_aff_get_range_tuple_id(get());
+ return manage(res);
+}
+
+isl::id pw_multi_aff::get_range_tuple_id() const
+{
+ return range_tuple_id();
+}
+
+isl::multi_pw_aff pw_multi_aff::reset_range_tuple_id() const
+{
+ return isl::multi_pw_aff(*this).reset_range_tuple_id();
+}
+
+isl::multi_pw_aff pw_multi_aff::reset_tuple_id(isl::dim type) const
+{
+ return isl::multi_pw_aff(*this).reset_tuple_id(type);
+}
+
+isl::multi_pw_aff pw_multi_aff::scale(const isl::multi_val &mv) const
+{
+ return isl::multi_pw_aff(*this).scale(mv);
+}
+
+isl::pw_multi_aff pw_multi_aff::scale(isl::val v) const
+{
+ auto res = isl_pw_multi_aff_scale_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::scale(long v) const
+{
+ return this->scale(isl::val(ctx(), v));
+}
+
+isl::multi_pw_aff pw_multi_aff::scale_down(const isl::multi_val &mv) const
+{
+ return isl::multi_pw_aff(*this).scale_down(mv);
+}
+
+isl::pw_multi_aff pw_multi_aff::scale_down(isl::val v) const
+{
+ auto res = isl_pw_multi_aff_scale_down_val(copy(), v.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::scale_down(long v) const
+{
+ return this->scale_down(isl::val(ctx(), v));
+}
+
+isl::multi_pw_aff pw_multi_aff::set_at(int pos, const isl::pw_aff &el) const
+{
+ return isl::multi_pw_aff(*this).set_at(pos, el);
+}
+
+isl::multi_union_pw_aff pw_multi_aff::set_at(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::multi_pw_aff(*this).set_at(pos, el);
+}
+
+isl::multi_pw_aff pw_multi_aff::set_pw_aff(int pos, const isl::pw_aff &el) const
+{
+ return isl::multi_pw_aff(*this).set_pw_aff(pos, el);
+}
+
+isl::pw_multi_aff pw_multi_aff::set_pw_aff(unsigned int pos, isl::pw_aff pa) const
+{
+ auto res = isl_pw_multi_aff_set_pw_aff(copy(), pos, pa.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::set_range_tuple(isl::id id) const
+{
+ auto res = isl_pw_multi_aff_set_range_tuple_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff::set_range_tuple(const std::string &id) const
+{
+ return this->set_range_tuple(isl::id(ctx(), id));
+}
+
+isl::multi_union_pw_aff pw_multi_aff::set_union_pw_aff(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::multi_pw_aff(*this).set_union_pw_aff(pos, el);
+}
+
+class size pw_multi_aff::size() const
+{
+ return isl::multi_pw_aff(*this).size();
+}
+
+isl::space pw_multi_aff::space() const
+{
+ auto res = isl_pw_multi_aff_get_space(get());
+ return manage(res);
+}
+
+isl::space pw_multi_aff::get_space() const
+{
+ return space();
+}
+
+isl::multi_pw_aff pw_multi_aff::sub(const isl::multi_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).sub(multi2);
+}
+
+isl::multi_union_pw_aff pw_multi_aff::sub(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_pw_aff(*this).sub(multi2);
+}
+
+isl::pw_multi_aff pw_multi_aff::sub(isl::pw_multi_aff pma2) const
+{
+ auto res = isl_pw_multi_aff_sub(copy(), pma2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::sub(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).sub(upma2);
+}
+
+isl::pw_multi_aff pw_multi_aff::sub(const isl::multi_aff &pma2) const
+{
+ return this->sub(isl::pw_multi_aff(pma2));
+}
+
+isl::pw_multi_aff pw_multi_aff::sub(const isl::pw_aff &pma2) const
+{
+ return this->sub(isl::pw_multi_aff(pma2));
+}
+
+isl::pw_multi_aff pw_multi_aff::subtract_domain(isl::set set) const
+{
+ auto res = isl_pw_multi_aff_subtract_domain(copy(), set.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::subtract_domain(const isl::space &space) const
+{
+ return isl::union_pw_multi_aff(*this).subtract_domain(space);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::subtract_domain(const isl::union_set &uset) const
+{
+ return isl::union_pw_multi_aff(*this).subtract_domain(uset);
+}
+
+isl::pw_multi_aff pw_multi_aff::subtract_domain(const isl::basic_set &set) const
+{
+ return this->subtract_domain(isl::set(set));
+}
+
+isl::pw_multi_aff pw_multi_aff::subtract_domain(const isl::point &set) const
+{
+ return this->subtract_domain(isl::set(set));
+}
+
+isl::pw_multi_aff_list pw_multi_aff::to_list() const
+{
+ auto res = isl_pw_multi_aff_to_list(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff pw_multi_aff::to_multi_pw_aff() const
+{
+ auto res = isl_pw_multi_aff_to_multi_pw_aff(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::to_union_pw_multi_aff() const
+{
+ auto res = isl_pw_multi_aff_to_union_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::id pw_multi_aff::tuple_id(isl::dim type) const
+{
+ auto res = isl_pw_multi_aff_get_tuple_id(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::id pw_multi_aff::get_tuple_id(isl::dim type) const
+{
+ return tuple_id(type);
+}
+
+isl::multi_pw_aff pw_multi_aff::unbind_params_insert_domain(const isl::multi_id &domain) const
+{
+ return isl::multi_pw_aff(*this).unbind_params_insert_domain(domain);
+}
+
+isl::multi_pw_aff pw_multi_aff::union_add(const isl::multi_pw_aff &mpa2) const
+{
+ return isl::multi_pw_aff(*this).union_add(mpa2);
+}
+
+isl::multi_union_pw_aff pw_multi_aff::union_add(const isl::multi_union_pw_aff &mupa2) const
+{
+ return isl::multi_pw_aff(*this).union_add(mupa2);
+}
+
+isl::pw_multi_aff pw_multi_aff::union_add(isl::pw_multi_aff pma2) const
+{
+ auto res = isl_pw_multi_aff_union_add(copy(), pma2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff pw_multi_aff::union_add(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).union_add(upma2);
+}
+
+isl::pw_multi_aff pw_multi_aff::union_add(const isl::multi_aff &pma2) const
+{
+ return this->union_add(isl::pw_multi_aff(pma2));
+}
+
+isl::pw_multi_aff pw_multi_aff::union_add(const isl::pw_aff &pma2) const
+{
+ return this->union_add(isl::pw_multi_aff(pma2));
+}
+
+isl::pw_multi_aff pw_multi_aff::zero(isl::space space)
+{
+ auto res = isl_pw_multi_aff_zero(space.release());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const pw_multi_aff &obj)
+{
+ char *str = isl_pw_multi_aff_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::pw_multi_aff_list
+pw_multi_aff_list manage(__isl_take isl_pw_multi_aff_list *ptr) {
+ return pw_multi_aff_list(ptr);
+}
+pw_multi_aff_list manage_copy(__isl_keep isl_pw_multi_aff_list *ptr) {
+ ptr = isl_pw_multi_aff_list_copy(ptr);
+ return pw_multi_aff_list(ptr);
+}
+
+pw_multi_aff_list::pw_multi_aff_list()
+ : ptr(nullptr) {}
+
+pw_multi_aff_list::pw_multi_aff_list(const pw_multi_aff_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+pw_multi_aff_list::pw_multi_aff_list(__isl_take isl_pw_multi_aff_list *ptr)
+ : ptr(ptr) {}
+
+pw_multi_aff_list::pw_multi_aff_list(isl::ctx ctx, int n)
+{
+ auto res = isl_pw_multi_aff_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+pw_multi_aff_list::pw_multi_aff_list(isl::pw_multi_aff el)
+{
+ auto res = isl_pw_multi_aff_list_from_pw_multi_aff(el.release());
+ ptr = res;
+}
+
+pw_multi_aff_list::pw_multi_aff_list(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_pw_multi_aff_list_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+pw_multi_aff_list &pw_multi_aff_list::operator=(pw_multi_aff_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+pw_multi_aff_list::~pw_multi_aff_list() {
+ if (ptr)
+ isl_pw_multi_aff_list_free(ptr);
+}
+
+__isl_give isl_pw_multi_aff_list *pw_multi_aff_list::copy() const & {
+ return isl_pw_multi_aff_list_copy(ptr);
+}
+
+__isl_keep isl_pw_multi_aff_list *pw_multi_aff_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_pw_multi_aff_list *pw_multi_aff_list::release() {
+ isl_pw_multi_aff_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool pw_multi_aff_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx pw_multi_aff_list::ctx() const {
+ return isl::ctx(isl_pw_multi_aff_list_get_ctx(ptr));
+}
+
+isl::pw_multi_aff_list pw_multi_aff_list::add(isl::pw_multi_aff el) const
+{
+ auto res = isl_pw_multi_aff_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff_list::at(int index) const
+{
+ auto res = isl_pw_multi_aff_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::pw_multi_aff pw_multi_aff_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::pw_multi_aff_list pw_multi_aff_list::clear() const
+{
+ auto res = isl_pw_multi_aff_list_clear(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff_list pw_multi_aff_list::concat(isl::pw_multi_aff_list list2) const
+{
+ auto res = isl_pw_multi_aff_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff_list pw_multi_aff_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_pw_multi_aff_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat pw_multi_aff_list::foreach(const std::function<stat(isl::pw_multi_aff)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::pw_multi_aff)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_pw_multi_aff *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_pw_multi_aff_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::pw_multi_aff_list pw_multi_aff_list::insert(unsigned int pos, isl::pw_multi_aff el) const
+{
+ auto res = isl_pw_multi_aff_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+class size pw_multi_aff_list::size() const
+{
+ auto res = isl_pw_multi_aff_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const pw_multi_aff_list &obj)
+{
+ char *str = isl_pw_multi_aff_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule
+schedule manage(__isl_take isl_schedule *ptr) {
+ return schedule(ptr);
+}
+schedule manage_copy(__isl_keep isl_schedule *ptr) {
+ ptr = isl_schedule_copy(ptr);
+ return schedule(ptr);
+}
+
+schedule::schedule()
+ : ptr(nullptr) {}
+
+schedule::schedule(const schedule &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+schedule::schedule(__isl_take isl_schedule *ptr)
+ : ptr(ptr) {}
+
+schedule::schedule(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_schedule_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+schedule &schedule::operator=(schedule obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+schedule::~schedule() {
+ if (ptr)
+ isl_schedule_free(ptr);
+}
+
+__isl_give isl_schedule *schedule::copy() const & {
+ return isl_schedule_copy(ptr);
+}
+
+__isl_keep isl_schedule *schedule::get() const {
+ return ptr;
+}
+
+__isl_give isl_schedule *schedule::release() {
+ isl_schedule *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool schedule::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx schedule::ctx() const {
+ return isl::ctx(isl_schedule_get_ctx(ptr));
+}
+
+isl::schedule schedule::align_params(isl::space space) const
+{
+ auto res = isl_schedule_align_params(copy(), space.release());
+ return manage(res);
+}
+
+isl::union_set schedule::domain() const
+{
+ auto res = isl_schedule_get_domain(get());
+ return manage(res);
+}
+
+isl::union_set schedule::get_domain() const
+{
+ return domain();
+}
+
+isl::schedule schedule::from_domain(isl::union_set domain)
+{
+ auto res = isl_schedule_from_domain(domain.release());
+ return manage(res);
+}
+
+isl::schedule schedule::gist_domain_params(isl::set context) const
+{
+ auto res = isl_schedule_gist_domain_params(copy(), context.release());
+ return manage(res);
+}
+
+isl::schedule schedule::insert_partial_schedule(isl::multi_union_pw_aff partial) const
+{
+ auto res = isl_schedule_insert_partial_schedule(copy(), partial.release());
+ return manage(res);
+}
+
+isl::schedule schedule::intersect_domain(isl::union_set domain) const
+{
+ auto res = isl_schedule_intersect_domain(copy(), domain.release());
+ return manage(res);
+}
+
+isl::union_map schedule::map() const
+{
+ auto res = isl_schedule_get_map(get());
+ return manage(res);
+}
+
+isl::union_map schedule::get_map() const
+{
+ return map();
+}
+
+isl::schedule schedule::pullback(isl::union_pw_multi_aff upma) const
+{
+ auto res = isl_schedule_pullback_union_pw_multi_aff(copy(), upma.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule::root() const
+{
+ auto res = isl_schedule_get_root(get());
+ return manage(res);
+}
+
+isl::schedule_node schedule::get_root() const
+{
+ return root();
+}
+
+isl::schedule schedule::sequence(isl::schedule schedule2) const
+{
+ auto res = isl_schedule_sequence(copy(), schedule2.release());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule &obj)
+{
+ char *str = isl_schedule_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_constraints
+schedule_constraints manage(__isl_take isl_schedule_constraints *ptr) {
+ return schedule_constraints(ptr);
+}
+schedule_constraints manage_copy(__isl_keep isl_schedule_constraints *ptr) {
+ ptr = isl_schedule_constraints_copy(ptr);
+ return schedule_constraints(ptr);
+}
+
+schedule_constraints::schedule_constraints()
+ : ptr(nullptr) {}
+
+schedule_constraints::schedule_constraints(const schedule_constraints &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+schedule_constraints::schedule_constraints(__isl_take isl_schedule_constraints *ptr)
+ : ptr(ptr) {}
+
+schedule_constraints::schedule_constraints(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_schedule_constraints_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+schedule_constraints &schedule_constraints::operator=(schedule_constraints obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+schedule_constraints::~schedule_constraints() {
+ if (ptr)
+ isl_schedule_constraints_free(ptr);
+}
+
+__isl_give isl_schedule_constraints *schedule_constraints::copy() const & {
+ return isl_schedule_constraints_copy(ptr);
+}
+
+__isl_keep isl_schedule_constraints *schedule_constraints::get() const {
+ return ptr;
+}
+
+__isl_give isl_schedule_constraints *schedule_constraints::release() {
+ isl_schedule_constraints *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool schedule_constraints::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx schedule_constraints::ctx() const {
+ return isl::ctx(isl_schedule_constraints_get_ctx(ptr));
+}
+
+isl::union_map schedule_constraints::coincidence() const
+{
+ auto res = isl_schedule_constraints_get_coincidence(get());
+ return manage(res);
+}
+
+isl::union_map schedule_constraints::get_coincidence() const
+{
+ return coincidence();
+}
+
+isl::schedule schedule_constraints::compute_schedule() const
+{
+ auto res = isl_schedule_constraints_compute_schedule(copy());
+ return manage(res);
+}
+
+isl::union_map schedule_constraints::conditional_validity() const
+{
+ auto res = isl_schedule_constraints_get_conditional_validity(get());
+ return manage(res);
+}
+
+isl::union_map schedule_constraints::get_conditional_validity() const
+{
+ return conditional_validity();
+}
+
+isl::union_map schedule_constraints::conditional_validity_condition() const
+{
+ auto res = isl_schedule_constraints_get_conditional_validity_condition(get());
+ return manage(res);
+}
+
+isl::union_map schedule_constraints::get_conditional_validity_condition() const
+{
+ return conditional_validity_condition();
+}
+
+isl::set schedule_constraints::context() const
+{
+ auto res = isl_schedule_constraints_get_context(get());
+ return manage(res);
+}
+
+isl::set schedule_constraints::get_context() const
+{
+ return context();
+}
+
+isl::union_set schedule_constraints::domain() const
+{
+ auto res = isl_schedule_constraints_get_domain(get());
+ return manage(res);
+}
+
+isl::union_set schedule_constraints::get_domain() const
+{
+ return domain();
+}
+
+isl::schedule_constraints schedule_constraints::on_domain(isl::union_set domain)
+{
+ auto res = isl_schedule_constraints_on_domain(domain.release());
+ return manage(res);
+}
+
+isl::union_map schedule_constraints::proximity() const
+{
+ auto res = isl_schedule_constraints_get_proximity(get());
+ return manage(res);
+}
+
+isl::union_map schedule_constraints::get_proximity() const
+{
+ return proximity();
+}
+
+isl::schedule_constraints schedule_constraints::set_coincidence(isl::union_map coincidence) const
+{
+ auto res = isl_schedule_constraints_set_coincidence(copy(), coincidence.release());
+ return manage(res);
+}
+
+isl::schedule_constraints schedule_constraints::set_conditional_validity(isl::union_map condition, isl::union_map validity) const
+{
+ auto res = isl_schedule_constraints_set_conditional_validity(copy(), condition.release(), validity.release());
+ return manage(res);
+}
+
+isl::schedule_constraints schedule_constraints::set_context(isl::set context) const
+{
+ auto res = isl_schedule_constraints_set_context(copy(), context.release());
+ return manage(res);
+}
+
+isl::schedule_constraints schedule_constraints::set_proximity(isl::union_map proximity) const
+{
+ auto res = isl_schedule_constraints_set_proximity(copy(), proximity.release());
+ return manage(res);
+}
+
+isl::schedule_constraints schedule_constraints::set_validity(isl::union_map validity) const
+{
+ auto res = isl_schedule_constraints_set_validity(copy(), validity.release());
+ return manage(res);
+}
+
+isl::union_map schedule_constraints::validity() const
+{
+ auto res = isl_schedule_constraints_get_validity(get());
+ return manage(res);
+}
+
+isl::union_map schedule_constraints::get_validity() const
+{
+ return validity();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_constraints &obj)
+{
+ char *str = isl_schedule_constraints_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node
+schedule_node manage(__isl_take isl_schedule_node *ptr) {
+ return schedule_node(ptr);
+}
+schedule_node manage_copy(__isl_keep isl_schedule_node *ptr) {
+ ptr = isl_schedule_node_copy(ptr);
+ return schedule_node(ptr);
+}
+
+schedule_node::schedule_node()
+ : ptr(nullptr) {}
+
+schedule_node::schedule_node(const schedule_node &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+schedule_node::schedule_node(__isl_take isl_schedule_node *ptr)
+ : ptr(ptr) {}
+
+schedule_node &schedule_node::operator=(schedule_node obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+schedule_node::~schedule_node() {
+ if (ptr)
+ isl_schedule_node_free(ptr);
+}
+
+__isl_give isl_schedule_node *schedule_node::copy() const & {
+ return isl_schedule_node_copy(ptr);
+}
+
+__isl_keep isl_schedule_node *schedule_node::get() const {
+ return ptr;
+}
+
+__isl_give isl_schedule_node *schedule_node::release() {
+ isl_schedule_node *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool schedule_node::is_null() const {
+ return ptr == nullptr;
+}
+
+template <typename T, typename>
+boolean schedule_node::isa_type(T subtype) const
+{
+ if (is_null())
+ return boolean();
+ return isl_schedule_node_get_type(get()) == subtype;
+}
+template <class T>
+boolean schedule_node::isa() const
+{
+ return isa_type<decltype(T::type)>(T::type);
+}
+template <class T>
+T schedule_node::as() const
+{
+ if (isa<T>().is_false())
+ isl_die(ctx().get(), isl_error_invalid, "not an object of the requested subtype", return T());
+ return T(copy());
+}
+
+isl::ctx schedule_node::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+isl::schedule_node schedule_node::ancestor(int generation) const
+{
+ auto res = isl_schedule_node_ancestor(copy(), generation);
+ return manage(res);
+}
+
+class size schedule_node::ancestor_child_position(const isl::schedule_node &ancestor) const
+{
+ auto res = isl_schedule_node_get_ancestor_child_position(get(), ancestor.get());
+ return manage(res);
+}
+
+class size schedule_node::get_ancestor_child_position(const isl::schedule_node &ancestor) const
+{
+ return ancestor_child_position(ancestor);
+}
+
+isl::schedule_node schedule_node::child(int pos) const
+{
+ auto res = isl_schedule_node_child(copy(), pos);
+ return manage(res);
+}
+
+class size schedule_node::child_position() const
+{
+ auto res = isl_schedule_node_get_child_position(get());
+ return manage(res);
+}
+
+class size schedule_node::get_child_position() const
+{
+ return child_position();
+}
+
+isl::union_set schedule_node::domain() const
+{
+ auto res = isl_schedule_node_get_domain(get());
+ return manage(res);
+}
+
+isl::union_set schedule_node::get_domain() const
+{
+ return domain();
+}
+
+boolean schedule_node::every_descendant(const std::function<boolean(isl::schedule_node)> &test) const
+{
+ struct test_data {
+ std::function<boolean(isl::schedule_node)> func;
+ } test_data = { test };
+ auto test_lambda = [](isl_schedule_node *arg_0, void *arg_1) -> isl_bool {
+ auto *data = static_cast<struct test_data *>(arg_1);
+ auto ret = (data->func)(manage_copy(arg_0));
+ return ret.release();
+ };
+ auto res = isl_schedule_node_every_descendant(get(), test_lambda, &test_data);
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::first_child() const
+{
+ auto res = isl_schedule_node_first_child(copy());
+ return manage(res);
+}
+
+stat schedule_node::foreach_ancestor_top_down(const std::function<stat(isl::schedule_node)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::schedule_node)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_schedule_node *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage_copy(arg_0));
+ return ret.release();
+ };
+ auto res = isl_schedule_node_foreach_ancestor_top_down(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+stat schedule_node::foreach_descendant_top_down(const std::function<boolean(isl::schedule_node)> &fn) const
+{
+ struct fn_data {
+ std::function<boolean(isl::schedule_node)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_schedule_node *arg_0, void *arg_1) -> isl_bool {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage_copy(arg_0));
+ return ret.release();
+ };
+ auto res = isl_schedule_node_foreach_descendant_top_down(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::from_domain(isl::union_set domain)
+{
+ auto res = isl_schedule_node_from_domain(domain.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::from_extension(isl::union_map extension)
+{
+ auto res = isl_schedule_node_from_extension(extension.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::graft_after(isl::schedule_node graft) const
+{
+ auto res = isl_schedule_node_graft_after(copy(), graft.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::graft_before(isl::schedule_node graft) const
+{
+ auto res = isl_schedule_node_graft_before(copy(), graft.release());
+ return manage(res);
+}
+
+boolean schedule_node::has_children() const
+{
+ auto res = isl_schedule_node_has_children(get());
+ return manage(res);
+}
+
+boolean schedule_node::has_next_sibling() const
+{
+ auto res = isl_schedule_node_has_next_sibling(get());
+ return manage(res);
+}
+
+boolean schedule_node::has_parent() const
+{
+ auto res = isl_schedule_node_has_parent(get());
+ return manage(res);
+}
+
+boolean schedule_node::has_previous_sibling() const
+{
+ auto res = isl_schedule_node_has_previous_sibling(get());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::insert_context(isl::set context) const
+{
+ auto res = isl_schedule_node_insert_context(copy(), context.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::insert_filter(isl::union_set filter) const
+{
+ auto res = isl_schedule_node_insert_filter(copy(), filter.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::insert_guard(isl::set context) const
+{
+ auto res = isl_schedule_node_insert_guard(copy(), context.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::insert_mark(isl::id mark) const
+{
+ auto res = isl_schedule_node_insert_mark(copy(), mark.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::insert_mark(const std::string &mark) const
+{
+ return this->insert_mark(isl::id(ctx(), mark));
+}
+
+isl::schedule_node schedule_node::insert_partial_schedule(isl::multi_union_pw_aff schedule) const
+{
+ auto res = isl_schedule_node_insert_partial_schedule(copy(), schedule.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::insert_sequence(isl::union_set_list filters) const
+{
+ auto res = isl_schedule_node_insert_sequence(copy(), filters.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::insert_set(isl::union_set_list filters) const
+{
+ auto res = isl_schedule_node_insert_set(copy(), filters.release());
+ return manage(res);
+}
+
+boolean schedule_node::is_equal(const isl::schedule_node &node2) const
+{
+ auto res = isl_schedule_node_is_equal(get(), node2.get());
+ return manage(res);
+}
+
+boolean schedule_node::is_subtree_anchored() const
+{
+ auto res = isl_schedule_node_is_subtree_anchored(get());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::map_descendant_bottom_up(const std::function<isl::schedule_node(isl::schedule_node)> &fn) const
+{
+ struct fn_data {
+ std::function<isl::schedule_node(isl::schedule_node)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_schedule_node *arg_0, void *arg_1) -> isl_schedule_node * {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_schedule_node_map_descendant_bottom_up(copy(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+class size schedule_node::n_children() const
+{
+ auto res = isl_schedule_node_n_children(get());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::next_sibling() const
+{
+ auto res = isl_schedule_node_next_sibling(copy());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::order_after(isl::union_set filter) const
+{
+ auto res = isl_schedule_node_order_after(copy(), filter.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::order_before(isl::union_set filter) const
+{
+ auto res = isl_schedule_node_order_before(copy(), filter.release());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::parent() const
+{
+ auto res = isl_schedule_node_parent(copy());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff schedule_node::prefix_schedule_multi_union_pw_aff() const
+{
+ auto res = isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(get());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff schedule_node::get_prefix_schedule_multi_union_pw_aff() const
+{
+ return prefix_schedule_multi_union_pw_aff();
+}
+
+isl::union_map schedule_node::prefix_schedule_relation() const
+{
+ auto res = isl_schedule_node_get_prefix_schedule_relation(get());
+ return manage(res);
+}
+
+isl::union_map schedule_node::get_prefix_schedule_relation() const
+{
+ return prefix_schedule_relation();
+}
+
+isl::union_map schedule_node::prefix_schedule_union_map() const
+{
+ auto res = isl_schedule_node_get_prefix_schedule_union_map(get());
+ return manage(res);
+}
+
+isl::union_map schedule_node::get_prefix_schedule_union_map() const
+{
+ return prefix_schedule_union_map();
+}
+
+isl::union_pw_multi_aff schedule_node::prefix_schedule_union_pw_multi_aff() const
+{
+ auto res = isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(get());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff schedule_node::get_prefix_schedule_union_pw_multi_aff() const
+{
+ return prefix_schedule_union_pw_multi_aff();
+}
+
+isl::schedule_node schedule_node::previous_sibling() const
+{
+ auto res = isl_schedule_node_previous_sibling(copy());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::root() const
+{
+ auto res = isl_schedule_node_root(copy());
+ return manage(res);
+}
+
+isl::schedule schedule_node::schedule() const
+{
+ auto res = isl_schedule_node_get_schedule(get());
+ return manage(res);
+}
+
+isl::schedule schedule_node::get_schedule() const
+{
+ return schedule();
+}
+
+class size schedule_node::schedule_depth() const
+{
+ auto res = isl_schedule_node_get_schedule_depth(get());
+ return manage(res);
+}
+
+class size schedule_node::get_schedule_depth() const
+{
+ return schedule_depth();
+}
+
+isl::schedule_node schedule_node::shared_ancestor(const isl::schedule_node &node2) const
+{
+ auto res = isl_schedule_node_get_shared_ancestor(get(), node2.get());
+ return manage(res);
+}
+
+isl::schedule_node schedule_node::get_shared_ancestor(const isl::schedule_node &node2) const
+{
+ return shared_ancestor(node2);
+}
+
+class size schedule_node::tree_depth() const
+{
+ auto res = isl_schedule_node_get_tree_depth(get());
+ return manage(res);
+}
+
+class size schedule_node::get_tree_depth() const
+{
+ return tree_depth();
+}
+
+isl::union_set schedule_node::universe_domain() const
+{
+ auto res = isl_schedule_node_get_universe_domain(get());
+ return manage(res);
+}
+
+isl::union_set schedule_node::get_universe_domain() const
+{
+ return universe_domain();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node_band
+schedule_node_band::schedule_node_band()
+ : schedule_node() {}
+
+schedule_node_band::schedule_node_band(const schedule_node_band &obj)
+ : schedule_node(obj)
+{
+}
+
+schedule_node_band::schedule_node_band(__isl_take isl_schedule_node *ptr)
+ : schedule_node(ptr) {}
+
+schedule_node_band &schedule_node_band::operator=(schedule_node_band obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx schedule_node_band::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+isl::union_set schedule_node_band::ast_build_options() const
+{
+ auto res = isl_schedule_node_band_get_ast_build_options(get());
+ return manage(res);
+}
+
+isl::union_set schedule_node_band::get_ast_build_options() const
+{
+ return ast_build_options();
+}
+
+isl::set schedule_node_band::ast_isolate_option() const
+{
+ auto res = isl_schedule_node_band_get_ast_isolate_option(get());
+ return manage(res);
+}
+
+isl::set schedule_node_band::get_ast_isolate_option() const
+{
+ return ast_isolate_option();
+}
+
+boolean schedule_node_band::member_get_coincident(int pos) const
+{
+ auto res = isl_schedule_node_band_member_get_coincident(get(), pos);
+ return manage(res);
+}
+
+schedule_node_band schedule_node_band::member_set_coincident(int pos, int coincident) const
+{
+ auto res = isl_schedule_node_band_member_set_coincident(copy(), pos, coincident);
+ return manage(res).as<schedule_node_band>();
+}
+
+schedule_node_band schedule_node_band::mod(isl::multi_val mv) const
+{
+ auto res = isl_schedule_node_band_mod(copy(), mv.release());
+ return manage(res).as<schedule_node_band>();
+}
+
+class size schedule_node_band::n_member() const
+{
+ auto res = isl_schedule_node_band_n_member(get());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff schedule_node_band::partial_schedule() const
+{
+ auto res = isl_schedule_node_band_get_partial_schedule(get());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff schedule_node_band::get_partial_schedule() const
+{
+ return partial_schedule();
+}
+
+boolean schedule_node_band::permutable() const
+{
+ auto res = isl_schedule_node_band_get_permutable(get());
+ return manage(res);
+}
+
+boolean schedule_node_band::get_permutable() const
+{
+ return permutable();
+}
+
+schedule_node_band schedule_node_band::scale(isl::multi_val mv) const
+{
+ auto res = isl_schedule_node_band_scale(copy(), mv.release());
+ return manage(res).as<schedule_node_band>();
+}
+
+schedule_node_band schedule_node_band::scale_down(isl::multi_val mv) const
+{
+ auto res = isl_schedule_node_band_scale_down(copy(), mv.release());
+ return manage(res).as<schedule_node_band>();
+}
+
+schedule_node_band schedule_node_band::set_ast_build_options(isl::union_set options) const
+{
+ auto res = isl_schedule_node_band_set_ast_build_options(copy(), options.release());
+ return manage(res).as<schedule_node_band>();
+}
+
+schedule_node_band schedule_node_band::set_permutable(int permutable) const
+{
+ auto res = isl_schedule_node_band_set_permutable(copy(), permutable);
+ return manage(res).as<schedule_node_band>();
+}
+
+schedule_node_band schedule_node_band::shift(isl::multi_union_pw_aff shift) const
+{
+ auto res = isl_schedule_node_band_shift(copy(), shift.release());
+ return manage(res).as<schedule_node_band>();
+}
+
+schedule_node_band schedule_node_band::split(int pos) const
+{
+ auto res = isl_schedule_node_band_split(copy(), pos);
+ return manage(res).as<schedule_node_band>();
+}
+
+schedule_node_band schedule_node_band::tile(isl::multi_val sizes) const
+{
+ auto res = isl_schedule_node_band_tile(copy(), sizes.release());
+ return manage(res).as<schedule_node_band>();
+}
+
+schedule_node_band schedule_node_band::member_set_ast_loop_default(int pos) const
+{
+ auto res = isl_schedule_node_band_member_set_ast_loop_type(copy(), pos, isl_ast_loop_default);
+ return manage(res).as<schedule_node_band>();
+}
+
+schedule_node_band schedule_node_band::member_set_ast_loop_atomic(int pos) const
+{
+ auto res = isl_schedule_node_band_member_set_ast_loop_type(copy(), pos, isl_ast_loop_atomic);
+ return manage(res).as<schedule_node_band>();
+}
+
+schedule_node_band schedule_node_band::member_set_ast_loop_unroll(int pos) const
+{
+ auto res = isl_schedule_node_band_member_set_ast_loop_type(copy(), pos, isl_ast_loop_unroll);
+ return manage(res).as<schedule_node_band>();
+}
+
+schedule_node_band schedule_node_band::member_set_ast_loop_separate(int pos) const
+{
+ auto res = isl_schedule_node_band_member_set_ast_loop_type(copy(), pos, isl_ast_loop_separate);
+ return manage(res).as<schedule_node_band>();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node_band &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node_context
+schedule_node_context::schedule_node_context()
+ : schedule_node() {}
+
+schedule_node_context::schedule_node_context(const schedule_node_context &obj)
+ : schedule_node(obj)
+{
+}
+
+schedule_node_context::schedule_node_context(__isl_take isl_schedule_node *ptr)
+ : schedule_node(ptr) {}
+
+schedule_node_context &schedule_node_context::operator=(schedule_node_context obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx schedule_node_context::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+isl::set schedule_node_context::context() const
+{
+ auto res = isl_schedule_node_context_get_context(get());
+ return manage(res);
+}
+
+isl::set schedule_node_context::get_context() const
+{
+ return context();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node_context &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node_domain
+schedule_node_domain::schedule_node_domain()
+ : schedule_node() {}
+
+schedule_node_domain::schedule_node_domain(const schedule_node_domain &obj)
+ : schedule_node(obj)
+{
+}
+
+schedule_node_domain::schedule_node_domain(__isl_take isl_schedule_node *ptr)
+ : schedule_node(ptr) {}
+
+schedule_node_domain &schedule_node_domain::operator=(schedule_node_domain obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx schedule_node_domain::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+isl::union_set schedule_node_domain::domain() const
+{
+ auto res = isl_schedule_node_domain_get_domain(get());
+ return manage(res);
+}
+
+isl::union_set schedule_node_domain::get_domain() const
+{
+ return domain();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node_domain &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node_expansion
+schedule_node_expansion::schedule_node_expansion()
+ : schedule_node() {}
+
+schedule_node_expansion::schedule_node_expansion(const schedule_node_expansion &obj)
+ : schedule_node(obj)
+{
+}
+
+schedule_node_expansion::schedule_node_expansion(__isl_take isl_schedule_node *ptr)
+ : schedule_node(ptr) {}
+
+schedule_node_expansion &schedule_node_expansion::operator=(schedule_node_expansion obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx schedule_node_expansion::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+isl::union_pw_multi_aff schedule_node_expansion::contraction() const
+{
+ auto res = isl_schedule_node_expansion_get_contraction(get());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff schedule_node_expansion::get_contraction() const
+{
+ return contraction();
+}
+
+isl::union_map schedule_node_expansion::expansion() const
+{
+ auto res = isl_schedule_node_expansion_get_expansion(get());
+ return manage(res);
+}
+
+isl::union_map schedule_node_expansion::get_expansion() const
+{
+ return expansion();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node_expansion &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node_extension
+schedule_node_extension::schedule_node_extension()
+ : schedule_node() {}
+
+schedule_node_extension::schedule_node_extension(const schedule_node_extension &obj)
+ : schedule_node(obj)
+{
+}
+
+schedule_node_extension::schedule_node_extension(__isl_take isl_schedule_node *ptr)
+ : schedule_node(ptr) {}
+
+schedule_node_extension &schedule_node_extension::operator=(schedule_node_extension obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx schedule_node_extension::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+isl::union_map schedule_node_extension::extension() const
+{
+ auto res = isl_schedule_node_extension_get_extension(get());
+ return manage(res);
+}
+
+isl::union_map schedule_node_extension::get_extension() const
+{
+ return extension();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node_extension &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node_filter
+schedule_node_filter::schedule_node_filter()
+ : schedule_node() {}
+
+schedule_node_filter::schedule_node_filter(const schedule_node_filter &obj)
+ : schedule_node(obj)
+{
+}
+
+schedule_node_filter::schedule_node_filter(__isl_take isl_schedule_node *ptr)
+ : schedule_node(ptr) {}
+
+schedule_node_filter &schedule_node_filter::operator=(schedule_node_filter obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx schedule_node_filter::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+isl::union_set schedule_node_filter::filter() const
+{
+ auto res = isl_schedule_node_filter_get_filter(get());
+ return manage(res);
+}
+
+isl::union_set schedule_node_filter::get_filter() const
+{
+ return filter();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node_filter &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node_guard
+schedule_node_guard::schedule_node_guard()
+ : schedule_node() {}
+
+schedule_node_guard::schedule_node_guard(const schedule_node_guard &obj)
+ : schedule_node(obj)
+{
+}
+
+schedule_node_guard::schedule_node_guard(__isl_take isl_schedule_node *ptr)
+ : schedule_node(ptr) {}
+
+schedule_node_guard &schedule_node_guard::operator=(schedule_node_guard obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx schedule_node_guard::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+isl::set schedule_node_guard::guard() const
+{
+ auto res = isl_schedule_node_guard_get_guard(get());
+ return manage(res);
+}
+
+isl::set schedule_node_guard::get_guard() const
+{
+ return guard();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node_guard &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node_leaf
+schedule_node_leaf::schedule_node_leaf()
+ : schedule_node() {}
+
+schedule_node_leaf::schedule_node_leaf(const schedule_node_leaf &obj)
+ : schedule_node(obj)
+{
+}
+
+schedule_node_leaf::schedule_node_leaf(__isl_take isl_schedule_node *ptr)
+ : schedule_node(ptr) {}
+
+schedule_node_leaf &schedule_node_leaf::operator=(schedule_node_leaf obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx schedule_node_leaf::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node_leaf &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node_mark
+schedule_node_mark::schedule_node_mark()
+ : schedule_node() {}
+
+schedule_node_mark::schedule_node_mark(const schedule_node_mark &obj)
+ : schedule_node(obj)
+{
+}
+
+schedule_node_mark::schedule_node_mark(__isl_take isl_schedule_node *ptr)
+ : schedule_node(ptr) {}
+
+schedule_node_mark &schedule_node_mark::operator=(schedule_node_mark obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx schedule_node_mark::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+isl::id schedule_node_mark::id() const
+{
+ auto res = isl_schedule_node_mark_get_id(get());
+ return manage(res);
+}
+
+isl::id schedule_node_mark::get_id() const
+{
+ return id();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node_mark &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node_sequence
+schedule_node_sequence::schedule_node_sequence()
+ : schedule_node() {}
+
+schedule_node_sequence::schedule_node_sequence(const schedule_node_sequence &obj)
+ : schedule_node(obj)
+{
+}
+
+schedule_node_sequence::schedule_node_sequence(__isl_take isl_schedule_node *ptr)
+ : schedule_node(ptr) {}
+
+schedule_node_sequence &schedule_node_sequence::operator=(schedule_node_sequence obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx schedule_node_sequence::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node_sequence &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::schedule_node_set
+schedule_node_set::schedule_node_set()
+ : schedule_node() {}
+
+schedule_node_set::schedule_node_set(const schedule_node_set &obj)
+ : schedule_node(obj)
+{
+}
+
+schedule_node_set::schedule_node_set(__isl_take isl_schedule_node *ptr)
+ : schedule_node(ptr) {}
+
+schedule_node_set &schedule_node_set::operator=(schedule_node_set obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+isl::ctx schedule_node_set::ctx() const {
+ return isl::ctx(isl_schedule_node_get_ctx(ptr));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const schedule_node_set &obj)
+{
+ char *str = isl_schedule_node_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::set
+set manage(__isl_take isl_set *ptr) {
+ return set(ptr);
+}
+set manage_copy(__isl_keep isl_set *ptr) {
+ ptr = isl_set_copy(ptr);
+ return set(ptr);
+}
+
+set::set()
+ : ptr(nullptr) {}
+
+set::set(const set &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+set::set(__isl_take isl_set *ptr)
+ : ptr(ptr) {}
+
+set::set(isl::basic_set bset)
+{
+ auto res = isl_set_from_basic_set(bset.release());
+ ptr = res;
+}
+
+set::set(isl::point pnt)
+{
+ auto res = isl_set_from_point(pnt.release());
+ ptr = res;
+}
+
+set::set(isl::union_set uset)
+{
+ auto res = isl_set_from_union_set(uset.release());
+ ptr = res;
+}
+
+set::set(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_set_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+set &set::operator=(set obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+set::~set() {
+ if (ptr)
+ isl_set_free(ptr);
+}
+
+__isl_give isl_set *set::copy() const & {
+ return isl_set_copy(ptr);
+}
+
+__isl_keep isl_set *set::get() const {
+ return ptr;
+}
+
+__isl_give isl_set *set::release() {
+ isl_set *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool set::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx set::ctx() const {
+ return isl::ctx(isl_set_get_ctx(ptr));
+}
+
+isl::set set::add_constraint(isl::constraint constraint) const
+{
+ auto res = isl_set_add_constraint(copy(), constraint.release());
+ return manage(res);
+}
+
+isl::set set::add_dims(isl::dim type, unsigned int n) const
+{
+ auto res = isl_set_add_dims(copy(), static_cast<enum isl_dim_type>(type), n);
+ return manage(res);
+}
+
+isl::basic_set set::affine_hull() const
+{
+ auto res = isl_set_affine_hull(copy());
+ return manage(res);
+}
+
+isl::set set::align_params(isl::space model) const
+{
+ auto res = isl_set_align_params(copy(), model.release());
+ return manage(res);
+}
+
+isl::set set::apply(isl::map map) const
+{
+ auto res = isl_set_apply(copy(), map.release());
+ return manage(res);
+}
+
+isl::union_set set::apply(const isl::union_map &umap) const
+{
+ return isl::union_set(*this).apply(umap);
+}
+
+isl::set set::apply(const isl::basic_map &map) const
+{
+ return this->apply(isl::map(map));
+}
+
+isl::pw_multi_aff set::as_pw_multi_aff() const
+{
+ auto res = isl_set_as_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::set set::as_set() const
+{
+ return isl::union_set(*this).as_set();
+}
+
+isl::basic_set_list set::basic_set_list() const
+{
+ auto res = isl_set_get_basic_set_list(get());
+ return manage(res);
+}
+
+isl::basic_set_list set::get_basic_set_list() const
+{
+ return basic_set_list();
+}
+
+isl::set set::bind(isl::multi_id tuple) const
+{
+ auto res = isl_set_bind(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::set set::coalesce() const
+{
+ auto res = isl_set_coalesce(copy());
+ return manage(res);
+}
+
+isl::set set::complement() const
+{
+ auto res = isl_set_complement(copy());
+ return manage(res);
+}
+
+isl::union_set set::compute_divs() const
+{
+ return isl::union_set(*this).compute_divs();
+}
+
+boolean set::contains(const isl::space &space) const
+{
+ return isl::union_set(*this).contains(space);
+}
+
+isl::basic_set set::convex_hull() const
+{
+ auto res = isl_set_convex_hull(copy());
+ return manage(res);
+}
+
+isl::set set::detect_equalities() const
+{
+ auto res = isl_set_detect_equalities(copy());
+ return manage(res);
+}
+
+class size set::dim(isl::dim type) const
+{
+ auto res = isl_set_dim(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+boolean set::dim_has_any_lower_bound(isl::dim type, unsigned int pos) const
+{
+ auto res = isl_set_dim_has_any_lower_bound(get(), static_cast<enum isl_dim_type>(type), pos);
+ return manage(res);
+}
+
+isl::id set::dim_id(isl::dim type, unsigned int pos) const
+{
+ auto res = isl_set_get_dim_id(get(), static_cast<enum isl_dim_type>(type), pos);
+ return manage(res);
+}
+
+isl::id set::get_dim_id(isl::dim type, unsigned int pos) const
+{
+ return dim_id(type, pos);
+}
+
+isl::pw_aff set::dim_max(int pos) const
+{
+ auto res = isl_set_dim_max(copy(), pos);
+ return manage(res);
+}
+
+isl::val set::dim_max_val(int pos) const
+{
+ auto res = isl_set_dim_max_val(copy(), pos);
+ return manage(res);
+}
+
+isl::pw_aff set::dim_min(int pos) const
+{
+ auto res = isl_set_dim_min(copy(), pos);
+ return manage(res);
+}
+
+isl::val set::dim_min_val(int pos) const
+{
+ auto res = isl_set_dim_min_val(copy(), pos);
+ return manage(res);
+}
+
+std::string set::dim_name(isl::dim type, unsigned int pos) const
+{
+ auto res = isl_set_get_dim_name(get(), static_cast<enum isl_dim_type>(type), pos);
+ std::string tmp(res);
+ return tmp;
+}
+
+std::string set::get_dim_name(isl::dim type, unsigned int pos) const
+{
+ return dim_name(type, pos);
+}
+
+isl::set set::drop_constraints_involving_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ auto res = isl_set_drop_constraints_involving_dims(copy(), static_cast<enum isl_dim_type>(type), first, n);
+ return manage(res);
+}
+
+isl::set set::eliminate(isl::dim type, unsigned int first, unsigned int n) const
+{
+ auto res = isl_set_eliminate(copy(), static_cast<enum isl_dim_type>(type), first, n);
+ return manage(res);
+}
+
+isl::set set::empty(isl::space space)
+{
+ auto res = isl_set_empty(space.release());
+ return manage(res);
+}
+
+boolean set::every_set(const std::function<boolean(isl::set)> &test) const
+{
+ return isl::union_set(*this).every_set(test);
+}
+
+isl::set set::extract_set(const isl::space &space) const
+{
+ return isl::union_set(*this).extract_set(space);
+}
+
+int set::find_dim_by_id(isl::dim type, const isl::id &id) const
+{
+ auto res = isl_set_find_dim_by_id(get(), static_cast<enum isl_dim_type>(type), id.get());
+ return res;
+}
+
+int set::find_dim_by_id(isl::dim type, const std::string &id) const
+{
+ return this->find_dim_by_id(type, isl::id(ctx(), id));
+}
+
+isl::set set::fix_si(isl::dim type, unsigned int pos, int value) const
+{
+ auto res = isl_set_fix_si(copy(), static_cast<enum isl_dim_type>(type), pos, value);
+ return manage(res);
+}
+
+isl::set set::flatten() const
+{
+ auto res = isl_set_flatten(copy());
+ return manage(res);
+}
+
+stat set::foreach_basic_set(const std::function<stat(isl::basic_set)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::basic_set)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_basic_set *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_set_foreach_basic_set(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+stat set::foreach_point(const std::function<stat(isl::point)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::point)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_point *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_set_foreach_point(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+stat set::foreach_set(const std::function<stat(isl::set)> &fn) const
+{
+ return isl::union_set(*this).foreach_set(fn);
+}
+
+isl::set set::gist(isl::set context) const
+{
+ auto res = isl_set_gist(copy(), context.release());
+ return manage(res);
+}
+
+isl::union_set set::gist(const isl::union_set &context) const
+{
+ return isl::union_set(*this).gist(context);
+}
+
+isl::set set::gist(const isl::basic_set &context) const
+{
+ return this->gist(isl::set(context));
+}
+
+isl::set set::gist(const isl::point &context) const
+{
+ return this->gist(isl::set(context));
+}
+
+isl::set set::gist_params(isl::set context) const
+{
+ auto res = isl_set_gist_params(copy(), context.release());
+ return manage(res);
+}
+
+boolean set::has_equal_space(const isl::set &set2) const
+{
+ auto res = isl_set_has_equal_space(get(), set2.get());
+ return manage(res);
+}
+
+isl::map set::identity() const
+{
+ auto res = isl_set_identity(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff set::identity_union_pw_multi_aff() const
+{
+ return isl::union_set(*this).identity_union_pw_multi_aff();
+}
+
+isl::pw_aff set::indicator_function() const
+{
+ auto res = isl_set_indicator_function(copy());
+ return manage(res);
+}
+
+isl::set set::insert_dims(isl::dim type, unsigned int pos, unsigned int n) const
+{
+ auto res = isl_set_insert_dims(copy(), static_cast<enum isl_dim_type>(type), pos, n);
+ return manage(res);
+}
+
+isl::map set::insert_domain(isl::space domain) const
+{
+ auto res = isl_set_insert_domain(copy(), domain.release());
+ return manage(res);
+}
+
+isl::set set::intersect(isl::set set2) const
+{
+ auto res = isl_set_intersect(copy(), set2.release());
+ return manage(res);
+}
+
+isl::union_set set::intersect(const isl::union_set &uset2) const
+{
+ return isl::union_set(*this).intersect(uset2);
+}
+
+isl::set set::intersect(const isl::basic_set &set2) const
+{
+ return this->intersect(isl::set(set2));
+}
+
+isl::set set::intersect(const isl::point &set2) const
+{
+ return this->intersect(isl::set(set2));
+}
+
+isl::set set::intersect_params(isl::set params) const
+{
+ auto res = isl_set_intersect_params(copy(), params.release());
+ return manage(res);
+}
+
+boolean set::involves_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ auto res = isl_set_involves_dims(get(), static_cast<enum isl_dim_type>(type), first, n);
+ return manage(res);
+}
+
+boolean set::involves_locals() const
+{
+ auto res = isl_set_involves_locals(get());
+ return manage(res);
+}
+
+boolean set::is_bounded() const
+{
+ auto res = isl_set_is_bounded(get());
+ return manage(res);
+}
+
+boolean set::is_disjoint(const isl::set &set2) const
+{
+ auto res = isl_set_is_disjoint(get(), set2.get());
+ return manage(res);
+}
+
+boolean set::is_disjoint(const isl::union_set &uset2) const
+{
+ return isl::union_set(*this).is_disjoint(uset2);
+}
+
+boolean set::is_disjoint(const isl::basic_set &set2) const
+{
+ return this->is_disjoint(isl::set(set2));
+}
+
+boolean set::is_disjoint(const isl::point &set2) const
+{
+ return this->is_disjoint(isl::set(set2));
+}
+
+boolean set::is_empty() const
+{
+ auto res = isl_set_is_empty(get());
+ return manage(res);
+}
+
+boolean set::is_equal(const isl::set &set2) const
+{
+ auto res = isl_set_is_equal(get(), set2.get());
+ return manage(res);
+}
+
+boolean set::is_equal(const isl::union_set &uset2) const
+{
+ return isl::union_set(*this).is_equal(uset2);
+}
+
+boolean set::is_equal(const isl::basic_set &set2) const
+{
+ return this->is_equal(isl::set(set2));
+}
+
+boolean set::is_equal(const isl::point &set2) const
+{
+ return this->is_equal(isl::set(set2));
+}
+
+boolean set::is_params() const
+{
+ auto res = isl_set_is_params(get());
+ return manage(res);
+}
+
+boolean set::is_singleton() const
+{
+ auto res = isl_set_is_singleton(get());
+ return manage(res);
+}
+
+boolean set::is_strict_subset(const isl::set &set2) const
+{
+ auto res = isl_set_is_strict_subset(get(), set2.get());
+ return manage(res);
+}
+
+boolean set::is_strict_subset(const isl::union_set &uset2) const
+{
+ return isl::union_set(*this).is_strict_subset(uset2);
+}
+
+boolean set::is_strict_subset(const isl::basic_set &set2) const
+{
+ return this->is_strict_subset(isl::set(set2));
+}
+
+boolean set::is_strict_subset(const isl::point &set2) const
+{
+ return this->is_strict_subset(isl::set(set2));
+}
+
+boolean set::is_subset(const isl::set &set2) const
+{
+ auto res = isl_set_is_subset(get(), set2.get());
+ return manage(res);
+}
+
+boolean set::is_subset(const isl::union_set &uset2) const
+{
+ return isl::union_set(*this).is_subset(uset2);
+}
+
+boolean set::is_subset(const isl::basic_set &set2) const
+{
+ return this->is_subset(isl::set(set2));
+}
+
+boolean set::is_subset(const isl::point &set2) const
+{
+ return this->is_subset(isl::set(set2));
+}
+
+boolean set::is_wrapping() const
+{
+ auto res = isl_set_is_wrapping(get());
+ return manage(res);
+}
+
+boolean set::isa_set() const
+{
+ return isl::union_set(*this).isa_set();
+}
+
+isl::set set::lexmax() const
+{
+ auto res = isl_set_lexmax(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff set::lexmax_pw_multi_aff() const
+{
+ auto res = isl_set_lexmax_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::set set::lexmin() const
+{
+ auto res = isl_set_lexmin(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff set::lexmin_pw_multi_aff() const
+{
+ auto res = isl_set_lexmin_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::set set::lower_bound(isl::multi_pw_aff lower) const
+{
+ auto res = isl_set_lower_bound_multi_pw_aff(copy(), lower.release());
+ return manage(res);
+}
+
+isl::set set::lower_bound(isl::multi_val lower) const
+{
+ auto res = isl_set_lower_bound_multi_val(copy(), lower.release());
+ return manage(res);
+}
+
+isl::set set::lower_bound_si(isl::dim type, unsigned int pos, int value) const
+{
+ auto res = isl_set_lower_bound_si(copy(), static_cast<enum isl_dim_type>(type), pos, value);
+ return manage(res);
+}
+
+isl::set set::lower_bound_val(isl::dim type, unsigned int pos, isl::val value) const
+{
+ auto res = isl_set_lower_bound_val(copy(), static_cast<enum isl_dim_type>(type), pos, value.release());
+ return manage(res);
+}
+
+isl::set set::lower_bound_val(isl::dim type, unsigned int pos, long value) const
+{
+ return this->lower_bound_val(type, pos, isl::val(ctx(), value));
+}
+
+isl::multi_pw_aff set::max_multi_pw_aff() const
+{
+ auto res = isl_set_max_multi_pw_aff(copy());
+ return manage(res);
+}
+
+isl::val set::max_val(const isl::aff &obj) const
+{
+ auto res = isl_set_max_val(get(), obj.get());
+ return manage(res);
+}
+
+isl::multi_pw_aff set::min_multi_pw_aff() const
+{
+ auto res = isl_set_min_multi_pw_aff(copy());
+ return manage(res);
+}
+
+isl::val set::min_val(const isl::aff &obj) const
+{
+ auto res = isl_set_min_val(get(), obj.get());
+ return manage(res);
+}
+
+class size set::n_basic_set() const
+{
+ auto res = isl_set_n_basic_set(get());
+ return manage(res);
+}
+
+isl::set set::params() const
+{
+ auto res = isl_set_params(copy());
+ return manage(res);
+}
+
+isl::val set::plain_get_val_if_fixed(isl::dim type, unsigned int pos) const
+{
+ auto res = isl_set_plain_get_val_if_fixed(get(), static_cast<enum isl_dim_type>(type), pos);
+ return manage(res);
+}
+
+isl::multi_val set::plain_multi_val_if_fixed() const
+{
+ auto res = isl_set_get_plain_multi_val_if_fixed(get());
+ return manage(res);
+}
+
+isl::multi_val set::get_plain_multi_val_if_fixed() const
+{
+ return plain_multi_val_if_fixed();
+}
+
+isl::basic_set set::polyhedral_hull() const
+{
+ auto res = isl_set_polyhedral_hull(copy());
+ return manage(res);
+}
+
+isl::set set::preimage(isl::multi_aff ma) const
+{
+ auto res = isl_set_preimage_multi_aff(copy(), ma.release());
+ return manage(res);
+}
+
+isl::set set::preimage(isl::multi_pw_aff mpa) const
+{
+ auto res = isl_set_preimage_multi_pw_aff(copy(), mpa.release());
+ return manage(res);
+}
+
+isl::set set::preimage(isl::pw_multi_aff pma) const
+{
+ auto res = isl_set_preimage_pw_multi_aff(copy(), pma.release());
+ return manage(res);
+}
+
+isl::union_set set::preimage(const isl::union_pw_multi_aff &upma) const
+{
+ return isl::union_set(*this).preimage(upma);
+}
+
+isl::set set::product(isl::set set2) const
+{
+ auto res = isl_set_product(copy(), set2.release());
+ return manage(res);
+}
+
+isl::set set::project_out(isl::dim type, unsigned int first, unsigned int n) const
+{
+ auto res = isl_set_project_out(copy(), static_cast<enum isl_dim_type>(type), first, n);
+ return manage(res);
+}
+
+isl::set set::project_out_all_params() const
+{
+ auto res = isl_set_project_out_all_params(copy());
+ return manage(res);
+}
+
+isl::set set::project_out_param(isl::id id) const
+{
+ auto res = isl_set_project_out_param_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::set set::project_out_param(const std::string &id) const
+{
+ return this->project_out_param(isl::id(ctx(), id));
+}
+
+isl::set set::project_out_param(isl::id_list list) const
+{
+ auto res = isl_set_project_out_param_id_list(copy(), list.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff set::pw_multi_aff_on_domain(isl::multi_val mv) const
+{
+ auto res = isl_set_pw_multi_aff_on_domain_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::set set::remove_dims(isl::dim type, unsigned int first, unsigned int n) const
+{
+ auto res = isl_set_remove_dims(copy(), static_cast<enum isl_dim_type>(type), first, n);
+ return manage(res);
+}
+
+isl::set set::remove_divs() const
+{
+ auto res = isl_set_remove_divs(copy());
+ return manage(res);
+}
+
+isl::set set::remove_redundancies() const
+{
+ auto res = isl_set_remove_redundancies(copy());
+ return manage(res);
+}
+
+isl::set set::reset_tuple_id() const
+{
+ auto res = isl_set_reset_tuple_id(copy());
+ return manage(res);
+}
+
+isl::basic_set set::sample() const
+{
+ auto res = isl_set_sample(copy());
+ return manage(res);
+}
+
+isl::point set::sample_point() const
+{
+ auto res = isl_set_sample_point(copy());
+ return manage(res);
+}
+
+isl::set set::set_dim_id(isl::dim type, unsigned int pos, isl::id id) const
+{
+ auto res = isl_set_set_dim_id(copy(), static_cast<enum isl_dim_type>(type), pos, id.release());
+ return manage(res);
+}
+
+isl::set set::set_dim_id(isl::dim type, unsigned int pos, const std::string &id) const
+{
+ return this->set_dim_id(type, pos, isl::id(ctx(), id));
+}
+
+isl::set_list set::set_list() const
+{
+ return isl::union_set(*this).set_list();
+}
+
+isl::set set::set_tuple_id(isl::id id) const
+{
+ auto res = isl_set_set_tuple_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::set set::set_tuple_id(const std::string &id) const
+{
+ return this->set_tuple_id(isl::id(ctx(), id));
+}
+
+isl::fixed_box set::simple_fixed_box_hull() const
+{
+ auto res = isl_set_get_simple_fixed_box_hull(get());
+ return manage(res);
+}
+
+isl::fixed_box set::get_simple_fixed_box_hull() const
+{
+ return simple_fixed_box_hull();
+}
+
+isl::basic_set set::simple_hull() const
+{
+ auto res = isl_set_simple_hull(copy());
+ return manage(res);
+}
+
+isl::space set::space() const
+{
+ auto res = isl_set_get_space(get());
+ return manage(res);
+}
+
+isl::space set::get_space() const
+{
+ return space();
+}
+
+isl::val set::stride(int pos) const
+{
+ auto res = isl_set_get_stride(get(), pos);
+ return manage(res);
+}
+
+isl::val set::get_stride(int pos) const
+{
+ return stride(pos);
+}
+
+isl::set set::subtract(isl::set set2) const
+{
+ auto res = isl_set_subtract(copy(), set2.release());
+ return manage(res);
+}
+
+isl::union_set set::subtract(const isl::union_set &uset2) const
+{
+ return isl::union_set(*this).subtract(uset2);
+}
+
+isl::set set::subtract(const isl::basic_set &set2) const
+{
+ return this->subtract(isl::set(set2));
+}
+
+isl::set set::subtract(const isl::point &set2) const
+{
+ return this->subtract(isl::set(set2));
+}
+
+isl::set_list set::to_list() const
+{
+ auto res = isl_set_to_list(copy());
+ return manage(res);
+}
+
+isl::union_set set::to_union_set() const
+{
+ auto res = isl_set_to_union_set(copy());
+ return manage(res);
+}
+
+isl::map set::translation() const
+{
+ auto res = isl_set_translation(copy());
+ return manage(res);
+}
+
+class size set::tuple_dim() const
+{
+ auto res = isl_set_tuple_dim(get());
+ return manage(res);
+}
+
+isl::id set::tuple_id() const
+{
+ auto res = isl_set_get_tuple_id(get());
+ return manage(res);
+}
+
+isl::id set::get_tuple_id() const
+{
+ return tuple_id();
+}
+
+std::string set::tuple_name() const
+{
+ auto res = isl_set_get_tuple_name(get());
+ std::string tmp(res);
+ return tmp;
+}
+
+std::string set::get_tuple_name() const
+{
+ return tuple_name();
+}
+
+isl::set set::unbind_params(isl::multi_id tuple) const
+{
+ auto res = isl_set_unbind_params(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::map set::unbind_params_insert_domain(isl::multi_id domain) const
+{
+ auto res = isl_set_unbind_params_insert_domain(copy(), domain.release());
+ return manage(res);
+}
+
+isl::set set::unite(isl::set set2) const
+{
+ auto res = isl_set_union(copy(), set2.release());
+ return manage(res);
+}
+
+isl::union_set set::unite(const isl::union_set &uset2) const
+{
+ return isl::union_set(*this).unite(uset2);
+}
+
+isl::set set::unite(const isl::basic_set &set2) const
+{
+ return this->unite(isl::set(set2));
+}
+
+isl::set set::unite(const isl::point &set2) const
+{
+ return this->unite(isl::set(set2));
+}
+
+isl::set set::universe(isl::space space)
+{
+ auto res = isl_set_universe(space.release());
+ return manage(res);
+}
+
+isl::basic_set set::unshifted_simple_hull() const
+{
+ auto res = isl_set_unshifted_simple_hull(copy());
+ return manage(res);
+}
+
+isl::map set::unwrap() const
+{
+ auto res = isl_set_unwrap(copy());
+ return manage(res);
+}
+
+isl::set set::upper_bound(isl::multi_pw_aff upper) const
+{
+ auto res = isl_set_upper_bound_multi_pw_aff(copy(), upper.release());
+ return manage(res);
+}
+
+isl::set set::upper_bound(isl::multi_val upper) const
+{
+ auto res = isl_set_upper_bound_multi_val(copy(), upper.release());
+ return manage(res);
+}
+
+isl::set set::upper_bound_val(isl::dim type, unsigned int pos, isl::val value) const
+{
+ auto res = isl_set_upper_bound_val(copy(), static_cast<enum isl_dim_type>(type), pos, value.release());
+ return manage(res);
+}
+
+isl::set set::upper_bound_val(isl::dim type, unsigned int pos, long value) const
+{
+ return this->upper_bound_val(type, pos, isl::val(ctx(), value));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const set &obj)
+{
+ char *str = isl_set_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::set_list
+set_list manage(__isl_take isl_set_list *ptr) {
+ return set_list(ptr);
+}
+set_list manage_copy(__isl_keep isl_set_list *ptr) {
+ ptr = isl_set_list_copy(ptr);
+ return set_list(ptr);
+}
+
+set_list::set_list()
+ : ptr(nullptr) {}
+
+set_list::set_list(const set_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+set_list::set_list(__isl_take isl_set_list *ptr)
+ : ptr(ptr) {}
+
+set_list::set_list(isl::ctx ctx, int n)
+{
+ auto res = isl_set_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+set_list::set_list(isl::set el)
+{
+ auto res = isl_set_list_from_set(el.release());
+ ptr = res;
+}
+
+set_list::set_list(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_set_list_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+set_list &set_list::operator=(set_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+set_list::~set_list() {
+ if (ptr)
+ isl_set_list_free(ptr);
+}
+
+__isl_give isl_set_list *set_list::copy() const & {
+ return isl_set_list_copy(ptr);
+}
+
+__isl_keep isl_set_list *set_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_set_list *set_list::release() {
+ isl_set_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool set_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx set_list::ctx() const {
+ return isl::ctx(isl_set_list_get_ctx(ptr));
+}
+
+isl::set_list set_list::add(isl::set el) const
+{
+ auto res = isl_set_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::set set_list::at(int index) const
+{
+ auto res = isl_set_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::set set_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::set_list set_list::clear() const
+{
+ auto res = isl_set_list_clear(copy());
+ return manage(res);
+}
+
+isl::set_list set_list::concat(isl::set_list list2) const
+{
+ auto res = isl_set_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::set_list set_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_set_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat set_list::foreach(const std::function<stat(isl::set)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::set)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_set *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_set_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::set_list set_list::insert(unsigned int pos, isl::set el) const
+{
+ auto res = isl_set_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+class size set_list::size() const
+{
+ auto res = isl_set_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const set_list &obj)
+{
+ char *str = isl_set_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::space
+space manage(__isl_take isl_space *ptr) {
+ return space(ptr);
+}
+space manage_copy(__isl_keep isl_space *ptr) {
+ ptr = isl_space_copy(ptr);
+ return space(ptr);
+}
+
+space::space()
+ : ptr(nullptr) {}
+
+space::space(const space &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+space::space(__isl_take isl_space *ptr)
+ : ptr(ptr) {}
+
+space::space(isl::ctx ctx, unsigned int nparam, unsigned int n_in, unsigned int n_out)
+{
+ auto res = isl_space_alloc(ctx.release(), nparam, n_in, n_out);
+ ptr = res;
+}
+
+space::space(isl::ctx ctx, unsigned int nparam, unsigned int dim)
+{
+ auto res = isl_space_set_alloc(ctx.release(), nparam, dim);
+ ptr = res;
+}
+
+space &space::operator=(space obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+space::~space() {
+ if (ptr)
+ isl_space_free(ptr);
+}
+
+__isl_give isl_space *space::copy() const & {
+ return isl_space_copy(ptr);
+}
+
+__isl_keep isl_space *space::get() const {
+ return ptr;
+}
+
+__isl_give isl_space *space::release() {
+ isl_space *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool space::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx space::ctx() const {
+ return isl::ctx(isl_space_get_ctx(ptr));
+}
+
+isl::space space::add_dims(isl::dim type, unsigned int n) const
+{
+ auto res = isl_space_add_dims(copy(), static_cast<enum isl_dim_type>(type), n);
+ return manage(res);
+}
+
+isl::space space::add_named_tuple(isl::id tuple_id, unsigned int dim) const
+{
+ auto res = isl_space_add_named_tuple_id_ui(copy(), tuple_id.release(), dim);
+ return manage(res);
+}
+
+isl::space space::add_named_tuple(const std::string &tuple_id, unsigned int dim) const
+{
+ return this->add_named_tuple(isl::id(ctx(), tuple_id), dim);
+}
+
+isl::space space::add_param(isl::id id) const
+{
+ auto res = isl_space_add_param_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::space space::add_param(const std::string &id) const
+{
+ return this->add_param(isl::id(ctx(), id));
+}
+
+isl::space space::add_unnamed_tuple(unsigned int dim) const
+{
+ auto res = isl_space_add_unnamed_tuple_ui(copy(), dim);
+ return manage(res);
+}
+
+isl::space space::align_params(isl::space space2) const
+{
+ auto res = isl_space_align_params(copy(), space2.release());
+ return manage(res);
+}
+
+isl::space space::curry() const
+{
+ auto res = isl_space_curry(copy());
+ return manage(res);
+}
+
+class size space::dim(isl::dim type) const
+{
+ auto res = isl_space_dim(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::id space::dim_id(isl::dim type, unsigned int pos) const
+{
+ auto res = isl_space_get_dim_id(get(), static_cast<enum isl_dim_type>(type), pos);
+ return manage(res);
+}
+
+isl::id space::get_dim_id(isl::dim type, unsigned int pos) const
+{
+ return dim_id(type, pos);
+}
+
+isl::space space::domain() const
+{
+ auto res = isl_space_domain(copy());
+ return manage(res);
+}
+
+isl::multi_aff space::domain_map_multi_aff() const
+{
+ auto res = isl_space_domain_map_multi_aff(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff space::domain_map_pw_multi_aff() const
+{
+ auto res = isl_space_domain_map_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::id space::domain_tuple_id() const
+{
+ auto res = isl_space_get_domain_tuple_id(get());
+ return manage(res);
+}
+
+isl::id space::get_domain_tuple_id() const
+{
+ return domain_tuple_id();
+}
+
+isl::space space::drop_dims(isl::dim type, unsigned int first, unsigned int num) const
+{
+ auto res = isl_space_drop_dims(copy(), static_cast<enum isl_dim_type>(type), first, num);
+ return manage(res);
+}
+
+int space::find_dim_by_id(isl::dim type, const isl::id &id) const
+{
+ auto res = isl_space_find_dim_by_id(get(), static_cast<enum isl_dim_type>(type), id.get());
+ return res;
+}
+
+int space::find_dim_by_id(isl::dim type, const std::string &id) const
+{
+ return this->find_dim_by_id(type, isl::id(ctx(), id));
+}
+
+isl::space space::flatten_domain() const
+{
+ auto res = isl_space_flatten_domain(copy());
+ return manage(res);
+}
+
+isl::space space::flatten_range() const
+{
+ auto res = isl_space_flatten_range(copy());
+ return manage(res);
+}
+
+boolean space::has_domain_tuple_id() const
+{
+ auto res = isl_space_has_domain_tuple_id(get());
+ return manage(res);
+}
+
+boolean space::has_equal_tuples(const isl::space &space2) const
+{
+ auto res = isl_space_has_equal_tuples(get(), space2.get());
+ return manage(res);
+}
+
+boolean space::has_range_tuple_id() const
+{
+ auto res = isl_space_has_range_tuple_id(get());
+ return manage(res);
+}
+
+boolean space::has_tuple_id(isl::dim type) const
+{
+ auto res = isl_space_has_tuple_id(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+boolean space::has_tuple_name(isl::dim type) const
+{
+ auto res = isl_space_has_tuple_name(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::multi_aff space::identity_multi_aff_on_domain() const
+{
+ auto res = isl_space_identity_multi_aff_on_domain(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff space::identity_multi_pw_aff_on_domain() const
+{
+ auto res = isl_space_identity_multi_pw_aff_on_domain(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff space::identity_pw_multi_aff_on_domain() const
+{
+ auto res = isl_space_identity_pw_multi_aff_on_domain(copy());
+ return manage(res);
+}
+
+boolean space::is_equal(const isl::space &space2) const
+{
+ auto res = isl_space_is_equal(get(), space2.get());
+ return manage(res);
+}
+
+boolean space::is_params() const
+{
+ auto res = isl_space_is_params(get());
+ return manage(res);
+}
+
+boolean space::is_set() const
+{
+ auto res = isl_space_is_set(get());
+ return manage(res);
+}
+
+boolean space::is_wrapping() const
+{
+ auto res = isl_space_is_wrapping(get());
+ return manage(res);
+}
+
+isl::space space::map_from_domain_and_range(isl::space range) const
+{
+ auto res = isl_space_map_from_domain_and_range(copy(), range.release());
+ return manage(res);
+}
+
+isl::space space::map_from_set() const
+{
+ auto res = isl_space_map_from_set(copy());
+ return manage(res);
+}
+
+isl::multi_aff space::multi_aff(isl::aff_list list) const
+{
+ auto res = isl_space_multi_aff(copy(), list.release());
+ return manage(res);
+}
+
+isl::multi_aff space::multi_aff_on_domain(isl::multi_val mv) const
+{
+ auto res = isl_space_multi_aff_on_domain_multi_val(copy(), mv.release());
+ return manage(res);
+}
+
+isl::multi_id space::multi_id(isl::id_list list) const
+{
+ auto res = isl_space_multi_id(copy(), list.release());
+ return manage(res);
+}
+
+isl::multi_pw_aff space::multi_pw_aff(isl::pw_aff_list list) const
+{
+ auto res = isl_space_multi_pw_aff(copy(), list.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff space::multi_union_pw_aff(isl::union_pw_aff_list list) const
+{
+ auto res = isl_space_multi_union_pw_aff(copy(), list.release());
+ return manage(res);
+}
+
+isl::multi_val space::multi_val(isl::val_list list) const
+{
+ auto res = isl_space_multi_val(copy(), list.release());
+ return manage(res);
+}
+
+isl::aff space::param_aff_on_domain(isl::id id) const
+{
+ auto res = isl_space_param_aff_on_domain_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::aff space::param_aff_on_domain(const std::string &id) const
+{
+ return this->param_aff_on_domain(isl::id(ctx(), id));
+}
+
+isl::space space::params() const
+{
+ auto res = isl_space_params(copy());
+ return manage(res);
+}
+
+isl::space space::params_alloc(isl::ctx ctx, unsigned int nparam)
+{
+ auto res = isl_space_params_alloc(ctx.release(), nparam);
+ return manage(res);
+}
+
+isl::space space::product(isl::space right) const
+{
+ auto res = isl_space_product(copy(), right.release());
+ return manage(res);
+}
+
+isl::space space::range() const
+{
+ auto res = isl_space_range(copy());
+ return manage(res);
+}
+
+isl::multi_aff space::range_map_multi_aff() const
+{
+ auto res = isl_space_range_map_multi_aff(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff space::range_map_pw_multi_aff() const
+{
+ auto res = isl_space_range_map_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::space space::range_reverse() const
+{
+ auto res = isl_space_range_reverse(copy());
+ return manage(res);
+}
+
+isl::id space::range_tuple_id() const
+{
+ auto res = isl_space_get_range_tuple_id(get());
+ return manage(res);
+}
+
+isl::id space::get_range_tuple_id() const
+{
+ return range_tuple_id();
+}
+
+isl::space space::reverse() const
+{
+ auto res = isl_space_reverse(copy());
+ return manage(res);
+}
+
+isl::space space::set_dim_id(isl::dim type, unsigned int pos, isl::id id) const
+{
+ auto res = isl_space_set_dim_id(copy(), static_cast<enum isl_dim_type>(type), pos, id.release());
+ return manage(res);
+}
+
+isl::space space::set_dim_id(isl::dim type, unsigned int pos, const std::string &id) const
+{
+ return this->set_dim_id(type, pos, isl::id(ctx(), id));
+}
+
+isl::space space::set_domain_tuple(isl::id id) const
+{
+ auto res = isl_space_set_domain_tuple_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::space space::set_domain_tuple(const std::string &id) const
+{
+ return this->set_domain_tuple(isl::id(ctx(), id));
+}
+
+isl::space space::set_from_params() const
+{
+ auto res = isl_space_set_from_params(copy());
+ return manage(res);
+}
+
+isl::space space::set_range_tuple(isl::id id) const
+{
+ auto res = isl_space_set_range_tuple_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::space space::set_range_tuple(const std::string &id) const
+{
+ return this->set_range_tuple(isl::id(ctx(), id));
+}
+
+isl::space space::set_tuple_id(isl::dim type, isl::id id) const
+{
+ auto res = isl_space_set_tuple_id(copy(), static_cast<enum isl_dim_type>(type), id.release());
+ return manage(res);
+}
+
+isl::space space::set_tuple_id(isl::dim type, const std::string &id) const
+{
+ return this->set_tuple_id(type, isl::id(ctx(), id));
+}
+
+isl::id space::tuple_id(isl::dim type) const
+{
+ auto res = isl_space_get_tuple_id(get(), static_cast<enum isl_dim_type>(type));
+ return manage(res);
+}
+
+isl::id space::get_tuple_id(isl::dim type) const
+{
+ return tuple_id(type);
+}
+
+std::string space::tuple_name(isl::dim type) const
+{
+ auto res = isl_space_get_tuple_name(get(), static_cast<enum isl_dim_type>(type));
+ std::string tmp(res);
+ return tmp;
+}
+
+std::string space::get_tuple_name(isl::dim type) const
+{
+ return tuple_name(type);
+}
+
+isl::space space::uncurry() const
+{
+ auto res = isl_space_uncurry(copy());
+ return manage(res);
+}
+
+isl::space space::unit(isl::ctx ctx)
+{
+ auto res = isl_space_unit(ctx.release());
+ return manage(res);
+}
+
+isl::map space::universe_map() const
+{
+ auto res = isl_space_universe_map(copy());
+ return manage(res);
+}
+
+isl::set space::universe_set() const
+{
+ auto res = isl_space_universe_set(copy());
+ return manage(res);
+}
+
+isl::space space::unwrap() const
+{
+ auto res = isl_space_unwrap(copy());
+ return manage(res);
+}
+
+isl::space space::wrap() const
+{
+ auto res = isl_space_wrap(copy());
+ return manage(res);
+}
+
+isl::aff space::zero_aff_on_domain() const
+{
+ auto res = isl_space_zero_aff_on_domain(copy());
+ return manage(res);
+}
+
+isl::multi_aff space::zero_multi_aff() const
+{
+ auto res = isl_space_zero_multi_aff(copy());
+ return manage(res);
+}
+
+isl::multi_pw_aff space::zero_multi_pw_aff() const
+{
+ auto res = isl_space_zero_multi_pw_aff(copy());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff space::zero_multi_union_pw_aff() const
+{
+ auto res = isl_space_zero_multi_union_pw_aff(copy());
+ return manage(res);
+}
+
+isl::multi_val space::zero_multi_val() const
+{
+ auto res = isl_space_zero_multi_val(copy());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const space &obj)
+{
+ char *str = isl_space_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::union_access_info
+union_access_info manage(__isl_take isl_union_access_info *ptr) {
+ return union_access_info(ptr);
+}
+union_access_info manage_copy(__isl_keep isl_union_access_info *ptr) {
+ ptr = isl_union_access_info_copy(ptr);
+ return union_access_info(ptr);
+}
+
+union_access_info::union_access_info()
+ : ptr(nullptr) {}
+
+union_access_info::union_access_info(const union_access_info &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+union_access_info::union_access_info(__isl_take isl_union_access_info *ptr)
+ : ptr(ptr) {}
+
+union_access_info::union_access_info(isl::union_map sink)
+{
+ auto res = isl_union_access_info_from_sink(sink.release());
+ ptr = res;
+}
+
+union_access_info &union_access_info::operator=(union_access_info obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+union_access_info::~union_access_info() {
+ if (ptr)
+ isl_union_access_info_free(ptr);
+}
+
+__isl_give isl_union_access_info *union_access_info::copy() const & {
+ return isl_union_access_info_copy(ptr);
+}
+
+__isl_keep isl_union_access_info *union_access_info::get() const {
+ return ptr;
+}
+
+__isl_give isl_union_access_info *union_access_info::release() {
+ isl_union_access_info *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool union_access_info::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx union_access_info::ctx() const {
+ return isl::ctx(isl_union_access_info_get_ctx(ptr));
+}
+
+isl::union_flow union_access_info::compute_flow() const
+{
+ auto res = isl_union_access_info_compute_flow(copy());
+ return manage(res);
+}
+
+isl::union_access_info union_access_info::set_kill(isl::union_map kill) const
+{
+ auto res = isl_union_access_info_set_kill(copy(), kill.release());
+ return manage(res);
+}
+
+isl::union_access_info union_access_info::set_may_source(isl::union_map may_source) const
+{
+ auto res = isl_union_access_info_set_may_source(copy(), may_source.release());
+ return manage(res);
+}
+
+isl::union_access_info union_access_info::set_must_source(isl::union_map must_source) const
+{
+ auto res = isl_union_access_info_set_must_source(copy(), must_source.release());
+ return manage(res);
+}
+
+isl::union_access_info union_access_info::set_schedule(isl::schedule schedule) const
+{
+ auto res = isl_union_access_info_set_schedule(copy(), schedule.release());
+ return manage(res);
+}
+
+isl::union_access_info union_access_info::set_schedule_map(isl::union_map schedule_map) const
+{
+ auto res = isl_union_access_info_set_schedule_map(copy(), schedule_map.release());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const union_access_info &obj)
+{
+ char *str = isl_union_access_info_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::union_flow
+union_flow manage(__isl_take isl_union_flow *ptr) {
+ return union_flow(ptr);
+}
+union_flow manage_copy(__isl_keep isl_union_flow *ptr) {
+ ptr = isl_union_flow_copy(ptr);
+ return union_flow(ptr);
+}
+
+union_flow::union_flow()
+ : ptr(nullptr) {}
+
+union_flow::union_flow(const union_flow &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+union_flow::union_flow(__isl_take isl_union_flow *ptr)
+ : ptr(ptr) {}
+
+union_flow &union_flow::operator=(union_flow obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+union_flow::~union_flow() {
+ if (ptr)
+ isl_union_flow_free(ptr);
+}
+
+__isl_give isl_union_flow *union_flow::copy() const & {
+ return isl_union_flow_copy(ptr);
+}
+
+__isl_keep isl_union_flow *union_flow::get() const {
+ return ptr;
+}
+
+__isl_give isl_union_flow *union_flow::release() {
+ isl_union_flow *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool union_flow::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx union_flow::ctx() const {
+ return isl::ctx(isl_union_flow_get_ctx(ptr));
+}
+
+isl::union_map union_flow::full_may_dependence() const
+{
+ auto res = isl_union_flow_get_full_may_dependence(get());
+ return manage(res);
+}
+
+isl::union_map union_flow::get_full_may_dependence() const
+{
+ return full_may_dependence();
+}
+
+isl::union_map union_flow::full_must_dependence() const
+{
+ auto res = isl_union_flow_get_full_must_dependence(get());
+ return manage(res);
+}
+
+isl::union_map union_flow::get_full_must_dependence() const
+{
+ return full_must_dependence();
+}
+
+isl::union_map union_flow::may_dependence() const
+{
+ auto res = isl_union_flow_get_may_dependence(get());
+ return manage(res);
+}
+
+isl::union_map union_flow::get_may_dependence() const
+{
+ return may_dependence();
+}
+
+isl::union_map union_flow::may_no_source() const
+{
+ auto res = isl_union_flow_get_may_no_source(get());
+ return manage(res);
+}
+
+isl::union_map union_flow::get_may_no_source() const
+{
+ return may_no_source();
+}
+
+isl::union_map union_flow::must_dependence() const
+{
+ auto res = isl_union_flow_get_must_dependence(get());
+ return manage(res);
+}
+
+isl::union_map union_flow::get_must_dependence() const
+{
+ return must_dependence();
+}
+
+isl::union_map union_flow::must_no_source() const
+{
+ auto res = isl_union_flow_get_must_no_source(get());
+ return manage(res);
+}
+
+isl::union_map union_flow::get_must_no_source() const
+{
+ return must_no_source();
+}
+
+inline std::ostream &operator<<(std::ostream &os, const union_flow &obj)
+{
+ char *str = isl_union_flow_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::union_map
+union_map manage(__isl_take isl_union_map *ptr) {
+ return union_map(ptr);
+}
+union_map manage_copy(__isl_keep isl_union_map *ptr) {
+ ptr = isl_union_map_copy(ptr);
+ return union_map(ptr);
+}
+
+union_map::union_map()
+ : ptr(nullptr) {}
+
+union_map::union_map(const union_map &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+union_map::union_map(__isl_take isl_union_map *ptr)
+ : ptr(ptr) {}
+
+union_map::union_map(isl::basic_map bmap)
+{
+ auto res = isl_union_map_from_basic_map(bmap.release());
+ ptr = res;
+}
+
+union_map::union_map(isl::map map)
+{
+ auto res = isl_union_map_from_map(map.release());
+ ptr = res;
+}
+
+union_map::union_map(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_union_map_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+union_map &union_map::operator=(union_map obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+union_map::~union_map() {
+ if (ptr)
+ isl_union_map_free(ptr);
+}
+
+__isl_give isl_union_map *union_map::copy() const & {
+ return isl_union_map_copy(ptr);
+}
+
+__isl_keep isl_union_map *union_map::get() const {
+ return ptr;
+}
+
+__isl_give isl_union_map *union_map::release() {
+ isl_union_map *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool union_map::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx union_map::ctx() const {
+ return isl::ctx(isl_union_map_get_ctx(ptr));
+}
+
+isl::union_map union_map::affine_hull() const
+{
+ auto res = isl_union_map_affine_hull(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::apply_domain(isl::union_map umap2) const
+{
+ auto res = isl_union_map_apply_domain(copy(), umap2.release());
+ return manage(res);
+}
+
+isl::union_map union_map::apply_range(isl::union_map umap2) const
+{
+ auto res = isl_union_map_apply_range(copy(), umap2.release());
+ return manage(res);
+}
+
+isl::map union_map::as_map() const
+{
+ auto res = isl_union_map_as_map(copy());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff union_map::as_multi_union_pw_aff() const
+{
+ auto res = isl_union_map_as_multi_union_pw_aff(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_map::as_union_pw_multi_aff() const
+{
+ auto res = isl_union_map_as_union_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::union_set union_map::bind_range(isl::multi_id tuple) const
+{
+ auto res = isl_union_map_bind_range(copy(), tuple.release());
+ return manage(res);
+}
+
+isl::union_map union_map::coalesce() const
+{
+ auto res = isl_union_map_coalesce(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::compute_divs() const
+{
+ auto res = isl_union_map_compute_divs(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::curry() const
+{
+ auto res = isl_union_map_curry(copy());
+ return manage(res);
+}
+
+isl::union_set union_map::deltas() const
+{
+ auto res = isl_union_map_deltas(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::detect_equalities() const
+{
+ auto res = isl_union_map_detect_equalities(copy());
+ return manage(res);
+}
+
+isl::union_set union_map::domain() const
+{
+ auto res = isl_union_map_domain(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::domain_factor_domain() const
+{
+ auto res = isl_union_map_domain_factor_domain(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::domain_factor_range() const
+{
+ auto res = isl_union_map_domain_factor_range(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::domain_map() const
+{
+ auto res = isl_union_map_domain_map(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_map::domain_map_union_pw_multi_aff() const
+{
+ auto res = isl_union_map_domain_map_union_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::domain_product(isl::union_map umap2) const
+{
+ auto res = isl_union_map_domain_product(copy(), umap2.release());
+ return manage(res);
+}
+
+isl::union_map union_map::empty(isl::ctx ctx)
+{
+ auto res = isl_union_map_empty_ctx(ctx.release());
+ return manage(res);
+}
+
+isl::union_map union_map::eq_at(isl::multi_union_pw_aff mupa) const
+{
+ auto res = isl_union_map_eq_at_multi_union_pw_aff(copy(), mupa.release());
+ return manage(res);
+}
+
+boolean union_map::every_map(const std::function<boolean(isl::map)> &test) const
+{
+ struct test_data {
+ std::function<boolean(isl::map)> func;
+ } test_data = { test };
+ auto test_lambda = [](isl_map *arg_0, void *arg_1) -> isl_bool {
+ auto *data = static_cast<struct test_data *>(arg_1);
+ auto ret = (data->func)(manage_copy(arg_0));
+ return ret.release();
+ };
+ auto res = isl_union_map_every_map(get(), test_lambda, &test_data);
+ return manage(res);
+}
+
+isl::map union_map::extract_map(isl::space space) const
+{
+ auto res = isl_union_map_extract_map(get(), space.release());
+ return manage(res);
+}
+
+isl::union_map union_map::factor_domain() const
+{
+ auto res = isl_union_map_factor_domain(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::factor_range() const
+{
+ auto res = isl_union_map_factor_range(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::fixed_power(isl::val exp) const
+{
+ auto res = isl_union_map_fixed_power_val(copy(), exp.release());
+ return manage(res);
+}
+
+isl::union_map union_map::fixed_power(long exp) const
+{
+ return this->fixed_power(isl::val(ctx(), exp));
+}
+
+isl::union_map union_map::flat_range_product(isl::union_map umap2) const
+{
+ auto res = isl_union_map_flat_range_product(copy(), umap2.release());
+ return manage(res);
+}
+
+stat union_map::foreach_map(const std::function<stat(isl::map)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::map)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_map *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_union_map_foreach_map(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::union_map union_map::from(isl::multi_union_pw_aff mupa)
+{
+ auto res = isl_union_map_from_multi_union_pw_aff(mupa.release());
+ return manage(res);
+}
+
+isl::union_map union_map::from(isl::union_pw_multi_aff upma)
+{
+ auto res = isl_union_map_from_union_pw_multi_aff(upma.release());
+ return manage(res);
+}
+
+isl::union_map union_map::from_domain(isl::union_set uset)
+{
+ auto res = isl_union_map_from_domain(uset.release());
+ return manage(res);
+}
+
+isl::union_map union_map::from_domain_and_range(isl::union_set domain, isl::union_set range)
+{
+ auto res = isl_union_map_from_domain_and_range(domain.release(), range.release());
+ return manage(res);
+}
+
+isl::union_map union_map::from_range(isl::union_set uset)
+{
+ auto res = isl_union_map_from_range(uset.release());
+ return manage(res);
+}
+
+isl::union_map union_map::gist(isl::union_map context) const
+{
+ auto res = isl_union_map_gist(copy(), context.release());
+ return manage(res);
+}
+
+isl::union_map union_map::gist_domain(isl::union_set uset) const
+{
+ auto res = isl_union_map_gist_domain(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_map union_map::gist_params(isl::set set) const
+{
+ auto res = isl_union_map_gist_params(copy(), set.release());
+ return manage(res);
+}
+
+isl::union_map union_map::gist_range(isl::union_set uset) const
+{
+ auto res = isl_union_map_gist_range(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_map union_map::intersect(isl::union_map umap2) const
+{
+ auto res = isl_union_map_intersect(copy(), umap2.release());
+ return manage(res);
+}
+
+isl::union_map union_map::intersect_domain(isl::space space) const
+{
+ auto res = isl_union_map_intersect_domain_space(copy(), space.release());
+ return manage(res);
+}
+
+isl::union_map union_map::intersect_domain(isl::union_set uset) const
+{
+ auto res = isl_union_map_intersect_domain_union_set(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_map union_map::intersect_domain_factor_domain(isl::union_map factor) const
+{
+ auto res = isl_union_map_intersect_domain_factor_domain(copy(), factor.release());
+ return manage(res);
+}
+
+isl::union_map union_map::intersect_domain_factor_range(isl::union_map factor) const
+{
+ auto res = isl_union_map_intersect_domain_factor_range(copy(), factor.release());
+ return manage(res);
+}
+
+isl::union_map union_map::intersect_params(isl::set set) const
+{
+ auto res = isl_union_map_intersect_params(copy(), set.release());
+ return manage(res);
+}
+
+isl::union_map union_map::intersect_range(isl::space space) const
+{
+ auto res = isl_union_map_intersect_range_space(copy(), space.release());
+ return manage(res);
+}
+
+isl::union_map union_map::intersect_range(isl::union_set uset) const
+{
+ auto res = isl_union_map_intersect_range_union_set(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_map union_map::intersect_range_factor_domain(isl::union_map factor) const
+{
+ auto res = isl_union_map_intersect_range_factor_domain(copy(), factor.release());
+ return manage(res);
+}
+
+isl::union_map union_map::intersect_range_factor_range(isl::union_map factor) const
+{
+ auto res = isl_union_map_intersect_range_factor_range(copy(), factor.release());
+ return manage(res);
+}
+
+boolean union_map::is_bijective() const
+{
+ auto res = isl_union_map_is_bijective(get());
+ return manage(res);
+}
+
+boolean union_map::is_disjoint(const isl::union_map &umap2) const
+{
+ auto res = isl_union_map_is_disjoint(get(), umap2.get());
+ return manage(res);
+}
+
+boolean union_map::is_empty() const
+{
+ auto res = isl_union_map_is_empty(get());
+ return manage(res);
+}
+
+boolean union_map::is_equal(const isl::union_map &umap2) const
+{
+ auto res = isl_union_map_is_equal(get(), umap2.get());
+ return manage(res);
+}
+
+boolean union_map::is_injective() const
+{
+ auto res = isl_union_map_is_injective(get());
+ return manage(res);
+}
+
+boolean union_map::is_single_valued() const
+{
+ auto res = isl_union_map_is_single_valued(get());
+ return manage(res);
+}
+
+boolean union_map::is_strict_subset(const isl::union_map &umap2) const
+{
+ auto res = isl_union_map_is_strict_subset(get(), umap2.get());
+ return manage(res);
+}
+
+boolean union_map::is_subset(const isl::union_map &umap2) const
+{
+ auto res = isl_union_map_is_subset(get(), umap2.get());
+ return manage(res);
+}
+
+boolean union_map::isa_map() const
+{
+ auto res = isl_union_map_isa_map(get());
+ return manage(res);
+}
+
+isl::union_map union_map::lexmax() const
+{
+ auto res = isl_union_map_lexmax(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::lexmin() const
+{
+ auto res = isl_union_map_lexmin(copy());
+ return manage(res);
+}
+
+isl::map_list union_map::map_list() const
+{
+ auto res = isl_union_map_get_map_list(get());
+ return manage(res);
+}
+
+isl::map_list union_map::get_map_list() const
+{
+ return map_list();
+}
+
+isl::set union_map::params() const
+{
+ auto res = isl_union_map_params(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::polyhedral_hull() const
+{
+ auto res = isl_union_map_polyhedral_hull(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::preimage_domain(isl::multi_aff ma) const
+{
+ auto res = isl_union_map_preimage_domain_multi_aff(copy(), ma.release());
+ return manage(res);
+}
+
+isl::union_map union_map::preimage_domain(isl::multi_pw_aff mpa) const
+{
+ auto res = isl_union_map_preimage_domain_multi_pw_aff(copy(), mpa.release());
+ return manage(res);
+}
+
+isl::union_map union_map::preimage_domain(isl::pw_multi_aff pma) const
+{
+ auto res = isl_union_map_preimage_domain_pw_multi_aff(copy(), pma.release());
+ return manage(res);
+}
+
+isl::union_map union_map::preimage_domain(isl::union_pw_multi_aff upma) const
+{
+ auto res = isl_union_map_preimage_domain_union_pw_multi_aff(copy(), upma.release());
+ return manage(res);
+}
+
+isl::union_map union_map::preimage_range(isl::multi_aff ma) const
+{
+ auto res = isl_union_map_preimage_range_multi_aff(copy(), ma.release());
+ return manage(res);
+}
+
+isl::union_map union_map::preimage_range(isl::pw_multi_aff pma) const
+{
+ auto res = isl_union_map_preimage_range_pw_multi_aff(copy(), pma.release());
+ return manage(res);
+}
+
+isl::union_map union_map::preimage_range(isl::union_pw_multi_aff upma) const
+{
+ auto res = isl_union_map_preimage_range_union_pw_multi_aff(copy(), upma.release());
+ return manage(res);
+}
+
+isl::union_map union_map::product(isl::union_map umap2) const
+{
+ auto res = isl_union_map_product(copy(), umap2.release());
+ return manage(res);
+}
+
+isl::union_map union_map::project_out_all_params() const
+{
+ auto res = isl_union_map_project_out_all_params(copy());
+ return manage(res);
+}
+
+isl::union_set union_map::range() const
+{
+ auto res = isl_union_map_range(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::range_factor_domain() const
+{
+ auto res = isl_union_map_range_factor_domain(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::range_factor_range() const
+{
+ auto res = isl_union_map_range_factor_range(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::range_map() const
+{
+ auto res = isl_union_map_range_map(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::range_product(isl::union_map umap2) const
+{
+ auto res = isl_union_map_range_product(copy(), umap2.release());
+ return manage(res);
+}
+
+isl::union_map union_map::range_reverse() const
+{
+ auto res = isl_union_map_range_reverse(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::reverse() const
+{
+ auto res = isl_union_map_reverse(copy());
+ return manage(res);
+}
+
+isl::space union_map::space() const
+{
+ auto res = isl_union_map_get_space(get());
+ return manage(res);
+}
+
+isl::space union_map::get_space() const
+{
+ return space();
+}
+
+isl::union_map union_map::subtract(isl::union_map umap2) const
+{
+ auto res = isl_union_map_subtract(copy(), umap2.release());
+ return manage(res);
+}
+
+isl::union_map union_map::subtract_domain(isl::union_set dom) const
+{
+ auto res = isl_union_map_subtract_domain(copy(), dom.release());
+ return manage(res);
+}
+
+isl::union_map union_map::subtract_range(isl::union_set dom) const
+{
+ auto res = isl_union_map_subtract_range(copy(), dom.release());
+ return manage(res);
+}
+
+isl::union_map union_map::uncurry() const
+{
+ auto res = isl_union_map_uncurry(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::unite(isl::union_map umap2) const
+{
+ auto res = isl_union_map_union(copy(), umap2.release());
+ return manage(res);
+}
+
+isl::union_map union_map::universe() const
+{
+ auto res = isl_union_map_universe(copy());
+ return manage(res);
+}
+
+isl::union_set union_map::wrap() const
+{
+ auto res = isl_union_map_wrap(copy());
+ return manage(res);
+}
+
+isl::union_map union_map::zip() const
+{
+ auto res = isl_union_map_zip(copy());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const union_map &obj)
+{
+ char *str = isl_union_map_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::union_pw_aff
+union_pw_aff manage(__isl_take isl_union_pw_aff *ptr) {
+ return union_pw_aff(ptr);
+}
+union_pw_aff manage_copy(__isl_keep isl_union_pw_aff *ptr) {
+ ptr = isl_union_pw_aff_copy(ptr);
+ return union_pw_aff(ptr);
+}
+
+union_pw_aff::union_pw_aff()
+ : ptr(nullptr) {}
+
+union_pw_aff::union_pw_aff(const union_pw_aff &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+union_pw_aff::union_pw_aff(__isl_take isl_union_pw_aff *ptr)
+ : ptr(ptr) {}
+
+union_pw_aff::union_pw_aff(isl::aff aff)
+{
+ auto res = isl_union_pw_aff_from_aff(aff.release());
+ ptr = res;
+}
+
+union_pw_aff::union_pw_aff(isl::pw_aff pa)
+{
+ auto res = isl_union_pw_aff_from_pw_aff(pa.release());
+ ptr = res;
+}
+
+union_pw_aff::union_pw_aff(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_union_pw_aff_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+union_pw_aff::union_pw_aff(isl::union_set domain, isl::val v)
+{
+ auto res = isl_union_pw_aff_val_on_domain(domain.release(), v.release());
+ ptr = res;
+}
+
+union_pw_aff &union_pw_aff::operator=(union_pw_aff obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+union_pw_aff::~union_pw_aff() {
+ if (ptr)
+ isl_union_pw_aff_free(ptr);
+}
+
+__isl_give isl_union_pw_aff *union_pw_aff::copy() const & {
+ return isl_union_pw_aff_copy(ptr);
+}
+
+__isl_keep isl_union_pw_aff *union_pw_aff::get() const {
+ return ptr;
+}
+
+__isl_give isl_union_pw_aff *union_pw_aff::release() {
+ isl_union_pw_aff *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool union_pw_aff::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx union_pw_aff::ctx() const {
+ return isl::ctx(isl_union_pw_aff_get_ctx(ptr));
+}
+
+isl::multi_union_pw_aff union_pw_aff::add(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_union_pw_aff(*this).add(multi2);
+}
+
+isl::union_pw_aff union_pw_aff::add(isl::union_pw_aff upa2) const
+{
+ auto res = isl_union_pw_aff_add(copy(), upa2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_aff::add(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).add(upma2);
+}
+
+isl::union_pw_aff union_pw_aff::add(const isl::aff &upa2) const
+{
+ return this->add(isl::union_pw_aff(upa2));
+}
+
+isl::union_pw_aff union_pw_aff::add(const isl::pw_aff &upa2) const
+{
+ return this->add(isl::union_pw_aff(upa2));
+}
+
+isl::union_pw_multi_aff union_pw_aff::add_pw_multi_aff(const isl::pw_multi_aff &pma) const
+{
+ return isl::union_pw_multi_aff(*this).add_pw_multi_aff(pma);
+}
+
+isl::union_pw_multi_aff union_pw_aff::apply(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).apply(upma2);
+}
+
+isl::multi_union_pw_aff union_pw_aff::as_multi_union_pw_aff() const
+{
+ return isl::union_pw_multi_aff(*this).as_multi_union_pw_aff();
+}
+
+isl::pw_multi_aff union_pw_aff::as_pw_multi_aff() const
+{
+ return isl::union_pw_multi_aff(*this).as_pw_multi_aff();
+}
+
+isl::union_map union_pw_aff::as_union_map() const
+{
+ return isl::union_pw_multi_aff(*this).as_union_map();
+}
+
+isl::union_pw_aff union_pw_aff::at(int pos) const
+{
+ return isl::multi_union_pw_aff(*this).at(pos);
+}
+
+isl::union_set union_pw_aff::bind(const isl::multi_id &tuple) const
+{
+ return isl::multi_union_pw_aff(*this).bind(tuple);
+}
+
+isl::union_set union_pw_aff::bind(isl::id id) const
+{
+ auto res = isl_union_pw_aff_bind_id(copy(), id.release());
+ return manage(res);
+}
+
+isl::union_set union_pw_aff::bind(const std::string &id) const
+{
+ return this->bind(isl::id(ctx(), id));
+}
+
+isl::union_pw_aff union_pw_aff::coalesce() const
+{
+ auto res = isl_union_pw_aff_coalesce(copy());
+ return manage(res);
+}
+
+class size union_pw_aff::dim(isl::dim type) const
+{
+ return isl::multi_union_pw_aff(*this).dim(type);
+}
+
+isl::union_set union_pw_aff::domain() const
+{
+ auto res = isl_union_pw_aff_domain(copy());
+ return manage(res);
+}
+
+isl::union_pw_aff union_pw_aff::empty(isl::space space)
+{
+ auto res = isl_union_pw_aff_empty(space.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff union_pw_aff::extract_pw_multi_aff(const isl::space &space) const
+{
+ return isl::union_pw_multi_aff(*this).extract_pw_multi_aff(space);
+}
+
+isl::multi_union_pw_aff union_pw_aff::flat_range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_union_pw_aff(*this).flat_range_product(multi2);
+}
+
+isl::union_pw_multi_aff union_pw_aff::flat_range_product(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).flat_range_product(upma2);
+}
+
+stat union_pw_aff::foreach_pw_aff(const std::function<stat(isl::pw_aff)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::pw_aff)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_pw_aff *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_union_pw_aff_foreach_pw_aff(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::union_pw_aff union_pw_aff::gist(isl::union_set context) const
+{
+ auto res = isl_union_pw_aff_gist(copy(), context.release());
+ return manage(res);
+}
+
+boolean union_pw_aff::has_range_tuple_id() const
+{
+ return isl::multi_union_pw_aff(*this).has_range_tuple_id();
+}
+
+isl::union_pw_aff union_pw_aff::intersect_domain(isl::space space) const
+{
+ auto res = isl_union_pw_aff_intersect_domain_space(copy(), space.release());
+ return manage(res);
+}
+
+isl::union_pw_aff union_pw_aff::intersect_domain(isl::union_set uset) const
+{
+ auto res = isl_union_pw_aff_intersect_domain_union_set(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_pw_aff union_pw_aff::intersect_domain_wrapped_domain(isl::union_set uset) const
+{
+ auto res = isl_union_pw_aff_intersect_domain_wrapped_domain(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_pw_aff union_pw_aff::intersect_domain_wrapped_range(isl::union_set uset) const
+{
+ auto res = isl_union_pw_aff_intersect_domain_wrapped_range(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_pw_aff union_pw_aff::intersect_params(isl::set set) const
+{
+ auto res = isl_union_pw_aff_intersect_params(copy(), set.release());
+ return manage(res);
+}
+
+boolean union_pw_aff::involves_locals() const
+{
+ return isl::union_pw_multi_aff(*this).involves_locals();
+}
+
+boolean union_pw_aff::involves_nan() const
+{
+ return isl::multi_union_pw_aff(*this).involves_nan();
+}
+
+boolean union_pw_aff::isa_pw_multi_aff() const
+{
+ return isl::union_pw_multi_aff(*this).isa_pw_multi_aff();
+}
+
+isl::union_pw_aff_list union_pw_aff::list() const
+{
+ return isl::multi_union_pw_aff(*this).list();
+}
+
+isl::multi_union_pw_aff union_pw_aff::neg() const
+{
+ return isl::multi_union_pw_aff(*this).neg();
+}
+
+boolean union_pw_aff::plain_is_empty() const
+{
+ return isl::union_pw_multi_aff(*this).plain_is_empty();
+}
+
+boolean union_pw_aff::plain_is_equal(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_union_pw_aff(*this).plain_is_equal(multi2);
+}
+
+isl::union_pw_multi_aff union_pw_aff::preimage_domain_wrapped_domain(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).preimage_domain_wrapped_domain(upma2);
+}
+
+isl::union_pw_aff union_pw_aff::pullback(isl::union_pw_multi_aff upma) const
+{
+ auto res = isl_union_pw_aff_pullback_union_pw_multi_aff(copy(), upma.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff_list union_pw_aff::pw_multi_aff_list() const
+{
+ return isl::union_pw_multi_aff(*this).pw_multi_aff_list();
+}
+
+isl::union_pw_multi_aff union_pw_aff::range_factor_domain() const
+{
+ return isl::union_pw_multi_aff(*this).range_factor_domain();
+}
+
+isl::union_pw_multi_aff union_pw_aff::range_factor_range() const
+{
+ return isl::union_pw_multi_aff(*this).range_factor_range();
+}
+
+isl::multi_union_pw_aff union_pw_aff::range_product(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_union_pw_aff(*this).range_product(multi2);
+}
+
+isl::union_pw_multi_aff union_pw_aff::range_product(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).range_product(upma2);
+}
+
+isl::id union_pw_aff::range_tuple_id() const
+{
+ return isl::multi_union_pw_aff(*this).range_tuple_id();
+}
+
+isl::multi_union_pw_aff union_pw_aff::reset_range_tuple_id() const
+{
+ return isl::multi_union_pw_aff(*this).reset_range_tuple_id();
+}
+
+isl::multi_union_pw_aff union_pw_aff::reset_tuple_id(isl::dim type) const
+{
+ return isl::multi_union_pw_aff(*this).reset_tuple_id(type);
+}
+
+isl::multi_union_pw_aff union_pw_aff::scale(const isl::multi_val &mv) const
+{
+ return isl::multi_union_pw_aff(*this).scale(mv);
+}
+
+isl::multi_union_pw_aff union_pw_aff::scale(const isl::val &v) const
+{
+ return isl::multi_union_pw_aff(*this).scale(v);
+}
+
+isl::multi_union_pw_aff union_pw_aff::scale(long v) const
+{
+ return this->scale(isl::val(ctx(), v));
+}
+
+isl::multi_union_pw_aff union_pw_aff::scale_down(const isl::multi_val &mv) const
+{
+ return isl::multi_union_pw_aff(*this).scale_down(mv);
+}
+
+isl::multi_union_pw_aff union_pw_aff::scale_down(const isl::val &v) const
+{
+ return isl::multi_union_pw_aff(*this).scale_down(v);
+}
+
+isl::multi_union_pw_aff union_pw_aff::scale_down(long v) const
+{
+ return this->scale_down(isl::val(ctx(), v));
+}
+
+isl::multi_union_pw_aff union_pw_aff::set_at(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::multi_union_pw_aff(*this).set_at(pos, el);
+}
+
+isl::multi_union_pw_aff union_pw_aff::set_range_tuple(const isl::id &id) const
+{
+ return isl::multi_union_pw_aff(*this).set_range_tuple(id);
+}
+
+isl::multi_union_pw_aff union_pw_aff::set_range_tuple(const std::string &id) const
+{
+ return this->set_range_tuple(isl::id(ctx(), id));
+}
+
+isl::multi_union_pw_aff union_pw_aff::set_union_pw_aff(int pos, const isl::union_pw_aff &el) const
+{
+ return isl::multi_union_pw_aff(*this).set_union_pw_aff(pos, el);
+}
+
+class size union_pw_aff::size() const
+{
+ return isl::multi_union_pw_aff(*this).size();
+}
+
+isl::space union_pw_aff::space() const
+{
+ auto res = isl_union_pw_aff_get_space(get());
+ return manage(res);
+}
+
+isl::space union_pw_aff::get_space() const
+{
+ return space();
+}
+
+isl::multi_union_pw_aff union_pw_aff::sub(const isl::multi_union_pw_aff &multi2) const
+{
+ return isl::multi_union_pw_aff(*this).sub(multi2);
+}
+
+isl::union_pw_aff union_pw_aff::sub(isl::union_pw_aff upa2) const
+{
+ auto res = isl_union_pw_aff_sub(copy(), upa2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_aff::sub(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).sub(upma2);
+}
+
+isl::union_pw_aff union_pw_aff::sub(const isl::aff &upa2) const
+{
+ return this->sub(isl::union_pw_aff(upa2));
+}
+
+isl::union_pw_aff union_pw_aff::sub(const isl::pw_aff &upa2) const
+{
+ return this->sub(isl::union_pw_aff(upa2));
+}
+
+isl::union_pw_aff union_pw_aff::subtract_domain(isl::space space) const
+{
+ auto res = isl_union_pw_aff_subtract_domain_space(copy(), space.release());
+ return manage(res);
+}
+
+isl::union_pw_aff union_pw_aff::subtract_domain(isl::union_set uset) const
+{
+ auto res = isl_union_pw_aff_subtract_domain_union_set(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_pw_aff_list union_pw_aff::to_list() const
+{
+ auto res = isl_union_pw_aff_to_list(copy());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff union_pw_aff::union_add(const isl::multi_union_pw_aff &mupa2) const
+{
+ return isl::multi_union_pw_aff(*this).union_add(mupa2);
+}
+
+isl::union_pw_aff union_pw_aff::union_add(isl::union_pw_aff upa2) const
+{
+ auto res = isl_union_pw_aff_union_add(copy(), upa2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_aff::union_add(const isl::union_pw_multi_aff &upma2) const
+{
+ return isl::union_pw_multi_aff(*this).union_add(upma2);
+}
+
+isl::union_pw_aff union_pw_aff::union_add(const isl::aff &upa2) const
+{
+ return this->union_add(isl::union_pw_aff(upa2));
+}
+
+isl::union_pw_aff union_pw_aff::union_add(const isl::pw_aff &upa2) const
+{
+ return this->union_add(isl::union_pw_aff(upa2));
+}
+
+inline std::ostream &operator<<(std::ostream &os, const union_pw_aff &obj)
+{
+ char *str = isl_union_pw_aff_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::union_pw_aff_list
+union_pw_aff_list manage(__isl_take isl_union_pw_aff_list *ptr) {
+ return union_pw_aff_list(ptr);
+}
+union_pw_aff_list manage_copy(__isl_keep isl_union_pw_aff_list *ptr) {
+ ptr = isl_union_pw_aff_list_copy(ptr);
+ return union_pw_aff_list(ptr);
+}
+
+union_pw_aff_list::union_pw_aff_list()
+ : ptr(nullptr) {}
+
+union_pw_aff_list::union_pw_aff_list(const union_pw_aff_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+union_pw_aff_list::union_pw_aff_list(__isl_take isl_union_pw_aff_list *ptr)
+ : ptr(ptr) {}
+
+union_pw_aff_list::union_pw_aff_list(isl::ctx ctx, int n)
+{
+ auto res = isl_union_pw_aff_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+union_pw_aff_list::union_pw_aff_list(isl::union_pw_aff el)
+{
+ auto res = isl_union_pw_aff_list_from_union_pw_aff(el.release());
+ ptr = res;
+}
+
+union_pw_aff_list::union_pw_aff_list(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_union_pw_aff_list_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+union_pw_aff_list &union_pw_aff_list::operator=(union_pw_aff_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+union_pw_aff_list::~union_pw_aff_list() {
+ if (ptr)
+ isl_union_pw_aff_list_free(ptr);
+}
+
+__isl_give isl_union_pw_aff_list *union_pw_aff_list::copy() const & {
+ return isl_union_pw_aff_list_copy(ptr);
+}
+
+__isl_keep isl_union_pw_aff_list *union_pw_aff_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_union_pw_aff_list *union_pw_aff_list::release() {
+ isl_union_pw_aff_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool union_pw_aff_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx union_pw_aff_list::ctx() const {
+ return isl::ctx(isl_union_pw_aff_list_get_ctx(ptr));
+}
+
+isl::union_pw_aff_list union_pw_aff_list::add(isl::union_pw_aff el) const
+{
+ auto res = isl_union_pw_aff_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::union_pw_aff union_pw_aff_list::at(int index) const
+{
+ auto res = isl_union_pw_aff_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::union_pw_aff union_pw_aff_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::union_pw_aff_list union_pw_aff_list::clear() const
+{
+ auto res = isl_union_pw_aff_list_clear(copy());
+ return manage(res);
+}
+
+isl::union_pw_aff_list union_pw_aff_list::concat(isl::union_pw_aff_list list2) const
+{
+ auto res = isl_union_pw_aff_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::union_pw_aff_list union_pw_aff_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_union_pw_aff_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat union_pw_aff_list::foreach(const std::function<stat(isl::union_pw_aff)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::union_pw_aff)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_union_pw_aff *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_union_pw_aff_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::union_pw_aff_list union_pw_aff_list::insert(unsigned int pos, isl::union_pw_aff el) const
+{
+ auto res = isl_union_pw_aff_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+class size union_pw_aff_list::size() const
+{
+ auto res = isl_union_pw_aff_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const union_pw_aff_list &obj)
+{
+ char *str = isl_union_pw_aff_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::union_pw_multi_aff
+union_pw_multi_aff manage(__isl_take isl_union_pw_multi_aff *ptr) {
+ return union_pw_multi_aff(ptr);
+}
+union_pw_multi_aff manage_copy(__isl_keep isl_union_pw_multi_aff *ptr) {
+ ptr = isl_union_pw_multi_aff_copy(ptr);
+ return union_pw_multi_aff(ptr);
+}
+
+union_pw_multi_aff::union_pw_multi_aff()
+ : ptr(nullptr) {}
+
+union_pw_multi_aff::union_pw_multi_aff(const union_pw_multi_aff &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+union_pw_multi_aff::union_pw_multi_aff(__isl_take isl_union_pw_multi_aff *ptr)
+ : ptr(ptr) {}
+
+union_pw_multi_aff::union_pw_multi_aff(isl::union_set uset)
+{
+ auto res = isl_union_pw_multi_aff_from_domain(uset.release());
+ ptr = res;
+}
+
+union_pw_multi_aff::union_pw_multi_aff(isl::multi_aff ma)
+{
+ auto res = isl_union_pw_multi_aff_from_multi_aff(ma.release());
+ ptr = res;
+}
+
+union_pw_multi_aff::union_pw_multi_aff(isl::pw_multi_aff pma)
+{
+ auto res = isl_union_pw_multi_aff_from_pw_multi_aff(pma.release());
+ ptr = res;
+}
+
+union_pw_multi_aff::union_pw_multi_aff(isl::union_map umap)
+{
+ auto res = isl_union_pw_multi_aff_from_union_map(umap.release());
+ ptr = res;
+}
+
+union_pw_multi_aff::union_pw_multi_aff(isl::union_pw_aff upa)
+{
+ auto res = isl_union_pw_multi_aff_from_union_pw_aff(upa.release());
+ ptr = res;
+}
+
+union_pw_multi_aff::union_pw_multi_aff(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_union_pw_multi_aff_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+union_pw_multi_aff &union_pw_multi_aff::operator=(union_pw_multi_aff obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+union_pw_multi_aff::~union_pw_multi_aff() {
+ if (ptr)
+ isl_union_pw_multi_aff_free(ptr);
+}
+
+__isl_give isl_union_pw_multi_aff *union_pw_multi_aff::copy() const & {
+ return isl_union_pw_multi_aff_copy(ptr);
+}
+
+__isl_keep isl_union_pw_multi_aff *union_pw_multi_aff::get() const {
+ return ptr;
+}
+
+__isl_give isl_union_pw_multi_aff *union_pw_multi_aff::release() {
+ isl_union_pw_multi_aff *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool union_pw_multi_aff::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx union_pw_multi_aff::ctx() const {
+ return isl::ctx(isl_union_pw_multi_aff_get_ctx(ptr));
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::add(isl::union_pw_multi_aff upma2) const
+{
+ auto res = isl_union_pw_multi_aff_add(copy(), upma2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::add_pw_multi_aff(isl::pw_multi_aff pma) const
+{
+ auto res = isl_union_pw_multi_aff_add_pw_multi_aff(copy(), pma.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::apply(isl::union_pw_multi_aff upma2) const
+{
+ auto res = isl_union_pw_multi_aff_apply_union_pw_multi_aff(copy(), upma2.release());
+ return manage(res);
+}
+
+isl::multi_union_pw_aff union_pw_multi_aff::as_multi_union_pw_aff() const
+{
+ auto res = isl_union_pw_multi_aff_as_multi_union_pw_aff(copy());
+ return manage(res);
+}
+
+isl::pw_multi_aff union_pw_multi_aff::as_pw_multi_aff() const
+{
+ auto res = isl_union_pw_multi_aff_as_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::union_map union_pw_multi_aff::as_union_map() const
+{
+ auto res = isl_union_pw_multi_aff_as_union_map(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::coalesce() const
+{
+ auto res = isl_union_pw_multi_aff_coalesce(copy());
+ return manage(res);
+}
+
+isl::union_set union_pw_multi_aff::domain() const
+{
+ auto res = isl_union_pw_multi_aff_domain(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::empty(isl::space space)
+{
+ auto res = isl_union_pw_multi_aff_empty(space.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::empty(isl::ctx ctx)
+{
+ auto res = isl_union_pw_multi_aff_empty_ctx(ctx.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff union_pw_multi_aff::extract_pw_multi_aff(isl::space space) const
+{
+ auto res = isl_union_pw_multi_aff_extract_pw_multi_aff(get(), space.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::flat_range_product(isl::union_pw_multi_aff upma2) const
+{
+ auto res = isl_union_pw_multi_aff_flat_range_product(copy(), upma2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::gist(isl::union_set context) const
+{
+ auto res = isl_union_pw_multi_aff_gist(copy(), context.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::intersect_domain(isl::space space) const
+{
+ auto res = isl_union_pw_multi_aff_intersect_domain_space(copy(), space.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::intersect_domain(isl::union_set uset) const
+{
+ auto res = isl_union_pw_multi_aff_intersect_domain_union_set(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::intersect_domain_wrapped_domain(isl::union_set uset) const
+{
+ auto res = isl_union_pw_multi_aff_intersect_domain_wrapped_domain(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::intersect_domain_wrapped_range(isl::union_set uset) const
+{
+ auto res = isl_union_pw_multi_aff_intersect_domain_wrapped_range(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::intersect_params(isl::set set) const
+{
+ auto res = isl_union_pw_multi_aff_intersect_params(copy(), set.release());
+ return manage(res);
+}
+
+boolean union_pw_multi_aff::involves_locals() const
+{
+ auto res = isl_union_pw_multi_aff_involves_locals(get());
+ return manage(res);
+}
+
+boolean union_pw_multi_aff::isa_pw_multi_aff() const
+{
+ auto res = isl_union_pw_multi_aff_isa_pw_multi_aff(get());
+ return manage(res);
+}
+
+boolean union_pw_multi_aff::plain_is_empty() const
+{
+ auto res = isl_union_pw_multi_aff_plain_is_empty(get());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::preimage_domain_wrapped_domain(isl::union_pw_multi_aff upma2) const
+{
+ auto res = isl_union_pw_multi_aff_preimage_domain_wrapped_domain_union_pw_multi_aff(copy(), upma2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::pullback(isl::union_pw_multi_aff upma2) const
+{
+ auto res = isl_union_pw_multi_aff_pullback_union_pw_multi_aff(copy(), upma2.release());
+ return manage(res);
+}
+
+isl::pw_multi_aff_list union_pw_multi_aff::pw_multi_aff_list() const
+{
+ auto res = isl_union_pw_multi_aff_get_pw_multi_aff_list(get());
+ return manage(res);
+}
+
+isl::pw_multi_aff_list union_pw_multi_aff::get_pw_multi_aff_list() const
+{
+ return pw_multi_aff_list();
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::range_factor_domain() const
+{
+ auto res = isl_union_pw_multi_aff_range_factor_domain(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::range_factor_range() const
+{
+ auto res = isl_union_pw_multi_aff_range_factor_range(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::range_product(isl::union_pw_multi_aff upma2) const
+{
+ auto res = isl_union_pw_multi_aff_range_product(copy(), upma2.release());
+ return manage(res);
+}
+
+isl::space union_pw_multi_aff::space() const
+{
+ auto res = isl_union_pw_multi_aff_get_space(get());
+ return manage(res);
+}
+
+isl::space union_pw_multi_aff::get_space() const
+{
+ return space();
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::sub(isl::union_pw_multi_aff upma2) const
+{
+ auto res = isl_union_pw_multi_aff_sub(copy(), upma2.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::subtract_domain(isl::space space) const
+{
+ auto res = isl_union_pw_multi_aff_subtract_domain_space(copy(), space.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::subtract_domain(isl::union_set uset) const
+{
+ auto res = isl_union_pw_multi_aff_subtract_domain_union_set(copy(), uset.release());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_pw_multi_aff::union_add(isl::union_pw_multi_aff upma2) const
+{
+ auto res = isl_union_pw_multi_aff_union_add(copy(), upma2.release());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const union_pw_multi_aff &obj)
+{
+ char *str = isl_union_pw_multi_aff_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::union_set
+union_set manage(__isl_take isl_union_set *ptr) {
+ return union_set(ptr);
+}
+union_set manage_copy(__isl_keep isl_union_set *ptr) {
+ ptr = isl_union_set_copy(ptr);
+ return union_set(ptr);
+}
+
+union_set::union_set()
+ : ptr(nullptr) {}
+
+union_set::union_set(const union_set &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+union_set::union_set(__isl_take isl_union_set *ptr)
+ : ptr(ptr) {}
+
+union_set::union_set(isl::basic_set bset)
+{
+ auto res = isl_union_set_from_basic_set(bset.release());
+ ptr = res;
+}
+
+union_set::union_set(isl::point pnt)
+{
+ auto res = isl_union_set_from_point(pnt.release());
+ ptr = res;
+}
+
+union_set::union_set(isl::set set)
+{
+ auto res = isl_union_set_from_set(set.release());
+ ptr = res;
+}
+
+union_set::union_set(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_union_set_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+union_set &union_set::operator=(union_set obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+union_set::~union_set() {
+ if (ptr)
+ isl_union_set_free(ptr);
+}
+
+__isl_give isl_union_set *union_set::copy() const & {
+ return isl_union_set_copy(ptr);
+}
+
+__isl_keep isl_union_set *union_set::get() const {
+ return ptr;
+}
+
+__isl_give isl_union_set *union_set::release() {
+ isl_union_set *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool union_set::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx union_set::ctx() const {
+ return isl::ctx(isl_union_set_get_ctx(ptr));
+}
+
+isl::union_set union_set::affine_hull() const
+{
+ auto res = isl_union_set_affine_hull(copy());
+ return manage(res);
+}
+
+isl::union_set union_set::apply(isl::union_map umap) const
+{
+ auto res = isl_union_set_apply(copy(), umap.release());
+ return manage(res);
+}
+
+isl::set union_set::as_set() const
+{
+ auto res = isl_union_set_as_set(copy());
+ return manage(res);
+}
+
+isl::union_set union_set::coalesce() const
+{
+ auto res = isl_union_set_coalesce(copy());
+ return manage(res);
+}
+
+isl::union_set union_set::compute_divs() const
+{
+ auto res = isl_union_set_compute_divs(copy());
+ return manage(res);
+}
+
+boolean union_set::contains(const isl::space &space) const
+{
+ auto res = isl_union_set_contains(get(), space.get());
+ return manage(res);
+}
+
+isl::union_set union_set::detect_equalities() const
+{
+ auto res = isl_union_set_detect_equalities(copy());
+ return manage(res);
+}
+
+isl::union_set union_set::empty(isl::ctx ctx)
+{
+ auto res = isl_union_set_empty_ctx(ctx.release());
+ return manage(res);
+}
+
+boolean union_set::every_set(const std::function<boolean(isl::set)> &test) const
+{
+ struct test_data {
+ std::function<boolean(isl::set)> func;
+ } test_data = { test };
+ auto test_lambda = [](isl_set *arg_0, void *arg_1) -> isl_bool {
+ auto *data = static_cast<struct test_data *>(arg_1);
+ auto ret = (data->func)(manage_copy(arg_0));
+ return ret.release();
+ };
+ auto res = isl_union_set_every_set(get(), test_lambda, &test_data);
+ return manage(res);
+}
+
+isl::set union_set::extract_set(isl::space space) const
+{
+ auto res = isl_union_set_extract_set(get(), space.release());
+ return manage(res);
+}
+
+stat union_set::foreach_point(const std::function<stat(isl::point)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::point)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_point *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_union_set_foreach_point(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+stat union_set::foreach_set(const std::function<stat(isl::set)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::set)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_set *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_union_set_foreach_set(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::union_set union_set::gist(isl::union_set context) const
+{
+ auto res = isl_union_set_gist(copy(), context.release());
+ return manage(res);
+}
+
+isl::union_set union_set::gist_params(isl::set set) const
+{
+ auto res = isl_union_set_gist_params(copy(), set.release());
+ return manage(res);
+}
+
+isl::union_map union_set::identity() const
+{
+ auto res = isl_union_set_identity(copy());
+ return manage(res);
+}
+
+isl::union_pw_multi_aff union_set::identity_union_pw_multi_aff() const
+{
+ auto res = isl_union_set_identity_union_pw_multi_aff(copy());
+ return manage(res);
+}
+
+isl::union_set union_set::intersect(isl::union_set uset2) const
+{
+ auto res = isl_union_set_intersect(copy(), uset2.release());
+ return manage(res);
+}
+
+isl::union_set union_set::intersect_params(isl::set set) const
+{
+ auto res = isl_union_set_intersect_params(copy(), set.release());
+ return manage(res);
+}
+
+boolean union_set::is_disjoint(const isl::union_set &uset2) const
+{
+ auto res = isl_union_set_is_disjoint(get(), uset2.get());
+ return manage(res);
+}
+
+boolean union_set::is_empty() const
+{
+ auto res = isl_union_set_is_empty(get());
+ return manage(res);
+}
+
+boolean union_set::is_equal(const isl::union_set &uset2) const
+{
+ auto res = isl_union_set_is_equal(get(), uset2.get());
+ return manage(res);
+}
+
+boolean union_set::is_strict_subset(const isl::union_set &uset2) const
+{
+ auto res = isl_union_set_is_strict_subset(get(), uset2.get());
+ return manage(res);
+}
+
+boolean union_set::is_subset(const isl::union_set &uset2) const
+{
+ auto res = isl_union_set_is_subset(get(), uset2.get());
+ return manage(res);
+}
+
+boolean union_set::isa_set() const
+{
+ auto res = isl_union_set_isa_set(get());
+ return manage(res);
+}
+
+isl::union_set union_set::lexmax() const
+{
+ auto res = isl_union_set_lexmax(copy());
+ return manage(res);
+}
+
+isl::union_set union_set::lexmin() const
+{
+ auto res = isl_union_set_lexmin(copy());
+ return manage(res);
+}
+
+isl::set union_set::params() const
+{
+ auto res = isl_union_set_params(copy());
+ return manage(res);
+}
+
+isl::union_set union_set::polyhedral_hull() const
+{
+ auto res = isl_union_set_polyhedral_hull(copy());
+ return manage(res);
+}
+
+isl::union_set union_set::preimage(isl::multi_aff ma) const
+{
+ auto res = isl_union_set_preimage_multi_aff(copy(), ma.release());
+ return manage(res);
+}
+
+isl::union_set union_set::preimage(isl::pw_multi_aff pma) const
+{
+ auto res = isl_union_set_preimage_pw_multi_aff(copy(), pma.release());
+ return manage(res);
+}
+
+isl::union_set union_set::preimage(isl::union_pw_multi_aff upma) const
+{
+ auto res = isl_union_set_preimage_union_pw_multi_aff(copy(), upma.release());
+ return manage(res);
+}
+
+isl::point union_set::sample_point() const
+{
+ auto res = isl_union_set_sample_point(copy());
+ return manage(res);
+}
+
+isl::set_list union_set::set_list() const
+{
+ auto res = isl_union_set_get_set_list(get());
+ return manage(res);
+}
+
+isl::set_list union_set::get_set_list() const
+{
+ return set_list();
+}
+
+isl::space union_set::space() const
+{
+ auto res = isl_union_set_get_space(get());
+ return manage(res);
+}
+
+isl::space union_set::get_space() const
+{
+ return space();
+}
+
+isl::union_set union_set::subtract(isl::union_set uset2) const
+{
+ auto res = isl_union_set_subtract(copy(), uset2.release());
+ return manage(res);
+}
+
+isl::union_set_list union_set::to_list() const
+{
+ auto res = isl_union_set_to_list(copy());
+ return manage(res);
+}
+
+isl::union_set union_set::unite(isl::union_set uset2) const
+{
+ auto res = isl_union_set_union(copy(), uset2.release());
+ return manage(res);
+}
+
+isl::union_set union_set::universe() const
+{
+ auto res = isl_union_set_universe(copy());
+ return manage(res);
+}
+
+isl::union_map union_set::unwrap() const
+{
+ auto res = isl_union_set_unwrap(copy());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const union_set &obj)
+{
+ char *str = isl_union_set_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::union_set_list
+union_set_list manage(__isl_take isl_union_set_list *ptr) {
+ return union_set_list(ptr);
+}
+union_set_list manage_copy(__isl_keep isl_union_set_list *ptr) {
+ ptr = isl_union_set_list_copy(ptr);
+ return union_set_list(ptr);
+}
+
+union_set_list::union_set_list()
+ : ptr(nullptr) {}
+
+union_set_list::union_set_list(const union_set_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+union_set_list::union_set_list(__isl_take isl_union_set_list *ptr)
+ : ptr(ptr) {}
+
+union_set_list::union_set_list(isl::ctx ctx, int n)
+{
+ auto res = isl_union_set_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+union_set_list::union_set_list(isl::union_set el)
+{
+ auto res = isl_union_set_list_from_union_set(el.release());
+ ptr = res;
+}
+
+union_set_list::union_set_list(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_union_set_list_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+union_set_list &union_set_list::operator=(union_set_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+union_set_list::~union_set_list() {
+ if (ptr)
+ isl_union_set_list_free(ptr);
+}
+
+__isl_give isl_union_set_list *union_set_list::copy() const & {
+ return isl_union_set_list_copy(ptr);
+}
+
+__isl_keep isl_union_set_list *union_set_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_union_set_list *union_set_list::release() {
+ isl_union_set_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool union_set_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx union_set_list::ctx() const {
+ return isl::ctx(isl_union_set_list_get_ctx(ptr));
+}
+
+isl::union_set_list union_set_list::add(isl::union_set el) const
+{
+ auto res = isl_union_set_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::union_set union_set_list::at(int index) const
+{
+ auto res = isl_union_set_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::union_set union_set_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::union_set_list union_set_list::clear() const
+{
+ auto res = isl_union_set_list_clear(copy());
+ return manage(res);
+}
+
+isl::union_set_list union_set_list::concat(isl::union_set_list list2) const
+{
+ auto res = isl_union_set_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::union_set_list union_set_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_union_set_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat union_set_list::foreach(const std::function<stat(isl::union_set)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::union_set)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_union_set *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_union_set_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::union_set_list union_set_list::insert(unsigned int pos, isl::union_set el) const
+{
+ auto res = isl_union_set_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+class size union_set_list::size() const
+{
+ auto res = isl_union_set_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const union_set_list &obj)
+{
+ char *str = isl_union_set_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::val
+val manage(__isl_take isl_val *ptr) {
+ return val(ptr);
+}
+val manage_copy(__isl_keep isl_val *ptr) {
+ ptr = isl_val_copy(ptr);
+ return val(ptr);
+}
+
+val::val()
+ : ptr(nullptr) {}
+
+val::val(const val &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+val::val(__isl_take isl_val *ptr)
+ : ptr(ptr) {}
+
+val::val(isl::ctx ctx, long i)
+{
+ auto res = isl_val_int_from_si(ctx.release(), i);
+ ptr = res;
+}
+
+val::val(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_val_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+val &val::operator=(val obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+val::~val() {
+ if (ptr)
+ isl_val_free(ptr);
+}
+
+__isl_give isl_val *val::copy() const & {
+ return isl_val_copy(ptr);
+}
+
+__isl_keep isl_val *val::get() const {
+ return ptr;
+}
+
+__isl_give isl_val *val::release() {
+ isl_val *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool val::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx val::ctx() const {
+ return isl::ctx(isl_val_get_ctx(ptr));
+}
+
+isl::val val::abs() const
+{
+ auto res = isl_val_abs(copy());
+ return manage(res);
+}
+
+boolean val::abs_eq(const isl::val &v2) const
+{
+ auto res = isl_val_abs_eq(get(), v2.get());
+ return manage(res);
+}
+
+boolean val::abs_eq(long v2) const
+{
+ return this->abs_eq(isl::val(ctx(), v2));
+}
+
+isl::val val::add(isl::val v2) const
+{
+ auto res = isl_val_add(copy(), v2.release());
+ return manage(res);
+}
+
+isl::val val::add(long v2) const
+{
+ return this->add(isl::val(ctx(), v2));
+}
+
+isl::val val::ceil() const
+{
+ auto res = isl_val_ceil(copy());
+ return manage(res);
+}
+
+int val::cmp_si(long i) const
+{
+ auto res = isl_val_cmp_si(get(), i);
+ return res;
+}
+
+long val::den_si() const
+{
+ auto res = isl_val_get_den_si(get());
+ return res;
+}
+
+long val::get_den_si() const
+{
+ return den_si();
+}
+
+isl::val val::div(isl::val v2) const
+{
+ auto res = isl_val_div(copy(), v2.release());
+ return manage(res);
+}
+
+isl::val val::div(long v2) const
+{
+ return this->div(isl::val(ctx(), v2));
+}
+
+boolean val::eq(const isl::val &v2) const
+{
+ auto res = isl_val_eq(get(), v2.get());
+ return manage(res);
+}
+
+boolean val::eq(long v2) const
+{
+ return this->eq(isl::val(ctx(), v2));
+}
+
+isl::val val::floor() const
+{
+ auto res = isl_val_floor(copy());
+ return manage(res);
+}
+
+isl::val val::gcd(isl::val v2) const
+{
+ auto res = isl_val_gcd(copy(), v2.release());
+ return manage(res);
+}
+
+isl::val val::gcd(long v2) const
+{
+ return this->gcd(isl::val(ctx(), v2));
+}
+
+boolean val::ge(const isl::val &v2) const
+{
+ auto res = isl_val_ge(get(), v2.get());
+ return manage(res);
+}
+
+boolean val::ge(long v2) const
+{
+ return this->ge(isl::val(ctx(), v2));
+}
+
+boolean val::gt(const isl::val &v2) const
+{
+ auto res = isl_val_gt(get(), v2.get());
+ return manage(res);
+}
+
+boolean val::gt(long v2) const
+{
+ return this->gt(isl::val(ctx(), v2));
+}
+
+isl::val val::infty(isl::ctx ctx)
+{
+ auto res = isl_val_infty(ctx.release());
+ return manage(res);
+}
+
+isl::val val::int_from_ui(isl::ctx ctx, unsigned long u)
+{
+ auto res = isl_val_int_from_ui(ctx.release(), u);
+ return manage(res);
+}
+
+isl::val val::inv() const
+{
+ auto res = isl_val_inv(copy());
+ return manage(res);
+}
+
+boolean val::is_divisible_by(const isl::val &v2) const
+{
+ auto res = isl_val_is_divisible_by(get(), v2.get());
+ return manage(res);
+}
+
+boolean val::is_divisible_by(long v2) const
+{
+ return this->is_divisible_by(isl::val(ctx(), v2));
+}
+
+boolean val::is_infty() const
+{
+ auto res = isl_val_is_infty(get());
+ return manage(res);
+}
+
+boolean val::is_int() const
+{
+ auto res = isl_val_is_int(get());
+ return manage(res);
+}
+
+boolean val::is_nan() const
+{
+ auto res = isl_val_is_nan(get());
+ return manage(res);
+}
+
+boolean val::is_neg() const
+{
+ auto res = isl_val_is_neg(get());
+ return manage(res);
+}
+
+boolean val::is_neginfty() const
+{
+ auto res = isl_val_is_neginfty(get());
+ return manage(res);
+}
+
+boolean val::is_negone() const
+{
+ auto res = isl_val_is_negone(get());
+ return manage(res);
+}
+
+boolean val::is_nonneg() const
+{
+ auto res = isl_val_is_nonneg(get());
+ return manage(res);
+}
+
+boolean val::is_nonpos() const
+{
+ auto res = isl_val_is_nonpos(get());
+ return manage(res);
+}
+
+boolean val::is_one() const
+{
+ auto res = isl_val_is_one(get());
+ return manage(res);
+}
+
+boolean val::is_pos() const
+{
+ auto res = isl_val_is_pos(get());
+ return manage(res);
+}
+
+boolean val::is_rat() const
+{
+ auto res = isl_val_is_rat(get());
+ return manage(res);
+}
+
+boolean val::is_zero() const
+{
+ auto res = isl_val_is_zero(get());
+ return manage(res);
+}
+
+boolean val::le(const isl::val &v2) const
+{
+ auto res = isl_val_le(get(), v2.get());
+ return manage(res);
+}
+
+boolean val::le(long v2) const
+{
+ return this->le(isl::val(ctx(), v2));
+}
+
+boolean val::lt(const isl::val &v2) const
+{
+ auto res = isl_val_lt(get(), v2.get());
+ return manage(res);
+}
+
+boolean val::lt(long v2) const
+{
+ return this->lt(isl::val(ctx(), v2));
+}
+
+isl::val val::max(isl::val v2) const
+{
+ auto res = isl_val_max(copy(), v2.release());
+ return manage(res);
+}
+
+isl::val val::max(long v2) const
+{
+ return this->max(isl::val(ctx(), v2));
+}
+
+isl::val val::min(isl::val v2) const
+{
+ auto res = isl_val_min(copy(), v2.release());
+ return manage(res);
+}
+
+isl::val val::min(long v2) const
+{
+ return this->min(isl::val(ctx(), v2));
+}
+
+isl::val val::mod(isl::val v2) const
+{
+ auto res = isl_val_mod(copy(), v2.release());
+ return manage(res);
+}
+
+isl::val val::mod(long v2) const
+{
+ return this->mod(isl::val(ctx(), v2));
+}
+
+isl::val val::mul(isl::val v2) const
+{
+ auto res = isl_val_mul(copy(), v2.release());
+ return manage(res);
+}
+
+isl::val val::mul(long v2) const
+{
+ return this->mul(isl::val(ctx(), v2));
+}
+
+isl::val val::nan(isl::ctx ctx)
+{
+ auto res = isl_val_nan(ctx.release());
+ return manage(res);
+}
+
+boolean val::ne(const isl::val &v2) const
+{
+ auto res = isl_val_ne(get(), v2.get());
+ return manage(res);
+}
+
+boolean val::ne(long v2) const
+{
+ return this->ne(isl::val(ctx(), v2));
+}
+
+isl::val val::neg() const
+{
+ auto res = isl_val_neg(copy());
+ return manage(res);
+}
+
+isl::val val::neginfty(isl::ctx ctx)
+{
+ auto res = isl_val_neginfty(ctx.release());
+ return manage(res);
+}
+
+isl::val val::negone(isl::ctx ctx)
+{
+ auto res = isl_val_negone(ctx.release());
+ return manage(res);
+}
+
+long val::num_si() const
+{
+ auto res = isl_val_get_num_si(get());
+ return res;
+}
+
+long val::get_num_si() const
+{
+ return num_si();
+}
+
+isl::val val::one(isl::ctx ctx)
+{
+ auto res = isl_val_one(ctx.release());
+ return manage(res);
+}
+
+isl::val val::pow2() const
+{
+ auto res = isl_val_pow2(copy());
+ return manage(res);
+}
+
+int val::sgn() const
+{
+ auto res = isl_val_sgn(get());
+ return res;
+}
+
+isl::val val::sub(isl::val v2) const
+{
+ auto res = isl_val_sub(copy(), v2.release());
+ return manage(res);
+}
+
+isl::val val::sub(long v2) const
+{
+ return this->sub(isl::val(ctx(), v2));
+}
+
+isl::val_list val::to_list() const
+{
+ auto res = isl_val_to_list(copy());
+ return manage(res);
+}
+
+isl::val val::trunc() const
+{
+ auto res = isl_val_trunc(copy());
+ return manage(res);
+}
+
+isl::val val::zero(isl::ctx ctx)
+{
+ auto res = isl_val_zero(ctx.release());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const val &obj)
+{
+ char *str = isl_val_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+
+// implementations for isl::val_list
+val_list manage(__isl_take isl_val_list *ptr) {
+ return val_list(ptr);
+}
+val_list manage_copy(__isl_keep isl_val_list *ptr) {
+ ptr = isl_val_list_copy(ptr);
+ return val_list(ptr);
+}
+
+val_list::val_list()
+ : ptr(nullptr) {}
+
+val_list::val_list(const val_list &obj)
+ : ptr(nullptr)
+{
+ ptr = obj.copy();
+}
+
+val_list::val_list(__isl_take isl_val_list *ptr)
+ : ptr(ptr) {}
+
+val_list::val_list(isl::ctx ctx, int n)
+{
+ auto res = isl_val_list_alloc(ctx.release(), n);
+ ptr = res;
+}
+
+val_list::val_list(isl::val el)
+{
+ auto res = isl_val_list_from_val(el.release());
+ ptr = res;
+}
+
+val_list::val_list(isl::ctx ctx, const std::string &str)
+{
+ auto res = isl_val_list_read_from_str(ctx.release(), str.c_str());
+ ptr = res;
+}
+
+val_list &val_list::operator=(val_list obj) {
+ std::swap(this->ptr, obj.ptr);
+ return *this;
+}
+
+val_list::~val_list() {
+ if (ptr)
+ isl_val_list_free(ptr);
+}
+
+__isl_give isl_val_list *val_list::copy() const & {
+ return isl_val_list_copy(ptr);
+}
+
+__isl_keep isl_val_list *val_list::get() const {
+ return ptr;
+}
+
+__isl_give isl_val_list *val_list::release() {
+ isl_val_list *tmp = ptr;
+ ptr = nullptr;
+ return tmp;
+}
+
+bool val_list::is_null() const {
+ return ptr == nullptr;
+}
+
+isl::ctx val_list::ctx() const {
+ return isl::ctx(isl_val_list_get_ctx(ptr));
+}
+
+isl::val_list val_list::add(isl::val el) const
+{
+ auto res = isl_val_list_add(copy(), el.release());
+ return manage(res);
+}
+
+isl::val_list val_list::add(long el) const
+{
+ return this->add(isl::val(ctx(), el));
+}
+
+isl::val val_list::at(int index) const
+{
+ auto res = isl_val_list_get_at(get(), index);
+ return manage(res);
+}
+
+isl::val val_list::get_at(int index) const
+{
+ return at(index);
+}
+
+isl::val_list val_list::clear() const
+{
+ auto res = isl_val_list_clear(copy());
+ return manage(res);
+}
+
+isl::val_list val_list::concat(isl::val_list list2) const
+{
+ auto res = isl_val_list_concat(copy(), list2.release());
+ return manage(res);
+}
+
+isl::val_list val_list::drop(unsigned int first, unsigned int n) const
+{
+ auto res = isl_val_list_drop(copy(), first, n);
+ return manage(res);
+}
+
+stat val_list::foreach(const std::function<stat(isl::val)> &fn) const
+{
+ struct fn_data {
+ std::function<stat(isl::val)> func;
+ } fn_data = { fn };
+ auto fn_lambda = [](isl_val *arg_0, void *arg_1) -> isl_stat {
+ auto *data = static_cast<struct fn_data *>(arg_1);
+ auto ret = (data->func)(manage(arg_0));
+ return ret.release();
+ };
+ auto res = isl_val_list_foreach(get(), fn_lambda, &fn_data);
+ return manage(res);
+}
+
+isl::val_list val_list::insert(unsigned int pos, isl::val el) const
+{
+ auto res = isl_val_list_insert(copy(), pos, el.release());
+ return manage(res);
+}
+
+isl::val_list val_list::insert(unsigned int pos, long el) const
+{
+ return this->insert(pos, isl::val(ctx(), el));
+}
+
+class size val_list::size() const
+{
+ auto res = isl_val_list_size(get());
+ return manage(res);
+}
+
+inline std::ostream &operator<<(std::ostream &os, const val_list &obj)
+{
+ char *str = isl_val_list_to_str(obj.get());
+ if (!str) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+ os << str;
+ free(str);
+ return os;
+}
+} // namespace isl
+
+#endif /* ISL_CPP_CHECKED */
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/list.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/list.h
new file mode 100644
index 00000000000..3d8dabedf05
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/list.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_LIST_H
+#define ISL_LIST_H
+
+#include <isl/ctx.h>
+#include <isl/printer_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define ISL_DECLARE_LIST_TYPE2(EL,EXPORT) \
+struct isl_##EL; \
+struct EXPORT isl_##EL##_list; \
+typedef struct isl_##EL##_list isl_##EL##_list;
+#define ISL_DECLARE_LIST_TYPE(EL) \
+ ISL_DECLARE_LIST_TYPE2(EL,)
+#define ISL_DECLARE_EXPORTED_LIST_TYPE(EL) \
+ ISL_DECLARE_LIST_TYPE2(EL,__isl_export)
+#define ISL_DECLARE_LIST_FN3(EL,CONSTRUCTOR,EXPORT) \
+isl_ctx *isl_##EL##_list_get_ctx(__isl_keep isl_##EL##_list *list); \
+EXPORT \
+__isl_give isl_##EL##_list *isl_##EL##_to_list(__isl_take isl_##EL *el);\
+CONSTRUCTOR \
+__isl_give isl_##EL##_list *isl_##EL##_list_from_##EL( \
+ __isl_take isl_##EL *el); \
+CONSTRUCTOR \
+__isl_give isl_##EL##_list *isl_##EL##_list_alloc(isl_ctx *ctx, int n); \
+__isl_give isl_##EL##_list *isl_##EL##_list_copy( \
+ __isl_keep isl_##EL##_list *list); \
+__isl_null isl_##EL##_list *isl_##EL##_list_free( \
+ __isl_take isl_##EL##_list *list); \
+EXPORT \
+__isl_give isl_##EL##_list *isl_##EL##_list_add( \
+ __isl_take isl_##EL##_list *list, \
+ __isl_take isl_##EL *el); \
+EXPORT \
+__isl_give isl_##EL##_list *isl_##EL##_list_insert( \
+ __isl_take isl_##EL##_list *list, unsigned pos, \
+ __isl_take isl_##EL *el); \
+EXPORT \
+__isl_give isl_##EL##_list *isl_##EL##_list_drop( \
+ __isl_take isl_##EL##_list *list, unsigned first, unsigned n); \
+EXPORT \
+__isl_give isl_##EL##_list *isl_##EL##_list_clear( \
+ __isl_take isl_##EL##_list *list); \
+__isl_give isl_##EL##_list *isl_##EL##_list_swap( \
+ __isl_take isl_##EL##_list *list, unsigned pos1, \
+ unsigned pos2); \
+__isl_give isl_##EL##_list *isl_##EL##_list_reverse( \
+ __isl_take isl_##EL##_list *list); \
+EXPORT \
+__isl_give isl_##EL##_list *isl_##EL##_list_concat( \
+ __isl_take isl_##EL##_list *list1, \
+ __isl_take isl_##EL##_list *list2); \
+EXPORT \
+isl_size isl_##EL##_list_size(__isl_keep isl_##EL##_list *list); \
+isl_size isl_##EL##_list_n_##EL(__isl_keep isl_##EL##_list *list); \
+EXPORT \
+__isl_give isl_##EL *isl_##EL##_list_get_at( \
+ __isl_keep isl_##EL##_list *list, int index); \
+__isl_give struct isl_##EL *isl_##EL##_list_get_##EL( \
+ __isl_keep isl_##EL##_list *list, int index); \
+__isl_give struct isl_##EL##_list *isl_##EL##_list_set_##EL( \
+ __isl_take struct isl_##EL##_list *list, int index, \
+ __isl_take struct isl_##EL *el); \
+EXPORT \
+isl_stat isl_##EL##_list_foreach(__isl_keep isl_##EL##_list *list, \
+ isl_stat (*fn)(__isl_take isl_##EL *el, void *user), \
+ void *user); \
+isl_bool isl_##EL##_list_every(__isl_keep isl_##EL##_list *list, \
+ isl_bool (*test)(__isl_keep isl_##EL *el, void *user), \
+ void *user); \
+__isl_give isl_##EL##_list *isl_##EL##_list_map( \
+ __isl_take isl_##EL##_list *list, \
+ __isl_give isl_##EL * (*fn)(__isl_take isl_##EL *el, \
+ void *user), \
+ void *user); \
+__isl_give isl_##EL##_list *isl_##EL##_list_sort( \
+ __isl_take isl_##EL##_list *list, \
+ int (*cmp)(__isl_keep struct isl_##EL *a, \
+ __isl_keep struct isl_##EL *b, \
+ void *user), void *user); \
+isl_stat isl_##EL##_list_foreach_scc(__isl_keep isl_##EL##_list *list, \
+ isl_bool (*follows)(__isl_keep struct isl_##EL *a, \
+ __isl_keep struct isl_##EL *b, void *user), \
+ void *follows_user, \
+ isl_stat (*fn)(__isl_take isl_##EL##_list *scc, void *user), \
+ void *fn_user); \
+__isl_give char *isl_##EL##_list_to_str( \
+ __isl_keep isl_##EL##_list *list); \
+__isl_give isl_printer *isl_printer_print_##EL##_list( \
+ __isl_take isl_printer *p, __isl_keep isl_##EL##_list *list); \
+void isl_##EL##_list_dump(__isl_keep isl_##EL##_list *list);
+#define ISL_DECLARE_LIST_FN(EL) \
+ ISL_DECLARE_LIST_FN3(EL,,)
+#define ISL_DECLARE_EXPORTED_LIST_FN(EL) \
+ ISL_DECLARE_LIST_FN3(EL,__isl_constructor,__isl_export)
+#define ISL_DECLARE_LIST_FN_READ2(EL,CONSTRUCTOR) \
+CONSTRUCTOR \
+__isl_give isl_##EL##_list *isl_##EL##_list_read_from_str( \
+ isl_ctx *ctx, const char *str);
+#define ISL_DECLARE_LIST_FN_READ(EL) \
+ ISL_DECLARE_LIST_FN_READ2(EL,)
+#define ISL_DECLARE_EXPORTED_LIST_FN_READ(EL) \
+ ISL_DECLARE_LIST_FN_READ2(EL,__isl_constructor)
+
+#define ISL_DECLARE_LIST(EL) \
+ ISL_DECLARE_LIST_TYPE(EL) \
+ ISL_DECLARE_LIST_FN(EL)
+#define ISL_DECLARE_EXPORTED_LIST(EL) \
+ ISL_DECLARE_EXPORTED_LIST_TYPE(EL) \
+ ISL_DECLARE_EXPORTED_LIST_FN(EL)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/local_space.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/local_space.h
new file mode 100644
index 00000000000..51305d2ee02
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/local_space.h
@@ -0,0 +1,98 @@
+#ifndef ISL_LOCAL_SPACE_H
+#define ISL_LOCAL_SPACE_H
+
+#include <isl/aff_type.h>
+#include <isl/space_type.h>
+#include <isl/printer.h>
+#include <isl/map_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_local_space;
+typedef struct isl_local_space isl_local_space;
+
+isl_ctx *isl_local_space_get_ctx(__isl_keep isl_local_space *ls);
+
+__isl_give isl_local_space *isl_local_space_from_space(
+ __isl_take isl_space *space);
+
+__isl_give isl_local_space *isl_local_space_copy(
+ __isl_keep isl_local_space *ls);
+__isl_null isl_local_space *isl_local_space_free(
+ __isl_take isl_local_space *ls);
+
+isl_bool isl_local_space_is_params(__isl_keep isl_local_space *ls);
+isl_bool isl_local_space_is_set(__isl_keep isl_local_space *ls);
+
+__isl_give isl_local_space *isl_local_space_set_tuple_id(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, __isl_take isl_id *id);
+
+isl_size isl_local_space_dim(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type);
+isl_bool isl_local_space_has_dim_name(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos);
+const char *isl_local_space_get_dim_name(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_local_space *isl_local_space_set_dim_name(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos, const char *s);
+isl_bool isl_local_space_has_dim_id(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_id *isl_local_space_get_dim_id(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_local_space *isl_local_space_set_dim_id(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id);
+__isl_give isl_space *isl_local_space_get_space(__isl_keep isl_local_space *ls);
+__isl_give isl_aff *isl_local_space_get_div(__isl_keep isl_local_space *ls,
+ int pos);
+
+int isl_local_space_find_dim_by_name(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type, const char *name);
+
+__isl_give isl_local_space *isl_local_space_domain(
+ __isl_take isl_local_space *ls);
+__isl_give isl_local_space *isl_local_space_range(
+ __isl_take isl_local_space *ls);
+__isl_give isl_local_space *isl_local_space_from_domain(
+ __isl_take isl_local_space *ls);
+__isl_give isl_local_space *isl_local_space_add_dims(
+ __isl_take isl_local_space *ls, enum isl_dim_type type, unsigned n);
+__isl_give isl_local_space *isl_local_space_drop_dims(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_local_space *isl_local_space_insert_dims(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_local_space *isl_local_space_set_from_params(
+ __isl_take isl_local_space *ls);
+
+__isl_give isl_local_space *isl_local_space_intersect(
+ __isl_take isl_local_space *ls1, __isl_take isl_local_space *ls2);
+
+__isl_give isl_local_space *isl_local_space_wrap(
+ __isl_take isl_local_space *ls);
+
+isl_bool isl_local_space_is_equal(__isl_keep isl_local_space *ls1,
+ __isl_keep isl_local_space *ls2);
+
+__isl_give isl_basic_map *isl_local_space_lifting(
+ __isl_take isl_local_space *ls);
+
+__isl_give isl_local_space *isl_local_space_flatten_domain(
+ __isl_take isl_local_space *ls);
+__isl_give isl_local_space *isl_local_space_flatten_range(
+ __isl_take isl_local_space *ls);
+
+__isl_give isl_printer *isl_printer_print_local_space(__isl_take isl_printer *p,
+ __isl_keep isl_local_space *ls);
+void isl_local_space_dump(__isl_keep isl_local_space *ls);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/lp.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/lp.h
new file mode 100644
index 00000000000..4ca39a9fc8b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/lp.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_LP_H
+#define ISL_LP_H
+
+#include <isl/aff.h>
+#include <isl/val_type.h>
+#include <isl/set_type.h>
+
+enum isl_lp_result {
+ isl_lp_error = -1,
+ isl_lp_ok = 0,
+ isl_lp_unbounded,
+ isl_lp_empty
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+__isl_give isl_val *isl_basic_set_min_lp_val(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_aff *obj);
+__isl_give isl_val *isl_basic_set_max_lp_val(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_aff *obj);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map.h
new file mode 100644
index 00000000000..55feb9b3b58
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map.h
@@ -0,0 +1,786 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_MAP_H
+#define ISL_MAP_H
+
+#include <stdio.h>
+
+#include <isl/ctx.h>
+#include <isl/space_type.h>
+#include <isl/vec.h>
+#include <isl/mat.h>
+#include <isl/printer.h>
+#include <isl/local_space.h>
+#include <isl/aff_type.h>
+#include <isl/list.h>
+#include <isl/map_type.h>
+#include <isl/val_type.h>
+#include <isl/stdint.h>
+#include <isl/stride_info.h>
+#include <isl/fixed_box.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+isl_size isl_basic_map_total_dim(__isl_keep const isl_basic_map *bmap);
+isl_size isl_basic_map_dim(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type);
+
+__isl_export
+isl_size isl_map_domain_tuple_dim(__isl_keep isl_map *map);
+__isl_export
+isl_size isl_map_range_tuple_dim(__isl_keep isl_map *map);
+isl_size isl_map_dim(__isl_keep isl_map *map, enum isl_dim_type type);
+
+isl_ctx *isl_basic_map_get_ctx(__isl_keep isl_basic_map *bmap);
+isl_ctx *isl_map_get_ctx(__isl_keep isl_map *map);
+__isl_give isl_space *isl_basic_map_get_space(__isl_keep isl_basic_map *bmap);
+__isl_export
+__isl_give isl_space *isl_map_get_space(__isl_keep isl_map *map);
+
+__isl_give isl_aff *isl_basic_map_get_div(__isl_keep isl_basic_map *bmap,
+ int pos);
+
+__isl_give isl_local_space *isl_basic_map_get_local_space(
+ __isl_keep isl_basic_map *bmap);
+
+__isl_give isl_basic_map *isl_basic_map_set_tuple_name(
+ __isl_take isl_basic_map *bmap, enum isl_dim_type type, const char *s);
+const char *isl_basic_map_get_tuple_name(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type);
+isl_bool isl_map_has_tuple_name(__isl_keep isl_map *map,
+ enum isl_dim_type type);
+const char *isl_map_get_tuple_name(__isl_keep isl_map *map,
+ enum isl_dim_type type);
+__isl_give isl_map *isl_map_set_tuple_name(__isl_take isl_map *map,
+ enum isl_dim_type type, const char *s);
+const char *isl_basic_map_get_dim_name(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos);
+isl_bool isl_map_has_dim_name(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos);
+const char *isl_map_get_dim_name(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_basic_map *isl_basic_map_set_dim_name(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, const char *s);
+__isl_give isl_map *isl_map_set_dim_name(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, const char *s);
+
+__isl_give isl_basic_map *isl_basic_map_set_tuple_id(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, __isl_take isl_id *id);
+__isl_give isl_map *isl_map_set_dim_id(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id);
+isl_bool isl_basic_map_has_dim_id(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos);
+isl_bool isl_map_has_dim_id(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_id *isl_map_get_dim_id(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos);
+__isl_overload
+__isl_give isl_map *isl_map_set_domain_tuple_id(__isl_take isl_map *map,
+ __isl_take isl_id *id);
+__isl_overload
+__isl_give isl_map *isl_map_set_range_tuple_id(__isl_take isl_map *map,
+ __isl_take isl_id *id);
+__isl_give isl_map *isl_map_set_tuple_id(__isl_take isl_map *map,
+ enum isl_dim_type type, __isl_take isl_id *id);
+__isl_give isl_map *isl_map_reset_tuple_id(__isl_take isl_map *map,
+ enum isl_dim_type type);
+__isl_export
+isl_bool isl_map_has_domain_tuple_id(__isl_keep isl_map *map);
+__isl_export
+isl_bool isl_map_has_range_tuple_id(__isl_keep isl_map *map);
+isl_bool isl_map_has_tuple_id(__isl_keep isl_map *map, enum isl_dim_type type);
+__isl_export
+__isl_give isl_id *isl_map_get_domain_tuple_id(__isl_keep isl_map *map);
+__isl_export
+__isl_give isl_id *isl_map_get_range_tuple_id(__isl_keep isl_map *map);
+__isl_give isl_id *isl_map_get_tuple_id(__isl_keep isl_map *map,
+ enum isl_dim_type type);
+__isl_give isl_map *isl_map_reset_user(__isl_take isl_map *map);
+
+int isl_basic_map_find_dim_by_name(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, const char *name);
+int isl_map_find_dim_by_id(__isl_keep isl_map *map, enum isl_dim_type type,
+ __isl_keep isl_id *id);
+int isl_map_find_dim_by_name(__isl_keep isl_map *map, enum isl_dim_type type,
+ const char *name);
+
+isl_bool isl_basic_map_is_rational(__isl_keep isl_basic_map *bmap);
+
+__isl_give isl_basic_map *isl_basic_map_identity(__isl_take isl_space *space);
+__isl_null isl_basic_map *isl_basic_map_free(__isl_take isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_copy(__isl_keep isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_equal(
+ __isl_take isl_space *space, unsigned n_equal);
+__isl_give isl_basic_map *isl_basic_map_less_at(__isl_take isl_space *space,
+ unsigned pos);
+__isl_give isl_basic_map *isl_basic_map_more_at(__isl_take isl_space *space,
+ unsigned pos);
+__isl_give isl_basic_map *isl_basic_map_empty(__isl_take isl_space *space);
+__isl_give isl_basic_map *isl_basic_map_universe(__isl_take isl_space *space);
+__isl_give isl_basic_map *isl_basic_map_nat_universe(
+ __isl_take isl_space *space);
+__isl_give isl_basic_map *isl_basic_map_remove_redundancies(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_map *isl_map_remove_redundancies(__isl_take isl_map *map);
+__isl_give isl_basic_map *isl_map_simple_hull(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_basic_map *isl_map_unshifted_simple_hull(
+ __isl_take isl_map *map);
+__isl_give isl_basic_map *isl_map_plain_unshifted_simple_hull(
+ __isl_take isl_map *map);
+__isl_give isl_basic_map *isl_map_unshifted_simple_hull_from_map_list(
+ __isl_take isl_map *map, __isl_take isl_map_list *list);
+
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_intersect_domain(
+ __isl_take isl_basic_map *bmap,
+ __isl_take isl_basic_set *bset);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_intersect_range(
+ __isl_take isl_basic_map *bmap,
+ __isl_take isl_basic_set *bset);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_intersect(
+ __isl_take isl_basic_map *bmap1,
+ __isl_take isl_basic_map *bmap2);
+__isl_give isl_basic_map *isl_basic_map_list_intersect(
+ __isl_take isl_basic_map_list *list);
+__isl_export
+__isl_give isl_map *isl_basic_map_union(
+ __isl_take isl_basic_map *bmap1,
+ __isl_take isl_basic_map *bmap2);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_apply_domain(
+ __isl_take isl_basic_map *bmap1,
+ __isl_take isl_basic_map *bmap2);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_apply_range(
+ __isl_take isl_basic_map *bmap1,
+ __isl_take isl_basic_map *bmap2);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_affine_hull(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_preimage_domain_multi_aff(
+ __isl_take isl_basic_map *bmap, __isl_take isl_multi_aff *ma);
+__isl_give isl_basic_map *isl_basic_map_preimage_range_multi_aff(
+ __isl_take isl_basic_map *bmap, __isl_take isl_multi_aff *ma);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_reverse(__isl_take isl_basic_map *bmap);
+__isl_give isl_basic_set *isl_basic_map_domain(__isl_take isl_basic_map *bmap);
+__isl_give isl_basic_set *isl_basic_map_range(__isl_take isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_domain_map(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_range_map(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_remove_dims(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_basic_map *isl_basic_map_eliminate(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_sample(__isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_detect_equalities(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_read_from_file(isl_ctx *ctx,
+ FILE *input);
+__isl_constructor
+__isl_give isl_basic_map *isl_basic_map_read_from_str(isl_ctx *ctx,
+ const char *str);
+__isl_give isl_map *isl_map_read_from_file(isl_ctx *ctx, FILE *input);
+__isl_constructor
+__isl_give isl_map *isl_map_read_from_str(isl_ctx *ctx, const char *str);
+void isl_basic_map_dump(__isl_keep isl_basic_map *bmap);
+void isl_map_dump(__isl_keep isl_map *map);
+__isl_give char *isl_basic_map_to_str(__isl_keep isl_basic_map *bmap);
+__isl_give isl_printer *isl_printer_print_basic_map(
+ __isl_take isl_printer *printer, __isl_keep isl_basic_map *bmap);
+__isl_give char *isl_map_to_str(__isl_keep isl_map *map);
+__isl_give isl_printer *isl_printer_print_map(__isl_take isl_printer *printer,
+ __isl_keep isl_map *map);
+__isl_give isl_basic_map *isl_basic_map_fix_si(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, int value);
+__isl_give isl_basic_map *isl_basic_map_fix_val(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *v);
+__isl_give isl_basic_map *isl_basic_map_lower_bound_si(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, int value);
+__isl_give isl_basic_map *isl_basic_map_upper_bound_si(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, int value);
+__isl_overload
+__isl_give isl_map *isl_map_lower_bound_multi_pw_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *lower);
+__isl_overload
+__isl_give isl_map *isl_map_upper_bound_multi_pw_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *upper);
+
+__isl_give isl_basic_map *isl_basic_map_sum(__isl_take isl_basic_map *bmap1,
+ __isl_take isl_basic_map *bmap2);
+__isl_give isl_basic_map *isl_basic_map_neg(__isl_take isl_basic_map *bmap);
+
+__isl_give isl_map *isl_map_sum(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_give isl_map *isl_map_neg(__isl_take isl_map *map);
+__isl_give isl_map *isl_map_floordiv_val(__isl_take isl_map *map,
+ __isl_take isl_val *d);
+
+__isl_export
+isl_bool isl_basic_map_is_equal(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2);
+isl_bool isl_basic_map_is_disjoint(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2);
+
+__isl_give isl_map *isl_basic_map_partial_lexmax(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty);
+__isl_give isl_map *isl_basic_map_partial_lexmin(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty);
+__isl_give isl_map *isl_map_partial_lexmax(
+ __isl_take isl_map *map, __isl_take isl_set *dom,
+ __isl_give isl_set **empty);
+__isl_give isl_map *isl_map_partial_lexmin(
+ __isl_take isl_map *map, __isl_take isl_set *dom,
+ __isl_give isl_set **empty);
+__isl_export
+__isl_give isl_map *isl_basic_map_lexmin(__isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_map *isl_basic_map_lexmax(__isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_map *isl_map_lexmin(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_lexmax(__isl_take isl_map *map);
+__isl_give isl_pw_multi_aff *isl_basic_map_partial_lexmin_pw_multi_aff(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty);
+__isl_give isl_pw_multi_aff *isl_basic_map_partial_lexmax_pw_multi_aff(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty);
+__isl_give isl_pw_multi_aff *isl_basic_map_lexmin_pw_multi_aff(
+ __isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_map_lexmin_pw_multi_aff(
+ __isl_take isl_map *map);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_map_lexmax_pw_multi_aff(
+ __isl_take isl_map *map);
+__isl_export
+__isl_give isl_multi_pw_aff *isl_map_min_multi_pw_aff(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_multi_pw_aff *isl_map_max_multi_pw_aff(__isl_take isl_map *map);
+
+void isl_basic_map_print_internal(__isl_keep isl_basic_map *bmap,
+ FILE *out, int indent);
+
+__isl_give isl_val *isl_basic_map_plain_get_val_if_fixed(
+ __isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos);
+
+isl_bool isl_basic_map_image_is_bounded(__isl_keep isl_basic_map *bmap);
+isl_bool isl_basic_map_plain_is_universe(__isl_keep isl_basic_map *bmap);
+isl_bool isl_basic_map_is_universe(__isl_keep isl_basic_map *bmap);
+isl_bool isl_basic_map_plain_is_empty(__isl_keep isl_basic_map *bmap);
+__isl_export
+isl_bool isl_basic_map_is_empty(__isl_keep isl_basic_map *bmap);
+__isl_export
+isl_bool isl_basic_map_is_subset(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2);
+isl_bool isl_basic_map_is_strict_subset(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2);
+
+__isl_export
+__isl_give isl_map *isl_map_universe(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_map *isl_space_universe_map(__isl_take isl_space *space);
+__isl_give isl_map *isl_map_nat_universe(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_map *isl_map_empty(__isl_take isl_space *space);
+__isl_give isl_map *isl_map_identity(__isl_take isl_space *space);
+__isl_give isl_map *isl_map_lex_lt_first(__isl_take isl_space *space,
+ unsigned n);
+__isl_give isl_map *isl_map_lex_le_first(__isl_take isl_space *space,
+ unsigned n);
+__isl_give isl_map *isl_map_lex_lt(__isl_take isl_space *set_space);
+__isl_give isl_map *isl_map_lex_le(__isl_take isl_space *set_space);
+__isl_give isl_map *isl_map_lex_gt_first(__isl_take isl_space *space,
+ unsigned n);
+__isl_give isl_map *isl_map_lex_ge_first(__isl_take isl_space *space,
+ unsigned n);
+__isl_give isl_map *isl_map_lex_gt(__isl_take isl_space *set_space);
+__isl_give isl_map *isl_map_lex_ge(__isl_take isl_space *set_space);
+__isl_null isl_map *isl_map_free(__isl_take isl_map *map);
+__isl_give isl_map *isl_map_copy(__isl_keep isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_reverse(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_range_reverse(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_union(
+ __isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_give isl_map *isl_map_union_disjoint(
+ __isl_take isl_map *map1, __isl_take isl_map *map2);
+__isl_export
+__isl_give isl_map *isl_map_intersect_domain(
+ __isl_take isl_map *map,
+ __isl_take isl_set *set);
+__isl_export
+__isl_give isl_map *isl_map_intersect_range(
+ __isl_take isl_map *map,
+ __isl_take isl_set *set);
+__isl_export
+__isl_give isl_map *isl_map_intersect_domain_factor_domain(
+ __isl_take isl_map *map, __isl_take isl_map *factor);
+__isl_export
+__isl_give isl_map *isl_map_intersect_domain_factor_range(
+ __isl_take isl_map *map, __isl_take isl_map *factor);
+__isl_export
+__isl_give isl_map *isl_map_intersect_range_factor_domain(
+ __isl_take isl_map *map, __isl_take isl_map *factor);
+__isl_export
+__isl_give isl_map *isl_map_intersect_range_factor_range(
+ __isl_take isl_map *map, __isl_take isl_map *factor);
+__isl_export
+__isl_give isl_map *isl_map_apply_domain(
+ __isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_export
+__isl_give isl_map *isl_map_apply_range(
+ __isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_overload
+__isl_give isl_map *isl_map_preimage_domain_multi_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_aff *ma);
+__isl_overload
+__isl_give isl_map *isl_map_preimage_range_multi_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_aff *ma);
+__isl_overload
+__isl_give isl_map *isl_map_preimage_domain_pw_multi_aff(
+ __isl_take isl_map *map, __isl_take isl_pw_multi_aff *pma);
+__isl_overload
+__isl_give isl_map *isl_map_preimage_range_pw_multi_aff(
+ __isl_take isl_map *map, __isl_take isl_pw_multi_aff *pma);
+__isl_overload
+__isl_give isl_map *isl_map_preimage_domain_multi_pw_aff(
+ __isl_take isl_map *map, __isl_take isl_multi_pw_aff *mpa);
+__isl_give isl_basic_map *isl_basic_map_product(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2);
+__isl_export
+__isl_give isl_map *isl_map_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_give isl_basic_map *isl_basic_map_domain_product(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2);
+__isl_give isl_basic_map *isl_basic_map_range_product(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2);
+__isl_export
+__isl_give isl_map *isl_map_domain_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_export
+__isl_give isl_map *isl_map_range_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_give isl_basic_map *isl_basic_map_flat_product(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2);
+__isl_give isl_map *isl_map_flat_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_give isl_basic_map *isl_basic_map_flat_range_product(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2);
+__isl_give isl_map *isl_map_flat_domain_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_give isl_map *isl_map_flat_range_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+isl_bool isl_map_domain_is_wrapping(__isl_keep isl_map *map);
+isl_bool isl_map_range_is_wrapping(__isl_keep isl_map *map);
+isl_bool isl_map_is_product(__isl_keep isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_factor_domain(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_factor_range(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_domain_factor_domain(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_domain_factor_range(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_range_factor_domain(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_range_factor_range(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_intersect(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_export
+__isl_give isl_map *isl_map_intersect_params(__isl_take isl_map *map,
+ __isl_take isl_set *params);
+__isl_export
+__isl_give isl_map *isl_map_subtract(
+ __isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_give isl_map *isl_map_subtract_domain(__isl_take isl_map *map,
+ __isl_take isl_set *dom);
+__isl_give isl_map *isl_map_subtract_range(__isl_take isl_map *map,
+ __isl_take isl_set *dom);
+__isl_export
+__isl_give isl_map *isl_map_complement(__isl_take isl_map *map);
+__isl_give isl_map *isl_map_fix_input_si(__isl_take isl_map *map,
+ unsigned input, int value);
+__isl_give isl_map *isl_map_fix_si(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, int value);
+__isl_give isl_map *isl_map_fix_val(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *v);
+__isl_give isl_map *isl_map_lower_bound_si(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, int value);
+__isl_give isl_map *isl_map_lower_bound_val(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *value);
+__isl_give isl_map *isl_map_upper_bound_si(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, int value);
+__isl_give isl_map *isl_map_upper_bound_val(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *value);
+__isl_export
+__isl_give isl_basic_set *isl_basic_map_deltas(__isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_set *isl_map_deltas(__isl_take isl_map *map);
+__isl_give isl_basic_map *isl_basic_map_deltas_map(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_map *isl_map_deltas_map(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_detect_equalities(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_basic_map *isl_map_affine_hull(__isl_take isl_map *map);
+__isl_give isl_basic_map *isl_map_convex_hull(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_basic_map *isl_map_polyhedral_hull(__isl_take isl_map *map);
+__isl_give isl_basic_map *isl_basic_map_add_dims(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned n);
+__isl_give isl_map *isl_map_add_dims(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned n);
+__isl_give isl_basic_map *isl_basic_map_insert_dims(
+ __isl_take isl_basic_map *bmap, enum isl_dim_type type,
+ unsigned pos, unsigned n);
+__isl_give isl_map *isl_map_insert_dims(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, unsigned n);
+__isl_give isl_basic_map *isl_basic_map_move_dims(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+__isl_give isl_map *isl_map_move_dims(__isl_take isl_map *map,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+__isl_give isl_basic_map *isl_basic_map_project_out(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_map *isl_map_project_out(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_export
+__isl_give isl_map *isl_map_project_out_all_params(__isl_take isl_map *map);
+__isl_give isl_basic_map *isl_basic_map_remove_divs(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_map *isl_map_remove_unknown_divs(__isl_take isl_map *map);
+__isl_give isl_map *isl_map_remove_divs(__isl_take isl_map *map);
+__isl_give isl_map *isl_map_eliminate(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_map *isl_map_remove_dims(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_basic_map *isl_basic_map_remove_divs_involving_dims(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_map *isl_map_remove_divs_involving_dims(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_map *isl_map_remove_inputs(__isl_take isl_map *map,
+ unsigned first, unsigned n);
+
+__isl_give isl_basic_map *isl_basic_map_equate(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2);
+__isl_give isl_basic_map *isl_basic_map_order_ge(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2);
+__isl_give isl_map *isl_map_order_ge(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2);
+__isl_give isl_map *isl_map_order_le(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2);
+__isl_give isl_map *isl_map_equate(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2);
+__isl_give isl_map *isl_map_oppose(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2);
+__isl_give isl_map *isl_map_order_lt(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2);
+__isl_give isl_basic_map *isl_basic_map_order_gt(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2);
+__isl_give isl_map *isl_map_order_gt(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2);
+
+__isl_export
+__isl_give isl_map *isl_set_translation(__isl_take isl_set *deltas);
+__isl_export
+__isl_give isl_map *isl_set_identity(__isl_take isl_set *set);
+
+__isl_export
+isl_bool isl_basic_set_is_wrapping(__isl_keep isl_basic_set *bset);
+__isl_export
+isl_bool isl_set_is_wrapping(__isl_keep isl_set *set);
+__isl_give isl_basic_set *isl_basic_map_wrap(__isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_set *isl_map_wrap(__isl_take isl_map *map);
+__isl_give isl_basic_map *isl_basic_set_unwrap(__isl_take isl_basic_set *bset);
+__isl_export
+__isl_give isl_map *isl_set_unwrap(__isl_take isl_set *set);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_flatten(__isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_map *isl_map_flatten(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_flatten_domain(
+ __isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_flatten_range(
+ __isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_map *isl_map_flatten_domain(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_map_flatten_range(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_basic_set *isl_basic_set_flatten(__isl_take isl_basic_set *bset);
+__isl_export
+__isl_give isl_set *isl_set_flatten(__isl_take isl_set *set);
+__isl_give isl_map *isl_set_flatten_map(__isl_take isl_set *set);
+__isl_give isl_set *isl_map_params(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_set *isl_map_domain(__isl_take isl_map *bmap);
+__isl_export
+__isl_give isl_set *isl_map_range(__isl_take isl_map *map);
+__isl_export
+__isl_give isl_map *isl_set_insert_domain(__isl_take isl_set *set,
+ __isl_take isl_space *domain);
+__isl_give isl_map *isl_map_domain_map(__isl_take isl_map *map);
+__isl_give isl_map *isl_map_range_map(__isl_take isl_map *map);
+__isl_give isl_map *isl_set_wrapped_domain_map(__isl_take isl_set *set);
+__isl_constructor
+__isl_give isl_map *isl_map_from_basic_map(__isl_take isl_basic_map *bmap);
+__isl_give isl_map *isl_map_from_domain(__isl_take isl_set *set);
+__isl_give isl_basic_map *isl_basic_map_from_domain(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_map *isl_basic_map_from_range(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_map *isl_map_from_range(__isl_take isl_set *set);
+__isl_give isl_basic_map *isl_basic_map_from_domain_and_range(
+ __isl_take isl_basic_set *domain, __isl_take isl_basic_set *range);
+__isl_give isl_map *isl_map_from_domain_and_range(__isl_take isl_set *domain,
+ __isl_take isl_set *range);
+__isl_export
+__isl_give isl_basic_map *isl_map_sample(__isl_take isl_map *map);
+
+__isl_export
+__isl_give isl_set *isl_map_bind_domain(__isl_take isl_map *map,
+ __isl_take isl_multi_id *tuple);
+__isl_export
+__isl_give isl_set *isl_map_bind_range(__isl_take isl_map *map,
+ __isl_take isl_multi_id *tuple);
+
+isl_bool isl_map_plain_is_empty(__isl_keep isl_map *map);
+isl_bool isl_map_plain_is_universe(__isl_keep isl_map *map);
+__isl_export
+isl_bool isl_map_is_empty(__isl_keep isl_map *map);
+__isl_export
+isl_bool isl_map_is_subset(__isl_keep isl_map *map1, __isl_keep isl_map *map2);
+__isl_export
+isl_bool isl_map_is_strict_subset(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2);
+__isl_export
+isl_bool isl_map_is_equal(__isl_keep isl_map *map1, __isl_keep isl_map *map2);
+__isl_export
+isl_bool isl_map_is_disjoint(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2);
+isl_bool isl_basic_map_is_single_valued(__isl_keep isl_basic_map *bmap);
+isl_bool isl_map_plain_is_single_valued(__isl_keep isl_map *map);
+__isl_export
+isl_bool isl_map_is_single_valued(__isl_keep isl_map *map);
+isl_bool isl_map_plain_is_injective(__isl_keep isl_map *map);
+__isl_export
+isl_bool isl_map_is_injective(__isl_keep isl_map *map);
+__isl_export
+isl_bool isl_map_is_bijective(__isl_keep isl_map *map);
+isl_bool isl_map_is_identity(__isl_keep isl_map *map);
+int isl_map_is_translation(__isl_keep isl_map *map);
+isl_bool isl_map_has_equal_space(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2);
+
+isl_bool isl_basic_map_can_zip(__isl_keep isl_basic_map *bmap);
+isl_bool isl_map_can_zip(__isl_keep isl_map *map);
+__isl_give isl_basic_map *isl_basic_map_zip(__isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_map *isl_map_zip(__isl_take isl_map *map);
+
+isl_bool isl_basic_map_can_curry(__isl_keep isl_basic_map *bmap);
+isl_bool isl_map_can_curry(__isl_keep isl_map *map);
+__isl_give isl_basic_map *isl_basic_map_curry(__isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_map *isl_map_curry(__isl_take isl_map *map);
+
+isl_bool isl_map_can_range_curry(__isl_keep isl_map *map);
+__isl_give isl_map *isl_map_range_curry(__isl_take isl_map *map);
+
+isl_bool isl_basic_map_can_uncurry(__isl_keep isl_basic_map *bmap);
+isl_bool isl_map_can_uncurry(__isl_keep isl_map *map);
+__isl_give isl_basic_map *isl_basic_map_uncurry(__isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_map *isl_map_uncurry(__isl_take isl_map *map);
+
+__isl_give isl_map *isl_map_make_disjoint(__isl_take isl_map *map);
+__isl_give isl_map *isl_basic_map_compute_divs(__isl_take isl_basic_map *bmap);
+__isl_give isl_map *isl_map_compute_divs(__isl_take isl_map *map);
+ISL_DEPRECATED
+__isl_give isl_map *isl_map_align_divs(__isl_take isl_map *map);
+
+__isl_give isl_basic_map *isl_basic_map_drop_constraints_involving_dims(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_basic_map *isl_basic_map_drop_constraints_not_involving_dims(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_map *isl_map_drop_constraints_involving_dims(
+ __isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_map *isl_map_drop_constraints_not_involving_dims(
+ __isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+isl_bool isl_basic_map_involves_dims(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n);
+isl_bool isl_map_involves_dims(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+void isl_map_print_internal(__isl_keep isl_map *map, FILE *out, int indent);
+
+__isl_give isl_val *isl_map_plain_get_val_if_fixed(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos);
+
+__isl_give isl_basic_map *isl_basic_map_gist_domain(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *context);
+__isl_export
+__isl_give isl_basic_map *isl_basic_map_gist(__isl_take isl_basic_map *bmap,
+ __isl_take isl_basic_map *context);
+__isl_export
+__isl_give isl_map *isl_map_gist(__isl_take isl_map *map,
+ __isl_take isl_map *context);
+__isl_export
+__isl_give isl_map *isl_map_gist_domain(__isl_take isl_map *map,
+ __isl_take isl_set *context);
+__isl_give isl_map *isl_map_gist_range(__isl_take isl_map *map,
+ __isl_take isl_set *context);
+__isl_give isl_map *isl_map_gist_params(__isl_take isl_map *map,
+ __isl_take isl_set *context);
+__isl_give isl_map *isl_map_gist_basic_map(__isl_take isl_map *map,
+ __isl_take isl_basic_map *context);
+
+__isl_give isl_stride_info *isl_map_get_range_stride_info(
+ __isl_keep isl_map *map, int pos);
+__isl_export
+__isl_give isl_fixed_box *isl_map_get_range_lattice_tile(
+ __isl_keep isl_map *map);
+__isl_export
+__isl_give isl_fixed_box *isl_map_get_range_simple_fixed_box_hull(
+ __isl_keep isl_map *map);
+
+__isl_export
+__isl_give isl_map *isl_map_coalesce(__isl_take isl_map *map);
+
+isl_bool isl_map_plain_is_equal(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2);
+
+uint32_t isl_map_get_hash(__isl_keep isl_map *map);
+
+__isl_export
+isl_size isl_map_n_basic_map(__isl_keep isl_map *map);
+__isl_export
+isl_stat isl_map_foreach_basic_map(__isl_keep isl_map *map,
+ isl_stat (*fn)(__isl_take isl_basic_map *bmap, void *user), void *user);
+__isl_give isl_basic_map_list *isl_map_get_basic_map_list(
+ __isl_keep isl_map *map);
+
+__isl_give isl_map *isl_map_fixed_power_val(__isl_take isl_map *map,
+ __isl_take isl_val *exp);
+__isl_give isl_map *isl_map_power(__isl_take isl_map *map, isl_bool *exact);
+__isl_give isl_map *isl_map_reaching_path_lengths(__isl_take isl_map *map,
+ isl_bool *exact);
+__isl_give isl_map *isl_map_transitive_closure(__isl_take isl_map *map,
+ isl_bool *exact);
+
+__isl_give isl_map *isl_map_lex_le_map(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_give isl_map *isl_map_lex_lt_map(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_give isl_map *isl_map_lex_ge_map(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+__isl_give isl_map *isl_map_lex_gt_map(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+
+__isl_overload
+__isl_give isl_map *isl_map_eq_at_multi_pw_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *mpa);
+__isl_overload
+__isl_give isl_map *isl_map_lex_lt_at_multi_pw_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *mpa);
+__isl_overload
+__isl_give isl_map *isl_map_lex_le_at_multi_pw_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *mpa);
+__isl_overload
+__isl_give isl_map *isl_map_lex_gt_at_multi_pw_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *mpa);
+__isl_overload
+__isl_give isl_map *isl_map_lex_ge_at_multi_pw_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *mpa);
+
+__isl_give isl_basic_map *isl_basic_map_align_params(
+ __isl_take isl_basic_map *bmap, __isl_take isl_space *model);
+__isl_give isl_map *isl_map_align_params(__isl_take isl_map *map,
+ __isl_take isl_space *model);
+__isl_give isl_basic_map *isl_basic_map_drop_unused_params(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_map *isl_map_drop_unused_params(__isl_take isl_map *map);
+
+__isl_give isl_mat *isl_basic_map_equalities_matrix(
+ __isl_keep isl_basic_map *bmap, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3,
+ enum isl_dim_type c4, enum isl_dim_type c5);
+__isl_give isl_mat *isl_basic_map_inequalities_matrix(
+ __isl_keep isl_basic_map *bmap, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3,
+ enum isl_dim_type c4, enum isl_dim_type c5);
+__isl_give isl_basic_map *isl_basic_map_from_constraint_matrices(
+ __isl_take isl_space *space,
+ __isl_take isl_mat *eq, __isl_take isl_mat *ineq, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3,
+ enum isl_dim_type c4, enum isl_dim_type c5);
+
+__isl_give isl_basic_map *isl_basic_map_from_aff(__isl_take isl_aff *aff);
+__isl_give isl_basic_map *isl_basic_map_from_multi_aff(
+ __isl_take isl_multi_aff *maff);
+__isl_give isl_basic_map *isl_basic_map_from_aff_list(
+ __isl_take isl_space *domain_space, __isl_take isl_aff_list *list);
+
+__isl_give isl_map *isl_map_from_aff(__isl_take isl_aff *aff);
+__isl_export
+__isl_give isl_map *isl_multi_aff_as_map(__isl_take isl_multi_aff *ma);
+__isl_give isl_map *isl_map_from_multi_aff(__isl_take isl_multi_aff *maff);
+
+__isl_give isl_pw_aff *isl_map_dim_min(__isl_take isl_map *map, int pos);
+__isl_give isl_pw_aff *isl_map_dim_max(__isl_take isl_map *map, int pos);
+
+ISL_DECLARE_LIST_FN(basic_map)
+ISL_DECLARE_EXPORTED_LIST_FN(map)
+ISL_DECLARE_EXPORTED_LIST_FN_READ(map)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map_to_basic_set.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map_to_basic_set.h
new file mode 100644
index 00000000000..80e9ab5bed2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map_to_basic_set.h
@@ -0,0 +1,18 @@
+#ifndef ISL_MAP_TO_BASIC_SET_H
+#define ISL_MAP_TO_BASIC_SET_H
+
+#include <isl/set_type.h>
+#include <isl/map_type.h>
+#include <isl/maybe_basic_set.h>
+
+#define ISL_KEY isl_map
+#define ISL_VAL isl_basic_set
+#define ISL_HMAP_SUFFIX map_to_basic_set
+#define ISL_HMAP isl_map_to_basic_set
+#include <isl/hmap.h>
+#undef ISL_KEY
+#undef ISL_VAL
+#undef ISL_HMAP_SUFFIX
+#undef ISL_HMAP
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map_type.h
new file mode 100644
index 00000000000..731dfeddda8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/map_type.h
@@ -0,0 +1,38 @@
+#ifndef ISL_MAP_TYPE_H
+#define ISL_MAP_TYPE_H
+
+#include <isl/ctx.h>
+#include <isl/list.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct __isl_subclass(isl_map) isl_basic_map;
+typedef struct isl_basic_map isl_basic_map;
+ISL_DECLARE_LIST_TYPE(basic_map)
+struct __isl_subclass(isl_union_map) isl_map;
+typedef struct isl_map isl_map;
+ISL_DECLARE_EXPORTED_LIST_TYPE(map)
+
+#ifndef isl_basic_set
+struct __isl_subclass(isl_set) isl_basic_set;
+typedef struct isl_basic_set isl_basic_set;
+ISL_DECLARE_LIST_TYPE(basic_set)
+#endif
+
+#ifndef isl_set
+struct __isl_subclass(isl_union_set) isl_set;
+typedef struct isl_set isl_set;
+ISL_DECLARE_EXPORTED_LIST_TYPE(set)
+#endif
+
+ISL_DECLARE_LIST_FN(basic_set)
+ISL_DECLARE_EXPORTED_LIST_FN(set)
+ISL_DECLARE_EXPORTED_LIST_FN_READ(set)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/mat.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/mat.h
new file mode 100644
index 00000000000..0ec6b935cf4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/mat.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_MAT_H
+#define ISL_MAT_H
+
+#include <stdio.h>
+
+#include <isl/ctx.h>
+#include <isl/vec.h>
+#include <isl/val_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_mat;
+typedef struct isl_mat isl_mat;
+
+isl_ctx *isl_mat_get_ctx(__isl_keep isl_mat *mat);
+
+__isl_give isl_mat *isl_mat_alloc(isl_ctx *ctx,
+ unsigned n_row, unsigned n_col);
+__isl_give isl_mat *isl_mat_extend(__isl_take isl_mat *mat,
+ unsigned n_row, unsigned n_col);
+__isl_give isl_mat *isl_mat_identity(isl_ctx *ctx, unsigned n_row);
+__isl_give isl_mat *isl_mat_copy(__isl_keep isl_mat *mat);
+__isl_null isl_mat *isl_mat_free(__isl_take isl_mat *mat);
+
+isl_size isl_mat_rows(__isl_keep isl_mat *mat);
+isl_size isl_mat_cols(__isl_keep isl_mat *mat);
+__isl_give isl_val *isl_mat_get_element_val(__isl_keep isl_mat *mat,
+ int row, int col);
+__isl_give isl_mat *isl_mat_set_element_si(__isl_take isl_mat *mat,
+ int row, int col, int v);
+__isl_give isl_mat *isl_mat_set_element_val(__isl_take isl_mat *mat,
+ int row, int col, __isl_take isl_val *v);
+
+__isl_give isl_mat *isl_mat_swap_cols(__isl_take isl_mat *mat,
+ unsigned i, unsigned j);
+__isl_give isl_mat *isl_mat_swap_rows(__isl_take isl_mat *mat,
+ unsigned i, unsigned j);
+
+__isl_give isl_vec *isl_mat_vec_product(__isl_take isl_mat *mat,
+ __isl_take isl_vec *vec);
+__isl_give isl_vec *isl_vec_mat_product(__isl_take isl_vec *vec,
+ __isl_take isl_mat *mat);
+__isl_give isl_vec *isl_mat_vec_inverse_product(__isl_take isl_mat *mat,
+ __isl_take isl_vec *vec);
+__isl_give isl_mat *isl_mat_aff_direct_sum(__isl_take isl_mat *left,
+ __isl_take isl_mat *right);
+__isl_give isl_mat *isl_mat_diagonal(__isl_take isl_mat *mat1,
+ __isl_take isl_mat *mat2);
+__isl_give isl_mat *isl_mat_left_hermite(__isl_take isl_mat *M, int neg,
+ __isl_give isl_mat **U, __isl_give isl_mat **Q);
+__isl_give isl_mat *isl_mat_lin_to_aff(__isl_take isl_mat *mat);
+__isl_give isl_mat *isl_mat_inverse_product(__isl_take isl_mat *left,
+ __isl_take isl_mat *right);
+__isl_give isl_mat *isl_mat_product(__isl_take isl_mat *left,
+ __isl_take isl_mat *right);
+__isl_give isl_mat *isl_mat_transpose(__isl_take isl_mat *mat);
+__isl_give isl_mat *isl_mat_right_inverse(__isl_take isl_mat *mat);
+__isl_give isl_mat *isl_mat_right_kernel(__isl_take isl_mat *mat);
+
+__isl_give isl_mat *isl_mat_normalize(__isl_take isl_mat *mat);
+__isl_give isl_mat *isl_mat_normalize_row(__isl_take isl_mat *mat, int row);
+
+__isl_give isl_mat *isl_mat_drop_cols(__isl_take isl_mat *mat,
+ unsigned col, unsigned n);
+__isl_give isl_mat *isl_mat_drop_rows(__isl_take isl_mat *mat,
+ unsigned row, unsigned n);
+__isl_give isl_mat *isl_mat_insert_cols(__isl_take isl_mat *mat,
+ unsigned col, unsigned n);
+__isl_give isl_mat *isl_mat_insert_rows(__isl_take isl_mat *mat,
+ unsigned row, unsigned n);
+__isl_give isl_mat *isl_mat_move_cols(__isl_take isl_mat *mat,
+ unsigned dst_col, unsigned src_col, unsigned n);
+__isl_give isl_mat *isl_mat_add_rows(__isl_take isl_mat *mat, unsigned n);
+__isl_give isl_mat *isl_mat_insert_zero_cols(__isl_take isl_mat *mat,
+ unsigned first, unsigned n);
+__isl_give isl_mat *isl_mat_add_zero_cols(__isl_take isl_mat *mat, unsigned n);
+__isl_give isl_mat *isl_mat_insert_zero_rows(__isl_take isl_mat *mat,
+ unsigned row, unsigned n);
+__isl_give isl_mat *isl_mat_add_zero_rows(__isl_take isl_mat *mat, unsigned n);
+
+void isl_mat_col_add(__isl_keep isl_mat *mat, int dst_col, int src_col);
+
+__isl_give isl_mat *isl_mat_unimodular_complete(__isl_take isl_mat *M, int row);
+__isl_give isl_mat *isl_mat_row_basis(__isl_take isl_mat *mat);
+__isl_give isl_mat *isl_mat_row_basis_extension(
+ __isl_take isl_mat *mat1, __isl_take isl_mat *mat2);
+
+__isl_give isl_mat *isl_mat_from_row_vec(__isl_take isl_vec *vec);
+__isl_give isl_mat *isl_mat_concat(__isl_take isl_mat *top,
+ __isl_take isl_mat *bot);
+__isl_give isl_mat *isl_mat_vec_concat(__isl_take isl_mat *top,
+ __isl_take isl_vec *bot);
+
+isl_bool isl_mat_is_equal(__isl_keep isl_mat *mat1, __isl_keep isl_mat *mat2);
+isl_bool isl_mat_has_linearly_independent_rows(__isl_keep isl_mat *mat1,
+ __isl_keep isl_mat *mat2);
+
+isl_size isl_mat_rank(__isl_keep isl_mat *mat);
+int isl_mat_initial_non_zero_cols(__isl_keep isl_mat *mat);
+
+void isl_mat_print_internal(__isl_keep isl_mat *mat, FILE *out, int indent);
+void isl_mat_dump(__isl_keep isl_mat *mat);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe.h
new file mode 100644
index 00000000000..1fb35737fca
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe.h
@@ -0,0 +1,7 @@
+#ifndef ISL_MAYBE_H
+#define ISL_MAYBE_H
+
+#define ISL_xMAYBE(TYPE) isl_maybe_ ## TYPE
+#define ISL_MAYBE(TYPE) ISL_xMAYBE(TYPE)
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_ast_expr.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_ast_expr.h
new file mode 100644
index 00000000000..260fa345be2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_ast_expr.h
@@ -0,0 +1,8 @@
+#ifndef ISL_MAYBE_AST_EXPR_H
+#define ISL_MAYBE_AST_EXPR_H
+
+#define ISL_TYPE isl_ast_expr
+#include <isl/maybe_templ.h>
+#undef ISL_TYPE
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_basic_set.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_basic_set.h
new file mode 100644
index 00000000000..1dc0881e9d5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_basic_set.h
@@ -0,0 +1,8 @@
+#ifndef ISL_MAYBE_BASIC_SET_H
+#define ISL_MAYBE_BASIC_SET_H
+
+#define ISL_TYPE isl_basic_set
+#include <isl/maybe_templ.h>
+#undef ISL_TYPE
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_id.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_id.h
new file mode 100644
index 00000000000..24ea308623b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_id.h
@@ -0,0 +1,8 @@
+#ifndef ISL_MAYBE_ID_H
+#define ISL_MAYBE_ID_H
+
+#define ISL_TYPE isl_id
+#include <isl/maybe_templ.h>
+#undef ISL_TYPE
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_pw_aff.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_pw_aff.h
new file mode 100644
index 00000000000..bc0dd9ca516
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_pw_aff.h
@@ -0,0 +1,8 @@
+#ifndef ISL_MAYBE_PW_AFF_H
+#define ISL_MAYBE_PW_AFF_H
+
+#define ISL_TYPE isl_pw_aff
+#include <isl/maybe_templ.h>
+#undef ISL_TYPE
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_templ.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_templ.h
new file mode 100644
index 00000000000..4dac253f652
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/maybe_templ.h
@@ -0,0 +1,12 @@
+#include <isl/ctx.h>
+#include <isl/maybe.h>
+
+/* A structure that possibly contains a pointer to an object of type ISL_TYPE.
+ * The pointer in "value" is only valid if "valid" is isl_bool_true.
+ * Otherwise, "value" is set to NULL.
+ */
+struct ISL_MAYBE(ISL_TYPE) {
+ isl_bool valid;
+ ISL_TYPE *value;
+};
+typedef struct ISL_MAYBE(ISL_TYPE) ISL_MAYBE(ISL_TYPE);
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/multi.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/multi.h
new file mode 100644
index 00000000000..3c39778da73
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/multi.h
@@ -0,0 +1,278 @@
+#ifndef ISL_MULTI_H
+#define ISL_MULTI_H
+
+#include <isl/val_type.h>
+#include <isl/space_type.h>
+#include <isl/list.h>
+#include <isl/set_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define ISL_DECLARE_MULTI(BASE) \
+isl_ctx *isl_multi_##BASE##_get_ctx( \
+ __isl_keep isl_multi_##BASE *multi); \
+__isl_export \
+__isl_give isl_space *isl_multi_##BASE##_get_space( \
+ __isl_keep isl_multi_##BASE *multi); \
+__isl_give isl_space *isl_multi_##BASE##_get_domain_space( \
+ __isl_keep isl_multi_##BASE *multi); \
+__isl_export \
+__isl_give isl_##BASE##_list *isl_multi_##BASE##_get_list( \
+ __isl_keep isl_multi_##BASE *multi); \
+__isl_constructor \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_from_##BASE##_list( \
+ __isl_take isl_space *space, __isl_take isl_##BASE##_list *list); \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_space_multi_##BASE( \
+ __isl_take isl_space *space, __isl_take isl_##BASE##_list *list); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_copy( \
+ __isl_keep isl_multi_##BASE *multi); \
+__isl_null isl_multi_##BASE *isl_multi_##BASE##_free( \
+ __isl_take isl_multi_##BASE *multi); \
+__isl_export \
+isl_bool isl_multi_##BASE##_plain_is_equal( \
+ __isl_keep isl_multi_##BASE *multi1, \
+ __isl_keep isl_multi_##BASE *multi2); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_reset_user( \
+ __isl_take isl_multi_##BASE *multi); \
+__isl_export \
+isl_size isl_multi_##BASE##_size(__isl_keep isl_multi_##BASE *multi); \
+__isl_export \
+__isl_give isl_##BASE *isl_multi_##BASE##_get_at( \
+ __isl_keep isl_multi_##BASE *multi, int pos); \
+__isl_give isl_##BASE *isl_multi_##BASE##_get_##BASE( \
+ __isl_keep isl_multi_##BASE *multi, int pos); \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_at( \
+ __isl_take isl_multi_##BASE *multi, int pos, \
+ __isl_take isl_##BASE *el); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_##BASE( \
+ __isl_take isl_multi_##BASE *multi, int pos, \
+ __isl_take isl_##BASE *el); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_range_splice( \
+ __isl_take isl_multi_##BASE *multi1, unsigned pos, \
+ __isl_take isl_multi_##BASE *multi2); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_flatten_range( \
+ __isl_take isl_multi_##BASE *multi); \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_flat_range_product( \
+ __isl_take isl_multi_##BASE *multi1, \
+ __isl_take isl_multi_##BASE *multi2); \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_range_product( \
+ __isl_take isl_multi_##BASE *multi1, \
+ __isl_take isl_multi_##BASE *multi2); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_factor_range( \
+ __isl_take isl_multi_##BASE *multi); \
+isl_bool isl_multi_##BASE##_range_is_wrapping( \
+ __isl_keep isl_multi_##BASE *multi); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_range_factor_domain( \
+ __isl_take isl_multi_##BASE *multi); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_range_factor_range( \
+ __isl_take isl_multi_##BASE *multi); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_align_params( \
+ __isl_take isl_multi_##BASE *multi, \
+ __isl_take isl_space *model); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_from_range( \
+ __isl_take isl_multi_##BASE *multi);
+
+#define ISL_DECLARE_MULTI_IDENTITY(BASE) \
+__isl_overload \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_identity_multi_##BASE( \
+ __isl_take isl_multi_##BASE *multi); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_identity( \
+ __isl_take isl_space *space); \
+__isl_overload \
+__isl_give isl_multi_##BASE * \
+isl_multi_##BASE##_identity_on_domain_space( \
+ __isl_take isl_space *space); \
+__isl_export \
+__isl_give isl_multi_##BASE * \
+isl_space_identity_multi_##BASE##_on_domain( \
+ __isl_take isl_space *space);
+
+#define ISL_DECLARE_MULTI_CMP(BASE) \
+int isl_multi_##BASE##_plain_cmp(__isl_keep isl_multi_##BASE *multi1, \
+ __isl_keep isl_multi_##BASE *multi2);
+
+#define ISL_DECLARE_MULTI_ARITH(BASE) \
+__isl_overload \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_scale_val( \
+ __isl_take isl_multi_##BASE *multi, __isl_take isl_val *v); \
+__isl_overload \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_scale_down_val( \
+ __isl_take isl_multi_##BASE *multi, __isl_take isl_val *v); \
+__isl_overload \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_scale_multi_val( \
+ __isl_take isl_multi_##BASE *multi, \
+ __isl_take isl_multi_val *mv); \
+__isl_overload \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_scale_down_multi_val( \
+ __isl_take isl_multi_##BASE *multi, \
+ __isl_take isl_multi_val *mv); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_mod_multi_val( \
+ __isl_take isl_multi_##BASE *multi, \
+ __isl_take isl_multi_val *mv); \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_add( \
+ __isl_take isl_multi_##BASE *multi1, \
+ __isl_take isl_multi_##BASE *multi2); \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_sub( \
+ __isl_take isl_multi_##BASE *multi1, \
+ __isl_take isl_multi_##BASE *multi2); \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_neg( \
+ __isl_take isl_multi_##BASE *multi);
+
+#define ISL_DECLARE_MULTI_MIN_MAX(BASE) \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_min( \
+ __isl_take isl_multi_##BASE *multi1, \
+ __isl_take isl_multi_##BASE *multi2); \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_max( \
+ __isl_take isl_multi_##BASE *multi1, \
+ __isl_take isl_multi_##BASE *multi2);
+
+#define ISL_DECLARE_MULTI_ADD_CONSTANT(BASE) \
+__isl_overload \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_add_constant_val( \
+ __isl_take isl_multi_##BASE *mpa, __isl_take isl_val *v); \
+__isl_overload \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_add_constant_multi_val( \
+ __isl_take isl_multi_##BASE *mpa, __isl_take isl_multi_val *mv);
+
+#define ISL_DECLARE_MULTI_ZERO(BASE) \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_zero( \
+ __isl_take isl_space *space); \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_space_zero_multi_##BASE( \
+ __isl_take isl_space *space);
+
+#define ISL_DECLARE_MULTI_NAN(BASE) \
+__isl_export \
+isl_bool isl_multi_##BASE##_involves_nan( \
+ __isl_keep isl_multi_##BASE *multi);
+
+#define ISL_DECLARE_MULTI_DROP_DIMS(BASE) \
+isl_size isl_multi_##BASE##_dim(__isl_keep isl_multi_##BASE *multi, \
+ enum isl_dim_type type); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_drop_dims( \
+ __isl_take isl_multi_##BASE *multi, enum isl_dim_type type, \
+ unsigned first, unsigned n);
+#define ISL_DECLARE_MULTI_DIMS(BASE) \
+ISL_DECLARE_MULTI_DROP_DIMS(BASE) \
+isl_bool isl_multi_##BASE##_involves_dims( \
+ __isl_keep isl_multi_##BASE *multi, enum isl_dim_type type, \
+ unsigned first, unsigned n); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_insert_dims( \
+ __isl_take isl_multi_##BASE *multi, enum isl_dim_type type, \
+ unsigned first, unsigned n); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_add_dims( \
+ __isl_take isl_multi_##BASE *multi, enum isl_dim_type type, \
+ unsigned n); \
+__isl_give isl_multi_##BASE * \
+isl_multi_##BASE##_project_domain_on_params( \
+ __isl_take isl_multi_##BASE *multi);
+
+#define ISL_DECLARE_MULTI_INSERT_DOMAIN(BASE) \
+__isl_export \
+__isl_give isl_multi_##BASE * \
+isl_multi_##BASE##_insert_domain(__isl_take isl_multi_##BASE *multi, \
+ __isl_take isl_space *domain);
+
+#define ISL_DECLARE_MULTI_LOCALS(BASE) \
+__isl_export \
+isl_bool isl_multi_##BASE##_involves_locals( \
+ __isl_keep isl_multi_##BASE *multi);
+
+#define ISL_DECLARE_MULTI_DIM_ID(BASE) \
+int isl_multi_##BASE##_find_dim_by_name( \
+ __isl_keep isl_multi_##BASE *multi, \
+ enum isl_dim_type type, const char *name); \
+int isl_multi_##BASE##_find_dim_by_id( \
+ __isl_keep isl_multi_##BASE *multi, enum isl_dim_type type, \
+ __isl_keep isl_id *id); \
+__isl_give isl_id *isl_multi_##BASE##_get_dim_id( \
+ __isl_keep isl_multi_##BASE *multi, \
+ enum isl_dim_type type, unsigned pos); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_dim_name( \
+ __isl_take isl_multi_##BASE *multi, \
+ enum isl_dim_type type, unsigned pos, const char *s); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_dim_id( \
+ __isl_take isl_multi_##BASE *multi, \
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id);
+
+#define ISL_DECLARE_MULTI_TUPLE_ID(BASE) \
+const char *isl_multi_##BASE##_get_tuple_name( \
+ __isl_keep isl_multi_##BASE *multi, enum isl_dim_type type); \
+__isl_export \
+isl_bool isl_multi_##BASE##_has_range_tuple_id( \
+ __isl_keep isl_multi_##BASE *multi); \
+isl_bool isl_multi_##BASE##_has_tuple_id( \
+ __isl_keep isl_multi_##BASE *multi, enum isl_dim_type type); \
+__isl_export \
+__isl_give isl_id *isl_multi_##BASE##_get_range_tuple_id( \
+ __isl_keep isl_multi_##BASE *multi); \
+__isl_give isl_id *isl_multi_##BASE##_get_tuple_id( \
+ __isl_keep isl_multi_##BASE *multi, enum isl_dim_type type); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_tuple_name( \
+ __isl_take isl_multi_##BASE *multi, \
+ enum isl_dim_type type, const char *s); \
+__isl_overload \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_range_tuple_id( \
+ __isl_take isl_multi_##BASE *multi, __isl_take isl_id *id); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_set_tuple_id( \
+ __isl_take isl_multi_##BASE *multi, \
+ enum isl_dim_type type, __isl_take isl_id *id); \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_reset_range_tuple_id( \
+ __isl_take isl_multi_##BASE *multi); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_reset_tuple_id( \
+ __isl_take isl_multi_##BASE *multi, enum isl_dim_type type);
+
+#define ISL_DECLARE_MULTI_WITH_DOMAIN(BASE) \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_product( \
+ __isl_take isl_multi_##BASE *multi1, \
+ __isl_take isl_multi_##BASE *multi2); \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_splice( \
+ __isl_take isl_multi_##BASE *multi1, unsigned in_pos, \
+ unsigned out_pos, __isl_take isl_multi_##BASE *multi2);
+
+#define ISL_DECLARE_MULTI_BIND_DOMAIN(BASE) \
+__isl_export \
+__isl_give isl_multi_##BASE *isl_multi_##BASE##_bind_domain( \
+ __isl_take isl_multi_##BASE *multi, \
+ __isl_take isl_multi_id *tuple); \
+__isl_export \
+__isl_give isl_multi_##BASE * \
+isl_multi_##BASE##_bind_domain_wrapped_domain( \
+ __isl_take isl_multi_##BASE *multi, \
+ __isl_take isl_multi_id *tuple);
+
+#define ISL_DECLARE_MULTI_UNBIND_PARAMS(BASE) \
+__isl_export \
+__isl_give isl_multi_##BASE * \
+isl_multi_##BASE##_unbind_params_insert_domain( \
+ __isl_take isl_multi_##BASE *multi, \
+ __isl_take isl_multi_id *domain);
+
+#define ISL_DECLARE_MULTI_PARAM(BASE) \
+__isl_overload \
+isl_bool isl_multi_##BASE##_involves_param_id( \
+ __isl_keep isl_multi_##BASE *multi, __isl_keep isl_id *id); \
+__isl_overload \
+isl_bool isl_multi_##BASE##_involves_param_id_list( \
+ __isl_keep isl_multi_##BASE *multi, \
+ __isl_keep isl_id_list *list);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/obj.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/obj.h
new file mode 100644
index 00000000000..07c6bf38c66
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/obj.h
@@ -0,0 +1,57 @@
+#ifndef ISL_OBJ_H
+#define ISL_OBJ_H
+
+#include <isl/set_type.h>
+#include <isl/map_type.h>
+#include <isl/union_set_type.h>
+#include <isl/union_map_type.h>
+#include <isl/polynomial_type.h>
+#include <isl/printer.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_obj_vtable {
+ void *(*copy)(void *v1);
+ void *(*add)(void *v1, void *v2);
+ __isl_give isl_printer *(*print)(__isl_take isl_printer *p, void *v);
+ void (*free)(void *v);
+};
+typedef struct isl_obj_vtable *isl_obj_type;
+extern struct isl_obj_vtable isl_obj_none_vtable;
+#define isl_obj_none (&isl_obj_none_vtable)
+extern struct isl_obj_vtable isl_obj_int_vtable;
+#define isl_obj_int (&isl_obj_int_vtable)
+extern struct isl_obj_vtable isl_obj_val_vtable;
+#define isl_obj_val (&isl_obj_val_vtable)
+extern struct isl_obj_vtable isl_obj_set_vtable;
+#define isl_obj_set (&isl_obj_set_vtable)
+extern struct isl_obj_vtable isl_obj_union_set_vtable;
+#define isl_obj_union_set (&isl_obj_union_set_vtable)
+extern struct isl_obj_vtable isl_obj_map_vtable;
+#define isl_obj_map (&isl_obj_map_vtable)
+extern struct isl_obj_vtable isl_obj_union_map_vtable;
+#define isl_obj_union_map (&isl_obj_union_map_vtable)
+extern struct isl_obj_vtable isl_obj_pw_multi_aff_vtable;
+#define isl_obj_pw_multi_aff (&isl_obj_pw_multi_aff_vtable)
+extern struct isl_obj_vtable isl_obj_pw_qpolynomial_vtable;
+#define isl_obj_pw_qpolynomial (&isl_obj_pw_qpolynomial_vtable)
+extern struct isl_obj_vtable isl_obj_union_pw_qpolynomial_vtable;
+#define isl_obj_union_pw_qpolynomial (&isl_obj_union_pw_qpolynomial_vtable)
+extern struct isl_obj_vtable isl_obj_pw_qpolynomial_fold_vtable;
+#define isl_obj_pw_qpolynomial_fold (&isl_obj_pw_qpolynomial_fold_vtable)
+extern struct isl_obj_vtable isl_obj_union_pw_qpolynomial_fold_vtable;
+#define isl_obj_union_pw_qpolynomial_fold (&isl_obj_union_pw_qpolynomial_fold_vtable)
+extern struct isl_obj_vtable isl_obj_schedule_vtable;
+#define isl_obj_schedule (&isl_obj_schedule_vtable)
+struct isl_obj {
+ isl_obj_type type;
+ void *v;
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/options.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/options.h
new file mode 100644
index 00000000000..1738155fb9e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/options.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_OPTIONS_H
+#define ISL_OPTIONS_H
+
+#include <isl/arg.h>
+#include <isl/ctx.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_options;
+
+ISL_ARG_DECL(isl_options, struct isl_options, isl_options_args)
+
+#define ISL_BOUND_BERNSTEIN 0
+#define ISL_BOUND_RANGE 1
+isl_stat isl_options_set_bound(isl_ctx *ctx, int val);
+int isl_options_get_bound(isl_ctx *ctx);
+
+#define ISL_ON_ERROR_WARN 0
+#define ISL_ON_ERROR_CONTINUE 1
+#define ISL_ON_ERROR_ABORT 2
+isl_stat isl_options_set_on_error(isl_ctx *ctx, int val);
+int isl_options_get_on_error(isl_ctx *ctx);
+
+isl_stat isl_options_set_gbr_only_first(isl_ctx *ctx, int val);
+int isl_options_get_gbr_only_first(isl_ctx *ctx);
+
+#define ISL_SCHEDULE_ALGORITHM_ISL 0
+#define ISL_SCHEDULE_ALGORITHM_FEAUTRIER 1
+isl_stat isl_options_set_schedule_algorithm(isl_ctx *ctx, int val);
+int isl_options_get_schedule_algorithm(isl_ctx *ctx);
+
+isl_stat isl_options_set_pip_symmetry(isl_ctx *ctx, int val);
+int isl_options_get_pip_symmetry(isl_ctx *ctx);
+
+isl_stat isl_options_set_coalesce_bounded_wrapping(isl_ctx *ctx, int val);
+int isl_options_get_coalesce_bounded_wrapping(isl_ctx *ctx);
+
+isl_stat isl_options_set_coalesce_preserve_locals(isl_ctx *ctx, int val);
+int isl_options_get_coalesce_preserve_locals(isl_ctx *ctx);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/point.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/point.h
new file mode 100644
index 00000000000..0f907ed718e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/point.h
@@ -0,0 +1,46 @@
+#ifndef ISL_POINT_H
+#define ISL_POINT_H
+
+#include <stdio.h>
+#include <isl/space_type.h>
+#include <isl/val_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct __isl_subclass(isl_basic_set) isl_point;
+typedef struct isl_point isl_point;
+
+isl_ctx *isl_point_get_ctx(__isl_keep isl_point *pnt);
+__isl_give isl_space *isl_point_get_space(__isl_keep isl_point *pnt);
+
+__isl_give isl_point *isl_point_zero(__isl_take isl_space *space);
+__isl_give isl_point *isl_point_copy(__isl_keep isl_point *pnt);
+__isl_null isl_point *isl_point_free(__isl_take isl_point *pnt);
+
+__isl_give isl_val *isl_point_get_coordinate_val(__isl_keep isl_point *pnt,
+ enum isl_dim_type type, int pos);
+__isl_give isl_point *isl_point_set_coordinate_val(__isl_take isl_point *pnt,
+ enum isl_dim_type type, int pos, __isl_take isl_val *v);
+__isl_export
+__isl_give isl_multi_val *isl_point_get_multi_val(__isl_keep isl_point *pnt);
+
+__isl_give isl_point *isl_point_add_ui(__isl_take isl_point *pnt,
+ enum isl_dim_type type, int pos, unsigned val);
+__isl_give isl_point *isl_point_sub_ui(__isl_take isl_point *pnt,
+ enum isl_dim_type type, int pos, unsigned val);
+
+__isl_give isl_point *isl_point_void(__isl_take isl_space *space);
+isl_bool isl_point_is_void(__isl_keep isl_point *pnt);
+
+__isl_give isl_printer *isl_printer_print_point(
+ __isl_take isl_printer *printer, __isl_keep isl_point *pnt);
+__isl_give char *isl_point_to_str(__isl_keep isl_point *pnt);
+void isl_point_dump(__isl_keep isl_point *pnt);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/polynomial.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/polynomial.h
new file mode 100644
index 00000000000..2d5baaf815e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/polynomial.h
@@ -0,0 +1,833 @@
+#ifndef ISL_POLYNOMIAL_H
+#define ISL_POLYNOMIAL_H
+
+#include <isl/ctx.h>
+#include <isl/constraint.h>
+#include <isl/space_type.h>
+#include <isl/set_type.h>
+#include <isl/point.h>
+#include <isl/printer.h>
+#include <isl/union_set_type.h>
+#include <isl/aff_type.h>
+#include <isl/polynomial_type.h>
+#include <isl/val_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+isl_ctx *isl_qpolynomial_get_ctx(__isl_keep isl_qpolynomial *qp);
+__isl_give isl_space *isl_qpolynomial_get_domain_space(
+ __isl_keep isl_qpolynomial *qp);
+__isl_give isl_space *isl_qpolynomial_get_space(__isl_keep isl_qpolynomial *qp);
+isl_size isl_qpolynomial_dim(__isl_keep isl_qpolynomial *qp,
+ enum isl_dim_type type);
+isl_bool isl_qpolynomial_involves_dims(__isl_keep isl_qpolynomial *qp,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+__isl_give isl_val *isl_qpolynomial_get_constant_val(
+ __isl_keep isl_qpolynomial *qp);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_set_dim_name(
+ __isl_take isl_qpolynomial *qp,
+ enum isl_dim_type type, unsigned pos, const char *s);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_zero_on_domain(
+ __isl_take isl_space *domain);
+__isl_give isl_qpolynomial *isl_qpolynomial_one_on_domain(
+ __isl_take isl_space *domain);
+__isl_give isl_qpolynomial *isl_qpolynomial_infty_on_domain(
+ __isl_take isl_space *domain);
+__isl_give isl_qpolynomial *isl_qpolynomial_neginfty_on_domain(
+ __isl_take isl_space *domain);
+__isl_give isl_qpolynomial *isl_qpolynomial_nan_on_domain(
+ __isl_take isl_space *domain);
+__isl_give isl_qpolynomial *isl_qpolynomial_val_on_domain(
+ __isl_take isl_space *space, __isl_take isl_val *val);
+__isl_give isl_qpolynomial *isl_qpolynomial_var_on_domain(
+ __isl_take isl_space *domain,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_qpolynomial *isl_qpolynomial_copy(__isl_keep isl_qpolynomial *qp);
+__isl_null isl_qpolynomial *isl_qpolynomial_free(
+ __isl_take isl_qpolynomial *qp);
+
+isl_bool isl_qpolynomial_plain_is_equal(__isl_keep isl_qpolynomial *qp1,
+ __isl_keep isl_qpolynomial *qp2);
+isl_bool isl_qpolynomial_is_zero(__isl_keep isl_qpolynomial *qp);
+isl_bool isl_qpolynomial_is_nan(__isl_keep isl_qpolynomial *qp);
+isl_bool isl_qpolynomial_is_infty(__isl_keep isl_qpolynomial *qp);
+isl_bool isl_qpolynomial_is_neginfty(__isl_keep isl_qpolynomial *qp);
+int isl_qpolynomial_sgn(__isl_keep isl_qpolynomial *qp);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_neg(__isl_take isl_qpolynomial *qp);
+__isl_give isl_qpolynomial *isl_qpolynomial_add(__isl_take isl_qpolynomial *qp1,
+ __isl_take isl_qpolynomial *qp2);
+__isl_give isl_qpolynomial *isl_qpolynomial_sub(__isl_take isl_qpolynomial *qp1,
+ __isl_take isl_qpolynomial *qp2);
+__isl_give isl_qpolynomial *isl_qpolynomial_mul(__isl_take isl_qpolynomial *qp1,
+ __isl_take isl_qpolynomial *qp2);
+__isl_give isl_qpolynomial *isl_qpolynomial_pow(__isl_take isl_qpolynomial *qp,
+ unsigned power);
+__isl_give isl_qpolynomial *isl_qpolynomial_scale_val(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_val *v);
+__isl_give isl_qpolynomial *isl_qpolynomial_scale_down_val(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_val *v);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_insert_dims(
+ __isl_take isl_qpolynomial *qp, enum isl_dim_type type,
+ unsigned first, unsigned n);
+__isl_give isl_qpolynomial *isl_qpolynomial_add_dims(
+ __isl_take isl_qpolynomial *qp, enum isl_dim_type type, unsigned n);
+__isl_give isl_qpolynomial *isl_qpolynomial_move_dims(
+ __isl_take isl_qpolynomial *qp,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+__isl_give isl_qpolynomial *isl_qpolynomial_project_domain_on_params(
+ __isl_take isl_qpolynomial *qp);
+__isl_give isl_qpolynomial *isl_qpolynomial_drop_dims(
+ __isl_take isl_qpolynomial *qp,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_substitute(
+ __isl_take isl_qpolynomial *qp,
+ enum isl_dim_type type, unsigned first, unsigned n,
+ __isl_keep isl_qpolynomial **subs);
+
+isl_stat isl_qpolynomial_as_polynomial_on_domain(__isl_keep isl_qpolynomial *qp,
+ __isl_keep isl_basic_set *bset,
+ isl_stat (*fn)(__isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, void *user), void *user);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_homogenize(
+ __isl_take isl_qpolynomial *poly);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_align_params(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_space *model);
+
+isl_ctx *isl_term_get_ctx(__isl_keep isl_term *term);
+
+__isl_give isl_term *isl_term_copy(__isl_keep isl_term *term);
+__isl_null isl_term *isl_term_free(__isl_take isl_term *term);
+
+isl_size isl_term_dim(__isl_keep isl_term *term, enum isl_dim_type type);
+__isl_give isl_val *isl_term_get_coefficient_val(__isl_keep isl_term *term);
+isl_size isl_term_get_exp(__isl_keep isl_term *term,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_aff *isl_term_get_div(__isl_keep isl_term *term, unsigned pos);
+
+isl_stat isl_qpolynomial_foreach_term(__isl_keep isl_qpolynomial *qp,
+ isl_stat (*fn)(__isl_take isl_term *term, void *user), void *user);
+
+__isl_give isl_val *isl_qpolynomial_eval(__isl_take isl_qpolynomial *qp,
+ __isl_take isl_point *pnt);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_gist_params(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_set *context);
+__isl_give isl_qpolynomial *isl_qpolynomial_gist(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_set *context);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_from_constraint(
+ __isl_take isl_constraint *c, enum isl_dim_type type, unsigned pos);
+__isl_give isl_qpolynomial *isl_qpolynomial_from_term(__isl_take isl_term *term);
+__isl_give isl_qpolynomial *isl_qpolynomial_from_aff(__isl_take isl_aff *aff);
+__isl_give isl_basic_map *isl_basic_map_from_qpolynomial(
+ __isl_take isl_qpolynomial *qp);
+
+__isl_give isl_printer *isl_printer_print_qpolynomial(
+ __isl_take isl_printer *p, __isl_keep isl_qpolynomial *qp);
+void isl_qpolynomial_print(__isl_keep isl_qpolynomial *qp, FILE *out,
+ unsigned output_format);
+void isl_qpolynomial_dump(__isl_keep isl_qpolynomial *qp);
+
+isl_ctx *isl_pw_qpolynomial_get_ctx(__isl_keep isl_pw_qpolynomial *pwqp);
+
+isl_bool isl_pw_qpolynomial_involves_nan(__isl_keep isl_pw_qpolynomial *pwqp);
+isl_bool isl_pw_qpolynomial_plain_is_equal(__isl_keep isl_pw_qpolynomial *pwqp1,
+ __isl_keep isl_pw_qpolynomial *pwqp2);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_zero(
+ __isl_take isl_space *space);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_alloc(__isl_take isl_set *set,
+ __isl_take isl_qpolynomial *qp);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_from_qpolynomial(
+ __isl_take isl_qpolynomial *qp);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_copy(
+ __isl_keep isl_pw_qpolynomial *pwqp);
+__isl_null isl_pw_qpolynomial *isl_pw_qpolynomial_free(
+ __isl_take isl_pw_qpolynomial *pwqp);
+
+isl_bool isl_pw_qpolynomial_is_zero(__isl_keep isl_pw_qpolynomial *pwqp);
+
+__isl_give isl_space *isl_pw_qpolynomial_get_domain_space(
+ __isl_keep isl_pw_qpolynomial *pwqp);
+__isl_give isl_space *isl_pw_qpolynomial_get_space(
+ __isl_keep isl_pw_qpolynomial *pwqp);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_reset_domain_space(
+ __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_space *space);
+isl_size isl_pw_qpolynomial_dim(__isl_keep isl_pw_qpolynomial *pwqp,
+ enum isl_dim_type type);
+isl_bool isl_pw_qpolynomial_involves_param_id(
+ __isl_keep isl_pw_qpolynomial *pwqp, __isl_keep isl_id *id);
+isl_bool isl_pw_qpolynomial_involves_dims(__isl_keep isl_pw_qpolynomial *pwqp,
+ enum isl_dim_type type, unsigned first, unsigned n);
+isl_bool isl_pw_qpolynomial_has_equal_space(
+ __isl_keep isl_pw_qpolynomial *pwqp1,
+ __isl_keep isl_pw_qpolynomial *pwqp2);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_set_dim_name(
+ __isl_take isl_pw_qpolynomial *pwqp,
+ enum isl_dim_type type, unsigned pos, const char *s);
+
+int isl_pw_qpolynomial_find_dim_by_name(__isl_keep isl_pw_qpolynomial *pwqp,
+ enum isl_dim_type type, const char *name);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_reset_user(
+ __isl_take isl_pw_qpolynomial *pwqp);
+
+__isl_export
+__isl_give isl_set *isl_pw_qpolynomial_domain(__isl_take isl_pw_qpolynomial *pwqp);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_intersect_domain(
+ __isl_take isl_pw_qpolynomial *pwpq, __isl_take isl_set *set);
+__isl_give isl_pw_qpolynomial *
+isl_pw_qpolynomial_intersect_domain_wrapped_domain(
+ __isl_take isl_pw_qpolynomial *pwpq, __isl_take isl_set *set);
+__isl_give isl_pw_qpolynomial *
+isl_pw_qpolynomial_intersect_domain_wrapped_range(
+ __isl_take isl_pw_qpolynomial *pwpq, __isl_take isl_set *set);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_intersect_params(
+ __isl_take isl_pw_qpolynomial *pwpq, __isl_take isl_set *set);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_subtract_domain(
+ __isl_take isl_pw_qpolynomial *pwpq, __isl_take isl_set *set);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_project_domain_on_params(
+ __isl_take isl_pw_qpolynomial *pwqp);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_from_range(
+ __isl_take isl_pw_qpolynomial *pwqp);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_drop_dims(
+ __isl_take isl_pw_qpolynomial *pwqp,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_split_dims(
+ __isl_take isl_pw_qpolynomial *pwqp,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_drop_unused_params(
+ __isl_take isl_pw_qpolynomial *pwqp);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add(
+ __isl_take isl_pw_qpolynomial *pwqp1,
+ __isl_take isl_pw_qpolynomial *pwqp2);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_sub(
+ __isl_take isl_pw_qpolynomial *pwqp1,
+ __isl_take isl_pw_qpolynomial *pwqp2);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add_disjoint(
+ __isl_take isl_pw_qpolynomial *pwqp1,
+ __isl_take isl_pw_qpolynomial *pwqp2);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_neg(
+ __isl_take isl_pw_qpolynomial *pwqp);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_mul(
+ __isl_take isl_pw_qpolynomial *pwqp1,
+ __isl_take isl_pw_qpolynomial *pwqp2);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_scale_val(
+ __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_val *v);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_scale_down_val(
+ __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_val *v);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_pow(
+ __isl_take isl_pw_qpolynomial *pwqp, unsigned exponent);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_insert_dims(
+ __isl_take isl_pw_qpolynomial *pwqp, enum isl_dim_type type,
+ unsigned first, unsigned n);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add_dims(
+ __isl_take isl_pw_qpolynomial *pwqp,
+ enum isl_dim_type type, unsigned n);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_move_dims(
+ __isl_take isl_pw_qpolynomial *pwqp,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_fix_val(
+ __isl_take isl_pw_qpolynomial *pwqp,
+ enum isl_dim_type type, unsigned n, __isl_take isl_val *v);
+
+__isl_export
+__isl_give isl_val *isl_pw_qpolynomial_eval(
+ __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_point *pnt);
+
+__isl_give isl_val *isl_pw_qpolynomial_max(__isl_take isl_pw_qpolynomial *pwqp);
+__isl_give isl_val *isl_pw_qpolynomial_min(__isl_take isl_pw_qpolynomial *pwqp);
+
+isl_size isl_pw_qpolynomial_n_piece(__isl_keep isl_pw_qpolynomial *pwqp);
+isl_stat isl_pw_qpolynomial_foreach_piece(__isl_keep isl_pw_qpolynomial *pwqp,
+ isl_stat (*fn)(__isl_take isl_set *set, __isl_take isl_qpolynomial *qp,
+ void *user), void *user);
+isl_bool isl_pw_qpolynomial_every_piece(__isl_keep isl_pw_qpolynomial *pwqp,
+ isl_bool (*test)(__isl_keep isl_set *set,
+ __isl_keep isl_qpolynomial *qp, void *user), void *user);
+isl_stat isl_pw_qpolynomial_foreach_lifted_piece(
+ __isl_keep isl_pw_qpolynomial *pwqp,
+ isl_stat (*fn)(__isl_take isl_set *set, __isl_take isl_qpolynomial *qp,
+ void *user), void *user);
+isl_bool isl_pw_qpolynomial_isa_qpolynomial(
+ __isl_keep isl_pw_qpolynomial *pwqp);
+__isl_give isl_qpolynomial *isl_pw_qpolynomial_as_qpolynomial(
+ __isl_take isl_pw_qpolynomial *pwqp);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_from_pw_aff(
+ __isl_take isl_pw_aff *pwaff);
+
+__isl_constructor
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_read_from_str(isl_ctx *ctx,
+ const char *str);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_read_from_file(isl_ctx *ctx,
+ FILE *input);
+__isl_give char *isl_pw_qpolynomial_to_str(__isl_keep isl_pw_qpolynomial *pwqp);
+__isl_give isl_printer *isl_printer_print_pw_qpolynomial(
+ __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial *pwqp);
+void isl_pw_qpolynomial_print(__isl_keep isl_pw_qpolynomial *pwqp, FILE *out,
+ unsigned output_format);
+void isl_pw_qpolynomial_dump(__isl_keep isl_pw_qpolynomial *pwqp);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_coalesce(
+ __isl_take isl_pw_qpolynomial *pwqp);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_gist(
+ __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_set *context);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_gist_params(
+ __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_set *context);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_split_periods(
+ __isl_take isl_pw_qpolynomial *pwqp, int max_periods);
+
+__isl_give isl_pw_qpolynomial *isl_basic_set_multiplicative_call(
+ __isl_take isl_basic_set *bset,
+ __isl_give isl_pw_qpolynomial *(*fn)(__isl_take isl_basic_set *bset));
+
+isl_ctx *isl_qpolynomial_fold_get_ctx(__isl_keep isl_qpolynomial_fold *fold);
+enum isl_fold isl_qpolynomial_fold_get_type(__isl_keep isl_qpolynomial_fold *fold);
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_empty(enum isl_fold type,
+ __isl_take isl_space *space);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_alloc(
+ enum isl_fold type, __isl_take isl_qpolynomial *qp);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_copy(
+ __isl_keep isl_qpolynomial_fold *fold);
+__isl_null isl_qpolynomial_fold *isl_qpolynomial_fold_free(
+ __isl_take isl_qpolynomial_fold *fold);
+
+isl_bool isl_qpolynomial_fold_is_empty(__isl_keep isl_qpolynomial_fold *fold);
+isl_bool isl_qpolynomial_fold_is_nan(__isl_keep isl_qpolynomial_fold *fold);
+isl_bool isl_qpolynomial_fold_plain_is_equal(
+ __isl_keep isl_qpolynomial_fold *fold1,
+ __isl_keep isl_qpolynomial_fold *fold2);
+
+__isl_give isl_space *isl_qpolynomial_fold_get_domain_space(
+ __isl_keep isl_qpolynomial_fold *fold);
+__isl_give isl_space *isl_qpolynomial_fold_get_space(
+ __isl_keep isl_qpolynomial_fold *fold);
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_fold(
+ __isl_take isl_qpolynomial_fold *fold1,
+ __isl_take isl_qpolynomial_fold *fold2);
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale_val(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_val *v);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale_down_val(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_val *v);
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_move_dims(
+ __isl_take isl_qpolynomial_fold *fold,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_substitute(
+ __isl_take isl_qpolynomial_fold *fold,
+ enum isl_dim_type type, unsigned first, unsigned n,
+ __isl_keep isl_qpolynomial **subs);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_fix_val(
+ __isl_take isl_pw_qpolynomial_fold *pwf,
+ enum isl_dim_type type, unsigned n, __isl_take isl_val *v);
+
+__isl_give isl_val *isl_qpolynomial_fold_eval(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_point *pnt);
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_gist_params(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *context);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_gist(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *context);
+
+isl_stat isl_qpolynomial_fold_foreach_qpolynomial(
+ __isl_keep isl_qpolynomial_fold *fold,
+ isl_stat (*fn)(__isl_take isl_qpolynomial *qp, void *user), void *user);
+
+__isl_give isl_printer *isl_printer_print_qpolynomial_fold(
+ __isl_take isl_printer *p, __isl_keep isl_qpolynomial_fold *fold);
+void isl_qpolynomial_fold_print(__isl_keep isl_qpolynomial_fold *fold, FILE *out,
+ unsigned output_format);
+void isl_qpolynomial_fold_dump(__isl_keep isl_qpolynomial_fold *fold);
+
+isl_ctx *isl_pw_qpolynomial_fold_get_ctx(__isl_keep isl_pw_qpolynomial_fold *pwf);
+enum isl_fold isl_pw_qpolynomial_fold_get_type(
+ __isl_keep isl_pw_qpolynomial_fold *pwf);
+
+isl_bool isl_pw_qpolynomial_fold_involves_nan(
+ __isl_keep isl_pw_qpolynomial_fold *pwf);
+isl_bool isl_pw_qpolynomial_fold_plain_is_equal(
+ __isl_keep isl_pw_qpolynomial_fold *pwf1,
+ __isl_keep isl_pw_qpolynomial_fold *pwf2);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_from_pw_qpolynomial(
+ enum isl_fold type, __isl_take isl_pw_qpolynomial *pwqp);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_alloc(
+ enum isl_fold type,
+ __isl_take isl_set *set, __isl_take isl_qpolynomial_fold *fold);
+__isl_give isl_pw_qpolynomial_fold *
+isl_pw_qpolynomial_fold_from_qpolynomial_fold(
+ __isl_take isl_qpolynomial_fold *fold);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_copy(
+ __isl_keep isl_pw_qpolynomial_fold *pwf);
+__isl_null isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_free(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+
+isl_bool isl_pw_qpolynomial_fold_is_zero(
+ __isl_keep isl_pw_qpolynomial_fold *pwf);
+
+__isl_give isl_space *isl_pw_qpolynomial_fold_get_domain_space(
+ __isl_keep isl_pw_qpolynomial_fold *pwf);
+__isl_give isl_space *isl_pw_qpolynomial_fold_get_space(
+ __isl_keep isl_pw_qpolynomial_fold *pwf);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_reset_space(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_space *space);
+isl_size isl_pw_qpolynomial_fold_dim(__isl_keep isl_pw_qpolynomial_fold *pwf,
+ enum isl_dim_type type);
+isl_bool isl_pw_qpolynomial_fold_involves_param_id(
+ __isl_keep isl_pw_qpolynomial_fold *pwf, __isl_keep isl_id *id);
+isl_bool isl_pw_qpolynomial_fold_has_equal_space(
+ __isl_keep isl_pw_qpolynomial_fold *pwf1,
+ __isl_keep isl_pw_qpolynomial_fold *pwf2);
+
+size_t isl_pw_qpolynomial_fold_size(__isl_keep isl_pw_qpolynomial_fold *pwf);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_zero(
+ __isl_take isl_space *space, enum isl_fold type);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_set_dim_name(
+ __isl_take isl_pw_qpolynomial_fold *pwf,
+ enum isl_dim_type type, unsigned pos, const char *s);
+
+int isl_pw_qpolynomial_fold_find_dim_by_name(
+ __isl_keep isl_pw_qpolynomial_fold *pwf,
+ enum isl_dim_type type, const char *name);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_reset_user(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+
+__isl_give isl_set *isl_pw_qpolynomial_fold_domain(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_intersect_domain(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *set);
+__isl_give isl_pw_qpolynomial_fold *
+isl_pw_qpolynomial_fold_intersect_domain_wrapped_domain(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *set);
+__isl_give isl_pw_qpolynomial_fold *
+isl_pw_qpolynomial_fold_intersect_domain_wrapped_range(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *set);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_intersect_params(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *set);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_subtract_domain(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *set);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_add(
+ __isl_take isl_pw_qpolynomial_fold *pwf1,
+ __isl_take isl_pw_qpolynomial_fold *pwf2);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_fold(
+ __isl_take isl_pw_qpolynomial_fold *pwf1,
+ __isl_take isl_pw_qpolynomial_fold *pwf2);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_add_disjoint(
+ __isl_take isl_pw_qpolynomial_fold *pwf1,
+ __isl_take isl_pw_qpolynomial_fold *pwf2);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_scale_val(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_val *v);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_scale_down_val(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_val *v);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_project_domain_on_params(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_from_range(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_drop_dims(
+ __isl_take isl_pw_qpolynomial_fold *pwf,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_move_dims(
+ __isl_take isl_pw_qpolynomial_fold *pwf,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_drop_unused_params(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+
+__isl_give isl_val *isl_pw_qpolynomial_fold_eval(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_point *pnt);
+
+isl_size isl_pw_qpolynomial_fold_n_piece(
+ __isl_keep isl_pw_qpolynomial_fold *pwf);
+isl_stat isl_pw_qpolynomial_fold_foreach_piece(
+ __isl_keep isl_pw_qpolynomial_fold *pwf,
+ isl_stat (*fn)(__isl_take isl_set *set,
+ __isl_take isl_qpolynomial_fold *fold, void *user), void *user);
+isl_bool isl_pw_qpolynomial_fold_every_piece(
+ __isl_keep isl_pw_qpolynomial_fold *pwf,
+ isl_bool (*test)(__isl_keep isl_set *set,
+ __isl_keep isl_qpolynomial_fold *fold, void *user), void *user);
+isl_stat isl_pw_qpolynomial_fold_foreach_lifted_piece(
+ __isl_keep isl_pw_qpolynomial_fold *pwf,
+ isl_stat (*fn)(__isl_take isl_set *set,
+ __isl_take isl_qpolynomial_fold *fold, void *user), void *user);
+isl_bool isl_pw_qpolynomial_fold_isa_qpolynomial_fold(
+ __isl_keep isl_pw_qpolynomial_fold *pwf);
+__isl_give isl_qpolynomial_fold *isl_pw_qpolynomial_fold_as_qpolynomial_fold(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_read_from_str(
+ isl_ctx *ctx, const char *str);
+__isl_give isl_printer *isl_printer_print_pw_qpolynomial_fold(
+ __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial_fold *pwf);
+void isl_pw_qpolynomial_fold_print(__isl_keep isl_pw_qpolynomial_fold *pwf,
+ FILE *out, unsigned output_format);
+void isl_pw_qpolynomial_fold_dump(__isl_keep isl_pw_qpolynomial_fold *pwf);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_coalesce(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_gist(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *context);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_gist_params(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_set *context);
+
+__isl_give isl_val *isl_pw_qpolynomial_fold_max(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+__isl_give isl_val *isl_pw_qpolynomial_fold_min(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_bound(
+ __isl_take isl_pw_qpolynomial *pwqp, enum isl_fold type,
+ isl_bool *tight);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_bound(
+ __isl_take isl_pw_qpolynomial_fold *pwf, isl_bool *tight);
+__isl_give isl_pw_qpolynomial_fold *isl_set_apply_pw_qpolynomial_fold(
+ __isl_take isl_set *set, __isl_take isl_pw_qpolynomial_fold *pwf,
+ isl_bool *tight);
+__isl_give isl_pw_qpolynomial_fold *isl_map_apply_pw_qpolynomial_fold(
+ __isl_take isl_map *map, __isl_take isl_pw_qpolynomial_fold *pwf,
+ isl_bool *tight);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_to_polynomial(
+ __isl_take isl_pw_qpolynomial *pwqp, int sign);
+
+isl_ctx *isl_union_pw_qpolynomial_get_ctx(
+ __isl_keep isl_union_pw_qpolynomial *upwqp);
+
+isl_size isl_union_pw_qpolynomial_dim(
+ __isl_keep isl_union_pw_qpolynomial *upwqp, enum isl_dim_type type);
+
+isl_bool isl_union_pw_qpolynomial_involves_nan(
+ __isl_keep isl_union_pw_qpolynomial *upwqp);
+isl_bool isl_union_pw_qpolynomial_plain_is_equal(
+ __isl_keep isl_union_pw_qpolynomial *upwqp1,
+ __isl_keep isl_union_pw_qpolynomial *upwqp2);
+
+__isl_give isl_union_pw_qpolynomial *isl_pw_qpolynomial_to_union_pw_qpolynomial(
+ __isl_take isl_pw_qpolynomial *pwqp);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_from_pw_qpolynomial(__isl_take isl_pw_qpolynomial *pwqp);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_zero_ctx(
+ isl_ctx *ctx);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_zero_space(
+ __isl_take isl_space *space);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_zero(
+ __isl_take isl_space *space);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_add_pw_qpolynomial(
+ __isl_take isl_union_pw_qpolynomial *upwqp,
+ __isl_take isl_pw_qpolynomial *pwqp);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_copy(
+ __isl_keep isl_union_pw_qpolynomial *upwqp);
+__isl_null isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_free(
+ __isl_take isl_union_pw_qpolynomial *upwqp);
+
+__isl_constructor
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_read_from_str(
+ isl_ctx *ctx, const char *str);
+__isl_give char *isl_union_pw_qpolynomial_to_str(
+ __isl_keep isl_union_pw_qpolynomial *upwqp);
+
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_neg(
+ __isl_take isl_union_pw_qpolynomial *upwqp);
+
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_add(
+ __isl_take isl_union_pw_qpolynomial *upwqp1,
+ __isl_take isl_union_pw_qpolynomial *upwqp2);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_sub(
+ __isl_take isl_union_pw_qpolynomial *upwqp1,
+ __isl_take isl_union_pw_qpolynomial *upwqp2);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_mul(
+ __isl_take isl_union_pw_qpolynomial *upwqp1,
+ __isl_take isl_union_pw_qpolynomial *upwqp2);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_scale_val(
+ __isl_take isl_union_pw_qpolynomial *upwqp, __isl_take isl_val *v);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_scale_down_val(
+ __isl_take isl_union_pw_qpolynomial *upwqp, __isl_take isl_val *v);
+
+__isl_export
+__isl_give isl_union_set *isl_union_pw_qpolynomial_domain(
+ __isl_take isl_union_pw_qpolynomial *upwqp);
+__isl_give isl_union_pw_qpolynomial *
+isl_union_pw_qpolynomial_intersect_domain_space(
+ __isl_take isl_union_pw_qpolynomial *upwpq,
+ __isl_take isl_space *space);
+__isl_give isl_union_pw_qpolynomial *
+isl_union_pw_qpolynomial_intersect_domain_union_set(
+ __isl_take isl_union_pw_qpolynomial *upwpq,
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_intersect_domain(
+ __isl_take isl_union_pw_qpolynomial *upwpq,
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_qpolynomial *
+isl_union_pw_qpolynomial_intersect_domain_wrapped_domain(
+ __isl_take isl_union_pw_qpolynomial *upwpq,
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_qpolynomial *
+isl_union_pw_qpolynomial_intersect_domain_wrapped_range(
+ __isl_take isl_union_pw_qpolynomial *upwpq,
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_intersect_params(
+ __isl_take isl_union_pw_qpolynomial *upwpq,
+ __isl_take isl_set *set);
+__isl_give isl_union_pw_qpolynomial *
+isl_union_pw_qpolynomial_subtract_domain_union_set(
+ __isl_take isl_union_pw_qpolynomial *upwpq,
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_qpolynomial *
+isl_union_pw_qpolynomial_subtract_domain_space(
+ __isl_take isl_union_pw_qpolynomial *upwpq,
+ __isl_take isl_space *space);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_subtract_domain(
+ __isl_take isl_union_pw_qpolynomial *upwpq,
+ __isl_take isl_union_set *uset);
+
+__isl_give isl_space *isl_union_pw_qpolynomial_get_space(
+ __isl_keep isl_union_pw_qpolynomial *upwqp);
+__isl_give isl_pw_qpolynomial_list *
+isl_union_pw_qpolynomial_get_pw_qpolynomial_list(
+ __isl_keep isl_union_pw_qpolynomial *upwqp);
+
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_set_dim_name(
+ __isl_take isl_union_pw_qpolynomial *upwqp,
+ enum isl_dim_type type, unsigned pos, const char *s);
+
+int isl_union_pw_qpolynomial_find_dim_by_name(
+ __isl_keep isl_union_pw_qpolynomial *upwqp,
+ enum isl_dim_type type, const char *name);
+
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_drop_dims(
+ __isl_take isl_union_pw_qpolynomial *upwqp,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_reset_user(
+ __isl_take isl_union_pw_qpolynomial *upwqp);
+
+__isl_export
+__isl_give isl_val *isl_union_pw_qpolynomial_eval(
+ __isl_take isl_union_pw_qpolynomial *upwqp, __isl_take isl_point *pnt);
+
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_coalesce(
+ __isl_take isl_union_pw_qpolynomial *upwqp);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_gist(
+ __isl_take isl_union_pw_qpolynomial *upwqp,
+ __isl_take isl_union_set *context);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_gist_params(
+ __isl_take isl_union_pw_qpolynomial *upwqp,
+ __isl_take isl_set *context);
+
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_align_params(
+ __isl_take isl_union_pw_qpolynomial *upwqp,
+ __isl_take isl_space *model);
+
+isl_size isl_union_pw_qpolynomial_n_pw_qpolynomial(
+ __isl_keep isl_union_pw_qpolynomial *upwqp);
+isl_stat isl_union_pw_qpolynomial_foreach_pw_qpolynomial(
+ __isl_keep isl_union_pw_qpolynomial *upwqp,
+ isl_stat (*fn)(__isl_take isl_pw_qpolynomial *pwqp, void *user),
+ void *user);
+isl_bool isl_union_pw_qpolynomial_every_pw_qpolynomial(
+ __isl_keep isl_union_pw_qpolynomial *upwqp,
+ isl_bool (*test)(__isl_keep isl_pw_qpolynomial *pwqp, void *user),
+ void *user);
+__isl_give isl_pw_qpolynomial *isl_union_pw_qpolynomial_extract_pw_qpolynomial(
+ __isl_keep isl_union_pw_qpolynomial *upwqp,
+ __isl_take isl_space *space);
+
+__isl_give isl_printer *isl_printer_print_union_pw_qpolynomial(
+ __isl_take isl_printer *p, __isl_keep isl_union_pw_qpolynomial *upwqp);
+
+isl_ctx *isl_union_pw_qpolynomial_fold_get_ctx(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf);
+
+isl_size isl_union_pw_qpolynomial_fold_dim(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf, enum isl_dim_type type);
+
+isl_bool isl_union_pw_qpolynomial_fold_involves_nan(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf);
+isl_bool isl_union_pw_qpolynomial_fold_plain_is_equal(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf1,
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf2);
+
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_pw_qpolynomial_fold_to_union_pw_qpolynomial_fold(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_from_pw_qpolynomial_fold(__isl_take isl_pw_qpolynomial_fold *pwf);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_zero_ctx(isl_ctx *ctx, enum isl_fold type);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_zero_space(__isl_take isl_space *space,
+ enum isl_fold type);
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_zero(
+ __isl_take isl_space *space, enum isl_fold type);
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_fold_pw_qpolynomial_fold(
+ __isl_take isl_union_pw_qpolynomial_fold *upwqp,
+ __isl_take isl_pw_qpolynomial_fold *pwqp);
+__isl_null isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_free(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf);
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_copy(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf);
+
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_fold(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf1,
+ __isl_take isl_union_pw_qpolynomial_fold *upwf2);
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_add_union_pw_qpolynomial(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_union_pw_qpolynomial *upwqp);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_scale_val(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf, __isl_take isl_val *v);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_scale_down_val(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf, __isl_take isl_val *v);
+
+__isl_give isl_union_set *isl_union_pw_qpolynomial_fold_domain(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_intersect_domain_space(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_space *space);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_intersect_domain_union_set(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_intersect_domain(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_intersect_domain_wrapped_domain(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_intersect_domain_wrapped_range(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_intersect_params(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_set *set);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_subtract_domain_union_set(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_subtract_domain_space(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_space *space);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_subtract_domain(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_union_set *uset);
+
+enum isl_fold isl_union_pw_qpolynomial_fold_get_type(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf);
+__isl_give isl_space *isl_union_pw_qpolynomial_fold_get_space(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf);
+__isl_give isl_pw_qpolynomial_fold_list *
+isl_union_pw_qpolynomial_fold_get_pw_qpolynomial_fold_list(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf);
+
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_set_dim_name(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ enum isl_dim_type type, unsigned pos, const char *s);
+
+int isl_union_pw_qpolynomial_fold_find_dim_by_name(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf,
+ enum isl_dim_type type, const char *name);
+
+__isl_give isl_union_pw_qpolynomial_fold *
+ isl_union_pw_qpolynomial_fold_drop_dims(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_reset_user(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf);
+
+__isl_give isl_val *isl_union_pw_qpolynomial_fold_eval(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_point *pnt);
+
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_coalesce(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf);
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_gist(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_union_set *context);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_gist_params(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_set *context);
+
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_align_params(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_space *model);
+
+isl_size isl_union_pw_qpolynomial_fold_n_pw_qpolynomial_fold(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf);
+isl_stat isl_union_pw_qpolynomial_fold_foreach_pw_qpolynomial_fold(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf,
+ isl_stat (*fn)(__isl_take isl_pw_qpolynomial_fold *pwf,
+ void *user), void *user);
+isl_bool isl_union_pw_qpolynomial_fold_every_pw_qpolynomial_fold(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf,
+ isl_bool (*test)(__isl_keep isl_pw_qpolynomial_fold *pwf,
+ void *user), void *user);
+__isl_give isl_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_extract_pw_qpolynomial_fold(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_space *space);
+
+__isl_give isl_printer *isl_printer_print_union_pw_qpolynomial_fold(
+ __isl_take isl_printer *p,
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf);
+
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_bound(
+ __isl_take isl_union_pw_qpolynomial *upwqp,
+ enum isl_fold type, isl_bool *tight);
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_set_apply_union_pw_qpolynomial_fold(
+ __isl_take isl_union_set *uset,
+ __isl_take isl_union_pw_qpolynomial_fold *upwf, isl_bool *tight);
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_map_apply_union_pw_qpolynomial_fold(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_union_pw_qpolynomial_fold *upwf, isl_bool *tight);
+
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_to_polynomial(
+ __isl_take isl_union_pw_qpolynomial *upwqp, int sign);
+
+ISL_DECLARE_LIST_FN(qpolynomial)
+ISL_DECLARE_LIST_FN(pw_qpolynomial)
+ISL_DECLARE_LIST_FN(pw_qpolynomial_fold)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/polynomial_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/polynomial_type.h
new file mode 100644
index 00000000000..f4a3f202934
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/polynomial_type.h
@@ -0,0 +1,38 @@
+#ifndef ISL_POLYNOMIAL_TYPE_H
+#define ISL_POLYNOMIAL_TYPE_H
+
+struct isl_qpolynomial;
+typedef struct isl_qpolynomial isl_qpolynomial;
+
+ISL_DECLARE_LIST_TYPE(qpolynomial)
+
+struct isl_term;
+typedef struct isl_term isl_term;
+
+struct __isl_export isl_pw_qpolynomial;
+typedef struct isl_pw_qpolynomial isl_pw_qpolynomial;
+
+ISL_DECLARE_LIST_TYPE(pw_qpolynomial)
+
+enum isl_fold {
+ isl_fold_error = -1,
+ isl_fold_min,
+ isl_fold_max,
+ isl_fold_list
+};
+
+struct isl_qpolynomial_fold;
+typedef struct isl_qpolynomial_fold isl_qpolynomial_fold;
+
+struct isl_pw_qpolynomial_fold;
+typedef struct isl_pw_qpolynomial_fold isl_pw_qpolynomial_fold;
+
+ISL_DECLARE_LIST_TYPE(pw_qpolynomial_fold)
+
+struct __isl_export isl_union_pw_qpolynomial;
+typedef struct isl_union_pw_qpolynomial isl_union_pw_qpolynomial;
+
+struct isl_union_pw_qpolynomial_fold;
+typedef struct isl_union_pw_qpolynomial_fold isl_union_pw_qpolynomial_fold;
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/printer.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/printer.h
new file mode 100644
index 00000000000..c3159546285
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/printer.h
@@ -0,0 +1,84 @@
+#ifndef ISL_PRINTER_H
+#define ISL_PRINTER_H
+
+#include <stdio.h>
+#include <isl/ctx.h>
+#include <isl/printer_type.h>
+#include <isl/id_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+__isl_give isl_printer *isl_printer_to_file(isl_ctx *ctx, FILE *file);
+__isl_give isl_printer *isl_printer_to_str(isl_ctx *ctx);
+__isl_null isl_printer *isl_printer_free(__isl_take isl_printer *printer);
+
+isl_ctx *isl_printer_get_ctx(__isl_keep isl_printer *printer);
+FILE *isl_printer_get_file(__isl_keep isl_printer *printer);
+
+__isl_give char *isl_printer_get_str(__isl_keep isl_printer *printer);
+
+__isl_give isl_printer *isl_printer_set_indent(__isl_take isl_printer *p,
+ int indent);
+__isl_give isl_printer *isl_printer_indent(__isl_take isl_printer *p,
+ int indent);
+
+#define ISL_FORMAT_ISL 0
+#define ISL_FORMAT_POLYLIB 1
+#define ISL_FORMAT_POLYLIB_CONSTRAINTS 2
+#define ISL_FORMAT_OMEGA 3
+#define ISL_FORMAT_C 4
+#define ISL_FORMAT_LATEX 5
+#define ISL_FORMAT_EXT_POLYLIB 6
+__isl_give isl_printer *isl_printer_set_output_format(__isl_take isl_printer *p,
+ int output_format);
+int isl_printer_get_output_format(__isl_keep isl_printer *p);
+
+#define ISL_YAML_STYLE_BLOCK 0
+#define ISL_YAML_STYLE_FLOW 1
+__isl_give isl_printer *isl_printer_set_yaml_style(__isl_take isl_printer *p,
+ int yaml_style);
+int isl_printer_get_yaml_style(__isl_keep isl_printer *p);
+
+__isl_give isl_printer *isl_printer_set_indent_prefix(__isl_take isl_printer *p,
+ const char *prefix);
+__isl_give isl_printer *isl_printer_set_prefix(__isl_take isl_printer *p,
+ const char *prefix);
+__isl_give isl_printer *isl_printer_set_suffix(__isl_take isl_printer *p,
+ const char *suffix);
+__isl_give isl_printer *isl_printer_set_isl_int_width(__isl_take isl_printer *p,
+ int width);
+
+isl_bool isl_printer_has_note(__isl_keep isl_printer *p,
+ __isl_keep isl_id *id);
+__isl_give isl_id *isl_printer_get_note(__isl_keep isl_printer *p,
+ __isl_take isl_id *id);
+__isl_give isl_printer *isl_printer_set_note(__isl_take isl_printer *p,
+ __isl_take isl_id *id, __isl_take isl_id *note);
+
+__isl_give isl_printer *isl_printer_start_line(__isl_take isl_printer *p);
+__isl_give isl_printer *isl_printer_end_line(__isl_take isl_printer *p);
+__isl_give isl_printer *isl_printer_print_double(__isl_take isl_printer *p,
+ double d);
+__isl_give isl_printer *isl_printer_print_int(__isl_take isl_printer *p, int i);
+__isl_give isl_printer *isl_printer_print_str(__isl_take isl_printer *p,
+ const char *s);
+
+__isl_give isl_printer *isl_printer_yaml_start_mapping(
+ __isl_take isl_printer *p);
+__isl_give isl_printer *isl_printer_yaml_end_mapping(
+ __isl_take isl_printer *p);
+__isl_give isl_printer *isl_printer_yaml_start_sequence(
+ __isl_take isl_printer *p);
+__isl_give isl_printer *isl_printer_yaml_end_sequence(
+ __isl_take isl_printer *p);
+__isl_give isl_printer *isl_printer_yaml_next(__isl_take isl_printer *p);
+
+__isl_give isl_printer *isl_printer_flush(__isl_take isl_printer *p);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/printer_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/printer_type.h
new file mode 100644
index 00000000000..985f8ca7fa4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/printer_type.h
@@ -0,0 +1,15 @@
+#ifndef ISL_PRINTER_TYPE_H
+#define ISL_PRINTER_TYPE_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_printer;
+typedef struct isl_printer isl_printer;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule.h
new file mode 100644
index 00000000000..880a1c2826a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule.h
@@ -0,0 +1,201 @@
+#ifndef ISL_SCHEDULE_H
+#define ISL_SCHEDULE_H
+
+#include <isl/union_set_type.h>
+#include <isl/union_map_type.h>
+#include <isl/schedule_type.h>
+#include <isl/aff_type.h>
+#include <isl/space_type.h>
+#include <isl/set_type.h>
+#include <isl/list.h>
+#include <isl/printer_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct __isl_export isl_schedule_constraints;
+typedef struct isl_schedule_constraints isl_schedule_constraints;
+
+isl_stat isl_options_set_schedule_max_coefficient(isl_ctx *ctx, int val);
+int isl_options_get_schedule_max_coefficient(isl_ctx *ctx);
+
+isl_stat isl_options_set_schedule_max_constant_term(isl_ctx *ctx, int val);
+int isl_options_get_schedule_max_constant_term(isl_ctx *ctx);
+
+isl_stat isl_options_set_schedule_maximize_band_depth(isl_ctx *ctx, int val);
+int isl_options_get_schedule_maximize_band_depth(isl_ctx *ctx);
+
+isl_stat isl_options_set_schedule_maximize_coincidence(isl_ctx *ctx, int val);
+int isl_options_get_schedule_maximize_coincidence(isl_ctx *ctx);
+
+isl_stat isl_options_set_schedule_outer_coincidence(isl_ctx *ctx, int val);
+int isl_options_get_schedule_outer_coincidence(isl_ctx *ctx);
+
+isl_stat isl_options_set_schedule_split_scaled(isl_ctx *ctx, int val);
+int isl_options_get_schedule_split_scaled(isl_ctx *ctx);
+
+isl_stat isl_options_set_schedule_treat_coalescing(isl_ctx *ctx, int val);
+int isl_options_get_schedule_treat_coalescing(isl_ctx *ctx);
+
+isl_stat isl_options_set_schedule_separate_components(isl_ctx *ctx, int val);
+int isl_options_get_schedule_separate_components(isl_ctx *ctx);
+
+isl_stat isl_options_set_schedule_serialize_sccs(isl_ctx *ctx, int val);
+int isl_options_get_schedule_serialize_sccs(isl_ctx *ctx);
+
+isl_stat isl_options_set_schedule_whole_component(isl_ctx *ctx, int val);
+int isl_options_get_schedule_whole_component(isl_ctx *ctx);
+
+isl_stat isl_options_set_schedule_carry_self_first(isl_ctx *ctx, int val);
+int isl_options_get_schedule_carry_self_first(isl_ctx *ctx);
+
+__isl_give isl_schedule_constraints *isl_schedule_constraints_copy(
+ __isl_keep isl_schedule_constraints *sc);
+__isl_export
+__isl_give isl_schedule_constraints *isl_schedule_constraints_on_domain(
+ __isl_take isl_union_set *domain);
+__isl_export
+__isl_give isl_schedule_constraints *isl_schedule_constraints_set_context(
+ __isl_take isl_schedule_constraints *sc, __isl_take isl_set *context);
+__isl_export
+__isl_give isl_schedule_constraints *isl_schedule_constraints_set_validity(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_take isl_union_map *validity);
+__isl_export
+__isl_give isl_schedule_constraints *isl_schedule_constraints_set_coincidence(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_take isl_union_map *coincidence);
+__isl_export
+__isl_give isl_schedule_constraints *isl_schedule_constraints_set_proximity(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_take isl_union_map *proximity);
+__isl_export
+__isl_give isl_schedule_constraints *
+isl_schedule_constraints_set_conditional_validity(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_take isl_union_map *condition,
+ __isl_take isl_union_map *validity);
+__isl_null isl_schedule_constraints *isl_schedule_constraints_free(
+ __isl_take isl_schedule_constraints *sc);
+
+isl_ctx *isl_schedule_constraints_get_ctx(
+ __isl_keep isl_schedule_constraints *sc);
+__isl_export
+__isl_give isl_union_set *isl_schedule_constraints_get_domain(
+ __isl_keep isl_schedule_constraints *sc);
+__isl_export
+__isl_give isl_set *isl_schedule_constraints_get_context(
+ __isl_keep isl_schedule_constraints *sc);
+__isl_export
+__isl_give isl_union_map *isl_schedule_constraints_get_validity(
+ __isl_keep isl_schedule_constraints *sc);
+__isl_export
+__isl_give isl_union_map *isl_schedule_constraints_get_coincidence(
+ __isl_keep isl_schedule_constraints *sc);
+__isl_export
+__isl_give isl_union_map *isl_schedule_constraints_get_proximity(
+ __isl_keep isl_schedule_constraints *sc);
+__isl_export
+__isl_give isl_union_map *isl_schedule_constraints_get_conditional_validity(
+ __isl_keep isl_schedule_constraints *sc);
+__isl_export
+__isl_give isl_union_map *
+isl_schedule_constraints_get_conditional_validity_condition(
+ __isl_keep isl_schedule_constraints *sc);
+
+__isl_give isl_schedule_constraints *isl_schedule_constraints_apply(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_take isl_union_map *umap);
+
+__isl_constructor
+__isl_give isl_schedule_constraints *isl_schedule_constraints_read_from_str(
+ isl_ctx *ctx, const char *str);
+__isl_give isl_schedule_constraints *isl_schedule_constraints_read_from_file(
+ isl_ctx *ctx, FILE *input);
+__isl_give isl_printer *isl_printer_print_schedule_constraints(
+ __isl_take isl_printer *p, __isl_keep isl_schedule_constraints *sc);
+void isl_schedule_constraints_dump(__isl_keep isl_schedule_constraints *sc);
+__isl_give char *isl_schedule_constraints_to_str(
+ __isl_keep isl_schedule_constraints *sc);
+
+__isl_export
+__isl_give isl_schedule *isl_schedule_constraints_compute_schedule(
+ __isl_take isl_schedule_constraints *sc);
+
+__isl_give isl_schedule *isl_union_set_compute_schedule(
+ __isl_take isl_union_set *domain,
+ __isl_take isl_union_map *validity,
+ __isl_take isl_union_map *proximity);
+
+__isl_give isl_schedule *isl_schedule_empty(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_schedule *isl_schedule_from_domain(
+ __isl_take isl_union_set *domain);
+__isl_give isl_schedule *isl_schedule_copy(__isl_keep isl_schedule *sched);
+__isl_null isl_schedule *isl_schedule_free(__isl_take isl_schedule *sched);
+__isl_export
+__isl_give isl_union_map *isl_schedule_get_map(__isl_keep isl_schedule *sched);
+
+isl_ctx *isl_schedule_get_ctx(__isl_keep isl_schedule *sched);
+isl_bool isl_schedule_plain_is_equal(__isl_keep isl_schedule *schedule1,
+ __isl_keep isl_schedule *schedule2);
+
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_get_root(
+ __isl_keep isl_schedule *schedule);
+__isl_export
+__isl_give isl_union_set *isl_schedule_get_domain(
+ __isl_keep isl_schedule *schedule);
+
+isl_stat isl_schedule_foreach_schedule_node_top_down(
+ __isl_keep isl_schedule *sched,
+ isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user),
+ void *user);
+__isl_give isl_schedule *isl_schedule_map_schedule_node_bottom_up(
+ __isl_take isl_schedule *schedule,
+ __isl_give isl_schedule_node *(*fn)(
+ __isl_take isl_schedule_node *node, void *user), void *user);
+
+__isl_give isl_schedule *isl_schedule_insert_context(
+ __isl_take isl_schedule *schedule, __isl_take isl_set *context);
+__isl_give isl_schedule *isl_schedule_insert_partial_schedule(
+ __isl_take isl_schedule *schedule,
+ __isl_take isl_multi_union_pw_aff *partial);
+__isl_give isl_schedule *isl_schedule_insert_guard(
+ __isl_take isl_schedule *schedule, __isl_take isl_set *guard);
+__isl_give isl_schedule *isl_schedule_sequence(
+ __isl_take isl_schedule *schedule1, __isl_take isl_schedule *schedule2);
+__isl_give isl_schedule *isl_schedule_set(
+ __isl_take isl_schedule *schedule1, __isl_take isl_schedule *schedule2);
+__isl_give isl_schedule *isl_schedule_intersect_domain(
+ __isl_take isl_schedule *schedule, __isl_take isl_union_set *domain);
+__isl_give isl_schedule *isl_schedule_gist_domain_params(
+ __isl_take isl_schedule *schedule, __isl_take isl_set *context);
+
+__isl_give isl_schedule *isl_schedule_reset_user(
+ __isl_take isl_schedule *schedule);
+__isl_give isl_schedule *isl_schedule_align_params(
+ __isl_take isl_schedule *schedule, __isl_take isl_space *space);
+__isl_overload
+__isl_give isl_schedule *isl_schedule_pullback_union_pw_multi_aff(
+ __isl_take isl_schedule *schedule,
+ __isl_take isl_union_pw_multi_aff *upma);
+__isl_give isl_schedule *isl_schedule_expand(__isl_take isl_schedule *schedule,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_schedule *expansion);
+
+__isl_give isl_schedule *isl_schedule_read_from_file(isl_ctx *ctx, FILE *input);
+__isl_constructor
+__isl_give isl_schedule *isl_schedule_read_from_str(isl_ctx *ctx,
+ const char *str);
+__isl_give isl_printer *isl_printer_print_schedule(__isl_take isl_printer *p,
+ __isl_keep isl_schedule *schedule);
+void isl_schedule_dump(__isl_keep isl_schedule *schedule);
+__isl_give char *isl_schedule_to_str(__isl_keep isl_schedule *schedule);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule_node.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule_node.h
new file mode 100644
index 00000000000..b336003222b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule_node.h
@@ -0,0 +1,300 @@
+#ifndef ISL_SCHEDULE_NODE_H
+#define ISL_SCHEDULE_NODE_H
+
+#include <isl/schedule_type.h>
+#include <isl/union_set_type.h>
+#include <isl/aff_type.h>
+#include <isl/ast_type.h>
+#include <isl/val_type.h>
+#include <isl/space_type.h>
+#include <isl/id_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_from_domain(
+ __isl_take isl_union_set *domain);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_from_extension(
+ __isl_take isl_union_map *extension);
+__isl_give isl_schedule_node *isl_schedule_node_copy(
+ __isl_keep isl_schedule_node *node);
+__isl_null isl_schedule_node *isl_schedule_node_free(
+ __isl_take isl_schedule_node *node);
+
+__isl_export
+isl_bool isl_schedule_node_is_equal(__isl_keep isl_schedule_node *node1,
+ __isl_keep isl_schedule_node *node2);
+
+isl_ctx *isl_schedule_node_get_ctx(__isl_keep isl_schedule_node *node);
+__isl_subclass(isl_schedule_node)
+enum isl_schedule_node_type isl_schedule_node_get_type(
+ __isl_keep isl_schedule_node *node);
+enum isl_schedule_node_type isl_schedule_node_get_parent_type(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_schedule *isl_schedule_node_get_schedule(
+ __isl_keep isl_schedule_node *node);
+
+__isl_export
+isl_stat isl_schedule_node_foreach_descendant_top_down(
+ __isl_keep isl_schedule_node *node,
+ isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user),
+ void *user);
+__isl_export
+isl_bool isl_schedule_node_every_descendant(__isl_keep isl_schedule_node *node,
+ isl_bool (*test)(__isl_keep isl_schedule_node *node, void *user),
+ void *user);
+__isl_export
+isl_stat isl_schedule_node_foreach_ancestor_top_down(
+ __isl_keep isl_schedule_node *node,
+ isl_stat (*fn)(__isl_keep isl_schedule_node *node, void *user),
+ void *user);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_map_descendant_bottom_up(
+ __isl_take isl_schedule_node *node,
+ __isl_give isl_schedule_node *(*fn)(__isl_take isl_schedule_node *node,
+ void *user), void *user);
+
+__isl_export
+isl_size isl_schedule_node_get_tree_depth(__isl_keep isl_schedule_node *node);
+__isl_export
+isl_bool isl_schedule_node_has_parent(__isl_keep isl_schedule_node *node);
+__isl_export
+isl_bool isl_schedule_node_has_children(__isl_keep isl_schedule_node *node);
+__isl_export
+isl_bool isl_schedule_node_has_previous_sibling(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+isl_bool isl_schedule_node_has_next_sibling(__isl_keep isl_schedule_node *node);
+__isl_export
+isl_size isl_schedule_node_n_children(__isl_keep isl_schedule_node *node);
+__isl_export
+isl_size isl_schedule_node_get_child_position(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+isl_size isl_schedule_node_get_ancestor_child_position(
+ __isl_keep isl_schedule_node *node,
+ __isl_keep isl_schedule_node *ancestor);
+__isl_give isl_schedule_node *isl_schedule_node_get_child(
+ __isl_keep isl_schedule_node *node, int pos);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_get_shared_ancestor(
+ __isl_keep isl_schedule_node *node1,
+ __isl_keep isl_schedule_node *node2);
+
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_root(
+ __isl_take isl_schedule_node *node);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_parent(
+ __isl_take isl_schedule_node *node);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_ancestor(
+ __isl_take isl_schedule_node *node, int generation);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_child(
+ __isl_take isl_schedule_node *node, int pos);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_first_child(
+ __isl_take isl_schedule_node *node);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_previous_sibling(
+ __isl_take isl_schedule_node *node);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_next_sibling(
+ __isl_take isl_schedule_node *node);
+
+__isl_export
+isl_bool isl_schedule_node_is_subtree_anchored(
+ __isl_keep isl_schedule_node *node);
+
+__isl_give isl_schedule_node *isl_schedule_node_group(
+ __isl_take isl_schedule_node *node, __isl_take isl_id *group_id);
+
+__isl_give isl_schedule_node *isl_schedule_node_sequence_splice_child(
+ __isl_take isl_schedule_node *node, int pos);
+
+__isl_give isl_space *isl_schedule_node_band_get_space(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_multi_union_pw_aff *isl_schedule_node_band_get_partial_schedule(
+ __isl_keep isl_schedule_node *node);
+__isl_give isl_union_map *isl_schedule_node_band_get_partial_schedule_union_map(
+ __isl_keep isl_schedule_node *node);
+enum isl_ast_loop_type isl_schedule_node_band_member_get_ast_loop_type(
+ __isl_keep isl_schedule_node *node, int pos);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_band_member_set_ast_loop_type(
+ __isl_take isl_schedule_node *node, int pos,
+ enum isl_ast_loop_type type);
+enum isl_ast_loop_type isl_schedule_node_band_member_get_isolate_ast_loop_type(
+ __isl_keep isl_schedule_node *node, int pos);
+__isl_give isl_schedule_node *
+isl_schedule_node_band_member_set_isolate_ast_loop_type(
+ __isl_take isl_schedule_node *node, int pos,
+ enum isl_ast_loop_type type);
+__isl_export
+__isl_give isl_union_set *isl_schedule_node_band_get_ast_build_options(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_band_set_ast_build_options(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *options);
+__isl_export
+__isl_give isl_set *isl_schedule_node_band_get_ast_isolate_option(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+isl_size isl_schedule_node_band_n_member(__isl_keep isl_schedule_node *node);
+__isl_export
+isl_bool isl_schedule_node_band_member_get_coincident(
+ __isl_keep isl_schedule_node *node, int pos);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_band_member_set_coincident(
+ __isl_take isl_schedule_node *node, int pos, int coincident);
+__isl_export
+isl_bool isl_schedule_node_band_get_permutable(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_band_set_permutable(
+ __isl_take isl_schedule_node *node, int permutable);
+
+isl_stat isl_options_set_tile_scale_tile_loops(isl_ctx *ctx, int val);
+int isl_options_get_tile_scale_tile_loops(isl_ctx *ctx);
+isl_stat isl_options_set_tile_shift_point_loops(isl_ctx *ctx, int val);
+int isl_options_get_tile_shift_point_loops(isl_ctx *ctx);
+
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_band_scale(
+ __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_band_scale_down(
+ __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_band_mod(
+ __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_band_shift(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_multi_union_pw_aff *shift);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_band_tile(
+ __isl_take isl_schedule_node *node, __isl_take isl_multi_val *sizes);
+__isl_give isl_schedule_node *isl_schedule_node_band_sink(
+ __isl_take isl_schedule_node *node);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_band_split(
+ __isl_take isl_schedule_node *node, int pos);
+
+__isl_export
+__isl_give isl_set *isl_schedule_node_context_get_context(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_union_set *isl_schedule_node_domain_get_domain(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_union_map *isl_schedule_node_expansion_get_expansion(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_schedule_node_expansion_get_contraction(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_union_map *isl_schedule_node_extension_get_extension(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_union_set *isl_schedule_node_filter_get_filter(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_set *isl_schedule_node_guard_get_guard(
+ __isl_keep isl_schedule_node *node);
+__isl_give isl_id *isl_schedule_node_mark_get_id(
+ __isl_keep isl_schedule_node *node);
+
+isl_size isl_schedule_node_get_schedule_depth(
+ __isl_keep isl_schedule_node *node);
+__isl_give isl_union_set *isl_schedule_node_get_domain(
+ __isl_keep isl_schedule_node *node);
+__isl_give isl_union_set *isl_schedule_node_get_universe_domain(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_multi_union_pw_aff *
+isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_union_pw_multi_aff *
+isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(
+ __isl_keep isl_schedule_node *node);
+__isl_export
+__isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_union_map(
+ __isl_keep isl_schedule_node *node);
+__isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_relation(
+ __isl_keep isl_schedule_node *node);
+__isl_give isl_union_map *isl_schedule_node_get_subtree_schedule_union_map(
+ __isl_keep isl_schedule_node *node);
+__isl_give isl_union_map *isl_schedule_node_get_subtree_expansion(
+ __isl_keep isl_schedule_node *node);
+__isl_give isl_union_pw_multi_aff *isl_schedule_node_get_subtree_contraction(
+ __isl_keep isl_schedule_node *node);
+
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_insert_context(
+ __isl_take isl_schedule_node *node, __isl_take isl_set *context);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_insert_partial_schedule(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_multi_union_pw_aff *schedule);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_insert_filter(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_insert_guard(
+ __isl_take isl_schedule_node *node, __isl_take isl_set *context);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_insert_mark(
+ __isl_take isl_schedule_node *node, __isl_take isl_id *mark);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_insert_sequence(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_set_list *filters);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_insert_set(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_set_list *filters);
+
+__isl_give isl_schedule_node *isl_schedule_node_cut(
+ __isl_take isl_schedule_node *node);
+__isl_give isl_schedule_node *isl_schedule_node_delete(
+ __isl_take isl_schedule_node *node);
+
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_order_before(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_order_after(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter);
+
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_graft_before(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_schedule_node *graft);
+__isl_export
+__isl_give isl_schedule_node *isl_schedule_node_graft_after(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_schedule_node *graft);
+
+__isl_give isl_schedule_node *isl_schedule_node_reset_user(
+ __isl_take isl_schedule_node *node);
+__isl_give isl_schedule_node *isl_schedule_node_align_params(
+ __isl_take isl_schedule_node *node, __isl_take isl_space *space);
+
+__isl_give isl_printer *isl_printer_print_schedule_node(
+ __isl_take isl_printer *p, __isl_keep isl_schedule_node *node);
+void isl_schedule_node_dump(__isl_keep isl_schedule_node *node);
+__isl_give char *isl_schedule_node_to_str(__isl_keep isl_schedule_node *node);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule_type.h
new file mode 100644
index 00000000000..f5a6d4cad91
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/schedule_type.h
@@ -0,0 +1,33 @@
+#ifndef ISL_SCHEDULE_TYPE_H
+#define ISL_SCHEDULE_TYPE_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+enum isl_schedule_node_type {
+ isl_schedule_node_error = -1,
+ isl_schedule_node_band,
+ isl_schedule_node_context,
+ isl_schedule_node_domain,
+ isl_schedule_node_expansion,
+ isl_schedule_node_extension,
+ isl_schedule_node_filter,
+ isl_schedule_node_leaf,
+ isl_schedule_node_guard,
+ isl_schedule_node_mark,
+ isl_schedule_node_sequence,
+ isl_schedule_node_set
+};
+
+struct __isl_export isl_schedule_node;
+typedef struct isl_schedule_node isl_schedule_node;
+
+struct __isl_export isl_schedule;
+typedef struct isl_schedule isl_schedule;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/set.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/set.h
new file mode 100644
index 00000000000..3e9b224fc48
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/set.h
@@ -0,0 +1,600 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_SET_H
+#define ISL_SET_H
+
+#include <isl/id_type.h>
+#include <isl/map_type.h>
+#include <isl/aff_type.h>
+#include <isl/list.h>
+#include <isl/mat.h>
+#include <isl/point.h>
+#include <isl/local_space.h>
+#include <isl/val_type.h>
+#include <isl/stdint.h>
+#include <isl/stride_info.h>
+#include <isl/fixed_box.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+isl_size isl_basic_set_n_dim(__isl_keep isl_basic_set *bset);
+isl_size isl_basic_set_n_param(__isl_keep isl_basic_set *bset);
+isl_size isl_basic_set_total_dim(__isl_keep const isl_basic_set *bset);
+isl_size isl_basic_set_dim(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type);
+
+isl_size isl_set_n_dim(__isl_keep isl_set *set);
+isl_size isl_set_n_param(__isl_keep isl_set *set);
+__isl_export
+isl_size isl_set_tuple_dim(__isl_keep isl_set *set);
+isl_size isl_set_dim(__isl_keep isl_set *set, enum isl_dim_type type);
+
+isl_ctx *isl_basic_set_get_ctx(__isl_keep isl_basic_set *bset);
+isl_ctx *isl_set_get_ctx(__isl_keep isl_set *set);
+__isl_give isl_space *isl_basic_set_get_space(__isl_keep isl_basic_set *bset);
+__isl_export
+__isl_give isl_space *isl_set_get_space(__isl_keep isl_set *set);
+__isl_give isl_set *isl_set_reset_space(__isl_take isl_set *set,
+ __isl_take isl_space *space);
+
+__isl_give isl_aff *isl_basic_set_get_div(__isl_keep isl_basic_set *bset,
+ int pos);
+
+__isl_give isl_local_space *isl_basic_set_get_local_space(
+ __isl_keep isl_basic_set *bset);
+
+const char *isl_basic_set_get_tuple_name(__isl_keep isl_basic_set *bset);
+isl_bool isl_set_has_tuple_name(__isl_keep isl_set *set);
+const char *isl_set_get_tuple_name(__isl_keep isl_set *set);
+__isl_give isl_basic_set *isl_basic_set_set_tuple_name(
+ __isl_take isl_basic_set *set, const char *s);
+__isl_give isl_set *isl_set_set_tuple_name(__isl_take isl_set *set,
+ const char *s);
+const char *isl_basic_set_get_dim_name(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_basic_set *isl_basic_set_set_dim_name(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos, const char *s);
+isl_bool isl_set_has_dim_name(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos);
+const char *isl_set_get_dim_name(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_set *isl_set_set_dim_name(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, const char *s);
+
+__isl_give isl_id *isl_basic_set_get_dim_id(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_basic_set *isl_basic_set_set_tuple_id(
+ __isl_take isl_basic_set *bset, __isl_take isl_id *id);
+__isl_give isl_set *isl_set_set_dim_id(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id);
+isl_bool isl_set_has_dim_id(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_id *isl_set_get_dim_id(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_set *isl_set_set_tuple_id(__isl_take isl_set *set,
+ __isl_take isl_id *id);
+__isl_give isl_set *isl_set_reset_tuple_id(__isl_take isl_set *set);
+isl_bool isl_set_has_tuple_id(__isl_keep isl_set *set);
+__isl_give isl_id *isl_set_get_tuple_id(__isl_keep isl_set *set);
+__isl_give isl_set *isl_set_reset_user(__isl_take isl_set *set);
+
+int isl_set_find_dim_by_id(__isl_keep isl_set *set, enum isl_dim_type type,
+ __isl_keep isl_id *id);
+int isl_set_find_dim_by_name(__isl_keep isl_set *set, enum isl_dim_type type,
+ const char *name);
+
+int isl_basic_set_is_rational(__isl_keep isl_basic_set *bset);
+
+__isl_null isl_basic_set *isl_basic_set_free(__isl_take isl_basic_set *bset);
+__isl_give isl_basic_set *isl_basic_set_copy(__isl_keep isl_basic_set *bset);
+__isl_give isl_basic_set *isl_basic_set_empty(__isl_take isl_space *space);
+__isl_give isl_basic_set *isl_basic_set_universe(__isl_take isl_space *space);
+__isl_give isl_basic_set *isl_basic_set_nat_universe(
+ __isl_take isl_space *space);
+__isl_give isl_basic_set *isl_basic_set_positive_orthant(
+ __isl_take isl_space *space);
+void isl_basic_set_print_internal(__isl_keep isl_basic_set *bset,
+ FILE *out, int indent);
+__isl_export
+__isl_give isl_basic_set *isl_basic_set_intersect(
+ __isl_take isl_basic_set *bset1,
+ __isl_take isl_basic_set *bset2);
+__isl_export
+__isl_give isl_basic_set *isl_basic_set_intersect_params(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2);
+__isl_export
+__isl_give isl_basic_set *isl_basic_set_apply(
+ __isl_take isl_basic_set *bset,
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_set *isl_basic_set_preimage_multi_aff(
+ __isl_take isl_basic_set *bset, __isl_take isl_multi_aff *ma);
+__isl_export
+__isl_give isl_basic_set *isl_basic_set_affine_hull(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_set *isl_basic_set_remove_dims(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_export
+__isl_give isl_basic_set *isl_basic_set_sample(__isl_take isl_basic_set *bset);
+__isl_export
+__isl_give isl_basic_set *isl_basic_set_detect_equalities(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_set *isl_basic_set_remove_redundancies(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_set *isl_set_remove_redundancies(__isl_take isl_set *set);
+__isl_give isl_basic_set *isl_basic_set_list_intersect(
+ __isl_take struct isl_basic_set_list *list);
+
+__isl_give isl_set *isl_set_list_union(__isl_take isl_set_list *list);
+
+__isl_give isl_basic_set *isl_basic_set_read_from_file(isl_ctx *ctx,
+ FILE *input);
+__isl_constructor
+__isl_give isl_basic_set *isl_basic_set_read_from_str(isl_ctx *ctx,
+ const char *str);
+__isl_give isl_set *isl_set_read_from_file(isl_ctx *ctx, FILE *input);
+__isl_constructor
+__isl_give isl_set *isl_set_read_from_str(isl_ctx *ctx, const char *str);
+void isl_basic_set_dump(__isl_keep isl_basic_set *bset);
+void isl_set_dump(__isl_keep isl_set *set);
+__isl_give isl_printer *isl_printer_print_basic_set(
+ __isl_take isl_printer *printer, __isl_keep isl_basic_set *bset);
+__isl_give isl_printer *isl_printer_print_set(__isl_take isl_printer *printer,
+ __isl_keep isl_set *map);
+__isl_give isl_basic_set *isl_basic_set_fix_si(__isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos, int value);
+__isl_give isl_basic_set *isl_basic_set_fix_val(__isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *v);
+__isl_give isl_set *isl_set_fix_si(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, int value);
+__isl_give isl_set *isl_set_lower_bound_si(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, int value);
+__isl_give isl_basic_set *isl_basic_set_lower_bound_val(
+ __isl_take isl_basic_set *bset, enum isl_dim_type type, unsigned pos,
+ __isl_take isl_val *value);
+__isl_give isl_set *isl_set_lower_bound_val(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *value);
+__isl_give isl_set *isl_set_upper_bound_si(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, int value);
+__isl_give isl_basic_set *isl_basic_set_upper_bound_val(
+ __isl_take isl_basic_set *bset, enum isl_dim_type type, unsigned pos,
+ __isl_take isl_val *value);
+__isl_give isl_set *isl_set_upper_bound_val(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *value);
+__isl_overload
+__isl_give isl_set *isl_set_lower_bound_multi_val(__isl_take isl_set *set,
+ __isl_take isl_multi_val *lower);
+__isl_overload
+__isl_give isl_set *isl_set_upper_bound_multi_val(__isl_take isl_set *set,
+ __isl_take isl_multi_val *upper);
+__isl_overload
+__isl_give isl_set *isl_set_lower_bound_multi_pw_aff(__isl_take isl_set *set,
+ __isl_take isl_multi_pw_aff *lower);
+__isl_overload
+__isl_give isl_set *isl_set_upper_bound_multi_pw_aff(__isl_take isl_set *set,
+ __isl_take isl_multi_pw_aff *upper);
+
+__isl_give isl_set *isl_set_equate(__isl_take isl_set *set,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2);
+
+__isl_export
+isl_bool isl_basic_set_is_equal(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2);
+isl_bool isl_basic_set_is_disjoint(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2);
+
+__isl_give isl_set *isl_basic_set_partial_lexmin(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty);
+__isl_give isl_set *isl_basic_set_partial_lexmax(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty);
+__isl_give isl_set *isl_set_partial_lexmin(
+ __isl_take isl_set *set, __isl_take isl_set *dom,
+ __isl_give isl_set **empty);
+__isl_give isl_set *isl_set_partial_lexmax(
+ __isl_take isl_set *set, __isl_take isl_set *dom,
+ __isl_give isl_set **empty);
+__isl_export
+__isl_give isl_set *isl_basic_set_lexmin(__isl_take isl_basic_set *bset);
+__isl_export
+__isl_give isl_set *isl_basic_set_lexmax(__isl_take isl_basic_set *bset);
+__isl_export
+__isl_give isl_set *isl_set_lexmin(__isl_take isl_set *set);
+__isl_export
+__isl_give isl_set *isl_set_lexmax(__isl_take isl_set *set);
+__isl_give isl_pw_multi_aff *isl_basic_set_partial_lexmin_pw_multi_aff(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty);
+__isl_give isl_pw_multi_aff *isl_basic_set_partial_lexmax_pw_multi_aff(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_set_lexmin_pw_multi_aff(
+ __isl_take isl_set *set);
+__isl_export
+__isl_give isl_pw_multi_aff *isl_set_lexmax_pw_multi_aff(
+ __isl_take isl_set *set);
+__isl_export
+__isl_give isl_multi_pw_aff *isl_set_min_multi_pw_aff(__isl_take isl_set *set);
+__isl_export
+__isl_give isl_multi_pw_aff *isl_set_max_multi_pw_aff(__isl_take isl_set *set);
+
+__isl_export
+__isl_give isl_set *isl_basic_set_union(
+ __isl_take isl_basic_set *bset1,
+ __isl_take isl_basic_set *bset2);
+
+int isl_basic_set_compare_at(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2, int pos);
+int isl_set_follows_at(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2, int pos);
+
+__isl_export
+__isl_give isl_basic_set *isl_basic_set_params(__isl_take isl_basic_set *bset);
+__isl_give isl_basic_set *isl_basic_set_from_params(
+ __isl_take isl_basic_set *bset);
+__isl_export
+__isl_give isl_set *isl_set_params(__isl_take isl_set *set);
+__isl_give isl_set *isl_set_from_params(__isl_take isl_set *set);
+
+__isl_export
+__isl_give isl_set *isl_set_bind(__isl_take isl_set *set,
+ __isl_take isl_multi_id *tuple);
+__isl_export
+__isl_give isl_set *isl_set_unbind_params(__isl_take isl_set *set,
+ __isl_take isl_multi_id *tuple);
+__isl_export
+__isl_give isl_map *isl_set_unbind_params_insert_domain(
+ __isl_take isl_set *set, __isl_take isl_multi_id *domain);
+
+isl_stat isl_basic_set_dims_get_sign(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos, unsigned n, int *signs);
+
+isl_bool isl_basic_set_plain_is_universe(__isl_keep isl_basic_set *bset);
+isl_bool isl_basic_set_is_universe(__isl_keep isl_basic_set *bset);
+isl_bool isl_basic_set_plain_is_empty(__isl_keep isl_basic_set *bset);
+__isl_export
+isl_bool isl_basic_set_is_empty(__isl_keep isl_basic_set *bset);
+isl_bool isl_basic_set_is_bounded(__isl_keep isl_basic_set *bset);
+__isl_export
+isl_bool isl_basic_set_is_subset(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2);
+isl_bool isl_basic_set_plain_is_equal(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2);
+
+__isl_export
+__isl_give isl_set *isl_set_empty(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_set *isl_set_universe(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_set *isl_space_universe_set(__isl_take isl_space *space);
+__isl_give isl_set *isl_set_nat_universe(__isl_take isl_space *space);
+__isl_give isl_set *isl_set_copy(__isl_keep isl_set *set);
+__isl_null isl_set *isl_set_free(__isl_take isl_set *set);
+__isl_export
+__isl_give isl_set *isl_basic_set_to_set(__isl_take isl_basic_set *bset);
+__isl_constructor
+__isl_give isl_set *isl_set_from_basic_set(__isl_take isl_basic_set *bset);
+__isl_export
+__isl_give isl_basic_set *isl_set_sample(__isl_take isl_set *set);
+__isl_export
+__isl_give isl_point *isl_basic_set_sample_point(__isl_take isl_basic_set *bset);
+__isl_export
+__isl_give isl_point *isl_set_sample_point(__isl_take isl_set *set);
+__isl_export
+__isl_give isl_set *isl_set_detect_equalities(__isl_take isl_set *set);
+__isl_export
+__isl_give isl_basic_set *isl_set_affine_hull(__isl_take isl_set *set);
+__isl_give isl_basic_set *isl_set_convex_hull(__isl_take isl_set *set);
+__isl_export
+__isl_give isl_basic_set *isl_set_polyhedral_hull(__isl_take isl_set *set);
+__isl_give isl_basic_set *isl_set_simple_hull(__isl_take isl_set *set);
+__isl_export
+__isl_give isl_basic_set *isl_set_unshifted_simple_hull(
+ __isl_take isl_set *set);
+__isl_give isl_basic_set *isl_set_plain_unshifted_simple_hull(
+ __isl_take isl_set *set);
+__isl_give isl_basic_set *isl_set_unshifted_simple_hull_from_set_list(
+ __isl_take isl_set *set, __isl_take isl_set_list *list);
+__isl_give isl_basic_set *isl_set_bounded_simple_hull(__isl_take isl_set *set);
+
+__isl_give isl_set *isl_set_union_disjoint(
+ __isl_take isl_set *set1, __isl_take isl_set *set2);
+__isl_export
+__isl_give isl_set *isl_set_union(
+ __isl_take isl_set *set1,
+ __isl_take isl_set *set2);
+__isl_export
+__isl_give isl_set *isl_set_product(__isl_take isl_set *set1,
+ __isl_take isl_set *set2);
+__isl_give isl_basic_set *isl_basic_set_flat_product(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2);
+__isl_give isl_set *isl_set_flat_product(__isl_take isl_set *set1,
+ __isl_take isl_set *set2);
+__isl_export
+__isl_give isl_set *isl_set_intersect(
+ __isl_take isl_set *set1,
+ __isl_take isl_set *set2);
+__isl_export
+__isl_give isl_set *isl_set_intersect_params(__isl_take isl_set *set,
+ __isl_take isl_set *params);
+__isl_give isl_set *isl_set_intersect_factor_domain(__isl_take isl_set *set,
+ __isl_take isl_set *domain);
+__isl_give isl_set *isl_set_intersect_factor_range(__isl_take isl_set *set,
+ __isl_take isl_set *range);
+__isl_export
+__isl_give isl_set *isl_set_subtract(
+ __isl_take isl_set *set1,
+ __isl_take isl_set *set2);
+__isl_export
+__isl_give isl_set *isl_set_complement(__isl_take isl_set *set);
+__isl_export
+__isl_give isl_set *isl_set_apply(
+ __isl_take isl_set *set,
+ __isl_take isl_map *map);
+__isl_overload
+__isl_give isl_set *isl_set_preimage_multi_aff(__isl_take isl_set *set,
+ __isl_take isl_multi_aff *ma);
+__isl_overload
+__isl_give isl_set *isl_set_preimage_pw_multi_aff(__isl_take isl_set *set,
+ __isl_take isl_pw_multi_aff *pma);
+__isl_overload
+__isl_give isl_set *isl_set_preimage_multi_pw_aff(__isl_take isl_set *set,
+ __isl_take isl_multi_pw_aff *mpa);
+__isl_give isl_set *isl_set_fix_val(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *v);
+__isl_give isl_set *isl_set_fix_dim_si(__isl_take isl_set *set,
+ unsigned dim, int value);
+__isl_give isl_basic_set *isl_basic_set_insert_dims(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos, unsigned n);
+__isl_give isl_set *isl_set_insert_dims(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_add_dims(__isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned n);
+__isl_give isl_set *isl_set_add_dims(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_move_dims(__isl_take isl_basic_set *bset,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+__isl_give isl_set *isl_set_move_dims(__isl_take isl_set *set,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_project_out(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_overload
+__isl_give isl_set *isl_set_project_out_param_id(__isl_take isl_set *set,
+ __isl_take isl_id *id);
+__isl_overload
+__isl_give isl_set *isl_set_project_out_param_id_list(__isl_take isl_set *set,
+ __isl_take isl_id_list *list);
+__isl_give isl_set *isl_set_project_out(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_export
+__isl_give isl_set *isl_set_project_out_all_params(__isl_take isl_set *set);
+__isl_give isl_map *isl_set_project_onto_map(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_remove_divs(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_set *isl_basic_set_eliminate(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_set *isl_set_eliminate(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_set *isl_set_eliminate_dims(__isl_take isl_set *set,
+ unsigned first, unsigned n);
+__isl_give isl_set *isl_set_remove_dims(__isl_take isl_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_remove_divs_involving_dims(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_set *isl_set_remove_divs_involving_dims(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_remove_unknown_divs(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_set *isl_set_remove_unknown_divs(__isl_take isl_set *set);
+__isl_give isl_set *isl_set_remove_divs(__isl_take isl_set *set);
+__isl_give isl_set *isl_set_split_dims(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+__isl_give isl_basic_set *isl_basic_set_drop_constraints_involving_dims(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_drop_constraints_not_involving_dims(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_set *isl_set_drop_constraints_involving_dims(
+ __isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_set *isl_set_drop_constraints_not_involving_dims(
+ __isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+__isl_export
+isl_bool isl_set_involves_locals(__isl_keep isl_set *set);
+
+isl_bool isl_basic_set_involves_dims(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n);
+isl_bool isl_set_involves_dims(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+void isl_set_print_internal(__isl_keep isl_set *set, FILE *out, int indent);
+isl_bool isl_set_plain_is_empty(__isl_keep isl_set *set);
+isl_bool isl_set_plain_is_universe(__isl_keep isl_set *set);
+isl_bool isl_set_is_params(__isl_keep isl_set *set);
+__isl_export
+isl_bool isl_set_is_empty(__isl_keep isl_set *set);
+isl_bool isl_set_is_bounded(__isl_keep isl_set *set);
+__isl_export
+isl_bool isl_set_is_subset(__isl_keep isl_set *set1, __isl_keep isl_set *set2);
+__isl_export
+isl_bool isl_set_is_strict_subset(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2);
+__isl_export
+isl_bool isl_set_is_equal(__isl_keep isl_set *set1, __isl_keep isl_set *set2);
+__isl_export
+isl_bool isl_set_is_disjoint(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2);
+__isl_export
+isl_bool isl_set_is_singleton(__isl_keep isl_set *set);
+isl_bool isl_set_is_box(__isl_keep isl_set *set);
+isl_bool isl_set_has_equal_space(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2);
+
+__isl_give isl_set *isl_set_sum(__isl_take isl_set *set1,
+ __isl_take isl_set *set2);
+__isl_give isl_basic_set *isl_basic_set_neg(__isl_take isl_basic_set *bset);
+__isl_give isl_set *isl_set_neg(__isl_take isl_set *set);
+
+__isl_give isl_set *isl_set_make_disjoint(__isl_take isl_set *set);
+__isl_give isl_set *isl_basic_set_compute_divs(__isl_take isl_basic_set *bset);
+__isl_give isl_set *isl_set_compute_divs(__isl_take isl_set *set);
+ISL_DEPRECATED
+__isl_give isl_set *isl_set_align_divs(__isl_take isl_set *set);
+
+__isl_export
+__isl_give isl_multi_val *isl_set_get_plain_multi_val_if_fixed(
+ __isl_keep isl_set *set);
+__isl_give isl_val *isl_set_plain_get_val_if_fixed(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos);
+isl_bool isl_set_dim_is_bounded(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos);
+isl_bool isl_set_dim_has_lower_bound(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos);
+isl_bool isl_set_dim_has_upper_bound(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos);
+isl_bool isl_set_dim_has_any_lower_bound(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos);
+isl_bool isl_set_dim_has_any_upper_bound(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos);
+
+__isl_export
+__isl_give isl_basic_set *isl_basic_set_gist(__isl_take isl_basic_set *bset,
+ __isl_take isl_basic_set *context);
+__isl_give isl_set *isl_set_gist_basic_set(__isl_take isl_set *set,
+ __isl_take isl_basic_set *context);
+__isl_export
+__isl_give isl_set *isl_set_gist(__isl_take isl_set *set,
+ __isl_take isl_set *context);
+__isl_give isl_set *isl_set_gist_params(__isl_take isl_set *set,
+ __isl_take isl_set *context);
+isl_stat isl_set_dim_residue_class_val(__isl_keep isl_set *set,
+ int pos, __isl_give isl_val **modulo, __isl_give isl_val **residue);
+
+__isl_give isl_stride_info *isl_set_get_stride_info(__isl_keep isl_set *set,
+ int pos);
+__isl_export
+__isl_give isl_val *isl_set_get_stride(__isl_keep isl_set *set, int pos);
+__isl_export
+__isl_give isl_fixed_box *isl_set_get_simple_fixed_box_hull(
+ __isl_keep isl_set *set);
+
+__isl_export
+__isl_give isl_set *isl_set_coalesce(__isl_take isl_set *set);
+
+int isl_set_plain_cmp(__isl_keep isl_set *set1, __isl_keep isl_set *set2);
+isl_bool isl_set_plain_is_equal(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2);
+isl_bool isl_set_plain_is_disjoint(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2);
+
+uint32_t isl_set_get_hash(__isl_keep isl_set *set);
+
+__isl_export
+isl_size isl_set_n_basic_set(__isl_keep isl_set *set);
+__isl_export
+isl_stat isl_set_foreach_basic_set(__isl_keep isl_set *set,
+ isl_stat (*fn)(__isl_take isl_basic_set *bset, void *user), void *user);
+__isl_give isl_basic_set_list *isl_set_get_basic_set_list(
+ __isl_keep isl_set *set);
+
+__isl_export
+isl_stat isl_set_foreach_point(__isl_keep isl_set *set,
+ isl_stat (*fn)(__isl_take isl_point *pnt, void *user), void *user);
+__isl_give isl_val *isl_set_count_val(__isl_keep isl_set *set);
+
+__isl_constructor
+__isl_give isl_basic_set *isl_basic_set_from_point(__isl_take isl_point *pnt);
+__isl_export
+__isl_give isl_set *isl_point_to_set(__isl_take isl_point *pnt);
+__isl_constructor
+__isl_give isl_set *isl_set_from_point(__isl_take isl_point *pnt);
+__isl_give isl_basic_set *isl_basic_set_box_from_points(
+ __isl_take isl_point *pnt1, __isl_take isl_point *pnt2);
+__isl_give isl_set *isl_set_box_from_points(__isl_take isl_point *pnt1,
+ __isl_take isl_point *pnt2);
+
+__isl_give isl_basic_set *isl_basic_set_lift(__isl_take isl_basic_set *bset);
+__isl_give isl_set *isl_set_lift(__isl_take isl_set *set);
+
+__isl_give isl_map *isl_set_lex_le_set(__isl_take isl_set *set1,
+ __isl_take isl_set *set2);
+__isl_give isl_map *isl_set_lex_lt_set(__isl_take isl_set *set1,
+ __isl_take isl_set *set2);
+__isl_give isl_map *isl_set_lex_ge_set(__isl_take isl_set *set1,
+ __isl_take isl_set *set2);
+__isl_give isl_map *isl_set_lex_gt_set(__isl_take isl_set *set1,
+ __isl_take isl_set *set2);
+
+int isl_set_size(__isl_keep isl_set *set);
+
+__isl_give isl_basic_set *isl_basic_set_align_params(
+ __isl_take isl_basic_set *bset, __isl_take isl_space *model);
+__isl_give isl_set *isl_set_align_params(__isl_take isl_set *set,
+ __isl_take isl_space *model);
+__isl_give isl_basic_set *isl_basic_set_drop_unused_params(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_set *isl_set_drop_unused_params(__isl_take isl_set *set);
+
+__isl_give isl_mat *isl_basic_set_equalities_matrix(
+ __isl_keep isl_basic_set *bset, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4);
+__isl_give isl_mat *isl_basic_set_inequalities_matrix(
+ __isl_keep isl_basic_set *bset, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4);
+__isl_give isl_basic_set *isl_basic_set_from_constraint_matrices(
+ __isl_take isl_space *space,
+ __isl_take isl_mat *eq, __isl_take isl_mat *ineq, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4);
+
+__isl_give isl_basic_set *isl_basic_set_from_multi_aff(
+ __isl_take isl_multi_aff *ma);
+
+__isl_export
+__isl_give isl_set *isl_multi_aff_as_set(__isl_take isl_multi_aff *ma);
+__isl_give isl_set *isl_set_from_multi_aff(__isl_take isl_multi_aff *ma);
+
+__isl_give isl_mat *isl_basic_set_reduced_basis(__isl_keep isl_basic_set *bset);
+
+__isl_give isl_basic_set *isl_basic_set_coefficients(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_set_list *isl_basic_set_list_coefficients(
+ __isl_take isl_basic_set_list *list);
+__isl_give isl_basic_set *isl_set_coefficients(__isl_take isl_set *set);
+__isl_give isl_basic_set *isl_basic_set_solutions(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_set *isl_set_solutions(__isl_take isl_set *set);
+
+__isl_give isl_pw_aff *isl_set_dim_max(__isl_take isl_set *set, int pos);
+__isl_give isl_pw_aff *isl_set_dim_min(__isl_take isl_set *set, int pos);
+
+__isl_give char *isl_basic_set_to_str(__isl_keep isl_basic_set *bset);
+__isl_give char *isl_set_to_str(__isl_keep isl_set *set);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/set_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/set_type.h
new file mode 100644
index 00000000000..ce349e1b5d4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/set_type.h
@@ -0,0 +1,6 @@
+#ifndef ISL_SET_TYPE_H
+#define ISL_SET_TYPE_H
+
+#include <isl/map_type.h>
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/space.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/space.h
new file mode 100644
index 00000000000..f3e1d8a15b2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/space.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_SPACE_H
+#define ISL_SPACE_H
+
+#include <isl/ctx.h>
+#include <isl/space_type.h>
+#include <isl/id_type.h>
+#include <isl/printer.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+isl_ctx *isl_space_get_ctx(__isl_keep isl_space *space);
+__isl_export
+__isl_give isl_space *isl_space_unit(isl_ctx *ctx);
+__isl_give isl_space *isl_space_alloc(isl_ctx *ctx,
+ unsigned nparam, unsigned n_in, unsigned n_out);
+__isl_give isl_space *isl_space_set_alloc(isl_ctx *ctx,
+ unsigned nparam, unsigned dim);
+__isl_give isl_space *isl_space_params_alloc(isl_ctx *ctx, unsigned nparam);
+__isl_give isl_space *isl_space_copy(__isl_keep isl_space *space);
+__isl_null isl_space *isl_space_free(__isl_take isl_space *space);
+
+isl_bool isl_space_is_params(__isl_keep isl_space *space);
+isl_bool isl_space_is_set(__isl_keep isl_space *space);
+isl_bool isl_space_is_map(__isl_keep isl_space *space);
+
+__isl_overload
+__isl_give isl_space *isl_space_add_param_id(__isl_take isl_space *space,
+ __isl_take isl_id *id);
+
+__isl_give isl_space *isl_space_set_tuple_name(__isl_take isl_space *space,
+ enum isl_dim_type type, const char *s);
+isl_bool isl_space_has_tuple_name(__isl_keep isl_space *space,
+ enum isl_dim_type type);
+__isl_keep const char *isl_space_get_tuple_name(__isl_keep isl_space *space,
+ enum isl_dim_type type);
+__isl_overload
+__isl_give isl_space *isl_space_set_domain_tuple_id(
+ __isl_take isl_space *space, __isl_take isl_id *id);
+__isl_overload
+__isl_give isl_space *isl_space_set_range_tuple_id(
+ __isl_take isl_space *space, __isl_take isl_id *id);
+__isl_give isl_space *isl_space_set_tuple_id(__isl_take isl_space *space,
+ enum isl_dim_type type, __isl_take isl_id *id);
+__isl_give isl_space *isl_space_reset_tuple_id(__isl_take isl_space *space,
+ enum isl_dim_type type);
+__isl_export
+isl_bool isl_space_has_domain_tuple_id(__isl_keep isl_space *space);
+__isl_export
+isl_bool isl_space_has_range_tuple_id(__isl_keep isl_space *space);
+isl_bool isl_space_has_tuple_id(__isl_keep isl_space *space,
+ enum isl_dim_type type);
+__isl_export
+__isl_give isl_id *isl_space_get_domain_tuple_id(
+ __isl_keep isl_space *space);
+__isl_export
+__isl_give isl_id *isl_space_get_range_tuple_id(
+ __isl_keep isl_space *space);
+__isl_give isl_id *isl_space_get_tuple_id(__isl_keep isl_space *space,
+ enum isl_dim_type type);
+__isl_give isl_space *isl_space_reset_user(__isl_take isl_space *space);
+
+__isl_give isl_space *isl_space_set_dim_id(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id);
+isl_bool isl_space_has_dim_id(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_id *isl_space_get_dim_id(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned pos);
+
+int isl_space_find_dim_by_id(__isl_keep isl_space *space,
+ enum isl_dim_type type, __isl_keep isl_id *id);
+int isl_space_find_dim_by_name(__isl_keep isl_space *space,
+ enum isl_dim_type type, const char *name);
+
+isl_bool isl_space_has_dim_name(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned pos);
+__isl_give isl_space *isl_space_set_dim_name(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned pos,
+ __isl_keep const char *name);
+__isl_keep const char *isl_space_get_dim_name(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned pos);
+
+ISL_DEPRECATED
+__isl_give isl_space *isl_space_extend(__isl_take isl_space *space,
+ unsigned nparam, unsigned n_in, unsigned n_out);
+__isl_give isl_space *isl_space_add_dims(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned n);
+__isl_give isl_space *isl_space_move_dims(__isl_take isl_space *space,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+__isl_give isl_space *isl_space_insert_dims(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned pos, unsigned n);
+__isl_give isl_space *isl_space_join(__isl_take isl_space *left,
+ __isl_take isl_space *right);
+__isl_export
+__isl_give isl_space *isl_space_product(__isl_take isl_space *left,
+ __isl_take isl_space *right);
+__isl_give isl_space *isl_space_domain_product(__isl_take isl_space *left,
+ __isl_take isl_space *right);
+__isl_give isl_space *isl_space_range_product(__isl_take isl_space *left,
+ __isl_take isl_space *right);
+__isl_give isl_space *isl_space_factor_domain(__isl_take isl_space *space);
+__isl_give isl_space *isl_space_factor_range(__isl_take isl_space *space);
+__isl_give isl_space *isl_space_domain_factor_domain(
+ __isl_take isl_space *space);
+__isl_give isl_space *isl_space_domain_factor_range(
+ __isl_take isl_space *space);
+__isl_give isl_space *isl_space_range_factor_domain(
+ __isl_take isl_space *space);
+__isl_give isl_space *isl_space_range_factor_range(
+ __isl_take isl_space *space);
+__isl_export
+__isl_give isl_space *isl_space_map_from_set(__isl_take isl_space *space);
+__isl_give isl_space *isl_space_map_from_domain_and_range(
+ __isl_take isl_space *domain, __isl_take isl_space *range);
+__isl_export
+__isl_give isl_space *isl_space_reverse(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_space *isl_space_range_reverse(__isl_take isl_space *space);
+__isl_give isl_space *isl_space_drop_dims(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned first, unsigned num);
+ISL_DEPRECATED
+__isl_give isl_space *isl_space_drop_inputs(__isl_take isl_space *space,
+ unsigned first, unsigned n);
+ISL_DEPRECATED
+__isl_give isl_space *isl_space_drop_outputs(__isl_take isl_space *space,
+ unsigned first, unsigned n);
+__isl_give isl_space *isl_space_drop_all_params(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_space *isl_space_domain(__isl_take isl_space *space);
+__isl_give isl_space *isl_space_from_domain(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_space *isl_space_range(__isl_take isl_space *space);
+__isl_give isl_space *isl_space_from_range(__isl_take isl_space *space);
+__isl_give isl_space *isl_space_domain_map(__isl_take isl_space *space);
+__isl_give isl_space *isl_space_range_map(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_space *isl_space_params(__isl_take isl_space *space);
+__isl_overload
+__isl_give isl_space *isl_space_add_unnamed_tuple_ui(
+ __isl_take isl_space *space, unsigned dim);
+__isl_overload
+__isl_give isl_space *isl_space_add_named_tuple_id_ui(
+ __isl_take isl_space *space, __isl_take isl_id *tuple_id, unsigned dim);
+__isl_give isl_space *isl_space_set_from_params(__isl_take isl_space *space);
+
+__isl_give isl_space *isl_space_align_params(__isl_take isl_space *space1,
+ __isl_take isl_space *space2);
+
+__isl_export
+isl_bool isl_space_is_wrapping(__isl_keep isl_space *space);
+isl_bool isl_space_domain_is_wrapping(__isl_keep isl_space *space);
+isl_bool isl_space_range_is_wrapping(__isl_keep isl_space *space);
+isl_bool isl_space_is_product(__isl_keep isl_space *space);
+__isl_export
+__isl_give isl_space *isl_space_wrap(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_space *isl_space_unwrap(__isl_take isl_space *space);
+
+isl_bool isl_space_can_zip(__isl_keep isl_space *space);
+__isl_give isl_space *isl_space_zip(__isl_take isl_space *space);
+
+isl_bool isl_space_can_curry(__isl_keep isl_space *space);
+__isl_export
+__isl_give isl_space *isl_space_curry(__isl_take isl_space *space);
+
+isl_bool isl_space_can_range_curry(__isl_keep isl_space *space);
+__isl_give isl_space *isl_space_range_curry(__isl_take isl_space *space);
+
+isl_bool isl_space_can_uncurry(__isl_keep isl_space *space);
+__isl_export
+__isl_give isl_space *isl_space_uncurry(__isl_take isl_space *space);
+
+isl_bool isl_space_is_domain(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+isl_bool isl_space_is_range(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+__isl_export
+isl_bool isl_space_is_equal(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+isl_bool isl_space_has_equal_params(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+isl_bool isl_space_has_equal_tuples(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+isl_bool isl_space_tuple_is_equal(__isl_keep isl_space *space1,
+ enum isl_dim_type type1, __isl_keep isl_space *space2,
+ enum isl_dim_type type2);
+ISL_DEPRECATED
+isl_bool isl_space_match(__isl_keep isl_space *space1, enum isl_dim_type type1,
+ __isl_keep isl_space *space2, enum isl_dim_type type2);
+isl_size isl_space_dim(__isl_keep isl_space *space, enum isl_dim_type type);
+
+__isl_export
+__isl_give isl_space *isl_space_flatten_domain(__isl_take isl_space *space);
+__isl_export
+__isl_give isl_space *isl_space_flatten_range(__isl_take isl_space *space);
+
+__isl_give char *isl_space_to_str(__isl_keep isl_space *space);
+__isl_give isl_printer *isl_printer_print_space(__isl_take isl_printer *p,
+ __isl_keep isl_space *space);
+void isl_space_dump(__isl_keep isl_space *space);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/space_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/space_type.h
new file mode 100644
index 00000000000..60c31b0d21d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/space_type.h
@@ -0,0 +1,27 @@
+#ifndef ISL_SPACE_TYPE_H
+#define ISL_SPACE_TYPE_H
+
+#include <isl/ctx.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct __isl_export isl_space;
+typedef struct isl_space isl_space;
+
+enum isl_dim_type {
+ isl_dim_cst,
+ isl_dim_param,
+ isl_dim_in,
+ isl_dim_out,
+ isl_dim_set = isl_dim_out,
+ isl_dim_div,
+ isl_dim_all
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stdint.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stdint.h
new file mode 100644
index 00000000000..9a6118bd859
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stdint.h
@@ -0,0 +1 @@
+#include <stdint.h>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stream.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stream.h
new file mode 100644
index 00000000000..b77797f327e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stream.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_STREAM_H
+#define ISL_STREAM_H
+
+#include <stdio.h>
+#include <isl/hash.h>
+#include <isl/aff_type.h>
+#include <isl/obj.h>
+#include <isl/val_type.h>
+#include <isl/schedule_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+enum isl_token_type { ISL_TOKEN_ERROR = -1,
+ ISL_TOKEN_UNKNOWN = 256, ISL_TOKEN_VALUE,
+ ISL_TOKEN_IDENT, ISL_TOKEN_GE,
+ ISL_TOKEN_LE, ISL_TOKEN_GT, ISL_TOKEN_LT,
+ ISL_TOKEN_NE, ISL_TOKEN_EQ_EQ,
+ ISL_TOKEN_LEX_GE, ISL_TOKEN_LEX_LE,
+ ISL_TOKEN_LEX_GT, ISL_TOKEN_LEX_LT,
+ ISL_TOKEN_TO, ISL_TOKEN_AND,
+ ISL_TOKEN_OR, ISL_TOKEN_EXISTS, ISL_TOKEN_NOT,
+ ISL_TOKEN_DEF, ISL_TOKEN_INFTY, ISL_TOKEN_NAN,
+ ISL_TOKEN_MIN, ISL_TOKEN_MAX, ISL_TOKEN_RAT,
+ ISL_TOKEN_TRUE, ISL_TOKEN_FALSE,
+ ISL_TOKEN_CEILD, ISL_TOKEN_FLOORD, ISL_TOKEN_MOD,
+ ISL_TOKEN_STRING,
+ ISL_TOKEN_MAP, ISL_TOKEN_AFF,
+ ISL_TOKEN_CEIL, ISL_TOKEN_FLOOR,
+ ISL_TOKEN_IMPLIES,
+ ISL_TOKEN_INT_DIV,
+ ISL_TOKEN_LAST };
+
+struct isl_token;
+
+__isl_give isl_val *isl_token_get_val(isl_ctx *ctx, struct isl_token *tok);
+__isl_give char *isl_token_get_str(isl_ctx *ctx, struct isl_token *tok);
+int isl_token_get_type(struct isl_token *tok);
+void isl_token_free(struct isl_token *tok);
+
+struct isl_stream;
+typedef struct isl_stream isl_stream;
+
+__isl_give isl_stream *isl_stream_new_file(isl_ctx *ctx, FILE *file);
+__isl_give isl_stream *isl_stream_new_str(isl_ctx *ctx, const char *str);
+void isl_stream_free(__isl_take isl_stream *s);
+
+isl_ctx *isl_stream_get_ctx(__isl_keep isl_stream *s);
+
+void isl_stream_error(__isl_keep isl_stream *s, struct isl_token *tok,
+ char *msg);
+
+struct isl_token *isl_stream_next_token(__isl_keep isl_stream *s);
+struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s);
+int isl_stream_next_token_is(__isl_keep isl_stream *s, int type);
+void isl_stream_push_token(__isl_keep isl_stream *s, struct isl_token *tok);
+void isl_stream_flush_tokens(__isl_keep isl_stream *s);
+int isl_stream_eat_if_available(__isl_keep isl_stream *s, int type);
+char *isl_stream_read_ident_if_available(__isl_keep isl_stream *s);
+int isl_stream_eat(__isl_keep isl_stream *s, int type);
+int isl_stream_is_empty(__isl_keep isl_stream *s);
+int isl_stream_skip_line(__isl_keep isl_stream *s);
+
+enum isl_token_type isl_stream_register_keyword(__isl_keep isl_stream *s,
+ const char *name);
+
+struct isl_obj isl_stream_read_obj(__isl_keep isl_stream *s);
+__isl_give isl_val *isl_stream_read_val(__isl_keep isl_stream *s);
+__isl_give isl_multi_aff *isl_stream_read_multi_aff(__isl_keep isl_stream *s);
+__isl_give isl_map *isl_stream_read_map(__isl_keep isl_stream *s);
+__isl_give isl_set *isl_stream_read_set(__isl_keep isl_stream *s);
+__isl_give isl_pw_qpolynomial *isl_stream_read_pw_qpolynomial(
+ __isl_keep isl_stream *s);
+__isl_give isl_union_set *isl_stream_read_union_set(__isl_keep isl_stream *s);
+__isl_give isl_union_map *isl_stream_read_union_map(__isl_keep isl_stream *s);
+__isl_give isl_schedule *isl_stream_read_schedule(isl_stream *s);
+
+int isl_stream_yaml_read_start_mapping(__isl_keep isl_stream *s);
+int isl_stream_yaml_read_end_mapping(__isl_keep isl_stream *s);
+int isl_stream_yaml_read_start_sequence(__isl_keep isl_stream *s);
+int isl_stream_yaml_read_end_sequence(__isl_keep isl_stream *s);
+int isl_stream_yaml_next(__isl_keep isl_stream *s);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stride_info.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stride_info.h
new file mode 100644
index 00000000000..7a4e0dcb1e3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/stride_info.h
@@ -0,0 +1,30 @@
+/*
+ * Use of this software is governed by the MIT license
+ */
+
+#ifndef ISL_STRIDE_INFO_H
+#define ISL_STRIDE_INFO_H
+
+#include <isl/val.h>
+#include <isl/aff_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_stride_info;
+typedef struct isl_stride_info isl_stride_info;
+
+isl_ctx *isl_stride_info_get_ctx(__isl_keep isl_stride_info *si);
+__isl_give isl_val *isl_stride_info_get_stride(__isl_keep isl_stride_info *si);
+__isl_give isl_aff *isl_stride_info_get_offset(__isl_keep isl_stride_info *si);
+__isl_null isl_stride_info *isl_stride_info_free(
+ __isl_take isl_stride_info *si);
+__isl_give isl_stride_info *isl_stride_info_copy(
+ __isl_keep isl_stride_info *si);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_map.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_map.h
new file mode 100644
index 00000000000..c630dc08079
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_map.h
@@ -0,0 +1,372 @@
+#ifndef ISL_UNION_MAP_H
+#define ISL_UNION_MAP_H
+
+#include <isl/stdint.h>
+#include <isl/space_type.h>
+#include <isl/aff_type.h>
+#include <isl/map_type.h>
+#include <isl/union_map_type.h>
+#include <isl/printer.h>
+#include <isl/val_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+isl_size isl_union_map_dim(__isl_keep isl_union_map *umap,
+ enum isl_dim_type type);
+isl_bool isl_union_map_involves_dims(__isl_keep isl_union_map *umap,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_id *isl_union_map_get_dim_id(__isl_keep isl_union_map *umap,
+ enum isl_dim_type type, unsigned pos);
+
+__isl_constructor
+__isl_give isl_union_map *isl_union_map_from_basic_map(
+ __isl_take isl_basic_map *bmap);
+__isl_export
+__isl_give isl_union_map *isl_map_to_union_map(__isl_take isl_map *map);
+__isl_constructor
+__isl_give isl_union_map *isl_union_map_from_map(__isl_take isl_map *map);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_empty_ctx(isl_ctx *ctx);
+__isl_give isl_union_map *isl_union_map_empty_space(
+ __isl_take isl_space *space);
+__isl_give isl_union_map *isl_union_map_empty(__isl_take isl_space *space);
+__isl_give isl_union_map *isl_union_map_copy(__isl_keep isl_union_map *umap);
+__isl_null isl_union_map *isl_union_map_free(__isl_take isl_union_map *umap);
+
+isl_ctx *isl_union_map_get_ctx(__isl_keep isl_union_map *umap);
+__isl_export
+__isl_give isl_space *isl_union_map_get_space(__isl_keep isl_union_map *umap);
+
+__isl_give isl_union_map *isl_union_map_reset_user(
+ __isl_take isl_union_map *umap);
+
+int isl_union_map_find_dim_by_name(__isl_keep isl_union_map *umap,
+ enum isl_dim_type type, const char *name);
+
+__isl_export
+__isl_give isl_union_map *isl_union_map_universe(
+ __isl_take isl_union_map *umap);
+__isl_give isl_set *isl_union_map_params(__isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_set *isl_union_map_domain(__isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_set *isl_union_map_range(__isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_domain_map(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_pw_multi_aff *isl_union_map_domain_map_union_pw_multi_aff(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_range_map(
+ __isl_take isl_union_map *umap);
+__isl_give isl_union_map *isl_union_set_wrapped_domain_map(
+ __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_map *isl_union_map_from_domain(
+ __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_map *isl_union_map_from_range(
+ __isl_take isl_union_set *uset);
+
+__isl_export
+__isl_give isl_union_map *isl_union_map_affine_hull(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_polyhedral_hull(
+ __isl_take isl_union_map *umap);
+__isl_give isl_union_map *isl_union_map_remove_redundancies(
+ __isl_take isl_union_map *umap);
+__isl_give isl_union_map *isl_union_map_simple_hull(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_coalesce(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_compute_divs(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_lexmin(__isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_lexmax(__isl_take isl_union_map *umap);
+
+__isl_give isl_union_map *isl_union_map_add_map(__isl_take isl_union_map *umap,
+ __isl_take isl_map *map);
+__isl_export
+__isl_give isl_union_map *isl_union_map_union(__isl_take isl_union_map *umap1,
+ __isl_take isl_union_map *umap2);
+__isl_export
+__isl_give isl_union_map *isl_union_map_subtract(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+__isl_export
+__isl_give isl_union_map *isl_union_map_intersect(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+__isl_export
+__isl_give isl_union_map *isl_union_map_intersect_params(
+ __isl_take isl_union_map *umap, __isl_take isl_set *set);
+__isl_export
+__isl_give isl_union_map *isl_union_map_product(__isl_take isl_union_map *umap1,
+ __isl_take isl_union_map *umap2);
+__isl_export
+__isl_give isl_union_map *isl_union_map_domain_product(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+__isl_give isl_union_map *isl_union_map_flat_domain_product(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+__isl_export
+__isl_give isl_union_map *isl_union_map_range_product(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+__isl_give isl_union_map *isl_union_map_flat_range_product(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+__isl_export
+__isl_give isl_union_map *isl_union_map_domain_factor_domain(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_domain_factor_range(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_range_factor_domain(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_range_factor_range(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_factor_domain(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_factor_range(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_gist(__isl_take isl_union_map *umap,
+ __isl_take isl_union_map *context);
+__isl_export
+__isl_give isl_union_map *isl_union_map_gist_params(
+ __isl_take isl_union_map *umap, __isl_take isl_set *set);
+__isl_export
+__isl_give isl_union_map *isl_union_map_gist_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_map *isl_union_map_gist_range(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset);
+
+__isl_overload
+__isl_give isl_union_map *isl_union_map_intersect_domain_union_set(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_intersect_domain_space(
+ __isl_take isl_union_map *umap, __isl_take isl_space *space);
+__isl_give isl_union_map *isl_union_map_intersect_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_intersect_range_union_set(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_intersect_range_space(
+ __isl_take isl_union_map *umap, __isl_take isl_space *space);
+__isl_give isl_union_map *isl_union_map_intersect_range(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_map *isl_union_map_intersect_domain_factor_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_map *factor);
+__isl_export
+__isl_give isl_union_map *isl_union_map_intersect_domain_factor_range(
+ __isl_take isl_union_map *umap, __isl_take isl_union_map *factor);
+__isl_export
+__isl_give isl_union_map *isl_union_map_intersect_range_factor_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_map *factor);
+__isl_export
+__isl_give isl_union_map *isl_union_map_intersect_range_factor_range(
+ __isl_take isl_union_map *umap, __isl_take isl_union_map *factor);
+
+__isl_export
+__isl_give isl_union_map *isl_union_map_subtract_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *dom);
+__isl_export
+__isl_give isl_union_map *isl_union_map_subtract_range(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *dom);
+
+__isl_export
+__isl_give isl_union_map *isl_union_map_apply_domain(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+__isl_export
+__isl_give isl_union_map *isl_union_map_apply_range(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_preimage_domain_multi_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_multi_aff *ma);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_preimage_range_multi_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_multi_aff *ma);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_preimage_domain_pw_multi_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_pw_multi_aff *pma);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_preimage_range_pw_multi_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_pw_multi_aff *pma);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_preimage_domain_multi_pw_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_multi_pw_aff *mpa);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_preimage_domain_union_pw_multi_aff(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_union_pw_multi_aff *upma);
+__isl_overload
+__isl_give isl_union_map *isl_union_map_preimage_range_union_pw_multi_aff(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_union_pw_multi_aff *upma);
+__isl_export
+__isl_give isl_union_map *isl_union_map_reverse(__isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_range_reverse(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_from_domain_and_range(
+ __isl_take isl_union_set *domain, __isl_take isl_union_set *range);
+
+__isl_export
+__isl_give isl_union_map *isl_union_map_detect_equalities(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_set *isl_union_map_deltas(__isl_take isl_union_map *umap);
+__isl_give isl_union_map *isl_union_map_deltas_map(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_set_identity(__isl_take isl_union_set *uset);
+
+__isl_give isl_union_map *isl_union_map_project_out(
+ __isl_take isl_union_map *umap,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_export
+__isl_give isl_union_map *isl_union_map_project_out_all_params(
+ __isl_take isl_union_map *umap);
+__isl_give isl_union_map *isl_union_map_remove_divs(
+ __isl_take isl_union_map *bmap);
+
+__isl_export
+__isl_give isl_union_set *isl_union_map_bind_range(
+ __isl_take isl_union_map *umap, __isl_take isl_multi_id *tuple);
+
+isl_bool isl_union_map_plain_is_empty(__isl_keep isl_union_map *umap);
+__isl_export
+isl_bool isl_union_map_is_empty(__isl_keep isl_union_map *umap);
+__isl_export
+isl_bool isl_union_map_is_single_valued(__isl_keep isl_union_map *umap);
+isl_bool isl_union_map_plain_is_injective(__isl_keep isl_union_map *umap);
+__isl_export
+isl_bool isl_union_map_is_injective(__isl_keep isl_union_map *umap);
+__isl_export
+isl_bool isl_union_map_is_bijective(__isl_keep isl_union_map *umap);
+isl_bool isl_union_map_is_identity(__isl_keep isl_union_map *umap);
+
+__isl_export
+isl_bool isl_union_map_is_subset(__isl_keep isl_union_map *umap1,
+ __isl_keep isl_union_map *umap2);
+__isl_export
+isl_bool isl_union_map_is_equal(__isl_keep isl_union_map *umap1,
+ __isl_keep isl_union_map *umap2);
+__isl_export
+isl_bool isl_union_map_is_disjoint(__isl_keep isl_union_map *umap1,
+ __isl_keep isl_union_map *umap2);
+__isl_export
+isl_bool isl_union_map_is_strict_subset(__isl_keep isl_union_map *umap1,
+ __isl_keep isl_union_map *umap2);
+
+uint32_t isl_union_map_get_hash(__isl_keep isl_union_map *umap);
+
+isl_size isl_union_map_n_map(__isl_keep isl_union_map *umap);
+__isl_export
+isl_stat isl_union_map_foreach_map(__isl_keep isl_union_map *umap,
+ isl_stat (*fn)(__isl_take isl_map *map, void *user), void *user);
+__isl_export
+__isl_give isl_map_list *isl_union_map_get_map_list(
+ __isl_keep isl_union_map *umap);
+__isl_export
+isl_bool isl_union_map_every_map(__isl_keep isl_union_map *umap,
+ isl_bool (*test)(__isl_keep isl_map *map, void *user), void *user);
+__isl_give isl_union_map *isl_union_map_remove_map_if(
+ __isl_take isl_union_map *umap,
+ isl_bool (*fn)(__isl_keep isl_map *map, void *user), void *user);
+isl_bool isl_union_map_contains(__isl_keep isl_union_map *umap,
+ __isl_keep isl_space *space);
+__isl_export
+__isl_give isl_map *isl_union_map_extract_map(__isl_keep isl_union_map *umap,
+ __isl_take isl_space *space);
+__isl_export
+isl_bool isl_union_map_isa_map(__isl_keep isl_union_map *umap);
+__isl_export
+__isl_give isl_map *isl_union_map_as_map(__isl_take isl_union_map *umap);
+__isl_give isl_map *isl_map_from_union_map(__isl_take isl_union_map *umap);
+
+__isl_give isl_basic_map *isl_union_map_sample(__isl_take isl_union_map *umap);
+
+__isl_overload
+__isl_give isl_union_map *isl_union_map_fixed_power_val(
+ __isl_take isl_union_map *umap, __isl_take isl_val *exp);
+__isl_give isl_union_map *isl_union_map_power(__isl_take isl_union_map *umap,
+ isl_bool *exact);
+__isl_give isl_union_map *isl_union_map_transitive_closure(
+ __isl_take isl_union_map *umap, isl_bool *exact);
+
+__isl_give isl_union_map *isl_union_map_lex_lt_union_map(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+__isl_give isl_union_map *isl_union_map_lex_le_union_map(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+__isl_give isl_union_map *isl_union_map_lex_gt_union_map(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+__isl_give isl_union_map *isl_union_map_lex_ge_union_map(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2);
+
+__isl_overload
+__isl_give isl_union_map *isl_union_map_eq_at_multi_union_pw_aff(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_multi_union_pw_aff *mupa);
+__isl_give isl_union_map *isl_union_map_lex_le_at_multi_union_pw_aff(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_multi_union_pw_aff *mupa);
+__isl_give isl_union_map *isl_union_map_lex_lt_at_multi_union_pw_aff(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_multi_union_pw_aff *mupa);
+__isl_give isl_union_map *isl_union_map_lex_ge_at_multi_union_pw_aff(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_multi_union_pw_aff *mupa);
+__isl_give isl_union_map *isl_union_map_lex_gt_at_multi_union_pw_aff(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_multi_union_pw_aff *mupa);
+
+__isl_give isl_union_map *isl_union_map_read_from_file(isl_ctx *ctx,
+ FILE *input);
+__isl_constructor
+__isl_give isl_union_map *isl_union_map_read_from_str(isl_ctx *ctx,
+ const char *str);
+__isl_give char *isl_union_map_to_str(__isl_keep isl_union_map *umap);
+__isl_give isl_printer *isl_printer_print_union_map(__isl_take isl_printer *p,
+ __isl_keep isl_union_map *umap);
+void isl_union_map_dump(__isl_keep isl_union_map *umap);
+
+__isl_export
+__isl_give isl_union_set *isl_union_map_wrap(__isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_set_unwrap(__isl_take isl_union_set *uset);
+
+__isl_export
+__isl_give isl_union_map *isl_union_map_zip(__isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_curry(__isl_take isl_union_map *umap);
+__isl_give isl_union_map *isl_union_map_range_curry(
+ __isl_take isl_union_map *umap);
+__isl_export
+__isl_give isl_union_map *isl_union_map_uncurry(__isl_take isl_union_map *umap);
+
+__isl_give isl_union_map *isl_union_map_align_params(
+ __isl_take isl_union_map *umap, __isl_take isl_space *model);
+__isl_give isl_union_set *isl_union_set_align_params(
+ __isl_take isl_union_set *uset, __isl_take isl_space *model);
+
+ISL_DECLARE_LIST_FN(union_map)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_map_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_map_type.h
new file mode 100644
index 00000000000..7b6e69f573e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_map_type.h
@@ -0,0 +1,24 @@
+#ifndef ISL_UNION_MAP_TYPE_H
+#define ISL_UNION_MAP_TYPE_H
+
+#include <isl/ctx.h>
+#include <isl/list.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct __isl_export isl_union_map;
+typedef struct isl_union_map isl_union_map;
+ISL_DECLARE_LIST_TYPE(union_map)
+#ifndef isl_union_set
+struct __isl_export isl_union_set;
+typedef struct isl_union_set isl_union_set;
+ISL_DECLARE_EXPORTED_LIST_TYPE(union_set)
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_set.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_set.h
new file mode 100644
index 00000000000..73cdb7f32d3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_set.h
@@ -0,0 +1,199 @@
+#ifndef ISL_UNION_SET_H
+#define ISL_UNION_SET_H
+
+#include <isl/point.h>
+#include <isl/union_map.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+isl_size isl_union_set_dim(__isl_keep isl_union_set *uset,
+ enum isl_dim_type type);
+
+__isl_constructor
+__isl_give isl_union_set *isl_union_set_from_basic_set(
+ __isl_take isl_basic_set *bset);
+__isl_export
+__isl_give isl_union_set *isl_set_to_union_set(__isl_take isl_set *set);
+__isl_constructor
+__isl_give isl_union_set *isl_union_set_from_set(__isl_take isl_set *set);
+__isl_overload
+__isl_give isl_union_set *isl_union_set_empty_ctx(isl_ctx *ctx);
+__isl_give isl_union_set *isl_union_set_empty_space(
+ __isl_take isl_space *space);
+__isl_give isl_union_set *isl_union_set_empty(__isl_take isl_space *space);
+__isl_give isl_union_set *isl_union_set_copy(__isl_keep isl_union_set *uset);
+__isl_null isl_union_set *isl_union_set_free(__isl_take isl_union_set *uset);
+
+isl_ctx *isl_union_set_get_ctx(__isl_keep isl_union_set *uset);
+__isl_export
+__isl_give isl_space *isl_union_set_get_space(__isl_keep isl_union_set *uset);
+
+__isl_give isl_union_set *isl_union_set_reset_user(
+ __isl_take isl_union_set *uset);
+
+__isl_export
+__isl_give isl_union_set *isl_union_set_universe(
+ __isl_take isl_union_set *uset);
+__isl_give isl_set *isl_union_set_params(__isl_take isl_union_set *uset);
+
+__isl_export
+__isl_give isl_union_set *isl_union_set_detect_equalities(
+ __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_set *isl_union_set_affine_hull(
+ __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_set *isl_union_set_polyhedral_hull(
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_set *isl_union_set_remove_redundancies(
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_set *isl_union_set_simple_hull(
+ __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_set *isl_union_set_coalesce(
+ __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_set *isl_union_set_compute_divs(
+ __isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_set *isl_union_set_lexmin(__isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_union_set *isl_union_set_lexmax(__isl_take isl_union_set *uset);
+
+__isl_give isl_union_set *isl_union_set_add_set(__isl_take isl_union_set *uset,
+ __isl_take isl_set *set);
+__isl_export
+__isl_give isl_union_set *isl_union_set_union(__isl_take isl_union_set *uset1,
+ __isl_take isl_union_set *uset2);
+__isl_export
+__isl_give isl_union_set *isl_union_set_subtract(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2);
+__isl_export
+__isl_give isl_union_set *isl_union_set_intersect(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2);
+__isl_export
+__isl_give isl_union_set *isl_union_set_intersect_params(
+ __isl_take isl_union_set *uset, __isl_take isl_set *set);
+__isl_give isl_union_set *isl_union_set_product(__isl_take isl_union_set *uset1,
+ __isl_take isl_union_set *uset2);
+__isl_export
+__isl_give isl_union_set *isl_union_set_gist(__isl_take isl_union_set *uset,
+ __isl_take isl_union_set *context);
+__isl_export
+__isl_give isl_union_set *isl_union_set_gist_params(
+ __isl_take isl_union_set *uset, __isl_take isl_set *set);
+
+__isl_export
+__isl_give isl_union_set *isl_union_set_apply(
+ __isl_take isl_union_set *uset, __isl_take isl_union_map *umap);
+__isl_overload
+__isl_give isl_union_set *isl_union_set_preimage_multi_aff(
+ __isl_take isl_union_set *uset, __isl_take isl_multi_aff *ma);
+__isl_overload
+__isl_give isl_union_set *isl_union_set_preimage_pw_multi_aff(
+ __isl_take isl_union_set *uset, __isl_take isl_pw_multi_aff *pma);
+__isl_overload
+__isl_give isl_union_set *isl_union_set_preimage_union_pw_multi_aff(
+ __isl_take isl_union_set *uset,
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_give isl_union_set *isl_union_set_project_out(
+ __isl_take isl_union_set *uset,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_union_set *isl_union_set_project_out_all_params(
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_set *isl_union_set_remove_divs(
+ __isl_take isl_union_set *bset);
+
+isl_bool isl_union_set_is_params(__isl_keep isl_union_set *uset);
+__isl_export
+isl_bool isl_union_set_is_empty(__isl_keep isl_union_set *uset);
+
+__isl_export
+isl_bool isl_union_set_is_subset(__isl_keep isl_union_set *uset1,
+ __isl_keep isl_union_set *uset2);
+__isl_export
+isl_bool isl_union_set_is_equal(__isl_keep isl_union_set *uset1,
+ __isl_keep isl_union_set *uset2);
+__isl_export
+isl_bool isl_union_set_is_disjoint(__isl_keep isl_union_set *uset1,
+ __isl_keep isl_union_set *uset2);
+__isl_export
+isl_bool isl_union_set_is_strict_subset(__isl_keep isl_union_set *uset1,
+ __isl_keep isl_union_set *uset2);
+
+uint32_t isl_union_set_get_hash(__isl_keep isl_union_set *uset);
+
+isl_size isl_union_set_n_set(__isl_keep isl_union_set *uset);
+__isl_export
+isl_stat isl_union_set_foreach_set(__isl_keep isl_union_set *uset,
+ isl_stat (*fn)(__isl_take isl_set *set, void *user), void *user);
+__isl_export
+isl_bool isl_union_set_every_set(__isl_keep isl_union_set *uset,
+ isl_bool (*test)(__isl_keep isl_set *set, void *user), void *user);
+__isl_give isl_basic_set_list *isl_union_set_get_basic_set_list(
+ __isl_keep isl_union_set *uset);
+__isl_export
+__isl_give isl_set_list *isl_union_set_get_set_list(
+ __isl_keep isl_union_set *uset);
+isl_bool isl_union_set_contains(__isl_keep isl_union_set *uset,
+ __isl_keep isl_space *space);
+__isl_export
+__isl_give isl_set *isl_union_set_extract_set(__isl_keep isl_union_set *uset,
+ __isl_take isl_space *space);
+__isl_export
+isl_bool isl_union_set_isa_set(__isl_keep isl_union_set *uset);
+__isl_export
+__isl_give isl_set *isl_union_set_as_set(__isl_take isl_union_set *uset);
+__isl_give isl_set *isl_set_from_union_set(__isl_take isl_union_set *uset);
+__isl_export
+isl_stat isl_union_set_foreach_point(__isl_keep isl_union_set *uset,
+ isl_stat (*fn)(__isl_take isl_point *pnt, void *user), void *user);
+
+__isl_give isl_basic_set *isl_union_set_sample(__isl_take isl_union_set *uset);
+__isl_export
+__isl_give isl_point *isl_union_set_sample_point(
+ __isl_take isl_union_set *uset);
+
+__isl_constructor
+__isl_give isl_union_set *isl_union_set_from_point(__isl_take isl_point *pnt);
+
+__isl_give isl_union_set *isl_union_set_lift(__isl_take isl_union_set *uset);
+
+__isl_give isl_union_map *isl_union_set_lex_lt_union_set(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2);
+__isl_give isl_union_map *isl_union_set_lex_le_union_set(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2);
+__isl_give isl_union_map *isl_union_set_lex_gt_union_set(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2);
+__isl_give isl_union_map *isl_union_set_lex_ge_union_set(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2);
+
+__isl_give isl_union_set *isl_union_set_coefficients(
+ __isl_take isl_union_set *bset);
+__isl_give isl_union_set *isl_union_set_solutions(
+ __isl_take isl_union_set *bset);
+
+__isl_give isl_union_set *isl_union_set_read_from_file(isl_ctx *ctx,
+ FILE *input);
+__isl_constructor
+__isl_give isl_union_set *isl_union_set_read_from_str(isl_ctx *ctx,
+ const char *str);
+__isl_give char *isl_union_set_to_str(__isl_keep isl_union_set *uset);
+__isl_give isl_printer *isl_printer_print_union_set(__isl_take isl_printer *p,
+ __isl_keep isl_union_set *uset);
+void isl_union_set_dump(__isl_keep isl_union_set *uset);
+
+ISL_DECLARE_EXPORTED_LIST_FN(union_set)
+ISL_DECLARE_EXPORTED_LIST_FN_READ(union_set)
+
+__isl_give isl_union_set *isl_union_set_list_union(
+ __isl_take isl_union_set_list *list);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_set_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_set_type.h
new file mode 100644
index 00000000000..86b2c317fa1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/union_set_type.h
@@ -0,0 +1,6 @@
+#ifndef ISL_UNION_SET_TYPE_H
+#define ISL_UNION_SET_TYPE_H
+
+#include <isl/union_map_type.h>
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/val.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/val.h
new file mode 100644
index 00000000000..942207db916
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/val.h
@@ -0,0 +1,179 @@
+#ifndef ISL_VAL_H
+#define ISL_VAL_H
+
+#include <isl/stdint.h>
+#include <isl/ctx.h>
+#include <isl/list.h>
+#include <isl/multi.h>
+#include <isl/printer.h>
+#include <isl/val_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+ISL_DECLARE_MULTI(val)
+ISL_DECLARE_MULTI_ARITH(val)
+ISL_DECLARE_MULTI_MIN_MAX(val)
+ISL_DECLARE_MULTI_ZERO(val)
+ISL_DECLARE_MULTI_NAN(val)
+ISL_DECLARE_MULTI_DIMS(val)
+ISL_DECLARE_MULTI_DIM_ID(val)
+ISL_DECLARE_MULTI_TUPLE_ID(val)
+ISL_DECLARE_MULTI_WITH_DOMAIN(val)
+
+__isl_export
+__isl_give isl_val *isl_val_zero(isl_ctx *ctx);
+__isl_export
+__isl_give isl_val *isl_val_one(isl_ctx *ctx);
+__isl_export
+__isl_give isl_val *isl_val_negone(isl_ctx *ctx);
+__isl_export
+__isl_give isl_val *isl_val_nan(isl_ctx *ctx);
+__isl_export
+__isl_give isl_val *isl_val_infty(isl_ctx *ctx);
+__isl_export
+__isl_give isl_val *isl_val_neginfty(isl_ctx *ctx);
+__isl_constructor
+__isl_give isl_val *isl_val_int_from_si(isl_ctx *ctx, long i);
+__isl_give isl_val *isl_val_int_from_ui(isl_ctx *ctx, unsigned long u);
+__isl_give isl_val *isl_val_int_from_chunks(isl_ctx *ctx, size_t n,
+ size_t size, const void *chunks);
+
+__isl_give isl_val *isl_val_copy(__isl_keep isl_val *v);
+__isl_null isl_val *isl_val_free(__isl_take isl_val *v);
+
+isl_ctx *isl_val_get_ctx(__isl_keep isl_val *val);
+uint32_t isl_val_get_hash(__isl_keep isl_val *val);
+__isl_export
+long isl_val_get_num_si(__isl_keep isl_val *v);
+__isl_export
+long isl_val_get_den_si(__isl_keep isl_val *v);
+__isl_give isl_val *isl_val_get_den_val(__isl_keep isl_val *v);
+double isl_val_get_d(__isl_keep isl_val *v);
+isl_size isl_val_n_abs_num_chunks(__isl_keep isl_val *v, size_t size);
+isl_stat isl_val_get_abs_num_chunks(__isl_keep isl_val *v, size_t size,
+ void *chunks);
+
+__isl_give isl_val *isl_val_set_si(__isl_take isl_val *v, long i);
+
+__isl_export
+__isl_give isl_val *isl_val_abs(__isl_take isl_val *v);
+__isl_export
+__isl_give isl_val *isl_val_neg(__isl_take isl_val *v);
+__isl_export
+__isl_give isl_val *isl_val_inv(__isl_take isl_val *v);
+__isl_export
+__isl_give isl_val *isl_val_floor(__isl_take isl_val *v);
+__isl_export
+__isl_give isl_val *isl_val_ceil(__isl_take isl_val *v);
+__isl_export
+__isl_give isl_val *isl_val_trunc(__isl_take isl_val *v);
+__isl_give isl_val *isl_val_2exp(__isl_take isl_val *v);
+__isl_export
+__isl_give isl_val *isl_val_pow2(__isl_take isl_val *v);
+__isl_export
+__isl_give isl_val *isl_val_min(__isl_take isl_val *v1, __isl_take isl_val *v2);
+__isl_export
+__isl_give isl_val *isl_val_max(__isl_take isl_val *v1, __isl_take isl_val *v2);
+__isl_export
+__isl_give isl_val *isl_val_add(__isl_take isl_val *v1, __isl_take isl_val *v2);
+__isl_give isl_val *isl_val_add_ui(__isl_take isl_val *v1, unsigned long v2);
+__isl_export
+__isl_give isl_val *isl_val_sub(__isl_take isl_val *v1, __isl_take isl_val *v2);
+__isl_give isl_val *isl_val_sub_ui(__isl_take isl_val *v1, unsigned long v2);
+__isl_export
+__isl_give isl_val *isl_val_mul(__isl_take isl_val *v1, __isl_take isl_val *v2);
+__isl_give isl_val *isl_val_mul_ui(__isl_take isl_val *v1, unsigned long v2);
+__isl_export
+__isl_give isl_val *isl_val_div(__isl_take isl_val *v1, __isl_take isl_val *v2);
+__isl_give isl_val *isl_val_div_ui(__isl_take isl_val *v1, unsigned long v2);
+__isl_export
+__isl_give isl_val *isl_val_mod(__isl_take isl_val *v1, __isl_take isl_val *v2);
+__isl_export
+__isl_give isl_val *isl_val_gcd(__isl_take isl_val *v1, __isl_take isl_val *v2);
+__isl_give isl_val *isl_val_gcdext(__isl_take isl_val *v1,
+ __isl_take isl_val *v2, __isl_give isl_val **x, __isl_give isl_val **y);
+
+__isl_export
+int isl_val_sgn(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_zero(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_one(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_negone(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_nonneg(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_nonpos(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_pos(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_neg(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_int(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_rat(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_nan(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_infty(__isl_keep isl_val *v);
+__isl_export
+isl_bool isl_val_is_neginfty(__isl_keep isl_val *v);
+
+__isl_export
+int isl_val_cmp_si(__isl_keep isl_val *v, long i);
+
+__isl_export
+isl_bool isl_val_lt(__isl_keep isl_val *v1, __isl_keep isl_val *v2);
+__isl_export
+isl_bool isl_val_le(__isl_keep isl_val *v1, __isl_keep isl_val *v2);
+__isl_export
+isl_bool isl_val_gt(__isl_keep isl_val *v1, __isl_keep isl_val *v2);
+isl_bool isl_val_gt_si(__isl_keep isl_val *v, long i);
+__isl_export
+isl_bool isl_val_ge(__isl_keep isl_val *v1, __isl_keep isl_val *v2);
+__isl_export
+isl_bool isl_val_eq(__isl_keep isl_val *v1, __isl_keep isl_val *v2);
+isl_bool isl_val_eq_si(__isl_keep isl_val *v, long i);
+__isl_export
+isl_bool isl_val_ne(__isl_keep isl_val *v1, __isl_keep isl_val *v2);
+__isl_export
+isl_bool isl_val_abs_eq(__isl_keep isl_val *v1, __isl_keep isl_val *v2);
+
+__isl_export
+isl_bool isl_val_is_divisible_by(__isl_keep isl_val *v1,
+ __isl_keep isl_val *v2);
+
+__isl_constructor
+__isl_give isl_val *isl_val_read_from_str(isl_ctx *ctx, const char *str);
+__isl_give isl_printer *isl_printer_print_val(__isl_take isl_printer *p,
+ __isl_keep isl_val *v);
+void isl_val_dump(__isl_keep isl_val *v);
+__isl_give char *isl_val_to_str(__isl_keep isl_val *v);
+
+isl_bool isl_multi_val_is_zero(__isl_keep isl_multi_val *mv);
+
+__isl_overload
+__isl_give isl_multi_val *isl_multi_val_add_val(__isl_take isl_multi_val *mv,
+ __isl_take isl_val *v);
+__isl_give isl_multi_val *isl_multi_val_mod_val(__isl_take isl_multi_val *mv,
+ __isl_take isl_val *v);
+
+__isl_constructor
+__isl_give isl_multi_val *isl_multi_val_read_from_str(isl_ctx *ctx,
+ const char *str);
+__isl_give isl_printer *isl_printer_print_multi_val(__isl_take isl_printer *p,
+ __isl_keep isl_multi_val *mv);
+void isl_multi_val_dump(__isl_keep isl_multi_val *mv);
+__isl_give char *isl_multi_val_to_str(__isl_keep isl_multi_val *mv);
+
+ISL_DECLARE_EXPORTED_LIST_FN(val)
+ISL_DECLARE_EXPORTED_LIST_FN_READ(val)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/val_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/val_type.h
new file mode 100644
index 00000000000..ede64013c6b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/val_type.h
@@ -0,0 +1,22 @@
+#ifndef ISL_VAL_TYPE_H
+#define ISL_VAL_TYPE_H
+
+#include <isl/list.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct __isl_export isl_val;
+typedef struct isl_val isl_val;
+
+ISL_DECLARE_EXPORTED_LIST_TYPE(val)
+
+struct __isl_export isl_multi_val;
+typedef struct isl_multi_val isl_multi_val;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/vec.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/vec.h
new file mode 100644
index 00000000000..71f7d64217a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/vec.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_VEC_H
+#define ISL_VEC_H
+
+#include <stdio.h>
+
+#include <isl/ctx.h>
+#include <isl/val_type.h>
+#include <isl/printer.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_vec;
+typedef struct isl_vec isl_vec;
+
+__isl_give isl_vec *isl_vec_alloc(isl_ctx *ctx, unsigned size);
+__isl_give isl_vec *isl_vec_zero(isl_ctx *ctx, unsigned size);
+__isl_give isl_vec *isl_vec_copy(__isl_keep isl_vec *vec);
+__isl_null isl_vec *isl_vec_free(__isl_take isl_vec *vec);
+
+isl_ctx *isl_vec_get_ctx(__isl_keep isl_vec *vec);
+
+isl_size isl_vec_size(__isl_keep isl_vec *vec);
+__isl_give isl_val *isl_vec_get_element_val(__isl_keep isl_vec *vec, int pos);
+__isl_give isl_vec *isl_vec_set_element_si(__isl_take isl_vec *vec,
+ int pos, int v);
+__isl_give isl_vec *isl_vec_set_element_val(__isl_take isl_vec *vec,
+ int pos, __isl_take isl_val *v);
+
+isl_bool isl_vec_is_equal(__isl_keep isl_vec *vec1, __isl_keep isl_vec *vec2);
+int isl_vec_cmp_element(__isl_keep isl_vec *vec1, __isl_keep isl_vec *vec2,
+ int pos);
+
+void isl_vec_dump(__isl_keep isl_vec *vec);
+__isl_give isl_printer *isl_printer_print_vec(__isl_take isl_printer *printer,
+ __isl_keep isl_vec *vec);
+
+__isl_give isl_vec *isl_vec_ceil(__isl_take isl_vec *vec);
+__isl_give isl_vec *isl_vec_normalize(__isl_take isl_vec *vec);
+__isl_give isl_vec *isl_vec_set_si(__isl_take isl_vec *vec, int v);
+__isl_give isl_vec *isl_vec_set_val(__isl_take isl_vec *vec,
+ __isl_take isl_val *v);
+__isl_give isl_vec *isl_vec_clr(__isl_take isl_vec *vec);
+__isl_give isl_vec *isl_vec_neg(__isl_take isl_vec *vec);
+__isl_give isl_vec *isl_vec_add(__isl_take isl_vec *vec1,
+ __isl_take isl_vec *vec2);
+__isl_give isl_vec *isl_vec_extend(__isl_take isl_vec *vec, unsigned size);
+__isl_give isl_vec *isl_vec_zero_extend(__isl_take isl_vec *vec, unsigned size);
+__isl_give isl_vec *isl_vec_concat(__isl_take isl_vec *vec1,
+ __isl_take isl_vec *vec2);
+
+__isl_give isl_vec *isl_vec_sort(__isl_take isl_vec *vec);
+
+__isl_give isl_vec *isl_vec_read_from_file(isl_ctx *ctx, FILE *input);
+
+__isl_give isl_vec *isl_vec_drop_els(__isl_take isl_vec *vec,
+ unsigned pos, unsigned n);
+__isl_give isl_vec *isl_vec_add_els(__isl_take isl_vec *vec, unsigned n);
+__isl_give isl_vec *isl_vec_insert_els(__isl_take isl_vec *vec,
+ unsigned pos, unsigned n);
+__isl_give isl_vec *isl_vec_insert_zero_els(__isl_take isl_vec *vec,
+ unsigned pos, unsigned n);
+__isl_give isl_vec *isl_vec_move_els(__isl_take isl_vec *vec,
+ unsigned dst_col, unsigned src_col, unsigned n);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/version.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/version.h
new file mode 100644
index 00000000000..7f8f23d6945
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/version.h
@@ -0,0 +1,14 @@
+#ifndef ISL_VERSION_H
+#define ISL_VERSION_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+const char *isl_version(void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/vertices.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/vertices.h
new file mode 100644
index 00000000000..9a7ac508c56
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/include/isl/vertices.h
@@ -0,0 +1,47 @@
+#ifndef ISL_VERTICES_H
+#define ISL_VERTICES_H
+
+#include <isl/aff_type.h>
+#include <isl/set_type.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_external_vertex;
+typedef struct isl_external_vertex isl_vertex;
+
+struct isl_cell;
+typedef struct isl_cell isl_cell;
+
+struct isl_vertices;
+typedef struct isl_vertices isl_vertices;
+
+isl_ctx *isl_vertex_get_ctx(__isl_keep isl_vertex *vertex);
+isl_size isl_vertex_get_id(__isl_keep isl_vertex *vertex);
+__isl_give isl_basic_set *isl_vertex_get_domain(__isl_keep isl_vertex *vertex);
+__isl_give isl_multi_aff *isl_vertex_get_expr(__isl_keep isl_vertex *vertex);
+__isl_null isl_vertex *isl_vertex_free(__isl_take isl_vertex *vertex);
+
+__isl_give isl_vertices *isl_basic_set_compute_vertices(
+ __isl_keep isl_basic_set *bset);
+isl_ctx *isl_vertices_get_ctx(__isl_keep isl_vertices *vertices);
+isl_size isl_vertices_get_n_vertices(__isl_keep isl_vertices *vertices);
+isl_stat isl_vertices_foreach_vertex(__isl_keep isl_vertices *vertices,
+ isl_stat (*fn)(__isl_take isl_vertex *vertex, void *user), void *user);
+__isl_null isl_vertices *isl_vertices_free(__isl_take isl_vertices *vertices);
+
+isl_ctx *isl_cell_get_ctx(__isl_keep isl_cell *cell);
+__isl_give isl_basic_set *isl_cell_get_domain(__isl_keep isl_cell *cell);
+isl_stat isl_cell_foreach_vertex(__isl_keep isl_cell *cell,
+ isl_stat (*fn)(__isl_take isl_vertex *vertex, void *user), void *user);
+__isl_null isl_cell *isl_cell_free(__isl_take isl_cell *cell);
+
+isl_stat isl_vertices_foreach_cell(__isl_keep isl_vertices *vertices,
+ isl_stat (*fn)(__isl_take isl_cell *cell, void *user), void *user);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff.c
new file mode 100644
index 00000000000..40ca78dec6b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff.c
@@ -0,0 +1,10096 @@
+/*
+ * Copyright 2011 INRIA Saclay
+ * Copyright 2011 Sven Verdoolaege
+ * Copyright 2012-2014 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ * Copyright 2016 Sven Verdoolaege
+ * Copyright 2018,2020 Cerebras Systems
+ * Copyright 2021 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ * and Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl_union_map_private.h>
+#include <isl_aff_private.h>
+#include <isl_space_private.h>
+#include <isl_local_space_private.h>
+#include <isl_vec_private.h>
+#include <isl_mat_private.h>
+#include <isl_id_private.h>
+#include <isl/constraint.h>
+#include <isl_seq.h>
+#include <isl/set.h>
+#include <isl_val_private.h>
+#include <isl_point_private.h>
+#include <isl_config.h>
+
+#undef EL_BASE
+#define EL_BASE aff
+
+#include <isl_list_templ.c>
+#include <isl_list_read_templ.c>
+
+#undef EL_BASE
+#define EL_BASE pw_aff
+
+#include <isl_list_templ.c>
+#include <isl_list_read_templ.c>
+
+#undef EL_BASE
+#define EL_BASE pw_multi_aff
+
+#include <isl_list_templ.c>
+#include <isl_list_read_templ.c>
+
+#undef EL_BASE
+#define EL_BASE union_pw_aff
+
+#include <isl_list_templ.c>
+#include <isl_list_read_templ.c>
+
+#undef EL_BASE
+#define EL_BASE union_pw_multi_aff
+
+#include <isl_list_templ.c>
+
+__isl_give isl_aff *isl_aff_alloc_vec(__isl_take isl_local_space *ls,
+ __isl_take isl_vec *v)
+{
+ isl_aff *aff;
+
+ if (!ls || !v)
+ goto error;
+
+ aff = isl_calloc_type(v->ctx, struct isl_aff);
+ if (!aff)
+ goto error;
+
+ aff->ref = 1;
+ aff->ls = ls;
+ aff->v = v;
+
+ return aff;
+error:
+ isl_local_space_free(ls);
+ isl_vec_free(v);
+ return NULL;
+}
+
+__isl_give isl_aff *isl_aff_alloc(__isl_take isl_local_space *ls)
+{
+ isl_ctx *ctx;
+ isl_vec *v;
+ isl_size total;
+
+ if (!ls)
+ return NULL;
+
+ ctx = isl_local_space_get_ctx(ls);
+ if (!isl_local_space_divs_known(ls))
+ isl_die(ctx, isl_error_invalid, "local space has unknown divs",
+ goto error);
+ if (!isl_local_space_is_set(ls))
+ isl_die(ctx, isl_error_invalid,
+ "domain of affine expression should be a set",
+ goto error);
+
+ total = isl_local_space_dim(ls, isl_dim_all);
+ if (total < 0)
+ goto error;
+ v = isl_vec_alloc(ctx, 1 + 1 + total);
+ return isl_aff_alloc_vec(ls, v);
+error:
+ isl_local_space_free(ls);
+ return NULL;
+}
+
+__isl_give isl_aff *isl_aff_copy(__isl_keep isl_aff *aff)
+{
+ if (!aff)
+ return NULL;
+
+ aff->ref++;
+ return aff;
+}
+
+__isl_give isl_aff *isl_aff_dup(__isl_keep isl_aff *aff)
+{
+ if (!aff)
+ return NULL;
+
+ return isl_aff_alloc_vec(isl_local_space_copy(aff->ls),
+ isl_vec_copy(aff->v));
+}
+
+__isl_give isl_aff *isl_aff_cow(__isl_take isl_aff *aff)
+{
+ if (!aff)
+ return NULL;
+
+ if (aff->ref == 1)
+ return aff;
+ aff->ref--;
+ return isl_aff_dup(aff);
+}
+
+__isl_give isl_aff *isl_aff_zero_on_domain(__isl_take isl_local_space *ls)
+{
+ isl_aff *aff;
+
+ aff = isl_aff_alloc(ls);
+ if (!aff)
+ return NULL;
+
+ isl_int_set_si(aff->v->el[0], 1);
+ isl_seq_clr(aff->v->el + 1, aff->v->size - 1);
+
+ return aff;
+}
+
+/* Return an affine expression that is equal to zero on domain space "space".
+ */
+__isl_give isl_aff *isl_aff_zero_on_domain_space(__isl_take isl_space *space)
+{
+ return isl_aff_zero_on_domain(isl_local_space_from_space(space));
+}
+
+/* This function performs the same operation as isl_aff_zero_on_domain_space,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give isl_aff *isl_space_zero_aff_on_domain(__isl_take isl_space *space)
+{
+ return isl_aff_zero_on_domain_space(space);
+}
+
+/* Return a piecewise affine expression defined on the specified domain
+ * that is equal to zero.
+ */
+__isl_give isl_pw_aff *isl_pw_aff_zero_on_domain(__isl_take isl_local_space *ls)
+{
+ return isl_pw_aff_from_aff(isl_aff_zero_on_domain(ls));
+}
+
+/* Change "aff" into a NaN.
+ *
+ * Note that this function gets called from isl_aff_nan_on_domain,
+ * so "aff" may not have been initialized yet.
+ */
+static __isl_give isl_aff *isl_aff_set_nan(__isl_take isl_aff *aff)
+{
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->v = isl_vec_clr(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ return aff;
+}
+
+/* Return an affine expression defined on the specified domain
+ * that represents NaN.
+ */
+__isl_give isl_aff *isl_aff_nan_on_domain(__isl_take isl_local_space *ls)
+{
+ isl_aff *aff;
+
+ aff = isl_aff_alloc(ls);
+ return isl_aff_set_nan(aff);
+}
+
+/* Return an affine expression defined on the specified domain space
+ * that represents NaN.
+ */
+__isl_give isl_aff *isl_aff_nan_on_domain_space(__isl_take isl_space *space)
+{
+ return isl_aff_nan_on_domain(isl_local_space_from_space(space));
+}
+
+/* Return a piecewise affine expression defined on the specified domain space
+ * that represents NaN.
+ */
+__isl_give isl_pw_aff *isl_pw_aff_nan_on_domain_space(
+ __isl_take isl_space *space)
+{
+ return isl_pw_aff_from_aff(isl_aff_nan_on_domain_space(space));
+}
+
+/* Return a piecewise affine expression defined on the specified domain
+ * that represents NaN.
+ */
+__isl_give isl_pw_aff *isl_pw_aff_nan_on_domain(__isl_take isl_local_space *ls)
+{
+ return isl_pw_aff_from_aff(isl_aff_nan_on_domain(ls));
+}
+
+/* Return an affine expression that is equal to "val" on
+ * domain local space "ls".
+ */
+__isl_give isl_aff *isl_aff_val_on_domain(__isl_take isl_local_space *ls,
+ __isl_take isl_val *val)
+{
+ isl_aff *aff;
+
+ if (!ls || !val)
+ goto error;
+ if (!isl_val_is_rat(val))
+ isl_die(isl_val_get_ctx(val), isl_error_invalid,
+ "expecting rational value", goto error);
+
+ aff = isl_aff_alloc(isl_local_space_copy(ls));
+ if (!aff)
+ goto error;
+
+ isl_seq_clr(aff->v->el + 2, aff->v->size - 2);
+ isl_int_set(aff->v->el[1], val->n);
+ isl_int_set(aff->v->el[0], val->d);
+
+ isl_local_space_free(ls);
+ isl_val_free(val);
+ return aff;
+error:
+ isl_local_space_free(ls);
+ isl_val_free(val);
+ return NULL;
+}
+
+/* Return an affine expression that is equal to "val" on domain space "space".
+ */
+__isl_give isl_aff *isl_aff_val_on_domain_space(__isl_take isl_space *space,
+ __isl_take isl_val *val)
+{
+ return isl_aff_val_on_domain(isl_local_space_from_space(space), val);
+}
+
+/* Return an affine expression that is equal to the specified dimension
+ * in "ls".
+ */
+__isl_give isl_aff *isl_aff_var_on_domain(__isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos)
+{
+ isl_space *space;
+ isl_aff *aff;
+
+ if (!ls)
+ return NULL;
+
+ space = isl_local_space_get_space(ls);
+ if (!space)
+ goto error;
+ if (isl_space_is_map(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "expecting (parameter) set space", goto error);
+ if (isl_local_space_check_range(ls, type, pos, 1) < 0)
+ goto error;
+
+ isl_space_free(space);
+ aff = isl_aff_alloc(ls);
+ if (!aff)
+ return NULL;
+
+ pos += isl_local_space_offset(aff->ls, type);
+
+ isl_int_set_si(aff->v->el[0], 1);
+ isl_seq_clr(aff->v->el + 1, aff->v->size - 1);
+ isl_int_set_si(aff->v->el[1 + pos], 1);
+
+ return aff;
+error:
+ isl_local_space_free(ls);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Return a piecewise affine expression that is equal to
+ * the specified dimension in "ls".
+ */
+__isl_give isl_pw_aff *isl_pw_aff_var_on_domain(__isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos)
+{
+ return isl_pw_aff_from_aff(isl_aff_var_on_domain(ls, type, pos));
+}
+
+/* Return an affine expression that is equal to the parameter
+ * in the domain space "space" with identifier "id".
+ */
+__isl_give isl_aff *isl_aff_param_on_domain_space_id(
+ __isl_take isl_space *space, __isl_take isl_id *id)
+{
+ int pos;
+ isl_local_space *ls;
+
+ if (!space || !id)
+ goto error;
+ pos = isl_space_find_dim_by_id(space, isl_dim_param, id);
+ if (pos < 0)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "parameter not found in space", goto error);
+ isl_id_free(id);
+ ls = isl_local_space_from_space(space);
+ return isl_aff_var_on_domain(ls, isl_dim_param, pos);
+error:
+ isl_space_free(space);
+ isl_id_free(id);
+ return NULL;
+}
+
+/* This function performs the same operation as
+ * isl_aff_param_on_domain_space_id,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give isl_aff *isl_space_param_aff_on_domain_id(
+ __isl_take isl_space *space, __isl_take isl_id *id)
+{
+ return isl_aff_param_on_domain_space_id(space, id);
+}
+
+__isl_null isl_aff *isl_aff_free(__isl_take isl_aff *aff)
+{
+ if (!aff)
+ return NULL;
+
+ if (--aff->ref > 0)
+ return NULL;
+
+ isl_local_space_free(aff->ls);
+ isl_vec_free(aff->v);
+
+ free(aff);
+
+ return NULL;
+}
+
+isl_ctx *isl_aff_get_ctx(__isl_keep isl_aff *aff)
+{
+ return aff ? isl_local_space_get_ctx(aff->ls) : NULL;
+}
+
+/* Return a hash value that digests "aff".
+ */
+uint32_t isl_aff_get_hash(__isl_keep isl_aff *aff)
+{
+ uint32_t hash, ls_hash, v_hash;
+
+ if (!aff)
+ return 0;
+
+ hash = isl_hash_init();
+ ls_hash = isl_local_space_get_hash(aff->ls);
+ isl_hash_hash(hash, ls_hash);
+ v_hash = isl_vec_get_hash(aff->v);
+ isl_hash_hash(hash, v_hash);
+
+ return hash;
+}
+
+/* Return the domain local space of "aff".
+ */
+static __isl_keep isl_local_space *isl_aff_peek_domain_local_space(
+ __isl_keep isl_aff *aff)
+{
+ return aff ? aff->ls : NULL;
+}
+
+/* Return the number of variables of the given type in the domain of "aff".
+ */
+isl_size isl_aff_domain_dim(__isl_keep isl_aff *aff, enum isl_dim_type type)
+{
+ isl_local_space *ls;
+
+ ls = isl_aff_peek_domain_local_space(aff);
+ return isl_local_space_dim(ls, type);
+}
+
+/* Externally, an isl_aff has a map space, but internally, the
+ * ls field corresponds to the domain of that space.
+ */
+isl_size isl_aff_dim(__isl_keep isl_aff *aff, enum isl_dim_type type)
+{
+ if (!aff)
+ return isl_size_error;
+ if (type == isl_dim_out)
+ return 1;
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ return isl_aff_domain_dim(aff, type);
+}
+
+/* Return the offset of the first coefficient of type "type" in
+ * the domain of "aff".
+ */
+isl_size isl_aff_domain_offset(__isl_keep isl_aff *aff, enum isl_dim_type type)
+{
+ isl_local_space *ls;
+
+ ls = isl_aff_peek_domain_local_space(aff);
+ return isl_local_space_offset(ls, type);
+}
+
+/* Return the position of the dimension of the given type and name
+ * in "aff".
+ * Return -1 if no such dimension can be found.
+ */
+int isl_aff_find_dim_by_name(__isl_keep isl_aff *aff, enum isl_dim_type type,
+ const char *name)
+{
+ if (!aff)
+ return -1;
+ if (type == isl_dim_out)
+ return -1;
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ return isl_local_space_find_dim_by_name(aff->ls, type, name);
+}
+
+/* Return the domain space of "aff".
+ */
+static __isl_keep isl_space *isl_aff_peek_domain_space(__isl_keep isl_aff *aff)
+{
+ return aff ? isl_local_space_peek_space(aff->ls) : NULL;
+}
+
+__isl_give isl_space *isl_aff_get_domain_space(__isl_keep isl_aff *aff)
+{
+ return isl_space_copy(isl_aff_peek_domain_space(aff));
+}
+
+__isl_give isl_space *isl_aff_get_space(__isl_keep isl_aff *aff)
+{
+ isl_space *space;
+ if (!aff)
+ return NULL;
+ space = isl_local_space_get_space(aff->ls);
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, 1);
+ return space;
+}
+
+/* Return a copy of the domain space of "aff".
+ */
+__isl_give isl_local_space *isl_aff_get_domain_local_space(
+ __isl_keep isl_aff *aff)
+{
+ return isl_local_space_copy(isl_aff_peek_domain_local_space(aff));
+}
+
+__isl_give isl_local_space *isl_aff_get_local_space(__isl_keep isl_aff *aff)
+{
+ isl_local_space *ls;
+ if (!aff)
+ return NULL;
+ ls = isl_local_space_copy(aff->ls);
+ ls = isl_local_space_from_domain(ls);
+ ls = isl_local_space_add_dims(ls, isl_dim_out, 1);
+ return ls;
+}
+
+/* Return the local space of the domain of "aff".
+ * This may be either a copy or the local space itself
+ * if there is only one reference to "aff".
+ * This allows the local space to be modified inplace
+ * if both the expression and its local space have only a single reference.
+ * The caller is not allowed to modify "aff" between this call and
+ * a subsequent call to isl_aff_restore_domain_local_space.
+ * The only exception is that isl_aff_free can be called instead.
+ */
+__isl_give isl_local_space *isl_aff_take_domain_local_space(
+ __isl_keep isl_aff *aff)
+{
+ isl_local_space *ls;
+
+ if (!aff)
+ return NULL;
+ if (aff->ref != 1)
+ return isl_aff_get_domain_local_space(aff);
+ ls = aff->ls;
+ aff->ls = NULL;
+ return ls;
+}
+
+/* Set the local space of the domain of "aff" to "ls",
+ * where the local space of "aff" may be missing
+ * due to a preceding call to isl_aff_take_domain_local_space.
+ * However, in this case, "aff" only has a single reference and
+ * then the call to isl_aff_cow has no effect.
+ */
+__isl_give isl_aff *isl_aff_restore_domain_local_space(
+ __isl_keep isl_aff *aff, __isl_take isl_local_space *ls)
+{
+ if (!aff || !ls)
+ goto error;
+
+ if (aff->ls == ls) {
+ isl_local_space_free(ls);
+ return aff;
+ }
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ goto error;
+ isl_local_space_free(aff->ls);
+ aff->ls = ls;
+
+ return aff;
+error:
+ isl_aff_free(aff);
+ isl_local_space_free(ls);
+ return NULL;
+}
+
+/* Externally, an isl_aff has a map space, but internally, the
+ * ls field corresponds to the domain of that space.
+ */
+const char *isl_aff_get_dim_name(__isl_keep isl_aff *aff,
+ enum isl_dim_type type, unsigned pos)
+{
+ if (!aff)
+ return NULL;
+ if (type == isl_dim_out)
+ return NULL;
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ return isl_local_space_get_dim_name(aff->ls, type, pos);
+}
+
+__isl_give isl_aff *isl_aff_reset_domain_space(__isl_take isl_aff *aff,
+ __isl_take isl_space *space)
+{
+ aff = isl_aff_cow(aff);
+ if (!aff || !space)
+ goto error;
+
+ aff->ls = isl_local_space_reset_space(aff->ls, space);
+ if (!aff->ls)
+ return isl_aff_free(aff);
+
+ return aff;
+error:
+ isl_aff_free(aff);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Reset the space of "aff". This function is called from isl_pw_templ.c
+ * and doesn't know if the space of an element object is represented
+ * directly or through its domain. It therefore passes along both.
+ */
+__isl_give isl_aff *isl_aff_reset_space_and_domain(__isl_take isl_aff *aff,
+ __isl_take isl_space *space, __isl_take isl_space *domain)
+{
+ isl_space_free(space);
+ return isl_aff_reset_domain_space(aff, domain);
+}
+
+/* Reorder the coefficients of the affine expression based
+ * on the given reordering.
+ * The reordering r is assumed to have been extended with the local
+ * variables.
+ */
+static __isl_give isl_vec *vec_reorder(__isl_take isl_vec *vec,
+ __isl_take isl_reordering *r, int n_div)
+{
+ isl_space *space;
+ isl_vec *res;
+ isl_size dim;
+ int i;
+
+ if (!vec || !r)
+ goto error;
+
+ space = isl_reordering_peek_space(r);
+ dim = isl_space_dim(space, isl_dim_all);
+ if (dim < 0)
+ goto error;
+ res = isl_vec_alloc(vec->ctx, 2 + dim + n_div);
+ if (!res)
+ goto error;
+ isl_seq_cpy(res->el, vec->el, 2);
+ isl_seq_clr(res->el + 2, res->size - 2);
+ for (i = 0; i < r->len; ++i)
+ isl_int_set(res->el[2 + r->pos[i]], vec->el[2 + i]);
+
+ isl_reordering_free(r);
+ isl_vec_free(vec);
+ return res;
+error:
+ isl_vec_free(vec);
+ isl_reordering_free(r);
+ return NULL;
+}
+
+/* Reorder the dimensions of the domain of "aff" according
+ * to the given reordering.
+ */
+__isl_give isl_aff *isl_aff_realign_domain(__isl_take isl_aff *aff,
+ __isl_take isl_reordering *r)
+{
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ goto error;
+
+ r = isl_reordering_extend(r, aff->ls->div->n_row);
+ aff->v = vec_reorder(aff->v, isl_reordering_copy(r),
+ aff->ls->div->n_row);
+ aff->ls = isl_local_space_realign(aff->ls, r);
+
+ if (!aff->v || !aff->ls)
+ return isl_aff_free(aff);
+
+ return aff;
+error:
+ isl_aff_free(aff);
+ isl_reordering_free(r);
+ return NULL;
+}
+
+__isl_give isl_aff *isl_aff_align_params(__isl_take isl_aff *aff,
+ __isl_take isl_space *model)
+{
+ isl_bool equal_params;
+
+ if (!aff || !model)
+ goto error;
+
+ equal_params = isl_space_has_equal_params(aff->ls->dim, model);
+ if (equal_params < 0)
+ goto error;
+ if (!equal_params) {
+ isl_reordering *exp;
+
+ exp = isl_parameter_alignment_reordering(aff->ls->dim, model);
+ exp = isl_reordering_extend_space(exp,
+ isl_aff_get_domain_space(aff));
+ aff = isl_aff_realign_domain(aff, exp);
+ }
+
+ isl_space_free(model);
+ return aff;
+error:
+ isl_space_free(model);
+ isl_aff_free(aff);
+ return NULL;
+}
+
+#undef TYPE
+#define TYPE isl_aff
+#include "isl_unbind_params_templ.c"
+
+/* Is "aff" obviously equal to zero?
+ *
+ * If the denominator is zero, then "aff" is not equal to zero.
+ */
+isl_bool isl_aff_plain_is_zero(__isl_keep isl_aff *aff)
+{
+ int pos;
+
+ if (!aff)
+ return isl_bool_error;
+
+ if (isl_int_is_zero(aff->v->el[0]))
+ return isl_bool_false;
+ pos = isl_seq_first_non_zero(aff->v->el + 1, aff->v->size - 1);
+ return isl_bool_ok(pos < 0);
+}
+
+/* Does "aff" represent NaN?
+ */
+isl_bool isl_aff_is_nan(__isl_keep isl_aff *aff)
+{
+ if (!aff)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_seq_first_non_zero(aff->v->el, 2) < 0);
+}
+
+/* Are "aff1" and "aff2" obviously equal?
+ *
+ * NaN is not equal to anything, not even to another NaN.
+ */
+isl_bool isl_aff_plain_is_equal(__isl_keep isl_aff *aff1,
+ __isl_keep isl_aff *aff2)
+{
+ isl_bool equal;
+
+ if (!aff1 || !aff2)
+ return isl_bool_error;
+
+ if (isl_aff_is_nan(aff1) || isl_aff_is_nan(aff2))
+ return isl_bool_false;
+
+ equal = isl_local_space_is_equal(aff1->ls, aff2->ls);
+ if (equal < 0 || !equal)
+ return equal;
+
+ return isl_vec_is_equal(aff1->v, aff2->v);
+}
+
+/* Return the common denominator of "aff" in "v".
+ *
+ * We cannot return anything meaningful in case of a NaN.
+ */
+isl_stat isl_aff_get_denominator(__isl_keep isl_aff *aff, isl_int *v)
+{
+ if (!aff)
+ return isl_stat_error;
+ if (isl_aff_is_nan(aff))
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "cannot get denominator of NaN", return isl_stat_error);
+ isl_int_set(*v, aff->v->el[0]);
+ return isl_stat_ok;
+}
+
+/* Return the common denominator of "aff".
+ */
+__isl_give isl_val *isl_aff_get_denominator_val(__isl_keep isl_aff *aff)
+{
+ isl_ctx *ctx;
+
+ if (!aff)
+ return NULL;
+
+ ctx = isl_aff_get_ctx(aff);
+ if (isl_aff_is_nan(aff))
+ return isl_val_nan(ctx);
+ return isl_val_int_from_isl_int(ctx, aff->v->el[0]);
+}
+
+/* Return the constant term of "aff".
+ */
+__isl_give isl_val *isl_aff_get_constant_val(__isl_keep isl_aff *aff)
+{
+ isl_ctx *ctx;
+ isl_val *v;
+
+ if (!aff)
+ return NULL;
+
+ ctx = isl_aff_get_ctx(aff);
+ if (isl_aff_is_nan(aff))
+ return isl_val_nan(ctx);
+ v = isl_val_rat_from_isl_int(ctx, aff->v->el[1], aff->v->el[0]);
+ return isl_val_normalize(v);
+}
+
+/* Return the coefficient of the variable of type "type" at position "pos"
+ * of "aff".
+ */
+__isl_give isl_val *isl_aff_get_coefficient_val(__isl_keep isl_aff *aff,
+ enum isl_dim_type type, int pos)
+{
+ isl_ctx *ctx;
+ isl_val *v;
+
+ if (!aff)
+ return NULL;
+
+ ctx = isl_aff_get_ctx(aff);
+ if (type == isl_dim_out)
+ isl_die(ctx, isl_error_invalid,
+ "output/set dimension does not have a coefficient",
+ return NULL);
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+
+ if (isl_local_space_check_range(aff->ls, type, pos, 1) < 0)
+ return NULL;
+
+ if (isl_aff_is_nan(aff))
+ return isl_val_nan(ctx);
+ pos += isl_local_space_offset(aff->ls, type);
+ v = isl_val_rat_from_isl_int(ctx, aff->v->el[1 + pos], aff->v->el[0]);
+ return isl_val_normalize(v);
+}
+
+/* Return the sign of the coefficient of the variable of type "type"
+ * at position "pos" of "aff".
+ */
+int isl_aff_coefficient_sgn(__isl_keep isl_aff *aff, enum isl_dim_type type,
+ int pos)
+{
+ isl_ctx *ctx;
+
+ if (!aff)
+ return 0;
+
+ ctx = isl_aff_get_ctx(aff);
+ if (type == isl_dim_out)
+ isl_die(ctx, isl_error_invalid,
+ "output/set dimension does not have a coefficient",
+ return 0);
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+
+ if (isl_local_space_check_range(aff->ls, type, pos, 1) < 0)
+ return 0;
+
+ pos += isl_local_space_offset(aff->ls, type);
+ return isl_int_sgn(aff->v->el[1 + pos]);
+}
+
+/* Replace the numerator of the constant term of "aff" by "v".
+ *
+ * A NaN is unaffected by this operation.
+ */
+__isl_give isl_aff *isl_aff_set_constant(__isl_take isl_aff *aff, isl_int v)
+{
+ if (!aff)
+ return NULL;
+ if (isl_aff_is_nan(aff))
+ return aff;
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ isl_int_set(aff->v->el[1], v);
+
+ return aff;
+}
+
+/* Replace the constant term of "aff" by "v".
+ *
+ * A NaN is unaffected by this operation.
+ */
+__isl_give isl_aff *isl_aff_set_constant_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *v)
+{
+ if (!aff || !v)
+ goto error;
+
+ if (isl_aff_is_nan(aff)) {
+ isl_val_free(v);
+ return aff;
+ }
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "expecting rational value", goto error);
+
+ if (isl_int_eq(aff->v->el[1], v->n) &&
+ isl_int_eq(aff->v->el[0], v->d)) {
+ isl_val_free(v);
+ return aff;
+ }
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ goto error;
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ goto error;
+
+ if (isl_int_eq(aff->v->el[0], v->d)) {
+ isl_int_set(aff->v->el[1], v->n);
+ } else if (isl_int_is_one(v->d)) {
+ isl_int_mul(aff->v->el[1], aff->v->el[0], v->n);
+ } else {
+ isl_seq_scale(aff->v->el + 1,
+ aff->v->el + 1, v->d, aff->v->size - 1);
+ isl_int_mul(aff->v->el[1], aff->v->el[0], v->n);
+ isl_int_mul(aff->v->el[0], aff->v->el[0], v->d);
+ aff->v = isl_vec_normalize(aff->v);
+ if (!aff->v)
+ goto error;
+ }
+
+ isl_val_free(v);
+ return aff;
+error:
+ isl_aff_free(aff);
+ isl_val_free(v);
+ return NULL;
+}
+
+/* Add "v" to the constant term of "aff".
+ *
+ * A NaN is unaffected by this operation.
+ */
+__isl_give isl_aff *isl_aff_add_constant(__isl_take isl_aff *aff, isl_int v)
+{
+ if (isl_int_is_zero(v))
+ return aff;
+
+ if (!aff)
+ return NULL;
+ if (isl_aff_is_nan(aff))
+ return aff;
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ isl_int_addmul(aff->v->el[1], aff->v->el[0], v);
+
+ return aff;
+}
+
+/* Add "v" to the constant term of "aff",
+ * in case "aff" is a rational expression.
+ */
+static __isl_give isl_aff *isl_aff_add_rat_constant_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *v)
+{
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ goto error;
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ goto error;
+
+ if (isl_int_is_one(v->d)) {
+ isl_int_addmul(aff->v->el[1], aff->v->el[0], v->n);
+ } else if (isl_int_eq(aff->v->el[0], v->d)) {
+ isl_int_add(aff->v->el[1], aff->v->el[1], v->n);
+ aff->v = isl_vec_normalize(aff->v);
+ if (!aff->v)
+ goto error;
+ } else {
+ isl_seq_scale(aff->v->el + 1,
+ aff->v->el + 1, v->d, aff->v->size - 1);
+ isl_int_addmul(aff->v->el[1], aff->v->el[0], v->n);
+ isl_int_mul(aff->v->el[0], aff->v->el[0], v->d);
+ aff->v = isl_vec_normalize(aff->v);
+ if (!aff->v)
+ goto error;
+ }
+
+ isl_val_free(v);
+ return aff;
+error:
+ isl_aff_free(aff);
+ isl_val_free(v);
+ return NULL;
+}
+
+/* Return the first argument and free the second.
+ */
+static __isl_give isl_aff *pick_free(__isl_take isl_aff *aff,
+ __isl_take isl_val *v)
+{
+ isl_val_free(v);
+ return aff;
+}
+
+/* Replace the first argument by NaN and free the second argument.
+ */
+static __isl_give isl_aff *set_nan_free_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *v)
+{
+ isl_val_free(v);
+ return isl_aff_set_nan(aff);
+}
+
+/* Add "v" to the constant term of "aff".
+ *
+ * A NaN is unaffected by this operation.
+ * Conversely, adding a NaN turns "aff" into a NaN.
+ */
+__isl_give isl_aff *isl_aff_add_constant_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *v)
+{
+ isl_bool is_nan, is_zero, is_rat;
+
+ is_nan = isl_aff_is_nan(aff);
+ is_zero = isl_val_is_zero(v);
+ if (is_nan < 0 || is_zero < 0)
+ goto error;
+ if (is_nan || is_zero)
+ return pick_free(aff, v);
+
+ is_nan = isl_val_is_nan(v);
+ is_rat = isl_val_is_rat(v);
+ if (is_nan < 0 || is_rat < 0)
+ goto error;
+ if (is_nan)
+ return set_nan_free_val(aff, v);
+ if (!is_rat)
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "expecting rational value or NaN", goto error);
+
+ return isl_aff_add_rat_constant_val(aff, v);
+error:
+ isl_aff_free(aff);
+ isl_val_free(v);
+ return NULL;
+}
+
+__isl_give isl_aff *isl_aff_add_constant_si(__isl_take isl_aff *aff, int v)
+{
+ isl_int t;
+
+ isl_int_init(t);
+ isl_int_set_si(t, v);
+ aff = isl_aff_add_constant(aff, t);
+ isl_int_clear(t);
+
+ return aff;
+}
+
+/* Add "v" to the numerator of the constant term of "aff".
+ *
+ * A NaN is unaffected by this operation.
+ */
+__isl_give isl_aff *isl_aff_add_constant_num(__isl_take isl_aff *aff, isl_int v)
+{
+ if (isl_int_is_zero(v))
+ return aff;
+
+ if (!aff)
+ return NULL;
+ if (isl_aff_is_nan(aff))
+ return aff;
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ isl_int_add(aff->v->el[1], aff->v->el[1], v);
+
+ return aff;
+}
+
+/* Add "v" to the numerator of the constant term of "aff".
+ *
+ * A NaN is unaffected by this operation.
+ */
+__isl_give isl_aff *isl_aff_add_constant_num_si(__isl_take isl_aff *aff, int v)
+{
+ isl_int t;
+
+ if (v == 0)
+ return aff;
+
+ isl_int_init(t);
+ isl_int_set_si(t, v);
+ aff = isl_aff_add_constant_num(aff, t);
+ isl_int_clear(t);
+
+ return aff;
+}
+
+/* Replace the numerator of the constant term of "aff" by "v".
+ *
+ * A NaN is unaffected by this operation.
+ */
+__isl_give isl_aff *isl_aff_set_constant_si(__isl_take isl_aff *aff, int v)
+{
+ if (!aff)
+ return NULL;
+ if (isl_aff_is_nan(aff))
+ return aff;
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ isl_int_set_si(aff->v->el[1], v);
+
+ return aff;
+}
+
+/* Replace the numerator of the coefficient of the variable of type "type"
+ * at position "pos" of "aff" by "v".
+ *
+ * A NaN is unaffected by this operation.
+ */
+__isl_give isl_aff *isl_aff_set_coefficient(__isl_take isl_aff *aff,
+ enum isl_dim_type type, int pos, isl_int v)
+{
+ if (!aff)
+ return NULL;
+
+ if (type == isl_dim_out)
+ isl_die(aff->v->ctx, isl_error_invalid,
+ "output/set dimension does not have a coefficient",
+ return isl_aff_free(aff));
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+
+ if (isl_local_space_check_range(aff->ls, type, pos, 1) < 0)
+ return isl_aff_free(aff);
+
+ if (isl_aff_is_nan(aff))
+ return aff;
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ pos += isl_local_space_offset(aff->ls, type);
+ isl_int_set(aff->v->el[1 + pos], v);
+
+ return aff;
+}
+
+/* Replace the numerator of the coefficient of the variable of type "type"
+ * at position "pos" of "aff" by "v".
+ *
+ * A NaN is unaffected by this operation.
+ */
+__isl_give isl_aff *isl_aff_set_coefficient_si(__isl_take isl_aff *aff,
+ enum isl_dim_type type, int pos, int v)
+{
+ if (!aff)
+ return NULL;
+
+ if (type == isl_dim_out)
+ isl_die(aff->v->ctx, isl_error_invalid,
+ "output/set dimension does not have a coefficient",
+ return isl_aff_free(aff));
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+
+ if (isl_local_space_check_range(aff->ls, type, pos, 1) < 0)
+ return isl_aff_free(aff);
+
+ if (isl_aff_is_nan(aff))
+ return aff;
+ pos += isl_local_space_offset(aff->ls, type);
+ if (isl_int_cmp_si(aff->v->el[1 + pos], v) == 0)
+ return aff;
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ isl_int_set_si(aff->v->el[1 + pos], v);
+
+ return aff;
+}
+
+/* Replace the coefficient of the variable of type "type" at position "pos"
+ * of "aff" by "v".
+ *
+ * A NaN is unaffected by this operation.
+ */
+__isl_give isl_aff *isl_aff_set_coefficient_val(__isl_take isl_aff *aff,
+ enum isl_dim_type type, int pos, __isl_take isl_val *v)
+{
+ if (!aff || !v)
+ goto error;
+
+ if (type == isl_dim_out)
+ isl_die(aff->v->ctx, isl_error_invalid,
+ "output/set dimension does not have a coefficient",
+ goto error);
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+
+ if (isl_local_space_check_range(aff->ls, type, pos, 1) < 0)
+ return isl_aff_free(aff);
+
+ if (isl_aff_is_nan(aff)) {
+ isl_val_free(v);
+ return aff;
+ }
+ if (!isl_val_is_rat(v))
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "expecting rational value", goto error);
+
+ pos += isl_local_space_offset(aff->ls, type);
+ if (isl_int_eq(aff->v->el[1 + pos], v->n) &&
+ isl_int_eq(aff->v->el[0], v->d)) {
+ isl_val_free(v);
+ return aff;
+ }
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ goto error;
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ goto error;
+
+ if (isl_int_eq(aff->v->el[0], v->d)) {
+ isl_int_set(aff->v->el[1 + pos], v->n);
+ } else if (isl_int_is_one(v->d)) {
+ isl_int_mul(aff->v->el[1 + pos], aff->v->el[0], v->n);
+ } else {
+ isl_seq_scale(aff->v->el + 1,
+ aff->v->el + 1, v->d, aff->v->size - 1);
+ isl_int_mul(aff->v->el[1 + pos], aff->v->el[0], v->n);
+ isl_int_mul(aff->v->el[0], aff->v->el[0], v->d);
+ aff->v = isl_vec_normalize(aff->v);
+ if (!aff->v)
+ goto error;
+ }
+
+ isl_val_free(v);
+ return aff;
+error:
+ isl_aff_free(aff);
+ isl_val_free(v);
+ return NULL;
+}
+
+/* Add "v" to the coefficient of the variable of type "type"
+ * at position "pos" of "aff".
+ *
+ * A NaN is unaffected by this operation.
+ */
+__isl_give isl_aff *isl_aff_add_coefficient(__isl_take isl_aff *aff,
+ enum isl_dim_type type, int pos, isl_int v)
+{
+ if (!aff)
+ return NULL;
+
+ if (type == isl_dim_out)
+ isl_die(aff->v->ctx, isl_error_invalid,
+ "output/set dimension does not have a coefficient",
+ return isl_aff_free(aff));
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+
+ if (isl_local_space_check_range(aff->ls, type, pos, 1) < 0)
+ return isl_aff_free(aff);
+
+ if (isl_aff_is_nan(aff))
+ return aff;
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ pos += isl_local_space_offset(aff->ls, type);
+ isl_int_addmul(aff->v->el[1 + pos], aff->v->el[0], v);
+
+ return aff;
+}
+
+/* Add "v" to the coefficient of the variable of type "type"
+ * at position "pos" of "aff".
+ *
+ * A NaN is unaffected by this operation.
+ */
+__isl_give isl_aff *isl_aff_add_coefficient_val(__isl_take isl_aff *aff,
+ enum isl_dim_type type, int pos, __isl_take isl_val *v)
+{
+ if (!aff || !v)
+ goto error;
+
+ if (isl_val_is_zero(v)) {
+ isl_val_free(v);
+ return aff;
+ }
+
+ if (type == isl_dim_out)
+ isl_die(aff->v->ctx, isl_error_invalid,
+ "output/set dimension does not have a coefficient",
+ goto error);
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+
+ if (isl_local_space_check_range(aff->ls, type, pos, 1) < 0)
+ goto error;
+
+ if (isl_aff_is_nan(aff)) {
+ isl_val_free(v);
+ return aff;
+ }
+ if (!isl_val_is_rat(v))
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "expecting rational value", goto error);
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ goto error;
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ goto error;
+
+ pos += isl_local_space_offset(aff->ls, type);
+ if (isl_int_is_one(v->d)) {
+ isl_int_addmul(aff->v->el[1 + pos], aff->v->el[0], v->n);
+ } else if (isl_int_eq(aff->v->el[0], v->d)) {
+ isl_int_add(aff->v->el[1 + pos], aff->v->el[1 + pos], v->n);
+ aff->v = isl_vec_normalize(aff->v);
+ if (!aff->v)
+ goto error;
+ } else {
+ isl_seq_scale(aff->v->el + 1,
+ aff->v->el + 1, v->d, aff->v->size - 1);
+ isl_int_addmul(aff->v->el[1 + pos], aff->v->el[0], v->n);
+ isl_int_mul(aff->v->el[0], aff->v->el[0], v->d);
+ aff->v = isl_vec_normalize(aff->v);
+ if (!aff->v)
+ goto error;
+ }
+
+ isl_val_free(v);
+ return aff;
+error:
+ isl_aff_free(aff);
+ isl_val_free(v);
+ return NULL;
+}
+
+__isl_give isl_aff *isl_aff_add_coefficient_si(__isl_take isl_aff *aff,
+ enum isl_dim_type type, int pos, int v)
+{
+ isl_int t;
+
+ isl_int_init(t);
+ isl_int_set_si(t, v);
+ aff = isl_aff_add_coefficient(aff, type, pos, t);
+ isl_int_clear(t);
+
+ return aff;
+}
+
+__isl_give isl_aff *isl_aff_get_div(__isl_keep isl_aff *aff, int pos)
+{
+ if (!aff)
+ return NULL;
+
+ return isl_local_space_get_div(aff->ls, pos);
+}
+
+/* Return the negation of "aff".
+ *
+ * As a special case, -NaN = NaN.
+ */
+__isl_give isl_aff *isl_aff_neg(__isl_take isl_aff *aff)
+{
+ if (!aff)
+ return NULL;
+ if (isl_aff_is_nan(aff))
+ return aff;
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ isl_seq_neg(aff->v->el + 1, aff->v->el + 1, aff->v->size - 1);
+
+ return aff;
+}
+
+/* Remove divs from the local space that do not appear in the affine
+ * expression.
+ * We currently only remove divs at the end.
+ * Some intermediate divs may also not appear directly in the affine
+ * expression, but we would also need to check that no other divs are
+ * defined in terms of them.
+ */
+__isl_give isl_aff *isl_aff_remove_unused_divs(__isl_take isl_aff *aff)
+{
+ int pos;
+ isl_size off;
+ isl_size n;
+
+ n = isl_aff_domain_dim(aff, isl_dim_div);
+ off = isl_aff_domain_offset(aff, isl_dim_div);
+ if (n < 0 || off < 0)
+ return isl_aff_free(aff);
+
+ pos = isl_seq_last_non_zero(aff->v->el + 1 + off, n) + 1;
+ if (pos == n)
+ return aff;
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->ls = isl_local_space_drop_dims(aff->ls, isl_dim_div, pos, n - pos);
+ aff->v = isl_vec_drop_els(aff->v, 1 + off + pos, n - pos);
+ if (!aff->ls || !aff->v)
+ return isl_aff_free(aff);
+
+ return aff;
+}
+
+/* Look for any divs in the aff->ls with a denominator equal to one
+ * and plug them into the affine expression and any subsequent divs
+ * that may reference the div.
+ */
+static __isl_give isl_aff *plug_in_integral_divs(__isl_take isl_aff *aff)
+{
+ int i;
+ isl_size n;
+ int len;
+ isl_int v;
+ isl_vec *vec;
+ isl_local_space *ls;
+ isl_size off;
+
+ n = isl_aff_domain_dim(aff, isl_dim_div);
+ off = isl_aff_domain_offset(aff, isl_dim_div);
+ if (n < 0 || off < 0)
+ return isl_aff_free(aff);
+ len = aff->v->size;
+ for (i = 0; i < n; ++i) {
+ if (!isl_int_is_one(aff->ls->div->row[i][0]))
+ continue;
+ ls = isl_local_space_copy(aff->ls);
+ ls = isl_local_space_substitute_seq(ls, isl_dim_div, i,
+ aff->ls->div->row[i], len, i + 1, n - (i + 1));
+ vec = isl_vec_copy(aff->v);
+ vec = isl_vec_cow(vec);
+ if (!ls || !vec)
+ goto error;
+
+ isl_int_init(v);
+
+ isl_seq_substitute(vec->el, off + i, aff->ls->div->row[i],
+ len, len, v);
+
+ isl_int_clear(v);
+
+ isl_vec_free(aff->v);
+ aff->v = vec;
+ isl_local_space_free(aff->ls);
+ aff->ls = ls;
+ }
+
+ return aff;
+error:
+ isl_vec_free(vec);
+ isl_local_space_free(ls);
+ return isl_aff_free(aff);
+}
+
+/* Look for any divs j that appear with a unit coefficient inside
+ * the definitions of other divs i and plug them into the definitions
+ * of the divs i.
+ *
+ * In particular, an expression of the form
+ *
+ * floor((f(..) + floor(g(..)/n))/m)
+ *
+ * is simplified to
+ *
+ * floor((n * f(..) + g(..))/(n * m))
+ *
+ * This simplification is correct because we can move the expression
+ * f(..) into the inner floor in the original expression to obtain
+ *
+ * floor(floor((n * f(..) + g(..))/n)/m)
+ *
+ * from which we can derive the simplified expression.
+ */
+static __isl_give isl_aff *plug_in_unit_divs(__isl_take isl_aff *aff)
+{
+ int i, j;
+ isl_size n;
+ isl_size off;
+
+ n = isl_aff_domain_dim(aff, isl_dim_div);
+ off = isl_aff_domain_offset(aff, isl_dim_div);
+ if (n < 0 || off < 0)
+ return isl_aff_free(aff);
+ for (i = 1; i < n; ++i) {
+ for (j = 0; j < i; ++j) {
+ if (!isl_int_is_one(aff->ls->div->row[i][1 + off + j]))
+ continue;
+ aff->ls = isl_local_space_substitute_seq(aff->ls,
+ isl_dim_div, j, aff->ls->div->row[j],
+ aff->v->size, i, 1);
+ if (!aff->ls)
+ return isl_aff_free(aff);
+ }
+ }
+
+ return aff;
+}
+
+/* Swap divs "a" and "b" in "aff", which is assumed to be non-NULL.
+ *
+ * Even though this function is only called on isl_affs with a single
+ * reference, we are careful to only change aff->v and aff->ls together.
+ */
+static __isl_give isl_aff *swap_div(__isl_take isl_aff *aff, int a, int b)
+{
+ isl_size off = isl_aff_domain_offset(aff, isl_dim_div);
+ isl_local_space *ls;
+ isl_vec *v;
+
+ if (off < 0)
+ return isl_aff_free(aff);
+
+ ls = isl_local_space_copy(aff->ls);
+ ls = isl_local_space_swap_div(ls, a, b);
+ v = isl_vec_copy(aff->v);
+ v = isl_vec_cow(v);
+ if (!ls || !v)
+ goto error;
+
+ isl_int_swap(v->el[1 + off + a], v->el[1 + off + b]);
+ isl_vec_free(aff->v);
+ aff->v = v;
+ isl_local_space_free(aff->ls);
+ aff->ls = ls;
+
+ return aff;
+error:
+ isl_vec_free(v);
+ isl_local_space_free(ls);
+ return isl_aff_free(aff);
+}
+
+/* Merge divs "a" and "b" in "aff", which is assumed to be non-NULL.
+ *
+ * We currently do not actually remove div "b", but simply add its
+ * coefficient to that of "a" and then zero it out.
+ */
+static __isl_give isl_aff *merge_divs(__isl_take isl_aff *aff, int a, int b)
+{
+ isl_size off = isl_aff_domain_offset(aff, isl_dim_div);
+
+ if (off < 0)
+ return isl_aff_free(aff);
+
+ if (isl_int_is_zero(aff->v->el[1 + off + b]))
+ return aff;
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ isl_int_add(aff->v->el[1 + off + a],
+ aff->v->el[1 + off + a], aff->v->el[1 + off + b]);
+ isl_int_set_si(aff->v->el[1 + off + b], 0);
+
+ return aff;
+}
+
+/* Sort the divs in the local space of "aff" according to
+ * the comparison function "cmp_row" in isl_local_space.c,
+ * combining the coefficients of identical divs.
+ *
+ * Reordering divs does not change the semantics of "aff",
+ * so there is no need to call isl_aff_cow.
+ * Moreover, this function is currently only called on isl_affs
+ * with a single reference.
+ */
+static __isl_give isl_aff *sort_divs(__isl_take isl_aff *aff)
+{
+ isl_size n;
+ int i, j;
+
+ n = isl_aff_dim(aff, isl_dim_div);
+ if (n < 0)
+ return isl_aff_free(aff);
+ for (i = 1; i < n; ++i) {
+ for (j = i - 1; j >= 0; --j) {
+ int cmp = isl_mat_cmp_div(aff->ls->div, j, j + 1);
+ if (cmp < 0)
+ break;
+ if (cmp == 0)
+ aff = merge_divs(aff, j, j + 1);
+ else
+ aff = swap_div(aff, j, j + 1);
+ if (!aff)
+ return NULL;
+ }
+ }
+
+ return aff;
+}
+
+/* Normalize the representation of "aff".
+ *
+ * This function should only be called on "new" isl_affs, i.e.,
+ * with only a single reference. We therefore do not need to
+ * worry about affecting other instances.
+ */
+__isl_give isl_aff *isl_aff_normalize(__isl_take isl_aff *aff)
+{
+ if (!aff)
+ return NULL;
+ aff->v = isl_vec_normalize(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+ aff = plug_in_integral_divs(aff);
+ aff = plug_in_unit_divs(aff);
+ aff = sort_divs(aff);
+ aff = isl_aff_remove_unused_divs(aff);
+ return aff;
+}
+
+/* Given f, return floor(f).
+ * If f is an integer expression, then just return f.
+ * If f is a constant, then return the constant floor(f).
+ * Otherwise, if f = g/m, write g = q m + r,
+ * create a new div d = [r/m] and return the expression q + d.
+ * The coefficients in r are taken to lie between -m/2 and m/2.
+ *
+ * reduce_div_coefficients performs the same normalization.
+ *
+ * As a special case, floor(NaN) = NaN.
+ */
+__isl_give isl_aff *isl_aff_floor(__isl_take isl_aff *aff)
+{
+ int i;
+ int size;
+ isl_ctx *ctx;
+ isl_vec *div;
+
+ if (!aff)
+ return NULL;
+
+ if (isl_aff_is_nan(aff))
+ return aff;
+ if (isl_int_is_one(aff->v->el[0]))
+ return aff;
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ if (isl_aff_is_cst(aff)) {
+ isl_int_fdiv_q(aff->v->el[1], aff->v->el[1], aff->v->el[0]);
+ isl_int_set_si(aff->v->el[0], 1);
+ return aff;
+ }
+
+ div = isl_vec_copy(aff->v);
+ div = isl_vec_cow(div);
+ if (!div)
+ return isl_aff_free(aff);
+
+ ctx = isl_aff_get_ctx(aff);
+ isl_int_fdiv_q(aff->v->el[0], aff->v->el[0], ctx->two);
+ for (i = 1; i < aff->v->size; ++i) {
+ isl_int_fdiv_r(div->el[i], div->el[i], div->el[0]);
+ isl_int_fdiv_q(aff->v->el[i], aff->v->el[i], div->el[0]);
+ if (isl_int_gt(div->el[i], aff->v->el[0])) {
+ isl_int_sub(div->el[i], div->el[i], div->el[0]);
+ isl_int_add_ui(aff->v->el[i], aff->v->el[i], 1);
+ }
+ }
+
+ aff->ls = isl_local_space_add_div(aff->ls, div);
+ if (!aff->ls)
+ return isl_aff_free(aff);
+
+ size = aff->v->size;
+ aff->v = isl_vec_extend(aff->v, size + 1);
+ if (!aff->v)
+ return isl_aff_free(aff);
+ isl_int_set_si(aff->v->el[0], 1);
+ isl_int_set_si(aff->v->el[size], 1);
+
+ aff = isl_aff_normalize(aff);
+
+ return aff;
+}
+
+/* Compute
+ *
+ * aff mod m = aff - m * floor(aff/m)
+ *
+ * with m an integer value.
+ */
+__isl_give isl_aff *isl_aff_mod_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *m)
+{
+ isl_aff *res;
+
+ if (!aff || !m)
+ goto error;
+
+ if (!isl_val_is_int(m))
+ isl_die(isl_val_get_ctx(m), isl_error_invalid,
+ "expecting integer modulo", goto error);
+
+ res = isl_aff_copy(aff);
+ aff = isl_aff_scale_down_val(aff, isl_val_copy(m));
+ aff = isl_aff_floor(aff);
+ aff = isl_aff_scale_val(aff, m);
+ res = isl_aff_sub(res, aff);
+
+ return res;
+error:
+ isl_aff_free(aff);
+ isl_val_free(m);
+ return NULL;
+}
+
+/* Compute
+ *
+ * pwaff mod m = pwaff - m * floor(pwaff/m)
+ */
+__isl_give isl_pw_aff *isl_pw_aff_mod(__isl_take isl_pw_aff *pwaff, isl_int m)
+{
+ isl_pw_aff *res;
+
+ res = isl_pw_aff_copy(pwaff);
+ pwaff = isl_pw_aff_scale_down(pwaff, m);
+ pwaff = isl_pw_aff_floor(pwaff);
+ pwaff = isl_pw_aff_scale(pwaff, m);
+ res = isl_pw_aff_sub(res, pwaff);
+
+ return res;
+}
+
+/* Compute
+ *
+ * pa mod m = pa - m * floor(pa/m)
+ *
+ * with m an integer value.
+ */
+__isl_give isl_pw_aff *isl_pw_aff_mod_val(__isl_take isl_pw_aff *pa,
+ __isl_take isl_val *m)
+{
+ if (!pa || !m)
+ goto error;
+ if (!isl_val_is_int(m))
+ isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid,
+ "expecting integer modulo", goto error);
+ pa = isl_pw_aff_mod(pa, m->n);
+ isl_val_free(m);
+ return pa;
+error:
+ isl_pw_aff_free(pa);
+ isl_val_free(m);
+ return NULL;
+}
+
+/* Given f, return ceil(f).
+ * If f is an integer expression, then just return f.
+ * Otherwise, let f be the expression
+ *
+ * e/m
+ *
+ * then return
+ *
+ * floor((e + m - 1)/m)
+ *
+ * As a special case, ceil(NaN) = NaN.
+ */
+__isl_give isl_aff *isl_aff_ceil(__isl_take isl_aff *aff)
+{
+ if (!aff)
+ return NULL;
+
+ if (isl_aff_is_nan(aff))
+ return aff;
+ if (isl_int_is_one(aff->v->el[0]))
+ return aff;
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ isl_int_add(aff->v->el[1], aff->v->el[1], aff->v->el[0]);
+ isl_int_sub_ui(aff->v->el[1], aff->v->el[1], 1);
+ aff = isl_aff_floor(aff);
+
+ return aff;
+}
+
+/* Apply the expansion computed by isl_merge_divs.
+ * The expansion itself is given by "exp" while the resulting
+ * list of divs is given by "div".
+ */
+__isl_give isl_aff *isl_aff_expand_divs(__isl_take isl_aff *aff,
+ __isl_take isl_mat *div, int *exp)
+{
+ isl_size old_n_div;
+ isl_size new_n_div;
+ isl_size offset;
+
+ aff = isl_aff_cow(aff);
+
+ offset = isl_aff_domain_offset(aff, isl_dim_div);
+ old_n_div = isl_aff_domain_dim(aff, isl_dim_div);
+ new_n_div = isl_mat_rows(div);
+ if (offset < 0 || old_n_div < 0 || new_n_div < 0)
+ goto error;
+
+ aff->v = isl_vec_expand(aff->v, 1 + offset, old_n_div, exp, new_n_div);
+ aff->ls = isl_local_space_replace_divs(aff->ls, div);
+ if (!aff->v || !aff->ls)
+ return isl_aff_free(aff);
+ return aff;
+error:
+ isl_aff_free(aff);
+ isl_mat_free(div);
+ return NULL;
+}
+
+/* Add two affine expressions that live in the same local space.
+ */
+static __isl_give isl_aff *add_expanded(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ isl_int gcd, f;
+
+ aff1 = isl_aff_cow(aff1);
+ if (!aff1 || !aff2)
+ goto error;
+
+ aff1->v = isl_vec_cow(aff1->v);
+ if (!aff1->v)
+ goto error;
+
+ isl_int_init(gcd);
+ isl_int_init(f);
+ isl_int_gcd(gcd, aff1->v->el[0], aff2->v->el[0]);
+ isl_int_divexact(f, aff2->v->el[0], gcd);
+ isl_seq_scale(aff1->v->el + 1, aff1->v->el + 1, f, aff1->v->size - 1);
+ isl_int_divexact(f, aff1->v->el[0], gcd);
+ isl_seq_addmul(aff1->v->el + 1, f, aff2->v->el + 1, aff1->v->size - 1);
+ isl_int_divexact(f, aff2->v->el[0], gcd);
+ isl_int_mul(aff1->v->el[0], aff1->v->el[0], f);
+ isl_int_clear(f);
+ isl_int_clear(gcd);
+
+ isl_aff_free(aff2);
+ aff1 = isl_aff_normalize(aff1);
+ return aff1;
+error:
+ isl_aff_free(aff1);
+ isl_aff_free(aff2);
+ return NULL;
+}
+
+/* Replace one of the arguments by a NaN and free the other one.
+ */
+static __isl_give isl_aff *set_nan_free(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ isl_aff_free(aff2);
+ return isl_aff_set_nan(aff1);
+}
+
+/* Return the sum of "aff1" and "aff2".
+ *
+ * If either of the two is NaN, then the result is NaN.
+ */
+__isl_give isl_aff *isl_aff_add(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ isl_ctx *ctx;
+ int *exp1 = NULL;
+ int *exp2 = NULL;
+ isl_mat *div;
+ isl_size n_div1, n_div2;
+
+ if (!aff1 || !aff2)
+ goto error;
+
+ ctx = isl_aff_get_ctx(aff1);
+ if (!isl_space_is_equal(aff1->ls->dim, aff2->ls->dim))
+ isl_die(ctx, isl_error_invalid,
+ "spaces don't match", goto error);
+
+ if (isl_aff_is_nan(aff1)) {
+ isl_aff_free(aff2);
+ return aff1;
+ }
+ if (isl_aff_is_nan(aff2)) {
+ isl_aff_free(aff1);
+ return aff2;
+ }
+
+ n_div1 = isl_aff_dim(aff1, isl_dim_div);
+ n_div2 = isl_aff_dim(aff2, isl_dim_div);
+ if (n_div1 < 0 || n_div2 < 0)
+ goto error;
+ if (n_div1 == 0 && n_div2 == 0)
+ return add_expanded(aff1, aff2);
+
+ exp1 = isl_alloc_array(ctx, int, n_div1);
+ exp2 = isl_alloc_array(ctx, int, n_div2);
+ if ((n_div1 && !exp1) || (n_div2 && !exp2))
+ goto error;
+
+ div = isl_merge_divs(aff1->ls->div, aff2->ls->div, exp1, exp2);
+ aff1 = isl_aff_expand_divs(aff1, isl_mat_copy(div), exp1);
+ aff2 = isl_aff_expand_divs(aff2, div, exp2);
+ free(exp1);
+ free(exp2);
+
+ return add_expanded(aff1, aff2);
+error:
+ free(exp1);
+ free(exp2);
+ isl_aff_free(aff1);
+ isl_aff_free(aff2);
+ return NULL;
+}
+
+__isl_give isl_aff *isl_aff_sub(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ return isl_aff_add(aff1, isl_aff_neg(aff2));
+}
+
+/* Return the result of scaling "aff" by a factor of "f".
+ *
+ * As a special case, f * NaN = NaN.
+ */
+__isl_give isl_aff *isl_aff_scale(__isl_take isl_aff *aff, isl_int f)
+{
+ isl_int gcd;
+
+ if (!aff)
+ return NULL;
+ if (isl_aff_is_nan(aff))
+ return aff;
+
+ if (isl_int_is_one(f))
+ return aff;
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ if (isl_int_is_pos(f) && isl_int_is_divisible_by(aff->v->el[0], f)) {
+ isl_int_divexact(aff->v->el[0], aff->v->el[0], f);
+ return aff;
+ }
+
+ isl_int_init(gcd);
+ isl_int_gcd(gcd, aff->v->el[0], f);
+ isl_int_divexact(aff->v->el[0], aff->v->el[0], gcd);
+ isl_int_divexact(gcd, f, gcd);
+ isl_seq_scale(aff->v->el + 1, aff->v->el + 1, gcd, aff->v->size - 1);
+ isl_int_clear(gcd);
+
+ return aff;
+}
+
+/* Multiple "aff" by "v".
+ */
+__isl_give isl_aff *isl_aff_scale_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *v)
+{
+ if (!aff || !v)
+ goto error;
+
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return aff;
+ }
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "expecting rational factor", goto error);
+
+ aff = isl_aff_scale(aff, v->n);
+ aff = isl_aff_scale_down(aff, v->d);
+
+ isl_val_free(v);
+ return aff;
+error:
+ isl_aff_free(aff);
+ isl_val_free(v);
+ return NULL;
+}
+
+/* Return the result of scaling "aff" down by a factor of "f".
+ *
+ * As a special case, NaN/f = NaN.
+ */
+__isl_give isl_aff *isl_aff_scale_down(__isl_take isl_aff *aff, isl_int f)
+{
+ isl_int gcd;
+
+ if (!aff)
+ return NULL;
+ if (isl_aff_is_nan(aff))
+ return aff;
+
+ if (isl_int_is_one(f))
+ return aff;
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ if (isl_int_is_zero(f))
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "cannot scale down by zero", return isl_aff_free(aff));
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ isl_int_init(gcd);
+ isl_seq_gcd(aff->v->el + 1, aff->v->size - 1, &gcd);
+ isl_int_gcd(gcd, gcd, f);
+ isl_seq_scale_down(aff->v->el + 1, aff->v->el + 1, gcd, aff->v->size - 1);
+ isl_int_divexact(gcd, f, gcd);
+ isl_int_mul(aff->v->el[0], aff->v->el[0], gcd);
+ isl_int_clear(gcd);
+
+ return aff;
+}
+
+/* Divide "aff" by "v".
+ */
+__isl_give isl_aff *isl_aff_scale_down_val(__isl_take isl_aff *aff,
+ __isl_take isl_val *v)
+{
+ if (!aff || !v)
+ goto error;
+
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return aff;
+ }
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "expecting rational factor", goto error);
+ if (!isl_val_is_pos(v))
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "factor needs to be positive", goto error);
+
+ aff = isl_aff_scale(aff, v->d);
+ aff = isl_aff_scale_down(aff, v->n);
+
+ isl_val_free(v);
+ return aff;
+error:
+ isl_aff_free(aff);
+ isl_val_free(v);
+ return NULL;
+}
+
+__isl_give isl_aff *isl_aff_scale_down_ui(__isl_take isl_aff *aff, unsigned f)
+{
+ isl_int v;
+
+ if (f == 1)
+ return aff;
+
+ isl_int_init(v);
+ isl_int_set_ui(v, f);
+ aff = isl_aff_scale_down(aff, v);
+ isl_int_clear(v);
+
+ return aff;
+}
+
+__isl_give isl_aff *isl_aff_set_dim_name(__isl_take isl_aff *aff,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+ if (type == isl_dim_out)
+ isl_die(aff->v->ctx, isl_error_invalid,
+ "cannot set name of output/set dimension",
+ return isl_aff_free(aff));
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ aff->ls = isl_local_space_set_dim_name(aff->ls, type, pos, s);
+ if (!aff->ls)
+ return isl_aff_free(aff);
+
+ return aff;
+}
+
+__isl_give isl_aff *isl_aff_set_dim_id(__isl_take isl_aff *aff,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id)
+{
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ goto error;
+ if (type == isl_dim_out)
+ isl_die(aff->v->ctx, isl_error_invalid,
+ "cannot set name of output/set dimension",
+ goto error);
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ aff->ls = isl_local_space_set_dim_id(aff->ls, type, pos, id);
+ if (!aff->ls)
+ return isl_aff_free(aff);
+
+ return aff;
+error:
+ isl_id_free(id);
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Replace the identifier of the input tuple of "aff" by "id".
+ * type is currently required to be equal to isl_dim_in
+ */
+__isl_give isl_aff *isl_aff_set_tuple_id(__isl_take isl_aff *aff,
+ enum isl_dim_type type, __isl_take isl_id *id)
+{
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ goto error;
+ if (type != isl_dim_in)
+ isl_die(aff->v->ctx, isl_error_invalid,
+ "cannot only set id of input tuple", goto error);
+ aff->ls = isl_local_space_set_tuple_id(aff->ls, isl_dim_set, id);
+ if (!aff->ls)
+ return isl_aff_free(aff);
+
+ return aff;
+error:
+ isl_id_free(id);
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Exploit the equalities in "eq" to simplify the affine expression
+ * and the expressions of the integer divisions in the local space.
+ * The integer divisions in this local space are assumed to appear
+ * as regular dimensions in "eq".
+ */
+static __isl_give isl_aff *isl_aff_substitute_equalities_lifted(
+ __isl_take isl_aff *aff, __isl_take isl_basic_set *eq)
+{
+ int i, j;
+ unsigned o_div;
+ unsigned n_div;
+
+ if (!eq)
+ goto error;
+ if (eq->n_eq == 0) {
+ isl_basic_set_free(eq);
+ return aff;
+ }
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ goto error;
+
+ aff->ls = isl_local_space_substitute_equalities(aff->ls,
+ isl_basic_set_copy(eq));
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->ls || !aff->v)
+ goto error;
+
+ o_div = isl_basic_set_offset(eq, isl_dim_div);
+ n_div = eq->n_div;
+ for (i = 0; i < eq->n_eq; ++i) {
+ j = isl_seq_last_non_zero(eq->eq[i], o_div + n_div);
+ if (j < 0 || j == 0 || j >= o_div)
+ continue;
+
+ isl_seq_elim(aff->v->el + 1, eq->eq[i], j, o_div,
+ &aff->v->el[0]);
+ }
+
+ isl_basic_set_free(eq);
+ aff = isl_aff_normalize(aff);
+ return aff;
+error:
+ isl_basic_set_free(eq);
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Exploit the equalities in "eq" to simplify the affine expression
+ * and the expressions of the integer divisions in the local space.
+ */
+__isl_give isl_aff *isl_aff_substitute_equalities(__isl_take isl_aff *aff,
+ __isl_take isl_basic_set *eq)
+{
+ isl_size n_div;
+
+ n_div = isl_aff_domain_dim(aff, isl_dim_div);
+ if (n_div < 0)
+ goto error;
+ if (n_div > 0)
+ eq = isl_basic_set_add_dims(eq, isl_dim_set, n_div);
+ return isl_aff_substitute_equalities_lifted(aff, eq);
+error:
+ isl_basic_set_free(eq);
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Look for equalities among the variables shared by context and aff
+ * and the integer divisions of aff, if any.
+ * The equalities are then used to eliminate coefficients and/or integer
+ * divisions from aff.
+ */
+__isl_give isl_aff *isl_aff_gist(__isl_take isl_aff *aff,
+ __isl_take isl_set *context)
+{
+ isl_local_space *ls;
+ isl_basic_set *hull;
+
+ ls = isl_aff_get_domain_local_space(aff);
+ context = isl_local_space_lift_set(ls, context);
+
+ hull = isl_set_affine_hull(context);
+ return isl_aff_substitute_equalities_lifted(aff, hull);
+}
+
+__isl_give isl_aff *isl_aff_gist_params(__isl_take isl_aff *aff,
+ __isl_take isl_set *context)
+{
+ isl_set *dom_context = isl_set_universe(isl_aff_get_domain_space(aff));
+ dom_context = isl_set_intersect_params(dom_context, context);
+ return isl_aff_gist(aff, dom_context);
+}
+
+/* Return a basic set containing those elements in the space
+ * of aff where it is positive. "rational" should not be set.
+ *
+ * If "aff" is NaN, then it is not positive.
+ */
+static __isl_give isl_basic_set *aff_pos_basic_set(__isl_take isl_aff *aff,
+ int rational, void *user)
+{
+ isl_constraint *ineq;
+ isl_basic_set *bset;
+ isl_val *c;
+
+ if (!aff)
+ return NULL;
+ if (isl_aff_is_nan(aff)) {
+ isl_space *space = isl_aff_get_domain_space(aff);
+ isl_aff_free(aff);
+ return isl_basic_set_empty(space);
+ }
+ if (rational)
+ isl_die(isl_aff_get_ctx(aff), isl_error_unsupported,
+ "rational sets not supported", goto error);
+
+ ineq = isl_inequality_from_aff(aff);
+ c = isl_constraint_get_constant_val(ineq);
+ c = isl_val_sub_ui(c, 1);
+ ineq = isl_constraint_set_constant_val(ineq, c);
+
+ bset = isl_basic_set_from_constraint(ineq);
+ bset = isl_basic_set_simplify(bset);
+ return bset;
+error:
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Return a basic set containing those elements in the space
+ * of aff where it is non-negative.
+ * If "rational" is set, then return a rational basic set.
+ *
+ * If "aff" is NaN, then it is not non-negative (it's not negative either).
+ */
+static __isl_give isl_basic_set *aff_nonneg_basic_set(
+ __isl_take isl_aff *aff, int rational, void *user)
+{
+ isl_constraint *ineq;
+ isl_basic_set *bset;
+
+ if (!aff)
+ return NULL;
+ if (isl_aff_is_nan(aff)) {
+ isl_space *space = isl_aff_get_domain_space(aff);
+ isl_aff_free(aff);
+ return isl_basic_set_empty(space);
+ }
+
+ ineq = isl_inequality_from_aff(aff);
+
+ bset = isl_basic_set_from_constraint(ineq);
+ if (rational)
+ bset = isl_basic_set_set_rational(bset);
+ bset = isl_basic_set_simplify(bset);
+ return bset;
+}
+
+/* Return a basic set containing those elements in the space
+ * of aff where it is non-negative.
+ */
+__isl_give isl_basic_set *isl_aff_nonneg_basic_set(__isl_take isl_aff *aff)
+{
+ return aff_nonneg_basic_set(aff, 0, NULL);
+}
+
+/* Return a basic set containing those elements in the domain space
+ * of "aff" where it is positive.
+ */
+__isl_give isl_basic_set *isl_aff_pos_basic_set(__isl_take isl_aff *aff)
+{
+ aff = isl_aff_add_constant_num_si(aff, -1);
+ return isl_aff_nonneg_basic_set(aff);
+}
+
+/* Return a basic set containing those elements in the domain space
+ * of aff where it is negative.
+ */
+__isl_give isl_basic_set *isl_aff_neg_basic_set(__isl_take isl_aff *aff)
+{
+ aff = isl_aff_neg(aff);
+ return isl_aff_pos_basic_set(aff);
+}
+
+/* Return a basic set containing those elements in the space
+ * of aff where it is zero.
+ * If "rational" is set, then return a rational basic set.
+ *
+ * If "aff" is NaN, then it is not zero.
+ */
+static __isl_give isl_basic_set *aff_zero_basic_set(__isl_take isl_aff *aff,
+ int rational, void *user)
+{
+ isl_constraint *ineq;
+ isl_basic_set *bset;
+
+ if (!aff)
+ return NULL;
+ if (isl_aff_is_nan(aff)) {
+ isl_space *space = isl_aff_get_domain_space(aff);
+ isl_aff_free(aff);
+ return isl_basic_set_empty(space);
+ }
+
+ ineq = isl_equality_from_aff(aff);
+
+ bset = isl_basic_set_from_constraint(ineq);
+ if (rational)
+ bset = isl_basic_set_set_rational(bset);
+ bset = isl_basic_set_simplify(bset);
+ return bset;
+}
+
+/* Return a basic set containing those elements in the space
+ * of aff where it is zero.
+ */
+__isl_give isl_basic_set *isl_aff_zero_basic_set(__isl_take isl_aff *aff)
+{
+ return aff_zero_basic_set(aff, 0, NULL);
+}
+
+/* Return a basic set containing those elements in the shared space
+ * of aff1 and aff2 where aff1 is greater than or equal to aff2.
+ */
+__isl_give isl_basic_set *isl_aff_ge_basic_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ aff1 = isl_aff_sub(aff1, aff2);
+
+ return isl_aff_nonneg_basic_set(aff1);
+}
+
+/* Return a basic set containing those elements in the shared domain space
+ * of "aff1" and "aff2" where "aff1" is greater than "aff2".
+ */
+__isl_give isl_basic_set *isl_aff_gt_basic_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ aff1 = isl_aff_sub(aff1, aff2);
+
+ return isl_aff_pos_basic_set(aff1);
+}
+
+/* Return a set containing those elements in the shared space
+ * of aff1 and aff2 where aff1 is greater than or equal to aff2.
+ */
+__isl_give isl_set *isl_aff_ge_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ return isl_set_from_basic_set(isl_aff_ge_basic_set(aff1, aff2));
+}
+
+/* Return a set containing those elements in the shared domain space
+ * of aff1 and aff2 where aff1 is greater than aff2.
+ *
+ * If either of the two inputs is NaN, then the result is empty,
+ * as comparisons with NaN always return false.
+ */
+__isl_give isl_set *isl_aff_gt_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ return isl_set_from_basic_set(isl_aff_gt_basic_set(aff1, aff2));
+}
+
+/* Return a basic set containing those elements in the shared space
+ * of aff1 and aff2 where aff1 is smaller than or equal to aff2.
+ */
+__isl_give isl_basic_set *isl_aff_le_basic_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ return isl_aff_ge_basic_set(aff2, aff1);
+}
+
+/* Return a basic set containing those elements in the shared domain space
+ * of "aff1" and "aff2" where "aff1" is smaller than "aff2".
+ */
+__isl_give isl_basic_set *isl_aff_lt_basic_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ return isl_aff_gt_basic_set(aff2, aff1);
+}
+
+/* Return a set containing those elements in the shared space
+ * of aff1 and aff2 where aff1 is smaller than or equal to aff2.
+ */
+__isl_give isl_set *isl_aff_le_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ return isl_aff_ge_set(aff2, aff1);
+}
+
+/* Return a set containing those elements in the shared domain space
+ * of "aff1" and "aff2" where "aff1" is smaller than "aff2".
+ */
+__isl_give isl_set *isl_aff_lt_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ return isl_set_from_basic_set(isl_aff_lt_basic_set(aff1, aff2));
+}
+
+/* Return a basic set containing those elements in the shared space
+ * of aff1 and aff2 where aff1 and aff2 are equal.
+ */
+__isl_give isl_basic_set *isl_aff_eq_basic_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ aff1 = isl_aff_sub(aff1, aff2);
+
+ return isl_aff_zero_basic_set(aff1);
+}
+
+/* Return a set containing those elements in the shared space
+ * of aff1 and aff2 where aff1 and aff2 are equal.
+ */
+__isl_give isl_set *isl_aff_eq_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ return isl_set_from_basic_set(isl_aff_eq_basic_set(aff1, aff2));
+}
+
+/* Return a set containing those elements in the shared domain space
+ * of aff1 and aff2 where aff1 and aff2 are not equal.
+ *
+ * If either of the two inputs is NaN, then the result is empty,
+ * as comparisons with NaN always return false.
+ */
+__isl_give isl_set *isl_aff_ne_set(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ isl_set *set_lt, *set_gt;
+
+ set_lt = isl_aff_lt_set(isl_aff_copy(aff1),
+ isl_aff_copy(aff2));
+ set_gt = isl_aff_gt_set(aff1, aff2);
+ return isl_set_union_disjoint(set_lt, set_gt);
+}
+
+__isl_give isl_aff *isl_aff_add_on_domain(__isl_keep isl_set *dom,
+ __isl_take isl_aff *aff1, __isl_take isl_aff *aff2)
+{
+ aff1 = isl_aff_add(aff1, aff2);
+ aff1 = isl_aff_gist(aff1, isl_set_copy(dom));
+ return aff1;
+}
+
+isl_bool isl_aff_is_empty(__isl_keep isl_aff *aff)
+{
+ if (!aff)
+ return isl_bool_error;
+
+ return isl_bool_false;
+}
+
+#undef TYPE
+#define TYPE isl_aff
+static
+#include "check_type_range_templ.c"
+
+/* Check whether the given affine expression has non-zero coefficient
+ * for any dimension in the given range or if any of these dimensions
+ * appear with non-zero coefficients in any of the integer divisions
+ * involved in the affine expression.
+ */
+isl_bool isl_aff_involves_dims(__isl_keep isl_aff *aff,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+ int *active = NULL;
+ isl_bool involves = isl_bool_false;
+
+ if (!aff)
+ return isl_bool_error;
+ if (n == 0)
+ return isl_bool_false;
+ if (isl_aff_check_range(aff, type, first, n) < 0)
+ return isl_bool_error;
+
+ active = isl_local_space_get_active(aff->ls, aff->v->el + 2);
+ if (!active)
+ goto error;
+
+ first += isl_local_space_offset(aff->ls, type) - 1;
+ for (i = 0; i < n; ++i)
+ if (active[first + i]) {
+ involves = isl_bool_true;
+ break;
+ }
+
+ free(active);
+
+ return involves;
+error:
+ free(active);
+ return isl_bool_error;
+}
+
+/* Does "aff" involve any local variables, i.e., integer divisions?
+ */
+isl_bool isl_aff_involves_locals(__isl_keep isl_aff *aff)
+{
+ isl_size n;
+
+ n = isl_aff_dim(aff, isl_dim_div);
+ if (n < 0)
+ return isl_bool_error;
+ return isl_bool_ok(n > 0);
+}
+
+__isl_give isl_aff *isl_aff_drop_dims(__isl_take isl_aff *aff,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ isl_ctx *ctx;
+
+ if (!aff)
+ return NULL;
+ if (type == isl_dim_out)
+ isl_die(aff->v->ctx, isl_error_invalid,
+ "cannot drop output/set dimension",
+ return isl_aff_free(aff));
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ if (n == 0 && !isl_local_space_is_named_or_nested(aff->ls, type))
+ return aff;
+
+ ctx = isl_aff_get_ctx(aff);
+ if (isl_local_space_check_range(aff->ls, type, first, n) < 0)
+ return isl_aff_free(aff);
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->ls = isl_local_space_drop_dims(aff->ls, type, first, n);
+ if (!aff->ls)
+ return isl_aff_free(aff);
+
+ first += 1 + isl_local_space_offset(aff->ls, type);
+ aff->v = isl_vec_drop_els(aff->v, first, n);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ return aff;
+}
+
+/* Is the domain of "aff" a product?
+ */
+static isl_bool isl_aff_domain_is_product(__isl_keep isl_aff *aff)
+{
+ return isl_space_is_product(isl_aff_peek_domain_space(aff));
+}
+
+#undef TYPE
+#define TYPE isl_aff
+#include <isl_domain_factor_templ.c>
+
+/* Project the domain of the affine expression onto its parameter space.
+ * The affine expression may not involve any of the domain dimensions.
+ */
+__isl_give isl_aff *isl_aff_project_domain_on_params(__isl_take isl_aff *aff)
+{
+ isl_space *space;
+ isl_size n;
+
+ n = isl_aff_dim(aff, isl_dim_in);
+ if (n < 0)
+ return isl_aff_free(aff);
+ aff = isl_aff_drop_domain(aff, 0, n);
+ space = isl_aff_get_domain_space(aff);
+ space = isl_space_params(space);
+ aff = isl_aff_reset_domain_space(aff, space);
+ return aff;
+}
+
+/* Convert an affine expression defined over a parameter domain
+ * into one that is defined over a zero-dimensional set.
+ */
+__isl_give isl_aff *isl_aff_from_range(__isl_take isl_aff *aff)
+{
+ isl_local_space *ls;
+
+ ls = isl_aff_take_domain_local_space(aff);
+ ls = isl_local_space_set_from_params(ls);
+ aff = isl_aff_restore_domain_local_space(aff, ls);
+
+ return aff;
+}
+
+__isl_give isl_aff *isl_aff_insert_dims(__isl_take isl_aff *aff,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ isl_ctx *ctx;
+
+ if (!aff)
+ return NULL;
+ if (type == isl_dim_out)
+ isl_die(aff->v->ctx, isl_error_invalid,
+ "cannot insert output/set dimensions",
+ return isl_aff_free(aff));
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ if (n == 0 && !isl_local_space_is_named_or_nested(aff->ls, type))
+ return aff;
+
+ ctx = isl_aff_get_ctx(aff);
+ if (isl_local_space_check_range(aff->ls, type, first, 0) < 0)
+ return isl_aff_free(aff);
+
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->ls = isl_local_space_insert_dims(aff->ls, type, first, n);
+ if (!aff->ls)
+ return isl_aff_free(aff);
+
+ first += 1 + isl_local_space_offset(aff->ls, type);
+ aff->v = isl_vec_insert_zero_els(aff->v, first, n);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ return aff;
+}
+
+__isl_give isl_aff *isl_aff_add_dims(__isl_take isl_aff *aff,
+ enum isl_dim_type type, unsigned n)
+{
+ isl_size pos;
+
+ pos = isl_aff_dim(aff, type);
+ if (pos < 0)
+ return isl_aff_free(aff);
+
+ return isl_aff_insert_dims(aff, type, pos, n);
+}
+
+/* Move the "n" dimensions of "src_type" starting at "src_pos" of "aff"
+ * to dimensions of "dst_type" at "dst_pos".
+ *
+ * We only support moving input dimensions to parameters and vice versa.
+ */
+__isl_give isl_aff *isl_aff_move_dims(__isl_take isl_aff *aff,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ unsigned g_dst_pos;
+ unsigned g_src_pos;
+ isl_size src_off, dst_off;
+
+ if (!aff)
+ return NULL;
+ if (n == 0 &&
+ !isl_local_space_is_named_or_nested(aff->ls, src_type) &&
+ !isl_local_space_is_named_or_nested(aff->ls, dst_type))
+ return aff;
+
+ if (dst_type == isl_dim_out || src_type == isl_dim_out)
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "cannot move output/set dimension",
+ return isl_aff_free(aff));
+ if (dst_type == isl_dim_div || src_type == isl_dim_div)
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "cannot move divs", return isl_aff_free(aff));
+ if (dst_type == isl_dim_in)
+ dst_type = isl_dim_set;
+ if (src_type == isl_dim_in)
+ src_type = isl_dim_set;
+
+ if (isl_local_space_check_range(aff->ls, src_type, src_pos, n) < 0)
+ return isl_aff_free(aff);
+ if (dst_type == src_type)
+ isl_die(isl_aff_get_ctx(aff), isl_error_unsupported,
+ "moving dims within the same type not supported",
+ return isl_aff_free(aff));
+
+ aff = isl_aff_cow(aff);
+ src_off = isl_aff_domain_offset(aff, src_type);
+ dst_off = isl_aff_domain_offset(aff, dst_type);
+ if (src_off < 0 || dst_off < 0)
+ return isl_aff_free(aff);
+
+ g_src_pos = 1 + src_off + src_pos;
+ g_dst_pos = 1 + dst_off + dst_pos;
+ if (dst_type > src_type)
+ g_dst_pos -= n;
+
+ aff->v = isl_vec_move_els(aff->v, g_dst_pos, g_src_pos, n);
+ aff->ls = isl_local_space_move_dims(aff->ls, dst_type, dst_pos,
+ src_type, src_pos, n);
+ if (!aff->v || !aff->ls)
+ return isl_aff_free(aff);
+
+ aff = sort_divs(aff);
+
+ return aff;
+}
+
+/* Return a zero isl_aff in the given space.
+ *
+ * This is a helper function for isl_pw_*_as_* that ensures a uniform
+ * interface over all piecewise types.
+ */
+static __isl_give isl_aff *isl_aff_zero_in_space(__isl_take isl_space *space)
+{
+ isl_local_space *ls;
+
+ ls = isl_local_space_from_space(isl_space_domain(space));
+ return isl_aff_zero_on_domain(ls);
+}
+
+#define isl_aff_involves_nan isl_aff_is_nan
+
+#undef PW
+#define PW isl_pw_aff
+#undef BASE
+#define BASE aff
+#undef EL_IS_ZERO
+#define EL_IS_ZERO is_empty
+#undef ZERO
+#define ZERO empty
+#undef IS_ZERO
+#define IS_ZERO is_empty
+#undef FIELD
+#define FIELD aff
+#undef DEFAULT_IS_ZERO
+#define DEFAULT_IS_ZERO 0
+
+#include <isl_pw_templ.c>
+#include <isl_pw_add_constant_val_templ.c>
+#include <isl_pw_bind_domain_templ.c>
+#include <isl_pw_eval.c>
+#include <isl_pw_hash.c>
+#include <isl_pw_insert_dims_templ.c>
+#include <isl_pw_insert_domain_templ.c>
+#include <isl_pw_move_dims_templ.c>
+#include <isl_pw_neg_templ.c>
+#include <isl_pw_pullback_templ.c>
+#include <isl_pw_sub_templ.c>
+#include <isl_pw_union_opt.c>
+
+#undef BASE
+#define BASE pw_aff
+
+#include <isl_union_single.c>
+#include <isl_union_neg.c>
+
+#undef BASE
+#define BASE aff
+
+#include <isl_union_pw_templ.c>
+
+/* Compute a piecewise quasi-affine expression with a domain that
+ * is the union of those of pwaff1 and pwaff2 and such that on each
+ * cell, the quasi-affine expression is the maximum of those of pwaff1
+ * and pwaff2. If only one of pwaff1 or pwaff2 is defined on a given
+ * cell, then the associated expression is the defined one.
+ */
+__isl_give isl_pw_aff *isl_pw_aff_union_max(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ isl_pw_aff_align_params_bin(&pwaff1, &pwaff2);
+ return isl_pw_aff_union_opt_cmp(pwaff1, pwaff2, &isl_aff_ge_set);
+}
+
+/* Compute a piecewise quasi-affine expression with a domain that
+ * is the union of those of pwaff1 and pwaff2 and such that on each
+ * cell, the quasi-affine expression is the minimum of those of pwaff1
+ * and pwaff2. If only one of pwaff1 or pwaff2 is defined on a given
+ * cell, then the associated expression is the defined one.
+ */
+__isl_give isl_pw_aff *isl_pw_aff_union_min(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ isl_pw_aff_align_params_bin(&pwaff1, &pwaff2);
+ return isl_pw_aff_union_opt_cmp(pwaff1, pwaff2, &isl_aff_le_set);
+}
+
+__isl_give isl_pw_aff *isl_pw_aff_union_opt(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2, int max)
+{
+ if (max)
+ return isl_pw_aff_union_max(pwaff1, pwaff2);
+ else
+ return isl_pw_aff_union_min(pwaff1, pwaff2);
+}
+
+/* Is the domain of "pa" a product?
+ */
+static isl_bool isl_pw_aff_domain_is_product(__isl_keep isl_pw_aff *pa)
+{
+ return isl_space_domain_is_wrapping(isl_pw_aff_peek_space(pa));
+}
+
+#undef TYPE
+#define TYPE isl_pw_aff
+#include <isl_domain_factor_templ.c>
+
+/* Return a set containing those elements in the domain
+ * of "pwaff" where it satisfies "fn" (if complement is 0) or
+ * does not satisfy "fn" (if complement is 1).
+ *
+ * The pieces with a NaN never belong to the result since
+ * NaN does not satisfy any property.
+ */
+static __isl_give isl_set *pw_aff_locus(__isl_take isl_pw_aff *pwaff,
+ __isl_give isl_basic_set *(*fn)(__isl_take isl_aff *aff, int rational,
+ void *user),
+ int complement, void *user)
+{
+ int i;
+ isl_set *set;
+
+ if (!pwaff)
+ return NULL;
+
+ set = isl_set_empty(isl_pw_aff_get_domain_space(pwaff));
+
+ for (i = 0; i < pwaff->n; ++i) {
+ isl_basic_set *bset;
+ isl_set *set_i, *locus;
+ isl_bool rational;
+
+ if (isl_aff_is_nan(pwaff->p[i].aff))
+ continue;
+
+ rational = isl_set_has_rational(pwaff->p[i].set);
+ bset = fn(isl_aff_copy(pwaff->p[i].aff), rational, user);
+ locus = isl_set_from_basic_set(bset);
+ set_i = isl_set_copy(pwaff->p[i].set);
+ if (complement)
+ set_i = isl_set_subtract(set_i, locus);
+ else
+ set_i = isl_set_intersect(set_i, locus);
+ set = isl_set_union_disjoint(set, set_i);
+ }
+
+ isl_pw_aff_free(pwaff);
+
+ return set;
+}
+
+/* Return a set containing those elements in the domain
+ * of "pa" where it is positive.
+ */
+__isl_give isl_set *isl_pw_aff_pos_set(__isl_take isl_pw_aff *pa)
+{
+ return pw_aff_locus(pa, &aff_pos_basic_set, 0, NULL);
+}
+
+/* Return a set containing those elements in the domain
+ * of pwaff where it is non-negative.
+ */
+__isl_give isl_set *isl_pw_aff_nonneg_set(__isl_take isl_pw_aff *pwaff)
+{
+ return pw_aff_locus(pwaff, &aff_nonneg_basic_set, 0, NULL);
+}
+
+/* Return a set containing those elements in the domain
+ * of pwaff where it is zero.
+ */
+__isl_give isl_set *isl_pw_aff_zero_set(__isl_take isl_pw_aff *pwaff)
+{
+ return pw_aff_locus(pwaff, &aff_zero_basic_set, 0, NULL);
+}
+
+/* Return a set containing those elements in the domain
+ * of pwaff where it is not zero.
+ */
+__isl_give isl_set *isl_pw_aff_non_zero_set(__isl_take isl_pw_aff *pwaff)
+{
+ return pw_aff_locus(pwaff, &aff_zero_basic_set, 1, NULL);
+}
+
+/* Bind the affine function "aff" to the parameter "id",
+ * returning the elements in the domain where the affine expression
+ * is equal to the parameter.
+ */
+__isl_give isl_basic_set *isl_aff_bind_id(__isl_take isl_aff *aff,
+ __isl_take isl_id *id)
+{
+ isl_space *space;
+ isl_aff *aff_id;
+
+ space = isl_aff_get_domain_space(aff);
+ space = isl_space_add_param_id(space, isl_id_copy(id));
+
+ aff = isl_aff_align_params(aff, isl_space_copy(space));
+ aff_id = isl_aff_param_on_domain_space_id(space, id);
+
+ return isl_aff_eq_basic_set(aff, aff_id);
+}
+
+/* Wrapper around isl_aff_bind_id for use as pw_aff_locus callback.
+ * "rational" should not be set.
+ */
+static __isl_give isl_basic_set *aff_bind_id(__isl_take isl_aff *aff,
+ int rational, void *user)
+{
+ isl_id *id = user;
+
+ if (!aff)
+ return NULL;
+ if (rational)
+ isl_die(isl_aff_get_ctx(aff), isl_error_unsupported,
+ "rational binding not supported", goto error);
+ return isl_aff_bind_id(aff, isl_id_copy(id));
+error:
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Bind the piecewise affine function "pa" to the parameter "id",
+ * returning the elements in the domain where the expression
+ * is equal to the parameter.
+ */
+__isl_give isl_set *isl_pw_aff_bind_id(__isl_take isl_pw_aff *pa,
+ __isl_take isl_id *id)
+{
+ isl_set *bound;
+
+ bound = pw_aff_locus(pa, &aff_bind_id, 0, id);
+ isl_id_free(id);
+
+ return bound;
+}
+
+/* Return a set containing those elements in the shared domain
+ * of pwaff1 and pwaff2 where pwaff1 is greater than (or equal) to pwaff2.
+ *
+ * We compute the difference on the shared domain and then construct
+ * the set of values where this difference is non-negative.
+ * If strict is set, we first subtract 1 from the difference.
+ * If equal is set, we only return the elements where pwaff1 and pwaff2
+ * are equal.
+ */
+static __isl_give isl_set *pw_aff_gte_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2, int strict, int equal)
+{
+ isl_set *set1, *set2;
+
+ set1 = isl_pw_aff_domain(isl_pw_aff_copy(pwaff1));
+ set2 = isl_pw_aff_domain(isl_pw_aff_copy(pwaff2));
+ set1 = isl_set_intersect(set1, set2);
+ pwaff1 = isl_pw_aff_intersect_domain(pwaff1, isl_set_copy(set1));
+ pwaff2 = isl_pw_aff_intersect_domain(pwaff2, isl_set_copy(set1));
+ pwaff1 = isl_pw_aff_add(pwaff1, isl_pw_aff_neg(pwaff2));
+
+ if (strict) {
+ isl_space *space = isl_set_get_space(set1);
+ isl_aff *aff;
+ aff = isl_aff_zero_on_domain(isl_local_space_from_space(space));
+ aff = isl_aff_add_constant_si(aff, -1);
+ pwaff1 = isl_pw_aff_add(pwaff1, isl_pw_aff_alloc(set1, aff));
+ } else
+ isl_set_free(set1);
+
+ if (equal)
+ return isl_pw_aff_zero_set(pwaff1);
+ return isl_pw_aff_nonneg_set(pwaff1);
+}
+
+/* Return a set containing those elements in the shared domain
+ * of pwaff1 and pwaff2 where pwaff1 is equal to pwaff2.
+ */
+__isl_give isl_set *isl_pw_aff_eq_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ isl_pw_aff_align_params_bin(&pwaff1, &pwaff2);
+ return pw_aff_gte_set(pwaff1, pwaff2, 0, 1);
+}
+
+/* Return a set containing those elements in the shared domain
+ * of pwaff1 and pwaff2 where pwaff1 is greater than or equal to pwaff2.
+ */
+__isl_give isl_set *isl_pw_aff_ge_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ isl_pw_aff_align_params_bin(&pwaff1, &pwaff2);
+ return pw_aff_gte_set(pwaff1, pwaff2, 0, 0);
+}
+
+/* Return a set containing those elements in the shared domain
+ * of pwaff1 and pwaff2 where pwaff1 is strictly greater than pwaff2.
+ */
+__isl_give isl_set *isl_pw_aff_gt_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ isl_pw_aff_align_params_bin(&pwaff1, &pwaff2);
+ return pw_aff_gte_set(pwaff1, pwaff2, 1, 0);
+}
+
+__isl_give isl_set *isl_pw_aff_le_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ return isl_pw_aff_ge_set(pwaff2, pwaff1);
+}
+
+__isl_give isl_set *isl_pw_aff_lt_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ return isl_pw_aff_gt_set(pwaff2, pwaff1);
+}
+
+/* Return a map containing pairs of elements in the domains of "pa1" and "pa2"
+ * where the function values are ordered in the same way as "order",
+ * which returns a set in the shared domain of its two arguments.
+ *
+ * Let "pa1" and "pa2" be defined on domains A and B respectively.
+ * We first pull back the two functions such that they are defined on
+ * the domain [A -> B]. Then we apply "order", resulting in a set
+ * in the space [A -> B]. Finally, we unwrap this set to obtain
+ * a map in the space A -> B.
+ */
+static __isl_give isl_map *isl_pw_aff_order_map(
+ __isl_take isl_pw_aff *pa1, __isl_take isl_pw_aff *pa2,
+ __isl_give isl_set *(*order)(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2))
+{
+ isl_space *space1, *space2;
+ isl_multi_aff *ma;
+ isl_set *set;
+
+ isl_pw_aff_align_params_bin(&pa1, &pa2);
+ space1 = isl_space_domain(isl_pw_aff_get_space(pa1));
+ space2 = isl_space_domain(isl_pw_aff_get_space(pa2));
+ space1 = isl_space_map_from_domain_and_range(space1, space2);
+ ma = isl_multi_aff_domain_map(isl_space_copy(space1));
+ pa1 = isl_pw_aff_pullback_multi_aff(pa1, ma);
+ ma = isl_multi_aff_range_map(space1);
+ pa2 = isl_pw_aff_pullback_multi_aff(pa2, ma);
+ set = order(pa1, pa2);
+
+ return isl_set_unwrap(set);
+}
+
+/* Return a map containing pairs of elements in the domains of "pa1" and "pa2"
+ * where the function values are equal.
+ */
+__isl_give isl_map *isl_pw_aff_eq_map(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2)
+{
+ return isl_pw_aff_order_map(pa1, pa2, &isl_pw_aff_eq_set);
+}
+
+/* Return a map containing pairs of elements in the domains of "pa1" and "pa2"
+ * where the function value of "pa1" is less than or equal to
+ * the function value of "pa2".
+ */
+__isl_give isl_map *isl_pw_aff_le_map(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2)
+{
+ return isl_pw_aff_order_map(pa1, pa2, &isl_pw_aff_le_set);
+}
+
+/* Return a map containing pairs of elements in the domains of "pa1" and "pa2"
+ * where the function value of "pa1" is less than the function value of "pa2".
+ */
+__isl_give isl_map *isl_pw_aff_lt_map(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2)
+{
+ return isl_pw_aff_order_map(pa1, pa2, &isl_pw_aff_lt_set);
+}
+
+/* Return a map containing pairs of elements in the domains of "pa1" and "pa2"
+ * where the function value of "pa1" is greater than or equal to
+ * the function value of "pa2".
+ */
+__isl_give isl_map *isl_pw_aff_ge_map(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2)
+{
+ return isl_pw_aff_order_map(pa1, pa2, &isl_pw_aff_ge_set);
+}
+
+/* Return a map containing pairs of elements in the domains of "pa1" and "pa2"
+ * where the function value of "pa1" is greater than the function value
+ * of "pa2".
+ */
+__isl_give isl_map *isl_pw_aff_gt_map(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2)
+{
+ return isl_pw_aff_order_map(pa1, pa2, &isl_pw_aff_gt_set);
+}
+
+/* Return a set containing those elements in the shared domain
+ * of the elements of list1 and list2 where each element in list1
+ * has the relation specified by "fn" with each element in list2.
+ */
+static __isl_give isl_set *pw_aff_list_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2,
+ __isl_give isl_set *(*fn)(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2))
+{
+ int i, j;
+ isl_ctx *ctx;
+ isl_set *set;
+
+ if (!list1 || !list2)
+ goto error;
+
+ ctx = isl_pw_aff_list_get_ctx(list1);
+ if (list1->n < 1 || list2->n < 1)
+ isl_die(ctx, isl_error_invalid,
+ "list should contain at least one element", goto error);
+
+ set = isl_set_universe(isl_pw_aff_get_domain_space(list1->p[0]));
+ for (i = 0; i < list1->n; ++i)
+ for (j = 0; j < list2->n; ++j) {
+ isl_set *set_ij;
+
+ set_ij = fn(isl_pw_aff_copy(list1->p[i]),
+ isl_pw_aff_copy(list2->p[j]));
+ set = isl_set_intersect(set, set_ij);
+ }
+
+ isl_pw_aff_list_free(list1);
+ isl_pw_aff_list_free(list2);
+ return set;
+error:
+ isl_pw_aff_list_free(list1);
+ isl_pw_aff_list_free(list2);
+ return NULL;
+}
+
+/* Return a set containing those elements in the shared domain
+ * of the elements of list1 and list2 where each element in list1
+ * is equal to each element in list2.
+ */
+__isl_give isl_set *isl_pw_aff_list_eq_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2)
+{
+ return pw_aff_list_set(list1, list2, &isl_pw_aff_eq_set);
+}
+
+__isl_give isl_set *isl_pw_aff_list_ne_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2)
+{
+ return pw_aff_list_set(list1, list2, &isl_pw_aff_ne_set);
+}
+
+/* Return a set containing those elements in the shared domain
+ * of the elements of list1 and list2 where each element in list1
+ * is less than or equal to each element in list2.
+ */
+__isl_give isl_set *isl_pw_aff_list_le_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2)
+{
+ return pw_aff_list_set(list1, list2, &isl_pw_aff_le_set);
+}
+
+__isl_give isl_set *isl_pw_aff_list_lt_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2)
+{
+ return pw_aff_list_set(list1, list2, &isl_pw_aff_lt_set);
+}
+
+__isl_give isl_set *isl_pw_aff_list_ge_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2)
+{
+ return pw_aff_list_set(list1, list2, &isl_pw_aff_ge_set);
+}
+
+__isl_give isl_set *isl_pw_aff_list_gt_set(__isl_take isl_pw_aff_list *list1,
+ __isl_take isl_pw_aff_list *list2)
+{
+ return pw_aff_list_set(list1, list2, &isl_pw_aff_gt_set);
+}
+
+
+/* Return a set containing those elements in the shared domain
+ * of pwaff1 and pwaff2 where pwaff1 is not equal to pwaff2.
+ */
+__isl_give isl_set *isl_pw_aff_ne_set(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ isl_set *set_lt, *set_gt;
+
+ isl_pw_aff_align_params_bin(&pwaff1, &pwaff2);
+ set_lt = isl_pw_aff_lt_set(isl_pw_aff_copy(pwaff1),
+ isl_pw_aff_copy(pwaff2));
+ set_gt = isl_pw_aff_gt_set(pwaff1, pwaff2);
+ return isl_set_union_disjoint(set_lt, set_gt);
+}
+
+__isl_give isl_pw_aff *isl_pw_aff_scale_down(__isl_take isl_pw_aff *pwaff,
+ isl_int v)
+{
+ int i;
+
+ if (isl_int_is_one(v))
+ return pwaff;
+ if (!isl_int_is_pos(v))
+ isl_die(isl_pw_aff_get_ctx(pwaff), isl_error_invalid,
+ "factor needs to be positive",
+ return isl_pw_aff_free(pwaff));
+ pwaff = isl_pw_aff_cow(pwaff);
+ if (!pwaff)
+ return NULL;
+ if (pwaff->n == 0)
+ return pwaff;
+
+ for (i = 0; i < pwaff->n; ++i) {
+ pwaff->p[i].aff = isl_aff_scale_down(pwaff->p[i].aff, v);
+ if (!pwaff->p[i].aff)
+ return isl_pw_aff_free(pwaff);
+ }
+
+ return pwaff;
+}
+
+__isl_give isl_pw_aff *isl_pw_aff_floor(__isl_take isl_pw_aff *pwaff)
+{
+ int i;
+
+ pwaff = isl_pw_aff_cow(pwaff);
+ if (!pwaff)
+ return NULL;
+ if (pwaff->n == 0)
+ return pwaff;
+
+ for (i = 0; i < pwaff->n; ++i) {
+ pwaff->p[i].aff = isl_aff_floor(pwaff->p[i].aff);
+ if (!pwaff->p[i].aff)
+ return isl_pw_aff_free(pwaff);
+ }
+
+ return pwaff;
+}
+
+__isl_give isl_pw_aff *isl_pw_aff_ceil(__isl_take isl_pw_aff *pwaff)
+{
+ int i;
+
+ pwaff = isl_pw_aff_cow(pwaff);
+ if (!pwaff)
+ return NULL;
+ if (pwaff->n == 0)
+ return pwaff;
+
+ for (i = 0; i < pwaff->n; ++i) {
+ pwaff->p[i].aff = isl_aff_ceil(pwaff->p[i].aff);
+ if (!pwaff->p[i].aff)
+ return isl_pw_aff_free(pwaff);
+ }
+
+ return pwaff;
+}
+
+/* Assuming that "cond1" and "cond2" are disjoint,
+ * return an affine expression that is equal to pwaff1 on cond1
+ * and to pwaff2 on cond2.
+ */
+static __isl_give isl_pw_aff *isl_pw_aff_select(
+ __isl_take isl_set *cond1, __isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_set *cond2, __isl_take isl_pw_aff *pwaff2)
+{
+ pwaff1 = isl_pw_aff_intersect_domain(pwaff1, cond1);
+ pwaff2 = isl_pw_aff_intersect_domain(pwaff2, cond2);
+
+ return isl_pw_aff_add_disjoint(pwaff1, pwaff2);
+}
+
+/* Return an affine expression that is equal to pwaff_true for elements
+ * where "cond" is non-zero and to pwaff_false for elements where "cond"
+ * is zero.
+ * That is, return cond ? pwaff_true : pwaff_false;
+ *
+ * If "cond" involves and NaN, then we conservatively return a NaN
+ * on its entire domain. In principle, we could consider the pieces
+ * where it is NaN separately from those where it is not.
+ *
+ * If "pwaff_true" and "pwaff_false" are obviously equal to each other,
+ * then only use the domain of "cond" to restrict the domain.
+ */
+__isl_give isl_pw_aff *isl_pw_aff_cond(__isl_take isl_pw_aff *cond,
+ __isl_take isl_pw_aff *pwaff_true, __isl_take isl_pw_aff *pwaff_false)
+{
+ isl_set *cond_true, *cond_false;
+ isl_bool equal;
+
+ if (!cond)
+ goto error;
+ if (isl_pw_aff_involves_nan(cond)) {
+ isl_space *space = isl_pw_aff_get_domain_space(cond);
+ isl_local_space *ls = isl_local_space_from_space(space);
+ isl_pw_aff_free(cond);
+ isl_pw_aff_free(pwaff_true);
+ isl_pw_aff_free(pwaff_false);
+ return isl_pw_aff_nan_on_domain(ls);
+ }
+
+ pwaff_true = isl_pw_aff_align_params(pwaff_true,
+ isl_pw_aff_get_space(pwaff_false));
+ pwaff_false = isl_pw_aff_align_params(pwaff_false,
+ isl_pw_aff_get_space(pwaff_true));
+ equal = isl_pw_aff_plain_is_equal(pwaff_true, pwaff_false);
+ if (equal < 0)
+ goto error;
+ if (equal) {
+ isl_set *dom;
+
+ dom = isl_set_coalesce(isl_pw_aff_domain(cond));
+ isl_pw_aff_free(pwaff_false);
+ return isl_pw_aff_intersect_domain(pwaff_true, dom);
+ }
+
+ cond_true = isl_pw_aff_non_zero_set(isl_pw_aff_copy(cond));
+ cond_false = isl_pw_aff_zero_set(cond);
+ return isl_pw_aff_select(cond_true, pwaff_true,
+ cond_false, pwaff_false);
+error:
+ isl_pw_aff_free(cond);
+ isl_pw_aff_free(pwaff_true);
+ isl_pw_aff_free(pwaff_false);
+ return NULL;
+}
+
+isl_bool isl_aff_is_cst(__isl_keep isl_aff *aff)
+{
+ int pos;
+
+ if (!aff)
+ return isl_bool_error;
+
+ pos = isl_seq_first_non_zero(aff->v->el + 2, aff->v->size - 2);
+ return isl_bool_ok(pos == -1);
+}
+
+/* Check whether pwaff is a piecewise constant.
+ */
+isl_bool isl_pw_aff_is_cst(__isl_keep isl_pw_aff *pwaff)
+{
+ int i;
+
+ if (!pwaff)
+ return isl_bool_error;
+
+ for (i = 0; i < pwaff->n; ++i) {
+ isl_bool is_cst = isl_aff_is_cst(pwaff->p[i].aff);
+ if (is_cst < 0 || !is_cst)
+ return is_cst;
+ }
+
+ return isl_bool_true;
+}
+
+/* Return the product of "aff1" and "aff2".
+ *
+ * If either of the two is NaN, then the result is NaN.
+ *
+ * Otherwise, at least one of "aff1" or "aff2" needs to be a constant.
+ */
+__isl_give isl_aff *isl_aff_mul(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ if (!aff1 || !aff2)
+ goto error;
+
+ if (isl_aff_is_nan(aff1)) {
+ isl_aff_free(aff2);
+ return aff1;
+ }
+ if (isl_aff_is_nan(aff2)) {
+ isl_aff_free(aff1);
+ return aff2;
+ }
+
+ if (!isl_aff_is_cst(aff2) && isl_aff_is_cst(aff1))
+ return isl_aff_mul(aff2, aff1);
+
+ if (!isl_aff_is_cst(aff2))
+ isl_die(isl_aff_get_ctx(aff1), isl_error_invalid,
+ "at least one affine expression should be constant",
+ goto error);
+
+ aff1 = isl_aff_cow(aff1);
+ if (!aff1 || !aff2)
+ goto error;
+
+ aff1 = isl_aff_scale(aff1, aff2->v->el[1]);
+ aff1 = isl_aff_scale_down(aff1, aff2->v->el[0]);
+
+ isl_aff_free(aff2);
+ return aff1;
+error:
+ isl_aff_free(aff1);
+ isl_aff_free(aff2);
+ return NULL;
+}
+
+/* Divide "aff1" by "aff2", assuming "aff2" is a constant.
+ *
+ * If either of the two is NaN, then the result is NaN.
+ * A division by zero also results in NaN.
+ */
+__isl_give isl_aff *isl_aff_div(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ isl_bool is_cst, is_zero;
+ int neg;
+
+ if (!aff1 || !aff2)
+ goto error;
+
+ if (isl_aff_is_nan(aff1)) {
+ isl_aff_free(aff2);
+ return aff1;
+ }
+ if (isl_aff_is_nan(aff2)) {
+ isl_aff_free(aff1);
+ return aff2;
+ }
+
+ is_cst = isl_aff_is_cst(aff2);
+ if (is_cst < 0)
+ goto error;
+ if (!is_cst)
+ isl_die(isl_aff_get_ctx(aff2), isl_error_invalid,
+ "second argument should be a constant", goto error);
+ is_zero = isl_aff_plain_is_zero(aff2);
+ if (is_zero < 0)
+ goto error;
+ if (is_zero)
+ return set_nan_free(aff1, aff2);
+
+ neg = isl_int_is_neg(aff2->v->el[1]);
+ if (neg) {
+ isl_int_neg(aff2->v->el[0], aff2->v->el[0]);
+ isl_int_neg(aff2->v->el[1], aff2->v->el[1]);
+ }
+
+ aff1 = isl_aff_scale(aff1, aff2->v->el[0]);
+ aff1 = isl_aff_scale_down(aff1, aff2->v->el[1]);
+
+ if (neg) {
+ isl_int_neg(aff2->v->el[0], aff2->v->el[0]);
+ isl_int_neg(aff2->v->el[1], aff2->v->el[1]);
+ }
+
+ isl_aff_free(aff2);
+ return aff1;
+error:
+ isl_aff_free(aff1);
+ isl_aff_free(aff2);
+ return NULL;
+}
+
+__isl_give isl_pw_aff *isl_pw_aff_add(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ isl_pw_aff_align_params_bin(&pwaff1, &pwaff2);
+ return isl_pw_aff_on_shared_domain(pwaff1, pwaff2, &isl_aff_add);
+}
+
+__isl_give isl_pw_aff *isl_pw_aff_union_add(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ return isl_pw_aff_union_add_(pwaff1, pwaff2);
+}
+
+__isl_give isl_pw_aff *isl_pw_aff_mul(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ isl_pw_aff_align_params_bin(&pwaff1, &pwaff2);
+ return isl_pw_aff_on_shared_domain(pwaff1, pwaff2, &isl_aff_mul);
+}
+
+/* Divide "pa1" by "pa2", assuming "pa2" is a piecewise constant.
+ */
+__isl_give isl_pw_aff *isl_pw_aff_div(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2)
+{
+ int is_cst;
+
+ is_cst = isl_pw_aff_is_cst(pa2);
+ if (is_cst < 0)
+ goto error;
+ if (!is_cst)
+ isl_die(isl_pw_aff_get_ctx(pa2), isl_error_invalid,
+ "second argument should be a piecewise constant",
+ goto error);
+ isl_pw_aff_align_params_bin(&pa1, &pa2);
+ return isl_pw_aff_on_shared_domain(pa1, pa2, &isl_aff_div);
+error:
+ isl_pw_aff_free(pa1);
+ isl_pw_aff_free(pa2);
+ return NULL;
+}
+
+/* Compute the quotient of the integer division of "pa1" by "pa2"
+ * with rounding towards zero.
+ * "pa2" is assumed to be a piecewise constant.
+ *
+ * In particular, return
+ *
+ * pa1 >= 0 ? floor(pa1/pa2) : ceil(pa1/pa2)
+ *
+ */
+__isl_give isl_pw_aff *isl_pw_aff_tdiv_q(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2)
+{
+ int is_cst;
+ isl_set *cond;
+ isl_pw_aff *f, *c;
+
+ is_cst = isl_pw_aff_is_cst(pa2);
+ if (is_cst < 0)
+ goto error;
+ if (!is_cst)
+ isl_die(isl_pw_aff_get_ctx(pa2), isl_error_invalid,
+ "second argument should be a piecewise constant",
+ goto error);
+
+ pa1 = isl_pw_aff_div(pa1, pa2);
+
+ cond = isl_pw_aff_nonneg_set(isl_pw_aff_copy(pa1));
+ f = isl_pw_aff_floor(isl_pw_aff_copy(pa1));
+ c = isl_pw_aff_ceil(pa1);
+ return isl_pw_aff_cond(isl_set_indicator_function(cond), f, c);
+error:
+ isl_pw_aff_free(pa1);
+ isl_pw_aff_free(pa2);
+ return NULL;
+}
+
+/* Compute the remainder of the integer division of "pa1" by "pa2"
+ * with rounding towards zero.
+ * "pa2" is assumed to be a piecewise constant.
+ *
+ * In particular, return
+ *
+ * pa1 - pa2 * (pa1 >= 0 ? floor(pa1/pa2) : ceil(pa1/pa2))
+ *
+ */
+__isl_give isl_pw_aff *isl_pw_aff_tdiv_r(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2)
+{
+ int is_cst;
+ isl_pw_aff *res;
+
+ is_cst = isl_pw_aff_is_cst(pa2);
+ if (is_cst < 0)
+ goto error;
+ if (!is_cst)
+ isl_die(isl_pw_aff_get_ctx(pa2), isl_error_invalid,
+ "second argument should be a piecewise constant",
+ goto error);
+ res = isl_pw_aff_tdiv_q(isl_pw_aff_copy(pa1), isl_pw_aff_copy(pa2));
+ res = isl_pw_aff_mul(pa2, res);
+ res = isl_pw_aff_sub(pa1, res);
+ return res;
+error:
+ isl_pw_aff_free(pa1);
+ isl_pw_aff_free(pa2);
+ return NULL;
+}
+
+/* Does either of "pa1" or "pa2" involve any NaN2?
+ */
+static isl_bool either_involves_nan(__isl_keep isl_pw_aff *pa1,
+ __isl_keep isl_pw_aff *pa2)
+{
+ isl_bool has_nan;
+
+ has_nan = isl_pw_aff_involves_nan(pa1);
+ if (has_nan < 0 || has_nan)
+ return has_nan;
+ return isl_pw_aff_involves_nan(pa2);
+}
+
+/* Replace "pa1" and "pa2" (at least one of which involves a NaN)
+ * by a NaN on their shared domain.
+ *
+ * In principle, the result could be refined to only being NaN
+ * on the parts of this domain where at least one of "pa1" or "pa2" is NaN.
+ */
+static __isl_give isl_pw_aff *replace_by_nan(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2)
+{
+ isl_local_space *ls;
+ isl_set *dom;
+ isl_pw_aff *pa;
+
+ dom = isl_set_intersect(isl_pw_aff_domain(pa1), isl_pw_aff_domain(pa2));
+ ls = isl_local_space_from_space(isl_set_get_space(dom));
+ pa = isl_pw_aff_nan_on_domain(ls);
+ pa = isl_pw_aff_intersect_domain(pa, dom);
+
+ return pa;
+}
+
+static __isl_give isl_pw_aff *pw_aff_min(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ isl_set *le;
+ isl_set *dom;
+
+ dom = isl_set_intersect(isl_pw_aff_domain(isl_pw_aff_copy(pwaff1)),
+ isl_pw_aff_domain(isl_pw_aff_copy(pwaff2)));
+ le = isl_pw_aff_le_set(isl_pw_aff_copy(pwaff1),
+ isl_pw_aff_copy(pwaff2));
+ dom = isl_set_subtract(dom, isl_set_copy(le));
+ return isl_pw_aff_select(le, pwaff1, dom, pwaff2);
+}
+
+static __isl_give isl_pw_aff *pw_aff_max(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ isl_set *ge;
+ isl_set *dom;
+
+ dom = isl_set_intersect(isl_pw_aff_domain(isl_pw_aff_copy(pwaff1)),
+ isl_pw_aff_domain(isl_pw_aff_copy(pwaff2)));
+ ge = isl_pw_aff_ge_set(isl_pw_aff_copy(pwaff1),
+ isl_pw_aff_copy(pwaff2));
+ dom = isl_set_subtract(dom, isl_set_copy(ge));
+ return isl_pw_aff_select(ge, pwaff1, dom, pwaff2);
+}
+
+/* Return an expression for the minimum (if "max" is not set) or
+ * the maximum (if "max" is set) of "pa1" and "pa2".
+ * If either expression involves any NaN, then return a NaN
+ * on the shared domain as result.
+ */
+static __isl_give isl_pw_aff *pw_aff_min_max(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2, int max)
+{
+ isl_bool has_nan;
+
+ has_nan = either_involves_nan(pa1, pa2);
+ if (has_nan < 0)
+ pa1 = isl_pw_aff_free(pa1);
+ else if (has_nan)
+ return replace_by_nan(pa1, pa2);
+
+ isl_pw_aff_align_params_bin(&pa1, &pa2);
+ if (max)
+ return pw_aff_max(pa1, pa2);
+ else
+ return pw_aff_min(pa1, pa2);
+}
+
+/* Return an expression for the minimum of "pwaff1" and "pwaff2".
+ */
+__isl_give isl_pw_aff *isl_pw_aff_min(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ return pw_aff_min_max(pwaff1, pwaff2, 0);
+}
+
+/* Return an expression for the maximum of "pwaff1" and "pwaff2".
+ */
+__isl_give isl_pw_aff *isl_pw_aff_max(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2)
+{
+ return pw_aff_min_max(pwaff1, pwaff2, 1);
+}
+
+static __isl_give isl_pw_aff *pw_aff_list_reduce(
+ __isl_take isl_pw_aff_list *list,
+ __isl_give isl_pw_aff *(*fn)(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2))
+{
+ int i;
+ isl_ctx *ctx;
+ isl_pw_aff *res;
+
+ if (!list)
+ return NULL;
+
+ ctx = isl_pw_aff_list_get_ctx(list);
+ if (list->n < 1)
+ isl_die(ctx, isl_error_invalid,
+ "list should contain at least one element", goto error);
+
+ res = isl_pw_aff_copy(list->p[0]);
+ for (i = 1; i < list->n; ++i)
+ res = fn(res, isl_pw_aff_copy(list->p[i]));
+
+ isl_pw_aff_list_free(list);
+ return res;
+error:
+ isl_pw_aff_list_free(list);
+ return NULL;
+}
+
+/* Return an isl_pw_aff that maps each element in the intersection of the
+ * domains of the elements of list to the minimal corresponding affine
+ * expression.
+ */
+__isl_give isl_pw_aff *isl_pw_aff_list_min(__isl_take isl_pw_aff_list *list)
+{
+ return pw_aff_list_reduce(list, &isl_pw_aff_min);
+}
+
+/* Return an isl_pw_aff that maps each element in the intersection of the
+ * domains of the elements of list to the maximal corresponding affine
+ * expression.
+ */
+__isl_give isl_pw_aff *isl_pw_aff_list_max(__isl_take isl_pw_aff_list *list)
+{
+ return pw_aff_list_reduce(list, &isl_pw_aff_max);
+}
+
+/* Mark the domains of "pwaff" as rational.
+ */
+__isl_give isl_pw_aff *isl_pw_aff_set_rational(__isl_take isl_pw_aff *pwaff)
+{
+ int i;
+
+ pwaff = isl_pw_aff_cow(pwaff);
+ if (!pwaff)
+ return NULL;
+ if (pwaff->n == 0)
+ return pwaff;
+
+ for (i = 0; i < pwaff->n; ++i) {
+ pwaff->p[i].set = isl_set_set_rational(pwaff->p[i].set);
+ if (!pwaff->p[i].set)
+ return isl_pw_aff_free(pwaff);
+ }
+
+ return pwaff;
+}
+
+/* Mark the domains of the elements of "list" as rational.
+ */
+__isl_give isl_pw_aff_list *isl_pw_aff_list_set_rational(
+ __isl_take isl_pw_aff_list *list)
+{
+ int i, n;
+
+ if (!list)
+ return NULL;
+ if (list->n == 0)
+ return list;
+
+ n = list->n;
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+
+ pa = isl_pw_aff_list_get_pw_aff(list, i);
+ pa = isl_pw_aff_set_rational(pa);
+ list = isl_pw_aff_list_set_pw_aff(list, i, pa);
+ }
+
+ return list;
+}
+
+/* Do the parameters of "aff" match those of "space"?
+ */
+isl_bool isl_aff_matching_params(__isl_keep isl_aff *aff,
+ __isl_keep isl_space *space)
+{
+ isl_space *aff_space;
+ isl_bool match;
+
+ if (!aff || !space)
+ return isl_bool_error;
+
+ aff_space = isl_aff_get_domain_space(aff);
+
+ match = isl_space_has_equal_params(space, aff_space);
+
+ isl_space_free(aff_space);
+ return match;
+}
+
+/* Check that the domain space of "aff" matches "space".
+ */
+isl_stat isl_aff_check_match_domain_space(__isl_keep isl_aff *aff,
+ __isl_keep isl_space *space)
+{
+ isl_space *aff_space;
+ isl_bool match;
+
+ if (!aff || !space)
+ return isl_stat_error;
+
+ aff_space = isl_aff_get_domain_space(aff);
+
+ match = isl_space_has_equal_params(space, aff_space);
+ if (match < 0)
+ goto error;
+ if (!match)
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "parameters don't match", goto error);
+ match = isl_space_tuple_is_equal(space, isl_dim_in,
+ aff_space, isl_dim_set);
+ if (match < 0)
+ goto error;
+ if (!match)
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "domains don't match", goto error);
+ isl_space_free(aff_space);
+ return isl_stat_ok;
+error:
+ isl_space_free(aff_space);
+ return isl_stat_error;
+}
+
+/* Return the shared (universe) domain of the elements of "ma".
+ *
+ * Since an isl_multi_aff (and an isl_aff) is always total,
+ * the domain is always the universe set in its domain space.
+ * This is a helper function for use in the generic isl_multi_*_bind.
+ */
+static __isl_give isl_basic_set *isl_multi_aff_domain(
+ __isl_take isl_multi_aff *ma)
+{
+ isl_space *space;
+
+ space = isl_multi_aff_get_space(ma);
+ isl_multi_aff_free(ma);
+
+ return isl_basic_set_universe(isl_space_domain(space));
+}
+
+#undef BASE
+#define BASE aff
+
+#include <isl_multi_no_explicit_domain.c>
+#include <isl_multi_templ.c>
+#include <isl_multi_add_constant_templ.c>
+#include <isl_multi_apply_set.c>
+#include <isl_multi_arith_templ.c>
+#include <isl_multi_bind_domain_templ.c>
+#include <isl_multi_cmp.c>
+#include <isl_multi_dim_id_templ.c>
+#include <isl_multi_dims.c>
+#include <isl_multi_floor.c>
+#include <isl_multi_from_base_templ.c>
+#include <isl_multi_identity_templ.c>
+#include <isl_multi_insert_domain_templ.c>
+#include <isl_multi_locals_templ.c>
+#include <isl_multi_move_dims_templ.c>
+#include <isl_multi_nan_templ.c>
+#include <isl_multi_product_templ.c>
+#include <isl_multi_splice_templ.c>
+#include <isl_multi_tuple_id_templ.c>
+#include <isl_multi_unbind_params_templ.c>
+#include <isl_multi_zero_templ.c>
+
+#undef DOMBASE
+#define DOMBASE set
+#include <isl_multi_gist.c>
+
+#undef DOMBASE
+#define DOMBASE basic_set
+#include <isl_multi_bind_templ.c>
+
+/* Construct an isl_multi_aff living in "space" that corresponds
+ * to the affine transformation matrix "mat".
+ */
+__isl_give isl_multi_aff *isl_multi_aff_from_aff_mat(
+ __isl_take isl_space *space, __isl_take isl_mat *mat)
+{
+ isl_ctx *ctx;
+ isl_local_space *ls = NULL;
+ isl_multi_aff *ma = NULL;
+ isl_size n_row, n_col, n_out, total;
+ int i;
+
+ if (!space || !mat)
+ goto error;
+
+ ctx = isl_mat_get_ctx(mat);
+
+ n_row = isl_mat_rows(mat);
+ n_col = isl_mat_cols(mat);
+ n_out = isl_space_dim(space, isl_dim_out);
+ total = isl_space_dim(space, isl_dim_all);
+ if (n_row < 0 || n_col < 0 || n_out < 0 || total < 0)
+ goto error;
+ if (n_row < 1)
+ isl_die(ctx, isl_error_invalid,
+ "insufficient number of rows", goto error);
+ if (n_col < 1)
+ isl_die(ctx, isl_error_invalid,
+ "insufficient number of columns", goto error);
+ if (1 + n_out != n_row || 2 + total != n_row + n_col)
+ isl_die(ctx, isl_error_invalid,
+ "dimension mismatch", goto error);
+
+ ma = isl_multi_aff_zero(isl_space_copy(space));
+ space = isl_space_domain(space);
+ ls = isl_local_space_from_space(isl_space_copy(space));
+
+ for (i = 0; i < n_row - 1; ++i) {
+ isl_vec *v;
+ isl_aff *aff;
+
+ v = isl_vec_alloc(ctx, 1 + n_col);
+ if (!v)
+ goto error;
+ isl_int_set(v->el[0], mat->row[0][0]);
+ isl_seq_cpy(v->el + 1, mat->row[1 + i], n_col);
+ v = isl_vec_normalize(v);
+ aff = isl_aff_alloc_vec(isl_local_space_copy(ls), v);
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ }
+
+ isl_space_free(space);
+ isl_local_space_free(ls);
+ isl_mat_free(mat);
+ return ma;
+error:
+ isl_space_free(space);
+ isl_local_space_free(ls);
+ isl_mat_free(mat);
+ isl_multi_aff_free(ma);
+ return NULL;
+}
+
+/* Return the constant terms of the affine expressions of "ma".
+ */
+__isl_give isl_multi_val *isl_multi_aff_get_constant_multi_val(
+ __isl_keep isl_multi_aff *ma)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_multi_val *mv;
+
+ n = isl_multi_aff_size(ma);
+ if (n < 0)
+ return NULL;
+ space = isl_space_range(isl_multi_aff_get_space(ma));
+ space = isl_space_drop_all_params(space);
+ mv = isl_multi_val_zero(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_aff *aff;
+ isl_val *val;
+
+ aff = isl_multi_aff_get_at(ma, i);
+ val = isl_aff_get_constant_val(aff);
+ isl_aff_free(aff);
+ mv = isl_multi_val_set_at(mv, i, val);
+ }
+
+ return mv;
+}
+
+/* Remove any internal structure of the domain of "ma".
+ * If there is any such internal structure in the input,
+ * then the name of the corresponding space is also removed.
+ */
+__isl_give isl_multi_aff *isl_multi_aff_flatten_domain(
+ __isl_take isl_multi_aff *ma)
+{
+ isl_space *space;
+
+ if (!ma)
+ return NULL;
+
+ if (!ma->space->nested[0])
+ return ma;
+
+ space = isl_multi_aff_get_space(ma);
+ space = isl_space_flatten_domain(space);
+ ma = isl_multi_aff_reset_space(ma, space);
+
+ return ma;
+}
+
+/* Given a map space, return an isl_multi_aff that maps a wrapped copy
+ * of the space to its domain.
+ */
+__isl_give isl_multi_aff *isl_multi_aff_domain_map(__isl_take isl_space *space)
+{
+ int i;
+ isl_size n_in;
+ isl_local_space *ls;
+ isl_multi_aff *ma;
+
+ if (!space)
+ return NULL;
+ if (!isl_space_is_map(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "not a map space", goto error);
+
+ n_in = isl_space_dim(space, isl_dim_in);
+ if (n_in < 0)
+ goto error;
+ space = isl_space_domain_map(space);
+
+ ma = isl_multi_aff_alloc(isl_space_copy(space));
+ if (n_in == 0) {
+ isl_space_free(space);
+ return ma;
+ }
+
+ space = isl_space_domain(space);
+ ls = isl_local_space_from_space(space);
+ for (i = 0; i < n_in; ++i) {
+ isl_aff *aff;
+
+ aff = isl_aff_var_on_domain(isl_local_space_copy(ls),
+ isl_dim_set, i);
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ }
+ isl_local_space_free(ls);
+ return ma;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* This function performs the same operation as isl_multi_aff_domain_map,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give isl_multi_aff *isl_space_domain_map_multi_aff(
+ __isl_take isl_space *space)
+{
+ return isl_multi_aff_domain_map(space);
+}
+
+/* Given a map space, return an isl_multi_aff that maps a wrapped copy
+ * of the space to its range.
+ */
+__isl_give isl_multi_aff *isl_multi_aff_range_map(__isl_take isl_space *space)
+{
+ int i;
+ isl_size n_in, n_out;
+ isl_local_space *ls;
+ isl_multi_aff *ma;
+
+ if (!space)
+ return NULL;
+ if (!isl_space_is_map(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "not a map space", goto error);
+
+ n_in = isl_space_dim(space, isl_dim_in);
+ n_out = isl_space_dim(space, isl_dim_out);
+ if (n_in < 0 || n_out < 0)
+ goto error;
+ space = isl_space_range_map(space);
+
+ ma = isl_multi_aff_alloc(isl_space_copy(space));
+ if (n_out == 0) {
+ isl_space_free(space);
+ return ma;
+ }
+
+ space = isl_space_domain(space);
+ ls = isl_local_space_from_space(space);
+ for (i = 0; i < n_out; ++i) {
+ isl_aff *aff;
+
+ aff = isl_aff_var_on_domain(isl_local_space_copy(ls),
+ isl_dim_set, n_in + i);
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ }
+ isl_local_space_free(ls);
+ return ma;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* This function performs the same operation as isl_multi_aff_range_map,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give isl_multi_aff *isl_space_range_map_multi_aff(
+ __isl_take isl_space *space)
+{
+ return isl_multi_aff_range_map(space);
+}
+
+/* Given a map space, return an isl_pw_multi_aff that maps a wrapped copy
+ * of the space to its domain.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_domain_map(
+ __isl_take isl_space *space)
+{
+ return isl_pw_multi_aff_from_multi_aff(isl_multi_aff_domain_map(space));
+}
+
+/* This function performs the same operation as isl_pw_multi_aff_domain_map,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give isl_pw_multi_aff *isl_space_domain_map_pw_multi_aff(
+ __isl_take isl_space *space)
+{
+ return isl_pw_multi_aff_domain_map(space);
+}
+
+/* Given a map space, return an isl_pw_multi_aff that maps a wrapped copy
+ * of the space to its range.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_map(
+ __isl_take isl_space *space)
+{
+ return isl_pw_multi_aff_from_multi_aff(isl_multi_aff_range_map(space));
+}
+
+/* This function performs the same operation as isl_pw_multi_aff_range_map,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give isl_pw_multi_aff *isl_space_range_map_pw_multi_aff(
+ __isl_take isl_space *space)
+{
+ return isl_pw_multi_aff_range_map(space);
+}
+
+/* Given the space of a set and a range of set dimensions,
+ * construct an isl_multi_aff that projects out those dimensions.
+ */
+__isl_give isl_multi_aff *isl_multi_aff_project_out_map(
+ __isl_take isl_space *space, enum isl_dim_type type,
+ unsigned first, unsigned n)
+{
+ int i;
+ isl_size dim;
+ isl_local_space *ls;
+ isl_multi_aff *ma;
+
+ if (!space)
+ return NULL;
+ if (!isl_space_is_set(space))
+ isl_die(isl_space_get_ctx(space), isl_error_unsupported,
+ "expecting set space", goto error);
+ if (type != isl_dim_set)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "only set dimensions can be projected out", goto error);
+ if (isl_space_check_range(space, type, first, n) < 0)
+ goto error;
+
+ dim = isl_space_dim(space, isl_dim_set);
+ if (dim < 0)
+ goto error;
+
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, dim - n);
+
+ if (dim == n)
+ return isl_multi_aff_alloc(space);
+
+ ma = isl_multi_aff_alloc(isl_space_copy(space));
+ space = isl_space_domain(space);
+ ls = isl_local_space_from_space(space);
+
+ for (i = 0; i < first; ++i) {
+ isl_aff *aff;
+
+ aff = isl_aff_var_on_domain(isl_local_space_copy(ls),
+ isl_dim_set, i);
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ }
+
+ for (i = 0; i < dim - (first + n); ++i) {
+ isl_aff *aff;
+
+ aff = isl_aff_var_on_domain(isl_local_space_copy(ls),
+ isl_dim_set, first + n + i);
+ ma = isl_multi_aff_set_aff(ma, first + i, aff);
+ }
+
+ isl_local_space_free(ls);
+ return ma;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Given the space of a set and a range of set dimensions,
+ * construct an isl_pw_multi_aff that projects out those dimensions.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_project_out_map(
+ __isl_take isl_space *space, enum isl_dim_type type,
+ unsigned first, unsigned n)
+{
+ isl_multi_aff *ma;
+
+ ma = isl_multi_aff_project_out_map(space, type, first, n);
+ return isl_pw_multi_aff_from_multi_aff(ma);
+}
+
+/* This function performs the same operation as isl_pw_multi_aff_from_multi_aff,
+ * but is considered as a function on an isl_multi_aff when exported.
+ */
+__isl_give isl_pw_multi_aff *isl_multi_aff_to_pw_multi_aff(
+ __isl_take isl_multi_aff *ma)
+{
+ return isl_pw_multi_aff_from_multi_aff(ma);
+}
+
+/* Create a piecewise multi-affine expression in the given space that maps each
+ * input dimension to the corresponding output dimension.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_identity(
+ __isl_take isl_space *space)
+{
+ return isl_pw_multi_aff_from_multi_aff(isl_multi_aff_identity(space));
+}
+
+/* Create a piecewise multi expression that maps elements in the given space
+ * to themselves.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_identity_on_domain_space(
+ __isl_take isl_space *space)
+{
+ isl_multi_aff *ma;
+
+ ma = isl_multi_aff_identity_on_domain_space(space);
+ return isl_pw_multi_aff_from_multi_aff(ma);
+}
+
+/* This function performs the same operation as
+ * isl_pw_multi_aff_identity_on_domain_space,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give isl_pw_multi_aff *isl_space_identity_pw_multi_aff_on_domain(
+ __isl_take isl_space *space)
+{
+ return isl_pw_multi_aff_identity_on_domain_space(space);
+}
+
+/* Exploit the equalities in "eq" to simplify the affine expressions.
+ */
+static __isl_give isl_multi_aff *isl_multi_aff_substitute_equalities(
+ __isl_take isl_multi_aff *maff, __isl_take isl_basic_set *eq)
+{
+ int i;
+
+ maff = isl_multi_aff_cow(maff);
+ if (!maff || !eq)
+ goto error;
+
+ for (i = 0; i < maff->n; ++i) {
+ maff->u.p[i] = isl_aff_substitute_equalities(maff->u.p[i],
+ isl_basic_set_copy(eq));
+ if (!maff->u.p[i])
+ goto error;
+ }
+
+ isl_basic_set_free(eq);
+ return maff;
+error:
+ isl_basic_set_free(eq);
+ isl_multi_aff_free(maff);
+ return NULL;
+}
+
+__isl_give isl_multi_aff *isl_multi_aff_scale(__isl_take isl_multi_aff *maff,
+ isl_int f)
+{
+ int i;
+
+ maff = isl_multi_aff_cow(maff);
+ if (!maff)
+ return NULL;
+
+ for (i = 0; i < maff->n; ++i) {
+ maff->u.p[i] = isl_aff_scale(maff->u.p[i], f);
+ if (!maff->u.p[i])
+ return isl_multi_aff_free(maff);
+ }
+
+ return maff;
+}
+
+__isl_give isl_multi_aff *isl_multi_aff_add_on_domain(__isl_keep isl_set *dom,
+ __isl_take isl_multi_aff *maff1, __isl_take isl_multi_aff *maff2)
+{
+ maff1 = isl_multi_aff_add(maff1, maff2);
+ maff1 = isl_multi_aff_gist(maff1, isl_set_copy(dom));
+ return maff1;
+}
+
+isl_bool isl_multi_aff_is_empty(__isl_keep isl_multi_aff *maff)
+{
+ if (!maff)
+ return isl_bool_error;
+
+ return isl_bool_false;
+}
+
+/* Return the set of domain elements where "ma1" is lexicographically
+ * smaller than or equal to "ma2".
+ */
+__isl_give isl_set *isl_multi_aff_lex_le_set(__isl_take isl_multi_aff *ma1,
+ __isl_take isl_multi_aff *ma2)
+{
+ return isl_multi_aff_lex_ge_set(ma2, ma1);
+}
+
+/* Return the set of domain elements where "ma1" is lexicographically
+ * smaller than "ma2".
+ */
+__isl_give isl_set *isl_multi_aff_lex_lt_set(__isl_take isl_multi_aff *ma1,
+ __isl_take isl_multi_aff *ma2)
+{
+ return isl_multi_aff_lex_gt_set(ma2, ma1);
+}
+
+/* Return the set of domain elements where "ma1" is lexicographically
+ * greater than to "ma2". If "equal" is set, then include the domain
+ * elements where they are equal.
+ * Do this for the case where there are no entries.
+ * In this case, "ma1" cannot be greater than "ma2",
+ * but it is (greater than or) equal to "ma2".
+ */
+static __isl_give isl_set *isl_multi_aff_lex_gte_set_0d(
+ __isl_take isl_multi_aff *ma1, __isl_take isl_multi_aff *ma2, int equal)
+{
+ isl_space *space;
+
+ space = isl_multi_aff_get_domain_space(ma1);
+
+ isl_multi_aff_free(ma1);
+ isl_multi_aff_free(ma2);
+
+ if (equal)
+ return isl_set_universe(space);
+ else
+ return isl_set_empty(space);
+}
+
+/* Return the set where entry "i" of "ma1" and "ma2"
+ * satisfy the relation prescribed by "cmp".
+ */
+static __isl_give isl_set *isl_multi_aff_order_at(__isl_keep isl_multi_aff *ma1,
+ __isl_keep isl_multi_aff *ma2, int i,
+ __isl_give isl_set *(*cmp)(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2))
+{
+ isl_aff *aff1, *aff2;
+
+ aff1 = isl_multi_aff_get_at(ma1, i);
+ aff2 = isl_multi_aff_get_at(ma2, i);
+ return cmp(aff1, aff2);
+}
+
+/* Return the set of domain elements where "ma1" is lexicographically
+ * greater than to "ma2". If "equal" is set, then include the domain
+ * elements where they are equal.
+ *
+ * In particular, for all but the final entry,
+ * include the set of elements where this entry is strictly greater in "ma1"
+ * and all previous entries are equal.
+ * The final entry is also allowed to be equal in the two functions
+ * if "equal" is set.
+ *
+ * The case where there are no entries is handled separately.
+ */
+static __isl_give isl_set *isl_multi_aff_lex_gte_set(
+ __isl_take isl_multi_aff *ma1, __isl_take isl_multi_aff *ma2, int equal)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_set *res;
+ isl_set *equal_set;
+ isl_set *gte;
+
+ if (isl_multi_aff_check_equal_space(ma1, ma2) < 0)
+ goto error;
+ n = isl_multi_aff_size(ma1);
+ if (n < 0)
+ goto error;
+ if (n == 0)
+ return isl_multi_aff_lex_gte_set_0d(ma1, ma2, equal);
+
+ space = isl_multi_aff_get_domain_space(ma1);
+ res = isl_set_empty(isl_space_copy(space));
+ equal_set = isl_set_universe(space);
+
+ for (i = 0; i + 1 < n; ++i) {
+ isl_bool empty;
+ isl_set *gt, *eq;
+
+ gt = isl_multi_aff_order_at(ma1, ma2, i, &isl_aff_gt_set);
+ gt = isl_set_intersect(gt, isl_set_copy(equal_set));
+ res = isl_set_union(res, gt);
+ eq = isl_multi_aff_order_at(ma1, ma2, i, &isl_aff_eq_set);
+ equal_set = isl_set_intersect(equal_set, eq);
+
+ empty = isl_set_is_empty(equal_set);
+ if (empty >= 0 && empty)
+ break;
+ }
+
+ if (equal)
+ gte = isl_multi_aff_order_at(ma1, ma2, n - 1, &isl_aff_ge_set);
+ else
+ gte = isl_multi_aff_order_at(ma1, ma2, n - 1, &isl_aff_gt_set);
+ isl_multi_aff_free(ma1);
+ isl_multi_aff_free(ma2);
+
+ gte = isl_set_intersect(gte, equal_set);
+ return isl_set_union(res, gte);
+error:
+ isl_multi_aff_free(ma1);
+ isl_multi_aff_free(ma2);
+ return NULL;
+}
+
+/* Return the set of domain elements where "ma1" is lexicographically
+ * greater than or equal to "ma2".
+ */
+__isl_give isl_set *isl_multi_aff_lex_ge_set(__isl_take isl_multi_aff *ma1,
+ __isl_take isl_multi_aff *ma2)
+{
+ return isl_multi_aff_lex_gte_set(ma1, ma2, 1);
+}
+
+/* Return the set of domain elements where "ma1" is lexicographically
+ * greater than "ma2".
+ */
+__isl_give isl_set *isl_multi_aff_lex_gt_set(__isl_take isl_multi_aff *ma1,
+ __isl_take isl_multi_aff *ma2)
+{
+ return isl_multi_aff_lex_gte_set(ma1, ma2, 0);
+}
+
+#define isl_multi_aff_zero_in_space isl_multi_aff_zero
+
+#undef PW
+#define PW isl_pw_multi_aff
+#undef BASE
+#define BASE multi_aff
+#undef EL_IS_ZERO
+#define EL_IS_ZERO is_empty
+#undef ZERO
+#define ZERO empty
+#undef IS_ZERO
+#define IS_ZERO is_empty
+#undef FIELD
+#define FIELD maff
+#undef DEFAULT_IS_ZERO
+#define DEFAULT_IS_ZERO 0
+
+#include <isl_pw_templ.c>
+#include <isl_pw_add_constant_multi_val_templ.c>
+#include <isl_pw_add_constant_val_templ.c>
+#include <isl_pw_bind_domain_templ.c>
+#include <isl_pw_insert_dims_templ.c>
+#include <isl_pw_insert_domain_templ.c>
+#include <isl_pw_locals_templ.c>
+#include <isl_pw_move_dims_templ.c>
+#include <isl_pw_neg_templ.c>
+#include <isl_pw_pullback_templ.c>
+#include <isl_pw_range_tuple_id_templ.c>
+#include <isl_pw_union_opt.c>
+
+#undef BASE
+#define BASE pw_multi_aff
+
+#include <isl_union_multi.c>
+#include "isl_union_locals_templ.c"
+#include <isl_union_neg.c>
+
+#undef BASE
+#define BASE multi_aff
+
+#include <isl_union_pw_templ.c>
+
+/* Generic function for extracting a factor from a product "pma".
+ * "check_space" checks that the space is that of the right kind of product.
+ * "space_factor" extracts the factor from the space.
+ * "multi_aff_factor" extracts the factor from the constituent functions.
+ */
+static __isl_give isl_pw_multi_aff *pw_multi_aff_factor(
+ __isl_take isl_pw_multi_aff *pma,
+ isl_stat (*check_space)(__isl_keep isl_pw_multi_aff *pma),
+ __isl_give isl_space *(*space_factor)(__isl_take isl_space *space),
+ __isl_give isl_multi_aff *(*multi_aff_factor)(
+ __isl_take isl_multi_aff *ma))
+{
+ int i;
+ isl_space *space;
+
+ if (check_space(pma) < 0)
+ return isl_pw_multi_aff_free(pma);
+
+ space = isl_pw_multi_aff_take_space(pma);
+ space = space_factor(space);
+
+ for (i = 0; pma && i < pma->n; ++i) {
+ isl_multi_aff *ma;
+
+ ma = isl_pw_multi_aff_take_base_at(pma, i);
+ ma = multi_aff_factor(ma);
+ pma = isl_pw_multi_aff_restore_base_at(pma, i, ma);
+ }
+
+ pma = isl_pw_multi_aff_restore_space(pma, space);
+
+ return pma;
+}
+
+/* Is the range of "pma" a wrapped relation?
+ */
+static isl_bool isl_pw_multi_aff_range_is_wrapping(
+ __isl_keep isl_pw_multi_aff *pma)
+{
+ return isl_space_range_is_wrapping(isl_pw_multi_aff_peek_space(pma));
+}
+
+/* Check that the range of "pma" is a product.
+ */
+static isl_stat pw_multi_aff_check_range_product(
+ __isl_keep isl_pw_multi_aff *pma)
+{
+ isl_bool wraps;
+
+ wraps = isl_pw_multi_aff_range_is_wrapping(pma);
+ if (wraps < 0)
+ return isl_stat_error;
+ if (!wraps)
+ isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid,
+ "range is not a product", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Given a function A -> [B -> C], extract the function A -> B.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_factor_domain(
+ __isl_take isl_pw_multi_aff *pma)
+{
+ return pw_multi_aff_factor(pma, &pw_multi_aff_check_range_product,
+ &isl_space_range_factor_domain,
+ &isl_multi_aff_range_factor_domain);
+}
+
+/* Given a function A -> [B -> C], extract the function A -> C.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_factor_range(
+ __isl_take isl_pw_multi_aff *pma)
+{
+ return pw_multi_aff_factor(pma, &pw_multi_aff_check_range_product,
+ &isl_space_range_factor_range,
+ &isl_multi_aff_range_factor_range);
+}
+
+/* Given two piecewise multi affine expressions, return a piecewise
+ * multi-affine expression defined on the union of the definition domains
+ * of the inputs that is equal to the lexicographic maximum of the two
+ * inputs on each cell. If only one of the two inputs is defined on
+ * a given cell, then it is considered to be the maximum.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_lexmax(
+ __isl_take isl_pw_multi_aff *pma1,
+ __isl_take isl_pw_multi_aff *pma2)
+{
+ isl_pw_multi_aff_align_params_bin(&pma1, &pma2);
+ return isl_pw_multi_aff_union_opt_cmp(pma1, pma2,
+ &isl_multi_aff_lex_ge_set);
+}
+
+/* Given two piecewise multi affine expressions, return a piecewise
+ * multi-affine expression defined on the union of the definition domains
+ * of the inputs that is equal to the lexicographic minimum of the two
+ * inputs on each cell. If only one of the two inputs is defined on
+ * a given cell, then it is considered to be the minimum.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_lexmin(
+ __isl_take isl_pw_multi_aff *pma1,
+ __isl_take isl_pw_multi_aff *pma2)
+{
+ isl_pw_multi_aff_align_params_bin(&pma1, &pma2);
+ return isl_pw_multi_aff_union_opt_cmp(pma1, pma2,
+ &isl_multi_aff_lex_le_set);
+}
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_add(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2)
+{
+ isl_pw_multi_aff_align_params_bin(&pma1, &pma2);
+ return isl_pw_multi_aff_on_shared_domain(pma1, pma2,
+ &isl_multi_aff_add);
+}
+
+/* Subtract "pma2" from "pma1" and return the result.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_sub(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2)
+{
+ isl_pw_multi_aff_align_params_bin(&pma1, &pma2);
+ return isl_pw_multi_aff_on_shared_domain(pma1, pma2,
+ &isl_multi_aff_sub);
+}
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_union_add(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2)
+{
+ return isl_pw_multi_aff_union_add_(pma1, pma2);
+}
+
+/* Compute the sum of "upa1" and "upa2" on the union of their domains,
+ * with the actual sum on the shared domain and
+ * the defined expression on the symmetric difference of the domains.
+ */
+__isl_give isl_union_pw_aff *isl_union_pw_aff_union_add(
+ __isl_take isl_union_pw_aff *upa1, __isl_take isl_union_pw_aff *upa2)
+{
+ return isl_union_pw_aff_union_add_(upa1, upa2);
+}
+
+/* Compute the sum of "upma1" and "upma2" on the union of their domains,
+ * with the actual sum on the shared domain and
+ * the defined expression on the symmetric difference of the domains.
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_union_add(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2)
+{
+ return isl_union_pw_multi_aff_union_add_(upma1, upma2);
+}
+
+/* Given two piecewise multi-affine expressions A -> B and C -> D,
+ * construct a piecewise multi-affine expression [A -> C] -> [B -> D].
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_product(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2)
+{
+ int i, j, n;
+ isl_space *space;
+ isl_pw_multi_aff *res;
+
+ if (isl_pw_multi_aff_align_params_bin(&pma1, &pma2) < 0)
+ goto error;
+
+ n = pma1->n * pma2->n;
+ space = isl_space_product(isl_space_copy(pma1->dim),
+ isl_space_copy(pma2->dim));
+ res = isl_pw_multi_aff_alloc_size(space, n);
+
+ for (i = 0; i < pma1->n; ++i) {
+ for (j = 0; j < pma2->n; ++j) {
+ isl_set *domain;
+ isl_multi_aff *ma;
+
+ domain = isl_set_product(isl_set_copy(pma1->p[i].set),
+ isl_set_copy(pma2->p[j].set));
+ ma = isl_multi_aff_product(
+ isl_multi_aff_copy(pma1->p[i].maff),
+ isl_multi_aff_copy(pma2->p[j].maff));
+ res = isl_pw_multi_aff_add_piece(res, domain, ma);
+ }
+ }
+
+ isl_pw_multi_aff_free(pma1);
+ isl_pw_multi_aff_free(pma2);
+ return res;
+error:
+ isl_pw_multi_aff_free(pma1);
+ isl_pw_multi_aff_free(pma2);
+ return NULL;
+}
+
+/* Subtract the initial "n" elements in "ma" with coefficients in "c" and
+ * denominator "denom".
+ * "denom" is allowed to be negative, in which case the actual denominator
+ * is -denom and the expressions are added instead.
+ */
+static __isl_give isl_aff *subtract_initial(__isl_take isl_aff *aff,
+ __isl_keep isl_multi_aff *ma, int n, isl_int *c, isl_int denom)
+{
+ int i, first;
+ int sign;
+ isl_int d;
+
+ first = isl_seq_first_non_zero(c, n);
+ if (first == -1)
+ return aff;
+
+ sign = isl_int_sgn(denom);
+ isl_int_init(d);
+ isl_int_abs(d, denom);
+ for (i = first; i < n; ++i) {
+ isl_aff *aff_i;
+
+ if (isl_int_is_zero(c[i]))
+ continue;
+ aff_i = isl_multi_aff_get_aff(ma, i);
+ aff_i = isl_aff_scale(aff_i, c[i]);
+ aff_i = isl_aff_scale_down(aff_i, d);
+ if (sign >= 0)
+ aff = isl_aff_sub(aff, aff_i);
+ else
+ aff = isl_aff_add(aff, aff_i);
+ }
+ isl_int_clear(d);
+
+ return aff;
+}
+
+/* Extract an affine expression that expresses the output dimension "pos"
+ * of "bmap" in terms of the parameters and input dimensions from
+ * equality "eq".
+ * Note that this expression may involve integer divisions defined
+ * in terms of parameters and input dimensions.
+ * The equality may also involve references to earlier (but not later)
+ * output dimensions. These are replaced by the corresponding elements
+ * in "ma".
+ *
+ * If the equality is of the form
+ *
+ * f(i) + h(j) + a x + g(i) = 0,
+ *
+ * with f(i) a linear combinations of the parameters and input dimensions,
+ * g(i) a linear combination of integer divisions defined in terms of the same
+ * and h(j) a linear combinations of earlier output dimensions,
+ * then the affine expression is
+ *
+ * (-f(i) - g(i))/a - h(j)/a
+ *
+ * If the equality is of the form
+ *
+ * f(i) + h(j) - a x + g(i) = 0,
+ *
+ * then the affine expression is
+ *
+ * (f(i) + g(i))/a - h(j)/(-a)
+ *
+ *
+ * If "div" refers to an integer division (i.e., it is smaller than
+ * the number of integer divisions), then the equality constraint
+ * does involve an integer division (the one at position "div") that
+ * is defined in terms of output dimensions. However, this integer
+ * division can be eliminated by exploiting a pair of constraints
+ * x >= l and x <= l + n, with n smaller than the coefficient of "div"
+ * in the equality constraint. "ineq" refers to inequality x >= l, i.e.,
+ * -l + x >= 0.
+ * In particular, let
+ *
+ * x = e(i) + m floor(...)
+ *
+ * with e(i) the expression derived above and floor(...) the integer
+ * division involving output dimensions.
+ * From
+ *
+ * l <= x <= l + n,
+ *
+ * we have
+ *
+ * 0 <= x - l <= n
+ *
+ * This means
+ *
+ * e(i) + m floor(...) - l = (e(i) + m floor(...) - l) mod m
+ * = (e(i) - l) mod m
+ *
+ * Therefore,
+ *
+ * x - l = (e(i) - l) mod m
+ *
+ * or
+ *
+ * x = ((e(i) - l) mod m) + l
+ *
+ * The variable "shift" below contains the expression -l, which may
+ * also involve a linear combination of earlier output dimensions.
+ */
+static __isl_give isl_aff *extract_aff_from_equality(
+ __isl_keep isl_basic_map *bmap, int pos, int eq, int div, int ineq,
+ __isl_keep isl_multi_aff *ma)
+{
+ unsigned o_out;
+ isl_size n_div, n_out;
+ isl_ctx *ctx;
+ isl_local_space *ls;
+ isl_aff *aff, *shift;
+ isl_val *mod;
+
+ ctx = isl_basic_map_get_ctx(bmap);
+ ls = isl_basic_map_get_local_space(bmap);
+ ls = isl_local_space_domain(ls);
+ aff = isl_aff_alloc(isl_local_space_copy(ls));
+ if (!aff)
+ goto error;
+ o_out = isl_basic_map_offset(bmap, isl_dim_out);
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_out < 0 || n_div < 0)
+ goto error;
+ if (isl_int_is_neg(bmap->eq[eq][o_out + pos])) {
+ isl_seq_cpy(aff->v->el + 1, bmap->eq[eq], o_out);
+ isl_seq_cpy(aff->v->el + 1 + o_out,
+ bmap->eq[eq] + o_out + n_out, n_div);
+ } else {
+ isl_seq_neg(aff->v->el + 1, bmap->eq[eq], o_out);
+ isl_seq_neg(aff->v->el + 1 + o_out,
+ bmap->eq[eq] + o_out + n_out, n_div);
+ }
+ if (div < n_div)
+ isl_int_set_si(aff->v->el[1 + o_out + div], 0);
+ isl_int_abs(aff->v->el[0], bmap->eq[eq][o_out + pos]);
+ aff = subtract_initial(aff, ma, pos, bmap->eq[eq] + o_out,
+ bmap->eq[eq][o_out + pos]);
+ if (div < n_div) {
+ shift = isl_aff_alloc(isl_local_space_copy(ls));
+ if (!shift)
+ goto error;
+ isl_seq_cpy(shift->v->el + 1, bmap->ineq[ineq], o_out);
+ isl_seq_cpy(shift->v->el + 1 + o_out,
+ bmap->ineq[ineq] + o_out + n_out, n_div);
+ isl_int_set_si(shift->v->el[0], 1);
+ shift = subtract_initial(shift, ma, pos,
+ bmap->ineq[ineq] + o_out, ctx->negone);
+ aff = isl_aff_add(aff, isl_aff_copy(shift));
+ mod = isl_val_int_from_isl_int(ctx,
+ bmap->eq[eq][o_out + n_out + div]);
+ mod = isl_val_abs(mod);
+ aff = isl_aff_mod_val(aff, mod);
+ aff = isl_aff_sub(aff, shift);
+ }
+
+ isl_local_space_free(ls);
+ return aff;
+error:
+ isl_local_space_free(ls);
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Given a basic map with output dimensions defined
+ * in terms of the parameters input dimensions and earlier
+ * output dimensions using an equality (and possibly a pair on inequalities),
+ * extract an isl_aff that expresses output dimension "pos" in terms
+ * of the parameters and input dimensions.
+ * Note that this expression may involve integer divisions defined
+ * in terms of parameters and input dimensions.
+ * "ma" contains the expressions corresponding to earlier output dimensions.
+ *
+ * This function shares some similarities with
+ * isl_basic_map_has_defining_equality and isl_constraint_get_bound.
+ */
+static __isl_give isl_aff *extract_isl_aff_from_basic_map(
+ __isl_keep isl_basic_map *bmap, int pos, __isl_keep isl_multi_aff *ma)
+{
+ int eq, div, ineq;
+ isl_aff *aff;
+
+ if (!bmap)
+ return NULL;
+ eq = isl_basic_map_output_defining_equality(bmap, pos, &div, &ineq);
+ if (eq >= bmap->n_eq)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "unable to find suitable equality", return NULL);
+ aff = extract_aff_from_equality(bmap, pos, eq, div, ineq, ma);
+
+ aff = isl_aff_remove_unused_divs(aff);
+ return aff;
+}
+
+/* Given a basic map where each output dimension is defined
+ * in terms of the parameters and input dimensions using an equality,
+ * extract an isl_multi_aff that expresses the output dimensions in terms
+ * of the parameters and input dimensions.
+ */
+static __isl_give isl_multi_aff *extract_isl_multi_aff_from_basic_map(
+ __isl_take isl_basic_map *bmap)
+{
+ int i;
+ isl_size n_out;
+ isl_multi_aff *ma;
+
+ if (!bmap)
+ return NULL;
+
+ ma = isl_multi_aff_alloc(isl_basic_map_get_space(bmap));
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ if (n_out < 0)
+ ma = isl_multi_aff_free(ma);
+
+ for (i = 0; i < n_out; ++i) {
+ isl_aff *aff;
+
+ aff = extract_isl_aff_from_basic_map(bmap, i, ma);
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ }
+
+ isl_basic_map_free(bmap);
+
+ return ma;
+}
+
+/* Given a basic set where each set dimension is defined
+ * in terms of the parameters using an equality,
+ * extract an isl_multi_aff that expresses the set dimensions in terms
+ * of the parameters.
+ */
+__isl_give isl_multi_aff *isl_multi_aff_from_basic_set_equalities(
+ __isl_take isl_basic_set *bset)
+{
+ return extract_isl_multi_aff_from_basic_map(bset);
+}
+
+/* Create an isl_pw_multi_aff that is equivalent to
+ * isl_map_intersect_domain(isl_map_from_basic_map(bmap), domain).
+ * The given basic map is such that each output dimension is defined
+ * in terms of the parameters and input dimensions using an equality.
+ *
+ * Since some applications expect the result of isl_pw_multi_aff_from_map
+ * to only contain integer affine expressions, we compute the floor
+ * of the expression before returning.
+ *
+ * Remove all constraints involving local variables without
+ * an explicit representation (resulting in the removal of those
+ * local variables) prior to the actual extraction to ensure
+ * that the local spaces in which the resulting affine expressions
+ * are created do not contain any unknown local variables.
+ * Removing such constraints is safe because constraints involving
+ * unknown local variables are not used to determine whether
+ * a basic map is obviously single-valued.
+ */
+static __isl_give isl_pw_multi_aff *plain_pw_multi_aff_from_map(
+ __isl_take isl_set *domain, __isl_take isl_basic_map *bmap)
+{
+ isl_multi_aff *ma;
+
+ bmap = isl_basic_map_drop_constraints_involving_unknown_divs(bmap);
+ ma = extract_isl_multi_aff_from_basic_map(bmap);
+ ma = isl_multi_aff_floor(ma);
+ return isl_pw_multi_aff_alloc(domain, ma);
+}
+
+/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map.
+ * This obviously only works if the input "map" is single-valued.
+ * If so, we compute the lexicographic minimum of the image in the form
+ * of an isl_pw_multi_aff. Since the image is unique, it is equal
+ * to its lexicographic minimum.
+ * If the input is not single-valued, we produce an error.
+ */
+static __isl_give isl_pw_multi_aff *pw_multi_aff_from_map_base(
+ __isl_take isl_map *map)
+{
+ int i;
+ int sv;
+ isl_pw_multi_aff *pma;
+
+ sv = isl_map_is_single_valued(map);
+ if (sv < 0)
+ goto error;
+ if (!sv)
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "map is not single-valued", goto error);
+ map = isl_map_make_disjoint(map);
+ if (!map)
+ return NULL;
+
+ pma = isl_pw_multi_aff_empty(isl_map_get_space(map));
+
+ for (i = 0; i < map->n; ++i) {
+ isl_pw_multi_aff *pma_i;
+ isl_basic_map *bmap;
+ bmap = isl_basic_map_copy(map->p[i]);
+ pma_i = isl_basic_map_lexmin_pw_multi_aff(bmap);
+ pma = isl_pw_multi_aff_add_disjoint(pma, pma_i);
+ }
+
+ isl_map_free(map);
+ return pma;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map,
+ * taking into account that the output dimension at position "d"
+ * can be represented as
+ *
+ * x = floor((e(...) + c1) / m)
+ *
+ * given that constraint "i" is of the form
+ *
+ * e(...) + c1 - m x >= 0
+ *
+ *
+ * Let "map" be of the form
+ *
+ * A -> B
+ *
+ * We construct a mapping
+ *
+ * A -> [A -> x = floor(...)]
+ *
+ * apply that to the map, obtaining
+ *
+ * [A -> x = floor(...)] -> B
+ *
+ * and equate dimension "d" to x.
+ * We then compute a isl_pw_multi_aff representation of the resulting map
+ * and plug in the mapping above.
+ */
+static __isl_give isl_pw_multi_aff *pw_multi_aff_from_map_div(
+ __isl_take isl_map *map, __isl_take isl_basic_map *hull, int d, int i)
+{
+ isl_ctx *ctx;
+ isl_space *space = NULL;
+ isl_local_space *ls;
+ isl_multi_aff *ma;
+ isl_aff *aff;
+ isl_vec *v;
+ isl_map *insert;
+ int offset;
+ isl_size n;
+ isl_size n_in;
+ isl_pw_multi_aff *pma;
+ isl_bool is_set;
+
+ is_set = isl_map_is_set(map);
+ if (is_set < 0)
+ goto error;
+
+ offset = isl_basic_map_offset(hull, isl_dim_out);
+ ctx = isl_map_get_ctx(map);
+ space = isl_space_domain(isl_map_get_space(map));
+ n_in = isl_space_dim(space, isl_dim_set);
+ n = isl_space_dim(space, isl_dim_all);
+ if (n_in < 0 || n < 0)
+ goto error;
+
+ v = isl_vec_alloc(ctx, 1 + 1 + n);
+ if (v) {
+ isl_int_neg(v->el[0], hull->ineq[i][offset + d]);
+ isl_seq_cpy(v->el + 1, hull->ineq[i], 1 + n);
+ }
+ isl_basic_map_free(hull);
+
+ ls = isl_local_space_from_space(isl_space_copy(space));
+ aff = isl_aff_alloc_vec(ls, v);
+ aff = isl_aff_floor(aff);
+ if (is_set) {
+ isl_space_free(space);
+ ma = isl_multi_aff_from_aff(aff);
+ } else {
+ ma = isl_multi_aff_identity(isl_space_map_from_set(space));
+ ma = isl_multi_aff_range_product(ma,
+ isl_multi_aff_from_aff(aff));
+ }
+
+ insert = isl_map_from_multi_aff_internal(isl_multi_aff_copy(ma));
+ map = isl_map_apply_domain(map, insert);
+ map = isl_map_equate(map, isl_dim_in, n_in, isl_dim_out, d);
+ pma = isl_pw_multi_aff_from_map(map);
+ pma = isl_pw_multi_aff_pullback_multi_aff(pma, ma);
+
+ return pma;
+error:
+ isl_space_free(space);
+ isl_map_free(map);
+ isl_basic_map_free(hull);
+ return NULL;
+}
+
+/* Is constraint "c" of the form
+ *
+ * e(...) + c1 - m x >= 0
+ *
+ * or
+ *
+ * -e(...) + c2 + m x >= 0
+ *
+ * where m > 1 and e only depends on parameters and input dimensions?
+ *
+ * "offset" is the offset of the output dimensions
+ * "pos" is the position of output dimension x.
+ */
+static int is_potential_div_constraint(isl_int *c, int offset, int d, int total)
+{
+ if (isl_int_is_zero(c[offset + d]))
+ return 0;
+ if (isl_int_is_one(c[offset + d]))
+ return 0;
+ if (isl_int_is_negone(c[offset + d]))
+ return 0;
+ if (isl_seq_first_non_zero(c + offset, d) != -1)
+ return 0;
+ if (isl_seq_first_non_zero(c + offset + d + 1,
+ total - (offset + d + 1)) != -1)
+ return 0;
+ return 1;
+}
+
+/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map.
+ *
+ * As a special case, we first check if there is any pair of constraints,
+ * shared by all the basic maps in "map" that force a given dimension
+ * to be equal to the floor of some affine combination of the input dimensions.
+ *
+ * In particular, if we can find two constraints
+ *
+ * e(...) + c1 - m x >= 0 i.e., m x <= e(...) + c1
+ *
+ * and
+ *
+ * -e(...) + c2 + m x >= 0 i.e., m x >= e(...) - c2
+ *
+ * where m > 1 and e only depends on parameters and input dimensions,
+ * and such that
+ *
+ * c1 + c2 < m i.e., -c2 >= c1 - (m - 1)
+ *
+ * then we know that we can take
+ *
+ * x = floor((e(...) + c1) / m)
+ *
+ * without having to perform any computation.
+ *
+ * Note that we know that
+ *
+ * c1 + c2 >= 1
+ *
+ * If c1 + c2 were 0, then we would have detected an equality during
+ * simplification. If c1 + c2 were negative, then we would have detected
+ * a contradiction.
+ */
+static __isl_give isl_pw_multi_aff *pw_multi_aff_from_map_check_div(
+ __isl_take isl_map *map)
+{
+ int d;
+ isl_size dim;
+ int i, j, n;
+ int offset;
+ isl_size total;
+ isl_int sum;
+ isl_basic_map *hull;
+
+ hull = isl_map_unshifted_simple_hull(isl_map_copy(map));
+ dim = isl_map_dim(map, isl_dim_out);
+ total = isl_basic_map_dim(hull, isl_dim_all);
+ if (dim < 0 || total < 0)
+ goto error;
+
+ isl_int_init(sum);
+ offset = isl_basic_map_offset(hull, isl_dim_out);
+ n = hull->n_ineq;
+ for (d = 0; d < dim; ++d) {
+ for (i = 0; i < n; ++i) {
+ if (!is_potential_div_constraint(hull->ineq[i],
+ offset, d, 1 + total))
+ continue;
+ for (j = i + 1; j < n; ++j) {
+ if (!isl_seq_is_neg(hull->ineq[i] + 1,
+ hull->ineq[j] + 1, total))
+ continue;
+ isl_int_add(sum, hull->ineq[i][0],
+ hull->ineq[j][0]);
+ if (isl_int_abs_lt(sum,
+ hull->ineq[i][offset + d]))
+ break;
+
+ }
+ if (j >= n)
+ continue;
+ isl_int_clear(sum);
+ if (isl_int_is_pos(hull->ineq[j][offset + d]))
+ j = i;
+ return pw_multi_aff_from_map_div(map, hull, d, j);
+ }
+ }
+ isl_int_clear(sum);
+ isl_basic_map_free(hull);
+ return pw_multi_aff_from_map_base(map);
+error:
+ isl_map_free(map);
+ isl_basic_map_free(hull);
+ return NULL;
+}
+
+/* Given an affine expression
+ *
+ * [A -> B] -> f(A,B)
+ *
+ * construct an isl_multi_aff
+ *
+ * [A -> B] -> B'
+ *
+ * such that dimension "d" in B' is set to "aff" and the remaining
+ * dimensions are set equal to the corresponding dimensions in B.
+ * "n_in" is the dimension of the space A.
+ * "n_out" is the dimension of the space B.
+ *
+ * If "is_set" is set, then the affine expression is of the form
+ *
+ * [B] -> f(B)
+ *
+ * and we construct an isl_multi_aff
+ *
+ * B -> B'
+ */
+static __isl_give isl_multi_aff *range_map(__isl_take isl_aff *aff, int d,
+ unsigned n_in, unsigned n_out, int is_set)
+{
+ int i;
+ isl_multi_aff *ma;
+ isl_space *space, *space2;
+ isl_local_space *ls;
+
+ space = isl_aff_get_domain_space(aff);
+ ls = isl_local_space_from_space(isl_space_copy(space));
+ space2 = isl_space_copy(space);
+ if (!is_set)
+ space2 = isl_space_range(isl_space_unwrap(space2));
+ space = isl_space_map_from_domain_and_range(space, space2);
+ ma = isl_multi_aff_alloc(space);
+ ma = isl_multi_aff_set_aff(ma, d, aff);
+
+ for (i = 0; i < n_out; ++i) {
+ if (i == d)
+ continue;
+ aff = isl_aff_var_on_domain(isl_local_space_copy(ls),
+ isl_dim_set, n_in + i);
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ }
+
+ isl_local_space_free(ls);
+
+ return ma;
+}
+
+/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map,
+ * taking into account that the dimension at position "d" can be written as
+ *
+ * x = m a + f(..) (1)
+ *
+ * where m is equal to "gcd".
+ * "i" is the index of the equality in "hull" that defines f(..).
+ * In particular, the equality is of the form
+ *
+ * f(..) - x + m g(existentials) = 0
+ *
+ * or
+ *
+ * -f(..) + x + m g(existentials) = 0
+ *
+ * We basically plug (1) into "map", resulting in a map with "a"
+ * in the range instead of "x". The corresponding isl_pw_multi_aff
+ * defining "a" is then plugged back into (1) to obtain a definition for "x".
+ *
+ * Specifically, given the input map
+ *
+ * A -> B
+ *
+ * We first wrap it into a set
+ *
+ * [A -> B]
+ *
+ * and define (1) on top of the corresponding space, resulting in "aff".
+ * We use this to create an isl_multi_aff that maps the output position "d"
+ * from "a" to "x", leaving all other (intput and output) dimensions unchanged.
+ * We plug this into the wrapped map, unwrap the result and compute the
+ * corresponding isl_pw_multi_aff.
+ * The result is an expression
+ *
+ * A -> T(A)
+ *
+ * We adjust that to
+ *
+ * A -> [A -> T(A)]
+ *
+ * so that we can plug that into "aff", after extending the latter to
+ * a mapping
+ *
+ * [A -> B] -> B'
+ *
+ *
+ * If "map" is actually a set, then there is no "A" space, meaning
+ * that we do not need to perform any wrapping, and that the result
+ * of the recursive call is of the form
+ *
+ * [T]
+ *
+ * which is plugged into a mapping of the form
+ *
+ * B -> B'
+ */
+static __isl_give isl_pw_multi_aff *pw_multi_aff_from_map_stride(
+ __isl_take isl_map *map, __isl_take isl_basic_map *hull, int d, int i,
+ isl_int gcd)
+{
+ isl_set *set;
+ isl_space *space;
+ isl_local_space *ls;
+ isl_aff *aff;
+ isl_multi_aff *ma;
+ isl_pw_multi_aff *pma, *id;
+ isl_size n_in;
+ unsigned o_out;
+ isl_size n_out;
+ isl_bool is_set;
+
+ is_set = isl_map_is_set(map);
+ if (is_set < 0)
+ goto error;
+
+ n_in = isl_basic_map_dim(hull, isl_dim_in);
+ n_out = isl_basic_map_dim(hull, isl_dim_out);
+ if (n_in < 0 || n_out < 0)
+ goto error;
+ o_out = isl_basic_map_offset(hull, isl_dim_out);
+
+ if (is_set)
+ set = map;
+ else
+ set = isl_map_wrap(map);
+ space = isl_space_map_from_set(isl_set_get_space(set));
+ ma = isl_multi_aff_identity(space);
+ ls = isl_local_space_from_space(isl_set_get_space(set));
+ aff = isl_aff_alloc(ls);
+ if (aff) {
+ isl_int_set_si(aff->v->el[0], 1);
+ if (isl_int_is_one(hull->eq[i][o_out + d]))
+ isl_seq_neg(aff->v->el + 1, hull->eq[i],
+ aff->v->size - 1);
+ else
+ isl_seq_cpy(aff->v->el + 1, hull->eq[i],
+ aff->v->size - 1);
+ isl_int_set(aff->v->el[1 + o_out + d], gcd);
+ }
+ ma = isl_multi_aff_set_aff(ma, n_in + d, isl_aff_copy(aff));
+ set = isl_set_preimage_multi_aff(set, ma);
+
+ ma = range_map(aff, d, n_in, n_out, is_set);
+
+ if (is_set)
+ map = set;
+ else
+ map = isl_set_unwrap(set);
+ pma = isl_pw_multi_aff_from_map(map);
+
+ if (!is_set) {
+ space = isl_pw_multi_aff_get_domain_space(pma);
+ space = isl_space_map_from_set(space);
+ id = isl_pw_multi_aff_identity(space);
+ pma = isl_pw_multi_aff_range_product(id, pma);
+ }
+ id = isl_pw_multi_aff_from_multi_aff(ma);
+ pma = isl_pw_multi_aff_pullback_pw_multi_aff(id, pma);
+
+ isl_basic_map_free(hull);
+ return pma;
+error:
+ isl_map_free(map);
+ isl_basic_map_free(hull);
+ return NULL;
+}
+
+/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map.
+ * "hull" contains the equalities valid for "map".
+ *
+ * Check if any of the output dimensions is "strided".
+ * That is, we check if it can be written as
+ *
+ * x = m a + f(..)
+ *
+ * with m greater than 1, a some combination of existentially quantified
+ * variables and f an expression in the parameters and input dimensions.
+ * If so, we remove the stride in pw_multi_aff_from_map_stride.
+ *
+ * Otherwise, we continue with pw_multi_aff_from_map_check_div for a further
+ * special case.
+ */
+static __isl_give isl_pw_multi_aff *pw_multi_aff_from_map_check_strides(
+ __isl_take isl_map *map, __isl_take isl_basic_map *hull)
+{
+ int i, j;
+ isl_size n_out;
+ unsigned o_out;
+ isl_size n_div;
+ unsigned o_div;
+ isl_int gcd;
+
+ n_div = isl_basic_map_dim(hull, isl_dim_div);
+ n_out = isl_basic_map_dim(hull, isl_dim_out);
+ if (n_div < 0 || n_out < 0)
+ goto error;
+
+ if (n_div == 0) {
+ isl_basic_map_free(hull);
+ return pw_multi_aff_from_map_check_div(map);
+ }
+
+ isl_int_init(gcd);
+
+ o_div = isl_basic_map_offset(hull, isl_dim_div);
+ o_out = isl_basic_map_offset(hull, isl_dim_out);
+
+ for (i = 0; i < n_out; ++i) {
+ for (j = 0; j < hull->n_eq; ++j) {
+ isl_int *eq = hull->eq[j];
+ isl_pw_multi_aff *res;
+
+ if (!isl_int_is_one(eq[o_out + i]) &&
+ !isl_int_is_negone(eq[o_out + i]))
+ continue;
+ if (isl_seq_first_non_zero(eq + o_out, i) != -1)
+ continue;
+ if (isl_seq_first_non_zero(eq + o_out + i + 1,
+ n_out - (i + 1)) != -1)
+ continue;
+ isl_seq_gcd(eq + o_div, n_div, &gcd);
+ if (isl_int_is_zero(gcd))
+ continue;
+ if (isl_int_is_one(gcd))
+ continue;
+
+ res = pw_multi_aff_from_map_stride(map, hull,
+ i, j, gcd);
+ isl_int_clear(gcd);
+ return res;
+ }
+ }
+
+ isl_int_clear(gcd);
+ isl_basic_map_free(hull);
+ return pw_multi_aff_from_map_check_div(map);
+error:
+ isl_map_free(map);
+ isl_basic_map_free(hull);
+ return NULL;
+}
+
+/* Try and create an isl_pw_multi_aff that is equivalent to the given isl_map.
+ *
+ * As a special case, we first check if all output dimensions are uniquely
+ * defined in terms of the parameters and input dimensions over the entire
+ * domain. If so, we extract the desired isl_pw_multi_aff directly
+ * from the affine hull of "map" and its domain.
+ *
+ * Otherwise, continue with pw_multi_aff_from_map_check_strides for more
+ * special cases.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_map(__isl_take isl_map *map)
+{
+ isl_bool sv;
+ isl_size n;
+ isl_basic_map *hull;
+
+ n = isl_map_n_basic_map(map);
+ if (n < 0)
+ goto error;
+
+ if (n == 1) {
+ hull = isl_map_unshifted_simple_hull(isl_map_copy(map));
+ hull = isl_basic_map_plain_affine_hull(hull);
+ sv = isl_basic_map_plain_is_single_valued(hull);
+ if (sv >= 0 && sv)
+ return plain_pw_multi_aff_from_map(isl_map_domain(map),
+ hull);
+ isl_basic_map_free(hull);
+ }
+ map = isl_map_detect_equalities(map);
+ hull = isl_map_unshifted_simple_hull(isl_map_copy(map));
+ sv = isl_basic_map_plain_is_single_valued(hull);
+ if (sv >= 0 && sv)
+ return plain_pw_multi_aff_from_map(isl_map_domain(map), hull);
+ if (sv >= 0)
+ return pw_multi_aff_from_map_check_strides(map, hull);
+ isl_basic_map_free(hull);
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* This function performs the same operation as isl_pw_multi_aff_from_map,
+ * but is considered as a function on an isl_map when exported.
+ */
+__isl_give isl_pw_multi_aff *isl_map_as_pw_multi_aff(__isl_take isl_map *map)
+{
+ return isl_pw_multi_aff_from_map(map);
+}
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_set(__isl_take isl_set *set)
+{
+ return isl_pw_multi_aff_from_map(set);
+}
+
+/* This function performs the same operation as isl_pw_multi_aff_from_set,
+ * but is considered as a function on an isl_set when exported.
+ */
+__isl_give isl_pw_multi_aff *isl_set_as_pw_multi_aff(__isl_take isl_set *set)
+{
+ return isl_pw_multi_aff_from_set(set);
+}
+
+/* Convert "map" into an isl_pw_multi_aff (if possible) and
+ * add it to *user.
+ */
+static isl_stat pw_multi_aff_from_map(__isl_take isl_map *map, void *user)
+{
+ isl_union_pw_multi_aff **upma = user;
+ isl_pw_multi_aff *pma;
+
+ pma = isl_pw_multi_aff_from_map(map);
+ *upma = isl_union_pw_multi_aff_add_pw_multi_aff(*upma, pma);
+
+ return *upma ? isl_stat_ok : isl_stat_error;
+}
+
+/* Create an isl_union_pw_multi_aff with the given isl_aff on a universe
+ * domain.
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_aff(
+ __isl_take isl_aff *aff)
+{
+ isl_multi_aff *ma;
+ isl_pw_multi_aff *pma;
+
+ ma = isl_multi_aff_from_aff(aff);
+ pma = isl_pw_multi_aff_from_multi_aff(ma);
+ return isl_union_pw_multi_aff_from_pw_multi_aff(pma);
+}
+
+/* Try and create an isl_union_pw_multi_aff that is equivalent
+ * to the given isl_union_map.
+ * The isl_union_map is required to be single-valued in each space.
+ * Otherwise, an error is produced.
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_map(
+ __isl_take isl_union_map *umap)
+{
+ isl_space *space;
+ isl_union_pw_multi_aff *upma;
+
+ space = isl_union_map_get_space(umap);
+ upma = isl_union_pw_multi_aff_empty(space);
+ if (isl_union_map_foreach_map(umap, &pw_multi_aff_from_map, &upma) < 0)
+ upma = isl_union_pw_multi_aff_free(upma);
+ isl_union_map_free(umap);
+
+ return upma;
+}
+
+/* This function performs the same operation as
+ * isl_union_pw_multi_aff_from_union_map,
+ * but is considered as a function on an isl_union_map when exported.
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_map_as_union_pw_multi_aff(
+ __isl_take isl_union_map *umap)
+{
+ return isl_union_pw_multi_aff_from_union_map(umap);
+}
+
+/* Try and create an isl_union_pw_multi_aff that is equivalent
+ * to the given isl_union_set.
+ * The isl_union_set is required to be a singleton in each space.
+ * Otherwise, an error is produced.
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_set(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_pw_multi_aff_from_union_map(uset);
+}
+
+/* Return the piecewise affine expression "set ? 1 : 0".
+ */
+__isl_give isl_pw_aff *isl_set_indicator_function(__isl_take isl_set *set)
+{
+ isl_pw_aff *pa;
+ isl_space *space = isl_set_get_space(set);
+ isl_local_space *ls = isl_local_space_from_space(space);
+ isl_aff *zero = isl_aff_zero_on_domain(isl_local_space_copy(ls));
+ isl_aff *one = isl_aff_zero_on_domain(ls);
+
+ one = isl_aff_add_constant_si(one, 1);
+ pa = isl_pw_aff_alloc(isl_set_copy(set), one);
+ set = isl_set_complement(set);
+ pa = isl_pw_aff_add_disjoint(pa, isl_pw_aff_alloc(set, zero));
+
+ return pa;
+}
+
+/* Plug in "subs" for dimension "type", "pos" of "aff".
+ *
+ * Let i be the dimension to replace and let "subs" be of the form
+ *
+ * f/d
+ *
+ * and "aff" of the form
+ *
+ * (a i + g)/m
+ *
+ * The result is
+ *
+ * (a f + d g')/(m d)
+ *
+ * where g' is the result of plugging in "subs" in each of the integer
+ * divisions in g.
+ */
+__isl_give isl_aff *isl_aff_substitute(__isl_take isl_aff *aff,
+ enum isl_dim_type type, unsigned pos, __isl_keep isl_aff *subs)
+{
+ isl_ctx *ctx;
+ isl_int v;
+ isl_size n_div;
+
+ aff = isl_aff_cow(aff);
+ if (!aff || !subs)
+ return isl_aff_free(aff);
+
+ ctx = isl_aff_get_ctx(aff);
+ if (!isl_space_is_equal(aff->ls->dim, subs->ls->dim))
+ isl_die(ctx, isl_error_invalid,
+ "spaces don't match", return isl_aff_free(aff));
+ n_div = isl_aff_domain_dim(subs, isl_dim_div);
+ if (n_div < 0)
+ return isl_aff_free(aff);
+ if (n_div != 0)
+ isl_die(ctx, isl_error_unsupported,
+ "cannot handle divs yet", return isl_aff_free(aff));
+
+ aff->ls = isl_local_space_substitute(aff->ls, type, pos, subs);
+ if (!aff->ls)
+ return isl_aff_free(aff);
+
+ aff->v = isl_vec_cow(aff->v);
+ if (!aff->v)
+ return isl_aff_free(aff);
+
+ pos += isl_local_space_offset(aff->ls, type);
+
+ isl_int_init(v);
+ isl_seq_substitute(aff->v->el, pos, subs->v->el,
+ aff->v->size, subs->v->size, v);
+ isl_int_clear(v);
+
+ return aff;
+}
+
+/* Plug in "subs" for dimension "type", "pos" in each of the affine
+ * expressions in "maff".
+ */
+__isl_give isl_multi_aff *isl_multi_aff_substitute(
+ __isl_take isl_multi_aff *maff, enum isl_dim_type type, unsigned pos,
+ __isl_keep isl_aff *subs)
+{
+ int i;
+
+ maff = isl_multi_aff_cow(maff);
+ if (!maff || !subs)
+ return isl_multi_aff_free(maff);
+
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+
+ for (i = 0; i < maff->n; ++i) {
+ maff->u.p[i] = isl_aff_substitute(maff->u.p[i],
+ type, pos, subs);
+ if (!maff->u.p[i])
+ return isl_multi_aff_free(maff);
+ }
+
+ return maff;
+}
+
+/* Plug in "subs" for input dimension "pos" of "pma".
+ *
+ * pma is of the form
+ *
+ * A_i(v) -> M_i(v)
+ *
+ * while subs is of the form
+ *
+ * v' = B_j(v) -> S_j
+ *
+ * Each pair i,j such that C_ij = A_i \cap B_i is non-empty
+ * has a contribution in the result, in particular
+ *
+ * C_ij(S_j) -> M_i(S_j)
+ *
+ * Note that plugging in S_j in C_ij may also result in an empty set
+ * and this contribution should simply be discarded.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_substitute(
+ __isl_take isl_pw_multi_aff *pma, unsigned pos,
+ __isl_keep isl_pw_aff *subs)
+{
+ int i, j, n;
+ isl_pw_multi_aff *res;
+
+ if (!pma || !subs)
+ return isl_pw_multi_aff_free(pma);
+
+ n = pma->n * subs->n;
+ res = isl_pw_multi_aff_alloc_size(isl_space_copy(pma->dim), n);
+
+ for (i = 0; i < pma->n; ++i) {
+ for (j = 0; j < subs->n; ++j) {
+ isl_set *common;
+ isl_multi_aff *res_ij;
+ int empty;
+
+ common = isl_set_intersect(
+ isl_set_copy(pma->p[i].set),
+ isl_set_copy(subs->p[j].set));
+ common = isl_set_substitute(common,
+ pos, subs->p[j].aff);
+ empty = isl_set_plain_is_empty(common);
+ if (empty < 0 || empty) {
+ isl_set_free(common);
+ if (empty < 0)
+ goto error;
+ continue;
+ }
+
+ res_ij = isl_multi_aff_substitute(
+ isl_multi_aff_copy(pma->p[i].maff),
+ isl_dim_in, pos, subs->p[j].aff);
+
+ res = isl_pw_multi_aff_add_piece(res, common, res_ij);
+ }
+ }
+
+ isl_pw_multi_aff_free(pma);
+ return res;
+error:
+ isl_pw_multi_aff_free(pma);
+ isl_pw_multi_aff_free(res);
+ return NULL;
+}
+
+/* Compute the preimage of a range of dimensions in the affine expression "src"
+ * under "ma" and put the result in "dst". The number of dimensions in "src"
+ * that precede the range is given by "n_before". The number of dimensions
+ * in the range is given by the number of output dimensions of "ma".
+ * The number of dimensions that follow the range is given by "n_after".
+ * If "has_denom" is set (to one),
+ * then "src" and "dst" have an extra initial denominator.
+ * "n_div_ma" is the number of existentials in "ma"
+ * "n_div_bset" is the number of existentials in "src"
+ * The resulting "dst" (which is assumed to have been allocated by
+ * the caller) contains coefficients for both sets of existentials,
+ * first those in "ma" and then those in "src".
+ * f, c1, c2 and g are temporary objects that have been initialized
+ * by the caller.
+ *
+ * Let src represent the expression
+ *
+ * (a(p) + f_u u + b v + f_w w + c(divs))/d
+ *
+ * and let ma represent the expressions
+ *
+ * v_i = (r_i(p) + s_i(y) + t_i(divs'))/m_i
+ *
+ * We start out with the following expression for dst:
+ *
+ * (a(p) + f_u u + 0 y + f_w w + 0 divs' + c(divs) + f \sum_i b_i v_i)/d
+ *
+ * with the multiplication factor f initially equal to 1
+ * and f \sum_i b_i v_i kept separately.
+ * For each x_i that we substitute, we multiply the numerator
+ * (and denominator) of dst by c_1 = m_i and add the numerator
+ * of the x_i expression multiplied by c_2 = f b_i,
+ * after removing the common factors of c_1 and c_2.
+ * The multiplication factor f also needs to be multiplied by c_1
+ * for the next x_j, j > i.
+ */
+isl_stat isl_seq_preimage(isl_int *dst, isl_int *src,
+ __isl_keep isl_multi_aff *ma, int n_before, int n_after,
+ int n_div_ma, int n_div_bmap,
+ isl_int f, isl_int c1, isl_int c2, isl_int g, int has_denom)
+{
+ int i;
+ isl_size n_param, n_in, n_out;
+ int o_dst, o_src;
+
+ n_param = isl_multi_aff_dim(ma, isl_dim_param);
+ n_in = isl_multi_aff_dim(ma, isl_dim_in);
+ n_out = isl_multi_aff_dim(ma, isl_dim_out);
+ if (n_param < 0 || n_in < 0 || n_out < 0)
+ return isl_stat_error;
+
+ isl_seq_cpy(dst, src, has_denom + 1 + n_param + n_before);
+ o_dst = o_src = has_denom + 1 + n_param + n_before;
+ isl_seq_clr(dst + o_dst, n_in);
+ o_dst += n_in;
+ o_src += n_out;
+ isl_seq_cpy(dst + o_dst, src + o_src, n_after);
+ o_dst += n_after;
+ o_src += n_after;
+ isl_seq_clr(dst + o_dst, n_div_ma);
+ o_dst += n_div_ma;
+ isl_seq_cpy(dst + o_dst, src + o_src, n_div_bmap);
+
+ isl_int_set_si(f, 1);
+
+ for (i = 0; i < n_out; ++i) {
+ int offset = has_denom + 1 + n_param + n_before + i;
+
+ if (isl_int_is_zero(src[offset]))
+ continue;
+ isl_int_set(c1, ma->u.p[i]->v->el[0]);
+ isl_int_mul(c2, f, src[offset]);
+ isl_int_gcd(g, c1, c2);
+ isl_int_divexact(c1, c1, g);
+ isl_int_divexact(c2, c2, g);
+
+ isl_int_mul(f, f, c1);
+ o_dst = has_denom;
+ o_src = 1;
+ isl_seq_combine(dst + o_dst, c1, dst + o_dst,
+ c2, ma->u.p[i]->v->el + o_src, 1 + n_param);
+ o_dst += 1 + n_param;
+ o_src += 1 + n_param;
+ isl_seq_scale(dst + o_dst, dst + o_dst, c1, n_before);
+ o_dst += n_before;
+ isl_seq_combine(dst + o_dst, c1, dst + o_dst,
+ c2, ma->u.p[i]->v->el + o_src, n_in);
+ o_dst += n_in;
+ o_src += n_in;
+ isl_seq_scale(dst + o_dst, dst + o_dst, c1, n_after);
+ o_dst += n_after;
+ isl_seq_combine(dst + o_dst, c1, dst + o_dst,
+ c2, ma->u.p[i]->v->el + o_src, n_div_ma);
+ o_dst += n_div_ma;
+ o_src += n_div_ma;
+ isl_seq_scale(dst + o_dst, dst + o_dst, c1, n_div_bmap);
+ if (has_denom)
+ isl_int_mul(dst[0], dst[0], c1);
+ }
+
+ return isl_stat_ok;
+}
+
+/* Compute the pullback of "aff" by the function represented by "ma".
+ * In other words, plug in "ma" in "aff". The result is an affine expression
+ * defined over the domain space of "ma".
+ *
+ * If "aff" is represented by
+ *
+ * (a(p) + b x + c(divs))/d
+ *
+ * and ma is represented by
+ *
+ * x = D(p) + F(y) + G(divs')
+ *
+ * then the result is
+ *
+ * (a(p) + b D(p) + b F(y) + b G(divs') + c(divs))/d
+ *
+ * The divs in the local space of the input are similarly adjusted
+ * through a call to isl_local_space_preimage_multi_aff.
+ */
+__isl_give isl_aff *isl_aff_pullback_multi_aff(__isl_take isl_aff *aff,
+ __isl_take isl_multi_aff *ma)
+{
+ isl_aff *res = NULL;
+ isl_local_space *ls;
+ isl_size n_div_aff, n_div_ma;
+ isl_int f, c1, c2, g;
+
+ ma = isl_multi_aff_align_divs(ma);
+ if (!aff || !ma)
+ goto error;
+
+ n_div_aff = isl_aff_dim(aff, isl_dim_div);
+ n_div_ma = ma->n ? isl_aff_dim(ma->u.p[0], isl_dim_div) : 0;
+ if (n_div_aff < 0 || n_div_ma < 0)
+ goto error;
+
+ ls = isl_aff_get_domain_local_space(aff);
+ ls = isl_local_space_preimage_multi_aff(ls, isl_multi_aff_copy(ma));
+ res = isl_aff_alloc(ls);
+ if (!res)
+ goto error;
+
+ isl_int_init(f);
+ isl_int_init(c1);
+ isl_int_init(c2);
+ isl_int_init(g);
+
+ if (isl_seq_preimage(res->v->el, aff->v->el, ma, 0, 0,
+ n_div_ma, n_div_aff, f, c1, c2, g, 1) < 0)
+ res = isl_aff_free(res);
+
+ isl_int_clear(f);
+ isl_int_clear(c1);
+ isl_int_clear(c2);
+ isl_int_clear(g);
+
+ isl_aff_free(aff);
+ isl_multi_aff_free(ma);
+ res = isl_aff_normalize(res);
+ return res;
+error:
+ isl_aff_free(aff);
+ isl_multi_aff_free(ma);
+ isl_aff_free(res);
+ return NULL;
+}
+
+/* Compute the pullback of "aff1" by the function represented by "aff2".
+ * In other words, plug in "aff2" in "aff1". The result is an affine expression
+ * defined over the domain space of "aff1".
+ *
+ * The domain of "aff1" should match the range of "aff2", which means
+ * that it should be single-dimensional.
+ */
+__isl_give isl_aff *isl_aff_pullback_aff(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2)
+{
+ isl_multi_aff *ma;
+
+ ma = isl_multi_aff_from_aff(aff2);
+ return isl_aff_pullback_multi_aff(aff1, ma);
+}
+
+/* Compute the pullback of "ma1" by the function represented by "ma2".
+ * In other words, plug in "ma2" in "ma1".
+ */
+__isl_give isl_multi_aff *isl_multi_aff_pullback_multi_aff(
+ __isl_take isl_multi_aff *ma1, __isl_take isl_multi_aff *ma2)
+{
+ int i;
+ isl_space *space = NULL;
+
+ isl_multi_aff_align_params_bin(&ma1, &ma2);
+ ma2 = isl_multi_aff_align_divs(ma2);
+ ma1 = isl_multi_aff_cow(ma1);
+ if (!ma1 || !ma2)
+ goto error;
+
+ space = isl_space_join(isl_multi_aff_get_space(ma2),
+ isl_multi_aff_get_space(ma1));
+
+ for (i = 0; i < ma1->n; ++i) {
+ ma1->u.p[i] = isl_aff_pullback_multi_aff(ma1->u.p[i],
+ isl_multi_aff_copy(ma2));
+ if (!ma1->u.p[i])
+ goto error;
+ }
+
+ ma1 = isl_multi_aff_reset_space(ma1, space);
+ isl_multi_aff_free(ma2);
+ return ma1;
+error:
+ isl_space_free(space);
+ isl_multi_aff_free(ma2);
+ isl_multi_aff_free(ma1);
+ return NULL;
+}
+
+/* Extend the local space of "dst" to include the divs
+ * in the local space of "src".
+ *
+ * If "src" does not have any divs or if the local spaces of "dst" and
+ * "src" are the same, then no extension is required.
+ */
+__isl_give isl_aff *isl_aff_align_divs(__isl_take isl_aff *dst,
+ __isl_keep isl_aff *src)
+{
+ isl_ctx *ctx;
+ isl_size src_n_div, dst_n_div;
+ int *exp1 = NULL;
+ int *exp2 = NULL;
+ isl_bool equal;
+ isl_mat *div;
+
+ if (!src || !dst)
+ return isl_aff_free(dst);
+
+ ctx = isl_aff_get_ctx(src);
+ equal = isl_local_space_has_equal_space(src->ls, dst->ls);
+ if (equal < 0)
+ return isl_aff_free(dst);
+ if (!equal)
+ isl_die(ctx, isl_error_invalid,
+ "spaces don't match", goto error);
+
+ src_n_div = isl_aff_domain_dim(src, isl_dim_div);
+ dst_n_div = isl_aff_domain_dim(dst, isl_dim_div);
+ if (src_n_div == 0)
+ return dst;
+ equal = isl_local_space_is_equal(src->ls, dst->ls);
+ if (equal < 0 || src_n_div < 0 || dst_n_div < 0)
+ return isl_aff_free(dst);
+ if (equal)
+ return dst;
+
+ exp1 = isl_alloc_array(ctx, int, src_n_div);
+ exp2 = isl_alloc_array(ctx, int, dst_n_div);
+ if (!exp1 || (dst_n_div && !exp2))
+ goto error;
+
+ div = isl_merge_divs(src->ls->div, dst->ls->div, exp1, exp2);
+ dst = isl_aff_expand_divs(dst, div, exp2);
+ free(exp1);
+ free(exp2);
+
+ return dst;
+error:
+ free(exp1);
+ free(exp2);
+ return isl_aff_free(dst);
+}
+
+/* Adjust the local spaces of the affine expressions in "maff"
+ * such that they all have the save divs.
+ */
+__isl_give isl_multi_aff *isl_multi_aff_align_divs(
+ __isl_take isl_multi_aff *maff)
+{
+ int i;
+
+ if (!maff)
+ return NULL;
+ if (maff->n == 0)
+ return maff;
+ maff = isl_multi_aff_cow(maff);
+ if (!maff)
+ return NULL;
+
+ for (i = 1; i < maff->n; ++i)
+ maff->u.p[0] = isl_aff_align_divs(maff->u.p[0], maff->u.p[i]);
+ for (i = 1; i < maff->n; ++i) {
+ maff->u.p[i] = isl_aff_align_divs(maff->u.p[i], maff->u.p[0]);
+ if (!maff->u.p[i])
+ return isl_multi_aff_free(maff);
+ }
+
+ return maff;
+}
+
+__isl_give isl_aff *isl_aff_lift(__isl_take isl_aff *aff)
+{
+ aff = isl_aff_cow(aff);
+ if (!aff)
+ return NULL;
+
+ aff->ls = isl_local_space_lift(aff->ls);
+ if (!aff->ls)
+ return isl_aff_free(aff);
+
+ return aff;
+}
+
+/* Lift "maff" to a space with extra dimensions such that the result
+ * has no more existentially quantified variables.
+ * If "ls" is not NULL, then *ls is assigned the local space that lies
+ * at the basis of the lifting applied to "maff".
+ */
+__isl_give isl_multi_aff *isl_multi_aff_lift(__isl_take isl_multi_aff *maff,
+ __isl_give isl_local_space **ls)
+{
+ int i;
+ isl_space *space;
+ isl_size n_div;
+
+ if (ls)
+ *ls = NULL;
+
+ if (!maff)
+ return NULL;
+
+ if (maff->n == 0) {
+ if (ls) {
+ isl_space *space = isl_multi_aff_get_domain_space(maff);
+ *ls = isl_local_space_from_space(space);
+ if (!*ls)
+ return isl_multi_aff_free(maff);
+ }
+ return maff;
+ }
+
+ maff = isl_multi_aff_cow(maff);
+ maff = isl_multi_aff_align_divs(maff);
+ if (!maff)
+ return NULL;
+
+ n_div = isl_aff_dim(maff->u.p[0], isl_dim_div);
+ if (n_div < 0)
+ return isl_multi_aff_free(maff);
+ space = isl_multi_aff_get_space(maff);
+ space = isl_space_lift(isl_space_domain(space), n_div);
+ space = isl_space_extend_domain_with_range(space,
+ isl_multi_aff_get_space(maff));
+ if (!space)
+ return isl_multi_aff_free(maff);
+ isl_space_free(maff->space);
+ maff->space = space;
+
+ if (ls) {
+ *ls = isl_aff_get_domain_local_space(maff->u.p[0]);
+ if (!*ls)
+ return isl_multi_aff_free(maff);
+ }
+
+ for (i = 0; i < maff->n; ++i) {
+ maff->u.p[i] = isl_aff_lift(maff->u.p[i]);
+ if (!maff->u.p[i])
+ goto error;
+ }
+
+ return maff;
+error:
+ if (ls)
+ isl_local_space_free(*ls);
+ return isl_multi_aff_free(maff);
+}
+
+#undef TYPE
+#define TYPE isl_pw_multi_aff
+static
+#include "check_type_range_templ.c"
+
+/* Extract an isl_pw_aff corresponding to output dimension "pos" of "pma".
+ */
+__isl_give isl_pw_aff *isl_pw_multi_aff_get_at(
+ __isl_keep isl_pw_multi_aff *pma, int pos)
+{
+ int i;
+ isl_size n_out;
+ isl_space *space;
+ isl_pw_aff *pa;
+
+ if (isl_pw_multi_aff_check_range(pma, isl_dim_out, pos, 1) < 0)
+ return NULL;
+
+ n_out = isl_pw_multi_aff_dim(pma, isl_dim_out);
+ if (n_out < 0)
+ return NULL;
+
+ space = isl_pw_multi_aff_get_space(pma);
+ space = isl_space_drop_dims(space, isl_dim_out,
+ pos + 1, n_out - pos - 1);
+ space = isl_space_drop_dims(space, isl_dim_out, 0, pos);
+
+ pa = isl_pw_aff_alloc_size(space, pma->n);
+ for (i = 0; i < pma->n; ++i) {
+ isl_aff *aff;
+ aff = isl_multi_aff_get_aff(pma->p[i].maff, pos);
+ pa = isl_pw_aff_add_piece(pa, isl_set_copy(pma->p[i].set), aff);
+ }
+
+ return pa;
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_pw_aff *isl_pw_multi_aff_get_pw_aff(
+ __isl_keep isl_pw_multi_aff *pma, int pos)
+{
+ return isl_pw_multi_aff_get_at(pma, pos);
+}
+
+/* Return an isl_pw_multi_aff with the given "set" as domain and
+ * an unnamed zero-dimensional range.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_domain(
+ __isl_take isl_set *set)
+{
+ isl_multi_aff *ma;
+ isl_space *space;
+
+ space = isl_set_get_space(set);
+ space = isl_space_from_domain(space);
+ ma = isl_multi_aff_zero(space);
+ return isl_pw_multi_aff_alloc(set, ma);
+}
+
+/* Add an isl_pw_multi_aff with the given "set" as domain and
+ * an unnamed zero-dimensional range to *user.
+ */
+static isl_stat add_pw_multi_aff_from_domain(__isl_take isl_set *set,
+ void *user)
+{
+ isl_union_pw_multi_aff **upma = user;
+ isl_pw_multi_aff *pma;
+
+ pma = isl_pw_multi_aff_from_domain(set);
+ *upma = isl_union_pw_multi_aff_add_pw_multi_aff(*upma, pma);
+
+ return isl_stat_ok;
+}
+
+/* Return an isl_union_pw_multi_aff with the given "uset" as domain and
+ * an unnamed zero-dimensional range.
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_domain(
+ __isl_take isl_union_set *uset)
+{
+ isl_space *space;
+ isl_union_pw_multi_aff *upma;
+
+ if (!uset)
+ return NULL;
+
+ space = isl_union_set_get_space(uset);
+ upma = isl_union_pw_multi_aff_empty(space);
+
+ if (isl_union_set_foreach_set(uset,
+ &add_pw_multi_aff_from_domain, &upma) < 0)
+ goto error;
+
+ isl_union_set_free(uset);
+ return upma;
+error:
+ isl_union_set_free(uset);
+ isl_union_pw_multi_aff_free(upma);
+ return NULL;
+}
+
+/* Local data for bin_entry and the callback "fn".
+ */
+struct isl_union_pw_multi_aff_bin_data {
+ isl_union_pw_multi_aff *upma2;
+ isl_union_pw_multi_aff *res;
+ isl_pw_multi_aff *pma;
+ isl_stat (*fn)(__isl_take isl_pw_multi_aff *pma, void *user);
+};
+
+/* Given an isl_pw_multi_aff from upma1, store it in data->pma
+ * and call data->fn for each isl_pw_multi_aff in data->upma2.
+ */
+static isl_stat bin_entry(__isl_take isl_pw_multi_aff *pma, void *user)
+{
+ struct isl_union_pw_multi_aff_bin_data *data = user;
+ isl_stat r;
+
+ data->pma = pma;
+ r = isl_union_pw_multi_aff_foreach_pw_multi_aff(data->upma2,
+ data->fn, data);
+ isl_pw_multi_aff_free(pma);
+
+ return r;
+}
+
+/* Call "fn" on each pair of isl_pw_multi_affs in "upma1" and "upma2".
+ * The isl_pw_multi_aff from upma1 is stored in data->pma (where data is
+ * passed as user field) and the isl_pw_multi_aff from upma2 is available
+ * as *entry. The callback should adjust data->res if desired.
+ */
+static __isl_give isl_union_pw_multi_aff *bin_op(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2,
+ isl_stat (*fn)(__isl_take isl_pw_multi_aff *pma, void *user))
+{
+ isl_space *space;
+ struct isl_union_pw_multi_aff_bin_data data = { NULL, NULL, NULL, fn };
+
+ space = isl_union_pw_multi_aff_get_space(upma2);
+ upma1 = isl_union_pw_multi_aff_align_params(upma1, space);
+ space = isl_union_pw_multi_aff_get_space(upma1);
+ upma2 = isl_union_pw_multi_aff_align_params(upma2, space);
+
+ if (!upma1 || !upma2)
+ goto error;
+
+ data.upma2 = upma2;
+ data.res = isl_union_pw_multi_aff_alloc_same_size(upma1);
+ if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma1,
+ &bin_entry, &data) < 0)
+ goto error;
+
+ isl_union_pw_multi_aff_free(upma1);
+ isl_union_pw_multi_aff_free(upma2);
+ return data.res;
+error:
+ isl_union_pw_multi_aff_free(upma1);
+ isl_union_pw_multi_aff_free(upma2);
+ isl_union_pw_multi_aff_free(data.res);
+ return NULL;
+}
+
+/* Given two isl_pw_multi_affs A -> B and C -> D,
+ * construct an isl_pw_multi_aff (A * C) -> [B -> D].
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_range_product(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2)
+{
+ isl_space *space;
+
+ isl_pw_multi_aff_align_params_bin(&pma1, &pma2);
+ space = isl_space_range_product(isl_pw_multi_aff_get_space(pma1),
+ isl_pw_multi_aff_get_space(pma2));
+ return isl_pw_multi_aff_on_shared_domain_in(pma1, pma2, space,
+ &isl_multi_aff_range_product);
+}
+
+/* Given two isl_pw_multi_affs A -> B and C -> D,
+ * construct an isl_pw_multi_aff (A * C) -> (B, D).
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_flat_range_product(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2)
+{
+ isl_space *space;
+
+ isl_pw_multi_aff_align_params_bin(&pma1, &pma2);
+ space = isl_space_range_product(isl_pw_multi_aff_get_space(pma1),
+ isl_pw_multi_aff_get_space(pma2));
+ space = isl_space_flatten_range(space);
+ return isl_pw_multi_aff_on_shared_domain_in(pma1, pma2, space,
+ &isl_multi_aff_flat_range_product);
+}
+
+/* If data->pma and "pma2" have the same domain space, then use "range_product"
+ * to compute some form of range product and add the result to data->res.
+ */
+static isl_stat gen_range_product_entry(__isl_take isl_pw_multi_aff *pma2,
+ __isl_give isl_pw_multi_aff *(*range_product)(
+ __isl_take isl_pw_multi_aff *pma1,
+ __isl_take isl_pw_multi_aff *pma2),
+ void *user)
+{
+ struct isl_union_pw_multi_aff_bin_data *data = user;
+ isl_bool match;
+ isl_space *space1, *space2;
+
+ space1 = isl_pw_multi_aff_peek_space(data->pma);
+ space2 = isl_pw_multi_aff_peek_space(pma2);
+ match = isl_space_tuple_is_equal(space1, isl_dim_in,
+ space2, isl_dim_in);
+ if (match < 0 || !match) {
+ isl_pw_multi_aff_free(pma2);
+ return match < 0 ? isl_stat_error : isl_stat_ok;
+ }
+
+ pma2 = range_product(isl_pw_multi_aff_copy(data->pma), pma2);
+
+ data->res = isl_union_pw_multi_aff_add_pw_multi_aff(data->res, pma2);
+
+ return isl_stat_ok;
+}
+
+/* If data->pma and "pma2" have the same domain space, then compute
+ * their flat range product and add the result to data->res.
+ */
+static isl_stat flat_range_product_entry(__isl_take isl_pw_multi_aff *pma2,
+ void *user)
+{
+ return gen_range_product_entry(pma2,
+ &isl_pw_multi_aff_flat_range_product, user);
+}
+
+/* Given two isl_union_pw_multi_affs A -> B and C -> D,
+ * construct an isl_union_pw_multi_aff (A * C) -> (B, D).
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_flat_range_product(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2)
+{
+ return bin_op(upma1, upma2, &flat_range_product_entry);
+}
+
+/* If data->pma and "pma2" have the same domain space, then compute
+ * their range product and add the result to data->res.
+ */
+static isl_stat range_product_entry(__isl_take isl_pw_multi_aff *pma2,
+ void *user)
+{
+ return gen_range_product_entry(pma2,
+ &isl_pw_multi_aff_range_product, user);
+}
+
+/* Given two isl_union_pw_multi_affs A -> B and C -> D,
+ * construct an isl_union_pw_multi_aff (A * C) -> [B -> D].
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_range_product(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2)
+{
+ return bin_op(upma1, upma2, &range_product_entry);
+}
+
+/* Replace the affine expressions at position "pos" in "pma" by "pa".
+ * The parameters are assumed to have been aligned.
+ *
+ * The implementation essentially performs an isl_pw_*_on_shared_domain,
+ * except that it works on two different isl_pw_* types.
+ */
+static __isl_give isl_pw_multi_aff *pw_multi_aff_set_pw_aff(
+ __isl_take isl_pw_multi_aff *pma, unsigned pos,
+ __isl_take isl_pw_aff *pa)
+{
+ int i, j, n;
+ isl_pw_multi_aff *res = NULL;
+
+ if (!pma || !pa)
+ goto error;
+
+ if (!isl_space_tuple_is_equal(pma->dim, isl_dim_in,
+ pa->dim, isl_dim_in))
+ isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid,
+ "domains don't match", goto error);
+ if (isl_pw_multi_aff_check_range(pma, isl_dim_out, pos, 1) < 0)
+ goto error;
+
+ n = pma->n * pa->n;
+ res = isl_pw_multi_aff_alloc_size(isl_pw_multi_aff_get_space(pma), n);
+
+ for (i = 0; i < pma->n; ++i) {
+ for (j = 0; j < pa->n; ++j) {
+ isl_set *common;
+ isl_multi_aff *res_ij;
+ int empty;
+
+ common = isl_set_intersect(isl_set_copy(pma->p[i].set),
+ isl_set_copy(pa->p[j].set));
+ empty = isl_set_plain_is_empty(common);
+ if (empty < 0 || empty) {
+ isl_set_free(common);
+ if (empty < 0)
+ goto error;
+ continue;
+ }
+
+ res_ij = isl_multi_aff_set_aff(
+ isl_multi_aff_copy(pma->p[i].maff), pos,
+ isl_aff_copy(pa->p[j].aff));
+ res_ij = isl_multi_aff_gist(res_ij,
+ isl_set_copy(common));
+
+ res = isl_pw_multi_aff_add_piece(res, common, res_ij);
+ }
+ }
+
+ isl_pw_multi_aff_free(pma);
+ isl_pw_aff_free(pa);
+ return res;
+error:
+ isl_pw_multi_aff_free(pma);
+ isl_pw_aff_free(pa);
+ return isl_pw_multi_aff_free(res);
+}
+
+/* Replace the affine expressions at position "pos" in "pma" by "pa".
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_set_pw_aff(
+ __isl_take isl_pw_multi_aff *pma, unsigned pos,
+ __isl_take isl_pw_aff *pa)
+{
+ isl_bool equal_params;
+
+ if (!pma || !pa)
+ goto error;
+ equal_params = isl_space_has_equal_params(pma->dim, pa->dim);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params)
+ return pw_multi_aff_set_pw_aff(pma, pos, pa);
+ if (isl_pw_multi_aff_check_named_params(pma) < 0 ||
+ isl_pw_aff_check_named_params(pa) < 0)
+ goto error;
+ pma = isl_pw_multi_aff_align_params(pma, isl_pw_aff_get_space(pa));
+ pa = isl_pw_aff_align_params(pa, isl_pw_multi_aff_get_space(pma));
+ return pw_multi_aff_set_pw_aff(pma, pos, pa);
+error:
+ isl_pw_multi_aff_free(pma);
+ isl_pw_aff_free(pa);
+ return NULL;
+}
+
+/* Do the parameters of "pa" match those of "space"?
+ */
+isl_bool isl_pw_aff_matching_params(__isl_keep isl_pw_aff *pa,
+ __isl_keep isl_space *space)
+{
+ isl_space *pa_space;
+ isl_bool match;
+
+ if (!pa || !space)
+ return isl_bool_error;
+
+ pa_space = isl_pw_aff_get_space(pa);
+
+ match = isl_space_has_equal_params(space, pa_space);
+
+ isl_space_free(pa_space);
+ return match;
+}
+
+/* Check that the domain space of "pa" matches "space".
+ */
+isl_stat isl_pw_aff_check_match_domain_space(__isl_keep isl_pw_aff *pa,
+ __isl_keep isl_space *space)
+{
+ isl_space *pa_space;
+ isl_bool match;
+
+ if (!pa || !space)
+ return isl_stat_error;
+
+ pa_space = isl_pw_aff_get_space(pa);
+
+ match = isl_space_has_equal_params(space, pa_space);
+ if (match < 0)
+ goto error;
+ if (!match)
+ isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid,
+ "parameters don't match", goto error);
+ match = isl_space_tuple_is_equal(space, isl_dim_in,
+ pa_space, isl_dim_in);
+ if (match < 0)
+ goto error;
+ if (!match)
+ isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid,
+ "domains don't match", goto error);
+ isl_space_free(pa_space);
+ return isl_stat_ok;
+error:
+ isl_space_free(pa_space);
+ return isl_stat_error;
+}
+
+#undef BASE
+#define BASE pw_aff
+#undef DOMBASE
+#define DOMBASE set
+
+#include <isl_multi_explicit_domain.c>
+#include <isl_multi_pw_aff_explicit_domain.c>
+#include <isl_multi_templ.c>
+#include <isl_multi_add_constant_templ.c>
+#include <isl_multi_apply_set.c>
+#include <isl_multi_arith_templ.c>
+#include <isl_multi_bind_templ.c>
+#include <isl_multi_bind_domain_templ.c>
+#include <isl_multi_coalesce.c>
+#include <isl_multi_domain_templ.c>
+#include <isl_multi_dim_id_templ.c>
+#include <isl_multi_dims.c>
+#include <isl_multi_from_base_templ.c>
+#include <isl_multi_gist.c>
+#include <isl_multi_hash.c>
+#include <isl_multi_identity_templ.c>
+#include <isl_multi_align_set.c>
+#include <isl_multi_insert_domain_templ.c>
+#include <isl_multi_intersect.c>
+#include <isl_multi_min_max_templ.c>
+#include <isl_multi_move_dims_templ.c>
+#include <isl_multi_nan_templ.c>
+#include <isl_multi_param_templ.c>
+#include <isl_multi_product_templ.c>
+#include <isl_multi_splice_templ.c>
+#include <isl_multi_tuple_id_templ.c>
+#include <isl_multi_union_add_templ.c>
+#include <isl_multi_zero_templ.c>
+#include <isl_multi_unbind_params_templ.c>
+
+/* Is every element of "mpa" defined over a single universe domain?
+ */
+isl_bool isl_multi_pw_aff_isa_multi_aff(__isl_keep isl_multi_pw_aff *mpa)
+{
+ return isl_multi_pw_aff_every(mpa, &isl_pw_aff_isa_aff);
+}
+
+/* Given that every element of "mpa" is defined over a single universe domain,
+ * return the corresponding base expressions.
+ */
+__isl_give isl_multi_aff *isl_multi_pw_aff_as_multi_aff(
+ __isl_take isl_multi_pw_aff *mpa)
+{
+ int i;
+ isl_size n;
+ isl_multi_aff *ma;
+
+ n = isl_multi_pw_aff_size(mpa);
+ if (n < 0)
+ mpa = isl_multi_pw_aff_free(mpa);
+ ma = isl_multi_aff_alloc(isl_multi_pw_aff_get_space(mpa));
+ for (i = 0; i < n; ++i) {
+ isl_aff *aff;
+
+ aff = isl_pw_aff_as_aff(isl_multi_pw_aff_get_at(mpa, i));
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ }
+ isl_multi_pw_aff_free(mpa);
+ return ma;
+}
+
+/* If "mpa" has an explicit domain, then intersect the domain of "map"
+ * with this explicit domain.
+ */
+__isl_give isl_map *isl_map_intersect_multi_pw_aff_explicit_domain(
+ __isl_take isl_map *map, __isl_keep isl_multi_pw_aff *mpa)
+{
+ isl_set *dom;
+
+ if (!isl_multi_pw_aff_has_explicit_domain(mpa))
+ return map;
+
+ dom = isl_multi_pw_aff_domain(isl_multi_pw_aff_copy(mpa));
+ map = isl_map_intersect_domain(map, dom);
+
+ return map;
+}
+
+/* Are all elements of "mpa" piecewise constants?
+ */
+isl_bool isl_multi_pw_aff_is_cst(__isl_keep isl_multi_pw_aff *mpa)
+{
+ return isl_multi_pw_aff_every(mpa, &isl_pw_aff_is_cst);
+}
+
+/* Does "mpa" have a non-trivial explicit domain?
+ *
+ * The explicit domain, if present, is trivial if it represents
+ * an (obviously) universe set.
+ */
+isl_bool isl_multi_pw_aff_has_non_trivial_domain(
+ __isl_keep isl_multi_pw_aff *mpa)
+{
+ if (!mpa)
+ return isl_bool_error;
+ if (!isl_multi_pw_aff_has_explicit_domain(mpa))
+ return isl_bool_false;
+ return isl_bool_not(isl_set_plain_is_universe(mpa->u.dom));
+}
+
+#undef BASE
+#define BASE set
+
+#include "isl_opt_mpa_templ.c"
+
+/* Compute the minima of the set dimensions as a function of the
+ * parameters, but independently of the other set dimensions.
+ */
+__isl_give isl_multi_pw_aff *isl_set_min_multi_pw_aff(__isl_take isl_set *set)
+{
+ return set_opt_mpa(set, &isl_set_dim_min);
+}
+
+/* Compute the maxima of the set dimensions as a function of the
+ * parameters, but independently of the other set dimensions.
+ */
+__isl_give isl_multi_pw_aff *isl_set_max_multi_pw_aff(__isl_take isl_set *set)
+{
+ return set_opt_mpa(set, &isl_set_dim_max);
+}
+
+#undef BASE
+#define BASE map
+
+#include "isl_opt_mpa_templ.c"
+
+/* Compute the minima of the output dimensions as a function of the
+ * parameters and input dimensions, but independently of
+ * the other output dimensions.
+ */
+__isl_give isl_multi_pw_aff *isl_map_min_multi_pw_aff(__isl_take isl_map *map)
+{
+ return map_opt_mpa(map, &isl_map_dim_min);
+}
+
+/* Compute the maxima of the output dimensions as a function of the
+ * parameters and input dimensions, but independently of
+ * the other output dimensions.
+ */
+__isl_give isl_multi_pw_aff *isl_map_max_multi_pw_aff(__isl_take isl_map *map)
+{
+ return map_opt_mpa(map, &isl_map_dim_max);
+}
+
+/* Scale the elements of "pma" by the corresponding elements of "mv".
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_scale_multi_val(
+ __isl_take isl_pw_multi_aff *pma, __isl_take isl_multi_val *mv)
+{
+ int i;
+ isl_bool equal_params;
+
+ pma = isl_pw_multi_aff_cow(pma);
+ if (!pma || !mv)
+ goto error;
+ if (!isl_space_tuple_is_equal(pma->dim, isl_dim_out,
+ mv->space, isl_dim_set))
+ isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid,
+ "spaces don't match", goto error);
+ equal_params = isl_space_has_equal_params(pma->dim, mv->space);
+ if (equal_params < 0)
+ goto error;
+ if (!equal_params) {
+ pma = isl_pw_multi_aff_align_params(pma,
+ isl_multi_val_get_space(mv));
+ mv = isl_multi_val_align_params(mv,
+ isl_pw_multi_aff_get_space(pma));
+ if (!pma || !mv)
+ goto error;
+ }
+
+ for (i = 0; i < pma->n; ++i) {
+ pma->p[i].maff = isl_multi_aff_scale_multi_val(pma->p[i].maff,
+ isl_multi_val_copy(mv));
+ if (!pma->p[i].maff)
+ goto error;
+ }
+
+ isl_multi_val_free(mv);
+ return pma;
+error:
+ isl_multi_val_free(mv);
+ isl_pw_multi_aff_free(pma);
+ return NULL;
+}
+
+/* This function is called for each entry of an isl_union_pw_multi_aff.
+ * If the space of the entry matches that of data->mv,
+ * then apply isl_pw_multi_aff_scale_multi_val and return the result.
+ * Otherwise, return an empty isl_pw_multi_aff.
+ */
+static __isl_give isl_pw_multi_aff *union_pw_multi_aff_scale_multi_val_entry(
+ __isl_take isl_pw_multi_aff *pma, void *user)
+{
+ isl_multi_val *mv = user;
+
+ if (!pma)
+ return NULL;
+ if (!isl_space_tuple_is_equal(pma->dim, isl_dim_out,
+ mv->space, isl_dim_set)) {
+ isl_space *space = isl_pw_multi_aff_get_space(pma);
+ isl_pw_multi_aff_free(pma);
+ return isl_pw_multi_aff_empty(space);
+ }
+
+ return isl_pw_multi_aff_scale_multi_val(pma, isl_multi_val_copy(mv));
+}
+
+/* Scale the elements of "upma" by the corresponding elements of "mv",
+ * for those entries that match the space of "mv".
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_scale_multi_val(
+ __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_multi_val *mv)
+{
+ struct isl_union_pw_multi_aff_transform_control control = {
+ .fn = &union_pw_multi_aff_scale_multi_val_entry,
+ .fn_user = mv,
+ };
+
+ upma = isl_union_pw_multi_aff_align_params(upma,
+ isl_multi_val_get_space(mv));
+ mv = isl_multi_val_align_params(mv,
+ isl_union_pw_multi_aff_get_space(upma));
+ if (!upma || !mv)
+ goto error;
+
+ return isl_union_pw_multi_aff_transform(upma, &control);
+
+ isl_multi_val_free(mv);
+ return upma;
+error:
+ isl_multi_val_free(mv);
+ isl_union_pw_multi_aff_free(upma);
+ return NULL;
+}
+
+/* Construct and return a piecewise multi affine expression
+ * in the given space with value zero in each of the output dimensions and
+ * a universe domain.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_zero(__isl_take isl_space *space)
+{
+ return isl_pw_multi_aff_from_multi_aff(isl_multi_aff_zero(space));
+}
+
+/* Construct and return a piecewise multi affine expression
+ * that is equal to the given piecewise affine expression.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_pw_aff(
+ __isl_take isl_pw_aff *pa)
+{
+ int i;
+ isl_space *space;
+ isl_pw_multi_aff *pma;
+
+ if (!pa)
+ return NULL;
+
+ space = isl_pw_aff_get_space(pa);
+ pma = isl_pw_multi_aff_alloc_size(space, pa->n);
+
+ for (i = 0; i < pa->n; ++i) {
+ isl_set *set;
+ isl_multi_aff *ma;
+
+ set = isl_set_copy(pa->p[i].set);
+ ma = isl_multi_aff_from_aff(isl_aff_copy(pa->p[i].aff));
+ pma = isl_pw_multi_aff_add_piece(pma, set, ma);
+ }
+
+ isl_pw_aff_free(pa);
+ return pma;
+}
+
+/* Construct and return a piecewise multi affine expression
+ * that is equal to the given multi piecewise affine expression
+ * on the shared domain of the piecewise affine expressions,
+ * in the special case of a 0D multi piecewise affine expression.
+ *
+ * Create a piecewise multi affine expression with the explicit domain of
+ * the 0D multi piecewise affine expression as domain.
+ */
+static __isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_multi_pw_aff_0D(
+ __isl_take isl_multi_pw_aff *mpa)
+{
+ isl_space *space;
+ isl_set *dom;
+ isl_multi_aff *ma;
+
+ space = isl_multi_pw_aff_get_space(mpa);
+ dom = isl_multi_pw_aff_get_explicit_domain(mpa);
+ isl_multi_pw_aff_free(mpa);
+
+ ma = isl_multi_aff_zero(space);
+ return isl_pw_multi_aff_alloc(dom, ma);
+}
+
+/* Construct and return a piecewise multi affine expression
+ * that is equal to the given multi piecewise affine expression
+ * on the shared domain of the piecewise affine expressions.
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_multi_pw_aff(
+ __isl_take isl_multi_pw_aff *mpa)
+{
+ int i;
+ isl_space *space;
+ isl_pw_aff *pa;
+ isl_pw_multi_aff *pma;
+
+ if (!mpa)
+ return NULL;
+
+ if (mpa->n == 0)
+ return isl_pw_multi_aff_from_multi_pw_aff_0D(mpa);
+
+ space = isl_multi_pw_aff_get_space(mpa);
+ pa = isl_multi_pw_aff_get_pw_aff(mpa, 0);
+ pma = isl_pw_multi_aff_from_pw_aff(pa);
+
+ for (i = 1; i < mpa->n; ++i) {
+ isl_pw_multi_aff *pma_i;
+
+ pa = isl_multi_pw_aff_get_pw_aff(mpa, i);
+ pma_i = isl_pw_multi_aff_from_pw_aff(pa);
+ pma = isl_pw_multi_aff_range_product(pma, pma_i);
+ }
+
+ pma = isl_pw_multi_aff_reset_space(pma, space);
+
+ isl_multi_pw_aff_free(mpa);
+ return pma;
+}
+
+/* Convenience function that constructs an isl_multi_pw_aff
+ * directly from an isl_aff.
+ */
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_aff(__isl_take isl_aff *aff)
+{
+ return isl_multi_pw_aff_from_pw_aff(isl_pw_aff_from_aff(aff));
+}
+
+/* Construct and return a multi piecewise affine expression
+ * that is equal to the given multi affine expression.
+ */
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_multi_aff(
+ __isl_take isl_multi_aff *ma)
+{
+ int i;
+ isl_size n;
+ isl_multi_pw_aff *mpa;
+
+ n = isl_multi_aff_dim(ma, isl_dim_out);
+ if (n < 0)
+ ma = isl_multi_aff_free(ma);
+ if (!ma)
+ return NULL;
+
+ mpa = isl_multi_pw_aff_alloc(isl_multi_aff_get_space(ma));
+
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+
+ pa = isl_pw_aff_from_aff(isl_multi_aff_get_aff(ma, i));
+ mpa = isl_multi_pw_aff_set_pw_aff(mpa, i, pa);
+ }
+
+ isl_multi_aff_free(ma);
+ return mpa;
+}
+
+/* This function performs the same operation as isl_multi_pw_aff_from_multi_aff,
+ * but is considered as a function on an isl_multi_aff when exported.
+ */
+__isl_give isl_multi_pw_aff *isl_multi_aff_to_multi_pw_aff(
+ __isl_take isl_multi_aff *ma)
+{
+ return isl_multi_pw_aff_from_multi_aff(ma);
+}
+
+/* Construct and return a multi piecewise affine expression
+ * that is equal to the given piecewise multi affine expression.
+ *
+ * If the resulting multi piecewise affine expression has
+ * an explicit domain, then assign it the domain of the input.
+ * In other cases, the domain is stored in the individual elements.
+ */
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_pw_multi_aff(
+ __isl_take isl_pw_multi_aff *pma)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_multi_pw_aff *mpa;
+
+ n = isl_pw_multi_aff_dim(pma, isl_dim_out);
+ if (n < 0)
+ pma = isl_pw_multi_aff_free(pma);
+ space = isl_pw_multi_aff_get_space(pma);
+ mpa = isl_multi_pw_aff_alloc(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+
+ pa = isl_pw_multi_aff_get_pw_aff(pma, i);
+ mpa = isl_multi_pw_aff_set_pw_aff(mpa, i, pa);
+ }
+ if (isl_multi_pw_aff_has_explicit_domain(mpa)) {
+ isl_set *dom;
+
+ dom = isl_pw_multi_aff_domain(isl_pw_multi_aff_copy(pma));
+ mpa = isl_multi_pw_aff_intersect_domain(mpa, dom);
+ }
+
+ isl_pw_multi_aff_free(pma);
+ return mpa;
+}
+
+/* This function performs the same operation as
+ * isl_multi_pw_aff_from_pw_multi_aff,
+ * but is considered as a function on an isl_pw_multi_aff when exported.
+ */
+__isl_give isl_multi_pw_aff *isl_pw_multi_aff_to_multi_pw_aff(
+ __isl_take isl_pw_multi_aff *pma)
+{
+ return isl_multi_pw_aff_from_pw_multi_aff(pma);
+}
+
+/* Do "pa1" and "pa2" represent the same function?
+ *
+ * We first check if they are obviously equal.
+ * If not, we convert them to maps and check if those are equal.
+ *
+ * If "pa1" or "pa2" contain any NaNs, then they are considered
+ * not to be the same. A NaN is not equal to anything, not even
+ * to another NaN.
+ */
+isl_bool isl_pw_aff_is_equal(__isl_keep isl_pw_aff *pa1,
+ __isl_keep isl_pw_aff *pa2)
+{
+ isl_bool equal;
+ isl_bool has_nan;
+ isl_map *map1, *map2;
+
+ if (!pa1 || !pa2)
+ return isl_bool_error;
+
+ equal = isl_pw_aff_plain_is_equal(pa1, pa2);
+ if (equal < 0 || equal)
+ return equal;
+ has_nan = either_involves_nan(pa1, pa2);
+ if (has_nan < 0)
+ return isl_bool_error;
+ if (has_nan)
+ return isl_bool_false;
+
+ map1 = isl_map_from_pw_aff_internal(isl_pw_aff_copy(pa1));
+ map2 = isl_map_from_pw_aff_internal(isl_pw_aff_copy(pa2));
+ equal = isl_map_is_equal(map1, map2);
+ isl_map_free(map1);
+ isl_map_free(map2);
+
+ return equal;
+}
+
+/* Do "mpa1" and "mpa2" represent the same function?
+ *
+ * Note that we cannot convert the entire isl_multi_pw_aff
+ * to a map because the domains of the piecewise affine expressions
+ * may not be the same.
+ */
+isl_bool isl_multi_pw_aff_is_equal(__isl_keep isl_multi_pw_aff *mpa1,
+ __isl_keep isl_multi_pw_aff *mpa2)
+{
+ int i;
+ isl_bool equal, equal_params;
+
+ if (!mpa1 || !mpa2)
+ return isl_bool_error;
+
+ equal_params = isl_space_has_equal_params(mpa1->space, mpa2->space);
+ if (equal_params < 0)
+ return isl_bool_error;
+ if (!equal_params) {
+ if (!isl_space_has_named_params(mpa1->space))
+ return isl_bool_false;
+ if (!isl_space_has_named_params(mpa2->space))
+ return isl_bool_false;
+ mpa1 = isl_multi_pw_aff_copy(mpa1);
+ mpa2 = isl_multi_pw_aff_copy(mpa2);
+ mpa1 = isl_multi_pw_aff_align_params(mpa1,
+ isl_multi_pw_aff_get_space(mpa2));
+ mpa2 = isl_multi_pw_aff_align_params(mpa2,
+ isl_multi_pw_aff_get_space(mpa1));
+ equal = isl_multi_pw_aff_is_equal(mpa1, mpa2);
+ isl_multi_pw_aff_free(mpa1);
+ isl_multi_pw_aff_free(mpa2);
+ return equal;
+ }
+
+ equal = isl_space_is_equal(mpa1->space, mpa2->space);
+ if (equal < 0 || !equal)
+ return equal;
+
+ for (i = 0; i < mpa1->n; ++i) {
+ equal = isl_pw_aff_is_equal(mpa1->u.p[i], mpa2->u.p[i]);
+ if (equal < 0 || !equal)
+ return equal;
+ }
+
+ return isl_bool_true;
+}
+
+/* Do "pma1" and "pma2" represent the same function?
+ *
+ * First check if they are obviously equal.
+ * If not, then convert them to maps and check if those are equal.
+ *
+ * If "pa1" or "pa2" contain any NaNs, then they are considered
+ * not to be the same. A NaN is not equal to anything, not even
+ * to another NaN.
+ */
+isl_bool isl_pw_multi_aff_is_equal(__isl_keep isl_pw_multi_aff *pma1,
+ __isl_keep isl_pw_multi_aff *pma2)
+{
+ isl_bool equal;
+ isl_bool has_nan;
+ isl_map *map1, *map2;
+
+ if (!pma1 || !pma2)
+ return isl_bool_error;
+
+ equal = isl_pw_multi_aff_plain_is_equal(pma1, pma2);
+ if (equal < 0 || equal)
+ return equal;
+ has_nan = isl_pw_multi_aff_involves_nan(pma1);
+ if (has_nan >= 0 && !has_nan)
+ has_nan = isl_pw_multi_aff_involves_nan(pma2);
+ if (has_nan < 0 || has_nan)
+ return isl_bool_not(has_nan);
+
+ map1 = isl_map_from_pw_multi_aff_internal(isl_pw_multi_aff_copy(pma1));
+ map2 = isl_map_from_pw_multi_aff_internal(isl_pw_multi_aff_copy(pma2));
+ equal = isl_map_is_equal(map1, map2);
+ isl_map_free(map1);
+ isl_map_free(map2);
+
+ return equal;
+}
+
+/* Compute the pullback of "mpa" by the function represented by "ma".
+ * In other words, plug in "ma" in "mpa".
+ *
+ * The parameters of "mpa" and "ma" are assumed to have been aligned.
+ *
+ * If "mpa" has an explicit domain, then it is this domain
+ * that needs to undergo a pullback, i.e., a preimage.
+ */
+static __isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_aff_aligned(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_multi_aff *ma)
+{
+ int i;
+ isl_space *space = NULL;
+
+ mpa = isl_multi_pw_aff_cow(mpa);
+ if (!mpa || !ma)
+ goto error;
+
+ space = isl_space_join(isl_multi_aff_get_space(ma),
+ isl_multi_pw_aff_get_space(mpa));
+ if (!space)
+ goto error;
+
+ for (i = 0; i < mpa->n; ++i) {
+ mpa->u.p[i] = isl_pw_aff_pullback_multi_aff(mpa->u.p[i],
+ isl_multi_aff_copy(ma));
+ if (!mpa->u.p[i])
+ goto error;
+ }
+ if (isl_multi_pw_aff_has_explicit_domain(mpa)) {
+ mpa->u.dom = isl_set_preimage_multi_aff(mpa->u.dom,
+ isl_multi_aff_copy(ma));
+ if (!mpa->u.dom)
+ goto error;
+ }
+
+ isl_multi_aff_free(ma);
+ isl_space_free(mpa->space);
+ mpa->space = space;
+ return mpa;
+error:
+ isl_space_free(space);
+ isl_multi_pw_aff_free(mpa);
+ isl_multi_aff_free(ma);
+ return NULL;
+}
+
+/* Compute the pullback of "mpa" by the function represented by "ma".
+ * In other words, plug in "ma" in "mpa".
+ */
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_aff(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_multi_aff *ma)
+{
+ isl_bool equal_params;
+
+ if (!mpa || !ma)
+ goto error;
+ equal_params = isl_space_has_equal_params(mpa->space, ma->space);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params)
+ return isl_multi_pw_aff_pullback_multi_aff_aligned(mpa, ma);
+ mpa = isl_multi_pw_aff_align_params(mpa, isl_multi_aff_get_space(ma));
+ ma = isl_multi_aff_align_params(ma, isl_multi_pw_aff_get_space(mpa));
+ return isl_multi_pw_aff_pullback_multi_aff_aligned(mpa, ma);
+error:
+ isl_multi_pw_aff_free(mpa);
+ isl_multi_aff_free(ma);
+ return NULL;
+}
+
+/* Compute the pullback of "mpa" by the function represented by "pma".
+ * In other words, plug in "pma" in "mpa".
+ *
+ * The parameters of "mpa" and "mpa" are assumed to have been aligned.
+ *
+ * If "mpa" has an explicit domain, then it is this domain
+ * that needs to undergo a pullback, i.e., a preimage.
+ */
+static __isl_give isl_multi_pw_aff *
+isl_multi_pw_aff_pullback_pw_multi_aff_aligned(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_pw_multi_aff *pma)
+{
+ int i;
+ isl_space *space = NULL;
+
+ mpa = isl_multi_pw_aff_cow(mpa);
+ if (!mpa || !pma)
+ goto error;
+
+ space = isl_space_join(isl_pw_multi_aff_get_space(pma),
+ isl_multi_pw_aff_get_space(mpa));
+
+ for (i = 0; i < mpa->n; ++i) {
+ mpa->u.p[i] = isl_pw_aff_pullback_pw_multi_aff_aligned(
+ mpa->u.p[i], isl_pw_multi_aff_copy(pma));
+ if (!mpa->u.p[i])
+ goto error;
+ }
+ if (isl_multi_pw_aff_has_explicit_domain(mpa)) {
+ mpa->u.dom = isl_set_preimage_pw_multi_aff(mpa->u.dom,
+ isl_pw_multi_aff_copy(pma));
+ if (!mpa->u.dom)
+ goto error;
+ }
+
+ isl_pw_multi_aff_free(pma);
+ isl_space_free(mpa->space);
+ mpa->space = space;
+ return mpa;
+error:
+ isl_space_free(space);
+ isl_multi_pw_aff_free(mpa);
+ isl_pw_multi_aff_free(pma);
+ return NULL;
+}
+
+/* Compute the pullback of "mpa" by the function represented by "pma".
+ * In other words, plug in "pma" in "mpa".
+ */
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_pw_multi_aff(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_pw_multi_aff *pma)
+{
+ isl_bool equal_params;
+
+ if (!mpa || !pma)
+ goto error;
+ equal_params = isl_space_has_equal_params(mpa->space, pma->dim);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params)
+ return isl_multi_pw_aff_pullback_pw_multi_aff_aligned(mpa, pma);
+ mpa = isl_multi_pw_aff_align_params(mpa,
+ isl_pw_multi_aff_get_space(pma));
+ pma = isl_pw_multi_aff_align_params(pma,
+ isl_multi_pw_aff_get_space(mpa));
+ return isl_multi_pw_aff_pullback_pw_multi_aff_aligned(mpa, pma);
+error:
+ isl_multi_pw_aff_free(mpa);
+ isl_pw_multi_aff_free(pma);
+ return NULL;
+}
+
+/* Apply "aff" to "mpa". The range of "mpa" needs to be compatible
+ * with the domain of "aff". The domain of the result is the same
+ * as that of "mpa".
+ * "mpa" and "aff" are assumed to have been aligned.
+ *
+ * We first extract the parametric constant from "aff", defined
+ * over the correct domain.
+ * Then we add the appropriate combinations of the members of "mpa".
+ * Finally, we add the integer divisions through recursive calls.
+ */
+static __isl_give isl_pw_aff *isl_multi_pw_aff_apply_aff_aligned(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_aff *aff)
+{
+ int i;
+ isl_size n_in, n_div, n_mpa_in;
+ isl_space *space;
+ isl_val *v;
+ isl_pw_aff *pa;
+ isl_aff *tmp;
+
+ n_in = isl_aff_dim(aff, isl_dim_in);
+ n_div = isl_aff_dim(aff, isl_dim_div);
+ n_mpa_in = isl_multi_pw_aff_dim(mpa, isl_dim_in);
+ if (n_in < 0 || n_div < 0 || n_mpa_in < 0)
+ goto error;
+
+ space = isl_space_domain(isl_multi_pw_aff_get_space(mpa));
+ tmp = isl_aff_copy(aff);
+ tmp = isl_aff_drop_dims(tmp, isl_dim_div, 0, n_div);
+ tmp = isl_aff_drop_dims(tmp, isl_dim_in, 0, n_in);
+ tmp = isl_aff_add_dims(tmp, isl_dim_in, n_mpa_in);
+ tmp = isl_aff_reset_domain_space(tmp, space);
+ pa = isl_pw_aff_from_aff(tmp);
+
+ for (i = 0; i < n_in; ++i) {
+ isl_pw_aff *pa_i;
+
+ if (!isl_aff_involves_dims(aff, isl_dim_in, i, 1))
+ continue;
+ v = isl_aff_get_coefficient_val(aff, isl_dim_in, i);
+ pa_i = isl_multi_pw_aff_get_pw_aff(mpa, i);
+ pa_i = isl_pw_aff_scale_val(pa_i, v);
+ pa = isl_pw_aff_add(pa, pa_i);
+ }
+
+ for (i = 0; i < n_div; ++i) {
+ isl_aff *div;
+ isl_pw_aff *pa_i;
+
+ if (!isl_aff_involves_dims(aff, isl_dim_div, i, 1))
+ continue;
+ div = isl_aff_get_div(aff, i);
+ pa_i = isl_multi_pw_aff_apply_aff_aligned(
+ isl_multi_pw_aff_copy(mpa), div);
+ pa_i = isl_pw_aff_floor(pa_i);
+ v = isl_aff_get_coefficient_val(aff, isl_dim_div, i);
+ pa_i = isl_pw_aff_scale_val(pa_i, v);
+ pa = isl_pw_aff_add(pa, pa_i);
+ }
+
+ isl_multi_pw_aff_free(mpa);
+ isl_aff_free(aff);
+
+ return pa;
+error:
+ isl_multi_pw_aff_free(mpa);
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Apply "aff" to "mpa". The range of "mpa" needs to be compatible
+ * with the domain of "aff". The domain of the result is the same
+ * as that of "mpa".
+ */
+__isl_give isl_pw_aff *isl_multi_pw_aff_apply_aff(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_aff *aff)
+{
+ isl_bool equal_params;
+
+ if (!aff || !mpa)
+ goto error;
+ equal_params = isl_space_has_equal_params(aff->ls->dim, mpa->space);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params)
+ return isl_multi_pw_aff_apply_aff_aligned(mpa, aff);
+
+ aff = isl_aff_align_params(aff, isl_multi_pw_aff_get_space(mpa));
+ mpa = isl_multi_pw_aff_align_params(mpa, isl_aff_get_space(aff));
+
+ return isl_multi_pw_aff_apply_aff_aligned(mpa, aff);
+error:
+ isl_aff_free(aff);
+ isl_multi_pw_aff_free(mpa);
+ return NULL;
+}
+
+/* Apply "pa" to "mpa". The range of "mpa" needs to be compatible
+ * with the domain of "pa". The domain of the result is the same
+ * as that of "mpa".
+ * "mpa" and "pa" are assumed to have been aligned.
+ *
+ * We consider each piece in turn. Note that the domains of the
+ * pieces are assumed to be disjoint and they remain disjoint
+ * after taking the preimage (over the same function).
+ */
+static __isl_give isl_pw_aff *isl_multi_pw_aff_apply_pw_aff_aligned(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_pw_aff *pa)
+{
+ isl_space *space;
+ isl_pw_aff *res;
+ int i;
+
+ if (!mpa || !pa)
+ goto error;
+
+ space = isl_space_join(isl_multi_pw_aff_get_space(mpa),
+ isl_pw_aff_get_space(pa));
+ res = isl_pw_aff_empty(space);
+
+ for (i = 0; i < pa->n; ++i) {
+ isl_pw_aff *pa_i;
+ isl_set *domain;
+
+ pa_i = isl_multi_pw_aff_apply_aff_aligned(
+ isl_multi_pw_aff_copy(mpa),
+ isl_aff_copy(pa->p[i].aff));
+ domain = isl_set_copy(pa->p[i].set);
+ domain = isl_set_preimage_multi_pw_aff(domain,
+ isl_multi_pw_aff_copy(mpa));
+ pa_i = isl_pw_aff_intersect_domain(pa_i, domain);
+ res = isl_pw_aff_add_disjoint(res, pa_i);
+ }
+
+ isl_pw_aff_free(pa);
+ isl_multi_pw_aff_free(mpa);
+ return res;
+error:
+ isl_pw_aff_free(pa);
+ isl_multi_pw_aff_free(mpa);
+ return NULL;
+}
+
+/* Apply "pa" to "mpa". The range of "mpa" needs to be compatible
+ * with the domain of "pa". The domain of the result is the same
+ * as that of "mpa".
+ */
+__isl_give isl_pw_aff *isl_multi_pw_aff_apply_pw_aff(
+ __isl_take isl_multi_pw_aff *mpa, __isl_take isl_pw_aff *pa)
+{
+ isl_bool equal_params;
+
+ if (!pa || !mpa)
+ goto error;
+ equal_params = isl_space_has_equal_params(pa->dim, mpa->space);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params)
+ return isl_multi_pw_aff_apply_pw_aff_aligned(mpa, pa);
+
+ pa = isl_pw_aff_align_params(pa, isl_multi_pw_aff_get_space(mpa));
+ mpa = isl_multi_pw_aff_align_params(mpa, isl_pw_aff_get_space(pa));
+
+ return isl_multi_pw_aff_apply_pw_aff_aligned(mpa, pa);
+error:
+ isl_pw_aff_free(pa);
+ isl_multi_pw_aff_free(mpa);
+ return NULL;
+}
+
+/* Compute the pullback of "pa" by the function represented by "mpa".
+ * In other words, plug in "mpa" in "pa".
+ * "pa" and "mpa" are assumed to have been aligned.
+ *
+ * The pullback is computed by applying "pa" to "mpa".
+ */
+static __isl_give isl_pw_aff *isl_pw_aff_pullback_multi_pw_aff_aligned(
+ __isl_take isl_pw_aff *pa, __isl_take isl_multi_pw_aff *mpa)
+{
+ return isl_multi_pw_aff_apply_pw_aff_aligned(mpa, pa);
+}
+
+/* Compute the pullback of "pa" by the function represented by "mpa".
+ * In other words, plug in "mpa" in "pa".
+ *
+ * The pullback is computed by applying "pa" to "mpa".
+ */
+__isl_give isl_pw_aff *isl_pw_aff_pullback_multi_pw_aff(
+ __isl_take isl_pw_aff *pa, __isl_take isl_multi_pw_aff *mpa)
+{
+ return isl_multi_pw_aff_apply_pw_aff(mpa, pa);
+}
+
+/* Compute the pullback of "mpa1" by the function represented by "mpa2".
+ * In other words, plug in "mpa2" in "mpa1".
+ *
+ * We pullback each member of "mpa1" in turn.
+ *
+ * If "mpa1" has an explicit domain, then it is this domain
+ * that needs to undergo a pullback instead, i.e., a preimage.
+ */
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_pw_aff(
+ __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2)
+{
+ int i;
+ isl_space *space = NULL;
+
+ isl_multi_pw_aff_align_params_bin(&mpa1, &mpa2);
+ mpa1 = isl_multi_pw_aff_cow(mpa1);
+ if (!mpa1 || !mpa2)
+ goto error;
+
+ space = isl_space_join(isl_multi_pw_aff_get_space(mpa2),
+ isl_multi_pw_aff_get_space(mpa1));
+
+ for (i = 0; i < mpa1->n; ++i) {
+ mpa1->u.p[i] = isl_pw_aff_pullback_multi_pw_aff_aligned(
+ mpa1->u.p[i], isl_multi_pw_aff_copy(mpa2));
+ if (!mpa1->u.p[i])
+ goto error;
+ }
+
+ if (isl_multi_pw_aff_has_explicit_domain(mpa1)) {
+ mpa1->u.dom = isl_set_preimage_multi_pw_aff(mpa1->u.dom,
+ isl_multi_pw_aff_copy(mpa2));
+ if (!mpa1->u.dom)
+ goto error;
+ }
+ mpa1 = isl_multi_pw_aff_reset_space(mpa1, space);
+
+ isl_multi_pw_aff_free(mpa2);
+ return mpa1;
+error:
+ isl_space_free(space);
+ isl_multi_pw_aff_free(mpa1);
+ isl_multi_pw_aff_free(mpa2);
+ return NULL;
+}
+
+/* Align the parameters of "mpa1" and "mpa2", check that the ranges
+ * of "mpa1" and "mpa2" live in the same space, construct map space
+ * between the domain spaces of "mpa1" and "mpa2" and call "order"
+ * with this map space as extract argument.
+ */
+static __isl_give isl_map *isl_multi_pw_aff_order_map(
+ __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2,
+ __isl_give isl_map *(*order)(__isl_keep isl_multi_pw_aff *mpa1,
+ __isl_keep isl_multi_pw_aff *mpa2, __isl_take isl_space *space))
+{
+ int match;
+ isl_space *space1, *space2;
+ isl_map *res;
+
+ mpa1 = isl_multi_pw_aff_align_params(mpa1,
+ isl_multi_pw_aff_get_space(mpa2));
+ mpa2 = isl_multi_pw_aff_align_params(mpa2,
+ isl_multi_pw_aff_get_space(mpa1));
+ if (!mpa1 || !mpa2)
+ goto error;
+ match = isl_space_tuple_is_equal(mpa1->space, isl_dim_out,
+ mpa2->space, isl_dim_out);
+ if (match < 0)
+ goto error;
+ if (!match)
+ isl_die(isl_multi_pw_aff_get_ctx(mpa1), isl_error_invalid,
+ "range spaces don't match", goto error);
+ space1 = isl_space_domain(isl_multi_pw_aff_get_space(mpa1));
+ space2 = isl_space_domain(isl_multi_pw_aff_get_space(mpa2));
+ space1 = isl_space_map_from_domain_and_range(space1, space2);
+
+ res = order(mpa1, mpa2, space1);
+ isl_multi_pw_aff_free(mpa1);
+ isl_multi_pw_aff_free(mpa2);
+ return res;
+error:
+ isl_multi_pw_aff_free(mpa1);
+ isl_multi_pw_aff_free(mpa2);
+ return NULL;
+}
+
+/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2"
+ * where the function values are equal. "space" is the space of the result.
+ * The parameters of "mpa1" and "mpa2" are assumed to have been aligned.
+ *
+ * "mpa1" and "mpa2" are equal when each of the pairs of elements
+ * in the sequences are equal.
+ */
+static __isl_give isl_map *isl_multi_pw_aff_eq_map_on_space(
+ __isl_keep isl_multi_pw_aff *mpa1, __isl_keep isl_multi_pw_aff *mpa2,
+ __isl_take isl_space *space)
+{
+ int i;
+ isl_size n;
+ isl_map *res;
+
+ n = isl_multi_pw_aff_dim(mpa1, isl_dim_out);
+ if (n < 0)
+ space = isl_space_free(space);
+ res = isl_map_universe(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa1, *pa2;
+ isl_map *map;
+
+ pa1 = isl_multi_pw_aff_get_pw_aff(mpa1, i);
+ pa2 = isl_multi_pw_aff_get_pw_aff(mpa2, i);
+ map = isl_pw_aff_eq_map(pa1, pa2);
+ res = isl_map_intersect(res, map);
+ }
+
+ return res;
+}
+
+/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2"
+ * where the function values are equal.
+ */
+__isl_give isl_map *isl_multi_pw_aff_eq_map(__isl_take isl_multi_pw_aff *mpa1,
+ __isl_take isl_multi_pw_aff *mpa2)
+{
+ return isl_multi_pw_aff_order_map(mpa1, mpa2,
+ &isl_multi_pw_aff_eq_map_on_space);
+}
+
+/* Intersect "map" with the result of applying "order"
+ * on two copies of "mpa".
+ */
+static __isl_give isl_map *isl_map_order_at_multi_pw_aff(
+ __isl_take isl_map *map, __isl_take isl_multi_pw_aff *mpa,
+ __isl_give isl_map *(*order)(__isl_take isl_multi_pw_aff *mpa1,
+ __isl_take isl_multi_pw_aff *mpa2))
+{
+ return isl_map_intersect(map, order(mpa, isl_multi_pw_aff_copy(mpa)));
+}
+
+/* Return the subset of "map" where the domain and the range
+ * have equal "mpa" values.
+ */
+__isl_give isl_map *isl_map_eq_at_multi_pw_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *mpa)
+{
+ return isl_map_order_at_multi_pw_aff(map, mpa,
+ &isl_multi_pw_aff_eq_map);
+}
+
+/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2"
+ * where the function values of "mpa1" lexicographically satisfies
+ * "strict_base"/"base" compared to that of "mpa2".
+ * "space" is the space of the result.
+ * The parameters of "mpa1" and "mpa2" are assumed to have been aligned.
+ *
+ * "mpa1" lexicographically satisfies "strict_base"/"base" compared to "mpa2"
+ * if, for some i, the i-th element of "mpa1" satisfies "strict_base"/"base"
+ * when compared to the i-th element of "mpa2" while all previous elements are
+ * pairwise equal.
+ * In particular, if i corresponds to the final elements
+ * then they need to satisfy "base", while "strict_base" needs to be satisfied
+ * for other values of i.
+ * If "base" is a strict order, then "base" and "strict_base" are the same.
+ */
+static __isl_give isl_map *isl_multi_pw_aff_lex_map_on_space(
+ __isl_keep isl_multi_pw_aff *mpa1, __isl_keep isl_multi_pw_aff *mpa2,
+ __isl_give isl_map *(*strict_base)(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2),
+ __isl_give isl_map *(*base)(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2),
+ __isl_take isl_space *space)
+{
+ int i;
+ isl_size n;
+ isl_map *res, *rest;
+
+ n = isl_multi_pw_aff_dim(mpa1, isl_dim_out);
+ if (n < 0)
+ space = isl_space_free(space);
+ res = isl_map_empty(isl_space_copy(space));
+ rest = isl_map_universe(space);
+
+ for (i = 0; i < n; ++i) {
+ int last;
+ isl_pw_aff *pa1, *pa2;
+ isl_map *map;
+
+ last = i == n - 1;
+
+ pa1 = isl_multi_pw_aff_get_pw_aff(mpa1, i);
+ pa2 = isl_multi_pw_aff_get_pw_aff(mpa2, i);
+ map = last ? base(pa1, pa2) : strict_base(pa1, pa2);
+ map = isl_map_intersect(map, isl_map_copy(rest));
+ res = isl_map_union(res, map);
+
+ if (last)
+ continue;
+
+ pa1 = isl_multi_pw_aff_get_pw_aff(mpa1, i);
+ pa2 = isl_multi_pw_aff_get_pw_aff(mpa2, i);
+ map = isl_pw_aff_eq_map(pa1, pa2);
+ rest = isl_map_intersect(rest, map);
+ }
+
+ isl_map_free(rest);
+ return res;
+}
+
+#undef ORDER
+#define ORDER le
+#undef STRICT_ORDER
+#define STRICT_ORDER lt
+#include "isl_aff_lex_templ.c"
+
+#undef ORDER
+#define ORDER lt
+#undef STRICT_ORDER
+#define STRICT_ORDER lt
+#include "isl_aff_lex_templ.c"
+
+#undef ORDER
+#define ORDER ge
+#undef STRICT_ORDER
+#define STRICT_ORDER gt
+#include "isl_aff_lex_templ.c"
+
+#undef ORDER
+#define ORDER gt
+#undef STRICT_ORDER
+#define STRICT_ORDER gt
+#include "isl_aff_lex_templ.c"
+
+/* Compare two isl_affs.
+ *
+ * Return -1 if "aff1" is "smaller" than "aff2", 1 if "aff1" is "greater"
+ * than "aff2" and 0 if they are equal.
+ *
+ * The order is fairly arbitrary. We do consider expressions that only involve
+ * earlier dimensions as "smaller".
+ */
+int isl_aff_plain_cmp(__isl_keep isl_aff *aff1, __isl_keep isl_aff *aff2)
+{
+ int cmp;
+ int last1, last2;
+
+ if (aff1 == aff2)
+ return 0;
+
+ if (!aff1)
+ return -1;
+ if (!aff2)
+ return 1;
+
+ cmp = isl_local_space_cmp(aff1->ls, aff2->ls);
+ if (cmp != 0)
+ return cmp;
+
+ last1 = isl_seq_last_non_zero(aff1->v->el + 1, aff1->v->size - 1);
+ last2 = isl_seq_last_non_zero(aff2->v->el + 1, aff1->v->size - 1);
+ if (last1 != last2)
+ return last1 - last2;
+
+ return isl_seq_cmp(aff1->v->el, aff2->v->el, aff1->v->size);
+}
+
+/* Compare two isl_pw_affs.
+ *
+ * Return -1 if "pa1" is "smaller" than "pa2", 1 if "pa1" is "greater"
+ * than "pa2" and 0 if they are equal.
+ *
+ * The order is fairly arbitrary. We do consider expressions that only involve
+ * earlier dimensions as "smaller".
+ */
+int isl_pw_aff_plain_cmp(__isl_keep isl_pw_aff *pa1,
+ __isl_keep isl_pw_aff *pa2)
+{
+ int i;
+ int cmp;
+
+ if (pa1 == pa2)
+ return 0;
+
+ if (!pa1)
+ return -1;
+ if (!pa2)
+ return 1;
+
+ cmp = isl_space_cmp(pa1->dim, pa2->dim);
+ if (cmp != 0)
+ return cmp;
+
+ if (pa1->n != pa2->n)
+ return pa1->n - pa2->n;
+
+ for (i = 0; i < pa1->n; ++i) {
+ cmp = isl_set_plain_cmp(pa1->p[i].set, pa2->p[i].set);
+ if (cmp != 0)
+ return cmp;
+ cmp = isl_aff_plain_cmp(pa1->p[i].aff, pa2->p[i].aff);
+ if (cmp != 0)
+ return cmp;
+ }
+
+ return 0;
+}
+
+/* Return a piecewise affine expression that is equal to "v" on "domain".
+ */
+__isl_give isl_pw_aff *isl_pw_aff_val_on_domain(__isl_take isl_set *domain,
+ __isl_take isl_val *v)
+{
+ isl_space *space;
+ isl_local_space *ls;
+ isl_aff *aff;
+
+ space = isl_set_get_space(domain);
+ ls = isl_local_space_from_space(space);
+ aff = isl_aff_val_on_domain(ls, v);
+
+ return isl_pw_aff_alloc(domain, aff);
+}
+
+/* Return a piecewise affine expression that is equal to the parameter
+ * with identifier "id" on "domain".
+ */
+__isl_give isl_pw_aff *isl_pw_aff_param_on_domain_id(
+ __isl_take isl_set *domain, __isl_take isl_id *id)
+{
+ isl_space *space;
+ isl_aff *aff;
+
+ space = isl_set_get_space(domain);
+ space = isl_space_add_param_id(space, isl_id_copy(id));
+ domain = isl_set_align_params(domain, isl_space_copy(space));
+ aff = isl_aff_param_on_domain_space_id(space, id);
+
+ return isl_pw_aff_alloc(domain, aff);
+}
+
+/* Return a multi affine expression that is equal to "mv" on domain
+ * space "space".
+ */
+__isl_give isl_multi_aff *isl_multi_aff_multi_val_on_domain_space(
+ __isl_take isl_space *space, __isl_take isl_multi_val *mv)
+{
+ int i;
+ isl_size n;
+ isl_space *space2;
+ isl_local_space *ls;
+ isl_multi_aff *ma;
+
+ n = isl_multi_val_dim(mv, isl_dim_set);
+ if (!space || n < 0)
+ goto error;
+
+ space2 = isl_multi_val_get_space(mv);
+ space2 = isl_space_align_params(space2, isl_space_copy(space));
+ space = isl_space_align_params(space, isl_space_copy(space2));
+ space = isl_space_map_from_domain_and_range(space, space2);
+ ma = isl_multi_aff_alloc(isl_space_copy(space));
+ ls = isl_local_space_from_space(isl_space_domain(space));
+ for (i = 0; i < n; ++i) {
+ isl_val *v;
+ isl_aff *aff;
+
+ v = isl_multi_val_get_val(mv, i);
+ aff = isl_aff_val_on_domain(isl_local_space_copy(ls), v);
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ }
+ isl_local_space_free(ls);
+
+ isl_multi_val_free(mv);
+ return ma;
+error:
+ isl_space_free(space);
+ isl_multi_val_free(mv);
+ return NULL;
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_multi_aff *isl_multi_aff_multi_val_on_space(
+ __isl_take isl_space *space, __isl_take isl_multi_val *mv)
+{
+ return isl_multi_aff_multi_val_on_domain_space(space, mv);
+}
+
+/* This function performs the same operation as
+ * isl_multi_aff_multi_val_on_domain_space,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give isl_multi_aff *isl_space_multi_aff_on_domain_multi_val(
+ __isl_take isl_space *space, __isl_take isl_multi_val *mv)
+{
+ return isl_multi_aff_multi_val_on_domain_space(space, mv);
+}
+
+/* Return a piecewise multi-affine expression
+ * that is equal to "mv" on "domain".
+ */
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_multi_val_on_domain(
+ __isl_take isl_set *domain, __isl_take isl_multi_val *mv)
+{
+ isl_space *space;
+ isl_multi_aff *ma;
+
+ space = isl_set_get_space(domain);
+ ma = isl_multi_aff_multi_val_on_space(space, mv);
+
+ return isl_pw_multi_aff_alloc(domain, ma);
+}
+
+/* This function performs the same operation as
+ * isl_pw_multi_aff_multi_val_on_domain,
+ * but is considered as a function on an isl_set when exported.
+ */
+__isl_give isl_pw_multi_aff *isl_set_pw_multi_aff_on_domain_multi_val(
+ __isl_take isl_set *domain, __isl_take isl_multi_val *mv)
+{
+ return isl_pw_multi_aff_multi_val_on_domain(domain, mv);
+}
+
+/* Internal data structure for isl_union_pw_multi_aff_multi_val_on_domain.
+ * mv is the value that should be attained on each domain set
+ * res collects the results
+ */
+struct isl_union_pw_multi_aff_multi_val_on_domain_data {
+ isl_multi_val *mv;
+ isl_union_pw_multi_aff *res;
+};
+
+/* Create an isl_pw_multi_aff equal to data->mv on "domain"
+ * and add it to data->res.
+ */
+static isl_stat pw_multi_aff_multi_val_on_domain(__isl_take isl_set *domain,
+ void *user)
+{
+ struct isl_union_pw_multi_aff_multi_val_on_domain_data *data = user;
+ isl_pw_multi_aff *pma;
+ isl_multi_val *mv;
+
+ mv = isl_multi_val_copy(data->mv);
+ pma = isl_pw_multi_aff_multi_val_on_domain(domain, mv);
+ data->res = isl_union_pw_multi_aff_add_pw_multi_aff(data->res, pma);
+
+ return data->res ? isl_stat_ok : isl_stat_error;
+}
+
+/* Return a union piecewise multi-affine expression
+ * that is equal to "mv" on "domain".
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_multi_val_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_multi_val *mv)
+{
+ struct isl_union_pw_multi_aff_multi_val_on_domain_data data;
+ isl_space *space;
+
+ space = isl_union_set_get_space(domain);
+ data.res = isl_union_pw_multi_aff_empty(space);
+ data.mv = mv;
+ if (isl_union_set_foreach_set(domain,
+ &pw_multi_aff_multi_val_on_domain, &data) < 0)
+ data.res = isl_union_pw_multi_aff_free(data.res);
+ isl_union_set_free(domain);
+ isl_multi_val_free(mv);
+ return data.res;
+}
+
+/* Compute the pullback of data->pma by the function represented by "pma2",
+ * provided the spaces match, and add the results to data->res.
+ */
+static isl_stat pullback_entry(__isl_take isl_pw_multi_aff *pma2, void *user)
+{
+ struct isl_union_pw_multi_aff_bin_data *data = user;
+
+ if (!isl_space_tuple_is_equal(data->pma->dim, isl_dim_in,
+ pma2->dim, isl_dim_out)) {
+ isl_pw_multi_aff_free(pma2);
+ return isl_stat_ok;
+ }
+
+ pma2 = isl_pw_multi_aff_pullback_pw_multi_aff(
+ isl_pw_multi_aff_copy(data->pma), pma2);
+
+ data->res = isl_union_pw_multi_aff_add_pw_multi_aff(data->res, pma2);
+ if (!data->res)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Compute the pullback of "upma1" by the function represented by "upma2".
+ */
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_pullback_union_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2)
+{
+ return bin_op(upma1, upma2, &pullback_entry);
+}
+
+/* Apply "upma2" to "upma1".
+ *
+ * That is, compute the pullback of "upma2" by "upma1".
+ */
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_apply_union_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2)
+{
+ return isl_union_pw_multi_aff_pullback_union_pw_multi_aff(upma2, upma1);
+}
+
+#undef TYPE
+#define TYPE isl_pw_multi_aff
+static
+#include "isl_copy_tuple_id_templ.c"
+
+/* Given a function "pma1" of the form A[B -> C] -> D and
+ * a function "pma2" of the form E -> B,
+ * replace the domain of the wrapped relation inside the domain of "pma1"
+ * by the preimage with respect to "pma2".
+ * In other words, plug in "pma2" in this nested domain.
+ * The result is of the form A[E -> C] -> D.
+ *
+ * In particular, extend E -> B to A[E -> C] -> A[B -> C] and
+ * plug that into "pma1".
+ */
+__isl_give isl_pw_multi_aff *
+isl_pw_multi_aff_preimage_domain_wrapped_domain_pw_multi_aff(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2)
+{
+ isl_space *pma1_space, *pma2_space;
+ isl_space *space;
+ isl_pw_multi_aff *id;
+
+ pma1_space = isl_pw_multi_aff_peek_space(pma1);
+ pma2_space = isl_pw_multi_aff_peek_space(pma2);
+
+ if (isl_space_check_domain_is_wrapping(pma1_space) < 0)
+ goto error;
+ if (isl_space_check_wrapped_tuple_is_equal(pma1_space,
+ isl_dim_in, isl_dim_in, pma2_space, isl_dim_out) < 0)
+ goto error;
+
+ space = isl_space_domain(isl_space_copy(pma1_space));
+ space = isl_space_range(isl_space_unwrap(space));
+ id = isl_pw_multi_aff_identity_on_domain_space(space);
+ pma2 = isl_pw_multi_aff_product(pma2, id);
+
+ pma2 = isl_pw_multi_aff_copy_tuple_id(pma2, isl_dim_in,
+ pma1_space, isl_dim_in);
+ pma2 = isl_pw_multi_aff_copy_tuple_id(pma2, isl_dim_out,
+ pma1_space, isl_dim_in);
+
+ return isl_pw_multi_aff_pullback_pw_multi_aff(pma1, pma2);
+error:
+ isl_pw_multi_aff_free(pma1);
+ isl_pw_multi_aff_free(pma2);
+ return NULL;
+}
+
+/* If data->pma and "pma2" are such that
+ * data->pma is of the form A[B -> C] -> D and
+ * "pma2" is of the form E -> B,
+ * then replace the domain of the wrapped relation
+ * inside the domain of data->pma by the preimage with respect to "pma2" and
+ * add the result to data->res.
+ */
+static isl_stat preimage_domain_wrapped_domain_entry(
+ __isl_take isl_pw_multi_aff *pma2, void *user)
+{
+ struct isl_union_pw_multi_aff_bin_data *data = user;
+ isl_space *pma1_space, *pma2_space;
+ isl_bool match;
+
+ pma1_space = isl_pw_multi_aff_peek_space(data->pma);
+ pma2_space = isl_pw_multi_aff_peek_space(pma2);
+
+ match = isl_space_domain_is_wrapping(pma1_space);
+ if (match >= 0 && match)
+ match = isl_space_wrapped_tuple_is_equal(pma1_space, isl_dim_in,
+ isl_dim_in, pma2_space, isl_dim_out);
+ if (match < 0 || !match) {
+ isl_pw_multi_aff_free(pma2);
+ return match < 0 ? isl_stat_error : isl_stat_ok;
+ }
+
+ pma2 = isl_pw_multi_aff_preimage_domain_wrapped_domain_pw_multi_aff(
+ isl_pw_multi_aff_copy(data->pma), pma2);
+
+ data->res = isl_union_pw_multi_aff_add_pw_multi_aff(data->res, pma2);
+
+ return isl_stat_non_null(data->res);
+}
+
+/* For each pair of functions A[B -> C] -> D in "upma1" and
+ * E -> B in "upma2",
+ * replace the domain of the wrapped relation inside the domain of the first
+ * by the preimage with respect to the second and collect the results.
+ * In other words, plug in the second function in this nested domain.
+ * The results are of the form A[E -> C] -> D.
+ */
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_preimage_domain_wrapped_domain_union_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma1,
+ __isl_take isl_union_pw_multi_aff *upma2)
+{
+ return bin_op(upma1, upma2, &preimage_domain_wrapped_domain_entry);
+}
+
+/* Check that the domain space of "upa" matches "space".
+ *
+ * This function is called from isl_multi_union_pw_aff_set_union_pw_aff and
+ * can in principle never fail since the space "space" is that
+ * of the isl_multi_union_pw_aff and is a set space such that
+ * there is no domain space to match.
+ *
+ * We check the parameters and double-check that "space" is
+ * indeed that of a set.
+ */
+static isl_stat isl_union_pw_aff_check_match_domain_space(
+ __isl_keep isl_union_pw_aff *upa, __isl_keep isl_space *space)
+{
+ isl_space *upa_space;
+ isl_bool match;
+
+ if (!upa || !space)
+ return isl_stat_error;
+
+ match = isl_space_is_set(space);
+ if (match < 0)
+ return isl_stat_error;
+ if (!match)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "expecting set space", return isl_stat_error);
+
+ upa_space = isl_union_pw_aff_get_space(upa);
+ match = isl_space_has_equal_params(space, upa_space);
+ if (match < 0)
+ goto error;
+ if (!match)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "parameters don't match", goto error);
+
+ isl_space_free(upa_space);
+ return isl_stat_ok;
+error:
+ isl_space_free(upa_space);
+ return isl_stat_error;
+}
+
+/* Do the parameters of "upa" match those of "space"?
+ */
+static isl_bool isl_union_pw_aff_matching_params(
+ __isl_keep isl_union_pw_aff *upa, __isl_keep isl_space *space)
+{
+ isl_space *upa_space;
+ isl_bool match;
+
+ if (!upa || !space)
+ return isl_bool_error;
+
+ upa_space = isl_union_pw_aff_get_space(upa);
+
+ match = isl_space_has_equal_params(space, upa_space);
+
+ isl_space_free(upa_space);
+ return match;
+}
+
+/* Internal data structure for isl_union_pw_aff_reset_domain_space.
+ * space represents the new parameters.
+ * res collects the results.
+ */
+struct isl_union_pw_aff_reset_params_data {
+ isl_space *space;
+ isl_union_pw_aff *res;
+};
+
+/* Replace the parameters of "pa" by data->space and
+ * add the result to data->res.
+ */
+static isl_stat reset_params(__isl_take isl_pw_aff *pa, void *user)
+{
+ struct isl_union_pw_aff_reset_params_data *data = user;
+ isl_space *space;
+
+ space = isl_pw_aff_get_space(pa);
+ space = isl_space_replace_params(space, data->space);
+ pa = isl_pw_aff_reset_space(pa, space);
+ data->res = isl_union_pw_aff_add_pw_aff(data->res, pa);
+
+ return data->res ? isl_stat_ok : isl_stat_error;
+}
+
+/* Replace the domain space of "upa" by "space".
+ * Since a union expression does not have a (single) domain space,
+ * "space" is necessarily a parameter space.
+ *
+ * Since the order and the names of the parameters determine
+ * the hash value, we need to create a new hash table.
+ */
+static __isl_give isl_union_pw_aff *isl_union_pw_aff_reset_domain_space(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_space *space)
+{
+ struct isl_union_pw_aff_reset_params_data data = { space };
+ isl_bool match;
+
+ match = isl_union_pw_aff_matching_params(upa, space);
+ if (match < 0)
+ upa = isl_union_pw_aff_free(upa);
+ else if (match) {
+ isl_space_free(space);
+ return upa;
+ }
+
+ data.res = isl_union_pw_aff_empty(isl_space_copy(space));
+ if (isl_union_pw_aff_foreach_pw_aff(upa, &reset_params, &data) < 0)
+ data.res = isl_union_pw_aff_free(data.res);
+
+ isl_union_pw_aff_free(upa);
+ isl_space_free(space);
+ return data.res;
+}
+
+/* Return the floor of "pa".
+ */
+static __isl_give isl_pw_aff *floor_entry(__isl_take isl_pw_aff *pa, void *user)
+{
+ return isl_pw_aff_floor(pa);
+}
+
+/* Given f, return floor(f).
+ */
+__isl_give isl_union_pw_aff *isl_union_pw_aff_floor(
+ __isl_take isl_union_pw_aff *upa)
+{
+ return isl_union_pw_aff_transform_inplace(upa, &floor_entry, NULL);
+}
+
+/* Compute
+ *
+ * upa mod m = upa - m * floor(upa/m)
+ *
+ * with m an integer value.
+ */
+__isl_give isl_union_pw_aff *isl_union_pw_aff_mod_val(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_val *m)
+{
+ isl_union_pw_aff *res;
+
+ if (!upa || !m)
+ goto error;
+
+ if (!isl_val_is_int(m))
+ isl_die(isl_val_get_ctx(m), isl_error_invalid,
+ "expecting integer modulo", goto error);
+ if (!isl_val_is_pos(m))
+ isl_die(isl_val_get_ctx(m), isl_error_invalid,
+ "expecting positive modulo", goto error);
+
+ res = isl_union_pw_aff_copy(upa);
+ upa = isl_union_pw_aff_scale_down_val(upa, isl_val_copy(m));
+ upa = isl_union_pw_aff_floor(upa);
+ upa = isl_union_pw_aff_scale_val(upa, m);
+ res = isl_union_pw_aff_sub(res, upa);
+
+ return res;
+error:
+ isl_val_free(m);
+ isl_union_pw_aff_free(upa);
+ return NULL;
+}
+
+/* Internal data structure for isl_union_pw_multi_aff_get_union_pw_aff.
+ * pos is the output position that needs to be extracted.
+ * res collects the results.
+ */
+struct isl_union_pw_multi_aff_get_union_pw_aff_data {
+ int pos;
+ isl_union_pw_aff *res;
+};
+
+/* Extract an isl_pw_aff corresponding to output dimension "pos" of "pma"
+ * (assuming it has such a dimension) and add it to data->res.
+ */
+static isl_stat get_union_pw_aff(__isl_take isl_pw_multi_aff *pma, void *user)
+{
+ struct isl_union_pw_multi_aff_get_union_pw_aff_data *data = user;
+ isl_size n_out;
+ isl_pw_aff *pa;
+
+ n_out = isl_pw_multi_aff_dim(pma, isl_dim_out);
+ if (n_out < 0)
+ return isl_stat_error;
+ if (data->pos >= n_out) {
+ isl_pw_multi_aff_free(pma);
+ return isl_stat_ok;
+ }
+
+ pa = isl_pw_multi_aff_get_pw_aff(pma, data->pos);
+ isl_pw_multi_aff_free(pma);
+
+ data->res = isl_union_pw_aff_add_pw_aff(data->res, pa);
+
+ return data->res ? isl_stat_ok : isl_stat_error;
+}
+
+/* Extract an isl_union_pw_aff corresponding to
+ * output dimension "pos" of "upma".
+ */
+__isl_give isl_union_pw_aff *isl_union_pw_multi_aff_get_union_pw_aff(
+ __isl_keep isl_union_pw_multi_aff *upma, int pos)
+{
+ struct isl_union_pw_multi_aff_get_union_pw_aff_data data;
+ isl_space *space;
+
+ if (!upma)
+ return NULL;
+
+ if (pos < 0)
+ isl_die(isl_union_pw_multi_aff_get_ctx(upma), isl_error_invalid,
+ "cannot extract at negative position", return NULL);
+
+ space = isl_union_pw_multi_aff_get_space(upma);
+ data.res = isl_union_pw_aff_empty(space);
+ data.pos = pos;
+ if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma,
+ &get_union_pw_aff, &data) < 0)
+ data.res = isl_union_pw_aff_free(data.res);
+
+ return data.res;
+}
+
+/* Return a union piecewise affine expression
+ * that is equal to "aff" on "domain".
+ */
+__isl_give isl_union_pw_aff *isl_union_pw_aff_aff_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_aff *aff)
+{
+ isl_pw_aff *pa;
+
+ pa = isl_pw_aff_from_aff(aff);
+ return isl_union_pw_aff_pw_aff_on_domain(domain, pa);
+}
+
+/* Return a union piecewise affine expression
+ * that is equal to the parameter identified by "id" on "domain".
+ *
+ * Make sure the parameter appears in the space passed to
+ * isl_aff_param_on_domain_space_id.
+ */
+__isl_give isl_union_pw_aff *isl_union_pw_aff_param_on_domain_id(
+ __isl_take isl_union_set *domain, __isl_take isl_id *id)
+{
+ isl_space *space;
+ isl_aff *aff;
+
+ space = isl_union_set_get_space(domain);
+ space = isl_space_add_param_id(space, isl_id_copy(id));
+ aff = isl_aff_param_on_domain_space_id(space, id);
+ return isl_union_pw_aff_aff_on_domain(domain, aff);
+}
+
+/* Internal data structure for isl_union_pw_aff_pw_aff_on_domain.
+ * "pa" is the piecewise symbolic value that the resulting isl_union_pw_aff
+ * needs to attain.
+ * "res" collects the results.
+ */
+struct isl_union_pw_aff_pw_aff_on_domain_data {
+ isl_pw_aff *pa;
+ isl_union_pw_aff *res;
+};
+
+/* Construct a piecewise affine expression that is equal to data->pa
+ * on "domain" and add the result to data->res.
+ */
+static isl_stat pw_aff_on_domain(__isl_take isl_set *domain, void *user)
+{
+ struct isl_union_pw_aff_pw_aff_on_domain_data *data = user;
+ isl_pw_aff *pa;
+ isl_size dim;
+
+ pa = isl_pw_aff_copy(data->pa);
+ dim = isl_set_dim(domain, isl_dim_set);
+ if (dim < 0)
+ pa = isl_pw_aff_free(pa);
+ pa = isl_pw_aff_from_range(pa);
+ pa = isl_pw_aff_add_dims(pa, isl_dim_in, dim);
+ pa = isl_pw_aff_reset_domain_space(pa, isl_set_get_space(domain));
+ pa = isl_pw_aff_intersect_domain(pa, domain);
+ data->res = isl_union_pw_aff_add_pw_aff(data->res, pa);
+
+ return data->res ? isl_stat_ok : isl_stat_error;
+}
+
+/* Return a union piecewise affine expression
+ * that is equal to "pa" on "domain", assuming "domain" and "pa"
+ * have been aligned.
+ *
+ * Construct an isl_pw_aff on each of the sets in "domain" and
+ * collect the results.
+ */
+static __isl_give isl_union_pw_aff *isl_union_pw_aff_pw_aff_on_domain_aligned(
+ __isl_take isl_union_set *domain, __isl_take isl_pw_aff *pa)
+{
+ struct isl_union_pw_aff_pw_aff_on_domain_data data;
+ isl_space *space;
+
+ space = isl_union_set_get_space(domain);
+ data.res = isl_union_pw_aff_empty(space);
+ data.pa = pa;
+ if (isl_union_set_foreach_set(domain, &pw_aff_on_domain, &data) < 0)
+ data.res = isl_union_pw_aff_free(data.res);
+ isl_union_set_free(domain);
+ isl_pw_aff_free(pa);
+ return data.res;
+}
+
+/* Return a union piecewise affine expression
+ * that is equal to "pa" on "domain".
+ *
+ * Check that "pa" is a parametric expression,
+ * align the parameters if needed and call
+ * isl_union_pw_aff_pw_aff_on_domain_aligned.
+ */
+__isl_give isl_union_pw_aff *isl_union_pw_aff_pw_aff_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_pw_aff *pa)
+{
+ isl_bool is_set;
+ isl_bool equal_params;
+ isl_space *domain_space, *pa_space;
+
+ pa_space = isl_pw_aff_peek_space(pa);
+ is_set = isl_space_is_set(pa_space);
+ if (is_set < 0)
+ goto error;
+ if (!is_set)
+ isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid,
+ "expecting parametric expression", goto error);
+
+ domain_space = isl_union_set_get_space(domain);
+ pa_space = isl_pw_aff_get_space(pa);
+ equal_params = isl_space_has_equal_params(domain_space, pa_space);
+ if (equal_params >= 0 && !equal_params) {
+ isl_space *space;
+
+ space = isl_space_align_params(domain_space, pa_space);
+ pa = isl_pw_aff_align_params(pa, isl_space_copy(space));
+ domain = isl_union_set_align_params(domain, space);
+ } else {
+ isl_space_free(domain_space);
+ isl_space_free(pa_space);
+ }
+
+ if (equal_params < 0)
+ goto error;
+ return isl_union_pw_aff_pw_aff_on_domain_aligned(domain, pa);
+error:
+ isl_union_set_free(domain);
+ isl_pw_aff_free(pa);
+ return NULL;
+}
+
+/* Internal data structure for isl_union_pw_aff_val_on_domain.
+ * "v" is the value that the resulting isl_union_pw_aff needs to attain.
+ * "res" collects the results.
+ */
+struct isl_union_pw_aff_val_on_domain_data {
+ isl_val *v;
+ isl_union_pw_aff *res;
+};
+
+/* Construct a piecewise affine expression that is equal to data->v
+ * on "domain" and add the result to data->res.
+ */
+static isl_stat pw_aff_val_on_domain(__isl_take isl_set *domain, void *user)
+{
+ struct isl_union_pw_aff_val_on_domain_data *data = user;
+ isl_pw_aff *pa;
+ isl_val *v;
+
+ v = isl_val_copy(data->v);
+ pa = isl_pw_aff_val_on_domain(domain, v);
+ data->res = isl_union_pw_aff_add_pw_aff(data->res, pa);
+
+ return data->res ? isl_stat_ok : isl_stat_error;
+}
+
+/* Return a union piecewise affine expression
+ * that is equal to "v" on "domain".
+ *
+ * Construct an isl_pw_aff on each of the sets in "domain" and
+ * collect the results.
+ */
+__isl_give isl_union_pw_aff *isl_union_pw_aff_val_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_val *v)
+{
+ struct isl_union_pw_aff_val_on_domain_data data;
+ isl_space *space;
+
+ space = isl_union_set_get_space(domain);
+ data.res = isl_union_pw_aff_empty(space);
+ data.v = v;
+ if (isl_union_set_foreach_set(domain, &pw_aff_val_on_domain, &data) < 0)
+ data.res = isl_union_pw_aff_free(data.res);
+ isl_union_set_free(domain);
+ isl_val_free(v);
+ return data.res;
+}
+
+/* Construct a piecewise multi affine expression
+ * that is equal to "pa" and add it to upma.
+ */
+static isl_stat pw_multi_aff_from_pw_aff_entry(__isl_take isl_pw_aff *pa,
+ void *user)
+{
+ isl_union_pw_multi_aff **upma = user;
+ isl_pw_multi_aff *pma;
+
+ pma = isl_pw_multi_aff_from_pw_aff(pa);
+ *upma = isl_union_pw_multi_aff_add_pw_multi_aff(*upma, pma);
+
+ return *upma ? isl_stat_ok : isl_stat_error;
+}
+
+/* Construct and return a union piecewise multi affine expression
+ * that is equal to the given union piecewise affine expression.
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_from_union_pw_aff(
+ __isl_take isl_union_pw_aff *upa)
+{
+ isl_space *space;
+ isl_union_pw_multi_aff *upma;
+
+ if (!upa)
+ return NULL;
+
+ space = isl_union_pw_aff_get_space(upa);
+ upma = isl_union_pw_multi_aff_empty(space);
+
+ if (isl_union_pw_aff_foreach_pw_aff(upa,
+ &pw_multi_aff_from_pw_aff_entry, &upma) < 0)
+ upma = isl_union_pw_multi_aff_free(upma);
+
+ isl_union_pw_aff_free(upa);
+ return upma;
+}
+
+/* Compute the set of elements in the domain of "pa" where it is zero and
+ * add this set to "uset".
+ */
+static isl_stat zero_union_set(__isl_take isl_pw_aff *pa, void *user)
+{
+ isl_union_set **uset = (isl_union_set **)user;
+
+ *uset = isl_union_set_add_set(*uset, isl_pw_aff_zero_set(pa));
+
+ return *uset ? isl_stat_ok : isl_stat_error;
+}
+
+/* Return a union set containing those elements in the domain
+ * of "upa" where it is zero.
+ */
+__isl_give isl_union_set *isl_union_pw_aff_zero_union_set(
+ __isl_take isl_union_pw_aff *upa)
+{
+ isl_union_set *zero;
+
+ zero = isl_union_set_empty(isl_union_pw_aff_get_space(upa));
+ if (isl_union_pw_aff_foreach_pw_aff(upa, &zero_union_set, &zero) < 0)
+ zero = isl_union_set_free(zero);
+
+ isl_union_pw_aff_free(upa);
+ return zero;
+}
+
+/* Internal data structure for isl_union_pw_aff_bind_id,
+ * storing the parameter that needs to be bound and
+ * the accumulated results.
+ */
+struct isl_bind_id_data {
+ isl_id *id;
+ isl_union_set *bound;
+};
+
+/* Bind the piecewise affine function "pa" to the parameter data->id,
+ * adding the resulting elements in the domain where the expression
+ * is equal to the parameter to data->bound.
+ */
+static isl_stat bind_id(__isl_take isl_pw_aff *pa, void *user)
+{
+ struct isl_bind_id_data *data = user;
+ isl_set *bound;
+
+ bound = isl_pw_aff_bind_id(pa, isl_id_copy(data->id));
+ data->bound = isl_union_set_add_set(data->bound, bound);
+
+ return data->bound ? isl_stat_ok : isl_stat_error;
+}
+
+/* Bind the union piecewise affine function "upa" to the parameter "id",
+ * returning the elements in the domain where the expression
+ * is equal to the parameter.
+ */
+__isl_give isl_union_set *isl_union_pw_aff_bind_id(
+ __isl_take isl_union_pw_aff *upa, __isl_take isl_id *id)
+{
+ struct isl_bind_id_data data = { id };
+
+ data.bound = isl_union_set_empty(isl_union_pw_aff_get_space(upa));
+ if (isl_union_pw_aff_foreach_pw_aff(upa, &bind_id, &data) < 0)
+ data.bound = isl_union_set_free(data.bound);
+
+ isl_union_pw_aff_free(upa);
+ isl_id_free(id);
+ return data.bound;
+}
+
+/* Internal data structure for isl_union_pw_aff_pullback_union_pw_multi_aff.
+ * upma is the function that is plugged in.
+ * pa is the current part of the function in which upma is plugged in.
+ * res collects the results.
+ */
+struct isl_union_pw_aff_pullback_upma_data {
+ isl_union_pw_multi_aff *upma;
+ isl_pw_aff *pa;
+ isl_union_pw_aff *res;
+};
+
+/* Check if "pma" can be plugged into data->pa.
+ * If so, perform the pullback and add the result to data->res.
+ */
+static isl_stat pa_pb_pma(__isl_take isl_pw_multi_aff *pma, void *user)
+{
+ struct isl_union_pw_aff_pullback_upma_data *data = user;
+ isl_pw_aff *pa;
+
+ if (!isl_space_tuple_is_equal(data->pa->dim, isl_dim_in,
+ pma->dim, isl_dim_out)) {
+ isl_pw_multi_aff_free(pma);
+ return isl_stat_ok;
+ }
+
+ pa = isl_pw_aff_copy(data->pa);
+ pa = isl_pw_aff_pullback_pw_multi_aff(pa, pma);
+
+ data->res = isl_union_pw_aff_add_pw_aff(data->res, pa);
+
+ return data->res ? isl_stat_ok : isl_stat_error;
+}
+
+/* Check if any of the elements of data->upma can be plugged into pa,
+ * add if so add the result to data->res.
+ */
+static isl_stat upa_pb_upma(__isl_take isl_pw_aff *pa, void *user)
+{
+ struct isl_union_pw_aff_pullback_upma_data *data = user;
+ isl_stat r;
+
+ data->pa = pa;
+ r = isl_union_pw_multi_aff_foreach_pw_multi_aff(data->upma,
+ &pa_pb_pma, data);
+ isl_pw_aff_free(pa);
+
+ return r;
+}
+
+/* Compute the pullback of "upa" by the function represented by "upma".
+ * In other words, plug in "upma" in "upa". The result contains
+ * expressions defined over the domain space of "upma".
+ *
+ * Run over all pairs of elements in "upa" and "upma", perform
+ * the pullback when appropriate and collect the results.
+ * If the hash value were based on the domain space rather than
+ * the function space, then we could run through all elements
+ * of "upma" and directly pick out the corresponding element of "upa".
+ */
+__isl_give isl_union_pw_aff *isl_union_pw_aff_pullback_union_pw_multi_aff(
+ __isl_take isl_union_pw_aff *upa,
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ struct isl_union_pw_aff_pullback_upma_data data = { NULL, NULL };
+ isl_space *space;
+
+ space = isl_union_pw_multi_aff_get_space(upma);
+ upa = isl_union_pw_aff_align_params(upa, space);
+ space = isl_union_pw_aff_get_space(upa);
+ upma = isl_union_pw_multi_aff_align_params(upma, space);
+
+ if (!upa || !upma)
+ goto error;
+
+ data.upma = upma;
+ data.res = isl_union_pw_aff_alloc_same_size(upa);
+ if (isl_union_pw_aff_foreach_pw_aff(upa, &upa_pb_upma, &data) < 0)
+ data.res = isl_union_pw_aff_free(data.res);
+
+ isl_union_pw_aff_free(upa);
+ isl_union_pw_multi_aff_free(upma);
+ return data.res;
+error:
+ isl_union_pw_aff_free(upa);
+ isl_union_pw_multi_aff_free(upma);
+ return NULL;
+}
+
+#undef BASE
+#define BASE union_pw_aff
+#undef DOMBASE
+#define DOMBASE union_set
+
+#include <isl_multi_explicit_domain.c>
+#include <isl_multi_union_pw_aff_explicit_domain.c>
+#include <isl_multi_templ.c>
+#include <isl_multi_apply_set.c>
+#include <isl_multi_apply_union_set.c>
+#include <isl_multi_arith_templ.c>
+#include <isl_multi_bind_templ.c>
+#include <isl_multi_coalesce.c>
+#include <isl_multi_dim_id_templ.c>
+#include <isl_multi_floor.c>
+#include <isl_multi_from_base_templ.c>
+#include <isl_multi_gist.c>
+#include <isl_multi_align_set.c>
+#include <isl_multi_align_union_set.c>
+#include <isl_multi_intersect.c>
+#include <isl_multi_nan_templ.c>
+#include <isl_multi_tuple_id_templ.c>
+#include <isl_multi_union_add_templ.c>
+#include <isl_multi_zero_space_templ.c>
+
+/* Does "mupa" have a non-trivial explicit domain?
+ *
+ * The explicit domain, if present, is trivial if it represents
+ * an (obviously) universe parameter set.
+ */
+isl_bool isl_multi_union_pw_aff_has_non_trivial_domain(
+ __isl_keep isl_multi_union_pw_aff *mupa)
+{
+ isl_bool is_params, trivial;
+ isl_set *set;
+
+ if (!mupa)
+ return isl_bool_error;
+ if (!isl_multi_union_pw_aff_has_explicit_domain(mupa))
+ return isl_bool_false;
+ is_params = isl_union_set_is_params(mupa->u.dom);
+ if (is_params < 0 || !is_params)
+ return isl_bool_not(is_params);
+ set = isl_set_from_union_set(isl_union_set_copy(mupa->u.dom));
+ trivial = isl_set_plain_is_universe(set);
+ isl_set_free(set);
+ return isl_bool_not(trivial);
+}
+
+/* Construct a multiple union piecewise affine expression
+ * in the given space with value zero in each of the output dimensions.
+ *
+ * Since there is no canonical zero value for
+ * a union piecewise affine expression, we can only construct
+ * a zero-dimensional "zero" value.
+ */
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_zero(
+ __isl_take isl_space *space)
+{
+ isl_bool params;
+ isl_size dim;
+
+ if (!space)
+ return NULL;
+
+ params = isl_space_is_params(space);
+ if (params < 0)
+ goto error;
+ if (params)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "expecting proper set space", goto error);
+ if (!isl_space_is_set(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "expecting set space", goto error);
+ dim = isl_space_dim(space, isl_dim_out);
+ if (dim < 0)
+ goto error;
+ if (dim != 0)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "expecting 0D space", goto error);
+
+ return isl_multi_union_pw_aff_alloc(space);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Construct and return a multi union piecewise affine expression
+ * that is equal to the given multi affine expression.
+ */
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_multi_aff(
+ __isl_take isl_multi_aff *ma)
+{
+ isl_multi_pw_aff *mpa;
+
+ mpa = isl_multi_pw_aff_from_multi_aff(ma);
+ return isl_multi_union_pw_aff_from_multi_pw_aff(mpa);
+}
+
+/* This function performs the same operation as
+ * isl_multi_union_pw_aff_from_multi_aff, but is considered as a function on an
+ * isl_multi_aff when exported.
+ */
+__isl_give isl_multi_union_pw_aff *isl_multi_aff_to_multi_union_pw_aff(
+ __isl_take isl_multi_aff *ma)
+{
+ return isl_multi_union_pw_aff_from_multi_aff(ma);
+}
+
+/* Construct and return a multi union piecewise affine expression
+ * that is equal to the given multi piecewise affine expression.
+ */
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_multi_pw_aff(
+ __isl_take isl_multi_pw_aff *mpa)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_multi_union_pw_aff *mupa;
+
+ n = isl_multi_pw_aff_dim(mpa, isl_dim_out);
+ if (n < 0)
+ mpa = isl_multi_pw_aff_free(mpa);
+ if (!mpa)
+ return NULL;
+
+ space = isl_multi_pw_aff_get_space(mpa);
+ space = isl_space_range(space);
+ mupa = isl_multi_union_pw_aff_alloc(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+ isl_union_pw_aff *upa;
+
+ pa = isl_multi_pw_aff_get_pw_aff(mpa, i);
+ upa = isl_union_pw_aff_from_pw_aff(pa);
+ mupa = isl_multi_union_pw_aff_restore_check_space(mupa, i, upa);
+ }
+
+ isl_multi_pw_aff_free(mpa);
+
+ return mupa;
+}
+
+/* Extract the range space of "pma" and assign it to *space.
+ * If *space has already been set (through a previous call to this function),
+ * then check that the range space is the same.
+ */
+static isl_stat extract_space(__isl_take isl_pw_multi_aff *pma, void *user)
+{
+ isl_space **space = user;
+ isl_space *pma_space;
+ isl_bool equal;
+
+ pma_space = isl_space_range(isl_pw_multi_aff_get_space(pma));
+ isl_pw_multi_aff_free(pma);
+
+ if (!pma_space)
+ return isl_stat_error;
+ if (!*space) {
+ *space = pma_space;
+ return isl_stat_ok;
+ }
+
+ equal = isl_space_is_equal(pma_space, *space);
+ isl_space_free(pma_space);
+
+ if (equal < 0)
+ return isl_stat_error;
+ if (!equal)
+ isl_die(isl_space_get_ctx(*space), isl_error_invalid,
+ "range spaces not the same", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Construct and return a multi union piecewise affine expression
+ * that is equal to the given union piecewise multi affine expression.
+ *
+ * In order to be able to perform the conversion, the input
+ * needs to be non-empty and may only involve a single range space.
+ *
+ * If the resulting multi union piecewise affine expression has
+ * an explicit domain, then assign it the domain of the input.
+ * In other cases, the domain is stored in the individual elements.
+ */
+__isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_from_union_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ isl_space *space = NULL;
+ isl_multi_union_pw_aff *mupa;
+ int i;
+ isl_size n;
+
+ n = isl_union_pw_multi_aff_n_pw_multi_aff(upma);
+ if (n < 0)
+ goto error;
+ if (n == 0)
+ isl_die(isl_union_pw_multi_aff_get_ctx(upma), isl_error_invalid,
+ "cannot extract range space from empty input",
+ goto error);
+ if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma, &extract_space,
+ &space) < 0)
+ goto error;
+
+ if (!space)
+ goto error;
+
+ n = isl_space_dim(space, isl_dim_set);
+ if (n < 0)
+ space = isl_space_free(space);
+ mupa = isl_multi_union_pw_aff_alloc(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_union_pw_aff *upa;
+
+ upa = isl_union_pw_multi_aff_get_union_pw_aff(upma, i);
+ mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa);
+ }
+ if (isl_multi_union_pw_aff_has_explicit_domain(mupa)) {
+ isl_union_set *dom;
+ isl_union_pw_multi_aff *copy;
+
+ copy = isl_union_pw_multi_aff_copy(upma);
+ dom = isl_union_pw_multi_aff_domain(copy);
+ mupa = isl_multi_union_pw_aff_intersect_domain(mupa, dom);
+ }
+
+ isl_union_pw_multi_aff_free(upma);
+ return mupa;
+error:
+ isl_space_free(space);
+ isl_union_pw_multi_aff_free(upma);
+ return NULL;
+}
+
+/* This function performs the same operation as
+ * isl_multi_union_pw_aff_from_union_pw_multi_aff,
+ * but is considered as a function on an isl_union_pw_multi_aff when exported.
+ */
+__isl_give isl_multi_union_pw_aff *
+isl_union_pw_multi_aff_as_multi_union_pw_aff(
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ return isl_multi_union_pw_aff_from_union_pw_multi_aff(upma);
+}
+
+/* Try and create an isl_multi_union_pw_aff that is equivalent
+ * to the given isl_union_map.
+ * The isl_union_map is required to be single-valued in each space.
+ * Moreover, it cannot be empty and all range spaces need to be the same.
+ * Otherwise, an error is produced.
+ */
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_from_union_map(
+ __isl_take isl_union_map *umap)
+{
+ isl_union_pw_multi_aff *upma;
+
+ upma = isl_union_pw_multi_aff_from_union_map(umap);
+ return isl_multi_union_pw_aff_from_union_pw_multi_aff(upma);
+}
+
+/* This function performs the same operation as
+ * isl_multi_union_pw_aff_from_union_map,
+ * but is considered as a function on an isl_union_map when exported.
+ */
+__isl_give isl_multi_union_pw_aff *isl_union_map_as_multi_union_pw_aff(
+ __isl_take isl_union_map *umap)
+{
+ return isl_multi_union_pw_aff_from_union_map(umap);
+}
+
+/* Return a multiple union piecewise affine expression
+ * that is equal to "mv" on "domain", assuming "domain" and "mv"
+ * have been aligned.
+ *
+ * If the resulting multi union piecewise affine expression has
+ * an explicit domain, then assign it the input domain.
+ * In other cases, the domain is stored in the individual elements.
+ */
+static __isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_multi_val_on_domain_aligned(
+ __isl_take isl_union_set *domain, __isl_take isl_multi_val *mv)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_multi_union_pw_aff *mupa;
+
+ n = isl_multi_val_dim(mv, isl_dim_set);
+ if (!domain || n < 0)
+ goto error;
+
+ space = isl_multi_val_get_space(mv);
+ mupa = isl_multi_union_pw_aff_alloc(space);
+ for (i = 0; i < n; ++i) {
+ isl_val *v;
+ isl_union_pw_aff *upa;
+
+ v = isl_multi_val_get_val(mv, i);
+ upa = isl_union_pw_aff_val_on_domain(isl_union_set_copy(domain),
+ v);
+ mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa);
+ }
+ if (isl_multi_union_pw_aff_has_explicit_domain(mupa))
+ mupa = isl_multi_union_pw_aff_intersect_domain(mupa,
+ isl_union_set_copy(domain));
+
+ isl_union_set_free(domain);
+ isl_multi_val_free(mv);
+ return mupa;
+error:
+ isl_union_set_free(domain);
+ isl_multi_val_free(mv);
+ return NULL;
+}
+
+/* Return a multiple union piecewise affine expression
+ * that is equal to "mv" on "domain".
+ */
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_multi_val_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_multi_val *mv)
+{
+ isl_bool equal_params;
+
+ if (!domain || !mv)
+ goto error;
+ equal_params = isl_space_has_equal_params(domain->dim, mv->space);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params)
+ return isl_multi_union_pw_aff_multi_val_on_domain_aligned(
+ domain, mv);
+ domain = isl_union_set_align_params(domain,
+ isl_multi_val_get_space(mv));
+ mv = isl_multi_val_align_params(mv, isl_union_set_get_space(domain));
+ return isl_multi_union_pw_aff_multi_val_on_domain_aligned(domain, mv);
+error:
+ isl_union_set_free(domain);
+ isl_multi_val_free(mv);
+ return NULL;
+}
+
+/* Return a multiple union piecewise affine expression
+ * that is equal to "ma" on "domain".
+ */
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_multi_aff_on_domain(
+ __isl_take isl_union_set *domain, __isl_take isl_multi_aff *ma)
+{
+ isl_pw_multi_aff *pma;
+
+ pma = isl_pw_multi_aff_from_multi_aff(ma);
+ return isl_multi_union_pw_aff_pw_multi_aff_on_domain(domain, pma);
+}
+
+/* Return a multiple union piecewise affine expression
+ * that is equal to "pma" on "domain", assuming "domain" and "pma"
+ * have been aligned.
+ *
+ * If the resulting multi union piecewise affine expression has
+ * an explicit domain, then assign it the input domain.
+ * In other cases, the domain is stored in the individual elements.
+ */
+static __isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_pw_multi_aff_on_domain_aligned(
+ __isl_take isl_union_set *domain, __isl_take isl_pw_multi_aff *pma)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_multi_union_pw_aff *mupa;
+
+ n = isl_pw_multi_aff_dim(pma, isl_dim_set);
+ if (!domain || n < 0)
+ goto error;
+ space = isl_pw_multi_aff_get_space(pma);
+ mupa = isl_multi_union_pw_aff_alloc(space);
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+ isl_union_pw_aff *upa;
+
+ pa = isl_pw_multi_aff_get_pw_aff(pma, i);
+ upa = isl_union_pw_aff_pw_aff_on_domain(
+ isl_union_set_copy(domain), pa);
+ mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa);
+ }
+ if (isl_multi_union_pw_aff_has_explicit_domain(mupa))
+ mupa = isl_multi_union_pw_aff_intersect_domain(mupa,
+ isl_union_set_copy(domain));
+
+ isl_union_set_free(domain);
+ isl_pw_multi_aff_free(pma);
+ return mupa;
+error:
+ isl_union_set_free(domain);
+ isl_pw_multi_aff_free(pma);
+ return NULL;
+}
+
+/* Return a multiple union piecewise affine expression
+ * that is equal to "pma" on "domain".
+ */
+__isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_pw_multi_aff_on_domain(__isl_take isl_union_set *domain,
+ __isl_take isl_pw_multi_aff *pma)
+{
+ isl_bool equal_params;
+ isl_space *space;
+
+ space = isl_pw_multi_aff_peek_space(pma);
+ equal_params = isl_union_set_space_has_equal_params(domain, space);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params)
+ return isl_multi_union_pw_aff_pw_multi_aff_on_domain_aligned(
+ domain, pma);
+ domain = isl_union_set_align_params(domain,
+ isl_pw_multi_aff_get_space(pma));
+ pma = isl_pw_multi_aff_align_params(pma,
+ isl_union_set_get_space(domain));
+ return isl_multi_union_pw_aff_pw_multi_aff_on_domain_aligned(domain,
+ pma);
+error:
+ isl_union_set_free(domain);
+ isl_pw_multi_aff_free(pma);
+ return NULL;
+}
+
+/* Return a union set containing those elements in the domains
+ * of the elements of "mupa" where they are all zero.
+ *
+ * If there are no elements, then simply return the entire domain.
+ */
+__isl_give isl_union_set *isl_multi_union_pw_aff_zero_union_set(
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ int i;
+ isl_size n;
+ isl_union_pw_aff *upa;
+ isl_union_set *zero;
+
+ n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set);
+ if (n < 0)
+ mupa = isl_multi_union_pw_aff_free(mupa);
+ if (!mupa)
+ return NULL;
+
+ if (n == 0)
+ return isl_multi_union_pw_aff_domain(mupa);
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, 0);
+ zero = isl_union_pw_aff_zero_union_set(upa);
+
+ for (i = 1; i < n; ++i) {
+ isl_union_set *zero_i;
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i);
+ zero_i = isl_union_pw_aff_zero_union_set(upa);
+
+ zero = isl_union_set_intersect(zero, zero_i);
+ }
+
+ isl_multi_union_pw_aff_free(mupa);
+ return zero;
+}
+
+/* Construct a union map mapping the shared domain
+ * of the union piecewise affine expressions to the range of "mupa"
+ * in the special case of a 0D multi union piecewise affine expression.
+ *
+ * Construct a map between the explicit domain of "mupa" and
+ * the range space.
+ * Note that this assumes that the domain consists of explicit elements.
+ */
+static __isl_give isl_union_map *isl_union_map_from_multi_union_pw_aff_0D(
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ isl_bool is_params;
+ isl_space *space;
+ isl_union_set *dom, *ran;
+
+ space = isl_multi_union_pw_aff_get_space(mupa);
+ dom = isl_multi_union_pw_aff_domain(mupa);
+ ran = isl_union_set_from_set(isl_set_universe(space));
+
+ is_params = isl_union_set_is_params(dom);
+ if (is_params < 0)
+ dom = isl_union_set_free(dom);
+ else if (is_params)
+ isl_die(isl_union_set_get_ctx(dom), isl_error_invalid,
+ "cannot create union map from expression without "
+ "explicit domain elements",
+ dom = isl_union_set_free(dom));
+
+ return isl_union_map_from_domain_and_range(dom, ran);
+}
+
+/* Construct a union map mapping the shared domain
+ * of the union piecewise affine expressions to the range of "mupa"
+ * with each dimension in the range equated to the
+ * corresponding union piecewise affine expression.
+ *
+ * If the input is zero-dimensional, then construct a mapping
+ * from its explicit domain.
+ */
+__isl_give isl_union_map *isl_union_map_from_multi_union_pw_aff(
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_union_map *umap;
+ isl_union_pw_aff *upa;
+
+ n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set);
+ if (n < 0)
+ mupa = isl_multi_union_pw_aff_free(mupa);
+ if (!mupa)
+ return NULL;
+
+ if (n == 0)
+ return isl_union_map_from_multi_union_pw_aff_0D(mupa);
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, 0);
+ umap = isl_union_map_from_union_pw_aff(upa);
+
+ for (i = 1; i < n; ++i) {
+ isl_union_map *umap_i;
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i);
+ umap_i = isl_union_map_from_union_pw_aff(upa);
+ umap = isl_union_map_flat_range_product(umap, umap_i);
+ }
+
+ space = isl_multi_union_pw_aff_get_space(mupa);
+ umap = isl_union_map_reset_range_space(umap, space);
+
+ isl_multi_union_pw_aff_free(mupa);
+ return umap;
+}
+
+/* Internal data structure for isl_union_pw_multi_aff_reset_range_space.
+ * "range" is the space from which to set the range space.
+ * "res" collects the results.
+ */
+struct isl_union_pw_multi_aff_reset_range_space_data {
+ isl_space *range;
+ isl_union_pw_multi_aff *res;
+};
+
+/* Replace the range space of "pma" by the range space of data->range and
+ * add the result to data->res.
+ */
+static isl_stat reset_range_space(__isl_take isl_pw_multi_aff *pma, void *user)
+{
+ struct isl_union_pw_multi_aff_reset_range_space_data *data = user;
+ isl_space *space;
+
+ space = isl_pw_multi_aff_get_space(pma);
+ space = isl_space_domain(space);
+ space = isl_space_extend_domain_with_range(space,
+ isl_space_copy(data->range));
+ pma = isl_pw_multi_aff_reset_space(pma, space);
+ data->res = isl_union_pw_multi_aff_add_pw_multi_aff(data->res, pma);
+
+ return data->res ? isl_stat_ok : isl_stat_error;
+}
+
+/* Replace the range space of all the piecewise affine expressions in "upma" by
+ * the range space of "space".
+ *
+ * This assumes that all these expressions have the same output dimension.
+ *
+ * Since the spaces of the expressions change, so do their hash values.
+ * We therefore need to create a new isl_union_pw_multi_aff.
+ * Note that the hash value is currently computed based on the entire
+ * space even though there can only be a single expression with a given
+ * domain space.
+ */
+static __isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_reset_range_space(
+ __isl_take isl_union_pw_multi_aff *upma, __isl_take isl_space *space)
+{
+ struct isl_union_pw_multi_aff_reset_range_space_data data = { space };
+ isl_space *space_upma;
+
+ space_upma = isl_union_pw_multi_aff_get_space(upma);
+ data.res = isl_union_pw_multi_aff_empty(space_upma);
+ if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma,
+ &reset_range_space, &data) < 0)
+ data.res = isl_union_pw_multi_aff_free(data.res);
+
+ isl_space_free(space);
+ isl_union_pw_multi_aff_free(upma);
+ return data.res;
+}
+
+/* Construct and return a union piecewise multi affine expression
+ * that is equal to the given multi union piecewise affine expression,
+ * in the special case of a 0D multi union piecewise affine expression.
+ *
+ * Construct a union piecewise multi affine expression
+ * on top of the explicit domain of the input.
+ */
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_from_multi_union_pw_aff_0D(
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ isl_space *space;
+ isl_multi_val *mv;
+ isl_union_set *domain;
+
+ space = isl_multi_union_pw_aff_get_space(mupa);
+ mv = isl_multi_val_zero(space);
+ domain = isl_multi_union_pw_aff_domain(mupa);
+ return isl_union_pw_multi_aff_multi_val_on_domain(domain, mv);
+}
+
+/* Construct and return a union piecewise multi affine expression
+ * that is equal to the given multi union piecewise affine expression.
+ *
+ * If the input is zero-dimensional, then
+ * construct a union piecewise multi affine expression
+ * on top of the explicit domain of the input.
+ */
+__isl_give isl_union_pw_multi_aff *
+isl_union_pw_multi_aff_from_multi_union_pw_aff(
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_union_pw_multi_aff *upma;
+ isl_union_pw_aff *upa;
+
+ n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set);
+ if (n < 0)
+ mupa = isl_multi_union_pw_aff_free(mupa);
+ if (!mupa)
+ return NULL;
+
+ if (n == 0)
+ return isl_union_pw_multi_aff_from_multi_union_pw_aff_0D(mupa);
+
+ space = isl_multi_union_pw_aff_get_space(mupa);
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, 0);
+ upma = isl_union_pw_multi_aff_from_union_pw_aff(upa);
+
+ for (i = 1; i < n; ++i) {
+ isl_union_pw_multi_aff *upma_i;
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i);
+ upma_i = isl_union_pw_multi_aff_from_union_pw_aff(upa);
+ upma = isl_union_pw_multi_aff_flat_range_product(upma, upma_i);
+ }
+
+ upma = isl_union_pw_multi_aff_reset_range_space(upma, space);
+
+ isl_multi_union_pw_aff_free(mupa);
+ return upma;
+}
+
+/* Intersect the range of "mupa" with "range",
+ * in the special case where "mupa" is 0D.
+ *
+ * Intersect the domain of "mupa" with the constraints on the parameters
+ * of "range".
+ */
+static __isl_give isl_multi_union_pw_aff *mupa_intersect_range_0D(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_set *range)
+{
+ range = isl_set_params(range);
+ mupa = isl_multi_union_pw_aff_intersect_params(mupa, range);
+ return mupa;
+}
+
+/* Intersect the range of "mupa" with "range".
+ * That is, keep only those domain elements that have a function value
+ * in "range".
+ */
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_intersect_range(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_set *range)
+{
+ isl_union_pw_multi_aff *upma;
+ isl_union_set *domain;
+ isl_space *space;
+ isl_size n;
+ int match;
+
+ n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set);
+ if (n < 0 || !range)
+ goto error;
+
+ space = isl_set_get_space(range);
+ match = isl_space_tuple_is_equal(mupa->space, isl_dim_set,
+ space, isl_dim_set);
+ isl_space_free(space);
+ if (match < 0)
+ goto error;
+ if (!match)
+ isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid,
+ "space don't match", goto error);
+ if (n == 0)
+ return mupa_intersect_range_0D(mupa, range);
+
+ upma = isl_union_pw_multi_aff_from_multi_union_pw_aff(
+ isl_multi_union_pw_aff_copy(mupa));
+ domain = isl_union_set_from_set(range);
+ domain = isl_union_set_preimage_union_pw_multi_aff(domain, upma);
+ mupa = isl_multi_union_pw_aff_intersect_domain(mupa, domain);
+
+ return mupa;
+error:
+ isl_multi_union_pw_aff_free(mupa);
+ isl_set_free(range);
+ return NULL;
+}
+
+/* Return the shared domain of the elements of "mupa",
+ * in the special case where "mupa" is zero-dimensional.
+ *
+ * Return the explicit domain of "mupa".
+ * Note that this domain may be a parameter set, either
+ * because "mupa" is meant to live in a set space or
+ * because no explicit domain has been set.
+ */
+__isl_give isl_union_set *isl_multi_union_pw_aff_domain_0D(
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ isl_union_set *dom;
+
+ dom = isl_multi_union_pw_aff_get_explicit_domain(mupa);
+ isl_multi_union_pw_aff_free(mupa);
+
+ return dom;
+}
+
+/* Return the shared domain of the elements of "mupa".
+ *
+ * If "mupa" is zero-dimensional, then return its explicit domain.
+ */
+__isl_give isl_union_set *isl_multi_union_pw_aff_domain(
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ int i;
+ isl_size n;
+ isl_union_pw_aff *upa;
+ isl_union_set *dom;
+
+ n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set);
+ if (n < 0)
+ mupa = isl_multi_union_pw_aff_free(mupa);
+ if (!mupa)
+ return NULL;
+
+ if (n == 0)
+ return isl_multi_union_pw_aff_domain_0D(mupa);
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, 0);
+ dom = isl_union_pw_aff_domain(upa);
+ for (i = 1; i < n; ++i) {
+ isl_union_set *dom_i;
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i);
+ dom_i = isl_union_pw_aff_domain(upa);
+ dom = isl_union_set_intersect(dom, dom_i);
+ }
+
+ isl_multi_union_pw_aff_free(mupa);
+ return dom;
+}
+
+/* Apply "aff" to "mupa". The space of "mupa" is equal to the domain of "aff".
+ * In particular, the spaces have been aligned.
+ * The result is defined over the shared domain of the elements of "mupa"
+ *
+ * We first extract the parametric constant part of "aff" and
+ * define that over the shared domain.
+ * Then we iterate over all input dimensions of "aff" and add the corresponding
+ * multiples of the elements of "mupa".
+ * Finally, we consider the integer divisions, calling the function
+ * recursively to obtain an isl_union_pw_aff corresponding to the
+ * integer division argument.
+ */
+static __isl_give isl_union_pw_aff *multi_union_pw_aff_apply_aff(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_aff *aff)
+{
+ int i;
+ isl_size n_in, n_div;
+ isl_union_pw_aff *upa;
+ isl_union_set *uset;
+ isl_val *v;
+ isl_aff *cst;
+
+ n_in = isl_aff_dim(aff, isl_dim_in);
+ n_div = isl_aff_dim(aff, isl_dim_div);
+ if (n_in < 0 || n_div < 0)
+ goto error;
+
+ uset = isl_multi_union_pw_aff_domain(isl_multi_union_pw_aff_copy(mupa));
+ cst = isl_aff_copy(aff);
+ cst = isl_aff_drop_dims(cst, isl_dim_div, 0, n_div);
+ cst = isl_aff_drop_dims(cst, isl_dim_in, 0, n_in);
+ cst = isl_aff_project_domain_on_params(cst);
+ upa = isl_union_pw_aff_aff_on_domain(uset, cst);
+
+ for (i = 0; i < n_in; ++i) {
+ isl_union_pw_aff *upa_i;
+
+ if (!isl_aff_involves_dims(aff, isl_dim_in, i, 1))
+ continue;
+ v = isl_aff_get_coefficient_val(aff, isl_dim_in, i);
+ upa_i = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i);
+ upa_i = isl_union_pw_aff_scale_val(upa_i, v);
+ upa = isl_union_pw_aff_add(upa, upa_i);
+ }
+
+ for (i = 0; i < n_div; ++i) {
+ isl_aff *div;
+ isl_union_pw_aff *upa_i;
+
+ if (!isl_aff_involves_dims(aff, isl_dim_div, i, 1))
+ continue;
+ div = isl_aff_get_div(aff, i);
+ upa_i = multi_union_pw_aff_apply_aff(
+ isl_multi_union_pw_aff_copy(mupa), div);
+ upa_i = isl_union_pw_aff_floor(upa_i);
+ v = isl_aff_get_coefficient_val(aff, isl_dim_div, i);
+ upa_i = isl_union_pw_aff_scale_val(upa_i, v);
+ upa = isl_union_pw_aff_add(upa, upa_i);
+ }
+
+ isl_multi_union_pw_aff_free(mupa);
+ isl_aff_free(aff);
+
+ return upa;
+error:
+ isl_multi_union_pw_aff_free(mupa);
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Apply "aff" to "mupa". The space of "mupa" needs to be compatible
+ * with the domain of "aff".
+ * Furthermore, the dimension of this space needs to be greater than zero.
+ * The result is defined over the shared domain of the elements of "mupa"
+ *
+ * We perform these checks and then hand over control to
+ * multi_union_pw_aff_apply_aff.
+ */
+__isl_give isl_union_pw_aff *isl_multi_union_pw_aff_apply_aff(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_aff *aff)
+{
+ isl_size dim;
+ isl_space *space1, *space2;
+ isl_bool equal;
+
+ mupa = isl_multi_union_pw_aff_align_params(mupa,
+ isl_aff_get_space(aff));
+ aff = isl_aff_align_params(aff, isl_multi_union_pw_aff_get_space(mupa));
+ if (!mupa || !aff)
+ goto error;
+
+ space1 = isl_multi_union_pw_aff_get_space(mupa);
+ space2 = isl_aff_get_domain_space(aff);
+ equal = isl_space_is_equal(space1, space2);
+ isl_space_free(space1);
+ isl_space_free(space2);
+ if (equal < 0)
+ goto error;
+ if (!equal)
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "spaces don't match", goto error);
+ dim = isl_aff_dim(aff, isl_dim_in);
+ if (dim < 0)
+ goto error;
+ if (dim == 0)
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "cannot determine domains", goto error);
+
+ return multi_union_pw_aff_apply_aff(mupa, aff);
+error:
+ isl_multi_union_pw_aff_free(mupa);
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Apply "ma" to "mupa", in the special case where "mupa" is 0D.
+ * The space of "mupa" is known to be compatible with the domain of "ma".
+ *
+ * Construct an isl_multi_union_pw_aff that is equal to "ma"
+ * on the domain of "mupa".
+ */
+static __isl_give isl_multi_union_pw_aff *mupa_apply_multi_aff_0D(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_multi_aff *ma)
+{
+ isl_union_set *dom;
+
+ dom = isl_multi_union_pw_aff_domain(mupa);
+ ma = isl_multi_aff_project_domain_on_params(ma);
+
+ return isl_multi_union_pw_aff_multi_aff_on_domain(dom, ma);
+}
+
+/* Apply "ma" to "mupa". The space of "mupa" needs to be compatible
+ * with the domain of "ma".
+ * The result is defined over the shared domain of the elements of "mupa"
+ */
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_multi_aff(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_multi_aff *ma)
+{
+ isl_space *space1, *space2;
+ isl_multi_union_pw_aff *res;
+ isl_bool equal;
+ int i;
+ isl_size n_in, n_out;
+
+ mupa = isl_multi_union_pw_aff_align_params(mupa,
+ isl_multi_aff_get_space(ma));
+ ma = isl_multi_aff_align_params(ma,
+ isl_multi_union_pw_aff_get_space(mupa));
+ n_in = isl_multi_aff_dim(ma, isl_dim_in);
+ n_out = isl_multi_aff_dim(ma, isl_dim_out);
+ if (!mupa || n_in < 0 || n_out < 0)
+ goto error;
+
+ space1 = isl_multi_union_pw_aff_get_space(mupa);
+ space2 = isl_multi_aff_get_domain_space(ma);
+ equal = isl_space_is_equal(space1, space2);
+ isl_space_free(space1);
+ isl_space_free(space2);
+ if (equal < 0)
+ goto error;
+ if (!equal)
+ isl_die(isl_multi_aff_get_ctx(ma), isl_error_invalid,
+ "spaces don't match", goto error);
+ if (n_in == 0)
+ return mupa_apply_multi_aff_0D(mupa, ma);
+
+ space1 = isl_space_range(isl_multi_aff_get_space(ma));
+ res = isl_multi_union_pw_aff_alloc(space1);
+
+ for (i = 0; i < n_out; ++i) {
+ isl_aff *aff;
+ isl_union_pw_aff *upa;
+
+ aff = isl_multi_aff_get_aff(ma, i);
+ upa = multi_union_pw_aff_apply_aff(
+ isl_multi_union_pw_aff_copy(mupa), aff);
+ res = isl_multi_union_pw_aff_set_union_pw_aff(res, i, upa);
+ }
+
+ isl_multi_aff_free(ma);
+ isl_multi_union_pw_aff_free(mupa);
+ return res;
+error:
+ isl_multi_union_pw_aff_free(mupa);
+ isl_multi_aff_free(ma);
+ return NULL;
+}
+
+/* Apply "pa" to "mupa", in the special case where "mupa" is 0D.
+ * The space of "mupa" is known to be compatible with the domain of "pa".
+ *
+ * Construct an isl_multi_union_pw_aff that is equal to "pa"
+ * on the domain of "mupa".
+ */
+static __isl_give isl_union_pw_aff *isl_multi_union_pw_aff_apply_pw_aff_0D(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_pw_aff *pa)
+{
+ isl_union_set *dom;
+
+ dom = isl_multi_union_pw_aff_domain(mupa);
+ pa = isl_pw_aff_project_domain_on_params(pa);
+
+ return isl_union_pw_aff_pw_aff_on_domain(dom, pa);
+}
+
+/* Apply "pa" to "mupa". The space of "mupa" needs to be compatible
+ * with the domain of "pa".
+ * Furthermore, the dimension of this space needs to be greater than zero.
+ * The result is defined over the shared domain of the elements of "mupa"
+ */
+__isl_give isl_union_pw_aff *isl_multi_union_pw_aff_apply_pw_aff(
+ __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_pw_aff *pa)
+{
+ int i;
+ isl_bool equal;
+ isl_size n_in;
+ isl_space *space, *space2;
+ isl_union_pw_aff *upa;
+
+ mupa = isl_multi_union_pw_aff_align_params(mupa,
+ isl_pw_aff_get_space(pa));
+ pa = isl_pw_aff_align_params(pa,
+ isl_multi_union_pw_aff_get_space(mupa));
+ if (!mupa || !pa)
+ goto error;
+
+ space = isl_multi_union_pw_aff_get_space(mupa);
+ space2 = isl_pw_aff_get_domain_space(pa);
+ equal = isl_space_is_equal(space, space2);
+ isl_space_free(space);
+ isl_space_free(space2);
+ if (equal < 0)
+ goto error;
+ if (!equal)
+ isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid,
+ "spaces don't match", goto error);
+ n_in = isl_pw_aff_dim(pa, isl_dim_in);
+ if (n_in < 0)
+ goto error;
+ if (n_in == 0)
+ return isl_multi_union_pw_aff_apply_pw_aff_0D(mupa, pa);
+
+ space = isl_space_params(isl_multi_union_pw_aff_get_space(mupa));
+ upa = isl_union_pw_aff_empty(space);
+
+ for (i = 0; i < pa->n; ++i) {
+ isl_aff *aff;
+ isl_set *domain;
+ isl_multi_union_pw_aff *mupa_i;
+ isl_union_pw_aff *upa_i;
+
+ mupa_i = isl_multi_union_pw_aff_copy(mupa);
+ domain = isl_set_copy(pa->p[i].set);
+ mupa_i = isl_multi_union_pw_aff_intersect_range(mupa_i, domain);
+ aff = isl_aff_copy(pa->p[i].aff);
+ upa_i = multi_union_pw_aff_apply_aff(mupa_i, aff);
+ upa = isl_union_pw_aff_union_add(upa, upa_i);
+ }
+
+ isl_multi_union_pw_aff_free(mupa);
+ isl_pw_aff_free(pa);
+ return upa;
+error:
+ isl_multi_union_pw_aff_free(mupa);
+ isl_pw_aff_free(pa);
+ return NULL;
+}
+
+/* Apply "pma" to "mupa", in the special case where "mupa" is 0D.
+ * The space of "mupa" is known to be compatible with the domain of "pma".
+ *
+ * Construct an isl_multi_union_pw_aff that is equal to "pma"
+ * on the domain of "mupa".
+ */
+static __isl_give isl_multi_union_pw_aff *mupa_apply_pw_multi_aff_0D(
+ __isl_take isl_multi_union_pw_aff *mupa,
+ __isl_take isl_pw_multi_aff *pma)
+{
+ isl_union_set *dom;
+
+ dom = isl_multi_union_pw_aff_domain(mupa);
+ pma = isl_pw_multi_aff_project_domain_on_params(pma);
+
+ return isl_multi_union_pw_aff_pw_multi_aff_on_domain(dom, pma);
+}
+
+/* Apply "pma" to "mupa". The space of "mupa" needs to be compatible
+ * with the domain of "pma".
+ * The result is defined over the shared domain of the elements of "mupa"
+ */
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_pw_multi_aff(
+ __isl_take isl_multi_union_pw_aff *mupa,
+ __isl_take isl_pw_multi_aff *pma)
+{
+ isl_space *space1, *space2;
+ isl_multi_union_pw_aff *res;
+ isl_bool equal;
+ int i;
+ isl_size n_in, n_out;
+
+ mupa = isl_multi_union_pw_aff_align_params(mupa,
+ isl_pw_multi_aff_get_space(pma));
+ pma = isl_pw_multi_aff_align_params(pma,
+ isl_multi_union_pw_aff_get_space(mupa));
+ if (!mupa || !pma)
+ goto error;
+
+ space1 = isl_multi_union_pw_aff_get_space(mupa);
+ space2 = isl_pw_multi_aff_get_domain_space(pma);
+ equal = isl_space_is_equal(space1, space2);
+ isl_space_free(space1);
+ isl_space_free(space2);
+ if (equal < 0)
+ goto error;
+ if (!equal)
+ isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid,
+ "spaces don't match", goto error);
+ n_in = isl_pw_multi_aff_dim(pma, isl_dim_in);
+ n_out = isl_pw_multi_aff_dim(pma, isl_dim_out);
+ if (n_in < 0 || n_out < 0)
+ goto error;
+ if (n_in == 0)
+ return mupa_apply_pw_multi_aff_0D(mupa, pma);
+
+ space1 = isl_space_range(isl_pw_multi_aff_get_space(pma));
+ res = isl_multi_union_pw_aff_alloc(space1);
+
+ for (i = 0; i < n_out; ++i) {
+ isl_pw_aff *pa;
+ isl_union_pw_aff *upa;
+
+ pa = isl_pw_multi_aff_get_pw_aff(pma, i);
+ upa = isl_multi_union_pw_aff_apply_pw_aff(
+ isl_multi_union_pw_aff_copy(mupa), pa);
+ res = isl_multi_union_pw_aff_set_union_pw_aff(res, i, upa);
+ }
+
+ isl_pw_multi_aff_free(pma);
+ isl_multi_union_pw_aff_free(mupa);
+ return res;
+error:
+ isl_multi_union_pw_aff_free(mupa);
+ isl_pw_multi_aff_free(pma);
+ return NULL;
+}
+
+/* Replace the explicit domain of "mupa" by its preimage under "upma".
+ * If the explicit domain only keeps track of constraints on the parameters,
+ * then only update those constraints.
+ */
+static __isl_give isl_multi_union_pw_aff *preimage_explicit_domain(
+ __isl_take isl_multi_union_pw_aff *mupa,
+ __isl_keep isl_union_pw_multi_aff *upma)
+{
+ isl_bool is_params;
+
+ if (isl_multi_union_pw_aff_check_has_explicit_domain(mupa) < 0)
+ return isl_multi_union_pw_aff_free(mupa);
+
+ mupa = isl_multi_union_pw_aff_cow(mupa);
+ if (!mupa)
+ return NULL;
+
+ is_params = isl_union_set_is_params(mupa->u.dom);
+ if (is_params < 0)
+ return isl_multi_union_pw_aff_free(mupa);
+
+ upma = isl_union_pw_multi_aff_copy(upma);
+ if (is_params)
+ mupa->u.dom = isl_union_set_intersect_params(mupa->u.dom,
+ isl_union_set_params(isl_union_pw_multi_aff_domain(upma)));
+ else
+ mupa->u.dom = isl_union_set_preimage_union_pw_multi_aff(
+ mupa->u.dom, upma);
+ if (!mupa->u.dom)
+ return isl_multi_union_pw_aff_free(mupa);
+ return mupa;
+}
+
+/* Compute the pullback of "mupa" by the function represented by "upma".
+ * In other words, plug in "upma" in "mupa". The result contains
+ * expressions defined over the domain space of "upma".
+ *
+ * Run over all elements of "mupa" and plug in "upma" in each of them.
+ *
+ * If "mupa" has an explicit domain, then it is this domain
+ * that needs to undergo a pullback instead, i.e., a preimage.
+ */
+__isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_pullback_union_pw_multi_aff(
+ __isl_take isl_multi_union_pw_aff *mupa,
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ int i;
+ isl_size n;
+
+ mupa = isl_multi_union_pw_aff_align_params(mupa,
+ isl_union_pw_multi_aff_get_space(upma));
+ upma = isl_union_pw_multi_aff_align_params(upma,
+ isl_multi_union_pw_aff_get_space(mupa));
+ mupa = isl_multi_union_pw_aff_cow(mupa);
+ n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set);
+ if (n < 0 || !upma)
+ goto error;
+
+ for (i = 0; i < n; ++i) {
+ isl_union_pw_aff *upa;
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i);
+ upa = isl_union_pw_aff_pullback_union_pw_multi_aff(upa,
+ isl_union_pw_multi_aff_copy(upma));
+ mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa);
+ }
+
+ if (isl_multi_union_pw_aff_has_explicit_domain(mupa))
+ mupa = preimage_explicit_domain(mupa, upma);
+
+ isl_union_pw_multi_aff_free(upma);
+ return mupa;
+error:
+ isl_multi_union_pw_aff_free(mupa);
+ isl_union_pw_multi_aff_free(upma);
+ return NULL;
+}
+
+/* Extract the sequence of elements in "mupa" with domain space "space"
+ * (ignoring parameters).
+ *
+ * For the elements of "mupa" that are not defined on the specified space,
+ * the corresponding element in the result is empty.
+ */
+__isl_give isl_multi_pw_aff *isl_multi_union_pw_aff_extract_multi_pw_aff(
+ __isl_keep isl_multi_union_pw_aff *mupa, __isl_take isl_space *space)
+{
+ int i;
+ isl_size n;
+ isl_space *space_mpa;
+ isl_multi_pw_aff *mpa;
+
+ n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set);
+ if (n < 0 || !space)
+ goto error;
+
+ space_mpa = isl_multi_union_pw_aff_get_space(mupa);
+ space = isl_space_replace_params(space, space_mpa);
+ space_mpa = isl_space_map_from_domain_and_range(isl_space_copy(space),
+ space_mpa);
+ mpa = isl_multi_pw_aff_alloc(space_mpa);
+
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, 1);
+ for (i = 0; i < n; ++i) {
+ isl_union_pw_aff *upa;
+ isl_pw_aff *pa;
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i);
+ pa = isl_union_pw_aff_extract_pw_aff(upa,
+ isl_space_copy(space));
+ mpa = isl_multi_pw_aff_set_pw_aff(mpa, i, pa);
+ isl_union_pw_aff_free(upa);
+ }
+
+ isl_space_free(space);
+ return mpa;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Data structure that specifies how isl_union_pw_multi_aff_un_op
+ * should modify the base expressions in the input.
+ *
+ * If "filter" is not NULL, then only the base expressions that satisfy "filter"
+ * are taken into account.
+ * "fn" is applied to each entry in the input.
+ */
+struct isl_union_pw_multi_aff_un_op_control {
+ isl_bool (*filter)(__isl_keep isl_pw_multi_aff *part);
+ __isl_give isl_pw_multi_aff *(*fn)(__isl_take isl_pw_multi_aff *pma);
+};
+
+/* Wrapper for isl_union_pw_multi_aff_un_op filter functions (which do not take
+ * a second argument) for use as an isl_union_pw_multi_aff_transform
+ * filter function (which does take a second argument).
+ * Simply call control->filter without the second argument.
+ */
+static isl_bool isl_union_pw_multi_aff_un_op_filter_drop_user(
+ __isl_take isl_pw_multi_aff *pma, void *user)
+{
+ struct isl_union_pw_multi_aff_un_op_control *control = user;
+
+ return control->filter(pma);
+}
+
+/* Wrapper for isl_union_pw_multi_aff_un_op base functions (which do not take
+ * a second argument) for use as an isl_union_pw_multi_aff_transform
+ * base function (which does take a second argument).
+ * Simply call control->fn without the second argument.
+ */
+static __isl_give isl_pw_multi_aff *isl_union_pw_multi_aff_un_op_drop_user(
+ __isl_take isl_pw_multi_aff *pma, void *user)
+{
+ struct isl_union_pw_multi_aff_un_op_control *control = user;
+
+ return control->fn(pma);
+}
+
+/* Construct an isl_union_pw_multi_aff that is obtained by
+ * modifying "upma" according to "control".
+ *
+ * isl_union_pw_multi_aff_transform performs essentially
+ * the same operation, but takes a filter and a callback function
+ * of a different form (with an extra argument).
+ * Call isl_union_pw_multi_aff_transform with wrappers
+ * that remove this extra argument.
+ */
+static __isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_un_op(
+ __isl_take isl_union_pw_multi_aff *upma,
+ struct isl_union_pw_multi_aff_un_op_control *control)
+{
+ struct isl_union_pw_multi_aff_transform_control t_control = {
+ .filter = &isl_union_pw_multi_aff_un_op_filter_drop_user,
+ .filter_user = control,
+ .fn = &isl_union_pw_multi_aff_un_op_drop_user,
+ .fn_user = control,
+ };
+
+ return isl_union_pw_multi_aff_transform(upma, &t_control);
+}
+
+/* For each function in "upma" of the form A -> [B -> C],
+ * extract the function A -> B and collect the results.
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_range_factor_domain(
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ struct isl_union_pw_multi_aff_un_op_control control = {
+ .filter = &isl_pw_multi_aff_range_is_wrapping,
+ .fn = &isl_pw_multi_aff_range_factor_domain,
+ };
+ return isl_union_pw_multi_aff_un_op(upma, &control);
+}
+
+/* For each function in "upma" of the form A -> [B -> C],
+ * extract the function A -> C and collect the results.
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_range_factor_range(
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ struct isl_union_pw_multi_aff_un_op_control control = {
+ .filter = &isl_pw_multi_aff_range_is_wrapping,
+ .fn = &isl_pw_multi_aff_range_factor_range,
+ };
+ return isl_union_pw_multi_aff_un_op(upma, &control);
+}
+
+/* Evaluate the affine function "aff" in the void point "pnt".
+ * In particular, return the value NaN.
+ */
+static __isl_give isl_val *eval_void(__isl_take isl_aff *aff,
+ __isl_take isl_point *pnt)
+{
+ isl_ctx *ctx;
+
+ ctx = isl_point_get_ctx(pnt);
+ isl_aff_free(aff);
+ isl_point_free(pnt);
+ return isl_val_nan(ctx);
+}
+
+/* Evaluate the affine expression "aff"
+ * in the coordinates (with denominator) "pnt".
+ */
+static __isl_give isl_val *eval(__isl_keep isl_vec *aff,
+ __isl_keep isl_vec *pnt)
+{
+ isl_int n, d;
+ isl_ctx *ctx;
+ isl_val *v;
+
+ if (!aff || !pnt)
+ return NULL;
+
+ ctx = isl_vec_get_ctx(aff);
+ isl_int_init(n);
+ isl_int_init(d);
+ isl_seq_inner_product(aff->el + 1, pnt->el, pnt->size, &n);
+ isl_int_mul(d, aff->el[0], pnt->el[0]);
+ v = isl_val_rat_from_isl_int(ctx, n, d);
+ v = isl_val_normalize(v);
+ isl_int_clear(n);
+ isl_int_clear(d);
+
+ return v;
+}
+
+/* Check that the domain space of "aff" is equal to "space".
+ */
+static isl_stat isl_aff_check_has_domain_space(__isl_keep isl_aff *aff,
+ __isl_keep isl_space *space)
+{
+ isl_bool ok;
+
+ ok = isl_space_is_equal(isl_aff_peek_domain_space(aff), space);
+ if (ok < 0)
+ return isl_stat_error;
+ if (!ok)
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "incompatible spaces", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Evaluate the affine function "aff" in "pnt".
+ */
+__isl_give isl_val *isl_aff_eval(__isl_take isl_aff *aff,
+ __isl_take isl_point *pnt)
+{
+ isl_bool is_void;
+ isl_val *v;
+ isl_local_space *ls;
+
+ if (isl_aff_check_has_domain_space(aff, isl_point_peek_space(pnt)) < 0)
+ goto error;
+ is_void = isl_point_is_void(pnt);
+ if (is_void < 0)
+ goto error;
+ if (is_void)
+ return eval_void(aff, pnt);
+
+ ls = isl_aff_get_domain_local_space(aff);
+ pnt = isl_local_space_lift_point(ls, pnt);
+
+ v = eval(aff->v, isl_point_peek_vec(pnt));
+
+ isl_aff_free(aff);
+ isl_point_free(pnt);
+
+ return v;
+error:
+ isl_aff_free(aff);
+ isl_point_free(pnt);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_lex_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_lex_templ.c
new file mode 100644
index 00000000000..9fbab61fb65
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_lex_templ.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 INRIA Rocquencourt
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2"
+ * where the function value of "mpa1" lexicographically compares as "ORDER"
+ * to that of "mpa2". "space" is the space of the result.
+ * The parameters of "mpa1" and "mpa2" are assumed to have been aligned.
+ *
+ * "mpa1" is in the given lexicographic order compared to "mpa2"
+ * if, for some i, the i-th element of "mpa1" is in that order compared to
+ * the i-th element of "mpa2" while all previous elements are
+ * pairwise equal, where the order needs to be strict (not-equal)
+ * if i corresponds to anything but the last element.
+ * The strict version of "ORDER" is defined by "STRICT_ORDER",
+ * which is the same if "ORDER" itself is strict.
+ */
+static __isl_give isl_map *FN(FN(isl_multi_pw_aff_lex,ORDER),map_on_space)(
+ __isl_keep isl_multi_pw_aff *mpa1, __isl_keep isl_multi_pw_aff *mpa2,
+ __isl_take isl_space *space)
+{
+ return isl_multi_pw_aff_lex_map_on_space(mpa1, mpa2,
+ &FN(FN(isl_pw_aff,STRICT_ORDER),map),
+ &FN(FN(isl_pw_aff,ORDER),map), space);
+}
+
+/* Return a map containing pairs of elements in the domains of "mpa1" and "mpa2"
+ * where the function value of "mpa1" lexicographically compares as "ORDER"
+ * to that of "mpa2".
+ */
+__isl_give isl_map *FN(FN(isl_multi_pw_aff_lex,ORDER),map)(
+ __isl_take isl_multi_pw_aff *mpa1, __isl_take isl_multi_pw_aff *mpa2)
+{
+ return isl_multi_pw_aff_order_map(mpa1, mpa2,
+ &FN(FN(isl_multi_pw_aff_lex,ORDER),map_on_space));
+}
+
+/* Return the subset of "map" where the domain and the range
+ * have "mpa" values that lexicographically compare as "ORDER".
+ */
+__isl_give isl_map *FN(FN(isl_map_lex,ORDER),at_multi_pw_aff)(
+ __isl_take isl_map *map, __isl_take isl_multi_pw_aff *mpa)
+{
+ return isl_map_order_at_multi_pw_aff(map, mpa,
+ &FN(FN(isl_multi_pw_aff_lex,ORDER),map));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_map.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_map.c
new file mode 100644
index 00000000000..f661b824d2e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_map.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright 2011 INRIA Saclay
+ * Copyright 2012-2013 Ecole Normale Superieure
+ * Copyright 2016 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/ctx.h>
+#include <isl/space.h>
+#include <isl/local_space.h>
+#include <isl/union_map.h>
+#include <isl_map_private.h>
+#include <isl_aff_private.h>
+#include <isl_vec_private.h>
+#include <isl_seq.h>
+
+#include <bset_from_bmap.c>
+#include <set_from_map.c>
+
+/* Check that the input living in "space" lives in a map space.
+ * That is, check that "space" is a map space.
+ */
+static isl_stat check_input_is_map(__isl_keep isl_space *space)
+{
+ isl_bool is_set;
+
+ is_set = isl_space_is_set(space);
+ if (is_set < 0)
+ return isl_stat_error;
+ if (is_set)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "space of input is not a map", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Check that the input living in "space" lives in a set space.
+ * That is, check that "space" is a set space.
+ */
+static isl_stat check_input_is_set(__isl_keep isl_space *space)
+{
+ isl_bool is_set;
+
+ is_set = isl_space_is_set(space);
+ if (is_set < 0)
+ return isl_stat_error;
+ if (!is_set)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "space of input is not a set", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Construct a basic map mapping the domain of the affine expression
+ * to a one-dimensional range prescribed by the affine expression.
+ * If "rational" is set, then construct a rational basic map.
+ *
+ * A NaN affine expression cannot be converted to a basic map.
+ */
+static __isl_give isl_basic_map *isl_basic_map_from_aff2(
+ __isl_take isl_aff *aff, int rational)
+{
+ int k;
+ int pos;
+ isl_bool is_nan;
+ isl_local_space *ls;
+ isl_basic_map *bmap = NULL;
+
+ if (!aff)
+ return NULL;
+ is_nan = isl_aff_is_nan(aff);
+ if (is_nan < 0)
+ goto error;
+ if (is_nan)
+ isl_die(isl_aff_get_ctx(aff), isl_error_invalid,
+ "cannot convert NaN", goto error);
+
+ ls = isl_aff_get_local_space(aff);
+ bmap = isl_basic_map_from_local_space(ls);
+ bmap = isl_basic_map_extend_constraints(bmap, 1, 0);
+ k = isl_basic_map_alloc_equality(bmap);
+ if (k < 0)
+ goto error;
+
+ pos = isl_basic_map_offset(bmap, isl_dim_out);
+ isl_seq_cpy(bmap->eq[k], aff->v->el + 1, pos);
+ isl_int_neg(bmap->eq[k][pos], aff->v->el[0]);
+ isl_seq_cpy(bmap->eq[k] + pos + 1, aff->v->el + 1 + pos,
+ aff->v->size - (pos + 1));
+
+ isl_aff_free(aff);
+ if (rational)
+ bmap = isl_basic_map_set_rational(bmap);
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+error:
+ isl_aff_free(aff);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Construct a basic map mapping the domain of the affine expression
+ * to a one-dimensional range prescribed by the affine expression.
+ */
+__isl_give isl_basic_map *isl_basic_map_from_aff(__isl_take isl_aff *aff)
+{
+ return isl_basic_map_from_aff2(aff, 0);
+}
+
+/* Construct a map mapping the domain of the affine expression
+ * to a one-dimensional range prescribed by the affine expression.
+ */
+__isl_give isl_map *isl_map_from_aff(__isl_take isl_aff *aff)
+{
+ isl_basic_map *bmap;
+
+ bmap = isl_basic_map_from_aff(aff);
+ return isl_map_from_basic_map(bmap);
+}
+
+/* Construct a basic map mapping the domain of the multi-affine expression
+ * to its range, with each dimension in the range equated to the
+ * corresponding affine expression.
+ * If "rational" is set, then construct a rational basic map.
+ */
+__isl_give isl_basic_map *isl_basic_map_from_multi_aff2(
+ __isl_take isl_multi_aff *maff, int rational)
+{
+ int i;
+ isl_size dim;
+ isl_space *space;
+ isl_basic_map *bmap;
+
+ dim = isl_multi_aff_dim(maff, isl_dim_out);
+ if (dim < 0)
+ goto error;
+
+ if (dim != maff->n)
+ isl_die(isl_multi_aff_get_ctx(maff), isl_error_internal,
+ "invalid space", goto error);
+
+ space = isl_space_domain(isl_multi_aff_get_space(maff));
+ bmap = isl_basic_map_universe(isl_space_from_domain(space));
+ if (rational)
+ bmap = isl_basic_map_set_rational(bmap);
+
+ for (i = 0; i < maff->n; ++i) {
+ isl_aff *aff;
+ isl_basic_map *bmap_i;
+
+ aff = isl_aff_copy(maff->u.p[i]);
+ bmap_i = isl_basic_map_from_aff2(aff, rational);
+
+ bmap = isl_basic_map_flat_range_product(bmap, bmap_i);
+ }
+
+ bmap = isl_basic_map_reset_space(bmap, isl_multi_aff_get_space(maff));
+
+ isl_multi_aff_free(maff);
+ return bmap;
+error:
+ isl_multi_aff_free(maff);
+ return NULL;
+}
+
+/* Construct a basic map mapping the domain of the multi-affine expression
+ * to its range, with each dimension in the range equated to the
+ * corresponding affine expression.
+ * If "ma" lives in a set space, then the result is actually a set.
+ */
+static __isl_give isl_basic_map *basic_map_from_multi_aff(
+ __isl_take isl_multi_aff *ma)
+{
+ return isl_basic_map_from_multi_aff2(ma, 0);
+}
+
+/* Construct a basic map mapping the domain of the multi-affine expression
+ * to its range, with each dimension in the range equated to the
+ * corresponding affine expression.
+ */
+__isl_give isl_basic_map *isl_basic_map_from_multi_aff(
+ __isl_take isl_multi_aff *ma)
+{
+ if (check_input_is_map(isl_multi_aff_peek_space(ma)) < 0)
+ ma = isl_multi_aff_free(ma);
+ return basic_map_from_multi_aff(ma);
+}
+
+/* Construct a basic set mapping the parameter domain
+ * of the multi-affine expression to its space, with each dimension
+ * in the space equated to the corresponding affine expression.
+ */
+__isl_give isl_basic_set *isl_basic_set_from_multi_aff(
+ __isl_take isl_multi_aff *ma)
+{
+ if (check_input_is_set(isl_multi_aff_peek_space(ma)) < 0)
+ ma = isl_multi_aff_free(ma);
+ return bset_from_bmap(basic_map_from_multi_aff(ma));
+}
+
+/* Construct a map mapping the domain of the multi-affine expression
+ * to its range, with each dimension in the range equated to the
+ * corresponding affine expression.
+ * If "maff" lives in a set space, then the result is actually a set.
+ */
+__isl_give isl_map *isl_map_from_multi_aff_internal(
+ __isl_take isl_multi_aff *maff)
+{
+ isl_basic_map *bmap;
+
+ bmap = basic_map_from_multi_aff(maff);
+ return isl_map_from_basic_map(bmap);
+}
+
+/* Construct a map mapping the domain the multi-affine expression
+ * to its range, with each dimension in the range equated to the
+ * corresponding affine expression.
+ */
+__isl_give isl_map *isl_map_from_multi_aff(__isl_take isl_multi_aff *ma)
+{
+ if (check_input_is_map(isl_multi_aff_peek_space(ma)) < 0)
+ ma = isl_multi_aff_free(ma);
+ return isl_map_from_multi_aff_internal(ma);
+}
+
+/* This function performs the same operation as isl_map_from_multi_aff,
+ * but is considered as a function on an isl_multi_aff when exported.
+ */
+__isl_give isl_map *isl_multi_aff_as_map(__isl_take isl_multi_aff *ma)
+{
+ return isl_map_from_multi_aff(ma);
+}
+
+/* Construct a set mapping the parameter domain the multi-affine expression
+ * to its space, with each dimension in the space equated to the
+ * corresponding affine expression.
+ */
+__isl_give isl_set *isl_set_from_multi_aff(__isl_take isl_multi_aff *ma)
+{
+ if (check_input_is_set(isl_multi_aff_peek_space(ma)) < 0)
+ ma = isl_multi_aff_free(ma);
+ return isl_map_from_multi_aff_internal(ma);
+}
+
+/* This function performs the same operation as isl_set_from_multi_aff,
+ * but is considered as a function on an isl_multi_aff when exported.
+ */
+__isl_give isl_set *isl_multi_aff_as_set(__isl_take isl_multi_aff *ma)
+{
+ return isl_set_from_multi_aff(ma);
+}
+
+/* Construct a basic map mapping a domain in the given space to
+ * to an n-dimensional range, with n the number of elements in the list,
+ * where each coordinate in the range is prescribed by the
+ * corresponding affine expression.
+ * The domains of all affine expressions in the list are assumed to match
+ * domain_space.
+ */
+__isl_give isl_basic_map *isl_basic_map_from_aff_list(
+ __isl_take isl_space *domain_space, __isl_take isl_aff_list *list)
+{
+ int i;
+ isl_space *space;
+ isl_basic_map *bmap;
+
+ if (!list)
+ return NULL;
+
+ space = isl_space_from_domain(domain_space);
+ bmap = isl_basic_map_universe(space);
+
+ for (i = 0; i < list->n; ++i) {
+ isl_aff *aff;
+ isl_basic_map *bmap_i;
+
+ aff = isl_aff_copy(list->p[i]);
+ bmap_i = isl_basic_map_from_aff(aff);
+
+ bmap = isl_basic_map_flat_range_product(bmap, bmap_i);
+ }
+
+ isl_aff_list_free(list);
+ return bmap;
+}
+
+/* Construct a map with as domain the domain of pwaff and
+ * one-dimensional range corresponding to the affine expressions.
+ * If "pwaff" lives in a set space, then the result is actually a set.
+ */
+__isl_give isl_map *isl_map_from_pw_aff_internal(__isl_take isl_pw_aff *pwaff)
+{
+ int i;
+ isl_space *space;
+ isl_map *map;
+
+ if (!pwaff)
+ return NULL;
+
+ space = isl_pw_aff_get_space(pwaff);
+ map = isl_map_empty(space);
+
+ for (i = 0; i < pwaff->n; ++i) {
+ isl_basic_map *bmap;
+ isl_map *map_i;
+
+ bmap = isl_basic_map_from_aff(isl_aff_copy(pwaff->p[i].aff));
+ map_i = isl_map_from_basic_map(bmap);
+ map_i = isl_map_intersect_domain(map_i,
+ isl_set_copy(pwaff->p[i].set));
+ map = isl_map_union_disjoint(map, map_i);
+ }
+
+ isl_pw_aff_free(pwaff);
+
+ return map;
+}
+
+/* Construct a map with as domain the domain of pwaff and
+ * one-dimensional range corresponding to the affine expressions.
+ */
+__isl_give isl_map *isl_map_from_pw_aff(__isl_take isl_pw_aff *pwaff)
+{
+ if (check_input_is_map(isl_pw_aff_peek_space(pwaff)) < 0)
+ pwaff = isl_pw_aff_free(pwaff);
+ return isl_map_from_pw_aff_internal(pwaff);
+}
+
+/* This function performs the same operation as isl_map_from_pw_aff,
+ * but is considered as a function on an isl_pw_aff when exported.
+ */
+__isl_give isl_map *isl_pw_aff_as_map(__isl_take isl_pw_aff *pa)
+{
+ return isl_map_from_pw_aff(pa);
+}
+
+/* Construct a one-dimensional set with as parameter domain
+ * the domain of pwaff and the single set dimension
+ * corresponding to the affine expressions.
+ */
+__isl_give isl_set *isl_set_from_pw_aff(__isl_take isl_pw_aff *pwaff)
+{
+ if (check_input_is_set(isl_pw_aff_peek_space(pwaff)) < 0)
+ pwaff = isl_pw_aff_free(pwaff);
+ return set_from_map(isl_map_from_pw_aff_internal(pwaff));
+}
+
+/* Construct a map mapping the domain of the piecewise multi-affine expression
+ * to its range, with each dimension in the range equated to the
+ * corresponding affine expression on its cell.
+ * If "pma" lives in a set space, then the result is actually a set.
+ *
+ * If the domain of "pma" is rational, then so is the constructed "map".
+ */
+__isl_give isl_map *isl_map_from_pw_multi_aff_internal(
+ __isl_take isl_pw_multi_aff *pma)
+{
+ int i;
+ isl_map *map;
+
+ if (!pma)
+ return NULL;
+
+ map = isl_map_empty(isl_pw_multi_aff_get_space(pma));
+
+ for (i = 0; i < pma->n; ++i) {
+ isl_bool rational;
+ isl_multi_aff *maff;
+ isl_basic_map *bmap;
+ isl_map *map_i;
+
+ rational = isl_set_is_rational(pma->p[i].set);
+ if (rational < 0)
+ map = isl_map_free(map);
+ maff = isl_multi_aff_copy(pma->p[i].maff);
+ bmap = isl_basic_map_from_multi_aff2(maff, rational);
+ map_i = isl_map_from_basic_map(bmap);
+ map_i = isl_map_intersect_domain(map_i,
+ isl_set_copy(pma->p[i].set));
+ map = isl_map_union_disjoint(map, map_i);
+ }
+
+ isl_pw_multi_aff_free(pma);
+ return map;
+}
+
+/* Construct a map mapping the domain of the piecewise multi-affine expression
+ * to its range, with each dimension in the range equated to the
+ * corresponding affine expression on its cell.
+ */
+__isl_give isl_map *isl_map_from_pw_multi_aff(__isl_take isl_pw_multi_aff *pma)
+{
+ if (check_input_is_map(isl_pw_multi_aff_peek_space(pma)) < 0)
+ pma = isl_pw_multi_aff_free(pma);
+ return isl_map_from_pw_multi_aff_internal(pma);
+}
+
+/* This function performs the same operation as isl_map_from_pw_multi_aff,
+ * but is considered as a function on an isl_pw_multi_aff when exported.
+ */
+__isl_give isl_map *isl_pw_multi_aff_as_map(__isl_take isl_pw_multi_aff *pma)
+{
+ return isl_map_from_pw_multi_aff(pma);
+}
+
+__isl_give isl_set *isl_set_from_pw_multi_aff(__isl_take isl_pw_multi_aff *pma)
+{
+ if (check_input_is_set(isl_pw_multi_aff_peek_space(pma)) < 0)
+ pma = isl_pw_multi_aff_free(pma);
+ return set_from_map(isl_map_from_pw_multi_aff_internal(pma));
+}
+
+/* This function performs the same operation as isl_set_from_pw_multi_aff,
+ * but is considered as a function on an isl_pw_multi_aff when exported.
+ */
+__isl_give isl_set *isl_pw_multi_aff_as_set(__isl_take isl_pw_multi_aff *pma)
+{
+ return isl_set_from_pw_multi_aff(pma);
+}
+
+/* Construct a set or map mapping the shared (parameter) domain
+ * of the piecewise affine expressions to the range of "mpa"
+ * with each dimension in the range equated to the
+ * corresponding piecewise affine expression.
+ */
+static __isl_give isl_map *map_from_multi_pw_aff(
+ __isl_take isl_multi_pw_aff *mpa)
+{
+ int i;
+ isl_size dim;
+ isl_space *space;
+ isl_map *map;
+
+ dim = isl_multi_pw_aff_dim(mpa, isl_dim_out);
+ if (dim < 0)
+ goto error;
+
+ if (isl_space_dim(mpa->space, isl_dim_out) != mpa->n)
+ isl_die(isl_multi_pw_aff_get_ctx(mpa), isl_error_internal,
+ "invalid space", goto error);
+
+ space = isl_multi_pw_aff_get_domain_space(mpa);
+ map = isl_map_universe(isl_space_from_domain(space));
+
+ for (i = 0; i < mpa->n; ++i) {
+ isl_pw_aff *pa;
+ isl_map *map_i;
+
+ pa = isl_pw_aff_copy(mpa->u.p[i]);
+ map_i = isl_map_from_pw_aff_internal(pa);
+
+ map = isl_map_flat_range_product(map, map_i);
+ }
+
+ map = isl_map_reset_space(map, isl_multi_pw_aff_get_space(mpa));
+
+ isl_multi_pw_aff_free(mpa);
+ return map;
+error:
+ isl_multi_pw_aff_free(mpa);
+ return NULL;
+}
+
+/* Construct a map mapping the shared domain
+ * of the piecewise affine expressions to the range of "mpa"
+ * with each dimension in the range equated to the
+ * corresponding piecewise affine expression.
+ */
+__isl_give isl_map *isl_map_from_multi_pw_aff(__isl_take isl_multi_pw_aff *mpa)
+{
+ if (check_input_is_map(isl_multi_pw_aff_peek_space(mpa)) < 0)
+ mpa = isl_multi_pw_aff_free(mpa);
+ return map_from_multi_pw_aff(mpa);
+}
+
+/* This function performs the same operation as isl_map_from_multi_pw_aff,
+ * but is considered as a function on an isl_multi_pw_aff when exported.
+ */
+__isl_give isl_map *isl_multi_pw_aff_as_map(__isl_take isl_multi_pw_aff *mpa)
+{
+ return isl_map_from_multi_pw_aff(mpa);
+}
+
+/* Construct a set mapping the shared parameter domain
+ * of the piecewise affine expressions to the space of "mpa"
+ * with each dimension in the range equated to the
+ * corresponding piecewise affine expression.
+ */
+__isl_give isl_set *isl_set_from_multi_pw_aff(__isl_take isl_multi_pw_aff *mpa)
+{
+ if (check_input_is_set(isl_multi_pw_aff_peek_space(mpa)) < 0)
+ mpa = isl_multi_pw_aff_free(mpa);
+ return set_from_map(map_from_multi_pw_aff(mpa));
+}
+
+/* This function performs the same operation as isl_set_from_multi_pw_aff,
+ * but is considered as a function on an isl_multi_pw_aff when exported.
+ */
+__isl_give isl_set *isl_multi_pw_aff_as_set(__isl_take isl_multi_pw_aff *mpa)
+{
+ return isl_set_from_multi_pw_aff(mpa);
+}
+
+/* Convert "pa" to an isl_map and add it to *umap.
+ */
+static isl_stat map_from_pw_aff_entry(__isl_take isl_pw_aff *pa, void *user)
+{
+ isl_union_map **umap = user;
+ isl_map *map;
+
+ map = isl_map_from_pw_aff(pa);
+ *umap = isl_union_map_add_map(*umap, map);
+
+ return *umap ? isl_stat_ok : isl_stat_error;
+}
+
+/* Construct a union map mapping the domain of the union
+ * piecewise affine expression to its range, with the single output dimension
+ * equated to the corresponding affine expressions on their cells.
+ */
+__isl_give isl_union_map *isl_union_map_from_union_pw_aff(
+ __isl_take isl_union_pw_aff *upa)
+{
+ isl_space *space;
+ isl_union_map *umap;
+
+ if (!upa)
+ return NULL;
+
+ space = isl_union_pw_aff_get_space(upa);
+ umap = isl_union_map_empty(space);
+
+ if (isl_union_pw_aff_foreach_pw_aff(upa, &map_from_pw_aff_entry,
+ &umap) < 0)
+ umap = isl_union_map_free(umap);
+
+ isl_union_pw_aff_free(upa);
+ return umap;
+}
+
+/* Convert "pma" to an isl_map and add it to *umap.
+ */
+static isl_stat map_from_pw_multi_aff(__isl_take isl_pw_multi_aff *pma,
+ void *user)
+{
+ isl_union_map **umap = user;
+ isl_map *map;
+
+ map = isl_map_from_pw_multi_aff(pma);
+ *umap = isl_union_map_add_map(*umap, map);
+
+ return isl_stat_ok;
+}
+
+/* Construct a union map mapping the domain of the union
+ * piecewise multi-affine expression to its range, with each dimension
+ * in the range equated to the corresponding affine expression on its cell.
+ */
+__isl_give isl_union_map *isl_union_map_from_union_pw_multi_aff(
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ isl_space *space;
+ isl_union_map *umap;
+
+ if (!upma)
+ return NULL;
+
+ space = isl_union_pw_multi_aff_get_space(upma);
+ umap = isl_union_map_empty(space);
+
+ if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma,
+ &map_from_pw_multi_aff, &umap) < 0)
+ goto error;
+
+ isl_union_pw_multi_aff_free(upma);
+ return umap;
+error:
+ isl_union_pw_multi_aff_free(upma);
+ isl_union_map_free(umap);
+ return NULL;
+}
+
+/* This function performs the same operation as
+ * isl_union_map_from_union_pw_multi_aff,
+ * but is considered as a function on an isl_union_pw_multi_aff when exported.
+ */
+__isl_give isl_union_map *isl_union_pw_multi_aff_as_union_map(
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ return isl_union_map_from_union_pw_multi_aff(upma);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_private.h
new file mode 100644
index 00000000000..473add3b3ab
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_aff_private.h
@@ -0,0 +1,237 @@
+#ifndef ISL_AFF_PRIVATE_H
+#define ISL_AFF_PRIVATE_H
+
+#include <isl/aff.h>
+#include <isl/vec.h>
+#include <isl/mat.h>
+#include <isl/local_space.h>
+#include <isl_int.h>
+#include <isl_reordering.h>
+#include <isl/stream.h>
+
+/* ls represents the domain space.
+ *
+ * If the first two elements of "v" (the denominator and the constant term)
+ * are zero, then the isl_aff represents NaN.
+ */
+struct isl_aff {
+ int ref;
+
+ isl_local_space *ls;
+ isl_vec *v;
+};
+
+#undef EL
+#define EL isl_aff
+
+#include <isl_list_templ.h>
+
+struct isl_pw_aff_piece {
+ struct isl_set *set;
+ struct isl_aff *aff;
+};
+
+struct isl_pw_aff {
+ int ref;
+
+ isl_space *dim;
+
+ int n;
+
+ size_t size;
+ struct isl_pw_aff_piece p[1];
+};
+
+#undef PW
+#define PW isl_pw_aff
+
+#include <isl_pw_templ.h>
+
+#undef EL
+#define EL isl_pw_aff
+
+#include <isl_list_templ.h>
+
+struct isl_pw_multi_aff_piece {
+ isl_set *set;
+ isl_multi_aff *maff;
+};
+
+struct isl_pw_multi_aff {
+ int ref;
+
+ isl_space *dim;
+
+ int n;
+
+ size_t size;
+ struct isl_pw_multi_aff_piece p[1];
+};
+
+#undef PW
+#define PW isl_pw_multi_aff
+
+#include <isl_pw_templ.h>
+
+__isl_give isl_aff *isl_aff_alloc_vec(__isl_take isl_local_space *ls,
+ __isl_take isl_vec *v);
+__isl_give isl_aff *isl_aff_alloc(__isl_take isl_local_space *ls);
+
+isl_size isl_aff_domain_dim(__isl_keep isl_aff *aff, enum isl_dim_type type);
+isl_size isl_aff_domain_offset(__isl_keep isl_aff *aff, enum isl_dim_type type);
+
+__isl_give isl_aff *isl_aff_reset_space_and_domain(__isl_take isl_aff *aff,
+ __isl_take isl_space *space, __isl_take isl_space *domain);
+__isl_give isl_aff *isl_aff_reset_domain_space(__isl_take isl_aff *aff,
+ __isl_take isl_space *space);
+__isl_give isl_aff *isl_aff_realign_domain(__isl_take isl_aff *aff,
+ __isl_take isl_reordering *r);
+
+__isl_give isl_aff *isl_aff_set_constant(__isl_take isl_aff *aff, isl_int v);
+__isl_give isl_aff *isl_aff_set_coefficient(__isl_take isl_aff *aff,
+ enum isl_dim_type type, int pos, isl_int v);
+__isl_give isl_aff *isl_aff_add_constant(__isl_take isl_aff *aff, isl_int v);
+
+__isl_give isl_aff *isl_aff_domain_factor_domain(__isl_take isl_aff *aff);
+
+int isl_aff_plain_cmp(__isl_keep isl_aff *aff1, __isl_keep isl_aff *aff2);
+
+__isl_give isl_aff *isl_aff_remove_unused_divs(__isl_take isl_aff *aff);
+__isl_give isl_aff *isl_aff_normalize(__isl_take isl_aff *aff);
+
+__isl_give isl_aff *isl_aff_expand_divs( __isl_take isl_aff *aff,
+ __isl_take isl_mat *div, int *exp);
+
+__isl_give isl_aff *isl_stream_read_aff(__isl_keep isl_stream *s);
+
+__isl_give isl_pw_aff *isl_pw_aff_alloc_size(__isl_take isl_space *space,
+ int n);
+__isl_give isl_pw_aff *isl_pw_aff_reset_space(__isl_take isl_pw_aff *pwaff,
+ __isl_take isl_space *space);
+__isl_give isl_pw_aff *isl_pw_aff_reset_domain_space(
+ __isl_take isl_pw_aff *pwaff, __isl_take isl_space *space);
+__isl_give isl_pw_aff *isl_pw_aff_add_disjoint(
+ __isl_take isl_pw_aff *pwaff1, __isl_take isl_pw_aff *pwaff2);
+
+__isl_give isl_pw_aff *isl_pw_aff_domain_factor_domain(
+ __isl_take isl_pw_aff *pa);
+
+__isl_give isl_pw_aff *isl_pw_aff_union_opt(__isl_take isl_pw_aff *pwaff1,
+ __isl_take isl_pw_aff *pwaff2, int max);
+
+__isl_give isl_pw_aff *isl_pw_aff_set_rational(__isl_take isl_pw_aff *pwaff);
+__isl_give isl_pw_aff_list *isl_pw_aff_list_set_rational(
+ __isl_take isl_pw_aff_list *list);
+
+__isl_give isl_aff *isl_aff_scale_down(__isl_take isl_aff *aff, isl_int f);
+__isl_give isl_pw_aff *isl_pw_aff_scale(__isl_take isl_pw_aff *pwaff,
+ isl_int f);
+__isl_give isl_pw_aff *isl_pw_aff_scale_down(__isl_take isl_pw_aff *pwaff,
+ isl_int f);
+
+__isl_give isl_pw_aff *isl_stream_read_pw_aff(__isl_keep isl_stream *s);
+
+isl_bool isl_aff_matching_params(__isl_keep isl_aff *aff,
+ __isl_keep isl_space *space);
+isl_stat isl_aff_check_match_domain_space(__isl_keep isl_aff *aff,
+ __isl_keep isl_space *space);
+
+#undef BASE
+#define BASE aff
+
+#include <isl_multi_templ.h>
+
+__isl_give isl_multi_aff *isl_multi_aff_dup(__isl_keep isl_multi_aff *multi);
+
+__isl_give isl_multi_aff *isl_multi_aff_align_divs(
+ __isl_take isl_multi_aff *maff);
+
+__isl_give isl_multi_aff *isl_multi_aff_from_basic_set_equalities(
+ __isl_take isl_basic_set *bset);
+
+__isl_give isl_multi_aff *isl_multi_aff_from_aff_mat(
+ __isl_take isl_space *space, __isl_take isl_mat *mat);
+
+#undef EL
+#define EL isl_pw_multi_aff
+
+#include <isl_list_templ.h>
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_move_dims(
+ __isl_take isl_pw_multi_aff *pma,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_reset_domain_space(
+ __isl_take isl_pw_multi_aff *pwmaff, __isl_take isl_space *space);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_reset_space(
+ __isl_take isl_pw_multi_aff *pwmaff, __isl_take isl_space *space);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_add_disjoint(
+ __isl_take isl_pw_multi_aff *pma1, __isl_take isl_pw_multi_aff *pma2);
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_project_out(
+ __isl_take isl_pw_multi_aff *pma,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+isl_stat isl_seq_preimage(isl_int *dst, isl_int *src,
+ __isl_keep isl_multi_aff *ma, int n_before, int n_after,
+ int n_div_ma, int n_div_bmap,
+ isl_int f, isl_int c1, isl_int c2, isl_int g, int has_denom);
+
+__isl_give isl_aff *isl_aff_substitute_equalities(__isl_take isl_aff *aff,
+ __isl_take isl_basic_set *eq);
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_substitute(
+ __isl_take isl_pw_multi_aff *pma, unsigned pos,
+ __isl_keep isl_pw_aff *subs);
+
+__isl_give isl_pw_multi_aff *isl_stream_read_pw_multi_aff(
+ __isl_keep isl_stream *s);
+
+__isl_give isl_union_pw_aff *isl_stream_read_union_pw_aff(
+ __isl_keep isl_stream *s);
+
+isl_stat isl_pw_aff_check_named_params(__isl_keep isl_pw_aff *pa);
+isl_stat isl_multi_aff_check_named_params(__isl_keep isl_multi_aff *ma);
+isl_stat isl_pw_multi_aff_check_named_params(__isl_keep isl_pw_multi_aff *pma);
+
+isl_bool isl_pw_aff_matching_params(__isl_keep isl_pw_aff *pa,
+ __isl_keep isl_space *space);
+isl_stat isl_pw_aff_check_match_domain_space(__isl_keep isl_pw_aff *pa,
+ __isl_keep isl_space *space);
+
+__isl_give isl_basic_set *isl_aff_pos_basic_set(__isl_take isl_aff *aff);
+
+#undef BASE
+#define BASE pw_aff
+#undef DOMBASE
+#define DOMBASE set
+#define EXPLICIT_DOMAIN
+
+#include <isl_multi_templ.h>
+
+#undef EXPLICIT_DOMAIN
+
+__isl_give isl_map *isl_map_intersect_multi_pw_aff_explicit_domain(
+ __isl_take isl_map *map, __isl_keep isl_multi_pw_aff *mpa);
+
+#undef EL
+#define EL isl_union_pw_aff
+
+#include <isl_list_templ.h>
+
+#undef BASE
+#define BASE union_pw_aff
+#undef DOMBASE
+#define DOMBASE union_set
+#define EXPLICIT_DOMAIN
+
+#include <isl_multi_templ.h>
+
+#undef EXPLICIT_DOMAIN
+
+#undef EL
+#define EL isl_union_pw_multi_aff
+
+#include <isl_list_templ.h>
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_affine_hull.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_affine_hull.c
new file mode 100644
index 00000000000..38625091698
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_affine_hull.c
@@ -0,0 +1,1252 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2012 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl_seq.h>
+#include <isl/set.h>
+#include <isl/lp.h>
+#include <isl/map.h>
+#include "isl_equalities.h"
+#include "isl_sample.h"
+#include "isl_tab.h"
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+
+#include <bset_to_bmap.c>
+#include <bset_from_bmap.c>
+#include <set_to_map.c>
+#include <set_from_map.c>
+
+__isl_give isl_basic_map *isl_basic_map_implicit_equalities(
+ __isl_take isl_basic_map *bmap)
+{
+ struct isl_tab *tab;
+
+ if (!bmap)
+ return bmap;
+
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY))
+ return bmap;
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_NO_IMPLICIT))
+ return bmap;
+ if (bmap->n_ineq <= 1)
+ return bmap;
+
+ tab = isl_tab_from_basic_map(bmap, 0);
+ if (isl_tab_detect_implicit_equalities(tab) < 0)
+ goto error;
+ bmap = isl_basic_map_update_from_tab(bmap, tab);
+ isl_tab_free(tab);
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ ISL_F_SET(bmap, ISL_BASIC_MAP_NO_IMPLICIT);
+ return bmap;
+error:
+ isl_tab_free(tab);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_implicit_equalities(
+ __isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(
+ isl_basic_map_implicit_equalities(bset_to_bmap(bset)));
+}
+
+/* Make eq[row][col] of both bmaps equal so we can add the row
+ * add the column to the common matrix.
+ * Note that because of the echelon form, the columns of row row
+ * after column col are zero.
+ */
+static void set_common_multiple(
+ struct isl_basic_set *bset1, struct isl_basic_set *bset2,
+ unsigned row, unsigned col)
+{
+ isl_int m, c;
+
+ if (isl_int_eq(bset1->eq[row][col], bset2->eq[row][col]))
+ return;
+
+ isl_int_init(c);
+ isl_int_init(m);
+ isl_int_lcm(m, bset1->eq[row][col], bset2->eq[row][col]);
+ isl_int_divexact(c, m, bset1->eq[row][col]);
+ isl_seq_scale(bset1->eq[row], bset1->eq[row], c, col+1);
+ isl_int_divexact(c, m, bset2->eq[row][col]);
+ isl_seq_scale(bset2->eq[row], bset2->eq[row], c, col+1);
+ isl_int_clear(c);
+ isl_int_clear(m);
+}
+
+/* Delete a given equality, moving all the following equalities one up.
+ */
+static void delete_row(__isl_keep isl_basic_set *bset, unsigned row)
+{
+ isl_int *t;
+ int r;
+
+ t = bset->eq[row];
+ bset->n_eq--;
+ for (r = row; r < bset->n_eq; ++r)
+ bset->eq[r] = bset->eq[r+1];
+ bset->eq[bset->n_eq] = t;
+}
+
+/* Make first row entries in column col of bset1 identical to
+ * those of bset2, using the fact that entry bset1->eq[row][col]=a
+ * is non-zero. Initially, these elements of bset1 are all zero.
+ * For each row i < row, we set
+ * A[i] = a * A[i] + B[i][col] * A[row]
+ * B[i] = a * B[i]
+ * so that
+ * A[i][col] = B[i][col] = a * old(B[i][col])
+ */
+static isl_stat construct_column(
+ __isl_keep isl_basic_set *bset1, __isl_keep isl_basic_set *bset2,
+ unsigned row, unsigned col)
+{
+ int r;
+ isl_int a;
+ isl_int b;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset1, isl_dim_set);
+ if (total < 0)
+ return isl_stat_error;
+
+ isl_int_init(a);
+ isl_int_init(b);
+ for (r = 0; r < row; ++r) {
+ if (isl_int_is_zero(bset2->eq[r][col]))
+ continue;
+ isl_int_gcd(b, bset2->eq[r][col], bset1->eq[row][col]);
+ isl_int_divexact(a, bset1->eq[row][col], b);
+ isl_int_divexact(b, bset2->eq[r][col], b);
+ isl_seq_combine(bset1->eq[r], a, bset1->eq[r],
+ b, bset1->eq[row], 1 + total);
+ isl_seq_scale(bset2->eq[r], bset2->eq[r], a, 1 + total);
+ }
+ isl_int_clear(a);
+ isl_int_clear(b);
+ delete_row(bset1, row);
+
+ return isl_stat_ok;
+}
+
+/* Make first row entries in column col of bset1 identical to
+ * those of bset2, using only these entries of the two matrices.
+ * Let t be the last row with different entries.
+ * For each row i < t, we set
+ * A[i] = (A[t][col]-B[t][col]) * A[i] + (B[i][col]-A[i][col) * A[t]
+ * B[i] = (A[t][col]-B[t][col]) * B[i] + (B[i][col]-A[i][col) * B[t]
+ * so that
+ * A[i][col] = B[i][col] = old(A[t][col]*B[i][col]-A[i][col]*B[t][col])
+ */
+static isl_bool transform_column(
+ __isl_keep isl_basic_set *bset1, __isl_keep isl_basic_set *bset2,
+ unsigned row, unsigned col)
+{
+ int i, t;
+ isl_int a, b, g;
+ isl_size total;
+
+ for (t = row-1; t >= 0; --t)
+ if (isl_int_ne(bset1->eq[t][col], bset2->eq[t][col]))
+ break;
+ if (t < 0)
+ return isl_bool_false;
+
+ total = isl_basic_set_dim(bset1, isl_dim_set);
+ if (total < 0)
+ return isl_bool_error;
+ isl_int_init(a);
+ isl_int_init(b);
+ isl_int_init(g);
+ isl_int_sub(b, bset1->eq[t][col], bset2->eq[t][col]);
+ for (i = 0; i < t; ++i) {
+ isl_int_sub(a, bset2->eq[i][col], bset1->eq[i][col]);
+ isl_int_gcd(g, a, b);
+ isl_int_divexact(a, a, g);
+ isl_int_divexact(g, b, g);
+ isl_seq_combine(bset1->eq[i], g, bset1->eq[i], a, bset1->eq[t],
+ 1 + total);
+ isl_seq_combine(bset2->eq[i], g, bset2->eq[i], a, bset2->eq[t],
+ 1 + total);
+ }
+ isl_int_clear(a);
+ isl_int_clear(b);
+ isl_int_clear(g);
+ delete_row(bset1, t);
+ delete_row(bset2, t);
+ return isl_bool_true;
+}
+
+/* The implementation is based on Section 5.2 of Michael Karr,
+ * "Affine Relationships Among Variables of a Program",
+ * except that the echelon form we use starts from the last column
+ * and that we are dealing with integer coefficients.
+ */
+static __isl_give isl_basic_set *affine_hull(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2)
+{
+ isl_size dim;
+ unsigned total;
+ int col;
+ int row;
+
+ dim = isl_basic_set_dim(bset1, isl_dim_set);
+ if (dim < 0 || !bset2)
+ goto error;
+
+ total = 1 + dim;
+
+ row = 0;
+ for (col = total-1; col >= 0; --col) {
+ int is_zero1 = row >= bset1->n_eq ||
+ isl_int_is_zero(bset1->eq[row][col]);
+ int is_zero2 = row >= bset2->n_eq ||
+ isl_int_is_zero(bset2->eq[row][col]);
+ if (!is_zero1 && !is_zero2) {
+ set_common_multiple(bset1, bset2, row, col);
+ ++row;
+ } else if (!is_zero1 && is_zero2) {
+ if (construct_column(bset1, bset2, row, col) < 0)
+ goto error;
+ } else if (is_zero1 && !is_zero2) {
+ if (construct_column(bset2, bset1, row, col) < 0)
+ goto error;
+ } else {
+ isl_bool transform;
+
+ transform = transform_column(bset1, bset2, row, col);
+ if (transform < 0)
+ goto error;
+ if (transform)
+ --row;
+ }
+ }
+ isl_assert(bset1->ctx, row == bset1->n_eq, goto error);
+ isl_basic_set_free(bset2);
+ bset1 = isl_basic_set_normalize_constraints(bset1);
+ return bset1;
+error:
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ return NULL;
+}
+
+/* Find an integer point in the set represented by "tab"
+ * that lies outside of the equality "eq" e(x) = 0.
+ * If "up" is true, look for a point satisfying e(x) - 1 >= 0.
+ * Otherwise, look for a point satisfying -e(x) - 1 >= 0 (i.e., e(x) <= -1).
+ * The point, if found, is returned.
+ * If no point can be found, a zero-length vector is returned.
+ *
+ * Before solving an ILP problem, we first check if simply
+ * adding the normal of the constraint to one of the known
+ * integer points in the basic set represented by "tab"
+ * yields another point inside the basic set.
+ *
+ * The caller of this function ensures that the tableau is bounded or
+ * that tab->basis and tab->n_unbounded have been set appropriately.
+ */
+static __isl_give isl_vec *outside_point(struct isl_tab *tab, isl_int *eq,
+ int up)
+{
+ struct isl_ctx *ctx;
+ struct isl_vec *sample = NULL;
+ struct isl_tab_undo *snap;
+ unsigned dim;
+
+ if (!tab)
+ return NULL;
+ ctx = tab->mat->ctx;
+
+ dim = tab->n_var;
+ sample = isl_vec_alloc(ctx, 1 + dim);
+ if (!sample)
+ return NULL;
+ isl_int_set_si(sample->el[0], 1);
+ isl_seq_combine(sample->el + 1,
+ ctx->one, tab->bmap->sample->el + 1,
+ up ? ctx->one : ctx->negone, eq + 1, dim);
+ if (isl_basic_map_contains(tab->bmap, sample))
+ return sample;
+ isl_vec_free(sample);
+ sample = NULL;
+
+ snap = isl_tab_snap(tab);
+
+ if (!up)
+ isl_seq_neg(eq, eq, 1 + dim);
+ isl_int_sub_ui(eq[0], eq[0], 1);
+
+ if (isl_tab_extend_cons(tab, 1) < 0)
+ goto error;
+ if (isl_tab_add_ineq(tab, eq) < 0)
+ goto error;
+
+ sample = isl_tab_sample(tab);
+
+ isl_int_add_ui(eq[0], eq[0], 1);
+ if (!up)
+ isl_seq_neg(eq, eq, 1 + dim);
+
+ if (sample && isl_tab_rollback(tab, snap) < 0)
+ goto error;
+
+ return sample;
+error:
+ isl_vec_free(sample);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_recession_cone(
+ __isl_take isl_basic_set *bset)
+{
+ int i;
+ isl_bool empty;
+
+ empty = isl_basic_set_plain_is_empty(bset);
+ if (empty < 0)
+ return isl_basic_set_free(bset);
+ if (empty)
+ return bset;
+
+ bset = isl_basic_set_cow(bset);
+ if (isl_basic_set_check_no_locals(bset) < 0)
+ return isl_basic_set_free(bset);
+
+ for (i = 0; i < bset->n_eq; ++i)
+ isl_int_set_si(bset->eq[i][0], 0);
+
+ for (i = 0; i < bset->n_ineq; ++i)
+ isl_int_set_si(bset->ineq[i][0], 0);
+
+ ISL_F_CLR(bset, ISL_BASIC_SET_NO_IMPLICIT);
+ return isl_basic_set_implicit_equalities(bset);
+}
+
+/* Move "sample" to a point that is one up (or down) from the original
+ * point in dimension "pos".
+ */
+static void adjacent_point(__isl_keep isl_vec *sample, int pos, int up)
+{
+ if (up)
+ isl_int_add_ui(sample->el[1 + pos], sample->el[1 + pos], 1);
+ else
+ isl_int_sub_ui(sample->el[1 + pos], sample->el[1 + pos], 1);
+}
+
+/* Check if any points that are adjacent to "sample" also belong to "bset".
+ * If so, add them to "hull" and return the updated hull.
+ *
+ * Before checking whether and adjacent point belongs to "bset", we first
+ * check whether it already belongs to "hull" as this test is typically
+ * much cheaper.
+ */
+static __isl_give isl_basic_set *add_adjacent_points(
+ __isl_take isl_basic_set *hull, __isl_take isl_vec *sample,
+ __isl_keep isl_basic_set *bset)
+{
+ int i, up;
+ isl_size dim;
+
+ dim = isl_basic_set_dim(hull, isl_dim_set);
+ if (!sample || dim < 0)
+ goto error;
+
+ for (i = 0; i < dim; ++i) {
+ for (up = 0; up <= 1; ++up) {
+ int contains;
+ isl_basic_set *point;
+
+ adjacent_point(sample, i, up);
+ contains = isl_basic_set_contains(hull, sample);
+ if (contains < 0)
+ goto error;
+ if (contains) {
+ adjacent_point(sample, i, !up);
+ continue;
+ }
+ contains = isl_basic_set_contains(bset, sample);
+ if (contains < 0)
+ goto error;
+ if (contains) {
+ point = isl_basic_set_from_vec(
+ isl_vec_copy(sample));
+ hull = affine_hull(hull, point);
+ }
+ adjacent_point(sample, i, !up);
+ if (contains)
+ break;
+ }
+ }
+
+ isl_vec_free(sample);
+
+ return hull;
+error:
+ isl_vec_free(sample);
+ isl_basic_set_free(hull);
+ return NULL;
+}
+
+/* Extend an initial (under-)approximation of the affine hull of basic
+ * set represented by the tableau "tab"
+ * by looking for points that do not satisfy one of the equalities
+ * in the current approximation and adding them to that approximation
+ * until no such points can be found any more.
+ *
+ * The caller of this function ensures that "tab" is bounded or
+ * that tab->basis and tab->n_unbounded have been set appropriately.
+ *
+ * "bset" may be either NULL or the basic set represented by "tab".
+ * If "bset" is not NULL, we check for any point we find if any
+ * of its adjacent points also belong to "bset".
+ */
+static __isl_give isl_basic_set *extend_affine_hull(struct isl_tab *tab,
+ __isl_take isl_basic_set *hull, __isl_keep isl_basic_set *bset)
+{
+ int i, j;
+ unsigned dim;
+
+ if (!tab || !hull)
+ goto error;
+
+ dim = tab->n_var;
+
+ if (isl_tab_extend_cons(tab, 2 * dim + 1) < 0)
+ goto error;
+
+ for (i = 0; i < dim; ++i) {
+ struct isl_vec *sample;
+ struct isl_basic_set *point;
+ for (j = 0; j < hull->n_eq; ++j) {
+ sample = outside_point(tab, hull->eq[j], 1);
+ if (!sample)
+ goto error;
+ if (sample->size > 0)
+ break;
+ isl_vec_free(sample);
+ sample = outside_point(tab, hull->eq[j], 0);
+ if (!sample)
+ goto error;
+ if (sample->size > 0)
+ break;
+ isl_vec_free(sample);
+
+ if (isl_tab_add_eq(tab, hull->eq[j]) < 0)
+ goto error;
+ }
+ if (j == hull->n_eq)
+ break;
+ if (tab->samples &&
+ isl_tab_add_sample(tab, isl_vec_copy(sample)) < 0)
+ hull = isl_basic_set_free(hull);
+ if (bset)
+ hull = add_adjacent_points(hull, isl_vec_copy(sample),
+ bset);
+ point = isl_basic_set_from_vec(sample);
+ hull = affine_hull(hull, point);
+ if (!hull)
+ return NULL;
+ }
+
+ return hull;
+error:
+ isl_basic_set_free(hull);
+ return NULL;
+}
+
+/* Construct an initial underapproximation of the hull of "bset"
+ * from "sample" and any of its adjacent points that also belong to "bset".
+ */
+static __isl_give isl_basic_set *initialize_hull(__isl_keep isl_basic_set *bset,
+ __isl_take isl_vec *sample)
+{
+ isl_basic_set *hull;
+
+ hull = isl_basic_set_from_vec(isl_vec_copy(sample));
+ hull = add_adjacent_points(hull, sample, bset);
+
+ return hull;
+}
+
+/* Look for all equalities satisfied by the integer points in bset,
+ * which is assumed to be bounded.
+ *
+ * The equalities are obtained by successively looking for
+ * a point that is affinely independent of the points found so far.
+ * In particular, for each equality satisfied by the points so far,
+ * we check if there is any point on a hyperplane parallel to the
+ * corresponding hyperplane shifted by at least one (in either direction).
+ */
+static __isl_give isl_basic_set *uset_affine_hull_bounded(
+ __isl_take isl_basic_set *bset)
+{
+ struct isl_vec *sample = NULL;
+ struct isl_basic_set *hull;
+ struct isl_tab *tab = NULL;
+ isl_size dim;
+
+ if (isl_basic_set_plain_is_empty(bset))
+ return bset;
+
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ if (dim < 0)
+ return isl_basic_set_free(bset);
+
+ if (bset->sample && bset->sample->size == 1 + dim) {
+ int contains = isl_basic_set_contains(bset, bset->sample);
+ if (contains < 0)
+ goto error;
+ if (contains) {
+ if (dim == 0)
+ return bset;
+ sample = isl_vec_copy(bset->sample);
+ } else {
+ isl_vec_free(bset->sample);
+ bset->sample = NULL;
+ }
+ }
+
+ tab = isl_tab_from_basic_set(bset, 1);
+ if (!tab)
+ goto error;
+ if (tab->empty) {
+ isl_tab_free(tab);
+ isl_vec_free(sample);
+ return isl_basic_set_set_to_empty(bset);
+ }
+
+ if (!sample) {
+ struct isl_tab_undo *snap;
+ snap = isl_tab_snap(tab);
+ sample = isl_tab_sample(tab);
+ if (isl_tab_rollback(tab, snap) < 0)
+ goto error;
+ isl_vec_free(tab->bmap->sample);
+ tab->bmap->sample = isl_vec_copy(sample);
+ }
+
+ if (!sample)
+ goto error;
+ if (sample->size == 0) {
+ isl_tab_free(tab);
+ isl_vec_free(sample);
+ return isl_basic_set_set_to_empty(bset);
+ }
+
+ hull = initialize_hull(bset, sample);
+
+ hull = extend_affine_hull(tab, hull, bset);
+ isl_basic_set_free(bset);
+ isl_tab_free(tab);
+
+ return hull;
+error:
+ isl_vec_free(sample);
+ isl_tab_free(tab);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Given an unbounded tableau and an integer point satisfying the tableau,
+ * construct an initial affine hull containing the recession cone
+ * shifted to the given point.
+ *
+ * The unbounded directions are taken from the last rows of the basis,
+ * which is assumed to have been initialized appropriately.
+ */
+static __isl_give isl_basic_set *initial_hull(struct isl_tab *tab,
+ __isl_take isl_vec *vec)
+{
+ int i;
+ int k;
+ struct isl_basic_set *bset = NULL;
+ struct isl_ctx *ctx;
+ isl_size dim;
+
+ if (!vec || !tab)
+ return NULL;
+ ctx = vec->ctx;
+ isl_assert(ctx, vec->size != 0, goto error);
+
+ bset = isl_basic_set_alloc(ctx, 0, vec->size - 1, 0, vec->size - 1, 0);
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ if (dim < 0)
+ goto error;
+ dim -= tab->n_unbounded;
+ for (i = 0; i < dim; ++i) {
+ k = isl_basic_set_alloc_equality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bset->eq[k] + 1, tab->basis->row[1 + i] + 1,
+ vec->size - 1);
+ isl_seq_inner_product(bset->eq[k] + 1, vec->el +1,
+ vec->size - 1, &bset->eq[k][0]);
+ isl_int_neg(bset->eq[k][0], bset->eq[k][0]);
+ }
+ bset->sample = vec;
+ bset = isl_basic_set_gauss(bset, NULL);
+
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ isl_vec_free(vec);
+ return NULL;
+}
+
+/* Given a tableau of a set and a tableau of the corresponding
+ * recession cone, detect and add all equalities to the tableau.
+ * If the tableau is bounded, then we can simply keep the
+ * tableau in its state after the return from extend_affine_hull.
+ * However, if the tableau is unbounded, then
+ * isl_tab_set_initial_basis_with_cone will add some additional
+ * constraints to the tableau that have to be removed again.
+ * In this case, we therefore rollback to the state before
+ * any constraints were added and then add the equalities back in.
+ */
+struct isl_tab *isl_tab_detect_equalities(struct isl_tab *tab,
+ struct isl_tab *tab_cone)
+{
+ int j;
+ struct isl_vec *sample;
+ struct isl_basic_set *hull = NULL;
+ struct isl_tab_undo *snap;
+
+ if (!tab || !tab_cone)
+ goto error;
+
+ snap = isl_tab_snap(tab);
+
+ isl_mat_free(tab->basis);
+ tab->basis = NULL;
+
+ isl_assert(tab->mat->ctx, tab->bmap, goto error);
+ isl_assert(tab->mat->ctx, tab->samples, goto error);
+ isl_assert(tab->mat->ctx, tab->samples->n_col == 1 + tab->n_var, goto error);
+ isl_assert(tab->mat->ctx, tab->n_sample > tab->n_outside, goto error);
+
+ if (isl_tab_set_initial_basis_with_cone(tab, tab_cone) < 0)
+ goto error;
+
+ sample = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_var);
+ if (!sample)
+ goto error;
+
+ isl_seq_cpy(sample->el, tab->samples->row[tab->n_outside], sample->size);
+
+ isl_vec_free(tab->bmap->sample);
+ tab->bmap->sample = isl_vec_copy(sample);
+
+ if (tab->n_unbounded == 0)
+ hull = isl_basic_set_from_vec(isl_vec_copy(sample));
+ else
+ hull = initial_hull(tab, isl_vec_copy(sample));
+
+ for (j = tab->n_outside + 1; j < tab->n_sample; ++j) {
+ isl_seq_cpy(sample->el, tab->samples->row[j], sample->size);
+ hull = affine_hull(hull,
+ isl_basic_set_from_vec(isl_vec_copy(sample)));
+ }
+
+ isl_vec_free(sample);
+
+ hull = extend_affine_hull(tab, hull, NULL);
+ if (!hull)
+ goto error;
+
+ if (tab->n_unbounded == 0) {
+ isl_basic_set_free(hull);
+ return tab;
+ }
+
+ if (isl_tab_rollback(tab, snap) < 0)
+ goto error;
+
+ if (hull->n_eq > tab->n_zero) {
+ for (j = 0; j < hull->n_eq; ++j) {
+ isl_seq_normalize(tab->mat->ctx, hull->eq[j], 1 + tab->n_var);
+ if (isl_tab_add_eq(tab, hull->eq[j]) < 0)
+ goto error;
+ }
+ }
+
+ isl_basic_set_free(hull);
+
+ return tab;
+error:
+ isl_basic_set_free(hull);
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Compute the affine hull of "bset", where "cone" is the recession cone
+ * of "bset".
+ *
+ * We first compute a unimodular transformation that puts the unbounded
+ * directions in the last dimensions. In particular, we take a transformation
+ * that maps all equalities to equalities (in HNF) on the first dimensions.
+ * Let x be the original dimensions and y the transformed, with y_1 bounded
+ * and y_2 unbounded.
+ *
+ * [ y_1 ] [ y_1 ] [ Q_1 ]
+ * x = U [ y_2 ] [ y_2 ] = [ Q_2 ] x
+ *
+ * Let's call the input basic set S. We compute S' = preimage(S, U)
+ * and drop the final dimensions including any constraints involving them.
+ * This results in set S''.
+ * Then we compute the affine hull A'' of S''.
+ * Let F y_1 >= g be the constraint system of A''. In the transformed
+ * space the y_2 are unbounded, so we can add them back without any constraints,
+ * resulting in
+ *
+ * [ y_1 ]
+ * [ F 0 ] [ y_2 ] >= g
+ * or
+ * [ Q_1 ]
+ * [ F 0 ] [ Q_2 ] x >= g
+ * or
+ * F Q_1 x >= g
+ *
+ * The affine hull in the original space is then obtained as
+ * A = preimage(A'', Q_1).
+ */
+static __isl_give isl_basic_set *affine_hull_with_cone(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *cone)
+{
+ isl_size total;
+ unsigned cone_dim;
+ struct isl_basic_set *hull;
+ struct isl_mat *M, *U, *Q;
+
+ total = isl_basic_set_dim(cone, isl_dim_all);
+ if (!bset || total < 0)
+ goto error;
+
+ cone_dim = total - cone->n_eq;
+
+ M = isl_mat_sub_alloc6(bset->ctx, cone->eq, 0, cone->n_eq, 1, total);
+ M = isl_mat_left_hermite(M, 0, &U, &Q);
+ if (!M)
+ goto error;
+ isl_mat_free(M);
+
+ U = isl_mat_lin_to_aff(U);
+ bset = isl_basic_set_preimage(bset, isl_mat_copy(U));
+
+ bset = isl_basic_set_drop_constraints_involving(bset, total - cone_dim,
+ cone_dim);
+ bset = isl_basic_set_drop_dims(bset, total - cone_dim, cone_dim);
+
+ Q = isl_mat_lin_to_aff(Q);
+ Q = isl_mat_drop_rows(Q, 1 + total - cone_dim, cone_dim);
+
+ if (bset && bset->sample && bset->sample->size == 1 + total)
+ bset->sample = isl_mat_vec_product(isl_mat_copy(Q), bset->sample);
+
+ hull = uset_affine_hull_bounded(bset);
+
+ if (!hull) {
+ isl_mat_free(Q);
+ isl_mat_free(U);
+ } else {
+ struct isl_vec *sample = isl_vec_copy(hull->sample);
+ U = isl_mat_drop_cols(U, 1 + total - cone_dim, cone_dim);
+ if (sample && sample->size > 0)
+ sample = isl_mat_vec_product(U, sample);
+ else
+ isl_mat_free(U);
+ hull = isl_basic_set_preimage(hull, Q);
+ if (hull) {
+ isl_vec_free(hull->sample);
+ hull->sample = sample;
+ } else
+ isl_vec_free(sample);
+ }
+
+ isl_basic_set_free(cone);
+
+ return hull;
+error:
+ isl_basic_set_free(bset);
+ isl_basic_set_free(cone);
+ return NULL;
+}
+
+/* Look for all equalities satisfied by the integer points in bset,
+ * which is assumed not to have any explicit equalities.
+ *
+ * The equalities are obtained by successively looking for
+ * a point that is affinely independent of the points found so far.
+ * In particular, for each equality satisfied by the points so far,
+ * we check if there is any point on a hyperplane parallel to the
+ * corresponding hyperplane shifted by at least one (in either direction).
+ *
+ * Before looking for any outside points, we first compute the recession
+ * cone. The directions of this recession cone will always be part
+ * of the affine hull, so there is no need for looking for any points
+ * in these directions.
+ * In particular, if the recession cone is full-dimensional, then
+ * the affine hull is simply the whole universe.
+ */
+static __isl_give isl_basic_set *uset_affine_hull(
+ __isl_take isl_basic_set *bset)
+{
+ struct isl_basic_set *cone;
+ isl_size total;
+
+ if (isl_basic_set_plain_is_empty(bset))
+ return bset;
+
+ cone = isl_basic_set_recession_cone(isl_basic_set_copy(bset));
+ if (!cone)
+ goto error;
+ if (cone->n_eq == 0) {
+ isl_space *space;
+ space = isl_basic_set_get_space(bset);
+ isl_basic_set_free(cone);
+ isl_basic_set_free(bset);
+ return isl_basic_set_universe(space);
+ }
+
+ total = isl_basic_set_dim(cone, isl_dim_all);
+ if (total < 0)
+ bset = isl_basic_set_free(bset);
+ if (cone->n_eq < total)
+ return affine_hull_with_cone(bset, cone);
+
+ isl_basic_set_free(cone);
+ return uset_affine_hull_bounded(bset);
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Look for all equalities satisfied by the integer points in bmap
+ * that are independent of the equalities already explicitly available
+ * in bmap.
+ *
+ * We first remove all equalities already explicitly available,
+ * then look for additional equalities in the reduced space
+ * and then transform the result to the original space.
+ * The original equalities are _not_ added to this set. This is
+ * the responsibility of the calling function.
+ * The resulting basic set has all meaning about the dimensions removed.
+ * In particular, dimensions that correspond to existential variables
+ * in bmap and that are found to be fixed are not removed.
+ */
+static __isl_give isl_basic_set *equalities_in_underlying_set(
+ __isl_take isl_basic_map *bmap)
+{
+ struct isl_mat *T1 = NULL;
+ struct isl_mat *T2 = NULL;
+ struct isl_basic_set *bset = NULL;
+ struct isl_basic_set *hull = NULL;
+
+ bset = isl_basic_map_underlying_set(bmap);
+ if (!bset)
+ return NULL;
+ if (bset->n_eq)
+ bset = isl_basic_set_remove_equalities(bset, &T1, &T2);
+ if (!bset)
+ goto error;
+
+ hull = uset_affine_hull(bset);
+ if (!T2)
+ return hull;
+
+ if (!hull) {
+ isl_mat_free(T1);
+ isl_mat_free(T2);
+ } else {
+ struct isl_vec *sample = isl_vec_copy(hull->sample);
+ if (sample && sample->size > 0)
+ sample = isl_mat_vec_product(T1, sample);
+ else
+ isl_mat_free(T1);
+ hull = isl_basic_set_preimage(hull, T2);
+ if (hull) {
+ isl_vec_free(hull->sample);
+ hull->sample = sample;
+ } else
+ isl_vec_free(sample);
+ }
+
+ return hull;
+error:
+ isl_mat_free(T1);
+ isl_mat_free(T2);
+ isl_basic_set_free(bset);
+ isl_basic_set_free(hull);
+ return NULL;
+}
+
+/* Detect and make explicit all equalities satisfied by the (integer)
+ * points in bmap.
+ */
+__isl_give isl_basic_map *isl_basic_map_detect_equalities(
+ __isl_take isl_basic_map *bmap)
+{
+ int i, j;
+ isl_size total;
+ struct isl_basic_set *hull = NULL;
+
+ if (!bmap)
+ return NULL;
+ if (bmap->n_ineq == 0)
+ return bmap;
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY))
+ return bmap;
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_ALL_EQUALITIES))
+ return bmap;
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL))
+ return isl_basic_map_implicit_equalities(bmap);
+
+ hull = equalities_in_underlying_set(isl_basic_map_copy(bmap));
+ if (!hull)
+ goto error;
+ if (ISL_F_ISSET(hull, ISL_BASIC_SET_EMPTY)) {
+ isl_basic_set_free(hull);
+ return isl_basic_map_set_to_empty(bmap);
+ }
+ bmap = isl_basic_map_extend(bmap, 0, hull->n_eq, 0);
+ total = isl_basic_set_dim(hull, isl_dim_all);
+ if (total < 0)
+ goto error;
+ for (i = 0; i < hull->n_eq; ++i) {
+ j = isl_basic_map_alloc_equality(bmap);
+ if (j < 0)
+ goto error;
+ isl_seq_cpy(bmap->eq[j], hull->eq[i], 1 + total);
+ }
+ isl_vec_free(bmap->sample);
+ bmap->sample = isl_vec_copy(hull->sample);
+ isl_basic_set_free(hull);
+ ISL_F_SET(bmap, ISL_BASIC_MAP_NO_IMPLICIT | ISL_BASIC_MAP_ALL_EQUALITIES);
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_set_free(hull);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_detect_equalities(
+ __isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(
+ isl_basic_map_detect_equalities(bset_to_bmap(bset)));
+}
+
+__isl_give isl_map *isl_map_detect_equalities(__isl_take isl_map *map)
+{
+ return isl_map_inline_foreach_basic_map(map,
+ &isl_basic_map_detect_equalities);
+}
+
+__isl_give isl_set *isl_set_detect_equalities(__isl_take isl_set *set)
+{
+ return set_from_map(isl_map_detect_equalities(set_to_map(set)));
+}
+
+/* Return the superset of "bmap" described by the equalities
+ * satisfied by "bmap" that are already known.
+ */
+__isl_give isl_basic_map *isl_basic_map_plain_affine_hull(
+ __isl_take isl_basic_map *bmap)
+{
+ bmap = isl_basic_map_cow(bmap);
+ if (bmap)
+ isl_basic_map_free_inequality(bmap, bmap->n_ineq);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+}
+
+/* Return the superset of "bset" described by the equalities
+ * satisfied by "bset" that are already known.
+ */
+__isl_give isl_basic_set *isl_basic_set_plain_affine_hull(
+ __isl_take isl_basic_set *bset)
+{
+ return isl_basic_map_plain_affine_hull(bset);
+}
+
+/* After computing the rational affine hull (by detecting the implicit
+ * equalities), we compute the additional equalities satisfied by
+ * the integer points (if any) and add the original equalities back in.
+ */
+__isl_give isl_basic_map *isl_basic_map_affine_hull(
+ __isl_take isl_basic_map *bmap)
+{
+ bmap = isl_basic_map_detect_equalities(bmap);
+ bmap = isl_basic_map_plain_affine_hull(bmap);
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_affine_hull(
+ __isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(isl_basic_map_affine_hull(bset_to_bmap(bset)));
+}
+
+/* Given a rational affine matrix "M", add stride constraints to "bmap"
+ * that ensure that
+ *
+ * M(x)
+ *
+ * is an integer vector. The variables x include all the variables
+ * of "bmap" except the unknown divs.
+ *
+ * If d is the common denominator of M, then we need to impose that
+ *
+ * d M(x) = 0 mod d
+ *
+ * or
+ *
+ * exists alpha : d M(x) = d alpha
+ *
+ * This function is similar to add_strides in isl_morph.c
+ */
+static __isl_give isl_basic_map *add_strides(__isl_take isl_basic_map *bmap,
+ __isl_keep isl_mat *M, int n_known)
+{
+ int i, div, k;
+ isl_int gcd;
+
+ if (isl_int_is_one(M->row[0][0]))
+ return bmap;
+
+ bmap = isl_basic_map_extend(bmap, M->n_row - 1, M->n_row - 1, 0);
+
+ isl_int_init(gcd);
+ for (i = 1; i < M->n_row; ++i) {
+ isl_seq_gcd(M->row[i], M->n_col, &gcd);
+ if (isl_int_is_divisible_by(gcd, M->row[0][0]))
+ continue;
+ div = isl_basic_map_alloc_div(bmap);
+ if (div < 0)
+ goto error;
+ isl_int_set_si(bmap->div[div][0], 0);
+ k = isl_basic_map_alloc_equality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bmap->eq[k], M->row[i], M->n_col);
+ isl_seq_clr(bmap->eq[k] + M->n_col, bmap->n_div - n_known);
+ isl_int_set(bmap->eq[k][M->n_col - n_known + div],
+ M->row[0][0]);
+ }
+ isl_int_clear(gcd);
+
+ return bmap;
+error:
+ isl_int_clear(gcd);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* If there are any equalities that involve (multiple) unknown divs,
+ * then extract the stride information encoded by those equalities
+ * and make it explicitly available in "bmap".
+ *
+ * We first sort the divs so that the unknown divs appear last and
+ * then we count how many equalities involve these divs.
+ *
+ * Let these equalities be of the form
+ *
+ * A(x) + B y = 0
+ *
+ * where y represents the unknown divs and x the remaining variables.
+ * Let [H 0] be the Hermite Normal Form of B, i.e.,
+ *
+ * B = [H 0] Q
+ *
+ * Then x is a solution of the equalities iff
+ *
+ * H^-1 A(x) (= - [I 0] Q y)
+ *
+ * is an integer vector. Let d be the common denominator of H^-1.
+ * We impose
+ *
+ * d H^-1 A(x) = d alpha
+ *
+ * in add_strides, with alpha fresh existentially quantified variables.
+ */
+static __isl_give isl_basic_map *isl_basic_map_make_strides_explicit(
+ __isl_take isl_basic_map *bmap)
+{
+ isl_bool known;
+ int n_known;
+ int n, n_col;
+ isl_size v_div;
+ isl_ctx *ctx;
+ isl_mat *A, *B, *M;
+
+ known = isl_basic_map_divs_known(bmap);
+ if (known < 0)
+ return isl_basic_map_free(bmap);
+ if (known)
+ return bmap;
+ bmap = isl_basic_map_sort_divs(bmap);
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ if (!bmap)
+ return NULL;
+
+ for (n_known = 0; n_known < bmap->n_div; ++n_known)
+ if (isl_int_is_zero(bmap->div[n_known][0]))
+ break;
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0)
+ return isl_basic_map_free(bmap);
+ for (n = 0; n < bmap->n_eq; ++n)
+ if (isl_seq_first_non_zero(bmap->eq[n] + 1 + v_div + n_known,
+ bmap->n_div - n_known) == -1)
+ break;
+ if (n == 0)
+ return bmap;
+ ctx = isl_basic_map_get_ctx(bmap);
+ B = isl_mat_sub_alloc6(ctx, bmap->eq, 0, n, 0, 1 + v_div + n_known);
+ n_col = bmap->n_div - n_known;
+ A = isl_mat_sub_alloc6(ctx, bmap->eq, 0, n, 1 + v_div + n_known, n_col);
+ A = isl_mat_left_hermite(A, 0, NULL, NULL);
+ A = isl_mat_drop_cols(A, n, n_col - n);
+ A = isl_mat_lin_to_aff(A);
+ A = isl_mat_right_inverse(A);
+ B = isl_mat_insert_zero_rows(B, 0, 1);
+ B = isl_mat_set_element_si(B, 0, 0, 1);
+ M = isl_mat_product(A, B);
+ if (!M)
+ return isl_basic_map_free(bmap);
+ bmap = add_strides(bmap, M, n_known);
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ isl_mat_free(M);
+
+ return bmap;
+}
+
+/* Compute the affine hull of each basic map in "map" separately
+ * and make all stride information explicit so that we can remove
+ * all unknown divs without losing this information.
+ * The result is also guaranteed to be gaussed.
+ *
+ * In simple cases where a div is determined by an equality,
+ * calling isl_basic_map_gauss is enough to make the stride information
+ * explicit, as it will derive an explicit representation for the div
+ * from the equality. If, however, the stride information
+ * is encoded through multiple unknown divs then we need to make
+ * some extra effort in isl_basic_map_make_strides_explicit.
+ */
+static __isl_give isl_map *isl_map_local_affine_hull(__isl_take isl_map *map)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_affine_hull(map->p[i]);
+ map->p[i] = isl_basic_map_gauss(map->p[i], NULL);
+ map->p[i] = isl_basic_map_make_strides_explicit(map->p[i]);
+ if (!map->p[i])
+ return isl_map_free(map);
+ }
+
+ return map;
+}
+
+static __isl_give isl_set *isl_set_local_affine_hull(__isl_take isl_set *set)
+{
+ return isl_map_local_affine_hull(set);
+}
+
+/* Return an empty basic map living in the same space as "map".
+ */
+static __isl_give isl_basic_map *replace_map_by_empty_basic_map(
+ __isl_take isl_map *map)
+{
+ isl_space *space;
+
+ space = isl_map_get_space(map);
+ isl_map_free(map);
+ return isl_basic_map_empty(space);
+}
+
+/* Compute the affine hull of "map".
+ *
+ * We first compute the affine hull of each basic map separately.
+ * Then we align the divs and recompute the affine hulls of the basic
+ * maps since some of them may now have extra divs.
+ * In order to avoid performing parametric integer programming to
+ * compute explicit expressions for the divs, possible leading to
+ * an explosion in the number of basic maps, we first drop all unknown
+ * divs before aligning the divs. Note that isl_map_local_affine_hull tries
+ * to make sure that all stride information is explicitly available
+ * in terms of known divs. This involves calling isl_basic_set_gauss,
+ * which is also needed because affine_hull assumes its input has been gaussed,
+ * while isl_map_affine_hull may be called on input that has not been gaussed,
+ * in particular from initial_facet_constraint.
+ * Similarly, align_divs may reorder some divs so that we need to
+ * gauss the result again.
+ * Finally, we combine the individual affine hulls into a single
+ * affine hull.
+ */
+__isl_give isl_basic_map *isl_map_affine_hull(__isl_take isl_map *map)
+{
+ struct isl_basic_map *model = NULL;
+ struct isl_basic_map *hull = NULL;
+ struct isl_set *set;
+ isl_basic_set *bset;
+
+ map = isl_map_detect_equalities(map);
+ map = isl_map_local_affine_hull(map);
+ map = isl_map_remove_empty_parts(map);
+ map = isl_map_remove_unknown_divs(map);
+ map = isl_map_align_divs_internal(map);
+
+ if (!map)
+ return NULL;
+
+ if (map->n == 0)
+ return replace_map_by_empty_basic_map(map);
+
+ model = isl_basic_map_copy(map->p[0]);
+ set = isl_map_underlying_set(map);
+ set = isl_set_cow(set);
+ set = isl_set_local_affine_hull(set);
+ if (!set)
+ goto error;
+
+ while (set->n > 1)
+ set->p[0] = affine_hull(set->p[0], set->p[--set->n]);
+
+ bset = isl_basic_set_copy(set->p[0]);
+ hull = isl_basic_map_overlying_set(bset, model);
+ isl_set_free(set);
+ hull = isl_basic_map_simplify(hull);
+ return isl_basic_map_finalize(hull);
+error:
+ isl_basic_map_free(model);
+ isl_set_free(set);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_set_affine_hull(__isl_take isl_set *set)
+{
+ return bset_from_bmap(isl_map_affine_hull(set_to_map(set)));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_align_params_bin_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_align_params_bin_templ.c
new file mode 100644
index 00000000000..eae8d4d7fd0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_align_params_bin_templ.c
@@ -0,0 +1,8 @@
+#undef ARG1
+#define ARG1 TYPE
+#undef ARG2
+#define ARG2 TYPE
+#undef SUFFIX
+#define SUFFIX bin
+
+#include "isl_align_params_templ.c"
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_align_params_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_align_params_templ.c
new file mode 100644
index 00000000000..f56df495664
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_align_params_templ.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege
+ */
+
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Align the parameters of the two arguments of type ARG1 and ARG2
+ * (if needed).
+ */
+isl_stat FN(FN(ARG1,align_params),SUFFIX)(__isl_keep ARG1 **obj1,
+ __isl_keep ARG2 **obj2)
+{
+ isl_space *space1, *space2;
+ isl_bool equal_params;
+
+ space1 = FN(ARG1,peek_space)(*obj1);
+ space2 = FN(ARG2,peek_space)(*obj2);
+ equal_params = isl_space_has_equal_params(space1, space2);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params)
+ return isl_stat_ok;
+ if (FN(ARG1,check_named_params)(*obj1) < 0 ||
+ FN(ARG2,check_named_params)(*obj2) < 0)
+ goto error;
+ *obj1 = FN(ARG1,align_params)(*obj1, FN(ARG2,get_space)(*obj2));
+ *obj2 = FN(ARG2,align_params)(*obj2, FN(ARG1,get_space)(*obj1));
+ if (!*obj1 || !*obj2)
+ goto error;
+ return isl_stat_ok;
+error:
+ *obj1 = FN(ARG1,free)(*obj1);
+ *obj2 = FN(ARG2,free)(*obj2);
+ return isl_stat_error;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_arg.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_arg.c
new file mode 100644
index 00000000000..8a9e26d1c62
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_arg.c
@@ -0,0 +1,1312 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isl/arg.h>
+#include <isl/ctx.h>
+#include <isl_config.h>
+
+static struct isl_arg help_arg[] = {
+ISL_ARG_PHANTOM_BOOL('h', "help", NULL, "print this help, then exit")
+{ isl_arg_end }
+};
+
+static void set_default_choice(struct isl_arg *arg, void *opt)
+{
+ if (arg->offset == ISL_ARG_OFFSET_NONE)
+ return;
+ *(unsigned *)(((char *)opt) + arg->offset) = arg->u.choice.default_value;
+}
+
+static void set_default_flags(struct isl_arg *arg, void *opt)
+{
+ *(unsigned *)(((char *)opt) + arg->offset) = arg->u.flags.default_value;
+}
+
+static void set_default_bool(struct isl_arg *arg, void *opt)
+{
+ if (arg->offset == ISL_ARG_OFFSET_NONE)
+ return;
+ *(unsigned *)(((char *)opt) + arg->offset) = arg->u.b.default_value;
+}
+
+static void set_default_child(struct isl_arg *arg, void *opt)
+{
+ void *child;
+
+ if (arg->offset == ISL_ARG_OFFSET_NONE)
+ child = opt;
+ else {
+ child = calloc(1, arg->u.child.child->options_size);
+ *(void **)(((char *)opt) + arg->offset) = child;
+ }
+
+ if (child)
+ isl_args_set_defaults(arg->u.child.child, child);
+}
+
+static void set_default_user(struct isl_arg *arg, void *opt)
+{
+ arg->u.user.init(((char *)opt) + arg->offset);
+}
+
+static void set_default_int(struct isl_arg *arg, void *opt)
+{
+ *(int *)(((char *)opt) + arg->offset) = arg->u.i.default_value;
+}
+
+static void set_default_long(struct isl_arg *arg, void *opt)
+{
+ *(long *)(((char *)opt) + arg->offset) = arg->u.l.default_value;
+}
+
+static void set_default_ulong(struct isl_arg *arg, void *opt)
+{
+ *(unsigned long *)(((char *)opt) + arg->offset) = arg->u.ul.default_value;
+}
+
+static void set_default_str(struct isl_arg *arg, void *opt)
+{
+ const char *str = NULL;
+ if (arg->u.str.default_value)
+ str = strdup(arg->u.str.default_value);
+ *(const char **)(((char *)opt) + arg->offset) = str;
+}
+
+static void set_default_str_list(struct isl_arg *arg, void *opt)
+{
+ *(const char ***)(((char *) opt) + arg->offset) = NULL;
+ *(int *)(((char *) opt) + arg->u.str_list.offset_n) = 0;
+}
+
+void isl_args_set_defaults(struct isl_args *args, void *opt)
+{
+ int i;
+
+ for (i = 0; args->args[i].type != isl_arg_end; ++i) {
+ switch (args->args[i].type) {
+ case isl_arg_choice:
+ set_default_choice(&args->args[i], opt);
+ break;
+ case isl_arg_flags:
+ set_default_flags(&args->args[i], opt);
+ break;
+ case isl_arg_bool:
+ set_default_bool(&args->args[i], opt);
+ break;
+ case isl_arg_child:
+ set_default_child(&args->args[i], opt);
+ break;
+ case isl_arg_user:
+ set_default_user(&args->args[i], opt);
+ break;
+ case isl_arg_int:
+ set_default_int(&args->args[i], opt);
+ break;
+ case isl_arg_long:
+ set_default_long(&args->args[i], opt);
+ break;
+ case isl_arg_ulong:
+ set_default_ulong(&args->args[i], opt);
+ break;
+ case isl_arg_arg:
+ case isl_arg_str:
+ set_default_str(&args->args[i], opt);
+ break;
+ case isl_arg_str_list:
+ set_default_str_list(&args->args[i], opt);
+ break;
+ case isl_arg_alias:
+ case isl_arg_footer:
+ case isl_arg_version:
+ case isl_arg_end:
+ break;
+ }
+ }
+}
+
+static void free_args(struct isl_arg *arg, void *opt);
+
+static void free_child(struct isl_arg *arg, void *opt)
+{
+ if (arg->offset == ISL_ARG_OFFSET_NONE)
+ free_args(arg->u.child.child->args, opt);
+ else
+ isl_args_free(arg->u.child.child,
+ *(void **)(((char *)opt) + arg->offset));
+}
+
+static void free_str_list(struct isl_arg *arg, void *opt)
+{
+ int i;
+ int n = *(int *)(((char *) opt) + arg->u.str_list.offset_n);
+ char **list = *(char ***)(((char *) opt) + arg->offset);
+
+ for (i = 0; i < n; ++i)
+ free(list[i]);
+ free(list);
+}
+
+static void free_user(struct isl_arg *arg, void *opt)
+{
+ if (arg->u.user.clear)
+ arg->u.user.clear(((char *)opt) + arg->offset);
+}
+
+static void free_args(struct isl_arg *arg, void *opt)
+{
+ int i;
+
+ for (i = 0; arg[i].type != isl_arg_end; ++i) {
+ switch (arg[i].type) {
+ case isl_arg_child:
+ free_child(&arg[i], opt);
+ break;
+ case isl_arg_arg:
+ case isl_arg_str:
+ free(*(char **)(((char *)opt) + arg[i].offset));
+ break;
+ case isl_arg_str_list:
+ free_str_list(&arg[i], opt);
+ break;
+ case isl_arg_user:
+ free_user(&arg[i], opt);
+ break;
+ case isl_arg_alias:
+ case isl_arg_bool:
+ case isl_arg_choice:
+ case isl_arg_flags:
+ case isl_arg_int:
+ case isl_arg_long:
+ case isl_arg_ulong:
+ case isl_arg_version:
+ case isl_arg_footer:
+ case isl_arg_end:
+ break;
+ }
+ }
+}
+
+void isl_args_free(struct isl_args *args, void *opt)
+{
+ if (!opt)
+ return;
+
+ free_args(args->args, opt);
+
+ free(opt);
+}
+
+/* Data structure for collecting the prefixes of ancestor nodes.
+ *
+ * n is the number of prefixes.
+ * prefix[i] for i < n is a prefix of an ancestor.
+ * len[i] for i < n is the length of prefix[i].
+ */
+struct isl_prefixes {
+ int n;
+ const char *prefix[10];
+ size_t len[10];
+};
+
+/* Add "prefix" to the list of prefixes and return the updated
+ * number of prefixes.
+ */
+static int add_prefix(struct isl_prefixes *prefixes, const char *prefix)
+{
+ int n = prefixes->n;
+
+ if (!prefix)
+ return n;
+
+ if (prefixes->n >= 10) {
+ fprintf(stderr, "too many prefixes\n");
+ exit(EXIT_FAILURE);
+ }
+ prefixes->len[prefixes->n] = strlen(prefix);
+ prefixes->prefix[prefixes->n] = prefix;
+ prefixes->n++;
+
+ return n;
+}
+
+/* Drop all prefixes starting at "first".
+ */
+static void drop_prefix(struct isl_prefixes *prefixes, int first)
+{
+ prefixes->n = first;
+}
+
+/* Print the prefixes in "prefixes".
+ */
+static int print_prefixes(struct isl_prefixes *prefixes)
+{
+ int i;
+ int len = 0;
+
+ if (!prefixes)
+ return 0;
+
+ for (i = 0; i < prefixes->n; ++i) {
+ printf("%s-", prefixes->prefix[i]);
+ len += strlen(prefixes->prefix[i]) + 1;
+ }
+
+ return len;
+}
+
+/* Check if "name" starts with one or more of the prefixes in "prefixes",
+ * starting at *first. If so, advance the pointer beyond the prefixes
+ * and return the updated pointer. Additionally, update *first to
+ * the index after the last prefix found.
+ */
+static const char *skip_prefixes(const char *name,
+ struct isl_prefixes *prefixes, int *first)
+{
+ int i;
+
+ for (i = first ? *first : 0; i < prefixes->n; ++i) {
+ size_t len = prefixes->len[i];
+ const char *prefix = prefixes->prefix[i];
+ if (strncmp(name, prefix, len) == 0 && name[len] == '-') {
+ name += len + 1;
+ if (first)
+ *first = i + 1;
+ }
+ }
+
+ return name;
+}
+
+static int print_arg_help(struct isl_arg *decl, struct isl_prefixes *prefixes,
+ int no)
+{
+ int len = 0;
+
+ if (!decl->long_name) {
+ printf(" -%c", decl->short_name);
+ return 4;
+ }
+
+ if (decl->short_name) {
+ printf(" -%c, --", decl->short_name);
+ len += 8;
+ } else if (decl->flags & ISL_ARG_SINGLE_DASH) {
+ printf(" -");
+ len += 3;
+ } else {
+ printf(" --");
+ len += 8;
+ }
+
+ if (no) {
+ printf("no-");
+ len += 3;
+ }
+ len += print_prefixes(prefixes);
+ printf("%s", decl->long_name);
+ len += strlen(decl->long_name);
+
+ while ((++decl)->type == isl_arg_alias) {
+ printf(", --");
+ len += 4;
+ if (no) {
+ printf("no-");
+ len += 3;
+ }
+ printf("%s", decl->long_name);
+ len += strlen(decl->long_name);
+ }
+
+ return len;
+}
+
+const void *isl_memrchr(const void *s, int c, size_t n)
+{
+ const char *p = s;
+ while (n-- > 0)
+ if (p[n] == c)
+ return p + n;
+ return NULL;
+}
+
+static int wrap_msg(const char *s, int indent, int pos)
+{
+ int len;
+ int wrap_len = 75 - indent;
+
+ if (pos + 1 >= indent)
+ printf("\n%*s", indent, "");
+ else
+ printf("%*s", indent - pos, "");
+
+ len = strlen(s);
+ while (len > wrap_len) {
+ const char *space = isl_memrchr(s, ' ', wrap_len);
+ int l;
+
+ if (!space)
+ space = strchr(s + wrap_len, ' ');
+ if (!space)
+ break;
+ l = space - s;
+ printf("%.*s", l, s);
+ s = space + 1;
+ len -= l + 1;
+ printf("\n%*s", indent, "");
+ }
+
+ printf("%s", s);
+ return len;
+}
+
+static int print_help_msg(struct isl_arg *decl, int pos)
+{
+ if (!decl->help_msg)
+ return pos;
+
+ return wrap_msg(decl->help_msg, 30, pos);
+}
+
+static void print_default(struct isl_arg *decl, const char *def, int pos)
+{
+ const char *default_prefix = "[default: ";
+ const char *default_suffix = "]";
+ int len;
+
+ len = strlen(default_prefix) + strlen(def) + strlen(default_suffix);
+
+ if (!decl->help_msg) {
+ if (pos >= 29)
+ printf("\n%30s", "");
+ else
+ printf("%*s", 30 - pos, "");
+ } else {
+ if (pos + len >= 48)
+ printf("\n%30s", "");
+ else
+ printf(" ");
+ }
+ printf("%s%s%s", default_prefix, def, default_suffix);
+}
+
+static void print_default_choice(struct isl_arg *decl, void *opt, int pos)
+{
+ int i;
+ const char *s = "none";
+ unsigned *p;
+
+ p = (unsigned *)(((char *) opt) + decl->offset);
+ for (i = 0; decl->u.choice.choice[i].name; ++i)
+ if (decl->u.choice.choice[i].value == *p) {
+ s = decl->u.choice.choice[i].name;
+ break;
+ }
+
+ print_default(decl, s, pos);
+}
+
+static void print_choice_help(struct isl_arg *decl,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int i;
+ int pos;
+
+ pos = print_arg_help(decl, prefixes, 0);
+ printf("=");
+ pos++;
+
+ for (i = 0; decl->u.choice.choice[i].name; ++i) {
+ if (i) {
+ printf("|");
+ pos++;
+ }
+ printf("%s", decl->u.choice.choice[i].name);
+ pos += strlen(decl->u.choice.choice[i].name);
+ }
+
+ pos = print_help_msg(decl, pos);
+ print_default_choice(decl, opt, pos);
+
+ printf("\n");
+}
+
+static void print_default_flags(struct isl_arg *decl, void *opt, int pos)
+{
+ int i, first;
+ const char *default_prefix = "[default: ";
+ const char *default_suffix = "]";
+ int len = strlen(default_prefix) + strlen(default_suffix);
+ unsigned *p;
+
+ p = (unsigned *)(((char *) opt) + decl->offset);
+ for (i = 0; decl->u.flags.flags[i].name; ++i)
+ if ((*p & decl->u.flags.flags[i].mask) ==
+ decl->u.flags.flags[i].value)
+ len += strlen(decl->u.flags.flags[i].name);
+
+ if (!decl->help_msg) {
+ if (pos >= 29)
+ printf("\n%30s", "");
+ else
+ printf("%*s", 30 - pos, "");
+ } else {
+ if (pos + len >= 48)
+ printf("\n%30s", "");
+ else
+ printf(" ");
+ }
+ printf("%s", default_prefix);
+
+ for (first = 1, i = 0; decl->u.flags.flags[i].name; ++i)
+ if ((*p & decl->u.flags.flags[i].mask) ==
+ decl->u.flags.flags[i].value) {
+ if (!first)
+ printf(",");
+ printf("%s", decl->u.flags.flags[i].name);
+ first = 0;
+ }
+
+ printf("%s", default_suffix);
+}
+
+static void print_flags_help(struct isl_arg *decl,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int i, j;
+ int pos;
+
+ pos = print_arg_help(decl, prefixes, 0);
+ printf("=");
+ pos++;
+
+ for (i = 0; decl->u.flags.flags[i].name; ++i) {
+ if (i) {
+ printf(",");
+ pos++;
+ }
+ for (j = i;
+ decl->u.flags.flags[j].mask == decl->u.flags.flags[i].mask;
+ ++j) {
+ if (j != i) {
+ printf("|");
+ pos++;
+ }
+ printf("%s", decl->u.flags.flags[j].name);
+ pos += strlen(decl->u.flags.flags[j].name);
+ }
+ i = j - 1;
+ }
+
+ pos = print_help_msg(decl, pos);
+ print_default_flags(decl, opt, pos);
+
+ printf("\n");
+}
+
+static void print_bool_help(struct isl_arg *decl,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int pos;
+ unsigned *p = opt ? (unsigned *)(((char *) opt) + decl->offset) : NULL;
+ int no = p ? *p == 1 : 0;
+ pos = print_arg_help(decl, prefixes, no);
+ pos = print_help_msg(decl, pos);
+ if (decl->offset != ISL_ARG_OFFSET_NONE)
+ print_default(decl, no ? "yes" : "no", pos);
+ printf("\n");
+}
+
+static int print_argument_name(struct isl_arg *decl, const char *name, int pos)
+{
+ printf("%c<%s>", decl->long_name ? '=' : ' ', name);
+ return pos + 3 + strlen(name);
+}
+
+static void print_int_help(struct isl_arg *decl,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int pos;
+ char val[20];
+ int *p = (int *)(((char *) opt) + decl->offset);
+ pos = print_arg_help(decl, prefixes, 0);
+ pos = print_argument_name(decl, decl->argument_name, pos);
+ pos = print_help_msg(decl, pos);
+ snprintf(val, sizeof(val), "%d", *p);
+ print_default(decl, val, pos);
+ printf("\n");
+}
+
+static void print_long_help(struct isl_arg *decl,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int pos;
+ long *p = (long *)(((char *) opt) + decl->offset);
+ pos = print_arg_help(decl, prefixes, 0);
+ if (*p != decl->u.l.default_selected) {
+ printf("[");
+ pos++;
+ }
+ printf("=long");
+ pos += 5;
+ if (*p != decl->u.l.default_selected) {
+ printf("]");
+ pos++;
+ }
+ print_help_msg(decl, pos);
+ printf("\n");
+}
+
+static void print_ulong_help(struct isl_arg *decl,
+ struct isl_prefixes *prefixes)
+{
+ int pos;
+ pos = print_arg_help(decl, prefixes, 0);
+ printf("=ulong");
+ pos += 6;
+ print_help_msg(decl, pos);
+ printf("\n");
+}
+
+static void print_str_help(struct isl_arg *decl,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int pos;
+ const char *a = decl->argument_name ? decl->argument_name : "string";
+ const char **p = (const char **)(((char *) opt) + decl->offset);
+ pos = print_arg_help(decl, prefixes, 0);
+ pos = print_argument_name(decl, a, pos);
+ pos = print_help_msg(decl, pos);
+ if (*p)
+ print_default(decl, *p, pos);
+ printf("\n");
+}
+
+static void print_str_list_help(struct isl_arg *decl,
+ struct isl_prefixes *prefixes)
+{
+ int pos;
+ const char *a = decl->argument_name ? decl->argument_name : "string";
+ pos = print_arg_help(decl, prefixes, 0);
+ pos = print_argument_name(decl, a, pos);
+ pos = print_help_msg(decl, pos);
+ printf("\n");
+}
+
+static void print_help(struct isl_arg *arg,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int i;
+ int any = 0;
+
+ for (i = 0; arg[i].type != isl_arg_end; ++i) {
+ if (arg[i].flags & ISL_ARG_HIDDEN)
+ continue;
+ switch (arg[i].type) {
+ case isl_arg_flags:
+ print_flags_help(&arg[i], prefixes, opt);
+ any = 1;
+ break;
+ case isl_arg_choice:
+ print_choice_help(&arg[i], prefixes, opt);
+ any = 1;
+ break;
+ case isl_arg_bool:
+ print_bool_help(&arg[i], prefixes, opt);
+ any = 1;
+ break;
+ case isl_arg_int:
+ print_int_help(&arg[i], prefixes, opt);
+ any = 1;
+ break;
+ case isl_arg_long:
+ print_long_help(&arg[i], prefixes, opt);
+ any = 1;
+ break;
+ case isl_arg_ulong:
+ print_ulong_help(&arg[i], prefixes);
+ any = 1;
+ break;
+ case isl_arg_str:
+ print_str_help(&arg[i], prefixes, opt);
+ any = 1;
+ break;
+ case isl_arg_str_list:
+ print_str_list_help(&arg[i], prefixes);
+ any = 1;
+ break;
+ case isl_arg_alias:
+ case isl_arg_version:
+ case isl_arg_arg:
+ case isl_arg_footer:
+ case isl_arg_child:
+ case isl_arg_user:
+ case isl_arg_end:
+ break;
+ }
+ }
+
+ for (i = 0; arg[i].type != isl_arg_end; ++i) {
+ void *child;
+ int first;
+
+ if (arg[i].type != isl_arg_child)
+ continue;
+ if (arg[i].flags & ISL_ARG_HIDDEN)
+ continue;
+
+ if (any)
+ printf("\n");
+ if (arg[i].help_msg)
+ printf(" %s\n", arg[i].help_msg);
+ if (arg[i].offset == ISL_ARG_OFFSET_NONE)
+ child = opt;
+ else
+ child = *(void **)(((char *) opt) + arg[i].offset);
+ first = add_prefix(prefixes, arg[i].long_name);
+ print_help(arg[i].u.child.child->args, prefixes, child);
+ drop_prefix(prefixes, first);
+ any = 1;
+ }
+}
+
+static const char *prog_name(const char *prog)
+{
+ const char *slash;
+
+ slash = strrchr(prog, '/');
+ if (slash)
+ prog = slash + 1;
+ if (strncmp(prog, "lt-", 3) == 0)
+ prog += 3;
+
+ return prog;
+}
+
+static int any_version(struct isl_arg *decl)
+{
+ int i;
+
+ for (i = 0; decl[i].type != isl_arg_end; ++i) {
+ switch (decl[i].type) {
+ case isl_arg_version:
+ return 1;
+ case isl_arg_child:
+ if (any_version(decl[i].u.child.child->args))
+ return 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void print_help_and_exit(struct isl_arg *arg, const char *prog,
+ void *opt)
+{
+ int i;
+ struct isl_prefixes prefixes = { 0 };
+
+ printf("Usage: %s [OPTION...]", prog_name(prog));
+
+ for (i = 0; arg[i].type != isl_arg_end; ++i)
+ if (arg[i].type == isl_arg_arg)
+ printf(" %s", arg[i].argument_name);
+
+ printf("\n\n");
+
+ print_help(arg, &prefixes, opt);
+ printf("\n");
+ if (any_version(arg))
+ printf(" -V, --version\n");
+ print_bool_help(help_arg, NULL, NULL);
+
+ for (i = 0; arg[i].type != isl_arg_end; ++i) {
+ if (arg[i].type != isl_arg_footer)
+ continue;
+ wrap_msg(arg[i].help_msg, 0, 0);
+ printf("\n");
+ }
+
+ exit(0);
+}
+
+static int match_long_name(struct isl_arg *decl,
+ const char *start, const char *end)
+{
+ do {
+ if (end - start == strlen(decl->long_name) &&
+ !strncmp(start, decl->long_name, end - start))
+ return 1;
+ } while ((++decl)->type == isl_arg_alias);
+
+ return 0;
+}
+
+static const char *skip_dash_dash(struct isl_arg *decl, const char *arg)
+{
+ if (!strncmp(arg, "--", 2))
+ return arg + 2;
+ if ((decl->flags & ISL_ARG_SINGLE_DASH) && arg[0] == '-')
+ return arg + 1;
+ return NULL;
+}
+
+static const char *skip_name(struct isl_arg *decl, const char *arg,
+ struct isl_prefixes *prefixes, int need_argument, int *has_argument)
+{
+ const char *equal;
+ const char *name;
+ const char *end;
+
+ if (arg[0] == '-' && arg[1] && arg[1] == decl->short_name) {
+ if (need_argument && !arg[2])
+ return NULL;
+ if (has_argument)
+ *has_argument = arg[2] != '\0';
+ return arg + 2;
+ }
+ if (!decl->long_name)
+ return NULL;
+
+ name = skip_dash_dash(decl, arg);
+ if (!name)
+ return NULL;
+
+ equal = strchr(name, '=');
+ if (need_argument && !equal)
+ return NULL;
+
+ if (has_argument)
+ *has_argument = !!equal;
+ end = equal ? equal : name + strlen(name);
+
+ name = skip_prefixes(name, prefixes, NULL);
+
+ if (!match_long_name(decl, name, end))
+ return NULL;
+
+ return equal ? equal + 1 : end;
+}
+
+static int parse_choice_option(struct isl_arg *decl, char **arg,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int i;
+ int has_argument;
+ const char *choice;
+
+ choice = skip_name(decl, arg[0], prefixes, 0, &has_argument);
+ if (!choice)
+ return 0;
+
+ if (!has_argument && (!arg[1] || arg[1][0] == '-')) {
+ unsigned u = decl->u.choice.default_selected;
+ if (decl->offset != ISL_ARG_OFFSET_NONE)
+ *(unsigned *)(((char *)opt) + decl->offset) = u;
+ if (decl->u.choice.set)
+ decl->u.choice.set(opt, u);
+
+ return 1;
+ }
+
+ if (!has_argument)
+ choice = arg[1];
+
+ for (i = 0; decl->u.choice.choice[i].name; ++i) {
+ unsigned u;
+
+ if (strcmp(choice, decl->u.choice.choice[i].name))
+ continue;
+
+ u = decl->u.choice.choice[i].value;
+ if (decl->offset != ISL_ARG_OFFSET_NONE)
+ *(unsigned *)(((char *)opt) + decl->offset) = u;
+ if (decl->u.choice.set)
+ decl->u.choice.set(opt, u);
+
+ return has_argument ? 1 : 2;
+ }
+
+ return 0;
+}
+
+static int set_flag(struct isl_arg *decl, unsigned *val, const char *flag,
+ size_t len)
+{
+ int i;
+
+ for (i = 0; decl->u.flags.flags[i].name; ++i) {
+ if (strncmp(flag, decl->u.flags.flags[i].name, len))
+ continue;
+
+ *val &= ~decl->u.flags.flags[i].mask;
+ *val |= decl->u.flags.flags[i].value;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int parse_flags_option(struct isl_arg *decl, char **arg,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int has_argument;
+ const char *flags;
+ const char *comma;
+ unsigned val;
+
+ flags = skip_name(decl, arg[0], prefixes, 0, &has_argument);
+ if (!flags)
+ return 0;
+
+ if (!has_argument && !arg[1])
+ return 0;
+
+ if (!has_argument)
+ flags = arg[1];
+
+ val = 0;
+
+ while ((comma = strchr(flags, ',')) != NULL) {
+ if (!set_flag(decl, &val, flags, comma - flags))
+ return 0;
+ flags = comma + 1;
+ }
+ if (!set_flag(decl, &val, flags, strlen(flags)))
+ return 0;
+
+ *(unsigned *)(((char *)opt) + decl->offset) = val;
+
+ return has_argument ? 1 : 2;
+}
+
+static int parse_bool_option(struct isl_arg *decl, char **arg,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ const char *name;
+ unsigned *p = (unsigned *)(((char *)opt) + decl->offset);
+ int next_prefix;
+
+ if (skip_name(decl, arg[0], prefixes, 0, NULL)) {
+ if ((decl->flags & ISL_ARG_BOOL_ARG) && arg[1]) {
+ char *endptr;
+ int val = strtol(arg[1], &endptr, 0);
+ if (*endptr == '\0' && (val == 0 || val == 1)) {
+ if (decl->offset != ISL_ARG_OFFSET_NONE)
+ *p = val;
+ if (decl->u.b.set)
+ decl->u.b.set(opt, val);
+ return 2;
+ }
+ }
+ if (decl->offset != ISL_ARG_OFFSET_NONE)
+ *p = 1;
+ if (decl->u.b.set)
+ decl->u.b.set(opt, 1);
+
+ return 1;
+ }
+
+ if (!decl->long_name)
+ return 0;
+
+ name = skip_dash_dash(decl, arg[0]);
+ if (!name)
+ return 0;
+
+ next_prefix = 0;
+ name = skip_prefixes(name, prefixes, &next_prefix);
+
+ if (strncmp(name, "no-", 3))
+ return 0;
+ name += 3;
+
+ name = skip_prefixes(name, prefixes, &next_prefix);
+
+ if (match_long_name(decl, name, name + strlen(name))) {
+ if (decl->offset != ISL_ARG_OFFSET_NONE)
+ *p = 0;
+ if (decl->u.b.set)
+ decl->u.b.set(opt, 0);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int parse_str_option(struct isl_arg *decl, char **arg,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int has_argument;
+ const char *s;
+ char **p = (char **)(((char *)opt) + decl->offset);
+
+ s = skip_name(decl, arg[0], prefixes, 0, &has_argument);
+ if (!s)
+ return 0;
+
+ if (has_argument) {
+ free(*p);
+ *p = strdup(s);
+ return 1;
+ }
+
+ if (arg[1]) {
+ free(*p);
+ *p = strdup(arg[1]);
+ return 2;
+ }
+
+ return 0;
+}
+
+static int isl_arg_str_list_append(struct isl_arg *decl, void *opt,
+ const char *s)
+{
+ int *n = (int *)(((char *) opt) + decl->u.str_list.offset_n);
+ char **list = *(char ***)(((char *) opt) + decl->offset);
+
+ list = realloc(list, (*n + 1) * sizeof(char *));
+ if (!list)
+ return -1;
+ *(char ***)(((char *) opt) + decl->offset) = list;
+ list[*n] = strdup(s);
+ (*n)++;
+ return 0;
+}
+
+static int parse_str_list_option(struct isl_arg *decl, char **arg,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int has_argument;
+ const char *s;
+
+ s = skip_name(decl, arg[0], prefixes, 0, &has_argument);
+ if (!s)
+ return 0;
+
+ if (has_argument) {
+ isl_arg_str_list_append(decl, opt, s);
+ return 1;
+ }
+
+ if (arg[1]) {
+ isl_arg_str_list_append(decl, opt, arg[1]);
+ return 2;
+ }
+
+ return 0;
+}
+
+static int parse_int_option(struct isl_arg *decl, char **arg,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int has_argument;
+ const char *val;
+ char *endptr;
+ int *p = (int *)(((char *)opt) + decl->offset);
+
+ val = skip_name(decl, arg[0], prefixes, 0, &has_argument);
+ if (!val)
+ return 0;
+
+ if (has_argument) {
+ *p = atoi(val);
+ return 1;
+ }
+
+ if (arg[1]) {
+ int i = strtol(arg[1], &endptr, 0);
+ if (*endptr == '\0') {
+ *p = i;
+ return 2;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_long_option(struct isl_arg *decl, char **arg,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int has_argument;
+ const char *val;
+ char *endptr;
+ long *p = (long *)(((char *)opt) + decl->offset);
+
+ val = skip_name(decl, arg[0], prefixes, 0, &has_argument);
+ if (!val)
+ return 0;
+
+ if (has_argument) {
+ long l = strtol(val, NULL, 0);
+ *p = l;
+ if (decl->u.l.set)
+ decl->u.l.set(opt, l);
+ return 1;
+ }
+
+ if (arg[1]) {
+ long l = strtol(arg[1], &endptr, 0);
+ if (*endptr == '\0') {
+ *p = l;
+ if (decl->u.l.set)
+ decl->u.l.set(opt, l);
+ return 2;
+ }
+ }
+
+ if (decl->u.l.default_value != decl->u.l.default_selected) {
+ *p = decl->u.l.default_selected;
+ if (decl->u.l.set)
+ decl->u.l.set(opt, decl->u.l.default_selected);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int parse_ulong_option(struct isl_arg *decl, char **arg,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int has_argument;
+ const char *val;
+ char *endptr;
+ unsigned long *p = (unsigned long *)(((char *)opt) + decl->offset);
+
+ val = skip_name(decl, arg[0], prefixes, 0, &has_argument);
+ if (!val)
+ return 0;
+
+ if (has_argument) {
+ *p = strtoul(val, NULL, 0);
+ return 1;
+ }
+
+ if (arg[1]) {
+ unsigned long ul = strtoul(arg[1], &endptr, 0);
+ if (*endptr == '\0') {
+ *p = ul;
+ return 2;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_option(struct isl_arg *decl, char **arg,
+ struct isl_prefixes *prefixes, void *opt);
+
+static int parse_child_option(struct isl_arg *decl, char **arg,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ void *child;
+ int first, parsed;
+
+ if (decl->offset == ISL_ARG_OFFSET_NONE)
+ child = opt;
+ else
+ child = *(void **)(((char *)opt) + decl->offset);
+
+ first = add_prefix(prefixes, decl->long_name);
+ parsed = parse_option(decl->u.child.child->args, arg, prefixes, child);
+ drop_prefix(prefixes, first);
+
+ return parsed;
+}
+
+static int parse_option(struct isl_arg *decl, char **arg,
+ struct isl_prefixes *prefixes, void *opt)
+{
+ int i;
+
+ for (i = 0; decl[i].type != isl_arg_end; ++i) {
+ int parsed = 0;
+ switch (decl[i].type) {
+ case isl_arg_choice:
+ parsed = parse_choice_option(&decl[i], arg,
+ prefixes, opt);
+ break;
+ case isl_arg_flags:
+ parsed = parse_flags_option(&decl[i], arg,
+ prefixes, opt);
+ break;
+ case isl_arg_int:
+ parsed = parse_int_option(&decl[i], arg, prefixes, opt);
+ break;
+ case isl_arg_long:
+ parsed = parse_long_option(&decl[i], arg,
+ prefixes, opt);
+ break;
+ case isl_arg_ulong:
+ parsed = parse_ulong_option(&decl[i], arg,
+ prefixes, opt);
+ break;
+ case isl_arg_bool:
+ parsed = parse_bool_option(&decl[i], arg,
+ prefixes, opt);
+ break;
+ case isl_arg_str:
+ parsed = parse_str_option(&decl[i], arg, prefixes, opt);
+ break;
+ case isl_arg_str_list:
+ parsed = parse_str_list_option(&decl[i], arg, prefixes,
+ opt);
+ break;
+ case isl_arg_child:
+ parsed = parse_child_option(&decl[i], arg,
+ prefixes, opt);
+ break;
+ case isl_arg_alias:
+ case isl_arg_arg:
+ case isl_arg_footer:
+ case isl_arg_user:
+ case isl_arg_version:
+ case isl_arg_end:
+ break;
+ }
+ if (parsed)
+ return parsed;
+ }
+
+ return 0;
+}
+
+static void print_version(struct isl_arg *decl)
+{
+ int i;
+
+ for (i = 0; decl[i].type != isl_arg_end; ++i) {
+ switch (decl[i].type) {
+ case isl_arg_version:
+ decl[i].u.version.print_version();
+ break;
+ case isl_arg_child:
+ print_version(decl[i].u.child.child->args);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void print_version_and_exit(struct isl_arg *decl)
+{
+ print_version(decl);
+
+ exit(0);
+}
+
+static int drop_argument(int argc, char **argv, int drop, int n)
+{
+ for (; drop + n < argc; ++drop)
+ argv[drop] = argv[drop + n];
+
+ return argc - n;
+}
+
+static int n_arg(struct isl_arg *arg)
+{
+ int i;
+ int n_arg = 0;
+
+ for (i = 0; arg[i].type != isl_arg_end; ++i)
+ if (arg[i].type == isl_arg_arg)
+ n_arg++;
+
+ return n_arg;
+}
+
+static int next_arg(struct isl_arg *arg, int a)
+{
+ for (++a; arg[a].type != isl_arg_end; ++a)
+ if (arg[a].type == isl_arg_arg)
+ return a;
+
+ return -1;
+}
+
+/* Unless ISL_ARG_SKIP_HELP is set, check if "arg" is
+ * equal to "--help" or "-h" and if so call print_help_and_exit.
+ */
+static void check_help(struct isl_args *args, char *arg, char *prog, void *opt,
+ unsigned flags)
+{
+ if (ISL_FL_ISSET(flags, ISL_ARG_SKIP_HELP))
+ return;
+
+ if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0)
+ print_help_and_exit(args->args, prog, opt);
+}
+
+int isl_args_parse(struct isl_args *args, int argc, char **argv, void *opt,
+ unsigned flags)
+{
+ int a = -1;
+ int skip = 0;
+ int i;
+ int n;
+ struct isl_prefixes prefixes = { 0 };
+
+ n = n_arg(args->args);
+
+ for (i = 1; i < argc; ++i) {
+ if ((strcmp(argv[i], "--version") == 0 ||
+ strcmp(argv[i], "-V") == 0) && any_version(args->args))
+ print_version_and_exit(args->args);
+ }
+
+ while (argc > 1 + skip) {
+ int parsed;
+ if (argv[1 + skip][0] != '-') {
+ a = next_arg(args->args, a);
+ if (a >= 0) {
+ char **p;
+ p = (char **)(((char *)opt)+args->args[a].offset);
+ free(*p);
+ *p = strdup(argv[1 + skip]);
+ argc = drop_argument(argc, argv, 1 + skip, 1);
+ --n;
+ } else if (ISL_FL_ISSET(flags, ISL_ARG_ALL)) {
+ fprintf(stderr, "%s: extra argument: %s\n",
+ prog_name(argv[0]), argv[1 + skip]);
+ exit(-1);
+ } else
+ ++skip;
+ continue;
+ }
+ check_help(args, argv[1 + skip], argv[0], opt, flags);
+ parsed = parse_option(args->args, &argv[1 + skip],
+ &prefixes, opt);
+ if (parsed)
+ argc = drop_argument(argc, argv, 1 + skip, parsed);
+ else if (ISL_FL_ISSET(flags, ISL_ARG_ALL)) {
+ fprintf(stderr, "%s: unrecognized option: %s\n",
+ prog_name(argv[0]), argv[1 + skip]);
+ exit(-1);
+ } else
+ ++skip;
+ }
+
+ if (n > 0) {
+ fprintf(stderr, "%s: expecting %d more argument(s)\n",
+ prog_name(argv[0]), n);
+ exit(-1);
+ }
+
+ return argc;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast.c
new file mode 100644
index 00000000000..6bf517893f7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast.c
@@ -0,0 +1,2972 @@
+/*
+ * Copyright 2012-2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <string.h>
+
+#include <isl/id.h>
+#include <isl/val.h>
+#include <isl_ast_private.h>
+
+#undef EL_BASE
+#define EL_BASE ast_expr
+
+#include <isl_list_templ.c>
+
+#undef EL_BASE
+#define EL_BASE ast_node
+
+#include <isl_list_templ.c>
+
+isl_ctx *isl_ast_print_options_get_ctx(
+ __isl_keep isl_ast_print_options *options)
+{
+ return options ? options->ctx : NULL;
+}
+
+__isl_give isl_ast_print_options *isl_ast_print_options_alloc(isl_ctx *ctx)
+{
+ isl_ast_print_options *options;
+
+ options = isl_calloc_type(ctx, isl_ast_print_options);
+ if (!options)
+ return NULL;
+
+ options->ctx = ctx;
+ isl_ctx_ref(ctx);
+ options->ref = 1;
+
+ return options;
+}
+
+__isl_give isl_ast_print_options *isl_ast_print_options_dup(
+ __isl_keep isl_ast_print_options *options)
+{
+ isl_ctx *ctx;
+ isl_ast_print_options *dup;
+
+ if (!options)
+ return NULL;
+
+ ctx = isl_ast_print_options_get_ctx(options);
+ dup = isl_ast_print_options_alloc(ctx);
+ if (!dup)
+ return NULL;
+
+ dup->print_for = options->print_for;
+ dup->print_for_user = options->print_for_user;
+ dup->print_user = options->print_user;
+ dup->print_user_user = options->print_user_user;
+
+ return dup;
+}
+
+__isl_give isl_ast_print_options *isl_ast_print_options_cow(
+ __isl_take isl_ast_print_options *options)
+{
+ if (!options)
+ return NULL;
+
+ if (options->ref == 1)
+ return options;
+ options->ref--;
+ return isl_ast_print_options_dup(options);
+}
+
+__isl_give isl_ast_print_options *isl_ast_print_options_copy(
+ __isl_keep isl_ast_print_options *options)
+{
+ if (!options)
+ return NULL;
+
+ options->ref++;
+ return options;
+}
+
+__isl_null isl_ast_print_options *isl_ast_print_options_free(
+ __isl_take isl_ast_print_options *options)
+{
+ if (!options)
+ return NULL;
+
+ if (--options->ref > 0)
+ return NULL;
+
+ isl_ctx_deref(options->ctx);
+
+ free(options);
+ return NULL;
+}
+
+/* Set the print_user callback of "options" to "print_user".
+ *
+ * If this callback is set, then it is used to print user nodes in the AST.
+ * Otherwise, the expression associated to the user node is printed.
+ */
+__isl_give isl_ast_print_options *isl_ast_print_options_set_print_user(
+ __isl_take isl_ast_print_options *options,
+ __isl_give isl_printer *(*print_user)(__isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *options,
+ __isl_keep isl_ast_node *node, void *user),
+ void *user)
+{
+ options = isl_ast_print_options_cow(options);
+ if (!options)
+ return NULL;
+
+ options->print_user = print_user;
+ options->print_user_user = user;
+
+ return options;
+}
+
+/* Set the print_for callback of "options" to "print_for".
+ *
+ * If this callback is set, then it used to print for nodes in the AST.
+ */
+__isl_give isl_ast_print_options *isl_ast_print_options_set_print_for(
+ __isl_take isl_ast_print_options *options,
+ __isl_give isl_printer *(*print_for)(__isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *options,
+ __isl_keep isl_ast_node *node, void *user),
+ void *user)
+{
+ options = isl_ast_print_options_cow(options);
+ if (!options)
+ return NULL;
+
+ options->print_for = print_for;
+ options->print_for_user = user;
+
+ return options;
+}
+
+__isl_give isl_ast_expr *isl_ast_expr_copy(__isl_keep isl_ast_expr *expr)
+{
+ if (!expr)
+ return NULL;
+
+ expr->ref++;
+ return expr;
+}
+
+__isl_give isl_ast_expr *isl_ast_expr_dup(__isl_keep isl_ast_expr *expr)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_ast_expr *dup;
+
+ if (!expr)
+ return NULL;
+
+ ctx = isl_ast_expr_get_ctx(expr);
+ switch (expr->type) {
+ case isl_ast_expr_int:
+ dup = isl_ast_expr_from_val(isl_val_copy(expr->u.v));
+ break;
+ case isl_ast_expr_id:
+ dup = isl_ast_expr_from_id(isl_id_copy(expr->u.id));
+ break;
+ case isl_ast_expr_op:
+ dup = isl_ast_expr_alloc_op(ctx,
+ expr->u.op.op, expr->u.op.n_arg);
+ if (!dup)
+ return NULL;
+ for (i = 0; i < expr->u.op.n_arg; ++i)
+ dup->u.op.args[i] =
+ isl_ast_expr_copy(expr->u.op.args[i]);
+ break;
+ case isl_ast_expr_error:
+ dup = NULL;
+ }
+
+ if (!dup)
+ return NULL;
+
+ return dup;
+}
+
+__isl_give isl_ast_expr *isl_ast_expr_cow(__isl_take isl_ast_expr *expr)
+{
+ if (!expr)
+ return NULL;
+
+ if (expr->ref == 1)
+ return expr;
+ expr->ref--;
+ return isl_ast_expr_dup(expr);
+}
+
+__isl_null isl_ast_expr *isl_ast_expr_free(__isl_take isl_ast_expr *expr)
+{
+ int i;
+
+ if (!expr)
+ return NULL;
+
+ if (--expr->ref > 0)
+ return NULL;
+
+ isl_ctx_deref(expr->ctx);
+
+ switch (expr->type) {
+ case isl_ast_expr_int:
+ isl_val_free(expr->u.v);
+ break;
+ case isl_ast_expr_id:
+ isl_id_free(expr->u.id);
+ break;
+ case isl_ast_expr_op:
+ if (expr->u.op.args)
+ for (i = 0; i < expr->u.op.n_arg; ++i)
+ isl_ast_expr_free(expr->u.op.args[i]);
+ free(expr->u.op.args);
+ break;
+ case isl_ast_expr_error:
+ break;
+ }
+
+ free(expr);
+ return NULL;
+}
+
+isl_ctx *isl_ast_expr_get_ctx(__isl_keep isl_ast_expr *expr)
+{
+ return expr ? expr->ctx : NULL;
+}
+
+enum isl_ast_expr_type isl_ast_expr_get_type(__isl_keep isl_ast_expr *expr)
+{
+ return expr ? expr->type : isl_ast_expr_error;
+}
+
+/* Return the integer value represented by "expr".
+ */
+__isl_give isl_val *isl_ast_expr_int_get_val(__isl_keep isl_ast_expr *expr)
+{
+ if (!expr)
+ return NULL;
+ if (expr->type != isl_ast_expr_int)
+ isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid,
+ "expression not an int", return NULL);
+ return isl_val_copy(expr->u.v);
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_val *isl_ast_expr_get_val(__isl_keep isl_ast_expr *expr)
+{
+ return isl_ast_expr_int_get_val(expr);
+}
+
+__isl_give isl_id *isl_ast_expr_id_get_id(__isl_keep isl_ast_expr *expr)
+{
+ if (!expr)
+ return NULL;
+ if (expr->type != isl_ast_expr_id)
+ isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid,
+ "expression not an identifier", return NULL);
+
+ return isl_id_copy(expr->u.id);
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_id *isl_ast_expr_get_id(__isl_keep isl_ast_expr *expr)
+{
+ return isl_ast_expr_id_get_id(expr);
+}
+
+/* Return the type of operation represented by "expr".
+ */
+enum isl_ast_expr_op_type isl_ast_expr_op_get_type(
+ __isl_keep isl_ast_expr *expr)
+{
+ if (!expr)
+ return isl_ast_expr_op_error;
+ if (expr->type != isl_ast_expr_op)
+ isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid,
+ "expression not an operation",
+ return isl_ast_expr_op_error);
+ return expr->u.op.op;
+}
+
+/* This is an alternative name for the function above.
+ */
+enum isl_ast_expr_op_type isl_ast_expr_get_op_type(
+ __isl_keep isl_ast_expr *expr)
+{
+ return isl_ast_expr_op_get_type(expr);
+}
+
+/* Return the number of arguments of the operation represented by "expr".
+ */
+isl_size isl_ast_expr_op_get_n_arg(__isl_keep isl_ast_expr *expr)
+{
+ if (!expr)
+ return isl_size_error;
+ if (expr->type != isl_ast_expr_op)
+ isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid,
+ "expression not an operation", return isl_size_error);
+ return expr->u.op.n_arg;
+}
+
+/* This is an alternative name for the function above.
+ */
+isl_size isl_ast_expr_get_op_n_arg(__isl_keep isl_ast_expr *expr)
+{
+ return isl_ast_expr_op_get_n_arg(expr);
+}
+
+/* Return the argument at position "pos" of the operation represented by "expr".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_op_get_arg(__isl_keep isl_ast_expr *expr,
+ int pos)
+{
+ if (!expr)
+ return NULL;
+ if (expr->type != isl_ast_expr_op)
+ isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid,
+ "expression not an operation", return NULL);
+ if (pos < 0 || pos >= expr->u.op.n_arg)
+ isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid,
+ "index out of bounds", return NULL);
+
+ return isl_ast_expr_copy(expr->u.op.args[pos]);
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_ast_expr *isl_ast_expr_get_op_arg(__isl_keep isl_ast_expr *expr,
+ int pos)
+{
+ return isl_ast_expr_op_get_arg(expr, pos);
+}
+
+/* Replace the argument at position "pos" of "expr" by "arg".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_set_op_arg(__isl_take isl_ast_expr *expr,
+ int pos, __isl_take isl_ast_expr *arg)
+{
+ expr = isl_ast_expr_cow(expr);
+ if (!expr || !arg)
+ goto error;
+ if (expr->type != isl_ast_expr_op)
+ isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid,
+ "expression not an operation", goto error);
+ if (pos < 0 || pos >= expr->u.op.n_arg)
+ isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid,
+ "index out of bounds", goto error);
+
+ isl_ast_expr_free(expr->u.op.args[pos]);
+ expr->u.op.args[pos] = arg;
+
+ return expr;
+error:
+ isl_ast_expr_free(arg);
+ return isl_ast_expr_free(expr);
+}
+
+/* Is "expr1" equal to "expr2"?
+ */
+isl_bool isl_ast_expr_is_equal(__isl_keep isl_ast_expr *expr1,
+ __isl_keep isl_ast_expr *expr2)
+{
+ int i;
+
+ if (!expr1 || !expr2)
+ return isl_bool_error;
+
+ if (expr1 == expr2)
+ return isl_bool_true;
+ if (expr1->type != expr2->type)
+ return isl_bool_false;
+ switch (expr1->type) {
+ case isl_ast_expr_int:
+ return isl_val_eq(expr1->u.v, expr2->u.v);
+ case isl_ast_expr_id:
+ return isl_bool_ok(expr1->u.id == expr2->u.id);
+ case isl_ast_expr_op:
+ if (expr1->u.op.op != expr2->u.op.op)
+ return isl_bool_false;
+ if (expr1->u.op.n_arg != expr2->u.op.n_arg)
+ return isl_bool_false;
+ for (i = 0; i < expr1->u.op.n_arg; ++i) {
+ isl_bool equal;
+ equal = isl_ast_expr_is_equal(expr1->u.op.args[i],
+ expr2->u.op.args[i]);
+ if (equal < 0 || !equal)
+ return equal;
+ }
+ return isl_bool_true;
+ case isl_ast_expr_error:
+ return isl_bool_error;
+ }
+
+ isl_die(isl_ast_expr_get_ctx(expr1), isl_error_internal,
+ "unhandled case", return isl_bool_error);
+}
+
+/* Create a new operation expression of operation type "op",
+ * with "n_arg" as yet unspecified arguments.
+ */
+__isl_give isl_ast_expr *isl_ast_expr_alloc_op(isl_ctx *ctx,
+ enum isl_ast_expr_op_type op, int n_arg)
+{
+ isl_ast_expr *expr;
+
+ expr = isl_calloc_type(ctx, isl_ast_expr);
+ if (!expr)
+ return NULL;
+
+ expr->ctx = ctx;
+ isl_ctx_ref(ctx);
+ expr->ref = 1;
+ expr->type = isl_ast_expr_op;
+ expr->u.op.op = op;
+ expr->u.op.n_arg = n_arg;
+ expr->u.op.args = isl_calloc_array(ctx, isl_ast_expr *, n_arg);
+
+ if (n_arg && !expr->u.op.args)
+ return isl_ast_expr_free(expr);
+
+ return expr;
+}
+
+/* Create a new id expression representing "id".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_from_id(__isl_take isl_id *id)
+{
+ isl_ctx *ctx;
+ isl_ast_expr *expr;
+
+ if (!id)
+ return NULL;
+
+ ctx = isl_id_get_ctx(id);
+ expr = isl_calloc_type(ctx, isl_ast_expr);
+ if (!expr)
+ goto error;
+
+ expr->ctx = ctx;
+ isl_ctx_ref(ctx);
+ expr->ref = 1;
+ expr->type = isl_ast_expr_id;
+ expr->u.id = id;
+
+ return expr;
+error:
+ isl_id_free(id);
+ return NULL;
+}
+
+/* Create a new integer expression representing "i".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_alloc_int_si(isl_ctx *ctx, int i)
+{
+ isl_ast_expr *expr;
+
+ expr = isl_calloc_type(ctx, isl_ast_expr);
+ if (!expr)
+ return NULL;
+
+ expr->ctx = ctx;
+ isl_ctx_ref(ctx);
+ expr->ref = 1;
+ expr->type = isl_ast_expr_int;
+ expr->u.v = isl_val_int_from_si(ctx, i);
+ if (!expr->u.v)
+ return isl_ast_expr_free(expr);
+
+ return expr;
+}
+
+/* Create a new integer expression representing "v".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_from_val(__isl_take isl_val *v)
+{
+ isl_ctx *ctx;
+ isl_ast_expr *expr;
+
+ if (!v)
+ return NULL;
+ if (!isl_val_is_int(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting integer value", goto error);
+
+ ctx = isl_val_get_ctx(v);
+ expr = isl_calloc_type(ctx, isl_ast_expr);
+ if (!expr)
+ goto error;
+
+ expr->ctx = ctx;
+ isl_ctx_ref(ctx);
+ expr->ref = 1;
+ expr->type = isl_ast_expr_int;
+ expr->u.v = v;
+
+ return expr;
+error:
+ isl_val_free(v);
+ return NULL;
+}
+
+/* Create an expression representing the unary operation "type" applied to
+ * "arg".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_alloc_unary(
+ enum isl_ast_expr_op_type type, __isl_take isl_ast_expr *arg)
+{
+ isl_ctx *ctx;
+ isl_ast_expr *expr = NULL;
+
+ if (!arg)
+ return NULL;
+
+ ctx = isl_ast_expr_get_ctx(arg);
+ expr = isl_ast_expr_alloc_op(ctx, type, 1);
+ if (!expr)
+ goto error;
+
+ expr->u.op.args[0] = arg;
+
+ return expr;
+error:
+ isl_ast_expr_free(arg);
+ return NULL;
+}
+
+/* Create an expression representing the negation of "arg".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_neg(__isl_take isl_ast_expr *arg)
+{
+ return isl_ast_expr_alloc_unary(isl_ast_expr_op_minus, arg);
+}
+
+/* Create an expression representing the address of "expr".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_address_of(__isl_take isl_ast_expr *expr)
+{
+ if (!expr)
+ return NULL;
+
+ if (isl_ast_expr_get_type(expr) != isl_ast_expr_op ||
+ isl_ast_expr_get_op_type(expr) != isl_ast_expr_op_access)
+ isl_die(isl_ast_expr_get_ctx(expr), isl_error_invalid,
+ "can only take address of access expressions",
+ return isl_ast_expr_free(expr));
+
+ return isl_ast_expr_alloc_unary(isl_ast_expr_op_address_of, expr);
+}
+
+/* Create an expression representing the binary operation "type"
+ * applied to "expr1" and "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_alloc_binary(
+ enum isl_ast_expr_op_type type,
+ __isl_take isl_ast_expr *expr1, __isl_take isl_ast_expr *expr2)
+{
+ isl_ctx *ctx;
+ isl_ast_expr *expr = NULL;
+
+ if (!expr1 || !expr2)
+ goto error;
+
+ ctx = isl_ast_expr_get_ctx(expr1);
+ expr = isl_ast_expr_alloc_op(ctx, type, 2);
+ if (!expr)
+ goto error;
+
+ expr->u.op.args[0] = expr1;
+ expr->u.op.args[1] = expr2;
+
+ return expr;
+error:
+ isl_ast_expr_free(expr1);
+ isl_ast_expr_free(expr2);
+ return NULL;
+}
+
+/* Create an expression representing the sum of "expr1" and "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_add(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_add, expr1, expr2);
+}
+
+/* Create an expression representing the difference of "expr1" and "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_sub(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_sub, expr1, expr2);
+}
+
+/* Create an expression representing the product of "expr1" and "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_mul(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_mul, expr1, expr2);
+}
+
+/* Create an expression representing the quotient of "expr1" and "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_div(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_div, expr1, expr2);
+}
+
+/* Create an expression representing the quotient of the integer
+ * division of "expr1" by "expr2", where "expr1" is known to be
+ * non-negative.
+ */
+__isl_give isl_ast_expr *isl_ast_expr_pdiv_q(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_pdiv_q, expr1, expr2);
+}
+
+/* Create an expression representing the remainder of the integer
+ * division of "expr1" by "expr2", where "expr1" is known to be
+ * non-negative.
+ */
+__isl_give isl_ast_expr *isl_ast_expr_pdiv_r(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_pdiv_r, expr1, expr2);
+}
+
+/* Create an expression representing the conjunction of "expr1" and "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_and(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_and, expr1, expr2);
+}
+
+/* Create an expression representing the conjunction of "expr1" and "expr2",
+ * where "expr2" is evaluated only if "expr1" is evaluated to true.
+ */
+__isl_give isl_ast_expr *isl_ast_expr_and_then(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_and_then, expr1, expr2);
+}
+
+/* Create an expression representing the disjunction of "expr1" and "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_or(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_or, expr1, expr2);
+}
+
+/* Create an expression representing the disjunction of "expr1" and "expr2",
+ * where "expr2" is evaluated only if "expr1" is evaluated to false.
+ */
+__isl_give isl_ast_expr *isl_ast_expr_or_else(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_or_else, expr1, expr2);
+}
+
+/* Create an expression representing "expr1" less than or equal to "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_le(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_le, expr1, expr2);
+}
+
+/* Create an expression representing "expr1" less than "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_lt(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_lt, expr1, expr2);
+}
+
+/* Create an expression representing "expr1" greater than or equal to "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_ge(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_ge, expr1, expr2);
+}
+
+/* Create an expression representing "expr1" greater than "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_gt(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_gt, expr1, expr2);
+}
+
+/* Create an expression representing "expr1" equal to "expr2".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_eq(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_eq, expr1, expr2);
+}
+
+/* Create an expression of type "type" with as arguments "arg0" followed
+ * by "arguments".
+ */
+static __isl_give isl_ast_expr *ast_expr_with_arguments(
+ enum isl_ast_expr_op_type type, __isl_take isl_ast_expr *arg0,
+ __isl_take isl_ast_expr_list *arguments)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_ast_expr *res = NULL;
+
+ if (!arg0 || !arguments)
+ goto error;
+
+ ctx = isl_ast_expr_get_ctx(arg0);
+ n = isl_ast_expr_list_n_ast_expr(arguments);
+ if (n < 0)
+ goto error;
+ res = isl_ast_expr_alloc_op(ctx, type, 1 + n);
+ if (!res)
+ goto error;
+ for (i = 0; i < n; ++i) {
+ isl_ast_expr *arg;
+ arg = isl_ast_expr_list_get_ast_expr(arguments, i);
+ res->u.op.args[1 + i] = arg;
+ if (!arg)
+ goto error;
+ }
+ res->u.op.args[0] = arg0;
+
+ isl_ast_expr_list_free(arguments);
+ return res;
+error:
+ isl_ast_expr_free(arg0);
+ isl_ast_expr_list_free(arguments);
+ isl_ast_expr_free(res);
+ return NULL;
+}
+
+/* Create an expression representing an access to "array" with index
+ * expressions "indices".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_access(__isl_take isl_ast_expr *array,
+ __isl_take isl_ast_expr_list *indices)
+{
+ return ast_expr_with_arguments(isl_ast_expr_op_access, array, indices);
+}
+
+/* Create an expression representing a call to "function" with argument
+ * expressions "arguments".
+ */
+__isl_give isl_ast_expr *isl_ast_expr_call(__isl_take isl_ast_expr *function,
+ __isl_take isl_ast_expr_list *arguments)
+{
+ return ast_expr_with_arguments(isl_ast_expr_op_call, function, arguments);
+}
+
+/* For each subexpression of "expr" of type isl_ast_expr_id,
+ * if it appears in "id2expr", then replace it by the corresponding
+ * expression.
+ */
+__isl_give isl_ast_expr *isl_ast_expr_substitute_ids(
+ __isl_take isl_ast_expr *expr, __isl_take isl_id_to_ast_expr *id2expr)
+{
+ int i;
+ isl_maybe_isl_ast_expr m;
+
+ if (!expr || !id2expr)
+ goto error;
+
+ switch (expr->type) {
+ case isl_ast_expr_int:
+ break;
+ case isl_ast_expr_id:
+ m = isl_id_to_ast_expr_try_get(id2expr, expr->u.id);
+ if (m.valid < 0)
+ goto error;
+ if (!m.valid)
+ break;
+ isl_ast_expr_free(expr);
+ expr = m.value;
+ break;
+ case isl_ast_expr_op:
+ for (i = 0; i < expr->u.op.n_arg; ++i) {
+ isl_ast_expr *arg;
+ arg = isl_ast_expr_copy(expr->u.op.args[i]);
+ arg = isl_ast_expr_substitute_ids(arg,
+ isl_id_to_ast_expr_copy(id2expr));
+ if (arg == expr->u.op.args[i]) {
+ isl_ast_expr_free(arg);
+ continue;
+ }
+ if (!arg)
+ expr = isl_ast_expr_free(expr);
+ expr = isl_ast_expr_cow(expr);
+ if (!expr) {
+ isl_ast_expr_free(arg);
+ break;
+ }
+ isl_ast_expr_free(expr->u.op.args[i]);
+ expr->u.op.args[i] = arg;
+ }
+ break;
+ case isl_ast_expr_error:
+ expr = isl_ast_expr_free(expr);
+ break;
+ }
+
+ isl_id_to_ast_expr_free(id2expr);
+ return expr;
+error:
+ isl_ast_expr_free(expr);
+ isl_id_to_ast_expr_free(id2expr);
+ return NULL;
+}
+
+isl_ctx *isl_ast_node_get_ctx(__isl_keep isl_ast_node *node)
+{
+ return node ? node->ctx : NULL;
+}
+
+enum isl_ast_node_type isl_ast_node_get_type(__isl_keep isl_ast_node *node)
+{
+ return node ? node->type : isl_ast_node_error;
+}
+
+__isl_give isl_ast_node *isl_ast_node_alloc(isl_ctx *ctx,
+ enum isl_ast_node_type type)
+{
+ isl_ast_node *node;
+
+ node = isl_calloc_type(ctx, isl_ast_node);
+ if (!node)
+ return NULL;
+
+ node->ctx = ctx;
+ isl_ctx_ref(ctx);
+ node->ref = 1;
+ node->type = type;
+
+ return node;
+}
+
+/* Create an if node with the given guard.
+ *
+ * The then body needs to be filled in later.
+ */
+__isl_give isl_ast_node *isl_ast_node_alloc_if(__isl_take isl_ast_expr *guard)
+{
+ isl_ast_node *node;
+
+ if (!guard)
+ return NULL;
+
+ node = isl_ast_node_alloc(isl_ast_expr_get_ctx(guard), isl_ast_node_if);
+ if (!node)
+ goto error;
+ node->u.i.guard = guard;
+
+ return node;
+error:
+ isl_ast_expr_free(guard);
+ return NULL;
+}
+
+/* Create a for node with the given iterator.
+ *
+ * The remaining fields need to be filled in later.
+ */
+__isl_give isl_ast_node *isl_ast_node_alloc_for(__isl_take isl_id *id)
+{
+ isl_ast_node *node;
+ isl_ctx *ctx;
+
+ if (!id)
+ return NULL;
+
+ ctx = isl_id_get_ctx(id);
+ node = isl_ast_node_alloc(ctx, isl_ast_node_for);
+ if (!node)
+ goto error;
+
+ node->u.f.iterator = isl_ast_expr_from_id(id);
+ if (!node->u.f.iterator)
+ return isl_ast_node_free(node);
+
+ return node;
+error:
+ isl_id_free(id);
+ return NULL;
+}
+
+/* Create a mark node, marking "node" with "id".
+ */
+__isl_give isl_ast_node *isl_ast_node_alloc_mark(__isl_take isl_id *id,
+ __isl_take isl_ast_node *node)
+{
+ isl_ctx *ctx;
+ isl_ast_node *mark;
+
+ if (!id || !node)
+ goto error;
+
+ ctx = isl_id_get_ctx(id);
+ mark = isl_ast_node_alloc(ctx, isl_ast_node_mark);
+ if (!mark)
+ goto error;
+
+ mark->u.m.mark = id;
+ mark->u.m.node = node;
+
+ return mark;
+error:
+ isl_id_free(id);
+ isl_ast_node_free(node);
+ return NULL;
+}
+
+/* Create a user node evaluating "expr".
+ */
+__isl_give isl_ast_node *isl_ast_node_alloc_user(__isl_take isl_ast_expr *expr)
+{
+ isl_ctx *ctx;
+ isl_ast_node *node;
+
+ if (!expr)
+ return NULL;
+
+ ctx = isl_ast_expr_get_ctx(expr);
+ node = isl_ast_node_alloc(ctx, isl_ast_node_user);
+ if (!node)
+ goto error;
+
+ node->u.e.expr = expr;
+
+ return node;
+error:
+ isl_ast_expr_free(expr);
+ return NULL;
+}
+
+/* Create a block node with the given children.
+ */
+__isl_give isl_ast_node *isl_ast_node_alloc_block(
+ __isl_take isl_ast_node_list *list)
+{
+ isl_ast_node *node;
+ isl_ctx *ctx;
+
+ if (!list)
+ return NULL;
+
+ ctx = isl_ast_node_list_get_ctx(list);
+ node = isl_ast_node_alloc(ctx, isl_ast_node_block);
+ if (!node)
+ goto error;
+
+ node->u.b.children = list;
+
+ return node;
+error:
+ isl_ast_node_list_free(list);
+ return NULL;
+}
+
+/* Represent the given list of nodes as a single node, either by
+ * extract the node from a single element list or by creating
+ * a block node with the list of nodes as children.
+ */
+__isl_give isl_ast_node *isl_ast_node_from_ast_node_list(
+ __isl_take isl_ast_node_list *list)
+{
+ isl_size n;
+ isl_ast_node *node;
+
+ n = isl_ast_node_list_n_ast_node(list);
+ if (n < 0)
+ goto error;
+ if (n != 1)
+ return isl_ast_node_alloc_block(list);
+
+ node = isl_ast_node_list_get_ast_node(list, 0);
+ isl_ast_node_list_free(list);
+
+ return node;
+error:
+ isl_ast_node_list_free(list);
+ return NULL;
+}
+
+__isl_give isl_ast_node *isl_ast_node_copy(__isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+
+ node->ref++;
+ return node;
+}
+
+__isl_give isl_ast_node *isl_ast_node_dup(__isl_keep isl_ast_node *node)
+{
+ isl_ast_node *dup;
+
+ if (!node)
+ return NULL;
+
+ dup = isl_ast_node_alloc(isl_ast_node_get_ctx(node), node->type);
+ if (!dup)
+ return NULL;
+
+ switch (node->type) {
+ case isl_ast_node_if:
+ dup->u.i.guard = isl_ast_expr_copy(node->u.i.guard);
+ dup->u.i.then = isl_ast_node_copy(node->u.i.then);
+ dup->u.i.else_node = isl_ast_node_copy(node->u.i.else_node);
+ if (!dup->u.i.guard || !dup->u.i.then ||
+ (node->u.i.else_node && !dup->u.i.else_node))
+ return isl_ast_node_free(dup);
+ break;
+ case isl_ast_node_for:
+ dup->u.f.iterator = isl_ast_expr_copy(node->u.f.iterator);
+ dup->u.f.init = isl_ast_expr_copy(node->u.f.init);
+ dup->u.f.cond = isl_ast_expr_copy(node->u.f.cond);
+ dup->u.f.inc = isl_ast_expr_copy(node->u.f.inc);
+ dup->u.f.body = isl_ast_node_copy(node->u.f.body);
+ if (!dup->u.f.iterator || !dup->u.f.init || !dup->u.f.cond ||
+ !dup->u.f.inc || !dup->u.f.body)
+ return isl_ast_node_free(dup);
+ break;
+ case isl_ast_node_block:
+ dup->u.b.children = isl_ast_node_list_copy(node->u.b.children);
+ if (!dup->u.b.children)
+ return isl_ast_node_free(dup);
+ break;
+ case isl_ast_node_mark:
+ dup->u.m.mark = isl_id_copy(node->u.m.mark);
+ dup->u.m.node = isl_ast_node_copy(node->u.m.node);
+ if (!dup->u.m.mark || !dup->u.m.node)
+ return isl_ast_node_free(dup);
+ break;
+ case isl_ast_node_user:
+ dup->u.e.expr = isl_ast_expr_copy(node->u.e.expr);
+ if (!dup->u.e.expr)
+ return isl_ast_node_free(dup);
+ break;
+ case isl_ast_node_error:
+ break;
+ }
+
+ return dup;
+}
+
+__isl_give isl_ast_node *isl_ast_node_cow(__isl_take isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+
+ if (node->ref == 1)
+ return node;
+ node->ref--;
+ return isl_ast_node_dup(node);
+}
+
+__isl_null isl_ast_node *isl_ast_node_free(__isl_take isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+
+ if (--node->ref > 0)
+ return NULL;
+
+ switch (node->type) {
+ case isl_ast_node_if:
+ isl_ast_expr_free(node->u.i.guard);
+ isl_ast_node_free(node->u.i.then);
+ isl_ast_node_free(node->u.i.else_node);
+ break;
+ case isl_ast_node_for:
+ isl_ast_expr_free(node->u.f.iterator);
+ isl_ast_expr_free(node->u.f.init);
+ isl_ast_expr_free(node->u.f.cond);
+ isl_ast_expr_free(node->u.f.inc);
+ isl_ast_node_free(node->u.f.body);
+ break;
+ case isl_ast_node_block:
+ isl_ast_node_list_free(node->u.b.children);
+ break;
+ case isl_ast_node_mark:
+ isl_id_free(node->u.m.mark);
+ isl_ast_node_free(node->u.m.node);
+ break;
+ case isl_ast_node_user:
+ isl_ast_expr_free(node->u.e.expr);
+ break;
+ case isl_ast_node_error:
+ break;
+ }
+
+ isl_id_free(node->annotation);
+ isl_ctx_deref(node->ctx);
+ free(node);
+
+ return NULL;
+}
+
+/* Replace the body of the for node "node" by "body".
+ */
+__isl_give isl_ast_node *isl_ast_node_for_set_body(
+ __isl_take isl_ast_node *node, __isl_take isl_ast_node *body)
+{
+ node = isl_ast_node_cow(node);
+ if (!node || !body)
+ goto error;
+ if (node->type != isl_ast_node_for)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a for node", goto error);
+
+ isl_ast_node_free(node->u.f.body);
+ node->u.f.body = body;
+
+ return node;
+error:
+ isl_ast_node_free(node);
+ isl_ast_node_free(body);
+ return NULL;
+}
+
+__isl_give isl_ast_node *isl_ast_node_for_get_body(
+ __isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_for)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a for node", return NULL);
+ return isl_ast_node_copy(node->u.f.body);
+}
+
+/* Mark the given for node as being degenerate.
+ */
+__isl_give isl_ast_node *isl_ast_node_for_mark_degenerate(
+ __isl_take isl_ast_node *node)
+{
+ node = isl_ast_node_cow(node);
+ if (!node)
+ return NULL;
+ node->u.f.degenerate = 1;
+ return node;
+}
+
+isl_bool isl_ast_node_for_is_degenerate(__isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return isl_bool_error;
+ if (node->type != isl_ast_node_for)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a for node", return isl_bool_error);
+ return isl_bool_ok(node->u.f.degenerate);
+}
+
+__isl_give isl_ast_expr *isl_ast_node_for_get_iterator(
+ __isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_for)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a for node", return NULL);
+ return isl_ast_expr_copy(node->u.f.iterator);
+}
+
+__isl_give isl_ast_expr *isl_ast_node_for_get_init(
+ __isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_for)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a for node", return NULL);
+ return isl_ast_expr_copy(node->u.f.init);
+}
+
+/* Return the condition expression of the given for node.
+ *
+ * If the for node is degenerate, then the condition is not explicitly
+ * stored in the node. Instead, it is constructed as
+ *
+ * iterator <= init
+ */
+__isl_give isl_ast_expr *isl_ast_node_for_get_cond(
+ __isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_for)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a for node", return NULL);
+ if (!node->u.f.degenerate)
+ return isl_ast_expr_copy(node->u.f.cond);
+
+ return isl_ast_expr_alloc_binary(isl_ast_expr_op_le,
+ isl_ast_expr_copy(node->u.f.iterator),
+ isl_ast_expr_copy(node->u.f.init));
+}
+
+/* Return the increment of the given for node.
+ *
+ * If the for node is degenerate, then the increment is not explicitly
+ * stored in the node. We simply return "1".
+ */
+__isl_give isl_ast_expr *isl_ast_node_for_get_inc(
+ __isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_for)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a for node", return NULL);
+ if (!node->u.f.degenerate)
+ return isl_ast_expr_copy(node->u.f.inc);
+ return isl_ast_expr_alloc_int_si(isl_ast_node_get_ctx(node), 1);
+}
+
+/* Replace the then branch of the if node "node" by "child".
+ */
+__isl_give isl_ast_node *isl_ast_node_if_set_then(
+ __isl_take isl_ast_node *node, __isl_take isl_ast_node *child)
+{
+ node = isl_ast_node_cow(node);
+ if (!node || !child)
+ goto error;
+ if (node->type != isl_ast_node_if)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not an if node", goto error);
+
+ isl_ast_node_free(node->u.i.then);
+ node->u.i.then = child;
+
+ return node;
+error:
+ isl_ast_node_free(node);
+ isl_ast_node_free(child);
+ return NULL;
+}
+
+/* Return the then-node of the given if-node.
+ */
+__isl_give isl_ast_node *isl_ast_node_if_get_then_node(
+ __isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_if)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not an if node", return NULL);
+ return isl_ast_node_copy(node->u.i.then);
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_ast_node *isl_ast_node_if_get_then(
+ __isl_keep isl_ast_node *node)
+{
+ return isl_ast_node_if_get_then_node(node);
+}
+
+/* Does the given if-node have an else-node?
+ */
+isl_bool isl_ast_node_if_has_else_node(__isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return isl_bool_error;
+ if (node->type != isl_ast_node_if)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not an if node", return isl_bool_error);
+ return isl_bool_ok(node->u.i.else_node != NULL);
+}
+
+/* This is an alternative name for the function above.
+ */
+isl_bool isl_ast_node_if_has_else(__isl_keep isl_ast_node *node)
+{
+ return isl_ast_node_if_has_else_node(node);
+}
+
+/* Return the else-node of the given if-node,
+ * assuming there is one.
+ */
+__isl_give isl_ast_node *isl_ast_node_if_get_else_node(
+ __isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_if)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not an if node", return NULL);
+ return isl_ast_node_copy(node->u.i.else_node);
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_ast_node *isl_ast_node_if_get_else(
+ __isl_keep isl_ast_node *node)
+{
+ return isl_ast_node_if_get_else_node(node);
+}
+
+__isl_give isl_ast_expr *isl_ast_node_if_get_cond(
+ __isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_if)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a guard node", return NULL);
+ return isl_ast_expr_copy(node->u.i.guard);
+}
+
+__isl_give isl_ast_node_list *isl_ast_node_block_get_children(
+ __isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_block)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a block node", return NULL);
+ return isl_ast_node_list_copy(node->u.b.children);
+}
+
+__isl_give isl_ast_expr *isl_ast_node_user_get_expr(
+ __isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_user)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a user node", return NULL);
+
+ return isl_ast_expr_copy(node->u.e.expr);
+}
+
+/* Return the mark identifier of the mark node "node".
+ */
+__isl_give isl_id *isl_ast_node_mark_get_id(__isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_mark)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a mark node", return NULL);
+
+ return isl_id_copy(node->u.m.mark);
+}
+
+/* Return the node marked by mark node "node".
+ */
+__isl_give isl_ast_node *isl_ast_node_mark_get_node(
+ __isl_keep isl_ast_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->type != isl_ast_node_mark)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a mark node", return NULL);
+
+ return isl_ast_node_copy(node->u.m.node);
+}
+
+__isl_give isl_id *isl_ast_node_get_annotation(__isl_keep isl_ast_node *node)
+{
+ return node ? isl_id_copy(node->annotation) : NULL;
+}
+
+/* Replace node->annotation by "annotation".
+ */
+__isl_give isl_ast_node *isl_ast_node_set_annotation(
+ __isl_take isl_ast_node *node, __isl_take isl_id *annotation)
+{
+ node = isl_ast_node_cow(node);
+ if (!node || !annotation)
+ goto error;
+
+ isl_id_free(node->annotation);
+ node->annotation = annotation;
+
+ return node;
+error:
+ isl_id_free(annotation);
+ return isl_ast_node_free(node);
+}
+
+/* Traverse the elements of "list" and all their descendants
+ * in depth first preorder.
+ *
+ * Return isl_stat_ok on success and isl_stat_error on failure.
+ */
+static isl_stat nodelist_foreach(__isl_keep isl_ast_node_list *list,
+ isl_bool (*fn)(__isl_keep isl_ast_node *node, void *user), void *user)
+{
+ int i;
+
+ if (!list)
+ return isl_stat_error;
+
+ for (i = 0; i < list->n; ++i) {
+ isl_stat ok;
+ isl_ast_node *node = list->p[i];
+
+ ok = isl_ast_node_foreach_descendant_top_down(node, fn, user);
+ if (ok < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Traverse the descendants of "node" (including the node itself)
+ * in depth first preorder.
+ *
+ * If "fn" returns isl_bool_error on any of the nodes, then the traversal
+ * is aborted.
+ * If "fn" returns isl_bool_false on any of the nodes, then the subtree rooted
+ * at that node is skipped.
+ *
+ * Return isl_stat_ok on success and isl_stat_error on failure.
+ */
+isl_stat isl_ast_node_foreach_descendant_top_down(
+ __isl_keep isl_ast_node *node,
+ isl_bool (*fn)(__isl_keep isl_ast_node *node, void *user), void *user)
+{
+ isl_bool more;
+ isl_stat ok;
+
+ if (!node)
+ return isl_stat_error;
+
+ more = fn(node, user);
+ if (more < 0)
+ return isl_stat_error;
+ if (!more)
+ return isl_stat_ok;
+
+ switch (node->type) {
+ case isl_ast_node_for:
+ node = node->u.f.body;
+ return isl_ast_node_foreach_descendant_top_down(node, fn, user);
+ case isl_ast_node_if:
+ ok = isl_ast_node_foreach_descendant_top_down(node->u.i.then,
+ fn, user);
+ if (ok < 0)
+ return isl_stat_error;
+ if (!node->u.i.else_node)
+ return isl_stat_ok;
+ node = node->u.i.else_node;
+ return isl_ast_node_foreach_descendant_top_down(node, fn, user);
+ case isl_ast_node_block:
+ return nodelist_foreach(node->u.b.children, fn, user);
+ case isl_ast_node_mark:
+ node = node->u.m.node;
+ return isl_ast_node_foreach_descendant_top_down(node, fn, user);
+ case isl_ast_node_user:
+ break;
+ case isl_ast_node_error:
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Textual C representation of the various operators.
+ */
+static char *op_str_c[] = {
+ [isl_ast_expr_op_and] = "&&",
+ [isl_ast_expr_op_and_then] = "&&",
+ [isl_ast_expr_op_or] = "||",
+ [isl_ast_expr_op_or_else] = "||",
+ [isl_ast_expr_op_max] = "max",
+ [isl_ast_expr_op_min] = "min",
+ [isl_ast_expr_op_minus] = "-",
+ [isl_ast_expr_op_add] = "+",
+ [isl_ast_expr_op_sub] = "-",
+ [isl_ast_expr_op_mul] = "*",
+ [isl_ast_expr_op_fdiv_q] = "floord",
+ [isl_ast_expr_op_pdiv_q] = "/",
+ [isl_ast_expr_op_pdiv_r] = "%",
+ [isl_ast_expr_op_zdiv_r] = "%",
+ [isl_ast_expr_op_div] = "/",
+ [isl_ast_expr_op_eq] = "==",
+ [isl_ast_expr_op_le] = "<=",
+ [isl_ast_expr_op_ge] = ">=",
+ [isl_ast_expr_op_lt] = "<",
+ [isl_ast_expr_op_gt] = ">",
+ [isl_ast_expr_op_member] = ".",
+ [isl_ast_expr_op_address_of] = "&"
+};
+
+/* Precedence in C of the various operators.
+ * Based on http://en.wikipedia.org/wiki/Operators_in_C_and_C++
+ * Lowest value means highest precedence.
+ */
+static int op_prec[] = {
+ [isl_ast_expr_op_and] = 13,
+ [isl_ast_expr_op_and_then] = 13,
+ [isl_ast_expr_op_or] = 14,
+ [isl_ast_expr_op_or_else] = 14,
+ [isl_ast_expr_op_max] = 2,
+ [isl_ast_expr_op_min] = 2,
+ [isl_ast_expr_op_minus] = 3,
+ [isl_ast_expr_op_add] = 6,
+ [isl_ast_expr_op_sub] = 6,
+ [isl_ast_expr_op_mul] = 5,
+ [isl_ast_expr_op_div] = 5,
+ [isl_ast_expr_op_fdiv_q] = 2,
+ [isl_ast_expr_op_pdiv_q] = 5,
+ [isl_ast_expr_op_pdiv_r] = 5,
+ [isl_ast_expr_op_zdiv_r] = 5,
+ [isl_ast_expr_op_cond] = 15,
+ [isl_ast_expr_op_select] = 15,
+ [isl_ast_expr_op_eq] = 9,
+ [isl_ast_expr_op_le] = 8,
+ [isl_ast_expr_op_ge] = 8,
+ [isl_ast_expr_op_lt] = 8,
+ [isl_ast_expr_op_gt] = 8,
+ [isl_ast_expr_op_call] = 2,
+ [isl_ast_expr_op_access] = 2,
+ [isl_ast_expr_op_member] = 2,
+ [isl_ast_expr_op_address_of] = 3
+};
+
+/* Is the operator left-to-right associative?
+ */
+static int op_left[] = {
+ [isl_ast_expr_op_and] = 1,
+ [isl_ast_expr_op_and_then] = 1,
+ [isl_ast_expr_op_or] = 1,
+ [isl_ast_expr_op_or_else] = 1,
+ [isl_ast_expr_op_max] = 1,
+ [isl_ast_expr_op_min] = 1,
+ [isl_ast_expr_op_minus] = 0,
+ [isl_ast_expr_op_add] = 1,
+ [isl_ast_expr_op_sub] = 1,
+ [isl_ast_expr_op_mul] = 1,
+ [isl_ast_expr_op_div] = 1,
+ [isl_ast_expr_op_fdiv_q] = 1,
+ [isl_ast_expr_op_pdiv_q] = 1,
+ [isl_ast_expr_op_pdiv_r] = 1,
+ [isl_ast_expr_op_zdiv_r] = 1,
+ [isl_ast_expr_op_cond] = 0,
+ [isl_ast_expr_op_select] = 0,
+ [isl_ast_expr_op_eq] = 1,
+ [isl_ast_expr_op_le] = 1,
+ [isl_ast_expr_op_ge] = 1,
+ [isl_ast_expr_op_lt] = 1,
+ [isl_ast_expr_op_gt] = 1,
+ [isl_ast_expr_op_call] = 1,
+ [isl_ast_expr_op_access] = 1,
+ [isl_ast_expr_op_member] = 1,
+ [isl_ast_expr_op_address_of] = 0
+};
+
+static int is_and(enum isl_ast_expr_op_type op)
+{
+ return op == isl_ast_expr_op_and || op == isl_ast_expr_op_and_then;
+}
+
+static int is_or(enum isl_ast_expr_op_type op)
+{
+ return op == isl_ast_expr_op_or || op == isl_ast_expr_op_or_else;
+}
+
+static int is_add_sub(enum isl_ast_expr_op_type op)
+{
+ return op == isl_ast_expr_op_add || op == isl_ast_expr_op_sub;
+}
+
+static int is_div_mod(enum isl_ast_expr_op_type op)
+{
+ return op == isl_ast_expr_op_div ||
+ op == isl_ast_expr_op_pdiv_r ||
+ op == isl_ast_expr_op_zdiv_r;
+}
+
+static __isl_give isl_printer *print_ast_expr_c(__isl_take isl_printer *p,
+ __isl_keep isl_ast_expr *expr);
+
+/* Do we need/want parentheses around "expr" as a subexpression of
+ * an "op" operation? If "left" is set, then "expr" is the left-most
+ * operand.
+ *
+ * We only need parentheses if "expr" represents an operation.
+ *
+ * If op has a higher precedence than expr->u.op.op, then we need
+ * parentheses.
+ * If op and expr->u.op.op have the same precedence, but the operations
+ * are performed in an order that is different from the associativity,
+ * then we need parentheses.
+ *
+ * An and inside an or technically does not require parentheses,
+ * but some compilers complain about that, so we add them anyway.
+ *
+ * Computations such as "a / b * c" and "a % b + c" can be somewhat
+ * difficult to read, so we add parentheses for those as well.
+ */
+static int sub_expr_need_parens(enum isl_ast_expr_op_type op,
+ __isl_keep isl_ast_expr *expr, int left)
+{
+ if (expr->type != isl_ast_expr_op)
+ return 0;
+
+ if (op_prec[expr->u.op.op] > op_prec[op])
+ return 1;
+ if (op_prec[expr->u.op.op] == op_prec[op] && left != op_left[op])
+ return 1;
+
+ if (is_or(op) && is_and(expr->u.op.op))
+ return 1;
+ if (op == isl_ast_expr_op_mul && expr->u.op.op != isl_ast_expr_op_mul &&
+ op_prec[expr->u.op.op] == op_prec[op])
+ return 1;
+ if (is_add_sub(op) && is_div_mod(expr->u.op.op))
+ return 1;
+
+ return 0;
+}
+
+/* Print "expr" as a subexpression of an "op" operation in C format.
+ * If "left" is set, then "expr" is the left-most operand.
+ */
+static __isl_give isl_printer *print_sub_expr_c(__isl_take isl_printer *p,
+ enum isl_ast_expr_op_type op, __isl_keep isl_ast_expr *expr, int left)
+{
+ int need_parens;
+
+ need_parens = sub_expr_need_parens(op, expr, left);
+
+ if (need_parens)
+ p = isl_printer_print_str(p, "(");
+ p = print_ast_expr_c(p, expr);
+ if (need_parens)
+ p = isl_printer_print_str(p, ")");
+ return p;
+}
+
+#define isl_ast_expr_op_last isl_ast_expr_op_address_of
+
+/* Data structure that holds the user-specified textual
+ * representations for the operators in C format.
+ * The entries are either NULL or copies of strings.
+ * A NULL entry means that the default name should be used.
+ */
+struct isl_ast_expr_op_names {
+ char *op_str[isl_ast_expr_op_last + 1];
+};
+
+/* Create an empty struct isl_ast_expr_op_names.
+ */
+static void *create_names(isl_ctx *ctx)
+{
+ return isl_calloc_type(ctx, struct isl_ast_expr_op_names);
+}
+
+/* Free a struct isl_ast_expr_op_names along with all memory
+ * owned by the struct.
+ */
+static void free_names(void *user)
+{
+ int i;
+ struct isl_ast_expr_op_names *names = user;
+
+ if (!user)
+ return;
+
+ for (i = 0; i <= isl_ast_expr_op_last; ++i)
+ free(names->op_str[i]);
+ free(user);
+}
+
+/* Create an identifier that is used to store
+ * an isl_ast_expr_op_names note.
+ */
+static __isl_give isl_id *names_id(isl_ctx *ctx)
+{
+ return isl_id_alloc(ctx, "isl_ast_expr_op_type_names", NULL);
+}
+
+/* Ensure that "p" has a note identified by "id".
+ * If there is no such note yet, then it is created by "note_create" and
+ * scheduled do be freed by "note_free".
+ */
+static __isl_give isl_printer *alloc_note(__isl_take isl_printer *p,
+ __isl_keep isl_id *id, void *(*note_create)(isl_ctx *),
+ void (*note_free)(void *))
+{
+ isl_ctx *ctx;
+ isl_id *note_id;
+ isl_bool has_note;
+ void *note;
+
+ has_note = isl_printer_has_note(p, id);
+ if (has_note < 0)
+ return isl_printer_free(p);
+ if (has_note)
+ return p;
+
+ ctx = isl_printer_get_ctx(p);
+ note = note_create(ctx);
+ if (!note)
+ return isl_printer_free(p);
+ note_id = isl_id_alloc(ctx, NULL, note);
+ if (!note_id)
+ note_free(note);
+ else
+ note_id = isl_id_set_free_user(note_id, note_free);
+
+ p = isl_printer_set_note(p, isl_id_copy(id), note_id);
+
+ return p;
+}
+
+/* Ensure that "p" has an isl_ast_expr_op_names note identified by "id".
+ */
+static __isl_give isl_printer *alloc_names(__isl_take isl_printer *p,
+ __isl_keep isl_id *id)
+{
+ return alloc_note(p, id, &create_names, &free_names);
+}
+
+/* Retrieve the note identified by "id" from "p".
+ * The note is assumed to exist.
+ */
+static void *get_note(__isl_keep isl_printer *p, __isl_keep isl_id *id)
+{
+ void *note;
+
+ id = isl_printer_get_note(p, isl_id_copy(id));
+ note = isl_id_get_user(id);
+ isl_id_free(id);
+
+ return note;
+}
+
+/* Use "name" to print operations of type "type" to "p".
+ *
+ * Store the name in an isl_ast_expr_op_names note attached to "p", such that
+ * it can be retrieved by get_op_str.
+ */
+__isl_give isl_printer *isl_ast_expr_op_type_set_print_name(
+ __isl_take isl_printer *p, enum isl_ast_expr_op_type type,
+ __isl_keep const char *name)
+{
+ isl_id *id;
+ struct isl_ast_expr_op_names *names;
+
+ if (!p)
+ return NULL;
+ if (type > isl_ast_expr_op_last)
+ isl_die(isl_printer_get_ctx(p), isl_error_invalid,
+ "invalid type", return isl_printer_free(p));
+
+ id = names_id(isl_printer_get_ctx(p));
+ p = alloc_names(p, id);
+ names = get_note(p, id);
+ isl_id_free(id);
+ if (!names)
+ return isl_printer_free(p);
+ free(names->op_str[type]);
+ names->op_str[type] = strdup(name);
+
+ return p;
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_printer *isl_ast_op_type_set_print_name(
+ __isl_take isl_printer *p, enum isl_ast_expr_op_type type,
+ __isl_keep const char *name)
+{
+ return isl_ast_expr_op_type_set_print_name(p, type, name);
+}
+
+/* Return the textual representation of "type" in C format.
+ *
+ * If there is a user-specified name in an isl_ast_expr_op_names note
+ * associated to "p", then return that.
+ * Otherwise, return the default name in op_str_c.
+ */
+static const char *get_op_str_c(__isl_keep isl_printer *p,
+ enum isl_ast_expr_op_type type)
+{
+ isl_id *id;
+ isl_bool has_names;
+ struct isl_ast_expr_op_names *names = NULL;
+
+ id = names_id(isl_printer_get_ctx(p));
+ has_names = isl_printer_has_note(p, id);
+ if (has_names >= 0 && has_names)
+ names = get_note(p, id);
+ isl_id_free(id);
+ if (names && names->op_str[type])
+ return names->op_str[type];
+ return op_str_c[type];
+}
+
+/* Print a min or max reduction "expr" in C format.
+ */
+static __isl_give isl_printer *print_min_max_c(__isl_take isl_printer *p,
+ __isl_keep isl_ast_expr *expr)
+{
+ int i = 0;
+
+ for (i = 1; i < expr->u.op.n_arg; ++i) {
+ p = isl_printer_print_str(p, get_op_str_c(p, expr->u.op.op));
+ p = isl_printer_print_str(p, "(");
+ }
+ p = isl_printer_print_ast_expr(p, expr->u.op.args[0]);
+ for (i = 1; i < expr->u.op.n_arg; ++i) {
+ p = isl_printer_print_str(p, ", ");
+ p = print_ast_expr_c(p, expr->u.op.args[i]);
+ p = isl_printer_print_str(p, ")");
+ }
+
+ return p;
+}
+
+/* Print a function call "expr" in C format.
+ *
+ * The first argument represents the function to be called.
+ */
+static __isl_give isl_printer *print_call_c(__isl_take isl_printer *p,
+ __isl_keep isl_ast_expr *expr)
+{
+ int i = 0;
+
+ p = print_ast_expr_c(p, expr->u.op.args[0]);
+ p = isl_printer_print_str(p, "(");
+ for (i = 1; i < expr->u.op.n_arg; ++i) {
+ if (i != 1)
+ p = isl_printer_print_str(p, ", ");
+ p = print_ast_expr_c(p, expr->u.op.args[i]);
+ }
+ p = isl_printer_print_str(p, ")");
+
+ return p;
+}
+
+/* Print an array access "expr" in C format.
+ *
+ * The first argument represents the array being accessed.
+ */
+static __isl_give isl_printer *print_access_c(__isl_take isl_printer *p,
+ __isl_keep isl_ast_expr *expr)
+{
+ int i = 0;
+
+ p = print_ast_expr_c(p, expr->u.op.args[0]);
+ for (i = 1; i < expr->u.op.n_arg; ++i) {
+ p = isl_printer_print_str(p, "[");
+ p = print_ast_expr_c(p, expr->u.op.args[i]);
+ p = isl_printer_print_str(p, "]");
+ }
+
+ return p;
+}
+
+/* Print "expr" to "p" in C format.
+ */
+static __isl_give isl_printer *print_ast_expr_c(__isl_take isl_printer *p,
+ __isl_keep isl_ast_expr *expr)
+{
+ if (!p)
+ return NULL;
+ if (!expr)
+ return isl_printer_free(p);
+
+ switch (expr->type) {
+ case isl_ast_expr_op:
+ if (expr->u.op.op == isl_ast_expr_op_call) {
+ p = print_call_c(p, expr);
+ break;
+ }
+ if (expr->u.op.op == isl_ast_expr_op_access) {
+ p = print_access_c(p, expr);
+ break;
+ }
+ if (expr->u.op.n_arg == 1) {
+ p = isl_printer_print_str(p,
+ get_op_str_c(p, expr->u.op.op));
+ p = print_sub_expr_c(p, expr->u.op.op,
+ expr->u.op.args[0], 0);
+ break;
+ }
+ if (expr->u.op.op == isl_ast_expr_op_fdiv_q) {
+ const char *name;
+
+ name = get_op_str_c(p, isl_ast_expr_op_fdiv_q);
+ p = isl_printer_print_str(p, name);
+ p = isl_printer_print_str(p, "(");
+ p = print_ast_expr_c(p, expr->u.op.args[0]);
+ p = isl_printer_print_str(p, ", ");
+ p = print_ast_expr_c(p, expr->u.op.args[1]);
+ p = isl_printer_print_str(p, ")");
+ break;
+ }
+ if (expr->u.op.op == isl_ast_expr_op_max ||
+ expr->u.op.op == isl_ast_expr_op_min) {
+ p = print_min_max_c(p, expr);
+ break;
+ }
+ if (expr->u.op.op == isl_ast_expr_op_cond ||
+ expr->u.op.op == isl_ast_expr_op_select) {
+ p = print_ast_expr_c(p, expr->u.op.args[0]);
+ p = isl_printer_print_str(p, " ? ");
+ p = print_ast_expr_c(p, expr->u.op.args[1]);
+ p = isl_printer_print_str(p, " : ");
+ p = print_ast_expr_c(p, expr->u.op.args[2]);
+ break;
+ }
+ if (expr->u.op.n_arg != 2)
+ isl_die(isl_printer_get_ctx(p), isl_error_internal,
+ "operation should have two arguments",
+ return isl_printer_free(p));
+ p = print_sub_expr_c(p, expr->u.op.op, expr->u.op.args[0], 1);
+ if (expr->u.op.op != isl_ast_expr_op_member)
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_str(p, get_op_str_c(p, expr->u.op.op));
+ if (expr->u.op.op != isl_ast_expr_op_member)
+ p = isl_printer_print_str(p, " ");
+ p = print_sub_expr_c(p, expr->u.op.op, expr->u.op.args[1], 0);
+ break;
+ case isl_ast_expr_id:
+ p = isl_printer_print_str(p, isl_id_get_name(expr->u.id));
+ break;
+ case isl_ast_expr_int:
+ p = isl_printer_print_val(p, expr->u.v);
+ break;
+ case isl_ast_expr_error:
+ break;
+ }
+
+ return p;
+}
+
+/* Textual representation of the isl_ast_expr_op_type elements
+ * for use in a YAML representation of an isl_ast_expr.
+ */
+static char *op_str[] = {
+ [isl_ast_expr_op_and] = "and",
+ [isl_ast_expr_op_and_then] = "and_then",
+ [isl_ast_expr_op_or] = "or",
+ [isl_ast_expr_op_or_else] = "or_else",
+ [isl_ast_expr_op_max] = "max",
+ [isl_ast_expr_op_min] = "min",
+ [isl_ast_expr_op_minus] = "minus",
+ [isl_ast_expr_op_add] = "add",
+ [isl_ast_expr_op_sub] = "sub",
+ [isl_ast_expr_op_mul] = "mul",
+ [isl_ast_expr_op_div] = "div",
+ [isl_ast_expr_op_fdiv_q] = "fdiv_q",
+ [isl_ast_expr_op_pdiv_q] = "pdiv_q",
+ [isl_ast_expr_op_pdiv_r] = "pdiv_r",
+ [isl_ast_expr_op_zdiv_r] = "zdiv_r",
+ [isl_ast_expr_op_cond] = "cond",
+ [isl_ast_expr_op_select] = "select",
+ [isl_ast_expr_op_eq] = "eq",
+ [isl_ast_expr_op_le] = "le",
+ [isl_ast_expr_op_lt] = "lt",
+ [isl_ast_expr_op_ge] = "ge",
+ [isl_ast_expr_op_gt] = "gt",
+ [isl_ast_expr_op_call] = "call",
+ [isl_ast_expr_op_access] = "access",
+ [isl_ast_expr_op_member] = "member",
+ [isl_ast_expr_op_address_of] = "address_of"
+};
+
+static __isl_give isl_printer *print_ast_expr_isl(__isl_take isl_printer *p,
+ __isl_keep isl_ast_expr *expr);
+
+/* Print the arguments of "expr" to "p" in isl format.
+ *
+ * If there are no arguments, then nothing needs to be printed.
+ * Otherwise add an "args" key to the current mapping with as value
+ * the list of arguments of "expr".
+ */
+static __isl_give isl_printer *print_arguments(__isl_take isl_printer *p,
+ __isl_keep isl_ast_expr *expr)
+{
+ int i;
+ isl_size n;
+
+ n = isl_ast_expr_get_op_n_arg(expr);
+ if (n < 0)
+ return isl_printer_free(p);
+ if (n == 0)
+ return p;
+
+ p = isl_printer_print_str(p, "args");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_yaml_start_sequence(p);
+ for (i = 0; i < n; ++i) {
+ isl_ast_expr *arg;
+
+ arg = isl_ast_expr_get_op_arg(expr, i);
+ p = print_ast_expr_isl(p, arg);
+ isl_ast_expr_free(arg);
+ p = isl_printer_yaml_next(p);
+ }
+ p = isl_printer_yaml_end_sequence(p);
+
+ return p;
+}
+
+/* Print "expr" to "p" in isl format.
+ *
+ * In particular, print the isl_ast_expr as a YAML document.
+ */
+static __isl_give isl_printer *print_ast_expr_isl(__isl_take isl_printer *p,
+ __isl_keep isl_ast_expr *expr)
+{
+ enum isl_ast_expr_type type;
+ enum isl_ast_expr_op_type op;
+ isl_id *id;
+ isl_val *v;
+
+ if (!expr)
+ return isl_printer_free(p);
+
+ p = isl_printer_yaml_start_mapping(p);
+ type = isl_ast_expr_get_type(expr);
+ switch (type) {
+ case isl_ast_expr_error:
+ return isl_printer_free(p);
+ case isl_ast_expr_op:
+ op = isl_ast_expr_get_op_type(expr);
+ if (op == isl_ast_expr_op_error)
+ return isl_printer_free(p);
+ p = isl_printer_print_str(p, "op");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_str(p, op_str[op]);
+ p = isl_printer_yaml_next(p);
+ p = print_arguments(p, expr);
+ break;
+ case isl_ast_expr_id:
+ p = isl_printer_print_str(p, "id");
+ p = isl_printer_yaml_next(p);
+ id = isl_ast_expr_get_id(expr);
+ p = isl_printer_print_id(p, id);
+ isl_id_free(id);
+ break;
+ case isl_ast_expr_int:
+ p = isl_printer_print_str(p, "val");
+ p = isl_printer_yaml_next(p);
+ v = isl_ast_expr_get_val(expr);
+ p = isl_printer_print_val(p, v);
+ isl_val_free(v);
+ break;
+ }
+ p = isl_printer_yaml_end_mapping(p);
+
+ return p;
+}
+
+/* Print "expr" to "p".
+ *
+ * Only an isl and a C format are supported.
+ */
+__isl_give isl_printer *isl_printer_print_ast_expr(__isl_take isl_printer *p,
+ __isl_keep isl_ast_expr *expr)
+{
+ int format;
+
+ if (!p)
+ return NULL;
+
+ format = isl_printer_get_output_format(p);
+ switch (format) {
+ case ISL_FORMAT_ISL:
+ p = print_ast_expr_isl(p, expr);
+ break;
+ case ISL_FORMAT_C:
+ p = print_ast_expr_c(p, expr);
+ break;
+ default:
+ isl_die(isl_printer_get_ctx(p), isl_error_unsupported,
+ "output format not supported for ast_expr",
+ return isl_printer_free(p));
+ }
+
+ return p;
+}
+
+static __isl_give isl_printer *print_ast_node_isl(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node);
+
+/* Print a YAML sequence containing the entries in "list" to "p".
+ */
+static __isl_give isl_printer *print_ast_node_list(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node_list *list)
+{
+ int i;
+ isl_size n;
+
+ n = isl_ast_node_list_n_ast_node(list);
+ if (n < 0)
+ return isl_printer_free(p);
+
+ p = isl_printer_yaml_start_sequence(p);
+ for (i = 0; i < n; ++i) {
+ isl_ast_node *node;
+
+ node = isl_ast_node_list_get_ast_node(list, i);
+ p = print_ast_node_isl(p, node);
+ isl_ast_node_free(node);
+ p = isl_printer_yaml_next(p);
+ }
+ p = isl_printer_yaml_end_sequence(p);
+
+ return p;
+}
+
+/* Print "node" to "p" in "isl format".
+ *
+ * In particular, print the isl_ast_node as a YAML document.
+ */
+static __isl_give isl_printer *print_ast_node_isl(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node)
+{
+ switch (node->type) {
+ case isl_ast_node_for:
+ p = isl_printer_yaml_start_mapping(p);
+ p = isl_printer_print_str(p, "iterator");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_ast_expr(p, node->u.f.iterator);
+ p = isl_printer_yaml_next(p);
+ if (node->u.f.degenerate) {
+ p = isl_printer_print_str(p, "value");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_ast_expr(p, node->u.f.init);
+ p = isl_printer_yaml_next(p);
+ } else {
+ p = isl_printer_print_str(p, "init");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_ast_expr(p, node->u.f.init);
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_str(p, "cond");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_ast_expr(p, node->u.f.cond);
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_str(p, "inc");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_ast_expr(p, node->u.f.inc);
+ p = isl_printer_yaml_next(p);
+ }
+ if (node->u.f.body) {
+ p = isl_printer_print_str(p, "body");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_ast_node(p, node->u.f.body);
+ p = isl_printer_yaml_next(p);
+ }
+ p = isl_printer_yaml_end_mapping(p);
+ break;
+ case isl_ast_node_mark:
+ p = isl_printer_yaml_start_mapping(p);
+ p = isl_printer_print_str(p, "mark");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_id(p, node->u.m.mark);
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_str(p, "node");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_ast_node(p, node->u.m.node);
+ p = isl_printer_yaml_end_mapping(p);
+ break;
+ case isl_ast_node_user:
+ p = isl_printer_yaml_start_mapping(p);
+ p = isl_printer_print_str(p, "user");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_ast_expr(p, node->u.e.expr);
+ p = isl_printer_yaml_end_mapping(p);
+ break;
+ case isl_ast_node_if:
+ p = isl_printer_yaml_start_mapping(p);
+ p = isl_printer_print_str(p, "guard");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_ast_expr(p, node->u.i.guard);
+ p = isl_printer_yaml_next(p);
+ if (node->u.i.then) {
+ p = isl_printer_print_str(p, "then");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_ast_node(p, node->u.i.then);
+ p = isl_printer_yaml_next(p);
+ }
+ if (node->u.i.else_node) {
+ p = isl_printer_print_str(p, "else");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_ast_node(p, node->u.i.else_node);
+ }
+ p = isl_printer_yaml_end_mapping(p);
+ break;
+ case isl_ast_node_block:
+ p = print_ast_node_list(p, node->u.b.children);
+ break;
+ case isl_ast_node_error:
+ break;
+ }
+ return p;
+}
+
+/* Do we need to print a block around the body "node" of a for or if node?
+ *
+ * If the node is a block, then we need to print a block.
+ * Also if the node is a degenerate for then we will print it as
+ * an assignment followed by the body of the for loop, so we need a block
+ * as well.
+ * If the node is an if node with an else, then we print a block
+ * to avoid spurious dangling else warnings emitted by some compilers.
+ * If the node is a mark, then in principle, we would have to check
+ * the child of the mark node. However, even if the child would not
+ * require us to print a block, for readability it is probably best
+ * to print a block anyway.
+ * If the ast_always_print_block option has been set, then we print a block.
+ */
+static int need_block(__isl_keep isl_ast_node *node)
+{
+ isl_ctx *ctx;
+
+ if (node->type == isl_ast_node_block)
+ return 1;
+ if (node->type == isl_ast_node_for && node->u.f.degenerate)
+ return 1;
+ if (node->type == isl_ast_node_if && node->u.i.else_node)
+ return 1;
+ if (node->type == isl_ast_node_mark)
+ return 1;
+
+ ctx = isl_ast_node_get_ctx(node);
+ return isl_options_get_ast_always_print_block(ctx);
+}
+
+static __isl_give isl_printer *print_ast_node_c(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node,
+ __isl_keep isl_ast_print_options *options, int in_block, int in_list);
+static __isl_give isl_printer *print_if_c(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node,
+ __isl_keep isl_ast_print_options *options, int new_line,
+ int force_block);
+
+/* Print the body "node" of a for or if node.
+ * If "else_node" is set, then it is printed as well.
+ * If "force_block" is set, then print out the body as a block.
+ *
+ * We first check if we need to print out a block.
+ * We always print out a block if there is an else node to make
+ * sure that the else node is matched to the correct if node.
+ * For consistency, the corresponding else node is also printed as a block.
+ *
+ * If the else node is itself an if, then we print it as
+ *
+ * } else if (..) {
+ * }
+ *
+ * Otherwise the else node is printed as
+ *
+ * } else {
+ * node
+ * }
+ */
+static __isl_give isl_printer *print_body_c(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node, __isl_keep isl_ast_node *else_node,
+ __isl_keep isl_ast_print_options *options, int force_block)
+{
+ if (!node)
+ return isl_printer_free(p);
+
+ if (!force_block && !else_node && !need_block(node)) {
+ p = isl_printer_end_line(p);
+ p = isl_printer_indent(p, 2);
+ p = isl_ast_node_print(node, p,
+ isl_ast_print_options_copy(options));
+ p = isl_printer_indent(p, -2);
+ return p;
+ }
+
+ p = isl_printer_print_str(p, " {");
+ p = isl_printer_end_line(p);
+ p = isl_printer_indent(p, 2);
+ p = print_ast_node_c(p, node, options, 1, 0);
+ p = isl_printer_indent(p, -2);
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "}");
+ if (else_node) {
+ if (else_node->type == isl_ast_node_if) {
+ p = isl_printer_print_str(p, " else ");
+ p = print_if_c(p, else_node, options, 0, 1);
+ } else {
+ p = isl_printer_print_str(p, " else");
+ p = print_body_c(p, else_node, NULL, options, 1);
+ }
+ } else
+ p = isl_printer_end_line(p);
+
+ return p;
+}
+
+/* Print the start of a compound statement.
+ */
+static __isl_give isl_printer *start_block(__isl_take isl_printer *p)
+{
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "{");
+ p = isl_printer_end_line(p);
+ p = isl_printer_indent(p, 2);
+
+ return p;
+}
+
+/* Print the end of a compound statement.
+ */
+static __isl_give isl_printer *end_block(__isl_take isl_printer *p)
+{
+ p = isl_printer_indent(p, -2);
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "}");
+ p = isl_printer_end_line(p);
+
+ return p;
+}
+
+/* Print the for node "node".
+ *
+ * If the for node is degenerate, it is printed as
+ *
+ * type iterator = init;
+ * body
+ *
+ * Otherwise, it is printed as
+ *
+ * for (type iterator = init; cond; iterator += inc)
+ * body
+ *
+ * "in_block" is set if we are currently inside a block.
+ * "in_list" is set if the current node is not alone in the block.
+ * If we are not in a block or if the current not is not alone in the block
+ * then we print a block around a degenerate for loop such that the variable
+ * declaration will not conflict with any potential other declaration
+ * of the same variable.
+ */
+static __isl_give isl_printer *print_for_c(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node,
+ __isl_keep isl_ast_print_options *options, int in_block, int in_list)
+{
+ isl_id *id;
+ const char *name;
+ const char *type;
+
+ type = isl_options_get_ast_iterator_type(isl_printer_get_ctx(p));
+ if (!node->u.f.degenerate) {
+ id = isl_ast_expr_get_id(node->u.f.iterator);
+ name = isl_id_get_name(id);
+ isl_id_free(id);
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "for (");
+ p = isl_printer_print_str(p, type);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_str(p, name);
+ p = isl_printer_print_str(p, " = ");
+ p = isl_printer_print_ast_expr(p, node->u.f.init);
+ p = isl_printer_print_str(p, "; ");
+ p = isl_printer_print_ast_expr(p, node->u.f.cond);
+ p = isl_printer_print_str(p, "; ");
+ p = isl_printer_print_str(p, name);
+ p = isl_printer_print_str(p, " += ");
+ p = isl_printer_print_ast_expr(p, node->u.f.inc);
+ p = isl_printer_print_str(p, ")");
+ p = print_body_c(p, node->u.f.body, NULL, options, 0);
+ } else {
+ id = isl_ast_expr_get_id(node->u.f.iterator);
+ name = isl_id_get_name(id);
+ isl_id_free(id);
+ if (!in_block || in_list)
+ p = start_block(p);
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, type);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_str(p, name);
+ p = isl_printer_print_str(p, " = ");
+ p = isl_printer_print_ast_expr(p, node->u.f.init);
+ p = isl_printer_print_str(p, ";");
+ p = isl_printer_end_line(p);
+ p = print_ast_node_c(p, node->u.f.body, options, 1, 0);
+ if (!in_block || in_list)
+ p = end_block(p);
+ }
+
+ return p;
+}
+
+/* Print the if node "node".
+ * If "new_line" is set then the if node should be printed on a new line.
+ * If "force_block" is set, then print out the body as a block.
+ */
+static __isl_give isl_printer *print_if_c(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node,
+ __isl_keep isl_ast_print_options *options, int new_line,
+ int force_block)
+{
+ if (new_line)
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "if (");
+ p = isl_printer_print_ast_expr(p, node->u.i.guard);
+ p = isl_printer_print_str(p, ")");
+ p = print_body_c(p, node->u.i.then, node->u.i.else_node, options,
+ force_block);
+
+ return p;
+}
+
+/* Print the "node" to "p".
+ *
+ * "in_block" is set if we are currently inside a block.
+ * If so, we do not print a block around the children of a block node.
+ * We do this to avoid an extra block around the body of a degenerate
+ * for node.
+ *
+ * "in_list" is set if the current node is not alone in the block.
+ */
+static __isl_give isl_printer *print_ast_node_c(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node,
+ __isl_keep isl_ast_print_options *options, int in_block, int in_list)
+{
+ switch (node->type) {
+ case isl_ast_node_for:
+ if (options->print_for)
+ return options->print_for(p,
+ isl_ast_print_options_copy(options),
+ node, options->print_for_user);
+ p = print_for_c(p, node, options, in_block, in_list);
+ break;
+ case isl_ast_node_if:
+ p = print_if_c(p, node, options, 1, 0);
+ break;
+ case isl_ast_node_block:
+ if (!in_block)
+ p = start_block(p);
+ p = isl_ast_node_list_print(node->u.b.children, p, options);
+ if (!in_block)
+ p = end_block(p);
+ break;
+ case isl_ast_node_mark:
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "// ");
+ p = isl_printer_print_str(p, isl_id_get_name(node->u.m.mark));
+ p = isl_printer_end_line(p);
+ p = print_ast_node_c(p, node->u.m.node, options, 0, in_list);
+ break;
+ case isl_ast_node_user:
+ if (options->print_user)
+ return options->print_user(p,
+ isl_ast_print_options_copy(options),
+ node, options->print_user_user);
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_ast_expr(p, node->u.e.expr);
+ p = isl_printer_print_str(p, ";");
+ p = isl_printer_end_line(p);
+ break;
+ case isl_ast_node_error:
+ break;
+ }
+ return p;
+}
+
+/* Print the for node "node" to "p".
+ */
+__isl_give isl_printer *isl_ast_node_for_print(__isl_keep isl_ast_node *node,
+ __isl_take isl_printer *p, __isl_take isl_ast_print_options *options)
+{
+ if (!node || !options)
+ goto error;
+ if (node->type != isl_ast_node_for)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not a for node", goto error);
+ p = print_for_c(p, node, options, 0, 0);
+ isl_ast_print_options_free(options);
+ return p;
+error:
+ isl_ast_print_options_free(options);
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print the if node "node" to "p".
+ */
+__isl_give isl_printer *isl_ast_node_if_print(__isl_keep isl_ast_node *node,
+ __isl_take isl_printer *p, __isl_take isl_ast_print_options *options)
+{
+ if (!node || !options)
+ goto error;
+ if (node->type != isl_ast_node_if)
+ isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+ "not an if node", goto error);
+ p = print_if_c(p, node, options, 1, 0);
+ isl_ast_print_options_free(options);
+ return p;
+error:
+ isl_ast_print_options_free(options);
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print "node" to "p".
+ *
+ * "node" is assumed to be either the outermost node in an AST or
+ * a node that is known not to be a block.
+ * If "node" is a block (and is therefore outermost) and
+ * if the ast_print_outermost_block options is not set,
+ * then act as if the printing occurs inside a block, such
+ * that no "extra" block will get printed.
+ */
+__isl_give isl_printer *isl_ast_node_print(__isl_keep isl_ast_node *node,
+ __isl_take isl_printer *p, __isl_take isl_ast_print_options *options)
+{
+ int in_block = 0;
+
+ if (!options || !node)
+ goto error;
+ if (node->type == isl_ast_node_block) {
+ isl_ctx *ctx;
+
+ ctx = isl_ast_node_get_ctx(node);
+ in_block = !isl_options_get_ast_print_outermost_block(ctx);
+ }
+ p = print_ast_node_c(p, node, options, in_block, 0);
+ isl_ast_print_options_free(options);
+ return p;
+error:
+ isl_ast_print_options_free(options);
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print "node" to "p".
+ */
+__isl_give isl_printer *isl_printer_print_ast_node(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node)
+{
+ int format;
+ isl_ast_print_options *options;
+
+ if (!p)
+ return NULL;
+
+ format = isl_printer_get_output_format(p);
+ switch (format) {
+ case ISL_FORMAT_ISL:
+ p = print_ast_node_isl(p, node);
+ break;
+ case ISL_FORMAT_C:
+ options = isl_ast_print_options_alloc(isl_printer_get_ctx(p));
+ p = isl_ast_node_print(node, p, options);
+ break;
+ default:
+ isl_die(isl_printer_get_ctx(p), isl_error_unsupported,
+ "output format not supported for ast_node",
+ return isl_printer_free(p));
+ }
+
+ return p;
+}
+
+/* Print the list of nodes "list" to "p".
+ */
+__isl_give isl_printer *isl_ast_node_list_print(
+ __isl_keep isl_ast_node_list *list, __isl_take isl_printer *p,
+ __isl_keep isl_ast_print_options *options)
+{
+ int i;
+
+ if (!p || !list || !options)
+ return isl_printer_free(p);
+
+ for (i = 0; i < list->n; ++i)
+ p = print_ast_node_c(p, list->p[i], options, 1, 1);
+
+ return p;
+}
+
+#define ISL_AST_MACRO_FDIV_Q (1 << 0)
+#define ISL_AST_MACRO_MIN (1 << 1)
+#define ISL_AST_MACRO_MAX (1 << 2)
+#define ISL_AST_MACRO_ALL (ISL_AST_MACRO_FDIV_Q | \
+ ISL_AST_MACRO_MIN | \
+ ISL_AST_MACRO_MAX)
+
+/* If "expr" contains an isl_ast_expr_op_min, isl_ast_expr_op_max or
+ * isl_ast_expr_op_fdiv_q then set the corresponding bit in "macros".
+ */
+static int ast_expr_required_macros(__isl_keep isl_ast_expr *expr, int macros)
+{
+ int i;
+
+ if (macros == ISL_AST_MACRO_ALL)
+ return macros;
+
+ if (expr->type != isl_ast_expr_op)
+ return macros;
+
+ if (expr->u.op.op == isl_ast_expr_op_min)
+ macros |= ISL_AST_MACRO_MIN;
+ if (expr->u.op.op == isl_ast_expr_op_max)
+ macros |= ISL_AST_MACRO_MAX;
+ if (expr->u.op.op == isl_ast_expr_op_fdiv_q)
+ macros |= ISL_AST_MACRO_FDIV_Q;
+
+ for (i = 0; i < expr->u.op.n_arg; ++i)
+ macros = ast_expr_required_macros(expr->u.op.args[i], macros);
+
+ return macros;
+}
+
+static int ast_node_list_required_macros(__isl_keep isl_ast_node_list *list,
+ int macros);
+
+/* If "node" contains an isl_ast_expr_op_min, isl_ast_expr_op_max or
+ * isl_ast_expr_op_fdiv_q then set the corresponding bit in "macros".
+ */
+static int ast_node_required_macros(__isl_keep isl_ast_node *node, int macros)
+{
+ if (macros == ISL_AST_MACRO_ALL)
+ return macros;
+
+ switch (node->type) {
+ case isl_ast_node_for:
+ macros = ast_expr_required_macros(node->u.f.init, macros);
+ if (!node->u.f.degenerate) {
+ macros = ast_expr_required_macros(node->u.f.cond,
+ macros);
+ macros = ast_expr_required_macros(node->u.f.inc,
+ macros);
+ }
+ macros = ast_node_required_macros(node->u.f.body, macros);
+ break;
+ case isl_ast_node_if:
+ macros = ast_expr_required_macros(node->u.i.guard, macros);
+ macros = ast_node_required_macros(node->u.i.then, macros);
+ if (node->u.i.else_node)
+ macros = ast_node_required_macros(node->u.i.else_node,
+ macros);
+ break;
+ case isl_ast_node_block:
+ macros = ast_node_list_required_macros(node->u.b.children,
+ macros);
+ break;
+ case isl_ast_node_mark:
+ macros = ast_node_required_macros(node->u.m.node, macros);
+ break;
+ case isl_ast_node_user:
+ macros = ast_expr_required_macros(node->u.e.expr, macros);
+ break;
+ case isl_ast_node_error:
+ break;
+ }
+
+ return macros;
+}
+
+/* If "list" contains an isl_ast_expr_op_min, isl_ast_expr_op_max or
+ * isl_ast_expr_op_fdiv_q then set the corresponding bit in "macros".
+ */
+static int ast_node_list_required_macros(__isl_keep isl_ast_node_list *list,
+ int macros)
+{
+ int i;
+
+ for (i = 0; i < list->n; ++i)
+ macros = ast_node_required_macros(list->p[i], macros);
+
+ return macros;
+}
+
+/* Data structure for keeping track of whether a macro definition
+ * for a given type has already been printed.
+ * The value is zero if no definition has been printed and non-zero otherwise.
+ */
+struct isl_ast_expr_op_printed {
+ char printed[isl_ast_expr_op_last + 1];
+};
+
+/* Create an empty struct isl_ast_expr_op_printed.
+ */
+static void *create_printed(isl_ctx *ctx)
+{
+ return isl_calloc_type(ctx, struct isl_ast_expr_op_printed);
+}
+
+/* Free a struct isl_ast_expr_op_printed.
+ */
+static void free_printed(void *user)
+{
+ free(user);
+}
+
+/* Ensure that "p" has an isl_ast_expr_op_printed note identified by "id".
+ */
+static __isl_give isl_printer *alloc_printed(__isl_take isl_printer *p,
+ __isl_keep isl_id *id)
+{
+ return alloc_note(p, id, &create_printed, &free_printed);
+}
+
+/* Create an identifier that is used to store
+ * an isl_ast_expr_op_printed note.
+ */
+static __isl_give isl_id *printed_id(isl_ctx *ctx)
+{
+ return isl_id_alloc(ctx, "isl_ast_expr_op_type_printed", NULL);
+}
+
+/* Did the user specify that a macro definition should only be
+ * printed once and has a macro definition for "type" already
+ * been printed to "p"?
+ * If definitions should only be printed once, but a definition
+ * for "p" has not yet been printed, then mark it as having been
+ * printed so that it will not printed again.
+ * The actual printing is taken care of by the caller.
+ */
+static isl_bool already_printed_once(__isl_keep isl_printer *p,
+ enum isl_ast_expr_op_type type)
+{
+ isl_ctx *ctx;
+ isl_id *id;
+ struct isl_ast_expr_op_printed *printed;
+
+ if (!p)
+ return isl_bool_error;
+
+ ctx = isl_printer_get_ctx(p);
+ if (!isl_options_get_ast_print_macro_once(ctx))
+ return isl_bool_false;
+
+ if (type > isl_ast_expr_op_last)
+ isl_die(isl_printer_get_ctx(p), isl_error_invalid,
+ "invalid type", return isl_bool_error);
+
+ id = printed_id(isl_printer_get_ctx(p));
+ p = alloc_printed(p, id);
+ printed = get_note(p, id);
+ isl_id_free(id);
+ if (!printed)
+ return isl_bool_error;
+
+ if (printed->printed[type])
+ return isl_bool_true;
+
+ printed->printed[type] = 1;
+ return isl_bool_false;
+}
+
+/* Print a macro definition for the operator "type".
+ *
+ * If the user has specified that a macro definition should
+ * only be printed once to any given printer and if the macro definition
+ * has already been printed to "p", then do not print the definition.
+ */
+__isl_give isl_printer *isl_ast_expr_op_type_print_macro(
+ enum isl_ast_expr_op_type type, __isl_take isl_printer *p)
+{
+ isl_bool skip;
+
+ skip = already_printed_once(p, type);
+ if (skip < 0)
+ return isl_printer_free(p);
+ if (skip)
+ return p;
+
+ switch (type) {
+ case isl_ast_expr_op_min:
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "#define ");
+ p = isl_printer_print_str(p, get_op_str_c(p, type));
+ p = isl_printer_print_str(p,
+ "(x,y) ((x) < (y) ? (x) : (y))");
+ p = isl_printer_end_line(p);
+ break;
+ case isl_ast_expr_op_max:
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "#define ");
+ p = isl_printer_print_str(p, get_op_str_c(p, type));
+ p = isl_printer_print_str(p,
+ "(x,y) ((x) > (y) ? (x) : (y))");
+ p = isl_printer_end_line(p);
+ break;
+ case isl_ast_expr_op_fdiv_q:
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "#define ");
+ p = isl_printer_print_str(p, get_op_str_c(p, type));
+ p = isl_printer_print_str(p,
+ "(n,d) "
+ "(((n)<0) ? -((-(n)+(d)-1)/(d)) : (n)/(d))");
+ p = isl_printer_end_line(p);
+ break;
+ default:
+ break;
+ }
+
+ return p;
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_printer *isl_ast_op_type_print_macro(
+ enum isl_ast_expr_op_type type, __isl_take isl_printer *p)
+{
+ return isl_ast_expr_op_type_print_macro(type, p);
+}
+
+/* Call "fn" for each type of operation represented in the "macros"
+ * bit vector.
+ */
+static isl_stat foreach_ast_expr_op_type(int macros,
+ isl_stat (*fn)(enum isl_ast_expr_op_type type, void *user), void *user)
+{
+ if (macros & ISL_AST_MACRO_MIN && fn(isl_ast_expr_op_min, user) < 0)
+ return isl_stat_error;
+ if (macros & ISL_AST_MACRO_MAX && fn(isl_ast_expr_op_max, user) < 0)
+ return isl_stat_error;
+ if (macros & ISL_AST_MACRO_FDIV_Q &&
+ fn(isl_ast_expr_op_fdiv_q, user) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Call "fn" for each type of operation that appears in "expr"
+ * and that requires a macro definition.
+ */
+isl_stat isl_ast_expr_foreach_ast_expr_op_type(__isl_keep isl_ast_expr *expr,
+ isl_stat (*fn)(enum isl_ast_expr_op_type type, void *user), void *user)
+{
+ int macros;
+
+ if (!expr)
+ return isl_stat_error;
+
+ macros = ast_expr_required_macros(expr, 0);
+ return foreach_ast_expr_op_type(macros, fn, user);
+}
+
+/* This is an alternative name for the function above.
+ */
+isl_stat isl_ast_expr_foreach_ast_op_type(__isl_keep isl_ast_expr *expr,
+ isl_stat (*fn)(enum isl_ast_expr_op_type type, void *user), void *user)
+{
+ return isl_ast_expr_foreach_ast_expr_op_type(expr, fn, user);
+}
+
+/* Call "fn" for each type of operation that appears in "node"
+ * and that requires a macro definition.
+ */
+isl_stat isl_ast_node_foreach_ast_expr_op_type(__isl_keep isl_ast_node *node,
+ isl_stat (*fn)(enum isl_ast_expr_op_type type, void *user), void *user)
+{
+ int macros;
+
+ if (!node)
+ return isl_stat_error;
+
+ macros = ast_node_required_macros(node, 0);
+ return foreach_ast_expr_op_type(macros, fn, user);
+}
+
+/* This is an alternative name for the function above.
+ */
+isl_stat isl_ast_node_foreach_ast_op_type(__isl_keep isl_ast_node *node,
+ isl_stat (*fn)(enum isl_ast_expr_op_type type, void *user), void *user)
+{
+ return isl_ast_node_foreach_ast_expr_op_type(node, fn, user);
+}
+
+static isl_stat ast_op_type_print_macro(enum isl_ast_expr_op_type type,
+ void *user)
+{
+ isl_printer **p = user;
+
+ *p = isl_ast_expr_op_type_print_macro(type, *p);
+
+ return isl_stat_ok;
+}
+
+/* Print macro definitions for all the macros used in the result
+ * of printing "expr".
+ */
+__isl_give isl_printer *isl_ast_expr_print_macros(
+ __isl_keep isl_ast_expr *expr, __isl_take isl_printer *p)
+{
+ if (isl_ast_expr_foreach_ast_expr_op_type(expr,
+ &ast_op_type_print_macro, &p) < 0)
+ return isl_printer_free(p);
+ return p;
+}
+
+/* Print macro definitions for all the macros used in the result
+ * of printing "node".
+ */
+__isl_give isl_printer *isl_ast_node_print_macros(
+ __isl_keep isl_ast_node *node, __isl_take isl_printer *p)
+{
+ if (isl_ast_node_foreach_ast_expr_op_type(node,
+ &ast_op_type_print_macro, &p) < 0)
+ return isl_printer_free(p);
+ return p;
+}
+
+/* Return a string containing C code representing this isl_ast_expr.
+ */
+__isl_give char *isl_ast_expr_to_C_str(__isl_keep isl_ast_expr *expr)
+{
+ isl_printer *p;
+ char *str;
+
+ if (!expr)
+ return NULL;
+
+ p = isl_printer_to_str(isl_ast_expr_get_ctx(expr));
+ p = isl_printer_set_output_format(p, ISL_FORMAT_C);
+ p = isl_printer_print_ast_expr(p, expr);
+
+ str = isl_printer_get_str(p);
+
+ isl_printer_free(p);
+
+ return str;
+}
+
+/* Return a string containing C code representing this isl_ast_node.
+ */
+__isl_give char *isl_ast_node_to_C_str(__isl_keep isl_ast_node *node)
+{
+ isl_printer *p;
+ char *str;
+
+ if (!node)
+ return NULL;
+
+ p = isl_printer_to_str(isl_ast_node_get_ctx(node));
+ p = isl_printer_set_output_format(p, ISL_FORMAT_C);
+ p = isl_printer_print_ast_node(p, node);
+
+ str = isl_printer_get_str(p);
+
+ isl_printer_free(p);
+
+ return str;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build.c
new file mode 100644
index 00000000000..a0776e82350
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build.c
@@ -0,0 +1,2459 @@
+/*
+ * Copyright 2012-2013 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#include <isl/id.h>
+#include <isl/val.h>
+#include <isl/space.h>
+#include <isl/map.h>
+#include <isl/aff.h>
+#include <isl/constraint.h>
+#include <isl/map.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl_ast_build_private.h>
+#include <isl_ast_private.h>
+#include <isl_config.h>
+
+/* Construct a map that isolates the current dimension.
+ *
+ * Essentially, the current dimension of "set" is moved to the single output
+ * dimension in the result, with the current dimension in the domain replaced
+ * by an unconstrained variable.
+ */
+__isl_give isl_map *isl_ast_build_map_to_iterator(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set)
+{
+ isl_map *map;
+
+ map = isl_map_from_domain(set);
+ map = isl_map_add_dims(map, isl_dim_out, 1);
+
+ if (!build)
+ return isl_map_free(map);
+
+ map = isl_map_equate(map, isl_dim_in, build->depth, isl_dim_out, 0);
+ map = isl_map_eliminate(map, isl_dim_in, build->depth, 1);
+
+ return map;
+}
+
+/* Initialize the information derived during the AST generation to default
+ * values for a schedule domain in "space".
+ *
+ * We also check that the remaining fields are not NULL so that
+ * the calling functions don't have to perform this test.
+ */
+static __isl_give isl_ast_build *isl_ast_build_init_derived(
+ __isl_take isl_ast_build *build, __isl_take isl_space *space)
+{
+ isl_ctx *ctx;
+ isl_vec *strides;
+ isl_size dim;
+
+ build = isl_ast_build_cow(build);
+ if (!build || !build->domain)
+ goto error;
+
+ ctx = isl_ast_build_get_ctx(build);
+ dim = isl_space_dim(space, isl_dim_set);
+ if (dim < 0)
+ goto error;
+ strides = isl_vec_alloc(ctx, dim);
+ strides = isl_vec_set_si(strides, 1);
+
+ isl_vec_free(build->strides);
+ build->strides = strides;
+
+ space = isl_space_map_from_set(space);
+ isl_multi_aff_free(build->offsets);
+ build->offsets = isl_multi_aff_zero(isl_space_copy(space));
+ isl_multi_aff_free(build->values);
+ build->values = isl_multi_aff_identity(isl_space_copy(space));
+ isl_multi_aff_free(build->internal2input);
+ build->internal2input = isl_multi_aff_identity(space);
+
+ if (!build->iterators || !build->domain || !build->generated ||
+ !build->pending || !build->values || !build->internal2input ||
+ !build->strides || !build->offsets || !build->options)
+ return isl_ast_build_free(build);
+
+ return build;
+error:
+ isl_space_free(space);
+ return isl_ast_build_free(build);
+}
+
+/* Return an isl_id called "c%d", with "%d" set to "i".
+ * If an isl_id with such a name already appears among the parameters
+ * in build->domain, then adjust the name to "c%d_%d".
+ */
+static __isl_give isl_id *generate_name(isl_ctx *ctx, int i,
+ __isl_keep isl_ast_build *build)
+{
+ int j;
+ char name[23];
+ isl_set *dom = build->domain;
+
+ snprintf(name, sizeof(name), "c%d", i);
+ j = 0;
+ while (isl_set_find_dim_by_name(dom, isl_dim_param, name) >= 0)
+ snprintf(name, sizeof(name), "c%d_%d", i, j++);
+ return isl_id_alloc(ctx, name, NULL);
+}
+
+/* Create an isl_ast_build with "set" as domain.
+ *
+ * The input set is usually a parameter domain, but we currently allow it to
+ * be any kind of set. We set the domain of the returned isl_ast_build
+ * to "set" and initialize all the other fields to default values.
+ */
+__isl_give isl_ast_build *isl_ast_build_from_context(__isl_take isl_set *set)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_ast_build *build;
+
+ set = isl_set_compute_divs(set);
+ n = isl_set_dim(set, isl_dim_set);
+ if (n < 0)
+ goto error;
+
+ ctx = isl_set_get_ctx(set);
+
+ build = isl_calloc_type(ctx, isl_ast_build);
+ if (!build)
+ goto error;
+
+ build->ref = 1;
+ build->domain = set;
+ build->generated = isl_set_copy(build->domain);
+ build->pending = isl_set_universe(isl_set_get_space(build->domain));
+ build->options = isl_union_map_empty(isl_space_params_alloc(ctx, 0));
+ build->depth = n;
+ build->iterators = isl_id_list_alloc(ctx, n);
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+ if (isl_set_has_dim_id(set, isl_dim_set, i))
+ id = isl_set_get_dim_id(set, isl_dim_set, i);
+ else
+ id = generate_name(ctx, i, build);
+ build->iterators = isl_id_list_add(build->iterators, id);
+ }
+ space = isl_set_get_space(set);
+ if (isl_space_is_params(space))
+ space = isl_space_set_from_params(space);
+
+ return isl_ast_build_init_derived(build, space);
+error:
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Create an isl_ast_build with a universe (parametric) context.
+ */
+__isl_give isl_ast_build *isl_ast_build_alloc(isl_ctx *ctx)
+{
+ isl_space *space;
+ isl_set *context;
+
+ space = isl_space_params_alloc(ctx, 0);
+ context = isl_set_universe(space);
+
+ return isl_ast_build_from_context(context);
+}
+
+__isl_give isl_ast_build *isl_ast_build_copy(__isl_keep isl_ast_build *build)
+{
+ if (!build)
+ return NULL;
+
+ build->ref++;
+ return build;
+}
+
+__isl_give isl_ast_build *isl_ast_build_dup(__isl_keep isl_ast_build *build)
+{
+ isl_ctx *ctx;
+ isl_ast_build *dup;
+
+ if (!build)
+ return NULL;
+
+ ctx = isl_ast_build_get_ctx(build);
+ dup = isl_calloc_type(ctx, isl_ast_build);
+ if (!dup)
+ return NULL;
+
+ dup->ref = 1;
+ dup->outer_pos = build->outer_pos;
+ dup->depth = build->depth;
+ dup->iterators = isl_id_list_copy(build->iterators);
+ dup->domain = isl_set_copy(build->domain);
+ dup->generated = isl_set_copy(build->generated);
+ dup->pending = isl_set_copy(build->pending);
+ dup->values = isl_multi_aff_copy(build->values);
+ dup->internal2input = isl_multi_aff_copy(build->internal2input);
+ dup->value = isl_pw_aff_copy(build->value);
+ dup->strides = isl_vec_copy(build->strides);
+ dup->offsets = isl_multi_aff_copy(build->offsets);
+ dup->executed = isl_union_map_copy(build->executed);
+ dup->single_valued = build->single_valued;
+ dup->options = isl_union_map_copy(build->options);
+ dup->at_each_domain = build->at_each_domain;
+ dup->at_each_domain_user = build->at_each_domain_user;
+ dup->before_each_for = build->before_each_for;
+ dup->before_each_for_user = build->before_each_for_user;
+ dup->after_each_for = build->after_each_for;
+ dup->after_each_for_user = build->after_each_for_user;
+ dup->before_each_mark = build->before_each_mark;
+ dup->before_each_mark_user = build->before_each_mark_user;
+ dup->after_each_mark = build->after_each_mark;
+ dup->after_each_mark_user = build->after_each_mark_user;
+ dup->create_leaf = build->create_leaf;
+ dup->create_leaf_user = build->create_leaf_user;
+ dup->node = isl_schedule_node_copy(build->node);
+ if (build->loop_type) {
+ int i;
+
+ dup->n = build->n;
+ dup->loop_type = isl_alloc_array(ctx,
+ enum isl_ast_loop_type, dup->n);
+ if (dup->n && !dup->loop_type)
+ return isl_ast_build_free(dup);
+ for (i = 0; i < dup->n; ++i)
+ dup->loop_type[i] = build->loop_type[i];
+ }
+
+ if (!dup->iterators || !dup->domain || !dup->generated ||
+ !dup->pending || !dup->values ||
+ !dup->strides || !dup->offsets || !dup->options ||
+ (build->internal2input && !dup->internal2input) ||
+ (build->executed && !dup->executed) ||
+ (build->value && !dup->value) ||
+ (build->node && !dup->node))
+ return isl_ast_build_free(dup);
+
+ return dup;
+}
+
+/* Align the parameters of "build" to those of "model", introducing
+ * additional parameters if needed.
+ */
+__isl_give isl_ast_build *isl_ast_build_align_params(
+ __isl_take isl_ast_build *build, __isl_take isl_space *model)
+{
+ build = isl_ast_build_cow(build);
+ if (!build)
+ goto error;
+
+ build->domain = isl_set_align_params(build->domain,
+ isl_space_copy(model));
+ build->generated = isl_set_align_params(build->generated,
+ isl_space_copy(model));
+ build->pending = isl_set_align_params(build->pending,
+ isl_space_copy(model));
+ build->values = isl_multi_aff_align_params(build->values,
+ isl_space_copy(model));
+ build->offsets = isl_multi_aff_align_params(build->offsets,
+ isl_space_copy(model));
+ build->options = isl_union_map_align_params(build->options,
+ isl_space_copy(model));
+ if (build->internal2input) {
+ build->internal2input =
+ isl_multi_aff_align_params(build->internal2input,
+ model);
+ if (!build->internal2input)
+ return isl_ast_build_free(build);
+ } else {
+ isl_space_free(model);
+ }
+
+ if (!build->domain || !build->values || !build->offsets ||
+ !build->options)
+ return isl_ast_build_free(build);
+
+ return build;
+error:
+ isl_space_free(model);
+ return NULL;
+}
+
+__isl_give isl_ast_build *isl_ast_build_cow(__isl_take isl_ast_build *build)
+{
+ if (!build)
+ return NULL;
+
+ if (build->ref == 1)
+ return build;
+ build->ref--;
+ return isl_ast_build_dup(build);
+}
+
+__isl_null isl_ast_build *isl_ast_build_free(
+ __isl_take isl_ast_build *build)
+{
+ if (!build)
+ return NULL;
+
+ if (--build->ref > 0)
+ return NULL;
+
+ isl_id_list_free(build->iterators);
+ isl_set_free(build->domain);
+ isl_set_free(build->generated);
+ isl_set_free(build->pending);
+ isl_multi_aff_free(build->values);
+ isl_multi_aff_free(build->internal2input);
+ isl_pw_aff_free(build->value);
+ isl_vec_free(build->strides);
+ isl_multi_aff_free(build->offsets);
+ isl_multi_aff_free(build->schedule_map);
+ isl_union_map_free(build->executed);
+ isl_union_map_free(build->options);
+ isl_schedule_node_free(build->node);
+ free(build->loop_type);
+ isl_set_free(build->isolated);
+
+ free(build);
+
+ return NULL;
+}
+
+isl_ctx *isl_ast_build_get_ctx(__isl_keep isl_ast_build *build)
+{
+ return build ? isl_set_get_ctx(build->domain) : NULL;
+}
+
+/* Replace build->options by "options".
+ */
+__isl_give isl_ast_build *isl_ast_build_set_options(
+ __isl_take isl_ast_build *build, __isl_take isl_union_map *options)
+{
+ build = isl_ast_build_cow(build);
+
+ if (!build || !options)
+ goto error;
+
+ isl_union_map_free(build->options);
+ build->options = options;
+
+ return build;
+error:
+ isl_union_map_free(options);
+ return isl_ast_build_free(build);
+}
+
+/* Set the iterators for the next code generation.
+ *
+ * If we still have some iterators left from the previous code generation
+ * (if any) or if iterators have already been set by a previous
+ * call to this function, then we remove them first.
+ */
+__isl_give isl_ast_build *isl_ast_build_set_iterators(
+ __isl_take isl_ast_build *build, __isl_take isl_id_list *iterators)
+{
+ isl_size dim, n_it;
+
+ build = isl_ast_build_cow(build);
+ if (!build)
+ goto error;
+
+ dim = isl_ast_build_dim(build, isl_dim_set);
+ n_it = isl_id_list_n_id(build->iterators);
+ if (dim < 0 || n_it < 0)
+ goto error;
+ if (n_it < dim)
+ isl_die(isl_ast_build_get_ctx(build), isl_error_internal,
+ "isl_ast_build in inconsistent state", goto error);
+ if (n_it > dim)
+ build->iterators = isl_id_list_drop(build->iterators,
+ dim, n_it - dim);
+ build->iterators = isl_id_list_concat(build->iterators, iterators);
+ if (!build->iterators)
+ return isl_ast_build_free(build);
+
+ return build;
+error:
+ isl_id_list_free(iterators);
+ return isl_ast_build_free(build);
+}
+
+/* Set the "at_each_domain" callback of "build" to "fn".
+ */
+__isl_give isl_ast_build *isl_ast_build_set_at_each_domain(
+ __isl_take isl_ast_build *build,
+ __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build, void *user), void *user)
+{
+ build = isl_ast_build_cow(build);
+
+ if (!build)
+ return NULL;
+
+ build->at_each_domain = fn;
+ build->at_each_domain_user = user;
+
+ return build;
+}
+
+/* Set the "before_each_for" callback of "build" to "fn".
+ */
+__isl_give isl_ast_build *isl_ast_build_set_before_each_for(
+ __isl_take isl_ast_build *build,
+ __isl_give isl_id *(*fn)(__isl_keep isl_ast_build *build,
+ void *user), void *user)
+{
+ build = isl_ast_build_cow(build);
+
+ if (!build)
+ return NULL;
+
+ build->before_each_for = fn;
+ build->before_each_for_user = user;
+
+ return build;
+}
+
+/* Set the "after_each_for" callback of "build" to "fn".
+ */
+__isl_give isl_ast_build *isl_ast_build_set_after_each_for(
+ __isl_take isl_ast_build *build,
+ __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build, void *user), void *user)
+{
+ build = isl_ast_build_cow(build);
+
+ if (!build)
+ return NULL;
+
+ build->after_each_for = fn;
+ build->after_each_for_user = user;
+
+ return build;
+}
+
+/* Set the "before_each_mark" callback of "build" to "fn".
+ */
+__isl_give isl_ast_build *isl_ast_build_set_before_each_mark(
+ __isl_take isl_ast_build *build,
+ isl_stat (*fn)(__isl_keep isl_id *mark, __isl_keep isl_ast_build *build,
+ void *user), void *user)
+{
+ build = isl_ast_build_cow(build);
+
+ if (!build)
+ return NULL;
+
+ build->before_each_mark = fn;
+ build->before_each_mark_user = user;
+
+ return build;
+}
+
+/* Set the "after_each_mark" callback of "build" to "fn".
+ */
+__isl_give isl_ast_build *isl_ast_build_set_after_each_mark(
+ __isl_take isl_ast_build *build,
+ __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build, void *user), void *user)
+{
+ build = isl_ast_build_cow(build);
+
+ if (!build)
+ return NULL;
+
+ build->after_each_mark = fn;
+ build->after_each_mark_user = user;
+
+ return build;
+}
+
+/* Set the "create_leaf" callback of "build" to "fn".
+ */
+__isl_give isl_ast_build *isl_ast_build_set_create_leaf(
+ __isl_take isl_ast_build *build,
+ __isl_give isl_ast_node *(*fn)(__isl_take isl_ast_build *build,
+ void *user), void *user)
+{
+ build = isl_ast_build_cow(build);
+
+ if (!build)
+ return NULL;
+
+ build->create_leaf = fn;
+ build->create_leaf_user = user;
+
+ return build;
+}
+
+/* Clear all information that is specific to this code generation
+ * and that is (probably) not meaningful to any nested code generation.
+ */
+__isl_give isl_ast_build *isl_ast_build_clear_local_info(
+ __isl_take isl_ast_build *build)
+{
+ isl_space *space;
+
+ build = isl_ast_build_cow(build);
+ if (!build)
+ return NULL;
+
+ space = isl_union_map_get_space(build->options);
+ isl_union_map_free(build->options);
+ build->options = isl_union_map_empty(space);
+
+ build->at_each_domain = NULL;
+ build->at_each_domain_user = NULL;
+ build->before_each_for = NULL;
+ build->before_each_for_user = NULL;
+ build->after_each_for = NULL;
+ build->after_each_for_user = NULL;
+ build->before_each_mark = NULL;
+ build->before_each_mark_user = NULL;
+ build->after_each_mark = NULL;
+ build->after_each_mark_user = NULL;
+ build->create_leaf = NULL;
+ build->create_leaf_user = NULL;
+
+ if (!build->options)
+ return isl_ast_build_free(build);
+
+ return build;
+}
+
+/* Have any loops been eliminated?
+ * That is, do any of the original schedule dimensions have a fixed
+ * value that has been substituted?
+ */
+static int any_eliminated(isl_ast_build *build)
+{
+ int i;
+
+ for (i = 0; i < build->depth; ++i)
+ if (isl_ast_build_has_affine_value(build, i))
+ return 1;
+
+ return 0;
+}
+
+/* Clear build->schedule_map.
+ * This function should be called whenever anything that might affect
+ * the result of isl_ast_build_get_schedule_map_multi_aff changes.
+ * In particular, it should be called when the depth is changed or
+ * when an iterator is determined to have a fixed value.
+ */
+static void isl_ast_build_reset_schedule_map(__isl_keep isl_ast_build *build)
+{
+ if (!build)
+ return;
+ isl_multi_aff_free(build->schedule_map);
+ build->schedule_map = NULL;
+}
+
+/* Do we need a (non-trivial) schedule map?
+ * That is, is the internal schedule space different from
+ * the external schedule space?
+ *
+ * The internal and external schedule spaces are only the same
+ * if code has been generated for the entire schedule and if none
+ * of the loops have been eliminated.
+ */
+isl_bool isl_ast_build_need_schedule_map(__isl_keep isl_ast_build *build)
+{
+ isl_size dim;
+
+ dim = isl_ast_build_dim(build, isl_dim_set);
+ if (dim < 0)
+ return isl_bool_error;
+ return isl_bool_ok(build->depth != dim || any_eliminated(build));
+}
+
+/* Return a mapping from the internal schedule space to the external
+ * schedule space in the form of an isl_multi_aff.
+ * The internal schedule space originally corresponds to that of the
+ * input schedule. This may change during the code generation if
+ * if isl_ast_build_insert_dim is ever called.
+ * The external schedule space corresponds to the
+ * loops that have been generated.
+ *
+ * Currently, the only difference between the internal schedule domain
+ * and the external schedule domain is that some dimensions are projected
+ * out in the external schedule domain. In particular, the dimensions
+ * for which no code has been generated yet and the dimensions that correspond
+ * to eliminated loops.
+ *
+ * We cache a copy of the schedule_map in build->schedule_map.
+ * The cache is cleared through isl_ast_build_reset_schedule_map
+ * whenever anything changes that might affect the result of this function.
+ */
+__isl_give isl_multi_aff *isl_ast_build_get_schedule_map_multi_aff(
+ __isl_keep isl_ast_build *build)
+{
+ isl_bool needs_map;
+ isl_space *space;
+ isl_multi_aff *ma;
+
+ if (!build)
+ return NULL;
+ if (build->schedule_map)
+ return isl_multi_aff_copy(build->schedule_map);
+ needs_map = isl_ast_build_need_schedule_map(build);
+ if (needs_map < 0)
+ return NULL;
+
+ space = isl_ast_build_get_space(build, 1);
+ space = isl_space_map_from_set(space);
+ ma = isl_multi_aff_identity(space);
+ if (needs_map) {
+ int i;
+ isl_size dim = isl_ast_build_dim(build, isl_dim_set);
+
+ if (dim < 0)
+ ma = isl_multi_aff_free(ma);
+ ma = isl_multi_aff_drop_dims(ma, isl_dim_out,
+ build->depth, dim - build->depth);
+ for (i = build->depth - 1; i >= 0; --i)
+ if (isl_ast_build_has_affine_value(build, i))
+ ma = isl_multi_aff_drop_dims(ma,
+ isl_dim_out, i, 1);
+ }
+
+ build->schedule_map = ma;
+ return isl_multi_aff_copy(build->schedule_map);
+}
+
+/* Return a mapping from the internal schedule space to the external
+ * schedule space in the form of an isl_map.
+ */
+__isl_give isl_map *isl_ast_build_get_schedule_map(
+ __isl_keep isl_ast_build *build)
+{
+ isl_multi_aff *ma;
+
+ ma = isl_ast_build_get_schedule_map_multi_aff(build);
+ return isl_map_from_multi_aff(ma);
+}
+
+/* Return the position of the dimension in build->domain for which
+ * an AST node is currently being generated.
+ */
+isl_size isl_ast_build_get_depth(__isl_keep isl_ast_build *build)
+{
+ return build ? build->depth : isl_size_error;
+}
+
+/* Prepare for generating code for the next level.
+ * In particular, increase the depth and reset any information
+ * that is local to the current depth.
+ */
+__isl_give isl_ast_build *isl_ast_build_increase_depth(
+ __isl_take isl_ast_build *build)
+{
+ build = isl_ast_build_cow(build);
+ if (!build)
+ return NULL;
+ build->depth++;
+ isl_ast_build_reset_schedule_map(build);
+ build->value = isl_pw_aff_free(build->value);
+ return build;
+}
+
+void isl_ast_build_dump(__isl_keep isl_ast_build *build)
+{
+ if (!build)
+ return;
+
+ fprintf(stderr, "domain: ");
+ isl_set_dump(build->domain);
+ fprintf(stderr, "generated: ");
+ isl_set_dump(build->generated);
+ fprintf(stderr, "pending: ");
+ isl_set_dump(build->pending);
+ fprintf(stderr, "iterators: ");
+ isl_id_list_dump(build->iterators);
+ fprintf(stderr, "values: ");
+ isl_multi_aff_dump(build->values);
+ if (build->value) {
+ fprintf(stderr, "value: ");
+ isl_pw_aff_dump(build->value);
+ }
+ fprintf(stderr, "strides: ");
+ isl_vec_dump(build->strides);
+ fprintf(stderr, "offsets: ");
+ isl_multi_aff_dump(build->offsets);
+ fprintf(stderr, "internal2input: ");
+ isl_multi_aff_dump(build->internal2input);
+}
+
+/* Initialize "build" for AST construction in schedule space "space"
+ * in the case that build->domain is a parameter set.
+ *
+ * build->iterators is assumed to have been updated already.
+ */
+static __isl_give isl_ast_build *isl_ast_build_init(
+ __isl_take isl_ast_build *build, __isl_take isl_space *space)
+{
+ isl_set *set;
+
+ build = isl_ast_build_cow(build);
+ if (!build)
+ goto error;
+
+ set = isl_set_universe(isl_space_copy(space));
+ build->domain = isl_set_intersect_params(isl_set_copy(set),
+ build->domain);
+ build->pending = isl_set_intersect_params(isl_set_copy(set),
+ build->pending);
+ build->generated = isl_set_intersect_params(set, build->generated);
+
+ return isl_ast_build_init_derived(build, space);
+error:
+ isl_ast_build_free(build);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Assign "aff" to *user and return -1, effectively extracting
+ * the first (and presumably only) affine expression in the isl_pw_aff
+ * on which this function is used.
+ */
+static isl_stat extract_single_piece(__isl_take isl_set *set,
+ __isl_take isl_aff *aff, void *user)
+{
+ isl_aff **p = user;
+
+ *p = aff;
+ isl_set_free(set);
+
+ return isl_stat_error;
+}
+
+/* Intersect "set" with the stride constraint of "build", if any.
+ */
+static __isl_give isl_set *intersect_stride_constraint(__isl_take isl_set *set,
+ __isl_keep isl_ast_build *build)
+{
+ isl_set *stride;
+
+ if (!build)
+ return isl_set_free(set);
+ if (!isl_ast_build_has_stride(build, build->depth))
+ return set;
+
+ stride = isl_ast_build_get_stride_constraint(build);
+ return isl_set_intersect(set, stride);
+}
+
+/* Check if the given bounds on the current dimension (together with
+ * the stride constraint, if any) imply that
+ * this current dimension attains only a single value (in terms of
+ * parameters and outer dimensions).
+ * If so, we record it in build->value.
+ * If, moreover, this value can be represented as a single affine expression,
+ * then we also update build->values, effectively marking the current
+ * dimension as "eliminated".
+ *
+ * When computing the gist of the fixed value that can be represented
+ * as a single affine expression, it is important to only take into
+ * account the domain constraints in the original AST build and
+ * not the domain of the affine expression itself.
+ * Otherwise, a [i/3] is changed into a i/3 because we know that i
+ * is a multiple of 3, but then we end up not expressing anywhere
+ * in the context that i is a multiple of 3.
+ */
+static __isl_give isl_ast_build *update_values(
+ __isl_take isl_ast_build *build, __isl_take isl_basic_set *bounds)
+{
+ isl_bool sv;
+ isl_size n;
+ isl_pw_multi_aff *pma;
+ isl_aff *aff = NULL;
+ isl_map *it_map;
+ isl_set *set;
+
+ set = isl_set_from_basic_set(bounds);
+ set = isl_set_intersect(set, isl_set_copy(build->domain));
+ set = intersect_stride_constraint(set, build);
+ it_map = isl_ast_build_map_to_iterator(build, set);
+
+ sv = isl_map_is_single_valued(it_map);
+ if (sv < 0)
+ build = isl_ast_build_free(build);
+ if (!build || !sv) {
+ isl_map_free(it_map);
+ return build;
+ }
+
+ pma = isl_pw_multi_aff_from_map(it_map);
+ build->value = isl_pw_multi_aff_get_pw_aff(pma, 0);
+ build->value = isl_ast_build_compute_gist_pw_aff(build, build->value);
+ build->value = isl_pw_aff_coalesce(build->value);
+ isl_pw_multi_aff_free(pma);
+
+ n = isl_pw_aff_n_piece(build->value);
+ if (n < 0)
+ return isl_ast_build_free(build);
+ if (n != 1)
+ return build;
+
+ isl_pw_aff_foreach_piece(build->value, &extract_single_piece, &aff);
+
+ build->values = isl_multi_aff_set_aff(build->values, build->depth, aff);
+ if (!build->values)
+ return isl_ast_build_free(build);
+ isl_ast_build_reset_schedule_map(build);
+ return build;
+}
+
+/* Update the AST build based on the given loop bounds for
+ * the current dimension and the stride information available in the build.
+ *
+ * We first make sure that the bounds do not refer to any iterators
+ * that have already been eliminated.
+ * Then, we check if the bounds imply that the current iterator
+ * has a fixed value.
+ * If they do and if this fixed value can be expressed as a single
+ * affine expression, we eliminate the iterators from the bounds.
+ * Note that we cannot simply plug in this single value using
+ * isl_basic_set_preimage_multi_aff as the single value may only
+ * be defined on a subset of the domain. Plugging in the value
+ * would restrict the build domain to this subset, while this
+ * restriction may not be reflected in the generated code.
+ * Finally, we intersect build->domain with the updated bounds.
+ * We also add the stride constraint unless we have been able
+ * to find a fixed value expressed as a single affine expression.
+ *
+ * Note that the check for a fixed value in update_values requires
+ * us to intersect the bounds with the current build domain.
+ * When we intersect build->domain with the updated bounds in
+ * the final step, we make sure that these updated bounds have
+ * not been intersected with the old build->domain.
+ * Otherwise, we would indirectly intersect the build domain with itself,
+ * which can lead to inefficiencies, in particular if the build domain
+ * contains any unknown divs.
+ *
+ * The pending and generated sets are not updated by this function to
+ * match the updated domain.
+ * The caller still needs to call isl_ast_build_set_pending_generated.
+ */
+__isl_give isl_ast_build *isl_ast_build_set_loop_bounds(
+ __isl_take isl_ast_build *build, __isl_take isl_basic_set *bounds)
+{
+ isl_set *set;
+
+ build = isl_ast_build_cow(build);
+ if (!build)
+ goto error;
+
+ build = update_values(build, isl_basic_set_copy(bounds));
+ if (!build)
+ goto error;
+ set = isl_set_from_basic_set(isl_basic_set_copy(bounds));
+ if (isl_ast_build_has_affine_value(build, build->depth)) {
+ set = isl_set_eliminate(set, isl_dim_set, build->depth, 1);
+ set = isl_set_compute_divs(set);
+ build->pending = isl_set_intersect(build->pending,
+ isl_set_copy(set));
+ build->domain = isl_set_intersect(build->domain, set);
+ } else {
+ build->domain = isl_set_intersect(build->domain, set);
+ build = isl_ast_build_include_stride(build);
+ if (!build)
+ goto error;
+ }
+ isl_basic_set_free(bounds);
+
+ if (!build->domain || !build->pending || !build->generated)
+ return isl_ast_build_free(build);
+
+ return build;
+error:
+ isl_ast_build_free(build);
+ isl_basic_set_free(bounds);
+ return NULL;
+}
+
+/* Update the pending and generated sets of "build" according to "bounds".
+ * If the build has an affine value at the current depth,
+ * then isl_ast_build_set_loop_bounds has already set the pending set.
+ * Otherwise, do it here.
+ */
+__isl_give isl_ast_build *isl_ast_build_set_pending_generated(
+ __isl_take isl_ast_build *build, __isl_take isl_basic_set *bounds)
+{
+ isl_basic_set *generated, *pending;
+
+ if (!build)
+ goto error;
+
+ if (isl_ast_build_has_affine_value(build, build->depth)) {
+ isl_basic_set_free(bounds);
+ return build;
+ }
+
+ build = isl_ast_build_cow(build);
+ if (!build)
+ goto error;
+
+ pending = isl_basic_set_copy(bounds);
+ pending = isl_basic_set_drop_constraints_involving_dims(pending,
+ isl_dim_set, build->depth, 1);
+ build->pending = isl_set_intersect(build->pending,
+ isl_set_from_basic_set(pending));
+ generated = bounds;
+ generated = isl_basic_set_drop_constraints_not_involving_dims(
+ generated, isl_dim_set, build->depth, 1);
+ build->generated = isl_set_intersect(build->generated,
+ isl_set_from_basic_set(generated));
+
+ if (!build->pending || !build->generated)
+ return isl_ast_build_free(build);
+
+ return build;
+error:
+ isl_ast_build_free(build);
+ isl_basic_set_free(bounds);
+ return NULL;
+}
+
+/* Intersect build->domain with "set", where "set" is specified
+ * in terms of the internal schedule domain.
+ */
+static __isl_give isl_ast_build *isl_ast_build_restrict_internal(
+ __isl_take isl_ast_build *build, __isl_take isl_set *set)
+{
+ build = isl_ast_build_cow(build);
+ if (!build)
+ goto error;
+
+ set = isl_set_compute_divs(set);
+ build->domain = isl_set_intersect(build->domain, set);
+ build->domain = isl_set_coalesce(build->domain);
+
+ if (!build->domain)
+ return isl_ast_build_free(build);
+
+ return build;
+error:
+ isl_ast_build_free(build);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Intersect build->generated and build->domain with "set",
+ * where "set" is specified in terms of the internal schedule domain.
+ */
+__isl_give isl_ast_build *isl_ast_build_restrict_generated(
+ __isl_take isl_ast_build *build, __isl_take isl_set *set)
+{
+ set = isl_set_compute_divs(set);
+ build = isl_ast_build_restrict_internal(build, isl_set_copy(set));
+ build = isl_ast_build_cow(build);
+ if (!build)
+ goto error;
+
+ build->generated = isl_set_intersect(build->generated, set);
+ build->generated = isl_set_coalesce(build->generated);
+
+ if (!build->generated)
+ return isl_ast_build_free(build);
+
+ return build;
+error:
+ isl_ast_build_free(build);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Replace the set of pending constraints by "guard", which is then
+ * no longer considered as pending.
+ * That is, add "guard" to the generated constraints and clear all pending
+ * constraints, making the domain equal to the generated constraints.
+ */
+__isl_give isl_ast_build *isl_ast_build_replace_pending_by_guard(
+ __isl_take isl_ast_build *build, __isl_take isl_set *guard)
+{
+ build = isl_ast_build_restrict_generated(build, guard);
+ build = isl_ast_build_cow(build);
+ if (!build)
+ return NULL;
+
+ isl_set_free(build->domain);
+ build->domain = isl_set_copy(build->generated);
+ isl_set_free(build->pending);
+ build->pending = isl_set_universe(isl_set_get_space(build->domain));
+
+ if (!build->pending)
+ return isl_ast_build_free(build);
+
+ return build;
+}
+
+/* Intersect build->domain with "set", where "set" is specified
+ * in terms of the external schedule domain.
+ */
+__isl_give isl_ast_build *isl_ast_build_restrict(
+ __isl_take isl_ast_build *build, __isl_take isl_set *set)
+{
+ isl_bool needs_map;
+
+ if (isl_set_is_params(set))
+ return isl_ast_build_restrict_generated(build, set);
+
+ needs_map = isl_ast_build_need_schedule_map(build);
+ if (needs_map < 0)
+ goto error;
+ if (needs_map) {
+ isl_multi_aff *ma;
+ ma = isl_ast_build_get_schedule_map_multi_aff(build);
+ set = isl_set_preimage_multi_aff(set, ma);
+ }
+ return isl_ast_build_restrict_generated(build, set);
+error:
+ isl_ast_build_free(build);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Replace build->executed by "executed".
+ */
+__isl_give isl_ast_build *isl_ast_build_set_executed(
+ __isl_take isl_ast_build *build, __isl_take isl_union_map *executed)
+{
+ build = isl_ast_build_cow(build);
+ if (!build)
+ goto error;
+
+ isl_union_map_free(build->executed);
+ build->executed = executed;
+
+ return build;
+error:
+ isl_ast_build_free(build);
+ isl_union_map_free(executed);
+ return NULL;
+}
+
+/* Does "build" point to a band node?
+ * That is, are we currently handling a band node inside a schedule tree?
+ */
+int isl_ast_build_has_schedule_node(__isl_keep isl_ast_build *build)
+{
+ if (!build)
+ return -1;
+ return build->node != NULL;
+}
+
+/* Return a copy of the band node that "build" refers to.
+ */
+__isl_give isl_schedule_node *isl_ast_build_get_schedule_node(
+ __isl_keep isl_ast_build *build)
+{
+ if (!build)
+ return NULL;
+ return isl_schedule_node_copy(build->node);
+}
+
+/* Extract the loop AST generation types for the members of build->node
+ * and store them in build->loop_type.
+ */
+static __isl_give isl_ast_build *extract_loop_types(
+ __isl_take isl_ast_build *build)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_schedule_node *node;
+
+ if (!build)
+ return NULL;
+ n = isl_schedule_node_band_n_member(build->node);
+ if (n < 0)
+ return isl_ast_build_free(build);
+ ctx = isl_ast_build_get_ctx(build);
+ if (!build->node)
+ isl_die(ctx, isl_error_internal, "missing AST node",
+ return isl_ast_build_free(build));
+
+ free(build->loop_type);
+ build->n = n;
+ build->loop_type = isl_alloc_array(ctx,
+ enum isl_ast_loop_type, build->n);
+ if (build->n && !build->loop_type)
+ return isl_ast_build_free(build);
+ node = build->node;
+ for (i = 0; i < build->n; ++i)
+ build->loop_type[i] =
+ isl_schedule_node_band_member_get_ast_loop_type(node, i);
+
+ return build;
+}
+
+/* Replace the band node that "build" refers to by "node" and
+ * extract the corresponding loop AST generation types.
+ */
+__isl_give isl_ast_build *isl_ast_build_set_schedule_node(
+ __isl_take isl_ast_build *build,
+ __isl_take isl_schedule_node *node)
+{
+ build = isl_ast_build_cow(build);
+ if (!build || !node)
+ goto error;
+
+ isl_schedule_node_free(build->node);
+ build->node = node;
+
+ build = extract_loop_types(build);
+
+ return build;
+error:
+ isl_ast_build_free(build);
+ isl_schedule_node_free(node);
+ return NULL;
+}
+
+/* Remove any reference to a band node from "build".
+ */
+__isl_give isl_ast_build *isl_ast_build_reset_schedule_node(
+ __isl_take isl_ast_build *build)
+{
+ build = isl_ast_build_cow(build);
+ if (!build)
+ return NULL;
+
+ isl_schedule_node_free(build->node);
+ build->node = NULL;
+
+ return build;
+}
+
+/* Return a copy of the current schedule domain.
+ */
+__isl_give isl_set *isl_ast_build_get_domain(__isl_keep isl_ast_build *build)
+{
+ return build ? isl_set_copy(build->domain) : NULL;
+}
+
+/* Return a copy of the set of pending constraints.
+ */
+__isl_give isl_set *isl_ast_build_get_pending(
+ __isl_keep isl_ast_build *build)
+{
+ return build ? isl_set_copy(build->pending) : NULL;
+}
+
+/* Return a copy of the set of generated constraints.
+ */
+__isl_give isl_set *isl_ast_build_get_generated(
+ __isl_keep isl_ast_build *build)
+{
+ return build ? isl_set_copy(build->generated) : NULL;
+}
+
+/* Return a copy of the map from the internal schedule domain
+ * to the original input schedule domain.
+ */
+__isl_give isl_multi_aff *isl_ast_build_get_internal2input(
+ __isl_keep isl_ast_build *build)
+{
+ return build ? isl_multi_aff_copy(build->internal2input) : NULL;
+}
+
+/* Return the number of variables of the given type
+ * in the (internal) schedule space.
+ */
+isl_size isl_ast_build_dim(__isl_keep isl_ast_build *build,
+ enum isl_dim_type type)
+{
+ if (!build)
+ return isl_size_error;
+ return isl_set_dim(build->domain, type);
+}
+
+/* Return the (schedule) space of "build".
+ *
+ * If "internal" is set, then this space is the space of the internal
+ * representation of the entire schedule, including those parts for
+ * which no code has been generated yet.
+ *
+ * If "internal" is not set, then this space is the external representation
+ * of the loops generated so far.
+ */
+__isl_give isl_space *isl_ast_build_get_space(__isl_keep isl_ast_build *build,
+ int internal)
+{
+ int i;
+ isl_size dim;
+ isl_bool needs_map;
+ isl_space *space;
+
+ if (!build)
+ return NULL;
+
+ space = isl_set_get_space(build->domain);
+ if (internal)
+ return space;
+
+ needs_map = isl_ast_build_need_schedule_map(build);
+ if (needs_map < 0)
+ return isl_space_free(space);
+ if (!needs_map)
+ return space;
+
+ dim = isl_ast_build_dim(build, isl_dim_set);
+ if (dim < 0)
+ return isl_space_free(space);
+ space = isl_space_drop_dims(space, isl_dim_set,
+ build->depth, dim - build->depth);
+ for (i = build->depth - 1; i >= 0; --i) {
+ isl_bool affine = isl_ast_build_has_affine_value(build, i);
+
+ if (affine < 0)
+ return isl_space_free(space);
+ if (affine)
+ space = isl_space_drop_dims(space, isl_dim_set, i, 1);
+ }
+
+ return space;
+}
+
+/* Return the external representation of the schedule space of "build",
+ * i.e., a space with a dimension for each loop generated so far,
+ * with the names of the dimensions set to the loop iterators.
+ */
+__isl_give isl_space *isl_ast_build_get_schedule_space(
+ __isl_keep isl_ast_build *build)
+{
+ isl_space *space;
+ int i, skip;
+
+ if (!build)
+ return NULL;
+
+ space = isl_ast_build_get_space(build, 0);
+
+ skip = 0;
+ for (i = 0; i < build->depth; ++i) {
+ isl_id *id;
+
+ if (isl_ast_build_has_affine_value(build, i)) {
+ skip++;
+ continue;
+ }
+
+ id = isl_ast_build_get_iterator_id(build, i);
+ space = isl_space_set_dim_id(space, isl_dim_set, i - skip, id);
+ }
+
+ return space;
+}
+
+/* Return the current schedule, as stored in build->executed, in terms
+ * of the external schedule domain.
+ */
+__isl_give isl_union_map *isl_ast_build_get_schedule(
+ __isl_keep isl_ast_build *build)
+{
+ isl_bool needs_map;
+ isl_union_map *executed;
+ isl_union_map *schedule;
+
+ needs_map = isl_ast_build_need_schedule_map(build);
+ if (needs_map < 0)
+ return NULL;
+
+ executed = isl_union_map_copy(build->executed);
+ if (needs_map) {
+ isl_map *proj = isl_ast_build_get_schedule_map(build);
+ executed = isl_union_map_apply_domain(executed,
+ isl_union_map_from_map(proj));
+ }
+ schedule = isl_union_map_reverse(executed);
+
+ return schedule;
+}
+
+/* Return the iterator attached to the internal schedule dimension "pos".
+ */
+__isl_give isl_id *isl_ast_build_get_iterator_id(
+ __isl_keep isl_ast_build *build, int pos)
+{
+ if (!build)
+ return NULL;
+
+ return isl_id_list_get_id(build->iterators, pos);
+}
+
+/* Set the stride and offset of the current dimension to the given
+ * value and expression.
+ */
+static __isl_give isl_ast_build *set_stride(__isl_take isl_ast_build *build,
+ __isl_take isl_val *stride, __isl_take isl_aff *offset)
+{
+ int pos;
+
+ build = isl_ast_build_cow(build);
+ if (!build || !stride || !offset)
+ goto error;
+
+ pos = build->depth;
+
+ build->strides = isl_vec_set_element_val(build->strides, pos, stride);
+ build->offsets = isl_multi_aff_set_aff(build->offsets, pos, offset);
+ if (!build->strides || !build->offsets)
+ return isl_ast_build_free(build);
+
+ return build;
+error:
+ isl_val_free(stride);
+ isl_aff_free(offset);
+ return isl_ast_build_free(build);
+}
+
+/* Return a set expressing the stride constraint at the current depth.
+ *
+ * In particular, if the current iterator (i) is known to attain values
+ *
+ * f + s a
+ *
+ * where f is the offset and s is the stride, then the returned set
+ * expresses the constraint
+ *
+ * (f - i) mod s = 0
+ */
+__isl_give isl_set *isl_ast_build_get_stride_constraint(
+ __isl_keep isl_ast_build *build)
+{
+ isl_aff *aff;
+ isl_set *set;
+ isl_val *stride;
+ int pos;
+
+ if (!build)
+ return NULL;
+
+ pos = build->depth;
+
+ if (!isl_ast_build_has_stride(build, pos))
+ return isl_set_universe(isl_ast_build_get_space(build, 1));
+
+ stride = isl_ast_build_get_stride(build, pos);
+ aff = isl_ast_build_get_offset(build, pos);
+ aff = isl_aff_add_coefficient_si(aff, isl_dim_in, pos, -1);
+ aff = isl_aff_mod_val(aff, stride);
+ set = isl_set_from_basic_set(isl_aff_zero_basic_set(aff));
+
+ return set;
+}
+
+/* Return the expansion implied by the stride and offset at the current
+ * depth.
+ *
+ * That is, return the mapping
+ *
+ * [i_0, ..., i_{d-1}, i_d, i_{d+1}, ...]
+ * -> [i_0, ..., i_{d-1}, s * i_d + offset(i), i_{d+1}, ...]
+ *
+ * where s is the stride at the current depth d and offset(i) is
+ * the corresponding offset.
+ */
+__isl_give isl_multi_aff *isl_ast_build_get_stride_expansion(
+ __isl_keep isl_ast_build *build)
+{
+ isl_space *space;
+ isl_multi_aff *ma;
+ isl_size pos;
+ isl_aff *aff, *offset;
+ isl_val *stride;
+
+ pos = isl_ast_build_get_depth(build);
+ if (pos < 0)
+ return NULL;
+
+ space = isl_ast_build_get_space(build, 1);
+ space = isl_space_map_from_set(space);
+ ma = isl_multi_aff_identity(space);
+
+ if (!isl_ast_build_has_stride(build, pos))
+ return ma;
+
+ offset = isl_ast_build_get_offset(build, pos);
+ stride = isl_ast_build_get_stride(build, pos);
+ aff = isl_multi_aff_get_aff(ma, pos);
+ aff = isl_aff_scale_val(aff, stride);
+ aff = isl_aff_add(aff, offset);
+ ma = isl_multi_aff_set_aff(ma, pos, aff);
+
+ return ma;
+}
+
+/* Add constraints corresponding to any previously detected
+ * stride on the current dimension to build->domain.
+ */
+__isl_give isl_ast_build *isl_ast_build_include_stride(
+ __isl_take isl_ast_build *build)
+{
+ isl_set *set;
+
+ if (!build)
+ return NULL;
+ if (!isl_ast_build_has_stride(build, build->depth))
+ return build;
+ build = isl_ast_build_cow(build);
+ if (!build)
+ return NULL;
+
+ set = isl_ast_build_get_stride_constraint(build);
+
+ build->domain = isl_set_intersect(build->domain, isl_set_copy(set));
+ build->generated = isl_set_intersect(build->generated, set);
+ if (!build->domain || !build->generated)
+ return isl_ast_build_free(build);
+
+ return build;
+}
+
+/* Check if the constraints in "set" imply any stride on the current
+ * dimension and, if so, record the stride information in "build"
+ * and return the updated "build".
+ *
+ * We assume that inner dimensions have been eliminated from "set"
+ * by the caller. This is needed because the common stride
+ * may be imposed by different inner dimensions on different parts of
+ * the domain.
+ * The assumption ensures that the lower bound does not depend
+ * on inner dimensions.
+ */
+__isl_give isl_ast_build *isl_ast_build_detect_strides(
+ __isl_take isl_ast_build *build, __isl_take isl_set *set)
+{
+ isl_size pos;
+ isl_bool no_stride;
+ isl_val *stride;
+ isl_aff *offset;
+ isl_stride_info *si;
+
+ pos = isl_ast_build_get_depth(build);
+ if (pos < 0)
+ goto error;
+
+ si = isl_set_get_stride_info(set, pos);
+ stride = isl_stride_info_get_stride(si);
+ offset = isl_stride_info_get_offset(si);
+ isl_stride_info_free(si);
+ isl_set_free(set);
+
+ no_stride = isl_val_is_one(stride);
+ if (no_stride >= 0 && !no_stride)
+ return set_stride(build, stride, offset);
+ isl_val_free(stride);
+ isl_aff_free(offset);
+ if (no_stride < 0)
+ return isl_ast_build_free(build);
+ return build;
+error:
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Does "map" not involve the input dimension data->depth?
+ */
+static isl_bool free_of_depth(__isl_keep isl_map *map, void *user)
+{
+ int *depth = user;
+
+ return isl_bool_not(isl_map_involves_dims(map, isl_dim_in, *depth, 1));
+}
+
+/* Do any options depend on the value of the dimension at the current depth?
+ */
+int isl_ast_build_options_involve_depth(__isl_keep isl_ast_build *build)
+{
+ isl_bool free;
+
+ if (!build)
+ return -1;
+
+ free = isl_union_map_every_map(build->options, &free_of_depth,
+ &build->depth);
+ return isl_bool_not(free);
+}
+
+/* Construct the map
+ *
+ * { [i] -> [i] : i < pos; [i] -> [i + 1] : i >= pos }
+ *
+ * with "space" the parameter space of the constructed map.
+ */
+static __isl_give isl_map *construct_insertion_map(__isl_take isl_space *space,
+ int pos)
+{
+ isl_constraint *c;
+ isl_basic_map *bmap1, *bmap2;
+
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, 1);
+ space = isl_space_map_from_set(space);
+ c = isl_constraint_alloc_equality(isl_local_space_from_space(space));
+ c = isl_constraint_set_coefficient_si(c, isl_dim_in, 0, 1);
+ c = isl_constraint_set_coefficient_si(c, isl_dim_out, 0, -1);
+ bmap1 = isl_basic_map_from_constraint(isl_constraint_copy(c));
+ c = isl_constraint_set_constant_si(c, 1);
+ bmap2 = isl_basic_map_from_constraint(c);
+
+ bmap1 = isl_basic_map_upper_bound_si(bmap1, isl_dim_in, 0, pos - 1);
+ bmap2 = isl_basic_map_lower_bound_si(bmap2, isl_dim_in, 0, pos);
+
+ return isl_basic_map_union(bmap1, bmap2);
+}
+
+static const char *option_str[] = {
+ [isl_ast_loop_atomic] = "atomic",
+ [isl_ast_loop_unroll] = "unroll",
+ [isl_ast_loop_separate] = "separate"
+};
+
+/* Update the "options" to reflect the insertion of a dimension
+ * at position "pos" in the schedule domain space.
+ * "space" is the original domain space before the insertion and
+ * may be named and/or structured.
+ *
+ * The (relevant) input options all have "space" as domain, which
+ * has to be mapped to the extended space.
+ * The values of the ranges also refer to the schedule domain positions
+ * and they therefore also need to be adjusted. In particular, values
+ * smaller than pos do not need to change, while values greater than or
+ * equal to pos need to be incremented.
+ * That is, we need to apply the following map.
+ *
+ * { atomic[i] -> atomic[i] : i < pos; [i] -> [i + 1] : i >= pos;
+ * unroll[i] -> unroll[i] : i < pos; [i] -> [i + 1] : i >= pos;
+ * separate[i] -> separate[i] : i < pos; [i] -> [i + 1] : i >= pos;
+ * separation_class[[i] -> [c]]
+ * -> separation_class[[i] -> [c]] : i < pos;
+ * separation_class[[i] -> [c]]
+ * -> separation_class[[i + 1] -> [c]] : i >= pos }
+ */
+static __isl_give isl_union_map *options_insert_dim(
+ __isl_take isl_union_map *options, __isl_take isl_space *space, int pos)
+{
+ isl_map *map;
+ isl_union_map *insertion;
+ enum isl_ast_loop_type type;
+ const char *name = "separation_class";
+
+ space = isl_space_map_from_set(space);
+ map = isl_map_identity(space);
+ map = isl_map_insert_dims(map, isl_dim_out, pos, 1);
+ options = isl_union_map_apply_domain(options,
+ isl_union_map_from_map(map));
+
+ if (!options)
+ return NULL;
+
+ map = construct_insertion_map(isl_union_map_get_space(options), pos);
+
+ insertion = isl_union_map_empty(isl_union_map_get_space(options));
+
+ for (type = isl_ast_loop_atomic;
+ type <= isl_ast_loop_separate; ++type) {
+ isl_map *map_type = isl_map_copy(map);
+ const char *name = option_str[type];
+ map_type = isl_map_set_tuple_name(map_type, isl_dim_in, name);
+ map_type = isl_map_set_tuple_name(map_type, isl_dim_out, name);
+ insertion = isl_union_map_add_map(insertion, map_type);
+ }
+
+ map = isl_map_product(map, isl_map_identity(isl_map_get_space(map)));
+ map = isl_map_set_tuple_name(map, isl_dim_in, name);
+ map = isl_map_set_tuple_name(map, isl_dim_out, name);
+ insertion = isl_union_map_add_map(insertion, map);
+
+ options = isl_union_map_apply_range(options, insertion);
+
+ return options;
+}
+
+/* If we are generating an AST from a schedule tree (build->node is set),
+ * then update the loop AST generation types
+ * to reflect the insertion of a dimension at (global) position "pos"
+ * in the schedule domain space.
+ * We do not need to adjust any isolate option since we would not be inserting
+ * any dimensions if there were any isolate option.
+ */
+static __isl_give isl_ast_build *node_insert_dim(
+ __isl_take isl_ast_build *build, int pos)
+{
+ int i;
+ int local_pos;
+ enum isl_ast_loop_type *loop_type;
+ isl_ctx *ctx;
+
+ build = isl_ast_build_cow(build);
+ if (!build)
+ return NULL;
+ if (!build->node)
+ return build;
+
+ ctx = isl_ast_build_get_ctx(build);
+ local_pos = pos - build->outer_pos;
+ loop_type = isl_realloc_array(ctx, build->loop_type,
+ enum isl_ast_loop_type, build->n + 1);
+ if (!loop_type)
+ return isl_ast_build_free(build);
+ build->loop_type = loop_type;
+ for (i = build->n - 1; i >= local_pos; --i)
+ loop_type[i + 1] = loop_type[i];
+ loop_type[local_pos] = isl_ast_loop_default;
+ build->n++;
+
+ return build;
+}
+
+/* Insert a single dimension in the schedule domain at position "pos".
+ * The new dimension is given an isl_id with the empty string as name.
+ *
+ * The main difficulty is updating build->options to reflect the
+ * extra dimension. This is handled in options_insert_dim.
+ *
+ * Note that because of the dimension manipulations, the resulting
+ * schedule domain space will always be unnamed and unstructured.
+ * However, the original schedule domain space may be named and/or
+ * structured, so we have to take this possibility into account
+ * while performing the transformations.
+ *
+ * Since the inserted schedule dimension is used by the caller
+ * to differentiate between different domain spaces, there is
+ * no longer a uniform mapping from the internal schedule space
+ * to the input schedule space. The internal2input mapping is
+ * therefore removed.
+ */
+__isl_give isl_ast_build *isl_ast_build_insert_dim(
+ __isl_take isl_ast_build *build, int pos)
+{
+ isl_ctx *ctx;
+ isl_space *space, *ma_space;
+ isl_id *id;
+ isl_multi_aff *ma;
+
+ build = isl_ast_build_cow(build);
+ if (!build)
+ return NULL;
+
+ ctx = isl_ast_build_get_ctx(build);
+ id = isl_id_alloc(ctx, "", NULL);
+ if (!build->node)
+ space = isl_ast_build_get_space(build, 1);
+ build->iterators = isl_id_list_insert(build->iterators, pos, id);
+ build->domain = isl_set_insert_dims(build->domain,
+ isl_dim_set, pos, 1);
+ build->generated = isl_set_insert_dims(build->generated,
+ isl_dim_set, pos, 1);
+ build->pending = isl_set_insert_dims(build->pending,
+ isl_dim_set, pos, 1);
+ build->strides = isl_vec_insert_els(build->strides, pos, 1);
+ build->strides = isl_vec_set_element_si(build->strides, pos, 1);
+ ma_space = isl_space_params(isl_multi_aff_get_space(build->offsets));
+ ma_space = isl_space_set_from_params(ma_space);
+ ma_space = isl_space_add_dims(ma_space, isl_dim_set, 1);
+ ma_space = isl_space_map_from_set(ma_space);
+ ma = isl_multi_aff_zero(isl_space_copy(ma_space));
+ build->offsets = isl_multi_aff_splice(build->offsets, pos, pos, ma);
+ ma = isl_multi_aff_identity(ma_space);
+ build->values = isl_multi_aff_splice(build->values, pos, pos, ma);
+ if (!build->node)
+ build->options = options_insert_dim(build->options, space, pos);
+ build->internal2input = isl_multi_aff_free(build->internal2input);
+
+ if (!build->iterators || !build->domain || !build->generated ||
+ !build->pending || !build->values ||
+ !build->strides || !build->offsets || !build->options)
+ return isl_ast_build_free(build);
+
+ build = node_insert_dim(build, pos);
+
+ return build;
+}
+
+/* Scale down the current dimension by a factor of "m".
+ * "umap" is an isl_union_map that implements the scaling down.
+ * That is, it is of the form
+ *
+ * { [.... i ....] -> [.... i' ....] : i = m i' }
+ *
+ * This function is called right after the strides have been
+ * detected, but before any constraints on the current dimension
+ * have been included in build->domain.
+ * We therefore only need to update stride, offset, the options and
+ * the mapping from internal schedule space to the original schedule
+ * space, if we are still keeping track of such a mapping.
+ * The latter mapping is updated by plugging in
+ * { [... i ...] -> [... m i ... ] }.
+ */
+__isl_give isl_ast_build *isl_ast_build_scale_down(
+ __isl_take isl_ast_build *build, __isl_take isl_val *m,
+ __isl_take isl_union_map *umap)
+{
+ isl_aff *aff;
+ isl_val *v;
+ int depth;
+
+ build = isl_ast_build_cow(build);
+ if (!build || !umap || !m)
+ goto error;
+
+ depth = build->depth;
+
+ if (build->internal2input) {
+ isl_space *space;
+ isl_multi_aff *ma;
+ isl_aff *aff;
+
+ space = isl_multi_aff_get_space(build->internal2input);
+ space = isl_space_map_from_set(isl_space_domain(space));
+ ma = isl_multi_aff_identity(space);
+ aff = isl_multi_aff_get_aff(ma, depth);
+ aff = isl_aff_scale_val(aff, isl_val_copy(m));
+ ma = isl_multi_aff_set_aff(ma, depth, aff);
+ build->internal2input =
+ isl_multi_aff_pullback_multi_aff(build->internal2input, ma);
+ if (!build->internal2input)
+ goto error;
+ }
+
+ v = isl_vec_get_element_val(build->strides, depth);
+ v = isl_val_div(v, isl_val_copy(m));
+ build->strides = isl_vec_set_element_val(build->strides, depth, v);
+
+ aff = isl_multi_aff_get_aff(build->offsets, depth);
+ aff = isl_aff_scale_down_val(aff, m);
+ build->offsets = isl_multi_aff_set_aff(build->offsets, depth, aff);
+ build->options = isl_union_map_apply_domain(build->options, umap);
+ if (!build->strides || !build->offsets || !build->options)
+ return isl_ast_build_free(build);
+
+ return build;
+error:
+ isl_val_free(m);
+ isl_union_map_free(umap);
+ return isl_ast_build_free(build);
+}
+
+/* Return a list of "n" isl_ids called "c%d", with "%d" starting at "first".
+ * If an isl_id with such a name already appears among the parameters
+ * in build->domain, then adjust the name to "c%d_%d".
+ */
+static __isl_give isl_id_list *generate_names(isl_ctx *ctx, int n, int first,
+ __isl_keep isl_ast_build *build)
+{
+ int i;
+ isl_id_list *names;
+
+ names = isl_id_list_alloc(ctx, n);
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+
+ id = generate_name(ctx, first + i, build);
+ names = isl_id_list_add(names, id);
+ }
+
+ return names;
+}
+
+/* Embed "options" into the given isl_ast_build space.
+ *
+ * This function is called from within a nested call to
+ * isl_ast_build_node_from_schedule_map.
+ * "options" refers to the additional schedule,
+ * while space refers to both the space of the outer isl_ast_build and
+ * that of the additional schedule.
+ * Specifically, space is of the form
+ *
+ * [I -> S]
+ *
+ * while options lives in the space(s)
+ *
+ * S -> *
+ *
+ * We compute
+ *
+ * [I -> S] -> S
+ *
+ * and compose this with options, to obtain the new options
+ * living in the space(s)
+ *
+ * [I -> S] -> *
+ */
+static __isl_give isl_union_map *embed_options(
+ __isl_take isl_union_map *options, __isl_take isl_space *space)
+{
+ isl_map *map;
+
+ map = isl_map_universe(isl_space_unwrap(space));
+ map = isl_map_range_map(map);
+
+ options = isl_union_map_apply_range(
+ isl_union_map_from_map(map), options);
+
+ return options;
+}
+
+/* Update "build" for use in a (possibly nested) code generation. That is,
+ * extend "build" from an AST build on some domain O to an AST build
+ * on domain [O -> S], with S corresponding to "space".
+ * If the original domain is a parameter domain, then the new domain is
+ * simply S.
+ * "iterators" is a list of iterators for S, but the number of elements
+ * may be smaller or greater than the number of set dimensions of S.
+ * If "keep_iterators" is set, then any extra ids in build->iterators
+ * are reused for S. Otherwise, these extra ids are dropped.
+ *
+ * We first update build->outer_pos to the current depth.
+ * This depth is zero in case this is the outermost code generation.
+ *
+ * We then add additional ids such that the number of iterators is at least
+ * equal to the dimension of the new build domain.
+ *
+ * If the original domain is parametric, then we are constructing
+ * an isl_ast_build for the outer code generation and we pass control
+ * to isl_ast_build_init.
+ *
+ * Otherwise, we adjust the fields of "build" to include "space".
+ */
+__isl_give isl_ast_build *isl_ast_build_product(
+ __isl_take isl_ast_build *build, __isl_take isl_space *space)
+{
+ isl_ctx *ctx;
+ isl_vec *strides;
+ isl_set *set;
+ isl_multi_aff *embedding;
+ isl_size dim, space_dim, n_it;
+
+ build = isl_ast_build_cow(build);
+ if (!build)
+ goto error;
+
+ build->outer_pos = build->depth;
+
+ ctx = isl_ast_build_get_ctx(build);
+ dim = isl_ast_build_dim(build, isl_dim_set);
+ space_dim = isl_space_dim(space, isl_dim_set);
+ n_it = isl_id_list_n_id(build->iterators);
+ if (dim < 0 || space_dim < 0 || n_it < 0)
+ goto error;
+ dim += space_dim;
+ if (n_it < dim) {
+ isl_id_list *l;
+ l = generate_names(ctx, dim - n_it, n_it, build);
+ build->iterators = isl_id_list_concat(build->iterators, l);
+ }
+
+ if (isl_set_is_params(build->domain))
+ return isl_ast_build_init(build, space);
+
+ set = isl_set_universe(isl_space_copy(space));
+ build->domain = isl_set_product(build->domain, isl_set_copy(set));
+ build->pending = isl_set_product(build->pending, isl_set_copy(set));
+ build->generated = isl_set_product(build->generated, set);
+
+ strides = isl_vec_alloc(ctx, space_dim);
+ strides = isl_vec_set_si(strides, 1);
+ build->strides = isl_vec_concat(build->strides, strides);
+
+ space = isl_space_map_from_set(space);
+ build->offsets = isl_multi_aff_align_params(build->offsets,
+ isl_space_copy(space));
+ build->offsets = isl_multi_aff_product(build->offsets,
+ isl_multi_aff_zero(isl_space_copy(space)));
+ build->values = isl_multi_aff_align_params(build->values,
+ isl_space_copy(space));
+ embedding = isl_multi_aff_identity(space);
+ build->values = isl_multi_aff_product(build->values,
+ isl_multi_aff_copy(embedding));
+ if (build->internal2input) {
+ build->internal2input =
+ isl_multi_aff_product(build->internal2input, embedding);
+ build->internal2input =
+ isl_multi_aff_flatten_range(build->internal2input);
+ if (!build->internal2input)
+ return isl_ast_build_free(build);
+ } else {
+ isl_multi_aff_free(embedding);
+ }
+
+ space = isl_ast_build_get_space(build, 1);
+ build->options = embed_options(build->options, space);
+
+ if (!build->iterators || !build->domain || !build->generated ||
+ !build->pending || !build->values ||
+ !build->strides || !build->offsets || !build->options)
+ return isl_ast_build_free(build);
+
+ return build;
+error:
+ isl_ast_build_free(build);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Does "aff" only attain non-negative values over build->domain?
+ * That is, does it not attain any negative values?
+ */
+int isl_ast_build_aff_is_nonneg(__isl_keep isl_ast_build *build,
+ __isl_keep isl_aff *aff)
+{
+ isl_set *test;
+ int empty;
+
+ if (!build)
+ return -1;
+
+ aff = isl_aff_copy(aff);
+ test = isl_set_from_basic_set(isl_aff_neg_basic_set(aff));
+ test = isl_set_intersect(test, isl_set_copy(build->domain));
+ empty = isl_set_is_empty(test);
+ isl_set_free(test);
+
+ return empty;
+}
+
+/* Does the dimension at (internal) position "pos" have a non-trivial stride?
+ */
+isl_bool isl_ast_build_has_stride(__isl_keep isl_ast_build *build, int pos)
+{
+ isl_val *v;
+ isl_bool has_stride;
+
+ if (!build)
+ return isl_bool_error;
+
+ v = isl_vec_get_element_val(build->strides, pos);
+ has_stride = isl_bool_not(isl_val_is_one(v));
+ isl_val_free(v);
+
+ return has_stride;
+}
+
+/* Given that the dimension at position "pos" takes on values
+ *
+ * f + s a
+ *
+ * with a an integer, return s.
+ */
+__isl_give isl_val *isl_ast_build_get_stride(__isl_keep isl_ast_build *build,
+ int pos)
+{
+ if (!build)
+ return NULL;
+
+ return isl_vec_get_element_val(build->strides, pos);
+}
+
+/* Given that the dimension at position "pos" takes on values
+ *
+ * f + s a
+ *
+ * with a an integer, return f.
+ */
+__isl_give isl_aff *isl_ast_build_get_offset(
+ __isl_keep isl_ast_build *build, int pos)
+{
+ if (!build)
+ return NULL;
+
+ return isl_multi_aff_get_aff(build->offsets, pos);
+}
+
+/* Is the dimension at position "pos" known to attain only a single
+ * value that, moreover, can be described by a single affine expression
+ * in terms of the outer dimensions and parameters?
+ *
+ * If not, then the corresponding affine expression in build->values
+ * is set to be equal to the same input dimension.
+ * Otherwise, it is set to the requested expression in terms of
+ * outer dimensions and parameters.
+ */
+isl_bool isl_ast_build_has_affine_value(__isl_keep isl_ast_build *build,
+ int pos)
+{
+ isl_aff *aff;
+ isl_bool involves;
+
+ if (!build)
+ return isl_bool_error;
+
+ aff = isl_multi_aff_get_aff(build->values, pos);
+ involves = isl_aff_involves_dims(aff, isl_dim_in, pos, 1);
+ isl_aff_free(aff);
+
+ return isl_bool_not(involves);
+}
+
+/* Plug in the known values (fixed affine expressions in terms of
+ * parameters and outer loop iterators) of all loop iterators
+ * in the domain of "umap".
+ *
+ * We simply precompose "umap" with build->values.
+ */
+__isl_give isl_union_map *isl_ast_build_substitute_values_union_map_domain(
+ __isl_keep isl_ast_build *build, __isl_take isl_union_map *umap)
+{
+ isl_multi_aff *values;
+
+ if (!build)
+ return isl_union_map_free(umap);
+
+ values = isl_multi_aff_copy(build->values);
+ umap = isl_union_map_preimage_domain_multi_aff(umap, values);
+
+ return umap;
+}
+
+/* Is the current dimension known to attain only a single value?
+ */
+int isl_ast_build_has_value(__isl_keep isl_ast_build *build)
+{
+ if (!build)
+ return -1;
+
+ return build->value != NULL;
+}
+
+/* Simplify the basic set "bset" based on what we know about
+ * the iterators of already generated loops.
+ *
+ * "bset" is assumed to live in the (internal) schedule domain.
+ */
+__isl_give isl_basic_set *isl_ast_build_compute_gist_basic_set(
+ __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset)
+{
+ if (!build)
+ goto error;
+
+ bset = isl_basic_set_preimage_multi_aff(bset,
+ isl_multi_aff_copy(build->values));
+ bset = isl_basic_set_gist(bset,
+ isl_set_simple_hull(isl_set_copy(build->domain)));
+
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Simplify the set "set" based on what we know about
+ * the iterators of already generated loops.
+ *
+ * "set" is assumed to live in the (internal) schedule domain.
+ */
+__isl_give isl_set *isl_ast_build_compute_gist(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set)
+{
+ if (!build)
+ goto error;
+
+ if (!isl_set_is_params(set))
+ set = isl_set_preimage_multi_aff(set,
+ isl_multi_aff_copy(build->values));
+ set = isl_set_gist(set, isl_set_copy(build->domain));
+
+ return set;
+error:
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Include information about what we know about the iterators of
+ * already generated loops to "set".
+ *
+ * We currently only plug in the known affine values of outer loop
+ * iterators.
+ * In principle we could also introduce equalities or even other
+ * constraints implied by the intersection of "set" and build->domain.
+ */
+__isl_give isl_set *isl_ast_build_specialize(__isl_keep isl_ast_build *build,
+ __isl_take isl_set *set)
+{
+ if (!build)
+ return isl_set_free(set);
+
+ return isl_set_preimage_multi_aff(set,
+ isl_multi_aff_copy(build->values));
+}
+
+/* Plug in the known affine values of outer loop iterators in "bset".
+ */
+__isl_give isl_basic_set *isl_ast_build_specialize_basic_set(
+ __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset)
+{
+ if (!build)
+ return isl_basic_set_free(bset);
+
+ return isl_basic_set_preimage_multi_aff(bset,
+ isl_multi_aff_copy(build->values));
+}
+
+/* Simplify the map "map" based on what we know about
+ * the iterators of already generated loops.
+ *
+ * The domain of "map" is assumed to live in the (internal) schedule domain.
+ */
+__isl_give isl_map *isl_ast_build_compute_gist_map_domain(
+ __isl_keep isl_ast_build *build, __isl_take isl_map *map)
+{
+ if (!build)
+ goto error;
+
+ map = isl_map_gist_domain(map, isl_set_copy(build->domain));
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Simplify the affine expression "aff" based on what we know about
+ * the iterators of already generated loops.
+ *
+ * The domain of "aff" is assumed to live in the (internal) schedule domain.
+ */
+__isl_give isl_aff *isl_ast_build_compute_gist_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_aff *aff)
+{
+ if (!build)
+ goto error;
+
+ aff = isl_aff_gist(aff, isl_set_copy(build->domain));
+
+ return aff;
+error:
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Simplify the piecewise affine expression "aff" based on what we know about
+ * the iterators of already generated loops.
+ *
+ * The domain of "pa" is assumed to live in the (internal) schedule domain.
+ */
+__isl_give isl_pw_aff *isl_ast_build_compute_gist_pw_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa)
+{
+ if (!build)
+ goto error;
+
+ if (!isl_set_is_params(build->domain))
+ pa = isl_pw_aff_pullback_multi_aff(pa,
+ isl_multi_aff_copy(build->values));
+ pa = isl_pw_aff_gist(pa, isl_set_copy(build->domain));
+
+ return pa;
+error:
+ isl_pw_aff_free(pa);
+ return NULL;
+}
+
+/* Simplify the piecewise multi-affine expression "aff" based on what
+ * we know about the iterators of already generated loops.
+ *
+ * The domain of "pma" is assumed to live in the (internal) schedule domain.
+ */
+__isl_give isl_pw_multi_aff *isl_ast_build_compute_gist_pw_multi_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma)
+{
+ if (!build)
+ goto error;
+
+ pma = isl_pw_multi_aff_pullback_multi_aff(pma,
+ isl_multi_aff_copy(build->values));
+ pma = isl_pw_multi_aff_gist(pma, isl_set_copy(build->domain));
+
+ return pma;
+error:
+ isl_pw_multi_aff_free(pma);
+ return NULL;
+}
+
+/* Extract the schedule domain of the given type from build->options
+ * at the current depth.
+ *
+ * In particular, find the subset of build->options that is of
+ * the following form
+ *
+ * schedule_domain -> type[depth]
+ *
+ * and return the corresponding domain, after eliminating inner dimensions
+ * and divs that depend on the current dimension.
+ *
+ * Note that the domain of build->options has been reformulated
+ * in terms of the internal build space in embed_options,
+ * but the position is still that within the current code generation.
+ */
+__isl_give isl_set *isl_ast_build_get_option_domain(
+ __isl_keep isl_ast_build *build, enum isl_ast_loop_type type)
+{
+ const char *name;
+ isl_space *space;
+ isl_map *option;
+ isl_set *domain;
+ int local_pos;
+
+ if (!build)
+ return NULL;
+
+ name = option_str[type];
+ local_pos = build->depth - build->outer_pos;
+
+ space = isl_ast_build_get_space(build, 1);
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, 1);
+ space = isl_space_set_tuple_name(space, isl_dim_out, name);
+
+ option = isl_union_map_extract_map(build->options, space);
+ option = isl_map_fix_si(option, isl_dim_out, 0, local_pos);
+
+ domain = isl_map_domain(option);
+ domain = isl_ast_build_eliminate(build, domain);
+
+ return domain;
+}
+
+/* How does the user want the current schedule dimension to be generated?
+ * These choices have been extracted from the schedule node
+ * in extract_loop_types and stored in build->loop_type.
+ * They have been updated to reflect any dimension insertion in
+ * node_insert_dim.
+ * Return isl_ast_domain_error on error.
+ *
+ * If "isolated" is set, then we get the loop AST generation type
+ * directly from the band node since node_insert_dim cannot have been
+ * called on a band with the isolate option.
+ */
+enum isl_ast_loop_type isl_ast_build_get_loop_type(
+ __isl_keep isl_ast_build *build, int isolated)
+{
+ int local_pos;
+ isl_ctx *ctx;
+
+ if (!build)
+ return isl_ast_loop_error;
+ ctx = isl_ast_build_get_ctx(build);
+ if (!build->node)
+ isl_die(ctx, isl_error_internal,
+ "only works for schedule tree based AST generation",
+ return isl_ast_loop_error);
+
+ local_pos = build->depth - build->outer_pos;
+ if (!isolated)
+ return build->loop_type[local_pos];
+ return isl_schedule_node_band_member_get_isolate_ast_loop_type(
+ build->node, local_pos);
+}
+
+/* Extract the isolated set from the isolate option, if any,
+ * and store in the build.
+ * If there is no isolate option, then the isolated set is
+ * set to the empty set.
+ *
+ * The isolate option is of the form
+ *
+ * isolate[[outer bands] -> current_band]
+ *
+ * We flatten this set and then map it back to the internal
+ * schedule space.
+ *
+ * If we have already extracted the isolated set
+ * or if internal2input is no longer set, then we do not
+ * need to do anything. In the latter case, we know
+ * that the current band cannot have any isolate option.
+ */
+__isl_give isl_ast_build *isl_ast_build_extract_isolated(
+ __isl_take isl_ast_build *build)
+{
+ isl_set *isolated;
+
+ if (!build)
+ return NULL;
+ if (!build->internal2input)
+ return build;
+ if (build->isolated)
+ return build;
+
+ build = isl_ast_build_cow(build);
+ if (!build)
+ return NULL;
+
+ isolated = isl_schedule_node_band_get_ast_isolate_option(build->node);
+ isolated = isl_set_flatten(isolated);
+ isolated = isl_set_preimage_multi_aff(isolated,
+ isl_multi_aff_copy(build->internal2input));
+
+ build->isolated = isolated;
+ if (!build->isolated)
+ return isl_ast_build_free(build);
+
+ return build;
+}
+
+/* Does "build" have a non-empty isolated set?
+ *
+ * The caller is assumed to have called isl_ast_build_extract_isolated first.
+ */
+int isl_ast_build_has_isolated(__isl_keep isl_ast_build *build)
+{
+ int empty;
+
+ if (!build)
+ return -1;
+ if (!build->internal2input)
+ return 0;
+ if (!build->isolated)
+ isl_die(isl_ast_build_get_ctx(build), isl_error_internal,
+ "isolated set not extracted yet", return -1);
+
+ empty = isl_set_plain_is_empty(build->isolated);
+ return empty < 0 ? -1 : !empty;
+}
+
+/* Return a copy of the isolated set of "build".
+ *
+ * The caller is assume to have called isl_ast_build_has_isolated first,
+ * with this function returning true.
+ * In particular, this function should not be called if we are no
+ * longer keeping track of internal2input (and there therefore could
+ * not possibly be any isolated set).
+ */
+__isl_give isl_set *isl_ast_build_get_isolated(__isl_keep isl_ast_build *build)
+{
+ if (!build)
+ return NULL;
+ if (!build->internal2input)
+ isl_die(isl_ast_build_get_ctx(build), isl_error_internal,
+ "build cannot have isolated set", return NULL);
+
+ return isl_set_copy(build->isolated);
+}
+
+/* Extract the separation class mapping at the current depth.
+ *
+ * In particular, find and return the subset of build->options that is of
+ * the following form
+ *
+ * schedule_domain -> separation_class[[depth] -> [class]]
+ *
+ * The caller is expected to eliminate inner dimensions from the domain.
+ *
+ * Note that the domain of build->options has been reformulated
+ * in terms of the internal build space in embed_options,
+ * but the position is still that within the current code generation.
+ */
+__isl_give isl_map *isl_ast_build_get_separation_class(
+ __isl_keep isl_ast_build *build)
+{
+ isl_ctx *ctx;
+ isl_space *space_sep, *space;
+ isl_map *res;
+ int local_pos;
+
+ if (!build)
+ return NULL;
+
+ local_pos = build->depth - build->outer_pos;
+ ctx = isl_ast_build_get_ctx(build);
+ space_sep = isl_space_alloc(ctx, 0, 1, 1);
+ space_sep = isl_space_wrap(space_sep);
+ space_sep = isl_space_set_tuple_name(space_sep, isl_dim_set,
+ "separation_class");
+ space = isl_ast_build_get_space(build, 1);
+ space_sep = isl_space_align_params(space_sep, isl_space_copy(space));
+ space = isl_space_map_from_domain_and_range(space, space_sep);
+
+ res = isl_union_map_extract_map(build->options, space);
+ res = isl_map_fix_si(res, isl_dim_out, 0, local_pos);
+ res = isl_map_coalesce(res);
+
+ return res;
+}
+
+/* Eliminate dimensions inner to the current dimension.
+ */
+__isl_give isl_set *isl_ast_build_eliminate_inner(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set)
+{
+ int dim;
+ int depth;
+
+ if (!build)
+ return isl_set_free(set);
+
+ dim = isl_set_dim(set, isl_dim_set);
+ depth = build->depth;
+ set = isl_set_detect_equalities(set);
+ set = isl_set_eliminate(set, isl_dim_set, depth + 1, dim - (depth + 1));
+
+ return set;
+}
+
+/* Eliminate unknown divs and divs that depend on the current dimension.
+ *
+ * Note that during the elimination of unknown divs, we may discover
+ * an explicit representation of some other unknown divs, which may
+ * depend on the current dimension. We therefore need to eliminate
+ * unknown divs first.
+ */
+__isl_give isl_set *isl_ast_build_eliminate_divs(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set)
+{
+ int depth;
+
+ if (!build)
+ return isl_set_free(set);
+
+ set = isl_set_remove_unknown_divs(set);
+ depth = build->depth;
+ set = isl_set_remove_divs_involving_dims(set, isl_dim_set, depth, 1);
+
+ return set;
+}
+
+/* Eliminate dimensions inner to the current dimension as well as
+ * unknown divs and divs that depend on the current dimension.
+ * The result then consists only of constraints that are independent
+ * of the current dimension and upper and lower bounds on the current
+ * dimension.
+ */
+__isl_give isl_set *isl_ast_build_eliminate(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *domain)
+{
+ domain = isl_ast_build_eliminate_inner(build, domain);
+ domain = isl_ast_build_eliminate_divs(build, domain);
+ return domain;
+}
+
+/* Replace build->single_valued by "sv".
+ */
+__isl_give isl_ast_build *isl_ast_build_set_single_valued(
+ __isl_take isl_ast_build *build, int sv)
+{
+ if (!build)
+ return build;
+ if (build->single_valued == sv)
+ return build;
+ build = isl_ast_build_cow(build);
+ if (!build)
+ return build;
+ build->single_valued = sv;
+
+ return build;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_expr.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_expr.c
new file mode 100644
index 00000000000..3e35416f725
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_expr.c
@@ -0,0 +1,2611 @@
+/*
+ * Copyright 2012-2014 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#include <isl/id.h>
+#include <isl/space.h>
+#include <isl/constraint.h>
+#include <isl/ilp.h>
+#include <isl/val.h>
+#include <isl_ast_build_expr.h>
+#include <isl_ast_private.h>
+#include <isl_ast_build_private.h>
+#include <isl_sort.h>
+
+/* Compute the "opposite" of the (numerator of the) argument of a div
+ * with denominator "d".
+ *
+ * In particular, compute
+ *
+ * -aff + (d - 1)
+ */
+static __isl_give isl_aff *oppose_div_arg(__isl_take isl_aff *aff,
+ __isl_take isl_val *d)
+{
+ aff = isl_aff_neg(aff);
+ aff = isl_aff_add_constant_val(aff, d);
+ aff = isl_aff_add_constant_si(aff, -1);
+
+ return aff;
+}
+
+/* Internal data structure used inside isl_ast_expr_add_term.
+ * The domain of "build" is used to simplify the expressions.
+ * "build" needs to be set by the caller of isl_ast_expr_add_term.
+ * "cst" is the constant term of the expression in which the added term
+ * appears. It may be modified by isl_ast_expr_add_term.
+ *
+ * "v" is the coefficient of the term that is being constructed and
+ * is set internally by isl_ast_expr_add_term.
+ */
+struct isl_ast_add_term_data {
+ isl_ast_build *build;
+ isl_val *cst;
+ isl_val *v;
+};
+
+/* Given the numerator "aff" of the argument of an integer division
+ * with denominator "d", check if it can be made non-negative over
+ * data->build->domain by stealing part of the constant term of
+ * the expression in which the integer division appears.
+ *
+ * In particular, the outer expression is of the form
+ *
+ * v * floor(aff/d) + cst
+ *
+ * We already know that "aff" itself may attain negative values.
+ * Here we check if aff + d*floor(cst/v) is non-negative, such
+ * that we could rewrite the expression to
+ *
+ * v * floor((aff + d*floor(cst/v))/d) + cst - v*floor(cst/v)
+ *
+ * Note that aff + d*floor(cst/v) can only possibly be non-negative
+ * if data->cst and data->v have the same sign.
+ * Similarly, if floor(cst/v) is zero, then there is no point in
+ * checking again.
+ */
+static int is_non_neg_after_stealing(__isl_keep isl_aff *aff,
+ __isl_keep isl_val *d, struct isl_ast_add_term_data *data)
+{
+ isl_aff *shifted;
+ isl_val *shift;
+ int is_zero;
+ int non_neg;
+
+ if (isl_val_sgn(data->cst) != isl_val_sgn(data->v))
+ return 0;
+
+ shift = isl_val_div(isl_val_copy(data->cst), isl_val_copy(data->v));
+ shift = isl_val_floor(shift);
+ is_zero = isl_val_is_zero(shift);
+ if (is_zero < 0 || is_zero) {
+ isl_val_free(shift);
+ return is_zero < 0 ? -1 : 0;
+ }
+ shift = isl_val_mul(shift, isl_val_copy(d));
+ shifted = isl_aff_copy(aff);
+ shifted = isl_aff_add_constant_val(shifted, shift);
+ non_neg = isl_ast_build_aff_is_nonneg(data->build, shifted);
+ isl_aff_free(shifted);
+
+ return non_neg;
+}
+
+/* Given the numerator "aff' of the argument of an integer division
+ * with denominator "d", steal part of the constant term of
+ * the expression in which the integer division appears to make it
+ * non-negative over data->build->domain.
+ *
+ * In particular, the outer expression is of the form
+ *
+ * v * floor(aff/d) + cst
+ *
+ * We know that "aff" itself may attain negative values,
+ * but that aff + d*floor(cst/v) is non-negative.
+ * Find the minimal positive value that we need to add to "aff"
+ * to make it positive and adjust data->cst accordingly.
+ * That is, compute the minimal value "m" of "aff" over
+ * data->build->domain and take
+ *
+ * s = ceil(m/d)
+ *
+ * such that
+ *
+ * aff + d * s >= 0
+ *
+ * and rewrite the expression to
+ *
+ * v * floor((aff + s*d)/d) + (cst - v*s)
+ */
+static __isl_give isl_aff *steal_from_cst(__isl_take isl_aff *aff,
+ __isl_keep isl_val *d, struct isl_ast_add_term_data *data)
+{
+ isl_set *domain;
+ isl_val *shift, *t;
+
+ domain = isl_ast_build_get_domain(data->build);
+ shift = isl_set_min_val(domain, aff);
+ isl_set_free(domain);
+
+ shift = isl_val_neg(shift);
+ shift = isl_val_div(shift, isl_val_copy(d));
+ shift = isl_val_ceil(shift);
+
+ t = isl_val_copy(shift);
+ t = isl_val_mul(t, isl_val_copy(data->v));
+ data->cst = isl_val_sub(data->cst, t);
+
+ shift = isl_val_mul(shift, isl_val_copy(d));
+ return isl_aff_add_constant_val(aff, shift);
+}
+
+/* Create an isl_ast_expr evaluating the div at position "pos" in "ls".
+ * The result is simplified in terms of data->build->domain.
+ * This function may change (the sign of) data->v.
+ *
+ * "ls" is known to be non-NULL.
+ *
+ * Let the div be of the form floor(e/d).
+ * If the ast_build_prefer_pdiv option is set then we check if "e"
+ * is non-negative, so that we can generate
+ *
+ * (pdiv_q, expr(e), expr(d))
+ *
+ * instead of
+ *
+ * (fdiv_q, expr(e), expr(d))
+ *
+ * If the ast_build_prefer_pdiv option is set and
+ * if "e" is not non-negative, then we check if "-e + d - 1" is non-negative.
+ * If so, we can rewrite
+ *
+ * floor(e/d) = -ceil(-e/d) = -floor((-e + d - 1)/d)
+ *
+ * and still use pdiv_q, while changing the sign of data->v.
+ *
+ * Otherwise, we check if
+ *
+ * e + d*floor(cst/v)
+ *
+ * is non-negative and if so, replace floor(e/d) by
+ *
+ * floor((e + s*d)/d) - s
+ *
+ * with s the minimal shift that makes the argument non-negative.
+ */
+static __isl_give isl_ast_expr *var_div(struct isl_ast_add_term_data *data,
+ __isl_keep isl_local_space *ls, int pos)
+{
+ isl_ctx *ctx = isl_local_space_get_ctx(ls);
+ isl_aff *aff;
+ isl_ast_expr *num, *den;
+ isl_val *d;
+ enum isl_ast_expr_op_type type;
+
+ aff = isl_local_space_get_div(ls, pos);
+ d = isl_aff_get_denominator_val(aff);
+ aff = isl_aff_scale_val(aff, isl_val_copy(d));
+ den = isl_ast_expr_from_val(isl_val_copy(d));
+
+ type = isl_ast_expr_op_fdiv_q;
+ if (isl_options_get_ast_build_prefer_pdiv(ctx)) {
+ int non_neg = isl_ast_build_aff_is_nonneg(data->build, aff);
+ if (non_neg >= 0 && !non_neg) {
+ isl_aff *opp = oppose_div_arg(isl_aff_copy(aff),
+ isl_val_copy(d));
+ non_neg = isl_ast_build_aff_is_nonneg(data->build, opp);
+ if (non_neg >= 0 && non_neg) {
+ data->v = isl_val_neg(data->v);
+ isl_aff_free(aff);
+ aff = opp;
+ } else
+ isl_aff_free(opp);
+ }
+ if (non_neg >= 0 && !non_neg) {
+ non_neg = is_non_neg_after_stealing(aff, d, data);
+ if (non_neg >= 0 && non_neg)
+ aff = steal_from_cst(aff, d, data);
+ }
+ if (non_neg < 0)
+ aff = isl_aff_free(aff);
+ else if (non_neg)
+ type = isl_ast_expr_op_pdiv_q;
+ }
+
+ isl_val_free(d);
+ num = isl_ast_expr_from_aff(aff, data->build);
+ return isl_ast_expr_alloc_binary(type, num, den);
+}
+
+/* Create an isl_ast_expr evaluating the specified dimension of "ls".
+ * The result is simplified in terms of data->build->domain.
+ * This function may change (the sign of) data->v.
+ *
+ * The isl_ast_expr is constructed based on the type of the dimension.
+ * - divs are constructed by var_div
+ * - set variables are constructed from the iterator isl_ids in data->build
+ * - parameters are constructed from the isl_ids in "ls"
+ */
+static __isl_give isl_ast_expr *var(struct isl_ast_add_term_data *data,
+ __isl_keep isl_local_space *ls, enum isl_dim_type type, int pos)
+{
+ isl_ctx *ctx = isl_local_space_get_ctx(ls);
+ isl_id *id;
+
+ if (type == isl_dim_div)
+ return var_div(data, ls, pos);
+
+ if (type == isl_dim_set) {
+ id = isl_ast_build_get_iterator_id(data->build, pos);
+ return isl_ast_expr_from_id(id);
+ }
+
+ if (!isl_local_space_has_dim_id(ls, type, pos))
+ isl_die(ctx, isl_error_internal, "unnamed dimension",
+ return NULL);
+ id = isl_local_space_get_dim_id(ls, type, pos);
+ return isl_ast_expr_from_id(id);
+}
+
+/* Does "expr" represent the zero integer?
+ */
+static int ast_expr_is_zero(__isl_keep isl_ast_expr *expr)
+{
+ if (!expr)
+ return -1;
+ if (expr->type != isl_ast_expr_int)
+ return 0;
+ return isl_val_is_zero(expr->u.v);
+}
+
+/* Create an expression representing the sum of "expr1" and "expr2",
+ * provided neither of the two expressions is identically zero.
+ */
+static __isl_give isl_ast_expr *ast_expr_add(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ if (!expr1 || !expr2)
+ goto error;
+
+ if (ast_expr_is_zero(expr1)) {
+ isl_ast_expr_free(expr1);
+ return expr2;
+ }
+
+ if (ast_expr_is_zero(expr2)) {
+ isl_ast_expr_free(expr2);
+ return expr1;
+ }
+
+ return isl_ast_expr_add(expr1, expr2);
+error:
+ isl_ast_expr_free(expr1);
+ isl_ast_expr_free(expr2);
+ return NULL;
+}
+
+/* Subtract expr2 from expr1.
+ *
+ * If expr2 is zero, we simply return expr1.
+ * If expr1 is zero, we return
+ *
+ * (isl_ast_expr_op_minus, expr2)
+ *
+ * Otherwise, we return
+ *
+ * (isl_ast_expr_op_sub, expr1, expr2)
+ */
+static __isl_give isl_ast_expr *ast_expr_sub(__isl_take isl_ast_expr *expr1,
+ __isl_take isl_ast_expr *expr2)
+{
+ if (!expr1 || !expr2)
+ goto error;
+
+ if (ast_expr_is_zero(expr2)) {
+ isl_ast_expr_free(expr2);
+ return expr1;
+ }
+
+ if (ast_expr_is_zero(expr1)) {
+ isl_ast_expr_free(expr1);
+ return isl_ast_expr_neg(expr2);
+ }
+
+ return isl_ast_expr_sub(expr1, expr2);
+error:
+ isl_ast_expr_free(expr1);
+ isl_ast_expr_free(expr2);
+ return NULL;
+}
+
+/* Return an isl_ast_expr that represents
+ *
+ * v * (aff mod d)
+ *
+ * v is assumed to be non-negative.
+ * The result is simplified in terms of build->domain.
+ */
+static __isl_give isl_ast_expr *isl_ast_expr_mod(__isl_keep isl_val *v,
+ __isl_keep isl_aff *aff, __isl_keep isl_val *d,
+ __isl_keep isl_ast_build *build)
+{
+ isl_ast_expr *expr;
+ isl_ast_expr *c;
+
+ if (!aff)
+ return NULL;
+
+ expr = isl_ast_expr_from_aff(isl_aff_copy(aff), build);
+
+ c = isl_ast_expr_from_val(isl_val_copy(d));
+ expr = isl_ast_expr_alloc_binary(isl_ast_expr_op_pdiv_r, expr, c);
+
+ if (!isl_val_is_one(v)) {
+ c = isl_ast_expr_from_val(isl_val_copy(v));
+ expr = isl_ast_expr_mul(c, expr);
+ }
+
+ return expr;
+}
+
+/* Create an isl_ast_expr that scales "expr" by "v".
+ *
+ * If v is 1, we simply return expr.
+ * If v is -1, we return
+ *
+ * (isl_ast_expr_op_minus, expr)
+ *
+ * Otherwise, we return
+ *
+ * (isl_ast_expr_op_mul, expr(v), expr)
+ */
+static __isl_give isl_ast_expr *scale(__isl_take isl_ast_expr *expr,
+ __isl_take isl_val *v)
+{
+ isl_ast_expr *c;
+
+ if (!expr || !v)
+ goto error;
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return expr;
+ }
+
+ if (isl_val_is_negone(v)) {
+ isl_val_free(v);
+ expr = isl_ast_expr_neg(expr);
+ } else {
+ c = isl_ast_expr_from_val(v);
+ expr = isl_ast_expr_mul(c, expr);
+ }
+
+ return expr;
+error:
+ isl_val_free(v);
+ isl_ast_expr_free(expr);
+ return NULL;
+}
+
+/* Add an expression for "*v" times the specified dimension of "ls"
+ * to expr.
+ * If the dimension is an integer division, then this function
+ * may modify data->cst in order to make the numerator non-negative.
+ * The result is simplified in terms of data->build->domain.
+ *
+ * Let e be the expression for the specified dimension,
+ * multiplied by the absolute value of "*v".
+ * If "*v" is negative, we create
+ *
+ * (isl_ast_expr_op_sub, expr, e)
+ *
+ * except when expr is trivially zero, in which case we create
+ *
+ * (isl_ast_expr_op_minus, e)
+ *
+ * instead.
+ *
+ * If "*v" is positive, we simply create
+ *
+ * (isl_ast_expr_op_add, expr, e)
+ *
+ */
+static __isl_give isl_ast_expr *isl_ast_expr_add_term(
+ __isl_take isl_ast_expr *expr,
+ __isl_keep isl_local_space *ls, enum isl_dim_type type, int pos,
+ __isl_take isl_val *v, struct isl_ast_add_term_data *data)
+{
+ isl_ast_expr *term;
+
+ if (!expr)
+ return NULL;
+
+ data->v = v;
+ term = var(data, ls, type, pos);
+ v = data->v;
+
+ if (isl_val_is_neg(v) && !ast_expr_is_zero(expr)) {
+ v = isl_val_neg(v);
+ term = scale(term, v);
+ return ast_expr_sub(expr, term);
+ } else {
+ term = scale(term, v);
+ return ast_expr_add(expr, term);
+ }
+}
+
+/* Add an expression for "v" to expr.
+ */
+static __isl_give isl_ast_expr *isl_ast_expr_add_int(
+ __isl_take isl_ast_expr *expr, __isl_take isl_val *v)
+{
+ isl_ast_expr *expr_int;
+
+ if (!expr || !v)
+ goto error;
+
+ if (isl_val_is_zero(v)) {
+ isl_val_free(v);
+ return expr;
+ }
+
+ if (isl_val_is_neg(v) && !ast_expr_is_zero(expr)) {
+ v = isl_val_neg(v);
+ expr_int = isl_ast_expr_from_val(v);
+ return ast_expr_sub(expr, expr_int);
+ } else {
+ expr_int = isl_ast_expr_from_val(v);
+ return ast_expr_add(expr, expr_int);
+ }
+error:
+ isl_ast_expr_free(expr);
+ isl_val_free(v);
+ return NULL;
+}
+
+/* Internal data structure used inside extract_modulos.
+ *
+ * If any modulo expressions are detected in "aff", then the
+ * expression is removed from "aff" and added to either "pos" or "neg"
+ * depending on the sign of the coefficient of the modulo expression
+ * inside "aff".
+ *
+ * "add" is an expression that needs to be added to "aff" at the end of
+ * the computation. It is NULL as long as no modulos have been extracted.
+ *
+ * "i" is the position in "aff" of the div under investigation
+ * "v" is the coefficient in "aff" of the div
+ * "div" is the argument of the div, with the denominator removed
+ * "d" is the original denominator of the argument of the div
+ *
+ * "nonneg" is an affine expression that is non-negative over "build"
+ * and that can be used to extract a modulo expression from "div".
+ * In particular, if "sign" is 1, then the coefficients of "nonneg"
+ * are equal to those of "div" modulo "d". If "sign" is -1, then
+ * the coefficients of "nonneg" are opposite to those of "div" modulo "d".
+ * If "sign" is 0, then no such affine expression has been found (yet).
+ */
+struct isl_extract_mod_data {
+ isl_ast_build *build;
+ isl_aff *aff;
+
+ isl_ast_expr *pos;
+ isl_ast_expr *neg;
+
+ isl_aff *add;
+
+ int i;
+ isl_val *v;
+ isl_val *d;
+ isl_aff *div;
+
+ isl_aff *nonneg;
+ int sign;
+};
+
+/* Does
+ *
+ * arg mod data->d
+ *
+ * represent (a special case of) a test for some linear expression
+ * being even?
+ *
+ * In particular, is it of the form
+ *
+ * (lin - 1) mod 2
+ *
+ * ?
+ */
+static isl_bool is_even_test(struct isl_extract_mod_data *data,
+ __isl_keep isl_aff *arg)
+{
+ isl_bool res;
+ isl_val *cst;
+
+ res = isl_val_eq_si(data->d, 2);
+ if (res < 0 || !res)
+ return res;
+
+ cst = isl_aff_get_constant_val(arg);
+ res = isl_val_eq_si(cst, -1);
+ isl_val_free(cst);
+
+ return res;
+}
+
+/* Given that data->v * div_i in data->aff is equal to
+ *
+ * f * (term - (arg mod d))
+ *
+ * with data->d * f = data->v and "arg" non-negative on data->build, add
+ *
+ * f * term
+ *
+ * to data->add and
+ *
+ * abs(f) * (arg mod d)
+ *
+ * to data->neg or data->pos depending on the sign of -f.
+ *
+ * In the special case that "arg mod d" is of the form "(lin - 1) mod 2",
+ * with "lin" some linear expression, first replace
+ *
+ * f * (term - ((lin - 1) mod 2))
+ *
+ * by
+ *
+ * -f * (1 - term - (lin mod 2))
+ *
+ * These two are equal because
+ *
+ * ((lin - 1) mod 2) + (lin mod 2) = 1
+ *
+ * Also, if "lin - 1" is non-negative, then "lin" is non-negative too.
+ */
+static int extract_term_and_mod(struct isl_extract_mod_data *data,
+ __isl_take isl_aff *term, __isl_take isl_aff *arg)
+{
+ isl_bool even;
+ isl_ast_expr *expr;
+ int s;
+
+ even = is_even_test(data, arg);
+ if (even < 0) {
+ arg = isl_aff_free(arg);
+ } else if (even) {
+ term = oppose_div_arg(term, isl_val_copy(data->d));
+ data->v = isl_val_neg(data->v);
+ arg = isl_aff_set_constant_si(arg, 0);
+ }
+
+ data->v = isl_val_div(data->v, isl_val_copy(data->d));
+ s = isl_val_sgn(data->v);
+ data->v = isl_val_abs(data->v);
+ expr = isl_ast_expr_mod(data->v, arg, data->d, data->build);
+ isl_aff_free(arg);
+ if (s > 0)
+ data->neg = ast_expr_add(data->neg, expr);
+ else
+ data->pos = ast_expr_add(data->pos, expr);
+ data->aff = isl_aff_set_coefficient_si(data->aff,
+ isl_dim_div, data->i, 0);
+ if (s < 0)
+ data->v = isl_val_neg(data->v);
+ term = isl_aff_scale_val(term, isl_val_copy(data->v));
+
+ if (!data->add)
+ data->add = term;
+ else
+ data->add = isl_aff_add(data->add, term);
+ if (!data->add)
+ return -1;
+
+ return 0;
+}
+
+/* Given that data->v * div_i in data->aff is of the form
+ *
+ * f * d * floor(div/d)
+ *
+ * with div nonnegative on data->build, rewrite it as
+ *
+ * f * (div - (div mod d)) = f * div - f * (div mod d)
+ *
+ * and add
+ *
+ * f * div
+ *
+ * to data->add and
+ *
+ * abs(f) * (div mod d)
+ *
+ * to data->neg or data->pos depending on the sign of -f.
+ */
+static int extract_mod(struct isl_extract_mod_data *data)
+{
+ return extract_term_and_mod(data, isl_aff_copy(data->div),
+ isl_aff_copy(data->div));
+}
+
+/* Given that data->v * div_i in data->aff is of the form
+ *
+ * f * d * floor(div/d) (1)
+ *
+ * check if div is non-negative on data->build and, if so,
+ * extract the corresponding modulo from data->aff.
+ * If not, then check if
+ *
+ * -div + d - 1
+ *
+ * is non-negative on data->build. If so, replace (1) by
+ *
+ * -f * d * floor((-div + d - 1)/d)
+ *
+ * and extract the corresponding modulo from data->aff.
+ *
+ * This function may modify data->div.
+ */
+static int extract_nonneg_mod(struct isl_extract_mod_data *data)
+{
+ int mod;
+
+ mod = isl_ast_build_aff_is_nonneg(data->build, data->div);
+ if (mod < 0)
+ goto error;
+ if (mod)
+ return extract_mod(data);
+
+ data->div = oppose_div_arg(data->div, isl_val_copy(data->d));
+ mod = isl_ast_build_aff_is_nonneg(data->build, data->div);
+ if (mod < 0)
+ goto error;
+ if (mod) {
+ data->v = isl_val_neg(data->v);
+ return extract_mod(data);
+ }
+
+ return 0;
+error:
+ data->aff = isl_aff_free(data->aff);
+ return -1;
+}
+
+/* Is the affine expression of constraint "c" "simpler" than data->nonneg
+ * for use in extracting a modulo expression?
+ *
+ * We currently only consider the constant term of the affine expression.
+ * In particular, we prefer the affine expression with the smallest constant
+ * term.
+ * This means that if there are two constraints, say x >= 0 and -x + 10 >= 0,
+ * then we would pick x >= 0
+ *
+ * More detailed heuristics could be used if it turns out that there is a need.
+ */
+static int mod_constraint_is_simpler(struct isl_extract_mod_data *data,
+ __isl_keep isl_constraint *c)
+{
+ isl_val *v1, *v2;
+ int simpler;
+
+ if (!data->nonneg)
+ return 1;
+
+ v1 = isl_val_abs(isl_constraint_get_constant_val(c));
+ v2 = isl_val_abs(isl_aff_get_constant_val(data->nonneg));
+ simpler = isl_val_lt(v1, v2);
+ isl_val_free(v1);
+ isl_val_free(v2);
+
+ return simpler;
+}
+
+/* Check if the coefficients of "c" are either equal or opposite to those
+ * of data->div modulo data->d. If so, and if "c" is "simpler" than
+ * data->nonneg, then replace data->nonneg by the affine expression of "c"
+ * and set data->sign accordingly.
+ *
+ * Both "c" and data->div are assumed not to involve any integer divisions.
+ *
+ * Before we start the actual comparison, we first quickly check if
+ * "c" and data->div have the same non-zero coefficients.
+ * If not, then we assume that "c" is not of the desired form.
+ * Note that while the coefficients of data->div can be reasonably expected
+ * not to involve any coefficients that are multiples of d, "c" may
+ * very well involve such coefficients. This means that we may actually
+ * miss some cases.
+ *
+ * If the constant term is "too large", then the constraint is rejected,
+ * where "too large" is fairly arbitrarily set to 1 << 15.
+ * We do this to avoid picking up constraints that bound a variable
+ * by a very large number, say the largest or smallest possible
+ * variable in the representation of some integer type.
+ */
+static isl_stat check_parallel_or_opposite(__isl_take isl_constraint *c,
+ void *user)
+{
+ struct isl_extract_mod_data *data = user;
+ enum isl_dim_type c_type[2] = { isl_dim_param, isl_dim_set };
+ enum isl_dim_type a_type[2] = { isl_dim_param, isl_dim_in };
+ int i, t;
+ isl_size n[2];
+ int parallel = 1, opposite = 1;
+
+ for (t = 0; t < 2; ++t) {
+ n[t] = isl_constraint_dim(c, c_type[t]);
+ if (n[t] < 0)
+ return isl_stat_error;
+ for (i = 0; i < n[t]; ++i) {
+ int a, b;
+
+ a = isl_constraint_involves_dims(c, c_type[t], i, 1);
+ b = isl_aff_involves_dims(data->div, a_type[t], i, 1);
+ if (a != b)
+ parallel = opposite = 0;
+ }
+ }
+
+ if (parallel || opposite) {
+ isl_val *v;
+
+ v = isl_val_abs(isl_constraint_get_constant_val(c));
+ if (isl_val_cmp_si(v, 1 << 15) > 0)
+ parallel = opposite = 0;
+ isl_val_free(v);
+ }
+
+ for (t = 0; t < 2; ++t) {
+ for (i = 0; i < n[t]; ++i) {
+ isl_val *v1, *v2;
+
+ if (!parallel && !opposite)
+ break;
+ v1 = isl_constraint_get_coefficient_val(c,
+ c_type[t], i);
+ v2 = isl_aff_get_coefficient_val(data->div,
+ a_type[t], i);
+ if (parallel) {
+ v1 = isl_val_sub(v1, isl_val_copy(v2));
+ parallel = isl_val_is_divisible_by(v1, data->d);
+ v1 = isl_val_add(v1, isl_val_copy(v2));
+ }
+ if (opposite) {
+ v1 = isl_val_add(v1, isl_val_copy(v2));
+ opposite = isl_val_is_divisible_by(v1, data->d);
+ }
+ isl_val_free(v1);
+ isl_val_free(v2);
+ }
+ }
+
+ if ((parallel || opposite) && mod_constraint_is_simpler(data, c)) {
+ isl_aff_free(data->nonneg);
+ data->nonneg = isl_constraint_get_aff(c);
+ data->sign = parallel ? 1 : -1;
+ }
+
+ isl_constraint_free(c);
+
+ if (data->sign != 0 && data->nonneg == NULL)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Given that data->v * div_i in data->aff is of the form
+ *
+ * f * d * floor(div/d) (1)
+ *
+ * see if we can find an expression div' that is non-negative over data->build
+ * and that is related to div through
+ *
+ * div' = div + d * e
+ *
+ * or
+ *
+ * div' = -div + d - 1 + d * e
+ *
+ * with e some affine expression.
+ * If so, we write (1) as
+ *
+ * f * div + f * (div' mod d)
+ *
+ * or
+ *
+ * -f * (-div + d - 1) - f * (div' mod d)
+ *
+ * exploiting (in the second case) the fact that
+ *
+ * f * d * floor(div/d) = -f * d * floor((-div + d - 1)/d)
+ *
+ *
+ * We first try to find an appropriate expression for div'
+ * from the constraints of data->build->domain (which is therefore
+ * guaranteed to be non-negative on data->build), where we remove
+ * any integer divisions from the constraints and skip this step
+ * if "div" itself involves any integer divisions.
+ * If we cannot find an appropriate expression this way, then
+ * we pass control to extract_nonneg_mod where check
+ * if div or "-div + d -1" themselves happen to be
+ * non-negative on data->build.
+ *
+ * While looking for an appropriate constraint in data->build->domain,
+ * we ignore the constant term, so after finding such a constraint,
+ * we still need to fix up the constant term.
+ * In particular, if a is the constant term of "div"
+ * (or d - 1 - the constant term of "div" if data->sign < 0)
+ * and b is the constant term of the constraint, then we need to find
+ * a non-negative constant c such that
+ *
+ * b + c \equiv a mod d
+ *
+ * We therefore take
+ *
+ * c = (a - b) mod d
+ *
+ * and add it to b to obtain the constant term of div'.
+ * If this constant term is "too negative", then we add an appropriate
+ * multiple of d to make it positive.
+ *
+ *
+ * Note that the above is a only a very simple heuristic for finding an
+ * appropriate expression. We could try a bit harder by also considering
+ * sums of constraints that involve disjoint sets of variables or
+ * we could consider arbitrary linear combinations of constraints,
+ * although that could potentially be much more expensive as it involves
+ * the solution of an LP problem.
+ *
+ * In particular, if v_i is a column vector representing constraint i,
+ * w represents div and e_i is the i-th unit vector, then we are looking
+ * for a solution of the constraints
+ *
+ * \sum_i lambda_i v_i = w + \sum_i alpha_i d e_i
+ *
+ * with \lambda_i >= 0 and alpha_i of unrestricted sign.
+ * If we are not just interested in a non-negative expression, but
+ * also in one with a minimal range, then we don't just want
+ * c = \sum_i lambda_i v_i to be non-negative over the domain,
+ * but also beta - c = \sum_i mu_i v_i, where beta is a scalar
+ * that we want to minimize and we now also have to take into account
+ * the constant terms of the constraints.
+ * Alternatively, we could first compute the dual of the domain
+ * and plug in the constraints on the coefficients.
+ */
+static int try_extract_mod(struct isl_extract_mod_data *data)
+{
+ isl_basic_set *hull;
+ isl_val *v1, *v2;
+ isl_stat r;
+ isl_size n;
+
+ if (!data->build)
+ goto error;
+
+ n = isl_aff_dim(data->div, isl_dim_div);
+ if (n < 0)
+ goto error;
+
+ if (isl_aff_involves_dims(data->div, isl_dim_div, 0, n))
+ return extract_nonneg_mod(data);
+
+ hull = isl_set_simple_hull(isl_set_copy(data->build->domain));
+ hull = isl_basic_set_remove_divs(hull);
+ data->sign = 0;
+ data->nonneg = NULL;
+ r = isl_basic_set_foreach_constraint(hull, &check_parallel_or_opposite,
+ data);
+ isl_basic_set_free(hull);
+
+ if (!data->sign || r < 0) {
+ isl_aff_free(data->nonneg);
+ if (r < 0)
+ goto error;
+ return extract_nonneg_mod(data);
+ }
+
+ v1 = isl_aff_get_constant_val(data->div);
+ v2 = isl_aff_get_constant_val(data->nonneg);
+ if (data->sign < 0) {
+ v1 = isl_val_neg(v1);
+ v1 = isl_val_add(v1, isl_val_copy(data->d));
+ v1 = isl_val_sub_ui(v1, 1);
+ }
+ v1 = isl_val_sub(v1, isl_val_copy(v2));
+ v1 = isl_val_mod(v1, isl_val_copy(data->d));
+ v1 = isl_val_add(v1, v2);
+ v2 = isl_val_div(isl_val_copy(v1), isl_val_copy(data->d));
+ v2 = isl_val_ceil(v2);
+ if (isl_val_is_neg(v2)) {
+ v2 = isl_val_mul(v2, isl_val_copy(data->d));
+ v1 = isl_val_sub(v1, isl_val_copy(v2));
+ }
+ data->nonneg = isl_aff_set_constant_val(data->nonneg, v1);
+ isl_val_free(v2);
+
+ if (data->sign < 0) {
+ data->div = oppose_div_arg(data->div, isl_val_copy(data->d));
+ data->v = isl_val_neg(data->v);
+ }
+
+ return extract_term_and_mod(data,
+ isl_aff_copy(data->div), data->nonneg);
+error:
+ data->aff = isl_aff_free(data->aff);
+ return -1;
+}
+
+/* Check if "data->aff" involves any (implicit) modulo computations based
+ * on div "data->i".
+ * If so, remove them from aff and add expressions corresponding
+ * to those modulo computations to data->pos and/or data->neg.
+ *
+ * "aff" is assumed to be an integer affine expression.
+ *
+ * In particular, check if (v * div_j) is of the form
+ *
+ * f * m * floor(a / m)
+ *
+ * and, if so, rewrite it as
+ *
+ * f * (a - (a mod m)) = f * a - f * (a mod m)
+ *
+ * and extract out -f * (a mod m).
+ * In particular, if f > 0, we add (f * (a mod m)) to *neg.
+ * If f < 0, we add ((-f) * (a mod m)) to *pos.
+ *
+ * Note that in order to represent "a mod m" as
+ *
+ * (isl_ast_expr_op_pdiv_r, a, m)
+ *
+ * we need to make sure that a is non-negative.
+ * If not, we check if "-a + m - 1" is non-negative.
+ * If so, we can rewrite
+ *
+ * floor(a/m) = -ceil(-a/m) = -floor((-a + m - 1)/m)
+ *
+ * and still extract a modulo.
+ */
+static int extract_modulo(struct isl_extract_mod_data *data)
+{
+ data->div = isl_aff_get_div(data->aff, data->i);
+ data->d = isl_aff_get_denominator_val(data->div);
+ if (isl_val_is_divisible_by(data->v, data->d)) {
+ data->div = isl_aff_scale_val(data->div, isl_val_copy(data->d));
+ if (try_extract_mod(data) < 0)
+ data->aff = isl_aff_free(data->aff);
+ }
+ isl_aff_free(data->div);
+ isl_val_free(data->d);
+ return 0;
+}
+
+/* Check if "aff" involves any (implicit) modulo computations.
+ * If so, remove them from aff and add expressions corresponding
+ * to those modulo computations to *pos and/or *neg.
+ * We only do this if the option ast_build_prefer_pdiv is set.
+ *
+ * "aff" is assumed to be an integer affine expression.
+ *
+ * A modulo expression is of the form
+ *
+ * a mod m = a - m * floor(a / m)
+ *
+ * To detect them in aff, we look for terms of the form
+ *
+ * f * m * floor(a / m)
+ *
+ * rewrite them as
+ *
+ * f * (a - (a mod m)) = f * a - f * (a mod m)
+ *
+ * and extract out -f * (a mod m).
+ * In particular, if f > 0, we add (f * (a mod m)) to *neg.
+ * If f < 0, we add ((-f) * (a mod m)) to *pos.
+ */
+static __isl_give isl_aff *extract_modulos(__isl_take isl_aff *aff,
+ __isl_keep isl_ast_expr **pos, __isl_keep isl_ast_expr **neg,
+ __isl_keep isl_ast_build *build)
+{
+ struct isl_extract_mod_data data = { build, aff, *pos, *neg };
+ isl_ctx *ctx;
+ isl_size n;
+
+ if (!aff)
+ return NULL;
+
+ ctx = isl_aff_get_ctx(aff);
+ if (!isl_options_get_ast_build_prefer_pdiv(ctx))
+ return aff;
+
+ n = isl_aff_dim(data.aff, isl_dim_div);
+ if (n < 0)
+ return isl_aff_free(aff);
+ for (data.i = 0; data.i < n; ++data.i) {
+ data.v = isl_aff_get_coefficient_val(data.aff,
+ isl_dim_div, data.i);
+ if (!data.v)
+ return isl_aff_free(aff);
+ if (isl_val_is_zero(data.v) ||
+ isl_val_is_one(data.v) || isl_val_is_negone(data.v)) {
+ isl_val_free(data.v);
+ continue;
+ }
+ if (extract_modulo(&data) < 0)
+ data.aff = isl_aff_free(data.aff);
+ isl_val_free(data.v);
+ if (!data.aff)
+ break;
+ }
+
+ if (data.add)
+ data.aff = isl_aff_add(data.aff, data.add);
+
+ *pos = data.pos;
+ *neg = data.neg;
+ return data.aff;
+}
+
+/* Check if aff involves any non-integer coefficients.
+ * If so, split aff into
+ *
+ * aff = aff1 + (aff2 / d)
+ *
+ * with both aff1 and aff2 having only integer coefficients.
+ * Return aff1 and add (aff2 / d) to *expr.
+ */
+static __isl_give isl_aff *extract_rational(__isl_take isl_aff *aff,
+ __isl_keep isl_ast_expr **expr, __isl_keep isl_ast_build *build)
+{
+ int i, j;
+ isl_size n;
+ isl_aff *rat = NULL;
+ isl_local_space *ls = NULL;
+ isl_ast_expr *rat_expr;
+ isl_val *v, *d;
+ enum isl_dim_type t[] = { isl_dim_param, isl_dim_in, isl_dim_div };
+ enum isl_dim_type l[] = { isl_dim_param, isl_dim_set, isl_dim_div };
+
+ if (!aff)
+ return NULL;
+ d = isl_aff_get_denominator_val(aff);
+ if (!d)
+ goto error;
+ if (isl_val_is_one(d)) {
+ isl_val_free(d);
+ return aff;
+ }
+
+ aff = isl_aff_scale_val(aff, isl_val_copy(d));
+
+ ls = isl_aff_get_domain_local_space(aff);
+ rat = isl_aff_zero_on_domain(isl_local_space_copy(ls));
+
+ for (i = 0; i < 3; ++i) {
+ n = isl_aff_dim(aff, t[i]);
+ if (n < 0)
+ goto error;
+ for (j = 0; j < n; ++j) {
+ isl_aff *rat_j;
+
+ v = isl_aff_get_coefficient_val(aff, t[i], j);
+ if (!v)
+ goto error;
+ if (isl_val_is_divisible_by(v, d)) {
+ isl_val_free(v);
+ continue;
+ }
+ rat_j = isl_aff_var_on_domain(isl_local_space_copy(ls),
+ l[i], j);
+ rat_j = isl_aff_scale_val(rat_j, v);
+ rat = isl_aff_add(rat, rat_j);
+ }
+ }
+
+ v = isl_aff_get_constant_val(aff);
+ if (isl_val_is_divisible_by(v, d)) {
+ isl_val_free(v);
+ } else {
+ isl_aff *rat_0;
+
+ rat_0 = isl_aff_val_on_domain(isl_local_space_copy(ls), v);
+ rat = isl_aff_add(rat, rat_0);
+ }
+
+ isl_local_space_free(ls);
+
+ aff = isl_aff_sub(aff, isl_aff_copy(rat));
+ aff = isl_aff_scale_down_val(aff, isl_val_copy(d));
+
+ rat_expr = isl_ast_expr_from_aff(rat, build);
+ rat_expr = isl_ast_expr_div(rat_expr, isl_ast_expr_from_val(d));
+ *expr = ast_expr_add(*expr, rat_expr);
+
+ return aff;
+error:
+ isl_aff_free(rat);
+ isl_local_space_free(ls);
+ isl_aff_free(aff);
+ isl_val_free(d);
+ return NULL;
+}
+
+/* Construct an isl_ast_expr that evaluates the affine expression "aff",
+ * The result is simplified in terms of build->domain.
+ *
+ * We first extract hidden modulo computations from the affine expression
+ * and then add terms for each variable with a non-zero coefficient.
+ * Finally, if the affine expression has a non-trivial denominator,
+ * we divide the resulting isl_ast_expr by this denominator.
+ */
+__isl_give isl_ast_expr *isl_ast_expr_from_aff(__isl_take isl_aff *aff,
+ __isl_keep isl_ast_build *build)
+{
+ int i, j;
+ isl_size n;
+ isl_val *v;
+ isl_ctx *ctx = isl_aff_get_ctx(aff);
+ isl_ast_expr *expr, *expr_neg;
+ enum isl_dim_type t[] = { isl_dim_param, isl_dim_in, isl_dim_div };
+ enum isl_dim_type l[] = { isl_dim_param, isl_dim_set, isl_dim_div };
+ isl_local_space *ls;
+ struct isl_ast_add_term_data data;
+
+ if (!aff)
+ return NULL;
+
+ expr = isl_ast_expr_alloc_int_si(ctx, 0);
+ expr_neg = isl_ast_expr_alloc_int_si(ctx, 0);
+
+ aff = extract_rational(aff, &expr, build);
+
+ aff = extract_modulos(aff, &expr, &expr_neg, build);
+ expr = ast_expr_sub(expr, expr_neg);
+
+ ls = isl_aff_get_domain_local_space(aff);
+
+ data.build = build;
+ data.cst = isl_aff_get_constant_val(aff);
+ for (i = 0; i < 3; ++i) {
+ n = isl_aff_dim(aff, t[i]);
+ if (n < 0)
+ expr = isl_ast_expr_free(expr);
+ for (j = 0; j < n; ++j) {
+ v = isl_aff_get_coefficient_val(aff, t[i], j);
+ if (!v)
+ expr = isl_ast_expr_free(expr);
+ if (isl_val_is_zero(v)) {
+ isl_val_free(v);
+ continue;
+ }
+ expr = isl_ast_expr_add_term(expr,
+ ls, l[i], j, v, &data);
+ }
+ }
+
+ expr = isl_ast_expr_add_int(expr, data.cst);
+
+ isl_local_space_free(ls);
+ isl_aff_free(aff);
+ return expr;
+}
+
+/* Add terms to "expr" for each variable in "aff" with a coefficient
+ * with sign equal to "sign".
+ * The result is simplified in terms of data->build->domain.
+ */
+static __isl_give isl_ast_expr *add_signed_terms(__isl_take isl_ast_expr *expr,
+ __isl_keep isl_aff *aff, int sign, struct isl_ast_add_term_data *data)
+{
+ int i, j;
+ isl_val *v;
+ enum isl_dim_type t[] = { isl_dim_param, isl_dim_in, isl_dim_div };
+ enum isl_dim_type l[] = { isl_dim_param, isl_dim_set, isl_dim_div };
+ isl_local_space *ls;
+
+ ls = isl_aff_get_domain_local_space(aff);
+
+ for (i = 0; i < 3; ++i) {
+ isl_size n = isl_aff_dim(aff, t[i]);
+ if (n < 0)
+ expr = isl_ast_expr_free(expr);
+ for (j = 0; j < n; ++j) {
+ v = isl_aff_get_coefficient_val(aff, t[i], j);
+ if (sign * isl_val_sgn(v) <= 0) {
+ isl_val_free(v);
+ continue;
+ }
+ v = isl_val_abs(v);
+ expr = isl_ast_expr_add_term(expr,
+ ls, l[i], j, v, data);
+ }
+ }
+
+ isl_local_space_free(ls);
+
+ return expr;
+}
+
+/* Should the constant term "v" be considered positive?
+ *
+ * A positive constant will be added to "pos" by the caller,
+ * while a negative constant will be added to "neg".
+ * If either "pos" or "neg" is exactly zero, then we prefer
+ * to add the constant "v" to that side, irrespective of the sign of "v".
+ * This results in slightly shorter expressions and may reduce the risk
+ * of overflows.
+ */
+static int constant_is_considered_positive(__isl_keep isl_val *v,
+ __isl_keep isl_ast_expr *pos, __isl_keep isl_ast_expr *neg)
+{
+ if (ast_expr_is_zero(pos))
+ return 1;
+ if (ast_expr_is_zero(neg))
+ return 0;
+ return isl_val_is_pos(v);
+}
+
+/* Check if the equality
+ *
+ * aff = 0
+ *
+ * represents a stride constraint on the integer division "pos".
+ *
+ * In particular, if the integer division "pos" is equal to
+ *
+ * floor(e/d)
+ *
+ * then check if aff is equal to
+ *
+ * e - d floor(e/d)
+ *
+ * or its opposite.
+ *
+ * If so, the equality is exactly
+ *
+ * e mod d = 0
+ *
+ * Note that in principle we could also accept
+ *
+ * e - d floor(e'/d)
+ *
+ * where e and e' differ by a constant.
+ */
+static int is_stride_constraint(__isl_keep isl_aff *aff, int pos)
+{
+ isl_aff *div;
+ isl_val *c, *d;
+ int eq;
+
+ div = isl_aff_get_div(aff, pos);
+ c = isl_aff_get_coefficient_val(aff, isl_dim_div, pos);
+ d = isl_aff_get_denominator_val(div);
+ eq = isl_val_abs_eq(c, d);
+ if (eq >= 0 && eq) {
+ aff = isl_aff_copy(aff);
+ aff = isl_aff_set_coefficient_si(aff, isl_dim_div, pos, 0);
+ div = isl_aff_scale_val(div, d);
+ if (isl_val_is_pos(c))
+ div = isl_aff_neg(div);
+ eq = isl_aff_plain_is_equal(div, aff);
+ isl_aff_free(aff);
+ } else
+ isl_val_free(d);
+ isl_val_free(c);
+ isl_aff_free(div);
+
+ return eq;
+}
+
+/* Are all coefficients of "aff" (zero or) negative?
+ */
+static isl_bool all_negative_coefficients(__isl_keep isl_aff *aff)
+{
+ int i;
+ isl_size n;
+
+ n = isl_aff_dim(aff, isl_dim_param);
+ if (n < 0)
+ return isl_bool_error;
+ for (i = 0; i < n; ++i)
+ if (isl_aff_coefficient_sgn(aff, isl_dim_param, i) > 0)
+ return isl_bool_false;
+
+ n = isl_aff_dim(aff, isl_dim_in);
+ if (n < 0)
+ return isl_bool_error;
+ for (i = 0; i < n; ++i)
+ if (isl_aff_coefficient_sgn(aff, isl_dim_in, i) > 0)
+ return isl_bool_false;
+
+ return isl_bool_true;
+}
+
+/* Give an equality of the form
+ *
+ * aff = e - d floor(e/d) = 0
+ *
+ * or
+ *
+ * aff = -e + d floor(e/d) = 0
+ *
+ * with the integer division "pos" equal to floor(e/d),
+ * construct the AST expression
+ *
+ * (isl_ast_expr_op_eq,
+ * (isl_ast_expr_op_zdiv_r, expr(e), expr(d)), expr(0))
+ *
+ * If e only has negative coefficients, then construct
+ *
+ * (isl_ast_expr_op_eq,
+ * (isl_ast_expr_op_zdiv_r, expr(-e), expr(d)), expr(0))
+ *
+ * instead.
+ */
+static __isl_give isl_ast_expr *extract_stride_constraint(
+ __isl_take isl_aff *aff, int pos, __isl_keep isl_ast_build *build)
+{
+ isl_bool all_neg;
+ isl_ctx *ctx;
+ isl_val *c;
+ isl_ast_expr *expr, *cst;
+
+ if (!aff)
+ return NULL;
+
+ ctx = isl_aff_get_ctx(aff);
+
+ c = isl_aff_get_coefficient_val(aff, isl_dim_div, pos);
+ aff = isl_aff_set_coefficient_si(aff, isl_dim_div, pos, 0);
+
+ all_neg = all_negative_coefficients(aff);
+ if (all_neg < 0)
+ aff = isl_aff_free(aff);
+ else if (all_neg)
+ aff = isl_aff_neg(aff);
+
+ cst = isl_ast_expr_from_val(isl_val_abs(c));
+ expr = isl_ast_expr_from_aff(aff, build);
+
+ expr = isl_ast_expr_alloc_binary(isl_ast_expr_op_zdiv_r, expr, cst);
+ cst = isl_ast_expr_alloc_int_si(ctx, 0);
+ expr = isl_ast_expr_alloc_binary(isl_ast_expr_op_eq, expr, cst);
+
+ return expr;
+}
+
+/* Construct an isl_ast_expr that evaluates the condition "constraint",
+ * The result is simplified in terms of build->domain.
+ *
+ * We first check if the constraint is an equality of the form
+ *
+ * e - d floor(e/d) = 0
+ *
+ * i.e.,
+ *
+ * e mod d = 0
+ *
+ * If so, we convert it to
+ *
+ * (isl_ast_expr_op_eq,
+ * (isl_ast_expr_op_zdiv_r, expr(e), expr(d)), expr(0))
+ *
+ * Otherwise, let the constraint by either "a >= 0" or "a == 0".
+ * We first extract hidden modulo computations from "a"
+ * and then collect all the terms with a positive coefficient in cons_pos
+ * and the terms with a negative coefficient in cons_neg.
+ *
+ * The result is then of the form
+ *
+ * (isl_ast_expr_op_ge, expr(pos), expr(-neg)))
+ *
+ * or
+ *
+ * (isl_ast_expr_op_eq, expr(pos), expr(-neg)))
+ *
+ * However, if the first expression is an integer constant (and the second
+ * is not), then we swap the two expressions. This ensures that we construct,
+ * e.g., "i <= 5" rather than "5 >= i".
+ *
+ * Furthermore, is there are no terms with positive coefficients (or no terms
+ * with negative coefficients), then the constant term is added to "pos"
+ * (or "neg"), ignoring the sign of the constant term.
+ */
+static __isl_give isl_ast_expr *isl_ast_expr_from_constraint(
+ __isl_take isl_constraint *constraint, __isl_keep isl_ast_build *build)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_ast_expr *expr_pos;
+ isl_ast_expr *expr_neg;
+ isl_ast_expr *expr;
+ isl_aff *aff;
+ int eq;
+ enum isl_ast_expr_op_type type;
+ struct isl_ast_add_term_data data;
+
+ if (!constraint)
+ return NULL;
+
+ aff = isl_constraint_get_aff(constraint);
+ eq = isl_constraint_is_equality(constraint);
+ isl_constraint_free(constraint);
+
+ n = isl_aff_dim(aff, isl_dim_div);
+ if (n < 0)
+ aff = isl_aff_free(aff);
+ if (eq && n > 0)
+ for (i = 0; i < n; ++i) {
+ int is_stride;
+ is_stride = is_stride_constraint(aff, i);
+ if (is_stride < 0)
+ goto error;
+ if (is_stride)
+ return extract_stride_constraint(aff, i, build);
+ }
+
+ ctx = isl_aff_get_ctx(aff);
+ expr_pos = isl_ast_expr_alloc_int_si(ctx, 0);
+ expr_neg = isl_ast_expr_alloc_int_si(ctx, 0);
+
+ aff = extract_modulos(aff, &expr_pos, &expr_neg, build);
+
+ data.build = build;
+ data.cst = isl_aff_get_constant_val(aff);
+ expr_pos = add_signed_terms(expr_pos, aff, 1, &data);
+ data.cst = isl_val_neg(data.cst);
+ expr_neg = add_signed_terms(expr_neg, aff, -1, &data);
+ data.cst = isl_val_neg(data.cst);
+
+ if (constant_is_considered_positive(data.cst, expr_pos, expr_neg)) {
+ expr_pos = isl_ast_expr_add_int(expr_pos, data.cst);
+ } else {
+ data.cst = isl_val_neg(data.cst);
+ expr_neg = isl_ast_expr_add_int(expr_neg, data.cst);
+ }
+
+ if (isl_ast_expr_get_type(expr_pos) == isl_ast_expr_int &&
+ isl_ast_expr_get_type(expr_neg) != isl_ast_expr_int) {
+ type = eq ? isl_ast_expr_op_eq : isl_ast_expr_op_le;
+ expr = isl_ast_expr_alloc_binary(type, expr_neg, expr_pos);
+ } else {
+ type = eq ? isl_ast_expr_op_eq : isl_ast_expr_op_ge;
+ expr = isl_ast_expr_alloc_binary(type, expr_pos, expr_neg);
+ }
+
+ isl_aff_free(aff);
+ return expr;
+error:
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Wrapper around isl_constraint_cmp_last_non_zero for use
+ * as a callback to isl_constraint_list_sort.
+ * If isl_constraint_cmp_last_non_zero cannot tell the constraints
+ * apart, then use isl_constraint_plain_cmp instead.
+ */
+static int cmp_constraint(__isl_keep isl_constraint *a,
+ __isl_keep isl_constraint *b, void *user)
+{
+ int cmp;
+
+ cmp = isl_constraint_cmp_last_non_zero(a, b);
+ if (cmp != 0)
+ return cmp;
+ return isl_constraint_plain_cmp(a, b);
+}
+
+/* Construct an isl_ast_expr that evaluates the conditions defining "bset".
+ * The result is simplified in terms of build->domain.
+ *
+ * If "bset" is not bounded by any constraint, then we construct
+ * the expression "1", i.e., "true".
+ *
+ * Otherwise, we sort the constraints, putting constraints that involve
+ * integer divisions after those that do not, and construct an "and"
+ * of the ast expressions of the individual constraints.
+ *
+ * Each constraint is added to the generated constraints of the build
+ * after it has been converted to an AST expression so that it can be used
+ * to simplify the following constraints. This may change the truth value
+ * of subsequent constraints that do not satisfy the earlier constraints,
+ * but this does not affect the outcome of the conjunction as it is
+ * only true if all the conjuncts are true (no matter in what order
+ * they are evaluated). In particular, the constraints that do not
+ * involve integer divisions may serve to simplify some constraints
+ * that do involve integer divisions.
+ */
+__isl_give isl_ast_expr *isl_ast_build_expr_from_basic_set(
+ __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset)
+{
+ int i;
+ isl_size n;
+ isl_constraint *c;
+ isl_constraint_list *list;
+ isl_ast_expr *res;
+ isl_set *set;
+
+ list = isl_basic_set_get_constraint_list(bset);
+ isl_basic_set_free(bset);
+ list = isl_constraint_list_sort(list, &cmp_constraint, NULL);
+ n = isl_constraint_list_n_constraint(list);
+ if (n < 0)
+ build = NULL;
+ if (n == 0) {
+ isl_ctx *ctx = isl_constraint_list_get_ctx(list);
+ isl_constraint_list_free(list);
+ return isl_ast_expr_alloc_int_si(ctx, 1);
+ }
+
+ build = isl_ast_build_copy(build);
+
+ c = isl_constraint_list_get_constraint(list, 0);
+ bset = isl_basic_set_from_constraint(isl_constraint_copy(c));
+ set = isl_set_from_basic_set(bset);
+ res = isl_ast_expr_from_constraint(c, build);
+ build = isl_ast_build_restrict_generated(build, set);
+
+ for (i = 1; i < n; ++i) {
+ isl_ast_expr *expr;
+
+ c = isl_constraint_list_get_constraint(list, i);
+ bset = isl_basic_set_from_constraint(isl_constraint_copy(c));
+ set = isl_set_from_basic_set(bset);
+ expr = isl_ast_expr_from_constraint(c, build);
+ build = isl_ast_build_restrict_generated(build, set);
+ res = isl_ast_expr_and(res, expr);
+ }
+
+ isl_constraint_list_free(list);
+ isl_ast_build_free(build);
+ return res;
+}
+
+/* Construct an isl_ast_expr that evaluates the conditions defining "set".
+ * The result is simplified in terms of build->domain.
+ *
+ * If "set" is an (obviously) empty set, then return the expression "0".
+ *
+ * If there are multiple disjuncts in the description of the set,
+ * then subsequent disjuncts are simplified in a context where
+ * the previous disjuncts have been removed from build->domain.
+ * In particular, constraints that ensure that there is no overlap
+ * with these previous disjuncts, can be removed.
+ * This is mostly useful for disjuncts that are only defined by
+ * a single constraint (relative to the build domain) as the opposite
+ * of that single constraint can then be removed from the other disjuncts.
+ * In order not to increase the number of disjuncts in the build domain
+ * after subtracting the previous disjuncts of "set", the simple hull
+ * is computed after taking the difference with each of these disjuncts.
+ * This means that constraints that prevent overlap with a union
+ * of multiple previous disjuncts are not removed.
+ *
+ * "set" lives in the internal schedule space.
+ */
+__isl_give isl_ast_expr *isl_ast_build_expr_from_set_internal(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set)
+{
+ int i;
+ isl_size n;
+ isl_basic_set *bset;
+ isl_basic_set_list *list;
+ isl_set *domain;
+ isl_ast_expr *res;
+
+ list = isl_set_get_basic_set_list(set);
+ isl_set_free(set);
+
+ n = isl_basic_set_list_n_basic_set(list);
+ if (n < 0)
+ build = NULL;
+ if (n == 0) {
+ isl_ctx *ctx = isl_ast_build_get_ctx(build);
+ isl_basic_set_list_free(list);
+ return isl_ast_expr_from_val(isl_val_zero(ctx));
+ }
+
+ domain = isl_ast_build_get_domain(build);
+
+ bset = isl_basic_set_list_get_basic_set(list, 0);
+ set = isl_set_from_basic_set(isl_basic_set_copy(bset));
+ res = isl_ast_build_expr_from_basic_set(build, bset);
+
+ for (i = 1; i < n; ++i) {
+ isl_ast_expr *expr;
+ isl_set *rest;
+
+ rest = isl_set_subtract(isl_set_copy(domain), set);
+ rest = isl_set_from_basic_set(isl_set_simple_hull(rest));
+ domain = isl_set_intersect(domain, rest);
+ bset = isl_basic_set_list_get_basic_set(list, i);
+ set = isl_set_from_basic_set(isl_basic_set_copy(bset));
+ bset = isl_basic_set_gist(bset,
+ isl_set_simple_hull(isl_set_copy(domain)));
+ expr = isl_ast_build_expr_from_basic_set(build, bset);
+ res = isl_ast_expr_or(res, expr);
+ }
+
+ isl_set_free(domain);
+ isl_set_free(set);
+ isl_basic_set_list_free(list);
+ return res;
+}
+
+/* Construct an isl_ast_expr that evaluates the conditions defining "set".
+ * The result is simplified in terms of build->domain.
+ *
+ * If "set" is an (obviously) empty set, then return the expression "0".
+ *
+ * "set" lives in the external schedule space.
+ *
+ * The internal AST expression generation assumes that there are
+ * no unknown divs, so make sure an explicit representation is available.
+ * Since the set comes from the outside, it may have constraints that
+ * are redundant with respect to the build domain. Remove them first.
+ */
+__isl_give isl_ast_expr *isl_ast_build_expr_from_set(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set)
+{
+ isl_bool needs_map;
+
+ needs_map = isl_ast_build_need_schedule_map(build);
+ if (needs_map < 0) {
+ set = isl_set_free(set);
+ } else if (needs_map) {
+ isl_multi_aff *ma;
+ ma = isl_ast_build_get_schedule_map_multi_aff(build);
+ set = isl_set_preimage_multi_aff(set, ma);
+ }
+
+ set = isl_set_compute_divs(set);
+ set = isl_ast_build_compute_gist(build, set);
+ return isl_ast_build_expr_from_set_internal(build, set);
+}
+
+/* State of data about previous pieces in
+ * isl_ast_build_expr_from_pw_aff_internal.
+ *
+ * isl_state_none: no data about previous pieces
+ * isl_state_single: data about a single previous piece
+ * isl_state_min: data represents minimum of several pieces
+ * isl_state_max: data represents maximum of several pieces
+ */
+enum isl_from_pw_aff_state {
+ isl_state_none,
+ isl_state_single,
+ isl_state_min,
+ isl_state_max
+};
+
+/* Internal date structure representing a single piece in the input of
+ * isl_ast_build_expr_from_pw_aff_internal.
+ *
+ * If "state" is isl_state_none, then "set_list" and "aff_list" are not used.
+ * If "state" is isl_state_single, then "set_list" and "aff_list" contain the
+ * single previous subpiece.
+ * If "state" is isl_state_min, then "set_list" and "aff_list" contain
+ * a sequence of several previous subpieces that are equal to the minimum
+ * of the entries in "aff_list" over the union of "set_list"
+ * If "state" is isl_state_max, then "set_list" and "aff_list" contain
+ * a sequence of several previous subpieces that are equal to the maximum
+ * of the entries in "aff_list" over the union of "set_list"
+ *
+ * During the construction of the pieces, "set" is NULL.
+ * After the construction, "set" is set to the union of the elements
+ * in "set_list", at which point "set_list" is set to NULL.
+ */
+struct isl_from_pw_aff_piece {
+ enum isl_from_pw_aff_state state;
+ isl_set *set;
+ isl_set_list *set_list;
+ isl_aff_list *aff_list;
+};
+
+/* Internal data structure for isl_ast_build_expr_from_pw_aff_internal.
+ *
+ * "build" specifies the domain against which the result is simplified.
+ * "dom" is the domain of the entire isl_pw_aff.
+ *
+ * "n" is the number of pieces constructed already.
+ * In particular, during the construction of the pieces, "n" points to
+ * the piece that is being constructed. After the construction of the
+ * pieces, "n" is set to the total number of pieces.
+ * "max" is the total number of allocated entries.
+ * "p" contains the individual pieces.
+ */
+struct isl_from_pw_aff_data {
+ isl_ast_build *build;
+ isl_set *dom;
+
+ int n;
+ int max;
+ struct isl_from_pw_aff_piece *p;
+};
+
+/* Initialize "data" based on "build" and "pa".
+ */
+static isl_stat isl_from_pw_aff_data_init(struct isl_from_pw_aff_data *data,
+ __isl_keep isl_ast_build *build, __isl_keep isl_pw_aff *pa)
+{
+ isl_size n;
+ isl_ctx *ctx;
+
+ ctx = isl_pw_aff_get_ctx(pa);
+ n = isl_pw_aff_n_piece(pa);
+ if (n < 0)
+ return isl_stat_error;
+ if (n == 0)
+ isl_die(ctx, isl_error_invalid,
+ "cannot handle void expression", return isl_stat_error);
+ data->max = n;
+ data->p = isl_calloc_array(ctx, struct isl_from_pw_aff_piece, n);
+ if (!data->p)
+ return isl_stat_error;
+ data->build = build;
+ data->dom = isl_pw_aff_domain(isl_pw_aff_copy(pa));
+ data->n = 0;
+
+ return isl_stat_ok;
+}
+
+/* Free all memory allocated for "data".
+ */
+static void isl_from_pw_aff_data_clear(struct isl_from_pw_aff_data *data)
+{
+ int i;
+
+ isl_set_free(data->dom);
+ if (!data->p)
+ return;
+
+ for (i = 0; i < data->max; ++i) {
+ isl_set_free(data->p[i].set);
+ isl_set_list_free(data->p[i].set_list);
+ isl_aff_list_free(data->p[i].aff_list);
+ }
+ free(data->p);
+}
+
+/* Initialize the current entry of "data" to an unused piece.
+ */
+static void set_none(struct isl_from_pw_aff_data *data)
+{
+ data->p[data->n].state = isl_state_none;
+ data->p[data->n].set_list = NULL;
+ data->p[data->n].aff_list = NULL;
+}
+
+/* Store "set" and "aff" in the current entry of "data" as a single subpiece.
+ */
+static void set_single(struct isl_from_pw_aff_data *data,
+ __isl_take isl_set *set, __isl_take isl_aff *aff)
+{
+ data->p[data->n].state = isl_state_single;
+ data->p[data->n].set_list = isl_set_list_from_set(set);
+ data->p[data->n].aff_list = isl_aff_list_from_aff(aff);
+}
+
+/* Extend the current entry of "data" with "set" and "aff"
+ * as a minimum expression.
+ */
+static isl_stat extend_min(struct isl_from_pw_aff_data *data,
+ __isl_take isl_set *set, __isl_take isl_aff *aff)
+{
+ int n = data->n;
+ data->p[n].state = isl_state_min;
+ data->p[n].set_list = isl_set_list_add(data->p[n].set_list, set);
+ data->p[n].aff_list = isl_aff_list_add(data->p[n].aff_list, aff);
+
+ if (!data->p[n].set_list || !data->p[n].aff_list)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Extend the current entry of "data" with "set" and "aff"
+ * as a maximum expression.
+ */
+static isl_stat extend_max(struct isl_from_pw_aff_data *data,
+ __isl_take isl_set *set, __isl_take isl_aff *aff)
+{
+ int n = data->n;
+ data->p[n].state = isl_state_max;
+ data->p[n].set_list = isl_set_list_add(data->p[n].set_list, set);
+ data->p[n].aff_list = isl_aff_list_add(data->p[n].aff_list, aff);
+
+ if (!data->p[n].set_list || !data->p[n].aff_list)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Extend the domain of the current entry of "data", which is assumed
+ * to contain a single subpiece, with "set". If "replace" is set,
+ * then also replace the affine function by "aff". Otherwise,
+ * simply free "aff".
+ */
+static isl_stat extend_domain(struct isl_from_pw_aff_data *data,
+ __isl_take isl_set *set, __isl_take isl_aff *aff, int replace)
+{
+ int n = data->n;
+ isl_set *set_n;
+
+ set_n = isl_set_list_get_set(data->p[n].set_list, 0);
+ set_n = isl_set_union(set_n, set);
+ data->p[n].set_list =
+ isl_set_list_set_set(data->p[n].set_list, 0, set_n);
+
+ if (replace)
+ data->p[n].aff_list =
+ isl_aff_list_set_aff(data->p[n].aff_list, 0, aff);
+ else
+ isl_aff_free(aff);
+
+ if (!data->p[n].set_list || !data->p[n].aff_list)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Construct an isl_ast_expr from "list" within "build".
+ * If "state" is isl_state_single, then "list" contains a single entry and
+ * an isl_ast_expr is constructed for that entry.
+ * Otherwise a min or max expression is constructed from "list"
+ * depending on "state".
+ */
+static __isl_give isl_ast_expr *ast_expr_from_aff_list(
+ __isl_take isl_aff_list *list, enum isl_from_pw_aff_state state,
+ __isl_keep isl_ast_build *build)
+{
+ int i;
+ isl_size n;
+ isl_aff *aff;
+ isl_ast_expr *expr = NULL;
+ enum isl_ast_expr_op_type op_type;
+
+ if (state == isl_state_single) {
+ aff = isl_aff_list_get_aff(list, 0);
+ isl_aff_list_free(list);
+ return isl_ast_expr_from_aff(aff, build);
+ }
+ n = isl_aff_list_n_aff(list);
+ if (n < 0)
+ goto error;
+ op_type = state == isl_state_min ? isl_ast_expr_op_min
+ : isl_ast_expr_op_max;
+ expr = isl_ast_expr_alloc_op(isl_ast_build_get_ctx(build), op_type, n);
+ if (!expr)
+ goto error;
+
+ for (i = 0; i < n; ++i) {
+ isl_ast_expr *expr_i;
+
+ aff = isl_aff_list_get_aff(list, i);
+ expr_i = isl_ast_expr_from_aff(aff, build);
+ if (!expr_i)
+ goto error;
+ expr->u.op.args[i] = expr_i;
+ }
+
+ isl_aff_list_free(list);
+ return expr;
+error:
+ isl_aff_list_free(list);
+ isl_ast_expr_free(expr);
+ return NULL;
+}
+
+/* Extend the expression in "next" to take into account
+ * the piece at position "pos" in "data", allowing for a further extension
+ * for the next piece(s).
+ * In particular, "next" is set to a select operation that selects
+ * an isl_ast_expr corresponding to data->aff_list on data->set and
+ * to an expression that will be filled in by later calls.
+ * Return a pointer to this location.
+ * Afterwards, the state of "data" is set to isl_state_none.
+ *
+ * The constraints of data->set are added to the generated
+ * constraints of the build such that they can be exploited to simplify
+ * the AST expression constructed from data->aff_list.
+ */
+static isl_ast_expr **add_intermediate_piece(struct isl_from_pw_aff_data *data,
+ int pos, isl_ast_expr **next)
+{
+ isl_ctx *ctx;
+ isl_ast_build *build;
+ isl_ast_expr *ternary, *arg;
+ isl_set *set, *gist;
+
+ set = data->p[pos].set;
+ data->p[pos].set = NULL;
+ ctx = isl_ast_build_get_ctx(data->build);
+ ternary = isl_ast_expr_alloc_op(ctx, isl_ast_expr_op_select, 3);
+ gist = isl_set_gist(isl_set_copy(set), isl_set_copy(data->dom));
+ arg = isl_ast_build_expr_from_set_internal(data->build, gist);
+ ternary = isl_ast_expr_set_op_arg(ternary, 0, arg);
+ build = isl_ast_build_copy(data->build);
+ build = isl_ast_build_restrict_generated(build, set);
+ arg = ast_expr_from_aff_list(data->p[pos].aff_list,
+ data->p[pos].state, build);
+ data->p[pos].aff_list = NULL;
+ isl_ast_build_free(build);
+ ternary = isl_ast_expr_set_op_arg(ternary, 1, arg);
+ data->p[pos].state = isl_state_none;
+ if (!ternary)
+ return NULL;
+
+ *next = ternary;
+ return &ternary->u.op.args[2];
+}
+
+/* Extend the expression in "next" to take into account
+ * the final piece, located at position "pos" in "data".
+ * In particular, "next" is set to evaluate data->aff_list
+ * and the domain is ignored.
+ * Return isl_stat_ok on success and isl_stat_error on failure.
+ *
+ * The constraints of data->set are however added to the generated
+ * constraints of the build such that they can be exploited to simplify
+ * the AST expression constructed from data->aff_list.
+ */
+static isl_stat add_last_piece(struct isl_from_pw_aff_data *data,
+ int pos, isl_ast_expr **next)
+{
+ isl_ast_build *build;
+
+ if (data->p[pos].state == isl_state_none)
+ isl_die(isl_ast_build_get_ctx(data->build), isl_error_invalid,
+ "cannot handle void expression", return isl_stat_error);
+
+ build = isl_ast_build_copy(data->build);
+ build = isl_ast_build_restrict_generated(build, data->p[pos].set);
+ data->p[pos].set = NULL;
+ *next = ast_expr_from_aff_list(data->p[pos].aff_list,
+ data->p[pos].state, build);
+ data->p[pos].aff_list = NULL;
+ isl_ast_build_free(build);
+ data->p[pos].state = isl_state_none;
+ if (!*next)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Return -1 if the piece "p1" should be sorted before "p2"
+ * and 1 if it should be sorted after "p2".
+ * Return 0 if they do not need to be sorted in a specific order.
+ *
+ * Pieces are sorted according to the number of disjuncts
+ * in their domains.
+ */
+static int sort_pieces_cmp(const void *p1, const void *p2, void *arg)
+{
+ const struct isl_from_pw_aff_piece *piece1 = p1;
+ const struct isl_from_pw_aff_piece *piece2 = p2;
+ isl_size n1, n2;
+
+ n1 = isl_set_n_basic_set(piece1->set);
+ n2 = isl_set_n_basic_set(piece2->set);
+
+ return n1 - n2;
+}
+
+/* Construct an isl_ast_expr from the pieces in "data".
+ * Return the result or NULL on failure.
+ *
+ * When this function is called, data->n points to the current piece.
+ * If this is an effective piece, then first increment data->n such
+ * that data->n contains the number of pieces.
+ * The "set_list" fields are subsequently replaced by the corresponding
+ * "set" fields, after which the pieces are sorted according to
+ * the number of disjuncts in these "set" fields.
+ *
+ * Construct intermediate AST expressions for the initial pieces and
+ * finish off with the final pieces.
+ */
+static isl_ast_expr *build_pieces(struct isl_from_pw_aff_data *data)
+{
+ int i;
+ isl_ast_expr *res = NULL;
+ isl_ast_expr **next = &res;
+
+ if (data->p[data->n].state != isl_state_none)
+ data->n++;
+ if (data->n == 0)
+ isl_die(isl_ast_build_get_ctx(data->build), isl_error_invalid,
+ "cannot handle void expression", return NULL);
+
+ for (i = 0; i < data->n; ++i) {
+ data->p[i].set = isl_set_list_union(data->p[i].set_list);
+ if (data->p[i].state != isl_state_single)
+ data->p[i].set = isl_set_coalesce(data->p[i].set);
+ data->p[i].set_list = NULL;
+ }
+
+ if (isl_sort(data->p, data->n, sizeof(data->p[0]),
+ &sort_pieces_cmp, NULL) < 0)
+ return isl_ast_expr_free(res);
+
+ for (i = 0; i + 1 < data->n; ++i) {
+ next = add_intermediate_piece(data, i, next);
+ if (!next)
+ return isl_ast_expr_free(res);
+ }
+
+ if (add_last_piece(data, data->n - 1, next) < 0)
+ return isl_ast_expr_free(res);
+
+ return res;
+}
+
+/* Is the domain of the current entry of "data", which is assumed
+ * to contain a single subpiece, a subset of "set"?
+ */
+static isl_bool single_is_subset(struct isl_from_pw_aff_data *data,
+ __isl_keep isl_set *set)
+{
+ isl_bool subset;
+ isl_set *set_n;
+
+ set_n = isl_set_list_get_set(data->p[data->n].set_list, 0);
+ subset = isl_set_is_subset(set_n, set);
+ isl_set_free(set_n);
+
+ return subset;
+}
+
+/* Is "aff" a rational expression, i.e., does it have a denominator
+ * different from one?
+ */
+static isl_bool aff_is_rational(__isl_keep isl_aff *aff)
+{
+ isl_bool rational;
+ isl_val *den;
+
+ den = isl_aff_get_denominator_val(aff);
+ rational = isl_bool_not(isl_val_is_one(den));
+ isl_val_free(den);
+
+ return rational;
+}
+
+/* Does "list" consist of a single rational affine expression?
+ */
+static isl_bool is_single_rational_aff(__isl_keep isl_aff_list *list)
+{
+ isl_size n;
+ isl_bool rational;
+ isl_aff *aff;
+
+ n = isl_aff_list_n_aff(list);
+ if (n < 0)
+ return isl_bool_error;
+ if (n != 1)
+ return isl_bool_false;
+ aff = isl_aff_list_get_aff(list, 0);
+ rational = aff_is_rational(aff);
+ isl_aff_free(aff);
+
+ return rational;
+}
+
+/* Can the list of subpieces in the last piece of "data" be extended with
+ * "set" and "aff" based on "test"?
+ * In particular, is it the case for each entry (set_i, aff_i) that
+ *
+ * test(aff, aff_i) holds on set_i, and
+ * test(aff_i, aff) holds on set?
+ *
+ * "test" returns the set of elements where the tests holds, meaning
+ * that test(aff_i, aff) holds on set if set is a subset of test(aff_i, aff).
+ *
+ * This function is used to detect min/max expressions.
+ * If the ast_build_detect_min_max option is turned off, then
+ * do not even try and perform any detection and return false instead.
+ *
+ * Rational affine expressions are not considered for min/max expressions
+ * since the combined expression will be defined on the union of the domains,
+ * while a rational expression may only yield integer values
+ * on its own definition domain.
+ */
+static isl_bool extends(struct isl_from_pw_aff_data *data,
+ __isl_keep isl_set *set, __isl_keep isl_aff *aff,
+ __isl_give isl_basic_set *(*test)(__isl_take isl_aff *aff1,
+ __isl_take isl_aff *aff2))
+{
+ int i;
+ isl_size n;
+ isl_bool is_rational;
+ isl_ctx *ctx;
+ isl_set *dom;
+
+ is_rational = aff_is_rational(aff);
+ if (is_rational >= 0 && !is_rational)
+ is_rational = is_single_rational_aff(data->p[data->n].aff_list);
+ if (is_rational < 0 || is_rational)
+ return isl_bool_not(is_rational);
+
+ ctx = isl_ast_build_get_ctx(data->build);
+ if (!isl_options_get_ast_build_detect_min_max(ctx))
+ return isl_bool_false;
+
+ n = isl_set_list_n_set(data->p[data->n].set_list);
+ if (n < 0)
+ return isl_bool_error;
+
+ dom = isl_ast_build_get_domain(data->build);
+ set = isl_set_intersect(dom, isl_set_copy(set));
+
+ for (i = 0; i < n ; ++i) {
+ isl_aff *aff_i;
+ isl_set *valid;
+ isl_set *dom, *required;
+ isl_bool is_valid;
+
+ aff_i = isl_aff_list_get_aff(data->p[data->n].aff_list, i);
+ valid = isl_set_from_basic_set(test(isl_aff_copy(aff), aff_i));
+ required = isl_set_list_get_set(data->p[data->n].set_list, i);
+ dom = isl_ast_build_get_domain(data->build);
+ required = isl_set_intersect(dom, required);
+ is_valid = isl_set_is_subset(required, valid);
+ isl_set_free(required);
+ isl_set_free(valid);
+ if (is_valid < 0 || !is_valid) {
+ isl_set_free(set);
+ return is_valid;
+ }
+
+ aff_i = isl_aff_list_get_aff(data->p[data->n].aff_list, i);
+ valid = isl_set_from_basic_set(test(aff_i, isl_aff_copy(aff)));
+ is_valid = isl_set_is_subset(set, valid);
+ isl_set_free(valid);
+ if (is_valid < 0 || !is_valid) {
+ isl_set_free(set);
+ return is_valid;
+ }
+ }
+
+ isl_set_free(set);
+ return isl_bool_true;
+}
+
+/* Can the list of pieces in "data" be extended with "set" and "aff"
+ * to form/preserve a minimum expression?
+ * In particular, is it the case for each entry (set_i, aff_i) that
+ *
+ * aff >= aff_i on set_i, and
+ * aff_i >= aff on set?
+ */
+static isl_bool extends_min(struct isl_from_pw_aff_data *data,
+ __isl_keep isl_set *set, __isl_keep isl_aff *aff)
+{
+ return extends(data, set, aff, &isl_aff_ge_basic_set);
+}
+
+/* Can the list of pieces in "data" be extended with "set" and "aff"
+ * to form/preserve a maximum expression?
+ * In particular, is it the case for each entry (set_i, aff_i) that
+ *
+ * aff <= aff_i on set_i, and
+ * aff_i <= aff on set?
+ */
+static isl_bool extends_max(struct isl_from_pw_aff_data *data,
+ __isl_keep isl_set *set, __isl_keep isl_aff *aff)
+{
+ return extends(data, set, aff, &isl_aff_le_basic_set);
+}
+
+/* This function is called during the construction of an isl_ast_expr
+ * that evaluates an isl_pw_aff.
+ * If the last piece of "data" contains a single subpiece and
+ * if its affine function is equal to "aff" on a part of the domain
+ * that includes either "set" or the domain of that single subpiece,
+ * then extend the domain of that single subpiece with "set".
+ * If it was the original domain of the single subpiece where
+ * the two affine functions are equal, then also replace
+ * the affine function of the single subpiece by "aff".
+ * If the last piece of "data" contains either a single subpiece
+ * or a minimum, then check if this minimum expression can be extended
+ * with (set, aff).
+ * If so, extend the sequence and return.
+ * Perform the same operation for maximum expressions.
+ * If no such extension can be performed, then move to the next piece
+ * in "data" (if the current piece contains any data), and then store
+ * the current subpiece in the current piece of "data" for later handling.
+ */
+static isl_stat ast_expr_from_pw_aff(__isl_take isl_set *set,
+ __isl_take isl_aff *aff, void *user)
+{
+ struct isl_from_pw_aff_data *data = user;
+ isl_bool test;
+ enum isl_from_pw_aff_state state;
+
+ state = data->p[data->n].state;
+ if (state == isl_state_single) {
+ isl_aff *aff0;
+ isl_set *eq;
+ isl_bool subset1, subset2 = isl_bool_false;
+ aff0 = isl_aff_list_get_aff(data->p[data->n].aff_list, 0);
+ eq = isl_aff_eq_set(isl_aff_copy(aff), aff0);
+ subset1 = isl_set_is_subset(set, eq);
+ if (subset1 >= 0 && !subset1)
+ subset2 = single_is_subset(data, eq);
+ isl_set_free(eq);
+ if (subset1 < 0 || subset2 < 0)
+ goto error;
+ if (subset1)
+ return extend_domain(data, set, aff, 0);
+ if (subset2)
+ return extend_domain(data, set, aff, 1);
+ }
+ if (state == isl_state_single || state == isl_state_min) {
+ test = extends_min(data, set, aff);
+ if (test < 0)
+ goto error;
+ if (test)
+ return extend_min(data, set, aff);
+ }
+ if (state == isl_state_single || state == isl_state_max) {
+ test = extends_max(data, set, aff);
+ if (test < 0)
+ goto error;
+ if (test)
+ return extend_max(data, set, aff);
+ }
+ if (state != isl_state_none)
+ data->n++;
+ set_single(data, set, aff);
+
+ return isl_stat_ok;
+error:
+ isl_set_free(set);
+ isl_aff_free(aff);
+ return isl_stat_error;
+}
+
+/* Construct an isl_ast_expr that evaluates "pa".
+ * The result is simplified in terms of build->domain.
+ *
+ * The domain of "pa" lives in the internal schedule space.
+ */
+__isl_give isl_ast_expr *isl_ast_build_expr_from_pw_aff_internal(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa)
+{
+ struct isl_from_pw_aff_data data = { NULL };
+ isl_ast_expr *res = NULL;
+
+ pa = isl_ast_build_compute_gist_pw_aff(build, pa);
+ pa = isl_pw_aff_coalesce(pa);
+ if (!pa)
+ return NULL;
+
+ if (isl_from_pw_aff_data_init(&data, build, pa) < 0)
+ goto error;
+ set_none(&data);
+
+ if (isl_pw_aff_foreach_piece(pa, &ast_expr_from_pw_aff, &data) >= 0)
+ res = build_pieces(&data);
+
+ isl_pw_aff_free(pa);
+ isl_from_pw_aff_data_clear(&data);
+ return res;
+error:
+ isl_pw_aff_free(pa);
+ isl_from_pw_aff_data_clear(&data);
+ return NULL;
+}
+
+/* Construct an isl_ast_expr that evaluates "pa".
+ * The result is simplified in terms of build->domain.
+ *
+ * The domain of "pa" lives in the external schedule space.
+ */
+__isl_give isl_ast_expr *isl_ast_build_expr_from_pw_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa)
+{
+ isl_ast_expr *expr;
+ isl_bool needs_map;
+
+ needs_map = isl_ast_build_need_schedule_map(build);
+ if (needs_map < 0) {
+ pa = isl_pw_aff_free(pa);
+ } else if (needs_map) {
+ isl_multi_aff *ma;
+ ma = isl_ast_build_get_schedule_map_multi_aff(build);
+ pa = isl_pw_aff_pullback_multi_aff(pa, ma);
+ }
+ expr = isl_ast_build_expr_from_pw_aff_internal(build, pa);
+ return expr;
+}
+
+/* Set the ids of the input dimensions of "mpa" to the iterator ids
+ * of "build".
+ *
+ * The domain of "mpa" is assumed to live in the internal schedule domain.
+ */
+static __isl_give isl_multi_pw_aff *set_iterator_names(
+ __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa)
+{
+ int i;
+ isl_size n;
+
+ n = isl_multi_pw_aff_dim(mpa, isl_dim_in);
+ if (n < 0)
+ return isl_multi_pw_aff_free(mpa);
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+
+ id = isl_ast_build_get_iterator_id(build, i);
+ mpa = isl_multi_pw_aff_set_dim_id(mpa, isl_dim_in, i, id);
+ }
+
+ return mpa;
+}
+
+/* Construct an isl_ast_expr of type "type" with as first argument "arg0" and
+ * the remaining arguments derived from "mpa".
+ * That is, construct a call or access expression that calls/accesses "arg0"
+ * with arguments/indices specified by "mpa".
+ */
+static __isl_give isl_ast_expr *isl_ast_build_with_arguments(
+ __isl_keep isl_ast_build *build, enum isl_ast_expr_op_type type,
+ __isl_take isl_ast_expr *arg0, __isl_take isl_multi_pw_aff *mpa)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_ast_expr *expr;
+
+ ctx = isl_ast_build_get_ctx(build);
+
+ n = isl_multi_pw_aff_dim(mpa, isl_dim_out);
+ expr = n >= 0 ? isl_ast_expr_alloc_op(ctx, type, 1 + n) : NULL;
+ expr = isl_ast_expr_set_op_arg(expr, 0, arg0);
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+ isl_ast_expr *arg;
+
+ pa = isl_multi_pw_aff_get_pw_aff(mpa, i);
+ arg = isl_ast_build_expr_from_pw_aff_internal(build, pa);
+ expr = isl_ast_expr_set_op_arg(expr, 1 + i, arg);
+ }
+
+ isl_multi_pw_aff_free(mpa);
+ return expr;
+}
+
+static __isl_give isl_ast_expr *isl_ast_build_from_multi_pw_aff_internal(
+ __isl_keep isl_ast_build *build, enum isl_ast_expr_op_type type,
+ __isl_take isl_multi_pw_aff *mpa);
+
+/* Construct an isl_ast_expr that accesses the member specified by "mpa".
+ * The range of "mpa" is assumed to be wrapped relation.
+ * The domain of this wrapped relation specifies the structure being
+ * accessed, while the range of this wrapped relation spacifies the
+ * member of the structure being accessed.
+ *
+ * The domain of "mpa" is assumed to live in the internal schedule domain.
+ */
+static __isl_give isl_ast_expr *isl_ast_build_from_multi_pw_aff_member(
+ __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa)
+{
+ isl_id *id;
+ isl_multi_pw_aff *domain;
+ isl_ast_expr *domain_expr, *expr;
+ enum isl_ast_expr_op_type type = isl_ast_expr_op_access;
+
+ domain = isl_multi_pw_aff_copy(mpa);
+ domain = isl_multi_pw_aff_range_factor_domain(domain);
+ domain_expr = isl_ast_build_from_multi_pw_aff_internal(build,
+ type, domain);
+ mpa = isl_multi_pw_aff_range_factor_range(mpa);
+ if (!isl_multi_pw_aff_has_tuple_id(mpa, isl_dim_out))
+ isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,
+ "missing field name", goto error);
+ id = isl_multi_pw_aff_get_tuple_id(mpa, isl_dim_out);
+ expr = isl_ast_expr_from_id(id);
+ expr = isl_ast_expr_alloc_binary(isl_ast_expr_op_member,
+ domain_expr, expr);
+ return isl_ast_build_with_arguments(build, type, expr, mpa);
+error:
+ isl_multi_pw_aff_free(mpa);
+ return NULL;
+}
+
+/* Construct an isl_ast_expr of type "type" that calls or accesses
+ * the element specified by "mpa".
+ * The first argument is obtained from the output tuple name.
+ * The remaining arguments are given by the piecewise affine expressions.
+ *
+ * If the range of "mpa" is a mapped relation, then we assume it
+ * represents an access to a member of a structure.
+ *
+ * The domain of "mpa" is assumed to live in the internal schedule domain.
+ */
+static __isl_give isl_ast_expr *isl_ast_build_from_multi_pw_aff_internal(
+ __isl_keep isl_ast_build *build, enum isl_ast_expr_op_type type,
+ __isl_take isl_multi_pw_aff *mpa)
+{
+ isl_ctx *ctx;
+ isl_id *id;
+ isl_ast_expr *expr;
+
+ if (!mpa)
+ goto error;
+
+ if (type == isl_ast_expr_op_access &&
+ isl_multi_pw_aff_range_is_wrapping(mpa))
+ return isl_ast_build_from_multi_pw_aff_member(build, mpa);
+
+ mpa = set_iterator_names(build, mpa);
+ if (!build || !mpa)
+ goto error;
+
+ ctx = isl_ast_build_get_ctx(build);
+
+ if (isl_multi_pw_aff_has_tuple_id(mpa, isl_dim_out))
+ id = isl_multi_pw_aff_get_tuple_id(mpa, isl_dim_out);
+ else
+ id = isl_id_alloc(ctx, "", NULL);
+
+ expr = isl_ast_expr_from_id(id);
+ return isl_ast_build_with_arguments(build, type, expr, mpa);
+error:
+ isl_multi_pw_aff_free(mpa);
+ return NULL;
+}
+
+/* Construct an isl_ast_expr of type "type" that calls or accesses
+ * the element specified by "pma".
+ * The first argument is obtained from the output tuple name.
+ * The remaining arguments are given by the piecewise affine expressions.
+ *
+ * The domain of "pma" is assumed to live in the internal schedule domain.
+ */
+static __isl_give isl_ast_expr *isl_ast_build_from_pw_multi_aff_internal(
+ __isl_keep isl_ast_build *build, enum isl_ast_expr_op_type type,
+ __isl_take isl_pw_multi_aff *pma)
+{
+ isl_multi_pw_aff *mpa;
+
+ mpa = isl_multi_pw_aff_from_pw_multi_aff(pma);
+ return isl_ast_build_from_multi_pw_aff_internal(build, type, mpa);
+}
+
+/* Construct an isl_ast_expr of type "type" that calls or accesses
+ * the element specified by "mpa".
+ * The first argument is obtained from the output tuple name.
+ * The remaining arguments are given by the piecewise affine expressions.
+ *
+ * The domain of "mpa" is assumed to live in the external schedule domain.
+ */
+static __isl_give isl_ast_expr *isl_ast_build_from_multi_pw_aff(
+ __isl_keep isl_ast_build *build, enum isl_ast_expr_op_type type,
+ __isl_take isl_multi_pw_aff *mpa)
+{
+ isl_bool is_domain;
+ isl_bool needs_map;
+ isl_ast_expr *expr;
+ isl_space *space_build, *space_mpa;
+
+ space_build = isl_ast_build_get_space(build, 0);
+ space_mpa = isl_multi_pw_aff_get_space(mpa);
+ is_domain = isl_space_tuple_is_equal(space_build, isl_dim_set,
+ space_mpa, isl_dim_in);
+ isl_space_free(space_build);
+ isl_space_free(space_mpa);
+ if (is_domain < 0)
+ goto error;
+ if (!is_domain)
+ isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,
+ "spaces don't match", goto error);
+
+ needs_map = isl_ast_build_need_schedule_map(build);
+ if (needs_map < 0)
+ goto error;
+ if (needs_map) {
+ isl_multi_aff *ma;
+ ma = isl_ast_build_get_schedule_map_multi_aff(build);
+ mpa = isl_multi_pw_aff_pullback_multi_aff(mpa, ma);
+ }
+
+ expr = isl_ast_build_from_multi_pw_aff_internal(build, type, mpa);
+ return expr;
+error:
+ isl_multi_pw_aff_free(mpa);
+ return NULL;
+}
+
+/* Construct an isl_ast_expr that calls the domain element specified by "mpa".
+ * The name of the function is obtained from the output tuple name.
+ * The arguments are given by the piecewise affine expressions.
+ *
+ * The domain of "mpa" is assumed to live in the external schedule domain.
+ */
+__isl_give isl_ast_expr *isl_ast_build_call_from_multi_pw_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa)
+{
+ return isl_ast_build_from_multi_pw_aff(build,
+ isl_ast_expr_op_call, mpa);
+}
+
+/* Construct an isl_ast_expr that accesses the array element specified by "mpa".
+ * The name of the array is obtained from the output tuple name.
+ * The index expressions are given by the piecewise affine expressions.
+ *
+ * The domain of "mpa" is assumed to live in the external schedule domain.
+ */
+__isl_give isl_ast_expr *isl_ast_build_access_from_multi_pw_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_multi_pw_aff *mpa)
+{
+ return isl_ast_build_from_multi_pw_aff(build,
+ isl_ast_expr_op_access, mpa);
+}
+
+/* Construct an isl_ast_expr of type "type" that calls or accesses
+ * the element specified by "pma".
+ * The first argument is obtained from the output tuple name.
+ * The remaining arguments are given by the piecewise affine expressions.
+ *
+ * The domain of "pma" is assumed to live in the external schedule domain.
+ */
+static __isl_give isl_ast_expr *isl_ast_build_from_pw_multi_aff(
+ __isl_keep isl_ast_build *build, enum isl_ast_expr_op_type type,
+ __isl_take isl_pw_multi_aff *pma)
+{
+ isl_multi_pw_aff *mpa;
+
+ mpa = isl_multi_pw_aff_from_pw_multi_aff(pma);
+ return isl_ast_build_from_multi_pw_aff(build, type, mpa);
+}
+
+/* Construct an isl_ast_expr that calls the domain element specified by "pma".
+ * The name of the function is obtained from the output tuple name.
+ * The arguments are given by the piecewise affine expressions.
+ *
+ * The domain of "pma" is assumed to live in the external schedule domain.
+ */
+__isl_give isl_ast_expr *isl_ast_build_call_from_pw_multi_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma)
+{
+ return isl_ast_build_from_pw_multi_aff(build,
+ isl_ast_expr_op_call, pma);
+}
+
+/* Construct an isl_ast_expr that accesses the array element specified by "pma".
+ * The name of the array is obtained from the output tuple name.
+ * The index expressions are given by the piecewise affine expressions.
+ *
+ * The domain of "pma" is assumed to live in the external schedule domain.
+ */
+__isl_give isl_ast_expr *isl_ast_build_access_from_pw_multi_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma)
+{
+ return isl_ast_build_from_pw_multi_aff(build,
+ isl_ast_expr_op_access, pma);
+}
+
+/* Construct an isl_ast_expr that calls the domain element
+ * specified by "executed".
+ *
+ * "executed" is assumed to be single-valued, with a domain that lives
+ * in the internal schedule space.
+ */
+__isl_give isl_ast_node *isl_ast_build_call_from_executed(
+ __isl_keep isl_ast_build *build, __isl_take isl_map *executed)
+{
+ isl_pw_multi_aff *iteration;
+ isl_ast_expr *expr;
+
+ iteration = isl_pw_multi_aff_from_map(executed);
+ iteration = isl_ast_build_compute_gist_pw_multi_aff(build, iteration);
+ iteration = isl_pw_multi_aff_intersect_domain(iteration,
+ isl_ast_build_get_domain(build));
+ expr = isl_ast_build_from_pw_multi_aff_internal(build,
+ isl_ast_expr_op_call, iteration);
+ return isl_ast_node_alloc_user(expr);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_expr.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_expr.h
new file mode 100644
index 00000000000..362f2bffc4a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_expr.h
@@ -0,0 +1,22 @@
+#ifndef ISL_AST_BUILD_EXPR_PRIVATE_H
+#define ISL_AST_BUILD_EXPR_PRIVATE_H
+
+#include <isl/ast.h>
+#include <isl/ast_build.h>
+
+__isl_give isl_ast_expr *isl_ast_build_expr_from_basic_set(
+ __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset);
+__isl_give isl_ast_expr *isl_ast_build_expr_from_set_internal(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set);
+
+__isl_give isl_ast_expr *isl_ast_build_expr_from_pw_aff_internal(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa);
+__isl_give isl_ast_expr *isl_ast_expr_from_aff(__isl_take isl_aff *aff,
+ __isl_keep isl_ast_build *build);
+__isl_give isl_ast_expr *isl_ast_expr_set_op_arg(__isl_take isl_ast_expr *expr,
+ int pos, __isl_take isl_ast_expr *arg);
+
+__isl_give isl_ast_node *isl_ast_build_call_from_executed(
+ __isl_keep isl_ast_build *build, __isl_take isl_map *executed);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_private.h
new file mode 100644
index 00000000000..1128e632c3a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_build_private.h
@@ -0,0 +1,327 @@
+#ifndef ISL_AST_BUILD_PRIVATE_H
+#define ISL_AST_BUILD_PRIVATE_H
+
+#include <isl/aff.h>
+#include <isl/ast.h>
+#include <isl/ast_build.h>
+#include <isl/set.h>
+#include <isl/list.h>
+#include <isl/schedule_node.h>
+
+/* An isl_ast_build represents the context in which AST is being
+ * generated. That is, it (mostly) contains information about outer
+ * loops that can be used to simplify inner loops.
+ *
+ * "domain" represents constraints on the internal schedule domain,
+ * corresponding to the context of the AST generation and the constraints
+ * implied by the loops that have already been generated.
+ * When an isl_ast_build is first created, outside any AST generation,
+ * the domain is typically a parameter set. It is only when a AST
+ * generation phase is initiated that the domain of the isl_ast_build
+ * is changed to refer to the internal schedule domain.
+ * The domain then lives in a space of the form
+ *
+ * S
+ *
+ * or
+ *
+ * [O -> S]
+ *
+ * O represents the loops generated in outer AST generations.
+ * S represents the loops (both generated and to be generated)
+ * of the current AST generation.
+ * Both include eliminated loops.
+ * "domain" is expected not to have any unknown divs because
+ * it is used as the context argument in a call to isl_basic_set_gist
+ * in isl_ast_build_compute_gist_basic_set.
+ *
+ * "depth" is equal to the number of loops that have already
+ * been generated (including those in outer AST generations).
+ * "outer_pos" is equal to the number of loops in outer AST generations.
+ *
+ * "generated" is a superset of "domain" corresponding to those
+ * constraints that were either given by the user or that have
+ * effectively been generated (as bounds on a for loop).
+ *
+ * "pending" is a superset of "domain" corresponding to the constraints
+ * that still need to be generated (as guards), but that may end up
+ * not getting generated if they are implied by any constraints
+ * enforced by inner loops.
+ *
+ * "strides" contains the stride of each loop. The number of elements
+ * is equal to the number of dimensions in "domain".
+ * "offsets" contains the offsets of strided loops. If s is the stride
+ * for a given dimension and f is the corresponding offset, then the
+ * dimension takes on values
+ *
+ * f + s a
+ *
+ * with a an integer. For non-strided loops, the offset is zero.
+ *
+ * "iterators" contains the loop iterators of both generated and
+ * to be generated loops. The number of elements is at least as
+ * large as the dimension of the internal schedule domain. The
+ * number may be larger, in which case the additional ids can be
+ * used in a nested AST generation should the schedule be non-injective.
+ *
+ * "values" lives in the space
+ *
+ * [O -> S] -> [O -> S] (or S -> S)
+ *
+ * and expresses (if possible) loop iterators in terms of parameters
+ * and outer loop iterators. If the value of a given loop iterator
+ * cannot be expressed as an affine expression (either because the iterator
+ * attains multiple values or because the single value is a piecewise
+ * affine expression), then it is expressed in "values" as being equal
+ * to itself.
+ *
+ * "value" is the value of the loop iterator at the current depth.
+ * It is NULL if it has not been computed yet or if the value of the
+ * given loop iterator cannot be expressed as a piecewise affine expression
+ * (because the iterator attains multiple values).
+ *
+ * "schedule_map" maps the internal schedule domain to the external schedule
+ * domain. It may be NULL if it hasn't been computed yet.
+ * See isl_ast_build_get_schedule_map_multi_aff.
+ *
+ * "internal2input" maps the internal schedule domain to the original
+ * input schedule domain. In case of a schedule tree input, the original
+ * input schedule domain consist of the flat product of all outer
+ * band node spaces, including the current band node.
+ * It may be NULL if there no longer is such a uniform mapping
+ * (because different iterations have been rescheduled differently).
+ *
+ * "options" contains the AST build options in case we are generating
+ * an AST from a flat schedule map. When creating an AST from a schedule
+ * tree, this field is ignored.
+ *
+ * The "create_leaf" callback is called for every leaf in the generated AST.
+ * The callback is responsible for creating the node to be placed at those
+ * leaves. If this callback is not set, then isl will generated user
+ * nodes with call expressions corresponding to an element of the domain.
+ *
+ * The "at_each_domain" callback is called on every node created to represent
+ * an element of the domain. Each of these nodes is a user node
+ * with as expression a call expression.
+ *
+ * The "before_each_for" callback is called on each for node before
+ * its children have been created.
+ *
+ * The "after_each_for" callback is called on each for node after
+ * its children have been created.
+ *
+ * The "before_each_mark" callback is called before we handle the subtree
+ * of an isl_schedule_node_mark node.
+ *
+ * The "after_each_mark" callback is called after we have handled the subtree
+ * of an isl_schedule_node_mark node.
+ *
+ * "executed" contains the inverse schedule at this point
+ * of the AST generation.
+ * It is currently only used in isl_ast_build_get_schedule, which is
+ * in turn only used by user code from within a callback.
+ * The value is set right before we may be calling such a callback.
+ *
+ * "single_valued" is set if the current inverse schedule (which may or may
+ * not be stored in "executed") is known to be single valued, specifically
+ * an inverse schedule that was not (appeared not to be) single valued
+ * is extended to a single valued inverse schedule. This is mainly used
+ * to avoid an infinite recursion when we fail to detect later on that
+ * the extended inverse schedule is single valued.
+ *
+ * "node" points to the current band node in case we are generating
+ * an AST from a schedule tree. It may be NULL if we are not generating
+ * an AST from a schedule tree or if we are not inside a band node.
+ *
+ * "loop_type" originally contains loop AST generation types for
+ * the "n" members of "node" and it is updated (along with "n") when
+ * a schedule dimension is inserted.
+ * It is NULL if "node" is NULL.
+ *
+ * "isolated" is the piece of the schedule domain isolated by the isolate
+ * option on the current band. This set may be NULL if we have not checked
+ * for the isolate option yet.
+ */
+struct isl_ast_build {
+ int ref;
+
+ int outer_pos;
+ int depth;
+
+ isl_id_list *iterators;
+
+ isl_set *domain;
+ isl_set *generated;
+ isl_set *pending;
+ isl_multi_aff *values;
+
+ isl_pw_aff *value;
+
+ isl_vec *strides;
+ isl_multi_aff *offsets;
+
+ isl_multi_aff *schedule_map;
+ isl_multi_aff *internal2input;
+
+ isl_union_map *options;
+
+ __isl_give isl_ast_node *(*at_each_domain)(
+ __isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build, void *user);
+ void *at_each_domain_user;
+
+ __isl_give isl_id *(*before_each_for)(
+ __isl_keep isl_ast_build *context, void *user);
+ void *before_each_for_user;
+ __isl_give isl_ast_node *(*after_each_for)(
+ __isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *context, void *user);
+ void *after_each_for_user;
+
+ isl_stat (*before_each_mark)(__isl_keep isl_id *mark,
+ __isl_keep isl_ast_build *build, void *user);
+ void *before_each_mark_user;
+ __isl_give isl_ast_node *(*after_each_mark)(
+ __isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *context, void *user);
+ void *after_each_mark_user;
+
+ __isl_give isl_ast_node *(*create_leaf)(
+ __isl_take isl_ast_build *build, void *user);
+ void *create_leaf_user;
+
+ isl_union_map *executed;
+ int single_valued;
+
+ isl_schedule_node *node;
+ int n;
+ enum isl_ast_loop_type *loop_type;
+ isl_set *isolated;
+};
+
+__isl_give isl_ast_build *isl_ast_build_clear_local_info(
+ __isl_take isl_ast_build *build);
+__isl_give isl_ast_build *isl_ast_build_increase_depth(
+ __isl_take isl_ast_build *build);
+isl_size isl_ast_build_get_depth(__isl_keep isl_ast_build *build);
+isl_size isl_ast_build_dim(__isl_keep isl_ast_build *build,
+ enum isl_dim_type type);
+__isl_give isl_space *isl_ast_build_get_space(
+ __isl_keep isl_ast_build *build, int internal);
+__isl_give isl_ast_build *isl_ast_build_align_params(
+ __isl_take isl_ast_build *build, __isl_take isl_space *model);
+__isl_give isl_ast_build *isl_ast_build_cow(
+ __isl_take isl_ast_build *build);
+__isl_give isl_ast_build *isl_ast_build_insert_dim(
+ __isl_take isl_ast_build *build, int pos);
+__isl_give isl_ast_build *isl_ast_build_scale_down(
+ __isl_take isl_ast_build *build, __isl_take isl_val *m,
+ __isl_take isl_union_map *umap);
+__isl_give isl_ast_build *isl_ast_build_product(
+ __isl_take isl_ast_build *build, __isl_take isl_space *embedding);
+__isl_give isl_ast_build *isl_ast_build_set_loop_bounds(
+ __isl_take isl_ast_build *build, __isl_take isl_basic_set *bounds);
+__isl_give isl_ast_build *isl_ast_build_set_pending_generated(
+ __isl_take isl_ast_build *build, __isl_take isl_basic_set *bounds);
+__isl_give isl_ast_build *isl_ast_build_detect_strides(
+ __isl_take isl_ast_build *build, __isl_take isl_set *set);
+__isl_give isl_ast_build *isl_ast_build_include_stride(
+ __isl_take isl_ast_build *build);
+__isl_give isl_ast_build *isl_ast_build_set_executed(
+ __isl_take isl_ast_build *build,
+ __isl_take isl_union_map *executed);
+__isl_give isl_ast_build *isl_ast_build_set_single_valued(
+ __isl_take isl_ast_build *build, int sv);
+__isl_give isl_multi_aff *isl_ast_build_get_internal2input(
+ __isl_keep isl_ast_build *build);
+__isl_give isl_set *isl_ast_build_get_domain(
+ __isl_keep isl_ast_build *build);
+__isl_give isl_set *isl_ast_build_get_pending(
+ __isl_keep isl_ast_build *build);
+__isl_give isl_set *isl_ast_build_get_generated(
+ __isl_keep isl_ast_build *build);
+__isl_give isl_ast_build *isl_ast_build_restrict_generated(
+ __isl_take isl_ast_build *build, __isl_take isl_set *set);
+__isl_give isl_ast_build *isl_ast_build_replace_pending_by_guard(
+ __isl_take isl_ast_build *build, __isl_take isl_set *guard);
+isl_bool isl_ast_build_need_schedule_map(__isl_keep isl_ast_build *build);
+__isl_give isl_multi_aff *isl_ast_build_get_schedule_map_multi_aff(
+ __isl_keep isl_ast_build *build);
+__isl_give isl_map *isl_ast_build_get_schedule_map(
+ __isl_keep isl_ast_build *build);
+isl_bool isl_ast_build_has_affine_value(__isl_keep isl_ast_build *build,
+ int pos);
+int isl_ast_build_has_value(__isl_keep isl_ast_build *build);
+__isl_give isl_id *isl_ast_build_get_iterator_id(
+ __isl_keep isl_ast_build *build, int pos);
+
+int isl_ast_build_has_schedule_node(__isl_keep isl_ast_build *build);
+__isl_give isl_schedule_node *isl_ast_build_get_schedule_node(
+ __isl_keep isl_ast_build *build);
+__isl_give isl_ast_build *isl_ast_build_set_schedule_node(
+ __isl_take isl_ast_build *build,
+ __isl_take isl_schedule_node *node);
+__isl_give isl_ast_build *isl_ast_build_reset_schedule_node(
+ __isl_take isl_ast_build *build);
+
+__isl_give isl_ast_build *isl_ast_build_extract_isolated(
+ __isl_take isl_ast_build *build);
+int isl_ast_build_has_isolated(__isl_keep isl_ast_build *build);
+__isl_give isl_set *isl_ast_build_get_isolated(
+ __isl_keep isl_ast_build *build);
+
+__isl_give isl_basic_set *isl_ast_build_specialize_basic_set(
+ __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset);
+__isl_give isl_basic_set *isl_ast_build_compute_gist_basic_set(
+ __isl_keep isl_ast_build *build, __isl_take isl_basic_set *bset);
+__isl_give isl_set *isl_ast_build_specialize(__isl_keep isl_ast_build *build,
+ __isl_take isl_set *set);
+__isl_give isl_set *isl_ast_build_compute_gist(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set);
+__isl_give isl_map *isl_ast_build_compute_gist_map_domain(
+ __isl_keep isl_ast_build *build, __isl_take isl_map *map);
+__isl_give isl_aff *isl_ast_build_compute_gist_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_aff *aff);
+__isl_give isl_pw_aff *isl_ast_build_compute_gist_pw_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_aff *pa);
+__isl_give isl_pw_multi_aff *isl_ast_build_compute_gist_pw_multi_aff(
+ __isl_keep isl_ast_build *build, __isl_take isl_pw_multi_aff *pma);
+
+__isl_give isl_union_map *isl_ast_build_substitute_values_union_map_domain(
+ __isl_keep isl_ast_build *build, __isl_take isl_union_map *umap);
+
+int isl_ast_build_aff_is_nonneg(__isl_keep isl_ast_build *build,
+ __isl_keep isl_aff *aff);
+
+isl_bool isl_ast_build_has_stride(__isl_keep isl_ast_build *build, int pos);
+__isl_give isl_aff *isl_ast_build_get_offset(__isl_keep isl_ast_build *build,
+ int pos);
+__isl_give isl_val *isl_ast_build_get_stride(__isl_keep isl_ast_build *build,
+ int pos);
+__isl_give isl_set *isl_ast_build_get_stride_constraint(
+ __isl_keep isl_ast_build *build);
+__isl_give isl_multi_aff *isl_ast_build_get_stride_expansion(
+ __isl_keep isl_ast_build *build);
+
+void isl_ast_build_dump(__isl_keep isl_ast_build *build);
+
+__isl_give isl_set *isl_ast_build_get_option_domain(
+ __isl_keep isl_ast_build *build, enum isl_ast_loop_type type);
+__isl_give isl_map *isl_ast_build_get_separation_class(
+ __isl_keep isl_ast_build *build);
+__isl_give isl_set *isl_ast_build_eliminate(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *domain);
+__isl_give isl_set *isl_ast_build_eliminate_inner(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set);
+__isl_give isl_set *isl_ast_build_eliminate_divs(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set);
+
+enum isl_ast_loop_type isl_ast_build_get_loop_type(
+ __isl_keep isl_ast_build *build, int isolated);
+
+__isl_give isl_map *isl_ast_build_map_to_iterator(
+ __isl_keep isl_ast_build *build, __isl_take isl_set *set);
+
+int isl_ast_build_options_involve_depth(__isl_keep isl_ast_build *build);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_codegen.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_codegen.c
new file mode 100644
index 00000000000..d337c4e4e2f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_codegen.c
@@ -0,0 +1,5952 @@
+/*
+ * Copyright 2012-2014 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#include <limits.h>
+#include <isl/id.h>
+#include <isl/val.h>
+#include <isl/space.h>
+#include <isl/aff.h>
+#include <isl/constraint.h>
+#include <isl/set.h>
+#include <isl/ilp.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl/schedule_node.h>
+#include <isl/options.h>
+#include <isl_sort.h>
+#include <isl_tarjan.h>
+#include <isl_ast_private.h>
+#include <isl_ast_build_expr.h>
+#include <isl_ast_build_private.h>
+#include <isl_ast_graft_private.h>
+
+/* Try and reduce the number of disjuncts in the representation of "set",
+ * without dropping explicit representations of local variables.
+ */
+static __isl_give isl_set *isl_set_coalesce_preserve(__isl_take isl_set *set)
+{
+ isl_ctx *ctx;
+ int save_preserve;
+
+ if (!set)
+ return NULL;
+
+ ctx = isl_set_get_ctx(set);
+ save_preserve = isl_options_get_coalesce_preserve_locals(ctx);
+ isl_options_set_coalesce_preserve_locals(ctx, 1);
+ set = isl_set_coalesce(set);
+ isl_options_set_coalesce_preserve_locals(ctx, save_preserve);
+ return set;
+}
+
+/* Data used in generate_domain.
+ *
+ * "build" is the input build.
+ * "list" collects the results.
+ */
+struct isl_generate_domain_data {
+ isl_ast_build *build;
+
+ isl_ast_graft_list *list;
+};
+
+static __isl_give isl_ast_graft_list *generate_next_level(
+ __isl_take isl_union_map *executed,
+ __isl_take isl_ast_build *build);
+static __isl_give isl_ast_graft_list *generate_code(
+ __isl_take isl_union_map *executed, __isl_take isl_ast_build *build,
+ int internal);
+
+/* Generate an AST for a single domain based on
+ * the (non single valued) inverse schedule "executed".
+ *
+ * We extend the schedule with the iteration domain
+ * and continue generating through a call to generate_code.
+ *
+ * In particular, if executed has the form
+ *
+ * S -> D
+ *
+ * then we continue generating code on
+ *
+ * [S -> D] -> D
+ *
+ * The extended inverse schedule is clearly single valued
+ * ensuring that the nested generate_code will not reach this function,
+ * but will instead create calls to all elements of D that need
+ * to be executed from the current schedule domain.
+ */
+static isl_stat generate_non_single_valued(__isl_take isl_map *executed,
+ struct isl_generate_domain_data *data)
+{
+ isl_map *identity;
+ isl_ast_build *build;
+ isl_ast_graft_list *list;
+
+ build = isl_ast_build_copy(data->build);
+
+ identity = isl_set_identity(isl_map_range(isl_map_copy(executed)));
+ executed = isl_map_domain_product(executed, identity);
+ build = isl_ast_build_set_single_valued(build, 1);
+
+ list = generate_code(isl_union_map_from_map(executed), build, 1);
+
+ data->list = isl_ast_graft_list_concat(data->list, list);
+
+ return isl_stat_ok;
+}
+
+/* Call the at_each_domain callback, if requested by the user,
+ * after recording the current inverse schedule in the build.
+ */
+static __isl_give isl_ast_graft *at_each_domain(__isl_take isl_ast_graft *graft,
+ __isl_keep isl_map *executed, __isl_keep isl_ast_build *build)
+{
+ if (!graft || !build)
+ return isl_ast_graft_free(graft);
+ if (!build->at_each_domain)
+ return graft;
+
+ build = isl_ast_build_copy(build);
+ build = isl_ast_build_set_executed(build,
+ isl_union_map_from_map(isl_map_copy(executed)));
+ if (!build)
+ return isl_ast_graft_free(graft);
+
+ graft->node = build->at_each_domain(graft->node,
+ build, build->at_each_domain_user);
+ isl_ast_build_free(build);
+
+ if (!graft->node)
+ graft = isl_ast_graft_free(graft);
+
+ return graft;
+}
+
+/* Generate a call expression for the single executed
+ * domain element "map" and put a guard around it based its (simplified)
+ * domain. "executed" is the original inverse schedule from which "map"
+ * has been derived. In particular, "map" is either identical to "executed"
+ * or it is the result of gisting "executed" with respect to the build domain.
+ * "executed" is only used if there is an at_each_domain callback.
+ *
+ * At this stage, any pending constraints in the build can no longer
+ * be simplified with respect to any enforced constraints since
+ * the call node does not have any enforced constraints.
+ * Since all pending constraints not covered by any enforced constraints
+ * will be added as a guard to the graft in create_node_scaled,
+ * even in the eliminated case, the pending constraints
+ * can be considered to have been generated by outer constructs.
+ *
+ * If the user has set an at_each_domain callback, it is called
+ * on the constructed call expression node.
+ */
+static isl_stat add_domain(__isl_take isl_map *executed,
+ __isl_take isl_map *map, struct isl_generate_domain_data *data)
+{
+ isl_ast_build *build;
+ isl_ast_graft *graft;
+ isl_ast_graft_list *list;
+ isl_set *guard, *pending;
+
+ build = isl_ast_build_copy(data->build);
+ pending = isl_ast_build_get_pending(build);
+ build = isl_ast_build_replace_pending_by_guard(build, pending);
+
+ guard = isl_map_domain(isl_map_copy(map));
+ guard = isl_set_compute_divs(guard);
+ guard = isl_set_coalesce_preserve(guard);
+ guard = isl_set_gist(guard, isl_ast_build_get_generated(build));
+ guard = isl_ast_build_specialize(build, guard);
+
+ graft = isl_ast_graft_alloc_domain(map, build);
+ graft = at_each_domain(graft, executed, build);
+ isl_ast_build_free(build);
+ isl_map_free(executed);
+ graft = isl_ast_graft_add_guard(graft, guard, data->build);
+
+ list = isl_ast_graft_list_from_ast_graft(graft);
+ data->list = isl_ast_graft_list_concat(data->list, list);
+
+ return isl_stat_ok;
+}
+
+/* Generate an AST for a single domain based on
+ * the inverse schedule "executed" and add it to data->list.
+ *
+ * If there is more than one domain element associated to the current
+ * schedule "time", then we need to continue the generation process
+ * in generate_non_single_valued.
+ * Note that the inverse schedule being single-valued may depend
+ * on constraints that are only available in the original context
+ * domain specified by the user. We therefore first introduce
+ * some of the constraints of data->build->domain. In particular,
+ * we intersect with a single-disjunct approximation of this set.
+ * We perform this approximation to avoid further splitting up
+ * the executed relation, possibly introducing a disjunctive guard
+ * on the statement.
+ *
+ * On the other hand, we only perform the test after having taken the gist
+ * of the domain as the resulting map is the one from which the call
+ * expression is constructed. Using this map to construct the call
+ * expression usually yields simpler results in cases where the original
+ * map is not obviously single-valued.
+ * If the original map is obviously single-valued, then the gist
+ * operation is skipped.
+ *
+ * Because we perform the single-valuedness test on the gisted map,
+ * we may in rare cases fail to recognize that the inverse schedule
+ * is single-valued. This becomes problematic if this happens
+ * from the recursive call through generate_non_single_valued
+ * as we would then end up in an infinite recursion.
+ * We therefore check if we are inside a call to generate_non_single_valued
+ * and revert to the ungisted map if the gisted map turns out not to be
+ * single-valued.
+ *
+ * Otherwise, call add_domain to generate a call expression (with guard) and
+ * to call the at_each_domain callback, if any.
+ */
+static isl_stat generate_domain(__isl_take isl_map *executed, void *user)
+{
+ struct isl_generate_domain_data *data = user;
+ isl_set *domain;
+ isl_map *map = NULL;
+ int empty, sv;
+
+ domain = isl_ast_build_get_domain(data->build);
+ domain = isl_set_from_basic_set(isl_set_simple_hull(domain));
+ executed = isl_map_intersect_domain(executed, domain);
+ empty = isl_map_is_empty(executed);
+ if (empty < 0)
+ goto error;
+ if (empty) {
+ isl_map_free(executed);
+ return isl_stat_ok;
+ }
+
+ sv = isl_map_plain_is_single_valued(executed);
+ if (sv < 0)
+ goto error;
+ if (sv)
+ return add_domain(executed, isl_map_copy(executed), data);
+
+ executed = isl_map_coalesce(executed);
+ map = isl_map_copy(executed);
+ map = isl_ast_build_compute_gist_map_domain(data->build, map);
+ sv = isl_map_is_single_valued(map);
+ if (sv < 0)
+ goto error;
+ if (!sv) {
+ isl_map_free(map);
+ if (data->build->single_valued)
+ map = isl_map_copy(executed);
+ else
+ return generate_non_single_valued(executed, data);
+ }
+
+ return add_domain(executed, map, data);
+error:
+ isl_map_free(map);
+ isl_map_free(executed);
+ return isl_stat_error;
+}
+
+/* Call build->create_leaf to a create "leaf" node in the AST,
+ * encapsulate the result in an isl_ast_graft and return the result
+ * as a 1-element list.
+ *
+ * Note that the node returned by the user may be an entire tree.
+ *
+ * Since the node itself cannot enforce any constraints, we turn
+ * all pending constraints into guards and add them to the resulting
+ * graft to ensure that they will be generated.
+ *
+ * Before we pass control to the user, we first clear some information
+ * from the build that is (presumbably) only meaningful
+ * for the current code generation.
+ * This includes the create_leaf callback itself, so we make a copy
+ * of the build first.
+ */
+static __isl_give isl_ast_graft_list *call_create_leaf(
+ __isl_take isl_union_map *executed, __isl_take isl_ast_build *build)
+{
+ isl_set *guard;
+ isl_ast_node *node;
+ isl_ast_graft *graft;
+ isl_ast_build *user_build;
+
+ guard = isl_ast_build_get_pending(build);
+ user_build = isl_ast_build_copy(build);
+ user_build = isl_ast_build_replace_pending_by_guard(user_build,
+ isl_set_copy(guard));
+ user_build = isl_ast_build_set_executed(user_build, executed);
+ user_build = isl_ast_build_clear_local_info(user_build);
+ if (!user_build)
+ node = NULL;
+ else
+ node = build->create_leaf(user_build, build->create_leaf_user);
+ graft = isl_ast_graft_alloc(node, build);
+ graft = isl_ast_graft_add_guard(graft, guard, build);
+ isl_ast_build_free(build);
+ return isl_ast_graft_list_from_ast_graft(graft);
+}
+
+static __isl_give isl_ast_graft_list *build_ast_from_child(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed);
+
+/* Generate an AST after having handled the complete schedule
+ * of this call to the code generator or the complete band
+ * if we are generating an AST from a schedule tree.
+ *
+ * If we are inside a band node, then move on to the child of the band.
+ *
+ * If the user has specified a create_leaf callback, control
+ * is passed to the user in call_create_leaf.
+ *
+ * Otherwise, we generate one or more calls for each individual
+ * domain in generate_domain.
+ */
+static __isl_give isl_ast_graft_list *generate_inner_level(
+ __isl_take isl_union_map *executed, __isl_take isl_ast_build *build)
+{
+ isl_ctx *ctx;
+ struct isl_generate_domain_data data = { build };
+
+ if (!build || !executed)
+ goto error;
+
+ if (isl_ast_build_has_schedule_node(build)) {
+ isl_schedule_node *node;
+ node = isl_ast_build_get_schedule_node(build);
+ build = isl_ast_build_reset_schedule_node(build);
+ return build_ast_from_child(build, node, executed);
+ }
+
+ if (build->create_leaf)
+ return call_create_leaf(executed, build);
+
+ ctx = isl_union_map_get_ctx(executed);
+ data.list = isl_ast_graft_list_alloc(ctx, 0);
+ if (isl_union_map_foreach_map(executed, &generate_domain, &data) < 0)
+ data.list = isl_ast_graft_list_free(data.list);
+
+ if (0)
+error: data.list = NULL;
+ isl_ast_build_free(build);
+ isl_union_map_free(executed);
+ return data.list;
+}
+
+/* Call the before_each_for callback, if requested by the user.
+ */
+static __isl_give isl_ast_node *before_each_for(__isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build)
+{
+ isl_id *id;
+
+ if (!node || !build)
+ return isl_ast_node_free(node);
+ if (!build->before_each_for)
+ return node;
+ id = build->before_each_for(build, build->before_each_for_user);
+ node = isl_ast_node_set_annotation(node, id);
+ return node;
+}
+
+/* Call the after_each_for callback, if requested by the user.
+ */
+static __isl_give isl_ast_graft *after_each_for(__isl_take isl_ast_graft *graft,
+ __isl_keep isl_ast_build *build)
+{
+ if (!graft || !build)
+ return isl_ast_graft_free(graft);
+ if (!build->after_each_for)
+ return graft;
+ graft->node = build->after_each_for(graft->node, build,
+ build->after_each_for_user);
+ if (!graft->node)
+ return isl_ast_graft_free(graft);
+ return graft;
+}
+
+/* Plug in all the know values of the current and outer dimensions
+ * in the domain of "executed". In principle, we only need to plug
+ * in the known value of the current dimension since the values of
+ * outer dimensions have been plugged in already.
+ * However, it turns out to be easier to just plug in all known values.
+ */
+static __isl_give isl_union_map *plug_in_values(
+ __isl_take isl_union_map *executed, __isl_keep isl_ast_build *build)
+{
+ return isl_ast_build_substitute_values_union_map_domain(build,
+ executed);
+}
+
+/* Check if the constraint "c" is a lower bound on dimension "pos",
+ * an upper bound, or independent of dimension "pos".
+ */
+static int constraint_type(isl_constraint *c, int pos)
+{
+ if (isl_constraint_is_lower_bound(c, isl_dim_set, pos))
+ return 1;
+ if (isl_constraint_is_upper_bound(c, isl_dim_set, pos))
+ return 2;
+ return 0;
+}
+
+/* Compare the types of the constraints "a" and "b",
+ * resulting in constraints that are independent of "depth"
+ * to be sorted before the lower bounds on "depth", which in
+ * turn are sorted before the upper bounds on "depth".
+ */
+static int cmp_constraint(__isl_keep isl_constraint *a,
+ __isl_keep isl_constraint *b, void *user)
+{
+ int *depth = user;
+ int t1 = constraint_type(a, *depth);
+ int t2 = constraint_type(b, *depth);
+
+ return t1 - t2;
+}
+
+/* Extract a lower bound on dimension "pos" from constraint "c".
+ *
+ * If the constraint is of the form
+ *
+ * a x + f(...) >= 0
+ *
+ * then we essentially return
+ *
+ * l = ceil(-f(...)/a)
+ *
+ * However, if the current dimension is strided, then we need to make
+ * sure that the lower bound we construct is of the form
+ *
+ * f + s a
+ *
+ * with f the offset and s the stride.
+ * We therefore compute
+ *
+ * f + s * ceil((l - f)/s)
+ */
+static __isl_give isl_aff *lower_bound(__isl_keep isl_constraint *c,
+ int pos, __isl_keep isl_ast_build *build)
+{
+ isl_aff *aff;
+
+ aff = isl_constraint_get_bound(c, isl_dim_set, pos);
+ aff = isl_aff_ceil(aff);
+
+ if (isl_ast_build_has_stride(build, pos)) {
+ isl_aff *offset;
+ isl_val *stride;
+
+ offset = isl_ast_build_get_offset(build, pos);
+ stride = isl_ast_build_get_stride(build, pos);
+
+ aff = isl_aff_sub(aff, isl_aff_copy(offset));
+ aff = isl_aff_scale_down_val(aff, isl_val_copy(stride));
+ aff = isl_aff_ceil(aff);
+ aff = isl_aff_scale_val(aff, stride);
+ aff = isl_aff_add(aff, offset);
+ }
+
+ aff = isl_ast_build_compute_gist_aff(build, aff);
+
+ return aff;
+}
+
+/* Return the exact lower bound (or upper bound if "upper" is set)
+ * of "domain" as a piecewise affine expression.
+ *
+ * If we are computing a lower bound (of a strided dimension), then
+ * we need to make sure it is of the form
+ *
+ * f + s a
+ *
+ * where f is the offset and s is the stride.
+ * We therefore need to include the stride constraint before computing
+ * the minimum.
+ */
+static __isl_give isl_pw_aff *exact_bound(__isl_keep isl_set *domain,
+ __isl_keep isl_ast_build *build, int upper)
+{
+ isl_set *stride;
+ isl_map *it_map;
+ isl_pw_aff *pa;
+ isl_pw_multi_aff *pma;
+
+ domain = isl_set_copy(domain);
+ if (!upper) {
+ stride = isl_ast_build_get_stride_constraint(build);
+ domain = isl_set_intersect(domain, stride);
+ }
+ it_map = isl_ast_build_map_to_iterator(build, domain);
+ if (upper)
+ pma = isl_map_lexmax_pw_multi_aff(it_map);
+ else
+ pma = isl_map_lexmin_pw_multi_aff(it_map);
+ pa = isl_pw_multi_aff_get_pw_aff(pma, 0);
+ isl_pw_multi_aff_free(pma);
+ pa = isl_ast_build_compute_gist_pw_aff(build, pa);
+ pa = isl_pw_aff_coalesce(pa);
+
+ return pa;
+}
+
+/* Callback for sorting the isl_pw_aff_list passed to reduce_list and
+ * remove_redundant_lower_bounds.
+ */
+static int reduce_list_cmp(__isl_keep isl_pw_aff *a, __isl_keep isl_pw_aff *b,
+ void *user)
+{
+ return isl_pw_aff_plain_cmp(a, b);
+}
+
+/* Given a list of lower bounds "list", remove those that are redundant
+ * with respect to the other bounds in "list" and the domain of "build".
+ *
+ * We first sort the bounds in the same way as they would be sorted
+ * by set_for_node_expressions so that we can try and remove the last
+ * bounds first.
+ *
+ * For a lower bound to be effective, there needs to be at least
+ * one domain element for which it is larger than all other lower bounds.
+ * For each lower bound we therefore intersect the domain with
+ * the conditions that it is larger than all other bounds and
+ * check whether the result is empty. If so, the bound can be removed.
+ */
+static __isl_give isl_pw_aff_list *remove_redundant_lower_bounds(
+ __isl_take isl_pw_aff_list *list, __isl_keep isl_ast_build *build)
+{
+ int i, j;
+ isl_size n;
+ isl_set *domain;
+
+ list = isl_pw_aff_list_sort(list, &reduce_list_cmp, NULL);
+
+ n = isl_pw_aff_list_n_pw_aff(list);
+ if (n < 0)
+ return isl_pw_aff_list_free(list);
+ if (n <= 1)
+ return list;
+
+ domain = isl_ast_build_get_domain(build);
+
+ for (i = n - 1; i >= 0; --i) {
+ isl_pw_aff *pa_i;
+ isl_set *domain_i;
+ int empty;
+
+ domain_i = isl_set_copy(domain);
+ pa_i = isl_pw_aff_list_get_pw_aff(list, i);
+
+ for (j = 0; j < n; ++j) {
+ isl_pw_aff *pa_j;
+ isl_set *better;
+
+ if (j == i)
+ continue;
+
+ pa_j = isl_pw_aff_list_get_pw_aff(list, j);
+ better = isl_pw_aff_gt_set(isl_pw_aff_copy(pa_i), pa_j);
+ domain_i = isl_set_intersect(domain_i, better);
+ }
+
+ empty = isl_set_is_empty(domain_i);
+
+ isl_set_free(domain_i);
+ isl_pw_aff_free(pa_i);
+
+ if (empty < 0)
+ goto error;
+ if (!empty)
+ continue;
+ list = isl_pw_aff_list_drop(list, i, 1);
+ n--;
+ }
+
+ isl_set_free(domain);
+
+ return list;
+error:
+ isl_set_free(domain);
+ return isl_pw_aff_list_free(list);
+}
+
+/* Extract a lower bound on dimension "pos" from each constraint
+ * in "constraints" and return the list of lower bounds.
+ * If "constraints" has zero elements, then we extract a lower bound
+ * from "domain" instead.
+ *
+ * If the current dimension is strided, then the lower bound
+ * is adjusted by lower_bound to match the stride information.
+ * This modification may make one or more lower bounds redundant
+ * with respect to the other lower bounds. We therefore check
+ * for this condition and remove the redundant lower bounds.
+ */
+static __isl_give isl_pw_aff_list *lower_bounds(
+ __isl_keep isl_constraint_list *constraints, int pos,
+ __isl_keep isl_set *domain, __isl_keep isl_ast_build *build)
+{
+ isl_ctx *ctx;
+ isl_pw_aff_list *list;
+ int i;
+ isl_size n;
+
+ if (!build)
+ return NULL;
+
+ n = isl_constraint_list_n_constraint(constraints);
+ if (n < 0)
+ return NULL;
+ if (n == 0) {
+ isl_pw_aff *pa;
+ pa = exact_bound(domain, build, 0);
+ return isl_pw_aff_list_from_pw_aff(pa);
+ }
+
+ ctx = isl_ast_build_get_ctx(build);
+ list = isl_pw_aff_list_alloc(ctx,n);
+
+ for (i = 0; i < n; ++i) {
+ isl_aff *aff;
+ isl_constraint *c;
+
+ c = isl_constraint_list_get_constraint(constraints, i);
+ aff = lower_bound(c, pos, build);
+ isl_constraint_free(c);
+ list = isl_pw_aff_list_add(list, isl_pw_aff_from_aff(aff));
+ }
+
+ if (isl_ast_build_has_stride(build, pos))
+ list = remove_redundant_lower_bounds(list, build);
+
+ return list;
+}
+
+/* Extract an upper bound on dimension "pos" from each constraint
+ * in "constraints" and return the list of upper bounds.
+ * If "constraints" has zero elements, then we extract an upper bound
+ * from "domain" instead.
+ */
+static __isl_give isl_pw_aff_list *upper_bounds(
+ __isl_keep isl_constraint_list *constraints, int pos,
+ __isl_keep isl_set *domain, __isl_keep isl_ast_build *build)
+{
+ isl_ctx *ctx;
+ isl_pw_aff_list *list;
+ int i;
+ isl_size n;
+
+ n = isl_constraint_list_n_constraint(constraints);
+ if (n < 0)
+ return NULL;
+ if (n == 0) {
+ isl_pw_aff *pa;
+ pa = exact_bound(domain, build, 1);
+ return isl_pw_aff_list_from_pw_aff(pa);
+ }
+
+ ctx = isl_ast_build_get_ctx(build);
+ list = isl_pw_aff_list_alloc(ctx,n);
+
+ for (i = 0; i < n; ++i) {
+ isl_aff *aff;
+ isl_constraint *c;
+
+ c = isl_constraint_list_get_constraint(constraints, i);
+ aff = isl_constraint_get_bound(c, isl_dim_set, pos);
+ isl_constraint_free(c);
+ aff = isl_aff_floor(aff);
+ list = isl_pw_aff_list_add(list, isl_pw_aff_from_aff(aff));
+ }
+
+ return list;
+}
+
+/* Return an isl_ast_expr that performs the reduction of type "type"
+ * on AST expressions corresponding to the elements in "list".
+ *
+ * The list is assumed to contain at least one element.
+ * If the list contains exactly one element, then the returned isl_ast_expr
+ * simply computes that affine expression.
+ * If the list contains more than one element, then we sort it
+ * using a fairly arbitrary but hopefully reasonably stable order.
+ */
+static __isl_give isl_ast_expr *reduce_list(enum isl_ast_expr_op_type type,
+ __isl_keep isl_pw_aff_list *list, __isl_keep isl_ast_build *build)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_ast_expr *expr;
+
+ n = isl_pw_aff_list_n_pw_aff(list);
+ if (n < 0)
+ return NULL;
+
+ if (n == 1)
+ return isl_ast_build_expr_from_pw_aff_internal(build,
+ isl_pw_aff_list_get_pw_aff(list, 0));
+
+ ctx = isl_pw_aff_list_get_ctx(list);
+ expr = isl_ast_expr_alloc_op(ctx, type, n);
+ if (!expr)
+ return NULL;
+
+ list = isl_pw_aff_list_copy(list);
+ list = isl_pw_aff_list_sort(list, &reduce_list_cmp, NULL);
+ if (!list)
+ return isl_ast_expr_free(expr);
+
+ for (i = 0; i < n; ++i) {
+ isl_ast_expr *expr_i;
+
+ expr_i = isl_ast_build_expr_from_pw_aff_internal(build,
+ isl_pw_aff_list_get_pw_aff(list, i));
+ if (!expr_i)
+ goto error;
+ expr->u.op.args[i] = expr_i;
+ }
+
+ isl_pw_aff_list_free(list);
+ return expr;
+error:
+ isl_pw_aff_list_free(list);
+ isl_ast_expr_free(expr);
+ return NULL;
+}
+
+/* Add guards implied by the "generated constraints",
+ * but not (necessarily) enforced by the generated AST to "guard".
+ * In particular, if there is any stride constraints,
+ * then add the guard implied by those constraints.
+ * If we have generated a degenerate loop, then add the guard
+ * implied by "bounds" on the outer dimensions, i.e., the guard
+ * that ensures that the single value actually exists.
+ * Since there may also be guards implied by a combination
+ * of these constraints, we first combine them before
+ * deriving the implied constraints.
+ */
+static __isl_give isl_set *add_implied_guards(__isl_take isl_set *guard,
+ int degenerate, __isl_keep isl_basic_set *bounds,
+ __isl_keep isl_ast_build *build)
+{
+ isl_size depth;
+ isl_bool has_stride;
+ isl_space *space;
+ isl_set *dom, *set;
+
+ depth = isl_ast_build_get_depth(build);
+ has_stride = isl_ast_build_has_stride(build, depth);
+ if (depth < 0 || has_stride < 0)
+ return isl_set_free(guard);
+ if (!has_stride && !degenerate)
+ return guard;
+
+ space = isl_basic_set_get_space(bounds);
+ dom = isl_set_universe(space);
+
+ if (degenerate) {
+ bounds = isl_basic_set_copy(bounds);
+ bounds = isl_basic_set_drop_constraints_not_involving_dims(
+ bounds, isl_dim_set, depth, 1);
+ set = isl_set_from_basic_set(bounds);
+ dom = isl_set_intersect(dom, set);
+ }
+
+ if (has_stride) {
+ set = isl_ast_build_get_stride_constraint(build);
+ dom = isl_set_intersect(dom, set);
+ }
+
+ dom = isl_set_eliminate(dom, isl_dim_set, depth, 1);
+ dom = isl_ast_build_compute_gist(build, dom);
+ guard = isl_set_intersect(guard, dom);
+
+ return guard;
+}
+
+/* Update "graft" based on "sub_build" for the degenerate case.
+ *
+ * "build" is the build in which graft->node was created
+ * "sub_build" contains information about the current level itself,
+ * including the single value attained.
+ *
+ * We set the initialization part of the for loop to the single
+ * value attained by the current dimension.
+ * The increment and condition are not strictly needed as they are known
+ * to be "1" and "iterator <= value" respectively.
+ */
+static __isl_give isl_ast_graft *refine_degenerate(
+ __isl_take isl_ast_graft *graft, __isl_keep isl_ast_build *build,
+ __isl_keep isl_ast_build *sub_build)
+{
+ isl_pw_aff *value;
+
+ if (!graft || !sub_build)
+ return isl_ast_graft_free(graft);
+
+ value = isl_pw_aff_copy(sub_build->value);
+
+ graft->node->u.f.init = isl_ast_build_expr_from_pw_aff_internal(build,
+ value);
+ if (!graft->node->u.f.init)
+ return isl_ast_graft_free(graft);
+
+ return graft;
+}
+
+/* Return the intersection of constraints in "list" as a set.
+ */
+static __isl_give isl_set *intersect_constraints(
+ __isl_keep isl_constraint_list *list)
+{
+ int i;
+ isl_size n;
+ isl_basic_set *bset;
+
+ n = isl_constraint_list_n_constraint(list);
+ if (n < 0)
+ return NULL;
+ if (n < 1)
+ isl_die(isl_constraint_list_get_ctx(list), isl_error_internal,
+ "expecting at least one constraint", return NULL);
+
+ bset = isl_basic_set_from_constraint(
+ isl_constraint_list_get_constraint(list, 0));
+ for (i = 1; i < n; ++i) {
+ isl_basic_set *bset_i;
+
+ bset_i = isl_basic_set_from_constraint(
+ isl_constraint_list_get_constraint(list, i));
+ bset = isl_basic_set_intersect(bset, bset_i);
+ }
+
+ return isl_set_from_basic_set(bset);
+}
+
+/* Compute the constraints on the outer dimensions enforced by
+ * graft->node and add those constraints to graft->enforced,
+ * in case the upper bound is expressed as a set "upper".
+ *
+ * In particular, if l(...) is a lower bound in "lower", and
+ *
+ * -a i + f(...) >= 0 or a i <= f(...)
+ *
+ * is an upper bound ocnstraint on the current dimension i,
+ * then the for loop enforces the constraint
+ *
+ * -a l(...) + f(...) >= 0 or a l(...) <= f(...)
+ *
+ * We therefore simply take each lower bound in turn, plug it into
+ * the upper bounds and compute the intersection over all lower bounds.
+ *
+ * If a lower bound is a rational expression, then
+ * isl_basic_set_preimage_multi_aff will force this rational
+ * expression to have only integer values. However, the loop
+ * itself does not enforce this integrality constraint. We therefore
+ * use the ceil of the lower bounds instead of the lower bounds themselves.
+ * Other constraints will make sure that the for loop is only executed
+ * when each of the lower bounds attains an integral value.
+ * In particular, potentially rational values only occur in
+ * lower_bound if the offset is a (seemingly) rational expression,
+ * but then outer conditions will make sure that this rational expression
+ * only attains integer values.
+ */
+static __isl_give isl_ast_graft *set_enforced_from_set(
+ __isl_take isl_ast_graft *graft,
+ __isl_keep isl_pw_aff_list *lower, int pos, __isl_keep isl_set *upper)
+{
+ isl_space *space;
+ isl_basic_set *enforced;
+ isl_pw_multi_aff *pma;
+ int i;
+ isl_size n;
+
+ n = isl_pw_aff_list_n_pw_aff(lower);
+ if (!graft || n < 0)
+ return isl_ast_graft_free(graft);
+
+ space = isl_set_get_space(upper);
+ enforced = isl_basic_set_universe(isl_space_copy(space));
+
+ space = isl_space_map_from_set(space);
+ pma = isl_pw_multi_aff_identity(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+ isl_set *enforced_i;
+ isl_basic_set *hull;
+ isl_pw_multi_aff *pma_i;
+
+ pa = isl_pw_aff_list_get_pw_aff(lower, i);
+ pa = isl_pw_aff_ceil(pa);
+ pma_i = isl_pw_multi_aff_copy(pma);
+ pma_i = isl_pw_multi_aff_set_pw_aff(pma_i, pos, pa);
+ enforced_i = isl_set_copy(upper);
+ enforced_i = isl_set_preimage_pw_multi_aff(enforced_i, pma_i);
+ hull = isl_set_simple_hull(enforced_i);
+ enforced = isl_basic_set_intersect(enforced, hull);
+ }
+
+ isl_pw_multi_aff_free(pma);
+
+ graft = isl_ast_graft_enforce(graft, enforced);
+
+ return graft;
+}
+
+/* Compute the constraints on the outer dimensions enforced by
+ * graft->node and add those constraints to graft->enforced,
+ * in case the upper bound is expressed as
+ * a list of affine expressions "upper".
+ *
+ * The enforced condition is that each lower bound expression is less
+ * than or equal to each upper bound expression.
+ */
+static __isl_give isl_ast_graft *set_enforced_from_list(
+ __isl_take isl_ast_graft *graft,
+ __isl_keep isl_pw_aff_list *lower, __isl_keep isl_pw_aff_list *upper)
+{
+ isl_set *cond;
+ isl_basic_set *enforced;
+
+ lower = isl_pw_aff_list_copy(lower);
+ upper = isl_pw_aff_list_copy(upper);
+ cond = isl_pw_aff_list_le_set(lower, upper);
+ enforced = isl_set_simple_hull(cond);
+ graft = isl_ast_graft_enforce(graft, enforced);
+
+ return graft;
+}
+
+/* Does "aff" have a negative constant term?
+ */
+static isl_bool aff_constant_is_negative(__isl_keep isl_set *set,
+ __isl_keep isl_aff *aff, void *user)
+{
+ isl_bool is_neg;
+ isl_val *v;
+
+ v = isl_aff_get_constant_val(aff);
+ is_neg = isl_val_is_neg(v);
+ isl_val_free(v);
+
+ return is_neg;
+}
+
+/* Does "pa" have a negative constant term over its entire domain?
+ */
+static isl_bool pw_aff_constant_is_negative(__isl_keep isl_pw_aff *pa,
+ void *user)
+{
+ return isl_pw_aff_every_piece(pa, &aff_constant_is_negative, NULL);
+}
+
+/* Does each element in "list" have a negative constant term?
+ */
+static int list_constant_is_negative(__isl_keep isl_pw_aff_list *list)
+{
+ return isl_pw_aff_list_every(list, &pw_aff_constant_is_negative, NULL);
+}
+
+/* Add 1 to each of the elements in "list", where each of these elements
+ * is defined over the internal schedule space of "build".
+ */
+static __isl_give isl_pw_aff_list *list_add_one(
+ __isl_take isl_pw_aff_list *list, __isl_keep isl_ast_build *build)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_aff *aff;
+ isl_pw_aff *one;
+
+ n = isl_pw_aff_list_n_pw_aff(list);
+ if (n < 0)
+ return isl_pw_aff_list_free(list);
+
+ space = isl_ast_build_get_space(build, 1);
+ aff = isl_aff_zero_on_domain(isl_local_space_from_space(space));
+ aff = isl_aff_add_constant_si(aff, 1);
+ one = isl_pw_aff_from_aff(aff);
+
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+ pa = isl_pw_aff_list_get_pw_aff(list, i);
+ pa = isl_pw_aff_add(pa, isl_pw_aff_copy(one));
+ list = isl_pw_aff_list_set_pw_aff(list, i, pa);
+ }
+
+ isl_pw_aff_free(one);
+
+ return list;
+}
+
+/* Set the condition part of the for node graft->node in case
+ * the upper bound is represented as a list of piecewise affine expressions.
+ *
+ * In particular, set the condition to
+ *
+ * iterator <= min(list of upper bounds)
+ *
+ * If each of the upper bounds has a negative constant term, then
+ * set the condition to
+ *
+ * iterator < min(list of (upper bound + 1)s)
+ *
+ */
+static __isl_give isl_ast_graft *set_for_cond_from_list(
+ __isl_take isl_ast_graft *graft, __isl_keep isl_pw_aff_list *list,
+ __isl_keep isl_ast_build *build)
+{
+ int neg;
+ isl_ast_expr *bound, *iterator, *cond;
+ enum isl_ast_expr_op_type type = isl_ast_expr_op_le;
+
+ if (!graft || !list)
+ return isl_ast_graft_free(graft);
+
+ neg = list_constant_is_negative(list);
+ if (neg < 0)
+ return isl_ast_graft_free(graft);
+ list = isl_pw_aff_list_copy(list);
+ if (neg) {
+ list = list_add_one(list, build);
+ type = isl_ast_expr_op_lt;
+ }
+
+ bound = reduce_list(isl_ast_expr_op_min, list, build);
+ iterator = isl_ast_expr_copy(graft->node->u.f.iterator);
+ cond = isl_ast_expr_alloc_binary(type, iterator, bound);
+ graft->node->u.f.cond = cond;
+
+ isl_pw_aff_list_free(list);
+ if (!graft->node->u.f.cond)
+ return isl_ast_graft_free(graft);
+ return graft;
+}
+
+/* Set the condition part of the for node graft->node in case
+ * the upper bound is represented as a set.
+ */
+static __isl_give isl_ast_graft *set_for_cond_from_set(
+ __isl_take isl_ast_graft *graft, __isl_keep isl_set *set,
+ __isl_keep isl_ast_build *build)
+{
+ isl_ast_expr *cond;
+
+ if (!graft)
+ return NULL;
+
+ cond = isl_ast_build_expr_from_set_internal(build, isl_set_copy(set));
+ graft->node->u.f.cond = cond;
+ if (!graft->node->u.f.cond)
+ return isl_ast_graft_free(graft);
+ return graft;
+}
+
+/* Construct an isl_ast_expr for the increment (i.e., stride) of
+ * the current dimension.
+ */
+static __isl_give isl_ast_expr *for_inc(__isl_keep isl_ast_build *build)
+{
+ isl_size depth;
+ isl_val *v;
+ isl_ctx *ctx;
+
+ depth = isl_ast_build_get_depth(build);
+ if (depth < 0)
+ return NULL;
+ ctx = isl_ast_build_get_ctx(build);
+
+ if (!isl_ast_build_has_stride(build, depth))
+ return isl_ast_expr_alloc_int_si(ctx, 1);
+
+ v = isl_ast_build_get_stride(build, depth);
+ return isl_ast_expr_from_val(v);
+}
+
+/* Should we express the loop condition as
+ *
+ * iterator <= min(list of upper bounds)
+ *
+ * or as a conjunction of constraints?
+ *
+ * The first is constructed from a list of upper bounds.
+ * The second is constructed from a set.
+ *
+ * If there are no upper bounds in "constraints", then this could mean
+ * that "domain" simply doesn't have an upper bound or that we didn't
+ * pick any upper bound. In the first case, we want to generate the
+ * loop condition as a(n empty) conjunction of constraints
+ * In the second case, we will compute
+ * a single upper bound from "domain" and so we use the list form.
+ *
+ * If there are upper bounds in "constraints",
+ * then we use the list form iff the atomic_upper_bound option is set.
+ */
+static int use_upper_bound_list(isl_ctx *ctx, int n_upper,
+ __isl_keep isl_set *domain, int depth)
+{
+ if (n_upper > 0)
+ return isl_options_get_ast_build_atomic_upper_bound(ctx);
+ else
+ return isl_set_dim_has_upper_bound(domain, isl_dim_set, depth);
+}
+
+/* Fill in the expressions of the for node in graft->node.
+ *
+ * In particular,
+ * - set the initialization part of the loop to the maximum of the lower bounds
+ * - extract the increment from the stride of the current dimension
+ * - construct the for condition either based on a list of upper bounds
+ * or on a set of upper bound constraints.
+ */
+static __isl_give isl_ast_graft *set_for_node_expressions(
+ __isl_take isl_ast_graft *graft, __isl_keep isl_pw_aff_list *lower,
+ int use_list, __isl_keep isl_pw_aff_list *upper_list,
+ __isl_keep isl_set *upper_set, __isl_keep isl_ast_build *build)
+{
+ isl_ast_node *node;
+
+ if (!graft)
+ return NULL;
+
+ build = isl_ast_build_copy(build);
+
+ node = graft->node;
+ node->u.f.init = reduce_list(isl_ast_expr_op_max, lower, build);
+ node->u.f.inc = for_inc(build);
+
+ if (!node->u.f.init || !node->u.f.inc)
+ graft = isl_ast_graft_free(graft);
+
+ if (use_list)
+ graft = set_for_cond_from_list(graft, upper_list, build);
+ else
+ graft = set_for_cond_from_set(graft, upper_set, build);
+
+ isl_ast_build_free(build);
+
+ return graft;
+}
+
+/* Update "graft" based on "bounds" and "domain" for the generic,
+ * non-degenerate, case.
+ *
+ * "c_lower" and "c_upper" contain the lower and upper bounds
+ * that the loop node should express.
+ * "domain" is the subset of the intersection of the constraints
+ * for which some code is executed.
+ *
+ * There may be zero lower bounds or zero upper bounds in "constraints"
+ * in case the list of constraints was created
+ * based on the atomic option or based on separation with explicit bounds.
+ * In that case, we use "domain" to derive lower and/or upper bounds.
+ *
+ * We first compute a list of one or more lower bounds.
+ *
+ * Then we decide if we want to express the condition as
+ *
+ * iterator <= min(list of upper bounds)
+ *
+ * or as a conjunction of constraints.
+ *
+ * The set of enforced constraints is then computed either based on
+ * a list of upper bounds or on a set of upper bound constraints.
+ * We do not compute any enforced constraints if we were forced
+ * to compute a lower or upper bound using exact_bound. The domains
+ * of the resulting expressions may imply some bounds on outer dimensions
+ * that we do not want to appear in the enforced constraints since
+ * they are not actually enforced by the corresponding code.
+ *
+ * Finally, we fill in the expressions of the for node.
+ */
+static __isl_give isl_ast_graft *refine_generic_bounds(
+ __isl_take isl_ast_graft *graft,
+ __isl_take isl_constraint_list *c_lower,
+ __isl_take isl_constraint_list *c_upper,
+ __isl_keep isl_set *domain, __isl_keep isl_ast_build *build)
+{
+ isl_size depth;
+ isl_ctx *ctx;
+ isl_pw_aff_list *lower;
+ int use_list;
+ isl_set *upper_set = NULL;
+ isl_pw_aff_list *upper_list = NULL;
+ isl_size n_lower, n_upper;
+
+ depth = isl_ast_build_get_depth(build);
+ if (!graft || !c_lower || !c_upper || depth < 0)
+ goto error;
+
+ ctx = isl_ast_graft_get_ctx(graft);
+
+ n_lower = isl_constraint_list_n_constraint(c_lower);
+ n_upper = isl_constraint_list_n_constraint(c_upper);
+ if (n_lower < 0 || n_upper < 0)
+ goto error;
+
+ use_list = use_upper_bound_list(ctx, n_upper, domain, depth);
+
+ lower = lower_bounds(c_lower, depth, domain, build);
+
+ if (use_list)
+ upper_list = upper_bounds(c_upper, depth, domain, build);
+ else if (n_upper > 0)
+ upper_set = intersect_constraints(c_upper);
+ else
+ upper_set = isl_set_universe(isl_set_get_space(domain));
+
+ if (n_lower == 0 || n_upper == 0)
+ ;
+ else if (use_list)
+ graft = set_enforced_from_list(graft, lower, upper_list);
+ else
+ graft = set_enforced_from_set(graft, lower, depth, upper_set);
+
+ graft = set_for_node_expressions(graft, lower, use_list, upper_list,
+ upper_set, build);
+
+ isl_pw_aff_list_free(lower);
+ isl_pw_aff_list_free(upper_list);
+ isl_set_free(upper_set);
+ isl_constraint_list_free(c_lower);
+ isl_constraint_list_free(c_upper);
+
+ return graft;
+error:
+ isl_constraint_list_free(c_lower);
+ isl_constraint_list_free(c_upper);
+ return isl_ast_graft_free(graft);
+}
+
+/* Internal data structure used inside count_constraints to keep
+ * track of the number of constraints that are independent of dimension "pos",
+ * the lower bounds in "pos" and the upper bounds in "pos".
+ */
+struct isl_ast_count_constraints_data {
+ int pos;
+
+ int n_indep;
+ int n_lower;
+ int n_upper;
+};
+
+/* Increment data->n_indep, data->lower or data->upper depending
+ * on whether "c" is independenct of dimensions data->pos,
+ * a lower bound or an upper bound.
+ */
+static isl_stat count_constraints(__isl_take isl_constraint *c, void *user)
+{
+ struct isl_ast_count_constraints_data *data = user;
+
+ if (isl_constraint_is_lower_bound(c, isl_dim_set, data->pos))
+ data->n_lower++;
+ else if (isl_constraint_is_upper_bound(c, isl_dim_set, data->pos))
+ data->n_upper++;
+ else
+ data->n_indep++;
+
+ isl_constraint_free(c);
+
+ return isl_stat_ok;
+}
+
+/* Update "graft" based on "bounds" and "domain" for the generic,
+ * non-degenerate, case.
+ *
+ * "list" respresent the list of bounds that need to be encoded by
+ * the for loop. Only the constraints that involve the iterator
+ * are relevant here. The other constraints are taken care of by
+ * the caller and are included in the generated constraints of "build".
+ * "domain" is the subset of the intersection of the constraints
+ * for which some code is executed.
+ * "build" is the build in which graft->node was created.
+ *
+ * We separate lower bounds, upper bounds and constraints that
+ * are independent of the loop iterator.
+ *
+ * The actual for loop bounds are generated in refine_generic_bounds.
+ */
+static __isl_give isl_ast_graft *refine_generic_split(
+ __isl_take isl_ast_graft *graft, __isl_take isl_constraint_list *list,
+ __isl_keep isl_set *domain, __isl_keep isl_ast_build *build)
+{
+ struct isl_ast_count_constraints_data data;
+ isl_size depth;
+ isl_constraint_list *lower;
+ isl_constraint_list *upper;
+
+ depth = isl_ast_build_get_depth(build);
+ if (depth < 0)
+ list = isl_constraint_list_free(list);
+ if (!list)
+ return isl_ast_graft_free(graft);
+
+ data.pos = depth;
+
+ list = isl_constraint_list_sort(list, &cmp_constraint, &data.pos);
+ if (!list)
+ return isl_ast_graft_free(graft);
+
+ data.n_indep = data.n_lower = data.n_upper = 0;
+ if (isl_constraint_list_foreach(list, &count_constraints, &data) < 0) {
+ isl_constraint_list_free(list);
+ return isl_ast_graft_free(graft);
+ }
+
+ lower = isl_constraint_list_drop(list, 0, data.n_indep);
+ upper = isl_constraint_list_copy(lower);
+ lower = isl_constraint_list_drop(lower, data.n_lower, data.n_upper);
+ upper = isl_constraint_list_drop(upper, 0, data.n_lower);
+
+ return refine_generic_bounds(graft, lower, upper, domain, build);
+}
+
+/* Update "graft" based on "bounds" and "domain" for the generic,
+ * non-degenerate, case.
+ *
+ * "bounds" respresent the bounds that need to be encoded by
+ * the for loop (or a guard around the for loop).
+ * "domain" is the subset of "bounds" for which some code is executed.
+ * "build" is the build in which graft->node was created.
+ *
+ * We break up "bounds" into a list of constraints and continue with
+ * refine_generic_split.
+ */
+static __isl_give isl_ast_graft *refine_generic(
+ __isl_take isl_ast_graft *graft,
+ __isl_keep isl_basic_set *bounds, __isl_keep isl_set *domain,
+ __isl_keep isl_ast_build *build)
+{
+ isl_constraint_list *list;
+
+ if (!build || !graft)
+ return isl_ast_graft_free(graft);
+
+ list = isl_basic_set_get_constraint_list(bounds);
+
+ graft = refine_generic_split(graft, list, domain, build);
+
+ return graft;
+}
+
+/* Create a for node for the current level.
+ *
+ * Mark the for node degenerate if "degenerate" is set.
+ */
+static __isl_give isl_ast_node *create_for(__isl_keep isl_ast_build *build,
+ int degenerate)
+{
+ isl_size depth;
+ isl_id *id;
+ isl_ast_node *node;
+
+ depth = isl_ast_build_get_depth(build);
+ if (depth < 0)
+ return NULL;
+
+ id = isl_ast_build_get_iterator_id(build, depth);
+ node = isl_ast_node_alloc_for(id);
+ if (degenerate)
+ node = isl_ast_node_for_mark_degenerate(node);
+
+ return node;
+}
+
+/* If the ast_build_exploit_nested_bounds option is set, then return
+ * the constraints enforced by all elements in "list".
+ * Otherwise, return the universe.
+ */
+static __isl_give isl_basic_set *extract_shared_enforced(
+ __isl_keep isl_ast_graft_list *list, __isl_keep isl_ast_build *build)
+{
+ isl_ctx *ctx;
+ isl_space *space;
+
+ if (!list)
+ return NULL;
+
+ ctx = isl_ast_graft_list_get_ctx(list);
+ if (isl_options_get_ast_build_exploit_nested_bounds(ctx))
+ return isl_ast_graft_list_extract_shared_enforced(list, build);
+
+ space = isl_ast_build_get_space(build, 1);
+ return isl_basic_set_universe(space);
+}
+
+/* Return the pending constraints of "build" that are not already taken
+ * care of (by a combination of "enforced" and the generated constraints
+ * of "build").
+ */
+static __isl_give isl_set *extract_pending(__isl_keep isl_ast_build *build,
+ __isl_keep isl_basic_set *enforced)
+{
+ isl_set *guard, *context;
+
+ guard = isl_ast_build_get_pending(build);
+ context = isl_set_from_basic_set(isl_basic_set_copy(enforced));
+ context = isl_set_intersect(context,
+ isl_ast_build_get_generated(build));
+ return isl_set_gist(guard, context);
+}
+
+/* Create an AST node for the current dimension based on
+ * the schedule domain "bounds" and return the node encapsulated
+ * in an isl_ast_graft.
+ *
+ * "executed" is the current inverse schedule, taking into account
+ * the bounds in "bounds"
+ * "domain" is the domain of "executed", with inner dimensions projected out.
+ * It may be a strict subset of "bounds" in case "bounds" was created
+ * based on the atomic option or based on separation with explicit bounds.
+ *
+ * "domain" may satisfy additional equalities that result
+ * from intersecting "executed" with "bounds" in add_node.
+ * It may also satisfy some global constraints that were dropped out because
+ * we performed separation with explicit bounds.
+ * The very first step is then to copy these constraints to "bounds".
+ *
+ * Since we may be calling before_each_for and after_each_for
+ * callbacks, we record the current inverse schedule in the build.
+ *
+ * We consider three builds,
+ * "build" is the one in which the current level is created,
+ * "body_build" is the build in which the next level is created,
+ * "sub_build" is essentially the same as "body_build", except that
+ * the depth has not been increased yet.
+ *
+ * "build" already contains information (in strides and offsets)
+ * about the strides at the current level, but this information is not
+ * reflected in the build->domain.
+ * We first add this information and the "bounds" to the sub_build->domain.
+ * isl_ast_build_set_loop_bounds adds the stride information and
+ * checks whether the current dimension attains
+ * only a single value and whether this single value can be represented using
+ * a single affine expression.
+ * In the first case, the current level is considered "degenerate".
+ * In the second, sub-case, the current level is considered "eliminated".
+ * Eliminated levels don't need to be reflected in the AST since we can
+ * simply plug in the affine expression. For degenerate, but non-eliminated,
+ * levels, we do introduce a for node, but mark is as degenerate so that
+ * it can be printed as an assignment of the single value to the loop
+ * "iterator".
+ *
+ * If the current level is eliminated, we explicitly plug in the value
+ * for the current level found by isl_ast_build_set_loop_bounds in the
+ * inverse schedule. This ensures that if we are working on a slice
+ * of the domain based on information available in the inverse schedule
+ * and the build domain, that then this information is also reflected
+ * in the inverse schedule. This operation also eliminates the current
+ * dimension from the inverse schedule making sure no inner dimensions depend
+ * on the current dimension. Otherwise, we create a for node, marking
+ * it degenerate if appropriate. The initial for node is still incomplete
+ * and will be completed in either refine_degenerate or refine_generic.
+ *
+ * We then generate a sequence of grafts for the next level,
+ * create a surrounding graft for the current level and insert
+ * the for node we created (if the current level is not eliminated).
+ * Before creating a graft for the current level, we first extract
+ * hoistable constraints from the child guards and combine them
+ * with the pending constraints in the build. These constraints
+ * are used to simplify the child guards and then added to the guard
+ * of the current graft to ensure that they will be generated.
+ * If the hoisted guard is a disjunction, then we use it directly
+ * to gist the guards on the children before intersect it with the
+ * pending constraints. We do so because this disjunction is typically
+ * identical to the guards on the children such that these guards
+ * can be effectively removed completely. After the intersection,
+ * the gist operation would have a harder time figuring this out.
+ *
+ * Finally, we set the bounds of the for loop in either
+ * refine_degenerate or refine_generic.
+ * We do so in a context where the pending constraints of the build
+ * have been replaced by the guard of the current graft.
+ */
+static __isl_give isl_ast_graft *create_node_scaled(
+ __isl_take isl_union_map *executed,
+ __isl_take isl_basic_set *bounds, __isl_take isl_set *domain,
+ __isl_take isl_ast_build *build)
+{
+ isl_size depth;
+ int degenerate;
+ isl_bool eliminated;
+ isl_size n;
+ isl_basic_set *hull;
+ isl_basic_set *enforced;
+ isl_set *guard, *hoisted;
+ isl_ast_node *node = NULL;
+ isl_ast_graft *graft;
+ isl_ast_graft_list *children;
+ isl_ast_build *sub_build;
+ isl_ast_build *body_build;
+
+ domain = isl_ast_build_eliminate_divs(build, domain);
+ domain = isl_set_detect_equalities(domain);
+ hull = isl_set_unshifted_simple_hull(isl_set_copy(domain));
+ bounds = isl_basic_set_intersect(bounds, hull);
+ build = isl_ast_build_set_executed(build, isl_union_map_copy(executed));
+
+ depth = isl_ast_build_get_depth(build);
+ if (depth < 0)
+ build = isl_ast_build_free(build);
+ sub_build = isl_ast_build_copy(build);
+ bounds = isl_basic_set_remove_redundancies(bounds);
+ bounds = isl_ast_build_specialize_basic_set(sub_build, bounds);
+ sub_build = isl_ast_build_set_loop_bounds(sub_build,
+ isl_basic_set_copy(bounds));
+ degenerate = isl_ast_build_has_value(sub_build);
+ eliminated = isl_ast_build_has_affine_value(sub_build, depth);
+ if (degenerate < 0 || eliminated < 0)
+ executed = isl_union_map_free(executed);
+ if (!degenerate)
+ bounds = isl_ast_build_compute_gist_basic_set(build, bounds);
+ sub_build = isl_ast_build_set_pending_generated(sub_build,
+ isl_basic_set_copy(bounds));
+ if (eliminated)
+ executed = plug_in_values(executed, sub_build);
+ else
+ node = create_for(build, degenerate);
+
+ body_build = isl_ast_build_copy(sub_build);
+ body_build = isl_ast_build_increase_depth(body_build);
+ if (!eliminated)
+ node = before_each_for(node, body_build);
+ children = generate_next_level(executed,
+ isl_ast_build_copy(body_build));
+
+ enforced = extract_shared_enforced(children, build);
+ guard = extract_pending(sub_build, enforced);
+ hoisted = isl_ast_graft_list_extract_hoistable_guard(children, build);
+ n = isl_set_n_basic_set(hoisted);
+ if (n < 0)
+ children = isl_ast_graft_list_free(children);
+ if (n > 1)
+ children = isl_ast_graft_list_gist_guards(children,
+ isl_set_copy(hoisted));
+ guard = isl_set_intersect(guard, hoisted);
+ if (!eliminated)
+ guard = add_implied_guards(guard, degenerate, bounds, build);
+
+ graft = isl_ast_graft_alloc_from_children(children,
+ isl_set_copy(guard), enforced, build, sub_build);
+
+ if (!eliminated) {
+ isl_ast_build *for_build;
+
+ graft = isl_ast_graft_insert_for(graft, node);
+ for_build = isl_ast_build_copy(build);
+ for_build = isl_ast_build_replace_pending_by_guard(for_build,
+ isl_set_copy(guard));
+ if (degenerate)
+ graft = refine_degenerate(graft, for_build, sub_build);
+ else
+ graft = refine_generic(graft, bounds,
+ domain, for_build);
+ isl_ast_build_free(for_build);
+ }
+ isl_set_free(guard);
+ if (!eliminated)
+ graft = after_each_for(graft, body_build);
+
+ isl_ast_build_free(body_build);
+ isl_ast_build_free(sub_build);
+ isl_ast_build_free(build);
+ isl_basic_set_free(bounds);
+ isl_set_free(domain);
+
+ return graft;
+}
+
+/* Internal data structure for checking if all constraints involving
+ * the input dimension "depth" are such that the other coefficients
+ * are multiples of "m", reducing "m" if they are not.
+ * If "m" is reduced all the way down to "1", then the check has failed
+ * and we break out of the iteration.
+ */
+struct isl_check_scaled_data {
+ int depth;
+ isl_val *m;
+};
+
+/* If constraint "c" involves the input dimension data->depth,
+ * then make sure that all the other coefficients are multiples of data->m,
+ * reducing data->m if needed.
+ * Break out of the iteration if data->m has become equal to "1".
+ */
+static isl_stat constraint_check_scaled(__isl_take isl_constraint *c,
+ void *user)
+{
+ struct isl_check_scaled_data *data = user;
+ int i, j;
+ isl_size n;
+ enum isl_dim_type t[] = { isl_dim_param, isl_dim_in, isl_dim_out,
+ isl_dim_div };
+
+ if (!isl_constraint_involves_dims(c, isl_dim_in, data->depth, 1)) {
+ isl_constraint_free(c);
+ return isl_stat_ok;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ n = isl_constraint_dim(c, t[i]);
+ if (n < 0)
+ break;
+ for (j = 0; j < n; ++j) {
+ isl_val *d;
+
+ if (t[i] == isl_dim_in && j == data->depth)
+ continue;
+ if (!isl_constraint_involves_dims(c, t[i], j, 1))
+ continue;
+ d = isl_constraint_get_coefficient_val(c, t[i], j);
+ data->m = isl_val_gcd(data->m, d);
+ if (isl_val_is_one(data->m))
+ break;
+ }
+ if (j < n)
+ break;
+ }
+
+ isl_constraint_free(c);
+
+ return i < 4 ? isl_stat_error : isl_stat_ok;
+}
+
+/* For each constraint of "bmap" that involves the input dimension data->depth,
+ * make sure that all the other coefficients are multiples of data->m,
+ * reducing data->m if needed.
+ * Break out of the iteration if data->m has become equal to "1".
+ */
+static isl_stat basic_map_check_scaled(__isl_take isl_basic_map *bmap,
+ void *user)
+{
+ isl_stat r;
+
+ r = isl_basic_map_foreach_constraint(bmap,
+ &constraint_check_scaled, user);
+ isl_basic_map_free(bmap);
+
+ return r;
+}
+
+/* For each constraint of "map" that involves the input dimension data->depth,
+ * make sure that all the other coefficients are multiples of data->m,
+ * reducing data->m if needed.
+ * Break out of the iteration if data->m has become equal to "1".
+ */
+static isl_stat map_check_scaled(__isl_take isl_map *map, void *user)
+{
+ isl_stat r;
+
+ r = isl_map_foreach_basic_map(map, &basic_map_check_scaled, user);
+ isl_map_free(map);
+
+ return r;
+}
+
+/* Create an AST node for the current dimension based on
+ * the schedule domain "bounds" and return the node encapsulated
+ * in an isl_ast_graft.
+ *
+ * "executed" is the current inverse schedule, taking into account
+ * the bounds in "bounds"
+ * "domain" is the domain of "executed", with inner dimensions projected out.
+ *
+ *
+ * Before moving on to the actual AST node construction in create_node_scaled,
+ * we first check if the current dimension is strided and if we can scale
+ * down this stride. Note that we only do this if the ast_build_scale_strides
+ * option is set.
+ *
+ * In particular, let the current dimension take on values
+ *
+ * f + s a
+ *
+ * with a an integer. We check if we can find an integer m that (obviously)
+ * divides both f and s.
+ *
+ * If so, we check if the current dimension only appears in constraints
+ * where the coefficients of the other variables are multiples of m.
+ * We perform this extra check to avoid the risk of introducing
+ * divisions by scaling down the current dimension.
+ *
+ * If so, we scale the current dimension down by a factor of m.
+ * That is, we plug in
+ *
+ * i = m i' (1)
+ *
+ * Note that in principle we could always scale down strided loops
+ * by plugging in
+ *
+ * i = f + s i'
+ *
+ * but this may result in i' taking on larger values than the original i,
+ * due to the shift by "f".
+ * By constrast, the scaling in (1) can only reduce the (absolute) value "i".
+ */
+static __isl_give isl_ast_graft *create_node(__isl_take isl_union_map *executed,
+ __isl_take isl_basic_set *bounds, __isl_take isl_set *domain,
+ __isl_take isl_ast_build *build)
+{
+ struct isl_check_scaled_data data;
+ isl_size depth;
+ isl_ctx *ctx;
+ isl_aff *offset;
+ isl_val *d;
+
+ ctx = isl_ast_build_get_ctx(build);
+ if (!isl_options_get_ast_build_scale_strides(ctx))
+ return create_node_scaled(executed, bounds, domain, build);
+
+ depth = isl_ast_build_get_depth(build);
+ if (depth < 0)
+ build = isl_ast_build_free(build);
+ data.depth = depth;
+ if (!isl_ast_build_has_stride(build, data.depth))
+ return create_node_scaled(executed, bounds, domain, build);
+
+ offset = isl_ast_build_get_offset(build, data.depth);
+ data.m = isl_ast_build_get_stride(build, data.depth);
+ if (!data.m)
+ offset = isl_aff_free(offset);
+ offset = isl_aff_scale_down_val(offset, isl_val_copy(data.m));
+ d = isl_aff_get_denominator_val(offset);
+ if (!d)
+ executed = isl_union_map_free(executed);
+
+ if (executed && isl_val_is_divisible_by(data.m, d))
+ data.m = isl_val_div(data.m, d);
+ else {
+ data.m = isl_val_set_si(data.m, 1);
+ isl_val_free(d);
+ }
+
+ if (!isl_val_is_one(data.m)) {
+ if (isl_union_map_foreach_map(executed, &map_check_scaled,
+ &data) < 0 &&
+ !isl_val_is_one(data.m))
+ executed = isl_union_map_free(executed);
+ }
+
+ if (!isl_val_is_one(data.m)) {
+ isl_space *space;
+ isl_multi_aff *ma;
+ isl_aff *aff;
+ isl_map *map;
+ isl_union_map *umap;
+
+ space = isl_ast_build_get_space(build, 1);
+ space = isl_space_map_from_set(space);
+ ma = isl_multi_aff_identity(space);
+ aff = isl_multi_aff_get_aff(ma, data.depth);
+ aff = isl_aff_scale_val(aff, isl_val_copy(data.m));
+ ma = isl_multi_aff_set_aff(ma, data.depth, aff);
+
+ bounds = isl_basic_set_preimage_multi_aff(bounds,
+ isl_multi_aff_copy(ma));
+ domain = isl_set_preimage_multi_aff(domain,
+ isl_multi_aff_copy(ma));
+ map = isl_map_reverse(isl_map_from_multi_aff(ma));
+ umap = isl_union_map_from_map(map);
+ executed = isl_union_map_apply_domain(executed,
+ isl_union_map_copy(umap));
+ build = isl_ast_build_scale_down(build, isl_val_copy(data.m),
+ umap);
+ }
+ isl_aff_free(offset);
+ isl_val_free(data.m);
+
+ return create_node_scaled(executed, bounds, domain, build);
+}
+
+/* Add the basic set to the list that "user" points to.
+ */
+static isl_stat collect_basic_set(__isl_take isl_basic_set *bset, void *user)
+{
+ isl_basic_set_list **list = user;
+
+ *list = isl_basic_set_list_add(*list, bset);
+
+ return isl_stat_ok;
+}
+
+/* Extract the basic sets of "set" and collect them in an isl_basic_set_list.
+ */
+static __isl_give isl_basic_set_list *isl_basic_set_list_from_set(
+ __isl_take isl_set *set)
+{
+ isl_size n;
+ isl_ctx *ctx;
+ isl_basic_set_list *list;
+
+ n = isl_set_n_basic_set(set);
+ if (n < 0)
+ set = isl_set_free(set);
+ if (!set)
+ return NULL;
+
+ ctx = isl_set_get_ctx(set);
+
+ list = isl_basic_set_list_alloc(ctx, n);
+ if (isl_set_foreach_basic_set(set, &collect_basic_set, &list) < 0)
+ list = isl_basic_set_list_free(list);
+
+ isl_set_free(set);
+ return list;
+}
+
+/* Generate code for the schedule domain "bounds"
+ * and add the result to "list".
+ *
+ * We mainly detect strides here and check if the bounds do not
+ * conflict with the current build domain
+ * and then pass over control to create_node.
+ *
+ * "bounds" reflects the bounds on the current dimension and possibly
+ * some extra conditions on outer dimensions.
+ * It does not, however, include any divs involving the current dimension,
+ * so it does not capture any stride constraints.
+ * We therefore need to compute that part of the schedule domain that
+ * intersects with "bounds" and derive the strides from the result.
+ */
+static __isl_give isl_ast_graft_list *add_node(
+ __isl_take isl_ast_graft_list *list, __isl_take isl_union_map *executed,
+ __isl_take isl_basic_set *bounds, __isl_take isl_ast_build *build)
+{
+ isl_ast_graft *graft;
+ isl_set *domain = NULL;
+ isl_union_set *uset;
+ int empty, disjoint;
+
+ uset = isl_union_set_from_basic_set(isl_basic_set_copy(bounds));
+ executed = isl_union_map_intersect_domain(executed, uset);
+ empty = isl_union_map_is_empty(executed);
+ if (empty < 0)
+ goto error;
+ if (empty)
+ goto done;
+
+ uset = isl_union_map_domain(isl_union_map_copy(executed));
+ domain = isl_set_from_union_set(uset);
+ domain = isl_ast_build_specialize(build, domain);
+
+ domain = isl_set_compute_divs(domain);
+ domain = isl_ast_build_eliminate_inner(build, domain);
+ disjoint = isl_set_is_disjoint(domain, build->domain);
+ if (disjoint < 0)
+ goto error;
+ if (disjoint)
+ goto done;
+
+ build = isl_ast_build_detect_strides(build, isl_set_copy(domain));
+
+ graft = create_node(executed, bounds, domain,
+ isl_ast_build_copy(build));
+ list = isl_ast_graft_list_add(list, graft);
+ isl_ast_build_free(build);
+ return list;
+error:
+ list = isl_ast_graft_list_free(list);
+done:
+ isl_set_free(domain);
+ isl_basic_set_free(bounds);
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+ return list;
+}
+
+/* Does any element of i follow or coincide with any element of j
+ * at the current depth for equal values of the outer dimensions?
+ */
+static isl_bool domain_follows_at_depth(__isl_keep isl_basic_set *i,
+ __isl_keep isl_basic_set *j, void *user)
+{
+ int depth = *(int *) user;
+ isl_basic_map *test;
+ isl_bool empty;
+ int l;
+
+ test = isl_basic_map_from_domain_and_range(isl_basic_set_copy(i),
+ isl_basic_set_copy(j));
+ for (l = 0; l < depth; ++l)
+ test = isl_basic_map_equate(test, isl_dim_in, l,
+ isl_dim_out, l);
+ test = isl_basic_map_order_ge(test, isl_dim_in, depth,
+ isl_dim_out, depth);
+ empty = isl_basic_map_is_empty(test);
+ isl_basic_map_free(test);
+
+ return isl_bool_not(empty);
+}
+
+/* Split up each element of "list" into a part that is related to "bset"
+ * according to "gt" and a part that is not.
+ * Return a list that consist of "bset" and all the pieces.
+ */
+static __isl_give isl_basic_set_list *add_split_on(
+ __isl_take isl_basic_set_list *list, __isl_take isl_basic_set *bset,
+ __isl_keep isl_basic_map *gt)
+{
+ int i;
+ isl_size n;
+ isl_basic_set_list *res;
+
+ n = isl_basic_set_list_n_basic_set(list);
+ if (n < 0)
+ bset = isl_basic_set_free(bset);
+
+ gt = isl_basic_map_copy(gt);
+ gt = isl_basic_map_intersect_domain(gt, isl_basic_set_copy(bset));
+ res = isl_basic_set_list_from_basic_set(bset);
+ for (i = 0; res && i < n; ++i) {
+ isl_basic_set *bset;
+ isl_set *set1, *set2;
+ isl_basic_map *bmap;
+ int empty;
+
+ bset = isl_basic_set_list_get_basic_set(list, i);
+ bmap = isl_basic_map_copy(gt);
+ bmap = isl_basic_map_intersect_range(bmap, bset);
+ bset = isl_basic_map_range(bmap);
+ empty = isl_basic_set_is_empty(bset);
+ if (empty < 0)
+ res = isl_basic_set_list_free(res);
+ if (empty) {
+ isl_basic_set_free(bset);
+ bset = isl_basic_set_list_get_basic_set(list, i);
+ res = isl_basic_set_list_add(res, bset);
+ continue;
+ }
+
+ res = isl_basic_set_list_add(res, isl_basic_set_copy(bset));
+ set1 = isl_set_from_basic_set(bset);
+ bset = isl_basic_set_list_get_basic_set(list, i);
+ set2 = isl_set_from_basic_set(bset);
+ set1 = isl_set_subtract(set2, set1);
+ set1 = isl_set_make_disjoint(set1);
+
+ res = isl_basic_set_list_concat(res,
+ isl_basic_set_list_from_set(set1));
+ }
+ isl_basic_map_free(gt);
+ isl_basic_set_list_free(list);
+ return res;
+}
+
+static __isl_give isl_ast_graft_list *generate_sorted_domains(
+ __isl_keep isl_basic_set_list *domain_list,
+ __isl_keep isl_union_map *executed,
+ __isl_keep isl_ast_build *build);
+
+/* Internal data structure for add_nodes.
+ *
+ * "executed" and "build" are extra arguments to be passed to add_node.
+ * "list" collects the results.
+ */
+struct isl_add_nodes_data {
+ isl_union_map *executed;
+ isl_ast_build *build;
+
+ isl_ast_graft_list *list;
+};
+
+/* Generate code for the schedule domains in "scc"
+ * and add the results to "list".
+ *
+ * The domains in "scc" form a strongly connected component in the ordering.
+ * If the number of domains in "scc" is larger than 1, then this means
+ * that we cannot determine a valid ordering for the domains in the component.
+ * This should be fairly rare because the individual domains
+ * have been made disjoint first.
+ * The problem is that the domains may be integrally disjoint but not
+ * rationally disjoint. For example, we may have domains
+ *
+ * { [i,i] : 0 <= i <= 1 } and { [i,1-i] : 0 <= i <= 1 }
+ *
+ * These two domains have an empty intersection, but their rational
+ * relaxations do intersect. It is impossible to order these domains
+ * in the second dimension because the first should be ordered before
+ * the second for outer dimension equal to 0, while it should be ordered
+ * after for outer dimension equal to 1.
+ *
+ * This may happen in particular in case of unrolling since the domain
+ * of each slice is replaced by its simple hull.
+ *
+ * For each basic set i in "scc" and for each of the following basic sets j,
+ * we split off that part of the basic set i that shares the outer dimensions
+ * with j and lies before j in the current dimension.
+ * We collect all the pieces in a new list that replaces "scc".
+ *
+ * While the elements in "scc" should be disjoint, we double-check
+ * this property to avoid running into an infinite recursion in case
+ * they intersect due to some internal error.
+ */
+static isl_stat add_nodes(__isl_take isl_basic_set_list *scc, void *user)
+{
+ struct isl_add_nodes_data *data = user;
+ int i;
+ isl_size depth;
+ isl_size n;
+ isl_basic_set *bset, *first;
+ isl_basic_set_list *list;
+ isl_space *space;
+ isl_basic_map *gt;
+
+ n = isl_basic_set_list_n_basic_set(scc);
+ if (n < 0)
+ goto error;
+ bset = isl_basic_set_list_get_basic_set(scc, 0);
+ if (n == 1) {
+ isl_basic_set_list_free(scc);
+ data->list = add_node(data->list,
+ isl_union_map_copy(data->executed), bset,
+ isl_ast_build_copy(data->build));
+ return data->list ? isl_stat_ok : isl_stat_error;
+ }
+
+ depth = isl_ast_build_get_depth(data->build);
+ if (depth < 0)
+ bset = isl_basic_set_free(bset);
+ space = isl_basic_set_get_space(bset);
+ space = isl_space_map_from_set(space);
+ gt = isl_basic_map_universe(space);
+ for (i = 0; i < depth; ++i)
+ gt = isl_basic_map_equate(gt, isl_dim_in, i, isl_dim_out, i);
+ gt = isl_basic_map_order_gt(gt, isl_dim_in, depth, isl_dim_out, depth);
+
+ first = isl_basic_set_copy(bset);
+ list = isl_basic_set_list_from_basic_set(bset);
+ for (i = 1; i < n; ++i) {
+ int disjoint;
+
+ bset = isl_basic_set_list_get_basic_set(scc, i);
+
+ disjoint = isl_basic_set_is_disjoint(bset, first);
+ if (disjoint < 0)
+ list = isl_basic_set_list_free(list);
+ else if (!disjoint)
+ isl_die(isl_basic_set_list_get_ctx(scc),
+ isl_error_internal,
+ "basic sets in scc are assumed to be disjoint",
+ list = isl_basic_set_list_free(list));
+
+ list = add_split_on(list, bset, gt);
+ }
+ isl_basic_set_free(first);
+ isl_basic_map_free(gt);
+ isl_basic_set_list_free(scc);
+ scc = list;
+ data->list = isl_ast_graft_list_concat(data->list,
+ generate_sorted_domains(scc, data->executed, data->build));
+ isl_basic_set_list_free(scc);
+
+ return data->list ? isl_stat_ok : isl_stat_error;
+error:
+ isl_basic_set_list_free(scc);
+ return isl_stat_error;
+}
+
+/* Sort the domains in "domain_list" according to the execution order
+ * at the current depth (for equal values of the outer dimensions),
+ * generate code for each of them, collecting the results in a list.
+ * If no code is generated (because the intersection of the inverse schedule
+ * with the domains turns out to be empty), then an empty list is returned.
+ *
+ * The caller is responsible for ensuring that the basic sets in "domain_list"
+ * are pair-wise disjoint. It can, however, in principle happen that
+ * two basic sets should be ordered one way for one value of the outer
+ * dimensions and the other way for some other value of the outer dimensions.
+ * We therefore play safe and look for strongly connected components.
+ * The function add_nodes takes care of handling non-trivial components.
+ */
+static __isl_give isl_ast_graft_list *generate_sorted_domains(
+ __isl_keep isl_basic_set_list *domain_list,
+ __isl_keep isl_union_map *executed, __isl_keep isl_ast_build *build)
+{
+ isl_ctx *ctx;
+ struct isl_add_nodes_data data;
+ isl_size depth;
+ isl_size n;
+
+ n = isl_basic_set_list_n_basic_set(domain_list);
+ if (n < 0)
+ return NULL;
+
+ ctx = isl_basic_set_list_get_ctx(domain_list);
+ data.list = isl_ast_graft_list_alloc(ctx, n);
+ if (n == 0)
+ return data.list;
+ if (n == 1)
+ return add_node(data.list, isl_union_map_copy(executed),
+ isl_basic_set_list_get_basic_set(domain_list, 0),
+ isl_ast_build_copy(build));
+
+ depth = isl_ast_build_get_depth(build);
+ data.executed = executed;
+ data.build = build;
+ if (depth < 0 || isl_basic_set_list_foreach_scc(domain_list,
+ &domain_follows_at_depth, &depth,
+ &add_nodes, &data) < 0)
+ data.list = isl_ast_graft_list_free(data.list);
+
+ return data.list;
+}
+
+/* Do i and j share any values for the outer dimensions?
+ */
+static isl_bool shared_outer(__isl_keep isl_basic_set *i,
+ __isl_keep isl_basic_set *j, void *user)
+{
+ int depth = *(int *) user;
+ isl_basic_map *test;
+ isl_bool empty;
+ int l;
+
+ test = isl_basic_map_from_domain_and_range(isl_basic_set_copy(i),
+ isl_basic_set_copy(j));
+ for (l = 0; l < depth; ++l)
+ test = isl_basic_map_equate(test, isl_dim_in, l,
+ isl_dim_out, l);
+ empty = isl_basic_map_is_empty(test);
+ isl_basic_map_free(test);
+
+ return isl_bool_not(empty);
+}
+
+/* Internal data structure for generate_sorted_domains_wrap.
+ *
+ * "n" is the total number of basic sets
+ * "executed" and "build" are extra arguments to be passed
+ * to generate_sorted_domains.
+ *
+ * "single" is set to 1 by generate_sorted_domains_wrap if there
+ * is only a single component.
+ * "list" collects the results.
+ */
+struct isl_ast_generate_parallel_domains_data {
+ isl_size n;
+ isl_union_map *executed;
+ isl_ast_build *build;
+
+ int single;
+ isl_ast_graft_list *list;
+};
+
+/* Call generate_sorted_domains on "scc", fuse the result into a list
+ * with either zero or one graft and collect the these single element
+ * lists into data->list.
+ *
+ * If there is only one component, i.e., if the number of basic sets
+ * in the current component is equal to the total number of basic sets,
+ * then data->single is set to 1 and the result of generate_sorted_domains
+ * is not fused.
+ */
+static isl_stat generate_sorted_domains_wrap(__isl_take isl_basic_set_list *scc,
+ void *user)
+{
+ struct isl_ast_generate_parallel_domains_data *data = user;
+ isl_ast_graft_list *list;
+ isl_size n;
+
+ n = isl_basic_set_list_n_basic_set(scc);
+ if (n < 0)
+ scc = isl_basic_set_list_free(scc);
+ list = generate_sorted_domains(scc, data->executed, data->build);
+ data->single = n == data->n;
+ if (!data->single)
+ list = isl_ast_graft_list_fuse(list, data->build);
+ if (!data->list)
+ data->list = list;
+ else
+ data->list = isl_ast_graft_list_concat(data->list, list);
+
+ isl_basic_set_list_free(scc);
+ if (!data->list)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Look for any (weakly connected) components in the "domain_list"
+ * of domains that share some values of the outer dimensions.
+ * That is, domains in different components do not share any values
+ * of the outer dimensions. This means that these components
+ * can be freely reordered.
+ * Within each of the components, we sort the domains according
+ * to the execution order at the current depth.
+ *
+ * If there is more than one component, then generate_sorted_domains_wrap
+ * fuses the result of each call to generate_sorted_domains
+ * into a list with either zero or one graft and collects these (at most)
+ * single element lists into a bigger list. This means that the elements of the
+ * final list can be freely reordered. In particular, we sort them
+ * according to an arbitrary but fixed ordering to ease merging of
+ * graft lists from different components.
+ */
+static __isl_give isl_ast_graft_list *generate_parallel_domains(
+ __isl_keep isl_basic_set_list *domain_list,
+ __isl_keep isl_union_map *executed, __isl_keep isl_ast_build *build)
+{
+ isl_size depth;
+ struct isl_ast_generate_parallel_domains_data data;
+
+ data.n = isl_basic_set_list_n_basic_set(domain_list);
+ if (data.n < 0)
+ return NULL;
+
+ if (data.n <= 1)
+ return generate_sorted_domains(domain_list, executed, build);
+
+ depth = isl_ast_build_get_depth(build);
+ if (depth < 0)
+ return NULL;
+ data.list = NULL;
+ data.executed = executed;
+ data.build = build;
+ data.single = 0;
+ if (isl_basic_set_list_foreach_scc(domain_list, &shared_outer, &depth,
+ &generate_sorted_domains_wrap,
+ &data) < 0)
+ data.list = isl_ast_graft_list_free(data.list);
+
+ if (!data.single)
+ data.list = isl_ast_graft_list_sort_guard(data.list);
+
+ return data.list;
+}
+
+/* Internal data for separate_domain.
+ *
+ * "explicit" is set if we only want to use explicit bounds.
+ *
+ * "domain" collects the separated domains.
+ */
+struct isl_separate_domain_data {
+ isl_ast_build *build;
+ int explicit;
+ isl_set *domain;
+};
+
+/* Extract implicit bounds on the current dimension for the executed "map".
+ *
+ * The domain of "map" may involve inner dimensions, so we
+ * need to eliminate them.
+ */
+static __isl_give isl_set *implicit_bounds(__isl_take isl_map *map,
+ __isl_keep isl_ast_build *build)
+{
+ isl_set *domain;
+
+ domain = isl_map_domain(map);
+ domain = isl_ast_build_eliminate(build, domain);
+
+ return domain;
+}
+
+/* Extract explicit bounds on the current dimension for the executed "map".
+ *
+ * Rather than eliminating the inner dimensions as in implicit_bounds,
+ * we simply drop any constraints involving those inner dimensions.
+ * The idea is that most bounds that are implied by constraints on the
+ * inner dimensions will be enforced by for loops and not by explicit guards.
+ * There is then no need to separate along those bounds.
+ */
+static __isl_give isl_set *explicit_bounds(__isl_take isl_map *map,
+ __isl_keep isl_ast_build *build)
+{
+ isl_set *domain;
+ isl_size depth;
+ isl_size dim;
+
+ depth = isl_ast_build_get_depth(build);
+ dim = isl_map_dim(map, isl_dim_out);
+ if (depth < 0 || dim < 0)
+ return isl_map_domain(isl_map_free(map));
+ map = isl_map_drop_constraints_involving_dims(map, isl_dim_out, 0, dim);
+
+ domain = isl_map_domain(map);
+ dim = isl_set_dim(domain, isl_dim_set);
+ domain = isl_set_detect_equalities(domain);
+ domain = isl_set_drop_constraints_involving_dims(domain,
+ isl_dim_set, depth + 1, dim - (depth + 1));
+ domain = isl_set_remove_divs_involving_dims(domain,
+ isl_dim_set, depth, 1);
+ domain = isl_set_remove_unknown_divs(domain);
+
+ return domain;
+}
+
+/* Split data->domain into pieces that intersect with the range of "map"
+ * and pieces that do not intersect with the range of "map"
+ * and then add that part of the range of "map" that does not intersect
+ * with data->domain.
+ */
+static isl_stat separate_domain(__isl_take isl_map *map, void *user)
+{
+ struct isl_separate_domain_data *data = user;
+ isl_set *domain;
+ isl_set *d1, *d2;
+
+ if (data->explicit)
+ domain = explicit_bounds(map, data->build);
+ else
+ domain = implicit_bounds(map, data->build);
+
+ domain = isl_set_coalesce(domain);
+ domain = isl_set_make_disjoint(domain);
+ d1 = isl_set_subtract(isl_set_copy(domain), isl_set_copy(data->domain));
+ d2 = isl_set_subtract(isl_set_copy(data->domain), isl_set_copy(domain));
+ data->domain = isl_set_intersect(data->domain, domain);
+ data->domain = isl_set_union(data->domain, d1);
+ data->domain = isl_set_union(data->domain, d2);
+
+ return isl_stat_ok;
+}
+
+/* Separate the schedule domains of "executed".
+ *
+ * That is, break up the domain of "executed" into basic sets,
+ * such that for each basic set S, every element in S is associated with
+ * the same domain spaces.
+ *
+ * "space" is the (single) domain space of "executed".
+ */
+static __isl_give isl_set *separate_schedule_domains(
+ __isl_take isl_space *space, __isl_take isl_union_map *executed,
+ __isl_keep isl_ast_build *build)
+{
+ struct isl_separate_domain_data data = { build };
+ isl_ctx *ctx;
+
+ ctx = isl_ast_build_get_ctx(build);
+ data.explicit = isl_options_get_ast_build_separation_bounds(ctx) ==
+ ISL_AST_BUILD_SEPARATION_BOUNDS_EXPLICIT;
+ data.domain = isl_set_empty(space);
+ if (isl_union_map_foreach_map(executed, &separate_domain, &data) < 0)
+ data.domain = isl_set_free(data.domain);
+
+ isl_union_map_free(executed);
+ return data.domain;
+}
+
+/* Temporary data used during the search for a lower bound for unrolling.
+ *
+ * "build" is the build in which the unrolling will be performed
+ * "domain" is the original set for which to find a lower bound
+ * "depth" is the dimension for which to find a lower boudn
+ * "expansion" is the expansion that needs to be applied to "domain"
+ * in the unrolling that will be performed
+ *
+ * "lower" is the best lower bound found so far. It is NULL if we have not
+ * found any yet.
+ * "n" is the corresponding size. If lower is NULL, then the value of n
+ * is undefined.
+ * "n_div" is the maximal number of integer divisions in the first
+ * unrolled iteration (after expansion). It is set to -1 if it hasn't
+ * been computed yet.
+ */
+struct isl_find_unroll_data {
+ isl_ast_build *build;
+ isl_set *domain;
+ int depth;
+ isl_basic_map *expansion;
+
+ isl_aff *lower;
+ int *n;
+ int n_div;
+};
+
+/* Return the constraint
+ *
+ * i_"depth" = aff + offset
+ */
+static __isl_give isl_constraint *at_offset(int depth, __isl_keep isl_aff *aff,
+ int offset)
+{
+ aff = isl_aff_copy(aff);
+ aff = isl_aff_add_coefficient_si(aff, isl_dim_in, depth, -1);
+ aff = isl_aff_add_constant_si(aff, offset);
+ return isl_equality_from_aff(aff);
+}
+
+/* Update *user to the number of integer divisions in the first element
+ * of "ma", if it is larger than the current value.
+ */
+static isl_stat update_n_div(__isl_take isl_set *set,
+ __isl_take isl_multi_aff *ma, void *user)
+{
+ isl_aff *aff;
+ int *n = user;
+ isl_size n_div;
+
+ aff = isl_multi_aff_get_aff(ma, 0);
+ n_div = isl_aff_dim(aff, isl_dim_div);
+ isl_aff_free(aff);
+ isl_multi_aff_free(ma);
+ isl_set_free(set);
+
+ if (n_div > *n)
+ *n = n_div;
+
+ return n_div >= 0 ? isl_stat_ok : isl_stat_error;
+}
+
+/* Get the number of integer divisions in the expression for the iterator
+ * value at the first slice in the unrolling based on lower bound "lower",
+ * taking into account the expansion that needs to be performed on this slice.
+ */
+static int get_expanded_n_div(struct isl_find_unroll_data *data,
+ __isl_keep isl_aff *lower)
+{
+ isl_constraint *c;
+ isl_set *set;
+ isl_map *it_map, *expansion;
+ isl_pw_multi_aff *pma;
+ int n;
+
+ c = at_offset(data->depth, lower, 0);
+ set = isl_set_copy(data->domain);
+ set = isl_set_add_constraint(set, c);
+ expansion = isl_map_from_basic_map(isl_basic_map_copy(data->expansion));
+ set = isl_set_apply(set, expansion);
+ it_map = isl_ast_build_map_to_iterator(data->build, set);
+ pma = isl_pw_multi_aff_from_map(it_map);
+ n = 0;
+ if (isl_pw_multi_aff_foreach_piece(pma, &update_n_div, &n) < 0)
+ n = -1;
+ isl_pw_multi_aff_free(pma);
+
+ return n;
+}
+
+/* Is the lower bound "lower" with corresponding iteration count "n"
+ * better than the one stored in "data"?
+ * If there is no upper bound on the iteration count ("n" is infinity) or
+ * if the count is too large, then we cannot use this lower bound.
+ * Otherwise, if there was no previous lower bound or
+ * if the iteration count of the new lower bound is smaller than
+ * the iteration count of the previous lower bound, then we consider
+ * the new lower bound to be better.
+ * If the iteration count is the same, then compare the number
+ * of integer divisions that would be needed to express
+ * the iterator value at the first slice in the unrolling
+ * according to the lower bound. If we end up computing this
+ * number, then store the lowest value in data->n_div.
+ */
+static int is_better_lower_bound(struct isl_find_unroll_data *data,
+ __isl_keep isl_aff *lower, __isl_keep isl_val *n)
+{
+ int cmp;
+ int n_div;
+
+ if (!n)
+ return -1;
+ if (isl_val_is_infty(n))
+ return 0;
+ if (isl_val_cmp_si(n, INT_MAX) > 0)
+ return 0;
+ if (!data->lower)
+ return 1;
+ cmp = isl_val_cmp_si(n, *data->n);
+ if (cmp < 0)
+ return 1;
+ if (cmp > 0)
+ return 0;
+ if (data->n_div < 0)
+ data->n_div = get_expanded_n_div(data, data->lower);
+ if (data->n_div < 0)
+ return -1;
+ if (data->n_div == 0)
+ return 0;
+ n_div = get_expanded_n_div(data, lower);
+ if (n_div < 0)
+ return -1;
+ if (n_div >= data->n_div)
+ return 0;
+ data->n_div = n_div;
+
+ return 1;
+}
+
+/* Check if we can use "c" as a lower bound and if it is better than
+ * any previously found lower bound.
+ *
+ * If "c" does not involve the dimension at the current depth,
+ * then we cannot use it.
+ * Otherwise, let "c" be of the form
+ *
+ * i >= f(j)/a
+ *
+ * We compute the maximal value of
+ *
+ * -ceil(f(j)/a)) + i + 1
+ *
+ * over the domain. If there is such a value "n", then we know
+ *
+ * -ceil(f(j)/a)) + i + 1 <= n
+ *
+ * or
+ *
+ * i < ceil(f(j)/a)) + n
+ *
+ * meaning that we can use ceil(f(j)/a)) as a lower bound for unrolling.
+ * We just need to check if we have found any lower bound before and
+ * if the new lower bound is better (smaller n or fewer integer divisions)
+ * than the previously found lower bounds.
+ */
+static isl_stat update_unrolling_lower_bound(struct isl_find_unroll_data *data,
+ __isl_keep isl_constraint *c)
+{
+ isl_aff *aff, *lower;
+ isl_val *max;
+ int better;
+
+ if (!isl_constraint_is_lower_bound(c, isl_dim_set, data->depth))
+ return isl_stat_ok;
+
+ lower = isl_constraint_get_bound(c, isl_dim_set, data->depth);
+ lower = isl_aff_ceil(lower);
+ aff = isl_aff_copy(lower);
+ aff = isl_aff_neg(aff);
+ aff = isl_aff_add_coefficient_si(aff, isl_dim_in, data->depth, 1);
+ aff = isl_aff_add_constant_si(aff, 1);
+ max = isl_set_max_val(data->domain, aff);
+ isl_aff_free(aff);
+
+ better = is_better_lower_bound(data, lower, max);
+ if (better < 0 || !better) {
+ isl_val_free(max);
+ isl_aff_free(lower);
+ return better < 0 ? isl_stat_error : isl_stat_ok;
+ }
+
+ isl_aff_free(data->lower);
+ data->lower = lower;
+ *data->n = isl_val_get_num_si(max);
+ isl_val_free(max);
+
+ return isl_stat_ok;
+}
+
+/* Check if we can use "c" as a lower bound and if it is better than
+ * any previously found lower bound.
+ */
+static isl_stat constraint_find_unroll(__isl_take isl_constraint *c, void *user)
+{
+ struct isl_find_unroll_data *data;
+ isl_stat r;
+
+ data = (struct isl_find_unroll_data *) user;
+ r = update_unrolling_lower_bound(data, c);
+ isl_constraint_free(c);
+
+ return r;
+}
+
+/* Look for a lower bound l(i) on the dimension at "depth"
+ * and a size n such that "domain" is a subset of
+ *
+ * { [i] : l(i) <= i_d < l(i) + n }
+ *
+ * where d is "depth" and l(i) depends only on earlier dimensions.
+ * Furthermore, try and find a lower bound such that n is as small as possible.
+ * In particular, "n" needs to be finite.
+ * "build" is the build in which the unrolling will be performed.
+ * "expansion" is the expansion that needs to be applied to "domain"
+ * in the unrolling that will be performed.
+ *
+ * Inner dimensions have been eliminated from "domain" by the caller.
+ *
+ * We first construct a collection of lower bounds on the input set
+ * by computing its simple hull. We then iterate through them,
+ * discarding those that we cannot use (either because they do not
+ * involve the dimension at "depth" or because they have no corresponding
+ * upper bound, meaning that "n" would be unbounded) and pick out the
+ * best from the remaining ones.
+ *
+ * If we cannot find a suitable lower bound, then we consider that
+ * to be an error.
+ */
+static __isl_give isl_aff *find_unroll_lower_bound(
+ __isl_keep isl_ast_build *build, __isl_keep isl_set *domain,
+ int depth, __isl_keep isl_basic_map *expansion, int *n)
+{
+ struct isl_find_unroll_data data =
+ { build, domain, depth, expansion, NULL, n, -1 };
+ isl_basic_set *hull;
+
+ hull = isl_set_simple_hull(isl_set_copy(domain));
+
+ if (isl_basic_set_foreach_constraint(hull,
+ &constraint_find_unroll, &data) < 0)
+ goto error;
+
+ isl_basic_set_free(hull);
+
+ if (!data.lower)
+ isl_die(isl_set_get_ctx(domain), isl_error_invalid,
+ "cannot find lower bound for unrolling", return NULL);
+
+ return data.lower;
+error:
+ isl_basic_set_free(hull);
+ return isl_aff_free(data.lower);
+}
+
+/* Call "fn" on each iteration of the current dimension of "domain".
+ * If "init" is not NULL, then it is called with the number of
+ * iterations before any call to "fn".
+ * Return -1 on failure.
+ *
+ * Since we are going to be iterating over the individual values,
+ * we first check if there are any strides on the current dimension.
+ * If there is, we rewrite the current dimension i as
+ *
+ * i = stride i' + offset
+ *
+ * and then iterate over individual values of i' instead.
+ *
+ * We then look for a lower bound on i' and a size such that the domain
+ * is a subset of
+ *
+ * { [j,i'] : l(j) <= i' < l(j) + n }
+ *
+ * and then take slices of the domain at values of i'
+ * between l(j) and l(j) + n - 1.
+ *
+ * We compute the unshifted simple hull of each slice to ensure that
+ * we have a single basic set per offset. The slicing constraint
+ * may get simplified away before the unshifted simple hull is taken
+ * and may therefore in some rare cases disappear from the result.
+ * We therefore explicitly add the constraint back after computing
+ * the unshifted simple hull to ensure that the basic sets
+ * remain disjoint. The constraints that are dropped by taking the hull
+ * will be taken into account at the next level, as in the case of the
+ * atomic option.
+ *
+ * Finally, we map i' back to i and call "fn".
+ */
+static int foreach_iteration(__isl_take isl_set *domain,
+ __isl_keep isl_ast_build *build, int (*init)(int n, void *user),
+ int (*fn)(__isl_take isl_basic_set *bset, void *user), void *user)
+{
+ int i, n;
+ isl_bool empty;
+ isl_size depth;
+ isl_multi_aff *expansion;
+ isl_basic_map *bmap;
+ isl_aff *lower = NULL;
+ isl_ast_build *stride_build;
+
+ depth = isl_ast_build_get_depth(build);
+ if (depth < 0)
+ domain = isl_set_free(domain);
+
+ domain = isl_ast_build_eliminate_inner(build, domain);
+ domain = isl_set_intersect(domain, isl_ast_build_get_domain(build));
+ stride_build = isl_ast_build_copy(build);
+ stride_build = isl_ast_build_detect_strides(stride_build,
+ isl_set_copy(domain));
+ expansion = isl_ast_build_get_stride_expansion(stride_build);
+
+ domain = isl_set_preimage_multi_aff(domain,
+ isl_multi_aff_copy(expansion));
+ domain = isl_ast_build_eliminate_divs(stride_build, domain);
+ isl_ast_build_free(stride_build);
+
+ bmap = isl_basic_map_from_multi_aff(expansion);
+
+ empty = isl_set_is_empty(domain);
+ if (empty < 0) {
+ n = -1;
+ } else if (empty) {
+ n = 0;
+ } else {
+ lower = find_unroll_lower_bound(build, domain, depth, bmap, &n);
+ if (!lower)
+ n = -1;
+ }
+ if (n >= 0 && init && init(n, user) < 0)
+ n = -1;
+ for (i = 0; i < n; ++i) {
+ isl_set *set;
+ isl_basic_set *bset;
+ isl_constraint *slice;
+
+ slice = at_offset(depth, lower, i);
+ set = isl_set_copy(domain);
+ set = isl_set_add_constraint(set, isl_constraint_copy(slice));
+ bset = isl_set_unshifted_simple_hull(set);
+ bset = isl_basic_set_add_constraint(bset, slice);
+ bset = isl_basic_set_apply(bset, isl_basic_map_copy(bmap));
+
+ if (fn(bset, user) < 0)
+ break;
+ }
+
+ isl_aff_free(lower);
+ isl_set_free(domain);
+ isl_basic_map_free(bmap);
+
+ return n < 0 || i < n ? -1 : 0;
+}
+
+/* Data structure for storing the results and the intermediate objects
+ * of compute_domains.
+ *
+ * "list" is the main result of the function and contains a list
+ * of disjoint basic sets for which code should be generated.
+ *
+ * "executed" and "build" are inputs to compute_domains.
+ * "schedule_domain" is the domain of "executed".
+ *
+ * "option" contains the domains at the current depth that should by
+ * atomic, separated or unrolled. These domains are as specified by
+ * the user, except that inner dimensions have been eliminated and
+ * that they have been made pair-wise disjoint.
+ *
+ * "sep_class" contains the user-specified split into separation classes
+ * specialized to the current depth.
+ * "done" contains the union of the separation domains that have already
+ * been handled.
+ */
+struct isl_codegen_domains {
+ isl_basic_set_list *list;
+
+ isl_union_map *executed;
+ isl_ast_build *build;
+ isl_set *schedule_domain;
+
+ isl_set *option[4];
+
+ isl_map *sep_class;
+ isl_set *done;
+};
+
+/* Internal data structure for do_unroll.
+ *
+ * "domains" stores the results of compute_domains.
+ * "class_domain" is the original class domain passed to do_unroll.
+ * "unroll_domain" collects the unrolled iterations.
+ */
+struct isl_ast_unroll_data {
+ struct isl_codegen_domains *domains;
+ isl_set *class_domain;
+ isl_set *unroll_domain;
+};
+
+/* Given an iteration of an unrolled domain represented by "bset",
+ * add it to data->domains->list.
+ * Since we may have dropped some constraints, we intersect with
+ * the class domain again to ensure that each element in the list
+ * is disjoint from the other class domains.
+ */
+static int do_unroll_iteration(__isl_take isl_basic_set *bset, void *user)
+{
+ struct isl_ast_unroll_data *data = user;
+ isl_set *set;
+ isl_basic_set_list *list;
+
+ set = isl_set_from_basic_set(bset);
+ data->unroll_domain = isl_set_union(data->unroll_domain,
+ isl_set_copy(set));
+ set = isl_set_intersect(set, isl_set_copy(data->class_domain));
+ set = isl_set_make_disjoint(set);
+ list = isl_basic_set_list_from_set(set);
+ data->domains->list = isl_basic_set_list_concat(data->domains->list,
+ list);
+
+ return 0;
+}
+
+/* Extend domains->list with a list of basic sets, one for each value
+ * of the current dimension in "domain" and remove the corresponding
+ * sets from the class domain. Return the updated class domain.
+ * The divs that involve the current dimension have not been projected out
+ * from this domain.
+ *
+ * We call foreach_iteration to iterate over the individual values and
+ * in do_unroll_iteration we collect the individual basic sets in
+ * domains->list and their union in data->unroll_domain, which is then
+ * used to update the class domain.
+ */
+static __isl_give isl_set *do_unroll(struct isl_codegen_domains *domains,
+ __isl_take isl_set *domain, __isl_take isl_set *class_domain)
+{
+ struct isl_ast_unroll_data data;
+
+ if (!domain)
+ return isl_set_free(class_domain);
+ if (!class_domain)
+ return isl_set_free(domain);
+
+ data.domains = domains;
+ data.class_domain = class_domain;
+ data.unroll_domain = isl_set_empty(isl_set_get_space(domain));
+
+ if (foreach_iteration(domain, domains->build, NULL,
+ &do_unroll_iteration, &data) < 0)
+ data.unroll_domain = isl_set_free(data.unroll_domain);
+
+ class_domain = isl_set_subtract(class_domain, data.unroll_domain);
+
+ return class_domain;
+}
+
+/* Add domains to domains->list for each individual value of the current
+ * dimension, for that part of the schedule domain that lies in the
+ * intersection of the option domain and the class domain.
+ * Remove the corresponding sets from the class domain and
+ * return the updated class domain.
+ *
+ * We first break up the unroll option domain into individual pieces
+ * and then handle each of them separately. The unroll option domain
+ * has been made disjoint in compute_domains_init_options,
+ *
+ * Note that we actively want to combine different pieces of the
+ * schedule domain that have the same value at the current dimension.
+ * We therefore need to break up the unroll option domain before
+ * intersecting with class and schedule domain, hoping that the
+ * unroll option domain specified by the user is relatively simple.
+ */
+static __isl_give isl_set *compute_unroll_domains(
+ struct isl_codegen_domains *domains, __isl_take isl_set *class_domain)
+{
+ isl_set *unroll_domain;
+ isl_basic_set_list *unroll_list;
+ int i;
+ isl_size n;
+ isl_bool empty;
+
+ empty = isl_set_is_empty(domains->option[isl_ast_loop_unroll]);
+ if (empty < 0)
+ return isl_set_free(class_domain);
+ if (empty)
+ return class_domain;
+
+ unroll_domain = isl_set_copy(domains->option[isl_ast_loop_unroll]);
+ unroll_list = isl_basic_set_list_from_set(unroll_domain);
+
+ n = isl_basic_set_list_n_basic_set(unroll_list);
+ if (n < 0)
+ class_domain = isl_set_free(class_domain);
+ for (i = 0; i < n; ++i) {
+ isl_basic_set *bset;
+
+ bset = isl_basic_set_list_get_basic_set(unroll_list, i);
+ unroll_domain = isl_set_from_basic_set(bset);
+ unroll_domain = isl_set_intersect(unroll_domain,
+ isl_set_copy(class_domain));
+ unroll_domain = isl_set_intersect(unroll_domain,
+ isl_set_copy(domains->schedule_domain));
+
+ empty = isl_set_is_empty(unroll_domain);
+ if (empty >= 0 && empty) {
+ isl_set_free(unroll_domain);
+ continue;
+ }
+
+ class_domain = do_unroll(domains, unroll_domain, class_domain);
+ }
+
+ isl_basic_set_list_free(unroll_list);
+
+ return class_domain;
+}
+
+/* Try and construct a single basic set that includes the intersection of
+ * the schedule domain, the atomic option domain and the class domain.
+ * Add the resulting basic set(s) to domains->list and remove them
+ * from class_domain. Return the updated class domain.
+ *
+ * We construct a single domain rather than trying to combine
+ * the schedule domains of individual domains because we are working
+ * within a single component so that non-overlapping schedule domains
+ * should already have been separated.
+ * We do however need to make sure that this single domains is a subset
+ * of the class domain so that it would not intersect with any other
+ * class domains. This means that we may end up splitting up the atomic
+ * domain in case separation classes are being used.
+ *
+ * "domain" is the intersection of the schedule domain and the class domain,
+ * with inner dimensions projected out.
+ */
+static __isl_give isl_set *compute_atomic_domain(
+ struct isl_codegen_domains *domains, __isl_take isl_set *class_domain)
+{
+ isl_basic_set *bset;
+ isl_basic_set_list *list;
+ isl_set *domain, *atomic_domain;
+ int empty;
+
+ domain = isl_set_copy(domains->option[isl_ast_loop_atomic]);
+ domain = isl_set_intersect(domain, isl_set_copy(class_domain));
+ domain = isl_set_intersect(domain,
+ isl_set_copy(domains->schedule_domain));
+ empty = isl_set_is_empty(domain);
+ if (empty < 0)
+ class_domain = isl_set_free(class_domain);
+ if (empty) {
+ isl_set_free(domain);
+ return class_domain;
+ }
+
+ domain = isl_ast_build_eliminate(domains->build, domain);
+ domain = isl_set_coalesce_preserve(domain);
+ bset = isl_set_unshifted_simple_hull(domain);
+ domain = isl_set_from_basic_set(bset);
+ atomic_domain = isl_set_copy(domain);
+ domain = isl_set_intersect(domain, isl_set_copy(class_domain));
+ class_domain = isl_set_subtract(class_domain, atomic_domain);
+ domain = isl_set_make_disjoint(domain);
+ list = isl_basic_set_list_from_set(domain);
+ domains->list = isl_basic_set_list_concat(domains->list, list);
+
+ return class_domain;
+}
+
+/* Split up the schedule domain into uniform basic sets,
+ * in the sense that each element in a basic set is associated to
+ * elements of the same domains, and add the result to domains->list.
+ * Do this for that part of the schedule domain that lies in the
+ * intersection of "class_domain" and the separate option domain.
+ *
+ * "class_domain" may or may not include the constraints
+ * of the schedule domain, but this does not make a difference
+ * since we are going to intersect it with the domain of the inverse schedule.
+ * If it includes schedule domain constraints, then they may involve
+ * inner dimensions, but we will eliminate them in separation_domain.
+ */
+static int compute_separate_domain(struct isl_codegen_domains *domains,
+ __isl_keep isl_set *class_domain)
+{
+ isl_space *space;
+ isl_set *domain;
+ isl_union_map *executed;
+ isl_basic_set_list *list;
+ int empty;
+
+ domain = isl_set_copy(domains->option[isl_ast_loop_separate]);
+ domain = isl_set_intersect(domain, isl_set_copy(class_domain));
+ executed = isl_union_map_copy(domains->executed);
+ executed = isl_union_map_intersect_domain(executed,
+ isl_union_set_from_set(domain));
+ empty = isl_union_map_is_empty(executed);
+ if (empty < 0 || empty) {
+ isl_union_map_free(executed);
+ return empty < 0 ? -1 : 0;
+ }
+
+ space = isl_set_get_space(class_domain);
+ domain = separate_schedule_domains(space, executed, domains->build);
+
+ list = isl_basic_set_list_from_set(domain);
+ domains->list = isl_basic_set_list_concat(domains->list, list);
+
+ return 0;
+}
+
+/* Split up the domain at the current depth into disjoint
+ * basic sets for which code should be generated separately
+ * for the given separation class domain.
+ *
+ * If any separation classes have been defined, then "class_domain"
+ * is the domain of the current class and does not refer to inner dimensions.
+ * Otherwise, "class_domain" is the universe domain.
+ *
+ * We first make sure that the class domain is disjoint from
+ * previously considered class domains.
+ *
+ * The separate domains can be computed directly from the "class_domain".
+ *
+ * The unroll, atomic and remainder domains need the constraints
+ * from the schedule domain.
+ *
+ * For unrolling, the actual schedule domain is needed (with divs that
+ * may refer to the current dimension) so that stride detection can be
+ * performed.
+ *
+ * For atomic and remainder domains, inner dimensions and divs involving
+ * the current dimensions should be eliminated.
+ * In case we are working within a separation class, we need to intersect
+ * the result with the current "class_domain" to ensure that the domains
+ * are disjoint from those generated from other class domains.
+ *
+ * The domain that has been made atomic may be larger than specified
+ * by the user since it needs to be representable as a single basic set.
+ * This possibly larger domain is removed from class_domain by
+ * compute_atomic_domain. It is computed first so that the extended domain
+ * would not overlap with any domains computed before.
+ * Similary, the unrolled domains may have some constraints removed and
+ * may therefore also be larger than specified by the user.
+ *
+ * If anything is left after handling separate, unroll and atomic,
+ * we split it up into basic sets and append the basic sets to domains->list.
+ */
+static isl_stat compute_partial_domains(struct isl_codegen_domains *domains,
+ __isl_take isl_set *class_domain)
+{
+ isl_basic_set_list *list;
+ isl_set *domain;
+
+ class_domain = isl_set_subtract(class_domain,
+ isl_set_copy(domains->done));
+ domains->done = isl_set_union(domains->done,
+ isl_set_copy(class_domain));
+
+ class_domain = compute_atomic_domain(domains, class_domain);
+ class_domain = compute_unroll_domains(domains, class_domain);
+
+ domain = isl_set_copy(class_domain);
+
+ if (compute_separate_domain(domains, domain) < 0)
+ goto error;
+ domain = isl_set_subtract(domain,
+ isl_set_copy(domains->option[isl_ast_loop_separate]));
+
+ domain = isl_set_intersect(domain,
+ isl_set_copy(domains->schedule_domain));
+
+ domain = isl_ast_build_eliminate(domains->build, domain);
+ domain = isl_set_intersect(domain, isl_set_copy(class_domain));
+
+ domain = isl_set_coalesce_preserve(domain);
+ domain = isl_set_make_disjoint(domain);
+
+ list = isl_basic_set_list_from_set(domain);
+ domains->list = isl_basic_set_list_concat(domains->list, list);
+
+ isl_set_free(class_domain);
+
+ return isl_stat_ok;
+error:
+ isl_set_free(domain);
+ isl_set_free(class_domain);
+ return isl_stat_error;
+}
+
+/* Split up the domain at the current depth into disjoint
+ * basic sets for which code should be generated separately
+ * for the separation class identified by "pnt".
+ *
+ * We extract the corresponding class domain from domains->sep_class,
+ * eliminate inner dimensions and pass control to compute_partial_domains.
+ */
+static isl_stat compute_class_domains(__isl_take isl_point *pnt, void *user)
+{
+ struct isl_codegen_domains *domains = user;
+ isl_set *class_set;
+ isl_set *domain;
+ int disjoint;
+
+ class_set = isl_set_from_point(pnt);
+ domain = isl_map_domain(isl_map_intersect_range(
+ isl_map_copy(domains->sep_class), class_set));
+ domain = isl_ast_build_compute_gist(domains->build, domain);
+ domain = isl_ast_build_eliminate(domains->build, domain);
+
+ disjoint = isl_set_plain_is_disjoint(domain, domains->schedule_domain);
+ if (disjoint < 0)
+ return isl_stat_error;
+ if (disjoint) {
+ isl_set_free(domain);
+ return isl_stat_ok;
+ }
+
+ return compute_partial_domains(domains, domain);
+}
+
+/* Extract the domains at the current depth that should be atomic,
+ * separated or unrolled and store them in option.
+ *
+ * The domains specified by the user might overlap, so we make
+ * them disjoint by subtracting earlier domains from later domains.
+ */
+static void compute_domains_init_options(isl_set *option[4],
+ __isl_keep isl_ast_build *build)
+{
+ enum isl_ast_loop_type type, type2;
+ isl_set *unroll;
+
+ for (type = isl_ast_loop_atomic;
+ type <= isl_ast_loop_separate; ++type) {
+ option[type] = isl_ast_build_get_option_domain(build, type);
+ for (type2 = isl_ast_loop_atomic; type2 < type; ++type2)
+ option[type] = isl_set_subtract(option[type],
+ isl_set_copy(option[type2]));
+ }
+
+ unroll = option[isl_ast_loop_unroll];
+ unroll = isl_set_coalesce(unroll);
+ unroll = isl_set_make_disjoint(unroll);
+ option[isl_ast_loop_unroll] = unroll;
+}
+
+/* Split up the domain at the current depth into disjoint
+ * basic sets for which code should be generated separately,
+ * based on the user-specified options.
+ * Return the list of disjoint basic sets.
+ *
+ * There are three kinds of domains that we need to keep track of.
+ * - the "schedule domain" is the domain of "executed"
+ * - the "class domain" is the domain corresponding to the currrent
+ * separation class
+ * - the "option domain" is the domain corresponding to one of the options
+ * atomic, unroll or separate
+ *
+ * We first consider the individial values of the separation classes
+ * and split up the domain for each of them separately.
+ * Finally, we consider the remainder. If no separation classes were
+ * specified, then we call compute_partial_domains with the universe
+ * "class_domain". Otherwise, we take the "schedule_domain" as "class_domain",
+ * with inner dimensions removed. We do this because we want to
+ * avoid computing the complement of the class domains (i.e., the difference
+ * between the universe and domains->done).
+ */
+static __isl_give isl_basic_set_list *compute_domains(
+ __isl_keep isl_union_map *executed, __isl_keep isl_ast_build *build)
+{
+ struct isl_codegen_domains domains;
+ isl_ctx *ctx;
+ isl_set *domain;
+ isl_union_set *schedule_domain;
+ isl_set *classes;
+ isl_space *space;
+ int n_param;
+ enum isl_ast_loop_type type;
+ isl_bool empty;
+
+ if (!executed)
+ return NULL;
+
+ ctx = isl_union_map_get_ctx(executed);
+ domains.list = isl_basic_set_list_alloc(ctx, 0);
+
+ schedule_domain = isl_union_map_domain(isl_union_map_copy(executed));
+ domain = isl_set_from_union_set(schedule_domain);
+
+ compute_domains_init_options(domains.option, build);
+
+ domains.sep_class = isl_ast_build_get_separation_class(build);
+ classes = isl_map_range(isl_map_copy(domains.sep_class));
+ n_param = isl_set_dim(classes, isl_dim_param);
+ if (n_param < 0)
+ classes = isl_set_free(classes);
+ classes = isl_set_project_out(classes, isl_dim_param, 0, n_param);
+
+ space = isl_set_get_space(domain);
+ domains.build = build;
+ domains.schedule_domain = isl_set_copy(domain);
+ domains.executed = executed;
+ domains.done = isl_set_empty(space);
+
+ if (isl_set_foreach_point(classes, &compute_class_domains, &domains) < 0)
+ domains.list = isl_basic_set_list_free(domains.list);
+ isl_set_free(classes);
+
+ empty = isl_set_is_empty(domains.done);
+ if (empty < 0) {
+ domains.list = isl_basic_set_list_free(domains.list);
+ domain = isl_set_free(domain);
+ } else if (empty) {
+ isl_set_free(domain);
+ domain = isl_set_universe(isl_set_get_space(domains.done));
+ } else {
+ domain = isl_ast_build_eliminate(build, domain);
+ }
+ if (compute_partial_domains(&domains, domain) < 0)
+ domains.list = isl_basic_set_list_free(domains.list);
+
+ isl_set_free(domains.schedule_domain);
+ isl_set_free(domains.done);
+ isl_map_free(domains.sep_class);
+ for (type = isl_ast_loop_atomic; type <= isl_ast_loop_separate; ++type)
+ isl_set_free(domains.option[type]);
+
+ return domains.list;
+}
+
+/* Generate code for a single component, after shifting (if any)
+ * has been applied, in case the schedule was specified as a union map.
+ *
+ * We first split up the domain at the current depth into disjoint
+ * basic sets based on the user-specified options.
+ * Then we generated code for each of them and concatenate the results.
+ */
+static __isl_give isl_ast_graft_list *generate_shifted_component_flat(
+ __isl_take isl_union_map *executed, __isl_take isl_ast_build *build)
+{
+ isl_basic_set_list *domain_list;
+ isl_ast_graft_list *list = NULL;
+
+ domain_list = compute_domains(executed, build);
+ list = generate_parallel_domains(domain_list, executed, build);
+
+ isl_basic_set_list_free(domain_list);
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+
+ return list;
+}
+
+/* Generate code for a single component, after shifting (if any)
+ * has been applied, in case the schedule was specified as a schedule tree
+ * and the separate option was specified.
+ *
+ * We perform separation on the domain of "executed" and then generate
+ * an AST for each of the resulting disjoint basic sets.
+ */
+static __isl_give isl_ast_graft_list *generate_shifted_component_tree_separate(
+ __isl_take isl_union_map *executed, __isl_take isl_ast_build *build)
+{
+ isl_space *space;
+ isl_set *domain;
+ isl_basic_set_list *domain_list;
+ isl_ast_graft_list *list;
+
+ space = isl_ast_build_get_space(build, 1);
+ domain = separate_schedule_domains(space,
+ isl_union_map_copy(executed), build);
+ domain_list = isl_basic_set_list_from_set(domain);
+
+ list = generate_parallel_domains(domain_list, executed, build);
+
+ isl_basic_set_list_free(domain_list);
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+
+ return list;
+}
+
+/* Internal data structure for generate_shifted_component_tree_unroll.
+ *
+ * "executed" and "build" are inputs to generate_shifted_component_tree_unroll.
+ * "list" collects the constructs grafts.
+ */
+struct isl_ast_unroll_tree_data {
+ isl_union_map *executed;
+ isl_ast_build *build;
+ isl_ast_graft_list *list;
+};
+
+/* Initialize data->list to a list of "n" elements.
+ */
+static int init_unroll_tree(int n, void *user)
+{
+ struct isl_ast_unroll_tree_data *data = user;
+ isl_ctx *ctx;
+
+ ctx = isl_ast_build_get_ctx(data->build);
+ data->list = isl_ast_graft_list_alloc(ctx, n);
+
+ return 0;
+}
+
+/* Given an iteration of an unrolled domain represented by "bset",
+ * generate the corresponding AST and add the result to data->list.
+ */
+static int do_unroll_tree_iteration(__isl_take isl_basic_set *bset, void *user)
+{
+ struct isl_ast_unroll_tree_data *data = user;
+
+ data->list = add_node(data->list, isl_union_map_copy(data->executed),
+ bset, isl_ast_build_copy(data->build));
+
+ return 0;
+}
+
+/* Generate code for a single component, after shifting (if any)
+ * has been applied, in case the schedule was specified as a schedule tree
+ * and the unroll option was specified.
+ *
+ * We call foreach_iteration to iterate over the individual values and
+ * construct and collect the corresponding grafts in do_unroll_tree_iteration.
+ */
+static __isl_give isl_ast_graft_list *generate_shifted_component_tree_unroll(
+ __isl_take isl_union_map *executed, __isl_take isl_set *domain,
+ __isl_take isl_ast_build *build)
+{
+ struct isl_ast_unroll_tree_data data = { executed, build, NULL };
+
+ if (foreach_iteration(domain, build, &init_unroll_tree,
+ &do_unroll_tree_iteration, &data) < 0)
+ data.list = isl_ast_graft_list_free(data.list);
+
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+
+ return data.list;
+}
+
+/* Does "domain" involve a disjunction that is purely based on
+ * constraints involving only outer dimension?
+ *
+ * In particular, is there a disjunction such that the constraints
+ * involving the current and later dimensions are the same over
+ * all the disjuncts?
+ */
+static isl_bool has_pure_outer_disjunction(__isl_keep isl_set *domain,
+ __isl_keep isl_ast_build *build)
+{
+ isl_basic_set *hull;
+ isl_set *shared, *inner;
+ isl_bool equal;
+ isl_size depth;
+ isl_size n;
+ isl_size dim;
+
+ n = isl_set_n_basic_set(domain);
+ if (n < 0)
+ return isl_bool_error;
+ if (n <= 1)
+ return isl_bool_false;
+ dim = isl_set_dim(domain, isl_dim_set);
+ depth = isl_ast_build_get_depth(build);
+ if (dim < 0 || depth < 0)
+ return isl_bool_error;
+
+ inner = isl_set_copy(domain);
+ inner = isl_set_drop_constraints_not_involving_dims(inner,
+ isl_dim_set, depth, dim - depth);
+ hull = isl_set_plain_unshifted_simple_hull(isl_set_copy(inner));
+ shared = isl_set_from_basic_set(hull);
+ equal = isl_set_plain_is_equal(inner, shared);
+ isl_set_free(inner);
+ isl_set_free(shared);
+
+ return equal;
+}
+
+/* Generate code for a single component, after shifting (if any)
+ * has been applied, in case the schedule was specified as a schedule tree.
+ * In particular, handle the base case where there is either no isolated
+ * set or we are within the isolated set (in which case "isolated" is set)
+ * or the iterations that precede or follow the isolated set.
+ *
+ * The schedule domain is broken up or combined into basic sets
+ * according to the AST generation option specified in the current
+ * schedule node, which may be either atomic, separate, unroll or
+ * unspecified. If the option is unspecified, then we currently simply
+ * split the schedule domain into disjoint basic sets.
+ *
+ * In case the separate option is specified, the AST generation is
+ * handled by generate_shifted_component_tree_separate.
+ * In the other cases, we need the global schedule domain.
+ * In the unroll case, the AST generation is then handled by
+ * generate_shifted_component_tree_unroll which needs the actual
+ * schedule domain (with divs that may refer to the current dimension)
+ * so that stride detection can be performed.
+ * In the atomic or unspecified case, inner dimensions and divs involving
+ * the current dimensions should be eliminated.
+ * The result is then either combined into a single basic set or
+ * split up into disjoint basic sets.
+ * Finally an AST is generated for each basic set and the results are
+ * concatenated.
+ *
+ * If the schedule domain involves a disjunction that is purely based on
+ * constraints involving only outer dimension, then it is treated as
+ * if atomic was specified. This ensures that only a single loop
+ * is generated instead of a sequence of identical loops with
+ * different guards.
+ */
+static __isl_give isl_ast_graft_list *generate_shifted_component_tree_base(
+ __isl_take isl_union_map *executed, __isl_take isl_ast_build *build,
+ int isolated)
+{
+ isl_bool outer_disjunction;
+ isl_union_set *schedule_domain;
+ isl_set *domain;
+ isl_basic_set_list *domain_list;
+ isl_ast_graft_list *list;
+ enum isl_ast_loop_type type;
+
+ type = isl_ast_build_get_loop_type(build, isolated);
+ if (type < 0)
+ goto error;
+
+ if (type == isl_ast_loop_separate)
+ return generate_shifted_component_tree_separate(executed,
+ build);
+
+ schedule_domain = isl_union_map_domain(isl_union_map_copy(executed));
+ domain = isl_set_from_union_set(schedule_domain);
+
+ if (type == isl_ast_loop_unroll)
+ return generate_shifted_component_tree_unroll(executed, domain,
+ build);
+
+ domain = isl_ast_build_eliminate(build, domain);
+ domain = isl_set_coalesce_preserve(domain);
+
+ outer_disjunction = has_pure_outer_disjunction(domain, build);
+ if (outer_disjunction < 0)
+ domain = isl_set_free(domain);
+
+ if (outer_disjunction || type == isl_ast_loop_atomic) {
+ isl_basic_set *hull;
+ hull = isl_set_unshifted_simple_hull(domain);
+ domain_list = isl_basic_set_list_from_basic_set(hull);
+ } else {
+ domain = isl_set_make_disjoint(domain);
+ domain_list = isl_basic_set_list_from_set(domain);
+ }
+
+ list = generate_parallel_domains(domain_list, executed, build);
+
+ isl_basic_set_list_free(domain_list);
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+
+ return list;
+error:
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+ return NULL;
+}
+
+/* Extract out the disjunction imposed by "domain" on the outer
+ * schedule dimensions.
+ *
+ * In particular, remove all inner dimensions from "domain" (including
+ * the current dimension) and then remove the constraints that are shared
+ * by all disjuncts in the result.
+ */
+static __isl_give isl_set *extract_disjunction(__isl_take isl_set *domain,
+ __isl_keep isl_ast_build *build)
+{
+ isl_set *hull;
+ isl_size depth;
+ isl_size dim;
+
+ domain = isl_ast_build_specialize(build, domain);
+ depth = isl_ast_build_get_depth(build);
+ dim = isl_set_dim(domain, isl_dim_set);
+ if (depth < 0 || dim < 0)
+ return isl_set_free(domain);
+ domain = isl_set_eliminate(domain, isl_dim_set, depth, dim - depth);
+ domain = isl_set_remove_unknown_divs(domain);
+ hull = isl_set_copy(domain);
+ hull = isl_set_from_basic_set(isl_set_unshifted_simple_hull(hull));
+ domain = isl_set_gist(domain, hull);
+
+ return domain;
+}
+
+/* Add "guard" to the grafts in "list".
+ * "build" is the outer AST build, while "sub_build" includes "guard"
+ * in its generated domain.
+ *
+ * First combine the grafts into a single graft and then add the guard.
+ * If the list is empty, or if some error occurred, then simply return
+ * the list.
+ */
+static __isl_give isl_ast_graft_list *list_add_guard(
+ __isl_take isl_ast_graft_list *list, __isl_keep isl_set *guard,
+ __isl_keep isl_ast_build *build, __isl_keep isl_ast_build *sub_build)
+{
+ isl_ast_graft *graft;
+ isl_size n;
+
+ list = isl_ast_graft_list_fuse(list, sub_build);
+
+ n = isl_ast_graft_list_n_ast_graft(list);
+ if (n < 0)
+ return isl_ast_graft_list_free(list);
+ if (n != 1)
+ return list;
+
+ graft = isl_ast_graft_list_get_ast_graft(list, 0);
+ graft = isl_ast_graft_add_guard(graft, isl_set_copy(guard), build);
+ list = isl_ast_graft_list_set_ast_graft(list, 0, graft);
+
+ return list;
+}
+
+/* Generate code for a single component, after shifting (if any)
+ * has been applied, in case the schedule was specified as a schedule tree.
+ * In particular, do so for the specified subset of the schedule domain.
+ *
+ * If we are outside of the isolated part, then "domain" may include
+ * a disjunction. Explicitly generate this disjunction at this point
+ * instead of relying on the disjunction getting hoisted back up
+ * to this level.
+ */
+static __isl_give isl_ast_graft_list *generate_shifted_component_tree_part(
+ __isl_keep isl_union_map *executed, __isl_take isl_set *domain,
+ __isl_keep isl_ast_build *build, int isolated)
+{
+ isl_union_set *uset;
+ isl_ast_graft_list *list;
+ isl_ast_build *sub_build;
+ int empty;
+
+ uset = isl_union_set_from_set(isl_set_copy(domain));
+ executed = isl_union_map_copy(executed);
+ executed = isl_union_map_intersect_domain(executed, uset);
+ empty = isl_union_map_is_empty(executed);
+ if (empty < 0)
+ goto error;
+ if (empty) {
+ isl_ctx *ctx;
+ isl_union_map_free(executed);
+ isl_set_free(domain);
+ ctx = isl_ast_build_get_ctx(build);
+ return isl_ast_graft_list_alloc(ctx, 0);
+ }
+
+ sub_build = isl_ast_build_copy(build);
+ if (!isolated) {
+ domain = extract_disjunction(domain, build);
+ sub_build = isl_ast_build_restrict_generated(sub_build,
+ isl_set_copy(domain));
+ }
+ list = generate_shifted_component_tree_base(executed,
+ isl_ast_build_copy(sub_build), isolated);
+ if (!isolated)
+ list = list_add_guard(list, domain, build, sub_build);
+ isl_ast_build_free(sub_build);
+ isl_set_free(domain);
+ return list;
+error:
+ isl_union_map_free(executed);
+ isl_set_free(domain);
+ return NULL;
+}
+
+/* Generate code for a single component, after shifting (if any)
+ * has been applied, in case the schedule was specified as a schedule tree.
+ * In particular, do so for the specified sequence of subsets
+ * of the schedule domain, "before", "isolated", "after" and "other",
+ * where only the "isolated" part is considered to be isolated.
+ */
+static __isl_give isl_ast_graft_list *generate_shifted_component_parts(
+ __isl_take isl_union_map *executed, __isl_take isl_set *before,
+ __isl_take isl_set *isolated, __isl_take isl_set *after,
+ __isl_take isl_set *other, __isl_take isl_ast_build *build)
+{
+ isl_ast_graft_list *list, *res;
+
+ res = generate_shifted_component_tree_part(executed, before, build, 0);
+ list = generate_shifted_component_tree_part(executed, isolated,
+ build, 1);
+ res = isl_ast_graft_list_concat(res, list);
+ list = generate_shifted_component_tree_part(executed, after, build, 0);
+ res = isl_ast_graft_list_concat(res, list);
+ list = generate_shifted_component_tree_part(executed, other, build, 0);
+ res = isl_ast_graft_list_concat(res, list);
+
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+
+ return res;
+}
+
+/* Does "set" intersect "first", but not "second"?
+ */
+static isl_bool only_intersects_first(__isl_keep isl_set *set,
+ __isl_keep isl_set *first, __isl_keep isl_set *second)
+{
+ isl_bool disjoint;
+
+ disjoint = isl_set_is_disjoint(set, first);
+ if (disjoint < 0)
+ return isl_bool_error;
+ if (disjoint)
+ return isl_bool_false;
+
+ return isl_set_is_disjoint(set, second);
+}
+
+/* Generate code for a single component, after shifting (if any)
+ * has been applied, in case the schedule was specified as a schedule tree.
+ * In particular, do so in case of isolation where there is
+ * only an "isolated" part and an "after" part.
+ * "dead1" and "dead2" are freed by this function in order to simplify
+ * the caller.
+ *
+ * The "before" and "other" parts are set to empty sets.
+ */
+static __isl_give isl_ast_graft_list *generate_shifted_component_only_after(
+ __isl_take isl_union_map *executed, __isl_take isl_set *isolated,
+ __isl_take isl_set *after, __isl_take isl_ast_build *build,
+ __isl_take isl_set *dead1, __isl_take isl_set *dead2)
+{
+ isl_set *empty;
+
+ empty = isl_set_empty(isl_set_get_space(after));
+ isl_set_free(dead1);
+ isl_set_free(dead2);
+ return generate_shifted_component_parts(executed, isl_set_copy(empty),
+ isolated, after, empty, build);
+}
+
+/* Generate code for a single component, after shifting (if any)
+ * has been applied, in case the schedule was specified as a schedule tree.
+ *
+ * We first check if the user has specified an isolated schedule domain
+ * and that we are not already outside of this isolated schedule domain.
+ * If so, we break up the schedule domain into iterations that
+ * precede the isolated domain, the isolated domain itself,
+ * the iterations that follow the isolated domain and
+ * the remaining iterations (those that are incomparable
+ * to the isolated domain).
+ * We generate an AST for each piece and concatenate the results.
+ *
+ * If the isolated domain is not convex, then it is replaced
+ * by a convex superset to ensure that the sets of preceding and
+ * following iterations are properly defined and, in particular,
+ * that there are no intermediate iterations that do not belong
+ * to the isolated domain.
+ *
+ * In the special case where at least one element of the schedule
+ * domain that does not belong to the isolated domain needs
+ * to be scheduled after this isolated domain, but none of those
+ * elements need to be scheduled before, break up the schedule domain
+ * in only two parts, the isolated domain, and a part that will be
+ * scheduled after the isolated domain.
+ *
+ * If no isolated set has been specified, then we generate an
+ * AST for the entire inverse schedule.
+ */
+static __isl_give isl_ast_graft_list *generate_shifted_component_tree(
+ __isl_take isl_union_map *executed, __isl_take isl_ast_build *build)
+{
+ int i;
+ isl_size depth;
+ int empty, has_isolate;
+ isl_space *space;
+ isl_union_set *schedule_domain;
+ isl_set *domain;
+ isl_basic_set *hull;
+ isl_set *isolated, *before, *after, *test;
+ isl_map *gt, *lt;
+ isl_bool pure;
+
+ build = isl_ast_build_extract_isolated(build);
+ has_isolate = isl_ast_build_has_isolated(build);
+ if (has_isolate < 0)
+ executed = isl_union_map_free(executed);
+ else if (!has_isolate)
+ return generate_shifted_component_tree_base(executed, build, 0);
+
+ schedule_domain = isl_union_map_domain(isl_union_map_copy(executed));
+ domain = isl_set_from_union_set(schedule_domain);
+
+ isolated = isl_ast_build_get_isolated(build);
+ isolated = isl_set_intersect(isolated, isl_set_copy(domain));
+ test = isl_ast_build_specialize(build, isl_set_copy(isolated));
+ empty = isl_set_is_empty(test);
+ isl_set_free(test);
+ if (empty < 0)
+ goto error;
+ if (empty) {
+ isl_set_free(isolated);
+ isl_set_free(domain);
+ return generate_shifted_component_tree_base(executed, build, 0);
+ }
+ depth = isl_ast_build_get_depth(build);
+ if (depth < 0)
+ goto error;
+
+ isolated = isl_ast_build_eliminate(build, isolated);
+ hull = isl_set_unshifted_simple_hull(isolated);
+ isolated = isl_set_from_basic_set(hull);
+
+ space = isl_space_map_from_set(isl_set_get_space(isolated));
+ gt = isl_map_universe(space);
+ for (i = 0; i < depth; ++i)
+ gt = isl_map_equate(gt, isl_dim_in, i, isl_dim_out, i);
+ gt = isl_map_order_gt(gt, isl_dim_in, depth, isl_dim_out, depth);
+ lt = isl_map_reverse(isl_map_copy(gt));
+ before = isl_set_apply(isl_set_copy(isolated), gt);
+ after = isl_set_apply(isl_set_copy(isolated), lt);
+
+ domain = isl_set_subtract(domain, isl_set_copy(isolated));
+ pure = only_intersects_first(domain, after, before);
+ if (pure < 0)
+ executed = isl_union_map_free(executed);
+ else if (pure)
+ return generate_shifted_component_only_after(executed, isolated,
+ domain, build, before, after);
+ domain = isl_set_subtract(domain, isl_set_copy(before));
+ domain = isl_set_subtract(domain, isl_set_copy(after));
+ after = isl_set_subtract(after, isl_set_copy(isolated));
+ after = isl_set_subtract(after, isl_set_copy(before));
+ before = isl_set_subtract(before, isl_set_copy(isolated));
+
+ return generate_shifted_component_parts(executed, before, isolated,
+ after, domain, build);
+error:
+ isl_set_free(domain);
+ isl_set_free(isolated);
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+ return NULL;
+}
+
+/* Generate code for a single component, after shifting (if any)
+ * has been applied.
+ *
+ * Call generate_shifted_component_tree or generate_shifted_component_flat
+ * depending on whether the schedule was specified as a schedule tree.
+ */
+static __isl_give isl_ast_graft_list *generate_shifted_component(
+ __isl_take isl_union_map *executed, __isl_take isl_ast_build *build)
+{
+ if (isl_ast_build_has_schedule_node(build))
+ return generate_shifted_component_tree(executed, build);
+ else
+ return generate_shifted_component_flat(executed, build);
+}
+
+struct isl_set_map_pair {
+ isl_set *set;
+ isl_map *map;
+};
+
+/* Given an array "domain" of isl_set_map_pairs and an array "order"
+ * of indices into the "domain" array,
+ * return the union of the "map" fields of the elements
+ * indexed by the first "n" elements of "order".
+ */
+static __isl_give isl_union_map *construct_component_executed(
+ struct isl_set_map_pair *domain, int *order, int n)
+{
+ int i;
+ isl_map *map;
+ isl_union_map *executed;
+
+ map = isl_map_copy(domain[order[0]].map);
+ executed = isl_union_map_from_map(map);
+ for (i = 1; i < n; ++i) {
+ map = isl_map_copy(domain[order[i]].map);
+ executed = isl_union_map_add_map(executed, map);
+ }
+
+ return executed;
+}
+
+/* Generate code for a single component, after shifting (if any)
+ * has been applied.
+ *
+ * The component inverse schedule is specified as the "map" fields
+ * of the elements of "domain" indexed by the first "n" elements of "order".
+ */
+static __isl_give isl_ast_graft_list *generate_shifted_component_from_list(
+ struct isl_set_map_pair *domain, int *order, int n,
+ __isl_take isl_ast_build *build)
+{
+ isl_union_map *executed;
+
+ executed = construct_component_executed(domain, order, n);
+ return generate_shifted_component(executed, build);
+}
+
+/* Does set dimension "pos" of "set" have an obviously fixed value?
+ */
+static int dim_is_fixed(__isl_keep isl_set *set, int pos)
+{
+ int fixed;
+ isl_val *v;
+
+ v = isl_set_plain_get_val_if_fixed(set, isl_dim_set, pos);
+ if (!v)
+ return -1;
+ fixed = !isl_val_is_nan(v);
+ isl_val_free(v);
+
+ return fixed;
+}
+
+/* Given an array "domain" of isl_set_map_pairs and an array "order"
+ * of indices into the "domain" array,
+ * do all (except for at most one) of the "set" field of the elements
+ * indexed by the first "n" elements of "order" have a fixed value
+ * at position "depth"?
+ */
+static int at_most_one_non_fixed(struct isl_set_map_pair *domain,
+ int *order, int n, int depth)
+{
+ int i;
+ int non_fixed = -1;
+
+ for (i = 0; i < n; ++i) {
+ int f;
+
+ f = dim_is_fixed(domain[order[i]].set, depth);
+ if (f < 0)
+ return -1;
+ if (f)
+ continue;
+ if (non_fixed >= 0)
+ return 0;
+ non_fixed = i;
+ }
+
+ return 1;
+}
+
+/* Given an array "domain" of isl_set_map_pairs and an array "order"
+ * of indices into the "domain" array,
+ * eliminate the inner dimensions from the "set" field of the elements
+ * indexed by the first "n" elements of "order", provided the current
+ * dimension does not have a fixed value.
+ *
+ * Return the index of the first element in "order" with a corresponding
+ * "set" field that does not have an (obviously) fixed value.
+ */
+static int eliminate_non_fixed(struct isl_set_map_pair *domain,
+ int *order, int n, int depth, __isl_keep isl_ast_build *build)
+{
+ int i;
+ int base = -1;
+
+ for (i = n - 1; i >= 0; --i) {
+ int f;
+ f = dim_is_fixed(domain[order[i]].set, depth);
+ if (f < 0)
+ return -1;
+ if (f)
+ continue;
+ domain[order[i]].set = isl_ast_build_eliminate_inner(build,
+ domain[order[i]].set);
+ base = i;
+ }
+
+ return base;
+}
+
+/* Given an array "domain" of isl_set_map_pairs and an array "order"
+ * of indices into the "domain" array,
+ * find the element of "domain" (amongst those indexed by the first "n"
+ * elements of "order") with the "set" field that has the smallest
+ * value for the current iterator.
+ *
+ * Note that the domain with the smallest value may depend on the parameters
+ * and/or outer loop dimension. Since the result of this function is only
+ * used as heuristic, we only make a reasonable attempt at finding the best
+ * domain, one that should work in case a single domain provides the smallest
+ * value for the current dimension over all values of the parameters
+ * and outer dimensions.
+ *
+ * In particular, we compute the smallest value of the first domain
+ * and replace it by that of any later domain if that later domain
+ * has a smallest value that is smaller for at least some value
+ * of the parameters and outer dimensions.
+ */
+static int first_offset(struct isl_set_map_pair *domain, int *order, int n,
+ __isl_keep isl_ast_build *build)
+{
+ int i;
+ isl_map *min_first;
+ int first = 0;
+
+ min_first = isl_ast_build_map_to_iterator(build,
+ isl_set_copy(domain[order[0]].set));
+ min_first = isl_map_lexmin(min_first);
+
+ for (i = 1; i < n; ++i) {
+ isl_map *min, *test;
+ int empty;
+
+ min = isl_ast_build_map_to_iterator(build,
+ isl_set_copy(domain[order[i]].set));
+ min = isl_map_lexmin(min);
+ test = isl_map_copy(min);
+ test = isl_map_apply_domain(isl_map_copy(min_first), test);
+ test = isl_map_order_lt(test, isl_dim_in, 0, isl_dim_out, 0);
+ empty = isl_map_is_empty(test);
+ isl_map_free(test);
+ if (empty >= 0 && !empty) {
+ isl_map_free(min_first);
+ first = i;
+ min_first = min;
+ } else
+ isl_map_free(min);
+
+ if (empty < 0)
+ break;
+ }
+
+ isl_map_free(min_first);
+
+ return i < n ? -1 : first;
+}
+
+/* Construct a shifted inverse schedule based on the original inverse schedule,
+ * the stride and the offset.
+ *
+ * The original inverse schedule is specified as the "map" fields
+ * of the elements of "domain" indexed by the first "n" elements of "order".
+ *
+ * "stride" and "offset" are such that the difference
+ * between the values of the current dimension of domain "i"
+ * and the values of the current dimension for some reference domain are
+ * equal to
+ *
+ * stride * integer + offset[i]
+ *
+ * Moreover, 0 <= offset[i] < stride.
+ *
+ * For each domain, we create a map
+ *
+ * { [..., j, ...] -> [..., j - offset[i], offset[i], ....] }
+ *
+ * where j refers to the current dimension and the other dimensions are
+ * unchanged, and apply this map to the original schedule domain.
+ *
+ * For example, for the original schedule
+ *
+ * { A[i] -> [2i]: 0 <= i < 10; B[i] -> [2i+1] : 0 <= i < 10 }
+ *
+ * and assuming the offset is 0 for the A domain and 1 for the B domain,
+ * we apply the mapping
+ *
+ * { [j] -> [j, 0] }
+ *
+ * to the schedule of the "A" domain and the mapping
+ *
+ * { [j - 1] -> [j, 1] }
+ *
+ * to the schedule of the "B" domain.
+ *
+ *
+ * Note that after the transformation, the differences between pairs
+ * of values of the current dimension over all domains are multiples
+ * of stride and that we have therefore exposed the stride.
+ *
+ *
+ * To see that the mapping preserves the lexicographic order,
+ * first note that each of the individual maps above preserves the order.
+ * If the value of the current iterator is j1 in one domain and j2 in another,
+ * then if j1 = j2, we know that the same map is applied to both domains
+ * and the order is preserved.
+ * Otherwise, let us assume, without loss of generality, that j1 < j2.
+ * If c1 >= c2 (with c1 and c2 the corresponding offsets), then
+ *
+ * j1 - c1 < j2 - c2
+ *
+ * and the order is preserved.
+ * If c1 < c2, then we know
+ *
+ * 0 <= c2 - c1 < s
+ *
+ * We also have
+ *
+ * j2 - j1 = n * s + r
+ *
+ * with n >= 0 and 0 <= r < s.
+ * In other words, r = c2 - c1.
+ * If n > 0, then
+ *
+ * j1 - c1 < j2 - c2
+ *
+ * If n = 0, then
+ *
+ * j1 - c1 = j2 - c2
+ *
+ * and so
+ *
+ * (j1 - c1, c1) << (j2 - c2, c2)
+ *
+ * with "<<" the lexicographic order, proving that the order is preserved
+ * in all cases.
+ */
+static __isl_give isl_union_map *construct_shifted_executed(
+ struct isl_set_map_pair *domain, int *order, int n,
+ __isl_keep isl_val *stride, __isl_keep isl_multi_val *offset,
+ __isl_keep isl_ast_build *build)
+{
+ int i;
+ isl_union_map *executed;
+ isl_space *space;
+ isl_map *map;
+ isl_size depth;
+ isl_constraint *c;
+
+ depth = isl_ast_build_get_depth(build);
+ if (depth < 0)
+ return NULL;
+ space = isl_ast_build_get_space(build, 1);
+ executed = isl_union_map_empty(isl_space_copy(space));
+ space = isl_space_map_from_set(space);
+ map = isl_map_identity(isl_space_copy(space));
+ map = isl_map_eliminate(map, isl_dim_out, depth, 1);
+ map = isl_map_insert_dims(map, isl_dim_out, depth + 1, 1);
+ space = isl_space_insert_dims(space, isl_dim_out, depth + 1, 1);
+
+ c = isl_constraint_alloc_equality(isl_local_space_from_space(space));
+ c = isl_constraint_set_coefficient_si(c, isl_dim_in, depth, 1);
+ c = isl_constraint_set_coefficient_si(c, isl_dim_out, depth, -1);
+
+ for (i = 0; i < n; ++i) {
+ isl_map *map_i;
+ isl_val *v;
+
+ v = isl_multi_val_get_val(offset, i);
+ if (!v)
+ break;
+ map_i = isl_map_copy(map);
+ map_i = isl_map_fix_val(map_i, isl_dim_out, depth + 1,
+ isl_val_copy(v));
+ v = isl_val_neg(v);
+ c = isl_constraint_set_constant_val(c, v);
+ map_i = isl_map_add_constraint(map_i, isl_constraint_copy(c));
+
+ map_i = isl_map_apply_domain(isl_map_copy(domain[order[i]].map),
+ map_i);
+ executed = isl_union_map_add_map(executed, map_i);
+ }
+
+ isl_constraint_free(c);
+ isl_map_free(map);
+
+ if (i < n)
+ executed = isl_union_map_free(executed);
+
+ return executed;
+}
+
+/* Generate code for a single component, after exposing the stride,
+ * given that the schedule domain is "shifted strided".
+ *
+ * The component inverse schedule is specified as the "map" fields
+ * of the elements of "domain" indexed by the first "n" elements of "order".
+ *
+ * The schedule domain being "shifted strided" means that the differences
+ * between the values of the current dimension of domain "i"
+ * and the values of the current dimension for some reference domain are
+ * equal to
+ *
+ * stride * integer + offset[i]
+ *
+ * We first look for the domain with the "smallest" value for the current
+ * dimension and adjust the offsets such that the offset of the "smallest"
+ * domain is equal to zero. The other offsets are reduced modulo stride.
+ *
+ * Based on this information, we construct a new inverse schedule in
+ * construct_shifted_executed that exposes the stride.
+ * Since this involves the introduction of a new schedule dimension,
+ * the build needs to be changed accordingly.
+ * After computing the AST, the newly introduced dimension needs
+ * to be removed again from the list of grafts. We do this by plugging
+ * in a mapping that represents the new schedule domain in terms of the
+ * old schedule domain.
+ */
+static __isl_give isl_ast_graft_list *generate_shift_component(
+ struct isl_set_map_pair *domain, int *order, int n,
+ __isl_keep isl_val *stride, __isl_keep isl_multi_val *offset,
+ __isl_take isl_ast_build *build)
+{
+ isl_ast_graft_list *list;
+ int first;
+ isl_size depth;
+ isl_val *val;
+ isl_multi_val *mv;
+ isl_space *space;
+ isl_multi_aff *ma, *zero;
+ isl_union_map *executed;
+
+ depth = isl_ast_build_get_depth(build);
+
+ first = first_offset(domain, order, n, build);
+ if (depth < 0 || first < 0)
+ goto error;
+
+ mv = isl_multi_val_copy(offset);
+ val = isl_multi_val_get_val(offset, first);
+ val = isl_val_neg(val);
+ mv = isl_multi_val_add_val(mv, val);
+ mv = isl_multi_val_mod_val(mv, isl_val_copy(stride));
+
+ executed = construct_shifted_executed(domain, order, n, stride, mv,
+ build);
+ space = isl_ast_build_get_space(build, 1);
+ space = isl_space_map_from_set(space);
+ ma = isl_multi_aff_identity(isl_space_copy(space));
+ space = isl_space_from_domain(isl_space_domain(space));
+ space = isl_space_add_dims(space, isl_dim_out, 1);
+ zero = isl_multi_aff_zero(space);
+ ma = isl_multi_aff_range_splice(ma, depth + 1, zero);
+ build = isl_ast_build_insert_dim(build, depth + 1);
+ list = generate_shifted_component(executed, build);
+
+ list = isl_ast_graft_list_preimage_multi_aff(list, ma);
+
+ isl_multi_val_free(mv);
+
+ return list;
+error:
+ isl_ast_build_free(build);
+ return NULL;
+}
+
+/* Does any node in the schedule tree rooted at the current schedule node
+ * of "build" depend on outer schedule nodes?
+ */
+static int has_anchored_subtree(__isl_keep isl_ast_build *build)
+{
+ isl_schedule_node *node;
+ int dependent = 0;
+
+ node = isl_ast_build_get_schedule_node(build);
+ dependent = isl_schedule_node_is_subtree_anchored(node);
+ isl_schedule_node_free(node);
+
+ return dependent;
+}
+
+/* Generate code for a single component.
+ *
+ * The component inverse schedule is specified as the "map" fields
+ * of the elements of "domain" indexed by the first "n" elements of "order".
+ *
+ * This function may modify the "set" fields of "domain".
+ *
+ * Before proceeding with the actual code generation for the component,
+ * we first check if there are any "shifted" strides, meaning that
+ * the schedule domains of the individual domains are all strided,
+ * but that they have different offsets, resulting in the union
+ * of schedule domains not being strided anymore.
+ *
+ * The simplest example is the schedule
+ *
+ * { A[i] -> [2i]: 0 <= i < 10; B[i] -> [2i+1] : 0 <= i < 10 }
+ *
+ * Both schedule domains are strided, but their union is not.
+ * This function detects such cases and then rewrites the schedule to
+ *
+ * { A[i] -> [2i, 0]: 0 <= i < 10; B[i] -> [2i, 1] : 0 <= i < 10 }
+ *
+ * In the new schedule, the schedule domains have the same offset (modulo
+ * the stride), ensuring that the union of schedule domains is also strided.
+ *
+ *
+ * If there is only a single domain in the component, then there is
+ * nothing to do. Similarly, if the current schedule dimension has
+ * a fixed value for almost all domains then there is nothing to be done.
+ * In particular, we need at least two domains where the current schedule
+ * dimension does not have a fixed value.
+ * Finally, in case of a schedule map input,
+ * if any of the options refer to the current schedule dimension,
+ * then we bail out as well. It would be possible to reformulate the options
+ * in terms of the new schedule domain, but that would introduce constraints
+ * that separate the domains in the options and that is something we would
+ * like to avoid.
+ * In the case of a schedule tree input, we bail out if any of
+ * the descendants of the current schedule node refer to outer
+ * schedule nodes in any way.
+ *
+ *
+ * To see if there is any shifted stride, we look at the differences
+ * between the values of the current dimension in pairs of domains
+ * for equal values of outer dimensions. These differences should be
+ * of the form
+ *
+ * m x + r
+ *
+ * with "m" the stride and "r" a constant. Note that we cannot perform
+ * this analysis on individual domains as the lower bound in each domain
+ * may depend on parameters or outer dimensions and so the current dimension
+ * itself may not have a fixed remainder on division by the stride.
+ *
+ * In particular, we compare the first domain that does not have an
+ * obviously fixed value for the current dimension to itself and all
+ * other domains and collect the offsets and the gcd of the strides.
+ * If the gcd becomes one, then we failed to find shifted strides.
+ * If the gcd is zero, then the differences were all fixed, meaning
+ * that some domains had non-obviously fixed values for the current dimension.
+ * If all the offsets are the same (for those domains that do not have
+ * an obviously fixed value for the current dimension), then we do not
+ * apply the transformation.
+ * If none of the domains were skipped, then there is nothing to do.
+ * If some of them were skipped, then if we apply separation, the schedule
+ * domain should get split in pieces with a (non-shifted) stride.
+ *
+ * Otherwise, we apply a shift to expose the stride in
+ * generate_shift_component.
+ */
+static __isl_give isl_ast_graft_list *generate_component(
+ struct isl_set_map_pair *domain, int *order, int n,
+ __isl_take isl_ast_build *build)
+{
+ int i, d;
+ isl_size depth;
+ isl_ctx *ctx;
+ isl_map *map;
+ isl_set *deltas;
+ isl_val *gcd = NULL;
+ isl_multi_val *mv;
+ int fixed, skip;
+ int base;
+ isl_ast_graft_list *list;
+ int res = 0;
+
+ depth = isl_ast_build_get_depth(build);
+ if (depth < 0)
+ goto error;
+
+ skip = n == 1;
+ if (skip >= 0 && !skip)
+ skip = at_most_one_non_fixed(domain, order, n, depth);
+ if (skip >= 0 && !skip) {
+ if (isl_ast_build_has_schedule_node(build))
+ skip = has_anchored_subtree(build);
+ else
+ skip = isl_ast_build_options_involve_depth(build);
+ }
+ if (skip < 0)
+ goto error;
+ if (skip)
+ return generate_shifted_component_from_list(domain,
+ order, n, build);
+
+ base = eliminate_non_fixed(domain, order, n, depth, build);
+ if (base < 0)
+ goto error;
+
+ ctx = isl_ast_build_get_ctx(build);
+
+ mv = isl_multi_val_zero(isl_space_set_alloc(ctx, 0, n));
+
+ fixed = 1;
+ for (i = 0; i < n; ++i) {
+ isl_val *r, *m;
+
+ map = isl_map_from_domain_and_range(
+ isl_set_copy(domain[order[base]].set),
+ isl_set_copy(domain[order[i]].set));
+ for (d = 0; d < depth; ++d)
+ map = isl_map_equate(map, isl_dim_in, d,
+ isl_dim_out, d);
+ deltas = isl_map_deltas(map);
+ res = isl_set_dim_residue_class_val(deltas, depth, &m, &r);
+ isl_set_free(deltas);
+ if (res < 0)
+ break;
+
+ if (i == 0)
+ gcd = m;
+ else
+ gcd = isl_val_gcd(gcd, m);
+ if (isl_val_is_one(gcd)) {
+ isl_val_free(r);
+ break;
+ }
+ mv = isl_multi_val_set_val(mv, i, r);
+
+ res = dim_is_fixed(domain[order[i]].set, depth);
+ if (res < 0)
+ break;
+ if (res)
+ continue;
+
+ if (fixed && i > base) {
+ isl_val *a, *b;
+ a = isl_multi_val_get_val(mv, i);
+ b = isl_multi_val_get_val(mv, base);
+ if (isl_val_ne(a, b))
+ fixed = 0;
+ isl_val_free(a);
+ isl_val_free(b);
+ }
+ }
+
+ if (res < 0 || !gcd) {
+ isl_ast_build_free(build);
+ list = NULL;
+ } else if (i < n || fixed || isl_val_is_zero(gcd)) {
+ list = generate_shifted_component_from_list(domain,
+ order, n, build);
+ } else {
+ list = generate_shift_component(domain, order, n, gcd, mv,
+ build);
+ }
+
+ isl_val_free(gcd);
+ isl_multi_val_free(mv);
+
+ return list;
+error:
+ isl_ast_build_free(build);
+ return NULL;
+}
+
+/* Store both "map" itself and its domain in the
+ * structure pointed to by *next and advance to the next array element.
+ */
+static isl_stat extract_domain(__isl_take isl_map *map, void *user)
+{
+ struct isl_set_map_pair **next = user;
+
+ (*next)->map = isl_map_copy(map);
+ (*next)->set = isl_map_domain(map);
+ (*next)++;
+
+ return isl_stat_ok;
+}
+
+static isl_bool after_in_tree(__isl_keep isl_union_map *umap,
+ __isl_keep isl_schedule_node *node);
+
+/* Is any domain element of "umap" scheduled after any of
+ * the corresponding image elements by the tree rooted at
+ * the child of "node"?
+ */
+static isl_bool after_in_child(__isl_keep isl_union_map *umap,
+ __isl_keep isl_schedule_node *node)
+{
+ isl_schedule_node *child;
+ isl_bool after;
+
+ child = isl_schedule_node_get_child(node, 0);
+ after = after_in_tree(umap, child);
+ isl_schedule_node_free(child);
+
+ return after;
+}
+
+/* Is any domain element of "umap" scheduled after any of
+ * the corresponding image elements by the tree rooted at
+ * the band node "node"?
+ *
+ * We first check if any domain element is scheduled after any
+ * of the corresponding image elements by the band node itself.
+ * If not, we restrict "map" to those pairs of element that
+ * are scheduled together by the band node and continue with
+ * the child of the band node.
+ * If there are no such pairs then the map passed to after_in_child
+ * will be empty causing it to return 0.
+ */
+static isl_bool after_in_band(__isl_keep isl_union_map *umap,
+ __isl_keep isl_schedule_node *node)
+{
+ isl_multi_union_pw_aff *mupa;
+ isl_union_map *partial, *test, *gt, *universe, *umap1, *umap2;
+ isl_union_set *domain, *range;
+ isl_space *space;
+ isl_bool empty;
+ isl_bool after;
+ isl_size n;
+
+ n = isl_schedule_node_band_n_member(node);
+ if (n < 0)
+ return isl_bool_error;
+ if (n == 0)
+ return after_in_child(umap, node);
+
+ mupa = isl_schedule_node_band_get_partial_schedule(node);
+ space = isl_multi_union_pw_aff_get_space(mupa);
+ partial = isl_union_map_from_multi_union_pw_aff(mupa);
+ test = isl_union_map_copy(umap);
+ test = isl_union_map_apply_domain(test, isl_union_map_copy(partial));
+ test = isl_union_map_apply_range(test, isl_union_map_copy(partial));
+ gt = isl_union_map_from_map(isl_map_lex_gt(space));
+ test = isl_union_map_intersect(test, gt);
+ empty = isl_union_map_is_empty(test);
+ isl_union_map_free(test);
+
+ if (empty < 0 || !empty) {
+ isl_union_map_free(partial);
+ return isl_bool_not(empty);
+ }
+
+ universe = isl_union_map_universe(isl_union_map_copy(umap));
+ domain = isl_union_map_domain(isl_union_map_copy(universe));
+ range = isl_union_map_range(universe);
+ umap1 = isl_union_map_copy(partial);
+ umap1 = isl_union_map_intersect_domain(umap1, domain);
+ umap2 = isl_union_map_intersect_domain(partial, range);
+ test = isl_union_map_apply_range(umap1, isl_union_map_reverse(umap2));
+ test = isl_union_map_intersect(test, isl_union_map_copy(umap));
+ after = after_in_child(test, node);
+ isl_union_map_free(test);
+ return after;
+}
+
+/* Is any domain element of "umap" scheduled after any of
+ * the corresponding image elements by the tree rooted at
+ * the context node "node"?
+ *
+ * The context constraints apply to the schedule domain,
+ * so we cannot apply them directly to "umap", which contains
+ * pairs of statement instances. Instead, we add them
+ * to the range of the prefix schedule for both domain and
+ * range of "umap".
+ */
+static isl_bool after_in_context(__isl_keep isl_union_map *umap,
+ __isl_keep isl_schedule_node *node)
+{
+ isl_union_map *prefix, *universe, *umap1, *umap2;
+ isl_union_set *domain, *range;
+ isl_set *context;
+ isl_bool after;
+
+ umap = isl_union_map_copy(umap);
+ context = isl_schedule_node_context_get_context(node);
+ prefix = isl_schedule_node_get_prefix_schedule_union_map(node);
+ universe = isl_union_map_universe(isl_union_map_copy(umap));
+ domain = isl_union_map_domain(isl_union_map_copy(universe));
+ range = isl_union_map_range(universe);
+ umap1 = isl_union_map_copy(prefix);
+ umap1 = isl_union_map_intersect_domain(umap1, domain);
+ umap2 = isl_union_map_intersect_domain(prefix, range);
+ umap1 = isl_union_map_intersect_range(umap1,
+ isl_union_set_from_set(context));
+ umap1 = isl_union_map_apply_range(umap1, isl_union_map_reverse(umap2));
+ umap = isl_union_map_intersect(umap, umap1);
+
+ after = after_in_child(umap, node);
+
+ isl_union_map_free(umap);
+
+ return after;
+}
+
+/* Is any domain element of "umap" scheduled after any of
+ * the corresponding image elements by the tree rooted at
+ * the expansion node "node"?
+ *
+ * We apply the expansion to domain and range of "umap" and
+ * continue with its child.
+ */
+static isl_bool after_in_expansion(__isl_keep isl_union_map *umap,
+ __isl_keep isl_schedule_node *node)
+{
+ isl_union_map *expansion;
+ isl_bool after;
+
+ expansion = isl_schedule_node_expansion_get_expansion(node);
+ umap = isl_union_map_copy(umap);
+ umap = isl_union_map_apply_domain(umap, isl_union_map_copy(expansion));
+ umap = isl_union_map_apply_range(umap, expansion);
+
+ after = after_in_child(umap, node);
+
+ isl_union_map_free(umap);
+
+ return after;
+}
+
+/* Is any domain element of "umap" scheduled after any of
+ * the corresponding image elements by the tree rooted at
+ * the extension node "node"?
+ *
+ * Since the extension node may add statement instances before or
+ * after the pairs of statement instances in "umap", we return isl_bool_true
+ * to ensure that these pairs are not broken up.
+ */
+static isl_bool after_in_extension(__isl_keep isl_union_map *umap,
+ __isl_keep isl_schedule_node *node)
+{
+ return isl_bool_true;
+}
+
+/* Is any domain element of "umap" scheduled after any of
+ * the corresponding image elements by the tree rooted at
+ * the filter node "node"?
+ *
+ * We intersect domain and range of "umap" with the filter and
+ * continue with its child.
+ */
+static isl_bool after_in_filter(__isl_keep isl_union_map *umap,
+ __isl_keep isl_schedule_node *node)
+{
+ isl_union_set *filter;
+ isl_bool after;
+
+ umap = isl_union_map_copy(umap);
+ filter = isl_schedule_node_filter_get_filter(node);
+ umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(filter));
+ umap = isl_union_map_intersect_range(umap, filter);
+
+ after = after_in_child(umap, node);
+
+ isl_union_map_free(umap);
+
+ return after;
+}
+
+/* Is any domain element of "umap" scheduled after any of
+ * the corresponding image elements by the tree rooted at
+ * the set node "node"?
+ *
+ * This is only the case if this condition holds in any
+ * of the (filter) children of the set node.
+ * In particular, if the domain and the range of "umap"
+ * are contained in different children, then the condition
+ * does not hold.
+ */
+static isl_bool after_in_set(__isl_keep isl_union_map *umap,
+ __isl_keep isl_schedule_node *node)
+{
+ int i;
+ isl_size n;
+
+ n = isl_schedule_node_n_children(node);
+ if (n < 0)
+ return isl_bool_error;
+ for (i = 0; i < n; ++i) {
+ isl_schedule_node *child;
+ isl_bool after;
+
+ child = isl_schedule_node_get_child(node, i);
+ after = after_in_tree(umap, child);
+ isl_schedule_node_free(child);
+
+ if (after < 0 || after)
+ return after;
+ }
+
+ return isl_bool_false;
+}
+
+/* Return the filter of child "i" of "node".
+ */
+static __isl_give isl_union_set *child_filter(
+ __isl_keep isl_schedule_node *node, int i)
+{
+ isl_schedule_node *child;
+ isl_union_set *filter;
+
+ child = isl_schedule_node_get_child(node, i);
+ filter = isl_schedule_node_filter_get_filter(child);
+ isl_schedule_node_free(child);
+
+ return filter;
+}
+
+/* Is any domain element of "umap" scheduled after any of
+ * the corresponding image elements by the tree rooted at
+ * the sequence node "node"?
+ *
+ * This happens in particular if any domain element is
+ * contained in a later child than one containing a range element or
+ * if the condition holds within a given child in the sequence.
+ * The later part of the condition is checked by after_in_set.
+ */
+static isl_bool after_in_sequence(__isl_keep isl_union_map *umap,
+ __isl_keep isl_schedule_node *node)
+{
+ int i, j;
+ isl_size n;
+ isl_union_map *umap_i;
+ isl_bool empty;
+ isl_bool after = isl_bool_false;
+
+ n = isl_schedule_node_n_children(node);
+ if (n < 0)
+ return isl_bool_error;
+ for (i = 1; i < n; ++i) {
+ isl_union_set *filter_i;
+
+ umap_i = isl_union_map_copy(umap);
+ filter_i = child_filter(node, i);
+ umap_i = isl_union_map_intersect_domain(umap_i, filter_i);
+ empty = isl_union_map_is_empty(umap_i);
+ if (empty < 0)
+ goto error;
+ if (empty) {
+ isl_union_map_free(umap_i);
+ continue;
+ }
+
+ for (j = 0; j < i; ++j) {
+ isl_union_set *filter_j;
+ isl_union_map *umap_ij;
+
+ umap_ij = isl_union_map_copy(umap_i);
+ filter_j = child_filter(node, j);
+ umap_ij = isl_union_map_intersect_range(umap_ij,
+ filter_j);
+ empty = isl_union_map_is_empty(umap_ij);
+ isl_union_map_free(umap_ij);
+
+ if (empty < 0)
+ goto error;
+ if (!empty)
+ after = isl_bool_true;
+ if (after)
+ break;
+ }
+
+ isl_union_map_free(umap_i);
+ if (after)
+ break;
+ }
+
+ if (after < 0 || after)
+ return after;
+
+ return after_in_set(umap, node);
+error:
+ isl_union_map_free(umap_i);
+ return isl_bool_error;
+}
+
+/* Is any domain element of "umap" scheduled after any of
+ * the corresponding image elements by the tree rooted at "node"?
+ *
+ * If "umap" is empty, then clearly there is no such element.
+ * Otherwise, consider the different types of nodes separately.
+ */
+static isl_bool after_in_tree(__isl_keep isl_union_map *umap,
+ __isl_keep isl_schedule_node *node)
+{
+ isl_bool empty;
+ enum isl_schedule_node_type type;
+
+ empty = isl_union_map_is_empty(umap);
+ if (empty < 0)
+ return isl_bool_error;
+ if (empty)
+ return isl_bool_false;
+ if (!node)
+ return isl_bool_error;
+
+ type = isl_schedule_node_get_type(node);
+ switch (type) {
+ case isl_schedule_node_error:
+ return isl_bool_error;
+ case isl_schedule_node_leaf:
+ return isl_bool_false;
+ case isl_schedule_node_band:
+ return after_in_band(umap, node);
+ case isl_schedule_node_domain:
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_internal,
+ "unexpected internal domain node",
+ return isl_bool_error);
+ case isl_schedule_node_context:
+ return after_in_context(umap, node);
+ case isl_schedule_node_expansion:
+ return after_in_expansion(umap, node);
+ case isl_schedule_node_extension:
+ return after_in_extension(umap, node);
+ case isl_schedule_node_filter:
+ return after_in_filter(umap, node);
+ case isl_schedule_node_guard:
+ case isl_schedule_node_mark:
+ return after_in_child(umap, node);
+ case isl_schedule_node_set:
+ return after_in_set(umap, node);
+ case isl_schedule_node_sequence:
+ return after_in_sequence(umap, node);
+ }
+
+ return isl_bool_true;
+}
+
+/* Is any domain element of "map1" scheduled after any domain
+ * element of "map2" by the subtree underneath the current band node,
+ * while at the same time being scheduled together by the current
+ * band node, i.e., by "map1" and "map2?
+ *
+ * If the child of the current band node is a leaf, then
+ * no element can be scheduled after any other element.
+ *
+ * Otherwise, we construct a relation between domain elements
+ * of "map1" and domain elements of "map2" that are scheduled
+ * together and then check if the subtree underneath the current
+ * band node determines their relative order.
+ */
+static isl_bool after_in_subtree(__isl_keep isl_ast_build *build,
+ __isl_keep isl_map *map1, __isl_keep isl_map *map2)
+{
+ isl_schedule_node *node;
+ isl_map *map;
+ isl_union_map *umap;
+ isl_bool after;
+
+ node = isl_ast_build_get_schedule_node(build);
+ if (!node)
+ return isl_bool_error;
+ node = isl_schedule_node_child(node, 0);
+ if (isl_schedule_node_get_type(node) == isl_schedule_node_leaf) {
+ isl_schedule_node_free(node);
+ return isl_bool_false;
+ }
+ map = isl_map_copy(map2);
+ map = isl_map_apply_domain(map, isl_map_copy(map1));
+ umap = isl_union_map_from_map(map);
+ after = after_in_tree(umap, node);
+ isl_union_map_free(umap);
+ isl_schedule_node_free(node);
+ return after;
+}
+
+/* Internal data for any_scheduled_after.
+ *
+ * "build" is the build in which the AST is constructed.
+ * "depth" is the number of loops that have already been generated
+ * "group_coscheduled" is a local copy of options->ast_build_group_coscheduled
+ * "domain" is an array of set-map pairs corresponding to the different
+ * iteration domains. The set is the schedule domain, i.e., the domain
+ * of the inverse schedule, while the map is the inverse schedule itself.
+ */
+struct isl_any_scheduled_after_data {
+ isl_ast_build *build;
+ int depth;
+ int group_coscheduled;
+ struct isl_set_map_pair *domain;
+};
+
+/* Is any element of domain "i" scheduled after any element of domain "j"
+ * (for a common iteration of the first data->depth loops)?
+ *
+ * data->domain[i].set contains the domain of the inverse schedule
+ * for domain "i", i.e., elements in the schedule domain.
+ *
+ * If we are inside a band of a schedule tree and there is a pair
+ * of elements in the two domains that is schedule together by
+ * the current band, then we check if any element of "i" may be schedule
+ * after element of "j" by the descendants of the band node.
+ *
+ * If data->group_coscheduled is set, then we also return 1 if there
+ * is any pair of elements in the two domains that are scheduled together.
+ */
+static isl_bool any_scheduled_after(int i, int j, void *user)
+{
+ struct isl_any_scheduled_after_data *data = user;
+ isl_size dim = isl_set_dim(data->domain[i].set, isl_dim_set);
+ int pos;
+
+ if (dim < 0)
+ return isl_bool_error;
+
+ for (pos = data->depth; pos < dim; ++pos) {
+ int follows;
+
+ follows = isl_set_follows_at(data->domain[i].set,
+ data->domain[j].set, pos);
+
+ if (follows < -1)
+ return isl_bool_error;
+ if (follows > 0)
+ return isl_bool_true;
+ if (follows < 0)
+ return isl_bool_false;
+ }
+
+ if (isl_ast_build_has_schedule_node(data->build)) {
+ isl_bool after;
+
+ after = after_in_subtree(data->build, data->domain[i].map,
+ data->domain[j].map);
+ if (after < 0 || after)
+ return after;
+ }
+
+ return isl_bool_ok(data->group_coscheduled);
+}
+
+/* Look for independent components at the current depth and generate code
+ * for each component separately. The resulting lists of grafts are
+ * merged in an attempt to combine grafts with identical guards.
+ *
+ * Code for two domains can be generated separately if all the elements
+ * of one domain are scheduled before (or together with) all the elements
+ * of the other domain. We therefore consider the graph with as nodes
+ * the domains and an edge between two nodes if any element of the first
+ * node is scheduled after any element of the second node.
+ * If the ast_build_group_coscheduled is set, then we also add an edge if
+ * there is any pair of elements in the two domains that are scheduled
+ * together.
+ * Code is then generated (by generate_component)
+ * for each of the strongly connected components in this graph
+ * in their topological order.
+ *
+ * Since the test is performed on the domain of the inverse schedules of
+ * the different domains, we precompute these domains and store
+ * them in data.domain.
+ */
+static __isl_give isl_ast_graft_list *generate_components(
+ __isl_take isl_union_map *executed, __isl_take isl_ast_build *build)
+{
+ int i;
+ isl_ctx *ctx = isl_ast_build_get_ctx(build);
+ isl_size n = isl_union_map_n_map(executed);
+ isl_size depth;
+ struct isl_any_scheduled_after_data data;
+ struct isl_set_map_pair *next;
+ struct isl_tarjan_graph *g = NULL;
+ isl_ast_graft_list *list = NULL;
+ int n_domain = 0;
+
+ data.domain = NULL;
+ if (n < 0)
+ goto error;
+ data.domain = isl_calloc_array(ctx, struct isl_set_map_pair, n);
+ if (!data.domain)
+ goto error;
+ n_domain = n;
+
+ next = data.domain;
+ if (isl_union_map_foreach_map(executed, &extract_domain, &next) < 0)
+ goto error;
+
+ depth = isl_ast_build_get_depth(build);
+ if (depth < 0)
+ goto error;
+ data.build = build;
+ data.depth = depth;
+ data.group_coscheduled = isl_options_get_ast_build_group_coscheduled(ctx);
+ g = isl_tarjan_graph_init(ctx, n, &any_scheduled_after, &data);
+ if (!g)
+ goto error;
+
+ list = isl_ast_graft_list_alloc(ctx, 0);
+
+ i = 0;
+ while (list && n) {
+ isl_ast_graft_list *list_c;
+ int first = i;
+
+ if (g->order[i] == -1)
+ isl_die(ctx, isl_error_internal, "cannot happen",
+ goto error);
+ ++i; --n;
+ while (g->order[i] != -1) {
+ ++i; --n;
+ }
+
+ list_c = generate_component(data.domain,
+ g->order + first, i - first,
+ isl_ast_build_copy(build));
+ list = isl_ast_graft_list_merge(list, list_c, build);
+
+ ++i;
+ }
+
+ if (0)
+error: list = isl_ast_graft_list_free(list);
+ isl_tarjan_graph_free(g);
+ for (i = 0; i < n_domain; ++i) {
+ isl_map_free(data.domain[i].map);
+ isl_set_free(data.domain[i].set);
+ }
+ free(data.domain);
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+
+ return list;
+}
+
+/* Generate code for the next level (and all inner levels).
+ *
+ * If "executed" is empty, i.e., no code needs to be generated,
+ * then we return an empty list.
+ *
+ * If we have already generated code for all loop levels, then we pass
+ * control to generate_inner_level.
+ *
+ * If "executed" lives in a single space, i.e., if code needs to be
+ * generated for a single domain, then there can only be a single
+ * component and we go directly to generate_shifted_component.
+ * Otherwise, we call generate_components to detect the components
+ * and to call generate_component on each of them separately.
+ */
+static __isl_give isl_ast_graft_list *generate_next_level(
+ __isl_take isl_union_map *executed, __isl_take isl_ast_build *build)
+{
+ isl_size depth;
+ isl_size dim;
+ isl_size n;
+
+ if (!build || !executed)
+ goto error;
+
+ if (isl_union_map_is_empty(executed)) {
+ isl_ctx *ctx = isl_ast_build_get_ctx(build);
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+ return isl_ast_graft_list_alloc(ctx, 0);
+ }
+
+ depth = isl_ast_build_get_depth(build);
+ dim = isl_ast_build_dim(build, isl_dim_set);
+ if (depth < 0 || dim < 0)
+ goto error;
+ if (depth >= dim)
+ return generate_inner_level(executed, build);
+
+ n = isl_union_map_n_map(executed);
+ if (n < 0)
+ goto error;
+ if (n == 1)
+ return generate_shifted_component(executed, build);
+
+ return generate_components(executed, build);
+error:
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+ return NULL;
+}
+
+/* Internal data structure used by isl_ast_build_node_from_schedule_map.
+ * internal, executed and build are the inputs to generate_code.
+ * list collects the output.
+ */
+struct isl_generate_code_data {
+ int internal;
+ isl_union_map *executed;
+ isl_ast_build *build;
+
+ isl_ast_graft_list *list;
+};
+
+/* Given an inverse schedule in terms of the external build schedule, i.e.,
+ *
+ * [E -> S] -> D
+ *
+ * with E the external build schedule and S the additional schedule "space",
+ * reformulate the inverse schedule in terms of the internal schedule domain,
+ * i.e., return
+ *
+ * [I -> S] -> D
+ *
+ * We first obtain a mapping
+ *
+ * I -> E
+ *
+ * take the inverse and the product with S -> S, resulting in
+ *
+ * [I -> S] -> [E -> S]
+ *
+ * Applying the map to the input produces the desired result.
+ */
+static __isl_give isl_union_map *internal_executed(
+ __isl_take isl_union_map *executed, __isl_keep isl_space *space,
+ __isl_keep isl_ast_build *build)
+{
+ isl_map *id, *proj;
+
+ proj = isl_ast_build_get_schedule_map(build);
+ proj = isl_map_reverse(proj);
+ space = isl_space_map_from_set(isl_space_copy(space));
+ id = isl_map_identity(space);
+ proj = isl_map_product(proj, id);
+ executed = isl_union_map_apply_domain(executed,
+ isl_union_map_from_map(proj));
+ return executed;
+}
+
+/* Generate an AST that visits the elements in the range of data->executed
+ * in the relative order specified by the corresponding domain element(s)
+ * for those domain elements that belong to "set".
+ * Add the result to data->list.
+ *
+ * The caller ensures that "set" is a universe domain.
+ * "space" is the space of the additional part of the schedule.
+ * It is equal to the space of "set" if build->domain is parametric.
+ * Otherwise, it is equal to the range of the wrapped space of "set".
+ *
+ * If the build space is not parametric and
+ * if isl_ast_build_node_from_schedule_map
+ * was called from an outside user (data->internal not set), then
+ * the (inverse) schedule refers to the external build domain and needs to
+ * be transformed to refer to the internal build domain.
+ *
+ * If the build space is parametric, then we add some of the parameter
+ * constraints to the executed relation. Adding these constraints
+ * allows for an earlier detection of conflicts in some cases.
+ * However, we do not want to divide the executed relation into
+ * more disjuncts than necessary. We therefore approximate
+ * the constraints on the parameters by a single disjunct set.
+ *
+ * The build is extended to include the additional part of the schedule.
+ * If the original build space was not parametric, then the options
+ * in data->build refer only to the additional part of the schedule
+ * and they need to be adjusted to refer to the complete AST build
+ * domain.
+ *
+ * After having adjusted inverse schedule and build, we start generating
+ * code with the outer loop of the current code generation
+ * in generate_next_level.
+ *
+ * If the original build space was not parametric, we undo the embedding
+ * on the resulting isl_ast_node_list so that it can be used within
+ * the outer AST build.
+ */
+static isl_stat generate_code_in_space(struct isl_generate_code_data *data,
+ __isl_take isl_set *set, __isl_take isl_space *space)
+{
+ isl_union_map *executed;
+ isl_ast_build *build;
+ isl_ast_graft_list *list;
+ int embed;
+
+ executed = isl_union_map_copy(data->executed);
+ executed = isl_union_map_intersect_domain(executed,
+ isl_union_set_from_set(set));
+
+ embed = !isl_set_is_params(data->build->domain);
+ if (embed && !data->internal)
+ executed = internal_executed(executed, space, data->build);
+ if (!embed) {
+ isl_set *domain;
+ domain = isl_ast_build_get_domain(data->build);
+ domain = isl_set_from_basic_set(isl_set_simple_hull(domain));
+ executed = isl_union_map_intersect_params(executed, domain);
+ }
+
+ build = isl_ast_build_copy(data->build);
+ build = isl_ast_build_product(build, space);
+
+ list = generate_next_level(executed, build);
+
+ list = isl_ast_graft_list_unembed(list, embed);
+
+ data->list = isl_ast_graft_list_concat(data->list, list);
+
+ return isl_stat_ok;
+}
+
+/* Generate an AST that visits the elements in the range of data->executed
+ * in the relative order specified by the corresponding domain element(s)
+ * for those domain elements that belong to "set".
+ * Add the result to data->list.
+ *
+ * The caller ensures that "set" is a universe domain.
+ *
+ * If the build space S is not parametric, then the space of "set"
+ * need to be a wrapped relation with S as domain. That is, it needs
+ * to be of the form
+ *
+ * [S -> T]
+ *
+ * Check this property and pass control to generate_code_in_space
+ * passing along T.
+ * If the build space is not parametric, then T is the space of "set".
+ */
+static isl_stat generate_code_set(__isl_take isl_set *set, void *user)
+{
+ struct isl_generate_code_data *data = user;
+ isl_space *space, *build_space;
+ int is_domain;
+
+ space = isl_set_get_space(set);
+
+ if (isl_set_is_params(data->build->domain))
+ return generate_code_in_space(data, set, space);
+
+ build_space = isl_ast_build_get_space(data->build, data->internal);
+ space = isl_space_unwrap(space);
+ is_domain = isl_space_is_domain(build_space, space);
+ isl_space_free(build_space);
+ space = isl_space_range(space);
+
+ if (is_domain < 0)
+ goto error;
+ if (!is_domain)
+ isl_die(isl_set_get_ctx(set), isl_error_invalid,
+ "invalid nested schedule space", goto error);
+
+ return generate_code_in_space(data, set, space);
+error:
+ isl_set_free(set);
+ isl_space_free(space);
+ return isl_stat_error;
+}
+
+/* Generate an AST that visits the elements in the range of "executed"
+ * in the relative order specified by the corresponding domain element(s).
+ *
+ * "build" is an isl_ast_build that has either been constructed by
+ * isl_ast_build_from_context or passed to a callback set by
+ * isl_ast_build_set_create_leaf.
+ * In the first case, the space of the isl_ast_build is typically
+ * a parametric space, although this is currently not enforced.
+ * In the second case, the space is never a parametric space.
+ * If the space S is not parametric, then the domain space(s) of "executed"
+ * need to be wrapped relations with S as domain.
+ *
+ * If the domain of "executed" consists of several spaces, then an AST
+ * is generated for each of them (in arbitrary order) and the results
+ * are concatenated.
+ *
+ * If "internal" is set, then the domain "S" above refers to the internal
+ * schedule domain representation. Otherwise, it refers to the external
+ * representation, as returned by isl_ast_build_get_schedule_space.
+ *
+ * We essentially run over all the spaces in the domain of "executed"
+ * and call generate_code_set on each of them.
+ */
+static __isl_give isl_ast_graft_list *generate_code(
+ __isl_take isl_union_map *executed, __isl_take isl_ast_build *build,
+ int internal)
+{
+ isl_ctx *ctx;
+ struct isl_generate_code_data data = { 0 };
+ isl_space *space;
+ isl_union_set *schedule_domain;
+ isl_union_map *universe;
+
+ if (!build)
+ goto error;
+ space = isl_ast_build_get_space(build, 1);
+ space = isl_space_align_params(space,
+ isl_union_map_get_space(executed));
+ space = isl_space_align_params(space,
+ isl_union_map_get_space(build->options));
+ build = isl_ast_build_align_params(build, isl_space_copy(space));
+ executed = isl_union_map_align_params(executed, space);
+ if (!executed || !build)
+ goto error;
+
+ ctx = isl_ast_build_get_ctx(build);
+
+ data.internal = internal;
+ data.executed = executed;
+ data.build = build;
+ data.list = isl_ast_graft_list_alloc(ctx, 0);
+
+ universe = isl_union_map_universe(isl_union_map_copy(executed));
+ schedule_domain = isl_union_map_domain(universe);
+ if (isl_union_set_foreach_set(schedule_domain, &generate_code_set,
+ &data) < 0)
+ data.list = isl_ast_graft_list_free(data.list);
+
+ isl_union_set_free(schedule_domain);
+ isl_union_map_free(executed);
+
+ isl_ast_build_free(build);
+ return data.list;
+error:
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+ return NULL;
+}
+
+/* Generate an AST that visits the elements in the domain of "schedule"
+ * in the relative order specified by the corresponding image element(s).
+ *
+ * "build" is an isl_ast_build that has either been constructed by
+ * isl_ast_build_from_context or passed to a callback set by
+ * isl_ast_build_set_create_leaf.
+ * In the first case, the space of the isl_ast_build is typically
+ * a parametric space, although this is currently not enforced.
+ * In the second case, the space is never a parametric space.
+ * If the space S is not parametric, then the range space(s) of "schedule"
+ * need to be wrapped relations with S as domain.
+ *
+ * If the range of "schedule" consists of several spaces, then an AST
+ * is generated for each of them (in arbitrary order) and the results
+ * are concatenated.
+ *
+ * We first initialize the local copies of the relevant options.
+ * We do this here rather than when the isl_ast_build is created
+ * because the options may have changed between the construction
+ * of the isl_ast_build and the call to isl_generate_code.
+ *
+ * The main computation is performed on an inverse schedule (with
+ * the schedule domain in the domain and the elements to be executed
+ * in the range) called "executed".
+ */
+__isl_give isl_ast_node *isl_ast_build_node_from_schedule_map(
+ __isl_keep isl_ast_build *build, __isl_take isl_union_map *schedule)
+{
+ isl_ast_graft_list *list;
+ isl_ast_node *node;
+ isl_union_map *executed;
+
+ build = isl_ast_build_copy(build);
+ build = isl_ast_build_set_single_valued(build, 0);
+ schedule = isl_union_map_coalesce(schedule);
+ schedule = isl_union_map_remove_redundancies(schedule);
+ executed = isl_union_map_reverse(schedule);
+ list = generate_code(executed, isl_ast_build_copy(build), 0);
+ node = isl_ast_node_from_graft_list(list, build);
+ isl_ast_build_free(build);
+
+ return node;
+}
+
+/* The old name for isl_ast_build_node_from_schedule_map.
+ * It is being kept for backward compatibility, but
+ * it will be removed in the future.
+ */
+__isl_give isl_ast_node *isl_ast_build_ast_from_schedule(
+ __isl_keep isl_ast_build *build, __isl_take isl_union_map *schedule)
+{
+ return isl_ast_build_node_from_schedule_map(build, schedule);
+}
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the leaf node "node".
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * Simply pass control to generate_inner_level.
+ * Note that the current build does not refer to any band node, so
+ * that generate_inner_level will not try to visit the child of
+ * the leaf node.
+ *
+ * If multiple statement instances reach a leaf,
+ * then they can be executed in any order.
+ * Group the list of grafts based on shared guards
+ * such that identical guards are only generated once
+ * when the list is eventually passed on to isl_ast_graft_list_fuse.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_leaf(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed)
+{
+ isl_ast_graft_list *list;
+
+ isl_schedule_node_free(node);
+ list = generate_inner_level(executed, isl_ast_build_copy(build));
+ list = isl_ast_graft_list_group_on_guard(list, build);
+ isl_ast_build_free(build);
+
+ return list;
+}
+
+/* Check that the band partial schedule "partial" does not filter out
+ * any statement instances, as specified by the range of "executed".
+ */
+static isl_stat check_band_schedule_total_on_instances(
+ __isl_keep isl_multi_union_pw_aff *partial,
+ __isl_keep isl_union_map *executed)
+{
+ isl_bool subset;
+ isl_union_set *domain, *instances;
+
+ instances = isl_union_map_range(isl_union_map_copy(executed));
+ partial = isl_multi_union_pw_aff_copy(partial);
+ domain = isl_multi_union_pw_aff_domain(partial);
+ subset = isl_union_set_is_subset(instances, domain);
+ isl_union_set_free(domain);
+ isl_union_set_free(instances);
+
+ if (subset < 0)
+ return isl_stat_error;
+ if (!subset)
+ isl_die(isl_union_map_get_ctx(executed), isl_error_invalid,
+ "band node is not allowed to drop statement instances",
+ return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the band node "node" and its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * If the band is empty, we continue with its descendants.
+ * Otherwise, we extend the build and the inverse schedule with
+ * the additional space/partial schedule and continue generating
+ * an AST in generate_next_level.
+ * As soon as we have extended the inverse schedule with the additional
+ * partial schedule, we look for equalities that may exists between
+ * the old and the new part.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_band(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed)
+{
+ isl_space *space;
+ isl_multi_union_pw_aff *extra;
+ isl_union_map *extra_umap;
+ isl_ast_graft_list *list;
+ isl_size n1, n2;
+ isl_size n;
+
+ n = isl_schedule_node_band_n_member(node);
+ if (!build || n < 0 || !executed)
+ goto error;
+
+ if (n == 0)
+ return build_ast_from_child(build, node, executed);
+
+ extra = isl_schedule_node_band_get_partial_schedule(node);
+ extra = isl_multi_union_pw_aff_align_params(extra,
+ isl_ast_build_get_space(build, 1));
+ space = isl_multi_union_pw_aff_get_space(extra);
+
+ if (check_band_schedule_total_on_instances(extra, executed) < 0)
+ executed = isl_union_map_free(executed);
+
+ extra_umap = isl_union_map_from_multi_union_pw_aff(extra);
+ extra_umap = isl_union_map_reverse(extra_umap);
+
+ executed = isl_union_map_domain_product(executed, extra_umap);
+ executed = isl_union_map_detect_equalities(executed);
+
+ n1 = isl_ast_build_dim(build, isl_dim_param);
+ build = isl_ast_build_product(build, space);
+ n2 = isl_ast_build_dim(build, isl_dim_param);
+ if (n1 < 0 || n2 < 0)
+ build = isl_ast_build_free(build);
+ else if (n2 > n1)
+ isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,
+ "band node is not allowed to introduce new parameters",
+ build = isl_ast_build_free(build));
+ build = isl_ast_build_set_schedule_node(build, node);
+
+ list = generate_next_level(executed, build);
+
+ list = isl_ast_graft_list_unembed(list, 1);
+
+ return list;
+error:
+ isl_schedule_node_free(node);
+ isl_union_map_free(executed);
+ isl_ast_build_free(build);
+ return NULL;
+}
+
+/* Hoist a list of grafts (in practice containing a single graft)
+ * from "sub_build" (which includes extra context information)
+ * to "build".
+ *
+ * In particular, project out all additional parameters introduced
+ * by the context node from the enforced constraints and the guard
+ * of the single graft.
+ */
+static __isl_give isl_ast_graft_list *hoist_out_of_context(
+ __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build,
+ __isl_keep isl_ast_build *sub_build)
+{
+ isl_ast_graft *graft;
+ isl_basic_set *enforced;
+ isl_set *guard;
+ isl_size n_param, extra_param;
+
+ n_param = isl_ast_build_dim(build, isl_dim_param);
+ extra_param = isl_ast_build_dim(sub_build, isl_dim_param);
+ if (n_param < 0 || extra_param < 0)
+ return isl_ast_graft_list_free(list);
+
+ if (extra_param == n_param)
+ return list;
+
+ extra_param -= n_param;
+ enforced = isl_ast_graft_list_extract_shared_enforced(list, sub_build);
+ enforced = isl_basic_set_project_out(enforced, isl_dim_param,
+ n_param, extra_param);
+ enforced = isl_basic_set_remove_unknown_divs(enforced);
+ guard = isl_ast_graft_list_extract_hoistable_guard(list, sub_build);
+ guard = isl_set_remove_divs_involving_dims(guard, isl_dim_param,
+ n_param, extra_param);
+ guard = isl_set_project_out(guard, isl_dim_param, n_param, extra_param);
+ guard = isl_set_compute_divs(guard);
+ graft = isl_ast_graft_alloc_from_children(list, guard, enforced,
+ build, sub_build);
+ list = isl_ast_graft_list_from_ast_graft(graft);
+
+ return list;
+}
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the context node "node"
+ * and its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * The context node may introduce additional parameters as well as
+ * constraints on the outer schedule dimensions or original parameters.
+ *
+ * We add the extra parameters to a new build and the context
+ * constraints to both the build and (as a single disjunct)
+ * to the domain of "executed". Since the context constraints
+ * are specified in terms of the input schedule, we first need
+ * to map them to the internal schedule domain.
+ *
+ * After constructing the AST from the descendants of "node",
+ * we combine the list of grafts into a single graft within
+ * the new build, in order to be able to exploit the additional
+ * context constraints during this combination.
+ *
+ * Additionally, if the current node is the outermost node in
+ * the schedule tree (apart from the root domain node), we generate
+ * all pending guards, again to be able to exploit the additional
+ * context constraints. We currently do not do this for internal
+ * context nodes since we may still want to hoist conditions
+ * to outer AST nodes.
+ *
+ * If the context node introduced any new parameters, then they
+ * are removed from the set of enforced constraints and guard
+ * in hoist_out_of_context.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_context(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed)
+{
+ isl_set *context;
+ isl_space *space;
+ isl_multi_aff *internal2input;
+ isl_ast_build *sub_build;
+ isl_ast_graft_list *list;
+ isl_size n;
+ isl_size depth;
+
+ depth = isl_schedule_node_get_tree_depth(node);
+ if (depth < 0)
+ build = isl_ast_build_free(build);
+ space = isl_ast_build_get_space(build, 1);
+ context = isl_schedule_node_context_get_context(node);
+ context = isl_set_align_params(context, space);
+ sub_build = isl_ast_build_copy(build);
+ space = isl_set_get_space(context);
+ sub_build = isl_ast_build_align_params(sub_build, space);
+ internal2input = isl_ast_build_get_internal2input(sub_build);
+ context = isl_set_preimage_multi_aff(context, internal2input);
+ sub_build = isl_ast_build_restrict_generated(sub_build,
+ isl_set_copy(context));
+ context = isl_set_from_basic_set(isl_set_simple_hull(context));
+ executed = isl_union_map_intersect_domain(executed,
+ isl_union_set_from_set(context));
+
+ list = build_ast_from_child(isl_ast_build_copy(sub_build),
+ node, executed);
+ n = isl_ast_graft_list_n_ast_graft(list);
+ if (n < 0)
+ list = isl_ast_graft_list_free(list);
+
+ list = isl_ast_graft_list_fuse(list, sub_build);
+ if (depth == 1)
+ list = isl_ast_graft_list_insert_pending_guard_nodes(list,
+ sub_build);
+ if (n >= 1)
+ list = hoist_out_of_context(list, build, sub_build);
+
+ isl_ast_build_free(build);
+ isl_ast_build_free(sub_build);
+
+ return list;
+}
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the expansion node "node" and
+ * its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * We expand the domain elements by the expansion and
+ * continue with the descendants of the node.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_expansion(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed)
+{
+ isl_union_map *expansion;
+ isl_size n1, n2;
+
+ expansion = isl_schedule_node_expansion_get_expansion(node);
+ expansion = isl_union_map_align_params(expansion,
+ isl_union_map_get_space(executed));
+
+ n1 = isl_union_map_dim(executed, isl_dim_param);
+ executed = isl_union_map_apply_range(executed, expansion);
+ n2 = isl_union_map_dim(executed, isl_dim_param);
+ if (n1 < 0 || n2 < 0)
+ goto error;
+ if (n2 > n1)
+ isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,
+ "expansion node is not allowed to introduce "
+ "new parameters", goto error);
+
+ return build_ast_from_child(build, node, executed);
+error:
+ isl_ast_build_free(build);
+ isl_schedule_node_free(node);
+ isl_union_map_free(executed);
+ return NULL;
+}
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the extension node "node" and
+ * its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * Extend the inverse schedule with the extension applied to current
+ * set of generated constraints. Since the extension if formulated
+ * in terms of the input schedule, it first needs to be transformed
+ * to refer to the internal schedule.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_extension(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed)
+{
+ isl_union_set *schedule_domain;
+ isl_union_map *extension;
+ isl_set *set;
+
+ set = isl_ast_build_get_generated(build);
+ set = isl_set_from_basic_set(isl_set_simple_hull(set));
+ schedule_domain = isl_union_set_from_set(set);
+
+ extension = isl_schedule_node_extension_get_extension(node);
+
+ extension = isl_union_map_preimage_domain_multi_aff(extension,
+ isl_multi_aff_copy(build->internal2input));
+ extension = isl_union_map_intersect_domain(extension, schedule_domain);
+ extension = isl_ast_build_substitute_values_union_map_domain(build,
+ extension);
+ executed = isl_union_map_union(executed, extension);
+
+ return build_ast_from_child(build, node, executed);
+}
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the filter node "node" and
+ * its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * We simply intersect the iteration domain (i.e., the range of "executed")
+ * with the filter and continue with the descendants of the node,
+ * unless the resulting inverse schedule is empty, in which
+ * case we return an empty list.
+ *
+ * If the result of the intersection is equal to the original "executed"
+ * relation, then keep the original representation since the intersection
+ * may have unnecessarily broken up the relation into a greater number
+ * of disjuncts.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_filter(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed)
+{
+ isl_ctx *ctx;
+ isl_union_set *filter;
+ isl_union_map *orig;
+ isl_ast_graft_list *list;
+ int empty;
+ isl_bool unchanged;
+ isl_size n1, n2;
+
+ orig = isl_union_map_copy(executed);
+ if (!build || !node || !executed)
+ goto error;
+
+ filter = isl_schedule_node_filter_get_filter(node);
+ filter = isl_union_set_align_params(filter,
+ isl_union_map_get_space(executed));
+ n1 = isl_union_map_dim(executed, isl_dim_param);
+ executed = isl_union_map_intersect_range(executed, filter);
+ n2 = isl_union_map_dim(executed, isl_dim_param);
+ if (n1 < 0 || n2 < 0)
+ goto error;
+ if (n2 > n1)
+ isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,
+ "filter node is not allowed to introduce "
+ "new parameters", goto error);
+
+ unchanged = isl_union_map_is_subset(orig, executed);
+ empty = isl_union_map_is_empty(executed);
+ if (unchanged < 0 || empty < 0)
+ goto error;
+ if (unchanged) {
+ isl_union_map_free(executed);
+ return build_ast_from_child(build, node, orig);
+ }
+ isl_union_map_free(orig);
+ if (!empty)
+ return build_ast_from_child(build, node, executed);
+
+ ctx = isl_ast_build_get_ctx(build);
+ list = isl_ast_graft_list_alloc(ctx, 0);
+ isl_ast_build_free(build);
+ isl_schedule_node_free(node);
+ isl_union_map_free(executed);
+ return list;
+error:
+ isl_ast_build_free(build);
+ isl_schedule_node_free(node);
+ isl_union_map_free(executed);
+ isl_union_map_free(orig);
+ return NULL;
+}
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the guard node "node" and
+ * its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * Ensure that the associated guard is enforced by the outer AST
+ * constructs by adding it to the guard of the graft.
+ * Since we know that we will enforce the guard, we can also include it
+ * in the generated constraints used to construct an AST for
+ * the descendant nodes.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_guard(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed)
+{
+ isl_space *space;
+ isl_set *guard, *hoisted;
+ isl_basic_set *enforced;
+ isl_ast_build *sub_build;
+ isl_ast_graft *graft;
+ isl_ast_graft_list *list;
+ isl_size n1, n2, n;
+
+ space = isl_ast_build_get_space(build, 1);
+ guard = isl_schedule_node_guard_get_guard(node);
+ n1 = isl_space_dim(space, isl_dim_param);
+ guard = isl_set_align_params(guard, space);
+ n2 = isl_set_dim(guard, isl_dim_param);
+ if (n1 < 0 || n2 < 0)
+ guard = isl_set_free(guard);
+ else if (n2 > n1)
+ isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,
+ "guard node is not allowed to introduce "
+ "new parameters", guard = isl_set_free(guard));
+ guard = isl_set_preimage_multi_aff(guard,
+ isl_multi_aff_copy(build->internal2input));
+ guard = isl_ast_build_specialize(build, guard);
+ guard = isl_set_gist(guard, isl_set_copy(build->generated));
+
+ sub_build = isl_ast_build_copy(build);
+ sub_build = isl_ast_build_restrict_generated(sub_build,
+ isl_set_copy(guard));
+
+ list = build_ast_from_child(isl_ast_build_copy(sub_build),
+ node, executed);
+
+ hoisted = isl_ast_graft_list_extract_hoistable_guard(list, sub_build);
+ n = isl_set_n_basic_set(hoisted);
+ if (n < 0)
+ list = isl_ast_graft_list_free(list);
+ if (n > 1)
+ list = isl_ast_graft_list_gist_guards(list,
+ isl_set_copy(hoisted));
+ guard = isl_set_intersect(guard, hoisted);
+ enforced = extract_shared_enforced(list, build);
+ graft = isl_ast_graft_alloc_from_children(list, guard, enforced,
+ build, sub_build);
+
+ isl_ast_build_free(sub_build);
+ isl_ast_build_free(build);
+ return isl_ast_graft_list_from_ast_graft(graft);
+}
+
+/* Call the before_each_mark callback, if requested by the user.
+ *
+ * Return 0 on success and -1 on error.
+ *
+ * The caller is responsible for recording the current inverse schedule
+ * in "build".
+ */
+static isl_stat before_each_mark(__isl_keep isl_id *mark,
+ __isl_keep isl_ast_build *build)
+{
+ if (!build)
+ return isl_stat_error;
+ if (!build->before_each_mark)
+ return isl_stat_ok;
+ return build->before_each_mark(mark, build,
+ build->before_each_mark_user);
+}
+
+/* Call the after_each_mark callback, if requested by the user.
+ *
+ * The caller is responsible for recording the current inverse schedule
+ * in "build".
+ */
+static __isl_give isl_ast_graft *after_each_mark(
+ __isl_take isl_ast_graft *graft, __isl_keep isl_ast_build *build)
+{
+ if (!graft || !build)
+ return isl_ast_graft_free(graft);
+ if (!build->after_each_mark)
+ return graft;
+ graft->node = build->after_each_mark(graft->node, build,
+ build->after_each_mark_user);
+ if (!graft->node)
+ return isl_ast_graft_free(graft);
+ return graft;
+}
+
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the mark node "node" and
+ * its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+
+ * Since we may be calling before_each_mark and after_each_mark
+ * callbacks, we record the current inverse schedule in the build.
+ *
+ * We generate an AST for the child of the mark node, combine
+ * the graft list into a single graft and then insert the mark
+ * in the AST of that single graft.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_mark(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed)
+{
+ isl_id *mark;
+ isl_ast_graft *graft;
+ isl_ast_graft_list *list;
+ isl_size n;
+
+ build = isl_ast_build_set_executed(build, isl_union_map_copy(executed));
+
+ mark = isl_schedule_node_mark_get_id(node);
+ if (before_each_mark(mark, build) < 0)
+ node = isl_schedule_node_free(node);
+
+ list = build_ast_from_child(isl_ast_build_copy(build), node, executed);
+ list = isl_ast_graft_list_fuse(list, build);
+ n = isl_ast_graft_list_n_ast_graft(list);
+ if (n < 0)
+ list = isl_ast_graft_list_free(list);
+ if (n == 0) {
+ isl_id_free(mark);
+ } else {
+ graft = isl_ast_graft_list_get_ast_graft(list, 0);
+ graft = isl_ast_graft_insert_mark(graft, mark);
+ graft = after_each_mark(graft, build);
+ list = isl_ast_graft_list_set_ast_graft(list, 0, graft);
+ }
+ isl_ast_build_free(build);
+
+ return list;
+}
+
+static __isl_give isl_ast_graft_list *build_ast_from_schedule_node(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed);
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the sequence (or set) node "node" and
+ * its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * We simply generate an AST for each of the children and concatenate
+ * the results.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_sequence(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_ast_graft_list *list;
+
+ ctx = isl_ast_build_get_ctx(build);
+ list = isl_ast_graft_list_alloc(ctx, 0);
+
+ n = isl_schedule_node_n_children(node);
+ if (n < 0)
+ list = isl_ast_graft_list_free(list);
+ for (i = 0; i < n; ++i) {
+ isl_schedule_node *child;
+ isl_ast_graft_list *list_i;
+
+ child = isl_schedule_node_get_child(node, i);
+ list_i = build_ast_from_schedule_node(isl_ast_build_copy(build),
+ child, isl_union_map_copy(executed));
+ list = isl_ast_graft_list_concat(list, list_i);
+ }
+ isl_ast_build_free(build);
+ isl_schedule_node_free(node);
+ isl_union_map_free(executed);
+
+ return list;
+}
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the node "node" and its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * The node types are handled in separate functions.
+ * Set nodes are currently treated in the same way as sequence nodes.
+ * The children of a set node may be executed in any order,
+ * including the order of the children.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_schedule_node(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed)
+{
+ enum isl_schedule_node_type type;
+
+ type = isl_schedule_node_get_type(node);
+
+ switch (type) {
+ case isl_schedule_node_error:
+ goto error;
+ case isl_schedule_node_leaf:
+ return build_ast_from_leaf(build, node, executed);
+ case isl_schedule_node_band:
+ return build_ast_from_band(build, node, executed);
+ case isl_schedule_node_context:
+ return build_ast_from_context(build, node, executed);
+ case isl_schedule_node_domain:
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported,
+ "unexpected internal domain node", goto error);
+ case isl_schedule_node_expansion:
+ return build_ast_from_expansion(build, node, executed);
+ case isl_schedule_node_extension:
+ return build_ast_from_extension(build, node, executed);
+ case isl_schedule_node_filter:
+ return build_ast_from_filter(build, node, executed);
+ case isl_schedule_node_guard:
+ return build_ast_from_guard(build, node, executed);
+ case isl_schedule_node_mark:
+ return build_ast_from_mark(build, node, executed);
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ return build_ast_from_sequence(build, node, executed);
+ }
+
+ isl_die(isl_ast_build_get_ctx(build), isl_error_internal,
+ "unhandled type", goto error);
+error:
+ isl_union_map_free(executed);
+ isl_schedule_node_free(node);
+ isl_ast_build_free(build);
+
+ return NULL;
+}
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the (single) child of "node" and
+ * its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * This function is never called on a leaf, set or sequence node,
+ * so the node always has exactly one child.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_child(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *executed)
+{
+ node = isl_schedule_node_child(node, 0);
+ return build_ast_from_schedule_node(build, node, executed);
+}
+
+/* Generate an AST that visits the elements in the domain of the domain
+ * node "node" in the relative order specified by its descendants.
+ *
+ * An initial inverse schedule is created that maps a zero-dimensional
+ * schedule space to the node domain.
+ * The input "build" is assumed to have a parametric domain and
+ * is replaced by the same zero-dimensional schedule space.
+ *
+ * We also add some of the parameter constraints in the build domain
+ * to the executed relation. Adding these constraints
+ * allows for an earlier detection of conflicts in some cases.
+ * However, we do not want to divide the executed relation into
+ * more disjuncts than necessary. We therefore approximate
+ * the constraints on the parameters by a single disjunct set.
+ */
+static __isl_give isl_ast_node *build_ast_from_domain(
+ __isl_take isl_ast_build *build, __isl_take isl_schedule_node *node)
+{
+ isl_ctx *ctx;
+ isl_union_set *domain, *schedule_domain;
+ isl_union_map *executed;
+ isl_space *space;
+ isl_set *set;
+ isl_ast_graft_list *list;
+ isl_ast_node *ast;
+ int is_params;
+
+ if (!build)
+ goto error;
+
+ ctx = isl_ast_build_get_ctx(build);
+ space = isl_ast_build_get_space(build, 1);
+ is_params = isl_space_is_params(space);
+ isl_space_free(space);
+ if (is_params < 0)
+ goto error;
+ if (!is_params)
+ isl_die(ctx, isl_error_unsupported,
+ "expecting parametric initial context", goto error);
+
+ domain = isl_schedule_node_domain_get_domain(node);
+ domain = isl_union_set_coalesce(domain);
+
+ space = isl_union_set_get_space(domain);
+ space = isl_space_set_from_params(space);
+ build = isl_ast_build_product(build, space);
+
+ set = isl_ast_build_get_domain(build);
+ set = isl_set_from_basic_set(isl_set_simple_hull(set));
+ schedule_domain = isl_union_set_from_set(set);
+
+ executed = isl_union_map_from_domain_and_range(schedule_domain, domain);
+ list = build_ast_from_child(isl_ast_build_copy(build), node, executed);
+ ast = isl_ast_node_from_graft_list(list, build);
+ isl_ast_build_free(build);
+
+ return ast;
+error:
+ isl_schedule_node_free(node);
+ isl_ast_build_free(build);
+ return NULL;
+}
+
+/* Generate an AST that visits the elements in the domain of "schedule"
+ * in the relative order specified by the schedule tree.
+ *
+ * "build" is an isl_ast_build that has been created using
+ * isl_ast_build_alloc or isl_ast_build_from_context based
+ * on a parametric set.
+ *
+ * The construction starts at the root node of the schedule,
+ * which is assumed to be a domain node.
+ */
+__isl_give isl_ast_node *isl_ast_build_node_from_schedule(
+ __isl_keep isl_ast_build *build, __isl_take isl_schedule *schedule)
+{
+ isl_ctx *ctx;
+ isl_schedule_node *node;
+
+ if (!build || !schedule)
+ goto error;
+
+ ctx = isl_ast_build_get_ctx(build);
+
+ node = isl_schedule_get_root(schedule);
+ if (!node)
+ goto error;
+ isl_schedule_free(schedule);
+
+ build = isl_ast_build_copy(build);
+ build = isl_ast_build_set_single_valued(build, 0);
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_domain)
+ isl_die(ctx, isl_error_unsupported,
+ "expecting root domain node",
+ build = isl_ast_build_free(build));
+ return build_ast_from_domain(build, node);
+error:
+ isl_schedule_free(schedule);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_graft.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_graft.c
new file mode 100644
index 00000000000..03e8449d90c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_graft.c
@@ -0,0 +1,1466 @@
+/*
+ * Copyright 2012 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ * and Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl/id.h>
+#include <isl/space.h>
+#include <isl_ast_private.h>
+#include <isl_ast_build_expr.h>
+#include <isl_ast_build_private.h>
+#include <isl_ast_graft_private.h>
+#include "isl_set_to_ast_graft_list.h"
+
+static __isl_give isl_ast_graft *isl_ast_graft_copy(
+ __isl_keep isl_ast_graft *graft);
+
+#undef EL_BASE
+#define EL_BASE ast_graft
+
+#include <isl_list_templ.c>
+
+#undef BASE
+#define BASE ast_graft
+#include <print_templ.c>
+
+isl_ctx *isl_ast_graft_get_ctx(__isl_keep isl_ast_graft *graft)
+{
+ if (!graft)
+ return NULL;
+ return isl_basic_set_get_ctx(graft->enforced);
+}
+
+__isl_give isl_ast_node *isl_ast_graft_get_node(
+ __isl_keep isl_ast_graft *graft)
+{
+ return graft ? isl_ast_node_copy(graft->node) : NULL;
+}
+
+/* Create a graft for "node" with no guards and no enforced conditions.
+ */
+__isl_give isl_ast_graft *isl_ast_graft_alloc(
+ __isl_take isl_ast_node *node, __isl_keep isl_ast_build *build)
+{
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_ast_graft *graft;
+
+ if (!node)
+ return NULL;
+
+ ctx = isl_ast_node_get_ctx(node);
+ graft = isl_calloc_type(ctx, isl_ast_graft);
+ if (!graft)
+ goto error;
+
+ space = isl_ast_build_get_space(build, 1);
+
+ graft->ref = 1;
+ graft->node = node;
+ graft->guard = isl_set_universe(isl_space_copy(space));
+ graft->enforced = isl_basic_set_universe(space);
+
+ if (!graft->guard || !graft->enforced)
+ return isl_ast_graft_free(graft);
+
+ return graft;
+error:
+ isl_ast_node_free(node);
+ return NULL;
+}
+
+/* Create a graft with no guards and no enforced conditions
+ * encapsulating a call to the domain element specified by "executed".
+ * "executed" is assumed to be single-valued.
+ */
+__isl_give isl_ast_graft *isl_ast_graft_alloc_domain(
+ __isl_take isl_map *executed, __isl_keep isl_ast_build *build)
+{
+ isl_ast_node *node;
+
+ node = isl_ast_build_call_from_executed(build, executed);
+
+ return isl_ast_graft_alloc(node, build);
+}
+
+static __isl_give isl_ast_graft *isl_ast_graft_copy(
+ __isl_keep isl_ast_graft *graft)
+{
+ if (!graft)
+ return NULL;
+
+ graft->ref++;
+ return graft;
+}
+
+/* Do all the grafts in "list" have the same guard and is this guard
+ * independent of the current depth?
+ */
+static isl_bool equal_independent_guards(__isl_keep isl_ast_graft_list *list,
+ __isl_keep isl_ast_build *build)
+{
+ int i;
+ isl_size n;
+ isl_size depth;
+ isl_size dim;
+ isl_ast_graft *graft_0;
+ isl_bool equal = isl_bool_true;
+ isl_bool skip;
+
+ n = isl_ast_graft_list_n_ast_graft(list);
+ depth = isl_ast_build_get_depth(build);
+ if (n < 0 || depth < 0)
+ return isl_bool_error;
+ graft_0 = isl_ast_graft_list_get_ast_graft(list, 0);
+ if (!graft_0)
+ return isl_bool_error;
+
+ dim = isl_set_dim(graft_0->guard, isl_dim_set);
+ if (dim < 0)
+ skip = isl_bool_error;
+ else if (dim <= depth)
+ skip = isl_bool_false;
+ else
+ skip = isl_set_involves_dims(graft_0->guard,
+ isl_dim_set, depth, 1);
+ if (skip < 0 || skip) {
+ isl_ast_graft_free(graft_0);
+ return isl_bool_not(skip);
+ }
+
+ for (i = 1; i < n; ++i) {
+ isl_ast_graft *graft;
+ graft = isl_ast_graft_list_get_ast_graft(list, i);
+ if (!graft)
+ equal = isl_bool_error;
+ else
+ equal = isl_set_is_equal(graft_0->guard, graft->guard);
+ isl_ast_graft_free(graft);
+ if (equal < 0 || !equal)
+ break;
+ }
+
+ isl_ast_graft_free(graft_0);
+
+ return equal;
+}
+
+/* Hoist "guard" out of the current level (given by "build").
+ *
+ * In particular, eliminate the dimension corresponding to the current depth.
+ */
+static __isl_give isl_set *hoist_guard(__isl_take isl_set *guard,
+ __isl_keep isl_ast_build *build)
+{
+ isl_size depth;
+ isl_size dim;
+
+ depth = isl_ast_build_get_depth(build);
+ dim = isl_set_dim(guard, isl_dim_set);
+ if (depth < 0 || dim < 0)
+ return isl_set_free(guard);
+ if (depth < dim) {
+ guard = isl_set_remove_divs_involving_dims(guard,
+ isl_dim_set, depth, 1);
+ guard = isl_set_eliminate(guard, isl_dim_set, depth, 1);
+ guard = isl_set_compute_divs(guard);
+ }
+
+ return guard;
+}
+
+/* Extract a common guard from the grafts in "list" that can be hoisted
+ * out of the current level. If no such guard can be found, then return
+ * a universal set.
+ *
+ * If all the grafts in the list have the same guard and if this guard
+ * is independent of the current level, then it can be hoisted out.
+ * If there is only one graft in the list and if its guard
+ * depends on the current level, then we eliminate this level and
+ * return the result.
+ *
+ * Otherwise, we return the unshifted simple hull of the guards.
+ * In order to be able to hoist as many constraints as possible,
+ * but at the same time avoid hoisting constraints that did not
+ * appear in the guards in the first place, we intersect the guards
+ * with all the information that is available (i.e., the domain
+ * from the build and the enforced constraints of the graft) and
+ * compute the unshifted hull of the result using only constraints
+ * from the original guards.
+ * In particular, intersecting the guards with other known information
+ * allows us to hoist guards that are only explicit is some of
+ * the grafts and implicit in the others.
+ *
+ * The special case for equal guards is needed in case those guards
+ * are non-convex. Taking the simple hull would remove information
+ * and would not allow for these guards to be hoisted completely.
+ */
+__isl_give isl_set *isl_ast_graft_list_extract_hoistable_guard(
+ __isl_keep isl_ast_graft_list *list, __isl_keep isl_ast_build *build)
+{
+ int i;
+ isl_size n;
+ isl_bool equal;
+ isl_ctx *ctx;
+ isl_set *guard;
+ isl_set_list *set_list;
+ isl_basic_set *hull;
+
+ if (!list || !build)
+ return NULL;
+
+ n = isl_ast_graft_list_n_ast_graft(list);
+ if (n < 0)
+ return NULL;
+ if (n == 0)
+ return isl_set_universe(isl_ast_build_get_space(build, 1));
+
+ equal = equal_independent_guards(list, build);
+ if (equal < 0)
+ return NULL;
+
+ if (equal || n == 1) {
+ isl_ast_graft *graft_0;
+
+ graft_0 = isl_ast_graft_list_get_ast_graft(list, 0);
+ if (!graft_0)
+ return NULL;
+ guard = isl_set_copy(graft_0->guard);
+ if (!equal)
+ guard = hoist_guard(guard, build);
+ isl_ast_graft_free(graft_0);
+ return guard;
+ }
+
+ ctx = isl_ast_build_get_ctx(build);
+ set_list = isl_set_list_alloc(ctx, n);
+ guard = isl_set_empty(isl_ast_build_get_space(build, 1));
+ for (i = 0; i < n; ++i) {
+ isl_ast_graft *graft;
+ isl_basic_set *enforced;
+ isl_set *guard_i;
+
+ graft = isl_ast_graft_list_get_ast_graft(list, i);
+ enforced = isl_ast_graft_get_enforced(graft);
+ guard_i = isl_set_copy(graft->guard);
+ isl_ast_graft_free(graft);
+ set_list = isl_set_list_add(set_list, isl_set_copy(guard_i));
+ guard_i = isl_set_intersect(guard_i,
+ isl_set_from_basic_set(enforced));
+ guard_i = isl_set_intersect(guard_i,
+ isl_ast_build_get_domain(build));
+ guard = isl_set_union(guard, guard_i);
+ }
+ hull = isl_set_unshifted_simple_hull_from_set_list(guard, set_list);
+ guard = isl_set_from_basic_set(hull);
+ return hoist_guard(guard, build);
+}
+
+/* Internal data structure used inside insert_if.
+ *
+ * list is the list of guarded nodes created by each call to insert_if.
+ * node is the original node that is guarded by insert_if.
+ * build is the build in which the AST is constructed.
+ */
+struct isl_insert_if_data {
+ isl_ast_node_list *list;
+ isl_ast_node *node;
+ isl_ast_build *build;
+};
+
+static isl_stat insert_if(__isl_take isl_basic_set *bset, void *user);
+
+/* Insert an if node around "node" testing the condition encoded
+ * in guard "guard".
+ *
+ * If the user does not want any disjunctions in the if conditions
+ * and if "guard" does involve a disjunction, then we make the different
+ * disjuncts disjoint and insert an if node corresponding to each disjunct
+ * around a copy of "node". The result is then a block node containing
+ * this sequence of guarded copies of "node".
+ */
+static __isl_give isl_ast_node *ast_node_insert_if(
+ __isl_take isl_ast_node *node, __isl_take isl_set *guard,
+ __isl_keep isl_ast_build *build)
+{
+ struct isl_insert_if_data data;
+ isl_ctx *ctx;
+ isl_size n;
+
+ n = isl_set_n_basic_set(guard);
+ if (n < 0)
+ goto error;
+ ctx = isl_ast_build_get_ctx(build);
+ if (isl_options_get_ast_build_allow_or(ctx) || n <= 1) {
+ isl_ast_node *if_node;
+ isl_ast_expr *expr;
+
+ expr = isl_ast_build_expr_from_set_internal(build, guard);
+
+ if_node = isl_ast_node_alloc_if(expr);
+ return isl_ast_node_if_set_then(if_node, node);
+ }
+
+ guard = isl_set_make_disjoint(guard);
+
+ data.list = isl_ast_node_list_alloc(ctx, 0);
+ data.node = node;
+ data.build = build;
+ if (isl_set_foreach_basic_set(guard, &insert_if, &data) < 0)
+ data.list = isl_ast_node_list_free(data.list);
+
+ isl_set_free(guard);
+ isl_ast_node_free(data.node);
+ return isl_ast_node_alloc_block(data.list);
+error:
+ isl_set_free(guard);
+ isl_ast_node_free(node);
+ return NULL;
+}
+
+/* Insert an if node around a copy of "data->node" testing the condition
+ * encoded in guard "bset" and add the result to data->list.
+ */
+static isl_stat insert_if(__isl_take isl_basic_set *bset, void *user)
+{
+ struct isl_insert_if_data *data = user;
+ isl_ast_node *node;
+ isl_set *set;
+
+ set = isl_set_from_basic_set(bset);
+ node = isl_ast_node_copy(data->node);
+ node = ast_node_insert_if(node, set, data->build);
+ data->list = isl_ast_node_list_add(data->list, node);
+
+ return isl_stat_ok;
+}
+
+/* Insert an if node around graft->node testing the condition encoded
+ * in guard "guard", assuming guard involves any conditions.
+ */
+static __isl_give isl_ast_graft *insert_if_node(
+ __isl_take isl_ast_graft *graft, __isl_take isl_set *guard,
+ __isl_keep isl_ast_build *build)
+{
+ int univ;
+
+ if (!graft)
+ goto error;
+
+ univ = isl_set_plain_is_universe(guard);
+ if (univ < 0)
+ goto error;
+ if (univ) {
+ isl_set_free(guard);
+ return graft;
+ }
+
+ build = isl_ast_build_copy(build);
+ graft->node = ast_node_insert_if(graft->node, guard, build);
+ isl_ast_build_free(build);
+
+ if (!graft->node)
+ return isl_ast_graft_free(graft);
+
+ return graft;
+error:
+ isl_set_free(guard);
+ return isl_ast_graft_free(graft);
+}
+
+/* Insert an if node around graft->node testing the condition encoded
+ * in graft->guard, assuming graft->guard involves any conditions.
+ */
+static __isl_give isl_ast_graft *insert_pending_guard_node(
+ __isl_take isl_ast_graft *graft, __isl_keep isl_ast_build *build)
+{
+ if (!graft)
+ return NULL;
+
+ return insert_if_node(graft, isl_set_copy(graft->guard), build);
+}
+
+/* Replace graft->enforced by "enforced".
+ */
+__isl_give isl_ast_graft *isl_ast_graft_set_enforced(
+ __isl_take isl_ast_graft *graft, __isl_take isl_basic_set *enforced)
+{
+ if (!graft || !enforced)
+ goto error;
+
+ isl_basic_set_free(graft->enforced);
+ graft->enforced = enforced;
+
+ return graft;
+error:
+ isl_basic_set_free(enforced);
+ return isl_ast_graft_free(graft);
+}
+
+/* Update "enforced" such that it only involves constraints that are
+ * also enforced by "graft".
+ */
+static __isl_give isl_basic_set *update_enforced(
+ __isl_take isl_basic_set *enforced, __isl_keep isl_ast_graft *graft,
+ int depth)
+{
+ isl_size dim;
+ isl_basic_set *enforced_g;
+
+ enforced_g = isl_ast_graft_get_enforced(graft);
+ dim = isl_basic_set_dim(enforced_g, isl_dim_set);
+ if (dim < 0)
+ enforced_g = isl_basic_set_free(enforced_g);
+ if (depth < dim)
+ enforced_g = isl_basic_set_eliminate(enforced_g,
+ isl_dim_set, depth, 1);
+ enforced_g = isl_basic_set_remove_unknown_divs(enforced_g);
+ enforced_g = isl_basic_set_align_params(enforced_g,
+ isl_basic_set_get_space(enforced));
+ enforced = isl_basic_set_align_params(enforced,
+ isl_basic_set_get_space(enforced_g));
+ enforced = isl_set_simple_hull(isl_basic_set_union(enforced,
+ enforced_g));
+
+ return enforced;
+}
+
+/* Extend the node at *body with node.
+ *
+ * If body points to the else branch, then *body may still be NULL.
+ * If so, we simply attach node to this else branch.
+ * Otherwise, we attach a list containing the statements already
+ * attached at *body followed by node.
+ */
+static void extend_body(__isl_keep isl_ast_node **body,
+ __isl_take isl_ast_node *node)
+{
+ isl_ast_node_list *list;
+
+ if (!*body) {
+ *body = node;
+ return;
+ }
+
+ if ((*body)->type == isl_ast_node_block) {
+ list = isl_ast_node_block_get_children(*body);
+ isl_ast_node_free(*body);
+ } else
+ list = isl_ast_node_list_from_ast_node(*body);
+ list = isl_ast_node_list_add(list, node);
+ *body = isl_ast_node_alloc_block(list);
+}
+
+/* Merge "graft" into the last graft of "list".
+ * body points to the then or else branch of an if node in that last graft.
+ *
+ * We attach graft->node to this branch and update the enforced
+ * set of the last graft of "list" to take into account the enforced
+ * set of "graft".
+ */
+static __isl_give isl_ast_graft_list *graft_extend_body(
+ __isl_take isl_ast_graft_list *list,
+ __isl_keep isl_ast_node **body, __isl_take isl_ast_graft *graft,
+ __isl_keep isl_ast_build *build)
+{
+ isl_size n;
+ isl_size depth;
+ isl_ast_graft *last;
+ isl_space *space;
+ isl_basic_set *enforced;
+
+ n = isl_ast_graft_list_n_ast_graft(list);
+ depth = isl_ast_build_get_depth(build);
+ if (n < 0 || depth < 0 || !graft)
+ goto error;
+ extend_body(body, isl_ast_node_copy(graft->node));
+ if (!*body)
+ goto error;
+
+ last = isl_ast_graft_list_get_ast_graft(list, n - 1);
+
+ space = isl_ast_build_get_space(build, 1);
+ enforced = isl_basic_set_empty(space);
+ enforced = update_enforced(enforced, last, depth);
+ enforced = update_enforced(enforced, graft, depth);
+ last = isl_ast_graft_set_enforced(last, enforced);
+
+ list = isl_ast_graft_list_set_ast_graft(list, n - 1, last);
+ isl_ast_graft_free(graft);
+ return list;
+error:
+ isl_ast_graft_free(graft);
+ return isl_ast_graft_list_free(list);
+}
+
+/* Merge "graft" into the last graft of "list", attaching graft->node
+ * to the then branch of "last_if".
+ */
+static __isl_give isl_ast_graft_list *extend_then(
+ __isl_take isl_ast_graft_list *list,
+ __isl_keep isl_ast_node *last_if, __isl_take isl_ast_graft *graft,
+ __isl_keep isl_ast_build *build)
+{
+ return graft_extend_body(list, &last_if->u.i.then, graft, build);
+}
+
+/* Merge "graft" into the last graft of "list", attaching graft->node
+ * to the else branch of "last_if".
+ */
+static __isl_give isl_ast_graft_list *extend_else(
+ __isl_take isl_ast_graft_list *list,
+ __isl_keep isl_ast_node *last_if, __isl_take isl_ast_graft *graft,
+ __isl_keep isl_ast_build *build)
+{
+ return graft_extend_body(list, &last_if->u.i.else_node, graft, build);
+}
+
+/* This data structure keeps track of an if node.
+ *
+ * "node" is the actual if-node
+ * "guard" is the original, non-simplified guard of the node
+ * "complement" is the complement of "guard" in the context of outer if nodes
+ */
+struct isl_if_node {
+ isl_ast_node *node;
+ isl_set *guard;
+ isl_set *complement;
+};
+
+/* Given a list of "n" if nodes, clear those starting at "first"
+ * and return "first" (i.e., the updated size of the array).
+ */
+static int clear_if_nodes(struct isl_if_node *if_node, int first, int n)
+{
+ int i;
+
+ for (i = first; i < n; ++i) {
+ isl_set_free(if_node[i].guard);
+ isl_set_free(if_node[i].complement);
+ }
+
+ return first;
+}
+
+/* For each graft in "list",
+ * insert an if node around graft->node testing the condition encoded
+ * in graft->guard, assuming graft->guard involves any conditions.
+ *
+ * We keep track of a list of generated if nodes that can be extended
+ * without changing the order of the elements in "list".
+ * If the guard of a graft is a subset of either the guard or its complement
+ * of one of those if nodes, then the node
+ * of the new graft is inserted into the then or else branch of the last graft
+ * and the current graft is discarded.
+ * The guard of the node is then simplified based on the conditions
+ * enforced at that then or else branch.
+ * Otherwise, the current graft is appended to the list.
+ *
+ * We only construct else branches if allowed by the user.
+ */
+static __isl_give isl_ast_graft_list *insert_pending_guard_nodes(
+ __isl_take isl_ast_graft_list *list,
+ __isl_keep isl_ast_build *build)
+{
+ int i, j, n_if;
+ isl_size n;
+ int allow_else;
+ isl_ctx *ctx;
+ isl_ast_graft_list *res;
+ struct isl_if_node *if_node = NULL;
+
+ n = isl_ast_graft_list_n_ast_graft(list);
+ if (!build || n < 0)
+ return isl_ast_graft_list_free(list);
+
+ ctx = isl_ast_build_get_ctx(build);
+
+ allow_else = isl_options_get_ast_build_allow_else(ctx);
+
+ n_if = 0;
+ if (n > 1) {
+ if_node = isl_alloc_array(ctx, struct isl_if_node, n - 1);
+ if (!if_node)
+ return isl_ast_graft_list_free(list);
+ }
+
+ res = isl_ast_graft_list_alloc(ctx, n);
+
+ for (i = 0; i < n; ++i) {
+ isl_set *guard;
+ isl_ast_graft *graft;
+ int subset, found_then, found_else;
+ isl_ast_node *node;
+
+ graft = isl_ast_graft_list_get_ast_graft(list, i);
+ if (!graft)
+ break;
+ subset = 0;
+ found_then = found_else = -1;
+ if (n_if > 0) {
+ isl_set *test;
+ test = isl_set_copy(graft->guard);
+ test = isl_set_intersect(test,
+ isl_set_copy(build->domain));
+ for (j = n_if - 1; j >= 0; --j) {
+ subset = isl_set_is_subset(test,
+ if_node[j].guard);
+ if (subset < 0 || subset) {
+ found_then = j;
+ break;
+ }
+ if (!allow_else)
+ continue;
+ subset = isl_set_is_subset(test,
+ if_node[j].complement);
+ if (subset < 0 || subset) {
+ found_else = j;
+ break;
+ }
+ }
+ n_if = clear_if_nodes(if_node, j + 1, n_if);
+ isl_set_free(test);
+ }
+ if (subset < 0) {
+ graft = isl_ast_graft_free(graft);
+ break;
+ }
+
+ guard = isl_set_copy(graft->guard);
+ if (found_then >= 0)
+ graft->guard = isl_set_gist(graft->guard,
+ isl_set_copy(if_node[found_then].guard));
+ else if (found_else >= 0)
+ graft->guard = isl_set_gist(graft->guard,
+ isl_set_copy(if_node[found_else].complement));
+
+ node = graft->node;
+ if (!graft->guard)
+ graft = isl_ast_graft_free(graft);
+ graft = insert_pending_guard_node(graft, build);
+ if (graft && graft->node != node && i != n - 1) {
+ isl_set *set;
+ if_node[n_if].node = graft->node;
+ if_node[n_if].guard = guard;
+ if (found_then >= 0)
+ set = if_node[found_then].guard;
+ else if (found_else >= 0)
+ set = if_node[found_else].complement;
+ else
+ set = build->domain;
+ set = isl_set_copy(set);
+ set = isl_set_subtract(set, isl_set_copy(guard));
+ if_node[n_if].complement = set;
+ n_if++;
+ } else
+ isl_set_free(guard);
+ if (!graft)
+ break;
+
+ if (found_then >= 0)
+ res = extend_then(res, if_node[found_then].node,
+ graft, build);
+ else if (found_else >= 0)
+ res = extend_else(res, if_node[found_else].node,
+ graft, build);
+ else
+ res = isl_ast_graft_list_add(res, graft);
+ }
+ if (i < n)
+ res = isl_ast_graft_list_free(res);
+
+ isl_ast_graft_list_free(list);
+ clear_if_nodes(if_node, 0, n_if);
+ free(if_node);
+ return res;
+}
+
+/* For each graft in "list",
+ * insert an if node around graft->node testing the condition encoded
+ * in graft->guard, assuming graft->guard involves any conditions.
+ * Subsequently remove the guards from the grafts.
+ */
+__isl_give isl_ast_graft_list *isl_ast_graft_list_insert_pending_guard_nodes(
+ __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build)
+{
+ int i;
+ isl_size n;
+ isl_set *universe;
+
+ list = insert_pending_guard_nodes(list, build);
+ n = isl_ast_graft_list_n_ast_graft(list);
+ if (n < 0)
+ return isl_ast_graft_list_free(list);
+
+ universe = isl_set_universe(isl_ast_build_get_space(build, 1));
+ for (i = 0; i < n; ++i) {
+ isl_ast_graft *graft;
+
+ graft = isl_ast_graft_list_get_ast_graft(list, i);
+ if (!graft)
+ break;
+ isl_set_free(graft->guard);
+ graft->guard = isl_set_copy(universe);
+ if (!graft->guard)
+ graft = isl_ast_graft_free(graft);
+ list = isl_ast_graft_list_set_ast_graft(list, i, graft);
+ }
+ isl_set_free(universe);
+ if (i < n)
+ return isl_ast_graft_list_free(list);
+
+ return list;
+}
+
+/* Collect the nodes contained in the grafts in "list" in a node list.
+ */
+static __isl_give isl_ast_node_list *extract_node_list(
+ __isl_keep isl_ast_graft_list *list)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_ast_node_list *node_list;
+
+ n = isl_ast_graft_list_n_ast_graft(list);
+ if (n < 0)
+ return NULL;
+ ctx = isl_ast_graft_list_get_ctx(list);
+ node_list = isl_ast_node_list_alloc(ctx, n);
+ for (i = 0; i < n; ++i) {
+ isl_ast_node *node;
+ isl_ast_graft *graft;
+
+ graft = isl_ast_graft_list_get_ast_graft(list, i);
+ node = isl_ast_graft_get_node(graft);
+ node_list = isl_ast_node_list_add(node_list, node);
+ isl_ast_graft_free(graft);
+ }
+
+ return node_list;
+}
+
+/* Look for shared enforced constraints by all the elements in "list"
+ * on outer loops (with respect to the current depth) and return the result.
+ *
+ * If there are no elements in "list", then return the empty set.
+ */
+__isl_give isl_basic_set *isl_ast_graft_list_extract_shared_enforced(
+ __isl_keep isl_ast_graft_list *list,
+ __isl_keep isl_ast_build *build)
+{
+ int i;
+ isl_size n;
+ isl_size depth;
+ isl_space *space;
+ isl_basic_set *enforced;
+
+ n = isl_ast_graft_list_n_ast_graft(list);
+ depth = isl_ast_build_get_depth(build);
+ if (n < 0 || depth < 0)
+ return NULL;
+
+ space = isl_ast_build_get_space(build, 1);
+ enforced = isl_basic_set_empty(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_ast_graft *graft;
+
+ graft = isl_ast_graft_list_get_ast_graft(list, i);
+ enforced = update_enforced(enforced, graft, depth);
+ isl_ast_graft_free(graft);
+ }
+
+ return enforced;
+}
+
+/* Record "guard" in "graft" so that it will be enforced somewhere
+ * up the tree. If the graft already has a guard, then it may be partially
+ * redundant in combination with the new guard and in the context
+ * the generated constraints of "build". In fact, the new guard
+ * may in itself have some redundant constraints.
+ * We therefore (re)compute the gist of the intersection
+ * and coalesce the result.
+ */
+static __isl_give isl_ast_graft *store_guard(__isl_take isl_ast_graft *graft,
+ __isl_take isl_set *guard, __isl_keep isl_ast_build *build)
+{
+ int is_universe;
+
+ if (!graft)
+ goto error;
+
+ is_universe = isl_set_plain_is_universe(guard);
+ if (is_universe < 0)
+ goto error;
+ if (is_universe) {
+ isl_set_free(guard);
+ return graft;
+ }
+
+ graft->guard = isl_set_intersect(graft->guard, guard);
+ graft->guard = isl_set_gist(graft->guard,
+ isl_ast_build_get_generated(build));
+ graft->guard = isl_set_coalesce(graft->guard);
+ if (!graft->guard)
+ return isl_ast_graft_free(graft);
+
+ return graft;
+error:
+ isl_set_free(guard);
+ return isl_ast_graft_free(graft);
+}
+
+/* For each graft in "list", replace its guard with the gist with
+ * respect to "context".
+ */
+static __isl_give isl_ast_graft_list *gist_guards(
+ __isl_take isl_ast_graft_list *list, __isl_keep isl_set *context)
+{
+ int i;
+ isl_size n;
+
+ n = isl_ast_graft_list_n_ast_graft(list);
+ if (!list)
+ return isl_ast_graft_list_free(list);
+
+ for (i = 0; i < n; ++i) {
+ isl_ast_graft *graft;
+
+ graft = isl_ast_graft_list_get_ast_graft(list, i);
+ if (!graft)
+ break;
+ graft->guard = isl_set_gist(graft->guard,
+ isl_set_copy(context));
+ if (!graft->guard)
+ graft = isl_ast_graft_free(graft);
+ list = isl_ast_graft_list_set_ast_graft(list, i, graft);
+ }
+ if (i < n)
+ return isl_ast_graft_list_free(list);
+
+ return list;
+}
+
+/* For each graft in "list", replace its guard with the gist with
+ * respect to "context".
+ */
+__isl_give isl_ast_graft_list *isl_ast_graft_list_gist_guards(
+ __isl_take isl_ast_graft_list *list, __isl_take isl_set *context)
+{
+ list = gist_guards(list, context);
+ isl_set_free(context);
+
+ return list;
+}
+
+/* Allocate a graft in "build" based on the list of grafts in "sub_build".
+ * "guard" and "enforced" are the guard and enforced constraints
+ * of the allocated graft. The guard is used to simplify the guards
+ * of the elements in "list".
+ *
+ * The node is initialized to either a block containing the nodes of "children"
+ * or, if there is only a single child, the node of that child.
+ * If the current level requires a for node, it should be inserted by
+ * a subsequent call to isl_ast_graft_insert_for.
+ */
+__isl_give isl_ast_graft *isl_ast_graft_alloc_from_children(
+ __isl_take isl_ast_graft_list *list, __isl_take isl_set *guard,
+ __isl_take isl_basic_set *enforced, __isl_keep isl_ast_build *build,
+ __isl_keep isl_ast_build *sub_build)
+{
+ isl_ast_build *guard_build;
+ isl_ast_node *node;
+ isl_ast_node_list *node_list;
+ isl_ast_graft *graft;
+
+ guard_build = isl_ast_build_copy(sub_build);
+ guard_build = isl_ast_build_replace_pending_by_guard(guard_build,
+ isl_set_copy(guard));
+ list = gist_guards(list, guard);
+ list = insert_pending_guard_nodes(list, guard_build);
+ isl_ast_build_free(guard_build);
+
+ node_list = extract_node_list(list);
+ node = isl_ast_node_from_ast_node_list(node_list);
+ isl_ast_graft_list_free(list);
+
+ graft = isl_ast_graft_alloc(node, build);
+ graft = store_guard(graft, guard, build);
+ graft = isl_ast_graft_enforce(graft, enforced);
+
+ return graft;
+}
+
+/* Combine the grafts in the list into a single graft.
+ *
+ * The guard is initialized to the shared guard of the list elements (if any),
+ * provided it does not depend on the current dimension.
+ * The guards in the elements are then simplified with respect to the
+ * hoisted guard and materialized as if nodes around the contained AST nodes
+ * in the context of "sub_build".
+ *
+ * The enforced set is initialized to the simple hull of the enforced sets
+ * of the elements, provided the ast_build_exploit_nested_bounds option is set
+ * or the new graft will be used at the same level.
+ *
+ * The node is initialized to either a block containing the nodes of "list"
+ * or, if there is only a single element, the node of that element.
+ */
+static __isl_give isl_ast_graft *ast_graft_list_fuse(
+ __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build)
+{
+ isl_ast_graft *graft;
+ isl_basic_set *enforced;
+ isl_set *guard;
+
+ if (!list)
+ return NULL;
+
+ enforced = isl_ast_graft_list_extract_shared_enforced(list, build);
+ guard = isl_ast_graft_list_extract_hoistable_guard(list, build);
+ graft = isl_ast_graft_alloc_from_children(list, guard, enforced,
+ build, build);
+
+ return graft;
+}
+
+/* Combine the grafts in the list into a single graft.
+ * Return a list containing this single graft.
+ * If the original list is empty, then return an empty list.
+ */
+__isl_give isl_ast_graft_list *isl_ast_graft_list_fuse(
+ __isl_take isl_ast_graft_list *list,
+ __isl_keep isl_ast_build *build)
+{
+ isl_size n;
+ isl_ast_graft *graft;
+
+ n = isl_ast_graft_list_n_ast_graft(list);
+ if (n < 0)
+ return isl_ast_graft_list_free(list);
+ if (n <= 1)
+ return list;
+ graft = ast_graft_list_fuse(list, build);
+ return isl_ast_graft_list_from_ast_graft(graft);
+}
+
+/* Combine the two grafts into a single graft.
+ * Return a list containing this single graft.
+ */
+static __isl_give isl_ast_graft *isl_ast_graft_fuse(
+ __isl_take isl_ast_graft *graft1, __isl_take isl_ast_graft *graft2,
+ __isl_keep isl_ast_build *build)
+{
+ isl_ctx *ctx;
+ isl_ast_graft_list *list;
+
+ ctx = isl_ast_build_get_ctx(build);
+
+ list = isl_ast_graft_list_alloc(ctx, 2);
+ list = isl_ast_graft_list_add(list, graft1);
+ list = isl_ast_graft_list_add(list, graft2);
+
+ return ast_graft_list_fuse(list, build);
+}
+
+/* Insert a for node enclosing the current graft->node.
+ */
+__isl_give isl_ast_graft *isl_ast_graft_insert_for(
+ __isl_take isl_ast_graft *graft, __isl_take isl_ast_node *node)
+{
+ if (!graft)
+ goto error;
+
+ graft->node = isl_ast_node_for_set_body(node, graft->node);
+ if (!graft->node)
+ return isl_ast_graft_free(graft);
+
+ return graft;
+error:
+ isl_ast_node_free(node);
+ isl_ast_graft_free(graft);
+ return NULL;
+}
+
+/* Insert a mark governing the current graft->node.
+ */
+__isl_give isl_ast_graft *isl_ast_graft_insert_mark(
+ __isl_take isl_ast_graft *graft, __isl_take isl_id *mark)
+{
+ if (!graft)
+ goto error;
+
+ graft->node = isl_ast_node_alloc_mark(mark, graft->node);
+ if (!graft->node)
+ return isl_ast_graft_free(graft);
+
+ return graft;
+error:
+ isl_id_free(mark);
+ isl_ast_graft_free(graft);
+ return NULL;
+}
+
+/* Represent the graft list as an AST node.
+ * This operation drops the information about guards in the grafts, so
+ * if there are any pending guards, then they are materialized as if nodes.
+ */
+__isl_give isl_ast_node *isl_ast_node_from_graft_list(
+ __isl_take isl_ast_graft_list *list,
+ __isl_keep isl_ast_build *build)
+{
+ isl_ast_node_list *node_list;
+
+ list = insert_pending_guard_nodes(list, build);
+ node_list = extract_node_list(list);
+ isl_ast_graft_list_free(list);
+
+ return isl_ast_node_from_ast_node_list(node_list);
+}
+
+__isl_null isl_ast_graft *isl_ast_graft_free(__isl_take isl_ast_graft *graft)
+{
+ if (!graft)
+ return NULL;
+
+ if (--graft->ref > 0)
+ return NULL;
+
+ isl_ast_node_free(graft->node);
+ isl_set_free(graft->guard);
+ isl_basic_set_free(graft->enforced);
+ free(graft);
+
+ return NULL;
+}
+
+/* Record that the grafted tree enforces
+ * "enforced" by intersecting graft->enforced with "enforced".
+ */
+__isl_give isl_ast_graft *isl_ast_graft_enforce(
+ __isl_take isl_ast_graft *graft, __isl_take isl_basic_set *enforced)
+{
+ if (!graft || !enforced)
+ goto error;
+
+ enforced = isl_basic_set_align_params(enforced,
+ isl_basic_set_get_space(graft->enforced));
+ graft->enforced = isl_basic_set_align_params(graft->enforced,
+ isl_basic_set_get_space(enforced));
+ graft->enforced = isl_basic_set_intersect(graft->enforced, enforced);
+ if (!graft->enforced)
+ return isl_ast_graft_free(graft);
+
+ return graft;
+error:
+ isl_basic_set_free(enforced);
+ return isl_ast_graft_free(graft);
+}
+
+__isl_give isl_basic_set *isl_ast_graft_get_enforced(
+ __isl_keep isl_ast_graft *graft)
+{
+ return graft ? isl_basic_set_copy(graft->enforced) : NULL;
+}
+
+__isl_give isl_set *isl_ast_graft_get_guard(__isl_keep isl_ast_graft *graft)
+{
+ return graft ? isl_set_copy(graft->guard) : NULL;
+}
+
+/* Record that "guard" needs to be inserted in "graft".
+ */
+__isl_give isl_ast_graft *isl_ast_graft_add_guard(
+ __isl_take isl_ast_graft *graft,
+ __isl_take isl_set *guard, __isl_keep isl_ast_build *build)
+{
+ return store_guard(graft, guard, build);
+}
+
+/* Reformulate the "graft", which was generated in the context
+ * of an inner code generation, in terms of the outer code generation
+ * AST build.
+ *
+ * If "product" is set, then the domain of the inner code generation build is
+ *
+ * [O -> S]
+ *
+ * with O the domain of the outer code generation build.
+ * We essentially need to project out S.
+ *
+ * If "product" is not set, then we need to project the domains onto
+ * their parameter spaces.
+ */
+__isl_give isl_ast_graft *isl_ast_graft_unembed(__isl_take isl_ast_graft *graft,
+ int product)
+{
+ isl_basic_set *enforced;
+
+ if (!graft)
+ return NULL;
+
+ if (product) {
+ enforced = graft->enforced;
+ enforced = isl_basic_map_domain(isl_basic_set_unwrap(enforced));
+ graft->enforced = enforced;
+ graft->guard = isl_map_domain(isl_set_unwrap(graft->guard));
+ } else {
+ graft->enforced = isl_basic_set_params(graft->enforced);
+ graft->guard = isl_set_params(graft->guard);
+ }
+ graft->guard = isl_set_compute_divs(graft->guard);
+
+ if (!graft->enforced || !graft->guard)
+ return isl_ast_graft_free(graft);
+
+ return graft;
+}
+
+/* Reformulate the grafts in "list", which were generated in the context
+ * of an inner code generation, in terms of the outer code generation
+ * AST build.
+ */
+__isl_give isl_ast_graft_list *isl_ast_graft_list_unembed(
+ __isl_take isl_ast_graft_list *list, int product)
+{
+ int i;
+ isl_size n;
+
+ n = isl_ast_graft_list_n_ast_graft(list);
+ if (n < 0)
+ return isl_ast_graft_list_free(list);
+ for (i = 0; i < n; ++i) {
+ isl_ast_graft *graft;
+
+ graft = isl_ast_graft_list_get_ast_graft(list, i);
+ graft = isl_ast_graft_unembed(graft, product);
+ list = isl_ast_graft_list_set_ast_graft(list, i, graft);
+ }
+
+ return list;
+}
+
+/* Compute the preimage of "graft" under the function represented by "ma".
+ * In other words, plug in "ma" in "enforced" and "guard" fields of "graft".
+ */
+__isl_give isl_ast_graft *isl_ast_graft_preimage_multi_aff(
+ __isl_take isl_ast_graft *graft, __isl_take isl_multi_aff *ma)
+{
+ isl_basic_set *enforced;
+
+ if (!graft)
+ return NULL;
+
+ enforced = graft->enforced;
+ graft->enforced = isl_basic_set_preimage_multi_aff(enforced,
+ isl_multi_aff_copy(ma));
+ graft->guard = isl_set_preimage_multi_aff(graft->guard, ma);
+
+ if (!graft->enforced || !graft->guard)
+ return isl_ast_graft_free(graft);
+
+ return graft;
+}
+
+/* Compute the preimage of all the grafts in "list" under
+ * the function represented by "ma".
+ */
+__isl_give isl_ast_graft_list *isl_ast_graft_list_preimage_multi_aff(
+ __isl_take isl_ast_graft_list *list, __isl_take isl_multi_aff *ma)
+{
+ int i;
+ isl_size n;
+
+ n = isl_ast_graft_list_n_ast_graft(list);
+ if (n < 0)
+ list = isl_ast_graft_list_free(list);
+ for (i = 0; i < n; ++i) {
+ isl_ast_graft *graft;
+
+ graft = isl_ast_graft_list_get_ast_graft(list, i);
+ graft = isl_ast_graft_preimage_multi_aff(graft,
+ isl_multi_aff_copy(ma));
+ list = isl_ast_graft_list_set_ast_graft(list, i, graft);
+ }
+
+ isl_multi_aff_free(ma);
+ return list;
+}
+
+/* Compare two grafts based on their guards.
+ */
+static int cmp_graft(__isl_keep isl_ast_graft *a, __isl_keep isl_ast_graft *b,
+ void *user)
+{
+ return isl_set_plain_cmp(a->guard, b->guard);
+}
+
+/* Order the elements in "list" based on their guards.
+ */
+__isl_give isl_ast_graft_list *isl_ast_graft_list_sort_guard(
+ __isl_take isl_ast_graft_list *list)
+{
+ return isl_ast_graft_list_sort(list, &cmp_graft, NULL);
+}
+
+/* Merge the given two lists into a single list of grafts,
+ * merging grafts with the same guard into a single graft.
+ *
+ * "list2" has been sorted using isl_ast_graft_list_sort.
+ * "list1" may be the result of a previous call to isl_ast_graft_list_merge
+ * and may therefore not be completely sorted.
+ *
+ * The elements in "list2" need to be executed after those in "list1",
+ * but if the guard of a graft in "list2" is disjoint from the guards
+ * of some final elements in "list1", then it can be moved up to before
+ * those final elements.
+ *
+ * In particular, we look at each element g of "list2" in turn
+ * and move it up beyond elements of "list1" that would be sorted
+ * after g as long as each of these elements has a guard that is disjoint
+ * from that of g.
+ *
+ * We do not allow the second or any later element of "list2" to be moved
+ * before a previous elements of "list2" even if the reason that
+ * that element didn't move up further was that its guard was not disjoint
+ * from that of the previous element in "list1".
+ */
+__isl_give isl_ast_graft_list *isl_ast_graft_list_merge(
+ __isl_take isl_ast_graft_list *list1,
+ __isl_take isl_ast_graft_list *list2,
+ __isl_keep isl_ast_build *build)
+{
+ int i, j, first;
+
+ if (!list1 || !list2 || !build)
+ goto error;
+ if (list2->n == 0) {
+ isl_ast_graft_list_free(list2);
+ return list1;
+ }
+ if (list1->n == 0) {
+ isl_ast_graft_list_free(list1);
+ return list2;
+ }
+
+ first = 0;
+ for (i = 0; i < list2->n; ++i) {
+ isl_ast_graft *graft;
+ graft = isl_ast_graft_list_get_ast_graft(list2, i);
+ if (!graft)
+ break;
+
+ for (j = list1->n; j >= 0; --j) {
+ int cmp, disjoint;
+ isl_ast_graft *graft_j;
+
+ if (j == first)
+ cmp = -1;
+ else
+ cmp = isl_set_plain_cmp(list1->p[j - 1]->guard,
+ graft->guard);
+ if (cmp > 0) {
+ disjoint = isl_set_is_disjoint(graft->guard,
+ list1->p[j - 1]->guard);
+ if (disjoint < 0) {
+ isl_ast_graft_free(graft);
+ list1 = isl_ast_graft_list_free(list1);
+ break;
+ }
+ if (!disjoint)
+ cmp = -1;
+ }
+ if (cmp > 0)
+ continue;
+ if (cmp < 0) {
+ list1 = isl_ast_graft_list_insert(list1, j,
+ graft);
+ break;
+ }
+
+ --j;
+
+ graft_j = isl_ast_graft_list_get_ast_graft(list1, j);
+ graft_j = isl_ast_graft_fuse(graft_j, graft, build);
+ list1 = isl_ast_graft_list_set_ast_graft(list1, j,
+ graft_j);
+ break;
+ }
+
+ if (j < 0) {
+ isl_ast_graft_free(graft);
+ isl_die(isl_ast_build_get_ctx(build),
+ isl_error_internal,
+ "element failed to get inserted", break);
+ }
+
+ first = j + 1;
+ if (!list1)
+ break;
+ }
+ if (i < list2->n)
+ list1 = isl_ast_graft_list_free(list1);
+ isl_ast_graft_list_free(list2);
+
+ return list1;
+error:
+ isl_ast_graft_list_free(list1);
+ isl_ast_graft_list_free(list2);
+ return NULL;
+}
+
+/* Internal data structure for split_on_guard.
+ *
+ * "guard2list" is the constructed associative array.
+ * "any_match" gets set if any guard was seen more than once.
+ */
+struct isl_split_on_guard_data {
+ isl_set_to_ast_graft_list *guard2list;
+ int *any_match;
+};
+
+/* Add "graft" to the list associated to its guard in data->guard2list.
+ * If some other graft was already associated to this guard,
+ * then set data->any_match.
+ */
+static isl_stat add_to_guard_list(__isl_take isl_ast_graft *graft, void *user)
+{
+ struct isl_split_on_guard_data *data = user;
+ isl_set *guard;
+ isl_maybe_isl_ast_graft_list m;
+
+ if (!graft)
+ return isl_stat_error;
+ m = isl_set_to_ast_graft_list_try_get(data->guard2list, graft->guard);
+ if (m.valid < 0)
+ return isl_stat_non_null(isl_ast_graft_free(graft));
+
+ if (m.valid) {
+ *data->any_match = 1;
+ m.value = isl_ast_graft_list_add(m.value, graft);
+ } else {
+ m.value = isl_ast_graft_list_from_ast_graft(graft);
+ }
+ guard = isl_set_copy(graft->guard);
+ data->guard2list =
+ isl_set_to_ast_graft_list_set(data->guard2list, guard, m.value);
+
+ return isl_stat_non_null(data->guard2list);
+}
+
+/* Construct an associative array that groups the elements
+ * of "list" based on their guards.
+ * If any guard appears more than once, then set "any_match".
+ */
+static __isl_give isl_set_to_ast_graft_list *split_on_guard(
+ __isl_keep isl_ast_graft_list *list, int *any_match)
+{
+ struct isl_split_on_guard_data data = { NULL, any_match };
+ isl_size n;
+ isl_ctx *ctx;
+
+ n = isl_ast_graft_list_size(list);
+ if (n < 0)
+ return NULL;
+
+ ctx = isl_ast_graft_list_get_ctx(list);
+ data.guard2list = isl_set_to_ast_graft_list_alloc(ctx, n);
+
+ if (isl_ast_graft_list_foreach(list, &add_to_guard_list, &data) < 0)
+ return isl_set_to_ast_graft_list_free(data.guard2list);
+
+ return data.guard2list;
+}
+
+/* Add the elements of "guard_list" to "list".
+ */
+static isl_stat add_same_guard(__isl_take isl_set *guard,
+ __isl_take isl_ast_graft_list *guard_list, void *user)
+{
+ isl_ast_graft_list **list = user;
+
+ isl_set_free(guard);
+ *list = isl_ast_graft_list_concat(*list, guard_list);
+
+ return isl_stat_non_null(*list);
+}
+
+/* Given an associative array "guard2list" containing the elements
+ * of "list" grouped on common guards, reconstruct "list"
+ * by placing elements with the same guard consecutively.
+ */
+static __isl_give isl_ast_graft_list *reconstruct(
+ __isl_take isl_ast_graft_list *list,
+ __isl_keep isl_set_to_ast_graft_list *guard2list,
+ __isl_keep isl_ast_build *build)
+{
+ list = isl_ast_graft_list_clear(list);
+ if (isl_set_to_ast_graft_list_foreach(guard2list,
+ &add_same_guard, &list) < 0)
+ list = isl_ast_graft_list_free(list);
+
+ return list;
+}
+
+/* Group the grafts in "list" based on identical guards.
+ *
+ * Note that there need to be a least three elements in the list
+ * for the elements not to be grouped already.
+ *
+ * Group the elements in an associative array based on their guards.
+ * If any guard was seen more than once, then reconstruct the list
+ * from the associative array. Otherwise, simply return the original list.
+ */
+__isl_give isl_ast_graft_list *isl_ast_graft_list_group_on_guard(
+ __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build)
+{
+ int any_match = 0;
+ isl_size n;
+ isl_set_to_ast_graft_list *guard2list;
+
+ n = isl_ast_graft_list_size(list);
+ if (n < 0)
+ return isl_ast_graft_list_free(list);
+ if (n <= 2)
+ return list;
+
+ guard2list = split_on_guard(list, &any_match);
+ if (any_match)
+ list = reconstruct(list, guard2list, build);
+
+ isl_set_to_ast_graft_list_free(guard2list);
+
+ return list;
+}
+
+__isl_give isl_printer *isl_printer_print_ast_graft(__isl_take isl_printer *p,
+ __isl_keep isl_ast_graft *graft)
+{
+ if (!p)
+ return NULL;
+ if (!graft)
+ return isl_printer_free(p);
+
+ p = isl_printer_print_str(p, "(");
+ p = isl_printer_print_str(p, "guard: ");
+ p = isl_printer_print_set(p, graft->guard);
+ p = isl_printer_print_str(p, ", ");
+ p = isl_printer_print_str(p, "enforced: ");
+ p = isl_printer_print_basic_set(p, graft->enforced);
+ p = isl_printer_print_str(p, ", ");
+ p = isl_printer_print_str(p, "node: ");
+ p = isl_printer_print_ast_node(p, graft->node);
+ p = isl_printer_print_str(p, ")");
+
+ return p;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_graft_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_graft_private.h
new file mode 100644
index 00000000000..5ca7c033663
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_graft_private.h
@@ -0,0 +1,105 @@
+#ifndef ISL_AST_GRAFT_PRIVATE_H
+#define ISL_AST_GRAFT_PRIVATE_H
+
+#include <isl/ast.h>
+#include <isl/ast_build.h>
+#include <isl/set.h>
+#include <isl/list.h>
+#include <isl/printer.h>
+
+struct isl_ast_graft;
+typedef struct isl_ast_graft isl_ast_graft;
+
+/* Representation of part of an AST ("node") with some additional polyhedral
+ * information about the tree.
+ *
+ * "guard" contains conditions that should still be enforced by
+ * some ancestor of the current tree. In particular, the already
+ * generated tree assumes that these conditions hold, but may not
+ * have enforced them itself.
+ * The guard should not contain any unknown divs as it will be used
+ * to generate an if condition.
+ *
+ * "enforced" expresses constraints that are already enforced by the for
+ * nodes in the current tree and that therefore do not need to be enforced
+ * by any ancestor.
+ * The constraints only involve outer loop iterators.
+ */
+struct isl_ast_graft {
+ int ref;
+
+ isl_ast_node *node;
+
+ isl_set *guard;
+ isl_basic_set *enforced;
+};
+
+ISL_DECLARE_LIST(ast_graft)
+
+#undef EL
+#define EL isl_ast_graft
+
+#include <isl_list_templ.h>
+
+isl_ctx *isl_ast_graft_get_ctx(__isl_keep isl_ast_graft *graft);
+
+__isl_give isl_ast_graft *isl_ast_graft_alloc(
+ __isl_take isl_ast_node *node, __isl_keep isl_ast_build *build);
+__isl_give isl_ast_graft *isl_ast_graft_alloc_from_children(
+ __isl_take isl_ast_graft_list *list, __isl_take isl_set *guard,
+ __isl_take isl_basic_set *enforced, __isl_keep isl_ast_build *build,
+ __isl_keep isl_ast_build *sub_build);
+__isl_give isl_ast_graft_list *isl_ast_graft_list_fuse(
+ __isl_take isl_ast_graft_list *children,
+ __isl_keep isl_ast_build *build);
+__isl_give isl_ast_graft *isl_ast_graft_alloc_domain(
+ __isl_take isl_map *schedule, __isl_keep isl_ast_build *build);
+__isl_null isl_ast_graft *isl_ast_graft_free(__isl_take isl_ast_graft *graft);
+__isl_give isl_ast_graft_list *isl_ast_graft_list_sort_guard(
+ __isl_take isl_ast_graft_list *list);
+
+__isl_give isl_ast_graft_list *isl_ast_graft_list_merge(
+ __isl_take isl_ast_graft_list *list1,
+ __isl_take isl_ast_graft_list *list2,
+ __isl_keep isl_ast_build *build);
+__isl_give isl_ast_graft_list *isl_ast_graft_list_group_on_guard(
+ __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build);
+
+__isl_give isl_ast_node *isl_ast_graft_get_node(
+ __isl_keep isl_ast_graft *graft);
+__isl_give isl_basic_set *isl_ast_graft_get_enforced(
+ __isl_keep isl_ast_graft *graft);
+__isl_give isl_set *isl_ast_graft_get_guard(__isl_keep isl_ast_graft *graft);
+
+__isl_give isl_ast_graft *isl_ast_graft_insert_for(
+ __isl_take isl_ast_graft *graft, __isl_take isl_ast_node *node);
+__isl_give isl_ast_graft *isl_ast_graft_add_guard(
+ __isl_take isl_ast_graft *graft,
+ __isl_take isl_set *guard, __isl_keep isl_ast_build *build);
+__isl_give isl_ast_graft *isl_ast_graft_enforce(
+ __isl_take isl_ast_graft *graft, __isl_take isl_basic_set *enforced);
+
+__isl_give isl_ast_graft *isl_ast_graft_insert_mark(
+ __isl_take isl_ast_graft *graft, __isl_take isl_id *mark);
+
+__isl_give isl_ast_graft_list *isl_ast_graft_list_unembed(
+ __isl_take isl_ast_graft_list *list, int product);
+__isl_give isl_ast_graft_list *isl_ast_graft_list_preimage_multi_aff(
+ __isl_take isl_ast_graft_list *list, __isl_take isl_multi_aff *ma);
+__isl_give isl_ast_graft_list *isl_ast_graft_list_insert_pending_guard_nodes(
+ __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build);
+
+__isl_give isl_ast_node *isl_ast_node_from_graft_list(
+ __isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build);
+
+__isl_give isl_basic_set *isl_ast_graft_list_extract_shared_enforced(
+ __isl_keep isl_ast_graft_list *list, __isl_keep isl_ast_build *build);
+__isl_give isl_set *isl_ast_graft_list_extract_hoistable_guard(
+ __isl_keep isl_ast_graft_list *list, __isl_keep isl_ast_build *build);
+__isl_give isl_ast_graft_list *isl_ast_graft_list_gist_guards(
+ __isl_take isl_ast_graft_list *list, __isl_take isl_set *context);
+
+__isl_give isl_printer *isl_printer_print_ast_graft(__isl_take isl_printer *p,
+ __isl_keep isl_ast_graft *graft);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_private.h
new file mode 100644
index 00000000000..a7726a28df7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ast_private.h
@@ -0,0 +1,122 @@
+#ifndef ISL_AST_PRIVATE_H
+#define ISL_AST_PRIVATE_H
+
+#include <isl/aff.h>
+#include <isl/ast.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl/vec.h>
+#include <isl/list.h>
+
+/* An expression is either an integer, an identifier or an operation
+ * with zero or more arguments.
+ */
+struct isl_ast_expr {
+ int ref;
+
+ isl_ctx *ctx;
+
+ enum isl_ast_expr_type type;
+
+ union {
+ isl_val *v;
+ isl_id *id;
+ struct {
+ enum isl_ast_expr_op_type op;
+ unsigned n_arg;
+ isl_ast_expr **args;
+ } op;
+ } u;
+};
+
+#undef EL
+#define EL isl_ast_expr
+
+#include <isl_list_templ.h>
+
+__isl_give isl_ast_expr *isl_ast_expr_alloc_int_si(isl_ctx *ctx, int i);
+__isl_give isl_ast_expr *isl_ast_expr_alloc_op(isl_ctx *ctx,
+ enum isl_ast_expr_op_type op, int n_arg);
+__isl_give isl_ast_expr *isl_ast_expr_alloc_binary(
+ enum isl_ast_expr_op_type type,
+ __isl_take isl_ast_expr *expr1, __isl_take isl_ast_expr *expr2);
+
+#undef EL
+#define EL isl_ast_node
+
+#include <isl_list_templ.h>
+
+/* A node is either a block, an if, a for, a user node or a mark node.
+ * "else_node" is NULL if the if node does not have an else branch.
+ * "cond" and "inc" are NULL for degenerate for nodes.
+ * In case of a mark node, "mark" is the mark and "node" is the marked node.
+ */
+struct isl_ast_node {
+ int ref;
+
+ isl_ctx *ctx;
+ enum isl_ast_node_type type;
+
+ union {
+ struct {
+ isl_ast_node_list *children;
+ } b;
+ struct {
+ isl_ast_expr *guard;
+ isl_ast_node *then;
+ isl_ast_node *else_node;
+ } i;
+ struct {
+ unsigned degenerate : 1;
+ isl_ast_expr *iterator;
+ isl_ast_expr *init;
+ isl_ast_expr *cond;
+ isl_ast_expr *inc;
+ isl_ast_node *body;
+ } f;
+ struct {
+ isl_ast_expr *expr;
+ } e;
+ struct {
+ isl_id *mark;
+ isl_ast_node *node;
+ } m;
+ } u;
+
+ isl_id *annotation;
+};
+
+__isl_give isl_ast_node *isl_ast_node_alloc_for(__isl_take isl_id *id);
+__isl_give isl_ast_node *isl_ast_node_for_mark_degenerate(
+ __isl_take isl_ast_node *node);
+__isl_give isl_ast_node *isl_ast_node_alloc_if(__isl_take isl_ast_expr *guard);
+__isl_give isl_ast_node *isl_ast_node_alloc_block(
+ __isl_take isl_ast_node_list *list);
+__isl_give isl_ast_node *isl_ast_node_alloc_mark(__isl_take isl_id *id,
+ __isl_take isl_ast_node *node);
+__isl_give isl_ast_node *isl_ast_node_from_ast_node_list(
+ __isl_take isl_ast_node_list *list);
+__isl_give isl_ast_node *isl_ast_node_for_set_body(
+ __isl_take isl_ast_node *node, __isl_take isl_ast_node *body);
+__isl_give isl_ast_node *isl_ast_node_if_set_then(
+ __isl_take isl_ast_node *node, __isl_take isl_ast_node *child);
+
+struct isl_ast_print_options {
+ int ref;
+ isl_ctx *ctx;
+
+ __isl_give isl_printer *(*print_for)(__isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *options,
+ __isl_keep isl_ast_node *node, void *user);
+ void *print_for_user;
+ __isl_give isl_printer *(*print_user)(__isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *options,
+ __isl_keep isl_ast_node *node, void *user);
+ void *print_user_user;
+};
+
+__isl_give isl_printer *isl_ast_node_list_print(
+ __isl_keep isl_ast_node_list *list, __isl_take isl_printer *p,
+ __isl_keep isl_ast_print_options *options);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_basis_reduction.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_basis_reduction.h
new file mode 100644
index 00000000000..2517c2f1605
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_basis_reduction.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_BASIS_REDUCTION_H
+#define ISL_BASIS_REDUCTION_H
+
+#include <isl/set.h>
+#include <isl_mat_private.h>
+#include "isl_tab.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_tab *isl_tab_compute_reduced_basis(struct isl_tab *tab);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bernstein.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bernstein.c
new file mode 100644
index 00000000000..eddea8346e1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bernstein.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright 2006-2007 Universiteit Leiden
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, Leiden Institute of Advanced Computer Science,
+ * Universiteit Leiden, Niels Bohrweg 1, 2333 CA Leiden, The Netherlands
+ * and K.U.Leuven, Departement Computerwetenschappen, Celestijnenlaan 200A,
+ * B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl/set.h>
+#include <isl_seq.h>
+#include <isl_morph.h>
+#include <isl_factorization.h>
+#include <isl_vertices_private.h>
+#include <isl_polynomial_private.h>
+#include <isl_options_private.h>
+#include <isl_vec_private.h>
+#include <isl_bernstein.h>
+
+struct bernstein_data {
+ enum isl_fold type;
+ isl_qpolynomial *poly;
+ int check_tight;
+
+ isl_cell *cell;
+
+ isl_qpolynomial_fold *fold;
+ isl_qpolynomial_fold *fold_tight;
+ isl_pw_qpolynomial_fold *pwf;
+ isl_pw_qpolynomial_fold *pwf_tight;
+};
+
+static isl_bool vertex_is_integral(__isl_keep isl_basic_set *vertex)
+{
+ isl_size nvar;
+ isl_size nparam;
+ int i;
+
+ nvar = isl_basic_set_dim(vertex, isl_dim_set);
+ nparam = isl_basic_set_dim(vertex, isl_dim_param);
+ if (nvar < 0 || nparam < 0)
+ return isl_bool_error;
+ for (i = 0; i < nvar; ++i) {
+ int r = nvar - 1 - i;
+ if (!isl_int_is_one(vertex->eq[r][1 + nparam + i]) &&
+ !isl_int_is_negone(vertex->eq[r][1 + nparam + i]))
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+static __isl_give isl_qpolynomial *vertex_coordinate(
+ __isl_keep isl_basic_set *vertex, int i, __isl_take isl_space *space)
+{
+ isl_size nvar;
+ isl_size nparam;
+ isl_size total;
+ int r;
+ isl_int denom;
+ isl_qpolynomial *v;
+
+ isl_int_init(denom);
+
+ nvar = isl_basic_set_dim(vertex, isl_dim_set);
+ nparam = isl_basic_set_dim(vertex, isl_dim_param);
+ total = isl_basic_set_dim(vertex, isl_dim_all);
+ if (nvar < 0 || nparam < 0 || total < 0)
+ goto error;
+ r = nvar - 1 - i;
+
+ isl_int_set(denom, vertex->eq[r][1 + nparam + i]);
+ isl_assert(vertex->ctx, !isl_int_is_zero(denom), goto error);
+
+ if (isl_int_is_pos(denom))
+ isl_seq_neg(vertex->eq[r], vertex->eq[r], 1 + total);
+ else
+ isl_int_neg(denom, denom);
+
+ v = isl_qpolynomial_from_affine(space, vertex->eq[r], denom);
+ isl_int_clear(denom);
+
+ return v;
+error:
+ isl_space_free(space);
+ isl_int_clear(denom);
+ return NULL;
+}
+
+/* Check whether the bound associated to the selection "k" is tight,
+ * which is the case if we select exactly one vertex (i.e., one of the
+ * exponents in "k" is exactly "d") and if that vertex
+ * is integral for all values of the parameters.
+ *
+ * If the degree "d" is zero, then there are no exponents.
+ * Since the polynomial is a constant expression in this case,
+ * the bound is necessarily tight.
+ */
+static isl_bool is_tight(int *k, int n, int d, isl_cell *cell)
+{
+ int i;
+
+ if (d == 0)
+ return isl_bool_true;
+
+ for (i = 0; i < n; ++i) {
+ int v;
+ if (!k[i])
+ continue;
+ if (k[i] != d)
+ return isl_bool_false;
+ v = cell->ids[n - 1 - i];
+ return vertex_is_integral(cell->vertices->v[v].vertex);
+ }
+
+ return isl_bool_false;
+}
+
+static isl_stat add_fold(__isl_take isl_qpolynomial *b, __isl_keep isl_set *dom,
+ int *k, int n, int d, struct bernstein_data *data)
+{
+ isl_qpolynomial_fold *fold;
+ isl_bool tight;
+
+ fold = isl_qpolynomial_fold_alloc(data->type, b);
+
+ tight = isl_bool_false;
+ if (data->check_tight)
+ tight = is_tight(k, n, d, data->cell);
+ if (tight < 0)
+ return isl_stat_error;
+ if (tight)
+ data->fold_tight = isl_qpolynomial_fold_fold_on_domain(dom,
+ data->fold_tight, fold);
+ else
+ data->fold = isl_qpolynomial_fold_fold_on_domain(dom,
+ data->fold, fold);
+ return isl_stat_ok;
+}
+
+/* Extract the coefficients of the Bernstein base polynomials and store
+ * them in data->fold and data->fold_tight.
+ *
+ * In particular, the coefficient of each monomial
+ * of multi-degree (k[0], k[1], ..., k[n-1]) is divided by the corresponding
+ * multinomial coefficient d!/k[0]! k[1]! ... k[n-1]!
+ *
+ * c[i] contains the coefficient of the selected powers of the first i+1 vars.
+ * multinom[i] contains the partial multinomial coefficient.
+ */
+static isl_stat extract_coefficients(isl_qpolynomial *poly,
+ __isl_keep isl_set *dom, struct bernstein_data *data)
+{
+ int i;
+ int d;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_qpolynomial **c = NULL;
+ int *k = NULL;
+ int *left = NULL;
+ isl_vec *multinom = NULL;
+
+ n = isl_qpolynomial_dim(poly, isl_dim_in);
+ if (n < 0)
+ return isl_stat_error;
+
+ ctx = isl_qpolynomial_get_ctx(poly);
+ d = isl_qpolynomial_degree(poly);
+ isl_assert(ctx, n >= 2, return isl_stat_error);
+
+ c = isl_calloc_array(ctx, isl_qpolynomial *, n);
+ k = isl_alloc_array(ctx, int, n);
+ left = isl_alloc_array(ctx, int, n);
+ multinom = isl_vec_alloc(ctx, n);
+ if (!c || !k || !left || !multinom)
+ goto error;
+
+ isl_int_set_si(multinom->el[0], 1);
+ for (k[0] = d; k[0] >= 0; --k[0]) {
+ int i = 1;
+ isl_qpolynomial_free(c[0]);
+ c[0] = isl_qpolynomial_coeff(poly, isl_dim_in, n - 1, k[0]);
+ left[0] = d - k[0];
+ k[1] = -1;
+ isl_int_set(multinom->el[1], multinom->el[0]);
+ while (i > 0) {
+ if (i == n - 1) {
+ int j;
+ isl_space *space;
+ isl_qpolynomial *b;
+ isl_qpolynomial *f;
+ for (j = 2; j <= left[i - 1]; ++j)
+ isl_int_divexact_ui(multinom->el[i],
+ multinom->el[i], j);
+ b = isl_qpolynomial_coeff(c[i - 1], isl_dim_in,
+ n - 1 - i, left[i - 1]);
+ b = isl_qpolynomial_project_domain_on_params(b);
+ space = isl_qpolynomial_get_domain_space(b);
+ f = isl_qpolynomial_rat_cst_on_domain(space,
+ ctx->one, multinom->el[i]);
+ b = isl_qpolynomial_mul(b, f);
+ k[n - 1] = left[n - 2];
+ if (add_fold(b, dom, k, n, d, data) < 0)
+ goto error;
+ --i;
+ continue;
+ }
+ if (k[i] >= left[i - 1]) {
+ --i;
+ continue;
+ }
+ ++k[i];
+ if (k[i])
+ isl_int_divexact_ui(multinom->el[i],
+ multinom->el[i], k[i]);
+ isl_qpolynomial_free(c[i]);
+ c[i] = isl_qpolynomial_coeff(c[i - 1], isl_dim_in,
+ n - 1 - i, k[i]);
+ left[i] = left[i - 1] - k[i];
+ k[i + 1] = -1;
+ isl_int_set(multinom->el[i + 1], multinom->el[i]);
+ ++i;
+ }
+ isl_int_mul_ui(multinom->el[0], multinom->el[0], k[0]);
+ }
+
+ for (i = 0; i < n; ++i)
+ isl_qpolynomial_free(c[i]);
+
+ isl_vec_free(multinom);
+ free(left);
+ free(k);
+ free(c);
+ return isl_stat_ok;
+error:
+ isl_vec_free(multinom);
+ free(left);
+ free(k);
+ if (c)
+ for (i = 0; i < n; ++i)
+ isl_qpolynomial_free(c[i]);
+ free(c);
+ return isl_stat_error;
+}
+
+/* Perform bernstein expansion on the parametric vertices that are active
+ * on "cell".
+ *
+ * data->poly has been homogenized in the calling function.
+ *
+ * We plug in the barycentric coordinates for the set variables
+ *
+ * \vec x = \sum_i \alpha_i v_i(\vec p)
+ *
+ * and the constant "1 = \sum_i \alpha_i" for the homogeneous dimension.
+ * Next, we extract the coefficients of the Bernstein base polynomials.
+ */
+static isl_stat bernstein_coefficients_cell(__isl_take isl_cell *cell,
+ void *user)
+{
+ int i, j;
+ struct bernstein_data *data = (struct bernstein_data *)user;
+ isl_space *space_param;
+ isl_space *space_dst;
+ isl_qpolynomial *poly = data->poly;
+ isl_size n_in;
+ unsigned nvar;
+ int n_vertices;
+ isl_qpolynomial **subs;
+ isl_pw_qpolynomial_fold *pwf;
+ isl_set *dom;
+ isl_ctx *ctx;
+
+ n_in = isl_qpolynomial_dim(poly, isl_dim_in);
+ if (n_in < 0)
+ goto error;
+
+ nvar = n_in - 1;
+ n_vertices = cell->n_vertices;
+
+ ctx = isl_qpolynomial_get_ctx(poly);
+ if (n_vertices > nvar + 1 && ctx->opt->bernstein_triangulate)
+ return isl_cell_foreach_simplex(cell,
+ &bernstein_coefficients_cell, user);
+
+ subs = isl_alloc_array(ctx, isl_qpolynomial *, 1 + nvar);
+ if (!subs)
+ goto error;
+
+ space_param = isl_basic_set_get_space(cell->dom);
+ space_dst = isl_qpolynomial_get_domain_space(poly);
+ space_dst = isl_space_add_dims(space_dst, isl_dim_set, n_vertices);
+
+ for (i = 0; i < 1 + nvar; ++i)
+ subs[i] =
+ isl_qpolynomial_zero_on_domain(isl_space_copy(space_dst));
+
+ for (i = 0; i < n_vertices; ++i) {
+ isl_qpolynomial *c;
+ c = isl_qpolynomial_var_on_domain(isl_space_copy(space_dst),
+ isl_dim_set, 1 + nvar + i);
+ for (j = 0; j < nvar; ++j) {
+ int k = cell->ids[i];
+ isl_qpolynomial *v;
+ v = vertex_coordinate(cell->vertices->v[k].vertex, j,
+ isl_space_copy(space_param));
+ v = isl_qpolynomial_add_dims(v, isl_dim_in,
+ 1 + nvar + n_vertices);
+ v = isl_qpolynomial_mul(v, isl_qpolynomial_copy(c));
+ subs[1 + j] = isl_qpolynomial_add(subs[1 + j], v);
+ }
+ subs[0] = isl_qpolynomial_add(subs[0], c);
+ }
+ isl_space_free(space_dst);
+
+ poly = isl_qpolynomial_copy(poly);
+
+ poly = isl_qpolynomial_add_dims(poly, isl_dim_in, n_vertices);
+ poly = isl_qpolynomial_substitute(poly, isl_dim_in, 0, 1 + nvar, subs);
+ poly = isl_qpolynomial_drop_dims(poly, isl_dim_in, 0, 1 + nvar);
+
+ data->cell = cell;
+ dom = isl_set_from_basic_set(isl_basic_set_copy(cell->dom));
+ data->fold = isl_qpolynomial_fold_empty(data->type,
+ isl_space_copy(space_param));
+ data->fold_tight = isl_qpolynomial_fold_empty(data->type, space_param);
+ if (extract_coefficients(poly, dom, data) < 0) {
+ data->fold = isl_qpolynomial_fold_free(data->fold);
+ data->fold_tight = isl_qpolynomial_fold_free(data->fold_tight);
+ }
+
+ pwf = isl_pw_qpolynomial_fold_alloc(data->type, isl_set_copy(dom),
+ data->fold);
+ data->pwf = isl_pw_qpolynomial_fold_fold(data->pwf, pwf);
+ pwf = isl_pw_qpolynomial_fold_alloc(data->type, dom, data->fold_tight);
+ data->pwf_tight = isl_pw_qpolynomial_fold_fold(data->pwf_tight, pwf);
+
+ isl_qpolynomial_free(poly);
+ isl_cell_free(cell);
+ for (i = 0; i < 1 + nvar; ++i)
+ isl_qpolynomial_free(subs[i]);
+ free(subs);
+ return isl_stat_ok;
+error:
+ isl_cell_free(cell);
+ return isl_stat_error;
+}
+
+/* Base case of applying bernstein expansion.
+ *
+ * We compute the chamber decomposition of the parametric polytope "bset"
+ * and then perform bernstein expansion on the parametric vertices
+ * that are active on each chamber.
+ *
+ * If the polynomial does not depend on the set variables
+ * (and in particular if the number of set variables is zero)
+ * then the bound is equal to the polynomial and
+ * no actual bernstein expansion needs to be performed.
+ */
+static __isl_give isl_pw_qpolynomial_fold *bernstein_coefficients_base(
+ __isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, struct bernstein_data *data,
+ isl_bool *tight)
+{
+ int degree;
+ isl_size nvar;
+ isl_space *space;
+ isl_vertices *vertices;
+ isl_bool covers;
+
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+ if (nvar < 0)
+ bset = isl_basic_set_free(bset);
+ if (nvar == 0)
+ return isl_qpolynomial_cst_bound(bset, poly, data->type, tight);
+
+ degree = isl_qpolynomial_degree(poly);
+ if (degree < -1)
+ bset = isl_basic_set_free(bset);
+ if (degree <= 0)
+ return isl_qpolynomial_cst_bound(bset, poly, data->type, tight);
+
+ space = isl_basic_set_get_space(bset);
+ space = isl_space_params(space);
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_set, 1);
+ data->pwf = isl_pw_qpolynomial_fold_zero(isl_space_copy(space),
+ data->type);
+ data->pwf_tight = isl_pw_qpolynomial_fold_zero(space, data->type);
+ data->poly = isl_qpolynomial_homogenize(isl_qpolynomial_copy(poly));
+ vertices = isl_basic_set_compute_vertices(bset);
+ if (isl_vertices_foreach_disjoint_cell(vertices,
+ &bernstein_coefficients_cell, data) < 0)
+ data->pwf = isl_pw_qpolynomial_fold_free(data->pwf);
+ isl_vertices_free(vertices);
+ isl_qpolynomial_free(data->poly);
+
+ isl_basic_set_free(bset);
+ isl_qpolynomial_free(poly);
+
+ covers = isl_pw_qpolynomial_fold_covers(data->pwf_tight, data->pwf);
+ if (covers < 0)
+ goto error;
+
+ if (tight)
+ *tight = covers;
+
+ if (covers) {
+ isl_pw_qpolynomial_fold_free(data->pwf);
+ return data->pwf_tight;
+ }
+
+ data->pwf = isl_pw_qpolynomial_fold_fold(data->pwf, data->pwf_tight);
+
+ return data->pwf;
+error:
+ isl_pw_qpolynomial_fold_free(data->pwf_tight);
+ isl_pw_qpolynomial_fold_free(data->pwf);
+ return NULL;
+}
+
+/* Apply bernstein expansion recursively by working in on len[i]
+ * set variables at a time, with i ranging from n_group - 1 to 0.
+ */
+static __isl_give isl_pw_qpolynomial_fold *bernstein_coefficients_recursive(
+ __isl_take isl_pw_qpolynomial *pwqp,
+ int n_group, int *len, struct bernstein_data *data, isl_bool *tight)
+{
+ int i;
+ isl_size nparam;
+ isl_size nvar;
+ isl_pw_qpolynomial_fold *pwf;
+
+ nparam = isl_pw_qpolynomial_dim(pwqp, isl_dim_param);
+ nvar = isl_pw_qpolynomial_dim(pwqp, isl_dim_in);
+ if (nparam < 0 || nvar < 0)
+ goto error;
+
+ pwqp = isl_pw_qpolynomial_move_dims(pwqp, isl_dim_param, nparam,
+ isl_dim_in, 0, nvar - len[n_group - 1]);
+ pwf = isl_pw_qpolynomial_bound(pwqp, data->type, tight);
+
+ for (i = n_group - 2; i >= 0; --i) {
+ nparam = isl_pw_qpolynomial_fold_dim(pwf, isl_dim_param);
+ if (nparam < 0)
+ return isl_pw_qpolynomial_fold_free(pwf);
+ pwf = isl_pw_qpolynomial_fold_move_dims(pwf, isl_dim_in, 0,
+ isl_dim_param, nparam - len[i], len[i]);
+ if (tight && !*tight)
+ tight = NULL;
+ pwf = isl_pw_qpolynomial_fold_bound(pwf, tight);
+ }
+
+ return pwf;
+error:
+ isl_pw_qpolynomial_free(pwqp);
+ return NULL;
+}
+
+static __isl_give isl_pw_qpolynomial_fold *bernstein_coefficients_factors(
+ __isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, struct bernstein_data *data,
+ isl_bool *tight)
+{
+ isl_factorizer *f;
+ isl_set *set;
+ isl_pw_qpolynomial *pwqp;
+ isl_pw_qpolynomial_fold *pwf;
+
+ f = isl_basic_set_factorizer(bset);
+ if (!f)
+ goto error;
+ if (f->n_group == 0) {
+ isl_factorizer_free(f);
+ return bernstein_coefficients_base(bset, poly, data, tight);
+ }
+
+ set = isl_set_from_basic_set(bset);
+ pwqp = isl_pw_qpolynomial_alloc(set, poly);
+ pwqp = isl_pw_qpolynomial_morph_domain(pwqp, isl_morph_copy(f->morph));
+
+ pwf = bernstein_coefficients_recursive(pwqp, f->n_group, f->len, data,
+ tight);
+
+ isl_factorizer_free(f);
+
+ return pwf;
+error:
+ isl_basic_set_free(bset);
+ isl_qpolynomial_free(poly);
+ return NULL;
+}
+
+static __isl_give isl_pw_qpolynomial_fold *bernstein_coefficients_full_recursive(
+ __isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, struct bernstein_data *data,
+ isl_bool *tight)
+{
+ int i;
+ int *len;
+ isl_size nvar;
+ isl_pw_qpolynomial_fold *pwf;
+ isl_set *set;
+ isl_pw_qpolynomial *pwqp;
+
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+ if (nvar < 0 || !poly)
+ goto error;
+
+ len = isl_alloc_array(bset->ctx, int, nvar);
+ if (nvar && !len)
+ goto error;
+
+ for (i = 0; i < nvar; ++i)
+ len[i] = 1;
+
+ set = isl_set_from_basic_set(bset);
+ pwqp = isl_pw_qpolynomial_alloc(set, poly);
+
+ pwf = bernstein_coefficients_recursive(pwqp, nvar, len, data, tight);
+
+ free(len);
+
+ return pwf;
+error:
+ isl_basic_set_free(bset);
+ isl_qpolynomial_free(poly);
+ return NULL;
+}
+
+/* Compute a bound on the polynomial defined over the parametric polytope
+ * using bernstein expansion and store the result
+ * in bound->pwf and bound->pwf_tight.
+ *
+ * If bernstein_recurse is set to ISL_BERNSTEIN_FACTORS, we check if
+ * the polytope can be factorized and apply bernstein expansion recursively
+ * on the factors.
+ * If bernstein_recurse is set to ISL_BERNSTEIN_INTERVALS, we apply
+ * bernstein expansion recursively on each dimension.
+ * Otherwise, we apply bernstein expansion on the entire polytope.
+ */
+isl_stat isl_qpolynomial_bound_on_domain_bernstein(
+ __isl_take isl_basic_set *bset, __isl_take isl_qpolynomial *poly,
+ struct isl_bound *bound)
+{
+ struct bernstein_data data;
+ isl_pw_qpolynomial_fold *pwf;
+ isl_size nvar;
+ isl_bool tight = isl_bool_false;
+ isl_bool *tp = bound->check_tight ? &tight : NULL;
+
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+ if (nvar < 0 || !poly)
+ goto error;
+
+ data.type = bound->type;
+ data.check_tight = bound->check_tight;
+
+ if (bset->ctx->opt->bernstein_recurse & ISL_BERNSTEIN_FACTORS)
+ pwf = bernstein_coefficients_factors(bset, poly, &data, tp);
+ else if (nvar > 1 &&
+ (bset->ctx->opt->bernstein_recurse & ISL_BERNSTEIN_INTERVALS))
+ pwf = bernstein_coefficients_full_recursive(bset, poly, &data, tp);
+ else
+ pwf = bernstein_coefficients_base(bset, poly, &data, tp);
+
+ if (tight)
+ return isl_bound_add_tight(bound, pwf);
+ else
+ return isl_bound_add(bound, pwf);
+error:
+ isl_basic_set_free(bset);
+ isl_qpolynomial_free(poly);
+ return isl_stat_error;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bernstein.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bernstein.h
new file mode 100644
index 00000000000..ca11df4bf0f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bernstein.h
@@ -0,0 +1,5 @@
+#include <isl_bound.h>
+
+isl_stat isl_qpolynomial_bound_on_domain_bernstein(
+ __isl_take isl_basic_set *bset, __isl_take isl_qpolynomial *poly,
+ struct isl_bound *bound);
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bind_domain_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bind_domain_templ.c
new file mode 100644
index 00000000000..06cbf07c7ef
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bind_domain_templ.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2018 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl/space.h>
+
+/* Merge parameter "param" into the input dimension "i" of "obj".
+ *
+ * First plug in the parameter for the input dimension in "obj".
+ * The drop the (now defunct) input dimension and
+ * move the parameter in its original position.
+ * Since dimension manipulations destroy spaces, modify the space
+ * separately by only dropping the parameter.
+ */
+static __isl_give TYPE *FN(TYPE,merge_param)(__isl_take TYPE *obj, int i,
+ int param)
+{
+ isl_id *id;
+ isl_aff *aff;
+ isl_space *space;
+ isl_multi_aff *ma;
+
+ space = FN(TYPE,get_domain_space)(obj);
+ id = isl_space_get_dim_id(space, isl_dim_param, param);
+ aff = isl_aff_param_on_domain_space_id(isl_space_copy(space), id);
+ space = isl_space_map_from_set(space);
+ ma = isl_multi_aff_identity(space);
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ obj = FN(TYPE,pullback_multi_aff)(obj, ma);
+ space = FN(TYPE,get_domain_space)(obj);
+ obj = FN(TYPE,drop_dims)(obj, isl_dim_in, i, 1);
+ obj = FN(TYPE,move_dims)(obj, isl_dim_in, i, isl_dim_param, param, 1);
+ space = isl_space_drop_dims(space, isl_dim_param, param, 1);
+ obj = FN(TYPE,reset_domain_space)(obj, space);
+
+ return obj;
+}
+
+/* Given a tuple of identifiers "tuple" that correspond
+ * to the initial input dimensions of "obj",
+ * if any of those identifiers appear as parameters
+ * in "obj", then equate those parameters with the corresponding
+ * input dimensions and project out the parameters.
+ * The result therefore has no such parameters.
+ */
+static __isl_give TYPE *FN(TYPE,equate_initial_params)(__isl_take TYPE *obj,
+ __isl_keep isl_multi_id *tuple)
+{
+ int i;
+ isl_size n;
+
+ n = isl_multi_id_size(tuple);
+ if (n < 0)
+ return FN(TYPE,free)(obj);
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+ int pos;
+
+ id = isl_multi_id_get_at(tuple, i);
+ if (!id)
+ return FN(TYPE,free)(obj);
+ pos = FN(TYPE,find_dim_by_id)(obj, isl_dim_param, id);
+ isl_id_free(id);
+ if (pos < 0)
+ continue;
+ obj = FN(TYPE,merge_param)(obj, i, pos);
+ }
+
+ return obj;
+}
+
+/* Given a tuple of identifiers "tuple" in a space that corresponds
+ * to the domain of "obj", if any of those identifiers appear as parameters
+ * in "obj", then equate those parameters with the corresponding
+ * input dimensions and project out the parameters.
+ * The result therefore has no such parameters.
+ */
+static __isl_give TYPE *FN(TYPE,equate_domain_params)(__isl_take TYPE *obj,
+ __isl_keep isl_multi_id *tuple)
+{
+ isl_stat r;
+ isl_space *obj_space, *tuple_space;
+
+ obj_space = FN(TYPE,get_space)(obj);
+ tuple_space = isl_multi_id_peek_space(tuple);
+ r = isl_space_check_domain_tuples(tuple_space, obj_space);
+ isl_space_free(obj_space);
+ if (r < 0)
+ return FN(TYPE,free)(obj);
+
+ return FN(TYPE,equate_initial_params)(obj, tuple);
+}
+
+/* Bind the domain dimensions of the function "obj" to parameters
+ * with identifiers specified by "tuple", living in the same space
+ * as the domain of "obj".
+ *
+ * If no parameters with these identifiers appear in "obj" already,
+ * then the domain dimensions are simply reinterpreted as parameters.
+ * Otherwise, the parameters are first equated to the corresponding
+ * domain dimensions.
+ */
+__isl_give TYPE *FN(TYPE,bind_domain)(__isl_take TYPE *obj,
+ __isl_take isl_multi_id *tuple)
+{
+ isl_space *space;
+
+ obj = FN(TYPE,equate_domain_params)(obj, tuple);
+ space = FN(TYPE,get_space)(obj);
+ space = isl_space_bind_map_domain(space, tuple);
+ isl_multi_id_free(tuple);
+ obj = FN(TYPE,reset_space)(obj, space);
+
+ return obj;
+}
+
+/* Given a tuple of identifiers "tuple" in a space that corresponds
+ * to the domain of the wrapped relation in the domain of "obj",
+ * if any of those identifiers appear as parameters
+ * in "obj", then equate those parameters with the corresponding
+ * input dimensions and project out the parameters.
+ * The result therefore has no such parameters.
+ */
+static __isl_give TYPE *FN(TYPE,equate_domain_wrapped_domain_params)(
+ __isl_take TYPE *obj, __isl_keep isl_multi_id *tuple)
+{
+ isl_stat r;
+ isl_space *obj_space, *tuple_space;
+
+ obj_space = FN(TYPE,get_space)(obj);
+ tuple_space = isl_multi_id_peek_space(tuple);
+ r = isl_space_check_domain_wrapped_domain_tuples(tuple_space,
+ obj_space);
+ isl_space_free(obj_space);
+ if (r < 0)
+ return FN(TYPE,free)(obj);
+
+ return FN(TYPE,equate_initial_params)(obj, tuple);
+}
+
+/* Given a function living in a space of the form [A -> B] -> C and
+ * a tuple of identifiers in A, bind the domain dimensions of the relation
+ * wrapped in the domain of "obj" with identifiers specified by "tuple",
+ * returning a function in the space B -> C.
+ *
+ * If no parameters with these identifiers appear in "obj" already,
+ * then the domain dimensions are simply reinterpreted as parameters.
+ * Otherwise, the parameters are first equated to the corresponding
+ * domain dimensions.
+ */
+__isl_give TYPE *FN(TYPE,bind_domain_wrapped_domain)(__isl_take TYPE *obj,
+ __isl_take isl_multi_id *tuple)
+{
+ isl_space *space;
+
+ obj = FN(TYPE,equate_domain_wrapped_domain_params)(obj, tuple);
+ space = FN(TYPE,get_space)(obj);
+ space = isl_space_bind_domain_wrapped_domain(space, tuple);
+ isl_multi_id_free(tuple);
+ obj = FN(TYPE,reset_space)(obj, space);
+
+ return obj;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_blk.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_blk.c
new file mode 100644
index 00000000000..0b03ecf0dd3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_blk.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <isl_blk.h>
+#include <isl_ctx_private.h>
+
+/* The maximal number of cache misses before first element is evicted */
+#define ISL_BLK_MAX_MISS 100
+
+struct isl_blk isl_blk_empty()
+{
+ struct isl_blk block;
+ block.size = 0;
+ block.data = NULL;
+ return block;
+}
+
+static int isl_blk_is_empty(struct isl_blk block)
+{
+ return block.size == 0 && block.data == NULL;
+}
+
+static struct isl_blk isl_blk_error()
+{
+ struct isl_blk block;
+ block.size = -1;
+ block.data = NULL;
+ return block;
+}
+
+int isl_blk_is_error(struct isl_blk block)
+{
+ return block.size == -1 && block.data == NULL;
+}
+
+static void isl_blk_free_force(struct isl_ctx *ctx, struct isl_blk block)
+{
+ int i;
+
+ for (i = 0; i < block.size; ++i)
+ isl_int_clear(block.data[i]);
+ free(block.data);
+}
+
+static struct isl_blk extend(struct isl_ctx *ctx, struct isl_blk block,
+ size_t new_n)
+{
+ int i;
+ isl_int *p;
+
+ if (block.size >= new_n)
+ return block;
+
+ p = isl_realloc_array(ctx, block.data, isl_int, new_n);
+ if (!p) {
+ isl_blk_free_force(ctx, block);
+ return isl_blk_error();
+ }
+ block.data = p;
+
+ for (i = block.size; i < new_n; ++i)
+ isl_int_init(block.data[i]);
+ block.size = new_n;
+
+ return block;
+}
+
+struct isl_blk isl_blk_alloc(struct isl_ctx *ctx, size_t n)
+{
+ int i;
+ struct isl_blk block;
+
+ block = isl_blk_empty();
+ if (n && ctx->n_cached) {
+ int best = 0;
+ for (i = 1; ctx->cache[best].size != n && i < ctx->n_cached; ++i) {
+ if (ctx->cache[best].size < n) {
+ if (ctx->cache[i].size > ctx->cache[best].size)
+ best = i;
+ } else if (ctx->cache[i].size >= n &&
+ ctx->cache[i].size < ctx->cache[best].size)
+ best = i;
+ }
+ if (ctx->cache[best].size < 2 * n + 100) {
+ block = ctx->cache[best];
+ if (--ctx->n_cached != best)
+ ctx->cache[best] = ctx->cache[ctx->n_cached];
+ if (best == 0)
+ ctx->n_miss = 0;
+ } else if (ctx->n_miss++ >= ISL_BLK_MAX_MISS) {
+ isl_blk_free_force(ctx, ctx->cache[0]);
+ if (--ctx->n_cached != 0)
+ ctx->cache[0] = ctx->cache[ctx->n_cached];
+ ctx->n_miss = 0;
+ }
+ }
+
+ return extend(ctx, block, n);
+}
+
+struct isl_blk isl_blk_extend(struct isl_ctx *ctx, struct isl_blk block,
+ size_t new_n)
+{
+ if (isl_blk_is_empty(block))
+ return isl_blk_alloc(ctx, new_n);
+
+ return extend(ctx, block, new_n);
+}
+
+void isl_blk_free(struct isl_ctx *ctx, struct isl_blk block)
+{
+ if (isl_blk_is_empty(block) || isl_blk_is_error(block))
+ return;
+
+ if (ctx->n_cached < ISL_BLK_CACHE_SIZE)
+ ctx->cache[ctx->n_cached++] = block;
+ else
+ isl_blk_free_force(ctx, block);
+}
+
+void isl_blk_clear_cache(struct isl_ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < ctx->n_cached; ++i)
+ isl_blk_free_force(ctx, ctx->cache[i]);
+ ctx->n_cached = 0;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_blk.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_blk.h
new file mode 100644
index 00000000000..7756e010ec2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_blk.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_BLK_H
+#define ISL_BLK_H
+
+#include <isl_int.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_blk {
+ size_t size;
+ isl_int *data;
+};
+
+#define ISL_BLK_CACHE_SIZE 20
+
+struct isl_ctx;
+
+struct isl_blk isl_blk_alloc(struct isl_ctx *ctx, size_t n);
+struct isl_blk isl_blk_empty(void);
+int isl_blk_is_error(struct isl_blk block);
+struct isl_blk isl_blk_extend(struct isl_ctx *ctx, struct isl_blk block,
+ size_t new_n);
+void isl_blk_free(struct isl_ctx *ctx, struct isl_blk block);
+void isl_blk_clear_cache(struct isl_ctx *ctx);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bound.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bound.c
new file mode 100644
index 00000000000..64247aca052
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bound.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl_bound.h>
+#include <isl_bernstein.h>
+#include <isl_range.h>
+#include <isl_polynomial_private.h>
+#include <isl_options_private.h>
+
+/* Given a polynomial "poly" that is constant in terms
+ * of the domain variables, construct a polynomial reduction
+ * of type "type" that is equal to "poly" on "bset",
+ * with the domain projected onto the parameters.
+ */
+__isl_give isl_pw_qpolynomial_fold *isl_qpolynomial_cst_bound(
+ __isl_take isl_basic_set *bset, __isl_take isl_qpolynomial *poly,
+ enum isl_fold type, isl_bool *tight)
+{
+ isl_set *dom;
+ isl_qpolynomial_fold *fold;
+ isl_pw_qpolynomial_fold *pwf;
+
+ fold = isl_qpolynomial_fold_alloc(type, poly);
+ dom = isl_set_from_basic_set(bset);
+ if (tight)
+ *tight = isl_bool_true;
+ pwf = isl_pw_qpolynomial_fold_alloc(type, dom, fold);
+ return isl_pw_qpolynomial_fold_project_domain_on_params(pwf);
+}
+
+/* Add the bound "pwf", which is not known to be tight,
+ * to the output of "bound".
+ */
+isl_stat isl_bound_add(struct isl_bound *bound,
+ __isl_take isl_pw_qpolynomial_fold *pwf)
+{
+ bound->pwf = isl_pw_qpolynomial_fold_fold(bound->pwf, pwf);
+ return isl_stat_non_null(bound->pwf);
+}
+
+/* Add the bound "pwf", which is known to be tight,
+ * to the output of "bound".
+ */
+isl_stat isl_bound_add_tight(struct isl_bound *bound,
+ __isl_take isl_pw_qpolynomial_fold *pwf)
+{
+ bound->pwf_tight = isl_pw_qpolynomial_fold_fold(bound->pwf_tight, pwf);
+ return isl_stat_non_null(bound->pwf);
+}
+
+/* Given a polynomial "poly" that is constant in terms
+ * of the domain variables and the domain "bset",
+ * construct the corresponding polynomial reduction and
+ * add it to the tight bounds of "bound".
+ */
+static isl_stat add_constant_poly(__isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, struct isl_bound *bound)
+{
+ isl_pw_qpolynomial_fold *pwf;
+
+ pwf = isl_qpolynomial_cst_bound(bset, poly, bound->type, NULL);
+ return isl_bound_add_tight(bound, pwf);
+}
+
+/* Compute a bound on the polynomial defined over the parametric polytope
+ * using either range propagation or bernstein expansion and
+ * store the result in bound->pwf and bound->pwf_tight.
+ * Since bernstein expansion requires bounded domains, we apply
+ * range propagation on unbounded domains. Otherwise, we respect the choice
+ * of the user.
+ *
+ * If the polynomial does not depend on the set variables
+ * then the bound is equal to the polynomial and
+ * it can be added to "bound" directly.
+ */
+static isl_stat compressed_guarded_poly_bound(__isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, void *user)
+{
+ struct isl_bound *bound = (struct isl_bound *)user;
+ isl_ctx *ctx;
+ int bounded;
+ int degree;
+
+ if (!bset || !poly)
+ goto error;
+
+ degree = isl_qpolynomial_degree(poly);
+ if (degree < -1)
+ goto error;
+ if (degree <= 0)
+ return add_constant_poly(bset, poly, bound);
+
+ ctx = isl_basic_set_get_ctx(bset);
+ if (ctx->opt->bound == ISL_BOUND_RANGE)
+ return isl_qpolynomial_bound_on_domain_range(bset, poly, bound);
+
+ bounded = isl_basic_set_is_bounded(bset);
+ if (bounded < 0)
+ goto error;
+ if (bounded)
+ return isl_qpolynomial_bound_on_domain_bernstein(bset, poly, bound);
+ else
+ return isl_qpolynomial_bound_on_domain_range(bset, poly, bound);
+error:
+ isl_basic_set_free(bset);
+ isl_qpolynomial_free(poly);
+ return isl_stat_error;
+}
+
+static isl_stat unwrapped_guarded_poly_bound(__isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, void *user)
+{
+ struct isl_bound *bound = (struct isl_bound *)user;
+ isl_pw_qpolynomial_fold *top_pwf;
+ isl_pw_qpolynomial_fold *top_pwf_tight;
+ isl_space *space;
+ isl_morph *morph;
+ isl_stat r;
+
+ bset = isl_basic_set_detect_equalities(bset);
+
+ if (!bset)
+ goto error;
+
+ if (bset->n_eq == 0)
+ return compressed_guarded_poly_bound(bset, poly, user);
+
+ morph = isl_basic_set_full_compression(bset);
+
+ bset = isl_morph_basic_set(isl_morph_copy(morph), bset);
+ poly = isl_qpolynomial_morph_domain(poly, isl_morph_copy(morph));
+
+ space = isl_morph_get_ran_space(morph);
+ space = isl_space_params(space);
+
+ top_pwf = bound->pwf;
+ top_pwf_tight = bound->pwf_tight;
+
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, 1);
+ bound->pwf = isl_pw_qpolynomial_fold_zero(isl_space_copy(space),
+ bound->type);
+ bound->pwf_tight = isl_pw_qpolynomial_fold_zero(space, bound->type);
+
+ r = compressed_guarded_poly_bound(bset, poly, user);
+
+ morph = isl_morph_dom_params(morph);
+ morph = isl_morph_ran_params(morph);
+ morph = isl_morph_inverse(morph);
+
+ bound->pwf = isl_pw_qpolynomial_fold_morph_domain(bound->pwf,
+ isl_morph_copy(morph));
+ bound->pwf_tight = isl_pw_qpolynomial_fold_morph_domain(
+ bound->pwf_tight, morph);
+
+ isl_bound_add(bound, top_pwf);
+ isl_bound_add_tight(bound, top_pwf_tight);
+
+ return r;
+error:
+ isl_basic_set_free(bset);
+ isl_qpolynomial_free(poly);
+ return isl_stat_error;
+}
+
+/* Update bound->pwf and bound->pwf_tight with a bound
+ * of type bound->type on the polynomial "poly" over the domain "bset".
+ *
+ * If the original problem had a wrapped relation in the domain,
+ * meaning that the bound should be computed over the range
+ * of this relation, then temporarily treat the domain dimensions
+ * of this wrapped relation as parameters, compute a bound
+ * in terms of these and the original parameters,
+ * turn the parameters back into set dimensions and
+ * add the results to bound->pwf and bound->pwf_tight.
+ *
+ * Note that even though "bset" is known to live in the same space
+ * as the domain of "poly", the names of the set dimensions
+ * may be different (or missing). Make sure the naming is exactly
+ * the same before turning these dimensions into parameters
+ * to ensure that the spaces are still the same after
+ * this operation.
+ */
+static isl_stat guarded_poly_bound(__isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, void *user)
+{
+ struct isl_bound *bound = (struct isl_bound *)user;
+ isl_space *space;
+ isl_pw_qpolynomial_fold *top_pwf;
+ isl_pw_qpolynomial_fold *top_pwf_tight;
+ isl_size nparam;
+ isl_size n_in;
+ isl_stat r;
+
+ if (!bound->wrapping)
+ return unwrapped_guarded_poly_bound(bset, poly, user);
+
+ nparam = isl_space_dim(bound->dim, isl_dim_param);
+ n_in = isl_space_dim(bound->dim, isl_dim_in);
+ if (nparam < 0 || n_in < 0)
+ goto error;
+
+ space = isl_qpolynomial_get_domain_space(poly);
+ bset = isl_basic_set_reset_space(bset, space);
+
+ bset = isl_basic_set_move_dims(bset, isl_dim_param, nparam,
+ isl_dim_set, 0, n_in);
+ poly = isl_qpolynomial_move_dims(poly, isl_dim_param, nparam,
+ isl_dim_in, 0, n_in);
+
+ space = isl_basic_set_get_space(bset);
+ space = isl_space_params(space);
+
+ top_pwf = bound->pwf;
+ top_pwf_tight = bound->pwf_tight;
+
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, 1);
+ bound->pwf = isl_pw_qpolynomial_fold_zero(isl_space_copy(space),
+ bound->type);
+ bound->pwf_tight = isl_pw_qpolynomial_fold_zero(space, bound->type);
+
+ r = unwrapped_guarded_poly_bound(bset, poly, user);
+
+ bound->pwf = isl_pw_qpolynomial_fold_reset_space(bound->pwf,
+ isl_space_copy(bound->dim));
+ bound->pwf_tight = isl_pw_qpolynomial_fold_reset_space(bound->pwf_tight,
+ isl_space_copy(bound->dim));
+
+ isl_bound_add(bound, top_pwf);
+ isl_bound_add_tight(bound, top_pwf_tight);
+
+ return r;
+error:
+ isl_basic_set_free(bset);
+ isl_qpolynomial_free(poly);
+ return isl_stat_error;
+}
+
+static isl_stat guarded_qp(__isl_take isl_qpolynomial *qp, void *user)
+{
+ struct isl_bound *bound = (struct isl_bound *)user;
+ isl_stat r;
+
+ r = isl_qpolynomial_as_polynomial_on_domain(qp, bound->bset,
+ &guarded_poly_bound, user);
+ isl_qpolynomial_free(qp);
+ return r;
+}
+
+static isl_stat basic_guarded_fold(__isl_take isl_basic_set *bset, void *user)
+{
+ struct isl_bound *bound = (struct isl_bound *)user;
+ isl_stat r;
+
+ bound->bset = bset;
+ r = isl_qpolynomial_fold_foreach_qpolynomial(bound->fold,
+ &guarded_qp, user);
+ isl_basic_set_free(bset);
+ return r;
+}
+
+static isl_stat guarded_fold(__isl_take isl_set *set,
+ __isl_take isl_qpolynomial_fold *fold, void *user)
+{
+ struct isl_bound *bound = (struct isl_bound *)user;
+
+ if (!set || !fold)
+ goto error;
+
+ set = isl_set_make_disjoint(set);
+
+ bound->fold = fold;
+ bound->type = isl_qpolynomial_fold_get_type(fold);
+
+ if (isl_set_foreach_basic_set(set, &basic_guarded_fold, bound) < 0)
+ goto error;
+
+ isl_set_free(set);
+ isl_qpolynomial_fold_free(fold);
+
+ return isl_stat_ok;
+error:
+ isl_set_free(set);
+ isl_qpolynomial_fold_free(fold);
+ return isl_stat_error;
+}
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_bound(
+ __isl_take isl_pw_qpolynomial_fold *pwf, isl_bool *tight)
+{
+ isl_size nvar;
+ struct isl_bound bound;
+ isl_bool covers;
+
+ if (!pwf)
+ return NULL;
+
+ bound.dim = isl_pw_qpolynomial_fold_get_domain_space(pwf);
+
+ bound.wrapping = isl_space_is_wrapping(bound.dim);
+ if (bound.wrapping)
+ bound.dim = isl_space_unwrap(bound.dim);
+ nvar = isl_space_dim(bound.dim, isl_dim_out);
+ if (nvar < 0)
+ bound.dim = isl_space_free(bound.dim);
+ bound.dim = isl_space_domain(bound.dim);
+ bound.dim = isl_space_from_domain(bound.dim);
+ bound.dim = isl_space_add_dims(bound.dim, isl_dim_out, 1);
+
+ if (nvar == 0) {
+ if (tight)
+ *tight = isl_bool_true;
+ return isl_pw_qpolynomial_fold_reset_space(pwf, bound.dim);
+ }
+
+ if (isl_pw_qpolynomial_fold_is_zero(pwf)) {
+ enum isl_fold type = pwf->type;
+ isl_pw_qpolynomial_fold_free(pwf);
+ if (tight)
+ *tight = isl_bool_true;
+ return isl_pw_qpolynomial_fold_zero(bound.dim, type);
+ }
+
+ bound.pwf = isl_pw_qpolynomial_fold_zero(isl_space_copy(bound.dim),
+ pwf->type);
+ bound.pwf_tight = isl_pw_qpolynomial_fold_zero(isl_space_copy(bound.dim),
+ pwf->type);
+ bound.check_tight = !!tight;
+
+ if (isl_pw_qpolynomial_fold_foreach_lifted_piece(pwf,
+ guarded_fold, &bound) < 0)
+ goto error;
+
+ covers = isl_pw_qpolynomial_fold_covers(bound.pwf_tight, bound.pwf);
+ if (covers < 0)
+ goto error;
+
+ if (tight)
+ *tight = covers;
+
+ isl_space_free(bound.dim);
+ isl_pw_qpolynomial_fold_free(pwf);
+
+ if (covers) {
+ isl_pw_qpolynomial_fold_free(bound.pwf);
+ return bound.pwf_tight;
+ }
+
+ bound.pwf = isl_pw_qpolynomial_fold_fold(bound.pwf, bound.pwf_tight);
+
+ return bound.pwf;
+error:
+ isl_pw_qpolynomial_fold_free(bound.pwf_tight);
+ isl_pw_qpolynomial_fold_free(bound.pwf);
+ isl_pw_qpolynomial_fold_free(pwf);
+ isl_space_free(bound.dim);
+ return NULL;
+}
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_bound(
+ __isl_take isl_pw_qpolynomial *pwqp, enum isl_fold type,
+ isl_bool *tight)
+{
+ isl_pw_qpolynomial_fold *pwf;
+
+ pwf = isl_pw_qpolynomial_fold_from_pw_qpolynomial(type, pwqp);
+ return isl_pw_qpolynomial_fold_bound(pwf, tight);
+}
+
+struct isl_union_bound_data {
+ enum isl_fold type;
+ isl_bool tight;
+ isl_union_pw_qpolynomial_fold *res;
+};
+
+static isl_stat bound_pw(__isl_take isl_pw_qpolynomial *pwqp, void *user)
+{
+ struct isl_union_bound_data *data = user;
+ isl_pw_qpolynomial_fold *pwf;
+
+ pwf = isl_pw_qpolynomial_bound(pwqp, data->type,
+ data->tight ? &data->tight : NULL);
+ data->res = isl_union_pw_qpolynomial_fold_fold_pw_qpolynomial_fold(
+ data->res, pwf);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_bound(
+ __isl_take isl_union_pw_qpolynomial *upwqp,
+ enum isl_fold type, isl_bool *tight)
+{
+ isl_space *space;
+ struct isl_union_bound_data data = { type, 1, NULL };
+
+ if (!upwqp)
+ return NULL;
+
+ if (!tight)
+ data.tight = isl_bool_false;
+
+ space = isl_union_pw_qpolynomial_get_space(upwqp);
+ data.res = isl_union_pw_qpolynomial_fold_zero(space, type);
+ if (isl_union_pw_qpolynomial_foreach_pw_qpolynomial(upwqp,
+ &bound_pw, &data) < 0)
+ goto error;
+
+ isl_union_pw_qpolynomial_free(upwqp);
+ if (tight)
+ *tight = data.tight;
+
+ return data.res;
+error:
+ isl_union_pw_qpolynomial_free(upwqp);
+ isl_union_pw_qpolynomial_fold_free(data.res);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bound.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bound.h
new file mode 100644
index 00000000000..13a5813a77e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_bound.h
@@ -0,0 +1,29 @@
+#ifndef ISL_BOUND_H
+#define ISL_BOUND_H
+
+#include <isl/polynomial.h>
+
+struct isl_bound {
+ /* input */
+ int check_tight;
+ int wrapping;
+ enum isl_fold type;
+ isl_space *dim;
+ isl_basic_set *bset;
+ isl_qpolynomial_fold *fold;
+
+ /* output */
+ isl_pw_qpolynomial_fold *pwf;
+ isl_pw_qpolynomial_fold *pwf_tight;
+};
+
+__isl_give isl_pw_qpolynomial_fold *isl_qpolynomial_cst_bound(
+ __isl_take isl_basic_set *bset, __isl_take isl_qpolynomial *poly,
+ enum isl_fold type, isl_bool *tight);
+
+isl_stat isl_bound_add(struct isl_bound *bound,
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+isl_stat isl_bound_add_tight(struct isl_bound *bound,
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_box.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_box.c
new file mode 100644
index 00000000000..d4eb54f3ecb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_box.c
@@ -0,0 +1,504 @@
+/*
+ * Copyright 2010-2011 INRIA Saclay
+ * Copyright 2012-2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/val.h>
+#include <isl/space.h>
+#include <isl_map_private.h>
+#include <isl_aff_private.h>
+#include <isl/constraint.h>
+#include <isl/ilp.h>
+#include <isl/fixed_box.h>
+
+/* Representation of a box of fixed size containing the elements
+ * [offset, offset + size).
+ * "size" lives in the target space of "offset".
+ *
+ * If any of the "offsets" is NaN, then the object represents
+ * the failure of finding a fixed-size box.
+ */
+struct isl_fixed_box {
+ isl_multi_aff *offset;
+ isl_multi_val *size;
+};
+
+/* Free "box" and return NULL.
+ */
+__isl_null isl_fixed_box *isl_fixed_box_free(__isl_take isl_fixed_box *box)
+{
+ if (!box)
+ return NULL;
+ isl_multi_aff_free(box->offset);
+ isl_multi_val_free(box->size);
+ free(box);
+ return NULL;
+}
+
+/* Construct an isl_fixed_box with the given offset and size.
+ */
+static __isl_give isl_fixed_box *isl_fixed_box_alloc(
+ __isl_take isl_multi_aff *offset, __isl_take isl_multi_val *size)
+{
+ isl_ctx *ctx;
+ isl_fixed_box *box;
+
+ if (!offset || !size)
+ goto error;
+ ctx = isl_multi_aff_get_ctx(offset);
+ box = isl_alloc_type(ctx, struct isl_fixed_box);
+ if (!box)
+ goto error;
+ box->offset = offset;
+ box->size = size;
+
+ return box;
+error:
+ isl_multi_aff_free(offset);
+ isl_multi_val_free(size);
+ return NULL;
+}
+
+/* Construct an initial isl_fixed_box with zero offsets
+ * in the given space and zero corresponding sizes.
+ */
+static __isl_give isl_fixed_box *isl_fixed_box_init(
+ __isl_take isl_space *space)
+{
+ isl_multi_aff *offset;
+ isl_multi_val *size;
+
+ offset = isl_multi_aff_zero(isl_space_copy(space));
+ space = isl_space_drop_all_params(isl_space_range(space));
+ size = isl_multi_val_zero(space);
+ return isl_fixed_box_alloc(offset, size);
+}
+
+/* Return a copy of "box".
+ */
+__isl_give isl_fixed_box *isl_fixed_box_copy(__isl_keep isl_fixed_box *box)
+{
+ isl_multi_aff *offset;
+ isl_multi_val *size;
+
+ offset = isl_fixed_box_get_offset(box);
+ size = isl_fixed_box_get_size(box);
+ return isl_fixed_box_alloc(offset, size);
+}
+
+/* Replace the offset and size in direction "pos" by "offset" and "size"
+ * (without checking whether "box" is a valid box).
+ */
+static __isl_give isl_fixed_box *isl_fixed_box_set_extent(
+ __isl_take isl_fixed_box *box, int pos, __isl_keep isl_aff *offset,
+ __isl_keep isl_val *size)
+{
+ if (!box)
+ return NULL;
+ box->offset = isl_multi_aff_set_aff(box->offset, pos,
+ isl_aff_copy(offset));
+ box->size = isl_multi_val_set_val(box->size, pos, isl_val_copy(size));
+ if (!box->offset || !box->size)
+ return isl_fixed_box_free(box);
+ return box;
+}
+
+/* Replace the offset and size in direction "pos" by "offset" and "size",
+ * if "box" is a valid box.
+ */
+static __isl_give isl_fixed_box *isl_fixed_box_set_valid_extent(
+ __isl_take isl_fixed_box *box, int pos, __isl_keep isl_aff *offset,
+ __isl_keep isl_val *size)
+{
+ isl_bool valid;
+
+ valid = isl_fixed_box_is_valid(box);
+ if (valid < 0 || !valid)
+ return box;
+ return isl_fixed_box_set_extent(box, pos, offset, size);
+}
+
+/* Replace "box" by an invalid box, by setting all offsets to NaN
+ * (and all sizes to infinity).
+ */
+static __isl_give isl_fixed_box *isl_fixed_box_invalidate(
+ __isl_take isl_fixed_box *box)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_val *infty;
+ isl_aff *nan;
+
+ if (!box)
+ return NULL;
+ n = isl_multi_val_dim(box->size, isl_dim_set);
+ if (n < 0)
+ return isl_fixed_box_free(box);
+
+ infty = isl_val_infty(isl_fixed_box_get_ctx(box));
+ space = isl_space_domain(isl_fixed_box_get_space(box));
+ nan = isl_aff_nan_on_domain(isl_local_space_from_space(space));
+ for (i = 0; i < n; ++i)
+ box = isl_fixed_box_set_extent(box, i, nan, infty);
+ isl_aff_free(nan);
+ isl_val_free(infty);
+
+ if (!box->offset || !box->size)
+ return isl_fixed_box_free(box);
+ return box;
+}
+
+/* Project the domain of the fixed box onto its parameter space.
+ * In particular, project out the domain of the offset.
+ */
+static __isl_give isl_fixed_box *isl_fixed_box_project_domain_on_params(
+ __isl_take isl_fixed_box *box)
+{
+ isl_bool valid;
+
+ valid = isl_fixed_box_is_valid(box);
+ if (valid < 0)
+ return isl_fixed_box_free(box);
+ if (!valid)
+ return box;
+
+ box->offset = isl_multi_aff_project_domain_on_params(box->offset);
+ if (!box->offset)
+ return isl_fixed_box_free(box);
+
+ return box;
+}
+
+/* Return the isl_ctx to which "box" belongs.
+ */
+isl_ctx *isl_fixed_box_get_ctx(__isl_keep isl_fixed_box *box)
+{
+ if (!box)
+ return NULL;
+ return isl_multi_aff_get_ctx(box->offset);
+}
+
+/* Return the space in which "box" lives.
+ */
+__isl_give isl_space *isl_fixed_box_get_space(__isl_keep isl_fixed_box *box)
+{
+ if (!box)
+ return NULL;
+ return isl_multi_aff_get_space(box->offset);
+}
+
+/* Does "box" contain valid information?
+ */
+isl_bool isl_fixed_box_is_valid(__isl_keep isl_fixed_box *box)
+{
+ if (!box)
+ return isl_bool_error;
+ return isl_bool_not(isl_multi_aff_involves_nan(box->offset));
+}
+
+/* Return the offsets of the box "box".
+ */
+__isl_give isl_multi_aff *isl_fixed_box_get_offset(
+ __isl_keep isl_fixed_box *box)
+{
+ if (!box)
+ return NULL;
+ return isl_multi_aff_copy(box->offset);
+}
+
+/* Return the sizes of the box "box".
+ */
+__isl_give isl_multi_val *isl_fixed_box_get_size(__isl_keep isl_fixed_box *box)
+{
+ if (!box)
+ return NULL;
+ return isl_multi_val_copy(box->size);
+}
+
+/* Data used in set_dim_extent and compute_size_in_direction.
+ *
+ * "bset" is a wrapped copy of the basic map that has the selected
+ * output dimension as range.
+ * "pos" is the position of the variable representing the output dimension,
+ * i.e., the variable for which the size should be computed. This variable
+ * is also the last variable in "bset".
+ * "size" is the best size found so far
+ * (infinity if no offset was found so far).
+ * "offset" is the offset corresponding to the best size
+ * (NULL if no offset was found so far).
+ */
+struct isl_size_info {
+ isl_basic_set *bset;
+ isl_size pos;
+ isl_val *size;
+ isl_aff *offset;
+};
+
+/* Is "c" a suitable bound on dimension "pos" for use as a lower bound
+ * of a fixed-size range.
+ * In particular, it needs to be a lower bound on "pos".
+ * In order for the final offset not to be too complicated,
+ * the constraint itself should also not involve any integer divisions.
+ */
+static isl_bool is_suitable_bound(__isl_keep isl_constraint *c, unsigned pos)
+{
+ isl_size n_div;
+ isl_bool is_bound, any_divs;
+
+ is_bound = isl_constraint_is_lower_bound(c, isl_dim_set, pos);
+ if (is_bound < 0 || !is_bound)
+ return is_bound;
+
+ n_div = isl_constraint_dim(c, isl_dim_div);
+ if (n_div < 0)
+ return isl_bool_error;
+ any_divs = isl_constraint_involves_dims(c, isl_dim_div, 0, n_div);
+ return isl_bool_not(any_divs);
+}
+
+/* Given a constraint from the basic set describing the bounds on
+ * an array index, check if it is a lower bound, say m i >= b(x), and,
+ * if so, check whether the expression "i - ceil(b(x)/m) + 1" has a constant
+ * upper bound. If so, and if this bound is smaller than any bound
+ * derived from earlier constraints, set the size to this bound on
+ * the expression and the lower bound to ceil(b(x)/m).
+ */
+static isl_stat compute_size_in_direction(__isl_take isl_constraint *c,
+ void *user)
+{
+ struct isl_size_info *info = user;
+ isl_val *v;
+ isl_aff *aff;
+ isl_aff *lb;
+ isl_bool is_bound, better;
+
+ is_bound = is_suitable_bound(c, info->pos);
+ if (is_bound < 0 || !is_bound) {
+ isl_constraint_free(c);
+ return is_bound < 0 ? isl_stat_error : isl_stat_ok;
+ }
+
+ aff = isl_constraint_get_bound(c, isl_dim_set, info->pos);
+ aff = isl_aff_ceil(aff);
+
+ lb = isl_aff_copy(aff);
+
+ aff = isl_aff_neg(aff);
+ aff = isl_aff_add_coefficient_si(aff, isl_dim_in, info->pos, 1);
+
+ v = isl_basic_set_max_val(info->bset, aff);
+ isl_aff_free(aff);
+
+ v = isl_val_add_ui(v, 1);
+ better = isl_val_lt(v, info->size);
+ if (better >= 0 && better) {
+ isl_val_free(info->size);
+ info->size = isl_val_copy(v);
+ lb = isl_aff_domain_factor_domain(lb);
+ isl_aff_free(info->offset);
+ info->offset = isl_aff_copy(lb);
+ }
+ isl_val_free(v);
+ isl_aff_free(lb);
+
+ isl_constraint_free(c);
+
+ return better < 0 ? isl_stat_error : isl_stat_ok;
+}
+
+/* Look for a fixed-size range of values for the output dimension "pos"
+ * of "map", by looking for a lower-bound expression in the parameters
+ * and input dimensions such that the range of the output dimension
+ * is a constant shifted by this expression.
+ *
+ * In particular, look through the explicit lower bounds on the output dimension
+ * for candidate expressions and pick the one that results in the smallest size.
+ * Initialize the size with infinity and if no better size is found
+ * then invalidate the box. Otherwise, set the offset and size
+ * in the given direction by those that correspond to the smallest size.
+ *
+ * Note that while evaluating the size corresponding to a lower bound,
+ * an affine expression is constructed from the lower bound.
+ * This lower bound may therefore not have any unknown local variables.
+ * Eliminate any unknown local variables up front.
+ * No such restriction needs to be imposed on the set over which
+ * the size is computed.
+ */
+static __isl_give isl_fixed_box *set_dim_extent(__isl_take isl_fixed_box *box,
+ __isl_keep isl_map *map, int pos)
+{
+ struct isl_size_info info;
+ isl_bool valid;
+ isl_ctx *ctx;
+ isl_basic_set *bset;
+
+ if (!box || !map)
+ return isl_fixed_box_free(box);
+
+ ctx = isl_map_get_ctx(map);
+ map = isl_map_copy(map);
+ map = isl_map_project_onto(map, isl_dim_out, pos, 1);
+ info.size = isl_val_infty(ctx);
+ info.offset = NULL;
+ info.pos = isl_map_dim(map, isl_dim_in);
+ info.bset = isl_basic_map_wrap(isl_map_simple_hull(map));
+ bset = isl_basic_set_copy(info.bset);
+ bset = isl_basic_set_remove_unknown_divs(bset);
+ if (info.pos < 0)
+ bset = isl_basic_set_free(bset);
+ if (isl_basic_set_foreach_constraint(bset,
+ &compute_size_in_direction, &info) < 0)
+ box = isl_fixed_box_free(box);
+ isl_basic_set_free(bset);
+ valid = isl_val_is_int(info.size);
+ if (valid < 0)
+ box = isl_fixed_box_free(box);
+ else if (valid)
+ box = isl_fixed_box_set_valid_extent(box, pos,
+ info.offset, info.size);
+ else
+ box = isl_fixed_box_invalidate(box);
+ isl_val_free(info.size);
+ isl_aff_free(info.offset);
+ isl_basic_set_free(info.bset);
+
+ return box;
+}
+
+/* Try and construct a fixed-size rectangular box with an offset
+ * in terms of the domain of "map" that contains the range of "map".
+ * If no such box can be constructed, then return an invalidated box,
+ * i.e., one where isl_fixed_box_is_valid returns false.
+ *
+ * Iterate over the dimensions in the range
+ * setting the corresponding offset and extent.
+ */
+__isl_give isl_fixed_box *isl_map_get_range_simple_fixed_box_hull(
+ __isl_keep isl_map *map)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_fixed_box *box;
+
+ n = isl_map_dim(map, isl_dim_out);
+ if (n < 0)
+ return NULL;
+ space = isl_map_get_space(map);
+ box = isl_fixed_box_init(space);
+
+ map = isl_map_detect_equalities(isl_map_copy(map));
+ for (i = 0; i < n; ++i) {
+ isl_bool valid;
+
+ box = set_dim_extent(box, map, i);
+ valid = isl_fixed_box_is_valid(box);
+ if (valid < 0 || !valid)
+ break;
+ }
+ isl_map_free(map);
+
+ return box;
+}
+
+/* Try and construct a fixed-size rectangular box with an offset
+ * in terms of the parameters of "set" that contains "set".
+ * If no such box can be constructed, then return an invalidated box,
+ * i.e., one where isl_fixed_box_is_valid returns false.
+ *
+ * Compute the box using isl_map_get_range_simple_fixed_box_hull
+ * by constructing a map from the set and
+ * project out the domain again from the result.
+ */
+__isl_give isl_fixed_box *isl_set_get_simple_fixed_box_hull(
+ __isl_keep isl_set *set)
+{
+ isl_map *map;
+ isl_fixed_box *box;
+
+ map = isl_map_from_range(isl_set_copy(set));
+ box = isl_map_get_range_simple_fixed_box_hull(map);
+ isl_map_free(map);
+ box = isl_fixed_box_project_domain_on_params(box);
+
+ return box;
+}
+
+/* Check whether the output elements lie on a rectangular lattice,
+ * possibly depending on the parameters and the input dimensions.
+ * Return a tile in this lattice.
+ * If no stride information can be found, then return a tile of size 1
+ * (and offset 0).
+ *
+ * Obtain stride information in each output dimension separately and
+ * combine the results.
+ */
+__isl_give isl_fixed_box *isl_map_get_range_lattice_tile(
+ __isl_keep isl_map *map)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_fixed_box *box;
+
+ n = isl_map_dim(map, isl_dim_out);
+ if (n < 0)
+ return NULL;
+ space = isl_map_get_space(map);
+ box = isl_fixed_box_init(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_val *stride;
+ isl_aff *offset;
+ isl_stride_info *si;
+
+ si = isl_map_get_range_stride_info(map, i);
+ stride = isl_stride_info_get_stride(si);
+ offset = isl_stride_info_get_offset(si);
+ isl_stride_info_free(si);
+
+ box = isl_fixed_box_set_valid_extent(box, i, offset, stride);
+
+ isl_aff_free(offset);
+ isl_val_free(stride);
+ }
+
+ return box;
+}
+
+#undef BASE
+#define BASE multi_val
+#include "print_yaml_field_templ.c"
+
+#undef BASE
+#define BASE multi_aff
+#include "print_yaml_field_templ.c"
+
+/* Print the information contained in "box" to "p".
+ * The information is printed as a YAML document.
+ */
+__isl_give isl_printer *isl_printer_print_fixed_box(
+ __isl_take isl_printer *p, __isl_keep isl_fixed_box *box)
+{
+ if (!box)
+ return isl_printer_free(p);
+
+ p = isl_printer_yaml_start_mapping(p);
+ p = print_yaml_field_multi_aff(p, "offset", box->offset);
+ p = print_yaml_field_multi_val(p, "size", box->size);
+ p = isl_printer_yaml_end_mapping(p);
+
+ return p;
+}
+
+#undef BASE
+#define BASE fixed_box
+#include <print_templ_yaml.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_check_named_params_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_check_named_params_templ.c
new file mode 100644
index 00000000000..f900768ec47
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_check_named_params_templ.c
@@ -0,0 +1,10 @@
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Check that "obj" has only named parameters, reporting an error
+ * if it does not.
+ */
+isl_stat FN(TYPE,check_named_params)(__isl_keep TYPE *obj)
+{
+ return isl_space_check_named_params(FN(TYPE,peek_space)(obj));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_coalesce.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_coalesce.c
new file mode 100644
index 00000000000..910c2146bd5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_coalesce.c
@@ -0,0 +1,4262 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2012-2013 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ * Copyright 2016 INRIA Paris
+ * Copyright 2020 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ * and Centre de Recherche Inria de Paris, 2 rue Simone Iff - Voie DQ12,
+ * CS 42112, 75589 Paris Cedex 12, France
+ * and Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_ctx_private.h>
+#include "isl_map_private.h"
+#include <isl_seq.h>
+#include <isl/options.h>
+#include "isl_tab.h"
+#include <isl_mat_private.h>
+#include <isl_local_space_private.h>
+#include <isl_val_private.h>
+#include <isl_vec_private.h>
+#include <isl_aff_private.h>
+#include <isl_equalities.h>
+#include <isl_constraint_private.h>
+
+#include <set_to_map.c>
+#include <set_from_map.c>
+
+#define STATUS_ERROR -1
+#define STATUS_REDUNDANT 1
+#define STATUS_VALID 2
+#define STATUS_SEPARATE 3
+#define STATUS_CUT 4
+#define STATUS_ADJ_EQ 5
+#define STATUS_ADJ_INEQ 6
+
+static int status_in(isl_int *ineq, struct isl_tab *tab)
+{
+ enum isl_ineq_type type = isl_tab_ineq_type(tab, ineq);
+ switch (type) {
+ default:
+ case isl_ineq_error: return STATUS_ERROR;
+ case isl_ineq_redundant: return STATUS_VALID;
+ case isl_ineq_separate: return STATUS_SEPARATE;
+ case isl_ineq_cut: return STATUS_CUT;
+ case isl_ineq_adj_eq: return STATUS_ADJ_EQ;
+ case isl_ineq_adj_ineq: return STATUS_ADJ_INEQ;
+ }
+}
+
+/* Compute the position of the equalities of basic map "bmap_i"
+ * with respect to the basic map represented by "tab_j".
+ * The resulting array has twice as many entries as the number
+ * of equalities corresponding to the two inequalities to which
+ * each equality corresponds.
+ */
+static int *eq_status_in(__isl_keep isl_basic_map *bmap_i,
+ struct isl_tab *tab_j)
+{
+ int k, l;
+ int *eq;
+ isl_size dim;
+
+ dim = isl_basic_map_dim(bmap_i, isl_dim_all);
+ if (dim < 0)
+ return NULL;
+
+ eq = isl_calloc_array(bmap_i->ctx, int, 2 * bmap_i->n_eq);
+ if (!eq)
+ return NULL;
+
+ for (k = 0; k < bmap_i->n_eq; ++k) {
+ for (l = 0; l < 2; ++l) {
+ isl_seq_neg(bmap_i->eq[k], bmap_i->eq[k], 1+dim);
+ eq[2 * k + l] = status_in(bmap_i->eq[k], tab_j);
+ if (eq[2 * k + l] == STATUS_ERROR)
+ goto error;
+ }
+ }
+
+ return eq;
+error:
+ free(eq);
+ return NULL;
+}
+
+/* Compute the position of the inequalities of basic map "bmap_i"
+ * (also represented by "tab_i", if not NULL) with respect to the basic map
+ * represented by "tab_j".
+ */
+static int *ineq_status_in(__isl_keep isl_basic_map *bmap_i,
+ struct isl_tab *tab_i, struct isl_tab *tab_j)
+{
+ int k;
+ unsigned n_eq = bmap_i->n_eq;
+ int *ineq = isl_calloc_array(bmap_i->ctx, int, bmap_i->n_ineq);
+
+ if (!ineq)
+ return NULL;
+
+ for (k = 0; k < bmap_i->n_ineq; ++k) {
+ if (tab_i && isl_tab_is_redundant(tab_i, n_eq + k)) {
+ ineq[k] = STATUS_REDUNDANT;
+ continue;
+ }
+ ineq[k] = status_in(bmap_i->ineq[k], tab_j);
+ if (ineq[k] == STATUS_ERROR)
+ goto error;
+ if (ineq[k] == STATUS_SEPARATE)
+ break;
+ }
+
+ return ineq;
+error:
+ free(ineq);
+ return NULL;
+}
+
+static int any(int *con, unsigned len, int status)
+{
+ int i;
+
+ for (i = 0; i < len ; ++i)
+ if (con[i] == status)
+ return 1;
+ return 0;
+}
+
+/* Return the first position of "status" in the list "con" of length "len".
+ * Return -1 if there is no such entry.
+ */
+static int find(int *con, unsigned len, int status)
+{
+ int i;
+
+ for (i = 0; i < len ; ++i)
+ if (con[i] == status)
+ return i;
+ return -1;
+}
+
+static int count(int *con, unsigned len, int status)
+{
+ int i;
+ int c = 0;
+
+ for (i = 0; i < len ; ++i)
+ if (con[i] == status)
+ c++;
+ return c;
+}
+
+static int all(int *con, unsigned len, int status)
+{
+ int i;
+
+ for (i = 0; i < len ; ++i) {
+ if (con[i] == STATUS_REDUNDANT)
+ continue;
+ if (con[i] != status)
+ return 0;
+ }
+ return 1;
+}
+
+/* Internal information associated to a basic map in a map
+ * that is to be coalesced by isl_map_coalesce.
+ *
+ * "bmap" is the basic map itself (or NULL if "removed" is set)
+ * "tab" is the corresponding tableau (or NULL if "removed" is set)
+ * "hull_hash" identifies the affine space in which "bmap" lives.
+ * "modified" is set if this basic map may not be identical
+ * to any of the basic maps in the input.
+ * "removed" is set if this basic map has been removed from the map
+ * "simplify" is set if this basic map may have some unknown integer
+ * divisions that were not present in the input basic maps. The basic
+ * map should then be simplified such that we may be able to find
+ * a definition among the constraints.
+ *
+ * "eq" and "ineq" are only set if we are currently trying to coalesce
+ * this basic map with another basic map, in which case they represent
+ * the position of the inequalities of this basic map with respect to
+ * the other basic map. The number of elements in the "eq" array
+ * is twice the number of equalities in the "bmap", corresponding
+ * to the two inequalities that make up each equality.
+ */
+struct isl_coalesce_info {
+ isl_basic_map *bmap;
+ struct isl_tab *tab;
+ uint32_t hull_hash;
+ int modified;
+ int removed;
+ int simplify;
+ int *eq;
+ int *ineq;
+};
+
+/* Is there any (half of an) equality constraint in the description
+ * of the basic map represented by "info" that
+ * has position "status" with respect to the other basic map?
+ */
+static int any_eq(struct isl_coalesce_info *info, int status)
+{
+ isl_size n_eq;
+
+ n_eq = isl_basic_map_n_equality(info->bmap);
+ return any(info->eq, 2 * n_eq, status);
+}
+
+/* Is there any inequality constraint in the description
+ * of the basic map represented by "info" that
+ * has position "status" with respect to the other basic map?
+ */
+static int any_ineq(struct isl_coalesce_info *info, int status)
+{
+ isl_size n_ineq;
+
+ n_ineq = isl_basic_map_n_inequality(info->bmap);
+ return any(info->ineq, n_ineq, status);
+}
+
+/* Return the position of the first half on an equality constraint
+ * in the description of the basic map represented by "info" that
+ * has position "status" with respect to the other basic map.
+ * The returned value is twice the position of the equality constraint
+ * plus zero for the negative half and plus one for the positive half.
+ * Return -1 if there is no such entry.
+ */
+static int find_eq(struct isl_coalesce_info *info, int status)
+{
+ isl_size n_eq;
+
+ n_eq = isl_basic_map_n_equality(info->bmap);
+ return find(info->eq, 2 * n_eq, status);
+}
+
+/* Return the position of the first inequality constraint in the description
+ * of the basic map represented by "info" that
+ * has position "status" with respect to the other basic map.
+ * Return -1 if there is no such entry.
+ */
+static int find_ineq(struct isl_coalesce_info *info, int status)
+{
+ isl_size n_ineq;
+
+ n_ineq = isl_basic_map_n_inequality(info->bmap);
+ return find(info->ineq, n_ineq, status);
+}
+
+/* Return the number of (halves of) equality constraints in the description
+ * of the basic map represented by "info" that
+ * have position "status" with respect to the other basic map.
+ */
+static int count_eq(struct isl_coalesce_info *info, int status)
+{
+ isl_size n_eq;
+
+ n_eq = isl_basic_map_n_equality(info->bmap);
+ return count(info->eq, 2 * n_eq, status);
+}
+
+/* Return the number of inequality constraints in the description
+ * of the basic map represented by "info" that
+ * have position "status" with respect to the other basic map.
+ */
+static int count_ineq(struct isl_coalesce_info *info, int status)
+{
+ isl_size n_ineq;
+
+ n_ineq = isl_basic_map_n_inequality(info->bmap);
+ return count(info->ineq, n_ineq, status);
+}
+
+/* Are all non-redundant constraints of the basic map represented by "info"
+ * either valid or cut constraints with respect to the other basic map?
+ */
+static int all_valid_or_cut(struct isl_coalesce_info *info)
+{
+ int i;
+
+ for (i = 0; i < 2 * info->bmap->n_eq; ++i) {
+ if (info->eq[i] == STATUS_REDUNDANT)
+ continue;
+ if (info->eq[i] == STATUS_VALID)
+ continue;
+ if (info->eq[i] == STATUS_CUT)
+ continue;
+ return 0;
+ }
+
+ for (i = 0; i < info->bmap->n_ineq; ++i) {
+ if (info->ineq[i] == STATUS_REDUNDANT)
+ continue;
+ if (info->ineq[i] == STATUS_VALID)
+ continue;
+ if (info->ineq[i] == STATUS_CUT)
+ continue;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Compute the hash of the (apparent) affine hull of info->bmap (with
+ * the existentially quantified variables removed) and store it
+ * in info->hash.
+ */
+static int coalesce_info_set_hull_hash(struct isl_coalesce_info *info)
+{
+ isl_basic_map *hull;
+ isl_size n_div;
+
+ hull = isl_basic_map_copy(info->bmap);
+ hull = isl_basic_map_plain_affine_hull(hull);
+ n_div = isl_basic_map_dim(hull, isl_dim_div);
+ if (n_div < 0)
+ hull = isl_basic_map_free(hull);
+ hull = isl_basic_map_drop_constraints_involving_dims(hull,
+ isl_dim_div, 0, n_div);
+ info->hull_hash = isl_basic_map_get_hash(hull);
+ isl_basic_map_free(hull);
+
+ return hull ? 0 : -1;
+}
+
+/* Free all the allocated memory in an array
+ * of "n" isl_coalesce_info elements.
+ */
+static void clear_coalesce_info(int n, struct isl_coalesce_info *info)
+{
+ int i;
+
+ if (!info)
+ return;
+
+ for (i = 0; i < n; ++i) {
+ isl_basic_map_free(info[i].bmap);
+ isl_tab_free(info[i].tab);
+ }
+
+ free(info);
+}
+
+/* Clear the memory associated to "info".
+ */
+static void clear(struct isl_coalesce_info *info)
+{
+ info->bmap = isl_basic_map_free(info->bmap);
+ isl_tab_free(info->tab);
+ info->tab = NULL;
+}
+
+/* Drop the basic map represented by "info".
+ * That is, clear the memory associated to the entry and
+ * mark it as having been removed.
+ */
+static void drop(struct isl_coalesce_info *info)
+{
+ clear(info);
+ info->removed = 1;
+}
+
+/* Exchange the information in "info1" with that in "info2".
+ */
+static void exchange(struct isl_coalesce_info *info1,
+ struct isl_coalesce_info *info2)
+{
+ struct isl_coalesce_info info;
+
+ info = *info1;
+ *info1 = *info2;
+ *info2 = info;
+}
+
+/* This type represents the kind of change that has been performed
+ * while trying to coalesce two basic maps.
+ *
+ * isl_change_none: nothing was changed
+ * isl_change_drop_first: the first basic map was removed
+ * isl_change_drop_second: the second basic map was removed
+ * isl_change_fuse: the two basic maps were replaced by a new basic map.
+ */
+enum isl_change {
+ isl_change_error = -1,
+ isl_change_none = 0,
+ isl_change_drop_first,
+ isl_change_drop_second,
+ isl_change_fuse,
+};
+
+/* Update "change" based on an interchange of the first and the second
+ * basic map. That is, interchange isl_change_drop_first and
+ * isl_change_drop_second.
+ */
+static enum isl_change invert_change(enum isl_change change)
+{
+ switch (change) {
+ case isl_change_error:
+ return isl_change_error;
+ case isl_change_none:
+ return isl_change_none;
+ case isl_change_drop_first:
+ return isl_change_drop_second;
+ case isl_change_drop_second:
+ return isl_change_drop_first;
+ case isl_change_fuse:
+ return isl_change_fuse;
+ }
+
+ return isl_change_error;
+}
+
+/* Add the valid constraints of the basic map represented by "info"
+ * to "bmap". "len" is the size of the constraints.
+ * If only one of the pair of inequalities that make up an equality
+ * is valid, then add that inequality.
+ */
+static __isl_give isl_basic_map *add_valid_constraints(
+ __isl_take isl_basic_map *bmap, struct isl_coalesce_info *info,
+ unsigned len)
+{
+ int k, l;
+
+ if (!bmap)
+ return NULL;
+
+ for (k = 0; k < info->bmap->n_eq; ++k) {
+ if (info->eq[2 * k] == STATUS_VALID &&
+ info->eq[2 * k + 1] == STATUS_VALID) {
+ l = isl_basic_map_alloc_equality(bmap);
+ if (l < 0)
+ return isl_basic_map_free(bmap);
+ isl_seq_cpy(bmap->eq[l], info->bmap->eq[k], len);
+ } else if (info->eq[2 * k] == STATUS_VALID) {
+ l = isl_basic_map_alloc_inequality(bmap);
+ if (l < 0)
+ return isl_basic_map_free(bmap);
+ isl_seq_neg(bmap->ineq[l], info->bmap->eq[k], len);
+ } else if (info->eq[2 * k + 1] == STATUS_VALID) {
+ l = isl_basic_map_alloc_inequality(bmap);
+ if (l < 0)
+ return isl_basic_map_free(bmap);
+ isl_seq_cpy(bmap->ineq[l], info->bmap->eq[k], len);
+ }
+ }
+
+ for (k = 0; k < info->bmap->n_ineq; ++k) {
+ if (info->ineq[k] != STATUS_VALID)
+ continue;
+ l = isl_basic_map_alloc_inequality(bmap);
+ if (l < 0)
+ return isl_basic_map_free(bmap);
+ isl_seq_cpy(bmap->ineq[l], info->bmap->ineq[k], len);
+ }
+
+ return bmap;
+}
+
+/* Is "bmap" defined by a number of (non-redundant) constraints that
+ * is greater than the number of constraints of basic maps i and j combined?
+ * Equalities are counted as two inequalities.
+ */
+static int number_of_constraints_increases(int i, int j,
+ struct isl_coalesce_info *info,
+ __isl_keep isl_basic_map *bmap, struct isl_tab *tab)
+{
+ int k, n_old, n_new;
+
+ n_old = 2 * info[i].bmap->n_eq + info[i].bmap->n_ineq;
+ n_old += 2 * info[j].bmap->n_eq + info[j].bmap->n_ineq;
+
+ n_new = 2 * bmap->n_eq;
+ for (k = 0; k < bmap->n_ineq; ++k)
+ if (!isl_tab_is_redundant(tab, bmap->n_eq + k))
+ ++n_new;
+
+ return n_new > n_old;
+}
+
+/* Replace the pair of basic maps i and j by the basic map bounded
+ * by the valid constraints in both basic maps and the constraints
+ * in extra (if not NULL).
+ * Place the fused basic map in the position that is the smallest of i and j.
+ *
+ * If "detect_equalities" is set, then look for equalities encoded
+ * as pairs of inequalities.
+ * If "check_number" is set, then the original basic maps are only
+ * replaced if the total number of constraints does not increase.
+ * While the number of integer divisions in the two basic maps
+ * is assumed to be the same, the actual definitions may be different.
+ * We only copy the definition from one of the basic maps if it is
+ * the same as that of the other basic map. Otherwise, we mark
+ * the integer division as unknown and simplify the basic map
+ * in an attempt to recover the integer division definition.
+ * If any extra constraints get introduced, then these may
+ * involve integer divisions with a unit coefficient.
+ * Eliminate those that do not appear with any other coefficient
+ * in other constraints, to ensure they get eliminated completely,
+ * improving the chances of further coalescing.
+ */
+static enum isl_change fuse(int i, int j, struct isl_coalesce_info *info,
+ __isl_keep isl_mat *extra, int detect_equalities, int check_number)
+{
+ int k, l;
+ struct isl_basic_map *fused = NULL;
+ struct isl_tab *fused_tab = NULL;
+ isl_size total = isl_basic_map_dim(info[i].bmap, isl_dim_all);
+ unsigned extra_rows = extra ? extra->n_row : 0;
+ unsigned n_eq, n_ineq;
+ int simplify = 0;
+
+ if (total < 0)
+ return isl_change_error;
+ if (j < i)
+ return fuse(j, i, info, extra, detect_equalities, check_number);
+
+ n_eq = info[i].bmap->n_eq + info[j].bmap->n_eq;
+ n_ineq = info[i].bmap->n_ineq + info[j].bmap->n_ineq;
+ fused = isl_basic_map_alloc_space(isl_space_copy(info[i].bmap->dim),
+ info[i].bmap->n_div, n_eq, n_eq + n_ineq + extra_rows);
+ fused = add_valid_constraints(fused, &info[i], 1 + total);
+ fused = add_valid_constraints(fused, &info[j], 1 + total);
+ if (!fused)
+ goto error;
+ if (ISL_F_ISSET(info[i].bmap, ISL_BASIC_MAP_RATIONAL) &&
+ ISL_F_ISSET(info[j].bmap, ISL_BASIC_MAP_RATIONAL))
+ ISL_F_SET(fused, ISL_BASIC_MAP_RATIONAL);
+
+ for (k = 0; k < info[i].bmap->n_div; ++k) {
+ int l = isl_basic_map_alloc_div(fused);
+ if (l < 0)
+ goto error;
+ if (isl_seq_eq(info[i].bmap->div[k], info[j].bmap->div[k],
+ 1 + 1 + total)) {
+ isl_seq_cpy(fused->div[l], info[i].bmap->div[k],
+ 1 + 1 + total);
+ } else {
+ isl_int_set_si(fused->div[l][0], 0);
+ simplify = 1;
+ }
+ }
+
+ for (k = 0; k < extra_rows; ++k) {
+ l = isl_basic_map_alloc_inequality(fused);
+ if (l < 0)
+ goto error;
+ isl_seq_cpy(fused->ineq[l], extra->row[k], 1 + total);
+ }
+
+ if (detect_equalities)
+ fused = isl_basic_map_detect_inequality_pairs(fused, NULL);
+ fused = isl_basic_map_gauss(fused, NULL);
+ if (simplify || info[j].simplify) {
+ fused = isl_basic_map_simplify(fused);
+ info[i].simplify = 0;
+ } else if (extra_rows > 0) {
+ fused = isl_basic_map_eliminate_pure_unit_divs(fused);
+ }
+ fused = isl_basic_map_finalize(fused);
+
+ fused_tab = isl_tab_from_basic_map(fused, 0);
+ if (isl_tab_detect_redundant(fused_tab) < 0)
+ goto error;
+
+ if (check_number &&
+ number_of_constraints_increases(i, j, info, fused, fused_tab)) {
+ isl_tab_free(fused_tab);
+ isl_basic_map_free(fused);
+ return isl_change_none;
+ }
+
+ clear(&info[i]);
+ info[i].bmap = fused;
+ info[i].tab = fused_tab;
+ info[i].modified = 1;
+ drop(&info[j]);
+
+ return isl_change_fuse;
+error:
+ isl_tab_free(fused_tab);
+ isl_basic_map_free(fused);
+ return isl_change_error;
+}
+
+/* Given a pair of basic maps i and j such that all constraints are either
+ * "valid" or "cut", check if the facets corresponding to the "cut"
+ * constraints of i lie entirely within basic map j.
+ * If so, replace the pair by the basic map consisting of the valid
+ * constraints in both basic maps.
+ * Checking whether the facet lies entirely within basic map j
+ * is performed by checking whether the constraints of basic map j
+ * are valid for the facet. These tests are performed on a rational
+ * tableau to avoid the theoretical possibility that a constraint
+ * that was considered to be a cut constraint for the entire basic map i
+ * happens to be considered to be a valid constraint for the facet,
+ * even though it cuts off the same rational points.
+ *
+ * To see that we are not introducing any extra points, call the
+ * two basic maps A and B and the resulting map U and let x
+ * be an element of U \setminus ( A \cup B ).
+ * A line connecting x with an element of A \cup B meets a facet F
+ * of either A or B. Assume it is a facet of B and let c_1 be
+ * the corresponding facet constraint. We have c_1(x) < 0 and
+ * so c_1 is a cut constraint. This implies that there is some
+ * (possibly rational) point x' satisfying the constraints of A
+ * and the opposite of c_1 as otherwise c_1 would have been marked
+ * valid for A. The line connecting x and x' meets a facet of A
+ * in a (possibly rational) point that also violates c_1, but this
+ * is impossible since all cut constraints of B are valid for all
+ * cut facets of A.
+ * In case F is a facet of A rather than B, then we can apply the
+ * above reasoning to find a facet of B separating x from A \cup B first.
+ */
+static enum isl_change check_facets(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ int k, l;
+ struct isl_tab_undo *snap, *snap2;
+ unsigned n_eq = info[i].bmap->n_eq;
+
+ snap = isl_tab_snap(info[i].tab);
+ if (isl_tab_mark_rational(info[i].tab) < 0)
+ return isl_change_error;
+ snap2 = isl_tab_snap(info[i].tab);
+
+ for (k = 0; k < info[i].bmap->n_ineq; ++k) {
+ if (info[i].ineq[k] != STATUS_CUT)
+ continue;
+ if (isl_tab_select_facet(info[i].tab, n_eq + k) < 0)
+ return isl_change_error;
+ for (l = 0; l < info[j].bmap->n_ineq; ++l) {
+ int stat;
+ if (info[j].ineq[l] != STATUS_CUT)
+ continue;
+ stat = status_in(info[j].bmap->ineq[l], info[i].tab);
+ if (stat < 0)
+ return isl_change_error;
+ if (stat != STATUS_VALID)
+ break;
+ }
+ if (isl_tab_rollback(info[i].tab, snap2) < 0)
+ return isl_change_error;
+ if (l < info[j].bmap->n_ineq)
+ break;
+ }
+
+ if (k < info[i].bmap->n_ineq) {
+ if (isl_tab_rollback(info[i].tab, snap) < 0)
+ return isl_change_error;
+ return isl_change_none;
+ }
+ return fuse(i, j, info, NULL, 0, 0);
+}
+
+/* Check if info->bmap contains the basic map represented
+ * by the tableau "tab".
+ * For each equality, we check both the constraint itself
+ * (as an inequality) and its negation. Make sure the
+ * equality is returned to its original state before returning.
+ */
+static isl_bool contains(struct isl_coalesce_info *info, struct isl_tab *tab)
+{
+ int k;
+ isl_size dim;
+ isl_basic_map *bmap = info->bmap;
+
+ dim = isl_basic_map_dim(bmap, isl_dim_all);
+ if (dim < 0)
+ return isl_bool_error;
+ for (k = 0; k < bmap->n_eq; ++k) {
+ int stat;
+ isl_seq_neg(bmap->eq[k], bmap->eq[k], 1 + dim);
+ stat = status_in(bmap->eq[k], tab);
+ isl_seq_neg(bmap->eq[k], bmap->eq[k], 1 + dim);
+ if (stat < 0)
+ return isl_bool_error;
+ if (stat != STATUS_VALID)
+ return isl_bool_false;
+ stat = status_in(bmap->eq[k], tab);
+ if (stat < 0)
+ return isl_bool_error;
+ if (stat != STATUS_VALID)
+ return isl_bool_false;
+ }
+
+ for (k = 0; k < bmap->n_ineq; ++k) {
+ int stat;
+ if (info->ineq[k] == STATUS_REDUNDANT)
+ continue;
+ stat = status_in(bmap->ineq[k], tab);
+ if (stat < 0)
+ return isl_bool_error;
+ if (stat != STATUS_VALID)
+ return isl_bool_false;
+ }
+ return isl_bool_true;
+}
+
+/* Basic map "i" has an inequality "k" that is adjacent
+ * to some inequality of basic map "j". All the other inequalities
+ * are valid for "j".
+ * If not NULL, then "extra" contains extra wrapping constraints that are valid
+ * for both "i" and "j".
+ * Check if basic map "j" forms an extension of basic map "i",
+ * taking into account the extra constraints, if any.
+ *
+ * Note that this function is only called if some of the equalities or
+ * inequalities of basic map "j" do cut basic map "i". The function is
+ * correct even if there are no such cut constraints, but in that case
+ * the additional checks performed by this function are overkill.
+ *
+ * In particular, we replace constraint k, say f >= 0, by constraint
+ * f <= -1, add the inequalities of "j" that are valid for "i",
+ * as well as the "extra" constraints, if any,
+ * and check if the result is a subset of basic map "j".
+ * To improve the chances of the subset relation being detected,
+ * any variable that only attains a single integer value
+ * in the tableau of "i" is first fixed to that value.
+ * If the result is a subset, then we know that this result is exactly equal
+ * to basic map "j" since all its constraints are valid for basic map "j".
+ * By combining the valid constraints of "i" (all equalities and all
+ * inequalities except "k"), the valid constraints of "j" and
+ * the "extra" constraints, if any, we therefore
+ * obtain a basic map that is equal to their union.
+ * In this case, there is no need to perform a rollback of the tableau
+ * since it is going to be destroyed in fuse().
+ *
+ *
+ * |\__ |\__
+ * | \__ | \__
+ * | \_ => | \__
+ * |_______| _ |_________\
+ *
+ *
+ * |\ |\
+ * | \ | \
+ * | \ | \
+ * | | | \
+ * | ||\ => | \
+ * | || \ | \
+ * | || | | |
+ * |__||_/ |_____/
+ *
+ *
+ * _______ _______
+ * | | __ | \__
+ * | ||__| => | __|
+ * |_______| |_______/
+ */
+static enum isl_change is_adj_ineq_extension_with_wraps(int i, int j, int k,
+ struct isl_coalesce_info *info, __isl_keep isl_mat *extra)
+{
+ struct isl_tab_undo *snap;
+ isl_size n_eq_i, n_ineq_j, n_extra;
+ isl_size total = isl_basic_map_dim(info[i].bmap, isl_dim_all);
+ isl_stat r;
+ isl_bool super;
+
+ if (total < 0)
+ return isl_change_error;
+
+ n_eq_i = isl_basic_map_n_equality(info[i].bmap);
+ n_ineq_j = isl_basic_map_n_inequality(info[j].bmap);
+ n_extra = isl_mat_rows(extra);
+ if (n_eq_i < 0 || n_ineq_j < 0 || n_extra < 0)
+ return isl_change_error;
+
+ if (isl_tab_extend_cons(info[i].tab, 1 + n_ineq_j + n_extra) < 0)
+ return isl_change_error;
+
+ snap = isl_tab_snap(info[i].tab);
+
+ if (isl_tab_unrestrict(info[i].tab, n_eq_i + k) < 0)
+ return isl_change_error;
+
+ isl_seq_neg(info[i].bmap->ineq[k], info[i].bmap->ineq[k], 1 + total);
+ isl_int_sub_ui(info[i].bmap->ineq[k][0], info[i].bmap->ineq[k][0], 1);
+ r = isl_tab_add_ineq(info[i].tab, info[i].bmap->ineq[k]);
+ isl_seq_neg(info[i].bmap->ineq[k], info[i].bmap->ineq[k], 1 + total);
+ isl_int_sub_ui(info[i].bmap->ineq[k][0], info[i].bmap->ineq[k][0], 1);
+ if (r < 0)
+ return isl_change_error;
+
+ for (k = 0; k < n_ineq_j; ++k) {
+ if (info[j].ineq[k] != STATUS_VALID)
+ continue;
+ if (isl_tab_add_ineq(info[i].tab, info[j].bmap->ineq[k]) < 0)
+ return isl_change_error;
+ }
+ for (k = 0; k < n_extra; ++k) {
+ if (isl_tab_add_ineq(info[i].tab, extra->row[k]) < 0)
+ return isl_change_error;
+ }
+ if (isl_tab_detect_constants(info[i].tab) < 0)
+ return isl_change_error;
+
+ super = contains(&info[j], info[i].tab);
+ if (super < 0)
+ return isl_change_error;
+ if (super)
+ return fuse(i, j, info, extra, 0, 0);
+
+ if (isl_tab_rollback(info[i].tab, snap) < 0)
+ return isl_change_error;
+
+ return isl_change_none;
+}
+
+/* Given an affine transformation matrix "T", does row "row" represent
+ * anything other than a unit vector (possibly shifted by a constant)
+ * that is not involved in any of the other rows?
+ *
+ * That is, if a constraint involves the variable corresponding to
+ * the row, then could its preimage by "T" have any coefficients
+ * that are different from those in the original constraint?
+ */
+static int not_unique_unit_row(__isl_keep isl_mat *T, int row)
+{
+ int i, j;
+ int len = T->n_col - 1;
+
+ i = isl_seq_first_non_zero(T->row[row] + 1, len);
+ if (i < 0)
+ return 1;
+ if (!isl_int_is_one(T->row[row][1 + i]) &&
+ !isl_int_is_negone(T->row[row][1 + i]))
+ return 1;
+
+ j = isl_seq_first_non_zero(T->row[row] + 1 + i + 1, len - (i + 1));
+ if (j >= 0)
+ return 1;
+
+ for (j = 1; j < T->n_row; ++j) {
+ if (j == row)
+ continue;
+ if (!isl_int_is_zero(T->row[j][1 + i]))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Does inequality constraint "ineq" of "bmap" involve any of
+ * the variables marked in "affected"?
+ * "total" is the total number of variables, i.e., the number
+ * of entries in "affected".
+ */
+static isl_bool is_affected(__isl_keep isl_basic_map *bmap, int ineq,
+ int *affected, int total)
+{
+ int i;
+
+ for (i = 0; i < total; ++i) {
+ if (!affected[i])
+ continue;
+ if (!isl_int_is_zero(bmap->ineq[ineq][1 + i]))
+ return isl_bool_true;
+ }
+
+ return isl_bool_false;
+}
+
+/* Given the compressed version of inequality constraint "ineq"
+ * of info->bmap in "v", check if the constraint can be tightened,
+ * where the compression is based on an equality constraint valid
+ * for info->tab.
+ * If so, add the tightened version of the inequality constraint
+ * to info->tab. "v" may be modified by this function.
+ *
+ * That is, if the compressed constraint is of the form
+ *
+ * m f() + c >= 0
+ *
+ * with 0 < c < m, then it is equivalent to
+ *
+ * f() >= 0
+ *
+ * This means that c can also be subtracted from the original,
+ * uncompressed constraint without affecting the integer points
+ * in info->tab. Add this tightened constraint as an extra row
+ * to info->tab to make this information explicitly available.
+ */
+static __isl_give isl_vec *try_tightening(struct isl_coalesce_info *info,
+ int ineq, __isl_take isl_vec *v)
+{
+ isl_ctx *ctx;
+ isl_stat r;
+
+ if (!v)
+ return NULL;
+
+ ctx = isl_vec_get_ctx(v);
+ isl_seq_gcd(v->el + 1, v->size - 1, &ctx->normalize_gcd);
+ if (isl_int_is_zero(ctx->normalize_gcd) ||
+ isl_int_is_one(ctx->normalize_gcd)) {
+ return v;
+ }
+
+ v = isl_vec_cow(v);
+ if (!v)
+ return NULL;
+
+ isl_int_fdiv_r(v->el[0], v->el[0], ctx->normalize_gcd);
+ if (isl_int_is_zero(v->el[0]))
+ return v;
+
+ if (isl_tab_extend_cons(info->tab, 1) < 0)
+ return isl_vec_free(v);
+
+ isl_int_sub(info->bmap->ineq[ineq][0],
+ info->bmap->ineq[ineq][0], v->el[0]);
+ r = isl_tab_add_ineq(info->tab, info->bmap->ineq[ineq]);
+ isl_int_add(info->bmap->ineq[ineq][0],
+ info->bmap->ineq[ineq][0], v->el[0]);
+
+ if (r < 0)
+ return isl_vec_free(v);
+
+ return v;
+}
+
+/* Tighten the (non-redundant) constraints on the facet represented
+ * by info->tab.
+ * In particular, on input, info->tab represents the result
+ * of relaxing the "n" inequality constraints of info->bmap in "relaxed"
+ * by one, i.e., replacing f_i >= 0 by f_i + 1 >= 0, and then
+ * replacing the one at index "l" by the corresponding equality,
+ * i.e., f_k + 1 = 0, with k = relaxed[l].
+ *
+ * Compute a variable compression from the equality constraint f_k + 1 = 0
+ * and use it to tighten the other constraints of info->bmap
+ * (that is, all constraints that have not been relaxed),
+ * updating info->tab (and leaving info->bmap untouched).
+ * The compression handles essentially two cases, one where a variable
+ * is assigned a fixed value and can therefore be eliminated, and one
+ * where one variable is a shifted multiple of some other variable and
+ * can therefore be replaced by that multiple.
+ * Gaussian elimination would also work for the first case, but for
+ * the second case, the effectiveness would depend on the order
+ * of the variables.
+ * After compression, some of the constraints may have coefficients
+ * with a common divisor. If this divisor does not divide the constant
+ * term, then the constraint can be tightened.
+ * The tightening is performed on the tableau info->tab by introducing
+ * extra (temporary) constraints.
+ *
+ * Only constraints that are possibly affected by the compression are
+ * considered. In particular, if the constraint only involves variables
+ * that are directly mapped to a distinct set of other variables, then
+ * no common divisor can be introduced and no tightening can occur.
+ *
+ * It is important to only consider the non-redundant constraints
+ * since the facet constraint has been relaxed prior to the call
+ * to this function, meaning that the constraints that were redundant
+ * prior to the relaxation may no longer be redundant.
+ * These constraints will be ignored in the fused result, so
+ * the fusion detection should not exploit them.
+ */
+static isl_stat tighten_on_relaxed_facet(struct isl_coalesce_info *info,
+ int n, int *relaxed, int l)
+{
+ isl_size total;
+ isl_ctx *ctx;
+ isl_vec *v = NULL;
+ isl_mat *T;
+ int i;
+ int k;
+ int *affected;
+
+ k = relaxed[l];
+ ctx = isl_basic_map_get_ctx(info->bmap);
+ total = isl_basic_map_dim(info->bmap, isl_dim_all);
+ if (total < 0)
+ return isl_stat_error;
+ isl_int_add_ui(info->bmap->ineq[k][0], info->bmap->ineq[k][0], 1);
+ T = isl_mat_sub_alloc6(ctx, info->bmap->ineq, k, 1, 0, 1 + total);
+ T = isl_mat_variable_compression(T, NULL);
+ isl_int_sub_ui(info->bmap->ineq[k][0], info->bmap->ineq[k][0], 1);
+ if (!T)
+ return isl_stat_error;
+ if (T->n_col == 0) {
+ isl_mat_free(T);
+ return isl_stat_ok;
+ }
+
+ affected = isl_alloc_array(ctx, int, total);
+ if (!affected)
+ goto error;
+
+ for (i = 0; i < total; ++i)
+ affected[i] = not_unique_unit_row(T, 1 + i);
+
+ for (i = 0; i < info->bmap->n_ineq; ++i) {
+ isl_bool handle;
+ if (any(relaxed, n, i))
+ continue;
+ if (info->ineq[i] == STATUS_REDUNDANT)
+ continue;
+ handle = is_affected(info->bmap, i, affected, total);
+ if (handle < 0)
+ goto error;
+ if (!handle)
+ continue;
+ v = isl_vec_alloc(ctx, 1 + total);
+ if (!v)
+ goto error;
+ isl_seq_cpy(v->el, info->bmap->ineq[i], 1 + total);
+ v = isl_vec_mat_product(v, isl_mat_copy(T));
+ v = try_tightening(info, i, v);
+ isl_vec_free(v);
+ if (!v)
+ goto error;
+ }
+
+ isl_mat_free(T);
+ free(affected);
+ return isl_stat_ok;
+error:
+ isl_mat_free(T);
+ free(affected);
+ return isl_stat_error;
+}
+
+/* Replace the basic maps "i" and "j" by an extension of "i"
+ * along the "n" inequality constraints in "relax" by one.
+ * The tableau info[i].tab has already been extended.
+ * Extend info[i].bmap accordingly by relaxing all constraints in "relax"
+ * by one.
+ * Each integer division that does not have exactly the same
+ * definition in "i" and "j" is marked unknown and the basic map
+ * is scheduled to be simplified in an attempt to recover
+ * the integer division definition.
+ * Place the extension in the position that is the smallest of i and j.
+ */
+static enum isl_change extend(int i, int j, int n, int *relax,
+ struct isl_coalesce_info *info)
+{
+ int l;
+ isl_size total;
+
+ info[i].bmap = isl_basic_map_cow(info[i].bmap);
+ total = isl_basic_map_dim(info[i].bmap, isl_dim_all);
+ if (total < 0)
+ return isl_change_error;
+ for (l = 0; l < info[i].bmap->n_div; ++l)
+ if (!isl_seq_eq(info[i].bmap->div[l],
+ info[j].bmap->div[l], 1 + 1 + total)) {
+ isl_int_set_si(info[i].bmap->div[l][0], 0);
+ info[i].simplify = 1;
+ }
+ for (l = 0; l < n; ++l)
+ isl_int_add_ui(info[i].bmap->ineq[relax[l]][0],
+ info[i].bmap->ineq[relax[l]][0], 1);
+ ISL_F_CLR(info[i].bmap, ISL_BASIC_MAP_NO_REDUNDANT);
+ ISL_F_SET(info[i].bmap, ISL_BASIC_MAP_FINAL);
+ drop(&info[j]);
+ info[i].modified = 1;
+ if (j < i)
+ exchange(&info[i], &info[j]);
+ return isl_change_fuse;
+}
+
+/* Basic map "i" has "n" inequality constraints (collected in "relax")
+ * that are such that they include basic map "j" if they are relaxed
+ * by one. All the other inequalities are valid for "j".
+ * Check if basic map "j" forms an extension of basic map "i".
+ *
+ * In particular, relax the constraints in "relax", compute the corresponding
+ * facets one by one and check whether each of these is included
+ * in the other basic map.
+ * Before testing for inclusion, the constraints on each facet
+ * are tightened to increase the chance of an inclusion being detected.
+ * (Adding the valid constraints of "j" to the tableau of "i", as is done
+ * in is_adj_ineq_extension, may further increase those chances, but this
+ * is not currently done.)
+ * If each facet is included, we know that relaxing the constraints extends
+ * the basic map with exactly the other basic map (we already know that this
+ * other basic map is included in the extension, because all other
+ * inequality constraints are valid of "j") and we can replace the
+ * two basic maps by this extension.
+ *
+ * If any of the relaxed constraints turn out to be redundant, then bail out.
+ * isl_tab_select_facet refuses to handle such constraints. It may be
+ * possible to handle them anyway by making a distinction between
+ * redundant constraints with a corresponding facet that still intersects
+ * the set (allowing isl_tab_select_facet to handle them) and
+ * those where the facet does not intersect the set (which can be ignored
+ * because the empty facet is trivially included in the other disjunct).
+ * However, relaxed constraints that turn out to be redundant should
+ * be fairly rare and no such instance has been reported where
+ * coalescing would be successful.
+ * ____ _____
+ * / || / |
+ * / || / |
+ * \ || => \ |
+ * \ || \ |
+ * \___|| \____|
+ *
+ *
+ * \ |\
+ * |\\ | \
+ * | \\ | \
+ * | | => | /
+ * | / | /
+ * |/ |/
+ */
+static enum isl_change is_relaxed_extension(int i, int j, int n, int *relax,
+ struct isl_coalesce_info *info)
+{
+ int l;
+ isl_bool super;
+ struct isl_tab_undo *snap, *snap2;
+ unsigned n_eq = info[i].bmap->n_eq;
+
+ for (l = 0; l < n; ++l)
+ if (isl_tab_is_equality(info[i].tab, n_eq + relax[l]))
+ return isl_change_none;
+
+ snap = isl_tab_snap(info[i].tab);
+ for (l = 0; l < n; ++l)
+ if (isl_tab_relax(info[i].tab, n_eq + relax[l]) < 0)
+ return isl_change_error;
+ for (l = 0; l < n; ++l) {
+ if (!isl_tab_is_redundant(info[i].tab, n_eq + relax[l]))
+ continue;
+ if (isl_tab_rollback(info[i].tab, snap) < 0)
+ return isl_change_error;
+ return isl_change_none;
+ }
+ snap2 = isl_tab_snap(info[i].tab);
+ for (l = 0; l < n; ++l) {
+ if (isl_tab_rollback(info[i].tab, snap2) < 0)
+ return isl_change_error;
+ if (isl_tab_select_facet(info[i].tab, n_eq + relax[l]) < 0)
+ return isl_change_error;
+ if (tighten_on_relaxed_facet(&info[i], n, relax, l) < 0)
+ return isl_change_error;
+ super = contains(&info[j], info[i].tab);
+ if (super < 0)
+ return isl_change_error;
+ if (super)
+ continue;
+ if (isl_tab_rollback(info[i].tab, snap) < 0)
+ return isl_change_error;
+ return isl_change_none;
+ }
+
+ if (isl_tab_rollback(info[i].tab, snap2) < 0)
+ return isl_change_error;
+ return extend(i, j, n, relax, info);
+}
+
+/* Data structure that keeps track of the wrapping constraints
+ * and of information to bound the coefficients of those constraints.
+ *
+ * "failed" is set if wrapping has failed.
+ * bound is set if we want to apply a bound on the coefficients
+ * mat contains the wrapping constraints
+ * max is the bound on the coefficients (if bound is set)
+ */
+struct isl_wraps {
+ int failed;
+ int bound;
+ isl_mat *mat;
+ isl_int max;
+};
+
+/* Update wraps->max to be greater than or equal to the coefficients
+ * in the equalities and inequalities of info->bmap that can be removed
+ * if we end up applying wrapping.
+ */
+static isl_stat wraps_update_max(struct isl_wraps *wraps,
+ struct isl_coalesce_info *info)
+{
+ int k;
+ isl_int max_k;
+ isl_size total = isl_basic_map_dim(info->bmap, isl_dim_all);
+
+ if (total < 0)
+ return isl_stat_error;
+ isl_int_init(max_k);
+
+ for (k = 0; k < info->bmap->n_eq; ++k) {
+ if (info->eq[2 * k] == STATUS_VALID &&
+ info->eq[2 * k + 1] == STATUS_VALID)
+ continue;
+ isl_seq_abs_max(info->bmap->eq[k] + 1, total, &max_k);
+ if (isl_int_abs_gt(max_k, wraps->max))
+ isl_int_set(wraps->max, max_k);
+ }
+
+ for (k = 0; k < info->bmap->n_ineq; ++k) {
+ if (info->ineq[k] == STATUS_VALID ||
+ info->ineq[k] == STATUS_REDUNDANT)
+ continue;
+ isl_seq_abs_max(info->bmap->ineq[k] + 1, total, &max_k);
+ if (isl_int_abs_gt(max_k, wraps->max))
+ isl_int_set(wraps->max, max_k);
+ }
+
+ isl_int_clear(max_k);
+
+ return isl_stat_ok;
+}
+
+/* Initialize the isl_wraps data structure.
+ * If we want to bound the coefficients of the wrapping constraints,
+ * we set wraps->max to the largest coefficient
+ * in the equalities and inequalities that can be removed if we end up
+ * applying wrapping.
+ */
+static isl_stat wraps_init(struct isl_wraps *wraps, __isl_take isl_mat *mat,
+ struct isl_coalesce_info *info, int i, int j)
+{
+ isl_ctx *ctx;
+
+ wraps->failed = 0;
+ wraps->bound = 0;
+ wraps->mat = mat;
+ if (!mat)
+ return isl_stat_error;
+ wraps->mat->n_row = 0;
+ ctx = isl_mat_get_ctx(mat);
+ wraps->bound = isl_options_get_coalesce_bounded_wrapping(ctx);
+ if (!wraps->bound)
+ return isl_stat_ok;
+ isl_int_init(wraps->max);
+ isl_int_set_si(wraps->max, 0);
+ if (wraps_update_max(wraps, &info[i]) < 0)
+ return isl_stat_error;
+ if (wraps_update_max(wraps, &info[j]) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Free the contents of the isl_wraps data structure.
+ */
+static void wraps_free(struct isl_wraps *wraps)
+{
+ isl_mat_free(wraps->mat);
+ if (wraps->bound)
+ isl_int_clear(wraps->max);
+}
+
+/* Mark the wrapping as failed.
+ */
+static isl_stat wraps_mark_failed(struct isl_wraps *wraps)
+{
+ wraps->failed = 1;
+ return isl_stat_ok;
+}
+
+/* Is the wrapping constraint in row "row" allowed?
+ *
+ * If wraps->bound is set, we check that none of the coefficients
+ * is greater than wraps->max.
+ */
+static int allow_wrap(struct isl_wraps *wraps, int row)
+{
+ int i;
+
+ if (!wraps->bound)
+ return 1;
+
+ for (i = 1; i < wraps->mat->n_col; ++i)
+ if (isl_int_abs_gt(wraps->mat->row[row][i], wraps->max))
+ return 0;
+
+ return 1;
+}
+
+/* Wrap "ineq" (or its opposite if "negate" is set) around "bound"
+ * to include "set" and add the result in position "w" of "wraps".
+ * "len" is the total number of coefficients in "bound" and "ineq".
+ * Return 1 on success, 0 on failure and -1 on error.
+ * Wrapping can fail if the result of wrapping is equal to "bound"
+ * or if we want to bound the sizes of the coefficients and
+ * the wrapped constraint does not satisfy this bound.
+ */
+static int add_wrap(struct isl_wraps *wraps, int w, isl_int *bound,
+ isl_int *ineq, unsigned len, __isl_keep isl_set *set, int negate)
+{
+ isl_seq_cpy(wraps->mat->row[w], bound, len);
+ if (negate) {
+ isl_seq_neg(wraps->mat->row[w + 1], ineq, len);
+ ineq = wraps->mat->row[w + 1];
+ }
+ if (!isl_set_wrap_facet(set, wraps->mat->row[w], ineq))
+ return -1;
+ if (isl_seq_eq(wraps->mat->row[w], bound, len))
+ return 0;
+ if (!allow_wrap(wraps, w))
+ return 0;
+ return 1;
+}
+
+/* This function has two modes of operations.
+ *
+ * If "add_valid" is set, then all the constraints of info->bmap
+ * (except the opposite of "bound") are valid for the other basic map.
+ * In this case, attempts are made to wrap some of these valid constraints
+ * to more tightly fit around "set". Only successful wrappings are recorded
+ * and failed wrappings are ignored.
+ *
+ * If "add_valid" is not set, then some of the constraints of info->bmap
+ * are not valid for the other basic map, and only those are considered
+ * for wrapping. In this case all attempted wrappings need to succeed.
+ * Otherwise "wraps" is marked as failed.
+ * Note that the constraints that are valid for the other basic map
+ * will be added to the combined basic map by default, so there is
+ * no need to wrap them.
+ * The caller wrap_in_facets even relies on this function not wrapping
+ * any constraints that are already valid.
+ *
+ * Only consider constraints that are not redundant (as determined
+ * by info->tab) and that are valid or invalid depending on "add_valid".
+ * Wrap each constraint around "bound" such that it includes the whole
+ * set "set" and append the resulting constraint to "wraps".
+ * "wraps" is assumed to have been pre-allocated to the appropriate size.
+ * wraps->n_row is the number of actual wrapped constraints that have
+ * been added.
+ * If any of the wrapping problems results in a constraint that is
+ * identical to "bound", then this means that "set" is unbounded in such
+ * a way that no wrapping is possible.
+ * Similarly, if we want to bound the coefficients of the wrapping
+ * constraints and a newly added wrapping constraint does not
+ * satisfy the bound, then the wrapping is considered to have failed.
+ * Note though that "wraps" is only marked failed if "add_valid" is not set.
+ */
+static isl_stat add_selected_wraps(struct isl_wraps *wraps,
+ struct isl_coalesce_info *info, isl_int *bound, __isl_keep isl_set *set,
+ int add_valid)
+{
+ int l, m;
+ int w;
+ int added;
+ isl_basic_map *bmap = info->bmap;
+ isl_size total = isl_basic_map_dim(bmap, isl_dim_all);
+ unsigned len = 1 + total;
+
+ if (total < 0)
+ return isl_stat_error;
+
+ w = wraps->mat->n_row;
+
+ for (l = 0; l < bmap->n_ineq; ++l) {
+ int is_valid = info->ineq[l] == STATUS_VALID;
+ if ((!add_valid && is_valid) ||
+ info->ineq[l] == STATUS_REDUNDANT)
+ continue;
+ if (isl_seq_is_neg(bound, bmap->ineq[l], len))
+ continue;
+ if (isl_seq_eq(bound, bmap->ineq[l], len))
+ continue;
+ if (isl_tab_is_redundant(info->tab, bmap->n_eq + l))
+ continue;
+
+ added = add_wrap(wraps, w, bound, bmap->ineq[l], len, set, 0);
+ if (added < 0)
+ return isl_stat_error;
+ if (!added && !is_valid)
+ goto unbounded;
+ if (added)
+ ++w;
+ }
+ for (l = 0; l < bmap->n_eq; ++l) {
+ if (isl_seq_is_neg(bound, bmap->eq[l], len))
+ continue;
+ if (isl_seq_eq(bound, bmap->eq[l], len))
+ continue;
+
+ for (m = 0; m < 2; ++m) {
+ if (info->eq[2 * l + m] == STATUS_VALID)
+ continue;
+ added = add_wrap(wraps, w, bound, bmap->eq[l], len,
+ set, !m);
+ if (added < 0)
+ return isl_stat_error;
+ if (!added)
+ goto unbounded;
+ ++w;
+ }
+ }
+
+ wraps->mat->n_row = w;
+ return isl_stat_ok;
+unbounded:
+ return wraps_mark_failed(wraps);
+}
+
+/* For each constraint in info->bmap that is not redundant (as determined
+ * by info->tab) and that is not a valid constraint for the other basic map,
+ * wrap the constraint around "bound" such that it includes the whole
+ * set "set" and append the resulting constraint to "wraps".
+ * Note that the constraints that are valid for the other basic map
+ * will be added to the combined basic map by default, so there is
+ * no need to wrap them.
+ * The caller wrap_in_facets even relies on this function not wrapping
+ * any constraints that are already valid.
+ * "wraps" is assumed to have been pre-allocated to the appropriate size.
+ * wraps->n_row is the number of actual wrapped constraints that have
+ * been added.
+ * If any of the wrapping problems results in a constraint that is
+ * identical to "bound", then this means that "set" is unbounded in such
+ * a way that no wrapping is possible. If this happens then "wraps"
+ * is marked as failed.
+ * Similarly, if we want to bound the coefficients of the wrapping
+ * constraints and a newly added wrapping constraint does not
+ * satisfy the bound, then "wraps" is also marked as failed.
+ */
+static isl_stat add_wraps(struct isl_wraps *wraps,
+ struct isl_coalesce_info *info, isl_int *bound, __isl_keep isl_set *set)
+{
+ return add_selected_wraps(wraps, info, bound, set, 0);
+}
+
+/* Check if the constraints in "wraps" from "first" until the last
+ * are all valid for the basic set represented by "tab",
+ * dropping the invalid constraints if "keep" is set and
+ * marking the wrapping as failed if "keep" is not set and
+ * any constraint turns out to be invalid.
+ */
+static isl_stat check_wraps(struct isl_wraps *wraps, int first,
+ struct isl_tab *tab, int keep)
+{
+ int i;
+
+ for (i = wraps->mat->n_row - 1; i >= first; --i) {
+ enum isl_ineq_type type;
+ type = isl_tab_ineq_type(tab, wraps->mat->row[i]);
+ if (type == isl_ineq_error)
+ return isl_stat_error;
+ if (type == isl_ineq_redundant)
+ continue;
+ if (!keep)
+ return wraps_mark_failed(wraps);
+ wraps->mat = isl_mat_drop_rows(wraps->mat, i, 1);
+ if (!wraps->mat)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Return a set that corresponds to the non-redundant constraints
+ * (as recorded in tab) of bmap.
+ *
+ * It's important to remove the redundant constraints as some
+ * of the other constraints may have been modified after the
+ * constraints were marked redundant.
+ * In particular, a constraint may have been relaxed.
+ * Redundant constraints are ignored when a constraint is relaxed
+ * and should therefore continue to be ignored ever after.
+ * Otherwise, the relaxation might be thwarted by some of
+ * these constraints.
+ *
+ * Update the underlying set to ensure that the dimension doesn't change.
+ * Otherwise the integer divisions could get dropped if the tab
+ * turns out to be empty.
+ */
+static __isl_give isl_set *set_from_updated_bmap(__isl_keep isl_basic_map *bmap,
+ struct isl_tab *tab)
+{
+ isl_basic_set *bset;
+
+ bmap = isl_basic_map_copy(bmap);
+ bset = isl_basic_map_underlying_set(bmap);
+ bset = isl_basic_set_cow(bset);
+ bset = isl_basic_set_update_from_tab(bset, tab);
+ return isl_set_from_basic_set(bset);
+}
+
+/* Does "info" have any cut constraints that are redundant?
+ */
+static isl_bool has_redundant_cuts(struct isl_coalesce_info *info)
+{
+ int l;
+ isl_size n_eq, n_ineq;
+
+ n_eq = isl_basic_map_n_equality(info->bmap);
+ n_ineq = isl_basic_map_n_inequality(info->bmap);
+ if (n_eq < 0 || n_ineq < 0)
+ return isl_bool_error;
+ for (l = 0; l < n_ineq; ++l) {
+ int red;
+
+ if (info->ineq[l] != STATUS_CUT)
+ continue;
+ red = isl_tab_is_redundant(info->tab, n_eq + l);
+ if (red < 0)
+ return isl_bool_error;
+ if (red)
+ return isl_bool_true;
+ }
+
+ return isl_bool_false;
+}
+
+/* Wrap some constraints of info->bmap that bound the facet defined
+ * by inequality "k" around (the opposite of) this inequality to
+ * include "set". "bound" may be used to store the negated inequality.
+ *
+ * If "add_valid" is set, then all ridges are already valid and
+ * the purpose is to wrap "set" more tightly. In this case,
+ * wrapping doesn't fail, although it is possible that no constraint
+ * gets wrapped.
+ *
+ * If "add_valid" is not set, then some of the ridges are cut constraints
+ * and only those are wrapped around "set".
+ *
+ * Since the wrapped constraints are not guaranteed to contain the whole
+ * of info->bmap, we check them in check_wraps.
+ * If any of the wrapped constraints turn out to be invalid, then
+ * check_wraps will mark "wraps" as failed if "add_valid" is not set.
+ * If "add_valid" is set, then the offending constraints are
+ * simply removed.
+ *
+ * If the facet turns out to be empty, then no wrapping can be performed.
+ * This is considered a failure, unless "add_valid" is set.
+ *
+ * If any of the cut constraints of info->bmap turn out
+ * to be redundant with respect to other constraints
+ * then these will neither be wrapped nor added directly to the result.
+ * The result may therefore not be correct.
+ * Skip wrapping and mark "wraps" as failed in this case.
+ */
+static isl_stat add_selected_wraps_around_facet(struct isl_wraps *wraps,
+ struct isl_coalesce_info *info, int k, isl_int *bound,
+ __isl_keep isl_set *set, int add_valid)
+{
+ isl_bool nowrap;
+ struct isl_tab_undo *snap;
+ int n;
+ isl_size total = isl_basic_map_dim(info->bmap, isl_dim_all);
+
+ if (total < 0)
+ return isl_stat_error;
+
+ snap = isl_tab_snap(info->tab);
+
+ if (isl_tab_select_facet(info->tab, info->bmap->n_eq + k) < 0)
+ return isl_stat_error;
+ if (isl_tab_detect_redundant(info->tab) < 0)
+ return isl_stat_error;
+ if (info->tab->empty) {
+ if (!add_valid)
+ return wraps_mark_failed(wraps);
+ return isl_stat_ok;
+ }
+ nowrap = has_redundant_cuts(info);
+ if (nowrap < 0)
+ return isl_stat_error;
+
+ n = wraps->mat->n_row;
+ if (!nowrap) {
+ isl_seq_neg(bound, info->bmap->ineq[k], 1 + total);
+
+ if (add_selected_wraps(wraps, info, bound, set, add_valid) < 0)
+ return isl_stat_error;
+ }
+
+ if (isl_tab_rollback(info->tab, snap) < 0)
+ return isl_stat_error;
+ if (nowrap)
+ return wraps_mark_failed(wraps);
+ if (check_wraps(wraps, n, info->tab, add_valid) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Wrap the constraints of info->bmap that bound the facet defined
+ * by inequality "k" around (the opposite of) this inequality to
+ * include "set". "bound" may be used to store the negated inequality.
+ * If any of the wrapped constraints turn out to be invalid for info->bmap
+ * itself, then mark "wraps" as failed.
+ */
+static isl_stat add_wraps_around_facet(struct isl_wraps *wraps,
+ struct isl_coalesce_info *info, int k, isl_int *bound,
+ __isl_keep isl_set *set)
+{
+ return add_selected_wraps_around_facet(wraps, info, k, bound, set, 0);
+}
+
+/* Wrap the (valid) constraints of info->bmap that bound the facet defined
+ * by inequality "k" around (the opposite of) this inequality to
+ * include "set" more tightly.
+ * "bound" may be used to store the negated inequality.
+ * Remove any wrapping constraints that turn out to be invalid
+ * for info->bmap itself.
+ */
+static isl_stat add_valid_wraps_around_facet(struct isl_wraps *wraps,
+ struct isl_coalesce_info *info, int k, isl_int *bound,
+ __isl_keep isl_set *set)
+{
+ return add_selected_wraps_around_facet(wraps, info, k, bound, set, 1);
+}
+
+/* Basic map "i" has an inequality (say "k") that is adjacent
+ * to some inequality of basic map "j". All the other inequalities
+ * are valid for "j".
+ * Check if basic map "j" forms an extension of basic map "i".
+ *
+ * Note that this function is only called if some of the equalities or
+ * inequalities of basic map "j" do cut basic map "i". The function is
+ * correct even if there are no such cut constraints, but in that case
+ * the additional checks performed by this function are overkill.
+ *
+ * First try and wrap the ridges of "k" around "j".
+ * Note that those ridges are already valid for "j",
+ * but the wrapped versions may wrap "j" more tightly,
+ * increasing the chances of "j" being detected as an extension of "i"
+ */
+static enum isl_change is_adj_ineq_extension(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ int k;
+ enum isl_change change;
+ isl_size total;
+ isl_size n_eq_i, n_ineq_i;
+ struct isl_wraps wraps;
+ isl_ctx *ctx;
+ isl_mat *mat;
+ isl_vec *bound;
+ isl_set *set_j;
+ isl_stat r;
+
+ k = find_ineq(&info[i], STATUS_ADJ_INEQ);
+ if (k < 0)
+ isl_die(isl_basic_map_get_ctx(info[i].bmap), isl_error_internal,
+ "info[i].ineq should have exactly one STATUS_ADJ_INEQ",
+ return isl_change_error);
+
+ total = isl_basic_map_dim(info[i].bmap, isl_dim_all);
+ n_eq_i = isl_basic_map_n_equality(info[i].bmap);
+ n_ineq_i = isl_basic_map_n_inequality(info[i].bmap);
+ if (total < 0 || n_eq_i < 0 || n_ineq_i < 0)
+ return isl_change_error;
+
+ set_j = set_from_updated_bmap(info[j].bmap, info[j].tab);
+ ctx = isl_basic_map_get_ctx(info[i].bmap);
+ bound = isl_vec_alloc(ctx, 1 + total);
+ mat = isl_mat_alloc(ctx, 2 * n_eq_i + n_ineq_i, 1 + total);
+ if (wraps_init(&wraps, mat, info, i, j) < 0)
+ goto error;
+ if (!bound || !set_j)
+ goto error;
+ r = add_valid_wraps_around_facet(&wraps, &info[i], k, bound->el, set_j);
+ if (r < 0)
+ goto error;
+
+ change = is_adj_ineq_extension_with_wraps(i, j, k, info, wraps.mat);
+
+ wraps_free(&wraps);
+ isl_vec_free(bound);
+ isl_set_free(set_j);
+
+ return change;
+error:
+ wraps_free(&wraps);
+ isl_vec_free(bound);
+ isl_set_free(set_j);
+ return isl_change_error;
+}
+
+/* Both basic maps have at least one inequality with and adjacent
+ * (but opposite) inequality in the other basic map.
+ * Check that there are no cut constraints and that there is only
+ * a single pair of adjacent inequalities.
+ * If so, we can replace the pair by a single basic map described
+ * by all but the pair of adjacent inequalities.
+ * Any additional points introduced lie strictly between the two
+ * adjacent hyperplanes and can therefore be integral.
+ *
+ * ____ _____
+ * / ||\ / \
+ * / || \ / \
+ * \ || \ => \ \
+ * \ || / \ /
+ * \___||_/ \_____/
+ *
+ * The test for a single pair of adjacent inequalities is important
+ * for avoiding the combination of two basic maps like the following
+ *
+ * /|
+ * / |
+ * /__|
+ * _____
+ * | |
+ * | |
+ * |___|
+ *
+ * If there are some cut constraints on one side, then we may
+ * still be able to fuse the two basic maps, but we need to perform
+ * some additional checks in is_adj_ineq_extension.
+ */
+static enum isl_change check_adj_ineq(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ int count_i, count_j;
+ int cut_i, cut_j;
+
+ count_i = count_ineq(&info[i], STATUS_ADJ_INEQ);
+ count_j = count_ineq(&info[j], STATUS_ADJ_INEQ);
+
+ if (count_i != 1 && count_j != 1)
+ return isl_change_none;
+
+ cut_i = any_eq(&info[i], STATUS_CUT) || any_ineq(&info[i], STATUS_CUT);
+ cut_j = any_eq(&info[j], STATUS_CUT) || any_ineq(&info[j], STATUS_CUT);
+
+ if (!cut_i && !cut_j && count_i == 1 && count_j == 1)
+ return fuse(i, j, info, NULL, 0, 0);
+
+ if (count_i == 1 && !cut_i)
+ return is_adj_ineq_extension(i, j, info);
+
+ if (count_j == 1 && !cut_j)
+ return is_adj_ineq_extension(j, i, info);
+
+ return isl_change_none;
+}
+
+/* Given a basic set i with a constraint k that is adjacent to
+ * basic set j, check if we can wrap
+ * both the facet corresponding to k (if "wrap_facet" is set) and basic map j
+ * (always) around their ridges to include the other set.
+ * If so, replace the pair of basic sets by their union.
+ *
+ * All constraints of i (except k) are assumed to be valid or
+ * cut constraints for j.
+ * Wrapping the cut constraints to include basic map j may result
+ * in constraints that are no longer valid of basic map i
+ * we have to check that the resulting wrapping constraints are valid for i.
+ * If "wrap_facet" is not set, then all constraints of i (except k)
+ * are assumed to be valid for j.
+ * ____ _____
+ * / | / \
+ * / || / |
+ * \ || => \ |
+ * \ || \ |
+ * \___|| \____|
+ *
+ */
+static enum isl_change can_wrap_in_facet(int i, int j, int k,
+ struct isl_coalesce_info *info, int wrap_facet)
+{
+ enum isl_change change = isl_change_none;
+ struct isl_wraps wraps;
+ isl_ctx *ctx;
+ isl_mat *mat;
+ struct isl_set *set_i = NULL;
+ struct isl_set *set_j = NULL;
+ struct isl_vec *bound = NULL;
+ isl_size total = isl_basic_map_dim(info[i].bmap, isl_dim_all);
+
+ if (total < 0)
+ return isl_change_error;
+ set_i = set_from_updated_bmap(info[i].bmap, info[i].tab);
+ set_j = set_from_updated_bmap(info[j].bmap, info[j].tab);
+ ctx = isl_basic_map_get_ctx(info[i].bmap);
+ mat = isl_mat_alloc(ctx, 2 * (info[i].bmap->n_eq + info[j].bmap->n_eq) +
+ info[i].bmap->n_ineq + info[j].bmap->n_ineq,
+ 1 + total);
+ if (wraps_init(&wraps, mat, info, i, j) < 0)
+ goto error;
+ bound = isl_vec_alloc(ctx, 1 + total);
+ if (!set_i || !set_j || !bound)
+ goto error;
+
+ isl_seq_cpy(bound->el, info[i].bmap->ineq[k], 1 + total);
+ isl_int_add_ui(bound->el[0], bound->el[0], 1);
+ isl_seq_normalize(ctx, bound->el, 1 + total);
+
+ isl_seq_cpy(wraps.mat->row[0], bound->el, 1 + total);
+ wraps.mat->n_row = 1;
+
+ if (add_wraps(&wraps, &info[j], bound->el, set_i) < 0)
+ goto error;
+ if (wraps.failed)
+ goto unbounded;
+
+ if (wrap_facet) {
+ if (add_wraps_around_facet(&wraps, &info[i], k,
+ bound->el, set_j) < 0)
+ goto error;
+ if (wraps.failed)
+ goto unbounded;
+ }
+
+ change = fuse(i, j, info, wraps.mat, 0, 0);
+
+unbounded:
+ wraps_free(&wraps);
+
+ isl_set_free(set_i);
+ isl_set_free(set_j);
+
+ isl_vec_free(bound);
+
+ return change;
+error:
+ wraps_free(&wraps);
+ isl_vec_free(bound);
+ isl_set_free(set_i);
+ isl_set_free(set_j);
+ return isl_change_error;
+}
+
+/* Given a cut constraint t(x) >= 0 of basic map i, stored in row "w"
+ * of wrap.mat, replace it by its relaxed version t(x) + 1 >= 0, and
+ * add wrapping constraints to wrap.mat for all constraints
+ * of basic map j that bound the part of basic map j that sticks out
+ * of the cut constraint.
+ * "set_i" is the underlying set of basic map i.
+ * If any wrapping fails, then wraps->mat.n_row is reset to zero.
+ *
+ * In particular, we first intersect basic map j with t(x) + 1 = 0.
+ * If the result is empty, then t(x) >= 0 was actually a valid constraint
+ * (with respect to the integer points), so we add t(x) >= 0 instead.
+ * Otherwise, we wrap the constraints of basic map j that are not
+ * redundant in this intersection and that are not already valid
+ * for basic map i over basic map i.
+ * Note that it is sufficient to wrap the constraints to include
+ * basic map i, because we will only wrap the constraints that do
+ * not include basic map i already. The wrapped constraint will
+ * therefore be more relaxed compared to the original constraint.
+ * Since the original constraint is valid for basic map j, so is
+ * the wrapped constraint.
+ */
+static isl_stat wrap_in_facet(struct isl_wraps *wraps, int w,
+ struct isl_coalesce_info *info_j, __isl_keep isl_set *set_i,
+ struct isl_tab_undo *snap)
+{
+ isl_int_add_ui(wraps->mat->row[w][0], wraps->mat->row[w][0], 1);
+ if (isl_tab_add_eq(info_j->tab, wraps->mat->row[w]) < 0)
+ return isl_stat_error;
+ if (isl_tab_detect_redundant(info_j->tab) < 0)
+ return isl_stat_error;
+
+ if (info_j->tab->empty)
+ isl_int_sub_ui(wraps->mat->row[w][0], wraps->mat->row[w][0], 1);
+ else if (add_wraps(wraps, info_j, wraps->mat->row[w], set_i) < 0)
+ return isl_stat_error;
+
+ if (isl_tab_rollback(info_j->tab, snap) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Given a pair of basic maps i and j such that j sticks out
+ * of i at n cut constraints, each time by at most one,
+ * try to compute wrapping constraints and replace the two
+ * basic maps by a single basic map.
+ * The other constraints of i are assumed to be valid for j.
+ * "set_i" is the underlying set of basic map i.
+ * "wraps" has been initialized to be of the right size.
+ *
+ * For each cut constraint t(x) >= 0 of i, we add the relaxed version
+ * t(x) + 1 >= 0, along with wrapping constraints for all constraints
+ * of basic map j that bound the part of basic map j that sticks out
+ * of the cut constraint.
+ *
+ * If any wrapping fails, i.e., if we cannot wrap to touch
+ * the union, then we give up.
+ * Otherwise, the pair of basic maps is replaced by their union.
+ */
+static enum isl_change try_wrap_in_facets(int i, int j,
+ struct isl_coalesce_info *info, struct isl_wraps *wraps,
+ __isl_keep isl_set *set_i)
+{
+ int k, l, w;
+ isl_size total;
+ struct isl_tab_undo *snap;
+
+ total = isl_basic_map_dim(info[i].bmap, isl_dim_all);
+ if (total < 0)
+ return isl_change_error;
+
+ snap = isl_tab_snap(info[j].tab);
+
+ for (k = 0; k < info[i].bmap->n_eq; ++k) {
+ for (l = 0; l < 2; ++l) {
+ if (info[i].eq[2 * k + l] != STATUS_CUT)
+ continue;
+ w = wraps->mat->n_row++;
+ if (l == 0)
+ isl_seq_neg(wraps->mat->row[w],
+ info[i].bmap->eq[k], 1 + total);
+ else
+ isl_seq_cpy(wraps->mat->row[w],
+ info[i].bmap->eq[k], 1 + total);
+ if (wrap_in_facet(wraps, w, &info[j], set_i, snap) < 0)
+ return isl_change_error;
+
+ if (wraps->failed)
+ return isl_change_none;
+ }
+ }
+
+ for (k = 0; k < info[i].bmap->n_ineq; ++k) {
+ if (info[i].ineq[k] != STATUS_CUT)
+ continue;
+ w = wraps->mat->n_row++;
+ isl_seq_cpy(wraps->mat->row[w],
+ info[i].bmap->ineq[k], 1 + total);
+ if (wrap_in_facet(wraps, w, &info[j], set_i, snap) < 0)
+ return isl_change_error;
+
+ if (wraps->failed)
+ return isl_change_none;
+ }
+
+ return fuse(i, j, info, wraps->mat, 0, 1);
+}
+
+/* Given a pair of basic maps i and j such that j sticks out
+ * of i at n cut constraints, each time by at most one,
+ * try to compute wrapping constraints and replace the two
+ * basic maps by a single basic map.
+ * The other constraints of i are assumed to be valid for j.
+ *
+ * The core computation is performed by try_wrap_in_facets.
+ * This function simply extracts an underlying set representation
+ * of basic map i and initializes the data structure for keeping
+ * track of wrapping constraints.
+ */
+static enum isl_change wrap_in_facets(int i, int j, int n,
+ struct isl_coalesce_info *info)
+{
+ enum isl_change change = isl_change_none;
+ struct isl_wraps wraps;
+ isl_ctx *ctx;
+ isl_mat *mat;
+ isl_set *set_i = NULL;
+ isl_size total = isl_basic_map_dim(info[i].bmap, isl_dim_all);
+ int max_wrap;
+
+ if (total < 0)
+ return isl_change_error;
+ if (isl_tab_extend_cons(info[j].tab, 1) < 0)
+ return isl_change_error;
+
+ max_wrap = 1 + 2 * info[j].bmap->n_eq + info[j].bmap->n_ineq;
+ max_wrap *= n;
+
+ set_i = set_from_updated_bmap(info[i].bmap, info[i].tab);
+ ctx = isl_basic_map_get_ctx(info[i].bmap);
+ mat = isl_mat_alloc(ctx, max_wrap, 1 + total);
+ if (wraps_init(&wraps, mat, info, i, j) < 0)
+ goto error;
+ if (!set_i)
+ goto error;
+
+ change = try_wrap_in_facets(i, j, info, &wraps, set_i);
+
+ wraps_free(&wraps);
+ isl_set_free(set_i);
+
+ return change;
+error:
+ wraps_free(&wraps);
+ isl_set_free(set_i);
+ return isl_change_error;
+}
+
+/* Return the effect of inequality "ineq" on the tableau "tab",
+ * after relaxing the constant term of "ineq" by one.
+ */
+static enum isl_ineq_type type_of_relaxed(struct isl_tab *tab, isl_int *ineq)
+{
+ enum isl_ineq_type type;
+
+ isl_int_add_ui(ineq[0], ineq[0], 1);
+ type = isl_tab_ineq_type(tab, ineq);
+ isl_int_sub_ui(ineq[0], ineq[0], 1);
+
+ return type;
+}
+
+/* Given two basic sets i and j,
+ * check if relaxing all the cut constraints of i by one turns
+ * them into valid constraint for j and check if we can wrap in
+ * the bits that are sticking out.
+ * If so, replace the pair by their union.
+ *
+ * We first check if all relaxed cut inequalities of i are valid for j
+ * and then try to wrap in the intersections of the relaxed cut inequalities
+ * with j.
+ *
+ * During this wrapping, we consider the points of j that lie at a distance
+ * of exactly 1 from i. In particular, we ignore the points that lie in
+ * between this lower-dimensional space and the basic map i.
+ * We can therefore only apply this to integer maps.
+ * ____ _____
+ * / ___|_ / \
+ * / | | / |
+ * \ | | => \ |
+ * \|____| \ |
+ * \___| \____/
+ *
+ * _____ ______
+ * | ____|_ | \
+ * | | | | |
+ * | | | => | |
+ * |_| | | |
+ * |_____| \______|
+ *
+ * _______
+ * | |
+ * | |\ |
+ * | | \ |
+ * | | \ |
+ * | | \|
+ * | | \
+ * | |_____\
+ * | |
+ * |_______|
+ *
+ * Wrapping can fail if the result of wrapping one of the facets
+ * around its edges does not produce any new facet constraint.
+ * In particular, this happens when we try to wrap in unbounded sets.
+ *
+ * _______________________________________________________________________
+ * |
+ * | ___
+ * | | |
+ * |_| |_________________________________________________________________
+ * |___|
+ *
+ * The following is not an acceptable result of coalescing the above two
+ * sets as it includes extra integer points.
+ * _______________________________________________________________________
+ * |
+ * |
+ * |
+ * |
+ * \______________________________________________________________________
+ */
+static enum isl_change can_wrap_in_set(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ int k, l;
+ int n;
+ isl_size total;
+
+ if (ISL_F_ISSET(info[i].bmap, ISL_BASIC_MAP_RATIONAL) ||
+ ISL_F_ISSET(info[j].bmap, ISL_BASIC_MAP_RATIONAL))
+ return isl_change_none;
+
+ n = count_eq(&info[i], STATUS_CUT) + count_ineq(&info[i], STATUS_CUT);
+ if (n == 0)
+ return isl_change_none;
+
+ total = isl_basic_map_dim(info[i].bmap, isl_dim_all);
+ if (total < 0)
+ return isl_change_error;
+ for (k = 0; k < info[i].bmap->n_eq; ++k) {
+ for (l = 0; l < 2; ++l) {
+ enum isl_ineq_type type;
+
+ if (info[i].eq[2 * k + l] != STATUS_CUT)
+ continue;
+
+ if (l == 0)
+ isl_seq_neg(info[i].bmap->eq[k],
+ info[i].bmap->eq[k], 1 + total);
+ type = type_of_relaxed(info[j].tab,
+ info[i].bmap->eq[k]);
+ if (l == 0)
+ isl_seq_neg(info[i].bmap->eq[k],
+ info[i].bmap->eq[k], 1 + total);
+ if (type == isl_ineq_error)
+ return isl_change_error;
+ if (type != isl_ineq_redundant)
+ return isl_change_none;
+ }
+ }
+
+ for (k = 0; k < info[i].bmap->n_ineq; ++k) {
+ enum isl_ineq_type type;
+
+ if (info[i].ineq[k] != STATUS_CUT)
+ continue;
+
+ type = type_of_relaxed(info[j].tab, info[i].bmap->ineq[k]);
+ if (type == isl_ineq_error)
+ return isl_change_error;
+ if (type != isl_ineq_redundant)
+ return isl_change_none;
+ }
+
+ return wrap_in_facets(i, j, n, info);
+}
+
+/* Check if either i or j has only cut constraints that can
+ * be used to wrap in (a facet of) the other basic set.
+ * if so, replace the pair by their union.
+ */
+static enum isl_change check_wrap(int i, int j, struct isl_coalesce_info *info)
+{
+ enum isl_change change = isl_change_none;
+
+ change = can_wrap_in_set(i, j, info);
+ if (change != isl_change_none)
+ return change;
+
+ change = can_wrap_in_set(j, i, info);
+ return change;
+}
+
+/* Check if all inequality constraints of "i" that cut "j" cease
+ * to be cut constraints if they are relaxed by one.
+ * If so, collect the cut constraints in "list".
+ * The caller is responsible for allocating "list".
+ */
+static isl_bool all_cut_by_one(int i, int j, struct isl_coalesce_info *info,
+ int *list)
+{
+ int l, n;
+
+ n = 0;
+ for (l = 0; l < info[i].bmap->n_ineq; ++l) {
+ enum isl_ineq_type type;
+
+ if (info[i].ineq[l] != STATUS_CUT)
+ continue;
+ type = type_of_relaxed(info[j].tab, info[i].bmap->ineq[l]);
+ if (type == isl_ineq_error)
+ return isl_bool_error;
+ if (type != isl_ineq_redundant)
+ return isl_bool_false;
+ list[n++] = l;
+ }
+
+ return isl_bool_true;
+}
+
+/* Given two basic maps such that "j" has at least one equality constraint
+ * that is adjacent to an inequality constraint of "i" and such that "i" has
+ * exactly one inequality constraint that is adjacent to an equality
+ * constraint of "j", check whether "i" can be extended to include "j" or
+ * whether "j" can be wrapped into "i".
+ * All remaining constraints of "i" and "j" are assumed to be valid
+ * or cut constraints of the other basic map.
+ * However, none of the equality constraints of "i" are cut constraints.
+ *
+ * If "i" has any "cut" inequality constraints, then check if relaxing
+ * each of them by one is sufficient for them to become valid.
+ * If so, check if the inequality constraint adjacent to an equality
+ * constraint of "j" along with all these cut constraints
+ * can be relaxed by one to contain exactly "j".
+ * Otherwise, or if this fails, check if "j" can be wrapped into "i".
+ */
+static enum isl_change check_single_adj_eq(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ enum isl_change change = isl_change_none;
+ int k;
+ int n_cut;
+ int *relax;
+ isl_ctx *ctx;
+ isl_bool try_relax;
+
+ n_cut = count_ineq(&info[i], STATUS_CUT);
+
+ k = find_ineq(&info[i], STATUS_ADJ_EQ);
+
+ if (n_cut > 0) {
+ ctx = isl_basic_map_get_ctx(info[i].bmap);
+ relax = isl_calloc_array(ctx, int, 1 + n_cut);
+ if (!relax)
+ return isl_change_error;
+ relax[0] = k;
+ try_relax = all_cut_by_one(i, j, info, relax + 1);
+ if (try_relax < 0)
+ change = isl_change_error;
+ } else {
+ try_relax = isl_bool_true;
+ relax = &k;
+ }
+ if (try_relax && change == isl_change_none)
+ change = is_relaxed_extension(i, j, 1 + n_cut, relax, info);
+ if (n_cut > 0)
+ free(relax);
+ if (change != isl_change_none)
+ return change;
+
+ change = can_wrap_in_facet(i, j, k, info, n_cut > 0);
+
+ return change;
+}
+
+/* At least one of the basic maps has an equality that is adjacent
+ * to an inequality. Make sure that only one of the basic maps has
+ * such an equality and that the other basic map has exactly one
+ * inequality adjacent to an equality.
+ * If the other basic map does not have such an inequality, then
+ * check if all its constraints are either valid or cut constraints
+ * and, if so, try wrapping in the first map into the second.
+ * Otherwise, try to extend one basic map with the other or
+ * wrap one basic map in the other.
+ */
+static enum isl_change check_adj_eq(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ if (any_eq(&info[i], STATUS_ADJ_INEQ) &&
+ any_eq(&info[j], STATUS_ADJ_INEQ))
+ /* ADJ EQ TOO MANY */
+ return isl_change_none;
+
+ if (any_eq(&info[i], STATUS_ADJ_INEQ))
+ return check_adj_eq(j, i, info);
+
+ /* j has an equality adjacent to an inequality in i */
+
+ if (count_ineq(&info[i], STATUS_ADJ_EQ) != 1) {
+ if (all_valid_or_cut(&info[i]))
+ return can_wrap_in_set(i, j, info);
+ return isl_change_none;
+ }
+ if (any_eq(&info[i], STATUS_CUT))
+ return isl_change_none;
+ if (any_ineq(&info[j], STATUS_ADJ_EQ) ||
+ any_ineq(&info[i], STATUS_ADJ_INEQ) ||
+ any_ineq(&info[j], STATUS_ADJ_INEQ))
+ /* ADJ EQ TOO MANY */
+ return isl_change_none;
+
+ return check_single_adj_eq(i, j, info);
+}
+
+/* Disjunct "j" lies on a hyperplane that is adjacent to disjunct "i".
+ * In particular, disjunct "i" has an inequality constraint that is adjacent
+ * to a (combination of) equality constraint(s) of disjunct "j",
+ * but disjunct "j" has no explicit equality constraint adjacent
+ * to an inequality constraint of disjunct "i".
+ *
+ * Disjunct "i" is already known not to have any equality constraints
+ * that are adjacent to an equality or inequality constraint.
+ * Check that, other than the inequality constraint mentioned above,
+ * all other constraints of disjunct "i" are valid for disjunct "j".
+ * If so, try and wrap in disjunct "j".
+ */
+static enum isl_change check_ineq_adj_eq(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ int k;
+
+ if (any_eq(&info[i], STATUS_CUT))
+ return isl_change_none;
+ if (any_ineq(&info[i], STATUS_CUT))
+ return isl_change_none;
+ if (any_ineq(&info[i], STATUS_ADJ_INEQ))
+ return isl_change_none;
+ if (count_ineq(&info[i], STATUS_ADJ_EQ) != 1)
+ return isl_change_none;
+
+ k = find_ineq(&info[i], STATUS_ADJ_EQ);
+
+ return can_wrap_in_facet(i, j, k, info, 0);
+}
+
+/* The two basic maps lie on adjacent hyperplanes. In particular,
+ * basic map "i" has an equality that lies parallel to basic map "j".
+ * Check if we can wrap the facets around the parallel hyperplanes
+ * to include the other set.
+ *
+ * We perform basically the same operations as can_wrap_in_facet,
+ * except that we don't need to select a facet of one of the sets.
+ * _
+ * \\ \\
+ * \\ => \\
+ * \ \|
+ *
+ * If there is more than one equality of "i" adjacent to an equality of "j",
+ * then the result will satisfy one or more equalities that are a linear
+ * combination of these equalities. These will be encoded as pairs
+ * of inequalities in the wrapping constraints and need to be made
+ * explicit.
+ */
+static enum isl_change check_eq_adj_eq(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ int k;
+ enum isl_change change = isl_change_none;
+ int detect_equalities = 0;
+ struct isl_wraps wraps;
+ isl_ctx *ctx;
+ isl_mat *mat;
+ struct isl_set *set_i = NULL;
+ struct isl_set *set_j = NULL;
+ struct isl_vec *bound = NULL;
+ isl_size total = isl_basic_map_dim(info[i].bmap, isl_dim_all);
+
+ if (total < 0)
+ return isl_change_error;
+ if (count_eq(&info[i], STATUS_ADJ_EQ) != 1)
+ detect_equalities = 1;
+
+ k = find_eq(&info[i], STATUS_ADJ_EQ);
+
+ set_i = set_from_updated_bmap(info[i].bmap, info[i].tab);
+ set_j = set_from_updated_bmap(info[j].bmap, info[j].tab);
+ ctx = isl_basic_map_get_ctx(info[i].bmap);
+ mat = isl_mat_alloc(ctx, 2 * (info[i].bmap->n_eq + info[j].bmap->n_eq) +
+ info[i].bmap->n_ineq + info[j].bmap->n_ineq,
+ 1 + total);
+ if (wraps_init(&wraps, mat, info, i, j) < 0)
+ goto error;
+ bound = isl_vec_alloc(ctx, 1 + total);
+ if (!set_i || !set_j || !bound)
+ goto error;
+
+ if (k % 2 == 0)
+ isl_seq_neg(bound->el, info[i].bmap->eq[k / 2], 1 + total);
+ else
+ isl_seq_cpy(bound->el, info[i].bmap->eq[k / 2], 1 + total);
+ isl_int_add_ui(bound->el[0], bound->el[0], 1);
+
+ isl_seq_cpy(wraps.mat->row[0], bound->el, 1 + total);
+ wraps.mat->n_row = 1;
+
+ if (add_wraps(&wraps, &info[j], bound->el, set_i) < 0)
+ goto error;
+ if (wraps.failed)
+ goto unbounded;
+
+ isl_int_sub_ui(bound->el[0], bound->el[0], 1);
+ isl_seq_neg(bound->el, bound->el, 1 + total);
+
+ isl_seq_cpy(wraps.mat->row[wraps.mat->n_row], bound->el, 1 + total);
+ wraps.mat->n_row++;
+
+ if (add_wraps(&wraps, &info[i], bound->el, set_j) < 0)
+ goto error;
+ if (wraps.failed)
+ goto unbounded;
+
+ change = fuse(i, j, info, wraps.mat, detect_equalities, 0);
+
+ if (0) {
+error: change = isl_change_error;
+ }
+unbounded:
+
+ wraps_free(&wraps);
+ isl_set_free(set_i);
+ isl_set_free(set_j);
+ isl_vec_free(bound);
+
+ return change;
+}
+
+/* Initialize the "eq" and "ineq" fields of "info".
+ */
+static void init_status(struct isl_coalesce_info *info)
+{
+ info->eq = info->ineq = NULL;
+}
+
+/* Set info->eq to the positions of the equalities of info->bmap
+ * with respect to the basic map represented by "tab".
+ * If info->eq has already been computed, then do not compute it again.
+ */
+static void set_eq_status_in(struct isl_coalesce_info *info,
+ struct isl_tab *tab)
+{
+ if (info->eq)
+ return;
+ info->eq = eq_status_in(info->bmap, tab);
+}
+
+/* Set info->ineq to the positions of the inequalities of info->bmap
+ * with respect to the basic map represented by "tab".
+ * If info->ineq has already been computed, then do not compute it again.
+ */
+static void set_ineq_status_in(struct isl_coalesce_info *info,
+ struct isl_tab *tab)
+{
+ if (info->ineq)
+ return;
+ info->ineq = ineq_status_in(info->bmap, info->tab, tab);
+}
+
+/* Free the memory allocated by the "eq" and "ineq" fields of "info".
+ * This function assumes that init_status has been called on "info" first,
+ * after which the "eq" and "ineq" fields may or may not have been
+ * assigned a newly allocated array.
+ */
+static void clear_status(struct isl_coalesce_info *info)
+{
+ free(info->eq);
+ free(info->ineq);
+}
+
+/* Are all inequality constraints of the basic map represented by "info"
+ * valid for the other basic map, except for a single constraint
+ * that is adjacent to an inequality constraint of the other basic map?
+ */
+static int all_ineq_valid_or_single_adj_ineq(struct isl_coalesce_info *info)
+{
+ int i;
+ int k = -1;
+
+ for (i = 0; i < info->bmap->n_ineq; ++i) {
+ if (info->ineq[i] == STATUS_REDUNDANT)
+ continue;
+ if (info->ineq[i] == STATUS_VALID)
+ continue;
+ if (info->ineq[i] != STATUS_ADJ_INEQ)
+ return 0;
+ if (k != -1)
+ return 0;
+ k = i;
+ }
+
+ return k != -1;
+}
+
+/* Basic map "i" has one or more equality constraints that separate it
+ * from basic map "j". Check if it happens to be an extension
+ * of basic map "j".
+ * In particular, check that all constraints of "j" are valid for "i",
+ * except for one inequality constraint that is adjacent
+ * to an inequality constraints of "i".
+ * If so, check for "i" being an extension of "j" by calling
+ * is_adj_ineq_extension.
+ *
+ * Clean up the memory allocated for keeping track of the status
+ * of the constraints before returning.
+ */
+static enum isl_change separating_equality(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ enum isl_change change = isl_change_none;
+
+ if (all(info[j].eq, 2 * info[j].bmap->n_eq, STATUS_VALID) &&
+ all_ineq_valid_or_single_adj_ineq(&info[j]))
+ change = is_adj_ineq_extension(j, i, info);
+
+ clear_status(&info[i]);
+ clear_status(&info[j]);
+ return change;
+}
+
+/* Check if the union of the given pair of basic maps
+ * can be represented by a single basic map.
+ * If so, replace the pair by the single basic map and return
+ * isl_change_drop_first, isl_change_drop_second or isl_change_fuse.
+ * Otherwise, return isl_change_none.
+ * The two basic maps are assumed to live in the same local space.
+ * The "eq" and "ineq" fields of info[i] and info[j] are assumed
+ * to have been initialized by the caller, either to NULL or
+ * to valid information.
+ *
+ * We first check the effect of each constraint of one basic map
+ * on the other basic map.
+ * The constraint may be
+ * redundant the constraint is redundant in its own
+ * basic map and should be ignore and removed
+ * in the end
+ * valid all (integer) points of the other basic map
+ * satisfy the constraint
+ * separate no (integer) point of the other basic map
+ * satisfies the constraint
+ * cut some but not all points of the other basic map
+ * satisfy the constraint
+ * adj_eq the given constraint is adjacent (on the outside)
+ * to an equality of the other basic map
+ * adj_ineq the given constraint is adjacent (on the outside)
+ * to an inequality of the other basic map
+ *
+ * We consider seven cases in which we can replace the pair by a single
+ * basic map. We ignore all "redundant" constraints.
+ *
+ * 1. all constraints of one basic map are valid
+ * => the other basic map is a subset and can be removed
+ *
+ * 2. all constraints of both basic maps are either "valid" or "cut"
+ * and the facets corresponding to the "cut" constraints
+ * of one of the basic maps lies entirely inside the other basic map
+ * => the pair can be replaced by a basic map consisting
+ * of the valid constraints in both basic maps
+ *
+ * 3. there is a single pair of adjacent inequalities
+ * (all other constraints are "valid")
+ * => the pair can be replaced by a basic map consisting
+ * of the valid constraints in both basic maps
+ *
+ * 4. one basic map has a single adjacent inequality, while the other
+ * constraints are "valid". The other basic map has some
+ * "cut" constraints, but replacing the adjacent inequality by
+ * its opposite and adding the valid constraints of the other
+ * basic map results in a subset of the other basic map
+ * => the pair can be replaced by a basic map consisting
+ * of the valid constraints in both basic maps
+ *
+ * 5. there is a single adjacent pair of an inequality and an equality,
+ * the other constraints of the basic map containing the inequality are
+ * "valid". Moreover, if the inequality the basic map is relaxed
+ * and then turned into an equality, then resulting facet lies
+ * entirely inside the other basic map
+ * => the pair can be replaced by the basic map containing
+ * the inequality, with the inequality relaxed.
+ *
+ * 6. there is a single inequality adjacent to an equality,
+ * the other constraints of the basic map containing the inequality are
+ * "valid". Moreover, the facets corresponding to both
+ * the inequality and the equality can be wrapped around their
+ * ridges to include the other basic map
+ * => the pair can be replaced by a basic map consisting
+ * of the valid constraints in both basic maps together
+ * with all wrapping constraints
+ *
+ * 7. one of the basic maps extends beyond the other by at most one.
+ * Moreover, the facets corresponding to the cut constraints and
+ * the pieces of the other basic map at offset one from these cut
+ * constraints can be wrapped around their ridges to include
+ * the union of the two basic maps
+ * => the pair can be replaced by a basic map consisting
+ * of the valid constraints in both basic maps together
+ * with all wrapping constraints
+ *
+ * 8. the two basic maps live in adjacent hyperplanes. In principle
+ * such sets can always be combined through wrapping, but we impose
+ * that there is only one such pair, to avoid overeager coalescing.
+ *
+ * Throughout the computation, we maintain a collection of tableaus
+ * corresponding to the basic maps. When the basic maps are dropped
+ * or combined, the tableaus are modified accordingly.
+ */
+static enum isl_change coalesce_local_pair_reuse(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ enum isl_change change = isl_change_none;
+
+ set_ineq_status_in(&info[i], info[j].tab);
+ if (info[i].bmap->n_ineq && !info[i].ineq)
+ goto error;
+ if (any_ineq(&info[i], STATUS_ERROR))
+ goto error;
+ if (any_ineq(&info[i], STATUS_SEPARATE))
+ goto done;
+
+ set_ineq_status_in(&info[j], info[i].tab);
+ if (info[j].bmap->n_ineq && !info[j].ineq)
+ goto error;
+ if (any_ineq(&info[j], STATUS_ERROR))
+ goto error;
+ if (any_ineq(&info[j], STATUS_SEPARATE))
+ goto done;
+
+ set_eq_status_in(&info[i], info[j].tab);
+ if (info[i].bmap->n_eq && !info[i].eq)
+ goto error;
+ if (any_eq(&info[i], STATUS_ERROR))
+ goto error;
+
+ set_eq_status_in(&info[j], info[i].tab);
+ if (info[j].bmap->n_eq && !info[j].eq)
+ goto error;
+ if (any_eq(&info[j], STATUS_ERROR))
+ goto error;
+
+ if (any_eq(&info[i], STATUS_SEPARATE))
+ return separating_equality(i, j, info);
+ if (any_eq(&info[j], STATUS_SEPARATE))
+ return separating_equality(j, i, info);
+
+ if (all(info[i].eq, 2 * info[i].bmap->n_eq, STATUS_VALID) &&
+ all(info[i].ineq, info[i].bmap->n_ineq, STATUS_VALID)) {
+ drop(&info[j]);
+ change = isl_change_drop_second;
+ } else if (all(info[j].eq, 2 * info[j].bmap->n_eq, STATUS_VALID) &&
+ all(info[j].ineq, info[j].bmap->n_ineq, STATUS_VALID)) {
+ drop(&info[i]);
+ change = isl_change_drop_first;
+ } else if (any_eq(&info[i], STATUS_ADJ_EQ)) {
+ change = check_eq_adj_eq(i, j, info);
+ } else if (any_eq(&info[j], STATUS_ADJ_EQ)) {
+ change = check_eq_adj_eq(j, i, info);
+ } else if (any_eq(&info[i], STATUS_ADJ_INEQ) ||
+ any_eq(&info[j], STATUS_ADJ_INEQ)) {
+ change = check_adj_eq(i, j, info);
+ } else if (any_ineq(&info[i], STATUS_ADJ_EQ)) {
+ change = check_ineq_adj_eq(i, j, info);
+ } else if (any_ineq(&info[j], STATUS_ADJ_EQ)) {
+ change = check_ineq_adj_eq(j, i, info);
+ } else if (any_ineq(&info[i], STATUS_ADJ_INEQ) ||
+ any_ineq(&info[j], STATUS_ADJ_INEQ)) {
+ change = check_adj_ineq(i, j, info);
+ } else {
+ if (!any_eq(&info[i], STATUS_CUT) &&
+ !any_eq(&info[j], STATUS_CUT))
+ change = check_facets(i, j, info);
+ if (change == isl_change_none)
+ change = check_wrap(i, j, info);
+ }
+
+done:
+ clear_status(&info[i]);
+ clear_status(&info[j]);
+ return change;
+error:
+ clear_status(&info[i]);
+ clear_status(&info[j]);
+ return isl_change_error;
+}
+
+/* Check if the union of the given pair of basic maps
+ * can be represented by a single basic map.
+ * If so, replace the pair by the single basic map and return
+ * isl_change_drop_first, isl_change_drop_second or isl_change_fuse.
+ * Otherwise, return isl_change_none.
+ * The two basic maps are assumed to live in the same local space.
+ */
+static enum isl_change coalesce_local_pair(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ init_status(&info[i]);
+ init_status(&info[j]);
+ return coalesce_local_pair_reuse(i, j, info);
+}
+
+/* Shift the integer division at position "div" of the basic map
+ * represented by "info" by "shift".
+ *
+ * That is, if the integer division has the form
+ *
+ * floor(f(x)/d)
+ *
+ * then replace it by
+ *
+ * floor((f(x) + shift * d)/d) - shift
+ */
+static isl_stat shift_div(struct isl_coalesce_info *info, int div,
+ isl_int shift)
+{
+ isl_size total, n_div;
+
+ info->bmap = isl_basic_map_shift_div(info->bmap, div, 0, shift);
+ if (!info->bmap)
+ return isl_stat_error;
+
+ total = isl_basic_map_dim(info->bmap, isl_dim_all);
+ n_div = isl_basic_map_dim(info->bmap, isl_dim_div);
+ if (total < 0 || n_div < 0)
+ return isl_stat_error;
+ total -= n_div;
+ if (isl_tab_shift_var(info->tab, total + div, shift) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* If the integer division at position "div" is defined by an equality,
+ * i.e., a stride constraint, then change the integer division expression
+ * to have a constant term equal to zero.
+ *
+ * Let the equality constraint be
+ *
+ * c + f + m a = 0
+ *
+ * The integer division expression is then typically of the form
+ *
+ * a = floor((-f - c')/m)
+ *
+ * The integer division is first shifted by t = floor(c/m),
+ * turning the equality constraint into
+ *
+ * c - m floor(c/m) + f + m a' = 0
+ *
+ * i.e.,
+ *
+ * (c mod m) + f + m a' = 0
+ *
+ * That is,
+ *
+ * a' = (-f - (c mod m))/m = floor((-f)/m)
+ *
+ * because a' is an integer and 0 <= (c mod m) < m.
+ * The constant term of a' can therefore be zeroed out,
+ * but only if the integer division expression is of the expected form.
+ */
+static isl_stat normalize_stride_div(struct isl_coalesce_info *info, int div)
+{
+ isl_bool defined, valid;
+ isl_stat r;
+ isl_constraint *c;
+ isl_int shift, stride;
+
+ defined = isl_basic_map_has_defining_equality(info->bmap, isl_dim_div,
+ div, &c);
+ if (defined < 0)
+ return isl_stat_error;
+ if (!defined)
+ return isl_stat_ok;
+ if (!c)
+ return isl_stat_error;
+ valid = isl_constraint_is_div_equality(c, div);
+ isl_int_init(shift);
+ isl_int_init(stride);
+ isl_constraint_get_constant(c, &shift);
+ isl_constraint_get_coefficient(c, isl_dim_div, div, &stride);
+ isl_int_fdiv_q(shift, shift, stride);
+ r = shift_div(info, div, shift);
+ isl_int_clear(stride);
+ isl_int_clear(shift);
+ isl_constraint_free(c);
+ if (r < 0 || valid < 0)
+ return isl_stat_error;
+ if (!valid)
+ return isl_stat_ok;
+ info->bmap = isl_basic_map_set_div_expr_constant_num_si_inplace(
+ info->bmap, div, 0);
+ if (!info->bmap)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* The basic maps represented by "info1" and "info2" are known
+ * to have the same number of integer divisions.
+ * Check if pairs of integer divisions are equal to each other
+ * despite the fact that they differ by a rational constant.
+ *
+ * In particular, look for any pair of integer divisions that
+ * only differ in their constant terms.
+ * If either of these integer divisions is defined
+ * by stride constraints, then modify it to have a zero constant term.
+ * If both are defined by stride constraints then in the end they will have
+ * the same (zero) constant term.
+ */
+static isl_stat harmonize_stride_divs(struct isl_coalesce_info *info1,
+ struct isl_coalesce_info *info2)
+{
+ int i;
+ isl_size n;
+
+ n = isl_basic_map_dim(info1->bmap, isl_dim_div);
+ if (n < 0)
+ return isl_stat_error;
+ for (i = 0; i < n; ++i) {
+ isl_bool known, harmonize;
+
+ known = isl_basic_map_div_is_known(info1->bmap, i);
+ if (known >= 0 && known)
+ known = isl_basic_map_div_is_known(info2->bmap, i);
+ if (known < 0)
+ return isl_stat_error;
+ if (!known)
+ continue;
+ harmonize = isl_basic_map_equal_div_expr_except_constant(
+ info1->bmap, i, info2->bmap, i);
+ if (harmonize < 0)
+ return isl_stat_error;
+ if (!harmonize)
+ continue;
+ if (normalize_stride_div(info1, i) < 0)
+ return isl_stat_error;
+ if (normalize_stride_div(info2, i) < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* If "shift" is an integer constant, then shift the integer division
+ * at position "div" of the basic map represented by "info" by "shift".
+ * If "shift" is not an integer constant, then do nothing.
+ * If "shift" is equal to zero, then no shift needs to be performed either.
+ *
+ * That is, if the integer division has the form
+ *
+ * floor(f(x)/d)
+ *
+ * then replace it by
+ *
+ * floor((f(x) + shift * d)/d) - shift
+ */
+static isl_stat shift_if_cst_int(struct isl_coalesce_info *info, int div,
+ __isl_keep isl_aff *shift)
+{
+ isl_bool cst;
+ isl_stat r;
+ isl_int d;
+ isl_val *c;
+
+ cst = isl_aff_is_cst(shift);
+ if (cst < 0 || !cst)
+ return cst < 0 ? isl_stat_error : isl_stat_ok;
+
+ c = isl_aff_get_constant_val(shift);
+ cst = isl_val_is_int(c);
+ if (cst >= 0 && cst)
+ cst = isl_bool_not(isl_val_is_zero(c));
+ if (cst < 0 || !cst) {
+ isl_val_free(c);
+ return cst < 0 ? isl_stat_error : isl_stat_ok;
+ }
+
+ isl_int_init(d);
+ r = isl_val_get_num_isl_int(c, &d);
+ if (r >= 0)
+ r = shift_div(info, div, d);
+ isl_int_clear(d);
+
+ isl_val_free(c);
+
+ return r;
+}
+
+/* Check if some of the divs in the basic map represented by "info1"
+ * are shifts of the corresponding divs in the basic map represented
+ * by "info2", taking into account the equality constraints "eq1" of "info1"
+ * and "eq2" of "info2". If so, align them with those of "info2".
+ * "info1" and "info2" are assumed to have the same number
+ * of integer divisions.
+ *
+ * An integer division is considered to be a shift of another integer
+ * division if, after simplification with respect to the equality
+ * constraints of the other basic map, one is equal to the other
+ * plus a constant.
+ *
+ * In particular, for each pair of integer divisions, if both are known,
+ * have the same denominator and are not already equal to each other,
+ * simplify each with respect to the equality constraints
+ * of the other basic map. If the difference is an integer constant,
+ * then move this difference outside.
+ * That is, if, after simplification, one integer division is of the form
+ *
+ * floor((f(x) + c_1)/d)
+ *
+ * while the other is of the form
+ *
+ * floor((f(x) + c_2)/d)
+ *
+ * and n = (c_2 - c_1)/d is an integer, then replace the first
+ * integer division by
+ *
+ * floor((f_1(x) + c_1 + n * d)/d) - n,
+ *
+ * where floor((f_1(x) + c_1 + n * d)/d) = floor((f2(x) + c_2)/d)
+ * after simplification with respect to the equality constraints.
+ */
+static isl_stat harmonize_divs_with_hulls(struct isl_coalesce_info *info1,
+ struct isl_coalesce_info *info2, __isl_keep isl_basic_set *eq1,
+ __isl_keep isl_basic_set *eq2)
+{
+ int i;
+ isl_size total;
+ isl_local_space *ls1, *ls2;
+
+ total = isl_basic_map_dim(info1->bmap, isl_dim_all);
+ if (total < 0)
+ return isl_stat_error;
+ ls1 = isl_local_space_wrap(isl_basic_map_get_local_space(info1->bmap));
+ ls2 = isl_local_space_wrap(isl_basic_map_get_local_space(info2->bmap));
+ for (i = 0; i < info1->bmap->n_div; ++i) {
+ isl_stat r;
+ isl_aff *div1, *div2;
+
+ if (!isl_local_space_div_is_known(ls1, i) ||
+ !isl_local_space_div_is_known(ls2, i))
+ continue;
+ if (isl_int_ne(info1->bmap->div[i][0], info2->bmap->div[i][0]))
+ continue;
+ if (isl_seq_eq(info1->bmap->div[i] + 1,
+ info2->bmap->div[i] + 1, 1 + total))
+ continue;
+ div1 = isl_local_space_get_div(ls1, i);
+ div2 = isl_local_space_get_div(ls2, i);
+ div1 = isl_aff_substitute_equalities(div1,
+ isl_basic_set_copy(eq2));
+ div2 = isl_aff_substitute_equalities(div2,
+ isl_basic_set_copy(eq1));
+ div2 = isl_aff_sub(div2, div1);
+ r = shift_if_cst_int(info1, i, div2);
+ isl_aff_free(div2);
+ if (r < 0)
+ break;
+ }
+ isl_local_space_free(ls1);
+ isl_local_space_free(ls2);
+
+ if (i < info1->bmap->n_div)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Check if some of the divs in the basic map represented by "info1"
+ * are shifts of the corresponding divs in the basic map represented
+ * by "info2". If so, align them with those of "info2".
+ * Only do this if "info1" and "info2" have the same number
+ * of integer divisions.
+ *
+ * An integer division is considered to be a shift of another integer
+ * division if, after simplification with respect to the equality
+ * constraints of the other basic map, one is equal to the other
+ * plus a constant.
+ *
+ * First check if pairs of integer divisions are equal to each other
+ * despite the fact that they differ by a rational constant.
+ * If so, try and arrange for them to have the same constant term.
+ *
+ * Then, extract the equality constraints and continue with
+ * harmonize_divs_with_hulls.
+ *
+ * If the equality constraints of both basic maps are the same,
+ * then there is no need to perform any shifting since
+ * the coefficients of the integer divisions should have been
+ * reduced in the same way.
+ */
+static isl_stat harmonize_divs(struct isl_coalesce_info *info1,
+ struct isl_coalesce_info *info2)
+{
+ isl_bool equal;
+ isl_basic_map *bmap1, *bmap2;
+ isl_basic_set *eq1, *eq2;
+ isl_stat r;
+
+ if (!info1->bmap || !info2->bmap)
+ return isl_stat_error;
+
+ if (info1->bmap->n_div != info2->bmap->n_div)
+ return isl_stat_ok;
+ if (info1->bmap->n_div == 0)
+ return isl_stat_ok;
+
+ if (harmonize_stride_divs(info1, info2) < 0)
+ return isl_stat_error;
+
+ bmap1 = isl_basic_map_copy(info1->bmap);
+ bmap2 = isl_basic_map_copy(info2->bmap);
+ eq1 = isl_basic_map_wrap(isl_basic_map_plain_affine_hull(bmap1));
+ eq2 = isl_basic_map_wrap(isl_basic_map_plain_affine_hull(bmap2));
+ equal = isl_basic_set_plain_is_equal(eq1, eq2);
+ if (equal < 0)
+ r = isl_stat_error;
+ else if (equal)
+ r = isl_stat_ok;
+ else
+ r = harmonize_divs_with_hulls(info1, info2, eq1, eq2);
+ isl_basic_set_free(eq1);
+ isl_basic_set_free(eq2);
+
+ return r;
+}
+
+/* Do the two basic maps live in the same local space, i.e.,
+ * do they have the same (known) divs?
+ * If either basic map has any unknown divs, then we can only assume
+ * that they do not live in the same local space.
+ */
+static isl_bool same_divs(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2)
+{
+ int i;
+ isl_bool known;
+ isl_size total;
+
+ if (!bmap1 || !bmap2)
+ return isl_bool_error;
+ if (bmap1->n_div != bmap2->n_div)
+ return isl_bool_false;
+
+ if (bmap1->n_div == 0)
+ return isl_bool_true;
+
+ known = isl_basic_map_divs_known(bmap1);
+ if (known < 0 || !known)
+ return known;
+ known = isl_basic_map_divs_known(bmap2);
+ if (known < 0 || !known)
+ return known;
+
+ total = isl_basic_map_dim(bmap1, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ for (i = 0; i < bmap1->n_div; ++i)
+ if (!isl_seq_eq(bmap1->div[i], bmap2->div[i], 2 + total))
+ return isl_bool_false;
+
+ return isl_bool_true;
+}
+
+/* Assuming that "tab" contains the equality constraints and
+ * the initial inequality constraints of "bmap", copy the remaining
+ * inequality constraints of "bmap" to "Tab".
+ */
+static isl_stat copy_ineq(struct isl_tab *tab, __isl_keep isl_basic_map *bmap)
+{
+ int i, n_ineq;
+
+ if (!bmap)
+ return isl_stat_error;
+
+ n_ineq = tab->n_con - tab->n_eq;
+ for (i = n_ineq; i < bmap->n_ineq; ++i)
+ if (isl_tab_add_ineq(tab, bmap->ineq[i]) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Description of an integer division that is added
+ * during an expansion.
+ * "pos" is the position of the corresponding variable.
+ * "cst" indicates whether this integer division has a fixed value.
+ * "val" contains the fixed value, if the value is fixed.
+ */
+struct isl_expanded {
+ int pos;
+ isl_bool cst;
+ isl_int val;
+};
+
+/* For each of the "n" integer division variables "expanded",
+ * if the variable has a fixed value, then add two inequality
+ * constraints expressing the fixed value.
+ * Otherwise, add the corresponding div constraints.
+ * The caller is responsible for removing the div constraints
+ * that it added for all these "n" integer divisions.
+ *
+ * The div constraints and the pair of inequality constraints
+ * forcing the fixed value cannot both be added for a given variable
+ * as the combination may render some of the original constraints redundant.
+ * These would then be ignored during the coalescing detection,
+ * while they could remain in the fused result.
+ *
+ * The two added inequality constraints are
+ *
+ * -a + v >= 0
+ * a - v >= 0
+ *
+ * with "a" the variable and "v" its fixed value.
+ * The facet corresponding to one of these two constraints is selected
+ * in the tableau to ensure that the pair of inequality constraints
+ * is treated as an equality constraint.
+ *
+ * The information in info->ineq is thrown away because it was
+ * computed in terms of div constraints, while some of those
+ * have now been replaced by these pairs of inequality constraints.
+ */
+static isl_stat fix_constant_divs(struct isl_coalesce_info *info,
+ int n, struct isl_expanded *expanded)
+{
+ unsigned o_div;
+ int i;
+ isl_vec *ineq;
+
+ o_div = isl_basic_map_offset(info->bmap, isl_dim_div) - 1;
+ ineq = isl_vec_alloc(isl_tab_get_ctx(info->tab), 1 + info->tab->n_var);
+ if (!ineq)
+ return isl_stat_error;
+ isl_seq_clr(ineq->el + 1, info->tab->n_var);
+
+ for (i = 0; i < n; ++i) {
+ if (!expanded[i].cst) {
+ info->bmap = isl_basic_map_extend_constraints(
+ info->bmap, 0, 2);
+ info->bmap = isl_basic_map_add_div_constraints(
+ info->bmap, expanded[i].pos - o_div);
+ } else {
+ isl_int_set_si(ineq->el[1 + expanded[i].pos], -1);
+ isl_int_set(ineq->el[0], expanded[i].val);
+ info->bmap = isl_basic_map_add_ineq(info->bmap,
+ ineq->el);
+ isl_int_set_si(ineq->el[1 + expanded[i].pos], 1);
+ isl_int_neg(ineq->el[0], expanded[i].val);
+ info->bmap = isl_basic_map_add_ineq(info->bmap,
+ ineq->el);
+ isl_int_set_si(ineq->el[1 + expanded[i].pos], 0);
+ }
+ if (copy_ineq(info->tab, info->bmap) < 0)
+ break;
+ if (expanded[i].cst &&
+ isl_tab_select_facet(info->tab, info->tab->n_con - 1) < 0)
+ break;
+ }
+
+ isl_vec_free(ineq);
+
+ clear_status(info);
+ init_status(info);
+
+ return i < n ? isl_stat_error : isl_stat_ok;
+}
+
+/* Insert the "n" integer division variables "expanded"
+ * into info->tab and info->bmap and
+ * update info->ineq with respect to the redundant constraints
+ * in the resulting tableau.
+ * "bmap" contains the result of this insertion in info->bmap,
+ * while info->bmap is the original version
+ * of "bmap", i.e., the one that corresponds to the current
+ * state of info->tab. The number of constraints in info->bmap
+ * is assumed to be the same as the number of constraints
+ * in info->tab. This is required to be able to detect
+ * the extra constraints in "bmap".
+ *
+ * In particular, introduce extra variables corresponding
+ * to the extra integer divisions and add the div constraints
+ * that were added to "bmap" after info->tab was created
+ * from info->bmap.
+ * Furthermore, check if these extra integer divisions happen
+ * to attain a fixed integer value in info->tab.
+ * If so, replace the corresponding div constraints by pairs
+ * of inequality constraints that fix these
+ * integer divisions to their single integer values.
+ * Replace info->bmap by "bmap" to match the changes to info->tab.
+ * info->ineq was computed without a tableau and therefore
+ * does not take into account the redundant constraints
+ * in the tableau. Mark them here.
+ * There is no need to check the newly added div constraints
+ * since they cannot be redundant.
+ * The redundancy check is not performed when constants have been discovered
+ * since info->ineq is completely thrown away in this case.
+ */
+static isl_stat tab_insert_divs(struct isl_coalesce_info *info,
+ int n, struct isl_expanded *expanded, __isl_take isl_basic_map *bmap)
+{
+ int i, n_ineq;
+ unsigned n_eq;
+ struct isl_tab_undo *snap;
+ int any;
+
+ if (!bmap)
+ return isl_stat_error;
+ if (info->bmap->n_eq + info->bmap->n_ineq != info->tab->n_con)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_internal,
+ "original tableau does not correspond "
+ "to original basic map", goto error);
+
+ if (isl_tab_extend_vars(info->tab, n) < 0)
+ goto error;
+ if (isl_tab_extend_cons(info->tab, 2 * n) < 0)
+ goto error;
+
+ for (i = 0; i < n; ++i) {
+ if (isl_tab_insert_var(info->tab, expanded[i].pos) < 0)
+ goto error;
+ }
+
+ snap = isl_tab_snap(info->tab);
+
+ n_ineq = info->tab->n_con - info->tab->n_eq;
+ if (copy_ineq(info->tab, bmap) < 0)
+ goto error;
+
+ isl_basic_map_free(info->bmap);
+ info->bmap = bmap;
+
+ any = 0;
+ for (i = 0; i < n; ++i) {
+ expanded[i].cst = isl_tab_is_constant(info->tab,
+ expanded[i].pos, &expanded[i].val);
+ if (expanded[i].cst < 0)
+ return isl_stat_error;
+ if (expanded[i].cst)
+ any = 1;
+ }
+
+ if (any) {
+ if (isl_tab_rollback(info->tab, snap) < 0)
+ return isl_stat_error;
+ info->bmap = isl_basic_map_cow(info->bmap);
+ info->bmap = isl_basic_map_free_inequality(info->bmap, 2 * n);
+ if (info->bmap < 0)
+ return isl_stat_error;
+
+ return fix_constant_divs(info, n, expanded);
+ }
+
+ n_eq = info->bmap->n_eq;
+ for (i = 0; i < n_ineq; ++i) {
+ if (isl_tab_is_redundant(info->tab, n_eq + i))
+ info->ineq[i] = STATUS_REDUNDANT;
+ }
+
+ return isl_stat_ok;
+error:
+ isl_basic_map_free(bmap);
+ return isl_stat_error;
+}
+
+/* Expand info->tab and info->bmap in the same way "bmap" was expanded
+ * in isl_basic_map_expand_divs using the expansion "exp" and
+ * update info->ineq with respect to the redundant constraints
+ * in the resulting tableau. info->bmap is the original version
+ * of "bmap", i.e., the one that corresponds to the current
+ * state of info->tab. The number of constraints in info->bmap
+ * is assumed to be the same as the number of constraints
+ * in info->tab. This is required to be able to detect
+ * the extra constraints in "bmap".
+ *
+ * Extract the positions where extra local variables are introduced
+ * from "exp" and call tab_insert_divs.
+ */
+static isl_stat expand_tab(struct isl_coalesce_info *info, int *exp,
+ __isl_take isl_basic_map *bmap)
+{
+ isl_ctx *ctx;
+ struct isl_expanded *expanded;
+ int i, j, k, n;
+ int extra_var;
+ isl_size total, n_div;
+ unsigned pos;
+ isl_stat r;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (total < 0 || n_div < 0)
+ return isl_stat_error;
+ pos = total - n_div;
+ extra_var = total - info->tab->n_var;
+ n = n_div - extra_var;
+
+ ctx = isl_basic_map_get_ctx(bmap);
+ expanded = isl_calloc_array(ctx, struct isl_expanded, extra_var);
+ if (extra_var && !expanded)
+ goto error;
+
+ i = 0;
+ k = 0;
+ for (j = 0; j < n_div; ++j) {
+ if (i < n && exp[i] == j) {
+ ++i;
+ continue;
+ }
+ expanded[k++].pos = pos + j;
+ }
+
+ for (k = 0; k < extra_var; ++k)
+ isl_int_init(expanded[k].val);
+
+ r = tab_insert_divs(info, extra_var, expanded, bmap);
+
+ for (k = 0; k < extra_var; ++k)
+ isl_int_clear(expanded[k].val);
+ free(expanded);
+
+ return r;
+error:
+ isl_basic_map_free(bmap);
+ return isl_stat_error;
+}
+
+/* Check if the union of the basic maps represented by info[i] and info[j]
+ * can be represented by a single basic map,
+ * after expanding the divs of info[i] to match those of info[j].
+ * If so, replace the pair by the single basic map and return
+ * isl_change_drop_first, isl_change_drop_second or isl_change_fuse.
+ * Otherwise, return isl_change_none.
+ *
+ * The caller has already checked for info[j] being a subset of info[i].
+ * If some of the divs of info[j] are unknown, then the expanded info[i]
+ * will not have the corresponding div constraints. The other patterns
+ * therefore cannot apply. Skip the computation in this case.
+ *
+ * The expansion is performed using the divs "div" and expansion "exp"
+ * computed by the caller.
+ * info[i].bmap has already been expanded and the result is passed in
+ * as "bmap".
+ * The "eq" and "ineq" fields of info[i] reflect the status of
+ * the constraints of the expanded "bmap" with respect to info[j].tab.
+ * However, inequality constraints that are redundant in info[i].tab
+ * have not yet been marked as such because no tableau was available.
+ *
+ * Replace info[i].bmap by "bmap" and expand info[i].tab as well,
+ * updating info[i].ineq with respect to the redundant constraints.
+ * Then try and coalesce the expanded info[i] with info[j],
+ * reusing the information in info[i].eq and info[i].ineq.
+ * If this does not result in any coalescing or if it results in info[j]
+ * getting dropped (which should not happen in practice, since the case
+ * of info[j] being a subset of info[i] has already been checked by
+ * the caller), then revert info[i] to its original state.
+ */
+static enum isl_change coalesce_expand_tab_divs(__isl_take isl_basic_map *bmap,
+ int i, int j, struct isl_coalesce_info *info, __isl_keep isl_mat *div,
+ int *exp)
+{
+ isl_bool known;
+ isl_basic_map *bmap_i;
+ struct isl_tab_undo *snap;
+ enum isl_change change = isl_change_none;
+
+ known = isl_basic_map_divs_known(info[j].bmap);
+ if (known < 0 || !known) {
+ clear_status(&info[i]);
+ isl_basic_map_free(bmap);
+ return known < 0 ? isl_change_error : isl_change_none;
+ }
+
+ bmap_i = isl_basic_map_copy(info[i].bmap);
+ snap = isl_tab_snap(info[i].tab);
+ if (expand_tab(&info[i], exp, bmap) < 0)
+ change = isl_change_error;
+
+ init_status(&info[j]);
+ if (change == isl_change_none)
+ change = coalesce_local_pair_reuse(i, j, info);
+ else
+ clear_status(&info[i]);
+ if (change != isl_change_none && change != isl_change_drop_second) {
+ isl_basic_map_free(bmap_i);
+ } else {
+ isl_basic_map_free(info[i].bmap);
+ info[i].bmap = bmap_i;
+
+ if (isl_tab_rollback(info[i].tab, snap) < 0)
+ change = isl_change_error;
+ }
+
+ return change;
+}
+
+/* Check if the union of "bmap" and the basic map represented by info[j]
+ * can be represented by a single basic map,
+ * after expanding the divs of "bmap" to match those of info[j].
+ * If so, replace the pair by the single basic map and return
+ * isl_change_drop_first, isl_change_drop_second or isl_change_fuse.
+ * Otherwise, return isl_change_none.
+ *
+ * In particular, check if the expanded "bmap" contains the basic map
+ * represented by the tableau info[j].tab.
+ * The expansion is performed using the divs "div" and expansion "exp"
+ * computed by the caller.
+ * Then we check if all constraints of the expanded "bmap" are valid for
+ * info[j].tab.
+ *
+ * If "i" is not equal to -1, then "bmap" is equal to info[i].bmap.
+ * In this case, the positions of the constraints of info[i].bmap
+ * with respect to the basic map represented by info[j] are stored
+ * in info[i].
+ *
+ * If the expanded "bmap" does not contain the basic map
+ * represented by the tableau info[j].tab and if "i" is not -1,
+ * i.e., if the original "bmap" is info[i].bmap, then expand info[i].tab
+ * as well and check if that results in coalescing.
+ */
+static enum isl_change coalesce_with_expanded_divs(
+ __isl_keep isl_basic_map *bmap, int i, int j,
+ struct isl_coalesce_info *info, __isl_keep isl_mat *div, int *exp)
+{
+ enum isl_change change = isl_change_none;
+ struct isl_coalesce_info info_local, *info_i;
+
+ info_i = i >= 0 ? &info[i] : &info_local;
+ init_status(info_i);
+ bmap = isl_basic_map_copy(bmap);
+ bmap = isl_basic_map_expand_divs(bmap, isl_mat_copy(div), exp);
+ bmap = isl_basic_map_mark_final(bmap);
+
+ if (!bmap)
+ goto error;
+
+ info_local.bmap = bmap;
+ info_i->eq = eq_status_in(bmap, info[j].tab);
+ if (bmap->n_eq && !info_i->eq)
+ goto error;
+ if (any_eq(info_i, STATUS_ERROR))
+ goto error;
+ if (any_eq(info_i, STATUS_SEPARATE))
+ goto done;
+
+ info_i->ineq = ineq_status_in(bmap, NULL, info[j].tab);
+ if (bmap->n_ineq && !info_i->ineq)
+ goto error;
+ if (any_ineq(info_i, STATUS_ERROR))
+ goto error;
+ if (any_ineq(info_i, STATUS_SEPARATE))
+ goto done;
+
+ if (all(info_i->eq, 2 * bmap->n_eq, STATUS_VALID) &&
+ all(info_i->ineq, bmap->n_ineq, STATUS_VALID)) {
+ drop(&info[j]);
+ change = isl_change_drop_second;
+ }
+
+ if (change == isl_change_none && i != -1)
+ return coalesce_expand_tab_divs(bmap, i, j, info, div, exp);
+
+done:
+ isl_basic_map_free(bmap);
+ clear_status(info_i);
+ return change;
+error:
+ isl_basic_map_free(bmap);
+ clear_status(info_i);
+ return isl_change_error;
+}
+
+/* Check if the union of "bmap_i" and the basic map represented by info[j]
+ * can be represented by a single basic map,
+ * after aligning the divs of "bmap_i" to match those of info[j].
+ * If so, replace the pair by the single basic map and return
+ * isl_change_drop_first, isl_change_drop_second or isl_change_fuse.
+ * Otherwise, return isl_change_none.
+ *
+ * In particular, check if "bmap_i" contains the basic map represented by
+ * info[j] after aligning the divs of "bmap_i" to those of info[j].
+ * Note that this can only succeed if the number of divs of "bmap_i"
+ * is smaller than (or equal to) the number of divs of info[j].
+ *
+ * We first check if the divs of "bmap_i" are all known and form a subset
+ * of those of info[j].bmap. If so, we pass control over to
+ * coalesce_with_expanded_divs.
+ *
+ * If "i" is not equal to -1, then "bmap" is equal to info[i].bmap.
+ */
+static enum isl_change coalesce_after_aligning_divs(
+ __isl_keep isl_basic_map *bmap_i, int i, int j,
+ struct isl_coalesce_info *info)
+{
+ isl_bool known;
+ isl_mat *div_i, *div_j, *div;
+ int *exp1 = NULL;
+ int *exp2 = NULL;
+ isl_ctx *ctx;
+ enum isl_change change;
+
+ known = isl_basic_map_divs_known(bmap_i);
+ if (known < 0)
+ return isl_change_error;
+ if (!known)
+ return isl_change_none;
+
+ ctx = isl_basic_map_get_ctx(bmap_i);
+
+ div_i = isl_basic_map_get_divs(bmap_i);
+ div_j = isl_basic_map_get_divs(info[j].bmap);
+
+ if (!div_i || !div_j)
+ goto error;
+
+ exp1 = isl_alloc_array(ctx, int, div_i->n_row);
+ exp2 = isl_alloc_array(ctx, int, div_j->n_row);
+ if ((div_i->n_row && !exp1) || (div_j->n_row && !exp2))
+ goto error;
+
+ div = isl_merge_divs(div_i, div_j, exp1, exp2);
+ if (!div)
+ goto error;
+
+ if (div->n_row == div_j->n_row)
+ change = coalesce_with_expanded_divs(bmap_i,
+ i, j, info, div, exp1);
+ else
+ change = isl_change_none;
+
+ isl_mat_free(div);
+
+ isl_mat_free(div_i);
+ isl_mat_free(div_j);
+
+ free(exp2);
+ free(exp1);
+
+ return change;
+error:
+ isl_mat_free(div_i);
+ isl_mat_free(div_j);
+ free(exp1);
+ free(exp2);
+ return isl_change_error;
+}
+
+/* Check if basic map "j" is a subset of basic map "i" after
+ * exploiting the extra equalities of "j" to simplify the divs of "i".
+ * If so, remove basic map "j" and return isl_change_drop_second.
+ *
+ * If "j" does not have any equalities or if they are the same
+ * as those of "i", then we cannot exploit them to simplify the divs.
+ * Similarly, if there are no divs in "i", then they cannot be simplified.
+ * If, on the other hand, the affine hulls of "i" and "j" do not intersect,
+ * then "j" cannot be a subset of "i".
+ *
+ * Otherwise, we intersect "i" with the affine hull of "j" and then
+ * check if "j" is a subset of the result after aligning the divs.
+ * If so, then "j" is definitely a subset of "i" and can be removed.
+ * Note that if after intersection with the affine hull of "j".
+ * "i" still has more divs than "j", then there is no way we can
+ * align the divs of "i" to those of "j".
+ */
+static enum isl_change coalesce_subset_with_equalities(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ isl_basic_map *hull_i, *hull_j, *bmap_i;
+ int equal, empty;
+ enum isl_change change;
+
+ if (info[j].bmap->n_eq == 0)
+ return isl_change_none;
+ if (info[i].bmap->n_div == 0)
+ return isl_change_none;
+
+ hull_i = isl_basic_map_copy(info[i].bmap);
+ hull_i = isl_basic_map_plain_affine_hull(hull_i);
+ hull_j = isl_basic_map_copy(info[j].bmap);
+ hull_j = isl_basic_map_plain_affine_hull(hull_j);
+
+ hull_j = isl_basic_map_intersect(hull_j, isl_basic_map_copy(hull_i));
+ equal = isl_basic_map_plain_is_equal(hull_i, hull_j);
+ empty = isl_basic_map_plain_is_empty(hull_j);
+ isl_basic_map_free(hull_i);
+
+ if (equal < 0 || equal || empty < 0 || empty) {
+ isl_basic_map_free(hull_j);
+ if (equal < 0 || empty < 0)
+ return isl_change_error;
+ return isl_change_none;
+ }
+
+ bmap_i = isl_basic_map_copy(info[i].bmap);
+ bmap_i = isl_basic_map_intersect(bmap_i, hull_j);
+ if (!bmap_i)
+ return isl_change_error;
+
+ if (bmap_i->n_div > info[j].bmap->n_div) {
+ isl_basic_map_free(bmap_i);
+ return isl_change_none;
+ }
+
+ change = coalesce_after_aligning_divs(bmap_i, -1, j, info);
+
+ isl_basic_map_free(bmap_i);
+
+ return change;
+}
+
+/* Check if the union of the basic maps represented by info[i] and info[j]
+ * can be represented by a single basic map, by aligning or equating
+ * their integer divisions.
+ * If so, replace the pair by the single basic map and return
+ * isl_change_drop_first, isl_change_drop_second or isl_change_fuse.
+ * Otherwise, return isl_change_none.
+ *
+ * Note that we only perform any test if the number of divs is different
+ * in the two basic maps. In case the number of divs is the same,
+ * we have already established that the divs are different
+ * in the two basic maps.
+ * In particular, if the number of divs of basic map i is smaller than
+ * the number of divs of basic map j, then we check if j is a subset of i
+ * and vice versa.
+ */
+static enum isl_change coalesce_divs(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ enum isl_change change = isl_change_none;
+
+ if (info[i].bmap->n_div < info[j].bmap->n_div)
+ change = coalesce_after_aligning_divs(info[i].bmap, i, j, info);
+ if (change != isl_change_none)
+ return change;
+
+ if (info[j].bmap->n_div < info[i].bmap->n_div)
+ change = coalesce_after_aligning_divs(info[j].bmap, j, i, info);
+ if (change != isl_change_none)
+ return invert_change(change);
+
+ change = coalesce_subset_with_equalities(i, j, info);
+ if (change != isl_change_none)
+ return change;
+
+ change = coalesce_subset_with_equalities(j, i, info);
+ if (change != isl_change_none)
+ return invert_change(change);
+
+ return isl_change_none;
+}
+
+/* Does "bmap" involve any divs that themselves refer to divs?
+ */
+static isl_bool has_nested_div(__isl_keep isl_basic_map *bmap)
+{
+ int i;
+ isl_size total;
+ isl_size n_div;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (total < 0 || n_div < 0)
+ return isl_bool_error;
+ total -= n_div;
+
+ for (i = 0; i < n_div; ++i)
+ if (isl_seq_first_non_zero(bmap->div[i] + 2 + total,
+ n_div) != -1)
+ return isl_bool_true;
+
+ return isl_bool_false;
+}
+
+/* Return a list of affine expressions, one for each integer division
+ * in "bmap_i". For each integer division that also appears in "bmap_j",
+ * the affine expression is set to NaN. The number of NaNs in the list
+ * is equal to the number of integer divisions in "bmap_j".
+ * For the other integer divisions of "bmap_i", the corresponding
+ * element in the list is a purely affine expression equal to the integer
+ * division in "hull".
+ * If no such list can be constructed, then the number of elements
+ * in the returned list is smaller than the number of integer divisions
+ * in "bmap_i".
+ * The integer division of "bmap_i" and "bmap_j" are assumed to be known and
+ * not contain any nested divs.
+ */
+static __isl_give isl_aff_list *set_up_substitutions(
+ __isl_keep isl_basic_map *bmap_i, __isl_keep isl_basic_map *bmap_j,
+ __isl_take isl_basic_map *hull)
+{
+ isl_size n_div_i, n_div_j, total;
+ isl_ctx *ctx;
+ isl_local_space *ls;
+ isl_basic_set *wrap_hull;
+ isl_aff *aff_nan;
+ isl_aff_list *list;
+ int i, j;
+
+ n_div_i = isl_basic_map_dim(bmap_i, isl_dim_div);
+ n_div_j = isl_basic_map_dim(bmap_j, isl_dim_div);
+ total = isl_basic_map_dim(bmap_i, isl_dim_all);
+ if (!hull || n_div_i < 0 || n_div_j < 0 || total < 0)
+ return NULL;
+
+ ctx = isl_basic_map_get_ctx(hull);
+ total -= n_div_i;
+
+ ls = isl_basic_map_get_local_space(bmap_i);
+ ls = isl_local_space_wrap(ls);
+ wrap_hull = isl_basic_map_wrap(hull);
+
+ aff_nan = isl_aff_nan_on_domain(isl_local_space_copy(ls));
+ list = isl_aff_list_alloc(ctx, n_div_i);
+
+ j = 0;
+ for (i = 0; i < n_div_i; ++i) {
+ isl_aff *aff;
+ isl_size n_div;
+
+ if (j < n_div_j &&
+ isl_basic_map_equal_div_expr_part(bmap_i, i, bmap_j, j,
+ 0, 2 + total)) {
+ ++j;
+ list = isl_aff_list_add(list, isl_aff_copy(aff_nan));
+ continue;
+ }
+ if (n_div_i - i <= n_div_j - j)
+ break;
+
+ aff = isl_local_space_get_div(ls, i);
+ aff = isl_aff_substitute_equalities(aff,
+ isl_basic_set_copy(wrap_hull));
+ aff = isl_aff_floor(aff);
+ n_div = isl_aff_dim(aff, isl_dim_div);
+ if (n_div < 0)
+ goto error;
+ if (n_div != 0) {
+ isl_aff_free(aff);
+ break;
+ }
+
+ list = isl_aff_list_add(list, aff);
+ }
+
+ isl_aff_free(aff_nan);
+ isl_local_space_free(ls);
+ isl_basic_set_free(wrap_hull);
+
+ return list;
+error:
+ isl_aff_free(aff_nan);
+ isl_local_space_free(ls);
+ isl_basic_set_free(wrap_hull);
+ isl_aff_list_free(list);
+ return NULL;
+}
+
+/* Add variables to info->bmap and info->tab corresponding to the elements
+ * in "list" that are not set to NaN.
+ * "extra_var" is the number of these elements.
+ * "dim" is the offset in the variables of "tab" where we should
+ * start considering the elements in "list".
+ * When this function returns, the total number of variables in "tab"
+ * is equal to "dim" plus the number of elements in "list".
+ *
+ * The newly added existentially quantified variables are not given
+ * an explicit representation because the corresponding div constraints
+ * do not appear in info->bmap. These constraints are not added
+ * to info->bmap because for internal consistency, they would need to
+ * be added to info->tab as well, where they could combine with the equality
+ * that is added later to result in constraints that do not hold
+ * in the original input.
+ */
+static isl_stat add_sub_vars(struct isl_coalesce_info *info,
+ __isl_keep isl_aff_list *list, int dim, int extra_var)
+{
+ int i, j, d;
+ isl_size n;
+
+ info->bmap = isl_basic_map_cow(info->bmap);
+ info->bmap = isl_basic_map_extend(info->bmap, extra_var, 0, 0);
+ n = isl_aff_list_n_aff(list);
+ if (!info->bmap || n < 0)
+ return isl_stat_error;
+ for (i = 0; i < n; ++i) {
+ int is_nan;
+ isl_aff *aff;
+
+ aff = isl_aff_list_get_aff(list, i);
+ is_nan = isl_aff_is_nan(aff);
+ isl_aff_free(aff);
+ if (is_nan < 0)
+ return isl_stat_error;
+ if (is_nan)
+ continue;
+
+ if (isl_tab_insert_var(info->tab, dim + i) < 0)
+ return isl_stat_error;
+ d = isl_basic_map_alloc_div(info->bmap);
+ if (d < 0)
+ return isl_stat_error;
+ info->bmap = isl_basic_map_mark_div_unknown(info->bmap, d);
+ for (j = d; j > i; --j)
+ info->bmap = isl_basic_map_swap_div(info->bmap,
+ j - 1, j);
+ if (!info->bmap)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* For each element in "list" that is not set to NaN, fix the corresponding
+ * variable in "tab" to the purely affine expression defined by the element.
+ * "dim" is the offset in the variables of "tab" where we should
+ * start considering the elements in "list".
+ *
+ * This function assumes that a sufficient number of rows and
+ * elements in the constraint array are available in the tableau.
+ */
+static isl_stat add_sub_equalities(struct isl_tab *tab,
+ __isl_keep isl_aff_list *list, int dim)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_vec *sub;
+ isl_aff *aff;
+
+ n = isl_aff_list_n_aff(list);
+ if (n < 0)
+ return isl_stat_error;
+
+ ctx = isl_tab_get_ctx(tab);
+ sub = isl_vec_alloc(ctx, 1 + dim + n);
+ if (!sub)
+ return isl_stat_error;
+ isl_seq_clr(sub->el + 1 + dim, n);
+
+ for (i = 0; i < n; ++i) {
+ aff = isl_aff_list_get_aff(list, i);
+ if (!aff)
+ goto error;
+ if (isl_aff_is_nan(aff)) {
+ isl_aff_free(aff);
+ continue;
+ }
+ isl_seq_cpy(sub->el, aff->v->el + 1, 1 + dim);
+ isl_int_neg(sub->el[1 + dim + i], aff->v->el[0]);
+ if (isl_tab_add_eq(tab, sub->el) < 0)
+ goto error;
+ isl_int_set_si(sub->el[1 + dim + i], 0);
+ isl_aff_free(aff);
+ }
+
+ isl_vec_free(sub);
+ return isl_stat_ok;
+error:
+ isl_aff_free(aff);
+ isl_vec_free(sub);
+ return isl_stat_error;
+}
+
+/* Add variables to info->tab and info->bmap corresponding to the elements
+ * in "list" that are not set to NaN. The value of the added variable
+ * in info->tab is fixed to the purely affine expression defined by the element.
+ * "dim" is the offset in the variables of info->tab where we should
+ * start considering the elements in "list".
+ * When this function returns, the total number of variables in info->tab
+ * is equal to "dim" plus the number of elements in "list".
+ */
+static isl_stat add_subs(struct isl_coalesce_info *info,
+ __isl_keep isl_aff_list *list, int dim)
+{
+ int extra_var;
+ isl_size n;
+
+ n = isl_aff_list_n_aff(list);
+ if (n < 0)
+ return isl_stat_error;
+
+ extra_var = n - (info->tab->n_var - dim);
+
+ if (isl_tab_extend_vars(info->tab, extra_var) < 0)
+ return isl_stat_error;
+ if (isl_tab_extend_cons(info->tab, 2 * extra_var) < 0)
+ return isl_stat_error;
+ if (add_sub_vars(info, list, dim, extra_var) < 0)
+ return isl_stat_error;
+
+ return add_sub_equalities(info->tab, list, dim);
+}
+
+/* Coalesce basic map "j" into basic map "i" after adding the extra integer
+ * divisions in "i" but not in "j" to basic map "j", with values
+ * specified by "list". The total number of elements in "list"
+ * is equal to the number of integer divisions in "i", while the number
+ * of NaN elements in the list is equal to the number of integer divisions
+ * in "j".
+ *
+ * If no coalescing can be performed, then we need to revert basic map "j"
+ * to its original state. We do the same if basic map "i" gets dropped
+ * during the coalescing, even though this should not happen in practice
+ * since we have already checked for "j" being a subset of "i"
+ * before we reach this stage.
+ */
+static enum isl_change coalesce_with_subs(int i, int j,
+ struct isl_coalesce_info *info, __isl_keep isl_aff_list *list)
+{
+ isl_basic_map *bmap_j;
+ struct isl_tab_undo *snap;
+ isl_size dim, n_div;
+ enum isl_change change;
+
+ bmap_j = isl_basic_map_copy(info[j].bmap);
+ snap = isl_tab_snap(info[j].tab);
+
+ dim = isl_basic_map_dim(bmap_j, isl_dim_all);
+ n_div = isl_basic_map_dim(bmap_j, isl_dim_div);
+ if (dim < 0 || n_div < 0)
+ goto error;
+ dim -= n_div;
+ if (add_subs(&info[j], list, dim) < 0)
+ goto error;
+
+ change = coalesce_local_pair(i, j, info);
+ if (change != isl_change_none && change != isl_change_drop_first) {
+ isl_basic_map_free(bmap_j);
+ } else {
+ isl_basic_map_free(info[j].bmap);
+ info[j].bmap = bmap_j;
+
+ if (isl_tab_rollback(info[j].tab, snap) < 0)
+ return isl_change_error;
+ }
+
+ return change;
+error:
+ isl_basic_map_free(bmap_j);
+ return isl_change_error;
+}
+
+/* Check if we can coalesce basic map "j" into basic map "i" after copying
+ * those extra integer divisions in "i" that can be simplified away
+ * using the extra equalities in "j".
+ * All divs are assumed to be known and not contain any nested divs.
+ *
+ * We first check if there are any extra equalities in "j" that we
+ * can exploit. Then we check if every integer division in "i"
+ * either already appears in "j" or can be simplified using the
+ * extra equalities to a purely affine expression.
+ * If these tests succeed, then we try to coalesce the two basic maps
+ * by introducing extra dimensions in "j" corresponding to
+ * the extra integer divisions "i" fixed to the corresponding
+ * purely affine expression.
+ */
+static enum isl_change check_coalesce_into_eq(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ isl_size n_div_i, n_div_j, n;
+ isl_basic_map *hull_i, *hull_j;
+ isl_bool equal, empty;
+ isl_aff_list *list;
+ enum isl_change change;
+
+ n_div_i = isl_basic_map_dim(info[i].bmap, isl_dim_div);
+ n_div_j = isl_basic_map_dim(info[j].bmap, isl_dim_div);
+ if (n_div_i < 0 || n_div_j < 0)
+ return isl_change_error;
+ if (n_div_i <= n_div_j)
+ return isl_change_none;
+ if (info[j].bmap->n_eq == 0)
+ return isl_change_none;
+
+ hull_i = isl_basic_map_copy(info[i].bmap);
+ hull_i = isl_basic_map_plain_affine_hull(hull_i);
+ hull_j = isl_basic_map_copy(info[j].bmap);
+ hull_j = isl_basic_map_plain_affine_hull(hull_j);
+
+ hull_j = isl_basic_map_intersect(hull_j, isl_basic_map_copy(hull_i));
+ equal = isl_basic_map_plain_is_equal(hull_i, hull_j);
+ empty = isl_basic_map_plain_is_empty(hull_j);
+ isl_basic_map_free(hull_i);
+
+ if (equal < 0 || empty < 0)
+ goto error;
+ if (equal || empty) {
+ isl_basic_map_free(hull_j);
+ return isl_change_none;
+ }
+
+ list = set_up_substitutions(info[i].bmap, info[j].bmap, hull_j);
+ if (!list)
+ return isl_change_error;
+ n = isl_aff_list_n_aff(list);
+ if (n < 0)
+ change = isl_change_error;
+ else if (n < n_div_i)
+ change = isl_change_none;
+ else
+ change = coalesce_with_subs(i, j, info, list);
+
+ isl_aff_list_free(list);
+
+ return change;
+error:
+ isl_basic_map_free(hull_j);
+ return isl_change_error;
+}
+
+/* Check if we can coalesce basic maps "i" and "j" after copying
+ * those extra integer divisions in one of the basic maps that can
+ * be simplified away using the extra equalities in the other basic map.
+ * We require all divs to be known in both basic maps.
+ * Furthermore, to simplify the comparison of div expressions,
+ * we do not allow any nested integer divisions.
+ */
+static enum isl_change check_coalesce_eq(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ isl_bool known, nested;
+ enum isl_change change;
+
+ known = isl_basic_map_divs_known(info[i].bmap);
+ if (known < 0 || !known)
+ return known < 0 ? isl_change_error : isl_change_none;
+ known = isl_basic_map_divs_known(info[j].bmap);
+ if (known < 0 || !known)
+ return known < 0 ? isl_change_error : isl_change_none;
+ nested = has_nested_div(info[i].bmap);
+ if (nested < 0 || nested)
+ return nested < 0 ? isl_change_error : isl_change_none;
+ nested = has_nested_div(info[j].bmap);
+ if (nested < 0 || nested)
+ return nested < 0 ? isl_change_error : isl_change_none;
+
+ change = check_coalesce_into_eq(i, j, info);
+ if (change != isl_change_none)
+ return change;
+ change = check_coalesce_into_eq(j, i, info);
+ if (change != isl_change_none)
+ return invert_change(change);
+
+ return isl_change_none;
+}
+
+/* Check if the union of the given pair of basic maps
+ * can be represented by a single basic map.
+ * If so, replace the pair by the single basic map and return
+ * isl_change_drop_first, isl_change_drop_second or isl_change_fuse.
+ * Otherwise, return isl_change_none.
+ *
+ * We first check if the two basic maps live in the same local space,
+ * after aligning the divs that differ by only an integer constant.
+ * If so, we do the complete check. Otherwise, we check if they have
+ * the same number of integer divisions and can be coalesced, if one is
+ * an obvious subset of the other or if the extra integer divisions
+ * of one basic map can be simplified away using the extra equalities
+ * of the other basic map.
+ *
+ * Note that trying to coalesce pairs of disjuncts with the same
+ * number, but different local variables may drop the explicit
+ * representation of some of these local variables.
+ * This operation is therefore not performed when
+ * the "coalesce_preserve_locals" option is set.
+ */
+static enum isl_change coalesce_pair(int i, int j,
+ struct isl_coalesce_info *info)
+{
+ int preserve;
+ isl_bool same;
+ enum isl_change change;
+ isl_ctx *ctx;
+
+ if (harmonize_divs(&info[i], &info[j]) < 0)
+ return isl_change_error;
+ same = same_divs(info[i].bmap, info[j].bmap);
+ if (same < 0)
+ return isl_change_error;
+ if (same)
+ return coalesce_local_pair(i, j, info);
+
+ ctx = isl_basic_map_get_ctx(info[i].bmap);
+ preserve = isl_options_get_coalesce_preserve_locals(ctx);
+ if (!preserve && info[i].bmap->n_div == info[j].bmap->n_div) {
+ change = coalesce_local_pair(i, j, info);
+ if (change != isl_change_none)
+ return change;
+ }
+
+ change = coalesce_divs(i, j, info);
+ if (change != isl_change_none)
+ return change;
+
+ return check_coalesce_eq(i, j, info);
+}
+
+/* Return the maximum of "a" and "b".
+ */
+static int isl_max(int a, int b)
+{
+ return a > b ? a : b;
+}
+
+/* Pairwise coalesce the basic maps in the range [start1, end1[ of "info"
+ * with those in the range [start2, end2[, skipping basic maps
+ * that have been removed (either before or within this function).
+ *
+ * For each basic map i in the first range, we check if it can be coalesced
+ * with respect to any previously considered basic map j in the second range.
+ * If i gets dropped (because it was a subset of some j), then
+ * we can move on to the next basic map.
+ * If j gets dropped, we need to continue checking against the other
+ * previously considered basic maps.
+ * If the two basic maps got fused, then we recheck the fused basic map
+ * against the previously considered basic maps, starting at i + 1
+ * (even if start2 is greater than i + 1).
+ */
+static int coalesce_range(isl_ctx *ctx, struct isl_coalesce_info *info,
+ int start1, int end1, int start2, int end2)
+{
+ int i, j;
+
+ for (i = end1 - 1; i >= start1; --i) {
+ if (info[i].removed)
+ continue;
+ for (j = isl_max(i + 1, start2); j < end2; ++j) {
+ enum isl_change changed;
+
+ if (info[j].removed)
+ continue;
+ if (info[i].removed)
+ isl_die(ctx, isl_error_internal,
+ "basic map unexpectedly removed",
+ return -1);
+ changed = coalesce_pair(i, j, info);
+ switch (changed) {
+ case isl_change_error:
+ return -1;
+ case isl_change_none:
+ case isl_change_drop_second:
+ continue;
+ case isl_change_drop_first:
+ j = end2;
+ break;
+ case isl_change_fuse:
+ j = i;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Pairwise coalesce the basic maps described by the "n" elements of "info".
+ *
+ * We consider groups of basic maps that live in the same apparent
+ * affine hull and we first coalesce within such a group before we
+ * coalesce the elements in the group with elements of previously
+ * considered groups. If a fuse happens during the second phase,
+ * then we also reconsider the elements within the group.
+ */
+static int coalesce(isl_ctx *ctx, int n, struct isl_coalesce_info *info)
+{
+ int start, end;
+
+ for (end = n; end > 0; end = start) {
+ start = end - 1;
+ while (start >= 1 &&
+ info[start - 1].hull_hash == info[start].hull_hash)
+ start--;
+ if (coalesce_range(ctx, info, start, end, start, end) < 0)
+ return -1;
+ if (coalesce_range(ctx, info, start, end, end, n) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Update the basic maps in "map" based on the information in "info".
+ * In particular, remove the basic maps that have been marked removed and
+ * update the others based on the information in the corresponding tableau.
+ * Since we detected implicit equalities without calling
+ * isl_basic_map_gauss, we need to do it now.
+ * Also call isl_basic_map_simplify if we may have lost the definition
+ * of one or more integer divisions.
+ * If a basic map is still equal to the one from which the corresponding "info"
+ * entry was created, then redundant constraint and
+ * implicit equality constraint detection have been performed
+ * on the corresponding tableau and the basic map can be marked as such.
+ */
+static __isl_give isl_map *update_basic_maps(__isl_take isl_map *map,
+ int n, struct isl_coalesce_info *info)
+{
+ int i;
+
+ if (!map)
+ return NULL;
+
+ for (i = n - 1; i >= 0; --i) {
+ if (info[i].removed) {
+ isl_basic_map_free(map->p[i]);
+ if (i != map->n - 1)
+ map->p[i] = map->p[map->n - 1];
+ map->n--;
+ continue;
+ }
+
+ info[i].bmap = isl_basic_map_update_from_tab(info[i].bmap,
+ info[i].tab);
+ info[i].bmap = isl_basic_map_gauss(info[i].bmap, NULL);
+ if (info[i].simplify)
+ info[i].bmap = isl_basic_map_simplify(info[i].bmap);
+ info[i].bmap = isl_basic_map_finalize(info[i].bmap);
+ if (!info[i].bmap)
+ return isl_map_free(map);
+ if (!info[i].modified) {
+ ISL_F_SET(info[i].bmap, ISL_BASIC_MAP_NO_IMPLICIT);
+ ISL_F_SET(info[i].bmap, ISL_BASIC_MAP_NO_REDUNDANT);
+ }
+ isl_basic_map_free(map->p[i]);
+ map->p[i] = info[i].bmap;
+ info[i].bmap = NULL;
+ }
+
+ return map;
+}
+
+/* For each pair of basic maps in the map, check if the union of the two
+ * can be represented by a single basic map.
+ * If so, replace the pair by the single basic map and start over.
+ *
+ * We factor out any (hidden) common factor from the constraint
+ * coefficients to improve the detection of adjacent constraints.
+ * Note that this function does not call isl_basic_map_gauss,
+ * but it does make sure that only a single copy of the basic map
+ * is affected. This means that isl_basic_map_gauss may have
+ * to be called at the end of the computation (in update_basic_maps)
+ * on this single copy to ensure that
+ * the basic maps are not left in an unexpected state.
+ *
+ * Since we are constructing the tableaus of the basic maps anyway,
+ * we exploit them to detect implicit equalities and redundant constraints.
+ * This also helps the coalescing as it can ignore the redundant constraints.
+ * In order to avoid confusion, we make all implicit equalities explicit
+ * in the basic maps. If the basic map only has a single reference
+ * (this happens in particular if it was modified by
+ * isl_basic_map_reduce_coefficients), then isl_basic_map_gauss
+ * does not get called on the result. The call to
+ * isl_basic_map_gauss in update_basic_maps resolves this as well.
+ * For each basic map, we also compute the hash of the apparent affine hull
+ * for use in coalesce.
+ */
+__isl_give isl_map *isl_map_coalesce(__isl_take isl_map *map)
+{
+ int i;
+ unsigned n;
+ isl_ctx *ctx;
+ struct isl_coalesce_info *info = NULL;
+
+ map = isl_map_remove_empty_parts(map);
+ if (!map)
+ return NULL;
+
+ if (map->n <= 1)
+ return map;
+
+ ctx = isl_map_get_ctx(map);
+ map = isl_map_sort_divs(map);
+ map = isl_map_cow(map);
+
+ if (!map)
+ return NULL;
+
+ n = map->n;
+
+ info = isl_calloc_array(map->ctx, struct isl_coalesce_info, n);
+ if (!info)
+ goto error;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_reduce_coefficients(map->p[i]);
+ if (!map->p[i])
+ goto error;
+ info[i].bmap = isl_basic_map_copy(map->p[i]);
+ info[i].tab = isl_tab_from_basic_map(info[i].bmap, 0);
+ if (!info[i].tab)
+ goto error;
+ if (!ISL_F_ISSET(info[i].bmap, ISL_BASIC_MAP_NO_IMPLICIT))
+ if (isl_tab_detect_implicit_equalities(info[i].tab) < 0)
+ goto error;
+ info[i].bmap = isl_tab_make_equalities_explicit(info[i].tab,
+ info[i].bmap);
+ if (!info[i].bmap)
+ goto error;
+ if (!ISL_F_ISSET(info[i].bmap, ISL_BASIC_MAP_NO_REDUNDANT))
+ if (isl_tab_detect_redundant(info[i].tab) < 0)
+ goto error;
+ if (coalesce_info_set_hull_hash(&info[i]) < 0)
+ goto error;
+ }
+ for (i = map->n - 1; i >= 0; --i)
+ if (info[i].tab->empty)
+ drop(&info[i]);
+
+ if (coalesce(ctx, n, info) < 0)
+ goto error;
+
+ map = update_basic_maps(map, n, info);
+
+ clear_coalesce_info(n, info);
+
+ return map;
+error:
+ clear_coalesce_info(n, info);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* For each pair of basic sets in the set, check if the union of the two
+ * can be represented by a single basic set.
+ * If so, replace the pair by the single basic set and start over.
+ */
+__isl_give isl_set *isl_set_coalesce(__isl_take isl_set *set)
+{
+ return set_from_map(isl_map_coalesce(set_to_map(set)));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-linux.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-linux.h
new file mode 100644
index 00000000000..93b658605cc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-linux.h
@@ -0,0 +1,56 @@
+/* define if your compiler has __attribute__ */
+#define HAVE___ATTRIBUTE__ /**/
+
+/* most gcc compilers know a function __attribute__((__warn_unused_result__)) */
+#define GCC_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
+
+
+/* Define to 1 if you have the declaration of `ffs', and to 0 if you don't. */
+#define HAVE_DECL_FFS 1
+
+/* Define to 1 if you have the declaration of `__builtin_ffs', and to 0 if you
+ don't. */
+#define HAVE_DECL___BUILTIN_FFS 1
+
+/* Define to 1 if you have the declaration of `_BitScanForward', and to 0 if
+ you don't. */
+#define HAVE_DECL__BITSCANFORWARD 0
+
+
+/* Define to 1 if you have the declaration of `strcasecmp', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRCASECMP 1
+
+/* Define to 1 if you have the declaration of `_stricmp', and to 0 if you
+ don't. */
+#define HAVE_DECL__STRICMP 0
+
+
+/* Define to 1 if you have the declaration of `strncasecmp', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRNCASECMP 1
+
+/* Define to 1 if you have the declaration of `_strnicmp', and to 0 if you
+ don't. */
+#define HAVE_DECL__STRNICMP 0
+
+
+/* Define to 1 if you have the declaration of `snprintf', and to 0 if you
+ don't. */
+#define HAVE_DECL_SNPRINTF 1
+
+/* Define to 1 if you have the declaration of `_snprintf', and to 0 if you
+ don't. */
+#define HAVE_DECL__SNPRINTF 0
+
+
+/* use gmp to implement isl_int */
+/* #undef USE_GMP_FOR_MP */
+
+/* use imath to implement isl_int */
+#define USE_IMATH_FOR_MP
+
+/* Use small integer optimization */
+#define USE_SMALL_INT_OPT
+
+#include <isl_config_post.h>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-osx.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-osx.h
new file mode 100644
index 00000000000..b06a86470f8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-osx.h
@@ -0,0 +1 @@
+#include "isl_config-linux.h"
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-win.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-win.h
new file mode 100644
index 00000000000..818f866b27e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config-win.h
@@ -0,0 +1,55 @@
+/* define if your compiler has __attribute__ */
+/* #undef HAVE___ATTRIBUTE__ */
+
+/* most gcc compilers know a function __attribute__((__warn_unused_result__)) */
+/* #define GCC_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) */
+
+/* Define to 1 if you have the declaration of `ffs', and to 0 if you don't. */
+#define HAVE_DECL_FFS 0
+
+/* Define to 1 if you have the declaration of `__builtin_ffs', and to 0 if you
+ don't. */
+#define HAVE_DECL___BUILTIN_FFS defined(__clang__)
+
+/* Define to 1 if you have the declaration of `_BitScanForward', and to 0 if
+ you don't. */
+#define HAVE_DECL__BITSCANFORWARD 1
+
+
+/* Define to 1 if you have the declaration of `strcasecmp', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRCASECMP 0
+
+/* Define to 1 if you have the declaration of `_stricmp', and to 0 if you
+ don't. */
+#define HAVE_DECL__STRICMP 1
+
+
+/* Define to 1 if you have the declaration of `strncasecmp', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRNCASECMP 0
+
+/* Define to 1 if you have the declaration of `_strnicmp', and to 0 if you
+ don't. */
+#define HAVE_DECL__STRNICMP 1
+
+
+/* Define to 1 if you have the declaration of `snprintf', and to 0 if you
+ don't. */
+#define HAVE_DECL_SNPRINTF 0
+
+/* Define to 1 if you have the declaration of `_snprintf', and to 0 if you
+ don't. */
+#define HAVE_DECL__SNPRINTF 1
+
+
+/* use gmp to implement isl_int */
+/* #undef USE_GMP_FOR_MP */
+
+/* use imath to implement isl_int */
+#define USE_IMATH_FOR_MP
+
+/* Use small integer optimization */
+#define USE_SMALL_INT_OPT
+
+#include <isl_config_post.h>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config.h
new file mode 100644
index 00000000000..6e264c71d84
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#if defined(__APPLE__)
+# include "isl_config-osx.h"
+#elif defined(_MSC_VER)
+# include "isl_config-win.h"
+#else
+# include "isl_config-linux.h"
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config_post.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config_post.h
new file mode 100644
index 00000000000..4ceb7f0edb7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_config_post.h
@@ -0,0 +1,38 @@
+#ifndef HAVE___ATTRIBUTE__
+#define __attribute__(x)
+#endif
+
+#if HAVE_DECL_FFS
+#include <strings.h>
+#endif
+
+#if (HAVE_DECL_FFS==0) && (HAVE_DECL___BUILTIN_FFS==1)
+#define ffs __builtin_ffs
+#endif
+
+#if !HAVE_DECL_FFS && !HAVE_DECL___BUILTIN_FFS && HAVE_DECL__BITSCANFORWARD
+int isl_ffs(int i);
+#define ffs isl_ffs
+#endif
+
+#if HAVE_DECL_STRCASECMP || HAVE_DECL_STRNCASECMP
+#include <strings.h>
+#endif
+
+#if !HAVE_DECL_STRCASECMP && HAVE_DECL__STRICMP
+#define strcasecmp _stricmp
+#endif
+
+#if !HAVE_DECL_STRNCASECMP && HAVE_DECL__STRNICMP
+#define strncasecmp _strnicmp
+#endif
+
+#if HAVE_DECL__SNPRINTF
+#define snprintf _snprintf
+#endif
+
+#ifdef GCC_WARN_UNUSED_RESULT
+#define WARN_UNUSED GCC_WARN_UNUSED_RESULT
+#else
+#define WARN_UNUSED
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_constraint.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_constraint.c
new file mode 100644
index 00000000000..4fc7aba8c9b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_constraint.c
@@ -0,0 +1,1371 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ */
+
+#include <isl_map_private.h>
+#include <isl_constraint_private.h>
+#include <isl_space_private.h>
+#include <isl_seq.h>
+#include <isl_aff_private.h>
+#include <isl_local_space_private.h>
+#include <isl_val_private.h>
+#include <isl_vec_private.h>
+
+#include <bset_to_bmap.c>
+#include <bset_from_bmap.c>
+
+#undef EL_BASE
+#define EL_BASE constraint
+
+#include <isl_list_templ.c>
+
+isl_ctx *isl_constraint_get_ctx(__isl_keep isl_constraint *c)
+{
+ return c ? isl_local_space_get_ctx(c->ls) : NULL;
+}
+
+static isl_size n(__isl_keep isl_constraint *c, enum isl_dim_type type)
+{
+ return isl_local_space_dim(c->ls, type);
+}
+
+static unsigned offset(__isl_keep isl_constraint *c, enum isl_dim_type type)
+{
+ return isl_local_space_offset(c->ls, type);
+}
+
+__isl_give isl_constraint *isl_constraint_alloc_vec(int eq,
+ __isl_take isl_local_space *ls, __isl_take isl_vec *v)
+{
+ isl_constraint *constraint;
+
+ if (!ls || !v)
+ goto error;
+
+ constraint = isl_alloc_type(isl_vec_get_ctx(v), isl_constraint);
+ if (!constraint)
+ goto error;
+
+ constraint->ref = 1;
+ constraint->eq = eq;
+ constraint->ls = ls;
+ constraint->v = v;
+
+ return constraint;
+error:
+ isl_local_space_free(ls);
+ isl_vec_free(v);
+ return NULL;
+}
+
+__isl_give isl_constraint *isl_constraint_alloc(int eq,
+ __isl_take isl_local_space *ls)
+{
+ isl_size dim;
+ isl_ctx *ctx;
+ isl_vec *v;
+
+ dim = isl_local_space_dim(ls, isl_dim_all);
+ if (dim < 0)
+ ls = isl_local_space_free(ls);
+ if (!ls)
+ return NULL;
+
+ ctx = isl_local_space_get_ctx(ls);
+ v = isl_vec_alloc(ctx, 1 + dim);
+ v = isl_vec_clr(v);
+ return isl_constraint_alloc_vec(eq, ls, v);
+}
+
+__isl_give isl_constraint *isl_basic_map_constraint(
+ __isl_take isl_basic_map *bmap, isl_int **line)
+{
+ int eq;
+ isl_size dim;
+ isl_ctx *ctx;
+ isl_vec *v;
+ isl_local_space *ls = NULL;
+ isl_constraint *constraint;
+
+ if (!bmap || !line)
+ goto error;
+
+ eq = line >= bmap->eq;
+
+ ctx = isl_basic_map_get_ctx(bmap);
+ ls = isl_basic_map_get_local_space(bmap);
+ dim = isl_local_space_dim(ls, isl_dim_all);
+ if (dim < 0)
+ goto error;
+ v = isl_vec_alloc(ctx, 1 + dim);
+ if (!v)
+ goto error;
+ isl_seq_cpy(v->el, line[0], v->size);
+ constraint = isl_constraint_alloc_vec(eq, ls, v);
+
+ isl_basic_map_free(bmap);
+ return constraint;
+error:
+ isl_local_space_free(ls);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_constraint *isl_basic_set_constraint(
+ __isl_take isl_basic_set *bset, isl_int **line)
+{
+ return isl_basic_map_constraint(bset_to_bmap(bset), line);
+}
+
+__isl_give isl_constraint *isl_constraint_alloc_equality(
+ __isl_take isl_local_space *ls)
+{
+ return isl_constraint_alloc(1, ls);
+}
+
+__isl_give isl_constraint *isl_constraint_alloc_inequality(
+ __isl_take isl_local_space *ls)
+{
+ return isl_constraint_alloc(0, ls);
+}
+
+__isl_give isl_constraint *isl_constraint_dup(__isl_keep isl_constraint *c)
+{
+ if (!c)
+ return NULL;
+
+ return isl_constraint_alloc_vec(c->eq, isl_local_space_copy(c->ls),
+ isl_vec_copy(c->v));
+}
+
+__isl_give isl_constraint *isl_constraint_cow(__isl_take isl_constraint *c)
+{
+ if (!c)
+ return NULL;
+
+ if (c->ref == 1)
+ return c;
+ c->ref--;
+ return isl_constraint_dup(c);
+}
+
+__isl_give isl_constraint *isl_constraint_copy(
+ __isl_keep isl_constraint *constraint)
+{
+ if (!constraint)
+ return NULL;
+
+ constraint->ref++;
+ return constraint;
+}
+
+__isl_null isl_constraint *isl_constraint_free(__isl_take isl_constraint *c)
+{
+ if (!c)
+ return NULL;
+
+ if (--c->ref > 0)
+ return NULL;
+
+ isl_local_space_free(c->ls);
+ isl_vec_free(c->v);
+ free(c);
+
+ return NULL;
+}
+
+/* Return the number of constraints in "bmap", i.e., the
+ * number of times isl_basic_map_foreach_constraint will
+ * call the callback.
+ */
+isl_size isl_basic_map_n_constraint(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return isl_size_error;
+
+ return bmap->n_eq + bmap->n_ineq;
+}
+
+/* Return the number of constraints in "bset", i.e., the
+ * number of times isl_basic_set_foreach_constraint will
+ * call the callback.
+ */
+isl_size isl_basic_set_n_constraint(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_n_constraint(bset);
+}
+
+isl_stat isl_basic_map_foreach_constraint(__isl_keep isl_basic_map *bmap,
+ isl_stat (*fn)(__isl_take isl_constraint *c, void *user), void *user)
+{
+ int i;
+ struct isl_constraint *c;
+
+ if (!bmap)
+ return isl_stat_error;
+
+ isl_assert(bmap->ctx, ISL_F_ISSET(bmap, ISL_BASIC_MAP_FINAL),
+ return isl_stat_error);
+
+ for (i = 0; i < bmap->n_eq; ++i) {
+ c = isl_basic_map_constraint(isl_basic_map_copy(bmap),
+ &bmap->eq[i]);
+ if (!c)
+ return isl_stat_error;
+ if (fn(c, user) < 0)
+ return isl_stat_error;
+ }
+
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ c = isl_basic_map_constraint(isl_basic_map_copy(bmap),
+ &bmap->ineq[i]);
+ if (!c)
+ return isl_stat_error;
+ if (fn(c, user) < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+isl_stat isl_basic_set_foreach_constraint(__isl_keep isl_basic_set *bset,
+ isl_stat (*fn)(__isl_take isl_constraint *c, void *user), void *user)
+{
+ return isl_basic_map_foreach_constraint(bset_to_bmap(bset), fn, user);
+}
+
+/* Add the constraint to the list that "user" points to, if it is not
+ * a div constraint.
+ */
+static isl_stat collect_constraint(__isl_take isl_constraint *constraint,
+ void *user)
+{
+ isl_constraint_list **list = user;
+ isl_bool is_div;
+
+ is_div = isl_constraint_is_div_constraint(constraint);
+ if (is_div < 0 || is_div)
+ isl_constraint_free(constraint);
+ else
+ *list = isl_constraint_list_add(*list, constraint);
+
+ return is_div < 0 ? isl_stat_error : isl_stat_ok;
+}
+
+/* Return a list of constraints that, when combined, are equivalent
+ * to "bmap". The input is required to have only known divs.
+ *
+ * There is no need to include the div constraints as they are
+ * implied by the div expressions.
+ */
+__isl_give isl_constraint_list *isl_basic_map_get_constraint_list(
+ __isl_keep isl_basic_map *bmap)
+{
+ isl_size n;
+ isl_bool known;
+ isl_ctx *ctx;
+ isl_constraint_list *list;
+
+ known = isl_basic_map_divs_known(bmap);
+ if (known < 0)
+ return NULL;
+ ctx = isl_basic_map_get_ctx(bmap);
+ if (!known)
+ isl_die(ctx, isl_error_invalid,
+ "input involves unknown divs", return NULL);
+
+ n = isl_basic_map_n_constraint(bmap);
+ if (n < 0)
+ return NULL;
+ list = isl_constraint_list_alloc(ctx, n);
+ if (isl_basic_map_foreach_constraint(bmap,
+ &collect_constraint, &list) < 0)
+ list = isl_constraint_list_free(list);
+
+ return list;
+}
+
+/* Return a list of constraints that, when combined, are equivalent
+ * to "bset". The input is required to have only known divs.
+ */
+__isl_give isl_constraint_list *isl_basic_set_get_constraint_list(
+ __isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_get_constraint_list(bset);
+}
+
+int isl_constraint_is_equal(__isl_keep isl_constraint *constraint1,
+ __isl_keep isl_constraint *constraint2)
+{
+ int equal;
+
+ if (!constraint1 || !constraint2)
+ return 0;
+ if (constraint1->eq != constraint2->eq)
+ return 0;
+ equal = isl_local_space_is_equal(constraint1->ls, constraint2->ls);
+ if (equal < 0 || !equal)
+ return equal;
+ return isl_vec_is_equal(constraint1->v, constraint2->v);
+}
+
+__isl_give isl_basic_map *isl_basic_map_add_constraint(
+ __isl_take isl_basic_map *bmap, __isl_take isl_constraint *constraint)
+{
+ isl_ctx *ctx;
+ isl_space *space;
+ int equal_space;
+
+ if (!bmap || !constraint)
+ goto error;
+
+ ctx = isl_constraint_get_ctx(constraint);
+ space = isl_constraint_get_space(constraint);
+ equal_space = isl_space_is_equal(bmap->dim, space);
+ isl_space_free(space);
+ isl_assert(ctx, equal_space, goto error);
+
+ bmap = isl_basic_map_intersect(bmap,
+ isl_basic_map_from_constraint(constraint));
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ isl_constraint_free(constraint);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_add_constraint(
+ __isl_take isl_basic_set *bset, __isl_take isl_constraint *constraint)
+{
+ return bset_from_bmap(isl_basic_map_add_constraint(bset_to_bmap(bset),
+ constraint));
+}
+
+__isl_give isl_map *isl_map_add_constraint(__isl_take isl_map *map,
+ __isl_take isl_constraint *constraint)
+{
+ isl_basic_map *bmap;
+
+ bmap = isl_basic_map_from_constraint(constraint);
+ map = isl_map_intersect(map, isl_map_from_basic_map(bmap));
+
+ return map;
+}
+
+__isl_give isl_set *isl_set_add_constraint(__isl_take isl_set *set,
+ __isl_take isl_constraint *constraint)
+{
+ return isl_map_add_constraint(set, constraint);
+}
+
+/* Return the space of "constraint".
+ */
+static __isl_keep isl_space *isl_constraint_peek_space(
+ __isl_keep isl_constraint *constraint)
+{
+ return constraint ? isl_local_space_peek_space(constraint->ls) : NULL;
+}
+
+__isl_give isl_space *isl_constraint_get_space(
+ __isl_keep isl_constraint *constraint)
+{
+ return constraint ? isl_local_space_get_space(constraint->ls) : NULL;
+}
+
+__isl_give isl_local_space *isl_constraint_get_local_space(
+ __isl_keep isl_constraint *constraint)
+{
+ return constraint ? isl_local_space_copy(constraint->ls) : NULL;
+}
+
+isl_size isl_constraint_dim(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type)
+{
+ if (!constraint)
+ return isl_size_error;
+ return n(constraint, type);
+}
+
+#undef TYPE
+#define TYPE isl_constraint
+static
+#include "check_type_range_templ.c"
+
+isl_bool isl_constraint_involves_dims(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+ int *active = NULL;
+ isl_bool involves = isl_bool_false;
+
+ if (!constraint)
+ return isl_bool_error;
+ if (n == 0)
+ return isl_bool_false;
+
+ if (isl_constraint_check_range(constraint, type, first, n) < 0)
+ return isl_bool_error;
+
+ active = isl_local_space_get_active(constraint->ls,
+ constraint->v->el + 1);
+ if (!active)
+ goto error;
+
+ first += isl_local_space_offset(constraint->ls, type) - 1;
+ for (i = 0; i < n; ++i)
+ if (active[first + i]) {
+ involves = isl_bool_true;
+ break;
+ }
+
+ free(active);
+
+ return involves;
+error:
+ free(active);
+ return isl_bool_error;
+}
+
+/* Does the given constraint represent a lower bound on the given
+ * dimension?
+ */
+isl_bool isl_constraint_is_lower_bound(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type, unsigned pos)
+{
+ if (isl_constraint_check_range(constraint, type, pos, 1) < 0)
+ return isl_bool_error;
+
+ pos += isl_local_space_offset(constraint->ls, type);
+ return isl_bool_ok(isl_int_is_pos(constraint->v->el[pos]));
+}
+
+/* Does the given constraint represent an upper bound on the given
+ * dimension?
+ */
+isl_bool isl_constraint_is_upper_bound(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type, unsigned pos)
+{
+ if (isl_constraint_check_range(constraint, type, pos, 1) < 0)
+ return isl_bool_error;
+
+ pos += isl_local_space_offset(constraint->ls, type);
+ return isl_bool_ok(isl_int_is_neg(constraint->v->el[pos]));
+}
+
+const char *isl_constraint_get_dim_name(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type, unsigned pos)
+{
+ return constraint ?
+ isl_local_space_get_dim_name(constraint->ls, type, pos) : NULL;
+}
+
+void isl_constraint_get_constant(__isl_keep isl_constraint *constraint,
+ isl_int *v)
+{
+ if (!constraint)
+ return;
+ isl_int_set(*v, constraint->v->el[0]);
+}
+
+/* Return the constant term of "constraint".
+ */
+__isl_give isl_val *isl_constraint_get_constant_val(
+ __isl_keep isl_constraint *constraint)
+{
+ isl_ctx *ctx;
+
+ if (!constraint)
+ return NULL;
+
+ ctx = isl_constraint_get_ctx(constraint);
+ return isl_val_int_from_isl_int(ctx, constraint->v->el[0]);
+}
+
+void isl_constraint_get_coefficient(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type, int pos, isl_int *v)
+{
+ if (isl_constraint_check_range(constraint, type, pos, 1) < 0)
+ return;
+
+ pos += isl_local_space_offset(constraint->ls, type);
+ isl_int_set(*v, constraint->v->el[pos]);
+}
+
+/* Return the coefficient of the variable of type "type" at position "pos"
+ * of "constraint".
+ */
+__isl_give isl_val *isl_constraint_get_coefficient_val(
+ __isl_keep isl_constraint *constraint, enum isl_dim_type type, int pos)
+{
+ isl_ctx *ctx;
+
+ if (isl_constraint_check_range(constraint, type, pos, 1) < 0)
+ return NULL;
+
+ ctx = isl_constraint_get_ctx(constraint);
+ pos += isl_local_space_offset(constraint->ls, type);
+ return isl_val_int_from_isl_int(ctx, constraint->v->el[pos]);
+}
+
+__isl_give isl_aff *isl_constraint_get_div(__isl_keep isl_constraint *constraint,
+ int pos)
+{
+ if (!constraint)
+ return NULL;
+
+ return isl_local_space_get_div(constraint->ls, pos);
+}
+
+__isl_give isl_constraint *isl_constraint_set_constant(
+ __isl_take isl_constraint *constraint, isl_int v)
+{
+ constraint = isl_constraint_cow(constraint);
+ if (!constraint)
+ return NULL;
+
+ constraint->v = isl_vec_cow(constraint->v);
+ if (!constraint->v)
+ return isl_constraint_free(constraint);
+
+ isl_int_set(constraint->v->el[0], v);
+ return constraint;
+}
+
+/* Replace the constant term of "constraint" by "v".
+ */
+__isl_give isl_constraint *isl_constraint_set_constant_val(
+ __isl_take isl_constraint *constraint, __isl_take isl_val *v)
+{
+ constraint = isl_constraint_cow(constraint);
+ if (!constraint || !v)
+ goto error;
+ if (!isl_val_is_int(v))
+ isl_die(isl_constraint_get_ctx(constraint), isl_error_invalid,
+ "expecting integer value", goto error);
+ constraint->v = isl_vec_set_element_val(constraint->v, 0, v);
+ if (!constraint->v)
+ constraint = isl_constraint_free(constraint);
+ return constraint;
+error:
+ isl_val_free(v);
+ return isl_constraint_free(constraint);
+}
+
+__isl_give isl_constraint *isl_constraint_set_constant_si(
+ __isl_take isl_constraint *constraint, int v)
+{
+ constraint = isl_constraint_cow(constraint);
+ if (!constraint)
+ return NULL;
+
+ constraint->v = isl_vec_cow(constraint->v);
+ if (!constraint->v)
+ return isl_constraint_free(constraint);
+
+ isl_int_set_si(constraint->v->el[0], v);
+ return constraint;
+}
+
+/* Replace the coefficient of the variable of type "type" at position "pos"
+ * of "constraint" by "v".
+ */
+__isl_give isl_constraint *isl_constraint_set_coefficient_val(
+ __isl_take isl_constraint *constraint,
+ enum isl_dim_type type, int pos, __isl_take isl_val *v)
+{
+ constraint = isl_constraint_cow(constraint);
+ if (!constraint || !v)
+ goto error;
+ if (!isl_val_is_int(v))
+ isl_die(isl_constraint_get_ctx(constraint), isl_error_invalid,
+ "expecting integer value", goto error);
+ if (isl_constraint_check_range(constraint, type, pos, 1) < 0)
+ goto error;
+
+ pos += isl_local_space_offset(constraint->ls, type);
+ constraint->v = isl_vec_set_element_val(constraint->v, pos, v);
+ if (!constraint->v)
+ constraint = isl_constraint_free(constraint);
+ return constraint;
+error:
+ isl_val_free(v);
+ return isl_constraint_free(constraint);
+}
+
+__isl_give isl_constraint *isl_constraint_set_coefficient_si(
+ __isl_take isl_constraint *constraint,
+ enum isl_dim_type type, int pos, int v)
+{
+ constraint = isl_constraint_cow(constraint);
+ if (isl_constraint_check_range(constraint, type, pos, 1) < 0)
+ return isl_constraint_free(constraint);
+
+ constraint->v = isl_vec_cow(constraint->v);
+ if (!constraint->v)
+ return isl_constraint_free(constraint);
+
+ pos += isl_local_space_offset(constraint->ls, type);
+ isl_int_set_si(constraint->v->el[pos], v);
+
+ return constraint;
+}
+
+__isl_give isl_constraint *isl_constraint_negate(
+ __isl_take isl_constraint *constraint)
+{
+ isl_ctx *ctx;
+
+ constraint = isl_constraint_cow(constraint);
+ if (!constraint)
+ return NULL;
+
+ ctx = isl_constraint_get_ctx(constraint);
+ if (isl_constraint_is_equality(constraint))
+ isl_die(ctx, isl_error_invalid, "cannot negate equality",
+ return isl_constraint_free(constraint));
+ constraint->v = isl_vec_neg(constraint->v);
+ constraint->v = isl_vec_cow(constraint->v);
+ if (!constraint->v)
+ return isl_constraint_free(constraint);
+ isl_int_sub_ui(constraint->v->el[0], constraint->v->el[0], 1);
+ return constraint;
+}
+
+isl_bool isl_constraint_is_equality(struct isl_constraint *constraint)
+{
+ if (!constraint)
+ return isl_bool_error;
+ return isl_bool_ok(constraint->eq);
+}
+
+isl_bool isl_constraint_is_div_constraint(__isl_keep isl_constraint *constraint)
+{
+ int i;
+ isl_size n_div;
+
+ if (!constraint)
+ return isl_bool_error;
+ if (isl_constraint_is_equality(constraint))
+ return isl_bool_false;
+ n_div = isl_constraint_dim(constraint, isl_dim_div);
+ if (n_div < 0)
+ return isl_bool_error;
+ for (i = 0; i < n_div; ++i) {
+ isl_bool is_div;
+ is_div = isl_local_space_is_div_constraint(constraint->ls,
+ constraint->v->el, i);
+ if (is_div < 0 || is_div)
+ return is_div;
+ }
+
+ return isl_bool_false;
+}
+
+/* Is "constraint" an equality that corresponds to integer division "div"?
+ *
+ * That is, given an integer division of the form
+ *
+ * a = floor((f + c)/m)
+ *
+ * is the equality of the form
+ *
+ * -f + m d + c' = 0
+ * ?
+ * Note that the constant term is not checked explicitly, but given
+ * that this is a valid equality constraint, the constant c' necessarily
+ * has a value close to -c.
+ */
+isl_bool isl_constraint_is_div_equality(__isl_keep isl_constraint *constraint,
+ unsigned div)
+{
+ isl_bool equality;
+
+ equality = isl_constraint_is_equality(constraint);
+ if (equality < 0 || !equality)
+ return equality;
+ return isl_local_space_is_div_equality(constraint->ls,
+ constraint->v->el, div);
+}
+
+/* We manually set ISL_BASIC_SET_FINAL instead of calling
+ * isl_basic_map_finalize because we want to keep the position
+ * of the divs and we therefore do not want to throw away redundant divs.
+ * This is arguably a bit fragile.
+ */
+__isl_give isl_basic_map *isl_basic_map_from_constraint(
+ __isl_take isl_constraint *constraint)
+{
+ int k;
+ isl_local_space *ls;
+ struct isl_basic_map *bmap;
+ isl_int *c;
+ isl_size total;
+
+ if (!constraint)
+ return NULL;
+
+ ls = isl_local_space_copy(constraint->ls);
+ bmap = isl_basic_map_from_local_space(ls);
+ bmap = isl_basic_map_extend_constraints(bmap, 1, 1);
+ if (isl_constraint_is_equality(constraint)) {
+ k = isl_basic_map_alloc_equality(bmap);
+ if (k < 0)
+ goto error;
+ c = bmap->eq[k];
+ }
+ else {
+ k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ c = bmap->ineq[k];
+ }
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ goto error;
+ isl_seq_cpy(c, constraint->v->el, 1 + total);
+ isl_constraint_free(constraint);
+ if (bmap)
+ ISL_F_SET(bmap, ISL_BASIC_SET_FINAL);
+ return bmap;
+error:
+ isl_constraint_free(constraint);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_from_constraint(
+ __isl_take isl_constraint *constraint)
+{
+ isl_space *space;
+
+ space = isl_constraint_peek_space(constraint);
+ if (isl_space_check_is_set(space) < 0)
+ goto error;
+ return bset_from_bmap(isl_basic_map_from_constraint(constraint));
+error:
+ isl_constraint_free(constraint);
+ return NULL;
+}
+
+/* Is the variable of "type" at position "pos" of "bmap" defined
+ * in terms of earlier dimensions through an equality?
+ *
+ * If so, and if c is not NULL, then return a copy of this equality in *c.
+ */
+isl_bool isl_basic_map_has_defining_equality(
+ __isl_keep isl_basic_map *bmap, enum isl_dim_type type, int pos,
+ __isl_give isl_constraint **c)
+{
+ int i;
+ unsigned offset;
+ isl_size total;
+
+ if (isl_basic_map_check_range(bmap, type, pos, 1) < 0)
+ return isl_bool_error;
+ offset = isl_basic_map_offset(bmap, type);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ for (i = 0; i < bmap->n_eq; ++i) {
+ if (isl_int_is_zero(bmap->eq[i][offset + pos]) ||
+ isl_seq_first_non_zero(bmap->eq[i]+offset+pos+1,
+ 1+total-offset-pos-1) != -1)
+ continue;
+ if (c)
+ *c = isl_basic_map_constraint(isl_basic_map_copy(bmap),
+ &bmap->eq[i]);
+ return isl_bool_true;
+ }
+ return isl_bool_false;
+}
+
+/* Is the variable of "type" at position "pos" of "bset" defined
+ * in terms of earlier dimensions through an equality?
+ *
+ * If so, and if c is not NULL, then return a copy of this equality in *c.
+ */
+isl_bool isl_basic_set_has_defining_equality(
+ __isl_keep isl_basic_set *bset, enum isl_dim_type type, int pos,
+ __isl_give isl_constraint **c)
+{
+ return isl_basic_map_has_defining_equality(bset_to_bmap(bset),
+ type, pos, c);
+}
+
+isl_bool isl_basic_set_has_defining_inequalities(
+ struct isl_basic_set *bset, enum isl_dim_type type, int pos,
+ struct isl_constraint **lower,
+ struct isl_constraint **upper)
+{
+ int i, j;
+ unsigned offset;
+ isl_size total;
+ isl_int m;
+ isl_int **lower_line, **upper_line;
+
+ if (isl_basic_set_check_range(bset, type, pos, 1) < 0)
+ return isl_bool_error;
+ offset = isl_basic_set_offset(bset, type);
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ isl_int_init(m);
+ for (i = 0; i < bset->n_ineq; ++i) {
+ if (isl_int_is_zero(bset->ineq[i][offset + pos]))
+ continue;
+ if (isl_int_is_one(bset->ineq[i][offset + pos]))
+ continue;
+ if (isl_int_is_negone(bset->ineq[i][offset + pos]))
+ continue;
+ if (isl_seq_first_non_zero(bset->ineq[i]+offset+pos+1,
+ 1+total-offset-pos-1) != -1)
+ continue;
+ for (j = i + 1; j < bset->n_ineq; ++j) {
+ if (!isl_seq_is_neg(bset->ineq[i]+1, bset->ineq[j]+1,
+ total))
+ continue;
+ isl_int_add(m, bset->ineq[i][0], bset->ineq[j][0]);
+ if (isl_int_abs_ge(m, bset->ineq[i][offset+pos]))
+ continue;
+
+ if (isl_int_is_pos(bset->ineq[i][offset+pos])) {
+ lower_line = &bset->ineq[i];
+ upper_line = &bset->ineq[j];
+ } else {
+ lower_line = &bset->ineq[j];
+ upper_line = &bset->ineq[i];
+ }
+ *lower = isl_basic_set_constraint(
+ isl_basic_set_copy(bset), lower_line);
+ *upper = isl_basic_set_constraint(
+ isl_basic_set_copy(bset), upper_line);
+ isl_int_clear(m);
+ return isl_bool_true;
+ }
+ }
+ *lower = NULL;
+ *upper = NULL;
+ isl_int_clear(m);
+ return isl_bool_false;
+}
+
+/* Given two constraints "a" and "b" on the variable at position "abs_pos"
+ * (in "a" and "b"), add a constraint to "bset" that ensures that the
+ * bound implied by "a" is (strictly) larger than the bound implied by "b".
+ *
+ * If both constraints imply lower bounds, then this means that "a" is
+ * active in the result.
+ * If both constraints imply upper bounds, then this means that "b" is
+ * active in the result.
+ */
+static __isl_give isl_basic_set *add_larger_bound_constraint(
+ __isl_take isl_basic_set *bset, isl_int *a, isl_int *b,
+ unsigned abs_pos, int strict)
+{
+ int k;
+ isl_int t;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ return isl_basic_set_free(bset);
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ goto error;
+
+ isl_int_init(t);
+ isl_int_neg(t, b[1 + abs_pos]);
+
+ isl_seq_combine(bset->ineq[k], t, a, a[1 + abs_pos], b, 1 + abs_pos);
+ isl_seq_combine(bset->ineq[k] + 1 + abs_pos,
+ t, a + 1 + abs_pos + 1, a[1 + abs_pos], b + 1 + abs_pos + 1,
+ total - abs_pos);
+
+ if (strict)
+ isl_int_sub_ui(bset->ineq[k][0], bset->ineq[k][0], 1);
+
+ isl_int_clear(t);
+
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Add constraints to "context" that ensure that "u" is the smallest
+ * (and therefore active) upper bound on "abs_pos" in "bset" and return
+ * the resulting basic set.
+ */
+static __isl_give isl_basic_set *set_smallest_upper_bound(
+ __isl_keep isl_basic_set *context,
+ __isl_keep isl_basic_set *bset, unsigned abs_pos, int n_upper, int u)
+{
+ int j;
+
+ context = isl_basic_set_copy(context);
+ context = isl_basic_set_cow(context);
+
+ context = isl_basic_set_extend_constraints(context, 0, n_upper - 1);
+
+ for (j = 0; j < bset->n_ineq; ++j) {
+ if (j == u)
+ continue;
+ if (!isl_int_is_neg(bset->ineq[j][1 + abs_pos]))
+ continue;
+ context = add_larger_bound_constraint(context,
+ bset->ineq[j], bset->ineq[u], abs_pos, j > u);
+ }
+
+ context = isl_basic_set_simplify(context);
+ context = isl_basic_set_finalize(context);
+
+ return context;
+}
+
+/* Add constraints to "context" that ensure that "u" is the largest
+ * (and therefore active) upper bound on "abs_pos" in "bset" and return
+ * the resulting basic set.
+ */
+static __isl_give isl_basic_set *set_largest_lower_bound(
+ __isl_keep isl_basic_set *context,
+ __isl_keep isl_basic_set *bset, unsigned abs_pos, int n_lower, int l)
+{
+ int j;
+
+ context = isl_basic_set_copy(context);
+ context = isl_basic_set_cow(context);
+
+ context = isl_basic_set_extend_constraints(context, 0, n_lower - 1);
+
+ for (j = 0; j < bset->n_ineq; ++j) {
+ if (j == l)
+ continue;
+ if (!isl_int_is_pos(bset->ineq[j][1 + abs_pos]))
+ continue;
+ context = add_larger_bound_constraint(context,
+ bset->ineq[l], bset->ineq[j], abs_pos, j > l);
+ }
+
+ context = isl_basic_set_simplify(context);
+ context = isl_basic_set_finalize(context);
+
+ return context;
+}
+
+static isl_stat foreach_upper_bound(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned abs_pos,
+ __isl_take isl_basic_set *context, int n_upper,
+ isl_stat (*fn)(__isl_take isl_constraint *lower,
+ __isl_take isl_constraint *upper,
+ __isl_take isl_basic_set *bset, void *user), void *user)
+{
+ isl_basic_set *context_i;
+ isl_constraint *upper = NULL;
+ int i;
+
+ for (i = 0; i < bset->n_ineq; ++i) {
+ if (isl_int_is_zero(bset->ineq[i][1 + abs_pos]))
+ continue;
+
+ context_i = set_smallest_upper_bound(context, bset,
+ abs_pos, n_upper, i);
+ if (isl_basic_set_is_empty(context_i)) {
+ isl_basic_set_free(context_i);
+ continue;
+ }
+ upper = isl_basic_set_constraint(isl_basic_set_copy(bset),
+ &bset->ineq[i]);
+ if (!upper || !context_i)
+ goto error;
+ if (fn(NULL, upper, context_i, user) < 0)
+ break;
+ }
+
+ isl_basic_set_free(context);
+
+ if (i < bset->n_ineq)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+error:
+ isl_constraint_free(upper);
+ isl_basic_set_free(context_i);
+ isl_basic_set_free(context);
+ return isl_stat_error;
+}
+
+static isl_stat foreach_lower_bound(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned abs_pos,
+ __isl_take isl_basic_set *context, int n_lower,
+ isl_stat (*fn)(__isl_take isl_constraint *lower,
+ __isl_take isl_constraint *upper,
+ __isl_take isl_basic_set *bset, void *user), void *user)
+{
+ isl_basic_set *context_i;
+ isl_constraint *lower = NULL;
+ int i;
+
+ for (i = 0; i < bset->n_ineq; ++i) {
+ if (isl_int_is_zero(bset->ineq[i][1 + abs_pos]))
+ continue;
+
+ context_i = set_largest_lower_bound(context, bset,
+ abs_pos, n_lower, i);
+ if (isl_basic_set_is_empty(context_i)) {
+ isl_basic_set_free(context_i);
+ continue;
+ }
+ lower = isl_basic_set_constraint(isl_basic_set_copy(bset),
+ &bset->ineq[i]);
+ if (!lower || !context_i)
+ goto error;
+ if (fn(lower, NULL, context_i, user) < 0)
+ break;
+ }
+
+ isl_basic_set_free(context);
+
+ if (i < bset->n_ineq)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+error:
+ isl_constraint_free(lower);
+ isl_basic_set_free(context_i);
+ isl_basic_set_free(context);
+ return isl_stat_error;
+}
+
+static isl_stat foreach_bound_pair(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned abs_pos,
+ __isl_take isl_basic_set *context, int n_lower, int n_upper,
+ isl_stat (*fn)(__isl_take isl_constraint *lower,
+ __isl_take isl_constraint *upper,
+ __isl_take isl_basic_set *bset, void *user), void *user)
+{
+ isl_basic_set *context_i, *context_j;
+ isl_constraint *lower = NULL;
+ isl_constraint *upper = NULL;
+ int i, j;
+
+ for (i = 0; i < bset->n_ineq; ++i) {
+ if (!isl_int_is_pos(bset->ineq[i][1 + abs_pos]))
+ continue;
+
+ context_i = set_largest_lower_bound(context, bset,
+ abs_pos, n_lower, i);
+ if (isl_basic_set_is_empty(context_i)) {
+ isl_basic_set_free(context_i);
+ continue;
+ }
+
+ for (j = 0; j < bset->n_ineq; ++j) {
+ if (!isl_int_is_neg(bset->ineq[j][1 + abs_pos]))
+ continue;
+
+ context_j = set_smallest_upper_bound(context_i, bset,
+ abs_pos, n_upper, j);
+ context_j = isl_basic_set_extend_constraints(context_j,
+ 0, 1);
+ context_j = add_larger_bound_constraint(context_j,
+ bset->ineq[i], bset->ineq[j], abs_pos, 0);
+ context_j = isl_basic_set_simplify(context_j);
+ context_j = isl_basic_set_finalize(context_j);
+ if (isl_basic_set_is_empty(context_j)) {
+ isl_basic_set_free(context_j);
+ continue;
+ }
+ lower = isl_basic_set_constraint(isl_basic_set_copy(bset),
+ &bset->ineq[i]);
+ upper = isl_basic_set_constraint(isl_basic_set_copy(bset),
+ &bset->ineq[j]);
+ if (!lower || !upper || !context_j)
+ goto error;
+ if (fn(lower, upper, context_j, user) < 0)
+ break;
+ }
+
+ isl_basic_set_free(context_i);
+
+ if (j < bset->n_ineq)
+ break;
+ }
+
+ isl_basic_set_free(context);
+
+ if (i < bset->n_ineq)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+error:
+ isl_constraint_free(lower);
+ isl_constraint_free(upper);
+ isl_basic_set_free(context_i);
+ isl_basic_set_free(context_j);
+ isl_basic_set_free(context);
+ return isl_stat_error;
+}
+
+/* For each pair of lower and upper bounds on the variable "pos"
+ * of type "type", call "fn" with these lower and upper bounds and the
+ * set of constraints on the remaining variables where these bounds
+ * are active, i.e., (stricly) larger/smaller than the other lower/upper bounds.
+ *
+ * If the designated variable is equal to an affine combination of the
+ * other variables then fn is called with both lower and upper
+ * set to the corresponding equality.
+ *
+ * If there is no lower (or upper) bound, then NULL is passed
+ * as the corresponding bound.
+ *
+ * We first check if the variable is involved in any equality.
+ * If not, we count the number of lower and upper bounds and
+ * act accordingly.
+ */
+isl_stat isl_basic_set_foreach_bound_pair(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos,
+ isl_stat (*fn)(__isl_take isl_constraint *lower,
+ __isl_take isl_constraint *upper,
+ __isl_take isl_basic_set *bset, void *user), void *user)
+{
+ int i;
+ isl_constraint *lower = NULL;
+ isl_constraint *upper = NULL;
+ isl_basic_set *context = NULL;
+ unsigned abs_pos;
+ int n_lower, n_upper;
+ isl_size off;
+
+ if (isl_basic_set_check_range(bset, type, pos, 1) < 0)
+ return isl_stat_error;
+ isl_assert(bset->ctx, type == isl_dim_param || type == isl_dim_set,
+ return isl_stat_error);
+
+ off = isl_basic_set_var_offset(bset, type);
+ if (off < 0)
+ return isl_stat_error;
+ abs_pos = off + pos;
+
+ for (i = 0; i < bset->n_eq; ++i) {
+ if (isl_int_is_zero(bset->eq[i][1 + abs_pos]))
+ continue;
+
+ lower = isl_basic_set_constraint(isl_basic_set_copy(bset),
+ &bset->eq[i]);
+ upper = isl_constraint_copy(lower);
+ context = isl_basic_set_remove_dims(isl_basic_set_copy(bset),
+ type, pos, 1);
+ if (!lower || !upper || !context)
+ goto error;
+ return fn(lower, upper, context, user);
+ }
+
+ n_lower = 0;
+ n_upper = 0;
+ for (i = 0; i < bset->n_ineq; ++i) {
+ if (isl_int_is_pos(bset->ineq[i][1 + abs_pos]))
+ n_lower++;
+ else if (isl_int_is_neg(bset->ineq[i][1 + abs_pos]))
+ n_upper++;
+ }
+
+ context = isl_basic_set_copy(bset);
+ context = isl_basic_set_cow(context);
+ if (!context)
+ goto error;
+ for (i = context->n_ineq - 1; i >= 0; --i)
+ if (!isl_int_is_zero(context->ineq[i][1 + abs_pos]))
+ isl_basic_set_drop_inequality(context, i);
+
+ context = isl_basic_set_drop(context, type, pos, 1);
+ if (!n_lower && !n_upper)
+ return fn(NULL, NULL, context, user);
+ if (!n_lower)
+ return foreach_upper_bound(bset, type, abs_pos, context, n_upper,
+ fn, user);
+ if (!n_upper)
+ return foreach_lower_bound(bset, type, abs_pos, context, n_lower,
+ fn, user);
+ return foreach_bound_pair(bset, type, abs_pos, context, n_lower, n_upper,
+ fn, user);
+error:
+ isl_constraint_free(lower);
+ isl_constraint_free(upper);
+ isl_basic_set_free(context);
+ return isl_stat_error;
+}
+
+__isl_give isl_aff *isl_constraint_get_bound(
+ __isl_keep isl_constraint *constraint, enum isl_dim_type type, int pos)
+{
+ isl_space *space;
+ isl_aff *aff;
+ isl_ctx *ctx;
+
+ if (isl_constraint_check_range(constraint, type, pos, 1) < 0)
+ return NULL;
+ space = isl_constraint_peek_space(constraint);
+ if (isl_space_check_is_set(space) < 0)
+ return NULL;
+
+ ctx = isl_constraint_get_ctx(constraint);
+ pos += offset(constraint, type);
+ if (isl_int_is_zero(constraint->v->el[pos]))
+ isl_die(ctx, isl_error_invalid,
+ "constraint does not define a bound on given dimension",
+ return NULL);
+
+ aff = isl_aff_alloc(isl_local_space_copy(constraint->ls));
+ if (!aff)
+ return NULL;
+
+ if (isl_int_is_neg(constraint->v->el[pos]))
+ isl_seq_cpy(aff->v->el + 1, constraint->v->el, aff->v->size - 1);
+ else
+ isl_seq_neg(aff->v->el + 1, constraint->v->el, aff->v->size - 1);
+ isl_int_set_si(aff->v->el[1 + pos], 0);
+ isl_int_abs(aff->v->el[0], constraint->v->el[pos]);
+ aff = isl_aff_normalize(aff);
+
+ return aff;
+}
+
+/* For an inequality constraint
+ *
+ * f >= 0
+ *
+ * or an equality constraint
+ *
+ * f = 0
+ *
+ * return the affine expression f.
+ */
+__isl_give isl_aff *isl_constraint_get_aff(
+ __isl_keep isl_constraint *constraint)
+{
+ isl_aff *aff;
+
+ if (!constraint)
+ return NULL;
+
+ aff = isl_aff_alloc(isl_local_space_copy(constraint->ls));
+ if (!aff)
+ return NULL;
+
+ isl_seq_cpy(aff->v->el + 1, constraint->v->el, aff->v->size - 1);
+ isl_int_set_si(aff->v->el[0], 1);
+
+ return aff;
+}
+
+/* Construct an inequality (eq = 0) or equality (eq = 1) constraint from "aff".
+ * In particular, construct aff >= 0 or aff = 0.
+ *
+ * The denominator of "aff" can be ignored.
+ */
+static __isl_give isl_constraint *isl_constraint_alloc_aff(int eq,
+ __isl_take isl_aff *aff)
+{
+ isl_local_space *ls;
+ isl_vec *v;
+
+ if (!aff)
+ return NULL;
+ ls = isl_aff_get_domain_local_space(aff);
+ v = isl_vec_drop_els(isl_vec_copy(aff->v), 0, 1);
+ isl_aff_free(aff);
+
+ return isl_constraint_alloc_vec(eq, ls, v);
+}
+
+/* Construct an equality constraint equating the given affine expression
+ * to zero.
+ */
+__isl_give isl_constraint *isl_equality_from_aff(__isl_take isl_aff *aff)
+{
+ return isl_constraint_alloc_aff(1, aff);
+}
+
+/* Construct an inequality constraint enforcing the given affine expression
+ * to be non-negative.
+ */
+__isl_give isl_constraint *isl_inequality_from_aff(__isl_take isl_aff *aff)
+{
+ return isl_constraint_alloc_aff(0, aff);
+}
+
+/* Compare two isl_constraints.
+ *
+ * Return -1 if "c1" is "smaller" than "c2", 1 if "c1" is "greater"
+ * than "c2" and 0 if they are equal.
+ *
+ * The order is fairly arbitrary. We do consider constraints that only involve
+ * earlier dimensions as "smaller".
+ */
+int isl_constraint_plain_cmp(__isl_keep isl_constraint *c1,
+ __isl_keep isl_constraint *c2)
+{
+ int cmp;
+ int last1, last2;
+
+ if (c1 == c2)
+ return 0;
+ if (!c1)
+ return -1;
+ if (!c2)
+ return 1;
+ cmp = isl_local_space_cmp(c1->ls, c2->ls);
+ if (cmp != 0)
+ return cmp;
+
+ last1 = isl_seq_last_non_zero(c1->v->el + 1, c1->v->size - 1);
+ last2 = isl_seq_last_non_zero(c2->v->el + 1, c1->v->size - 1);
+ if (last1 != last2)
+ return last1 - last2;
+
+ return isl_seq_cmp(c1->v->el, c2->v->el, c1->v->size);
+}
+
+/* Compare two constraints based on their final (non-zero) coefficients.
+ * In particular, the constraint that involves later variables or
+ * that has a larger coefficient for a shared latest variable
+ * is considered "greater" than the other constraint.
+ *
+ * Return -1 if "c1" is "smaller" than "c2", 1 if "c1" is "greater"
+ * than "c2" and 0 if they are equal.
+ *
+ * If the constraints live in different local spaces, then we cannot
+ * really compare the constraints so we compare the local spaces instead.
+ */
+int isl_constraint_cmp_last_non_zero(__isl_keep isl_constraint *c1,
+ __isl_keep isl_constraint *c2)
+{
+ int cmp;
+ int last1, last2;
+
+ if (c1 == c2)
+ return 0;
+ if (!c1)
+ return -1;
+ if (!c2)
+ return 1;
+ cmp = isl_local_space_cmp(c1->ls, c2->ls);
+ if (cmp != 0)
+ return cmp;
+
+ last1 = isl_seq_last_non_zero(c1->v->el + 1, c1->v->size - 1);
+ last2 = isl_seq_last_non_zero(c2->v->el + 1, c1->v->size - 1);
+ if (last1 != last2)
+ return last1 - last2;
+ if (last1 == -1)
+ return 0;
+ return isl_int_abs_cmp(c1->v->el[1 + last1], c2->v->el[1 + last2]);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_constraint_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_constraint_private.h
new file mode 100644
index 00000000000..901bdcac2e7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_constraint_private.h
@@ -0,0 +1,32 @@
+#ifndef ISL_CONSTRAINT_PRIVATE_H
+#define ISL_CONSTRAINT_PRIVATE_H
+
+#include <isl/constraint.h>
+#include <isl/local_space.h>
+#include <isl/vec.h>
+
+struct isl_constraint {
+ int ref;
+
+ int eq;
+ isl_local_space *ls;
+ isl_vec *v;
+};
+
+#undef EL
+#define EL isl_constraint
+
+#include <isl_list_templ.h>
+
+__isl_give isl_constraint *isl_basic_set_constraint(
+ __isl_take isl_basic_set *bset, isl_int **line);
+
+void isl_constraint_get_constant(__isl_keep isl_constraint *constraint,
+ isl_int *v);
+void isl_constraint_get_coefficient(__isl_keep isl_constraint *constraint,
+ enum isl_dim_type type, int pos, isl_int *v);
+
+isl_bool isl_constraint_is_div_equality(__isl_keep isl_constraint *constraint,
+ unsigned div);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_convex_hull.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_convex_hull.c
new file mode 100644
index 00000000000..a15092e68d5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_convex_hull.c
@@ -0,0 +1,3148 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2014 INRIA Rocquencourt
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl_lp_private.h>
+#include <isl/map.h>
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+#include <isl/set.h>
+#include <isl_seq.h>
+#include <isl_options_private.h>
+#include "isl_equalities.h"
+#include "isl_tab.h"
+#include <isl_sort.h>
+
+#include <bset_to_bmap.c>
+#include <bset_from_bmap.c>
+#include <set_to_map.c>
+
+static __isl_give isl_basic_set *uset_convex_hull_wrap_bounded(
+ __isl_take isl_set *set);
+
+/* Remove redundant
+ * constraints. If the minimal value along the normal of a constraint
+ * is the same if the constraint is removed, then the constraint is redundant.
+ *
+ * Since some constraints may be mutually redundant, sort the constraints
+ * first such that constraints that involve existentially quantified
+ * variables are considered for removal before those that do not.
+ * The sorting is also needed for the use in map_simple_hull.
+ *
+ * Note that isl_tab_detect_implicit_equalities may also end up
+ * marking some constraints as redundant. Make sure the constraints
+ * are preserved and undo those marking such that isl_tab_detect_redundant
+ * can consider the constraints in the sorted order.
+ *
+ * Alternatively, we could have intersected the basic map with the
+ * corresponding equality and then checked if the dimension was that
+ * of a facet.
+ */
+__isl_give isl_basic_map *isl_basic_map_remove_redundancies(
+ __isl_take isl_basic_map *bmap)
+{
+ struct isl_tab *tab;
+
+ if (!bmap)
+ return NULL;
+
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY))
+ return bmap;
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_NO_REDUNDANT))
+ return bmap;
+ if (bmap->n_ineq <= 1)
+ return bmap;
+
+ bmap = isl_basic_map_sort_constraints(bmap);
+ tab = isl_tab_from_basic_map(bmap, 0);
+ if (!tab)
+ goto error;
+ tab->preserve = 1;
+ if (isl_tab_detect_implicit_equalities(tab) < 0)
+ goto error;
+ if (isl_tab_restore_redundant(tab) < 0)
+ goto error;
+ tab->preserve = 0;
+ if (isl_tab_detect_redundant(tab) < 0)
+ goto error;
+ bmap = isl_basic_map_update_from_tab(bmap, tab);
+ isl_tab_free(tab);
+ if (!bmap)
+ return NULL;
+ ISL_F_SET(bmap, ISL_BASIC_MAP_NO_IMPLICIT);
+ ISL_F_SET(bmap, ISL_BASIC_MAP_NO_REDUNDANT);
+ return bmap;
+error:
+ isl_tab_free(tab);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_remove_redundancies(
+ __isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(
+ isl_basic_map_remove_redundancies(bset_to_bmap(bset)));
+}
+
+/* Remove redundant constraints in each of the basic maps.
+ */
+__isl_give isl_map *isl_map_remove_redundancies(__isl_take isl_map *map)
+{
+ return isl_map_inline_foreach_basic_map(map,
+ &isl_basic_map_remove_redundancies);
+}
+
+__isl_give isl_set *isl_set_remove_redundancies(__isl_take isl_set *set)
+{
+ return isl_map_remove_redundancies(set);
+}
+
+/* Check if the set set is bound in the direction of the affine
+ * constraint c and if so, set the constant term such that the
+ * resulting constraint is a bounding constraint for the set.
+ */
+static isl_bool uset_is_bound(__isl_keep isl_set *set, isl_int *c, unsigned len)
+{
+ int first;
+ int j;
+ isl_int opt;
+ isl_int opt_denom;
+
+ isl_int_init(opt);
+ isl_int_init(opt_denom);
+ first = 1;
+ for (j = 0; j < set->n; ++j) {
+ enum isl_lp_result res;
+
+ if (ISL_F_ISSET(set->p[j], ISL_BASIC_SET_EMPTY))
+ continue;
+
+ res = isl_basic_set_solve_lp(set->p[j],
+ 0, c, set->ctx->one, &opt, &opt_denom, NULL);
+ if (res == isl_lp_unbounded)
+ break;
+ if (res == isl_lp_error)
+ goto error;
+ if (res == isl_lp_empty) {
+ set->p[j] = isl_basic_set_set_to_empty(set->p[j]);
+ if (!set->p[j])
+ goto error;
+ continue;
+ }
+ if (first || isl_int_is_neg(opt)) {
+ if (!isl_int_is_one(opt_denom))
+ isl_seq_scale(c, c, opt_denom, len);
+ isl_int_sub(c[0], c[0], opt);
+ }
+ first = 0;
+ }
+ isl_int_clear(opt);
+ isl_int_clear(opt_denom);
+ return isl_bool_ok(j >= set->n);
+error:
+ isl_int_clear(opt);
+ isl_int_clear(opt_denom);
+ return isl_bool_error;
+}
+
+static __isl_give isl_set *isl_set_add_basic_set_equality(
+ __isl_take isl_set *set, isl_int *c)
+{
+ int i;
+
+ set = isl_set_cow(set);
+ if (!set)
+ return NULL;
+ for (i = 0; i < set->n; ++i) {
+ set->p[i] = isl_basic_set_add_eq(set->p[i], c);
+ if (!set->p[i])
+ goto error;
+ }
+ return set;
+error:
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Given a union of basic sets, construct the constraints for wrapping
+ * a facet around one of its ridges.
+ * In particular, if each of n the d-dimensional basic sets i in "set"
+ * contains the origin, satisfies the constraints x_1 >= 0 and x_2 >= 0
+ * and is defined by the constraints
+ * [ 1 ]
+ * A_i [ x ] >= 0
+ *
+ * then the resulting set is of dimension n*(1+d) and has as constraints
+ *
+ * [ a_i ]
+ * A_i [ x_i ] >= 0
+ *
+ * a_i >= 0
+ *
+ * \sum_i x_{i,1} = 1
+ */
+static __isl_give isl_basic_set *wrap_constraints(__isl_keep isl_set *set)
+{
+ struct isl_basic_set *lp;
+ unsigned n_eq;
+ unsigned n_ineq;
+ int i, j, k;
+ isl_size dim, lp_dim;
+
+ dim = isl_set_dim(set, isl_dim_set);
+ if (dim < 0)
+ return NULL;
+
+ dim += 1;
+ n_eq = 1;
+ n_ineq = set->n;
+ for (i = 0; i < set->n; ++i) {
+ n_eq += set->p[i]->n_eq;
+ n_ineq += set->p[i]->n_ineq;
+ }
+ lp = isl_basic_set_alloc(set->ctx, 0, dim * set->n, 0, n_eq, n_ineq);
+ lp = isl_basic_set_set_rational(lp);
+ if (!lp)
+ return NULL;
+ lp_dim = isl_basic_set_dim(lp, isl_dim_set);
+ if (lp_dim < 0)
+ return isl_basic_set_free(lp);
+ k = isl_basic_set_alloc_equality(lp);
+ isl_int_set_si(lp->eq[k][0], -1);
+ for (i = 0; i < set->n; ++i) {
+ isl_int_set_si(lp->eq[k][1+dim*i], 0);
+ isl_int_set_si(lp->eq[k][1+dim*i+1], 1);
+ isl_seq_clr(lp->eq[k]+1+dim*i+2, dim-2);
+ }
+ for (i = 0; i < set->n; ++i) {
+ k = isl_basic_set_alloc_inequality(lp);
+ isl_seq_clr(lp->ineq[k], 1+lp_dim);
+ isl_int_set_si(lp->ineq[k][1+dim*i], 1);
+
+ for (j = 0; j < set->p[i]->n_eq; ++j) {
+ k = isl_basic_set_alloc_equality(lp);
+ isl_seq_clr(lp->eq[k], 1+dim*i);
+ isl_seq_cpy(lp->eq[k]+1+dim*i, set->p[i]->eq[j], dim);
+ isl_seq_clr(lp->eq[k]+1+dim*(i+1), dim*(set->n-i-1));
+ }
+
+ for (j = 0; j < set->p[i]->n_ineq; ++j) {
+ k = isl_basic_set_alloc_inequality(lp);
+ isl_seq_clr(lp->ineq[k], 1+dim*i);
+ isl_seq_cpy(lp->ineq[k]+1+dim*i, set->p[i]->ineq[j], dim);
+ isl_seq_clr(lp->ineq[k]+1+dim*(i+1), dim*(set->n-i-1));
+ }
+ }
+ return lp;
+}
+
+/* Given a facet "facet" of the convex hull of "set" and a facet "ridge"
+ * of that facet, compute the other facet of the convex hull that contains
+ * the ridge.
+ *
+ * We first transform the set such that the facet constraint becomes
+ *
+ * x_1 >= 0
+ *
+ * I.e., the facet lies in
+ *
+ * x_1 = 0
+ *
+ * and on that facet, the constraint that defines the ridge is
+ *
+ * x_2 >= 0
+ *
+ * (This transformation is not strictly needed, all that is needed is
+ * that the ridge contains the origin.)
+ *
+ * Since the ridge contains the origin, the cone of the convex hull
+ * will be of the form
+ *
+ * x_1 >= 0
+ * x_2 >= a x_1
+ *
+ * with this second constraint defining the new facet.
+ * The constant a is obtained by settting x_1 in the cone of the
+ * convex hull to 1 and minimizing x_2.
+ * Now, each element in the cone of the convex hull is the sum
+ * of elements in the cones of the basic sets.
+ * If a_i is the dilation factor of basic set i, then the problem
+ * we need to solve is
+ *
+ * min \sum_i x_{i,2}
+ * st
+ * \sum_i x_{i,1} = 1
+ * a_i >= 0
+ * [ a_i ]
+ * A [ x_i ] >= 0
+ *
+ * with
+ * [ 1 ]
+ * A_i [ x_i ] >= 0
+ *
+ * the constraints of each (transformed) basic set.
+ * If a = n/d, then the constraint defining the new facet (in the transformed
+ * space) is
+ *
+ * -n x_1 + d x_2 >= 0
+ *
+ * In the original space, we need to take the same combination of the
+ * corresponding constraints "facet" and "ridge".
+ *
+ * If a = -infty = "-1/0", then we just return the original facet constraint.
+ * This means that the facet is unbounded, but has a bounded intersection
+ * with the union of sets.
+ */
+isl_int *isl_set_wrap_facet(__isl_keep isl_set *set,
+ isl_int *facet, isl_int *ridge)
+{
+ int i;
+ isl_ctx *ctx;
+ struct isl_mat *T = NULL;
+ struct isl_basic_set *lp = NULL;
+ struct isl_vec *obj;
+ enum isl_lp_result res;
+ isl_int num, den;
+ isl_size dim;
+
+ dim = isl_set_dim(set, isl_dim_set);
+ if (dim < 0)
+ return NULL;
+ ctx = set->ctx;
+ set = isl_set_copy(set);
+ set = isl_set_set_rational(set);
+
+ dim += 1;
+ T = isl_mat_alloc(ctx, 3, dim);
+ if (!T)
+ goto error;
+ isl_int_set_si(T->row[0][0], 1);
+ isl_seq_clr(T->row[0]+1, dim - 1);
+ isl_seq_cpy(T->row[1], facet, dim);
+ isl_seq_cpy(T->row[2], ridge, dim);
+ T = isl_mat_right_inverse(T);
+ set = isl_set_preimage(set, T);
+ T = NULL;
+ if (!set)
+ goto error;
+ lp = wrap_constraints(set);
+ obj = isl_vec_alloc(ctx, 1 + dim*set->n);
+ if (!obj)
+ goto error;
+ isl_int_set_si(obj->block.data[0], 0);
+ for (i = 0; i < set->n; ++i) {
+ isl_seq_clr(obj->block.data + 1 + dim*i, 2);
+ isl_int_set_si(obj->block.data[1 + dim*i+2], 1);
+ isl_seq_clr(obj->block.data + 1 + dim*i+3, dim-3);
+ }
+ isl_int_init(num);
+ isl_int_init(den);
+ res = isl_basic_set_solve_lp(lp, 0,
+ obj->block.data, ctx->one, &num, &den, NULL);
+ if (res == isl_lp_ok) {
+ isl_int_neg(num, num);
+ isl_seq_combine(facet, num, facet, den, ridge, dim);
+ isl_seq_normalize(ctx, facet, dim);
+ }
+ isl_int_clear(num);
+ isl_int_clear(den);
+ isl_vec_free(obj);
+ isl_basic_set_free(lp);
+ isl_set_free(set);
+ if (res == isl_lp_error)
+ return NULL;
+ isl_assert(ctx, res == isl_lp_ok || res == isl_lp_unbounded,
+ return NULL);
+ return facet;
+error:
+ isl_basic_set_free(lp);
+ isl_mat_free(T);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Compute the constraint of a facet of "set".
+ *
+ * We first compute the intersection with a bounding constraint
+ * that is orthogonal to one of the coordinate axes.
+ * If the affine hull of this intersection has only one equality,
+ * we have found a facet.
+ * Otherwise, we wrap the current bounding constraint around
+ * one of the equalities of the face (one that is not equal to
+ * the current bounding constraint).
+ * This process continues until we have found a facet.
+ * The dimension of the intersection increases by at least
+ * one on each iteration, so termination is guaranteed.
+ */
+static __isl_give isl_mat *initial_facet_constraint(__isl_keep isl_set *set)
+{
+ struct isl_set *slice = NULL;
+ struct isl_basic_set *face = NULL;
+ int i;
+ isl_size dim = isl_set_dim(set, isl_dim_set);
+ isl_bool is_bound;
+ isl_mat *bounds = NULL;
+
+ if (dim < 0)
+ return NULL;
+ isl_assert(set->ctx, set->n > 0, goto error);
+ bounds = isl_mat_alloc(set->ctx, 1, 1 + dim);
+ if (!bounds)
+ return NULL;
+
+ isl_seq_clr(bounds->row[0], dim);
+ isl_int_set_si(bounds->row[0][1 + dim - 1], 1);
+ is_bound = uset_is_bound(set, bounds->row[0], 1 + dim);
+ if (is_bound < 0)
+ goto error;
+ isl_assert(set->ctx, is_bound, goto error);
+ isl_seq_normalize(set->ctx, bounds->row[0], 1 + dim);
+ bounds->n_row = 1;
+
+ for (;;) {
+ slice = isl_set_copy(set);
+ slice = isl_set_add_basic_set_equality(slice, bounds->row[0]);
+ face = isl_set_affine_hull(slice);
+ if (!face)
+ goto error;
+ if (face->n_eq == 1) {
+ isl_basic_set_free(face);
+ break;
+ }
+ for (i = 0; i < face->n_eq; ++i)
+ if (!isl_seq_eq(bounds->row[0], face->eq[i], 1 + dim) &&
+ !isl_seq_is_neg(bounds->row[0],
+ face->eq[i], 1 + dim))
+ break;
+ isl_assert(set->ctx, i < face->n_eq, goto error);
+ if (!isl_set_wrap_facet(set, bounds->row[0], face->eq[i]))
+ goto error;
+ isl_seq_normalize(set->ctx, bounds->row[0], bounds->n_col);
+ isl_basic_set_free(face);
+ }
+
+ return bounds;
+error:
+ isl_basic_set_free(face);
+ isl_mat_free(bounds);
+ return NULL;
+}
+
+/* Given the bounding constraint "c" of a facet of the convex hull of "set",
+ * compute a hyperplane description of the facet, i.e., compute the facets
+ * of the facet.
+ *
+ * We compute an affine transformation that transforms the constraint
+ *
+ * [ 1 ]
+ * c [ x ] = 0
+ *
+ * to the constraint
+ *
+ * z_1 = 0
+ *
+ * by computing the right inverse U of a matrix that starts with the rows
+ *
+ * [ 1 0 ]
+ * [ c ]
+ *
+ * Then
+ * [ 1 ] [ 1 ]
+ * [ x ] = U [ z ]
+ * and
+ * [ 1 ] [ 1 ]
+ * [ z ] = Q [ x ]
+ *
+ * with Q = U^{-1}
+ * Since z_1 is zero, we can drop this variable as well as the corresponding
+ * column of U to obtain
+ *
+ * [ 1 ] [ 1 ]
+ * [ x ] = U' [ z' ]
+ * and
+ * [ 1 ] [ 1 ]
+ * [ z' ] = Q' [ x ]
+ *
+ * with Q' equal to Q, but without the corresponding row.
+ * After computing the facets of the facet in the z' space,
+ * we convert them back to the x space through Q.
+ */
+static __isl_give isl_basic_set *compute_facet(__isl_keep isl_set *set,
+ isl_int *c)
+{
+ struct isl_mat *m, *U, *Q;
+ struct isl_basic_set *facet = NULL;
+ struct isl_ctx *ctx;
+ isl_size dim;
+
+ dim = isl_set_dim(set, isl_dim_set);
+ if (dim < 0)
+ return NULL;
+ ctx = set->ctx;
+ set = isl_set_copy(set);
+ m = isl_mat_alloc(set->ctx, 2, 1 + dim);
+ if (!m)
+ goto error;
+ isl_int_set_si(m->row[0][0], 1);
+ isl_seq_clr(m->row[0]+1, dim);
+ isl_seq_cpy(m->row[1], c, 1+dim);
+ U = isl_mat_right_inverse(m);
+ Q = isl_mat_right_inverse(isl_mat_copy(U));
+ U = isl_mat_drop_cols(U, 1, 1);
+ Q = isl_mat_drop_rows(Q, 1, 1);
+ set = isl_set_preimage(set, U);
+ facet = uset_convex_hull_wrap_bounded(set);
+ facet = isl_basic_set_preimage(facet, Q);
+ if (facet && facet->n_eq != 0)
+ isl_die(ctx, isl_error_internal, "unexpected equality",
+ return isl_basic_set_free(facet));
+ return facet;
+error:
+ isl_basic_set_free(facet);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Given an initial facet constraint, compute the remaining facets.
+ * We do this by running through all facets found so far and computing
+ * the adjacent facets through wrapping, adding those facets that we
+ * hadn't already found before.
+ *
+ * For each facet we have found so far, we first compute its facets
+ * in the resulting convex hull. That is, we compute the ridges
+ * of the resulting convex hull contained in the facet.
+ * We also compute the corresponding facet in the current approximation
+ * of the convex hull. There is no need to wrap around the ridges
+ * in this facet since that would result in a facet that is already
+ * present in the current approximation.
+ *
+ * This function can still be significantly optimized by checking which of
+ * the facets of the basic sets are also facets of the convex hull and
+ * using all the facets so far to help in constructing the facets of the
+ * facets
+ * and/or
+ * using the technique in section "3.1 Ridge Generation" of
+ * "Extended Convex Hull" by Fukuda et al.
+ */
+static __isl_give isl_basic_set *extend(__isl_take isl_basic_set *hull,
+ __isl_keep isl_set *set)
+{
+ int i, j, f;
+ int k;
+ struct isl_basic_set *facet = NULL;
+ struct isl_basic_set *hull_facet = NULL;
+ isl_size dim;
+
+ dim = isl_set_dim(set, isl_dim_set);
+ if (dim < 0 || !hull)
+ return isl_basic_set_free(hull);
+
+ isl_assert(set->ctx, set->n > 0, goto error);
+
+ for (i = 0; i < hull->n_ineq; ++i) {
+ facet = compute_facet(set, hull->ineq[i]);
+ facet = isl_basic_set_add_eq(facet, hull->ineq[i]);
+ facet = isl_basic_set_gauss(facet, NULL);
+ facet = isl_basic_set_normalize_constraints(facet);
+ hull_facet = isl_basic_set_copy(hull);
+ hull_facet = isl_basic_set_add_eq(hull_facet, hull->ineq[i]);
+ hull_facet = isl_basic_set_gauss(hull_facet, NULL);
+ hull_facet = isl_basic_set_normalize_constraints(hull_facet);
+ if (!facet || !hull_facet)
+ goto error;
+ hull = isl_basic_set_cow(hull);
+ hull = isl_basic_set_extend(hull, 0, 0, facet->n_ineq);
+ if (!hull)
+ goto error;
+ for (j = 0; j < facet->n_ineq; ++j) {
+ for (f = 0; f < hull_facet->n_ineq; ++f)
+ if (isl_seq_eq(facet->ineq[j],
+ hull_facet->ineq[f], 1 + dim))
+ break;
+ if (f < hull_facet->n_ineq)
+ continue;
+ k = isl_basic_set_alloc_inequality(hull);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(hull->ineq[k], hull->ineq[i], 1+dim);
+ if (!isl_set_wrap_facet(set, hull->ineq[k], facet->ineq[j]))
+ goto error;
+ }
+ isl_basic_set_free(hull_facet);
+ isl_basic_set_free(facet);
+ }
+ hull = isl_basic_set_simplify(hull);
+ hull = isl_basic_set_finalize(hull);
+ return hull;
+error:
+ isl_basic_set_free(hull_facet);
+ isl_basic_set_free(facet);
+ isl_basic_set_free(hull);
+ return NULL;
+}
+
+/* Special case for computing the convex hull of a one dimensional set.
+ * We simply collect the lower and upper bounds of each basic set
+ * and the biggest of those.
+ */
+static __isl_give isl_basic_set *convex_hull_1d(__isl_take isl_set *set)
+{
+ struct isl_mat *c = NULL;
+ isl_int *lower = NULL;
+ isl_int *upper = NULL;
+ int i, j, k;
+ isl_int a, b;
+ struct isl_basic_set *hull;
+
+ for (i = 0; i < set->n; ++i) {
+ set->p[i] = isl_basic_set_simplify(set->p[i]);
+ if (!set->p[i])
+ goto error;
+ }
+ set = isl_set_remove_empty_parts(set);
+ if (!set)
+ goto error;
+ isl_assert(set->ctx, set->n > 0, goto error);
+ c = isl_mat_alloc(set->ctx, 2, 2);
+ if (!c)
+ goto error;
+
+ if (set->p[0]->n_eq > 0) {
+ isl_assert(set->ctx, set->p[0]->n_eq == 1, goto error);
+ lower = c->row[0];
+ upper = c->row[1];
+ if (isl_int_is_pos(set->p[0]->eq[0][1])) {
+ isl_seq_cpy(lower, set->p[0]->eq[0], 2);
+ isl_seq_neg(upper, set->p[0]->eq[0], 2);
+ } else {
+ isl_seq_neg(lower, set->p[0]->eq[0], 2);
+ isl_seq_cpy(upper, set->p[0]->eq[0], 2);
+ }
+ } else {
+ for (j = 0; j < set->p[0]->n_ineq; ++j) {
+ if (isl_int_is_pos(set->p[0]->ineq[j][1])) {
+ lower = c->row[0];
+ isl_seq_cpy(lower, set->p[0]->ineq[j], 2);
+ } else {
+ upper = c->row[1];
+ isl_seq_cpy(upper, set->p[0]->ineq[j], 2);
+ }
+ }
+ }
+
+ isl_int_init(a);
+ isl_int_init(b);
+ for (i = 0; i < set->n; ++i) {
+ struct isl_basic_set *bset = set->p[i];
+ int has_lower = 0;
+ int has_upper = 0;
+
+ for (j = 0; j < bset->n_eq; ++j) {
+ has_lower = 1;
+ has_upper = 1;
+ if (lower) {
+ isl_int_mul(a, lower[0], bset->eq[j][1]);
+ isl_int_mul(b, lower[1], bset->eq[j][0]);
+ if (isl_int_lt(a, b) && isl_int_is_pos(bset->eq[j][1]))
+ isl_seq_cpy(lower, bset->eq[j], 2);
+ if (isl_int_gt(a, b) && isl_int_is_neg(bset->eq[j][1]))
+ isl_seq_neg(lower, bset->eq[j], 2);
+ }
+ if (upper) {
+ isl_int_mul(a, upper[0], bset->eq[j][1]);
+ isl_int_mul(b, upper[1], bset->eq[j][0]);
+ if (isl_int_lt(a, b) && isl_int_is_pos(bset->eq[j][1]))
+ isl_seq_neg(upper, bset->eq[j], 2);
+ if (isl_int_gt(a, b) && isl_int_is_neg(bset->eq[j][1]))
+ isl_seq_cpy(upper, bset->eq[j], 2);
+ }
+ }
+ for (j = 0; j < bset->n_ineq; ++j) {
+ if (isl_int_is_pos(bset->ineq[j][1]))
+ has_lower = 1;
+ if (isl_int_is_neg(bset->ineq[j][1]))
+ has_upper = 1;
+ if (lower && isl_int_is_pos(bset->ineq[j][1])) {
+ isl_int_mul(a, lower[0], bset->ineq[j][1]);
+ isl_int_mul(b, lower[1], bset->ineq[j][0]);
+ if (isl_int_lt(a, b))
+ isl_seq_cpy(lower, bset->ineq[j], 2);
+ }
+ if (upper && isl_int_is_neg(bset->ineq[j][1])) {
+ isl_int_mul(a, upper[0], bset->ineq[j][1]);
+ isl_int_mul(b, upper[1], bset->ineq[j][0]);
+ if (isl_int_gt(a, b))
+ isl_seq_cpy(upper, bset->ineq[j], 2);
+ }
+ }
+ if (!has_lower)
+ lower = NULL;
+ if (!has_upper)
+ upper = NULL;
+ }
+ isl_int_clear(a);
+ isl_int_clear(b);
+
+ hull = isl_basic_set_alloc(set->ctx, 0, 1, 0, 0, 2);
+ hull = isl_basic_set_set_rational(hull);
+ if (!hull)
+ goto error;
+ if (lower) {
+ k = isl_basic_set_alloc_inequality(hull);
+ isl_seq_cpy(hull->ineq[k], lower, 2);
+ }
+ if (upper) {
+ k = isl_basic_set_alloc_inequality(hull);
+ isl_seq_cpy(hull->ineq[k], upper, 2);
+ }
+ hull = isl_basic_set_finalize(hull);
+ isl_set_free(set);
+ isl_mat_free(c);
+ return hull;
+error:
+ isl_set_free(set);
+ isl_mat_free(c);
+ return NULL;
+}
+
+static __isl_give isl_basic_set *convex_hull_0d(__isl_take isl_set *set)
+{
+ struct isl_basic_set *convex_hull;
+
+ if (!set)
+ return NULL;
+
+ if (isl_set_is_empty(set))
+ convex_hull = isl_basic_set_empty(isl_space_copy(set->dim));
+ else
+ convex_hull = isl_basic_set_universe(isl_space_copy(set->dim));
+ isl_set_free(set);
+ return convex_hull;
+}
+
+/* Compute the convex hull of a pair of basic sets without any parameters or
+ * integer divisions using Fourier-Motzkin elimination.
+ * The convex hull is the set of all points that can be written as
+ * the sum of points from both basic sets (in homogeneous coordinates).
+ * We set up the constraints in a space with dimensions for each of
+ * the three sets and then project out the dimensions corresponding
+ * to the two original basic sets, retaining only those corresponding
+ * to the convex hull.
+ */
+static __isl_give isl_basic_set *convex_hull_pair_elim(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2)
+{
+ int i, j, k;
+ struct isl_basic_set *bset[2];
+ struct isl_basic_set *hull = NULL;
+ isl_size dim;
+
+ dim = isl_basic_set_dim(bset1, isl_dim_set);
+ if (dim < 0 || !bset2)
+ goto error;
+
+ hull = isl_basic_set_alloc(bset1->ctx, 0, 2 + 3 * dim, 0,
+ 1 + dim + bset1->n_eq + bset2->n_eq,
+ 2 + bset1->n_ineq + bset2->n_ineq);
+ bset[0] = bset1;
+ bset[1] = bset2;
+ for (i = 0; i < 2; ++i) {
+ for (j = 0; j < bset[i]->n_eq; ++j) {
+ k = isl_basic_set_alloc_equality(hull);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(hull->eq[k], (i+1) * (1+dim));
+ isl_seq_clr(hull->eq[k]+(i+2)*(1+dim), (1-i)*(1+dim));
+ isl_seq_cpy(hull->eq[k]+(i+1)*(1+dim), bset[i]->eq[j],
+ 1+dim);
+ }
+ for (j = 0; j < bset[i]->n_ineq; ++j) {
+ k = isl_basic_set_alloc_inequality(hull);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(hull->ineq[k], (i+1) * (1+dim));
+ isl_seq_clr(hull->ineq[k]+(i+2)*(1+dim), (1-i)*(1+dim));
+ isl_seq_cpy(hull->ineq[k]+(i+1)*(1+dim),
+ bset[i]->ineq[j], 1+dim);
+ }
+ k = isl_basic_set_alloc_inequality(hull);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(hull->ineq[k], 1+2+3*dim);
+ isl_int_set_si(hull->ineq[k][(i+1)*(1+dim)], 1);
+ }
+ for (j = 0; j < 1+dim; ++j) {
+ k = isl_basic_set_alloc_equality(hull);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(hull->eq[k], 1+2+3*dim);
+ isl_int_set_si(hull->eq[k][j], -1);
+ isl_int_set_si(hull->eq[k][1+dim+j], 1);
+ isl_int_set_si(hull->eq[k][2*(1+dim)+j], 1);
+ }
+ hull = isl_basic_set_set_rational(hull);
+ hull = isl_basic_set_remove_dims(hull, isl_dim_set, dim, 2*(1+dim));
+ hull = isl_basic_set_remove_redundancies(hull);
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ return hull;
+error:
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ isl_basic_set_free(hull);
+ return NULL;
+}
+
+/* Is the set bounded for each value of the parameters?
+ */
+isl_bool isl_basic_set_is_bounded(__isl_keep isl_basic_set *bset)
+{
+ struct isl_tab *tab;
+ isl_bool bounded;
+
+ if (!bset)
+ return isl_bool_error;
+ if (isl_basic_set_plain_is_empty(bset))
+ return isl_bool_true;
+
+ tab = isl_tab_from_recession_cone(bset, 1);
+ bounded = isl_tab_cone_is_bounded(tab);
+ isl_tab_free(tab);
+ return bounded;
+}
+
+/* Is the image bounded for each value of the parameters and
+ * the domain variables?
+ */
+isl_bool isl_basic_map_image_is_bounded(__isl_keep isl_basic_map *bmap)
+{
+ isl_size nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ isl_size n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ isl_bool bounded;
+
+ if (nparam < 0 || n_in < 0)
+ return isl_bool_error;
+
+ bmap = isl_basic_map_copy(bmap);
+ bmap = isl_basic_map_cow(bmap);
+ bmap = isl_basic_map_move_dims(bmap, isl_dim_param, nparam,
+ isl_dim_in, 0, n_in);
+ bounded = isl_basic_set_is_bounded(bset_from_bmap(bmap));
+ isl_basic_map_free(bmap);
+
+ return bounded;
+}
+
+/* Is the set bounded for each value of the parameters?
+ */
+isl_bool isl_set_is_bounded(__isl_keep isl_set *set)
+{
+ int i;
+
+ if (!set)
+ return isl_bool_error;
+
+ for (i = 0; i < set->n; ++i) {
+ isl_bool bounded = isl_basic_set_is_bounded(set->p[i]);
+ if (!bounded || bounded < 0)
+ return bounded;
+ }
+ return isl_bool_true;
+}
+
+/* Compute the lineality space of the convex hull of bset1 and bset2.
+ *
+ * We first compute the intersection of the recession cone of bset1
+ * with the negative of the recession cone of bset2 and then compute
+ * the linear hull of the resulting cone.
+ */
+static __isl_give isl_basic_set *induced_lineality_space(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2)
+{
+ int i, k;
+ struct isl_basic_set *lin = NULL;
+ isl_size dim;
+
+ dim = isl_basic_set_dim(bset1, isl_dim_all);
+ if (dim < 0 || !bset2)
+ goto error;
+
+ lin = isl_basic_set_alloc_space(isl_basic_set_get_space(bset1), 0,
+ bset1->n_eq + bset2->n_eq,
+ bset1->n_ineq + bset2->n_ineq);
+ lin = isl_basic_set_set_rational(lin);
+ if (!lin)
+ goto error;
+ for (i = 0; i < bset1->n_eq; ++i) {
+ k = isl_basic_set_alloc_equality(lin);
+ if (k < 0)
+ goto error;
+ isl_int_set_si(lin->eq[k][0], 0);
+ isl_seq_cpy(lin->eq[k] + 1, bset1->eq[i] + 1, dim);
+ }
+ for (i = 0; i < bset1->n_ineq; ++i) {
+ k = isl_basic_set_alloc_inequality(lin);
+ if (k < 0)
+ goto error;
+ isl_int_set_si(lin->ineq[k][0], 0);
+ isl_seq_cpy(lin->ineq[k] + 1, bset1->ineq[i] + 1, dim);
+ }
+ for (i = 0; i < bset2->n_eq; ++i) {
+ k = isl_basic_set_alloc_equality(lin);
+ if (k < 0)
+ goto error;
+ isl_int_set_si(lin->eq[k][0], 0);
+ isl_seq_neg(lin->eq[k] + 1, bset2->eq[i] + 1, dim);
+ }
+ for (i = 0; i < bset2->n_ineq; ++i) {
+ k = isl_basic_set_alloc_inequality(lin);
+ if (k < 0)
+ goto error;
+ isl_int_set_si(lin->ineq[k][0], 0);
+ isl_seq_neg(lin->ineq[k] + 1, bset2->ineq[i] + 1, dim);
+ }
+
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ return isl_basic_set_affine_hull(lin);
+error:
+ isl_basic_set_free(lin);
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ return NULL;
+}
+
+static __isl_give isl_basic_set *uset_convex_hull(__isl_take isl_set *set);
+
+/* Given a set and a linear space "lin" of dimension n > 0,
+ * project the linear space from the set, compute the convex hull
+ * and then map the set back to the original space.
+ *
+ * Let
+ *
+ * M x = 0
+ *
+ * describe the linear space. We first compute the Hermite normal
+ * form H = M U of M = H Q, to obtain
+ *
+ * H Q x = 0
+ *
+ * The last n rows of H will be zero, so the last n variables of x' = Q x
+ * are the one we want to project out. We do this by transforming each
+ * basic set A x >= b to A U x' >= b and then removing the last n dimensions.
+ * After computing the convex hull in x'_1, i.e., A' x'_1 >= b',
+ * we transform the hull back to the original space as A' Q_1 x >= b',
+ * with Q_1 all but the last n rows of Q.
+ */
+static __isl_give isl_basic_set *modulo_lineality(__isl_take isl_set *set,
+ __isl_take isl_basic_set *lin)
+{
+ isl_size total = isl_basic_set_dim(lin, isl_dim_all);
+ unsigned lin_dim;
+ struct isl_basic_set *hull;
+ struct isl_mat *M, *U, *Q;
+
+ if (!set || total < 0)
+ goto error;
+ lin_dim = total - lin->n_eq;
+ M = isl_mat_sub_alloc6(set->ctx, lin->eq, 0, lin->n_eq, 1, total);
+ M = isl_mat_left_hermite(M, 0, &U, &Q);
+ if (!M)
+ goto error;
+ isl_mat_free(M);
+ isl_basic_set_free(lin);
+
+ Q = isl_mat_drop_rows(Q, Q->n_row - lin_dim, lin_dim);
+
+ U = isl_mat_lin_to_aff(U);
+ Q = isl_mat_lin_to_aff(Q);
+
+ set = isl_set_preimage(set, U);
+ set = isl_set_remove_dims(set, isl_dim_set, total - lin_dim, lin_dim);
+ hull = uset_convex_hull(set);
+ hull = isl_basic_set_preimage(hull, Q);
+
+ return hull;
+error:
+ isl_basic_set_free(lin);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Given two polyhedra with as constraints h_{ij} x >= 0 in homegeneous space,
+ * set up an LP for solving
+ *
+ * \sum_j \alpha_{1j} h_{1j} = \sum_j \alpha_{2j} h_{2j}
+ *
+ * \alpha{i0} corresponds to the (implicit) positivity constraint 1 >= 0
+ * The next \alpha{ij} correspond to the equalities and come in pairs.
+ * The final \alpha{ij} correspond to the inequalities.
+ */
+static __isl_give isl_basic_set *valid_direction_lp(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2)
+{
+ isl_space *space;
+ struct isl_basic_set *lp;
+ unsigned d;
+ int n;
+ int i, j, k;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset1, isl_dim_all);
+ if (total < 0 || !bset2)
+ goto error;
+ d = 1 + total;
+ n = 2 +
+ 2 * bset1->n_eq + bset1->n_ineq + 2 * bset2->n_eq + bset2->n_ineq;
+ space = isl_space_set_alloc(bset1->ctx, 0, n);
+ lp = isl_basic_set_alloc_space(space, 0, d, n);
+ if (!lp)
+ goto error;
+ for (i = 0; i < n; ++i) {
+ k = isl_basic_set_alloc_inequality(lp);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(lp->ineq[k] + 1, n);
+ isl_int_set_si(lp->ineq[k][0], -1);
+ isl_int_set_si(lp->ineq[k][1 + i], 1);
+ }
+ for (i = 0; i < d; ++i) {
+ k = isl_basic_set_alloc_equality(lp);
+ if (k < 0)
+ goto error;
+ n = 0;
+ isl_int_set_si(lp->eq[k][n], 0); n++;
+ /* positivity constraint 1 >= 0 */
+ isl_int_set_si(lp->eq[k][n], i == 0); n++;
+ for (j = 0; j < bset1->n_eq; ++j) {
+ isl_int_set(lp->eq[k][n], bset1->eq[j][i]); n++;
+ isl_int_neg(lp->eq[k][n], bset1->eq[j][i]); n++;
+ }
+ for (j = 0; j < bset1->n_ineq; ++j) {
+ isl_int_set(lp->eq[k][n], bset1->ineq[j][i]); n++;
+ }
+ /* positivity constraint 1 >= 0 */
+ isl_int_set_si(lp->eq[k][n], -(i == 0)); n++;
+ for (j = 0; j < bset2->n_eq; ++j) {
+ isl_int_neg(lp->eq[k][n], bset2->eq[j][i]); n++;
+ isl_int_set(lp->eq[k][n], bset2->eq[j][i]); n++;
+ }
+ for (j = 0; j < bset2->n_ineq; ++j) {
+ isl_int_neg(lp->eq[k][n], bset2->ineq[j][i]); n++;
+ }
+ }
+ lp = isl_basic_set_gauss(lp, NULL);
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ return lp;
+error:
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ return NULL;
+}
+
+/* Compute a vector s in the homogeneous space such that <s, r> > 0
+ * for all rays in the homogeneous space of the two cones that correspond
+ * to the input polyhedra bset1 and bset2.
+ *
+ * We compute s as a vector that satisfies
+ *
+ * s = \sum_j \alpha_{ij} h_{ij} for i = 1,2 (*)
+ *
+ * with h_{ij} the normals of the facets of polyhedron i
+ * (including the "positivity constraint" 1 >= 0) and \alpha_{ij}
+ * strictly positive numbers. For simplicity we impose \alpha_{ij} >= 1.
+ * We first set up an LP with as variables the \alpha{ij}.
+ * In this formulation, for each polyhedron i,
+ * the first constraint is the positivity constraint, followed by pairs
+ * of variables for the equalities, followed by variables for the inequalities.
+ * We then simply pick a feasible solution and compute s using (*).
+ *
+ * Note that we simply pick any valid direction and make no attempt
+ * to pick a "good" or even the "best" valid direction.
+ */
+static __isl_give isl_vec *valid_direction(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2)
+{
+ struct isl_basic_set *lp;
+ struct isl_tab *tab;
+ struct isl_vec *sample = NULL;
+ struct isl_vec *dir;
+ isl_size d;
+ int i;
+ int n;
+
+ if (!bset1 || !bset2)
+ goto error;
+ lp = valid_direction_lp(isl_basic_set_copy(bset1),
+ isl_basic_set_copy(bset2));
+ tab = isl_tab_from_basic_set(lp, 0);
+ sample = isl_tab_get_sample_value(tab);
+ isl_tab_free(tab);
+ isl_basic_set_free(lp);
+ if (!sample)
+ goto error;
+ d = isl_basic_set_dim(bset1, isl_dim_all);
+ if (d < 0)
+ goto error;
+ dir = isl_vec_alloc(bset1->ctx, 1 + d);
+ if (!dir)
+ goto error;
+ isl_seq_clr(dir->block.data + 1, dir->size - 1);
+ n = 1;
+ /* positivity constraint 1 >= 0 */
+ isl_int_set(dir->block.data[0], sample->block.data[n]); n++;
+ for (i = 0; i < bset1->n_eq; ++i) {
+ isl_int_sub(sample->block.data[n],
+ sample->block.data[n], sample->block.data[n+1]);
+ isl_seq_combine(dir->block.data,
+ bset1->ctx->one, dir->block.data,
+ sample->block.data[n], bset1->eq[i], 1 + d);
+
+ n += 2;
+ }
+ for (i = 0; i < bset1->n_ineq; ++i)
+ isl_seq_combine(dir->block.data,
+ bset1->ctx->one, dir->block.data,
+ sample->block.data[n++], bset1->ineq[i], 1 + d);
+ isl_vec_free(sample);
+ isl_seq_normalize(bset1->ctx, dir->el, dir->size);
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ return dir;
+error:
+ isl_vec_free(sample);
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ return NULL;
+}
+
+/* Given a polyhedron b_i + A_i x >= 0 and a map T = S^{-1},
+ * compute b_i' + A_i' x' >= 0, with
+ *
+ * [ b_i A_i ] [ y' ] [ y' ]
+ * [ 1 0 ] S^{-1} [ x' ] >= 0 or [ b_i' A_i' ] [ x' ] >= 0
+ *
+ * In particular, add the "positivity constraint" and then perform
+ * the mapping.
+ */
+static __isl_give isl_basic_set *homogeneous_map(__isl_take isl_basic_set *bset,
+ __isl_take isl_mat *T)
+{
+ int k;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ goto error;
+ bset = isl_basic_set_extend_constraints(bset, 0, 1);
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bset->ineq[k] + 1, total);
+ isl_int_set_si(bset->ineq[k][0], 1);
+ bset = isl_basic_set_preimage(bset, T);
+ return bset;
+error:
+ isl_mat_free(T);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Compute the convex hull of a pair of basic sets without any parameters or
+ * integer divisions, where the convex hull is known to be pointed,
+ * but the basic sets may be unbounded.
+ *
+ * We turn this problem into the computation of a convex hull of a pair
+ * _bounded_ polyhedra by "changing the direction of the homogeneous
+ * dimension". This idea is due to Matthias Koeppe.
+ *
+ * Consider the cones in homogeneous space that correspond to the
+ * input polyhedra. The rays of these cones are also rays of the
+ * polyhedra if the coordinate that corresponds to the homogeneous
+ * dimension is zero. That is, if the inner product of the rays
+ * with the homogeneous direction is zero.
+ * The cones in the homogeneous space can also be considered to
+ * correspond to other pairs of polyhedra by chosing a different
+ * homogeneous direction. To ensure that both of these polyhedra
+ * are bounded, we need to make sure that all rays of the cones
+ * correspond to vertices and not to rays.
+ * Let s be a direction such that <s, r> > 0 for all rays r of both cones.
+ * Then using s as a homogeneous direction, we obtain a pair of polytopes.
+ * The vector s is computed in valid_direction.
+ *
+ * Note that we need to consider _all_ rays of the cones and not just
+ * the rays that correspond to rays in the polyhedra. If we were to
+ * only consider those rays and turn them into vertices, then we
+ * may inadvertently turn some vertices into rays.
+ *
+ * The standard homogeneous direction is the unit vector in the 0th coordinate.
+ * We therefore transform the two polyhedra such that the selected
+ * direction is mapped onto this standard direction and then proceed
+ * with the normal computation.
+ * Let S be a non-singular square matrix with s as its first row,
+ * then we want to map the polyhedra to the space
+ *
+ * [ y' ] [ y ] [ y ] [ y' ]
+ * [ x' ] = S [ x ] i.e., [ x ] = S^{-1} [ x' ]
+ *
+ * We take S to be the unimodular completion of s to limit the growth
+ * of the coefficients in the following computations.
+ *
+ * Let b_i + A_i x >= 0 be the constraints of polyhedron i.
+ * We first move to the homogeneous dimension
+ *
+ * b_i y + A_i x >= 0 [ b_i A_i ] [ y ] [ 0 ]
+ * y >= 0 or [ 1 0 ] [ x ] >= [ 0 ]
+ *
+ * Then we change directoin
+ *
+ * [ b_i A_i ] [ y' ] [ y' ]
+ * [ 1 0 ] S^{-1} [ x' ] >= 0 or [ b_i' A_i' ] [ x' ] >= 0
+ *
+ * Then we compute the convex hull of the polytopes b_i' + A_i' x' >= 0
+ * resulting in b' + A' x' >= 0, which we then convert back
+ *
+ * [ y ] [ y ]
+ * [ b' A' ] S [ x ] >= 0 or [ b A ] [ x ] >= 0
+ *
+ * The polyhedron b + A x >= 0 is then the convex hull of the input polyhedra.
+ */
+static __isl_give isl_basic_set *convex_hull_pair_pointed(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2)
+{
+ struct isl_ctx *ctx = NULL;
+ struct isl_vec *dir = NULL;
+ struct isl_mat *T = NULL;
+ struct isl_mat *T2 = NULL;
+ struct isl_basic_set *hull;
+ struct isl_set *set;
+
+ if (!bset1 || !bset2)
+ goto error;
+ ctx = isl_basic_set_get_ctx(bset1);
+ dir = valid_direction(isl_basic_set_copy(bset1),
+ isl_basic_set_copy(bset2));
+ if (!dir)
+ goto error;
+ T = isl_mat_alloc(ctx, dir->size, dir->size);
+ if (!T)
+ goto error;
+ isl_seq_cpy(T->row[0], dir->block.data, dir->size);
+ T = isl_mat_unimodular_complete(T, 1);
+ T2 = isl_mat_right_inverse(isl_mat_copy(T));
+
+ bset1 = homogeneous_map(bset1, isl_mat_copy(T2));
+ bset2 = homogeneous_map(bset2, T2);
+ set = isl_set_alloc_space(isl_basic_set_get_space(bset1), 2, 0);
+ set = isl_set_add_basic_set(set, bset1);
+ set = isl_set_add_basic_set(set, bset2);
+ hull = uset_convex_hull(set);
+ hull = isl_basic_set_preimage(hull, T);
+
+ isl_vec_free(dir);
+
+ return hull;
+error:
+ isl_vec_free(dir);
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ return NULL;
+}
+
+static __isl_give isl_basic_set *uset_convex_hull_wrap(__isl_take isl_set *set);
+static __isl_give isl_basic_set *modulo_affine_hull(
+ __isl_take isl_set *set, __isl_take isl_basic_set *affine_hull);
+
+/* Compute the convex hull of a pair of basic sets without any parameters or
+ * integer divisions.
+ *
+ * This function is called from uset_convex_hull_unbounded, which
+ * means that the complete convex hull is unbounded. Some pairs
+ * of basic sets may still be bounded, though.
+ * They may even lie inside a lower dimensional space, in which
+ * case they need to be handled inside their affine hull since
+ * the main algorithm assumes that the result is full-dimensional.
+ *
+ * If the convex hull of the two basic sets would have a non-trivial
+ * lineality space, we first project out this lineality space.
+ */
+static __isl_give isl_basic_set *convex_hull_pair(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2)
+{
+ isl_basic_set *lin, *aff;
+ isl_bool bounded1, bounded2;
+ isl_size total;
+
+ if (bset1->ctx->opt->convex == ISL_CONVEX_HULL_FM)
+ return convex_hull_pair_elim(bset1, bset2);
+
+ aff = isl_set_affine_hull(isl_basic_set_union(isl_basic_set_copy(bset1),
+ isl_basic_set_copy(bset2)));
+ if (!aff)
+ goto error;
+ if (aff->n_eq != 0)
+ return modulo_affine_hull(isl_basic_set_union(bset1, bset2), aff);
+ isl_basic_set_free(aff);
+
+ bounded1 = isl_basic_set_is_bounded(bset1);
+ bounded2 = isl_basic_set_is_bounded(bset2);
+
+ if (bounded1 < 0 || bounded2 < 0)
+ goto error;
+
+ if (bounded1 && bounded2)
+ return uset_convex_hull_wrap(isl_basic_set_union(bset1, bset2));
+
+ if (bounded1 || bounded2)
+ return convex_hull_pair_pointed(bset1, bset2);
+
+ lin = induced_lineality_space(isl_basic_set_copy(bset1),
+ isl_basic_set_copy(bset2));
+ if (!lin)
+ goto error;
+ if (isl_basic_set_plain_is_universe(lin)) {
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ return lin;
+ }
+ total = isl_basic_set_dim(lin, isl_dim_all);
+ if (lin->n_eq < total) {
+ struct isl_set *set;
+ set = isl_set_alloc_space(isl_basic_set_get_space(bset1), 2, 0);
+ set = isl_set_add_basic_set(set, bset1);
+ set = isl_set_add_basic_set(set, bset2);
+ return modulo_lineality(set, lin);
+ }
+ isl_basic_set_free(lin);
+ if (total < 0)
+ goto error;
+
+ return convex_hull_pair_pointed(bset1, bset2);
+error:
+ isl_basic_set_free(bset1);
+ isl_basic_set_free(bset2);
+ return NULL;
+}
+
+/* Compute the lineality space of a basic set.
+ * We basically just drop the constants and turn every inequality
+ * into an equality.
+ * Any explicit representations of local variables are removed
+ * because they may no longer be valid representations
+ * in the lineality space.
+ */
+__isl_give isl_basic_set *isl_basic_set_lineality_space(
+ __isl_take isl_basic_set *bset)
+{
+ int i, k;
+ struct isl_basic_set *lin = NULL;
+ isl_size n_div, dim;
+
+ n_div = isl_basic_set_dim(bset, isl_dim_div);
+ dim = isl_basic_set_dim(bset, isl_dim_all);
+ if (n_div < 0 || dim < 0)
+ return isl_basic_set_free(bset);
+
+ lin = isl_basic_set_alloc_space(isl_basic_set_get_space(bset),
+ n_div, dim, 0);
+ for (i = 0; i < n_div; ++i)
+ if (isl_basic_set_alloc_div(lin) < 0)
+ goto error;
+ if (!lin)
+ goto error;
+ for (i = 0; i < bset->n_eq; ++i) {
+ k = isl_basic_set_alloc_equality(lin);
+ if (k < 0)
+ goto error;
+ isl_int_set_si(lin->eq[k][0], 0);
+ isl_seq_cpy(lin->eq[k] + 1, bset->eq[i] + 1, dim);
+ }
+ lin = isl_basic_set_gauss(lin, NULL);
+ if (!lin)
+ goto error;
+ for (i = 0; i < bset->n_ineq && lin->n_eq < dim; ++i) {
+ k = isl_basic_set_alloc_equality(lin);
+ if (k < 0)
+ goto error;
+ isl_int_set_si(lin->eq[k][0], 0);
+ isl_seq_cpy(lin->eq[k] + 1, bset->ineq[i] + 1, dim);
+ lin = isl_basic_set_gauss(lin, NULL);
+ if (!lin)
+ goto error;
+ }
+ isl_basic_set_free(bset);
+ return lin;
+error:
+ isl_basic_set_free(lin);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Compute the (linear) hull of the lineality spaces of the basic sets in the
+ * set "set".
+ */
+__isl_give isl_basic_set *isl_set_combined_lineality_space(
+ __isl_take isl_set *set)
+{
+ int i;
+ struct isl_set *lin = NULL;
+
+ if (!set)
+ return NULL;
+ if (set->n == 0) {
+ isl_space *space = isl_set_get_space(set);
+ isl_set_free(set);
+ return isl_basic_set_empty(space);
+ }
+
+ lin = isl_set_alloc_space(isl_set_get_space(set), set->n, 0);
+ for (i = 0; i < set->n; ++i)
+ lin = isl_set_add_basic_set(lin,
+ isl_basic_set_lineality_space(isl_basic_set_copy(set->p[i])));
+ isl_set_free(set);
+ return isl_set_affine_hull(lin);
+}
+
+/* Compute the convex hull of a set without any parameters or
+ * integer divisions.
+ * In each step, we combined two basic sets until only one
+ * basic set is left.
+ * The input basic sets are assumed not to have a non-trivial
+ * lineality space. If any of the intermediate results has
+ * a non-trivial lineality space, it is projected out.
+ */
+static __isl_give isl_basic_set *uset_convex_hull_unbounded(
+ __isl_take isl_set *set)
+{
+ isl_basic_set_list *list;
+
+ list = isl_set_get_basic_set_list(set);
+ isl_set_free(set);
+
+ while (list) {
+ isl_size n, total;
+ struct isl_basic_set *t;
+ isl_basic_set *bset1, *bset2;
+
+ n = isl_basic_set_list_n_basic_set(list);
+ if (n < 0)
+ goto error;
+ if (n < 2)
+ isl_die(isl_basic_set_list_get_ctx(list),
+ isl_error_internal,
+ "expecting at least two elements", goto error);
+ bset1 = isl_basic_set_list_get_basic_set(list, n - 1);
+ bset2 = isl_basic_set_list_get_basic_set(list, n - 2);
+ bset1 = convex_hull_pair(bset1, bset2);
+ if (n == 2) {
+ isl_basic_set_list_free(list);
+ return bset1;
+ }
+ bset1 = isl_basic_set_underlying_set(bset1);
+ list = isl_basic_set_list_drop(list, n - 2, 2);
+ list = isl_basic_set_list_add(list, bset1);
+
+ t = isl_basic_set_list_get_basic_set(list, n - 2);
+ t = isl_basic_set_lineality_space(t);
+ if (!t)
+ goto error;
+ if (isl_basic_set_plain_is_universe(t)) {
+ isl_basic_set_list_free(list);
+ return t;
+ }
+ total = isl_basic_set_dim(t, isl_dim_all);
+ if (t->n_eq < total) {
+ set = isl_basic_set_list_union(list);
+ return modulo_lineality(set, t);
+ }
+ isl_basic_set_free(t);
+ if (total < 0)
+ goto error;
+ }
+
+ return NULL;
+error:
+ isl_basic_set_list_free(list);
+ return NULL;
+}
+
+/* Compute an initial hull for wrapping containing a single initial
+ * facet.
+ * This function assumes that the given set is bounded.
+ */
+static __isl_give isl_basic_set *initial_hull(__isl_take isl_basic_set *hull,
+ __isl_keep isl_set *set)
+{
+ struct isl_mat *bounds = NULL;
+ isl_size dim;
+ int k;
+
+ if (!hull)
+ goto error;
+ bounds = initial_facet_constraint(set);
+ if (!bounds)
+ goto error;
+ k = isl_basic_set_alloc_inequality(hull);
+ if (k < 0)
+ goto error;
+ dim = isl_set_dim(set, isl_dim_set);
+ if (dim < 0)
+ goto error;
+ isl_assert(set->ctx, 1 + dim == bounds->n_col, goto error);
+ isl_seq_cpy(hull->ineq[k], bounds->row[0], bounds->n_col);
+ isl_mat_free(bounds);
+
+ return hull;
+error:
+ isl_basic_set_free(hull);
+ isl_mat_free(bounds);
+ return NULL;
+}
+
+struct max_constraint {
+ struct isl_mat *c;
+ int count;
+ int ineq;
+};
+
+static isl_bool max_constraint_equal(const void *entry, const void *val)
+{
+ struct max_constraint *a = (struct max_constraint *)entry;
+ isl_int *b = (isl_int *)val;
+
+ return isl_bool_ok(isl_seq_eq(a->c->row[0] + 1, b, a->c->n_col - 1));
+}
+
+static isl_stat update_constraint(struct isl_ctx *ctx,
+ struct isl_hash_table *table,
+ isl_int *con, unsigned len, int n, int ineq)
+{
+ struct isl_hash_table_entry *entry;
+ struct max_constraint *c;
+ uint32_t c_hash;
+
+ c_hash = isl_seq_get_hash(con + 1, len);
+ entry = isl_hash_table_find(ctx, table, c_hash, max_constraint_equal,
+ con + 1, 0);
+ if (!entry)
+ return isl_stat_error;
+ if (entry == isl_hash_table_entry_none)
+ return isl_stat_ok;
+ c = entry->data;
+ if (c->count < n) {
+ isl_hash_table_remove(ctx, table, entry);
+ return isl_stat_ok;
+ }
+ c->count++;
+ if (isl_int_gt(c->c->row[0][0], con[0]))
+ return isl_stat_ok;
+ if (isl_int_eq(c->c->row[0][0], con[0])) {
+ if (ineq)
+ c->ineq = ineq;
+ return isl_stat_ok;
+ }
+ c->c = isl_mat_cow(c->c);
+ isl_int_set(c->c->row[0][0], con[0]);
+ c->ineq = ineq;
+
+ return isl_stat_ok;
+}
+
+/* Check whether the constraint hash table "table" contains the constraint
+ * "con".
+ */
+static isl_bool has_constraint(struct isl_ctx *ctx,
+ struct isl_hash_table *table, isl_int *con, unsigned len, int n)
+{
+ struct isl_hash_table_entry *entry;
+ struct max_constraint *c;
+ uint32_t c_hash;
+
+ c_hash = isl_seq_get_hash(con + 1, len);
+ entry = isl_hash_table_find(ctx, table, c_hash, max_constraint_equal,
+ con + 1, 0);
+ if (!entry)
+ return isl_bool_error;
+ if (entry == isl_hash_table_entry_none)
+ return isl_bool_false;
+ c = entry->data;
+ if (c->count < n)
+ return isl_bool_false;
+ return isl_bool_ok(isl_int_eq(c->c->row[0][0], con[0]));
+}
+
+/* Are the constraints of "bset" known to be facets?
+ * If there are any equality constraints, then they are not.
+ * If there may be redundant constraints, then those
+ * redundant constraints are not facets.
+ */
+static isl_bool has_facets(__isl_keep isl_basic_set *bset)
+{
+ isl_size n_eq;
+
+ n_eq = isl_basic_set_n_equality(bset);
+ if (n_eq < 0)
+ return isl_bool_error;
+ if (n_eq != 0)
+ return isl_bool_false;
+ return ISL_F_ISSET(bset, ISL_BASIC_SET_NO_REDUNDANT);
+}
+
+/* Check for inequality constraints of a basic set without equalities
+ * or redundant constraints
+ * such that the same or more stringent copies of the constraint appear
+ * in all of the basic sets. Such constraints are necessarily facet
+ * constraints of the convex hull.
+ *
+ * If the resulting basic set is by chance identical to one of
+ * the basic sets in "set", then we know that this basic set contains
+ * all other basic sets and is therefore the convex hull of set.
+ * In this case we set *is_hull to 1.
+ */
+static __isl_give isl_basic_set *common_constraints(
+ __isl_take isl_basic_set *hull, __isl_keep isl_set *set, int *is_hull)
+{
+ int i, j, s, n;
+ int min_constraints;
+ int best;
+ struct max_constraint *constraints = NULL;
+ struct isl_hash_table *table = NULL;
+ isl_size total;
+
+ *is_hull = 0;
+
+ for (i = 0; i < set->n; ++i) {
+ isl_bool facets = has_facets(set->p[i]);
+ if (facets < 0)
+ return isl_basic_set_free(hull);
+ if (facets)
+ break;
+ }
+ if (i >= set->n)
+ return hull;
+ min_constraints = set->p[i]->n_ineq;
+ best = i;
+ for (i = best + 1; i < set->n; ++i) {
+ isl_bool facets = has_facets(set->p[i]);
+ if (facets < 0)
+ return isl_basic_set_free(hull);
+ if (!facets)
+ continue;
+ if (set->p[i]->n_ineq >= min_constraints)
+ continue;
+ min_constraints = set->p[i]->n_ineq;
+ best = i;
+ }
+ constraints = isl_calloc_array(hull->ctx, struct max_constraint,
+ min_constraints);
+ if (!constraints)
+ return hull;
+ table = isl_alloc_type(hull->ctx, struct isl_hash_table);
+ if (isl_hash_table_init(hull->ctx, table, min_constraints))
+ goto error;
+
+ total = isl_set_dim(set, isl_dim_all);
+ if (total < 0)
+ goto error;
+ for (i = 0; i < set->p[best]->n_ineq; ++i) {
+ constraints[i].c = isl_mat_sub_alloc6(hull->ctx,
+ set->p[best]->ineq + i, 0, 1, 0, 1 + total);
+ if (!constraints[i].c)
+ goto error;
+ constraints[i].ineq = 1;
+ }
+ for (i = 0; i < min_constraints; ++i) {
+ struct isl_hash_table_entry *entry;
+ uint32_t c_hash;
+ c_hash = isl_seq_get_hash(constraints[i].c->row[0] + 1, total);
+ entry = isl_hash_table_find(hull->ctx, table, c_hash,
+ max_constraint_equal, constraints[i].c->row[0] + 1, 1);
+ if (!entry)
+ goto error;
+ isl_assert(hull->ctx, !entry->data, goto error);
+ entry->data = &constraints[i];
+ }
+
+ n = 0;
+ for (s = 0; s < set->n; ++s) {
+ if (s == best)
+ continue;
+
+ for (i = 0; i < set->p[s]->n_eq; ++i) {
+ isl_int *eq = set->p[s]->eq[i];
+ for (j = 0; j < 2; ++j) {
+ isl_seq_neg(eq, eq, 1 + total);
+ if (update_constraint(hull->ctx, table,
+ eq, total, n, 0) < 0)
+ goto error;
+ }
+ }
+ for (i = 0; i < set->p[s]->n_ineq; ++i) {
+ isl_int *ineq = set->p[s]->ineq[i];
+ if (update_constraint(hull->ctx, table, ineq, total, n,
+ set->p[s]->n_eq == 0) < 0)
+ goto error;
+ }
+ ++n;
+ }
+
+ for (i = 0; i < min_constraints; ++i) {
+ if (constraints[i].count < n)
+ continue;
+ if (!constraints[i].ineq)
+ continue;
+ j = isl_basic_set_alloc_inequality(hull);
+ if (j < 0)
+ goto error;
+ isl_seq_cpy(hull->ineq[j], constraints[i].c->row[0], 1 + total);
+ }
+
+ for (s = 0; s < set->n; ++s) {
+ if (set->p[s]->n_eq)
+ continue;
+ if (set->p[s]->n_ineq != hull->n_ineq)
+ continue;
+ for (i = 0; i < set->p[s]->n_ineq; ++i) {
+ isl_bool has;
+ isl_int *ineq = set->p[s]->ineq[i];
+ has = has_constraint(hull->ctx, table, ineq, total, n);
+ if (has < 0)
+ goto error;
+ if (!has)
+ break;
+ }
+ if (i == set->p[s]->n_ineq)
+ *is_hull = 1;
+ }
+
+ isl_hash_table_clear(table);
+ for (i = 0; i < min_constraints; ++i)
+ isl_mat_free(constraints[i].c);
+ free(constraints);
+ free(table);
+ return hull;
+error:
+ isl_hash_table_clear(table);
+ free(table);
+ if (constraints)
+ for (i = 0; i < min_constraints; ++i)
+ isl_mat_free(constraints[i].c);
+ free(constraints);
+ return hull;
+}
+
+/* Create a template for the convex hull of "set" and fill it up
+ * obvious facet constraints, if any. If the result happens to
+ * be the convex hull of "set" then *is_hull is set to 1.
+ */
+static __isl_give isl_basic_set *proto_hull(__isl_keep isl_set *set,
+ int *is_hull)
+{
+ struct isl_basic_set *hull;
+ unsigned n_ineq;
+ int i;
+
+ n_ineq = 1;
+ for (i = 0; i < set->n; ++i) {
+ n_ineq += set->p[i]->n_eq;
+ n_ineq += set->p[i]->n_ineq;
+ }
+ hull = isl_basic_set_alloc_space(isl_space_copy(set->dim), 0, 0, n_ineq);
+ hull = isl_basic_set_set_rational(hull);
+ if (!hull)
+ return NULL;
+ return common_constraints(hull, set, is_hull);
+}
+
+static __isl_give isl_basic_set *uset_convex_hull_wrap(__isl_take isl_set *set)
+{
+ struct isl_basic_set *hull;
+ int is_hull;
+
+ hull = proto_hull(set, &is_hull);
+ if (hull && !is_hull) {
+ if (hull->n_ineq == 0)
+ hull = initial_hull(hull, set);
+ hull = extend(hull, set);
+ }
+ isl_set_free(set);
+
+ return hull;
+}
+
+/* Compute the convex hull of a set without any parameters or
+ * integer divisions. Depending on whether the set is bounded,
+ * we pass control to the wrapping based convex hull or
+ * the Fourier-Motzkin elimination based convex hull.
+ * We also handle a few special cases before checking the boundedness.
+ */
+static __isl_give isl_basic_set *uset_convex_hull(__isl_take isl_set *set)
+{
+ isl_bool bounded;
+ isl_size dim;
+ struct isl_basic_set *convex_hull = NULL;
+ struct isl_basic_set *lin;
+
+ dim = isl_set_dim(set, isl_dim_all);
+ if (dim < 0)
+ goto error;
+ if (dim == 0)
+ return convex_hull_0d(set);
+
+ set = isl_set_coalesce(set);
+ set = isl_set_set_rational(set);
+
+ if (!set)
+ return NULL;
+ if (set->n == 1) {
+ convex_hull = isl_basic_set_copy(set->p[0]);
+ isl_set_free(set);
+ return convex_hull;
+ }
+ if (dim == 1)
+ return convex_hull_1d(set);
+
+ bounded = isl_set_is_bounded(set);
+ if (bounded < 0)
+ goto error;
+ if (bounded && set->ctx->opt->convex == ISL_CONVEX_HULL_WRAP)
+ return uset_convex_hull_wrap(set);
+
+ lin = isl_set_combined_lineality_space(isl_set_copy(set));
+ if (!lin)
+ goto error;
+ if (isl_basic_set_plain_is_universe(lin)) {
+ isl_set_free(set);
+ return lin;
+ }
+ if (lin->n_eq < dim)
+ return modulo_lineality(set, lin);
+ isl_basic_set_free(lin);
+
+ return uset_convex_hull_unbounded(set);
+error:
+ isl_set_free(set);
+ isl_basic_set_free(convex_hull);
+ return NULL;
+}
+
+/* This is the core procedure, where "set" is a "pure" set, i.e.,
+ * without parameters or divs and where the convex hull of set is
+ * known to be full-dimensional.
+ */
+static __isl_give isl_basic_set *uset_convex_hull_wrap_bounded(
+ __isl_take isl_set *set)
+{
+ struct isl_basic_set *convex_hull = NULL;
+ isl_size dim;
+
+ dim = isl_set_dim(set, isl_dim_all);
+ if (dim < 0)
+ goto error;
+
+ if (dim == 0) {
+ convex_hull = isl_basic_set_universe(isl_space_copy(set->dim));
+ isl_set_free(set);
+ convex_hull = isl_basic_set_set_rational(convex_hull);
+ return convex_hull;
+ }
+
+ set = isl_set_set_rational(set);
+ set = isl_set_coalesce(set);
+ if (!set)
+ goto error;
+ if (set->n == 1) {
+ convex_hull = isl_basic_set_copy(set->p[0]);
+ isl_set_free(set);
+ convex_hull = isl_basic_map_remove_redundancies(convex_hull);
+ return convex_hull;
+ }
+ if (dim == 1)
+ return convex_hull_1d(set);
+
+ return uset_convex_hull_wrap(set);
+error:
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Compute the convex hull of set "set" with affine hull "affine_hull",
+ * We first remove the equalities (transforming the set), compute the
+ * convex hull of the transformed set and then add the equalities back
+ * (after performing the inverse transformation.
+ */
+static __isl_give isl_basic_set *modulo_affine_hull(
+ __isl_take isl_set *set, __isl_take isl_basic_set *affine_hull)
+{
+ struct isl_mat *T;
+ struct isl_mat *T2;
+ struct isl_basic_set *dummy;
+ struct isl_basic_set *convex_hull;
+
+ dummy = isl_basic_set_remove_equalities(
+ isl_basic_set_copy(affine_hull), &T, &T2);
+ if (!dummy)
+ goto error;
+ isl_basic_set_free(dummy);
+ set = isl_set_preimage(set, T);
+ convex_hull = uset_convex_hull(set);
+ convex_hull = isl_basic_set_preimage(convex_hull, T2);
+ convex_hull = isl_basic_set_intersect(convex_hull, affine_hull);
+ return convex_hull;
+error:
+ isl_mat_free(T);
+ isl_mat_free(T2);
+ isl_basic_set_free(affine_hull);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Return an empty basic map living in the same space as "map".
+ */
+static __isl_give isl_basic_map *replace_map_by_empty_basic_map(
+ __isl_take isl_map *map)
+{
+ isl_space *space;
+
+ space = isl_map_get_space(map);
+ isl_map_free(map);
+ return isl_basic_map_empty(space);
+}
+
+/* Compute the convex hull of a map.
+ *
+ * The implementation was inspired by "Extended Convex Hull" by Fukuda et al.,
+ * specifically, the wrapping of facets to obtain new facets.
+ */
+__isl_give isl_basic_map *isl_map_convex_hull(__isl_take isl_map *map)
+{
+ struct isl_basic_set *bset;
+ struct isl_basic_map *model = NULL;
+ struct isl_basic_set *affine_hull = NULL;
+ struct isl_basic_map *convex_hull = NULL;
+ struct isl_set *set = NULL;
+
+ map = isl_map_detect_equalities(map);
+ map = isl_map_align_divs_internal(map);
+ if (!map)
+ goto error;
+
+ if (map->n == 0)
+ return replace_map_by_empty_basic_map(map);
+
+ model = isl_basic_map_copy(map->p[0]);
+ set = isl_map_underlying_set(map);
+ if (!set)
+ goto error;
+
+ affine_hull = isl_set_affine_hull(isl_set_copy(set));
+ if (!affine_hull)
+ goto error;
+ if (affine_hull->n_eq != 0)
+ bset = modulo_affine_hull(set, affine_hull);
+ else {
+ isl_basic_set_free(affine_hull);
+ bset = uset_convex_hull(set);
+ }
+
+ convex_hull = isl_basic_map_overlying_set(bset, model);
+ if (!convex_hull)
+ return NULL;
+
+ ISL_F_SET(convex_hull, ISL_BASIC_MAP_NO_IMPLICIT);
+ ISL_F_SET(convex_hull, ISL_BASIC_MAP_ALL_EQUALITIES);
+ ISL_F_CLR(convex_hull, ISL_BASIC_MAP_RATIONAL);
+ return convex_hull;
+error:
+ isl_set_free(set);
+ isl_basic_map_free(model);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_set_convex_hull(__isl_take isl_set *set)
+{
+ return bset_from_bmap(isl_map_convex_hull(set_to_map(set)));
+}
+
+__isl_give isl_basic_map *isl_map_polyhedral_hull(__isl_take isl_map *map)
+{
+ isl_basic_map *hull;
+
+ hull = isl_map_convex_hull(map);
+ return isl_basic_map_remove_divs(hull);
+}
+
+__isl_give isl_basic_set *isl_set_polyhedral_hull(__isl_take isl_set *set)
+{
+ return bset_from_bmap(isl_map_polyhedral_hull(set_to_map(set)));
+}
+
+struct sh_data_entry {
+ struct isl_hash_table *table;
+ struct isl_tab *tab;
+};
+
+/* Holds the data needed during the simple hull computation.
+ * In particular,
+ * n the number of basic sets in the original set
+ * hull_table a hash table of already computed constraints
+ * in the simple hull
+ * p for each basic set,
+ * table a hash table of the constraints
+ * tab the tableau corresponding to the basic set
+ */
+struct sh_data {
+ struct isl_ctx *ctx;
+ unsigned n;
+ struct isl_hash_table *hull_table;
+ struct sh_data_entry p[1];
+};
+
+static void sh_data_free(struct sh_data *data)
+{
+ int i;
+
+ if (!data)
+ return;
+ isl_hash_table_free(data->ctx, data->hull_table);
+ for (i = 0; i < data->n; ++i) {
+ isl_hash_table_free(data->ctx, data->p[i].table);
+ isl_tab_free(data->p[i].tab);
+ }
+ free(data);
+}
+
+struct ineq_cmp_data {
+ unsigned len;
+ isl_int *p;
+};
+
+static isl_bool has_ineq(const void *entry, const void *val)
+{
+ isl_int *row = (isl_int *)entry;
+ struct ineq_cmp_data *v = (struct ineq_cmp_data *)val;
+
+ return isl_bool_ok(isl_seq_eq(row + 1, v->p + 1, v->len) ||
+ isl_seq_is_neg(row + 1, v->p + 1, v->len));
+}
+
+static int hash_ineq(struct isl_ctx *ctx, struct isl_hash_table *table,
+ isl_int *ineq, unsigned len)
+{
+ uint32_t c_hash;
+ struct ineq_cmp_data v;
+ struct isl_hash_table_entry *entry;
+
+ v.len = len;
+ v.p = ineq;
+ c_hash = isl_seq_get_hash(ineq + 1, len);
+ entry = isl_hash_table_find(ctx, table, c_hash, has_ineq, &v, 1);
+ if (!entry)
+ return - 1;
+ entry->data = ineq;
+ return 0;
+}
+
+/* Fill hash table "table" with the constraints of "bset".
+ * Equalities are added as two inequalities.
+ * The value in the hash table is a pointer to the (in)equality of "bset".
+ */
+static isl_stat hash_basic_set(struct isl_hash_table *table,
+ __isl_keep isl_basic_set *bset)
+{
+ int i, j;
+ isl_size dim = isl_basic_set_dim(bset, isl_dim_all);
+
+ if (dim < 0)
+ return isl_stat_error;
+ for (i = 0; i < bset->n_eq; ++i) {
+ for (j = 0; j < 2; ++j) {
+ isl_seq_neg(bset->eq[i], bset->eq[i], 1 + dim);
+ if (hash_ineq(bset->ctx, table, bset->eq[i], dim) < 0)
+ return isl_stat_error;
+ }
+ }
+ for (i = 0; i < bset->n_ineq; ++i) {
+ if (hash_ineq(bset->ctx, table, bset->ineq[i], dim) < 0)
+ return isl_stat_error;
+ }
+ return isl_stat_ok;
+}
+
+static struct sh_data *sh_data_alloc(__isl_keep isl_set *set, unsigned n_ineq)
+{
+ struct sh_data *data;
+ int i;
+
+ data = isl_calloc(set->ctx, struct sh_data,
+ sizeof(struct sh_data) +
+ (set->n - 1) * sizeof(struct sh_data_entry));
+ if (!data)
+ return NULL;
+ data->ctx = set->ctx;
+ data->n = set->n;
+ data->hull_table = isl_hash_table_alloc(set->ctx, n_ineq);
+ if (!data->hull_table)
+ goto error;
+ for (i = 0; i < set->n; ++i) {
+ data->p[i].table = isl_hash_table_alloc(set->ctx,
+ 2 * set->p[i]->n_eq + set->p[i]->n_ineq);
+ if (!data->p[i].table)
+ goto error;
+ if (hash_basic_set(data->p[i].table, set->p[i]) < 0)
+ goto error;
+ }
+ return data;
+error:
+ sh_data_free(data);
+ return NULL;
+}
+
+/* Check if inequality "ineq" is a bound for basic set "j" or if
+ * it can be relaxed (by increasing the constant term) to become
+ * a bound for that basic set. In the latter case, the constant
+ * term is updated.
+ * Relaxation of the constant term is only allowed if "shift" is set.
+ *
+ * Return 1 if "ineq" is a bound
+ * 0 if "ineq" may attain arbitrarily small values on basic set "j"
+ * -1 if some error occurred
+ */
+static int is_bound(struct sh_data *data, __isl_keep isl_set *set, int j,
+ isl_int *ineq, int shift)
+{
+ enum isl_lp_result res;
+ isl_int opt;
+
+ if (!data->p[j].tab) {
+ data->p[j].tab = isl_tab_from_basic_set(set->p[j], 0);
+ if (!data->p[j].tab)
+ return -1;
+ }
+
+ isl_int_init(opt);
+
+ res = isl_tab_min(data->p[j].tab, ineq, data->ctx->one,
+ &opt, NULL, 0);
+ if (res == isl_lp_ok && isl_int_is_neg(opt)) {
+ if (shift)
+ isl_int_sub(ineq[0], ineq[0], opt);
+ else
+ res = isl_lp_unbounded;
+ }
+
+ isl_int_clear(opt);
+
+ return (res == isl_lp_ok || res == isl_lp_empty) ? 1 :
+ res == isl_lp_unbounded ? 0 : -1;
+}
+
+/* Set the constant term of "ineq" to the maximum of those of the constraints
+ * in the basic sets of "set" following "i" that are parallel to "ineq".
+ * That is, if any of the basic sets of "set" following "i" have a more
+ * relaxed copy of "ineq", then replace "ineq" by the most relaxed copy.
+ * "c_hash" is the hash value of the linear part of "ineq".
+ * "v" has been set up for use by has_ineq.
+ *
+ * Note that the two inequality constraints corresponding to an equality are
+ * represented by the same inequality constraint in data->p[j].table
+ * (but with different hash values). This means the constraint (or at
+ * least its constant term) may need to be temporarily negated to get
+ * the actually hashed constraint.
+ */
+static isl_stat set_max_constant_term(struct sh_data *data,
+ __isl_keep isl_set *set,
+ int i, isl_int *ineq, uint32_t c_hash, struct ineq_cmp_data *v)
+{
+ int j;
+ isl_ctx *ctx;
+ struct isl_hash_table_entry *entry;
+
+ ctx = isl_set_get_ctx(set);
+ for (j = i + 1; j < set->n; ++j) {
+ int neg;
+ isl_int *ineq_j;
+
+ entry = isl_hash_table_find(ctx, data->p[j].table,
+ c_hash, &has_ineq, v, 0);
+ if (!entry)
+ return isl_stat_error;
+ if (entry == isl_hash_table_entry_none)
+ continue;
+
+ ineq_j = entry->data;
+ neg = isl_seq_is_neg(ineq_j + 1, ineq + 1, v->len);
+ if (neg)
+ isl_int_neg(ineq_j[0], ineq_j[0]);
+ if (isl_int_gt(ineq_j[0], ineq[0]))
+ isl_int_set(ineq[0], ineq_j[0]);
+ if (neg)
+ isl_int_neg(ineq_j[0], ineq_j[0]);
+ }
+
+ return isl_stat_ok;
+}
+
+/* Check if inequality "ineq" from basic set "i" is or can be relaxed to
+ * become a bound on the whole set. If so, add the (relaxed) inequality
+ * to "hull". Relaxation is only allowed if "shift" is set.
+ *
+ * We first check if "hull" already contains a translate of the inequality.
+ * If so, we are done.
+ * Then, we check if any of the previous basic sets contains a translate
+ * of the inequality. If so, then we have already considered this
+ * inequality and we are done.
+ * Otherwise, for each basic set other than "i", we check if the inequality
+ * is a bound on the basic set, but first replace the constant term
+ * by the maximal value of any translate of the inequality in any
+ * of the following basic sets.
+ * For previous basic sets, we know that they do not contain a translate
+ * of the inequality, so we directly call is_bound.
+ * For following basic sets, we first check if a translate of the
+ * inequality appears in its description. If so, the constant term
+ * of the inequality has already been updated with respect to this
+ * translate and the inequality is therefore known to be a bound
+ * of this basic set.
+ */
+static __isl_give isl_basic_set *add_bound(__isl_take isl_basic_set *hull,
+ struct sh_data *data, __isl_keep isl_set *set, int i, isl_int *ineq,
+ int shift)
+{
+ uint32_t c_hash;
+ struct ineq_cmp_data v;
+ struct isl_hash_table_entry *entry;
+ int j, k;
+ isl_size total;
+
+ total = isl_basic_set_dim(hull, isl_dim_all);
+ if (total < 0)
+ return isl_basic_set_free(hull);
+
+ v.len = total;
+ v.p = ineq;
+ c_hash = isl_seq_get_hash(ineq + 1, v.len);
+
+ entry = isl_hash_table_find(hull->ctx, data->hull_table, c_hash,
+ has_ineq, &v, 0);
+ if (!entry)
+ return isl_basic_set_free(hull);
+ if (entry != isl_hash_table_entry_none)
+ return hull;
+
+ for (j = 0; j < i; ++j) {
+ entry = isl_hash_table_find(hull->ctx, data->p[j].table,
+ c_hash, has_ineq, &v, 0);
+ if (!entry)
+ return isl_basic_set_free(hull);
+ if (entry != isl_hash_table_entry_none)
+ break;
+ }
+ if (j < i)
+ return hull;
+
+ k = isl_basic_set_alloc_inequality(hull);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(hull->ineq[k], ineq, 1 + v.len);
+
+ if (set_max_constant_term(data, set, i, hull->ineq[k], c_hash, &v) < 0)
+ goto error;
+ for (j = 0; j < i; ++j) {
+ int bound;
+ bound = is_bound(data, set, j, hull->ineq[k], shift);
+ if (bound < 0)
+ goto error;
+ if (!bound)
+ break;
+ }
+ if (j < i)
+ return isl_basic_set_free_inequality(hull, 1);
+
+ for (j = i + 1; j < set->n; ++j) {
+ int bound;
+ entry = isl_hash_table_find(hull->ctx, data->p[j].table,
+ c_hash, has_ineq, &v, 0);
+ if (!entry)
+ return isl_basic_set_free(hull);
+ if (entry != isl_hash_table_entry_none)
+ continue;
+ bound = is_bound(data, set, j, hull->ineq[k], shift);
+ if (bound < 0)
+ goto error;
+ if (!bound)
+ break;
+ }
+ if (j < set->n)
+ return isl_basic_set_free_inequality(hull, 1);
+
+ entry = isl_hash_table_find(hull->ctx, data->hull_table, c_hash,
+ has_ineq, &v, 1);
+ if (!entry)
+ goto error;
+ entry->data = hull->ineq[k];
+
+ return hull;
+error:
+ isl_basic_set_free(hull);
+ return NULL;
+}
+
+/* Check if any inequality from basic set "i" is or can be relaxed to
+ * become a bound on the whole set. If so, add the (relaxed) inequality
+ * to "hull". Relaxation is only allowed if "shift" is set.
+ */
+static __isl_give isl_basic_set *add_bounds(__isl_take isl_basic_set *bset,
+ struct sh_data *data, __isl_keep isl_set *set, int i, int shift)
+{
+ int j, k;
+ isl_size dim = isl_basic_set_dim(bset, isl_dim_all);
+
+ if (dim < 0)
+ return isl_basic_set_free(bset);
+
+ for (j = 0; j < set->p[i]->n_eq; ++j) {
+ for (k = 0; k < 2; ++k) {
+ isl_seq_neg(set->p[i]->eq[j], set->p[i]->eq[j], 1+dim);
+ bset = add_bound(bset, data, set, i, set->p[i]->eq[j],
+ shift);
+ }
+ }
+ for (j = 0; j < set->p[i]->n_ineq; ++j)
+ bset = add_bound(bset, data, set, i, set->p[i]->ineq[j], shift);
+ return bset;
+}
+
+/* Compute a superset of the convex hull of set that is described
+ * by only (translates of) the constraints in the constituents of set.
+ * Translation is only allowed if "shift" is set.
+ */
+static __isl_give isl_basic_set *uset_simple_hull(__isl_take isl_set *set,
+ int shift)
+{
+ struct sh_data *data = NULL;
+ struct isl_basic_set *hull = NULL;
+ unsigned n_ineq;
+ int i;
+
+ if (!set)
+ return NULL;
+
+ n_ineq = 0;
+ for (i = 0; i < set->n; ++i) {
+ if (!set->p[i])
+ goto error;
+ n_ineq += 2 * set->p[i]->n_eq + set->p[i]->n_ineq;
+ }
+
+ hull = isl_basic_set_alloc_space(isl_space_copy(set->dim), 0, 0, n_ineq);
+ if (!hull)
+ goto error;
+
+ data = sh_data_alloc(set, n_ineq);
+ if (!data)
+ goto error;
+
+ for (i = 0; i < set->n; ++i)
+ hull = add_bounds(hull, data, set, i, shift);
+
+ sh_data_free(data);
+ isl_set_free(set);
+
+ return hull;
+error:
+ sh_data_free(data);
+ isl_basic_set_free(hull);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Compute a superset of the convex hull of map that is described
+ * by only (translates of) the constraints in the constituents of map.
+ * Handle trivial cases where map is NULL or contains at most one disjunct.
+ */
+static __isl_give isl_basic_map *map_simple_hull_trivial(
+ __isl_take isl_map *map)
+{
+ isl_basic_map *hull;
+
+ if (!map)
+ return NULL;
+ if (map->n == 0)
+ return replace_map_by_empty_basic_map(map);
+
+ hull = isl_basic_map_copy(map->p[0]);
+ isl_map_free(map);
+ return hull;
+}
+
+/* Return a copy of the simple hull cached inside "map".
+ * "shift" determines whether to return the cached unshifted or shifted
+ * simple hull.
+ */
+static __isl_give isl_basic_map *cached_simple_hull(__isl_take isl_map *map,
+ int shift)
+{
+ isl_basic_map *hull;
+
+ hull = isl_basic_map_copy(map->cached_simple_hull[shift]);
+ isl_map_free(map);
+
+ return hull;
+}
+
+/* Compute a superset of the convex hull of map that is described
+ * by only (translates of) the constraints in the constituents of map.
+ * Translation is only allowed if "shift" is set.
+ *
+ * The constraints are sorted while removing redundant constraints
+ * in order to indicate a preference of which constraints should
+ * be preserved. In particular, pairs of constraints that are
+ * sorted together are preferred to either both be preserved
+ * or both be removed. The sorting is performed inside
+ * isl_basic_map_remove_redundancies.
+ *
+ * The result of the computation is stored in map->cached_simple_hull[shift]
+ * such that it can be reused in subsequent calls. The cache is cleared
+ * whenever the map is modified (in isl_map_cow).
+ * Note that the results need to be stored in the input map for there
+ * to be any chance that they may get reused. In particular, they
+ * are stored in a copy of the input map that is saved before
+ * the integer division alignment.
+ */
+static __isl_give isl_basic_map *map_simple_hull(__isl_take isl_map *map,
+ int shift)
+{
+ struct isl_set *set = NULL;
+ struct isl_basic_map *model = NULL;
+ struct isl_basic_map *hull;
+ struct isl_basic_map *affine_hull;
+ struct isl_basic_set *bset = NULL;
+ isl_map *input;
+
+ if (!map || map->n <= 1)
+ return map_simple_hull_trivial(map);
+
+ if (map->cached_simple_hull[shift])
+ return cached_simple_hull(map, shift);
+
+ map = isl_map_detect_equalities(map);
+ if (!map || map->n <= 1)
+ return map_simple_hull_trivial(map);
+ affine_hull = isl_map_affine_hull(isl_map_copy(map));
+ input = isl_map_copy(map);
+ map = isl_map_align_divs_internal(map);
+ model = map ? isl_basic_map_copy(map->p[0]) : NULL;
+
+ set = isl_map_underlying_set(map);
+
+ bset = uset_simple_hull(set, shift);
+
+ hull = isl_basic_map_overlying_set(bset, model);
+
+ hull = isl_basic_map_intersect(hull, affine_hull);
+ hull = isl_basic_map_remove_redundancies(hull);
+
+ if (hull) {
+ ISL_F_SET(hull, ISL_BASIC_MAP_NO_IMPLICIT);
+ ISL_F_SET(hull, ISL_BASIC_MAP_ALL_EQUALITIES);
+ }
+
+ hull = isl_basic_map_finalize(hull);
+ if (input)
+ input->cached_simple_hull[shift] = isl_basic_map_copy(hull);
+ isl_map_free(input);
+
+ return hull;
+}
+
+/* Compute a superset of the convex hull of map that is described
+ * by only translates of the constraints in the constituents of map.
+ */
+__isl_give isl_basic_map *isl_map_simple_hull(__isl_take isl_map *map)
+{
+ return map_simple_hull(map, 1);
+}
+
+__isl_give isl_basic_set *isl_set_simple_hull(__isl_take isl_set *set)
+{
+ return bset_from_bmap(isl_map_simple_hull(set_to_map(set)));
+}
+
+/* Compute a superset of the convex hull of map that is described
+ * by only the constraints in the constituents of map.
+ */
+__isl_give isl_basic_map *isl_map_unshifted_simple_hull(
+ __isl_take isl_map *map)
+{
+ return map_simple_hull(map, 0);
+}
+
+__isl_give isl_basic_set *isl_set_unshifted_simple_hull(
+ __isl_take isl_set *set)
+{
+ return isl_map_unshifted_simple_hull(set);
+}
+
+/* Drop all inequalities from "bmap1" that do not also appear in "bmap2".
+ * A constraint that appears with different constant terms
+ * in "bmap1" and "bmap2" is also kept, with the least restrictive
+ * (i.e., greatest) constant term.
+ * "bmap1" and "bmap2" are assumed to have the same (known)
+ * integer divisions.
+ * The constraints of both "bmap1" and "bmap2" are assumed
+ * to have been sorted using isl_basic_map_sort_constraints.
+ *
+ * Run through the inequality constraints of "bmap1" and "bmap2"
+ * in sorted order.
+ * Each constraint of "bmap1" without a matching constraint in "bmap2"
+ * is removed.
+ * If a match is found, the constraint is kept. If needed, the constant
+ * term of the constraint is adjusted.
+ */
+static __isl_give isl_basic_map *select_shared_inequalities(
+ __isl_take isl_basic_map *bmap1, __isl_keep isl_basic_map *bmap2)
+{
+ int i1, i2;
+
+ bmap1 = isl_basic_map_cow(bmap1);
+ if (!bmap1 || !bmap2)
+ return isl_basic_map_free(bmap1);
+
+ i1 = bmap1->n_ineq - 1;
+ i2 = bmap2->n_ineq - 1;
+ while (bmap1 && i1 >= 0 && i2 >= 0) {
+ int cmp;
+
+ cmp = isl_basic_map_constraint_cmp(bmap1, bmap1->ineq[i1],
+ bmap2->ineq[i2]);
+ if (cmp < 0) {
+ --i2;
+ continue;
+ }
+ if (cmp > 0) {
+ if (isl_basic_map_drop_inequality(bmap1, i1) < 0)
+ bmap1 = isl_basic_map_free(bmap1);
+ --i1;
+ continue;
+ }
+ if (isl_int_lt(bmap1->ineq[i1][0], bmap2->ineq[i2][0]))
+ isl_int_set(bmap1->ineq[i1][0], bmap2->ineq[i2][0]);
+ --i1;
+ --i2;
+ }
+ for (; i1 >= 0; --i1)
+ if (isl_basic_map_drop_inequality(bmap1, i1) < 0)
+ bmap1 = isl_basic_map_free(bmap1);
+
+ return bmap1;
+}
+
+/* Drop all equalities from "bmap1" that do not also appear in "bmap2".
+ * "bmap1" and "bmap2" are assumed to have the same (known)
+ * integer divisions.
+ *
+ * Run through the equality constraints of "bmap1" and "bmap2".
+ * Each constraint of "bmap1" without a matching constraint in "bmap2"
+ * is removed.
+ */
+static __isl_give isl_basic_map *select_shared_equalities(
+ __isl_take isl_basic_map *bmap1, __isl_keep isl_basic_map *bmap2)
+{
+ int i1, i2;
+ isl_size total;
+
+ bmap1 = isl_basic_map_cow(bmap1);
+ total = isl_basic_map_dim(bmap1, isl_dim_all);
+ if (total < 0 || !bmap2)
+ return isl_basic_map_free(bmap1);
+
+ i1 = bmap1->n_eq - 1;
+ i2 = bmap2->n_eq - 1;
+ while (bmap1 && i1 >= 0 && i2 >= 0) {
+ int last1, last2;
+
+ last1 = isl_seq_last_non_zero(bmap1->eq[i1] + 1, total);
+ last2 = isl_seq_last_non_zero(bmap2->eq[i2] + 1, total);
+ if (last1 > last2) {
+ --i2;
+ continue;
+ }
+ if (last1 < last2) {
+ if (isl_basic_map_drop_equality(bmap1, i1) < 0)
+ bmap1 = isl_basic_map_free(bmap1);
+ --i1;
+ continue;
+ }
+ if (!isl_seq_eq(bmap1->eq[i1], bmap2->eq[i2], 1 + total)) {
+ if (isl_basic_map_drop_equality(bmap1, i1) < 0)
+ bmap1 = isl_basic_map_free(bmap1);
+ }
+ --i1;
+ --i2;
+ }
+ for (; i1 >= 0; --i1)
+ if (isl_basic_map_drop_equality(bmap1, i1) < 0)
+ bmap1 = isl_basic_map_free(bmap1);
+
+ return bmap1;
+}
+
+/* Compute a superset of "bmap1" and "bmap2" that is described
+ * by only the constraints that appear in both "bmap1" and "bmap2".
+ *
+ * First drop constraints that involve unknown integer divisions
+ * since it is not trivial to check whether two such integer divisions
+ * in different basic maps are the same.
+ * Then align the remaining (known) divs and sort the constraints.
+ * Finally drop all inequalities and equalities from "bmap1" that
+ * do not also appear in "bmap2".
+ */
+__isl_give isl_basic_map *isl_basic_map_plain_unshifted_simple_hull(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2)
+{
+ if (isl_basic_map_check_equal_space(bmap1, bmap2) < 0)
+ goto error;
+
+ bmap1 = isl_basic_map_drop_constraints_involving_unknown_divs(bmap1);
+ bmap2 = isl_basic_map_drop_constraints_involving_unknown_divs(bmap2);
+ bmap1 = isl_basic_map_order_divs(bmap1);
+ bmap2 = isl_basic_map_align_divs(bmap2, bmap1);
+ bmap1 = isl_basic_map_align_divs(bmap1, bmap2);
+ bmap1 = isl_basic_map_sort_constraints(bmap1);
+ bmap2 = isl_basic_map_sort_constraints(bmap2);
+
+ bmap1 = select_shared_inequalities(bmap1, bmap2);
+ bmap1 = select_shared_equalities(bmap1, bmap2);
+
+ isl_basic_map_free(bmap2);
+ bmap1 = isl_basic_map_finalize(bmap1);
+ return bmap1;
+error:
+ isl_basic_map_free(bmap1);
+ isl_basic_map_free(bmap2);
+ return NULL;
+}
+
+/* Compute a superset of the convex hull of "map" that is described
+ * by only the constraints in the constituents of "map".
+ * In particular, the result is composed of constraints that appear
+ * in each of the basic maps of "map"
+ *
+ * Constraints that involve unknown integer divisions are dropped
+ * since it is not trivial to check whether two such integer divisions
+ * in different basic maps are the same.
+ *
+ * The hull is initialized from the first basic map and then
+ * updated with respect to the other basic maps in turn.
+ */
+__isl_give isl_basic_map *isl_map_plain_unshifted_simple_hull(
+ __isl_take isl_map *map)
+{
+ int i;
+ isl_basic_map *hull;
+
+ if (!map)
+ return NULL;
+ if (map->n <= 1)
+ return map_simple_hull_trivial(map);
+ map = isl_map_drop_constraints_involving_unknown_divs(map);
+ hull = isl_basic_map_copy(map->p[0]);
+ for (i = 1; i < map->n; ++i) {
+ isl_basic_map *bmap_i;
+
+ bmap_i = isl_basic_map_copy(map->p[i]);
+ hull = isl_basic_map_plain_unshifted_simple_hull(hull, bmap_i);
+ }
+
+ isl_map_free(map);
+ return hull;
+}
+
+/* Compute a superset of the convex hull of "set" that is described
+ * by only the constraints in the constituents of "set".
+ * In particular, the result is composed of constraints that appear
+ * in each of the basic sets of "set"
+ */
+__isl_give isl_basic_set *isl_set_plain_unshifted_simple_hull(
+ __isl_take isl_set *set)
+{
+ return isl_map_plain_unshifted_simple_hull(set);
+}
+
+/* Check if "ineq" is a bound on "set" and, if so, add it to "hull".
+ *
+ * For each basic set in "set", we first check if the basic set
+ * contains a translate of "ineq". If this translate is more relaxed,
+ * then we assume that "ineq" is not a bound on this basic set.
+ * Otherwise, we know that it is a bound.
+ * If the basic set does not contain a translate of "ineq", then
+ * we call is_bound to perform the test.
+ */
+static __isl_give isl_basic_set *add_bound_from_constraint(
+ __isl_take isl_basic_set *hull, struct sh_data *data,
+ __isl_keep isl_set *set, isl_int *ineq)
+{
+ int i, k;
+ isl_ctx *ctx;
+ uint32_t c_hash;
+ struct ineq_cmp_data v;
+ isl_size total;
+
+ total = isl_basic_set_dim(hull, isl_dim_all);
+ if (total < 0 || !set)
+ return isl_basic_set_free(hull);
+
+ v.len = total;
+ v.p = ineq;
+ c_hash = isl_seq_get_hash(ineq + 1, v.len);
+
+ ctx = isl_basic_set_get_ctx(hull);
+ for (i = 0; i < set->n; ++i) {
+ int bound;
+ struct isl_hash_table_entry *entry;
+
+ entry = isl_hash_table_find(ctx, data->p[i].table,
+ c_hash, &has_ineq, &v, 0);
+ if (!entry)
+ return isl_basic_set_free(hull);
+ if (entry != isl_hash_table_entry_none) {
+ isl_int *ineq_i = entry->data;
+ int neg, more_relaxed;
+
+ neg = isl_seq_is_neg(ineq_i + 1, ineq + 1, v.len);
+ if (neg)
+ isl_int_neg(ineq_i[0], ineq_i[0]);
+ more_relaxed = isl_int_gt(ineq_i[0], ineq[0]);
+ if (neg)
+ isl_int_neg(ineq_i[0], ineq_i[0]);
+ if (more_relaxed)
+ break;
+ else
+ continue;
+ }
+ bound = is_bound(data, set, i, ineq, 0);
+ if (bound < 0)
+ return isl_basic_set_free(hull);
+ if (!bound)
+ break;
+ }
+ if (i < set->n)
+ return hull;
+
+ k = isl_basic_set_alloc_inequality(hull);
+ if (k < 0)
+ return isl_basic_set_free(hull);
+ isl_seq_cpy(hull->ineq[k], ineq, 1 + v.len);
+
+ return hull;
+}
+
+/* Compute a superset of the convex hull of "set" that is described
+ * by only some of the "n_ineq" constraints in the list "ineq", where "set"
+ * has no parameters or integer divisions.
+ *
+ * The inequalities in "ineq" are assumed to have been sorted such
+ * that constraints with the same linear part appear together and
+ * that among constraints with the same linear part, those with
+ * smaller constant term appear first.
+ *
+ * We reuse the same data structure that is used by uset_simple_hull,
+ * but we do not need the hull table since we will not consider the
+ * same constraint more than once. We therefore allocate it with zero size.
+ *
+ * We run through the constraints and try to add them one by one,
+ * skipping identical constraints. If we have added a constraint and
+ * the next constraint is a more relaxed translate, then we skip this
+ * next constraint as well.
+ */
+static __isl_give isl_basic_set *uset_unshifted_simple_hull_from_constraints(
+ __isl_take isl_set *set, int n_ineq, isl_int **ineq)
+{
+ int i;
+ int last_added = 0;
+ struct sh_data *data = NULL;
+ isl_basic_set *hull = NULL;
+ isl_size dim;
+
+ hull = isl_basic_set_alloc_space(isl_set_get_space(set), 0, 0, n_ineq);
+ if (!hull)
+ goto error;
+
+ data = sh_data_alloc(set, 0);
+ if (!data)
+ goto error;
+
+ dim = isl_set_dim(set, isl_dim_set);
+ if (dim < 0)
+ goto error;
+ for (i = 0; i < n_ineq; ++i) {
+ int hull_n_ineq = hull->n_ineq;
+ int parallel;
+
+ parallel = i > 0 && isl_seq_eq(ineq[i - 1] + 1, ineq[i] + 1,
+ dim);
+ if (parallel &&
+ (last_added || isl_int_eq(ineq[i - 1][0], ineq[i][0])))
+ continue;
+ hull = add_bound_from_constraint(hull, data, set, ineq[i]);
+ if (!hull)
+ goto error;
+ last_added = hull->n_ineq > hull_n_ineq;
+ }
+
+ sh_data_free(data);
+ isl_set_free(set);
+ return hull;
+error:
+ sh_data_free(data);
+ isl_set_free(set);
+ isl_basic_set_free(hull);
+ return NULL;
+}
+
+/* Collect pointers to all the inequalities in the elements of "list"
+ * in "ineq". For equalities, store both a pointer to the equality and
+ * a pointer to its opposite, which is first copied to "mat".
+ * "ineq" and "mat" are assumed to have been preallocated to the right size
+ * (the number of inequalities + 2 times the number of equalites and
+ * the number of equalities, respectively).
+ */
+static __isl_give isl_mat *collect_inequalities(__isl_take isl_mat *mat,
+ __isl_keep isl_basic_set_list *list, isl_int **ineq)
+{
+ int i, j, n_eq, n_ineq;
+ isl_size n;
+
+ n = isl_basic_set_list_n_basic_set(list);
+ if (!mat || n < 0)
+ return isl_mat_free(mat);
+
+ n_eq = 0;
+ n_ineq = 0;
+ for (i = 0; i < n; ++i) {
+ isl_basic_set *bset;
+ bset = isl_basic_set_list_get_basic_set(list, i);
+ if (!bset)
+ return isl_mat_free(mat);
+ for (j = 0; j < bset->n_eq; ++j) {
+ ineq[n_ineq++] = mat->row[n_eq];
+ ineq[n_ineq++] = bset->eq[j];
+ isl_seq_neg(mat->row[n_eq++], bset->eq[j], mat->n_col);
+ }
+ for (j = 0; j < bset->n_ineq; ++j)
+ ineq[n_ineq++] = bset->ineq[j];
+ isl_basic_set_free(bset);
+ }
+
+ return mat;
+}
+
+/* Comparison routine for use as an isl_sort callback.
+ *
+ * Constraints with the same linear part are sorted together and
+ * among constraints with the same linear part, those with smaller
+ * constant term are sorted first.
+ */
+static int cmp_ineq(const void *a, const void *b, void *arg)
+{
+ unsigned dim = *(unsigned *) arg;
+ isl_int * const *ineq1 = a;
+ isl_int * const *ineq2 = b;
+ int cmp;
+
+ cmp = isl_seq_cmp((*ineq1) + 1, (*ineq2) + 1, dim);
+ if (cmp != 0)
+ return cmp;
+ return isl_int_cmp((*ineq1)[0], (*ineq2)[0]);
+}
+
+/* Compute a superset of the convex hull of "set" that is described
+ * by only constraints in the elements of "list", where "set" has
+ * no parameters or integer divisions.
+ *
+ * We collect all the constraints in those elements and then
+ * sort the constraints such that constraints with the same linear part
+ * are sorted together and that those with smaller constant term are
+ * sorted first.
+ */
+static __isl_give isl_basic_set *uset_unshifted_simple_hull_from_basic_set_list(
+ __isl_take isl_set *set, __isl_take isl_basic_set_list *list)
+{
+ int i, n_eq, n_ineq;
+ isl_size n;
+ isl_size dim;
+ isl_ctx *ctx;
+ isl_mat *mat = NULL;
+ isl_int **ineq = NULL;
+ isl_basic_set *hull;
+
+ n = isl_basic_set_list_n_basic_set(list);
+ if (!set || n < 0)
+ goto error;
+ ctx = isl_set_get_ctx(set);
+
+ n_eq = 0;
+ n_ineq = 0;
+ for (i = 0; i < n; ++i) {
+ isl_basic_set *bset;
+ bset = isl_basic_set_list_get_basic_set(list, i);
+ if (!bset)
+ goto error;
+ n_eq += bset->n_eq;
+ n_ineq += 2 * bset->n_eq + bset->n_ineq;
+ isl_basic_set_free(bset);
+ }
+
+ ineq = isl_alloc_array(ctx, isl_int *, n_ineq);
+ if (n_ineq > 0 && !ineq)
+ goto error;
+
+ dim = isl_set_dim(set, isl_dim_set);
+ if (dim < 0)
+ goto error;
+ mat = isl_mat_alloc(ctx, n_eq, 1 + dim);
+ mat = collect_inequalities(mat, list, ineq);
+ if (!mat)
+ goto error;
+
+ if (isl_sort(ineq, n_ineq, sizeof(ineq[0]), &cmp_ineq, &dim) < 0)
+ goto error;
+
+ hull = uset_unshifted_simple_hull_from_constraints(set, n_ineq, ineq);
+
+ isl_mat_free(mat);
+ free(ineq);
+ isl_basic_set_list_free(list);
+ return hull;
+error:
+ isl_mat_free(mat);
+ free(ineq);
+ isl_set_free(set);
+ isl_basic_set_list_free(list);
+ return NULL;
+}
+
+/* Compute a superset of the convex hull of "map" that is described
+ * by only constraints in the elements of "list".
+ *
+ * If the list is empty, then we can only describe the universe set.
+ * If the input map is empty, then all constraints are valid, so
+ * we return the intersection of the elements in "list".
+ *
+ * Otherwise, we align all divs and temporarily treat them
+ * as regular variables, computing the unshifted simple hull in
+ * uset_unshifted_simple_hull_from_basic_set_list.
+ */
+static __isl_give isl_basic_map *map_unshifted_simple_hull_from_basic_map_list(
+ __isl_take isl_map *map, __isl_take isl_basic_map_list *list)
+{
+ isl_size n;
+ isl_basic_map *model;
+ isl_basic_map *hull;
+ isl_set *set;
+ isl_basic_set_list *bset_list;
+
+ n = isl_basic_map_list_n_basic_map(list);
+ if (!map || n < 0)
+ goto error;
+
+ if (n == 0) {
+ isl_space *space;
+
+ space = isl_map_get_space(map);
+ isl_map_free(map);
+ isl_basic_map_list_free(list);
+ return isl_basic_map_universe(space);
+ }
+ if (isl_map_plain_is_empty(map)) {
+ isl_map_free(map);
+ return isl_basic_map_list_intersect(list);
+ }
+
+ map = isl_map_align_divs_to_basic_map_list(map, list);
+ if (!map)
+ goto error;
+ list = isl_basic_map_list_align_divs_to_basic_map(list, map->p[0]);
+
+ model = isl_basic_map_list_get_basic_map(list, 0);
+
+ set = isl_map_underlying_set(map);
+ bset_list = isl_basic_map_list_underlying_set(list);
+
+ hull = uset_unshifted_simple_hull_from_basic_set_list(set, bset_list);
+ hull = isl_basic_map_overlying_set(hull, model);
+
+ return hull;
+error:
+ isl_map_free(map);
+ isl_basic_map_list_free(list);
+ return NULL;
+}
+
+/* Return a sequence of the basic maps that make up the maps in "list".
+ */
+static __isl_give isl_basic_map_list *collect_basic_maps(
+ __isl_take isl_map_list *list)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_basic_map_list *bmap_list;
+
+ if (!list)
+ return NULL;
+ n = isl_map_list_n_map(list);
+ ctx = isl_map_list_get_ctx(list);
+ bmap_list = isl_basic_map_list_alloc(ctx, 0);
+ if (n < 0)
+ bmap_list = isl_basic_map_list_free(bmap_list);
+
+ for (i = 0; i < n; ++i) {
+ isl_map *map;
+ isl_basic_map_list *list_i;
+
+ map = isl_map_list_get_map(list, i);
+ map = isl_map_compute_divs(map);
+ list_i = isl_map_get_basic_map_list(map);
+ isl_map_free(map);
+ bmap_list = isl_basic_map_list_concat(bmap_list, list_i);
+ }
+
+ isl_map_list_free(list);
+ return bmap_list;
+}
+
+/* Compute a superset of the convex hull of "map" that is described
+ * by only constraints in the elements of "list".
+ *
+ * If "map" is the universe, then the convex hull (and therefore
+ * any superset of the convexhull) is the universe as well.
+ *
+ * Otherwise, we collect all the basic maps in the map list and
+ * continue with map_unshifted_simple_hull_from_basic_map_list.
+ */
+__isl_give isl_basic_map *isl_map_unshifted_simple_hull_from_map_list(
+ __isl_take isl_map *map, __isl_take isl_map_list *list)
+{
+ isl_basic_map_list *bmap_list;
+ int is_universe;
+
+ is_universe = isl_map_plain_is_universe(map);
+ if (is_universe < 0)
+ map = isl_map_free(map);
+ if (is_universe < 0 || is_universe) {
+ isl_map_list_free(list);
+ return isl_map_unshifted_simple_hull(map);
+ }
+
+ bmap_list = collect_basic_maps(list);
+ return map_unshifted_simple_hull_from_basic_map_list(map, bmap_list);
+}
+
+/* Compute a superset of the convex hull of "set" that is described
+ * by only constraints in the elements of "list".
+ */
+__isl_give isl_basic_set *isl_set_unshifted_simple_hull_from_set_list(
+ __isl_take isl_set *set, __isl_take isl_set_list *list)
+{
+ return isl_map_unshifted_simple_hull_from_map_list(set, list);
+}
+
+/* Given a set "set", return parametric bounds on the dimension "dim".
+ */
+static __isl_give isl_basic_set *set_bounds(__isl_keep isl_set *set, int dim)
+{
+ isl_size set_dim = isl_set_dim(set, isl_dim_set);
+ if (set_dim < 0)
+ return NULL;
+ set = isl_set_copy(set);
+ set = isl_set_eliminate_dims(set, dim + 1, set_dim - (dim + 1));
+ set = isl_set_eliminate_dims(set, 0, dim);
+ return isl_set_convex_hull(set);
+}
+
+/* Computes a "simple hull" and then check if each dimension in the
+ * resulting hull is bounded by a symbolic constant. If not, the
+ * hull is intersected with the corresponding bounds on the whole set.
+ */
+__isl_give isl_basic_set *isl_set_bounded_simple_hull(__isl_take isl_set *set)
+{
+ int i, j;
+ struct isl_basic_set *hull;
+ isl_size nparam, dim, total;
+ unsigned left;
+ int removed_divs = 0;
+
+ hull = isl_set_simple_hull(isl_set_copy(set));
+ nparam = isl_basic_set_dim(hull, isl_dim_param);
+ dim = isl_basic_set_dim(hull, isl_dim_set);
+ total = isl_basic_set_dim(hull, isl_dim_all);
+ if (nparam < 0 || dim < 0 || total < 0)
+ goto error;
+
+ for (i = 0; i < dim; ++i) {
+ int lower = 0, upper = 0;
+ struct isl_basic_set *bounds;
+
+ left = total - nparam - i - 1;
+ for (j = 0; j < hull->n_eq; ++j) {
+ if (isl_int_is_zero(hull->eq[j][1 + nparam + i]))
+ continue;
+ if (isl_seq_first_non_zero(hull->eq[j]+1+nparam+i+1,
+ left) == -1)
+ break;
+ }
+ if (j < hull->n_eq)
+ continue;
+
+ for (j = 0; j < hull->n_ineq; ++j) {
+ if (isl_int_is_zero(hull->ineq[j][1 + nparam + i]))
+ continue;
+ if (isl_seq_first_non_zero(hull->ineq[j]+1+nparam+i+1,
+ left) != -1 ||
+ isl_seq_first_non_zero(hull->ineq[j]+1+nparam,
+ i) != -1)
+ continue;
+ if (isl_int_is_pos(hull->ineq[j][1 + nparam + i]))
+ lower = 1;
+ else
+ upper = 1;
+ if (lower && upper)
+ break;
+ }
+
+ if (lower && upper)
+ continue;
+
+ if (!removed_divs) {
+ set = isl_set_remove_divs(set);
+ if (!set)
+ goto error;
+ removed_divs = 1;
+ }
+ bounds = set_bounds(set, i);
+ hull = isl_basic_set_intersect(hull, bounds);
+ if (!hull)
+ goto error;
+ }
+
+ isl_set_free(set);
+ return hull;
+error:
+ isl_set_free(set);
+ isl_basic_set_free(hull);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_copy_tuple_id_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_copy_tuple_id_templ.c
new file mode 100644
index 00000000000..5345ab3624a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_copy_tuple_id_templ.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Copy the identifier of tuple "src_type" in "src"
+ * to that of "dst_type" in "dst", if there is any such identifier.
+ */
+__isl_give TYPE *FN(TYPE,copy_tuple_id)(__isl_take TYPE *dst,
+ enum isl_dim_type dst_type, __isl_keep isl_space *src,
+ enum isl_dim_type src_type)
+{
+ isl_bool has_id;
+ isl_id *id;
+
+ has_id = isl_space_has_tuple_id(src, src_type);
+ if (has_id < 0)
+ return FN(TYPE,free)(dst);
+ if (!has_id)
+ return dst;
+
+ id = isl_space_get_tuple_id(src, src_type);
+ dst = FN(TYPE,set_tuple_id)(dst, dst_type, id);
+
+ return dst;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ctx.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ctx.c
new file mode 100644
index 00000000000..176fd6f56a1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ctx.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <isl_ctx_private.h>
+#include <isl/vec.h>
+#include <isl_options_private.h>
+
+#define __isl_calloc(type,size) ((type *)calloc(1, size))
+#define __isl_calloc_type(type) __isl_calloc(type,sizeof(type))
+
+/* Construct an isl_stat indicating whether "obj" is non-NULL.
+ *
+ * That is, return isl_stat_ok if "obj" is non_NULL and
+ * isl_stat_error otherwise.
+ */
+isl_stat isl_stat_non_null(void *obj)
+{
+ if (obj != NULL)
+ return isl_stat_ok;
+ return isl_stat_error;
+}
+
+/* Return the negation of "b", where the negation of isl_bool_error
+ * is isl_bool_error again.
+ */
+isl_bool isl_bool_not(isl_bool b)
+{
+ if (b < 0)
+ return isl_bool_error;
+ if (b == isl_bool_false)
+ return isl_bool_true;
+ return isl_bool_false;
+}
+
+/* Create an isl_bool from an integer.
+ *
+ * Return isl_bool_false if b is zero, otherwise return isl_bool_true.
+ * This function never returns isl_bool_error.
+ */
+isl_bool isl_bool_ok(int b)
+{
+ if (b)
+ return isl_bool_true;
+ return isl_bool_false;
+}
+
+/* Check that the result of an allocation ("p") is not NULL and
+ * complain if it is.
+ * The only exception is when allocation size ("size") is equal to zero.
+ */
+static void *check_non_null(isl_ctx *ctx, void *p, size_t size)
+{
+ if (p || size == 0)
+ return p;
+ isl_die(ctx, isl_error_alloc, "allocation failure", return NULL);
+}
+
+/* Prepare for performing the next "operation" in the context.
+ * Return 0 if we are allowed to perform this operation and
+ * return -1 if we should abort the computation.
+ *
+ * In particular, we should stop if the user has explicitly aborted
+ * the computation or if the maximal number of operations has been exceeded.
+ */
+int isl_ctx_next_operation(isl_ctx *ctx)
+{
+ if (!ctx)
+ return -1;
+ if (ctx->abort) {
+ isl_ctx_set_error(ctx, isl_error_abort);
+ return -1;
+ }
+ if (ctx->max_operations && ctx->operations >= ctx->max_operations)
+ isl_die(ctx, isl_error_quota,
+ "maximal number of operations exceeded", return -1);
+ ctx->operations++;
+ return 0;
+}
+
+/* Call malloc and complain if it fails.
+ * If ctx is NULL, then return NULL.
+ */
+void *isl_malloc_or_die(isl_ctx *ctx, size_t size)
+{
+ if (isl_ctx_next_operation(ctx) < 0)
+ return NULL;
+ return ctx ? check_non_null(ctx, malloc(size), size) : NULL;
+}
+
+/* Call calloc and complain if it fails.
+ * If ctx is NULL, then return NULL.
+ */
+void *isl_calloc_or_die(isl_ctx *ctx, size_t nmemb, size_t size)
+{
+ if (isl_ctx_next_operation(ctx) < 0)
+ return NULL;
+ return ctx ? check_non_null(ctx, calloc(nmemb, size), nmemb) : NULL;
+}
+
+/* Call realloc and complain if it fails.
+ * If ctx is NULL, then return NULL.
+ */
+void *isl_realloc_or_die(isl_ctx *ctx, void *ptr, size_t size)
+{
+ if (isl_ctx_next_operation(ctx) < 0)
+ return NULL;
+ return ctx ? check_non_null(ctx, realloc(ptr, size), size) : NULL;
+}
+
+/* Keep track of all information about the current error ("error", "msg",
+ * "file", "line") in "ctx".
+ */
+void isl_ctx_set_full_error(isl_ctx *ctx, enum isl_error error, const char *msg,
+ const char *file, int line)
+{
+ if (!ctx)
+ return;
+ ctx->error = error;
+ ctx->error_msg = msg;
+ ctx->error_file = file;
+ ctx->error_line = line;
+}
+
+void isl_handle_error(isl_ctx *ctx, enum isl_error error, const char *msg,
+ const char *file, int line)
+{
+ if (!ctx)
+ return;
+
+ isl_ctx_set_full_error(ctx, error, msg, file, line);
+
+ switch (ctx->opt->on_error) {
+ case ISL_ON_ERROR_WARN:
+ fprintf(stderr, "%s:%d: %s\n", file, line, msg);
+ return;
+ case ISL_ON_ERROR_CONTINUE:
+ return;
+ case ISL_ON_ERROR_ABORT:
+ fprintf(stderr, "%s:%d: %s\n", file, line, msg);
+ abort();
+ return;
+ }
+}
+
+static struct isl_options *find_nested_options(struct isl_args *args,
+ void *opt, struct isl_args *wanted)
+{
+ int i;
+ struct isl_options *options;
+
+ if (args == wanted)
+ return opt;
+
+ for (i = 0; args->args[i].type != isl_arg_end; ++i) {
+ struct isl_arg *arg = &args->args[i];
+ void *child;
+
+ if (arg->type != isl_arg_child)
+ continue;
+
+ if (arg->offset == ISL_ARG_OFFSET_NONE)
+ child = opt;
+ else
+ child = *(void **)(((char *)opt) + arg->offset);
+
+ options = find_nested_options(arg->u.child.child,
+ child, wanted);
+ if (options)
+ return options;
+ }
+
+ return NULL;
+}
+
+static struct isl_options *find_nested_isl_options(struct isl_args *args,
+ void *opt)
+{
+ return find_nested_options(args, opt, &isl_options_args);
+}
+
+void *isl_ctx_peek_options(isl_ctx *ctx, struct isl_args *args)
+{
+ if (!ctx)
+ return NULL;
+ if (args == &isl_options_args)
+ return ctx->opt;
+ return find_nested_options(ctx->user_args, ctx->user_opt, args);
+}
+
+isl_ctx *isl_ctx_alloc_with_options(struct isl_args *args, void *user_opt)
+{
+ struct isl_ctx *ctx = NULL;
+ struct isl_options *opt = NULL;
+ int opt_allocated = 0;
+
+ if (!user_opt)
+ return NULL;
+
+ opt = find_nested_isl_options(args, user_opt);
+ if (!opt) {
+ opt = isl_options_new_with_defaults();
+ if (!opt)
+ goto error;
+ opt_allocated = 1;
+ }
+
+ ctx = __isl_calloc_type(struct isl_ctx);
+ if (!ctx)
+ goto error;
+
+ if (isl_hash_table_init(ctx, &ctx->id_table, 0))
+ goto error;
+
+ ctx->stats = isl_calloc_type(ctx, struct isl_stats);
+ if (!ctx->stats)
+ goto error;
+
+ ctx->user_args = args;
+ ctx->user_opt = user_opt;
+ ctx->opt_allocated = opt_allocated;
+ ctx->opt = opt;
+ ctx->ref = 0;
+
+ isl_int_init(ctx->zero);
+ isl_int_set_si(ctx->zero, 0);
+
+ isl_int_init(ctx->one);
+ isl_int_set_si(ctx->one, 1);
+
+ isl_int_init(ctx->two);
+ isl_int_set_si(ctx->two, 2);
+
+ isl_int_init(ctx->negone);
+ isl_int_set_si(ctx->negone, -1);
+
+ isl_int_init(ctx->normalize_gcd);
+
+ ctx->n_cached = 0;
+ ctx->n_miss = 0;
+
+ isl_ctx_reset_error(ctx);
+
+ ctx->operations = 0;
+ isl_ctx_set_max_operations(ctx, ctx->opt->max_operations);
+
+ return ctx;
+error:
+ isl_args_free(args, user_opt);
+ if (opt_allocated)
+ isl_options_free(opt);
+ free(ctx);
+ return NULL;
+}
+
+struct isl_ctx *isl_ctx_alloc()
+{
+ struct isl_options *opt;
+
+ opt = isl_options_new_with_defaults();
+
+ return isl_ctx_alloc_with_options(&isl_options_args, opt);
+}
+
+void isl_ctx_ref(struct isl_ctx *ctx)
+{
+ ctx->ref++;
+}
+
+void isl_ctx_deref(struct isl_ctx *ctx)
+{
+ isl_assert(ctx, ctx->ref > 0, return);
+ ctx->ref--;
+}
+
+/* Print statistics on usage.
+ */
+static void print_stats(isl_ctx *ctx)
+{
+ fprintf(stderr, "operations: %lu\n", ctx->operations);
+}
+
+void isl_ctx_free(struct isl_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ if (ctx->ref != 0)
+ isl_die(ctx, isl_error_invalid,
+ "isl_ctx freed, but some objects still reference it",
+ return);
+
+ if (ctx->opt->print_stats)
+ print_stats(ctx);
+
+ isl_hash_table_clear(&ctx->id_table);
+ isl_blk_clear_cache(ctx);
+ isl_int_clear(ctx->zero);
+ isl_int_clear(ctx->one);
+ isl_int_clear(ctx->two);
+ isl_int_clear(ctx->negone);
+ isl_int_clear(ctx->normalize_gcd);
+ isl_args_free(ctx->user_args, ctx->user_opt);
+ if (ctx->opt_allocated)
+ isl_options_free(ctx->opt);
+ free(ctx->stats);
+ free(ctx);
+}
+
+struct isl_options *isl_ctx_options(isl_ctx *ctx)
+{
+ if (!ctx)
+ return NULL;
+ return ctx->opt;
+}
+
+enum isl_error isl_ctx_last_error(isl_ctx *ctx)
+{
+ return ctx ? ctx->error : isl_error_invalid;
+}
+
+/* Return the error message of the last error in "ctx".
+ */
+const char *isl_ctx_last_error_msg(isl_ctx *ctx)
+{
+ return ctx ? ctx->error_msg : NULL;
+}
+
+/* Return the file name where the last error in "ctx" occurred.
+ */
+const char *isl_ctx_last_error_file(isl_ctx *ctx)
+{
+ return ctx ? ctx->error_file : NULL;
+}
+
+/* Return the line number where the last error in "ctx" occurred.
+ */
+int isl_ctx_last_error_line(isl_ctx *ctx)
+{
+ return ctx ? ctx->error_line : -1;
+}
+
+void isl_ctx_reset_error(isl_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ ctx->error = isl_error_none;
+ ctx->error_msg = NULL;
+ ctx->error_file = NULL;
+ ctx->error_line = -1;
+}
+
+void isl_ctx_set_error(isl_ctx *ctx, enum isl_error error)
+{
+ isl_ctx_set_full_error(ctx, error, NULL, NULL, -1);
+}
+
+void isl_ctx_abort(isl_ctx *ctx)
+{
+ if (ctx)
+ ctx->abort = 1;
+}
+
+void isl_ctx_resume(isl_ctx *ctx)
+{
+ if (ctx)
+ ctx->abort = 0;
+}
+
+int isl_ctx_aborted(isl_ctx *ctx)
+{
+ return ctx ? ctx->abort : -1;
+}
+
+int isl_ctx_parse_options(isl_ctx *ctx, int argc, char **argv, unsigned flags)
+{
+ if (!ctx)
+ return -1;
+ return isl_args_parse(ctx->user_args, argc, argv, ctx->user_opt, flags);
+}
+
+/* Set the maximal number of iterations of "ctx" to "max_operations".
+ */
+void isl_ctx_set_max_operations(isl_ctx *ctx, unsigned long max_operations)
+{
+ if (!ctx)
+ return;
+ ctx->max_operations = max_operations;
+}
+
+/* Return the maximal number of iterations of "ctx".
+ */
+unsigned long isl_ctx_get_max_operations(isl_ctx *ctx)
+{
+ return ctx ? ctx->max_operations : 0;
+}
+
+/* Reset the number of operations performed by "ctx".
+ */
+void isl_ctx_reset_operations(isl_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ ctx->operations = 0;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ctx_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ctx_private.h
new file mode 100644
index 00000000000..5083a965e92
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ctx_private.h
@@ -0,0 +1,47 @@
+#include <isl/ctx.h>
+#include <isl_blk.h>
+
+/* "error" stores the last error that has occurred.
+ * It is reset to isl_error_none by isl_ctx_reset_error.
+ * "error_msg" stores the error message of the last error,
+ * while "error_file" and "error_line" specify where the last error occurred.
+ * "error_msg" and "error_file" always point to statically allocated
+ * strings (if not NULL).
+ */
+struct isl_ctx {
+ int ref;
+
+ struct isl_stats *stats;
+
+ int opt_allocated;
+ struct isl_options *opt;
+ void *user_opt;
+ struct isl_args *user_args;
+
+ isl_int zero;
+ isl_int one;
+ isl_int two;
+ isl_int negone;
+
+ isl_int normalize_gcd;
+
+ int n_cached;
+ int n_miss;
+ struct isl_blk cache[ISL_BLK_CACHE_SIZE];
+ struct isl_hash_table id_table;
+
+ enum isl_error error;
+ const char *error_msg;
+ const char *error_file;
+ int error_line;
+
+ int abort;
+
+ unsigned long operations;
+ unsigned long max_operations;
+};
+
+int isl_ctx_next_operation(isl_ctx *ctx);
+
+void isl_ctx_set_full_error(isl_ctx *ctx, enum isl_error error, const char *msg,
+ const char *file, int line);
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_deprecated.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_deprecated.c
new file mode 100644
index 00000000000..f65be1f8d77
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_deprecated.c
@@ -0,0 +1,15 @@
+#include <isl/constraint.h>
+
+/* This function was replaced by isl_constraint_alloc_equality.
+ */
+__isl_give isl_constraint *isl_equality_alloc(__isl_take isl_local_space *ls)
+{
+ return isl_constraint_alloc_equality(ls);
+}
+
+/* This function was replaced by isl_constraint_alloc_inequality.
+ */
+__isl_give isl_constraint *isl_inequality_alloc(__isl_take isl_local_space *ls)
+{
+ return isl_constraint_alloc_inequality(ls);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_dim_map.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_dim_map.c
new file mode 100644
index 00000000000..b0b067d80e6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_dim_map.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010-2011 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ */
+
+#include <isl_map_private.h>
+#include <isl_space_private.h>
+#include <isl_dim_map.h>
+#include <isl_reordering.h>
+
+struct isl_dim_map_entry {
+ int pos;
+ int sgn;
+};
+
+/* Maps dst positions to src positions */
+struct isl_dim_map {
+ unsigned len;
+ struct isl_dim_map_entry m[1];
+};
+
+__isl_give isl_dim_map *isl_dim_map_alloc(isl_ctx *ctx, unsigned len)
+{
+ int i;
+ struct isl_dim_map *dim_map;
+ dim_map = isl_alloc(ctx, struct isl_dim_map,
+ sizeof(struct isl_dim_map) + len * sizeof(struct isl_dim_map_entry));
+ if (!dim_map)
+ return NULL;
+ dim_map->len = 1 + len;
+ dim_map->m[0].pos = 0;
+ dim_map->m[0].sgn = 1;
+ for (i = 0; i < len; ++i)
+ dim_map->m[1 + i].sgn = 0;
+ return dim_map;
+}
+
+/* Free "dim_map" and return NULL.
+ */
+__isl_null isl_dim_map *isl_dim_map_free(__isl_take isl_dim_map *dim_map)
+{
+ free(dim_map);
+ return NULL;
+}
+
+void isl_dim_map_range(__isl_keep isl_dim_map *dim_map,
+ unsigned dst_pos, int dst_stride, unsigned src_pos, int src_stride,
+ unsigned n, int sign)
+{
+ int i;
+
+ if (!dim_map)
+ return;
+
+ for (i = 0; i < n; ++i) {
+ unsigned d = 1 + dst_pos + dst_stride * i;
+ unsigned s = 1 + src_pos + src_stride * i;
+ dim_map->m[d].pos = s;
+ dim_map->m[d].sgn = sign;
+ }
+}
+
+void isl_dim_map_dim_range(__isl_keep isl_dim_map *dim_map,
+ __isl_keep isl_space *space, enum isl_dim_type type,
+ unsigned first, unsigned n, unsigned dst_pos)
+{
+ int i;
+ unsigned src_pos;
+
+ if (!dim_map || !space)
+ return;
+
+ src_pos = 1 + isl_space_offset(space, type);
+ for (i = 0; i < n; ++i) {
+ dim_map->m[1 + dst_pos + i].pos = src_pos + first + i;
+ dim_map->m[1 + dst_pos + i].sgn = 1;
+ }
+}
+
+void isl_dim_map_dim(__isl_keep isl_dim_map *dim_map,
+ __isl_keep isl_space *space, enum isl_dim_type type, unsigned dst_pos)
+{
+ isl_size dim = isl_space_dim(space, type);
+
+ if (dim < 0)
+ return;
+ isl_dim_map_dim_range(dim_map, space, type, 0, dim, dst_pos);
+}
+
+void isl_dim_map_div(__isl_keep isl_dim_map *dim_map,
+ __isl_keep isl_basic_map *bmap, unsigned dst_pos)
+{
+ int i;
+ unsigned src_pos;
+
+ if (!dim_map || !bmap)
+ return;
+
+ src_pos = isl_basic_map_offset(bmap, isl_dim_div);
+ for (i = 0; i < bmap->n_div; ++i) {
+ dim_map->m[1 + dst_pos + i].pos = src_pos + i;
+ dim_map->m[1 + dst_pos + i].sgn = 1;
+ }
+}
+
+void isl_dim_map_dump(struct isl_dim_map *dim_map)
+{
+ int i;
+
+ for (i = 0; i < dim_map->len; ++i)
+ fprintf(stderr, "%d -> %d * %d; ", i,
+ dim_map->m[i].sgn, dim_map->m[i].pos);
+ fprintf(stderr, "\n");
+}
+
+static void copy_constraint_dim_map(isl_int *dst, isl_int *src,
+ struct isl_dim_map *dim_map)
+{
+ int i;
+
+ for (i = 0; i < dim_map->len; ++i) {
+ if (dim_map->m[i].sgn == 0)
+ isl_int_set_si(dst[i], 0);
+ else if (dim_map->m[i].sgn > 0)
+ isl_int_set(dst[i], src[dim_map->m[i].pos]);
+ else
+ isl_int_neg(dst[i], src[dim_map->m[i].pos]);
+ }
+}
+
+static void copy_div_dim_map(isl_int *dst, isl_int *src,
+ struct isl_dim_map *dim_map)
+{
+ isl_int_set(dst[0], src[0]);
+ copy_constraint_dim_map(dst+1, src+1, dim_map);
+}
+
+__isl_give isl_basic_map *isl_basic_map_add_constraints_dim_map(
+ __isl_take isl_basic_map *dst, __isl_take isl_basic_map *src,
+ __isl_take isl_dim_map *dim_map)
+{
+ int i;
+
+ if (!src || !dst || !dim_map)
+ goto error;
+
+ for (i = 0; i < src->n_eq; ++i) {
+ int i1 = isl_basic_map_alloc_equality(dst);
+ if (i1 < 0)
+ goto error;
+ copy_constraint_dim_map(dst->eq[i1], src->eq[i], dim_map);
+ }
+
+ for (i = 0; i < src->n_ineq; ++i) {
+ int i1 = isl_basic_map_alloc_inequality(dst);
+ if (i1 < 0)
+ goto error;
+ copy_constraint_dim_map(dst->ineq[i1], src->ineq[i], dim_map);
+ }
+
+ for (i = 0; i < src->n_div; ++i) {
+ int i1 = isl_basic_map_alloc_div(dst);
+ if (i1 < 0)
+ goto error;
+ copy_div_dim_map(dst->div[i1], src->div[i], dim_map);
+ }
+
+ isl_dim_map_free(dim_map);
+ isl_basic_map_free(src);
+
+ return dst;
+error:
+ isl_dim_map_free(dim_map);
+ isl_basic_map_free(src);
+ isl_basic_map_free(dst);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_add_constraints_dim_map(
+ __isl_take isl_basic_set *dst, __isl_take isl_basic_set *src,
+ __isl_take isl_dim_map *dim_map)
+{
+ return isl_basic_map_add_constraints_dim_map(dst, src, dim_map);
+}
+
+/* Extend the given dim_map with mappings for the divs in bmap.
+ */
+__isl_give isl_dim_map *isl_dim_map_extend(__isl_keep isl_dim_map *dim_map,
+ __isl_keep isl_basic_map *bmap)
+{
+ int i;
+ struct isl_dim_map *res;
+ int offset;
+
+ if (!dim_map)
+ return NULL;
+
+ offset = isl_basic_map_offset(bmap, isl_dim_div);
+
+ res = isl_dim_map_alloc(bmap->ctx, dim_map->len - 1 + bmap->n_div);
+ if (!res)
+ return NULL;
+
+ for (i = 0; i < dim_map->len; ++i)
+ res->m[i] = dim_map->m[i];
+ for (i = 0; i < bmap->n_div; ++i) {
+ res->m[dim_map->len + i].pos = offset + i;
+ res->m[dim_map->len + i].sgn = 1;
+ }
+
+ return res;
+}
+
+/* Extract a dim_map from a reordering.
+ * We essentially need to reverse the mapping, and add an offset
+ * of 1 for the constant term.
+ */
+__isl_give isl_dim_map *isl_dim_map_from_reordering(
+ __isl_keep isl_reordering *exp)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_size dim;
+ isl_space *space;
+ struct isl_dim_map *dim_map;
+
+ if (!exp)
+ return NULL;
+
+ ctx = isl_reordering_get_ctx(exp);
+ space = isl_reordering_peek_space(exp);
+ dim = isl_space_dim(space, isl_dim_all);
+ if (dim < 0)
+ return NULL;
+ dim_map = isl_dim_map_alloc(ctx, dim);
+ if (!dim_map)
+ return NULL;
+
+ for (i = 0; i < exp->len; ++i) {
+ dim_map->m[1 + exp->pos[i]].pos = 1 + i;
+ dim_map->m[1 + exp->pos[i]].sgn = 1;
+ }
+
+ return dim_map;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_dim_map.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_dim_map.h
new file mode 100644
index 00000000000..5e45fe00b2b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_dim_map.h
@@ -0,0 +1,37 @@
+#ifndef ISL_DIM_MAP_H
+#define ISL_DIM_MAP_H
+
+#include <isl/ctx.h>
+#include <isl/space.h>
+#include <isl/map.h>
+#include <isl_reordering.h>
+
+struct isl_dim_map;
+typedef struct isl_dim_map isl_dim_map;
+
+__isl_give isl_dim_map *isl_dim_map_alloc(isl_ctx *ctx, unsigned len);
+__isl_null isl_dim_map *isl_dim_map_free(__isl_take isl_dim_map *dim_map);
+void isl_dim_map_range(__isl_keep isl_dim_map *dim_map,
+ unsigned dst_pos, int dst_stride, unsigned src_pos, int src_stride,
+ unsigned n, int sign);
+void isl_dim_map_dim_range(__isl_keep isl_dim_map *dim_map,
+ __isl_keep isl_space *space, enum isl_dim_type type,
+ unsigned first, unsigned n, unsigned dst_pos);
+void isl_dim_map_dim(__isl_keep isl_dim_map *dim_map,
+ __isl_keep isl_space *space, enum isl_dim_type type, unsigned dst_pos);
+void isl_dim_map_div(__isl_keep isl_dim_map *dim_map,
+ __isl_keep isl_basic_map *bmap, unsigned dst_pos);
+__isl_give isl_basic_set *isl_basic_set_add_constraints_dim_map(
+ __isl_take isl_basic_set *dst, __isl_take isl_basic_set *src,
+ __isl_take isl_dim_map *dim_map);
+__isl_give isl_basic_map *isl_basic_map_add_constraints_dim_map(
+ __isl_take isl_basic_map *dst, __isl_take isl_basic_map *src,
+ __isl_take isl_dim_map *dim_map);
+
+__isl_give isl_dim_map *isl_dim_map_extend(__isl_keep isl_dim_map *dim_map,
+ __isl_keep isl_basic_map *bmap);
+
+__isl_give isl_dim_map *isl_dim_map_from_reordering(
+ __isl_keep isl_reordering *exp);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_domain_factor_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_domain_factor_templ.c
new file mode 100644
index 00000000000..c64e9a262f0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_domain_factor_templ.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012 Ecole Normale Superieure
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Drop the "n" domain dimensions starting at "first" from "obj",
+ * after checking that they do not appear in the affine expression.
+ */
+static __isl_give TYPE *FN(TYPE,drop_domain)(__isl_take TYPE *obj,
+ unsigned first, unsigned n)
+{
+ isl_bool involves;
+
+ involves = FN(TYPE,involves_dims)(obj, isl_dim_in, first, n);
+ if (involves < 0)
+ return FN(TYPE,free)(obj);
+ if (involves)
+ isl_die(FN(TYPE,get_ctx)(obj), isl_error_invalid,
+ "affine expression involves some of the domain dimensions",
+ return FN(TYPE,free)(obj));
+ return FN(TYPE,drop_dims)(obj, isl_dim_in, first, n);
+}
+
+/* Check that the domain of "obj" is a product.
+ */
+static isl_stat FN(TYPE,check_domain_product)(__isl_keep TYPE *obj)
+{
+ isl_bool is_product;
+
+ is_product = FN(TYPE,domain_is_product)(obj);
+ if (is_product < 0)
+ return isl_stat_error;
+ if (!is_product)
+ isl_die(FN(TYPE,get_ctx)(obj), isl_error_invalid,
+ "domain is not a product", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Given an affine function with a domain of the form [A -> B] that
+ * does not depend on B, return the same function on domain A.
+ */
+__isl_give TYPE *FN(TYPE,domain_factor_domain)(__isl_take TYPE *obj)
+{
+ isl_space *space;
+ isl_size n, n_in;
+
+ if (FN(TYPE,check_domain_product)(obj) < 0)
+ return FN(TYPE,free)(obj);
+ space = FN(TYPE,get_domain_space)(obj);
+ n = isl_space_dim(space, isl_dim_set);
+ space = isl_space_factor_domain(space);
+ n_in = isl_space_dim(space, isl_dim_set);
+ if (n < 0 || n_in < 0)
+ obj = FN(TYPE,free)(obj);
+ else
+ obj = FN(TYPE,drop_domain)(obj, n_in, n - n_in);
+ obj = FN(TYPE,reset_domain_space)(obj, space);
+ return obj;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_equalities.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_equalities.c
new file mode 100644
index 00000000000..90dfb7709ce
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_equalities.c
@@ -0,0 +1,897 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ */
+
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+#include <isl_seq.h>
+#include "isl_map_private.h"
+#include "isl_equalities.h"
+#include <isl_val_private.h>
+
+/* Given a set of modulo constraints
+ *
+ * c + A y = 0 mod d
+ *
+ * this function computes a particular solution y_0
+ *
+ * The input is given as a matrix B = [ c A ] and a vector d.
+ *
+ * The output is matrix containing the solution y_0 or
+ * a zero-column matrix if the constraints admit no integer solution.
+ *
+ * The given set of constrains is equivalent to
+ *
+ * c + A y = -D x
+ *
+ * with D = diag d and x a fresh set of variables.
+ * Reducing both c and A modulo d does not change the
+ * value of y in the solution and may lead to smaller coefficients.
+ * Let M = [ D A ] and [ H 0 ] = M U, the Hermite normal form of M.
+ * Then
+ * [ x ]
+ * M [ y ] = - c
+ * and so
+ * [ x ]
+ * [ H 0 ] U^{-1} [ y ] = - c
+ * Let
+ * [ A ] [ x ]
+ * [ B ] = U^{-1} [ y ]
+ * then
+ * H A + 0 B = -c
+ *
+ * so B may be chosen arbitrarily, e.g., B = 0, and then
+ *
+ * [ x ] = [ -c ]
+ * U^{-1} [ y ] = [ 0 ]
+ * or
+ * [ x ] [ -c ]
+ * [ y ] = U [ 0 ]
+ * specifically,
+ *
+ * y = U_{2,1} (-c)
+ *
+ * If any of the coordinates of this y are non-integer
+ * then the constraints admit no integer solution and
+ * a zero-column matrix is returned.
+ */
+static __isl_give isl_mat *particular_solution(__isl_keep isl_mat *B,
+ __isl_keep isl_vec *d)
+{
+ int i, j;
+ struct isl_mat *M = NULL;
+ struct isl_mat *C = NULL;
+ struct isl_mat *U = NULL;
+ struct isl_mat *H = NULL;
+ struct isl_mat *cst = NULL;
+ struct isl_mat *T = NULL;
+
+ M = isl_mat_alloc(B->ctx, B->n_row, B->n_row + B->n_col - 1);
+ C = isl_mat_alloc(B->ctx, 1 + B->n_row, 1);
+ if (!M || !C)
+ goto error;
+ isl_int_set_si(C->row[0][0], 1);
+ for (i = 0; i < B->n_row; ++i) {
+ isl_seq_clr(M->row[i], B->n_row);
+ isl_int_set(M->row[i][i], d->block.data[i]);
+ isl_int_neg(C->row[1 + i][0], B->row[i][0]);
+ isl_int_fdiv_r(C->row[1+i][0], C->row[1+i][0], M->row[i][i]);
+ for (j = 0; j < B->n_col - 1; ++j)
+ isl_int_fdiv_r(M->row[i][B->n_row + j],
+ B->row[i][1 + j], M->row[i][i]);
+ }
+ M = isl_mat_left_hermite(M, 0, &U, NULL);
+ if (!M || !U)
+ goto error;
+ H = isl_mat_sub_alloc(M, 0, B->n_row, 0, B->n_row);
+ H = isl_mat_lin_to_aff(H);
+ C = isl_mat_inverse_product(H, C);
+ if (!C)
+ goto error;
+ for (i = 0; i < B->n_row; ++i) {
+ if (!isl_int_is_divisible_by(C->row[1+i][0], C->row[0][0]))
+ break;
+ isl_int_divexact(C->row[1+i][0], C->row[1+i][0], C->row[0][0]);
+ }
+ if (i < B->n_row)
+ cst = isl_mat_alloc(B->ctx, B->n_row, 0);
+ else
+ cst = isl_mat_sub_alloc(C, 1, B->n_row, 0, 1);
+ T = isl_mat_sub_alloc(U, B->n_row, B->n_col - 1, 0, B->n_row);
+ cst = isl_mat_product(T, cst);
+ isl_mat_free(M);
+ isl_mat_free(C);
+ isl_mat_free(U);
+ return cst;
+error:
+ isl_mat_free(M);
+ isl_mat_free(C);
+ isl_mat_free(U);
+ return NULL;
+}
+
+/* Compute and return the matrix
+ *
+ * U_1^{-1} diag(d_1, 1, ..., 1)
+ *
+ * with U_1 the unimodular completion of the first (and only) row of B.
+ * The columns of this matrix generate the lattice that satisfies
+ * the single (linear) modulo constraint.
+ */
+static __isl_take isl_mat *parameter_compression_1(__isl_keep isl_mat *B,
+ __isl_keep isl_vec *d)
+{
+ struct isl_mat *U;
+
+ U = isl_mat_alloc(B->ctx, B->n_col - 1, B->n_col - 1);
+ if (!U)
+ return NULL;
+ isl_seq_cpy(U->row[0], B->row[0] + 1, B->n_col - 1);
+ U = isl_mat_unimodular_complete(U, 1);
+ U = isl_mat_right_inverse(U);
+ if (!U)
+ return NULL;
+ isl_mat_col_mul(U, 0, d->block.data[0], 0);
+ U = isl_mat_lin_to_aff(U);
+ return U;
+}
+
+/* Compute a common lattice of solutions to the linear modulo
+ * constraints specified by B and d.
+ * See also the documentation of isl_mat_parameter_compression.
+ * We put the matrix
+ *
+ * A = [ L_1^{-T} L_2^{-T} ... L_k^{-T} ]
+ *
+ * on a common denominator. This denominator D is the lcm of modulos d.
+ * Since L_i = U_i^{-1} diag(d_i, 1, ... 1), we have
+ * L_i^{-T} = U_i^T diag(d_i, 1, ... 1)^{-T} = U_i^T diag(1/d_i, 1, ..., 1).
+ * Putting this on the common denominator, we have
+ * D * L_i^{-T} = U_i^T diag(D/d_i, D, ..., D).
+ */
+static __isl_give isl_mat *parameter_compression_multi(__isl_keep isl_mat *B,
+ __isl_keep isl_vec *d)
+{
+ int i, j, k;
+ isl_int D;
+ struct isl_mat *A = NULL, *U = NULL;
+ struct isl_mat *T;
+ unsigned size;
+
+ isl_int_init(D);
+
+ isl_vec_lcm(d, &D);
+
+ size = B->n_col - 1;
+ A = isl_mat_alloc(B->ctx, size, B->n_row * size);
+ U = isl_mat_alloc(B->ctx, size, size);
+ if (!U || !A)
+ goto error;
+ for (i = 0; i < B->n_row; ++i) {
+ isl_seq_cpy(U->row[0], B->row[i] + 1, size);
+ U = isl_mat_unimodular_complete(U, 1);
+ if (!U)
+ goto error;
+ isl_int_divexact(D, D, d->block.data[i]);
+ for (k = 0; k < U->n_col; ++k)
+ isl_int_mul(A->row[k][i*size+0], D, U->row[0][k]);
+ isl_int_mul(D, D, d->block.data[i]);
+ for (j = 1; j < U->n_row; ++j)
+ for (k = 0; k < U->n_col; ++k)
+ isl_int_mul(A->row[k][i*size+j],
+ D, U->row[j][k]);
+ }
+ A = isl_mat_left_hermite(A, 0, NULL, NULL);
+ T = isl_mat_sub_alloc(A, 0, A->n_row, 0, A->n_row);
+ T = isl_mat_lin_to_aff(T);
+ if (!T)
+ goto error;
+ isl_int_set(T->row[0][0], D);
+ T = isl_mat_right_inverse(T);
+ if (!T)
+ goto error;
+ isl_assert(T->ctx, isl_int_is_one(T->row[0][0]), goto error);
+ T = isl_mat_transpose(T);
+ isl_mat_free(A);
+ isl_mat_free(U);
+
+ isl_int_clear(D);
+ return T;
+error:
+ isl_mat_free(A);
+ isl_mat_free(U);
+ isl_int_clear(D);
+ return NULL;
+}
+
+/* Given a set of modulo constraints
+ *
+ * c + A y = 0 mod d
+ *
+ * this function returns an affine transformation T,
+ *
+ * y = T y'
+ *
+ * that bijectively maps the integer vectors y' to integer
+ * vectors y that satisfy the modulo constraints.
+ *
+ * This function is inspired by Section 2.5.3
+ * of B. Meister, "Stating and Manipulating Periodicity in the Polytope
+ * Model. Applications to Program Analysis and Optimization".
+ * However, the implementation only follows the algorithm of that
+ * section for computing a particular solution and not for computing
+ * a general homogeneous solution. The latter is incomplete and
+ * may remove some valid solutions.
+ * Instead, we use an adaptation of the algorithm in Section 7 of
+ * B. Meister, S. Verdoolaege, "Polynomial Approximations in the Polytope
+ * Model: Bringing the Power of Quasi-Polynomials to the Masses".
+ *
+ * The input is given as a matrix B = [ c A ] and a vector d.
+ * Each element of the vector d corresponds to a row in B.
+ * The output is a lower triangular matrix.
+ * If no integer vector y satisfies the given constraints then
+ * a matrix with zero columns is returned.
+ *
+ * We first compute a particular solution y_0 to the given set of
+ * modulo constraints in particular_solution. If no such solution
+ * exists, then we return a zero-columned transformation matrix.
+ * Otherwise, we compute the generic solution to
+ *
+ * A y = 0 mod d
+ *
+ * That is we want to compute G such that
+ *
+ * y = G y''
+ *
+ * with y'' integer, describes the set of solutions.
+ *
+ * We first remove the common factors of each row.
+ * In particular if gcd(A_i,d_i) != 1, then we divide the whole
+ * row i (including d_i) by this common factor. If afterwards gcd(A_i) != 1,
+ * then we divide this row of A by the common factor, unless gcd(A_i) = 0.
+ * In the later case, we simply drop the row (in both A and d).
+ *
+ * If there are no rows left in A, then G is the identity matrix. Otherwise,
+ * for each row i, we now determine the lattice of integer vectors
+ * that satisfies this row. Let U_i be the unimodular extension of the
+ * row A_i. This unimodular extension exists because gcd(A_i) = 1.
+ * The first component of
+ *
+ * y' = U_i y
+ *
+ * needs to be a multiple of d_i. Let y' = diag(d_i, 1, ..., 1) y''.
+ * Then,
+ *
+ * y = U_i^{-1} diag(d_i, 1, ..., 1) y''
+ *
+ * for arbitrary integer vectors y''. That is, y belongs to the lattice
+ * generated by the columns of L_i = U_i^{-1} diag(d_i, 1, ..., 1).
+ * If there is only one row, then G = L_1.
+ *
+ * If there is more than one row left, we need to compute the intersection
+ * of the lattices. That is, we need to compute an L such that
+ *
+ * L = L_i L_i' for all i
+ *
+ * with L_i' some integer matrices. Let A be constructed as follows
+ *
+ * A = [ L_1^{-T} L_2^{-T} ... L_k^{-T} ]
+ *
+ * and computed the Hermite Normal Form of A = [ H 0 ] U
+ * Then,
+ *
+ * L_i^{-T} = H U_{1,i}
+ *
+ * or
+ *
+ * H^{-T} = L_i U_{1,i}^T
+ *
+ * In other words G = L = H^{-T}.
+ * To ensure that G is lower triangular, we compute and use its Hermite
+ * normal form.
+ *
+ * The affine transformation matrix returned is then
+ *
+ * [ 1 0 ]
+ * [ y_0 G ]
+ *
+ * as any y = y_0 + G y' with y' integer is a solution to the original
+ * modulo constraints.
+ */
+__isl_give isl_mat *isl_mat_parameter_compression(__isl_take isl_mat *B,
+ __isl_take isl_vec *d)
+{
+ int i;
+ struct isl_mat *cst = NULL;
+ struct isl_mat *T = NULL;
+ isl_int D;
+
+ if (!B || !d)
+ goto error;
+ isl_assert(B->ctx, B->n_row == d->size, goto error);
+ cst = particular_solution(B, d);
+ if (!cst)
+ goto error;
+ if (cst->n_col == 0) {
+ T = isl_mat_alloc(B->ctx, B->n_col, 0);
+ isl_mat_free(cst);
+ isl_mat_free(B);
+ isl_vec_free(d);
+ return T;
+ }
+ isl_int_init(D);
+ /* Replace a*g*row = 0 mod g*m by row = 0 mod m */
+ for (i = 0; i < B->n_row; ++i) {
+ isl_seq_gcd(B->row[i] + 1, B->n_col - 1, &D);
+ if (isl_int_is_one(D))
+ continue;
+ if (isl_int_is_zero(D)) {
+ B = isl_mat_drop_rows(B, i, 1);
+ d = isl_vec_cow(d);
+ if (!B || !d)
+ goto error2;
+ isl_seq_cpy(d->block.data+i, d->block.data+i+1,
+ d->size - (i+1));
+ d->size--;
+ i--;
+ continue;
+ }
+ B = isl_mat_cow(B);
+ if (!B)
+ goto error2;
+ isl_seq_scale_down(B->row[i] + 1, B->row[i] + 1, D, B->n_col-1);
+ isl_int_gcd(D, D, d->block.data[i]);
+ d = isl_vec_cow(d);
+ if (!d)
+ goto error2;
+ isl_int_divexact(d->block.data[i], d->block.data[i], D);
+ }
+ isl_int_clear(D);
+ if (B->n_row == 0)
+ T = isl_mat_identity(B->ctx, B->n_col);
+ else if (B->n_row == 1)
+ T = parameter_compression_1(B, d);
+ else
+ T = parameter_compression_multi(B, d);
+ T = isl_mat_left_hermite(T, 0, NULL, NULL);
+ if (!T)
+ goto error;
+ isl_mat_sub_copy(T->ctx, T->row + 1, cst->row, cst->n_row, 0, 0, 1);
+ isl_mat_free(cst);
+ isl_mat_free(B);
+ isl_vec_free(d);
+ return T;
+error2:
+ isl_int_clear(D);
+error:
+ isl_mat_free(cst);
+ isl_mat_free(B);
+ isl_vec_free(d);
+ return NULL;
+}
+
+/* Given a set of equalities
+ *
+ * B(y) + A x = 0 (*)
+ *
+ * compute and return an affine transformation T,
+ *
+ * y = T y'
+ *
+ * that bijectively maps the integer vectors y' to integer
+ * vectors y that satisfy the modulo constraints for some value of x.
+ *
+ * Let [H 0] be the Hermite Normal Form of A, i.e.,
+ *
+ * A = [H 0] Q
+ *
+ * Then y is a solution of (*) iff
+ *
+ * H^-1 B(y) (= - [I 0] Q x)
+ *
+ * is an integer vector. Let d be the common denominator of H^-1.
+ * We impose
+ *
+ * d H^-1 B(y) = 0 mod d
+ *
+ * and compute the solution using isl_mat_parameter_compression.
+ */
+__isl_give isl_mat *isl_mat_parameter_compression_ext(__isl_take isl_mat *B,
+ __isl_take isl_mat *A)
+{
+ isl_ctx *ctx;
+ isl_vec *d;
+ int n_row, n_col;
+
+ if (!A)
+ return isl_mat_free(B);
+
+ ctx = isl_mat_get_ctx(A);
+ n_row = A->n_row;
+ n_col = A->n_col;
+ A = isl_mat_left_hermite(A, 0, NULL, NULL);
+ A = isl_mat_drop_cols(A, n_row, n_col - n_row);
+ A = isl_mat_lin_to_aff(A);
+ A = isl_mat_right_inverse(A);
+ d = isl_vec_alloc(ctx, n_row);
+ if (A)
+ d = isl_vec_set(d, A->row[0][0]);
+ A = isl_mat_drop_rows(A, 0, 1);
+ A = isl_mat_drop_cols(A, 0, 1);
+ B = isl_mat_product(A, B);
+
+ return isl_mat_parameter_compression(B, d);
+}
+
+/* Return a compression matrix that indicates that there are no solutions
+ * to the original constraints. In particular, return a zero-column
+ * matrix with 1 + dim rows. If "T2" is not NULL, then assign *T2
+ * the inverse of this matrix. *T2 may already have been assigned
+ * matrix, so free it first.
+ * "free1", "free2" and "free3" are temporary matrices that are
+ * not useful when an empty compression is returned. They are
+ * simply freed.
+ */
+static __isl_give isl_mat *empty_compression(isl_ctx *ctx, unsigned dim,
+ __isl_give isl_mat **T2, __isl_take isl_mat *free1,
+ __isl_take isl_mat *free2, __isl_take isl_mat *free3)
+{
+ isl_mat_free(free1);
+ isl_mat_free(free2);
+ isl_mat_free(free3);
+ if (T2) {
+ isl_mat_free(*T2);
+ *T2 = isl_mat_alloc(ctx, 0, 1 + dim);
+ }
+ return isl_mat_alloc(ctx, 1 + dim, 0);
+}
+
+/* Given a matrix that maps a (possibly) parametric domain to
+ * a parametric domain, add in rows that map the "nparam" parameters onto
+ * themselves.
+ */
+static __isl_give isl_mat *insert_parameter_rows(__isl_take isl_mat *mat,
+ unsigned nparam)
+{
+ int i;
+
+ if (nparam == 0)
+ return mat;
+ if (!mat)
+ return NULL;
+
+ mat = isl_mat_insert_rows(mat, 1, nparam);
+ if (!mat)
+ return NULL;
+
+ for (i = 0; i < nparam; ++i) {
+ isl_seq_clr(mat->row[1 + i], mat->n_col);
+ isl_int_set(mat->row[1 + i][1 + i], mat->row[0][0]);
+ }
+
+ return mat;
+}
+
+/* Given a set of equalities
+ *
+ * -C(y) + M x = 0
+ *
+ * this function computes a unimodular transformation from a lower-dimensional
+ * space to the original space that bijectively maps the integer points x'
+ * in the lower-dimensional space to the integer points x in the original
+ * space that satisfy the equalities.
+ *
+ * The input is given as a matrix B = [ -C M ] and the output is a
+ * matrix that maps [1 x'] to [1 x].
+ * The number of equality constraints in B is assumed to be smaller than
+ * or equal to the number of variables x.
+ * "first" is the position of the first x variable.
+ * The preceding variables are considered to be y-variables.
+ * If T2 is not NULL, then *T2 is set to a matrix mapping [1 x] to [1 x'].
+ *
+ * First compute the (left) Hermite normal form of M,
+ *
+ * M [U1 U2] = M U = H = [H1 0]
+ * or
+ * M = H Q = [H1 0] [Q1]
+ * [Q2]
+ *
+ * with U, Q unimodular, Q = U^{-1} (and H lower triangular).
+ * Define the transformed variables as
+ *
+ * x = [U1 U2] [ x1' ] = [U1 U2] [Q1] x
+ * [ x2' ] [Q2]
+ *
+ * The equalities then become
+ *
+ * -C(y) + H1 x1' = 0 or x1' = H1^{-1} C(y) = C'(y)
+ *
+ * If the denominator of the constant term does not divide the
+ * the common denominator of the coefficients of y, then every
+ * integer point is mapped to a non-integer point and then the original set
+ * has no integer solutions (since the x' are a unimodular transformation
+ * of the x). In this case, a zero-column matrix is returned.
+ * Otherwise, the transformation is given by
+ *
+ * x = U1 H1^{-1} C(y) + U2 x2'
+ *
+ * The inverse transformation is simply
+ *
+ * x2' = Q2 x
+ */
+__isl_give isl_mat *isl_mat_final_variable_compression(__isl_take isl_mat *B,
+ int first, __isl_give isl_mat **T2)
+{
+ int i, n;
+ isl_ctx *ctx;
+ isl_mat *H = NULL, *C, *H1, *U = NULL, *U1, *U2;
+ unsigned dim;
+
+ if (T2)
+ *T2 = NULL;
+ if (!B)
+ goto error;
+
+ ctx = isl_mat_get_ctx(B);
+ dim = B->n_col - 1;
+ n = dim - first;
+ if (n < B->n_row)
+ isl_die(ctx, isl_error_invalid, "too many equality constraints",
+ goto error);
+ H = isl_mat_sub_alloc(B, 0, B->n_row, 1 + first, n);
+ H = isl_mat_left_hermite(H, 0, &U, T2);
+ if (!H || !U || (T2 && !*T2))
+ goto error;
+ if (T2) {
+ *T2 = isl_mat_drop_rows(*T2, 0, B->n_row);
+ *T2 = isl_mat_diagonal(isl_mat_identity(ctx, 1 + first), *T2);
+ if (!*T2)
+ goto error;
+ }
+ C = isl_mat_alloc(ctx, 1 + B->n_row, 1 + first);
+ if (!C)
+ goto error;
+ isl_int_set_si(C->row[0][0], 1);
+ isl_seq_clr(C->row[0] + 1, first);
+ isl_mat_sub_neg(ctx, C->row + 1, B->row, B->n_row, 0, 0, 1 + first);
+ H1 = isl_mat_sub_alloc(H, 0, H->n_row, 0, H->n_row);
+ H1 = isl_mat_lin_to_aff(H1);
+ C = isl_mat_inverse_product(H1, C);
+ if (!C)
+ goto error;
+ isl_mat_free(H);
+ if (!isl_int_is_one(C->row[0][0])) {
+ isl_int g;
+
+ isl_int_init(g);
+ for (i = 0; i < B->n_row; ++i) {
+ isl_seq_gcd(C->row[1 + i] + 1, first, &g);
+ isl_int_gcd(g, g, C->row[0][0]);
+ if (!isl_int_is_divisible_by(C->row[1 + i][0], g))
+ break;
+ }
+ isl_int_clear(g);
+
+ if (i < B->n_row)
+ return empty_compression(ctx, dim, T2, B, C, U);
+ C = isl_mat_normalize(C);
+ }
+ U1 = isl_mat_sub_alloc(U, 0, U->n_row, 0, B->n_row);
+ U1 = isl_mat_lin_to_aff(U1);
+ U2 = isl_mat_sub_alloc(U, 0, U->n_row, B->n_row, U->n_row - B->n_row);
+ U2 = isl_mat_lin_to_aff(U2);
+ isl_mat_free(U);
+ C = isl_mat_product(U1, C);
+ C = isl_mat_aff_direct_sum(C, U2);
+ C = insert_parameter_rows(C, first);
+
+ isl_mat_free(B);
+
+ return C;
+error:
+ isl_mat_free(B);
+ isl_mat_free(H);
+ isl_mat_free(U);
+ if (T2) {
+ isl_mat_free(*T2);
+ *T2 = NULL;
+ }
+ return NULL;
+}
+
+/* Given a set of equalities
+ *
+ * M x - c = 0
+ *
+ * this function computes a unimodular transformation from a lower-dimensional
+ * space to the original space that bijectively maps the integer points x'
+ * in the lower-dimensional space to the integer points x in the original
+ * space that satisfy the equalities.
+ *
+ * The input is given as a matrix B = [ -c M ] and the output is a
+ * matrix that maps [1 x'] to [1 x].
+ * The number of equality constraints in B is assumed to be smaller than
+ * or equal to the number of variables x.
+ * If T2 is not NULL, then *T2 is set to a matrix mapping [1 x] to [1 x'].
+ */
+__isl_give isl_mat *isl_mat_variable_compression(__isl_take isl_mat *B,
+ __isl_give isl_mat **T2)
+{
+ return isl_mat_final_variable_compression(B, 0, T2);
+}
+
+/* Return "bset" and set *T and *T2 to the identity transformation
+ * on "bset" (provided T and T2 are not NULL).
+ */
+static __isl_give isl_basic_set *return_with_identity(
+ __isl_take isl_basic_set *bset, __isl_give isl_mat **T,
+ __isl_give isl_mat **T2)
+{
+ isl_size dim;
+ isl_mat *id;
+
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ if (dim < 0)
+ return isl_basic_set_free(bset);
+ if (!T && !T2)
+ return bset;
+
+ id = isl_mat_identity(isl_basic_map_get_ctx(bset), 1 + dim);
+ if (T)
+ *T = isl_mat_copy(id);
+ if (T2)
+ *T2 = isl_mat_copy(id);
+ isl_mat_free(id);
+
+ return bset;
+}
+
+/* Use the n equalities of bset to unimodularly transform the
+ * variables x such that n transformed variables x1' have a constant value
+ * and rewrite the constraints of bset in terms of the remaining
+ * transformed variables x2'. The matrix pointed to by T maps
+ * the new variables x2' back to the original variables x, while T2
+ * maps the original variables to the new variables.
+ */
+static __isl_give isl_basic_set *compress_variables(
+ __isl_take isl_basic_set *bset,
+ __isl_give isl_mat **T, __isl_give isl_mat **T2)
+{
+ struct isl_mat *B, *TC;
+ isl_size dim;
+
+ if (T)
+ *T = NULL;
+ if (T2)
+ *T2 = NULL;
+ if (isl_basic_set_check_no_params(bset) < 0 ||
+ isl_basic_set_check_no_locals(bset) < 0)
+ return isl_basic_set_free(bset);
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ if (dim < 0)
+ return isl_basic_set_free(bset);
+ isl_assert(bset->ctx, bset->n_eq <= dim, goto error);
+ if (bset->n_eq == 0)
+ return return_with_identity(bset, T, T2);
+
+ B = isl_mat_sub_alloc6(bset->ctx, bset->eq, 0, bset->n_eq, 0, 1 + dim);
+ TC = isl_mat_variable_compression(B, T2);
+ if (!TC)
+ goto error;
+ if (TC->n_col == 0) {
+ isl_mat_free(TC);
+ if (T2) {
+ isl_mat_free(*T2);
+ *T2 = NULL;
+ }
+ bset = isl_basic_set_set_to_empty(bset);
+ return return_with_identity(bset, T, T2);
+ }
+
+ bset = isl_basic_set_preimage(bset, T ? isl_mat_copy(TC) : TC);
+ if (T)
+ *T = TC;
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_remove_equalities(
+ __isl_take isl_basic_set *bset, __isl_give isl_mat **T,
+ __isl_give isl_mat **T2)
+{
+ if (T)
+ *T = NULL;
+ if (T2)
+ *T2 = NULL;
+ if (isl_basic_set_check_no_params(bset) < 0)
+ return isl_basic_set_free(bset);
+ bset = isl_basic_set_gauss(bset, NULL);
+ if (ISL_F_ISSET(bset, ISL_BASIC_SET_EMPTY))
+ return return_with_identity(bset, T, T2);
+ bset = compress_variables(bset, T, T2);
+ return bset;
+}
+
+/* Check if dimension dim belongs to a residue class
+ * i_dim \equiv r mod m
+ * with m != 1 and if so return m in *modulo and r in *residue.
+ * As a special case, when i_dim has a fixed value v, then
+ * *modulo is set to 0 and *residue to v.
+ *
+ * If i_dim does not belong to such a residue class, then *modulo
+ * is set to 1 and *residue is set to 0.
+ */
+isl_stat isl_basic_set_dim_residue_class(__isl_keep isl_basic_set *bset,
+ int pos, isl_int *modulo, isl_int *residue)
+{
+ isl_bool fixed;
+ struct isl_ctx *ctx;
+ struct isl_mat *H = NULL, *U = NULL, *C, *H1, *U1;
+ isl_size total;
+ isl_size nparam;
+
+ if (!bset || !modulo || !residue)
+ return isl_stat_error;
+
+ fixed = isl_basic_set_plain_dim_is_fixed(bset, pos, residue);
+ if (fixed < 0)
+ return isl_stat_error;
+ if (fixed) {
+ isl_int_set_si(*modulo, 0);
+ return isl_stat_ok;
+ }
+
+ ctx = isl_basic_set_get_ctx(bset);
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ if (total < 0 || nparam < 0)
+ return isl_stat_error;
+ H = isl_mat_sub_alloc6(ctx, bset->eq, 0, bset->n_eq, 1, total);
+ H = isl_mat_left_hermite(H, 0, &U, NULL);
+ if (!H)
+ return isl_stat_error;
+
+ isl_seq_gcd(U->row[nparam + pos]+bset->n_eq,
+ total-bset->n_eq, modulo);
+ if (isl_int_is_zero(*modulo))
+ isl_int_set_si(*modulo, 1);
+ if (isl_int_is_one(*modulo)) {
+ isl_int_set_si(*residue, 0);
+ isl_mat_free(H);
+ isl_mat_free(U);
+ return isl_stat_ok;
+ }
+
+ C = isl_mat_alloc(ctx, 1 + bset->n_eq, 1);
+ if (!C)
+ goto error;
+ isl_int_set_si(C->row[0][0], 1);
+ isl_mat_sub_neg(ctx, C->row + 1, bset->eq, bset->n_eq, 0, 0, 1);
+ H1 = isl_mat_sub_alloc(H, 0, H->n_row, 0, H->n_row);
+ H1 = isl_mat_lin_to_aff(H1);
+ C = isl_mat_inverse_product(H1, C);
+ isl_mat_free(H);
+ U1 = isl_mat_sub_alloc(U, nparam+pos, 1, 0, bset->n_eq);
+ U1 = isl_mat_lin_to_aff(U1);
+ isl_mat_free(U);
+ C = isl_mat_product(U1, C);
+ if (!C)
+ return isl_stat_error;
+ if (!isl_int_is_divisible_by(C->row[1][0], C->row[0][0])) {
+ bset = isl_basic_set_copy(bset);
+ bset = isl_basic_set_set_to_empty(bset);
+ isl_basic_set_free(bset);
+ isl_int_set_si(*modulo, 1);
+ isl_int_set_si(*residue, 0);
+ return isl_stat_ok;
+ }
+ isl_int_divexact(*residue, C->row[1][0], C->row[0][0]);
+ isl_int_fdiv_r(*residue, *residue, *modulo);
+ isl_mat_free(C);
+ return isl_stat_ok;
+error:
+ isl_mat_free(H);
+ isl_mat_free(U);
+ return isl_stat_error;
+}
+
+/* Check if dimension dim belongs to a residue class
+ * i_dim \equiv r mod m
+ * with m != 1 and if so return m in *modulo and r in *residue.
+ * As a special case, when i_dim has a fixed value v, then
+ * *modulo is set to 0 and *residue to v.
+ *
+ * If i_dim does not belong to such a residue class, then *modulo
+ * is set to 1 and *residue is set to 0.
+ */
+isl_stat isl_set_dim_residue_class(__isl_keep isl_set *set,
+ int pos, isl_int *modulo, isl_int *residue)
+{
+ isl_int m;
+ isl_int r;
+ int i;
+
+ if (!set || !modulo || !residue)
+ return isl_stat_error;
+
+ if (set->n == 0) {
+ isl_int_set_si(*modulo, 0);
+ isl_int_set_si(*residue, 0);
+ return isl_stat_ok;
+ }
+
+ if (isl_basic_set_dim_residue_class(set->p[0], pos, modulo, residue)<0)
+ return isl_stat_error;
+
+ if (set->n == 1)
+ return isl_stat_ok;
+
+ if (isl_int_is_one(*modulo))
+ return isl_stat_ok;
+
+ isl_int_init(m);
+ isl_int_init(r);
+
+ for (i = 1; i < set->n; ++i) {
+ if (isl_basic_set_dim_residue_class(set->p[i], pos, &m, &r) < 0)
+ goto error;
+ isl_int_gcd(*modulo, *modulo, m);
+ isl_int_sub(m, *residue, r);
+ isl_int_gcd(*modulo, *modulo, m);
+ if (!isl_int_is_zero(*modulo))
+ isl_int_fdiv_r(*residue, *residue, *modulo);
+ if (isl_int_is_one(*modulo))
+ break;
+ }
+
+ isl_int_clear(m);
+ isl_int_clear(r);
+
+ return isl_stat_ok;
+error:
+ isl_int_clear(m);
+ isl_int_clear(r);
+ return isl_stat_error;
+}
+
+/* Check if dimension "dim" belongs to a residue class
+ * i_dim \equiv r mod m
+ * with m != 1 and if so return m in *modulo and r in *residue.
+ * As a special case, when i_dim has a fixed value v, then
+ * *modulo is set to 0 and *residue to v.
+ *
+ * If i_dim does not belong to such a residue class, then *modulo
+ * is set to 1 and *residue is set to 0.
+ */
+isl_stat isl_set_dim_residue_class_val(__isl_keep isl_set *set,
+ int pos, __isl_give isl_val **modulo, __isl_give isl_val **residue)
+{
+ *modulo = NULL;
+ *residue = NULL;
+ if (!set)
+ return isl_stat_error;
+ *modulo = isl_val_alloc(isl_set_get_ctx(set));
+ *residue = isl_val_alloc(isl_set_get_ctx(set));
+ if (!*modulo || !*residue)
+ goto error;
+ if (isl_set_dim_residue_class(set, pos,
+ &(*modulo)->n, &(*residue)->n) < 0)
+ goto error;
+ isl_int_set_si((*modulo)->d, 1);
+ isl_int_set_si((*residue)->d, 1);
+ return isl_stat_ok;
+error:
+ isl_val_free(*modulo);
+ isl_val_free(*residue);
+ return isl_stat_error;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_equalities.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_equalities.h
new file mode 100644
index 00000000000..05c69ddd8d9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_equalities.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_EQUALITIES_H
+#define ISL_EQUALITIES_H
+
+#include <isl/set.h>
+#include <isl/mat.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+__isl_give isl_mat *isl_mat_final_variable_compression(__isl_take isl_mat *B,
+ int first, __isl_give isl_mat **T2);
+__isl_give isl_mat *isl_mat_variable_compression(__isl_take isl_mat *B,
+ __isl_give isl_mat **T2);
+__isl_give isl_mat *isl_mat_parameter_compression(__isl_take isl_mat *B,
+ __isl_take isl_vec *d);
+__isl_give isl_mat *isl_mat_parameter_compression_ext(__isl_take isl_mat *B,
+ __isl_take isl_mat *A);
+__isl_give isl_basic_set *isl_basic_set_remove_equalities(
+ __isl_take isl_basic_set *bset, __isl_give isl_mat **T,
+ __isl_give isl_mat **T2);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_factorization.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_factorization.c
new file mode 100644
index 00000000000..06aa462a3f5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_factorization.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright 2005-2007 Universiteit Leiden
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, Leiden Institute of Advanced Computer Science,
+ * Universiteit Leiden, Niels Bohrweg 1, 2333 CA Leiden, The Netherlands
+ * and K.U.Leuven, Departement Computerwetenschappen, Celestijnenlaan 200A,
+ * B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ */
+
+#include <isl_map_private.h>
+#include <isl_factorization.h>
+#include <isl_space_private.h>
+#include <isl_mat_private.h>
+
+/* Return the isl_ctx to which "f" belongs.
+ */
+isl_ctx *isl_factorizer_get_ctx(__isl_keep isl_factorizer *f)
+{
+ if (!f)
+ return NULL;
+ return isl_basic_set_get_ctx(f->bset);
+}
+
+static __isl_give isl_factorizer *isl_factorizer_alloc(
+ __isl_keep isl_basic_set *bset, __isl_take isl_morph *morph,
+ int n_group)
+{
+ isl_factorizer *f = NULL;
+ int *len = NULL;
+
+ if (!morph)
+ return NULL;
+
+ if (n_group > 0) {
+ len = isl_alloc_array(morph->dom->ctx, int, n_group);
+ if (!len)
+ goto error;
+ }
+
+ f = isl_alloc_type(morph->dom->ctx, struct isl_factorizer);
+ if (!f)
+ goto error;
+
+ f->bset = isl_basic_set_copy(bset);
+ f->morph = morph;
+ f->n_group = n_group;
+ f->len = len;
+
+ return f;
+error:
+ free(len);
+ isl_morph_free(morph);
+ return NULL;
+}
+
+__isl_null isl_factorizer *isl_factorizer_free(__isl_take isl_factorizer *f)
+{
+ if (!f)
+ return NULL;
+
+ isl_basic_set_free(f->bset);
+ isl_morph_free(f->morph);
+ free(f->len);
+ free(f);
+ return NULL;
+}
+
+void isl_factorizer_dump(__isl_take isl_factorizer *f)
+{
+ int i;
+
+ if (!f)
+ return;
+
+ isl_morph_print_internal(f->morph, stderr);
+ fprintf(stderr, "[");
+ for (i = 0; i < f->n_group; ++i) {
+ if (i)
+ fprintf(stderr, ", ");
+ fprintf(stderr, "%d", f->len[i]);
+ }
+ fprintf(stderr, "]\n");
+}
+
+__isl_give isl_factorizer *isl_factorizer_identity(__isl_keep isl_basic_set *bset)
+{
+ return isl_factorizer_alloc(bset, isl_morph_identity(bset), 0);
+}
+
+__isl_give isl_factorizer *isl_factorizer_groups(__isl_keep isl_basic_set *bset,
+ __isl_take isl_mat *Q, __isl_take isl_mat *U, int n, int *len)
+{
+ int i;
+ isl_size nvar;
+ unsigned ovar;
+ isl_space *space;
+ isl_basic_set *dom;
+ isl_basic_set *ran;
+ isl_morph *morph;
+ isl_factorizer *f;
+ isl_mat *id;
+
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+ if (nvar < 0 || !Q || !U)
+ goto error;
+
+ ovar = 1 + isl_space_offset(bset->dim, isl_dim_set);
+ id = isl_mat_identity(bset->ctx, ovar);
+ Q = isl_mat_diagonal(isl_mat_copy(id), Q);
+ U = isl_mat_diagonal(id, U);
+
+ space = isl_basic_set_get_space(bset);
+ dom = isl_basic_set_universe(isl_space_copy(space));
+ space = isl_space_drop_dims(space, isl_dim_set, 0, nvar);
+ space = isl_space_add_dims(space, isl_dim_set, nvar);
+ ran = isl_basic_set_universe(space);
+ morph = isl_morph_alloc(dom, ran, Q, U);
+ f = isl_factorizer_alloc(bset, morph, n);
+ if (!f)
+ return NULL;
+ for (i = 0; i < n; ++i)
+ f->len[i] = len[i];
+ return f;
+error:
+ isl_mat_free(Q);
+ isl_mat_free(U);
+ return NULL;
+}
+
+struct isl_factor_groups {
+ int *pos; /* for each column: row position of pivot */
+ int *group; /* group to which a column belongs */
+ int *cnt; /* number of columns in the group */
+ int *rowgroup; /* group to which a constraint belongs */
+};
+
+/* Initialize isl_factor_groups structure: find pivot row positions,
+ * each column initially belongs to its own group and the groups
+ * of the constraints are still unknown.
+ */
+static int init_groups(struct isl_factor_groups *g, __isl_keep isl_mat *H)
+{
+ int i, j;
+
+ if (!H)
+ return -1;
+
+ g->pos = isl_alloc_array(H->ctx, int, H->n_col);
+ g->group = isl_alloc_array(H->ctx, int, H->n_col);
+ g->cnt = isl_alloc_array(H->ctx, int, H->n_col);
+ g->rowgroup = isl_alloc_array(H->ctx, int, H->n_row);
+
+ if (!g->pos || !g->group || !g->cnt || !g->rowgroup)
+ return -1;
+
+ for (i = 0; i < H->n_row; ++i)
+ g->rowgroup[i] = -1;
+ for (i = 0, j = 0; i < H->n_col; ++i) {
+ for ( ; j < H->n_row; ++j)
+ if (!isl_int_is_zero(H->row[j][i]))
+ break;
+ g->pos[i] = j;
+ }
+ for (i = 0; i < H->n_col; ++i) {
+ g->group[i] = i;
+ g->cnt[i] = 1;
+ }
+
+ return 0;
+}
+
+/* Update group[k] to the group column k belongs to.
+ * When merging two groups, only the group of the current
+ * group leader is changed. Here we change the group of
+ * the other members to also point to the group that the
+ * old group leader now points to.
+ */
+static void update_group(struct isl_factor_groups *g, int k)
+{
+ int p = g->group[k];
+ while (g->cnt[p] == 0)
+ p = g->group[p];
+ g->group[k] = p;
+}
+
+/* Merge group i with all groups of the subsequent columns
+ * with non-zero coefficients in row j of H.
+ * (The previous columns are all zero; otherwise we would have handled
+ * the row before.)
+ */
+static int update_group_i_with_row_j(struct isl_factor_groups *g, int i, int j,
+ __isl_keep isl_mat *H)
+{
+ int k;
+
+ g->rowgroup[j] = g->group[i];
+ for (k = i + 1; k < H->n_col && j >= g->pos[k]; ++k) {
+ update_group(g, k);
+ update_group(g, i);
+ if (g->group[k] != g->group[i] &&
+ !isl_int_is_zero(H->row[j][k])) {
+ isl_assert(H->ctx, g->cnt[g->group[k]] != 0, return -1);
+ isl_assert(H->ctx, g->cnt[g->group[i]] != 0, return -1);
+ if (g->group[i] < g->group[k]) {
+ g->cnt[g->group[i]] += g->cnt[g->group[k]];
+ g->cnt[g->group[k]] = 0;
+ g->group[g->group[k]] = g->group[i];
+ } else {
+ g->cnt[g->group[k]] += g->cnt[g->group[i]];
+ g->cnt[g->group[i]] = 0;
+ g->group[g->group[i]] = g->group[k];
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Update the group information based on the constraint matrix.
+ */
+static int update_groups(struct isl_factor_groups *g, __isl_keep isl_mat *H)
+{
+ int i, j;
+
+ for (i = 0; i < H->n_col && g->cnt[0] < H->n_col; ++i) {
+ if (g->pos[i] == H->n_row)
+ continue; /* A line direction */
+ if (g->rowgroup[g->pos[i]] == -1)
+ g->rowgroup[g->pos[i]] = i;
+ for (j = g->pos[i] + 1; j < H->n_row; ++j) {
+ if (isl_int_is_zero(H->row[j][i]))
+ continue;
+ if (g->rowgroup[j] != -1)
+ continue;
+ if (update_group_i_with_row_j(g, i, j, H) < 0)
+ return -1;
+ }
+ }
+ for (i = 1; i < H->n_col; ++i)
+ update_group(g, i);
+
+ return 0;
+}
+
+static void clear_groups(struct isl_factor_groups *g)
+{
+ if (!g)
+ return;
+ free(g->pos);
+ free(g->group);
+ free(g->cnt);
+ free(g->rowgroup);
+}
+
+/* Determine if the set variables of the basic set can be factorized and
+ * return the results in an isl_factorizer.
+ *
+ * The algorithm works by first computing the Hermite normal form
+ * and then grouping columns linked by one or more constraints together,
+ * where a constraints "links" two or more columns if the constraint
+ * has nonzero coefficients in the columns.
+ */
+__isl_give isl_factorizer *isl_basic_set_factorizer(
+ __isl_keep isl_basic_set *bset)
+{
+ int i, j, n, done;
+ isl_mat *H, *U, *Q;
+ isl_size nvar;
+ struct isl_factor_groups g = { 0 };
+ isl_factorizer *f;
+
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+ if (nvar < 0 || isl_basic_set_check_no_locals(bset) < 0)
+ return NULL;
+
+ if (nvar <= 1)
+ return isl_factorizer_identity(bset);
+
+ H = isl_mat_alloc(bset->ctx, bset->n_eq + bset->n_ineq, nvar);
+ if (!H)
+ return NULL;
+ isl_mat_sub_copy(bset->ctx, H->row, bset->eq, bset->n_eq,
+ 0, 1 + isl_space_offset(bset->dim, isl_dim_set), nvar);
+ isl_mat_sub_copy(bset->ctx, H->row + bset->n_eq, bset->ineq, bset->n_ineq,
+ 0, 1 + isl_space_offset(bset->dim, isl_dim_set), nvar);
+ H = isl_mat_left_hermite(H, 0, &U, &Q);
+
+ if (init_groups(&g, H) < 0)
+ goto error;
+ if (update_groups(&g, H) < 0)
+ goto error;
+
+ if (g.cnt[0] == nvar) {
+ isl_mat_free(H);
+ isl_mat_free(U);
+ isl_mat_free(Q);
+ clear_groups(&g);
+
+ return isl_factorizer_identity(bset);
+ }
+
+ done = 0;
+ n = 0;
+ while (done != nvar) {
+ int group = g.group[done];
+ for (i = 1; i < g.cnt[group]; ++i) {
+ if (g.group[done + i] == group)
+ continue;
+ for (j = done + g.cnt[group]; j < nvar; ++j)
+ if (g.group[j] == group)
+ break;
+ if (j == nvar)
+ isl_die(bset->ctx, isl_error_internal,
+ "internal error", goto error);
+ g.group[j] = g.group[done + i];
+ Q = isl_mat_swap_rows(Q, done + i, j);
+ U = isl_mat_swap_cols(U, done + i, j);
+ }
+ done += g.cnt[group];
+ g.pos[n++] = g.cnt[group];
+ }
+
+ f = isl_factorizer_groups(bset, Q, U, n, g.pos);
+
+ isl_mat_free(H);
+ clear_groups(&g);
+
+ return f;
+error:
+ isl_mat_free(H);
+ isl_mat_free(U);
+ isl_mat_free(Q);
+ clear_groups(&g);
+ return NULL;
+}
+
+/* Given the factorizer "f" of a basic set,
+ * call "test" on each resulting factor as long as each call succeeds.
+ */
+__isl_give isl_bool isl_factorizer_every_factor_basic_set(
+ __isl_keep isl_factorizer *f,
+ isl_bool (*test)(__isl_keep isl_basic_set *bset, void *user),
+ void *user)
+{
+ int i, n;
+ isl_bool every = isl_bool_true;
+ isl_size nparam, nvar;
+ isl_basic_set *bset;
+
+ if (!f)
+ return isl_bool_error;
+ nparam = isl_basic_set_dim(f->bset, isl_dim_param);
+ nvar = isl_basic_set_dim(f->bset, isl_dim_set);
+ if (nparam < 0 || nvar < 0)
+ return isl_bool_error;
+
+ bset = isl_basic_set_copy(f->bset);
+ bset = isl_morph_basic_set(isl_morph_copy(f->morph), bset);
+
+ for (i = 0, n = 0; i < f->n_group; ++i) {
+ isl_basic_set *factor;
+
+ factor = isl_basic_set_copy(bset);
+ factor = isl_basic_set_drop_constraints_involving(factor,
+ nparam + n + f->len[i], nvar - n - f->len[i]);
+ factor = isl_basic_set_drop_constraints_involving(factor,
+ nparam, n);
+ factor = isl_basic_set_drop(factor, isl_dim_set,
+ n + f->len[i], nvar - n - f->len[i]);
+ factor = isl_basic_set_drop(factor, isl_dim_set, 0, n);
+ every = test(factor, user);
+ isl_basic_set_free(factor);
+
+ if (every < 0 || !every)
+ break;
+
+ n += f->len[i];
+ }
+
+ isl_basic_set_free(bset);
+
+ return every;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_factorization.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_factorization.h
new file mode 100644
index 00000000000..8b9bf76be3c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_factorization.h
@@ -0,0 +1,42 @@
+#ifndef ISL_FACTORIZATION_H
+#define ISL_FACTORIZATION_H
+
+#include <isl/set.h>
+#include <isl_morph.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Data for factorizing the basic set "bset".
+ * After applying "morph" to the basic set, there are "n_group"
+ * groups of consecutive set variables, each of length "len[i]",
+ * with 0 <= i < n_group.
+ * If no factorization is possible, then "n_group" is set to 0.
+ */
+struct isl_factorizer {
+ isl_basic_set *bset;
+ isl_morph *morph;
+ int n_group;
+ int *len;
+};
+typedef struct isl_factorizer isl_factorizer;
+
+__isl_give isl_factorizer *isl_basic_set_factorizer(
+ __isl_keep isl_basic_set *bset);
+
+isl_ctx *isl_factorizer_get_ctx(__isl_keep isl_factorizer *f);
+
+__isl_null isl_factorizer *isl_factorizer_free(__isl_take isl_factorizer *f);
+void isl_factorizer_dump(__isl_take isl_factorizer *f);
+
+__isl_give isl_bool isl_factorizer_every_factor_basic_set(
+ __isl_keep isl_factorizer *f,
+ isl_bool (*test)(__isl_keep isl_basic_set *bset, void *user),
+ void *user);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_farkas.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_farkas.c
new file mode 100644
index 00000000000..4ff6d8a4059
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_farkas.c
@@ -0,0 +1,966 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_map_private.h>
+#include <isl/set.h>
+#include <isl_space_private.h>
+#include <isl_seq.h>
+#include <isl_aff_private.h>
+#include <isl_mat_private.h>
+#include <isl_factorization.h>
+
+/*
+ * Let C be a cone and define
+ *
+ * C' := { y | forall x in C : y x >= 0 }
+ *
+ * C' contains the coefficients of all linear constraints
+ * that are valid for C.
+ * Furthermore, C'' = C.
+ *
+ * If C is defined as { x | A x >= 0 }
+ * then any element in C' must be a non-negative combination
+ * of the rows of A, i.e., y = t A with t >= 0. That is,
+ *
+ * C' = { y | exists t >= 0 : y = t A }
+ *
+ * If any of the rows in A actually represents an equality, then
+ * also negative combinations of this row are allowed and so the
+ * non-negativity constraint on the corresponding element of t
+ * can be dropped.
+ *
+ * A polyhedron P = { x | b + A x >= 0 } can be represented
+ * in homogeneous coordinates by the cone
+ * C = { [z,x] | b z + A x >= and z >= 0 }
+ * The valid linear constraints on C correspond to the valid affine
+ * constraints on P.
+ * This is essentially Farkas' lemma.
+ *
+ * Since
+ * [ 1 0 ]
+ * [ w y ] = [t_0 t] [ b A ]
+ *
+ * we have
+ *
+ * C' = { w, y | exists t_0, t >= 0 : y = t A and w = t_0 + t b }
+ * or
+ *
+ * C' = { w, y | exists t >= 0 : y = t A and w - t b >= 0 }
+ *
+ * In practice, we introduce an extra variable (w), shifting all
+ * other variables to the right, and an extra inequality
+ * (w - t b >= 0) corresponding to the positivity constraint on
+ * the homogeneous coordinate.
+ *
+ * When going back from coefficients to solutions, we immediately
+ * plug in 1 for z, which corresponds to shifting all variables
+ * to the left, with the leftmost ending up in the constant position.
+ */
+
+/* Add the given prefix to all named isl_dim_set dimensions in "space".
+ */
+static __isl_give isl_space *isl_space_prefix(__isl_take isl_space *space,
+ const char *prefix)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_size nvar;
+ size_t prefix_len = strlen(prefix);
+
+ if (!space)
+ return NULL;
+
+ ctx = isl_space_get_ctx(space);
+ nvar = isl_space_dim(space, isl_dim_set);
+ if (nvar < 0)
+ return isl_space_free(space);
+
+ for (i = 0; i < nvar; ++i) {
+ const char *name;
+ char *prefix_name;
+
+ name = isl_space_get_dim_name(space, isl_dim_set, i);
+ if (!name)
+ continue;
+
+ prefix_name = isl_alloc_array(ctx, char,
+ prefix_len + strlen(name) + 1);
+ if (!prefix_name)
+ goto error;
+ memcpy(prefix_name, prefix, prefix_len);
+ strcpy(prefix_name + prefix_len, name);
+
+ space = isl_space_set_dim_name(space,
+ isl_dim_set, i, prefix_name);
+ free(prefix_name);
+ }
+
+ return space;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Given a dimension specification of the solutions space, construct
+ * a dimension specification for the space of coefficients.
+ *
+ * In particular transform
+ *
+ * [params] -> { S }
+ *
+ * to
+ *
+ * { coefficients[[cst, params] -> S] }
+ *
+ * and prefix each dimension name with "c_".
+ */
+static __isl_give isl_space *isl_space_coefficients(__isl_take isl_space *space)
+{
+ isl_space *space_param;
+ isl_size nvar;
+ isl_size nparam;
+
+ nvar = isl_space_dim(space, isl_dim_set);
+ nparam = isl_space_dim(space, isl_dim_param);
+ if (nvar < 0 || nparam < 0)
+ return isl_space_free(space);
+ space_param = isl_space_copy(space);
+ space_param = isl_space_drop_dims(space_param, isl_dim_set, 0, nvar);
+ space_param = isl_space_move_dims(space_param, isl_dim_set, 0,
+ isl_dim_param, 0, nparam);
+ space_param = isl_space_prefix(space_param, "c_");
+ space_param = isl_space_insert_dims(space_param, isl_dim_set, 0, 1);
+ space_param = isl_space_set_dim_name(space_param,
+ isl_dim_set, 0, "c_cst");
+ space = isl_space_drop_dims(space, isl_dim_param, 0, nparam);
+ space = isl_space_prefix(space, "c_");
+ space = isl_space_join(isl_space_from_domain(space_param),
+ isl_space_from_range(space));
+ space = isl_space_wrap(space);
+ space = isl_space_set_tuple_name(space, isl_dim_set, "coefficients");
+
+ return space;
+}
+
+/* Drop the given prefix from all named dimensions of type "type" in "space".
+ */
+static __isl_give isl_space *isl_space_unprefix(__isl_take isl_space *space,
+ enum isl_dim_type type, const char *prefix)
+{
+ int i;
+ isl_size n;
+ size_t prefix_len = strlen(prefix);
+
+ n = isl_space_dim(space, type);
+ if (n < 0)
+ return isl_space_free(space);
+
+ for (i = 0; i < n; ++i) {
+ const char *name;
+
+ name = isl_space_get_dim_name(space, type, i);
+ if (!name)
+ continue;
+ if (strncmp(name, prefix, prefix_len))
+ continue;
+
+ space = isl_space_set_dim_name(space,
+ type, i, name + prefix_len);
+ }
+
+ return space;
+}
+
+/* Given a dimension specification of the space of coefficients, construct
+ * a dimension specification for the space of solutions.
+ *
+ * In particular transform
+ *
+ * { coefficients[[cst, params] -> S] }
+ *
+ * to
+ *
+ * [params] -> { S }
+ *
+ * and drop the "c_" prefix from the dimension names.
+ */
+static __isl_give isl_space *isl_space_solutions(__isl_take isl_space *space)
+{
+ isl_size nparam;
+
+ space = isl_space_unwrap(space);
+ space = isl_space_drop_dims(space, isl_dim_in, 0, 1);
+ space = isl_space_unprefix(space, isl_dim_in, "c_");
+ space = isl_space_unprefix(space, isl_dim_out, "c_");
+ nparam = isl_space_dim(space, isl_dim_in);
+ if (nparam < 0)
+ return isl_space_free(space);
+ space = isl_space_move_dims(space,
+ isl_dim_param, 0, isl_dim_in, 0, nparam);
+ space = isl_space_range(space);
+
+ return space;
+}
+
+/* Return the rational universe basic set in the given space.
+ */
+static __isl_give isl_basic_set *rational_universe(__isl_take isl_space *space)
+{
+ isl_basic_set *bset;
+
+ bset = isl_basic_set_universe(space);
+ bset = isl_basic_set_set_rational(bset);
+
+ return bset;
+}
+
+/* Compute the dual of "bset" by applying Farkas' lemma.
+ * As explained above, we add an extra dimension to represent
+ * the coefficient of the constant term when going from solutions
+ * to coefficients (shift == 1) and we drop the extra dimension when going
+ * in the opposite direction (shift == -1).
+ * The dual can be created in an arbitrary space.
+ * The caller is responsible for putting the result in the appropriate space.
+ *
+ * If "bset" is (obviously) empty, then the way this emptiness
+ * is represented by the constraints does not allow for the application
+ * of the standard farkas algorithm. We therefore handle this case
+ * specifically and return the universe basic set.
+ */
+static __isl_give isl_basic_set *farkas(__isl_take isl_basic_set *bset,
+ int shift)
+{
+ int i, j, k;
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_basic_set *dual = NULL;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ return isl_basic_set_free(bset);
+
+ ctx = isl_basic_set_get_ctx(bset);
+ space = isl_space_set_alloc(ctx, 0, total + shift);
+ if (isl_basic_set_plain_is_empty(bset)) {
+ isl_basic_set_free(bset);
+ return rational_universe(space);
+ }
+
+ dual = isl_basic_set_alloc_space(space, bset->n_eq + bset->n_ineq,
+ total, bset->n_ineq + (shift > 0));
+ dual = isl_basic_set_set_rational(dual);
+
+ for (i = 0; i < bset->n_eq + bset->n_ineq; ++i) {
+ k = isl_basic_set_alloc_div(dual);
+ if (k < 0)
+ goto error;
+ isl_int_set_si(dual->div[k][0], 0);
+ }
+
+ for (i = 0; i < total; ++i) {
+ k = isl_basic_set_alloc_equality(dual);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(dual->eq[k], 1 + shift + total);
+ isl_int_set_si(dual->eq[k][1 + shift + i], -1);
+ for (j = 0; j < bset->n_eq; ++j)
+ isl_int_set(dual->eq[k][1 + shift + total + j],
+ bset->eq[j][1 + i]);
+ for (j = 0; j < bset->n_ineq; ++j)
+ isl_int_set(dual->eq[k][1 + shift + total + bset->n_eq + j],
+ bset->ineq[j][1 + i]);
+ }
+
+ for (i = 0; i < bset->n_ineq; ++i) {
+ k = isl_basic_set_alloc_inequality(dual);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(dual->ineq[k],
+ 1 + shift + total + bset->n_eq + bset->n_ineq);
+ isl_int_set_si(dual->ineq[k][1 + shift + total + bset->n_eq + i], 1);
+ }
+
+ if (shift > 0) {
+ k = isl_basic_set_alloc_inequality(dual);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(dual->ineq[k], 2 + total);
+ isl_int_set_si(dual->ineq[k][1], 1);
+ for (j = 0; j < bset->n_eq; ++j)
+ isl_int_neg(dual->ineq[k][2 + total + j],
+ bset->eq[j][0]);
+ for (j = 0; j < bset->n_ineq; ++j)
+ isl_int_neg(dual->ineq[k][2 + total + bset->n_eq + j],
+ bset->ineq[j][0]);
+ }
+
+ dual = isl_basic_set_remove_divs(dual);
+ dual = isl_basic_set_simplify(dual);
+ dual = isl_basic_set_finalize(dual);
+
+ isl_basic_set_free(bset);
+ return dual;
+error:
+ isl_basic_set_free(bset);
+ isl_basic_set_free(dual);
+ return NULL;
+}
+
+/* Construct a basic set containing the tuples of coefficients of all
+ * valid affine constraints on the given basic set, ignoring
+ * the space of input and output and without any further decomposition.
+ */
+static __isl_give isl_basic_set *isl_basic_set_coefficients_base(
+ __isl_take isl_basic_set *bset)
+{
+ return farkas(bset, 1);
+}
+
+/* Return the inverse mapping of "morph".
+ */
+static __isl_give isl_mat *peek_inv(__isl_keep isl_morph *morph)
+{
+ return morph ? morph->inv : NULL;
+}
+
+/* Return a copy of the inverse mapping of "morph".
+ */
+static __isl_give isl_mat *get_inv(__isl_keep isl_morph *morph)
+{
+ return isl_mat_copy(peek_inv(morph));
+}
+
+/* Information about a single factor within isl_basic_set_coefficients_product.
+ *
+ * "start" is the position of the first coefficient (beyond
+ * the one corresponding to the constant term) in this factor.
+ * "dim" is the number of coefficients (other than
+ * the one corresponding to the constant term) in this factor.
+ * "n_line" is the number of lines in "coeff".
+ * "n_ray" is the number of rays (other than lines) in "coeff".
+ * "n_vertex" is the number of vertices in "coeff".
+ *
+ * While iterating over the vertices,
+ * "pos" represents the inequality constraint corresponding
+ * to the current vertex.
+ */
+struct isl_coefficients_factor_data {
+ isl_basic_set *coeff;
+ int start;
+ int dim;
+ int n_line;
+ int n_ray;
+ int n_vertex;
+ int pos;
+};
+
+/* Internal data structure for isl_basic_set_coefficients_product.
+ * "n" is the number of factors in the factorization.
+ * "pos" is the next factor that will be considered.
+ * "start_next" is the position of the first coefficient (beyond
+ * the one corresponding to the constant term) in the next factor.
+ * "factors" contains information about the individual "n" factors.
+ */
+struct isl_coefficients_product_data {
+ int n;
+ int pos;
+ int start_next;
+ struct isl_coefficients_factor_data *factors;
+};
+
+/* Initialize the internal data structure for
+ * isl_basic_set_coefficients_product.
+ */
+static isl_stat isl_coefficients_product_data_init(isl_ctx *ctx,
+ struct isl_coefficients_product_data *data, int n)
+{
+ data->n = n;
+ data->pos = 0;
+ data->start_next = 0;
+ data->factors = isl_calloc_array(ctx,
+ struct isl_coefficients_factor_data, n);
+ if (!data->factors)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Free all memory allocated in "data".
+ */
+static void isl_coefficients_product_data_clear(
+ struct isl_coefficients_product_data *data)
+{
+ int i;
+
+ if (data->factors) {
+ for (i = 0; i < data->n; ++i) {
+ isl_basic_set_free(data->factors[i].coeff);
+ }
+ }
+ free(data->factors);
+}
+
+/* Does inequality "ineq" in the (dual) basic set "bset" represent a ray?
+ * In particular, does it have a zero denominator
+ * (i.e., a zero coefficient for the constant term)?
+ */
+static int is_ray(__isl_keep isl_basic_set *bset, int ineq)
+{
+ return isl_int_is_zero(bset->ineq[ineq][1]);
+}
+
+/* isl_factorizer_every_factor_basic_set callback that
+ * constructs a basic set containing the tuples of coefficients of all
+ * valid affine constraints on the factor "bset" and
+ * extracts further information that will be used
+ * when combining the results over the different factors.
+ */
+static isl_bool isl_basic_set_coefficients_factor(
+ __isl_keep isl_basic_set *bset, void *user)
+{
+ struct isl_coefficients_product_data *data = user;
+ isl_basic_set *coeff;
+ isl_size n_eq, n_ineq, dim;
+ int i, n_ray, n_vertex;
+
+ coeff = isl_basic_set_coefficients_base(isl_basic_set_copy(bset));
+ data->factors[data->pos].coeff = coeff;
+ if (!coeff)
+ return isl_bool_error;
+
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ n_eq = isl_basic_set_n_equality(coeff);
+ n_ineq = isl_basic_set_n_inequality(coeff);
+ if (dim < 0 || n_eq < 0 || n_ineq < 0)
+ return isl_bool_error;
+ n_ray = n_vertex = 0;
+ for (i = 0; i < n_ineq; ++i) {
+ if (is_ray(coeff, i))
+ n_ray++;
+ else
+ n_vertex++;
+ }
+ data->factors[data->pos].start = data->start_next;
+ data->factors[data->pos].dim = dim;
+ data->factors[data->pos].n_line = n_eq;
+ data->factors[data->pos].n_ray = n_ray;
+ data->factors[data->pos].n_vertex = n_vertex;
+ data->pos++;
+ data->start_next += dim;
+
+ return isl_bool_true;
+}
+
+/* Clear an entry in the product, given that there is a "total" number
+ * of coefficients (other than that of the constant term).
+ */
+static void clear_entry(isl_int *entry, int total)
+{
+ isl_seq_clr(entry, 1 + 1 + total);
+}
+
+/* Set the part of the entry corresponding to factor "data",
+ * from the factor coefficients in "src".
+ */
+static void set_factor(isl_int *entry, isl_int *src,
+ struct isl_coefficients_factor_data *data)
+{
+ isl_seq_cpy(entry + 1 + 1 + data->start, src + 1 + 1, data->dim);
+}
+
+/* Set the part of the entry corresponding to factor "data",
+ * from the factor coefficients in "src" multiplied by "f".
+ */
+static void scale_factor(isl_int *entry, isl_int *src, isl_int f,
+ struct isl_coefficients_factor_data *data)
+{
+ isl_seq_scale(entry + 1 + 1 + data->start, src + 1 + 1, f, data->dim);
+}
+
+/* Add all lines from the given factor to "bset",
+ * given that there is a "total" number of coefficients
+ * (other than that of the constant term).
+ */
+static __isl_give isl_basic_set *add_lines(__isl_take isl_basic_set *bset,
+ struct isl_coefficients_factor_data *factor, int total)
+{
+ int i;
+
+ for (i = 0; i < factor->n_line; ++i) {
+ int k;
+
+ k = isl_basic_set_alloc_equality(bset);
+ if (k < 0)
+ return isl_basic_set_free(bset);
+ clear_entry(bset->eq[k], total);
+ set_factor(bset->eq[k], factor->coeff->eq[i], factor);
+ }
+
+ return bset;
+}
+
+/* Add all rays (other than lines) from the given factor to "bset",
+ * given that there is a "total" number of coefficients
+ * (other than that of the constant term).
+ */
+static __isl_give isl_basic_set *add_rays(__isl_take isl_basic_set *bset,
+ struct isl_coefficients_factor_data *data, int total)
+{
+ int i;
+ int n_ineq = data->n_ray + data->n_vertex;
+
+ for (i = 0; i < n_ineq; ++i) {
+ int k;
+
+ if (!is_ray(data->coeff, i))
+ continue;
+
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ return isl_basic_set_free(bset);
+ clear_entry(bset->ineq[k], total);
+ set_factor(bset->ineq[k], data->coeff->ineq[i], data);
+ }
+
+ return bset;
+}
+
+/* Move to the first vertex of the given factor starting
+ * at inequality constraint "start", setting factor->pos and
+ * returning 1 if a vertex is found.
+ */
+static int factor_first_vertex(struct isl_coefficients_factor_data *factor,
+ int start)
+{
+ int j;
+ int n = factor->n_ray + factor->n_vertex;
+
+ for (j = start; j < n; ++j) {
+ if (is_ray(factor->coeff, j))
+ continue;
+ factor->pos = j;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Move to the first constraint in each factor starting at "first"
+ * that represents a vertex.
+ * In particular, skip the initial constraints that correspond to rays.
+ */
+static void first_vertex(struct isl_coefficients_product_data *data, int first)
+{
+ int i;
+
+ for (i = first; i < data->n; ++i)
+ factor_first_vertex(&data->factors[i], 0);
+}
+
+/* Move to the next vertex in the product.
+ * In particular, move to the next vertex of the last factor.
+ * If all vertices of this last factor have already been considered,
+ * then move to the next vertex of the previous factor(s)
+ * until a factor is found that still has a next vertex.
+ * Once such a next vertex has been found, the subsequent
+ * factors are reset to the first vertex.
+ * Return 1 if any next vertex was found.
+ */
+static int next_vertex(struct isl_coefficients_product_data *data)
+{
+ int i;
+
+ for (i = data->n - 1; i >= 0; --i) {
+ struct isl_coefficients_factor_data *factor = &data->factors[i];
+
+ if (!factor_first_vertex(factor, factor->pos + 1))
+ continue;
+ first_vertex(data, i + 1);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Add a vertex to the product "bset" combining the currently selected
+ * vertices of the factors.
+ *
+ * In the dual representation, the constant term is always zero.
+ * The vertex itself is the sum of the contributions of the factors
+ * with a shared denominator in position 1.
+ *
+ * First compute the shared denominator (lcm) and
+ * then scale the numerators to this shared denominator.
+ */
+static __isl_give isl_basic_set *add_vertex(__isl_take isl_basic_set *bset,
+ struct isl_coefficients_product_data *data)
+{
+ int i;
+ int k;
+ isl_int lcm, f;
+
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ return isl_basic_set_free(bset);
+
+ isl_int_init(lcm);
+ isl_int_init(f);
+ isl_int_set_si(lcm, 1);
+ for (i = 0; i < data->n; ++i) {
+ struct isl_coefficients_factor_data *factor = &data->factors[i];
+ isl_basic_set *coeff = factor->coeff;
+ int pos = factor->pos;
+ isl_int_lcm(lcm, lcm, coeff->ineq[pos][1]);
+ }
+ isl_int_set_si(bset->ineq[k][0], 0);
+ isl_int_set(bset->ineq[k][1], lcm);
+
+ for (i = 0; i < data->n; ++i) {
+ struct isl_coefficients_factor_data *factor = &data->factors[i];
+ isl_basic_set *coeff = factor->coeff;
+ int pos = factor->pos;
+ isl_int_divexact(f, lcm, coeff->ineq[pos][1]);
+ scale_factor(bset->ineq[k], coeff->ineq[pos], f, factor);
+ }
+
+ isl_int_clear(f);
+ isl_int_clear(lcm);
+
+ return bset;
+}
+
+/* Combine the duals of the factors in the factorization of a basic set
+ * to form the dual of the entire basic set.
+ * The dual share the coefficient of the constant term.
+ * All other coefficients are specific to a factor.
+ * Any constraint not involving the coefficient of the constant term
+ * can therefor simply be copied into the appropriate position.
+ * This includes all equality constraints since the coefficient
+ * of the constant term can always be increased and therefore
+ * never appears in an equality constraint.
+ * The inequality constraints involving the coefficient of
+ * the constant term need to be combined across factors.
+ * In particular, if this coefficient needs to be greater than or equal
+ * to some linear combination of the other coefficients in each factor,
+ * then it needs to be greater than or equal to the sum of
+ * these linear combinations across the factors.
+ *
+ * Alternatively, the constraints of the dual can be seen
+ * as the vertices, rays and lines of the original basic set.
+ * Clearly, rays and lines can simply be copied,
+ * while vertices needs to be combined across factors.
+ * This means that the number of rays and lines in the product
+ * is equal to the sum of the numbers in the factors,
+ * while the number of vertices is the product
+ * of the number of vertices in the factors. Note that each
+ * factor has at least one vertex.
+ * The only exception is when the factor is the dual of an obviously empty set,
+ * in which case a universe dual is created.
+ * In this case, return a universe dual for the product as well.
+ *
+ * While constructing the vertices, look for the first combination
+ * of inequality constraints that represent a vertex,
+ * construct the corresponding vertex and then move on
+ * to the next combination of inequality constraints until
+ * all combinations have been considered.
+ */
+static __isl_give isl_basic_set *construct_product(isl_ctx *ctx,
+ struct isl_coefficients_product_data *data)
+{
+ int i;
+ int n_line, n_ray, n_vertex;
+ int total;
+ isl_space *space;
+ isl_basic_set *product;
+
+ if (!data->factors)
+ return NULL;
+
+ total = data->start_next;
+
+ n_line = 0;
+ n_ray = 0;
+ n_vertex = 1;
+ for (i = 0; i < data->n; ++i) {
+ n_line += data->factors[i].n_line;
+ n_ray += data->factors[i].n_ray;
+ n_vertex *= data->factors[i].n_vertex;
+ }
+
+ space = isl_space_set_alloc(ctx, 0, 1 + total);
+ if (n_vertex == 0)
+ return rational_universe(space);
+ product = isl_basic_set_alloc_space(space, 0, n_line, n_ray + n_vertex);
+ product = isl_basic_set_set_rational(product);
+
+ for (i = 0; i < data->n; ++i)
+ product = add_lines(product, &data->factors[i], total);
+ for (i = 0; i < data->n; ++i)
+ product = add_rays(product, &data->factors[i], total);
+
+ first_vertex(data, 0);
+ do {
+ product = add_vertex(product, data);
+ } while (next_vertex(data));
+
+ return product;
+}
+
+/* Given a factorization "f" of a basic set,
+ * construct a basic set containing the tuples of coefficients of all
+ * valid affine constraints on the product of the factors, ignoring
+ * the space of input and output.
+ * Note that this product may not be equal to the original basic set,
+ * if a non-trivial transformation is involved.
+ * This is handled by the caller.
+ *
+ * Compute the tuples of coefficients for each factor separately and
+ * then combine the results.
+ */
+static __isl_give isl_basic_set *isl_basic_set_coefficients_product(
+ __isl_take isl_factorizer *f)
+{
+ struct isl_coefficients_product_data data;
+ isl_ctx *ctx;
+ isl_basic_set *coeff;
+ isl_bool every;
+
+ ctx = isl_factorizer_get_ctx(f);
+ if (isl_coefficients_product_data_init(ctx, &data, f->n_group) < 0)
+ f = isl_factorizer_free(f);
+ every = isl_factorizer_every_factor_basic_set(f,
+ &isl_basic_set_coefficients_factor, &data);
+ isl_factorizer_free(f);
+ if (every >= 0)
+ coeff = construct_product(ctx, &data);
+ else
+ coeff = NULL;
+ isl_coefficients_product_data_clear(&data);
+
+ return coeff;
+}
+
+/* Given a factorization "f" of a basic set,
+ * construct a basic set containing the tuples of coefficients of all
+ * valid affine constraints on the basic set, ignoring
+ * the space of input and output.
+ *
+ * The factorization may involve a linear transformation of the basic set.
+ * In particular, the transformed basic set is formulated
+ * in terms of x' = U x, i.e., x = V x', with V = U^{-1}.
+ * The dual is then computed in terms of y' with y'^t [z; x'] >= 0.
+ * Plugging in y' = [1 0; 0 V^t] y yields
+ * y^t [1 0; 0 V] [z; x'] >= 0, i.e., y^t [z; x] >= 0, which is
+ * the desired set of coefficients y.
+ * Note that this transformation to y' only needs to be applied
+ * if U is not the identity matrix.
+ */
+static __isl_give isl_basic_set *isl_basic_set_coefficients_morphed_product(
+ __isl_take isl_factorizer *f)
+{
+ isl_bool is_identity;
+ isl_space *space;
+ isl_mat *inv;
+ isl_multi_aff *ma;
+ isl_basic_set *coeff;
+
+ if (!f)
+ goto error;
+ is_identity = isl_mat_is_scaled_identity(peek_inv(f->morph));
+ if (is_identity < 0)
+ goto error;
+ if (is_identity)
+ return isl_basic_set_coefficients_product(f);
+
+ inv = get_inv(f->morph);
+ inv = isl_mat_transpose(inv);
+ inv = isl_mat_lin_to_aff(inv);
+
+ coeff = isl_basic_set_coefficients_product(f);
+ space = isl_space_map_from_set(isl_basic_set_get_space(coeff));
+ ma = isl_multi_aff_from_aff_mat(space, inv);
+ coeff = isl_basic_set_preimage_multi_aff(coeff, ma);
+
+ return coeff;
+error:
+ isl_factorizer_free(f);
+ return NULL;
+}
+
+/* Construct a basic set containing the tuples of coefficients of all
+ * valid affine constraints on the given basic set, ignoring
+ * the space of input and output.
+ *
+ * The caller has already checked that "bset" does not involve
+ * any local variables. It may have parameters, though.
+ * Treat them as regular variables internally.
+ * This is especially important for the factorization,
+ * since the (original) parameters should be taken into account
+ * explicitly in this factorization.
+ *
+ * Check if the basic set can be factorized.
+ * If so, compute constraints on the coefficients of the factors
+ * separately and combine the results.
+ * Otherwise, compute the results for the input basic set as a whole.
+ */
+static __isl_give isl_basic_set *basic_set_coefficients(
+ __isl_take isl_basic_set *bset)
+{
+ isl_factorizer *f;
+ isl_size nparam;
+
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ if (nparam < 0)
+ return isl_basic_set_free(bset);
+ bset = isl_basic_set_move_dims(bset, isl_dim_set, 0,
+ isl_dim_param, 0, nparam);
+
+ f = isl_basic_set_factorizer(bset);
+ if (!f)
+ return isl_basic_set_free(bset);
+ if (f->n_group > 0) {
+ isl_basic_set_free(bset);
+ return isl_basic_set_coefficients_morphed_product(f);
+ }
+ isl_factorizer_free(f);
+ return isl_basic_set_coefficients_base(bset);
+}
+
+/* Construct a basic set containing the tuples of coefficients of all
+ * valid affine constraints on the given basic set.
+ */
+__isl_give isl_basic_set *isl_basic_set_coefficients(
+ __isl_take isl_basic_set *bset)
+{
+ isl_space *space;
+
+ if (!bset)
+ return NULL;
+ if (bset->n_div)
+ isl_die(bset->ctx, isl_error_invalid,
+ "input set not allowed to have local variables",
+ goto error);
+
+ space = isl_basic_set_get_space(bset);
+ space = isl_space_coefficients(space);
+
+ bset = basic_set_coefficients(bset);
+ bset = isl_basic_set_reset_space(bset, space);
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Construct a basic set containing the elements that satisfy all
+ * affine constraints whose coefficient tuples are
+ * contained in the given basic set.
+ */
+__isl_give isl_basic_set *isl_basic_set_solutions(
+ __isl_take isl_basic_set *bset)
+{
+ isl_space *space;
+
+ if (!bset)
+ return NULL;
+ if (bset->n_div)
+ isl_die(bset->ctx, isl_error_invalid,
+ "input set not allowed to have local variables",
+ goto error);
+
+ space = isl_basic_set_get_space(bset);
+ space = isl_space_solutions(space);
+
+ bset = farkas(bset, -1);
+ bset = isl_basic_set_reset_space(bset, space);
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Construct a basic set containing the tuples of coefficients of all
+ * valid affine constraints on the given set.
+ */
+__isl_give isl_basic_set *isl_set_coefficients(__isl_take isl_set *set)
+{
+ int i;
+ isl_basic_set *coeff;
+
+ if (!set)
+ return NULL;
+ if (set->n == 0) {
+ isl_space *space = isl_set_get_space(set);
+ space = isl_space_coefficients(space);
+ isl_set_free(set);
+ return rational_universe(space);
+ }
+
+ coeff = isl_basic_set_coefficients(isl_basic_set_copy(set->p[0]));
+
+ for (i = 1; i < set->n; ++i) {
+ isl_basic_set *bset, *coeff_i;
+ bset = isl_basic_set_copy(set->p[i]);
+ coeff_i = isl_basic_set_coefficients(bset);
+ coeff = isl_basic_set_intersect(coeff, coeff_i);
+ }
+
+ isl_set_free(set);
+ return coeff;
+}
+
+/* Wrapper around isl_basic_set_coefficients for use
+ * as a isl_basic_set_list_map callback.
+ */
+static __isl_give isl_basic_set *coefficients_wrap(
+ __isl_take isl_basic_set *bset, void *user)
+{
+ return isl_basic_set_coefficients(bset);
+}
+
+/* Replace the elements of "list" by the result of applying
+ * isl_basic_set_coefficients to them.
+ */
+__isl_give isl_basic_set_list *isl_basic_set_list_coefficients(
+ __isl_take isl_basic_set_list *list)
+{
+ return isl_basic_set_list_map(list, &coefficients_wrap, NULL);
+}
+
+/* Construct a basic set containing the elements that satisfy all
+ * affine constraints whose coefficient tuples are
+ * contained in the given set.
+ */
+__isl_give isl_basic_set *isl_set_solutions(__isl_take isl_set *set)
+{
+ int i;
+ isl_basic_set *sol;
+
+ if (!set)
+ return NULL;
+ if (set->n == 0) {
+ isl_space *space = isl_set_get_space(set);
+ space = isl_space_solutions(space);
+ isl_set_free(set);
+ return rational_universe(space);
+ }
+
+ sol = isl_basic_set_solutions(isl_basic_set_copy(set->p[0]));
+
+ for (i = 1; i < set->n; ++i) {
+ isl_basic_set *bset, *sol_i;
+ bset = isl_basic_set_copy(set->p[i]);
+ sol_i = isl_basic_set_solutions(bset);
+ sol = isl_basic_set_intersect(sol, sol_i);
+ }
+
+ isl_set_free(set);
+ return sol;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ffs.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ffs.c
new file mode 100644
index 00000000000..c1ee928f8b8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ffs.c
@@ -0,0 +1,24 @@
+#include <isl_config.h>
+
+#if !HAVE_DECL_FFS && !HAVE_DECL___BUILTIN_FFS && HAVE_DECL__BITSCANFORWARD
+#include <intrin.h>
+
+/* Implementation of ffs in terms of _BitScanForward.
+ *
+ * ffs returns the position of the least significant bit set in i,
+ * with the least significant bit is position 1, or 0 if not bits are set.
+ *
+ * _BitScanForward returns 1 if mask is non-zero and sets index
+ * to the position of the least significant bit set in i,
+ * with the least significant bit is position 0.
+ */
+int isl_ffs(int i)
+{
+ unsigned char non_zero;
+ unsigned long index, mask = i;
+
+ non_zero = _BitScanForward(&index, mask);
+
+ return non_zero ? 1 + index : 0;
+}
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_flow.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_flow.c
new file mode 100644
index 00000000000..3be3ad500f1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_flow.c
@@ -0,0 +1,3340 @@
+/*
+ * Copyright 2005-2007 Universiteit Leiden
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2012 Universiteit Leiden
+ * Copyright 2014 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, Leiden Institute of Advanced Computer Science,
+ * Universiteit Leiden, Niels Bohrweg 1, 2333 CA Leiden, The Netherlands
+ * and K.U.Leuven, Departement Computerwetenschappen, Celestijnenlaan 200A,
+ * B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/val.h>
+#include <isl/space.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl/flow.h>
+#include <isl/schedule_node.h>
+#include <isl_sort.h>
+#include <isl/stream.h>
+
+enum isl_restriction_type {
+ isl_restriction_type_empty,
+ isl_restriction_type_none,
+ isl_restriction_type_input,
+ isl_restriction_type_output
+};
+
+struct isl_restriction {
+ enum isl_restriction_type type;
+
+ isl_set *source;
+ isl_set *sink;
+};
+
+/* Create a restriction of the given type.
+ */
+static __isl_give isl_restriction *isl_restriction_alloc(
+ __isl_take isl_map *source_map, enum isl_restriction_type type)
+{
+ isl_ctx *ctx;
+ isl_restriction *restr;
+
+ if (!source_map)
+ return NULL;
+
+ ctx = isl_map_get_ctx(source_map);
+ restr = isl_calloc_type(ctx, struct isl_restriction);
+ if (!restr)
+ goto error;
+
+ restr->type = type;
+
+ isl_map_free(source_map);
+ return restr;
+error:
+ isl_map_free(source_map);
+ return NULL;
+}
+
+/* Create a restriction that doesn't restrict anything.
+ */
+__isl_give isl_restriction *isl_restriction_none(__isl_take isl_map *source_map)
+{
+ return isl_restriction_alloc(source_map, isl_restriction_type_none);
+}
+
+/* Create a restriction that removes everything.
+ */
+__isl_give isl_restriction *isl_restriction_empty(
+ __isl_take isl_map *source_map)
+{
+ return isl_restriction_alloc(source_map, isl_restriction_type_empty);
+}
+
+/* Create a restriction on the input of the maximization problem
+ * based on the given source and sink restrictions.
+ */
+__isl_give isl_restriction *isl_restriction_input(
+ __isl_take isl_set *source_restr, __isl_take isl_set *sink_restr)
+{
+ isl_ctx *ctx;
+ isl_restriction *restr;
+
+ if (!source_restr || !sink_restr)
+ goto error;
+
+ ctx = isl_set_get_ctx(source_restr);
+ restr = isl_calloc_type(ctx, struct isl_restriction);
+ if (!restr)
+ goto error;
+
+ restr->type = isl_restriction_type_input;
+ restr->source = source_restr;
+ restr->sink = sink_restr;
+
+ return restr;
+error:
+ isl_set_free(source_restr);
+ isl_set_free(sink_restr);
+ return NULL;
+}
+
+/* Create a restriction on the output of the maximization problem
+ * based on the given source restriction.
+ */
+__isl_give isl_restriction *isl_restriction_output(
+ __isl_take isl_set *source_restr)
+{
+ isl_ctx *ctx;
+ isl_restriction *restr;
+
+ if (!source_restr)
+ return NULL;
+
+ ctx = isl_set_get_ctx(source_restr);
+ restr = isl_calloc_type(ctx, struct isl_restriction);
+ if (!restr)
+ goto error;
+
+ restr->type = isl_restriction_type_output;
+ restr->source = source_restr;
+
+ return restr;
+error:
+ isl_set_free(source_restr);
+ return NULL;
+}
+
+__isl_null isl_restriction *isl_restriction_free(
+ __isl_take isl_restriction *restr)
+{
+ if (!restr)
+ return NULL;
+
+ isl_set_free(restr->source);
+ isl_set_free(restr->sink);
+ free(restr);
+ return NULL;
+}
+
+isl_ctx *isl_restriction_get_ctx(__isl_keep isl_restriction *restr)
+{
+ return restr ? isl_set_get_ctx(restr->source) : NULL;
+}
+
+/* A private structure to keep track of a mapping together with
+ * a user-specified identifier and a boolean indicating whether
+ * the map represents a must or may access/dependence.
+ */
+struct isl_labeled_map {
+ struct isl_map *map;
+ void *data;
+ int must;
+};
+
+typedef isl_bool (*isl_access_coscheduled)(void *first, void *second);
+
+/* A structure containing the input for dependence analysis:
+ * - a sink
+ * - n_must + n_may (<= max_source) sources
+ * - a function for determining the relative order of sources and sink
+ * - an optional function "coscheduled" for determining whether sources
+ * may be coscheduled. If "coscheduled" is NULL, then the sources
+ * are assumed not to be coscheduled.
+ * The must sources are placed before the may sources.
+ *
+ * domain_map is an auxiliary map that maps the sink access relation
+ * to the domain of this access relation.
+ * This field is only needed when restrict_fn is set and
+ * the field itself is set by isl_access_info_compute_flow.
+ *
+ * restrict_fn is a callback that (if not NULL) will be called
+ * right before any lexicographical maximization.
+ */
+struct isl_access_info {
+ isl_map *domain_map;
+ struct isl_labeled_map sink;
+ isl_access_level_before level_before;
+ isl_access_coscheduled coscheduled;
+
+ isl_access_restrict restrict_fn;
+ void *restrict_user;
+
+ int max_source;
+ int n_must;
+ int n_may;
+ struct isl_labeled_map source[1];
+};
+
+/* A structure containing the output of dependence analysis:
+ * - n_source dependences
+ * - a wrapped subset of the sink for which definitely no source could be found
+ * - a wrapped subset of the sink for which possibly no source could be found
+ */
+struct isl_flow {
+ isl_set *must_no_source;
+ isl_set *may_no_source;
+ int n_source;
+ struct isl_labeled_map *dep;
+};
+
+/* Construct an isl_access_info structure and fill it up with
+ * the given data. The number of sources is set to 0.
+ */
+__isl_give isl_access_info *isl_access_info_alloc(__isl_take isl_map *sink,
+ void *sink_user, isl_access_level_before fn, int max_source)
+{
+ isl_ctx *ctx;
+ struct isl_access_info *acc;
+
+ if (!sink)
+ return NULL;
+
+ ctx = isl_map_get_ctx(sink);
+ isl_assert(ctx, max_source >= 0, goto error);
+
+ acc = isl_calloc(ctx, struct isl_access_info,
+ sizeof(struct isl_access_info) +
+ (max_source - 1) * sizeof(struct isl_labeled_map));
+ if (!acc)
+ goto error;
+
+ acc->sink.map = sink;
+ acc->sink.data = sink_user;
+ acc->level_before = fn;
+ acc->max_source = max_source;
+ acc->n_must = 0;
+ acc->n_may = 0;
+
+ return acc;
+error:
+ isl_map_free(sink);
+ return NULL;
+}
+
+/* Free the given isl_access_info structure.
+ */
+__isl_null isl_access_info *isl_access_info_free(
+ __isl_take isl_access_info *acc)
+{
+ int i;
+
+ if (!acc)
+ return NULL;
+ isl_map_free(acc->domain_map);
+ isl_map_free(acc->sink.map);
+ for (i = 0; i < acc->n_must + acc->n_may; ++i)
+ isl_map_free(acc->source[i].map);
+ free(acc);
+ return NULL;
+}
+
+isl_ctx *isl_access_info_get_ctx(__isl_keep isl_access_info *acc)
+{
+ return acc ? isl_map_get_ctx(acc->sink.map) : NULL;
+}
+
+__isl_give isl_access_info *isl_access_info_set_restrict(
+ __isl_take isl_access_info *acc, isl_access_restrict fn, void *user)
+{
+ if (!acc)
+ return NULL;
+ acc->restrict_fn = fn;
+ acc->restrict_user = user;
+ return acc;
+}
+
+/* Add another source to an isl_access_info structure, making
+ * sure the "must" sources are placed before the "may" sources.
+ * This function may be called at most max_source times on a
+ * given isl_access_info structure, with max_source as specified
+ * in the call to isl_access_info_alloc that constructed the structure.
+ */
+__isl_give isl_access_info *isl_access_info_add_source(
+ __isl_take isl_access_info *acc, __isl_take isl_map *source,
+ int must, void *source_user)
+{
+ isl_ctx *ctx;
+
+ if (!acc)
+ goto error;
+ ctx = isl_map_get_ctx(acc->sink.map);
+ isl_assert(ctx, acc->n_must + acc->n_may < acc->max_source, goto error);
+
+ if (must) {
+ if (acc->n_may)
+ acc->source[acc->n_must + acc->n_may] =
+ acc->source[acc->n_must];
+ acc->source[acc->n_must].map = source;
+ acc->source[acc->n_must].data = source_user;
+ acc->source[acc->n_must].must = 1;
+ acc->n_must++;
+ } else {
+ acc->source[acc->n_must + acc->n_may].map = source;
+ acc->source[acc->n_must + acc->n_may].data = source_user;
+ acc->source[acc->n_must + acc->n_may].must = 0;
+ acc->n_may++;
+ }
+
+ return acc;
+error:
+ isl_map_free(source);
+ isl_access_info_free(acc);
+ return NULL;
+}
+
+/* A helper struct carrying the isl_access_info and an error condition.
+ */
+struct access_sort_info {
+ isl_access_info *access_info;
+ int error;
+};
+
+/* Return -n, 0 or n (with n a positive value), depending on whether
+ * the source access identified by p1 should be sorted before, together
+ * or after that identified by p2.
+ *
+ * If p1 appears before p2, then it should be sorted first.
+ * For more generic initial schedules, it is possible that neither
+ * p1 nor p2 appears before the other, or at least not in any obvious way.
+ * We therefore also check if p2 appears before p1, in which case p2
+ * should be sorted first.
+ * If not, we try to order the two statements based on the description
+ * of the iteration domains. This results in an arbitrary, but fairly
+ * stable ordering.
+ *
+ * In case of an error, sort_info.error is set to true and all elements are
+ * reported to be equal.
+ */
+static int access_sort_cmp(const void *p1, const void *p2, void *user)
+{
+ struct access_sort_info *sort_info = user;
+ isl_access_info *acc = sort_info->access_info;
+
+ if (sort_info->error)
+ return 0;
+
+ const struct isl_labeled_map *i1, *i2;
+ int level1, level2;
+ uint32_t h1, h2;
+ i1 = (const struct isl_labeled_map *) p1;
+ i2 = (const struct isl_labeled_map *) p2;
+
+ level1 = acc->level_before(i1->data, i2->data);
+ if (level1 < 0)
+ goto error;
+ if (level1 % 2)
+ return -1;
+
+ level2 = acc->level_before(i2->data, i1->data);
+ if (level2 < 0)
+ goto error;
+ if (level2 % 2)
+ return 1;
+
+ h1 = isl_map_get_hash(i1->map);
+ h2 = isl_map_get_hash(i2->map);
+ return h1 > h2 ? 1 : h1 < h2 ? -1 : 0;
+error:
+ sort_info->error = 1;
+ return 0;
+}
+
+/* Sort the must source accesses in their textual order.
+ */
+static __isl_give isl_access_info *isl_access_info_sort_sources(
+ __isl_take isl_access_info *acc)
+{
+ struct access_sort_info sort_info;
+
+ sort_info.access_info = acc;
+ sort_info.error = 0;
+
+ if (!acc)
+ return NULL;
+ if (acc->n_must <= 1)
+ return acc;
+
+ if (isl_sort(acc->source, acc->n_must, sizeof(struct isl_labeled_map),
+ access_sort_cmp, &sort_info) < 0)
+ return isl_access_info_free(acc);
+ if (sort_info.error)
+ return isl_access_info_free(acc);
+
+ return acc;
+}
+
+/* Align the parameters of the two spaces if needed and then call
+ * isl_space_join.
+ */
+static __isl_give isl_space *space_align_and_join(__isl_take isl_space *left,
+ __isl_take isl_space *right)
+{
+ isl_bool equal_params;
+
+ equal_params = isl_space_has_equal_params(left, right);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params)
+ return isl_space_join(left, right);
+
+ left = isl_space_align_params(left, isl_space_copy(right));
+ right = isl_space_align_params(right, isl_space_copy(left));
+ return isl_space_join(left, right);
+error:
+ isl_space_free(left);
+ isl_space_free(right);
+ return NULL;
+}
+
+/* Initialize an empty isl_flow structure corresponding to a given
+ * isl_access_info structure.
+ * For each must access, two dependences are created (initialized
+ * to the empty relation), one for the resulting must dependences
+ * and one for the resulting may dependences. May accesses can
+ * only lead to may dependences, so only one dependence is created
+ * for each of them.
+ * This function is private as isl_flow structures are only supposed
+ * to be created by isl_access_info_compute_flow.
+ */
+static __isl_give isl_flow *isl_flow_alloc(__isl_keep isl_access_info *acc)
+{
+ int i, n;
+ struct isl_ctx *ctx;
+ struct isl_flow *dep;
+
+ if (!acc)
+ return NULL;
+
+ ctx = isl_map_get_ctx(acc->sink.map);
+ dep = isl_calloc_type(ctx, struct isl_flow);
+ if (!dep)
+ return NULL;
+
+ n = 2 * acc->n_must + acc->n_may;
+ dep->dep = isl_calloc_array(ctx, struct isl_labeled_map, n);
+ if (n && !dep->dep)
+ goto error;
+
+ dep->n_source = n;
+ for (i = 0; i < acc->n_must; ++i) {
+ isl_space *space;
+ space = space_align_and_join(
+ isl_map_get_space(acc->source[i].map),
+ isl_space_reverse(isl_map_get_space(acc->sink.map)));
+ dep->dep[2 * i].map = isl_map_empty(space);
+ dep->dep[2 * i + 1].map = isl_map_copy(dep->dep[2 * i].map);
+ dep->dep[2 * i].data = acc->source[i].data;
+ dep->dep[2 * i + 1].data = acc->source[i].data;
+ dep->dep[2 * i].must = 1;
+ dep->dep[2 * i + 1].must = 0;
+ if (!dep->dep[2 * i].map || !dep->dep[2 * i + 1].map)
+ goto error;
+ }
+ for (i = acc->n_must; i < acc->n_must + acc->n_may; ++i) {
+ isl_space *space;
+ space = space_align_and_join(
+ isl_map_get_space(acc->source[i].map),
+ isl_space_reverse(isl_map_get_space(acc->sink.map)));
+ dep->dep[acc->n_must + i].map = isl_map_empty(space);
+ dep->dep[acc->n_must + i].data = acc->source[i].data;
+ dep->dep[acc->n_must + i].must = 0;
+ if (!dep->dep[acc->n_must + i].map)
+ goto error;
+ }
+
+ return dep;
+error:
+ isl_flow_free(dep);
+ return NULL;
+}
+
+/* Iterate over all sources and for each resulting flow dependence
+ * that is not empty, call the user specfied function.
+ * The second argument in this function call identifies the source,
+ * while the third argument correspond to the final argument of
+ * the isl_flow_foreach call.
+ */
+isl_stat isl_flow_foreach(__isl_keep isl_flow *deps,
+ isl_stat (*fn)(__isl_take isl_map *dep, int must, void *dep_user,
+ void *user),
+ void *user)
+{
+ int i;
+
+ if (!deps)
+ return isl_stat_error;
+
+ for (i = 0; i < deps->n_source; ++i) {
+ if (isl_map_plain_is_empty(deps->dep[i].map))
+ continue;
+ if (fn(isl_map_copy(deps->dep[i].map), deps->dep[i].must,
+ deps->dep[i].data, user) < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Return a copy of the subset of the sink for which no source could be found.
+ */
+__isl_give isl_map *isl_flow_get_no_source(__isl_keep isl_flow *deps, int must)
+{
+ if (!deps)
+ return NULL;
+
+ if (must)
+ return isl_set_unwrap(isl_set_copy(deps->must_no_source));
+ else
+ return isl_set_unwrap(isl_set_copy(deps->may_no_source));
+}
+
+__isl_null isl_flow *isl_flow_free(__isl_take isl_flow *deps)
+{
+ int i;
+
+ if (!deps)
+ return NULL;
+ isl_set_free(deps->must_no_source);
+ isl_set_free(deps->may_no_source);
+ if (deps->dep) {
+ for (i = 0; i < deps->n_source; ++i)
+ isl_map_free(deps->dep[i].map);
+ free(deps->dep);
+ }
+ free(deps);
+
+ return NULL;
+}
+
+isl_ctx *isl_flow_get_ctx(__isl_keep isl_flow *deps)
+{
+ return deps ? isl_set_get_ctx(deps->must_no_source) : NULL;
+}
+
+/* Return a map that enforces that the domain iteration occurs after
+ * the range iteration at the given level.
+ * If level is odd, then the domain iteration should occur after
+ * the target iteration in their shared level/2 outermost loops.
+ * In this case we simply need to enforce that these outermost
+ * loop iterations are the same.
+ * If level is even, then the loop iterator of the domain should
+ * be greater than the loop iterator of the range at the last
+ * of the level/2 shared loops, i.e., loop level/2 - 1.
+ */
+static __isl_give isl_map *after_at_level(__isl_take isl_space *space,
+ int level)
+{
+ struct isl_basic_map *bmap;
+
+ if (level % 2)
+ bmap = isl_basic_map_equal(space, level/2);
+ else
+ bmap = isl_basic_map_more_at(space, level/2 - 1);
+
+ return isl_map_from_basic_map(bmap);
+}
+
+/* Compute the partial lexicographic maximum of "dep" on domain "sink",
+ * but first check if the user has set acc->restrict_fn and if so
+ * update either the input or the output of the maximization problem
+ * with respect to the resulting restriction.
+ *
+ * Since the user expects a mapping from sink iterations to source iterations,
+ * whereas the domain of "dep" is a wrapped map, mapping sink iterations
+ * to accessed array elements, we first need to project out the accessed
+ * sink array elements by applying acc->domain_map.
+ * Similarly, the sink restriction specified by the user needs to be
+ * converted back to the wrapped map.
+ */
+static __isl_give isl_map *restricted_partial_lexmax(
+ __isl_keep isl_access_info *acc, __isl_take isl_map *dep,
+ int source, __isl_take isl_set *sink, __isl_give isl_set **empty)
+{
+ isl_map *source_map;
+ isl_restriction *restr;
+ isl_set *sink_domain;
+ isl_set *sink_restr;
+ isl_map *res;
+
+ if (!acc->restrict_fn)
+ return isl_map_partial_lexmax(dep, sink, empty);
+
+ source_map = isl_map_copy(dep);
+ source_map = isl_map_apply_domain(source_map,
+ isl_map_copy(acc->domain_map));
+ sink_domain = isl_set_copy(sink);
+ sink_domain = isl_set_apply(sink_domain, isl_map_copy(acc->domain_map));
+ restr = acc->restrict_fn(source_map, sink_domain,
+ acc->source[source].data, acc->restrict_user);
+ isl_set_free(sink_domain);
+ isl_map_free(source_map);
+
+ if (!restr)
+ goto error;
+ if (restr->type == isl_restriction_type_input) {
+ dep = isl_map_intersect_range(dep, isl_set_copy(restr->source));
+ sink_restr = isl_set_copy(restr->sink);
+ sink_restr = isl_set_apply(sink_restr,
+ isl_map_reverse(isl_map_copy(acc->domain_map)));
+ sink = isl_set_intersect(sink, sink_restr);
+ } else if (restr->type == isl_restriction_type_empty) {
+ isl_space *space = isl_map_get_space(dep);
+ isl_map_free(dep);
+ dep = isl_map_empty(space);
+ }
+
+ res = isl_map_partial_lexmax(dep, sink, empty);
+
+ if (restr->type == isl_restriction_type_output)
+ res = isl_map_intersect_range(res, isl_set_copy(restr->source));
+
+ isl_restriction_free(restr);
+ return res;
+error:
+ isl_map_free(dep);
+ isl_set_free(sink);
+ *empty = NULL;
+ return NULL;
+}
+
+/* Compute the last iteration of must source j that precedes the sink
+ * at the given level for sink iterations in set_C.
+ * The subset of set_C for which no such iteration can be found is returned
+ * in *empty.
+ */
+static struct isl_map *last_source(struct isl_access_info *acc,
+ struct isl_set *set_C,
+ int j, int level, struct isl_set **empty)
+{
+ struct isl_map *read_map;
+ struct isl_map *write_map;
+ struct isl_map *dep_map;
+ struct isl_map *after;
+ struct isl_map *result;
+
+ read_map = isl_map_copy(acc->sink.map);
+ write_map = isl_map_copy(acc->source[j].map);
+ write_map = isl_map_reverse(write_map);
+ dep_map = isl_map_apply_range(read_map, write_map);
+ after = after_at_level(isl_map_get_space(dep_map), level);
+ dep_map = isl_map_intersect(dep_map, after);
+ result = restricted_partial_lexmax(acc, dep_map, j, set_C, empty);
+ result = isl_map_reverse(result);
+
+ return result;
+}
+
+/* For a given mapping between iterations of must source j and iterations
+ * of the sink, compute the last iteration of must source k preceding
+ * the sink at level before_level for any of the sink iterations,
+ * but following the corresponding iteration of must source j at level
+ * after_level.
+ */
+static struct isl_map *last_later_source(struct isl_access_info *acc,
+ struct isl_map *old_map,
+ int j, int before_level,
+ int k, int after_level,
+ struct isl_set **empty)
+{
+ isl_space *space;
+ struct isl_set *set_C;
+ struct isl_map *read_map;
+ struct isl_map *write_map;
+ struct isl_map *dep_map;
+ struct isl_map *after_write;
+ struct isl_map *before_read;
+ struct isl_map *result;
+
+ set_C = isl_map_range(isl_map_copy(old_map));
+ read_map = isl_map_copy(acc->sink.map);
+ write_map = isl_map_copy(acc->source[k].map);
+
+ write_map = isl_map_reverse(write_map);
+ dep_map = isl_map_apply_range(read_map, write_map);
+ space = space_align_and_join(isl_map_get_space(acc->source[k].map),
+ isl_space_reverse(isl_map_get_space(acc->source[j].map)));
+ after_write = after_at_level(space, after_level);
+ after_write = isl_map_apply_range(after_write, old_map);
+ after_write = isl_map_reverse(after_write);
+ dep_map = isl_map_intersect(dep_map, after_write);
+ before_read = after_at_level(isl_map_get_space(dep_map), before_level);
+ dep_map = isl_map_intersect(dep_map, before_read);
+ result = restricted_partial_lexmax(acc, dep_map, k, set_C, empty);
+ result = isl_map_reverse(result);
+
+ return result;
+}
+
+/* Given a shared_level between two accesses, return 1 if the
+ * the first can precede the second at the requested target_level.
+ * If the target level is odd, i.e., refers to a statement level
+ * dimension, then first needs to precede second at the requested
+ * level, i.e., shared_level must be equal to target_level.
+ * If the target level is odd, then the two loops should share
+ * at least the requested number of outer loops.
+ */
+static int can_precede_at_level(int shared_level, int target_level)
+{
+ if (shared_level < target_level)
+ return 0;
+ if ((target_level % 2) && shared_level > target_level)
+ return 0;
+ return 1;
+}
+
+/* Given a possible flow dependence temp_rel[j] between source j and the sink
+ * at level sink_level, remove those elements for which
+ * there is an iteration of another source k < j that is closer to the sink.
+ * The flow dependences temp_rel[k] are updated with the improved sources.
+ * Any improved source needs to precede the sink at the same level
+ * and needs to follow source j at the same or a deeper level.
+ * The lower this level, the later the execution date of source k.
+ * We therefore consider lower levels first.
+ *
+ * If temp_rel[j] is empty, then there can be no improvement and
+ * we return immediately.
+ *
+ * This function returns isl_stat_ok in case it was executed successfully and
+ * isl_stat_error in case of errors during the execution of this function.
+ */
+static isl_stat intermediate_sources(__isl_keep isl_access_info *acc,
+ struct isl_map **temp_rel, int j, int sink_level)
+{
+ int k, level;
+ isl_size n_in = isl_map_dim(acc->source[j].map, isl_dim_in);
+ int depth = 2 * n_in + 1;
+
+ if (n_in < 0)
+ return isl_stat_error;
+ if (isl_map_plain_is_empty(temp_rel[j]))
+ return isl_stat_ok;
+
+ for (k = j - 1; k >= 0; --k) {
+ int plevel, plevel2;
+ plevel = acc->level_before(acc->source[k].data, acc->sink.data);
+ if (plevel < 0)
+ return isl_stat_error;
+ if (!can_precede_at_level(plevel, sink_level))
+ continue;
+
+ plevel2 = acc->level_before(acc->source[j].data,
+ acc->source[k].data);
+ if (plevel2 < 0)
+ return isl_stat_error;
+
+ for (level = sink_level; level <= depth; ++level) {
+ struct isl_map *T;
+ struct isl_set *trest;
+ struct isl_map *copy;
+
+ if (!can_precede_at_level(plevel2, level))
+ continue;
+
+ copy = isl_map_copy(temp_rel[j]);
+ T = last_later_source(acc, copy, j, sink_level, k,
+ level, &trest);
+ if (isl_map_plain_is_empty(T)) {
+ isl_set_free(trest);
+ isl_map_free(T);
+ continue;
+ }
+ temp_rel[j] = isl_map_intersect_range(temp_rel[j], trest);
+ temp_rel[k] = isl_map_union_disjoint(temp_rel[k], T);
+ }
+ }
+
+ return isl_stat_ok;
+}
+
+/* Compute all iterations of may source j that precedes the sink at the given
+ * level for sink iterations in set_C.
+ */
+static __isl_give isl_map *all_sources(__isl_keep isl_access_info *acc,
+ __isl_take isl_set *set_C, int j, int level)
+{
+ isl_map *read_map;
+ isl_map *write_map;
+ isl_map *dep_map;
+ isl_map *after;
+
+ read_map = isl_map_copy(acc->sink.map);
+ read_map = isl_map_intersect_domain(read_map, set_C);
+ write_map = isl_map_copy(acc->source[acc->n_must + j].map);
+ write_map = isl_map_reverse(write_map);
+ dep_map = isl_map_apply_range(read_map, write_map);
+ after = after_at_level(isl_map_get_space(dep_map), level);
+ dep_map = isl_map_intersect(dep_map, after);
+
+ return isl_map_reverse(dep_map);
+}
+
+/* For a given mapping between iterations of must source k and iterations
+ * of the sink, compute all iterations of may source j preceding
+ * the sink at level before_level for any of the sink iterations,
+ * but following the corresponding iteration of must source k at level
+ * after_level.
+ */
+static __isl_give isl_map *all_later_sources(__isl_keep isl_access_info *acc,
+ __isl_take isl_map *old_map,
+ int j, int before_level, int k, int after_level)
+{
+ isl_space *space;
+ isl_set *set_C;
+ isl_map *read_map;
+ isl_map *write_map;
+ isl_map *dep_map;
+ isl_map *after_write;
+ isl_map *before_read;
+
+ set_C = isl_map_range(isl_map_copy(old_map));
+ read_map = isl_map_copy(acc->sink.map);
+ read_map = isl_map_intersect_domain(read_map, set_C);
+ write_map = isl_map_copy(acc->source[acc->n_must + j].map);
+
+ write_map = isl_map_reverse(write_map);
+ dep_map = isl_map_apply_range(read_map, write_map);
+ space = isl_space_join(isl_map_get_space(
+ acc->source[acc->n_must + j].map),
+ isl_space_reverse(isl_map_get_space(acc->source[k].map)));
+ after_write = after_at_level(space, after_level);
+ after_write = isl_map_apply_range(after_write, old_map);
+ after_write = isl_map_reverse(after_write);
+ dep_map = isl_map_intersect(dep_map, after_write);
+ before_read = after_at_level(isl_map_get_space(dep_map), before_level);
+ dep_map = isl_map_intersect(dep_map, before_read);
+ return isl_map_reverse(dep_map);
+}
+
+/* Given the must and may dependence relations for the must accesses
+ * for level sink_level, check if there are any accesses of may access j
+ * that occur in between and return their union.
+ * If some of these accesses are intermediate with respect to
+ * (previously thought to be) must dependences, then these
+ * must dependences are turned into may dependences.
+ */
+static __isl_give isl_map *all_intermediate_sources(
+ __isl_keep isl_access_info *acc, __isl_take isl_map *map,
+ struct isl_map **must_rel, struct isl_map **may_rel,
+ int j, int sink_level)
+{
+ int k, level;
+ isl_size n_in = isl_map_dim(acc->source[acc->n_must + j].map,
+ isl_dim_in);
+ int depth = 2 * n_in + 1;
+
+ if (n_in < 0)
+ return isl_map_free(map);
+ for (k = 0; k < acc->n_must; ++k) {
+ int plevel;
+
+ if (isl_map_plain_is_empty(may_rel[k]) &&
+ isl_map_plain_is_empty(must_rel[k]))
+ continue;
+
+ plevel = acc->level_before(acc->source[k].data,
+ acc->source[acc->n_must + j].data);
+ if (plevel < 0)
+ return isl_map_free(map);
+
+ for (level = sink_level; level <= depth; ++level) {
+ isl_map *T;
+ isl_map *copy;
+ isl_set *ran;
+
+ if (!can_precede_at_level(plevel, level))
+ continue;
+
+ copy = isl_map_copy(may_rel[k]);
+ T = all_later_sources(acc, copy, j, sink_level, k, level);
+ map = isl_map_union(map, T);
+
+ copy = isl_map_copy(must_rel[k]);
+ T = all_later_sources(acc, copy, j, sink_level, k, level);
+ ran = isl_map_range(isl_map_copy(T));
+ map = isl_map_union(map, T);
+ may_rel[k] = isl_map_union_disjoint(may_rel[k],
+ isl_map_intersect_range(isl_map_copy(must_rel[k]),
+ isl_set_copy(ran)));
+ T = isl_map_from_domain_and_range(
+ isl_set_universe(
+ isl_space_domain(isl_map_get_space(must_rel[k]))),
+ ran);
+ must_rel[k] = isl_map_subtract(must_rel[k], T);
+ }
+ }
+
+ return map;
+}
+
+/* Given a dependence relation "old_map" between a must-source and the sink,
+ * return a subset of the dependences, augmented with instances
+ * of the source at position "pos" in "acc" that are coscheduled
+ * with the must-source and that access the same element.
+ * That is, if the input lives in a space T -> K, then the output
+ * lives in the space [T -> S] -> K, with S the space of source "pos", and
+ * the domain factor of the domain product is a subset of the input.
+ * The sources are considered to be coscheduled if they have the same values
+ * for the initial "depth" coordinates.
+ *
+ * First construct a dependence relation S -> K and a mapping
+ * between coscheduled sources T -> S.
+ * The second is combined with the original dependence relation T -> K
+ * to form a relation in T -> [S -> K], which is subsequently
+ * uncurried to [T -> S] -> K.
+ * This result is then intersected with the dependence relation S -> K
+ * to form the output.
+ *
+ * In case a negative depth is given, NULL is returned to indicate an error.
+ */
+static __isl_give isl_map *coscheduled_source(__isl_keep isl_access_info *acc,
+ __isl_keep isl_map *old_map, int pos, int depth)
+{
+ isl_space *space;
+ isl_set *set_C;
+ isl_map *read_map;
+ isl_map *write_map;
+ isl_map *dep_map;
+ isl_map *equal;
+ isl_map *map;
+
+ if (depth < 0)
+ return NULL;
+
+ set_C = isl_map_range(isl_map_copy(old_map));
+ read_map = isl_map_copy(acc->sink.map);
+ read_map = isl_map_intersect_domain(read_map, set_C);
+ write_map = isl_map_copy(acc->source[pos].map);
+ dep_map = isl_map_domain_product(write_map, read_map);
+ dep_map = isl_set_unwrap(isl_map_domain(dep_map));
+ space = isl_space_join(isl_map_get_space(old_map),
+ isl_space_reverse(isl_map_get_space(dep_map)));
+ equal = isl_map_from_basic_map(isl_basic_map_equal(space, depth));
+ map = isl_map_range_product(equal, isl_map_copy(old_map));
+ map = isl_map_uncurry(map);
+ map = isl_map_intersect_domain_factor_range(map, dep_map);
+
+ return map;
+}
+
+/* After the dependences derived from a must-source have been computed
+ * at a certain level, check if any of the sources of the must-dependences
+ * may be coscheduled with other sources.
+ * If they are any such sources, then there is no way of determining
+ * which of the sources actually comes last and the must-dependences
+ * need to be turned into may-dependences, while dependences from
+ * the other sources need to be added to the may-dependences as well.
+ * "acc" describes the sources and a callback for checking whether
+ * two sources may be coscheduled. If acc->coscheduled is NULL then
+ * the sources are assumed not to be coscheduled.
+ * "must_rel" and "may_rel" describe the must and may-dependence relations
+ * computed at the current level for the must-sources. Some of the dependences
+ * may be moved from "must_rel" to "may_rel".
+ * "flow" contains all dependences computed so far (apart from those
+ * in "must_rel" and "may_rel") and may be updated with additional
+ * dependences derived from may-sources.
+ *
+ * In particular, consider all the must-sources with a non-empty
+ * dependence relation in "must_rel". They are considered in reverse
+ * order because that is the order in which they are considered in the caller.
+ * If any of the must-sources are coscheduled, then the last one
+ * is the one that will have a corresponding dependence relation.
+ * For each must-source i, consider both all the previous must-sources
+ * and all the may-sources. If any of those may be coscheduled with
+ * must-source i, then compute the coscheduled instances that access
+ * the same memory elements. The result is a relation [T -> S] -> K.
+ * The projection onto T -> K is a subset of the must-dependence relation
+ * that needs to be turned into may-dependences.
+ * The projection onto S -> K needs to be added to the may-dependences
+ * of source S.
+ * Since a given must-source instance may be coscheduled with several
+ * other source instances, the dependences that need to be turned
+ * into may-dependences are first collected and only actually removed
+ * from the must-dependences after all other sources have been considered.
+ */
+static __isl_give isl_flow *handle_coscheduled(__isl_keep isl_access_info *acc,
+ __isl_keep isl_map **must_rel, __isl_keep isl_map **may_rel,
+ __isl_take isl_flow *flow)
+{
+ int i, j;
+
+ if (!acc->coscheduled)
+ return flow;
+ for (i = acc->n_must - 1; i >= 0; --i) {
+ isl_map *move;
+
+ if (isl_map_plain_is_empty(must_rel[i]))
+ continue;
+ move = isl_map_empty(isl_map_get_space(must_rel[i]));
+ for (j = i - 1; j >= 0; --j) {
+ int depth;
+ isl_bool coscheduled;
+ isl_map *map, *factor;
+
+ coscheduled = acc->coscheduled(acc->source[i].data,
+ acc->source[j].data);
+ if (coscheduled < 0) {
+ isl_map_free(move);
+ return isl_flow_free(flow);
+ }
+ if (!coscheduled)
+ continue;
+ depth = acc->level_before(acc->source[i].data,
+ acc->source[j].data) / 2;
+ map = coscheduled_source(acc, must_rel[i], j, depth);
+ factor = isl_map_domain_factor_range(isl_map_copy(map));
+ may_rel[j] = isl_map_union(may_rel[j], factor);
+ map = isl_map_domain_factor_domain(map);
+ move = isl_map_union(move, map);
+ }
+ for (j = 0; j < acc->n_may; ++j) {
+ int depth, pos;
+ isl_bool coscheduled;
+ isl_map *map, *factor;
+
+ pos = acc->n_must + j;
+ coscheduled = acc->coscheduled(acc->source[i].data,
+ acc->source[pos].data);
+ if (coscheduled < 0) {
+ isl_map_free(move);
+ return isl_flow_free(flow);
+ }
+ if (!coscheduled)
+ continue;
+ depth = acc->level_before(acc->source[i].data,
+ acc->source[pos].data) / 2;
+ map = coscheduled_source(acc, must_rel[i], pos, depth);
+ factor = isl_map_domain_factor_range(isl_map_copy(map));
+ pos = 2 * acc->n_must + j;
+ flow->dep[pos].map = isl_map_union(flow->dep[pos].map,
+ factor);
+ map = isl_map_domain_factor_domain(map);
+ move = isl_map_union(move, map);
+ }
+ must_rel[i] = isl_map_subtract(must_rel[i], isl_map_copy(move));
+ may_rel[i] = isl_map_union(may_rel[i], move);
+ }
+
+ return flow;
+}
+
+/* Compute dependences for the case where all accesses are "may"
+ * accesses, which boils down to computing memory based dependences.
+ * The generic algorithm would also work in this case, but it would
+ * be overkill to use it.
+ */
+static __isl_give isl_flow *compute_mem_based_dependences(
+ __isl_keep isl_access_info *acc)
+{
+ int i;
+ isl_set *mustdo;
+ isl_set *maydo;
+ isl_flow *res;
+
+ res = isl_flow_alloc(acc);
+ if (!res)
+ return NULL;
+
+ mustdo = isl_map_domain(isl_map_copy(acc->sink.map));
+ maydo = isl_set_copy(mustdo);
+
+ for (i = 0; i < acc->n_may; ++i) {
+ int plevel;
+ int is_before;
+ isl_space *space;
+ isl_map *before;
+ isl_map *dep;
+
+ plevel = acc->level_before(acc->source[i].data, acc->sink.data);
+ if (plevel < 0)
+ goto error;
+
+ is_before = plevel & 1;
+ plevel >>= 1;
+
+ space = isl_map_get_space(res->dep[i].map);
+ if (is_before)
+ before = isl_map_lex_le_first(space, plevel);
+ else
+ before = isl_map_lex_lt_first(space, plevel);
+ dep = isl_map_apply_range(isl_map_copy(acc->source[i].map),
+ isl_map_reverse(isl_map_copy(acc->sink.map)));
+ dep = isl_map_intersect(dep, before);
+ mustdo = isl_set_subtract(mustdo,
+ isl_map_range(isl_map_copy(dep)));
+ res->dep[i].map = isl_map_union(res->dep[i].map, dep);
+ }
+
+ res->may_no_source = isl_set_subtract(maydo, isl_set_copy(mustdo));
+ res->must_no_source = mustdo;
+
+ return res;
+error:
+ isl_set_free(mustdo);
+ isl_set_free(maydo);
+ isl_flow_free(res);
+ return NULL;
+}
+
+/* Compute dependences for the case where there is at least one
+ * "must" access.
+ *
+ * The core algorithm considers all levels in which a source may precede
+ * the sink, where a level may either be a statement level or a loop level.
+ * The outermost statement level is 1, the first loop level is 2, etc...
+ * The algorithm basically does the following:
+ * for all levels l of the read access from innermost to outermost
+ * for all sources w that may precede the sink access at that level
+ * compute the last iteration of the source that precedes the sink access
+ * at that level
+ * add result to possible last accesses at level l of source w
+ * for all sources w2 that we haven't considered yet at this level that may
+ * also precede the sink access
+ * for all levels l2 of w from l to innermost
+ * for all possible last accesses dep of w at l
+ * compute last iteration of w2 between the source and sink
+ * of dep
+ * add result to possible last accesses at level l of write w2
+ * and replace possible last accesses dep by the remainder
+ *
+ *
+ * The above algorithm is applied to the must access. During the course
+ * of the algorithm, we keep track of sink iterations that still
+ * need to be considered. These iterations are split into those that
+ * haven't been matched to any source access (mustdo) and those that have only
+ * been matched to may accesses (maydo).
+ * At the end of each level, must-sources and may-sources that are coscheduled
+ * with the sources of the must-dependences at that level are considered.
+ * If any coscheduled instances are found, then corresponding may-dependences
+ * are added and the original must-dependences are turned into may-dependences.
+ * Afterwards, the may accesses that occur after must-dependence sources
+ * are considered.
+ * In particular, we consider may accesses that precede the remaining
+ * sink iterations, moving elements from mustdo to maydo when appropriate,
+ * and may accesses that occur between a must source and a sink of any
+ * dependences found at the current level, turning must dependences into
+ * may dependences when appropriate.
+ *
+ */
+static __isl_give isl_flow *compute_val_based_dependences(
+ __isl_keep isl_access_info *acc)
+{
+ isl_ctx *ctx;
+ isl_flow *res;
+ isl_set *mustdo = NULL;
+ isl_set *maydo = NULL;
+ int level, j;
+ isl_size n_in;
+ int depth;
+ isl_map **must_rel = NULL;
+ isl_map **may_rel = NULL;
+
+ if (!acc)
+ return NULL;
+
+ res = isl_flow_alloc(acc);
+ if (!res)
+ goto error;
+ ctx = isl_map_get_ctx(acc->sink.map);
+
+ n_in = isl_map_dim(acc->sink.map, isl_dim_in);
+ if (n_in < 0)
+ goto error;
+ depth = 2 * n_in + 1;
+ mustdo = isl_map_domain(isl_map_copy(acc->sink.map));
+ maydo = isl_set_empty(isl_set_get_space(mustdo));
+ if (!mustdo || !maydo)
+ goto error;
+ if (isl_set_plain_is_empty(mustdo))
+ goto done;
+
+ must_rel = isl_calloc_array(ctx, struct isl_map *, acc->n_must);
+ may_rel = isl_calloc_array(ctx, struct isl_map *, acc->n_must);
+ if (!must_rel || !may_rel)
+ goto error;
+
+ for (level = depth; level >= 1; --level) {
+ for (j = acc->n_must-1; j >=0; --j) {
+ isl_space *space;
+ space = isl_map_get_space(res->dep[2 * j].map);
+ must_rel[j] = isl_map_empty(space);
+ may_rel[j] = isl_map_copy(must_rel[j]);
+ }
+
+ for (j = acc->n_must - 1; j >= 0; --j) {
+ struct isl_map *T;
+ struct isl_set *rest;
+ int plevel;
+
+ plevel = acc->level_before(acc->source[j].data,
+ acc->sink.data);
+ if (plevel < 0)
+ goto error;
+ if (!can_precede_at_level(plevel, level))
+ continue;
+
+ T = last_source(acc, mustdo, j, level, &rest);
+ must_rel[j] = isl_map_union_disjoint(must_rel[j], T);
+ mustdo = rest;
+
+ if (intermediate_sources(acc, must_rel, j, level) < 0)
+ goto error;
+
+ T = last_source(acc, maydo, j, level, &rest);
+ may_rel[j] = isl_map_union_disjoint(may_rel[j], T);
+ maydo = rest;
+
+ if (intermediate_sources(acc, may_rel, j, level) < 0)
+ goto error;
+
+ if (isl_set_plain_is_empty(mustdo) &&
+ isl_set_plain_is_empty(maydo))
+ break;
+ }
+ for (j = j - 1; j >= 0; --j) {
+ int plevel;
+
+ plevel = acc->level_before(acc->source[j].data,
+ acc->sink.data);
+ if (plevel < 0)
+ goto error;
+ if (!can_precede_at_level(plevel, level))
+ continue;
+
+ if (intermediate_sources(acc, must_rel, j, level) < 0)
+ goto error;
+ if (intermediate_sources(acc, may_rel, j, level) < 0)
+ goto error;
+ }
+
+ res = handle_coscheduled(acc, must_rel, may_rel, res);
+ if (!res)
+ goto error;
+
+ for (j = 0; j < acc->n_may; ++j) {
+ int plevel;
+ isl_map *T;
+ isl_set *ran;
+
+ plevel = acc->level_before(acc->source[acc->n_must + j].data,
+ acc->sink.data);
+ if (plevel < 0)
+ goto error;
+ if (!can_precede_at_level(plevel, level))
+ continue;
+
+ T = all_sources(acc, isl_set_copy(maydo), j, level);
+ res->dep[2 * acc->n_must + j].map =
+ isl_map_union(res->dep[2 * acc->n_must + j].map, T);
+ T = all_sources(acc, isl_set_copy(mustdo), j, level);
+ ran = isl_map_range(isl_map_copy(T));
+ res->dep[2 * acc->n_must + j].map =
+ isl_map_union(res->dep[2 * acc->n_must + j].map, T);
+ mustdo = isl_set_subtract(mustdo, isl_set_copy(ran));
+ maydo = isl_set_union_disjoint(maydo, ran);
+
+ T = res->dep[2 * acc->n_must + j].map;
+ T = all_intermediate_sources(acc, T, must_rel, may_rel,
+ j, level);
+ res->dep[2 * acc->n_must + j].map = T;
+ }
+
+ for (j = acc->n_must - 1; j >= 0; --j) {
+ res->dep[2 * j].map =
+ isl_map_union_disjoint(res->dep[2 * j].map,
+ must_rel[j]);
+ res->dep[2 * j + 1].map =
+ isl_map_union_disjoint(res->dep[2 * j + 1].map,
+ may_rel[j]);
+ }
+
+ if (isl_set_plain_is_empty(mustdo) &&
+ isl_set_plain_is_empty(maydo))
+ break;
+ }
+
+ free(must_rel);
+ free(may_rel);
+done:
+ res->must_no_source = mustdo;
+ res->may_no_source = maydo;
+ return res;
+error:
+ if (must_rel)
+ for (j = 0; j < acc->n_must; ++j)
+ isl_map_free(must_rel[j]);
+ if (may_rel)
+ for (j = 0; j < acc->n_must; ++j)
+ isl_map_free(may_rel[j]);
+ isl_flow_free(res);
+ isl_set_free(mustdo);
+ isl_set_free(maydo);
+ free(must_rel);
+ free(may_rel);
+ return NULL;
+}
+
+/* Given a "sink" access, a list of n "source" accesses,
+ * compute for each iteration of the sink access
+ * and for each element accessed by that iteration,
+ * the source access in the list that last accessed the
+ * element accessed by the sink access before this sink access.
+ * Each access is given as a map from the loop iterators
+ * to the array indices.
+ * The result is a list of n relations between source and sink
+ * iterations and a subset of the domain of the sink access,
+ * corresponding to those iterations that access an element
+ * not previously accessed.
+ *
+ * To deal with multi-valued sink access relations, the sink iteration
+ * domain is first extended with dimensions that correspond to the data
+ * space. However, these extra dimensions are not projected out again.
+ * It is up to the caller to decide whether these dimensions should be kept.
+ */
+static __isl_give isl_flow *access_info_compute_flow_core(
+ __isl_take isl_access_info *acc)
+{
+ struct isl_flow *res = NULL;
+
+ if (!acc)
+ return NULL;
+
+ acc->sink.map = isl_map_range_map(acc->sink.map);
+ if (!acc->sink.map)
+ goto error;
+
+ if (acc->n_must == 0)
+ res = compute_mem_based_dependences(acc);
+ else {
+ acc = isl_access_info_sort_sources(acc);
+ res = compute_val_based_dependences(acc);
+ }
+ acc = isl_access_info_free(acc);
+ if (!res)
+ return NULL;
+ if (!res->must_no_source || !res->may_no_source)
+ goto error;
+ return res;
+error:
+ isl_access_info_free(acc);
+ isl_flow_free(res);
+ return NULL;
+}
+
+/* Given a "sink" access, a list of n "source" accesses,
+ * compute for each iteration of the sink access
+ * and for each element accessed by that iteration,
+ * the source access in the list that last accessed the
+ * element accessed by the sink access before this sink access.
+ * Each access is given as a map from the loop iterators
+ * to the array indices.
+ * The result is a list of n relations between source and sink
+ * iterations and a subset of the domain of the sink access,
+ * corresponding to those iterations that access an element
+ * not previously accessed.
+ *
+ * To deal with multi-valued sink access relations,
+ * access_info_compute_flow_core extends the sink iteration domain
+ * with dimensions that correspond to the data space. These extra dimensions
+ * are projected out from the result of access_info_compute_flow_core.
+ */
+__isl_give isl_flow *isl_access_info_compute_flow(__isl_take isl_access_info *acc)
+{
+ int j;
+ struct isl_flow *res;
+
+ if (!acc)
+ return NULL;
+
+ acc->domain_map = isl_map_domain_map(isl_map_copy(acc->sink.map));
+ res = access_info_compute_flow_core(acc);
+ if (!res)
+ return NULL;
+
+ for (j = 0; j < res->n_source; ++j) {
+ res->dep[j].map = isl_map_range_factor_domain(res->dep[j].map);
+ if (!res->dep[j].map)
+ goto error;
+ }
+
+ return res;
+error:
+ isl_flow_free(res);
+ return NULL;
+}
+
+
+/* Keep track of some information about a schedule for a given
+ * access. In particular, keep track of which dimensions
+ * have a constant value and of the actual constant values.
+ */
+struct isl_sched_info {
+ int *is_cst;
+ isl_vec *cst;
+};
+
+static void sched_info_free(__isl_take struct isl_sched_info *info)
+{
+ if (!info)
+ return;
+ isl_vec_free(info->cst);
+ free(info->is_cst);
+ free(info);
+}
+
+/* Extract information on the constant dimensions of the schedule
+ * for a given access. The "map" is of the form
+ *
+ * [S -> D] -> A
+ *
+ * with S the schedule domain, D the iteration domain and A the data domain.
+ */
+static __isl_give struct isl_sched_info *sched_info_alloc(
+ __isl_keep isl_map *map)
+{
+ isl_ctx *ctx;
+ isl_space *space;
+ struct isl_sched_info *info;
+ int i;
+ isl_size n;
+
+ if (!map)
+ return NULL;
+
+ space = isl_space_unwrap(isl_space_domain(isl_map_get_space(map)));
+ if (!space)
+ return NULL;
+ n = isl_space_dim(space, isl_dim_in);
+ isl_space_free(space);
+ if (n < 0)
+ return NULL;
+
+ ctx = isl_map_get_ctx(map);
+ info = isl_alloc_type(ctx, struct isl_sched_info);
+ if (!info)
+ return NULL;
+ info->is_cst = isl_alloc_array(ctx, int, n);
+ info->cst = isl_vec_alloc(ctx, n);
+ if (n && (!info->is_cst || !info->cst))
+ goto error;
+
+ for (i = 0; i < n; ++i) {
+ isl_val *v;
+
+ v = isl_map_plain_get_val_if_fixed(map, isl_dim_in, i);
+ if (!v)
+ goto error;
+ info->is_cst[i] = !isl_val_is_nan(v);
+ if (info->is_cst[i])
+ info->cst = isl_vec_set_element_val(info->cst, i, v);
+ else
+ isl_val_free(v);
+ }
+
+ return info;
+error:
+ sched_info_free(info);
+ return NULL;
+}
+
+/* The different types of access relations that isl_union_access_info
+ * keeps track of.
+
+ * "isl_access_sink" represents the sink accesses.
+ * "isl_access_must_source" represents the definite source accesses.
+ * "isl_access_may_source" represents the possible source accesses.
+ * "isl_access_kill" represents the kills.
+ *
+ * isl_access_sink is sometimes treated differently and
+ * should therefore appear first.
+ */
+enum isl_access_type {
+ isl_access_sink,
+ isl_access_must_source,
+ isl_access_may_source,
+ isl_access_kill,
+ isl_access_end
+};
+
+/* This structure represents the input for a dependence analysis computation.
+ *
+ * "access" contains the access relations.
+ *
+ * "schedule" or "schedule_map" represents the execution order.
+ * Exactly one of these fields should be NULL. The other field
+ * determines the execution order.
+ *
+ * The domains of these four maps refer to the same iteration spaces(s).
+ * The ranges of the first three maps also refer to the same data space(s).
+ *
+ * After a call to isl_union_access_info_introduce_schedule,
+ * the "schedule_map" field no longer contains useful information.
+ */
+struct isl_union_access_info {
+ isl_union_map *access[isl_access_end];
+
+ isl_schedule *schedule;
+ isl_union_map *schedule_map;
+};
+
+/* Free "access" and return NULL.
+ */
+__isl_null isl_union_access_info *isl_union_access_info_free(
+ __isl_take isl_union_access_info *access)
+{
+ enum isl_access_type i;
+
+ if (!access)
+ return NULL;
+
+ for (i = isl_access_sink; i < isl_access_end; ++i)
+ isl_union_map_free(access->access[i]);
+ isl_schedule_free(access->schedule);
+ isl_union_map_free(access->schedule_map);
+ free(access);
+
+ return NULL;
+}
+
+/* Return the isl_ctx to which "access" belongs.
+ */
+isl_ctx *isl_union_access_info_get_ctx(__isl_keep isl_union_access_info *access)
+{
+ if (!access)
+ return NULL;
+ return isl_union_map_get_ctx(access->access[isl_access_sink]);
+}
+
+/* Construct an empty (invalid) isl_union_access_info object.
+ * The caller is responsible for setting the sink access relation and
+ * initializing all the other fields, e.g., by calling
+ * isl_union_access_info_init.
+ */
+static __isl_give isl_union_access_info *isl_union_access_info_alloc(
+ isl_ctx *ctx)
+{
+ return isl_calloc_type(ctx, isl_union_access_info);
+}
+
+/* Initialize all the fields of "info", except the sink access relation,
+ * which is assumed to have been set by the caller.
+ *
+ * By default, we use the schedule field of the isl_union_access_info,
+ * but this may be overridden by a call
+ * to isl_union_access_info_set_schedule_map.
+ */
+static __isl_give isl_union_access_info *isl_union_access_info_init(
+ __isl_take isl_union_access_info *info)
+{
+ isl_space *space;
+ isl_union_map *empty;
+ enum isl_access_type i;
+
+ if (!info)
+ return NULL;
+ if (!info->access[isl_access_sink])
+ return isl_union_access_info_free(info);
+
+ space = isl_union_map_get_space(info->access[isl_access_sink]);
+ empty = isl_union_map_empty(isl_space_copy(space));
+ for (i = isl_access_sink + 1; i < isl_access_end; ++i)
+ if (!info->access[i])
+ info->access[i] = isl_union_map_copy(empty);
+ isl_union_map_free(empty);
+ if (!info->schedule && !info->schedule_map)
+ info->schedule = isl_schedule_empty(isl_space_copy(space));
+ isl_space_free(space);
+
+ for (i = isl_access_sink + 1; i < isl_access_end; ++i)
+ if (!info->access[i])
+ return isl_union_access_info_free(info);
+ if (!info->schedule && !info->schedule_map)
+ return isl_union_access_info_free(info);
+
+ return info;
+}
+
+/* Create a new isl_union_access_info with the given sink accesses and
+ * and no other accesses or schedule information.
+ */
+__isl_give isl_union_access_info *isl_union_access_info_from_sink(
+ __isl_take isl_union_map *sink)
+{
+ isl_ctx *ctx;
+ isl_union_access_info *access;
+
+ if (!sink)
+ return NULL;
+ ctx = isl_union_map_get_ctx(sink);
+ access = isl_union_access_info_alloc(ctx);
+ if (!access)
+ goto error;
+ access->access[isl_access_sink] = sink;
+ return isl_union_access_info_init(access);
+error:
+ isl_union_map_free(sink);
+ return NULL;
+}
+
+/* Replace the access relation of type "type" of "info" by "access".
+ */
+static __isl_give isl_union_access_info *isl_union_access_info_set(
+ __isl_take isl_union_access_info *info,
+ enum isl_access_type type, __isl_take isl_union_map *access)
+{
+ if (!info || !access)
+ goto error;
+
+ isl_union_map_free(info->access[type]);
+ info->access[type] = access;
+
+ return info;
+error:
+ isl_union_access_info_free(info);
+ isl_union_map_free(access);
+ return NULL;
+}
+
+/* Replace the definite source accesses of "access" by "must_source".
+ */
+__isl_give isl_union_access_info *isl_union_access_info_set_must_source(
+ __isl_take isl_union_access_info *access,
+ __isl_take isl_union_map *must_source)
+{
+ return isl_union_access_info_set(access, isl_access_must_source,
+ must_source);
+}
+
+/* Replace the possible source accesses of "access" by "may_source".
+ */
+__isl_give isl_union_access_info *isl_union_access_info_set_may_source(
+ __isl_take isl_union_access_info *access,
+ __isl_take isl_union_map *may_source)
+{
+ return isl_union_access_info_set(access, isl_access_may_source,
+ may_source);
+}
+
+/* Replace the kills of "info" by "kill".
+ */
+__isl_give isl_union_access_info *isl_union_access_info_set_kill(
+ __isl_take isl_union_access_info *info, __isl_take isl_union_map *kill)
+{
+ return isl_union_access_info_set(info, isl_access_kill, kill);
+}
+
+/* Return the access relation of type "type" of "info".
+ */
+static __isl_give isl_union_map *isl_union_access_info_get(
+ __isl_keep isl_union_access_info *info, enum isl_access_type type)
+{
+ if (!info)
+ return NULL;
+ return isl_union_map_copy(info->access[type]);
+}
+
+/* Return the definite source accesses of "info".
+ */
+__isl_give isl_union_map *isl_union_access_info_get_must_source(
+ __isl_keep isl_union_access_info *info)
+{
+ return isl_union_access_info_get(info, isl_access_must_source);
+}
+
+/* Return the possible source accesses of "info".
+ */
+__isl_give isl_union_map *isl_union_access_info_get_may_source(
+ __isl_keep isl_union_access_info *info)
+{
+ return isl_union_access_info_get(info, isl_access_may_source);
+}
+
+/* Return the kills of "info".
+ */
+__isl_give isl_union_map *isl_union_access_info_get_kill(
+ __isl_keep isl_union_access_info *info)
+{
+ return isl_union_access_info_get(info, isl_access_kill);
+}
+
+/* Does "info" specify any kills?
+ */
+static isl_bool isl_union_access_has_kill(
+ __isl_keep isl_union_access_info *info)
+{
+ isl_bool empty;
+
+ if (!info)
+ return isl_bool_error;
+ empty = isl_union_map_is_empty(info->access[isl_access_kill]);
+ return isl_bool_not(empty);
+}
+
+/* Replace the schedule of "access" by "schedule".
+ * Also free the schedule_map in case it was set last.
+ */
+__isl_give isl_union_access_info *isl_union_access_info_set_schedule(
+ __isl_take isl_union_access_info *access,
+ __isl_take isl_schedule *schedule)
+{
+ if (!access || !schedule)
+ goto error;
+
+ access->schedule_map = isl_union_map_free(access->schedule_map);
+ isl_schedule_free(access->schedule);
+ access->schedule = schedule;
+
+ return access;
+error:
+ isl_union_access_info_free(access);
+ isl_schedule_free(schedule);
+ return NULL;
+}
+
+/* Replace the schedule map of "access" by "schedule_map".
+ * Also free the schedule in case it was set last.
+ */
+__isl_give isl_union_access_info *isl_union_access_info_set_schedule_map(
+ __isl_take isl_union_access_info *access,
+ __isl_take isl_union_map *schedule_map)
+{
+ if (!access || !schedule_map)
+ goto error;
+
+ isl_union_map_free(access->schedule_map);
+ access->schedule = isl_schedule_free(access->schedule);
+ access->schedule_map = schedule_map;
+
+ return access;
+error:
+ isl_union_access_info_free(access);
+ isl_union_map_free(schedule_map);
+ return NULL;
+}
+
+__isl_give isl_union_access_info *isl_union_access_info_copy(
+ __isl_keep isl_union_access_info *access)
+{
+ isl_union_access_info *copy;
+ enum isl_access_type i;
+
+ if (!access)
+ return NULL;
+ copy = isl_union_access_info_from_sink(
+ isl_union_map_copy(access->access[isl_access_sink]));
+ for (i = isl_access_sink + 1; i < isl_access_end; ++i)
+ copy = isl_union_access_info_set(copy, i,
+ isl_union_map_copy(access->access[i]));
+ if (access->schedule)
+ copy = isl_union_access_info_set_schedule(copy,
+ isl_schedule_copy(access->schedule));
+ else
+ copy = isl_union_access_info_set_schedule_map(copy,
+ isl_union_map_copy(access->schedule_map));
+
+ return copy;
+}
+
+#undef BASE
+#define BASE union_map
+#include "print_yaml_field_templ.c"
+
+/* An enumeration of the various keys that may appear in a YAML mapping
+ * of an isl_union_access_info object.
+ * The keys for the access relation types are assumed to have the same values
+ * as the access relation types in isl_access_type.
+ */
+enum isl_ai_key {
+ isl_ai_key_error = -1,
+ isl_ai_key_sink = isl_access_sink,
+ isl_ai_key_must_source = isl_access_must_source,
+ isl_ai_key_may_source = isl_access_may_source,
+ isl_ai_key_kill = isl_access_kill,
+ isl_ai_key_schedule_map,
+ isl_ai_key_schedule,
+ isl_ai_key_end
+};
+
+/* Textual representations of the YAML keys for an isl_union_access_info
+ * object.
+ */
+static char *key_str[] = {
+ [isl_ai_key_sink] = "sink",
+ [isl_ai_key_must_source] = "must_source",
+ [isl_ai_key_may_source] = "may_source",
+ [isl_ai_key_kill] = "kill",
+ [isl_ai_key_schedule_map] = "schedule_map",
+ [isl_ai_key_schedule] = "schedule",
+};
+
+/* Print a key-value pair corresponding to the access relation of type "type"
+ * of a YAML mapping of "info" to "p".
+ *
+ * The sink access relation is always printed, but any other access relation
+ * is only printed if it is non-empty.
+ */
+static __isl_give isl_printer *print_access_field(__isl_take isl_printer *p,
+ __isl_keep isl_union_access_info *info, enum isl_access_type type)
+{
+ if (type != isl_access_sink) {
+ isl_bool empty;
+
+ empty = isl_union_map_is_empty(info->access[type]);
+ if (empty < 0)
+ return isl_printer_free(p);
+ if (empty)
+ return p;
+ }
+ return print_yaml_field_union_map(p, key_str[type], info->access[type]);
+}
+
+/* Print the information contained in "access" to "p".
+ * The information is printed as a YAML document.
+ */
+__isl_give isl_printer *isl_printer_print_union_access_info(
+ __isl_take isl_printer *p, __isl_keep isl_union_access_info *access)
+{
+ enum isl_access_type i;
+
+ if (!access)
+ return isl_printer_free(p);
+
+ p = isl_printer_yaml_start_mapping(p);
+ for (i = isl_access_sink; i < isl_access_end; ++i)
+ p = print_access_field(p, access, i);
+ if (access->schedule) {
+ p = isl_printer_print_str(p, key_str[isl_ai_key_schedule]);
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_schedule(p, access->schedule);
+ p = isl_printer_yaml_next(p);
+ } else {
+ p = print_yaml_field_union_map(p,
+ key_str[isl_ai_key_schedule_map], access->schedule_map);
+ }
+ p = isl_printer_yaml_end_mapping(p);
+
+ return p;
+}
+
+/* Return a string representation of the information in "access".
+ * The information is printed in flow format.
+ */
+__isl_give char *isl_union_access_info_to_str(
+ __isl_keep isl_union_access_info *access)
+{
+ isl_printer *p;
+ char *s;
+
+ if (!access)
+ return NULL;
+
+ p = isl_printer_to_str(isl_union_access_info_get_ctx(access));
+ p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_FLOW);
+ p = isl_printer_print_union_access_info(p, access);
+ s = isl_printer_get_str(p);
+ isl_printer_free(p);
+
+ return s;
+}
+
+#undef KEY
+#define KEY enum isl_ai_key
+#undef KEY_ERROR
+#define KEY_ERROR isl_ai_key_error
+#undef KEY_END
+#define KEY_END isl_ai_key_end
+#include "extract_key.c"
+
+#undef BASE
+#define BASE union_map
+#include "read_in_string_templ.c"
+
+/* Read an isl_union_access_info object from "s".
+ *
+ * Start off with an empty (invalid) isl_union_access_info object and
+ * then fill up the fields based on the input.
+ * The input needs to contain at least a description of the sink
+ * access relation as well as some form of schedule.
+ * The other access relations are set to empty relations
+ * by isl_union_access_info_init if they are not specified in the input.
+ */
+__isl_give isl_union_access_info *isl_stream_read_union_access_info(
+ isl_stream *s)
+{
+ isl_ctx *ctx;
+ isl_union_access_info *info;
+ int more;
+ int sink_set = 0;
+ int schedule_set = 0;
+
+ if (isl_stream_yaml_read_start_mapping(s))
+ return NULL;
+
+ ctx = isl_stream_get_ctx(s);
+ info = isl_union_access_info_alloc(ctx);
+ while ((more = isl_stream_yaml_next(s)) > 0) {
+ enum isl_ai_key key;
+ isl_union_map *access, *schedule_map;
+ isl_schedule *schedule;
+
+ key = get_key(s);
+ if (isl_stream_yaml_next(s) < 0)
+ return isl_union_access_info_free(info);
+ switch (key) {
+ case isl_ai_key_end:
+ case isl_ai_key_error:
+ return isl_union_access_info_free(info);
+ case isl_ai_key_sink:
+ sink_set = 1;
+ case isl_ai_key_must_source:
+ case isl_ai_key_may_source:
+ case isl_ai_key_kill:
+ access = read_union_map(s);
+ info = isl_union_access_info_set(info, key, access);
+ if (!info)
+ return NULL;
+ break;
+ case isl_ai_key_schedule_map:
+ schedule_set = 1;
+ schedule_map = read_union_map(s);
+ info = isl_union_access_info_set_schedule_map(info,
+ schedule_map);
+ if (!info)
+ return NULL;
+ break;
+ case isl_ai_key_schedule:
+ schedule_set = 1;
+ schedule = isl_stream_read_schedule(s);
+ info = isl_union_access_info_set_schedule(info,
+ schedule);
+ if (!info)
+ return NULL;
+ break;
+ }
+ }
+ if (more < 0)
+ return isl_union_access_info_free(info);
+
+ if (isl_stream_yaml_read_end_mapping(s) < 0) {
+ isl_stream_error(s, NULL, "unexpected extra elements");
+ return isl_union_access_info_free(info);
+ }
+
+ if (!sink_set) {
+ isl_stream_error(s, NULL, "no sink specified");
+ return isl_union_access_info_free(info);
+ }
+
+ if (!schedule_set) {
+ isl_stream_error(s, NULL, "no schedule specified");
+ return isl_union_access_info_free(info);
+ }
+
+ return isl_union_access_info_init(info);
+}
+
+/* Read an isl_union_access_info object from the file "input".
+ */
+__isl_give isl_union_access_info *isl_union_access_info_read_from_file(
+ isl_ctx *ctx, FILE *input)
+{
+ isl_stream *s;
+ isl_union_access_info *access;
+
+ s = isl_stream_new_file(ctx, input);
+ if (!s)
+ return NULL;
+ access = isl_stream_read_union_access_info(s);
+ isl_stream_free(s);
+
+ return access;
+}
+
+/* Update the fields of "access" such that they all have the same parameters,
+ * keeping in mind that the schedule_map field may be NULL and ignoring
+ * the schedule field.
+ */
+static __isl_give isl_union_access_info *isl_union_access_info_align_params(
+ __isl_take isl_union_access_info *access)
+{
+ isl_space *space;
+ enum isl_access_type i;
+
+ if (!access)
+ return NULL;
+
+ space = isl_union_map_get_space(access->access[isl_access_sink]);
+ for (i = isl_access_sink + 1; i < isl_access_end; ++i)
+ space = isl_space_align_params(space,
+ isl_union_map_get_space(access->access[i]));
+ if (access->schedule_map)
+ space = isl_space_align_params(space,
+ isl_union_map_get_space(access->schedule_map));
+ for (i = isl_access_sink; i < isl_access_end; ++i)
+ access->access[i] =
+ isl_union_map_align_params(access->access[i],
+ isl_space_copy(space));
+ if (!access->schedule_map) {
+ isl_space_free(space);
+ } else {
+ access->schedule_map =
+ isl_union_map_align_params(access->schedule_map, space);
+ if (!access->schedule_map)
+ return isl_union_access_info_free(access);
+ }
+
+ for (i = isl_access_sink; i < isl_access_end; ++i)
+ if (!access->access[i])
+ return isl_union_access_info_free(access);
+
+ return access;
+}
+
+/* Prepend the schedule dimensions to the iteration domains.
+ *
+ * That is, if the schedule is of the form
+ *
+ * D -> S
+ *
+ * while the access relations are of the form
+ *
+ * D -> A
+ *
+ * then the updated access relations are of the form
+ *
+ * [S -> D] -> A
+ *
+ * The schedule map is also replaced by the map
+ *
+ * [S -> D] -> D
+ *
+ * that is used during the internal computation.
+ * Neither the original schedule map nor this updated schedule map
+ * are used after the call to this function.
+ */
+static __isl_give isl_union_access_info *
+isl_union_access_info_introduce_schedule(
+ __isl_take isl_union_access_info *access)
+{
+ isl_union_map *sm;
+ enum isl_access_type i;
+
+ if (!access)
+ return NULL;
+
+ sm = isl_union_map_reverse(access->schedule_map);
+ sm = isl_union_map_range_map(sm);
+ for (i = isl_access_sink; i < isl_access_end; ++i)
+ access->access[i] =
+ isl_union_map_apply_range(isl_union_map_copy(sm),
+ access->access[i]);
+ access->schedule_map = sm;
+
+ for (i = isl_access_sink; i < isl_access_end; ++i)
+ if (!access->access[i])
+ return isl_union_access_info_free(access);
+ if (!access->schedule_map)
+ return isl_union_access_info_free(access);
+
+ return access;
+}
+
+/* This structure represents the result of a dependence analysis computation.
+ *
+ * "must_dep" represents the full definite dependences
+ * "may_dep" represents the full non-definite dependences.
+ * Both are of the form
+ *
+ * [Source] -> [[Sink -> Data]]
+ *
+ * (after the schedule dimensions have been projected out).
+ * "must_no_source" represents the subset of the sink accesses for which
+ * definitely no source was found.
+ * "may_no_source" represents the subset of the sink accesses for which
+ * possibly, but not definitely, no source was found.
+ */
+struct isl_union_flow {
+ isl_union_map *must_dep;
+ isl_union_map *may_dep;
+ isl_union_map *must_no_source;
+ isl_union_map *may_no_source;
+};
+
+/* Return the isl_ctx to which "flow" belongs.
+ */
+isl_ctx *isl_union_flow_get_ctx(__isl_keep isl_union_flow *flow)
+{
+ return flow ? isl_union_map_get_ctx(flow->must_dep) : NULL;
+}
+
+/* Free "flow" and return NULL.
+ */
+__isl_null isl_union_flow *isl_union_flow_free(__isl_take isl_union_flow *flow)
+{
+ if (!flow)
+ return NULL;
+ isl_union_map_free(flow->must_dep);
+ isl_union_map_free(flow->may_dep);
+ isl_union_map_free(flow->must_no_source);
+ isl_union_map_free(flow->may_no_source);
+ free(flow);
+ return NULL;
+}
+
+void isl_union_flow_dump(__isl_keep isl_union_flow *flow)
+{
+ if (!flow)
+ return;
+
+ fprintf(stderr, "must dependences: ");
+ isl_union_map_dump(flow->must_dep);
+ fprintf(stderr, "may dependences: ");
+ isl_union_map_dump(flow->may_dep);
+ fprintf(stderr, "must no source: ");
+ isl_union_map_dump(flow->must_no_source);
+ fprintf(stderr, "may no source: ");
+ isl_union_map_dump(flow->may_no_source);
+}
+
+/* Return the full definite dependences in "flow", with accessed elements.
+ */
+__isl_give isl_union_map *isl_union_flow_get_full_must_dependence(
+ __isl_keep isl_union_flow *flow)
+{
+ if (!flow)
+ return NULL;
+ return isl_union_map_copy(flow->must_dep);
+}
+
+/* Return the full possible dependences in "flow", including the definite
+ * dependences, with accessed elements.
+ */
+__isl_give isl_union_map *isl_union_flow_get_full_may_dependence(
+ __isl_keep isl_union_flow *flow)
+{
+ if (!flow)
+ return NULL;
+ return isl_union_map_union(isl_union_map_copy(flow->must_dep),
+ isl_union_map_copy(flow->may_dep));
+}
+
+/* Return the definite dependences in "flow", without the accessed elements.
+ */
+__isl_give isl_union_map *isl_union_flow_get_must_dependence(
+ __isl_keep isl_union_flow *flow)
+{
+ isl_union_map *dep;
+
+ if (!flow)
+ return NULL;
+ dep = isl_union_map_copy(flow->must_dep);
+ return isl_union_map_range_factor_domain(dep);
+}
+
+/* Return the possible dependences in "flow", including the definite
+ * dependences, without the accessed elements.
+ */
+__isl_give isl_union_map *isl_union_flow_get_may_dependence(
+ __isl_keep isl_union_flow *flow)
+{
+ isl_union_map *dep;
+
+ if (!flow)
+ return NULL;
+ dep = isl_union_map_union(isl_union_map_copy(flow->must_dep),
+ isl_union_map_copy(flow->may_dep));
+ return isl_union_map_range_factor_domain(dep);
+}
+
+/* Return the non-definite dependences in "flow".
+ */
+static __isl_give isl_union_map *isl_union_flow_get_non_must_dependence(
+ __isl_keep isl_union_flow *flow)
+{
+ if (!flow)
+ return NULL;
+ return isl_union_map_copy(flow->may_dep);
+}
+
+/* Return the subset of the sink accesses for which definitely
+ * no source was found.
+ */
+__isl_give isl_union_map *isl_union_flow_get_must_no_source(
+ __isl_keep isl_union_flow *flow)
+{
+ if (!flow)
+ return NULL;
+ return isl_union_map_copy(flow->must_no_source);
+}
+
+/* Return the subset of the sink accesses for which possibly
+ * no source was found, including those for which definitely
+ * no source was found.
+ */
+__isl_give isl_union_map *isl_union_flow_get_may_no_source(
+ __isl_keep isl_union_flow *flow)
+{
+ if (!flow)
+ return NULL;
+ return isl_union_map_union(isl_union_map_copy(flow->must_no_source),
+ isl_union_map_copy(flow->may_no_source));
+}
+
+/* Return the subset of the sink accesses for which possibly, but not
+ * definitely, no source was found.
+ */
+static __isl_give isl_union_map *isl_union_flow_get_non_must_no_source(
+ __isl_keep isl_union_flow *flow)
+{
+ if (!flow)
+ return NULL;
+ return isl_union_map_copy(flow->may_no_source);
+}
+
+/* Create a new isl_union_flow object, initialized with empty
+ * dependence relations and sink subsets.
+ */
+static __isl_give isl_union_flow *isl_union_flow_alloc(
+ __isl_take isl_space *space)
+{
+ isl_ctx *ctx;
+ isl_union_map *empty;
+ isl_union_flow *flow;
+
+ if (!space)
+ return NULL;
+ ctx = isl_space_get_ctx(space);
+ flow = isl_alloc_type(ctx, isl_union_flow);
+ if (!flow)
+ goto error;
+
+ empty = isl_union_map_empty(space);
+ flow->must_dep = isl_union_map_copy(empty);
+ flow->may_dep = isl_union_map_copy(empty);
+ flow->must_no_source = isl_union_map_copy(empty);
+ flow->may_no_source = empty;
+
+ if (!flow->must_dep || !flow->may_dep ||
+ !flow->must_no_source || !flow->may_no_source)
+ return isl_union_flow_free(flow);
+
+ return flow;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Copy this isl_union_flow object.
+ */
+__isl_give isl_union_flow *isl_union_flow_copy(__isl_keep isl_union_flow *flow)
+{
+ isl_union_flow *copy;
+
+ if (!flow)
+ return NULL;
+
+ copy = isl_union_flow_alloc(isl_union_map_get_space(flow->must_dep));
+
+ if (!copy)
+ return NULL;
+
+ copy->must_dep = isl_union_map_union(copy->must_dep,
+ isl_union_map_copy(flow->must_dep));
+ copy->may_dep = isl_union_map_union(copy->may_dep,
+ isl_union_map_copy(flow->may_dep));
+ copy->must_no_source = isl_union_map_union(copy->must_no_source,
+ isl_union_map_copy(flow->must_no_source));
+ copy->may_no_source = isl_union_map_union(copy->may_no_source,
+ isl_union_map_copy(flow->may_no_source));
+
+ if (!copy->must_dep || !copy->may_dep ||
+ !copy->must_no_source || !copy->may_no_source)
+ return isl_union_flow_free(copy);
+
+ return copy;
+}
+
+/* Drop the schedule dimensions from the iteration domains in "flow".
+ * In particular, the schedule dimensions have been prepended
+ * to the iteration domains prior to the dependence analysis by
+ * replacing the iteration domain D, by the wrapped map [S -> D].
+ * Replace these wrapped maps by the original D.
+ *
+ * In particular, the dependences computed by access_info_compute_flow_core
+ * are of the form
+ *
+ * [S -> D] -> [[S' -> D'] -> A]
+ *
+ * The schedule dimensions are projected out by first currying the range,
+ * resulting in
+ *
+ * [S -> D] -> [S' -> [D' -> A]]
+ *
+ * and then computing the factor range
+ *
+ * D -> [D' -> A]
+ */
+static __isl_give isl_union_flow *isl_union_flow_drop_schedule(
+ __isl_take isl_union_flow *flow)
+{
+ if (!flow)
+ return NULL;
+
+ flow->must_dep = isl_union_map_range_curry(flow->must_dep);
+ flow->must_dep = isl_union_map_factor_range(flow->must_dep);
+ flow->may_dep = isl_union_map_range_curry(flow->may_dep);
+ flow->may_dep = isl_union_map_factor_range(flow->may_dep);
+ flow->must_no_source =
+ isl_union_map_domain_factor_range(flow->must_no_source);
+ flow->may_no_source =
+ isl_union_map_domain_factor_range(flow->may_no_source);
+
+ if (!flow->must_dep || !flow->may_dep ||
+ !flow->must_no_source || !flow->may_no_source)
+ return isl_union_flow_free(flow);
+
+ return flow;
+}
+
+struct isl_compute_flow_data {
+ isl_union_map *must_source;
+ isl_union_map *may_source;
+ isl_union_flow *flow;
+
+ int count;
+ int must;
+ isl_space *dim;
+ struct isl_sched_info *sink_info;
+ struct isl_sched_info **source_info;
+ isl_access_info *accesses;
+};
+
+static isl_stat count_matching_array(__isl_take isl_map *map, void *user)
+{
+ int eq;
+ isl_space *space;
+ struct isl_compute_flow_data *data;
+
+ data = (struct isl_compute_flow_data *)user;
+
+ space = isl_space_range(isl_map_get_space(map));
+
+ eq = isl_space_is_equal(space, data->dim);
+
+ isl_space_free(space);
+ isl_map_free(map);
+
+ if (eq < 0)
+ return isl_stat_error;
+ if (eq)
+ data->count++;
+
+ return isl_stat_ok;
+}
+
+static isl_stat collect_matching_array(__isl_take isl_map *map, void *user)
+{
+ int eq;
+ isl_space *space;
+ struct isl_sched_info *info;
+ struct isl_compute_flow_data *data;
+
+ data = (struct isl_compute_flow_data *)user;
+
+ space = isl_space_range(isl_map_get_space(map));
+
+ eq = isl_space_is_equal(space, data->dim);
+
+ isl_space_free(space);
+
+ if (eq < 0)
+ goto error;
+ if (!eq) {
+ isl_map_free(map);
+ return isl_stat_ok;
+ }
+
+ info = sched_info_alloc(map);
+ data->source_info[data->count] = info;
+
+ data->accesses = isl_access_info_add_source(data->accesses,
+ map, data->must, info);
+
+ data->count++;
+
+ return isl_stat_ok;
+error:
+ isl_map_free(map);
+ return isl_stat_error;
+}
+
+/* Determine the shared nesting level and the "textual order" of
+ * the given accesses.
+ *
+ * We first determine the minimal schedule dimension for both accesses.
+ *
+ * If among those dimensions, we can find one where both have a fixed
+ * value and if moreover those values are different, then the previous
+ * dimension is the last shared nesting level and the textual order
+ * is determined based on the order of the fixed values.
+ * If no such fixed values can be found, then we set the shared
+ * nesting level to the minimal schedule dimension, with no textual ordering.
+ */
+static int before(void *first, void *second)
+{
+ struct isl_sched_info *info1 = first;
+ struct isl_sched_info *info2 = second;
+ isl_size n1, n2;
+ int i;
+
+ n1 = isl_vec_size(info1->cst);
+ n2 = isl_vec_size(info2->cst);
+ if (n1 < 0 || n2 < 0)
+ return -1;
+
+ if (n2 < n1)
+ n1 = n2;
+
+ for (i = 0; i < n1; ++i) {
+ int r;
+ int cmp;
+
+ if (!info1->is_cst[i])
+ continue;
+ if (!info2->is_cst[i])
+ continue;
+ cmp = isl_vec_cmp_element(info1->cst, info2->cst, i);
+ if (cmp == 0)
+ continue;
+
+ r = 2 * i + (cmp < 0);
+
+ return r;
+ }
+
+ return 2 * n1;
+}
+
+/* Check if the given two accesses may be coscheduled.
+ * If so, return isl_bool_true. Otherwise return isl_bool_false.
+ *
+ * Two accesses may only be coscheduled if the fixed schedule
+ * coordinates have the same values.
+ */
+static isl_bool coscheduled(void *first, void *second)
+{
+ struct isl_sched_info *info1 = first;
+ struct isl_sched_info *info2 = second;
+ isl_size n1, n2;
+ int i;
+
+ n1 = isl_vec_size(info1->cst);
+ n2 = isl_vec_size(info2->cst);
+ if (n1 < 0 || n2 < 0)
+ return isl_bool_error;
+
+ if (n2 < n1)
+ n1 = n2;
+
+ for (i = 0; i < n1; ++i) {
+ int cmp;
+
+ if (!info1->is_cst[i])
+ continue;
+ if (!info2->is_cst[i])
+ continue;
+ cmp = isl_vec_cmp_element(info1->cst, info2->cst, i);
+ if (cmp != 0)
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+/* Given a sink access, look for all the source accesses that access
+ * the same array and perform dataflow analysis on them using
+ * isl_access_info_compute_flow_core.
+ */
+static isl_stat compute_flow(__isl_take isl_map *map, void *user)
+{
+ int i;
+ isl_ctx *ctx;
+ struct isl_compute_flow_data *data;
+ isl_flow *flow;
+ isl_union_flow *df;
+
+ data = (struct isl_compute_flow_data *)user;
+ df = data->flow;
+
+ ctx = isl_map_get_ctx(map);
+
+ data->accesses = NULL;
+ data->sink_info = NULL;
+ data->source_info = NULL;
+ data->count = 0;
+ data->dim = isl_space_range(isl_map_get_space(map));
+
+ if (isl_union_map_foreach_map(data->must_source,
+ &count_matching_array, data) < 0)
+ goto error;
+ if (isl_union_map_foreach_map(data->may_source,
+ &count_matching_array, data) < 0)
+ goto error;
+
+ data->sink_info = sched_info_alloc(map);
+ data->source_info = isl_calloc_array(ctx, struct isl_sched_info *,
+ data->count);
+
+ data->accesses = isl_access_info_alloc(isl_map_copy(map),
+ data->sink_info, &before, data->count);
+ if (!data->sink_info || (data->count && !data->source_info) ||
+ !data->accesses)
+ goto error;
+ data->accesses->coscheduled = &coscheduled;
+ data->count = 0;
+ data->must = 1;
+ if (isl_union_map_foreach_map(data->must_source,
+ &collect_matching_array, data) < 0)
+ goto error;
+ data->must = 0;
+ if (isl_union_map_foreach_map(data->may_source,
+ &collect_matching_array, data) < 0)
+ goto error;
+
+ flow = access_info_compute_flow_core(data->accesses);
+ data->accesses = NULL;
+
+ if (!flow)
+ goto error;
+
+ df->must_no_source = isl_union_map_union(df->must_no_source,
+ isl_union_map_from_map(isl_flow_get_no_source(flow, 1)));
+ df->may_no_source = isl_union_map_union(df->may_no_source,
+ isl_union_map_from_map(isl_flow_get_no_source(flow, 0)));
+
+ for (i = 0; i < flow->n_source; ++i) {
+ isl_union_map *dep;
+ dep = isl_union_map_from_map(isl_map_copy(flow->dep[i].map));
+ if (flow->dep[i].must)
+ df->must_dep = isl_union_map_union(df->must_dep, dep);
+ else
+ df->may_dep = isl_union_map_union(df->may_dep, dep);
+ }
+
+ isl_flow_free(flow);
+
+ sched_info_free(data->sink_info);
+ if (data->source_info) {
+ for (i = 0; i < data->count; ++i)
+ sched_info_free(data->source_info[i]);
+ free(data->source_info);
+ }
+ isl_space_free(data->dim);
+ isl_map_free(map);
+
+ return isl_stat_ok;
+error:
+ isl_access_info_free(data->accesses);
+ sched_info_free(data->sink_info);
+ if (data->source_info) {
+ for (i = 0; i < data->count; ++i)
+ sched_info_free(data->source_info[i]);
+ free(data->source_info);
+ }
+ isl_space_free(data->dim);
+ isl_map_free(map);
+
+ return isl_stat_error;
+}
+
+/* Add the kills of "info" to the must-sources.
+ */
+static __isl_give isl_union_access_info *
+isl_union_access_info_add_kill_to_must_source(
+ __isl_take isl_union_access_info *info)
+{
+ isl_union_map *must, *kill;
+
+ must = isl_union_access_info_get_must_source(info);
+ kill = isl_union_access_info_get_kill(info);
+ must = isl_union_map_union(must, kill);
+ return isl_union_access_info_set_must_source(info, must);
+}
+
+/* Drop dependences from "flow" that purely originate from kills.
+ * That is, only keep those dependences that originate from
+ * the original must-sources "must" and/or the original may-sources "may".
+ * In particular, "must" contains the must-sources from before
+ * the kills were added and "may" contains the may-source from before
+ * the kills were removed.
+ *
+ * The dependences are of the form
+ *
+ * Source -> [Sink -> Data]
+ *
+ * Only those dependences are kept where the Source -> Data part
+ * is a subset of the original may-sources or must-sources.
+ * Of those, only the must-dependences that intersect with the must-sources
+ * remain must-dependences.
+ * If there is some overlap between the may-sources and the must-sources,
+ * then the may-dependences and must-dependences may also overlap.
+ * This should be fine since the may-dependences are only kept
+ * disjoint from the must-dependences for the isl_union_map_compute_flow
+ * interface. This interface does not support kills, so it will
+ * not end up calling this function.
+ */
+static __isl_give isl_union_flow *isl_union_flow_drop_kill_source(
+ __isl_take isl_union_flow *flow, __isl_take isl_union_map *must,
+ __isl_take isl_union_map *may)
+{
+ isl_union_map *move;
+
+ if (!flow)
+ goto error;
+ move = isl_union_map_copy(flow->must_dep);
+ move = isl_union_map_intersect_range_factor_range(move,
+ isl_union_map_copy(may));
+ may = isl_union_map_union(may, isl_union_map_copy(must));
+ flow->may_dep = isl_union_map_intersect_range_factor_range(
+ flow->may_dep, may);
+ flow->must_dep = isl_union_map_intersect_range_factor_range(
+ flow->must_dep, must);
+ flow->may_dep = isl_union_map_union(flow->may_dep, move);
+ if (!flow->must_dep || !flow->may_dep)
+ return isl_union_flow_free(flow);
+
+ return flow;
+error:
+ isl_union_map_free(must);
+ isl_union_map_free(may);
+ return NULL;
+}
+
+/* Remove the must accesses from the may accesses.
+ *
+ * A must access always trumps a may access, so there is no need
+ * for a must access to also be considered as a may access. Doing so
+ * would only cost extra computations only to find out that
+ * the duplicated may access does not make any difference.
+ */
+static __isl_give isl_union_access_info *isl_union_access_info_normalize(
+ __isl_take isl_union_access_info *access)
+{
+ if (!access)
+ return NULL;
+ access->access[isl_access_may_source] =
+ isl_union_map_subtract(access->access[isl_access_may_source],
+ isl_union_map_copy(access->access[isl_access_must_source]));
+ if (!access->access[isl_access_may_source])
+ return isl_union_access_info_free(access);
+
+ return access;
+}
+
+/* Given a description of the "sink" accesses, the "source" accesses and
+ * a schedule, compute for each instance of a sink access
+ * and for each element accessed by that instance,
+ * the possible or definite source accesses that last accessed the
+ * element accessed by the sink access before this sink access
+ * in the sense that there is no intermediate definite source access.
+ *
+ * The must_no_source and may_no_source elements of the result
+ * are subsets of access->sink. The elements must_dep and may_dep
+ * map domain elements of access->{may,must)_source to
+ * domain elements of access->sink.
+ *
+ * This function is used when only the schedule map representation
+ * is available.
+ *
+ * We first prepend the schedule dimensions to the domain
+ * of the accesses so that we can easily compare their relative order.
+ * Then we consider each sink access individually in compute_flow.
+ */
+static __isl_give isl_union_flow *compute_flow_union_map(
+ __isl_take isl_union_access_info *access)
+{
+ struct isl_compute_flow_data data;
+ isl_union_map *sink;
+
+ access = isl_union_access_info_align_params(access);
+ access = isl_union_access_info_introduce_schedule(access);
+ if (!access)
+ return NULL;
+
+ data.must_source = access->access[isl_access_must_source];
+ data.may_source = access->access[isl_access_may_source];
+
+ sink = access->access[isl_access_sink];
+ data.flow = isl_union_flow_alloc(isl_union_map_get_space(sink));
+
+ if (isl_union_map_foreach_map(sink, &compute_flow, &data) < 0)
+ goto error;
+
+ data.flow = isl_union_flow_drop_schedule(data.flow);
+
+ isl_union_access_info_free(access);
+ return data.flow;
+error:
+ isl_union_access_info_free(access);
+ isl_union_flow_free(data.flow);
+ return NULL;
+}
+
+/* A schedule access relation.
+ *
+ * The access relation "access" is of the form [S -> D] -> A,
+ * where S corresponds to the prefix schedule at "node".
+ * "must" is only relevant for source accesses and indicates
+ * whether the access is a must source or a may source.
+ */
+struct isl_scheduled_access {
+ isl_map *access;
+ int must;
+ isl_schedule_node *node;
+};
+
+/* Data structure for keeping track of individual scheduled sink and source
+ * accesses when computing dependence analysis based on a schedule tree.
+ *
+ * "n_sink" is the number of used entries in "sink"
+ * "n_source" is the number of used entries in "source"
+ *
+ * "set_sink", "must" and "node" are only used inside collect_sink_source,
+ * to keep track of the current node and
+ * of what extract_sink_source needs to do.
+ */
+struct isl_compute_flow_schedule_data {
+ isl_union_access_info *access;
+
+ int n_sink;
+ int n_source;
+
+ struct isl_scheduled_access *sink;
+ struct isl_scheduled_access *source;
+
+ int set_sink;
+ int must;
+ isl_schedule_node *node;
+};
+
+/* Align the parameters of all sinks with all sources.
+ *
+ * If there are no sinks or no sources, then no alignment is needed.
+ */
+static void isl_compute_flow_schedule_data_align_params(
+ struct isl_compute_flow_schedule_data *data)
+{
+ int i;
+ isl_space *space;
+
+ if (data->n_sink == 0 || data->n_source == 0)
+ return;
+
+ space = isl_map_get_space(data->sink[0].access);
+
+ for (i = 1; i < data->n_sink; ++i)
+ space = isl_space_align_params(space,
+ isl_map_get_space(data->sink[i].access));
+ for (i = 0; i < data->n_source; ++i)
+ space = isl_space_align_params(space,
+ isl_map_get_space(data->source[i].access));
+
+ for (i = 0; i < data->n_sink; ++i)
+ data->sink[i].access =
+ isl_map_align_params(data->sink[i].access,
+ isl_space_copy(space));
+ for (i = 0; i < data->n_source; ++i)
+ data->source[i].access =
+ isl_map_align_params(data->source[i].access,
+ isl_space_copy(space));
+
+ isl_space_free(space);
+}
+
+/* Free all the memory referenced from "data".
+ * Do not free "data" itself as it may be allocated on the stack.
+ */
+static void isl_compute_flow_schedule_data_clear(
+ struct isl_compute_flow_schedule_data *data)
+{
+ int i;
+
+ if (!data->sink)
+ return;
+
+ for (i = 0; i < data->n_sink; ++i) {
+ isl_map_free(data->sink[i].access);
+ isl_schedule_node_free(data->sink[i].node);
+ }
+
+ for (i = 0; i < data->n_source; ++i) {
+ isl_map_free(data->source[i].access);
+ isl_schedule_node_free(data->source[i].node);
+ }
+
+ free(data->sink);
+}
+
+/* isl_schedule_foreach_schedule_node_top_down callback for counting
+ * (an upper bound on) the number of sinks and sources.
+ *
+ * Sinks and sources are only extracted at leaves of the tree,
+ * so we skip the node if it is not a leaf.
+ * Otherwise we increment data->n_sink and data->n_source with
+ * the number of spaces in the sink and source access domains
+ * that reach this node.
+ */
+static isl_bool count_sink_source(__isl_keep isl_schedule_node *node,
+ void *user)
+{
+ struct isl_compute_flow_schedule_data *data = user;
+ isl_union_set *domain;
+ isl_union_map *umap;
+ isl_bool r = isl_bool_false;
+ isl_size n;
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_leaf)
+ return isl_bool_true;
+
+ domain = isl_schedule_node_get_universe_domain(node);
+
+ umap = isl_union_map_copy(data->access->access[isl_access_sink]);
+ umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(domain));
+ data->n_sink += n = isl_union_map_n_map(umap);
+ isl_union_map_free(umap);
+ if (n < 0)
+ r = isl_bool_error;
+
+ umap = isl_union_map_copy(data->access->access[isl_access_must_source]);
+ umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(domain));
+ data->n_source += n = isl_union_map_n_map(umap);
+ isl_union_map_free(umap);
+ if (n < 0)
+ r = isl_bool_error;
+
+ umap = isl_union_map_copy(data->access->access[isl_access_may_source]);
+ umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(domain));
+ data->n_source += n = isl_union_map_n_map(umap);
+ isl_union_map_free(umap);
+ if (n < 0)
+ r = isl_bool_error;
+
+ isl_union_set_free(domain);
+
+ return r;
+}
+
+/* Add a single scheduled sink or source (depending on data->set_sink)
+ * with scheduled access relation "map", must property data->must and
+ * schedule node data->node to the list of sinks or sources.
+ */
+static isl_stat extract_sink_source(__isl_take isl_map *map, void *user)
+{
+ struct isl_compute_flow_schedule_data *data = user;
+ struct isl_scheduled_access *access;
+
+ if (data->set_sink)
+ access = data->sink + data->n_sink++;
+ else
+ access = data->source + data->n_source++;
+
+ access->access = map;
+ access->must = data->must;
+ access->node = isl_schedule_node_copy(data->node);
+
+ return isl_stat_ok;
+}
+
+/* isl_schedule_foreach_schedule_node_top_down callback for collecting
+ * individual scheduled source and sink accesses (taking into account
+ * the domain of the schedule).
+ *
+ * We only collect accesses at the leaves of the schedule tree.
+ * We prepend the schedule dimensions at the leaf to the iteration
+ * domains of the source and sink accesses and then extract
+ * the individual accesses (per space).
+ *
+ * In particular, if the prefix schedule at the node is of the form
+ *
+ * D -> S
+ *
+ * while the access relations are of the form
+ *
+ * D -> A
+ *
+ * then the updated access relations are of the form
+ *
+ * [S -> D] -> A
+ *
+ * Note that S consists of a single space such that introducing S
+ * in the access relations does not increase the number of spaces.
+ */
+static isl_bool collect_sink_source(__isl_keep isl_schedule_node *node,
+ void *user)
+{
+ struct isl_compute_flow_schedule_data *data = user;
+ isl_union_map *prefix;
+ isl_union_map *umap;
+ isl_bool r = isl_bool_false;
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_leaf)
+ return isl_bool_true;
+
+ data->node = node;
+
+ prefix = isl_schedule_node_get_prefix_schedule_relation(node);
+ prefix = isl_union_map_reverse(prefix);
+ prefix = isl_union_map_range_map(prefix);
+
+ data->set_sink = 1;
+ umap = isl_union_map_copy(data->access->access[isl_access_sink]);
+ umap = isl_union_map_apply_range(isl_union_map_copy(prefix), umap);
+ if (isl_union_map_foreach_map(umap, &extract_sink_source, data) < 0)
+ r = isl_bool_error;
+ isl_union_map_free(umap);
+
+ data->set_sink = 0;
+ data->must = 1;
+ umap = isl_union_map_copy(data->access->access[isl_access_must_source]);
+ umap = isl_union_map_apply_range(isl_union_map_copy(prefix), umap);
+ if (isl_union_map_foreach_map(umap, &extract_sink_source, data) < 0)
+ r = isl_bool_error;
+ isl_union_map_free(umap);
+
+ data->set_sink = 0;
+ data->must = 0;
+ umap = isl_union_map_copy(data->access->access[isl_access_may_source]);
+ umap = isl_union_map_apply_range(isl_union_map_copy(prefix), umap);
+ if (isl_union_map_foreach_map(umap, &extract_sink_source, data) < 0)
+ r = isl_bool_error;
+ isl_union_map_free(umap);
+
+ isl_union_map_free(prefix);
+
+ return r;
+}
+
+/* isl_access_info_compute_flow callback for determining whether
+ * the shared nesting level and the ordering within that level
+ * for two scheduled accesses for use in compute_single_flow.
+ *
+ * The tokens passed to this function refer to the leaves
+ * in the schedule tree where the accesses take place.
+ *
+ * If n is the shared number of loops, then we need to return
+ * "2 * n + 1" if "first" precedes "second" inside the innermost
+ * shared loop and "2 * n" otherwise.
+ *
+ * The innermost shared ancestor may be the leaves themselves
+ * if the accesses take place in the same leaf. Otherwise,
+ * it is either a set node or a sequence node. Only in the case
+ * of a sequence node do we consider one access to precede the other.
+ */
+static int before_node(void *first, void *second)
+{
+ isl_schedule_node *node1 = first;
+ isl_schedule_node *node2 = second;
+ isl_schedule_node *shared;
+ isl_size depth;
+ int before = 0;
+
+ shared = isl_schedule_node_get_shared_ancestor(node1, node2);
+ depth = isl_schedule_node_get_schedule_depth(shared);
+ if (depth < 0) {
+ isl_schedule_node_free(shared);
+ return -1;
+ }
+
+ if (isl_schedule_node_get_type(shared) == isl_schedule_node_sequence) {
+ isl_size pos1, pos2;
+
+ pos1 = isl_schedule_node_get_ancestor_child_position(node1,
+ shared);
+ pos2 = isl_schedule_node_get_ancestor_child_position(node2,
+ shared);
+ if (pos1 < 0 || pos2 < 0) {
+ isl_schedule_node_free(shared);
+ return -1;
+ }
+ before = pos1 < pos2;
+ }
+
+ isl_schedule_node_free(shared);
+
+ return 2 * depth + before;
+}
+
+/* Check if the given two accesses may be coscheduled.
+ * If so, return isl_bool_true. Otherwise return isl_bool_false.
+ *
+ * Two accesses may only be coscheduled if they appear in the same leaf.
+ */
+static isl_bool coscheduled_node(void *first, void *second)
+{
+ isl_schedule_node *node1 = first;
+ isl_schedule_node *node2 = second;
+
+ return isl_bool_ok(node1 == node2);
+}
+
+/* Add the scheduled sources from "data" that access
+ * the same data space as "sink" to "access".
+ */
+static __isl_give isl_access_info *add_matching_sources(
+ __isl_take isl_access_info *access, struct isl_scheduled_access *sink,
+ struct isl_compute_flow_schedule_data *data)
+{
+ int i;
+ isl_space *space;
+
+ space = isl_space_range(isl_map_get_space(sink->access));
+ for (i = 0; i < data->n_source; ++i) {
+ struct isl_scheduled_access *source;
+ isl_space *source_space;
+ int eq;
+
+ source = &data->source[i];
+ source_space = isl_map_get_space(source->access);
+ source_space = isl_space_range(source_space);
+ eq = isl_space_is_equal(space, source_space);
+ isl_space_free(source_space);
+
+ if (!eq)
+ continue;
+ if (eq < 0)
+ goto error;
+
+ access = isl_access_info_add_source(access,
+ isl_map_copy(source->access), source->must, source->node);
+ }
+
+ isl_space_free(space);
+ return access;
+error:
+ isl_space_free(space);
+ isl_access_info_free(access);
+ return NULL;
+}
+
+/* Given a scheduled sink access relation "sink", compute the corresponding
+ * dependences on the sources in "data" and add the computed dependences
+ * to "uf".
+ *
+ * The dependences computed by access_info_compute_flow_core are of the form
+ *
+ * [S -> I] -> [[S' -> I'] -> A]
+ *
+ * The schedule dimensions are projected out by first currying the range,
+ * resulting in
+ *
+ * [S -> I] -> [S' -> [I' -> A]]
+ *
+ * and then computing the factor range
+ *
+ * I -> [I' -> A]
+ */
+static __isl_give isl_union_flow *compute_single_flow(
+ __isl_take isl_union_flow *uf, struct isl_scheduled_access *sink,
+ struct isl_compute_flow_schedule_data *data)
+{
+ int i;
+ isl_access_info *access;
+ isl_flow *flow;
+ isl_map *map;
+
+ if (!uf)
+ return NULL;
+
+ access = isl_access_info_alloc(isl_map_copy(sink->access), sink->node,
+ &before_node, data->n_source);
+ if (access)
+ access->coscheduled = &coscheduled_node;
+ access = add_matching_sources(access, sink, data);
+
+ flow = access_info_compute_flow_core(access);
+ if (!flow)
+ return isl_union_flow_free(uf);
+
+ map = isl_map_domain_factor_range(isl_flow_get_no_source(flow, 1));
+ uf->must_no_source = isl_union_map_union(uf->must_no_source,
+ isl_union_map_from_map(map));
+ map = isl_map_domain_factor_range(isl_flow_get_no_source(flow, 0));
+ uf->may_no_source = isl_union_map_union(uf->may_no_source,
+ isl_union_map_from_map(map));
+
+ for (i = 0; i < flow->n_source; ++i) {
+ isl_union_map *dep;
+
+ map = isl_map_range_curry(isl_map_copy(flow->dep[i].map));
+ map = isl_map_factor_range(map);
+ dep = isl_union_map_from_map(map);
+ if (flow->dep[i].must)
+ uf->must_dep = isl_union_map_union(uf->must_dep, dep);
+ else
+ uf->may_dep = isl_union_map_union(uf->may_dep, dep);
+ }
+
+ isl_flow_free(flow);
+
+ return uf;
+}
+
+/* Given a description of the "sink" accesses, the "source" accesses and
+ * a schedule, compute for each instance of a sink access
+ * and for each element accessed by that instance,
+ * the possible or definite source accesses that last accessed the
+ * element accessed by the sink access before this sink access
+ * in the sense that there is no intermediate definite source access.
+ * Only consider dependences between statement instances that belong
+ * to the domain of the schedule.
+ *
+ * The must_no_source and may_no_source elements of the result
+ * are subsets of access->sink. The elements must_dep and may_dep
+ * map domain elements of access->{may,must)_source to
+ * domain elements of access->sink.
+ *
+ * This function is used when a schedule tree representation
+ * is available.
+ *
+ * We extract the individual scheduled source and sink access relations
+ * (taking into account the domain of the schedule) and
+ * then compute dependences for each scheduled sink individually.
+ */
+static __isl_give isl_union_flow *compute_flow_schedule(
+ __isl_take isl_union_access_info *access)
+{
+ struct isl_compute_flow_schedule_data data = { access };
+ int i, n;
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_union_flow *flow;
+
+ ctx = isl_union_access_info_get_ctx(access);
+
+ data.n_sink = 0;
+ data.n_source = 0;
+ if (isl_schedule_foreach_schedule_node_top_down(access->schedule,
+ &count_sink_source, &data) < 0)
+ goto error;
+
+ n = data.n_sink + data.n_source;
+ data.sink = isl_calloc_array(ctx, struct isl_scheduled_access, n);
+ if (n && !data.sink)
+ goto error;
+ data.source = data.sink + data.n_sink;
+
+ data.n_sink = 0;
+ data.n_source = 0;
+ if (isl_schedule_foreach_schedule_node_top_down(access->schedule,
+ &collect_sink_source, &data) < 0)
+ goto error;
+
+ space = isl_union_map_get_space(access->access[isl_access_sink]);
+ flow = isl_union_flow_alloc(space);
+
+ isl_compute_flow_schedule_data_align_params(&data);
+
+ for (i = 0; i < data.n_sink; ++i)
+ flow = compute_single_flow(flow, &data.sink[i], &data);
+
+ isl_compute_flow_schedule_data_clear(&data);
+
+ isl_union_access_info_free(access);
+ return flow;
+error:
+ isl_union_access_info_free(access);
+ isl_compute_flow_schedule_data_clear(&data);
+ return NULL;
+}
+
+/* Given a description of the "sink" accesses, the "source" accesses and
+ * a schedule, compute for each instance of a sink access
+ * and for each element accessed by that instance,
+ * the possible or definite source accesses that last accessed the
+ * element accessed by the sink access before this sink access
+ * in the sense that there is no intermediate definite source access.
+ *
+ * The must_no_source and may_no_source elements of the result
+ * are subsets of access->sink. The elements must_dep and may_dep
+ * map domain elements of access->{may,must)_source to
+ * domain elements of access->sink.
+ *
+ * If any kills have been specified, then they are treated as
+ * must-sources internally. Any dependence that purely derives
+ * from an original kill is removed from the output.
+ *
+ * We check whether the schedule is available as a schedule tree
+ * or a schedule map and call the corresponding function to perform
+ * the analysis.
+ */
+__isl_give isl_union_flow *isl_union_access_info_compute_flow(
+ __isl_take isl_union_access_info *access)
+{
+ isl_bool has_kill;
+ isl_union_map *must = NULL, *may = NULL;
+ isl_union_flow *flow;
+
+ has_kill = isl_union_access_has_kill(access);
+ if (has_kill < 0)
+ goto error;
+ if (has_kill) {
+ must = isl_union_access_info_get_must_source(access);
+ may = isl_union_access_info_get_may_source(access);
+ }
+ access = isl_union_access_info_add_kill_to_must_source(access);
+ access = isl_union_access_info_normalize(access);
+ if (!access)
+ goto error;
+ if (access->schedule)
+ flow = compute_flow_schedule(access);
+ else
+ flow = compute_flow_union_map(access);
+ if (has_kill)
+ flow = isl_union_flow_drop_kill_source(flow, must, may);
+ return flow;
+error:
+ isl_union_access_info_free(access);
+ isl_union_map_free(must);
+ isl_union_map_free(may);
+ return NULL;
+}
+
+/* Print the information contained in "flow" to "p".
+ * The information is printed as a YAML document.
+ */
+__isl_give isl_printer *isl_printer_print_union_flow(
+ __isl_take isl_printer *p, __isl_keep isl_union_flow *flow)
+{
+ isl_union_map *umap;
+
+ if (!flow)
+ return isl_printer_free(p);
+
+ p = isl_printer_yaml_start_mapping(p);
+ umap = isl_union_flow_get_full_must_dependence(flow);
+ p = print_yaml_field_union_map(p, "must_dependence", umap);
+ isl_union_map_free(umap);
+ umap = isl_union_flow_get_full_may_dependence(flow);
+ p = print_yaml_field_union_map(p, "may_dependence", umap);
+ isl_union_map_free(umap);
+ p = print_yaml_field_union_map(p, "must_no_source",
+ flow->must_no_source);
+ umap = isl_union_flow_get_may_no_source(flow);
+ p = print_yaml_field_union_map(p, "may_no_source", umap);
+ isl_union_map_free(umap);
+ p = isl_printer_yaml_end_mapping(p);
+
+ return p;
+}
+
+/* Return a string representation of the information in "flow".
+ * The information is printed in flow format.
+ */
+__isl_give char *isl_union_flow_to_str(__isl_keep isl_union_flow *flow)
+{
+ isl_printer *p;
+ char *s;
+
+ if (!flow)
+ return NULL;
+
+ p = isl_printer_to_str(isl_union_flow_get_ctx(flow));
+ p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_FLOW);
+ p = isl_printer_print_union_flow(p, flow);
+ s = isl_printer_get_str(p);
+ isl_printer_free(p);
+
+ return s;
+}
+
+/* Given a collection of "sink" and "source" accesses,
+ * compute for each iteration of a sink access
+ * and for each element accessed by that iteration,
+ * the source access in the list that last accessed the
+ * element accessed by the sink access before this sink access.
+ * Each access is given as a map from the loop iterators
+ * to the array indices.
+ * The result is a relations between source and sink
+ * iterations and a subset of the domain of the sink accesses,
+ * corresponding to those iterations that access an element
+ * not previously accessed.
+ *
+ * We collect the inputs in an isl_union_access_info object,
+ * call isl_union_access_info_compute_flow and extract
+ * the outputs from the result.
+ */
+int isl_union_map_compute_flow(__isl_take isl_union_map *sink,
+ __isl_take isl_union_map *must_source,
+ __isl_take isl_union_map *may_source,
+ __isl_take isl_union_map *schedule,
+ __isl_give isl_union_map **must_dep, __isl_give isl_union_map **may_dep,
+ __isl_give isl_union_map **must_no_source,
+ __isl_give isl_union_map **may_no_source)
+{
+ isl_union_access_info *access;
+ isl_union_flow *flow;
+
+ access = isl_union_access_info_from_sink(sink);
+ access = isl_union_access_info_set_must_source(access, must_source);
+ access = isl_union_access_info_set_may_source(access, may_source);
+ access = isl_union_access_info_set_schedule_map(access, schedule);
+ flow = isl_union_access_info_compute_flow(access);
+
+ if (must_dep)
+ *must_dep = isl_union_flow_get_must_dependence(flow);
+ if (may_dep)
+ *may_dep = isl_union_flow_get_non_must_dependence(flow);
+ if (must_no_source)
+ *must_no_source = isl_union_flow_get_must_no_source(flow);
+ if (may_no_source)
+ *may_no_source = isl_union_flow_get_non_must_no_source(flow);
+
+ isl_union_flow_free(flow);
+
+ if ((must_dep && !*must_dep) || (may_dep && !*may_dep) ||
+ (must_no_source && !*must_no_source) ||
+ (may_no_source && !*may_no_source))
+ goto error;
+
+ return 0;
+error:
+ if (must_dep)
+ *must_dep = isl_union_map_free(*must_dep);
+ if (may_dep)
+ *may_dep = isl_union_map_free(*may_dep);
+ if (must_no_source)
+ *must_no_source = isl_union_map_free(*must_no_source);
+ if (may_no_source)
+ *may_no_source = isl_union_map_free(*may_no_source);
+ return -1;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_fold.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_fold.c
new file mode 100644
index 00000000000..2d7f85c0fd5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_fold.c
@@ -0,0 +1,2183 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_map_private.h>
+#include <isl_union_map_private.h>
+#include <isl_polynomial_private.h>
+#include <isl_point_private.h>
+#include <isl_space_private.h>
+#include <isl_lp_private.h>
+#include <isl_seq.h>
+#include <isl_mat_private.h>
+#include <isl_val_private.h>
+#include <isl_vec_private.h>
+#include <isl_config.h>
+
+#undef EL_BASE
+#define EL_BASE pw_qpolynomial_fold
+
+#include <isl_list_templ.c>
+
+enum isl_fold isl_fold_type_negate(enum isl_fold type)
+{
+ switch (type) {
+ case isl_fold_error:
+ return isl_fold_error;
+ case isl_fold_min:
+ return isl_fold_max;
+ case isl_fold_max:
+ return isl_fold_min;
+ case isl_fold_list:
+ return isl_fold_list;
+ }
+
+ isl_die(NULL, isl_error_internal, "unhandled isl_fold type", abort());
+}
+
+/* Construct a new reduction with the given type, domain space and
+ * list of polynomials.
+ */
+static __isl_give isl_qpolynomial_fold *qpolynomial_fold_alloc(
+ enum isl_fold type, __isl_take isl_space *space,
+ __isl_take isl_qpolynomial_list *list)
+{
+ isl_ctx *ctx;
+ isl_qpolynomial_fold *fold;
+
+ if (type < 0 || !space || !list)
+ goto error;
+
+ ctx = isl_space_get_ctx(space);
+ fold = isl_calloc_type(ctx, struct isl_qpolynomial_fold);
+ if (!fold)
+ goto error;
+
+ fold->ref = 1;
+ fold->type = type;
+ fold->dim = space;
+ fold->list = list;
+
+ return fold;
+error:
+ isl_space_free(space);
+ isl_qpolynomial_list_free(list);
+ return NULL;
+}
+
+isl_ctx *isl_qpolynomial_fold_get_ctx(__isl_keep isl_qpolynomial_fold *fold)
+{
+ return fold ? fold->dim->ctx : NULL;
+}
+
+/* Return the domain space of "fold".
+ */
+static __isl_keep isl_space *isl_qpolynomial_fold_peek_domain_space(
+ __isl_keep isl_qpolynomial_fold *fold)
+{
+ return fold ? fold->dim : NULL;
+}
+
+__isl_give isl_space *isl_qpolynomial_fold_get_domain_space(
+ __isl_keep isl_qpolynomial_fold *fold)
+{
+ return isl_space_copy(isl_qpolynomial_fold_peek_domain_space(fold));
+}
+
+/* Return the space of the domain of "fold".
+ * This may be either a copy or the space itself
+ * if there is only one reference to "fold".
+ * This allows the space to be modified inplace
+ * if both the expression and its space have only a single reference.
+ * The caller is not allowed to modify "fold" between this call and
+ * a subsequent call to isl_qpolynomial_fold_restore_domain_space.
+ * The only exception is that isl_qpolynomial_fold_free can be called instead.
+ */
+static __isl_give isl_space *isl_qpolynomial_fold_take_domain_space(
+ __isl_keep isl_qpolynomial_fold *fold)
+{
+ isl_space *space;
+
+ if (!fold)
+ return NULL;
+ if (fold->ref != 1)
+ return isl_qpolynomial_fold_get_domain_space(fold);
+ space = fold->dim;
+ fold->dim = NULL;
+ return space;
+}
+
+/* Set the space of the domain of "fold" to "space",
+ * where the space of "fold" may be missing
+ * due to a preceding call to isl_qpolynomial_fold_take_domain_space.
+ * However, in this case, "fold" only has a single reference and
+ * then the call to isl_qpolynomial_fold_cow has no effect.
+ */
+static
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_restore_domain_space(
+ __isl_keep isl_qpolynomial_fold *fold, __isl_take isl_space *space)
+{
+ if (!fold || !space)
+ goto error;
+
+ if (fold->dim == space) {
+ isl_space_free(space);
+ return fold;
+ }
+
+ fold = isl_qpolynomial_fold_cow(fold);
+ if (!fold)
+ goto error;
+ isl_space_free(fold->dim);
+ fold->dim = space;
+
+ return fold;
+error:
+ isl_qpolynomial_fold_free(fold);
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_space *isl_qpolynomial_fold_get_space(
+ __isl_keep isl_qpolynomial_fold *fold)
+{
+ isl_space *space;
+ if (!fold)
+ return NULL;
+ space = isl_space_copy(fold->dim);
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, 1);
+ return space;
+}
+
+/* Return the list of polynomials in the reduction "fold".
+ */
+__isl_keep isl_qpolynomial_list *isl_qpolynomial_fold_peek_list(
+ __isl_keep isl_qpolynomial_fold *fold)
+{
+ return fold ? fold->list : NULL;
+}
+
+/* Return a copy of the list of polynomials in the reduction "fold".
+ */
+static __isl_give isl_qpolynomial_list *isl_qpolynomial_fold_get_list(
+ __isl_keep isl_qpolynomial_fold *fold)
+{
+ return isl_qpolynomial_list_copy(isl_qpolynomial_fold_peek_list(fold));
+}
+
+/* Return the list of polynomials of "fold".
+ * This may be either a copy or the list itself
+ * if there is only one reference to "fold".
+ * This allows the list to be modified inplace
+ * if both the expression and its list have only a single reference.
+ * The caller is not allowed to modify "fold" between this call and
+ * a subsequent call to isl_qpolynomial_fold_restore_list.
+ * The only exception is that isl_qpolynomial_fold_free can be called instead.
+ */
+static __isl_give isl_qpolynomial_list *isl_qpolynomial_fold_take_list(
+ __isl_keep isl_qpolynomial_fold *fold)
+{
+ isl_qpolynomial_list *list;
+
+ if (!fold)
+ return NULL;
+ if (fold->ref != 1)
+ return isl_qpolynomial_fold_get_list(fold);
+ list = fold->list;
+ fold->list = NULL;
+ return list;
+}
+
+/* Set the space of the list of polynomials of "fold" to "space",
+ * where the list of polynomials of "fold" may be missing
+ * due to a preceding call to isl_qpolynomial_fold_take_list.
+ * However, in this case, "fold" only has a single reference and
+ * then the call to isl_qpolynomial_fold_cow has no effect.
+ */
+static __isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_restore_list(
+ __isl_keep isl_qpolynomial_fold *fold,
+ __isl_take isl_qpolynomial_list *list)
+{
+ if (!fold || !list)
+ goto error;
+
+ if (fold->list == list) {
+ isl_qpolynomial_list_free(list);
+ return fold;
+ }
+
+ fold = isl_qpolynomial_fold_cow(fold);
+ if (!fold)
+ goto error;
+ isl_qpolynomial_list_free(fold->list);
+ fold->list = list;
+
+ return fold;
+error:
+ isl_qpolynomial_fold_free(fold);
+ isl_qpolynomial_list_free(list);
+ return NULL;
+}
+
+/* isl_qpolynomial_list_map callback that calls
+ * isl_qpolynomial_reset_domain_space on "qp".
+ */
+static __isl_give isl_qpolynomial *reset_domain_space(
+ __isl_take isl_qpolynomial *qp, void *user)
+{
+ isl_space *space = user;
+
+ return isl_qpolynomial_reset_domain_space(qp, isl_space_copy(space));
+}
+
+/* Replace the domain space of "fold" by "space".
+ *
+ * Replace the domain space itself and that of all polynomials
+ * in the list.
+ */
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_reset_domain_space(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *space)
+{
+ isl_qpolynomial_list *list;
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &reset_domain_space, space);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ isl_space_free(isl_qpolynomial_fold_take_domain_space(fold));
+ fold = isl_qpolynomial_fold_restore_domain_space(fold, space);
+
+ return fold;
+}
+
+/* Reset the space of "fold". This function is called from isl_pw_templ.c
+ * and doesn't know if the space of an element object is represented
+ * directly or through its domain. It therefore passes along both.
+ */
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_reset_space_and_domain(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *space,
+ __isl_take isl_space *domain)
+{
+ isl_space_free(space);
+ return isl_qpolynomial_fold_reset_domain_space(fold, domain);
+}
+
+/* Internal data structure for isl_qpolynomial_fold_*_dims
+ * representing their arguments.
+ */
+struct isl_fold_dims_data {
+ enum isl_dim_type type;
+ unsigned first;
+ unsigned n;
+};
+
+/* isl_qpolynomial_list_every callback that checks whether "qp"
+ * does not involve any dimensions in the given range.
+ */
+static isl_bool not_involved(__isl_keep isl_qpolynomial *qp, void *user)
+{
+ struct isl_fold_dims_data *data = user;
+ isl_bool involves;
+
+ involves = isl_qpolynomial_involves_dims(qp, data->type,
+ data->first, data->n);
+ return isl_bool_not(involves);
+}
+
+/* Does "fold" involve any dimensions in the given range.
+ *
+ * It involves any of those dimensions if it is not the case
+ * that every polynomial in the reduction does not involve
+ * any of the dimensions.
+ */
+static isl_bool isl_qpolynomial_fold_involves_dims(
+ __isl_keep isl_qpolynomial_fold *fold,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ struct isl_fold_dims_data data = { type, first, n };
+ isl_qpolynomial_list *list;
+ isl_bool not;
+
+ if (!fold)
+ return isl_bool_error;
+ if (n == 0)
+ return isl_bool_false;
+
+ list = isl_qpolynomial_fold_peek_list(fold);
+ not = isl_qpolynomial_list_every(list, &not_involved, &data);
+ return isl_bool_not(not);
+}
+
+/* Internal data structure for isl_qpolynomial_fold_set_dim_name
+ * representing its arguments.
+ */
+struct isl_fold_set_dim_name_data {
+ enum isl_dim_type type;
+ unsigned pos;
+ const char *s;
+};
+
+/* isl_qpolynomial_list_map callback for calling
+ * isl_qpolynomial_set_dim_name on "qp".
+ */
+static __isl_give isl_qpolynomial *set_dim_name(__isl_take isl_qpolynomial *qp,
+ void *user)
+{
+ struct isl_fold_set_dim_name_data *data = user;
+
+ qp = isl_qpolynomial_set_dim_name(qp, data->type, data->pos, data->s);
+ return qp;
+}
+
+/* Given a dimension type for an isl_qpolynomial_fold,
+ * return the corresponding type for the domain.
+ */
+static enum isl_dim_type domain_type(enum isl_dim_type type)
+{
+ if (type == isl_dim_in)
+ return isl_dim_set;
+ return type;
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_set_dim_name(
+ __isl_take isl_qpolynomial_fold *fold,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ struct isl_fold_set_dim_name_data data = { type, pos, s };
+ enum isl_dim_type set_type;
+ isl_space *space;
+ isl_qpolynomial_list *list;
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &set_dim_name, &data);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ set_type = domain_type(type);
+ space = isl_qpolynomial_fold_take_domain_space(fold);
+ space = isl_space_set_dim_name(space, set_type, pos, s);
+ fold = isl_qpolynomial_fold_restore_domain_space(fold, space);
+
+ return fold;
+}
+
+/* isl_qpolynomial_list_map callback for calling
+ * isl_qpolynomial_drop_dims on "qp".
+ */
+static __isl_give isl_qpolynomial *drop_dims(__isl_take isl_qpolynomial *qp,
+ void *user)
+{
+ struct isl_fold_dims_data *data = user;
+
+ qp = isl_qpolynomial_drop_dims(qp, data->type, data->first, data->n);
+ return qp;
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_drop_dims(
+ __isl_take isl_qpolynomial_fold *fold,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ struct isl_fold_dims_data data = { type, first, n };
+ enum isl_dim_type set_type;
+ isl_space *space;
+ isl_qpolynomial_list *list;
+
+ if (!fold)
+ return NULL;
+ if (n == 0)
+ return fold;
+
+ set_type = domain_type(type);
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &drop_dims, &data);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ space = isl_qpolynomial_fold_take_domain_space(fold);
+ space = isl_space_drop_dims(space, set_type, first, n);
+ fold = isl_qpolynomial_fold_restore_domain_space(fold, space);
+
+ return fold;
+}
+
+/* isl_qpolynomial_list_map callback for calling
+ * isl_qpolynomial_insert_dims on "qp".
+ */
+static __isl_give isl_qpolynomial *insert_dims(__isl_take isl_qpolynomial *qp,
+ void *user)
+{
+ struct isl_fold_dims_data *data = user;
+
+ qp = isl_qpolynomial_insert_dims(qp, data->type, data->first, data->n);
+ return qp;
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_insert_dims(
+ __isl_take isl_qpolynomial_fold *fold,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ struct isl_fold_dims_data data = { type, first, n };
+ enum isl_dim_type set_type;
+ isl_space *space;
+ isl_qpolynomial_list *list;
+
+ if (!fold)
+ return NULL;
+ if (n == 0 && !isl_space_is_named_or_nested(fold->dim, type))
+ return fold;
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &insert_dims, &data);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ set_type = domain_type(type);
+ space = isl_qpolynomial_fold_take_domain_space(fold);
+ space = isl_space_insert_dims(space, set_type, first, n);
+ fold = isl_qpolynomial_fold_restore_domain_space(fold, space);
+
+ return fold;
+}
+
+/* Determine the sign of the constant quasipolynomial "qp".
+ *
+ * Return
+ * -1 if qp <= 0
+ * 1 if qp >= 0
+ * 0 if unknown
+ *
+ * For qp == 0, we can return either -1 or 1. In practice, we return 1.
+ * For qp == NaN, the sign is undefined, so we return 0.
+ */
+static int isl_qpolynomial_cst_sign(__isl_keep isl_qpolynomial *qp)
+{
+ isl_poly_cst *cst;
+
+ if (isl_qpolynomial_is_nan(qp))
+ return 0;
+
+ cst = isl_poly_as_cst(qp->poly);
+ if (!cst)
+ return 0;
+
+ return isl_int_sgn(cst->n) < 0 ? -1 : 1;
+}
+
+static int isl_qpolynomial_aff_sign(__isl_keep isl_set *set,
+ __isl_keep isl_qpolynomial *qp)
+{
+ enum isl_lp_result res;
+ isl_vec *aff;
+ isl_int opt;
+ int sgn = 0;
+
+ aff = isl_qpolynomial_extract_affine(qp);
+ if (!aff)
+ return 0;
+
+ isl_int_init(opt);
+
+ res = isl_set_solve_lp(set, 0, aff->el + 1, aff->el[0],
+ &opt, NULL, NULL);
+ if (res == isl_lp_error)
+ goto done;
+ if (res == isl_lp_empty ||
+ (res == isl_lp_ok && !isl_int_is_neg(opt))) {
+ sgn = 1;
+ goto done;
+ }
+
+ res = isl_set_solve_lp(set, 1, aff->el + 1, aff->el[0],
+ &opt, NULL, NULL);
+ if (res == isl_lp_ok && !isl_int_is_pos(opt))
+ sgn = -1;
+
+done:
+ isl_int_clear(opt);
+ isl_vec_free(aff);
+ return sgn;
+}
+
+/* Determine, if possible, the sign of the quasipolynomial "qp" on
+ * the domain "set".
+ *
+ * If qp is a constant, then the problem is trivial.
+ * If qp is linear, then we check if the minimum of the corresponding
+ * affine constraint is non-negative or if the maximum is non-positive.
+ *
+ * Otherwise, we check if the outermost variable "v" has a lower bound "l"
+ * in "set". If so, we write qp(v,v') as
+ *
+ * q(v,v') * (v - l) + r(v')
+ *
+ * if q(v,v') and r(v') have the same known sign, then the original
+ * quasipolynomial has the same sign as well.
+ *
+ * Return
+ * -1 if qp <= 0
+ * 1 if qp >= 0
+ * 0 if unknown
+ */
+static int isl_qpolynomial_sign(__isl_keep isl_set *set,
+ __isl_keep isl_qpolynomial *qp)
+{
+ isl_size d;
+ int i;
+ isl_bool is;
+ isl_poly_rec *rec;
+ isl_vec *v;
+ isl_int l;
+ enum isl_lp_result res;
+ int sgn = 0;
+
+ is = isl_qpolynomial_is_cst(qp, NULL, NULL);
+ if (is < 0)
+ return 0;
+ if (is)
+ return isl_qpolynomial_cst_sign(qp);
+
+ is = isl_qpolynomial_is_affine(qp);
+ if (is < 0)
+ return 0;
+ if (is)
+ return isl_qpolynomial_aff_sign(set, qp);
+
+ if (qp->div->n_row > 0)
+ return 0;
+
+ rec = isl_poly_as_rec(qp->poly);
+ if (!rec)
+ return 0;
+
+ d = isl_space_dim(qp->dim, isl_dim_all);
+ if (d < 0)
+ return 0;
+ v = isl_vec_alloc(set->ctx, 2 + d);
+ if (!v)
+ return 0;
+
+ isl_seq_clr(v->el + 1, 1 + d);
+ isl_int_set_si(v->el[0], 1);
+ isl_int_set_si(v->el[2 + qp->poly->var], 1);
+
+ isl_int_init(l);
+
+ res = isl_set_solve_lp(set, 0, v->el + 1, v->el[0], &l, NULL, NULL);
+ if (res == isl_lp_ok) {
+ isl_qpolynomial *min;
+ isl_qpolynomial *base;
+ isl_qpolynomial *r, *q;
+ isl_qpolynomial *t;
+
+ min = isl_qpolynomial_cst_on_domain(isl_space_copy(qp->dim), l);
+ base = isl_qpolynomial_var_pow_on_domain(isl_space_copy(qp->dim),
+ qp->poly->var, 1);
+
+ r = isl_qpolynomial_alloc(isl_space_copy(qp->dim), 0,
+ isl_poly_copy(rec->p[rec->n - 1]));
+ q = isl_qpolynomial_copy(r);
+
+ for (i = rec->n - 2; i >= 0; --i) {
+ r = isl_qpolynomial_mul(r, isl_qpolynomial_copy(min));
+ t = isl_qpolynomial_alloc(isl_space_copy(qp->dim), 0,
+ isl_poly_copy(rec->p[i]));
+ r = isl_qpolynomial_add(r, t);
+ if (i == 0)
+ break;
+ q = isl_qpolynomial_mul(q, isl_qpolynomial_copy(base));
+ q = isl_qpolynomial_add(q, isl_qpolynomial_copy(r));
+ }
+
+ if (isl_qpolynomial_is_zero(q))
+ sgn = isl_qpolynomial_sign(set, r);
+ else if (isl_qpolynomial_is_zero(r))
+ sgn = isl_qpolynomial_sign(set, q);
+ else {
+ int sgn_q, sgn_r;
+ sgn_r = isl_qpolynomial_sign(set, r);
+ sgn_q = isl_qpolynomial_sign(set, q);
+ if (sgn_r == sgn_q)
+ sgn = sgn_r;
+ }
+
+ isl_qpolynomial_free(min);
+ isl_qpolynomial_free(base);
+ isl_qpolynomial_free(q);
+ isl_qpolynomial_free(r);
+ }
+
+ isl_int_clear(l);
+
+ isl_vec_free(v);
+
+ return sgn;
+}
+
+/* Check that "fold1" and "fold2" have the same type.
+ */
+static isl_stat isl_qpolynomial_fold_check_equal_type(
+ __isl_keep isl_qpolynomial_fold *fold1,
+ __isl_keep isl_qpolynomial_fold *fold2)
+{
+ enum isl_fold type1, type2;
+
+ type1 = isl_qpolynomial_fold_get_type(fold1);
+ type2 = isl_qpolynomial_fold_get_type(fold2);
+ if (type1 < 0 || type2 < 0)
+ return isl_stat_error;
+ if (type1 != type2)
+ isl_die(isl_qpolynomial_fold_get_ctx(fold1), isl_error_invalid,
+ "fold types don't match", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Check that "fold1" and "fold2" have the same (domain) space.
+ */
+static isl_stat isl_qpolynomial_fold_check_equal_space(
+ __isl_keep isl_qpolynomial_fold *fold1,
+ __isl_keep isl_qpolynomial_fold *fold2)
+{
+ isl_bool equal;
+ isl_space *space1, *space2;
+
+ space1 = isl_qpolynomial_fold_peek_domain_space(fold1);
+ space2 = isl_qpolynomial_fold_peek_domain_space(fold2);
+ equal = isl_space_is_equal(space1, space2);
+ if (equal < 0)
+ return isl_stat_error;
+ if (!equal)
+ isl_die(isl_qpolynomial_fold_get_ctx(fold1), isl_error_invalid,
+ "spaces don't match", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Combine "list1" and "list2" into a single list, eliminating
+ * those elements of one list that are already covered by the other
+ * list on "set".
+ *
+ * "better" is the sign that the difference qp1 - qp2 needs to have for qp1
+ * to be covered by qp2.
+ */
+static __isl_give isl_qpolynomial_list *merge_lists(__isl_keep isl_set *set,
+ __isl_take isl_qpolynomial_list *list1,
+ __isl_take isl_qpolynomial_list *list2, int better)
+{
+ int i, j;
+ isl_size n1, n2;
+
+ n1 = isl_qpolynomial_list_size(list1);
+ n2 = isl_qpolynomial_list_size(list2);
+ if (n1 < 0 || n2 < 0)
+ goto error;
+
+ for (i = n2 - 1; i >= 0; --i) {
+ for (j = n1 - 1; j >= 0; --j) {
+ isl_qpolynomial *qp1, *qp2, *d;
+ int sgn;
+ isl_bool equal;
+
+ qp1 = isl_qpolynomial_list_peek(list1, j);
+ qp2 = isl_qpolynomial_list_peek(list2, i);
+ equal = isl_qpolynomial_plain_is_equal(qp1, qp2);
+ if (equal < 0)
+ goto error;
+ if (equal)
+ break;
+ d = isl_qpolynomial_sub(
+ isl_qpolynomial_copy(qp1),
+ isl_qpolynomial_copy(qp2));
+ sgn = isl_qpolynomial_sign(set, d);
+ isl_qpolynomial_free(d);
+ if (sgn == 0)
+ continue;
+ if (sgn != better)
+ break;
+ list1 = isl_qpolynomial_list_drop(list1, j, 1);
+ n1--;
+ }
+ if (j < 0)
+ continue;
+ list2 = isl_qpolynomial_list_drop(list2, i, 1);
+ n2--;
+ }
+
+ return isl_qpolynomial_list_concat(list1, list2);
+error:
+ isl_qpolynomial_list_free(list1);
+ isl_qpolynomial_list_free(list2);
+ return NULL;
+}
+
+/* Combine "fold1" and "fold2" into a single reduction, eliminating
+ * those elements of one reduction that are already covered by the other
+ * reduction on "set".
+ *
+ * If "fold1" or "fold2" is an empty reduction, then return
+ * the other reduction.
+ * If "fold1" or "fold2" is a NaN, then return this NaN.
+ */
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_fold_on_domain(
+ __isl_keep isl_set *set,
+ __isl_take isl_qpolynomial_fold *fold1,
+ __isl_take isl_qpolynomial_fold *fold2)
+{
+ isl_qpolynomial_list *list1;
+ isl_qpolynomial_list *list2;
+ int better;
+
+ if (isl_qpolynomial_fold_check_equal_type(fold1, fold2) < 0)
+ goto error;
+ if (isl_qpolynomial_fold_check_equal_space(fold1, fold2) < 0)
+ goto error;
+
+ better = fold1->type == isl_fold_max ? -1 : 1;
+
+ if (isl_qpolynomial_fold_is_empty(fold1) ||
+ isl_qpolynomial_fold_is_nan(fold2)) {
+ isl_qpolynomial_fold_free(fold1);
+ return fold2;
+ }
+
+ if (isl_qpolynomial_fold_is_empty(fold2) ||
+ isl_qpolynomial_fold_is_nan(fold1)) {
+ isl_qpolynomial_fold_free(fold2);
+ return fold1;
+ }
+
+ list1 = isl_qpolynomial_fold_take_list(fold1);
+ list2 = isl_qpolynomial_fold_take_list(fold2);
+
+ list1 = merge_lists(set, list1, list2, better);
+
+ fold1 = isl_qpolynomial_fold_restore_list(fold1, list1);
+ isl_qpolynomial_fold_free(fold2);
+
+ return fold1;
+error:
+ isl_qpolynomial_fold_free(fold1);
+ isl_qpolynomial_fold_free(fold2);
+ return NULL;
+}
+
+/* isl_qpolynomial_list_map callback for adding "qp2" to "qp".
+ */
+static __isl_give isl_qpolynomial *add_qpolynomial(
+ __isl_take isl_qpolynomial *qp, void *user)
+{
+ isl_qpolynomial *qp2 = user;
+
+ return isl_qpolynomial_add(qp, isl_qpolynomial_copy(qp2));
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_add_qpolynomial(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_qpolynomial *qp)
+{
+ isl_qpolynomial_list *list;
+
+ if (!fold || !qp)
+ goto error;
+
+ if (isl_qpolynomial_is_zero(qp)) {
+ isl_qpolynomial_free(qp);
+ return fold;
+ }
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &add_qpolynomial, qp);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ isl_qpolynomial_free(qp);
+ return fold;
+error:
+ isl_qpolynomial_fold_free(fold);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_add_on_domain(
+ __isl_keep isl_set *dom,
+ __isl_take isl_qpolynomial_fold *fold1,
+ __isl_take isl_qpolynomial_fold *fold2)
+{
+ int i;
+ isl_size n1, n2;
+ isl_qpolynomial_fold *res = NULL;
+ isl_qpolynomial *qp;
+ isl_qpolynomial_list *list1, *list2;
+
+ if (!fold1 || !fold2)
+ goto error;
+
+ if (isl_qpolynomial_fold_is_empty(fold1)) {
+ isl_qpolynomial_fold_free(fold1);
+ return fold2;
+ }
+
+ if (isl_qpolynomial_fold_is_empty(fold2)) {
+ isl_qpolynomial_fold_free(fold2);
+ return fold1;
+ }
+
+ list1 = isl_qpolynomial_fold_peek_list(fold1);
+ list2 = isl_qpolynomial_fold_peek_list(fold2);
+ n1 = isl_qpolynomial_list_size(list1);
+ n2 = isl_qpolynomial_list_size(list2);
+ if (n1 < 0 || n2 < 0)
+ goto error;
+
+ if (n1 == 1 && n2 != 1)
+ return isl_qpolynomial_fold_add_on_domain(dom, fold2, fold1);
+
+ qp = isl_qpolynomial_list_get_at(list2, 0);
+ if (n2 == 1) {
+ res = isl_qpolynomial_fold_add_qpolynomial(fold1, qp);
+ isl_qpolynomial_fold_free(fold2);
+ return res;
+ }
+
+ res = isl_qpolynomial_fold_add_qpolynomial(
+ isl_qpolynomial_fold_copy(fold1), qp);
+
+ for (i = 1; i < n2; ++i) {
+ isl_qpolynomial_fold *res_i;
+
+ qp = isl_qpolynomial_list_get_at(list2, i);
+ res_i = isl_qpolynomial_fold_add_qpolynomial(
+ isl_qpolynomial_fold_copy(fold1), qp);
+ res = isl_qpolynomial_fold_fold_on_domain(dom, res, res_i);
+ }
+
+ isl_qpolynomial_fold_free(fold1);
+ isl_qpolynomial_fold_free(fold2);
+ return res;
+error:
+ isl_qpolynomial_fold_free(res);
+ isl_qpolynomial_fold_free(fold1);
+ isl_qpolynomial_fold_free(fold2);
+ return NULL;
+}
+
+/* isl_qpolynomial_list_map callback for calling
+ * isl_qpolynomial_substitute_equalities on "qp" and "eq".
+ */
+static __isl_give isl_qpolynomial *substitute_equalities(
+ __isl_take isl_qpolynomial *qp, void *user)
+{
+ isl_basic_set *eq = user;
+
+ eq = isl_basic_set_copy(eq);
+ return isl_qpolynomial_substitute_equalities(qp, eq);
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_substitute_equalities(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_basic_set *eq)
+{
+ isl_qpolynomial_list *list;
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &substitute_equalities, eq);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ isl_basic_set_free(eq);
+ return fold;
+}
+
+/* isl_qpolynomial_list_map callback for calling
+ * isl_qpolynomial_substitute_equalities on "qp" and "context".
+ */
+static __isl_give isl_qpolynomial *gist(__isl_take isl_qpolynomial *qp,
+ void *user)
+{
+ isl_set *context = user;
+
+ return isl_qpolynomial_gist(qp, isl_set_copy(context));
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_gist(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *context)
+{
+ isl_qpolynomial_list *list;
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &gist, context);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ isl_set_free(context);
+ return fold;
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_gist_params(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *context)
+{
+ isl_space *space = isl_qpolynomial_fold_get_domain_space(fold);
+ isl_set *dom_context = isl_set_universe(space);
+ dom_context = isl_set_intersect_params(dom_context, context);
+ return isl_qpolynomial_fold_gist(fold, dom_context);
+}
+
+/* Return a zero (i.e., empty) isl_qpolynomial_fold in the given space.
+ *
+ * This is a helper function for isl_pw_*_as_* that ensures a uniform
+ * interface over all piecewise types.
+ */
+static __isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_zero_in_space(
+ __isl_take isl_space *space, enum isl_fold type)
+{
+ return isl_qpolynomial_fold_empty(type, isl_space_domain(space));
+}
+
+#define isl_qpolynomial_fold_involves_nan isl_qpolynomial_fold_is_nan
+
+#define HAS_TYPE
+
+#undef PW
+#define PW isl_pw_qpolynomial_fold
+#undef BASE
+#define BASE qpolynomial_fold
+#undef EL_IS_ZERO
+#define EL_IS_ZERO is_empty
+#undef ZERO
+#define ZERO zero
+#undef IS_ZERO
+#define IS_ZERO is_zero
+#undef FIELD
+#define FIELD fold
+#undef DEFAULT_IS_ZERO
+#define DEFAULT_IS_ZERO 1
+
+#include <isl_pw_templ.c>
+#include <isl_pw_eval.c>
+#include <isl_pw_insert_dims_templ.c>
+#include <isl_pw_lift_templ.c>
+#include <isl_pw_morph_templ.c>
+#include <isl_pw_move_dims_templ.c>
+#include <isl_pw_opt_templ.c>
+
+#undef BASE
+#define BASE pw_qpolynomial_fold
+
+#define NO_SUB
+
+#include <isl_union_single.c>
+#include <isl_union_eval.c>
+
+/* Construct a new reduction of the given type and space
+ * with an empty list of polynomials.
+ */
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_empty(enum isl_fold type,
+ __isl_take isl_space *space)
+{
+ isl_ctx *ctx;
+ isl_qpolynomial_list *list;
+
+ if (!space)
+ return NULL;
+ ctx = isl_space_get_ctx(space);
+ list = isl_qpolynomial_list_alloc(ctx, 0);
+ return qpolynomial_fold_alloc(type, space, list);
+}
+
+/* Construct a new reduction of the given type and
+ * a single given polynomial.
+ */
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_alloc(
+ enum isl_fold type, __isl_take isl_qpolynomial *qp)
+{
+ isl_space *space;
+ isl_qpolynomial_list *list;
+
+ space = isl_qpolynomial_get_domain_space(qp);
+ list = isl_qpolynomial_list_from_qpolynomial(qp);
+ return qpolynomial_fold_alloc(type, space, list);
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_copy(
+ __isl_keep isl_qpolynomial_fold *fold)
+{
+ if (!fold)
+ return NULL;
+
+ fold->ref++;
+ return fold;
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_dup(
+ __isl_keep isl_qpolynomial_fold *fold)
+{
+ enum isl_fold type;
+ isl_space *space;
+ isl_qpolynomial_list *list;
+
+ type = isl_qpolynomial_fold_get_type(fold);
+ space = isl_qpolynomial_fold_get_domain_space(fold);
+ list = isl_qpolynomial_fold_get_list(fold);
+ return qpolynomial_fold_alloc(type, space, list);
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_cow(
+ __isl_take isl_qpolynomial_fold *fold)
+{
+ if (!fold)
+ return NULL;
+
+ if (fold->ref == 1)
+ return fold;
+ fold->ref--;
+ return isl_qpolynomial_fold_dup(fold);
+}
+
+__isl_null isl_qpolynomial_fold *isl_qpolynomial_fold_free(
+ __isl_take isl_qpolynomial_fold *fold)
+{
+ if (!fold)
+ return NULL;
+ if (--fold->ref > 0)
+ return NULL;
+
+ isl_qpolynomial_list_free(fold->list);
+ isl_space_free(fold->dim);
+ free(fold);
+
+ return NULL;
+}
+
+isl_bool isl_qpolynomial_fold_is_empty(__isl_keep isl_qpolynomial_fold *fold)
+{
+ isl_size n;
+ isl_qpolynomial_list *list;
+
+ list = isl_qpolynomial_fold_peek_list(fold);
+ n = isl_qpolynomial_list_size(list);
+ if (n < 0)
+ return isl_bool_error;
+
+ return isl_bool_ok(n == 0);
+}
+
+/* Does "fold" represent max(NaN) or min(NaN)?
+ */
+isl_bool isl_qpolynomial_fold_is_nan(__isl_keep isl_qpolynomial_fold *fold)
+{
+ isl_size n;
+ isl_qpolynomial *qp;
+ isl_qpolynomial_list *list;
+
+ list = isl_qpolynomial_fold_peek_list(fold);
+ n = isl_qpolynomial_list_size(list);
+ if (n < 0)
+ return isl_bool_error;
+ if (n != 1)
+ return isl_bool_false;
+ qp = isl_qpolynomial_list_peek(list, 0);
+ return isl_qpolynomial_is_nan(qp);
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_fold(
+ __isl_take isl_qpolynomial_fold *fold1,
+ __isl_take isl_qpolynomial_fold *fold2)
+{
+ isl_qpolynomial_list *list1, *list2;
+
+ if (isl_qpolynomial_fold_check_equal_type(fold1, fold2) < 0)
+ goto error;
+ if (isl_qpolynomial_fold_check_equal_space(fold1, fold2) < 0)
+ goto error;
+
+ if (isl_qpolynomial_fold_is_empty(fold1)) {
+ isl_qpolynomial_fold_free(fold1);
+ return fold2;
+ }
+
+ if (isl_qpolynomial_fold_is_empty(fold2)) {
+ isl_qpolynomial_fold_free(fold2);
+ return fold1;
+ }
+
+ list1 = isl_qpolynomial_fold_take_list(fold1);
+ list2 = isl_qpolynomial_fold_take_list(fold2);
+ list1 = isl_qpolynomial_list_concat(list1, list2);
+ fold1 = isl_qpolynomial_fold_restore_list(fold1, list1);
+ isl_qpolynomial_fold_free(fold2);
+
+ return fold1;
+error:
+ isl_qpolynomial_fold_free(fold1);
+ isl_qpolynomial_fold_free(fold2);
+ return NULL;
+}
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_fold(
+ __isl_take isl_pw_qpolynomial_fold *pw1,
+ __isl_take isl_pw_qpolynomial_fold *pw2)
+{
+ int i, j, n;
+ struct isl_pw_qpolynomial_fold *res;
+ isl_set *set;
+
+ if (!pw1 || !pw2)
+ goto error;
+
+ isl_assert(pw1->dim->ctx, isl_space_is_equal(pw1->dim, pw2->dim), goto error);
+
+ if (isl_pw_qpolynomial_fold_is_zero(pw1)) {
+ isl_pw_qpolynomial_fold_free(pw1);
+ return pw2;
+ }
+
+ if (isl_pw_qpolynomial_fold_is_zero(pw2)) {
+ isl_pw_qpolynomial_fold_free(pw2);
+ return pw1;
+ }
+
+ if (pw1->type != pw2->type)
+ isl_die(pw1->dim->ctx, isl_error_invalid,
+ "fold types don't match", goto error);
+
+ n = (pw1->n + 1) * (pw2->n + 1);
+ res = isl_pw_qpolynomial_fold_alloc_size(isl_space_copy(pw1->dim),
+ pw1->type, n);
+
+ for (i = 0; i < pw1->n; ++i) {
+ set = isl_set_copy(pw1->p[i].set);
+ for (j = 0; j < pw2->n; ++j) {
+ struct isl_set *common;
+ isl_qpolynomial_fold *sum;
+ set = isl_set_subtract(set,
+ isl_set_copy(pw2->p[j].set));
+ common = isl_set_intersect(isl_set_copy(pw1->p[i].set),
+ isl_set_copy(pw2->p[j].set));
+ if (isl_set_plain_is_empty(common)) {
+ isl_set_free(common);
+ continue;
+ }
+
+ sum = isl_qpolynomial_fold_fold_on_domain(common,
+ isl_qpolynomial_fold_copy(pw1->p[i].fold),
+ isl_qpolynomial_fold_copy(pw2->p[j].fold));
+
+ res = isl_pw_qpolynomial_fold_add_piece(res, common, sum);
+ }
+ res = isl_pw_qpolynomial_fold_add_piece(res, set,
+ isl_qpolynomial_fold_copy(pw1->p[i].fold));
+ }
+
+ for (j = 0; j < pw2->n; ++j) {
+ set = isl_set_copy(pw2->p[j].set);
+ for (i = 0; i < pw1->n; ++i)
+ set = isl_set_subtract(set, isl_set_copy(pw1->p[i].set));
+ res = isl_pw_qpolynomial_fold_add_piece(res, set,
+ isl_qpolynomial_fold_copy(pw2->p[j].fold));
+ }
+
+ isl_pw_qpolynomial_fold_free(pw1);
+ isl_pw_qpolynomial_fold_free(pw2);
+
+ return res;
+error:
+ isl_pw_qpolynomial_fold_free(pw1);
+ isl_pw_qpolynomial_fold_free(pw2);
+ return NULL;
+}
+
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_fold_pw_qpolynomial_fold(
+ __isl_take isl_union_pw_qpolynomial_fold *u,
+ __isl_take isl_pw_qpolynomial_fold *part)
+{
+ struct isl_hash_table_entry *entry;
+
+ u = isl_union_pw_qpolynomial_fold_cow(u);
+
+ if (!part || !u)
+ goto error;
+ if (isl_space_check_equal_params(part->dim, u->space) < 0)
+ goto error;
+
+ entry = isl_union_pw_qpolynomial_fold_find_part_entry(u, part->dim, 1);
+ if (!entry)
+ goto error;
+
+ if (!entry->data)
+ entry->data = part;
+ else {
+ entry->data = isl_pw_qpolynomial_fold_fold(entry->data,
+ isl_pw_qpolynomial_fold_copy(part));
+ if (!entry->data)
+ goto error;
+ isl_pw_qpolynomial_fold_free(part);
+ }
+
+ return u;
+error:
+ isl_pw_qpolynomial_fold_free(part);
+ isl_union_pw_qpolynomial_fold_free(u);
+ return NULL;
+}
+
+static isl_stat fold_part(__isl_take isl_pw_qpolynomial_fold *part, void *user)
+{
+ isl_union_pw_qpolynomial_fold **u;
+ u = (isl_union_pw_qpolynomial_fold **)user;
+
+ *u = isl_union_pw_qpolynomial_fold_fold_pw_qpolynomial_fold(*u, part);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_fold(
+ __isl_take isl_union_pw_qpolynomial_fold *u1,
+ __isl_take isl_union_pw_qpolynomial_fold *u2)
+{
+ u1 = isl_union_pw_qpolynomial_fold_cow(u1);
+
+ if (!u1 || !u2)
+ goto error;
+
+ if (isl_union_pw_qpolynomial_fold_foreach_pw_qpolynomial_fold(u2,
+ &fold_part, &u1) < 0)
+ goto error;
+
+ isl_union_pw_qpolynomial_fold_free(u2);
+
+ return u1;
+error:
+ isl_union_pw_qpolynomial_fold_free(u1);
+ isl_union_pw_qpolynomial_fold_free(u2);
+ return NULL;
+}
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_from_pw_qpolynomial(
+ enum isl_fold type, __isl_take isl_pw_qpolynomial *pwqp)
+{
+ int i;
+ isl_pw_qpolynomial_fold *pwf;
+
+ if (!pwqp)
+ return NULL;
+
+ pwf = isl_pw_qpolynomial_fold_alloc_size(isl_space_copy(pwqp->dim),
+ type, pwqp->n);
+
+ for (i = 0; i < pwqp->n; ++i)
+ pwf = isl_pw_qpolynomial_fold_add_piece(pwf,
+ isl_set_copy(pwqp->p[i].set),
+ isl_qpolynomial_fold_alloc(type,
+ isl_qpolynomial_copy(pwqp->p[i].qp)));
+
+ isl_pw_qpolynomial_free(pwqp);
+
+ return pwf;
+}
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_add(
+ __isl_take isl_pw_qpolynomial_fold *pwf1,
+ __isl_take isl_pw_qpolynomial_fold *pwf2)
+{
+ return isl_pw_qpolynomial_fold_union_add_(pwf1, pwf2);
+}
+
+/* Compare two quasi-polynomial reductions.
+ *
+ * Return -1 if "fold1" is "smaller" than "fold2", 1 if "fold1" is "greater"
+ * than "fold2" and 0 if they are equal.
+ */
+int isl_qpolynomial_fold_plain_cmp(__isl_keep isl_qpolynomial_fold *fold1,
+ __isl_keep isl_qpolynomial_fold *fold2)
+{
+ int i;
+ isl_size n1, n2;
+ isl_qpolynomial_list *list1, *list2;
+
+ if (fold1 == fold2)
+ return 0;
+ list1 = isl_qpolynomial_fold_peek_list(fold1);
+ list2 = isl_qpolynomial_fold_peek_list(fold2);
+ n1 = isl_qpolynomial_list_size(list1);
+ n2 = isl_qpolynomial_list_size(list2);
+ if (n1 < 0)
+ return -1;
+ if (n2 < 0)
+ return 1;
+
+ if (n1 != n2)
+ return n1 - n2;
+
+ for (i = 0; i < n1; ++i) {
+ int cmp;
+ isl_qpolynomial *qp1, *qp2;
+
+ qp1 = isl_qpolynomial_list_peek(list1, i);
+ qp2 = isl_qpolynomial_list_peek(list2, i);
+ cmp = isl_qpolynomial_plain_cmp(qp1, qp2);
+ if (cmp != 0)
+ return cmp;
+ }
+
+ return 0;
+}
+
+/* Are the lists "list1" and "list2", both consisting of "n" elements
+ * obviously equal to each other?
+ */
+static isl_bool isl_qpolynomial_list_plain_is_equal(unsigned n,
+ isl_qpolynomial_list *list1, isl_qpolynomial_list *list2)
+{
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ isl_bool eq;
+ isl_qpolynomial *qp1, *qp2;
+
+ qp1 = isl_qpolynomial_list_peek(list1, i);
+ qp2 = isl_qpolynomial_list_peek(list2, i);
+ eq = isl_qpolynomial_plain_is_equal(qp1, qp2);
+ if (eq < 0 || !eq)
+ return eq;
+ }
+
+ return isl_bool_true;
+}
+
+/* Wrapper around isl_qpolynomial_plain_cmp for use
+ * as a isl_qpolynomial_list_sort callback.
+ */
+static int qpolynomial_cmp(__isl_keep isl_qpolynomial *a,
+ __isl_keep isl_qpolynomial *b, void *user)
+{
+ return isl_qpolynomial_plain_cmp(a, b);
+}
+
+isl_bool isl_qpolynomial_fold_plain_is_equal(
+ __isl_keep isl_qpolynomial_fold *fold1,
+ __isl_keep isl_qpolynomial_fold *fold2)
+{
+ isl_bool equal;
+ isl_size n1, n2;
+ isl_qpolynomial_list *list1, *list2;
+
+ list1 = isl_qpolynomial_fold_peek_list(fold1);
+ list2 = isl_qpolynomial_fold_peek_list(fold2);
+ n1 = isl_qpolynomial_list_size(list1);
+ n2 = isl_qpolynomial_list_size(list2);
+ if (n1 < 0 || n2 < 0)
+ return isl_bool_error;
+
+ if (n1 != n2)
+ return isl_bool_false;
+
+ list1 = isl_qpolynomial_list_copy(list1);
+ list1 = isl_qpolynomial_list_sort(list1, &qpolynomial_cmp, NULL);
+ list2 = isl_qpolynomial_list_copy(list2);
+ list2 = isl_qpolynomial_list_sort(list2, &qpolynomial_cmp, NULL);
+ equal = isl_qpolynomial_list_plain_is_equal(n1, list1, list2);
+ isl_qpolynomial_list_free(list1);
+ isl_qpolynomial_list_free(list2);
+ return equal;
+}
+
+__isl_give isl_val *isl_qpolynomial_fold_eval(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_point *pnt)
+{
+ isl_size n;
+ isl_ctx *ctx;
+ isl_val *v;
+ isl_qpolynomial *qp;
+ isl_qpolynomial_list *list;
+
+ if (!fold || !pnt)
+ goto error;
+ ctx = isl_point_get_ctx(pnt);
+ isl_assert(pnt->dim->ctx, isl_space_is_equal(pnt->dim, fold->dim), goto error);
+ isl_assert(pnt->dim->ctx,
+ fold->type == isl_fold_max || fold->type == isl_fold_min,
+ goto error);
+
+ list = isl_qpolynomial_fold_peek_list(fold);
+ n = isl_qpolynomial_list_size(list);
+ if (n < 0)
+ goto error;
+
+ if (n == 0)
+ v = isl_val_zero(ctx);
+ else {
+ int i;
+
+ qp = isl_qpolynomial_list_get_at(list, 0);
+ v = isl_qpolynomial_eval(qp, isl_point_copy(pnt));
+ for (i = 1; i < n; ++i) {
+ isl_val *v_i;
+
+ qp = isl_qpolynomial_list_get_at(list, i);
+ v_i = isl_qpolynomial_eval(qp, isl_point_copy(pnt));
+ if (fold->type == isl_fold_max)
+ v = isl_val_max(v, v_i);
+ else
+ v = isl_val_min(v, v_i);
+ }
+ }
+ isl_qpolynomial_fold_free(fold);
+ isl_point_free(pnt);
+
+ return v;
+error:
+ isl_qpolynomial_fold_free(fold);
+ isl_point_free(pnt);
+ return NULL;
+}
+
+size_t isl_pw_qpolynomial_fold_size(__isl_keep isl_pw_qpolynomial_fold *pwf)
+{
+ int i;
+ size_t n = 0;
+
+ for (i = 0; i < pwf->n; ++i) {
+ isl_size n_i;
+ isl_qpolynomial_list *list;
+
+ list = isl_qpolynomial_fold_peek_list(pwf->p[i].fold);
+ n_i = isl_qpolynomial_list_size(list);
+ if (n_i < 0)
+ return isl_size_error;
+
+ n += n_i;
+ }
+
+ return n;
+}
+
+__isl_give isl_val *isl_qpolynomial_fold_opt_on_domain(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *set, int max)
+{
+ int i;
+ isl_size n;
+ isl_val *opt;
+ isl_qpolynomial *qp;
+ isl_qpolynomial_list *list;
+
+ list = isl_qpolynomial_fold_peek_list(fold);
+ n = isl_qpolynomial_list_size(list);
+ if (!set || n < 0)
+ goto error;
+
+ if (n == 0) {
+ opt = isl_val_zero(isl_set_get_ctx(set));
+ isl_set_free(set);
+ isl_qpolynomial_fold_free(fold);
+ return opt;
+ }
+
+ qp = isl_qpolynomial_list_get_at(list, 0);
+ opt = isl_qpolynomial_opt_on_domain(qp, isl_set_copy(set), max);
+ for (i = 1; i < n; ++i) {
+ isl_val *opt_i;
+
+ qp = isl_qpolynomial_list_get_at(list, i);
+ opt_i = isl_qpolynomial_opt_on_domain(qp,
+ isl_set_copy(set), max);
+ if (max)
+ opt = isl_val_max(opt, opt_i);
+ else
+ opt = isl_val_min(opt, opt_i);
+ }
+
+ isl_set_free(set);
+ isl_qpolynomial_fold_free(fold);
+
+ return opt;
+error:
+ isl_set_free(set);
+ isl_qpolynomial_fold_free(fold);
+ return NULL;
+}
+
+/* Check whether for each quasi-polynomial in "fold2" there is
+ * a quasi-polynomial in "fold1" that dominates it on "set".
+ */
+static isl_bool qpolynomial_fold_covers_on_domain(__isl_keep isl_set *set,
+ __isl_keep isl_qpolynomial_fold *fold1,
+ __isl_keep isl_qpolynomial_fold *fold2)
+{
+ int i, j;
+ int covers;
+ isl_size n1, n2;
+ isl_qpolynomial_list *list1, *list2;
+
+ list1 = isl_qpolynomial_fold_peek_list(fold1);
+ list2 = isl_qpolynomial_fold_peek_list(fold2);
+ n1 = isl_qpolynomial_list_size(list1);
+ n2 = isl_qpolynomial_list_size(list2);
+ if (!set || n1 < 0 || n2 < 0)
+ return isl_bool_error;
+
+ covers = fold1->type == isl_fold_max ? 1 : -1;
+
+ for (i = 0; i < n2; ++i) {
+ for (j = 0; j < n1; ++j) {
+ isl_qpolynomial *qp1, *qp2, *d;
+ int sgn;
+
+ qp1 = isl_qpolynomial_list_get_at(list1, j);
+ qp2 = isl_qpolynomial_list_get_at(list2, i);
+ d = isl_qpolynomial_sub(qp1, qp2);
+ sgn = isl_qpolynomial_sign(set, d);
+ isl_qpolynomial_free(d);
+ if (sgn == covers)
+ break;
+ }
+ if (j >= n1)
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+/* Check whether "pwf1" dominated "pwf2", i.e., the domain of "pwf1" contains
+ * that of "pwf2" and on each cell, the corresponding fold from pwf1 dominates
+ * that of pwf2.
+ */
+isl_bool isl_pw_qpolynomial_fold_covers(
+ __isl_keep isl_pw_qpolynomial_fold *pwf1,
+ __isl_keep isl_pw_qpolynomial_fold *pwf2)
+{
+ int i, j;
+ isl_set *dom1, *dom2;
+ isl_bool is_subset;
+
+ if (!pwf1 || !pwf2)
+ return isl_bool_error;
+
+ if (pwf2->n == 0)
+ return isl_bool_true;
+ if (pwf1->n == 0)
+ return isl_bool_false;
+
+ dom1 = isl_pw_qpolynomial_fold_domain(isl_pw_qpolynomial_fold_copy(pwf1));
+ dom2 = isl_pw_qpolynomial_fold_domain(isl_pw_qpolynomial_fold_copy(pwf2));
+ is_subset = isl_set_is_subset(dom2, dom1);
+ isl_set_free(dom1);
+ isl_set_free(dom2);
+
+ if (is_subset < 0 || !is_subset)
+ return is_subset;
+
+ for (i = 0; i < pwf2->n; ++i) {
+ for (j = 0; j < pwf1->n; ++j) {
+ isl_bool is_empty;
+ isl_set *common;
+ isl_bool covers;
+
+ common = isl_set_intersect(isl_set_copy(pwf1->p[j].set),
+ isl_set_copy(pwf2->p[i].set));
+ is_empty = isl_set_is_empty(common);
+ if (is_empty < 0 || is_empty) {
+ isl_set_free(common);
+ if (is_empty < 0)
+ return isl_bool_error;
+ continue;
+ }
+ covers = qpolynomial_fold_covers_on_domain(common,
+ pwf1->p[j].fold, pwf2->p[i].fold);
+ isl_set_free(common);
+ if (covers < 0 || !covers)
+ return covers;
+ }
+ }
+
+ return isl_bool_true;
+}
+
+/* isl_qpolynomial_list_map callback that calls
+ * isl_qpolynomial_morph_domain on "qp".
+ */
+static __isl_give isl_qpolynomial *morph_domain(
+ __isl_take isl_qpolynomial *qp, void *user)
+{
+ isl_morph *morph = user;
+
+ return isl_qpolynomial_morph_domain(qp, isl_morph_copy(morph));
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_morph_domain(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_morph *morph)
+{
+ isl_space *space;
+ isl_qpolynomial_list *list;
+
+ space = isl_qpolynomial_fold_peek_domain_space(fold);
+ if (isl_morph_check_applies(morph, space) < 0)
+ goto error;
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &morph_domain, morph);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ space = isl_morph_get_ran_space(morph);
+ isl_space_free(isl_qpolynomial_fold_take_domain_space(fold));
+ fold = isl_qpolynomial_fold_restore_domain_space(fold, space);
+
+ isl_morph_free(morph);
+
+ return fold;
+error:
+ isl_qpolynomial_fold_free(fold);
+ isl_morph_free(morph);
+ return NULL;
+}
+
+enum isl_fold isl_qpolynomial_fold_get_type(__isl_keep isl_qpolynomial_fold *fold)
+{
+ if (!fold)
+ return isl_fold_error;
+ return fold->type;
+}
+
+/* Return the type of this piecewise quasipolynomial reduction.
+ */
+enum isl_fold isl_pw_qpolynomial_fold_get_type(
+ __isl_keep isl_pw_qpolynomial_fold *pwf)
+{
+ if (!pwf)
+ return isl_fold_error;
+ return pwf->type;
+}
+
+enum isl_fold isl_union_pw_qpolynomial_fold_get_type(
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf)
+{
+ if (!upwf)
+ return isl_fold_error;
+ return upwf->type;
+}
+
+/* isl_qpolynomial_list_map callback that calls
+ * isl_qpolynomial_lift on "qp".
+ */
+static __isl_give isl_qpolynomial *lift(__isl_take isl_qpolynomial *qp,
+ void *user)
+{
+ isl_space *space = user;
+
+ return isl_qpolynomial_lift(qp, isl_space_copy(space));
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_lift(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *space)
+{
+ isl_qpolynomial_list *list;
+
+ if (!fold || !space)
+ goto error;
+
+ if (isl_space_is_equal(fold->dim, space)) {
+ isl_space_free(space);
+ return fold;
+ }
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &lift, space);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ isl_space_free(isl_qpolynomial_fold_take_domain_space(fold));
+ fold = isl_qpolynomial_fold_restore_domain_space(fold, space);
+
+ return fold;
+error:
+ isl_qpolynomial_fold_free(fold);
+ isl_space_free(space);
+ return NULL;
+}
+
+isl_stat isl_qpolynomial_fold_foreach_qpolynomial(
+ __isl_keep isl_qpolynomial_fold *fold,
+ isl_stat (*fn)(__isl_take isl_qpolynomial *qp, void *user), void *user)
+{
+ isl_qpolynomial_list *list;
+
+ list = isl_qpolynomial_fold_peek_list(fold);
+ return isl_qpolynomial_list_foreach(list, fn, user);
+}
+
+/* Internal data structure for isl_qpolynomial_fold_move_dims
+ * representing its arguments.
+ */
+struct isl_fold_move_dims_data {
+ enum isl_dim_type dst_type;
+ unsigned dst_pos;
+ enum isl_dim_type src_type;
+ unsigned src_pos;
+ unsigned n;
+};
+
+/* isl_qpolynomial_list_map callback for calling
+ * isl_qpolynomial_move_dims on "qp".
+ */
+static __isl_give isl_qpolynomial *move_dims(__isl_take isl_qpolynomial *qp,
+ void *user)
+{
+ struct isl_fold_move_dims_data *data = user;
+
+ qp = isl_qpolynomial_move_dims(qp, data->dst_type, data->dst_pos,
+ data->src_type, data->src_pos, data->n);
+ return qp;
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_move_dims(
+ __isl_take isl_qpolynomial_fold *fold,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ struct isl_fold_move_dims_data data =
+ { dst_type, dst_pos, src_type, src_pos, n };
+ enum isl_dim_type set_src_type, set_dst_type;
+ isl_space *space;
+ isl_qpolynomial_list *list;
+
+ if (n == 0)
+ return fold;
+
+ fold = isl_qpolynomial_fold_cow(fold);
+ if (!fold)
+ return NULL;
+
+ set_src_type = domain_type(src_type);
+ set_dst_type = domain_type(dst_type);
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &move_dims, &data);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ space = isl_qpolynomial_fold_take_domain_space(fold);
+ space = isl_space_move_dims(space, set_dst_type, dst_pos,
+ set_src_type, src_pos, n);
+ fold = isl_qpolynomial_fold_restore_domain_space(fold, space);
+
+ return fold;
+}
+
+/* Internal data structure for isl_qpolynomial_fold_substitute
+ * representing its arguments.
+ */
+struct isl_fold_substitute {
+ enum isl_dim_type type;
+ unsigned first;
+ unsigned n;
+ isl_qpolynomial **subs;
+};
+
+/* isl_qpolynomial_list_map callback for calling
+ * isl_qpolynomial_substitute on "qp".
+ */
+static __isl_give isl_qpolynomial *substitute(__isl_take isl_qpolynomial *qp,
+ void *user)
+{
+ struct isl_fold_substitute *data = user;
+
+ qp = isl_qpolynomial_substitute(qp,
+ data->type, data->first, data->n, data->subs);
+ return qp;
+}
+
+/* For each 0 <= i < "n", replace variable "first" + i of type "type"
+ * in fold->qp[k] by subs[i].
+ */
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_substitute(
+ __isl_take isl_qpolynomial_fold *fold,
+ enum isl_dim_type type, unsigned first, unsigned n,
+ __isl_keep isl_qpolynomial **subs)
+{
+ struct isl_fold_substitute data = { type, first, n, subs };
+ isl_qpolynomial_list *list;
+
+ if (n == 0)
+ return fold;
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &substitute, &data);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ return fold;
+}
+
+static isl_stat add_pwqp(__isl_take isl_pw_qpolynomial *pwqp, void *user)
+{
+ isl_pw_qpolynomial_fold *pwf;
+ isl_union_pw_qpolynomial_fold **upwf;
+ struct isl_hash_table_entry *entry;
+
+ upwf = (isl_union_pw_qpolynomial_fold **)user;
+
+ entry = isl_union_pw_qpolynomial_fold_find_part_entry(*upwf,
+ pwqp->dim, 1);
+ if (!entry)
+ goto error;
+
+ pwf = isl_pw_qpolynomial_fold_from_pw_qpolynomial((*upwf)->type, pwqp);
+ if (!entry->data)
+ entry->data = pwf;
+ else {
+ entry->data = isl_pw_qpolynomial_fold_add(entry->data, pwf);
+ if (!entry->data)
+ return isl_stat_error;
+ if (isl_pw_qpolynomial_fold_is_zero(entry->data))
+ *upwf = isl_union_pw_qpolynomial_fold_remove_part_entry(
+ *upwf, entry);
+ }
+
+ return isl_stat_ok;
+error:
+ isl_pw_qpolynomial_free(pwqp);
+ return isl_stat_error;
+}
+
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_pw_qpolynomial_fold_add_union_pw_qpolynomial(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf,
+ __isl_take isl_union_pw_qpolynomial *upwqp)
+{
+ upwf = isl_union_pw_qpolynomial_fold_align_params(upwf,
+ isl_union_pw_qpolynomial_get_space(upwqp));
+ upwqp = isl_union_pw_qpolynomial_align_params(upwqp,
+ isl_union_pw_qpolynomial_fold_get_space(upwf));
+
+ upwf = isl_union_pw_qpolynomial_fold_cow(upwf);
+ if (!upwf || !upwqp)
+ goto error;
+
+ if (isl_union_pw_qpolynomial_foreach_pw_qpolynomial(upwqp, &add_pwqp,
+ &upwf) < 0)
+ goto error;
+
+ isl_union_pw_qpolynomial_free(upwqp);
+
+ return upwf;
+error:
+ isl_union_pw_qpolynomial_fold_free(upwf);
+ isl_union_pw_qpolynomial_free(upwqp);
+ return NULL;
+}
+
+static isl_bool join_compatible(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ isl_bool m;
+ m = isl_space_has_equal_params(space1, space2);
+ if (m < 0 || !m)
+ return m;
+ return isl_space_tuple_is_equal(space1, isl_dim_out,
+ space2, isl_dim_in);
+}
+
+/* Compute the intersection of the range of the map and the domain
+ * of the piecewise quasipolynomial reduction and then compute a bound
+ * on the associated quasipolynomial reduction over all elements
+ * in this intersection.
+ *
+ * We first introduce some unconstrained dimensions in the
+ * piecewise quasipolynomial, intersect the resulting domain
+ * with the wrapped map and the compute the sum.
+ */
+__isl_give isl_pw_qpolynomial_fold *isl_map_apply_pw_qpolynomial_fold(
+ __isl_take isl_map *map, __isl_take isl_pw_qpolynomial_fold *pwf,
+ isl_bool *tight)
+{
+ isl_ctx *ctx;
+ isl_set *dom;
+ isl_space *map_space;
+ isl_space *pwf_space;
+ isl_size n_in;
+ isl_bool ok;
+
+ ctx = isl_map_get_ctx(map);
+ if (!ctx)
+ goto error;
+
+ map_space = isl_map_get_space(map);
+ pwf_space = isl_pw_qpolynomial_fold_get_space(pwf);
+ ok = join_compatible(map_space, pwf_space);
+ isl_space_free(map_space);
+ isl_space_free(pwf_space);
+ if (ok < 0)
+ goto error;
+ if (!ok)
+ isl_die(ctx, isl_error_invalid, "incompatible dimensions",
+ goto error);
+
+ n_in = isl_map_dim(map, isl_dim_in);
+ if (n_in < 0)
+ goto error;
+ pwf = isl_pw_qpolynomial_fold_insert_dims(pwf, isl_dim_in, 0, n_in);
+
+ dom = isl_map_wrap(map);
+ pwf = isl_pw_qpolynomial_fold_reset_domain_space(pwf,
+ isl_set_get_space(dom));
+
+ pwf = isl_pw_qpolynomial_fold_intersect_domain(pwf, dom);
+ pwf = isl_pw_qpolynomial_fold_bound(pwf, tight);
+
+ return pwf;
+error:
+ isl_map_free(map);
+ isl_pw_qpolynomial_fold_free(pwf);
+ return NULL;
+}
+
+__isl_give isl_pw_qpolynomial_fold *isl_set_apply_pw_qpolynomial_fold(
+ __isl_take isl_set *set, __isl_take isl_pw_qpolynomial_fold *pwf,
+ isl_bool *tight)
+{
+ return isl_map_apply_pw_qpolynomial_fold(set, pwf, tight);
+}
+
+struct isl_apply_fold_data {
+ isl_union_pw_qpolynomial_fold *upwf;
+ isl_union_pw_qpolynomial_fold *res;
+ isl_map *map;
+ isl_bool tight;
+};
+
+static isl_stat pw_qpolynomial_fold_apply(
+ __isl_take isl_pw_qpolynomial_fold *pwf, void *user)
+{
+ isl_space *map_dim;
+ isl_space *pwf_dim;
+ struct isl_apply_fold_data *data = user;
+ isl_bool ok;
+
+ map_dim = isl_map_get_space(data->map);
+ pwf_dim = isl_pw_qpolynomial_fold_get_space(pwf);
+ ok = join_compatible(map_dim, pwf_dim);
+ isl_space_free(map_dim);
+ isl_space_free(pwf_dim);
+
+ if (ok < 0)
+ return isl_stat_error;
+ if (ok) {
+ pwf = isl_map_apply_pw_qpolynomial_fold(isl_map_copy(data->map),
+ pwf, data->tight ? &data->tight : NULL);
+ data->res = isl_union_pw_qpolynomial_fold_fold_pw_qpolynomial_fold(
+ data->res, pwf);
+ } else
+ isl_pw_qpolynomial_fold_free(pwf);
+
+ return isl_stat_ok;
+}
+
+static isl_stat map_apply(__isl_take isl_map *map, void *user)
+{
+ struct isl_apply_fold_data *data = user;
+ isl_stat r;
+
+ data->map = map;
+ r = isl_union_pw_qpolynomial_fold_foreach_pw_qpolynomial_fold(
+ data->upwf, &pw_qpolynomial_fold_apply, data);
+
+ isl_map_free(map);
+ return r;
+}
+
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_map_apply_union_pw_qpolynomial_fold(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_union_pw_qpolynomial_fold *upwf, isl_bool *tight)
+{
+ isl_space *space;
+ enum isl_fold type;
+ struct isl_apply_fold_data data;
+
+ upwf = isl_union_pw_qpolynomial_fold_align_params(upwf,
+ isl_union_map_get_space(umap));
+ umap = isl_union_map_align_params(umap,
+ isl_union_pw_qpolynomial_fold_get_space(upwf));
+
+ data.upwf = upwf;
+ data.tight = tight ? isl_bool_true : isl_bool_false;
+ space = isl_union_pw_qpolynomial_fold_get_space(upwf);
+ type = isl_union_pw_qpolynomial_fold_get_type(upwf);
+ data.res = isl_union_pw_qpolynomial_fold_zero(space, type);
+ if (isl_union_map_foreach_map(umap, &map_apply, &data) < 0)
+ goto error;
+
+ isl_union_map_free(umap);
+ isl_union_pw_qpolynomial_fold_free(upwf);
+
+ if (tight)
+ *tight = data.tight;
+
+ return data.res;
+error:
+ isl_union_map_free(umap);
+ isl_union_pw_qpolynomial_fold_free(upwf);
+ isl_union_pw_qpolynomial_fold_free(data.res);
+ return NULL;
+}
+
+__isl_give isl_union_pw_qpolynomial_fold *isl_union_set_apply_union_pw_qpolynomial_fold(
+ __isl_take isl_union_set *uset,
+ __isl_take isl_union_pw_qpolynomial_fold *upwf, isl_bool *tight)
+{
+ return isl_union_map_apply_union_pw_qpolynomial_fold(uset, upwf, tight);
+}
+
+/* isl_qpolynomial_list_map callback for calling
+ * isl_qpolynomial_realign_domain on "qp".
+ */
+static __isl_give isl_qpolynomial *realign_domain(
+ __isl_take isl_qpolynomial *qp, void *user)
+{
+ isl_reordering *r = user;
+
+ qp = isl_qpolynomial_realign_domain(qp, isl_reordering_copy(r));
+ return qp;
+}
+
+/* Reorder the dimension of "fold" according to the given reordering.
+ */
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_realign_domain(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_reordering *r)
+{
+ isl_space *space;
+ isl_qpolynomial_list *list;
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &realign_domain, r);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ space = isl_reordering_get_space(r);
+ fold = isl_qpolynomial_fold_reset_domain_space(fold, space);
+
+ isl_reordering_free(r);
+
+ return fold;
+}
+
+/* isl_qpolynomial_list_map callback for calling
+ * isl_qpolynomial_mul_isl_int on "qp".
+ */
+static __isl_give isl_qpolynomial *mul_int(__isl_take isl_qpolynomial *qp,
+ void *user)
+{
+ isl_int *v = user;
+
+ qp = isl_qpolynomial_mul_isl_int(qp, *v);
+ return qp;
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_mul_isl_int(
+ __isl_take isl_qpolynomial_fold *fold, isl_int v)
+{
+ isl_qpolynomial_list *list;
+
+ if (isl_int_is_one(v))
+ return fold;
+ if (fold && isl_int_is_zero(v)) {
+ isl_qpolynomial_fold *zero;
+ isl_space *space = isl_space_copy(fold->dim);
+ zero = isl_qpolynomial_fold_empty(fold->type, space);
+ isl_qpolynomial_fold_free(fold);
+ return zero;
+ }
+
+ fold = isl_qpolynomial_fold_cow(fold);
+ if (!fold)
+ return NULL;
+
+ if (isl_int_is_neg(v))
+ fold->type = isl_fold_type_negate(fold->type);
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &mul_int, &v);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ return fold;
+}
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale(
+ __isl_take isl_qpolynomial_fold *fold, isl_int v)
+{
+ return isl_qpolynomial_fold_mul_isl_int(fold, v);
+}
+
+/* isl_qpolynomial_list_map callback for calling
+ * isl_qpolynomial_scale_val on "qp".
+ */
+static __isl_give isl_qpolynomial *scale_val(__isl_take isl_qpolynomial *qp,
+ void *user)
+{
+ isl_val *v = user;
+
+ qp = isl_qpolynomial_scale_val(qp, isl_val_copy(v));
+ return qp;
+}
+
+/* Multiply "fold" by "v".
+ */
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale_val(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_val *v)
+{
+ isl_qpolynomial_list *list;
+
+ if (!fold || !v)
+ goto error;
+
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return fold;
+ }
+ if (isl_val_is_zero(v)) {
+ isl_qpolynomial_fold *zero;
+ isl_space *space = isl_qpolynomial_fold_get_domain_space(fold);
+ zero = isl_qpolynomial_fold_empty(fold->type, space);
+ isl_qpolynomial_fold_free(fold);
+ isl_val_free(v);
+ return zero;
+ }
+ if (!isl_val_is_rat(v))
+ isl_die(isl_qpolynomial_fold_get_ctx(fold), isl_error_invalid,
+ "expecting rational factor", goto error);
+
+ fold = isl_qpolynomial_fold_cow(fold);
+ if (!fold)
+ goto error;
+
+ if (isl_val_is_neg(v))
+ fold->type = isl_fold_type_negate(fold->type);
+
+ list = isl_qpolynomial_fold_take_list(fold);
+ list = isl_qpolynomial_list_map(list, &scale_val, v);
+ fold = isl_qpolynomial_fold_restore_list(fold, list);
+
+ isl_val_free(v);
+ return fold;
+error:
+ isl_val_free(v);
+ isl_qpolynomial_fold_free(fold);
+ return NULL;
+}
+
+/* Divide "fold" by "v".
+ */
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale_down_val(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_val *v)
+{
+ if (!fold || !v)
+ goto error;
+
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return fold;
+ }
+ if (!isl_val_is_rat(v))
+ isl_die(isl_qpolynomial_fold_get_ctx(fold), isl_error_invalid,
+ "expecting rational factor", goto error);
+ if (isl_val_is_zero(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "cannot scale down by zero", goto error);
+
+ return isl_qpolynomial_fold_scale_val(fold, isl_val_inv(v));
+error:
+ isl_val_free(v);
+ isl_qpolynomial_fold_free(fold);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_hash.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_hash.c
new file mode 100644
index 00000000000..59cb06e85ed
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_hash.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <stdlib.h>
+#include <isl/hash.h>
+#include <isl/ctx.h>
+#include "isl_config.h"
+
+uint32_t isl_hash_string(uint32_t hash, const char *s)
+{
+ for (; *s; s++)
+ isl_hash_byte(hash, *s);
+ return hash;
+}
+
+uint32_t isl_hash_mem(uint32_t hash, const void *p, size_t len)
+{
+ int i;
+ const char *s = p;
+ for (i = 0; i < len; ++i)
+ isl_hash_byte(hash, s[i]);
+ return hash;
+}
+
+static unsigned int round_up(unsigned int v)
+{
+ int old_v = v;
+
+ while (v) {
+ old_v = v;
+ v ^= v & -v;
+ }
+ return old_v << 1;
+}
+
+int isl_hash_table_init(struct isl_ctx *ctx, struct isl_hash_table *table,
+ int min_size)
+{
+ size_t size;
+
+ if (!table)
+ return -1;
+
+ if (min_size < 2)
+ min_size = 2;
+ table->bits = ffs(round_up(4 * (min_size + 1) / 3 - 1)) - 1;
+ table->n = 0;
+
+ size = 1 << table->bits;
+ table->entries = isl_calloc_array(ctx, struct isl_hash_table_entry,
+ size);
+ if (!table->entries)
+ return -1;
+
+ return 0;
+}
+
+/* Dummy comparison function that always returns false.
+ */
+static isl_bool no(const void *entry, const void *val)
+{
+ return isl_bool_false;
+}
+
+/* Extend "table" to twice its size.
+ * Return 0 on success and -1 on error.
+ *
+ * We reuse isl_hash_table_find to create entries in the extended table.
+ * Since all entries in the original table are assumed to be different,
+ * there is no need to compare them against each other.
+ */
+static int grow_table(struct isl_ctx *ctx, struct isl_hash_table *table)
+{
+ int n;
+ size_t old_size, size;
+ struct isl_hash_table_entry *entries;
+ uint32_t h;
+
+ entries = table->entries;
+ old_size = 1 << table->bits;
+ size = 2 * old_size;
+ table->entries = isl_calloc_array(ctx, struct isl_hash_table_entry,
+ size);
+ if (!table->entries) {
+ table->entries = entries;
+ return -1;
+ }
+
+ n = table->n;
+ table->n = 0;
+ table->bits++;
+
+ for (h = 0; h < old_size; ++h) {
+ struct isl_hash_table_entry *entry;
+
+ if (!entries[h].data)
+ continue;
+
+ entry = isl_hash_table_find(ctx, table, entries[h].hash,
+ &no, NULL, 1);
+ if (!entry) {
+ table->bits--;
+ free(table->entries);
+ table->entries = entries;
+ table->n = n;
+ return -1;
+ }
+
+ *entry = entries[h];
+ }
+
+ free(entries);
+
+ return 0;
+}
+
+struct isl_hash_table *isl_hash_table_alloc(struct isl_ctx *ctx, int min_size)
+{
+ struct isl_hash_table *table = NULL;
+
+ table = isl_alloc_type(ctx, struct isl_hash_table);
+ if (isl_hash_table_init(ctx, table, min_size))
+ goto error;
+ return table;
+error:
+ isl_hash_table_free(ctx, table);
+ return NULL;
+}
+
+void isl_hash_table_clear(struct isl_hash_table *table)
+{
+ if (!table)
+ return;
+ free(table->entries);
+}
+
+void isl_hash_table_free(struct isl_ctx *ctx, struct isl_hash_table *table)
+{
+ if (!table)
+ return;
+ isl_hash_table_clear(table);
+ free(table);
+}
+
+/* A dummy entry that is used by isl_hash_table_find
+ * to make a distinction between a missing entry and an error condition.
+ */
+static struct isl_hash_table_entry none = { 0, NULL };
+struct isl_hash_table_entry *isl_hash_table_entry_none = &none;
+
+struct isl_hash_table_entry *isl_hash_table_find(struct isl_ctx *ctx,
+ struct isl_hash_table *table,
+ uint32_t key_hash,
+ isl_bool (*eq)(const void *entry, const void *val),
+ const void *val, int reserve)
+{
+ size_t size;
+ uint32_t h, key_bits;
+
+ key_bits = isl_hash_bits(key_hash, table->bits);
+ size = 1 << table->bits;
+ for (h = key_bits; table->entries[h].data; h = (h+1) % size) {
+ isl_bool equal;
+
+ if (table->entries[h].hash != key_hash)
+ continue;
+ equal = eq(table->entries[h].data, val);
+ if (equal < 0)
+ return NULL;
+ if (equal)
+ return &table->entries[h];
+ }
+
+ if (!reserve)
+ return isl_hash_table_entry_none;
+
+ if (4 * table->n >= 3 * size) {
+ if (grow_table(ctx, table) < 0)
+ return NULL;
+ return isl_hash_table_find(ctx, table, key_hash, eq, val, 1);
+ }
+
+ table->n++;
+ table->entries[h].hash = key_hash;
+
+ return &table->entries[h];
+}
+
+isl_stat isl_hash_table_foreach(isl_ctx *ctx, struct isl_hash_table *table,
+ isl_stat (*fn)(void **entry, void *user), void *user)
+{
+ size_t size;
+ uint32_t h;
+
+ if (!table->entries)
+ return isl_stat_error;
+
+ size = 1 << table->bits;
+ for (h = 0; h < size; ++ h)
+ if (table->entries[h].data &&
+ fn(&table->entries[h].data, user) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+void isl_hash_table_remove(struct isl_ctx *ctx,
+ struct isl_hash_table *table,
+ struct isl_hash_table_entry *entry)
+{
+ int h, h2;
+ size_t size;
+
+ if (!table || !entry)
+ return;
+
+ size = 1 << table->bits;
+ h = entry - table->entries;
+ isl_assert(ctx, h >= 0 && h < size, return);
+
+ for (h2 = h+1; table->entries[h2 % size].data; h2++) {
+ uint32_t bits = isl_hash_bits(table->entries[h2 % size].hash,
+ table->bits);
+ uint32_t offset = (size + bits - (h+1)) % size;
+ if (offset <= h2 - (h+1))
+ continue;
+ *entry = table->entries[h2 % size];
+ h = h2;
+ entry = &table->entries[h % size];
+ }
+
+ entry->hash = 0;
+ entry->data = NULL;
+ table->n--;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id.c
new file mode 100644
index 00000000000..ab0f75b3693
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <string.h>
+#include <isl_ctx_private.h>
+#include <isl_id_private.h>
+
+#undef EL_BASE
+#define EL_BASE id
+
+#include <isl_list_templ.c>
+#include <isl_list_read_templ.c>
+
+/* A special, static isl_id to use as domains (and ranges)
+ * of sets and parameters domains.
+ * The user should never get a hold on this isl_id.
+ */
+isl_id isl_id_none = {
+ .ref = -1,
+ .ctx = NULL,
+ .name = "#none",
+ .user = NULL
+};
+
+isl_ctx *isl_id_get_ctx(__isl_keep isl_id *id)
+{
+ return id ? id->ctx : NULL;
+}
+
+void *isl_id_get_user(__isl_keep isl_id *id)
+{
+ return id ? id->user : NULL;
+}
+
+const char *isl_id_get_name(__isl_keep isl_id *id)
+{
+ return id ? id->name : NULL;
+}
+
+static __isl_give isl_id *id_alloc(isl_ctx *ctx, const char *name, void *user)
+{
+ const char *copy = name ? strdup(name) : NULL;
+ isl_id *id;
+
+ if (name && !copy)
+ return NULL;
+ id = isl_calloc_type(ctx, struct isl_id);
+ if (!id)
+ goto error;
+
+ id->ctx = ctx;
+ isl_ctx_ref(id->ctx);
+ id->ref = 1;
+ id->name = copy;
+ id->user = user;
+
+ id->hash = isl_hash_init();
+ if (name)
+ id->hash = isl_hash_string(id->hash, name);
+ else
+ id->hash = isl_hash_builtin(id->hash, user);
+
+ return id;
+error:
+ free((char *)copy);
+ return NULL;
+}
+
+uint32_t isl_id_get_hash(__isl_keep isl_id *id)
+{
+ return id ? id->hash : 0;
+}
+
+struct isl_name_and_user {
+ const char *name;
+ void *user;
+};
+
+static isl_bool isl_id_has_name_and_user(const void *entry, const void *val)
+{
+ isl_id *id = (isl_id *)entry;
+ struct isl_name_and_user *nu = (struct isl_name_and_user *) val;
+
+ if (id->user != nu->user)
+ return isl_bool_false;
+ if (id->name == nu->name)
+ return isl_bool_true;
+ if (!id->name || !nu->name)
+ return isl_bool_false;
+
+ return isl_bool_ok(!strcmp(id->name, nu->name));
+}
+
+__isl_give isl_id *isl_id_alloc(isl_ctx *ctx, const char *name, void *user)
+{
+ struct isl_hash_table_entry *entry;
+ uint32_t id_hash;
+ struct isl_name_and_user nu = { name, user };
+
+ if (!ctx)
+ return NULL;
+
+ id_hash = isl_hash_init();
+ if (name)
+ id_hash = isl_hash_string(id_hash, name);
+ else
+ id_hash = isl_hash_builtin(id_hash, user);
+ entry = isl_hash_table_find(ctx, &ctx->id_table, id_hash,
+ isl_id_has_name_and_user, &nu, 1);
+ if (!entry)
+ return NULL;
+ if (entry->data)
+ return isl_id_copy(entry->data);
+ entry->data = id_alloc(ctx, name, user);
+ if (!entry->data)
+ ctx->id_table.n--;
+ return entry->data;
+}
+
+/* If the id has a negative refcount, then it is a static isl_id
+ * which should not be changed.
+ */
+__isl_give isl_id *isl_id_copy(isl_id *id)
+{
+ if (!id)
+ return NULL;
+
+ if (id->ref < 0)
+ return id;
+
+ id->ref++;
+ return id;
+}
+
+/* Compare two isl_ids.
+ *
+ * The order is fairly arbitrary. We do keep the comparison of
+ * the user pointers as a last resort since these pointer values
+ * may not be stable across different systems or even different runs.
+ */
+int isl_id_cmp(__isl_keep isl_id *id1, __isl_keep isl_id *id2)
+{
+ if (id1 == id2)
+ return 0;
+ if (!id1)
+ return -1;
+ if (!id2)
+ return 1;
+ if (!id1->name != !id2->name)
+ return !id1->name - !id2->name;
+ if (id1->name) {
+ int cmp = strcmp(id1->name, id2->name);
+ if (cmp != 0)
+ return cmp;
+ }
+ if (id1->user < id2->user)
+ return -1;
+ else
+ return 1;
+}
+
+static isl_bool isl_id_eq(const void *entry, const void *name)
+{
+ return isl_bool_ok(entry == name);
+}
+
+uint32_t isl_hash_id(uint32_t hash, __isl_keep isl_id *id)
+{
+ if (id)
+ isl_hash_hash(hash, id->hash);
+
+ return hash;
+}
+
+/* Replace the free_user callback by "free_user".
+ */
+__isl_give isl_id *isl_id_set_free_user(__isl_take isl_id *id,
+ void (*free_user)(void *user))
+{
+ if (!id)
+ return NULL;
+
+ id->free_user = free_user;
+
+ return id;
+}
+
+/* If the id has a negative refcount, then it is a static isl_id
+ * and should not be freed.
+ */
+__isl_null isl_id *isl_id_free(__isl_take isl_id *id)
+{
+ struct isl_hash_table_entry *entry;
+
+ if (!id)
+ return NULL;
+
+ if (id->ref < 0)
+ return NULL;
+
+ if (--id->ref > 0)
+ return NULL;
+
+ entry = isl_hash_table_find(id->ctx, &id->ctx->id_table, id->hash,
+ isl_id_eq, id, 0);
+ if (!entry)
+ return NULL;
+ if (entry == isl_hash_table_entry_none)
+ isl_die(id->ctx, isl_error_unknown,
+ "unable to find id", (void)0);
+ else
+ isl_hash_table_remove(id->ctx, &id->ctx->id_table, entry);
+
+ if (id->free_user)
+ id->free_user(id->user);
+
+ free((char *)id->name);
+ isl_ctx_deref(id->ctx);
+ free(id);
+
+ return NULL;
+}
+
+__isl_give isl_printer *isl_printer_print_id(__isl_take isl_printer *p,
+ __isl_keep isl_id *id)
+{
+ if (!id)
+ goto error;
+
+ if (id->name)
+ p = isl_printer_print_str(p, id->name);
+ if (id->user) {
+ char buffer[50];
+ snprintf(buffer, sizeof(buffer), "@%p", id->user);
+ p = isl_printer_print_str(p, buffer);
+ }
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Read an isl_id from "s" based on its name.
+ */
+__isl_give isl_id *isl_stream_read_id(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ char *str;
+ isl_ctx *ctx;
+ isl_id *id;
+
+ if (!s)
+ return NULL;
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return NULL;
+ }
+ ctx = isl_stream_get_ctx(s);
+ str = isl_token_get_str(ctx, tok);
+ isl_token_free(tok);
+ if (!str)
+ return NULL;
+ id = isl_id_alloc(ctx, str, NULL);
+ free(str);
+
+ return id;
+}
+
+/* Read an isl_id object from the string "str".
+ */
+__isl_give isl_id *isl_id_read_from_str(isl_ctx *ctx, const char *str)
+{
+ isl_id *id;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ id = isl_stream_read_id(s);
+ isl_stream_free(s);
+ return id;
+}
+
+/* Is "id1" (obviously) equal to "id2"?
+ *
+ * isl_id objects can be compared by pointer value, but
+ * isl_multi_*_plain_is_equal needs an isl_*_plain_is_equal.
+ */
+static isl_bool isl_id_plain_is_equal(__isl_keep isl_id *id1,
+ __isl_keep isl_id *id2)
+{
+ if (!id1 || !id2)
+ return isl_bool_error;
+ return id1 == id2;
+}
+
+#undef BASE
+#define BASE id
+
+#include <isl_multi_no_domain_templ.c>
+#include <isl_multi_no_explicit_domain.c>
+#include <isl_multi_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_private.h
new file mode 100644
index 00000000000..3b053e87c20
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_private.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_ID_PRIVATE_H
+#define ISL_ID_PRIVATE_H
+
+#include <isl/id.h>
+#include <isl/stream.h>
+
+/* Represent a name and/or user pointer.
+ *
+ * If "free_user" is set, then it will be called on "user" when
+ * the last instance of the isl_id is freed.
+ */
+struct isl_id {
+ int ref;
+ isl_ctx *ctx;
+
+ const char *name;
+ void *user;
+ uint32_t hash;
+
+ __isl_give void (*free_user)(void *user);
+};
+
+#undef EL
+#define EL isl_id
+
+#include <isl_list_templ.h>
+
+uint32_t isl_hash_id(uint32_t hash, __isl_keep isl_id *id);
+int isl_id_cmp(__isl_keep isl_id *id1, __isl_keep isl_id *id2);
+__isl_give isl_id *isl_stream_read_id(__isl_keep isl_stream *s);
+
+extern isl_id isl_id_none;
+
+#undef BASE
+#define BASE id
+
+#include <isl_multi_templ.h>
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_ast_expr.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_ast_expr.c
new file mode 100644
index 00000000000..4bfa9530ab0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_ast_expr.c
@@ -0,0 +1,16 @@
+#include <isl/id_to_ast_expr.h>
+#include <isl/id.h>
+#include <isl/ast.h>
+
+#define isl_id_is_equal(id1,id2) isl_bool_ok(id1 == id2)
+
+#define ISL_KEY isl_id
+#define ISL_VAL isl_ast_expr
+#define ISL_HMAP_SUFFIX id_to_ast_expr
+#define ISL_HMAP isl_id_to_ast_expr
+#define ISL_KEY_IS_EQUAL isl_id_is_equal
+#define ISL_VAL_IS_EQUAL isl_ast_expr_is_equal
+#define ISL_KEY_PRINT isl_printer_print_id
+#define ISL_VAL_PRINT isl_printer_print_ast_expr
+
+#include <isl/hmap_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_id.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_id.c
new file mode 100644
index 00000000000..e92e8f63835
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_id.c
@@ -0,0 +1,16 @@
+#include <isl/ctx.h>
+#include <isl/id_to_id.h>
+#include <isl/id.h>
+
+#define isl_id_is_equal(id1,id2) isl_bool_ok(id1 == id2)
+
+#define ISL_KEY isl_id
+#define ISL_VAL isl_id
+#define ISL_HMAP_SUFFIX id_to_id
+#define ISL_HMAP isl_id_to_id
+#define ISL_KEY_IS_EQUAL isl_id_is_equal
+#define ISL_VAL_IS_EQUAL isl_id_is_equal
+#define ISL_KEY_PRINT isl_printer_print_id
+#define ISL_VAL_PRINT isl_printer_print_id
+
+#include <isl/hmap_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_pw_aff.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_pw_aff.c
new file mode 100644
index 00000000000..1ef27b5eaa7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_id_to_pw_aff.c
@@ -0,0 +1,16 @@
+#include <isl/id_to_pw_aff.h>
+#include <isl/id.h>
+#include <isl/aff.h>
+
+#define isl_id_is_equal(id1,id2) isl_bool_ok(id1 == id2)
+
+#define ISL_KEY isl_id
+#define ISL_VAL isl_pw_aff
+#define ISL_HMAP_SUFFIX id_to_pw_aff
+#define ISL_HMAP isl_id_to_pw_aff
+#define ISL_KEY_IS_EQUAL isl_id_is_equal
+#define ISL_VAL_IS_EQUAL isl_pw_aff_plain_is_equal
+#define ISL_KEY_PRINT isl_printer_print_id
+#define ISL_VAL_PRINT isl_printer_print_pw_aff
+
+#include <isl/hmap_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp.c
new file mode 100644
index 00000000000..fe623ba09cd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp.c
@@ -0,0 +1,912 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl/ilp.h>
+#include <isl/union_set.h>
+#include "isl_sample.h"
+#include <isl_seq.h>
+#include "isl_equalities.h"
+#include <isl_aff_private.h>
+#include <isl_local_space_private.h>
+#include <isl_mat_private.h>
+#include <isl_val_private.h>
+#include <isl_vec_private.h>
+#include <isl_lp_private.h>
+#include <isl_ilp_private.h>
+
+/* Given a basic set "bset", construct a basic set U such that for
+ * each element x in U, the whole unit box positioned at x is inside
+ * the given basic set.
+ * Note that U may not contain all points that satisfy this property.
+ *
+ * We simply add the sum of all negative coefficients to the constant
+ * term. This ensures that if x satisfies the resulting constraints,
+ * then x plus any sum of unit vectors satisfies the original constraints.
+ */
+static __isl_give isl_basic_set *unit_box_base_points(
+ __isl_take isl_basic_set *bset)
+{
+ int i, j, k;
+ struct isl_basic_set *unit_box = NULL;
+ isl_size total;
+
+ if (!bset)
+ goto error;
+
+ if (bset->n_eq != 0) {
+ isl_space *space = isl_basic_set_get_space(bset);
+ isl_basic_set_free(bset);
+ return isl_basic_set_empty(space);
+ }
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ goto error;
+ unit_box = isl_basic_set_alloc_space(isl_basic_set_get_space(bset),
+ 0, 0, bset->n_ineq);
+
+ for (i = 0; i < bset->n_ineq; ++i) {
+ k = isl_basic_set_alloc_inequality(unit_box);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(unit_box->ineq[k], bset->ineq[i], 1 + total);
+ for (j = 0; j < total; ++j) {
+ if (isl_int_is_nonneg(unit_box->ineq[k][1 + j]))
+ continue;
+ isl_int_add(unit_box->ineq[k][0],
+ unit_box->ineq[k][0], unit_box->ineq[k][1 + j]);
+ }
+ }
+
+ isl_basic_set_free(bset);
+ return unit_box;
+error:
+ isl_basic_set_free(bset);
+ isl_basic_set_free(unit_box);
+ return NULL;
+}
+
+/* Find an integer point in "bset", preferably one that is
+ * close to minimizing "f".
+ *
+ * We first check if we can easily put unit boxes inside bset.
+ * If so, we take the best base point of any of the unit boxes we can find
+ * and round it up to the nearest integer.
+ * If not, we simply pick any integer point in "bset".
+ */
+static __isl_give isl_vec *initial_solution(__isl_keep isl_basic_set *bset,
+ isl_int *f)
+{
+ enum isl_lp_result res;
+ struct isl_basic_set *unit_box;
+ struct isl_vec *sol;
+
+ unit_box = unit_box_base_points(isl_basic_set_copy(bset));
+
+ res = isl_basic_set_solve_lp(unit_box, 0, f, bset->ctx->one,
+ NULL, NULL, &sol);
+ if (res == isl_lp_ok) {
+ isl_basic_set_free(unit_box);
+ return isl_vec_ceil(sol);
+ }
+
+ isl_basic_set_free(unit_box);
+
+ return isl_basic_set_sample_vec(isl_basic_set_copy(bset));
+}
+
+/* Restrict "bset" to those points with values for f in the interval [l, u].
+ */
+static __isl_give isl_basic_set *add_bounds(__isl_take isl_basic_set *bset,
+ isl_int *f, isl_int l, isl_int u)
+{
+ int k;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ return isl_basic_set_free(bset);
+ bset = isl_basic_set_extend_constraints(bset, 0, 2);
+
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bset->ineq[k], f, 1 + total);
+ isl_int_sub(bset->ineq[k][0], bset->ineq[k][0], l);
+
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_neg(bset->ineq[k], f, 1 + total);
+ isl_int_add(bset->ineq[k][0], bset->ineq[k][0], u);
+
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Find an integer point in "bset" that minimizes f (in any) such that
+ * the value of f lies inside the interval [l, u].
+ * Return this integer point if it can be found.
+ * Otherwise, return sol.
+ *
+ * We perform a number of steps until l > u.
+ * In each step, we look for an integer point with value in either
+ * the whole interval [l, u] or half of the interval [l, l+floor(u-l-1/2)].
+ * The choice depends on whether we have found an integer point in the
+ * previous step. If so, we look for the next point in half of the remaining
+ * interval.
+ * If we find a point, the current solution is updated and u is set
+ * to its value minus 1.
+ * If no point can be found, we update l to the upper bound of the interval
+ * we checked (u or l+floor(u-l-1/2)) plus 1.
+ */
+static __isl_give isl_vec *solve_ilp_search(__isl_keep isl_basic_set *bset,
+ isl_int *f, isl_int *opt, __isl_take isl_vec *sol, isl_int l, isl_int u)
+{
+ isl_int tmp;
+ int divide = 1;
+
+ isl_int_init(tmp);
+
+ while (isl_int_le(l, u)) {
+ struct isl_basic_set *slice;
+ struct isl_vec *sample;
+
+ if (!divide)
+ isl_int_set(tmp, u);
+ else {
+ isl_int_sub(tmp, u, l);
+ isl_int_fdiv_q_ui(tmp, tmp, 2);
+ isl_int_add(tmp, tmp, l);
+ }
+ slice = add_bounds(isl_basic_set_copy(bset), f, l, tmp);
+ sample = isl_basic_set_sample_vec(slice);
+ if (!sample) {
+ isl_vec_free(sol);
+ sol = NULL;
+ break;
+ }
+ if (sample->size > 0) {
+ isl_vec_free(sol);
+ sol = sample;
+ isl_seq_inner_product(f, sol->el, sol->size, opt);
+ isl_int_sub_ui(u, *opt, 1);
+ divide = 1;
+ } else {
+ isl_vec_free(sample);
+ if (!divide)
+ break;
+ isl_int_add_ui(l, tmp, 1);
+ divide = 0;
+ }
+ }
+
+ isl_int_clear(tmp);
+
+ return sol;
+}
+
+/* Find an integer point in "bset" that minimizes f (if any).
+ * If sol_p is not NULL then the integer point is returned in *sol_p.
+ * The optimal value of f is returned in *opt.
+ *
+ * The algorithm maintains a currently best solution and an interval [l, u]
+ * of values of f for which integer solutions could potentially still be found.
+ * The initial value of the best solution so far is any solution.
+ * The initial value of l is minimal value of f over the rationals
+ * (rounded up to the nearest integer).
+ * The initial value of u is the value of f at the initial solution minus 1.
+ *
+ * We then call solve_ilp_search to perform a binary search on the interval.
+ */
+static enum isl_lp_result solve_ilp(__isl_keep isl_basic_set *bset,
+ isl_int *f, isl_int *opt, __isl_give isl_vec **sol_p)
+{
+ enum isl_lp_result res;
+ isl_int l, u;
+ struct isl_vec *sol;
+
+ res = isl_basic_set_solve_lp(bset, 0, f, bset->ctx->one,
+ opt, NULL, &sol);
+ if (res == isl_lp_ok && isl_int_is_one(sol->el[0])) {
+ if (sol_p)
+ *sol_p = sol;
+ else
+ isl_vec_free(sol);
+ return isl_lp_ok;
+ }
+ isl_vec_free(sol);
+ if (res == isl_lp_error || res == isl_lp_empty)
+ return res;
+
+ sol = initial_solution(bset, f);
+ if (!sol)
+ return isl_lp_error;
+ if (sol->size == 0) {
+ isl_vec_free(sol);
+ return isl_lp_empty;
+ }
+ if (res == isl_lp_unbounded) {
+ isl_vec_free(sol);
+ return isl_lp_unbounded;
+ }
+
+ isl_int_init(l);
+ isl_int_init(u);
+
+ isl_int_set(l, *opt);
+
+ isl_seq_inner_product(f, sol->el, sol->size, opt);
+ isl_int_sub_ui(u, *opt, 1);
+
+ sol = solve_ilp_search(bset, f, opt, sol, l, u);
+ if (!sol)
+ res = isl_lp_error;
+
+ isl_int_clear(l);
+ isl_int_clear(u);
+
+ if (sol_p)
+ *sol_p = sol;
+ else
+ isl_vec_free(sol);
+
+ return res;
+}
+
+static enum isl_lp_result solve_ilp_with_eq(__isl_keep isl_basic_set *bset,
+ int max, isl_int *f, isl_int *opt, __isl_give isl_vec **sol_p)
+{
+ isl_size dim;
+ enum isl_lp_result res;
+ struct isl_mat *T = NULL;
+ struct isl_vec *v;
+
+ bset = isl_basic_set_copy(bset);
+ dim = isl_basic_set_dim(bset, isl_dim_all);
+ if (dim < 0)
+ goto error;
+ v = isl_vec_alloc(bset->ctx, 1 + dim);
+ if (!v)
+ goto error;
+ isl_seq_cpy(v->el, f, 1 + dim);
+ bset = isl_basic_set_remove_equalities(bset, &T, NULL);
+ v = isl_vec_mat_product(v, isl_mat_copy(T));
+ if (!v)
+ goto error;
+ res = isl_basic_set_solve_ilp(bset, max, v->el, opt, sol_p);
+ isl_vec_free(v);
+ if (res == isl_lp_ok && sol_p) {
+ *sol_p = isl_mat_vec_product(T, *sol_p);
+ if (!*sol_p)
+ res = isl_lp_error;
+ } else
+ isl_mat_free(T);
+ isl_basic_set_free(bset);
+ return res;
+error:
+ isl_mat_free(T);
+ isl_basic_set_free(bset);
+ return isl_lp_error;
+}
+
+/* Find an integer point in "bset" that minimizes (or maximizes if max is set)
+ * f (if any).
+ * If sol_p is not NULL then the integer point is returned in *sol_p.
+ * The optimal value of f is returned in *opt.
+ *
+ * If there is any equality among the points in "bset", then we first
+ * project it out. Otherwise, we continue with solve_ilp above.
+ */
+enum isl_lp_result isl_basic_set_solve_ilp(__isl_keep isl_basic_set *bset,
+ int max, isl_int *f, isl_int *opt, __isl_give isl_vec **sol_p)
+{
+ isl_size dim;
+ enum isl_lp_result res;
+
+ if (sol_p)
+ *sol_p = NULL;
+
+ if (isl_basic_set_check_no_params(bset) < 0)
+ return isl_lp_error;
+
+ if (isl_basic_set_plain_is_empty(bset))
+ return isl_lp_empty;
+
+ if (bset->n_eq)
+ return solve_ilp_with_eq(bset, max, f, opt, sol_p);
+
+ dim = isl_basic_set_dim(bset, isl_dim_all);
+ if (dim < 0)
+ return isl_lp_error;
+
+ if (max)
+ isl_seq_neg(f, f, 1 + dim);
+
+ res = solve_ilp(bset, f, opt, sol_p);
+
+ if (max) {
+ isl_seq_neg(f, f, 1 + dim);
+ isl_int_neg(*opt, *opt);
+ }
+
+ return res;
+}
+
+static enum isl_lp_result basic_set_opt(__isl_keep isl_basic_set *bset, int max,
+ __isl_keep isl_aff *obj, isl_int *opt)
+{
+ enum isl_lp_result res;
+
+ if (!obj)
+ return isl_lp_error;
+ bset = isl_basic_set_copy(bset);
+ bset = isl_basic_set_underlying_set(bset);
+ res = isl_basic_set_solve_ilp(bset, max, obj->v->el + 1, opt, NULL);
+ isl_basic_set_free(bset);
+ return res;
+}
+
+enum isl_lp_result isl_basic_set_opt(__isl_keep isl_basic_set *bset, int max,
+ __isl_keep isl_aff *obj, isl_int *opt)
+{
+ int *exp1 = NULL;
+ int *exp2 = NULL;
+ isl_ctx *ctx;
+ isl_mat *bset_div = NULL;
+ isl_mat *div = NULL;
+ enum isl_lp_result res;
+ isl_size bset_n_div, obj_n_div;
+
+ if (!bset || !obj)
+ return isl_lp_error;
+
+ ctx = isl_aff_get_ctx(obj);
+ if (!isl_space_is_equal(bset->dim, obj->ls->dim))
+ isl_die(ctx, isl_error_invalid,
+ "spaces don't match", return isl_lp_error);
+ if (!isl_int_is_one(obj->v->el[0]))
+ isl_die(ctx, isl_error_unsupported,
+ "expecting integer affine expression",
+ return isl_lp_error);
+
+ bset_n_div = isl_basic_set_dim(bset, isl_dim_div);
+ obj_n_div = isl_aff_dim(obj, isl_dim_div);
+ if (bset_n_div < 0 || obj_n_div < 0)
+ return isl_lp_error;
+ if (bset_n_div == 0 && obj_n_div == 0)
+ return basic_set_opt(bset, max, obj, opt);
+
+ bset = isl_basic_set_copy(bset);
+ obj = isl_aff_copy(obj);
+
+ bset_div = isl_basic_set_get_divs(bset);
+ exp1 = isl_alloc_array(ctx, int, bset_n_div);
+ exp2 = isl_alloc_array(ctx, int, obj_n_div);
+ if (!bset_div || (bset_n_div && !exp1) || (obj_n_div && !exp2))
+ goto error;
+
+ div = isl_merge_divs(bset_div, obj->ls->div, exp1, exp2);
+
+ bset = isl_basic_set_expand_divs(bset, isl_mat_copy(div), exp1);
+ obj = isl_aff_expand_divs(obj, isl_mat_copy(div), exp2);
+
+ res = basic_set_opt(bset, max, obj, opt);
+
+ isl_mat_free(bset_div);
+ isl_mat_free(div);
+ free(exp1);
+ free(exp2);
+ isl_basic_set_free(bset);
+ isl_aff_free(obj);
+
+ return res;
+error:
+ isl_mat_free(div);
+ isl_mat_free(bset_div);
+ free(exp1);
+ free(exp2);
+ isl_basic_set_free(bset);
+ isl_aff_free(obj);
+ return isl_lp_error;
+}
+
+/* Compute the minimum (maximum if max is set) of the integer affine
+ * expression obj over the points in set and put the result in *opt.
+ *
+ * The parameters are assumed to have been aligned.
+ */
+static enum isl_lp_result isl_set_opt_aligned(__isl_keep isl_set *set, int max,
+ __isl_keep isl_aff *obj, isl_int *opt)
+{
+ int i;
+ enum isl_lp_result res;
+ int empty = 1;
+ isl_int opt_i;
+
+ if (!set || !obj)
+ return isl_lp_error;
+ if (set->n == 0)
+ return isl_lp_empty;
+
+ res = isl_basic_set_opt(set->p[0], max, obj, opt);
+ if (res == isl_lp_error || res == isl_lp_unbounded)
+ return res;
+ if (set->n == 1)
+ return res;
+ if (res == isl_lp_ok)
+ empty = 0;
+
+ isl_int_init(opt_i);
+ for (i = 1; i < set->n; ++i) {
+ res = isl_basic_set_opt(set->p[i], max, obj, &opt_i);
+ if (res == isl_lp_error || res == isl_lp_unbounded) {
+ isl_int_clear(opt_i);
+ return res;
+ }
+ if (res == isl_lp_empty)
+ continue;
+ empty = 0;
+ if (max ? isl_int_gt(opt_i, *opt) : isl_int_lt(opt_i, *opt))
+ isl_int_set(*opt, opt_i);
+ }
+ isl_int_clear(opt_i);
+
+ return empty ? isl_lp_empty : isl_lp_ok;
+}
+
+/* Compute the minimum (maximum if max is set) of the integer affine
+ * expression obj over the points in set and put the result in *opt.
+ */
+enum isl_lp_result isl_set_opt(__isl_keep isl_set *set, int max,
+ __isl_keep isl_aff *obj, isl_int *opt)
+{
+ enum isl_lp_result res;
+ isl_bool aligned;
+
+ if (!set || !obj)
+ return isl_lp_error;
+
+ aligned = isl_set_space_has_equal_params(set, obj->ls->dim);
+ if (aligned < 0)
+ return isl_lp_error;
+ if (aligned)
+ return isl_set_opt_aligned(set, max, obj, opt);
+
+ set = isl_set_copy(set);
+ obj = isl_aff_copy(obj);
+ set = isl_set_align_params(set, isl_aff_get_domain_space(obj));
+ obj = isl_aff_align_params(obj, isl_set_get_space(set));
+
+ res = isl_set_opt_aligned(set, max, obj, opt);
+
+ isl_set_free(set);
+ isl_aff_free(obj);
+
+ return res;
+}
+
+/* Convert the result of a function that returns an isl_lp_result
+ * to an isl_val. The numerator of "v" is set to the optimal value
+ * if lp_res is isl_lp_ok. "max" is set if a maximum was computed.
+ *
+ * Return "v" with denominator set to 1 if lp_res is isl_lp_ok.
+ * Return NULL on error.
+ * Return a NaN if lp_res is isl_lp_empty.
+ * Return infinity or negative infinity if lp_res is isl_lp_unbounded,
+ * depending on "max".
+ */
+static __isl_give isl_val *convert_lp_result(enum isl_lp_result lp_res,
+ __isl_take isl_val *v, int max)
+{
+ isl_ctx *ctx;
+
+ if (lp_res == isl_lp_ok) {
+ isl_int_set_si(v->d, 1);
+ return isl_val_normalize(v);
+ }
+ ctx = isl_val_get_ctx(v);
+ isl_val_free(v);
+ if (lp_res == isl_lp_error)
+ return NULL;
+ if (lp_res == isl_lp_empty)
+ return isl_val_nan(ctx);
+ if (max)
+ return isl_val_infty(ctx);
+ else
+ return isl_val_neginfty(ctx);
+}
+
+/* Return the minimum (maximum if max is set) of the integer affine
+ * expression "obj" over the points in "bset".
+ *
+ * Return infinity or negative infinity if the optimal value is unbounded and
+ * NaN if "bset" is empty.
+ *
+ * Call isl_basic_set_opt and translate the results.
+ */
+__isl_give isl_val *isl_basic_set_opt_val(__isl_keep isl_basic_set *bset,
+ int max, __isl_keep isl_aff *obj)
+{
+ isl_ctx *ctx;
+ isl_val *res;
+ enum isl_lp_result lp_res;
+
+ if (!bset || !obj)
+ return NULL;
+
+ ctx = isl_aff_get_ctx(obj);
+ res = isl_val_alloc(ctx);
+ if (!res)
+ return NULL;
+ lp_res = isl_basic_set_opt(bset, max, obj, &res->n);
+ return convert_lp_result(lp_res, res, max);
+}
+
+/* Return the maximum of the integer affine
+ * expression "obj" over the points in "bset".
+ *
+ * Return infinity or negative infinity if the optimal value is unbounded and
+ * NaN if "bset" is empty.
+ */
+__isl_give isl_val *isl_basic_set_max_val(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_aff *obj)
+{
+ return isl_basic_set_opt_val(bset, 1, obj);
+}
+
+/* Return the minimum (maximum if max is set) of the integer affine
+ * expression "obj" over the points in "set".
+ *
+ * Return infinity or negative infinity if the optimal value is unbounded and
+ * NaN if "set" is empty.
+ *
+ * Call isl_set_opt and translate the results.
+ */
+__isl_give isl_val *isl_set_opt_val(__isl_keep isl_set *set, int max,
+ __isl_keep isl_aff *obj)
+{
+ isl_ctx *ctx;
+ isl_val *res;
+ enum isl_lp_result lp_res;
+
+ if (!set || !obj)
+ return NULL;
+
+ ctx = isl_aff_get_ctx(obj);
+ res = isl_val_alloc(ctx);
+ if (!res)
+ return NULL;
+ lp_res = isl_set_opt(set, max, obj, &res->n);
+ return convert_lp_result(lp_res, res, max);
+}
+
+/* Return the minimum of the integer affine
+ * expression "obj" over the points in "set".
+ *
+ * Return infinity or negative infinity if the optimal value is unbounded and
+ * NaN if "set" is empty.
+ */
+__isl_give isl_val *isl_set_min_val(__isl_keep isl_set *set,
+ __isl_keep isl_aff *obj)
+{
+ return isl_set_opt_val(set, 0, obj);
+}
+
+/* Return the maximum of the integer affine
+ * expression "obj" over the points in "set".
+ *
+ * Return infinity or negative infinity if the optimal value is unbounded and
+ * NaN if "set" is empty.
+ */
+__isl_give isl_val *isl_set_max_val(__isl_keep isl_set *set,
+ __isl_keep isl_aff *obj)
+{
+ return isl_set_opt_val(set, 1, obj);
+}
+
+/* Return the optimum (min or max depending on "max") of "v1" and "v2",
+ * where either may be NaN, signifying an uninitialized value.
+ * That is, if either is NaN, then return the other one.
+ */
+static __isl_give isl_val *val_opt(__isl_take isl_val *v1,
+ __isl_take isl_val *v2, int max)
+{
+ if (!v1 || !v2)
+ goto error;
+ if (isl_val_is_nan(v1)) {
+ isl_val_free(v1);
+ return v2;
+ }
+ if (isl_val_is_nan(v2)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (max)
+ return isl_val_max(v1, v2);
+ else
+ return isl_val_min(v1, v2);
+error:
+ isl_val_free(v1);
+ isl_val_free(v2);
+ return NULL;
+}
+
+/* Internal data structure for isl_pw_aff_opt_val.
+ *
+ * "max" is set if the maximum should be computed.
+ * "res" contains the current optimum and is initialized to NaN.
+ */
+struct isl_pw_aff_opt_data {
+ int max;
+
+ isl_val *res;
+};
+
+/* Update the optimum in data->res with respect to the affine function
+ * "aff" defined over "set".
+ */
+static isl_stat piece_opt(__isl_take isl_set *set, __isl_take isl_aff *aff,
+ void *user)
+{
+ struct isl_pw_aff_opt_data *data = user;
+ isl_val *opt;
+
+ opt = isl_set_opt_val(set, data->max, aff);
+ isl_set_free(set);
+ isl_aff_free(aff);
+
+ data->res = val_opt(data->res, opt, data->max);
+ if (!data->res)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Return the minimum (maximum if "max" is set) of the integer piecewise affine
+ * expression "pa" over its definition domain.
+ *
+ * Return infinity or negative infinity if the optimal value is unbounded and
+ * NaN if the domain of "pa" is empty.
+ *
+ * Initialize the result to NaN and then update it for each of the pieces
+ * in "pa".
+ */
+static __isl_give isl_val *isl_pw_aff_opt_val(__isl_take isl_pw_aff *pa,
+ int max)
+{
+ struct isl_pw_aff_opt_data data = { max };
+
+ data.res = isl_val_nan(isl_pw_aff_get_ctx(pa));
+ if (isl_pw_aff_foreach_piece(pa, &piece_opt, &data) < 0)
+ data.res = isl_val_free(data.res);
+
+ isl_pw_aff_free(pa);
+ return data.res;
+}
+
+#undef TYPE
+#define TYPE isl_pw_multi_aff
+#include "isl_ilp_opt_multi_val_templ.c"
+
+#undef TYPE
+#define TYPE isl_multi_pw_aff
+#include "isl_ilp_opt_multi_val_templ.c"
+
+/* Internal data structure for isl_union_pw_aff_opt_val.
+ *
+ * "max" is set if the maximum should be computed.
+ * "res" contains the current optimum and is initialized to NaN.
+ */
+struct isl_union_pw_aff_opt_data {
+ int max;
+
+ isl_val *res;
+};
+
+/* Update the optimum in data->res with the optimum of "pa".
+ */
+static isl_stat pw_aff_opt(__isl_take isl_pw_aff *pa, void *user)
+{
+ struct isl_union_pw_aff_opt_data *data = user;
+ isl_val *opt;
+
+ opt = isl_pw_aff_opt_val(pa, data->max);
+
+ data->res = val_opt(data->res, opt, data->max);
+ if (!data->res)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Return the minimum (maximum if "max" is set) of the integer piecewise affine
+ * expression "upa" over its definition domain.
+ *
+ * Return infinity or negative infinity if the optimal value is unbounded and
+ * NaN if the domain of the expression is empty.
+ *
+ * Initialize the result to NaN and then update it
+ * for each of the piecewise affine expressions in "upa".
+ */
+static __isl_give isl_val *isl_union_pw_aff_opt_val(
+ __isl_take isl_union_pw_aff *upa, int max)
+{
+ struct isl_union_pw_aff_opt_data data = { max };
+
+ data.res = isl_val_nan(isl_union_pw_aff_get_ctx(upa));
+ if (isl_union_pw_aff_foreach_pw_aff(upa, &pw_aff_opt, &data) < 0)
+ data.res = isl_val_free(data.res);
+ isl_union_pw_aff_free(upa);
+
+ return data.res;
+}
+
+/* Return the minimum of the integer piecewise affine
+ * expression "upa" over its definition domain.
+ *
+ * Return negative infinity if the optimal value is unbounded and
+ * NaN if the domain of the expression is empty.
+ */
+__isl_give isl_val *isl_union_pw_aff_min_val(__isl_take isl_union_pw_aff *upa)
+{
+ return isl_union_pw_aff_opt_val(upa, 0);
+}
+
+/* Return the maximum of the integer piecewise affine
+ * expression "upa" over its definition domain.
+ *
+ * Return infinity if the optimal value is unbounded and
+ * NaN if the domain of the expression is empty.
+ */
+__isl_give isl_val *isl_union_pw_aff_max_val(__isl_take isl_union_pw_aff *upa)
+{
+ return isl_union_pw_aff_opt_val(upa, 1);
+}
+
+/* Return a list of minima (maxima if "max" is set)
+ * for each of the expressions in "mupa" over their domains.
+ *
+ * An element in the list is infinity or negative infinity if the optimal
+ * value of the corresponding expression is unbounded and
+ * NaN if the domain of the expression is empty.
+ *
+ * Iterate over all the expressions in "mupa" and collect the results.
+ */
+static __isl_give isl_multi_val *isl_multi_union_pw_aff_opt_multi_val(
+ __isl_take isl_multi_union_pw_aff *mupa, int max)
+{
+ int i;
+ isl_size n;
+ isl_multi_val *mv;
+
+ n = isl_multi_union_pw_aff_size(mupa);
+ if (n < 0)
+ mupa = isl_multi_union_pw_aff_free(mupa);
+ if (!mupa)
+ return NULL;
+
+ mv = isl_multi_val_zero(isl_multi_union_pw_aff_get_space(mupa));
+
+ for (i = 0; i < n; ++i) {
+ isl_val *v;
+ isl_union_pw_aff *upa;
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, i);
+ v = isl_union_pw_aff_opt_val(upa, max);
+ mv = isl_multi_val_set_val(mv, i, v);
+ }
+
+ isl_multi_union_pw_aff_free(mupa);
+ return mv;
+}
+
+/* Return a list of minima (maxima if "max" is set) over the points in "uset"
+ * for each of the expressions in "obj".
+ *
+ * An element in the list is infinity or negative infinity if the optimal
+ * value of the corresponding expression is unbounded and
+ * NaN if the intersection of "uset" with the domain of the expression
+ * is empty.
+ */
+static __isl_give isl_multi_val *isl_union_set_opt_multi_union_pw_aff(
+ __isl_keep isl_union_set *uset, int max,
+ __isl_keep isl_multi_union_pw_aff *obj)
+{
+ uset = isl_union_set_copy(uset);
+ obj = isl_multi_union_pw_aff_copy(obj);
+ obj = isl_multi_union_pw_aff_intersect_domain(obj, uset);
+ return isl_multi_union_pw_aff_opt_multi_val(obj, max);
+}
+
+/* Return a list of minima over the points in "uset"
+ * for each of the expressions in "obj".
+ *
+ * An element in the list is infinity or negative infinity if the optimal
+ * value of the corresponding expression is unbounded and
+ * NaN if the intersection of "uset" with the domain of the expression
+ * is empty.
+ */
+__isl_give isl_multi_val *isl_union_set_min_multi_union_pw_aff(
+ __isl_keep isl_union_set *uset, __isl_keep isl_multi_union_pw_aff *obj)
+{
+ return isl_union_set_opt_multi_union_pw_aff(uset, 0, obj);
+}
+
+/* Return a list of minima
+ * for each of the expressions in "mupa" over their domains.
+ *
+ * An element in the list is negative infinity if the optimal
+ * value of the corresponding expression is unbounded and
+ * NaN if the domain of the expression is empty.
+ */
+__isl_give isl_multi_val *isl_multi_union_pw_aff_min_multi_val(
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ return isl_multi_union_pw_aff_opt_multi_val(mupa, 0);
+}
+
+/* Return a list of maxima
+ * for each of the expressions in "mupa" over their domains.
+ *
+ * An element in the list is infinity if the optimal
+ * value of the corresponding expression is unbounded and
+ * NaN if the domain of the expression is empty.
+ */
+__isl_give isl_multi_val *isl_multi_union_pw_aff_max_multi_val(
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ return isl_multi_union_pw_aff_opt_multi_val(mupa, 1);
+}
+
+#undef BASE
+#define BASE basic_set
+#include "isl_ilp_opt_val_templ.c"
+
+/* Return the maximal value attained by the given set dimension,
+ * independently of the parameter values and of any other dimensions.
+ *
+ * Return infinity if the optimal value is unbounded and
+ * NaN if "bset" is empty.
+ */
+__isl_give isl_val *isl_basic_set_dim_max_val(__isl_take isl_basic_set *bset,
+ int pos)
+{
+ return isl_basic_set_dim_opt_val(bset, 1, pos);
+}
+
+#undef BASE
+#define BASE set
+#include "isl_ilp_opt_val_templ.c"
+
+/* Return the minimal value attained by the given set dimension,
+ * independently of the parameter values and of any other dimensions.
+ *
+ * Return negative infinity if the optimal value is unbounded and
+ * NaN if "set" is empty.
+ */
+__isl_give isl_val *isl_set_dim_min_val(__isl_take isl_set *set, int pos)
+{
+ return isl_set_dim_opt_val(set, 0, pos);
+}
+
+/* Return the maximal value attained by the given set dimension,
+ * independently of the parameter values and of any other dimensions.
+ *
+ * Return infinity if the optimal value is unbounded and
+ * NaN if "set" is empty.
+ */
+__isl_give isl_val *isl_set_dim_max_val(__isl_take isl_set *set, int pos)
+{
+ return isl_set_dim_opt_val(set, 1, pos);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_opt_multi_val_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_opt_multi_val_templ.c
new file mode 100644
index 00000000000..13d0230b593
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_opt_multi_val_templ.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Return a list of minima (maxima if "max" is set)
+ * for each of the expressions in "f" over their (shared) domain.
+ *
+ * An element in the list is infinity or negative infinity if the optimal
+ * value of the corresponding expression is unbounded and
+ * NaN if the domain of the expression is empty.
+ *
+ * Iterate over all the expressions in "f" and collect the results.
+ */
+static __isl_give isl_multi_val *FN(TYPE,opt_multi_val)(__isl_take TYPE *f,
+ int max)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_multi_val *mv;
+
+ n = FN(TYPE,dim)(f, isl_dim_out);
+ if (n < 0)
+ f = FN(TYPE,free)(f);
+ if (!f)
+ return NULL;
+
+ space = isl_space_range(FN(TYPE,get_space)(f));
+ space = isl_space_drop_all_params(space);
+ mv = isl_multi_val_zero(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_val *v;
+ isl_pw_aff *pa;
+
+ pa = FN(TYPE,get_pw_aff)(f, i);
+ v = isl_pw_aff_opt_val(pa, max);
+ mv = isl_multi_val_set_val(mv, i, v);
+ }
+
+ FN(TYPE,free)(f);
+ return mv;
+}
+
+/* Return a list of minima
+ * for each of the expressions in "f" over their (shared) domain.
+ *
+ * An element in the list is negative infinity if the optimal
+ * value of the corresponding expression is unbounded and
+ * NaN if the domain of the expression is empty.
+ */
+__isl_give isl_multi_val *FN(TYPE,min_multi_val)(__isl_take TYPE *f)
+{
+ return FN(TYPE,opt_multi_val)(f, 0);
+}
+
+/* Return a list of maxima
+ * for each of the expressions in "f" over their (shared) domain.
+ *
+ * An element in the list is infinity if the optimal
+ * value of the corresponding expression is unbounded and
+ * NaN if the domain of the expression is empty.
+ */
+__isl_give isl_multi_val *FN(TYPE,max_multi_val)(__isl_take TYPE *f)
+{
+ return FN(TYPE,opt_multi_val)(f, 1);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_opt_val_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_opt_val_templ.c
new file mode 100644
index 00000000000..7e3520d6546
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_opt_val_templ.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege
+ */
+
+#define xCAT(A,B) A ## B
+#define CAT(A,B) xCAT(A,B)
+#undef TYPE
+#define TYPE CAT(isl_,BASE)
+#define xBFN(BASE,NAME) isl_ ## BASE ## _ ## NAME
+#define BFN(BASE,NAME) xBFN(BASE,NAME)
+
+/* Return the minimal (maximal if "max" is set) value attained
+ * by the given set dimension,
+ * independently of the parameter values and of any other dimensions.
+ *
+ * Return infinity or negative infinity if the optimal value is unbounded and
+ * NaN if "set" is empty.
+ */
+static __isl_give isl_val *BFN(BASE,dim_opt_val)(__isl_take TYPE *set, int max,
+ int pos)
+{
+ isl_local_space *ls;
+ isl_aff *obj;
+ isl_val *v;
+
+ if (BFN(BASE,check_range)(set, isl_dim_set, pos, 1) < 0)
+ goto error;
+ ls = isl_local_space_from_space(BFN(BASE,get_space)(set));
+ obj = isl_aff_var_on_domain(ls, isl_dim_set, pos);
+ v = BFN(BASE,opt_val)(set, max, obj);
+ isl_aff_free(obj);
+ BFN(BASE,free)(set);
+
+ return v;
+error:
+ BFN(BASE,free)(set);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_private.h
new file mode 100644
index 00000000000..932b2c3d4f2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_ilp_private.h
@@ -0,0 +1,11 @@
+#ifndef ISL_ILP_PRIVATE_H
+#define ISL_ILP_PRIVATE_H
+
+#include <isl_int.h>
+#include <isl/lp.h>
+#include <isl/set.h>
+
+enum isl_lp_result isl_basic_set_solve_ilp(__isl_keep isl_basic_set *bset,
+ int max, isl_int *f, isl_int *opt, __isl_give isl_vec **sol_p);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_imath.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_imath.c
new file mode 100644
index 00000000000..d870f1486da
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_imath.c
@@ -0,0 +1,83 @@
+#include <isl_int.h>
+
+uint32_t isl_imath_hash(mp_int v, uint32_t hash)
+{
+ unsigned const char *data = (unsigned char *)v->digits;
+ unsigned const char *end = data + v->used * sizeof(v->digits[0]);
+
+ if (v->sign == 1)
+ isl_hash_byte(hash, 0xFF);
+ for (; data < end; ++data)
+ isl_hash_byte(hash, *data);
+ return hash;
+}
+
+/* Try a standard conversion that fits into a long.
+ */
+int isl_imath_fits_slong_p(mp_int op)
+{
+ long out;
+ mp_result res = mp_int_to_int(op, &out);
+ return res == MP_OK;
+}
+
+/* Try a standard conversion that fits into an unsigned long.
+ */
+int isl_imath_fits_ulong_p(mp_int op)
+{
+ unsigned long out;
+ mp_result res = mp_int_to_uint(op, &out);
+ return res == MP_OK;
+}
+
+void isl_imath_addmul_ui(mp_int rop, mp_int op1, unsigned long op2)
+{
+ mpz_t temp;
+ mp_int_init(&temp);
+
+ mp_int_set_uvalue(&temp, op2);
+ mp_int_mul(op1, &temp, &temp);
+ mp_int_add(rop, &temp, rop);
+
+ mp_int_clear(&temp);
+}
+
+void isl_imath_submul_ui(mp_int rop, mp_int op1, unsigned long op2)
+{
+ mpz_t temp;
+ mp_int_init(&temp);
+
+ mp_int_set_uvalue(&temp, op2);
+ mp_int_mul(op1, &temp, &temp);
+ mp_int_sub(rop, &temp, rop);
+
+ mp_int_clear(&temp);
+}
+
+/* Compute the division of lhs by a rhs of type unsigned long, rounding towards
+ * positive infinity (Ceil).
+ */
+void isl_imath_cdiv_q_ui(mp_int rop, mp_int lhs, unsigned long rhs)
+{
+ mpz_t temp;
+ mp_int_init(&temp);
+
+ mp_int_set_uvalue(&temp, rhs);
+ impz_cdiv_q(rop, lhs, &temp);
+
+ mp_int_clear(&temp);
+}
+
+/* Compute the division of lhs by a rhs of type unsigned long, rounding towards
+ * negative infinity (Floor).
+ */
+void isl_imath_fdiv_q_ui(mp_int rop, mp_int lhs, unsigned long rhs)
+{
+ mpz_t temp;
+ mp_int_init(&temp);
+
+ mp_int_set_uvalue(&temp, rhs);
+ impz_fdiv_q(rop, lhs, &temp);
+
+ mp_int_clear(&temp);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_imath.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_imath.h
new file mode 100644
index 00000000000..9efbe319ee2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_imath.h
@@ -0,0 +1,10 @@
+#include <imath.h>
+#include <gmp_compat.h>
+
+uint32_t isl_imath_hash(mp_int v, uint32_t hash);
+int isl_imath_fits_ulong_p(mp_int op);
+int isl_imath_fits_slong_p(mp_int op);
+void isl_imath_addmul_ui(mp_int rop, mp_int op1, unsigned long op2);
+void isl_imath_submul_ui(mp_int rop, mp_int op1, unsigned long op2);
+void isl_imath_cdiv_q_ui(mp_int rop, mp_int op1, unsigned long op2);
+void isl_imath_fdiv_q_ui(mp_int rop, mp_int op1, unsigned long op2);
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_input.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_input.c
new file mode 100644
index 00000000000..4c6839d15d8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_input.c
@@ -0,0 +1,4491 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2012-2013 Ecole Normale Superieure
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ * and Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl_id_private.h>
+#include <isl/set.h>
+#include <isl_seq.h>
+#include <isl_stream_private.h>
+#include <isl/obj.h>
+#include "isl_polynomial_private.h"
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl_mat_private.h>
+#include <isl_aff_private.h>
+#include <isl_vec_private.h>
+#include <isl/list.h>
+#include <isl_val_private.h>
+
+struct variable {
+ char *name;
+ int pos;
+ struct variable *next;
+};
+
+struct vars {
+ struct isl_ctx *ctx;
+ int n;
+ struct variable *v;
+};
+
+static struct vars *vars_new(struct isl_ctx *ctx)
+{
+ struct vars *v;
+ v = isl_alloc_type(ctx, struct vars);
+ if (!v)
+ return NULL;
+ v->ctx = ctx;
+ v->n = 0;
+ v->v = NULL;
+ return v;
+}
+
+static void variable_free(struct variable *var)
+{
+ while (var) {
+ struct variable *next = var->next;
+ free(var->name);
+ free(var);
+ var = next;
+ }
+}
+
+static void vars_free(struct vars *v)
+{
+ if (!v)
+ return;
+ variable_free(v->v);
+ free(v);
+}
+
+static void vars_drop(struct vars *v, int n)
+{
+ struct variable *var;
+
+ if (!v || !v->v)
+ return;
+
+ v->n -= n;
+
+ var = v->v;
+ while (--n >= 0) {
+ struct variable *next = var->next;
+ free(var->name);
+ free(var);
+ var = next;
+ }
+ v->v = var;
+}
+
+static struct variable *variable_new(struct vars *v, const char *name, int len,
+ int pos)
+{
+ struct variable *var;
+ var = isl_calloc_type(v->ctx, struct variable);
+ if (!var)
+ goto error;
+ var->name = strdup(name);
+ var->name[len] = '\0';
+ var->pos = pos;
+ var->next = v->v;
+ return var;
+error:
+ variable_free(v->v);
+ return NULL;
+}
+
+static int vars_pos(struct vars *v, const char *s, int len)
+{
+ int pos;
+ struct variable *q;
+
+ if (len == -1)
+ len = strlen(s);
+ for (q = v->v; q; q = q->next) {
+ if (strncmp(q->name, s, len) == 0 && q->name[len] == '\0')
+ break;
+ }
+ if (q)
+ pos = q->pos;
+ else {
+ pos = v->n;
+ v->v = variable_new(v, s, len, v->n);
+ if (!v->v)
+ return -1;
+ v->n++;
+ }
+ return pos;
+}
+
+static int vars_add_anon(struct vars *v)
+{
+ v->v = variable_new(v, "", 0, v->n);
+
+ if (!v->v)
+ return -1;
+ v->n++;
+
+ return 0;
+}
+
+/* Obtain next token, with some preprocessing.
+ * In particular, evaluate expressions of the form x^y,
+ * with x and y values.
+ */
+static struct isl_token *next_token(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok, *tok2;
+
+ tok = isl_stream_next_token(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE)
+ return tok;
+ if (!isl_stream_eat_if_available(s, '^'))
+ return tok;
+ tok2 = isl_stream_next_token(s);
+ if (!tok2 || tok2->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok2, "expecting constant value");
+ goto error;
+ }
+
+ isl_int_pow_ui(tok->u.v, tok->u.v, isl_int_get_ui(tok2->u.v));
+
+ isl_token_free(tok2);
+ return tok;
+error:
+ isl_token_free(tok);
+ isl_token_free(tok2);
+ return NULL;
+}
+
+/* Read an isl_val from "s".
+ *
+ * The following token sequences are recognized
+ *
+ * "infty" -> infty
+ * "-" "infty" -> -infty
+ * "NaN" -> NaN
+ * n "/" d -> n/d
+ * v -> v
+ *
+ * where n, d and v are integer constants.
+ */
+__isl_give isl_val *isl_stream_read_val(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok = NULL;
+ struct isl_token *tok2 = NULL;
+ isl_val *val;
+
+ tok = next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ goto error;
+ }
+ if (tok->type == ISL_TOKEN_INFTY) {
+ isl_token_free(tok);
+ return isl_val_infty(s->ctx);
+ }
+ if (tok->type == '-' &&
+ isl_stream_eat_if_available(s, ISL_TOKEN_INFTY)) {
+ isl_token_free(tok);
+ return isl_val_neginfty(s->ctx);
+ }
+ if (tok->type == ISL_TOKEN_NAN) {
+ isl_token_free(tok);
+ return isl_val_nan(s->ctx);
+ }
+ if (tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok, "expecting value");
+ goto error;
+ }
+
+ if (isl_stream_eat_if_available(s, '/')) {
+ tok2 = next_token(s);
+ if (!tok2) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ goto error;
+ }
+ if (tok2->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok2, "expecting value");
+ goto error;
+ }
+ val = isl_val_rat_from_isl_int(s->ctx, tok->u.v, tok2->u.v);
+ val = isl_val_normalize(val);
+ } else {
+ val = isl_val_int_from_isl_int(s->ctx, tok->u.v);
+ }
+
+ isl_token_free(tok);
+ isl_token_free(tok2);
+ return val;
+error:
+ isl_token_free(tok);
+ isl_token_free(tok2);
+ return NULL;
+}
+
+/* Read an isl_val from "str".
+ */
+__isl_give isl_val *isl_val_read_from_str(isl_ctx *ctx, const char *str)
+{
+ isl_val *val;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ val = isl_stream_read_val(s);
+ isl_stream_free(s);
+ return val;
+}
+
+/* Perform an integer division on *f and
+ * an integer value read from the stream.
+ */
+static isl_stat int_div_by_cst(__isl_keep isl_stream *s, isl_int *f)
+{
+ struct isl_token *tok;
+
+ tok = next_token(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok, "expecting constant value");
+ goto error;
+ }
+
+ isl_int_fdiv_q(*f, *f, tok->u.v);
+
+ isl_token_free(tok);
+
+ return isl_stat_ok;
+error:
+ isl_token_free(tok);
+ return isl_stat_error;
+}
+
+static isl_stat accept_cst_factor(__isl_keep isl_stream *s, isl_int *f)
+{
+ struct isl_token *tok;
+
+ tok = next_token(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok, "expecting constant value");
+ goto error;
+ }
+
+ isl_int_mul(*f, *f, tok->u.v);
+
+ isl_token_free(tok);
+
+ if (isl_stream_eat_if_available(s, '*'))
+ return accept_cst_factor(s, f);
+
+ return isl_stat_ok;
+error:
+ isl_token_free(tok);
+ return isl_stat_error;
+}
+
+/* Given an affine expression aff, return an affine expression
+ * for aff % d, with d the next token on the stream, which is
+ * assumed to be a constant.
+ *
+ * We introduce an integer division q = [aff/d] and the result
+ * is set to aff - d q.
+ */
+static __isl_give isl_pw_aff *affine_mod(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_pw_aff *aff)
+{
+ struct isl_token *tok;
+ isl_pw_aff *q;
+
+ tok = next_token(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok, "expecting constant value");
+ goto error;
+ }
+
+ q = isl_pw_aff_copy(aff);
+ q = isl_pw_aff_scale_down(q, tok->u.v);
+ q = isl_pw_aff_floor(q);
+ q = isl_pw_aff_scale(q, tok->u.v);
+
+ aff = isl_pw_aff_sub(aff, q);
+
+ isl_token_free(tok);
+ return aff;
+error:
+ isl_pw_aff_free(aff);
+ isl_token_free(tok);
+ return NULL;
+}
+
+static __isl_give isl_pw_aff *accept_affine(__isl_keep isl_stream *s,
+ __isl_take isl_space *space, struct vars *v);
+static __isl_give isl_pw_aff_list *accept_affine_list(__isl_keep isl_stream *s,
+ __isl_take isl_space *space, struct vars *v);
+
+static __isl_give isl_pw_aff *accept_minmax(__isl_keep isl_stream *s,
+ __isl_take isl_space *space, struct vars *v)
+{
+ struct isl_token *tok;
+ isl_pw_aff_list *list = NULL;
+ int min;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ goto error;
+ min = tok->type == ISL_TOKEN_MIN;
+ isl_token_free(tok);
+
+ if (isl_stream_eat(s, '('))
+ goto error;
+
+ list = accept_affine_list(s, isl_space_copy(space), v);
+ if (!list)
+ goto error;
+
+ if (isl_stream_eat(s, ')'))
+ goto error;
+
+ isl_space_free(space);
+ return min ? isl_pw_aff_list_min(list) : isl_pw_aff_list_max(list);
+error:
+ isl_space_free(space);
+ isl_pw_aff_list_free(list);
+ return NULL;
+}
+
+/* Is "tok" the start of an integer division?
+ */
+static int is_start_of_div(struct isl_token *tok)
+{
+ if (!tok)
+ return 0;
+ if (tok->type == '[')
+ return 1;
+ if (tok->type == ISL_TOKEN_FLOOR)
+ return 1;
+ if (tok->type == ISL_TOKEN_CEIL)
+ return 1;
+ if (tok->type == ISL_TOKEN_FLOORD)
+ return 1;
+ if (tok->type == ISL_TOKEN_CEILD)
+ return 1;
+ return 0;
+}
+
+/* Read an integer division from "s" and return it as an isl_pw_aff.
+ *
+ * The integer division can be of the form
+ *
+ * [<affine expression>]
+ * floor(<affine expression>)
+ * ceil(<affine expression>)
+ * floord(<affine expression>,<denominator>)
+ * ceild(<affine expression>,<denominator>)
+ */
+static __isl_give isl_pw_aff *accept_div(__isl_keep isl_stream *s,
+ __isl_take isl_space *space, struct vars *v)
+{
+ struct isl_token *tok;
+ int f = 0;
+ int c = 0;
+ int extra = 0;
+ isl_pw_aff *pwaff = NULL;
+
+ if (isl_stream_eat_if_available(s, ISL_TOKEN_FLOORD))
+ extra = f = 1;
+ else if (isl_stream_eat_if_available(s, ISL_TOKEN_CEILD))
+ extra = c = 1;
+ else if (isl_stream_eat_if_available(s, ISL_TOKEN_FLOOR))
+ f = 1;
+ else if (isl_stream_eat_if_available(s, ISL_TOKEN_CEIL))
+ c = 1;
+ if (f || c) {
+ if (isl_stream_eat(s, '('))
+ goto error;
+ } else {
+ if (isl_stream_eat(s, '['))
+ goto error;
+ }
+
+ pwaff = accept_affine(s, isl_space_copy(space), v);
+
+ if (extra) {
+ if (isl_stream_eat(s, ','))
+ goto error;
+
+ tok = next_token(s);
+ if (!tok)
+ goto error;
+ if (tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok, "expected denominator");
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ pwaff = isl_pw_aff_scale_down(pwaff, tok->u.v);
+ isl_token_free(tok);
+ }
+
+ if (c)
+ pwaff = isl_pw_aff_ceil(pwaff);
+ else
+ pwaff = isl_pw_aff_floor(pwaff);
+
+ if (f || c) {
+ if (isl_stream_eat(s, ')'))
+ goto error;
+ } else {
+ if (isl_stream_eat(s, ']'))
+ goto error;
+ }
+
+ isl_space_free(space);
+ return pwaff;
+error:
+ isl_space_free(space);
+ isl_pw_aff_free(pwaff);
+ return NULL;
+}
+
+/* Divide "pa" by an integer constant read from the stream.
+ */
+static __isl_give isl_pw_aff *pw_aff_div_by_cst(__isl_keep isl_stream *s,
+ __isl_take isl_pw_aff *pa)
+{
+ isl_int f;
+ isl_int_init(f);
+ isl_int_set_si(f, 1);
+ if (accept_cst_factor(s, &f) < 0)
+ pa = isl_pw_aff_free(pa);
+ pa = isl_pw_aff_scale_down(pa, f);
+ isl_int_clear(f);
+
+ return pa;
+}
+
+static __isl_give isl_pw_aff *accept_affine_factor(__isl_keep isl_stream *s,
+ __isl_take isl_space *space, struct vars *v)
+{
+ struct isl_token *tok = NULL;
+ isl_pw_aff *res = NULL;
+
+ tok = next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ goto error;
+ }
+
+ if (tok->type == ISL_TOKEN_AFF) {
+ res = isl_pw_aff_copy(tok->u.pwaff);
+ isl_token_free(tok);
+ } else if (tok->type == ISL_TOKEN_IDENT) {
+ int n = v->n;
+ int pos = vars_pos(v, tok->u.s, -1);
+ isl_aff *aff;
+
+ if (pos < 0)
+ goto error;
+ if (pos >= n) {
+ vars_drop(v, v->n - n);
+ isl_stream_error(s, tok, "unknown identifier");
+ goto error;
+ }
+
+ aff = isl_aff_zero_on_domain(isl_local_space_from_space(isl_space_copy(space)));
+ if (!aff)
+ goto error;
+ aff->v = isl_vec_set_element_si(aff->v, 2 + pos, 1);
+ if (!aff->v)
+ aff = isl_aff_free(aff);
+ res = isl_pw_aff_from_aff(aff);
+ isl_token_free(tok);
+ } else if (tok->type == ISL_TOKEN_VALUE) {
+ if (isl_stream_eat_if_available(s, '*')) {
+ res = accept_affine_factor(s, isl_space_copy(space), v);
+ res = isl_pw_aff_scale(res, tok->u.v);
+ } else {
+ isl_local_space *ls;
+ isl_aff *aff;
+ ls = isl_local_space_from_space(isl_space_copy(space));
+ aff = isl_aff_zero_on_domain(ls);
+ aff = isl_aff_add_constant(aff, tok->u.v);
+ res = isl_pw_aff_from_aff(aff);
+ }
+ isl_token_free(tok);
+ } else if (tok->type == '(') {
+ isl_token_free(tok);
+ tok = NULL;
+ res = accept_affine(s, isl_space_copy(space), v);
+ if (!res)
+ goto error;
+ if (isl_stream_eat(s, ')'))
+ goto error;
+ } else if (is_start_of_div(tok)) {
+ isl_stream_push_token(s, tok);
+ tok = NULL;
+ res = accept_div(s, isl_space_copy(space), v);
+ } else if (tok->type == ISL_TOKEN_MIN || tok->type == ISL_TOKEN_MAX) {
+ isl_stream_push_token(s, tok);
+ tok = NULL;
+ res = accept_minmax(s, isl_space_copy(space), v);
+ } else {
+ isl_stream_error(s, tok, "expecting factor");
+ goto error;
+ }
+ if (isl_stream_eat_if_available(s, '%') ||
+ isl_stream_eat_if_available(s, ISL_TOKEN_MOD)) {
+ isl_space_free(space);
+ return affine_mod(s, v, res);
+ }
+ if (isl_stream_eat_if_available(s, '*')) {
+ isl_int f;
+ isl_int_init(f);
+ isl_int_set_si(f, 1);
+ if (accept_cst_factor(s, &f) < 0) {
+ isl_int_clear(f);
+ goto error2;
+ }
+ res = isl_pw_aff_scale(res, f);
+ isl_int_clear(f);
+ }
+ if (isl_stream_eat_if_available(s, '/'))
+ res = pw_aff_div_by_cst(s, res);
+ if (isl_stream_eat_if_available(s, ISL_TOKEN_INT_DIV))
+ res = isl_pw_aff_floor(pw_aff_div_by_cst(s, res));
+
+ isl_space_free(space);
+ return res;
+error:
+ isl_token_free(tok);
+error2:
+ isl_pw_aff_free(res);
+ isl_space_free(space);
+ return NULL;
+}
+
+static __isl_give isl_pw_aff *add_cst(__isl_take isl_pw_aff *pwaff, isl_int v)
+{
+ isl_aff *aff;
+ isl_space *space;
+
+ space = isl_pw_aff_get_domain_space(pwaff);
+ aff = isl_aff_zero_on_domain(isl_local_space_from_space(space));
+ aff = isl_aff_add_constant(aff, v);
+
+ return isl_pw_aff_add(pwaff, isl_pw_aff_from_aff(aff));
+}
+
+/* Return a piecewise affine expression defined on the specified domain
+ * that represents NaN.
+ */
+static __isl_give isl_pw_aff *nan_on_domain(__isl_keep isl_space *space)
+{
+ return isl_pw_aff_nan_on_domain_space(isl_space_copy(space));
+}
+
+static __isl_give isl_pw_aff *accept_affine(__isl_keep isl_stream *s,
+ __isl_take isl_space *space, struct vars *v)
+{
+ struct isl_token *tok = NULL;
+ isl_local_space *ls;
+ isl_pw_aff *res;
+ int sign = 1;
+
+ ls = isl_local_space_from_space(isl_space_copy(space));
+ res = isl_pw_aff_from_aff(isl_aff_zero_on_domain(ls));
+ if (!res)
+ goto error;
+
+ for (;;) {
+ tok = next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ goto error;
+ }
+ if (tok->type == '-') {
+ sign = -sign;
+ isl_token_free(tok);
+ continue;
+ }
+ if (tok->type == '(' || is_start_of_div(tok) ||
+ tok->type == ISL_TOKEN_MIN || tok->type == ISL_TOKEN_MAX ||
+ tok->type == ISL_TOKEN_IDENT ||
+ tok->type == ISL_TOKEN_AFF) {
+ isl_pw_aff *term;
+ isl_stream_push_token(s, tok);
+ tok = NULL;
+ term = accept_affine_factor(s,
+ isl_space_copy(space), v);
+ if (sign < 0)
+ res = isl_pw_aff_sub(res, term);
+ else
+ res = isl_pw_aff_add(res, term);
+ if (!res)
+ goto error;
+ sign = 1;
+ } else if (tok->type == ISL_TOKEN_VALUE) {
+ if (sign < 0)
+ isl_int_neg(tok->u.v, tok->u.v);
+ if (isl_stream_eat_if_available(s, '*') ||
+ isl_stream_next_token_is(s, ISL_TOKEN_IDENT)) {
+ isl_pw_aff *term;
+ term = accept_affine_factor(s,
+ isl_space_copy(space), v);
+ term = isl_pw_aff_scale(term, tok->u.v);
+ res = isl_pw_aff_add(res, term);
+ if (!res)
+ goto error;
+ } else {
+ if (isl_stream_eat_if_available(s,
+ ISL_TOKEN_INT_DIV) &&
+ int_div_by_cst(s, &tok->u.v) < 0)
+ goto error;
+ res = add_cst(res, tok->u.v);
+ }
+ sign = 1;
+ } else if (tok->type == ISL_TOKEN_NAN) {
+ res = isl_pw_aff_add(res, nan_on_domain(space));
+ } else {
+ isl_stream_error(s, tok, "unexpected isl_token");
+ isl_stream_push_token(s, tok);
+ isl_pw_aff_free(res);
+ isl_space_free(space);
+ return NULL;
+ }
+ isl_token_free(tok);
+
+ tok = next_token(s);
+ if (tok && tok->type == '-') {
+ sign = -sign;
+ isl_token_free(tok);
+ } else if (tok && tok->type == '+') {
+ /* nothing */
+ isl_token_free(tok);
+ } else if (tok && tok->type == ISL_TOKEN_VALUE &&
+ isl_int_is_neg(tok->u.v)) {
+ isl_stream_push_token(s, tok);
+ } else {
+ if (tok)
+ isl_stream_push_token(s, tok);
+ break;
+ }
+ }
+
+ isl_space_free(space);
+ return res;
+error:
+ isl_space_free(space);
+ isl_token_free(tok);
+ isl_pw_aff_free(res);
+ return NULL;
+}
+
+/* Is "type" the type of a comparison operator between lists
+ * of affine expressions?
+ */
+static int is_list_comparator_type(int type)
+{
+ switch (type) {
+ case ISL_TOKEN_LEX_LT:
+ case ISL_TOKEN_LEX_GT:
+ case ISL_TOKEN_LEX_LE:
+ case ISL_TOKEN_LEX_GE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int is_comparator(struct isl_token *tok)
+{
+ if (!tok)
+ return 0;
+ if (is_list_comparator_type(tok->type))
+ return 1;
+
+ switch (tok->type) {
+ case ISL_TOKEN_LT:
+ case ISL_TOKEN_GT:
+ case ISL_TOKEN_LE:
+ case ISL_TOKEN_GE:
+ case ISL_TOKEN_NE:
+ case '=':
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static __isl_give isl_map *read_formula(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_map *map, int rational);
+static __isl_give isl_pw_aff *accept_extended_affine(__isl_keep isl_stream *s,
+ __isl_take isl_space *space, struct vars *v, int rational);
+
+/* Accept a ternary operator, given the first argument.
+ */
+static __isl_give isl_pw_aff *accept_ternary(__isl_keep isl_stream *s,
+ __isl_take isl_map *cond, struct vars *v, int rational)
+{
+ isl_space *space;
+ isl_pw_aff *pwaff1 = NULL, *pwaff2 = NULL, *pa_cond;
+
+ if (!cond)
+ return NULL;
+
+ if (isl_stream_eat(s, '?'))
+ goto error;
+
+ space = isl_space_wrap(isl_map_get_space(cond));
+ pwaff1 = accept_extended_affine(s, space, v, rational);
+ if (!pwaff1)
+ goto error;
+
+ if (isl_stream_eat(s, ':'))
+ goto error;
+
+ space = isl_pw_aff_get_domain_space(pwaff1);
+ pwaff2 = accept_extended_affine(s, space, v, rational);
+ if (!pwaff2)
+ goto error;
+
+ pa_cond = isl_set_indicator_function(isl_map_wrap(cond));
+ return isl_pw_aff_cond(pa_cond, pwaff1, pwaff2);
+error:
+ isl_map_free(cond);
+ isl_pw_aff_free(pwaff1);
+ isl_pw_aff_free(pwaff2);
+ return NULL;
+}
+
+/* Set *line and *col to those of the next token, if any.
+ */
+static void set_current_line_col(__isl_keep isl_stream *s, int *line, int *col)
+{
+ struct isl_token *tok;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return;
+
+ *line = tok->line;
+ *col = tok->col;
+ isl_stream_push_token(s, tok);
+}
+
+/* Push a token encapsulating "pa" onto "s", with the given
+ * line and column.
+ */
+static isl_stat push_aff(__isl_keep isl_stream *s, int line, int col,
+ __isl_take isl_pw_aff *pa)
+{
+ struct isl_token *tok;
+
+ tok = isl_token_new(s->ctx, line, col, 0);
+ if (!tok)
+ goto error;
+ tok->type = ISL_TOKEN_AFF;
+ tok->u.pwaff = pa;
+ isl_stream_push_token(s, tok);
+
+ return isl_stat_ok;
+error:
+ isl_pw_aff_free(pa);
+ return isl_stat_error;
+}
+
+/* Is the next token a comparison operator?
+ */
+static int next_is_comparator(__isl_keep isl_stream *s)
+{
+ int is_comp;
+ struct isl_token *tok;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+
+ is_comp = is_comparator(tok);
+ isl_stream_push_token(s, tok);
+
+ return is_comp;
+}
+
+/* Accept an affine expression that may involve ternary operators.
+ * We first read an affine expression.
+ * If it is not followed by a comparison operator, we simply return it.
+ * Otherwise, we assume the affine expression is part of the first
+ * argument of a ternary operator and try to parse that.
+ */
+static __isl_give isl_pw_aff *accept_extended_affine(__isl_keep isl_stream *s,
+ __isl_take isl_space *space, struct vars *v, int rational)
+{
+ isl_map *cond;
+ isl_pw_aff *pwaff;
+ int line = -1, col = -1;
+
+ set_current_line_col(s, &line, &col);
+
+ pwaff = accept_affine(s, space, v);
+ if (rational)
+ pwaff = isl_pw_aff_set_rational(pwaff);
+ if (!pwaff)
+ return NULL;
+ if (!next_is_comparator(s))
+ return pwaff;
+
+ space = isl_pw_aff_get_domain_space(pwaff);
+ cond = isl_map_universe(isl_space_unwrap(space));
+
+ if (push_aff(s, line, col, pwaff) < 0)
+ cond = isl_map_free(cond);
+ if (!cond)
+ return NULL;
+
+ cond = read_formula(s, v, cond, rational);
+
+ return accept_ternary(s, cond, v, rational);
+}
+
+static __isl_give isl_map *read_var_def(__isl_keep isl_stream *s,
+ __isl_take isl_map *map, enum isl_dim_type type, struct vars *v,
+ int rational)
+{
+ isl_pw_aff *def;
+ isl_size pos;
+ isl_map *def_map;
+
+ if (type == isl_dim_param)
+ pos = isl_map_dim(map, isl_dim_param);
+ else {
+ pos = isl_map_dim(map, isl_dim_in);
+ if (type == isl_dim_out) {
+ isl_size n_out = isl_map_dim(map, isl_dim_out);
+ if (pos < 0 || n_out < 0)
+ return isl_map_free(map);
+ pos += n_out;
+ }
+ type = isl_dim_in;
+ }
+ if (pos < 0)
+ return isl_map_free(map);
+ --pos;
+
+ def = accept_extended_affine(s, isl_space_wrap(isl_map_get_space(map)),
+ v, rational);
+ def_map = isl_map_from_pw_aff(def);
+ def_map = isl_map_equate(def_map, type, pos, isl_dim_out, 0);
+ def_map = isl_set_unwrap(isl_map_domain(def_map));
+
+ map = isl_map_intersect(map, def_map);
+
+ return map;
+}
+
+static __isl_give isl_pw_aff_list *accept_affine_list(__isl_keep isl_stream *s,
+ __isl_take isl_space *space, struct vars *v)
+{
+ isl_pw_aff *pwaff;
+ isl_pw_aff_list *list;
+ struct isl_token *tok = NULL;
+
+ pwaff = accept_affine(s, isl_space_copy(space), v);
+ list = isl_pw_aff_list_from_pw_aff(pwaff);
+ if (!list)
+ goto error;
+
+ for (;;) {
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ goto error;
+ }
+ if (tok->type != ',') {
+ isl_stream_push_token(s, tok);
+ break;
+ }
+ isl_token_free(tok);
+
+ pwaff = accept_affine(s, isl_space_copy(space), v);
+ list = isl_pw_aff_list_concat(list,
+ isl_pw_aff_list_from_pw_aff(pwaff));
+ if (!list)
+ goto error;
+ }
+
+ isl_space_free(space);
+ return list;
+error:
+ isl_space_free(space);
+ isl_pw_aff_list_free(list);
+ return NULL;
+}
+
+static __isl_give isl_map *read_defined_var_list(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_map *map, int rational)
+{
+ struct isl_token *tok;
+
+ while ((tok = isl_stream_next_token(s)) != NULL) {
+ int p;
+ int n = v->n;
+
+ if (tok->type != ISL_TOKEN_IDENT)
+ break;
+
+ p = vars_pos(v, tok->u.s, -1);
+ if (p < 0)
+ goto error;
+ if (p < n) {
+ isl_stream_error(s, tok, "expecting unique identifier");
+ goto error;
+ }
+
+ map = isl_map_add_dims(map, isl_dim_out, 1);
+
+ isl_token_free(tok);
+ tok = isl_stream_next_token(s);
+ if (tok && tok->type == '=') {
+ isl_token_free(tok);
+ map = read_var_def(s, map, isl_dim_out, v, rational);
+ tok = isl_stream_next_token(s);
+ }
+
+ if (!tok || tok->type != ',')
+ break;
+
+ isl_token_free(tok);
+ }
+ if (tok)
+ isl_stream_push_token(s, tok);
+
+ return map;
+error:
+ isl_token_free(tok);
+ isl_map_free(map);
+ return NULL;
+}
+
+static int next_is_tuple(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ int is_tuple;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+ if (tok->type == '[') {
+ isl_stream_push_token(s, tok);
+ return 1;
+ }
+ if (tok->type != ISL_TOKEN_IDENT && !tok->is_keyword) {
+ isl_stream_push_token(s, tok);
+ return 0;
+ }
+
+ is_tuple = isl_stream_next_token_is(s, '[');
+
+ isl_stream_push_token(s, tok);
+
+ return is_tuple;
+}
+
+/* Does the next token mark the end of a tuple element?
+ */
+static int next_is_end_tuple_element(__isl_keep isl_stream *s)
+{
+ return isl_stream_next_token_is(s, ',') ||
+ isl_stream_next_token_is(s, ']');
+}
+
+/* Is the next token one that necessarily forms the start of a condition?
+ */
+static int next_is_condition_start(__isl_keep isl_stream *s)
+{
+ return isl_stream_next_token_is(s, ISL_TOKEN_EXISTS) ||
+ isl_stream_next_token_is(s, ISL_TOKEN_NOT) ||
+ isl_stream_next_token_is(s, ISL_TOKEN_TRUE) ||
+ isl_stream_next_token_is(s, ISL_TOKEN_FALSE) ||
+ isl_stream_next_token_is(s, ISL_TOKEN_MAP);
+}
+
+/* Is "pa" an expression in term of earlier dimensions?
+ * The alternative is that the dimension is defined to be equal to itself,
+ * meaning that it has a universe domain and an expression that depends
+ * on itself. "i" is the position of the expression in a sequence
+ * of "n" expressions. The final dimensions of "pa" correspond to
+ * these "n" expressions.
+ */
+static isl_bool pw_aff_is_expr(__isl_keep isl_pw_aff *pa, int i, int n)
+{
+ isl_aff *aff;
+
+ if (!pa)
+ return isl_bool_error;
+ if (pa->n != 1)
+ return isl_bool_true;
+ if (!isl_set_plain_is_universe(pa->p[0].set))
+ return isl_bool_true;
+
+ aff = pa->p[0].aff;
+ if (isl_int_is_zero(aff->v->el[aff->v->size - n + i]))
+ return isl_bool_true;
+ return isl_bool_false;
+}
+
+/* Does the tuple contain any dimensions that are defined
+ * in terms of earlier dimensions?
+ */
+static isl_bool tuple_has_expr(__isl_keep isl_multi_pw_aff *tuple)
+{
+ int i;
+ isl_size n;
+ isl_bool has_expr = isl_bool_false;
+ isl_pw_aff *pa;
+
+ n = isl_multi_pw_aff_dim(tuple, isl_dim_out);
+ if (n < 0)
+ return isl_bool_error;
+ for (i = 0; i < n; ++i) {
+ pa = isl_multi_pw_aff_get_pw_aff(tuple, i);
+ has_expr = pw_aff_is_expr(pa, i, n);
+ isl_pw_aff_free(pa);
+ if (has_expr < 0 || has_expr)
+ break;
+ }
+
+ return has_expr;
+}
+
+/* Set the name of dimension "pos" in "space" to "name".
+ * During printing, we add primes if the same name appears more than once
+ * to distinguish the occurrences. Here, we remove those primes from "name"
+ * before setting the name of the dimension.
+ */
+static __isl_give isl_space *space_set_dim_name(__isl_take isl_space *space,
+ int pos, char *name)
+{
+ char *prime;
+
+ if (!name)
+ return space;
+
+ prime = strchr(name, '\'');
+ if (prime)
+ *prime = '\0';
+ space = isl_space_set_dim_name(space, isl_dim_out, pos, name);
+ if (prime)
+ *prime = '\'';
+
+ return space;
+}
+
+/* Construct an isl_pw_aff defined on a "space" (with v->n variables)
+ * that is equal to the last of those variables.
+ */
+static __isl_give isl_pw_aff *identity_tuple_el_on_space(
+ __isl_take isl_space *space, struct vars *v)
+{
+ isl_aff *aff;
+
+ aff = isl_aff_zero_on_domain(isl_local_space_from_space(space));
+ aff = isl_aff_add_coefficient_si(aff, isl_dim_in, v->n - 1, 1);
+ return isl_pw_aff_from_aff(aff);
+}
+
+/* Construct an isl_pw_aff defined on the domain space of "pa"
+ * that is equal to the last variable in "v".
+ *
+ * That is, if D is the domain space of "pa", then construct
+ *
+ * D[..., i] -> i.
+ */
+static __isl_give isl_pw_aff *init_range(__isl_keep isl_pw_aff *pa,
+ struct vars *v)
+{
+ isl_space *space;
+
+ space = isl_pw_aff_get_domain_space(pa);
+ return identity_tuple_el_on_space(space, v);
+}
+
+/* Impose the lower bound "lower" on the variable represented by "range_pa".
+ *
+ * In particular, "range_pa" is of the form
+ *
+ * D[..., i] -> i : C
+ *
+ * with D also the domains space of "lower' and "C" some constraints.
+ *
+ * Return the expression
+ *
+ * D[..., i] -> i : C and i >= lower
+ */
+static __isl_give isl_pw_aff *set_lower(__isl_take isl_pw_aff *range_pa,
+ __isl_take isl_pw_aff *lower)
+{
+ isl_set *range;
+
+ range = isl_pw_aff_ge_set(isl_pw_aff_copy(range_pa), lower);
+ return isl_pw_aff_intersect_domain(range_pa, range);
+}
+
+/* Impose the upper bound "upper" on the variable represented by "range_pa".
+ *
+ * In particular, "range_pa" is of the form
+ *
+ * D[..., i] -> i : C
+ *
+ * with D also the domains space of "upper' and "C" some constraints.
+ *
+ * Return the expression
+ *
+ * D[..., i] -> i : C and i <= upper
+ */
+static __isl_give isl_pw_aff *set_upper(__isl_take isl_pw_aff *range_pa,
+ __isl_take isl_pw_aff *upper)
+{
+ isl_set *range;
+
+ range = isl_pw_aff_le_set(isl_pw_aff_copy(range_pa), upper);
+ return isl_pw_aff_intersect_domain(range_pa, range);
+}
+
+/* Construct a piecewise affine expression corresponding
+ * to the last variable in "v" that is greater than or equal to "pa".
+ *
+ * In particular, if D is the domain space of "pa",
+ * then construct the expression
+ *
+ * D[..., i] -> i,
+ *
+ * impose lower bound "pa" and return
+ *
+ * D[..., i] -> i : i >= pa
+ */
+static __isl_give isl_pw_aff *construct_lower(__isl_take isl_pw_aff *pa,
+ struct vars *v)
+{
+ return set_lower(init_range(pa, v), pa);
+}
+
+/* Construct a piecewise affine expression corresponding
+ * to the last variable in "v" that is smaller than or equal to "pa".
+ *
+ * In particular, if D is the domain space of "pa",
+ * then construct the expression
+ *
+ * D[..., i] -> i,
+ *
+ * impose lower bound "pa" and return
+ *
+ * D[..., i] -> i : i <= pa
+ */
+static __isl_give isl_pw_aff *construct_upper(__isl_take isl_pw_aff *pa,
+ struct vars *v)
+{
+ return set_upper(init_range(pa, v), pa);
+}
+
+/* Construct a piecewise affine expression corresponding
+ * to the last variable in "v" that ranges between "pa" and "pa2".
+ *
+ * In particular, if D is the domain space of "pa" (and "pa2"),
+ * then construct the expression
+ *
+ * D[..., i] -> i,
+ *
+ * impose lower bound "pa" and upper bound "pa2" and return
+ *
+ * D[..., i] -> i : pa <= i <= pa2
+ */
+static __isl_give isl_pw_aff *construct_range(__isl_take isl_pw_aff *pa,
+ __isl_take isl_pw_aff *pa2, struct vars *v)
+{
+ return set_upper(set_lower(init_range(pa, v), pa), pa2);
+}
+
+static int resolve_paren_expr(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_map *map, int rational);
+
+/* Given that the (piecewise) affine expression "pa"
+ * has just been parsed, followed by a colon,
+ * continue parsing as part of a piecewise affine expression.
+ *
+ * In particular, check if the colon is followed by a condition.
+ * If so, parse the conditions(a) on "pa" and include them in the domain.
+ * Otherwise, if the colon is followed by another (piecewise) affine expression
+ * then consider the two expressions as endpoints of a range of values and
+ * return a piecewise affine expression that takes values in that range.
+ * Note that an affine expression followed by a comparison operator
+ * is considered to be part of a condition.
+ * If the colon is not followed by anything (inside the tuple element),
+ * then consider "pa" as a lower bound on a range of values without upper bound
+ * and return a piecewise affine expression that takes values in that range.
+ */
+static __isl_give isl_pw_aff *update_piecewise_affine_colon(
+ __isl_take isl_pw_aff *pa, __isl_keep isl_stream *s,
+ struct vars *v, int rational)
+{
+ isl_space *dom_space;
+ isl_map *map;
+
+ dom_space = isl_pw_aff_get_domain_space(pa);
+ map = isl_map_universe(isl_space_from_domain(dom_space));
+
+ if (isl_stream_next_token_is(s, '('))
+ if (resolve_paren_expr(s, v, isl_map_copy(map), rational))
+ goto error;
+ if (next_is_end_tuple_element(s)) {
+ isl_map_free(map);
+ return construct_lower(pa, v);
+ }
+ if (!next_is_condition_start(s)) {
+ int line = -1, col = -1;
+ isl_space *space;
+ isl_pw_aff *pa2;
+
+ set_current_line_col(s, &line, &col);
+ space = isl_space_wrap(isl_map_get_space(map));
+ pa2 = accept_affine(s, space, v);
+ if (rational)
+ pa2 = isl_pw_aff_set_rational(pa2);
+ if (!next_is_comparator(s)) {
+ isl_map_free(map);
+ pa2 = isl_pw_aff_domain_factor_domain(pa2);
+ return construct_range(pa, pa2, v);
+ }
+ if (push_aff(s, line, col, pa2) < 0)
+ goto error;
+ }
+
+ map = read_formula(s, v, map, rational);
+ pa = isl_pw_aff_intersect_domain(pa, isl_map_domain(map));
+
+ return pa;
+error:
+ isl_map_free(map);
+ isl_pw_aff_free(pa);
+ return NULL;
+}
+
+/* Accept a piecewise affine expression.
+ *
+ * At the outer level, the piecewise affine expression may be of the form
+ *
+ * aff1 : condition1; aff2 : conditions2; ...
+ *
+ * or one of
+ *
+ * aff :
+ * aff1 : aff2
+ * : aff
+ * :
+ *
+ * or simply
+ *
+ * aff
+ *
+ * each of the affine expressions may in turn include ternary operators.
+ *
+ * If the first token is a colon, then the expression must be
+ * ":" or ": aff2", depending on whether anything follows the colon
+ * inside the tuple element.
+ * The first is considered to represent an arbitrary value.
+ * The second is considered to represent a range of values
+ * with the given upper bound and no lower bound.
+ *
+ * There may be parentheses around some subexpression of "aff1"
+ * around "aff1" itself, around "aff1 : condition1" and/or
+ * around the entire piecewise affine expression.
+ * We therefore remove the opening parenthesis (if any) from the stream
+ * in case the closing parenthesis follows the colon, but if the closing
+ * parenthesis is the first thing in the stream after the parsed affine
+ * expression, we push the parsed expression onto the stream and parse
+ * again in case the parentheses enclose some subexpression of "aff1".
+ */
+static __isl_give isl_pw_aff *accept_piecewise_affine(__isl_keep isl_stream *s,
+ __isl_take isl_space *space, struct vars *v, int rational)
+{
+ isl_pw_aff *res;
+ isl_space *res_space;
+
+ if (isl_stream_eat_if_available(s, ':')) {
+ if (next_is_end_tuple_element(s))
+ return identity_tuple_el_on_space(space, v);
+ else
+ return construct_upper(accept_affine(s, space, v), v);
+ }
+
+ res_space = isl_space_from_domain(isl_space_copy(space));
+ res_space = isl_space_add_dims(res_space, isl_dim_out, 1);
+ res = isl_pw_aff_empty(res_space);
+ do {
+ isl_pw_aff *pa;
+ int seen_paren;
+ int line = -1, col = -1;
+
+ set_current_line_col(s, &line, &col);
+ seen_paren = isl_stream_eat_if_available(s, '(');
+ if (seen_paren)
+ pa = accept_piecewise_affine(s, isl_space_copy(space),
+ v, rational);
+ else
+ pa = accept_extended_affine(s, isl_space_copy(space),
+ v, rational);
+ if (seen_paren && isl_stream_eat_if_available(s, ')')) {
+ seen_paren = 0;
+ if (push_aff(s, line, col, pa) < 0)
+ goto error;
+ pa = accept_extended_affine(s, isl_space_copy(space),
+ v, rational);
+ }
+ if (isl_stream_eat_if_available(s, ':'))
+ pa = update_piecewise_affine_colon(pa, s, v, rational);
+
+ res = isl_pw_aff_union_add(res, pa);
+
+ if (seen_paren && isl_stream_eat(s, ')'))
+ goto error;
+ } while (isl_stream_eat_if_available(s, ';'));
+
+ isl_space_free(space);
+
+ return res;
+error:
+ isl_space_free(space);
+ return isl_pw_aff_free(res);
+}
+
+/* Read an affine expression from "s" for use in read_tuple.
+ *
+ * accept_extended_affine requires a wrapped space as input.
+ * read_tuple on the other hand expects each isl_pw_aff
+ * to have an anonymous space. We therefore adjust the space
+ * of the isl_pw_aff before returning it.
+ */
+static __isl_give isl_pw_aff *read_tuple_var_def(__isl_keep isl_stream *s,
+ struct vars *v, int rational)
+{
+ isl_space *space;
+ isl_pw_aff *def;
+
+ space = isl_space_wrap(isl_space_alloc(s->ctx, 0, v->n, 0));
+
+ def = accept_piecewise_affine(s, space, v, rational);
+ def = isl_pw_aff_domain_factor_domain(def);
+
+ return def;
+}
+
+/* Read a list of tuple elements by calling "read_el" on each of them and
+ * return a space with the same number of set dimensions derived from
+ * the parameter space "space" and possibly updated by "read_el".
+ * The elements in the list are separated by either "," or "][".
+ * If "comma" is set then only "," is allowed.
+ */
+static __isl_give isl_space *read_tuple_list(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_space *space, int rational, int comma,
+ __isl_give isl_space *(*read_el)(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_space *space, int rational,
+ void *user),
+ void *user)
+{
+ if (!space)
+ return NULL;
+
+ space = isl_space_set_from_params(space);
+
+ if (isl_stream_next_token_is(s, ']'))
+ return space;
+
+ for (;;) {
+ struct isl_token *tok;
+
+ space = isl_space_add_dims(space, isl_dim_set, 1);
+
+ space = read_el(s, v, space, rational, user);
+ if (!space)
+ return NULL;
+
+ tok = isl_stream_next_token(s);
+ if (!comma && tok && tok->type == ']' &&
+ isl_stream_next_token_is(s, '[')) {
+ isl_token_free(tok);
+ tok = isl_stream_next_token(s);
+ } else if (!tok || tok->type != ',') {
+ if (tok)
+ isl_stream_push_token(s, tok);
+ break;
+ }
+
+ isl_token_free(tok);
+ }
+
+ return space;
+}
+
+/* Read a tuple space from "s" derived from the parameter space "space".
+ * Call "read_el" on each element in the tuples.
+ */
+static __isl_give isl_space *read_tuple_space(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_space *space, int rational, int comma,
+ __isl_give isl_space *(*read_el)(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_space *space, int rational,
+ void *user),
+ void *user)
+{
+ struct isl_token *tok;
+ char *name = NULL;
+ isl_space *res = NULL;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ goto error;
+ if (tok->type == ISL_TOKEN_IDENT || tok->is_keyword) {
+ name = strdup(tok->u.s);
+ isl_token_free(tok);
+ if (!name)
+ goto error;
+ } else
+ isl_stream_push_token(s, tok);
+ if (isl_stream_eat(s, '['))
+ goto error;
+ if (next_is_tuple(s)) {
+ isl_space *out;
+ res = read_tuple_space(s, v, isl_space_copy(space),
+ rational, comma, read_el, user);
+ if (isl_stream_eat(s, ISL_TOKEN_TO))
+ goto error;
+ out = read_tuple_space(s, v, isl_space_copy(space),
+ rational, comma, read_el, user);
+ res = isl_space_product(res, out);
+ } else
+ res = read_tuple_list(s, v, isl_space_copy(space),
+ rational, comma, read_el, user);
+ if (isl_stream_eat(s, ']'))
+ goto error;
+
+ if (name) {
+ res = isl_space_set_tuple_name(res, isl_dim_set, name);
+ free(name);
+ }
+
+ isl_space_free(space);
+ return res;
+error:
+ free(name);
+ isl_space_free(res);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Construct an isl_pw_aff defined on a space with v->n variables
+ * that is equal to the last of those variables.
+ */
+static __isl_give isl_pw_aff *identity_tuple_el(struct vars *v)
+{
+ isl_space *space;
+
+ space = isl_space_set_alloc(v->ctx, 0, v->n);
+ return identity_tuple_el_on_space(space, v);
+}
+
+/* This function is called for each element in a tuple inside read_tuple.
+ * Add a new variable to "v" and construct a corresponding isl_pw_aff defined
+ * over a space containing all variables in "v" defined so far.
+ * The isl_pw_aff expresses the new variable in terms of earlier variables
+ * if a definition is provided. Otherwise, it is represented as being
+ * equal to itself.
+ * Add the isl_pw_aff to *list.
+ * If the new variable was named, then adjust "space" accordingly and
+ * return the updated space.
+ */
+static __isl_give isl_space *read_tuple_pw_aff_el(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_space *space, int rational, void *user)
+{
+ isl_pw_aff_list **list = (isl_pw_aff_list **) user;
+ isl_pw_aff *pa;
+ struct isl_token *tok;
+ int new_name = 0;
+
+ tok = next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return isl_space_free(space);
+ }
+
+ if (tok->type == ISL_TOKEN_IDENT) {
+ int n = v->n;
+ int p = vars_pos(v, tok->u.s, -1);
+ if (p < 0)
+ goto error;
+ new_name = p >= n;
+ }
+
+ if (tok->type == '*') {
+ if (vars_add_anon(v) < 0)
+ goto error;
+ isl_token_free(tok);
+ pa = identity_tuple_el(v);
+ } else if (new_name) {
+ isl_size pos = isl_space_dim(space, isl_dim_out);
+ if (pos < 0)
+ goto error;
+ pos -= 1;
+ space = space_set_dim_name(space, pos, v->v->name);
+ isl_token_free(tok);
+ if (isl_stream_eat_if_available(s, '='))
+ pa = read_tuple_var_def(s, v, rational);
+ else
+ pa = identity_tuple_el(v);
+ } else {
+ isl_stream_push_token(s, tok);
+ tok = NULL;
+ if (vars_add_anon(v) < 0)
+ goto error;
+ pa = read_tuple_var_def(s, v, rational);
+ }
+
+ *list = isl_pw_aff_list_add(*list, pa);
+ if (!*list)
+ return isl_space_free(space);
+
+ return space;
+error:
+ isl_token_free(tok);
+ return isl_space_free(space);
+}
+
+/* Read a tuple and represent it as an isl_multi_pw_aff.
+ * The range space of the isl_multi_pw_aff is the space of the tuple.
+ * The domain space is an anonymous space
+ * with a dimension for each variable in the set of variables in "v",
+ * including the variables in the range.
+ * If a given dimension is not defined in terms of earlier dimensions in
+ * the input, then the corresponding isl_pw_aff is set equal to one time
+ * the variable corresponding to the dimension being defined.
+ *
+ * The elements in the tuple are collected in a list by read_tuple_pw_aff_el.
+ * Each element in this list is defined over a space representing
+ * the variables defined so far. We need to adjust the earlier
+ * elements to have as many variables in the domain as the final
+ * element in the list.
+ */
+static __isl_give isl_multi_pw_aff *read_tuple(__isl_keep isl_stream *s,
+ struct vars *v, int rational, int comma)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_pw_aff_list *list;
+
+ space = isl_space_params_alloc(v->ctx, 0);
+ list = isl_pw_aff_list_alloc(s->ctx, 0);
+ space = read_tuple_space(s, v, space, rational, comma,
+ &read_tuple_pw_aff_el, &list);
+ n = isl_space_dim(space, isl_dim_set);
+ if (n < 0)
+ space = isl_space_free(space);
+ for (i = 0; i + 1 < n; ++i) {
+ isl_pw_aff *pa;
+
+ pa = isl_pw_aff_list_get_pw_aff(list, i);
+ pa = isl_pw_aff_add_dims(pa, isl_dim_in, n - (i + 1));
+ list = isl_pw_aff_list_set_pw_aff(list, i, pa);
+ }
+
+ space = isl_space_from_range(space);
+ space = isl_space_add_dims(space, isl_dim_in, v->n);
+ return isl_multi_pw_aff_from_pw_aff_list(space, list);
+}
+
+/* Add the tuple represented by the isl_multi_pw_aff "tuple" to "map".
+ * We first create the appropriate space in "map" based on the range
+ * space of this isl_multi_pw_aff. Then, we add equalities based
+ * on the affine expressions. These live in an anonymous space,
+ * however, so we first need to reset the space to that of "map".
+ */
+static __isl_give isl_map *map_from_tuple(__isl_take isl_multi_pw_aff *tuple,
+ __isl_take isl_map *map, enum isl_dim_type type, struct vars *v,
+ int rational)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_space *space = NULL;
+
+ n = isl_multi_pw_aff_dim(tuple, isl_dim_out);
+ if (!map || n < 0)
+ goto error;
+ ctx = isl_multi_pw_aff_get_ctx(tuple);
+ space = isl_space_range(isl_multi_pw_aff_get_space(tuple));
+ if (!space)
+ goto error;
+
+ if (type == isl_dim_param) {
+ if (isl_space_has_tuple_name(space, isl_dim_set) ||
+ isl_space_is_wrapping(space)) {
+ isl_die(ctx, isl_error_invalid,
+ "parameter tuples cannot be named or nested",
+ goto error);
+ }
+ map = isl_map_add_dims(map, type, n);
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+ if (!isl_space_has_dim_name(space, isl_dim_set, i))
+ isl_die(ctx, isl_error_invalid,
+ "parameters must be named",
+ goto error);
+ id = isl_space_get_dim_id(space, isl_dim_set, i);
+ map = isl_map_set_dim_id(map, isl_dim_param, i, id);
+ }
+ } else if (type == isl_dim_in) {
+ isl_set *set;
+
+ set = isl_set_universe(isl_space_copy(space));
+ if (rational)
+ set = isl_set_set_rational(set);
+ set = isl_set_intersect_params(set, isl_map_params(map));
+ map = isl_map_from_domain(set);
+ } else {
+ isl_set *set;
+
+ set = isl_set_universe(isl_space_copy(space));
+ if (rational)
+ set = isl_set_set_rational(set);
+ map = isl_map_from_domain_and_range(isl_map_domain(map), set);
+ }
+
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+ isl_space *space;
+ isl_aff *aff;
+ isl_set *set;
+ isl_map *map_i;
+
+ pa = isl_multi_pw_aff_get_pw_aff(tuple, i);
+ space = isl_pw_aff_get_domain_space(pa);
+ aff = isl_aff_zero_on_domain(isl_local_space_from_space(space));
+ aff = isl_aff_add_coefficient_si(aff,
+ isl_dim_in, v->n - n + i, -1);
+ pa = isl_pw_aff_add(pa, isl_pw_aff_from_aff(aff));
+ if (rational)
+ pa = isl_pw_aff_set_rational(pa);
+ set = isl_pw_aff_zero_set(pa);
+ map_i = isl_map_from_range(set);
+ map_i = isl_map_reset_space(map_i, isl_map_get_space(map));
+ map = isl_map_intersect(map, map_i);
+ }
+
+ isl_space_free(space);
+ isl_multi_pw_aff_free(tuple);
+ return map;
+error:
+ isl_space_free(space);
+ isl_multi_pw_aff_free(tuple);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Read a tuple from "s" and add it to "map".
+ * The tuple is initially represented as an isl_multi_pw_aff and
+ * then added to "map".
+ */
+static __isl_give isl_map *read_map_tuple(__isl_keep isl_stream *s,
+ __isl_take isl_map *map, enum isl_dim_type type, struct vars *v,
+ int rational, int comma)
+{
+ isl_multi_pw_aff *tuple;
+
+ tuple = read_tuple(s, v, rational, comma);
+ if (!tuple)
+ return isl_map_free(map);
+
+ return map_from_tuple(tuple, map, type, v, rational);
+}
+
+/* Given two equal-length lists of piecewise affine expression with the space
+ * of "set" as domain, construct a set in the same space that expresses
+ * that "left" and "right" satisfy the comparison "type".
+ *
+ * A space is constructed of the same dimension as the number of elements
+ * in the two lists. The comparison is then expressed in a map from
+ * this space to itself and wrapped into a set. Finally the two lists
+ * of piecewise affine expressions are plugged into this set.
+ *
+ * Let S be the space of "set" and T the constructed space.
+ * The lists are first changed into two isl_multi_pw_affs in S -> T and
+ * then combined into an isl_multi_pw_aff in S -> [T -> T],
+ * while the comparison is first expressed in T -> T, then [T -> T]
+ * and finally in S.
+ */
+static __isl_give isl_set *list_cmp(__isl_keep isl_set *set, int type,
+ __isl_take isl_pw_aff_list *left, __isl_take isl_pw_aff_list *right)
+{
+ isl_space *space;
+ isl_size n;
+ isl_multi_pw_aff *mpa1, *mpa2;
+
+ n = isl_pw_aff_list_n_pw_aff(left);
+ if (!set || n < 0 || !right)
+ goto error;
+
+ space = isl_set_get_space(set);
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, n);
+ mpa1 = isl_multi_pw_aff_from_pw_aff_list(isl_space_copy(space), left);
+ mpa2 = isl_multi_pw_aff_from_pw_aff_list(isl_space_copy(space), right);
+ mpa1 = isl_multi_pw_aff_range_product(mpa1, mpa2);
+
+ space = isl_space_range(space);
+ switch (type) {
+ case ISL_TOKEN_LEX_LT:
+ set = isl_map_wrap(isl_map_lex_lt(space));
+ break;
+ case ISL_TOKEN_LEX_GT:
+ set = isl_map_wrap(isl_map_lex_gt(space));
+ break;
+ case ISL_TOKEN_LEX_LE:
+ set = isl_map_wrap(isl_map_lex_le(space));
+ break;
+ case ISL_TOKEN_LEX_GE:
+ set = isl_map_wrap(isl_map_lex_ge(space));
+ break;
+ default:
+ isl_multi_pw_aff_free(mpa1);
+ isl_space_free(space);
+ isl_die(isl_set_get_ctx(set), isl_error_internal,
+ "unhandled list comparison type", return NULL);
+ }
+ set = isl_set_preimage_multi_pw_aff(set, mpa1);
+ return set;
+error:
+ isl_pw_aff_list_free(left);
+ isl_pw_aff_list_free(right);
+ return NULL;
+}
+
+/* Construct constraints of the form
+ *
+ * a op b
+ *
+ * where a is an element in "left", op is an operator of type "type" and
+ * b is an element in "right", add the constraints to "set" and return
+ * the result.
+ * "rational" is set if the constraints should be treated as
+ * a rational constraints.
+ *
+ * If "type" is the type of a comparison operator between lists
+ * of affine expressions, then a single (compound) constraint
+ * is constructed by list_cmp instead.
+ */
+static __isl_give isl_set *construct_constraints(
+ __isl_take isl_set *set, int type,
+ __isl_keep isl_pw_aff_list *left, __isl_keep isl_pw_aff_list *right,
+ int rational)
+{
+ isl_set *cond;
+
+ left = isl_pw_aff_list_copy(left);
+ right = isl_pw_aff_list_copy(right);
+ if (rational) {
+ left = isl_pw_aff_list_set_rational(left);
+ right = isl_pw_aff_list_set_rational(right);
+ }
+ if (is_list_comparator_type(type))
+ cond = list_cmp(set, type, left, right);
+ else if (type == ISL_TOKEN_LE)
+ cond = isl_pw_aff_list_le_set(left, right);
+ else if (type == ISL_TOKEN_GE)
+ cond = isl_pw_aff_list_ge_set(left, right);
+ else if (type == ISL_TOKEN_LT)
+ cond = isl_pw_aff_list_lt_set(left, right);
+ else if (type == ISL_TOKEN_GT)
+ cond = isl_pw_aff_list_gt_set(left, right);
+ else if (type == ISL_TOKEN_NE)
+ cond = isl_pw_aff_list_ne_set(left, right);
+ else
+ cond = isl_pw_aff_list_eq_set(left, right);
+
+ return isl_set_intersect(set, cond);
+}
+
+/* Read a constraint from "s", add it to "map" and return the result.
+ * "v" contains a description of the identifiers parsed so far.
+ * "rational" is set if the constraint should be treated as
+ * a rational constraint.
+ * The constraint read from "s" may be applied to multiple pairs
+ * of affine expressions and may be chained.
+ * In particular, a list of affine expressions is read, followed
+ * by a comparison operator and another list of affine expressions.
+ * The comparison operator is then applied to each pair of elements
+ * in the two lists and the results are added to "map".
+ * However, if the operator expects two lists of affine expressions,
+ * then it is applied directly to those lists and the two lists
+ * are required to have the same length.
+ * If the next token is another comparison operator, then another
+ * list of affine expressions is read and the process repeats.
+ *
+ * The processing is performed on a wrapped copy of "map" because
+ * an affine expression cannot have a binary relation as domain.
+ */
+static __isl_give isl_map *add_constraint(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_map *map, int rational)
+{
+ struct isl_token *tok;
+ int type;
+ isl_pw_aff_list *list1 = NULL, *list2 = NULL;
+ isl_size n1, n2;
+ isl_set *set;
+
+ set = isl_map_wrap(map);
+ list1 = accept_affine_list(s, isl_set_get_space(set), v);
+ if (!list1)
+ goto error;
+ tok = isl_stream_next_token(s);
+ if (!is_comparator(tok)) {
+ isl_stream_error(s, tok, "missing operator");
+ if (tok)
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ type = tok->type;
+ isl_token_free(tok);
+ for (;;) {
+ list2 = accept_affine_list(s, isl_set_get_space(set), v);
+ n1 = isl_pw_aff_list_n_pw_aff(list1);
+ n2 = isl_pw_aff_list_n_pw_aff(list2);
+ if (n1 < 0 || n2 < 0)
+ goto error;
+ if (is_list_comparator_type(type) && n1 != n2) {
+ isl_stream_error(s, NULL,
+ "list arguments not of same size");
+ goto error;
+ }
+
+ set = construct_constraints(set, type, list1, list2, rational);
+ isl_pw_aff_list_free(list1);
+ list1 = list2;
+
+ if (!next_is_comparator(s))
+ break;
+ tok = isl_stream_next_token(s);
+ type = tok->type;
+ isl_token_free(tok);
+ }
+ isl_pw_aff_list_free(list1);
+
+ return isl_set_unwrap(set);
+error:
+ isl_pw_aff_list_free(list1);
+ isl_pw_aff_list_free(list2);
+ isl_set_free(set);
+ return NULL;
+}
+
+static __isl_give isl_map *read_exists(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_map *map, int rational)
+{
+ int n = v->n;
+ int seen_paren = isl_stream_eat_if_available(s, '(');
+
+ map = isl_map_from_domain(isl_map_wrap(map));
+ map = read_defined_var_list(s, v, map, rational);
+
+ if (isl_stream_eat(s, ':'))
+ goto error;
+
+ map = read_formula(s, v, map, rational);
+ map = isl_set_unwrap(isl_map_domain(map));
+
+ vars_drop(v, v->n - n);
+ if (seen_paren && isl_stream_eat(s, ')'))
+ goto error;
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Parse an expression between parentheses and push the result
+ * back on the stream.
+ *
+ * The parsed expression may be either an affine expression
+ * or a condition. The first type is pushed onto the stream
+ * as an isl_pw_aff, while the second is pushed as an isl_map.
+ *
+ * If the initial token indicates the start of a condition,
+ * we parse it as such.
+ * Otherwise, we first parse an affine expression and push
+ * that onto the stream. If the affine expression covers the
+ * entire expression between parentheses, we return.
+ * Otherwise, we assume that the affine expression is the
+ * start of a condition and continue parsing.
+ */
+static int resolve_paren_expr(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_map *map, int rational)
+{
+ struct isl_token *tok, *tok2;
+ int has_paren;
+ int line, col;
+ isl_pw_aff *pwaff;
+
+ tok = isl_stream_next_token(s);
+ if (!tok || tok->type != '(')
+ goto error;
+
+ if (isl_stream_next_token_is(s, '('))
+ if (resolve_paren_expr(s, v, isl_map_copy(map), rational))
+ goto error;
+
+ if (next_is_condition_start(s)) {
+ map = read_formula(s, v, map, rational);
+ if (isl_stream_eat(s, ')'))
+ goto error;
+ tok->type = ISL_TOKEN_MAP;
+ tok->u.map = map;
+ isl_stream_push_token(s, tok);
+ return 0;
+ }
+
+ tok2 = isl_stream_next_token(s);
+ if (!tok2)
+ goto error;
+ line = tok2->line;
+ col = tok2->col;
+ isl_stream_push_token(s, tok2);
+
+ pwaff = accept_affine(s, isl_space_wrap(isl_map_get_space(map)), v);
+ if (!pwaff)
+ goto error;
+
+ has_paren = isl_stream_eat_if_available(s, ')');
+
+ if (push_aff(s, line, col, pwaff) < 0)
+ goto error;
+
+ if (has_paren) {
+ isl_token_free(tok);
+ isl_map_free(map);
+ return 0;
+ }
+
+ map = read_formula(s, v, map, rational);
+ if (isl_stream_eat(s, ')'))
+ goto error;
+
+ tok->type = ISL_TOKEN_MAP;
+ tok->u.map = map;
+ isl_stream_push_token(s, tok);
+
+ return 0;
+error:
+ isl_token_free(tok);
+ isl_map_free(map);
+ return -1;
+}
+
+static __isl_give isl_map *read_conjunct(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_map *map, int rational)
+{
+ if (isl_stream_next_token_is(s, '('))
+ if (resolve_paren_expr(s, v, isl_map_copy(map), rational))
+ goto error;
+
+ if (isl_stream_next_token_is(s, ISL_TOKEN_MAP)) {
+ struct isl_token *tok;
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ goto error;
+ isl_map_free(map);
+ map = isl_map_copy(tok->u.map);
+ isl_token_free(tok);
+ return map;
+ }
+
+ if (isl_stream_eat_if_available(s, ISL_TOKEN_EXISTS))
+ return read_exists(s, v, map, rational);
+
+ if (isl_stream_eat_if_available(s, ISL_TOKEN_TRUE))
+ return map;
+
+ if (isl_stream_eat_if_available(s, ISL_TOKEN_FALSE)) {
+ isl_space *space = isl_map_get_space(map);
+ isl_map_free(map);
+ return isl_map_empty(space);
+ }
+
+ return add_constraint(s, v, map, rational);
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+static __isl_give isl_map *read_conjuncts(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_map *map, int rational)
+{
+ isl_map *res;
+ int negate;
+
+ negate = isl_stream_eat_if_available(s, ISL_TOKEN_NOT);
+ res = read_conjunct(s, v, isl_map_copy(map), rational);
+ if (negate)
+ res = isl_map_subtract(isl_map_copy(map), res);
+
+ while (res && isl_stream_eat_if_available(s, ISL_TOKEN_AND)) {
+ isl_map *res_i;
+
+ negate = isl_stream_eat_if_available(s, ISL_TOKEN_NOT);
+ res_i = read_conjunct(s, v, isl_map_copy(map), rational);
+ if (negate)
+ res = isl_map_subtract(res, res_i);
+ else
+ res = isl_map_intersect(res, res_i);
+ }
+
+ isl_map_free(map);
+ return res;
+}
+
+static __isl_give isl_map *read_disjuncts(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_map *map, int rational)
+{
+ isl_map *res;
+
+ if (isl_stream_next_token_is(s, '}'))
+ return map;
+
+ res = read_conjuncts(s, v, isl_map_copy(map), rational);
+ while (isl_stream_eat_if_available(s, ISL_TOKEN_OR)) {
+ isl_map *res_i;
+
+ res_i = read_conjuncts(s, v, isl_map_copy(map), rational);
+ res = isl_map_union(res, res_i);
+ }
+
+ isl_map_free(map);
+ return res;
+}
+
+/* Read a first order formula from "s", add the corresponding
+ * constraints to "map" and return the result.
+ *
+ * In particular, read a formula of the form
+ *
+ * a
+ *
+ * or
+ *
+ * a implies b
+ *
+ * where a and b are disjunctions.
+ *
+ * In the first case, map is replaced by
+ *
+ * map \cap { [..] : a }
+ *
+ * In the second case, it is replaced by
+ *
+ * (map \setminus { [..] : a}) \cup (map \cap { [..] : b })
+ */
+static __isl_give isl_map *read_formula(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_map *map, int rational)
+{
+ isl_map *res;
+
+ res = read_disjuncts(s, v, isl_map_copy(map), rational);
+
+ if (isl_stream_eat_if_available(s, ISL_TOKEN_IMPLIES)) {
+ isl_map *res2;
+
+ res = isl_map_subtract(isl_map_copy(map), res);
+ res2 = read_disjuncts(s, v, map, rational);
+ res = isl_map_union(res, res2);
+ } else
+ isl_map_free(map);
+
+ return res;
+}
+
+static isl_size polylib_pos_to_isl_pos(__isl_keep isl_basic_map *bmap, int pos)
+{
+ isl_size n_out, n_in, n_param, n_div;
+
+ n_param = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_param < 0 || n_in < 0 || n_out < 0 || n_div < 0)
+ return isl_size_error;
+
+ if (pos < n_out)
+ return 1 + n_param + n_in + pos;
+ pos -= n_out;
+
+ if (pos < n_in)
+ return 1 + n_param + pos;
+ pos -= n_in;
+
+ if (pos < n_div)
+ return 1 + n_param + n_in + n_out + pos;
+ pos -= n_div;
+
+ if (pos < n_param)
+ return 1 + pos;
+
+ return 0;
+}
+
+static __isl_give isl_basic_map *basic_map_read_polylib_constraint(
+ __isl_keep isl_stream *s, __isl_take isl_basic_map *bmap)
+{
+ int j;
+ struct isl_token *tok;
+ int type;
+ int k;
+ isl_int *c;
+ isl_size total;
+
+ if (!bmap)
+ return NULL;
+
+ tok = isl_stream_next_token(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok, "expecting coefficient");
+ if (tok)
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ if (!tok->on_new_line) {
+ isl_stream_error(s, tok, "coefficient should appear on new line");
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+
+ type = isl_int_get_si(tok->u.v);
+ isl_token_free(tok);
+
+ isl_assert(s->ctx, type == 0 || type == 1, goto error);
+ if (type == 0) {
+ k = isl_basic_map_alloc_equality(bmap);
+ c = bmap->eq[k];
+ } else {
+ k = isl_basic_map_alloc_inequality(bmap);
+ c = bmap->ineq[k];
+ }
+ if (k < 0)
+ goto error;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ for (j = 0; j < 1 + total; ++j) {
+ isl_size pos;
+ tok = isl_stream_next_token(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok, "expecting coefficient");
+ if (tok)
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ if (tok->on_new_line) {
+ isl_stream_error(s, tok,
+ "coefficient should not appear on new line");
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ pos = polylib_pos_to_isl_pos(bmap, j);
+ if (pos >= 0)
+ isl_int_set(c[pos], tok->u.v);
+ isl_token_free(tok);
+ if (pos < 0)
+ return isl_basic_map_free(bmap);
+ }
+
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+static __isl_give isl_basic_map *basic_map_read_polylib(
+ __isl_keep isl_stream *s)
+{
+ int i;
+ struct isl_token *tok;
+ struct isl_token *tok2;
+ int n_row, n_col;
+ int on_new_line;
+ unsigned in = 0, out, local = 0;
+ struct isl_basic_map *bmap = NULL;
+ int nparam = 0;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return NULL;
+ }
+ tok2 = isl_stream_next_token(s);
+ if (!tok2) {
+ isl_token_free(tok);
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return NULL;
+ }
+ if (tok->type != ISL_TOKEN_VALUE || tok2->type != ISL_TOKEN_VALUE) {
+ isl_stream_push_token(s, tok2);
+ isl_stream_push_token(s, tok);
+ isl_stream_error(s, NULL,
+ "expecting constraint matrix dimensions");
+ return NULL;
+ }
+ n_row = isl_int_get_si(tok->u.v);
+ n_col = isl_int_get_si(tok2->u.v);
+ on_new_line = tok2->on_new_line;
+ isl_token_free(tok2);
+ isl_token_free(tok);
+ isl_assert(s->ctx, !on_new_line, return NULL);
+ isl_assert(s->ctx, n_row >= 0, return NULL);
+ isl_assert(s->ctx, n_col >= 2 + nparam, return NULL);
+ tok = isl_stream_next_token_on_same_line(s);
+ if (tok) {
+ if (tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok,
+ "expecting number of output dimensions");
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ out = isl_int_get_si(tok->u.v);
+ isl_token_free(tok);
+
+ tok = isl_stream_next_token_on_same_line(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok,
+ "expecting number of input dimensions");
+ if (tok)
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ in = isl_int_get_si(tok->u.v);
+ isl_token_free(tok);
+
+ tok = isl_stream_next_token_on_same_line(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok,
+ "expecting number of existentials");
+ if (tok)
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ local = isl_int_get_si(tok->u.v);
+ isl_token_free(tok);
+
+ tok = isl_stream_next_token_on_same_line(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok,
+ "expecting number of parameters");
+ if (tok)
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ nparam = isl_int_get_si(tok->u.v);
+ isl_token_free(tok);
+ if (n_col != 1 + out + in + local + nparam + 1) {
+ isl_stream_error(s, NULL,
+ "dimensions don't match");
+ goto error;
+ }
+ } else
+ out = n_col - 2 - nparam;
+ bmap = isl_basic_map_alloc(s->ctx, nparam, in, out, local, n_row, n_row);
+ if (!bmap)
+ return NULL;
+
+ for (i = 0; i < local; ++i) {
+ int k = isl_basic_map_alloc_div(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bmap->div[k], 1 + 1 + nparam + in + out + local);
+ }
+
+ for (i = 0; i < n_row; ++i)
+ bmap = basic_map_read_polylib_constraint(s, bmap);
+
+ tok = isl_stream_next_token_on_same_line(s);
+ if (tok) {
+ isl_stream_error(s, tok, "unexpected extra token on line");
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+
+ bmap = isl_basic_map_simplify(bmap);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+static __isl_give isl_map *map_read_polylib(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ struct isl_token *tok2;
+ int i, n;
+ struct isl_map *map;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return NULL;
+ }
+ tok2 = isl_stream_next_token_on_same_line(s);
+ if (tok2 && tok2->type == ISL_TOKEN_VALUE) {
+ isl_stream_push_token(s, tok2);
+ isl_stream_push_token(s, tok);
+ return isl_map_from_basic_map(basic_map_read_polylib(s));
+ }
+ if (tok2) {
+ isl_stream_error(s, tok2, "unexpected token");
+ isl_stream_push_token(s, tok2);
+ isl_stream_push_token(s, tok);
+ return NULL;
+ }
+ n = isl_int_get_si(tok->u.v);
+ isl_token_free(tok);
+
+ isl_assert(s->ctx, n >= 1, return NULL);
+
+ map = isl_map_from_basic_map(basic_map_read_polylib(s));
+
+ for (i = 1; map && i < n; ++i)
+ map = isl_map_union(map,
+ isl_map_from_basic_map(basic_map_read_polylib(s)));
+
+ return map;
+}
+
+static int optional_power(__isl_keep isl_stream *s)
+{
+ int pow;
+ struct isl_token *tok;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 1;
+ if (tok->type != '^') {
+ isl_stream_push_token(s, tok);
+ return 1;
+ }
+ isl_token_free(tok);
+ tok = isl_stream_next_token(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok, "expecting exponent");
+ if (tok)
+ isl_stream_push_token(s, tok);
+ return 1;
+ }
+ pow = isl_int_get_si(tok->u.v);
+ isl_token_free(tok);
+ return pow;
+}
+
+static __isl_give isl_pw_qpolynomial *read_term(__isl_keep isl_stream *s,
+ __isl_keep isl_map *map, struct vars *v);
+
+static __isl_give isl_pw_qpolynomial *read_factor(__isl_keep isl_stream *s,
+ __isl_keep isl_map *map, struct vars *v)
+{
+ isl_pw_qpolynomial *pwqp;
+ struct isl_token *tok;
+
+ tok = next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return NULL;
+ }
+ if (tok->type == '(') {
+ int pow;
+
+ isl_token_free(tok);
+ pwqp = read_term(s, map, v);
+ if (!pwqp)
+ return NULL;
+ if (isl_stream_eat(s, ')'))
+ goto error;
+ pow = optional_power(s);
+ pwqp = isl_pw_qpolynomial_pow(pwqp, pow);
+ } else if (tok->type == ISL_TOKEN_VALUE) {
+ struct isl_token *tok2;
+ isl_qpolynomial *qp;
+
+ tok2 = isl_stream_next_token(s);
+ if (tok2 && tok2->type == '/') {
+ isl_token_free(tok2);
+ tok2 = next_token(s);
+ if (!tok2 || tok2->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok2, "expected denominator");
+ isl_token_free(tok);
+ isl_token_free(tok2);
+ return NULL;
+ }
+ qp = isl_qpolynomial_rat_cst_on_domain(isl_map_get_space(map),
+ tok->u.v, tok2->u.v);
+ isl_token_free(tok2);
+ } else {
+ isl_stream_push_token(s, tok2);
+ qp = isl_qpolynomial_cst_on_domain(isl_map_get_space(map),
+ tok->u.v);
+ }
+ isl_token_free(tok);
+ pwqp = isl_pw_qpolynomial_from_qpolynomial(qp);
+ } else if (tok->type == ISL_TOKEN_INFTY) {
+ isl_qpolynomial *qp;
+ isl_token_free(tok);
+ qp = isl_qpolynomial_infty_on_domain(isl_map_get_space(map));
+ pwqp = isl_pw_qpolynomial_from_qpolynomial(qp);
+ } else if (tok->type == ISL_TOKEN_NAN) {
+ isl_qpolynomial *qp;
+ isl_token_free(tok);
+ qp = isl_qpolynomial_nan_on_domain(isl_map_get_space(map));
+ pwqp = isl_pw_qpolynomial_from_qpolynomial(qp);
+ } else if (tok->type == ISL_TOKEN_IDENT) {
+ int n = v->n;
+ int pos = vars_pos(v, tok->u.s, -1);
+ int pow;
+ isl_qpolynomial *qp;
+ if (pos < 0) {
+ isl_token_free(tok);
+ return NULL;
+ }
+ if (pos >= n) {
+ vars_drop(v, v->n - n);
+ isl_stream_error(s, tok, "unknown identifier");
+ isl_token_free(tok);
+ return NULL;
+ }
+ isl_token_free(tok);
+ pow = optional_power(s);
+ qp = isl_qpolynomial_var_pow_on_domain(isl_map_get_space(map), pos, pow);
+ pwqp = isl_pw_qpolynomial_from_qpolynomial(qp);
+ } else if (is_start_of_div(tok)) {
+ isl_pw_aff *pwaff;
+ int pow;
+
+ isl_stream_push_token(s, tok);
+ pwaff = accept_div(s, isl_map_get_space(map), v);
+ pow = optional_power(s);
+ pwqp = isl_pw_qpolynomial_from_pw_aff(pwaff);
+ pwqp = isl_pw_qpolynomial_pow(pwqp, pow);
+ } else if (tok->type == '-') {
+ isl_token_free(tok);
+ pwqp = read_factor(s, map, v);
+ pwqp = isl_pw_qpolynomial_neg(pwqp);
+ } else {
+ isl_stream_error(s, tok, "unexpected isl_token");
+ isl_stream_push_token(s, tok);
+ return NULL;
+ }
+
+ if (isl_stream_eat_if_available(s, '*') ||
+ isl_stream_next_token_is(s, ISL_TOKEN_IDENT)) {
+ isl_pw_qpolynomial *pwqp2;
+
+ pwqp2 = read_factor(s, map, v);
+ pwqp = isl_pw_qpolynomial_mul(pwqp, pwqp2);
+ }
+
+ return pwqp;
+error:
+ isl_pw_qpolynomial_free(pwqp);
+ return NULL;
+}
+
+static __isl_give isl_pw_qpolynomial *read_term(__isl_keep isl_stream *s,
+ __isl_keep isl_map *map, struct vars *v)
+{
+ struct isl_token *tok;
+ isl_pw_qpolynomial *pwqp;
+
+ pwqp = read_factor(s, map, v);
+
+ for (;;) {
+ tok = next_token(s);
+ if (!tok)
+ return pwqp;
+
+ if (tok->type == '+') {
+ isl_pw_qpolynomial *pwqp2;
+
+ isl_token_free(tok);
+ pwqp2 = read_factor(s, map, v);
+ pwqp = isl_pw_qpolynomial_add(pwqp, pwqp2);
+ } else if (tok->type == '-') {
+ isl_pw_qpolynomial *pwqp2;
+
+ isl_token_free(tok);
+ pwqp2 = read_factor(s, map, v);
+ pwqp = isl_pw_qpolynomial_sub(pwqp, pwqp2);
+ } else if (tok->type == ISL_TOKEN_VALUE &&
+ isl_int_is_neg(tok->u.v)) {
+ isl_pw_qpolynomial *pwqp2;
+
+ isl_stream_push_token(s, tok);
+ pwqp2 = read_factor(s, map, v);
+ pwqp = isl_pw_qpolynomial_add(pwqp, pwqp2);
+ } else {
+ isl_stream_push_token(s, tok);
+ break;
+ }
+ }
+
+ return pwqp;
+}
+
+static __isl_give isl_map *read_optional_formula(__isl_keep isl_stream *s,
+ __isl_take isl_map *map, struct vars *v, int rational)
+{
+ struct isl_token *tok;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ goto error;
+ }
+ if (tok->type == ':' ||
+ (tok->type == ISL_TOKEN_OR && !strcmp(tok->u.s, "|"))) {
+ isl_token_free(tok);
+ map = read_formula(s, v, map, rational);
+ } else
+ isl_stream_push_token(s, tok);
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+static struct isl_obj obj_read_poly(__isl_keep isl_stream *s,
+ __isl_take isl_map *map, struct vars *v, int n)
+{
+ struct isl_obj obj = { isl_obj_pw_qpolynomial, NULL };
+ isl_pw_qpolynomial *pwqp;
+ struct isl_set *set;
+
+ pwqp = read_term(s, map, v);
+ map = read_optional_formula(s, map, v, 0);
+ set = isl_map_range(map);
+
+ pwqp = isl_pw_qpolynomial_intersect_domain(pwqp, set);
+
+ vars_drop(v, v->n - n);
+
+ obj.v = pwqp;
+ return obj;
+}
+
+static struct isl_obj obj_read_poly_or_fold(__isl_keep isl_stream *s,
+ __isl_take isl_set *set, struct vars *v, int n)
+{
+ int min, max;
+ struct isl_obj obj = { isl_obj_pw_qpolynomial_fold, NULL };
+ isl_pw_qpolynomial *pwqp;
+ isl_pw_qpolynomial_fold *pwf = NULL;
+ enum isl_fold fold;
+
+ max = isl_stream_eat_if_available(s, ISL_TOKEN_MAX);
+ min = !max && isl_stream_eat_if_available(s, ISL_TOKEN_MIN);
+ if (!min && !max)
+ return obj_read_poly(s, set, v, n);
+ fold = max ? isl_fold_max : isl_fold_min;
+
+ if (isl_stream_eat(s, '('))
+ goto error;
+
+ pwqp = read_term(s, set, v);
+ pwf = isl_pw_qpolynomial_fold_from_pw_qpolynomial(fold, pwqp);
+
+ while (isl_stream_eat_if_available(s, ',')) {
+ isl_pw_qpolynomial_fold *pwf_i;
+ pwqp = read_term(s, set, v);
+ pwf_i = isl_pw_qpolynomial_fold_from_pw_qpolynomial(fold, pwqp);
+ pwf = isl_pw_qpolynomial_fold_fold(pwf, pwf_i);
+ }
+
+ if (isl_stream_eat(s, ')'))
+ goto error;
+
+ set = read_optional_formula(s, set, v, 0);
+ pwf = isl_pw_qpolynomial_fold_intersect_domain(pwf, set);
+
+ vars_drop(v, v->n - n);
+
+ obj.v = pwf;
+ return obj;
+error:
+ isl_set_free(set);
+ isl_pw_qpolynomial_fold_free(pwf);
+ obj.type = isl_obj_none;
+ return obj;
+}
+
+static int is_rational(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+ if (tok->type == ISL_TOKEN_RAT && isl_stream_next_token_is(s, ':')) {
+ isl_token_free(tok);
+ isl_stream_eat(s, ':');
+ return 1;
+ }
+
+ isl_stream_push_token(s, tok);
+
+ return 0;
+}
+
+static struct isl_obj obj_read_body(__isl_keep isl_stream *s,
+ __isl_take isl_map *map, struct vars *v)
+{
+ struct isl_token *tok;
+ struct isl_obj obj = { isl_obj_set, NULL };
+ int n = v->n;
+ int rational;
+
+ rational = is_rational(s);
+ if (rational)
+ map = isl_map_set_rational(map);
+
+ if (isl_stream_next_token_is(s, ':')) {
+ obj.type = isl_obj_set;
+ obj.v = read_optional_formula(s, map, v, rational);
+ return obj;
+ }
+
+ if (!next_is_tuple(s))
+ return obj_read_poly_or_fold(s, map, v, n);
+
+ map = read_map_tuple(s, map, isl_dim_in, v, rational, 0);
+ if (!map)
+ goto error;
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ goto error;
+ if (tok->type == ISL_TOKEN_TO) {
+ obj.type = isl_obj_map;
+ isl_token_free(tok);
+ if (!next_is_tuple(s)) {
+ isl_set *set = isl_map_domain(map);
+ return obj_read_poly_or_fold(s, set, v, n);
+ }
+ map = read_map_tuple(s, map, isl_dim_out, v, rational, 0);
+ if (!map)
+ goto error;
+ } else {
+ map = isl_map_domain(map);
+ isl_stream_push_token(s, tok);
+ }
+
+ map = read_optional_formula(s, map, v, rational);
+
+ vars_drop(v, v->n - n);
+
+ obj.v = map;
+ return obj;
+error:
+ isl_map_free(map);
+ obj.type = isl_obj_none;
+ return obj;
+}
+
+static struct isl_obj to_union(isl_ctx *ctx, struct isl_obj obj)
+{
+ if (obj.type == isl_obj_map) {
+ obj.v = isl_union_map_from_map(obj.v);
+ obj.type = isl_obj_union_map;
+ } else if (obj.type == isl_obj_set) {
+ obj.v = isl_union_set_from_set(obj.v);
+ obj.type = isl_obj_union_set;
+ } else if (obj.type == isl_obj_pw_qpolynomial) {
+ obj.v = isl_union_pw_qpolynomial_from_pw_qpolynomial(obj.v);
+ obj.type = isl_obj_union_pw_qpolynomial;
+ } else if (obj.type == isl_obj_pw_qpolynomial_fold) {
+ obj.v = isl_union_pw_qpolynomial_fold_from_pw_qpolynomial_fold(obj.v);
+ obj.type = isl_obj_union_pw_qpolynomial_fold;
+ } else
+ isl_assert(ctx, 0, goto error);
+ return obj;
+error:
+ obj.type->free(obj.v);
+ obj.type = isl_obj_none;
+ return obj;
+}
+
+static struct isl_obj obj_add(__isl_keep isl_stream *s,
+ struct isl_obj obj1, struct isl_obj obj2)
+{
+ if (obj2.type == isl_obj_none || !obj2.v)
+ goto error;
+ if (obj1.type == isl_obj_set && obj2.type == isl_obj_union_set)
+ obj1 = to_union(s->ctx, obj1);
+ if (obj1.type == isl_obj_union_set && obj2.type == isl_obj_set)
+ obj2 = to_union(s->ctx, obj2);
+ if (obj1.type == isl_obj_map && obj2.type == isl_obj_union_map)
+ obj1 = to_union(s->ctx, obj1);
+ if (obj1.type == isl_obj_union_map && obj2.type == isl_obj_map)
+ obj2 = to_union(s->ctx, obj2);
+ if (obj1.type == isl_obj_pw_qpolynomial &&
+ obj2.type == isl_obj_union_pw_qpolynomial)
+ obj1 = to_union(s->ctx, obj1);
+ if (obj1.type == isl_obj_union_pw_qpolynomial &&
+ obj2.type == isl_obj_pw_qpolynomial)
+ obj2 = to_union(s->ctx, obj2);
+ if (obj1.type == isl_obj_pw_qpolynomial_fold &&
+ obj2.type == isl_obj_union_pw_qpolynomial_fold)
+ obj1 = to_union(s->ctx, obj1);
+ if (obj1.type == isl_obj_union_pw_qpolynomial_fold &&
+ obj2.type == isl_obj_pw_qpolynomial_fold)
+ obj2 = to_union(s->ctx, obj2);
+ if (obj1.type != obj2.type) {
+ isl_stream_error(s, NULL,
+ "attempt to combine incompatible objects");
+ goto error;
+ }
+ if (!obj1.type->add)
+ isl_die(s->ctx, isl_error_internal,
+ "combination not supported on object type", goto error);
+ if (obj1.type == isl_obj_map && !isl_map_has_equal_space(obj1.v, obj2.v)) {
+ obj1 = to_union(s->ctx, obj1);
+ obj2 = to_union(s->ctx, obj2);
+ }
+ if (obj1.type == isl_obj_set && !isl_set_has_equal_space(obj1.v, obj2.v)) {
+ obj1 = to_union(s->ctx, obj1);
+ obj2 = to_union(s->ctx, obj2);
+ }
+ if (obj1.type == isl_obj_pw_qpolynomial &&
+ !isl_pw_qpolynomial_has_equal_space(obj1.v, obj2.v)) {
+ obj1 = to_union(s->ctx, obj1);
+ obj2 = to_union(s->ctx, obj2);
+ }
+ if (obj1.type == isl_obj_pw_qpolynomial_fold &&
+ !isl_pw_qpolynomial_fold_has_equal_space(obj1.v, obj2.v)) {
+ obj1 = to_union(s->ctx, obj1);
+ obj2 = to_union(s->ctx, obj2);
+ }
+ obj1.v = obj1.type->add(obj1.v, obj2.v);
+ return obj1;
+error:
+ obj1.type->free(obj1.v);
+ obj2.type->free(obj2.v);
+ obj1.type = isl_obj_none;
+ obj1.v = NULL;
+ return obj1;
+}
+
+/* Are the first two tokens on "s", "domain" (either as a string
+ * or as an identifier) followed by ":"?
+ */
+static int next_is_domain_colon(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ char *name;
+ int res;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+ if (tok->type != ISL_TOKEN_IDENT && tok->type != ISL_TOKEN_STRING) {
+ isl_stream_push_token(s, tok);
+ return 0;
+ }
+
+ name = isl_token_get_str(s->ctx, tok);
+ res = !strcmp(name, "domain") && isl_stream_next_token_is(s, ':');
+ free(name);
+
+ isl_stream_push_token(s, tok);
+
+ return res;
+}
+
+/* Do the first tokens on "s" look like a schedule?
+ *
+ * The root of a schedule is always a domain node, so the first thing
+ * we expect in the stream is a domain key, i.e., "domain" followed
+ * by ":". If the schedule was printed in YAML flow style, then
+ * we additionally expect a "{" to open the outer mapping.
+ */
+static int next_is_schedule(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ int is_schedule;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+ if (tok->type != '{') {
+ isl_stream_push_token(s, tok);
+ return next_is_domain_colon(s);
+ }
+
+ is_schedule = next_is_domain_colon(s);
+ isl_stream_push_token(s, tok);
+
+ return is_schedule;
+}
+
+/* Read an isl_schedule from "s" and store it in an isl_obj.
+ */
+static struct isl_obj schedule_read(__isl_keep isl_stream *s)
+{
+ struct isl_obj obj;
+
+ obj.type = isl_obj_schedule;
+ obj.v = isl_stream_read_schedule(s);
+
+ return obj;
+}
+
+/* Read a disjunction of object bodies from "s".
+ * That is, read the inside of the braces, but not the braces themselves.
+ * "v" contains a description of the identifiers parsed so far.
+ * "map" contains information about the parameters.
+ */
+static struct isl_obj obj_read_disjuncts(__isl_keep isl_stream *s,
+ struct vars *v, __isl_keep isl_map *map)
+{
+ struct isl_obj obj = { isl_obj_set, NULL };
+
+ if (isl_stream_next_token_is(s, '}')) {
+ obj.type = isl_obj_union_set;
+ obj.v = isl_union_set_empty(isl_map_get_space(map));
+ return obj;
+ }
+
+ for (;;) {
+ struct isl_obj o;
+ o = obj_read_body(s, isl_map_copy(map), v);
+ if (!obj.v)
+ obj = o;
+ else
+ obj = obj_add(s, obj, o);
+ if (obj.type == isl_obj_none || !obj.v)
+ return obj;
+ if (!isl_stream_eat_if_available(s, ';'))
+ break;
+ if (isl_stream_next_token_is(s, '}'))
+ break;
+ }
+
+ return obj;
+}
+
+static struct isl_obj obj_read(__isl_keep isl_stream *s)
+{
+ isl_map *map = NULL;
+ struct isl_token *tok;
+ struct vars *v = NULL;
+ struct isl_obj obj = { isl_obj_set, NULL };
+
+ if (next_is_schedule(s))
+ return schedule_read(s);
+
+ tok = next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ goto error;
+ }
+ if (tok->type == ISL_TOKEN_VALUE) {
+ struct isl_token *tok2;
+ struct isl_map *map;
+
+ tok2 = isl_stream_next_token(s);
+ if (!tok2 || tok2->type != ISL_TOKEN_VALUE ||
+ isl_int_is_neg(tok2->u.v)) {
+ if (tok2)
+ isl_stream_push_token(s, tok2);
+ obj.type = isl_obj_val;
+ obj.v = isl_val_int_from_isl_int(s->ctx, tok->u.v);
+ isl_token_free(tok);
+ return obj;
+ }
+ isl_stream_push_token(s, tok2);
+ isl_stream_push_token(s, tok);
+ map = map_read_polylib(s);
+ if (!map)
+ goto error;
+ if (isl_map_may_be_set(map))
+ obj.v = isl_map_range(map);
+ else {
+ obj.type = isl_obj_map;
+ obj.v = map;
+ }
+ return obj;
+ }
+ v = vars_new(s->ctx);
+ if (!v) {
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ map = isl_map_universe(isl_space_params_alloc(s->ctx, 0));
+ if (tok->type == '[') {
+ isl_stream_push_token(s, tok);
+ map = read_map_tuple(s, map, isl_dim_param, v, 0, 0);
+ if (!map)
+ goto error;
+ tok = isl_stream_next_token(s);
+ if (!tok || tok->type != ISL_TOKEN_TO) {
+ isl_stream_error(s, tok, "expecting '->'");
+ if (tok)
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ isl_token_free(tok);
+ tok = isl_stream_next_token(s);
+ }
+ if (!tok || tok->type != '{') {
+ isl_stream_error(s, tok, "expecting '{'");
+ if (tok)
+ isl_stream_push_token(s, tok);
+ goto error;
+ }
+ isl_token_free(tok);
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ ;
+ else if (tok->type == ISL_TOKEN_IDENT && !strcmp(tok->u.s, "Sym")) {
+ isl_token_free(tok);
+ if (isl_stream_eat(s, '='))
+ goto error;
+ map = read_map_tuple(s, map, isl_dim_param, v, 0, 1);
+ if (!map)
+ goto error;
+ } else
+ isl_stream_push_token(s, tok);
+
+ obj = obj_read_disjuncts(s, v, map);
+ if (obj.type == isl_obj_none || !obj.v)
+ goto error;
+
+ tok = isl_stream_next_token(s);
+ if (tok && tok->type == '}') {
+ isl_token_free(tok);
+ } else {
+ isl_stream_error(s, tok, "unexpected isl_token");
+ if (tok)
+ isl_token_free(tok);
+ goto error;
+ }
+
+ vars_free(v);
+ isl_map_free(map);
+
+ return obj;
+error:
+ isl_map_free(map);
+ obj.type->free(obj.v);
+ if (v)
+ vars_free(v);
+ obj.v = NULL;
+ return obj;
+}
+
+struct isl_obj isl_stream_read_obj(__isl_keep isl_stream *s)
+{
+ return obj_read(s);
+}
+
+__isl_give isl_map *isl_stream_read_map(__isl_keep isl_stream *s)
+{
+ struct isl_obj obj;
+
+ obj = obj_read(s);
+ if (obj.v)
+ isl_assert(s->ctx, obj.type == isl_obj_map ||
+ obj.type == isl_obj_set, goto error);
+
+ if (obj.type == isl_obj_set)
+ obj.v = isl_map_from_range(obj.v);
+
+ return obj.v;
+error:
+ obj.type->free(obj.v);
+ return NULL;
+}
+
+__isl_give isl_set *isl_stream_read_set(__isl_keep isl_stream *s)
+{
+ struct isl_obj obj;
+
+ obj = obj_read(s);
+ if (obj.v) {
+ if (obj.type == isl_obj_map && isl_map_may_be_set(obj.v)) {
+ obj.v = isl_map_range(obj.v);
+ obj.type = isl_obj_set;
+ }
+ isl_assert(s->ctx, obj.type == isl_obj_set, goto error);
+ }
+
+ return obj.v;
+error:
+ obj.type->free(obj.v);
+ return NULL;
+}
+
+__isl_give isl_union_map *isl_stream_read_union_map(__isl_keep isl_stream *s)
+{
+ struct isl_obj obj;
+
+ obj = obj_read(s);
+ if (obj.type == isl_obj_map) {
+ obj.type = isl_obj_union_map;
+ obj.v = isl_union_map_from_map(obj.v);
+ }
+ if (obj.type == isl_obj_set) {
+ obj.type = isl_obj_union_set;
+ obj.v = isl_union_set_from_set(obj.v);
+ }
+ if (obj.v && obj.type == isl_obj_union_set &&
+ isl_union_set_is_empty(obj.v))
+ obj.type = isl_obj_union_map;
+ if (obj.v && obj.type != isl_obj_union_map)
+ isl_die(s->ctx, isl_error_invalid, "invalid input", goto error);
+
+ return obj.v;
+error:
+ obj.type->free(obj.v);
+ return NULL;
+}
+
+/* Extract an isl_union_set from "obj".
+ * This only works if the object was detected as either a set
+ * (in which case it is converted to a union set) or a union set.
+ */
+static __isl_give isl_union_set *extract_union_set(isl_ctx *ctx,
+ struct isl_obj obj)
+{
+ if (obj.type == isl_obj_set) {
+ obj.type = isl_obj_union_set;
+ obj.v = isl_union_set_from_set(obj.v);
+ }
+ if (obj.v)
+ isl_assert(ctx, obj.type == isl_obj_union_set, goto error);
+
+ return obj.v;
+error:
+ obj.type->free(obj.v);
+ return NULL;
+}
+
+/* Read an isl_union_set from "s".
+ * First read a generic object and then try and extract
+ * an isl_union_set from that.
+ */
+__isl_give isl_union_set *isl_stream_read_union_set(__isl_keep isl_stream *s)
+{
+ struct isl_obj obj;
+
+ obj = obj_read(s);
+ return extract_union_set(s->ctx, obj);
+}
+
+static __isl_give isl_basic_map *basic_map_read(__isl_keep isl_stream *s)
+{
+ struct isl_obj obj;
+ struct isl_map *map;
+ struct isl_basic_map *bmap;
+
+ obj = obj_read(s);
+ if (obj.v && (obj.type != isl_obj_map && obj.type != isl_obj_set))
+ isl_die(s->ctx, isl_error_invalid, "not a (basic) set or map",
+ goto error);
+ map = obj.v;
+ if (!map)
+ return NULL;
+
+ if (map->n > 1)
+ isl_die(s->ctx, isl_error_invalid,
+ "set or map description involves "
+ "more than one disjunct", goto error);
+
+ if (map->n == 0)
+ bmap = isl_basic_map_empty(isl_map_get_space(map));
+ else
+ bmap = isl_basic_map_copy(map->p[0]);
+
+ isl_map_free(map);
+
+ return bmap;
+error:
+ obj.type->free(obj.v);
+ return NULL;
+}
+
+static __isl_give isl_basic_set *basic_set_read(__isl_keep isl_stream *s)
+{
+ isl_basic_map *bmap;
+ bmap = basic_map_read(s);
+ if (!bmap)
+ return NULL;
+ if (!isl_basic_map_may_be_set(bmap))
+ isl_die(s->ctx, isl_error_invalid,
+ "input is not a set", goto error);
+ return isl_basic_map_range(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_read_from_file(isl_ctx *ctx,
+ FILE *input)
+{
+ struct isl_basic_map *bmap;
+ isl_stream *s = isl_stream_new_file(ctx, input);
+ if (!s)
+ return NULL;
+ bmap = basic_map_read(s);
+ isl_stream_free(s);
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_read_from_file(isl_ctx *ctx,
+ FILE *input)
+{
+ isl_basic_set *bset;
+ isl_stream *s = isl_stream_new_file(ctx, input);
+ if (!s)
+ return NULL;
+ bset = basic_set_read(s);
+ isl_stream_free(s);
+ return bset;
+}
+
+__isl_give isl_basic_map *isl_basic_map_read_from_str(isl_ctx *ctx,
+ const char *str)
+{
+ struct isl_basic_map *bmap;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ bmap = basic_map_read(s);
+ isl_stream_free(s);
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_read_from_str(isl_ctx *ctx,
+ const char *str)
+{
+ isl_basic_set *bset;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ bset = basic_set_read(s);
+ isl_stream_free(s);
+ return bset;
+}
+
+__isl_give isl_map *isl_map_read_from_file(struct isl_ctx *ctx,
+ FILE *input)
+{
+ struct isl_map *map;
+ isl_stream *s = isl_stream_new_file(ctx, input);
+ if (!s)
+ return NULL;
+ map = isl_stream_read_map(s);
+ isl_stream_free(s);
+ return map;
+}
+
+__isl_give isl_map *isl_map_read_from_str(struct isl_ctx *ctx,
+ const char *str)
+{
+ struct isl_map *map;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ map = isl_stream_read_map(s);
+ isl_stream_free(s);
+ return map;
+}
+
+__isl_give isl_set *isl_set_read_from_file(struct isl_ctx *ctx,
+ FILE *input)
+{
+ isl_set *set;
+ isl_stream *s = isl_stream_new_file(ctx, input);
+ if (!s)
+ return NULL;
+ set = isl_stream_read_set(s);
+ isl_stream_free(s);
+ return set;
+}
+
+__isl_give isl_set *isl_set_read_from_str(isl_ctx *ctx, const char *str)
+{
+ isl_set *set;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ set = isl_stream_read_set(s);
+ isl_stream_free(s);
+ return set;
+}
+
+__isl_give isl_union_map *isl_union_map_read_from_file(isl_ctx *ctx,
+ FILE *input)
+{
+ isl_union_map *umap;
+ isl_stream *s = isl_stream_new_file(ctx, input);
+ if (!s)
+ return NULL;
+ umap = isl_stream_read_union_map(s);
+ isl_stream_free(s);
+ return umap;
+}
+
+__isl_give isl_union_map *isl_union_map_read_from_str(struct isl_ctx *ctx,
+ const char *str)
+{
+ isl_union_map *umap;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ umap = isl_stream_read_union_map(s);
+ isl_stream_free(s);
+ return umap;
+}
+
+__isl_give isl_union_set *isl_union_set_read_from_file(isl_ctx *ctx,
+ FILE *input)
+{
+ isl_union_set *uset;
+ isl_stream *s = isl_stream_new_file(ctx, input);
+ if (!s)
+ return NULL;
+ uset = isl_stream_read_union_set(s);
+ isl_stream_free(s);
+ return uset;
+}
+
+__isl_give isl_union_set *isl_union_set_read_from_str(struct isl_ctx *ctx,
+ const char *str)
+{
+ isl_union_set *uset;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ uset = isl_stream_read_union_set(s);
+ isl_stream_free(s);
+ return uset;
+}
+
+static __isl_give isl_vec *isl_vec_read_polylib(__isl_keep isl_stream *s)
+{
+ struct isl_vec *vec = NULL;
+ struct isl_token *tok;
+ unsigned size;
+ int j;
+
+ tok = isl_stream_next_token(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok, "expecting vector length");
+ goto error;
+ }
+
+ size = isl_int_get_si(tok->u.v);
+ isl_token_free(tok);
+
+ vec = isl_vec_alloc(s->ctx, size);
+
+ for (j = 0; j < size; ++j) {
+ tok = isl_stream_next_token(s);
+ if (!tok || tok->type != ISL_TOKEN_VALUE) {
+ isl_stream_error(s, tok, "expecting constant value");
+ goto error;
+ }
+ isl_int_set(vec->el[j], tok->u.v);
+ isl_token_free(tok);
+ }
+
+ return vec;
+error:
+ isl_token_free(tok);
+ isl_vec_free(vec);
+ return NULL;
+}
+
+static __isl_give isl_vec *vec_read(__isl_keep isl_stream *s)
+{
+ return isl_vec_read_polylib(s);
+}
+
+__isl_give isl_vec *isl_vec_read_from_file(isl_ctx *ctx, FILE *input)
+{
+ isl_vec *v;
+ isl_stream *s = isl_stream_new_file(ctx, input);
+ if (!s)
+ return NULL;
+ v = vec_read(s);
+ isl_stream_free(s);
+ return v;
+}
+
+__isl_give isl_pw_qpolynomial *isl_stream_read_pw_qpolynomial(
+ __isl_keep isl_stream *s)
+{
+ struct isl_obj obj;
+
+ obj = obj_read(s);
+ if (obj.v)
+ isl_assert(s->ctx, obj.type == isl_obj_pw_qpolynomial,
+ goto error);
+
+ return obj.v;
+error:
+ obj.type->free(obj.v);
+ return NULL;
+}
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_read_from_str(isl_ctx *ctx,
+ const char *str)
+{
+ isl_pw_qpolynomial *pwqp;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ pwqp = isl_stream_read_pw_qpolynomial(s);
+ isl_stream_free(s);
+ return pwqp;
+}
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_read_from_file(isl_ctx *ctx,
+ FILE *input)
+{
+ isl_pw_qpolynomial *pwqp;
+ isl_stream *s = isl_stream_new_file(ctx, input);
+ if (!s)
+ return NULL;
+ pwqp = isl_stream_read_pw_qpolynomial(s);
+ isl_stream_free(s);
+ return pwqp;
+}
+
+/* Read an isl_pw_qpolynomial_fold from "s".
+ * First read a generic object and
+ * then check that it is an isl_pw_qpolynomial_fold.
+ */
+__isl_give isl_pw_qpolynomial_fold *isl_stream_read_pw_qpolynomial_fold(
+ __isl_keep isl_stream *s)
+{
+ struct isl_obj obj;
+
+ obj = obj_read(s);
+ if (obj.v && obj.type != isl_obj_pw_qpolynomial_fold)
+ isl_die(s->ctx, isl_error_invalid, "invalid input", goto error);
+
+ return obj.v;
+error:
+ obj.type->free(obj.v);
+ return NULL;
+}
+
+/* Read an isl_pw_qpolynomial_fold from "str".
+ */
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_read_from_str(
+ isl_ctx *ctx, const char *str)
+{
+ isl_pw_qpolynomial_fold *pwqp;
+ isl_stream *s;
+
+ s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ pwqp = isl_stream_read_pw_qpolynomial_fold(s);
+ isl_stream_free(s);
+
+ return pwqp;
+}
+
+/* Is the next token an identifier not in "v"?
+ */
+static int next_is_fresh_ident(__isl_keep isl_stream *s, struct vars *v)
+{
+ int n = v->n;
+ int fresh;
+ struct isl_token *tok;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+ fresh = tok->type == ISL_TOKEN_IDENT && vars_pos(v, tok->u.s, -1) >= n;
+ isl_stream_push_token(s, tok);
+
+ vars_drop(v, v->n - n);
+
+ return fresh;
+}
+
+/* First read the domain of the affine expression, which may be
+ * a parameter space or a set.
+ * The tricky part is that we don't know if the domain is a set or not,
+ * so when we are trying to read the domain, we may actually be reading
+ * the affine expression itself (defined on a parameter domains)
+ * If the tuple we are reading is named, we assume it's the domain.
+ * Also, if inside the tuple, the first thing we find is a nested tuple
+ * or a new identifier, we again assume it's the domain.
+ * Finally, if the tuple is empty, then it must be the domain
+ * since it does not contain an affine expression.
+ * Otherwise, we assume we are reading an affine expression.
+ */
+static __isl_give isl_set *read_aff_domain(__isl_keep isl_stream *s,
+ __isl_take isl_set *dom, struct vars *v)
+{
+ struct isl_token *tok, *tok2;
+ int is_empty;
+
+ tok = isl_stream_next_token(s);
+ if (tok && (tok->type == ISL_TOKEN_IDENT || tok->is_keyword)) {
+ isl_stream_push_token(s, tok);
+ return read_map_tuple(s, dom, isl_dim_set, v, 0, 0);
+ }
+ if (!tok || tok->type != '[') {
+ isl_stream_error(s, tok, "expecting '['");
+ goto error;
+ }
+ tok2 = isl_stream_next_token(s);
+ is_empty = tok2 && tok2->type == ']';
+ if (tok2)
+ isl_stream_push_token(s, tok2);
+ if (is_empty || next_is_tuple(s) || next_is_fresh_ident(s, v)) {
+ isl_stream_push_token(s, tok);
+ dom = read_map_tuple(s, dom, isl_dim_set, v, 0, 0);
+ } else
+ isl_stream_push_token(s, tok);
+
+ return dom;
+error:
+ if (tok)
+ isl_stream_push_token(s, tok);
+ isl_set_free(dom);
+ return NULL;
+}
+
+/* Read an affine expression from "s".
+ */
+__isl_give isl_aff *isl_stream_read_aff(__isl_keep isl_stream *s)
+{
+ isl_aff *aff;
+ isl_multi_aff *ma;
+ isl_size dim;
+
+ ma = isl_stream_read_multi_aff(s);
+ dim = isl_multi_aff_dim(ma, isl_dim_out);
+ if (dim < 0)
+ goto error;
+ if (dim != 1)
+ isl_die(s->ctx, isl_error_invalid,
+ "expecting single affine expression",
+ goto error);
+
+ aff = isl_multi_aff_get_aff(ma, 0);
+ isl_multi_aff_free(ma);
+ return aff;
+error:
+ isl_multi_aff_free(ma);
+ return NULL;
+}
+
+/* Read a piecewise affine expression from "s" with domain (space) "dom".
+ */
+static __isl_give isl_pw_aff *read_pw_aff_with_dom(__isl_keep isl_stream *s,
+ __isl_take isl_set *dom, struct vars *v)
+{
+ isl_pw_aff *pwaff = NULL;
+
+ if (!isl_set_is_params(dom) && isl_stream_eat(s, ISL_TOKEN_TO))
+ goto error;
+
+ if (isl_stream_eat(s, '['))
+ goto error;
+
+ pwaff = accept_affine(s, isl_set_get_space(dom), v);
+
+ if (isl_stream_eat(s, ']'))
+ goto error;
+
+ dom = read_optional_formula(s, dom, v, 0);
+ pwaff = isl_pw_aff_intersect_domain(pwaff, dom);
+
+ return pwaff;
+error:
+ isl_set_free(dom);
+ isl_pw_aff_free(pwaff);
+ return NULL;
+}
+
+__isl_give isl_pw_aff *isl_stream_read_pw_aff(__isl_keep isl_stream *s)
+{
+ struct vars *v;
+ isl_set *dom = NULL;
+ isl_set *aff_dom;
+ isl_pw_aff *pa = NULL;
+ int n;
+
+ v = vars_new(s->ctx);
+ if (!v)
+ return NULL;
+
+ dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0));
+ if (next_is_tuple(s)) {
+ dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0);
+ if (isl_stream_eat(s, ISL_TOKEN_TO))
+ goto error;
+ }
+ if (isl_stream_eat(s, '{'))
+ goto error;
+
+ n = v->n;
+ aff_dom = read_aff_domain(s, isl_set_copy(dom), v);
+ pa = read_pw_aff_with_dom(s, aff_dom, v);
+ vars_drop(v, v->n - n);
+
+ while (isl_stream_eat_if_available(s, ';')) {
+ isl_pw_aff *pa_i;
+
+ n = v->n;
+ aff_dom = read_aff_domain(s, isl_set_copy(dom), v);
+ pa_i = read_pw_aff_with_dom(s, aff_dom, v);
+ vars_drop(v, v->n - n);
+
+ pa = isl_pw_aff_union_add(pa, pa_i);
+ }
+
+ if (isl_stream_eat(s, '}'))
+ goto error;
+
+ vars_free(v);
+ isl_set_free(dom);
+ return pa;
+error:
+ vars_free(v);
+ isl_set_free(dom);
+ isl_pw_aff_free(pa);
+ return NULL;
+}
+
+__isl_give isl_aff *isl_aff_read_from_str(isl_ctx *ctx, const char *str)
+{
+ isl_aff *aff;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ aff = isl_stream_read_aff(s);
+ isl_stream_free(s);
+ return aff;
+}
+
+__isl_give isl_pw_aff *isl_pw_aff_read_from_str(isl_ctx *ctx, const char *str)
+{
+ isl_pw_aff *pa;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ pa = isl_stream_read_pw_aff(s);
+ isl_stream_free(s);
+ return pa;
+}
+
+/* Extract an isl_multi_pw_aff with domain space "dom_space"
+ * from a tuple "tuple" read by read_tuple.
+ *
+ * Note that the function read_tuple accepts tuples where some output or
+ * set dimensions are defined in terms of other output or set dimensions
+ * since this function is also used to read maps. As a special case,
+ * read_tuple also accept dimensions that are defined in terms of themselves
+ * (i.e., that are not defined).
+ * These cases are not allowed when extracting an isl_multi_pw_aff so check
+ * that the definitions of the output/set dimensions do not involve any
+ * output/set dimensions.
+ * Finally, drop the output dimensions from the domain of the result
+ * of read_tuple (which is of the form [input, output] -> [output],
+ * with anonymous domain) and reset the space.
+ */
+static __isl_give isl_multi_pw_aff *extract_mpa_from_tuple(
+ __isl_take isl_space *dom_space, __isl_keep isl_multi_pw_aff *tuple)
+{
+ int i;
+ isl_size dim, n;
+ isl_space *space;
+ isl_multi_pw_aff *mpa;
+
+ n = isl_multi_pw_aff_dim(tuple, isl_dim_out);
+ dim = isl_space_dim(dom_space, isl_dim_all);
+ if (n < 0 || dim < 0)
+ dom_space = isl_space_free(dom_space);
+ space = isl_space_range(isl_multi_pw_aff_get_space(tuple));
+ space = isl_space_align_params(space, isl_space_copy(dom_space));
+ if (!isl_space_is_params(dom_space))
+ space = isl_space_map_from_domain_and_range(
+ isl_space_copy(dom_space), space);
+ isl_space_free(dom_space);
+ mpa = isl_multi_pw_aff_alloc(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+ pa = isl_multi_pw_aff_get_pw_aff(tuple, i);
+ if (!pa)
+ return isl_multi_pw_aff_free(mpa);
+ if (isl_pw_aff_involves_dims(pa, isl_dim_in, dim, i + 1)) {
+ isl_ctx *ctx = isl_pw_aff_get_ctx(pa);
+ isl_pw_aff_free(pa);
+ isl_die(ctx, isl_error_invalid,
+ "not an affine expression",
+ return isl_multi_pw_aff_free(mpa));
+ }
+ pa = isl_pw_aff_drop_dims(pa, isl_dim_in, dim, n);
+ space = isl_multi_pw_aff_get_domain_space(mpa);
+ pa = isl_pw_aff_reset_domain_space(pa, space);
+ mpa = isl_multi_pw_aff_set_pw_aff(mpa, i, pa);
+ }
+
+ return mpa;
+}
+
+/* Read a tuple of affine expressions, together with optional constraints
+ * on the domain from "s". "dom" represents the initial constraints
+ * on the domain.
+ *
+ * The isl_multi_aff may live in either a set or a map space.
+ * First read the first tuple and check if it is followed by a "->".
+ * If so, convert the tuple into the domain of the isl_multi_pw_aff and
+ * read in the next tuple. This tuple (or the first tuple if it was
+ * not followed by a "->") is then converted into an isl_multi_pw_aff
+ * through a call to extract_mpa_from_tuple.
+ * The result is converted to an isl_pw_multi_aff and
+ * its domain is intersected with the domain.
+ *
+ * Note that the last tuple may introduce new identifiers,
+ * but these cannot be referenced in the description of the domain.
+ */
+static __isl_give isl_pw_multi_aff *read_conditional_multi_aff(
+ __isl_keep isl_stream *s, __isl_take isl_set *dom, struct vars *v)
+{
+ isl_multi_pw_aff *tuple;
+ isl_multi_pw_aff *mpa;
+ isl_pw_multi_aff *pma;
+ int n = v->n;
+ int n_dom;
+
+ n_dom = v->n;
+ tuple = read_tuple(s, v, 0, 0);
+ if (!tuple)
+ goto error;
+ if (isl_stream_eat_if_available(s, ISL_TOKEN_TO)) {
+ isl_map *map = map_from_tuple(tuple, dom, isl_dim_in, v, 0);
+ dom = isl_map_domain(map);
+ n_dom = v->n;
+ tuple = read_tuple(s, v, 0, 0);
+ if (!tuple)
+ goto error;
+ }
+ mpa = extract_mpa_from_tuple(isl_set_get_space(dom), tuple);
+ isl_multi_pw_aff_free(tuple);
+ if (!mpa)
+ dom = isl_set_free(dom);
+
+ vars_drop(v, v->n - n_dom);
+ dom = read_optional_formula(s, dom, v, 0);
+
+ vars_drop(v, v->n - n);
+
+ pma = isl_pw_multi_aff_from_multi_pw_aff(mpa);
+ pma = isl_pw_multi_aff_intersect_domain(pma, dom);
+
+ return pma;
+error:
+ isl_set_free(dom);
+ return NULL;
+}
+
+/* Read an isl_union_pw_multi_aff from "s".
+ *
+ * In particular, first read the parameters and then read a sequence
+ * of zero or more tuples of affine expressions with optional conditions and
+ * add them up.
+ */
+__isl_give isl_union_pw_multi_aff *isl_stream_read_union_pw_multi_aff(
+ __isl_keep isl_stream *s)
+{
+ struct vars *v;
+ isl_set *dom;
+ isl_union_pw_multi_aff *upma = NULL;
+
+ v = vars_new(s->ctx);
+ if (!v)
+ return NULL;
+
+ dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0));
+ if (next_is_tuple(s)) {
+ dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0);
+ if (isl_stream_eat(s, ISL_TOKEN_TO))
+ goto error;
+ }
+ if (isl_stream_eat(s, '{'))
+ goto error;
+
+ upma = isl_union_pw_multi_aff_empty(isl_set_get_space(dom));
+
+ do {
+ isl_pw_multi_aff *pma;
+ isl_union_pw_multi_aff *upma2;
+
+ if (isl_stream_next_token_is(s, '}'))
+ break;
+
+ pma = read_conditional_multi_aff(s, isl_set_copy(dom), v);
+ upma2 = isl_union_pw_multi_aff_from_pw_multi_aff(pma);
+ upma = isl_union_pw_multi_aff_union_add(upma, upma2);
+ if (!upma)
+ goto error;
+ } while (isl_stream_eat_if_available(s, ';'));
+
+ if (isl_stream_eat(s, '}'))
+ goto error;
+
+ isl_set_free(dom);
+ vars_free(v);
+ return upma;
+error:
+ isl_union_pw_multi_aff_free(upma);
+ isl_set_free(dom);
+ vars_free(v);
+ return NULL;
+}
+
+/* Read an isl_pw_multi_aff from "s".
+ *
+ * Read a more generic isl_union_pw_multi_aff first and
+ * then check that the result lives in a single space.
+ */
+__isl_give isl_pw_multi_aff *isl_stream_read_pw_multi_aff(
+ __isl_keep isl_stream *s)
+{
+ isl_bool single_space;
+ isl_union_pw_multi_aff *upma;
+
+ upma = isl_stream_read_union_pw_multi_aff(s);
+ single_space = isl_union_pw_multi_aff_isa_pw_multi_aff(upma);
+ if (single_space < 0)
+ upma = isl_union_pw_multi_aff_free(upma);
+ else if (!single_space)
+ isl_die(s->ctx, isl_error_invalid,
+ "expecting expression in single space",
+ upma = isl_union_pw_multi_aff_free(upma));
+ return isl_union_pw_multi_aff_as_pw_multi_aff(upma);
+}
+
+__isl_give isl_pw_multi_aff *isl_pw_multi_aff_read_from_str(isl_ctx *ctx,
+ const char *str)
+{
+ isl_pw_multi_aff *pma;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ pma = isl_stream_read_pw_multi_aff(s);
+ isl_stream_free(s);
+ return pma;
+}
+
+/* Read an isl_union_pw_multi_aff from "str".
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_pw_multi_aff_read_from_str(
+ isl_ctx *ctx, const char *str)
+{
+ isl_union_pw_multi_aff *upma;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ upma = isl_stream_read_union_pw_multi_aff(s);
+ isl_stream_free(s);
+ return upma;
+}
+
+/* Assuming "pa" represents a single affine expression defined on a universe
+ * domain, extract this affine expression.
+ */
+static __isl_give isl_aff *aff_from_pw_aff(__isl_take isl_pw_aff *pa)
+{
+ isl_aff *aff;
+
+ if (!pa)
+ return NULL;
+ if (pa->n != 1)
+ isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid,
+ "expecting single affine expression",
+ goto error);
+ if (!isl_set_plain_is_universe(pa->p[0].set))
+ isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid,
+ "expecting universe domain",
+ goto error);
+
+ aff = isl_aff_copy(pa->p[0].aff);
+ isl_pw_aff_free(pa);
+ return aff;
+error:
+ isl_pw_aff_free(pa);
+ return NULL;
+}
+
+#undef BASE
+#define BASE val
+
+#include <isl_multi_read_no_explicit_domain_templ.c>
+
+#undef BASE
+#define BASE id
+
+#include <isl_multi_read_no_explicit_domain_templ.c>
+
+/* Read a multi-affine expression from "s".
+ * If the multi-affine expression has a domain, then the tuple
+ * representing this domain cannot involve any affine expressions.
+ * The tuple representing the actual expressions needs to consist
+ * of only affine expressions. Moreover, these expressions can
+ * only depend on parameters and input dimensions and not on other
+ * output dimensions.
+ */
+__isl_give isl_multi_aff *isl_stream_read_multi_aff(__isl_keep isl_stream *s)
+{
+ struct vars *v;
+ isl_set *dom = NULL;
+ isl_multi_pw_aff *tuple = NULL;
+ int i;
+ isl_size dim, n;
+ isl_space *space, *dom_space;
+ isl_multi_aff *ma = NULL;
+
+ v = vars_new(s->ctx);
+ if (!v)
+ return NULL;
+
+ dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0));
+ if (next_is_tuple(s)) {
+ dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0);
+ if (isl_stream_eat(s, ISL_TOKEN_TO))
+ goto error;
+ }
+ if (!isl_set_plain_is_universe(dom))
+ isl_die(s->ctx, isl_error_invalid,
+ "expecting universe parameter domain", goto error);
+ if (isl_stream_eat(s, '{'))
+ goto error;
+
+ tuple = read_tuple(s, v, 0, 0);
+ if (!tuple)
+ goto error;
+ if (isl_stream_eat_if_available(s, ISL_TOKEN_TO)) {
+ isl_set *set;
+ isl_space *space;
+ isl_bool has_expr;
+
+ has_expr = tuple_has_expr(tuple);
+ if (has_expr < 0)
+ goto error;
+ if (has_expr)
+ isl_die(s->ctx, isl_error_invalid,
+ "expecting universe domain", goto error);
+ space = isl_space_range(isl_multi_pw_aff_get_space(tuple));
+ set = isl_set_universe(space);
+ dom = isl_set_intersect_params(set, dom);
+ isl_multi_pw_aff_free(tuple);
+ tuple = read_tuple(s, v, 0, 0);
+ if (!tuple)
+ goto error;
+ }
+
+ if (isl_stream_eat(s, '}'))
+ goto error;
+
+ n = isl_multi_pw_aff_dim(tuple, isl_dim_out);
+ dim = isl_set_dim(dom, isl_dim_all);
+ if (n < 0 || dim < 0)
+ goto error;
+ dom_space = isl_set_get_space(dom);
+ space = isl_space_range(isl_multi_pw_aff_get_space(tuple));
+ space = isl_space_align_params(space, isl_space_copy(dom_space));
+ if (!isl_space_is_params(dom_space))
+ space = isl_space_map_from_domain_and_range(
+ isl_space_copy(dom_space), space);
+ isl_space_free(dom_space);
+ ma = isl_multi_aff_alloc(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+ isl_aff *aff;
+ pa = isl_multi_pw_aff_get_pw_aff(tuple, i);
+ aff = aff_from_pw_aff(pa);
+ if (!aff)
+ goto error;
+ if (isl_aff_involves_dims(aff, isl_dim_in, dim, i + 1)) {
+ isl_aff_free(aff);
+ isl_die(s->ctx, isl_error_invalid,
+ "not an affine expression", goto error);
+ }
+ aff = isl_aff_drop_dims(aff, isl_dim_in, dim, n);
+ space = isl_multi_aff_get_domain_space(ma);
+ aff = isl_aff_reset_domain_space(aff, space);
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ }
+
+ isl_multi_pw_aff_free(tuple);
+ vars_free(v);
+ isl_set_free(dom);
+ return ma;
+error:
+ isl_multi_pw_aff_free(tuple);
+ vars_free(v);
+ isl_set_free(dom);
+ isl_multi_aff_free(ma);
+ return NULL;
+}
+
+__isl_give isl_multi_aff *isl_multi_aff_read_from_str(isl_ctx *ctx,
+ const char *str)
+{
+ isl_multi_aff *maff;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ maff = isl_stream_read_multi_aff(s);
+ isl_stream_free(s);
+ return maff;
+}
+
+/* Read an isl_multi_pw_aff from "s".
+ *
+ * The input format is similar to that of map, except that any conditions
+ * on the domains should be specified inside the tuple since each
+ * piecewise affine expression may have a different domain.
+ * However, additional, shared conditions can also be specified.
+ * This is especially useful for setting the explicit domain
+ * of a zero-dimensional isl_multi_pw_aff.
+ *
+ * Since we do not know in advance if the isl_multi_pw_aff lives
+ * in a set or a map space, we first read the first tuple and check
+ * if it is followed by a "->". If so, we convert the tuple into
+ * the domain of the isl_multi_pw_aff and read in the next tuple.
+ * This tuple (or the first tuple if it was not followed by a "->")
+ * is then converted into the isl_multi_pw_aff through a call
+ * to extract_mpa_from_tuple and the domain of the result
+ * is intersected with the domain.
+ *
+ * Note that the last tuple may introduce new identifiers,
+ * but these cannot be referenced in the description of the domain.
+ */
+__isl_give isl_multi_pw_aff *isl_stream_read_multi_pw_aff(
+ __isl_keep isl_stream *s)
+{
+ int n_dom;
+ struct vars *v;
+ isl_set *dom = NULL;
+ isl_multi_pw_aff *tuple = NULL;
+ isl_multi_pw_aff *mpa = NULL;
+
+ v = vars_new(s->ctx);
+ if (!v)
+ return NULL;
+
+ dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0));
+ if (next_is_tuple(s)) {
+ dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0);
+ if (isl_stream_eat(s, ISL_TOKEN_TO))
+ goto error;
+ }
+ if (isl_stream_eat(s, '{'))
+ goto error;
+
+ n_dom = v->n;
+ tuple = read_tuple(s, v, 0, 0);
+ if (!tuple)
+ goto error;
+ if (isl_stream_eat_if_available(s, ISL_TOKEN_TO)) {
+ isl_map *map = map_from_tuple(tuple, dom, isl_dim_in, v, 0);
+ dom = isl_map_domain(map);
+ n_dom = v->n;
+ tuple = read_tuple(s, v, 0, 0);
+ if (!tuple)
+ goto error;
+ }
+
+ vars_drop(v, v->n - n_dom);
+ if (isl_stream_eat_if_available(s, ':'))
+ dom = read_formula(s, v, dom, 0);
+
+ if (isl_stream_eat(s, '}'))
+ goto error;
+
+ mpa = extract_mpa_from_tuple(isl_set_get_space(dom), tuple);
+
+ isl_multi_pw_aff_free(tuple);
+ vars_free(v);
+ mpa = isl_multi_pw_aff_intersect_domain(mpa, dom);
+ return mpa;
+error:
+ isl_multi_pw_aff_free(tuple);
+ vars_free(v);
+ isl_set_free(dom);
+ isl_multi_pw_aff_free(mpa);
+ return NULL;
+}
+
+/* Read an isl_multi_pw_aff from "str".
+ */
+__isl_give isl_multi_pw_aff *isl_multi_pw_aff_read_from_str(isl_ctx *ctx,
+ const char *str)
+{
+ isl_multi_pw_aff *mpa;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ mpa = isl_stream_read_multi_pw_aff(s);
+ isl_stream_free(s);
+ return mpa;
+}
+
+/* Read the body of an isl_union_pw_aff from "s" with parameter domain "dom".
+ */
+static __isl_give isl_union_pw_aff *read_union_pw_aff_with_dom(
+ __isl_keep isl_stream *s, __isl_take isl_set *dom, struct vars *v)
+{
+ isl_pw_aff *pa;
+ isl_union_pw_aff *upa = NULL;
+ isl_set *aff_dom;
+ int n;
+
+ n = v->n;
+ aff_dom = read_aff_domain(s, isl_set_copy(dom), v);
+ pa = read_pw_aff_with_dom(s, aff_dom, v);
+ vars_drop(v, v->n - n);
+
+ upa = isl_union_pw_aff_from_pw_aff(pa);
+
+ while (isl_stream_eat_if_available(s, ';')) {
+ isl_pw_aff *pa_i;
+ isl_union_pw_aff *upa_i;
+
+ n = v->n;
+ aff_dom = read_aff_domain(s, isl_set_copy(dom), v);
+ pa_i = read_pw_aff_with_dom(s, aff_dom, v);
+ vars_drop(v, v->n - n);
+
+ upa_i = isl_union_pw_aff_from_pw_aff(pa_i);
+ upa = isl_union_pw_aff_union_add(upa, upa_i);
+ }
+
+ isl_set_free(dom);
+ return upa;
+}
+
+/* Read an isl_union_pw_aff from "s".
+ *
+ * First check if there are any paramters, then read in the opening brace
+ * and use read_union_pw_aff_with_dom to read in the body of
+ * the isl_union_pw_aff. Finally, read the closing brace.
+ */
+__isl_give isl_union_pw_aff *isl_stream_read_union_pw_aff(
+ __isl_keep isl_stream *s)
+{
+ struct vars *v;
+ isl_set *dom;
+ isl_union_pw_aff *upa = NULL;
+
+ v = vars_new(s->ctx);
+ if (!v)
+ return NULL;
+
+ dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0));
+ if (next_is_tuple(s)) {
+ dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0);
+ if (isl_stream_eat(s, ISL_TOKEN_TO))
+ goto error;
+ }
+ if (isl_stream_eat(s, '{'))
+ goto error;
+
+ upa = read_union_pw_aff_with_dom(s, isl_set_copy(dom), v);
+
+ if (isl_stream_eat(s, '}'))
+ goto error;
+
+ vars_free(v);
+ isl_set_free(dom);
+ return upa;
+error:
+ vars_free(v);
+ isl_set_free(dom);
+ isl_union_pw_aff_free(upa);
+ return NULL;
+}
+
+/* Read an isl_union_pw_aff from "str".
+ */
+__isl_give isl_union_pw_aff *isl_union_pw_aff_read_from_str(isl_ctx *ctx,
+ const char *str)
+{
+ isl_union_pw_aff *upa;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ upa = isl_stream_read_union_pw_aff(s);
+ isl_stream_free(s);
+ return upa;
+}
+
+/* This function is called for each element in a tuple inside
+ * isl_stream_read_multi_union_pw_aff.
+ *
+ * Read a '{', the union piecewise affine expression body and a '}' and
+ * add the isl_union_pw_aff to *list.
+ */
+static __isl_give isl_space *read_union_pw_aff_el(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_space *space, int rational, void *user)
+{
+ isl_set *dom;
+ isl_union_pw_aff *upa;
+ isl_union_pw_aff_list **list = (isl_union_pw_aff_list **) user;
+
+ dom = isl_set_universe(isl_space_params(isl_space_copy(space)));
+ if (isl_stream_eat(s, '{'))
+ goto error;
+ upa = read_union_pw_aff_with_dom(s, dom, v);
+ *list = isl_union_pw_aff_list_add(*list, upa);
+ if (isl_stream_eat(s, '}'))
+ return isl_space_free(space);
+ if (!*list)
+ return isl_space_free(space);
+ return space;
+error:
+ isl_set_free(dom);
+ return isl_space_free(space);
+}
+
+/* Do the next tokens in "s" correspond to an empty tuple?
+ * In particular, does the stream start with a '[', followed by a ']',
+ * not followed by a "->"?
+ */
+static int next_is_empty_tuple(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok, *tok2, *tok3;
+ int is_empty_tuple = 0;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+ if (tok->type != '[') {
+ isl_stream_push_token(s, tok);
+ return 0;
+ }
+
+ tok2 = isl_stream_next_token(s);
+ if (tok2 && tok2->type == ']') {
+ tok3 = isl_stream_next_token(s);
+ is_empty_tuple = !tok || tok->type != ISL_TOKEN_TO;
+ if (tok3)
+ isl_stream_push_token(s, tok3);
+ }
+ if (tok2)
+ isl_stream_push_token(s, tok2);
+ isl_stream_push_token(s, tok);
+
+ return is_empty_tuple;
+}
+
+/* Do the next tokens in "s" correspond to a tuple of parameters?
+ * In particular, does the stream start with a '[' that is not
+ * followed by a '{' or a nested tuple?
+ */
+static int next_is_param_tuple(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok, *tok2;
+ int is_tuple;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+ if (tok->type != '[' || next_is_tuple(s)) {
+ isl_stream_push_token(s, tok);
+ return 0;
+ }
+
+ tok2 = isl_stream_next_token(s);
+ is_tuple = tok2 && tok2->type != '{';
+ if (tok2)
+ isl_stream_push_token(s, tok2);
+ isl_stream_push_token(s, tok);
+
+ return is_tuple;
+}
+
+/* Read the core of a body of an isl_multi_union_pw_aff from "s",
+ * i.e., everything except the parameter specification and
+ * without shared domain constraints.
+ * "v" contains a description of the identifiers parsed so far.
+ * The parameters, if any, are specified by "space".
+ *
+ * The body is of the form
+ *
+ * [{ [..] : ... ; [..] : ... }, { [..] : ... ; [..] : ... }]
+ *
+ * Read the tuple, collecting the individual isl_union_pw_aff
+ * elements in a list and construct the result from the tuple space and
+ * the list.
+ */
+static __isl_give isl_multi_union_pw_aff *read_multi_union_pw_aff_body_core(
+ __isl_keep isl_stream *s, struct vars *v, __isl_take isl_space *space)
+{
+ isl_union_pw_aff_list *list;
+ isl_multi_union_pw_aff *mupa;
+
+ list = isl_union_pw_aff_list_alloc(s->ctx, 0);
+ space = read_tuple_space(s, v, space, 1, 0,
+ &read_union_pw_aff_el, &list);
+ mupa = isl_multi_union_pw_aff_from_union_pw_aff_list(space, list);
+
+ return mupa;
+}
+
+/* Read the body of an isl_union_set from "s",
+ * i.e., everything except the parameter specification.
+ * "v" contains a description of the identifiers parsed so far.
+ * The parameters, if any, are specified by "space".
+ *
+ * First read a generic disjunction of object bodies and then try and extract
+ * an isl_union_set from that.
+ */
+static __isl_give isl_union_set *read_union_set_body(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_space *space)
+{
+ struct isl_obj obj = { isl_obj_set, NULL };
+ isl_map *map;
+
+ map = isl_set_universe(space);
+ if (isl_stream_eat(s, '{') < 0)
+ goto error;
+ obj = obj_read_disjuncts(s, v, map);
+ if (isl_stream_eat(s, '}') < 0)
+ goto error;
+ isl_map_free(map);
+
+ return extract_union_set(s->ctx, obj);
+error:
+ obj.type->free(obj.v);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Read the body of an isl_multi_union_pw_aff from "s",
+ * i.e., everything except the parameter specification.
+ * "v" contains a description of the identifiers parsed so far.
+ * The parameters, if any, are specified by "space".
+ *
+ * In particular, handle the special case with shared domain constraints.
+ * These are specified as
+ *
+ * ([...] : ...)
+ *
+ * and are especially useful for setting the explicit domain
+ * of a zero-dimensional isl_multi_union_pw_aff.
+ * The core isl_multi_union_pw_aff body ([...]) is read by
+ * read_multi_union_pw_aff_body_core.
+ */
+static __isl_give isl_multi_union_pw_aff *read_multi_union_pw_aff_body(
+ __isl_keep isl_stream *s, struct vars *v, __isl_take isl_space *space)
+{
+ isl_multi_union_pw_aff *mupa;
+
+ if (!isl_stream_next_token_is(s, '('))
+ return read_multi_union_pw_aff_body_core(s, v, space);
+
+ if (isl_stream_eat(s, '(') < 0)
+ goto error;
+ mupa = read_multi_union_pw_aff_body_core(s, v, isl_space_copy(space));
+ if (isl_stream_eat_if_available(s, ':')) {
+ isl_union_set *dom;
+
+ dom = read_union_set_body(s, v, space);
+ mupa = isl_multi_union_pw_aff_intersect_domain(mupa, dom);
+ } else {
+ isl_space_free(space);
+ }
+ if (isl_stream_eat(s, ')') < 0)
+ return isl_multi_union_pw_aff_free(mupa);
+
+ return mupa;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Read an isl_multi_union_pw_aff from "s".
+ *
+ * The input has the form
+ *
+ * [{ [..] : ... ; [..] : ... }, { [..] : ... ; [..] : ... }]
+ *
+ * or
+ *
+ * [..] -> [{ [..] : ... ; [..] : ... }, { [..] : ... ; [..] : ... }]
+ *
+ * Additionally, a shared domain may be specified as
+ *
+ * ([..] : ...)
+ *
+ * or
+ *
+ * [..] -> ([..] : ...)
+ *
+ * The first case is handled by the caller, the second case
+ * is handled by read_multi_union_pw_aff_body.
+ *
+ * We first check for the special case of an empty tuple "[]".
+ * Then we check if there are any parameters.
+ * Finally, read the tuple and construct the result.
+ */
+static __isl_give isl_multi_union_pw_aff *read_multi_union_pw_aff_core(
+ __isl_keep isl_stream *s)
+{
+ struct vars *v;
+ isl_set *dom = NULL;
+ isl_space *space;
+ isl_multi_union_pw_aff *mupa = NULL;
+
+ if (next_is_empty_tuple(s)) {
+ if (isl_stream_eat(s, '['))
+ return NULL;
+ if (isl_stream_eat(s, ']'))
+ return NULL;
+ space = isl_space_set_alloc(s->ctx, 0, 0);
+ return isl_multi_union_pw_aff_zero(space);
+ }
+
+ v = vars_new(s->ctx);
+ if (!v)
+ return NULL;
+
+ dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0));
+ if (next_is_param_tuple(s)) {
+ dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0);
+ if (isl_stream_eat(s, ISL_TOKEN_TO))
+ goto error;
+ }
+ space = isl_set_get_space(dom);
+ isl_set_free(dom);
+ mupa = read_multi_union_pw_aff_body(s, v, space);
+
+ vars_free(v);
+
+ return mupa;
+error:
+ vars_free(v);
+ isl_set_free(dom);
+ isl_multi_union_pw_aff_free(mupa);
+ return NULL;
+}
+
+/* Read an isl_multi_union_pw_aff from "s".
+ *
+ * In particular, handle the special case with shared domain constraints.
+ * These are specified as
+ *
+ * ([...] : ...)
+ *
+ * and are especially useful for setting the explicit domain
+ * of a zero-dimensional isl_multi_union_pw_aff.
+ * The core isl_multi_union_pw_aff ([...]) is read by
+ * read_multi_union_pw_aff_core.
+ */
+__isl_give isl_multi_union_pw_aff *isl_stream_read_multi_union_pw_aff(
+ __isl_keep isl_stream *s)
+{
+ isl_multi_union_pw_aff *mupa;
+
+ if (!isl_stream_next_token_is(s, '('))
+ return read_multi_union_pw_aff_core(s);
+
+ if (isl_stream_eat(s, '(') < 0)
+ return NULL;
+ mupa = read_multi_union_pw_aff_core(s);
+ if (isl_stream_eat_if_available(s, ':')) {
+ isl_union_set *dom;
+
+ dom = isl_stream_read_union_set(s);
+ mupa = isl_multi_union_pw_aff_intersect_domain(mupa, dom);
+ }
+ if (isl_stream_eat(s, ')') < 0)
+ return isl_multi_union_pw_aff_free(mupa);
+ return mupa;
+}
+
+/* Read an isl_multi_union_pw_aff from "str".
+ */
+__isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_read_from_str(
+ isl_ctx *ctx, const char *str)
+{
+ isl_multi_union_pw_aff *mupa;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ mupa = isl_stream_read_multi_union_pw_aff(s);
+ isl_stream_free(s);
+ return mupa;
+}
+
+__isl_give isl_union_pw_qpolynomial *isl_stream_read_union_pw_qpolynomial(
+ __isl_keep isl_stream *s)
+{
+ struct isl_obj obj;
+
+ obj = obj_read(s);
+ if (obj.type == isl_obj_pw_qpolynomial) {
+ obj.type = isl_obj_union_pw_qpolynomial;
+ obj.v = isl_union_pw_qpolynomial_from_pw_qpolynomial(obj.v);
+ }
+ if (obj.v)
+ isl_assert(s->ctx, obj.type == isl_obj_union_pw_qpolynomial,
+ goto error);
+
+ return obj.v;
+error:
+ obj.type->free(obj.v);
+ return NULL;
+}
+
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_read_from_str(
+ isl_ctx *ctx, const char *str)
+{
+ isl_union_pw_qpolynomial *upwqp;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ upwqp = isl_stream_read_union_pw_qpolynomial(s);
+ isl_stream_free(s);
+ return upwqp;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_insert_domain_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_insert_domain_templ.c
new file mode 100644
index 00000000000..0b88d1c0d85
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_insert_domain_templ.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+/* Given a function defined over a parameter domain,
+ * convert it to a function defined over a domain corresponding
+ * to "domain".
+ */
+__isl_give TYPE *FN(TYPE,insert_domain)(__isl_take TYPE *obj,
+ __isl_take isl_space *domain)
+{
+ isl_size dim;
+ isl_space *obj_space;
+
+ obj_space = FN(TYPE,peek_space)(obj);
+ if (isl_space_check_is_set(domain) < 0 ||
+ isl_space_check_is_set(obj_space) < 0)
+ goto error;
+ dim = isl_space_dim(domain, isl_dim_set);
+ if (dim < 0)
+ goto error;
+
+ domain = isl_space_replace_params(domain, obj_space);
+
+ obj = FN(TYPE,from_range)(obj);
+ obj = FN(TYPE,add_dims)(obj, isl_dim_in, dim);
+ obj = FN(TYPE,reset_domain_space)(obj, domain);
+
+ return obj;
+error:
+ isl_space_free(domain);
+ FN(TYPE,free)(obj);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int.h
new file mode 100644
index 00000000000..a2e35c2ee2f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_INT_H
+#define ISL_INT_H
+#define ISL_DEPRECATED_INT_H
+
+#include <isl/hash.h>
+#include <isl/printer.h>
+#include <string.h>
+#include <isl_config.h>
+
+#ifdef USE_GMP_FOR_MP
+#error #include <isl_int_gmp.h>
+#endif
+
+#ifdef USE_IMATH_FOR_MP
+#ifdef USE_SMALL_INT_OPT
+#include <isl_int_sioimath.h>
+#else /* USE_SMALL_INT_OPT */
+#error #include <isl_int_imath.h>
+#endif /* USE_SMALL_INT_OPT */
+#endif /* USE_IMATH_FOR_MP */
+
+#define isl_int_is_zero(i) (isl_int_sgn(i) == 0)
+#define isl_int_is_one(i) (isl_int_cmp_si(i,1) == 0)
+#define isl_int_is_negone(i) (isl_int_cmp_si(i,-1) == 0)
+#define isl_int_is_pos(i) (isl_int_sgn(i) > 0)
+#define isl_int_is_neg(i) (isl_int_sgn(i) < 0)
+#define isl_int_is_nonpos(i) (isl_int_sgn(i) <= 0)
+#define isl_int_is_nonneg(i) (isl_int_sgn(i) >= 0)
+
+#ifndef USE_SMALL_INT_OPT
+#define isl_int_print(out,i,width) \
+ do { \
+ char *s; \
+ s = isl_int_get_str(i); \
+ fprintf(out, "%*s", width, s); \
+ isl_int_free_str(s); \
+ } while (0)
+#endif /* USE_SMALL_INT_OPT */
+
+__isl_give isl_printer *isl_printer_print_isl_int(__isl_take isl_printer *p,
+ isl_int i);
+
+#endif /* ISL_INT_H */
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int_sioimath.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int_sioimath.c
new file mode 100644
index 00000000000..170286213c6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int_sioimath.c
@@ -0,0 +1,223 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include <isl_int.h>
+
+extern int isl_sioimath_decode(isl_sioimath val, int32_t *small, mp_int *big);
+extern int isl_sioimath_decode_big(isl_sioimath val, mp_int *big);
+extern int isl_sioimath_decode_small(isl_sioimath val, int32_t *small);
+
+extern isl_sioimath isl_sioimath_encode_small(int32_t val);
+extern isl_sioimath isl_sioimath_encode_big(mp_int val);
+extern int isl_sioimath_is_small(isl_sioimath val);
+extern int isl_sioimath_is_big(isl_sioimath val);
+extern int32_t isl_sioimath_get_small(isl_sioimath val);
+extern mp_int isl_sioimath_get_big(isl_sioimath val);
+
+extern void isl_siomath_uint32_to_digits(uint32_t num, mp_digit *digits,
+ mp_size *used);
+extern void isl_siomath_ulong_to_digits(unsigned long num, mp_digit *digits,
+ mp_size *used);
+extern void isl_siomath_uint64_to_digits(uint64_t num, mp_digit *digits,
+ mp_size *used);
+
+extern mp_int isl_sioimath_bigarg_src(isl_sioimath arg,
+ isl_sioimath_scratchspace_t *scratch);
+extern mp_int isl_sioimath_siarg_src(signed long arg,
+ isl_sioimath_scratchspace_t *scratch);
+extern mp_int isl_sioimath_si64arg_src(int64_t arg,
+ isl_sioimath_scratchspace_t *scratch);
+extern mp_int isl_sioimath_uiarg_src(unsigned long arg,
+ isl_sioimath_scratchspace_t *scratch);
+extern mp_int isl_sioimath_reinit_big(isl_sioimath_ptr ptr);
+extern void isl_sioimath_set_small(isl_sioimath_ptr ptr, int32_t val);
+extern void isl_sioimath_set_int32(isl_sioimath_ptr ptr, int32_t val);
+extern void isl_sioimath_set_int64(isl_sioimath_ptr ptr, int64_t val);
+extern void isl_sioimath_promote(isl_sioimath_ptr dst);
+extern void isl_sioimath_try_demote(isl_sioimath_ptr dst);
+
+extern void isl_sioimath_init(isl_sioimath_ptr dst);
+extern void isl_sioimath_clear(isl_sioimath_ptr dst);
+extern void isl_sioimath_set(isl_sioimath_ptr dst, isl_sioimath_src val);
+extern void isl_sioimath_set_si(isl_sioimath_ptr dst, long val);
+extern void isl_sioimath_set_ui(isl_sioimath_ptr dst, unsigned long val);
+extern int isl_sioimath_fits_slong(isl_sioimath_src val);
+extern long isl_sioimath_get_si(isl_sioimath_src val);
+extern int isl_sioimath_fits_ulong(isl_sioimath_src val);
+extern unsigned long isl_sioimath_get_ui(isl_sioimath_src val);
+extern double isl_sioimath_get_d(isl_sioimath_src val);
+extern char *isl_sioimath_get_str(isl_sioimath_src val);
+extern void isl_sioimath_abs(isl_sioimath_ptr dst, isl_sioimath_src arg);
+extern void isl_sioimath_neg(isl_sioimath_ptr dst, isl_sioimath_src arg);
+extern void isl_sioimath_swap(isl_sioimath_ptr lhs, isl_sioimath_ptr rhs);
+extern void isl_sioimath_add_ui(isl_sioimath_ptr dst, isl_sioimath lhs,
+ unsigned long rhs);
+extern void isl_sioimath_sub_ui(isl_sioimath_ptr dst, isl_sioimath lhs,
+ unsigned long rhs);
+
+extern void isl_sioimath_add(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+extern void isl_sioimath_sub(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+extern void isl_sioimath_mul(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+extern void isl_sioimath_mul_2exp(isl_sioimath_ptr dst, isl_sioimath lhs,
+ unsigned long rhs);
+extern void isl_sioimath_mul_si(isl_sioimath_ptr dst, isl_sioimath lhs,
+ signed long rhs);
+extern void isl_sioimath_mul_ui(isl_sioimath_ptr dst, isl_sioimath lhs,
+ unsigned long rhs);
+extern void isl_sioimath_pow_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs);
+extern void isl_sioimath_addmul(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+extern void isl_sioimath_addmul_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs);
+extern void isl_sioimath_submul(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+extern void isl_sioimath_submul_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs);
+
+/* Implements the Euclidean algorithm to compute the greatest common divisor of
+ * two values in small representation.
+ */
+static uint32_t isl_sioimath_smallgcd(int32_t lhs, int32_t rhs)
+{
+ uint32_t dividend, divisor, remainder;
+
+ dividend = labs(lhs);
+ divisor = labs(rhs);
+ while (divisor) {
+ remainder = dividend % divisor;
+ dividend = divisor;
+ divisor = remainder;
+ }
+
+ return dividend;
+}
+
+/* Compute the greatest common divisor.
+ *
+ * Per GMP convention, gcd(0,0)==0 and otherwise always positive.
+ */
+void isl_sioimath_gcd(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ int32_t lhssmall, rhssmall;
+ uint32_t smallgcd;
+ isl_sioimath_scratchspace_t scratchlhs, scratchrhs;
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall) &&
+ isl_sioimath_decode_small(rhs, &rhssmall)) {
+ smallgcd = isl_sioimath_smallgcd(lhssmall, rhssmall);
+ isl_sioimath_set_small(dst, smallgcd);
+ return;
+ }
+
+ impz_gcd(isl_sioimath_reinit_big(dst),
+ isl_sioimath_bigarg_src(lhs, &scratchlhs),
+ isl_sioimath_bigarg_src(rhs, &scratchrhs));
+ isl_sioimath_try_demote(dst);
+}
+
+/* Compute the lowest common multiple of two numbers.
+ */
+void isl_sioimath_lcm(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ int32_t lhssmall, rhssmall;
+ uint32_t smallgcd;
+ uint64_t multiple;
+ isl_sioimath_scratchspace_t scratchlhs, scratchrhs;
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall) &&
+ isl_sioimath_decode_small(rhs, &rhssmall)) {
+ if (lhssmall == 0 || rhssmall == 0) {
+ isl_sioimath_set_small(dst, 0);
+ return;
+ }
+ smallgcd = isl_sioimath_smallgcd(lhssmall, rhssmall);
+ multiple = (uint64_t) abs(lhssmall) * (uint64_t) abs(rhssmall);
+ isl_sioimath_set_int64(dst, multiple / smallgcd);
+ return;
+ }
+
+ impz_lcm(isl_sioimath_reinit_big(dst),
+ isl_sioimath_bigarg_src(lhs, &scratchlhs),
+ isl_sioimath_bigarg_src(rhs, &scratchrhs));
+ isl_sioimath_try_demote(dst);
+}
+
+extern void isl_sioimath_tdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+extern void isl_sioimath_tdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs);
+extern void isl_sioimath_cdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+extern void isl_sioimath_cdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs);
+extern void isl_sioimath_fdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+extern void isl_sioimath_fdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs);
+extern void isl_sioimath_fdiv_r(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+
+/* Parse a number from a string.
+ * If it has less than 10 characters then it will fit into the small
+ * representation (i.e. strlen("2147483647")). Otherwise, let IMath parse it.
+ */
+void isl_sioimath_read(isl_sioimath_ptr dst, const char *str)
+{
+ int32_t small;
+
+ if (strlen(str) < 10) {
+ small = strtol(str, NULL, 10);
+ isl_sioimath_set_small(dst, small);
+ return;
+ }
+
+ mp_int_read_string(isl_sioimath_reinit_big(dst), 10, str);
+ isl_sioimath_try_demote(dst);
+}
+
+extern int isl_sioimath_sgn(isl_sioimath_src arg);
+extern int isl_sioimath_cmp(isl_sioimath_src lhs, isl_sioimath_src rhs);
+extern int isl_sioimath_cmp_si(isl_sioimath_src lhs, signed long rhs);
+extern int isl_sioimath_abs_cmp(isl_sioimath_src lhs, isl_sioimath_src rhs);
+extern int isl_sioimath_is_divisible_by(isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+
+extern uint32_t isl_sioimath_hash(isl_sioimath_src arg, uint32_t hash);
+extern size_t isl_sioimath_sizeinbase(isl_sioimath_src arg, int base);
+extern void isl_sioimath_print(FILE *out, isl_sioimath_src i, int width);
+
+/* Print an isl_int to FILE*. Adds space padding to the left until at least
+ * width characters are printed.
+ */
+void isl_sioimath_print(FILE *out, isl_sioimath_src i, int width)
+{
+ size_t len;
+ int32_t small;
+ mp_int big;
+ char *buf;
+
+ if (isl_sioimath_decode_small(i, &small)) {
+ fprintf(out, "%*" PRIi32, width, small);
+ return;
+ }
+
+ big = isl_sioimath_get_big(i);
+ len = mp_int_string_len(big, 10);
+ buf = malloc(len);
+ mp_int_to_string(big, 10, buf, len);
+ fprintf(out, "%*s", width, buf);
+ free(buf);
+}
+
+/* Print a number to stdout. Meant for debugging.
+ */
+void isl_sioimath_dump(isl_sioimath_src arg)
+{
+ isl_sioimath_print(stdout, arg, 0);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int_sioimath.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int_sioimath.h
new file mode 100644
index 00000000000..dc691b8a0b1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_int_sioimath.h
@@ -0,0 +1,1253 @@
+/*
+ * Copyright 2015 INRIA Paris-Rocquencourt
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Michael Kruse, INRIA Paris-Rocquencourt,
+ * Domaine de Voluceau, Rocquenqourt, B.P. 105,
+ * 78153 Le Chesnay Cedex France
+ */
+#ifndef ISL_INT_SIOIMATH_H
+#define ISL_INT_SIOIMATH_H
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <isl_imath.h>
+#include <isl/hash.h>
+
+#define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array))
+
+/* Visual Studio before VS2015 does not support the inline keyword when
+ * compiling in C mode because it was introduced in C99 which it does not
+ * officially support. Instead, it has a proprietary extension using __inline.
+ */
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+#define inline __inline
+#endif
+
+/* The type to represent integers optimized for small values. It is either a
+ * pointer to an mp_int ( = mpz_t*; big representation) or an int32_t (small
+ * represenation) with a discriminator at the least significant bit. In big
+ * representation it will be always zero because of heap alignment. It is set
+ * to 1 for small representation and use the 32 most significant bits for the
+ * int32_t.
+ *
+ * Structure on 64 bit machines, with 8-byte aligment (3 bits):
+ *
+ * Big representation:
+ * MSB LSB
+ * |------------------------------------------------------------000
+ * | mpz_t* |
+ * | != NULL |
+ *
+ * Small representation:
+ * MSB 32 LSB
+ * |------------------------------|00000000000000000000000000000001
+ * | int32_t |
+ * | 2147483647 ... -2147483647 |
+ * ^
+ * |
+ * discriminator bit
+ *
+ * On 32 bit machines isl_sioimath type is blown up to 8 bytes, i.e.
+ * isl_sioimath is guaranteed to be at least 8 bytes. This is to ensure the
+ * int32_t can be hidden in that type without data loss. In the future we might
+ * optimize this to use 31 hidden bits in a 32 bit pointer. We may also use 63
+ * bits on 64 bit machines, but this comes with the cost of additional overflow
+ * checks because there is no standardized 128 bit integer we could expand to.
+ *
+ * We use native integer types and avoid union structures to avoid assumptions
+ * on the machine's endianness.
+ *
+ * This implementation makes the following assumptions:
+ * - long can represent any int32_t
+ * - mp_small is signed long
+ * - mp_usmall is unsigned long
+ * - adresses returned by malloc are aligned to 2-byte boundaries (leastmost
+ * bit is zero)
+ */
+#if UINT64_MAX > UINTPTR_MAX
+typedef uint64_t isl_sioimath;
+#else
+typedef uintptr_t isl_sioimath;
+#endif
+
+/* The negation of the smallest possible number in int32_t, INT32_MIN
+ * (0x80000000u, -2147483648), cannot be represented in an int32_t, therefore
+ * every operation that may produce this value needs to special-case it.
+ * The operations are:
+ * abs(INT32_MIN)
+ * -INT32_MIN (negation)
+ * -1 * INT32_MIN (multiplication)
+ * INT32_MIN/-1 (any division: divexact, fdiv, cdiv, tdiv)
+ * To avoid checking these cases, we exclude INT32_MIN from small
+ * representation.
+ */
+#define ISL_SIOIMATH_SMALL_MIN (-INT32_MAX)
+
+/* Largest possible number in small representation */
+#define ISL_SIOIMATH_SMALL_MAX INT32_MAX
+
+/* Used for function parameters the function modifies. */
+typedef isl_sioimath *isl_sioimath_ptr;
+
+/* Used for function parameters that are read-only. */
+typedef isl_sioimath isl_sioimath_src;
+
+/* Return whether the argument is stored in small representation.
+ */
+inline int isl_sioimath_is_small(isl_sioimath val)
+{
+ return val & 0x00000001;
+}
+
+/* Return whether the argument is stored in big representation.
+ */
+inline int isl_sioimath_is_big(isl_sioimath val)
+{
+ return !isl_sioimath_is_small(val);
+}
+
+/* Get the number of an isl_int in small representation. Result is undefined if
+ * val is not stored in that format.
+ */
+inline int32_t isl_sioimath_get_small(isl_sioimath val)
+{
+ return val >> 32;
+}
+
+/* Get the number of an in isl_int in big representation. Result is undefined if
+ * val is not stored in that format.
+ */
+inline mp_int isl_sioimath_get_big(isl_sioimath val)
+{
+ return (mp_int)(uintptr_t) val;
+}
+
+/* Return 1 if val is stored in small representation and store its value to
+ * small. We rely on the compiler to optimize the isl_sioimath_get_small such
+ * that the shift is moved into the branch that executes in case of small
+ * representation. If there is no such branch, then a single shift is still
+ * cheaper than introducing branching code.
+ */
+inline int isl_sioimath_decode_small(isl_sioimath val, int32_t *small)
+{
+ *small = isl_sioimath_get_small(val);
+ return isl_sioimath_is_small(val);
+}
+
+/* Return 1 if val is stored in big representation and store its value to big.
+ */
+inline int isl_sioimath_decode_big(isl_sioimath val, mp_int *big)
+{
+ *big = isl_sioimath_get_big(val);
+ return isl_sioimath_is_big(val);
+}
+
+/* Encode a small representation into an isl_int.
+ */
+inline isl_sioimath isl_sioimath_encode_small(int32_t val)
+{
+ return ((isl_sioimath) val) << 32 | 0x00000001;
+}
+
+/* Encode a big representation.
+ */
+inline isl_sioimath isl_sioimath_encode_big(mp_int val)
+{
+ return (isl_sioimath)(uintptr_t) val;
+}
+
+/* A common situation is to call an IMath function with at least one argument
+ * that is currently in small representation or an integer parameter, i.e. a big
+ * representation of the same number is required. Promoting the original
+ * argument comes with multiple problems, such as modifying a read-only
+ * argument, the responsibility of deallocation and the execution cost. Instead,
+ * we make a copy by 'faking' the IMath internal structure.
+ *
+ * We reserve the maximum number of required digits on the stack to avoid heap
+ * allocations.
+ *
+ * mp_digit can be uint32_t or uint16_t. This code must work for little and big
+ * endian digits. The structure for an uint64_t argument and 32-bit mp_digits is
+ * sketched below.
+ *
+ * |----------------------------|
+ * uint64_t
+ *
+ * |-------------||-------------|
+ * mp_digit mp_digit
+ * digits[1] digits[0]
+ * Most sig digit Least sig digit
+ */
+typedef struct {
+ mpz_t big;
+ mp_digit digits[(sizeof(uintmax_t) + sizeof(mp_digit) - 1) /
+ sizeof(mp_digit)];
+} isl_sioimath_scratchspace_t;
+
+/* Convert a native integer to IMath's digit representation. A native integer
+ * might be big- or little endian, but IMath always stores the least significant
+ * digit in the lowest array indices. memcpy therefore is not possible.
+ *
+ * We also have to consider that long and mp_digit can be of different sizes,
+ * depending on the compiler (LP64, LLP64) and IMath's USE_64BIT_WORDS. This
+ * macro should work for all of them.
+ *
+ * "used" is set to the number of written digits. It must be minimal (IMath
+ * checks zeroness using the used field), but always at least one. Also note
+ * that the result of num>>(sizeof(num)*CHAR_BIT) is undefined.
+ */
+#define ISL_SIOIMATH_TO_DIGITS(num, digits, used) \
+ do { \
+ int i = 0; \
+ do { \
+ (digits)[i] = \
+ ((num) >> (sizeof(mp_digit) * CHAR_BIT * i)); \
+ i += 1; \
+ if (i >= (sizeof(num) + sizeof(mp_digit) - 1) / \
+ sizeof(mp_digit)) \
+ break; \
+ if (((num) >> (sizeof(mp_digit) * CHAR_BIT * i)) == 0) \
+ break; \
+ } while (1); \
+ (used) = i; \
+ } while (0)
+
+inline void isl_siomath_uint32_to_digits(uint32_t num, mp_digit *digits,
+ mp_size *used)
+{
+ ISL_SIOIMATH_TO_DIGITS(num, digits, *used);
+}
+
+inline void isl_siomath_ulong_to_digits(unsigned long num, mp_digit *digits,
+ mp_size *used)
+{
+ ISL_SIOIMATH_TO_DIGITS(num, digits, *used);
+}
+
+inline void isl_siomath_uint64_to_digits(uint64_t num, mp_digit *digits,
+ mp_size *used)
+{
+ ISL_SIOIMATH_TO_DIGITS(num, digits, *used);
+}
+
+/* Get the IMath representation of an isl_int without modifying it.
+ * For the case it is not in big representation yet, pass some scratch space we
+ * can use to store the big representation in.
+ * In order to avoid requiring init and free on the scratch space, we directly
+ * modify the internal representation.
+ *
+ * The name derives from its indented use: getting the big representation of an
+ * input (src) argument.
+ */
+inline mp_int isl_sioimath_bigarg_src(isl_sioimath arg,
+ isl_sioimath_scratchspace_t *scratch)
+{
+ mp_int big;
+ int32_t small;
+ uint32_t num;
+
+ if (isl_sioimath_decode_big(arg, &big))
+ return big;
+
+ small = isl_sioimath_get_small(arg);
+ scratch->big.digits = scratch->digits;
+ scratch->big.alloc = ARRAY_SIZE(scratch->digits);
+ if (small >= 0) {
+ scratch->big.sign = MP_ZPOS;
+ num = small;
+ } else {
+ scratch->big.sign = MP_NEG;
+ num = -small;
+ }
+
+ isl_siomath_uint32_to_digits(num, scratch->digits, &scratch->big.used);
+ return &scratch->big;
+}
+
+/* Create a temporary IMath mp_int for a signed long.
+ */
+inline mp_int isl_sioimath_siarg_src(signed long arg,
+ isl_sioimath_scratchspace_t *scratch)
+{
+ unsigned long num;
+
+ scratch->big.digits = scratch->digits;
+ scratch->big.alloc = ARRAY_SIZE(scratch->digits);
+ if (arg >= 0) {
+ scratch->big.sign = MP_ZPOS;
+ num = arg;
+ } else {
+ scratch->big.sign = MP_NEG;
+ num = (arg == LONG_MIN) ? ((unsigned long) LONG_MAX) + 1 : -arg;
+ }
+
+ isl_siomath_ulong_to_digits(num, scratch->digits, &scratch->big.used);
+ return &scratch->big;
+}
+
+/* Create a temporary IMath mp_int for an int64_t.
+ */
+inline mp_int isl_sioimath_si64arg_src(int64_t arg,
+ isl_sioimath_scratchspace_t *scratch)
+{
+ uint64_t num;
+
+ scratch->big.digits = scratch->digits;
+ scratch->big.alloc = ARRAY_SIZE(scratch->digits);
+ if (arg >= 0) {
+ scratch->big.sign = MP_ZPOS;
+ num = arg;
+ } else {
+ scratch->big.sign = MP_NEG;
+ num = (arg == INT64_MIN) ? ((uint64_t) INT64_MAX) + 1 : -arg;
+ }
+
+ isl_siomath_uint64_to_digits(num, scratch->digits, &scratch->big.used);
+ return &scratch->big;
+}
+
+/* Create a temporary IMath mp_int for an unsigned long.
+ */
+inline mp_int isl_sioimath_uiarg_src(unsigned long arg,
+ isl_sioimath_scratchspace_t *scratch)
+{
+ scratch->big.digits = scratch->digits;
+ scratch->big.alloc = ARRAY_SIZE(scratch->digits);
+ scratch->big.sign = MP_ZPOS;
+
+ isl_siomath_ulong_to_digits(arg, scratch->digits, &scratch->big.used);
+ return &scratch->big;
+}
+
+/* Ensure big representation. Does not preserve the current number.
+ * Callers may use the fact that the value _is_ preserved if the presentation
+ * was big before.
+ */
+inline mp_int isl_sioimath_reinit_big(isl_sioimath_ptr ptr)
+{
+ if (isl_sioimath_is_small(*ptr))
+ *ptr = isl_sioimath_encode_big(mp_int_alloc());
+ return isl_sioimath_get_big(*ptr);
+}
+
+/* Set ptr to a number in small representation.
+ */
+inline void isl_sioimath_set_small(isl_sioimath_ptr ptr, int32_t val)
+{
+ if (isl_sioimath_is_big(*ptr))
+ mp_int_free(isl_sioimath_get_big(*ptr));
+ *ptr = isl_sioimath_encode_small(val);
+}
+
+/* Set ptr to val, choosing small representation if possible.
+ */
+inline void isl_sioimath_set_int32(isl_sioimath_ptr ptr, int32_t val)
+{
+ if (ISL_SIOIMATH_SMALL_MIN <= val && val <= ISL_SIOIMATH_SMALL_MAX) {
+ isl_sioimath_set_small(ptr, val);
+ return;
+ }
+
+ mp_int_init_value(isl_sioimath_reinit_big(ptr), val);
+}
+
+/* Assign an int64_t number using small representation if possible.
+ */
+inline void isl_sioimath_set_int64(isl_sioimath_ptr ptr, int64_t val)
+{
+ if (ISL_SIOIMATH_SMALL_MIN <= val && val <= ISL_SIOIMATH_SMALL_MAX) {
+ isl_sioimath_set_small(ptr, val);
+ return;
+ }
+
+ isl_sioimath_scratchspace_t scratch;
+ mp_int_copy(isl_sioimath_si64arg_src(val, &scratch),
+ isl_sioimath_reinit_big(ptr));
+}
+
+/* Convert to big representation while preserving the current number.
+ */
+inline void isl_sioimath_promote(isl_sioimath_ptr dst)
+{
+ int32_t small;
+
+ if (isl_sioimath_is_big(*dst))
+ return;
+
+ small = isl_sioimath_get_small(*dst);
+ mp_int_set_value(isl_sioimath_reinit_big(dst), small);
+}
+
+/* Convert to small representation while preserving the current number. Does
+ * nothing if dst doesn't fit small representation.
+ */
+inline void isl_sioimath_try_demote(isl_sioimath_ptr dst)
+{
+ mp_small small;
+
+ if (isl_sioimath_is_small(*dst))
+ return;
+
+ if (mp_int_to_int(isl_sioimath_get_big(*dst), &small) != MP_OK)
+ return;
+
+ if (ISL_SIOIMATH_SMALL_MIN <= small && small <= ISL_SIOIMATH_SMALL_MAX)
+ isl_sioimath_set_small(dst, small);
+}
+
+/* Initialize an isl_int. The implicit value is 0 in small representation.
+ */
+inline void isl_sioimath_init(isl_sioimath_ptr dst)
+{
+ *dst = isl_sioimath_encode_small(0);
+}
+
+/* Free the resources taken by an isl_int.
+ */
+inline void isl_sioimath_clear(isl_sioimath_ptr dst)
+{
+ if (isl_sioimath_is_small(*dst))
+ return;
+
+ mp_int_free(isl_sioimath_get_big(*dst));
+}
+
+/* Copy the value of one isl_int to another.
+ */
+inline void isl_sioimath_set(isl_sioimath_ptr dst, isl_sioimath_src val)
+{
+ if (isl_sioimath_is_small(val)) {
+ isl_sioimath_set_small(dst, isl_sioimath_get_small(val));
+ return;
+ }
+
+ mp_int_copy(isl_sioimath_get_big(val), isl_sioimath_reinit_big(dst));
+}
+
+/* Store a signed long into an isl_int.
+ */
+inline void isl_sioimath_set_si(isl_sioimath_ptr dst, long val)
+{
+ if (ISL_SIOIMATH_SMALL_MIN <= val && val <= ISL_SIOIMATH_SMALL_MAX) {
+ isl_sioimath_set_small(dst, val);
+ return;
+ }
+
+ mp_int_set_value(isl_sioimath_reinit_big(dst), val);
+}
+
+/* Store an unsigned long into an isl_int.
+ */
+inline void isl_sioimath_set_ui(isl_sioimath_ptr dst, unsigned long val)
+{
+ if (val <= ISL_SIOIMATH_SMALL_MAX) {
+ isl_sioimath_set_small(dst, val);
+ return;
+ }
+
+ mp_int_set_uvalue(isl_sioimath_reinit_big(dst), val);
+}
+
+/* Return whether a number can be represented by a signed long.
+ */
+inline int isl_sioimath_fits_slong(isl_sioimath_src val)
+{
+ mp_small dummy;
+
+ if (isl_sioimath_is_small(val))
+ return 1;
+
+ return mp_int_to_int(isl_sioimath_get_big(val), &dummy) == MP_OK;
+}
+
+/* Return a number as signed long. Result is undefined if the number cannot be
+ * represented as long.
+ */
+inline long isl_sioimath_get_si(isl_sioimath_src val)
+{
+ mp_small result;
+
+ if (isl_sioimath_is_small(val))
+ return isl_sioimath_get_small(val);
+
+ mp_int_to_int(isl_sioimath_get_big(val), &result);
+ return result;
+}
+
+/* Return whether a number can be represented as unsigned long.
+ */
+inline int isl_sioimath_fits_ulong(isl_sioimath_src val)
+{
+ mp_usmall dummy;
+
+ if (isl_sioimath_is_small(val))
+ return isl_sioimath_get_small(val) >= 0;
+
+ return mp_int_to_uint(isl_sioimath_get_big(val), &dummy) == MP_OK;
+}
+
+/* Return a number as unsigned long. Result is undefined if the number cannot be
+ * represented as unsigned long.
+ */
+inline unsigned long isl_sioimath_get_ui(isl_sioimath_src val)
+{
+ mp_usmall result;
+
+ if (isl_sioimath_is_small(val))
+ return isl_sioimath_get_small(val);
+
+ mp_int_to_uint(isl_sioimath_get_big(val), &result);
+ return result;
+}
+
+/* Return a number as floating point value.
+ */
+inline double isl_sioimath_get_d(isl_sioimath_src val)
+{
+ mp_int big;
+ double result = 0;
+ int i;
+
+ if (isl_sioimath_is_small(val))
+ return isl_sioimath_get_small(val);
+
+ big = isl_sioimath_get_big(val);
+ for (i = 0; i < big->used; ++i)
+ result = result * (double) ((uintmax_t) MP_DIGIT_MAX + 1) +
+ (double) big->digits[i];
+
+ if (big->sign == MP_NEG)
+ result = -result;
+
+ return result;
+}
+
+/* Format a number as decimal string.
+ *
+ * The largest possible string from small representation is 12 characters
+ * ("-2147483647").
+ */
+inline char *isl_sioimath_get_str(isl_sioimath_src val)
+{
+ char *result;
+
+ if (isl_sioimath_is_small(val)) {
+ result = malloc(12);
+ snprintf(result, 12, "%" PRIi32, isl_sioimath_get_small(val));
+ return result;
+ }
+
+ return impz_get_str(NULL, 10, isl_sioimath_get_big(val));
+}
+
+/* Return the absolute value.
+ */
+inline void isl_sioimath_abs(isl_sioimath_ptr dst, isl_sioimath_src arg)
+{
+ if (isl_sioimath_is_small(arg)) {
+ isl_sioimath_set_small(dst, labs(isl_sioimath_get_small(arg)));
+ return;
+ }
+
+ mp_int_abs(isl_sioimath_get_big(arg), isl_sioimath_reinit_big(dst));
+}
+
+/* Return the negation of a number.
+ */
+inline void isl_sioimath_neg(isl_sioimath_ptr dst, isl_sioimath_src arg)
+{
+ if (isl_sioimath_is_small(arg)) {
+ isl_sioimath_set_small(dst, -isl_sioimath_get_small(arg));
+ return;
+ }
+
+ mp_int_neg(isl_sioimath_get_big(arg), isl_sioimath_reinit_big(dst));
+}
+
+/* Swap two isl_ints.
+ *
+ * isl_sioimath can be copied bytewise; nothing depends on its address. It can
+ * also be stored in a CPU register.
+ */
+inline void isl_sioimath_swap(isl_sioimath_ptr lhs, isl_sioimath_ptr rhs)
+{
+ isl_sioimath tmp = *lhs;
+ *lhs = *rhs;
+ *rhs = tmp;
+}
+
+/* Add an unsigned long to the number.
+ *
+ * On LP64 unsigned long exceeds the range of an int64_t, therefore we check in
+ * advance whether small representation possibly overflows.
+ */
+inline void isl_sioimath_add_ui(isl_sioimath_ptr dst, isl_sioimath lhs,
+ unsigned long rhs)
+{
+ int32_t smalllhs;
+ isl_sioimath_scratchspace_t lhsscratch;
+
+ if (isl_sioimath_decode_small(lhs, &smalllhs) &&
+ (rhs <= (uint64_t) INT64_MAX - (uint64_t) ISL_SIOIMATH_SMALL_MAX)) {
+ isl_sioimath_set_int64(dst, (int64_t) smalllhs + rhs);
+ return;
+ }
+
+ impz_add_ui(isl_sioimath_reinit_big(dst),
+ isl_sioimath_bigarg_src(lhs, &lhsscratch), rhs);
+ isl_sioimath_try_demote(dst);
+}
+
+/* Subtract an unsigned long.
+ *
+ * On LP64 unsigned long exceeds the range of an int64_t. If
+ * ISL_SIOIMATH_SMALL_MIN-rhs>=INT64_MIN we can do the calculation using int64_t
+ * without risking an overflow.
+ */
+inline void isl_sioimath_sub_ui(isl_sioimath_ptr dst, isl_sioimath lhs,
+ unsigned long rhs)
+{
+ int32_t smalllhs;
+ isl_sioimath_scratchspace_t lhsscratch;
+
+ if (isl_sioimath_decode_small(lhs, &smalllhs) &&
+ (rhs < (uint64_t) INT64_MIN - (uint64_t) ISL_SIOIMATH_SMALL_MIN)) {
+ isl_sioimath_set_int64(dst, (int64_t) smalllhs - rhs);
+ return;
+ }
+
+ impz_sub_ui(isl_sioimath_reinit_big(dst),
+ isl_sioimath_bigarg_src(lhs, &lhsscratch), rhs);
+ isl_sioimath_try_demote(dst);
+}
+
+/* Sum of two isl_ints.
+ */
+inline void isl_sioimath_add(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ isl_sioimath_scratchspace_t scratchlhs, scratchrhs;
+ int32_t smalllhs, smallrhs;
+
+ if (isl_sioimath_decode_small(lhs, &smalllhs) &&
+ isl_sioimath_decode_small(rhs, &smallrhs)) {
+ isl_sioimath_set_int64(
+ dst, (int64_t) smalllhs + (int64_t) smallrhs);
+ return;
+ }
+
+ mp_int_add(isl_sioimath_bigarg_src(lhs, &scratchlhs),
+ isl_sioimath_bigarg_src(rhs, &scratchrhs),
+ isl_sioimath_reinit_big(dst));
+ isl_sioimath_try_demote(dst);
+}
+
+/* Subtract two isl_ints.
+ */
+inline void isl_sioimath_sub(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ isl_sioimath_scratchspace_t scratchlhs, scratchrhs;
+ int32_t smalllhs, smallrhs;
+
+ if (isl_sioimath_decode_small(lhs, &smalllhs) &&
+ isl_sioimath_decode_small(rhs, &smallrhs)) {
+ isl_sioimath_set_int64(
+ dst, (int64_t) smalllhs - (int64_t) smallrhs);
+ return;
+ }
+
+ mp_int_sub(isl_sioimath_bigarg_src(lhs, &scratchlhs),
+ isl_sioimath_bigarg_src(rhs, &scratchrhs),
+ isl_sioimath_reinit_big(dst));
+ isl_sioimath_try_demote(dst);
+}
+
+/* Multiply two isl_ints.
+ */
+inline void isl_sioimath_mul(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ isl_sioimath_scratchspace_t scratchlhs, scratchrhs;
+ int32_t smalllhs, smallrhs;
+
+ if (isl_sioimath_decode_small(lhs, &smalllhs) &&
+ isl_sioimath_decode_small(rhs, &smallrhs)) {
+ isl_sioimath_set_int64(
+ dst, (int64_t) smalllhs * (int64_t) smallrhs);
+ return;
+ }
+
+ mp_int_mul(isl_sioimath_bigarg_src(lhs, &scratchlhs),
+ isl_sioimath_bigarg_src(rhs, &scratchrhs),
+ isl_sioimath_reinit_big(dst));
+ isl_sioimath_try_demote(dst);
+}
+
+/* Shift lhs by rhs bits to the left and store the result in dst. Effectively,
+ * this operation computes 'lhs * 2^rhs'.
+ */
+inline void isl_sioimath_mul_2exp(isl_sioimath_ptr dst, isl_sioimath lhs,
+ unsigned long rhs)
+{
+ isl_sioimath_scratchspace_t scratchlhs;
+ int32_t smalllhs;
+
+ if (isl_sioimath_decode_small(lhs, &smalllhs) && (rhs <= 32ul)) {
+ isl_sioimath_set_int64(dst, ((int64_t) smalllhs) << rhs);
+ return;
+ }
+
+ mp_int_mul_pow2(isl_sioimath_bigarg_src(lhs, &scratchlhs), rhs,
+ isl_sioimath_reinit_big(dst));
+}
+
+/* Multiply an isl_int and a signed long.
+ */
+inline void isl_sioimath_mul_si(isl_sioimath_ptr dst, isl_sioimath lhs,
+ signed long rhs)
+{
+ isl_sioimath_scratchspace_t scratchlhs, scratchrhs;
+ int32_t smalllhs;
+
+ if (isl_sioimath_decode_small(lhs, &smalllhs) && (rhs > LONG_MIN) &&
+ (labs(rhs) <= UINT32_MAX)) {
+ isl_sioimath_set_int64(dst, (int64_t) smalllhs * (int64_t) rhs);
+ return;
+ }
+
+ mp_int_mul(isl_sioimath_bigarg_src(lhs, &scratchlhs),
+ isl_sioimath_siarg_src(rhs, &scratchrhs),
+ isl_sioimath_reinit_big(dst));
+ isl_sioimath_try_demote(dst);
+}
+
+/* Multiply an isl_int and an unsigned long.
+ */
+inline void isl_sioimath_mul_ui(isl_sioimath_ptr dst, isl_sioimath lhs,
+ unsigned long rhs)
+{
+ isl_sioimath_scratchspace_t scratchlhs, scratchrhs;
+ int32_t smalllhs;
+
+ if (isl_sioimath_decode_small(lhs, &smalllhs) && (rhs <= UINT32_MAX)) {
+ isl_sioimath_set_int64(dst, (int64_t) smalllhs * (int64_t) rhs);
+ return;
+ }
+
+ mp_int_mul(isl_sioimath_bigarg_src(lhs, &scratchlhs),
+ isl_sioimath_uiarg_src(rhs, &scratchrhs),
+ isl_sioimath_reinit_big(dst));
+ isl_sioimath_try_demote(dst);
+}
+
+/* Compute the power of an isl_int to an unsigned long.
+ * Always let IMath do it; the result is unlikely to be small except in some
+ * special cases.
+ * Note: 0^0 == 1
+ */
+inline void isl_sioimath_pow_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs)
+{
+ isl_sioimath_scratchspace_t scratchlhs, scratchrhs;
+ int32_t smalllhs;
+
+ switch (rhs) {
+ case 0:
+ isl_sioimath_set_small(dst, 1);
+ return;
+ case 1:
+ isl_sioimath_set(dst, lhs);
+ return;
+ case 2:
+ isl_sioimath_mul(dst, lhs, lhs);
+ return;
+ }
+
+ if (isl_sioimath_decode_small(lhs, &smalllhs)) {
+ switch (smalllhs) {
+ case 0:
+ isl_sioimath_set_small(dst, 0);
+ return;
+ case 1:
+ isl_sioimath_set_small(dst, 1);
+ return;
+ case 2:
+ isl_sioimath_set_small(dst, 1);
+ isl_sioimath_mul_2exp(dst, *dst, rhs);
+ return;
+ default:
+ if ((MP_SMALL_MIN <= rhs) && (rhs <= MP_SMALL_MAX)) {
+ mp_int_expt_value(smalllhs, rhs,
+ isl_sioimath_reinit_big(dst));
+ isl_sioimath_try_demote(dst);
+ return;
+ }
+ }
+ }
+
+ mp_int_expt_full(isl_sioimath_bigarg_src(lhs, &scratchlhs),
+ isl_sioimath_uiarg_src(rhs, &scratchrhs),
+ isl_sioimath_reinit_big(dst));
+ isl_sioimath_try_demote(dst);
+}
+
+/* Fused multiply-add.
+ */
+inline void isl_sioimath_addmul(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ isl_sioimath tmp;
+ isl_sioimath_init(&tmp);
+ isl_sioimath_mul(&tmp, lhs, rhs);
+ isl_sioimath_add(dst, *dst, tmp);
+ isl_sioimath_clear(&tmp);
+}
+
+/* Fused multiply-add with an unsigned long.
+ */
+inline void isl_sioimath_addmul_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs)
+{
+ isl_sioimath tmp;
+ isl_sioimath_init(&tmp);
+ isl_sioimath_mul_ui(&tmp, lhs, rhs);
+ isl_sioimath_add(dst, *dst, tmp);
+ isl_sioimath_clear(&tmp);
+}
+
+/* Fused multiply-subtract.
+ */
+inline void isl_sioimath_submul(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ isl_sioimath tmp;
+ isl_sioimath_init(&tmp);
+ isl_sioimath_mul(&tmp, lhs, rhs);
+ isl_sioimath_sub(dst, *dst, tmp);
+ isl_sioimath_clear(&tmp);
+}
+
+/* Fused multiply-add with an unsigned long.
+ */
+inline void isl_sioimath_submul_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs)
+{
+ isl_sioimath tmp;
+ isl_sioimath_init(&tmp);
+ isl_sioimath_mul_ui(&tmp, lhs, rhs);
+ isl_sioimath_sub(dst, *dst, tmp);
+ isl_sioimath_clear(&tmp);
+}
+
+void isl_sioimath_gcd(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+void isl_sioimath_lcm(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs);
+
+/* Divide lhs by rhs, rounding to zero (Truncate).
+ */
+inline void isl_sioimath_tdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ isl_sioimath_scratchspace_t lhsscratch, rhsscratch;
+ int32_t lhssmall, rhssmall;
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall) &&
+ isl_sioimath_decode_small(rhs, &rhssmall)) {
+ isl_sioimath_set_small(dst, lhssmall / rhssmall);
+ return;
+ }
+
+ mp_int_div(isl_sioimath_bigarg_src(lhs, &lhsscratch),
+ isl_sioimath_bigarg_src(rhs, &rhsscratch),
+ isl_sioimath_reinit_big(dst), NULL);
+ isl_sioimath_try_demote(dst);
+}
+
+/* Divide lhs by an unsigned long rhs, rounding to zero (Truncate).
+ */
+inline void isl_sioimath_tdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs)
+{
+ isl_sioimath_scratchspace_t lhsscratch, rhsscratch;
+ int32_t lhssmall;
+
+ if (isl_sioimath_is_small(lhs) && (rhs <= (unsigned long) INT32_MAX)) {
+ lhssmall = isl_sioimath_get_small(lhs);
+ isl_sioimath_set_small(dst, lhssmall / (int32_t) rhs);
+ return;
+ }
+
+ if (rhs <= MP_SMALL_MAX) {
+ mp_int_div_value(isl_sioimath_bigarg_src(lhs, &lhsscratch), rhs,
+ isl_sioimath_reinit_big(dst), NULL);
+ isl_sioimath_try_demote(dst);
+ return;
+ }
+
+ mp_int_div(isl_sioimath_bigarg_src(lhs, &lhsscratch),
+ isl_sioimath_uiarg_src(rhs, &rhsscratch),
+ isl_sioimath_reinit_big(dst), NULL);
+ isl_sioimath_try_demote(dst);
+}
+
+/* Divide lhs by rhs, rounding to positive infinity (Ceil).
+ */
+inline void isl_sioimath_cdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ int32_t lhssmall, rhssmall;
+ isl_sioimath_scratchspace_t lhsscratch, rhsscratch;
+ int32_t q;
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall) &&
+ isl_sioimath_decode_small(rhs, &rhssmall)) {
+ if ((lhssmall >= 0) && (rhssmall >= 0))
+ q = ((int64_t) lhssmall + (int64_t) rhssmall - 1) /
+ rhssmall;
+ else if ((lhssmall < 0) && (rhssmall < 0))
+ q = ((int64_t) lhssmall + (int64_t) rhssmall + 1) /
+ rhssmall;
+ else
+ q = lhssmall / rhssmall;
+ isl_sioimath_set_small(dst, q);
+ return;
+ }
+
+ impz_cdiv_q(isl_sioimath_reinit_big(dst),
+ isl_sioimath_bigarg_src(lhs, &lhsscratch),
+ isl_sioimath_bigarg_src(rhs, &rhsscratch));
+ isl_sioimath_try_demote(dst);
+}
+
+/* Compute the division of lhs by a rhs of type unsigned long, rounding towards
+ * positive infinity (Ceil).
+ */
+inline void isl_sioimath_cdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs)
+{
+ isl_sioimath_scratchspace_t lhsscratch, rhsscratch;
+ int32_t lhssmall, q;
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall) && (rhs <= INT32_MAX)) {
+ if (lhssmall >= 0)
+ q = ((int64_t) lhssmall + ((int64_t) rhs - 1)) /
+ (int64_t) rhs;
+ else
+ q = lhssmall / (int32_t) rhs;
+ isl_sioimath_set_small(dst, q);
+ return;
+ }
+
+ impz_cdiv_q(isl_sioimath_reinit_big(dst),
+ isl_sioimath_bigarg_src(lhs, &lhsscratch),
+ isl_sioimath_uiarg_src(rhs, &rhsscratch));
+ isl_sioimath_try_demote(dst);
+}
+
+/* Divide lhs by rhs, rounding to negative infinity (Floor).
+ */
+inline void isl_sioimath_fdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ isl_sioimath_scratchspace_t lhsscratch, rhsscratch;
+ int32_t lhssmall, rhssmall;
+ int32_t q;
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall) &&
+ isl_sioimath_decode_small(rhs, &rhssmall)) {
+ if ((lhssmall < 0) && (rhssmall >= 0))
+ q = ((int64_t) lhssmall - ((int64_t) rhssmall - 1)) /
+ rhssmall;
+ else if ((lhssmall >= 0) && (rhssmall < 0))
+ q = ((int64_t) lhssmall - ((int64_t) rhssmall + 1)) /
+ rhssmall;
+ else
+ q = lhssmall / rhssmall;
+ isl_sioimath_set_small(dst, q);
+ return;
+ }
+
+ impz_fdiv_q(isl_sioimath_reinit_big(dst),
+ isl_sioimath_bigarg_src(lhs, &lhsscratch),
+ isl_sioimath_bigarg_src(rhs, &rhsscratch));
+ isl_sioimath_try_demote(dst);
+}
+
+/* Compute the division of lhs by a rhs of type unsigned long, rounding towards
+ * negative infinity (Floor).
+ */
+inline void isl_sioimath_fdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ unsigned long rhs)
+{
+ isl_sioimath_scratchspace_t lhsscratch, rhsscratch;
+ int32_t lhssmall, q;
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall) && (rhs <= INT32_MAX)) {
+ if (lhssmall >= 0)
+ q = (uint32_t) lhssmall / rhs;
+ else
+ q = ((int64_t) lhssmall - ((int64_t) rhs - 1)) /
+ (int64_t) rhs;
+ isl_sioimath_set_small(dst, q);
+ return;
+ }
+
+ impz_fdiv_q(isl_sioimath_reinit_big(dst),
+ isl_sioimath_bigarg_src(lhs, &lhsscratch),
+ isl_sioimath_uiarg_src(rhs, &rhsscratch));
+ isl_sioimath_try_demote(dst);
+}
+
+/* Get the remainder of: lhs divided by rhs rounded towards negative infinite
+ * (Floor).
+ */
+inline void isl_sioimath_fdiv_r(isl_sioimath_ptr dst, isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ isl_sioimath_scratchspace_t lhsscratch, rhsscratch;
+ int64_t lhssmall, rhssmall;
+ int32_t r;
+
+ if (isl_sioimath_is_small(lhs) && isl_sioimath_is_small(rhs)) {
+ lhssmall = isl_sioimath_get_small(lhs);
+ rhssmall = isl_sioimath_get_small(rhs);
+ r = (rhssmall + lhssmall % rhssmall) % rhssmall;
+ isl_sioimath_set_small(dst, r);
+ return;
+ }
+
+ impz_fdiv_r(isl_sioimath_reinit_big(dst),
+ isl_sioimath_bigarg_src(lhs, &lhsscratch),
+ isl_sioimath_bigarg_src(rhs, &rhsscratch));
+ isl_sioimath_try_demote(dst);
+}
+
+void isl_sioimath_read(isl_sioimath_ptr dst, const char *str);
+
+/* Return:
+ * +1 for a positive number
+ * -1 for a negative number
+ * 0 if the number is zero
+ */
+inline int isl_sioimath_sgn(isl_sioimath_src arg)
+{
+ int32_t small;
+
+ if (isl_sioimath_decode_small(arg, &small))
+ return (small > 0) - (small < 0);
+
+ return mp_int_compare_zero(isl_sioimath_get_big(arg));
+}
+
+/* Return:
+ * +1 if lhs > rhs
+ * -1 if lhs < rhs
+ * 0 if lhs = rhs
+ */
+inline int isl_sioimath_cmp(isl_sioimath_src lhs, isl_sioimath_src rhs)
+{
+ isl_sioimath_scratchspace_t lhsscratch, rhsscratch;
+ int32_t lhssmall, rhssmall;
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall) &&
+ isl_sioimath_decode_small(rhs, &rhssmall))
+ return (lhssmall > rhssmall) - (lhssmall < rhssmall);
+
+ if (isl_sioimath_decode_small(rhs, &rhssmall))
+ return mp_int_compare_value(
+ isl_sioimath_bigarg_src(lhs, &lhsscratch), rhssmall);
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall))
+ return -mp_int_compare_value(
+ isl_sioimath_bigarg_src(rhs, &rhsscratch), lhssmall);
+
+ return mp_int_compare(
+ isl_sioimath_get_big(lhs), isl_sioimath_get_big(rhs));
+}
+
+/* As isl_sioimath_cmp, but with signed long rhs.
+ */
+inline int isl_sioimath_cmp_si(isl_sioimath_src lhs, signed long rhs)
+{
+ int32_t lhssmall;
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall))
+ return (lhssmall > rhs) - (lhssmall < rhs);
+
+ return mp_int_compare_value(isl_sioimath_get_big(lhs), rhs);
+}
+
+/* Return:
+ * +1 if |lhs| > |rhs|
+ * -1 if |lhs| < |rhs|
+ * 0 if |lhs| = |rhs|
+ */
+inline int isl_sioimath_abs_cmp(isl_sioimath_src lhs, isl_sioimath_src rhs)
+{
+ isl_sioimath_scratchspace_t lhsscratch, rhsscratch;
+ int32_t lhssmall, rhssmall;
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall) &&
+ isl_sioimath_decode_small(rhs, &rhssmall)) {
+ lhssmall = labs(lhssmall);
+ rhssmall = labs(rhssmall);
+ return (lhssmall > rhssmall) - (lhssmall < rhssmall);
+ }
+
+ return mp_int_compare_unsigned(
+ isl_sioimath_bigarg_src(lhs, &lhsscratch),
+ isl_sioimath_bigarg_src(rhs, &rhsscratch));
+}
+
+/* Return whether lhs is divisible by rhs.
+ * In particular, can rhs be multiplied by some integer to result in lhs?
+ * If rhs is zero, then this means lhs has to be zero too.
+ */
+inline int isl_sioimath_is_divisible_by(isl_sioimath_src lhs,
+ isl_sioimath_src rhs)
+{
+ isl_sioimath_scratchspace_t lhsscratch, rhsscratch;
+ int32_t lhssmall, rhssmall;
+ mpz_t rem;
+ int cmp;
+
+ if (isl_sioimath_sgn(rhs) == 0)
+ return isl_sioimath_sgn(lhs) == 0;
+
+ if (isl_sioimath_decode_small(lhs, &lhssmall) &&
+ isl_sioimath_decode_small(rhs, &rhssmall))
+ return lhssmall % rhssmall == 0;
+
+ if (isl_sioimath_decode_small(rhs, &rhssmall))
+ return mp_int_divisible_value(
+ isl_sioimath_bigarg_src(lhs, &lhsscratch), rhssmall);
+
+ mp_int_init(&rem);
+ mp_int_div(isl_sioimath_bigarg_src(lhs, &lhsscratch),
+ isl_sioimath_bigarg_src(rhs, &rhsscratch), NULL, &rem);
+ cmp = mp_int_compare_zero(&rem);
+ mp_int_clear(&rem);
+ return cmp == 0;
+}
+
+/* Return a hash code of an isl_sioimath.
+ * The hash code for a number in small and big representation must be identical
+ * on the same machine because small representation if not obligatory if fits.
+ */
+inline uint32_t isl_sioimath_hash(isl_sioimath_src arg, uint32_t hash)
+{
+ int32_t small;
+ int i;
+ uint32_t num;
+ mp_digit digits[(sizeof(uint32_t) + sizeof(mp_digit) - 1) /
+ sizeof(mp_digit)];
+ mp_size used;
+ const unsigned char *digitdata = (const unsigned char *) &digits;
+
+ if (isl_sioimath_decode_small(arg, &small)) {
+ if (small < 0)
+ isl_hash_byte(hash, 0xFF);
+ num = labs(small);
+
+ isl_siomath_uint32_to_digits(num, digits, &used);
+ for (i = 0; i < used * sizeof(mp_digit); i += 1)
+ isl_hash_byte(hash, digitdata[i]);
+ return hash;
+ }
+
+ return isl_imath_hash(isl_sioimath_get_big(arg), hash);
+}
+
+/* Return the number of digits in a number of the given base or more, i.e. the
+ * string length without sign and null terminator.
+ *
+ * Current implementation for small representation returns the maximal number
+ * of binary digits in that representation, which can be much larger than the
+ * smallest possible solution.
+ */
+inline size_t isl_sioimath_sizeinbase(isl_sioimath_src arg, int base)
+{
+ int32_t small;
+
+ if (isl_sioimath_decode_small(arg, &small))
+ return sizeof(int32_t) * CHAR_BIT - 1;
+
+ return impz_sizeinbase(isl_sioimath_get_big(arg), base);
+}
+
+void isl_sioimath_print(FILE *out, isl_sioimath_src i, int width);
+void isl_sioimath_dump(isl_sioimath_src arg);
+
+typedef isl_sioimath isl_int[1];
+#define isl_int_init(i) isl_sioimath_init((i))
+#define isl_int_clear(i) isl_sioimath_clear((i))
+
+#define isl_int_set(r, i) isl_sioimath_set((r), *(i))
+#define isl_int_set_si(r, i) isl_sioimath_set_si((r), i)
+#define isl_int_set_ui(r, i) isl_sioimath_set_ui((r), i)
+#define isl_int_fits_slong(r) isl_sioimath_fits_slong(*(r))
+#define isl_int_get_si(r) isl_sioimath_get_si(*(r))
+#define isl_int_fits_ulong(r) isl_sioimath_fits_ulong(*(r))
+#define isl_int_get_ui(r) isl_sioimath_get_ui(*(r))
+#define isl_int_get_d(r) isl_sioimath_get_d(*(r))
+#define isl_int_get_str(r) isl_sioimath_get_str(*(r))
+#define isl_int_abs(r, i) isl_sioimath_abs((r), *(i))
+#define isl_int_neg(r, i) isl_sioimath_neg((r), *(i))
+#define isl_int_swap(i, j) isl_sioimath_swap((i), (j))
+#define isl_int_swap_or_set(i, j) isl_sioimath_swap((i), (j))
+#define isl_int_add_ui(r, i, j) isl_sioimath_add_ui((r), *(i), j)
+#define isl_int_sub_ui(r, i, j) isl_sioimath_sub_ui((r), *(i), j)
+
+#define isl_int_add(r, i, j) isl_sioimath_add((r), *(i), *(j))
+#define isl_int_sub(r, i, j) isl_sioimath_sub((r), *(i), *(j))
+#define isl_int_mul(r, i, j) isl_sioimath_mul((r), *(i), *(j))
+#define isl_int_mul_2exp(r, i, j) isl_sioimath_mul_2exp((r), *(i), j)
+#define isl_int_mul_si(r, i, j) isl_sioimath_mul_si((r), *(i), j)
+#define isl_int_mul_ui(r, i, j) isl_sioimath_mul_ui((r), *(i), j)
+#define isl_int_pow_ui(r, i, j) isl_sioimath_pow_ui((r), *(i), j)
+#define isl_int_addmul(r, i, j) isl_sioimath_addmul((r), *(i), *(j))
+#define isl_int_addmul_ui(r, i, j) isl_sioimath_addmul_ui((r), *(i), j)
+#define isl_int_submul(r, i, j) isl_sioimath_submul((r), *(i), *(j))
+#define isl_int_submul_ui(r, i, j) isl_sioimath_submul_ui((r), *(i), j)
+
+#define isl_int_gcd(r, i, j) isl_sioimath_gcd((r), *(i), *(j))
+#define isl_int_lcm(r, i, j) isl_sioimath_lcm((r), *(i), *(j))
+#define isl_int_divexact(r, i, j) isl_sioimath_tdiv_q((r), *(i), *(j))
+#define isl_int_divexact_ui(r, i, j) isl_sioimath_tdiv_q_ui((r), *(i), j)
+#define isl_int_tdiv_q(r, i, j) isl_sioimath_tdiv_q((r), *(i), *(j))
+#define isl_int_cdiv_q(r, i, j) isl_sioimath_cdiv_q((r), *(i), *(j))
+#define isl_int_cdiv_q_ui(r, i, j) isl_sioimath_cdiv_q_ui((r), *(i), j)
+#define isl_int_fdiv_q(r, i, j) isl_sioimath_fdiv_q((r), *(i), *(j))
+#define isl_int_fdiv_r(r, i, j) isl_sioimath_fdiv_r((r), *(i), *(j))
+#define isl_int_fdiv_q_ui(r, i, j) isl_sioimath_fdiv_q_ui((r), *(i), j)
+
+#define isl_int_read(r, s) isl_sioimath_read((r), s)
+#define isl_int_sgn(i) isl_sioimath_sgn(*(i))
+#define isl_int_cmp(i, j) isl_sioimath_cmp(*(i), *(j))
+#define isl_int_cmp_si(i, si) isl_sioimath_cmp_si(*(i), si)
+#define isl_int_eq(i, j) (isl_sioimath_cmp(*(i), *(j)) == 0)
+#define isl_int_ne(i, j) (isl_sioimath_cmp(*(i), *(j)) != 0)
+#define isl_int_lt(i, j) (isl_sioimath_cmp(*(i), *(j)) < 0)
+#define isl_int_le(i, j) (isl_sioimath_cmp(*(i), *(j)) <= 0)
+#define isl_int_gt(i, j) (isl_sioimath_cmp(*(i), *(j)) > 0)
+#define isl_int_ge(i, j) (isl_sioimath_cmp(*(i), *(j)) >= 0)
+#define isl_int_abs_cmp(i, j) isl_sioimath_abs_cmp(*(i), *(j))
+#define isl_int_abs_eq(i, j) (isl_sioimath_abs_cmp(*(i), *(j)) == 0)
+#define isl_int_abs_ne(i, j) (isl_sioimath_abs_cmp(*(i), *(j)) != 0)
+#define isl_int_abs_lt(i, j) (isl_sioimath_abs_cmp(*(i), *(j)) < 0)
+#define isl_int_abs_gt(i, j) (isl_sioimath_abs_cmp(*(i), *(j)) > 0)
+#define isl_int_abs_ge(i, j) (isl_sioimath_abs_cmp(*(i), *(j)) >= 0)
+#define isl_int_is_divisible_by(i, j) isl_sioimath_is_divisible_by(*(i), *(j))
+
+#define isl_int_hash(v, h) isl_sioimath_hash(*(v), h)
+#define isl_int_free_str(s) free(s)
+#define isl_int_print(out, i, width) isl_sioimath_print(out, *(i), width)
+
+#endif /* ISL_INT_SIOIMATH_H */
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_macro.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_macro.h
new file mode 100644
index 00000000000..72f258a298d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_macro.h
@@ -0,0 +1,8 @@
+#define xCAT(A,B) A ## B
+#define CAT(A,B) xCAT(A,B)
+#undef EL
+#define EL CAT(isl_,EL_BASE)
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+#define xLIST(EL) EL ## _list
+#define LIST(EL) xLIST(EL)
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_private.h
new file mode 100644
index 00000000000..006b2871e4c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_private.h
@@ -0,0 +1,10 @@
+#ifndef ISL_LIST_PRIVATE_H
+#define ISL_LIST_PRIVATE_H
+
+#include <isl/list.h>
+
+#define ISL_DECLARE_LIST_FN_PRIVATE(EL) \
+__isl_keep isl_##EL *isl_##EL##_list_peek( \
+ __isl_keep isl_##EL##_list *list, int index);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_read_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_read_templ.c
new file mode 100644
index 00000000000..d9a2c40e9f1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_read_templ.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+#include <isl/stream.h>
+
+#include <isl_list_macro.h>
+
+/* Read a list of elements of type EL from "s".
+ * The input format corresponds to the way lists are printed
+ * by isl_printer_print_list_*.
+ * In particular, the elements are separated by a comma and
+ * the entire list is surrounded by parentheses.
+ */
+static __isl_give LIST(EL) *FN(isl_stream_read,LIST(EL_BASE))(isl_stream *s)
+{
+ isl_ctx *ctx;
+ LIST(EL) *list;
+
+ if (!s)
+ return NULL;
+ ctx = isl_stream_get_ctx(s);
+ list = FN(LIST(EL),alloc)(ctx, 0);
+ if (!list)
+ return NULL;
+ if (isl_stream_eat(s, '(') < 0)
+ return FN(LIST(EL),free)(list);
+ if (isl_stream_eat_if_available(s, ')'))
+ return list;
+ do {
+ EL *el;
+
+ el = FN(isl_stream_read,EL_BASE)(s);
+ list = FN(LIST(EL),add)(list, el);
+ if (!list)
+ return NULL;
+ } while (isl_stream_eat_if_available(s, ','));
+ if (isl_stream_eat(s, ')') < 0)
+ return FN(LIST(EL),free)(list);
+ return list;
+}
+
+/* Read a list of elements of type EL from the string "str".
+ * The input format corresponds to the way lists are printed
+ * by isl_printer_print_list_*.
+ * In particular, the elements are separated by a comma and
+ * the entire list is surrounded by parentheses.
+ */
+__isl_give LIST(EL) *FN(LIST(EL),read_from_str)(isl_ctx *ctx,
+ const char *str)
+{
+ LIST(EL) *list;
+ isl_stream *s;
+
+ s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ list = FN(isl_stream_read,LIST(EL_BASE))(s);
+ isl_stream_free(s);
+ return list;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_templ.c
new file mode 100644
index 00000000000..cfb4f339779
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_templ.c
@@ -0,0 +1,691 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2011 INRIA Saclay
+ * Copyright 2012-2013 Ecole Normale Superieure
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <isl_sort.h>
+#include <isl_tarjan.h>
+#include <isl/printer.h>
+
+#include <isl_list_macro.h>
+
+#define xS(TYPE,NAME) struct TYPE ## _ ## NAME
+#define S(TYPE,NAME) xS(TYPE,NAME)
+
+isl_ctx *FN(LIST(EL),get_ctx)(__isl_keep LIST(EL) *list)
+{
+ return list ? list->ctx : NULL;
+}
+
+__isl_give LIST(EL) *FN(LIST(EL),alloc)(isl_ctx *ctx, int n)
+{
+ LIST(EL) *list;
+
+ if (n < 0)
+ isl_die(ctx, isl_error_invalid,
+ "cannot create list of negative length",
+ return NULL);
+ list = isl_alloc(ctx, LIST(EL),
+ sizeof(LIST(EL)) + (n - 1) * sizeof(struct EL *));
+ if (!list)
+ return NULL;
+
+ list->ctx = ctx;
+ isl_ctx_ref(ctx);
+ list->ref = 1;
+ list->size = n;
+ list->n = 0;
+ return list;
+}
+
+__isl_give LIST(EL) *FN(LIST(EL),copy)(__isl_keep LIST(EL) *list)
+{
+ if (!list)
+ return NULL;
+
+ list->ref++;
+ return list;
+}
+
+__isl_give LIST(EL) *FN(LIST(EL),dup)(__isl_keep LIST(EL) *list)
+{
+ int i;
+ LIST(EL) *dup;
+
+ if (!list)
+ return NULL;
+
+ dup = FN(LIST(EL),alloc)(FN(LIST(EL),get_ctx)(list), list->n);
+ if (!dup)
+ return NULL;
+ for (i = 0; i < list->n; ++i)
+ dup = FN(LIST(EL),add)(dup, FN(EL,copy)(list->p[i]));
+ return dup;
+}
+
+__isl_give LIST(EL) *FN(LIST(EL),cow)(__isl_take LIST(EL) *list)
+{
+ if (!list)
+ return NULL;
+
+ if (list->ref == 1)
+ return list;
+ list->ref--;
+ return FN(LIST(EL),dup)(list);
+}
+
+/* Make sure "list" has room for at least "n" more pieces.
+ * Always return a list with a single reference.
+ *
+ * If there is only one reference to list, we extend it in place.
+ * Otherwise, we create a new LIST(EL) and copy the elements.
+ */
+static __isl_give LIST(EL) *FN(LIST(EL),grow)(__isl_take LIST(EL) *list, int n)
+{
+ isl_ctx *ctx;
+ int i, new_size;
+ LIST(EL) *res;
+
+ if (!list)
+ return NULL;
+ if (list->ref == 1 && list->n + n <= list->size)
+ return list;
+
+ ctx = FN(LIST(EL),get_ctx)(list);
+ new_size = ((list->n + n + 1) * 3) / 2;
+ if (list->ref == 1) {
+ res = isl_realloc(ctx, list, LIST(EL),
+ sizeof(LIST(EL)) + (new_size - 1) * sizeof(EL *));
+ if (!res)
+ return FN(LIST(EL),free)(list);
+ res->size = new_size;
+ return res;
+ }
+
+ if (list->n + n <= list->size && list->size < new_size)
+ new_size = list->size;
+
+ res = FN(LIST(EL),alloc)(ctx, new_size);
+ if (!res)
+ return FN(LIST(EL),free)(list);
+
+ for (i = 0; i < list->n; ++i)
+ res = FN(LIST(EL),add)(res, FN(EL,copy)(list->p[i]));
+
+ FN(LIST(EL),free)(list);
+ return res;
+}
+
+/* Check that "index" is a valid position in "list".
+ */
+static isl_stat FN(LIST(EL),check_index)(__isl_keep LIST(EL) *list, int index)
+{
+ if (!list)
+ return isl_stat_error;
+ if (index < 0 || index >= list->n)
+ isl_die(FN(LIST(EL),get_ctx)(list), isl_error_invalid,
+ "index out of bounds", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+__isl_give LIST(EL) *FN(LIST(EL),add)(__isl_take LIST(EL) *list,
+ __isl_take struct EL *el)
+{
+ list = FN(LIST(EL),grow)(list, 1);
+ if (!list || !el)
+ goto error;
+ list->p[list->n] = el;
+ list->n++;
+ return list;
+error:
+ FN(EL,free)(el);
+ FN(LIST(EL),free)(list);
+ return NULL;
+}
+
+/* Remove the "n" elements starting at "first" from "list".
+ */
+__isl_give LIST(EL) *FN(LIST(EL),drop)(__isl_take LIST(EL) *list,
+ unsigned first, unsigned n)
+{
+ int i;
+
+ if (!list)
+ return NULL;
+ if (first + n > list->n || first + n < first)
+ isl_die(list->ctx, isl_error_invalid,
+ "index out of bounds", return FN(LIST(EL),free)(list));
+ if (n == 0)
+ return list;
+ list = FN(LIST(EL),cow)(list);
+ if (!list)
+ return NULL;
+ for (i = 0; i < n; ++i)
+ FN(EL,free)(list->p[first + i]);
+ for (i = first; i + n < list->n; ++i)
+ list->p[i] = list->p[i + n];
+ list->n -= n;
+ return list;
+}
+
+/* Remove all elements from "list".
+ */
+__isl_give LIST(EL) *FN(LIST(EL),clear)(__isl_take LIST(EL) *list)
+{
+ if (!list)
+ return NULL;
+ return FN(LIST(EL),drop)(list, 0, list->n);
+}
+
+/* Insert "el" at position "pos" in "list".
+ *
+ * If there is only one reference to "list" and if it already has space
+ * for one extra element, we insert it directly into "list".
+ * Otherwise, we create a new list consisting of "el" and copied
+ * elements from "list".
+ */
+__isl_give LIST(EL) *FN(LIST(EL),insert)(__isl_take LIST(EL) *list,
+ unsigned pos, __isl_take struct EL *el)
+{
+ int i;
+ isl_ctx *ctx;
+ LIST(EL) *res;
+
+ if (!list || !el)
+ goto error;
+ ctx = FN(LIST(EL),get_ctx)(list);
+ if (pos > list->n)
+ isl_die(ctx, isl_error_invalid,
+ "index out of bounds", goto error);
+
+ if (list->ref == 1 && list->size > list->n) {
+ for (i = list->n; i > pos; --i)
+ list->p[i] = list->p[i - 1];
+ list->n++;
+ list->p[pos] = el;
+ return list;
+ }
+
+ res = FN(LIST(EL),alloc)(ctx, list->n + 1);
+ for (i = 0; i < pos; ++i)
+ res = FN(LIST(EL),add)(res, FN(EL,copy)(list->p[i]));
+ res = FN(LIST(EL),add)(res, el);
+ for (i = pos; i < list->n; ++i)
+ res = FN(LIST(EL),add)(res, FN(EL,copy)(list->p[i]));
+ FN(LIST(EL),free)(list);
+
+ return res;
+error:
+ FN(EL,free)(el);
+ FN(LIST(EL),free)(list);
+ return NULL;
+}
+
+__isl_null LIST(EL) *FN(LIST(EL),free)(__isl_take LIST(EL) *list)
+{
+ int i;
+
+ if (!list)
+ return NULL;
+
+ if (--list->ref > 0)
+ return NULL;
+
+ isl_ctx_deref(list->ctx);
+ for (i = 0; i < list->n; ++i)
+ FN(EL,free)(list->p[i]);
+ free(list);
+
+ return NULL;
+}
+
+/* Return the number of elements in "list".
+ */
+isl_size FN(LIST(EL),size)(__isl_keep LIST(EL) *list)
+{
+ return list ? list->n : isl_size_error;
+}
+
+/* This is an alternative name for the function above.
+ */
+isl_size FN(FN(LIST(EL),n),EL_BASE)(__isl_keep LIST(EL) *list)
+{
+ return FN(LIST(EL),size)(list);
+}
+
+/* Return the element at position "index" in "list".
+ */
+__isl_keep EL *FN(LIST(EL),peek)(__isl_keep LIST(EL) *list, int index)
+{
+ if (FN(LIST(EL),check_index)(list, index) < 0)
+ return NULL;
+ return list->p[index];
+}
+
+/* Return a copy of the element at position "index" in "list".
+ */
+__isl_give EL *FN(LIST(EL),get_at)(__isl_keep LIST(EL) *list, int index)
+{
+ return FN(EL,copy)(FN(LIST(EL),peek)(list, index));
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give EL *FN(FN(LIST(EL),get),EL_BASE)(__isl_keep LIST(EL) *list,
+ int index)
+{
+ return FN(LIST(EL),get_at)(list, index);
+}
+
+/* Replace the element at position "index" in "list" by "el".
+ */
+__isl_give LIST(EL) *FN(FN(LIST(EL),set),EL_BASE)(__isl_take LIST(EL) *list,
+ int index, __isl_take EL *el)
+{
+ if (!list || !el)
+ goto error;
+ if (FN(LIST(EL),check_index)(list, index) < 0)
+ goto error;
+ if (list->p[index] == el) {
+ FN(EL,free)(el);
+ return list;
+ }
+ list = FN(LIST(EL),cow)(list);
+ if (!list)
+ goto error;
+ FN(EL,free)(list->p[index]);
+ list->p[index] = el;
+ return list;
+error:
+ FN(EL,free)(el);
+ FN(LIST(EL),free)(list);
+ return NULL;
+}
+
+/* Return the element at position "index" of "list".
+ * This may be either a copy or the element itself
+ * if there is only one reference to "list".
+ * This allows the element to be modified inplace
+ * if both the list and the element have only a single reference.
+ * The caller is not allowed to modify "list" between
+ * this call to isl_list_*_take_* and a subsequent call
+ * to isl_list_*_restore_*.
+ * The only exception is that isl_list_*_free can be called instead.
+ */
+static __isl_give EL *FN(FN(LIST(EL),take),EL_BASE)(__isl_keep LIST(EL) *list,
+ int index)
+{
+ EL *el;
+
+ if (FN(LIST(EL),check_index)(list, index) < 0)
+ return NULL;
+ if (list->ref != 1)
+ return FN(FN(LIST(EL),get),EL_BASE)(list, index);
+ el = list->p[index];
+ list->p[index] = NULL;
+ return el;
+}
+
+/* Set the element at position "index" of "list" to "el",
+ * where the position may be empty due to a previous call
+ * to isl_list_*_take_*.
+ */
+static __isl_give LIST(EL) *FN(FN(LIST(EL),restore),EL_BASE)(
+ __isl_take LIST(EL) *list, int index, __isl_take EL *el)
+{
+ return FN(FN(LIST(EL),set),EL_BASE)(list, index, el);
+}
+
+/* Swap the elements of "list" in positions "pos1" and "pos2".
+ */
+__isl_give LIST(EL) *FN(LIST(EL),swap)(__isl_take LIST(EL) *list,
+ unsigned pos1, unsigned pos2)
+{
+ EL *el1, *el2;
+
+ if (pos1 == pos2)
+ return list;
+ el1 = FN(FN(LIST(EL),take),EL_BASE)(list, pos1);
+ el2 = FN(FN(LIST(EL),take),EL_BASE)(list, pos2);
+ list = FN(FN(LIST(EL),restore),EL_BASE)(list, pos1, el2);
+ list = FN(FN(LIST(EL),restore),EL_BASE)(list, pos2, el1);
+ return list;
+}
+
+/* Reverse the elements of "list".
+ */
+__isl_give LIST(EL) *FN(LIST(EL),reverse)(__isl_take LIST(EL) *list)
+{
+ int i, n;
+
+ n = FN(LIST(EL),size)(list);
+ for (i = 0; i < n - 1 - i; ++i)
+ list = FN(LIST(EL),swap)(list, i, n - 1 - i);
+ return list;
+}
+
+isl_stat FN(LIST(EL),foreach)(__isl_keep LIST(EL) *list,
+ isl_stat (*fn)(__isl_take EL *el, void *user), void *user)
+{
+ int i;
+
+ if (!list)
+ return isl_stat_error;
+
+ for (i = 0; i < list->n; ++i) {
+ EL *el = FN(EL,copy)(list->p[i]);
+ if (!el)
+ return isl_stat_error;
+ if (fn(el, user) < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Does "test" succeed on every element of "list"?
+ */
+isl_bool FN(LIST(EL),every)(__isl_keep LIST(EL) *list,
+ isl_bool (*test)(__isl_keep EL *el, void *user), void *user)
+{
+ int i;
+
+ if (!list)
+ return isl_bool_error;
+
+ for (i = 0; i < list->n; ++i) {
+ isl_bool r;
+
+ r = test(list->p[i], user);
+ if (r < 0 || !r)
+ return r;
+ }
+
+ return isl_bool_true;
+}
+
+/* Replace each element in "list" by the result of calling "fn"
+ * on the element.
+ */
+__isl_give LIST(EL) *FN(LIST(EL),map)(__isl_keep LIST(EL) *list,
+ __isl_give EL *(*fn)(__isl_take EL *el, void *user), void *user)
+{
+ int i, n;
+
+ if (!list)
+ return NULL;
+
+ n = list->n;
+ for (i = 0; i < n; ++i) {
+ EL *el = FN(FN(LIST(EL),take),EL_BASE)(list, i);
+ if (!el)
+ return FN(LIST(EL),free)(list);
+ el = fn(el, user);
+ list = FN(FN(LIST(EL),restore),EL_BASE)(list, i, el);
+ }
+
+ return list;
+}
+
+/* Internal data structure for isl_*_list_sort.
+ *
+ * "cmp" is the original comparison function.
+ * "user" is a user provided pointer that should be passed to "cmp".
+ */
+S(LIST(EL),sort_data) {
+ int (*cmp)(__isl_keep EL *a, __isl_keep EL *b, void *user);
+ void *user;
+};
+
+/* Compare two entries of an isl_*_list based on the user provided
+ * comparison function on pairs of isl_* objects.
+ */
+static int FN(LIST(EL),cmp)(const void *a, const void *b, void *user)
+{
+ S(LIST(EL),sort_data) *data = user;
+ EL * const *el1 = a;
+ EL * const *el2 = b;
+
+ return data->cmp(*el1, *el2, data->user);
+}
+
+/* Sort the elements of "list" in ascending order according to
+ * comparison function "cmp".
+ */
+__isl_give LIST(EL) *FN(LIST(EL),sort)(__isl_take LIST(EL) *list,
+ int (*cmp)(__isl_keep EL *a, __isl_keep EL *b, void *user), void *user)
+{
+ S(LIST(EL),sort_data) data = { cmp, user };
+
+ if (!list)
+ return NULL;
+ if (list->n <= 1)
+ return list;
+ list = FN(LIST(EL),cow)(list);
+ if (!list)
+ return NULL;
+
+ if (isl_sort(list->p, list->n, sizeof(list->p[0]),
+ &FN(LIST(EL),cmp), &data) < 0)
+ return FN(LIST(EL),free)(list);
+
+ return list;
+}
+
+/* Internal data structure for isl_*_list_foreach_scc.
+ *
+ * "list" is the original list.
+ * "follows" is the user provided callback that defines the edges of the graph.
+ */
+S(LIST(EL),foreach_scc_data) {
+ LIST(EL) *list;
+ isl_bool (*follows)(__isl_keep EL *a, __isl_keep EL *b, void *user);
+ void *follows_user;
+};
+
+/* Does element i of data->list follow element j?
+ *
+ * Use the user provided callback to find out.
+ */
+static isl_bool FN(LIST(EL),follows)(int i, int j, void *user)
+{
+ S(LIST(EL),foreach_scc_data) *data = user;
+
+ return data->follows(data->list->p[i], data->list->p[j],
+ data->follows_user);
+}
+
+/* Call "fn" on the sublist of "list" that consists of the elements
+ * with indices specified by the "n" elements of "pos".
+ */
+static isl_stat FN(LIST(EL),call_on_scc)(__isl_keep LIST(EL) *list, int *pos,
+ int n, isl_stat (*fn)(__isl_take LIST(EL) *scc, void *user), void *user)
+{
+ int i;
+ isl_ctx *ctx;
+ LIST(EL) *slice;
+
+ ctx = FN(LIST(EL),get_ctx)(list);
+ slice = FN(LIST(EL),alloc)(ctx, n);
+ for (i = 0; i < n; ++i) {
+ EL *el;
+
+ el = FN(EL,copy)(list->p[pos[i]]);
+ slice = FN(LIST(EL),add)(slice, el);
+ }
+
+ return fn(slice, user);
+}
+
+/* Call "fn" on each of the strongly connected components (SCCs) of
+ * the graph with as vertices the elements of "list" and
+ * a directed edge from node b to node a iff follows(a, b)
+ * returns 1. follows should return -1 on error.
+ *
+ * If SCC a contains a node i that follows a node j in another SCC b
+ * (i.e., follows(i, j, user) returns 1), then fn will be called on SCC a
+ * after being called on SCC b.
+ *
+ * We simply call isl_tarjan_graph_init, extract the SCCs from the result and
+ * call fn on each of them.
+ */
+isl_stat FN(LIST(EL),foreach_scc)(__isl_keep LIST(EL) *list,
+ isl_bool (*follows)(__isl_keep EL *a, __isl_keep EL *b, void *user),
+ void *follows_user,
+ isl_stat (*fn)(__isl_take LIST(EL) *scc, void *user), void *fn_user)
+{
+ S(LIST(EL),foreach_scc_data) data = { list, follows, follows_user };
+ int i, n;
+ isl_ctx *ctx;
+ struct isl_tarjan_graph *g;
+
+ if (!list)
+ return isl_stat_error;
+ if (list->n == 0)
+ return isl_stat_ok;
+ if (list->n == 1)
+ return fn(FN(LIST(EL),copy)(list), fn_user);
+
+ ctx = FN(LIST(EL),get_ctx)(list);
+ n = list->n;
+ g = isl_tarjan_graph_init(ctx, n, &FN(LIST(EL),follows), &data);
+ if (!g)
+ return isl_stat_error;
+
+ i = 0;
+ do {
+ int first;
+
+ if (g->order[i] == -1)
+ isl_die(ctx, isl_error_internal, "cannot happen",
+ break);
+ first = i;
+ while (g->order[i] != -1) {
+ ++i; --n;
+ }
+ if (first == 0 && n == 0) {
+ isl_tarjan_graph_free(g);
+ return fn(FN(LIST(EL),copy)(list), fn_user);
+ }
+ if (FN(LIST(EL),call_on_scc)(list, g->order + first, i - first,
+ fn, fn_user) < 0)
+ break;
+ ++i;
+ } while (n);
+
+ isl_tarjan_graph_free(g);
+
+ return n > 0 ? isl_stat_error : isl_stat_ok;
+}
+
+__isl_give LIST(EL) *FN(FN(LIST(EL),from),EL_BASE)(__isl_take EL *el)
+{
+ isl_ctx *ctx;
+ LIST(EL) *list;
+
+ if (!el)
+ return NULL;
+ ctx = FN(EL,get_ctx)(el);
+ list = FN(LIST(EL),alloc)(ctx, 1);
+ if (!list)
+ goto error;
+ list = FN(LIST(EL),add)(list, el);
+ return list;
+error:
+ FN(EL,free)(el);
+ return NULL;
+}
+
+/* This function performs the same operation as isl_*_list_from_*,
+ * but is considered as a function on the element when exported.
+ */
+__isl_give LIST(EL) *FN(EL,to_list)(__isl_take EL *el)
+{
+ return FN(FN(LIST(EL),from),EL_BASE)(el);
+}
+
+/* Append the elements of "list2" to "list1", where "list1" is known
+ * to have only a single reference and enough room to hold
+ * the extra elements.
+ */
+static __isl_give LIST(EL) *FN(LIST(EL),concat_inplace)(
+ __isl_take LIST(EL) *list1, __isl_take LIST(EL) *list2)
+{
+ int i;
+
+ for (i = 0; i < list2->n; ++i)
+ list1 = FN(LIST(EL),add)(list1, FN(EL,copy)(list2->p[i]));
+ FN(LIST(EL),free)(list2);
+ return list1;
+}
+
+/* Concatenate "list1" and "list2".
+ * If "list1" has only one reference and has enough room
+ * for the elements of "list2", the add the elements to "list1" itself.
+ * Otherwise, create a new list to store the result.
+ */
+__isl_give LIST(EL) *FN(LIST(EL),concat)(__isl_take LIST(EL) *list1,
+ __isl_take LIST(EL) *list2)
+{
+ int i;
+ isl_ctx *ctx;
+ LIST(EL) *res;
+
+ if (!list1 || !list2)
+ goto error;
+
+ if (list1->ref == 1 && list1->n + list2->n <= list1->size)
+ return FN(LIST(EL),concat_inplace)(list1, list2);
+
+ ctx = FN(LIST(EL),get_ctx)(list1);
+ res = FN(LIST(EL),alloc)(ctx, list1->n + list2->n);
+ for (i = 0; i < list1->n; ++i)
+ res = FN(LIST(EL),add)(res, FN(EL,copy)(list1->p[i]));
+ for (i = 0; i < list2->n; ++i)
+ res = FN(LIST(EL),add)(res, FN(EL,copy)(list2->p[i]));
+
+ FN(LIST(EL),free)(list1);
+ FN(LIST(EL),free)(list2);
+ return res;
+error:
+ FN(LIST(EL),free)(list1);
+ FN(LIST(EL),free)(list2);
+ return NULL;
+}
+
+__isl_give isl_printer *CAT(isl_printer_print_,LIST(EL_BASE))(
+ __isl_take isl_printer *p, __isl_keep LIST(EL) *list)
+{
+ int i;
+
+ if (!p || !list)
+ goto error;
+ p = isl_printer_print_str(p, "(");
+ for (i = 0; i < list->n; ++i) {
+ if (i)
+ p = isl_printer_print_str(p, ",");
+ p = CAT(isl_printer_print_,EL_BASE)(p, list->p[i]);
+ }
+ p = isl_printer_print_str(p, ")");
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+#undef BASE
+#define BASE LIST(EL_BASE)
+
+#define PRINT_DUMP_DEFAULT 0
+#include "print_templ.c"
+#undef PRINT_DUMP_DEFAULT
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_templ.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_templ.h
new file mode 100644
index 00000000000..893f9d91d4c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_list_templ.h
@@ -0,0 +1,16 @@
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+#define xLIST(EL) EL ## _list
+#define LIST(EL) xLIST(EL)
+
+struct LIST(EL) {
+ int ref;
+ isl_ctx *ctx;
+
+ int n;
+
+ size_t size;
+ struct EL *p[1];
+};
+
+__isl_give LIST(EL) *FN(LIST(EL),dup)(__isl_keep LIST(EL) *list);
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local.c
new file mode 100644
index 00000000000..e409359d28d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2011 INRIA Saclay
+ * Copyright 2014 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+#include <isl_vec_private.h>
+#include <isl_mat_private.h>
+#include <isl_reordering.h>
+#include <isl_seq.h>
+#include <isl_local_private.h>
+
+/* Return the isl_ctx to which "local" belongs.
+ */
+isl_ctx *isl_local_get_ctx(__isl_keep isl_local *local)
+{
+ if (!local)
+ return NULL;
+
+ return isl_mat_get_ctx(local);
+}
+
+/* Create an isl_local object from a matrix describing
+ * integer divisions.
+ *
+ * An isl_local object is current defined as exactly such a matrix,
+ * so simply return the input.
+ */
+__isl_give isl_local *isl_local_alloc_from_mat(__isl_take isl_mat *mat)
+{
+ return mat;
+}
+
+/* Free "local" and return NULL.
+ */
+__isl_null isl_local *isl_local_free(__isl_take isl_local *local)
+{
+ isl_mat_free(local);
+ return NULL;
+}
+
+/* Return the number of local variables (isl_dim_div),
+ * the number of other variables (isl_dim_set) or
+ * the total number of variables (isl_dim_all) in "local".
+ *
+ * Other types do not have any meaning for an isl_local object.
+ */
+isl_size isl_local_dim(__isl_keep isl_local *local, enum isl_dim_type type)
+{
+ isl_mat *mat = local;
+
+ if (!local)
+ return isl_size_error;
+ if (type == isl_dim_div)
+ return isl_mat_rows(mat);
+ if (type == isl_dim_all) {
+ isl_size cols = isl_mat_cols(mat);
+ if (cols < 0)
+ return isl_size_error;
+ return cols - 2;
+ }
+ if (type == isl_dim_set) {
+ isl_size total, n_div;
+
+ total = isl_local_dim(local, isl_dim_all);
+ n_div = isl_local_dim(local, isl_dim_div);
+ if (total < 0 || n_div < 0)
+ return isl_size_error;
+ return total - n_div;
+ }
+ isl_die(isl_local_get_ctx(local), isl_error_unsupported,
+ "unsupported dimension type", return isl_size_error);
+}
+
+#undef TYPE
+#define TYPE isl_local
+static
+#include "check_type_range_templ.c"
+
+/* Check that "pos" is a valid position for a variable in "local".
+ */
+static isl_stat isl_local_check_pos(__isl_keep isl_local *local, int pos)
+{
+ return isl_local_check_range(local, isl_dim_div, pos, 1);
+}
+
+/* Given local variables "local",
+ * is the variable at position "pos" marked as not having
+ * an explicit representation?
+ * Note that even if this variable is not marked in this way and therefore
+ * does have an explicit representation, this representation may still
+ * depend (indirectly) on other local variables that do not
+ * have an explicit representation.
+ */
+isl_bool isl_local_div_is_marked_unknown(__isl_keep isl_local *local, int pos)
+{
+ isl_mat *mat = local;
+
+ if (isl_local_check_pos(local, pos) < 0)
+ return isl_bool_error;
+ return isl_bool_ok(isl_int_is_zero(mat->row[pos][0]));
+}
+
+/* Given local variables "local",
+ * does the variable at position "pos" have a complete explicit representation?
+ * Having a complete explicit representation requires not only
+ * an explicit representation, but also that all local variables
+ * that appear in this explicit representation in turn have
+ * a complete explicit representation.
+ */
+isl_bool isl_local_div_is_known(__isl_keep isl_local *local, int pos)
+{
+ isl_bool marked;
+ int i, off;
+ isl_size n, cols;
+ isl_mat *mat = local;
+
+ if (isl_local_check_pos(local, pos) < 0)
+ return isl_bool_error;
+
+ marked = isl_local_div_is_marked_unknown(local, pos);
+ if (marked < 0 || marked)
+ return isl_bool_not(marked);
+
+ n = isl_local_dim(local, isl_dim_div);
+ cols = isl_mat_cols(mat);
+ if (n < 0 || cols < 0)
+ return isl_bool_error;
+ off = cols - n;
+
+ for (i = n - 1; i >= 0; --i) {
+ isl_bool known;
+
+ if (isl_int_is_zero(mat->row[pos][off + i]))
+ continue;
+ known = isl_local_div_is_known(local, i);
+ if (known < 0 || !known)
+ return known;
+ }
+
+ return isl_bool_true;
+}
+
+/* Does "local" have an explicit representation for all local variables?
+ */
+isl_bool isl_local_divs_known(__isl_keep isl_local *local)
+{
+ int i;
+ isl_size n;
+
+ n = isl_local_dim(local, isl_dim_div);
+ if (n < 0)
+ return isl_bool_error;
+
+ for (i = 0; i < n; ++i) {
+ isl_bool unknown = isl_local_div_is_marked_unknown(local, i);
+ if (unknown < 0 || unknown)
+ return isl_bool_not(unknown);
+ }
+
+ return isl_bool_true;
+}
+
+/* Compare two sets of local variables, defined over
+ * the same space.
+ *
+ * Return -1 if "local1" is "smaller" than "local2", 1 if "local1" is "greater"
+ * than "local2" and 0 if they are equal.
+ *
+ * The order is fairly arbitrary. We do "prefer" divs that only involve
+ * earlier dimensions in the sense that we consider matrices where
+ * the first differing div involves earlier dimensions to be smaller.
+ */
+int isl_local_cmp(__isl_keep isl_local *local1, __isl_keep isl_local *local2)
+{
+ int i;
+ int cmp;
+ isl_bool unknown1, unknown2;
+ int last1, last2;
+ isl_size n_col;
+ isl_mat *mat1 = local1;
+ isl_mat *mat2 = local2;
+
+ if (local1 == local2)
+ return 0;
+ if (!local1)
+ return -1;
+ if (!local2)
+ return 1;
+
+ if (mat1->n_row != mat2->n_row)
+ return mat1->n_row - mat2->n_row;
+
+ n_col = isl_mat_cols(mat1);
+ if (n_col < 0)
+ return -1;
+ for (i = 0; i < mat1->n_row; ++i) {
+ unknown1 = isl_local_div_is_marked_unknown(local1, i);
+ unknown2 = isl_local_div_is_marked_unknown(local2, i);
+ if (unknown1 && unknown2)
+ continue;
+ if (unknown1)
+ return 1;
+ if (unknown2)
+ return -1;
+ last1 = isl_seq_last_non_zero(mat1->row[i] + 1, n_col - 1);
+ last2 = isl_seq_last_non_zero(mat2->row[i] + 1, n_col - 1);
+ if (last1 != last2)
+ return last1 - last2;
+ cmp = isl_seq_cmp(mat1->row[i], mat2->row[i], n_col);
+ if (cmp != 0)
+ return cmp;
+ }
+
+ return 0;
+}
+
+/* Reorder the columns of the given local variables according to the
+ * given reordering.
+ * The order of the local variables themselves is assumed not to change.
+ */
+__isl_give isl_local *isl_local_reorder(__isl_take isl_local *local,
+ __isl_take isl_reordering *r)
+{
+ isl_mat *div = local;
+ int i, j;
+ isl_size dim;
+ isl_space *space;
+ isl_mat *mat;
+ int extra;
+
+ if (!local || !r)
+ goto error;
+
+ space = isl_reordering_peek_space(r);
+ dim = isl_space_dim(space, isl_dim_all);
+ if (dim < 0)
+ goto error;
+ extra = dim + div->n_row - r->len;
+ mat = isl_mat_alloc(div->ctx, div->n_row, div->n_col + extra);
+ if (!mat)
+ goto error;
+
+ for (i = 0; i < div->n_row; ++i) {
+ isl_seq_cpy(mat->row[i], div->row[i], 2);
+ isl_seq_clr(mat->row[i] + 2, mat->n_col - 2);
+ for (j = 0; j < r->len; ++j)
+ isl_int_set(mat->row[i][2 + r->pos[j]],
+ div->row[i][2 + j]);
+ }
+
+ isl_reordering_free(r);
+ isl_local_free(local);
+ return isl_local_alloc_from_mat(mat);
+error:
+ isl_reordering_free(r);
+ isl_local_free(local);
+ return NULL;
+}
+
+/* Extend a vector "v" representing an integer point
+ * in the domain space of "local"
+ * to one that also includes values for the local variables.
+ * All local variables are required to have an explicit representation.
+ * If there are no local variables, then the point is not required
+ * to be integral.
+ */
+__isl_give isl_vec *isl_local_extend_point_vec(__isl_keep isl_local *local,
+ __isl_take isl_vec *v)
+{
+ isl_size dim, n_div, size;
+ isl_bool known;
+ isl_mat *mat = local;
+
+ if (!local || !v)
+ return isl_vec_free(v);
+ known = isl_local_divs_known(local);
+ if (known < 0)
+ return isl_vec_free(v);
+ if (!known)
+ isl_die(isl_local_get_ctx(local), isl_error_invalid,
+ "unknown local variables", return isl_vec_free(v));
+ dim = isl_local_dim(local, isl_dim_set);
+ n_div = isl_local_dim(local, isl_dim_div);
+ size = isl_vec_size(v);
+ if (dim < 0 || n_div < 0 || size < 0)
+ return isl_vec_free(v);
+ if (size != 1 + dim)
+ isl_die(isl_local_get_ctx(local), isl_error_invalid,
+ "incorrect size", return isl_vec_free(v));
+ if (n_div == 0)
+ return v;
+ if (!isl_int_is_one(v->el[0]))
+ isl_die(isl_local_get_ctx(local), isl_error_invalid,
+ "expecting integer point", return isl_vec_free(v));
+ {
+ int i;
+ v = isl_vec_add_els(v, n_div);
+ if (!v)
+ return NULL;
+
+ for (i = 0; i < n_div; ++i) {
+ isl_seq_inner_product(mat->row[i] + 1, v->el,
+ 1 + dim + i, &v->el[1+dim+i]);
+ isl_int_fdiv_q(v->el[1+dim+i], v->el[1+dim+i],
+ mat->row[i][0]);
+ }
+ }
+
+ return v;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local.h
new file mode 100644
index 00000000000..a52622e4a1d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local.h
@@ -0,0 +1,23 @@
+#ifndef ISL_LOCAL_H
+#define ISL_LOCAL_H
+
+#include <isl/mat.h>
+#include <isl_reordering.h>
+
+typedef isl_mat isl_local;
+
+__isl_null isl_local *isl_local_free(__isl_take isl_local *local);
+
+isl_bool isl_local_div_is_marked_unknown(__isl_keep isl_local *local, int pos);
+isl_bool isl_local_div_is_known(__isl_keep isl_local *local, int pos);
+isl_bool isl_local_divs_known(__isl_keep isl_local *local);
+
+int isl_local_cmp(__isl_keep isl_local *local1, __isl_keep isl_local *local2);
+
+__isl_give isl_local *isl_local_reorder(__isl_take isl_local *local,
+ __isl_take isl_reordering *r);
+
+__isl_give isl_vec *isl_local_extend_point_vec(__isl_keep isl_local *local,
+ __isl_take isl_vec *v);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_private.h
new file mode 100644
index 00000000000..bb44d36b83a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_private.h
@@ -0,0 +1,8 @@
+#ifndef ISL_LOCAL_PRIVATE_H
+#define ISL_LOCAL_PRIVATE_H
+
+#include <isl_local.h>
+
+__isl_give isl_local *isl_local_alloc_from_mat(__isl_take isl_mat *mat);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_space.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_space.c
new file mode 100644
index 00000000000..1eebd447557
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_space.c
@@ -0,0 +1,1707 @@
+/*
+ * Copyright 2011 INRIA Saclay
+ * Copyright 2012-2014 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl/id.h>
+#include <isl_map_private.h>
+#include <isl_local_space_private.h>
+#include <isl_space_private.h>
+#include <isl_mat_private.h>
+#include <isl_aff_private.h>
+#include <isl_vec_private.h>
+#include <isl_point_private.h>
+#include <isl_seq.h>
+#include <isl_local.h>
+
+isl_ctx *isl_local_space_get_ctx(__isl_keep isl_local_space *ls)
+{
+ return ls ? ls->dim->ctx : NULL;
+}
+
+/* Return a hash value that digests "ls".
+ */
+uint32_t isl_local_space_get_hash(__isl_keep isl_local_space *ls)
+{
+ uint32_t hash, space_hash, div_hash;
+
+ if (!ls)
+ return 0;
+
+ hash = isl_hash_init();
+ space_hash = isl_space_get_full_hash(isl_local_space_peek_space(ls));
+ isl_hash_hash(hash, space_hash);
+ div_hash = isl_mat_get_hash(ls->div);
+ isl_hash_hash(hash, div_hash);
+
+ return hash;
+}
+
+__isl_give isl_local_space *isl_local_space_alloc_div(
+ __isl_take isl_space *space, __isl_take isl_mat *div)
+{
+ isl_ctx *ctx;
+ isl_local_space *ls = NULL;
+
+ if (!space || !div)
+ goto error;
+
+ ctx = isl_space_get_ctx(space);
+ ls = isl_calloc_type(ctx, struct isl_local_space);
+ if (!ls)
+ goto error;
+
+ ls->ref = 1;
+ ls->dim = space;
+ ls->div = div;
+
+ return ls;
+error:
+ isl_mat_free(div);
+ isl_space_free(space);
+ isl_local_space_free(ls);
+ return NULL;
+}
+
+__isl_give isl_local_space *isl_local_space_alloc(__isl_take isl_space *space,
+ unsigned n_div)
+{
+ isl_ctx *ctx;
+ isl_mat *div;
+ isl_size total;
+
+ if (!space)
+ return NULL;
+
+ total = isl_space_dim(space, isl_dim_all);
+ if (total < 0)
+ return isl_local_space_from_space(isl_space_free(space));
+
+ ctx = isl_space_get_ctx(space);
+ div = isl_mat_alloc(ctx, n_div, 1 + 1 + total + n_div);
+ return isl_local_space_alloc_div(space, div);
+}
+
+__isl_give isl_local_space *isl_local_space_from_space(
+ __isl_take isl_space *space)
+{
+ return isl_local_space_alloc(space, 0);
+}
+
+__isl_give isl_local_space *isl_local_space_copy(__isl_keep isl_local_space *ls)
+{
+ if (!ls)
+ return NULL;
+
+ ls->ref++;
+ return ls;
+}
+
+__isl_give isl_local_space *isl_local_space_dup(__isl_keep isl_local_space *ls)
+{
+ if (!ls)
+ return NULL;
+
+ return isl_local_space_alloc_div(isl_space_copy(ls->dim),
+ isl_mat_copy(ls->div));
+
+}
+
+__isl_give isl_local_space *isl_local_space_cow(__isl_take isl_local_space *ls)
+{
+ if (!ls)
+ return NULL;
+
+ if (ls->ref == 1)
+ return ls;
+ ls->ref--;
+ return isl_local_space_dup(ls);
+}
+
+__isl_null isl_local_space *isl_local_space_free(
+ __isl_take isl_local_space *ls)
+{
+ if (!ls)
+ return NULL;
+
+ if (--ls->ref > 0)
+ return NULL;
+
+ isl_space_free(ls->dim);
+ isl_mat_free(ls->div);
+
+ free(ls);
+
+ return NULL;
+}
+
+/* Is the local space that of a parameter domain?
+ */
+isl_bool isl_local_space_is_params(__isl_keep isl_local_space *ls)
+{
+ if (!ls)
+ return isl_bool_error;
+ return isl_space_is_params(ls->dim);
+}
+
+/* Is the local space that of a set?
+ */
+isl_bool isl_local_space_is_set(__isl_keep isl_local_space *ls)
+{
+ return ls ? isl_space_is_set(ls->dim) : isl_bool_error;
+}
+
+#undef TYPE
+#define TYPE isl_local_space
+
+#include "isl_type_has_equal_space_bin_templ.c"
+#include "isl_type_has_space_templ.c"
+
+/* Check that the space of "ls" is equal to "space".
+ */
+static isl_stat isl_local_space_check_has_space(__isl_keep isl_local_space *ls,
+ __isl_keep isl_space *space)
+{
+ isl_bool ok;
+
+ ok = isl_local_space_has_space(ls, space);
+ if (ok < 0)
+ return isl_stat_error;
+ if (!ok)
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "spaces don't match", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Return true if the two local spaces are identical, with identical
+ * expressions for the integer divisions.
+ */
+isl_bool isl_local_space_is_equal(__isl_keep isl_local_space *ls1,
+ __isl_keep isl_local_space *ls2)
+{
+ isl_bool equal;
+
+ equal = isl_local_space_has_equal_space(ls1, ls2);
+ if (equal < 0 || !equal)
+ return equal;
+
+ if (!isl_local_space_divs_known(ls1))
+ return isl_bool_false;
+ if (!isl_local_space_divs_known(ls2))
+ return isl_bool_false;
+
+ return isl_mat_is_equal(ls1->div, ls2->div);
+}
+
+/* Compare two isl_local_spaces.
+ *
+ * Return -1 if "ls1" is "smaller" than "ls2", 1 if "ls1" is "greater"
+ * than "ls2" and 0 if they are equal.
+ */
+int isl_local_space_cmp(__isl_keep isl_local_space *ls1,
+ __isl_keep isl_local_space *ls2)
+{
+ int cmp;
+
+ if (ls1 == ls2)
+ return 0;
+ if (!ls1)
+ return -1;
+ if (!ls2)
+ return 1;
+
+ cmp = isl_space_cmp(ls1->dim, ls2->dim);
+ if (cmp != 0)
+ return cmp;
+
+ return isl_local_cmp(ls1->div, ls2->div);
+}
+
+isl_size isl_local_space_dim(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type)
+{
+ if (!ls)
+ return isl_size_error;
+ if (type == isl_dim_div)
+ return ls->div->n_row;
+ if (type == isl_dim_all) {
+ isl_size dim = isl_space_dim(ls->dim, isl_dim_all);
+ if (dim < 0)
+ return isl_size_error;
+ return dim + ls->div->n_row;
+ }
+ return isl_space_dim(ls->dim, type);
+}
+
+#undef TYPE
+#define TYPE isl_local_space
+#include "check_type_range_templ.c"
+
+unsigned isl_local_space_offset(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type)
+{
+ isl_space *space;
+
+ if (!ls)
+ return 0;
+
+ space = ls->dim;
+ switch (type) {
+ case isl_dim_cst: return 0;
+ case isl_dim_param: return 1;
+ case isl_dim_in: return 1 + space->nparam;
+ case isl_dim_out: return 1 + space->nparam + space->n_in;
+ case isl_dim_div:
+ return 1 + space->nparam + space->n_in + space->n_out;
+ default: return 0;
+ }
+}
+
+/* Return the position of the dimension of the given type and name
+ * in "ls".
+ * Return -1 if no such dimension can be found.
+ */
+int isl_local_space_find_dim_by_name(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type, const char *name)
+{
+ if (!ls)
+ return -1;
+ if (type == isl_dim_div)
+ return -1;
+ return isl_space_find_dim_by_name(ls->dim, type, name);
+}
+
+/* Does the given dimension have a name?
+ */
+isl_bool isl_local_space_has_dim_name(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos)
+{
+ return ls ? isl_space_has_dim_name(ls->dim, type, pos) : isl_bool_error;
+}
+
+const char *isl_local_space_get_dim_name(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos)
+{
+ return ls ? isl_space_get_dim_name(ls->dim, type, pos) : NULL;
+}
+
+isl_bool isl_local_space_has_dim_id(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos)
+{
+ return ls ? isl_space_has_dim_id(ls->dim, type, pos) : isl_bool_error;
+}
+
+__isl_give isl_id *isl_local_space_get_dim_id(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos)
+{
+ return ls ? isl_space_get_dim_id(ls->dim, type, pos) : NULL;
+}
+
+/* Return the argument of the integer division at position "pos" in "ls".
+ * All local variables in "ls" are known to have a (complete) explicit
+ * representation.
+ */
+static __isl_give isl_aff *extract_div(__isl_keep isl_local_space *ls, int pos)
+{
+ isl_aff *aff;
+
+ aff = isl_aff_alloc(isl_local_space_copy(ls));
+ if (!aff)
+ return NULL;
+ isl_seq_cpy(aff->v->el, ls->div->row[pos], aff->v->size);
+ return aff;
+}
+
+/* Return the argument of the integer division at position "pos" in "ls".
+ * The integer division at that position is known to have a complete
+ * explicit representation, but some of the others do not.
+ * Remove them first because the domain of an isl_aff
+ * is not allowed to have unknown local variables.
+ */
+static __isl_give isl_aff *drop_unknown_divs_and_extract_div(
+ __isl_keep isl_local_space *ls, int pos)
+{
+ int i;
+ isl_size n;
+ isl_bool unknown;
+ isl_aff *aff;
+
+ n = isl_local_space_dim(ls, isl_dim_div);
+ if (n < 0)
+ return NULL;
+ ls = isl_local_space_copy(ls);
+ for (i = n - 1; i >= 0; --i) {
+ unknown = isl_local_space_div_is_marked_unknown(ls, i);
+ if (unknown < 0)
+ ls = isl_local_space_free(ls);
+ else if (!unknown)
+ continue;
+ ls = isl_local_space_drop_dims(ls, isl_dim_div, i, 1);
+ if (pos > i)
+ --pos;
+ }
+ aff = extract_div(ls, pos);
+ isl_local_space_free(ls);
+ return aff;
+}
+
+/* Return the argument of the integer division at position "pos" in "ls".
+ * The integer division is assumed to have a complete explicit
+ * representation. If some of the other integer divisions
+ * do not have an explicit representation, then they need
+ * to be removed first because the domain of an isl_aff
+ * is not allowed to have unknown local variables.
+ */
+__isl_give isl_aff *isl_local_space_get_div(__isl_keep isl_local_space *ls,
+ int pos)
+{
+ isl_bool known;
+
+ if (!ls)
+ return NULL;
+
+ if (pos < 0 || pos >= ls->div->n_row)
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "index out of bounds", return NULL);
+
+ known = isl_local_space_div_is_known(ls, pos);
+ if (known < 0)
+ return NULL;
+ if (!known)
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "expression of div unknown", return NULL);
+ if (!isl_local_space_is_set(ls))
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "cannot represent divs of map spaces", return NULL);
+
+ known = isl_local_space_divs_known(ls);
+ if (known < 0)
+ return NULL;
+ if (known)
+ return extract_div(ls, pos);
+ else
+ return drop_unknown_divs_and_extract_div(ls, pos);
+}
+
+/* Return the space of "ls".
+ */
+__isl_keep isl_space *isl_local_space_peek_space(__isl_keep isl_local_space *ls)
+{
+ if (!ls)
+ return NULL;
+
+ return ls->dim;
+}
+
+__isl_give isl_space *isl_local_space_get_space(__isl_keep isl_local_space *ls)
+{
+ return isl_space_copy(isl_local_space_peek_space(ls));
+}
+
+/* Return the space of "ls".
+ * This may be either a copy or the space itself
+ * if there is only one reference to "ls".
+ * This allows the space to be modified inplace
+ * if both the local space and its space have only a single reference.
+ * The caller is not allowed to modify "ls" between this call and
+ * a subsequent call to isl_local_space_restore_space.
+ * The only exception is that isl_local_space_free can be called instead.
+ */
+__isl_give isl_space *isl_local_space_take_space(__isl_keep isl_local_space *ls)
+{
+ isl_space *space;
+
+ if (!ls)
+ return NULL;
+ if (ls->ref != 1)
+ return isl_local_space_get_space(ls);
+ space = ls->dim;
+ ls->dim = NULL;
+ return space;
+}
+
+/* Set the space of "ls" to "space", where the space of "ls" may be missing
+ * due to a preceding call to isl_local_space_take_space.
+ * However, in this case, "ls" only has a single reference and
+ * then the call to isl_local_space_cow has no effect.
+ */
+__isl_give isl_local_space *isl_local_space_restore_space(
+ __isl_take isl_local_space *ls, __isl_take isl_space *space)
+{
+ if (!ls || !space)
+ goto error;
+
+ if (ls->dim == space) {
+ isl_space_free(space);
+ return ls;
+ }
+
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ goto error;
+ isl_space_free(ls->dim);
+ ls->dim = space;
+
+ return ls;
+error:
+ isl_local_space_free(ls);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Return the local variables of "ls".
+ */
+__isl_keep isl_local *isl_local_space_peek_local(__isl_keep isl_local_space *ls)
+{
+ return ls ? ls->div : NULL;
+}
+
+/* Replace the identifier of the tuple of type "type" by "id".
+ */
+__isl_give isl_local_space *isl_local_space_set_tuple_id(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, __isl_take isl_id *id)
+{
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ goto error;
+ ls->dim = isl_space_set_tuple_id(ls->dim, type, id);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+ return ls;
+error:
+ isl_id_free(id);
+ return NULL;
+}
+
+__isl_give isl_local_space *isl_local_space_set_dim_name(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+ ls->dim = isl_space_set_dim_name(ls->dim, type, pos, s);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+
+ return ls;
+}
+
+__isl_give isl_local_space *isl_local_space_set_dim_id(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id)
+{
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ goto error;
+ ls->dim = isl_space_set_dim_id(ls->dim, type, pos, id);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+
+ return ls;
+error:
+ isl_id_free(id);
+ return NULL;
+}
+
+/* Construct a zero-dimensional local space with the given parameter domain.
+ */
+__isl_give isl_local_space *isl_local_space_set_from_params(
+ __isl_take isl_local_space *ls)
+{
+ isl_space *space;
+
+ space = isl_local_space_take_space(ls);
+ space = isl_space_set_from_params(space);
+ ls = isl_local_space_restore_space(ls, space);
+
+ return ls;
+}
+
+__isl_give isl_local_space *isl_local_space_reset_space(
+ __isl_take isl_local_space *ls, __isl_take isl_space *space)
+{
+ ls = isl_local_space_cow(ls);
+ if (!ls || !space)
+ goto error;
+
+ isl_space_free(ls->dim);
+ ls->dim = space;
+
+ return ls;
+error:
+ isl_local_space_free(ls);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Reorder the dimensions of "ls" according to the given reordering.
+ * The reordering r is assumed to have been extended with the local
+ * variables, leaving them in the same order.
+ */
+__isl_give isl_local_space *isl_local_space_realign(
+ __isl_take isl_local_space *ls, __isl_take isl_reordering *r)
+{
+ ls = isl_local_space_cow(ls);
+ if (!ls || !r)
+ goto error;
+
+ ls->div = isl_local_reorder(ls->div, isl_reordering_copy(r));
+ if (!ls->div)
+ goto error;
+
+ ls = isl_local_space_reset_space(ls, isl_reordering_get_space(r));
+
+ isl_reordering_free(r);
+ return ls;
+error:
+ isl_local_space_free(ls);
+ isl_reordering_free(r);
+ return NULL;
+}
+
+__isl_give isl_local_space *isl_local_space_add_div(
+ __isl_take isl_local_space *ls, __isl_take isl_vec *div)
+{
+ ls = isl_local_space_cow(ls);
+ if (!ls || !div)
+ goto error;
+
+ if (ls->div->n_col != div->size)
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "incompatible dimensions", goto error);
+
+ ls->div = isl_mat_add_zero_cols(ls->div, 1);
+ ls->div = isl_mat_add_rows(ls->div, 1);
+ if (!ls->div)
+ goto error;
+
+ isl_seq_cpy(ls->div->row[ls->div->n_row - 1], div->el, div->size);
+ isl_int_set_si(ls->div->row[ls->div->n_row - 1][div->size], 0);
+
+ isl_vec_free(div);
+ return ls;
+error:
+ isl_local_space_free(ls);
+ isl_vec_free(div);
+ return NULL;
+}
+
+__isl_give isl_local_space *isl_local_space_replace_divs(
+ __isl_take isl_local_space *ls, __isl_take isl_mat *div)
+{
+ ls = isl_local_space_cow(ls);
+
+ if (!ls || !div)
+ goto error;
+
+ isl_mat_free(ls->div);
+ ls->div = div;
+ return ls;
+error:
+ isl_mat_free(div);
+ isl_local_space_free(ls);
+ return NULL;
+}
+
+/* Copy row "s" of "src" to row "d" of "dst", applying the expansion
+ * defined by "exp".
+ */
+static void expand_row(__isl_keep isl_mat *dst, int d,
+ __isl_keep isl_mat *src, int s, int *exp)
+{
+ int i;
+ unsigned c = src->n_col - src->n_row;
+
+ isl_seq_cpy(dst->row[d], src->row[s], c);
+ isl_seq_clr(dst->row[d] + c, dst->n_col - c);
+
+ for (i = 0; i < s; ++i)
+ isl_int_set(dst->row[d][c + exp[i]], src->row[s][c + i]);
+}
+
+/* Compare (known) divs.
+ * Return non-zero if at least one of the two divs is unknown.
+ * In particular, if both divs are unknown, we respect their
+ * current order. Otherwise, we sort the known div after the unknown
+ * div only if the known div depends on the unknown div.
+ */
+static int cmp_row(isl_int *row_i, isl_int *row_j, int i, int j,
+ unsigned n_row, unsigned n_col)
+{
+ int li, lj;
+ int unknown_i, unknown_j;
+
+ unknown_i = isl_int_is_zero(row_i[0]);
+ unknown_j = isl_int_is_zero(row_j[0]);
+
+ if (unknown_i && unknown_j)
+ return i - j;
+
+ if (unknown_i)
+ li = n_col - n_row + i;
+ else
+ li = isl_seq_last_non_zero(row_i, n_col);
+ if (unknown_j)
+ lj = n_col - n_row + j;
+ else
+ lj = isl_seq_last_non_zero(row_j, n_col);
+
+ if (li != lj)
+ return li - lj;
+
+ return isl_seq_cmp(row_i, row_j, n_col);
+}
+
+/* Call cmp_row for divs in a matrix.
+ */
+int isl_mat_cmp_div(__isl_keep isl_mat *div, int i, int j)
+{
+ return cmp_row(div->row[i], div->row[j], i, j, div->n_row, div->n_col);
+}
+
+/* Call cmp_row for divs in a basic map.
+ */
+static int bmap_cmp_row(__isl_keep isl_basic_map *bmap, int i, int j,
+ unsigned total)
+{
+ return cmp_row(bmap->div[i], bmap->div[j], i, j, bmap->n_div, total);
+}
+
+/* Sort the divs in "bmap".
+ *
+ * We first make sure divs are placed after divs on which they depend.
+ * Then we perform a simple insertion sort based on the same ordering
+ * that is used in isl_merge_divs.
+ */
+__isl_give isl_basic_map *isl_basic_map_sort_divs(
+ __isl_take isl_basic_map *bmap)
+{
+ int i, j;
+ isl_size total;
+
+ bmap = isl_basic_map_order_divs(bmap);
+ if (!bmap)
+ return NULL;
+ if (bmap->n_div <= 1)
+ return bmap;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ for (i = 1; i < bmap->n_div; ++i) {
+ for (j = i - 1; j >= 0; --j) {
+ if (bmap_cmp_row(bmap, j, j + 1, 2 + total) <= 0)
+ break;
+ bmap = isl_basic_map_swap_div(bmap, j, j + 1);
+ if (!bmap)
+ return NULL;
+ }
+ }
+
+ return bmap;
+}
+
+/* Sort the divs in the basic maps of "map".
+ */
+__isl_give isl_map *isl_map_sort_divs(__isl_take isl_map *map)
+{
+ return isl_map_inline_foreach_basic_map(map, &isl_basic_map_sort_divs);
+}
+
+/* Combine the two lists of divs into a single list.
+ * For each row i in div1, exp1[i] is set to the position of the corresponding
+ * row in the result. Similarly for div2 and exp2.
+ * This function guarantees
+ * exp1[i] >= i
+ * exp1[i+1] > exp1[i]
+ * For optimal merging, the two input list should have been sorted.
+ */
+__isl_give isl_mat *isl_merge_divs(__isl_keep isl_mat *div1,
+ __isl_keep isl_mat *div2, int *exp1, int *exp2)
+{
+ int i, j, k;
+ isl_mat *div = NULL;
+ unsigned d;
+
+ if (!div1 || !div2)
+ return NULL;
+
+ d = div1->n_col - div1->n_row;
+ div = isl_mat_alloc(div1->ctx, 1 + div1->n_row + div2->n_row,
+ d + div1->n_row + div2->n_row);
+ if (!div)
+ return NULL;
+
+ for (i = 0, j = 0, k = 0; i < div1->n_row && j < div2->n_row; ++k) {
+ int cmp;
+
+ expand_row(div, k, div1, i, exp1);
+ expand_row(div, k + 1, div2, j, exp2);
+
+ cmp = isl_mat_cmp_div(div, k, k + 1);
+ if (cmp == 0) {
+ exp1[i++] = k;
+ exp2[j++] = k;
+ } else if (cmp < 0) {
+ exp1[i++] = k;
+ } else {
+ exp2[j++] = k;
+ isl_seq_cpy(div->row[k], div->row[k + 1], div->n_col);
+ }
+ }
+ for (; i < div1->n_row; ++i, ++k) {
+ expand_row(div, k, div1, i, exp1);
+ exp1[i] = k;
+ }
+ for (; j < div2->n_row; ++j, ++k) {
+ expand_row(div, k, div2, j, exp2);
+ exp2[j] = k;
+ }
+
+ div->n_row = k;
+ div->n_col = d + k;
+
+ return div;
+}
+
+/* Swap divs "a" and "b" in "ls".
+ */
+__isl_give isl_local_space *isl_local_space_swap_div(
+ __isl_take isl_local_space *ls, int a, int b)
+{
+ int offset;
+
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+ if (a < 0 || a >= ls->div->n_row || b < 0 || b >= ls->div->n_row)
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "index out of bounds", return isl_local_space_free(ls));
+ offset = ls->div->n_col - ls->div->n_row;
+ ls->div = isl_mat_swap_cols(ls->div, offset + a, offset + b);
+ ls->div = isl_mat_swap_rows(ls->div, a, b);
+ if (!ls->div)
+ return isl_local_space_free(ls);
+ return ls;
+}
+
+/* Construct a local space that contains all the divs in either
+ * "ls1" or "ls2".
+ */
+__isl_give isl_local_space *isl_local_space_intersect(
+ __isl_take isl_local_space *ls1, __isl_take isl_local_space *ls2)
+{
+ isl_ctx *ctx;
+ int *exp1 = NULL;
+ int *exp2 = NULL;
+ isl_mat *div = NULL;
+ isl_bool equal;
+
+ if (!ls1 || !ls2)
+ goto error;
+
+ ctx = isl_local_space_get_ctx(ls1);
+ if (!isl_space_is_equal(ls1->dim, ls2->dim))
+ isl_die(ctx, isl_error_invalid,
+ "spaces should be identical", goto error);
+
+ if (ls2->div->n_row == 0) {
+ isl_local_space_free(ls2);
+ return ls1;
+ }
+
+ if (ls1->div->n_row == 0) {
+ isl_local_space_free(ls1);
+ return ls2;
+ }
+
+ exp1 = isl_alloc_array(ctx, int, ls1->div->n_row);
+ exp2 = isl_alloc_array(ctx, int, ls2->div->n_row);
+ if (!exp1 || !exp2)
+ goto error;
+
+ div = isl_merge_divs(ls1->div, ls2->div, exp1, exp2);
+ if (!div)
+ goto error;
+
+ equal = isl_mat_is_equal(ls1->div, div);
+ if (equal < 0)
+ goto error;
+ if (!equal)
+ ls1 = isl_local_space_cow(ls1);
+ if (!ls1)
+ goto error;
+
+ free(exp1);
+ free(exp2);
+ isl_local_space_free(ls2);
+ isl_mat_free(ls1->div);
+ ls1->div = div;
+
+ return ls1;
+error:
+ free(exp1);
+ free(exp2);
+ isl_mat_free(div);
+ isl_local_space_free(ls1);
+ isl_local_space_free(ls2);
+ return NULL;
+}
+
+/* Is the local variable "div" of "ls" marked as not having
+ * an explicit representation?
+ * Note that even if this variable is not marked in this way and therefore
+ * does have an explicit representation, this representation may still
+ * depend (indirectly) on other local variables that do not
+ * have an explicit representation.
+ */
+isl_bool isl_local_space_div_is_marked_unknown(__isl_keep isl_local_space *ls,
+ int div)
+{
+ if (!ls)
+ return isl_bool_error;
+ return isl_local_div_is_marked_unknown(ls->div, div);
+}
+
+/* Does "ls" have a complete explicit representation for div "div"?
+ */
+isl_bool isl_local_space_div_is_known(__isl_keep isl_local_space *ls, int div)
+{
+ if (!ls)
+ return isl_bool_error;
+ return isl_local_div_is_known(ls->div, div);
+}
+
+/* Does "ls" have an explicit representation for all local variables?
+ */
+isl_bool isl_local_space_divs_known(__isl_keep isl_local_space *ls)
+{
+ if (!ls)
+ return isl_bool_error;
+ return isl_local_divs_known(ls->div);
+}
+
+__isl_give isl_local_space *isl_local_space_domain(
+ __isl_take isl_local_space *ls)
+{
+ isl_size n_out;
+
+ n_out = isl_local_space_dim(ls, isl_dim_out);
+ if (n_out < 0)
+ return isl_local_space_free(ls);
+ ls = isl_local_space_drop_dims(ls, isl_dim_out, 0, n_out);
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+ ls->dim = isl_space_domain(ls->dim);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+ return ls;
+}
+
+__isl_give isl_local_space *isl_local_space_range(
+ __isl_take isl_local_space *ls)
+{
+ isl_size n_in;
+
+ n_in = isl_local_space_dim(ls, isl_dim_in);
+ if (n_in < 0)
+ return isl_local_space_free(ls);
+ ls = isl_local_space_drop_dims(ls, isl_dim_in, 0, n_in);
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+
+ ls->dim = isl_space_range(ls->dim);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+ return ls;
+}
+
+/* Construct a local space for a map that has the given local
+ * space as domain and that has a zero-dimensional range.
+ */
+__isl_give isl_local_space *isl_local_space_from_domain(
+ __isl_take isl_local_space *ls)
+{
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+ ls->dim = isl_space_from_domain(ls->dim);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+ return ls;
+}
+
+__isl_give isl_local_space *isl_local_space_add_dims(
+ __isl_take isl_local_space *ls, enum isl_dim_type type, unsigned n)
+{
+ isl_size pos;
+
+ pos = isl_local_space_dim(ls, type);
+ if (pos < 0)
+ return isl_local_space_free(ls);
+ return isl_local_space_insert_dims(ls, type, pos, n);
+}
+
+/* Lift the basic set "bset", living in the space of "ls"
+ * to live in a space with extra coordinates corresponding
+ * to the local variables of "ls".
+ */
+__isl_give isl_basic_set *isl_local_space_lift_basic_set(
+ __isl_take isl_local_space *ls, __isl_take isl_basic_set *bset)
+{
+ isl_size n_local;
+ isl_space *space;
+ isl_basic_set *ls_bset;
+
+ n_local = isl_local_space_dim(ls, isl_dim_div);
+ space = isl_basic_set_peek_space(bset);
+ if (n_local < 0 ||
+ isl_local_space_check_has_space(ls, space) < 0)
+ goto error;
+
+ if (n_local == 0) {
+ isl_local_space_free(ls);
+ return bset;
+ }
+
+ bset = isl_basic_set_add_dims(bset, isl_dim_set, n_local);
+ ls_bset = isl_basic_set_from_local_space(ls);
+ ls_bset = isl_basic_set_lift(ls_bset);
+ ls_bset = isl_basic_set_flatten(ls_bset);
+ bset = isl_basic_set_intersect(bset, ls_bset);
+
+ return bset;
+error:
+ isl_local_space_free(ls);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Lift the set "set", living in the space of "ls"
+ * to live in a space with extra coordinates corresponding
+ * to the local variables of "ls".
+ */
+__isl_give isl_set *isl_local_space_lift_set(__isl_take isl_local_space *ls,
+ __isl_take isl_set *set)
+{
+ isl_size n_local;
+ isl_basic_set *bset;
+
+ n_local = isl_local_space_dim(ls, isl_dim_div);
+ if (n_local < 0 ||
+ isl_local_space_check_has_space(ls, isl_set_peek_space(set)) < 0)
+ goto error;
+
+ if (n_local == 0) {
+ isl_local_space_free(ls);
+ return set;
+ }
+
+ set = isl_set_add_dims(set, isl_dim_set, n_local);
+ bset = isl_basic_set_from_local_space(ls);
+ bset = isl_basic_set_lift(bset);
+ bset = isl_basic_set_flatten(bset);
+ set = isl_set_intersect(set, isl_set_from_basic_set(bset));
+
+ return set;
+error:
+ isl_local_space_free(ls);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Remove common factor of non-constant terms and denominator.
+ */
+static __isl_give isl_local_space *normalize_div(
+ __isl_take isl_local_space *ls, int div)
+{
+ isl_ctx *ctx = ls->div->ctx;
+ unsigned total = ls->div->n_col - 2;
+
+ isl_seq_gcd(ls->div->row[div] + 2, total, &ctx->normalize_gcd);
+ isl_int_gcd(ctx->normalize_gcd,
+ ctx->normalize_gcd, ls->div->row[div][0]);
+ if (isl_int_is_one(ctx->normalize_gcd))
+ return ls;
+
+ isl_seq_scale_down(ls->div->row[div] + 2, ls->div->row[div] + 2,
+ ctx->normalize_gcd, total);
+ isl_int_divexact(ls->div->row[div][0], ls->div->row[div][0],
+ ctx->normalize_gcd);
+ isl_int_fdiv_q(ls->div->row[div][1], ls->div->row[div][1],
+ ctx->normalize_gcd);
+
+ return ls;
+}
+
+/* Exploit the equalities in "eq" to simplify the expressions of
+ * the integer divisions in "ls".
+ * The integer divisions in "ls" are assumed to appear as regular
+ * dimensions in "eq".
+ */
+__isl_give isl_local_space *isl_local_space_substitute_equalities(
+ __isl_take isl_local_space *ls, __isl_take isl_basic_set *eq)
+{
+ int i, j, k;
+ isl_size total, dim;
+ unsigned n_div;
+
+ if (!ls || !eq)
+ goto error;
+
+ total = isl_space_dim(eq->dim, isl_dim_all);
+ dim = isl_local_space_dim(ls, isl_dim_all);
+ if (dim < 0 || total < 0)
+ goto error;
+ if (dim != total)
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "spaces don't match", goto error);
+ total++;
+ n_div = eq->n_div;
+ for (i = 0; i < eq->n_eq; ++i) {
+ j = isl_seq_last_non_zero(eq->eq[i], total + n_div);
+ if (j < 0 || j == 0 || j >= total)
+ continue;
+
+ for (k = 0; k < ls->div->n_row; ++k) {
+ if (isl_int_is_zero(ls->div->row[k][1 + j]))
+ continue;
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ goto error;
+ ls->div = isl_mat_cow(ls->div);
+ if (!ls->div)
+ goto error;
+ isl_seq_elim(ls->div->row[k] + 1, eq->eq[i], j, total,
+ &ls->div->row[k][0]);
+ ls = normalize_div(ls, k);
+ if (!ls)
+ goto error;
+ }
+ }
+
+ isl_basic_set_free(eq);
+ return ls;
+error:
+ isl_basic_set_free(eq);
+ isl_local_space_free(ls);
+ return NULL;
+}
+
+/* Plug in the affine expressions "subs" of length "subs_len" (including
+ * the denominator and the constant term) into the variable at position "pos"
+ * of the "n" div expressions starting at "first".
+ *
+ * Let i be the dimension to replace and let "subs" be of the form
+ *
+ * f/d
+ *
+ * Any integer division starting at "first" with a non-zero coefficient for i,
+ *
+ * floor((a i + g)/m)
+ *
+ * is replaced by
+ *
+ * floor((a f + d g)/(m d))
+ */
+__isl_give isl_local_space *isl_local_space_substitute_seq(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos, isl_int *subs, int subs_len,
+ int first, int n)
+{
+ int i;
+ isl_int v;
+
+ if (n == 0)
+ return ls;
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+ ls->div = isl_mat_cow(ls->div);
+ if (!ls->div)
+ return isl_local_space_free(ls);
+
+ if (first + n > ls->div->n_row)
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "index out of bounds", return isl_local_space_free(ls));
+
+ pos += isl_local_space_offset(ls, type);
+
+ isl_int_init(v);
+ for (i = first; i < first + n; ++i) {
+ if (isl_int_is_zero(ls->div->row[i][1 + pos]))
+ continue;
+ isl_seq_substitute(ls->div->row[i], pos, subs,
+ ls->div->n_col, subs_len, v);
+ ls = normalize_div(ls, i);
+ if (!ls)
+ break;
+ }
+ isl_int_clear(v);
+
+ return ls;
+}
+
+/* Plug in "subs" for dimension "type", "pos" in the integer divisions
+ * of "ls".
+ *
+ * Let i be the dimension to replace and let "subs" be of the form
+ *
+ * f/d
+ *
+ * Any integer division with a non-zero coefficient for i,
+ *
+ * floor((a i + g)/m)
+ *
+ * is replaced by
+ *
+ * floor((a f + d g)/(m d))
+ */
+__isl_give isl_local_space *isl_local_space_substitute(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos, __isl_keep isl_aff *subs)
+{
+ isl_size n_div;
+
+ ls = isl_local_space_cow(ls);
+ if (!ls || !subs)
+ return isl_local_space_free(ls);
+
+ if (!isl_space_is_equal(ls->dim, subs->ls->dim))
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "spaces don't match", return isl_local_space_free(ls));
+ n_div = isl_local_space_dim(subs->ls, isl_dim_div);
+ if (n_div < 0)
+ return isl_local_space_free(ls);
+ if (n_div != 0)
+ isl_die(isl_local_space_get_ctx(ls), isl_error_unsupported,
+ "cannot handle divs yet",
+ return isl_local_space_free(ls));
+
+ return isl_local_space_substitute_seq(ls, type, pos, subs->v->el,
+ subs->v->size, 0, ls->div->n_row);
+}
+
+isl_bool isl_local_space_is_named_or_nested(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type)
+{
+ if (!ls)
+ return isl_bool_error;
+ return isl_space_is_named_or_nested(ls->dim, type);
+}
+
+__isl_give isl_local_space *isl_local_space_drop_dims(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ if (!ls)
+ return NULL;
+ if (n == 0 && !isl_local_space_is_named_or_nested(ls, type))
+ return ls;
+
+ if (isl_local_space_check_range(ls, type, first, n) < 0)
+ return isl_local_space_free(ls);
+
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+
+ if (type == isl_dim_div) {
+ ls->div = isl_mat_drop_rows(ls->div, first, n);
+ } else {
+ ls->dim = isl_space_drop_dims(ls->dim, type, first, n);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+ }
+
+ first += 1 + isl_local_space_offset(ls, type);
+ ls->div = isl_mat_drop_cols(ls->div, first, n);
+ if (!ls->div)
+ return isl_local_space_free(ls);
+
+ return ls;
+}
+
+__isl_give isl_local_space *isl_local_space_insert_dims(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ if (!ls)
+ return NULL;
+ if (n == 0 && !isl_local_space_is_named_or_nested(ls, type))
+ return ls;
+
+ if (isl_local_space_check_range(ls, type, first, 0) < 0)
+ return isl_local_space_free(ls);
+
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+
+ if (type == isl_dim_div) {
+ ls->div = isl_mat_insert_zero_rows(ls->div, first, n);
+ } else {
+ ls->dim = isl_space_insert_dims(ls->dim, type, first, n);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+ }
+
+ first += 1 + isl_local_space_offset(ls, type);
+ ls->div = isl_mat_insert_zero_cols(ls->div, first, n);
+ if (!ls->div)
+ return isl_local_space_free(ls);
+
+ return ls;
+}
+
+/* Does the linear part of "constraint" correspond to
+ * integer division "div" in "ls"?
+ *
+ * That is, given div = floor((c + f)/m), is the constraint of the form
+ *
+ * f - m d + c' >= 0 [sign = 1]
+ * or
+ * -f + m d + c'' >= 0 [sign = -1]
+ * ?
+ * If so, set *sign to the corresponding value.
+ */
+static isl_bool is_linear_div_constraint(__isl_keep isl_local_space *ls,
+ isl_int *constraint, unsigned div, int *sign)
+{
+ isl_bool unknown;
+ unsigned pos;
+
+ unknown = isl_local_space_div_is_marked_unknown(ls, div);
+ if (unknown < 0)
+ return isl_bool_error;
+ if (unknown)
+ return isl_bool_false;
+
+ pos = isl_local_space_offset(ls, isl_dim_div) + div;
+
+ if (isl_int_eq(constraint[pos], ls->div->row[div][0])) {
+ *sign = -1;
+ if (!isl_seq_is_neg(constraint + 1,
+ ls->div->row[div] + 2, pos - 1))
+ return isl_bool_false;
+ } else if (isl_int_abs_eq(constraint[pos], ls->div->row[div][0])) {
+ *sign = 1;
+ if (!isl_seq_eq(constraint + 1, ls->div->row[div] + 2, pos - 1))
+ return isl_bool_false;
+ } else {
+ return isl_bool_false;
+ }
+ if (isl_seq_first_non_zero(constraint + pos + 1,
+ ls->div->n_row - div - 1) != -1)
+ return isl_bool_false;
+ return isl_bool_true;
+}
+
+/* Check if the constraints pointed to by "constraint" is a div
+ * constraint corresponding to div "div" in "ls".
+ *
+ * That is, if div = floor(f/m), then check if the constraint is
+ *
+ * f - m d >= 0
+ * or
+ * -(f-(m-1)) + m d >= 0
+ *
+ * First check if the linear part is of the right form and
+ * then check the constant term.
+ */
+isl_bool isl_local_space_is_div_constraint(__isl_keep isl_local_space *ls,
+ isl_int *constraint, unsigned div)
+{
+ int sign;
+ isl_bool linear;
+
+ linear = is_linear_div_constraint(ls, constraint, div, &sign);
+ if (linear < 0 || !linear)
+ return linear;
+
+ if (sign < 0) {
+ int neg;
+ isl_int_sub(ls->div->row[div][1],
+ ls->div->row[div][1], ls->div->row[div][0]);
+ isl_int_add_ui(ls->div->row[div][1], ls->div->row[div][1], 1);
+ neg = isl_seq_is_neg(constraint, ls->div->row[div] + 1, 1);
+ isl_int_sub_ui(ls->div->row[div][1], ls->div->row[div][1], 1);
+ isl_int_add(ls->div->row[div][1],
+ ls->div->row[div][1], ls->div->row[div][0]);
+ if (!neg)
+ return isl_bool_false;
+ } else {
+ if (!isl_int_eq(constraint[0], ls->div->row[div][1]))
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+/* Is the constraint pointed to by "constraint" one
+ * of an equality that corresponds to integer division "div" in "ls"?
+ *
+ * That is, given an integer division of the form
+ *
+ * a = floor((f + c)/m)
+ *
+ * is the equality of the form
+ *
+ * -f + m d + c' = 0
+ * ?
+ * Note that the constant term is not checked explicitly, but given
+ * that this is a valid equality constraint, the constant c' necessarily
+ * has a value close to -c.
+ */
+isl_bool isl_local_space_is_div_equality(__isl_keep isl_local_space *ls,
+ isl_int *constraint, unsigned div)
+{
+ int sign;
+ isl_bool linear;
+
+ linear = is_linear_div_constraint(ls, constraint, div, &sign);
+ if (linear < 0 || !linear)
+ return linear;
+
+ return isl_bool_ok(sign < 0);
+}
+
+/*
+ * Set active[i] to 1 if the dimension at position i is involved
+ * in the linear expression l.
+ */
+int *isl_local_space_get_active(__isl_keep isl_local_space *ls, isl_int *l)
+{
+ int i, j;
+ isl_ctx *ctx;
+ int *active = NULL;
+ isl_size total;
+ unsigned offset;
+
+ ctx = isl_local_space_get_ctx(ls);
+ total = isl_local_space_dim(ls, isl_dim_all);
+ if (total < 0)
+ return NULL;
+ active = isl_calloc_array(ctx, int, total);
+ if (total && !active)
+ return NULL;
+
+ for (i = 0; i < total; ++i)
+ active[i] = !isl_int_is_zero(l[i]);
+
+ offset = isl_local_space_offset(ls, isl_dim_div) - 1;
+ for (i = ls->div->n_row - 1; i >= 0; --i) {
+ if (!active[offset + i])
+ continue;
+ for (j = 0; j < total; ++j)
+ active[j] |= !isl_int_is_zero(ls->div->row[i][2 + j]);
+ }
+
+ return active;
+}
+
+/* Given a local space "ls" of a set, create a local space
+ * for the lift of the set. In particular, the result
+ * is of the form [dim -> local[..]], with ls->div->n_row variables in the
+ * range of the wrapped map.
+ */
+__isl_give isl_local_space *isl_local_space_lift(
+ __isl_take isl_local_space *ls)
+{
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+
+ ls->dim = isl_space_lift(ls->dim, ls->div->n_row);
+ ls->div = isl_mat_drop_rows(ls->div, 0, ls->div->n_row);
+ if (!ls->dim || !ls->div)
+ return isl_local_space_free(ls);
+
+ return ls;
+}
+
+/* Construct a basic map that maps a set living in local space "ls"
+ * to the corresponding lifted local space.
+ */
+__isl_give isl_basic_map *isl_local_space_lifting(
+ __isl_take isl_local_space *ls)
+{
+ isl_basic_map *lifting;
+ isl_basic_set *bset;
+
+ if (!ls)
+ return NULL;
+ if (!isl_local_space_is_set(ls))
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "lifting only defined on set spaces", goto error);
+
+ bset = isl_basic_set_from_local_space(ls);
+ lifting = isl_basic_set_unwrap(isl_basic_set_lift(bset));
+ lifting = isl_basic_map_domain_map(lifting);
+ lifting = isl_basic_map_reverse(lifting);
+
+ return lifting;
+error:
+ isl_local_space_free(ls);
+ return NULL;
+}
+
+/* Compute the preimage of "ls" under the function represented by "ma".
+ * In other words, plug in "ma" in "ls". The result is a local space
+ * that is part of the domain space of "ma".
+ *
+ * If the divs in "ls" are represented as
+ *
+ * floor((a_i(p) + b_i x + c_i(divs))/n_i)
+ *
+ * and ma is represented by
+ *
+ * x = D(p) + F(y) + G(divs')
+ *
+ * then the resulting divs are
+ *
+ * floor((a_i(p) + b_i D(p) + b_i F(y) + B_i G(divs') + c_i(divs))/n_i)
+ *
+ * We first copy over the divs from "ma" and then
+ * we add the modified divs from "ls".
+ */
+__isl_give isl_local_space *isl_local_space_preimage_multi_aff(
+ __isl_take isl_local_space *ls, __isl_take isl_multi_aff *ma)
+{
+ int i;
+ isl_space *space;
+ isl_local_space *res = NULL;
+ isl_size n_div_ls, n_div_ma;
+ isl_int f, c1, c2, g;
+
+ ma = isl_multi_aff_align_divs(ma);
+ if (!ls || !ma)
+ goto error;
+ if (!isl_space_is_range_internal(ls->dim, ma->space))
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "spaces don't match", goto error);
+
+ n_div_ls = isl_local_space_dim(ls, isl_dim_div);
+ n_div_ma = ma->n ? isl_aff_dim(ma->u.p[0], isl_dim_div) : 0;
+ if (n_div_ls < 0 || n_div_ma < 0)
+ goto error;
+
+ space = isl_space_domain(isl_multi_aff_get_space(ma));
+ res = isl_local_space_alloc(space, n_div_ma + n_div_ls);
+ if (!res)
+ goto error;
+
+ if (n_div_ma) {
+ isl_mat_free(res->div);
+ res->div = isl_mat_copy(ma->u.p[0]->ls->div);
+ res->div = isl_mat_add_zero_cols(res->div, n_div_ls);
+ res->div = isl_mat_add_rows(res->div, n_div_ls);
+ if (!res->div)
+ goto error;
+ }
+
+ isl_int_init(f);
+ isl_int_init(c1);
+ isl_int_init(c2);
+ isl_int_init(g);
+
+ for (i = 0; i < ls->div->n_row; ++i) {
+ if (isl_int_is_zero(ls->div->row[i][0])) {
+ isl_int_set_si(res->div->row[n_div_ma + i][0], 0);
+ continue;
+ }
+ if (isl_seq_preimage(res->div->row[n_div_ma + i],
+ ls->div->row[i],
+ ma, 0, 0, n_div_ma, n_div_ls, f, c1, c2, g, 1) < 0)
+ res = isl_local_space_free(res);
+ res = normalize_div(res, n_div_ma + i);
+ if (!res)
+ break;
+ }
+
+ isl_int_clear(f);
+ isl_int_clear(c1);
+ isl_int_clear(c2);
+ isl_int_clear(g);
+
+ isl_local_space_free(ls);
+ isl_multi_aff_free(ma);
+ return res;
+error:
+ isl_local_space_free(ls);
+ isl_multi_aff_free(ma);
+ isl_local_space_free(res);
+ return NULL;
+}
+
+/* Move the "n" dimensions of "src_type" starting at "src_pos" of "ls"
+ * to dimensions of "dst_type" at "dst_pos".
+ *
+ * Moving to/from local dimensions is not allowed.
+ * We currently assume that the dimension type changes.
+ */
+__isl_give isl_local_space *isl_local_space_move_dims(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ unsigned g_dst_pos;
+ unsigned g_src_pos;
+
+ if (!ls)
+ return NULL;
+ if (n == 0 &&
+ !isl_local_space_is_named_or_nested(ls, src_type) &&
+ !isl_local_space_is_named_or_nested(ls, dst_type))
+ return ls;
+
+ if (isl_local_space_check_range(ls, src_type, src_pos, n) < 0)
+ return isl_local_space_free(ls);
+ if (isl_local_space_check_range(ls, dst_type, dst_pos, 0) < 0)
+ return isl_local_space_free(ls);
+ if (src_type == isl_dim_div)
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "cannot move divs", return isl_local_space_free(ls));
+ if (dst_type == isl_dim_div)
+ isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
+ "cannot move to divs", return isl_local_space_free(ls));
+ if (dst_type == src_type && dst_pos == src_pos)
+ return ls;
+ if (dst_type == src_type)
+ isl_die(isl_local_space_get_ctx(ls), isl_error_unsupported,
+ "moving dims within the same type not supported",
+ return isl_local_space_free(ls));
+
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+
+ g_src_pos = 1 + isl_local_space_offset(ls, src_type) + src_pos;
+ g_dst_pos = 1 + isl_local_space_offset(ls, dst_type) + dst_pos;
+ if (dst_type > src_type)
+ g_dst_pos -= n;
+ ls->div = isl_mat_move_cols(ls->div, g_dst_pos, g_src_pos, n);
+ if (!ls->div)
+ return isl_local_space_free(ls);
+ ls->dim = isl_space_move_dims(ls->dim, dst_type, dst_pos,
+ src_type, src_pos, n);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+
+ return ls;
+}
+
+/* Remove any internal structure of the domain of "ls".
+ * If there is any such internal structure in the input,
+ * then the name of the corresponding space is also removed.
+ */
+__isl_give isl_local_space *isl_local_space_flatten_domain(
+ __isl_take isl_local_space *ls)
+{
+ if (!ls)
+ return NULL;
+
+ if (!ls->dim->nested[0])
+ return ls;
+
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+
+ ls->dim = isl_space_flatten_domain(ls->dim);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+
+ return ls;
+}
+
+/* Remove any internal structure of the range of "ls".
+ * If there is any such internal structure in the input,
+ * then the name of the corresponding space is also removed.
+ */
+__isl_give isl_local_space *isl_local_space_flatten_range(
+ __isl_take isl_local_space *ls)
+{
+ if (!ls)
+ return NULL;
+
+ if (!ls->dim->nested[1])
+ return ls;
+
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+
+ ls->dim = isl_space_flatten_range(ls->dim);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+
+ return ls;
+}
+
+/* Given the local space "ls" of a map, return the local space of a set
+ * that lives in a space that wraps the space of "ls" and that has
+ * the same divs.
+ */
+__isl_give isl_local_space *isl_local_space_wrap(__isl_take isl_local_space *ls)
+{
+ ls = isl_local_space_cow(ls);
+ if (!ls)
+ return NULL;
+
+ ls->dim = isl_space_wrap(ls->dim);
+ if (!ls->dim)
+ return isl_local_space_free(ls);
+
+ return ls;
+}
+
+/* Lift the point "pnt", living in the (set) space of "ls"
+ * to live in a space with extra coordinates corresponding
+ * to the local variables of "ls".
+ */
+__isl_give isl_point *isl_local_space_lift_point(__isl_take isl_local_space *ls,
+ __isl_take isl_point *pnt)
+{
+ isl_size n_local;
+ isl_space *space;
+ isl_local *local;
+ isl_vec *vec;
+
+ if (isl_local_space_check_has_space(ls, isl_point_peek_space(pnt)) < 0)
+ goto error;
+
+ local = isl_local_space_peek_local(ls);
+ n_local = isl_local_space_dim(ls, isl_dim_div);
+ if (n_local < 0)
+ goto error;
+
+ space = isl_point_take_space(pnt);
+ vec = isl_point_take_vec(pnt);
+
+ space = isl_space_lift(space, n_local);
+ vec = isl_local_extend_point_vec(local, vec);
+
+ pnt = isl_point_restore_vec(pnt, vec);
+ pnt = isl_point_restore_space(pnt, space);
+
+ isl_local_space_free(ls);
+
+ return pnt;
+error:
+ isl_local_space_free(ls);
+ isl_point_free(pnt);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_space_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_space_private.h
new file mode 100644
index 00000000000..42c2ad0fc02
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_local_space_private.h
@@ -0,0 +1,100 @@
+#ifndef ISL_LOCAL_SPACE_PRIVATE_H
+#define ISL_LOCAL_SPACE_PRIVATE_H
+
+#include <isl/mat.h>
+#include <isl/set.h>
+#include <isl/local_space.h>
+
+struct isl_local_space {
+ int ref;
+
+ isl_space *dim;
+ isl_mat *div;
+};
+
+isl_stat isl_local_space_check_range(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+uint32_t isl_local_space_get_hash(__isl_keep isl_local_space *ls);
+
+__isl_give isl_local_space *isl_local_space_alloc(__isl_take isl_space *space,
+ unsigned n_div);
+__isl_give isl_local_space *isl_local_space_alloc_div(
+ __isl_take isl_space *space, __isl_take isl_mat *div);
+
+__isl_keep isl_space *isl_local_space_peek_space(
+ __isl_keep isl_local_space *ls);
+
+__isl_give isl_local_space *isl_local_space_swap_div(
+ __isl_take isl_local_space *ls, int a, int b);
+__isl_give isl_local_space *isl_local_space_add_div(
+ __isl_take isl_local_space *ls, __isl_take isl_vec *div);
+
+int isl_mat_cmp_div(__isl_keep isl_mat *div, int i, int j);
+__isl_give isl_mat *isl_merge_divs(__isl_keep isl_mat *div1,
+ __isl_keep isl_mat *div2, int *exp1, int *exp2);
+
+unsigned isl_local_space_offset(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type);
+
+__isl_give isl_local_space *isl_local_space_replace_divs(
+ __isl_take isl_local_space *ls, __isl_take isl_mat *div);
+isl_bool isl_local_space_div_is_marked_unknown(__isl_keep isl_local_space *ls,
+ int div);
+isl_bool isl_local_space_div_is_known(__isl_keep isl_local_space *ls, int div);
+isl_bool isl_local_space_divs_known(__isl_keep isl_local_space *ls);
+
+__isl_give isl_basic_set *isl_local_space_lift_basic_set(
+ __isl_take isl_local_space *ls, __isl_take isl_basic_set *bset);
+__isl_give isl_set *isl_local_space_lift_set(__isl_take isl_local_space *ls,
+ __isl_take isl_set *set);
+__isl_give isl_local_space *isl_local_space_substitute_equalities(
+ __isl_take isl_local_space *ls, __isl_take isl_basic_set *eq);
+
+isl_bool isl_local_space_is_named_or_nested(__isl_keep isl_local_space *ls,
+ enum isl_dim_type type);
+
+isl_bool isl_local_space_has_equal_space(__isl_keep isl_local_space *ls1,
+ __isl_keep isl_local_space *ls2);
+
+__isl_give isl_local_space *isl_local_space_reset_space(
+ __isl_take isl_local_space *ls, __isl_take isl_space *space);
+__isl_give isl_local_space *isl_local_space_realign(
+ __isl_take isl_local_space *ls, __isl_take isl_reordering *r);
+
+isl_bool isl_local_space_is_div_constraint(__isl_keep isl_local_space *ls,
+ isl_int *constraint, unsigned div);
+isl_bool isl_local_space_is_div_equality(__isl_keep isl_local_space *ls,
+ isl_int *constraint, unsigned div);
+
+int *isl_local_space_get_active(__isl_keep isl_local_space *ls, isl_int *l);
+
+__isl_give isl_local_space *isl_local_space_substitute_seq(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos, isl_int *subs, int subs_len,
+ int first, int n);
+__isl_give isl_local_space *isl_local_space_substitute(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type type, unsigned pos, __isl_keep isl_aff *subs);
+
+__isl_give isl_local_space *isl_local_space_lift(
+ __isl_take isl_local_space *ls);
+
+__isl_give isl_local_space *isl_local_space_preimage_multi_aff(
+ __isl_take isl_local_space *ls, __isl_take isl_multi_aff *ma);
+
+__isl_give isl_local_space *isl_local_space_move_dims(
+ __isl_take isl_local_space *ls,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n);
+
+int isl_local_space_cmp(__isl_keep isl_local_space *ls1,
+ __isl_keep isl_local_space *ls2);
+
+__isl_give isl_point *isl_local_space_lift_point(__isl_take isl_local_space *ls,
+ __isl_take isl_point *pnt);
+
+isl_bool isl_local_space_has_space(__isl_keep isl_local_space *ls,
+ __isl_keep isl_space *space);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_lp.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_lp.c
new file mode 100644
index 00000000000..5fe179ab29d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_lp.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl/lp.h>
+#include <isl_seq.h>
+#include "isl_tab.h"
+#include <isl_options_private.h>
+#include <isl_local_space_private.h>
+#include <isl_aff_private.h>
+#include <isl_mat_private.h>
+#include <isl_val_private.h>
+#include <isl_vec_private.h>
+
+#include <bset_to_bmap.c>
+#include <set_to_map.c>
+
+enum isl_lp_result isl_tab_solve_lp(__isl_keep isl_basic_map *bmap,
+ int maximize, isl_int *f, isl_int denom, isl_int *opt,
+ isl_int *opt_denom, __isl_give isl_vec **sol)
+{
+ struct isl_tab *tab;
+ enum isl_lp_result res;
+ isl_size dim = isl_basic_map_dim(bmap, isl_dim_all);
+
+ if (dim < 0)
+ return isl_lp_error;
+ if (maximize)
+ isl_seq_neg(f, f, 1 + dim);
+
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ tab = isl_tab_from_basic_map(bmap, 0);
+ res = isl_tab_min(tab, f, denom, opt, opt_denom, 0);
+ if (res == isl_lp_ok && sol) {
+ *sol = isl_tab_get_sample_value(tab);
+ if (!*sol)
+ res = isl_lp_error;
+ }
+ isl_tab_free(tab);
+
+ if (maximize)
+ isl_seq_neg(f, f, 1 + dim);
+ if (maximize && opt)
+ isl_int_neg(*opt, *opt);
+
+ return res;
+}
+
+/* Given a basic map "bmap" and an affine combination of the variables "f"
+ * with denominator "denom", set *opt / *opt_denom to the minimal
+ * (or maximal if "maximize" is true) value attained by f/d over "bmap",
+ * assuming the basic map is not empty and the expression cannot attain
+ * arbitrarily small (or large) values.
+ * If opt_denom is NULL, then *opt is rounded up (or down)
+ * to the nearest integer.
+ * The return value reflects the nature of the result (empty, unbounded,
+ * minimal or maximal value returned in *opt).
+ */
+enum isl_lp_result isl_basic_map_solve_lp(__isl_keep isl_basic_map *bmap,
+ int max, isl_int *f, isl_int d, isl_int *opt, isl_int *opt_denom,
+ __isl_give isl_vec **sol)
+{
+ if (sol)
+ *sol = NULL;
+
+ if (!bmap)
+ return isl_lp_error;
+
+ return isl_tab_solve_lp(bmap, max, f, d, opt, opt_denom, sol);
+}
+
+enum isl_lp_result isl_basic_set_solve_lp(__isl_keep isl_basic_set *bset,
+ int max, isl_int *f, isl_int d, isl_int *opt, isl_int *opt_denom,
+ __isl_give isl_vec **sol)
+{
+ return isl_basic_map_solve_lp(bset_to_bmap(bset), max,
+ f, d, opt, opt_denom, sol);
+}
+
+enum isl_lp_result isl_map_solve_lp(__isl_keep isl_map *map, int max,
+ isl_int *f, isl_int d, isl_int *opt,
+ isl_int *opt_denom,
+ __isl_give isl_vec **sol)
+{
+ int i;
+ isl_int o;
+ isl_int t;
+ isl_int opt_i;
+ isl_int opt_denom_i;
+ enum isl_lp_result res;
+ int max_div;
+ isl_vec *v = NULL;
+
+ if (!map)
+ return isl_lp_error;
+ if (map->n == 0)
+ return isl_lp_empty;
+
+ max_div = 0;
+ for (i = 0; i < map->n; ++i)
+ if (map->p[i]->n_div > max_div)
+ max_div = map->p[i]->n_div;
+ if (max_div > 0) {
+ isl_size total = isl_map_dim(map, isl_dim_all);
+ if (total < 0)
+ return isl_lp_error;
+ v = isl_vec_alloc(map->ctx, 1 + total + max_div);
+ if (!v)
+ return isl_lp_error;
+ isl_seq_cpy(v->el, f, 1 + total);
+ isl_seq_clr(v->el + 1 + total, max_div);
+ f = v->el;
+ }
+
+ if (!opt && map->n > 1 && sol) {
+ isl_int_init(o);
+ opt = &o;
+ }
+ if (map->n > 0)
+ isl_int_init(opt_i);
+ if (map->n > 0 && opt_denom) {
+ isl_int_init(opt_denom_i);
+ isl_int_init(t);
+ }
+
+ res = isl_basic_map_solve_lp(map->p[0], max, f, d,
+ opt, opt_denom, sol);
+ if (res == isl_lp_error || res == isl_lp_unbounded)
+ goto done;
+
+ if (sol)
+ *sol = NULL;
+
+ for (i = 1; i < map->n; ++i) {
+ isl_vec *sol_i = NULL;
+ enum isl_lp_result res_i;
+ int better;
+
+ res_i = isl_basic_map_solve_lp(map->p[i], max, f, d,
+ &opt_i,
+ opt_denom ? &opt_denom_i : NULL,
+ sol ? &sol_i : NULL);
+ if (res_i == isl_lp_error || res_i == isl_lp_unbounded) {
+ res = res_i;
+ goto done;
+ }
+ if (res_i == isl_lp_empty)
+ continue;
+ if (res == isl_lp_empty) {
+ better = 1;
+ } else if (!opt_denom) {
+ if (max)
+ better = isl_int_gt(opt_i, *opt);
+ else
+ better = isl_int_lt(opt_i, *opt);
+ } else {
+ isl_int_mul(t, opt_i, *opt_denom);
+ isl_int_submul(t, *opt, opt_denom_i);
+ if (max)
+ better = isl_int_is_pos(t);
+ else
+ better = isl_int_is_neg(t);
+ }
+ if (better) {
+ res = res_i;
+ if (opt)
+ isl_int_set(*opt, opt_i);
+ if (opt_denom)
+ isl_int_set(*opt_denom, opt_denom_i);
+ if (sol) {
+ isl_vec_free(*sol);
+ *sol = sol_i;
+ }
+ } else
+ isl_vec_free(sol_i);
+ }
+
+done:
+ isl_vec_free(v);
+ if (map->n > 0 && opt_denom) {
+ isl_int_clear(opt_denom_i);
+ isl_int_clear(t);
+ }
+ if (map->n > 0)
+ isl_int_clear(opt_i);
+ if (opt == &o)
+ isl_int_clear(o);
+ return res;
+}
+
+enum isl_lp_result isl_set_solve_lp(__isl_keep isl_set *set, int max,
+ isl_int *f, isl_int d, isl_int *opt,
+ isl_int *opt_denom,
+ __isl_give isl_vec **sol)
+{
+ return isl_map_solve_lp(set_to_map(set), max,
+ f, d, opt, opt_denom, sol);
+}
+
+/* Return the optimal (rational) value of "obj" over "bset", assuming
+ * that "obj" and "bset" have aligned parameters and divs.
+ * If "max" is set, then the maximal value is computed.
+ * Otherwise, the minimal value is computed.
+ *
+ * Return infinity or negative infinity if the optimal value is unbounded and
+ * NaN if "bset" is empty.
+ *
+ * Call isl_basic_set_solve_lp and translate the results.
+ */
+static __isl_give isl_val *basic_set_opt_lp(
+ __isl_keep isl_basic_set *bset, int max, __isl_keep isl_aff *obj)
+{
+ isl_ctx *ctx;
+ isl_val *res;
+ enum isl_lp_result lp_res;
+
+ if (!bset || !obj)
+ return NULL;
+
+ ctx = isl_aff_get_ctx(obj);
+ res = isl_val_alloc(ctx);
+ if (!res)
+ return NULL;
+ lp_res = isl_basic_set_solve_lp(bset, max, obj->v->el + 1,
+ obj->v->el[0], &res->n, &res->d, NULL);
+ if (lp_res == isl_lp_ok)
+ return isl_val_normalize(res);
+ isl_val_free(res);
+ if (lp_res == isl_lp_error)
+ return NULL;
+ if (lp_res == isl_lp_empty)
+ return isl_val_nan(ctx);
+ if (max)
+ return isl_val_infty(ctx);
+ else
+ return isl_val_neginfty(ctx);
+}
+
+/* Return the optimal (rational) value of "obj" over "bset", assuming
+ * that "obj" and "bset" have aligned parameters.
+ * If "max" is set, then the maximal value is computed.
+ * Otherwise, the minimal value is computed.
+ *
+ * Return infinity or negative infinity if the optimal value is unbounded and
+ * NaN if "bset" is empty.
+ *
+ * Align the divs of "bset" and "obj" and call basic_set_opt_lp.
+ */
+static __isl_give isl_val *isl_basic_set_opt_lp_val_aligned(
+ __isl_keep isl_basic_set *bset, int max, __isl_keep isl_aff *obj)
+{
+ int *exp1 = NULL;
+ int *exp2 = NULL;
+ isl_ctx *ctx;
+ isl_mat *bset_div = NULL;
+ isl_mat *div = NULL;
+ isl_val *res;
+ isl_size bset_n_div, obj_n_div;
+
+ if (!bset || !obj)
+ return NULL;
+
+ ctx = isl_aff_get_ctx(obj);
+ if (!isl_space_is_equal(bset->dim, obj->ls->dim))
+ isl_die(ctx, isl_error_invalid,
+ "spaces don't match", return NULL);
+
+ bset_n_div = isl_basic_set_dim(bset, isl_dim_div);
+ obj_n_div = isl_aff_dim(obj, isl_dim_div);
+ if (bset_n_div < 0 || obj_n_div < 0)
+ return NULL;
+ if (bset_n_div == 0 && obj_n_div == 0)
+ return basic_set_opt_lp(bset, max, obj);
+
+ bset = isl_basic_set_copy(bset);
+ obj = isl_aff_copy(obj);
+
+ bset_div = isl_basic_set_get_divs(bset);
+ exp1 = isl_alloc_array(ctx, int, bset_n_div);
+ exp2 = isl_alloc_array(ctx, int, obj_n_div);
+ if (!bset_div || (bset_n_div && !exp1) || (obj_n_div && !exp2))
+ goto error;
+
+ div = isl_merge_divs(bset_div, obj->ls->div, exp1, exp2);
+
+ bset = isl_basic_set_expand_divs(bset, isl_mat_copy(div), exp1);
+ obj = isl_aff_expand_divs(obj, isl_mat_copy(div), exp2);
+
+ res = basic_set_opt_lp(bset, max, obj);
+
+ isl_mat_free(bset_div);
+ isl_mat_free(div);
+ free(exp1);
+ free(exp2);
+ isl_basic_set_free(bset);
+ isl_aff_free(obj);
+
+ return res;
+error:
+ isl_mat_free(div);
+ isl_mat_free(bset_div);
+ free(exp1);
+ free(exp2);
+ isl_basic_set_free(bset);
+ isl_aff_free(obj);
+ return NULL;
+}
+
+/* Return the optimal (rational) value of "obj" over "bset".
+ * If "max" is set, then the maximal value is computed.
+ * Otherwise, the minimal value is computed.
+ *
+ * Return infinity or negative infinity if the optimal value is unbounded and
+ * NaN if "bset" is empty.
+ */
+static __isl_give isl_val *isl_basic_set_opt_lp_val(
+ __isl_keep isl_basic_set *bset, int max, __isl_keep isl_aff *obj)
+{
+ isl_bool equal;
+ isl_val *res;
+
+ if (!bset || !obj)
+ return NULL;
+
+ equal = isl_basic_set_space_has_equal_params(bset, obj->ls->dim);
+ if (equal < 0)
+ return NULL;
+ if (equal)
+ return isl_basic_set_opt_lp_val_aligned(bset, max, obj);
+
+ bset = isl_basic_set_copy(bset);
+ obj = isl_aff_copy(obj);
+ bset = isl_basic_set_align_params(bset, isl_aff_get_domain_space(obj));
+ obj = isl_aff_align_params(obj, isl_basic_set_get_space(bset));
+
+ res = isl_basic_set_opt_lp_val_aligned(bset, max, obj);
+
+ isl_basic_set_free(bset);
+ isl_aff_free(obj);
+
+ return res;
+}
+
+/* Return the minimal (rational) value of "obj" over "bset".
+ *
+ * Return negative infinity if the minimal value is unbounded and
+ * NaN if "bset" is empty.
+ */
+__isl_give isl_val *isl_basic_set_min_lp_val(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_aff *obj)
+{
+ return isl_basic_set_opt_lp_val(bset, 0, obj);
+}
+
+/* Return the maximal (rational) value of "obj" over "bset".
+ *
+ * Return infinity if the maximal value is unbounded and
+ * NaN if "bset" is empty.
+ */
+__isl_give isl_val *isl_basic_set_max_lp_val(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_aff *obj)
+{
+ return isl_basic_set_opt_lp_val(bset, 1, obj);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_lp_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_lp_private.h
new file mode 100644
index 00000000000..ddc44c1eeda
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_lp_private.h
@@ -0,0 +1,21 @@
+#ifndef ISL_LP_PRIVATE_H
+#define ISL_LP_PRIVATE_H
+
+#include <isl_int.h>
+#include <isl/lp.h>
+#include <isl/vec.h>
+
+enum isl_lp_result isl_basic_map_solve_lp(__isl_keep isl_basic_map *bmap,
+ int max, isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom,
+ __isl_give isl_vec **sol);
+enum isl_lp_result isl_basic_set_solve_lp(__isl_keep isl_basic_set *bset,
+ int max, isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom,
+ __isl_give isl_vec **sol);
+enum isl_lp_result isl_map_solve_lp(__isl_keep isl_map *map, int max,
+ isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom,
+ __isl_give isl_vec **sol);
+enum isl_lp_result isl_set_solve_lp(__isl_keep isl_set *set, int max,
+ isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom,
+ __isl_give isl_vec **sol);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map.c
new file mode 100644
index 00000000000..516106cf3a8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map.c
@@ -0,0 +1,14451 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2012-2014 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ * Copyright 2016 INRIA Paris
+ * Copyright 2016 Sven Verdoolaege
+ * Copyright 2018-2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ * and Centre de Recherche Inria de Paris, 2 rue Simone Iff - Voie DQ12,
+ * CS 42112, 75589 Paris Cedex 12, France
+ * and Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <string.h>
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl_blk.h>
+#include <isl_id_private.h>
+#include <isl/constraint.h>
+#include "isl_space_private.h"
+#include "isl_equalities.h"
+#include <isl_lp_private.h>
+#include <isl_seq.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl_reordering.h>
+#include "isl_sample.h"
+#include <isl_sort.h>
+#include "isl_tab.h"
+#include <isl/vec.h>
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+#include <isl_dim_map.h>
+#include <isl_local_space_private.h>
+#include <isl_aff_private.h>
+#include <isl_options_private.h>
+#include <isl_morph.h>
+#include <isl_val_private.h>
+#include <isl_printer_private.h>
+
+#include <bset_to_bmap.c>
+#include <bset_from_bmap.c>
+#include <set_to_map.c>
+#include <set_from_map.c>
+
+/* Treat "bset" as a basic map.
+ * Internally, isl_basic_set is defined to isl_basic_map, so in practice,
+ * this function performs a redundant cast.
+ */
+static __isl_keep const isl_basic_map *const_bset_to_bmap(
+ __isl_keep const isl_basic_set *bset)
+{
+ return (const isl_basic_map *) bset;
+}
+
+#undef TYPE
+#define TYPE isl_basic_map
+#include "has_single_reference_templ.c"
+
+static unsigned pos(__isl_keep isl_space *space, enum isl_dim_type type)
+{
+ switch (type) {
+ case isl_dim_param: return 1;
+ case isl_dim_in: return 1 + space->nparam;
+ case isl_dim_out: return 1 + space->nparam + space->n_in;
+ default: return 0;
+ }
+}
+
+isl_size isl_basic_map_dim(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type)
+{
+ if (!bmap)
+ return isl_size_error;
+ switch (type) {
+ case isl_dim_cst: return 1;
+ case isl_dim_param:
+ case isl_dim_in:
+ case isl_dim_out: return isl_space_dim(bmap->dim, type);
+ case isl_dim_div: return bmap->n_div;
+ case isl_dim_all: return isl_basic_map_total_dim(bmap);
+ default: return 0;
+ }
+}
+
+/* Return the space of "map".
+ */
+__isl_keep isl_space *isl_map_peek_space(__isl_keep const isl_map *map)
+{
+ return map ? map->dim : NULL;
+}
+
+/* Return the space of "set".
+ */
+__isl_keep isl_space *isl_set_peek_space(__isl_keep isl_set *set)
+{
+ return isl_map_peek_space(set_to_map(set));
+}
+
+isl_size isl_map_dim(__isl_keep isl_map *map, enum isl_dim_type type)
+{
+ return isl_space_dim(isl_map_peek_space(map), type);
+}
+
+/* Return the dimensionality of the domain (tuple) of the map.
+ */
+isl_size isl_map_domain_tuple_dim(__isl_keep isl_map *map)
+{
+ return isl_map_dim(map, isl_dim_in);
+}
+
+/* Return the dimensionality of the range (tuple) of the map.
+ */
+isl_size isl_map_range_tuple_dim(__isl_keep isl_map *map)
+{
+ return isl_map_dim(map, isl_dim_out);
+}
+
+isl_size isl_set_dim(__isl_keep isl_set *set, enum isl_dim_type type)
+{
+ return isl_map_dim(set_to_map(set), type);
+}
+
+/* Return the dimensionality of the (tuple of the) set.
+ */
+isl_size isl_set_tuple_dim(__isl_keep isl_set *set)
+{
+ return isl_set_dim(set, isl_dim_set);
+}
+
+/* Return the position of the variables of the given type
+ * within the sequence of variables of "bmap".
+ */
+isl_size isl_basic_map_var_offset(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type)
+{
+ isl_space *space;
+
+ space = isl_basic_map_peek_space(bmap);
+ if (!space)
+ return isl_size_error;
+
+ switch (type) {
+ case isl_dim_param:
+ case isl_dim_in:
+ case isl_dim_out: return isl_space_offset(space, type);
+ case isl_dim_div: return isl_space_dim(space, isl_dim_all);
+ case isl_dim_cst:
+ default:
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "invalid dimension type", return isl_size_error);
+ }
+}
+
+/* Return the position of the variables of the given type
+ * within the sequence of variables of "bset".
+ */
+isl_size isl_basic_set_var_offset(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type)
+{
+ return isl_basic_map_var_offset(bset_to_bmap(bset), type);
+}
+
+/* Return the position of the coefficients of the variables of the given type
+ * within the sequence of coefficients of "bmap".
+ */
+unsigned isl_basic_map_offset(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type)
+{
+ switch (type) {
+ case isl_dim_cst: return 0;
+ case isl_dim_param:
+ case isl_dim_in:
+ case isl_dim_out:
+ case isl_dim_div: return 1 + isl_basic_map_var_offset(bmap, type);
+ default: return 0;
+ }
+}
+
+unsigned isl_basic_set_offset(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type)
+{
+ return isl_basic_map_offset(bset, type);
+}
+
+static unsigned map_offset(__isl_keep isl_map *map, enum isl_dim_type type)
+{
+ return pos(map->dim, type);
+}
+
+isl_size isl_basic_set_dim(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type)
+{
+ return isl_basic_map_dim(bset, type);
+}
+
+isl_size isl_basic_set_n_dim(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_set_dim(bset, isl_dim_set);
+}
+
+isl_size isl_basic_set_n_param(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_set_dim(bset, isl_dim_param);
+}
+
+isl_size isl_basic_set_total_dim(__isl_keep const isl_basic_set *bset)
+{
+ return isl_basic_map_total_dim(const_bset_to_bmap(bset));
+}
+
+isl_size isl_set_n_dim(__isl_keep isl_set *set)
+{
+ return isl_set_dim(set, isl_dim_set);
+}
+
+isl_size isl_set_n_param(__isl_keep isl_set *set)
+{
+ return isl_set_dim(set, isl_dim_param);
+}
+
+isl_size isl_basic_map_total_dim(__isl_keep const isl_basic_map *bmap)
+{
+ isl_size dim;
+
+ if (!bmap)
+ return isl_size_error;
+ dim = isl_space_dim(bmap->dim, isl_dim_all);
+ if (dim < 0)
+ return isl_size_error;
+ return dim + bmap->n_div;
+}
+
+/* Return the number of equality constraints in the description of "bmap".
+ * Return isl_size_error on error.
+ */
+isl_size isl_basic_map_n_equality(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return isl_size_error;
+ return bmap->n_eq;
+}
+
+/* Return the number of equality constraints in the description of "bset".
+ * Return isl_size_error on error.
+ */
+isl_size isl_basic_set_n_equality(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_n_equality(bset_to_bmap(bset));
+}
+
+/* Return the number of inequality constraints in the description of "bmap".
+ * Return isl_size_error on error.
+ */
+isl_size isl_basic_map_n_inequality(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return isl_size_error;
+ return bmap->n_ineq;
+}
+
+/* Return the number of inequality constraints in the description of "bset".
+ * Return isl_size_error on error.
+ */
+isl_size isl_basic_set_n_inequality(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_n_inequality(bset_to_bmap(bset));
+}
+
+/* Do "bmap1" and "bmap2" have the same parameters?
+ */
+static isl_bool isl_basic_map_has_equal_params(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2)
+{
+ isl_space *space1, *space2;
+
+ space1 = isl_basic_map_peek_space(bmap1);
+ space2 = isl_basic_map_peek_space(bmap2);
+ return isl_space_has_equal_params(space1, space2);
+}
+
+/* Do "map1" and "map2" have the same parameters?
+ */
+isl_bool isl_map_has_equal_params(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2)
+{
+ isl_space *space1, *space2;
+
+ space1 = isl_map_peek_space(map1);
+ space2 = isl_map_peek_space(map2);
+ return isl_space_has_equal_params(space1, space2);
+}
+
+/* Do "map" and "set" have the same parameters?
+ */
+static isl_bool isl_map_set_has_equal_params(__isl_keep isl_map *map,
+ __isl_keep isl_set *set)
+{
+ return isl_map_has_equal_params(map, set_to_map(set));
+}
+
+/* Is the tuple of type "type" of "bmap" the same as the single tuple of "bset"?
+ */
+static isl_bool isl_basic_map_set_tuple_is_equal(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, __isl_keep isl_basic_set *bset)
+{
+ isl_space *bmap_space, *bset_space;
+
+ bmap_space = isl_basic_map_peek_space(bmap);
+ bset_space = isl_basic_set_peek_space(bset);
+ return isl_space_tuple_is_equal(bmap_space, type,
+ bset_space, isl_dim_set);
+}
+
+/* Is the tuple of type "type" of "map" the same as the single tuple of "set"?
+ */
+static isl_bool isl_map_set_tuple_is_equal(__isl_keep isl_map *map,
+ enum isl_dim_type type, __isl_keep isl_set *set)
+{
+ return isl_map_tuple_is_equal(map, type, set_to_map(set), isl_dim_set);
+}
+
+isl_bool isl_map_compatible_domain(__isl_keep isl_map *map,
+ __isl_keep isl_set *set)
+{
+ isl_bool m;
+ if (!map || !set)
+ return isl_bool_error;
+ m = isl_map_has_equal_params(map, set_to_map(set));
+ if (m < 0 || !m)
+ return m;
+ return isl_map_set_tuple_is_equal(map, isl_dim_in, set);
+}
+
+isl_bool isl_basic_map_compatible_domain(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_basic_set *bset)
+{
+ isl_bool m;
+ if (!bmap || !bset)
+ return isl_bool_error;
+ m = isl_basic_map_has_equal_params(bmap, bset_to_bmap(bset));
+ if (m < 0 || !m)
+ return m;
+ return isl_basic_map_set_tuple_is_equal(bmap, isl_dim_in, bset);
+}
+
+isl_bool isl_map_compatible_range(__isl_keep isl_map *map,
+ __isl_keep isl_set *set)
+{
+ isl_bool m;
+ if (!map || !set)
+ return isl_bool_error;
+ m = isl_map_has_equal_params(map, set_to_map(set));
+ if (m < 0 || !m)
+ return m;
+ return isl_map_set_tuple_is_equal(map, isl_dim_out, set);
+}
+
+isl_bool isl_basic_map_compatible_range(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_basic_set *bset)
+{
+ isl_bool m;
+ if (!bmap || !bset)
+ return isl_bool_error;
+ m = isl_basic_map_has_equal_params(bmap, bset_to_bmap(bset));
+ if (m < 0 || !m)
+ return m;
+ return isl_basic_map_set_tuple_is_equal(bmap, isl_dim_out, bset);
+}
+
+isl_ctx *isl_basic_map_get_ctx(__isl_keep isl_basic_map *bmap)
+{
+ return bmap ? bmap->ctx : NULL;
+}
+
+isl_ctx *isl_basic_set_get_ctx(__isl_keep isl_basic_set *bset)
+{
+ return bset ? bset->ctx : NULL;
+}
+
+isl_ctx *isl_map_get_ctx(__isl_keep isl_map *map)
+{
+ return map ? map->ctx : NULL;
+}
+
+isl_ctx *isl_set_get_ctx(__isl_keep isl_set *set)
+{
+ return set ? set->ctx : NULL;
+}
+
+/* Return the space of "bmap".
+ */
+__isl_keep isl_space *isl_basic_map_peek_space(
+ __isl_keep const isl_basic_map *bmap)
+{
+ return bmap ? bmap->dim : NULL;
+}
+
+/* Return the space of "bset".
+ */
+__isl_keep isl_space *isl_basic_set_peek_space(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_peek_space(bset_to_bmap(bset));
+}
+
+__isl_give isl_space *isl_basic_map_get_space(__isl_keep isl_basic_map *bmap)
+{
+ return isl_space_copy(isl_basic_map_peek_space(bmap));
+}
+
+__isl_give isl_space *isl_basic_set_get_space(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_get_space(bset_to_bmap(bset));
+}
+
+/* Return the space of "bmap".
+ * This may be either a copy or the space itself
+ * if there is only one reference to "bmap".
+ * This allows the space to be modified inplace
+ * if both the basic map and its space have only a single reference.
+ * The caller is not allowed to modify "bmap" between this call and
+ * a subsequent call to isl_basic_map_restore_space.
+ * The only exception is that isl_basic_map_free can be called instead.
+ */
+static __isl_give isl_space *isl_basic_map_take_space(
+ __isl_keep isl_basic_map *bmap)
+{
+ isl_space *space;
+
+ if (!bmap)
+ return NULL;
+ if (bmap->ref != 1)
+ return isl_basic_map_get_space(bmap);
+ space = bmap->dim;
+ bmap->dim = NULL;
+ return space;
+}
+
+/* Set the space of "bmap" to "space", where the space of "bmap" may be missing
+ * due to a preceding call to isl_basic_map_take_space.
+ * However, in this case, "bmap" only has a single reference and
+ * then the call to isl_basic_map_cow has no effect.
+ */
+static __isl_give isl_basic_map *isl_basic_map_restore_space(
+ __isl_take isl_basic_map *bmap, __isl_take isl_space *space)
+{
+ if (!bmap || !space)
+ goto error;
+
+ if (bmap->dim == space) {
+ isl_space_free(space);
+ return bmap;
+ }
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ goto error;
+ isl_space_free(bmap->dim);
+ bmap->dim = space;
+
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Extract the divs in "bmap" as a matrix.
+ */
+__isl_give isl_mat *isl_basic_map_get_divs(__isl_keep isl_basic_map *bmap)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_mat *div;
+ isl_size v_div;
+ unsigned cols;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0)
+ return NULL;
+
+ ctx = isl_basic_map_get_ctx(bmap);
+ cols = 1 + 1 + v_div + bmap->n_div;
+ div = isl_mat_alloc(ctx, bmap->n_div, cols);
+ if (!div)
+ return NULL;
+
+ for (i = 0; i < bmap->n_div; ++i)
+ isl_seq_cpy(div->row[i], bmap->div[i], cols);
+
+ return div;
+}
+
+/* Extract the divs in "bset" as a matrix.
+ */
+__isl_give isl_mat *isl_basic_set_get_divs(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_get_divs(bset);
+}
+
+__isl_give isl_local_space *isl_basic_map_get_local_space(
+ __isl_keep isl_basic_map *bmap)
+{
+ isl_mat *div;
+
+ if (!bmap)
+ return NULL;
+
+ div = isl_basic_map_get_divs(bmap);
+ return isl_local_space_alloc_div(isl_space_copy(bmap->dim), div);
+}
+
+__isl_give isl_local_space *isl_basic_set_get_local_space(
+ __isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_get_local_space(bset);
+}
+
+/* For each known div d = floor(f/m), add the constraints
+ *
+ * f - m d >= 0
+ * -(f-(m-1)) + m d >= 0
+ *
+ * Do not finalize the result.
+ */
+static __isl_give isl_basic_map *add_known_div_constraints(
+ __isl_take isl_basic_map *bmap)
+{
+ int i;
+ isl_size n_div;
+
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0)
+ return isl_basic_map_free(bmap);
+ if (n_div == 0)
+ return bmap;
+ bmap = isl_basic_map_cow(bmap);
+ bmap = isl_basic_map_extend_constraints(bmap, 0, 2 * n_div);
+ if (!bmap)
+ return NULL;
+ for (i = 0; i < n_div; ++i) {
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ bmap = isl_basic_map_add_div_constraints(bmap, i);
+ }
+
+ return bmap;
+}
+
+__isl_give isl_basic_map *isl_basic_map_from_local_space(
+ __isl_take isl_local_space *ls)
+{
+ int i;
+ isl_size n_div;
+ isl_basic_map *bmap;
+
+ n_div = isl_local_space_dim(ls, isl_dim_div);
+ if (n_div < 0)
+ ls = isl_local_space_free(ls);
+ if (!ls)
+ return NULL;
+
+ bmap = isl_basic_map_alloc_space(isl_local_space_get_space(ls),
+ n_div, 0, 2 * n_div);
+
+ for (i = 0; i < n_div; ++i)
+ if (isl_basic_map_alloc_div(bmap) < 0)
+ goto error;
+
+ for (i = 0; i < n_div; ++i)
+ isl_seq_cpy(bmap->div[i], ls->div->row[i], ls->div->n_col);
+ bmap = add_known_div_constraints(bmap);
+
+ isl_local_space_free(ls);
+ return bmap;
+error:
+ isl_local_space_free(ls);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_from_local_space(
+ __isl_take isl_local_space *ls)
+{
+ return isl_basic_map_from_local_space(ls);
+}
+
+__isl_give isl_space *isl_map_get_space(__isl_keep isl_map *map)
+{
+ return isl_space_copy(isl_map_peek_space(map));
+}
+
+__isl_give isl_space *isl_set_get_space(__isl_keep isl_set *set)
+{
+ if (!set)
+ return NULL;
+ return isl_space_copy(set->dim);
+}
+
+/* Return the space of "map".
+ * This may be either a copy or the space itself
+ * if there is only one reference to "map".
+ * This allows the space to be modified inplace
+ * if both the map and its space have only a single reference.
+ * The caller is not allowed to modify "map" between this call and
+ * a subsequent call to isl_map_restore_space.
+ * The only exception is that isl_map_free can be called instead.
+ */
+static __isl_give isl_space *isl_map_take_space(__isl_keep isl_map *map)
+{
+ isl_space *space;
+
+ if (!map)
+ return NULL;
+ if (map->ref != 1)
+ return isl_map_get_space(map);
+ space = map->dim;
+ map->dim = NULL;
+ return space;
+}
+
+/* Set the space of "map" to "space", where the space of "map" may be missing
+ * due to a preceding call to isl_map_take_space.
+ * However, in this case, "map" only has a single reference and
+ * then the call to isl_map_cow has no effect.
+ */
+static __isl_give isl_map *isl_map_restore_space(__isl_take isl_map *map,
+ __isl_take isl_space *space)
+{
+ if (!map || !space)
+ goto error;
+
+ if (map->dim == space) {
+ isl_space_free(space);
+ return map;
+ }
+
+ map = isl_map_cow(map);
+ if (!map)
+ goto error;
+ isl_space_free(map->dim);
+ map->dim = space;
+
+ return map;
+error:
+ isl_map_free(map);
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_set_tuple_name(
+ __isl_take isl_basic_map *bmap, enum isl_dim_type type, const char *s)
+{
+ isl_space *space;
+
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_set_tuple_name(space, type, s);
+ bmap = isl_basic_map_restore_space(bmap, space);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_set_tuple_name(
+ __isl_take isl_basic_set *bset, const char *s)
+{
+ return isl_basic_map_set_tuple_name(bset, isl_dim_set, s);
+}
+
+const char *isl_basic_map_get_tuple_name(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type)
+{
+ return bmap ? isl_space_get_tuple_name(bmap->dim, type) : NULL;
+}
+
+__isl_give isl_map *isl_map_set_tuple_name(__isl_take isl_map *map,
+ enum isl_dim_type type, const char *s)
+{
+ int i;
+ isl_space *space;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_set_tuple_name(map->p[i], type, s);
+ if (!map->p[i])
+ goto error;
+ }
+
+ space = isl_map_take_space(map);
+ space = isl_space_set_tuple_name(space, type, s);
+ map = isl_map_restore_space(map, space);
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Replace the identifier of the tuple of type "type" by "id".
+ */
+__isl_give isl_basic_map *isl_basic_map_set_tuple_id(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, __isl_take isl_id *id)
+{
+ isl_space *space;
+
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_set_tuple_id(space, type, id);
+ bmap = isl_basic_map_restore_space(bmap, space);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+}
+
+/* Replace the identifier of the tuple by "id".
+ */
+__isl_give isl_basic_set *isl_basic_set_set_tuple_id(
+ __isl_take isl_basic_set *bset, __isl_take isl_id *id)
+{
+ return isl_basic_map_set_tuple_id(bset, isl_dim_set, id);
+}
+
+/* Does the input or output tuple have a name?
+ */
+isl_bool isl_map_has_tuple_name(__isl_keep isl_map *map, enum isl_dim_type type)
+{
+ return map ? isl_space_has_tuple_name(map->dim, type) : isl_bool_error;
+}
+
+const char *isl_map_get_tuple_name(__isl_keep isl_map *map,
+ enum isl_dim_type type)
+{
+ return map ? isl_space_get_tuple_name(map->dim, type) : NULL;
+}
+
+__isl_give isl_set *isl_set_set_tuple_name(__isl_take isl_set *set,
+ const char *s)
+{
+ return set_from_map(isl_map_set_tuple_name(set_to_map(set),
+ isl_dim_set, s));
+}
+
+__isl_give isl_map *isl_map_set_tuple_id(__isl_take isl_map *map,
+ enum isl_dim_type type, __isl_take isl_id *id)
+{
+ isl_space *space;
+
+ space = isl_map_take_space(map);
+ space = isl_space_set_tuple_id(space, type, id);
+ map = isl_map_restore_space(map, space);
+
+ return isl_map_reset_space(map, isl_map_get_space(map));
+}
+
+/* Replace the identifier of the domain tuple of "map" by "id".
+ */
+__isl_give isl_map *isl_map_set_domain_tuple_id(__isl_take isl_map *map,
+ __isl_take isl_id *id)
+{
+ return isl_map_set_tuple_id(map, isl_dim_in, id);
+}
+
+/* Replace the identifier of the range tuple of "map" by "id".
+ */
+__isl_give isl_map *isl_map_set_range_tuple_id(__isl_take isl_map *map,
+ __isl_take isl_id *id)
+{
+ return isl_map_set_tuple_id(map, isl_dim_out, id);
+}
+
+__isl_give isl_set *isl_set_set_tuple_id(__isl_take isl_set *set,
+ __isl_take isl_id *id)
+{
+ return isl_map_set_tuple_id(set, isl_dim_set, id);
+}
+
+__isl_give isl_map *isl_map_reset_tuple_id(__isl_take isl_map *map,
+ enum isl_dim_type type)
+{
+ isl_space *space;
+
+ space = isl_map_take_space(map);
+ space = isl_space_reset_tuple_id(space, type);
+ map = isl_map_restore_space(map, space);
+
+ return isl_map_reset_space(map, isl_map_get_space(map));
+}
+
+__isl_give isl_set *isl_set_reset_tuple_id(__isl_take isl_set *set)
+{
+ return isl_map_reset_tuple_id(set, isl_dim_set);
+}
+
+isl_bool isl_map_has_tuple_id(__isl_keep isl_map *map, enum isl_dim_type type)
+{
+ return map ? isl_space_has_tuple_id(map->dim, type) : isl_bool_error;
+}
+
+/* Does the domain tuple of "map" have an identifier?
+ */
+isl_bool isl_map_has_domain_tuple_id(__isl_keep isl_map *map)
+{
+ return isl_map_has_tuple_id(map, isl_dim_in);
+}
+
+/* Does the range tuple of "map" have an identifier?
+ */
+isl_bool isl_map_has_range_tuple_id(__isl_keep isl_map *map)
+{
+ return isl_map_has_tuple_id(map, isl_dim_out);
+}
+
+__isl_give isl_id *isl_map_get_tuple_id(__isl_keep isl_map *map,
+ enum isl_dim_type type)
+{
+ return map ? isl_space_get_tuple_id(map->dim, type) : NULL;
+}
+
+/* Return the identifier of the domain tuple of "map", assuming it has one.
+ */
+__isl_give isl_id *isl_map_get_domain_tuple_id(__isl_keep isl_map *map)
+{
+ return isl_map_get_tuple_id(map, isl_dim_in);
+}
+
+/* Return the identifier of the range tuple of "map", assuming it has one.
+ */
+__isl_give isl_id *isl_map_get_range_tuple_id(__isl_keep isl_map *map)
+{
+ return isl_map_get_tuple_id(map, isl_dim_out);
+}
+
+isl_bool isl_set_has_tuple_id(__isl_keep isl_set *set)
+{
+ return isl_map_has_tuple_id(set, isl_dim_set);
+}
+
+__isl_give isl_id *isl_set_get_tuple_id(__isl_keep isl_set *set)
+{
+ return isl_map_get_tuple_id(set, isl_dim_set);
+}
+
+/* Does the set tuple have a name?
+ */
+isl_bool isl_set_has_tuple_name(__isl_keep isl_set *set)
+{
+ if (!set)
+ return isl_bool_error;
+ return isl_space_has_tuple_name(set->dim, isl_dim_set);
+}
+
+
+const char *isl_basic_set_get_tuple_name(__isl_keep isl_basic_set *bset)
+{
+ return bset ? isl_space_get_tuple_name(bset->dim, isl_dim_set) : NULL;
+}
+
+const char *isl_set_get_tuple_name(__isl_keep isl_set *set)
+{
+ return set ? isl_space_get_tuple_name(set->dim, isl_dim_set) : NULL;
+}
+
+const char *isl_basic_map_get_dim_name(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos)
+{
+ return bmap ? isl_space_get_dim_name(bmap->dim, type, pos) : NULL;
+}
+
+const char *isl_basic_set_get_dim_name(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos)
+{
+ return bset ? isl_space_get_dim_name(bset->dim, type, pos) : NULL;
+}
+
+/* Does the given dimension have a name?
+ */
+isl_bool isl_map_has_dim_name(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos)
+{
+ if (!map)
+ return isl_bool_error;
+ return isl_space_has_dim_name(map->dim, type, pos);
+}
+
+const char *isl_map_get_dim_name(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos)
+{
+ return map ? isl_space_get_dim_name(map->dim, type, pos) : NULL;
+}
+
+const char *isl_set_get_dim_name(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos)
+{
+ return set ? isl_space_get_dim_name(set->dim, type, pos) : NULL;
+}
+
+/* Does the given dimension have a name?
+ */
+isl_bool isl_set_has_dim_name(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos)
+{
+ if (!set)
+ return isl_bool_error;
+ return isl_space_has_dim_name(set->dim, type, pos);
+}
+
+__isl_give isl_basic_map *isl_basic_map_set_dim_name(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ isl_space *space;
+
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_set_dim_name(space, type, pos, s);
+ bmap = isl_basic_map_restore_space(bmap, space);
+ return isl_basic_map_finalize(bmap);
+}
+
+__isl_give isl_map *isl_map_set_dim_name(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ int i;
+ isl_space *space;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_set_dim_name(map->p[i], type, pos, s);
+ if (!map->p[i])
+ goto error;
+ }
+
+ space = isl_map_take_space(map);
+ space = isl_space_set_dim_name(space, type, pos, s);
+ map = isl_map_restore_space(map, space);
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_set_dim_name(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ return bset_from_bmap(isl_basic_map_set_dim_name(bset_to_bmap(bset),
+ type, pos, s));
+}
+
+__isl_give isl_set *isl_set_set_dim_name(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ return set_from_map(isl_map_set_dim_name(set_to_map(set),
+ type, pos, s));
+}
+
+isl_bool isl_basic_map_has_dim_id(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos)
+{
+ if (!bmap)
+ return isl_bool_error;
+ return isl_space_has_dim_id(bmap->dim, type, pos);
+}
+
+__isl_give isl_id *isl_basic_set_get_dim_id(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos)
+{
+ return bset ? isl_space_get_dim_id(bset->dim, type, pos) : NULL;
+}
+
+isl_bool isl_map_has_dim_id(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos)
+{
+ return map ? isl_space_has_dim_id(map->dim, type, pos) : isl_bool_error;
+}
+
+__isl_give isl_id *isl_map_get_dim_id(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos)
+{
+ return map ? isl_space_get_dim_id(map->dim, type, pos) : NULL;
+}
+
+isl_bool isl_set_has_dim_id(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos)
+{
+ return isl_map_has_dim_id(set, type, pos);
+}
+
+__isl_give isl_id *isl_set_get_dim_id(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos)
+{
+ return isl_map_get_dim_id(set, type, pos);
+}
+
+__isl_give isl_map *isl_map_set_dim_id(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id)
+{
+ isl_space *space;
+
+ space = isl_map_take_space(map);
+ space = isl_space_set_dim_id(space, type, pos, id);
+ map = isl_map_restore_space(map, space);
+
+ return isl_map_reset_space(map, isl_map_get_space(map));
+}
+
+__isl_give isl_set *isl_set_set_dim_id(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id)
+{
+ return isl_map_set_dim_id(set, type, pos, id);
+}
+
+int isl_map_find_dim_by_id(__isl_keep isl_map *map, enum isl_dim_type type,
+ __isl_keep isl_id *id)
+{
+ if (!map)
+ return -1;
+ return isl_space_find_dim_by_id(map->dim, type, id);
+}
+
+int isl_set_find_dim_by_id(__isl_keep isl_set *set, enum isl_dim_type type,
+ __isl_keep isl_id *id)
+{
+ return isl_map_find_dim_by_id(set, type, id);
+}
+
+/* Return the position of the dimension of the given type and name
+ * in "bmap".
+ * Return -1 if no such dimension can be found.
+ */
+int isl_basic_map_find_dim_by_name(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, const char *name)
+{
+ if (!bmap)
+ return -1;
+ return isl_space_find_dim_by_name(bmap->dim, type, name);
+}
+
+int isl_map_find_dim_by_name(__isl_keep isl_map *map, enum isl_dim_type type,
+ const char *name)
+{
+ if (!map)
+ return -1;
+ return isl_space_find_dim_by_name(map->dim, type, name);
+}
+
+int isl_set_find_dim_by_name(__isl_keep isl_set *set, enum isl_dim_type type,
+ const char *name)
+{
+ return isl_map_find_dim_by_name(set, type, name);
+}
+
+/* Check whether equality i of bset is a pure stride constraint
+ * on a single dimension, i.e., of the form
+ *
+ * v = k e
+ *
+ * with k a constant and e an existentially quantified variable.
+ */
+isl_bool isl_basic_set_eq_is_stride(__isl_keep isl_basic_set *bset, int i)
+{
+ isl_size nparam;
+ isl_size d;
+ isl_size n_div;
+ int pos1;
+ int pos2;
+
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ d = isl_basic_set_dim(bset, isl_dim_set);
+ n_div = isl_basic_set_dim(bset, isl_dim_div);
+ if (nparam < 0 || d < 0 || n_div < 0)
+ return isl_bool_error;
+
+ if (!isl_int_is_zero(bset->eq[i][0]))
+ return isl_bool_false;
+
+ if (isl_seq_first_non_zero(bset->eq[i] + 1, nparam) != -1)
+ return isl_bool_false;
+ pos1 = isl_seq_first_non_zero(bset->eq[i] + 1 + nparam, d);
+ if (pos1 == -1)
+ return isl_bool_false;
+ if (isl_seq_first_non_zero(bset->eq[i] + 1 + nparam + pos1 + 1,
+ d - pos1 - 1) != -1)
+ return isl_bool_false;
+
+ pos2 = isl_seq_first_non_zero(bset->eq[i] + 1 + nparam + d, n_div);
+ if (pos2 == -1)
+ return isl_bool_false;
+ if (isl_seq_first_non_zero(bset->eq[i] + 1 + nparam + d + pos2 + 1,
+ n_div - pos2 - 1) != -1)
+ return isl_bool_false;
+ if (!isl_int_is_one(bset->eq[i][1 + nparam + pos1]) &&
+ !isl_int_is_negone(bset->eq[i][1 + nparam + pos1]))
+ return isl_bool_false;
+
+ return isl_bool_true;
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * of the space of "map".
+ */
+__isl_give isl_map *isl_map_reset_user(__isl_take isl_map *map)
+{
+ isl_space *space;
+
+ space = isl_map_get_space(map);
+ space = isl_space_reset_user(space);
+ map = isl_map_reset_space(map, space);
+
+ return map;
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * of the space of "set".
+ */
+__isl_give isl_set *isl_set_reset_user(__isl_take isl_set *set)
+{
+ return isl_map_reset_user(set);
+}
+
+isl_bool isl_basic_map_is_rational(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return isl_bool_error;
+ return ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL);
+}
+
+/* Has "map" been marked as a rational map?
+ * In particular, have all basic maps in "map" been marked this way?
+ * An empty map is not considered to be rational.
+ * Maps where only some of the basic maps are marked rational
+ * are not allowed.
+ */
+isl_bool isl_map_is_rational(__isl_keep isl_map *map)
+{
+ int i;
+ isl_bool rational;
+
+ if (!map)
+ return isl_bool_error;
+ if (map->n == 0)
+ return isl_bool_false;
+ rational = isl_basic_map_is_rational(map->p[0]);
+ if (rational < 0)
+ return rational;
+ for (i = 1; i < map->n; ++i) {
+ isl_bool rational_i;
+
+ rational_i = isl_basic_map_is_rational(map->p[i]);
+ if (rational_i < 0)
+ return rational_i;
+ if (rational != rational_i)
+ isl_die(isl_map_get_ctx(map), isl_error_unsupported,
+ "mixed rational and integer basic maps "
+ "not supported", return isl_bool_error);
+ }
+
+ return rational;
+}
+
+/* Has "set" been marked as a rational set?
+ * In particular, have all basic set in "set" been marked this way?
+ * An empty set is not considered to be rational.
+ * Sets where only some of the basic sets are marked rational
+ * are not allowed.
+ */
+isl_bool isl_set_is_rational(__isl_keep isl_set *set)
+{
+ return isl_map_is_rational(set);
+}
+
+int isl_basic_set_is_rational(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_is_rational(bset);
+}
+
+/* Does "bmap" contain any rational points?
+ *
+ * If "bmap" has an equality for each dimension, equating the dimension
+ * to an integer constant, then it has no rational points, even if it
+ * is marked as rational.
+ */
+isl_bool isl_basic_map_has_rational(__isl_keep isl_basic_map *bmap)
+{
+ isl_bool has_rational = isl_bool_true;
+ isl_size total;
+
+ if (!bmap)
+ return isl_bool_error;
+ if (isl_basic_map_plain_is_empty(bmap))
+ return isl_bool_false;
+ if (!isl_basic_map_is_rational(bmap))
+ return isl_bool_false;
+ bmap = isl_basic_map_copy(bmap);
+ bmap = isl_basic_map_implicit_equalities(bmap);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ if (bmap->n_eq == total) {
+ int i, j;
+ for (i = 0; i < bmap->n_eq; ++i) {
+ j = isl_seq_first_non_zero(bmap->eq[i] + 1, total);
+ if (j < 0)
+ break;
+ if (!isl_int_is_one(bmap->eq[i][1 + j]) &&
+ !isl_int_is_negone(bmap->eq[i][1 + j]))
+ break;
+ j = isl_seq_first_non_zero(bmap->eq[i] + 1 + j + 1,
+ total - j - 1);
+ if (j >= 0)
+ break;
+ }
+ if (i == bmap->n_eq)
+ has_rational = isl_bool_false;
+ }
+ isl_basic_map_free(bmap);
+
+ return has_rational;
+}
+
+/* Does "map" contain any rational points?
+ */
+isl_bool isl_map_has_rational(__isl_keep isl_map *map)
+{
+ int i;
+ isl_bool has_rational;
+
+ if (!map)
+ return isl_bool_error;
+ for (i = 0; i < map->n; ++i) {
+ has_rational = isl_basic_map_has_rational(map->p[i]);
+ if (has_rational < 0 || has_rational)
+ return has_rational;
+ }
+ return isl_bool_false;
+}
+
+/* Does "set" contain any rational points?
+ */
+isl_bool isl_set_has_rational(__isl_keep isl_set *set)
+{
+ return isl_map_has_rational(set);
+}
+
+/* Is this basic set a parameter domain?
+ */
+isl_bool isl_basic_set_is_params(__isl_keep isl_basic_set *bset)
+{
+ if (!bset)
+ return isl_bool_error;
+ return isl_space_is_params(bset->dim);
+}
+
+/* Is this set a parameter domain?
+ */
+isl_bool isl_set_is_params(__isl_keep isl_set *set)
+{
+ if (!set)
+ return isl_bool_error;
+ return isl_space_is_params(set->dim);
+}
+
+/* Is this map actually a parameter domain?
+ * Users should never call this function. Outside of isl,
+ * a map can never be a parameter domain.
+ */
+isl_bool isl_map_is_params(__isl_keep isl_map *map)
+{
+ if (!map)
+ return isl_bool_error;
+ return isl_space_is_params(map->dim);
+}
+
+static __isl_give isl_basic_map *basic_map_init(isl_ctx *ctx,
+ __isl_take isl_basic_map *bmap, unsigned extra,
+ unsigned n_eq, unsigned n_ineq)
+{
+ int i;
+ isl_space *space = isl_basic_map_peek_space(bmap);
+ isl_size n_var = isl_space_dim(space, isl_dim_all);
+ size_t row_size = 1 + n_var + extra;
+
+ bmap->ctx = ctx;
+ isl_ctx_ref(ctx);
+
+ if (n_var < 0)
+ return isl_basic_map_free(bmap);
+
+ bmap->block = isl_blk_alloc(ctx, (n_ineq + n_eq) * row_size);
+ if (isl_blk_is_error(bmap->block))
+ goto error;
+
+ bmap->ineq = isl_alloc_array(ctx, isl_int *, n_ineq + n_eq);
+ if ((n_ineq + n_eq) && !bmap->ineq)
+ goto error;
+
+ if (extra == 0) {
+ bmap->block2 = isl_blk_empty();
+ bmap->div = NULL;
+ } else {
+ bmap->block2 = isl_blk_alloc(ctx, extra * (1 + row_size));
+ if (isl_blk_is_error(bmap->block2))
+ goto error;
+
+ bmap->div = isl_alloc_array(ctx, isl_int *, extra);
+ if (!bmap->div)
+ goto error;
+ }
+
+ for (i = 0; i < n_ineq + n_eq; ++i)
+ bmap->ineq[i] = bmap->block.data + i * row_size;
+
+ for (i = 0; i < extra; ++i)
+ bmap->div[i] = bmap->block2.data + i * (1 + row_size);
+
+ bmap->ref = 1;
+ bmap->flags = 0;
+ bmap->c_size = n_eq + n_ineq;
+ bmap->eq = bmap->ineq + n_ineq;
+ bmap->extra = extra;
+ bmap->n_eq = 0;
+ bmap->n_ineq = 0;
+ bmap->n_div = 0;
+ bmap->sample = NULL;
+
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_alloc(isl_ctx *ctx,
+ unsigned nparam, unsigned dim, unsigned extra,
+ unsigned n_eq, unsigned n_ineq)
+{
+ struct isl_basic_map *bmap;
+ isl_space *space;
+
+ space = isl_space_set_alloc(ctx, nparam, dim);
+ if (!space)
+ return NULL;
+
+ bmap = isl_basic_map_alloc_space(space, extra, n_eq, n_ineq);
+ return bset_from_bmap(bmap);
+}
+
+__isl_give isl_basic_set *isl_basic_set_alloc_space(__isl_take isl_space *space,
+ unsigned extra, unsigned n_eq, unsigned n_ineq)
+{
+ struct isl_basic_map *bmap;
+ if (!space)
+ return NULL;
+ isl_assert(space->ctx, space->n_in == 0, goto error);
+ bmap = isl_basic_map_alloc_space(space, extra, n_eq, n_ineq);
+ return bset_from_bmap(bmap);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_alloc_space(__isl_take isl_space *space,
+ unsigned extra, unsigned n_eq, unsigned n_ineq)
+{
+ struct isl_basic_map *bmap;
+
+ if (!space)
+ return NULL;
+ bmap = isl_calloc_type(space->ctx, struct isl_basic_map);
+ if (!bmap)
+ goto error;
+ bmap->dim = space;
+
+ return basic_map_init(space->ctx, bmap, extra, n_eq, n_ineq);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_alloc(isl_ctx *ctx,
+ unsigned nparam, unsigned in, unsigned out, unsigned extra,
+ unsigned n_eq, unsigned n_ineq)
+{
+ struct isl_basic_map *bmap;
+ isl_space *space;
+
+ space = isl_space_alloc(ctx, nparam, in, out);
+ if (!space)
+ return NULL;
+
+ bmap = isl_basic_map_alloc_space(space, extra, n_eq, n_ineq);
+ return bmap;
+}
+
+static __isl_give isl_basic_map *dup_constraints(__isl_take isl_basic_map *dst,
+ __isl_keep isl_basic_map *src)
+{
+ int i;
+ isl_size total = isl_basic_map_dim(src, isl_dim_all);
+
+ if (!dst || total < 0)
+ return isl_basic_map_free(dst);
+
+ for (i = 0; i < src->n_eq; ++i) {
+ int j = isl_basic_map_alloc_equality(dst);
+ if (j < 0)
+ return isl_basic_map_free(dst);
+ isl_seq_cpy(dst->eq[j], src->eq[i], 1+total);
+ }
+
+ for (i = 0; i < src->n_ineq; ++i) {
+ int j = isl_basic_map_alloc_inequality(dst);
+ if (j < 0)
+ return isl_basic_map_free(dst);
+ isl_seq_cpy(dst->ineq[j], src->ineq[i], 1+total);
+ }
+
+ for (i = 0; i < src->n_div; ++i) {
+ int j = isl_basic_map_alloc_div(dst);
+ if (j < 0)
+ return isl_basic_map_free(dst);
+ isl_seq_cpy(dst->div[j], src->div[i], 1+1+total);
+ }
+ ISL_F_SET(dst, ISL_BASIC_SET_FINAL);
+ return dst;
+}
+
+__isl_give isl_basic_map *isl_basic_map_dup(__isl_keep isl_basic_map *bmap)
+{
+ struct isl_basic_map *dup;
+
+ if (!bmap)
+ return NULL;
+ dup = isl_basic_map_alloc_space(isl_space_copy(bmap->dim),
+ bmap->n_div, bmap->n_eq, bmap->n_ineq);
+ dup = dup_constraints(dup, bmap);
+ if (!dup)
+ return NULL;
+ dup->flags = bmap->flags;
+ dup->sample = isl_vec_copy(bmap->sample);
+ return dup;
+}
+
+__isl_give isl_basic_set *isl_basic_set_dup(__isl_keep isl_basic_set *bset)
+{
+ struct isl_basic_map *dup;
+
+ dup = isl_basic_map_dup(bset_to_bmap(bset));
+ return bset_from_bmap(dup);
+}
+
+__isl_give isl_basic_set *isl_basic_set_copy(__isl_keep isl_basic_set *bset)
+{
+ if (!bset)
+ return NULL;
+
+ if (ISL_F_ISSET(bset, ISL_BASIC_SET_FINAL)) {
+ bset->ref++;
+ return bset;
+ }
+ return isl_basic_set_dup(bset);
+}
+
+__isl_give isl_set *isl_set_copy(__isl_keep isl_set *set)
+{
+ if (!set)
+ return NULL;
+
+ set->ref++;
+ return set;
+}
+
+__isl_give isl_basic_map *isl_basic_map_copy(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return NULL;
+
+ if (ISL_F_ISSET(bmap, ISL_BASIC_SET_FINAL)) {
+ bmap->ref++;
+ return bmap;
+ }
+ bmap = isl_basic_map_dup(bmap);
+ if (bmap)
+ ISL_F_SET(bmap, ISL_BASIC_SET_FINAL);
+ return bmap;
+}
+
+__isl_give isl_map *isl_map_copy(__isl_keep isl_map *map)
+{
+ if (!map)
+ return NULL;
+
+ map->ref++;
+ return map;
+}
+
+__isl_null isl_basic_map *isl_basic_map_free(__isl_take isl_basic_map *bmap)
+{
+ if (!bmap)
+ return NULL;
+
+ if (--bmap->ref > 0)
+ return NULL;
+
+ isl_ctx_deref(bmap->ctx);
+ free(bmap->div);
+ isl_blk_free(bmap->ctx, bmap->block2);
+ free(bmap->ineq);
+ isl_blk_free(bmap->ctx, bmap->block);
+ isl_vec_free(bmap->sample);
+ isl_space_free(bmap->dim);
+ free(bmap);
+
+ return NULL;
+}
+
+__isl_null isl_basic_set *isl_basic_set_free(__isl_take isl_basic_set *bset)
+{
+ return isl_basic_map_free(bset_to_bmap(bset));
+}
+
+static int room_for_con(__isl_keep isl_basic_map *bmap, unsigned n)
+{
+ return bmap->n_eq + bmap->n_ineq + n <= bmap->c_size;
+}
+
+/* Check that "bset" does not involve any parameters.
+ */
+isl_stat isl_basic_set_check_no_params(__isl_keep isl_basic_set *bset)
+{
+ isl_size nparam;
+
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ if (nparam < 0)
+ return isl_stat_error;
+ if (nparam != 0)
+ isl_die(isl_basic_set_get_ctx(bset), isl_error_invalid,
+ "basic set should not have any parameters",
+ return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Check that "bset" does not involve any local variables.
+ */
+isl_stat isl_basic_set_check_no_locals(__isl_keep isl_basic_set *bset)
+{
+ isl_size n_div;
+
+ n_div = isl_basic_set_dim(bset, isl_dim_div);
+ if (n_div < 0)
+ return isl_stat_error;
+ if (n_div != 0)
+ isl_die(isl_basic_set_get_ctx(bset), isl_error_invalid,
+ "basic set should not have any local variables",
+ return isl_stat_error);
+ return isl_stat_ok;
+}
+
+#undef TYPE
+#define TYPE isl_map
+
+#include "isl_check_named_params_templ.c"
+
+#undef TYPE
+#define TYPE isl_basic_map
+
+static
+#include "isl_check_named_params_templ.c"
+
+/* Check that "bmap1" and "bmap2" have the same parameters,
+ * reporting an error if they do not.
+ */
+static isl_stat isl_basic_map_check_equal_params(
+ __isl_keep isl_basic_map *bmap1, __isl_keep isl_basic_map *bmap2)
+{
+ isl_bool match;
+
+ match = isl_basic_map_has_equal_params(bmap1, bmap2);
+ if (match < 0)
+ return isl_stat_error;
+ if (!match)
+ isl_die(isl_basic_map_get_ctx(bmap1), isl_error_invalid,
+ "parameters don't match", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+#undef TYPE
+#define TYPE isl_map
+
+#include "isl_align_params_bin_templ.c"
+
+#undef SUFFIX
+#define SUFFIX set
+#undef ARG1
+#define ARG1 isl_map
+#undef ARG2
+#define ARG2 isl_set
+
+#include "isl_align_params_templ.c"
+
+isl_bool isl_map_align_params_map_map_and_test(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2,
+ isl_bool (*fn)(__isl_keep isl_map *map1, __isl_keep isl_map *map2))
+{
+ isl_bool r;
+
+ if (!map1 || !map2)
+ return isl_bool_error;
+ if (isl_map_has_equal_params(map1, map2))
+ return fn(map1, map2);
+ if (isl_map_check_named_params(map1) < 0)
+ return isl_bool_error;
+ if (isl_map_check_named_params(map2) < 0)
+ return isl_bool_error;
+ map1 = isl_map_copy(map1);
+ map2 = isl_map_copy(map2);
+ map1 = isl_map_align_params(map1, isl_map_get_space(map2));
+ map2 = isl_map_align_params(map2, isl_map_get_space(map1));
+ r = fn(map1, map2);
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return r;
+}
+
+int isl_basic_map_alloc_equality(__isl_keep isl_basic_map *bmap)
+{
+ isl_size total;
+ struct isl_ctx *ctx;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return -1;
+ ctx = bmap->ctx;
+ isl_assert(ctx, room_for_con(bmap, 1), return -1);
+ isl_assert(ctx, (bmap->eq - bmap->ineq) + bmap->n_eq <= bmap->c_size,
+ return -1);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_REDUNDANT);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_IMPLICIT);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_ALL_EQUALITIES);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS);
+ if ((bmap->eq - bmap->ineq) + bmap->n_eq == bmap->c_size) {
+ isl_int *t;
+ int j = isl_basic_map_alloc_inequality(bmap);
+ if (j < 0)
+ return -1;
+ t = bmap->ineq[j];
+ bmap->ineq[j] = bmap->ineq[bmap->n_ineq - 1];
+ bmap->ineq[bmap->n_ineq - 1] = bmap->eq[-1];
+ bmap->eq[-1] = t;
+ bmap->n_eq++;
+ bmap->n_ineq--;
+ bmap->eq--;
+ return 0;
+ }
+ isl_seq_clr(bmap->eq[bmap->n_eq] + 1 + total,
+ bmap->extra - bmap->n_div);
+ return bmap->n_eq++;
+}
+
+int isl_basic_set_alloc_equality(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_alloc_equality(bset_to_bmap(bset));
+}
+
+__isl_give isl_basic_map *isl_basic_map_free_equality(
+ __isl_take isl_basic_map *bmap, unsigned n)
+{
+ if (!bmap)
+ return NULL;
+ if (n > bmap->n_eq)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "invalid number of equalities",
+ isl_basic_map_free(bmap));
+ bmap->n_eq -= n;
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_free_equality(
+ __isl_take isl_basic_set *bset, unsigned n)
+{
+ return bset_from_bmap(isl_basic_map_free_equality(bset_to_bmap(bset),
+ n));
+}
+
+/* Drop the equality constraint at position "pos",
+ * preserving the order of the other equality constraints.
+ */
+int isl_basic_map_drop_equality(__isl_keep isl_basic_map *bmap, unsigned pos)
+{
+ isl_int *t;
+ int r;
+
+ if (!bmap)
+ return -1;
+ isl_assert(bmap->ctx, pos < bmap->n_eq, return -1);
+
+ t = bmap->eq[pos];
+ bmap->n_eq--;
+ for (r = pos; r < bmap->n_eq; ++r)
+ bmap->eq[r] = bmap->eq[r + 1];
+ bmap->eq[bmap->n_eq] = t;
+
+ return 0;
+}
+
+/* Turn inequality "pos" of "bmap" into an equality.
+ *
+ * In particular, we move the inequality in front of the equalities
+ * and move the last inequality in the position of the moved inequality.
+ * Note that isl_tab_make_equalities_explicit depends on this particular
+ * change in the ordering of the constraints.
+ */
+void isl_basic_map_inequality_to_equality(
+ __isl_keep isl_basic_map *bmap, unsigned pos)
+{
+ isl_int *t;
+
+ t = bmap->ineq[pos];
+ bmap->ineq[pos] = bmap->ineq[bmap->n_ineq - 1];
+ bmap->ineq[bmap->n_ineq - 1] = bmap->eq[-1];
+ bmap->eq[-1] = t;
+ bmap->n_eq++;
+ bmap->n_ineq--;
+ bmap->eq--;
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_REDUNDANT);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_SORTED);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_ALL_EQUALITIES);
+}
+
+static int room_for_ineq(__isl_keep isl_basic_map *bmap, unsigned n)
+{
+ return bmap->n_ineq + n <= bmap->eq - bmap->ineq;
+}
+
+int isl_basic_map_alloc_inequality(__isl_keep isl_basic_map *bmap)
+{
+ isl_size total;
+ struct isl_ctx *ctx;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return -1;
+ ctx = bmap->ctx;
+ isl_assert(ctx, room_for_ineq(bmap, 1), return -1);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_IMPLICIT);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_REDUNDANT);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_SORTED);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_ALL_EQUALITIES);
+ isl_seq_clr(bmap->ineq[bmap->n_ineq] + 1 + total,
+ bmap->extra - bmap->n_div);
+ return bmap->n_ineq++;
+}
+
+int isl_basic_set_alloc_inequality(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_alloc_inequality(bset_to_bmap(bset));
+}
+
+__isl_give isl_basic_map *isl_basic_map_free_inequality(
+ __isl_take isl_basic_map *bmap, unsigned n)
+{
+ if (!bmap)
+ return NULL;
+ if (n > bmap->n_ineq)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "invalid number of inequalities",
+ return isl_basic_map_free(bmap));
+ bmap->n_ineq -= n;
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_free_inequality(
+ __isl_take isl_basic_set *bset, unsigned n)
+{
+ return bset_from_bmap(isl_basic_map_free_inequality(bset_to_bmap(bset),
+ n));
+}
+
+int isl_basic_map_drop_inequality(__isl_keep isl_basic_map *bmap, unsigned pos)
+{
+ isl_int *t;
+ if (!bmap)
+ return -1;
+ isl_assert(bmap->ctx, pos < bmap->n_ineq, return -1);
+
+ if (pos != bmap->n_ineq - 1) {
+ t = bmap->ineq[pos];
+ bmap->ineq[pos] = bmap->ineq[bmap->n_ineq - 1];
+ bmap->ineq[bmap->n_ineq - 1] = t;
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_SORTED);
+ }
+ bmap->n_ineq--;
+ return 0;
+}
+
+int isl_basic_set_drop_inequality(__isl_keep isl_basic_set *bset, unsigned pos)
+{
+ return isl_basic_map_drop_inequality(bset_to_bmap(bset), pos);
+}
+
+__isl_give isl_basic_map *isl_basic_map_add_eq(__isl_take isl_basic_map *bmap,
+ isl_int *eq)
+{
+ isl_bool empty;
+ isl_size total;
+ int k;
+
+ empty = isl_basic_map_plain_is_empty(bmap);
+ if (empty < 0)
+ return isl_basic_map_free(bmap);
+ if (empty)
+ return bmap;
+
+ bmap = isl_basic_map_cow(bmap);
+ bmap = isl_basic_map_extend_constraints(bmap, 1, 0);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ k = isl_basic_map_alloc_equality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bmap->eq[k], eq, 1 + total);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_add_eq(__isl_take isl_basic_set *bset,
+ isl_int *eq)
+{
+ return bset_from_bmap(isl_basic_map_add_eq(bset_to_bmap(bset), eq));
+}
+
+__isl_give isl_basic_map *isl_basic_map_add_ineq(__isl_take isl_basic_map *bmap,
+ isl_int *ineq)
+{
+ isl_size total;
+ int k;
+
+ bmap = isl_basic_map_cow(bmap);
+ bmap = isl_basic_map_extend_constraints(bmap, 0, 1);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bmap->ineq[k], ineq, 1 + total);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_add_ineq(__isl_take isl_basic_set *bset,
+ isl_int *ineq)
+{
+ return bset_from_bmap(isl_basic_map_add_ineq(bset_to_bmap(bset), ineq));
+}
+
+int isl_basic_map_alloc_div(__isl_keep isl_basic_map *bmap)
+{
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return -1;
+ isl_assert(bmap->ctx, bmap->n_div < bmap->extra, return -1);
+ isl_seq_clr(bmap->div[bmap->n_div] + 1 + 1 + total,
+ bmap->extra - bmap->n_div);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS);
+ return bmap->n_div++;
+}
+
+int isl_basic_set_alloc_div(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_alloc_div(bset_to_bmap(bset));
+}
+
+#undef TYPE
+#define TYPE isl_basic_map
+#include "check_type_range_templ.c"
+
+/* Check that there are "n" dimensions of type "type" starting at "first"
+ * in "bset".
+ */
+isl_stat isl_basic_set_check_range(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return isl_basic_map_check_range(bset_to_bmap(bset),
+ type, first, n);
+}
+
+/* Insert an extra integer division, prescribed by "div", to "bmap"
+ * at (integer division) position "pos".
+ *
+ * The integer division is first added at the end and then moved
+ * into the right position.
+ */
+__isl_give isl_basic_map *isl_basic_map_insert_div(
+ __isl_take isl_basic_map *bmap, int pos, __isl_keep isl_vec *div)
+{
+ int i, k;
+ isl_size total;
+
+ bmap = isl_basic_map_cow(bmap);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0 || !div)
+ return isl_basic_map_free(bmap);
+
+ if (div->size != 1 + 1 + total)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "unexpected size", return isl_basic_map_free(bmap));
+ if (isl_basic_map_check_range(bmap, isl_dim_div, pos, 0) < 0)
+ return isl_basic_map_free(bmap);
+
+ bmap = isl_basic_map_extend(bmap, 1, 0, 2);
+ k = isl_basic_map_alloc_div(bmap);
+ if (k < 0)
+ return isl_basic_map_free(bmap);
+ isl_seq_cpy(bmap->div[k], div->el, div->size);
+ isl_int_set_si(bmap->div[k][div->size], 0);
+
+ for (i = k; i > pos; --i)
+ bmap = isl_basic_map_swap_div(bmap, i, i - 1);
+
+ return bmap;
+}
+
+isl_stat isl_basic_map_free_div(__isl_keep isl_basic_map *bmap, unsigned n)
+{
+ if (!bmap)
+ return isl_stat_error;
+ isl_assert(bmap->ctx, n <= bmap->n_div, return isl_stat_error);
+ bmap->n_div -= n;
+ return isl_stat_ok;
+}
+
+static __isl_give isl_basic_map *add_constraints(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2,
+ unsigned i_pos, unsigned o_pos)
+{
+ isl_size total, n_param, n_in, n_out, n_div;
+ unsigned o_in, o_out;
+ isl_ctx *ctx;
+ isl_space *space;
+ struct isl_dim_map *dim_map;
+
+ space = isl_basic_map_peek_space(bmap2);
+ if (!bmap1 || !space)
+ goto error;
+
+ total = isl_basic_map_dim(bmap1, isl_dim_all);
+ n_param = isl_basic_map_dim(bmap2, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap2, isl_dim_in);
+ o_in = isl_basic_map_offset(bmap1, isl_dim_in) - 1 + i_pos;
+ n_out = isl_basic_map_dim(bmap2, isl_dim_out);
+ o_out = isl_basic_map_offset(bmap1, isl_dim_out) - 1 + o_pos;
+ n_div = isl_basic_map_dim(bmap2, isl_dim_div);
+ if (total < 0 || n_param < 0 || n_in < 0 || n_out < 0 || n_div < 0)
+ goto error;
+ ctx = isl_basic_map_get_ctx(bmap1);
+ dim_map = isl_dim_map_alloc(ctx, total + n_div);
+ isl_dim_map_dim_range(dim_map, space, isl_dim_param, 0, n_param, 0);
+ isl_dim_map_dim_range(dim_map, space, isl_dim_in, 0, n_in, o_in);
+ isl_dim_map_dim_range(dim_map, space, isl_dim_out, 0, n_out, o_out);
+ isl_dim_map_div(dim_map, bmap2, total);
+
+ return isl_basic_map_add_constraints_dim_map(bmap1, bmap2, dim_map);
+error:
+ isl_basic_map_free(bmap1);
+ isl_basic_map_free(bmap2);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_extend(__isl_take isl_basic_map *base,
+ unsigned extra, unsigned n_eq, unsigned n_ineq)
+{
+ isl_space *space;
+ struct isl_basic_map *ext;
+ unsigned flags;
+ int dims_ok;
+
+ if (!base)
+ goto error;
+
+ dims_ok = base->extra >= base->n_div + extra;
+
+ if (dims_ok && room_for_con(base, n_eq + n_ineq) &&
+ room_for_ineq(base, n_ineq))
+ return base;
+
+ extra += base->extra;
+ n_eq += base->n_eq;
+ n_ineq += base->n_ineq;
+
+ space = isl_basic_map_get_space(base);
+ ext = isl_basic_map_alloc_space(space, extra, n_eq, n_ineq);
+ if (!ext)
+ goto error;
+
+ if (dims_ok)
+ ext->sample = isl_vec_copy(base->sample);
+ flags = base->flags;
+ ext = add_constraints(ext, base, 0, 0);
+ if (ext) {
+ ext->flags = flags;
+ ISL_F_CLR(ext, ISL_BASIC_SET_FINAL);
+ }
+
+ return ext;
+
+error:
+ isl_basic_map_free(base);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_extend(__isl_take isl_basic_set *base,
+ unsigned extra, unsigned n_eq, unsigned n_ineq)
+{
+ return bset_from_bmap(isl_basic_map_extend(bset_to_bmap(base),
+ extra, n_eq, n_ineq));
+}
+
+__isl_give isl_basic_map *isl_basic_map_extend_constraints(
+ __isl_take isl_basic_map *base, unsigned n_eq, unsigned n_ineq)
+{
+ return isl_basic_map_extend(base, 0, n_eq, n_ineq);
+}
+
+__isl_give isl_basic_set *isl_basic_set_extend_constraints(
+ __isl_take isl_basic_set *base, unsigned n_eq, unsigned n_ineq)
+{
+ isl_basic_map *bmap = bset_to_bmap(base);
+ bmap = isl_basic_map_extend_constraints(bmap, n_eq, n_ineq);
+ return bset_from_bmap(bmap);
+}
+
+__isl_give isl_basic_set *isl_basic_set_cow(__isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(isl_basic_map_cow(bset_to_bmap(bset)));
+}
+
+__isl_give isl_basic_map *isl_basic_map_cow(__isl_take isl_basic_map *bmap)
+{
+ if (!bmap)
+ return NULL;
+
+ if (bmap->ref > 1) {
+ bmap->ref--;
+ bmap = isl_basic_map_dup(bmap);
+ }
+ if (bmap) {
+ ISL_F_CLR(bmap, ISL_BASIC_SET_FINAL);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_REDUCED_COEFFICIENTS);
+ }
+ return bmap;
+}
+
+/* Clear all cached information in "map", either because it is about
+ * to be modified or because it is being freed.
+ * Always return the same pointer that is passed in.
+ * This is needed for the use in isl_map_free.
+ */
+static __isl_give isl_map *clear_caches(__isl_take isl_map *map)
+{
+ isl_basic_map_free(map->cached_simple_hull[0]);
+ isl_basic_map_free(map->cached_simple_hull[1]);
+ map->cached_simple_hull[0] = NULL;
+ map->cached_simple_hull[1] = NULL;
+ return map;
+}
+
+__isl_give isl_set *isl_set_cow(__isl_take isl_set *set)
+{
+ return isl_map_cow(set);
+}
+
+/* Return an isl_map that is equal to "map" and that has only
+ * a single reference.
+ *
+ * If the original input already has only one reference, then
+ * simply return it, but clear all cached information, since
+ * it may be rendered invalid by the operations that will be
+ * performed on the result.
+ *
+ * Otherwise, create a duplicate (without any cached information).
+ */
+__isl_give isl_map *isl_map_cow(__isl_take isl_map *map)
+{
+ if (!map)
+ return NULL;
+
+ if (map->ref == 1)
+ return clear_caches(map);
+ map->ref--;
+ return isl_map_dup(map);
+}
+
+static void swap_vars(struct isl_blk blk, isl_int *a,
+ unsigned a_len, unsigned b_len)
+{
+ isl_seq_cpy(blk.data, a+a_len, b_len);
+ isl_seq_cpy(blk.data+b_len, a, a_len);
+ isl_seq_cpy(a, blk.data, b_len+a_len);
+}
+
+static __isl_give isl_basic_map *isl_basic_map_swap_vars(
+ __isl_take isl_basic_map *bmap, unsigned pos, unsigned n1, unsigned n2)
+{
+ int i;
+ struct isl_blk blk;
+
+ if (isl_basic_map_check_range(bmap, isl_dim_all, pos - 1, n1 + n2) < 0)
+ goto error;
+
+ if (n1 == 0 || n2 == 0)
+ return bmap;
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+
+ blk = isl_blk_alloc(bmap->ctx, n1 + n2);
+ if (isl_blk_is_error(blk))
+ goto error;
+
+ for (i = 0; i < bmap->n_eq; ++i)
+ swap_vars(blk,
+ bmap->eq[i] + pos, n1, n2);
+
+ for (i = 0; i < bmap->n_ineq; ++i)
+ swap_vars(blk,
+ bmap->ineq[i] + pos, n1, n2);
+
+ for (i = 0; i < bmap->n_div; ++i)
+ swap_vars(blk,
+ bmap->div[i]+1 + pos, n1, n2);
+
+ isl_blk_free(bmap->ctx, blk);
+
+ ISL_F_CLR(bmap, ISL_BASIC_SET_SORTED);
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* The given basic map has turned out to be empty.
+ * Explicitly mark it as such and change the representation
+ * to a canonical representation of the empty basic map.
+ * Since the basic map has conflicting constraints,
+ * it must have at least one constraint, except perhaps
+ * if it was already explicitly marked as being empty.
+ * Do nothing in the latter case, i.e., if it has been marked empty and
+ * has no constraints.
+ */
+__isl_give isl_basic_map *isl_basic_map_set_to_empty(
+ __isl_take isl_basic_map *bmap)
+{
+ int i = 0;
+ isl_bool empty;
+ isl_size n;
+ isl_size total;
+
+ n = isl_basic_map_n_constraint(bmap);
+ empty = isl_basic_map_plain_is_empty(bmap);
+ if (n < 0 || empty < 0)
+ return isl_basic_map_free(bmap);
+ if (n == 0 && empty)
+ return bmap;
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ if (isl_basic_map_free_div(bmap, bmap->n_div) < 0)
+ return isl_basic_map_free(bmap);
+ bmap = isl_basic_map_free_inequality(bmap, bmap->n_ineq);
+ if (!bmap)
+ return NULL;
+ if (bmap->n_eq > 0) {
+ bmap = isl_basic_map_free_equality(bmap, bmap->n_eq - 1);
+ if (!bmap)
+ return NULL;
+ } else {
+ i = isl_basic_map_alloc_equality(bmap);
+ if (i < 0)
+ goto error;
+ }
+ isl_int_set_si(bmap->eq[i][0], 1);
+ isl_seq_clr(bmap->eq[i]+1, total);
+ ISL_F_SET(bmap, ISL_BASIC_MAP_EMPTY);
+ isl_vec_free(bmap->sample);
+ bmap->sample = NULL;
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_set_to_empty(
+ __isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(isl_basic_map_set_to_empty(bset_to_bmap(bset)));
+}
+
+__isl_give isl_basic_map *isl_basic_map_set_rational(
+ __isl_take isl_basic_map *bmap)
+{
+ if (!bmap)
+ return NULL;
+
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL))
+ return bmap;
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+
+ ISL_F_SET(bmap, ISL_BASIC_MAP_RATIONAL);
+
+ return isl_basic_map_finalize(bmap);
+}
+
+__isl_give isl_basic_set *isl_basic_set_set_rational(
+ __isl_take isl_basic_set *bset)
+{
+ return isl_basic_map_set_rational(bset);
+}
+
+__isl_give isl_basic_set *isl_basic_set_set_integral(
+ __isl_take isl_basic_set *bset)
+{
+ if (!bset)
+ return NULL;
+
+ if (!ISL_F_ISSET(bset, ISL_BASIC_MAP_RATIONAL))
+ return bset;
+
+ bset = isl_basic_set_cow(bset);
+ if (!bset)
+ return NULL;
+
+ ISL_F_CLR(bset, ISL_BASIC_MAP_RATIONAL);
+
+ return isl_basic_set_finalize(bset);
+}
+
+__isl_give isl_map *isl_map_set_rational(__isl_take isl_map *map)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_set_rational(map->p[i]);
+ if (!map->p[i])
+ goto error;
+ }
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_set_rational(__isl_take isl_set *set)
+{
+ return isl_map_set_rational(set);
+}
+
+/* Swap divs "a" and "b" in "bmap" (without modifying any of the constraints
+ * of "bmap").
+ */
+static void swap_div(__isl_keep isl_basic_map *bmap, int a, int b)
+{
+ isl_int *t = bmap->div[a];
+ bmap->div[a] = bmap->div[b];
+ bmap->div[b] = t;
+}
+
+/* Swap divs "a" and "b" in "bmap" and adjust the constraints and
+ * div definitions accordingly.
+ */
+__isl_give isl_basic_map *isl_basic_map_swap_div(__isl_take isl_basic_map *bmap,
+ int a, int b)
+{
+ int i;
+ isl_size off;
+
+ off = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (off < 0)
+ return isl_basic_map_free(bmap);
+
+ swap_div(bmap, a, b);
+
+ for (i = 0; i < bmap->n_eq; ++i)
+ isl_int_swap(bmap->eq[i][1+off+a], bmap->eq[i][1+off+b]);
+
+ for (i = 0; i < bmap->n_ineq; ++i)
+ isl_int_swap(bmap->ineq[i][1+off+a], bmap->ineq[i][1+off+b]);
+
+ for (i = 0; i < bmap->n_div; ++i)
+ isl_int_swap(bmap->div[i][1+1+off+a], bmap->div[i][1+1+off+b]);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_SORTED);
+
+ return bmap;
+}
+
+static void constraint_drop_vars(isl_int *c, unsigned n, unsigned rem)
+{
+ isl_seq_cpy(c, c + n, rem);
+ isl_seq_clr(c + rem, n);
+}
+
+/* Drop n dimensions starting at first.
+ *
+ * In principle, this frees up some extra variables as the number
+ * of columns remains constant, but we would have to extend
+ * the div array too as the number of rows in this array is assumed
+ * to be equal to extra.
+ */
+__isl_give isl_basic_set *isl_basic_set_drop_dims(
+ __isl_take isl_basic_set *bset, unsigned first, unsigned n)
+{
+ return isl_basic_map_drop(bset_to_bmap(bset), isl_dim_set, first, n);
+}
+
+/* Move "n" divs starting at "first" to the end of the list of divs.
+ */
+static __isl_give isl_basic_map *move_divs_last(__isl_take isl_basic_map *bmap,
+ unsigned first, unsigned n)
+{
+ isl_int **div;
+ int i;
+
+ if (first + n == bmap->n_div)
+ return bmap;
+
+ div = isl_alloc_array(bmap->ctx, isl_int *, n);
+ if (!div)
+ goto error;
+ for (i = 0; i < n; ++i)
+ div[i] = bmap->div[first + i];
+ for (i = 0; i < bmap->n_div - first - n; ++i)
+ bmap->div[first + i] = bmap->div[first + n + i];
+ for (i = 0; i < n; ++i)
+ bmap->div[bmap->n_div - n + i] = div[i];
+ free(div);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+#undef TYPE
+#define TYPE isl_map
+static
+#include "check_type_range_templ.c"
+
+/* Check that there are "n" dimensions of type "type" starting at "first"
+ * in "set".
+ */
+isl_stat isl_set_check_range(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return isl_map_check_range(set_to_map(set), type, first, n);
+}
+
+/* Drop "n" dimensions of type "type" starting at "first".
+ * Perform the core computation, without cowing or
+ * simplifying and finalizing the result.
+ *
+ * In principle, this frees up some extra variables as the number
+ * of columns remains constant, but we would have to extend
+ * the div array too as the number of rows in this array is assumed
+ * to be equal to extra.
+ */
+__isl_give isl_basic_map *isl_basic_map_drop_core(
+ __isl_take isl_basic_map *bmap, enum isl_dim_type type,
+ unsigned first, unsigned n)
+{
+ int i;
+ unsigned offset;
+ unsigned left;
+ isl_size total;
+
+ if (isl_basic_map_check_range(bmap, type, first, n) < 0)
+ return isl_basic_map_free(bmap);
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+
+ offset = isl_basic_map_offset(bmap, type) + first;
+ left = total - (offset - 1) - n;
+ for (i = 0; i < bmap->n_eq; ++i)
+ constraint_drop_vars(bmap->eq[i]+offset, n, left);
+
+ for (i = 0; i < bmap->n_ineq; ++i)
+ constraint_drop_vars(bmap->ineq[i]+offset, n, left);
+
+ for (i = 0; i < bmap->n_div; ++i)
+ constraint_drop_vars(bmap->div[i]+1+offset, n, left);
+
+ if (type == isl_dim_div) {
+ bmap = move_divs_last(bmap, first, n);
+ if (!bmap)
+ return NULL;
+ if (isl_basic_map_free_div(bmap, n) < 0)
+ return isl_basic_map_free(bmap);
+ } else
+ bmap->dim = isl_space_drop_dims(bmap->dim, type, first, n);
+ if (!bmap->dim)
+ return isl_basic_map_free(bmap);
+
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_REDUNDANT);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_SORTED);
+ return bmap;
+}
+
+/* Drop "n" dimensions of type "type" starting at "first".
+ *
+ * In principle, this frees up some extra variables as the number
+ * of columns remains constant, but we would have to extend
+ * the div array too as the number of rows in this array is assumed
+ * to be equal to extra.
+ */
+__isl_give isl_basic_map *isl_basic_map_drop(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ if (!bmap)
+ return NULL;
+ if (n == 0 && !isl_space_is_named_or_nested(bmap->dim, type))
+ return bmap;
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+
+ bmap = isl_basic_map_drop_core(bmap, type, first, n);
+
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+}
+
+__isl_give isl_basic_set *isl_basic_set_drop(__isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return bset_from_bmap(isl_basic_map_drop(bset_to_bmap(bset),
+ type, first, n));
+}
+
+/* No longer consider "map" to be normalized.
+ */
+static __isl_give isl_map *isl_map_unmark_normalized(__isl_take isl_map *map)
+{
+ if (!map)
+ return NULL;
+ ISL_F_CLR(map, ISL_MAP_NORMALIZED);
+ return map;
+}
+
+__isl_give isl_map *isl_map_drop(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+ isl_space *space;
+
+ if (isl_map_check_range(map, type, first, n) < 0)
+ return isl_map_free(map);
+
+ if (n == 0 && !isl_space_is_named_or_nested(map->dim, type))
+ return map;
+ map = isl_map_cow(map);
+ if (!map)
+ goto error;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_drop(map->p[i], type, first, n);
+ if (!map->p[i])
+ goto error;
+ }
+ map = isl_map_unmark_normalized(map);
+
+ space = isl_map_take_space(map);
+ space = isl_space_drop_dims(space, type, first, n);
+ map = isl_map_restore_space(map, space);
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_drop(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return set_from_map(isl_map_drop(set_to_map(set), type, first, n));
+}
+
+/* Drop the integer division at position "div", which is assumed
+ * not to appear in any of the constraints or
+ * in any of the other integer divisions.
+ *
+ * Since the integer division is redundant, there is no need to cow.
+ */
+__isl_give isl_basic_map *isl_basic_map_drop_div(
+ __isl_take isl_basic_map *bmap, unsigned div)
+{
+ return isl_basic_map_drop_core(bmap, isl_dim_div, div, 1);
+}
+
+/* Eliminate the specified n dimensions starting at first from the
+ * constraints, without removing the dimensions from the space.
+ * If the set is rational, the dimensions are eliminated using Fourier-Motzkin.
+ */
+__isl_give isl_map *isl_map_eliminate(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+
+ if (n == 0)
+ return map;
+
+ if (isl_map_check_range(map, type, first, n) < 0)
+ return isl_map_free(map);
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_eliminate(map->p[i], type, first, n);
+ if (!map->p[i])
+ goto error;
+ }
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Eliminate the specified n dimensions starting at first from the
+ * constraints, without removing the dimensions from the space.
+ * If the set is rational, the dimensions are eliminated using Fourier-Motzkin.
+ */
+__isl_give isl_set *isl_set_eliminate(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return set_from_map(isl_map_eliminate(set_to_map(set), type, first, n));
+}
+
+/* Eliminate the specified n dimensions starting at first from the
+ * constraints, without removing the dimensions from the space.
+ * If the set is rational, the dimensions are eliminated using Fourier-Motzkin.
+ */
+__isl_give isl_set *isl_set_eliminate_dims(__isl_take isl_set *set,
+ unsigned first, unsigned n)
+{
+ return isl_set_eliminate(set, isl_dim_set, first, n);
+}
+
+__isl_give isl_basic_map *isl_basic_map_remove_divs(
+ __isl_take isl_basic_map *bmap)
+{
+ isl_size v_div;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0)
+ return isl_basic_map_free(bmap);
+ bmap = isl_basic_map_eliminate_vars(bmap, v_div, bmap->n_div);
+ if (!bmap)
+ return NULL;
+ bmap->n_div = 0;
+ return isl_basic_map_finalize(bmap);
+}
+
+__isl_give isl_basic_set *isl_basic_set_remove_divs(
+ __isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(isl_basic_map_remove_divs(bset_to_bmap(bset)));
+}
+
+__isl_give isl_map *isl_map_remove_divs(__isl_take isl_map *map)
+{
+ int i;
+
+ if (!map)
+ return NULL;
+ if (map->n == 0)
+ return map;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_remove_divs(map->p[i]);
+ if (!map->p[i])
+ goto error;
+ }
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_remove_divs(__isl_take isl_set *set)
+{
+ return isl_map_remove_divs(set);
+}
+
+__isl_give isl_basic_map *isl_basic_map_remove_dims(
+ __isl_take isl_basic_map *bmap, enum isl_dim_type type,
+ unsigned first, unsigned n)
+{
+ if (isl_basic_map_check_range(bmap, type, first, n) < 0)
+ return isl_basic_map_free(bmap);
+ if (n == 0 && !isl_space_is_named_or_nested(bmap->dim, type))
+ return bmap;
+ bmap = isl_basic_map_eliminate_vars(bmap,
+ isl_basic_map_offset(bmap, type) - 1 + first, n);
+ if (!bmap)
+ return bmap;
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY) && type == isl_dim_div)
+ return bmap;
+ bmap = isl_basic_map_drop(bmap, type, first, n);
+ return bmap;
+}
+
+/* Return true if the definition of the given div (recursively) involves
+ * any of the given variables.
+ */
+static isl_bool div_involves_vars(__isl_keep isl_basic_map *bmap, int div,
+ unsigned first, unsigned n)
+{
+ int i;
+ unsigned div_offset = isl_basic_map_offset(bmap, isl_dim_div);
+
+ if (isl_int_is_zero(bmap->div[div][0]))
+ return isl_bool_false;
+ if (isl_seq_first_non_zero(bmap->div[div] + 1 + first, n) >= 0)
+ return isl_bool_true;
+
+ for (i = bmap->n_div - 1; i >= 0; --i) {
+ isl_bool involves;
+
+ if (isl_int_is_zero(bmap->div[div][1 + div_offset + i]))
+ continue;
+ involves = div_involves_vars(bmap, i, first, n);
+ if (involves < 0 || involves)
+ return involves;
+ }
+
+ return isl_bool_false;
+}
+
+/* Try and add a lower and/or upper bound on "div" to "bmap"
+ * based on inequality "i".
+ * "total" is the total number of variables (excluding the divs).
+ * "v" is a temporary object that can be used during the calculations.
+ * If "lb" is set, then a lower bound should be constructed.
+ * If "ub" is set, then an upper bound should be constructed.
+ *
+ * The calling function has already checked that the inequality does not
+ * reference "div", but we still need to check that the inequality is
+ * of the right form. We'll consider the case where we want to construct
+ * a lower bound. The construction of upper bounds is similar.
+ *
+ * Let "div" be of the form
+ *
+ * q = floor((a + f(x))/d)
+ *
+ * We essentially check if constraint "i" is of the form
+ *
+ * b + f(x) >= 0
+ *
+ * so that we can use it to derive a lower bound on "div".
+ * However, we allow a slightly more general form
+ *
+ * b + g(x) >= 0
+ *
+ * with the condition that the coefficients of g(x) - f(x) are all
+ * divisible by d.
+ * Rewriting this constraint as
+ *
+ * 0 >= -b - g(x)
+ *
+ * adding a + f(x) to both sides and dividing by d, we obtain
+ *
+ * (a + f(x))/d >= (a-b)/d + (f(x)-g(x))/d
+ *
+ * Taking the floor on both sides, we obtain
+ *
+ * q >= floor((a-b)/d) + (f(x)-g(x))/d
+ *
+ * or
+ *
+ * (g(x)-f(x))/d + ceil((b-a)/d) + q >= 0
+ *
+ * In the case of an upper bound, we construct the constraint
+ *
+ * (g(x)+f(x))/d + floor((b+a)/d) - q >= 0
+ *
+ */
+static __isl_give isl_basic_map *insert_bounds_on_div_from_ineq(
+ __isl_take isl_basic_map *bmap, int div, int i,
+ unsigned total, isl_int v, int lb, int ub)
+{
+ int j;
+
+ for (j = 0; (lb || ub) && j < total + bmap->n_div; ++j) {
+ if (lb) {
+ isl_int_sub(v, bmap->ineq[i][1 + j],
+ bmap->div[div][1 + 1 + j]);
+ lb = isl_int_is_divisible_by(v, bmap->div[div][0]);
+ }
+ if (ub) {
+ isl_int_add(v, bmap->ineq[i][1 + j],
+ bmap->div[div][1 + 1 + j]);
+ ub = isl_int_is_divisible_by(v, bmap->div[div][0]);
+ }
+ }
+ if (!lb && !ub)
+ return bmap;
+
+ bmap = isl_basic_map_cow(bmap);
+ bmap = isl_basic_map_extend_constraints(bmap, 0, lb + ub);
+ if (lb) {
+ int k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ for (j = 0; j < 1 + total + bmap->n_div; ++j) {
+ isl_int_sub(bmap->ineq[k][j], bmap->ineq[i][j],
+ bmap->div[div][1 + j]);
+ isl_int_cdiv_q(bmap->ineq[k][j],
+ bmap->ineq[k][j], bmap->div[div][0]);
+ }
+ isl_int_set_si(bmap->ineq[k][1 + total + div], 1);
+ }
+ if (ub) {
+ int k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ for (j = 0; j < 1 + total + bmap->n_div; ++j) {
+ isl_int_add(bmap->ineq[k][j], bmap->ineq[i][j],
+ bmap->div[div][1 + j]);
+ isl_int_fdiv_q(bmap->ineq[k][j],
+ bmap->ineq[k][j], bmap->div[div][0]);
+ }
+ isl_int_set_si(bmap->ineq[k][1 + total + div], -1);
+ }
+
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* This function is called right before "div" is eliminated from "bmap"
+ * using Fourier-Motzkin.
+ * Look through the constraints of "bmap" for constraints on the argument
+ * of the integer division and use them to construct constraints on the
+ * integer division itself. These constraints can then be combined
+ * during the Fourier-Motzkin elimination.
+ * Note that it is only useful to introduce lower bounds on "div"
+ * if "bmap" already contains upper bounds on "div" as the newly
+ * introduce lower bounds can then be combined with the pre-existing
+ * upper bounds. Similarly for upper bounds.
+ * We therefore first check if "bmap" contains any lower and/or upper bounds
+ * on "div".
+ *
+ * It is interesting to note that the introduction of these constraints
+ * can indeed lead to more accurate results, even when compared to
+ * deriving constraints on the argument of "div" from constraints on "div".
+ * Consider, for example, the set
+ *
+ * { [i,j,k] : 3 + i + 2j >= 0 and 2 * [(i+2j)/4] <= k }
+ *
+ * The second constraint can be rewritten as
+ *
+ * 2 * [(-i-2j+3)/4] + k >= 0
+ *
+ * from which we can derive
+ *
+ * -i - 2j + 3 >= -2k
+ *
+ * or
+ *
+ * i + 2j <= 3 + 2k
+ *
+ * Combined with the first constraint, we obtain
+ *
+ * -3 <= 3 + 2k or k >= -3
+ *
+ * If, on the other hand we derive a constraint on [(i+2j)/4] from
+ * the first constraint, we obtain
+ *
+ * [(i + 2j)/4] >= [-3/4] = -1
+ *
+ * Combining this constraint with the second constraint, we obtain
+ *
+ * k >= -2
+ */
+static __isl_give isl_basic_map *insert_bounds_on_div(
+ __isl_take isl_basic_map *bmap, int div)
+{
+ int i;
+ int check_lb, check_ub;
+ isl_int v;
+ isl_size v_div;
+
+ if (!bmap)
+ return NULL;
+
+ if (isl_int_is_zero(bmap->div[div][0]))
+ return bmap;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0)
+ return isl_basic_map_free(bmap);
+
+ check_lb = 0;
+ check_ub = 0;
+ for (i = 0; (!check_lb || !check_ub) && i < bmap->n_ineq; ++i) {
+ int s = isl_int_sgn(bmap->ineq[i][1 + v_div + div]);
+ if (s > 0)
+ check_ub = 1;
+ if (s < 0)
+ check_lb = 1;
+ }
+
+ if (!check_lb && !check_ub)
+ return bmap;
+
+ isl_int_init(v);
+
+ for (i = 0; bmap && i < bmap->n_ineq; ++i) {
+ if (!isl_int_is_zero(bmap->ineq[i][1 + v_div + div]))
+ continue;
+
+ bmap = insert_bounds_on_div_from_ineq(bmap, div, i, v_div, v,
+ check_lb, check_ub);
+ }
+
+ isl_int_clear(v);
+
+ return bmap;
+}
+
+/* Remove all divs (recursively) involving any of the given dimensions
+ * in their definitions.
+ */
+__isl_give isl_basic_map *isl_basic_map_remove_divs_involving_dims(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+
+ if (isl_basic_map_check_range(bmap, type, first, n) < 0)
+ return isl_basic_map_free(bmap);
+ first += isl_basic_map_offset(bmap, type);
+
+ for (i = bmap->n_div - 1; i >= 0; --i) {
+ isl_bool involves;
+
+ involves = div_involves_vars(bmap, i, first, n);
+ if (involves < 0)
+ return isl_basic_map_free(bmap);
+ if (!involves)
+ continue;
+ bmap = insert_bounds_on_div(bmap, i);
+ bmap = isl_basic_map_remove_dims(bmap, isl_dim_div, i, 1);
+ if (!bmap)
+ return NULL;
+ i = bmap->n_div;
+ }
+
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_remove_divs_involving_dims(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return isl_basic_map_remove_divs_involving_dims(bset, type, first, n);
+}
+
+__isl_give isl_map *isl_map_remove_divs_involving_dims(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+
+ if (!map)
+ return NULL;
+ if (map->n == 0)
+ return map;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_remove_divs_involving_dims(map->p[i],
+ type, first, n);
+ if (!map->p[i])
+ goto error;
+ }
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_remove_divs_involving_dims(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return set_from_map(isl_map_remove_divs_involving_dims(set_to_map(set),
+ type, first, n));
+}
+
+/* Does the description of "bmap" depend on the specified dimensions?
+ * We also check whether the dimensions appear in any of the div definitions.
+ * In principle there is no need for this check. If the dimensions appear
+ * in a div definition, they also appear in the defining constraints of that
+ * div.
+ */
+isl_bool isl_basic_map_involves_dims(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+
+ if (isl_basic_map_check_range(bmap, type, first, n) < 0)
+ return isl_bool_error;
+
+ first += isl_basic_map_offset(bmap, type);
+ for (i = 0; i < bmap->n_eq; ++i)
+ if (isl_seq_first_non_zero(bmap->eq[i] + first, n) >= 0)
+ return isl_bool_true;
+ for (i = 0; i < bmap->n_ineq; ++i)
+ if (isl_seq_first_non_zero(bmap->ineq[i] + first, n) >= 0)
+ return isl_bool_true;
+ for (i = 0; i < bmap->n_div; ++i) {
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ if (isl_seq_first_non_zero(bmap->div[i] + 1 + first, n) >= 0)
+ return isl_bool_true;
+ }
+
+ return isl_bool_false;
+}
+
+isl_bool isl_map_involves_dims(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+
+ if (isl_map_check_range(map, type, first, n) < 0)
+ return isl_bool_error;
+
+ for (i = 0; i < map->n; ++i) {
+ isl_bool involves = isl_basic_map_involves_dims(map->p[i],
+ type, first, n);
+ if (involves < 0 || involves)
+ return involves;
+ }
+
+ return isl_bool_false;
+}
+
+isl_bool isl_basic_set_involves_dims(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return isl_basic_map_involves_dims(bset, type, first, n);
+}
+
+isl_bool isl_set_involves_dims(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return isl_map_involves_dims(set, type, first, n);
+}
+
+/* Does "bset" involve any local variables, i.e., integer divisions?
+ */
+static isl_bool isl_basic_set_involves_locals(__isl_keep isl_basic_set *bset)
+{
+ isl_size n;
+
+ n = isl_basic_set_dim(bset, isl_dim_div);
+ if (n < 0)
+ return isl_bool_error;
+ return isl_bool_ok(n > 0);
+}
+
+/* isl_set_every_basic_set callback that checks whether "bset"
+ * is free of local variables.
+ */
+static isl_bool basic_set_no_locals(__isl_keep isl_basic_set *bset, void *user)
+{
+ return isl_bool_not(isl_basic_set_involves_locals(bset));
+}
+
+/* Does "set" involve any local variables, i.e., integer divisions?
+ */
+isl_bool isl_set_involves_locals(__isl_keep isl_set *set)
+{
+ isl_bool no_locals;
+
+ no_locals = isl_set_every_basic_set(set, &basic_set_no_locals, NULL);
+ return isl_bool_not(no_locals);
+}
+
+/* Drop all constraints in bmap that involve any of the dimensions
+ * first to first+n-1.
+ * This function only performs the actual removal of constraints.
+ *
+ * This function should not call finalize since it is used by
+ * remove_redundant_divs, which in turn is called by isl_basic_map_finalize.
+ */
+__isl_give isl_basic_map *isl_basic_map_drop_constraints_involving(
+ __isl_take isl_basic_map *bmap, unsigned first, unsigned n)
+{
+ int i;
+
+ if (n == 0)
+ return bmap;
+
+ bmap = isl_basic_map_cow(bmap);
+
+ if (!bmap)
+ return NULL;
+
+ for (i = bmap->n_eq - 1; i >= 0; --i) {
+ if (isl_seq_first_non_zero(bmap->eq[i] + 1 + first, n) == -1)
+ continue;
+ if (isl_basic_map_drop_equality(bmap, i) < 0)
+ return isl_basic_map_free(bmap);
+ }
+
+ for (i = bmap->n_ineq - 1; i >= 0; --i) {
+ if (isl_seq_first_non_zero(bmap->ineq[i] + 1 + first, n) == -1)
+ continue;
+ if (isl_basic_map_drop_inequality(bmap, i) < 0)
+ return isl_basic_map_free(bmap);
+ }
+
+ return bmap;
+}
+
+/* Drop all constraints in bset that involve any of the dimensions
+ * first to first+n-1.
+ * This function only performs the actual removal of constraints.
+ */
+__isl_give isl_basic_set *isl_basic_set_drop_constraints_involving(
+ __isl_take isl_basic_set *bset, unsigned first, unsigned n)
+{
+ return isl_basic_map_drop_constraints_involving(bset, first, n);
+}
+
+/* Drop all constraints in bmap that do not involve any of the dimensions
+ * first to first + n - 1 of the given type.
+ */
+__isl_give isl_basic_map *isl_basic_map_drop_constraints_not_involving_dims(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+
+ if (n == 0) {
+ isl_space *space = isl_basic_map_get_space(bmap);
+ isl_basic_map_free(bmap);
+ return isl_basic_map_universe(space);
+ }
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+
+ if (isl_basic_map_check_range(bmap, type, first, n) < 0)
+ return isl_basic_map_free(bmap);
+
+ first += isl_basic_map_offset(bmap, type) - 1;
+
+ for (i = bmap->n_eq - 1; i >= 0; --i) {
+ if (isl_seq_first_non_zero(bmap->eq[i] + 1 + first, n) != -1)
+ continue;
+ if (isl_basic_map_drop_equality(bmap, i) < 0)
+ return isl_basic_map_free(bmap);
+ }
+
+ for (i = bmap->n_ineq - 1; i >= 0; --i) {
+ if (isl_seq_first_non_zero(bmap->ineq[i] + 1 + first, n) != -1)
+ continue;
+ if (isl_basic_map_drop_inequality(bmap, i) < 0)
+ return isl_basic_map_free(bmap);
+ }
+
+ bmap = isl_basic_map_add_known_div_constraints(bmap);
+ return bmap;
+}
+
+/* Drop all constraints in bset that do not involve any of the dimensions
+ * first to first + n - 1 of the given type.
+ */
+__isl_give isl_basic_set *isl_basic_set_drop_constraints_not_involving_dims(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return isl_basic_map_drop_constraints_not_involving_dims(bset,
+ type, first, n);
+}
+
+/* Drop all constraints in bmap that involve any of the dimensions
+ * first to first + n - 1 of the given type.
+ */
+__isl_give isl_basic_map *isl_basic_map_drop_constraints_involving_dims(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ if (!bmap)
+ return NULL;
+ if (n == 0)
+ return bmap;
+
+ if (isl_basic_map_check_range(bmap, type, first, n) < 0)
+ return isl_basic_map_free(bmap);
+
+ bmap = isl_basic_map_remove_divs_involving_dims(bmap, type, first, n);
+ first += isl_basic_map_offset(bmap, type) - 1;
+ bmap = isl_basic_map_drop_constraints_involving(bmap, first, n);
+ bmap = isl_basic_map_add_known_div_constraints(bmap);
+ return bmap;
+}
+
+/* Drop all constraints in bset that involve any of the dimensions
+ * first to first + n - 1 of the given type.
+ */
+__isl_give isl_basic_set *isl_basic_set_drop_constraints_involving_dims(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return isl_basic_map_drop_constraints_involving_dims(bset,
+ type, first, n);
+}
+
+/* Drop constraints from "map" by applying "drop" to each basic map.
+ */
+static __isl_give isl_map *drop_constraints(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n,
+ __isl_give isl_basic_map *(*drop)(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n))
+{
+ int i;
+
+ if (isl_map_check_range(map, type, first, n) < 0)
+ return isl_map_free(map);
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = drop(map->p[i], type, first, n);
+ if (!map->p[i])
+ return isl_map_free(map);
+ }
+
+ if (map->n > 1)
+ ISL_F_CLR(map, ISL_MAP_DISJOINT);
+
+ return map;
+}
+
+/* Drop all constraints in map that involve any of the dimensions
+ * first to first + n - 1 of the given type.
+ */
+__isl_give isl_map *isl_map_drop_constraints_involving_dims(
+ __isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ if (n == 0)
+ return map;
+ return drop_constraints(map, type, first, n,
+ &isl_basic_map_drop_constraints_involving_dims);
+}
+
+/* Drop all constraints in "map" that do not involve any of the dimensions
+ * first to first + n - 1 of the given type.
+ */
+__isl_give isl_map *isl_map_drop_constraints_not_involving_dims(
+ __isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ if (n == 0) {
+ isl_space *space = isl_map_get_space(map);
+ isl_map_free(map);
+ return isl_map_universe(space);
+ }
+ return drop_constraints(map, type, first, n,
+ &isl_basic_map_drop_constraints_not_involving_dims);
+}
+
+/* Drop all constraints in set that involve any of the dimensions
+ * first to first + n - 1 of the given type.
+ */
+__isl_give isl_set *isl_set_drop_constraints_involving_dims(
+ __isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return isl_map_drop_constraints_involving_dims(set, type, first, n);
+}
+
+/* Drop all constraints in "set" that do not involve any of the dimensions
+ * first to first + n - 1 of the given type.
+ */
+__isl_give isl_set *isl_set_drop_constraints_not_involving_dims(
+ __isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return isl_map_drop_constraints_not_involving_dims(set, type, first, n);
+}
+
+/* Does local variable "div" of "bmap" have a complete explicit representation?
+ * Having a complete explicit representation requires not only
+ * an explicit representation, but also that all local variables
+ * that appear in this explicit representation in turn have
+ * a complete explicit representation.
+ */
+isl_bool isl_basic_map_div_is_known(__isl_keep isl_basic_map *bmap, int div)
+{
+ int i;
+ unsigned div_offset = isl_basic_map_offset(bmap, isl_dim_div);
+ isl_bool marked;
+
+ marked = isl_basic_map_div_is_marked_unknown(bmap, div);
+ if (marked < 0 || marked)
+ return isl_bool_not(marked);
+
+ for (i = bmap->n_div - 1; i >= 0; --i) {
+ isl_bool known;
+
+ if (isl_int_is_zero(bmap->div[div][1 + div_offset + i]))
+ continue;
+ known = isl_basic_map_div_is_known(bmap, i);
+ if (known < 0 || !known)
+ return known;
+ }
+
+ return isl_bool_true;
+}
+
+/* Remove all divs that are unknown or defined in terms of unknown divs.
+ */
+__isl_give isl_basic_map *isl_basic_map_remove_unknown_divs(
+ __isl_take isl_basic_map *bmap)
+{
+ int i;
+
+ if (!bmap)
+ return NULL;
+
+ for (i = bmap->n_div - 1; i >= 0; --i) {
+ if (isl_basic_map_div_is_known(bmap, i))
+ continue;
+ bmap = isl_basic_map_remove_dims(bmap, isl_dim_div, i, 1);
+ if (!bmap)
+ return NULL;
+ i = bmap->n_div;
+ }
+
+ return bmap;
+}
+
+/* Remove all divs that are unknown or defined in terms of unknown divs.
+ */
+__isl_give isl_basic_set *isl_basic_set_remove_unknown_divs(
+ __isl_take isl_basic_set *bset)
+{
+ return isl_basic_map_remove_unknown_divs(bset);
+}
+
+__isl_give isl_map *isl_map_remove_unknown_divs(__isl_take isl_map *map)
+{
+ int i;
+
+ if (!map)
+ return NULL;
+ if (map->n == 0)
+ return map;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_remove_unknown_divs(map->p[i]);
+ if (!map->p[i])
+ goto error;
+ }
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_remove_unknown_divs(__isl_take isl_set *set)
+{
+ return set_from_map(isl_map_remove_unknown_divs(set_to_map(set)));
+}
+
+__isl_give isl_basic_set *isl_basic_set_remove_dims(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ isl_basic_map *bmap = bset_to_bmap(bset);
+ bmap = isl_basic_map_remove_dims(bmap, type, first, n);
+ return bset_from_bmap(bmap);
+}
+
+__isl_give isl_map *isl_map_remove_dims(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+
+ if (n == 0)
+ return map;
+
+ map = isl_map_cow(map);
+ if (isl_map_check_range(map, type, first, n) < 0)
+ return isl_map_free(map);
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_eliminate_vars(map->p[i],
+ isl_basic_map_offset(map->p[i], type) - 1 + first, n);
+ if (!map->p[i])
+ goto error;
+ }
+ map = isl_map_drop(map, type, first, n);
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_remove_dims(__isl_take isl_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return set_from_map(isl_map_remove_dims(set_to_map(bset),
+ type, first, n));
+}
+
+/* Project out n inputs starting at first using Fourier-Motzkin */
+__isl_give isl_map *isl_map_remove_inputs(__isl_take isl_map *map,
+ unsigned first, unsigned n)
+{
+ return isl_map_remove_dims(map, isl_dim_in, first, n);
+}
+
+void isl_basic_set_print_internal(__isl_keep isl_basic_set *bset,
+ FILE *out, int indent)
+{
+ isl_printer *p;
+
+ if (!bset) {
+ fprintf(out, "null basic set\n");
+ return;
+ }
+
+ fprintf(out, "%*s", indent, "");
+ fprintf(out, "ref: %d, nparam: %d, dim: %d, extra: %d, flags: %x\n",
+ bset->ref, bset->dim->nparam, bset->dim->n_out,
+ bset->extra, bset->flags);
+
+ p = isl_printer_to_file(isl_basic_set_get_ctx(bset), out);
+ p = isl_printer_set_dump(p, 1);
+ p = isl_printer_set_indent(p, indent);
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_basic_set(p, bset);
+ p = isl_printer_end_line(p);
+ isl_printer_free(p);
+}
+
+void isl_basic_map_print_internal(__isl_keep isl_basic_map *bmap,
+ FILE *out, int indent)
+{
+ isl_printer *p;
+
+ if (!bmap) {
+ fprintf(out, "null basic map\n");
+ return;
+ }
+
+ fprintf(out, "%*s", indent, "");
+ fprintf(out, "ref: %d, nparam: %d, in: %d, out: %d, extra: %d, "
+ "flags: %x, n_name: %d\n",
+ bmap->ref,
+ bmap->dim->nparam, bmap->dim->n_in, bmap->dim->n_out,
+ bmap->extra, bmap->flags, bmap->dim->n_id);
+
+ p = isl_printer_to_file(isl_basic_map_get_ctx(bmap), out);
+ p = isl_printer_set_dump(p, 1);
+ p = isl_printer_set_indent(p, indent);
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_basic_map(p, bmap);
+ p = isl_printer_end_line(p);
+ isl_printer_free(p);
+}
+
+__isl_give isl_basic_map *isl_inequality_negate(__isl_take isl_basic_map *bmap,
+ unsigned pos)
+{
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ if (pos >= bmap->n_ineq)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "invalid position", return isl_basic_map_free(bmap));
+ isl_seq_neg(bmap->ineq[pos], bmap->ineq[pos], 1 + total);
+ isl_int_sub_ui(bmap->ineq[pos][0], bmap->ineq[pos][0], 1);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_REDUNDANT);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_SORTED);
+ return bmap;
+}
+
+__isl_give isl_set *isl_set_alloc_space(__isl_take isl_space *space, int n,
+ unsigned flags)
+{
+ if (isl_space_check_is_set(space) < 0)
+ goto error;
+ return isl_map_alloc_space(space, n, flags);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Make sure "map" has room for at least "n" more basic maps.
+ */
+__isl_give isl_map *isl_map_grow(__isl_take isl_map *map, int n)
+{
+ int i;
+ struct isl_map *grown = NULL;
+
+ if (!map)
+ return NULL;
+ isl_assert(map->ctx, n >= 0, goto error);
+ if (map->n + n <= map->size)
+ return map;
+ grown = isl_map_alloc_space(isl_map_get_space(map), map->n + n, map->flags);
+ if (!grown)
+ goto error;
+ for (i = 0; i < map->n; ++i) {
+ grown->p[i] = isl_basic_map_copy(map->p[i]);
+ if (!grown->p[i])
+ goto error;
+ grown->n++;
+ }
+ isl_map_free(map);
+ return grown;
+error:
+ isl_map_free(grown);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Make sure "set" has room for at least "n" more basic sets.
+ */
+__isl_give isl_set *isl_set_grow(__isl_take isl_set *set, int n)
+{
+ return set_from_map(isl_map_grow(set_to_map(set), n));
+}
+
+__isl_give isl_set *isl_set_from_basic_set(__isl_take isl_basic_set *bset)
+{
+ return isl_map_from_basic_map(bset);
+}
+
+/* This function performs the same operation as isl_set_from_basic_set,
+ * but is considered as a function on an isl_basic_set when exported.
+ */
+__isl_give isl_set *isl_basic_set_to_set(__isl_take isl_basic_set *bset)
+{
+ return isl_set_from_basic_set(bset);
+}
+
+__isl_give isl_map *isl_map_from_basic_map(__isl_take isl_basic_map *bmap)
+{
+ struct isl_map *map;
+
+ if (!bmap)
+ return NULL;
+
+ map = isl_map_alloc_space(isl_space_copy(bmap->dim), 1, ISL_MAP_DISJOINT);
+ return isl_map_add_basic_map(map, bmap);
+}
+
+__isl_give isl_set *isl_set_add_basic_set(__isl_take isl_set *set,
+ __isl_take isl_basic_set *bset)
+{
+ return set_from_map(isl_map_add_basic_map(set_to_map(set),
+ bset_to_bmap(bset)));
+}
+
+__isl_null isl_set *isl_set_free(__isl_take isl_set *set)
+{
+ return isl_map_free(set);
+}
+
+void isl_set_print_internal(__isl_keep isl_set *set, FILE *out, int indent)
+{
+ int i;
+
+ if (!set) {
+ fprintf(out, "null set\n");
+ return;
+ }
+
+ fprintf(out, "%*s", indent, "");
+ fprintf(out, "ref: %d, n: %d, nparam: %d, dim: %d, flags: %x\n",
+ set->ref, set->n, set->dim->nparam, set->dim->n_out,
+ set->flags);
+ for (i = 0; i < set->n; ++i) {
+ fprintf(out, "%*s", indent, "");
+ fprintf(out, "basic set %d:\n", i);
+ isl_basic_set_print_internal(set->p[i], out, indent+4);
+ }
+}
+
+void isl_map_print_internal(__isl_keep isl_map *map, FILE *out, int indent)
+{
+ int i;
+
+ if (!map) {
+ fprintf(out, "null map\n");
+ return;
+ }
+
+ fprintf(out, "%*s", indent, "");
+ fprintf(out, "ref: %d, n: %d, nparam: %d, in: %d, out: %d, "
+ "flags: %x, n_name: %d\n",
+ map->ref, map->n, map->dim->nparam, map->dim->n_in,
+ map->dim->n_out, map->flags, map->dim->n_id);
+ for (i = 0; i < map->n; ++i) {
+ fprintf(out, "%*s", indent, "");
+ fprintf(out, "basic map %d:\n", i);
+ isl_basic_map_print_internal(map->p[i], out, indent+4);
+ }
+}
+
+/* Check that the space of "bset" is the same as that of the domain of "bmap".
+ */
+static isl_stat isl_basic_map_check_compatible_domain(
+ __isl_keep isl_basic_map *bmap, __isl_keep isl_basic_set *bset)
+{
+ isl_bool ok;
+
+ ok = isl_basic_map_compatible_domain(bmap, bset);
+ if (ok < 0)
+ return isl_stat_error;
+ if (!ok)
+ isl_die(isl_basic_set_get_ctx(bset), isl_error_invalid,
+ "incompatible spaces", return isl_stat_error);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_basic_map *isl_basic_map_intersect_domain(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *bset)
+{
+ struct isl_basic_map *bmap_domain;
+ isl_size dim;
+
+ if (isl_basic_map_check_equal_params(bmap, bset_to_bmap(bset)) < 0)
+ goto error;
+
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ if (dim < 0)
+ goto error;
+ if (dim != 0 &&
+ isl_basic_map_check_compatible_domain(bmap, bset) < 0)
+ goto error;
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ goto error;
+ bmap = isl_basic_map_extend(bmap,
+ bset->n_div, bset->n_eq, bset->n_ineq);
+ bmap_domain = isl_basic_map_from_domain(bset);
+ bmap = add_constraints(bmap, bmap_domain, 0, 0);
+
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Check that the space of "bset" is the same as that of the range of "bmap".
+ */
+static isl_stat isl_basic_map_check_compatible_range(
+ __isl_keep isl_basic_map *bmap, __isl_keep isl_basic_set *bset)
+{
+ isl_bool ok;
+
+ ok = isl_basic_map_compatible_range(bmap, bset);
+ if (ok < 0)
+ return isl_stat_error;
+ if (!ok)
+ isl_die(isl_basic_set_get_ctx(bset), isl_error_invalid,
+ "incompatible spaces", return isl_stat_error);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_basic_map *isl_basic_map_intersect_range(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *bset)
+{
+ struct isl_basic_map *bmap_range;
+ isl_size dim;
+
+ if (isl_basic_map_check_equal_params(bmap, bset_to_bmap(bset)) < 0)
+ goto error;
+
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ if (dim < 0)
+ goto error;
+ if (dim != 0 && isl_basic_map_check_compatible_range(bmap, bset) < 0)
+ goto error;
+
+ if (isl_basic_set_plain_is_universe(bset)) {
+ isl_basic_set_free(bset);
+ return bmap;
+ }
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ goto error;
+ bmap = isl_basic_map_extend(bmap,
+ bset->n_div, bset->n_eq, bset->n_ineq);
+ bmap_range = bset_to_bmap(bset);
+ bmap = add_constraints(bmap, bmap_range, 0, 0);
+
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+isl_bool isl_basic_map_contains(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_vec *vec)
+{
+ int i;
+ isl_size total;
+ isl_int s;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0 || !vec)
+ return isl_bool_error;
+
+ if (1 + total != vec->size)
+ return isl_bool_false;
+
+ isl_int_init(s);
+
+ for (i = 0; i < bmap->n_eq; ++i) {
+ isl_seq_inner_product(vec->el, bmap->eq[i], 1 + total, &s);
+ if (!isl_int_is_zero(s)) {
+ isl_int_clear(s);
+ return isl_bool_false;
+ }
+ }
+
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ isl_seq_inner_product(vec->el, bmap->ineq[i], 1 + total, &s);
+ if (isl_int_is_neg(s)) {
+ isl_int_clear(s);
+ return isl_bool_false;
+ }
+ }
+
+ isl_int_clear(s);
+
+ return isl_bool_true;
+}
+
+isl_bool isl_basic_set_contains(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_vec *vec)
+{
+ return isl_basic_map_contains(bset_to_bmap(bset), vec);
+}
+
+__isl_give isl_basic_map *isl_basic_map_intersect(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2)
+{
+ struct isl_vec *sample = NULL;
+ isl_space *space1, *space2;
+ isl_size dim1, dim2, nparam1, nparam2;
+
+ if (isl_basic_map_check_equal_params(bmap1, bmap2) < 0)
+ goto error;
+ space1 = isl_basic_map_peek_space(bmap1);
+ space2 = isl_basic_map_peek_space(bmap2);
+ dim1 = isl_space_dim(space1, isl_dim_all);
+ dim2 = isl_space_dim(space2, isl_dim_all);
+ nparam1 = isl_space_dim(space1, isl_dim_param);
+ nparam2 = isl_space_dim(space2, isl_dim_param);
+ if (dim1 < 0 || dim2 < 0 || nparam1 < 0 || nparam2 < 0)
+ goto error;
+ if (dim1 == nparam1 && dim2 != nparam2)
+ return isl_basic_map_intersect(bmap2, bmap1);
+
+ if (dim2 != nparam2 &&
+ isl_basic_map_check_equal_space(bmap1, bmap2) < 0)
+ goto error;
+
+ if (isl_basic_map_plain_is_empty(bmap1)) {
+ isl_basic_map_free(bmap2);
+ return bmap1;
+ }
+ if (isl_basic_map_plain_is_empty(bmap2)) {
+ isl_basic_map_free(bmap1);
+ return bmap2;
+ }
+
+ if (bmap1->sample &&
+ isl_basic_map_contains(bmap1, bmap1->sample) > 0 &&
+ isl_basic_map_contains(bmap2, bmap1->sample) > 0)
+ sample = isl_vec_copy(bmap1->sample);
+ else if (bmap2->sample &&
+ isl_basic_map_contains(bmap1, bmap2->sample) > 0 &&
+ isl_basic_map_contains(bmap2, bmap2->sample) > 0)
+ sample = isl_vec_copy(bmap2->sample);
+
+ bmap1 = isl_basic_map_cow(bmap1);
+ if (!bmap1)
+ goto error;
+ bmap1 = isl_basic_map_extend(bmap1,
+ bmap2->n_div, bmap2->n_eq, bmap2->n_ineq);
+ bmap1 = add_constraints(bmap1, bmap2, 0, 0);
+
+ if (!bmap1)
+ isl_vec_free(sample);
+ else if (sample) {
+ isl_vec_free(bmap1->sample);
+ bmap1->sample = sample;
+ }
+
+ bmap1 = isl_basic_map_simplify(bmap1);
+ return isl_basic_map_finalize(bmap1);
+error:
+ if (sample)
+ isl_vec_free(sample);
+ isl_basic_map_free(bmap1);
+ isl_basic_map_free(bmap2);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_intersect(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2)
+{
+ return bset_from_bmap(isl_basic_map_intersect(bset_to_bmap(bset1),
+ bset_to_bmap(bset2)));
+}
+
+__isl_give isl_basic_set *isl_basic_set_intersect_params(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2)
+{
+ return isl_basic_set_intersect(bset1, bset2);
+}
+
+/* Does "map" consist of a single disjunct, without any local variables?
+ */
+static isl_bool is_convex_no_locals(__isl_keep isl_map *map)
+{
+ isl_size n_div;
+
+ if (!map)
+ return isl_bool_error;
+ if (map->n != 1)
+ return isl_bool_false;
+ n_div = isl_basic_map_dim(map->p[0], isl_dim_div);
+ if (n_div < 0)
+ return isl_bool_error;
+ if (n_div != 0)
+ return isl_bool_false;
+ return isl_bool_true;
+}
+
+/* Check that "map" consists of a single disjunct, without any local variables.
+ */
+static isl_stat check_convex_no_locals(__isl_keep isl_map *map)
+{
+ isl_bool ok;
+
+ ok = is_convex_no_locals(map);
+ if (ok < 0)
+ return isl_stat_error;
+ if (ok)
+ return isl_stat_ok;
+
+ isl_die(isl_map_get_ctx(map), isl_error_internal,
+ "unexpectedly not convex or involving local variables",
+ return isl_stat_error);
+}
+
+/* Special case of isl_map_intersect, where both map1 and map2
+ * are convex, without any divs and such that either map1 or map2
+ * contains a single constraint. This constraint is then simply
+ * added to the other map.
+ */
+static __isl_give isl_map *map_intersect_add_constraint(
+ __isl_take isl_map *map1, __isl_take isl_map *map2)
+{
+ if (check_convex_no_locals(map1) < 0 ||
+ check_convex_no_locals(map2) < 0)
+ goto error;
+
+ if (map2->p[0]->n_eq + map2->p[0]->n_ineq != 1)
+ return isl_map_intersect(map2, map1);
+
+ map1 = isl_map_cow(map1);
+ if (!map1)
+ goto error;
+ if (isl_map_plain_is_empty(map1)) {
+ isl_map_free(map2);
+ return map1;
+ }
+ if (map2->p[0]->n_eq == 1)
+ map1->p[0] = isl_basic_map_add_eq(map1->p[0], map2->p[0]->eq[0]);
+ else
+ map1->p[0] = isl_basic_map_add_ineq(map1->p[0],
+ map2->p[0]->ineq[0]);
+
+ map1->p[0] = isl_basic_map_simplify(map1->p[0]);
+ map1->p[0] = isl_basic_map_finalize(map1->p[0]);
+ if (!map1->p[0])
+ goto error;
+
+ if (isl_basic_map_plain_is_empty(map1->p[0])) {
+ isl_basic_map_free(map1->p[0]);
+ map1->n = 0;
+ }
+
+ isl_map_free(map2);
+
+ map1 = isl_map_unmark_normalized(map1);
+ return map1;
+error:
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return NULL;
+}
+
+/* map2 may be either a parameter domain or a map living in the same
+ * space as map1.
+ */
+static __isl_give isl_map *map_intersect_internal(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ unsigned flags = 0;
+ isl_bool equal;
+ isl_map *result;
+ int i, j;
+ isl_size dim2, nparam2;
+
+ if (!map1 || !map2)
+ goto error;
+
+ if ((isl_map_plain_is_empty(map1) ||
+ isl_map_plain_is_universe(map2)) &&
+ isl_space_is_equal(map1->dim, map2->dim)) {
+ isl_map_free(map2);
+ return map1;
+ }
+ if ((isl_map_plain_is_empty(map2) ||
+ isl_map_plain_is_universe(map1)) &&
+ isl_space_is_equal(map1->dim, map2->dim)) {
+ isl_map_free(map1);
+ return map2;
+ }
+
+ if (is_convex_no_locals(map1) == isl_bool_true &&
+ is_convex_no_locals(map2) == isl_bool_true &&
+ isl_space_is_equal(map1->dim, map2->dim) &&
+ (map1->p[0]->n_eq + map1->p[0]->n_ineq == 1 ||
+ map2->p[0]->n_eq + map2->p[0]->n_ineq == 1))
+ return map_intersect_add_constraint(map1, map2);
+
+ equal = isl_map_plain_is_equal(map1, map2);
+ if (equal < 0)
+ goto error;
+ if (equal) {
+ isl_map_free(map2);
+ return map1;
+ }
+
+ dim2 = isl_map_dim(map2, isl_dim_all);
+ nparam2 = isl_map_dim(map2, isl_dim_param);
+ if (dim2 < 0 || nparam2 < 0)
+ goto error;
+ if (dim2 != nparam2)
+ isl_assert(map1->ctx,
+ isl_space_is_equal(map1->dim, map2->dim), goto error);
+
+ if (ISL_F_ISSET(map1, ISL_MAP_DISJOINT) &&
+ ISL_F_ISSET(map2, ISL_MAP_DISJOINT))
+ ISL_FL_SET(flags, ISL_MAP_DISJOINT);
+
+ result = isl_map_alloc_space(isl_space_copy(map1->dim),
+ map1->n * map2->n, flags);
+ if (!result)
+ goto error;
+ for (i = 0; i < map1->n; ++i)
+ for (j = 0; j < map2->n; ++j) {
+ struct isl_basic_map *part;
+ part = isl_basic_map_intersect(
+ isl_basic_map_copy(map1->p[i]),
+ isl_basic_map_copy(map2->p[j]));
+ if (isl_basic_map_is_empty(part) < 0)
+ part = isl_basic_map_free(part);
+ result = isl_map_add_basic_map(result, part);
+ if (!result)
+ goto error;
+ }
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return result;
+error:
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return NULL;
+}
+
+static __isl_give isl_map *map_intersect(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ if (isl_map_check_equal_space(map1, map2) < 0)
+ goto error;
+ return map_intersect_internal(map1, map2);
+error:
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return NULL;
+}
+
+__isl_give isl_map *isl_map_intersect(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map_align_params_bin(&map1, &map2);
+ return map_intersect(map1, map2);
+}
+
+__isl_give isl_set *isl_set_intersect(__isl_take isl_set *set1,
+ __isl_take isl_set *set2)
+{
+ return set_from_map(isl_map_intersect(set_to_map(set1),
+ set_to_map(set2)));
+}
+
+/* map_intersect_internal accepts intersections
+ * with parameter domains, so we can just call that function.
+ */
+__isl_give isl_map *isl_map_intersect_params(__isl_take isl_map *map,
+ __isl_take isl_set *params)
+{
+ isl_map_align_params_set(&map, &params);
+ return map_intersect_internal(map, params);
+}
+
+__isl_give isl_set *isl_set_intersect_params(__isl_take isl_set *set,
+ __isl_take isl_set *params)
+{
+ return isl_map_intersect_params(set, params);
+}
+
+__isl_give isl_basic_map *isl_basic_map_reverse(__isl_take isl_basic_map *bmap)
+{
+ isl_space *space;
+ unsigned pos;
+ isl_size n1, n2;
+
+ if (!bmap)
+ return NULL;
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+ space = isl_space_reverse(isl_space_copy(bmap->dim));
+ pos = isl_basic_map_offset(bmap, isl_dim_in);
+ n1 = isl_basic_map_dim(bmap, isl_dim_in);
+ n2 = isl_basic_map_dim(bmap, isl_dim_out);
+ if (n1 < 0 || n2 < 0)
+ bmap = isl_basic_map_free(bmap);
+ bmap = isl_basic_map_swap_vars(bmap, pos, n1, n2);
+ return isl_basic_map_reset_space(bmap, space);
+}
+
+/* Given a basic map A -> (B -> C), return the corresponding basic map
+ * A -> (C -> B).
+ */
+static __isl_give isl_basic_map *isl_basic_map_range_reverse(
+ __isl_take isl_basic_map *bmap)
+{
+ isl_space *space;
+ isl_size offset, n1, n2;
+
+ space = isl_basic_map_peek_space(bmap);
+ if (isl_space_check_range_is_wrapping(space) < 0)
+ return isl_basic_map_free(bmap);
+ offset = isl_basic_map_var_offset(bmap, isl_dim_out);
+ n1 = isl_space_wrapped_dim(space, isl_dim_out, isl_dim_in);
+ n2 = isl_space_wrapped_dim(space, isl_dim_out, isl_dim_out);
+ if (offset < 0 || n1 < 0 || n2 < 0)
+ return isl_basic_map_free(bmap);
+
+ bmap = isl_basic_map_swap_vars(bmap, 1 + offset, n1, n2);
+
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_range_reverse(space);
+ bmap = isl_basic_map_restore_space(bmap, space);
+
+ return bmap;
+}
+
+static __isl_give isl_basic_map *basic_map_space_reset(
+ __isl_take isl_basic_map *bmap, enum isl_dim_type type)
+{
+ isl_space *space;
+
+ if (!bmap)
+ return NULL;
+ if (!isl_space_is_named_or_nested(bmap->dim, type))
+ return bmap;
+
+ space = isl_basic_map_get_space(bmap);
+ space = isl_space_reset(space, type);
+ bmap = isl_basic_map_reset_space(bmap, space);
+ return bmap;
+}
+
+__isl_give isl_basic_map *isl_basic_map_insert_dims(
+ __isl_take isl_basic_map *bmap, enum isl_dim_type type,
+ unsigned pos, unsigned n)
+{
+ isl_bool rational, is_empty;
+ isl_space *res_space;
+ struct isl_basic_map *res;
+ struct isl_dim_map *dim_map;
+ isl_size total;
+ unsigned off;
+ enum isl_dim_type t;
+
+ if (n == 0)
+ return basic_map_space_reset(bmap, type);
+
+ is_empty = isl_basic_map_plain_is_empty(bmap);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (is_empty < 0 || total < 0)
+ return isl_basic_map_free(bmap);
+ res_space = isl_space_insert_dims(isl_basic_map_get_space(bmap),
+ type, pos, n);
+ if (!res_space)
+ return isl_basic_map_free(bmap);
+ if (is_empty) {
+ isl_basic_map_free(bmap);
+ return isl_basic_map_empty(res_space);
+ }
+
+ dim_map = isl_dim_map_alloc(bmap->ctx, total + n);
+ off = 0;
+ for (t = isl_dim_param; t <= isl_dim_out; ++t) {
+ isl_size dim;
+
+ if (t != type) {
+ isl_dim_map_dim(dim_map, bmap->dim, t, off);
+ } else {
+ isl_size size = isl_basic_map_dim(bmap, t);
+ if (size < 0)
+ dim_map = isl_dim_map_free(dim_map);
+ isl_dim_map_dim_range(dim_map, bmap->dim, t,
+ 0, pos, off);
+ isl_dim_map_dim_range(dim_map, bmap->dim, t,
+ pos, size - pos, off + pos + n);
+ }
+ dim = isl_space_dim(res_space, t);
+ if (dim < 0)
+ dim_map = isl_dim_map_free(dim_map);
+ off += dim;
+ }
+ isl_dim_map_div(dim_map, bmap, off);
+
+ res = isl_basic_map_alloc_space(res_space,
+ bmap->n_div, bmap->n_eq, bmap->n_ineq);
+ rational = isl_basic_map_is_rational(bmap);
+ if (rational < 0)
+ res = isl_basic_map_free(res);
+ if (rational)
+ res = isl_basic_map_set_rational(res);
+ res = isl_basic_map_add_constraints_dim_map(res, bmap, dim_map);
+ return isl_basic_map_finalize(res);
+}
+
+__isl_give isl_basic_set *isl_basic_set_insert_dims(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos, unsigned n)
+{
+ return isl_basic_map_insert_dims(bset, type, pos, n);
+}
+
+__isl_give isl_basic_map *isl_basic_map_add_dims(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned n)
+{
+ isl_size dim;
+
+ dim = isl_basic_map_dim(bmap, type);
+ if (dim < 0)
+ return isl_basic_map_free(bmap);
+ return isl_basic_map_insert_dims(bmap, type, dim, n);
+}
+
+__isl_give isl_basic_set *isl_basic_set_add_dims(__isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned n)
+{
+ if (!bset)
+ return NULL;
+ isl_assert(bset->ctx, type != isl_dim_in, goto error);
+ return isl_basic_map_add_dims(bset, type, n);
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+static __isl_give isl_map *map_space_reset(__isl_take isl_map *map,
+ enum isl_dim_type type)
+{
+ isl_space *space;
+
+ if (!map || !isl_space_is_named_or_nested(map->dim, type))
+ return map;
+
+ space = isl_map_get_space(map);
+ space = isl_space_reset(space, type);
+ map = isl_map_reset_space(map, space);
+ return map;
+}
+
+__isl_give isl_map *isl_map_insert_dims(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, unsigned n)
+{
+ int i;
+ isl_space *space;
+
+ if (n == 0)
+ return map_space_reset(map, type);
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_insert_dims(map->p[i], type, pos, n);
+ if (!map->p[i])
+ goto error;
+ }
+
+ space = isl_map_take_space(map);
+ space = isl_space_insert_dims(space, type, pos, n);
+ map = isl_map_restore_space(map, space);
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_insert_dims(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, unsigned n)
+{
+ return isl_map_insert_dims(set, type, pos, n);
+}
+
+__isl_give isl_map *isl_map_add_dims(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned n)
+{
+ isl_size dim;
+
+ dim = isl_map_dim(map, type);
+ if (dim < 0)
+ return isl_map_free(map);
+ return isl_map_insert_dims(map, type, dim, n);
+}
+
+__isl_give isl_set *isl_set_add_dims(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned n)
+{
+ if (!set)
+ return NULL;
+ isl_assert(set->ctx, type != isl_dim_in, goto error);
+ return set_from_map(isl_map_add_dims(set_to_map(set), type, n));
+error:
+ isl_set_free(set);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_move_dims(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ isl_space *space;
+ struct isl_dim_map *dim_map;
+ struct isl_basic_map *res;
+ enum isl_dim_type t;
+ isl_size total;
+ unsigned off;
+
+ if (!bmap)
+ return NULL;
+ if (n == 0) {
+ bmap = isl_basic_map_reset(bmap, src_type);
+ bmap = isl_basic_map_reset(bmap, dst_type);
+ return bmap;
+ }
+
+ if (isl_basic_map_check_range(bmap, src_type, src_pos, n) < 0)
+ return isl_basic_map_free(bmap);
+
+ if (dst_type == src_type && dst_pos == src_pos)
+ return bmap;
+
+ isl_assert(bmap->ctx, dst_type != src_type, goto error);
+
+ if (pos(bmap->dim, dst_type) + dst_pos ==
+ pos(bmap->dim, src_type) + src_pos +
+ ((src_type < dst_type) ? n : 0)) {
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_move_dims(space, dst_type, dst_pos,
+ src_type, src_pos, n);
+ bmap = isl_basic_map_restore_space(bmap, space);
+ bmap = isl_basic_map_finalize(bmap);
+
+ return bmap;
+ }
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ dim_map = isl_dim_map_alloc(bmap->ctx, total);
+
+ off = 0;
+ space = isl_basic_map_peek_space(bmap);
+ for (t = isl_dim_param; t <= isl_dim_out; ++t) {
+ isl_size size = isl_space_dim(space, t);
+ if (size < 0)
+ dim_map = isl_dim_map_free(dim_map);
+ if (t == dst_type) {
+ isl_dim_map_dim_range(dim_map, space, t,
+ 0, dst_pos, off);
+ off += dst_pos;
+ isl_dim_map_dim_range(dim_map, space, src_type,
+ src_pos, n, off);
+ off += n;
+ isl_dim_map_dim_range(dim_map, space, t,
+ dst_pos, size - dst_pos, off);
+ off += size - dst_pos;
+ } else if (t == src_type) {
+ isl_dim_map_dim_range(dim_map, space, t,
+ 0, src_pos, off);
+ off += src_pos;
+ isl_dim_map_dim_range(dim_map, space, t,
+ src_pos + n, size - src_pos - n, off);
+ off += size - src_pos - n;
+ } else {
+ isl_dim_map_dim(dim_map, space, t, off);
+ off += size;
+ }
+ }
+ isl_dim_map_div(dim_map, bmap, off);
+
+ res = isl_basic_map_alloc_space(isl_basic_map_get_space(bmap),
+ bmap->n_div, bmap->n_eq, bmap->n_ineq);
+ bmap = isl_basic_map_add_constraints_dim_map(res, bmap, dim_map);
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_move_dims(space, dst_type, dst_pos,
+ src_type, src_pos, n);
+ bmap = isl_basic_map_restore_space(bmap, space);
+ if (!bmap)
+ goto error;
+
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_SORTED);
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ bmap = isl_basic_map_finalize(bmap);
+
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_move_dims(__isl_take isl_basic_set *bset,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ isl_basic_map *bmap = bset_to_bmap(bset);
+ bmap = isl_basic_map_move_dims(bmap, dst_type, dst_pos,
+ src_type, src_pos, n);
+ return bset_from_bmap(bmap);
+}
+
+__isl_give isl_set *isl_set_move_dims(__isl_take isl_set *set,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ if (!set)
+ return NULL;
+ isl_assert(set->ctx, dst_type != isl_dim_in, goto error);
+ return set_from_map(isl_map_move_dims(set_to_map(set),
+ dst_type, dst_pos, src_type, src_pos, n));
+error:
+ isl_set_free(set);
+ return NULL;
+}
+
+__isl_give isl_map *isl_map_move_dims(__isl_take isl_map *map,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ int i;
+ isl_space *space;
+
+ if (n == 0) {
+ map = isl_map_reset(map, src_type);
+ map = isl_map_reset(map, dst_type);
+ return map;
+ }
+
+ if (isl_map_check_range(map, src_type, src_pos, n))
+ return isl_map_free(map);
+
+ if (dst_type == src_type && dst_pos == src_pos)
+ return map;
+
+ isl_assert(map->ctx, dst_type != src_type, goto error);
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_move_dims(map->p[i],
+ dst_type, dst_pos,
+ src_type, src_pos, n);
+ if (!map->p[i])
+ goto error;
+ }
+
+ space = isl_map_take_space(map);
+ space = isl_space_move_dims(space, dst_type, dst_pos,
+ src_type, src_pos, n);
+ map = isl_map_restore_space(map, space);
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Move the specified dimensions to the last columns right before
+ * the divs. Don't change the dimension specification of bmap.
+ * That's the responsibility of the caller.
+ */
+static __isl_give isl_basic_map *move_last(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ isl_space *space;
+ struct isl_dim_map *dim_map;
+ struct isl_basic_map *res;
+ enum isl_dim_type t;
+ isl_size total;
+ unsigned off;
+
+ if (!bmap)
+ return NULL;
+ if (isl_basic_map_offset(bmap, type) + first + n ==
+ isl_basic_map_offset(bmap, isl_dim_div))
+ return bmap;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ dim_map = isl_dim_map_alloc(bmap->ctx, total);
+
+ off = 0;
+ space = isl_basic_map_peek_space(bmap);
+ for (t = isl_dim_param; t <= isl_dim_out; ++t) {
+ isl_size size = isl_space_dim(space, t);
+ if (size < 0)
+ dim_map = isl_dim_map_free(dim_map);
+ if (t == type) {
+ isl_dim_map_dim_range(dim_map, space, t,
+ 0, first, off);
+ off += first;
+ isl_dim_map_dim_range(dim_map, space, t,
+ first, n, total - bmap->n_div - n);
+ isl_dim_map_dim_range(dim_map, space, t,
+ first + n, size - (first + n), off);
+ off += size - (first + n);
+ } else {
+ isl_dim_map_dim(dim_map, space, t, off);
+ off += size;
+ }
+ }
+ isl_dim_map_div(dim_map, bmap, off + n);
+
+ res = isl_basic_map_alloc_space(isl_basic_map_get_space(bmap),
+ bmap->n_div, bmap->n_eq, bmap->n_ineq);
+ res = isl_basic_map_add_constraints_dim_map(res, bmap, dim_map);
+ return res;
+}
+
+/* Insert "n" rows in the divs of "bmap".
+ *
+ * The number of columns is not changed, which means that the last
+ * dimensions of "bmap" are being reintepreted as the new divs.
+ * The space of "bmap" is not adjusted, however, which means
+ * that "bmap" is left in an inconsistent state. Removing "n" dimensions
+ * from the space of "bmap" is the responsibility of the caller.
+ */
+static __isl_give isl_basic_map *insert_div_rows(__isl_take isl_basic_map *bmap,
+ int n)
+{
+ int i;
+ size_t row_size;
+ isl_int **new_div;
+ isl_int *old;
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+
+ row_size = isl_basic_map_offset(bmap, isl_dim_div) + bmap->extra;
+ old = bmap->block2.data;
+ bmap->block2 = isl_blk_extend(bmap->ctx, bmap->block2,
+ (bmap->extra + n) * (1 + row_size));
+ if (!bmap->block2.data)
+ return isl_basic_map_free(bmap);
+ new_div = isl_alloc_array(bmap->ctx, isl_int *, bmap->extra + n);
+ if (!new_div)
+ return isl_basic_map_free(bmap);
+ for (i = 0; i < n; ++i) {
+ new_div[i] = bmap->block2.data +
+ (bmap->extra + i) * (1 + row_size);
+ isl_seq_clr(new_div[i], 1 + row_size);
+ }
+ for (i = 0; i < bmap->extra; ++i)
+ new_div[n + i] = bmap->block2.data + (bmap->div[i] - old);
+ free(bmap->div);
+ bmap->div = new_div;
+ bmap->n_div += n;
+ bmap->extra += n;
+
+ return bmap;
+}
+
+/* Drop constraints from "bmap" that only involve the variables
+ * of "type" in the range [first, first + n] that are not related
+ * to any of the variables outside that interval.
+ * These constraints cannot influence the values for the variables
+ * outside the interval, except in case they cause "bmap" to be empty.
+ * Only drop the constraints if "bmap" is known to be non-empty.
+ */
+static __isl_give isl_basic_map *drop_irrelevant_constraints(
+ __isl_take isl_basic_map *bmap, enum isl_dim_type type,
+ unsigned first, unsigned n)
+{
+ int i;
+ int *groups;
+ isl_size dim, n_div;
+ isl_bool non_empty;
+
+ non_empty = isl_basic_map_plain_is_non_empty(bmap);
+ if (non_empty < 0)
+ return isl_basic_map_free(bmap);
+ if (!non_empty)
+ return bmap;
+
+ dim = isl_basic_map_dim(bmap, isl_dim_all);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (dim < 0 || n_div < 0)
+ return isl_basic_map_free(bmap);
+ groups = isl_calloc_array(isl_basic_map_get_ctx(bmap), int, dim);
+ if (!groups)
+ return isl_basic_map_free(bmap);
+ first += isl_basic_map_offset(bmap, type) - 1;
+ for (i = 0; i < first; ++i)
+ groups[i] = -1;
+ for (i = first + n; i < dim - n_div; ++i)
+ groups[i] = -1;
+
+ bmap = isl_basic_map_drop_unrelated_constraints(bmap, groups);
+
+ return bmap;
+}
+
+/* Turn the n dimensions of type type, starting at first
+ * into existentially quantified variables.
+ *
+ * If a subset of the projected out variables are unrelated
+ * to any of the variables that remain, then the constraints
+ * involving this subset are simply dropped first.
+ */
+__isl_give isl_basic_map *isl_basic_map_project_out(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ isl_bool empty;
+ isl_space *space;
+
+ if (n == 0)
+ return basic_map_space_reset(bmap, type);
+ if (type == isl_dim_div)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "cannot project out existentially quantified variables",
+ return isl_basic_map_free(bmap));
+
+ empty = isl_basic_map_plain_is_empty(bmap);
+ if (empty < 0)
+ return isl_basic_map_free(bmap);
+ if (empty)
+ bmap = isl_basic_map_set_to_empty(bmap);
+
+ bmap = drop_irrelevant_constraints(bmap, type, first, n);
+ if (!bmap)
+ return NULL;
+
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL))
+ return isl_basic_map_remove_dims(bmap, type, first, n);
+
+ if (isl_basic_map_check_range(bmap, type, first, n) < 0)
+ return isl_basic_map_free(bmap);
+
+ bmap = move_last(bmap, type, first, n);
+ bmap = isl_basic_map_cow(bmap);
+ bmap = insert_div_rows(bmap, n);
+
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_drop_dims(space, type, first, n);
+ bmap = isl_basic_map_restore_space(bmap, space);
+ bmap = isl_basic_map_simplify(bmap);
+ bmap = isl_basic_map_drop_redundant_divs(bmap);
+ return isl_basic_map_finalize(bmap);
+}
+
+/* Turn the n dimensions of type type, starting at first
+ * into existentially quantified variables.
+ */
+__isl_give isl_basic_set *isl_basic_set_project_out(
+ __isl_take isl_basic_set *bset, enum isl_dim_type type,
+ unsigned first, unsigned n)
+{
+ return bset_from_bmap(isl_basic_map_project_out(bset_to_bmap(bset),
+ type, first, n));
+}
+
+/* Turn the n dimensions of type type, starting at first
+ * into existentially quantified variables.
+ */
+__isl_give isl_map *isl_map_project_out(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+ isl_space *space;
+
+ if (n == 0)
+ return map_space_reset(map, type);
+
+ if (isl_map_check_range(map, type, first, n) < 0)
+ return isl_map_free(map);
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_project_out(map->p[i], type, first, n);
+ if (!map->p[i])
+ goto error;
+ }
+
+ if (map->n > 1)
+ ISL_F_CLR(map, ISL_MAP_DISJOINT);
+ map = isl_map_unmark_normalized(map);
+
+ space = isl_map_take_space(map);
+ space = isl_space_drop_dims(space, type, first, n);
+ map = isl_map_restore_space(map, space);
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+#undef TYPE
+#define TYPE isl_map
+#include "isl_project_out_all_params_templ.c"
+
+/* Turn all the dimensions of type "type", except the "n" starting at "first"
+ * into existentially quantified variables.
+ */
+__isl_give isl_map *isl_map_project_onto(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ isl_size dim;
+
+ dim = isl_map_dim(map, type);
+ if (isl_map_check_range(map, type, first, n) < 0 || dim < 0)
+ return isl_map_free(map);
+ map = isl_map_project_out(map, type, first + n, dim - (first + n));
+ map = isl_map_project_out(map, type, 0, first);
+ return map;
+}
+
+/* Turn the n dimensions of type type, starting at first
+ * into existentially quantified variables.
+ */
+__isl_give isl_set *isl_set_project_out(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return set_from_map(isl_map_project_out(set_to_map(set),
+ type, first, n));
+}
+
+/* If "set" involves a parameter with identifier "id",
+ * then turn it into an existentially quantified variable.
+ */
+__isl_give isl_set *isl_set_project_out_param_id(__isl_take isl_set *set,
+ __isl_take isl_id *id)
+{
+ int pos;
+
+ if (!set || !id)
+ goto error;
+ pos = isl_set_find_dim_by_id(set, isl_dim_param, id);
+ isl_id_free(id);
+ if (pos < 0)
+ return set;
+ return isl_set_project_out(set, isl_dim_param, pos, 1);
+error:
+ isl_set_free(set);
+ isl_id_free(id);
+ return NULL;
+}
+
+/* If "set" involves any of the parameters with identifiers in "list",
+ * then turn them into existentially quantified variables.
+ */
+__isl_give isl_set *isl_set_project_out_param_id_list(__isl_take isl_set *set,
+ __isl_take isl_id_list *list)
+{
+ int i;
+ isl_size n;
+
+ n = isl_id_list_size(list);
+ if (n < 0)
+ goto error;
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+
+ id = isl_id_list_get_at(list, i);
+ set = isl_set_project_out_param_id(set, id);
+ }
+
+ isl_id_list_free(list);
+ return set;
+error:
+ isl_id_list_free(list);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Project out all parameters from "set" by existentially quantifying
+ * over them.
+ */
+__isl_give isl_set *isl_set_project_out_all_params(__isl_take isl_set *set)
+{
+ return set_from_map(isl_map_project_out_all_params(set_to_map(set)));
+}
+
+/* Return a map that projects the elements in "set" onto their
+ * "n" set dimensions starting at "first".
+ * "type" should be equal to isl_dim_set.
+ */
+__isl_give isl_map *isl_set_project_onto_map(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+ isl_map *map;
+
+ if (type != isl_dim_set)
+ isl_die(isl_set_get_ctx(set), isl_error_invalid,
+ "only set dimensions can be projected out", goto error);
+ if (isl_set_check_range(set, type, first, n) < 0)
+ return isl_set_free(set);
+
+ map = isl_map_from_domain(set);
+ map = isl_map_add_dims(map, isl_dim_out, n);
+ for (i = 0; i < n; ++i)
+ map = isl_map_equate(map, isl_dim_in, first + i,
+ isl_dim_out, i);
+ return map;
+error:
+ isl_set_free(set);
+ return NULL;
+}
+
+static __isl_give isl_basic_map *add_divs(__isl_take isl_basic_map *bmap,
+ unsigned n)
+{
+ int i, j;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ for (i = 0; i < n; ++i) {
+ j = isl_basic_map_alloc_div(bmap);
+ if (j < 0)
+ goto error;
+ isl_seq_clr(bmap->div[j], 1 + 1 + total);
+ }
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Does "bmap2" apply to the range of "bmap1" (ignoring parameters)?
+ */
+isl_bool isl_basic_map_applies_range(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2)
+{
+ isl_space *space1, *space2;
+
+ space1 = isl_basic_map_peek_space(bmap1);
+ space2 = isl_basic_map_peek_space(bmap2);
+ return isl_space_tuple_is_equal(space1, isl_dim_out,
+ space2, isl_dim_in);
+}
+
+/* Check that "bmap2" applies to the range of "bmap1" (ignoring parameters).
+ */
+static isl_stat isl_basic_map_check_applies_range(
+ __isl_keep isl_basic_map *bmap1, __isl_keep isl_basic_map *bmap2)
+{
+ isl_bool equal;
+
+ equal = isl_basic_map_applies_range(bmap1, bmap2);
+ if (equal < 0)
+ return isl_stat_error;
+ if (!equal)
+ isl_die(isl_basic_map_get_ctx(bmap1), isl_error_invalid,
+ "spaces don't match", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+__isl_give isl_basic_map *isl_basic_map_apply_range(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2)
+{
+ isl_space *space_result = NULL;
+ struct isl_basic_map *bmap;
+ isl_size n_in, n_out, n, nparam;
+ unsigned total, pos;
+ struct isl_dim_map *dim_map1, *dim_map2;
+
+ if (isl_basic_map_check_equal_params(bmap1, bmap2) < 0)
+ goto error;
+ if (isl_basic_map_check_applies_range(bmap1, bmap2) < 0)
+ goto error;
+
+ n_in = isl_basic_map_dim(bmap1, isl_dim_in);
+ n_out = isl_basic_map_dim(bmap2, isl_dim_out);
+ n = isl_basic_map_dim(bmap1, isl_dim_out);
+ nparam = isl_basic_map_dim(bmap1, isl_dim_param);
+ if (n_in < 0 || n_out < 0 || n < 0 || nparam < 0)
+ goto error;
+
+ space_result = isl_space_join(isl_basic_map_get_space(bmap1),
+ isl_basic_map_get_space(bmap2));
+
+ total = nparam + n_in + n_out + bmap1->n_div + bmap2->n_div + n;
+ dim_map1 = isl_dim_map_alloc(bmap1->ctx, total);
+ dim_map2 = isl_dim_map_alloc(bmap1->ctx, total);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_param, pos = 0);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_param, pos = 0);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_in, pos += nparam);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_out, pos += n_in);
+ isl_dim_map_div(dim_map1, bmap1, pos += n_out);
+ isl_dim_map_div(dim_map2, bmap2, pos += bmap1->n_div);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_out, pos += bmap2->n_div);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_in, pos);
+
+ bmap = isl_basic_map_alloc_space(space_result,
+ bmap1->n_div + bmap2->n_div + n,
+ bmap1->n_eq + bmap2->n_eq,
+ bmap1->n_ineq + bmap2->n_ineq);
+ bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap1, dim_map1);
+ bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap2, dim_map2);
+ bmap = add_divs(bmap, n);
+ bmap = isl_basic_map_simplify(bmap);
+ bmap = isl_basic_map_drop_redundant_divs(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap1);
+ isl_basic_map_free(bmap2);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_apply(__isl_take isl_basic_set *bset,
+ __isl_take isl_basic_map *bmap)
+{
+ if (isl_basic_map_check_compatible_domain(bmap, bset) < 0)
+ goto error;
+
+ return bset_from_bmap(isl_basic_map_apply_range(bset_to_bmap(bset),
+ bmap));
+error:
+ isl_basic_set_free(bset);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_apply_domain(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2)
+{
+ if (isl_basic_map_check_equal_params(bmap1, bmap2) < 0)
+ goto error;
+ if (!isl_space_tuple_is_equal(bmap1->dim, isl_dim_in,
+ bmap2->dim, isl_dim_in))
+ isl_die(isl_basic_map_get_ctx(bmap1), isl_error_invalid,
+ "spaces don't match", goto error);
+
+ bmap1 = isl_basic_map_reverse(bmap1);
+ bmap1 = isl_basic_map_apply_range(bmap1, bmap2);
+ return isl_basic_map_reverse(bmap1);
+error:
+ isl_basic_map_free(bmap1);
+ isl_basic_map_free(bmap2);
+ return NULL;
+}
+
+/* Given two basic maps A -> f(A) and B -> g(B), construct a basic map
+ * A \cap B -> f(A) + f(B)
+ */
+__isl_give isl_basic_map *isl_basic_map_sum(__isl_take isl_basic_map *bmap1,
+ __isl_take isl_basic_map *bmap2)
+{
+ isl_size n_in, n_out, nparam;
+ unsigned total, pos;
+ struct isl_basic_map *bmap = NULL;
+ struct isl_dim_map *dim_map1, *dim_map2;
+ int i;
+
+ if (isl_basic_map_check_equal_space(bmap1, bmap2) < 0)
+ goto error;
+
+ nparam = isl_basic_map_dim(bmap1, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap1, isl_dim_in);
+ n_out = isl_basic_map_dim(bmap1, isl_dim_out);
+ if (nparam < 0 || n_in < 0 || n_out < 0)
+ goto error;
+
+ total = nparam + n_in + n_out + bmap1->n_div + bmap2->n_div + 2 * n_out;
+ dim_map1 = isl_dim_map_alloc(bmap1->ctx, total);
+ dim_map2 = isl_dim_map_alloc(bmap2->ctx, total);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_param, pos = 0);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_param, pos);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_in, pos += nparam);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_in, pos);
+ isl_dim_map_div(dim_map1, bmap1, pos += n_in + n_out);
+ isl_dim_map_div(dim_map2, bmap2, pos += bmap1->n_div);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_out, pos += bmap2->n_div);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_out, pos += n_out);
+
+ bmap = isl_basic_map_alloc_space(isl_space_copy(bmap1->dim),
+ bmap1->n_div + bmap2->n_div + 2 * n_out,
+ bmap1->n_eq + bmap2->n_eq + n_out,
+ bmap1->n_ineq + bmap2->n_ineq);
+ for (i = 0; i < n_out; ++i) {
+ int j = isl_basic_map_alloc_equality(bmap);
+ if (j < 0)
+ goto error;
+ isl_seq_clr(bmap->eq[j], 1+total);
+ isl_int_set_si(bmap->eq[j][1+nparam+n_in+i], -1);
+ isl_int_set_si(bmap->eq[j][1+pos+i], 1);
+ isl_int_set_si(bmap->eq[j][1+pos-n_out+i], 1);
+ }
+ bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap1, dim_map1);
+ bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap2, dim_map2);
+ bmap = add_divs(bmap, 2 * n_out);
+
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ isl_basic_map_free(bmap1);
+ isl_basic_map_free(bmap2);
+ return NULL;
+}
+
+/* Given two maps A -> f(A) and B -> g(B), construct a map
+ * A \cap B -> f(A) + f(B)
+ */
+__isl_give isl_map *isl_map_sum(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ struct isl_map *result;
+ int i, j;
+
+ if (isl_map_check_equal_space(map1, map2) < 0)
+ goto error;
+
+ result = isl_map_alloc_space(isl_space_copy(map1->dim),
+ map1->n * map2->n, 0);
+ if (!result)
+ goto error;
+ for (i = 0; i < map1->n; ++i)
+ for (j = 0; j < map2->n; ++j) {
+ struct isl_basic_map *part;
+ part = isl_basic_map_sum(
+ isl_basic_map_copy(map1->p[i]),
+ isl_basic_map_copy(map2->p[j]));
+ if (isl_basic_map_is_empty(part))
+ isl_basic_map_free(part);
+ else
+ result = isl_map_add_basic_map(result, part);
+ if (!result)
+ goto error;
+ }
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return result;
+error:
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_sum(__isl_take isl_set *set1,
+ __isl_take isl_set *set2)
+{
+ return set_from_map(isl_map_sum(set_to_map(set1), set_to_map(set2)));
+}
+
+/* Given a basic map A -> f(A), construct A -> -f(A).
+ */
+__isl_give isl_basic_map *isl_basic_map_neg(__isl_take isl_basic_map *bmap)
+{
+ int i, j;
+ unsigned off;
+ isl_size n;
+
+ bmap = isl_basic_map_cow(bmap);
+ n = isl_basic_map_dim(bmap, isl_dim_out);
+ if (n < 0)
+ return isl_basic_map_free(bmap);
+
+ off = isl_basic_map_offset(bmap, isl_dim_out);
+ for (i = 0; i < bmap->n_eq; ++i)
+ for (j = 0; j < n; ++j)
+ isl_int_neg(bmap->eq[i][off+j], bmap->eq[i][off+j]);
+ for (i = 0; i < bmap->n_ineq; ++i)
+ for (j = 0; j < n; ++j)
+ isl_int_neg(bmap->ineq[i][off+j], bmap->ineq[i][off+j]);
+ for (i = 0; i < bmap->n_div; ++i)
+ for (j = 0; j < n; ++j)
+ isl_int_neg(bmap->div[i][1+off+j], bmap->div[i][1+off+j]);
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ return isl_basic_map_finalize(bmap);
+}
+
+__isl_give isl_basic_set *isl_basic_set_neg(__isl_take isl_basic_set *bset)
+{
+ return isl_basic_map_neg(bset);
+}
+
+/* Given a map A -> f(A), construct A -> -f(A).
+ */
+__isl_give isl_map *isl_map_neg(__isl_take isl_map *map)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_neg(map->p[i]);
+ if (!map->p[i])
+ goto error;
+ }
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_neg(__isl_take isl_set *set)
+{
+ return set_from_map(isl_map_neg(set_to_map(set)));
+}
+
+/* Given a basic map A -> f(A) and an integer d, construct a basic map
+ * A -> floor(f(A)/d).
+ */
+__isl_give isl_basic_map *isl_basic_map_floordiv(__isl_take isl_basic_map *bmap,
+ isl_int d)
+{
+ isl_size n_in, n_out, nparam;
+ unsigned total, pos;
+ struct isl_basic_map *result = NULL;
+ struct isl_dim_map *dim_map;
+ int i;
+
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ if (nparam < 0 || n_in < 0 || n_out < 0)
+ return isl_basic_map_free(bmap);
+
+ total = nparam + n_in + n_out + bmap->n_div + n_out;
+ dim_map = isl_dim_map_alloc(bmap->ctx, total);
+ isl_dim_map_dim(dim_map, bmap->dim, isl_dim_param, pos = 0);
+ isl_dim_map_dim(dim_map, bmap->dim, isl_dim_in, pos += nparam);
+ isl_dim_map_div(dim_map, bmap, pos += n_in + n_out);
+ isl_dim_map_dim(dim_map, bmap->dim, isl_dim_out, pos += bmap->n_div);
+
+ result = isl_basic_map_alloc_space(isl_space_copy(bmap->dim),
+ bmap->n_div + n_out,
+ bmap->n_eq, bmap->n_ineq + 2 * n_out);
+ result = isl_basic_map_add_constraints_dim_map(result, bmap, dim_map);
+ result = add_divs(result, n_out);
+ for (i = 0; i < n_out; ++i) {
+ int j;
+ j = isl_basic_map_alloc_inequality(result);
+ if (j < 0)
+ goto error;
+ isl_seq_clr(result->ineq[j], 1+total);
+ isl_int_neg(result->ineq[j][1+nparam+n_in+i], d);
+ isl_int_set_si(result->ineq[j][1+pos+i], 1);
+ j = isl_basic_map_alloc_inequality(result);
+ if (j < 0)
+ goto error;
+ isl_seq_clr(result->ineq[j], 1+total);
+ isl_int_set(result->ineq[j][1+nparam+n_in+i], d);
+ isl_int_set_si(result->ineq[j][1+pos+i], -1);
+ isl_int_sub_ui(result->ineq[j][0], d, 1);
+ }
+
+ result = isl_basic_map_simplify(result);
+ return isl_basic_map_finalize(result);
+error:
+ isl_basic_map_free(result);
+ return NULL;
+}
+
+/* Given a map A -> f(A) and an integer d, construct a map
+ * A -> floor(f(A)/d).
+ */
+__isl_give isl_map *isl_map_floordiv(__isl_take isl_map *map, isl_int d)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ ISL_F_CLR(map, ISL_MAP_DISJOINT);
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_floordiv(map->p[i], d);
+ if (!map->p[i])
+ goto error;
+ }
+ map = isl_map_unmark_normalized(map);
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Given a map A -> f(A) and an integer d, construct a map
+ * A -> floor(f(A)/d).
+ */
+__isl_give isl_map *isl_map_floordiv_val(__isl_take isl_map *map,
+ __isl_take isl_val *d)
+{
+ if (!map || !d)
+ goto error;
+ if (!isl_val_is_int(d))
+ isl_die(isl_val_get_ctx(d), isl_error_invalid,
+ "expecting integer denominator", goto error);
+ map = isl_map_floordiv(map, d->n);
+ isl_val_free(d);
+ return map;
+error:
+ isl_map_free(map);
+ isl_val_free(d);
+ return NULL;
+}
+
+static __isl_give isl_basic_map *var_equal(__isl_take isl_basic_map *bmap,
+ unsigned pos)
+{
+ int i;
+ isl_size nparam;
+ isl_size n_in;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ if (total < 0 || nparam < 0 || n_in < 0)
+ return isl_basic_map_free(bmap);
+ i = isl_basic_map_alloc_equality(bmap);
+ if (i < 0)
+ goto error;
+ isl_seq_clr(bmap->eq[i], 1 + total);
+ isl_int_set_si(bmap->eq[i][1+nparam+pos], -1);
+ isl_int_set_si(bmap->eq[i][1+nparam+n_in+pos], 1);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Add a constraint to "bmap" expressing i_pos < o_pos
+ */
+static __isl_give isl_basic_map *var_less(__isl_take isl_basic_map *bmap,
+ unsigned pos)
+{
+ int i;
+ isl_size nparam;
+ isl_size n_in;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ if (total < 0 || nparam < 0 || n_in < 0)
+ return isl_basic_map_free(bmap);
+ i = isl_basic_map_alloc_inequality(bmap);
+ if (i < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[i], 1 + total);
+ isl_int_set_si(bmap->ineq[i][0], -1);
+ isl_int_set_si(bmap->ineq[i][1+nparam+pos], -1);
+ isl_int_set_si(bmap->ineq[i][1+nparam+n_in+pos], 1);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Add a constraint to "bmap" expressing i_pos <= o_pos
+ */
+static __isl_give isl_basic_map *var_less_or_equal(
+ __isl_take isl_basic_map *bmap, unsigned pos)
+{
+ int i;
+ isl_size nparam;
+ isl_size n_in;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ if (total < 0 || nparam < 0 || n_in < 0)
+ return isl_basic_map_free(bmap);
+ i = isl_basic_map_alloc_inequality(bmap);
+ if (i < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[i], 1 + total);
+ isl_int_set_si(bmap->ineq[i][1+nparam+pos], -1);
+ isl_int_set_si(bmap->ineq[i][1+nparam+n_in+pos], 1);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Add a constraint to "bmap" expressing i_pos > o_pos
+ */
+static __isl_give isl_basic_map *var_more(__isl_take isl_basic_map *bmap,
+ unsigned pos)
+{
+ int i;
+ isl_size nparam;
+ isl_size n_in;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ if (total < 0 || nparam < 0 || n_in < 0)
+ return isl_basic_map_free(bmap);
+ i = isl_basic_map_alloc_inequality(bmap);
+ if (i < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[i], 1 + total);
+ isl_int_set_si(bmap->ineq[i][0], -1);
+ isl_int_set_si(bmap->ineq[i][1+nparam+pos], 1);
+ isl_int_set_si(bmap->ineq[i][1+nparam+n_in+pos], -1);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Add a constraint to "bmap" expressing i_pos >= o_pos
+ */
+static __isl_give isl_basic_map *var_more_or_equal(
+ __isl_take isl_basic_map *bmap, unsigned pos)
+{
+ int i;
+ isl_size nparam;
+ isl_size n_in;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ if (total < 0 || nparam < 0 || n_in < 0)
+ return isl_basic_map_free(bmap);
+ i = isl_basic_map_alloc_inequality(bmap);
+ if (i < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[i], 1 + total);
+ isl_int_set_si(bmap->ineq[i][1+nparam+pos], 1);
+ isl_int_set_si(bmap->ineq[i][1+nparam+n_in+pos], -1);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_equal(
+ __isl_take isl_space *space, unsigned n_equal)
+{
+ int i;
+ struct isl_basic_map *bmap;
+ bmap = isl_basic_map_alloc_space(space, 0, n_equal, 0);
+ if (!bmap)
+ return NULL;
+ for (i = 0; i < n_equal && bmap; ++i)
+ bmap = var_equal(bmap, i);
+ return isl_basic_map_finalize(bmap);
+}
+
+/* Return a relation on of dimension "space" expressing i_[0..pos] << o_[0..pos]
+ */
+__isl_give isl_basic_map *isl_basic_map_less_at(__isl_take isl_space *space,
+ unsigned pos)
+{
+ int i;
+ struct isl_basic_map *bmap;
+ bmap = isl_basic_map_alloc_space(space, 0, pos, 1);
+ if (!bmap)
+ return NULL;
+ for (i = 0; i < pos && bmap; ++i)
+ bmap = var_equal(bmap, i);
+ if (bmap)
+ bmap = var_less(bmap, pos);
+ return isl_basic_map_finalize(bmap);
+}
+
+/* Return a relation on "space" expressing i_[0..pos] <<= o_[0..pos]
+ */
+__isl_give isl_basic_map *isl_basic_map_less_or_equal_at(
+ __isl_take isl_space *space, unsigned pos)
+{
+ int i;
+ isl_basic_map *bmap;
+
+ bmap = isl_basic_map_alloc_space(space, 0, pos, 1);
+ for (i = 0; i < pos; ++i)
+ bmap = var_equal(bmap, i);
+ bmap = var_less_or_equal(bmap, pos);
+ return isl_basic_map_finalize(bmap);
+}
+
+/* Return a relation on "space" expressing i_pos > o_pos
+ */
+__isl_give isl_basic_map *isl_basic_map_more_at(__isl_take isl_space *space,
+ unsigned pos)
+{
+ int i;
+ struct isl_basic_map *bmap;
+ bmap = isl_basic_map_alloc_space(space, 0, pos, 1);
+ if (!bmap)
+ return NULL;
+ for (i = 0; i < pos && bmap; ++i)
+ bmap = var_equal(bmap, i);
+ if (bmap)
+ bmap = var_more(bmap, pos);
+ return isl_basic_map_finalize(bmap);
+}
+
+/* Return a relation on "space" expressing i_[0..pos] >>= o_[0..pos]
+ */
+__isl_give isl_basic_map *isl_basic_map_more_or_equal_at(
+ __isl_take isl_space *space, unsigned pos)
+{
+ int i;
+ isl_basic_map *bmap;
+
+ bmap = isl_basic_map_alloc_space(space, 0, pos, 1);
+ for (i = 0; i < pos; ++i)
+ bmap = var_equal(bmap, i);
+ bmap = var_more_or_equal(bmap, pos);
+ return isl_basic_map_finalize(bmap);
+}
+
+static __isl_give isl_map *map_lex_lte_first(__isl_take isl_space *space,
+ unsigned n, int equal)
+{
+ struct isl_map *map;
+ int i;
+
+ if (n == 0 && equal)
+ return isl_map_universe(space);
+
+ map = isl_map_alloc_space(isl_space_copy(space), n, ISL_MAP_DISJOINT);
+
+ for (i = 0; i + 1 < n; ++i)
+ map = isl_map_add_basic_map(map,
+ isl_basic_map_less_at(isl_space_copy(space), i));
+ if (n > 0) {
+ if (equal)
+ map = isl_map_add_basic_map(map,
+ isl_basic_map_less_or_equal_at(space, n - 1));
+ else
+ map = isl_map_add_basic_map(map,
+ isl_basic_map_less_at(space, n - 1));
+ } else
+ isl_space_free(space);
+
+ return map;
+}
+
+static __isl_give isl_map *map_lex_lte(__isl_take isl_space *space, int equal)
+{
+ if (!space)
+ return NULL;
+ return map_lex_lte_first(space, space->n_out, equal);
+}
+
+__isl_give isl_map *isl_map_lex_lt_first(__isl_take isl_space *space,
+ unsigned n)
+{
+ return map_lex_lte_first(space, n, 0);
+}
+
+__isl_give isl_map *isl_map_lex_le_first(__isl_take isl_space *space,
+ unsigned n)
+{
+ return map_lex_lte_first(space, n, 1);
+}
+
+__isl_give isl_map *isl_map_lex_lt(__isl_take isl_space *set_space)
+{
+ return map_lex_lte(isl_space_map_from_set(set_space), 0);
+}
+
+__isl_give isl_map *isl_map_lex_le(__isl_take isl_space *set_space)
+{
+ return map_lex_lte(isl_space_map_from_set(set_space), 1);
+}
+
+static __isl_give isl_map *map_lex_gte_first(__isl_take isl_space *space,
+ unsigned n, int equal)
+{
+ struct isl_map *map;
+ int i;
+
+ if (n == 0 && equal)
+ return isl_map_universe(space);
+
+ map = isl_map_alloc_space(isl_space_copy(space), n, ISL_MAP_DISJOINT);
+
+ for (i = 0; i + 1 < n; ++i)
+ map = isl_map_add_basic_map(map,
+ isl_basic_map_more_at(isl_space_copy(space), i));
+ if (n > 0) {
+ if (equal)
+ map = isl_map_add_basic_map(map,
+ isl_basic_map_more_or_equal_at(space, n - 1));
+ else
+ map = isl_map_add_basic_map(map,
+ isl_basic_map_more_at(space, n - 1));
+ } else
+ isl_space_free(space);
+
+ return map;
+}
+
+static __isl_give isl_map *map_lex_gte(__isl_take isl_space *space, int equal)
+{
+ if (!space)
+ return NULL;
+ return map_lex_gte_first(space, space->n_out, equal);
+}
+
+__isl_give isl_map *isl_map_lex_gt_first(__isl_take isl_space *space,
+ unsigned n)
+{
+ return map_lex_gte_first(space, n, 0);
+}
+
+__isl_give isl_map *isl_map_lex_ge_first(__isl_take isl_space *space,
+ unsigned n)
+{
+ return map_lex_gte_first(space, n, 1);
+}
+
+__isl_give isl_map *isl_map_lex_gt(__isl_take isl_space *set_space)
+{
+ return map_lex_gte(isl_space_map_from_set(set_space), 0);
+}
+
+__isl_give isl_map *isl_map_lex_ge(__isl_take isl_space *set_space)
+{
+ return map_lex_gte(isl_space_map_from_set(set_space), 1);
+}
+
+__isl_give isl_map *isl_set_lex_le_set(__isl_take isl_set *set1,
+ __isl_take isl_set *set2)
+{
+ isl_map *map;
+ map = isl_map_lex_le(isl_set_get_space(set1));
+ map = isl_map_intersect_domain(map, set1);
+ map = isl_map_intersect_range(map, set2);
+ return map;
+}
+
+__isl_give isl_map *isl_set_lex_lt_set(__isl_take isl_set *set1,
+ __isl_take isl_set *set2)
+{
+ isl_map *map;
+ map = isl_map_lex_lt(isl_set_get_space(set1));
+ map = isl_map_intersect_domain(map, set1);
+ map = isl_map_intersect_range(map, set2);
+ return map;
+}
+
+__isl_give isl_map *isl_set_lex_ge_set(__isl_take isl_set *set1,
+ __isl_take isl_set *set2)
+{
+ isl_map *map;
+ map = isl_map_lex_ge(isl_set_get_space(set1));
+ map = isl_map_intersect_domain(map, set1);
+ map = isl_map_intersect_range(map, set2);
+ return map;
+}
+
+__isl_give isl_map *isl_set_lex_gt_set(__isl_take isl_set *set1,
+ __isl_take isl_set *set2)
+{
+ isl_map *map;
+ map = isl_map_lex_gt(isl_set_get_space(set1));
+ map = isl_map_intersect_domain(map, set1);
+ map = isl_map_intersect_range(map, set2);
+ return map;
+}
+
+__isl_give isl_map *isl_map_lex_le_map(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map *map;
+ map = isl_map_lex_le(isl_space_range(isl_map_get_space(map1)));
+ map = isl_map_apply_domain(map, isl_map_reverse(map1));
+ map = isl_map_apply_range(map, isl_map_reverse(map2));
+ return map;
+}
+
+__isl_give isl_map *isl_map_lex_lt_map(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map *map;
+ map = isl_map_lex_lt(isl_space_range(isl_map_get_space(map1)));
+ map = isl_map_apply_domain(map, isl_map_reverse(map1));
+ map = isl_map_apply_range(map, isl_map_reverse(map2));
+ return map;
+}
+
+__isl_give isl_map *isl_map_lex_ge_map(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map *map;
+ map = isl_map_lex_ge(isl_space_range(isl_map_get_space(map1)));
+ map = isl_map_apply_domain(map, isl_map_reverse(map1));
+ map = isl_map_apply_range(map, isl_map_reverse(map2));
+ return map;
+}
+
+__isl_give isl_map *isl_map_lex_gt_map(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map *map;
+ map = isl_map_lex_gt(isl_space_range(isl_map_get_space(map1)));
+ map = isl_map_apply_domain(map, isl_map_reverse(map1));
+ map = isl_map_apply_range(map, isl_map_reverse(map2));
+ return map;
+}
+
+/* For the div d = floor(f/m) at position "div", add the constraint
+ *
+ * f - m d >= 0
+ */
+static __isl_give isl_basic_map *add_upper_div_constraint(
+ __isl_take isl_basic_map *bmap, unsigned div)
+{
+ int i;
+ isl_size v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ isl_size n_div;
+ unsigned pos;
+
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (v_div < 0 || n_div < 0)
+ return isl_basic_map_free(bmap);
+ pos = v_div + div;
+ i = isl_basic_map_alloc_inequality(bmap);
+ if (i < 0)
+ return isl_basic_map_free(bmap);
+ isl_seq_cpy(bmap->ineq[i], bmap->div[div] + 1, 1 + v_div + n_div);
+ isl_int_neg(bmap->ineq[i][1 + pos], bmap->div[div][0]);
+
+ return bmap;
+}
+
+/* For the div d = floor(f/m) at position "div", add the constraint
+ *
+ * -(f-(m-1)) + m d >= 0
+ */
+static __isl_give isl_basic_map *add_lower_div_constraint(
+ __isl_take isl_basic_map *bmap, unsigned div)
+{
+ int i;
+ isl_size v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ isl_size n_div;
+ unsigned pos;
+
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (v_div < 0 || n_div < 0)
+ return isl_basic_map_free(bmap);
+ pos = v_div + div;
+ i = isl_basic_map_alloc_inequality(bmap);
+ if (i < 0)
+ return isl_basic_map_free(bmap);
+ isl_seq_neg(bmap->ineq[i], bmap->div[div] + 1, 1 + v_div + n_div);
+ isl_int_set(bmap->ineq[i][1 + pos], bmap->div[div][0]);
+ isl_int_add(bmap->ineq[i][0], bmap->ineq[i][0], bmap->ineq[i][1 + pos]);
+ isl_int_sub_ui(bmap->ineq[i][0], bmap->ineq[i][0], 1);
+
+ return bmap;
+}
+
+/* For the div d = floor(f/m) at position "pos", add the constraints
+ *
+ * f - m d >= 0
+ * -(f-(m-1)) + m d >= 0
+ *
+ * Note that the second constraint is the negation of
+ *
+ * f - m d >= m
+ */
+__isl_give isl_basic_map *isl_basic_map_add_div_constraints(
+ __isl_take isl_basic_map *bmap, unsigned pos)
+{
+ bmap = add_upper_div_constraint(bmap, pos);
+ bmap = add_lower_div_constraint(bmap, pos);
+ return bmap;
+}
+
+/* For each known div d = floor(f/m), add the constraints
+ *
+ * f - m d >= 0
+ * -(f-(m-1)) + m d >= 0
+ *
+ * Remove duplicate constraints in case of some these div constraints
+ * already appear in "bmap".
+ */
+__isl_give isl_basic_map *isl_basic_map_add_known_div_constraints(
+ __isl_take isl_basic_map *bmap)
+{
+ isl_size n_div;
+
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0)
+ return isl_basic_map_free(bmap);
+ if (n_div == 0)
+ return bmap;
+
+ bmap = add_known_div_constraints(bmap);
+ bmap = isl_basic_map_remove_duplicate_constraints(bmap, NULL, 0);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+}
+
+/* Add the div constraint of sign "sign" for div "div" of "bmap".
+ *
+ * In particular, if this div is of the form d = floor(f/m),
+ * then add the constraint
+ *
+ * f - m d >= 0
+ *
+ * if sign < 0 or the constraint
+ *
+ * -(f-(m-1)) + m d >= 0
+ *
+ * if sign > 0.
+ */
+__isl_give isl_basic_map *isl_basic_map_add_div_constraint(
+ __isl_take isl_basic_map *bmap, unsigned div, int sign)
+{
+ if (sign < 0)
+ return add_upper_div_constraint(bmap, div);
+ else
+ return add_lower_div_constraint(bmap, div);
+}
+
+__isl_give isl_basic_set *isl_basic_map_underlying_set(
+ __isl_take isl_basic_map *bmap)
+{
+ isl_space *space;
+
+ if (!bmap)
+ goto error;
+ if (bmap->dim->nparam == 0 && bmap->dim->n_in == 0 &&
+ bmap->n_div == 0 &&
+ !isl_space_is_named_or_nested(bmap->dim, isl_dim_in) &&
+ !isl_space_is_named_or_nested(bmap->dim, isl_dim_out))
+ return bset_from_bmap(bmap);
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_underlying(space, bmap->n_div);
+ bmap = isl_basic_map_restore_space(bmap, space);
+ if (!bmap)
+ return NULL;
+ bmap->extra -= bmap->n_div;
+ bmap->n_div = 0;
+ bmap = isl_basic_map_finalize(bmap);
+ return bset_from_bmap(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_underlying_set(
+ __isl_take isl_basic_set *bset)
+{
+ return isl_basic_map_underlying_set(bset_to_bmap(bset));
+}
+
+/* Replace each element in "list" by the result of applying
+ * isl_basic_map_underlying_set to the element.
+ */
+__isl_give isl_basic_set_list *isl_basic_map_list_underlying_set(
+ __isl_take isl_basic_map_list *list)
+{
+ int i;
+ isl_size n;
+
+ n = isl_basic_map_list_n_basic_map(list);
+ if (n < 0)
+ goto error;
+
+ for (i = 0; i < n; ++i) {
+ isl_basic_map *bmap;
+ isl_basic_set *bset;
+
+ bmap = isl_basic_map_list_get_basic_map(list, i);
+ bset = isl_basic_set_underlying_set(bmap);
+ list = isl_basic_set_list_set_basic_set(list, i, bset);
+ }
+
+ return list;
+error:
+ isl_basic_map_list_free(list);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_overlying_set(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_map *like)
+{
+ struct isl_basic_map *bmap;
+ struct isl_ctx *ctx;
+ isl_size dim, bmap_total;
+ unsigned total;
+ int i;
+
+ if (!bset || !like)
+ goto error;
+ ctx = bset->ctx;
+ if (isl_basic_set_check_no_params(bset) < 0 ||
+ isl_basic_set_check_no_locals(bset) < 0)
+ goto error;
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ bmap_total = isl_basic_map_dim(like, isl_dim_all);
+ if (dim < 0 || bmap_total < 0)
+ goto error;
+ isl_assert(ctx, dim == bmap_total, goto error);
+ if (like->n_div == 0) {
+ isl_space *space = isl_basic_map_get_space(like);
+ isl_basic_map_free(like);
+ return isl_basic_map_reset_space(bset, space);
+ }
+ bset = isl_basic_set_cow(bset);
+ if (!bset)
+ goto error;
+ total = dim + bset->extra;
+ bmap = bset_to_bmap(bset);
+ isl_space_free(isl_basic_map_take_space(bmap));
+ bmap = isl_basic_map_restore_space(bmap, isl_basic_map_get_space(like));
+ if (!bmap)
+ goto error;
+ bmap->n_div = like->n_div;
+ bmap->extra += like->n_div;
+ if (bmap->extra) {
+ unsigned ltotal;
+ isl_int **div;
+ ltotal = total - bmap->extra + like->extra;
+ if (ltotal > total)
+ ltotal = total;
+ bmap->block2 = isl_blk_extend(ctx, bmap->block2,
+ bmap->extra * (1 + 1 + total));
+ if (isl_blk_is_error(bmap->block2))
+ goto error;
+ div = isl_realloc_array(ctx, bmap->div, isl_int *, bmap->extra);
+ if (!div)
+ goto error;
+ bmap->div = div;
+ for (i = 0; i < bmap->extra; ++i)
+ bmap->div[i] = bmap->block2.data + i * (1 + 1 + total);
+ for (i = 0; i < like->n_div; ++i) {
+ isl_seq_cpy(bmap->div[i], like->div[i], 1 + 1 + ltotal);
+ isl_seq_clr(bmap->div[i]+1+1+ltotal, total - ltotal);
+ }
+ bmap = isl_basic_map_add_known_div_constraints(bmap);
+ }
+ isl_basic_map_free(like);
+ bmap = isl_basic_map_simplify(bmap);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+error:
+ isl_basic_map_free(like);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_from_underlying_set(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *like)
+{
+ return bset_from_bmap(isl_basic_map_overlying_set(bset,
+ bset_to_bmap(like)));
+}
+
+__isl_give isl_set *isl_map_underlying_set(__isl_take isl_map *map)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+ map->dim = isl_space_cow(map->dim);
+ if (!map->dim)
+ goto error;
+
+ for (i = 1; i < map->n; ++i)
+ isl_assert(map->ctx, map->p[0]->n_div == map->p[i]->n_div,
+ goto error);
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = bset_to_bmap(
+ isl_basic_map_underlying_set(map->p[i]));
+ if (!map->p[i])
+ goto error;
+ }
+ if (map->n == 0)
+ map->dim = isl_space_underlying(map->dim, 0);
+ else {
+ isl_space_free(map->dim);
+ map->dim = isl_space_copy(map->p[0]->dim);
+ }
+ if (!map->dim)
+ goto error;
+ return set_from_map(map);
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Replace the space of "bmap" by "space".
+ *
+ * If the space of "bmap" is identical to "space" (including the identifiers
+ * of the input and output dimensions), then simply return the original input.
+ */
+__isl_give isl_basic_map *isl_basic_map_reset_space(
+ __isl_take isl_basic_map *bmap, __isl_take isl_space *space)
+{
+ isl_bool equal;
+ isl_space *bmap_space;
+
+ bmap_space = isl_basic_map_peek_space(bmap);
+ equal = isl_space_is_equal(bmap_space, space);
+ if (equal >= 0 && equal)
+ equal = isl_space_has_equal_ids(bmap_space, space);
+ if (equal < 0)
+ goto error;
+ if (equal) {
+ isl_space_free(space);
+ return bmap;
+ }
+ isl_space_free(isl_basic_map_take_space(bmap));
+ bmap = isl_basic_map_restore_space(bmap, space);
+
+ bmap = isl_basic_map_finalize(bmap);
+
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_reset_space(
+ __isl_take isl_basic_set *bset, __isl_take isl_space *space)
+{
+ return bset_from_bmap(isl_basic_map_reset_space(bset_to_bmap(bset),
+ space));
+}
+
+/* Check that the total dimensions of "map" and "space" are the same.
+ */
+static isl_stat check_map_space_equal_total_dim(__isl_keep isl_map *map,
+ __isl_keep isl_space *space)
+{
+ isl_size dim1, dim2;
+
+ dim1 = isl_map_dim(map, isl_dim_all);
+ dim2 = isl_space_dim(space, isl_dim_all);
+ if (dim1 < 0 || dim2 < 0)
+ return isl_stat_error;
+ if (dim1 == dim2)
+ return isl_stat_ok;
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "total dimensions do not match", return isl_stat_error);
+}
+
+__isl_give isl_map *isl_map_reset_space(__isl_take isl_map *map,
+ __isl_take isl_space *space)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (!map || !space)
+ goto error;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_reset_space(map->p[i],
+ isl_space_copy(space));
+ if (!map->p[i])
+ goto error;
+ }
+ isl_space_free(isl_map_take_space(map));
+ map = isl_map_restore_space(map, space);
+
+ return map;
+error:
+ isl_map_free(map);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Replace the space of "map" by "space", without modifying
+ * the dimension of "map".
+ *
+ * If the space of "map" is identical to "space" (including the identifiers
+ * of the input and output dimensions), then simply return the original input.
+ */
+__isl_give isl_map *isl_map_reset_equal_dim_space(__isl_take isl_map *map,
+ __isl_take isl_space *space)
+{
+ isl_bool equal;
+ isl_space *map_space;
+
+ map_space = isl_map_peek_space(map);
+ equal = isl_space_is_equal(map_space, space);
+ if (equal >= 0 && equal)
+ equal = isl_space_has_equal_ids(map_space, space);
+ if (equal < 0)
+ goto error;
+ if (equal) {
+ isl_space_free(space);
+ return map;
+ }
+ if (check_map_space_equal_total_dim(map, space) < 0)
+ goto error;
+ return isl_map_reset_space(map, space);
+error:
+ isl_map_free(map);
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_reset_space(__isl_take isl_set *set,
+ __isl_take isl_space *space)
+{
+ return set_from_map(isl_map_reset_space(set_to_map(set), space));
+}
+
+/* Compute the parameter domain of the given basic set.
+ */
+__isl_give isl_basic_set *isl_basic_set_params(__isl_take isl_basic_set *bset)
+{
+ isl_bool is_params;
+ isl_space *space;
+ isl_size n;
+
+ is_params = isl_basic_set_is_params(bset);
+ if (is_params < 0)
+ return isl_basic_set_free(bset);
+ if (is_params)
+ return bset;
+
+ n = isl_basic_set_dim(bset, isl_dim_set);
+ if (n < 0)
+ return isl_basic_set_free(bset);
+ bset = isl_basic_set_project_out(bset, isl_dim_set, 0, n);
+ space = isl_basic_set_get_space(bset);
+ space = isl_space_params(space);
+ bset = isl_basic_set_reset_space(bset, space);
+ return bset;
+}
+
+/* Construct a zero-dimensional basic set with the given parameter domain.
+ */
+__isl_give isl_basic_set *isl_basic_set_from_params(
+ __isl_take isl_basic_set *bset)
+{
+ isl_space *space;
+ space = isl_basic_set_get_space(bset);
+ space = isl_space_set_from_params(space);
+ bset = isl_basic_set_reset_space(bset, space);
+ return bset;
+}
+
+/* Compute the parameter domain of the given set.
+ */
+__isl_give isl_set *isl_set_params(__isl_take isl_set *set)
+{
+ return isl_map_params(set_to_map(set));
+}
+
+/* Construct a zero-dimensional set with the given parameter domain.
+ */
+__isl_give isl_set *isl_set_from_params(__isl_take isl_set *set)
+{
+ isl_space *space;
+ space = isl_set_get_space(set);
+ space = isl_space_set_from_params(space);
+ set = isl_set_reset_space(set, space);
+ return set;
+}
+
+/* Compute the parameter domain of the given map.
+ */
+__isl_give isl_set *isl_map_params(__isl_take isl_map *map)
+{
+ isl_space *space;
+ isl_size n_in, n_out;
+
+ n_in = isl_map_dim(map, isl_dim_in);
+ n_out = isl_map_dim(map, isl_dim_out);
+ if (n_in < 0 || n_out < 0)
+ return isl_map_free(map);
+ map = isl_map_project_out(map, isl_dim_in, 0, n_in);
+ map = isl_map_project_out(map, isl_dim_out, 0, n_out);
+ space = isl_map_get_space(map);
+ space = isl_space_params(space);
+ map = isl_map_reset_space(map, space);
+ return map;
+}
+
+__isl_give isl_basic_set *isl_basic_map_domain(__isl_take isl_basic_map *bmap)
+{
+ isl_space *space;
+ isl_size n_out;
+
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ if (n_out < 0)
+ return isl_basic_map_free(bmap);
+ space = isl_space_domain(isl_basic_map_get_space(bmap));
+
+ bmap = isl_basic_map_project_out(bmap, isl_dim_out, 0, n_out);
+
+ return isl_basic_map_reset_space(bmap, space);
+}
+
+isl_bool isl_basic_map_may_be_set(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return isl_bool_error;
+ return isl_space_may_be_set(bmap->dim);
+}
+
+/* Is this basic map actually a set?
+ * Users should never call this function. Outside of isl,
+ * the type should indicate whether something is a set or a map.
+ */
+isl_bool isl_basic_map_is_set(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return isl_bool_error;
+ return isl_space_is_set(bmap->dim);
+}
+
+__isl_give isl_basic_set *isl_basic_map_range(__isl_take isl_basic_map *bmap)
+{
+ isl_bool is_set;
+
+ is_set = isl_basic_map_is_set(bmap);
+ if (is_set < 0)
+ goto error;
+ if (is_set)
+ return bmap;
+ return isl_basic_map_domain(isl_basic_map_reverse(bmap));
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_domain_map(
+ __isl_take isl_basic_map *bmap)
+{
+ int i;
+ isl_space *space;
+ isl_basic_map *domain;
+ isl_size nparam, n_in, n_out;
+
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ if (nparam < 0 || n_in < 0 || n_out < 0)
+ return isl_basic_map_free(bmap);
+
+ space = isl_basic_map_get_space(bmap);
+ space = isl_space_from_range(isl_space_domain(space));
+ domain = isl_basic_map_universe(space);
+
+ bmap = isl_basic_map_from_domain(isl_basic_map_wrap(bmap));
+ bmap = isl_basic_map_apply_range(bmap, domain);
+ bmap = isl_basic_map_extend_constraints(bmap, n_in, 0);
+
+ for (i = 0; i < n_in; ++i)
+ bmap = isl_basic_map_equate(bmap, isl_dim_in, i,
+ isl_dim_out, i);
+
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ return isl_basic_map_finalize(bmap);
+}
+
+__isl_give isl_basic_map *isl_basic_map_range_map(
+ __isl_take isl_basic_map *bmap)
+{
+ int i;
+ isl_space *space;
+ isl_basic_map *range;
+ isl_size nparam, n_in, n_out;
+
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ if (nparam < 0 || n_in < 0 || n_out < 0)
+ return isl_basic_map_free(bmap);
+
+ space = isl_basic_map_get_space(bmap);
+ space = isl_space_from_range(isl_space_range(space));
+ range = isl_basic_map_universe(space);
+
+ bmap = isl_basic_map_from_domain(isl_basic_map_wrap(bmap));
+ bmap = isl_basic_map_apply_range(bmap, range);
+ bmap = isl_basic_map_extend_constraints(bmap, n_out, 0);
+
+ for (i = 0; i < n_out; ++i)
+ bmap = isl_basic_map_equate(bmap, isl_dim_in, n_in + i,
+ isl_dim_out, i);
+
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ return isl_basic_map_finalize(bmap);
+}
+
+int isl_map_may_be_set(__isl_keep isl_map *map)
+{
+ if (!map)
+ return -1;
+ return isl_space_may_be_set(map->dim);
+}
+
+/* Is this map actually a set?
+ * Users should never call this function. Outside of isl,
+ * the type should indicate whether something is a set or a map.
+ */
+isl_bool isl_map_is_set(__isl_keep isl_map *map)
+{
+ if (!map)
+ return isl_bool_error;
+ return isl_space_is_set(map->dim);
+}
+
+__isl_give isl_set *isl_map_range(__isl_take isl_map *map)
+{
+ isl_space *space;
+ isl_size n_in;
+
+ n_in = isl_map_dim(map, isl_dim_in);
+ if (n_in < 0)
+ return set_from_map(isl_map_free(map));
+ space = isl_space_range(isl_map_get_space(map));
+
+ map = isl_map_project_out(map, isl_dim_in, 0, n_in);
+
+ return set_from_map(isl_map_reset_space(map, space));
+}
+
+/* Transform "map" by applying "fn_space" to its space and "fn_bmap"
+ * to each of its basic maps.
+ */
+static __isl_give isl_map *isl_map_transform(__isl_take isl_map *map,
+ __isl_give isl_space *(*fn_space)(__isl_take isl_space *space),
+ __isl_give isl_basic_map *(*fn_bmap)(__isl_take isl_basic_map *bmap))
+{
+ int i;
+ isl_space *space;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = fn_bmap(map->p[i]);
+ if (!map->p[i])
+ return isl_map_free(map);
+ }
+ map = isl_map_unmark_normalized(map);
+
+ space = isl_map_take_space(map);
+ space = fn_space(space);
+ map = isl_map_restore_space(map, space);
+
+ return map;
+}
+
+__isl_give isl_map *isl_map_domain_map(__isl_take isl_map *map)
+{
+ return isl_map_transform(map, &isl_space_domain_map,
+ &isl_basic_map_domain_map);
+}
+
+__isl_give isl_map *isl_map_range_map(__isl_take isl_map *map)
+{
+ return isl_map_transform(map, &isl_space_range_map,
+ &isl_basic_map_range_map);
+}
+
+/* Given a wrapped map of the form A[B -> C],
+ * return the map A[B -> C] -> B.
+ */
+__isl_give isl_map *isl_set_wrapped_domain_map(__isl_take isl_set *set)
+{
+ isl_id *id;
+ isl_map *map;
+
+ if (!set)
+ return NULL;
+ if (!isl_set_has_tuple_id(set))
+ return isl_map_domain_map(isl_set_unwrap(set));
+
+ id = isl_set_get_tuple_id(set);
+ map = isl_map_domain_map(isl_set_unwrap(set));
+ map = isl_map_set_tuple_id(map, isl_dim_in, id);
+
+ return map;
+}
+
+__isl_give isl_basic_map *isl_basic_map_from_domain(
+ __isl_take isl_basic_set *bset)
+{
+ return isl_basic_map_reverse(isl_basic_map_from_range(bset));
+}
+
+__isl_give isl_basic_map *isl_basic_map_from_range(
+ __isl_take isl_basic_set *bset)
+{
+ isl_space *space;
+ space = isl_basic_set_get_space(bset);
+ space = isl_space_from_range(space);
+ bset = isl_basic_set_reset_space(bset, space);
+ return bset_to_bmap(bset);
+}
+
+/* Create a relation with the given set as range.
+ * The domain of the created relation is a zero-dimensional
+ * flat anonymous space.
+ */
+__isl_give isl_map *isl_map_from_range(__isl_take isl_set *set)
+{
+ isl_space *space;
+ space = isl_set_get_space(set);
+ space = isl_space_from_range(space);
+ set = isl_set_reset_space(set, space);
+ return set_to_map(set);
+}
+
+/* Create a relation with the given set as domain.
+ * The range of the created relation is a zero-dimensional
+ * flat anonymous space.
+ */
+__isl_give isl_map *isl_map_from_domain(__isl_take isl_set *set)
+{
+ return isl_map_reverse(isl_map_from_range(set));
+}
+
+__isl_give isl_basic_map *isl_basic_map_from_domain_and_range(
+ __isl_take isl_basic_set *domain, __isl_take isl_basic_set *range)
+{
+ return isl_basic_map_apply_range(isl_basic_map_reverse(domain), range);
+}
+
+__isl_give isl_map *isl_map_from_domain_and_range(__isl_take isl_set *domain,
+ __isl_take isl_set *range)
+{
+ return isl_map_apply_range(isl_map_reverse(domain), range);
+}
+
+/* Return a newly allocated isl_map with given space and flags and
+ * room for "n" basic maps.
+ * Make sure that all cached information is cleared.
+ */
+__isl_give isl_map *isl_map_alloc_space(__isl_take isl_space *space, int n,
+ unsigned flags)
+{
+ struct isl_map *map;
+
+ if (!space)
+ return NULL;
+ if (n < 0)
+ isl_die(space->ctx, isl_error_internal,
+ "negative number of basic maps", goto error);
+ map = isl_calloc(space->ctx, struct isl_map,
+ sizeof(struct isl_map) +
+ (n - 1) * sizeof(struct isl_basic_map *));
+ if (!map)
+ goto error;
+
+ map->ctx = space->ctx;
+ isl_ctx_ref(map->ctx);
+ map->ref = 1;
+ map->size = n;
+ map->n = 0;
+ map->dim = space;
+ map->flags = flags;
+ return map;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_empty(__isl_take isl_space *space)
+{
+ struct isl_basic_map *bmap;
+ bmap = isl_basic_map_alloc_space(space, 0, 1, 0);
+ bmap = isl_basic_map_set_to_empty(bmap);
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_empty(__isl_take isl_space *space)
+{
+ struct isl_basic_set *bset;
+ bset = isl_basic_set_alloc_space(space, 0, 1, 0);
+ bset = isl_basic_set_set_to_empty(bset);
+ return bset;
+}
+
+__isl_give isl_basic_map *isl_basic_map_universe(__isl_take isl_space *space)
+{
+ struct isl_basic_map *bmap;
+ bmap = isl_basic_map_alloc_space(space, 0, 0, 0);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_universe(__isl_take isl_space *space)
+{
+ struct isl_basic_set *bset;
+ bset = isl_basic_set_alloc_space(space, 0, 0, 0);
+ bset = isl_basic_set_finalize(bset);
+ return bset;
+}
+
+__isl_give isl_basic_map *isl_basic_map_nat_universe(
+ __isl_take isl_space *space)
+{
+ int i;
+ isl_size total = isl_space_dim(space, isl_dim_all);
+ isl_basic_map *bmap;
+
+ if (total < 0)
+ space = isl_space_free(space);
+ bmap = isl_basic_map_alloc_space(space, 0, 0, total);
+ for (i = 0; i < total; ++i) {
+ int k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[k], 1 + total);
+ isl_int_set_si(bmap->ineq[k][1 + i], 1);
+ }
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_nat_universe(
+ __isl_take isl_space *space)
+{
+ return isl_basic_map_nat_universe(space);
+}
+
+__isl_give isl_map *isl_map_nat_universe(__isl_take isl_space *space)
+{
+ return isl_map_from_basic_map(isl_basic_map_nat_universe(space));
+}
+
+__isl_give isl_set *isl_set_nat_universe(__isl_take isl_space *space)
+{
+ return isl_map_nat_universe(space);
+}
+
+__isl_give isl_map *isl_map_empty(__isl_take isl_space *space)
+{
+ return isl_map_alloc_space(space, 0, ISL_MAP_DISJOINT);
+}
+
+__isl_give isl_set *isl_set_empty(__isl_take isl_space *space)
+{
+ return isl_set_alloc_space(space, 0, ISL_MAP_DISJOINT);
+}
+
+__isl_give isl_map *isl_map_universe(__isl_take isl_space *space)
+{
+ struct isl_map *map;
+ if (!space)
+ return NULL;
+ map = isl_map_alloc_space(isl_space_copy(space), 1, ISL_MAP_DISJOINT);
+ map = isl_map_add_basic_map(map, isl_basic_map_universe(space));
+ return map;
+}
+
+/* This function performs the same operation as isl_map_universe,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give isl_map *isl_space_universe_map(__isl_take isl_space *space)
+{
+ return isl_map_universe(space);
+}
+
+__isl_give isl_set *isl_set_universe(__isl_take isl_space *space)
+{
+ struct isl_set *set;
+ if (!space)
+ return NULL;
+ set = isl_set_alloc_space(isl_space_copy(space), 1, ISL_MAP_DISJOINT);
+ set = isl_set_add_basic_set(set, isl_basic_set_universe(space));
+ return set;
+}
+
+/* This function performs the same operation as isl_set_universe,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give isl_set *isl_space_universe_set(__isl_take isl_space *space)
+{
+ return isl_set_universe(space);
+}
+
+__isl_give isl_map *isl_map_dup(__isl_keep isl_map *map)
+{
+ int i;
+ struct isl_map *dup;
+
+ if (!map)
+ return NULL;
+ dup = isl_map_alloc_space(isl_space_copy(map->dim), map->n, map->flags);
+ for (i = 0; i < map->n; ++i)
+ dup = isl_map_add_basic_map(dup, isl_basic_map_copy(map->p[i]));
+ return dup;
+}
+
+__isl_give isl_map *isl_map_add_basic_map(__isl_take isl_map *map,
+ __isl_take isl_basic_map *bmap)
+{
+ if (!bmap || !map)
+ goto error;
+ if (isl_basic_map_plain_is_empty(bmap)) {
+ isl_basic_map_free(bmap);
+ return map;
+ }
+ if (isl_map_basic_map_check_equal_space(map, bmap) < 0)
+ goto error;
+ isl_assert(map->ctx, map->n < map->size, goto error);
+ map->p[map->n] = bmap;
+ map->n++;
+ map = isl_map_unmark_normalized(map);
+ return map;
+error:
+ if (map)
+ isl_map_free(map);
+ if (bmap)
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_null isl_map *isl_map_free(__isl_take isl_map *map)
+{
+ int i;
+
+ if (!map)
+ return NULL;
+
+ if (--map->ref > 0)
+ return NULL;
+
+ clear_caches(map);
+ isl_ctx_deref(map->ctx);
+ for (i = 0; i < map->n; ++i)
+ isl_basic_map_free(map->p[i]);
+ isl_space_free(map->dim);
+ free(map);
+
+ return NULL;
+}
+
+static __isl_give isl_basic_map *isl_basic_map_fix_pos_si(
+ __isl_take isl_basic_map *bmap, unsigned pos, int value)
+{
+ int j;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+
+ bmap = isl_basic_map_cow(bmap);
+ bmap = isl_basic_map_extend_constraints(bmap, 1, 0);
+ j = isl_basic_map_alloc_equality(bmap);
+ if (j < 0)
+ goto error;
+ isl_seq_clr(bmap->eq[j] + 1, total);
+ isl_int_set_si(bmap->eq[j][pos], -1);
+ isl_int_set_si(bmap->eq[j][0], value);
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+static __isl_give isl_basic_map *isl_basic_map_fix_pos(
+ __isl_take isl_basic_map *bmap, unsigned pos, isl_int value)
+{
+ int j;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+
+ bmap = isl_basic_map_cow(bmap);
+ bmap = isl_basic_map_extend_constraints(bmap, 1, 0);
+ j = isl_basic_map_alloc_equality(bmap);
+ if (j < 0)
+ goto error;
+ isl_seq_clr(bmap->eq[j] + 1, total);
+ isl_int_set_si(bmap->eq[j][pos], -1);
+ isl_int_set(bmap->eq[j][0], value);
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_fix_si(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, int value)
+{
+ if (isl_basic_map_check_range(bmap, type, pos, 1) < 0)
+ return isl_basic_map_free(bmap);
+ return isl_basic_map_fix_pos_si(bmap,
+ isl_basic_map_offset(bmap, type) + pos, value);
+}
+
+__isl_give isl_basic_map *isl_basic_map_fix(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, isl_int value)
+{
+ if (isl_basic_map_check_range(bmap, type, pos, 1) < 0)
+ return isl_basic_map_free(bmap);
+ return isl_basic_map_fix_pos(bmap,
+ isl_basic_map_offset(bmap, type) + pos, value);
+}
+
+/* Fix the value of the variable at position "pos" of type "type" of "bmap"
+ * to be equal to "v".
+ */
+__isl_give isl_basic_map *isl_basic_map_fix_val(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *v)
+{
+ if (!bmap || !v)
+ goto error;
+ if (!isl_val_is_int(v))
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "expecting integer value", goto error);
+ if (isl_basic_map_check_range(bmap, type, pos, 1) < 0)
+ goto error;
+ pos += isl_basic_map_offset(bmap, type);
+ bmap = isl_basic_map_fix_pos(bmap, pos, v->n);
+ isl_val_free(v);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ isl_val_free(v);
+ return NULL;
+}
+
+/* Fix the value of the variable at position "pos" of type "type" of "bset"
+ * to be equal to "v".
+ */
+__isl_give isl_basic_set *isl_basic_set_fix_val(__isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *v)
+{
+ return isl_basic_map_fix_val(bset, type, pos, v);
+}
+
+__isl_give isl_basic_set *isl_basic_set_fix_si(__isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos, int value)
+{
+ return bset_from_bmap(isl_basic_map_fix_si(bset_to_bmap(bset),
+ type, pos, value));
+}
+
+__isl_give isl_basic_set *isl_basic_set_fix(__isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos, isl_int value)
+{
+ return bset_from_bmap(isl_basic_map_fix(bset_to_bmap(bset),
+ type, pos, value));
+}
+
+/* Remove the basic map at position "i" from "map" if this basic map
+ * is (obviously) empty.
+ */
+static __isl_give isl_map *remove_if_empty(__isl_take isl_map *map, int i)
+{
+ isl_bool empty;
+
+ if (!map)
+ return NULL;
+
+ empty = isl_basic_map_plain_is_empty(map->p[i]);
+ if (empty < 0)
+ return isl_map_free(map);
+ if (!empty)
+ return map;
+
+ isl_basic_map_free(map->p[i]);
+ map->n--;
+ if (i != map->n) {
+ map->p[i] = map->p[map->n];
+ map = isl_map_unmark_normalized(map);
+
+ }
+
+ return map;
+}
+
+/* Perform "fn" on each basic map of "map", where we may not be holding
+ * the only reference to "map".
+ * In particular, "fn" should be a semantics preserving operation
+ * that we want to apply to all copies of "map". We therefore need
+ * to be careful not to modify "map" in a way that breaks "map"
+ * in case anything goes wrong.
+ */
+__isl_give isl_map *isl_map_inline_foreach_basic_map(__isl_take isl_map *map,
+ __isl_give isl_basic_map *(*fn)(__isl_take isl_basic_map *bmap))
+{
+ struct isl_basic_map *bmap;
+ int i;
+
+ if (!map)
+ return NULL;
+
+ for (i = map->n - 1; i >= 0; --i) {
+ bmap = isl_basic_map_copy(map->p[i]);
+ bmap = fn(bmap);
+ if (!bmap)
+ goto error;
+ isl_basic_map_free(map->p[i]);
+ map->p[i] = bmap;
+ map = remove_if_empty(map, i);
+ if (!map)
+ return NULL;
+ }
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_map *isl_map_fix_si(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, int value)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (isl_map_check_range(map, type, pos, 1) < 0)
+ return isl_map_free(map);
+ for (i = map->n - 1; i >= 0; --i) {
+ map->p[i] = isl_basic_map_fix_si(map->p[i], type, pos, value);
+ map = remove_if_empty(map, i);
+ if (!map)
+ return NULL;
+ }
+ map = isl_map_unmark_normalized(map);
+ return map;
+}
+
+__isl_give isl_set *isl_set_fix_si(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, int value)
+{
+ return set_from_map(isl_map_fix_si(set_to_map(set), type, pos, value));
+}
+
+__isl_give isl_map *isl_map_fix(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, isl_int value)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (isl_map_check_range(map, type, pos, 1) < 0)
+ return isl_map_free(map);
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_fix(map->p[i], type, pos, value);
+ if (!map->p[i])
+ goto error;
+ }
+ map = isl_map_unmark_normalized(map);
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_fix(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, isl_int value)
+{
+ return set_from_map(isl_map_fix(set_to_map(set), type, pos, value));
+}
+
+/* Fix the value of the variable at position "pos" of type "type" of "map"
+ * to be equal to "v".
+ */
+__isl_give isl_map *isl_map_fix_val(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *v)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (!map || !v)
+ goto error;
+
+ if (!isl_val_is_int(v))
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "expecting integer value", goto error);
+ if (isl_map_check_range(map, type, pos, 1) < 0)
+ goto error;
+ for (i = map->n - 1; i >= 0; --i) {
+ map->p[i] = isl_basic_map_fix_val(map->p[i], type, pos,
+ isl_val_copy(v));
+ map = remove_if_empty(map, i);
+ if (!map)
+ goto error;
+ }
+ map = isl_map_unmark_normalized(map);
+ isl_val_free(v);
+ return map;
+error:
+ isl_map_free(map);
+ isl_val_free(v);
+ return NULL;
+}
+
+/* Fix the value of the variable at position "pos" of type "type" of "set"
+ * to be equal to "v".
+ */
+__isl_give isl_set *isl_set_fix_val(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *v)
+{
+ return isl_map_fix_val(set, type, pos, v);
+}
+
+__isl_give isl_map *isl_map_fix_input_si(__isl_take isl_map *map,
+ unsigned input, int value)
+{
+ return isl_map_fix_si(map, isl_dim_in, input, value);
+}
+
+__isl_give isl_set *isl_set_fix_dim_si(__isl_take isl_set *set, unsigned dim,
+ int value)
+{
+ return set_from_map(isl_map_fix_si(set_to_map(set),
+ isl_dim_set, dim, value));
+}
+
+static __isl_give isl_basic_map *basic_map_bound_si(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, int value, int upper)
+{
+ int j;
+ isl_size total;
+
+ if (isl_basic_map_check_range(bmap, type, pos, 1) < 0)
+ return isl_basic_map_free(bmap);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ pos += isl_basic_map_offset(bmap, type);
+ bmap = isl_basic_map_cow(bmap);
+ bmap = isl_basic_map_extend_constraints(bmap, 0, 1);
+ j = isl_basic_map_alloc_inequality(bmap);
+ if (j < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[j], 1 + total);
+ if (upper) {
+ isl_int_set_si(bmap->ineq[j][pos], -1);
+ isl_int_set_si(bmap->ineq[j][0], value);
+ } else {
+ isl_int_set_si(bmap->ineq[j][pos], 1);
+ isl_int_set_si(bmap->ineq[j][0], -value);
+ }
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_lower_bound_si(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, int value)
+{
+ return basic_map_bound_si(bmap, type, pos, value, 0);
+}
+
+/* Constrain the values of the given dimension to be no greater than "value".
+ */
+__isl_give isl_basic_map *isl_basic_map_upper_bound_si(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, int value)
+{
+ return basic_map_bound_si(bmap, type, pos, value, 1);
+}
+
+static __isl_give isl_map *map_bound_si(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, int value, int upper)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (isl_map_check_range(map, type, pos, 1) < 0)
+ return isl_map_free(map);
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = basic_map_bound_si(map->p[i],
+ type, pos, value, upper);
+ if (!map->p[i])
+ goto error;
+ }
+ map = isl_map_unmark_normalized(map);
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_map *isl_map_lower_bound_si(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, int value)
+{
+ return map_bound_si(map, type, pos, value, 0);
+}
+
+__isl_give isl_map *isl_map_upper_bound_si(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, int value)
+{
+ return map_bound_si(map, type, pos, value, 1);
+}
+
+__isl_give isl_set *isl_set_lower_bound_si(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, int value)
+{
+ return set_from_map(isl_map_lower_bound_si(set_to_map(set),
+ type, pos, value));
+}
+
+__isl_give isl_set *isl_set_upper_bound_si(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, int value)
+{
+ return isl_map_upper_bound_si(set, type, pos, value);
+}
+
+/* Bound the given variable of "bmap" from below (or above is "upper"
+ * is set) to "value".
+ */
+static __isl_give isl_basic_map *basic_map_bound(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, isl_int value, int upper)
+{
+ int j;
+ isl_size total;
+
+ if (isl_basic_map_check_range(bmap, type, pos, 1) < 0)
+ return isl_basic_map_free(bmap);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ pos += isl_basic_map_offset(bmap, type);
+ bmap = isl_basic_map_cow(bmap);
+ bmap = isl_basic_map_extend_constraints(bmap, 0, 1);
+ j = isl_basic_map_alloc_inequality(bmap);
+ if (j < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[j], 1 + total);
+ if (upper) {
+ isl_int_set_si(bmap->ineq[j][pos], -1);
+ isl_int_set(bmap->ineq[j][0], value);
+ } else {
+ isl_int_set_si(bmap->ineq[j][pos], 1);
+ isl_int_neg(bmap->ineq[j][0], value);
+ }
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Bound the given variable of "map" from below (or above is "upper"
+ * is set) to "value".
+ */
+static __isl_give isl_map *map_bound(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, isl_int value, int upper)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (isl_map_check_range(map, type, pos, 1) < 0)
+ return isl_map_free(map);
+ for (i = map->n - 1; i >= 0; --i) {
+ map->p[i] = basic_map_bound(map->p[i], type, pos, value, upper);
+ map = remove_if_empty(map, i);
+ if (!map)
+ return NULL;
+ }
+ map = isl_map_unmark_normalized(map);
+ return map;
+}
+
+__isl_give isl_map *isl_map_lower_bound(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, isl_int value)
+{
+ return map_bound(map, type, pos, value, 0);
+}
+
+__isl_give isl_map *isl_map_upper_bound(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, isl_int value)
+{
+ return map_bound(map, type, pos, value, 1);
+}
+
+__isl_give isl_set *isl_set_lower_bound(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, isl_int value)
+{
+ return isl_map_lower_bound(set, type, pos, value);
+}
+
+__isl_give isl_set *isl_set_upper_bound(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, isl_int value)
+{
+ return isl_map_upper_bound(set, type, pos, value);
+}
+
+/* Force the values of the variable at position "pos" of type "type" of "map"
+ * to be no smaller than "value".
+ */
+__isl_give isl_map *isl_map_lower_bound_val(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *value)
+{
+ if (!value)
+ goto error;
+ if (!isl_val_is_int(value))
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "expecting integer value", goto error);
+ map = isl_map_lower_bound(map, type, pos, value->n);
+ isl_val_free(value);
+ return map;
+error:
+ isl_val_free(value);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Force the values of the variable at position "pos" of type "type" of "set"
+ * to be no smaller than "value".
+ */
+__isl_give isl_set *isl_set_lower_bound_val(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *value)
+{
+ isl_map *map;
+
+ map = set_to_map(set);
+ return set_from_map(isl_map_lower_bound_val(map, type, pos, value));
+}
+
+/* Force the values of the variable at position "pos" of type "type" of "map"
+ * to be no greater than "value".
+ */
+__isl_give isl_map *isl_map_upper_bound_val(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *value)
+{
+ if (!value)
+ goto error;
+ if (!isl_val_is_int(value))
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "expecting integer value", goto error);
+ map = isl_map_upper_bound(map, type, pos, value->n);
+ isl_val_free(value);
+ return map;
+error:
+ isl_val_free(value);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Force the values of the variable at position "pos" of type "type" of "set"
+ * to be no greater than "value".
+ */
+__isl_give isl_set *isl_set_upper_bound_val(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *value)
+{
+ isl_map *map;
+
+ map = set_to_map(set);
+ return set_from_map(isl_map_upper_bound_val(map, type, pos, value));
+}
+
+/* If "mv" has an explicit domain, then intersect the domain of "map"
+ * with this explicit domain.
+ *
+ * An isl_multi_val object never has an explicit domain,
+ * so simply return "map".
+ */
+static __isl_give isl_map *isl_map_intersect_multi_val_explicit_domain(
+ __isl_take isl_map *map, __isl_keep isl_multi_val *mv)
+{
+ return map;
+}
+
+#undef BASE
+#define BASE val
+#include "isl_map_bound_templ.c"
+
+/* Apply "map_bound" to "set" with the corresponding value in "bound"
+ * for each set dimension, by treating the set as a map.
+ */
+static __isl_give isl_set *set_bound_multi_val(__isl_take isl_set *set,
+ __isl_take isl_multi_val *bound,
+ __isl_give isl_map *map_bound(__isl_take isl_map *map,
+ unsigned pos, __isl_take isl_val *value))
+{
+ isl_map *map;
+
+ map = set_to_map(set);
+ return set_from_map(map_bound_multi_val(map, bound, map_bound));
+}
+
+#undef BASE
+#define BASE pw_aff
+#include "isl_map_bound_templ.c"
+
+/* Apply "map_bound" to "set" with the corresponding value in "bound"
+ * for each set dimension, by converting the set and the bound
+ * to objects living in a map space.
+ */
+static __isl_give isl_set *set_bound_multi_pw_aff(__isl_take isl_set *set,
+ __isl_take isl_multi_pw_aff *bound,
+ __isl_give isl_map *set_bound(__isl_take isl_map *map,
+ unsigned pos, __isl_take TYPE *value))
+{
+ isl_map *map;
+
+ map = isl_map_from_range(set);
+ bound = isl_multi_pw_aff_from_range(bound);
+ map = map_bound_multi_pw_aff(map, bound, set_bound);
+ return isl_map_range(map);
+}
+
+/* Wrapper around isl_map_lower_bound_val for use in map_bound_multi_val,
+ * setting a bound on the given output dimension.
+ */
+static __isl_give isl_map *map_lower_bound_val(__isl_take isl_map *map,
+ unsigned pos, __isl_take isl_val *v)
+{
+ return isl_map_lower_bound_val(map, isl_dim_out, pos, v);
+}
+
+/* Force the values of the set dimensions of "set"
+ * to be no smaller than the corresponding values in "lower".
+ */
+__isl_give isl_set *isl_set_lower_bound_multi_val(__isl_take isl_set *set,
+ __isl_take isl_multi_val *lower)
+{
+ return set_bound_multi_val(set, lower, &map_lower_bound_val);
+}
+
+/* Wrapper around isl_map_upper_bound_val for use in map_bound_multi_val,
+ * setting a bound on the given output dimension.
+ */
+static __isl_give isl_map *map_upper_bound_val(__isl_take isl_map *map,
+ unsigned pos, __isl_take isl_val *v)
+{
+ return isl_map_upper_bound_val(map, isl_dim_out, pos, v);
+}
+
+/* Force the values of the set dimensions of "set"
+ * to be no greater than the corresponding values in "upper".
+ */
+__isl_give isl_set *isl_set_upper_bound_multi_val(__isl_take isl_set *set,
+ __isl_take isl_multi_val *upper)
+{
+ return set_bound_multi_val(set, upper, &map_upper_bound_val);
+}
+
+/* Force the symbolic constant expression "bound"
+ * to satisfy the relation "order" with respect to
+ * the output variable at position "pos" of "map".
+ *
+ * Create an affine expression representing the output variable
+ * in terms of the range and
+ * compare it using "order" to "bound" (defined on the domain).
+ * The result is a relation between elements in domain and range that
+ * can be intersected with "map".
+ */
+static __isl_give isl_map *map_bound_pw_aff(__isl_take isl_map *map,
+ unsigned pos, __isl_take isl_pw_aff *bound,
+ __isl_give isl_map *(*order)(__isl_take isl_pw_aff *pa1,
+ __isl_take isl_pw_aff *pa2))
+{
+ isl_space *space;
+ isl_local_space *ls;
+ isl_pw_aff *var;
+
+ space = isl_space_range(isl_map_get_space(map));
+ ls = isl_local_space_from_space(space);
+ var = isl_pw_aff_var_on_domain(ls, isl_dim_set, pos);
+ map = isl_map_intersect(map, order(bound, var));
+ return map;
+}
+
+/* Force the values of the output variable at position "pos" of "map"
+ * to be no smaller than the symbolic constant expression "lower".
+ */
+static __isl_give isl_map *map_lower_bound_pw_aff(__isl_take isl_map *map,
+ unsigned pos, __isl_take isl_pw_aff *lower)
+{
+ return map_bound_pw_aff(map, pos, lower, &isl_pw_aff_le_map);
+}
+
+/* Force the values of the output variable at position "pos" of "map"
+ * to be no greater than the symbolic constant expression "upper".
+ */
+static __isl_give isl_map *map_upper_bound_pw_aff(__isl_take isl_map *map,
+ unsigned pos, __isl_take isl_pw_aff *upper)
+{
+ return map_bound_pw_aff(map, pos, upper, &isl_pw_aff_ge_map);
+}
+
+/* Force the values of the set dimensions of "set"
+ * to be no smaller than the corresponding constant symbolic expressions
+ * in "lower".
+ */
+__isl_give isl_set *isl_set_lower_bound_multi_pw_aff(__isl_take isl_set *set,
+ __isl_take isl_multi_pw_aff *lower)
+{
+ return set_bound_multi_pw_aff(set, lower, &map_lower_bound_pw_aff);
+}
+
+/* Force the values of the set dimensions of "set"
+ * to be no greater than the corresponding constant symbolic expressions
+ * in "upper".
+ */
+__isl_give isl_set *isl_set_upper_bound_multi_pw_aff(__isl_take isl_set *set,
+ __isl_take isl_multi_pw_aff *upper)
+{
+ return set_bound_multi_pw_aff(set, upper, &map_upper_bound_pw_aff);
+}
+
+/* Force the values of the output dimensions of "map"
+ * to be no smaller than the corresponding constant symbolic expressions
+ * in "lower".
+ */
+__isl_give isl_map *isl_map_lower_bound_multi_pw_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *lower)
+{
+ return map_bound_multi_pw_aff(map, lower, &map_lower_bound_pw_aff);
+}
+
+/* Force the values of the output dimensions of "map"
+ * to be no greater than the corresponding constant symbolic expressions
+ * in "upper".
+ */
+__isl_give isl_map *isl_map_upper_bound_multi_pw_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *upper)
+{
+ return map_bound_multi_pw_aff(map, upper, &map_upper_bound_pw_aff);
+}
+
+/* Bound the given variable of "bset" from below (or above is "upper"
+ * is set) to "value".
+ */
+static __isl_give isl_basic_set *isl_basic_set_bound(
+ __isl_take isl_basic_set *bset, enum isl_dim_type type, unsigned pos,
+ isl_int value, int upper)
+{
+ return bset_from_bmap(basic_map_bound(bset_to_bmap(bset),
+ type, pos, value, upper));
+}
+
+/* Bound the given variable of "bset" from below (or above is "upper"
+ * is set) to "value".
+ */
+static __isl_give isl_basic_set *isl_basic_set_bound_val(
+ __isl_take isl_basic_set *bset, enum isl_dim_type type, unsigned pos,
+ __isl_take isl_val *value, int upper)
+{
+ if (!value)
+ goto error;
+ if (!isl_val_is_int(value))
+ isl_die(isl_basic_set_get_ctx(bset), isl_error_invalid,
+ "expecting integer value", goto error);
+ bset = isl_basic_set_bound(bset, type, pos, value->n, upper);
+ isl_val_free(value);
+ return bset;
+error:
+ isl_val_free(value);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Bound the given variable of "bset" from below to "value".
+ */
+__isl_give isl_basic_set *isl_basic_set_lower_bound_val(
+ __isl_take isl_basic_set *bset, enum isl_dim_type type, unsigned pos,
+ __isl_take isl_val *value)
+{
+ return isl_basic_set_bound_val(bset, type, pos, value, 0);
+}
+
+/* Bound the given variable of "bset" from above to "value".
+ */
+__isl_give isl_basic_set *isl_basic_set_upper_bound_val(
+ __isl_take isl_basic_set *bset, enum isl_dim_type type, unsigned pos,
+ __isl_take isl_val *value)
+{
+ return isl_basic_set_bound_val(bset, type, pos, value, 1);
+}
+
+__isl_give isl_map *isl_map_reverse(__isl_take isl_map *map)
+{
+ return isl_map_transform(map, &isl_space_reverse,
+ &isl_basic_map_reverse);
+}
+
+/* Given a map A -> (B -> C), return the corresponding map A -> (C -> B).
+ */
+__isl_give isl_map *isl_map_range_reverse(__isl_take isl_map *map)
+{
+ return isl_map_transform(map, &isl_space_range_reverse,
+ &isl_basic_map_range_reverse);
+}
+
+#undef TYPE
+#define TYPE isl_pw_multi_aff
+#undef SUFFIX
+#define SUFFIX _pw_multi_aff
+#undef EMPTY
+#define EMPTY isl_pw_multi_aff_empty
+#undef ADD
+#define ADD isl_pw_multi_aff_union_add
+#include "isl_map_lexopt_templ.c"
+
+/* Given a map "map", compute the lexicographically minimal
+ * (or maximal) image element for each domain element in dom,
+ * in the form of an isl_pw_multi_aff.
+ * If "empty" is not NULL, then set *empty to those elements in dom that
+ * do not have an image element.
+ * If "flags" includes ISL_OPT_FULL, then "dom" is NULL and the optimum
+ * should be computed over the domain of "map". "empty" is also NULL
+ * in this case.
+ *
+ * We first compute the lexicographically minimal or maximal element
+ * in the first basic map. This results in a partial solution "res"
+ * and a subset "todo" of dom that still need to be handled.
+ * We then consider each of the remaining maps in "map" and successively
+ * update both "res" and "todo".
+ * If "empty" is NULL, then the todo sets are not needed and therefore
+ * also not computed.
+ */
+static __isl_give isl_pw_multi_aff *isl_map_partial_lexopt_aligned_pw_multi_aff(
+ __isl_take isl_map *map, __isl_take isl_set *dom,
+ __isl_give isl_set **empty, unsigned flags)
+{
+ int i;
+ int full;
+ isl_pw_multi_aff *res;
+ isl_set *todo;
+
+ full = ISL_FL_ISSET(flags, ISL_OPT_FULL);
+ if (!map || (!full && !dom))
+ goto error;
+
+ if (isl_map_plain_is_empty(map)) {
+ if (empty)
+ *empty = dom;
+ else
+ isl_set_free(dom);
+ return isl_pw_multi_aff_from_map(map);
+ }
+
+ res = basic_map_partial_lexopt_pw_multi_aff(
+ isl_basic_map_copy(map->p[0]),
+ isl_set_copy(dom), empty, flags);
+
+ if (empty)
+ todo = *empty;
+ for (i = 1; i < map->n; ++i) {
+ isl_pw_multi_aff *res_i;
+
+ res_i = basic_map_partial_lexopt_pw_multi_aff(
+ isl_basic_map_copy(map->p[i]),
+ isl_set_copy(dom), empty, flags);
+
+ if (ISL_FL_ISSET(flags, ISL_OPT_MAX))
+ res = isl_pw_multi_aff_union_lexmax(res, res_i);
+ else
+ res = isl_pw_multi_aff_union_lexmin(res, res_i);
+
+ if (empty)
+ todo = isl_set_intersect(todo, *empty);
+ }
+
+ isl_set_free(dom);
+ isl_map_free(map);
+
+ if (empty)
+ *empty = todo;
+
+ return res;
+error:
+ if (empty)
+ *empty = NULL;
+ isl_set_free(dom);
+ isl_map_free(map);
+ return NULL;
+}
+
+#undef TYPE
+#define TYPE isl_map
+#undef SUFFIX
+#define SUFFIX
+#undef EMPTY
+#define EMPTY isl_map_empty
+#undef ADD
+#define ADD isl_map_union_disjoint
+#include "isl_map_lexopt_templ.c"
+
+/* Given a map "map", compute the lexicographically minimal
+ * (or maximal) image element for each domain element in "dom",
+ * in the form of an isl_map.
+ * If "empty" is not NULL, then set *empty to those elements in "dom" that
+ * do not have an image element.
+ * If "flags" includes ISL_OPT_FULL, then "dom" is NULL and the optimum
+ * should be computed over the domain of "map". "empty" is also NULL
+ * in this case.
+ *
+ * If the input consists of more than one disjunct, then first
+ * compute the desired result in the form of an isl_pw_multi_aff and
+ * then convert that into an isl_map.
+ *
+ * This function used to have an explicit implementation in terms
+ * of isl_maps, but it would continually intersect the domains of
+ * partial results with the complement of the domain of the next
+ * partial solution, potentially leading to an explosion in the number
+ * of disjuncts if there are several disjuncts in the input.
+ * An even earlier implementation of this function would look for
+ * better results in the domain of the partial result and for extra
+ * results in the complement of this domain, which would lead to
+ * even more splintering.
+ */
+static __isl_give isl_map *isl_map_partial_lexopt_aligned(
+ __isl_take isl_map *map, __isl_take isl_set *dom,
+ __isl_give isl_set **empty, unsigned flags)
+{
+ int full;
+ struct isl_map *res;
+ isl_pw_multi_aff *pma;
+
+ full = ISL_FL_ISSET(flags, ISL_OPT_FULL);
+ if (!map || (!full && !dom))
+ goto error;
+
+ if (isl_map_plain_is_empty(map)) {
+ if (empty)
+ *empty = dom;
+ else
+ isl_set_free(dom);
+ return map;
+ }
+
+ if (map->n == 1) {
+ res = basic_map_partial_lexopt(isl_basic_map_copy(map->p[0]),
+ dom, empty, flags);
+ isl_map_free(map);
+ return res;
+ }
+
+ pma = isl_map_partial_lexopt_aligned_pw_multi_aff(map, dom, empty,
+ flags);
+ return isl_map_from_pw_multi_aff_internal(pma);
+error:
+ if (empty)
+ *empty = NULL;
+ isl_set_free(dom);
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_map *isl_map_partial_lexmax(
+ __isl_take isl_map *map, __isl_take isl_set *dom,
+ __isl_give isl_set **empty)
+{
+ return isl_map_partial_lexopt(map, dom, empty, ISL_OPT_MAX);
+}
+
+__isl_give isl_map *isl_map_partial_lexmin(
+ __isl_take isl_map *map, __isl_take isl_set *dom,
+ __isl_give isl_set **empty)
+{
+ return isl_map_partial_lexopt(map, dom, empty, 0);
+}
+
+__isl_give isl_set *isl_set_partial_lexmin(
+ __isl_take isl_set *set, __isl_take isl_set *dom,
+ __isl_give isl_set **empty)
+{
+ return set_from_map(isl_map_partial_lexmin(set_to_map(set),
+ dom, empty));
+}
+
+__isl_give isl_set *isl_set_partial_lexmax(
+ __isl_take isl_set *set, __isl_take isl_set *dom,
+ __isl_give isl_set **empty)
+{
+ return set_from_map(isl_map_partial_lexmax(set_to_map(set),
+ dom, empty));
+}
+
+/* Compute the lexicographic minimum (or maximum if "flags" includes
+ * ISL_OPT_MAX) of "bset" over its parametric domain.
+ */
+__isl_give isl_set *isl_basic_set_lexopt(__isl_take isl_basic_set *bset,
+ unsigned flags)
+{
+ return isl_basic_map_lexopt(bset, flags);
+}
+
+__isl_give isl_map *isl_basic_map_lexmax(__isl_take isl_basic_map *bmap)
+{
+ return isl_basic_map_lexopt(bmap, ISL_OPT_MAX);
+}
+
+__isl_give isl_set *isl_basic_set_lexmin(__isl_take isl_basic_set *bset)
+{
+ return set_from_map(isl_basic_map_lexmin(bset_to_bmap(bset)));
+}
+
+__isl_give isl_set *isl_basic_set_lexmax(__isl_take isl_basic_set *bset)
+{
+ return set_from_map(isl_basic_map_lexmax(bset_to_bmap(bset)));
+}
+
+/* Compute the lexicographic minimum of "bset" over its parametric domain
+ * for the purpose of quantifier elimination.
+ * That is, find an explicit representation for all the existentially
+ * quantified variables in "bset" by computing their lexicographic
+ * minimum.
+ */
+static __isl_give isl_set *isl_basic_set_lexmin_compute_divs(
+ __isl_take isl_basic_set *bset)
+{
+ return isl_basic_set_lexopt(bset, ISL_OPT_QE);
+}
+
+/* Given a basic map with one output dimension, compute the minimum or
+ * maximum of that dimension as an isl_pw_aff.
+ *
+ * Compute the optimum as a lexicographic optimum over the single
+ * output dimension and extract the single isl_pw_aff from the result.
+ */
+static __isl_give isl_pw_aff *basic_map_dim_opt(__isl_keep isl_basic_map *bmap,
+ int max)
+{
+ isl_pw_multi_aff *pma;
+ isl_pw_aff *pwaff;
+
+ bmap = isl_basic_map_copy(bmap);
+ pma = isl_basic_map_lexopt_pw_multi_aff(bmap, max ? ISL_OPT_MAX : 0);
+ pwaff = isl_pw_multi_aff_get_pw_aff(pma, 0);
+ isl_pw_multi_aff_free(pma);
+
+ return pwaff;
+}
+
+/* Compute the minimum or maximum of the given output dimension
+ * as a function of the parameters and the input dimensions,
+ * but independently of the other output dimensions.
+ *
+ * We first project out the other output dimension and then compute
+ * the "lexicographic" maximum in each basic map, combining the results
+ * using isl_pw_aff_union_max.
+ */
+static __isl_give isl_pw_aff *map_dim_opt(__isl_take isl_map *map, int pos,
+ int max)
+{
+ int i;
+ isl_pw_aff *pwaff;
+ isl_size n_out;
+
+ n_out = isl_map_dim(map, isl_dim_out);
+ if (n_out < 0)
+ map = isl_map_free(map);
+ map = isl_map_project_out(map, isl_dim_out, pos + 1, n_out - (pos + 1));
+ map = isl_map_project_out(map, isl_dim_out, 0, pos);
+ if (!map)
+ return NULL;
+
+ if (map->n == 0) {
+ isl_space *space = isl_map_get_space(map);
+ isl_map_free(map);
+ return isl_pw_aff_empty(space);
+ }
+
+ pwaff = basic_map_dim_opt(map->p[0], max);
+ for (i = 1; i < map->n; ++i) {
+ isl_pw_aff *pwaff_i;
+
+ pwaff_i = basic_map_dim_opt(map->p[i], max);
+ pwaff = isl_pw_aff_union_opt(pwaff, pwaff_i, max);
+ }
+
+ isl_map_free(map);
+
+ return pwaff;
+}
+
+/* Compute the minimum of the given output dimension as a function of the
+ * parameters and input dimensions, but independently of
+ * the other output dimensions.
+ */
+__isl_give isl_pw_aff *isl_map_dim_min(__isl_take isl_map *map, int pos)
+{
+ return map_dim_opt(map, pos, 0);
+}
+
+/* Compute the maximum of the given output dimension as a function of the
+ * parameters and input dimensions, but independently of
+ * the other output dimensions.
+ */
+__isl_give isl_pw_aff *isl_map_dim_max(__isl_take isl_map *map, int pos)
+{
+ return map_dim_opt(map, pos, 1);
+}
+
+/* Compute the minimum or maximum of the given set dimension
+ * as a function of the parameters,
+ * but independently of the other set dimensions.
+ */
+static __isl_give isl_pw_aff *set_dim_opt(__isl_take isl_set *set, int pos,
+ int max)
+{
+ return map_dim_opt(set, pos, max);
+}
+
+/* Compute the maximum of the given set dimension as a function of the
+ * parameters, but independently of the other set dimensions.
+ */
+__isl_give isl_pw_aff *isl_set_dim_max(__isl_take isl_set *set, int pos)
+{
+ return set_dim_opt(set, pos, 1);
+}
+
+/* Compute the minimum of the given set dimension as a function of the
+ * parameters, but independently of the other set dimensions.
+ */
+__isl_give isl_pw_aff *isl_set_dim_min(__isl_take isl_set *set, int pos)
+{
+ return set_dim_opt(set, pos, 0);
+}
+
+/* Apply a preimage specified by "mat" on the parameters of "bset".
+ * bset is assumed to have only parameters and divs.
+ */
+static __isl_give isl_basic_set *basic_set_parameter_preimage(
+ __isl_take isl_basic_set *bset, __isl_take isl_mat *mat)
+{
+ isl_size nparam;
+
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ if (nparam < 0 || !mat)
+ goto error;
+
+ bset->dim = isl_space_cow(bset->dim);
+ if (!bset->dim)
+ goto error;
+
+ isl_assert(bset->ctx, mat->n_row == 1 + nparam, goto error);
+
+ bset->dim->nparam = 0;
+ bset->dim->n_out = nparam;
+ bset = isl_basic_set_preimage(bset, mat);
+ if (bset) {
+ bset->dim->nparam = bset->dim->n_out;
+ bset->dim->n_out = 0;
+ }
+ return bset;
+error:
+ isl_mat_free(mat);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Apply a preimage specified by "mat" on the parameters of "set".
+ * set is assumed to have only parameters and divs.
+ */
+static __isl_give isl_set *set_parameter_preimage(__isl_take isl_set *set,
+ __isl_take isl_mat *mat)
+{
+ isl_space *space;
+ isl_size nparam;
+
+ nparam = isl_set_dim(set, isl_dim_param);
+ if (nparam < 0 || !mat)
+ goto error;
+
+ if (mat->n_row != 1 + nparam)
+ isl_die(isl_set_get_ctx(set), isl_error_internal,
+ "unexpected number of rows", goto error);
+
+ space = isl_set_get_space(set);
+ space = isl_space_move_dims(space, isl_dim_set, 0,
+ isl_dim_param, 0, nparam);
+ set = isl_set_reset_space(set, space);
+ set = isl_set_preimage(set, mat);
+ nparam = isl_set_dim(set, isl_dim_out);
+ if (nparam < 0)
+ set = isl_set_free(set);
+ space = isl_set_get_space(set);
+ space = isl_space_move_dims(space, isl_dim_param, 0,
+ isl_dim_out, 0, nparam);
+ set = isl_set_reset_space(set, space);
+ return set;
+error:
+ isl_mat_free(mat);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Intersect the basic set "bset" with the affine space specified by the
+ * equalities in "eq".
+ */
+static __isl_give isl_basic_set *basic_set_append_equalities(
+ __isl_take isl_basic_set *bset, __isl_take isl_mat *eq)
+{
+ int i, k;
+ unsigned len;
+
+ if (!bset || !eq)
+ goto error;
+
+ bset = isl_basic_set_extend(bset, 0, eq->n_row, 0);
+ if (!bset)
+ goto error;
+
+ len = isl_basic_set_offset(bset, isl_dim_div) + bset->extra;
+ for (i = 0; i < eq->n_row; ++i) {
+ k = isl_basic_set_alloc_equality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bset->eq[k], eq->row[i], eq->n_col);
+ isl_seq_clr(bset->eq[k] + eq->n_col, len - eq->n_col);
+ }
+ isl_mat_free(eq);
+
+ bset = isl_basic_set_gauss(bset, NULL);
+ bset = isl_basic_set_finalize(bset);
+
+ return bset;
+error:
+ isl_mat_free(eq);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Intersect the set "set" with the affine space specified by the
+ * equalities in "eq".
+ */
+static __isl_give isl_set *set_append_equalities(__isl_take isl_set *set,
+ __isl_take isl_mat *eq)
+{
+ int i;
+
+ if (!set || !eq)
+ goto error;
+
+ for (i = 0; i < set->n; ++i) {
+ set->p[i] = basic_set_append_equalities(set->p[i],
+ isl_mat_copy(eq));
+ if (!set->p[i])
+ goto error;
+ }
+ isl_mat_free(eq);
+ return set;
+error:
+ isl_mat_free(eq);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Given a basic set "bset" that only involves parameters and existentially
+ * quantified variables, return the index of the first equality
+ * that only involves parameters. If there is no such equality then
+ * return bset->n_eq.
+ *
+ * This function assumes that isl_basic_set_gauss has been called on "bset".
+ */
+static int first_parameter_equality(__isl_keep isl_basic_set *bset)
+{
+ int i, j;
+ isl_size nparam, n_div;
+
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ n_div = isl_basic_set_dim(bset, isl_dim_div);
+ if (nparam < 0 || n_div < 0)
+ return -1;
+
+ for (i = 0, j = n_div - 1; i < bset->n_eq && j >= 0; --j) {
+ if (!isl_int_is_zero(bset->eq[i][1 + nparam + j]))
+ ++i;
+ }
+
+ return i;
+}
+
+/* Compute an explicit representation for the existentially quantified
+ * variables in "bset" by computing the "minimal value" of the set
+ * variables. Since there are no set variables, the computation of
+ * the minimal value essentially computes an explicit representation
+ * of the non-empty part(s) of "bset".
+ *
+ * The input only involves parameters and existentially quantified variables.
+ * All equalities among parameters have been removed.
+ *
+ * Since the existentially quantified variables in the result are in general
+ * going to be different from those in the input, we first replace
+ * them by the minimal number of variables based on their equalities.
+ * This should simplify the parametric integer programming.
+ */
+static __isl_give isl_set *base_compute_divs(__isl_take isl_basic_set *bset)
+{
+ isl_morph *morph1, *morph2;
+ isl_set *set;
+ isl_size n;
+
+ if (!bset)
+ return NULL;
+ if (bset->n_eq == 0)
+ return isl_basic_set_lexmin_compute_divs(bset);
+
+ morph1 = isl_basic_set_parameter_compression(bset);
+ bset = isl_morph_basic_set(isl_morph_copy(morph1), bset);
+ bset = isl_basic_set_lift(bset);
+ morph2 = isl_basic_set_variable_compression(bset, isl_dim_set);
+ bset = isl_morph_basic_set(morph2, bset);
+ n = isl_basic_set_dim(bset, isl_dim_set);
+ if (n < 0)
+ bset = isl_basic_set_free(bset);
+ bset = isl_basic_set_project_out(bset, isl_dim_set, 0, n);
+
+ set = isl_basic_set_lexmin_compute_divs(bset);
+
+ set = isl_morph_set(isl_morph_inverse(morph1), set);
+
+ return set;
+}
+
+/* Project the given basic set onto its parameter domain, possibly introducing
+ * new, explicit, existential variables in the constraints.
+ * The input has parameters and (possibly implicit) existential variables.
+ * The output has the same parameters, but only
+ * explicit existentially quantified variables.
+ *
+ * The actual projection is performed by pip, but pip doesn't seem
+ * to like equalities very much, so we first remove the equalities
+ * among the parameters by performing a variable compression on
+ * the parameters. Afterward, an inverse transformation is performed
+ * and the equalities among the parameters are inserted back in.
+ *
+ * The variable compression on the parameters may uncover additional
+ * equalities that were only implicit before. We therefore check
+ * if there are any new parameter equalities in the result and
+ * if so recurse. The removal of parameter equalities is required
+ * for the parameter compression performed by base_compute_divs.
+ */
+static __isl_give isl_set *parameter_compute_divs(
+ __isl_take isl_basic_set *bset)
+{
+ int i;
+ struct isl_mat *eq;
+ struct isl_mat *T, *T2;
+ struct isl_set *set;
+ isl_size nparam;
+
+ bset = isl_basic_set_cow(bset);
+ if (!bset)
+ return NULL;
+
+ if (bset->n_eq == 0)
+ return base_compute_divs(bset);
+
+ bset = isl_basic_set_gauss(bset, NULL);
+ if (!bset)
+ return NULL;
+ if (isl_basic_set_plain_is_empty(bset))
+ return isl_set_from_basic_set(bset);
+
+ i = first_parameter_equality(bset);
+ if (i == bset->n_eq)
+ return base_compute_divs(bset);
+
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ if (nparam < 0)
+ return isl_set_from_basic_set(isl_basic_set_free(bset));
+ eq = isl_mat_sub_alloc6(bset->ctx, bset->eq, i, bset->n_eq - i,
+ 0, 1 + nparam);
+ eq = isl_mat_cow(eq);
+ T = isl_mat_variable_compression(isl_mat_copy(eq), &T2);
+ if (T && T->n_col == 0) {
+ isl_mat_free(T);
+ isl_mat_free(T2);
+ isl_mat_free(eq);
+ bset = isl_basic_set_set_to_empty(bset);
+ return isl_set_from_basic_set(bset);
+ }
+ bset = basic_set_parameter_preimage(bset, T);
+
+ i = first_parameter_equality(bset);
+ if (!bset)
+ set = NULL;
+ else if (i == bset->n_eq)
+ set = base_compute_divs(bset);
+ else
+ set = parameter_compute_divs(bset);
+ set = set_parameter_preimage(set, T2);
+ set = set_append_equalities(set, eq);
+ return set;
+}
+
+/* Insert the divs from "ls" before those of "bmap".
+ *
+ * The number of columns is not changed, which means that the last
+ * dimensions of "bmap" are being reintepreted as the divs from "ls".
+ * The caller is responsible for removing the same number of dimensions
+ * from the space of "bmap".
+ */
+static __isl_give isl_basic_map *insert_divs_from_local_space(
+ __isl_take isl_basic_map *bmap, __isl_keep isl_local_space *ls)
+{
+ int i;
+ isl_size n_div;
+ int old_n_div;
+
+ n_div = isl_local_space_dim(ls, isl_dim_div);
+ if (n_div < 0)
+ return isl_basic_map_free(bmap);
+ if (n_div == 0)
+ return bmap;
+
+ old_n_div = bmap->n_div;
+ bmap = insert_div_rows(bmap, n_div);
+ if (!bmap)
+ return NULL;
+
+ for (i = 0; i < n_div; ++i) {
+ isl_seq_cpy(bmap->div[i], ls->div->row[i], ls->div->n_col);
+ isl_seq_clr(bmap->div[i] + ls->div->n_col, old_n_div);
+ }
+
+ return bmap;
+}
+
+/* Replace the space of "bmap" by the space and divs of "ls".
+ *
+ * If "ls" has any divs, then we simplify the result since we may
+ * have discovered some additional equalities that could simplify
+ * the div expressions.
+ */
+static __isl_give isl_basic_map *basic_replace_space_by_local_space(
+ __isl_take isl_basic_map *bmap, __isl_take isl_local_space *ls)
+{
+ isl_size n_div;
+
+ bmap = isl_basic_map_cow(bmap);
+ n_div = isl_local_space_dim(ls, isl_dim_div);
+ if (!bmap || n_div < 0)
+ goto error;
+
+ bmap = insert_divs_from_local_space(bmap, ls);
+ if (!bmap)
+ goto error;
+
+ isl_space_free(bmap->dim);
+ bmap->dim = isl_local_space_get_space(ls);
+ if (!bmap->dim)
+ goto error;
+
+ isl_local_space_free(ls);
+ if (n_div > 0)
+ bmap = isl_basic_map_simplify(bmap);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ isl_local_space_free(ls);
+ return NULL;
+}
+
+/* Replace the space of "map" by the space and divs of "ls".
+ */
+static __isl_give isl_map *replace_space_by_local_space(__isl_take isl_map *map,
+ __isl_take isl_local_space *ls)
+{
+ int i;
+
+ map = isl_map_cow(map);
+ if (!map || !ls)
+ goto error;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = basic_replace_space_by_local_space(map->p[i],
+ isl_local_space_copy(ls));
+ if (!map->p[i])
+ goto error;
+ }
+ isl_space_free(isl_map_take_space(map));
+ map = isl_map_restore_space(map, isl_local_space_get_space(ls));
+
+ isl_local_space_free(ls);
+ return map;
+error:
+ isl_local_space_free(ls);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Compute an explicit representation for the existentially
+ * quantified variables for which do not know any explicit representation yet.
+ *
+ * We first sort the existentially quantified variables so that the
+ * existentially quantified variables for which we already have an explicit
+ * representation are placed before those for which we do not.
+ * The input dimensions, the output dimensions and the existentially
+ * quantified variables for which we already have an explicit
+ * representation are then turned into parameters.
+ * compute_divs returns a map with the same parameters and
+ * no input or output dimensions and the dimension specification
+ * is reset to that of the input, including the existentially quantified
+ * variables for which we already had an explicit representation.
+ */
+static __isl_give isl_map *compute_divs(__isl_take isl_basic_map *bmap)
+{
+ struct isl_basic_set *bset;
+ struct isl_set *set;
+ struct isl_map *map;
+ isl_space *space;
+ isl_local_space *ls;
+ isl_size nparam;
+ isl_size n_in;
+ isl_size n_out;
+ int n_known;
+ int i;
+
+ bmap = isl_basic_map_sort_divs(bmap);
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+
+ n_known = isl_basic_map_first_unknown_div(bmap);
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ if (n_known < 0 || nparam < 0 || n_in < 0 || n_out < 0)
+ return isl_map_from_basic_map(isl_basic_map_free(bmap));
+
+ space = isl_space_set_alloc(bmap->ctx,
+ nparam + n_in + n_out + n_known, 0);
+ if (!space)
+ goto error;
+
+ ls = isl_basic_map_get_local_space(bmap);
+ ls = isl_local_space_drop_dims(ls, isl_dim_div,
+ n_known, bmap->n_div - n_known);
+ if (n_known > 0) {
+ for (i = n_known; i < bmap->n_div; ++i)
+ swap_div(bmap, i - n_known, i);
+ bmap->n_div -= n_known;
+ bmap->extra -= n_known;
+ }
+ bmap = isl_basic_map_reset_space(bmap, space);
+ bset = bset_from_bmap(bmap);
+
+ set = parameter_compute_divs(bset);
+ map = set_to_map(set);
+ map = replace_space_by_local_space(map, ls);
+
+ return map;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Remove the explicit representation of local variable "div",
+ * if there is any.
+ */
+__isl_give isl_basic_map *isl_basic_map_mark_div_unknown(
+ __isl_take isl_basic_map *bmap, int div)
+{
+ isl_bool unknown;
+
+ unknown = isl_basic_map_div_is_marked_unknown(bmap, div);
+ if (unknown < 0)
+ return isl_basic_map_free(bmap);
+ if (unknown)
+ return bmap;
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+ isl_int_set_si(bmap->div[div][0], 0);
+ return bmap;
+}
+
+/* Is local variable "div" of "bmap" marked as not having an explicit
+ * representation?
+ * Note that even if "div" is not marked in this way and therefore
+ * has an explicit representation, this representation may still
+ * depend (indirectly) on other local variables that do not
+ * have an explicit representation.
+ */
+isl_bool isl_basic_map_div_is_marked_unknown(__isl_keep isl_basic_map *bmap,
+ int div)
+{
+ if (isl_basic_map_check_range(bmap, isl_dim_div, div, 1) < 0)
+ return isl_bool_error;
+ return isl_int_is_zero(bmap->div[div][0]);
+}
+
+/* Return the position of the first local variable that does not
+ * have an explicit representation.
+ * Return the total number of local variables if they all have
+ * an explicit representation.
+ * Return -1 on error.
+ */
+int isl_basic_map_first_unknown_div(__isl_keep isl_basic_map *bmap)
+{
+ int i;
+
+ if (!bmap)
+ return -1;
+
+ for (i = 0; i < bmap->n_div; ++i) {
+ if (!isl_basic_map_div_is_known(bmap, i))
+ return i;
+ }
+ return bmap->n_div;
+}
+
+/* Return the position of the first local variable that does not
+ * have an explicit representation.
+ * Return the total number of local variables if they all have
+ * an explicit representation.
+ * Return -1 on error.
+ */
+int isl_basic_set_first_unknown_div(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_first_unknown_div(bset);
+}
+
+/* Does "bmap" have an explicit representation for all local variables?
+ */
+isl_bool isl_basic_map_divs_known(__isl_keep isl_basic_map *bmap)
+{
+ int first;
+ isl_size n;
+
+ n = isl_basic_map_dim(bmap, isl_dim_div);
+ first = isl_basic_map_first_unknown_div(bmap);
+ if (n < 0 || first < 0)
+ return isl_bool_error;
+ return first == n;
+}
+
+/* Do all basic maps in "map" have an explicit representation
+ * for all local variables?
+ */
+isl_bool isl_map_divs_known(__isl_keep isl_map *map)
+{
+ int i;
+
+ if (!map)
+ return isl_bool_error;
+
+ for (i = 0; i < map->n; ++i) {
+ int known = isl_basic_map_divs_known(map->p[i]);
+ if (known <= 0)
+ return known;
+ }
+
+ return isl_bool_true;
+}
+
+/* If bmap contains any unknown divs, then compute explicit
+ * expressions for them. However, this computation may be
+ * quite expensive, so first try to remove divs that aren't
+ * strictly needed.
+ */
+__isl_give isl_map *isl_basic_map_compute_divs(__isl_take isl_basic_map *bmap)
+{
+ int known;
+ struct isl_map *map;
+
+ known = isl_basic_map_divs_known(bmap);
+ if (known < 0)
+ goto error;
+ if (known)
+ return isl_map_from_basic_map(bmap);
+
+ bmap = isl_basic_map_drop_redundant_divs(bmap);
+
+ known = isl_basic_map_divs_known(bmap);
+ if (known < 0)
+ goto error;
+ if (known)
+ return isl_map_from_basic_map(bmap);
+
+ map = compute_divs(bmap);
+ return map;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_map *isl_map_compute_divs(__isl_take isl_map *map)
+{
+ int i;
+ int known;
+ struct isl_map *res;
+
+ if (!map)
+ return NULL;
+ if (map->n == 0)
+ return map;
+
+ known = isl_map_divs_known(map);
+ if (known < 0) {
+ isl_map_free(map);
+ return NULL;
+ }
+ if (known)
+ return map;
+
+ res = isl_basic_map_compute_divs(isl_basic_map_copy(map->p[0]));
+ for (i = 1 ; i < map->n; ++i) {
+ struct isl_map *r2;
+ r2 = isl_basic_map_compute_divs(isl_basic_map_copy(map->p[i]));
+ if (ISL_F_ISSET(map, ISL_MAP_DISJOINT))
+ res = isl_map_union_disjoint(res, r2);
+ else
+ res = isl_map_union(res, r2);
+ }
+ isl_map_free(map);
+
+ return res;
+}
+
+__isl_give isl_set *isl_basic_set_compute_divs(__isl_take isl_basic_set *bset)
+{
+ return set_from_map(isl_basic_map_compute_divs(bset_to_bmap(bset)));
+}
+
+__isl_give isl_set *isl_set_compute_divs(__isl_take isl_set *set)
+{
+ return set_from_map(isl_map_compute_divs(set_to_map(set)));
+}
+
+__isl_give isl_set *isl_map_domain(__isl_take isl_map *map)
+{
+ isl_space *space;
+ isl_size n_out;
+
+ n_out = isl_map_dim(map, isl_dim_out);
+ if (n_out < 0)
+ return set_from_map(isl_map_free(map));
+ space = isl_space_domain(isl_map_get_space(map));
+
+ map = isl_map_project_out(map, isl_dim_out, 0, n_out);
+
+ return set_from_map(isl_map_reset_space(map, space));
+}
+
+/* Return the union of "map1" and "map2", where we assume for now that
+ * "map1" and "map2" are disjoint. Note that the basic maps inside
+ * "map1" or "map2" may not be disjoint from each other.
+ * Also note that this function is also called from isl_map_union,
+ * which takes care of handling the situation where "map1" and "map2"
+ * may not be disjoint.
+ *
+ * If one of the inputs is empty, we can simply return the other input.
+ * Similarly, if one of the inputs is universal, then it is equal to the union.
+ */
+static __isl_give isl_map *map_union_disjoint(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ int i;
+ unsigned flags = 0;
+ struct isl_map *map = NULL;
+ int is_universe;
+
+ if (isl_map_check_equal_space(map1, map2) < 0)
+ goto error;
+
+ if (map1->n == 0) {
+ isl_map_free(map1);
+ return map2;
+ }
+ if (map2->n == 0) {
+ isl_map_free(map2);
+ return map1;
+ }
+
+ is_universe = isl_map_plain_is_universe(map1);
+ if (is_universe < 0)
+ goto error;
+ if (is_universe) {
+ isl_map_free(map2);
+ return map1;
+ }
+
+ is_universe = isl_map_plain_is_universe(map2);
+ if (is_universe < 0)
+ goto error;
+ if (is_universe) {
+ isl_map_free(map1);
+ return map2;
+ }
+
+ if (ISL_F_ISSET(map1, ISL_MAP_DISJOINT) &&
+ ISL_F_ISSET(map2, ISL_MAP_DISJOINT))
+ ISL_FL_SET(flags, ISL_MAP_DISJOINT);
+
+ map = isl_map_alloc_space(isl_space_copy(map1->dim),
+ map1->n + map2->n, flags);
+ if (!map)
+ goto error;
+ for (i = 0; i < map1->n; ++i) {
+ map = isl_map_add_basic_map(map,
+ isl_basic_map_copy(map1->p[i]));
+ if (!map)
+ goto error;
+ }
+ for (i = 0; i < map2->n; ++i) {
+ map = isl_map_add_basic_map(map,
+ isl_basic_map_copy(map2->p[i]));
+ if (!map)
+ goto error;
+ }
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return map;
+error:
+ isl_map_free(map);
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return NULL;
+}
+
+/* Return the union of "map1" and "map2", where "map1" and "map2" are
+ * guaranteed to be disjoint by the caller.
+ *
+ * Note that this functions is called from within isl_map_make_disjoint,
+ * so we have to be careful not to touch the constraints of the inputs
+ * in any way.
+ */
+__isl_give isl_map *isl_map_union_disjoint(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map_align_params_bin(&map1, &map2);
+ return map_union_disjoint(map1, map2);
+}
+
+/* Return the union of "map1" and "map2", where "map1" and "map2" may
+ * not be disjoint.
+ *
+ * We currently simply call map_union_disjoint, the internal operation
+ * of which does not really depend on the inputs being disjoint.
+ * If the result contains more than one basic map, then we clear
+ * the disjoint flag since the result may contain basic maps from
+ * both inputs and these are not guaranteed to be disjoint.
+ *
+ * As a special case, if "map1" and "map2" are obviously equal,
+ * then we simply return "map1".
+ */
+__isl_give isl_map *isl_map_union(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ int equal;
+
+ if (isl_map_align_params_bin(&map1, &map2) < 0)
+ goto error;
+
+ equal = isl_map_plain_is_equal(map1, map2);
+ if (equal < 0)
+ goto error;
+ if (equal) {
+ isl_map_free(map2);
+ return map1;
+ }
+
+ map1 = map_union_disjoint(map1, map2);
+ if (!map1)
+ return NULL;
+ if (map1->n > 1)
+ ISL_F_CLR(map1, ISL_MAP_DISJOINT);
+ return map1;
+error:
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_union_disjoint(
+ __isl_take isl_set *set1, __isl_take isl_set *set2)
+{
+ return set_from_map(isl_map_union_disjoint(set_to_map(set1),
+ set_to_map(set2)));
+}
+
+__isl_give isl_set *isl_set_union(__isl_take isl_set *set1,
+ __isl_take isl_set *set2)
+{
+ return set_from_map(isl_map_union(set_to_map(set1), set_to_map(set2)));
+}
+
+/* Apply "fn" to pairs of elements from "map" and "set" and collect
+ * the results in a map living in "space".
+ *
+ * "map" and "set" are assumed to be compatible and non-NULL.
+ */
+static __isl_give isl_map *map_intersect_set(__isl_take isl_map *map,
+ __isl_take isl_space *space, __isl_take isl_set *set,
+ __isl_give isl_basic_map *fn(__isl_take isl_basic_map *bmap,
+ __isl_take isl_basic_set *bset))
+{
+ unsigned flags = 0;
+ struct isl_map *result;
+ int i, j;
+
+ if (isl_set_plain_is_universe(set)) {
+ isl_set_free(set);
+ return isl_map_reset_equal_dim_space(map, space);
+ }
+
+ if (ISL_F_ISSET(map, ISL_MAP_DISJOINT) &&
+ ISL_F_ISSET(set, ISL_MAP_DISJOINT))
+ ISL_FL_SET(flags, ISL_MAP_DISJOINT);
+
+ result = isl_map_alloc_space(space, map->n * set->n, flags);
+ for (i = 0; result && i < map->n; ++i)
+ for (j = 0; j < set->n; ++j) {
+ result = isl_map_add_basic_map(result,
+ fn(isl_basic_map_copy(map->p[i]),
+ isl_basic_set_copy(set->p[j])));
+ if (!result)
+ break;
+ }
+
+ isl_map_free(map);
+ isl_set_free(set);
+ return result;
+}
+
+__isl_give isl_map *isl_map_intersect_range(__isl_take isl_map *map,
+ __isl_take isl_set *set)
+{
+ isl_bool ok;
+ isl_space *space;
+
+ isl_map_align_params_set(&map, &set);
+ ok = isl_map_compatible_range(map, set);
+ if (ok < 0)
+ goto error;
+ if (!ok)
+ isl_die(set->ctx, isl_error_invalid,
+ "incompatible spaces", goto error);
+
+ space = isl_map_get_space(map);
+ return map_intersect_set(map, space, set,
+ &isl_basic_map_intersect_range);
+error:
+ isl_map_free(map);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Intersect the domain of "map" with "set".
+ *
+ * If the domain dimensions of "map" do not have any identifiers,
+ * then copy them over from "set".
+ */
+__isl_give isl_map *isl_map_intersect_domain(__isl_take isl_map *map,
+ __isl_take isl_set *set)
+{
+ isl_bool ok;
+ isl_space *space;
+
+ isl_map_align_params_set(&map, &set);
+ ok = isl_map_compatible_domain(map, set);
+ if (ok < 0)
+ goto error;
+ if (!ok)
+ isl_die(set->ctx, isl_error_invalid,
+ "incompatible spaces", goto error);
+
+ space = isl_map_get_space(map);
+ space = isl_space_copy_ids_if_unset(space, isl_dim_in,
+ isl_set_peek_space(set), isl_dim_set);
+ return map_intersect_set(map, space, set,
+ &isl_basic_map_intersect_domain);
+error:
+ isl_map_free(map);
+ isl_set_free(set);
+ return NULL;
+}
+
+#undef TYPE
+#define TYPE isl_map
+static
+#include "isl_copy_tuple_id_templ.c"
+
+/* Data structure that specifies how isl_map_intersect_factor
+ * should operate.
+ *
+ * "preserve_type" is the tuple where the factor differs from
+ * the input map and of which the identifiers needs
+ * to be preserved explicitly.
+ * "other_factor" is used to extract the space of the other factor
+ * from the space of the product ("map").
+ * "product" is used to combine the given factor and a universe map
+ * in the space returned by "other_factor" to produce a map
+ * that lives in the same space as the input map.
+ */
+struct isl_intersect_factor_control {
+ enum isl_dim_type preserve_type;
+ __isl_give isl_space *(*other_factor)(__isl_take isl_space *space);
+ __isl_give isl_map *(*product)(__isl_take isl_map *factor,
+ __isl_take isl_map *other);
+};
+
+/* Given a map "map" in some product space and a map "factor"
+ * living in some factor space, return the intersection.
+ *
+ * After aligning the parameters,
+ * the map "factor" is first extended to a map living in the same space
+ * as "map" and then a regular intersection is computed.
+ *
+ * Note that the extension is computed as a product, which is anonymous
+ * by default. If "map" has an identifier on the corresponding tuple,
+ * then this identifier needs to be set on the product
+ * before the intersection is computed.
+ */
+static __isl_give isl_map *isl_map_intersect_factor(
+ __isl_take isl_map *map, __isl_take isl_map *factor,
+ struct isl_intersect_factor_control *control)
+{
+ isl_bool equal;
+ isl_space *space;
+ isl_map *other, *product;
+
+ equal = isl_map_has_equal_params(map, factor);
+ if (equal < 0)
+ goto error;
+ if (!equal) {
+ map = isl_map_align_params(map, isl_map_get_space(factor));
+ factor = isl_map_align_params(factor, isl_map_get_space(map));
+ }
+
+ space = isl_map_get_space(map);
+ other = isl_map_universe(control->other_factor(space));
+ product = control->product(factor, other);
+
+ space = isl_map_peek_space(map);
+ product = isl_map_copy_tuple_id(product, control->preserve_type,
+ space, control->preserve_type);
+ return map_intersect(map, product);
+error:
+ isl_map_free(map);
+ isl_map_free(factor);
+ return NULL;
+}
+
+/* Return the domain product of "map2" and "map1".
+ */
+static __isl_give isl_map *isl_map_reverse_domain_product(
+ __isl_take isl_map *map1, __isl_take isl_map *map2)
+{
+ return isl_map_domain_product(map2, map1);
+}
+
+/* Return the range product of "map2" and "map1".
+ */
+static __isl_give isl_map *isl_map_reverse_range_product(
+ __isl_take isl_map *map1, __isl_take isl_map *map2)
+{
+ return isl_map_range_product(map2, map1);
+}
+
+/* Given a map "map" in a space [A -> B] -> C and a map "factor"
+ * in the space A -> C, return the intersection.
+ */
+__isl_give isl_map *isl_map_intersect_domain_factor_domain(
+ __isl_take isl_map *map, __isl_take isl_map *factor)
+{
+ struct isl_intersect_factor_control control = {
+ .preserve_type = isl_dim_in,
+ .other_factor = isl_space_domain_factor_range,
+ .product = isl_map_domain_product,
+ };
+
+ return isl_map_intersect_factor(map, factor, &control);
+}
+
+/* Given a map "map" in a space [A -> B] -> C and a map "factor"
+ * in the space B -> C, return the intersection.
+ */
+__isl_give isl_map *isl_map_intersect_domain_factor_range(
+ __isl_take isl_map *map, __isl_take isl_map *factor)
+{
+ struct isl_intersect_factor_control control = {
+ .preserve_type = isl_dim_in,
+ .other_factor = isl_space_domain_factor_domain,
+ .product = isl_map_reverse_domain_product,
+ };
+
+ return isl_map_intersect_factor(map, factor, &control);
+}
+
+/* Given a map "map" in a space A -> [B -> C] and a map "factor"
+ * in the space A -> B, return the intersection.
+ */
+__isl_give isl_map *isl_map_intersect_range_factor_domain(
+ __isl_take isl_map *map, __isl_take isl_map *factor)
+{
+ struct isl_intersect_factor_control control = {
+ .preserve_type = isl_dim_out,
+ .other_factor = isl_space_range_factor_range,
+ .product = isl_map_range_product,
+ };
+
+ return isl_map_intersect_factor(map, factor, &control);
+}
+
+/* Given a map "map" in a space A -> [B -> C] and a map "factor"
+ * in the space A -> C, return the intersection.
+ */
+__isl_give isl_map *isl_map_intersect_range_factor_range(
+ __isl_take isl_map *map, __isl_take isl_map *factor)
+{
+ struct isl_intersect_factor_control control = {
+ .preserve_type = isl_dim_out,
+ .other_factor = isl_space_range_factor_domain,
+ .product = isl_map_reverse_range_product,
+ };
+
+ return isl_map_intersect_factor(map, factor, &control);
+}
+
+/* Given a set "set" in a space [A -> B] and a set "domain"
+ * in the space A, return the intersection.
+ *
+ * The set "domain" is first extended to a set living in the space
+ * [A -> B] and then a regular intersection is computed.
+ */
+__isl_give isl_set *isl_set_intersect_factor_domain(__isl_take isl_set *set,
+ __isl_take isl_set *domain)
+{
+ struct isl_intersect_factor_control control = {
+ .preserve_type = isl_dim_set,
+ .other_factor = isl_space_factor_range,
+ .product = isl_map_range_product,
+ };
+
+ return set_from_map(isl_map_intersect_factor(set_to_map(set),
+ set_to_map(domain), &control));
+}
+
+/* Given a set "set" in a space [A -> B] and a set "range"
+ * in the space B, return the intersection.
+ *
+ * The set "range" is first extended to a set living in the space
+ * [A -> B] and then a regular intersection is computed.
+ */
+__isl_give isl_set *isl_set_intersect_factor_range(__isl_take isl_set *set,
+ __isl_take isl_set *range)
+{
+ struct isl_intersect_factor_control control = {
+ .preserve_type = isl_dim_set,
+ .other_factor = isl_space_factor_domain,
+ .product = isl_map_reverse_range_product,
+ };
+
+ return set_from_map(isl_map_intersect_factor(set_to_map(set),
+ set_to_map(range), &control));
+}
+
+__isl_give isl_map *isl_map_apply_domain(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ if (isl_map_align_params_bin(&map1, &map2) < 0)
+ goto error;
+ map1 = isl_map_reverse(map1);
+ map1 = isl_map_apply_range(map1, map2);
+ return isl_map_reverse(map1);
+error:
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return NULL;
+}
+
+__isl_give isl_map *isl_map_apply_range(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_space *space;
+ struct isl_map *result;
+ int i, j;
+
+ if (isl_map_align_params_bin(&map1, &map2) < 0)
+ goto error;
+
+ space = isl_space_join(isl_space_copy(map1->dim),
+ isl_space_copy(map2->dim));
+
+ result = isl_map_alloc_space(space, map1->n * map2->n, 0);
+ if (!result)
+ goto error;
+ for (i = 0; i < map1->n; ++i)
+ for (j = 0; j < map2->n; ++j) {
+ result = isl_map_add_basic_map(result,
+ isl_basic_map_apply_range(
+ isl_basic_map_copy(map1->p[i]),
+ isl_basic_map_copy(map2->p[j])));
+ if (!result)
+ goto error;
+ }
+ isl_map_free(map1);
+ isl_map_free(map2);
+ if (result && result->n <= 1)
+ ISL_F_SET(result, ISL_MAP_DISJOINT);
+ return result;
+error:
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return NULL;
+}
+
+/* Is "bmap" a transformation, i.e.,
+ * does it relate elements from the same space.
+ */
+isl_bool isl_basic_map_is_transformation(__isl_keep isl_basic_map *bmap)
+{
+ isl_space *space;
+
+ space = isl_basic_map_peek_space(bmap);
+ return isl_space_tuple_is_equal(space, isl_dim_in, space, isl_dim_out);
+}
+
+/* Check that "bmap" is a transformation, i.e.,
+ * that it relates elements from the same space.
+ */
+static isl_stat isl_basic_map_check_transformation(
+ __isl_keep isl_basic_map *bmap)
+{
+ isl_bool equal;
+
+ equal = isl_basic_map_is_transformation(bmap);
+ if (equal < 0)
+ return isl_stat_error;
+ if (!equal)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "domain and range don't match", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/*
+ * returns range - domain
+ */
+__isl_give isl_basic_set *isl_basic_map_deltas(__isl_take isl_basic_map *bmap)
+{
+ isl_space *target_space;
+ struct isl_basic_set *bset;
+ isl_size dim;
+ isl_size nparam;
+ isl_size total;
+ int i;
+
+ if (isl_basic_map_check_transformation(bmap) < 0)
+ return isl_basic_map_free(bmap);
+ dim = isl_basic_map_dim(bmap, isl_dim_in);
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ if (dim < 0 || nparam < 0)
+ goto error;
+ target_space = isl_space_domain(isl_basic_map_get_space(bmap));
+ bmap = isl_basic_map_from_range(isl_basic_map_wrap(bmap));
+ bmap = isl_basic_map_add_dims(bmap, isl_dim_in, dim);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ bmap = isl_basic_map_free(bmap);
+ bmap = isl_basic_map_extend_constraints(bmap, dim, 0);
+ for (i = 0; i < dim; ++i) {
+ int j = isl_basic_map_alloc_equality(bmap);
+ if (j < 0) {
+ bmap = isl_basic_map_free(bmap);
+ break;
+ }
+ isl_seq_clr(bmap->eq[j], 1 + total);
+ isl_int_set_si(bmap->eq[j][1+nparam+i], 1);
+ isl_int_set_si(bmap->eq[j][1+nparam+dim+i], 1);
+ isl_int_set_si(bmap->eq[j][1+nparam+2*dim+i], -1);
+ }
+ bset = isl_basic_map_domain(bmap);
+ bset = isl_basic_set_reset_space(bset, target_space);
+ return bset;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Is the tuple of type "type1" of "map" the same as
+ * the tuple of type "type2" of "space"?
+ */
+isl_bool isl_map_space_tuple_is_equal(__isl_keep isl_map *map,
+ enum isl_dim_type type1, __isl_keep isl_space *space,
+ enum isl_dim_type type2)
+{
+ isl_space *map_space;
+
+ map_space = isl_map_peek_space(map);
+ return isl_space_tuple_is_equal(map_space, type1, space, type2);
+}
+
+/* Is the tuple of type "type1" of "map1" the same as
+ * the tuple of type "type2" of "map2"?
+ */
+isl_bool isl_map_tuple_is_equal(__isl_keep isl_map *map1,
+ enum isl_dim_type type1, __isl_keep isl_map *map2,
+ enum isl_dim_type type2)
+{
+ isl_space *space1, *space2;
+
+ space1 = isl_map_peek_space(map1);
+ space2 = isl_map_peek_space(map2);
+ return isl_space_tuple_is_equal(space1, type1, space2, type2);
+}
+
+/* Is the space of "obj" equal to "space", ignoring parameters?
+ */
+isl_bool isl_map_has_space_tuples(__isl_keep isl_map *map,
+ __isl_keep isl_space *space)
+{
+ isl_space *map_space;
+
+ map_space = isl_map_peek_space(map);
+ return isl_space_has_equal_tuples(map_space, space);
+}
+
+/* Check that "map" is a transformation, i.e.,
+ * that it relates elements from the same space.
+ */
+isl_stat isl_map_check_transformation(__isl_keep isl_map *map)
+{
+ isl_bool equal;
+
+ equal = isl_map_tuple_is_equal(map, isl_dim_in, map, isl_dim_out);
+ if (equal < 0)
+ return isl_stat_error;
+ if (!equal)
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "domain and range don't match", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/*
+ * returns range - domain
+ */
+__isl_give isl_set *isl_map_deltas(__isl_take isl_map *map)
+{
+ int i;
+ isl_space *space;
+ struct isl_set *result;
+
+ if (isl_map_check_transformation(map) < 0)
+ goto error;
+ space = isl_map_get_space(map);
+ space = isl_space_domain(space);
+ result = isl_set_alloc_space(space, map->n, 0);
+ if (!result)
+ goto error;
+ for (i = 0; i < map->n; ++i)
+ result = isl_set_add_basic_set(result,
+ isl_basic_map_deltas(isl_basic_map_copy(map->p[i])));
+ isl_map_free(map);
+ return result;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/*
+ * returns [domain -> range] -> range - domain
+ */
+__isl_give isl_basic_map *isl_basic_map_deltas_map(
+ __isl_take isl_basic_map *bmap)
+{
+ int i, k;
+ isl_space *space;
+ isl_basic_map *domain;
+ isl_size nparam, n;
+ isl_size total;
+
+ if (isl_basic_map_check_transformation(bmap) < 0)
+ return isl_basic_map_free(bmap);
+
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n = isl_basic_map_dim(bmap, isl_dim_in);
+ if (nparam < 0 || n < 0)
+ return isl_basic_map_free(bmap);
+
+ space = isl_basic_map_get_space(bmap);
+ space = isl_space_from_range(isl_space_domain(space));
+ domain = isl_basic_map_universe(space);
+
+ bmap = isl_basic_map_from_domain(isl_basic_map_wrap(bmap));
+ bmap = isl_basic_map_apply_range(bmap, domain);
+ bmap = isl_basic_map_extend_constraints(bmap, n, 0);
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+
+ for (i = 0; i < n; ++i) {
+ k = isl_basic_map_alloc_equality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bmap->eq[k], 1 + total);
+ isl_int_set_si(bmap->eq[k][1 + nparam + i], 1);
+ isl_int_set_si(bmap->eq[k][1 + nparam + n + i], -1);
+ isl_int_set_si(bmap->eq[k][1 + nparam + n + n + i], 1);
+ }
+
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/*
+ * returns [domain -> range] -> range - domain
+ */
+__isl_give isl_map *isl_map_deltas_map(__isl_take isl_map *map)
+{
+ if (isl_map_check_transformation(map) < 0)
+ return isl_map_free(map);
+
+ return isl_map_transform(map, &isl_space_range_map,
+ &isl_basic_map_deltas_map);
+}
+
+/* Return pairs of elements { x -> y } such that y - x is in "deltas".
+ */
+__isl_give isl_map *isl_set_translation(__isl_take isl_set *deltas)
+{
+ isl_space *space;
+ isl_map *map;
+
+ space = isl_space_map_from_set(isl_set_get_space(deltas));
+ map = isl_map_deltas_map(isl_map_universe(space));
+ map = isl_map_intersect_range(map, deltas);
+
+ return isl_set_unwrap(isl_map_domain(map));
+}
+
+__isl_give isl_basic_map *isl_basic_map_identity(__isl_take isl_space *space)
+{
+ isl_size n_in, n_out;
+
+ n_in = isl_space_dim(space, isl_dim_in);
+ n_out = isl_space_dim(space, isl_dim_out);
+ if (n_in < 0 || n_out < 0)
+ goto error;
+ if (n_in != n_out)
+ isl_die(space->ctx, isl_error_invalid,
+ "number of input and output dimensions needs to be "
+ "the same", goto error);
+ return isl_basic_map_equal(space, n_in);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_map *isl_map_identity(__isl_take isl_space *space)
+{
+ return isl_map_from_basic_map(isl_basic_map_identity(space));
+}
+
+__isl_give isl_map *isl_set_identity(__isl_take isl_set *set)
+{
+ isl_space *space = isl_set_get_space(set);
+ isl_map *id;
+ id = isl_map_identity(isl_space_map_from_set(space));
+ return isl_map_intersect_range(id, set);
+}
+
+/* Construct a basic set with all set dimensions having only non-negative
+ * values.
+ */
+__isl_give isl_basic_set *isl_basic_set_positive_orthant(
+ __isl_take isl_space *space)
+{
+ int i;
+ isl_size nparam;
+ isl_size dim;
+ isl_size total;
+ struct isl_basic_set *bset;
+
+ nparam = isl_space_dim(space, isl_dim_param);
+ dim = isl_space_dim(space, isl_dim_set);
+ total = isl_space_dim(space, isl_dim_all);
+ if (nparam < 0 || dim < 0 || total < 0)
+ space = isl_space_free(space);
+ bset = isl_basic_set_alloc_space(space, 0, 0, dim);
+ if (!bset)
+ return NULL;
+ for (i = 0; i < dim; ++i) {
+ int k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bset->ineq[k], 1 + total);
+ isl_int_set_si(bset->ineq[k][1 + nparam + i], 1);
+ }
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Construct the half-space x_pos >= 0.
+ */
+static __isl_give isl_basic_set *nonneg_halfspace(__isl_take isl_space *space,
+ int pos)
+{
+ int k;
+ isl_size total;
+ isl_basic_set *nonneg;
+
+ total = isl_space_dim(space, isl_dim_all);
+ if (total < 0)
+ space = isl_space_free(space);
+ nonneg = isl_basic_set_alloc_space(space, 0, 0, 1);
+ k = isl_basic_set_alloc_inequality(nonneg);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(nonneg->ineq[k], 1 + total);
+ isl_int_set_si(nonneg->ineq[k][pos], 1);
+
+ return isl_basic_set_finalize(nonneg);
+error:
+ isl_basic_set_free(nonneg);
+ return NULL;
+}
+
+/* Construct the half-space x_pos <= -1.
+ */
+static __isl_give isl_basic_set *neg_halfspace(__isl_take isl_space *space,
+ int pos)
+{
+ int k;
+ isl_size total;
+ isl_basic_set *neg;
+
+ total = isl_space_dim(space, isl_dim_all);
+ if (total < 0)
+ space = isl_space_free(space);
+ neg = isl_basic_set_alloc_space(space, 0, 0, 1);
+ k = isl_basic_set_alloc_inequality(neg);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(neg->ineq[k], 1 + total);
+ isl_int_set_si(neg->ineq[k][0], -1);
+ isl_int_set_si(neg->ineq[k][pos], -1);
+
+ return isl_basic_set_finalize(neg);
+error:
+ isl_basic_set_free(neg);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_split_dims(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+ unsigned offset;
+ isl_basic_set *nonneg;
+ isl_basic_set *neg;
+
+ if (n == 0)
+ return set;
+
+ if (isl_set_check_range(set, type, first, n) < 0)
+ return isl_set_free(set);
+
+ offset = pos(set->dim, type);
+ for (i = 0; i < n; ++i) {
+ nonneg = nonneg_halfspace(isl_set_get_space(set),
+ offset + first + i);
+ neg = neg_halfspace(isl_set_get_space(set), offset + first + i);
+
+ set = isl_set_intersect(set, isl_basic_set_union(nonneg, neg));
+ }
+
+ return set;
+}
+
+static isl_stat foreach_orthant(__isl_take isl_set *set, int *signs, int first,
+ int len,
+ isl_stat (*fn)(__isl_take isl_set *orthant, int *signs, void *user),
+ void *user)
+{
+ isl_set *half;
+
+ if (!set)
+ return isl_stat_error;
+ if (isl_set_plain_is_empty(set)) {
+ isl_set_free(set);
+ return isl_stat_ok;
+ }
+ if (first == len)
+ return fn(set, signs, user);
+
+ signs[first] = 1;
+ half = isl_set_from_basic_set(nonneg_halfspace(isl_set_get_space(set),
+ 1 + first));
+ half = isl_set_intersect(half, isl_set_copy(set));
+ if (foreach_orthant(half, signs, first + 1, len, fn, user) < 0)
+ goto error;
+
+ signs[first] = -1;
+ half = isl_set_from_basic_set(neg_halfspace(isl_set_get_space(set),
+ 1 + first));
+ half = isl_set_intersect(half, set);
+ return foreach_orthant(half, signs, first + 1, len, fn, user);
+error:
+ isl_set_free(set);
+ return isl_stat_error;
+}
+
+/* Call "fn" on the intersections of "set" with each of the orthants
+ * (except for obviously empty intersections). The orthant is identified
+ * by the signs array, with each entry having value 1 or -1 according
+ * to the sign of the corresponding variable.
+ */
+isl_stat isl_set_foreach_orthant(__isl_keep isl_set *set,
+ isl_stat (*fn)(__isl_take isl_set *orthant, int *signs, void *user),
+ void *user)
+{
+ isl_size nparam;
+ isl_size nvar;
+ int *signs;
+ isl_stat r;
+
+ if (!set)
+ return isl_stat_error;
+ if (isl_set_plain_is_empty(set))
+ return isl_stat_ok;
+
+ nparam = isl_set_dim(set, isl_dim_param);
+ nvar = isl_set_dim(set, isl_dim_set);
+ if (nparam < 0 || nvar < 0)
+ return isl_stat_error;
+
+ signs = isl_alloc_array(set->ctx, int, nparam + nvar);
+
+ r = foreach_orthant(isl_set_copy(set), signs, 0, nparam + nvar,
+ fn, user);
+
+ free(signs);
+
+ return r;
+}
+
+isl_bool isl_set_is_equal(__isl_keep isl_set *set1, __isl_keep isl_set *set2)
+{
+ return isl_map_is_equal(set_to_map(set1), set_to_map(set2));
+}
+
+isl_bool isl_basic_map_is_subset(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2)
+{
+ isl_bool is_subset;
+ struct isl_map *map1;
+ struct isl_map *map2;
+
+ if (!bmap1 || !bmap2)
+ return isl_bool_error;
+
+ map1 = isl_map_from_basic_map(isl_basic_map_copy(bmap1));
+ map2 = isl_map_from_basic_map(isl_basic_map_copy(bmap2));
+
+ is_subset = isl_map_is_subset(map1, map2);
+
+ isl_map_free(map1);
+ isl_map_free(map2);
+
+ return is_subset;
+}
+
+isl_bool isl_basic_set_is_subset(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2)
+{
+ return isl_basic_map_is_subset(bset1, bset2);
+}
+
+isl_bool isl_basic_map_is_equal(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2)
+{
+ isl_bool is_subset;
+
+ if (!bmap1 || !bmap2)
+ return isl_bool_error;
+ is_subset = isl_basic_map_is_subset(bmap1, bmap2);
+ if (is_subset != isl_bool_true)
+ return is_subset;
+ is_subset = isl_basic_map_is_subset(bmap2, bmap1);
+ return is_subset;
+}
+
+isl_bool isl_basic_set_is_equal(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2)
+{
+ return isl_basic_map_is_equal(
+ bset_to_bmap(bset1), bset_to_bmap(bset2));
+}
+
+isl_bool isl_map_is_empty(__isl_keep isl_map *map)
+{
+ int i;
+ int is_empty;
+
+ if (!map)
+ return isl_bool_error;
+ for (i = 0; i < map->n; ++i) {
+ is_empty = isl_basic_map_is_empty(map->p[i]);
+ if (is_empty < 0)
+ return isl_bool_error;
+ if (!is_empty)
+ return isl_bool_false;
+ }
+ return isl_bool_true;
+}
+
+isl_bool isl_map_plain_is_empty(__isl_keep isl_map *map)
+{
+ return map ? map->n == 0 : isl_bool_error;
+}
+
+isl_bool isl_set_plain_is_empty(__isl_keep isl_set *set)
+{
+ return set ? set->n == 0 : isl_bool_error;
+}
+
+isl_bool isl_set_is_empty(__isl_keep isl_set *set)
+{
+ return isl_map_is_empty(set_to_map(set));
+}
+
+#undef TYPE
+#define TYPE isl_basic_map
+
+static
+#include "isl_type_has_equal_space_bin_templ.c"
+#include "isl_type_check_equal_space_templ.c"
+
+/* Check that "bset1" and "bset2" live in the same space,
+ * reporting an error if they do not.
+ */
+isl_stat isl_basic_set_check_equal_space(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2)
+{
+ return isl_basic_map_check_equal_space(bset_to_bmap(bset1),
+ bset_to_bmap(bset1));
+}
+
+#undef TYPE
+#define TYPE isl_map
+
+#include "isl_type_has_equal_space_bin_templ.c"
+#include "isl_type_check_equal_space_templ.c"
+#include "isl_type_has_space_templ.c"
+
+isl_bool isl_set_has_equal_space(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2)
+{
+ return isl_map_has_equal_space(set_to_map(set1), set_to_map(set2));
+}
+
+#undef TYPE1
+#define TYPE1 isl_map
+#undef TYPE2
+#define TYPE2 isl_basic_map
+#undef TYPE_PAIR
+#define TYPE_PAIR isl_map_basic_map
+
+static
+#include "isl_type_has_equal_space_templ.c"
+#include "isl_type_check_equal_space_templ.c"
+
+/* Check that "set" and "bset" live in the same space,
+ * reporting an error if they do not.
+ */
+isl_stat isl_set_basic_set_check_equal_space(__isl_keep isl_set *set,
+ __isl_keep isl_basic_set *bset)
+{
+ return isl_map_basic_map_check_equal_space(set_to_map(set),
+ bset_to_bmap(bset));
+}
+
+static isl_bool map_is_equal(__isl_keep isl_map *map1, __isl_keep isl_map *map2)
+{
+ isl_bool is_subset;
+
+ if (!map1 || !map2)
+ return isl_bool_error;
+ is_subset = isl_map_is_subset(map1, map2);
+ if (is_subset != isl_bool_true)
+ return is_subset;
+ is_subset = isl_map_is_subset(map2, map1);
+ return is_subset;
+}
+
+/* Is "map1" equal to "map2"?
+ *
+ * First check if they are obviously equal.
+ * If not, then perform a more detailed analysis.
+ */
+isl_bool isl_map_is_equal(__isl_keep isl_map *map1, __isl_keep isl_map *map2)
+{
+ isl_bool equal;
+
+ equal = isl_map_plain_is_equal(map1, map2);
+ if (equal < 0 || equal)
+ return equal;
+ return isl_map_align_params_map_map_and_test(map1, map2, &map_is_equal);
+}
+
+isl_bool isl_basic_map_is_strict_subset(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2)
+{
+ isl_bool is_subset;
+
+ if (!bmap1 || !bmap2)
+ return isl_bool_error;
+ is_subset = isl_basic_map_is_subset(bmap1, bmap2);
+ if (is_subset != isl_bool_true)
+ return is_subset;
+ is_subset = isl_basic_map_is_subset(bmap2, bmap1);
+ return isl_bool_not(is_subset);
+}
+
+isl_bool isl_map_is_strict_subset(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2)
+{
+ isl_bool is_subset;
+
+ if (!map1 || !map2)
+ return isl_bool_error;
+ is_subset = isl_map_is_subset(map1, map2);
+ if (is_subset != isl_bool_true)
+ return is_subset;
+ is_subset = isl_map_is_subset(map2, map1);
+ return isl_bool_not(is_subset);
+}
+
+isl_bool isl_set_is_strict_subset(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2)
+{
+ return isl_map_is_strict_subset(set_to_map(set1), set_to_map(set2));
+}
+
+/* Is "bmap" obviously equal to the universe with the same space?
+ *
+ * That is, does it not have any constraints?
+ */
+isl_bool isl_basic_map_plain_is_universe(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return isl_bool_error;
+ return bmap->n_eq == 0 && bmap->n_ineq == 0;
+}
+
+/* Is "bset" obviously equal to the universe with the same space?
+ */
+isl_bool isl_basic_set_plain_is_universe(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_plain_is_universe(bset);
+}
+
+/* If "c" does not involve any existentially quantified variables,
+ * then set *univ to false and abort
+ */
+static isl_stat involves_divs(__isl_take isl_constraint *c, void *user)
+{
+ isl_bool *univ = user;
+ isl_size n;
+
+ n = isl_constraint_dim(c, isl_dim_div);
+ if (n < 0)
+ c = isl_constraint_free(c);
+ *univ = isl_constraint_involves_dims(c, isl_dim_div, 0, n);
+ isl_constraint_free(c);
+ if (*univ < 0 || !*univ)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Is "bmap" equal to the universe with the same space?
+ *
+ * First check if it is obviously equal to the universe.
+ * If not and if there are any constraints not involving
+ * existentially quantified variables, then it is certainly
+ * not equal to the universe.
+ * Otherwise, check if the universe is a subset of "bmap".
+ */
+isl_bool isl_basic_map_is_universe(__isl_keep isl_basic_map *bmap)
+{
+ isl_size n_div;
+ isl_bool univ;
+ isl_basic_map *test;
+
+ univ = isl_basic_map_plain_is_universe(bmap);
+ if (univ < 0 || univ)
+ return univ;
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0)
+ return isl_bool_error;
+ if (n_div == 0)
+ return isl_bool_false;
+ univ = isl_bool_true;
+ if (isl_basic_map_foreach_constraint(bmap, &involves_divs, &univ) < 0 &&
+ univ)
+ return isl_bool_error;
+ if (univ < 0 || !univ)
+ return univ;
+ test = isl_basic_map_universe(isl_basic_map_get_space(bmap));
+ univ = isl_basic_map_is_subset(test, bmap);
+ isl_basic_map_free(test);
+ return univ;
+}
+
+/* Is "bset" equal to the universe with the same space?
+ */
+isl_bool isl_basic_set_is_universe(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_is_universe(bset);
+}
+
+isl_bool isl_map_plain_is_universe(__isl_keep isl_map *map)
+{
+ int i;
+
+ if (!map)
+ return isl_bool_error;
+
+ for (i = 0; i < map->n; ++i) {
+ isl_bool r = isl_basic_map_plain_is_universe(map->p[i]);
+ if (r < 0 || r)
+ return r;
+ }
+
+ return isl_bool_false;
+}
+
+isl_bool isl_set_plain_is_universe(__isl_keep isl_set *set)
+{
+ return isl_map_plain_is_universe(set_to_map(set));
+}
+
+isl_bool isl_basic_map_is_empty(__isl_keep isl_basic_map *bmap)
+{
+ struct isl_basic_set *bset = NULL;
+ struct isl_vec *sample = NULL;
+ isl_bool empty, non_empty;
+
+ if (!bmap)
+ return isl_bool_error;
+
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY))
+ return isl_bool_true;
+
+ if (isl_basic_map_plain_is_universe(bmap))
+ return isl_bool_false;
+
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL)) {
+ struct isl_basic_map *copy = isl_basic_map_copy(bmap);
+ copy = isl_basic_map_remove_redundancies(copy);
+ empty = isl_basic_map_plain_is_empty(copy);
+ isl_basic_map_free(copy);
+ return empty;
+ }
+
+ non_empty = isl_basic_map_plain_is_non_empty(bmap);
+ if (non_empty < 0)
+ return isl_bool_error;
+ if (non_empty)
+ return isl_bool_false;
+ isl_vec_free(bmap->sample);
+ bmap->sample = NULL;
+ bset = isl_basic_map_underlying_set(isl_basic_map_copy(bmap));
+ if (!bset)
+ return isl_bool_error;
+ sample = isl_basic_set_sample_vec(bset);
+ if (!sample)
+ return isl_bool_error;
+ empty = sample->size == 0;
+ isl_vec_free(bmap->sample);
+ bmap->sample = sample;
+ if (empty)
+ ISL_F_SET(bmap, ISL_BASIC_MAP_EMPTY);
+
+ return empty;
+}
+
+isl_bool isl_basic_map_plain_is_empty(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return isl_bool_error;
+ return ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY);
+}
+
+isl_bool isl_basic_set_plain_is_empty(__isl_keep isl_basic_set *bset)
+{
+ if (!bset)
+ return isl_bool_error;
+ return ISL_F_ISSET(bset, ISL_BASIC_SET_EMPTY);
+}
+
+/* Is "bmap" known to be non-empty?
+ *
+ * That is, is the cached sample still valid?
+ */
+isl_bool isl_basic_map_plain_is_non_empty(__isl_keep isl_basic_map *bmap)
+{
+ isl_size total;
+
+ if (!bmap)
+ return isl_bool_error;
+ if (!bmap->sample)
+ return isl_bool_false;
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ if (bmap->sample->size != 1 + total)
+ return isl_bool_false;
+ return isl_basic_map_contains(bmap, bmap->sample);
+}
+
+isl_bool isl_basic_set_is_empty(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_is_empty(bset_to_bmap(bset));
+}
+
+__isl_give isl_map *isl_basic_map_union(__isl_take isl_basic_map *bmap1,
+ __isl_take isl_basic_map *bmap2)
+{
+ struct isl_map *map;
+
+ if (isl_basic_map_check_equal_space(bmap1, bmap2) < 0)
+ goto error;
+
+ map = isl_map_alloc_space(isl_space_copy(bmap1->dim), 2, 0);
+ if (!map)
+ goto error;
+ map = isl_map_add_basic_map(map, bmap1);
+ map = isl_map_add_basic_map(map, bmap2);
+ return map;
+error:
+ isl_basic_map_free(bmap1);
+ isl_basic_map_free(bmap2);
+ return NULL;
+}
+
+__isl_give isl_set *isl_basic_set_union(__isl_take isl_basic_set *bset1,
+ __isl_take isl_basic_set *bset2)
+{
+ return set_from_map(isl_basic_map_union(bset_to_bmap(bset1),
+ bset_to_bmap(bset2)));
+}
+
+/* Order divs such that any div only depends on previous divs */
+__isl_give isl_basic_map *isl_basic_map_order_divs(
+ __isl_take isl_basic_map *bmap)
+{
+ int i;
+ isl_size off;
+
+ off = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (off < 0)
+ return isl_basic_map_free(bmap);
+
+ for (i = 0; i < bmap->n_div; ++i) {
+ int pos;
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ pos = isl_seq_first_non_zero(bmap->div[i]+1+1+off+i,
+ bmap->n_div-i);
+ if (pos == -1)
+ continue;
+ if (pos == 0)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_internal,
+ "integer division depends on itself",
+ return isl_basic_map_free(bmap));
+ bmap = isl_basic_map_swap_div(bmap, i, i + pos);
+ if (!bmap)
+ return NULL;
+ --i;
+ }
+ return bmap;
+}
+
+__isl_give isl_map *isl_map_order_divs(__isl_take isl_map *map)
+{
+ int i;
+
+ if (!map)
+ return 0;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_order_divs(map->p[i]);
+ if (!map->p[i])
+ goto error;
+ }
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Sort the local variables of "bset".
+ */
+__isl_give isl_basic_set *isl_basic_set_sort_divs(
+ __isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(isl_basic_map_sort_divs(bset_to_bmap(bset)));
+}
+
+/* Apply the expansion computed by isl_merge_divs.
+ * The expansion itself is given by "exp" while the resulting
+ * list of divs is given by "div".
+ *
+ * Move the integer divisions of "bmap" into the right position
+ * according to "exp" and then introduce the additional integer
+ * divisions, adding div constraints.
+ * The moving should be done first to avoid moving coefficients
+ * in the definitions of the extra integer divisions.
+ */
+__isl_give isl_basic_map *isl_basic_map_expand_divs(
+ __isl_take isl_basic_map *bmap, __isl_take isl_mat *div, int *exp)
+{
+ int i, j;
+ int n_div;
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap || !div)
+ goto error;
+
+ if (div->n_row < bmap->n_div)
+ isl_die(isl_mat_get_ctx(div), isl_error_invalid,
+ "not an expansion", goto error);
+
+ n_div = bmap->n_div;
+ bmap = isl_basic_map_extend(bmap, div->n_row - n_div, 0,
+ 2 * (div->n_row - n_div));
+
+ for (i = n_div; i < div->n_row; ++i)
+ if (isl_basic_map_alloc_div(bmap) < 0)
+ goto error;
+
+ for (j = n_div - 1; j >= 0; --j) {
+ if (exp[j] == j)
+ break;
+ bmap = isl_basic_map_swap_div(bmap, j, exp[j]);
+ if (!bmap)
+ goto error;
+ }
+ j = 0;
+ for (i = 0; i < div->n_row; ++i) {
+ if (j < n_div && exp[j] == i) {
+ j++;
+ } else {
+ isl_seq_cpy(bmap->div[i], div->row[i], div->n_col);
+ if (isl_basic_map_div_is_marked_unknown(bmap, i))
+ continue;
+ bmap = isl_basic_map_add_div_constraints(bmap, i);
+ if (!bmap)
+ goto error;
+ }
+ }
+
+ isl_mat_free(div);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ isl_mat_free(div);
+ return NULL;
+}
+
+/* Apply the expansion computed by isl_merge_divs.
+ * The expansion itself is given by "exp" while the resulting
+ * list of divs is given by "div".
+ */
+__isl_give isl_basic_set *isl_basic_set_expand_divs(
+ __isl_take isl_basic_set *bset, __isl_take isl_mat *div, int *exp)
+{
+ return isl_basic_map_expand_divs(bset, div, exp);
+}
+
+/* Look for a div in dst that corresponds to the div "div" in src.
+ * The divs before "div" in src and dst are assumed to be the same.
+ *
+ * Return the position of the corresponding div in dst
+ * if there is one. Otherwise, return a position beyond the integer divisions.
+ * Return -1 on error.
+ */
+static int find_div(__isl_keep isl_basic_map *dst,
+ __isl_keep isl_basic_map *src, unsigned div)
+{
+ int i;
+ isl_size n_div;
+ isl_size v_div;
+
+ v_div = isl_basic_map_var_offset(src, isl_dim_div);
+ n_div = isl_basic_map_dim(dst, isl_dim_div);
+ if (n_div < 0 || v_div < 0)
+ return -1;
+ isl_assert(dst->ctx, div <= n_div, return -1);
+ for (i = div; i < n_div; ++i)
+ if (isl_seq_eq(dst->div[i], src->div[div], 1+1+v_div+div) &&
+ isl_seq_first_non_zero(dst->div[i] + 1 + 1 + v_div + div,
+ n_div - div) == -1)
+ return i;
+ return n_div;
+}
+
+/* Align the divs of "dst" to those of "src", adding divs from "src"
+ * if needed. That is, make sure that the first src->n_div divs
+ * of the result are equal to those of src.
+ * The integer division of "src" are assumed to be ordered.
+ *
+ * The integer divisions are swapped into the right position
+ * (possibly after adding them first). This may result
+ * in the remaining integer divisions appearing in the wrong order,
+ * i.e., with some integer division appearing before
+ * some other integer division on which it depends.
+ * The integer divisions therefore need to be ordered.
+ * This will not affect the integer divisions aligned to those of "src",
+ * since "src" is assumed to have ordered integer divisions.
+ *
+ * The result is not finalized as by design it will have redundant
+ * divs if any divs from "src" were copied.
+ */
+__isl_give isl_basic_map *isl_basic_map_align_divs(
+ __isl_take isl_basic_map *dst, __isl_keep isl_basic_map *src)
+{
+ int i;
+ isl_bool known;
+ int extended;
+ isl_size v_div;
+ isl_size dst_n_div;
+
+ if (!dst || !src)
+ return isl_basic_map_free(dst);
+
+ if (src->n_div == 0)
+ return dst;
+
+ known = isl_basic_map_divs_known(src);
+ if (known < 0)
+ return isl_basic_map_free(dst);
+ if (!known)
+ isl_die(isl_basic_map_get_ctx(src), isl_error_invalid,
+ "some src divs are unknown",
+ return isl_basic_map_free(dst));
+
+ v_div = isl_basic_map_var_offset(src, isl_dim_div);
+ if (v_div < 0)
+ return isl_basic_map_free(dst);
+
+ extended = 0;
+ dst_n_div = isl_basic_map_dim(dst, isl_dim_div);
+ if (dst_n_div < 0)
+ dst = isl_basic_map_free(dst);
+ for (i = 0; i < src->n_div; ++i) {
+ int j = find_div(dst, src, i);
+ if (j < 0)
+ dst = isl_basic_map_free(dst);
+ if (j == dst_n_div) {
+ if (!extended) {
+ int extra = src->n_div - i;
+ dst = isl_basic_map_cow(dst);
+ if (!dst)
+ return isl_basic_map_free(dst);
+ dst = isl_basic_map_extend(dst,
+ extra, 0, 2 * extra);
+ extended = 1;
+ }
+ j = isl_basic_map_alloc_div(dst);
+ if (j < 0)
+ return isl_basic_map_free(dst);
+ isl_seq_cpy(dst->div[j], src->div[i], 1+1+v_div+i);
+ isl_seq_clr(dst->div[j]+1+1+v_div+i, dst->n_div - i);
+ dst_n_div++;
+ dst = isl_basic_map_add_div_constraints(dst, j);
+ if (!dst)
+ return isl_basic_map_free(dst);
+ }
+ if (j != i)
+ dst = isl_basic_map_swap_div(dst, i, j);
+ if (!dst)
+ return isl_basic_map_free(dst);
+ }
+ return isl_basic_map_order_divs(dst);
+}
+
+__isl_give isl_map *isl_map_align_divs_internal(__isl_take isl_map *map)
+{
+ int i;
+
+ if (!map)
+ return NULL;
+ if (map->n == 0)
+ return map;
+ map = isl_map_compute_divs(map);
+ map = isl_map_order_divs(map);
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 1; i < map->n; ++i)
+ map->p[0] = isl_basic_map_align_divs(map->p[0], map->p[i]);
+ for (i = 1; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_align_divs(map->p[i], map->p[0]);
+ if (!map->p[i])
+ return isl_map_free(map);
+ }
+
+ map = isl_map_unmark_normalized(map);
+ return map;
+}
+
+__isl_give isl_map *isl_map_align_divs(__isl_take isl_map *map)
+{
+ return isl_map_align_divs_internal(map);
+}
+
+__isl_give isl_set *isl_set_align_divs(__isl_take isl_set *set)
+{
+ return set_from_map(isl_map_align_divs_internal(set_to_map(set)));
+}
+
+/* Align the divs of the basic maps in "map" to those
+ * of the basic maps in "list", as well as to the other basic maps in "map".
+ * The elements in "list" are assumed to have known divs.
+ */
+__isl_give isl_map *isl_map_align_divs_to_basic_map_list(
+ __isl_take isl_map *map, __isl_keep isl_basic_map_list *list)
+{
+ int i;
+ isl_size n;
+
+ n = isl_basic_map_list_n_basic_map(list);
+ map = isl_map_compute_divs(map);
+ map = isl_map_cow(map);
+ if (!map || n < 0)
+ return isl_map_free(map);
+ if (map->n == 0)
+ return map;
+
+ for (i = 0; i < n; ++i) {
+ isl_basic_map *bmap;
+
+ bmap = isl_basic_map_list_get_basic_map(list, i);
+ bmap = isl_basic_map_order_divs(bmap);
+ map->p[0] = isl_basic_map_align_divs(map->p[0], bmap);
+ isl_basic_map_free(bmap);
+ }
+ if (!map->p[0])
+ return isl_map_free(map);
+
+ return isl_map_align_divs_internal(map);
+}
+
+/* Align the divs of each element of "list" to those of "bmap".
+ * Both "bmap" and the elements of "list" are assumed to have known divs.
+ */
+__isl_give isl_basic_map_list *isl_basic_map_list_align_divs_to_basic_map(
+ __isl_take isl_basic_map_list *list, __isl_keep isl_basic_map *bmap)
+{
+ int i;
+ isl_size n;
+
+ n = isl_basic_map_list_n_basic_map(list);
+ if (n < 0 || !bmap)
+ return isl_basic_map_list_free(list);
+
+ for (i = 0; i < n; ++i) {
+ isl_basic_map *bmap_i;
+
+ bmap_i = isl_basic_map_list_get_basic_map(list, i);
+ bmap_i = isl_basic_map_align_divs(bmap_i, bmap);
+ list = isl_basic_map_list_set_basic_map(list, i, bmap_i);
+ }
+
+ return list;
+}
+
+__isl_give isl_set *isl_set_apply( __isl_take isl_set *set,
+ __isl_take isl_map *map)
+{
+ isl_bool ok;
+
+ isl_map_align_params_set(&map, &set);
+ ok = isl_map_compatible_domain(map, set);
+ if (ok < 0)
+ goto error;
+ if (!ok)
+ isl_die(isl_set_get_ctx(set), isl_error_invalid,
+ "incompatible spaces", goto error);
+ map = isl_map_intersect_domain(map, set);
+ set = isl_map_range(map);
+ return set;
+error:
+ isl_set_free(set);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* There is no need to cow as removing empty parts doesn't change
+ * the meaning of the set.
+ */
+__isl_give isl_map *isl_map_remove_empty_parts(__isl_take isl_map *map)
+{
+ int i;
+
+ if (!map)
+ return NULL;
+
+ for (i = map->n - 1; i >= 0; --i)
+ map = remove_if_empty(map, i);
+
+ return map;
+}
+
+__isl_give isl_set *isl_set_remove_empty_parts(__isl_take isl_set *set)
+{
+ return set_from_map(isl_map_remove_empty_parts(set_to_map(set)));
+}
+
+/* Create a binary relation that maps the shared initial "pos" dimensions
+ * of "bset1" and "bset2" to the remaining dimensions of "bset1" and "bset2".
+ */
+static __isl_give isl_basic_map *join_initial(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2, int pos)
+{
+ isl_basic_map *bmap1;
+ isl_basic_map *bmap2;
+
+ bmap1 = isl_basic_map_from_range(isl_basic_set_copy(bset1));
+ bmap2 = isl_basic_map_from_range(isl_basic_set_copy(bset2));
+ bmap1 = isl_basic_map_move_dims(bmap1, isl_dim_in, 0,
+ isl_dim_out, 0, pos);
+ bmap2 = isl_basic_map_move_dims(bmap2, isl_dim_in, 0,
+ isl_dim_out, 0, pos);
+ return isl_basic_map_range_product(bmap1, bmap2);
+}
+
+/* Given two basic sets bset1 and bset2, compute the maximal difference
+ * between the values of dimension pos in bset1 and those in bset2
+ * for any common value of the parameters and dimensions preceding pos.
+ */
+static enum isl_lp_result basic_set_maximal_difference_at(
+ __isl_keep isl_basic_set *bset1, __isl_keep isl_basic_set *bset2,
+ int pos, isl_int *opt)
+{
+ isl_basic_map *bmap1;
+ struct isl_ctx *ctx;
+ struct isl_vec *obj;
+ isl_size total;
+ isl_size nparam;
+ isl_size dim1;
+ enum isl_lp_result res;
+
+ nparam = isl_basic_set_dim(bset1, isl_dim_param);
+ dim1 = isl_basic_set_dim(bset1, isl_dim_set);
+ if (nparam < 0 || dim1 < 0 || !bset2)
+ return isl_lp_error;
+
+ bmap1 = join_initial(bset1, bset2, pos);
+ total = isl_basic_map_dim(bmap1, isl_dim_all);
+ if (total < 0)
+ return isl_lp_error;
+
+ ctx = bmap1->ctx;
+ obj = isl_vec_alloc(ctx, 1 + total);
+ if (!obj)
+ goto error;
+ isl_seq_clr(obj->block.data, 1 + total);
+ isl_int_set_si(obj->block.data[1+nparam+pos], 1);
+ isl_int_set_si(obj->block.data[1+nparam+pos+(dim1-pos)], -1);
+ res = isl_basic_map_solve_lp(bmap1, 1, obj->block.data, ctx->one,
+ opt, NULL, NULL);
+ isl_basic_map_free(bmap1);
+ isl_vec_free(obj);
+ return res;
+error:
+ isl_basic_map_free(bmap1);
+ return isl_lp_error;
+}
+
+/* Given two _disjoint_ basic sets bset1 and bset2, check whether
+ * for any common value of the parameters and dimensions preceding pos
+ * in both basic sets, the values of dimension pos in bset1 are
+ * smaller or larger than those in bset2.
+ *
+ * Returns
+ * 1 if bset1 follows bset2
+ * -1 if bset1 precedes bset2
+ * 0 if bset1 and bset2 are incomparable
+ * -2 if some error occurred.
+ */
+int isl_basic_set_compare_at(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2, int pos)
+{
+ isl_int opt;
+ enum isl_lp_result res;
+ int cmp;
+
+ isl_int_init(opt);
+
+ res = basic_set_maximal_difference_at(bset1, bset2, pos, &opt);
+
+ if (res == isl_lp_empty)
+ cmp = 0;
+ else if ((res == isl_lp_ok && isl_int_is_pos(opt)) ||
+ res == isl_lp_unbounded)
+ cmp = 1;
+ else if (res == isl_lp_ok && isl_int_is_neg(opt))
+ cmp = -1;
+ else
+ cmp = -2;
+
+ isl_int_clear(opt);
+ return cmp;
+}
+
+/* Given two basic sets bset1 and bset2, check whether
+ * for any common value of the parameters and dimensions preceding pos
+ * there is a value of dimension pos in bset1 that is larger
+ * than a value of the same dimension in bset2.
+ *
+ * Return
+ * 1 if there exists such a pair
+ * 0 if there is no such pair, but there is a pair of equal values
+ * -1 otherwise
+ * -2 if some error occurred.
+ */
+int isl_basic_set_follows_at(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2, int pos)
+{
+ isl_bool empty;
+ isl_basic_map *bmap;
+ isl_size dim1;
+
+ dim1 = isl_basic_set_dim(bset1, isl_dim_set);
+ if (dim1 < 0)
+ return -2;
+ bmap = join_initial(bset1, bset2, pos);
+ bmap = isl_basic_map_order_ge(bmap, isl_dim_out, 0,
+ isl_dim_out, dim1 - pos);
+ empty = isl_basic_map_is_empty(bmap);
+ if (empty < 0)
+ goto error;
+ if (empty) {
+ isl_basic_map_free(bmap);
+ return -1;
+ }
+ bmap = isl_basic_map_order_gt(bmap, isl_dim_out, 0,
+ isl_dim_out, dim1 - pos);
+ empty = isl_basic_map_is_empty(bmap);
+ if (empty < 0)
+ goto error;
+ isl_basic_map_free(bmap);
+ if (empty)
+ return 0;
+ return 1;
+error:
+ isl_basic_map_free(bmap);
+ return -2;
+}
+
+/* Given two sets set1 and set2, check whether
+ * for any common value of the parameters and dimensions preceding pos
+ * there is a value of dimension pos in set1 that is larger
+ * than a value of the same dimension in set2.
+ *
+ * Return
+ * 1 if there exists such a pair
+ * 0 if there is no such pair, but there is a pair of equal values
+ * -1 otherwise
+ * -2 if some error occurred.
+ */
+int isl_set_follows_at(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2, int pos)
+{
+ int i, j;
+ int follows = -1;
+
+ if (!set1 || !set2)
+ return -2;
+
+ for (i = 0; i < set1->n; ++i)
+ for (j = 0; j < set2->n; ++j) {
+ int f;
+ f = isl_basic_set_follows_at(set1->p[i], set2->p[j], pos);
+ if (f == 1 || f == -2)
+ return f;
+ if (f > follows)
+ follows = f;
+ }
+
+ return follows;
+}
+
+static isl_bool isl_basic_map_plain_has_fixed_var(
+ __isl_keep isl_basic_map *bmap, unsigned pos, isl_int *val)
+{
+ int i;
+ int d;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ for (i = 0, d = total-1; i < bmap->n_eq && d+1 > pos; ++i) {
+ for (; d+1 > pos; --d)
+ if (!isl_int_is_zero(bmap->eq[i][1+d]))
+ break;
+ if (d != pos)
+ continue;
+ if (isl_seq_first_non_zero(bmap->eq[i]+1, d) != -1)
+ return isl_bool_false;
+ if (isl_seq_first_non_zero(bmap->eq[i]+1+d+1, total-d-1) != -1)
+ return isl_bool_false;
+ if (!isl_int_is_one(bmap->eq[i][1+d]))
+ return isl_bool_false;
+ if (val)
+ isl_int_neg(*val, bmap->eq[i][0]);
+ return isl_bool_true;
+ }
+ return isl_bool_false;
+}
+
+static isl_bool isl_map_plain_has_fixed_var(__isl_keep isl_map *map,
+ unsigned pos, isl_int *val)
+{
+ int i;
+ isl_int v;
+ isl_int tmp;
+ isl_bool fixed;
+
+ if (!map)
+ return isl_bool_error;
+ if (map->n == 0)
+ return isl_bool_false;
+ if (map->n == 1)
+ return isl_basic_map_plain_has_fixed_var(map->p[0], pos, val);
+ isl_int_init(v);
+ isl_int_init(tmp);
+ fixed = isl_basic_map_plain_has_fixed_var(map->p[0], pos, &v);
+ for (i = 1; fixed == isl_bool_true && i < map->n; ++i) {
+ fixed = isl_basic_map_plain_has_fixed_var(map->p[i], pos, &tmp);
+ if (fixed == isl_bool_true && isl_int_ne(tmp, v))
+ fixed = isl_bool_false;
+ }
+ if (val)
+ isl_int_set(*val, v);
+ isl_int_clear(tmp);
+ isl_int_clear(v);
+ return fixed;
+}
+
+static isl_bool isl_basic_set_plain_has_fixed_var(
+ __isl_keep isl_basic_set *bset, unsigned pos, isl_int *val)
+{
+ return isl_basic_map_plain_has_fixed_var(bset_to_bmap(bset),
+ pos, val);
+}
+
+isl_bool isl_basic_map_plain_is_fixed(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, isl_int *val)
+{
+ if (isl_basic_map_check_range(bmap, type, pos, 1) < 0)
+ return isl_bool_error;
+ return isl_basic_map_plain_has_fixed_var(bmap,
+ isl_basic_map_offset(bmap, type) - 1 + pos, val);
+}
+
+/* If "bmap" obviously lies on a hyperplane where the given dimension
+ * has a fixed value, then return that value.
+ * Otherwise return NaN.
+ */
+__isl_give isl_val *isl_basic_map_plain_get_val_if_fixed(
+ __isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos)
+{
+ isl_ctx *ctx;
+ isl_val *v;
+ isl_bool fixed;
+
+ if (!bmap)
+ return NULL;
+ ctx = isl_basic_map_get_ctx(bmap);
+ v = isl_val_alloc(ctx);
+ if (!v)
+ return NULL;
+ fixed = isl_basic_map_plain_is_fixed(bmap, type, pos, &v->n);
+ if (fixed < 0)
+ return isl_val_free(v);
+ if (fixed) {
+ isl_int_set_si(v->d, 1);
+ return v;
+ }
+ isl_val_free(v);
+ return isl_val_nan(ctx);
+}
+
+isl_bool isl_map_plain_is_fixed(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos, isl_int *val)
+{
+ if (isl_map_check_range(map, type, pos, 1) < 0)
+ return isl_bool_error;
+ return isl_map_plain_has_fixed_var(map,
+ map_offset(map, type) - 1 + pos, val);
+}
+
+/* If "map" obviously lies on a hyperplane where the given dimension
+ * has a fixed value, then return that value.
+ * Otherwise return NaN.
+ */
+__isl_give isl_val *isl_map_plain_get_val_if_fixed(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos)
+{
+ isl_ctx *ctx;
+ isl_val *v;
+ isl_bool fixed;
+
+ if (!map)
+ return NULL;
+ ctx = isl_map_get_ctx(map);
+ v = isl_val_alloc(ctx);
+ if (!v)
+ return NULL;
+ fixed = isl_map_plain_is_fixed(map, type, pos, &v->n);
+ if (fixed < 0)
+ return isl_val_free(v);
+ if (fixed) {
+ isl_int_set_si(v->d, 1);
+ return v;
+ }
+ isl_val_free(v);
+ return isl_val_nan(ctx);
+}
+
+/* If "set" obviously lies on a hyperplane where the given dimension
+ * has a fixed value, then return that value.
+ * Otherwise return NaN.
+ */
+__isl_give isl_val *isl_set_plain_get_val_if_fixed(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos)
+{
+ return isl_map_plain_get_val_if_fixed(set, type, pos);
+}
+
+/* Return a sequence of values in the same space as "set"
+ * that are equal to the corresponding set dimensions of "set"
+ * for those set dimensions that obviously lie on a hyperplane
+ * where the dimension has a fixed value.
+ * The other elements are set to NaN.
+ */
+__isl_give isl_multi_val *isl_set_get_plain_multi_val_if_fixed(
+ __isl_keep isl_set *set)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_multi_val *mv;
+
+ space = isl_space_drop_all_params(isl_set_get_space(set));
+ mv = isl_multi_val_alloc(space);
+ n = isl_multi_val_size(mv);
+ if (n < 0)
+ return isl_multi_val_free(mv);
+
+ for (i = 0; i < n; ++i) {
+ isl_val *v;
+
+ v = isl_set_plain_get_val_if_fixed(set, isl_dim_set, i);
+ mv = isl_multi_val_set_val(mv, i, v);
+ }
+
+ return mv;
+}
+
+/* Check if dimension dim has fixed value and if so and if val is not NULL,
+ * then return this fixed value in *val.
+ */
+isl_bool isl_basic_set_plain_dim_is_fixed(__isl_keep isl_basic_set *bset,
+ unsigned dim, isl_int *val)
+{
+ isl_size nparam;
+
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ if (nparam < 0)
+ return isl_bool_error;
+ return isl_basic_set_plain_has_fixed_var(bset, nparam + dim, val);
+}
+
+/* Return -1 if the constraint "c1" should be sorted before "c2"
+ * and 1 if it should be sorted after "c2".
+ * Return 0 if the two constraints are the same (up to the constant term).
+ *
+ * In particular, if a constraint involves later variables than another
+ * then it is sorted after this other constraint.
+ * uset_gist depends on constraints without existentially quantified
+ * variables sorting first.
+ *
+ * For constraints that have the same latest variable, those
+ * with the same coefficient for this latest variable (first in absolute value
+ * and then in actual value) are grouped together.
+ * This is useful for detecting pairs of constraints that can
+ * be chained in their printed representation.
+ *
+ * Finally, within a group, constraints are sorted according to
+ * their coefficients (excluding the constant term).
+ */
+static int sort_constraint_cmp(const void *p1, const void *p2, void *arg)
+{
+ isl_int **c1 = (isl_int **) p1;
+ isl_int **c2 = (isl_int **) p2;
+ int l1, l2;
+ unsigned size = *(unsigned *) arg;
+ int cmp;
+
+ l1 = isl_seq_last_non_zero(*c1 + 1, size);
+ l2 = isl_seq_last_non_zero(*c2 + 1, size);
+
+ if (l1 != l2)
+ return l1 - l2;
+
+ cmp = isl_int_abs_cmp((*c1)[1 + l1], (*c2)[1 + l1]);
+ if (cmp != 0)
+ return cmp;
+ cmp = isl_int_cmp((*c1)[1 + l1], (*c2)[1 + l1]);
+ if (cmp != 0)
+ return -cmp;
+
+ return isl_seq_cmp(*c1 + 1, *c2 + 1, size);
+}
+
+/* Return -1 if the constraint "c1" of "bmap" is sorted before "c2"
+ * by isl_basic_map_sort_constraints, 1 if it is sorted after "c2"
+ * and 0 if the two constraints are the same (up to the constant term).
+ */
+int isl_basic_map_constraint_cmp(__isl_keep isl_basic_map *bmap,
+ isl_int *c1, isl_int *c2)
+{
+ isl_size total;
+ unsigned size;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return -2;
+ size = total;
+ return sort_constraint_cmp(&c1, &c2, &size);
+}
+
+__isl_give isl_basic_map *isl_basic_map_sort_constraints(
+ __isl_take isl_basic_map *bmap)
+{
+ isl_size total;
+ unsigned size;
+
+ if (!bmap)
+ return NULL;
+ if (bmap->n_ineq == 0)
+ return bmap;
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_SORTED))
+ return bmap;
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ size = total;
+ if (isl_sort(bmap->ineq, bmap->n_ineq, sizeof(isl_int *),
+ &sort_constraint_cmp, &size) < 0)
+ return isl_basic_map_free(bmap);
+ ISL_F_SET(bmap, ISL_BASIC_MAP_SORTED);
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_sort_constraints(
+ __isl_take isl_basic_set *bset)
+{
+ isl_basic_map *bmap = bset_to_bmap(bset);
+ return bset_from_bmap(isl_basic_map_sort_constraints(bmap));
+}
+
+__isl_give isl_basic_map *isl_basic_map_normalize(
+ __isl_take isl_basic_map *bmap)
+{
+ bmap = isl_basic_map_remove_redundancies(bmap);
+ bmap = isl_basic_map_sort_constraints(bmap);
+ return bmap;
+}
+int isl_basic_map_plain_cmp(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2)
+{
+ int i, cmp;
+ isl_size total;
+ isl_space *space1, *space2;
+
+ if (!bmap1 || !bmap2)
+ return -1;
+
+ if (bmap1 == bmap2)
+ return 0;
+ space1 = isl_basic_map_peek_space(bmap1);
+ space2 = isl_basic_map_peek_space(bmap2);
+ cmp = isl_space_cmp(space1, space2);
+ if (cmp)
+ return cmp;
+ if (ISL_F_ISSET(bmap1, ISL_BASIC_MAP_RATIONAL) !=
+ ISL_F_ISSET(bmap2, ISL_BASIC_MAP_RATIONAL))
+ return ISL_F_ISSET(bmap1, ISL_BASIC_MAP_RATIONAL) ? -1 : 1;
+ if (ISL_F_ISSET(bmap1, ISL_BASIC_MAP_EMPTY) &&
+ ISL_F_ISSET(bmap2, ISL_BASIC_MAP_EMPTY))
+ return 0;
+ if (ISL_F_ISSET(bmap1, ISL_BASIC_MAP_EMPTY))
+ return 1;
+ if (ISL_F_ISSET(bmap2, ISL_BASIC_MAP_EMPTY))
+ return -1;
+ if (bmap1->n_eq != bmap2->n_eq)
+ return bmap1->n_eq - bmap2->n_eq;
+ if (bmap1->n_ineq != bmap2->n_ineq)
+ return bmap1->n_ineq - bmap2->n_ineq;
+ if (bmap1->n_div != bmap2->n_div)
+ return bmap1->n_div - bmap2->n_div;
+ total = isl_basic_map_dim(bmap1, isl_dim_all);
+ if (total < 0)
+ return -1;
+ for (i = 0; i < bmap1->n_eq; ++i) {
+ cmp = isl_seq_cmp(bmap1->eq[i], bmap2->eq[i], 1+total);
+ if (cmp)
+ return cmp;
+ }
+ for (i = 0; i < bmap1->n_ineq; ++i) {
+ cmp = isl_seq_cmp(bmap1->ineq[i], bmap2->ineq[i], 1+total);
+ if (cmp)
+ return cmp;
+ }
+ for (i = 0; i < bmap1->n_div; ++i) {
+ cmp = isl_seq_cmp(bmap1->div[i], bmap2->div[i], 1+1+total);
+ if (cmp)
+ return cmp;
+ }
+ return 0;
+}
+
+int isl_basic_set_plain_cmp(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2)
+{
+ return isl_basic_map_plain_cmp(bset1, bset2);
+}
+
+int isl_set_plain_cmp(__isl_keep isl_set *set1, __isl_keep isl_set *set2)
+{
+ int i, cmp;
+
+ if (set1 == set2)
+ return 0;
+ if (set1->n != set2->n)
+ return set1->n - set2->n;
+
+ for (i = 0; i < set1->n; ++i) {
+ cmp = isl_basic_set_plain_cmp(set1->p[i], set2->p[i]);
+ if (cmp)
+ return cmp;
+ }
+
+ return 0;
+}
+
+isl_bool isl_basic_map_plain_is_equal(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2)
+{
+ if (!bmap1 || !bmap2)
+ return isl_bool_error;
+ return isl_basic_map_plain_cmp(bmap1, bmap2) == 0;
+}
+
+isl_bool isl_basic_set_plain_is_equal(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2)
+{
+ return isl_basic_map_plain_is_equal(bset_to_bmap(bset1),
+ bset_to_bmap(bset2));
+}
+
+static int qsort_bmap_cmp(const void *p1, const void *p2)
+{
+ isl_basic_map *bmap1 = *(isl_basic_map **) p1;
+ isl_basic_map *bmap2 = *(isl_basic_map **) p2;
+
+ return isl_basic_map_plain_cmp(bmap1, bmap2);
+}
+
+/* Sort the basic maps of "map" and remove duplicate basic maps.
+ *
+ * While removing basic maps, we make sure that the basic maps remain
+ * sorted because isl_map_normalize expects the basic maps of the result
+ * to be sorted.
+ */
+static __isl_give isl_map *sort_and_remove_duplicates(__isl_take isl_map *map)
+{
+ int i, j;
+
+ map = isl_map_remove_empty_parts(map);
+ if (!map)
+ return NULL;
+ qsort(map->p, map->n, sizeof(struct isl_basic_map *), qsort_bmap_cmp);
+ for (i = map->n - 1; i >= 1; --i) {
+ if (!isl_basic_map_plain_is_equal(map->p[i - 1], map->p[i]))
+ continue;
+ isl_basic_map_free(map->p[i-1]);
+ for (j = i; j < map->n; ++j)
+ map->p[j - 1] = map->p[j];
+ map->n--;
+ }
+
+ return map;
+}
+
+/* Remove obvious duplicates among the basic maps of "map".
+ *
+ * Unlike isl_map_normalize, this function does not remove redundant
+ * constraints and only removes duplicates that have exactly the same
+ * constraints in the input. It does sort the constraints and
+ * the basic maps to ease the detection of duplicates.
+ *
+ * If "map" has already been normalized or if the basic maps are
+ * disjoint, then there can be no duplicates.
+ */
+__isl_give isl_map *isl_map_remove_obvious_duplicates(__isl_take isl_map *map)
+{
+ int i;
+ isl_basic_map *bmap;
+
+ if (!map)
+ return NULL;
+ if (map->n <= 1)
+ return map;
+ if (ISL_F_ISSET(map, ISL_MAP_NORMALIZED | ISL_MAP_DISJOINT))
+ return map;
+ for (i = 0; i < map->n; ++i) {
+ bmap = isl_basic_map_copy(map->p[i]);
+ bmap = isl_basic_map_sort_constraints(bmap);
+ if (!bmap)
+ return isl_map_free(map);
+ isl_basic_map_free(map->p[i]);
+ map->p[i] = bmap;
+ }
+
+ map = sort_and_remove_duplicates(map);
+ return map;
+}
+
+/* We normalize in place, but if anything goes wrong we need
+ * to return NULL, so we need to make sure we don't change the
+ * meaning of any possible other copies of map.
+ */
+__isl_give isl_map *isl_map_normalize(__isl_take isl_map *map)
+{
+ int i;
+ struct isl_basic_map *bmap;
+
+ if (!map)
+ return NULL;
+ if (ISL_F_ISSET(map, ISL_MAP_NORMALIZED))
+ return map;
+ for (i = 0; i < map->n; ++i) {
+ bmap = isl_basic_map_normalize(isl_basic_map_copy(map->p[i]));
+ if (!bmap)
+ goto error;
+ isl_basic_map_free(map->p[i]);
+ map->p[i] = bmap;
+ }
+
+ map = sort_and_remove_duplicates(map);
+ if (map)
+ ISL_F_SET(map, ISL_MAP_NORMALIZED);
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_normalize(__isl_take isl_set *set)
+{
+ return set_from_map(isl_map_normalize(set_to_map(set)));
+}
+
+isl_bool isl_map_plain_is_equal(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2)
+{
+ int i;
+ isl_bool equal;
+
+ if (!map1 || !map2)
+ return isl_bool_error;
+
+ if (map1 == map2)
+ return isl_bool_true;
+ equal = isl_map_has_equal_space(map1, map2);
+ if (equal < 0 || !equal)
+ return equal;
+
+ map1 = isl_map_copy(map1);
+ map2 = isl_map_copy(map2);
+ map1 = isl_map_normalize(map1);
+ map2 = isl_map_normalize(map2);
+ if (!map1 || !map2)
+ goto error;
+ equal = map1->n == map2->n;
+ for (i = 0; equal && i < map1->n; ++i) {
+ equal = isl_basic_map_plain_is_equal(map1->p[i], map2->p[i]);
+ if (equal < 0)
+ goto error;
+ }
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return equal;
+error:
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return isl_bool_error;
+}
+
+isl_bool isl_set_plain_is_equal(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2)
+{
+ return isl_map_plain_is_equal(set_to_map(set1), set_to_map(set2));
+}
+
+/* Return the basic maps in "map" as a list.
+ */
+__isl_give isl_basic_map_list *isl_map_get_basic_map_list(
+ __isl_keep isl_map *map)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_basic_map_list *list;
+
+ if (!map)
+ return NULL;
+ ctx = isl_map_get_ctx(map);
+ list = isl_basic_map_list_alloc(ctx, map->n);
+
+ for (i = 0; i < map->n; ++i) {
+ isl_basic_map *bmap;
+
+ bmap = isl_basic_map_copy(map->p[i]);
+ list = isl_basic_map_list_add(list, bmap);
+ }
+
+ return list;
+}
+
+/* Return the intersection of the elements in the non-empty list "list".
+ * All elements are assumed to live in the same space.
+ */
+__isl_give isl_basic_map *isl_basic_map_list_intersect(
+ __isl_take isl_basic_map_list *list)
+{
+ int i;
+ isl_size n;
+ isl_basic_map *bmap;
+
+ n = isl_basic_map_list_n_basic_map(list);
+ if (n < 0)
+ goto error;
+ if (n < 1)
+ isl_die(isl_basic_map_list_get_ctx(list), isl_error_invalid,
+ "expecting non-empty list", goto error);
+
+ bmap = isl_basic_map_list_get_basic_map(list, 0);
+ for (i = 1; i < n; ++i) {
+ isl_basic_map *bmap_i;
+
+ bmap_i = isl_basic_map_list_get_basic_map(list, i);
+ bmap = isl_basic_map_intersect(bmap, bmap_i);
+ }
+
+ isl_basic_map_list_free(list);
+ return bmap;
+error:
+ isl_basic_map_list_free(list);
+ return NULL;
+}
+
+/* Return the intersection of the elements in the non-empty list "list".
+ * All elements are assumed to live in the same space.
+ */
+__isl_give isl_basic_set *isl_basic_set_list_intersect(
+ __isl_take isl_basic_set_list *list)
+{
+ return isl_basic_map_list_intersect(list);
+}
+
+/* Return the union of the elements of "list".
+ * The list is required to have at least one element.
+ */
+__isl_give isl_set *isl_basic_set_list_union(
+ __isl_take isl_basic_set_list *list)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_basic_set *bset;
+ isl_set *set;
+
+ n = isl_basic_set_list_n_basic_set(list);
+ if (n < 0)
+ goto error;
+ if (n < 1)
+ isl_die(isl_basic_set_list_get_ctx(list), isl_error_invalid,
+ "expecting non-empty list", goto error);
+
+ bset = isl_basic_set_list_get_basic_set(list, 0);
+ space = isl_basic_set_get_space(bset);
+ isl_basic_set_free(bset);
+
+ set = isl_set_alloc_space(space, n, 0);
+ for (i = 0; i < n; ++i) {
+ bset = isl_basic_set_list_get_basic_set(list, i);
+ set = isl_set_add_basic_set(set, bset);
+ }
+
+ isl_basic_set_list_free(list);
+ return set;
+error:
+ isl_basic_set_list_free(list);
+ return NULL;
+}
+
+/* Return the union of the elements in the non-empty list "list".
+ * All elements are assumed to live in the same space.
+ */
+__isl_give isl_set *isl_set_list_union(__isl_take isl_set_list *list)
+{
+ int i;
+ isl_size n;
+ isl_set *set;
+
+ n = isl_set_list_n_set(list);
+ if (n < 0)
+ goto error;
+ if (n < 1)
+ isl_die(isl_set_list_get_ctx(list), isl_error_invalid,
+ "expecting non-empty list", goto error);
+
+ set = isl_set_list_get_set(list, 0);
+ for (i = 1; i < n; ++i) {
+ isl_set *set_i;
+
+ set_i = isl_set_list_get_set(list, i);
+ set = isl_set_union(set, set_i);
+ }
+
+ isl_set_list_free(list);
+ return set;
+error:
+ isl_set_list_free(list);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_product(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2)
+{
+ isl_space *space_result = NULL;
+ struct isl_basic_map *bmap;
+ unsigned in1, in2, out1, out2, nparam, total, pos;
+ struct isl_dim_map *dim_map1, *dim_map2;
+
+ if (isl_basic_map_check_equal_params(bmap1, bmap2) < 0)
+ goto error;
+ space_result = isl_space_product(isl_space_copy(bmap1->dim),
+ isl_space_copy(bmap2->dim));
+
+ in1 = isl_basic_map_dim(bmap1, isl_dim_in);
+ in2 = isl_basic_map_dim(bmap2, isl_dim_in);
+ out1 = isl_basic_map_dim(bmap1, isl_dim_out);
+ out2 = isl_basic_map_dim(bmap2, isl_dim_out);
+ nparam = isl_basic_map_dim(bmap1, isl_dim_param);
+
+ total = nparam + in1 + in2 + out1 + out2 + bmap1->n_div + bmap2->n_div;
+ dim_map1 = isl_dim_map_alloc(bmap1->ctx, total);
+ dim_map2 = isl_dim_map_alloc(bmap1->ctx, total);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_param, pos = 0);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_param, pos = 0);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_in, pos += nparam);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_in, pos += in1);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_out, pos += in2);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_out, pos += out1);
+ isl_dim_map_div(dim_map1, bmap1, pos += out2);
+ isl_dim_map_div(dim_map2, bmap2, pos += bmap1->n_div);
+
+ bmap = isl_basic_map_alloc_space(space_result,
+ bmap1->n_div + bmap2->n_div,
+ bmap1->n_eq + bmap2->n_eq,
+ bmap1->n_ineq + bmap2->n_ineq);
+ bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap1, dim_map1);
+ bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap2, dim_map2);
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap1);
+ isl_basic_map_free(bmap2);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_flat_product(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2)
+{
+ isl_basic_map *prod;
+
+ prod = isl_basic_map_product(bmap1, bmap2);
+ prod = isl_basic_map_flatten(prod);
+ return prod;
+}
+
+__isl_give isl_basic_set *isl_basic_set_flat_product(
+ __isl_take isl_basic_set *bset1, __isl_take isl_basic_set *bset2)
+{
+ return isl_basic_map_flat_range_product(bset1, bset2);
+}
+
+__isl_give isl_basic_map *isl_basic_map_domain_product(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2)
+{
+ isl_space *space1, *space2;
+ isl_space *space_result = NULL;
+ isl_basic_map *bmap;
+ isl_size in1, in2, out, nparam;
+ unsigned total, pos;
+ struct isl_dim_map *dim_map1, *dim_map2;
+
+ in1 = isl_basic_map_dim(bmap1, isl_dim_in);
+ in2 = isl_basic_map_dim(bmap2, isl_dim_in);
+ out = isl_basic_map_dim(bmap1, isl_dim_out);
+ nparam = isl_basic_map_dim(bmap1, isl_dim_param);
+ if (in1 < 0 || in2 < 0 || out < 0 || nparam < 0)
+ goto error;
+
+ space1 = isl_basic_map_get_space(bmap1);
+ space2 = isl_basic_map_get_space(bmap2);
+ space_result = isl_space_domain_product(space1, space2);
+
+ total = nparam + in1 + in2 + out + bmap1->n_div + bmap2->n_div;
+ dim_map1 = isl_dim_map_alloc(bmap1->ctx, total);
+ dim_map2 = isl_dim_map_alloc(bmap1->ctx, total);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_param, pos = 0);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_param, pos = 0);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_in, pos += nparam);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_in, pos += in1);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_out, pos += in2);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_out, pos);
+ isl_dim_map_div(dim_map1, bmap1, pos += out);
+ isl_dim_map_div(dim_map2, bmap2, pos += bmap1->n_div);
+
+ bmap = isl_basic_map_alloc_space(space_result,
+ bmap1->n_div + bmap2->n_div,
+ bmap1->n_eq + bmap2->n_eq,
+ bmap1->n_ineq + bmap2->n_ineq);
+ bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap1, dim_map1);
+ bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap2, dim_map2);
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap1);
+ isl_basic_map_free(bmap2);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_range_product(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2)
+{
+ isl_bool rational;
+ isl_space *space_result = NULL;
+ isl_basic_map *bmap;
+ isl_size in, out1, out2, nparam;
+ unsigned total, pos;
+ struct isl_dim_map *dim_map1, *dim_map2;
+
+ rational = isl_basic_map_is_rational(bmap1);
+ if (rational >= 0 && rational)
+ rational = isl_basic_map_is_rational(bmap2);
+ in = isl_basic_map_dim(bmap1, isl_dim_in);
+ out1 = isl_basic_map_dim(bmap1, isl_dim_out);
+ out2 = isl_basic_map_dim(bmap2, isl_dim_out);
+ nparam = isl_basic_map_dim(bmap1, isl_dim_param);
+ if (in < 0 || out1 < 0 || out2 < 0 || nparam < 0 || rational < 0)
+ goto error;
+
+ if (isl_basic_map_check_equal_params(bmap1, bmap2) < 0)
+ goto error;
+
+ space_result = isl_space_range_product(isl_space_copy(bmap1->dim),
+ isl_space_copy(bmap2->dim));
+
+ total = nparam + in + out1 + out2 + bmap1->n_div + bmap2->n_div;
+ dim_map1 = isl_dim_map_alloc(bmap1->ctx, total);
+ dim_map2 = isl_dim_map_alloc(bmap1->ctx, total);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_param, pos = 0);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_param, pos = 0);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_in, pos += nparam);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_in, pos);
+ isl_dim_map_dim(dim_map1, bmap1->dim, isl_dim_out, pos += in);
+ isl_dim_map_dim(dim_map2, bmap2->dim, isl_dim_out, pos += out1);
+ isl_dim_map_div(dim_map1, bmap1, pos += out2);
+ isl_dim_map_div(dim_map2, bmap2, pos += bmap1->n_div);
+
+ bmap = isl_basic_map_alloc_space(space_result,
+ bmap1->n_div + bmap2->n_div,
+ bmap1->n_eq + bmap2->n_eq,
+ bmap1->n_ineq + bmap2->n_ineq);
+ bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap1, dim_map1);
+ bmap = isl_basic_map_add_constraints_dim_map(bmap, bmap2, dim_map2);
+ if (rational)
+ bmap = isl_basic_map_set_rational(bmap);
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_basic_map_free(bmap1);
+ isl_basic_map_free(bmap2);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_flat_range_product(
+ __isl_take isl_basic_map *bmap1, __isl_take isl_basic_map *bmap2)
+{
+ isl_basic_map *prod;
+
+ prod = isl_basic_map_range_product(bmap1, bmap2);
+ prod = isl_basic_map_flatten_range(prod);
+ return prod;
+}
+
+/* Apply "basic_map_product" to each pair of basic maps in "map1" and "map2"
+ * and collect the results.
+ * The result live in the space obtained by calling "space_product"
+ * on the spaces of "map1" and "map2".
+ * If "remove_duplicates" is set then the result may contain duplicates
+ * (even if the inputs do not) and so we try and remove the obvious
+ * duplicates.
+ */
+static __isl_give isl_map *map_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2,
+ __isl_give isl_space *(*space_product)(__isl_take isl_space *left,
+ __isl_take isl_space *right),
+ __isl_give isl_basic_map *(*basic_map_product)(
+ __isl_take isl_basic_map *left,
+ __isl_take isl_basic_map *right),
+ int remove_duplicates)
+{
+ unsigned flags = 0;
+ struct isl_map *result;
+ int i, j;
+ isl_bool m;
+
+ m = isl_map_has_equal_params(map1, map2);
+ if (m < 0)
+ goto error;
+ if (!m)
+ isl_die(isl_map_get_ctx(map1), isl_error_invalid,
+ "parameters don't match", goto error);
+
+ if (ISL_F_ISSET(map1, ISL_MAP_DISJOINT) &&
+ ISL_F_ISSET(map2, ISL_MAP_DISJOINT))
+ ISL_FL_SET(flags, ISL_MAP_DISJOINT);
+
+ result = isl_map_alloc_space(space_product(isl_space_copy(map1->dim),
+ isl_space_copy(map2->dim)),
+ map1->n * map2->n, flags);
+ if (!result)
+ goto error;
+ for (i = 0; i < map1->n; ++i)
+ for (j = 0; j < map2->n; ++j) {
+ struct isl_basic_map *part;
+ part = basic_map_product(isl_basic_map_copy(map1->p[i]),
+ isl_basic_map_copy(map2->p[j]));
+ if (isl_basic_map_is_empty(part))
+ isl_basic_map_free(part);
+ else
+ result = isl_map_add_basic_map(result, part);
+ if (!result)
+ goto error;
+ }
+ if (remove_duplicates)
+ result = isl_map_remove_obvious_duplicates(result);
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return result;
+error:
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return NULL;
+}
+
+/* Given two maps A -> B and C -> D, construct a map [A -> C] -> [B -> D]
+ */
+__isl_give isl_map *isl_map_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map_align_params_bin(&map1, &map2);
+ return map_product(map1, map2, &isl_space_product,
+ &isl_basic_map_product, 0);
+}
+
+/* Given two maps A -> B and C -> D, construct a map (A, C) -> (B, D)
+ */
+__isl_give isl_map *isl_map_flat_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map *prod;
+
+ prod = isl_map_product(map1, map2);
+ prod = isl_map_flatten(prod);
+ return prod;
+}
+
+/* Given two set A and B, construct its Cartesian product A x B.
+ */
+__isl_give isl_set *isl_set_product(__isl_take isl_set *set1,
+ __isl_take isl_set *set2)
+{
+ return isl_map_range_product(set1, set2);
+}
+
+__isl_give isl_set *isl_set_flat_product(__isl_take isl_set *set1,
+ __isl_take isl_set *set2)
+{
+ return isl_map_flat_range_product(set1, set2);
+}
+
+/* Given two maps A -> B and C -> D, construct a map [A -> C] -> (B * D)
+ */
+__isl_give isl_map *isl_map_domain_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map_align_params_bin(&map1, &map2);
+ return map_product(map1, map2, &isl_space_domain_product,
+ &isl_basic_map_domain_product, 1);
+}
+
+/* Given two maps A -> B and C -> D, construct a map (A * C) -> [B -> D]
+ */
+__isl_give isl_map *isl_map_range_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map_align_params_bin(&map1, &map2);
+ return map_product(map1, map2, &isl_space_range_product,
+ &isl_basic_map_range_product, 1);
+}
+
+/* Given a map of the form [A -> B] -> [C -> D], return the map A -> C.
+ */
+__isl_give isl_map *isl_map_factor_domain(__isl_take isl_map *map)
+{
+ isl_space *space;
+ isl_size total1, keep1, total2, keep2;
+
+ total1 = isl_map_dim(map, isl_dim_in);
+ total2 = isl_map_dim(map, isl_dim_out);
+ if (total1 < 0 || total2 < 0)
+ return isl_map_free(map);
+ if (!isl_space_domain_is_wrapping(map->dim) ||
+ !isl_space_range_is_wrapping(map->dim))
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "not a product", return isl_map_free(map));
+
+ space = isl_map_get_space(map);
+ space = isl_space_factor_domain(space);
+ keep1 = isl_space_dim(space, isl_dim_in);
+ keep2 = isl_space_dim(space, isl_dim_out);
+ if (keep1 < 0 || keep2 < 0)
+ map = isl_map_free(map);
+ map = isl_map_project_out(map, isl_dim_in, keep1, total1 - keep1);
+ map = isl_map_project_out(map, isl_dim_out, keep2, total2 - keep2);
+ map = isl_map_reset_space(map, space);
+
+ return map;
+}
+
+/* Given a map of the form [A -> B] -> [C -> D], return the map B -> D.
+ */
+__isl_give isl_map *isl_map_factor_range(__isl_take isl_map *map)
+{
+ isl_space *space;
+ isl_size total1, keep1, total2, keep2;
+
+ total1 = isl_map_dim(map, isl_dim_in);
+ total2 = isl_map_dim(map, isl_dim_out);
+ if (total1 < 0 || total2 < 0)
+ return isl_map_free(map);
+ if (!isl_space_domain_is_wrapping(map->dim) ||
+ !isl_space_range_is_wrapping(map->dim))
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "not a product", return isl_map_free(map));
+
+ space = isl_map_get_space(map);
+ space = isl_space_factor_range(space);
+ keep1 = isl_space_dim(space, isl_dim_in);
+ keep2 = isl_space_dim(space, isl_dim_out);
+ if (keep1 < 0 || keep2 < 0)
+ map = isl_map_free(map);
+ map = isl_map_project_out(map, isl_dim_in, 0, total1 - keep1);
+ map = isl_map_project_out(map, isl_dim_out, 0, total2 - keep2);
+ map = isl_map_reset_space(map, space);
+
+ return map;
+}
+
+/* Given a map of the form [A -> B] -> C, return the map A -> C.
+ */
+__isl_give isl_map *isl_map_domain_factor_domain(__isl_take isl_map *map)
+{
+ isl_space *space;
+ isl_size total, keep;
+
+ total = isl_map_dim(map, isl_dim_in);
+ if (total < 0)
+ return isl_map_free(map);
+ if (!isl_space_domain_is_wrapping(map->dim))
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "domain is not a product", return isl_map_free(map));
+
+ space = isl_map_get_space(map);
+ space = isl_space_domain_factor_domain(space);
+ keep = isl_space_dim(space, isl_dim_in);
+ if (keep < 0)
+ map = isl_map_free(map);
+ map = isl_map_project_out(map, isl_dim_in, keep, total - keep);
+ map = isl_map_reset_space(map, space);
+
+ return map;
+}
+
+/* Given a map of the form [A -> B] -> C, return the map B -> C.
+ */
+__isl_give isl_map *isl_map_domain_factor_range(__isl_take isl_map *map)
+{
+ isl_space *space;
+ isl_size total, keep;
+
+ total = isl_map_dim(map, isl_dim_in);
+ if (total < 0)
+ return isl_map_free(map);
+ if (!isl_space_domain_is_wrapping(map->dim))
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "domain is not a product", return isl_map_free(map));
+
+ space = isl_map_get_space(map);
+ space = isl_space_domain_factor_range(space);
+ keep = isl_space_dim(space, isl_dim_in);
+ if (keep < 0)
+ map = isl_map_free(map);
+ map = isl_map_project_out(map, isl_dim_in, 0, total - keep);
+ map = isl_map_reset_space(map, space);
+
+ return map;
+}
+
+/* Given a map A -> [B -> C], extract the map A -> B.
+ */
+__isl_give isl_map *isl_map_range_factor_domain(__isl_take isl_map *map)
+{
+ isl_space *space;
+ isl_size total, keep;
+
+ total = isl_map_dim(map, isl_dim_out);
+ if (total < 0)
+ return isl_map_free(map);
+ if (!isl_space_range_is_wrapping(map->dim))
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "range is not a product", return isl_map_free(map));
+
+ space = isl_map_get_space(map);
+ space = isl_space_range_factor_domain(space);
+ keep = isl_space_dim(space, isl_dim_out);
+ if (keep < 0)
+ map = isl_map_free(map);
+ map = isl_map_project_out(map, isl_dim_out, keep, total - keep);
+ map = isl_map_reset_space(map, space);
+
+ return map;
+}
+
+/* Given a map A -> [B -> C], extract the map A -> C.
+ */
+__isl_give isl_map *isl_map_range_factor_range(__isl_take isl_map *map)
+{
+ isl_space *space;
+ isl_size total, keep;
+
+ total = isl_map_dim(map, isl_dim_out);
+ if (total < 0)
+ return isl_map_free(map);
+ if (!isl_space_range_is_wrapping(map->dim))
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "range is not a product", return isl_map_free(map));
+
+ space = isl_map_get_space(map);
+ space = isl_space_range_factor_range(space);
+ keep = isl_space_dim(space, isl_dim_out);
+ if (keep < 0)
+ map = isl_map_free(map);
+ map = isl_map_project_out(map, isl_dim_out, 0, total - keep);
+ map = isl_map_reset_space(map, space);
+
+ return map;
+}
+
+/* Given two maps A -> B and C -> D, construct a map (A, C) -> (B * D)
+ */
+__isl_give isl_map *isl_map_flat_domain_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map *prod;
+
+ prod = isl_map_domain_product(map1, map2);
+ prod = isl_map_flatten_domain(prod);
+ return prod;
+}
+
+/* Given two maps A -> B and C -> D, construct a map (A * C) -> (B, D)
+ */
+__isl_give isl_map *isl_map_flat_range_product(__isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_map *prod;
+
+ prod = isl_map_range_product(map1, map2);
+ prod = isl_map_flatten_range(prod);
+ return prod;
+}
+
+uint32_t isl_basic_map_get_hash(__isl_keep isl_basic_map *bmap)
+{
+ int i;
+ uint32_t hash = isl_hash_init();
+ isl_size total;
+
+ if (!bmap)
+ return 0;
+ bmap = isl_basic_map_copy(bmap);
+ bmap = isl_basic_map_normalize(bmap);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return 0;
+ isl_hash_byte(hash, bmap->n_eq & 0xFF);
+ for (i = 0; i < bmap->n_eq; ++i) {
+ uint32_t c_hash;
+ c_hash = isl_seq_get_hash(bmap->eq[i], 1 + total);
+ isl_hash_hash(hash, c_hash);
+ }
+ isl_hash_byte(hash, bmap->n_ineq & 0xFF);
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ uint32_t c_hash;
+ c_hash = isl_seq_get_hash(bmap->ineq[i], 1 + total);
+ isl_hash_hash(hash, c_hash);
+ }
+ isl_hash_byte(hash, bmap->n_div & 0xFF);
+ for (i = 0; i < bmap->n_div; ++i) {
+ uint32_t c_hash;
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ isl_hash_byte(hash, i & 0xFF);
+ c_hash = isl_seq_get_hash(bmap->div[i], 1 + 1 + total);
+ isl_hash_hash(hash, c_hash);
+ }
+ isl_basic_map_free(bmap);
+ return hash;
+}
+
+uint32_t isl_basic_set_get_hash(__isl_keep isl_basic_set *bset)
+{
+ return isl_basic_map_get_hash(bset_to_bmap(bset));
+}
+
+uint32_t isl_map_get_hash(__isl_keep isl_map *map)
+{
+ int i;
+ uint32_t hash;
+
+ if (!map)
+ return 0;
+ map = isl_map_copy(map);
+ map = isl_map_normalize(map);
+ if (!map)
+ return 0;
+
+ hash = isl_hash_init();
+ for (i = 0; i < map->n; ++i) {
+ uint32_t bmap_hash;
+ bmap_hash = isl_basic_map_get_hash(map->p[i]);
+ isl_hash_hash(hash, bmap_hash);
+ }
+
+ isl_map_free(map);
+
+ return hash;
+}
+
+uint32_t isl_set_get_hash(__isl_keep isl_set *set)
+{
+ return isl_map_get_hash(set_to_map(set));
+}
+
+/* Return the number of basic maps in the (current) representation of "map".
+ */
+isl_size isl_map_n_basic_map(__isl_keep isl_map *map)
+{
+ return map ? map->n : isl_size_error;
+}
+
+isl_size isl_set_n_basic_set(__isl_keep isl_set *set)
+{
+ return set ? set->n : isl_size_error;
+}
+
+isl_stat isl_map_foreach_basic_map(__isl_keep isl_map *map,
+ isl_stat (*fn)(__isl_take isl_basic_map *bmap, void *user), void *user)
+{
+ int i;
+
+ if (!map)
+ return isl_stat_error;
+
+ for (i = 0; i < map->n; ++i)
+ if (fn(isl_basic_map_copy(map->p[i]), user) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+isl_stat isl_set_foreach_basic_set(__isl_keep isl_set *set,
+ isl_stat (*fn)(__isl_take isl_basic_set *bset, void *user), void *user)
+{
+ int i;
+
+ if (!set)
+ return isl_stat_error;
+
+ for (i = 0; i < set->n; ++i)
+ if (fn(isl_basic_set_copy(set->p[i]), user) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Does "test" succeed on every basic set in "set"?
+ */
+isl_bool isl_set_every_basic_set(__isl_keep isl_set *set,
+ isl_bool (*test)(__isl_keep isl_basic_set *bset, void *user),
+ void *user)
+{
+ int i;
+
+ if (!set)
+ return isl_bool_error;
+
+ for (i = 0; i < set->n; ++i) {
+ isl_bool r;
+
+ r = test(set->p[i], user);
+ if (r < 0 || !r)
+ return r;
+ }
+
+ return isl_bool_true;
+}
+
+/* Return a list of basic sets, the union of which is equal to "set".
+ */
+__isl_give isl_basic_set_list *isl_set_get_basic_set_list(
+ __isl_keep isl_set *set)
+{
+ int i;
+ isl_basic_set_list *list;
+
+ if (!set)
+ return NULL;
+
+ list = isl_basic_set_list_alloc(isl_set_get_ctx(set), set->n);
+ for (i = 0; i < set->n; ++i) {
+ isl_basic_set *bset;
+
+ bset = isl_basic_set_copy(set->p[i]);
+ list = isl_basic_set_list_add(list, bset);
+ }
+
+ return list;
+}
+
+__isl_give isl_basic_set *isl_basic_set_lift(__isl_take isl_basic_set *bset)
+{
+ isl_space *space;
+
+ if (!bset)
+ return NULL;
+
+ bset = isl_basic_set_cow(bset);
+ if (!bset)
+ return NULL;
+
+ space = isl_basic_set_get_space(bset);
+ space = isl_space_lift(space, bset->n_div);
+ if (!space)
+ goto error;
+ isl_space_free(bset->dim);
+ bset->dim = space;
+ bset->extra -= bset->n_div;
+ bset->n_div = 0;
+
+ bset = isl_basic_set_finalize(bset);
+
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_lift(__isl_take isl_set *set)
+{
+ int i;
+ isl_space *space;
+ unsigned n_div;
+
+ set = set_from_map(isl_map_align_divs_internal(set_to_map(set)));
+
+ if (!set)
+ return NULL;
+
+ set = isl_set_cow(set);
+ if (!set)
+ return NULL;
+
+ n_div = set->p[0]->n_div;
+ space = isl_set_get_space(set);
+ space = isl_space_lift(space, n_div);
+ if (!space)
+ goto error;
+ isl_space_free(set->dim);
+ set->dim = space;
+
+ for (i = 0; i < set->n; ++i) {
+ set->p[i] = isl_basic_set_lift(set->p[i]);
+ if (!set->p[i])
+ goto error;
+ }
+
+ return set;
+error:
+ isl_set_free(set);
+ return NULL;
+}
+
+int isl_basic_set_size(__isl_keep isl_basic_set *bset)
+{
+ isl_size dim;
+ int size = 0;
+
+ dim = isl_basic_set_dim(bset, isl_dim_all);
+ if (dim < 0)
+ return -1;
+ size += bset->n_eq * (1 + dim);
+ size += bset->n_ineq * (1 + dim);
+ size += bset->n_div * (2 + dim);
+
+ return size;
+}
+
+int isl_set_size(__isl_keep isl_set *set)
+{
+ int i;
+ int size = 0;
+
+ if (!set)
+ return -1;
+
+ for (i = 0; i < set->n; ++i)
+ size += isl_basic_set_size(set->p[i]);
+
+ return size;
+}
+
+/* Check if there is any lower bound (if lower == 0) and/or upper
+ * bound (if upper == 0) on the specified dim.
+ */
+static isl_bool basic_map_dim_is_bounded(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, int lower, int upper)
+{
+ int i;
+
+ if (isl_basic_map_check_range(bmap, type, pos, 1) < 0)
+ return isl_bool_error;
+
+ pos += isl_basic_map_offset(bmap, type);
+
+ for (i = 0; i < bmap->n_div; ++i) {
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ if (!isl_int_is_zero(bmap->div[i][1 + pos]))
+ return isl_bool_true;
+ }
+
+ for (i = 0; i < bmap->n_eq; ++i)
+ if (!isl_int_is_zero(bmap->eq[i][pos]))
+ return isl_bool_true;
+
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ int sgn = isl_int_sgn(bmap->ineq[i][pos]);
+ if (sgn > 0)
+ lower = 1;
+ if (sgn < 0)
+ upper = 1;
+ }
+
+ return lower && upper;
+}
+
+isl_bool isl_basic_map_dim_is_bounded(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos)
+{
+ return basic_map_dim_is_bounded(bmap, type, pos, 0, 0);
+}
+
+isl_bool isl_basic_map_dim_has_lower_bound(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos)
+{
+ return basic_map_dim_is_bounded(bmap, type, pos, 0, 1);
+}
+
+isl_bool isl_basic_map_dim_has_upper_bound(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos)
+{
+ return basic_map_dim_is_bounded(bmap, type, pos, 1, 0);
+}
+
+isl_bool isl_map_dim_is_bounded(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos)
+{
+ int i;
+
+ if (!map)
+ return isl_bool_error;
+
+ for (i = 0; i < map->n; ++i) {
+ isl_bool bounded;
+ bounded = isl_basic_map_dim_is_bounded(map->p[i], type, pos);
+ if (bounded < 0 || !bounded)
+ return bounded;
+ }
+
+ return isl_bool_true;
+}
+
+/* Return true if the specified dim is involved in both an upper bound
+ * and a lower bound.
+ */
+isl_bool isl_set_dim_is_bounded(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos)
+{
+ return isl_map_dim_is_bounded(set_to_map(set), type, pos);
+}
+
+/* Does "map" have a bound (according to "fn") for any of its basic maps?
+ */
+static isl_bool has_any_bound(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos,
+ isl_bool (*fn)(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos))
+{
+ int i;
+
+ if (!map)
+ return isl_bool_error;
+
+ for (i = 0; i < map->n; ++i) {
+ isl_bool bounded;
+ bounded = fn(map->p[i], type, pos);
+ if (bounded < 0 || bounded)
+ return bounded;
+ }
+
+ return isl_bool_false;
+}
+
+/* Return 1 if the specified dim is involved in any lower bound.
+ */
+isl_bool isl_set_dim_has_any_lower_bound(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos)
+{
+ return has_any_bound(set, type, pos,
+ &isl_basic_map_dim_has_lower_bound);
+}
+
+/* Return 1 if the specified dim is involved in any upper bound.
+ */
+isl_bool isl_set_dim_has_any_upper_bound(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos)
+{
+ return has_any_bound(set, type, pos,
+ &isl_basic_map_dim_has_upper_bound);
+}
+
+/* Does "map" have a bound (according to "fn") for all of its basic maps?
+ */
+static isl_bool has_bound(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos,
+ isl_bool (*fn)(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos))
+{
+ int i;
+
+ if (!map)
+ return isl_bool_error;
+
+ for (i = 0; i < map->n; ++i) {
+ isl_bool bounded;
+ bounded = fn(map->p[i], type, pos);
+ if (bounded < 0 || !bounded)
+ return bounded;
+ }
+
+ return isl_bool_true;
+}
+
+/* Return 1 if the specified dim has a lower bound (in each of its basic sets).
+ */
+isl_bool isl_set_dim_has_lower_bound(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos)
+{
+ return has_bound(set, type, pos, &isl_basic_map_dim_has_lower_bound);
+}
+
+/* Return 1 if the specified dim has an upper bound (in each of its basic sets).
+ */
+isl_bool isl_set_dim_has_upper_bound(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned pos)
+{
+ return has_bound(set, type, pos, &isl_basic_map_dim_has_upper_bound);
+}
+
+/* For each of the "n" variables starting at "first", determine
+ * the sign of the variable and put the results in the first "n"
+ * elements of the array "signs".
+ * Sign
+ * 1 means that the variable is non-negative
+ * -1 means that the variable is non-positive
+ * 0 means the variable attains both positive and negative values.
+ */
+isl_stat isl_basic_set_vars_get_sign(__isl_keep isl_basic_set *bset,
+ unsigned first, unsigned n, int *signs)
+{
+ isl_vec *bound = NULL;
+ struct isl_tab *tab = NULL;
+ struct isl_tab_undo *snap;
+ int i;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0 || !signs)
+ return isl_stat_error;
+
+ bound = isl_vec_alloc(bset->ctx, 1 + total);
+ tab = isl_tab_from_basic_set(bset, 0);
+ if (!bound || !tab)
+ goto error;
+
+ isl_seq_clr(bound->el, bound->size);
+ isl_int_set_si(bound->el[0], -1);
+
+ snap = isl_tab_snap(tab);
+ for (i = 0; i < n; ++i) {
+ int empty;
+
+ isl_int_set_si(bound->el[1 + first + i], -1);
+ if (isl_tab_add_ineq(tab, bound->el) < 0)
+ goto error;
+ empty = tab->empty;
+ isl_int_set_si(bound->el[1 + first + i], 0);
+ if (isl_tab_rollback(tab, snap) < 0)
+ goto error;
+
+ if (empty) {
+ signs[i] = 1;
+ continue;
+ }
+
+ isl_int_set_si(bound->el[1 + first + i], 1);
+ if (isl_tab_add_ineq(tab, bound->el) < 0)
+ goto error;
+ empty = tab->empty;
+ isl_int_set_si(bound->el[1 + first + i], 0);
+ if (isl_tab_rollback(tab, snap) < 0)
+ goto error;
+
+ signs[i] = empty ? -1 : 0;
+ }
+
+ isl_tab_free(tab);
+ isl_vec_free(bound);
+ return isl_stat_ok;
+error:
+ isl_tab_free(tab);
+ isl_vec_free(bound);
+ return isl_stat_error;
+}
+
+isl_stat isl_basic_set_dims_get_sign(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n, int *signs)
+{
+ if (!bset || !signs)
+ return isl_stat_error;
+ if (isl_basic_set_check_range(bset, type, first, n) < 0)
+ return isl_stat_error;
+
+ first += pos(bset->dim, type) - 1;
+ return isl_basic_set_vars_get_sign(bset, first, n, signs);
+}
+
+/* Is it possible for the integer division "div" to depend (possibly
+ * indirectly) on any output dimensions?
+ *
+ * If the div is undefined, then we conservatively assume that it
+ * may depend on them.
+ * Otherwise, we check if it actually depends on them or on any integer
+ * divisions that may depend on them.
+ */
+static isl_bool div_may_involve_output(__isl_keep isl_basic_map *bmap, int div)
+{
+ int i;
+ isl_size n_out, n_div;
+ unsigned o_out, o_div;
+
+ if (isl_int_is_zero(bmap->div[div][0]))
+ return isl_bool_true;
+
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ if (n_out < 0)
+ return isl_bool_error;
+ o_out = isl_basic_map_offset(bmap, isl_dim_out);
+
+ if (isl_seq_first_non_zero(bmap->div[div] + 1 + o_out, n_out) != -1)
+ return isl_bool_true;
+
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0)
+ return isl_bool_error;
+ o_div = isl_basic_map_offset(bmap, isl_dim_div);
+
+ for (i = 0; i < n_div; ++i) {
+ isl_bool may_involve;
+
+ if (isl_int_is_zero(bmap->div[div][1 + o_div + i]))
+ continue;
+ may_involve = div_may_involve_output(bmap, i);
+ if (may_involve < 0 || may_involve)
+ return may_involve;
+ }
+
+ return isl_bool_false;
+}
+
+/* Return the first integer division of "bmap" in the range
+ * [first, first + n[ that may depend on any output dimensions and
+ * that has a non-zero coefficient in "c" (where the first coefficient
+ * in "c" corresponds to integer division "first").
+ */
+static int first_div_may_involve_output(__isl_keep isl_basic_map *bmap,
+ isl_int *c, int first, int n)
+{
+ int k;
+
+ if (!bmap)
+ return -1;
+
+ for (k = first; k < first + n; ++k) {
+ isl_bool may_involve;
+
+ if (isl_int_is_zero(c[k]))
+ continue;
+ may_involve = div_may_involve_output(bmap, k);
+ if (may_involve < 0)
+ return -1;
+ if (may_involve)
+ return k;
+ }
+
+ return first + n;
+}
+
+/* Look for a pair of inequality constraints in "bmap" of the form
+ *
+ * -l + i >= 0 or i >= l
+ * and
+ * n + l - i >= 0 or i <= l + n
+ *
+ * with n < "m" and i the output dimension at position "pos".
+ * (Note that n >= 0 as otherwise the two constraints would conflict.)
+ * Furthermore, "l" is only allowed to involve parameters, input dimensions
+ * and earlier output dimensions, as well as integer divisions that do
+ * not involve any of the output dimensions.
+ *
+ * Return the index of the first inequality constraint or bmap->n_ineq
+ * if no such pair can be found.
+ */
+static int find_modulo_constraint_pair(__isl_keep isl_basic_map *bmap,
+ int pos, isl_int m)
+{
+ int i, j;
+ isl_ctx *ctx;
+ isl_size total;
+ isl_size n_div, n_out;
+ unsigned o_div, o_out;
+ int less;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (total < 0 || n_out < 0 || n_div < 0)
+ return -1;
+
+ ctx = isl_basic_map_get_ctx(bmap);
+ o_out = isl_basic_map_offset(bmap, isl_dim_out);
+ o_div = isl_basic_map_offset(bmap, isl_dim_div);
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ if (!isl_int_abs_eq(bmap->ineq[i][o_out + pos], ctx->one))
+ continue;
+ if (isl_seq_first_non_zero(bmap->ineq[i] + o_out + pos + 1,
+ n_out - (pos + 1)) != -1)
+ continue;
+ if (first_div_may_involve_output(bmap, bmap->ineq[i] + o_div,
+ 0, n_div) < n_div)
+ continue;
+ for (j = i + 1; j < bmap->n_ineq; ++j) {
+ if (!isl_int_abs_eq(bmap->ineq[j][o_out + pos],
+ ctx->one))
+ continue;
+ if (!isl_seq_is_neg(bmap->ineq[i] + 1,
+ bmap->ineq[j] + 1, total))
+ continue;
+ break;
+ }
+ if (j >= bmap->n_ineq)
+ continue;
+ isl_int_add(bmap->ineq[i][0],
+ bmap->ineq[i][0], bmap->ineq[j][0]);
+ less = isl_int_abs_lt(bmap->ineq[i][0], m);
+ isl_int_sub(bmap->ineq[i][0],
+ bmap->ineq[i][0], bmap->ineq[j][0]);
+ if (!less)
+ continue;
+ if (isl_int_is_one(bmap->ineq[i][o_out + pos]))
+ return i;
+ else
+ return j;
+ }
+
+ return bmap->n_ineq;
+}
+
+/* Return the index of the equality of "bmap" that defines
+ * the output dimension "pos" in terms of earlier dimensions.
+ * The equality may also involve integer divisions, as long
+ * as those integer divisions are defined in terms of
+ * parameters or input dimensions.
+ * In this case, *div is set to the number of integer divisions and
+ * *ineq is set to the number of inequality constraints (provided
+ * div and ineq are not NULL).
+ *
+ * The equality may also involve a single integer division involving
+ * the output dimensions (typically only output dimension "pos") as
+ * long as the coefficient of output dimension "pos" is 1 or -1 and
+ * there is a pair of constraints i >= l and i <= l + n, with i referring
+ * to output dimension "pos", l an expression involving only earlier
+ * dimensions and n smaller than the coefficient of the integer division
+ * in the equality. In this case, the output dimension can be defined
+ * in terms of a modulo expression that does not involve the integer division.
+ * *div is then set to this single integer division and
+ * *ineq is set to the index of constraint i >= l.
+ *
+ * Return bmap->n_eq if there is no such equality.
+ * Return -1 on error.
+ */
+int isl_basic_map_output_defining_equality(__isl_keep isl_basic_map *bmap,
+ int pos, int *div, int *ineq)
+{
+ int j, k, l;
+ isl_size n_div, n_out;
+ unsigned o_div, o_out;
+
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_out < 0 || n_div < 0)
+ return -1;
+
+ o_out = isl_basic_map_offset(bmap, isl_dim_out);
+ o_div = isl_basic_map_offset(bmap, isl_dim_div);
+
+ if (ineq)
+ *ineq = bmap->n_ineq;
+ if (div)
+ *div = n_div;
+ for (j = 0; j < bmap->n_eq; ++j) {
+ if (isl_int_is_zero(bmap->eq[j][o_out + pos]))
+ continue;
+ if (isl_seq_first_non_zero(bmap->eq[j] + o_out + pos + 1,
+ n_out - (pos + 1)) != -1)
+ continue;
+ k = first_div_may_involve_output(bmap, bmap->eq[j] + o_div,
+ 0, n_div);
+ if (k >= n_div)
+ return j;
+ if (!isl_int_is_one(bmap->eq[j][o_out + pos]) &&
+ !isl_int_is_negone(bmap->eq[j][o_out + pos]))
+ continue;
+ if (first_div_may_involve_output(bmap, bmap->eq[j] + o_div,
+ k + 1, n_div - (k+1)) < n_div)
+ continue;
+ l = find_modulo_constraint_pair(bmap, pos,
+ bmap->eq[j][o_div + k]);
+ if (l < 0)
+ return -1;
+ if (l >= bmap->n_ineq)
+ continue;
+ if (div)
+ *div = k;
+ if (ineq)
+ *ineq = l;
+ return j;
+ }
+
+ return bmap->n_eq;
+}
+
+/* Check if the given basic map is obviously single-valued.
+ * In particular, for each output dimension, check that there is
+ * an equality that defines the output dimension in terms of
+ * earlier dimensions.
+ */
+isl_bool isl_basic_map_plain_is_single_valued(__isl_keep isl_basic_map *bmap)
+{
+ int i;
+ isl_size n_out;
+
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ if (n_out < 0)
+ return isl_bool_error;
+
+ for (i = 0; i < n_out; ++i) {
+ int eq;
+
+ eq = isl_basic_map_output_defining_equality(bmap, i,
+ NULL, NULL);
+ if (eq < 0)
+ return isl_bool_error;
+ if (eq >= bmap->n_eq)
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+/* Check if the given basic map is single-valued.
+ * We simply compute
+ *
+ * M \circ M^-1
+ *
+ * and check if the result is a subset of the identity mapping.
+ */
+isl_bool isl_basic_map_is_single_valued(__isl_keep isl_basic_map *bmap)
+{
+ isl_space *space;
+ isl_basic_map *test;
+ isl_basic_map *id;
+ isl_bool sv;
+
+ sv = isl_basic_map_plain_is_single_valued(bmap);
+ if (sv < 0 || sv)
+ return sv;
+
+ test = isl_basic_map_reverse(isl_basic_map_copy(bmap));
+ test = isl_basic_map_apply_range(test, isl_basic_map_copy(bmap));
+
+ space = isl_basic_map_get_space(bmap);
+ space = isl_space_map_from_set(isl_space_range(space));
+ id = isl_basic_map_identity(space);
+
+ sv = isl_basic_map_is_subset(test, id);
+
+ isl_basic_map_free(test);
+ isl_basic_map_free(id);
+
+ return sv;
+}
+
+/* Check if the given map is obviously single-valued.
+ */
+isl_bool isl_map_plain_is_single_valued(__isl_keep isl_map *map)
+{
+ if (!map)
+ return isl_bool_error;
+ if (map->n == 0)
+ return isl_bool_true;
+ if (map->n >= 2)
+ return isl_bool_false;
+
+ return isl_basic_map_plain_is_single_valued(map->p[0]);
+}
+
+/* Check if the given map is single-valued.
+ * We simply compute
+ *
+ * M \circ M^-1
+ *
+ * and check if the result is a subset of the identity mapping.
+ */
+isl_bool isl_map_is_single_valued(__isl_keep isl_map *map)
+{
+ isl_space *space;
+ isl_map *test;
+ isl_map *id;
+ isl_bool sv;
+
+ sv = isl_map_plain_is_single_valued(map);
+ if (sv < 0 || sv)
+ return sv;
+
+ test = isl_map_reverse(isl_map_copy(map));
+ test = isl_map_apply_range(test, isl_map_copy(map));
+
+ space = isl_space_map_from_set(isl_space_range(isl_map_get_space(map)));
+ id = isl_map_identity(space);
+
+ sv = isl_map_is_subset(test, id);
+
+ isl_map_free(test);
+ isl_map_free(id);
+
+ return sv;
+}
+
+isl_bool isl_map_is_injective(__isl_keep isl_map *map)
+{
+ isl_bool in;
+
+ map = isl_map_copy(map);
+ map = isl_map_reverse(map);
+ in = isl_map_is_single_valued(map);
+ isl_map_free(map);
+
+ return in;
+}
+
+/* Check if the given map is obviously injective.
+ */
+isl_bool isl_map_plain_is_injective(__isl_keep isl_map *map)
+{
+ isl_bool in;
+
+ map = isl_map_copy(map);
+ map = isl_map_reverse(map);
+ in = isl_map_plain_is_single_valued(map);
+ isl_map_free(map);
+
+ return in;
+}
+
+isl_bool isl_map_is_bijective(__isl_keep isl_map *map)
+{
+ isl_bool sv;
+
+ sv = isl_map_is_single_valued(map);
+ if (sv < 0 || !sv)
+ return sv;
+
+ return isl_map_is_injective(map);
+}
+
+isl_bool isl_set_is_singleton(__isl_keep isl_set *set)
+{
+ return isl_map_is_single_valued(set_to_map(set));
+}
+
+/* Does "map" only map elements to themselves?
+ *
+ * If the domain and range spaces are different, then "map"
+ * is considered not to be an identity relation, even if it is empty.
+ * Otherwise, construct the maximal identity relation and
+ * check whether "map" is a subset of this relation.
+ */
+isl_bool isl_map_is_identity(__isl_keep isl_map *map)
+{
+ isl_map *id;
+ isl_bool equal, is_identity;
+
+ equal = isl_map_tuple_is_equal(map, isl_dim_in, map, isl_dim_out);
+ if (equal < 0 || !equal)
+ return equal;
+
+ id = isl_map_identity(isl_map_get_space(map));
+ is_identity = isl_map_is_subset(map, id);
+ isl_map_free(id);
+
+ return is_identity;
+}
+
+int isl_map_is_translation(__isl_keep isl_map *map)
+{
+ int ok;
+ isl_set *delta;
+
+ delta = isl_map_deltas(isl_map_copy(map));
+ ok = isl_set_is_singleton(delta);
+ isl_set_free(delta);
+
+ return ok;
+}
+
+static int unique(isl_int *p, unsigned pos, unsigned len)
+{
+ if (isl_seq_first_non_zero(p, pos) != -1)
+ return 0;
+ if (isl_seq_first_non_zero(p + pos + 1, len - pos - 1) != -1)
+ return 0;
+ return 1;
+}
+
+isl_bool isl_basic_set_is_box(__isl_keep isl_basic_set *bset)
+{
+ int i, j;
+ isl_size nvar, n_div;
+ unsigned ovar;
+
+ n_div = isl_basic_set_dim(bset, isl_dim_div);
+ if (n_div < 0)
+ return isl_bool_error;
+ if (n_div != 0)
+ return isl_bool_false;
+
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+ if (nvar < 0)
+ return isl_bool_error;
+ ovar = isl_space_offset(bset->dim, isl_dim_set);
+ for (j = 0; j < nvar; ++j) {
+ int lower = 0, upper = 0;
+ for (i = 0; i < bset->n_eq; ++i) {
+ if (isl_int_is_zero(bset->eq[i][1 + ovar + j]))
+ continue;
+ if (!unique(bset->eq[i] + 1 + ovar, j, nvar))
+ return isl_bool_false;
+ break;
+ }
+ if (i < bset->n_eq)
+ continue;
+ for (i = 0; i < bset->n_ineq; ++i) {
+ if (isl_int_is_zero(bset->ineq[i][1 + ovar + j]))
+ continue;
+ if (!unique(bset->ineq[i] + 1 + ovar, j, nvar))
+ return isl_bool_false;
+ if (isl_int_is_pos(bset->ineq[i][1 + ovar + j]))
+ lower = 1;
+ else
+ upper = 1;
+ }
+ if (!lower || !upper)
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+isl_bool isl_set_is_box(__isl_keep isl_set *set)
+{
+ if (!set)
+ return isl_bool_error;
+ if (set->n != 1)
+ return isl_bool_false;
+
+ return isl_basic_set_is_box(set->p[0]);
+}
+
+isl_bool isl_basic_set_is_wrapping(__isl_keep isl_basic_set *bset)
+{
+ if (!bset)
+ return isl_bool_error;
+
+ return isl_space_is_wrapping(bset->dim);
+}
+
+isl_bool isl_set_is_wrapping(__isl_keep isl_set *set)
+{
+ if (!set)
+ return isl_bool_error;
+
+ return isl_space_is_wrapping(set->dim);
+}
+
+/* Modify the space of "map" through a call to "change".
+ * If "can_change" is set (not NULL), then first call it to check
+ * if the modification is allowed, printing the error message "cannot_change"
+ * if it is not.
+ */
+static __isl_give isl_map *isl_map_change_space(__isl_take isl_map *map,
+ isl_bool (*can_change)(__isl_keep isl_map *map),
+ const char *cannot_change,
+ __isl_give isl_space *(*change)(__isl_take isl_space *space))
+{
+ isl_bool ok;
+ isl_space *space;
+
+ if (!map)
+ return NULL;
+
+ ok = can_change ? can_change(map) : isl_bool_true;
+ if (ok < 0)
+ return isl_map_free(map);
+ if (!ok)
+ isl_die(isl_map_get_ctx(map), isl_error_invalid, cannot_change,
+ return isl_map_free(map));
+
+ space = change(isl_map_get_space(map));
+ map = isl_map_reset_space(map, space);
+
+ return map;
+}
+
+/* Is the domain of "map" a wrapped relation?
+ */
+isl_bool isl_map_domain_is_wrapping(__isl_keep isl_map *map)
+{
+ if (!map)
+ return isl_bool_error;
+
+ return isl_space_domain_is_wrapping(map->dim);
+}
+
+/* Does "map" have a wrapped relation in both domain and range?
+ */
+isl_bool isl_map_is_product(__isl_keep isl_map *map)
+{
+ return isl_space_is_product(isl_map_peek_space(map));
+}
+
+/* Is the range of "map" a wrapped relation?
+ */
+isl_bool isl_map_range_is_wrapping(__isl_keep isl_map *map)
+{
+ if (!map)
+ return isl_bool_error;
+
+ return isl_space_range_is_wrapping(map->dim);
+}
+
+__isl_give isl_basic_set *isl_basic_map_wrap(__isl_take isl_basic_map *bmap)
+{
+ isl_space *space;
+
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_wrap(space);
+ bmap = isl_basic_map_restore_space(bmap, space);
+
+ bmap = isl_basic_map_finalize(bmap);
+
+ return bset_from_bmap(bmap);
+}
+
+/* Given a map A -> B, return the set (A -> B).
+ */
+__isl_give isl_set *isl_map_wrap(__isl_take isl_map *map)
+{
+ return isl_map_change_space(map, NULL, NULL, &isl_space_wrap);
+}
+
+__isl_give isl_basic_map *isl_basic_set_unwrap(__isl_take isl_basic_set *bset)
+{
+ bset = isl_basic_set_cow(bset);
+ if (!bset)
+ return NULL;
+
+ bset->dim = isl_space_unwrap(bset->dim);
+ if (!bset->dim)
+ goto error;
+
+ bset = isl_basic_set_finalize(bset);
+
+ return bset_to_bmap(bset);
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Given a set (A -> B), return the map A -> B.
+ * Error out if "set" is not of the form (A -> B).
+ */
+__isl_give isl_map *isl_set_unwrap(__isl_take isl_set *set)
+{
+ return isl_map_change_space(set, &isl_set_is_wrapping,
+ "not a wrapping set", &isl_space_unwrap);
+}
+
+__isl_give isl_basic_map *isl_basic_map_reset(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type)
+{
+ isl_space *space;
+
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_reset(space, type);
+ bmap = isl_basic_map_restore_space(bmap, space);
+
+ bmap = isl_basic_map_mark_final(bmap);
+
+ return bmap;
+}
+
+__isl_give isl_map *isl_map_reset(__isl_take isl_map *map,
+ enum isl_dim_type type)
+{
+ int i;
+ isl_space *space;
+
+ if (!map)
+ return NULL;
+
+ if (!isl_space_is_named_or_nested(map->dim, type))
+ return map;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_reset(map->p[i], type);
+ if (!map->p[i])
+ goto error;
+ }
+
+ space = isl_map_take_space(map);
+ space = isl_space_reset(space, type);
+ map = isl_map_restore_space(map, space);
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_flatten(__isl_take isl_basic_map *bmap)
+{
+ isl_space *space;
+
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_flatten(space);
+ bmap = isl_basic_map_restore_space(bmap, space);
+
+ bmap = isl_basic_map_mark_final(bmap);
+
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_flatten(__isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(isl_basic_map_flatten(bset_to_bmap(bset)));
+}
+
+__isl_give isl_basic_map *isl_basic_map_flatten_domain(
+ __isl_take isl_basic_map *bmap)
+{
+ isl_space *space;
+
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_flatten_domain(space);
+ bmap = isl_basic_map_restore_space(bmap, space);
+
+ bmap = isl_basic_map_mark_final(bmap);
+
+ return bmap;
+}
+
+__isl_give isl_basic_map *isl_basic_map_flatten_range(
+ __isl_take isl_basic_map *bmap)
+{
+ isl_space *space;
+
+ space = isl_basic_map_take_space(bmap);
+ space = isl_space_flatten_range(space);
+ bmap = isl_basic_map_restore_space(bmap, space);
+
+ bmap = isl_basic_map_mark_final(bmap);
+
+ return bmap;
+}
+
+/* Remove any internal structure from the spaces of domain and range of "map".
+ */
+__isl_give isl_map *isl_map_flatten(__isl_take isl_map *map)
+{
+ if (!map)
+ return NULL;
+
+ if (!map->dim->nested[0] && !map->dim->nested[1])
+ return map;
+
+ return isl_map_change_space(map, NULL, NULL, &isl_space_flatten);
+}
+
+__isl_give isl_set *isl_set_flatten(__isl_take isl_set *set)
+{
+ return set_from_map(isl_map_flatten(set_to_map(set)));
+}
+
+__isl_give isl_map *isl_set_flatten_map(__isl_take isl_set *set)
+{
+ isl_space *space, *flat_space;
+ isl_map *map;
+
+ space = isl_set_get_space(set);
+ flat_space = isl_space_flatten(isl_space_copy(space));
+ map = isl_map_identity(isl_space_join(isl_space_reverse(space),
+ flat_space));
+ map = isl_map_intersect_domain(map, set);
+
+ return map;
+}
+
+/* Remove any internal structure from the space of the domain of "map".
+ */
+__isl_give isl_map *isl_map_flatten_domain(__isl_take isl_map *map)
+{
+ if (!map)
+ return NULL;
+
+ if (!map->dim->nested[0])
+ return map;
+
+ return isl_map_change_space(map, NULL, NULL, &isl_space_flatten_domain);
+}
+
+/* Remove any internal structure from the space of the range of "map".
+ */
+__isl_give isl_map *isl_map_flatten_range(__isl_take isl_map *map)
+{
+ if (!map)
+ return NULL;
+
+ if (!map->dim->nested[1])
+ return map;
+
+ return isl_map_change_space(map, NULL, NULL, &isl_space_flatten_range);
+}
+
+/* Reorder the dimensions of "bmap" according to the given dim_map
+ * and set the dimension specification to "space" and
+ * perform Gaussian elimination on the result.
+ */
+__isl_give isl_basic_map *isl_basic_map_realign(__isl_take isl_basic_map *bmap,
+ __isl_take isl_space *space, __isl_take struct isl_dim_map *dim_map)
+{
+ isl_basic_map *res;
+ unsigned flags;
+ isl_size n_div;
+
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0 || !space || !dim_map)
+ goto error;
+
+ flags = bmap->flags;
+ ISL_FL_CLR(flags, ISL_BASIC_MAP_FINAL);
+ ISL_FL_CLR(flags, ISL_BASIC_MAP_SORTED);
+ ISL_FL_CLR(flags, ISL_BASIC_MAP_NORMALIZED_DIVS);
+ res = isl_basic_map_alloc_space(space, n_div, bmap->n_eq, bmap->n_ineq);
+ res = isl_basic_map_add_constraints_dim_map(res, bmap, dim_map);
+ if (res)
+ res->flags = flags;
+ res = isl_basic_map_gauss(res, NULL);
+ res = isl_basic_map_finalize(res);
+ return res;
+error:
+ isl_dim_map_free(dim_map);
+ isl_basic_map_free(bmap);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Reorder the dimensions of "map" according to given reordering.
+ */
+__isl_give isl_map *isl_map_realign(__isl_take isl_map *map,
+ __isl_take isl_reordering *r)
+{
+ int i;
+ struct isl_dim_map *dim_map;
+
+ map = isl_map_cow(map);
+ dim_map = isl_dim_map_from_reordering(r);
+ if (!map || !r || !dim_map)
+ goto error;
+
+ for (i = 0; i < map->n; ++i) {
+ struct isl_dim_map *dim_map_i;
+ isl_space *space;
+
+ dim_map_i = isl_dim_map_extend(dim_map, map->p[i]);
+
+ space = isl_reordering_get_space(r);
+ map->p[i] = isl_basic_map_realign(map->p[i], space, dim_map_i);
+
+ if (!map->p[i])
+ goto error;
+ }
+
+ map = isl_map_reset_space(map, isl_reordering_get_space(r));
+ map = isl_map_unmark_normalized(map);
+
+ isl_reordering_free(r);
+ isl_dim_map_free(dim_map);
+ return map;
+error:
+ isl_dim_map_free(dim_map);
+ isl_map_free(map);
+ isl_reordering_free(r);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_realign(__isl_take isl_set *set,
+ __isl_take isl_reordering *r)
+{
+ return set_from_map(isl_map_realign(set_to_map(set), r));
+}
+
+__isl_give isl_map *isl_map_align_params(__isl_take isl_map *map,
+ __isl_take isl_space *model)
+{
+ isl_ctx *ctx;
+ isl_bool aligned;
+
+ if (!map || !model)
+ goto error;
+
+ ctx = isl_space_get_ctx(model);
+ if (!isl_space_has_named_params(model))
+ isl_die(ctx, isl_error_invalid,
+ "model has unnamed parameters", goto error);
+ if (isl_map_check_named_params(map) < 0)
+ goto error;
+ aligned = isl_map_space_has_equal_params(map, model);
+ if (aligned < 0)
+ goto error;
+ if (!aligned) {
+ isl_reordering *exp;
+
+ exp = isl_parameter_alignment_reordering(map->dim, model);
+ exp = isl_reordering_extend_space(exp, isl_map_get_space(map));
+ map = isl_map_realign(map, exp);
+ }
+
+ isl_space_free(model);
+ return map;
+error:
+ isl_space_free(model);
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_align_params(__isl_take isl_set *set,
+ __isl_take isl_space *model)
+{
+ return isl_map_align_params(set, model);
+}
+
+/* Align the parameters of "bmap" to those of "model", introducing
+ * additional parameters if needed.
+ */
+__isl_give isl_basic_map *isl_basic_map_align_params(
+ __isl_take isl_basic_map *bmap, __isl_take isl_space *model)
+{
+ isl_ctx *ctx;
+ isl_bool equal_params;
+
+ if (!bmap || !model)
+ goto error;
+
+ ctx = isl_space_get_ctx(model);
+ if (!isl_space_has_named_params(model))
+ isl_die(ctx, isl_error_invalid,
+ "model has unnamed parameters", goto error);
+ if (isl_basic_map_check_named_params(bmap) < 0)
+ goto error;
+ equal_params = isl_space_has_equal_params(bmap->dim, model);
+ if (equal_params < 0)
+ goto error;
+ if (!equal_params) {
+ isl_reordering *exp;
+ struct isl_dim_map *dim_map;
+
+ exp = isl_parameter_alignment_reordering(bmap->dim, model);
+ exp = isl_reordering_extend_space(exp,
+ isl_basic_map_get_space(bmap));
+ dim_map = isl_dim_map_from_reordering(exp);
+ bmap = isl_basic_map_realign(bmap,
+ isl_reordering_get_space(exp),
+ isl_dim_map_extend(dim_map, bmap));
+ isl_reordering_free(exp);
+ isl_dim_map_free(dim_map);
+ }
+
+ isl_space_free(model);
+ return bmap;
+error:
+ isl_space_free(model);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Do "bset" and "space" have the same parameters?
+ */
+isl_bool isl_basic_set_space_has_equal_params(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_space *space)
+{
+ isl_space *bset_space;
+
+ bset_space = isl_basic_set_peek_space(bset);
+ return isl_space_has_equal_params(bset_space, space);
+}
+
+/* Do "map" and "space" have the same parameters?
+ */
+isl_bool isl_map_space_has_equal_params(__isl_keep isl_map *map,
+ __isl_keep isl_space *space)
+{
+ isl_space *map_space;
+
+ map_space = isl_map_peek_space(map);
+ return isl_space_has_equal_params(map_space, space);
+}
+
+/* Do "set" and "space" have the same parameters?
+ */
+isl_bool isl_set_space_has_equal_params(__isl_keep isl_set *set,
+ __isl_keep isl_space *space)
+{
+ return isl_map_space_has_equal_params(set_to_map(set), space);
+}
+
+/* Align the parameters of "bset" to those of "model", introducing
+ * additional parameters if needed.
+ */
+__isl_give isl_basic_set *isl_basic_set_align_params(
+ __isl_take isl_basic_set *bset, __isl_take isl_space *model)
+{
+ return isl_basic_map_align_params(bset, model);
+}
+
+/* Drop all parameters not referenced by "map".
+ */
+__isl_give isl_map *isl_map_drop_unused_params(__isl_take isl_map *map)
+{
+ int i;
+ isl_size n;
+
+ n = isl_map_dim(map, isl_dim_param);
+ if (isl_map_check_named_params(map) < 0 || n < 0)
+ return isl_map_free(map);
+
+ for (i = n - 1; i >= 0; i--) {
+ isl_bool involves;
+
+ involves = isl_map_involves_dims(map, isl_dim_param, i, 1);
+ if (involves < 0)
+ return isl_map_free(map);
+ if (!involves)
+ map = isl_map_project_out(map, isl_dim_param, i, 1);
+ }
+
+ return map;
+}
+
+/* Drop all parameters not referenced by "set".
+ */
+__isl_give isl_set *isl_set_drop_unused_params(
+ __isl_take isl_set *set)
+{
+ return set_from_map(isl_map_drop_unused_params(set_to_map(set)));
+}
+
+/* Drop all parameters not referenced by "bmap".
+ */
+__isl_give isl_basic_map *isl_basic_map_drop_unused_params(
+ __isl_take isl_basic_map *bmap)
+{
+ isl_size nparam;
+ int i;
+
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ if (nparam < 0 || isl_basic_map_check_named_params(bmap) < 0)
+ return isl_basic_map_free(bmap);
+
+ for (i = nparam - 1; i >= 0; i--) {
+ isl_bool involves;
+
+ involves = isl_basic_map_involves_dims(bmap,
+ isl_dim_param, i, 1);
+ if (involves < 0)
+ return isl_basic_map_free(bmap);
+ if (!involves)
+ bmap = isl_basic_map_drop(bmap, isl_dim_param, i, 1);
+ }
+
+ return bmap;
+}
+
+/* Drop all parameters not referenced by "bset".
+ */
+__isl_give isl_basic_set *isl_basic_set_drop_unused_params(
+ __isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(isl_basic_map_drop_unused_params(
+ bset_to_bmap(bset)));
+}
+
+/* Given a tuple of identifiers "tuple" in a space that corresponds
+ * to that of "set", if any of those identifiers appear as parameters
+ * in "set", then equate those parameters with the corresponding
+ * set dimensions and project out the parameters.
+ * The result therefore has no such parameters.
+ */
+static __isl_give isl_set *equate_params(__isl_take isl_set *set,
+ __isl_keep isl_multi_id *tuple)
+{
+ int i;
+ isl_size n;
+ isl_space *set_space, *tuple_space;
+
+ set_space = isl_set_peek_space(set);
+ tuple_space = isl_multi_id_peek_space(tuple);
+ if (isl_space_check_equal_tuples(tuple_space, set_space) < 0)
+ return isl_set_free(set);
+ n = isl_multi_id_size(tuple);
+ if (n < 0)
+ return isl_set_free(set);
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+ int pos;
+
+ id = isl_multi_id_get_at(tuple, i);
+ if (!id)
+ return isl_set_free(set);
+ pos = isl_set_find_dim_by_id(set, isl_dim_param, id);
+ isl_id_free(id);
+ if (pos < 0)
+ continue;
+ set = isl_set_equate(set, isl_dim_param, pos, isl_dim_set, i);
+ set = isl_set_project_out(set, isl_dim_param, pos, 1);
+ }
+ return set;
+}
+
+/* Bind the set dimensions of "set" to parameters with identifiers
+ * specified by "tuple", living in the same space as "set".
+ *
+ * If no parameters with these identifiers appear in "set" already,
+ * then the set dimensions are simply reinterpreted as parameters.
+ * Otherwise, the parameters are first equated to the corresponding
+ * set dimensions.
+ */
+__isl_give isl_set *isl_set_bind(__isl_take isl_set *set,
+ __isl_take isl_multi_id *tuple)
+{
+ isl_space *space;
+
+ set = equate_params(set, tuple);
+ space = isl_set_get_space(set);
+ space = isl_space_bind_set(space, tuple);
+ isl_multi_id_free(tuple);
+ set = isl_set_reset_space(set, space);
+
+ return set;
+}
+
+/* Given a tuple of identifiers "tuple" in a space that corresponds
+ * to the domain of "map", if any of those identifiers appear as parameters
+ * in "map", then equate those parameters with the corresponding
+ * input dimensions and project out the parameters.
+ * The result therefore has no such parameters.
+ */
+static __isl_give isl_map *map_equate_params(__isl_take isl_map *map,
+ __isl_keep isl_multi_id *tuple)
+{
+ int i;
+ isl_size n;
+ isl_space *map_space, *tuple_space;
+
+ map_space = isl_map_peek_space(map);
+ tuple_space = isl_multi_id_peek_space(tuple);
+ if (isl_space_check_domain_tuples(tuple_space, map_space) < 0)
+ return isl_map_free(map);
+ n = isl_multi_id_size(tuple);
+ if (n < 0)
+ return isl_map_free(map);
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+ int pos;
+
+ id = isl_multi_id_get_at(tuple, i);
+ if (!id)
+ return isl_map_free(map);
+ pos = isl_map_find_dim_by_id(map, isl_dim_param, id);
+ isl_id_free(id);
+ if (pos < 0)
+ continue;
+ map = isl_map_equate(map, isl_dim_param, pos, isl_dim_in, i);
+ map = isl_map_project_out(map, isl_dim_param, pos, 1);
+ }
+ return map;
+}
+
+/* Bind the input dimensions of "map" to parameters with identifiers
+ * specified by "tuple", living in the domain space of "map".
+ *
+ * If no parameters with these identifiers appear in "map" already,
+ * then the input dimensions are simply reinterpreted as parameters.
+ * Otherwise, the parameters are first equated to the corresponding
+ * input dimensions.
+ */
+__isl_give isl_set *isl_map_bind_domain(__isl_take isl_map *map,
+ __isl_take isl_multi_id *tuple)
+{
+ isl_space *space;
+ isl_set *set;
+
+ map = map_equate_params(map, tuple);
+ space = isl_map_get_space(map);
+ space = isl_space_bind_map_domain(space, tuple);
+ isl_multi_id_free(tuple);
+ set = set_from_map(isl_map_reset_space(map, space));
+
+ return set;
+}
+
+/* Bind the output dimensions of "map" to parameters with identifiers
+ * specified by "tuple", living in the range space of "map".
+ *
+ * Since binding is more easily implemented on the domain,
+ * bind the input dimensions of the inverse of "map".
+ */
+__isl_give isl_set *isl_map_bind_range(__isl_take isl_map *map,
+ __isl_take isl_multi_id *tuple)
+{
+ return isl_map_bind_domain(isl_map_reverse(map), tuple);
+}
+
+/* Insert a domain corresponding to "tuple"
+ * into the nullary or unary relation "set".
+ * The result has an extra initial tuple and is therefore
+ * either a unary or binary relation.
+ * Any parameters with identifiers in "tuple" are reinterpreted
+ * as the corresponding domain dimensions.
+ */
+static __isl_give isl_map *unbind_params_insert_domain(
+ __isl_take isl_set *set, __isl_take isl_multi_id *tuple)
+{
+ isl_space *space;
+ isl_reordering *r;
+
+ space = isl_set_peek_space(set);
+ r = isl_reordering_unbind_params_insert_domain(space, tuple);
+ isl_multi_id_free(tuple);
+
+ return isl_map_realign(set_to_map(set), r);
+}
+
+/* Construct a set with "tuple" as domain from the parameter domain "set".
+ * Any parameters with identifiers in "tuple" are reinterpreted
+ * as the corresponding set dimensions.
+ */
+__isl_give isl_set *isl_set_unbind_params(__isl_take isl_set *set,
+ __isl_take isl_multi_id *tuple)
+{
+ isl_bool is_params;
+
+ is_params = isl_set_is_params(set);
+ if (is_params < 0)
+ set = isl_set_free(set);
+ else if (!is_params)
+ isl_die(isl_set_get_ctx(set), isl_error_invalid,
+ "expecting parameter domain", set = isl_set_free(set));
+ return set_from_map(unbind_params_insert_domain(set, tuple));
+}
+
+/* Check that "set" is a proper set, i.e., that it is not a parameter domain.
+ */
+static isl_stat isl_set_check_is_set(__isl_keep isl_set *set)
+{
+ isl_bool is_params;
+
+ is_params = isl_set_is_params(set);
+ if (is_params < 0)
+ return isl_stat_error;
+ else if (is_params)
+ isl_die(isl_set_get_ctx(set), isl_error_invalid,
+ "expecting proper set", return isl_stat_error);
+
+ return isl_stat_ok;
+}
+
+/* Construct a map with "domain" as domain and "set" as range.
+ * Any parameters with identifiers in "domain" are reinterpreted
+ * as the corresponding domain dimensions.
+ */
+__isl_give isl_map *isl_set_unbind_params_insert_domain(
+ __isl_take isl_set *set, __isl_take isl_multi_id *domain)
+{
+ if (isl_set_check_is_set(set) < 0)
+ set = isl_set_free(set);
+ return unbind_params_insert_domain(set, domain);
+}
+
+/* Construct a map with "domain" as domain and "set" as range.
+ */
+__isl_give isl_map *isl_set_insert_domain(__isl_take isl_set *set,
+ __isl_take isl_space *domain)
+{
+ isl_size dim;
+ isl_space *space;
+ isl_map *map;
+
+ if (isl_set_check_is_set(set) < 0 || isl_space_check_is_set(domain) < 0)
+ domain = isl_space_free(domain);
+ dim = isl_space_dim(domain, isl_dim_set);
+ if (dim < 0)
+ domain = isl_space_free(domain);
+ space = isl_set_get_space(set);
+ domain = isl_space_replace_params(domain, space);
+ space = isl_space_map_from_domain_and_range(domain, space);
+
+ map = isl_map_from_range(set);
+ map = isl_map_add_dims(map, isl_dim_in, dim);
+ map = isl_map_reset_space(map, space);
+
+ return map;
+}
+
+__isl_give isl_mat *isl_basic_map_equalities_matrix(
+ __isl_keep isl_basic_map *bmap, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3,
+ enum isl_dim_type c4, enum isl_dim_type c5)
+{
+ enum isl_dim_type c[5] = { c1, c2, c3, c4, c5 };
+ struct isl_mat *mat;
+ int i, j, k;
+ int pos;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return NULL;
+ mat = isl_mat_alloc(bmap->ctx, bmap->n_eq, total + 1);
+ if (!mat)
+ return NULL;
+ for (i = 0; i < bmap->n_eq; ++i)
+ for (j = 0, pos = 0; j < 5; ++j) {
+ int off = isl_basic_map_offset(bmap, c[j]);
+ isl_size dim = isl_basic_map_dim(bmap, c[j]);
+ if (dim < 0)
+ return isl_mat_free(mat);
+ for (k = 0; k < dim; ++k) {
+ isl_int_set(mat->row[i][pos],
+ bmap->eq[i][off + k]);
+ ++pos;
+ }
+ }
+
+ return mat;
+}
+
+__isl_give isl_mat *isl_basic_map_inequalities_matrix(
+ __isl_keep isl_basic_map *bmap, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3,
+ enum isl_dim_type c4, enum isl_dim_type c5)
+{
+ enum isl_dim_type c[5] = { c1, c2, c3, c4, c5 };
+ struct isl_mat *mat;
+ int i, j, k;
+ int pos;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return NULL;
+ mat = isl_mat_alloc(bmap->ctx, bmap->n_ineq, total + 1);
+ if (!mat)
+ return NULL;
+ for (i = 0; i < bmap->n_ineq; ++i)
+ for (j = 0, pos = 0; j < 5; ++j) {
+ int off = isl_basic_map_offset(bmap, c[j]);
+ isl_size dim = isl_basic_map_dim(bmap, c[j]);
+ if (dim < 0)
+ return isl_mat_free(mat);
+ for (k = 0; k < dim; ++k) {
+ isl_int_set(mat->row[i][pos],
+ bmap->ineq[i][off + k]);
+ ++pos;
+ }
+ }
+
+ return mat;
+}
+
+__isl_give isl_basic_map *isl_basic_map_from_constraint_matrices(
+ __isl_take isl_space *space,
+ __isl_take isl_mat *eq, __isl_take isl_mat *ineq, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3,
+ enum isl_dim_type c4, enum isl_dim_type c5)
+{
+ enum isl_dim_type c[5] = { c1, c2, c3, c4, c5 };
+ isl_basic_map *bmap = NULL;
+ isl_size dim;
+ unsigned total;
+ unsigned extra;
+ int i, j, k, l;
+ int pos;
+
+ dim = isl_space_dim(space, isl_dim_all);
+ if (dim < 0 || !eq || !ineq)
+ goto error;
+
+ if (eq->n_col != ineq->n_col)
+ isl_die(space->ctx, isl_error_invalid,
+ "equalities and inequalities matrices should have "
+ "same number of columns", goto error);
+
+ total = 1 + dim;
+
+ if (eq->n_col < total)
+ isl_die(space->ctx, isl_error_invalid,
+ "number of columns too small", goto error);
+
+ extra = eq->n_col - total;
+
+ bmap = isl_basic_map_alloc_space(isl_space_copy(space), extra,
+ eq->n_row, ineq->n_row);
+ if (!bmap)
+ goto error;
+ for (i = 0; i < extra; ++i) {
+ k = isl_basic_map_alloc_div(bmap);
+ if (k < 0)
+ goto error;
+ isl_int_set_si(bmap->div[k][0], 0);
+ }
+ for (i = 0; i < eq->n_row; ++i) {
+ l = isl_basic_map_alloc_equality(bmap);
+ if (l < 0)
+ goto error;
+ for (j = 0, pos = 0; j < 5; ++j) {
+ int off = isl_basic_map_offset(bmap, c[j]);
+ isl_size dim = isl_basic_map_dim(bmap, c[j]);
+ if (dim < 0)
+ goto error;
+ for (k = 0; k < dim; ++k) {
+ isl_int_set(bmap->eq[l][off + k],
+ eq->row[i][pos]);
+ ++pos;
+ }
+ }
+ }
+ for (i = 0; i < ineq->n_row; ++i) {
+ l = isl_basic_map_alloc_inequality(bmap);
+ if (l < 0)
+ goto error;
+ for (j = 0, pos = 0; j < 5; ++j) {
+ int off = isl_basic_map_offset(bmap, c[j]);
+ isl_size dim = isl_basic_map_dim(bmap, c[j]);
+ if (dim < 0)
+ goto error;
+ for (k = 0; k < dim; ++k) {
+ isl_int_set(bmap->ineq[l][off + k],
+ ineq->row[i][pos]);
+ ++pos;
+ }
+ }
+ }
+
+ isl_space_free(space);
+ isl_mat_free(eq);
+ isl_mat_free(ineq);
+
+ bmap = isl_basic_map_simplify(bmap);
+ return isl_basic_map_finalize(bmap);
+error:
+ isl_space_free(space);
+ isl_mat_free(eq);
+ isl_mat_free(ineq);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_mat *isl_basic_set_equalities_matrix(
+ __isl_keep isl_basic_set *bset, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4)
+{
+ return isl_basic_map_equalities_matrix(bset_to_bmap(bset),
+ c1, c2, c3, c4, isl_dim_in);
+}
+
+__isl_give isl_mat *isl_basic_set_inequalities_matrix(
+ __isl_keep isl_basic_set *bset, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4)
+{
+ return isl_basic_map_inequalities_matrix(bset_to_bmap(bset),
+ c1, c2, c3, c4, isl_dim_in);
+}
+
+__isl_give isl_basic_set *isl_basic_set_from_constraint_matrices(
+ __isl_take isl_space *space,
+ __isl_take isl_mat *eq, __isl_take isl_mat *ineq, enum isl_dim_type c1,
+ enum isl_dim_type c2, enum isl_dim_type c3, enum isl_dim_type c4)
+{
+ isl_basic_map *bmap;
+ bmap = isl_basic_map_from_constraint_matrices(space, eq, ineq,
+ c1, c2, c3, c4, isl_dim_in);
+ return bset_from_bmap(bmap);
+}
+
+isl_bool isl_basic_map_can_zip(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return isl_bool_error;
+
+ return isl_space_can_zip(bmap->dim);
+}
+
+isl_bool isl_map_can_zip(__isl_keep isl_map *map)
+{
+ if (!map)
+ return isl_bool_error;
+
+ return isl_space_can_zip(map->dim);
+}
+
+/* Given a basic map (A -> B) -> (C -> D), return the corresponding basic map
+ * (A -> C) -> (B -> D).
+ */
+__isl_give isl_basic_map *isl_basic_map_zip(__isl_take isl_basic_map *bmap)
+{
+ unsigned pos;
+ isl_size n_in;
+ isl_size n1;
+ isl_size n2;
+
+ if (!bmap)
+ return NULL;
+
+ if (!isl_basic_map_can_zip(bmap))
+ isl_die(bmap->ctx, isl_error_invalid,
+ "basic map cannot be zipped", goto error);
+ n_in = isl_space_dim(bmap->dim->nested[0], isl_dim_in);
+ n1 = isl_space_dim(bmap->dim->nested[0], isl_dim_out);
+ n2 = isl_space_dim(bmap->dim->nested[1], isl_dim_in);
+ if (n_in < 0 || n1 < 0 || n2 < 0)
+ return isl_basic_map_free(bmap);
+ pos = isl_basic_map_offset(bmap, isl_dim_in) + n_in;
+ bmap = isl_basic_map_cow(bmap);
+ bmap = isl_basic_map_swap_vars(bmap, pos, n1, n2);
+ if (!bmap)
+ return NULL;
+ bmap->dim = isl_space_zip(bmap->dim);
+ if (!bmap->dim)
+ goto error;
+ bmap = isl_basic_map_mark_final(bmap);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Given a map (A -> B) -> (C -> D), return the corresponding map
+ * (A -> C) -> (B -> D).
+ */
+__isl_give isl_map *isl_map_zip(__isl_take isl_map *map)
+{
+ if (!map)
+ return NULL;
+
+ if (!isl_map_can_zip(map))
+ isl_die(map->ctx, isl_error_invalid, "map cannot be zipped",
+ goto error);
+
+ return isl_map_transform(map, &isl_space_zip, &isl_basic_map_zip);
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Can we apply isl_basic_map_curry to "bmap"?
+ * That is, does it have a nested relation in its domain?
+ */
+isl_bool isl_basic_map_can_curry(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return isl_bool_error;
+
+ return isl_space_can_curry(bmap->dim);
+}
+
+/* Can we apply isl_map_curry to "map"?
+ * That is, does it have a nested relation in its domain?
+ */
+isl_bool isl_map_can_curry(__isl_keep isl_map *map)
+{
+ if (!map)
+ return isl_bool_error;
+
+ return isl_space_can_curry(map->dim);
+}
+
+/* Given a basic map (A -> B) -> C, return the corresponding basic map
+ * A -> (B -> C).
+ */
+__isl_give isl_basic_map *isl_basic_map_curry(__isl_take isl_basic_map *bmap)
+{
+
+ if (!bmap)
+ return NULL;
+
+ if (!isl_basic_map_can_curry(bmap))
+ isl_die(bmap->ctx, isl_error_invalid,
+ "basic map cannot be curried", goto error);
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+ bmap->dim = isl_space_curry(bmap->dim);
+ if (!bmap->dim)
+ goto error;
+ bmap = isl_basic_map_mark_final(bmap);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Given a map (A -> B) -> C, return the corresponding map
+ * A -> (B -> C).
+ */
+__isl_give isl_map *isl_map_curry(__isl_take isl_map *map)
+{
+ return isl_map_change_space(map, &isl_map_can_curry,
+ "map cannot be curried", &isl_space_curry);
+}
+
+/* Can isl_map_range_curry be applied to "map"?
+ * That is, does it have a nested relation in its range,
+ * the domain of which is itself a nested relation?
+ */
+isl_bool isl_map_can_range_curry(__isl_keep isl_map *map)
+{
+ if (!map)
+ return isl_bool_error;
+
+ return isl_space_can_range_curry(map->dim);
+}
+
+/* Given a map A -> ((B -> C) -> D), return the corresponding map
+ * A -> (B -> (C -> D)).
+ */
+__isl_give isl_map *isl_map_range_curry(__isl_take isl_map *map)
+{
+ return isl_map_change_space(map, &isl_map_can_range_curry,
+ "map range cannot be curried",
+ &isl_space_range_curry);
+}
+
+/* Can we apply isl_basic_map_uncurry to "bmap"?
+ * That is, does it have a nested relation in its domain?
+ */
+isl_bool isl_basic_map_can_uncurry(__isl_keep isl_basic_map *bmap)
+{
+ if (!bmap)
+ return isl_bool_error;
+
+ return isl_space_can_uncurry(bmap->dim);
+}
+
+/* Can we apply isl_map_uncurry to "map"?
+ * That is, does it have a nested relation in its domain?
+ */
+isl_bool isl_map_can_uncurry(__isl_keep isl_map *map)
+{
+ if (!map)
+ return isl_bool_error;
+
+ return isl_space_can_uncurry(map->dim);
+}
+
+/* Given a basic map A -> (B -> C), return the corresponding basic map
+ * (A -> B) -> C.
+ */
+__isl_give isl_basic_map *isl_basic_map_uncurry(__isl_take isl_basic_map *bmap)
+{
+
+ if (!bmap)
+ return NULL;
+
+ if (!isl_basic_map_can_uncurry(bmap))
+ isl_die(bmap->ctx, isl_error_invalid,
+ "basic map cannot be uncurried",
+ return isl_basic_map_free(bmap));
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+ bmap->dim = isl_space_uncurry(bmap->dim);
+ if (!bmap->dim)
+ return isl_basic_map_free(bmap);
+ bmap = isl_basic_map_mark_final(bmap);
+ return bmap;
+}
+
+/* Given a map A -> (B -> C), return the corresponding map
+ * (A -> B) -> C.
+ */
+__isl_give isl_map *isl_map_uncurry(__isl_take isl_map *map)
+{
+ return isl_map_change_space(map, &isl_map_can_uncurry,
+ "map cannot be uncurried", &isl_space_uncurry);
+}
+
+__isl_give isl_set *isl_set_equate(__isl_take isl_set *set,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ return isl_map_equate(set, type1, pos1, type2, pos2);
+}
+
+/* Construct a basic map where the given dimensions are equal to each other.
+ */
+static __isl_give isl_basic_map *equator(__isl_take isl_space *space,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ isl_basic_map *bmap = NULL;
+ int i;
+ isl_size total;
+
+ total = isl_space_dim(space, isl_dim_all);
+ if (total < 0 ||
+ isl_space_check_range(space, type1, pos1, 1) < 0 ||
+ isl_space_check_range(space, type2, pos2, 1) < 0)
+ goto error;
+
+ if (type1 == type2 && pos1 == pos2)
+ return isl_basic_map_universe(space);
+
+ bmap = isl_basic_map_alloc_space(isl_space_copy(space), 0, 1, 0);
+ i = isl_basic_map_alloc_equality(bmap);
+ if (i < 0)
+ goto error;
+ isl_seq_clr(bmap->eq[i], 1 + total);
+ pos1 += isl_basic_map_offset(bmap, type1);
+ pos2 += isl_basic_map_offset(bmap, type2);
+ isl_int_set_si(bmap->eq[i][pos1], -1);
+ isl_int_set_si(bmap->eq[i][pos2], 1);
+ bmap = isl_basic_map_finalize(bmap);
+ isl_space_free(space);
+ return bmap;
+error:
+ isl_space_free(space);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Add a constraint imposing that the given two dimensions are equal.
+ */
+__isl_give isl_basic_map *isl_basic_map_equate(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ isl_basic_map *eq;
+
+ eq = equator(isl_basic_map_get_space(bmap), type1, pos1, type2, pos2);
+
+ bmap = isl_basic_map_intersect(bmap, eq);
+
+ return bmap;
+}
+
+/* Add a constraint imposing that the given two dimensions are equal.
+ */
+__isl_give isl_map *isl_map_equate(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ isl_basic_map *bmap;
+
+ bmap = equator(isl_map_get_space(map), type1, pos1, type2, pos2);
+
+ map = isl_map_intersect(map, isl_map_from_basic_map(bmap));
+
+ return map;
+}
+
+/* Add a constraint imposing that the given two dimensions have opposite values.
+ */
+__isl_give isl_map *isl_map_oppose(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ isl_basic_map *bmap = NULL;
+ int i;
+ isl_size total;
+
+ if (isl_map_check_range(map, type1, pos1, 1) < 0)
+ return isl_map_free(map);
+ if (isl_map_check_range(map, type2, pos2, 1) < 0)
+ return isl_map_free(map);
+
+ total = isl_map_dim(map, isl_dim_all);
+ if (total < 0)
+ return isl_map_free(map);
+ bmap = isl_basic_map_alloc_space(isl_map_get_space(map), 0, 1, 0);
+ i = isl_basic_map_alloc_equality(bmap);
+ if (i < 0)
+ goto error;
+ isl_seq_clr(bmap->eq[i], 1 + total);
+ pos1 += isl_basic_map_offset(bmap, type1);
+ pos2 += isl_basic_map_offset(bmap, type2);
+ isl_int_set_si(bmap->eq[i][pos1], 1);
+ isl_int_set_si(bmap->eq[i][pos2], 1);
+ bmap = isl_basic_map_finalize(bmap);
+
+ map = isl_map_intersect(map, isl_map_from_basic_map(bmap));
+
+ return map;
+error:
+ isl_basic_map_free(bmap);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Construct a constraint imposing that the value of the first dimension is
+ * greater than or equal to that of the second.
+ */
+static __isl_give isl_constraint *constraint_order_ge(
+ __isl_take isl_space *space, enum isl_dim_type type1, int pos1,
+ enum isl_dim_type type2, int pos2)
+{
+ isl_constraint *c;
+
+ if (isl_space_check_range(space, type1, pos1, 1) < 0 ||
+ isl_space_check_range(space, type2, pos2, 1) < 0)
+ space = isl_space_free(space);
+ if (!space)
+ return NULL;
+
+ c = isl_constraint_alloc_inequality(isl_local_space_from_space(space));
+
+ if (type1 == type2 && pos1 == pos2)
+ return c;
+
+ c = isl_constraint_set_coefficient_si(c, type1, pos1, 1);
+ c = isl_constraint_set_coefficient_si(c, type2, pos2, -1);
+
+ return c;
+}
+
+/* Add a constraint imposing that the value of the first dimension is
+ * greater than or equal to that of the second.
+ */
+__isl_give isl_basic_map *isl_basic_map_order_ge(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ isl_constraint *c;
+ isl_space *space;
+
+ if (type1 == type2 && pos1 == pos2)
+ return bmap;
+ space = isl_basic_map_get_space(bmap);
+ c = constraint_order_ge(space, type1, pos1, type2, pos2);
+ bmap = isl_basic_map_add_constraint(bmap, c);
+
+ return bmap;
+}
+
+/* Add a constraint imposing that the value of the first dimension is
+ * greater than or equal to that of the second.
+ */
+__isl_give isl_map *isl_map_order_ge(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ isl_constraint *c;
+ isl_space *space;
+
+ if (type1 == type2 && pos1 == pos2)
+ return map;
+ space = isl_map_get_space(map);
+ c = constraint_order_ge(space, type1, pos1, type2, pos2);
+ map = isl_map_add_constraint(map, c);
+
+ return map;
+}
+
+/* Add a constraint imposing that the value of the first dimension is
+ * less than or equal to that of the second.
+ */
+__isl_give isl_map *isl_map_order_le(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ return isl_map_order_ge(map, type2, pos2, type1, pos1);
+}
+
+/* Construct a basic map where the value of the first dimension is
+ * greater than that of the second.
+ */
+static __isl_give isl_basic_map *greator(__isl_take isl_space *space,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ isl_basic_map *bmap = NULL;
+ int i;
+ isl_size total;
+
+ if (isl_space_check_range(space, type1, pos1, 1) < 0 ||
+ isl_space_check_range(space, type2, pos2, 1) < 0)
+ goto error;
+
+ if (type1 == type2 && pos1 == pos2)
+ return isl_basic_map_empty(space);
+
+ bmap = isl_basic_map_alloc_space(space, 0, 0, 1);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ i = isl_basic_map_alloc_inequality(bmap);
+ if (total < 0 || i < 0)
+ return isl_basic_map_free(bmap);
+ isl_seq_clr(bmap->ineq[i], 1 + total);
+ pos1 += isl_basic_map_offset(bmap, type1);
+ pos2 += isl_basic_map_offset(bmap, type2);
+ isl_int_set_si(bmap->ineq[i][pos1], 1);
+ isl_int_set_si(bmap->ineq[i][pos2], -1);
+ isl_int_set_si(bmap->ineq[i][0], -1);
+ bmap = isl_basic_map_finalize(bmap);
+
+ return bmap;
+error:
+ isl_space_free(space);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Add a constraint imposing that the value of the first dimension is
+ * greater than that of the second.
+ */
+__isl_give isl_basic_map *isl_basic_map_order_gt(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ isl_basic_map *gt;
+
+ gt = greator(isl_basic_map_get_space(bmap), type1, pos1, type2, pos2);
+
+ bmap = isl_basic_map_intersect(bmap, gt);
+
+ return bmap;
+}
+
+/* Add a constraint imposing that the value of the first dimension is
+ * greater than that of the second.
+ */
+__isl_give isl_map *isl_map_order_gt(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ isl_basic_map *bmap;
+
+ bmap = greator(isl_map_get_space(map), type1, pos1, type2, pos2);
+
+ map = isl_map_intersect(map, isl_map_from_basic_map(bmap));
+
+ return map;
+}
+
+/* Add a constraint imposing that the value of the first dimension is
+ * smaller than that of the second.
+ */
+__isl_give isl_map *isl_map_order_lt(__isl_take isl_map *map,
+ enum isl_dim_type type1, int pos1, enum isl_dim_type type2, int pos2)
+{
+ return isl_map_order_gt(map, type2, pos2, type1, pos1);
+}
+
+__isl_give isl_aff *isl_basic_map_get_div(__isl_keep isl_basic_map *bmap,
+ int pos)
+{
+ isl_aff *div;
+ isl_local_space *ls;
+
+ if (!bmap)
+ return NULL;
+
+ if (!isl_basic_map_divs_known(bmap))
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "some divs are unknown", return NULL);
+
+ ls = isl_basic_map_get_local_space(bmap);
+ div = isl_local_space_get_div(ls, pos);
+ isl_local_space_free(ls);
+
+ return div;
+}
+
+__isl_give isl_aff *isl_basic_set_get_div(__isl_keep isl_basic_set *bset,
+ int pos)
+{
+ return isl_basic_map_get_div(bset, pos);
+}
+
+/* Plug in "subs" for set dimension "pos" of "set".
+ */
+__isl_give isl_set *isl_set_substitute(__isl_take isl_set *set,
+ unsigned pos, __isl_keep isl_aff *subs)
+{
+ isl_multi_aff *ma;
+
+ if (set && isl_set_plain_is_empty(set))
+ return set;
+
+ ma = isl_multi_aff_identity_on_domain_space(isl_set_get_space(set));
+ ma = isl_multi_aff_set_aff(ma, pos, isl_aff_copy(subs));
+ return isl_set_preimage_multi_aff(set, ma);
+}
+
+/* Check if the range of "ma" is compatible with the domain or range
+ * (depending on "type") of "bmap".
+ */
+static isl_stat check_basic_map_compatible_range_multi_aff(
+ __isl_keep isl_basic_map *bmap, enum isl_dim_type type,
+ __isl_keep isl_multi_aff *ma)
+{
+ isl_bool m;
+ isl_space *ma_space;
+
+ ma_space = isl_multi_aff_get_space(ma);
+
+ m = isl_space_has_equal_params(bmap->dim, ma_space);
+ if (m < 0)
+ goto error;
+ if (!m)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "parameters don't match", goto error);
+ m = isl_space_tuple_is_equal(bmap->dim, type, ma_space, isl_dim_out);
+ if (m < 0)
+ goto error;
+ if (!m)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "spaces don't match", goto error);
+
+ isl_space_free(ma_space);
+ return isl_stat_ok;
+error:
+ isl_space_free(ma_space);
+ return isl_stat_error;
+}
+
+/* Copy the divs from "ma" to "bmap", adding zeros for the "n_before"
+ * coefficients before the transformed range of dimensions,
+ * the "n_after" coefficients after the transformed range of dimensions
+ * and the coefficients of the other divs in "bmap".
+ */
+static __isl_give isl_basic_map *set_ma_divs(__isl_take isl_basic_map *bmap,
+ __isl_keep isl_multi_aff *ma, int n_before, int n_after, int n_div)
+{
+ int i;
+ isl_size n_param;
+ isl_size n_set;
+ isl_local_space *ls;
+
+ if (n_div == 0)
+ return bmap;
+
+ ls = isl_aff_get_domain_local_space(ma->u.p[0]);
+ n_param = isl_local_space_dim(ls, isl_dim_param);
+ n_set = isl_local_space_dim(ls, isl_dim_set);
+ if (n_param < 0 || n_set < 0)
+ return isl_basic_map_free(bmap);
+
+ for (i = 0; i < n_div; ++i) {
+ int o_bmap = 0, o_ls = 0;
+
+ isl_seq_cpy(bmap->div[i], ls->div->row[i], 1 + 1 + n_param);
+ o_bmap += 1 + 1 + n_param;
+ o_ls += 1 + 1 + n_param;
+ isl_seq_clr(bmap->div[i] + o_bmap, n_before);
+ o_bmap += n_before;
+ isl_seq_cpy(bmap->div[i] + o_bmap,
+ ls->div->row[i] + o_ls, n_set);
+ o_bmap += n_set;
+ o_ls += n_set;
+ isl_seq_clr(bmap->div[i] + o_bmap, n_after);
+ o_bmap += n_after;
+ isl_seq_cpy(bmap->div[i] + o_bmap,
+ ls->div->row[i] + o_ls, n_div);
+ o_bmap += n_div;
+ o_ls += n_div;
+ isl_seq_clr(bmap->div[i] + o_bmap, bmap->n_div - n_div);
+ bmap = isl_basic_map_add_div_constraints(bmap, i);
+ if (!bmap)
+ goto error;
+ }
+
+ isl_local_space_free(ls);
+ return bmap;
+error:
+ isl_local_space_free(ls);
+ return isl_basic_map_free(bmap);
+}
+
+/* How many stride constraints does "ma" enforce?
+ * That is, how many of the affine expressions have a denominator
+ * different from one?
+ */
+static int multi_aff_strides(__isl_keep isl_multi_aff *ma)
+{
+ int i;
+ int strides = 0;
+
+ for (i = 0; i < ma->n; ++i)
+ if (!isl_int_is_one(ma->u.p[i]->v->el[0]))
+ strides++;
+
+ return strides;
+}
+
+/* For each affine expression in ma of the form
+ *
+ * x_i = (f_i y + h_i)/m_i
+ *
+ * with m_i different from one, add a constraint to "bmap"
+ * of the form
+ *
+ * f_i y + h_i = m_i alpha_i
+ *
+ * with alpha_i an additional existentially quantified variable.
+ *
+ * The input variables of "ma" correspond to a subset of the variables
+ * of "bmap". There are "n_before" variables in "bmap" before this
+ * subset and "n_after" variables after this subset.
+ * The integer divisions of the affine expressions in "ma" are assumed
+ * to have been aligned. There are "n_div_ma" of them and
+ * they appear first in "bmap", straight after the "n_after" variables.
+ */
+static __isl_give isl_basic_map *add_ma_strides(
+ __isl_take isl_basic_map *bmap, __isl_keep isl_multi_aff *ma,
+ int n_before, int n_after, int n_div_ma)
+{
+ int i, k;
+ int div;
+ isl_size total;
+ isl_size n_param;
+ isl_size n_in;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ n_param = isl_multi_aff_dim(ma, isl_dim_param);
+ n_in = isl_multi_aff_dim(ma, isl_dim_in);
+ if (total < 0 || n_param < 0 || n_in < 0)
+ return isl_basic_map_free(bmap);
+ for (i = 0; i < ma->n; ++i) {
+ int o_bmap = 0, o_ma = 1;
+
+ if (isl_int_is_one(ma->u.p[i]->v->el[0]))
+ continue;
+ div = isl_basic_map_alloc_div(bmap);
+ k = isl_basic_map_alloc_equality(bmap);
+ if (div < 0 || k < 0)
+ goto error;
+ isl_int_set_si(bmap->div[div][0], 0);
+ isl_seq_cpy(bmap->eq[k] + o_bmap,
+ ma->u.p[i]->v->el + o_ma, 1 + n_param);
+ o_bmap += 1 + n_param;
+ o_ma += 1 + n_param;
+ isl_seq_clr(bmap->eq[k] + o_bmap, n_before);
+ o_bmap += n_before;
+ isl_seq_cpy(bmap->eq[k] + o_bmap,
+ ma->u.p[i]->v->el + o_ma, n_in);
+ o_bmap += n_in;
+ o_ma += n_in;
+ isl_seq_clr(bmap->eq[k] + o_bmap, n_after);
+ o_bmap += n_after;
+ isl_seq_cpy(bmap->eq[k] + o_bmap,
+ ma->u.p[i]->v->el + o_ma, n_div_ma);
+ o_bmap += n_div_ma;
+ o_ma += n_div_ma;
+ isl_seq_clr(bmap->eq[k] + o_bmap, 1 + total - o_bmap);
+ isl_int_neg(bmap->eq[k][1 + total], ma->u.p[i]->v->el[0]);
+ total++;
+ }
+
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Replace the domain or range space (depending on "type) of "space" by "set".
+ */
+static __isl_give isl_space *isl_space_set(__isl_take isl_space *space,
+ enum isl_dim_type type, __isl_take isl_space *set)
+{
+ if (type == isl_dim_in) {
+ space = isl_space_range(space);
+ space = isl_space_map_from_domain_and_range(set, space);
+ } else {
+ space = isl_space_domain(space);
+ space = isl_space_map_from_domain_and_range(space, set);
+ }
+
+ return space;
+}
+
+/* Compute the preimage of the domain or range (depending on "type")
+ * of "bmap" under the function represented by "ma".
+ * In other words, plug in "ma" in the domain or range of "bmap".
+ * The result is a basic map that lives in the same space as "bmap"
+ * except that the domain or range has been replaced by
+ * the domain space of "ma".
+ *
+ * If bmap is represented by
+ *
+ * A(p) + S u + B x + T v + C(divs) >= 0,
+ *
+ * where u and x are input and output dimensions if type == isl_dim_out
+ * while x and v are input and output dimensions if type == isl_dim_in,
+ * and ma is represented by
+ *
+ * x = D(p) + F(y) + G(divs')
+ *
+ * then the result is
+ *
+ * A(p) + B D(p) + S u + B F(y) + T v + B G(divs') + C(divs) >= 0
+ *
+ * The divs in the input set are similarly adjusted.
+ * In particular
+ *
+ * floor((a_i(p) + s u + b_i x + t v + c_i(divs))/n_i)
+ *
+ * becomes
+ *
+ * floor((a_i(p) + b_i D(p) + s u + b_i F(y) + t v +
+ * B_i G(divs') + c_i(divs))/n_i)
+ *
+ * If bmap is not a rational map and if F(y) involves any denominators
+ *
+ * x_i = (f_i y + h_i)/m_i
+ *
+ * then additional constraints are added to ensure that we only
+ * map back integer points. That is we enforce
+ *
+ * f_i y + h_i = m_i alpha_i
+ *
+ * with alpha_i an additional existentially quantified variable.
+ *
+ * We first copy over the divs from "ma".
+ * Then we add the modified constraints and divs from "bmap".
+ * Finally, we add the stride constraints, if needed.
+ */
+__isl_give isl_basic_map *isl_basic_map_preimage_multi_aff(
+ __isl_take isl_basic_map *bmap, enum isl_dim_type type,
+ __isl_take isl_multi_aff *ma)
+{
+ int i, k;
+ isl_space *space;
+ isl_basic_map *res = NULL;
+ isl_size n_before, n_after, n_div_bmap, n_div_ma;
+ isl_int f, c1, c2, g;
+ isl_bool rational;
+ int strides;
+
+ isl_int_init(f);
+ isl_int_init(c1);
+ isl_int_init(c2);
+ isl_int_init(g);
+
+ ma = isl_multi_aff_align_divs(ma);
+ if (!bmap || !ma)
+ goto error;
+ if (check_basic_map_compatible_range_multi_aff(bmap, type, ma) < 0)
+ goto error;
+
+ if (type == isl_dim_in) {
+ n_before = 0;
+ n_after = isl_basic_map_dim(bmap, isl_dim_out);
+ } else {
+ n_before = isl_basic_map_dim(bmap, isl_dim_in);
+ n_after = 0;
+ }
+ n_div_bmap = isl_basic_map_dim(bmap, isl_dim_div);
+ n_div_ma = ma->n ? isl_aff_dim(ma->u.p[0], isl_dim_div) : 0;
+ if (n_before < 0 || n_after < 0 || n_div_bmap < 0 || n_div_ma < 0)
+ goto error;
+
+ space = isl_multi_aff_get_domain_space(ma);
+ space = isl_space_set(isl_basic_map_get_space(bmap), type, space);
+ rational = isl_basic_map_is_rational(bmap);
+ strides = rational ? 0 : multi_aff_strides(ma);
+ res = isl_basic_map_alloc_space(space, n_div_ma + n_div_bmap + strides,
+ bmap->n_eq + strides, bmap->n_ineq + 2 * n_div_ma);
+ if (rational)
+ res = isl_basic_map_set_rational(res);
+
+ for (i = 0; i < n_div_ma + n_div_bmap; ++i)
+ if (isl_basic_map_alloc_div(res) < 0)
+ goto error;
+
+ res = set_ma_divs(res, ma, n_before, n_after, n_div_ma);
+ if (!res)
+ goto error;
+
+ for (i = 0; i < bmap->n_eq; ++i) {
+ k = isl_basic_map_alloc_equality(res);
+ if (k < 0)
+ goto error;
+ if (isl_seq_preimage(res->eq[k], bmap->eq[i], ma, n_before,
+ n_after, n_div_ma, n_div_bmap,
+ f, c1, c2, g, 0) < 0)
+ goto error;
+ }
+
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ k = isl_basic_map_alloc_inequality(res);
+ if (k < 0)
+ goto error;
+ if (isl_seq_preimage(res->ineq[k], bmap->ineq[i], ma, n_before,
+ n_after, n_div_ma, n_div_bmap,
+ f, c1, c2, g, 0) < 0)
+ goto error;
+ }
+
+ for (i = 0; i < bmap->n_div; ++i) {
+ if (isl_int_is_zero(bmap->div[i][0])) {
+ isl_int_set_si(res->div[n_div_ma + i][0], 0);
+ continue;
+ }
+ if (isl_seq_preimage(res->div[n_div_ma + i], bmap->div[i], ma,
+ n_before, n_after, n_div_ma, n_div_bmap,
+ f, c1, c2, g, 1) < 0)
+ goto error;
+ }
+
+ if (strides)
+ res = add_ma_strides(res, ma, n_before, n_after, n_div_ma);
+
+ isl_int_clear(f);
+ isl_int_clear(c1);
+ isl_int_clear(c2);
+ isl_int_clear(g);
+ isl_basic_map_free(bmap);
+ isl_multi_aff_free(ma);
+ res = isl_basic_map_simplify(res);
+ return isl_basic_map_finalize(res);
+error:
+ isl_int_clear(f);
+ isl_int_clear(c1);
+ isl_int_clear(c2);
+ isl_int_clear(g);
+ isl_basic_map_free(bmap);
+ isl_multi_aff_free(ma);
+ isl_basic_map_free(res);
+ return NULL;
+}
+
+/* Compute the preimage of "bset" under the function represented by "ma".
+ * In other words, plug in "ma" in "bset". The result is a basic set
+ * that lives in the domain space of "ma".
+ */
+__isl_give isl_basic_set *isl_basic_set_preimage_multi_aff(
+ __isl_take isl_basic_set *bset, __isl_take isl_multi_aff *ma)
+{
+ return isl_basic_map_preimage_multi_aff(bset, isl_dim_set, ma);
+}
+
+/* Compute the preimage of the domain of "bmap" under the function
+ * represented by "ma".
+ * In other words, plug in "ma" in the domain of "bmap".
+ * The result is a basic map that lives in the same space as "bmap"
+ * except that the domain has been replaced by the domain space of "ma".
+ */
+__isl_give isl_basic_map *isl_basic_map_preimage_domain_multi_aff(
+ __isl_take isl_basic_map *bmap, __isl_take isl_multi_aff *ma)
+{
+ return isl_basic_map_preimage_multi_aff(bmap, isl_dim_in, ma);
+}
+
+/* Compute the preimage of the range of "bmap" under the function
+ * represented by "ma".
+ * In other words, plug in "ma" in the range of "bmap".
+ * The result is a basic map that lives in the same space as "bmap"
+ * except that the range has been replaced by the domain space of "ma".
+ */
+__isl_give isl_basic_map *isl_basic_map_preimage_range_multi_aff(
+ __isl_take isl_basic_map *bmap, __isl_take isl_multi_aff *ma)
+{
+ return isl_basic_map_preimage_multi_aff(bmap, isl_dim_out, ma);
+}
+
+/* Check if the range of "ma" is compatible with the domain or range
+ * (depending on "type") of "map".
+ * Return isl_stat_error if anything is wrong.
+ */
+static isl_stat check_map_compatible_range_multi_aff(
+ __isl_keep isl_map *map, enum isl_dim_type type,
+ __isl_keep isl_multi_aff *ma)
+{
+ isl_bool m;
+ isl_space *ma_space;
+
+ ma_space = isl_multi_aff_get_space(ma);
+ m = isl_map_space_tuple_is_equal(map, type, ma_space, isl_dim_out);
+ isl_space_free(ma_space);
+ if (m < 0)
+ return isl_stat_error;
+ if (!m)
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "spaces don't match", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Compute the preimage of the domain or range (depending on "type")
+ * of "map" under the function represented by "ma".
+ * In other words, plug in "ma" in the domain or range of "map".
+ * The result is a map that lives in the same space as "map"
+ * except that the domain or range has been replaced by
+ * the domain space of "ma".
+ *
+ * The parameters are assumed to have been aligned.
+ */
+static __isl_give isl_map *map_preimage_multi_aff(__isl_take isl_map *map,
+ enum isl_dim_type type, __isl_take isl_multi_aff *ma)
+{
+ int i;
+ isl_space *space;
+
+ map = isl_map_cow(map);
+ ma = isl_multi_aff_align_divs(ma);
+ if (!map || !ma)
+ goto error;
+ if (check_map_compatible_range_multi_aff(map, type, ma) < 0)
+ goto error;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_preimage_multi_aff(map->p[i], type,
+ isl_multi_aff_copy(ma));
+ if (!map->p[i])
+ goto error;
+ }
+
+ space = isl_multi_aff_get_domain_space(ma);
+ space = isl_space_set(isl_map_get_space(map), type, space);
+
+ isl_space_free(isl_map_take_space(map));
+ map = isl_map_restore_space(map, space);
+ if (!map)
+ goto error;
+
+ isl_multi_aff_free(ma);
+ if (map->n > 1)
+ ISL_F_CLR(map, ISL_MAP_DISJOINT);
+ ISL_F_CLR(map, ISL_SET_NORMALIZED);
+ return map;
+error:
+ isl_multi_aff_free(ma);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Compute the preimage of the domain or range (depending on "type")
+ * of "map" under the function represented by "ma".
+ * In other words, plug in "ma" in the domain or range of "map".
+ * The result is a map that lives in the same space as "map"
+ * except that the domain or range has been replaced by
+ * the domain space of "ma".
+ */
+__isl_give isl_map *isl_map_preimage_multi_aff(__isl_take isl_map *map,
+ enum isl_dim_type type, __isl_take isl_multi_aff *ma)
+{
+ isl_bool aligned;
+
+ if (!map || !ma)
+ goto error;
+
+ aligned = isl_map_space_has_equal_params(map, ma->space);
+ if (aligned < 0)
+ goto error;
+ if (aligned)
+ return map_preimage_multi_aff(map, type, ma);
+
+ if (isl_map_check_named_params(map) < 0)
+ goto error;
+ if (!isl_space_has_named_params(ma->space))
+ isl_die(map->ctx, isl_error_invalid,
+ "unaligned unnamed parameters", goto error);
+ map = isl_map_align_params(map, isl_multi_aff_get_space(ma));
+ ma = isl_multi_aff_align_params(ma, isl_map_get_space(map));
+
+ return map_preimage_multi_aff(map, type, ma);
+error:
+ isl_multi_aff_free(ma);
+ return isl_map_free(map);
+}
+
+/* Compute the preimage of "set" under the function represented by "ma".
+ * In other words, plug in "ma" in "set". The result is a set
+ * that lives in the domain space of "ma".
+ */
+__isl_give isl_set *isl_set_preimage_multi_aff(__isl_take isl_set *set,
+ __isl_take isl_multi_aff *ma)
+{
+ return isl_map_preimage_multi_aff(set, isl_dim_set, ma);
+}
+
+/* Compute the preimage of the domain of "map" under the function
+ * represented by "ma".
+ * In other words, plug in "ma" in the domain of "map".
+ * The result is a map that lives in the same space as "map"
+ * except that the domain has been replaced by the domain space of "ma".
+ */
+__isl_give isl_map *isl_map_preimage_domain_multi_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_aff *ma)
+{
+ return isl_map_preimage_multi_aff(map, isl_dim_in, ma);
+}
+
+/* Compute the preimage of the range of "map" under the function
+ * represented by "ma".
+ * In other words, plug in "ma" in the range of "map".
+ * The result is a map that lives in the same space as "map"
+ * except that the range has been replaced by the domain space of "ma".
+ */
+__isl_give isl_map *isl_map_preimage_range_multi_aff(__isl_take isl_map *map,
+ __isl_take isl_multi_aff *ma)
+{
+ return isl_map_preimage_multi_aff(map, isl_dim_out, ma);
+}
+
+/* Compute the preimage of "map" under the function represented by "pma".
+ * In other words, plug in "pma" in the domain or range of "map".
+ * The result is a map that lives in the same space as "map",
+ * except that the space of type "type" has been replaced by
+ * the domain space of "pma".
+ *
+ * The parameters of "map" and "pma" are assumed to have been aligned.
+ */
+static __isl_give isl_map *isl_map_preimage_pw_multi_aff_aligned(
+ __isl_take isl_map *map, enum isl_dim_type type,
+ __isl_take isl_pw_multi_aff *pma)
+{
+ int i;
+ isl_map *res;
+
+ if (!pma)
+ goto error;
+
+ if (pma->n == 0) {
+ isl_pw_multi_aff_free(pma);
+ res = isl_map_empty(isl_map_get_space(map));
+ isl_map_free(map);
+ return res;
+ }
+
+ res = isl_map_preimage_multi_aff(isl_map_copy(map), type,
+ isl_multi_aff_copy(pma->p[0].maff));
+ if (type == isl_dim_in)
+ res = isl_map_intersect_domain(res,
+ isl_map_copy(pma->p[0].set));
+ else
+ res = isl_map_intersect_range(res,
+ isl_map_copy(pma->p[0].set));
+
+ for (i = 1; i < pma->n; ++i) {
+ isl_map *res_i;
+
+ res_i = isl_map_preimage_multi_aff(isl_map_copy(map), type,
+ isl_multi_aff_copy(pma->p[i].maff));
+ if (type == isl_dim_in)
+ res_i = isl_map_intersect_domain(res_i,
+ isl_map_copy(pma->p[i].set));
+ else
+ res_i = isl_map_intersect_range(res_i,
+ isl_map_copy(pma->p[i].set));
+ res = isl_map_union(res, res_i);
+ }
+
+ isl_pw_multi_aff_free(pma);
+ isl_map_free(map);
+ return res;
+error:
+ isl_pw_multi_aff_free(pma);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Compute the preimage of "map" under the function represented by "pma".
+ * In other words, plug in "pma" in the domain or range of "map".
+ * The result is a map that lives in the same space as "map",
+ * except that the space of type "type" has been replaced by
+ * the domain space of "pma".
+ */
+__isl_give isl_map *isl_map_preimage_pw_multi_aff(__isl_take isl_map *map,
+ enum isl_dim_type type, __isl_take isl_pw_multi_aff *pma)
+{
+ isl_bool aligned;
+
+ if (!map || !pma)
+ goto error;
+
+ aligned = isl_map_space_has_equal_params(map, pma->dim);
+ if (aligned < 0)
+ goto error;
+ if (aligned)
+ return isl_map_preimage_pw_multi_aff_aligned(map, type, pma);
+
+ if (isl_map_check_named_params(map) < 0)
+ goto error;
+ if (isl_pw_multi_aff_check_named_params(pma) < 0)
+ goto error;
+ map = isl_map_align_params(map, isl_pw_multi_aff_get_space(pma));
+ pma = isl_pw_multi_aff_align_params(pma, isl_map_get_space(map));
+
+ return isl_map_preimage_pw_multi_aff_aligned(map, type, pma);
+error:
+ isl_pw_multi_aff_free(pma);
+ return isl_map_free(map);
+}
+
+/* Compute the preimage of "set" under the function represented by "pma".
+ * In other words, plug in "pma" in "set". The result is a set
+ * that lives in the domain space of "pma".
+ */
+__isl_give isl_set *isl_set_preimage_pw_multi_aff(__isl_take isl_set *set,
+ __isl_take isl_pw_multi_aff *pma)
+{
+ return isl_map_preimage_pw_multi_aff(set, isl_dim_set, pma);
+}
+
+/* Compute the preimage of the domain of "map" under the function
+ * represented by "pma".
+ * In other words, plug in "pma" in the domain of "map".
+ * The result is a map that lives in the same space as "map",
+ * except that domain space has been replaced by the domain space of "pma".
+ */
+__isl_give isl_map *isl_map_preimage_domain_pw_multi_aff(
+ __isl_take isl_map *map, __isl_take isl_pw_multi_aff *pma)
+{
+ return isl_map_preimage_pw_multi_aff(map, isl_dim_in, pma);
+}
+
+/* Compute the preimage of the range of "map" under the function
+ * represented by "pma".
+ * In other words, plug in "pma" in the range of "map".
+ * The result is a map that lives in the same space as "map",
+ * except that range space has been replaced by the domain space of "pma".
+ */
+__isl_give isl_map *isl_map_preimage_range_pw_multi_aff(
+ __isl_take isl_map *map, __isl_take isl_pw_multi_aff *pma)
+{
+ return isl_map_preimage_pw_multi_aff(map, isl_dim_out, pma);
+}
+
+/* Compute the preimage of "map" under the function represented by "mpa".
+ * In other words, plug in "mpa" in the domain or range of "map".
+ * The result is a map that lives in the same space as "map",
+ * except that the space of type "type" has been replaced by
+ * the domain space of "mpa".
+ *
+ * If the map does not involve any constraints that refer to the
+ * dimensions of the substituted space, then the only possible
+ * effect of "mpa" on the map is to map the space to a different space.
+ * We create a separate isl_multi_aff to effectuate this change
+ * in order to avoid spurious splitting of the map along the pieces
+ * of "mpa".
+ * If "mpa" has a non-trivial explicit domain, however,
+ * then the full substitution should be performed.
+ */
+__isl_give isl_map *isl_map_preimage_multi_pw_aff(__isl_take isl_map *map,
+ enum isl_dim_type type, __isl_take isl_multi_pw_aff *mpa)
+{
+ isl_size n;
+ isl_bool full;
+ isl_pw_multi_aff *pma;
+
+ n = isl_map_dim(map, type);
+ if (n < 0 || !mpa)
+ goto error;
+
+ full = isl_map_involves_dims(map, type, 0, n);
+ if (full >= 0 && !full)
+ full = isl_multi_pw_aff_has_non_trivial_domain(mpa);
+ if (full < 0)
+ goto error;
+ if (!full) {
+ isl_space *space;
+ isl_multi_aff *ma;
+
+ space = isl_multi_pw_aff_get_space(mpa);
+ isl_multi_pw_aff_free(mpa);
+ ma = isl_multi_aff_zero(space);
+ return isl_map_preimage_multi_aff(map, type, ma);
+ }
+
+ pma = isl_pw_multi_aff_from_multi_pw_aff(mpa);
+ return isl_map_preimage_pw_multi_aff(map, type, pma);
+error:
+ isl_map_free(map);
+ isl_multi_pw_aff_free(mpa);
+ return NULL;
+}
+
+/* Compute the preimage of "map" under the function represented by "mpa".
+ * In other words, plug in "mpa" in the domain "map".
+ * The result is a map that lives in the same space as "map",
+ * except that domain space has been replaced by the domain space of "mpa".
+ */
+__isl_give isl_map *isl_map_preimage_domain_multi_pw_aff(
+ __isl_take isl_map *map, __isl_take isl_multi_pw_aff *mpa)
+{
+ return isl_map_preimage_multi_pw_aff(map, isl_dim_in, mpa);
+}
+
+/* Compute the preimage of "set" by the function represented by "mpa".
+ * In other words, plug in "mpa" in "set".
+ */
+__isl_give isl_set *isl_set_preimage_multi_pw_aff(__isl_take isl_set *set,
+ __isl_take isl_multi_pw_aff *mpa)
+{
+ return isl_map_preimage_multi_pw_aff(set, isl_dim_set, mpa);
+}
+
+/* Return a copy of the equality constraints of "bset" as a matrix.
+ */
+__isl_give isl_mat *isl_basic_set_extract_equalities(
+ __isl_keep isl_basic_set *bset)
+{
+ isl_ctx *ctx;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ return NULL;
+
+ ctx = isl_basic_set_get_ctx(bset);
+ return isl_mat_sub_alloc6(ctx, bset->eq, 0, bset->n_eq, 0, 1 + total);
+}
+
+/* Are the "n" "coefficients" starting at "first" of the integer division
+ * expressions at position "pos1" in "bmap1" and "pos2" in "bmap2" equal
+ * to each other?
+ * The "coefficient" at position 0 is the denominator.
+ * The "coefficient" at position 1 is the constant term.
+ */
+isl_bool isl_basic_map_equal_div_expr_part(__isl_keep isl_basic_map *bmap1,
+ int pos1, __isl_keep isl_basic_map *bmap2, int pos2,
+ unsigned first, unsigned n)
+{
+ if (isl_basic_map_check_range(bmap1, isl_dim_div, pos1, 1) < 0)
+ return isl_bool_error;
+ if (isl_basic_map_check_range(bmap2, isl_dim_div, pos2, 1) < 0)
+ return isl_bool_error;
+ return isl_seq_eq(bmap1->div[pos1] + first,
+ bmap2->div[pos2] + first, n);
+}
+
+/* Are the integer division expressions at position "pos1" in "bmap1" and
+ * "pos2" in "bmap2" equal to each other, except that the constant terms
+ * are different?
+ */
+isl_bool isl_basic_map_equal_div_expr_except_constant(
+ __isl_keep isl_basic_map *bmap1, int pos1,
+ __isl_keep isl_basic_map *bmap2, int pos2)
+{
+ isl_bool equal;
+ isl_size total, total2;
+
+ total = isl_basic_map_dim(bmap1, isl_dim_all);
+ total2 = isl_basic_map_dim(bmap2, isl_dim_all);
+ if (total < 0 || total2 < 0)
+ return isl_bool_error;
+ if (total != total2)
+ isl_die(isl_basic_map_get_ctx(bmap1), isl_error_invalid,
+ "incomparable div expressions", return isl_bool_error);
+ equal = isl_basic_map_equal_div_expr_part(bmap1, pos1, bmap2, pos2,
+ 0, 1);
+ if (equal < 0 || !equal)
+ return equal;
+ equal = isl_basic_map_equal_div_expr_part(bmap1, pos1, bmap2, pos2,
+ 1, 1);
+ if (equal < 0 || equal)
+ return isl_bool_not(equal);
+ return isl_basic_map_equal_div_expr_part(bmap1, pos1, bmap2, pos2,
+ 2, total);
+}
+
+/* Replace the numerator of the constant term of the integer division
+ * expression at position "div" in "bmap" by "value".
+ * The caller guarantees that this does not change the meaning
+ * of the input.
+ */
+__isl_give isl_basic_map *isl_basic_map_set_div_expr_constant_num_si_inplace(
+ __isl_take isl_basic_map *bmap, int div, int value)
+{
+ if (isl_basic_map_check_range(bmap, isl_dim_div, div, 1) < 0)
+ return isl_basic_map_free(bmap);
+
+ isl_int_set_si(bmap->div[div][1], value);
+
+ return bmap;
+}
+
+/* Is the point "inner" internal to inequality constraint "ineq"
+ * of "bset"?
+ * The point is considered to be internal to the inequality constraint,
+ * if it strictly lies on the positive side of the inequality constraint,
+ * or if it lies on the constraint and the constraint is lexico-positive.
+ */
+static isl_bool is_internal(__isl_keep isl_vec *inner,
+ __isl_keep isl_basic_set *bset, int ineq)
+{
+ isl_ctx *ctx;
+ int pos;
+ isl_size total;
+
+ if (!inner || !bset)
+ return isl_bool_error;
+
+ ctx = isl_basic_set_get_ctx(bset);
+ isl_seq_inner_product(inner->el, bset->ineq[ineq], inner->size,
+ &ctx->normalize_gcd);
+ if (!isl_int_is_zero(ctx->normalize_gcd))
+ return isl_int_is_nonneg(ctx->normalize_gcd);
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ pos = isl_seq_first_non_zero(bset->ineq[ineq] + 1, total);
+ return isl_int_is_pos(bset->ineq[ineq][1 + pos]);
+}
+
+/* Tighten the inequality constraints of "bset" that are outward with respect
+ * to the point "vec".
+ * That is, tighten the constraints that are not satisfied by "vec".
+ *
+ * "vec" is a point internal to some superset S of "bset" that is used
+ * to make the subsets of S disjoint, by tightening one half of the constraints
+ * that separate two subsets. In particular, the constraints of S
+ * are all satisfied by "vec" and should not be tightened.
+ * Of the internal constraints, those that have "vec" on the outside
+ * are tightened. The shared facet is included in the adjacent subset
+ * with the opposite constraint.
+ * For constraints that saturate "vec", this criterion cannot be used
+ * to determine which of the two sides should be tightened.
+ * Instead, the sign of the first non-zero coefficient is used
+ * to make this choice. Note that this second criterion is never used
+ * on the constraints of S since "vec" is interior to "S".
+ */
+__isl_give isl_basic_set *isl_basic_set_tighten_outward(
+ __isl_take isl_basic_set *bset, __isl_keep isl_vec *vec)
+{
+ int j;
+
+ bset = isl_basic_set_cow(bset);
+ if (!bset)
+ return NULL;
+ for (j = 0; j < bset->n_ineq; ++j) {
+ isl_bool internal;
+
+ internal = is_internal(vec, bset, j);
+ if (internal < 0)
+ return isl_basic_set_free(bset);
+ if (internal)
+ continue;
+ isl_int_sub_ui(bset->ineq[j][0], bset->ineq[j][0], 1);
+ }
+
+ return bset;
+}
+
+/* Replace the variables x of type "type" starting at "first" in "bmap"
+ * by x' with x = M x' with M the matrix trans.
+ * That is, replace the corresponding coefficients c by c M.
+ *
+ * The transformation matrix should be a square matrix.
+ */
+__isl_give isl_basic_map *isl_basic_map_transform_dims(
+ __isl_take isl_basic_map *bmap, enum isl_dim_type type, unsigned first,
+ __isl_take isl_mat *trans)
+{
+ unsigned pos;
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap || !trans)
+ goto error;
+
+ if (trans->n_row != trans->n_col)
+ isl_die(trans->ctx, isl_error_invalid,
+ "expecting square transformation matrix", goto error);
+ if (isl_basic_map_check_range(bmap, type, first, trans->n_row) < 0)
+ goto error;
+
+ pos = isl_basic_map_offset(bmap, type) + first;
+
+ if (isl_mat_sub_transform(bmap->eq, bmap->n_eq, pos,
+ isl_mat_copy(trans)) < 0)
+ goto error;
+ if (isl_mat_sub_transform(bmap->ineq, bmap->n_ineq, pos,
+ isl_mat_copy(trans)) < 0)
+ goto error;
+ if (isl_mat_sub_transform(bmap->div, bmap->n_div, 1 + pos,
+ isl_mat_copy(trans)) < 0)
+ goto error;
+
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_SORTED);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS);
+
+ isl_mat_free(trans);
+ return bmap;
+error:
+ isl_mat_free(trans);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Replace the variables x of type "type" starting at "first" in "bset"
+ * by x' with x = M x' with M the matrix trans.
+ * That is, replace the corresponding coefficients c by c M.
+ *
+ * The transformation matrix should be a square matrix.
+ */
+__isl_give isl_basic_set *isl_basic_set_transform_dims(
+ __isl_take isl_basic_set *bset, enum isl_dim_type type, unsigned first,
+ __isl_take isl_mat *trans)
+{
+ return isl_basic_map_transform_dims(bset, type, first, trans);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_bound_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_bound_templ.c
new file mode 100644
index 00000000000..7e1bcf2e063
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_bound_templ.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include "isl_multi_macro.h"
+#undef TYPE
+#define TYPE CAT(isl_,BASE)
+
+/* Check that "map" and "multi" live in the same space, ignoring parameters.
+ */
+static isl_stat FN(check_map_equal_tuples_multi,BASE)(__isl_keep isl_map *map,
+ __isl_keep MULTI(BASE) *multi)
+{
+ isl_space *map_space, *multi_space;
+
+ map_space = isl_map_peek_space(map);
+ multi_space = FN(MULTI(BASE),peek_space)(multi);
+ return isl_space_check_equal_tuples(map_space, multi_space);
+}
+
+/* Apply "map_bound" to "map" with the corresponding value in "bound"
+ * for each output dimension.
+ * If "bound" has an explicit domain (which implies that "bound"
+ * is zero-dimensional), then intersect the domain of "map"
+ * with this explicit domain instead.
+ */
+static __isl_give isl_map *FN(map_bound_multi,BASE)(__isl_take isl_map *map,
+ __isl_take MULTI(BASE) *bound,
+ __isl_give isl_map *map_bound(__isl_take isl_map *map,
+ unsigned pos, __isl_take TYPE *value))
+{
+ int i;
+ isl_size dim;
+
+ dim = isl_map_dim(map, isl_dim_out);
+ if (dim < 0 || FN(check_map_equal_tuples_multi,BASE)(map, bound) < 0)
+ goto error;
+
+ for (i = 0; i < dim; ++i) {
+ TYPE *el;
+
+ el = FN(MULTI(BASE),get_at)(bound, i);
+ map = map_bound(map, i, el);
+ }
+ map = FN(FN(isl_map_intersect_multi,BASE),explicit_domain)(map, bound);
+ FN(MULTI(BASE),free)(bound);
+ return map;
+error:
+ isl_map_free(map);
+ FN(MULTI(BASE),free)(bound);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_lexopt_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_lexopt_templ.c
new file mode 100644
index 00000000000..780a54f3b20
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_lexopt_templ.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2012 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+/* Function for computing the lexicographic optimum of a map
+ * in the form of either an isl_map or an isl_pw_multi_aff.
+ */
+
+#define xSF(TYPE,SUFFIX) TYPE ## SUFFIX
+#define SF(TYPE,SUFFIX) xSF(TYPE,SUFFIX)
+
+/* Compute the lexicographic minimum (or maximum if "flags" includes
+ * ISL_OPT_MAX) of "bmap" over the domain "dom" and return the result.
+ * If "empty" is not NULL, then *empty is assigned a set that
+ * contains those parts of the domain where there is no solution.
+ * If "flags" includes ISL_OPT_FULL, then "dom" is NULL and the optimum
+ * should be computed over the domain of "bmap". "empty" is also NULL
+ * in this case.
+ * If "bmap" is marked as rational (ISL_BASIC_MAP_RATIONAL),
+ * then the rational optimum is computed. Otherwise, the integral optimum
+ * is computed.
+ */
+static __isl_give TYPE *SF(isl_basic_map_partial_lexopt,SUFFIX)(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, unsigned flags)
+{
+ return SF(isl_tab_basic_map_partial_lexopt,SUFFIX)(bmap, dom, empty,
+ flags);
+}
+
+__isl_give TYPE *SF(isl_basic_map_partial_lexmax,SUFFIX)(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty)
+{
+ unsigned flags = ISL_OPT_MAX;
+ return SF(isl_basic_map_partial_lexopt,SUFFIX)(bmap, dom, empty, flags);
+}
+
+__isl_give TYPE *SF(isl_basic_map_partial_lexmin,SUFFIX)(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty)
+{
+ unsigned flags = 0;
+ return SF(isl_basic_map_partial_lexopt,SUFFIX)(bmap, dom, empty, flags);
+}
+
+__isl_give TYPE *SF(isl_basic_set_partial_lexmin,SUFFIX)(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty)
+{
+ return SF(isl_basic_map_partial_lexmin,SUFFIX)(bset, dom, empty);
+}
+
+__isl_give TYPE *SF(isl_basic_set_partial_lexmax,SUFFIX)(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty)
+{
+ return SF(isl_basic_map_partial_lexmax,SUFFIX)(bset, dom, empty);
+}
+
+/* Given a basic map "bmap", compute the lexicographically minimal
+ * (or maximal) image element for each domain element in dom.
+ * If empty is not NULL, then set *empty to those elements in dom
+ * that do not have an image element.
+ * If "flags" includes ISL_OPT_FULL, then "dom" is NULL and the optimum
+ * should be computed over the domain of "bmap". "empty" is also NULL
+ * in this case.
+ *
+ * We first make sure the basic sets in dom are disjoint and then
+ * simply collect the results over each of the basic sets separately.
+ * We could probably improve the efficiency a bit by moving the union
+ * domain down into the parametric integer programming.
+ *
+ * If a full optimum is being computed (i.e., "flags" includes ISL_OPT_FULL),
+ * then no domain is given and there is then also no need to consider
+ * the disjuncts of the domain.
+ */
+static __isl_give TYPE *SF(basic_map_partial_lexopt,SUFFIX)(
+ __isl_take isl_basic_map *bmap, __isl_take isl_set *dom,
+ __isl_give isl_set **empty, unsigned flags)
+{
+ int i;
+ TYPE *res;
+ isl_set *all_empty;
+
+ if (ISL_FL_ISSET(flags, ISL_OPT_FULL))
+ return SF(isl_basic_map_partial_lexopt,SUFFIX)(bmap, NULL,
+ empty, flags);
+
+ dom = isl_set_make_disjoint(dom);
+ if (!dom)
+ goto error;
+
+ if (isl_set_plain_is_empty(dom)) {
+ isl_space *space = isl_basic_map_get_space(bmap);
+ if (empty)
+ *empty = dom;
+ else
+ isl_set_free(dom);
+ isl_basic_map_free(bmap);
+ return EMPTY(space);
+ }
+
+ res = SF(isl_basic_map_partial_lexopt,SUFFIX)(isl_basic_map_copy(bmap),
+ isl_basic_set_copy(dom->p[0]), empty, flags);
+
+ if (empty)
+ all_empty = *empty;
+ for (i = 1; i < dom->n; ++i) {
+ TYPE *res_i;
+
+ res_i = SF(isl_basic_map_partial_lexopt,SUFFIX)(
+ isl_basic_map_copy(bmap),
+ isl_basic_set_copy(dom->p[i]), empty, flags);
+
+ res = ADD(res, res_i);
+ if (empty)
+ all_empty = isl_set_union_disjoint(all_empty, *empty);
+ }
+
+ if (empty)
+ *empty = all_empty;
+ isl_set_free(dom);
+ isl_basic_map_free(bmap);
+ return res;
+error:
+ if (empty)
+ *empty = NULL;
+ isl_set_free(dom);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Compute the lexicographic minimum (or maximum if "flags" includes
+ * ISL_OPT_MAX) of "bmap" over its domain.
+ */
+__isl_give TYPE *SF(isl_basic_map_lexopt,SUFFIX)(
+ __isl_take isl_basic_map *bmap, unsigned flags)
+{
+ ISL_FL_SET(flags, ISL_OPT_FULL);
+ return SF(isl_basic_map_partial_lexopt,SUFFIX)(bmap, NULL, NULL, flags);
+}
+
+__isl_give TYPE *SF(isl_basic_map_lexmin,SUFFIX)(__isl_take isl_basic_map *bmap)
+{
+ return SF(isl_basic_map_lexopt,SUFFIX)(bmap, 0);
+}
+
+static __isl_give TYPE *SF(isl_map_partial_lexopt_aligned,SUFFIX)(
+ __isl_take isl_map *map, __isl_take isl_set *dom,
+ __isl_give isl_set **empty, unsigned flags);
+/* This function is currently only used when TYPE is defined as isl_map. */
+static __isl_give TYPE *SF(isl_map_partial_lexopt,SUFFIX)(
+ __isl_take isl_map *map, __isl_take isl_set *dom,
+ __isl_give isl_set **empty, unsigned flags)
+ __attribute__ ((unused));
+
+/* Given a map "map", compute the lexicographically minimal
+ * (or maximal) image element for each domain element in dom.
+ * Set *empty to those elements in dom that do not have an image element.
+ *
+ * Align parameters if needed and then call isl_map_partial_lexopt_aligned.
+ */
+static __isl_give TYPE *SF(isl_map_partial_lexopt,SUFFIX)(
+ __isl_take isl_map *map, __isl_take isl_set *dom,
+ __isl_give isl_set **empty, unsigned flags)
+{
+ isl_bool aligned;
+
+ aligned = isl_map_set_has_equal_params(map, dom);
+ if (aligned < 0)
+ goto error;
+ if (aligned)
+ return SF(isl_map_partial_lexopt_aligned,SUFFIX)(map, dom,
+ empty, flags);
+ if (!isl_space_has_named_params(map->dim) ||
+ !isl_space_has_named_params(dom->dim))
+ isl_die(map->ctx, isl_error_invalid,
+ "unaligned unnamed parameters", goto error);
+ map = isl_map_align_params(map, isl_map_get_space(dom));
+ dom = isl_map_align_params(dom, isl_map_get_space(map));
+ return SF(isl_map_partial_lexopt_aligned,SUFFIX)(map, dom, empty,
+ flags);
+error:
+ if (empty)
+ *empty = NULL;
+ isl_set_free(dom);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Compute the lexicographic minimum (or maximum if "flags" includes
+ * ISL_OPT_MAX) of "map" over its domain.
+ */
+__isl_give TYPE *SF(isl_map_lexopt,SUFFIX)(__isl_take isl_map *map,
+ unsigned flags)
+{
+ ISL_FL_SET(flags, ISL_OPT_FULL);
+ return SF(isl_map_partial_lexopt_aligned,SUFFIX)(map, NULL, NULL,
+ flags);
+}
+
+__isl_give TYPE *SF(isl_map_lexmin,SUFFIX)(__isl_take isl_map *map)
+{
+ return SF(isl_map_lexopt,SUFFIX)(map, 0);
+}
+
+__isl_give TYPE *SF(isl_map_lexmax,SUFFIX)(__isl_take isl_map *map)
+{
+ return SF(isl_map_lexopt,SUFFIX)(map, ISL_OPT_MAX);
+}
+
+__isl_give TYPE *SF(isl_set_lexmin,SUFFIX)(__isl_take isl_set *set)
+{
+ return SF(isl_map_lexmin,SUFFIX)(set);
+}
+
+__isl_give TYPE *SF(isl_set_lexmax,SUFFIX)(__isl_take isl_set *set)
+{
+ return SF(isl_map_lexmax,SUFFIX)(set);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_list.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_list.c
new file mode 100644
index 00000000000..3314fa70a9a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_list.c
@@ -0,0 +1,33 @@
+#include <isl/map.h>
+#include <isl/union_map.h>
+
+#undef EL
+#define EL isl_basic_map
+
+#include <isl_list_templ.h>
+
+#undef EL_BASE
+#define EL_BASE basic_map
+
+#include <isl_list_templ.c>
+
+#undef EL
+#define EL isl_map
+
+#include <isl_list_templ.h>
+
+#undef EL_BASE
+#define EL_BASE map
+
+#include <isl_list_templ.c>
+#include <isl_list_read_templ.c>
+
+#undef EL
+#define EL isl_union_map
+
+#include <isl_list_templ.h>
+
+#undef EL_BASE
+#define EL_BASE union_map
+
+#include <isl_list_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_private.h
new file mode 100644
index 00000000000..c52e835a359
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_private.h
@@ -0,0 +1,598 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_MAP_PRIVATE_H
+#define ISL_MAP_PRIVATE_H
+
+#define isl_basic_set isl_basic_map
+#define isl_maybe_isl_basic_set isl_maybe_isl_basic_map
+#define isl_set isl_map
+#define isl_basic_set_list isl_basic_map_list
+#define isl_set_list isl_map_list
+#include <isl/list.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl_reordering.h>
+#include <isl/vec.h>
+#include <isl/hash.h>
+#include <isl_blk.h>
+
+/* A "basic map" is a relation between two sets of variables,
+ * called the "in" and "out" variables.
+ * A "basic set" is a basic map with a zero-dimensional
+ * domain.
+ *
+ * It is implemented as a set with two extra fields:
+ * n_in is the number of in variables
+ * n_out is the number of out variables
+ * n_in + n_out should be equal to set.dim
+ */
+struct isl_basic_map {
+ int ref;
+#define ISL_BASIC_MAP_FINAL (1 << 0)
+#define ISL_BASIC_MAP_EMPTY (1 << 1)
+#define ISL_BASIC_MAP_NO_IMPLICIT (1 << 2)
+#define ISL_BASIC_MAP_NO_REDUNDANT (1 << 3)
+#define ISL_BASIC_MAP_RATIONAL (1 << 4)
+#define ISL_BASIC_MAP_SORTED (1 << 5)
+#define ISL_BASIC_MAP_NORMALIZED_DIVS (1 << 6)
+#define ISL_BASIC_MAP_ALL_EQUALITIES (1 << 7)
+#define ISL_BASIC_MAP_REDUCED_COEFFICIENTS (1 << 8)
+#define ISL_BASIC_SET_FINAL (1 << 0)
+#define ISL_BASIC_SET_EMPTY (1 << 1)
+#define ISL_BASIC_SET_NO_IMPLICIT (1 << 2)
+#define ISL_BASIC_SET_NO_REDUNDANT (1 << 3)
+#define ISL_BASIC_SET_RATIONAL (1 << 4)
+#define ISL_BASIC_SET_SORTED (1 << 5)
+#define ISL_BASIC_SET_NORMALIZED_DIVS (1 << 6)
+#define ISL_BASIC_SET_ALL_EQUALITIES (1 << 7)
+#define ISL_BASIC_SET_REDUCED_COEFFICIENTS (1 << 8)
+ unsigned flags;
+
+ struct isl_ctx *ctx;
+
+ isl_space *dim;
+ unsigned extra;
+
+ unsigned n_eq;
+ unsigned n_ineq;
+
+ size_t c_size;
+ isl_int **eq;
+ isl_int **ineq;
+
+ unsigned n_div;
+
+ isl_int **div;
+
+ struct isl_vec *sample;
+
+ struct isl_blk block;
+ struct isl_blk block2;
+};
+
+#undef EL
+#define EL isl_basic_set
+
+#include <isl_list_templ.h>
+
+/* A "map" is a (possibly disjoint) union of basic maps.
+ * A "set" is a (possibly disjoint) union of basic sets.
+ *
+ * Currently, the isl_set structure is identical to the isl_map structure
+ * and the library depends on this correspondence internally.
+ * However, users should not depend on this correspondence.
+ *
+ * "cached_simple_hull" contains copies of the unshifted and shifted
+ * simple hulls, if they have already been computed. Otherwise,
+ * the entries are NULL.
+ */
+struct isl_map {
+ int ref;
+#define ISL_MAP_DISJOINT (1 << 0)
+#define ISL_MAP_NORMALIZED (1 << 1)
+#define ISL_SET_DISJOINT (1 << 0)
+#define ISL_SET_NORMALIZED (1 << 1)
+ unsigned flags;
+ isl_basic_map *cached_simple_hull[2];
+
+ struct isl_ctx *ctx;
+
+ isl_space *dim;
+
+ int n;
+
+ size_t size;
+ struct isl_basic_map *p[1];
+};
+
+#undef EL
+#define EL isl_set
+
+#include <isl_list_templ.h>
+
+__isl_give isl_basic_set *isl_basic_set_alloc(isl_ctx *ctx,
+ unsigned nparam, unsigned dim, unsigned extra,
+ unsigned n_eq, unsigned n_ineq);
+__isl_give isl_basic_set *isl_basic_set_extend_constraints(
+ __isl_take isl_basic_set *base, unsigned n_eq, unsigned n_ineq);
+__isl_give isl_basic_set *isl_basic_set_finalize(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_set *isl_basic_set_dup(__isl_keep isl_basic_set *bset);
+__isl_give isl_basic_set *isl_basic_set_simplify(
+ __isl_take isl_basic_set *bset);
+
+__isl_give isl_basic_map *isl_basic_map_alloc(isl_ctx *ctx,
+ unsigned nparam, unsigned in, unsigned out, unsigned extra,
+ unsigned n_eq, unsigned n_ineq);
+__isl_give isl_basic_map *isl_basic_map_mark_final(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_finalize(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_extend_constraints(
+ __isl_take isl_basic_map *base, unsigned n_eq, unsigned n_ineq);
+__isl_give isl_basic_map *isl_basic_map_simplify(
+ __isl_take isl_basic_map *bmap);
+
+__isl_give isl_set *isl_set_add_basic_set(__isl_take isl_set *set,
+ __isl_take isl_basic_set *bset);
+
+__isl_give isl_map *isl_map_add_basic_map(__isl_take isl_map *map,
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_map *isl_map_dup(__isl_keep isl_map *map);
+
+__isl_give isl_basic_set *isl_basic_set_from_underlying_set(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *like);
+
+__isl_give isl_map *isl_map_realign(__isl_take isl_map *map,
+ __isl_take isl_reordering *r);
+__isl_give isl_set *isl_set_realign(__isl_take isl_set *set,
+ __isl_take isl_reordering *r);
+
+__isl_give isl_basic_map *isl_basic_map_reset(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type);
+__isl_give isl_map *isl_map_reset(__isl_take isl_map *map,
+ enum isl_dim_type type);
+
+__isl_keep isl_space *isl_basic_map_peek_space(
+ __isl_keep const isl_basic_map *bmap);
+__isl_keep isl_space *isl_basic_set_peek_space(__isl_keep isl_basic_set *bset);
+__isl_keep isl_space *isl_map_peek_space(__isl_keep const isl_map *map);
+__isl_keep isl_space *isl_set_peek_space(__isl_keep isl_set *set);
+
+__isl_give isl_basic_set *isl_basic_set_reset_space(
+ __isl_take isl_basic_set *bset, __isl_take isl_space *space);
+__isl_give isl_basic_map *isl_basic_map_reset_space(
+ __isl_take isl_basic_map *bmap, __isl_take isl_space *space);
+__isl_give isl_map *isl_map_reset_space(__isl_take isl_map *map,
+ __isl_take isl_space *space);
+__isl_give isl_map *isl_map_reset_equal_dim_space(__isl_take isl_map *map,
+ __isl_take isl_space *space);
+
+isl_size isl_basic_map_var_offset(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type);
+isl_size isl_basic_set_var_offset(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type);
+unsigned isl_basic_map_offset(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type);
+unsigned isl_basic_set_offset(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type);
+
+isl_bool isl_basic_map_may_be_set(__isl_keep isl_basic_map *bmap);
+int isl_map_may_be_set(__isl_keep isl_map *map);
+isl_bool isl_map_compatible_domain(__isl_keep isl_map *map,
+ __isl_keep isl_set *set);
+isl_bool isl_basic_map_compatible_domain(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_basic_set *bset);
+isl_bool isl_basic_map_compatible_range(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_basic_set *bset);
+
+__isl_give isl_basic_map *isl_basic_map_extend(__isl_take isl_basic_map *base,
+ unsigned extra, unsigned n_eq, unsigned n_ineq);
+__isl_give isl_basic_set *isl_basic_set_extend(__isl_take isl_basic_set *base,
+ unsigned extra, unsigned n_eq, unsigned n_ineq);
+
+__isl_give isl_map *isl_map_grow(__isl_take isl_map *map, int n);
+__isl_give isl_set *isl_set_grow(__isl_take isl_set *set, int n);
+
+isl_bool isl_basic_set_contains(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_vec *vec);
+isl_bool isl_basic_map_contains(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_vec *vec);
+
+__isl_give isl_basic_set *isl_basic_set_alloc_space(__isl_take isl_space *space,
+ unsigned extra, unsigned n_eq, unsigned n_ineq);
+__isl_give isl_set *isl_set_alloc_space(__isl_take isl_space *space, int n,
+ unsigned flags);
+__isl_give isl_basic_map *isl_basic_map_alloc_space(__isl_take isl_space *space,
+ unsigned extra, unsigned n_eq, unsigned n_ineq);
+__isl_give isl_map *isl_map_alloc_space(__isl_take isl_space *space, int n,
+ unsigned flags);
+
+int isl_basic_map_alloc_equality(__isl_keep isl_basic_map *bmap);
+int isl_basic_set_alloc_equality(__isl_keep isl_basic_set *bset);
+__isl_give isl_basic_set *isl_basic_set_free_inequality(
+ __isl_take isl_basic_set *bset, unsigned n);
+__isl_give isl_basic_map *isl_basic_map_free_equality(
+ __isl_take isl_basic_map *bmap, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_free_equality(
+ __isl_take isl_basic_set *bset, unsigned n);
+int isl_basic_set_alloc_inequality(__isl_keep isl_basic_set *bset);
+int isl_basic_map_alloc_inequality(__isl_keep isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_free_inequality(
+ __isl_take isl_basic_map *bmap, unsigned n);
+int isl_basic_map_alloc_div(__isl_keep isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_insert_div(
+ __isl_take isl_basic_map *bmap, int pos, __isl_keep isl_vec *div);
+int isl_basic_set_alloc_div(__isl_keep isl_basic_set *bset);
+isl_stat isl_basic_map_free_div(__isl_keep isl_basic_map *bmap, unsigned n);
+__isl_give isl_basic_map *isl_basic_map_drop_div(
+ __isl_take isl_basic_map *bmap, unsigned div);
+void isl_basic_map_inequality_to_equality(
+ __isl_keep isl_basic_map *bmap, unsigned pos);
+int isl_basic_map_drop_equality(__isl_keep isl_basic_map *bmap, unsigned pos);
+int isl_basic_set_drop_inequality(__isl_keep isl_basic_set *bset, unsigned pos);
+int isl_basic_map_drop_inequality(__isl_keep isl_basic_map *bmap, unsigned pos);
+__isl_give isl_basic_set *isl_basic_set_add_eq(__isl_take isl_basic_set *bset,
+ isl_int *eq);
+__isl_give isl_basic_map *isl_basic_map_add_eq(__isl_take isl_basic_map *bmap,
+ isl_int *eq);
+__isl_give isl_basic_set *isl_basic_set_add_ineq(__isl_take isl_basic_set *bset,
+ isl_int *ineq);
+__isl_give isl_basic_map *isl_basic_map_add_ineq(__isl_take isl_basic_map *bmap,
+ isl_int *ineq);
+
+__isl_give isl_basic_set *isl_basic_set_tighten_outward(
+ __isl_take isl_basic_set *bset, __isl_keep isl_vec *vec);
+
+__isl_give isl_basic_map *isl_inequality_negate(__isl_take isl_basic_map *bmap,
+ unsigned pos);
+
+isl_bool isl_basic_map_has_single_reference(__isl_keep isl_basic_map *bmap);
+__isl_give isl_basic_set *isl_basic_set_cow(__isl_take isl_basic_set *bset);
+__isl_give isl_basic_map *isl_basic_map_cow(__isl_take isl_basic_map *bmap);
+__isl_give isl_set *isl_set_cow(__isl_take isl_set *set);
+__isl_give isl_map *isl_map_cow(__isl_take isl_map *map);
+
+uint32_t isl_basic_map_get_hash(__isl_keep isl_basic_map *bmap);
+
+__isl_give isl_set *isl_basic_set_list_union(
+ __isl_take isl_basic_set_list *list);
+
+__isl_give isl_basic_map *isl_basic_map_set_to_empty(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_set *isl_basic_set_set_to_empty(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_map *isl_basic_map_swap_div(__isl_take isl_basic_map *bmap,
+ int a, int b);
+__isl_give isl_basic_map *isl_basic_map_order_divs(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_map *isl_map_order_divs(__isl_take isl_map *map);
+__isl_give isl_basic_map *isl_basic_map_align_divs(
+ __isl_take isl_basic_map *dst, __isl_keep isl_basic_map *src);
+__isl_give isl_map *isl_map_align_divs_to_basic_map_list(
+ __isl_take isl_map *map, __isl_keep isl_basic_map_list *list);
+__isl_give isl_basic_map_list *isl_basic_map_list_align_divs_to_basic_map(
+ __isl_take isl_basic_map_list *list, __isl_keep isl_basic_map *bmap);
+__isl_give isl_map *isl_map_align_divs_internal(__isl_take isl_map *map);
+__isl_give isl_basic_set *isl_basic_set_sort_divs(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_map *isl_basic_map_sort_divs(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_map *isl_map_sort_divs(__isl_take isl_map *map);
+__isl_give isl_basic_map *isl_basic_map_gauss5(__isl_take isl_basic_map *bmap,
+ int *progress,
+ isl_stat (*swap)(unsigned a, unsigned b, void *user),
+ isl_stat (*drop)(unsigned n, void *user), void *user);
+__isl_give isl_basic_map *isl_basic_map_gauss(__isl_take isl_basic_map *bmap,
+ int *progress);
+__isl_give isl_basic_set *isl_basic_set_gauss(
+ __isl_take isl_basic_set *bset, int *progress);
+int isl_basic_map_constraint_cmp(__isl_keep isl_basic_map *bmap,
+ isl_int *c1, isl_int *c2);
+__isl_give isl_basic_map *isl_basic_map_sort_constraints(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_set *isl_basic_set_sort_constraints(
+ __isl_take isl_basic_set *bset);
+int isl_basic_map_plain_cmp(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2);
+isl_bool isl_basic_map_plain_is_equal(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2);
+__isl_give isl_basic_map *isl_basic_map_normalize_constraints(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_set *isl_basic_set_normalize_constraints(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_map *isl_basic_map_implicit_equalities(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_set *isl_basic_map_underlying_set(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_set *isl_basic_set_underlying_set(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_set_list *isl_basic_map_list_underlying_set(
+ __isl_take isl_basic_map_list *list);
+__isl_give isl_set *isl_map_underlying_set(__isl_take isl_map *map);
+__isl_give isl_basic_map *isl_basic_map_overlying_set(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_map *like);
+__isl_give isl_basic_set *isl_basic_set_drop_constraints_involving_unknown_divs(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_map *isl_basic_map_drop_constraints_involving_unknown_divs(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_map *isl_map_drop_constraints_involving_unknown_divs(
+ __isl_take isl_map *map);
+__isl_give isl_basic_map *isl_basic_map_drop_constraints_involving(
+ __isl_take isl_basic_map *bmap, unsigned first, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_drop_constraints_involving(
+ __isl_take isl_basic_set *bset, unsigned first, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_drop(__isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_basic_map *isl_basic_map_drop(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_set *isl_set_drop(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_drop_dims(
+ __isl_take isl_basic_set *bset, unsigned first, unsigned n);
+__isl_give isl_map *isl_map_drop(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_basic_map *isl_basic_map_drop_unrelated_constraints(
+ __isl_take isl_basic_map *bmap, __isl_take int *group);
+
+__isl_give isl_basic_map *isl_basic_map_eliminate_pure_unit_divs(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_remove_duplicate_constraints(
+ __isl_take isl_basic_map *bmap, int *progress, int detect_divs);
+__isl_give isl_basic_map *isl_basic_map_detect_inequality_pairs(
+ __isl_take isl_basic_map *bmap, int *progress);
+
+__isl_give isl_map *isl_map_remove_empty_parts(__isl_take isl_map *map);
+__isl_give isl_set *isl_set_remove_empty_parts(__isl_take isl_set *set);
+__isl_give isl_map *isl_map_remove_obvious_duplicates(__isl_take isl_map *map);
+
+__isl_give isl_set *isl_set_normalize(__isl_take isl_set *set);
+
+__isl_give isl_basic_map *isl_basic_map_eliminate_vars(
+ __isl_take isl_basic_map *bmap, unsigned pos, unsigned n);
+__isl_give isl_basic_set *isl_basic_set_eliminate_vars(
+ __isl_take isl_basic_set *bset, unsigned pos, unsigned n);
+
+__isl_give isl_map *isl_map_eliminate(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_set *isl_set_eliminate(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_map *isl_map_project_onto(__isl_take isl_map *map,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+__isl_give isl_basic_map *isl_basic_map_add_div_constraint(
+ __isl_take isl_basic_map *bmap, unsigned div, int sign);
+__isl_give isl_basic_map *isl_basic_map_add_div_constraints(
+ __isl_take isl_basic_map *bmap, unsigned div);
+__isl_give isl_basic_map *isl_basic_map_add_known_div_constraints(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_basic_map *isl_basic_map_drop_redundant_divs(
+ __isl_take isl_basic_map *bmap);
+
+__isl_give isl_basic_set *isl_basic_set_recession_cone(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_set *isl_basic_set_lineality_space(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_set *isl_set_combined_lineality_space(
+ __isl_take isl_set *set);
+
+__isl_give isl_basic_set *isl_basic_set_set_integral(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_set *isl_basic_set_set_rational(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_set *isl_set_set_rational(__isl_take isl_set *set);
+__isl_give isl_basic_map *isl_basic_map_set_rational(
+ __isl_take isl_basic_map *bmap);
+__isl_give isl_map *isl_map_set_rational(__isl_take isl_map *map);
+
+isl_bool isl_map_is_rational(__isl_keep isl_map *map);
+isl_bool isl_set_is_rational(__isl_keep isl_set *set);
+
+isl_bool isl_map_has_rational(__isl_keep isl_map *map);
+isl_bool isl_set_has_rational(__isl_keep isl_set *set);
+
+__isl_give isl_basic_map *isl_basic_map_from_multi_aff2(
+ __isl_take isl_multi_aff *maff, int rational);
+__isl_give isl_map *isl_map_from_multi_aff_internal(
+ __isl_take isl_multi_aff *ma);
+__isl_give isl_map *isl_map_from_pw_aff_internal(__isl_take isl_pw_aff *pa);
+__isl_give isl_map *isl_map_from_pw_multi_aff_internal(
+ __isl_take isl_pw_multi_aff *pma);
+
+struct isl_mat;
+
+__isl_give isl_basic_set *isl_basic_set_preimage(
+ __isl_take isl_basic_set *bset, __isl_take isl_mat *mat);
+__isl_give isl_set *isl_set_preimage(
+ __isl_take isl_set *set, __isl_take isl_mat *mat);
+
+__isl_give isl_basic_map *isl_basic_map_transform_dims(
+ __isl_take isl_basic_map *bmap, enum isl_dim_type type, unsigned first,
+ __isl_take isl_mat *trans);
+__isl_give isl_basic_set *isl_basic_set_transform_dims(
+ __isl_take isl_basic_set *bset, enum isl_dim_type type, unsigned first,
+ __isl_take isl_mat *trans);
+
+isl_int *isl_set_wrap_facet(__isl_keep isl_set *set,
+ isl_int *facet, isl_int *ridge);
+
+isl_bool isl_basic_map_contains_point(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_point *point);
+isl_bool isl_set_contains_point(__isl_keep isl_set *set,
+ __isl_keep isl_point *point);
+
+isl_stat isl_basic_set_vars_get_sign(__isl_keep isl_basic_set *bset,
+ unsigned first, unsigned n, int *signs);
+isl_stat isl_set_foreach_orthant(__isl_keep isl_set *set,
+ isl_stat (*fn)(__isl_take isl_set *orthant, int *signs, void *user),
+ void *user);
+
+isl_bool isl_basic_set_eq_is_stride(__isl_keep isl_basic_set *bset, int i);
+
+isl_bool isl_basic_map_is_div_constraint(__isl_keep isl_basic_map *bmap,
+ isl_int *constraint, unsigned div);
+
+__isl_give isl_basic_set *isl_basic_set_from_local_space(
+ __isl_take isl_local_space *ls);
+__isl_give isl_basic_map *isl_basic_map_from_local_space(
+ __isl_take isl_local_space *ls);
+__isl_give isl_basic_set *isl_basic_set_expand_divs(
+ __isl_take isl_basic_set *bset, __isl_take isl_mat *div, int *exp);
+__isl_give isl_basic_map *isl_basic_map_expand_divs(
+ __isl_take isl_basic_set *bmap, __isl_take isl_mat *div, int *exp);
+
+isl_size isl_basic_set_n_equality(__isl_keep isl_basic_set *bset);
+isl_size isl_basic_map_n_equality(__isl_keep isl_basic_map *bmap);
+isl_size isl_basic_set_n_inequality(__isl_keep isl_basic_set *bset);
+isl_size isl_basic_map_n_inequality(__isl_keep isl_basic_map *bmap);
+
+__isl_give isl_basic_map *isl_basic_map_mark_div_unknown(
+ __isl_take isl_basic_map *bmap, int div);
+isl_bool isl_basic_map_div_is_marked_unknown(__isl_keep isl_basic_map *bmap,
+ int div);
+isl_bool isl_basic_map_div_is_known(__isl_keep isl_basic_map *bmap, int div);
+int isl_basic_set_first_unknown_div(__isl_keep isl_basic_set *bset);
+int isl_basic_map_first_unknown_div(__isl_keep isl_basic_map *bmap);
+isl_bool isl_basic_map_divs_known(__isl_keep isl_basic_map *bmap);
+isl_bool isl_map_divs_known(__isl_keep isl_map *map);
+__isl_give isl_mat *isl_basic_set_get_divs(__isl_keep isl_basic_set *bset);
+__isl_give isl_mat *isl_basic_map_get_divs(__isl_keep isl_basic_map *bmap);
+
+isl_bool isl_set_every_basic_set(__isl_keep isl_set *set,
+ isl_bool (*test)(__isl_keep isl_basic_set *bset, void *user),
+ void *user);
+__isl_give isl_map *isl_map_inline_foreach_basic_map(__isl_take isl_map *map,
+ __isl_give isl_basic_map *(*fn)(__isl_take isl_basic_map *bmap));
+
+isl_stat isl_basic_set_check_no_params(__isl_keep isl_basic_set *bset);
+isl_stat isl_basic_set_check_no_locals(__isl_keep isl_basic_set *bset);
+
+isl_stat isl_basic_set_check_range(__isl_keep isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n);
+isl_stat isl_set_check_range(__isl_keep isl_set *set,
+ enum isl_dim_type type, unsigned first, unsigned n);
+isl_stat isl_basic_map_check_range(__isl_keep isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n);
+isl_stat isl_map_check_named_params(__isl_keep isl_map *map);
+
+isl_bool isl_map_has_equal_params(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2);
+isl_bool isl_basic_set_space_has_equal_params(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_space *space);
+isl_bool isl_set_space_has_equal_params(__isl_keep isl_set *set,
+ __isl_keep isl_space *space);
+isl_bool isl_map_space_has_equal_params(__isl_keep isl_map *map,
+ __isl_keep isl_space *space);
+
+isl_stat isl_map_align_params_bin(__isl_keep isl_map **map1,
+ __isl_keep isl_map **map2);
+isl_stat isl_map_align_params_set(__isl_keep isl_map **map,
+ __isl_keep isl_set **set);
+isl_bool isl_map_align_params_map_map_and_test(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2,
+ isl_bool (*fn)(__isl_keep isl_map *map1, __isl_keep isl_map *map2));
+
+__isl_give isl_set *isl_set_substitute(__isl_take isl_set *set,
+ unsigned pos, __isl_keep isl_aff *subs);
+
+__isl_give isl_set *isl_set_gist_params_basic_set(__isl_take isl_set *set,
+ __isl_take isl_basic_set *context);
+
+isl_bool isl_map_compatible_range(__isl_keep isl_map *map,
+ __isl_keep isl_set *set);
+
+isl_bool isl_basic_map_plain_is_non_empty(__isl_keep isl_basic_map *bmap);
+isl_bool isl_basic_map_plain_is_single_valued(__isl_keep isl_basic_map *bmap);
+
+isl_bool isl_map_is_set(__isl_keep isl_map *map);
+isl_bool isl_map_is_params(__isl_keep isl_map *map);
+
+isl_bool isl_basic_set_plain_dim_is_fixed(__isl_keep isl_basic_set *bset,
+ unsigned dim, isl_int *val);
+
+__isl_give isl_set *isl_set_plain_gist_basic_set(__isl_take isl_set *set,
+ __isl_take isl_basic_set *context);
+__isl_give isl_map *isl_map_plain_gist_basic_map(__isl_take isl_map *map,
+ __isl_take isl_basic_map *context);
+__isl_give isl_map *isl_map_plain_gist(__isl_take isl_map *map,
+ __isl_take isl_map *context);
+
+__isl_give isl_basic_set *isl_basic_set_plain_affine_hull(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_basic_map *isl_basic_map_plain_affine_hull(
+ __isl_take isl_basic_map *bmap);
+
+isl_stat isl_basic_set_dim_residue_class(__isl_keep isl_basic_set *bset,
+ int pos, isl_int *modulo, isl_int *residue);
+isl_stat isl_set_dim_residue_class(__isl_keep isl_set *set,
+ int pos, isl_int *modulo, isl_int *residue);
+
+__isl_give isl_basic_set *isl_basic_set_fix(__isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned pos, isl_int value);
+__isl_give isl_basic_map *isl_basic_map_fix(__isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned pos, isl_int value);
+__isl_give isl_set *isl_set_fix(__isl_take isl_set *set,
+ enum isl_dim_type type, unsigned pos, isl_int value);
+isl_bool isl_map_plain_is_fixed(__isl_keep isl_map *map,
+ enum isl_dim_type type, unsigned pos, isl_int *val);
+
+int isl_basic_map_output_defining_equality(__isl_keep isl_basic_map *bmap,
+ int pos, int *div, int *ineq);
+
+__isl_give isl_basic_map *isl_basic_map_reduce_coefficients(
+ __isl_take isl_basic_map *bmap);
+
+__isl_give isl_basic_map *isl_basic_map_shift_div(
+ __isl_take isl_basic_map *bmap, int div, int pos, isl_int shift);
+
+int isl_basic_set_count_upto(__isl_keep isl_basic_set *bset,
+ isl_int max, isl_int *count);
+int isl_set_count_upto(__isl_keep isl_set *set, isl_int max, isl_int *count);
+
+isl_bool isl_map_space_tuple_is_equal(__isl_keep isl_map *map,
+ enum isl_dim_type type1, __isl_keep isl_space *space,
+ enum isl_dim_type type2);
+isl_bool isl_map_tuple_is_equal(__isl_keep isl_map *map1,
+ enum isl_dim_type type1, __isl_keep isl_map *map2,
+ enum isl_dim_type type2);
+isl_bool isl_map_has_space(__isl_keep isl_map *map,
+ __isl_keep isl_space *space);
+isl_bool isl_map_has_space_tuples(__isl_keep isl_map *map,
+ __isl_keep isl_space *space);
+
+isl_bool isl_basic_map_is_transformation(__isl_keep isl_basic_map *bmap);
+isl_stat isl_map_check_transformation(__isl_keep isl_map *map);
+isl_stat isl_basic_set_check_equal_space(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2);
+isl_stat isl_basic_map_check_equal_space(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2);
+isl_stat isl_set_basic_set_check_equal_space(__isl_keep isl_set *set,
+ __isl_keep isl_basic_set *bset);
+isl_stat isl_map_basic_map_check_equal_space(__isl_keep isl_map *map,
+ __isl_keep isl_basic_map *bmap);
+isl_stat isl_map_check_equal_space(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2);
+
+isl_bool isl_basic_map_applies_range(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2);
+
+__isl_give isl_mat *isl_basic_set_extract_equalities(
+ __isl_keep isl_basic_set *bset);
+
+isl_bool isl_basic_map_equal_div_expr_part(__isl_keep isl_basic_map *bmap1,
+ int pos1, __isl_keep isl_basic_map *bmap2, int pos2,
+ unsigned first, unsigned n);
+isl_bool isl_basic_map_equal_div_expr_except_constant(
+ __isl_keep isl_basic_map *bmap1, int pos1,
+ __isl_keep isl_basic_map *bmap2, int pos2);
+__isl_give isl_basic_map *isl_basic_map_set_div_expr_constant_num_si_inplace(
+ __isl_take isl_basic_map *bmap, int div, int value);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_simplify.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_simplify.c
new file mode 100644
index 00000000000..9759c0df9fa
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_simplify.c
@@ -0,0 +1,5544 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2012-2013 Ecole Normale Superieure
+ * Copyright 2014-2015 INRIA Rocquencourt
+ * Copyright 2016 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include "isl_equalities.h"
+#include <isl/map.h>
+#include <isl_seq.h>
+#include "isl_tab.h"
+#include <isl_space_private.h>
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+
+#include <bset_to_bmap.c>
+#include <bset_from_bmap.c>
+#include <set_to_map.c>
+#include <set_from_map.c>
+
+static void swap_equality(__isl_keep isl_basic_map *bmap, int a, int b)
+{
+ isl_int *t = bmap->eq[a];
+ bmap->eq[a] = bmap->eq[b];
+ bmap->eq[b] = t;
+}
+
+static void swap_inequality(__isl_keep isl_basic_map *bmap, int a, int b)
+{
+ if (a != b) {
+ isl_int *t = bmap->ineq[a];
+ bmap->ineq[a] = bmap->ineq[b];
+ bmap->ineq[b] = t;
+ }
+}
+
+__isl_give isl_basic_map *isl_basic_map_normalize_constraints(
+ __isl_take isl_basic_map *bmap)
+{
+ int i;
+ isl_int gcd;
+ isl_size total = isl_basic_map_dim(bmap, isl_dim_all);
+
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+
+ isl_int_init(gcd);
+ for (i = bmap->n_eq - 1; i >= 0; --i) {
+ isl_seq_gcd(bmap->eq[i]+1, total, &gcd);
+ if (isl_int_is_zero(gcd)) {
+ if (!isl_int_is_zero(bmap->eq[i][0])) {
+ bmap = isl_basic_map_set_to_empty(bmap);
+ break;
+ }
+ if (isl_basic_map_drop_equality(bmap, i) < 0)
+ goto error;
+ continue;
+ }
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL))
+ isl_int_gcd(gcd, gcd, bmap->eq[i][0]);
+ if (isl_int_is_one(gcd))
+ continue;
+ if (!isl_int_is_divisible_by(bmap->eq[i][0], gcd)) {
+ bmap = isl_basic_map_set_to_empty(bmap);
+ break;
+ }
+ isl_seq_scale_down(bmap->eq[i], bmap->eq[i], gcd, 1+total);
+ }
+
+ for (i = bmap->n_ineq - 1; i >= 0; --i) {
+ isl_seq_gcd(bmap->ineq[i]+1, total, &gcd);
+ if (isl_int_is_zero(gcd)) {
+ if (isl_int_is_neg(bmap->ineq[i][0])) {
+ bmap = isl_basic_map_set_to_empty(bmap);
+ break;
+ }
+ if (isl_basic_map_drop_inequality(bmap, i) < 0)
+ goto error;
+ continue;
+ }
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL))
+ isl_int_gcd(gcd, gcd, bmap->ineq[i][0]);
+ if (isl_int_is_one(gcd))
+ continue;
+ isl_int_fdiv_q(bmap->ineq[i][0], bmap->ineq[i][0], gcd);
+ isl_seq_scale_down(bmap->ineq[i]+1, bmap->ineq[i]+1, gcd, total);
+ }
+ isl_int_clear(gcd);
+
+ return bmap;
+error:
+ isl_int_clear(gcd);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_normalize_constraints(
+ __isl_take isl_basic_set *bset)
+{
+ isl_basic_map *bmap = bset_to_bmap(bset);
+ return bset_from_bmap(isl_basic_map_normalize_constraints(bmap));
+}
+
+/* Reduce the coefficient of the variable at position "pos"
+ * in integer division "div", such that it lies in the half-open
+ * interval (1/2,1/2], extracting any excess value from this integer division.
+ * "pos" is as determined by isl_basic_map_offset, i.e., pos == 0
+ * corresponds to the constant term.
+ *
+ * That is, the integer division is of the form
+ *
+ * floor((... + (c * d + r) * x_pos + ...)/d)
+ *
+ * with -d < 2 * r <= d.
+ * Replace it by
+ *
+ * floor((... + r * x_pos + ...)/d) + c * x_pos
+ *
+ * If 2 * ((c * d + r) % d) <= d, then c = floor((c * d + r)/d).
+ * Otherwise, c = floor((c * d + r)/d) + 1.
+ *
+ * This is the same normalization that is performed by isl_aff_floor.
+ */
+static __isl_give isl_basic_map *reduce_coefficient_in_div(
+ __isl_take isl_basic_map *bmap, int div, int pos)
+{
+ isl_int shift;
+ int add_one;
+
+ isl_int_init(shift);
+ isl_int_fdiv_r(shift, bmap->div[div][1 + pos], bmap->div[div][0]);
+ isl_int_mul_ui(shift, shift, 2);
+ add_one = isl_int_gt(shift, bmap->div[div][0]);
+ isl_int_fdiv_q(shift, bmap->div[div][1 + pos], bmap->div[div][0]);
+ if (add_one)
+ isl_int_add_ui(shift, shift, 1);
+ isl_int_neg(shift, shift);
+ bmap = isl_basic_map_shift_div(bmap, div, pos, shift);
+ isl_int_clear(shift);
+
+ return bmap;
+}
+
+/* Does the coefficient of the variable at position "pos"
+ * in integer division "div" need to be reduced?
+ * That is, does it lie outside the half-open interval (1/2,1/2]?
+ * The coefficient c/d lies outside this interval if abs(2 * c) >= d and
+ * 2 * c != d.
+ */
+static isl_bool needs_reduction(__isl_keep isl_basic_map *bmap, int div,
+ int pos)
+{
+ isl_bool r;
+
+ if (isl_int_is_zero(bmap->div[div][1 + pos]))
+ return isl_bool_false;
+
+ isl_int_mul_ui(bmap->div[div][1 + pos], bmap->div[div][1 + pos], 2);
+ r = isl_int_abs_ge(bmap->div[div][1 + pos], bmap->div[div][0]) &&
+ !isl_int_eq(bmap->div[div][1 + pos], bmap->div[div][0]);
+ isl_int_divexact_ui(bmap->div[div][1 + pos],
+ bmap->div[div][1 + pos], 2);
+
+ return r;
+}
+
+/* Reduce the coefficients (including the constant term) of
+ * integer division "div", if needed.
+ * In particular, make sure all coefficients lie in
+ * the half-open interval (1/2,1/2].
+ */
+static __isl_give isl_basic_map *reduce_div_coefficients_of_div(
+ __isl_take isl_basic_map *bmap, int div)
+{
+ int i;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ for (i = 0; i < 1 + total; ++i) {
+ isl_bool reduce;
+
+ reduce = needs_reduction(bmap, div, i);
+ if (reduce < 0)
+ return isl_basic_map_free(bmap);
+ if (!reduce)
+ continue;
+ bmap = reduce_coefficient_in_div(bmap, div, i);
+ if (!bmap)
+ break;
+ }
+
+ return bmap;
+}
+
+/* Reduce the coefficients (including the constant term) of
+ * the known integer divisions, if needed
+ * In particular, make sure all coefficients lie in
+ * the half-open interval (1/2,1/2].
+ */
+static __isl_give isl_basic_map *reduce_div_coefficients(
+ __isl_take isl_basic_map *bmap)
+{
+ int i;
+
+ if (!bmap)
+ return NULL;
+ if (bmap->n_div == 0)
+ return bmap;
+
+ for (i = 0; i < bmap->n_div; ++i) {
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ bmap = reduce_div_coefficients_of_div(bmap, i);
+ if (!bmap)
+ break;
+ }
+
+ return bmap;
+}
+
+/* Remove any common factor in numerator and denominator of the div expression,
+ * not taking into account the constant term.
+ * That is, if the div is of the form
+ *
+ * floor((a + m f(x))/(m d))
+ *
+ * then replace it by
+ *
+ * floor((floor(a/m) + f(x))/d)
+ *
+ * The difference {a/m}/d in the argument satisfies 0 <= {a/m}/d < 1/d
+ * and can therefore not influence the result of the floor.
+ */
+static __isl_give isl_basic_map *normalize_div_expression(
+ __isl_take isl_basic_map *bmap, int div)
+{
+ isl_size total = isl_basic_map_dim(bmap, isl_dim_all);
+ isl_ctx *ctx = bmap->ctx;
+
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ if (isl_int_is_zero(bmap->div[div][0]))
+ return bmap;
+ isl_seq_gcd(bmap->div[div] + 2, total, &ctx->normalize_gcd);
+ isl_int_gcd(ctx->normalize_gcd, ctx->normalize_gcd, bmap->div[div][0]);
+ if (isl_int_is_one(ctx->normalize_gcd))
+ return bmap;
+ isl_int_fdiv_q(bmap->div[div][1], bmap->div[div][1],
+ ctx->normalize_gcd);
+ isl_int_divexact(bmap->div[div][0], bmap->div[div][0],
+ ctx->normalize_gcd);
+ isl_seq_scale_down(bmap->div[div] + 2, bmap->div[div] + 2,
+ ctx->normalize_gcd, total);
+
+ return bmap;
+}
+
+/* Remove any common factor in numerator and denominator of a div expression,
+ * not taking into account the constant term.
+ * That is, look for any div of the form
+ *
+ * floor((a + m f(x))/(m d))
+ *
+ * and replace it by
+ *
+ * floor((floor(a/m) + f(x))/d)
+ *
+ * The difference {a/m}/d in the argument satisfies 0 <= {a/m}/d < 1/d
+ * and can therefore not influence the result of the floor.
+ */
+static __isl_give isl_basic_map *normalize_div_expressions(
+ __isl_take isl_basic_map *bmap)
+{
+ int i;
+
+ if (!bmap)
+ return NULL;
+ if (bmap->n_div == 0)
+ return bmap;
+
+ for (i = 0; i < bmap->n_div; ++i)
+ bmap = normalize_div_expression(bmap, i);
+
+ return bmap;
+}
+
+/* Assumes divs have been ordered if keep_divs is set.
+ */
+static __isl_give isl_basic_map *eliminate_var_using_equality(
+ __isl_take isl_basic_map *bmap,
+ unsigned pos, isl_int *eq, int keep_divs, int *progress)
+{
+ isl_size total;
+ isl_size v_div;
+ int k;
+ int last_div;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (total < 0 || v_div < 0)
+ return isl_basic_map_free(bmap);
+ last_div = isl_seq_last_non_zero(eq + 1 + v_div, bmap->n_div);
+ for (k = 0; k < bmap->n_eq; ++k) {
+ if (bmap->eq[k] == eq)
+ continue;
+ if (isl_int_is_zero(bmap->eq[k][1+pos]))
+ continue;
+ if (progress)
+ *progress = 1;
+ isl_seq_elim(bmap->eq[k], eq, 1+pos, 1+total, NULL);
+ isl_seq_normalize(bmap->ctx, bmap->eq[k], 1 + total);
+ }
+
+ for (k = 0; k < bmap->n_ineq; ++k) {
+ if (isl_int_is_zero(bmap->ineq[k][1+pos]))
+ continue;
+ if (progress)
+ *progress = 1;
+ isl_seq_elim(bmap->ineq[k], eq, 1+pos, 1+total, NULL);
+ isl_seq_normalize(bmap->ctx, bmap->ineq[k], 1 + total);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_REDUNDANT);
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_SORTED);
+ }
+
+ for (k = 0; k < bmap->n_div; ++k) {
+ if (isl_int_is_zero(bmap->div[k][0]))
+ continue;
+ if (isl_int_is_zero(bmap->div[k][1+1+pos]))
+ continue;
+ if (progress)
+ *progress = 1;
+ /* We need to be careful about circular definitions,
+ * so for now we just remove the definition of div k
+ * if the equality contains any divs.
+ * If keep_divs is set, then the divs have been ordered
+ * and we can keep the definition as long as the result
+ * is still ordered.
+ */
+ if (last_div == -1 || (keep_divs && last_div < k)) {
+ isl_seq_elim(bmap->div[k]+1, eq,
+ 1+pos, 1+total, &bmap->div[k][0]);
+ bmap = normalize_div_expression(bmap, k);
+ if (!bmap)
+ return NULL;
+ } else
+ isl_seq_clr(bmap->div[k], 1 + total);
+ }
+
+ return bmap;
+}
+
+/* Assumes divs have been ordered if keep_divs is set.
+ */
+static __isl_give isl_basic_map *eliminate_div(__isl_take isl_basic_map *bmap,
+ isl_int *eq, unsigned div, int keep_divs)
+{
+ isl_size v_div;
+ unsigned pos;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0)
+ return isl_basic_map_free(bmap);
+ pos = v_div + div;
+ bmap = eliminate_var_using_equality(bmap, pos, eq, keep_divs, NULL);
+
+ bmap = isl_basic_map_drop_div(bmap, div);
+
+ return bmap;
+}
+
+/* Check if elimination of div "div" using equality "eq" would not
+ * result in a div depending on a later div.
+ */
+static isl_bool ok_to_eliminate_div(__isl_keep isl_basic_map *bmap, isl_int *eq,
+ unsigned div)
+{
+ int k;
+ int last_div;
+ isl_size v_div;
+ unsigned pos;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0)
+ return isl_bool_error;
+ pos = v_div + div;
+
+ last_div = isl_seq_last_non_zero(eq + 1 + v_div, bmap->n_div);
+ if (last_div < 0 || last_div <= div)
+ return isl_bool_true;
+
+ for (k = 0; k <= last_div; ++k) {
+ if (isl_int_is_zero(bmap->div[k][0]))
+ continue;
+ if (!isl_int_is_zero(bmap->div[k][1 + 1 + pos]))
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+/* Eliminate divs based on equalities
+ */
+static __isl_give isl_basic_map *eliminate_divs_eq(
+ __isl_take isl_basic_map *bmap, int *progress)
+{
+ int d;
+ int i;
+ int modified = 0;
+ unsigned off;
+
+ bmap = isl_basic_map_order_divs(bmap);
+
+ if (!bmap)
+ return NULL;
+
+ off = isl_basic_map_offset(bmap, isl_dim_div);
+
+ for (d = bmap->n_div - 1; d >= 0 ; --d) {
+ for (i = 0; i < bmap->n_eq; ++i) {
+ isl_bool ok;
+
+ if (!isl_int_is_one(bmap->eq[i][off + d]) &&
+ !isl_int_is_negone(bmap->eq[i][off + d]))
+ continue;
+ ok = ok_to_eliminate_div(bmap, bmap->eq[i], d);
+ if (ok < 0)
+ return isl_basic_map_free(bmap);
+ if (!ok)
+ continue;
+ modified = 1;
+ *progress = 1;
+ bmap = eliminate_div(bmap, bmap->eq[i], d, 1);
+ if (isl_basic_map_drop_equality(bmap, i) < 0)
+ return isl_basic_map_free(bmap);
+ break;
+ }
+ }
+ if (modified)
+ return eliminate_divs_eq(bmap, progress);
+ return bmap;
+}
+
+/* Eliminate divs based on inequalities
+ */
+static __isl_give isl_basic_map *eliminate_divs_ineq(
+ __isl_take isl_basic_map *bmap, int *progress)
+{
+ int d;
+ int i;
+ unsigned off;
+ struct isl_ctx *ctx;
+
+ if (!bmap)
+ return NULL;
+
+ ctx = bmap->ctx;
+ off = isl_basic_map_offset(bmap, isl_dim_div);
+
+ for (d = bmap->n_div - 1; d >= 0 ; --d) {
+ for (i = 0; i < bmap->n_eq; ++i)
+ if (!isl_int_is_zero(bmap->eq[i][off + d]))
+ break;
+ if (i < bmap->n_eq)
+ continue;
+ for (i = 0; i < bmap->n_ineq; ++i)
+ if (isl_int_abs_gt(bmap->ineq[i][off + d], ctx->one))
+ break;
+ if (i < bmap->n_ineq)
+ continue;
+ *progress = 1;
+ bmap = isl_basic_map_eliminate_vars(bmap, (off-1)+d, 1);
+ if (!bmap || ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY))
+ break;
+ bmap = isl_basic_map_drop_div(bmap, d);
+ if (!bmap)
+ break;
+ }
+ return bmap;
+}
+
+/* Does the equality constraint at position "eq" in "bmap" involve
+ * any local variables in the range [first, first + n)
+ * that are not marked as having an explicit representation?
+ */
+static isl_bool bmap_eq_involves_unknown_divs(__isl_keep isl_basic_map *bmap,
+ int eq, unsigned first, unsigned n)
+{
+ unsigned o_div;
+ int i;
+
+ if (!bmap)
+ return isl_bool_error;
+
+ o_div = isl_basic_map_offset(bmap, isl_dim_div);
+ for (i = 0; i < n; ++i) {
+ isl_bool unknown;
+
+ if (isl_int_is_zero(bmap->eq[eq][o_div + first + i]))
+ continue;
+ unknown = isl_basic_map_div_is_marked_unknown(bmap, first + i);
+ if (unknown < 0)
+ return isl_bool_error;
+ if (unknown)
+ return isl_bool_true;
+ }
+
+ return isl_bool_false;
+}
+
+/* The last local variable involved in the equality constraint
+ * at position "eq" in "bmap" is the local variable at position "div".
+ * It can therefore be used to extract an explicit representation
+ * for that variable.
+ * Do so unless the local variable already has an explicit representation or
+ * the explicit representation would involve any other local variables
+ * that in turn do not have an explicit representation.
+ * An equality constraint involving local variables without an explicit
+ * representation can be used in isl_basic_map_drop_redundant_divs
+ * to separate out an independent local variable. Introducing
+ * an explicit representation here would block this transformation,
+ * while the partial explicit representation in itself is not very useful.
+ * Set *progress if anything is changed.
+ *
+ * The equality constraint is of the form
+ *
+ * f(x) + n e >= 0
+ *
+ * with n a positive number. The explicit representation derived from
+ * this constraint is
+ *
+ * floor((-f(x))/n)
+ */
+static __isl_give isl_basic_map *set_div_from_eq(__isl_take isl_basic_map *bmap,
+ int div, int eq, int *progress)
+{
+ isl_size total;
+ unsigned o_div;
+ isl_bool involves;
+
+ if (!bmap)
+ return NULL;
+
+ if (!isl_int_is_zero(bmap->div[div][0]))
+ return bmap;
+
+ involves = bmap_eq_involves_unknown_divs(bmap, eq, 0, div);
+ if (involves < 0)
+ return isl_basic_map_free(bmap);
+ if (involves)
+ return bmap;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ o_div = isl_basic_map_offset(bmap, isl_dim_div);
+ isl_seq_neg(bmap->div[div] + 1, bmap->eq[eq], 1 + total);
+ isl_int_set_si(bmap->div[div][1 + o_div + div], 0);
+ isl_int_set(bmap->div[div][0], bmap->eq[eq][o_div + div]);
+ if (progress)
+ *progress = 1;
+
+ return bmap;
+}
+
+/* Perform fangcheng (Gaussian elimination) on the equality
+ * constraints of "bmap".
+ * That is, put them into row-echelon form, starting from the last column
+ * backward and use them to eliminate the corresponding coefficients
+ * from all constraints.
+ *
+ * If "progress" is not NULL, then it gets set if the elimination
+ * results in any changes.
+ * The elimination process may result in some equality constraints
+ * getting interchanged or removed.
+ * If "swap" or "drop" are not NULL, then they get called when
+ * two equality constraints get interchanged or
+ * when a number of final equality constraints get removed.
+ * As a special case, if the input turns out to be empty,
+ * then drop gets called with the number of removed equality
+ * constraints set to the total number of equality constraints.
+ * If "swap" or "drop" are not NULL, then the local variables (if any)
+ * are assumed to be in a valid order.
+ */
+__isl_give isl_basic_map *isl_basic_map_gauss5(__isl_take isl_basic_map *bmap,
+ int *progress,
+ isl_stat (*swap)(unsigned a, unsigned b, void *user),
+ isl_stat (*drop)(unsigned n, void *user), void *user)
+{
+ int k;
+ int done;
+ int last_var;
+ unsigned total_var;
+ isl_size total;
+ unsigned n_drop;
+
+ if (!swap && !drop)
+ bmap = isl_basic_map_order_divs(bmap);
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+
+ total_var = total - bmap->n_div;
+
+ last_var = total - 1;
+ for (done = 0; done < bmap->n_eq; ++done) {
+ for (; last_var >= 0; --last_var) {
+ for (k = done; k < bmap->n_eq; ++k)
+ if (!isl_int_is_zero(bmap->eq[k][1+last_var]))
+ break;
+ if (k < bmap->n_eq)
+ break;
+ }
+ if (last_var < 0)
+ break;
+ if (k != done) {
+ swap_equality(bmap, k, done);
+ if (swap && swap(k, done, user) < 0)
+ return isl_basic_map_free(bmap);
+ }
+ if (isl_int_is_neg(bmap->eq[done][1+last_var]))
+ isl_seq_neg(bmap->eq[done], bmap->eq[done], 1+total);
+
+ bmap = eliminate_var_using_equality(bmap, last_var,
+ bmap->eq[done], 1, progress);
+
+ if (last_var >= total_var)
+ bmap = set_div_from_eq(bmap, last_var - total_var,
+ done, progress);
+ if (!bmap)
+ return NULL;
+ }
+ if (done == bmap->n_eq)
+ return bmap;
+ for (k = done; k < bmap->n_eq; ++k) {
+ if (isl_int_is_zero(bmap->eq[k][0]))
+ continue;
+ if (drop && drop(bmap->n_eq, user) < 0)
+ return isl_basic_map_free(bmap);
+ return isl_basic_map_set_to_empty(bmap);
+ }
+ n_drop = bmap->n_eq - done;
+ bmap = isl_basic_map_free_equality(bmap, n_drop);
+ if (drop && drop(n_drop, user) < 0)
+ return isl_basic_map_free(bmap);
+ return bmap;
+}
+
+__isl_give isl_basic_map *isl_basic_map_gauss(__isl_take isl_basic_map *bmap,
+ int *progress)
+{
+ return isl_basic_map_gauss5(bmap, progress, NULL, NULL, NULL);
+}
+
+__isl_give isl_basic_set *isl_basic_set_gauss(
+ __isl_take isl_basic_set *bset, int *progress)
+{
+ return bset_from_bmap(isl_basic_map_gauss(bset_to_bmap(bset),
+ progress));
+}
+
+
+static unsigned int round_up(unsigned int v)
+{
+ int old_v = v;
+
+ while (v) {
+ old_v = v;
+ v ^= v & -v;
+ }
+ return old_v << 1;
+}
+
+/* Hash table of inequalities in a basic map.
+ * "index" is an array of addresses of inequalities in the basic map, some
+ * of which are NULL. The inequalities are hashed on the coefficients
+ * except the constant term.
+ * "size" is the number of elements in the array and is always a power of two
+ * "bits" is the number of bits need to represent an index into the array.
+ * "total" is the total dimension of the basic map.
+ */
+struct isl_constraint_index {
+ unsigned int size;
+ int bits;
+ isl_int ***index;
+ isl_size total;
+};
+
+/* Fill in the "ci" data structure for holding the inequalities of "bmap".
+ */
+static isl_stat create_constraint_index(struct isl_constraint_index *ci,
+ __isl_keep isl_basic_map *bmap)
+{
+ isl_ctx *ctx;
+
+ ci->index = NULL;
+ if (!bmap)
+ return isl_stat_error;
+ ci->total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (ci->total < 0)
+ return isl_stat_error;
+ if (bmap->n_ineq == 0)
+ return isl_stat_ok;
+ ci->size = round_up(4 * (bmap->n_ineq + 1) / 3 - 1);
+ ci->bits = ffs(ci->size) - 1;
+ ctx = isl_basic_map_get_ctx(bmap);
+ ci->index = isl_calloc_array(ctx, isl_int **, ci->size);
+ if (!ci->index)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Free the memory allocated by create_constraint_index.
+ */
+static void constraint_index_free(struct isl_constraint_index *ci)
+{
+ free(ci->index);
+}
+
+/* Return the position in ci->index that contains the address of
+ * an inequality that is equal to *ineq up to the constant term,
+ * provided this address is not identical to "ineq".
+ * If there is no such inequality, then return the position where
+ * such an inequality should be inserted.
+ */
+static int hash_index_ineq(struct isl_constraint_index *ci, isl_int **ineq)
+{
+ int h;
+ uint32_t hash = isl_seq_get_hash_bits((*ineq) + 1, ci->total, ci->bits);
+ for (h = hash; ci->index[h]; h = (h+1) % ci->size)
+ if (ineq != ci->index[h] &&
+ isl_seq_eq((*ineq) + 1, ci->index[h][0]+1, ci->total))
+ break;
+ return h;
+}
+
+/* Return the position in ci->index that contains the address of
+ * an inequality that is equal to the k'th inequality of "bmap"
+ * up to the constant term, provided it does not point to the very
+ * same inequality.
+ * If there is no such inequality, then return the position where
+ * such an inequality should be inserted.
+ */
+static int hash_index(struct isl_constraint_index *ci,
+ __isl_keep isl_basic_map *bmap, int k)
+{
+ return hash_index_ineq(ci, &bmap->ineq[k]);
+}
+
+static int set_hash_index(struct isl_constraint_index *ci,
+ __isl_keep isl_basic_set *bset, int k)
+{
+ return hash_index(ci, bset, k);
+}
+
+/* Fill in the "ci" data structure with the inequalities of "bset".
+ */
+static isl_stat setup_constraint_index(struct isl_constraint_index *ci,
+ __isl_keep isl_basic_set *bset)
+{
+ int k, h;
+
+ if (create_constraint_index(ci, bset) < 0)
+ return isl_stat_error;
+
+ for (k = 0; k < bset->n_ineq; ++k) {
+ h = set_hash_index(ci, bset, k);
+ ci->index[h] = &bset->ineq[k];
+ }
+
+ return isl_stat_ok;
+}
+
+/* Is the inequality ineq (obviously) redundant with respect
+ * to the constraints in "ci"?
+ *
+ * Look for an inequality in "ci" with the same coefficients and then
+ * check if the contant term of "ineq" is greater than or equal
+ * to the constant term of that inequality. If so, "ineq" is clearly
+ * redundant.
+ *
+ * Note that hash_index_ineq ignores a stored constraint if it has
+ * the same address as the passed inequality. It is ok to pass
+ * the address of a local variable here since it will never be
+ * the same as the address of a constraint in "ci".
+ */
+static isl_bool constraint_index_is_redundant(struct isl_constraint_index *ci,
+ isl_int *ineq)
+{
+ int h;
+
+ h = hash_index_ineq(ci, &ineq);
+ if (!ci->index[h])
+ return isl_bool_false;
+ return isl_int_ge(ineq[0], (*ci->index[h])[0]);
+}
+
+/* If we can eliminate more than one div, then we need to make
+ * sure we do it from last div to first div, in order not to
+ * change the position of the other divs that still need to
+ * be removed.
+ */
+static __isl_give isl_basic_map *remove_duplicate_divs(
+ __isl_take isl_basic_map *bmap, int *progress)
+{
+ unsigned int size;
+ int *index;
+ int *elim_for;
+ int k, l, h;
+ int bits;
+ struct isl_blk eq;
+ isl_size v_div;
+ unsigned total;
+ struct isl_ctx *ctx;
+
+ bmap = isl_basic_map_order_divs(bmap);
+ if (!bmap || bmap->n_div <= 1)
+ return bmap;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0)
+ return isl_basic_map_free(bmap);
+ total = v_div + bmap->n_div;
+
+ ctx = bmap->ctx;
+ for (k = bmap->n_div - 1; k >= 0; --k)
+ if (!isl_int_is_zero(bmap->div[k][0]))
+ break;
+ if (k <= 0)
+ return bmap;
+
+ size = round_up(4 * bmap->n_div / 3 - 1);
+ if (size == 0)
+ return bmap;
+ elim_for = isl_calloc_array(ctx, int, bmap->n_div);
+ bits = ffs(size) - 1;
+ index = isl_calloc_array(ctx, int, size);
+ if (!elim_for || !index)
+ goto out;
+ eq = isl_blk_alloc(ctx, 1+total);
+ if (isl_blk_is_error(eq))
+ goto out;
+
+ isl_seq_clr(eq.data, 1+total);
+ index[isl_seq_get_hash_bits(bmap->div[k], 2+total, bits)] = k + 1;
+ for (--k; k >= 0; --k) {
+ uint32_t hash;
+
+ if (isl_int_is_zero(bmap->div[k][0]))
+ continue;
+
+ hash = isl_seq_get_hash_bits(bmap->div[k], 2+total, bits);
+ for (h = hash; index[h]; h = (h+1) % size)
+ if (isl_seq_eq(bmap->div[k],
+ bmap->div[index[h]-1], 2+total))
+ break;
+ if (index[h]) {
+ *progress = 1;
+ l = index[h] - 1;
+ elim_for[l] = k + 1;
+ }
+ index[h] = k+1;
+ }
+ for (l = bmap->n_div - 1; l >= 0; --l) {
+ if (!elim_for[l])
+ continue;
+ k = elim_for[l] - 1;
+ isl_int_set_si(eq.data[1 + v_div + k], -1);
+ isl_int_set_si(eq.data[1 + v_div + l], 1);
+ bmap = eliminate_div(bmap, eq.data, l, 1);
+ if (!bmap)
+ break;
+ isl_int_set_si(eq.data[1 + v_div + k], 0);
+ isl_int_set_si(eq.data[1 + v_div + l], 0);
+ }
+
+ isl_blk_free(ctx, eq);
+out:
+ free(index);
+ free(elim_for);
+ return bmap;
+}
+
+static int n_pure_div_eq(__isl_keep isl_basic_map *bmap)
+{
+ int i, j;
+ isl_size v_div;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0)
+ return -1;
+ for (i = 0, j = bmap->n_div-1; i < bmap->n_eq; ++i) {
+ while (j >= 0 && isl_int_is_zero(bmap->eq[i][1 + v_div + j]))
+ --j;
+ if (j < 0)
+ break;
+ if (isl_seq_first_non_zero(bmap->eq[i] + 1 + v_div, j) != -1)
+ return 0;
+ }
+ return i;
+}
+
+/* Normalize divs that appear in equalities.
+ *
+ * In particular, we assume that bmap contains some equalities
+ * of the form
+ *
+ * a x = m * e_i
+ *
+ * and we want to replace the set of e_i by a minimal set and
+ * such that the new e_i have a canonical representation in terms
+ * of the vector x.
+ * If any of the equalities involves more than one divs, then
+ * we currently simply bail out.
+ *
+ * Let us first additionally assume that all equalities involve
+ * a div. The equalities then express modulo constraints on the
+ * remaining variables and we can use "parameter compression"
+ * to find a minimal set of constraints. The result is a transformation
+ *
+ * x = T(x') = x_0 + G x'
+ *
+ * with G a lower-triangular matrix with all elements below the diagonal
+ * non-negative and smaller than the diagonal element on the same row.
+ * We first normalize x_0 by making the same property hold in the affine
+ * T matrix.
+ * The rows i of G with a 1 on the diagonal do not impose any modulo
+ * constraint and simply express x_i = x'_i.
+ * For each of the remaining rows i, we introduce a div and a corresponding
+ * equality. In particular
+ *
+ * g_ii e_j = x_i - g_i(x')
+ *
+ * where each x'_k is replaced either by x_k (if g_kk = 1) or the
+ * corresponding div (if g_kk != 1).
+ *
+ * If there are any equalities not involving any div, then we
+ * first apply a variable compression on the variables x:
+ *
+ * x = C x'' x'' = C_2 x
+ *
+ * and perform the above parameter compression on A C instead of on A.
+ * The resulting compression is then of the form
+ *
+ * x'' = T(x') = x_0 + G x'
+ *
+ * and in constructing the new divs and the corresponding equalities,
+ * we have to replace each x'', i.e., the x'_k with (g_kk = 1),
+ * by the corresponding row from C_2.
+ */
+static __isl_give isl_basic_map *normalize_divs(__isl_take isl_basic_map *bmap,
+ int *progress)
+{
+ int i, j, k;
+ isl_size v_div;
+ int div_eq;
+ struct isl_mat *B;
+ struct isl_vec *d;
+ struct isl_mat *T = NULL;
+ struct isl_mat *C = NULL;
+ struct isl_mat *C2 = NULL;
+ isl_int v;
+ int *pos = NULL;
+ int dropped, needed;
+
+ if (!bmap)
+ return NULL;
+
+ if (bmap->n_div == 0)
+ return bmap;
+
+ if (bmap->n_eq == 0)
+ return bmap;
+
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS))
+ return bmap;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ div_eq = n_pure_div_eq(bmap);
+ if (v_div < 0 || div_eq < 0)
+ return isl_basic_map_free(bmap);
+ if (div_eq == 0)
+ return bmap;
+
+ if (div_eq < bmap->n_eq) {
+ B = isl_mat_sub_alloc6(bmap->ctx, bmap->eq, div_eq,
+ bmap->n_eq - div_eq, 0, 1 + v_div);
+ C = isl_mat_variable_compression(B, &C2);
+ if (!C || !C2)
+ goto error;
+ if (C->n_col == 0) {
+ bmap = isl_basic_map_set_to_empty(bmap);
+ isl_mat_free(C);
+ isl_mat_free(C2);
+ goto done;
+ }
+ }
+
+ d = isl_vec_alloc(bmap->ctx, div_eq);
+ if (!d)
+ goto error;
+ for (i = 0, j = bmap->n_div-1; i < div_eq; ++i) {
+ while (j >= 0 && isl_int_is_zero(bmap->eq[i][1 + v_div + j]))
+ --j;
+ isl_int_set(d->block.data[i], bmap->eq[i][1 + v_div + j]);
+ }
+ B = isl_mat_sub_alloc6(bmap->ctx, bmap->eq, 0, div_eq, 0, 1 + v_div);
+
+ if (C) {
+ B = isl_mat_product(B, C);
+ C = NULL;
+ }
+
+ T = isl_mat_parameter_compression(B, d);
+ if (!T)
+ goto error;
+ if (T->n_col == 0) {
+ bmap = isl_basic_map_set_to_empty(bmap);
+ isl_mat_free(C2);
+ isl_mat_free(T);
+ goto done;
+ }
+ isl_int_init(v);
+ for (i = 0; i < T->n_row - 1; ++i) {
+ isl_int_fdiv_q(v, T->row[1 + i][0], T->row[1 + i][1 + i]);
+ if (isl_int_is_zero(v))
+ continue;
+ isl_mat_col_submul(T, 0, v, 1 + i);
+ }
+ isl_int_clear(v);
+ pos = isl_alloc_array(bmap->ctx, int, T->n_row);
+ if (!pos)
+ goto error;
+ /* We have to be careful because dropping equalities may reorder them */
+ dropped = 0;
+ for (j = bmap->n_div - 1; j >= 0; --j) {
+ for (i = 0; i < bmap->n_eq; ++i)
+ if (!isl_int_is_zero(bmap->eq[i][1 + v_div + j]))
+ break;
+ if (i < bmap->n_eq) {
+ bmap = isl_basic_map_drop_div(bmap, j);
+ if (isl_basic_map_drop_equality(bmap, i) < 0)
+ goto error;
+ ++dropped;
+ }
+ }
+ pos[0] = 0;
+ needed = 0;
+ for (i = 1; i < T->n_row; ++i) {
+ if (isl_int_is_one(T->row[i][i]))
+ pos[i] = i;
+ else
+ needed++;
+ }
+ if (needed > dropped) {
+ bmap = isl_basic_map_extend(bmap, needed, needed, 0);
+ if (!bmap)
+ goto error;
+ }
+ for (i = 1; i < T->n_row; ++i) {
+ if (isl_int_is_one(T->row[i][i]))
+ continue;
+ k = isl_basic_map_alloc_div(bmap);
+ pos[i] = 1 + v_div + k;
+ isl_seq_clr(bmap->div[k] + 1, 1 + v_div + bmap->n_div);
+ isl_int_set(bmap->div[k][0], T->row[i][i]);
+ if (C2)
+ isl_seq_cpy(bmap->div[k] + 1, C2->row[i], 1 + v_div);
+ else
+ isl_int_set_si(bmap->div[k][1 + i], 1);
+ for (j = 0; j < i; ++j) {
+ if (isl_int_is_zero(T->row[i][j]))
+ continue;
+ if (pos[j] < T->n_row && C2)
+ isl_seq_submul(bmap->div[k] + 1, T->row[i][j],
+ C2->row[pos[j]], 1 + v_div);
+ else
+ isl_int_neg(bmap->div[k][1 + pos[j]],
+ T->row[i][j]);
+ }
+ j = isl_basic_map_alloc_equality(bmap);
+ isl_seq_neg(bmap->eq[j], bmap->div[k]+1, 1+v_div+bmap->n_div);
+ isl_int_set(bmap->eq[j][pos[i]], bmap->div[k][0]);
+ }
+ free(pos);
+ isl_mat_free(C2);
+ isl_mat_free(T);
+
+ if (progress)
+ *progress = 1;
+done:
+ ISL_F_SET(bmap, ISL_BASIC_MAP_NORMALIZED_DIVS);
+
+ return bmap;
+error:
+ free(pos);
+ isl_mat_free(C);
+ isl_mat_free(C2);
+ isl_mat_free(T);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+static __isl_give isl_basic_map *set_div_from_lower_bound(
+ __isl_take isl_basic_map *bmap, int div, int ineq)
+{
+ unsigned total = isl_basic_map_offset(bmap, isl_dim_div);
+
+ isl_seq_neg(bmap->div[div] + 1, bmap->ineq[ineq], total + bmap->n_div);
+ isl_int_set(bmap->div[div][0], bmap->ineq[ineq][total + div]);
+ isl_int_add(bmap->div[div][1], bmap->div[div][1], bmap->div[div][0]);
+ isl_int_sub_ui(bmap->div[div][1], bmap->div[div][1], 1);
+ isl_int_set_si(bmap->div[div][1 + total + div], 0);
+
+ return bmap;
+}
+
+/* Check whether it is ok to define a div based on an inequality.
+ * To avoid the introduction of circular definitions of divs, we
+ * do not allow such a definition if the resulting expression would refer to
+ * any other undefined divs or if any known div is defined in
+ * terms of the unknown div.
+ */
+static isl_bool ok_to_set_div_from_bound(__isl_keep isl_basic_map *bmap,
+ int div, int ineq)
+{
+ int j;
+ unsigned total = isl_basic_map_offset(bmap, isl_dim_div);
+
+ /* Not defined in terms of unknown divs */
+ for (j = 0; j < bmap->n_div; ++j) {
+ if (div == j)
+ continue;
+ if (isl_int_is_zero(bmap->ineq[ineq][total + j]))
+ continue;
+ if (isl_int_is_zero(bmap->div[j][0]))
+ return isl_bool_false;
+ }
+
+ /* No other div defined in terms of this one => avoid loops */
+ for (j = 0; j < bmap->n_div; ++j) {
+ if (div == j)
+ continue;
+ if (isl_int_is_zero(bmap->div[j][0]))
+ continue;
+ if (!isl_int_is_zero(bmap->div[j][1 + total + div]))
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+/* Would an expression for div "div" based on inequality "ineq" of "bmap"
+ * be a better expression than the current one?
+ *
+ * If we do not have any expression yet, then any expression would be better.
+ * Otherwise we check if the last variable involved in the inequality
+ * (disregarding the div that it would define) is in an earlier position
+ * than the last variable involved in the current div expression.
+ */
+static isl_bool better_div_constraint(__isl_keep isl_basic_map *bmap,
+ int div, int ineq)
+{
+ unsigned total = isl_basic_map_offset(bmap, isl_dim_div);
+ int last_div;
+ int last_ineq;
+
+ if (isl_int_is_zero(bmap->div[div][0]))
+ return isl_bool_true;
+
+ if (isl_seq_last_non_zero(bmap->ineq[ineq] + total + div + 1,
+ bmap->n_div - (div + 1)) >= 0)
+ return isl_bool_false;
+
+ last_ineq = isl_seq_last_non_zero(bmap->ineq[ineq], total + div);
+ last_div = isl_seq_last_non_zero(bmap->div[div] + 1,
+ total + bmap->n_div);
+
+ return last_ineq < last_div;
+}
+
+/* Given two constraints "k" and "l" that are opposite to each other,
+ * except for the constant term, check if we can use them
+ * to obtain an expression for one of the hitherto unknown divs or
+ * a "better" expression for a div for which we already have an expression.
+ * "sum" is the sum of the constant terms of the constraints.
+ * If this sum is strictly smaller than the coefficient of one
+ * of the divs, then this pair can be used define the div.
+ * To avoid the introduction of circular definitions of divs, we
+ * do not use the pair if the resulting expression would refer to
+ * any other undefined divs or if any known div is defined in
+ * terms of the unknown div.
+ */
+static __isl_give isl_basic_map *check_for_div_constraints(
+ __isl_take isl_basic_map *bmap, int k, int l, isl_int sum,
+ int *progress)
+{
+ int i;
+ unsigned total = isl_basic_map_offset(bmap, isl_dim_div);
+
+ for (i = 0; i < bmap->n_div; ++i) {
+ isl_bool set_div;
+
+ if (isl_int_is_zero(bmap->ineq[k][total + i]))
+ continue;
+ if (isl_int_abs_ge(sum, bmap->ineq[k][total + i]))
+ continue;
+ set_div = better_div_constraint(bmap, i, k);
+ if (set_div >= 0 && set_div)
+ set_div = ok_to_set_div_from_bound(bmap, i, k);
+ if (set_div < 0)
+ return isl_basic_map_free(bmap);
+ if (!set_div)
+ break;
+ if (isl_int_is_pos(bmap->ineq[k][total + i]))
+ bmap = set_div_from_lower_bound(bmap, i, k);
+ else
+ bmap = set_div_from_lower_bound(bmap, i, l);
+ if (progress)
+ *progress = 1;
+ break;
+ }
+ return bmap;
+}
+
+__isl_give isl_basic_map *isl_basic_map_remove_duplicate_constraints(
+ __isl_take isl_basic_map *bmap, int *progress, int detect_divs)
+{
+ struct isl_constraint_index ci;
+ int k, l, h;
+ isl_size total = isl_basic_map_dim(bmap, isl_dim_all);
+ isl_int sum;
+
+ if (total < 0 || bmap->n_ineq <= 1)
+ return bmap;
+
+ if (create_constraint_index(&ci, bmap) < 0)
+ return bmap;
+
+ h = isl_seq_get_hash_bits(bmap->ineq[0] + 1, total, ci.bits);
+ ci.index[h] = &bmap->ineq[0];
+ for (k = 1; k < bmap->n_ineq; ++k) {
+ h = hash_index(&ci, bmap, k);
+ if (!ci.index[h]) {
+ ci.index[h] = &bmap->ineq[k];
+ continue;
+ }
+ if (progress)
+ *progress = 1;
+ l = ci.index[h] - &bmap->ineq[0];
+ if (isl_int_lt(bmap->ineq[k][0], bmap->ineq[l][0]))
+ swap_inequality(bmap, k, l);
+ isl_basic_map_drop_inequality(bmap, k);
+ --k;
+ }
+ isl_int_init(sum);
+ for (k = 0; bmap && k < bmap->n_ineq-1; ++k) {
+ isl_seq_neg(bmap->ineq[k]+1, bmap->ineq[k]+1, total);
+ h = hash_index(&ci, bmap, k);
+ isl_seq_neg(bmap->ineq[k]+1, bmap->ineq[k]+1, total);
+ if (!ci.index[h])
+ continue;
+ l = ci.index[h] - &bmap->ineq[0];
+ isl_int_add(sum, bmap->ineq[k][0], bmap->ineq[l][0]);
+ if (isl_int_is_pos(sum)) {
+ if (detect_divs)
+ bmap = check_for_div_constraints(bmap, k, l,
+ sum, progress);
+ continue;
+ }
+ if (isl_int_is_zero(sum)) {
+ /* We need to break out of the loop after these
+ * changes since the contents of the hash
+ * will no longer be valid.
+ * Plus, we probably we want to regauss first.
+ */
+ if (progress)
+ *progress = 1;
+ isl_basic_map_drop_inequality(bmap, l);
+ isl_basic_map_inequality_to_equality(bmap, k);
+ } else
+ bmap = isl_basic_map_set_to_empty(bmap);
+ break;
+ }
+ isl_int_clear(sum);
+
+ constraint_index_free(&ci);
+ return bmap;
+}
+
+/* Detect all pairs of inequalities that form an equality.
+ *
+ * isl_basic_map_remove_duplicate_constraints detects at most one such pair.
+ * Call it repeatedly while it is making progress.
+ */
+__isl_give isl_basic_map *isl_basic_map_detect_inequality_pairs(
+ __isl_take isl_basic_map *bmap, int *progress)
+{
+ int duplicate;
+
+ do {
+ duplicate = 0;
+ bmap = isl_basic_map_remove_duplicate_constraints(bmap,
+ &duplicate, 0);
+ if (progress && duplicate)
+ *progress = 1;
+ } while (duplicate);
+
+ return bmap;
+}
+
+/* Given a known integer division "div" that is not integral
+ * (with denominator 1), eliminate it from the constraints in "bmap"
+ * where it appears with a (positive or negative) unit coefficient.
+ * If "progress" is not NULL, then it gets set if the elimination
+ * results in any changes.
+ *
+ * That is, replace
+ *
+ * floor(e/m) + f >= 0
+ *
+ * by
+ *
+ * e + m f >= 0
+ *
+ * and
+ *
+ * -floor(e/m) + f >= 0
+ *
+ * by
+ *
+ * -e + m f + m - 1 >= 0
+ *
+ * The first conversion is valid because floor(e/m) >= -f is equivalent
+ * to e/m >= -f because -f is an integral expression.
+ * The second conversion follows from the fact that
+ *
+ * -floor(e/m) = ceil(-e/m) = floor((-e + m - 1)/m)
+ *
+ *
+ * Note that one of the div constraints may have been eliminated
+ * due to being redundant with respect to the constraint that is
+ * being modified by this function. The modified constraint may
+ * no longer imply this div constraint, so we add it back to make
+ * sure we do not lose any information.
+ */
+static __isl_give isl_basic_map *eliminate_unit_div(
+ __isl_take isl_basic_map *bmap, int div, int *progress)
+{
+ int j;
+ isl_size v_div, dim;
+ isl_ctx *ctx;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ dim = isl_basic_map_dim(bmap, isl_dim_all);
+ if (v_div < 0 || dim < 0)
+ return isl_basic_map_free(bmap);
+
+ ctx = isl_basic_map_get_ctx(bmap);
+
+ for (j = 0; j < bmap->n_ineq; ++j) {
+ int s;
+
+ if (!isl_int_is_one(bmap->ineq[j][1 + v_div + div]) &&
+ !isl_int_is_negone(bmap->ineq[j][1 + v_div + div]))
+ continue;
+
+ if (progress)
+ *progress = 1;
+
+ s = isl_int_sgn(bmap->ineq[j][1 + v_div + div]);
+ isl_int_set_si(bmap->ineq[j][1 + v_div + div], 0);
+ if (s < 0)
+ isl_seq_combine(bmap->ineq[j],
+ ctx->negone, bmap->div[div] + 1,
+ bmap->div[div][0], bmap->ineq[j], 1 + dim);
+ else
+ isl_seq_combine(bmap->ineq[j],
+ ctx->one, bmap->div[div] + 1,
+ bmap->div[div][0], bmap->ineq[j], 1 + dim);
+ if (s < 0) {
+ isl_int_add(bmap->ineq[j][0],
+ bmap->ineq[j][0], bmap->div[div][0]);
+ isl_int_sub_ui(bmap->ineq[j][0],
+ bmap->ineq[j][0], 1);
+ }
+
+ bmap = isl_basic_map_extend_constraints(bmap, 0, 1);
+ bmap = isl_basic_map_add_div_constraint(bmap, div, s);
+ if (!bmap)
+ return NULL;
+ }
+
+ return bmap;
+}
+
+/* Eliminate selected known divs from constraints where they appear with
+ * a (positive or negative) unit coefficient.
+ * In particular, only handle those for which "select" returns isl_bool_true.
+ * If "progress" is not NULL, then it gets set if the elimination
+ * results in any changes.
+ *
+ * We skip integral divs, i.e., those with denominator 1, as we would
+ * risk eliminating the div from the div constraints. We do not need
+ * to handle those divs here anyway since the div constraints will turn
+ * out to form an equality and this equality can then be used to eliminate
+ * the div from all constraints.
+ */
+static __isl_give isl_basic_map *eliminate_selected_unit_divs(
+ __isl_take isl_basic_map *bmap,
+ isl_bool (*select)(__isl_keep isl_basic_map *bmap, int div),
+ int *progress)
+{
+ int i;
+
+ if (!bmap)
+ return NULL;
+
+ for (i = 0; i < bmap->n_div; ++i) {
+ isl_bool selected;
+
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ if (isl_int_is_one(bmap->div[i][0]))
+ continue;
+ selected = select(bmap, i);
+ if (selected < 0)
+ return isl_basic_map_free(bmap);
+ if (!selected)
+ continue;
+ bmap = eliminate_unit_div(bmap, i, progress);
+ if (!bmap)
+ return NULL;
+ }
+
+ return bmap;
+}
+
+/* eliminate_selected_unit_divs callback that selects every
+ * integer division.
+ */
+static isl_bool is_any_div(__isl_keep isl_basic_map *bmap, int div)
+{
+ return isl_bool_true;
+}
+
+/* Eliminate known divs from constraints where they appear with
+ * a (positive or negative) unit coefficient.
+ * If "progress" is not NULL, then it gets set if the elimination
+ * results in any changes.
+ */
+static __isl_give isl_basic_map *eliminate_unit_divs(
+ __isl_take isl_basic_map *bmap, int *progress)
+{
+ return eliminate_selected_unit_divs(bmap, &is_any_div, progress);
+}
+
+/* eliminate_selected_unit_divs callback that selects
+ * integer divisions that only appear with
+ * a (positive or negative) unit coefficient
+ * (outside their div constraints).
+ */
+static isl_bool is_pure_unit_div(__isl_keep isl_basic_map *bmap, int div)
+{
+ int i;
+ isl_size v_div, n_ineq;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ n_ineq = isl_basic_map_n_inequality(bmap);
+ if (v_div < 0 || n_ineq < 0)
+ return isl_bool_error;
+
+ for (i = 0; i < n_ineq; ++i) {
+ isl_bool skip;
+
+ if (isl_int_is_zero(bmap->ineq[i][1 + v_div + div]))
+ continue;
+ skip = isl_basic_map_is_div_constraint(bmap,
+ bmap->ineq[i], div);
+ if (skip < 0)
+ return isl_bool_error;
+ if (skip)
+ continue;
+ if (!isl_int_is_one(bmap->ineq[i][1 + v_div + div]) &&
+ !isl_int_is_negone(bmap->ineq[i][1 + v_div + div]))
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+/* Eliminate known divs from constraints where they appear with
+ * a (positive or negative) unit coefficient,
+ * but only if they do not appear in any other constraints
+ * (other than the div constraints).
+ */
+__isl_give isl_basic_map *isl_basic_map_eliminate_pure_unit_divs(
+ __isl_take isl_basic_map *bmap)
+{
+ return eliminate_selected_unit_divs(bmap, &is_pure_unit_div, NULL);
+}
+
+__isl_give isl_basic_map *isl_basic_map_simplify(__isl_take isl_basic_map *bmap)
+{
+ int progress = 1;
+ if (!bmap)
+ return NULL;
+ while (progress) {
+ isl_bool empty;
+
+ progress = 0;
+ empty = isl_basic_map_plain_is_empty(bmap);
+ if (empty < 0)
+ return isl_basic_map_free(bmap);
+ if (empty)
+ break;
+ bmap = isl_basic_map_normalize_constraints(bmap);
+ bmap = reduce_div_coefficients(bmap);
+ bmap = normalize_div_expressions(bmap);
+ bmap = remove_duplicate_divs(bmap, &progress);
+ bmap = eliminate_unit_divs(bmap, &progress);
+ bmap = eliminate_divs_eq(bmap, &progress);
+ bmap = eliminate_divs_ineq(bmap, &progress);
+ bmap = isl_basic_map_gauss(bmap, &progress);
+ /* requires equalities in normal form */
+ bmap = normalize_divs(bmap, &progress);
+ bmap = isl_basic_map_remove_duplicate_constraints(bmap,
+ &progress, 1);
+ if (bmap && progress)
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_REDUCED_COEFFICIENTS);
+ }
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_simplify(
+ __isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(isl_basic_map_simplify(bset_to_bmap(bset)));
+}
+
+
+isl_bool isl_basic_map_is_div_constraint(__isl_keep isl_basic_map *bmap,
+ isl_int *constraint, unsigned div)
+{
+ unsigned pos;
+
+ if (!bmap)
+ return isl_bool_error;
+
+ pos = isl_basic_map_offset(bmap, isl_dim_div) + div;
+
+ if (isl_int_eq(constraint[pos], bmap->div[div][0])) {
+ int neg;
+ isl_int_sub(bmap->div[div][1],
+ bmap->div[div][1], bmap->div[div][0]);
+ isl_int_add_ui(bmap->div[div][1], bmap->div[div][1], 1);
+ neg = isl_seq_is_neg(constraint, bmap->div[div]+1, pos);
+ isl_int_sub_ui(bmap->div[div][1], bmap->div[div][1], 1);
+ isl_int_add(bmap->div[div][1],
+ bmap->div[div][1], bmap->div[div][0]);
+ if (!neg)
+ return isl_bool_false;
+ if (isl_seq_first_non_zero(constraint+pos+1,
+ bmap->n_div-div-1) != -1)
+ return isl_bool_false;
+ } else if (isl_int_abs_eq(constraint[pos], bmap->div[div][0])) {
+ if (!isl_seq_eq(constraint, bmap->div[div]+1, pos))
+ return isl_bool_false;
+ if (isl_seq_first_non_zero(constraint+pos+1,
+ bmap->n_div-div-1) != -1)
+ return isl_bool_false;
+ } else
+ return isl_bool_false;
+
+ return isl_bool_true;
+}
+
+/* If the only constraints a div d=floor(f/m)
+ * appears in are its two defining constraints
+ *
+ * f - m d >=0
+ * -(f - (m - 1)) + m d >= 0
+ *
+ * then it can safely be removed.
+ */
+static isl_bool div_is_redundant(__isl_keep isl_basic_map *bmap, int div)
+{
+ int i;
+ isl_size v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ unsigned pos = 1 + v_div + div;
+
+ if (v_div < 0)
+ return isl_bool_error;
+
+ for (i = 0; i < bmap->n_eq; ++i)
+ if (!isl_int_is_zero(bmap->eq[i][pos]))
+ return isl_bool_false;
+
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ isl_bool red;
+
+ if (isl_int_is_zero(bmap->ineq[i][pos]))
+ continue;
+ red = isl_basic_map_is_div_constraint(bmap, bmap->ineq[i], div);
+ if (red < 0 || !red)
+ return red;
+ }
+
+ for (i = 0; i < bmap->n_div; ++i) {
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ if (!isl_int_is_zero(bmap->div[i][1+pos]))
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+/*
+ * Remove divs that don't occur in any of the constraints or other divs.
+ * These can arise when dropping constraints from a basic map or
+ * when the divs of a basic map have been temporarily aligned
+ * with the divs of another basic map.
+ */
+static __isl_give isl_basic_map *remove_redundant_divs(
+ __isl_take isl_basic_map *bmap)
+{
+ int i;
+ isl_size v_div;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0)
+ return isl_basic_map_free(bmap);
+
+ for (i = bmap->n_div-1; i >= 0; --i) {
+ isl_bool redundant;
+
+ redundant = div_is_redundant(bmap, i);
+ if (redundant < 0)
+ return isl_basic_map_free(bmap);
+ if (!redundant)
+ continue;
+ bmap = isl_basic_map_drop_constraints_involving(bmap,
+ v_div + i, 1);
+ bmap = isl_basic_map_drop_div(bmap, i);
+ }
+ return bmap;
+}
+
+/* Mark "bmap" as final, without checking for obviously redundant
+ * integer divisions. This function should be used when "bmap"
+ * is known not to involve any such integer divisions.
+ */
+__isl_give isl_basic_map *isl_basic_map_mark_final(
+ __isl_take isl_basic_map *bmap)
+{
+ if (!bmap)
+ return NULL;
+ ISL_F_SET(bmap, ISL_BASIC_SET_FINAL);
+ return bmap;
+}
+
+/* Mark "bmap" as final, after removing obviously redundant integer divisions.
+ */
+__isl_give isl_basic_map *isl_basic_map_finalize(__isl_take isl_basic_map *bmap)
+{
+ bmap = remove_redundant_divs(bmap);
+ bmap = isl_basic_map_mark_final(bmap);
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_finalize(
+ __isl_take isl_basic_set *bset)
+{
+ return bset_from_bmap(isl_basic_map_finalize(bset_to_bmap(bset)));
+}
+
+/* Remove definition of any div that is defined in terms of the given variable.
+ * The div itself is not removed. Functions such as
+ * eliminate_divs_ineq depend on the other divs remaining in place.
+ */
+static __isl_give isl_basic_map *remove_dependent_vars(
+ __isl_take isl_basic_map *bmap, int pos)
+{
+ int i;
+
+ if (!bmap)
+ return NULL;
+
+ for (i = 0; i < bmap->n_div; ++i) {
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ if (isl_int_is_zero(bmap->div[i][1+1+pos]))
+ continue;
+ bmap = isl_basic_map_mark_div_unknown(bmap, i);
+ if (!bmap)
+ return NULL;
+ }
+ return bmap;
+}
+
+/* Eliminate the specified variables from the constraints using
+ * Fourier-Motzkin. The variables themselves are not removed.
+ */
+__isl_give isl_basic_map *isl_basic_map_eliminate_vars(
+ __isl_take isl_basic_map *bmap, unsigned pos, unsigned n)
+{
+ int d;
+ int i, j, k;
+ isl_size total;
+ int need_gauss = 0;
+
+ if (n == 0)
+ return bmap;
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+
+ bmap = isl_basic_map_cow(bmap);
+ for (d = pos + n - 1; d >= 0 && d >= pos; --d)
+ bmap = remove_dependent_vars(bmap, d);
+ if (!bmap)
+ return NULL;
+
+ for (d = pos + n - 1;
+ d >= 0 && d >= total - bmap->n_div && d >= pos; --d)
+ isl_seq_clr(bmap->div[d-(total-bmap->n_div)], 2+total);
+ for (d = pos + n - 1; d >= 0 && d >= pos; --d) {
+ int n_lower, n_upper;
+ if (!bmap)
+ return NULL;
+ for (i = 0; i < bmap->n_eq; ++i) {
+ if (isl_int_is_zero(bmap->eq[i][1+d]))
+ continue;
+ bmap = eliminate_var_using_equality(bmap, d,
+ bmap->eq[i], 0, NULL);
+ if (isl_basic_map_drop_equality(bmap, i) < 0)
+ return isl_basic_map_free(bmap);
+ need_gauss = 1;
+ break;
+ }
+ if (i < bmap->n_eq)
+ continue;
+ n_lower = 0;
+ n_upper = 0;
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ if (isl_int_is_pos(bmap->ineq[i][1+d]))
+ n_lower++;
+ else if (isl_int_is_neg(bmap->ineq[i][1+d]))
+ n_upper++;
+ }
+ bmap = isl_basic_map_extend_constraints(bmap,
+ 0, n_lower * n_upper);
+ if (!bmap)
+ goto error;
+ for (i = bmap->n_ineq - 1; i >= 0; --i) {
+ int last;
+ if (isl_int_is_zero(bmap->ineq[i][1+d]))
+ continue;
+ last = -1;
+ for (j = 0; j < i; ++j) {
+ if (isl_int_is_zero(bmap->ineq[j][1+d]))
+ continue;
+ last = j;
+ if (isl_int_sgn(bmap->ineq[i][1+d]) ==
+ isl_int_sgn(bmap->ineq[j][1+d]))
+ continue;
+ k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bmap->ineq[k], bmap->ineq[i],
+ 1+total);
+ isl_seq_elim(bmap->ineq[k], bmap->ineq[j],
+ 1+d, 1+total, NULL);
+ }
+ isl_basic_map_drop_inequality(bmap, i);
+ i = last + 1;
+ }
+ if (n_lower > 0 && n_upper > 0) {
+ bmap = isl_basic_map_normalize_constraints(bmap);
+ bmap = isl_basic_map_remove_duplicate_constraints(bmap,
+ NULL, 0);
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ bmap = isl_basic_map_remove_redundancies(bmap);
+ need_gauss = 0;
+ if (!bmap)
+ goto error;
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY))
+ break;
+ }
+ }
+ if (need_gauss)
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_eliminate_vars(
+ __isl_take isl_basic_set *bset, unsigned pos, unsigned n)
+{
+ return bset_from_bmap(isl_basic_map_eliminate_vars(bset_to_bmap(bset),
+ pos, n));
+}
+
+/* Eliminate the specified n dimensions starting at first from the
+ * constraints, without removing the dimensions from the space.
+ * If the set is rational, the dimensions are eliminated using Fourier-Motzkin.
+ * Otherwise, they are projected out and the original space is restored.
+ */
+__isl_give isl_basic_map *isl_basic_map_eliminate(
+ __isl_take isl_basic_map *bmap,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ isl_space *space;
+
+ if (!bmap)
+ return NULL;
+ if (n == 0)
+ return bmap;
+
+ if (isl_basic_map_check_range(bmap, type, first, n) < 0)
+ return isl_basic_map_free(bmap);
+
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL)) {
+ first += isl_basic_map_offset(bmap, type) - 1;
+ bmap = isl_basic_map_eliminate_vars(bmap, first, n);
+ return isl_basic_map_finalize(bmap);
+ }
+
+ space = isl_basic_map_get_space(bmap);
+ bmap = isl_basic_map_project_out(bmap, type, first, n);
+ bmap = isl_basic_map_insert_dims(bmap, type, first, n);
+ bmap = isl_basic_map_reset_space(bmap, space);
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_eliminate(
+ __isl_take isl_basic_set *bset,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return isl_basic_map_eliminate(bset, type, first, n);
+}
+
+/* Remove all constraints from "bmap" that reference any unknown local
+ * variables (directly or indirectly).
+ *
+ * Dropping all constraints on a local variable will make it redundant,
+ * so it will get removed implicitly by
+ * isl_basic_map_drop_constraints_involving_dims. Some other local
+ * variables may also end up becoming redundant if they only appear
+ * in constraints together with the unknown local variable.
+ * Therefore, start over after calling
+ * isl_basic_map_drop_constraints_involving_dims.
+ */
+__isl_give isl_basic_map *isl_basic_map_drop_constraints_involving_unknown_divs(
+ __isl_take isl_basic_map *bmap)
+{
+ isl_bool known;
+ isl_size n_div;
+ int i, o_div;
+
+ known = isl_basic_map_divs_known(bmap);
+ if (known < 0)
+ return isl_basic_map_free(bmap);
+ if (known)
+ return bmap;
+
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0)
+ return isl_basic_map_free(bmap);
+ o_div = isl_basic_map_offset(bmap, isl_dim_div) - 1;
+
+ for (i = 0; i < n_div; ++i) {
+ known = isl_basic_map_div_is_known(bmap, i);
+ if (known < 0)
+ return isl_basic_map_free(bmap);
+ if (known)
+ continue;
+ bmap = remove_dependent_vars(bmap, o_div + i);
+ bmap = isl_basic_map_drop_constraints_involving_dims(bmap,
+ isl_dim_div, i, 1);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0)
+ return isl_basic_map_free(bmap);
+ i = -1;
+ }
+
+ return bmap;
+}
+
+/* Remove all constraints from "bset" that reference any unknown local
+ * variables (directly or indirectly).
+ */
+__isl_give isl_basic_set *isl_basic_set_drop_constraints_involving_unknown_divs(
+ __isl_take isl_basic_set *bset)
+{
+ isl_basic_map *bmap;
+
+ bmap = bset_to_bmap(bset);
+ bmap = isl_basic_map_drop_constraints_involving_unknown_divs(bmap);
+ return bset_from_bmap(bmap);
+}
+
+/* Remove all constraints from "map" that reference any unknown local
+ * variables (directly or indirectly).
+ *
+ * Since constraints may get dropped from the basic maps,
+ * they may no longer be disjoint from each other.
+ */
+__isl_give isl_map *isl_map_drop_constraints_involving_unknown_divs(
+ __isl_take isl_map *map)
+{
+ int i;
+ isl_bool known;
+
+ known = isl_map_divs_known(map);
+ if (known < 0)
+ return isl_map_free(map);
+ if (known)
+ return map;
+
+ map = isl_map_cow(map);
+ if (!map)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] =
+ isl_basic_map_drop_constraints_involving_unknown_divs(
+ map->p[i]);
+ if (!map->p[i])
+ return isl_map_free(map);
+ }
+
+ if (map->n > 1)
+ ISL_F_CLR(map, ISL_MAP_DISJOINT);
+
+ return map;
+}
+
+/* Don't assume equalities are in order, because align_divs
+ * may have changed the order of the divs.
+ */
+static void compute_elimination_index(__isl_keep isl_basic_map *bmap, int *elim,
+ unsigned len)
+{
+ int d, i;
+
+ for (d = 0; d < len; ++d)
+ elim[d] = -1;
+ for (i = 0; i < bmap->n_eq; ++i) {
+ for (d = len - 1; d >= 0; --d) {
+ if (isl_int_is_zero(bmap->eq[i][1+d]))
+ continue;
+ elim[d] = i;
+ break;
+ }
+ }
+}
+
+static void set_compute_elimination_index(__isl_keep isl_basic_set *bset,
+ int *elim, unsigned len)
+{
+ compute_elimination_index(bset_to_bmap(bset), elim, len);
+}
+
+static int reduced_using_equalities(isl_int *dst, isl_int *src,
+ __isl_keep isl_basic_map *bmap, int *elim, unsigned total)
+{
+ int d;
+ int copied = 0;
+
+ for (d = total - 1; d >= 0; --d) {
+ if (isl_int_is_zero(src[1+d]))
+ continue;
+ if (elim[d] == -1)
+ continue;
+ if (!copied) {
+ isl_seq_cpy(dst, src, 1 + total);
+ copied = 1;
+ }
+ isl_seq_elim(dst, bmap->eq[elim[d]], 1 + d, 1 + total, NULL);
+ }
+ return copied;
+}
+
+static int set_reduced_using_equalities(isl_int *dst, isl_int *src,
+ __isl_keep isl_basic_set *bset, int *elim, unsigned total)
+{
+ return reduced_using_equalities(dst, src,
+ bset_to_bmap(bset), elim, total);
+}
+
+static __isl_give isl_basic_set *isl_basic_set_reduce_using_equalities(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *context)
+{
+ int i;
+ int *elim;
+ isl_size dim;
+
+ if (!bset || !context)
+ goto error;
+
+ if (context->n_eq == 0) {
+ isl_basic_set_free(context);
+ return bset;
+ }
+
+ bset = isl_basic_set_cow(bset);
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ if (dim < 0)
+ goto error;
+
+ elim = isl_alloc_array(bset->ctx, int, dim);
+ if (!elim)
+ goto error;
+ set_compute_elimination_index(context, elim, dim);
+ for (i = 0; i < bset->n_eq; ++i)
+ set_reduced_using_equalities(bset->eq[i], bset->eq[i],
+ context, elim, dim);
+ for (i = 0; i < bset->n_ineq; ++i)
+ set_reduced_using_equalities(bset->ineq[i], bset->ineq[i],
+ context, elim, dim);
+ isl_basic_set_free(context);
+ free(elim);
+ bset = isl_basic_set_simplify(bset);
+ bset = isl_basic_set_finalize(bset);
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ isl_basic_set_free(context);
+ return NULL;
+}
+
+/* For each inequality in "ineq" that is a shifted (more relaxed)
+ * copy of an inequality in "context", mark the corresponding entry
+ * in "row" with -1.
+ * If an inequality only has a non-negative constant term, then
+ * mark it as well.
+ */
+static isl_stat mark_shifted_constraints(__isl_keep isl_mat *ineq,
+ __isl_keep isl_basic_set *context, int *row)
+{
+ struct isl_constraint_index ci;
+ isl_size n_ineq, cols;
+ unsigned total;
+ int k;
+
+ if (!ineq || !context)
+ return isl_stat_error;
+ if (context->n_ineq == 0)
+ return isl_stat_ok;
+ if (setup_constraint_index(&ci, context) < 0)
+ return isl_stat_error;
+
+ n_ineq = isl_mat_rows(ineq);
+ cols = isl_mat_cols(ineq);
+ if (n_ineq < 0 || cols < 0)
+ return isl_stat_error;
+ total = cols - 1;
+ for (k = 0; k < n_ineq; ++k) {
+ int l;
+ isl_bool redundant;
+
+ l = isl_seq_first_non_zero(ineq->row[k] + 1, total);
+ if (l < 0 && isl_int_is_nonneg(ineq->row[k][0])) {
+ row[k] = -1;
+ continue;
+ }
+ redundant = constraint_index_is_redundant(&ci, ineq->row[k]);
+ if (redundant < 0)
+ goto error;
+ if (!redundant)
+ continue;
+ row[k] = -1;
+ }
+ constraint_index_free(&ci);
+ return isl_stat_ok;
+error:
+ constraint_index_free(&ci);
+ return isl_stat_error;
+}
+
+static __isl_give isl_basic_set *remove_shifted_constraints(
+ __isl_take isl_basic_set *bset, __isl_keep isl_basic_set *context)
+{
+ struct isl_constraint_index ci;
+ int k;
+
+ if (!bset || !context)
+ return bset;
+
+ if (context->n_ineq == 0)
+ return bset;
+ if (setup_constraint_index(&ci, context) < 0)
+ return bset;
+
+ for (k = 0; k < bset->n_ineq; ++k) {
+ isl_bool redundant;
+
+ redundant = constraint_index_is_redundant(&ci, bset->ineq[k]);
+ if (redundant < 0)
+ goto error;
+ if (!redundant)
+ continue;
+ bset = isl_basic_set_cow(bset);
+ if (!bset)
+ goto error;
+ isl_basic_set_drop_inequality(bset, k);
+ --k;
+ }
+ constraint_index_free(&ci);
+ return bset;
+error:
+ constraint_index_free(&ci);
+ return bset;
+}
+
+/* Remove constraints from "bmap" that are identical to constraints
+ * in "context" or that are more relaxed (greater constant term).
+ *
+ * We perform the test for shifted copies on the pure constraints
+ * in remove_shifted_constraints.
+ */
+static __isl_give isl_basic_map *isl_basic_map_remove_shifted_constraints(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_map *context)
+{
+ isl_basic_set *bset, *bset_context;
+
+ if (!bmap || !context)
+ goto error;
+
+ if (bmap->n_ineq == 0 || context->n_ineq == 0) {
+ isl_basic_map_free(context);
+ return bmap;
+ }
+
+ bmap = isl_basic_map_order_divs(bmap);
+ context = isl_basic_map_align_divs(context, bmap);
+ bmap = isl_basic_map_align_divs(bmap, context);
+
+ bset = isl_basic_map_underlying_set(isl_basic_map_copy(bmap));
+ bset_context = isl_basic_map_underlying_set(context);
+ bset = remove_shifted_constraints(bset, bset_context);
+ isl_basic_set_free(bset_context);
+
+ bmap = isl_basic_map_overlying_set(bset, bmap);
+
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ isl_basic_map_free(context);
+ return NULL;
+}
+
+/* Does the (linear part of a) constraint "c" involve any of the "len"
+ * "relevant" dimensions?
+ */
+static int is_related(isl_int *c, int len, int *relevant)
+{
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ if (!relevant[i])
+ continue;
+ if (!isl_int_is_zero(c[i]))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Drop constraints from "bmap" that do not involve any of
+ * the dimensions marked "relevant".
+ */
+static __isl_give isl_basic_map *drop_unrelated_constraints(
+ __isl_take isl_basic_map *bmap, int *relevant)
+{
+ int i;
+ isl_size dim;
+
+ dim = isl_basic_map_dim(bmap, isl_dim_all);
+ if (dim < 0)
+ return isl_basic_map_free(bmap);
+ for (i = 0; i < dim; ++i)
+ if (!relevant[i])
+ break;
+ if (i >= dim)
+ return bmap;
+
+ for (i = bmap->n_eq - 1; i >= 0; --i)
+ if (!is_related(bmap->eq[i] + 1, dim, relevant)) {
+ bmap = isl_basic_map_cow(bmap);
+ if (isl_basic_map_drop_equality(bmap, i) < 0)
+ return isl_basic_map_free(bmap);
+ }
+
+ for (i = bmap->n_ineq - 1; i >= 0; --i)
+ if (!is_related(bmap->ineq[i] + 1, dim, relevant)) {
+ bmap = isl_basic_map_cow(bmap);
+ if (isl_basic_map_drop_inequality(bmap, i) < 0)
+ return isl_basic_map_free(bmap);
+ }
+
+ return bmap;
+}
+
+/* Update the groups in "group" based on the (linear part of a) constraint "c".
+ *
+ * In particular, for any variable involved in the constraint,
+ * find the actual group id from before and replace the group
+ * of the corresponding variable by the minimal group of all
+ * the variables involved in the constraint considered so far
+ * (if this minimum is smaller) or replace the minimum by this group
+ * (if the minimum is larger).
+ *
+ * At the end, all the variables in "c" will (indirectly) point
+ * to the minimal of the groups that they referred to originally.
+ */
+static void update_groups(int dim, int *group, isl_int *c)
+{
+ int j;
+ int min = dim;
+
+ for (j = 0; j < dim; ++j) {
+ if (isl_int_is_zero(c[j]))
+ continue;
+ while (group[j] >= 0 && group[group[j]] != group[j])
+ group[j] = group[group[j]];
+ if (group[j] == min)
+ continue;
+ if (group[j] < min) {
+ if (min >= 0 && min < dim)
+ group[min] = group[j];
+ min = group[j];
+ } else
+ group[group[j]] = min;
+ }
+}
+
+/* Allocate an array of groups of variables, one for each variable
+ * in "context", initialized to zero.
+ */
+static int *alloc_groups(__isl_keep isl_basic_set *context)
+{
+ isl_ctx *ctx;
+ isl_size dim;
+
+ dim = isl_basic_set_dim(context, isl_dim_set);
+ if (dim < 0)
+ return NULL;
+ ctx = isl_basic_set_get_ctx(context);
+ return isl_calloc_array(ctx, int, dim);
+}
+
+/* Drop constraints from "bmap" that only involve variables that are
+ * not related to any of the variables marked with a "-1" in "group".
+ *
+ * We construct groups of variables that collect variables that
+ * (indirectly) appear in some common constraint of "bmap".
+ * Each group is identified by the first variable in the group,
+ * except for the special group of variables that was already identified
+ * in the input as -1 (or are related to those variables).
+ * If group[i] is equal to i (or -1), then the group of i is i (or -1),
+ * otherwise the group of i is the group of group[i].
+ *
+ * We first initialize groups for the remaining variables.
+ * Then we iterate over the constraints of "bmap" and update the
+ * group of the variables in the constraint by the smallest group.
+ * Finally, we resolve indirect references to groups by running over
+ * the variables.
+ *
+ * After computing the groups, we drop constraints that do not involve
+ * any variables in the -1 group.
+ */
+__isl_give isl_basic_map *isl_basic_map_drop_unrelated_constraints(
+ __isl_take isl_basic_map *bmap, __isl_take int *group)
+{
+ isl_size dim;
+ int i;
+ int last;
+
+ dim = isl_basic_map_dim(bmap, isl_dim_all);
+ if (dim < 0)
+ return isl_basic_map_free(bmap);
+
+ last = -1;
+ for (i = 0; i < dim; ++i)
+ if (group[i] >= 0)
+ last = group[i] = i;
+ if (last < 0) {
+ free(group);
+ return bmap;
+ }
+
+ for (i = 0; i < bmap->n_eq; ++i)
+ update_groups(dim, group, bmap->eq[i] + 1);
+ for (i = 0; i < bmap->n_ineq; ++i)
+ update_groups(dim, group, bmap->ineq[i] + 1);
+
+ for (i = 0; i < dim; ++i)
+ if (group[i] >= 0)
+ group[i] = group[group[i]];
+
+ for (i = 0; i < dim; ++i)
+ group[i] = group[i] == -1;
+
+ bmap = drop_unrelated_constraints(bmap, group);
+
+ free(group);
+ return bmap;
+}
+
+/* Drop constraints from "context" that are irrelevant for computing
+ * the gist of "bset".
+ *
+ * In particular, drop constraints in variables that are not related
+ * to any of the variables involved in the constraints of "bset"
+ * in the sense that there is no sequence of constraints that connects them.
+ *
+ * We first mark all variables that appear in "bset" as belonging
+ * to a "-1" group and then continue with group_and_drop_irrelevant_constraints.
+ */
+static __isl_give isl_basic_set *drop_irrelevant_constraints(
+ __isl_take isl_basic_set *context, __isl_keep isl_basic_set *bset)
+{
+ int *group;
+ isl_size dim;
+ int i, j;
+
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ if (!context || dim < 0)
+ return isl_basic_set_free(context);
+
+ group = alloc_groups(context);
+
+ if (!group)
+ return isl_basic_set_free(context);
+
+ for (i = 0; i < dim; ++i) {
+ for (j = 0; j < bset->n_eq; ++j)
+ if (!isl_int_is_zero(bset->eq[j][1 + i]))
+ break;
+ if (j < bset->n_eq) {
+ group[i] = -1;
+ continue;
+ }
+ for (j = 0; j < bset->n_ineq; ++j)
+ if (!isl_int_is_zero(bset->ineq[j][1 + i]))
+ break;
+ if (j < bset->n_ineq)
+ group[i] = -1;
+ }
+
+ return isl_basic_map_drop_unrelated_constraints(context, group);
+}
+
+/* Drop constraints from "context" that are irrelevant for computing
+ * the gist of the inequalities "ineq".
+ * Inequalities in "ineq" for which the corresponding element of row
+ * is set to -1 have already been marked for removal and should be ignored.
+ *
+ * In particular, drop constraints in variables that are not related
+ * to any of the variables involved in "ineq"
+ * in the sense that there is no sequence of constraints that connects them.
+ *
+ * We first mark all variables that appear in "bset" as belonging
+ * to a "-1" group and then continue with group_and_drop_irrelevant_constraints.
+ */
+static __isl_give isl_basic_set *drop_irrelevant_constraints_marked(
+ __isl_take isl_basic_set *context, __isl_keep isl_mat *ineq, int *row)
+{
+ int *group;
+ isl_size dim;
+ int i, j;
+ isl_size n;
+
+ dim = isl_basic_set_dim(context, isl_dim_set);
+ n = isl_mat_rows(ineq);
+ if (dim < 0 || n < 0)
+ return isl_basic_set_free(context);
+
+ group = alloc_groups(context);
+
+ if (!group)
+ return isl_basic_set_free(context);
+
+ for (i = 0; i < dim; ++i) {
+ for (j = 0; j < n; ++j) {
+ if (row[j] < 0)
+ continue;
+ if (!isl_int_is_zero(ineq->row[j][1 + i]))
+ break;
+ }
+ if (j < n)
+ group[i] = -1;
+ }
+
+ return isl_basic_map_drop_unrelated_constraints(context, group);
+}
+
+/* Do all "n" entries of "row" contain a negative value?
+ */
+static int all_neg(int *row, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i)
+ if (row[i] >= 0)
+ return 0;
+
+ return 1;
+}
+
+/* Update the inequalities in "bset" based on the information in "row"
+ * and "tab".
+ *
+ * In particular, the array "row" contains either -1, meaning that
+ * the corresponding inequality of "bset" is redundant, or the index
+ * of an inequality in "tab".
+ *
+ * If the row entry is -1, then drop the inequality.
+ * Otherwise, if the constraint is marked redundant in the tableau,
+ * then drop the inequality. Similarly, if it is marked as an equality
+ * in the tableau, then turn the inequality into an equality and
+ * perform Gaussian elimination.
+ */
+static __isl_give isl_basic_set *update_ineq(__isl_take isl_basic_set *bset,
+ __isl_keep int *row, struct isl_tab *tab)
+{
+ int i;
+ unsigned n_ineq;
+ unsigned n_eq;
+ int found_equality = 0;
+
+ if (!bset)
+ return NULL;
+ if (tab && tab->empty)
+ return isl_basic_set_set_to_empty(bset);
+
+ n_ineq = bset->n_ineq;
+ for (i = n_ineq - 1; i >= 0; --i) {
+ if (row[i] < 0) {
+ if (isl_basic_set_drop_inequality(bset, i) < 0)
+ return isl_basic_set_free(bset);
+ continue;
+ }
+ if (!tab)
+ continue;
+ n_eq = tab->n_eq;
+ if (isl_tab_is_equality(tab, n_eq + row[i])) {
+ isl_basic_map_inequality_to_equality(bset, i);
+ found_equality = 1;
+ } else if (isl_tab_is_redundant(tab, n_eq + row[i])) {
+ if (isl_basic_set_drop_inequality(bset, i) < 0)
+ return isl_basic_set_free(bset);
+ }
+ }
+
+ if (found_equality)
+ bset = isl_basic_set_gauss(bset, NULL);
+ bset = isl_basic_set_finalize(bset);
+ return bset;
+}
+
+/* Update the inequalities in "bset" based on the information in "row"
+ * and "tab" and free all arguments (other than "bset").
+ */
+static __isl_give isl_basic_set *update_ineq_free(
+ __isl_take isl_basic_set *bset, __isl_take isl_mat *ineq,
+ __isl_take isl_basic_set *context, __isl_take int *row,
+ struct isl_tab *tab)
+{
+ isl_mat_free(ineq);
+ isl_basic_set_free(context);
+
+ bset = update_ineq(bset, row, tab);
+
+ free(row);
+ isl_tab_free(tab);
+ return bset;
+}
+
+/* Remove all information from bset that is redundant in the context
+ * of context.
+ * "ineq" contains the (possibly transformed) inequalities of "bset",
+ * in the same order.
+ * The (explicit) equalities of "bset" are assumed to have been taken
+ * into account by the transformation such that only the inequalities
+ * are relevant.
+ * "context" is assumed not to be empty.
+ *
+ * "row" keeps track of the constraint index of a "bset" inequality in "tab".
+ * A value of -1 means that the inequality is obviously redundant and may
+ * not even appear in "tab".
+ *
+ * We first mark the inequalities of "bset"
+ * that are obviously redundant with respect to some inequality in "context".
+ * Then we remove those constraints from "context" that have become
+ * irrelevant for computing the gist of "bset".
+ * Note that this removal of constraints cannot be replaced by
+ * a factorization because factors in "bset" may still be connected
+ * to each other through constraints in "context".
+ *
+ * If there are any inequalities left, we construct a tableau for
+ * the context and then add the inequalities of "bset".
+ * Before adding these inequalities, we freeze all constraints such that
+ * they won't be considered redundant in terms of the constraints of "bset".
+ * Then we detect all redundant constraints (among the
+ * constraints that weren't frozen), first by checking for redundancy in the
+ * the tableau and then by checking if replacing a constraint by its negation
+ * would lead to an empty set. This last step is fairly expensive
+ * and could be optimized by more reuse of the tableau.
+ * Finally, we update bset according to the results.
+ */
+static __isl_give isl_basic_set *uset_gist_full(__isl_take isl_basic_set *bset,
+ __isl_take isl_mat *ineq, __isl_take isl_basic_set *context)
+{
+ int i, r;
+ int *row = NULL;
+ isl_ctx *ctx;
+ isl_basic_set *combined = NULL;
+ struct isl_tab *tab = NULL;
+ unsigned n_eq, context_ineq;
+
+ if (!bset || !ineq || !context)
+ goto error;
+
+ if (bset->n_ineq == 0 || isl_basic_set_plain_is_universe(context)) {
+ isl_basic_set_free(context);
+ isl_mat_free(ineq);
+ return bset;
+ }
+
+ ctx = isl_basic_set_get_ctx(context);
+ row = isl_calloc_array(ctx, int, bset->n_ineq);
+ if (!row)
+ goto error;
+
+ if (mark_shifted_constraints(ineq, context, row) < 0)
+ goto error;
+ if (all_neg(row, bset->n_ineq))
+ return update_ineq_free(bset, ineq, context, row, NULL);
+
+ context = drop_irrelevant_constraints_marked(context, ineq, row);
+ if (!context)
+ goto error;
+ if (isl_basic_set_plain_is_universe(context))
+ return update_ineq_free(bset, ineq, context, row, NULL);
+
+ n_eq = context->n_eq;
+ context_ineq = context->n_ineq;
+ combined = isl_basic_set_cow(isl_basic_set_copy(context));
+ combined = isl_basic_set_extend_constraints(combined, 0, bset->n_ineq);
+ tab = isl_tab_from_basic_set(combined, 0);
+ for (i = 0; i < context_ineq; ++i)
+ if (isl_tab_freeze_constraint(tab, n_eq + i) < 0)
+ goto error;
+ if (isl_tab_extend_cons(tab, bset->n_ineq) < 0)
+ goto error;
+ r = context_ineq;
+ for (i = 0; i < bset->n_ineq; ++i) {
+ if (row[i] < 0)
+ continue;
+ combined = isl_basic_set_add_ineq(combined, ineq->row[i]);
+ if (isl_tab_add_ineq(tab, ineq->row[i]) < 0)
+ goto error;
+ row[i] = r++;
+ }
+ if (isl_tab_detect_implicit_equalities(tab) < 0)
+ goto error;
+ if (isl_tab_detect_redundant(tab) < 0)
+ goto error;
+ for (i = bset->n_ineq - 1; i >= 0; --i) {
+ isl_basic_set *test;
+ int is_empty;
+
+ if (row[i] < 0)
+ continue;
+ r = row[i];
+ if (tab->con[n_eq + r].is_redundant)
+ continue;
+ test = isl_basic_set_dup(combined);
+ test = isl_inequality_negate(test, r);
+ test = isl_basic_set_update_from_tab(test, tab);
+ is_empty = isl_basic_set_is_empty(test);
+ isl_basic_set_free(test);
+ if (is_empty < 0)
+ goto error;
+ if (is_empty)
+ tab->con[n_eq + r].is_redundant = 1;
+ }
+ bset = update_ineq_free(bset, ineq, context, row, tab);
+ if (bset) {
+ ISL_F_SET(bset, ISL_BASIC_SET_NO_IMPLICIT);
+ ISL_F_SET(bset, ISL_BASIC_SET_NO_REDUNDANT);
+ }
+
+ isl_basic_set_free(combined);
+ return bset;
+error:
+ free(row);
+ isl_mat_free(ineq);
+ isl_tab_free(tab);
+ isl_basic_set_free(combined);
+ isl_basic_set_free(context);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Extract the inequalities of "bset" as an isl_mat.
+ */
+static __isl_give isl_mat *extract_ineq(__isl_keep isl_basic_set *bset)
+{
+ isl_size total;
+ isl_ctx *ctx;
+ isl_mat *ineq;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ return NULL;
+
+ ctx = isl_basic_set_get_ctx(bset);
+ ineq = isl_mat_sub_alloc6(ctx, bset->ineq, 0, bset->n_ineq,
+ 0, 1 + total);
+
+ return ineq;
+}
+
+/* Remove all information from "bset" that is redundant in the context
+ * of "context", for the case where both "bset" and "context" are
+ * full-dimensional.
+ */
+static __isl_give isl_basic_set *uset_gist_uncompressed(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *context)
+{
+ isl_mat *ineq;
+
+ ineq = extract_ineq(bset);
+ return uset_gist_full(bset, ineq, context);
+}
+
+/* Replace "bset" by an empty basic set in the same space.
+ */
+static __isl_give isl_basic_set *replace_by_empty(
+ __isl_take isl_basic_set *bset)
+{
+ isl_space *space;
+
+ space = isl_basic_set_get_space(bset);
+ isl_basic_set_free(bset);
+ return isl_basic_set_empty(space);
+}
+
+/* Remove all information from "bset" that is redundant in the context
+ * of "context", for the case where the combined equalities of
+ * "bset" and "context" allow for a compression that can be obtained
+ * by preapplication of "T".
+ * If the compression of "context" is empty, meaning that "bset" and
+ * "context" do not intersect, then return the empty set.
+ *
+ * "bset" itself is not transformed by "T". Instead, the inequalities
+ * are extracted from "bset" and those are transformed by "T".
+ * uset_gist_full then determines which of the transformed inequalities
+ * are redundant with respect to the transformed "context" and removes
+ * the corresponding inequalities from "bset".
+ *
+ * After preapplying "T" to the inequalities, any common factor is
+ * removed from the coefficients. If this results in a tightening
+ * of the constant term, then the same tightening is applied to
+ * the corresponding untransformed inequality in "bset".
+ * That is, if after plugging in T, a constraint f(x) >= 0 is of the form
+ *
+ * g f'(x) + r >= 0
+ *
+ * with 0 <= r < g, then it is equivalent to
+ *
+ * f'(x) >= 0
+ *
+ * This means that f(x) >= 0 is equivalent to f(x) - r >= 0 in the affine
+ * subspace compressed by T since the latter would be transformed to
+ *
+ * g f'(x) >= 0
+ */
+static __isl_give isl_basic_set *uset_gist_compressed(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *context,
+ __isl_take isl_mat *T)
+{
+ isl_ctx *ctx;
+ isl_mat *ineq;
+ int i;
+ isl_size n_row, n_col;
+ isl_int rem;
+
+ ineq = extract_ineq(bset);
+ ineq = isl_mat_product(ineq, isl_mat_copy(T));
+ context = isl_basic_set_preimage(context, T);
+
+ if (!ineq || !context)
+ goto error;
+ if (isl_basic_set_plain_is_empty(context)) {
+ isl_mat_free(ineq);
+ isl_basic_set_free(context);
+ return replace_by_empty(bset);
+ }
+
+ ctx = isl_mat_get_ctx(ineq);
+ n_row = isl_mat_rows(ineq);
+ n_col = isl_mat_cols(ineq);
+ if (n_row < 0 || n_col < 0)
+ goto error;
+ isl_int_init(rem);
+ for (i = 0; i < n_row; ++i) {
+ isl_seq_gcd(ineq->row[i] + 1, n_col - 1, &ctx->normalize_gcd);
+ if (isl_int_is_zero(ctx->normalize_gcd))
+ continue;
+ if (isl_int_is_one(ctx->normalize_gcd))
+ continue;
+ isl_seq_scale_down(ineq->row[i] + 1, ineq->row[i] + 1,
+ ctx->normalize_gcd, n_col - 1);
+ isl_int_fdiv_r(rem, ineq->row[i][0], ctx->normalize_gcd);
+ isl_int_fdiv_q(ineq->row[i][0],
+ ineq->row[i][0], ctx->normalize_gcd);
+ if (isl_int_is_zero(rem))
+ continue;
+ bset = isl_basic_set_cow(bset);
+ if (!bset)
+ break;
+ isl_int_sub(bset->ineq[i][0], bset->ineq[i][0], rem);
+ }
+ isl_int_clear(rem);
+
+ return uset_gist_full(bset, ineq, context);
+error:
+ isl_mat_free(ineq);
+ isl_basic_set_free(context);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Project "bset" onto the variables that are involved in "template".
+ */
+static __isl_give isl_basic_set *project_onto_involved(
+ __isl_take isl_basic_set *bset, __isl_keep isl_basic_set *template)
+{
+ int i;
+ isl_size n;
+
+ n = isl_basic_set_dim(template, isl_dim_set);
+ if (n < 0 || !template)
+ return isl_basic_set_free(bset);
+
+ for (i = 0; i < n; ++i) {
+ isl_bool involved;
+
+ involved = isl_basic_set_involves_dims(template,
+ isl_dim_set, i, 1);
+ if (involved < 0)
+ return isl_basic_set_free(bset);
+ if (involved)
+ continue;
+ bset = isl_basic_set_eliminate_vars(bset, i, 1);
+ }
+
+ return bset;
+}
+
+/* Remove all information from bset that is redundant in the context
+ * of context. In particular, equalities that are linear combinations
+ * of those in context are removed. Then the inequalities that are
+ * redundant in the context of the equalities and inequalities of
+ * context are removed.
+ *
+ * First of all, we drop those constraints from "context"
+ * that are irrelevant for computing the gist of "bset".
+ * Alternatively, we could factorize the intersection of "context" and "bset".
+ *
+ * We first compute the intersection of the integer affine hulls
+ * of "bset" and "context",
+ * compute the gist inside this intersection and then reduce
+ * the constraints with respect to the equalities of the context
+ * that only involve variables already involved in the input.
+ * If the intersection of the affine hulls turns out to be empty,
+ * then return the empty set.
+ *
+ * If two constraints are mutually redundant, then uset_gist_full
+ * will remove the second of those constraints. We therefore first
+ * sort the constraints so that constraints not involving existentially
+ * quantified variables are given precedence over those that do.
+ * We have to perform this sorting before the variable compression,
+ * because that may effect the order of the variables.
+ */
+static __isl_give isl_basic_set *uset_gist(__isl_take isl_basic_set *bset,
+ __isl_take isl_basic_set *context)
+{
+ isl_mat *eq;
+ isl_mat *T;
+ isl_basic_set *aff;
+ isl_basic_set *aff_context;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0 || !context)
+ goto error;
+
+ context = drop_irrelevant_constraints(context, bset);
+
+ bset = isl_basic_set_detect_equalities(bset);
+ aff = isl_basic_set_copy(bset);
+ aff = isl_basic_set_plain_affine_hull(aff);
+ context = isl_basic_set_detect_equalities(context);
+ aff_context = isl_basic_set_copy(context);
+ aff_context = isl_basic_set_plain_affine_hull(aff_context);
+ aff = isl_basic_set_intersect(aff, aff_context);
+ if (!aff)
+ goto error;
+ if (isl_basic_set_plain_is_empty(aff)) {
+ isl_basic_set_free(bset);
+ isl_basic_set_free(context);
+ return aff;
+ }
+ bset = isl_basic_set_sort_constraints(bset);
+ if (aff->n_eq == 0) {
+ isl_basic_set_free(aff);
+ return uset_gist_uncompressed(bset, context);
+ }
+ eq = isl_mat_sub_alloc6(bset->ctx, aff->eq, 0, aff->n_eq, 0, 1 + total);
+ eq = isl_mat_cow(eq);
+ T = isl_mat_variable_compression(eq, NULL);
+ isl_basic_set_free(aff);
+ if (T && T->n_col == 0) {
+ isl_mat_free(T);
+ isl_basic_set_free(context);
+ return replace_by_empty(bset);
+ }
+
+ aff_context = isl_basic_set_affine_hull(isl_basic_set_copy(context));
+ aff_context = project_onto_involved(aff_context, bset);
+
+ bset = uset_gist_compressed(bset, context, T);
+ bset = isl_basic_set_reduce_using_equalities(bset, aff_context);
+
+ if (bset) {
+ ISL_F_SET(bset, ISL_BASIC_SET_NO_IMPLICIT);
+ ISL_F_SET(bset, ISL_BASIC_SET_NO_REDUNDANT);
+ }
+
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ isl_basic_set_free(context);
+ return NULL;
+}
+
+/* Return the number of equality constraints in "bmap" that involve
+ * local variables. This function assumes that Gaussian elimination
+ * has been applied to the equality constraints.
+ */
+static int n_div_eq(__isl_keep isl_basic_map *bmap)
+{
+ int i;
+ isl_size total, n_div;
+
+ if (!bmap)
+ return -1;
+
+ if (bmap->n_eq == 0)
+ return 0;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (total < 0 || n_div < 0)
+ return -1;
+ total -= n_div;
+
+ for (i = 0; i < bmap->n_eq; ++i)
+ if (isl_seq_first_non_zero(bmap->eq[i] + 1 + total,
+ n_div) == -1)
+ return i;
+
+ return bmap->n_eq;
+}
+
+/* Construct a basic map in "space" defined by the equality constraints in "eq".
+ * The constraints are assumed not to involve any local variables.
+ */
+static __isl_give isl_basic_map *basic_map_from_equalities(
+ __isl_take isl_space *space, __isl_take isl_mat *eq)
+{
+ int i, k;
+ isl_size total;
+ isl_basic_map *bmap = NULL;
+
+ total = isl_space_dim(space, isl_dim_all);
+ if (total < 0 || !eq)
+ goto error;
+
+ if (1 + total != eq->n_col)
+ isl_die(isl_space_get_ctx(space), isl_error_internal,
+ "unexpected number of columns", goto error);
+
+ bmap = isl_basic_map_alloc_space(isl_space_copy(space),
+ 0, eq->n_row, 0);
+ for (i = 0; i < eq->n_row; ++i) {
+ k = isl_basic_map_alloc_equality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bmap->eq[k], eq->row[i], eq->n_col);
+ }
+
+ isl_space_free(space);
+ isl_mat_free(eq);
+ return bmap;
+error:
+ isl_space_free(space);
+ isl_mat_free(eq);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Construct and return a variable compression based on the equality
+ * constraints in "bmap1" and "bmap2" that do not involve the local variables.
+ * "n1" is the number of (initial) equality constraints in "bmap1"
+ * that do involve local variables.
+ * "n2" is the number of (initial) equality constraints in "bmap2"
+ * that do involve local variables.
+ * "total" is the total number of other variables.
+ * This function assumes that Gaussian elimination
+ * has been applied to the equality constraints in both "bmap1" and "bmap2"
+ * such that the equality constraints not involving local variables
+ * are those that start at "n1" or "n2".
+ *
+ * If either of "bmap1" and "bmap2" does not have such equality constraints,
+ * then simply compute the compression based on the equality constraints
+ * in the other basic map.
+ * Otherwise, combine the equality constraints from both into a new
+ * basic map such that Gaussian elimination can be applied to this combination
+ * and then construct a variable compression from the resulting
+ * equality constraints.
+ */
+static __isl_give isl_mat *combined_variable_compression(
+ __isl_keep isl_basic_map *bmap1, int n1,
+ __isl_keep isl_basic_map *bmap2, int n2, int total)
+{
+ isl_ctx *ctx;
+ isl_mat *E1, *E2, *V;
+ isl_basic_map *bmap;
+
+ ctx = isl_basic_map_get_ctx(bmap1);
+ if (bmap1->n_eq == n1) {
+ E2 = isl_mat_sub_alloc6(ctx, bmap2->eq,
+ n2, bmap2->n_eq - n2, 0, 1 + total);
+ return isl_mat_variable_compression(E2, NULL);
+ }
+ if (bmap2->n_eq == n2) {
+ E1 = isl_mat_sub_alloc6(ctx, bmap1->eq,
+ n1, bmap1->n_eq - n1, 0, 1 + total);
+ return isl_mat_variable_compression(E1, NULL);
+ }
+ E1 = isl_mat_sub_alloc6(ctx, bmap1->eq,
+ n1, bmap1->n_eq - n1, 0, 1 + total);
+ E2 = isl_mat_sub_alloc6(ctx, bmap2->eq,
+ n2, bmap2->n_eq - n2, 0, 1 + total);
+ E1 = isl_mat_concat(E1, E2);
+ bmap = basic_map_from_equalities(isl_basic_map_get_space(bmap1), E1);
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ if (!bmap)
+ return NULL;
+ E1 = isl_mat_sub_alloc6(ctx, bmap->eq, 0, bmap->n_eq, 0, 1 + total);
+ V = isl_mat_variable_compression(E1, NULL);
+ isl_basic_map_free(bmap);
+
+ return V;
+}
+
+/* Extract the stride constraints from "bmap", compressed
+ * with respect to both the stride constraints in "context" and
+ * the remaining equality constraints in both "bmap" and "context".
+ * "bmap_n_eq" is the number of (initial) stride constraints in "bmap".
+ * "context_n_eq" is the number of (initial) stride constraints in "context".
+ *
+ * Let x be all variables in "bmap" (and "context") other than the local
+ * variables. First compute a variable compression
+ *
+ * x = V x'
+ *
+ * based on the non-stride equality constraints in "bmap" and "context".
+ * Consider the stride constraints of "context",
+ *
+ * A(x) + B(y) = 0
+ *
+ * with y the local variables and plug in the variable compression,
+ * resulting in
+ *
+ * A(V x') + B(y) = 0
+ *
+ * Use these constraints to compute a parameter compression on x'
+ *
+ * x' = T x''
+ *
+ * Now consider the stride constraints of "bmap"
+ *
+ * C(x) + D(y) = 0
+ *
+ * and plug in x = V*T x''.
+ * That is, return A = [C*V*T D].
+ */
+static __isl_give isl_mat *extract_compressed_stride_constraints(
+ __isl_keep isl_basic_map *bmap, int bmap_n_eq,
+ __isl_keep isl_basic_map *context, int context_n_eq)
+{
+ isl_size total, n_div;
+ isl_ctx *ctx;
+ isl_mat *A, *B, *T, *V;
+
+ total = isl_basic_map_dim(context, isl_dim_all);
+ n_div = isl_basic_map_dim(context, isl_dim_div);
+ if (total < 0 || n_div < 0)
+ return NULL;
+ total -= n_div;
+
+ ctx = isl_basic_map_get_ctx(bmap);
+
+ V = combined_variable_compression(bmap, bmap_n_eq,
+ context, context_n_eq, total);
+
+ A = isl_mat_sub_alloc6(ctx, context->eq, 0, context_n_eq, 0, 1 + total);
+ B = isl_mat_sub_alloc6(ctx, context->eq,
+ 0, context_n_eq, 1 + total, n_div);
+ A = isl_mat_product(A, isl_mat_copy(V));
+ T = isl_mat_parameter_compression_ext(A, B);
+ T = isl_mat_product(V, T);
+
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0)
+ T = isl_mat_free(T);
+ else
+ T = isl_mat_diagonal(T, isl_mat_identity(ctx, n_div));
+
+ A = isl_mat_sub_alloc6(ctx, bmap->eq,
+ 0, bmap_n_eq, 0, 1 + total + n_div);
+ A = isl_mat_product(A, T);
+
+ return A;
+}
+
+/* Remove the prime factors from *g that have an exponent that
+ * is strictly smaller than the exponent in "c".
+ * All exponents in *g are known to be smaller than or equal
+ * to those in "c".
+ *
+ * That is, if *g is equal to
+ *
+ * p_1^{e_1} p_2^{e_2} ... p_n^{e_n}
+ *
+ * and "c" is equal to
+ *
+ * p_1^{f_1} p_2^{f_2} ... p_n^{f_n}
+ *
+ * then update *g to
+ *
+ * p_1^{e_1 * (e_1 = f_1)} p_2^{e_2 * (e_2 = f_2)} ...
+ * p_n^{e_n * (e_n = f_n)}
+ *
+ * If e_i = f_i, then c / *g does not have any p_i factors and therefore
+ * neither does the gcd of *g and c / *g.
+ * If e_i < f_i, then the gcd of *g and c / *g has a positive
+ * power min(e_i, s_i) of p_i with s_i = f_i - e_i among its factors.
+ * Dividing *g by this gcd therefore strictly reduces the exponent
+ * of the prime factors that need to be removed, while leaving the
+ * other prime factors untouched.
+ * Repeating this process until gcd(*g, c / *g) = 1 therefore
+ * removes all undesired factors, without removing any others.
+ */
+static void remove_incomplete_powers(isl_int *g, isl_int c)
+{
+ isl_int t;
+
+ isl_int_init(t);
+ for (;;) {
+ isl_int_divexact(t, c, *g);
+ isl_int_gcd(t, t, *g);
+ if (isl_int_is_one(t))
+ break;
+ isl_int_divexact(*g, *g, t);
+ }
+ isl_int_clear(t);
+}
+
+/* Reduce the "n" stride constraints in "bmap" based on a copy "A"
+ * of the same stride constraints in a compressed space that exploits
+ * all equalities in the context and the other equalities in "bmap".
+ *
+ * If the stride constraints of "bmap" are of the form
+ *
+ * C(x) + D(y) = 0
+ *
+ * then A is of the form
+ *
+ * B(x') + D(y) = 0
+ *
+ * If any of these constraints involves only a single local variable y,
+ * then the constraint appears as
+ *
+ * f(x) + m y_i = 0
+ *
+ * in "bmap" and as
+ *
+ * h(x') + m y_i = 0
+ *
+ * in "A".
+ *
+ * Let g be the gcd of m and the coefficients of h.
+ * Then, in particular, g is a divisor of the coefficients of h and
+ *
+ * f(x) = h(x')
+ *
+ * is known to be a multiple of g.
+ * If some prime factor in m appears with the same exponent in g,
+ * then it can be removed from m because f(x) is already known
+ * to be a multiple of g and therefore in particular of this power
+ * of the prime factors.
+ * Prime factors that appear with a smaller exponent in g cannot
+ * be removed from m.
+ * Let g' be the divisor of g containing all prime factors that
+ * appear with the same exponent in m and g, then
+ *
+ * f(x) + m y_i = 0
+ *
+ * can be replaced by
+ *
+ * f(x) + m/g' y_i' = 0
+ *
+ * Note that (if g' != 1) this changes the explicit representation
+ * of y_i to that of y_i', so the integer division at position i
+ * is marked unknown and later recomputed by a call to
+ * isl_basic_map_gauss.
+ */
+static __isl_give isl_basic_map *reduce_stride_constraints(
+ __isl_take isl_basic_map *bmap, int n, __isl_keep isl_mat *A)
+{
+ int i;
+ isl_size total, n_div;
+ int any = 0;
+ isl_int gcd;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (total < 0 || n_div < 0 || !A)
+ return isl_basic_map_free(bmap);
+ total -= n_div;
+
+ isl_int_init(gcd);
+ for (i = 0; i < n; ++i) {
+ int div;
+
+ div = isl_seq_first_non_zero(bmap->eq[i] + 1 + total, n_div);
+ if (div < 0)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_internal,
+ "equality constraints modified unexpectedly",
+ goto error);
+ if (isl_seq_first_non_zero(bmap->eq[i] + 1 + total + div + 1,
+ n_div - div - 1) != -1)
+ continue;
+ if (isl_mat_row_gcd(A, i, &gcd) < 0)
+ goto error;
+ if (isl_int_is_one(gcd))
+ continue;
+ remove_incomplete_powers(&gcd, bmap->eq[i][1 + total + div]);
+ if (isl_int_is_one(gcd))
+ continue;
+ isl_int_divexact(bmap->eq[i][1 + total + div],
+ bmap->eq[i][1 + total + div], gcd);
+ bmap = isl_basic_map_mark_div_unknown(bmap, div);
+ if (!bmap)
+ goto error;
+ any = 1;
+ }
+ isl_int_clear(gcd);
+
+ if (any)
+ bmap = isl_basic_map_gauss(bmap, NULL);
+
+ return bmap;
+error:
+ isl_int_clear(gcd);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Simplify the stride constraints in "bmap" based on
+ * the remaining equality constraints in "bmap" and all equality
+ * constraints in "context".
+ * Only do this if both "bmap" and "context" have stride constraints.
+ *
+ * First extract a copy of the stride constraints in "bmap" in a compressed
+ * space exploiting all the other equality constraints and then
+ * use this compressed copy to simplify the original stride constraints.
+ */
+static __isl_give isl_basic_map *gist_strides(__isl_take isl_basic_map *bmap,
+ __isl_keep isl_basic_map *context)
+{
+ int bmap_n_eq, context_n_eq;
+ isl_mat *A;
+
+ if (!bmap || !context)
+ return isl_basic_map_free(bmap);
+
+ bmap_n_eq = n_div_eq(bmap);
+ context_n_eq = n_div_eq(context);
+
+ if (bmap_n_eq < 0 || context_n_eq < 0)
+ return isl_basic_map_free(bmap);
+ if (bmap_n_eq == 0 || context_n_eq == 0)
+ return bmap;
+
+ A = extract_compressed_stride_constraints(bmap, bmap_n_eq,
+ context, context_n_eq);
+ bmap = reduce_stride_constraints(bmap, bmap_n_eq, A);
+
+ isl_mat_free(A);
+
+ return bmap;
+}
+
+/* Return a basic map that has the same intersection with "context" as "bmap"
+ * and that is as "simple" as possible.
+ *
+ * The core computation is performed on the pure constraints.
+ * When we add back the meaning of the integer divisions, we need
+ * to (re)introduce the div constraints. If we happen to have
+ * discovered that some of these integer divisions are equal to
+ * some affine combination of other variables, then these div
+ * constraints may end up getting simplified in terms of the equalities,
+ * resulting in extra inequalities on the other variables that
+ * may have been removed already or that may not even have been
+ * part of the input. We try and remove those constraints of
+ * this form that are most obviously redundant with respect to
+ * the context. We also remove those div constraints that are
+ * redundant with respect to the other constraints in the result.
+ *
+ * The stride constraints among the equality constraints in "bmap" are
+ * also simplified with respecting to the other equality constraints
+ * in "bmap" and with respect to all equality constraints in "context".
+ */
+__isl_give isl_basic_map *isl_basic_map_gist(__isl_take isl_basic_map *bmap,
+ __isl_take isl_basic_map *context)
+{
+ isl_basic_set *bset, *eq;
+ isl_basic_map *eq_bmap;
+ isl_size total, n_div, n_div_bmap;
+ unsigned extra, n_eq, n_ineq;
+
+ if (!bmap || !context)
+ goto error;
+
+ if (isl_basic_map_plain_is_universe(bmap)) {
+ isl_basic_map_free(context);
+ return bmap;
+ }
+ if (isl_basic_map_plain_is_empty(context)) {
+ isl_space *space = isl_basic_map_get_space(bmap);
+ isl_basic_map_free(bmap);
+ isl_basic_map_free(context);
+ return isl_basic_map_universe(space);
+ }
+ if (isl_basic_map_plain_is_empty(bmap)) {
+ isl_basic_map_free(context);
+ return bmap;
+ }
+
+ bmap = isl_basic_map_remove_redundancies(bmap);
+ context = isl_basic_map_remove_redundancies(context);
+ bmap = isl_basic_map_order_divs(bmap);
+ context = isl_basic_map_align_divs(context, bmap);
+
+ n_div = isl_basic_map_dim(context, isl_dim_div);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ n_div_bmap = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0 || total < 0 || n_div_bmap < 0)
+ goto error;
+ extra = n_div - n_div_bmap;
+
+ bset = isl_basic_map_underlying_set(isl_basic_map_copy(bmap));
+ bset = isl_basic_set_add_dims(bset, isl_dim_set, extra);
+ bset = uset_gist(bset,
+ isl_basic_map_underlying_set(isl_basic_map_copy(context)));
+ bset = isl_basic_set_project_out(bset, isl_dim_set, total, extra);
+
+ if (!bset || bset->n_eq == 0 || n_div == 0 ||
+ isl_basic_set_plain_is_empty(bset)) {
+ isl_basic_map_free(context);
+ return isl_basic_map_overlying_set(bset, bmap);
+ }
+
+ n_eq = bset->n_eq;
+ n_ineq = bset->n_ineq;
+ eq = isl_basic_set_copy(bset);
+ eq = isl_basic_set_cow(eq);
+ eq = isl_basic_set_free_inequality(eq, n_ineq);
+ bset = isl_basic_set_free_equality(bset, n_eq);
+
+ eq_bmap = isl_basic_map_overlying_set(eq, isl_basic_map_copy(bmap));
+ eq_bmap = gist_strides(eq_bmap, context);
+ eq_bmap = isl_basic_map_remove_shifted_constraints(eq_bmap, context);
+ bmap = isl_basic_map_overlying_set(bset, bmap);
+ bmap = isl_basic_map_intersect(bmap, eq_bmap);
+ bmap = isl_basic_map_remove_redundancies(bmap);
+
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ isl_basic_map_free(context);
+ return NULL;
+}
+
+/*
+ * Assumes context has no implicit divs.
+ */
+__isl_give isl_map *isl_map_gist_basic_map(__isl_take isl_map *map,
+ __isl_take isl_basic_map *context)
+{
+ int i;
+
+ if (!map || !context)
+ goto error;
+
+ if (isl_basic_map_plain_is_empty(context)) {
+ isl_space *space = isl_map_get_space(map);
+ isl_map_free(map);
+ isl_basic_map_free(context);
+ return isl_map_universe(space);
+ }
+
+ context = isl_basic_map_remove_redundancies(context);
+ map = isl_map_cow(map);
+ if (isl_map_basic_map_check_equal_space(map, context) < 0)
+ goto error;
+ map = isl_map_compute_divs(map);
+ if (!map)
+ goto error;
+ for (i = map->n - 1; i >= 0; --i) {
+ map->p[i] = isl_basic_map_gist(map->p[i],
+ isl_basic_map_copy(context));
+ if (!map->p[i])
+ goto error;
+ if (isl_basic_map_plain_is_empty(map->p[i])) {
+ isl_basic_map_free(map->p[i]);
+ if (i != map->n - 1)
+ map->p[i] = map->p[map->n - 1];
+ map->n--;
+ }
+ }
+ isl_basic_map_free(context);
+ ISL_F_CLR(map, ISL_MAP_NORMALIZED);
+ return map;
+error:
+ isl_map_free(map);
+ isl_basic_map_free(context);
+ return NULL;
+}
+
+/* Drop all inequalities from "bmap" that also appear in "context".
+ * "context" is assumed to have only known local variables and
+ * the initial local variables of "bmap" are assumed to be the same
+ * as those of "context".
+ * The constraints of both "bmap" and "context" are assumed
+ * to have been sorted using isl_basic_map_sort_constraints.
+ *
+ * Run through the inequality constraints of "bmap" and "context"
+ * in sorted order.
+ * If a constraint of "bmap" involves variables not in "context",
+ * then it cannot appear in "context".
+ * If a matching constraint is found, it is removed from "bmap".
+ */
+static __isl_give isl_basic_map *drop_inequalities(
+ __isl_take isl_basic_map *bmap, __isl_keep isl_basic_map *context)
+{
+ int i1, i2;
+ isl_size total, bmap_total;
+ unsigned extra;
+
+ total = isl_basic_map_dim(context, isl_dim_all);
+ bmap_total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0 || bmap_total < 0)
+ return isl_basic_map_free(bmap);
+
+ extra = bmap_total - total;
+
+ i1 = bmap->n_ineq - 1;
+ i2 = context->n_ineq - 1;
+ while (bmap && i1 >= 0 && i2 >= 0) {
+ int cmp;
+
+ if (isl_seq_first_non_zero(bmap->ineq[i1] + 1 + total,
+ extra) != -1) {
+ --i1;
+ continue;
+ }
+ cmp = isl_basic_map_constraint_cmp(context, bmap->ineq[i1],
+ context->ineq[i2]);
+ if (cmp < 0) {
+ --i2;
+ continue;
+ }
+ if (cmp > 0) {
+ --i1;
+ continue;
+ }
+ if (isl_int_eq(bmap->ineq[i1][0], context->ineq[i2][0])) {
+ bmap = isl_basic_map_cow(bmap);
+ if (isl_basic_map_drop_inequality(bmap, i1) < 0)
+ bmap = isl_basic_map_free(bmap);
+ }
+ --i1;
+ --i2;
+ }
+
+ return bmap;
+}
+
+/* Drop all equalities from "bmap" that also appear in "context".
+ * "context" is assumed to have only known local variables and
+ * the initial local variables of "bmap" are assumed to be the same
+ * as those of "context".
+ *
+ * Run through the equality constraints of "bmap" and "context"
+ * in sorted order.
+ * If a constraint of "bmap" involves variables not in "context",
+ * then it cannot appear in "context".
+ * If a matching constraint is found, it is removed from "bmap".
+ */
+static __isl_give isl_basic_map *drop_equalities(
+ __isl_take isl_basic_map *bmap, __isl_keep isl_basic_map *context)
+{
+ int i1, i2;
+ isl_size total, bmap_total;
+ unsigned extra;
+
+ total = isl_basic_map_dim(context, isl_dim_all);
+ bmap_total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0 || bmap_total < 0)
+ return isl_basic_map_free(bmap);
+
+ extra = bmap_total - total;
+
+ i1 = bmap->n_eq - 1;
+ i2 = context->n_eq - 1;
+
+ while (bmap && i1 >= 0 && i2 >= 0) {
+ int last1, last2;
+
+ if (isl_seq_first_non_zero(bmap->eq[i1] + 1 + total,
+ extra) != -1)
+ break;
+ last1 = isl_seq_last_non_zero(bmap->eq[i1] + 1, total);
+ last2 = isl_seq_last_non_zero(context->eq[i2] + 1, total);
+ if (last1 > last2) {
+ --i2;
+ continue;
+ }
+ if (last1 < last2) {
+ --i1;
+ continue;
+ }
+ if (isl_seq_eq(bmap->eq[i1], context->eq[i2], 1 + total)) {
+ bmap = isl_basic_map_cow(bmap);
+ if (isl_basic_map_drop_equality(bmap, i1) < 0)
+ bmap = isl_basic_map_free(bmap);
+ }
+ --i1;
+ --i2;
+ }
+
+ return bmap;
+}
+
+/* Remove the constraints in "context" from "bmap".
+ * "context" is assumed to have explicit representations
+ * for all local variables.
+ *
+ * First align the divs of "bmap" to those of "context" and
+ * sort the constraints. Then drop all constraints from "bmap"
+ * that appear in "context".
+ */
+__isl_give isl_basic_map *isl_basic_map_plain_gist(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_map *context)
+{
+ isl_bool done, known;
+
+ done = isl_basic_map_plain_is_universe(context);
+ if (done == isl_bool_false)
+ done = isl_basic_map_plain_is_universe(bmap);
+ if (done == isl_bool_false)
+ done = isl_basic_map_plain_is_empty(context);
+ if (done == isl_bool_false)
+ done = isl_basic_map_plain_is_empty(bmap);
+ if (done < 0)
+ goto error;
+ if (done) {
+ isl_basic_map_free(context);
+ return bmap;
+ }
+ known = isl_basic_map_divs_known(context);
+ if (known < 0)
+ goto error;
+ if (!known)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "context has unknown divs", goto error);
+
+ context = isl_basic_map_order_divs(context);
+ bmap = isl_basic_map_align_divs(bmap, context);
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ bmap = isl_basic_map_sort_constraints(bmap);
+ context = isl_basic_map_sort_constraints(context);
+
+ bmap = drop_inequalities(bmap, context);
+ bmap = drop_equalities(bmap, context);
+
+ isl_basic_map_free(context);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ isl_basic_map_free(context);
+ return NULL;
+}
+
+/* Replace "map" by the disjunct at position "pos" and free "context".
+ */
+static __isl_give isl_map *replace_by_disjunct(__isl_take isl_map *map,
+ int pos, __isl_take isl_basic_map *context)
+{
+ isl_basic_map *bmap;
+
+ bmap = isl_basic_map_copy(map->p[pos]);
+ isl_map_free(map);
+ isl_basic_map_free(context);
+ return isl_map_from_basic_map(bmap);
+}
+
+/* Remove the constraints in "context" from "map".
+ * If any of the disjuncts in the result turns out to be the universe,
+ * then return this universe.
+ * "context" is assumed to have explicit representations
+ * for all local variables.
+ */
+__isl_give isl_map *isl_map_plain_gist_basic_map(__isl_take isl_map *map,
+ __isl_take isl_basic_map *context)
+{
+ int i;
+ isl_bool univ, known;
+
+ univ = isl_basic_map_plain_is_universe(context);
+ if (univ < 0)
+ goto error;
+ if (univ) {
+ isl_basic_map_free(context);
+ return map;
+ }
+ known = isl_basic_map_divs_known(context);
+ if (known < 0)
+ goto error;
+ if (!known)
+ isl_die(isl_map_get_ctx(map), isl_error_invalid,
+ "context has unknown divs", goto error);
+
+ map = isl_map_cow(map);
+ if (!map)
+ goto error;
+ for (i = 0; i < map->n; ++i) {
+ map->p[i] = isl_basic_map_plain_gist(map->p[i],
+ isl_basic_map_copy(context));
+ univ = isl_basic_map_plain_is_universe(map->p[i]);
+ if (univ < 0)
+ goto error;
+ if (univ && map->n > 1)
+ return replace_by_disjunct(map, i, context);
+ }
+
+ isl_basic_map_free(context);
+ ISL_F_CLR(map, ISL_MAP_NORMALIZED);
+ if (map->n > 1)
+ ISL_F_CLR(map, ISL_MAP_DISJOINT);
+ return map;
+error:
+ isl_map_free(map);
+ isl_basic_map_free(context);
+ return NULL;
+}
+
+/* Remove the constraints in "context" from "set".
+ * If any of the disjuncts in the result turns out to be the universe,
+ * then return this universe.
+ * "context" is assumed to have explicit representations
+ * for all local variables.
+ */
+__isl_give isl_set *isl_set_plain_gist_basic_set(__isl_take isl_set *set,
+ __isl_take isl_basic_set *context)
+{
+ return set_from_map(isl_map_plain_gist_basic_map(set_to_map(set),
+ bset_to_bmap(context)));
+}
+
+/* Remove the constraints in "context" from "map".
+ * If any of the disjuncts in the result turns out to be the universe,
+ * then return this universe.
+ * "context" is assumed to consist of a single disjunct and
+ * to have explicit representations for all local variables.
+ */
+__isl_give isl_map *isl_map_plain_gist(__isl_take isl_map *map,
+ __isl_take isl_map *context)
+{
+ isl_basic_map *hull;
+
+ hull = isl_map_unshifted_simple_hull(context);
+ return isl_map_plain_gist_basic_map(map, hull);
+}
+
+/* Replace "map" by a universe map in the same space and free "drop".
+ */
+static __isl_give isl_map *replace_by_universe(__isl_take isl_map *map,
+ __isl_take isl_map *drop)
+{
+ isl_map *res;
+
+ res = isl_map_universe(isl_map_get_space(map));
+ isl_map_free(map);
+ isl_map_free(drop);
+ return res;
+}
+
+/* Return a map that has the same intersection with "context" as "map"
+ * and that is as "simple" as possible.
+ *
+ * If "map" is already the universe, then we cannot make it any simpler.
+ * Similarly, if "context" is the universe, then we cannot exploit it
+ * to simplify "map"
+ * If "map" and "context" are identical to each other, then we can
+ * return the corresponding universe.
+ *
+ * If either "map" or "context" consists of multiple disjuncts,
+ * then check if "context" happens to be a subset of "map",
+ * in which case all constraints can be removed.
+ * In case of multiple disjuncts, the standard procedure
+ * may not be able to detect that all constraints can be removed.
+ *
+ * If none of these cases apply, we have to work a bit harder.
+ * During this computation, we make use of a single disjunct context,
+ * so if the original context consists of more than one disjunct
+ * then we need to approximate the context by a single disjunct set.
+ * Simply taking the simple hull may drop constraints that are
+ * only implicitly available in each disjunct. We therefore also
+ * look for constraints among those defining "map" that are valid
+ * for the context. These can then be used to simplify away
+ * the corresponding constraints in "map".
+ */
+__isl_give isl_map *isl_map_gist(__isl_take isl_map *map,
+ __isl_take isl_map *context)
+{
+ int equal;
+ int is_universe;
+ isl_size n_disjunct_map, n_disjunct_context;
+ isl_bool subset;
+ isl_basic_map *hull;
+
+ is_universe = isl_map_plain_is_universe(map);
+ if (is_universe >= 0 && !is_universe)
+ is_universe = isl_map_plain_is_universe(context);
+ if (is_universe < 0)
+ goto error;
+ if (is_universe) {
+ isl_map_free(context);
+ return map;
+ }
+
+ isl_map_align_params_bin(&map, &context);
+ equal = isl_map_plain_is_equal(map, context);
+ if (equal < 0)
+ goto error;
+ if (equal)
+ return replace_by_universe(map, context);
+
+ n_disjunct_map = isl_map_n_basic_map(map);
+ n_disjunct_context = isl_map_n_basic_map(context);
+ if (n_disjunct_map < 0 || n_disjunct_context < 0)
+ goto error;
+ if (n_disjunct_map != 1 || n_disjunct_context != 1) {
+ subset = isl_map_is_subset(context, map);
+ if (subset < 0)
+ goto error;
+ if (subset)
+ return replace_by_universe(map, context);
+ }
+
+ context = isl_map_compute_divs(context);
+ if (!context)
+ goto error;
+ if (n_disjunct_context == 1) {
+ hull = isl_map_simple_hull(context);
+ } else {
+ isl_ctx *ctx;
+ isl_map_list *list;
+
+ ctx = isl_map_get_ctx(map);
+ list = isl_map_list_alloc(ctx, 2);
+ list = isl_map_list_add(list, isl_map_copy(context));
+ list = isl_map_list_add(list, isl_map_copy(map));
+ hull = isl_map_unshifted_simple_hull_from_map_list(context,
+ list);
+ }
+ return isl_map_gist_basic_map(map, hull);
+error:
+ isl_map_free(map);
+ isl_map_free(context);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_gist(__isl_take isl_basic_set *bset,
+ __isl_take isl_basic_set *context)
+{
+ return bset_from_bmap(isl_basic_map_gist(bset_to_bmap(bset),
+ bset_to_bmap(context)));
+}
+
+__isl_give isl_set *isl_set_gist_basic_set(__isl_take isl_set *set,
+ __isl_take isl_basic_set *context)
+{
+ return set_from_map(isl_map_gist_basic_map(set_to_map(set),
+ bset_to_bmap(context)));
+}
+
+__isl_give isl_set *isl_set_gist_params_basic_set(__isl_take isl_set *set,
+ __isl_take isl_basic_set *context)
+{
+ isl_space *space = isl_set_get_space(set);
+ isl_basic_set *dom_context = isl_basic_set_universe(space);
+ dom_context = isl_basic_set_intersect_params(dom_context, context);
+ return isl_set_gist_basic_set(set, dom_context);
+}
+
+__isl_give isl_set *isl_set_gist(__isl_take isl_set *set,
+ __isl_take isl_set *context)
+{
+ return set_from_map(isl_map_gist(set_to_map(set), set_to_map(context)));
+}
+
+/* Compute the gist of "bmap" with respect to the constraints "context"
+ * on the domain.
+ */
+__isl_give isl_basic_map *isl_basic_map_gist_domain(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *context)
+{
+ isl_space *space = isl_basic_map_get_space(bmap);
+ isl_basic_map *bmap_context = isl_basic_map_universe(space);
+
+ bmap_context = isl_basic_map_intersect_domain(bmap_context, context);
+ return isl_basic_map_gist(bmap, bmap_context);
+}
+
+__isl_give isl_map *isl_map_gist_domain(__isl_take isl_map *map,
+ __isl_take isl_set *context)
+{
+ isl_map *map_context = isl_map_universe(isl_map_get_space(map));
+ map_context = isl_map_intersect_domain(map_context, context);
+ return isl_map_gist(map, map_context);
+}
+
+__isl_give isl_map *isl_map_gist_range(__isl_take isl_map *map,
+ __isl_take isl_set *context)
+{
+ isl_map *map_context = isl_map_universe(isl_map_get_space(map));
+ map_context = isl_map_intersect_range(map_context, context);
+ return isl_map_gist(map, map_context);
+}
+
+__isl_give isl_map *isl_map_gist_params(__isl_take isl_map *map,
+ __isl_take isl_set *context)
+{
+ isl_map *map_context = isl_map_universe(isl_map_get_space(map));
+ map_context = isl_map_intersect_params(map_context, context);
+ return isl_map_gist(map, map_context);
+}
+
+__isl_give isl_set *isl_set_gist_params(__isl_take isl_set *set,
+ __isl_take isl_set *context)
+{
+ return isl_map_gist_params(set, context);
+}
+
+/* Quick check to see if two basic maps are disjoint.
+ * In particular, we reduce the equalities and inequalities of
+ * one basic map in the context of the equalities of the other
+ * basic map and check if we get a contradiction.
+ */
+isl_bool isl_basic_map_plain_is_disjoint(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2)
+{
+ struct isl_vec *v = NULL;
+ int *elim = NULL;
+ isl_size total;
+ int i;
+
+ if (isl_basic_map_check_equal_space(bmap1, bmap2) < 0)
+ return isl_bool_error;
+ if (bmap1->n_div || bmap2->n_div)
+ return isl_bool_false;
+ if (!bmap1->n_eq && !bmap2->n_eq)
+ return isl_bool_false;
+
+ total = isl_space_dim(bmap1->dim, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ if (total == 0)
+ return isl_bool_false;
+ v = isl_vec_alloc(bmap1->ctx, 1 + total);
+ if (!v)
+ goto error;
+ elim = isl_alloc_array(bmap1->ctx, int, total);
+ if (!elim)
+ goto error;
+ compute_elimination_index(bmap1, elim, total);
+ for (i = 0; i < bmap2->n_eq; ++i) {
+ int reduced;
+ reduced = reduced_using_equalities(v->block.data, bmap2->eq[i],
+ bmap1, elim, total);
+ if (reduced && !isl_int_is_zero(v->block.data[0]) &&
+ isl_seq_first_non_zero(v->block.data + 1, total) == -1)
+ goto disjoint;
+ }
+ for (i = 0; i < bmap2->n_ineq; ++i) {
+ int reduced;
+ reduced = reduced_using_equalities(v->block.data,
+ bmap2->ineq[i], bmap1, elim, total);
+ if (reduced && isl_int_is_neg(v->block.data[0]) &&
+ isl_seq_first_non_zero(v->block.data + 1, total) == -1)
+ goto disjoint;
+ }
+ compute_elimination_index(bmap2, elim, total);
+ for (i = 0; i < bmap1->n_ineq; ++i) {
+ int reduced;
+ reduced = reduced_using_equalities(v->block.data,
+ bmap1->ineq[i], bmap2, elim, total);
+ if (reduced && isl_int_is_neg(v->block.data[0]) &&
+ isl_seq_first_non_zero(v->block.data + 1, total) == -1)
+ goto disjoint;
+ }
+ isl_vec_free(v);
+ free(elim);
+ return isl_bool_false;
+disjoint:
+ isl_vec_free(v);
+ free(elim);
+ return isl_bool_true;
+error:
+ isl_vec_free(v);
+ free(elim);
+ return isl_bool_error;
+}
+
+int isl_basic_set_plain_is_disjoint(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2)
+{
+ return isl_basic_map_plain_is_disjoint(bset_to_bmap(bset1),
+ bset_to_bmap(bset2));
+}
+
+/* Does "test" hold for all pairs of basic maps in "map1" and "map2"?
+ */
+static isl_bool all_pairs(__isl_keep isl_map *map1, __isl_keep isl_map *map2,
+ isl_bool (*test)(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2))
+{
+ int i, j;
+
+ if (!map1 || !map2)
+ return isl_bool_error;
+
+ for (i = 0; i < map1->n; ++i) {
+ for (j = 0; j < map2->n; ++j) {
+ isl_bool d = test(map1->p[i], map2->p[j]);
+ if (d != isl_bool_true)
+ return d;
+ }
+ }
+
+ return isl_bool_true;
+}
+
+/* Are "map1" and "map2" obviously disjoint, based on information
+ * that can be derived without looking at the individual basic maps?
+ *
+ * In particular, if one of them is empty or if they live in different spaces
+ * (ignoring parameters), then they are clearly disjoint.
+ */
+static isl_bool isl_map_plain_is_disjoint_global(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2)
+{
+ isl_bool disjoint;
+ isl_bool match;
+
+ if (!map1 || !map2)
+ return isl_bool_error;
+
+ disjoint = isl_map_plain_is_empty(map1);
+ if (disjoint < 0 || disjoint)
+ return disjoint;
+
+ disjoint = isl_map_plain_is_empty(map2);
+ if (disjoint < 0 || disjoint)
+ return disjoint;
+
+ match = isl_map_tuple_is_equal(map1, isl_dim_in, map2, isl_dim_in);
+ if (match < 0 || !match)
+ return match < 0 ? isl_bool_error : isl_bool_true;
+
+ match = isl_map_tuple_is_equal(map1, isl_dim_out, map2, isl_dim_out);
+ if (match < 0 || !match)
+ return match < 0 ? isl_bool_error : isl_bool_true;
+
+ return isl_bool_false;
+}
+
+/* Are "map1" and "map2" obviously disjoint?
+ *
+ * If one of them is empty or if they live in different spaces (ignoring
+ * parameters), then they are clearly disjoint.
+ * This is checked by isl_map_plain_is_disjoint_global.
+ *
+ * If they have different parameters, then we skip any further tests.
+ *
+ * If they are obviously equal, but not obviously empty, then we will
+ * not be able to detect if they are disjoint.
+ *
+ * Otherwise we check if each basic map in "map1" is obviously disjoint
+ * from each basic map in "map2".
+ */
+isl_bool isl_map_plain_is_disjoint(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2)
+{
+ isl_bool disjoint;
+ isl_bool intersect;
+ isl_bool match;
+
+ disjoint = isl_map_plain_is_disjoint_global(map1, map2);
+ if (disjoint < 0 || disjoint)
+ return disjoint;
+
+ match = isl_map_has_equal_params(map1, map2);
+ if (match < 0 || !match)
+ return match < 0 ? isl_bool_error : isl_bool_false;
+
+ intersect = isl_map_plain_is_equal(map1, map2);
+ if (intersect < 0 || intersect)
+ return intersect < 0 ? isl_bool_error : isl_bool_false;
+
+ return all_pairs(map1, map2, &isl_basic_map_plain_is_disjoint);
+}
+
+/* Are "map1" and "map2" disjoint?
+ * The parameters are assumed to have been aligned.
+ *
+ * In particular, check whether all pairs of basic maps are disjoint.
+ */
+static isl_bool isl_map_is_disjoint_aligned(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2)
+{
+ return all_pairs(map1, map2, &isl_basic_map_is_disjoint);
+}
+
+/* Are "map1" and "map2" disjoint?
+ *
+ * They are disjoint if they are "obviously disjoint" or if one of them
+ * is empty. Otherwise, they are not disjoint if one of them is universal.
+ * If the two inputs are (obviously) equal and not empty, then they are
+ * not disjoint.
+ * If none of these cases apply, then check if all pairs of basic maps
+ * are disjoint after aligning the parameters.
+ */
+isl_bool isl_map_is_disjoint(__isl_keep isl_map *map1, __isl_keep isl_map *map2)
+{
+ isl_bool disjoint;
+ isl_bool intersect;
+
+ disjoint = isl_map_plain_is_disjoint_global(map1, map2);
+ if (disjoint < 0 || disjoint)
+ return disjoint;
+
+ disjoint = isl_map_is_empty(map1);
+ if (disjoint < 0 || disjoint)
+ return disjoint;
+
+ disjoint = isl_map_is_empty(map2);
+ if (disjoint < 0 || disjoint)
+ return disjoint;
+
+ intersect = isl_map_plain_is_universe(map1);
+ if (intersect < 0 || intersect)
+ return isl_bool_not(intersect);
+
+ intersect = isl_map_plain_is_universe(map2);
+ if (intersect < 0 || intersect)
+ return isl_bool_not(intersect);
+
+ intersect = isl_map_plain_is_equal(map1, map2);
+ if (intersect < 0 || intersect)
+ return isl_bool_not(intersect);
+
+ return isl_map_align_params_map_map_and_test(map1, map2,
+ &isl_map_is_disjoint_aligned);
+}
+
+/* Are "bmap1" and "bmap2" disjoint?
+ *
+ * They are disjoint if they are "obviously disjoint" or if one of them
+ * is empty. Otherwise, they are not disjoint if one of them is universal.
+ * If none of these cases apply, we compute the intersection and see if
+ * the result is empty.
+ */
+isl_bool isl_basic_map_is_disjoint(__isl_keep isl_basic_map *bmap1,
+ __isl_keep isl_basic_map *bmap2)
+{
+ isl_bool disjoint;
+ isl_bool intersect;
+ isl_basic_map *test;
+
+ disjoint = isl_basic_map_plain_is_disjoint(bmap1, bmap2);
+ if (disjoint < 0 || disjoint)
+ return disjoint;
+
+ disjoint = isl_basic_map_is_empty(bmap1);
+ if (disjoint < 0 || disjoint)
+ return disjoint;
+
+ disjoint = isl_basic_map_is_empty(bmap2);
+ if (disjoint < 0 || disjoint)
+ return disjoint;
+
+ intersect = isl_basic_map_plain_is_universe(bmap1);
+ if (intersect < 0 || intersect)
+ return isl_bool_not(intersect);
+
+ intersect = isl_basic_map_plain_is_universe(bmap2);
+ if (intersect < 0 || intersect)
+ return isl_bool_not(intersect);
+
+ test = isl_basic_map_intersect(isl_basic_map_copy(bmap1),
+ isl_basic_map_copy(bmap2));
+ disjoint = isl_basic_map_is_empty(test);
+ isl_basic_map_free(test);
+
+ return disjoint;
+}
+
+/* Are "bset1" and "bset2" disjoint?
+ */
+isl_bool isl_basic_set_is_disjoint(__isl_keep isl_basic_set *bset1,
+ __isl_keep isl_basic_set *bset2)
+{
+ return isl_basic_map_is_disjoint(bset1, bset2);
+}
+
+isl_bool isl_set_plain_is_disjoint(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2)
+{
+ return isl_map_plain_is_disjoint(set_to_map(set1), set_to_map(set2));
+}
+
+/* Are "set1" and "set2" disjoint?
+ */
+isl_bool isl_set_is_disjoint(__isl_keep isl_set *set1, __isl_keep isl_set *set2)
+{
+ return isl_map_is_disjoint(set1, set2);
+}
+
+/* Is "v" equal to 0, 1 or -1?
+ */
+static int is_zero_or_one(isl_int v)
+{
+ return isl_int_is_zero(v) || isl_int_is_one(v) || isl_int_is_negone(v);
+}
+
+/* Are the "n" coefficients starting at "first" of inequality constraints
+ * "i" and "j" of "bmap" opposite to each other?
+ */
+static int is_opposite_part(__isl_keep isl_basic_map *bmap, int i, int j,
+ int first, int n)
+{
+ return isl_seq_is_neg(bmap->ineq[i] + first, bmap->ineq[j] + first, n);
+}
+
+/* Are inequality constraints "i" and "j" of "bmap" opposite to each other,
+ * apart from the constant term?
+ */
+static isl_bool is_opposite(__isl_keep isl_basic_map *bmap, int i, int j)
+{
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ return is_opposite_part(bmap, i, j, 1, total);
+}
+
+/* Check if we can combine a given div with lower bound l and upper
+ * bound u with some other div and if so return that other div.
+ * Otherwise, return a position beyond the integer divisions.
+ * Return -1 on error.
+ *
+ * We first check that
+ * - the bounds are opposites of each other (except for the constant
+ * term)
+ * - the bounds do not reference any other div
+ * - no div is defined in terms of this div
+ *
+ * Let m be the size of the range allowed on the div by the bounds.
+ * That is, the bounds are of the form
+ *
+ * e <= a <= e + m - 1
+ *
+ * with e some expression in the other variables.
+ * We look for another div b such that no third div is defined in terms
+ * of this second div b and such that in any constraint that contains
+ * a (except for the given lower and upper bound), also contains b
+ * with a coefficient that is m times that of b.
+ * That is, all constraints (except for the lower and upper bound)
+ * are of the form
+ *
+ * e + f (a + m b) >= 0
+ *
+ * Furthermore, in the constraints that only contain b, the coefficient
+ * of b should be equal to 1 or -1.
+ * If so, we return b so that "a + m b" can be replaced by
+ * a single div "c = a + m b".
+ */
+static int div_find_coalesce(__isl_keep isl_basic_map *bmap, int *pairs,
+ unsigned div, unsigned l, unsigned u)
+{
+ int i, j;
+ unsigned n_div;
+ isl_size v_div;
+ int coalesce;
+ isl_bool opp;
+
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div <= 1)
+ return n_div;
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0)
+ return -1;
+ if (isl_seq_first_non_zero(bmap->ineq[l] + 1 + v_div, div) != -1)
+ return n_div;
+ if (isl_seq_first_non_zero(bmap->ineq[l] + 1 + v_div + div + 1,
+ n_div - div - 1) != -1)
+ return n_div;
+ opp = is_opposite(bmap, l, u);
+ if (opp < 0 || !opp)
+ return opp < 0 ? -1 : n_div;
+
+ for (i = 0; i < n_div; ++i) {
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ if (!isl_int_is_zero(bmap->div[i][1 + 1 + v_div + div]))
+ return n_div;
+ }
+
+ isl_int_add(bmap->ineq[l][0], bmap->ineq[l][0], bmap->ineq[u][0]);
+ if (isl_int_is_neg(bmap->ineq[l][0])) {
+ isl_int_sub(bmap->ineq[l][0],
+ bmap->ineq[l][0], bmap->ineq[u][0]);
+ bmap = isl_basic_map_copy(bmap);
+ bmap = isl_basic_map_set_to_empty(bmap);
+ isl_basic_map_free(bmap);
+ return n_div;
+ }
+ isl_int_add_ui(bmap->ineq[l][0], bmap->ineq[l][0], 1);
+ coalesce = n_div;
+ for (i = 0; i < n_div; ++i) {
+ if (i == div)
+ continue;
+ if (!pairs[i])
+ continue;
+ for (j = 0; j < n_div; ++j) {
+ if (isl_int_is_zero(bmap->div[j][0]))
+ continue;
+ if (!isl_int_is_zero(bmap->div[j][1 + 1 + v_div + i]))
+ break;
+ }
+ if (j < n_div)
+ continue;
+ for (j = 0; j < bmap->n_ineq; ++j) {
+ int valid;
+ if (j == l || j == u)
+ continue;
+ if (isl_int_is_zero(bmap->ineq[j][1 + v_div + div])) {
+ if (is_zero_or_one(bmap->ineq[j][1 + v_div + i]))
+ continue;
+ break;
+ }
+ if (isl_int_is_zero(bmap->ineq[j][1 + v_div + i]))
+ break;
+ isl_int_mul(bmap->ineq[j][1 + v_div + div],
+ bmap->ineq[j][1 + v_div + div],
+ bmap->ineq[l][0]);
+ valid = isl_int_eq(bmap->ineq[j][1 + v_div + div],
+ bmap->ineq[j][1 + v_div + i]);
+ isl_int_divexact(bmap->ineq[j][1 + v_div + div],
+ bmap->ineq[j][1 + v_div + div],
+ bmap->ineq[l][0]);
+ if (!valid)
+ break;
+ }
+ if (j < bmap->n_ineq)
+ continue;
+ coalesce = i;
+ break;
+ }
+ isl_int_sub_ui(bmap->ineq[l][0], bmap->ineq[l][0], 1);
+ isl_int_sub(bmap->ineq[l][0], bmap->ineq[l][0], bmap->ineq[u][0]);
+ return coalesce;
+}
+
+/* Internal data structure used during the construction and/or evaluation of
+ * an inequality that ensures that a pair of bounds always allows
+ * for an integer value.
+ *
+ * "tab" is the tableau in which the inequality is evaluated. It may
+ * be NULL until it is actually needed.
+ * "v" contains the inequality coefficients.
+ * "g", "fl" and "fu" are temporary scalars used during the construction and
+ * evaluation.
+ */
+struct test_ineq_data {
+ struct isl_tab *tab;
+ isl_vec *v;
+ isl_int g;
+ isl_int fl;
+ isl_int fu;
+};
+
+/* Free all the memory allocated by the fields of "data".
+ */
+static void test_ineq_data_clear(struct test_ineq_data *data)
+{
+ isl_tab_free(data->tab);
+ isl_vec_free(data->v);
+ isl_int_clear(data->g);
+ isl_int_clear(data->fl);
+ isl_int_clear(data->fu);
+}
+
+/* Is the inequality stored in data->v satisfied by "bmap"?
+ * That is, does it only attain non-negative values?
+ * data->tab is a tableau corresponding to "bmap".
+ */
+static isl_bool test_ineq_is_satisfied(__isl_keep isl_basic_map *bmap,
+ struct test_ineq_data *data)
+{
+ isl_ctx *ctx;
+ enum isl_lp_result res;
+
+ ctx = isl_basic_map_get_ctx(bmap);
+ if (!data->tab)
+ data->tab = isl_tab_from_basic_map(bmap, 0);
+ res = isl_tab_min(data->tab, data->v->el, ctx->one, &data->g, NULL, 0);
+ if (res == isl_lp_error)
+ return isl_bool_error;
+ return res == isl_lp_ok && isl_int_is_nonneg(data->g);
+}
+
+/* Given a lower and an upper bound on div i, do they always allow
+ * for an integer value of the given div?
+ * Determine this property by constructing an inequality
+ * such that the property is guaranteed when the inequality is nonnegative.
+ * The lower bound is inequality l, while the upper bound is inequality u.
+ * The constructed inequality is stored in data->v.
+ *
+ * Let the upper bound be
+ *
+ * -n_u a + e_u >= 0
+ *
+ * and the lower bound
+ *
+ * n_l a + e_l >= 0
+ *
+ * Let n_u = f_u g and n_l = f_l g, with g = gcd(n_u, n_l).
+ * We have
+ *
+ * - f_u e_l <= f_u f_l g a <= f_l e_u
+ *
+ * Since all variables are integer valued, this is equivalent to
+ *
+ * - f_u e_l - (f_u - 1) <= f_u f_l g a <= f_l e_u + (f_l - 1)
+ *
+ * If this interval is at least f_u f_l g, then it contains at least
+ * one integer value for a.
+ * That is, the test constraint is
+ *
+ * f_l e_u + f_u e_l + f_l - 1 + f_u - 1 + 1 >= f_u f_l g
+ *
+ * or
+ *
+ * f_l e_u + f_u e_l + f_l - 1 + f_u - 1 + 1 - f_u f_l g >= 0
+ *
+ * If the coefficients of f_l e_u + f_u e_l have a common divisor g',
+ * then the constraint can be scaled down by a factor g',
+ * with the constant term replaced by
+ * floor((f_l e_{u,0} + f_u e_{l,0} + f_l - 1 + f_u - 1 + 1 - f_u f_l g)/g').
+ * Note that the result of applying Fourier-Motzkin to this pair
+ * of constraints is
+ *
+ * f_l e_u + f_u e_l >= 0
+ *
+ * If the constant term of the scaled down version of this constraint,
+ * i.e., floor((f_l e_{u,0} + f_u e_{l,0})/g') is equal to the constant
+ * term of the scaled down test constraint, then the test constraint
+ * is known to hold and no explicit evaluation is required.
+ * This is essentially the Omega test.
+ *
+ * If the test constraint consists of only a constant term, then
+ * it is sufficient to look at the sign of this constant term.
+ */
+static isl_bool int_between_bounds(__isl_keep isl_basic_map *bmap, int i,
+ int l, int u, struct test_ineq_data *data)
+{
+ unsigned offset;
+ isl_size n_div;
+
+ offset = isl_basic_map_offset(bmap, isl_dim_div);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0)
+ return isl_bool_error;
+
+ isl_int_gcd(data->g,
+ bmap->ineq[l][offset + i], bmap->ineq[u][offset + i]);
+ isl_int_divexact(data->fl, bmap->ineq[l][offset + i], data->g);
+ isl_int_divexact(data->fu, bmap->ineq[u][offset + i], data->g);
+ isl_int_neg(data->fu, data->fu);
+ isl_seq_combine(data->v->el, data->fl, bmap->ineq[u],
+ data->fu, bmap->ineq[l], offset + n_div);
+ isl_int_mul(data->g, data->g, data->fl);
+ isl_int_mul(data->g, data->g, data->fu);
+ isl_int_sub(data->g, data->g, data->fl);
+ isl_int_sub(data->g, data->g, data->fu);
+ isl_int_add_ui(data->g, data->g, 1);
+ isl_int_sub(data->fl, data->v->el[0], data->g);
+
+ isl_seq_gcd(data->v->el + 1, offset - 1 + n_div, &data->g);
+ if (isl_int_is_zero(data->g))
+ return isl_int_is_nonneg(data->fl);
+ if (isl_int_is_one(data->g)) {
+ isl_int_set(data->v->el[0], data->fl);
+ return test_ineq_is_satisfied(bmap, data);
+ }
+ isl_int_fdiv_q(data->fl, data->fl, data->g);
+ isl_int_fdiv_q(data->v->el[0], data->v->el[0], data->g);
+ if (isl_int_eq(data->fl, data->v->el[0]))
+ return isl_bool_true;
+ isl_int_set(data->v->el[0], data->fl);
+ isl_seq_scale_down(data->v->el + 1, data->v->el + 1, data->g,
+ offset - 1 + n_div);
+
+ return test_ineq_is_satisfied(bmap, data);
+}
+
+/* Remove more kinds of divs that are not strictly needed.
+ * In particular, if all pairs of lower and upper bounds on a div
+ * are such that they allow at least one integer value of the div,
+ * then we can eliminate the div using Fourier-Motzkin without
+ * introducing any spurious solutions.
+ *
+ * If at least one of the two constraints has a unit coefficient for the div,
+ * then the presence of such a value is guaranteed so there is no need to check.
+ * In particular, the value attained by the bound with unit coefficient
+ * can serve as this intermediate value.
+ */
+static __isl_give isl_basic_map *drop_more_redundant_divs(
+ __isl_take isl_basic_map *bmap, __isl_take int *pairs, int n)
+{
+ isl_ctx *ctx;
+ struct test_ineq_data data = { NULL, NULL };
+ unsigned off;
+ isl_size n_div;
+ int remove = -1;
+
+ isl_int_init(data.g);
+ isl_int_init(data.fl);
+ isl_int_init(data.fu);
+
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0)
+ goto error;
+
+ ctx = isl_basic_map_get_ctx(bmap);
+ off = isl_basic_map_offset(bmap, isl_dim_div);
+ data.v = isl_vec_alloc(ctx, off + n_div);
+ if (!data.v)
+ goto error;
+
+ while (n > 0) {
+ int i, l, u;
+ int best = -1;
+ isl_bool has_int;
+
+ for (i = 0; i < n_div; ++i) {
+ if (!pairs[i])
+ continue;
+ if (best >= 0 && pairs[best] <= pairs[i])
+ continue;
+ best = i;
+ }
+
+ i = best;
+ for (l = 0; l < bmap->n_ineq; ++l) {
+ if (!isl_int_is_pos(bmap->ineq[l][off + i]))
+ continue;
+ if (isl_int_is_one(bmap->ineq[l][off + i]))
+ continue;
+ for (u = 0; u < bmap->n_ineq; ++u) {
+ if (!isl_int_is_neg(bmap->ineq[u][off + i]))
+ continue;
+ if (isl_int_is_negone(bmap->ineq[u][off + i]))
+ continue;
+ has_int = int_between_bounds(bmap, i, l, u,
+ &data);
+ if (has_int < 0)
+ goto error;
+ if (data.tab && data.tab->empty)
+ break;
+ if (!has_int)
+ break;
+ }
+ if (u < bmap->n_ineq)
+ break;
+ }
+ if (data.tab && data.tab->empty) {
+ bmap = isl_basic_map_set_to_empty(bmap);
+ break;
+ }
+ if (l == bmap->n_ineq) {
+ remove = i;
+ break;
+ }
+ pairs[i] = 0;
+ --n;
+ }
+
+ test_ineq_data_clear(&data);
+
+ free(pairs);
+
+ if (remove < 0)
+ return bmap;
+
+ bmap = isl_basic_map_remove_dims(bmap, isl_dim_div, remove, 1);
+ return isl_basic_map_drop_redundant_divs(bmap);
+error:
+ free(pairs);
+ isl_basic_map_free(bmap);
+ test_ineq_data_clear(&data);
+ return NULL;
+}
+
+/* Given a pair of divs div1 and div2 such that, except for the lower bound l
+ * and the upper bound u, div1 always occurs together with div2 in the form
+ * (div1 + m div2), where m is the constant range on the variable div1
+ * allowed by l and u, replace the pair div1 and div2 by a single
+ * div that is equal to div1 + m div2.
+ *
+ * The new div will appear in the location that contains div2.
+ * We need to modify all constraints that contain
+ * div2 = (div - div1) / m
+ * The coefficient of div2 is known to be equal to 1 or -1.
+ * (If a constraint does not contain div2, it will also not contain div1.)
+ * If the constraint also contains div1, then we know they appear
+ * as f (div1 + m div2) and we can simply replace (div1 + m div2) by div,
+ * i.e., the coefficient of div is f.
+ *
+ * Otherwise, we first need to introduce div1 into the constraint.
+ * Let l be
+ *
+ * div1 + f >=0
+ *
+ * and u
+ *
+ * -div1 + f' >= 0
+ *
+ * A lower bound on div2
+ *
+ * div2 + t >= 0
+ *
+ * can be replaced by
+ *
+ * m div2 + div1 + m t + f >= 0
+ *
+ * An upper bound
+ *
+ * -div2 + t >= 0
+ *
+ * can be replaced by
+ *
+ * -(m div2 + div1) + m t + f' >= 0
+ *
+ * These constraint are those that we would obtain from eliminating
+ * div1 using Fourier-Motzkin.
+ *
+ * After all constraints have been modified, we drop the lower and upper
+ * bound and then drop div1.
+ * Since the new div is only placed in the same location that used
+ * to store div2, but otherwise has a different meaning, any possible
+ * explicit representation of the original div2 is removed.
+ */
+static __isl_give isl_basic_map *coalesce_divs(__isl_take isl_basic_map *bmap,
+ unsigned div1, unsigned div2, unsigned l, unsigned u)
+{
+ isl_ctx *ctx;
+ isl_int m;
+ isl_size v_div;
+ unsigned total;
+ int i;
+
+ ctx = isl_basic_map_get_ctx(bmap);
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0)
+ return isl_basic_map_free(bmap);
+ total = 1 + v_div + bmap->n_div;
+
+ isl_int_init(m);
+ isl_int_add(m, bmap->ineq[l][0], bmap->ineq[u][0]);
+ isl_int_add_ui(m, m, 1);
+
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ if (i == l || i == u)
+ continue;
+ if (isl_int_is_zero(bmap->ineq[i][1 + v_div + div2]))
+ continue;
+ if (isl_int_is_zero(bmap->ineq[i][1 + v_div + div1])) {
+ if (isl_int_is_pos(bmap->ineq[i][1 + v_div + div2]))
+ isl_seq_combine(bmap->ineq[i], m, bmap->ineq[i],
+ ctx->one, bmap->ineq[l], total);
+ else
+ isl_seq_combine(bmap->ineq[i], m, bmap->ineq[i],
+ ctx->one, bmap->ineq[u], total);
+ }
+ isl_int_set(bmap->ineq[i][1 + v_div + div2],
+ bmap->ineq[i][1 + v_div + div1]);
+ isl_int_set_si(bmap->ineq[i][1 + v_div + div1], 0);
+ }
+
+ isl_int_clear(m);
+ if (l > u) {
+ isl_basic_map_drop_inequality(bmap, l);
+ isl_basic_map_drop_inequality(bmap, u);
+ } else {
+ isl_basic_map_drop_inequality(bmap, u);
+ isl_basic_map_drop_inequality(bmap, l);
+ }
+ bmap = isl_basic_map_mark_div_unknown(bmap, div2);
+ bmap = isl_basic_map_drop_div(bmap, div1);
+ return bmap;
+}
+
+/* First check if we can coalesce any pair of divs and
+ * then continue with dropping more redundant divs.
+ *
+ * We loop over all pairs of lower and upper bounds on a div
+ * with coefficient 1 and -1, respectively, check if there
+ * is any other div "c" with which we can coalesce the div
+ * and if so, perform the coalescing.
+ */
+static __isl_give isl_basic_map *coalesce_or_drop_more_redundant_divs(
+ __isl_take isl_basic_map *bmap, int *pairs, int n)
+{
+ int i, l, u;
+ isl_size v_div;
+ isl_size n_div;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (v_div < 0 || n_div < 0)
+ return isl_basic_map_free(bmap);
+
+ for (i = 0; i < n_div; ++i) {
+ if (!pairs[i])
+ continue;
+ for (l = 0; l < bmap->n_ineq; ++l) {
+ if (!isl_int_is_one(bmap->ineq[l][1 + v_div + i]))
+ continue;
+ for (u = 0; u < bmap->n_ineq; ++u) {
+ int c;
+
+ if (!isl_int_is_negone(bmap->ineq[u][1+v_div+i]))
+ continue;
+ c = div_find_coalesce(bmap, pairs, i, l, u);
+ if (c < 0)
+ goto error;
+ if (c >= n_div)
+ continue;
+ free(pairs);
+ bmap = coalesce_divs(bmap, i, c, l, u);
+ return isl_basic_map_drop_redundant_divs(bmap);
+ }
+ }
+ }
+
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) {
+ free(pairs);
+ return bmap;
+ }
+
+ return drop_more_redundant_divs(bmap, pairs, n);
+error:
+ free(pairs);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Are the "n" coefficients starting at "first" of inequality constraints
+ * "i" and "j" of "bmap" equal to each other?
+ */
+static int is_parallel_part(__isl_keep isl_basic_map *bmap, int i, int j,
+ int first, int n)
+{
+ return isl_seq_eq(bmap->ineq[i] + first, bmap->ineq[j] + first, n);
+}
+
+/* Are inequality constraints "i" and "j" of "bmap" equal to each other,
+ * apart from the constant term and the coefficient at position "pos"?
+ */
+static isl_bool is_parallel_except(__isl_keep isl_basic_map *bmap, int i, int j,
+ int pos)
+{
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ return is_parallel_part(bmap, i, j, 1, pos - 1) &&
+ is_parallel_part(bmap, i, j, pos + 1, total - pos);
+}
+
+/* Are inequality constraints "i" and "j" of "bmap" opposite to each other,
+ * apart from the constant term and the coefficient at position "pos"?
+ */
+static isl_bool is_opposite_except(__isl_keep isl_basic_map *bmap, int i, int j,
+ int pos)
+{
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ return is_opposite_part(bmap, i, j, 1, pos - 1) &&
+ is_opposite_part(bmap, i, j, pos + 1, total - pos);
+}
+
+/* Restart isl_basic_map_drop_redundant_divs after "bmap" has
+ * been modified, simplying it if "simplify" is set.
+ * Free the temporary data structure "pairs" that was associated
+ * to the old version of "bmap".
+ */
+static __isl_give isl_basic_map *drop_redundant_divs_again(
+ __isl_take isl_basic_map *bmap, __isl_take int *pairs, int simplify)
+{
+ if (simplify)
+ bmap = isl_basic_map_simplify(bmap);
+ free(pairs);
+ return isl_basic_map_drop_redundant_divs(bmap);
+}
+
+/* Is "div" the single unknown existentially quantified variable
+ * in inequality constraint "ineq" of "bmap"?
+ * "div" is known to have a non-zero coefficient in "ineq".
+ */
+static isl_bool single_unknown(__isl_keep isl_basic_map *bmap, int ineq,
+ int div)
+{
+ int i;
+ isl_size n_div;
+ unsigned o_div;
+ isl_bool known;
+
+ known = isl_basic_map_div_is_known(bmap, div);
+ if (known < 0 || known)
+ return isl_bool_not(known);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0)
+ return isl_bool_error;
+ if (n_div == 1)
+ return isl_bool_true;
+ o_div = isl_basic_map_offset(bmap, isl_dim_div);
+ for (i = 0; i < n_div; ++i) {
+ isl_bool known;
+
+ if (i == div)
+ continue;
+ if (isl_int_is_zero(bmap->ineq[ineq][o_div + i]))
+ continue;
+ known = isl_basic_map_div_is_known(bmap, i);
+ if (known < 0 || !known)
+ return known;
+ }
+
+ return isl_bool_true;
+}
+
+/* Does integer division "div" have coefficient 1 in inequality constraint
+ * "ineq" of "map"?
+ */
+static isl_bool has_coef_one(__isl_keep isl_basic_map *bmap, int div, int ineq)
+{
+ unsigned o_div;
+
+ o_div = isl_basic_map_offset(bmap, isl_dim_div);
+ if (isl_int_is_one(bmap->ineq[ineq][o_div + div]))
+ return isl_bool_true;
+
+ return isl_bool_false;
+}
+
+/* Turn inequality constraint "ineq" of "bmap" into an equality and
+ * then try and drop redundant divs again,
+ * freeing the temporary data structure "pairs" that was associated
+ * to the old version of "bmap".
+ */
+static __isl_give isl_basic_map *set_eq_and_try_again(
+ __isl_take isl_basic_map *bmap, int ineq, __isl_take int *pairs)
+{
+ bmap = isl_basic_map_cow(bmap);
+ isl_basic_map_inequality_to_equality(bmap, ineq);
+ return drop_redundant_divs_again(bmap, pairs, 1);
+}
+
+/* Drop the integer division at position "div", along with the two
+ * inequality constraints "ineq1" and "ineq2" in which it appears
+ * from "bmap" and then try and drop redundant divs again,
+ * freeing the temporary data structure "pairs" that was associated
+ * to the old version of "bmap".
+ */
+static __isl_give isl_basic_map *drop_div_and_try_again(
+ __isl_take isl_basic_map *bmap, int div, int ineq1, int ineq2,
+ __isl_take int *pairs)
+{
+ if (ineq1 > ineq2) {
+ isl_basic_map_drop_inequality(bmap, ineq1);
+ isl_basic_map_drop_inequality(bmap, ineq2);
+ } else {
+ isl_basic_map_drop_inequality(bmap, ineq2);
+ isl_basic_map_drop_inequality(bmap, ineq1);
+ }
+ bmap = isl_basic_map_drop_div(bmap, div);
+ return drop_redundant_divs_again(bmap, pairs, 0);
+}
+
+/* Given two inequality constraints
+ *
+ * f(x) + n d + c >= 0, (ineq)
+ *
+ * with d the variable at position "pos", and
+ *
+ * f(x) + c0 >= 0, (lower)
+ *
+ * compute the maximal value of the lower bound ceil((-f(x) - c)/n)
+ * determined by the first constraint.
+ * That is, store
+ *
+ * ceil((c0 - c)/n)
+ *
+ * in *l.
+ */
+static void lower_bound_from_parallel(__isl_keep isl_basic_map *bmap,
+ int ineq, int lower, int pos, isl_int *l)
+{
+ isl_int_neg(*l, bmap->ineq[ineq][0]);
+ isl_int_add(*l, *l, bmap->ineq[lower][0]);
+ isl_int_cdiv_q(*l, *l, bmap->ineq[ineq][pos]);
+}
+
+/* Given two inequality constraints
+ *
+ * f(x) + n d + c >= 0, (ineq)
+ *
+ * with d the variable at position "pos", and
+ *
+ * -f(x) - c0 >= 0, (upper)
+ *
+ * compute the minimal value of the lower bound ceil((-f(x) - c)/n)
+ * determined by the first constraint.
+ * That is, store
+ *
+ * ceil((-c1 - c)/n)
+ *
+ * in *u.
+ */
+static void lower_bound_from_opposite(__isl_keep isl_basic_map *bmap,
+ int ineq, int upper, int pos, isl_int *u)
+{
+ isl_int_neg(*u, bmap->ineq[ineq][0]);
+ isl_int_sub(*u, *u, bmap->ineq[upper][0]);
+ isl_int_cdiv_q(*u, *u, bmap->ineq[ineq][pos]);
+}
+
+/* Given a lower bound constraint "ineq" on "div" in "bmap",
+ * does the corresponding lower bound have a fixed value in "bmap"?
+ *
+ * In particular, "ineq" is of the form
+ *
+ * f(x) + n d + c >= 0
+ *
+ * with n > 0, c the constant term and
+ * d the existentially quantified variable "div".
+ * That is, the lower bound is
+ *
+ * ceil((-f(x) - c)/n)
+ *
+ * Look for a pair of constraints
+ *
+ * f(x) + c0 >= 0
+ * -f(x) + c1 >= 0
+ *
+ * i.e., -c1 <= -f(x) <= c0, that fix ceil((-f(x) - c)/n) to a constant value.
+ * That is, check that
+ *
+ * ceil((-c1 - c)/n) = ceil((c0 - c)/n)
+ *
+ * If so, return the index of inequality f(x) + c0 >= 0.
+ * Otherwise, return bmap->n_ineq.
+ * Return -1 on error.
+ */
+static int lower_bound_is_cst(__isl_keep isl_basic_map *bmap, int div, int ineq)
+{
+ int i;
+ int lower = -1, upper = -1;
+ unsigned o_div;
+ isl_int l, u;
+ int equal;
+
+ o_div = isl_basic_map_offset(bmap, isl_dim_div);
+ for (i = 0; i < bmap->n_ineq && (lower < 0 || upper < 0); ++i) {
+ isl_bool par, opp;
+
+ if (i == ineq)
+ continue;
+ if (!isl_int_is_zero(bmap->ineq[i][o_div + div]))
+ continue;
+ par = isl_bool_false;
+ if (lower < 0)
+ par = is_parallel_except(bmap, ineq, i, o_div + div);
+ if (par < 0)
+ return -1;
+ if (par) {
+ lower = i;
+ continue;
+ }
+ opp = isl_bool_false;
+ if (upper < 0)
+ opp = is_opposite_except(bmap, ineq, i, o_div + div);
+ if (opp < 0)
+ return -1;
+ if (opp)
+ upper = i;
+ }
+
+ if (lower < 0 || upper < 0)
+ return bmap->n_ineq;
+
+ isl_int_init(l);
+ isl_int_init(u);
+
+ lower_bound_from_parallel(bmap, ineq, lower, o_div + div, &l);
+ lower_bound_from_opposite(bmap, ineq, upper, o_div + div, &u);
+
+ equal = isl_int_eq(l, u);
+
+ isl_int_clear(l);
+ isl_int_clear(u);
+
+ return equal ? lower : bmap->n_ineq;
+}
+
+/* Given a lower bound constraint "ineq" on the existentially quantified
+ * variable "div", such that the corresponding lower bound has
+ * a fixed value in "bmap", assign this fixed value to the variable and
+ * then try and drop redundant divs again,
+ * freeing the temporary data structure "pairs" that was associated
+ * to the old version of "bmap".
+ * "lower" determines the constant value for the lower bound.
+ *
+ * In particular, "ineq" is of the form
+ *
+ * f(x) + n d + c >= 0,
+ *
+ * while "lower" is of the form
+ *
+ * f(x) + c0 >= 0
+ *
+ * The lower bound is ceil((-f(x) - c)/n) and its constant value
+ * is ceil((c0 - c)/n).
+ */
+static __isl_give isl_basic_map *fix_cst_lower(__isl_take isl_basic_map *bmap,
+ int div, int ineq, int lower, int *pairs)
+{
+ isl_int c;
+ unsigned o_div;
+
+ isl_int_init(c);
+
+ o_div = isl_basic_map_offset(bmap, isl_dim_div);
+ lower_bound_from_parallel(bmap, ineq, lower, o_div + div, &c);
+ bmap = isl_basic_map_fix(bmap, isl_dim_div, div, c);
+ free(pairs);
+
+ isl_int_clear(c);
+
+ return isl_basic_map_drop_redundant_divs(bmap);
+}
+
+/* Do any of the integer divisions of "bmap" involve integer division "div"?
+ *
+ * The integer division "div" could only ever appear in any later
+ * integer division (with an explicit representation).
+ */
+static isl_bool any_div_involves_div(__isl_keep isl_basic_map *bmap, int div)
+{
+ int i;
+ isl_size v_div, n_div;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (v_div < 0 || n_div < 0)
+ return isl_bool_error;
+
+ for (i = div + 1; i < n_div; ++i) {
+ isl_bool unknown;
+
+ unknown = isl_basic_map_div_is_marked_unknown(bmap, i);
+ if (unknown < 0)
+ return isl_bool_error;
+ if (unknown)
+ continue;
+ if (!isl_int_is_zero(bmap->div[i][1 + 1 + v_div + div]))
+ return isl_bool_true;
+ }
+
+ return isl_bool_false;
+}
+
+/* Remove divs that are not strictly needed based on the inequality
+ * constraints.
+ * In particular, if a div only occurs positively (or negatively)
+ * in constraints, then it can simply be dropped.
+ * Also, if a div occurs in only two constraints and if moreover
+ * those two constraints are opposite to each other, except for the constant
+ * term and if the sum of the constant terms is such that for any value
+ * of the other values, there is always at least one integer value of the
+ * div, i.e., if one plus this sum is greater than or equal to
+ * the (absolute value) of the coefficient of the div in the constraints,
+ * then we can also simply drop the div.
+ *
+ * If an existentially quantified variable does not have an explicit
+ * representation, appears in only a single lower bound that does not
+ * involve any other such existentially quantified variables and appears
+ * in this lower bound with coefficient 1,
+ * then fix the variable to the value of the lower bound. That is,
+ * turn the inequality into an equality.
+ * If for any value of the other variables, there is any value
+ * for the existentially quantified variable satisfying the constraints,
+ * then this lower bound also satisfies the constraints.
+ * It is therefore safe to pick this lower bound.
+ *
+ * The same reasoning holds even if the coefficient is not one.
+ * However, fixing the variable to the value of the lower bound may
+ * in general introduce an extra integer division, in which case
+ * it may be better to pick another value.
+ * If this integer division has a known constant value, then plugging
+ * in this constant value removes the existentially quantified variable
+ * completely. In particular, if the lower bound is of the form
+ * ceil((-f(x) - c)/n) and there are two constraints, f(x) + c0 >= 0 and
+ * -f(x) + c1 >= 0 such that ceil((-c1 - c)/n) = ceil((c0 - c)/n),
+ * then the existentially quantified variable can be assigned this
+ * shared value.
+ *
+ * We skip divs that appear in equalities or in the definition of other divs.
+ * Divs that appear in the definition of other divs usually occur in at least
+ * 4 constraints, but the constraints may have been simplified.
+ *
+ * If any divs are left after these simple checks then we move on
+ * to more complicated cases in drop_more_redundant_divs.
+ */
+static __isl_give isl_basic_map *isl_basic_map_drop_redundant_divs_ineq(
+ __isl_take isl_basic_map *bmap)
+{
+ int i, j;
+ isl_size off;
+ int *pairs = NULL;
+ int n = 0;
+ isl_size n_ineq;
+
+ if (!bmap)
+ goto error;
+ if (bmap->n_div == 0)
+ return bmap;
+
+ off = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (off < 0)
+ return isl_basic_map_free(bmap);
+ pairs = isl_calloc_array(bmap->ctx, int, bmap->n_div);
+ if (!pairs)
+ goto error;
+
+ n_ineq = isl_basic_map_n_inequality(bmap);
+ if (n_ineq < 0)
+ goto error;
+ for (i = 0; i < bmap->n_div; ++i) {
+ int pos, neg;
+ int last_pos, last_neg;
+ int redundant;
+ int defined;
+ isl_bool involves, opp, set_div;
+
+ defined = !isl_int_is_zero(bmap->div[i][0]);
+ involves = any_div_involves_div(bmap, i);
+ if (involves < 0)
+ goto error;
+ if (involves)
+ continue;
+ for (j = 0; j < bmap->n_eq; ++j)
+ if (!isl_int_is_zero(bmap->eq[j][1 + off + i]))
+ break;
+ if (j < bmap->n_eq)
+ continue;
+ ++n;
+ pos = neg = 0;
+ for (j = 0; j < bmap->n_ineq; ++j) {
+ if (isl_int_is_pos(bmap->ineq[j][1 + off + i])) {
+ last_pos = j;
+ ++pos;
+ }
+ if (isl_int_is_neg(bmap->ineq[j][1 + off + i])) {
+ last_neg = j;
+ ++neg;
+ }
+ }
+ pairs[i] = pos * neg;
+ if (pairs[i] == 0) {
+ for (j = bmap->n_ineq - 1; j >= 0; --j)
+ if (!isl_int_is_zero(bmap->ineq[j][1+off+i]))
+ isl_basic_map_drop_inequality(bmap, j);
+ bmap = isl_basic_map_drop_div(bmap, i);
+ return drop_redundant_divs_again(bmap, pairs, 0);
+ }
+ if (pairs[i] != 1)
+ opp = isl_bool_false;
+ else
+ opp = is_opposite(bmap, last_pos, last_neg);
+ if (opp < 0)
+ goto error;
+ if (!opp) {
+ int lower;
+ isl_bool single, one;
+
+ if (pos != 1)
+ continue;
+ single = single_unknown(bmap, last_pos, i);
+ if (single < 0)
+ goto error;
+ if (!single)
+ continue;
+ one = has_coef_one(bmap, i, last_pos);
+ if (one < 0)
+ goto error;
+ if (one)
+ return set_eq_and_try_again(bmap, last_pos,
+ pairs);
+ lower = lower_bound_is_cst(bmap, i, last_pos);
+ if (lower < 0)
+ goto error;
+ if (lower < n_ineq)
+ return fix_cst_lower(bmap, i, last_pos, lower,
+ pairs);
+ continue;
+ }
+
+ isl_int_add(bmap->ineq[last_pos][0],
+ bmap->ineq[last_pos][0], bmap->ineq[last_neg][0]);
+ isl_int_add_ui(bmap->ineq[last_pos][0],
+ bmap->ineq[last_pos][0], 1);
+ redundant = isl_int_ge(bmap->ineq[last_pos][0],
+ bmap->ineq[last_pos][1+off+i]);
+ isl_int_sub_ui(bmap->ineq[last_pos][0],
+ bmap->ineq[last_pos][0], 1);
+ isl_int_sub(bmap->ineq[last_pos][0],
+ bmap->ineq[last_pos][0], bmap->ineq[last_neg][0]);
+ if (redundant)
+ return drop_div_and_try_again(bmap, i,
+ last_pos, last_neg, pairs);
+ if (defined)
+ set_div = isl_bool_false;
+ else
+ set_div = ok_to_set_div_from_bound(bmap, i, last_pos);
+ if (set_div < 0)
+ return isl_basic_map_free(bmap);
+ if (set_div) {
+ bmap = set_div_from_lower_bound(bmap, i, last_pos);
+ return drop_redundant_divs_again(bmap, pairs, 1);
+ }
+ pairs[i] = 0;
+ --n;
+ }
+
+ if (n > 0)
+ return coalesce_or_drop_more_redundant_divs(bmap, pairs, n);
+
+ free(pairs);
+ return bmap;
+error:
+ free(pairs);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Consider the coefficients at "c" as a row vector and replace
+ * them with their product with "T". "T" is assumed to be a square matrix.
+ */
+static isl_stat preimage(isl_int *c, __isl_keep isl_mat *T)
+{
+ isl_size n;
+ isl_ctx *ctx;
+ isl_vec *v;
+
+ n = isl_mat_rows(T);
+ if (n < 0)
+ return isl_stat_error;
+ if (isl_seq_first_non_zero(c, n) == -1)
+ return isl_stat_ok;
+ ctx = isl_mat_get_ctx(T);
+ v = isl_vec_alloc(ctx, n);
+ if (!v)
+ return isl_stat_error;
+ isl_seq_swp_or_cpy(v->el, c, n);
+ v = isl_vec_mat_product(v, isl_mat_copy(T));
+ if (!v)
+ return isl_stat_error;
+ isl_seq_swp_or_cpy(c, v->el, n);
+ isl_vec_free(v);
+
+ return isl_stat_ok;
+}
+
+/* Plug in T for the variables in "bmap" starting at "pos".
+ * T is a linear unimodular matrix, i.e., without constant term.
+ */
+static __isl_give isl_basic_map *isl_basic_map_preimage_vars(
+ __isl_take isl_basic_map *bmap, unsigned pos, __isl_take isl_mat *T)
+{
+ int i;
+ isl_size n_row, n_col;
+
+ bmap = isl_basic_map_cow(bmap);
+ n_row = isl_mat_rows(T);
+ n_col = isl_mat_cols(T);
+ if (!bmap || n_row < 0 || n_col < 0)
+ goto error;
+
+ if (n_col != n_row)
+ isl_die(isl_mat_get_ctx(T), isl_error_invalid,
+ "expecting square matrix", goto error);
+
+ if (isl_basic_map_check_range(bmap, isl_dim_all, pos, n_col) < 0)
+ goto error;
+
+ for (i = 0; i < bmap->n_eq; ++i)
+ if (preimage(bmap->eq[i] + 1 + pos, T) < 0)
+ goto error;
+ for (i = 0; i < bmap->n_ineq; ++i)
+ if (preimage(bmap->ineq[i] + 1 + pos, T) < 0)
+ goto error;
+ for (i = 0; i < bmap->n_div; ++i) {
+ if (isl_basic_map_div_is_marked_unknown(bmap, i))
+ continue;
+ if (preimage(bmap->div[i] + 1 + 1 + pos, T) < 0)
+ goto error;
+ }
+
+ isl_mat_free(T);
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ isl_mat_free(T);
+ return NULL;
+}
+
+/* Remove divs that are not strictly needed.
+ *
+ * First look for an equality constraint involving two or more
+ * existentially quantified variables without an explicit
+ * representation. Replace the combination that appears
+ * in the equality constraint by a single existentially quantified
+ * variable such that the equality can be used to derive
+ * an explicit representation for the variable.
+ * If there are no more such equality constraints, then continue
+ * with isl_basic_map_drop_redundant_divs_ineq.
+ *
+ * In particular, if the equality constraint is of the form
+ *
+ * f(x) + \sum_i c_i a_i = 0
+ *
+ * with a_i existentially quantified variable without explicit
+ * representation, then apply a transformation on the existentially
+ * quantified variables to turn the constraint into
+ *
+ * f(x) + g a_1' = 0
+ *
+ * with g the gcd of the c_i.
+ * In order to easily identify which existentially quantified variables
+ * have a complete explicit representation, i.e., without being defined
+ * in terms of other existentially quantified variables without
+ * an explicit representation, the existentially quantified variables
+ * are first sorted.
+ *
+ * The variable transformation is computed by extending the row
+ * [c_1/g ... c_n/g] to a unimodular matrix, obtaining the transformation
+ *
+ * [a_1'] [c_1/g ... c_n/g] [ a_1 ]
+ * [a_2'] [ a_2 ]
+ * ... = U ....
+ * [a_n'] [ a_n ]
+ *
+ * with [c_1/g ... c_n/g] representing the first row of U.
+ * The inverse of U is then plugged into the original constraints.
+ * The call to isl_basic_map_simplify makes sure the explicit
+ * representation for a_1' is extracted from the equality constraint.
+ */
+__isl_give isl_basic_map *isl_basic_map_drop_redundant_divs(
+ __isl_take isl_basic_map *bmap)
+{
+ int first;
+ int i;
+ unsigned o_div;
+ isl_size n_div;
+ int l;
+ isl_ctx *ctx;
+ isl_mat *T;
+
+ if (!bmap)
+ return NULL;
+ if (isl_basic_map_divs_known(bmap))
+ return isl_basic_map_drop_redundant_divs_ineq(bmap);
+ if (bmap->n_eq == 0)
+ return isl_basic_map_drop_redundant_divs_ineq(bmap);
+ bmap = isl_basic_map_sort_divs(bmap);
+ if (!bmap)
+ return NULL;
+
+ first = isl_basic_map_first_unknown_div(bmap);
+ if (first < 0)
+ return isl_basic_map_free(bmap);
+
+ o_div = isl_basic_map_offset(bmap, isl_dim_div);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (n_div < 0)
+ return isl_basic_map_free(bmap);
+
+ for (i = 0; i < bmap->n_eq; ++i) {
+ l = isl_seq_first_non_zero(bmap->eq[i] + o_div + first,
+ n_div - (first));
+ if (l < 0)
+ continue;
+ l += first;
+ if (isl_seq_first_non_zero(bmap->eq[i] + o_div + l + 1,
+ n_div - (l + 1)) == -1)
+ continue;
+ break;
+ }
+ if (i >= bmap->n_eq)
+ return isl_basic_map_drop_redundant_divs_ineq(bmap);
+
+ ctx = isl_basic_map_get_ctx(bmap);
+ T = isl_mat_alloc(ctx, n_div - l, n_div - l);
+ if (!T)
+ return isl_basic_map_free(bmap);
+ isl_seq_cpy(T->row[0], bmap->eq[i] + o_div + l, n_div - l);
+ T = isl_mat_normalize_row(T, 0);
+ T = isl_mat_unimodular_complete(T, 1);
+ T = isl_mat_right_inverse(T);
+
+ for (i = l; i < n_div; ++i)
+ bmap = isl_basic_map_mark_div_unknown(bmap, i);
+ bmap = isl_basic_map_preimage_vars(bmap, o_div - 1 + l, T);
+ bmap = isl_basic_map_simplify(bmap);
+
+ return isl_basic_map_drop_redundant_divs(bmap);
+}
+
+/* Does "bmap" satisfy any equality that involves more than 2 variables
+ * and/or has coefficients different from -1 and 1?
+ */
+static isl_bool has_multiple_var_equality(__isl_keep isl_basic_map *bmap)
+{
+ int i;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+
+ for (i = 0; i < bmap->n_eq; ++i) {
+ int j, k;
+
+ j = isl_seq_first_non_zero(bmap->eq[i] + 1, total);
+ if (j < 0)
+ continue;
+ if (!isl_int_is_one(bmap->eq[i][1 + j]) &&
+ !isl_int_is_negone(bmap->eq[i][1 + j]))
+ return isl_bool_true;
+
+ j += 1;
+ k = isl_seq_first_non_zero(bmap->eq[i] + 1 + j, total - j);
+ if (k < 0)
+ continue;
+ j += k;
+ if (!isl_int_is_one(bmap->eq[i][1 + j]) &&
+ !isl_int_is_negone(bmap->eq[i][1 + j]))
+ return isl_bool_true;
+
+ j += 1;
+ k = isl_seq_first_non_zero(bmap->eq[i] + 1 + j, total - j);
+ if (k >= 0)
+ return isl_bool_true;
+ }
+
+ return isl_bool_false;
+}
+
+/* Remove any common factor g from the constraint coefficients in "v".
+ * The constant term is stored in the first position and is replaced
+ * by floor(c/g). If any common factor is removed and if this results
+ * in a tightening of the constraint, then set *tightened.
+ */
+static __isl_give isl_vec *normalize_constraint(__isl_take isl_vec *v,
+ int *tightened)
+{
+ isl_ctx *ctx;
+
+ if (!v)
+ return NULL;
+ ctx = isl_vec_get_ctx(v);
+ isl_seq_gcd(v->el + 1, v->size - 1, &ctx->normalize_gcd);
+ if (isl_int_is_zero(ctx->normalize_gcd))
+ return v;
+ if (isl_int_is_one(ctx->normalize_gcd))
+ return v;
+ v = isl_vec_cow(v);
+ if (!v)
+ return NULL;
+ if (tightened && !isl_int_is_divisible_by(v->el[0], ctx->normalize_gcd))
+ *tightened = 1;
+ isl_int_fdiv_q(v->el[0], v->el[0], ctx->normalize_gcd);
+ isl_seq_scale_down(v->el + 1, v->el + 1, ctx->normalize_gcd,
+ v->size - 1);
+ return v;
+}
+
+/* If "bmap" is an integer set that satisfies any equality involving
+ * more than 2 variables and/or has coefficients different from -1 and 1,
+ * then use variable compression to reduce the coefficients by removing
+ * any (hidden) common factor.
+ * In particular, apply the variable compression to each constraint,
+ * factor out any common factor in the non-constant coefficients and
+ * then apply the inverse of the compression.
+ * At the end, we mark the basic map as having reduced constants.
+ * If this flag is still set on the next invocation of this function,
+ * then we skip the computation.
+ *
+ * Removing a common factor may result in a tightening of some of
+ * the constraints. If this happens, then we may end up with two
+ * opposite inequalities that can be replaced by an equality.
+ * We therefore call isl_basic_map_detect_inequality_pairs,
+ * which checks for such pairs of inequalities as well as eliminate_divs_eq
+ * and isl_basic_map_gauss if such a pair was found.
+ *
+ * Tightening may also result in some other constraints becoming
+ * (rationally) redundant with respect to the tightened constraint
+ * (in combination with other constraints). The basic map may
+ * therefore no longer be assumed to have no redundant constraints.
+ *
+ * Note that this function may leave the result in an inconsistent state.
+ * In particular, the constraints may not be gaussed.
+ * Unfortunately, isl_map_coalesce actually depends on this inconsistent state
+ * for some of the test cases to pass successfully.
+ * Any potential modification of the representation is therefore only
+ * performed on a single copy of the basic map.
+ */
+__isl_give isl_basic_map *isl_basic_map_reduce_coefficients(
+ __isl_take isl_basic_map *bmap)
+{
+ isl_size total;
+ isl_bool multi;
+ isl_ctx *ctx;
+ isl_vec *v;
+ isl_mat *eq, *T, *T2;
+ int i;
+ int tightened;
+
+ if (!bmap)
+ return NULL;
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_REDUCED_COEFFICIENTS))
+ return bmap;
+ if (isl_basic_map_is_rational(bmap))
+ return bmap;
+ if (bmap->n_eq == 0)
+ return bmap;
+ multi = has_multiple_var_equality(bmap);
+ if (multi < 0)
+ return isl_basic_map_free(bmap);
+ if (!multi)
+ return bmap;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_basic_map_free(bmap);
+ ctx = isl_basic_map_get_ctx(bmap);
+ v = isl_vec_alloc(ctx, 1 + total);
+ if (!v)
+ return isl_basic_map_free(bmap);
+
+ eq = isl_mat_sub_alloc6(ctx, bmap->eq, 0, bmap->n_eq, 0, 1 + total);
+ T = isl_mat_variable_compression(eq, &T2);
+ if (!T || !T2)
+ goto error;
+ if (T->n_col == 0) {
+ isl_mat_free(T);
+ isl_mat_free(T2);
+ isl_vec_free(v);
+ return isl_basic_map_set_to_empty(bmap);
+ }
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ goto error;
+
+ tightened = 0;
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ isl_seq_cpy(v->el, bmap->ineq[i], 1 + total);
+ v = isl_vec_mat_product(v, isl_mat_copy(T));
+ v = normalize_constraint(v, &tightened);
+ v = isl_vec_mat_product(v, isl_mat_copy(T2));
+ if (!v)
+ goto error;
+ isl_seq_cpy(bmap->ineq[i], v->el, 1 + total);
+ }
+
+ isl_mat_free(T);
+ isl_mat_free(T2);
+ isl_vec_free(v);
+
+ ISL_F_SET(bmap, ISL_BASIC_MAP_REDUCED_COEFFICIENTS);
+
+ if (tightened) {
+ int progress = 0;
+
+ ISL_F_CLR(bmap, ISL_BASIC_MAP_NO_REDUNDANT);
+ bmap = isl_basic_map_detect_inequality_pairs(bmap, &progress);
+ if (progress) {
+ bmap = eliminate_divs_eq(bmap, &progress);
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ }
+ }
+
+ return bmap;
+error:
+ isl_mat_free(T);
+ isl_mat_free(T2);
+ isl_vec_free(v);
+ return isl_basic_map_free(bmap);
+}
+
+/* Shift the integer division at position "div" of "bmap"
+ * by "shift" times the variable at position "pos".
+ * "pos" is as determined by isl_basic_map_offset, i.e., pos == 0
+ * corresponds to the constant term.
+ *
+ * That is, if the integer division has the form
+ *
+ * floor(f(x)/d)
+ *
+ * then replace it by
+ *
+ * floor((f(x) + shift * d * x_pos)/d) - shift * x_pos
+ */
+__isl_give isl_basic_map *isl_basic_map_shift_div(
+ __isl_take isl_basic_map *bmap, int div, int pos, isl_int shift)
+{
+ int i;
+ isl_size total, n_div;
+
+ if (isl_int_is_zero(shift))
+ return bmap;
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ total -= n_div;
+ if (total < 0 || n_div < 0)
+ return isl_basic_map_free(bmap);
+
+ isl_int_addmul(bmap->div[div][1 + pos], shift, bmap->div[div][0]);
+
+ for (i = 0; i < bmap->n_eq; ++i) {
+ if (isl_int_is_zero(bmap->eq[i][1 + total + div]))
+ continue;
+ isl_int_submul(bmap->eq[i][pos],
+ shift, bmap->eq[i][1 + total + div]);
+ }
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ if (isl_int_is_zero(bmap->ineq[i][1 + total + div]))
+ continue;
+ isl_int_submul(bmap->ineq[i][pos],
+ shift, bmap->ineq[i][1 + total + div]);
+ }
+ for (i = 0; i < bmap->n_div; ++i) {
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ if (isl_int_is_zero(bmap->div[i][1 + 1 + total + div]))
+ continue;
+ isl_int_submul(bmap->div[i][1 + pos],
+ shift, bmap->div[i][1 + 1 + total + div]);
+ }
+
+ return bmap;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_subtract.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_subtract.c
new file mode 100644
index 00000000000..6431b3daa96
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_subtract.c
@@ -0,0 +1,944 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <isl_map_private.h>
+#include <isl_seq.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include "isl_tab.h"
+#include <isl_point_private.h>
+#include <isl_vec_private.h>
+
+#include <set_to_map.c>
+#include <set_from_map.c>
+
+/* Expand the constraint "c" into "v". The initial "dim" dimensions
+ * are the same, but "v" may have more divs than "c" and the divs of "c"
+ * may appear in different positions in "v".
+ * The number of divs in "c" is given by "n_div" and the mapping
+ * of divs in "c" to divs in "v" is given by "div_map".
+ *
+ * Although it shouldn't happen in practice, it is theoretically
+ * possible that two or more divs in "c" are mapped to the same div in "v".
+ * These divs are then necessarily the same, so we simply add their
+ * coefficients.
+ */
+static void expand_constraint(isl_vec *v, unsigned dim,
+ isl_int *c, int *div_map, unsigned n_div)
+{
+ int i;
+
+ isl_seq_cpy(v->el, c, 1 + dim);
+ isl_seq_clr(v->el + 1 + dim, v->size - (1 + dim));
+
+ for (i = 0; i < n_div; ++i) {
+ int pos = 1 + dim + div_map[i];
+ isl_int_add(v->el[pos], v->el[pos], c[1 + dim + i]);
+ }
+}
+
+/* Add all constraints of bmap to tab. The equalities of bmap
+ * are added as a pair of inequalities.
+ */
+static isl_stat tab_add_constraints(struct isl_tab *tab,
+ __isl_keep isl_basic_map *bmap, int *div_map)
+{
+ int i;
+ unsigned dim;
+ isl_size tab_total;
+ isl_size bmap_n_div;
+ isl_size bmap_total;
+ isl_vec *v;
+
+ if (!tab || !bmap)
+ return isl_stat_error;
+
+ tab_total = isl_basic_map_dim(tab->bmap, isl_dim_all);
+ bmap_total = isl_basic_map_dim(bmap, isl_dim_all);
+ bmap_n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ dim = bmap_total - bmap_n_div;
+ if (tab_total < 0 || bmap_total < 0 || bmap_n_div < 0)
+ return isl_stat_error;
+
+ if (isl_tab_extend_cons(tab, 2 * bmap->n_eq + bmap->n_ineq) < 0)
+ return isl_stat_error;
+
+ v = isl_vec_alloc(bmap->ctx, 1 + tab_total);
+ if (!v)
+ return isl_stat_error;
+
+ for (i = 0; i < bmap->n_eq; ++i) {
+ expand_constraint(v, dim, bmap->eq[i], div_map, bmap_n_div);
+ if (isl_tab_add_ineq(tab, v->el) < 0)
+ goto error;
+ isl_seq_neg(bmap->eq[i], bmap->eq[i], 1 + bmap_total);
+ expand_constraint(v, dim, bmap->eq[i], div_map, bmap_n_div);
+ if (isl_tab_add_ineq(tab, v->el) < 0)
+ goto error;
+ isl_seq_neg(bmap->eq[i], bmap->eq[i], 1 + bmap_total);
+ if (tab->empty)
+ break;
+ }
+
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ expand_constraint(v, dim, bmap->ineq[i], div_map, bmap_n_div);
+ if (isl_tab_add_ineq(tab, v->el) < 0)
+ goto error;
+ if (tab->empty)
+ break;
+ }
+
+ isl_vec_free(v);
+ return isl_stat_ok;
+error:
+ isl_vec_free(v);
+ return isl_stat_error;
+}
+
+/* Add a specific constraint of bmap (or its opposite) to tab.
+ * The position of the constraint is specified by "c", where
+ * the equalities of bmap are counted twice, once for the inequality
+ * that is equal to the equality, and once for its negation.
+ *
+ * Each of these constraints has been added to "tab" before by
+ * tab_add_constraints (and later removed again), so there should
+ * already be a row available for the constraint.
+ */
+static isl_stat tab_add_constraint(struct isl_tab *tab,
+ __isl_keep isl_basic_map *bmap, int *div_map, int c, int oppose)
+{
+ unsigned dim;
+ isl_size tab_total;
+ isl_size bmap_n_div;
+ isl_size bmap_total;
+ isl_vec *v;
+ isl_stat r;
+
+ if (!tab || !bmap)
+ return isl_stat_error;
+
+ tab_total = isl_basic_map_dim(tab->bmap, isl_dim_all);
+ bmap_total = isl_basic_map_dim(bmap, isl_dim_all);
+ bmap_n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ dim = bmap_total - bmap_n_div;
+ if (tab_total < 0 || bmap_total < 0 || bmap_n_div < 0)
+ return isl_stat_error;
+
+ v = isl_vec_alloc(bmap->ctx, 1 + tab_total);
+ if (!v)
+ return isl_stat_error;
+
+ if (c < 2 * bmap->n_eq) {
+ if ((c % 2) != oppose)
+ isl_seq_neg(bmap->eq[c/2], bmap->eq[c/2],
+ 1 + bmap_total);
+ if (oppose)
+ isl_int_sub_ui(bmap->eq[c/2][0], bmap->eq[c/2][0], 1);
+ expand_constraint(v, dim, bmap->eq[c/2], div_map, bmap_n_div);
+ r = isl_tab_add_ineq(tab, v->el);
+ if (oppose)
+ isl_int_add_ui(bmap->eq[c/2][0], bmap->eq[c/2][0], 1);
+ if ((c % 2) != oppose)
+ isl_seq_neg(bmap->eq[c/2], bmap->eq[c/2],
+ 1 + bmap_total);
+ } else {
+ c -= 2 * bmap->n_eq;
+ if (oppose) {
+ isl_seq_neg(bmap->ineq[c], bmap->ineq[c],
+ 1 + bmap_total);
+ isl_int_sub_ui(bmap->ineq[c][0], bmap->ineq[c][0], 1);
+ }
+ expand_constraint(v, dim, bmap->ineq[c], div_map, bmap_n_div);
+ r = isl_tab_add_ineq(tab, v->el);
+ if (oppose) {
+ isl_int_add_ui(bmap->ineq[c][0], bmap->ineq[c][0], 1);
+ isl_seq_neg(bmap->ineq[c], bmap->ineq[c],
+ 1 + bmap_total);
+ }
+ }
+
+ isl_vec_free(v);
+ return r;
+}
+
+static isl_stat tab_add_divs(struct isl_tab *tab,
+ __isl_keep isl_basic_map *bmap, int **div_map)
+{
+ int i, j;
+ struct isl_vec *vec;
+ isl_size total;
+ unsigned dim;
+
+ if (!bmap)
+ return isl_stat_error;
+ if (!bmap->n_div)
+ return isl_stat_ok;
+
+ if (!*div_map)
+ *div_map = isl_alloc_array(bmap->ctx, int, bmap->n_div);
+ if (!*div_map)
+ return isl_stat_error;
+
+ total = isl_basic_map_dim(tab->bmap, isl_dim_all);
+ if (total < 0)
+ return isl_stat_error;
+ dim = total - tab->bmap->n_div;
+ vec = isl_vec_alloc(bmap->ctx, 2 + total + bmap->n_div);
+ if (!vec)
+ return isl_stat_error;
+
+ for (i = 0; i < bmap->n_div; ++i) {
+ isl_seq_cpy(vec->el, bmap->div[i], 2 + dim);
+ isl_seq_clr(vec->el + 2 + dim, tab->bmap->n_div);
+ for (j = 0; j < i; ++j)
+ isl_int_add(vec->el[2 + dim + (*div_map)[j]],
+ vec->el[2 + dim + (*div_map)[j]],
+ bmap->div[i][2 + dim + j]);
+ for (j = 0; j < tab->bmap->n_div; ++j)
+ if (isl_seq_eq(tab->bmap->div[j],
+ vec->el, 2 + dim + tab->bmap->n_div))
+ break;
+ (*div_map)[i] = j;
+ if (j == tab->bmap->n_div) {
+ vec->size = 2 + dim + tab->bmap->n_div;
+ if (isl_tab_add_div(tab, vec) < 0)
+ goto error;
+ }
+ }
+
+ isl_vec_free(vec);
+
+ return isl_stat_ok;
+error:
+ isl_vec_free(vec);
+
+ return isl_stat_error;
+}
+
+/* Freeze all constraints of tableau tab.
+ */
+static int tab_freeze_constraints(struct isl_tab *tab)
+{
+ int i;
+
+ for (i = 0; i < tab->n_con; ++i)
+ if (isl_tab_freeze_constraint(tab, i) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Check for redundant constraints starting at offset.
+ * Put the indices of the redundant constraints in index
+ * and return the number of redundant constraints.
+ */
+static int n_non_redundant(isl_ctx *ctx, struct isl_tab *tab,
+ int offset, int **index)
+{
+ int i, n;
+ int n_test = tab->n_con - offset;
+
+ if (isl_tab_detect_redundant(tab) < 0)
+ return -1;
+
+ if (n_test == 0)
+ return 0;
+ if (!*index)
+ *index = isl_alloc_array(ctx, int, n_test);
+ if (!*index)
+ return -1;
+
+ for (n = 0, i = 0; i < n_test; ++i) {
+ int r;
+ r = isl_tab_is_redundant(tab, offset + i);
+ if (r < 0)
+ return -1;
+ if (r)
+ continue;
+ (*index)[n++] = i;
+ }
+
+ return n;
+}
+
+/* basic_map_collect_diff calls add on each of the pieces of
+ * the set difference between bmap and map until the add method
+ * return a negative value.
+ */
+struct isl_diff_collector {
+ isl_stat (*add)(struct isl_diff_collector *dc,
+ __isl_take isl_basic_map *bmap);
+};
+
+/* Compute the set difference between bmap and map and call
+ * dc->add on each of the piece until this function returns
+ * a negative value.
+ * Return 0 on success and -1 on error. dc->add returning
+ * a negative value is treated as an error, but the calling
+ * function can interpret the results based on the state of dc.
+ *
+ * Assumes that map has known divs.
+ *
+ * The difference is computed by a backtracking algorithm.
+ * Each level corresponds to a basic map in "map".
+ * When a node in entered for the first time, we check
+ * if the corresonding basic map intersects the current piece
+ * of "bmap". If not, we move to the next level.
+ * Otherwise, we split the current piece into as many
+ * pieces as there are non-redundant constraints of the current
+ * basic map in the intersection. Each of these pieces is
+ * handled by a child of the current node.
+ * In particular, if there are n non-redundant constraints,
+ * then for each 0 <= i < n, a piece is cut off by adding
+ * constraints 0 <= j < i and adding the opposite of constraint i.
+ * If there are no non-redundant constraints, meaning that the current
+ * piece is a subset of the current basic map, then we simply backtrack.
+ *
+ * In the leaves, we check if the remaining piece has any integer points
+ * and if so, pass it along to dc->add. As a special case, if nothing
+ * has been removed when we end up in a leaf, we simply pass along
+ * the original basic map.
+ */
+static isl_stat basic_map_collect_diff(__isl_take isl_basic_map *bmap,
+ __isl_take isl_map *map, struct isl_diff_collector *dc)
+{
+ int i;
+ int modified;
+ int level;
+ int init;
+ isl_bool empty;
+ isl_ctx *ctx;
+ struct isl_tab *tab = NULL;
+ struct isl_tab_undo **snap = NULL;
+ int *k = NULL;
+ int *n = NULL;
+ int **index = NULL;
+ int **div_map = NULL;
+
+ empty = isl_basic_map_is_empty(bmap);
+ if (empty) {
+ isl_basic_map_free(bmap);
+ isl_map_free(map);
+ return empty < 0 ? isl_stat_error : isl_stat_ok;
+ }
+
+ bmap = isl_basic_map_cow(bmap);
+ map = isl_map_cow(map);
+
+ if (!bmap || !map)
+ goto error;
+
+ ctx = map->ctx;
+ snap = isl_alloc_array(map->ctx, struct isl_tab_undo *, map->n);
+ k = isl_alloc_array(map->ctx, int, map->n);
+ n = isl_alloc_array(map->ctx, int, map->n);
+ index = isl_calloc_array(map->ctx, int *, map->n);
+ div_map = isl_calloc_array(map->ctx, int *, map->n);
+ if (!snap || !k || !n || !index || !div_map)
+ goto error;
+
+ bmap = isl_basic_map_order_divs(bmap);
+ map = isl_map_order_divs(map);
+
+ tab = isl_tab_from_basic_map(bmap, 1);
+ if (!tab)
+ goto error;
+
+ modified = 0;
+ level = 0;
+ init = 1;
+
+ while (level >= 0) {
+ if (level >= map->n) {
+ int empty;
+ struct isl_basic_map *bm;
+ if (!modified) {
+ if (dc->add(dc, isl_basic_map_copy(bmap)) < 0)
+ goto error;
+ break;
+ }
+ bm = isl_basic_map_copy(tab->bmap);
+ bm = isl_basic_map_cow(bm);
+ bm = isl_basic_map_update_from_tab(bm, tab);
+ bm = isl_basic_map_simplify(bm);
+ bm = isl_basic_map_finalize(bm);
+ empty = isl_basic_map_is_empty(bm);
+ if (empty)
+ isl_basic_map_free(bm);
+ else if (dc->add(dc, bm) < 0)
+ goto error;
+ if (empty < 0)
+ goto error;
+ level--;
+ init = 0;
+ continue;
+ }
+ if (init) {
+ int offset;
+ struct isl_tab_undo *snap2;
+ snap2 = isl_tab_snap(tab);
+ if (tab_add_divs(tab, map->p[level],
+ &div_map[level]) < 0)
+ goto error;
+ offset = tab->n_con;
+ snap[level] = isl_tab_snap(tab);
+ if (tab_freeze_constraints(tab) < 0)
+ goto error;
+ if (tab_add_constraints(tab, map->p[level],
+ div_map[level]) < 0)
+ goto error;
+ k[level] = 0;
+ n[level] = 0;
+ if (tab->empty) {
+ if (isl_tab_rollback(tab, snap2) < 0)
+ goto error;
+ level++;
+ continue;
+ }
+ modified = 1;
+ n[level] = n_non_redundant(ctx, tab, offset,
+ &index[level]);
+ if (n[level] < 0)
+ goto error;
+ if (n[level] == 0) {
+ level--;
+ init = 0;
+ continue;
+ }
+ if (isl_tab_rollback(tab, snap[level]) < 0)
+ goto error;
+ if (tab_add_constraint(tab, map->p[level],
+ div_map[level], index[level][0], 1) < 0)
+ goto error;
+ level++;
+ continue;
+ } else {
+ if (k[level] + 1 >= n[level]) {
+ level--;
+ continue;
+ }
+ if (isl_tab_rollback(tab, snap[level]) < 0)
+ goto error;
+ if (tab_add_constraint(tab, map->p[level],
+ div_map[level],
+ index[level][k[level]], 0) < 0)
+ goto error;
+ snap[level] = isl_tab_snap(tab);
+ k[level]++;
+ if (tab_add_constraint(tab, map->p[level],
+ div_map[level],
+ index[level][k[level]], 1) < 0)
+ goto error;
+ level++;
+ init = 1;
+ continue;
+ }
+ }
+
+ isl_tab_free(tab);
+ free(snap);
+ free(n);
+ free(k);
+ for (i = 0; index && i < map->n; ++i)
+ free(index[i]);
+ free(index);
+ for (i = 0; div_map && i < map->n; ++i)
+ free(div_map[i]);
+ free(div_map);
+
+ isl_basic_map_free(bmap);
+ isl_map_free(map);
+
+ return isl_stat_ok;
+error:
+ isl_tab_free(tab);
+ free(snap);
+ free(n);
+ free(k);
+ for (i = 0; index && i < map->n; ++i)
+ free(index[i]);
+ free(index);
+ for (i = 0; div_map && i < map->n; ++i)
+ free(div_map[i]);
+ free(div_map);
+ isl_basic_map_free(bmap);
+ isl_map_free(map);
+ return isl_stat_error;
+}
+
+/* A diff collector that actually collects all parts of the
+ * set difference in the field diff.
+ */
+struct isl_subtract_diff_collector {
+ struct isl_diff_collector dc;
+ struct isl_map *diff;
+};
+
+/* isl_subtract_diff_collector callback.
+ */
+static isl_stat basic_map_subtract_add(struct isl_diff_collector *dc,
+ __isl_take isl_basic_map *bmap)
+{
+ struct isl_subtract_diff_collector *sdc;
+ sdc = (struct isl_subtract_diff_collector *)dc;
+
+ sdc->diff = isl_map_union_disjoint(sdc->diff,
+ isl_map_from_basic_map(bmap));
+
+ return sdc->diff ? isl_stat_ok : isl_stat_error;
+}
+
+/* Return the set difference between bmap and map.
+ */
+static __isl_give isl_map *basic_map_subtract(__isl_take isl_basic_map *bmap,
+ __isl_take isl_map *map)
+{
+ struct isl_subtract_diff_collector sdc;
+ sdc.dc.add = &basic_map_subtract_add;
+ sdc.diff = isl_map_empty(isl_basic_map_get_space(bmap));
+ if (basic_map_collect_diff(bmap, map, &sdc.dc) < 0) {
+ isl_map_free(sdc.diff);
+ sdc.diff = NULL;
+ }
+ return sdc.diff;
+}
+
+/* Return an empty map living in the same space as "map1" and "map2".
+ */
+static __isl_give isl_map *replace_pair_by_empty( __isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ isl_space *space;
+
+ space = isl_map_get_space(map1);
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return isl_map_empty(space);
+}
+
+/* Return the set difference between map1 and map2.
+ * (U_i A_i) \ (U_j B_j) is computed as U_i (A_i \ (U_j B_j))
+ *
+ * If "map1" and "map2" are obviously equal to each other,
+ * then return an empty map in the same space.
+ *
+ * If "map1" and "map2" are disjoint, then simply return "map1".
+ */
+__isl_give isl_map *isl_map_subtract( __isl_take isl_map *map1,
+ __isl_take isl_map *map2)
+{
+ int i;
+ int equal, disjoint;
+ struct isl_map *diff;
+
+ if (isl_map_align_params_bin(&map1, &map2) < 0)
+ goto error;
+ if (isl_map_check_equal_space(map1, map2) < 0)
+ goto error;
+
+ equal = isl_map_plain_is_equal(map1, map2);
+ if (equal < 0)
+ goto error;
+ if (equal)
+ return replace_pair_by_empty(map1, map2);
+
+ disjoint = isl_map_is_disjoint(map1, map2);
+ if (disjoint < 0)
+ goto error;
+ if (disjoint) {
+ isl_map_free(map2);
+ return map1;
+ }
+
+ map1 = isl_map_compute_divs(map1);
+ map2 = isl_map_compute_divs(map2);
+ if (!map1 || !map2)
+ goto error;
+
+ map1 = isl_map_remove_empty_parts(map1);
+ map2 = isl_map_remove_empty_parts(map2);
+
+ diff = isl_map_empty(isl_map_get_space(map1));
+ for (i = 0; i < map1->n; ++i) {
+ struct isl_map *d;
+ d = basic_map_subtract(isl_basic_map_copy(map1->p[i]),
+ isl_map_copy(map2));
+ if (ISL_F_ISSET(map1, ISL_MAP_DISJOINT))
+ diff = isl_map_union_disjoint(diff, d);
+ else
+ diff = isl_map_union(diff, d);
+ }
+
+ isl_map_free(map1);
+ isl_map_free(map2);
+
+ return diff;
+error:
+ isl_map_free(map1);
+ isl_map_free(map2);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_subtract(__isl_take isl_set *set1,
+ __isl_take isl_set *set2)
+{
+ return set_from_map(isl_map_subtract(set_to_map(set1),
+ set_to_map(set2)));
+}
+
+/* Remove the elements of "dom" from the domain of "map".
+ */
+__isl_give isl_map *isl_map_subtract_domain(__isl_take isl_map *map,
+ __isl_take isl_set *dom)
+{
+ isl_bool ok;
+ isl_map *ext_dom;
+
+ isl_map_align_params_set(&map, &dom);
+ ok = isl_map_compatible_domain(map, dom);
+ if (ok < 0)
+ goto error;
+ if (!ok)
+ isl_die(isl_set_get_ctx(dom), isl_error_invalid,
+ "incompatible spaces", goto error);
+
+ ext_dom = isl_map_universe(isl_map_get_space(map));
+ ext_dom = isl_map_intersect_domain(ext_dom, dom);
+ return isl_map_subtract(map, ext_dom);
+error:
+ isl_map_free(map);
+ isl_set_free(dom);
+ return NULL;
+}
+
+/* Remove the elements of "dom" from the range of "map".
+ */
+__isl_give isl_map *isl_map_subtract_range(__isl_take isl_map *map,
+ __isl_take isl_set *dom)
+{
+ isl_bool ok;
+ isl_map *ext_dom;
+
+ isl_map_align_params_set(&map, &dom);
+ ok = isl_map_compatible_range(map, dom);
+ if (ok < 0)
+ goto error;
+ if (!ok)
+ isl_die(isl_set_get_ctx(dom), isl_error_invalid,
+ "incompatible spaces", goto error);
+
+ ext_dom = isl_map_universe(isl_map_get_space(map));
+ ext_dom = isl_map_intersect_range(ext_dom, dom);
+ return isl_map_subtract(map, ext_dom);
+error:
+ isl_map_free(map);
+ isl_set_free(dom);
+ return NULL;
+}
+
+/* A diff collector that aborts as soon as its add function is called,
+ * setting empty to isl_false.
+ */
+struct isl_is_empty_diff_collector {
+ struct isl_diff_collector dc;
+ isl_bool empty;
+};
+
+/* isl_is_empty_diff_collector callback.
+ */
+static isl_stat basic_map_is_empty_add(struct isl_diff_collector *dc,
+ __isl_take isl_basic_map *bmap)
+{
+ struct isl_is_empty_diff_collector *edc;
+ edc = (struct isl_is_empty_diff_collector *)dc;
+
+ edc->empty = isl_bool_false;
+
+ isl_basic_map_free(bmap);
+ return isl_stat_error;
+}
+
+/* Check if bmap \ map is empty by computing this set difference
+ * and breaking off as soon as the difference is known to be non-empty.
+ */
+static isl_bool basic_map_diff_is_empty(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_map *map)
+{
+ isl_bool empty;
+ isl_stat r;
+ struct isl_is_empty_diff_collector edc;
+
+ empty = isl_basic_map_plain_is_empty(bmap);
+ if (empty)
+ return empty;
+
+ edc.dc.add = &basic_map_is_empty_add;
+ edc.empty = isl_bool_true;
+ r = basic_map_collect_diff(isl_basic_map_copy(bmap),
+ isl_map_copy(map), &edc.dc);
+ if (!edc.empty)
+ return isl_bool_false;
+
+ return r < 0 ? isl_bool_error : isl_bool_true;
+}
+
+/* Check if map1 \ map2 is empty by checking if the set difference is empty
+ * for each of the basic maps in map1.
+ */
+static isl_bool map_diff_is_empty(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2)
+{
+ int i;
+ isl_bool is_empty = isl_bool_true;
+
+ if (!map1 || !map2)
+ return isl_bool_error;
+
+ for (i = 0; i < map1->n; ++i) {
+ is_empty = basic_map_diff_is_empty(map1->p[i], map2);
+ if (is_empty < 0 || !is_empty)
+ break;
+ }
+
+ return is_empty;
+}
+
+/* Return true if "bmap" contains a single element.
+ */
+isl_bool isl_basic_map_plain_is_singleton(__isl_keep isl_basic_map *bmap)
+{
+ isl_size total;
+
+ if (!bmap)
+ return isl_bool_error;
+ if (bmap->n_div)
+ return isl_bool_false;
+ if (bmap->n_ineq)
+ return isl_bool_false;
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+ return bmap->n_eq == total;
+}
+
+/* Return true if "map" contains a single element.
+ */
+isl_bool isl_map_plain_is_singleton(__isl_keep isl_map *map)
+{
+ if (!map)
+ return isl_bool_error;
+ if (map->n != 1)
+ return isl_bool_false;
+
+ return isl_basic_map_plain_is_singleton(map->p[0]);
+}
+
+/* Given a singleton basic map, extract the single element
+ * as an isl_point.
+ */
+static __isl_give isl_point *singleton_extract_point(
+ __isl_keep isl_basic_map *bmap)
+{
+ int j;
+ isl_size dim;
+ struct isl_vec *point;
+ isl_int m;
+
+ dim = isl_basic_map_dim(bmap, isl_dim_all);
+ if (dim < 0)
+ return NULL;
+
+ isl_assert(bmap->ctx, bmap->n_eq == dim, return NULL);
+ point = isl_vec_alloc(bmap->ctx, 1 + dim);
+ if (!point)
+ return NULL;
+
+ isl_int_init(m);
+
+ isl_int_set_si(point->el[0], 1);
+ for (j = 0; j < bmap->n_eq; ++j) {
+ int i = dim - 1 - j;
+ isl_assert(bmap->ctx,
+ isl_seq_first_non_zero(bmap->eq[j] + 1, i) == -1,
+ goto error);
+ isl_assert(bmap->ctx,
+ isl_int_is_one(bmap->eq[j][1 + i]) ||
+ isl_int_is_negone(bmap->eq[j][1 + i]),
+ goto error);
+ isl_assert(bmap->ctx,
+ isl_seq_first_non_zero(bmap->eq[j]+1+i+1, dim-i-1) == -1,
+ goto error);
+
+ isl_int_gcd(m, point->el[0], bmap->eq[j][1 + i]);
+ isl_int_divexact(m, bmap->eq[j][1 + i], m);
+ isl_int_abs(m, m);
+ isl_seq_scale(point->el, point->el, m, 1 + i);
+ isl_int_divexact(m, point->el[0], bmap->eq[j][1 + i]);
+ isl_int_neg(m, m);
+ isl_int_mul(point->el[1 + i], m, bmap->eq[j][0]);
+ }
+
+ isl_int_clear(m);
+ return isl_point_alloc(isl_basic_map_get_space(bmap), point);
+error:
+ isl_int_clear(m);
+ isl_vec_free(point);
+ return NULL;
+}
+
+/* Return isl_bool_true if the singleton map "map1" is a subset of "map2",
+ * i.e., if the single element of "map1" is also an element of "map2".
+ * Assumes "map2" has known divs.
+ */
+static isl_bool map_is_singleton_subset(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2)
+{
+ int i;
+ isl_bool is_subset = isl_bool_false;
+ struct isl_point *point;
+
+ if (!map1 || !map2)
+ return isl_bool_error;
+ if (map1->n != 1)
+ isl_die(isl_map_get_ctx(map1), isl_error_invalid,
+ "expecting single-disjunct input",
+ return isl_bool_error);
+
+ point = singleton_extract_point(map1->p[0]);
+ if (!point)
+ return isl_bool_error;
+
+ for (i = 0; i < map2->n; ++i) {
+ is_subset = isl_basic_map_contains_point(map2->p[i], point);
+ if (is_subset)
+ break;
+ }
+
+ isl_point_free(point);
+ return is_subset;
+}
+
+static isl_bool map_is_subset(__isl_keep isl_map *map1,
+ __isl_keep isl_map *map2)
+{
+ isl_bool is_subset = isl_bool_false;
+ isl_bool empty, single;
+ isl_bool rat1, rat2;
+
+ if (!map1 || !map2)
+ return isl_bool_error;
+
+ if (!isl_map_has_equal_space(map1, map2))
+ return isl_bool_false;
+
+ empty = isl_map_is_empty(map1);
+ if (empty < 0)
+ return isl_bool_error;
+ if (empty)
+ return isl_bool_true;
+
+ empty = isl_map_is_empty(map2);
+ if (empty < 0)
+ return isl_bool_error;
+ if (empty)
+ return isl_bool_false;
+
+ rat1 = isl_map_has_rational(map1);
+ rat2 = isl_map_has_rational(map2);
+ if (rat1 < 0 || rat2 < 0)
+ return isl_bool_error;
+ if (rat1 && !rat2)
+ return isl_bool_false;
+
+ if (isl_map_plain_is_universe(map2))
+ return isl_bool_true;
+
+ single = isl_map_plain_is_singleton(map1);
+ if (single < 0)
+ return isl_bool_error;
+ map2 = isl_map_compute_divs(isl_map_copy(map2));
+ if (single) {
+ is_subset = map_is_singleton_subset(map1, map2);
+ isl_map_free(map2);
+ return is_subset;
+ }
+ is_subset = map_diff_is_empty(map1, map2);
+ isl_map_free(map2);
+
+ return is_subset;
+}
+
+isl_bool isl_map_is_subset(__isl_keep isl_map *map1, __isl_keep isl_map *map2)
+{
+ return isl_map_align_params_map_map_and_test(map1, map2,
+ &map_is_subset);
+}
+
+isl_bool isl_set_is_subset(__isl_keep isl_set *set1, __isl_keep isl_set *set2)
+{
+ return isl_map_is_subset(set_to_map(set1), set_to_map(set2));
+}
+
+__isl_give isl_map *isl_map_make_disjoint(__isl_take isl_map *map)
+{
+ int i;
+ struct isl_subtract_diff_collector sdc;
+ sdc.dc.add = &basic_map_subtract_add;
+
+ if (!map)
+ return NULL;
+ if (ISL_F_ISSET(map, ISL_MAP_DISJOINT))
+ return map;
+ if (map->n <= 1)
+ return map;
+
+ map = isl_map_compute_divs(map);
+ map = isl_map_remove_empty_parts(map);
+
+ if (!map || map->n <= 1)
+ return map;
+
+ sdc.diff = isl_map_from_basic_map(isl_basic_map_copy(map->p[0]));
+
+ for (i = 1; i < map->n; ++i) {
+ struct isl_basic_map *bmap = isl_basic_map_copy(map->p[i]);
+ struct isl_map *copy = isl_map_copy(sdc.diff);
+ if (basic_map_collect_diff(bmap, copy, &sdc.dc) < 0) {
+ isl_map_free(sdc.diff);
+ sdc.diff = NULL;
+ break;
+ }
+ }
+
+ isl_map_free(map);
+
+ return sdc.diff;
+}
+
+__isl_give isl_set *isl_set_make_disjoint(__isl_take isl_set *set)
+{
+ return set_from_map(isl_map_make_disjoint(set_to_map(set)));
+}
+
+__isl_give isl_map *isl_map_complement(__isl_take isl_map *map)
+{
+ isl_map *universe;
+
+ if (!map)
+ return NULL;
+
+ universe = isl_map_universe(isl_map_get_space(map));
+
+ return isl_map_subtract(universe, map);
+}
+
+__isl_give isl_set *isl_set_complement(__isl_take isl_set *set)
+{
+ return isl_map_complement(set);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_to_basic_set.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_to_basic_set.c
new file mode 100644
index 00000000000..f0c8d505c6c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_map_to_basic_set.c
@@ -0,0 +1,14 @@
+#include <isl/map_to_basic_set.h>
+#include <isl/map.h>
+#include <isl/set.h>
+
+#define ISL_KEY isl_map
+#define ISL_VAL isl_basic_set
+#define ISL_HMAP_SUFFIX map_to_basic_set
+#define ISL_HMAP isl_map_to_basic_set
+#define ISL_KEY_IS_EQUAL isl_map_plain_is_equal
+#define ISL_VAL_IS_EQUAL isl_basic_set_plain_is_equal
+#define ISL_KEY_PRINT isl_printer_print_map
+#define ISL_VAL_PRINT isl_printer_print_basic_set
+
+#include <isl/hmap_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_mat.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_mat.c
new file mode 100644
index 00000000000..37468ee30ef
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_mat.c
@@ -0,0 +1,2110 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2014 Ecole Normale Superieure
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl/space.h>
+#include <isl_seq.h>
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+#include <isl_space_private.h>
+#include <isl_val_private.h>
+
+isl_ctx *isl_mat_get_ctx(__isl_keep isl_mat *mat)
+{
+ return mat ? mat->ctx : NULL;
+}
+
+/* Return a hash value that digests "mat".
+ */
+uint32_t isl_mat_get_hash(__isl_keep isl_mat *mat)
+{
+ int i;
+ uint32_t hash;
+
+ if (!mat)
+ return 0;
+
+ hash = isl_hash_init();
+ isl_hash_byte(hash, mat->n_row & 0xFF);
+ isl_hash_byte(hash, mat->n_col & 0xFF);
+ for (i = 0; i < mat->n_row; ++i) {
+ uint32_t row_hash;
+
+ row_hash = isl_seq_get_hash(mat->row[i], mat->n_col);
+ isl_hash_hash(hash, row_hash);
+ }
+
+ return hash;
+}
+
+__isl_give isl_mat *isl_mat_alloc(isl_ctx *ctx,
+ unsigned n_row, unsigned n_col)
+{
+ int i;
+ struct isl_mat *mat;
+
+ mat = isl_alloc_type(ctx, struct isl_mat);
+ if (!mat)
+ return NULL;
+
+ mat->row = NULL;
+ mat->block = isl_blk_alloc(ctx, n_row * n_col);
+ if (isl_blk_is_error(mat->block))
+ goto error;
+ mat->row = isl_calloc_array(ctx, isl_int *, n_row);
+ if (n_row && !mat->row)
+ goto error;
+
+ if (n_col != 0) {
+ for (i = 0; i < n_row; ++i)
+ mat->row[i] = mat->block.data + i * n_col;
+ }
+
+ mat->ctx = ctx;
+ isl_ctx_ref(ctx);
+ mat->ref = 1;
+ mat->n_row = n_row;
+ mat->n_col = n_col;
+ mat->max_col = n_col;
+ mat->flags = 0;
+
+ return mat;
+error:
+ isl_blk_free(ctx, mat->block);
+ free(mat);
+ return NULL;
+}
+
+__isl_give isl_mat *isl_mat_extend(__isl_take isl_mat *mat,
+ unsigned n_row, unsigned n_col)
+{
+ int i;
+ isl_int *old;
+ isl_int **row;
+
+ if (!mat)
+ return NULL;
+
+ if (mat->max_col >= n_col && mat->n_row >= n_row) {
+ if (mat->n_col < n_col)
+ mat->n_col = n_col;
+ return mat;
+ }
+
+ if (mat->max_col < n_col) {
+ struct isl_mat *new_mat;
+
+ if (n_row < mat->n_row)
+ n_row = mat->n_row;
+ new_mat = isl_mat_alloc(mat->ctx, n_row, n_col);
+ if (!new_mat)
+ goto error;
+ for (i = 0; i < mat->n_row; ++i)
+ isl_seq_cpy(new_mat->row[i], mat->row[i], mat->n_col);
+ isl_mat_free(mat);
+ return new_mat;
+ }
+
+ mat = isl_mat_cow(mat);
+ if (!mat)
+ goto error;
+
+ old = mat->block.data;
+ mat->block = isl_blk_extend(mat->ctx, mat->block, n_row * mat->max_col);
+ if (isl_blk_is_error(mat->block))
+ goto error;
+ row = isl_realloc_array(mat->ctx, mat->row, isl_int *, n_row);
+ if (n_row && !row)
+ goto error;
+ mat->row = row;
+
+ for (i = 0; i < mat->n_row; ++i)
+ mat->row[i] = mat->block.data + (mat->row[i] - old);
+ for (i = mat->n_row; i < n_row; ++i)
+ mat->row[i] = mat->block.data + i * mat->max_col;
+ mat->n_row = n_row;
+ if (mat->n_col < n_col)
+ mat->n_col = n_col;
+
+ return mat;
+error:
+ isl_mat_free(mat);
+ return NULL;
+}
+
+__isl_give isl_mat *isl_mat_sub_alloc6(isl_ctx *ctx, isl_int **row,
+ unsigned first_row, unsigned n_row, unsigned first_col, unsigned n_col)
+{
+ int i;
+ struct isl_mat *mat;
+
+ mat = isl_alloc_type(ctx, struct isl_mat);
+ if (!mat)
+ return NULL;
+ mat->row = isl_alloc_array(ctx, isl_int *, n_row);
+ if (n_row && !mat->row)
+ goto error;
+ for (i = 0; i < n_row; ++i)
+ mat->row[i] = row[first_row+i] + first_col;
+ mat->ctx = ctx;
+ isl_ctx_ref(ctx);
+ mat->ref = 1;
+ mat->n_row = n_row;
+ mat->n_col = n_col;
+ mat->block = isl_blk_empty();
+ mat->flags = ISL_MAT_BORROWED;
+ return mat;
+error:
+ free(mat);
+ return NULL;
+}
+
+__isl_give isl_mat *isl_mat_sub_alloc(__isl_keep isl_mat *mat,
+ unsigned first_row, unsigned n_row, unsigned first_col, unsigned n_col)
+{
+ if (!mat)
+ return NULL;
+ return isl_mat_sub_alloc6(mat->ctx, mat->row, first_row, n_row,
+ first_col, n_col);
+}
+
+void isl_mat_sub_copy(struct isl_ctx *ctx, isl_int **dst, isl_int **src,
+ unsigned n_row, unsigned dst_col, unsigned src_col, unsigned n_col)
+{
+ int i;
+
+ for (i = 0; i < n_row; ++i)
+ isl_seq_cpy(dst[i]+dst_col, src[i]+src_col, n_col);
+}
+
+void isl_mat_sub_neg(struct isl_ctx *ctx, isl_int **dst, isl_int **src,
+ unsigned n_row, unsigned dst_col, unsigned src_col, unsigned n_col)
+{
+ int i;
+
+ for (i = 0; i < n_row; ++i)
+ isl_seq_neg(dst[i]+dst_col, src[i]+src_col, n_col);
+}
+
+__isl_give isl_mat *isl_mat_copy(__isl_keep isl_mat *mat)
+{
+ if (!mat)
+ return NULL;
+
+ mat->ref++;
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_dup(__isl_keep isl_mat *mat)
+{
+ int i;
+ struct isl_mat *mat2;
+
+ if (!mat)
+ return NULL;
+ mat2 = isl_mat_alloc(mat->ctx, mat->n_row, mat->n_col);
+ if (!mat2)
+ return NULL;
+ for (i = 0; i < mat->n_row; ++i)
+ isl_seq_cpy(mat2->row[i], mat->row[i], mat->n_col);
+ return mat2;
+}
+
+__isl_give isl_mat *isl_mat_cow(__isl_take isl_mat *mat)
+{
+ struct isl_mat *mat2;
+ if (!mat)
+ return NULL;
+
+ if (mat->ref == 1 && !ISL_F_ISSET(mat, ISL_MAT_BORROWED))
+ return mat;
+
+ mat2 = isl_mat_dup(mat);
+ isl_mat_free(mat);
+ return mat2;
+}
+
+__isl_null isl_mat *isl_mat_free(__isl_take isl_mat *mat)
+{
+ if (!mat)
+ return NULL;
+
+ if (--mat->ref > 0)
+ return NULL;
+
+ if (!ISL_F_ISSET(mat, ISL_MAT_BORROWED))
+ isl_blk_free(mat->ctx, mat->block);
+ isl_ctx_deref(mat->ctx);
+ free(mat->row);
+ free(mat);
+
+ return NULL;
+}
+
+isl_size isl_mat_rows(__isl_keep isl_mat *mat)
+{
+ return mat ? mat->n_row : isl_size_error;
+}
+
+isl_size isl_mat_cols(__isl_keep isl_mat *mat)
+{
+ return mat ? mat->n_col : isl_size_error;
+}
+
+/* Check that "col" is a valid column position for "mat".
+ */
+static isl_stat check_col(__isl_keep isl_mat *mat, int col)
+{
+ if (!mat)
+ return isl_stat_error;
+ if (col < 0 || col >= mat->n_col)
+ isl_die(isl_mat_get_ctx(mat), isl_error_invalid,
+ "column out of range", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Check that "row" is a valid row position for "mat".
+ */
+static isl_stat check_row(__isl_keep isl_mat *mat, int row)
+{
+ if (!mat)
+ return isl_stat_error;
+ if (row < 0 || row >= mat->n_row)
+ isl_die(isl_mat_get_ctx(mat), isl_error_invalid,
+ "row out of range", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Check that there are "n" columns starting at position "first" in "mat".
+ */
+static isl_stat check_col_range(__isl_keep isl_mat *mat, unsigned first,
+ unsigned n)
+{
+ if (!mat)
+ return isl_stat_error;
+ if (first + n > mat->n_col || first + n < first)
+ isl_die(isl_mat_get_ctx(mat), isl_error_invalid,
+ "column position or range out of bounds",
+ return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Check that there are "n" rows starting at position "first" in "mat".
+ */
+static isl_stat check_row_range(__isl_keep isl_mat *mat, unsigned first,
+ unsigned n)
+{
+ if (!mat)
+ return isl_stat_error;
+ if (first + n > mat->n_row || first + n < first)
+ isl_die(isl_mat_get_ctx(mat), isl_error_invalid,
+ "row position or range out of bounds",
+ return isl_stat_error);
+ return isl_stat_ok;
+}
+
+int isl_mat_get_element(__isl_keep isl_mat *mat, int row, int col, isl_int *v)
+{
+ if (check_row(mat, row) < 0)
+ return -1;
+ if (check_col(mat, col) < 0)
+ return -1;
+ isl_int_set(*v, mat->row[row][col]);
+ return 0;
+}
+
+/* Extract the element at row "row", oolumn "col" of "mat".
+ */
+__isl_give isl_val *isl_mat_get_element_val(__isl_keep isl_mat *mat,
+ int row, int col)
+{
+ isl_ctx *ctx;
+
+ if (check_row(mat, row) < 0)
+ return NULL;
+ if (check_col(mat, col) < 0)
+ return NULL;
+ ctx = isl_mat_get_ctx(mat);
+ return isl_val_int_from_isl_int(ctx, mat->row[row][col]);
+}
+
+__isl_give isl_mat *isl_mat_set_element(__isl_take isl_mat *mat,
+ int row, int col, isl_int v)
+{
+ mat = isl_mat_cow(mat);
+ if (check_row(mat, row) < 0)
+ return isl_mat_free(mat);
+ if (check_col(mat, col) < 0)
+ return isl_mat_free(mat);
+ isl_int_set(mat->row[row][col], v);
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_set_element_si(__isl_take isl_mat *mat,
+ int row, int col, int v)
+{
+ mat = isl_mat_cow(mat);
+ if (check_row(mat, row) < 0)
+ return isl_mat_free(mat);
+ if (check_col(mat, col) < 0)
+ return isl_mat_free(mat);
+ isl_int_set_si(mat->row[row][col], v);
+ return mat;
+}
+
+/* Replace the element at row "row", column "col" of "mat" by "v".
+ */
+__isl_give isl_mat *isl_mat_set_element_val(__isl_take isl_mat *mat,
+ int row, int col, __isl_take isl_val *v)
+{
+ if (!v)
+ return isl_mat_free(mat);
+ if (!isl_val_is_int(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting integer value", goto error);
+ mat = isl_mat_set_element(mat, row, col, v->n);
+ isl_val_free(v);
+ return mat;
+error:
+ isl_val_free(v);
+ return isl_mat_free(mat);
+}
+
+__isl_give isl_mat *isl_mat_diag(isl_ctx *ctx, unsigned n_row, isl_int d)
+{
+ int i;
+ struct isl_mat *mat;
+
+ mat = isl_mat_alloc(ctx, n_row, n_row);
+ if (!mat)
+ return NULL;
+ for (i = 0; i < n_row; ++i) {
+ isl_seq_clr(mat->row[i], i);
+ isl_int_set(mat->row[i][i], d);
+ isl_seq_clr(mat->row[i]+i+1, n_row-(i+1));
+ }
+
+ return mat;
+}
+
+/* Create an "n_row" by "n_col" matrix with zero elements.
+ */
+__isl_give isl_mat *isl_mat_zero(isl_ctx *ctx, unsigned n_row, unsigned n_col)
+{
+ int i;
+ isl_mat *mat;
+
+ mat = isl_mat_alloc(ctx, n_row, n_col);
+ if (!mat)
+ return NULL;
+ for (i = 0; i < n_row; ++i)
+ isl_seq_clr(mat->row[i], n_col);
+
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_identity(isl_ctx *ctx, unsigned n_row)
+{
+ if (!ctx)
+ return NULL;
+ return isl_mat_diag(ctx, n_row, ctx->one);
+}
+
+/* Is "mat" a (possibly scaled) identity matrix?
+ */
+isl_bool isl_mat_is_scaled_identity(__isl_keep isl_mat *mat)
+{
+ int i;
+
+ if (!mat)
+ return isl_bool_error;
+ if (mat->n_row != mat->n_col)
+ return isl_bool_false;
+
+ for (i = 0; i < mat->n_row; ++i) {
+ if (isl_seq_first_non_zero(mat->row[i], i) != -1)
+ return isl_bool_false;
+ if (isl_int_ne(mat->row[0][0], mat->row[i][i]))
+ return isl_bool_false;
+ if (isl_seq_first_non_zero(mat->row[i] + i + 1,
+ mat->n_col - (i + 1)) != -1)
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+__isl_give isl_vec *isl_mat_vec_product(__isl_take isl_mat *mat,
+ __isl_take isl_vec *vec)
+{
+ int i;
+ struct isl_vec *prod;
+
+ if (!mat || !vec)
+ goto error;
+
+ isl_assert(mat->ctx, mat->n_col == vec->size, goto error);
+
+ prod = isl_vec_alloc(mat->ctx, mat->n_row);
+ if (!prod)
+ goto error;
+
+ for (i = 0; i < prod->size; ++i)
+ isl_seq_inner_product(mat->row[i], vec->el, vec->size,
+ &prod->block.data[i]);
+ isl_mat_free(mat);
+ isl_vec_free(vec);
+ return prod;
+error:
+ isl_mat_free(mat);
+ isl_vec_free(vec);
+ return NULL;
+}
+
+__isl_give isl_vec *isl_mat_vec_inverse_product(__isl_take isl_mat *mat,
+ __isl_take isl_vec *vec)
+{
+ struct isl_mat *vec_mat;
+ int i;
+
+ if (!mat || !vec)
+ goto error;
+ vec_mat = isl_mat_alloc(vec->ctx, vec->size, 1);
+ if (!vec_mat)
+ goto error;
+ for (i = 0; i < vec->size; ++i)
+ isl_int_set(vec_mat->row[i][0], vec->el[i]);
+ vec_mat = isl_mat_inverse_product(mat, vec_mat);
+ isl_vec_free(vec);
+ if (!vec_mat)
+ return NULL;
+ vec = isl_vec_alloc(vec_mat->ctx, vec_mat->n_row);
+ if (vec)
+ for (i = 0; i < vec->size; ++i)
+ isl_int_set(vec->el[i], vec_mat->row[i][0]);
+ isl_mat_free(vec_mat);
+ return vec;
+error:
+ isl_mat_free(mat);
+ isl_vec_free(vec);
+ return NULL;
+}
+
+__isl_give isl_vec *isl_vec_mat_product(__isl_take isl_vec *vec,
+ __isl_take isl_mat *mat)
+{
+ int i, j;
+ struct isl_vec *prod;
+
+ if (!mat || !vec)
+ goto error;
+
+ isl_assert(mat->ctx, mat->n_row == vec->size, goto error);
+
+ prod = isl_vec_alloc(mat->ctx, mat->n_col);
+ if (!prod)
+ goto error;
+
+ for (i = 0; i < prod->size; ++i) {
+ isl_int_set_si(prod->el[i], 0);
+ for (j = 0; j < vec->size; ++j)
+ isl_int_addmul(prod->el[i], vec->el[j], mat->row[j][i]);
+ }
+ isl_mat_free(mat);
+ isl_vec_free(vec);
+ return prod;
+error:
+ isl_mat_free(mat);
+ isl_vec_free(vec);
+ return NULL;
+}
+
+__isl_give isl_mat *isl_mat_aff_direct_sum(__isl_take isl_mat *left,
+ __isl_take isl_mat *right)
+{
+ int i;
+ struct isl_mat *sum;
+
+ if (!left || !right)
+ goto error;
+
+ isl_assert(left->ctx, left->n_row == right->n_row, goto error);
+ isl_assert(left->ctx, left->n_row >= 1, goto error);
+ isl_assert(left->ctx, left->n_col >= 1, goto error);
+ isl_assert(left->ctx, right->n_col >= 1, goto error);
+ isl_assert(left->ctx,
+ isl_seq_first_non_zero(left->row[0]+1, left->n_col-1) == -1,
+ goto error);
+ isl_assert(left->ctx,
+ isl_seq_first_non_zero(right->row[0]+1, right->n_col-1) == -1,
+ goto error);
+
+ sum = isl_mat_alloc(left->ctx, left->n_row, left->n_col + right->n_col - 1);
+ if (!sum)
+ goto error;
+ isl_int_lcm(sum->row[0][0], left->row[0][0], right->row[0][0]);
+ isl_int_divexact(left->row[0][0], sum->row[0][0], left->row[0][0]);
+ isl_int_divexact(right->row[0][0], sum->row[0][0], right->row[0][0]);
+
+ isl_seq_clr(sum->row[0]+1, sum->n_col-1);
+ for (i = 1; i < sum->n_row; ++i) {
+ isl_int_mul(sum->row[i][0], left->row[0][0], left->row[i][0]);
+ isl_int_addmul(sum->row[i][0],
+ right->row[0][0], right->row[i][0]);
+ isl_seq_scale(sum->row[i]+1, left->row[i]+1, left->row[0][0],
+ left->n_col-1);
+ isl_seq_scale(sum->row[i]+left->n_col,
+ right->row[i]+1, right->row[0][0],
+ right->n_col-1);
+ }
+
+ isl_int_divexact(left->row[0][0], sum->row[0][0], left->row[0][0]);
+ isl_int_divexact(right->row[0][0], sum->row[0][0], right->row[0][0]);
+ isl_mat_free(left);
+ isl_mat_free(right);
+ return sum;
+error:
+ isl_mat_free(left);
+ isl_mat_free(right);
+ return NULL;
+}
+
+static void exchange(__isl_keep isl_mat *M, __isl_keep isl_mat **U,
+ __isl_keep isl_mat **Q, unsigned row, unsigned i, unsigned j)
+{
+ int r;
+ for (r = row; r < M->n_row; ++r)
+ isl_int_swap(M->row[r][i], M->row[r][j]);
+ if (U) {
+ for (r = 0; r < (*U)->n_row; ++r)
+ isl_int_swap((*U)->row[r][i], (*U)->row[r][j]);
+ }
+ if (Q)
+ isl_mat_swap_rows(*Q, i, j);
+}
+
+static void subtract(__isl_keep isl_mat *M, __isl_keep isl_mat **U,
+ __isl_keep isl_mat **Q, unsigned row, unsigned i, unsigned j, isl_int m)
+{
+ int r;
+ for (r = row; r < M->n_row; ++r)
+ isl_int_submul(M->row[r][j], m, M->row[r][i]);
+ if (U) {
+ for (r = 0; r < (*U)->n_row; ++r)
+ isl_int_submul((*U)->row[r][j], m, (*U)->row[r][i]);
+ }
+ if (Q) {
+ for (r = 0; r < (*Q)->n_col; ++r)
+ isl_int_addmul((*Q)->row[i][r], m, (*Q)->row[j][r]);
+ }
+}
+
+static void oppose(__isl_keep isl_mat *M, __isl_keep isl_mat **U,
+ __isl_keep isl_mat **Q, unsigned row, unsigned col)
+{
+ int r;
+ for (r = row; r < M->n_row; ++r)
+ isl_int_neg(M->row[r][col], M->row[r][col]);
+ if (U) {
+ for (r = 0; r < (*U)->n_row; ++r)
+ isl_int_neg((*U)->row[r][col], (*U)->row[r][col]);
+ }
+ if (Q)
+ isl_seq_neg((*Q)->row[col], (*Q)->row[col], (*Q)->n_col);
+}
+
+/* Given matrix M, compute
+ *
+ * M U = H
+ * M = H Q
+ *
+ * with U and Q unimodular matrices and H a matrix in column echelon form
+ * such that on each echelon row the entries in the non-echelon column
+ * are non-negative (if neg == 0) or non-positive (if neg == 1)
+ * and strictly smaller (in absolute value) than the entries in the echelon
+ * column.
+ * If U or Q are NULL, then these matrices are not computed.
+ */
+__isl_give isl_mat *isl_mat_left_hermite(__isl_take isl_mat *M, int neg,
+ __isl_give isl_mat **U, __isl_give isl_mat **Q)
+{
+ isl_int c;
+ int row, col;
+
+ if (U)
+ *U = NULL;
+ if (Q)
+ *Q = NULL;
+ if (!M)
+ goto error;
+ if (U) {
+ *U = isl_mat_identity(M->ctx, M->n_col);
+ if (!*U)
+ goto error;
+ }
+ if (Q) {
+ *Q = isl_mat_identity(M->ctx, M->n_col);
+ if (!*Q)
+ goto error;
+ }
+
+ if (M->n_col == 0)
+ return M;
+
+ M = isl_mat_cow(M);
+ if (!M)
+ goto error;
+
+ col = 0;
+ isl_int_init(c);
+ for (row = 0; row < M->n_row; ++row) {
+ int first, i, off;
+ first = isl_seq_abs_min_non_zero(M->row[row]+col, M->n_col-col);
+ if (first == -1)
+ continue;
+ first += col;
+ if (first != col)
+ exchange(M, U, Q, row, first, col);
+ if (isl_int_is_neg(M->row[row][col]))
+ oppose(M, U, Q, row, col);
+ first = col+1;
+ while ((off = isl_seq_first_non_zero(M->row[row]+first,
+ M->n_col-first)) != -1) {
+ first += off;
+ isl_int_fdiv_q(c, M->row[row][first], M->row[row][col]);
+ subtract(M, U, Q, row, col, first, c);
+ if (!isl_int_is_zero(M->row[row][first]))
+ exchange(M, U, Q, row, first, col);
+ else
+ ++first;
+ }
+ for (i = 0; i < col; ++i) {
+ if (isl_int_is_zero(M->row[row][i]))
+ continue;
+ if (neg)
+ isl_int_cdiv_q(c, M->row[row][i], M->row[row][col]);
+ else
+ isl_int_fdiv_q(c, M->row[row][i], M->row[row][col]);
+ if (isl_int_is_zero(c))
+ continue;
+ subtract(M, U, Q, row, col, i, c);
+ }
+ ++col;
+ }
+ isl_int_clear(c);
+
+ return M;
+error:
+ if (Q) {
+ isl_mat_free(*Q);
+ *Q = NULL;
+ }
+ if (U) {
+ isl_mat_free(*U);
+ *U = NULL;
+ }
+ isl_mat_free(M);
+ return NULL;
+}
+
+/* Use row "row" of "mat" to eliminate column "col" from all other rows.
+ */
+static __isl_give isl_mat *eliminate(__isl_take isl_mat *mat, int row, int col)
+{
+ int k;
+ isl_size nr, nc;
+ isl_ctx *ctx;
+
+ nr = isl_mat_rows(mat);
+ nc = isl_mat_cols(mat);
+ if (nr < 0 || nc < 0)
+ return isl_mat_free(mat);
+
+ ctx = isl_mat_get_ctx(mat);
+
+ for (k = 0; k < nr; ++k) {
+ if (k == row)
+ continue;
+ if (isl_int_is_zero(mat->row[k][col]))
+ continue;
+ mat = isl_mat_cow(mat);
+ if (!mat)
+ return NULL;
+ isl_seq_elim(mat->row[k], mat->row[row], col, nc, NULL);
+ isl_seq_normalize(ctx, mat->row[k], nc);
+ }
+
+ return mat;
+}
+
+/* Perform Gaussian elimination on the rows of "mat", but start
+ * from the final row and the final column.
+ * Any zero rows that result from the elimination are removed.
+ *
+ * In particular, for each column from last to first,
+ * look for the last row with a non-zero coefficient in that column,
+ * move it last (but before other rows moved last in previous steps) and
+ * use it to eliminate the column from the other rows.
+ */
+__isl_give isl_mat *isl_mat_reverse_gauss(__isl_take isl_mat *mat)
+{
+ int k, row, last;
+ isl_size nr, nc;
+
+ nr = isl_mat_rows(mat);
+ nc = isl_mat_cols(mat);
+ if (nr < 0 || nc < 0)
+ return isl_mat_free(mat);
+
+ last = nc - 1;
+ for (row = nr - 1; row >= 0; --row) {
+ for (; last >= 0; --last) {
+ for (k = row; k >= 0; --k)
+ if (!isl_int_is_zero(mat->row[k][last]))
+ break;
+ if (k >= 0)
+ break;
+ }
+ if (last < 0)
+ break;
+ if (k != row)
+ mat = isl_mat_swap_rows(mat, k, row);
+ if (!mat)
+ return NULL;
+ if (isl_int_is_neg(mat->row[row][last]))
+ mat = isl_mat_row_neg(mat, row);
+ mat = eliminate(mat, row, last);
+ if (!mat)
+ return NULL;
+ }
+ mat = isl_mat_drop_rows(mat, 0, row + 1);
+
+ return mat;
+}
+
+/* Negate the lexicographically negative rows of "mat" such that
+ * all rows in the result are lexicographically non-negative.
+ */
+__isl_give isl_mat *isl_mat_lexnonneg_rows(__isl_take isl_mat *mat)
+{
+ int i;
+ isl_size nr, nc;
+
+ nr = isl_mat_rows(mat);
+ nc = isl_mat_cols(mat);
+ if (nr < 0 || nc < 0)
+ return isl_mat_free(mat);
+
+ for (i = 0; i < nr; ++i) {
+ int pos;
+
+ pos = isl_seq_first_non_zero(mat->row[i], nc);
+ if (pos < 0)
+ continue;
+ if (isl_int_is_nonneg(mat->row[i][pos]))
+ continue;
+ mat = isl_mat_row_neg(mat, i);
+ if (!mat)
+ return NULL;
+ }
+
+ return mat;
+}
+
+/* Given a matrix "H" is column echelon form, what is the first
+ * zero column? That is how many initial columns are non-zero?
+ * Start looking at column "first_col" and only consider
+ * the columns to be of size "n_row".
+ * "H" is assumed to be non-NULL.
+ *
+ * Since "H" is in column echelon form, the first non-zero entry
+ * in a column is always in a later position compared to the previous column.
+ */
+static int hermite_first_zero_col(__isl_keep isl_mat *H, int first_col,
+ int n_row)
+{
+ int row, col;
+
+ for (col = first_col, row = 0; col < H->n_col; ++col) {
+ for (; row < n_row; ++row)
+ if (!isl_int_is_zero(H->row[row][col]))
+ break;
+ if (row == n_row)
+ return col;
+ }
+
+ return H->n_col;
+}
+
+/* Return the rank of "mat", or isl_size_error in case of error.
+ */
+isl_size isl_mat_rank(__isl_keep isl_mat *mat)
+{
+ int rank;
+ isl_mat *H;
+
+ H = isl_mat_left_hermite(isl_mat_copy(mat), 0, NULL, NULL);
+ if (!H)
+ return isl_size_error;
+
+ rank = hermite_first_zero_col(H, 0, H->n_row);
+ isl_mat_free(H);
+
+ return rank;
+}
+
+__isl_give isl_mat *isl_mat_right_kernel(__isl_take isl_mat *mat)
+{
+ int rank;
+ struct isl_mat *U = NULL;
+ struct isl_mat *K;
+
+ mat = isl_mat_left_hermite(mat, 0, &U, NULL);
+ if (!mat || !U)
+ goto error;
+
+ rank = hermite_first_zero_col(mat, 0, mat->n_row);
+ K = isl_mat_alloc(U->ctx, U->n_row, U->n_col - rank);
+ if (!K)
+ goto error;
+ isl_mat_sub_copy(K->ctx, K->row, U->row, U->n_row, 0, rank, U->n_col-rank);
+ isl_mat_free(mat);
+ isl_mat_free(U);
+ return K;
+error:
+ isl_mat_free(mat);
+ isl_mat_free(U);
+ return NULL;
+}
+
+__isl_give isl_mat *isl_mat_lin_to_aff(__isl_take isl_mat *mat)
+{
+ int i;
+ struct isl_mat *mat2;
+
+ if (!mat)
+ return NULL;
+ mat2 = isl_mat_alloc(mat->ctx, 1+mat->n_row, 1+mat->n_col);
+ if (!mat2)
+ goto error;
+ isl_int_set_si(mat2->row[0][0], 1);
+ isl_seq_clr(mat2->row[0]+1, mat->n_col);
+ for (i = 0; i < mat->n_row; ++i) {
+ isl_int_set_si(mat2->row[1+i][0], 0);
+ isl_seq_cpy(mat2->row[1+i]+1, mat->row[i], mat->n_col);
+ }
+ isl_mat_free(mat);
+ return mat2;
+error:
+ isl_mat_free(mat);
+ return NULL;
+}
+
+/* Given two matrices M1 and M2, return the block matrix
+ *
+ * [ M1 0 ]
+ * [ 0 M2 ]
+ */
+__isl_give isl_mat *isl_mat_diagonal(__isl_take isl_mat *mat1,
+ __isl_take isl_mat *mat2)
+{
+ int i;
+ isl_mat *mat;
+
+ if (!mat1 || !mat2)
+ goto error;
+
+ mat = isl_mat_alloc(mat1->ctx, mat1->n_row + mat2->n_row,
+ mat1->n_col + mat2->n_col);
+ if (!mat)
+ goto error;
+ for (i = 0; i < mat1->n_row; ++i) {
+ isl_seq_cpy(mat->row[i], mat1->row[i], mat1->n_col);
+ isl_seq_clr(mat->row[i] + mat1->n_col, mat2->n_col);
+ }
+ for (i = 0; i < mat2->n_row; ++i) {
+ isl_seq_clr(mat->row[mat1->n_row + i], mat1->n_col);
+ isl_seq_cpy(mat->row[mat1->n_row + i] + mat1->n_col,
+ mat2->row[i], mat2->n_col);
+ }
+ isl_mat_free(mat1);
+ isl_mat_free(mat2);
+ return mat;
+error:
+ isl_mat_free(mat1);
+ isl_mat_free(mat2);
+ return NULL;
+}
+
+static int row_first_non_zero(isl_int **row, unsigned n_row, unsigned col)
+{
+ int i;
+
+ for (i = 0; i < n_row; ++i)
+ if (!isl_int_is_zero(row[i][col]))
+ return i;
+ return -1;
+}
+
+static int row_abs_min_non_zero(isl_int **row, unsigned n_row, unsigned col)
+{
+ int i, min = row_first_non_zero(row, n_row, col);
+ if (min < 0)
+ return -1;
+ for (i = min + 1; i < n_row; ++i) {
+ if (isl_int_is_zero(row[i][col]))
+ continue;
+ if (isl_int_abs_lt(row[i][col], row[min][col]))
+ min = i;
+ }
+ return min;
+}
+
+static isl_stat inv_exchange(__isl_keep isl_mat **left,
+ __isl_keep isl_mat **right, unsigned i, unsigned j)
+{
+ *left = isl_mat_swap_rows(*left, i, j);
+ *right = isl_mat_swap_rows(*right, i, j);
+
+ if (!*left || !*right)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+static void inv_oppose(
+ __isl_keep isl_mat *left, __isl_keep isl_mat *right, unsigned row)
+{
+ isl_seq_neg(left->row[row]+row, left->row[row]+row, left->n_col-row);
+ isl_seq_neg(right->row[row], right->row[row], right->n_col);
+}
+
+static void inv_subtract(__isl_keep isl_mat *left, __isl_keep isl_mat *right,
+ unsigned row, unsigned i, isl_int m)
+{
+ isl_int_neg(m, m);
+ isl_seq_combine(left->row[i]+row,
+ left->ctx->one, left->row[i]+row,
+ m, left->row[row]+row,
+ left->n_col-row);
+ isl_seq_combine(right->row[i], right->ctx->one, right->row[i],
+ m, right->row[row], right->n_col);
+}
+
+/* Compute inv(left)*right
+ */
+__isl_give isl_mat *isl_mat_inverse_product(__isl_take isl_mat *left,
+ __isl_take isl_mat *right)
+{
+ int row;
+ isl_int a, b;
+
+ if (!left || !right)
+ goto error;
+
+ isl_assert(left->ctx, left->n_row == left->n_col, goto error);
+ isl_assert(left->ctx, left->n_row == right->n_row, goto error);
+
+ if (left->n_row == 0) {
+ isl_mat_free(left);
+ return right;
+ }
+
+ left = isl_mat_cow(left);
+ right = isl_mat_cow(right);
+ if (!left || !right)
+ goto error;
+
+ isl_int_init(a);
+ isl_int_init(b);
+ for (row = 0; row < left->n_row; ++row) {
+ int pivot, first, i, off;
+ pivot = row_abs_min_non_zero(left->row+row, left->n_row-row, row);
+ if (pivot < 0) {
+ isl_int_clear(a);
+ isl_int_clear(b);
+ isl_assert(left->ctx, pivot >= 0, goto error);
+ }
+ pivot += row;
+ if (pivot != row)
+ if (inv_exchange(&left, &right, pivot, row) < 0)
+ goto error;
+ if (isl_int_is_neg(left->row[row][row]))
+ inv_oppose(left, right, row);
+ first = row+1;
+ while ((off = row_first_non_zero(left->row+first,
+ left->n_row-first, row)) != -1) {
+ first += off;
+ isl_int_fdiv_q(a, left->row[first][row],
+ left->row[row][row]);
+ inv_subtract(left, right, row, first, a);
+ if (!isl_int_is_zero(left->row[first][row])) {
+ if (inv_exchange(&left, &right, row, first) < 0)
+ goto error;
+ } else {
+ ++first;
+ }
+ }
+ for (i = 0; i < row; ++i) {
+ if (isl_int_is_zero(left->row[i][row]))
+ continue;
+ isl_int_gcd(a, left->row[row][row], left->row[i][row]);
+ isl_int_divexact(b, left->row[i][row], a);
+ isl_int_divexact(a, left->row[row][row], a);
+ isl_int_neg(b, b);
+ isl_seq_combine(left->row[i] + i,
+ a, left->row[i] + i,
+ b, left->row[row] + i,
+ left->n_col - i);
+ isl_seq_combine(right->row[i], a, right->row[i],
+ b, right->row[row], right->n_col);
+ }
+ }
+ isl_int_clear(b);
+
+ isl_int_set(a, left->row[0][0]);
+ for (row = 1; row < left->n_row; ++row)
+ isl_int_lcm(a, a, left->row[row][row]);
+ if (isl_int_is_zero(a)){
+ isl_int_clear(a);
+ isl_assert(left->ctx, 0, goto error);
+ }
+ for (row = 0; row < left->n_row; ++row) {
+ isl_int_divexact(left->row[row][row], a, left->row[row][row]);
+ if (isl_int_is_one(left->row[row][row]))
+ continue;
+ isl_seq_scale(right->row[row], right->row[row],
+ left->row[row][row], right->n_col);
+ }
+ isl_int_clear(a);
+
+ isl_mat_free(left);
+ return right;
+error:
+ isl_mat_free(left);
+ isl_mat_free(right);
+ return NULL;
+}
+
+void isl_mat_col_scale(__isl_keep isl_mat *mat, unsigned col, isl_int m)
+{
+ int i;
+
+ for (i = 0; i < mat->n_row; ++i)
+ isl_int_mul(mat->row[i][col], mat->row[i][col], m);
+}
+
+void isl_mat_col_combine(__isl_keep isl_mat *mat, unsigned dst,
+ isl_int m1, unsigned src1, isl_int m2, unsigned src2)
+{
+ int i;
+ isl_int tmp;
+
+ isl_int_init(tmp);
+ for (i = 0; i < mat->n_row; ++i) {
+ isl_int_mul(tmp, m1, mat->row[i][src1]);
+ isl_int_addmul(tmp, m2, mat->row[i][src2]);
+ isl_int_set(mat->row[i][dst], tmp);
+ }
+ isl_int_clear(tmp);
+}
+
+__isl_give isl_mat *isl_mat_right_inverse(__isl_take isl_mat *mat)
+{
+ struct isl_mat *inv;
+ int row;
+ isl_int a, b;
+
+ mat = isl_mat_cow(mat);
+ if (!mat)
+ return NULL;
+
+ inv = isl_mat_identity(mat->ctx, mat->n_col);
+ inv = isl_mat_cow(inv);
+ if (!inv)
+ goto error;
+
+ isl_int_init(a);
+ isl_int_init(b);
+ for (row = 0; row < mat->n_row; ++row) {
+ int pivot, first, i, off;
+ pivot = isl_seq_abs_min_non_zero(mat->row[row]+row, mat->n_col-row);
+ if (pivot < 0) {
+ isl_int_clear(a);
+ isl_int_clear(b);
+ isl_assert(mat->ctx, pivot >= 0, goto error);
+ }
+ pivot += row;
+ if (pivot != row)
+ exchange(mat, &inv, NULL, row, pivot, row);
+ if (isl_int_is_neg(mat->row[row][row]))
+ oppose(mat, &inv, NULL, row, row);
+ first = row+1;
+ while ((off = isl_seq_first_non_zero(mat->row[row]+first,
+ mat->n_col-first)) != -1) {
+ first += off;
+ isl_int_fdiv_q(a, mat->row[row][first],
+ mat->row[row][row]);
+ subtract(mat, &inv, NULL, row, row, first, a);
+ if (!isl_int_is_zero(mat->row[row][first]))
+ exchange(mat, &inv, NULL, row, row, first);
+ else
+ ++first;
+ }
+ for (i = 0; i < row; ++i) {
+ if (isl_int_is_zero(mat->row[row][i]))
+ continue;
+ isl_int_gcd(a, mat->row[row][row], mat->row[row][i]);
+ isl_int_divexact(b, mat->row[row][i], a);
+ isl_int_divexact(a, mat->row[row][row], a);
+ isl_int_neg(a, a);
+ isl_mat_col_combine(mat, i, a, i, b, row);
+ isl_mat_col_combine(inv, i, a, i, b, row);
+ }
+ }
+ isl_int_clear(b);
+
+ isl_int_set(a, mat->row[0][0]);
+ for (row = 1; row < mat->n_row; ++row)
+ isl_int_lcm(a, a, mat->row[row][row]);
+ if (isl_int_is_zero(a)){
+ isl_int_clear(a);
+ goto error;
+ }
+ for (row = 0; row < mat->n_row; ++row) {
+ isl_int_divexact(mat->row[row][row], a, mat->row[row][row]);
+ if (isl_int_is_one(mat->row[row][row]))
+ continue;
+ isl_mat_col_scale(inv, row, mat->row[row][row]);
+ }
+ isl_int_clear(a);
+
+ isl_mat_free(mat);
+
+ return inv;
+error:
+ isl_mat_free(mat);
+ isl_mat_free(inv);
+ return NULL;
+}
+
+__isl_give isl_mat *isl_mat_transpose(__isl_take isl_mat *mat)
+{
+ struct isl_mat *transpose = NULL;
+ int i, j;
+
+ if (!mat)
+ return NULL;
+
+ if (mat->n_col == mat->n_row) {
+ mat = isl_mat_cow(mat);
+ if (!mat)
+ return NULL;
+ for (i = 0; i < mat->n_row; ++i)
+ for (j = i + 1; j < mat->n_col; ++j)
+ isl_int_swap(mat->row[i][j], mat->row[j][i]);
+ return mat;
+ }
+ transpose = isl_mat_alloc(mat->ctx, mat->n_col, mat->n_row);
+ if (!transpose)
+ goto error;
+ for (i = 0; i < mat->n_row; ++i)
+ for (j = 0; j < mat->n_col; ++j)
+ isl_int_set(transpose->row[j][i], mat->row[i][j]);
+ isl_mat_free(mat);
+ return transpose;
+error:
+ isl_mat_free(mat);
+ return NULL;
+}
+
+__isl_give isl_mat *isl_mat_swap_cols(__isl_take isl_mat *mat,
+ unsigned i, unsigned j)
+{
+ int r;
+
+ mat = isl_mat_cow(mat);
+ if (check_col_range(mat, i, 1) < 0 ||
+ check_col_range(mat, j, 1) < 0)
+ return isl_mat_free(mat);
+
+ for (r = 0; r < mat->n_row; ++r)
+ isl_int_swap(mat->row[r][i], mat->row[r][j]);
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_swap_rows(__isl_take isl_mat *mat,
+ unsigned i, unsigned j)
+{
+ isl_int *t;
+
+ if (!mat)
+ return NULL;
+ mat = isl_mat_cow(mat);
+ if (check_row_range(mat, i, 1) < 0 ||
+ check_row_range(mat, j, 1) < 0)
+ return isl_mat_free(mat);
+
+ t = mat->row[i];
+ mat->row[i] = mat->row[j];
+ mat->row[j] = t;
+ return mat;
+}
+
+/* Calculate the product of two matrices.
+ *
+ * This function is optimized for operand matrices that contain many zeros and
+ * skips multiplications where we know one of the operands is zero.
+ */
+__isl_give isl_mat *isl_mat_product(__isl_take isl_mat *left,
+ __isl_take isl_mat *right)
+{
+ int i, j, k;
+ struct isl_mat *prod;
+
+ if (!left || !right)
+ goto error;
+ isl_assert(left->ctx, left->n_col == right->n_row, goto error);
+ prod = isl_mat_alloc(left->ctx, left->n_row, right->n_col);
+ if (!prod)
+ goto error;
+ if (left->n_col == 0) {
+ for (i = 0; i < prod->n_row; ++i)
+ isl_seq_clr(prod->row[i], prod->n_col);
+ isl_mat_free(left);
+ isl_mat_free(right);
+ return prod;
+ }
+ for (i = 0; i < prod->n_row; ++i) {
+ for (j = 0; j < prod->n_col; ++j)
+ isl_int_mul(prod->row[i][j],
+ left->row[i][0], right->row[0][j]);
+ for (k = 1; k < left->n_col; ++k) {
+ if (isl_int_is_zero(left->row[i][k]))
+ continue;
+ for (j = 0; j < prod->n_col; ++j)
+ isl_int_addmul(prod->row[i][j],
+ left->row[i][k], right->row[k][j]);
+ }
+ }
+ isl_mat_free(left);
+ isl_mat_free(right);
+ return prod;
+error:
+ isl_mat_free(left);
+ isl_mat_free(right);
+ return NULL;
+}
+
+/* Replace the variables x in the rows q by x' given by x = M x',
+ * with M the matrix mat.
+ *
+ * If the number of new variables is greater than the original
+ * number of variables, then the rows q have already been
+ * preextended. If the new number is smaller, then the coefficients
+ * of the divs, which are not changed, need to be shifted down.
+ * The row q may be the equalities, the inequalities or the
+ * div expressions. In the latter case, has_div is true and
+ * we need to take into account the extra denominator column.
+ */
+static int preimage(struct isl_ctx *ctx, isl_int **q, unsigned n,
+ unsigned n_div, int has_div, struct isl_mat *mat)
+{
+ int i;
+ struct isl_mat *t;
+ int e;
+
+ if (mat->n_col >= mat->n_row)
+ e = 0;
+ else
+ e = mat->n_row - mat->n_col;
+ if (has_div)
+ for (i = 0; i < n; ++i)
+ isl_int_mul(q[i][0], q[i][0], mat->row[0][0]);
+ t = isl_mat_sub_alloc6(mat->ctx, q, 0, n, has_div, mat->n_row);
+ t = isl_mat_product(t, mat);
+ if (!t)
+ return -1;
+ for (i = 0; i < n; ++i) {
+ isl_seq_swp_or_cpy(q[i] + has_div, t->row[i], t->n_col);
+ isl_seq_cpy(q[i] + has_div + t->n_col,
+ q[i] + has_div + t->n_col + e, n_div);
+ isl_seq_clr(q[i] + has_div + t->n_col + n_div, e);
+ }
+ isl_mat_free(t);
+ return 0;
+}
+
+/* Replace the variables x in bset by x' given by x = M x', with
+ * M the matrix mat.
+ *
+ * If there are fewer variables x' then there are x, then we perform
+ * the transformation in place, which means that, in principle,
+ * this frees up some extra variables as the number
+ * of columns remains constant, but we would have to extend
+ * the div array too as the number of rows in this array is assumed
+ * to be equal to extra.
+ */
+__isl_give isl_basic_set *isl_basic_set_preimage(
+ __isl_take isl_basic_set *bset, __isl_take isl_mat *mat)
+{
+ struct isl_ctx *ctx;
+
+ if (!bset || !mat)
+ goto error;
+
+ ctx = bset->ctx;
+ bset = isl_basic_set_cow(bset);
+ if (isl_basic_set_check_no_params(bset) < 0)
+ goto error;
+
+ isl_assert(ctx, 1+bset->dim->n_out == mat->n_row, goto error);
+ isl_assert(ctx, mat->n_col > 0, goto error);
+
+ if (mat->n_col > mat->n_row) {
+ bset = isl_basic_set_add_dims(bset, isl_dim_set,
+ mat->n_col - mat->n_row);
+ if (!bset)
+ goto error;
+ } else if (mat->n_col < mat->n_row) {
+ bset->dim = isl_space_cow(bset->dim);
+ if (!bset->dim)
+ goto error;
+ bset->dim->n_out -= mat->n_row - mat->n_col;
+ }
+
+ if (preimage(ctx, bset->eq, bset->n_eq, bset->n_div, 0,
+ isl_mat_copy(mat)) < 0)
+ goto error;
+
+ if (preimage(ctx, bset->ineq, bset->n_ineq, bset->n_div, 0,
+ isl_mat_copy(mat)) < 0)
+ goto error;
+
+ if (preimage(ctx, bset->div, bset->n_div, bset->n_div, 1, mat) < 0)
+ goto error2;
+
+ ISL_F_CLR(bset, ISL_BASIC_SET_NO_IMPLICIT);
+ ISL_F_CLR(bset, ISL_BASIC_SET_NO_REDUNDANT);
+ ISL_F_CLR(bset, ISL_BASIC_SET_SORTED);
+ ISL_F_CLR(bset, ISL_BASIC_SET_NORMALIZED_DIVS);
+ ISL_F_CLR(bset, ISL_BASIC_SET_ALL_EQUALITIES);
+
+ bset = isl_basic_set_simplify(bset);
+ bset = isl_basic_set_finalize(bset);
+
+ return bset;
+error:
+ isl_mat_free(mat);
+error2:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_preimage(
+ __isl_take isl_set *set, __isl_take isl_mat *mat)
+{
+ int i;
+
+ set = isl_set_cow(set);
+ if (!set)
+ goto error;
+
+ for (i = 0; i < set->n; ++i) {
+ set->p[i] = isl_basic_set_preimage(set->p[i],
+ isl_mat_copy(mat));
+ if (!set->p[i])
+ goto error;
+ }
+ if (mat->n_col != mat->n_row) {
+ set->dim = isl_space_cow(set->dim);
+ if (!set->dim)
+ goto error;
+ set->dim->n_out += mat->n_col;
+ set->dim->n_out -= mat->n_row;
+ }
+ isl_mat_free(mat);
+ ISL_F_CLR(set, ISL_SET_NORMALIZED);
+ return set;
+error:
+ isl_set_free(set);
+ isl_mat_free(mat);
+ return NULL;
+}
+
+/* Replace the variables x starting at "first_col" in the rows "rows"
+ * of some coefficient matrix by x' with x = M x' with M the matrix mat.
+ * That is, replace the corresponding coefficients c by c M.
+ */
+isl_stat isl_mat_sub_transform(isl_int **row, unsigned n_row,
+ unsigned first_col, __isl_take isl_mat *mat)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_mat *t;
+
+ if (!mat)
+ return isl_stat_error;
+ ctx = isl_mat_get_ctx(mat);
+ t = isl_mat_sub_alloc6(ctx, row, 0, n_row, first_col, mat->n_row);
+ t = isl_mat_product(t, mat);
+ if (!t)
+ return isl_stat_error;
+ for (i = 0; i < n_row; ++i)
+ isl_seq_swp_or_cpy(row[i] + first_col, t->row[i], t->n_col);
+ isl_mat_free(t);
+ return isl_stat_ok;
+}
+
+void isl_mat_print_internal(__isl_keep isl_mat *mat, FILE *out, int indent)
+{
+ int i, j;
+
+ if (!mat) {
+ fprintf(out, "%*snull mat\n", indent, "");
+ return;
+ }
+
+ if (mat->n_row == 0)
+ fprintf(out, "%*s[]\n", indent, "");
+
+ for (i = 0; i < mat->n_row; ++i) {
+ if (!i)
+ fprintf(out, "%*s[[", indent, "");
+ else
+ fprintf(out, "%*s[", indent+1, "");
+ for (j = 0; j < mat->n_col; ++j) {
+ if (j)
+ fprintf(out, ",");
+ isl_int_print(out, mat->row[i][j], 0);
+ }
+ if (i == mat->n_row-1)
+ fprintf(out, "]]\n");
+ else
+ fprintf(out, "]\n");
+ }
+}
+
+void isl_mat_dump(__isl_keep isl_mat *mat)
+{
+ isl_mat_print_internal(mat, stderr, 0);
+}
+
+__isl_give isl_mat *isl_mat_drop_cols(__isl_take isl_mat *mat,
+ unsigned col, unsigned n)
+{
+ int r;
+
+ if (n == 0)
+ return mat;
+
+ mat = isl_mat_cow(mat);
+ if (check_col_range(mat, col, n) < 0)
+ return isl_mat_free(mat);
+
+ if (col != mat->n_col-n) {
+ for (r = 0; r < mat->n_row; ++r)
+ isl_seq_cpy(mat->row[r]+col, mat->row[r]+col+n,
+ mat->n_col - col - n);
+ }
+ mat->n_col -= n;
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_drop_rows(__isl_take isl_mat *mat,
+ unsigned row, unsigned n)
+{
+ int r;
+
+ mat = isl_mat_cow(mat);
+ if (check_row_range(mat, row, n) < 0)
+ return isl_mat_free(mat);
+
+ for (r = row; r+n < mat->n_row; ++r)
+ mat->row[r] = mat->row[r+n];
+
+ mat->n_row -= n;
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_insert_cols(__isl_take isl_mat *mat,
+ unsigned col, unsigned n)
+{
+ isl_mat *ext;
+
+ if (check_col_range(mat, col, 0) < 0)
+ return isl_mat_free(mat);
+ if (n == 0)
+ return mat;
+
+ ext = isl_mat_alloc(mat->ctx, mat->n_row, mat->n_col + n);
+ if (!ext)
+ goto error;
+
+ isl_mat_sub_copy(mat->ctx, ext->row, mat->row, mat->n_row, 0, 0, col);
+ isl_mat_sub_copy(mat->ctx, ext->row, mat->row, mat->n_row,
+ col + n, col, mat->n_col - col);
+
+ isl_mat_free(mat);
+ return ext;
+error:
+ isl_mat_free(mat);
+ return NULL;
+}
+
+__isl_give isl_mat *isl_mat_insert_zero_cols(__isl_take isl_mat *mat,
+ unsigned first, unsigned n)
+{
+ int i;
+
+ if (!mat)
+ return NULL;
+ mat = isl_mat_insert_cols(mat, first, n);
+ if (!mat)
+ return NULL;
+
+ for (i = 0; i < mat->n_row; ++i)
+ isl_seq_clr(mat->row[i] + first, n);
+
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_add_zero_cols(__isl_take isl_mat *mat, unsigned n)
+{
+ if (!mat)
+ return NULL;
+
+ return isl_mat_insert_zero_cols(mat, mat->n_col, n);
+}
+
+__isl_give isl_mat *isl_mat_insert_rows(__isl_take isl_mat *mat,
+ unsigned row, unsigned n)
+{
+ isl_mat *ext;
+
+ if (check_row_range(mat, row, 0) < 0)
+ return isl_mat_free(mat);
+ if (n == 0)
+ return mat;
+
+ ext = isl_mat_alloc(mat->ctx, mat->n_row + n, mat->n_col);
+ if (!ext)
+ goto error;
+
+ isl_mat_sub_copy(mat->ctx, ext->row, mat->row, row, 0, 0, mat->n_col);
+ isl_mat_sub_copy(mat->ctx, ext->row + row + n, mat->row + row,
+ mat->n_row - row, 0, 0, mat->n_col);
+
+ isl_mat_free(mat);
+ return ext;
+error:
+ isl_mat_free(mat);
+ return NULL;
+}
+
+__isl_give isl_mat *isl_mat_add_rows(__isl_take isl_mat *mat, unsigned n)
+{
+ if (!mat)
+ return NULL;
+
+ return isl_mat_insert_rows(mat, mat->n_row, n);
+}
+
+__isl_give isl_mat *isl_mat_insert_zero_rows(__isl_take isl_mat *mat,
+ unsigned row, unsigned n)
+{
+ int i;
+
+ mat = isl_mat_insert_rows(mat, row, n);
+ if (!mat)
+ return NULL;
+
+ for (i = 0; i < n; ++i)
+ isl_seq_clr(mat->row[row + i], mat->n_col);
+
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_add_zero_rows(__isl_take isl_mat *mat, unsigned n)
+{
+ if (!mat)
+ return NULL;
+
+ return isl_mat_insert_zero_rows(mat, mat->n_row, n);
+}
+
+void isl_mat_col_submul(__isl_keep isl_mat *mat,
+ int dst_col, isl_int f, int src_col)
+{
+ int i;
+
+ for (i = 0; i < mat->n_row; ++i)
+ isl_int_submul(mat->row[i][dst_col], f, mat->row[i][src_col]);
+}
+
+void isl_mat_col_add(__isl_keep isl_mat *mat, int dst_col, int src_col)
+{
+ int i;
+
+ if (!mat)
+ return;
+
+ for (i = 0; i < mat->n_row; ++i)
+ isl_int_add(mat->row[i][dst_col],
+ mat->row[i][dst_col], mat->row[i][src_col]);
+}
+
+void isl_mat_col_mul(__isl_keep isl_mat *mat, int dst_col, isl_int f,
+ int src_col)
+{
+ int i;
+
+ for (i = 0; i < mat->n_row; ++i)
+ isl_int_mul(mat->row[i][dst_col], f, mat->row[i][src_col]);
+}
+
+/* Add "f" times column "src_col" to column "dst_col" of "mat" and
+ * return the result.
+ */
+__isl_give isl_mat *isl_mat_col_addmul(__isl_take isl_mat *mat, int dst_col,
+ isl_int f, int src_col)
+{
+ int i;
+
+ if (check_col(mat, dst_col) < 0 || check_col(mat, src_col) < 0)
+ return isl_mat_free(mat);
+
+ for (i = 0; i < mat->n_row; ++i) {
+ if (isl_int_is_zero(mat->row[i][src_col]))
+ continue;
+ mat = isl_mat_cow(mat);
+ if (!mat)
+ return NULL;
+ isl_int_addmul(mat->row[i][dst_col], f, mat->row[i][src_col]);
+ }
+
+ return mat;
+}
+
+/* Negate column "col" of "mat" and return the result.
+ */
+__isl_give isl_mat *isl_mat_col_neg(__isl_take isl_mat *mat, int col)
+{
+ int i;
+
+ if (check_col(mat, col) < 0)
+ return isl_mat_free(mat);
+
+ for (i = 0; i < mat->n_row; ++i) {
+ if (isl_int_is_zero(mat->row[i][col]))
+ continue;
+ mat = isl_mat_cow(mat);
+ if (!mat)
+ return NULL;
+ isl_int_neg(mat->row[i][col], mat->row[i][col]);
+ }
+
+ return mat;
+}
+
+/* Negate row "row" of "mat" and return the result.
+ */
+__isl_give isl_mat *isl_mat_row_neg(__isl_take isl_mat *mat, int row)
+{
+ if (check_row(mat, row) < 0)
+ return isl_mat_free(mat);
+ if (isl_seq_first_non_zero(mat->row[row], mat->n_col) == -1)
+ return mat;
+ mat = isl_mat_cow(mat);
+ if (!mat)
+ return NULL;
+ isl_seq_neg(mat->row[row], mat->row[row], mat->n_col);
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_unimodular_complete(__isl_take isl_mat *M, int row)
+{
+ int r;
+ struct isl_mat *H = NULL, *Q = NULL;
+
+ if (!M)
+ return NULL;
+
+ isl_assert(M->ctx, M->n_row == M->n_col, goto error);
+ M->n_row = row;
+ H = isl_mat_left_hermite(isl_mat_copy(M), 0, NULL, &Q);
+ M->n_row = M->n_col;
+ if (!H)
+ goto error;
+ for (r = 0; r < row; ++r)
+ isl_assert(M->ctx, isl_int_is_one(H->row[r][r]), goto error);
+ for (r = row; r < M->n_row; ++r)
+ isl_seq_cpy(M->row[r], Q->row[r], M->n_col);
+ isl_mat_free(H);
+ isl_mat_free(Q);
+ return M;
+error:
+ isl_mat_free(H);
+ isl_mat_free(Q);
+ isl_mat_free(M);
+ return NULL;
+}
+
+__isl_give isl_mat *isl_mat_concat(__isl_take isl_mat *top,
+ __isl_take isl_mat *bot)
+{
+ struct isl_mat *mat;
+
+ if (!top || !bot)
+ goto error;
+
+ isl_assert(top->ctx, top->n_col == bot->n_col, goto error);
+ if (top->n_row == 0) {
+ isl_mat_free(top);
+ return bot;
+ }
+ if (bot->n_row == 0) {
+ isl_mat_free(bot);
+ return top;
+ }
+
+ mat = isl_mat_alloc(top->ctx, top->n_row + bot->n_row, top->n_col);
+ if (!mat)
+ goto error;
+ isl_mat_sub_copy(mat->ctx, mat->row, top->row, top->n_row,
+ 0, 0, mat->n_col);
+ isl_mat_sub_copy(mat->ctx, mat->row + top->n_row, bot->row, bot->n_row,
+ 0, 0, mat->n_col);
+ isl_mat_free(top);
+ isl_mat_free(bot);
+ return mat;
+error:
+ isl_mat_free(top);
+ isl_mat_free(bot);
+ return NULL;
+}
+
+isl_bool isl_mat_is_equal(__isl_keep isl_mat *mat1, __isl_keep isl_mat *mat2)
+{
+ int i;
+
+ if (!mat1 || !mat2)
+ return isl_bool_error;
+
+ if (mat1->n_row != mat2->n_row)
+ return isl_bool_false;
+
+ if (mat1->n_col != mat2->n_col)
+ return isl_bool_false;
+
+ for (i = 0; i < mat1->n_row; ++i)
+ if (!isl_seq_eq(mat1->row[i], mat2->row[i], mat1->n_col))
+ return isl_bool_false;
+
+ return isl_bool_true;
+}
+
+__isl_give isl_mat *isl_mat_from_row_vec(__isl_take isl_vec *vec)
+{
+ struct isl_mat *mat;
+
+ if (!vec)
+ return NULL;
+ mat = isl_mat_alloc(vec->ctx, 1, vec->size);
+ if (!mat)
+ goto error;
+
+ isl_seq_cpy(mat->row[0], vec->el, vec->size);
+
+ isl_vec_free(vec);
+ return mat;
+error:
+ isl_vec_free(vec);
+ return NULL;
+}
+
+/* Return a copy of row "row" of "mat" as an isl_vec.
+ */
+__isl_give isl_vec *isl_mat_get_row(__isl_keep isl_mat *mat, unsigned row)
+{
+ isl_vec *v;
+
+ if (!mat)
+ return NULL;
+ if (row >= mat->n_row)
+ isl_die(mat->ctx, isl_error_invalid, "row out of range",
+ return NULL);
+
+ v = isl_vec_alloc(isl_mat_get_ctx(mat), mat->n_col);
+ if (!v)
+ return NULL;
+ isl_seq_cpy(v->el, mat->row[row], mat->n_col);
+
+ return v;
+}
+
+__isl_give isl_mat *isl_mat_vec_concat(__isl_take isl_mat *top,
+ __isl_take isl_vec *bot)
+{
+ return isl_mat_concat(top, isl_mat_from_row_vec(bot));
+}
+
+__isl_give isl_mat *isl_mat_move_cols(__isl_take isl_mat *mat,
+ unsigned dst_col, unsigned src_col, unsigned n)
+{
+ isl_mat *res;
+
+ if (!mat)
+ return NULL;
+ if (n == 0 || dst_col == src_col)
+ return mat;
+
+ res = isl_mat_alloc(mat->ctx, mat->n_row, mat->n_col);
+ if (!res)
+ goto error;
+
+ if (dst_col < src_col) {
+ isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row,
+ 0, 0, dst_col);
+ isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row,
+ dst_col, src_col, n);
+ isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row,
+ dst_col + n, dst_col, src_col - dst_col);
+ isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row,
+ src_col + n, src_col + n,
+ res->n_col - src_col - n);
+ } else {
+ isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row,
+ 0, 0, src_col);
+ isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row,
+ src_col, src_col + n, dst_col - src_col);
+ isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row,
+ dst_col, src_col, n);
+ isl_mat_sub_copy(res->ctx, res->row, mat->row, mat->n_row,
+ dst_col + n, dst_col + n,
+ res->n_col - dst_col - n);
+ }
+ isl_mat_free(mat);
+
+ return res;
+error:
+ isl_mat_free(mat);
+ return NULL;
+}
+
+/* Return the gcd of the elements in row "row" of "mat" in *gcd.
+ * Return isl_stat_ok on success and isl_stat_error on failure.
+ */
+isl_stat isl_mat_row_gcd(__isl_keep isl_mat *mat, int row, isl_int *gcd)
+{
+ if (check_row(mat, row) < 0)
+ return isl_stat_error;
+
+ isl_seq_gcd(mat->row[row], mat->n_col, gcd);
+
+ return isl_stat_ok;
+}
+
+void isl_mat_gcd(__isl_keep isl_mat *mat, isl_int *gcd)
+{
+ int i;
+ isl_int g;
+
+ isl_int_set_si(*gcd, 0);
+ if (!mat)
+ return;
+
+ isl_int_init(g);
+ for (i = 0; i < mat->n_row; ++i) {
+ isl_seq_gcd(mat->row[i], mat->n_col, &g);
+ isl_int_gcd(*gcd, *gcd, g);
+ }
+ isl_int_clear(g);
+}
+
+/* Return the result of scaling "mat" by a factor of "m".
+ */
+__isl_give isl_mat *isl_mat_scale(__isl_take isl_mat *mat, isl_int m)
+{
+ int i;
+
+ if (isl_int_is_one(m))
+ return mat;
+
+ mat = isl_mat_cow(mat);
+ if (!mat)
+ return NULL;
+
+ for (i = 0; i < mat->n_row; ++i)
+ isl_seq_scale(mat->row[i], mat->row[i], m, mat->n_col);
+
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_scale_down(__isl_take isl_mat *mat, isl_int m)
+{
+ int i;
+
+ if (isl_int_is_one(m))
+ return mat;
+
+ mat = isl_mat_cow(mat);
+ if (!mat)
+ return NULL;
+
+ for (i = 0; i < mat->n_row; ++i)
+ isl_seq_scale_down(mat->row[i], mat->row[i], m, mat->n_col);
+
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_scale_down_row(__isl_take isl_mat *mat, int row,
+ isl_int m)
+{
+ if (isl_int_is_one(m))
+ return mat;
+
+ mat = isl_mat_cow(mat);
+ if (!mat)
+ return NULL;
+
+ isl_seq_scale_down(mat->row[row], mat->row[row], m, mat->n_col);
+
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_normalize(__isl_take isl_mat *mat)
+{
+ isl_int gcd;
+
+ if (!mat)
+ return NULL;
+
+ isl_int_init(gcd);
+ isl_mat_gcd(mat, &gcd);
+ mat = isl_mat_scale_down(mat, gcd);
+ isl_int_clear(gcd);
+
+ return mat;
+}
+
+__isl_give isl_mat *isl_mat_normalize_row(__isl_take isl_mat *mat, int row)
+{
+ mat = isl_mat_cow(mat);
+ if (!mat)
+ return NULL;
+
+ isl_seq_normalize(mat->ctx, mat->row[row], mat->n_col);
+
+ return mat;
+}
+
+/* Number of initial non-zero columns.
+ */
+int isl_mat_initial_non_zero_cols(__isl_keep isl_mat *mat)
+{
+ int i;
+
+ if (!mat)
+ return -1;
+
+ for (i = 0; i < mat->n_col; ++i)
+ if (row_first_non_zero(mat->row, mat->n_row, i) < 0)
+ break;
+
+ return i;
+}
+
+/* Return a basis for the space spanned by the rows of "mat".
+ * Any basis will do, so simply perform Gaussian elimination and
+ * remove the empty rows.
+ */
+__isl_give isl_mat *isl_mat_row_basis(__isl_take isl_mat *mat)
+{
+ return isl_mat_reverse_gauss(mat);
+}
+
+/* Return rows that extend a basis of "mat1" to one
+ * that covers both "mat1" and "mat2".
+ * The Hermite normal form of the concatenation of the two matrices is
+ *
+ * [ Q1 ]
+ * [ M1 ] = [ H1 0 0 ] [ Q2 ]
+ * [ M2 ] = [ H2 H3 0 ] [ Q3 ]
+ *
+ * The number of columns in H1 and H3 determine the number of rows
+ * in Q1 and Q2. Q1 is a basis for M1, while Q2 extends this basis
+ * to also cover M2.
+ */
+__isl_give isl_mat *isl_mat_row_basis_extension(
+ __isl_take isl_mat *mat1, __isl_take isl_mat *mat2)
+{
+ isl_size n_row;
+ int r1, r;
+ isl_size n1;
+ isl_mat *H, *Q;
+
+ n1 = isl_mat_rows(mat1);
+ H = isl_mat_concat(mat1, mat2);
+ H = isl_mat_left_hermite(H, 0, NULL, &Q);
+ if (n1 < 0 || !H || !Q)
+ goto error;
+
+ r1 = hermite_first_zero_col(H, 0, n1);
+ r = hermite_first_zero_col(H, r1, H->n_row);
+ n_row = isl_mat_rows(Q);
+ if (n_row < 0)
+ goto error;
+ Q = isl_mat_drop_rows(Q, r, n_row - r);
+ Q = isl_mat_drop_rows(Q, 0, r1);
+
+ isl_mat_free(H);
+ return Q;
+error:
+ isl_mat_free(H);
+ isl_mat_free(Q);
+ return NULL;
+}
+
+/* Are the rows of "mat1" linearly independent of those of "mat2"?
+ * That is, is there no linear dependence among the combined rows
+ * that is not already present in either "mat1" or "mat2"?
+ * In other words, is the rank of "mat1" and "mat2" combined equal
+ * to the sum of the ranks of "mat1" and "mat2"?
+ */
+isl_bool isl_mat_has_linearly_independent_rows(__isl_keep isl_mat *mat1,
+ __isl_keep isl_mat *mat2)
+{
+ isl_size r1, r2, r;
+ isl_mat *mat;
+
+ r1 = isl_mat_rank(mat1);
+ if (r1 < 0)
+ return isl_bool_error;
+ if (r1 == 0)
+ return isl_bool_true;
+ r2 = isl_mat_rank(mat2);
+ if (r2 < 0)
+ return isl_bool_error;
+ if (r2 == 0)
+ return isl_bool_true;
+
+ mat = isl_mat_concat(isl_mat_copy(mat1), isl_mat_copy(mat2));
+ r = isl_mat_rank(mat);
+ isl_mat_free(mat);
+ if (r < 0)
+ return isl_bool_error;
+ return isl_bool_ok(r == r1 + r2);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_mat_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_mat_private.h
new file mode 100644
index 00000000000..af760a2ee9d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_mat_private.h
@@ -0,0 +1,70 @@
+#ifndef ISL_MAT_PRIVATE_H
+#define ISL_MAT_PRIVATE_H
+
+#include <isl/mat.h>
+#include <isl_blk.h>
+
+struct isl_mat {
+ int ref;
+
+ struct isl_ctx *ctx;
+
+#define ISL_MAT_BORROWED (1 << 0)
+ unsigned flags;
+
+ unsigned n_row;
+ unsigned n_col;
+
+ isl_int **row;
+
+ /* actual size of the rows in memory; n_col <= max_col */
+ unsigned max_col;
+
+ struct isl_blk block;
+};
+
+uint32_t isl_mat_get_hash(__isl_keep isl_mat *mat);
+
+__isl_give isl_mat *isl_mat_zero(isl_ctx *ctx, unsigned n_row, unsigned n_col);
+__isl_give isl_mat *isl_mat_dup(__isl_keep isl_mat *mat);
+__isl_give isl_mat *isl_mat_cow(__isl_take isl_mat *mat);
+__isl_give isl_mat *isl_mat_sub_alloc(__isl_keep isl_mat *mat,
+ unsigned first_row, unsigned n_row, unsigned first_col, unsigned n_col);
+__isl_give isl_mat *isl_mat_sub_alloc6(isl_ctx *ctx, isl_int **row,
+ unsigned first_row, unsigned n_row, unsigned first_col, unsigned n_col);
+void isl_mat_sub_copy(struct isl_ctx *ctx, isl_int **dst, isl_int **src,
+ unsigned n_row, unsigned dst_col, unsigned src_col, unsigned n_col);
+void isl_mat_sub_neg(struct isl_ctx *ctx, isl_int **dst, isl_int **src,
+ unsigned n_row, unsigned dst_col, unsigned src_col, unsigned n_col);
+isl_stat isl_mat_sub_transform(isl_int **row, unsigned n_row,
+ unsigned first_col, __isl_take isl_mat *mat);
+__isl_give isl_mat *isl_mat_diag(isl_ctx *ctx, unsigned n_row, isl_int d);
+
+__isl_give isl_mat *isl_mat_reverse_gauss(__isl_take isl_mat *mat);
+
+__isl_give isl_mat *isl_mat_scale(__isl_take isl_mat *mat, isl_int m);
+__isl_give isl_mat *isl_mat_scale_down_row(__isl_take isl_mat *mat, int row,
+ isl_int m);
+
+__isl_give isl_vec *isl_mat_get_row(__isl_keep isl_mat *mat, unsigned row);
+
+__isl_give isl_mat *isl_mat_lexnonneg_rows(__isl_take isl_mat *mat);
+
+isl_bool isl_mat_is_scaled_identity(__isl_keep isl_mat *mat);
+
+isl_stat isl_mat_row_gcd(__isl_keep isl_mat *mat, int row, isl_int *gcd);
+
+void isl_mat_col_mul(__isl_keep isl_mat *mat, int dst_col, isl_int f,
+ int src_col);
+void isl_mat_col_submul(__isl_keep isl_mat *mat,
+ int dst_col, isl_int f, int src_col);
+__isl_give isl_mat *isl_mat_col_addmul(__isl_take isl_mat *mat, int dst_col,
+ isl_int f, int src_col);
+__isl_give isl_mat *isl_mat_col_neg(__isl_take isl_mat *mat, int col);
+__isl_give isl_mat *isl_mat_row_neg(__isl_take isl_mat *mat, int row);
+
+int isl_mat_get_element(__isl_keep isl_mat *mat, int row, int col, isl_int *v);
+__isl_give isl_mat *isl_mat_set_element(__isl_take isl_mat *mat,
+ int row, int col, isl_int v);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_maybe_ast_graft_list.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_maybe_ast_graft_list.h
new file mode 100644
index 00000000000..14466918aac
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_maybe_ast_graft_list.h
@@ -0,0 +1,10 @@
+#ifndef ISL_MAYBE_AST_GRAFT_LIST_H
+#define ISL_MAYBE_AST_GRAFT_LIST_H
+
+#include "isl_ast_graft_private.h"
+
+#define ISL_TYPE isl_ast_graft_list
+#include <isl/maybe_templ.h>
+#undef ISL_TYPE
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_maybe_map.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_maybe_map.h
new file mode 100644
index 00000000000..460a89e8a8b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_maybe_map.h
@@ -0,0 +1,10 @@
+#ifndef ISL_MAYBE_MAP_H
+#define ISL_MAYBE_MAP_H
+
+#include <isl/map_type.h>
+
+#define ISL_TYPE isl_map
+#include <isl/maybe_templ.h>
+#undef ISL_TYPE
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_morph.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_morph.c
new file mode 100644
index 00000000000..f7b920b166d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_morph.c
@@ -0,0 +1,804 @@
+/*
+ * Copyright 2010-2011 INRIA Saclay
+ * Copyright 2014 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl_map_private.h>
+#include <isl_aff_private.h>
+#include <isl_morph.h>
+#include <isl_seq.h>
+#include <isl_mat_private.h>
+#include <isl_space_private.h>
+#include <isl_equalities.h>
+#include <isl_id_private.h>
+#include <isl_aff_private.h>
+#include <isl_vec_private.h>
+
+isl_ctx *isl_morph_get_ctx(__isl_keep isl_morph *morph)
+{
+ if (!morph)
+ return NULL;
+ return isl_basic_set_get_ctx(morph->dom);
+}
+
+__isl_give isl_morph *isl_morph_alloc(
+ __isl_take isl_basic_set *dom, __isl_take isl_basic_set *ran,
+ __isl_take isl_mat *map, __isl_take isl_mat *inv)
+{
+ isl_morph *morph;
+
+ if (!dom || !ran || !map || !inv)
+ goto error;
+
+ morph = isl_alloc_type(dom->ctx, struct isl_morph);
+ if (!morph)
+ goto error;
+
+ morph->ref = 1;
+ morph->dom = dom;
+ morph->ran = ran;
+ morph->map = map;
+ morph->inv = inv;
+
+ return morph;
+error:
+ isl_basic_set_free(dom);
+ isl_basic_set_free(ran);
+ isl_mat_free(map);
+ isl_mat_free(inv);
+ return NULL;
+}
+
+__isl_give isl_morph *isl_morph_copy(__isl_keep isl_morph *morph)
+{
+ if (!morph)
+ return NULL;
+
+ morph->ref++;
+ return morph;
+}
+
+__isl_give isl_morph *isl_morph_dup(__isl_keep isl_morph *morph)
+{
+ if (!morph)
+ return NULL;
+
+ return isl_morph_alloc(isl_basic_set_copy(morph->dom),
+ isl_basic_set_copy(morph->ran),
+ isl_mat_copy(morph->map), isl_mat_copy(morph->inv));
+}
+
+__isl_give isl_morph *isl_morph_cow(__isl_take isl_morph *morph)
+{
+ if (!morph)
+ return NULL;
+
+ if (morph->ref == 1)
+ return morph;
+ morph->ref--;
+ return isl_morph_dup(morph);
+}
+
+__isl_null isl_morph *isl_morph_free(__isl_take isl_morph *morph)
+{
+ if (!morph)
+ return NULL;
+
+ if (--morph->ref > 0)
+ return NULL;
+
+ isl_basic_set_free(morph->dom);
+ isl_basic_set_free(morph->ran);
+ isl_mat_free(morph->map);
+ isl_mat_free(morph->inv);
+ free(morph);
+
+ return NULL;
+}
+
+/* Is "morph" an identity on the parameters?
+ */
+static isl_bool identity_on_parameters(__isl_keep isl_morph *morph)
+{
+ isl_bool is_identity;
+ isl_size nparam, nparam_ran;
+ isl_mat *sub;
+
+ nparam = isl_morph_dom_dim(morph, isl_dim_param);
+ nparam_ran = isl_morph_ran_dim(morph, isl_dim_param);
+ if (nparam < 0 || nparam_ran < 0)
+ return isl_bool_error;
+ if (nparam != nparam_ran)
+ return isl_bool_false;
+ if (nparam == 0)
+ return isl_bool_true;
+ sub = isl_mat_sub_alloc(morph->map, 0, 1 + nparam, 0, 1 + nparam);
+ is_identity = isl_mat_is_scaled_identity(sub);
+ isl_mat_free(sub);
+
+ return is_identity;
+}
+
+/* Return an affine expression of the variables of the range of "morph"
+ * in terms of the parameters and the variables of the domain on "morph".
+ *
+ * In order for the space manipulations to make sense, we require
+ * that the parameters are not modified by "morph".
+ */
+__isl_give isl_multi_aff *isl_morph_get_var_multi_aff(
+ __isl_keep isl_morph *morph)
+{
+ isl_space *dom, *ran, *space;
+ isl_local_space *ls;
+ isl_multi_aff *ma;
+ isl_size nparam, nvar;
+ int i;
+ isl_bool is_identity;
+
+ if (!morph)
+ return NULL;
+
+ is_identity = identity_on_parameters(morph);
+ if (is_identity < 0)
+ return NULL;
+ if (!is_identity)
+ isl_die(isl_morph_get_ctx(morph), isl_error_invalid,
+ "cannot handle parameter compression", return NULL);
+
+ dom = isl_morph_get_dom_space(morph);
+ ls = isl_local_space_from_space(isl_space_copy(dom));
+ ran = isl_morph_get_ran_space(morph);
+ space = isl_space_map_from_domain_and_range(dom, ran);
+ ma = isl_multi_aff_zero(space);
+
+ nparam = isl_multi_aff_dim(ma, isl_dim_param);
+ nvar = isl_multi_aff_dim(ma, isl_dim_out);
+ if (nparam < 0 || nvar < 0)
+ ma = isl_multi_aff_free(ma);
+ for (i = 0; i < nvar; ++i) {
+ isl_val *val;
+ isl_vec *v;
+ isl_aff *aff;
+
+ v = isl_mat_get_row(morph->map, 1 + nparam + i);
+ v = isl_vec_insert_els(v, 0, 1);
+ val = isl_mat_get_element_val(morph->map, 0, 0);
+ v = isl_vec_set_element_val(v, 0, val);
+ aff = isl_aff_alloc_vec(isl_local_space_copy(ls), v);
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ }
+
+ isl_local_space_free(ls);
+ return ma;
+}
+
+/* Return the domain space of "morph".
+ */
+static __isl_keep isl_space *isl_morph_peek_dom_space(
+ __isl_keep isl_morph *morph)
+{
+ if (!morph)
+ return NULL;
+
+ return isl_basic_set_peek_space(morph->dom);
+}
+
+/* Return a copy of the domain space of "morph".
+ */
+__isl_give isl_space *isl_morph_get_dom_space(__isl_keep isl_morph *morph)
+{
+ return isl_space_copy(isl_morph_peek_dom_space(morph));
+}
+
+/* Check that the match against "space" with result "match" was successful.
+ */
+static isl_stat check_space_match(__isl_keep isl_space *space, isl_bool match)
+{
+ if (match < 0)
+ return isl_stat_error;
+ if (!match)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "spaces don't match", return isl_stat_error);
+
+ return isl_stat_ok;
+}
+
+/* Check that "morph" can be applied to the "space".
+ */
+isl_stat isl_morph_check_applies(__isl_keep isl_morph *morph,
+ __isl_keep isl_space *space)
+{
+ isl_space *dom_space;
+ isl_bool applies;
+
+ dom_space = isl_morph_peek_dom_space(morph);
+ applies = isl_space_is_equal(dom_space, space);
+ return check_space_match(space, applies);
+}
+
+__isl_give isl_space *isl_morph_get_ran_space(__isl_keep isl_morph *morph)
+{
+ if (!morph)
+ return NULL;
+
+ return isl_space_copy(morph->ran->dim);
+}
+
+isl_size isl_morph_dom_dim(__isl_keep isl_morph *morph, enum isl_dim_type type)
+{
+ if (!morph)
+ return isl_size_error;
+
+ return isl_basic_set_dim(morph->dom, type);
+}
+
+isl_size isl_morph_ran_dim(__isl_keep isl_morph *morph, enum isl_dim_type type)
+{
+ if (!morph)
+ return isl_size_error;
+
+ return isl_basic_set_dim(morph->ran, type);
+}
+
+__isl_give isl_morph *isl_morph_remove_dom_dims(__isl_take isl_morph *morph,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ unsigned dom_offset;
+
+ if (n == 0)
+ return morph;
+
+ morph = isl_morph_cow(morph);
+ if (!morph)
+ return NULL;
+
+ dom_offset = 1 + isl_space_offset(morph->dom->dim, type);
+
+ morph->dom = isl_basic_set_remove_dims(morph->dom, type, first, n);
+
+ morph->map = isl_mat_drop_cols(morph->map, dom_offset + first, n);
+
+ morph->inv = isl_mat_drop_rows(morph->inv, dom_offset + first, n);
+
+ if (morph->dom && morph->ran && morph->map && morph->inv)
+ return morph;
+
+ isl_morph_free(morph);
+ return NULL;
+}
+
+__isl_give isl_morph *isl_morph_remove_ran_dims(__isl_take isl_morph *morph,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ unsigned ran_offset;
+
+ if (n == 0)
+ return morph;
+
+ morph = isl_morph_cow(morph);
+ if (!morph)
+ return NULL;
+
+ ran_offset = 1 + isl_space_offset(morph->ran->dim, type);
+
+ morph->ran = isl_basic_set_remove_dims(morph->ran, type, first, n);
+
+ morph->map = isl_mat_drop_rows(morph->map, ran_offset + first, n);
+
+ morph->inv = isl_mat_drop_cols(morph->inv, ran_offset + first, n);
+
+ if (morph->dom && morph->ran && morph->map && morph->inv)
+ return morph;
+
+ isl_morph_free(morph);
+ return NULL;
+}
+
+/* Project domain of morph onto its parameter domain.
+ */
+__isl_give isl_morph *isl_morph_dom_params(__isl_take isl_morph *morph)
+{
+ isl_size n;
+
+ morph = isl_morph_cow(morph);
+ if (!morph)
+ return NULL;
+ n = isl_basic_set_dim(morph->dom, isl_dim_set);
+ if (n < 0)
+ return isl_morph_free(morph);
+ morph = isl_morph_remove_dom_dims(morph, isl_dim_set, 0, n);
+ if (!morph)
+ return NULL;
+ morph->dom = isl_basic_set_params(morph->dom);
+ if (morph->dom)
+ return morph;
+
+ isl_morph_free(morph);
+ return NULL;
+}
+
+/* Project range of morph onto its parameter domain.
+ */
+__isl_give isl_morph *isl_morph_ran_params(__isl_take isl_morph *morph)
+{
+ isl_size n;
+
+ morph = isl_morph_cow(morph);
+ if (!morph)
+ return NULL;
+ n = isl_basic_set_dim(morph->ran, isl_dim_set);
+ if (n < 0)
+ return isl_morph_free(morph);
+ morph = isl_morph_remove_ran_dims(morph, isl_dim_set, 0, n);
+ if (!morph)
+ return NULL;
+ morph->ran = isl_basic_set_params(morph->ran);
+ if (morph->ran)
+ return morph;
+
+ isl_morph_free(morph);
+ return NULL;
+}
+
+/* Replace the identifier of the tuple of the range of the morph by "id".
+ */
+static __isl_give isl_morph *isl_morph_set_ran_tuple_id(
+ __isl_take isl_morph *morph, __isl_keep isl_id *id)
+{
+ morph = isl_morph_cow(morph);
+ if (!morph)
+ return NULL;
+ morph->ran = isl_basic_set_set_tuple_id(morph->ran, isl_id_copy(id));
+ if (!morph->ran)
+ return isl_morph_free(morph);
+ return morph;
+}
+
+void isl_morph_print_internal(__isl_take isl_morph *morph, FILE *out)
+{
+ if (!morph)
+ return;
+
+ isl_basic_set_dump(morph->dom);
+ isl_basic_set_dump(morph->ran);
+ isl_mat_print_internal(morph->map, out, 4);
+ isl_mat_print_internal(morph->inv, out, 4);
+}
+
+void isl_morph_dump(__isl_take isl_morph *morph)
+{
+ isl_morph_print_internal(morph, stderr);
+}
+
+__isl_give isl_morph *isl_morph_identity(__isl_keep isl_basic_set *bset)
+{
+ isl_mat *id;
+ isl_basic_set *universe;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ return NULL;
+
+ id = isl_mat_identity(bset->ctx, 1 + total);
+ universe = isl_basic_set_universe(isl_space_copy(bset->dim));
+
+ return isl_morph_alloc(universe, isl_basic_set_copy(universe),
+ id, isl_mat_copy(id));
+}
+
+/* Create a(n identity) morphism between empty sets of the same dimension
+ * a "bset".
+ */
+__isl_give isl_morph *isl_morph_empty(__isl_keep isl_basic_set *bset)
+{
+ isl_mat *id;
+ isl_basic_set *empty;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ return NULL;
+
+ id = isl_mat_identity(bset->ctx, 1 + total);
+ empty = isl_basic_set_empty(isl_space_copy(bset->dim));
+
+ return isl_morph_alloc(empty, isl_basic_set_copy(empty),
+ id, isl_mat_copy(id));
+}
+
+/* Construct a basic set described by the "n" equalities of "bset" starting
+ * at "first".
+ */
+static __isl_give isl_basic_set *copy_equalities(__isl_keep isl_basic_set *bset,
+ unsigned first, unsigned n)
+{
+ int i, k;
+ isl_basic_set *eq;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0 || isl_basic_set_check_no_locals(bset) < 0)
+ return NULL;
+
+ eq = isl_basic_set_alloc_space(isl_basic_set_get_space(bset), 0, n, 0);
+ if (!eq)
+ return NULL;
+ for (i = 0; i < n; ++i) {
+ k = isl_basic_set_alloc_equality(eq);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(eq->eq[k], bset->eq[first + i], 1 + total);
+ }
+
+ return eq;
+error:
+ isl_basic_set_free(eq);
+ return NULL;
+}
+
+/* Given a basic set, exploit the equalities in the basic set to construct
+ * a morphism that maps the basic set to a lower-dimensional space.
+ * Specifically, the morphism reduces the number of dimensions of type "type".
+ *
+ * We first select the equalities of interest, that is those that involve
+ * variables of type "type" and no later variables.
+ * Denote those equalities as
+ *
+ * -C(p) + M x = 0
+ *
+ * where C(p) depends on the parameters if type == isl_dim_set and
+ * is a constant if type == isl_dim_param.
+ *
+ * Use isl_mat_final_variable_compression to construct a compression
+ *
+ * x = T x'
+ *
+ * x' = Q x
+ *
+ * If T is a zero-column matrix, then the set of equality constraints
+ * do not admit a solution. In this case, an empty morphism is returned.
+ *
+ * Both matrices are extended to map the full original space to the full
+ * compressed space.
+ */
+__isl_give isl_morph *isl_basic_set_variable_compression(
+ __isl_keep isl_basic_set *bset, enum isl_dim_type type)
+{
+ unsigned otype;
+ isl_size ntype;
+ unsigned orest;
+ unsigned nrest;
+ isl_size total;
+ int f_eq, n_eq;
+ isl_space *space;
+ isl_mat *E, *Q, *C;
+ isl_basic_set *dom, *ran;
+
+ if (!bset)
+ return NULL;
+
+ if (isl_basic_set_plain_is_empty(bset))
+ return isl_morph_empty(bset);
+
+ if (isl_basic_set_check_no_locals(bset) < 0)
+ return NULL;
+
+ ntype = isl_basic_set_dim(bset, type);
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (ntype < 0 || total < 0)
+ return NULL;
+ otype = isl_basic_set_offset(bset, type);
+ orest = otype + ntype;
+ nrest = total - (orest - 1);
+
+ for (f_eq = 0; f_eq < bset->n_eq; ++f_eq)
+ if (isl_seq_first_non_zero(bset->eq[f_eq] + orest, nrest) == -1)
+ break;
+ for (n_eq = 0; f_eq + n_eq < bset->n_eq; ++n_eq)
+ if (isl_seq_first_non_zero(bset->eq[f_eq + n_eq] + otype, ntype) == -1)
+ break;
+ if (n_eq == 0)
+ return isl_morph_identity(bset);
+
+ E = isl_mat_sub_alloc6(bset->ctx, bset->eq, f_eq, n_eq, 0, orest);
+ C = isl_mat_final_variable_compression(E, otype - 1, &Q);
+ if (!Q)
+ C = isl_mat_free(C);
+ if (C && C->n_col == 0) {
+ isl_mat_free(C);
+ isl_mat_free(Q);
+ return isl_morph_empty(bset);
+ }
+
+ Q = isl_mat_diagonal(Q, isl_mat_identity(bset->ctx, nrest));
+ C = isl_mat_diagonal(C, isl_mat_identity(bset->ctx, nrest));
+
+ space = isl_space_copy(bset->dim);
+ space = isl_space_drop_dims(space, type, 0, ntype);
+ space = isl_space_add_dims(space, type, ntype - n_eq);
+ ran = isl_basic_set_universe(space);
+ dom = copy_equalities(bset, f_eq, n_eq);
+
+ return isl_morph_alloc(dom, ran, Q, C);
+}
+
+/* Given a basic set, exploit the equalities in the basic set to construct
+ * a morphism that maps the basic set to a lower-dimensional space
+ * with identifier "id".
+ * Specifically, the morphism reduces the number of set dimensions.
+ */
+__isl_give isl_morph *isl_basic_set_variable_compression_with_id(
+ __isl_keep isl_basic_set *bset, __isl_keep isl_id *id)
+{
+ isl_morph *morph;
+
+ morph = isl_basic_set_variable_compression(bset, isl_dim_set);
+ morph = isl_morph_set_ran_tuple_id(morph, id);
+ return morph;
+}
+
+/* Construct a parameter compression for "bset".
+ * We basically just call isl_mat_parameter_compression with the right input
+ * and then extend the resulting matrix to include the variables.
+ *
+ * The implementation assumes that "bset" does not have any equalities
+ * that only involve the parameters and that isl_basic_set_gauss has
+ * been applied to "bset".
+ *
+ * Let the equalities be given as
+ *
+ * B(p) + A x = 0.
+ *
+ * We use isl_mat_parameter_compression_ext to compute the compression
+ *
+ * p = T p'.
+ */
+__isl_give isl_morph *isl_basic_set_parameter_compression(
+ __isl_keep isl_basic_set *bset)
+{
+ isl_size nparam;
+ isl_size nvar;
+ isl_size n_div;
+ int n_eq;
+ isl_mat *H, *B;
+ isl_mat *map, *inv;
+ isl_basic_set *dom, *ran;
+
+ if (!bset)
+ return NULL;
+
+ if (isl_basic_set_plain_is_empty(bset))
+ return isl_morph_empty(bset);
+ if (bset->n_eq == 0)
+ return isl_morph_identity(bset);
+
+ n_eq = bset->n_eq;
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+ n_div = isl_basic_set_dim(bset, isl_dim_div);
+ if (nparam < 0 || nvar < 0 || n_div < 0)
+ return NULL;
+
+ if (isl_seq_first_non_zero(bset->eq[bset->n_eq - 1] + 1 + nparam,
+ nvar + n_div) == -1)
+ isl_die(isl_basic_set_get_ctx(bset), isl_error_invalid,
+ "input not allowed to have parameter equalities",
+ return NULL);
+ if (n_eq > nvar + n_div)
+ isl_die(isl_basic_set_get_ctx(bset), isl_error_invalid,
+ "input not gaussed", return NULL);
+
+ B = isl_mat_sub_alloc6(bset->ctx, bset->eq, 0, n_eq, 0, 1 + nparam);
+ H = isl_mat_sub_alloc6(bset->ctx, bset->eq,
+ 0, n_eq, 1 + nparam, nvar + n_div);
+ inv = isl_mat_parameter_compression_ext(B, H);
+ inv = isl_mat_diagonal(inv, isl_mat_identity(bset->ctx, nvar));
+ map = isl_mat_right_inverse(isl_mat_copy(inv));
+
+ dom = isl_basic_set_universe(isl_space_copy(bset->dim));
+ ran = isl_basic_set_universe(isl_space_copy(bset->dim));
+
+ return isl_morph_alloc(dom, ran, map, inv);
+}
+
+/* Construct an isl_multi_aff that corresponds
+ * to the affine transformation matrix "mat" and
+ * that lives in an anonymous space.
+ */
+static __isl_give isl_multi_aff *isl_multi_aff_from_aff_mat_anonymous(
+ __isl_take isl_mat *mat)
+{
+ isl_size n_row, n_col;
+ isl_ctx *ctx;
+ isl_space *space;
+
+ ctx = isl_mat_get_ctx(mat);
+ n_row = isl_mat_rows(mat);
+ n_col = isl_mat_cols(mat);
+ if (n_row < 0 || n_col < 0)
+ space = NULL;
+ else
+ space = isl_space_alloc(ctx, 0, n_col - 1, n_row - 1);
+
+ return isl_multi_aff_from_aff_mat(space, mat);
+}
+
+/* Apply the morphism to the basic set.
+ * In particular, compute the preimage of "bset" under the inverse mapping
+ * in morph and intersect with the range of the morphism.
+ * Note that the mapping in morph applies to both parameters and set dimensions,
+ * so the parameters need to be treated as set dimensions during the call
+ * to isl_basic_set_preimage_multi_aff.
+ */
+__isl_give isl_basic_set *isl_morph_basic_set(__isl_take isl_morph *morph,
+ __isl_take isl_basic_set *bset)
+{
+ isl_size n_param;
+ isl_space *space;
+ isl_multi_aff *ma;
+
+ if (!morph || isl_basic_set_check_equal_space(bset, morph->dom) < 0)
+ goto error;
+ n_param = isl_basic_set_dim(morph->dom, isl_dim_param);
+ if (n_param < 0)
+ goto error;
+
+ ma = isl_multi_aff_from_aff_mat_anonymous(isl_mat_copy(morph->inv));
+
+ bset = isl_basic_set_move_dims(bset, isl_dim_set, 0,
+ isl_dim_param, 0, n_param);
+ bset = isl_basic_set_preimage_multi_aff(bset, ma);
+ space = isl_basic_set_get_space(morph->ran);
+ bset = isl_basic_set_reset_space(bset, space);
+ bset = isl_basic_set_intersect(bset, isl_basic_set_copy(morph->ran));
+
+ isl_morph_free(morph);
+ return bset;
+error:
+ isl_morph_free(morph);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Apply the morphism to the set.
+ * In particular, compute the preimage of "set" under the inverse mapping
+ * in morph and intersect with the range of the morphism.
+ * Note that the mapping in morph applies to both parameters and set dimensions,
+ * so the parameters need to be treated as set dimensions during the call
+ * to isl_set_preimage_multi_aff.
+ */
+__isl_give isl_set *isl_morph_set(__isl_take isl_morph *morph,
+ __isl_take isl_set *set)
+{
+ isl_size n_param;
+ isl_space *space;
+ isl_multi_aff *ma;
+ isl_basic_set *ran;
+
+ if (!morph || isl_set_basic_set_check_equal_space(set, morph->dom) < 0)
+ goto error;
+ n_param = isl_basic_set_dim(morph->dom, isl_dim_param);
+ if (n_param < 0)
+ goto error;
+
+ ma = isl_multi_aff_from_aff_mat_anonymous(isl_mat_copy(morph->inv));
+
+ set = isl_set_move_dims(set, isl_dim_set, 0, isl_dim_param, 0, n_param);
+ set = isl_set_preimage_multi_aff(set, ma);
+ space = isl_basic_set_get_space(morph->ran);
+ set = isl_set_reset_space(set, space);
+ ran = isl_basic_set_copy(morph->ran);
+ set = isl_set_intersect(set, isl_set_from_basic_set(ran));
+
+ isl_morph_free(morph);
+ return set;
+error:
+ isl_set_free(set);
+ isl_morph_free(morph);
+ return NULL;
+}
+
+/* Construct a morphism that first does morph2 and then morph1.
+ */
+__isl_give isl_morph *isl_morph_compose(__isl_take isl_morph *morph1,
+ __isl_take isl_morph *morph2)
+{
+ isl_mat *map, *inv;
+ isl_basic_set *dom, *ran;
+
+ if (!morph1 || !morph2)
+ goto error;
+
+ map = isl_mat_product(isl_mat_copy(morph1->map), isl_mat_copy(morph2->map));
+ inv = isl_mat_product(isl_mat_copy(morph2->inv), isl_mat_copy(morph1->inv));
+ dom = isl_morph_basic_set(isl_morph_inverse(isl_morph_copy(morph2)),
+ isl_basic_set_copy(morph1->dom));
+ dom = isl_basic_set_intersect(dom, isl_basic_set_copy(morph2->dom));
+ ran = isl_morph_basic_set(isl_morph_copy(morph1),
+ isl_basic_set_copy(morph2->ran));
+ ran = isl_basic_set_intersect(ran, isl_basic_set_copy(morph1->ran));
+
+ isl_morph_free(morph1);
+ isl_morph_free(morph2);
+
+ return isl_morph_alloc(dom, ran, map, inv);
+error:
+ isl_morph_free(morph1);
+ isl_morph_free(morph2);
+ return NULL;
+}
+
+__isl_give isl_morph *isl_morph_inverse(__isl_take isl_morph *morph)
+{
+ isl_basic_set *bset;
+ isl_mat *mat;
+
+ morph = isl_morph_cow(morph);
+ if (!morph)
+ return NULL;
+
+ bset = morph->dom;
+ morph->dom = morph->ran;
+ morph->ran = bset;
+
+ mat = morph->map;
+ morph->map = morph->inv;
+ morph->inv = mat;
+
+ return morph;
+}
+
+/* We detect all the equalities first to avoid implicit equalities
+ * being discovered during the computations. In particular,
+ * the compression on the variables could expose additional stride
+ * constraints on the parameters. This would result in existentially
+ * quantified variables after applying the resulting morph, which
+ * in turn could break invariants of the calling functions.
+ */
+__isl_give isl_morph *isl_basic_set_full_compression(
+ __isl_keep isl_basic_set *bset)
+{
+ isl_morph *morph, *morph2;
+
+ bset = isl_basic_set_copy(bset);
+ bset = isl_basic_set_detect_equalities(bset);
+
+ morph = isl_basic_set_variable_compression(bset, isl_dim_param);
+ bset = isl_morph_basic_set(isl_morph_copy(morph), bset);
+
+ morph2 = isl_basic_set_parameter_compression(bset);
+ bset = isl_morph_basic_set(isl_morph_copy(morph2), bset);
+
+ morph = isl_morph_compose(morph2, morph);
+
+ morph2 = isl_basic_set_variable_compression(bset, isl_dim_set);
+ isl_basic_set_free(bset);
+
+ morph = isl_morph_compose(morph2, morph);
+
+ return morph;
+}
+
+__isl_give isl_vec *isl_morph_vec(__isl_take isl_morph *morph,
+ __isl_take isl_vec *vec)
+{
+ if (!morph)
+ goto error;
+
+ vec = isl_mat_vec_product(isl_mat_copy(morph->map), vec);
+
+ isl_morph_free(morph);
+ return vec;
+error:
+ isl_morph_free(morph);
+ isl_vec_free(vec);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_morph.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_morph.h
new file mode 100644
index 00000000000..004e148946d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_morph.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#ifndef ISL_MORHP_H
+#define ISL_MORHP_H
+
+#include <stdio.h>
+#include <isl/id_type.h>
+#include <isl/space.h>
+#include <isl/mat.h>
+#include <isl/set.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* An isl_morph is a "morphism" on (basic) sets.
+ * "map" is an affine mapping from "dom" to "ran"
+ * and "inv" is the inverse mapping.
+ */
+struct isl_morph {
+ int ref;
+
+ isl_basic_set *dom;
+ isl_basic_set *ran;
+
+ isl_mat *map;
+ isl_mat *inv;
+};
+typedef struct isl_morph isl_morph;
+
+isl_ctx *isl_morph_get_ctx(__isl_keep isl_morph *morph);
+
+__isl_give isl_morph *isl_morph_alloc(
+ __isl_take isl_basic_set *dom, __isl_take isl_basic_set *ran,
+ __isl_take isl_mat *map, __isl_take isl_mat *inv);
+__isl_give isl_morph *isl_morph_copy(__isl_keep isl_morph *morph);
+__isl_give isl_morph *isl_morph_identity(__isl_keep isl_basic_set *bset);
+__isl_null isl_morph *isl_morph_free(__isl_take isl_morph *morph);
+
+isl_stat isl_morph_check_applies(__isl_keep isl_morph *morph,
+ __isl_keep isl_space *space);
+
+__isl_give isl_space *isl_morph_get_dom_space(__isl_keep isl_morph *morph);
+__isl_give isl_space *isl_morph_get_ran_space(__isl_keep isl_morph *morph);
+__isl_give isl_multi_aff *isl_morph_get_var_multi_aff(
+ __isl_keep isl_morph *morph);
+isl_size isl_morph_dom_dim(__isl_keep isl_morph *morph, enum isl_dim_type type);
+isl_size isl_morph_ran_dim(__isl_keep isl_morph *morph, enum isl_dim_type type);
+
+__isl_give isl_morph *isl_morph_remove_dom_dims(__isl_take isl_morph *morph,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_morph *isl_morph_remove_ran_dims(__isl_take isl_morph *morph,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_morph *isl_morph_dom_params(__isl_take isl_morph *morph);
+__isl_give isl_morph *isl_morph_ran_params(__isl_take isl_morph *morph);
+
+__isl_give isl_morph *isl_morph_compose(__isl_take isl_morph *morph1,
+ __isl_take isl_morph *morph2);
+__isl_give isl_morph *isl_morph_inverse(__isl_take isl_morph *morph);
+
+void isl_morph_print_internal(__isl_take isl_morph *morph, FILE *out);
+void isl_morph_dump(__isl_take isl_morph *morph);
+
+__isl_give isl_morph *isl_basic_set_variable_compression(
+ __isl_keep isl_basic_set *bset, enum isl_dim_type type);
+__isl_give isl_morph *isl_basic_set_variable_compression_with_id(
+ __isl_keep isl_basic_set *bset, __isl_keep isl_id *id);
+__isl_give isl_morph *isl_basic_set_parameter_compression(
+ __isl_keep isl_basic_set *bset);
+__isl_give isl_morph *isl_basic_set_full_compression(
+ __isl_keep isl_basic_set *bset);
+
+__isl_give isl_basic_set *isl_morph_basic_set(__isl_take isl_morph *morph,
+ __isl_take isl_basic_set *bset);
+__isl_give isl_set *isl_morph_set(__isl_take isl_morph *morph,
+ __isl_take isl_set *set);
+__isl_give isl_vec *isl_morph_vec(__isl_take isl_morph *morph,
+ __isl_take isl_vec *vec);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_add_constant_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_add_constant_templ.c
new file mode 100644
index 00000000000..fa4971fb733
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_add_constant_templ.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_multi_macro.h>
+
+/* Add "v" to the constant terms of all the base expressions of "multi".
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),add_constant_val)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_val *v)
+{
+ isl_bool zero;
+ isl_size n;
+ int i;
+
+ zero = isl_val_is_zero(v);
+ n = FN(MULTI(BASE),size)(multi);
+ if (zero < 0 || n < 0)
+ goto error;
+ if (zero || n == 0) {
+ isl_val_free(v);
+ return multi;
+ }
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ goto error;
+
+ for (i = 0; i < n; ++i) {
+ multi->u.p[i] = FN(EL,add_constant_val)(multi->u.p[i],
+ isl_val_copy(v));
+ if (!multi->u.p[i])
+ goto error;
+ }
+
+ isl_val_free(v);
+ return multi;
+error:
+ FN(MULTI(BASE),free)(multi);
+ isl_val_free(v);
+ return NULL;
+}
+
+/* Add the elements of "mv" to the constant terms of
+ * the corresponding base expressions of "multi".
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),add_constant_multi_val)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_multi_val *mv)
+{
+ isl_space *multi_space, *mv_space;
+ isl_bool zero, equal;
+ isl_size n;
+ int i;
+
+ zero = isl_multi_val_is_zero(mv);
+ n = FN(MULTI(BASE),size)(multi);
+ multi_space = FN(MULTI(BASE),peek_space)(multi);
+ mv_space = isl_multi_val_peek_space(mv);
+ equal = isl_space_tuple_is_equal(multi_space, isl_dim_out,
+ mv_space, isl_dim_out);
+ if (zero < 0 || n < 0 || equal < 0)
+ goto error;
+ if (!equal)
+ isl_die(isl_multi_val_get_ctx(mv), isl_error_invalid,
+ "spaces don't match", goto error);
+ if (zero || n == 0) {
+ isl_multi_val_free(mv);
+ return multi;
+ }
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ goto error;
+
+ for (i = 0; i < n; ++i) {
+ isl_val *v = isl_multi_val_get_at(mv, i);
+ multi->u.p[i] = FN(EL,add_constant_val)(multi->u.p[i], v);
+ if (!multi->u.p[i])
+ goto error;
+ }
+
+ isl_multi_val_free(mv);
+ return multi;
+error:
+ FN(MULTI(BASE),free)(multi);
+ isl_multi_val_free(mv);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_set.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_set.c
new file mode 100644
index 00000000000..d7ef608772d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_set.c
@@ -0,0 +1,7 @@
+#define ALIGN_DOMBASE set
+#define ALIGN_DOM isl_set
+
+#include <isl_multi_align_templ.c>
+
+#undef ALIGN_DOMBASE
+#undef ALIGN_DOM
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_templ.c
new file mode 100644
index 00000000000..f4604f07365
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_templ.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ */
+
+/* Align the parameters of "multi" and "domain" (if needed) and
+ * call "fn".
+ */
+static __isl_give MULTI(BASE) *FN(FN(MULTI(BASE),align_params),ALIGN_DOMBASE)(
+ __isl_take MULTI(BASE) *multi, __isl_take ALIGN_DOM *domain,
+ __isl_give MULTI(BASE) *fn(__isl_take MULTI(BASE) *multi,
+ __isl_take ALIGN_DOM *domain))
+{
+ isl_bool aligned;
+ isl_bool named;
+ isl_space *dom_space;
+
+ aligned = FN(ALIGN_DOM,space_has_equal_params)(domain, multi->space);
+ if (aligned < 0)
+ goto error;
+ if (aligned)
+ return fn(multi, domain);
+
+ dom_space = FN(ALIGN_DOM,peek_space)(domain);
+ named = isl_space_has_named_params(multi->space);
+ if (named >= 0 && named)
+ named = isl_space_has_named_params(dom_space);
+ if (named < 0)
+ goto error;
+ if (!named)
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid,
+ "unaligned unnamed parameters", goto error);
+ multi = FN(MULTI(BASE),align_params)(multi,
+ FN(ALIGN_DOM,get_space)(domain));
+ domain = FN(ALIGN_DOM,align_params)(domain,
+ FN(MULTI(BASE),get_space)(multi));
+ return fn(multi, domain);
+error:
+ FN(MULTI(BASE),free)(multi);
+ FN(ALIGN_DOM,free)(domain);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_union_set.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_union_set.c
new file mode 100644
index 00000000000..545a887c7d7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_align_union_set.c
@@ -0,0 +1,7 @@
+#define ALIGN_DOMBASE union_set
+#define ALIGN_DOM isl_union_set
+
+#include <isl_multi_align_templ.c>
+
+#undef ALIGN_DOMBASE
+#undef ALIGN_DOM
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_set.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_set.c
new file mode 100644
index 00000000000..df97878d109
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_set.c
@@ -0,0 +1,7 @@
+#define APPLY_DOMBASE set
+#define APPLY_DOM isl_set
+
+#include <isl_multi_apply_templ.c>
+
+#undef APPLY_DOMBASE
+#undef APPLY_DOM
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_templ.c
new file mode 100644
index 00000000000..870f2676c42
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_templ.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011 Sven Verdoolaege
+ * Copyright 2012-2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <isl_multi_macro.h>
+
+/* Transform the elements of "multi" by applying "fn" to them
+ * with extra argument "set".
+ *
+ * The parameters of "multi" and "set" are assumed to have been aligned.
+ */
+__isl_give MULTI(BASE) *FN(FN(MULTI(BASE),apply_aligned),APPLY_DOMBASE)(
+ __isl_take MULTI(BASE) *multi, __isl_take APPLY_DOM *set,
+ __isl_give EL *(*fn)(EL *el, __isl_take APPLY_DOM *set))
+{
+ int i;
+
+ if (!multi || !set)
+ goto error;
+
+ if (multi->n == 0) {
+ FN(APPLY_DOM,free)(set);
+ return multi;
+ }
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ goto error;
+
+ for (i = 0; i < multi->n; ++i) {
+ multi->u.p[i] = fn(multi->u.p[i], FN(APPLY_DOM,copy)(set));
+ if (!multi->u.p[i])
+ goto error;
+ }
+
+ FN(APPLY_DOM,free)(set);
+ return multi;
+error:
+ FN(APPLY_DOM,free)(set);
+ FN(MULTI(BASE),free)(multi);
+ return NULL;
+}
+
+/* Transform the elements of "multi" by applying "fn" to them
+ * with extra argument "set".
+ *
+ * Align the parameters if needed and call apply_set_aligned.
+ */
+static __isl_give MULTI(BASE) *FN(FN(MULTI(BASE),apply),APPLY_DOMBASE)(
+ __isl_take MULTI(BASE) *multi, __isl_take APPLY_DOM *set,
+ __isl_give EL *(*fn)(EL *el, __isl_take APPLY_DOM *set))
+{
+ isl_bool aligned;
+ isl_ctx *ctx;
+
+ if (!multi || !set)
+ goto error;
+
+ aligned = FN(APPLY_DOM,space_has_equal_params)(set, multi->space);
+ if (aligned < 0)
+ goto error;
+ if (aligned)
+ return FN(FN(MULTI(BASE),apply_aligned),APPLY_DOMBASE)(multi,
+ set, fn);
+ ctx = FN(MULTI(BASE),get_ctx)(multi);
+ if (!isl_space_has_named_params(multi->space) ||
+ !isl_space_has_named_params(set->dim))
+ isl_die(ctx, isl_error_invalid,
+ "unaligned unnamed parameters", goto error);
+ multi = FN(MULTI(BASE),align_params)(multi,
+ FN(APPLY_DOM,get_space)(set));
+ set = FN(APPLY_DOM,align_params)(set, FN(MULTI(BASE),get_space)(multi));
+ return FN(FN(MULTI(BASE),apply_aligned),APPLY_DOMBASE)(multi, set, fn);
+error:
+ FN(MULTI(BASE),free)(multi);
+ FN(APPLY_DOM,free)(set);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_union_set.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_union_set.c
new file mode 100644
index 00000000000..d7c298a3630
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_apply_union_set.c
@@ -0,0 +1,7 @@
+#define APPLY_DOMBASE union_set
+#define APPLY_DOM isl_union_set
+
+#include <isl_multi_apply_templ.c>
+
+#undef APPLY_DOMBASE
+#undef APPLY_DOM
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_arith_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_arith_templ.c
new file mode 100644
index 00000000000..6765bd23cb5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_arith_templ.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2011 Sven Verdoolaege
+ * Copyright 2012-2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+#include <isl_val_private.h>
+
+#include <isl_multi_macro.h>
+
+/* Add "multi2" to "multi1" and return the result.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),add)(__isl_take MULTI(BASE) *multi1,
+ __isl_take MULTI(BASE) *multi2)
+{
+ return FN(MULTI(BASE),bin_op)(multi1, multi2, &FN(EL,add));
+}
+
+/* Subtract "multi2" from "multi1" and return the result.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),sub)(__isl_take MULTI(BASE) *multi1,
+ __isl_take MULTI(BASE) *multi2)
+{
+ return FN(MULTI(BASE),bin_op)(multi1, multi2, &FN(EL,sub));
+}
+
+/* Multiply the elements of "multi" by "v" and return the result.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),scale_val)(__isl_take MULTI(BASE) *multi,
+ __isl_take isl_val *v)
+{
+ int i;
+
+ if (!multi || !v)
+ goto error;
+
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return multi;
+ }
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational factor", goto error);
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ return NULL;
+
+ for (i = 0; i < multi->n; ++i) {
+ multi->u.p[i] = FN(EL,scale_val)(multi->u.p[i],
+ isl_val_copy(v));
+ if (!multi->u.p[i])
+ goto error;
+ }
+
+ isl_val_free(v);
+ return multi;
+error:
+ isl_val_free(v);
+ return FN(MULTI(BASE),free)(multi);
+}
+
+/* Divide the elements of "multi" by "v" and return the result.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),scale_down_val)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_val *v)
+{
+ int i;
+
+ if (!multi || !v)
+ goto error;
+
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return multi;
+ }
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational factor", goto error);
+ if (isl_val_is_zero(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "cannot scale down by zero", goto error);
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ return NULL;
+
+ for (i = 0; i < multi->n; ++i) {
+ multi->u.p[i] = FN(EL,scale_down_val)(multi->u.p[i],
+ isl_val_copy(v));
+ if (!multi->u.p[i])
+ goto error;
+ }
+
+ isl_val_free(v);
+ return multi;
+error:
+ isl_val_free(v);
+ return FN(MULTI(BASE),free)(multi);
+}
+
+/* Multiply the elements of "multi" by the corresponding element of "mv"
+ * and return the result.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),scale_multi_val)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_multi_val *mv)
+{
+ int i;
+
+ if (!multi || !mv)
+ goto error;
+
+ if (!isl_space_tuple_is_equal(multi->space, isl_dim_out,
+ mv->space, isl_dim_set))
+ isl_die(isl_multi_val_get_ctx(mv), isl_error_invalid,
+ "spaces don't match", goto error);
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ goto error;
+
+ for (i = 0; i < multi->n; ++i) {
+ isl_val *v;
+
+ v = isl_multi_val_get_val(mv, i);
+ multi->u.p[i] = FN(EL,scale_val)(multi->u.p[i], v);
+ if (!multi->u.p[i])
+ goto error;
+ }
+
+ isl_multi_val_free(mv);
+ return multi;
+error:
+ isl_multi_val_free(mv);
+ return FN(MULTI(BASE),free)(multi);
+}
+
+/* Divide the elements of "multi" by the corresponding element of "mv"
+ * and return the result.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),scale_down_multi_val)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_multi_val *mv)
+{
+ int i;
+
+ if (!multi || !mv)
+ goto error;
+
+ if (!isl_space_tuple_is_equal(multi->space, isl_dim_out,
+ mv->space, isl_dim_set))
+ isl_die(isl_multi_val_get_ctx(mv), isl_error_invalid,
+ "spaces don't match", goto error);
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ return NULL;
+
+ for (i = 0; i < multi->n; ++i) {
+ isl_val *v;
+
+ v = isl_multi_val_get_val(mv, i);
+ multi->u.p[i] = FN(EL,scale_down_val)(multi->u.p[i], v);
+ if (!multi->u.p[i])
+ goto error;
+ }
+
+ isl_multi_val_free(mv);
+ return multi;
+error:
+ isl_multi_val_free(mv);
+ return FN(MULTI(BASE),free)(multi);
+}
+
+/* Compute the residues of the elements of "multi" modulo
+ * the corresponding element of "mv" and return the result.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),mod_multi_val)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_multi_val *mv)
+{
+ int i;
+
+ if (!multi || !mv)
+ goto error;
+
+ if (!isl_space_tuple_is_equal(multi->space, isl_dim_out,
+ mv->space, isl_dim_set))
+ isl_die(isl_multi_val_get_ctx(mv), isl_error_invalid,
+ "spaces don't match", goto error);
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ goto error;
+
+ for (i = 0; i < multi->n; ++i) {
+ isl_val *v;
+
+ v = isl_multi_val_get_val(mv, i);
+ multi->u.p[i] = FN(EL,mod_val)(multi->u.p[i], v);
+ if (!multi->u.p[i])
+ goto error;
+ }
+
+ isl_multi_val_free(mv);
+ return multi;
+error:
+ isl_multi_val_free(mv);
+ return FN(MULTI(BASE),free)(multi);
+}
+
+/* Return the opposite of "multi".
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),neg)(__isl_take MULTI(BASE) *multi)
+{
+ int i;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ return NULL;
+
+ for (i = 0; i < multi->n; ++i) {
+ multi->u.p[i] = FN(EL,neg)(multi->u.p[i]);
+ if (!multi->u.p[i])
+ return FN(MULTI(BASE),free)(multi);
+ }
+
+ return multi;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_bind_domain_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_bind_domain_templ.c
new file mode 100644
index 00000000000..d7eec37cad2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_bind_domain_templ.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2018 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_multi_macro.h>
+
+#undef TYPE
+#define TYPE MULTI(BASE)
+#include <isl_bind_domain_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_bind_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_bind_templ.c
new file mode 100644
index 00000000000..ce2508c4517
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_bind_templ.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+/* Bind the expressions of "multi" to parameters with identifiers
+ * specified by "tuple", living in the same space as
+ * (the target space of) "multi",
+ * returning the elements in the domain where the expressions
+ * are equal to the parameters.
+ */
+__isl_give DOM *FN(MULTI(BASE),bind)(__isl_take MULTI(BASE) *multi,
+ __isl_take isl_multi_id *tuple)
+{
+ int i;
+ isl_id *id;
+ isl_stat r;
+ isl_size n;
+ isl_space *multi_space, *tuple_space;
+ EL *el;
+ DOM *bnd;
+
+ multi_space = isl_space_range(FN(MULTI(BASE),get_space)(multi));
+ tuple_space = isl_multi_id_peek_space(tuple);
+ r = isl_space_check_equal_tuples(multi_space, tuple_space);
+ isl_space_free(multi_space);
+ if (r < 0)
+ goto error;
+ n = FN(MULTI(BASE),dim)(multi, isl_dim_set);
+ if (n < 0)
+ goto error;
+
+ if (n == 0) {
+ isl_multi_id_free(tuple);
+ return FN(MULTI(BASE),domain)(multi);
+ }
+
+ el = FN(MULTI(BASE),get_at)(multi, 0);
+ id = isl_multi_id_get_at(tuple, 0);
+ bnd = FN(EL,bind_id)(el, id);
+
+ for (i = 1; i < n; ++i) {
+ DOM *bnd_i;
+
+ el = FN(MULTI(BASE),get_at)(multi, i);
+ id = isl_multi_id_get_at(tuple, i);
+ bnd_i = FN(EL,bind_id)(el, id);
+
+ bnd_i = FN(DOM,align_params)(bnd_i, FN(DOM,get_space)(bnd));
+ bnd = FN(DOM,align_params)(bnd, FN(DOM,get_space)(bnd_i));
+ bnd = FN(DOM,intersect)(bnd, bnd_i);
+ }
+
+ FN(MULTI(BASE),free)(multi);
+ isl_multi_id_free(tuple);
+ return bnd;
+error:
+ FN(MULTI(BASE),free)(multi);
+ isl_multi_id_free(tuple);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_cmp.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_cmp.c
new file mode 100644
index 00000000000..27ab6dcb22f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_cmp.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege
+ */
+
+#include <isl_multi_macro.h>
+
+/* Compare two multi expressions.
+ *
+ * Return -1 if "multi1" is "smaller" than "multi2", 1 if "multi1" is "greater"
+ * than "multi2" and 0 if they are equal.
+ */
+int FN(MULTI(BASE),plain_cmp)(__isl_keep MULTI(BASE) *multi1,
+ __isl_keep MULTI(BASE) *multi2)
+{
+ int i;
+ int cmp;
+
+ if (multi1 == multi2)
+ return 0;
+ if (!multi1)
+ return -1;
+ if (!multi2)
+ return 1;
+
+ cmp = isl_space_cmp(multi1->space, multi2->space);
+ if (cmp != 0)
+ return cmp;
+
+ for (i = 0; i < multi1->n; ++i) {
+ cmp = FN(EL,plain_cmp)(multi1->u.p[i], multi2->u.p[i]);
+ if (cmp != 0)
+ return cmp;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_coalesce.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_coalesce.c
new file mode 100644
index 00000000000..588e107f4ca
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_coalesce.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl_multi_macro.h>
+
+/* Coalesce the elements of "multi".
+ *
+ * Note that such coalescing does not change the meaning of "multi"
+ * so there is no need to cow. We do need to be careful not to
+ * destroy any other copies of "multi" in case of failure.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),coalesce)(__isl_take MULTI(BASE) *multi)
+{
+ int i;
+
+ if (!multi)
+ return NULL;
+
+ for (i = 0; i < multi->n; ++i) {
+ EL *el = FN(EL,copy)(multi->u.p[i]);
+ el = FN(EL,coalesce)(el);
+ if (!el)
+ return FN(MULTI(BASE),free)(multi);
+ FN(EL,free)(multi->u.p[i]);
+ multi->u.p[i] = el;
+ }
+
+ return multi;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_dim_id_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_dim_id_templ.c
new file mode 100644
index 00000000000..80dd7db5878
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_dim_id_templ.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2011 Sven Verdoolaege
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+
+#include <isl_multi_macro.h>
+
+/* Return the position of the dimension of the given type and name
+ * in "multi".
+ * Return -1 if no such dimension can be found.
+ */
+int FN(MULTI(BASE),find_dim_by_name)(__isl_keep MULTI(BASE) *multi,
+ enum isl_dim_type type, const char *name)
+{
+ if (!multi)
+ return -1;
+ return isl_space_find_dim_by_name(multi->space, type, name);
+}
+
+/* Return the position of the first dimension of "type" with id "id".
+ * Return -1 if there is no such dimension.
+ */
+int FN(MULTI(BASE),find_dim_by_id)(__isl_keep MULTI(BASE) *multi,
+ enum isl_dim_type type, __isl_keep isl_id *id)
+{
+ if (!multi)
+ return -1;
+ return isl_space_find_dim_by_id(multi->space, type, id);
+}
+
+/* Return the id of the given dimension.
+ */
+__isl_give isl_id *FN(MULTI(BASE),get_dim_id)(__isl_keep MULTI(BASE) *multi,
+ enum isl_dim_type type, unsigned pos)
+{
+ return multi ? isl_space_get_dim_id(multi->space, type, pos) : NULL;
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),set_dim_name)(
+ __isl_take MULTI(BASE) *multi,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ int i;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ return NULL;
+
+ multi->space = isl_space_set_dim_name(multi->space, type, pos, s);
+ if (!multi->space)
+ return FN(MULTI(BASE),free)(multi);
+
+ if (type == isl_dim_out)
+ return multi;
+ for (i = 0; i < multi->n; ++i) {
+ multi->u.p[i] = FN(EL,set_dim_name)(multi->u.p[i],
+ type, pos, s);
+ if (!multi->u.p[i])
+ return FN(MULTI(BASE),free)(multi);
+ }
+
+ return multi;
+}
+
+/* Set the id of the given dimension of "multi" to "id".
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),set_dim_id)(
+ __isl_take MULTI(BASE) *multi,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id)
+{
+ isl_space *space;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi || !id)
+ goto error;
+
+ space = FN(MULTI(BASE),get_space)(multi);
+ space = isl_space_set_dim_id(space, type, pos, id);
+
+ return FN(MULTI(BASE),reset_space)(multi, space);
+error:
+ isl_id_free(id);
+ FN(MULTI(BASE),free)(multi);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_dims.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_dims.c
new file mode 100644
index 00000000000..a2775eab9fc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_dims.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2011 Sven Verdoolaege
+ * Copyright 2012-2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl_space_private.h>
+
+#include <isl_multi_macro.h>
+
+/* Check whether "multi" has non-zero coefficients for any dimension
+ * in the given range or if any of these dimensions appear
+ * with non-zero coefficients in any of the integer divisions involved.
+ */
+isl_bool FN(MULTI(BASE),involves_dims)(__isl_keep MULTI(BASE) *multi,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+
+ if (!multi)
+ return isl_bool_error;
+ if (n == 0)
+ return isl_bool_false;
+
+ for (i = 0; i < multi->n; ++i) {
+ isl_bool involves;
+
+ involves = FN(EL,involves_dims)(multi->u.p[i], type, first, n);
+ if (involves < 0 || involves)
+ return involves;
+ }
+
+ if (FN(MULTI(BASE),has_explicit_domain)(multi))
+ return FN(MULTI(BASE),involves_explicit_domain_dims)(multi,
+ type, first, n);
+
+ return isl_bool_false;
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),insert_dims)(
+ __isl_take MULTI(BASE) *multi,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+
+ if (!multi)
+ return NULL;
+ if (type == isl_dim_out)
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid,
+ "cannot insert output/set dimensions",
+ return FN(MULTI(BASE),free)(multi));
+ if (n == 0 && !isl_space_is_named_or_nested(multi->space, type))
+ return multi;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ return NULL;
+
+ multi->space = isl_space_insert_dims(multi->space, type, first, n);
+ if (!multi->space)
+ return FN(MULTI(BASE),free)(multi);
+ if (FN(MULTI(BASE),has_explicit_domain)(multi))
+ multi = FN(MULTI(BASE),insert_explicit_domain_dims)(multi,
+ type, first, n);
+ if (!multi)
+ return NULL;
+
+ for (i = 0; i < multi->n; ++i) {
+ multi->u.p[i] = FN(EL,insert_dims)(multi->u.p[i],
+ type, first, n);
+ if (!multi->u.p[i])
+ return FN(MULTI(BASE),free)(multi);
+ }
+
+ return multi;
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),add_dims)(__isl_take MULTI(BASE) *multi,
+ enum isl_dim_type type, unsigned n)
+{
+ isl_size pos;
+
+ pos = FN(MULTI(BASE),dim)(multi, type);
+ if (pos < 0)
+ return FN(MULTI(BASE),free)(multi);
+
+ return FN(MULTI(BASE),insert_dims)(multi, type, pos, n);
+}
+
+/* Project the domain of "multi" onto its parameter space.
+ * "multi" may not involve any of the domain dimensions.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),project_domain_on_params)(
+ __isl_take MULTI(BASE) *multi)
+{
+ isl_size n;
+ isl_bool involves;
+ isl_space *space;
+
+ n = FN(MULTI(BASE),dim)(multi, isl_dim_in);
+ if (n < 0)
+ return FN(MULTI(BASE),free)(multi);
+ involves = FN(MULTI(BASE),involves_dims)(multi, isl_dim_in, 0, n);
+ if (involves < 0)
+ return FN(MULTI(BASE),free)(multi);
+ if (involves)
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid,
+ "expression involves some of the domain dimensions",
+ return FN(MULTI(BASE),free)(multi));
+ multi = FN(MULTI(BASE),drop_dims)(multi, isl_dim_in, 0, n);
+ space = FN(MULTI(BASE),get_domain_space)(multi);
+ space = isl_space_params(space);
+ multi = FN(MULTI(BASE),reset_domain_space)(multi, space);
+ return multi;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_domain_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_domain_templ.c
new file mode 100644
index 00000000000..6aa97fe3201
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_domain_templ.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/set.h>
+
+#include <isl_multi_macro.h>
+
+/* Return the shared domain of the elements of "multi".
+ *
+ * If "multi" has an explicit domain, then return this domain.
+ */
+__isl_give isl_set *FN(MULTI(BASE),domain)(__isl_take MULTI(BASE) *multi)
+{
+ int i;
+ isl_set *dom;
+
+ if (!multi)
+ return NULL;
+
+ if (FN(MULTI(BASE),has_explicit_domain)(multi)) {
+ dom = FN(MULTI(BASE),get_explicit_domain)(multi);
+ FN(MULTI(BASE),free)(multi);
+ return dom;
+ }
+
+ dom = isl_set_universe(FN(MULTI(BASE),get_domain_space)(multi));
+ for (i = 0; i < multi->n; ++i) {
+ isl_set *dom_i;
+
+ dom_i = FN(EL,domain)(FN(FN(MULTI(BASE),get),BASE)(multi, i));
+ dom = isl_set_intersect(dom, dom_i);
+ }
+
+ FN(MULTI(BASE),free)(multi);
+ return dom;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_explicit_domain.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_explicit_domain.c
new file mode 100644
index 00000000000..bb65823835f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_explicit_domain.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+/* These versions of the explicit domain functions are used
+ * when the multi expression may have an explicit domain.
+ */
+
+#include <isl_multi_macro.h>
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),cow)(__isl_take MULTI(BASE) *multi);
+
+/* Does "multi" have an explicit domain?
+ *
+ * An explicit domain is only available if "multi" is zero-dimensional.
+ */
+static int FN(MULTI(BASE),has_explicit_domain)(__isl_keep MULTI(BASE) *multi)
+{
+ return multi && multi->n == 0;
+}
+
+/* Check that "multi" has an explicit domain.
+ */
+static isl_stat FN(MULTI(BASE),check_has_explicit_domain)(
+ __isl_keep MULTI(BASE) *multi)
+{
+ if (!multi)
+ return isl_stat_error;
+ if (!FN(MULTI(BASE),has_explicit_domain)(multi))
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_internal,
+ "expression does not have an explicit domain",
+ return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Return the explicit domain of "multi", assuming it has one.
+ */
+static __isl_keep DOM *FN(MULTI(BASE),peek_explicit_domain)(
+ __isl_keep MULTI(BASE) *multi)
+{
+ if (FN(MULTI(BASE),check_has_explicit_domain)(multi) < 0)
+ return NULL;
+ return multi->u.dom;
+}
+
+/* Return a copy of the explicit domain of "multi", assuming it has one.
+ */
+static __isl_give DOM *FN(MULTI(BASE),get_explicit_domain)(
+ __isl_keep MULTI(BASE) *multi)
+{
+ return FN(DOM,copy)(FN(MULTI(BASE),peek_explicit_domain)(multi));
+}
+
+/* Replace the explicit domain of "multi" by "dom", assuming it has one.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),set_explicit_domain)(
+ __isl_take MULTI(BASE) *multi, __isl_take DOM *dom)
+{
+ if (FN(MULTI(BASE),check_has_explicit_domain)(multi) < 0)
+ goto error;
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi || !dom)
+ goto error;
+ FN(DOM,free)(multi->u.dom);
+ multi->u.dom = dom;
+ if (!multi->u.dom)
+ return FN(MULTI(BASE),free)(multi);
+ return multi;
+error:
+ FN(MULTI(BASE),free)(multi);
+ FN(DOM,free)(dom);
+ return NULL;
+}
+
+/* Intersect the domain of "dst" with the explicit domain of "src".
+ *
+ * In the case of isl_multi_union_pw_aff objects, the explicit domain
+ * of "src" is allowed to have only constraints on the parameters, even
+ * if the domain of "dst" contains actual domain elements. In this case,
+ * the domain of "dst" is intersected with those parameter constraints.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),intersect_explicit_domain)(
+ __isl_take MULTI(BASE) *dst, __isl_keep MULTI(BASE) *src)
+{
+ isl_bool is_params;
+ DOM *dom;
+
+ dom = FN(MULTI(BASE),peek_explicit_domain)(src);
+ is_params = FN(DOM,is_params)(dom);
+ if (is_params < 0)
+ return FN(MULTI(BASE),free)(dst);
+
+ dom = FN(DOM,copy)(dom);
+ if (!is_params) {
+ dst = FN(MULTI(BASE),intersect_domain)(dst, dom);
+ } else {
+ isl_set *params;
+
+ params = FN(DOM,params)(dom);
+ dst = FN(MULTI(BASE),intersect_params)(dst, params);
+ }
+
+ return dst;
+}
+
+/* Set the explicit domain of "dst" to that of "src".
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),copy_explicit_domain)(
+ __isl_take MULTI(BASE) *dst, __isl_keep MULTI(BASE) *src)
+{
+ DOM *dom;
+
+ dom = FN(MULTI(BASE),get_explicit_domain)(src);
+ dst = FN(MULTI(BASE),set_explicit_domain)(dst, dom);
+
+ return dst;
+}
+
+/* Align the parameters of the explicit domain of "multi" to those of "space".
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),align_explicit_domain_params)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_space *space)
+{
+ DOM *dom;
+
+ dom = FN(MULTI(BASE),get_explicit_domain)(multi);
+ dom = FN(DOM,align_params)(dom, space);
+ multi = FN(MULTI(BASE),set_explicit_domain)(multi, dom);
+
+ return multi;
+}
+
+/* Replace the space of the explicit domain of "multi" by "space",
+ * without modifying its dimension.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),reset_explicit_domain_space)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_space *space)
+{
+ DOM *dom;
+
+ dom = FN(MULTI(BASE),get_explicit_domain)(multi);
+ dom = FN(DOM,reset_equal_dim_space)(dom, space);
+ multi = FN(MULTI(BASE),set_explicit_domain)(multi, dom);
+
+ return multi;
+}
+
+/* Free the explicit domain of "multi".
+ */
+static void FN(MULTI(BASE),free_explicit_domain)(__isl_keep MULTI(BASE) *multi)
+{
+ if (FN(MULTI(BASE),check_has_explicit_domain)(multi) < 0)
+ return;
+ FN(DOM,free)(multi->u.dom);
+}
+
+/* Do "multi1" and "multi2" have the same explicit domain?
+ */
+static isl_bool FN(MULTI(BASE),equal_explicit_domain)(
+ __isl_keep MULTI(BASE) *multi1, __isl_keep MULTI(BASE) *multi2)
+{
+ DOM *dom1, *dom2;
+ isl_bool equal;
+
+ if (FN(MULTI(BASE),check_has_explicit_domain)(multi1) < 0 ||
+ FN(MULTI(BASE),check_has_explicit_domain)(multi2) < 0)
+ return isl_bool_error;
+ dom1 = FN(MULTI(BASE),get_explicit_domain)(multi1);
+ dom2 = FN(MULTI(BASE),get_explicit_domain)(multi2);
+ equal = FN(DOM,is_equal)(dom1, dom2);
+ FN(DOM,free)(dom1);
+ FN(DOM,free)(dom2);
+
+ return equal;
+}
+
+static isl_stat FN(MULTI(BASE),check_explicit_domain)(
+ __isl_keep MULTI(BASE) *multi) __attribute__ ((unused));
+
+/* Debugging function to check that the explicit domain of "multi"
+ * has the correct space.
+ */
+isl_stat FN(MULTI(BASE),check_explicit_domain)(__isl_keep MULTI(BASE) *multi)
+{
+ isl_space *space1, *space2;
+ isl_bool equal;
+
+ if (FN(MULTI(BASE),check_has_explicit_domain)(multi) < 0)
+ return isl_stat_error;
+ space1 = isl_space_domain(isl_space_copy(multi->space));
+ space2 = FN(DOM,get_space)(multi->u.dom);
+ equal = isl_space_is_equal(space1, space2);
+ isl_space_free(space1);
+ isl_space_free(space2);
+ if (equal < 0)
+ return isl_stat_error;
+ if (!equal)
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_internal,
+ "check failed", return isl_stat_error);
+ return isl_stat_ok;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_floor.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_floor.c
new file mode 100644
index 00000000000..b9a88988312
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_floor.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl_multi_macro.h>
+
+/* Given f, return floor(f).
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),floor)(__isl_take MULTI(BASE) *multi)
+{
+ int i;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ return NULL;
+
+ for (i = 0; i < multi->n; ++i) {
+ multi->u.p[i] = FN(EL,floor)(multi->u.p[i]);
+ if (!multi->u.p[i])
+ return FN(MULTI(BASE),free)(multi);
+ }
+
+ return multi;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_from_base_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_from_base_templ.c
new file mode 100644
index 00000000000..2a2a6b1ecfe
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_from_base_templ.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012,2014 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+
+#include <isl_multi_macro.h>
+
+/* Create a multiple expression with a single output/set dimension
+ * equal to "el".
+ * For most multiple expression types, the base type has a single
+ * output/set dimension and the space of the result is therefore
+ * the same as the space of the input.
+ * In the case of isl_multi_union_pw_aff, however, the base type
+ * lives in a parameter space and we therefore need to add
+ * a single set dimension.
+ */
+__isl_give MULTI(BASE) *FN(FN(MULTI(BASE),from),BASE)(__isl_take EL *el)
+{
+ isl_space *space;
+ MULTI(BASE) *multi;
+
+ space = FN(EL,get_space(el));
+ if (isl_space_is_params(space)) {
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, 1);
+ }
+ multi = FN(MULTI(BASE),alloc)(space);
+ multi = FN(FN(MULTI(BASE),set),BASE)(multi, 0, el);
+
+ return multi;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_gist.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_gist.c
new file mode 100644
index 00000000000..d43525f8720
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_gist.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 Sven Verdoolaege
+ * Copyright 2012-2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <isl_multi_macro.h>
+
+/* Compute the gist of "multi" with respect to the domain constraints
+ * of "context".
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),gist)(__isl_take MULTI(BASE) *multi,
+ __isl_take DOM *context)
+{
+ return FN(FN(MULTI(BASE),apply),DOMBASE)(multi, context, &FN(EL,gist));
+}
+
+/* Compute the gist of "multi" with respect to the parameter constraints
+ * of "context".
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),gist_params)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_set *context)
+{
+ return FN(MULTI(BASE),apply_set)(multi, context, &FN(EL,gist_params));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_hash.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_hash.c
new file mode 100644
index 00000000000..0c7ebdf7e56
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_hash.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege
+ */
+
+#include <isl_multi_macro.h>
+#include <isl/hash.h>
+
+/* Return a hash value that digests "multi".
+ */
+uint32_t FN(MULTI(BASE),get_hash)(__isl_keep MULTI(BASE) *multi)
+{
+ int i;
+ uint32_t hash;
+
+ if (!multi)
+ return 0;
+
+ hash = isl_hash_init();
+ for (i = 0; i < multi->n; ++i) {
+ uint32_t el_hash;
+ el_hash = FN(EL,get_hash)(multi->u.p[i]);
+ isl_hash_hash(hash, el_hash);
+ }
+
+ return hash;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_identity_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_identity_templ.c
new file mode 100644
index 00000000000..1ffeadf57a4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_identity_templ.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2012 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+#include <isl/local_space.h>
+
+#include <isl_multi_macro.h>
+
+/* Create a multi expression in the given space that maps each
+ * input dimension to the corresponding output dimension.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),identity)(__isl_take isl_space *space)
+{
+ int i;
+ isl_size n_in, n_out;
+ isl_local_space *ls;
+ MULTI(BASE) *multi;
+
+ if (!space)
+ return NULL;
+
+ if (isl_space_is_set(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "expecting map space", goto error);
+
+ n_in = isl_space_dim(space, isl_dim_in);
+ n_out = isl_space_dim(space, isl_dim_out);
+ if (n_in < 0 || n_out < 0)
+ goto error;
+ if (n_in != n_out)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "number of input and output dimensions needs to be "
+ "the same", goto error);
+
+ multi = FN(MULTI(BASE),alloc)(isl_space_copy(space));
+
+ if (!n_out) {
+ isl_space_free(space);
+ return multi;
+ }
+
+ space = isl_space_domain(space);
+ ls = isl_local_space_from_space(space);
+
+ for (i = 0; i < n_out; ++i) {
+ EL *el;
+ el = FN(EL,var_on_domain)(isl_local_space_copy(ls),
+ isl_dim_set, i);
+ multi = FN(FN(MULTI(BASE),set),BASE)(multi, i, el);
+ }
+
+ isl_local_space_free(ls);
+
+ return multi;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Create a multi expression that maps elements in the given space
+ * to themselves.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),identity_on_domain_space)(
+ __isl_take isl_space *space)
+{
+ return FN(MULTI(BASE),identity)(isl_space_map_from_set(space));
+}
+
+/* This function performs the same operation as
+ * isl_multi_*_identity_on_domain_space,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give MULTI(BASE) *FN(FN(isl_space_identity_multi,BASE),on_domain)(
+ __isl_take isl_space *space)
+{
+ return FN(MULTI(BASE),identity_on_domain_space)(space);
+}
+
+/* Create a multi expression in the same space as "multi" that maps each
+ * input dimension to the corresponding output dimension.
+ */
+__isl_give MULTI(BASE) *FN(FN(MULTI(BASE),identity_multi),BASE)(
+ __isl_take MULTI(BASE) *multi)
+{
+ isl_space *space;
+
+ space = FN(MULTI(BASE),get_space)(multi);
+ FN(MULTI(BASE),free)(multi);
+ return FN(MULTI(BASE),identity)(space);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_insert_domain_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_insert_domain_templ.c
new file mode 100644
index 00000000000..e4e403cd3ab
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_insert_domain_templ.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_multi_macro.h>
+
+#undef TYPE
+#define TYPE MULTI(BASE)
+#include <isl_insert_domain_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_intersect.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_intersect.c
new file mode 100644
index 00000000000..a147ec8e323
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_intersect.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2011 Sven Verdoolaege
+ * Copyright 2012-2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <isl_multi_macro.h>
+
+/* Does the space of "domain" correspond to that of the domain of "multi"?
+ * The parameters do not need to be aligned.
+ */
+static isl_bool FN(MULTI(BASE),compatible_domain)(
+ __isl_keep MULTI(BASE) *multi, __isl_keep DOM *domain)
+{
+ isl_bool ok;
+ isl_space *space, *domain_space;
+
+ domain_space = FN(DOM,get_space)(domain);
+ space = FN(MULTI(BASE),get_space)(multi);
+ ok = isl_space_has_domain_tuples(domain_space, space);
+ isl_space_free(space);
+ isl_space_free(domain_space);
+
+ return ok;
+}
+
+/* Check that the space of "domain" corresponds to
+ * that of the domain of "multi", ignoring parameters.
+ */
+static isl_stat FN(MULTI(BASE),check_compatible_domain)(
+ __isl_keep MULTI(BASE) *multi, __isl_keep DOM *domain)
+{
+ isl_bool ok;
+
+ ok = FN(MULTI(BASE),compatible_domain)(multi, domain);
+ if (ok < 0)
+ return isl_stat_error;
+ if (!ok)
+ isl_die(FN(DOM,get_ctx)(domain), isl_error_invalid,
+ "incompatible spaces", return isl_stat_error);
+
+ return isl_stat_ok;
+}
+
+/* Intersect the explicit domain of "multi" with "domain".
+ *
+ * The parameters of "multi" and "domain" are assumed to have been aligned.
+ *
+ * In the case of an isl_multi_union_pw_aff object, the explicit domain
+ * is allowed to have only constraints on the parameters, while
+ * "domain" contains actual domain elements. In this case,
+ * "domain" is intersected with those parameter constraints and
+ * then used as the explicit domain of "multi".
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),domain_intersect_aligned)(
+ __isl_take MULTI(BASE) *multi, __isl_take DOM *domain)
+{
+ isl_bool is_params;
+ DOM *multi_dom;
+
+ if (FN(MULTI(BASE),check_compatible_domain)(multi, domain) < 0)
+ goto error;
+ if (FN(MULTI(BASE),check_has_explicit_domain)(multi) < 0)
+ goto error;
+ is_params = FN(DOM,is_params)(multi->u.dom);
+ if (is_params < 0)
+ goto error;
+ multi_dom = FN(MULTI(BASE),get_explicit_domain)(multi);
+ if (!is_params) {
+ domain = FN(DOM,intersect)(multi_dom, domain);
+ } else {
+ isl_set *params;
+
+ params = FN(DOM,params)(multi_dom);
+ domain = FN(DOM,intersect_params)(domain, params);
+ }
+ multi = FN(MULTI(BASE),set_explicit_domain)(multi, domain);
+ return multi;
+error:
+ FN(MULTI(BASE),free)(multi);
+ FN(DOM,free)(domain);
+ return NULL;
+}
+
+/* Intersect the explicit domain of "multi" with "domain".
+ * First align the parameters, if needed.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),domain_intersect)(
+ __isl_take MULTI(BASE) *multi, __isl_take DOM *domain)
+{
+ return FN(FN(MULTI(BASE),align_params),DOMBASE)(multi, domain,
+ FN(MULTI(BASE),domain_intersect_aligned));
+}
+
+/* Intersect the domain of "multi" with "domain".
+ *
+ * If "multi" has an explicit domain, then only this domain
+ * needs to be intersected.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),intersect_domain)(
+ __isl_take MULTI(BASE) *multi, __isl_take DOM *domain)
+{
+ if (FN(MULTI(BASE),has_explicit_domain)(multi))
+ return FN(MULTI(BASE),domain_intersect)(multi, domain);
+ return FN(FN(MULTI(BASE),apply),DOMBASE)(multi, domain,
+ &FN(EL,intersect_domain));
+}
+
+/* Intersect the parameter domain of the explicit domain of "multi"
+ * with "domain".
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),domain_intersect_params_aligned)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_set *domain)
+{
+ DOM *multi_dom;
+
+ multi_dom = FN(MULTI(BASE),get_explicit_domain)(multi);
+ multi_dom = FN(DOM,intersect_params)(multi_dom, domain);
+ multi = FN(MULTI(BASE),set_explicit_domain)(multi, multi_dom);
+
+ return multi;
+}
+
+/* Intersect the parameter domain of the explicit domain of "multi"
+ * with "domain".
+ * First align the parameters, if needed.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),domain_intersect_params)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_set *domain)
+{
+ return FN(FN(MULTI(BASE),align_params),set)(multi, domain,
+ FN(MULTI(BASE),domain_intersect_params_aligned));
+}
+
+/* Intersect the parameter domain of "multi" with "domain".
+ *
+ * If "multi" has an explicit domain, then only this domain
+ * needs to be intersected.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),intersect_params)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_set *domain)
+{
+ if (FN(MULTI(BASE),has_explicit_domain)(multi))
+ return FN(MULTI(BASE),domain_intersect_params)(multi, domain);
+ return FN(MULTI(BASE),apply_set)(multi, domain,
+ &FN(EL,intersect_params));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_locals_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_locals_templ.c
new file mode 100644
index 00000000000..3cfb0a33c35
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_locals_templ.c
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_multi_macro.h>
+
+/* Does "multi" involve any local variables?
+ */
+isl_bool FN(MULTI(BASE),involves_locals)(__isl_keep MULTI(BASE) *multi)
+{
+ return FN(MULTI(BASE),any)(multi, FN(EL,involves_locals));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_macro.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_macro.h
new file mode 100644
index 00000000000..394494b06e6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_macro.h
@@ -0,0 +1,8 @@
+#undef EL_BASE
+#define EL_BASE BASE
+#include <isl_list_macro.h>
+
+#define xMULTI(BASE) isl_multi_ ## BASE
+#define MULTI(BASE) xMULTI(BASE)
+#undef DOM
+#define DOM CAT(isl_,DOMBASE)
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_min_max_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_min_max_templ.c
new file mode 100644
index 00000000000..5a1959290e9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_min_max_templ.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+/* Return the (elementwise) minimum of "multi1" and "multi2".
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),min)(__isl_take MULTI(BASE) *multi1,
+ __isl_take MULTI(BASE) *multi2)
+{
+ return FN(MULTI(BASE),bin_op)(multi1, multi2, &FN(EL,min));
+}
+
+/* Return the (elementwise) maximum of "multi1" and "multi2".
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),max)(__isl_take MULTI(BASE) *multi1,
+ __isl_take MULTI(BASE) *multi2)
+{
+ return FN(MULTI(BASE),bin_op)(multi1, multi2, &FN(EL,max));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_move_dims_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_move_dims_templ.c
new file mode 100644
index 00000000000..9c880857948
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_move_dims_templ.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+
+#include <isl_multi_macro.h>
+
+/* Move the "n" dimensions of "src_type" starting at "src_pos" of "multi"
+ * to dimensions of "dst_type" at "dst_pos".
+ *
+ * We only support moving input dimensions to parameters and vice versa.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),move_dims)(__isl_take MULTI(BASE) *multi,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ int i;
+
+ if (!multi)
+ return NULL;
+
+ if (n == 0 &&
+ !isl_space_is_named_or_nested(multi->space, src_type) &&
+ !isl_space_is_named_or_nested(multi->space, dst_type))
+ return multi;
+
+ if (dst_type == isl_dim_out || src_type == isl_dim_out)
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid,
+ "cannot move output/set dimension",
+ return FN(MULTI(BASE),free)(multi));
+ if (dst_type == isl_dim_div || src_type == isl_dim_div)
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid,
+ "cannot move divs",
+ return FN(MULTI(BASE),free)(multi));
+ if (FN(MULTI(BASE),check_range)(multi, src_type, src_pos, n) < 0)
+ return FN(MULTI(BASE),free)(multi);
+ if (dst_type == src_type)
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_unsupported,
+ "moving dims within the same type not supported",
+ return FN(MULTI(BASE),free)(multi));
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ return NULL;
+
+ multi->space = isl_space_move_dims(multi->space, dst_type, dst_pos,
+ src_type, src_pos, n);
+ if (!multi->space)
+ return FN(MULTI(BASE),free)(multi);
+ if (FN(MULTI(BASE),has_explicit_domain)(multi))
+ multi = FN(MULTI(BASE),move_explicit_domain_dims)(multi,
+ dst_type, dst_pos, src_type, src_pos, n);
+ if (!multi)
+ return NULL;
+
+ for (i = 0; i < multi->n; ++i) {
+ multi->u.p[i] = FN(EL,move_dims)(multi->u.p[i],
+ dst_type, dst_pos,
+ src_type, src_pos, n);
+ if (!multi->u.p[i])
+ return FN(MULTI(BASE),free)(multi);
+ }
+
+ return multi;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_nan_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_nan_templ.c
new file mode 100644
index 00000000000..2ea73a8140b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_nan_templ.c
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2014 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl_multi_macro.h>
+
+/* Does "multi" involve any NaNs?
+ */
+isl_bool FN(MULTI(BASE),involves_nan)(__isl_keep MULTI(BASE) *multi)
+{
+ return FN(MULTI(BASE),any)(multi, &FN(EL,involves_nan));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_no_domain_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_no_domain_templ.c
new file mode 100644
index 00000000000..1c98ac45245
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_no_domain_templ.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+#include <isl/local_space.h>
+#include <isl_reordering.h>
+
+#include <isl_multi_macro.h>
+
+/* The functions in this file are meant for base object types
+ * that do not have any associated space. They are only meant to be used
+ * in the generic isl_multi_* functions which have to deal with base objects
+ * that do have an associated space.
+ */
+
+
+/* Drop the "n" first dimensions of type "type" at position "first".
+ *
+ * For a base expression without an associated space, this function
+ * does not do anything.
+ */
+static __isl_give EL *FN(EL,drop_dims)(__isl_take EL *el,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return el;
+}
+
+/* Return the space of "el".
+ *
+ * For a base expression without an associated space,
+ * the conditions surrounding the call to this function make sure
+ * that this function will never actually get called. We return a valid
+ * space anyway, just in case.
+ */
+static __isl_give isl_space *FN(EL,get_space)(__isl_keep EL *el)
+{
+ if (!el)
+ return NULL;
+
+ return isl_space_params_alloc(FN(EL,get_ctx)(el), 0);
+}
+
+/* Reset the domain space of "el" to "space".
+ *
+ * For a base expression without an associated space, this function
+ * does not do anything, apart from error handling and cleaning up memory.
+ */
+static __isl_give EL *FN(EL,reset_domain_space)(__isl_take EL *el,
+ __isl_take isl_space *space)
+{
+ if (!space)
+ return FN(EL,free)(el);
+ isl_space_free(space);
+ return el;
+}
+
+/* Align the parameters of "el" to those of "space".
+ *
+ * For a base expression without an associated space, this function
+ * does not do anything, apart from error handling and cleaning up memory.
+ * Note that the conditions surrounding the call to this function make sure
+ * that this function will never actually get called.
+ */
+static __isl_give EL *FN(EL,align_params)(__isl_take EL *el,
+ __isl_take isl_space *space)
+{
+ if (!space)
+ return FN(EL,free)(el);
+ isl_space_free(space);
+ return el;
+}
+
+/* Reorder the dimensions of the domain of "el" according
+ * to the given reordering.
+ *
+ * For a base expression without an associated space, this function
+ * does not do anything, apart from error handling and cleaning up memory.
+ */
+static __isl_give EL *FN(EL,realign_domain)(__isl_take EL *el,
+ __isl_take isl_reordering *r)
+{
+ if (!r)
+ return FN(EL,free)(el);
+ isl_reordering_free(r);
+ return el;
+}
+
+/* Do the parameters of "el" match those of "space"?
+ *
+ * For a base expression without an associated space, this function
+ * simply returns true, except if "el" or "space" are NULL.
+ */
+static isl_bool FN(EL,matching_params)(__isl_keep EL *el,
+ __isl_keep isl_space *space)
+{
+ if (!el || !space)
+ return isl_bool_error;
+ return isl_bool_true;
+}
+
+/* Check that the domain space of "el" matches "space".
+ *
+ * For a base expression without an associated space, this function
+ * simply returns isl_stat_ok, except if "el" or "space" are NULL.
+ */
+static isl_stat FN(EL,check_match_domain_space)(__isl_keep EL *el,
+ __isl_keep isl_space *space)
+{
+ if (!el || !space)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_no_explicit_domain.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_no_explicit_domain.c
new file mode 100644
index 00000000000..cba4387ab58
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_no_explicit_domain.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+/* These versions of the explicit domain functions are used
+ * when the multi expression cannot have an explicit domain.
+ */
+
+#include <isl/space.h>
+
+#include <isl_multi_macro.h>
+
+/* Does "multi" have an explicit domain?
+ *
+ * No.
+ */
+static int FN(MULTI(BASE),has_explicit_domain)(__isl_keep MULTI(BASE) *multi)
+{
+ return 0;
+}
+
+/* Initialize the explicit domain of "multi".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),init_explicit_domain)(
+ __isl_take MULTI(BASE) *multi)
+{
+ return multi;
+}
+
+/* Intersect the domain of "dst" with the explicit domain of "src".
+ * "src" cannot have an explicit domain, so this function is never called.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),intersect_explicit_domain)(
+ __isl_take MULTI(BASE) *dst, __isl_keep MULTI(BASE) *src)
+{
+ return dst;
+}
+
+/* Set the explicit domain of "dst" to that of "src".
+ * "src" and "dst" cannot have an explicit domain,
+ * so this function is never called.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),copy_explicit_domain)(
+ __isl_take MULTI(BASE) *dst, __isl_keep MULTI(BASE) *src)
+{
+ return dst;
+}
+
+/* Only used by multi-expressions that include "isl_multi_product_templ.c".
+ */
+static __isl_give MULTI(BASE) *
+FN(MULTI(BASE),intersect_explicit_domain_product)(
+ __isl_take MULTI(BASE) *dst, __isl_keep MULTI(BASE) *src1,
+ __isl_keep MULTI(BASE) *src2) __attribute__ ((unused));
+
+/* Intersect the domain of "dst" with the domain product
+ * of the explicit domains of "src1" and "src2".
+ * This function is only called if at least one of "src1" or "src2"
+ * has an explicit domain.
+ * "src1", "src2" and "dst" cannot have an explicit domain,
+ * so this function is never called.
+ */
+static __isl_give MULTI(BASE) *
+FN(MULTI(BASE),intersect_explicit_domain_product)(
+ __isl_take MULTI(BASE) *dst, __isl_keep MULTI(BASE) *src1,
+ __isl_keep MULTI(BASE) *src2)
+{
+ return dst;
+}
+
+/* Align the parameters of the explicit domain of "multi" to those of "space".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),align_explicit_domain_params)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_space *space)
+{
+ isl_space_free(space);
+ return multi;
+}
+
+/* Replace the space of the explicit domain of "multi" by "space",
+ * without modifying its dimension.
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),reset_explicit_domain_space)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_space *space)
+{
+ isl_space_free(space);
+ return multi;
+}
+
+/* Check whether the explicit domain of "multi" has non-zero coefficients
+ * for any dimension in the given range or if any of these dimensions appear
+ * with non-zero coefficients in any of the integer divisions involved.
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+isl_bool FN(MULTI(BASE),involves_explicit_domain_dims)(
+ __isl_keep MULTI(BASE) *multi,
+ enum isl_dim_type type, unsigned pos, unsigned n)
+{
+ return isl_bool_false;
+}
+
+/* Insert "n" dimensions of type "type" at position "pos"
+ * of the explicit domain of "multi".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),insert_explicit_domain_dims)(
+ __isl_take MULTI(BASE) *multi,
+ enum isl_dim_type type, unsigned pos, unsigned n)
+{
+ return multi;
+}
+
+/* Drop the "n" dimensions of type "type" starting at position "pos"
+ * of the explicit domain of "multi".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),drop_explicit_domain_dims)(
+ __isl_take MULTI(BASE) *multi,
+ enum isl_dim_type type, unsigned pos, unsigned n)
+{
+ return multi;
+}
+
+/* Move the "n" dimensions of "src_type" starting at "src_pos" of
+ * of the explicit domain of "multi" to dimensions of "dst_type" at "dst_pos".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),move_explicit_domain_dims)(
+ __isl_take MULTI(BASE) *multi,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ return multi;
+}
+
+/* Free the explicit domain of "multi".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+static void FN(MULTI(BASE),free_explicit_domain)(__isl_keep MULTI(BASE) *multi)
+{
+}
+
+/* Do "multi1" and "multi2" have the same explicit domain?
+ * "multi1" and "multi2" cannot have an explicit domain,
+ * so this function is never called.
+ */
+static isl_bool FN(MULTI(BASE),equal_explicit_domain)(
+ __isl_keep MULTI(BASE) *multi1, __isl_keep MULTI(BASE) *multi2)
+{
+ return isl_bool_true;
+}
+
+static isl_stat FN(MULTI(BASE),check_explicit_domain)(
+ __isl_keep MULTI(BASE) *multi) __attribute__ ((unused));
+
+/* Debugging function to check that the explicit domain of "multi"
+ * has the correct space.
+ * "multi" cannot have an explicit domain,
+ * so this function should never be called.
+ */
+static isl_stat FN(MULTI(BASE),check_explicit_domain)(
+ __isl_keep MULTI(BASE) *multi)
+{
+ return isl_stat_ok;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_param_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_param_templ.c
new file mode 100644
index 00000000000..eab93548933
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_param_templ.c
@@ -0,0 +1,60 @@
+/*
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege
+ */
+
+#include <isl_multi_macro.h>
+
+/* Does the multiple expression "multi" depend in any way
+ * on the parameter with identifier "id"?
+ */
+isl_bool FN(MULTI(BASE),involves_param_id)(__isl_keep MULTI(BASE) *multi,
+ __isl_keep isl_id *id)
+{
+ int i;
+ int pos;
+
+ if (!multi || !id)
+ return isl_bool_error;
+ if (multi->n == 0)
+ return isl_bool_false;
+ pos = FN(MULTI(BASE),find_dim_by_id)(multi, isl_dim_param, id);
+ if (pos < 0)
+ return isl_bool_false;
+
+ for (i = 0; i < multi->n; ++i) {
+ isl_bool involved = FN(EL,involves_param_id)(multi->u.p[i], id);
+ if (involved < 0 || involved)
+ return involved;
+ }
+
+ return isl_bool_false;
+}
+
+/* Does the multiple expression "multi" depend in any way
+ * on any of the parameters with identifiers in "list"?
+ */
+isl_bool FN(MULTI(BASE),involves_param_id_list)(__isl_keep MULTI(BASE) *multi,
+ __isl_keep isl_id_list *list)
+{
+ int i;
+ isl_size n;
+
+ n = isl_id_list_size(list);
+ if (n < 0)
+ return isl_bool_error;
+ for (i = 0; i < n; ++i) {
+ isl_bool involves;
+ isl_id *id;
+
+ id = isl_id_list_get_at(list, i);
+ involves = FN(MULTI(BASE),involves_param_id)(multi, id);
+ isl_id_free(id);
+
+ if (involves < 0 || involves)
+ return involves;
+ }
+
+ return isl_bool_false;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_product_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_product_templ.c
new file mode 100644
index 00000000000..5640326fd82
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_product_templ.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+
+#include <isl_multi_macro.h>
+
+/* Given two MULTI(BASE)s A -> B and C -> D,
+ * construct a MULTI(BASE) [A -> C] -> [B -> D].
+ *
+ * If "multi1" and/or "multi2" has an explicit domain, then
+ * intersect the domain of the result with these explicit domains.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),product)(
+ __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2)
+{
+ int i;
+ EL *el;
+ isl_space *space;
+ MULTI(BASE) *res;
+ isl_size in1, in2, out1, out2;
+
+ FN(MULTI(BASE),align_params_bin)(&multi1, &multi2);
+ in1 = FN(MULTI(BASE),dim)(multi1, isl_dim_in);
+ in2 = FN(MULTI(BASE),dim)(multi2, isl_dim_in);
+ out1 = FN(MULTI(BASE),dim)(multi1, isl_dim_out);
+ out2 = FN(MULTI(BASE),dim)(multi2, isl_dim_out);
+ if (in1 < 0 || in2 < 0 || out1 < 0 || out2 < 0)
+ goto error;
+ space = isl_space_product(FN(MULTI(BASE),get_space)(multi1),
+ FN(MULTI(BASE),get_space)(multi2));
+ res = FN(MULTI(BASE),alloc)(isl_space_copy(space));
+ space = isl_space_domain(space);
+
+ for (i = 0; i < out1; ++i) {
+ el = FN(FN(MULTI(BASE),get),BASE)(multi1, i);
+ el = FN(EL,insert_dims)(el, isl_dim_in, in1, in2);
+ el = FN(EL,reset_domain_space)(el, isl_space_copy(space));
+ res = FN(FN(MULTI(BASE),set),BASE)(res, i, el);
+ }
+
+ for (i = 0; i < out2; ++i) {
+ el = FN(FN(MULTI(BASE),get),BASE)(multi2, i);
+ el = FN(EL,insert_dims)(el, isl_dim_in, 0, in1);
+ el = FN(EL,reset_domain_space)(el, isl_space_copy(space));
+ res = FN(FN(MULTI(BASE),set),BASE)(res, out1 + i, el);
+ }
+
+ if (FN(MULTI(BASE),has_explicit_domain)(multi1) ||
+ FN(MULTI(BASE),has_explicit_domain)(multi2))
+ res = FN(MULTI(BASE),intersect_explicit_domain_product)(res,
+ multi1, multi2);
+
+ isl_space_free(space);
+ FN(MULTI(BASE),free)(multi1);
+ FN(MULTI(BASE),free)(multi2);
+ return res;
+error:
+ FN(MULTI(BASE),free)(multi1);
+ FN(MULTI(BASE),free)(multi2);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_pw_aff_explicit_domain.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_pw_aff_explicit_domain.c
new file mode 100644
index 00000000000..e9efea0bd00
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_pw_aff_explicit_domain.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+/* Initialize the explicit domain of "mpa".
+ *
+ * The explicit domain is initialized to a universe set
+ * in the domain space.
+ */
+static __isl_give isl_multi_pw_aff *isl_multi_pw_aff_init_explicit_domain(
+ __isl_take isl_multi_pw_aff *mpa)
+{
+ if (isl_multi_pw_aff_check_has_explicit_domain(mpa) < 0)
+ return isl_multi_pw_aff_free(mpa);
+ mpa->u.dom = isl_set_universe(isl_multi_pw_aff_get_domain_space(mpa));
+ if (!mpa->u.dom)
+ return isl_multi_pw_aff_free(mpa);
+ return mpa;
+}
+
+/* Intersect the domain of "dst" with the domain product
+ * of the explicit domains of "src1" and "src2".
+ * This function is only called if at least one of "src1" or "src2"
+ * has an explicit domain.
+ */
+static __isl_give isl_multi_pw_aff *
+isl_multi_pw_aff_intersect_explicit_domain_product(
+ __isl_take isl_multi_pw_aff *dst, __isl_keep isl_multi_pw_aff *src1,
+ __isl_keep isl_multi_pw_aff *src2)
+{
+ isl_space *space;
+ isl_set *dom;
+ isl_map *map;
+
+ if (!src1 || !src2)
+ return FN(isl_multi_pw_aff,free)(dst);
+ space = isl_multi_pw_aff_get_domain_space(dst);
+ dom = isl_set_universe(space);
+ map = isl_set_unwrap(dom);
+ if (isl_multi_pw_aff_has_explicit_domain(src1)) {
+ dom = isl_set_copy(src1->u.dom);
+ map = isl_map_intersect_domain(map, dom);
+ }
+ if (isl_multi_pw_aff_has_explicit_domain(src2)) {
+ dom = isl_set_copy(src2->u.dom);
+ map = isl_map_intersect_range(map, dom);
+ }
+ dom = isl_map_wrap(map);
+ dst = isl_multi_pw_aff_intersect_domain(dst, dom);
+ return dst;
+}
+
+/* Check whether the explicit domain of "mpa" has non-zero coefficients
+ * for any dimension in the given range or if any of these dimensions appear
+ * with non-zero coefficients in any of the integer divisions involved.
+ */
+isl_bool isl_multi_pw_aff_involves_explicit_domain_dims(
+ __isl_keep isl_multi_pw_aff *mpa,
+ enum isl_dim_type type, unsigned pos, unsigned n)
+{
+ if (isl_multi_pw_aff_check_has_explicit_domain(mpa) < 0)
+ return isl_bool_error;
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ return isl_set_involves_dims(mpa->u.dom, type, pos, n);
+}
+
+/* Insert "n" dimensions of type "type" at position "pos"
+ * of the explicit domain of "mpa".
+ */
+static __isl_give isl_multi_pw_aff *
+isl_multi_pw_aff_insert_explicit_domain_dims(__isl_take isl_multi_pw_aff *mpa,
+ enum isl_dim_type type, unsigned pos, unsigned n)
+{
+ if (isl_multi_pw_aff_check_has_explicit_domain(mpa) < 0)
+ return isl_multi_pw_aff_free(mpa);
+ mpa = isl_multi_pw_aff_cow(mpa);
+ if (!mpa)
+ return NULL;
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ mpa->u.dom = isl_set_insert_dims(mpa->u.dom, type, pos, n);
+ if (!mpa->u.dom)
+ return isl_multi_pw_aff_free(mpa);
+ return mpa;
+}
+
+/* Drop the "n" dimensions of type "type" starting at position "pos"
+ * of the explicit domain of "mpa".
+ */
+static __isl_give isl_multi_pw_aff *
+isl_multi_pw_aff_drop_explicit_domain_dims(__isl_take isl_multi_pw_aff *mpa,
+ enum isl_dim_type type, unsigned pos, unsigned n)
+{
+ if (isl_multi_pw_aff_check_has_explicit_domain(mpa) < 0)
+ return isl_multi_pw_aff_free(mpa);
+ mpa = isl_multi_pw_aff_cow(mpa);
+ if (!mpa)
+ return NULL;
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ mpa->u.dom = isl_set_drop(mpa->u.dom, type, pos, n);
+ if (!mpa->u.dom)
+ return isl_multi_pw_aff_free(mpa);
+ return mpa;
+}
+
+/* Move the "n" dimensions of "src_type" starting at "src_pos" of
+ * of the explicit domain of "mpa" to dimensions of "dst_type" at "dst_pos".
+ */
+static __isl_give isl_multi_pw_aff *isl_multi_pw_aff_move_explicit_domain_dims(
+ __isl_take isl_multi_pw_aff *mpa,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ if (isl_multi_pw_aff_check_has_explicit_domain(mpa) < 0)
+ return isl_multi_pw_aff_free(mpa);
+ mpa = isl_multi_pw_aff_cow(mpa);
+ if (!mpa)
+ return NULL;
+ if (dst_type == isl_dim_in)
+ dst_type = isl_dim_set;
+ if (src_type == isl_dim_in)
+ src_type = isl_dim_set;
+ mpa->u.dom = isl_set_move_dims(mpa->u.dom, dst_type, dst_pos,
+ src_type, src_pos, n);
+ if (!mpa->u.dom)
+ return isl_multi_pw_aff_free(mpa);
+ return mpa;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_read_no_explicit_domain_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_read_no_explicit_domain_templ.c
new file mode 100644
index 00000000000..b8a6ec5440e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_read_no_explicit_domain_templ.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+#include <isl/set.h>
+
+#include <isl_multi_macro.h>
+
+/* This function is called for each element in a tuple inside
+ * isl_stream_read_multi_*.
+ * Read an EL from "s" and add it to *list.
+ */
+static __isl_give isl_space *FN(read_el,BASE)(__isl_keep isl_stream *s,
+ struct vars *v, __isl_take isl_space *space, int rational, void *user)
+{
+ LIST(EL) **list = (LIST(EL) **) user;
+ EL *el;
+
+ el = FN(isl_stream_read,BASE)(s);
+ *list = FN(LIST(EL),add)(*list, el);
+ if (!*list)
+ return isl_space_free(space);
+
+ return space;
+}
+
+/* Read a multi expression from "s".
+ *
+ * We first read a tuple space, collecting the element values in a list.
+ * Then we create an isl_multi_* from the space and the isl_*_list.
+ */
+__isl_give MULTI(BASE) *FN(isl_stream_read_multi,BASE)(
+ __isl_keep isl_stream *s)
+{
+ struct vars *v;
+ isl_set *dom = NULL;
+ isl_space *space;
+ MULTI(BASE) *multi = NULL;
+ LIST(EL) *list;
+
+ v = vars_new(s->ctx);
+ if (!v)
+ return NULL;
+
+ dom = isl_set_universe(isl_space_params_alloc(s->ctx, 0));
+ if (next_is_tuple(s)) {
+ dom = read_map_tuple(s, dom, isl_dim_param, v, 1, 0);
+ if (isl_stream_eat(s, ISL_TOKEN_TO))
+ goto error;
+ }
+ if (!isl_set_plain_is_universe(dom))
+ isl_die(s->ctx, isl_error_invalid,
+ "expecting universe parameter domain", goto error);
+ if (isl_stream_eat(s, '{'))
+ goto error;
+
+ space = isl_set_get_space(dom);
+
+ list = FN(LIST(EL),alloc)(s->ctx, 0);
+ space = read_tuple_space(s, v, space, 1, 0, &FN(read_el,BASE), &list);
+ multi = FN(FN(MULTI(BASE),from),LIST(BASE))(space, list);
+
+ if (isl_stream_eat(s, '}'))
+ goto error;
+
+ vars_free(v);
+ isl_set_free(dom);
+ return multi;
+error:
+ vars_free(v);
+ isl_set_free(dom);
+ FN(MULTI(BASE),free)(multi);
+ return NULL;
+}
+
+/* Read a multi expression from "str".
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),read_from_str)(isl_ctx *ctx,
+ const char *str)
+{
+ MULTI(BASE) *multi;
+ isl_stream *s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ multi = FN(isl_stream_read_multi,BASE)(s);
+ isl_stream_free(s);
+ return multi;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_splice_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_splice_templ.c
new file mode 100644
index 00000000000..8dca62089b7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_splice_templ.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2012 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/ctx.h>
+#include <isl/space.h>
+
+#include <isl_multi_macro.h>
+
+/* Given two multi expressions, "multi1"
+ *
+ * [A1 A2] -> [B1 B2]
+ *
+ * where A2 starts at position "in_pos" and B2 starts at position "out_pos",
+ * and "multi2"
+ *
+ * [C] -> [D]
+ *
+ * return the multi expression
+ *
+ * [A1 C A2] -> [B1 D B2]
+ *
+ * We first insert input dimensions to obtain
+ *
+ * [A1 C A2] -> [B1 B2]
+ *
+ * and
+ *
+ * [A1 C A2] -> [D]
+ *
+ * and then apply range_splice.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),splice)(
+ __isl_take MULTI(BASE) *multi1, unsigned in_pos, unsigned out_pos,
+ __isl_take MULTI(BASE) *multi2)
+{
+ isl_size n_in1;
+ isl_size n_in2;
+
+ n_in1 = FN(MULTI(BASE),dim)(multi1, isl_dim_in);
+ n_in2 = FN(MULTI(BASE),dim)(multi2, isl_dim_in);
+ if (n_in1 < 0 || n_in2 < 0)
+ goto error;
+
+ if (FN(MULTI(BASE),check_range)(multi1, isl_dim_in, in_pos, 0) < 0)
+ goto error;
+
+ multi1 = FN(MULTI(BASE),insert_dims)(multi1, isl_dim_in, in_pos, n_in2);
+ multi2 = FN(MULTI(BASE),insert_dims)(multi2, isl_dim_in, n_in2,
+ n_in1 - in_pos);
+ multi2 = FN(MULTI(BASE),insert_dims)(multi2, isl_dim_in, 0, in_pos);
+
+ return FN(MULTI(BASE),range_splice)(multi1, out_pos, multi2);
+error:
+ FN(MULTI(BASE),free)(multi1);
+ FN(MULTI(BASE),free)(multi2);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_templ.c
new file mode 100644
index 00000000000..3777024d2e9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_templ.c
@@ -0,0 +1,906 @@
+/*
+ * Copyright 2011 Sven Verdoolaege
+ * Copyright 2012-2014 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <isl/id.h>
+#include <isl_space_private.h>
+#include <isl/set.h>
+#include <isl_reordering.h>
+
+#include <isl_multi_macro.h>
+
+#define MULTI_NAME(BASE) "isl_multi_" #BASE
+
+isl_ctx *FN(MULTI(BASE),get_ctx)(__isl_keep MULTI(BASE) *multi)
+{
+ return multi ? isl_space_get_ctx(multi->space) : NULL;
+}
+
+/* Return the space of "multi".
+ */
+__isl_keep isl_space *FN(MULTI(BASE),peek_space)(__isl_keep MULTI(BASE) *multi)
+{
+ return multi ? multi->space : NULL;
+}
+
+__isl_give isl_space *FN(MULTI(BASE),get_space)(__isl_keep MULTI(BASE) *multi)
+{
+ return isl_space_copy(FN(MULTI(BASE),peek_space)(multi));
+}
+
+__isl_give isl_space *FN(MULTI(BASE),get_domain_space)(
+ __isl_keep MULTI(BASE) *multi)
+{
+ return multi ? isl_space_domain(isl_space_copy(multi->space)) : NULL;
+}
+
+/* Allocate a multi expression living in "space".
+ *
+ * If the number of base expressions is zero, then make sure
+ * there is enough room in the structure for the explicit domain,
+ * in case the type supports such an explicit domain.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),alloc)(__isl_take isl_space *space)
+{
+ isl_ctx *ctx;
+ isl_size n;
+ MULTI(BASE) *multi;
+
+ n = isl_space_dim(space, isl_dim_out);
+ if (n < 0)
+ goto error;
+
+ ctx = isl_space_get_ctx(space);
+ if (n > 0)
+ multi = isl_calloc(ctx, MULTI(BASE),
+ sizeof(MULTI(BASE)) + (n - 1) * sizeof(struct EL *));
+ else
+ multi = isl_calloc(ctx, MULTI(BASE), sizeof(MULTI(BASE)));
+ if (!multi)
+ goto error;
+
+ multi->space = space;
+ multi->n = n;
+ multi->ref = 1;
+ if (FN(MULTI(BASE),has_explicit_domain)(multi))
+ multi = FN(MULTI(BASE),init_explicit_domain)(multi);
+ return multi;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),dup)(__isl_keep MULTI(BASE) *multi)
+{
+ int i;
+ MULTI(BASE) *dup;
+
+ if (!multi)
+ return NULL;
+
+ dup = FN(MULTI(BASE),alloc)(isl_space_copy(multi->space));
+ if (!dup)
+ return NULL;
+
+ for (i = 0; i < multi->n; ++i)
+ dup = FN(FN(MULTI(BASE),set),BASE)(dup, i,
+ FN(EL,copy)(multi->u.p[i]));
+ if (FN(MULTI(BASE),has_explicit_domain)(multi))
+ dup = FN(MULTI(BASE),copy_explicit_domain)(dup, multi);
+
+ return dup;
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),cow)(__isl_take MULTI(BASE) *multi)
+{
+ if (!multi)
+ return NULL;
+
+ if (multi->ref == 1)
+ return multi;
+
+ multi->ref--;
+ return FN(MULTI(BASE),dup)(multi);
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),copy)(__isl_keep MULTI(BASE) *multi)
+{
+ if (!multi)
+ return NULL;
+
+ multi->ref++;
+ return multi;
+}
+
+__isl_null MULTI(BASE) *FN(MULTI(BASE),free)(__isl_take MULTI(BASE) *multi)
+{
+ int i;
+
+ if (!multi)
+ return NULL;
+
+ if (--multi->ref > 0)
+ return NULL;
+
+ isl_space_free(multi->space);
+ for (i = 0; i < multi->n; ++i)
+ FN(EL,free)(multi->u.p[i]);
+ if (FN(MULTI(BASE),has_explicit_domain)(multi))
+ FN(MULTI(BASE),free_explicit_domain)(multi);
+ free(multi);
+
+ return NULL;
+}
+
+isl_size FN(MULTI(BASE),dim)(__isl_keep MULTI(BASE) *multi,
+ enum isl_dim_type type)
+{
+ return isl_space_dim(FN(MULTI(BASE),peek_space)(multi), type);
+}
+
+/* Return the number of base expressions in "multi".
+ */
+isl_size FN(MULTI(BASE),size)(__isl_keep MULTI(BASE) *multi)
+{
+ return multi ? multi->n : isl_size_error;
+}
+
+#undef TYPE
+#define TYPE MULTI(BASE)
+static
+#include "check_type_range_templ.c"
+
+/* Return a copy of the base expression at position "pos" in "multi".
+ */
+__isl_give EL *FN(MULTI(BASE),get_at)(__isl_keep MULTI(BASE) *multi, int pos)
+{
+ isl_ctx *ctx;
+
+ if (FN(MULTI(BASE),check_range)(multi, isl_dim_out, pos, 1) < 0)
+ return NULL;
+ ctx = FN(MULTI(BASE),get_ctx)(multi);
+ return FN(EL,copy)(multi->u.p[pos]);
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give EL *FN(FN(MULTI(BASE),get),BASE)(__isl_keep MULTI(BASE) *multi,
+ int pos)
+{
+ return FN(MULTI(BASE),get_at)(multi, pos);
+}
+
+/* Set the element at position "pos" of "multi" to "el",
+ * where the position may be empty if "multi" has only a single reference.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),restore)(
+ __isl_take MULTI(BASE) *multi, int pos, __isl_take EL *el)
+{
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi || !el)
+ goto error;
+
+ if (FN(MULTI(BASE),check_range)(multi, isl_dim_out, pos, 1) < 0)
+ goto error;
+
+ FN(EL,free)(multi->u.p[pos]);
+ multi->u.p[pos] = el;
+
+ return multi;
+error:
+ FN(MULTI(BASE),free)(multi);
+ FN(EL,free)(el);
+ return NULL;
+}
+
+/* Set the element at position "pos" of "multi" to "el",
+ * where the position may be empty if "multi" has only a single reference.
+ * However, the space of "multi" is available and is checked
+ * for compatibility with "el".
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),restore_check_space)(
+ __isl_take MULTI(BASE) *multi, int pos, __isl_take EL *el)
+{
+ isl_space *space;
+
+ space = FN(MULTI(BASE),peek_space)(multi);
+ if (FN(EL,check_match_domain_space)(el, space) < 0)
+ multi = FN(MULTI(BASE),free)(multi);
+ return FN(MULTI(BASE),restore)(multi, pos, el);
+}
+
+/* Replace the base expression at position "pos" in "multi" with "el".
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),set_at)(
+ __isl_take MULTI(BASE) *multi, int pos, __isl_take EL *el)
+{
+ isl_space *multi_space = NULL;
+ isl_space *el_space = NULL;
+ isl_bool match;
+
+ multi_space = FN(MULTI(BASE),get_space)(multi);
+ match = FN(EL,matching_params)(el, multi_space);
+ if (match < 0)
+ goto error;
+ if (!match) {
+ multi = FN(MULTI(BASE),align_params)(multi,
+ FN(EL,get_space)(el));
+ isl_space_free(multi_space);
+ multi_space = FN(MULTI(BASE),get_space)(multi);
+ el = FN(EL,align_params)(el, isl_space_copy(multi_space));
+ }
+
+ multi = FN(MULTI(BASE),restore_check_space)(multi, pos, el);
+
+ isl_space_free(multi_space);
+ isl_space_free(el_space);
+
+ return multi;
+error:
+ FN(MULTI(BASE),free)(multi);
+ FN(EL,free)(el);
+ isl_space_free(multi_space);
+ isl_space_free(el_space);
+ return NULL;
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give MULTI(BASE) *FN(FN(MULTI(BASE),set),BASE)(
+ __isl_take MULTI(BASE) *multi, int pos, __isl_take EL *el)
+{
+ return FN(MULTI(BASE),set_at)(multi, pos, el);
+}
+
+/* Return the base expressions of "multi" as a list.
+ */
+__isl_give LIST(EL) *FN(MULTI(BASE),get_list)(
+ __isl_keep MULTI(BASE) *multi)
+{
+ isl_size n;
+ int i;
+ LIST(EL) *list;
+
+ n = FN(MULTI(BASE),size)(multi);
+ if (n < 0)
+ return NULL;
+ list = FN(LIST(EL),alloc)(FN(MULTI(BASE),get_ctx(multi)), n);
+ for (i = 0; i < n; ++i) {
+ EL *el = FN(MULTI(BASE),get_at)(multi, i);
+ list = FN(LIST(EL),add)(list, el);
+ }
+
+ return list;
+}
+
+/* Reset the space of "multi". This function is called from isl_pw_templ.c
+ * and doesn't know if the space of an element object is represented
+ * directly or through its domain. It therefore passes along both,
+ * which we pass along to the element function since we don't know how
+ * that is represented either.
+ *
+ * If "multi" has an explicit domain, then the caller is expected
+ * to make sure that any modification that would change the dimensions
+ * of the explicit domain has bee applied before this function is called.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),reset_space_and_domain)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_space *space,
+ __isl_take isl_space *domain)
+{
+ int i;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi || !space || !domain)
+ goto error;
+
+ for (i = 0; i < multi->n; ++i) {
+ multi->u.p[i] = FN(EL,reset_domain_space)(multi->u.p[i],
+ isl_space_copy(domain));
+ if (!multi->u.p[i])
+ goto error;
+ }
+ if (FN(MULTI(BASE),has_explicit_domain)(multi)) {
+ multi = FN(MULTI(BASE),reset_explicit_domain_space)(multi,
+ isl_space_copy(domain));
+ if (!multi)
+ goto error;
+ }
+ isl_space_free(domain);
+ isl_space_free(multi->space);
+ multi->space = space;
+
+ return multi;
+error:
+ isl_space_free(domain);
+ isl_space_free(space);
+ FN(MULTI(BASE),free)(multi);
+ return NULL;
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),reset_domain_space)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_space *domain)
+{
+ isl_space *space;
+
+ space = isl_space_extend_domain_with_range(isl_space_copy(domain),
+ isl_space_copy(multi->space));
+ return FN(MULTI(BASE),reset_space_and_domain)(multi, space, domain);
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),reset_space)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_space *space)
+{
+ isl_space *domain;
+
+ domain = isl_space_domain(isl_space_copy(space));
+ return FN(MULTI(BASE),reset_space_and_domain)(multi, space, domain);
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * of the space of "multi".
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),reset_user)(
+ __isl_take MULTI(BASE) *multi)
+{
+ isl_space *space;
+
+ space = FN(MULTI(BASE),get_space)(multi);
+ space = isl_space_reset_user(space);
+
+ return FN(MULTI(BASE),reset_space)(multi, space);
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),realign_domain)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_reordering *exp)
+{
+ int i;
+ isl_space *space;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi || !exp)
+ goto error;
+
+ for (i = 0; i < multi->n; ++i) {
+ multi->u.p[i] = FN(EL,realign_domain)(multi->u.p[i],
+ isl_reordering_copy(exp));
+ if (!multi->u.p[i])
+ goto error;
+ }
+
+ space = isl_reordering_get_space(exp);
+ multi = FN(MULTI(BASE),reset_domain_space)(multi, space);
+
+ isl_reordering_free(exp);
+ return multi;
+error:
+ isl_reordering_free(exp);
+ FN(MULTI(BASE),free)(multi);
+ return NULL;
+}
+
+/* Align the parameters of "multi" to those of "model".
+ *
+ * If "multi" has an explicit domain, then align the parameters
+ * of the domain first.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),align_params)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_space *model)
+{
+ isl_ctx *ctx;
+ isl_bool equal_params;
+ isl_reordering *exp;
+
+ if (!multi || !model)
+ goto error;
+
+ equal_params = isl_space_has_equal_params(multi->space, model);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params) {
+ isl_space_free(model);
+ return multi;
+ }
+
+ ctx = isl_space_get_ctx(model);
+ if (!isl_space_has_named_params(model))
+ isl_die(ctx, isl_error_invalid,
+ "model has unnamed parameters", goto error);
+ if (!isl_space_has_named_params(multi->space))
+ isl_die(ctx, isl_error_invalid,
+ "input has unnamed parameters", goto error);
+
+ if (FN(MULTI(BASE),has_explicit_domain)(multi)) {
+ multi = FN(MULTI(BASE),align_explicit_domain_params)(multi,
+ isl_space_copy(model));
+ if (!multi)
+ goto error;
+ }
+ exp = isl_parameter_alignment_reordering(multi->space, model);
+ exp = isl_reordering_extend_space(exp,
+ FN(MULTI(BASE),get_domain_space)(multi));
+ multi = FN(MULTI(BASE),realign_domain)(multi, exp);
+
+ isl_space_free(model);
+ return multi;
+error:
+ isl_space_free(model);
+ FN(MULTI(BASE),free)(multi);
+ return NULL;
+}
+
+/* Create a multi expression in the given space with the elements of "list"
+ * as base expressions.
+ *
+ * Since isl_multi_*_restore_* assumes that the element and
+ * the multi expression have matching spaces, the alignment
+ * (if any) needs to be performed beforehand.
+ */
+__isl_give MULTI(BASE) *FN(FN(MULTI(BASE),from),LIST(BASE))(
+ __isl_take isl_space *space, __isl_take LIST(EL) *list)
+{
+ int i;
+ isl_size n, dim;
+ isl_ctx *ctx;
+ MULTI(BASE) *multi;
+
+ dim = isl_space_dim(space, isl_dim_out);
+ n = FN(FN(LIST(EL),n),BASE)(list);
+ if (dim < 0 || n < 0)
+ goto error;
+
+ ctx = isl_space_get_ctx(space);
+ if (n != dim)
+ isl_die(ctx, isl_error_invalid,
+ "invalid number of elements in list", goto error);
+
+ for (i = 0; i < n; ++i) {
+ EL *el = FN(LIST(EL),peek)(list, i);
+ space = isl_space_align_params(space, FN(EL,get_space)(el));
+ }
+ multi = FN(MULTI(BASE),alloc)(isl_space_copy(space));
+ for (i = 0; i < n; ++i) {
+ EL *el = FN(FN(LIST(EL),get),BASE)(list, i);
+ el = FN(EL,align_params)(el, isl_space_copy(space));
+ multi = FN(MULTI(BASE),restore_check_space)(multi, i, el);
+ }
+
+ isl_space_free(space);
+ FN(LIST(EL),free)(list);
+ return multi;
+error:
+ isl_space_free(space);
+ FN(LIST(EL),free)(list);
+ return NULL;
+}
+
+/* This function performs the same operation as isl_multi_*_from_*_list,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give MULTI(BASE) *FN(isl_space_multi,BASE)(__isl_take isl_space *space,
+ __isl_take LIST(EL) *list)
+{
+ return FN(FN(MULTI(BASE),from),LIST(BASE))(space, list);
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),drop_dims)(
+ __isl_take MULTI(BASE) *multi,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (FN(MULTI(BASE),check_range)(multi, type, first, n) < 0)
+ return FN(MULTI(BASE),free)(multi);
+
+ multi->space = isl_space_drop_dims(multi->space, type, first, n);
+ if (!multi->space)
+ return FN(MULTI(BASE),free)(multi);
+
+ if (type == isl_dim_out) {
+ for (i = 0; i < n; ++i)
+ FN(EL,free)(multi->u.p[first + i]);
+ for (i = first; i + n < multi->n; ++i)
+ multi->u.p[i] = multi->u.p[i + n];
+ multi->n -= n;
+ if (n > 0 && FN(MULTI(BASE),has_explicit_domain)(multi))
+ multi = FN(MULTI(BASE),init_explicit_domain)(multi);
+
+ return multi;
+ }
+
+ if (FN(MULTI(BASE),has_explicit_domain)(multi))
+ multi = FN(MULTI(BASE),drop_explicit_domain_dims)(multi,
+ type, first, n);
+ if (!multi)
+ return NULL;
+
+ for (i = 0; i < multi->n; ++i) {
+ multi->u.p[i] = FN(EL,drop_dims)(multi->u.p[i], type, first, n);
+ if (!multi->u.p[i])
+ return FN(MULTI(BASE),free)(multi);
+ }
+
+ return multi;
+}
+
+#undef TYPE
+#define TYPE MULTI(BASE)
+
+#include "isl_check_named_params_templ.c"
+static
+#include "isl_align_params_bin_templ.c"
+
+/* Given two MULTI(BASE)s A -> B and C -> D,
+ * construct a MULTI(BASE) (A * C) -> [B -> D].
+ *
+ * If "multi1" and/or "multi2" has an explicit domain, then
+ * intersect the domain of the result with these explicit domains.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),range_product)(
+ __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2)
+{
+ int i;
+ isl_size n1, n2;
+ EL *el;
+ isl_space *space;
+ MULTI(BASE) *res;
+
+ FN(MULTI(BASE),align_params_bin)(&multi1, &multi2);
+ n1 = FN(MULTI(BASE),size)(multi1);
+ n2 = FN(MULTI(BASE),size)(multi2);
+ if (n1 < 0 || n2 < 0)
+ goto error;
+
+ space = isl_space_range_product(FN(MULTI(BASE),get_space)(multi1),
+ FN(MULTI(BASE),get_space)(multi2));
+ res = FN(MULTI(BASE),alloc)(space);
+
+ for (i = 0; i < n1; ++i) {
+ el = FN(FN(MULTI(BASE),get),BASE)(multi1, i);
+ res = FN(FN(MULTI(BASE),set),BASE)(res, i, el);
+ }
+
+ for (i = 0; i < n2; ++i) {
+ el = FN(FN(MULTI(BASE),get),BASE)(multi2, i);
+ res = FN(FN(MULTI(BASE),set),BASE)(res, n1 + i, el);
+ }
+
+ if (FN(MULTI(BASE),has_explicit_domain)(multi1))
+ res = FN(MULTI(BASE),intersect_explicit_domain)(res, multi1);
+ if (FN(MULTI(BASE),has_explicit_domain)(multi2))
+ res = FN(MULTI(BASE),intersect_explicit_domain)(res, multi2);
+
+ FN(MULTI(BASE),free)(multi1);
+ FN(MULTI(BASE),free)(multi2);
+ return res;
+error:
+ FN(MULTI(BASE),free)(multi1);
+ FN(MULTI(BASE),free)(multi2);
+ return NULL;
+}
+
+/* Is the range of "multi" a wrapped relation?
+ */
+isl_bool FN(MULTI(BASE),range_is_wrapping)(__isl_keep MULTI(BASE) *multi)
+{
+ if (!multi)
+ return isl_bool_error;
+ return isl_space_range_is_wrapping(multi->space);
+}
+
+/* Given a function A -> [B -> C], extract the function A -> B.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),range_factor_domain)(
+ __isl_take MULTI(BASE) *multi)
+{
+ isl_space *space;
+ isl_size total, keep;
+
+ total = FN(MULTI(BASE),dim)(multi, isl_dim_out);
+ if (total < 0)
+ return FN(MULTI(BASE),free)(multi);
+ if (!isl_space_range_is_wrapping(multi->space))
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid,
+ "range is not a product",
+ return FN(MULTI(BASE),free)(multi));
+
+ space = FN(MULTI(BASE),get_space)(multi);
+ space = isl_space_range_factor_domain(space);
+ keep = isl_space_dim(space, isl_dim_out);
+ if (keep < 0)
+ multi = FN(MULTI(BASE),free)(multi);
+ multi = FN(MULTI(BASE),drop_dims)(multi,
+ isl_dim_out, keep, total - keep);
+ multi = FN(MULTI(BASE),reset_space)(multi, space);
+
+ return multi;
+}
+
+/* Given a function A -> [B -> C], extract the function A -> C.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),range_factor_range)(
+ __isl_take MULTI(BASE) *multi)
+{
+ isl_space *space;
+ isl_size total, keep;
+
+ total = FN(MULTI(BASE),dim)(multi, isl_dim_out);
+ if (total < 0)
+ return FN(MULTI(BASE),free)(multi);
+ if (!isl_space_range_is_wrapping(multi->space))
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid,
+ "range is not a product",
+ return FN(MULTI(BASE),free)(multi));
+
+ space = FN(MULTI(BASE),get_space)(multi);
+ space = isl_space_range_factor_range(space);
+ keep = isl_space_dim(space, isl_dim_out);
+ if (keep < 0)
+ multi = FN(MULTI(BASE),free)(multi);
+ multi = FN(MULTI(BASE),drop_dims)(multi, isl_dim_out, 0, total - keep);
+ multi = FN(MULTI(BASE),reset_space)(multi, space);
+
+ return multi;
+}
+
+/* Given a function [B -> C], extract the function C.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),factor_range)(
+ __isl_take MULTI(BASE) *multi)
+{
+ isl_space *space;
+ isl_size total, keep;
+
+ total = FN(MULTI(BASE),dim)(multi, isl_dim_set);
+ if (total < 0)
+ return FN(MULTI(BASE),free)(multi);
+ if (!isl_space_is_wrapping(multi->space))
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid,
+ "not a product", return FN(MULTI(BASE),free)(multi));
+
+ space = FN(MULTI(BASE),get_space)(multi);
+ space = isl_space_factor_range(space);
+ keep = isl_space_dim(space, isl_dim_set);
+ if (keep < 0)
+ multi = FN(MULTI(BASE),free)(multi);
+ multi = FN(MULTI(BASE),drop_dims)(multi, isl_dim_set, 0, total - keep);
+ multi = FN(MULTI(BASE),reset_space)(multi, space);
+
+ return multi;
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),flatten_range)(
+ __isl_take MULTI(BASE) *multi)
+{
+ if (!multi)
+ return NULL;
+
+ if (!multi->space->nested[1])
+ return multi;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ return NULL;
+
+ multi->space = isl_space_flatten_range(multi->space);
+ if (!multi->space)
+ return FN(MULTI(BASE),free)(multi);
+
+ return multi;
+}
+
+/* Given two MULTI(BASE)s A -> B and C -> D,
+ * construct a MULTI(BASE) (A * C) -> (B, D).
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),flat_range_product)(
+ __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2)
+{
+ MULTI(BASE) *multi;
+
+ multi = FN(MULTI(BASE),range_product)(multi1, multi2);
+ multi = FN(MULTI(BASE),flatten_range)(multi);
+ return multi;
+}
+
+/* Given two multi expressions, "multi1"
+ *
+ * [A] -> [B1 B2]
+ *
+ * where B2 starts at position "pos", and "multi2"
+ *
+ * [A] -> [D]
+ *
+ * return the multi expression
+ *
+ * [A] -> [B1 D B2]
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),range_splice)(
+ __isl_take MULTI(BASE) *multi1, unsigned pos,
+ __isl_take MULTI(BASE) *multi2)
+{
+ MULTI(BASE) *res;
+ isl_size dim;
+
+ dim = FN(MULTI(BASE),size)(multi1);
+ if (dim < 0 || !multi2)
+ goto error;
+
+ if (FN(MULTI(BASE),check_range)(multi1, isl_dim_out, pos, 0) < 0)
+ goto error;
+
+ res = FN(MULTI(BASE),copy)(multi1);
+ res = FN(MULTI(BASE),drop_dims)(res, isl_dim_out, pos, dim - pos);
+ multi1 = FN(MULTI(BASE),drop_dims)(multi1, isl_dim_out, 0, pos);
+
+ res = FN(MULTI(BASE),flat_range_product)(res, multi2);
+ res = FN(MULTI(BASE),flat_range_product)(res, multi1);
+
+ return res;
+error:
+ FN(MULTI(BASE),free)(multi1);
+ FN(MULTI(BASE),free)(multi2);
+ return NULL;
+}
+
+#undef TYPE
+#define TYPE MULTI(BASE)
+
+static
+#include "isl_type_has_equal_space_bin_templ.c"
+static
+#include "isl_type_check_equal_space_templ.c"
+
+/* This function is currently only used from isl_aff.c
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),bin_op)(
+ __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2,
+ __isl_give EL *(*fn)(__isl_take EL *, __isl_take EL *))
+ __attribute__ ((unused));
+
+/* Pairwise perform "fn" to the elements of "multi1" and "multi2" and
+ * return the result.
+ *
+ * If "multi2" has an explicit domain, then
+ * intersect the domain of the result with this explicit domain.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),bin_op)(
+ __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2,
+ __isl_give EL *(*fn)(__isl_take EL *, __isl_take EL *))
+{
+ int i;
+
+ FN(MULTI(BASE),align_params_bin)(&multi1, &multi2);
+ multi1 = FN(MULTI(BASE),cow)(multi1);
+ if (FN(MULTI(BASE),check_equal_space)(multi1, multi2) < 0)
+ goto error;
+
+ for (i = 0; i < multi1->n; ++i) {
+ multi1->u.p[i] = fn(multi1->u.p[i],
+ FN(EL,copy)(multi2->u.p[i]));
+ if (!multi1->u.p[i])
+ goto error;
+ }
+
+ if (FN(MULTI(BASE),has_explicit_domain)(multi2))
+ multi1 = FN(MULTI(BASE),intersect_explicit_domain)(multi1,
+ multi2);
+
+ FN(MULTI(BASE),free)(multi2);
+ return multi1;
+error:
+ FN(MULTI(BASE),free)(multi1);
+ FN(MULTI(BASE),free)(multi2);
+ return NULL;
+}
+
+/* Only used on some multi-expressions.
+ */
+static isl_bool FN(MULTI(BASE),any)(__isl_keep MULTI(BASE) *multi,
+ isl_bool (*test)(__isl_keep EL *)) __attribute__ ((unused));
+
+/* Does "test" succeed on any base expression of "multi"?
+ */
+static isl_bool FN(MULTI(BASE),any)(__isl_keep MULTI(BASE) *multi,
+ isl_bool (*test)(__isl_keep EL *))
+{
+ isl_size n;
+ int i;
+
+ n = FN(MULTI(BASE),size)(multi);
+ if (n < 0)
+ return isl_bool_error;
+
+ for (i = 0; i < n; ++i) {
+ isl_bool any = test(multi->u.p[i]);
+ if (any < 0 || any)
+ return any;
+ }
+
+ return isl_bool_false;
+}
+
+/* Only used on some multi-expressions.
+ */
+static isl_bool FN(MULTI(BASE),every)(__isl_keep MULTI(BASE) *multi,
+ isl_bool (*test)(__isl_keep EL *)) __attribute__ ((unused));
+
+/* Does "test" succeed on every base expression of "multi"?
+ */
+static isl_bool FN(MULTI(BASE),every)(__isl_keep MULTI(BASE) *multi,
+ isl_bool (*test)(__isl_keep EL *))
+{
+ isl_size n;
+ int i;
+
+ n = FN(MULTI(BASE),size)(multi);
+ if (n < 0)
+ return isl_bool_error;
+
+ for (i = 0; i < n; ++i) {
+ isl_bool every = test(multi->u.p[i]);
+ if (every < 0 || !every)
+ return every;
+ }
+
+ return isl_bool_true;
+}
+
+/* Convert a multiple expression defined over a parameter domain
+ * into one that is defined over a zero-dimensional set.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),from_range)(
+ __isl_take MULTI(BASE) *multi)
+{
+ isl_space *space;
+
+ if (!multi)
+ return NULL;
+ if (!isl_space_is_set(multi->space))
+ isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid,
+ "not living in a set space",
+ return FN(MULTI(BASE),free)(multi));
+
+ space = FN(MULTI(BASE),get_space)(multi);
+ space = isl_space_from_range(space);
+ multi = FN(MULTI(BASE),reset_space)(multi, space);
+
+ return multi;
+}
+
+/* Are "multi1" and "multi2" obviously equal?
+ */
+isl_bool FN(MULTI(BASE),plain_is_equal)(__isl_keep MULTI(BASE) *multi1,
+ __isl_keep MULTI(BASE) *multi2)
+{
+ int i;
+ isl_bool equal;
+
+ if (!multi1 || !multi2)
+ return isl_bool_error;
+ if (multi1->n != multi2->n)
+ return isl_bool_false;
+ equal = isl_space_is_equal(multi1->space, multi2->space);
+ if (equal < 0 || !equal)
+ return equal;
+
+ for (i = 0; i < multi1->n; ++i) {
+ equal = FN(EL,plain_is_equal)(multi1->u.p[i], multi2->u.p[i]);
+ if (equal < 0 || !equal)
+ return equal;
+ }
+
+ if (FN(MULTI(BASE),has_explicit_domain)(multi1) ||
+ FN(MULTI(BASE),has_explicit_domain)(multi2)) {
+ equal = FN(MULTI(BASE),equal_explicit_domain)(multi1, multi2);
+ if (equal < 0 || !equal)
+ return equal;
+ }
+
+ return isl_bool_true;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_templ.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_templ.h
new file mode 100644
index 00000000000..c5049adcc43
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_templ.h
@@ -0,0 +1,34 @@
+#include <isl/space.h>
+
+#include <isl_multi_macro.h>
+
+/* A multiple expression with base expressions of type EL.
+ *
+ * "space" is the space in which the multiple expression lives.
+ * "n" is the number of base expression and is equal
+ * to the output or set dimension of "space".
+ * "p" is an array of size "n" of base expressions.
+ * The array is only accessible when n > 0.
+ * "dom" is the explicit domain, if present
+ * The explicit domain is only accessible when n == 0.
+ */
+struct MULTI(BASE) {
+ int ref;
+ isl_space *space;
+
+ int n;
+ struct {
+#ifdef EXPLICIT_DOMAIN
+ DOM *dom;
+#endif
+ EL *p[1];
+ } u;
+};
+
+__isl_give MULTI(BASE) *CAT(MULTI(BASE),_alloc)(__isl_take isl_space *space);
+__isl_keep isl_space *FN(MULTI(BASE),peek_space)(__isl_keep MULTI(BASE) *multi);
+
+#ifdef EXPLICIT_DOMAIN
+isl_bool CAT(MULTI(BASE),_has_non_trivial_domain)(
+ __isl_keep MULTI(BASE) *multi);
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_tuple_id_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_tuple_id_templ.c
new file mode 100644
index 00000000000..87f9fe6b71d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_tuple_id_templ.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2011 Sven Verdoolaege
+ * Copyright 2012-2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+
+#include <isl_multi_macro.h>
+
+const char *FN(MULTI(BASE),get_tuple_name)(__isl_keep MULTI(BASE) *multi,
+ enum isl_dim_type type)
+{
+ return multi ? isl_space_get_tuple_name(multi->space, type) : NULL;
+}
+
+/* Does the specified tuple have an id?
+ */
+isl_bool FN(MULTI(BASE),has_tuple_id)(__isl_keep MULTI(BASE) *multi,
+ enum isl_dim_type type)
+{
+ if (!multi)
+ return isl_bool_error;
+ return isl_space_has_tuple_id(multi->space, type);
+}
+
+/* Does the (range) tuple of "multi" have an identifier?
+ *
+ * Technically, the implementation should use isl_dim_set if "multi"
+ * lives in a set space and isl_dim_out if it lives in a map space.
+ * Internally, however, it can be assumed that isl_dim_set is equal
+ * to isl_dim_out.
+ */
+isl_bool FN(MULTI(BASE),has_range_tuple_id)(__isl_keep MULTI(BASE) *multi)
+{
+ return FN(MULTI(BASE),has_tuple_id)(multi, isl_dim_out);
+}
+
+/* Return the id of the specified tuple.
+ */
+__isl_give isl_id *FN(MULTI(BASE),get_tuple_id)(__isl_keep MULTI(BASE) *multi,
+ enum isl_dim_type type)
+{
+ return multi ? isl_space_get_tuple_id(multi->space, type) : NULL;
+}
+
+/* Return the identifier of the (range) tuple of "multi", assuming it has one.
+ *
+ * Technically, the implementation should use isl_dim_set if "multi"
+ * lives in a set space and isl_dim_out if it lives in a map space.
+ * Internally, however, it can be assumed that isl_dim_set is equal
+ * to isl_dim_out.
+ */
+__isl_give isl_id *FN(MULTI(BASE),get_range_tuple_id)(
+ __isl_keep MULTI(BASE) *multi)
+{
+ return FN(MULTI(BASE),get_tuple_id)(multi, isl_dim_out);
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),set_tuple_name)(
+ __isl_keep MULTI(BASE) *multi, enum isl_dim_type type,
+ const char *s)
+{
+ isl_space *space;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ return NULL;
+
+ space = FN(MULTI(BASE),get_space)(multi);
+ space = isl_space_set_tuple_name(space, type, s);
+
+ return FN(MULTI(BASE),reset_space)(multi, space);
+}
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),set_tuple_id)(
+ __isl_take MULTI(BASE) *multi, enum isl_dim_type type,
+ __isl_take isl_id *id)
+{
+ isl_space *space;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ goto error;
+
+ space = FN(MULTI(BASE),get_space)(multi);
+ space = isl_space_set_tuple_id(space, type, id);
+
+ return FN(MULTI(BASE),reset_space)(multi, space);
+error:
+ isl_id_free(id);
+ return NULL;
+}
+
+/* Replace the identifier of the (range) tuple of "multi" by "id".
+ *
+ * Technically, the implementation should use isl_dim_set if "multi"
+ * lives in a set space and isl_dim_out if it lives in a map space.
+ * Internally, however, it can be assumed that isl_dim_set is equal
+ * to isl_dim_out.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),set_range_tuple_id)(
+ __isl_take MULTI(BASE) *multi, __isl_take isl_id *id)
+{
+ return FN(MULTI(BASE),set_tuple_id)(multi, isl_dim_out, id);
+}
+
+/* Drop the id on the specified tuple.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),reset_tuple_id)(
+ __isl_take MULTI(BASE) *multi, enum isl_dim_type type)
+{
+ isl_space *space;
+
+ if (!multi)
+ return NULL;
+ if (!FN(MULTI(BASE),has_tuple_id)(multi, type))
+ return multi;
+
+ multi = FN(MULTI(BASE),cow)(multi);
+ if (!multi)
+ return NULL;
+
+ space = FN(MULTI(BASE),get_space)(multi);
+ space = isl_space_reset_tuple_id(space, type);
+
+ return FN(MULTI(BASE),reset_space)(multi, space);
+}
+
+/* Drop the identifier of the (range) tuple of "multi".
+ *
+ * Technically, the implementation should use isl_dim_set if "multi"
+ * lives in a set space and isl_dim_out if it lives in a map space.
+ * Internally, however, it can be assumed that isl_dim_set is equal
+ * to isl_dim_out.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),reset_range_tuple_id)(
+ __isl_take MULTI(BASE) *multi)
+{
+ return FN(MULTI(BASE),reset_tuple_id)(multi, isl_dim_out);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_unbind_params_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_unbind_params_templ.c
new file mode 100644
index 00000000000..47433195401
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_unbind_params_templ.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_multi_macro.h>
+
+#undef TYPE
+#define TYPE MULTI(BASE)
+#include "isl_unbind_params_templ.c"
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_union_add_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_union_add_templ.c
new file mode 100644
index 00000000000..3178998d325
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_union_add_templ.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+#include <isl_multi_macro.h>
+
+/* Compute the sum of "multi1" and "multi2" on the union of their domains,
+ * with the actual sum on the shared domain and
+ * the defined expression on the symmetric difference of the domains.
+ *
+ * We simply iterate over the elements in both arguments and
+ * call isl_union_pw_aff_union_add on each of them, if there is
+ * at least one element.
+ *
+ * Otherwise, the two expressions have an explicit domain and
+ * the union of these explicit domains is computed.
+ * This assumes that the explicit domains are either both in terms
+ * of specific domains elements or both in terms of parameters.
+ * However, if one of the expressions does not have any constraints
+ * on its explicit domain, then this is allowed as well and the result
+ * is the expression with no constraints on its explicit domain.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),union_add)(
+ __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2)
+{
+ isl_bool has_domain, is_params1, is_params2;
+
+ if (!multi1)
+ goto error;
+ if (multi1->n > 0)
+ return FN(MULTI(BASE),bin_op)(multi1, multi2,
+ &FN(EL,union_add));
+ FN(MULTI(BASE),align_params_bin)(&multi1, &multi2);
+ if (FN(MULTI(BASE),check_equal_space)(multi1, multi2) < 0)
+ goto error;
+ if (FN(MULTI(BASE),check_has_explicit_domain)(multi1) < 0 ||
+ FN(MULTI(BASE),check_has_explicit_domain)(multi2) < 0)
+ goto error;
+
+ has_domain = FN(MULTI(BASE),has_non_trivial_domain)(multi1);
+ if (has_domain < 0)
+ goto error;
+ if (!has_domain) {
+ FN(MULTI(BASE),free)(multi2);
+ return multi1;
+ }
+ has_domain = FN(MULTI(BASE),has_non_trivial_domain)(multi2);
+ if (has_domain < 0)
+ goto error;
+ if (!has_domain) {
+ FN(MULTI(BASE),free)(multi1);
+ return multi2;
+ }
+
+ is_params1 = FN(DOM,is_params)(multi1->u.dom);
+ is_params2 = FN(DOM,is_params)(multi2->u.dom);
+ if (is_params1 < 0 || is_params2 < 0)
+ goto error;
+ if (is_params1 != is_params2)
+ isl_die(FN(MULTI(BASE),get_ctx)(multi1),
+ isl_error_invalid,
+ "cannot compute union of concrete domain and "
+ "parameter constraints", goto error);
+ multi1 = FN(MULTI(BASE),cow)(multi1);
+ if (!multi1)
+ goto error;
+ multi1->u.dom = FN(DOM,union)(multi1->u.dom,
+ FN(DOM,copy)(multi2->u.dom));
+ if (!multi1->u.dom)
+ goto error;
+ FN(MULTI(BASE),free)(multi2);
+ return multi1;
+error:
+ FN(MULTI(BASE),free)(multi1);
+ FN(MULTI(BASE),free)(multi2);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_union_pw_aff_explicit_domain.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_union_pw_aff_explicit_domain.c
new file mode 100644
index 00000000000..7ff9b12bb64
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_union_pw_aff_explicit_domain.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+/* Initialize the explicit domain of "mupa".
+ *
+ * The explicit domain is initialized to a universe parameter set.
+ * It may later be specialized with constraints on the parameter or
+ * specific domain instances.
+ */
+static __isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_init_explicit_domain(
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ isl_space *space;
+
+ if (isl_multi_union_pw_aff_check_has_explicit_domain(mupa) < 0)
+ return isl_multi_union_pw_aff_free(mupa);
+ space = isl_space_params(isl_multi_union_pw_aff_get_space(mupa));
+ mupa->u.dom = isl_union_set_from_set(isl_set_universe(space));
+ if (!mupa->u.dom)
+ return isl_multi_union_pw_aff_free(mupa);
+ return mupa;
+}
+
+/* Drop the "n" dimensions of type "type" starting at position "pos"
+ * of the explicit domain of "mupa".
+ */
+static __isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_drop_explicit_domain_dims(
+ __isl_take isl_multi_union_pw_aff *mupa,
+ enum isl_dim_type type, unsigned pos, unsigned n)
+{
+ if (isl_multi_union_pw_aff_check_has_explicit_domain(mupa) < 0)
+ return isl_multi_union_pw_aff_free(mupa);
+ if (type != isl_dim_param)
+ isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid,
+ "can only drop parameters",
+ return isl_multi_union_pw_aff_free(mupa));
+ mupa = isl_multi_union_pw_aff_cow(mupa);
+ if (!mupa)
+ return NULL;
+ mupa->u.dom = isl_union_set_project_out(mupa->u.dom, type, pos, n);
+ if (!mupa->u.dom)
+ return isl_multi_union_pw_aff_free(mupa);
+ return mupa;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_zero_space_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_zero_space_templ.c
new file mode 100644
index 00000000000..8bd027b2ef5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_zero_space_templ.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2020 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl/space.h>
+
+#include "isl_multi_macro.h"
+
+/* This function performs the same operation as isl_multi_*_zero,
+ * but is considered as a function on an isl_space when exported.
+ */
+__isl_give MULTI(BASE) *FN(isl_space_zero_multi,BASE)(
+ __isl_take isl_space *space)
+{
+ return FN(MULTI(BASE),zero)(space);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_zero_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_zero_templ.c
new file mode 100644
index 00000000000..dee3a8fb264
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_multi_zero_templ.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+#include <isl/local_space.h>
+
+#include <isl_multi_macro.h>
+
+/* Construct a multi expression in the given space with value zero in
+ * each of the output dimensions.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),zero)(__isl_take isl_space *space)
+{
+ isl_size n;
+ MULTI(BASE) *multi;
+
+ n = isl_space_dim(space , isl_dim_out);
+ if (n < 0)
+ goto error;
+
+ multi = FN(MULTI(BASE),alloc)(isl_space_copy(space));
+
+ if (!n)
+ isl_space_free(space);
+ else {
+ int i;
+ isl_local_space *ls;
+ EL *el;
+
+ space = isl_space_domain(space);
+ ls = isl_local_space_from_space(space);
+ el = FN(EL,zero_on_domain)(ls);
+
+ for (i = 0; i < n; ++i)
+ multi = FN(FN(MULTI(BASE),set),BASE)(multi, i,
+ FN(EL,copy)(el));
+
+ FN(EL,free)(el);
+ }
+
+ return multi;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+#include "isl_multi_zero_space_templ.c"
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_obj.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_obj.c
new file mode 100644
index 00000000000..da593d3ffc1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_obj.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2014 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#include <isl/val.h>
+#include <isl/aff.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl/polynomial.h>
+#include <isl/schedule.h>
+#include <isl/obj.h>
+
+static void *isl_obj_val_copy(void *v)
+{
+ return isl_val_copy((isl_val *)v);
+}
+
+static void isl_obj_val_free(void *v)
+{
+ isl_val_free((isl_val *)v);
+}
+
+static __isl_give isl_printer *isl_obj_val_print(__isl_take isl_printer *p,
+ void *v)
+{
+ return isl_printer_print_val(p, (isl_val *)v);
+}
+
+static void *isl_obj_val_add(void *v1, void *v2)
+{
+ return isl_val_add((isl_val *) v1, (isl_val *) v2);
+}
+
+struct isl_obj_vtable isl_obj_val_vtable = {
+ isl_obj_val_copy,
+ isl_obj_val_add,
+ isl_obj_val_print,
+ isl_obj_val_free
+};
+
+static void *isl_obj_map_copy(void *v)
+{
+ return isl_map_copy((struct isl_map *)v);
+}
+
+static void isl_obj_map_free(void *v)
+{
+ isl_map_free((struct isl_map *)v);
+}
+
+static __isl_give isl_printer *isl_obj_map_print(__isl_take isl_printer *p,
+ void *v)
+{
+ return isl_printer_print_map(p, (struct isl_map *)v);
+}
+
+static void *isl_obj_map_add(void *v1, void *v2)
+{
+ return isl_map_union((struct isl_map *)v1, (struct isl_map *)v2);
+}
+
+struct isl_obj_vtable isl_obj_map_vtable = {
+ isl_obj_map_copy,
+ isl_obj_map_add,
+ isl_obj_map_print,
+ isl_obj_map_free
+};
+
+static void *isl_obj_union_map_copy(void *v)
+{
+ return isl_union_map_copy((isl_union_map *)v);
+}
+
+static void isl_obj_union_map_free(void *v)
+{
+ isl_union_map_free((isl_union_map *)v);
+}
+
+static __isl_give isl_printer *isl_obj_union_map_print(__isl_take isl_printer *p,
+ void *v)
+{
+ return isl_printer_print_union_map(p, (isl_union_map *)v);
+}
+
+static void *isl_obj_union_map_add(void *v1, void *v2)
+{
+ return isl_union_map_union((isl_union_map *)v1, (isl_union_map *)v2);
+}
+
+struct isl_obj_vtable isl_obj_union_map_vtable = {
+ isl_obj_union_map_copy,
+ isl_obj_union_map_add,
+ isl_obj_union_map_print,
+ isl_obj_union_map_free
+};
+
+static void *isl_obj_set_copy(void *v)
+{
+ return isl_set_copy((struct isl_set *)v);
+}
+
+static void isl_obj_set_free(void *v)
+{
+ isl_set_free((struct isl_set *)v);
+}
+
+static __isl_give isl_printer *isl_obj_set_print(__isl_take isl_printer *p,
+ void *v)
+{
+ return isl_printer_print_set(p, (struct isl_set *)v);
+}
+
+static void *isl_obj_set_add(void *v1, void *v2)
+{
+ return isl_set_union((struct isl_set *)v1, (struct isl_set *)v2);
+}
+
+struct isl_obj_vtable isl_obj_set_vtable = {
+ isl_obj_set_copy,
+ isl_obj_set_add,
+ isl_obj_set_print,
+ isl_obj_set_free
+};
+
+static void *isl_obj_union_set_copy(void *v)
+{
+ return isl_union_set_copy((isl_union_set *)v);
+}
+
+static void isl_obj_union_set_free(void *v)
+{
+ isl_union_set_free((isl_union_set *)v);
+}
+
+static __isl_give isl_printer *isl_obj_union_set_print(__isl_take isl_printer *p,
+ void *v)
+{
+ return isl_printer_print_union_set(p, (isl_union_set *)v);
+}
+
+static void *isl_obj_union_set_add(void *v1, void *v2)
+{
+ return isl_union_set_union((isl_union_set *)v1, (isl_union_set *)v2);
+}
+
+struct isl_obj_vtable isl_obj_union_set_vtable = {
+ isl_obj_union_set_copy,
+ isl_obj_union_set_add,
+ isl_obj_union_set_print,
+ isl_obj_union_set_free
+};
+
+static void *isl_obj_pw_multi_aff_copy(void *v)
+{
+ return isl_pw_multi_aff_copy((isl_pw_multi_aff *) v);
+}
+
+static void isl_obj_pw_multi_aff_free(void *v)
+{
+ isl_pw_multi_aff_free((isl_pw_multi_aff *) v);
+}
+
+static __isl_give isl_printer *isl_obj_pw_multi_aff_print(
+ __isl_take isl_printer *p, void *v)
+{
+ return isl_printer_print_pw_multi_aff(p, (isl_pw_multi_aff *) v);
+}
+
+static void *isl_obj_pw_multi_aff_add(void *v1, void *v2)
+{
+ return isl_pw_multi_aff_add((isl_pw_multi_aff *) v1,
+ (isl_pw_multi_aff *) v2);
+}
+
+struct isl_obj_vtable isl_obj_pw_multi_aff_vtable = {
+ isl_obj_pw_multi_aff_copy,
+ isl_obj_pw_multi_aff_add,
+ isl_obj_pw_multi_aff_print,
+ isl_obj_pw_multi_aff_free
+};
+
+static void *isl_obj_none_copy(void *v)
+{
+ return v;
+}
+
+static void isl_obj_none_free(void *v)
+{
+}
+
+static __isl_give isl_printer *isl_obj_none_print(__isl_take isl_printer *p,
+ void *v)
+{
+ return p;
+}
+
+static void *isl_obj_none_add(void *v1, void *v2)
+{
+ return NULL;
+}
+
+struct isl_obj_vtable isl_obj_none_vtable = {
+ isl_obj_none_copy,
+ isl_obj_none_add,
+ isl_obj_none_print,
+ isl_obj_none_free
+};
+
+static void *isl_obj_pw_qp_copy(void *v)
+{
+ return isl_pw_qpolynomial_copy((struct isl_pw_qpolynomial *)v);
+}
+
+static void isl_obj_pw_qp_free(void *v)
+{
+ isl_pw_qpolynomial_free((struct isl_pw_qpolynomial *)v);
+}
+
+static __isl_give isl_printer *isl_obj_pw_qp_print(__isl_take isl_printer *p,
+ void *v)
+{
+ return isl_printer_print_pw_qpolynomial(p,
+ (struct isl_pw_qpolynomial *)v);
+}
+
+static void *isl_obj_pw_qp_add(void *v1, void *v2)
+{
+ return isl_pw_qpolynomial_add((struct isl_pw_qpolynomial *)v1,
+ (struct isl_pw_qpolynomial *)v2);
+}
+
+struct isl_obj_vtable isl_obj_pw_qpolynomial_vtable = {
+ isl_obj_pw_qp_copy,
+ isl_obj_pw_qp_add,
+ isl_obj_pw_qp_print,
+ isl_obj_pw_qp_free
+};
+
+static void *isl_obj_union_pw_qp_copy(void *v)
+{
+ return isl_union_pw_qpolynomial_copy((struct isl_union_pw_qpolynomial *)v);
+}
+
+static void isl_obj_union_pw_qp_free(void *v)
+{
+ isl_union_pw_qpolynomial_free((struct isl_union_pw_qpolynomial *)v);
+}
+
+static __isl_give isl_printer *isl_obj_union_pw_qp_print(
+ __isl_take isl_printer *p, void *v)
+{
+ return isl_printer_print_union_pw_qpolynomial(p,
+ (struct isl_union_pw_qpolynomial *)v);
+}
+
+static void *isl_obj_union_pw_qp_add(void *v1, void *v2)
+{
+ return isl_union_pw_qpolynomial_add(
+ (struct isl_union_pw_qpolynomial *)v1,
+ (struct isl_union_pw_qpolynomial *)v2);
+}
+
+struct isl_obj_vtable isl_obj_union_pw_qpolynomial_vtable = {
+ isl_obj_union_pw_qp_copy,
+ isl_obj_union_pw_qp_add,
+ isl_obj_union_pw_qp_print,
+ isl_obj_union_pw_qp_free
+};
+
+static void *isl_obj_pw_qpf_copy(void *v)
+{
+ return isl_pw_qpolynomial_fold_copy((struct isl_pw_qpolynomial_fold *)v);
+}
+
+static void isl_obj_pw_qpf_free(void *v)
+{
+ isl_pw_qpolynomial_fold_free((struct isl_pw_qpolynomial_fold *)v);
+}
+
+static __isl_give isl_printer *isl_obj_pw_qpf_print(__isl_take isl_printer *p,
+ void *v)
+{
+ return isl_printer_print_pw_qpolynomial_fold(p,
+ (struct isl_pw_qpolynomial_fold *)v);
+}
+
+static void *isl_obj_pw_qpf_add(void *v1, void *v2)
+{
+ return isl_pw_qpolynomial_fold_fold((struct isl_pw_qpolynomial_fold *)v1,
+ (struct isl_pw_qpolynomial_fold *)v2);
+}
+
+struct isl_obj_vtable isl_obj_pw_qpolynomial_fold_vtable = {
+ isl_obj_pw_qpf_copy,
+ isl_obj_pw_qpf_add,
+ isl_obj_pw_qpf_print,
+ isl_obj_pw_qpf_free
+};
+
+static void *isl_obj_union_pw_qpf_copy(void *v)
+{
+ return isl_union_pw_qpolynomial_fold_copy((struct isl_union_pw_qpolynomial_fold *)v);
+}
+
+static void isl_obj_union_pw_qpf_free(void *v)
+{
+ isl_union_pw_qpolynomial_fold_free((struct isl_union_pw_qpolynomial_fold *)v);
+}
+
+static __isl_give isl_printer *isl_obj_union_pw_qpf_print(
+ __isl_take isl_printer *p, void *v)
+{
+ return isl_printer_print_union_pw_qpolynomial_fold(p,
+ (struct isl_union_pw_qpolynomial_fold *)v);
+}
+
+static void *isl_obj_union_pw_qpf_add(void *v1, void *v2)
+{
+ return isl_union_pw_qpolynomial_fold_fold(
+ (struct isl_union_pw_qpolynomial_fold *)v1,
+ (struct isl_union_pw_qpolynomial_fold *)v2);
+}
+
+struct isl_obj_vtable isl_obj_union_pw_qpolynomial_fold_vtable = {
+ isl_obj_union_pw_qpf_copy,
+ isl_obj_union_pw_qpf_add,
+ isl_obj_union_pw_qpf_print,
+ isl_obj_union_pw_qpf_free
+};
+
+static void *isl_obj_schedule_copy(void *v)
+{
+ return isl_schedule_copy((isl_schedule *) v);
+}
+
+static void isl_obj_schedule_free(void *v)
+{
+ isl_schedule_free((isl_schedule *) v);
+}
+
+static __isl_give isl_printer *isl_obj_schedule_print(
+ __isl_take isl_printer *p, void *v)
+{
+ return isl_printer_print_schedule(p, (isl_schedule *) v);
+}
+
+struct isl_obj_vtable isl_obj_schedule_vtable = {
+ isl_obj_schedule_copy,
+ NULL,
+ isl_obj_schedule_print,
+ isl_obj_schedule_free
+};
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_opt_mpa_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_opt_mpa_templ.c
new file mode 100644
index 00000000000..e4245435d49
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_opt_mpa_templ.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2018 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#undef TYPE
+#define TYPE CAT(isl_,BASE)
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Compute the optima of the set or output dimensions as a function of the
+ * parameters (and input dimensions), but independently of
+ * the other set or output dimensions,
+ * given a function "opt" that computes this optimum
+ * for a single dimension.
+ *
+ * If the resulting multi piecewise affine expression has
+ * an explicit domain, then assign it the (parameter) domain of the input.
+ * In other cases, the (parameter) domain is stored in the individual elements.
+ */
+static __isl_give isl_multi_pw_aff *FN(BASE,opt_mpa)(__isl_take TYPE *obj,
+ __isl_give isl_pw_aff *(*opt)(__isl_take TYPE *obj, int pos))
+{
+ int i;
+ isl_size n;
+ isl_multi_pw_aff *mpa;
+
+ mpa = isl_multi_pw_aff_alloc(FN(TYPE,get_space)(obj));
+ n = isl_multi_pw_aff_size(mpa);
+ if (n < 0)
+ mpa = isl_multi_pw_aff_free(mpa);
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+
+ pa = opt(FN(TYPE,copy)(obj), i);
+ mpa = isl_multi_pw_aff_set_pw_aff(mpa, i, pa);
+ }
+ if (isl_multi_pw_aff_has_explicit_domain(mpa)) {
+ isl_set *dom;
+
+ dom = FN(TYPE,domain)(FN(TYPE,copy)(obj));
+ mpa = isl_multi_pw_aff_intersect_domain(mpa, dom);
+ }
+ FN(TYPE,free)(obj);
+
+ return mpa;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_options.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_options.c
new file mode 100644
index 00000000000..a9b59c59345
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_options.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isl/ctx.h>
+#include <isl_options_private.h>
+#include <isl/ast_build.h>
+#include <isl/schedule.h>
+#include <isl/version.h>
+
+struct isl_arg_choice isl_pip_context_choice[] = {
+ {"gbr", ISL_CONTEXT_GBR},
+ {"lexmin", ISL_CONTEXT_LEXMIN},
+ {0}
+};
+
+struct isl_arg_choice isl_gbr_choice[] = {
+ {"never", ISL_GBR_NEVER},
+ {"once", ISL_GBR_ONCE},
+ {"always", ISL_GBR_ALWAYS},
+ {0}
+};
+
+struct isl_arg_choice isl_closure_choice[] = {
+ {"isl", ISL_CLOSURE_ISL},
+ {"box", ISL_CLOSURE_BOX},
+ {0}
+};
+
+static struct isl_arg_choice bound[] = {
+ {"bernstein", ISL_BOUND_BERNSTEIN},
+ {"range", ISL_BOUND_RANGE},
+ {0}
+};
+
+static struct isl_arg_choice on_error[] = {
+ {"warn", ISL_ON_ERROR_WARN},
+ {"continue", ISL_ON_ERROR_CONTINUE},
+ {"abort", ISL_ON_ERROR_ABORT},
+ {0}
+};
+
+static struct isl_arg_choice isl_schedule_algorithm_choice[] = {
+ {"isl", ISL_SCHEDULE_ALGORITHM_ISL},
+ {"feautrier", ISL_SCHEDULE_ALGORITHM_FEAUTRIER},
+ {0}
+};
+
+static struct isl_arg_flags bernstein_recurse[] = {
+ {"none", ISL_BERNSTEIN_FACTORS | ISL_BERNSTEIN_INTERVALS, 0},
+ {"factors", ISL_BERNSTEIN_FACTORS | ISL_BERNSTEIN_INTERVALS,
+ ISL_BERNSTEIN_FACTORS},
+ {"intervals", ISL_BERNSTEIN_FACTORS | ISL_BERNSTEIN_INTERVALS,
+ ISL_BERNSTEIN_INTERVALS},
+ {"full", ISL_BERNSTEIN_FACTORS | ISL_BERNSTEIN_INTERVALS,
+ ISL_BERNSTEIN_FACTORS | ISL_BERNSTEIN_INTERVALS},
+ {0}
+};
+
+static struct isl_arg_choice convex[] = {
+ {"wrap", ISL_CONVEX_HULL_WRAP},
+ {"fm", ISL_CONVEX_HULL_FM},
+ {0}
+};
+
+#define ISL_SCHEDULE_FUSE_MAX 0
+#define ISL_SCHEDULE_FUSE_MIN 1
+
+static struct isl_arg_choice fuse[] = {
+ {"max", ISL_SCHEDULE_FUSE_MAX},
+ {"min", ISL_SCHEDULE_FUSE_MIN},
+ {0}
+};
+
+/* Callback for setting the "schedule-fuse" option.
+ * This (now hidden) option tries to mimic an option that was
+ * replaced by the schedule-serialize-sccs option.
+ * Setting the old option to ISL_SCHEDULE_FUSE_MIN is now
+ * expressed by turning on the schedule-serialize-sccs option.
+ */
+static int set_fuse(void *opt, unsigned val)
+{
+ struct isl_options *options = opt;
+
+ options->schedule_serialize_sccs = (val == ISL_SCHEDULE_FUSE_MIN);
+
+ return 0;
+}
+
+static struct isl_arg_choice separation_bounds[] = {
+ {"explicit", ISL_AST_BUILD_SEPARATION_BOUNDS_EXPLICIT},
+ {"implicit", ISL_AST_BUILD_SEPARATION_BOUNDS_IMPLICIT},
+ {0}
+};
+
+static void print_version(void)
+{
+ printf("%s", isl_version());
+}
+
+ISL_ARGS_START(struct isl_options, isl_options_args)
+ISL_ARG_CHOICE(struct isl_options, context, 0, "context", \
+ isl_pip_context_choice, ISL_CONTEXT_GBR,
+ "how to handle the pip context tableau")
+ISL_ARG_CHOICE(struct isl_options, gbr, 0, "gbr", \
+ isl_gbr_choice, ISL_GBR_ALWAYS,
+ "how often to use generalized basis reduction")
+ISL_ARG_CHOICE(struct isl_options, closure, 0, "closure", \
+ isl_closure_choice, ISL_CLOSURE_ISL,
+ "closure operation to use")
+ISL_ARG_BOOL(struct isl_options, gbr_only_first, 0, "gbr-only-first", 0,
+ "only perform basis reduction in first direction")
+ISL_ARG_CHOICE(struct isl_options, bound, 0, "bound", bound,
+ ISL_BOUND_BERNSTEIN, "algorithm to use for computing bounds")
+ISL_ARG_CHOICE(struct isl_options, on_error, 0, "on-error", on_error,
+ ISL_ON_ERROR_WARN, "how to react if an error is detected")
+ISL_ARG_FLAGS(struct isl_options, bernstein_recurse, 0,
+ "bernstein-recurse", bernstein_recurse, ISL_BERNSTEIN_FACTORS, NULL)
+ISL_ARG_BOOL(struct isl_options, bernstein_triangulate, 0,
+ "bernstein-triangulate", 1,
+ "triangulate domains during Bernstein expansion")
+ISL_ARG_BOOL(struct isl_options, pip_symmetry, 0, "pip-symmetry", 1,
+ "detect simple symmetries in PIP input")
+ISL_ARG_CHOICE(struct isl_options, convex, 0, "convex-hull", \
+ convex, ISL_CONVEX_HULL_WRAP, "convex hull algorithm to use")
+ISL_ARG_BOOL(struct isl_options, coalesce_bounded_wrapping, 0,
+ "coalesce-bounded-wrapping", 1, "bound wrapping during coalescing")
+ISL_ARG_BOOL(struct isl_options, coalesce_preserve_locals, 0,
+ "coalesce-preserve-locals", 0,
+ "preserve local variables during coalescing")
+ISL_ARG_INT(struct isl_options, schedule_max_coefficient, 0,
+ "schedule-max-coefficient", "limit", -1, "Only consider schedules "
+ "where the coefficients of the variable and parameter dimensions "
+ "do not exceed <limit>. A value of -1 allows arbitrary coefficients.")
+ISL_ARG_INT(struct isl_options, schedule_max_constant_term, 0,
+ "schedule-max-constant-term", "limit", -1, "Only consider schedules "
+ "where the coefficients of the constant dimension do not exceed "
+ "<limit>. A value of -1 allows arbitrary coefficients.")
+ISL_ARG_BOOL(struct isl_options, schedule_parametric, 0,
+ "schedule-parametric", 1, "construct possibly parametric schedules")
+ISL_ARG_BOOL(struct isl_options, schedule_outer_coincidence, 0,
+ "schedule-outer-coincidence", 0,
+ "try to construct schedules where the outer member of each band "
+ "satisfies the coincidence constraints")
+ISL_ARG_BOOL(struct isl_options, schedule_maximize_band_depth, 0,
+ "schedule-maximize-band-depth", 0,
+ "maximize the number of scheduling dimensions in a band")
+ISL_ARG_BOOL(struct isl_options, schedule_maximize_coincidence, 0,
+ "schedule-maximize-coincidence", 0,
+ "maximize the number of coincident dimensions in a band")
+ISL_ARG_BOOL(struct isl_options, schedule_split_scaled, 0,
+ "schedule-split-scaled", 1,
+ "split non-tilable bands with scaled schedules")
+ISL_ARG_BOOL(struct isl_options, schedule_treat_coalescing, 0,
+ "schedule-treat-coalescing", 1,
+ "try and prevent or adjust schedules that perform loop coalescing")
+ISL_ARG_BOOL(struct isl_options, schedule_separate_components, 0,
+ "schedule-separate-components", 1,
+ "separate components in dependence graph")
+ISL_ARG_BOOL(struct isl_options, schedule_whole_component, 0,
+ "schedule-whole-component", 0,
+ "try and compute schedule for entire component first")
+ISL_ARG_CHOICE(struct isl_options, schedule_algorithm, 0,
+ "schedule-algorithm", isl_schedule_algorithm_choice,
+ ISL_SCHEDULE_ALGORITHM_ISL, "scheduling algorithm to use")
+ISL_ARG_BOOL(struct isl_options, schedule_carry_self_first, 0,
+ "schedule-carry-self-first", 1, "try and carry self-dependences first")
+ISL_ARG_BOOL(struct isl_options, schedule_serialize_sccs, 0,
+ "schedule-serialize-sccs", 0,
+ "serialize strongly connected components in dependence graph")
+ISL_ARG_PHANTOM_USER_CHOICE_F(0, "schedule-fuse", fuse, &set_fuse,
+ ISL_SCHEDULE_FUSE_MAX, "level of fusion during scheduling",
+ ISL_ARG_HIDDEN)
+ISL_ARG_BOOL(struct isl_options, tile_scale_tile_loops, 0,
+ "tile-scale-tile-loops", 1, "scale tile loops")
+ISL_ARG_BOOL(struct isl_options, tile_shift_point_loops, 0,
+ "tile-shift-point-loops", 1, "shift point loops to start at zero")
+ISL_ARG_STR(struct isl_options, ast_iterator_type, 0,
+ "ast-iterator-type", "type", "int",
+ "type used for iterators during printing of AST")
+ISL_ARG_BOOL(struct isl_options, ast_always_print_block, 0,
+ "ast-always-print-block", 0, "print for and if bodies as a block "
+ "regardless of the number of statements in the body")
+ISL_ARG_BOOL(struct isl_options, ast_print_outermost_block, 0,
+ "ast-print-outermost-block", 1, "print outermost block node as a block")
+ISL_ARG_BOOL(struct isl_options, ast_print_macro_once, 0,
+ "ast-print-macro-once", 0, "only print macro definitions once")
+ISL_ARG_BOOL(struct isl_options, ast_build_atomic_upper_bound, 0,
+ "ast-build-atomic-upper-bound", 1, "generate atomic upper bounds")
+ISL_ARG_BOOL(struct isl_options, ast_build_prefer_pdiv, 0,
+ "ast-build-prefer-pdiv", 1, "prefer pdiv operation over fdiv")
+ISL_ARG_BOOL(struct isl_options, ast_build_detect_min_max, 0,
+ "ast-build-detect-min-max", 0, "detect min/max expressions")
+ISL_ARG_BOOL(struct isl_options, ast_build_exploit_nested_bounds, 0,
+ "ast-build-exploit-nested-bounds", 1,
+ "simplify conditions based on bounds of nested for loops")
+ISL_ARG_BOOL(struct isl_options, ast_build_group_coscheduled, 0,
+ "ast-build-group-coscheduled", 0,
+ "keep coscheduled domain elements together")
+ISL_ARG_CHOICE(struct isl_options, ast_build_separation_bounds, 0,
+ "ast-build-separation-bounds", separation_bounds,
+ ISL_AST_BUILD_SEPARATION_BOUNDS_EXPLICIT,
+ "bounds to use during separation")
+ISL_ARG_BOOL(struct isl_options, ast_build_scale_strides, 0,
+ "ast-build-scale-strides", 1,
+ "allow iterators of strided loops to be scaled down")
+ISL_ARG_BOOL(struct isl_options, ast_build_allow_else, 0,
+ "ast-build-allow-else", 1, "generate if statements with else branches")
+ISL_ARG_BOOL(struct isl_options, ast_build_allow_or, 0,
+ "ast-build-allow-or", 1, "generate if conditions with disjunctions")
+ISL_ARG_BOOL(struct isl_options, print_stats, 0, "print-stats", 0,
+ "print statistics for every isl_ctx")
+ISL_ARG_ULONG(struct isl_options, max_operations, 0,
+ "max-operations", 0, "default number of maximal operations per isl_ctx")
+ISL_ARG_VERSION(print_version)
+ISL_ARGS_END
+
+ISL_ARG_DEF(isl_options, struct isl_options, isl_options_args)
+
+ISL_ARG_CTX_DEF(isl_options, struct isl_options, isl_options_args)
+
+ISL_CTX_SET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args, bound)
+ISL_CTX_GET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args, bound)
+
+ISL_CTX_SET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args,
+ on_error)
+ISL_CTX_GET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args,
+ on_error)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ pip_symmetry)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ pip_symmetry)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ coalesce_bounded_wrapping)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ coalesce_bounded_wrapping)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ coalesce_preserve_locals)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ coalesce_preserve_locals)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ gbr_only_first)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ gbr_only_first)
+
+ISL_CTX_SET_INT_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_max_coefficient)
+ISL_CTX_GET_INT_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_max_coefficient)
+
+ISL_CTX_SET_INT_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_max_constant_term)
+ISL_CTX_GET_INT_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_max_constant_term)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_maximize_band_depth)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_maximize_band_depth)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_maximize_coincidence)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_maximize_coincidence)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_split_scaled)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_split_scaled)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_treat_coalescing)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_treat_coalescing)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_separate_components)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_separate_components)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_whole_component)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_whole_component)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_outer_coincidence)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_outer_coincidence)
+
+ISL_CTX_SET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_algorithm)
+ISL_CTX_GET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_algorithm)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_carry_self_first)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_carry_self_first)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_serialize_sccs)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ schedule_serialize_sccs)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ tile_scale_tile_loops)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ tile_scale_tile_loops)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ tile_shift_point_loops)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ tile_shift_point_loops)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_atomic_upper_bound)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_atomic_upper_bound)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_prefer_pdiv)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_prefer_pdiv)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_detect_min_max)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_detect_min_max)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_exploit_nested_bounds)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_exploit_nested_bounds)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_group_coscheduled)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_group_coscheduled)
+
+ISL_CTX_SET_STR_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_iterator_type)
+ISL_CTX_GET_STR_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_iterator_type)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_always_print_block)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_always_print_block)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_print_outermost_block)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_print_outermost_block)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_print_macro_once)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_print_macro_once)
+
+ISL_CTX_SET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_separation_bounds)
+ISL_CTX_GET_CHOICE_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_separation_bounds)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_scale_strides)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_scale_strides)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_allow_else)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_allow_else)
+
+ISL_CTX_SET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_allow_or)
+ISL_CTX_GET_BOOL_DEF(isl_options, struct isl_options, isl_options_args,
+ ast_build_allow_or)
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_options_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_options_private.h
new file mode 100644
index 00000000000..97297d8063c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_options_private.h
@@ -0,0 +1,75 @@
+#ifndef ISL_OPTIONS_PRIVATE_H
+#define ISL_OPTIONS_PRIVATE_H
+
+#include <isl/options.h>
+
+struct isl_options {
+ #define ISL_CONTEXT_GBR 0
+ #define ISL_CONTEXT_LEXMIN 1
+ unsigned context;
+
+ #define ISL_GBR_NEVER 0
+ #define ISL_GBR_ONCE 1
+ #define ISL_GBR_ALWAYS 2
+ unsigned gbr;
+ unsigned gbr_only_first;
+
+ #define ISL_CLOSURE_ISL 0
+ #define ISL_CLOSURE_BOX 1
+ unsigned closure;
+
+ int bound;
+ unsigned on_error;
+
+ #define ISL_BERNSTEIN_FACTORS 1
+ #define ISL_BERNSTEIN_INTERVALS 2
+ int bernstein_recurse;
+
+ int bernstein_triangulate;
+
+ int pip_symmetry;
+
+ #define ISL_CONVEX_HULL_WRAP 0
+ #define ISL_CONVEX_HULL_FM 1
+ int convex;
+
+ int coalesce_bounded_wrapping;
+ int coalesce_preserve_locals;
+
+ int schedule_max_coefficient;
+ int schedule_max_constant_term;
+ int schedule_parametric;
+ int schedule_outer_coincidence;
+ int schedule_maximize_band_depth;
+ int schedule_maximize_coincidence;
+ int schedule_split_scaled;
+ int schedule_treat_coalescing;
+ int schedule_separate_components;
+ int schedule_whole_component;
+ unsigned schedule_algorithm;
+ int schedule_carry_self_first;
+ int schedule_serialize_sccs;
+
+ int tile_scale_tile_loops;
+ int tile_shift_point_loops;
+
+ char *ast_iterator_type;
+ int ast_always_print_block;
+ int ast_print_outermost_block;
+ int ast_print_macro_once;
+
+ int ast_build_atomic_upper_bound;
+ int ast_build_prefer_pdiv;
+ int ast_build_detect_min_max;
+ int ast_build_exploit_nested_bounds;
+ int ast_build_group_coscheduled;
+ int ast_build_separation_bounds;
+ int ast_build_scale_strides;
+ int ast_build_allow_else;
+ int ast_build_allow_or;
+
+ int print_stats;
+ unsigned long max_operations;
+};
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_output.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_output.c
new file mode 100644
index 00000000000..9839a52516a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_output.c
@@ -0,0 +1,3646 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2012-2013 Ecole Normale Superieure
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ * and Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl/set.h>
+#include <isl_seq.h>
+#include <isl_polynomial_private.h>
+#include <isl_printer_private.h>
+#include <isl_space_private.h>
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl/constraint.h>
+#include <isl_local.h>
+#include <isl_local_space_private.h>
+#include <isl_aff_private.h>
+#include <isl_id_private.h>
+#include <isl_val_private.h>
+#include <isl_constraint_private.h>
+#include <isl/ast_build.h>
+#include <isl_sort.h>
+#include <isl_output_private.h>
+
+#include <bset_to_bmap.c>
+#include <set_to_map.c>
+#include <uset_to_umap.c>
+
+static const char *s_to[2] = { " -> ", " \\to " };
+static const char *s_and[2] = { " and ", " \\wedge " };
+static const char *s_or[2] = { " or ", " \\vee " };
+static const char *s_le[2] = { "<=", "\\le" };
+static const char *s_ge[2] = { ">=", "\\ge" };
+static const char *s_open_set[2] = { "{ ", "\\{\\, " };
+static const char *s_close_set[2] = { " }", " \\,\\}" };
+static const char *s_open_list[2] = { "[", "(" };
+static const char *s_close_list[2] = { "]", ")" };
+static const char *s_such_that[2] = { " : ", " \\mid " };
+static const char *s_open_exists[2] = { "exists (", "\\exists \\, " };
+static const char *s_close_exists[2] = { ")", "" };
+static const char *s_div_prefix[2] = { "e", "\\alpha_" };
+static const char *s_mod[2] = { "mod", "\\bmod" };
+static const char *s_param_prefix[2] = { "p", "p_" };
+static const char *s_input_prefix[2] = { "i", "i_" };
+static const char *s_output_prefix[2] = { "o", "o_" };
+
+static __isl_give isl_printer *print_constraint_polylib(
+ struct isl_basic_map *bmap, int ineq, int n, __isl_take isl_printer *p)
+{
+ int i;
+ isl_size n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ isl_size n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ isl_size nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ isl_int *c = ineq ? bmap->ineq[n] : bmap->eq[n];
+
+ if (n_in < 0 || n_out < 0 || nparam < 0)
+ return isl_printer_free(p);
+
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_int(p, ineq);
+ for (i = 0; i < n_out; ++i) {
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_isl_int(p, c[1+nparam+n_in+i]);
+ }
+ for (i = 0; i < n_in; ++i) {
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_isl_int(p, c[1+nparam+i]);
+ }
+ for (i = 0; i < bmap->n_div; ++i) {
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_isl_int(p, c[1+nparam+n_in+n_out+i]);
+ }
+ for (i = 0; i < nparam; ++i) {
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_isl_int(p, c[1+i]);
+ }
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_isl_int(p, c[0]);
+ p = isl_printer_end_line(p);
+ return p;
+}
+
+static __isl_give isl_printer *print_constraints_polylib(
+ struct isl_basic_map *bmap, __isl_take isl_printer *p)
+{
+ int i;
+
+ p = isl_printer_set_isl_int_width(p, 5);
+
+ for (i = 0; i < bmap->n_eq; ++i)
+ p = print_constraint_polylib(bmap, 0, i, p);
+ for (i = 0; i < bmap->n_ineq; ++i)
+ p = print_constraint_polylib(bmap, 1, i, p);
+
+ return p;
+}
+
+static __isl_give isl_printer *bset_print_constraints_polylib(
+ struct isl_basic_set *bset, __isl_take isl_printer *p)
+{
+ return print_constraints_polylib(bset_to_bmap(bset), p);
+}
+
+static __isl_give isl_printer *isl_basic_map_print_polylib(
+ __isl_keep isl_basic_map *bmap, __isl_take isl_printer *p, int ext)
+{
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_printer_free(p);
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_int(p, bmap->n_eq + bmap->n_ineq);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_int(p, 1 + total + 1);
+ if (ext) {
+ isl_size n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ isl_size n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ isl_size n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ isl_size nparam = isl_basic_map_dim(bmap, isl_dim_param);
+
+ if (n_in < 0 || n_out < 0 || n_div < 0 || nparam < 0)
+ return isl_printer_free(p);
+
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_int(p, n_out);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_int(p, n_in);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_int(p, n_div);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_int(p, nparam);
+ }
+ p = isl_printer_end_line(p);
+ return print_constraints_polylib(bmap, p);
+}
+
+static __isl_give isl_printer *isl_basic_set_print_polylib(
+ __isl_keep isl_basic_set *bset, __isl_take isl_printer *p, int ext)
+{
+ return isl_basic_map_print_polylib(bset_to_bmap(bset), p, ext);
+}
+
+static __isl_give isl_printer *isl_map_print_polylib(__isl_keep isl_map *map,
+ __isl_take isl_printer *p, int ext)
+{
+ int i;
+
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_int(p, map->n);
+ p = isl_printer_end_line(p);
+ for (i = 0; i < map->n; ++i) {
+ p = isl_printer_start_line(p);
+ p = isl_printer_end_line(p);
+ p = isl_basic_map_print_polylib(map->p[i], p, ext);
+ }
+ return p;
+}
+
+static __isl_give isl_printer *isl_set_print_polylib(__isl_keep isl_set *set,
+ __isl_take isl_printer *p, int ext)
+{
+ return isl_map_print_polylib(set_to_map(set), p, ext);
+}
+
+static isl_size count_same_name(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned pos, const char *name)
+{
+ enum isl_dim_type t;
+ int p;
+ isl_size s;
+ int count = 0;
+
+ for (t = isl_dim_param; t <= type && t <= isl_dim_out; ++t) {
+ s = t == type ? pos : isl_space_dim(space, t);
+ if (s < 0)
+ return isl_size_error;
+ for (p = 0; p < s; ++p) {
+ const char *n = isl_space_get_dim_name(space, t, p);
+ if (n && !strcmp(n, name))
+ count++;
+ }
+ }
+ return count;
+}
+
+/* Print the name of the variable of type "type" and position "pos"
+ * in "space" to "p".
+ */
+static __isl_give isl_printer *print_name(__isl_keep isl_space *space,
+ __isl_take isl_printer *p, enum isl_dim_type type, unsigned pos,
+ int latex)
+{
+ const char *name;
+ char buffer[20];
+ isl_size primes;
+
+ name = type == isl_dim_div ? NULL
+ : isl_space_get_dim_name(space, type, pos);
+
+ if (!name) {
+ const char *prefix;
+ if (type == isl_dim_param)
+ prefix = s_param_prefix[latex];
+ else if (type == isl_dim_div)
+ prefix = s_div_prefix[latex];
+ else if (isl_space_is_set(space) || type == isl_dim_in)
+ prefix = s_input_prefix[latex];
+ else
+ prefix = s_output_prefix[latex];
+ snprintf(buffer, sizeof(buffer), "%s%d", prefix, pos);
+ name = buffer;
+ }
+ primes = count_same_name(space, name == buffer ? isl_dim_div : type,
+ pos, name);
+ if (primes < 0)
+ return isl_printer_free(p);
+ p = isl_printer_print_str(p, name);
+ while (primes-- > 0)
+ p = isl_printer_print_str(p, "'");
+ return p;
+}
+
+static isl_stat pos2type(__isl_keep isl_space *space,
+ enum isl_dim_type *type, unsigned *pos)
+{
+ isl_size n_in = isl_space_dim(space, isl_dim_in);
+ isl_size n_out = isl_space_dim(space, isl_dim_out);
+ isl_size nparam = isl_space_dim(space, isl_dim_param);
+
+ if (n_in < 0 || n_out < 0 || nparam < 0)
+ return isl_stat_error;
+
+ if (*pos < 1 + nparam) {
+ *type = isl_dim_param;
+ *pos -= 1;
+ } else if (*pos < 1 + nparam + n_in) {
+ *type = isl_dim_in;
+ *pos -= 1 + nparam;
+ } else if (*pos < 1 + nparam + n_in + n_out) {
+ *type = isl_dim_out;
+ *pos -= 1 + nparam + n_in;
+ } else {
+ *type = isl_dim_div;
+ *pos -= 1 + nparam + n_in + n_out;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Can the div expression of the integer division at position "row" of "div"
+ * be printed?
+ * In particular, are the div expressions available and does the selected
+ * variable have a known explicit representation?
+ * Furthermore, the Omega format does not allow any div expressions
+ * to be printed.
+ */
+static isl_bool can_print_div_expr(__isl_keep isl_printer *p,
+ __isl_keep isl_mat *div, int pos)
+{
+ if (p->output_format == ISL_FORMAT_OMEGA)
+ return isl_bool_false;
+ if (!div)
+ return isl_bool_false;
+ return isl_bool_not(isl_local_div_is_marked_unknown(div, pos));
+}
+
+static __isl_give isl_printer *print_div(__isl_keep isl_space *space,
+ __isl_keep isl_mat *div, int pos, __isl_take isl_printer *p);
+
+static __isl_give isl_printer *print_term(__isl_keep isl_space *space,
+ __isl_keep isl_mat *div,
+ isl_int c, unsigned pos, __isl_take isl_printer *p, int latex)
+{
+ enum isl_dim_type type;
+ int print_div_def;
+
+ if (!p || !space)
+ return isl_printer_free(p);
+
+ if (pos == 0)
+ return isl_printer_print_isl_int(p, c);
+
+ if (pos2type(space, &type, &pos) < 0)
+ return isl_printer_free(p);
+ print_div_def = type == isl_dim_div && can_print_div_expr(p, div, pos);
+
+ if (isl_int_is_one(c))
+ ;
+ else if (isl_int_is_negone(c))
+ p = isl_printer_print_str(p, "-");
+ else {
+ p = isl_printer_print_isl_int(p, c);
+ if (p->output_format == ISL_FORMAT_C || print_div_def)
+ p = isl_printer_print_str(p, "*");
+ }
+ if (print_div_def)
+ p = print_div(space, div, pos, p);
+ else
+ p = print_name(space, p, type, pos, latex);
+ return p;
+}
+
+static __isl_give isl_printer *print_affine_of_len(__isl_keep isl_space *space,
+ __isl_keep isl_mat *div,
+ __isl_take isl_printer *p, isl_int *c, int len)
+{
+ int i;
+ int first;
+
+ for (i = 0, first = 1; i < len; ++i) {
+ int flip = 0;
+ if (isl_int_is_zero(c[i]))
+ continue;
+ if (!first) {
+ if (isl_int_is_neg(c[i])) {
+ flip = 1;
+ isl_int_neg(c[i], c[i]);
+ p = isl_printer_print_str(p, " - ");
+ } else
+ p = isl_printer_print_str(p, " + ");
+ }
+ first = 0;
+ p = print_term(space, div, c[i], i, p, 0);
+ if (flip)
+ isl_int_neg(c[i], c[i]);
+ }
+ if (first)
+ p = isl_printer_print_str(p, "0");
+ return p;
+}
+
+/* Print an affine expression "c"
+ * to "p", with the variable names taken from "space" and
+ * the integer division definitions taken from "div".
+ */
+static __isl_give isl_printer *print_affine(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div, isl_int *c)
+{
+ isl_size n_div, total;
+ unsigned len;
+
+ total = isl_space_dim(space, isl_dim_all);
+ n_div = isl_mat_rows(div);
+ if (total < 0 || n_div < 0)
+ return isl_printer_free(p);
+ len = 1 + total + n_div;
+ return print_affine_of_len(space, div, p, c, len);
+}
+
+/* offset is the offset of local_space inside data->type of data->space.
+ */
+static __isl_give isl_printer *print_nested_var_list(__isl_take isl_printer *p,
+ __isl_keep isl_space *local_space, enum isl_dim_type local_type,
+ struct isl_print_space_data *data, int offset)
+{
+ int i;
+ isl_size dim;
+
+ if (data->space != local_space && local_type == isl_dim_out)
+ offset += local_space->n_in;
+
+ dim = isl_space_dim(local_space, local_type);
+ if (dim < 0)
+ return isl_printer_free(p);
+ for (i = 0; i < dim; ++i) {
+ if (i)
+ p = isl_printer_print_str(p, ", ");
+ if (data->print_dim)
+ p = data->print_dim(p, data, offset + i);
+ else
+ p = print_name(data->space, p, data->type, offset + i,
+ data->latex);
+ }
+ return p;
+}
+
+static __isl_give isl_printer *print_var_list(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, enum isl_dim_type type)
+{
+ struct isl_print_space_data data = { .space = space, .type = type };
+
+ return print_nested_var_list(p, space, type, &data, 0);
+}
+
+static __isl_give isl_printer *print_nested_map_dim(__isl_take isl_printer *p,
+ __isl_keep isl_space *local_dim,
+ struct isl_print_space_data *data, int offset);
+
+static __isl_give isl_printer *print_nested_tuple(__isl_take isl_printer *p,
+ __isl_keep isl_space *local_space, enum isl_dim_type local_type,
+ struct isl_print_space_data *data, int offset)
+{
+ const char *name = NULL;
+ isl_size n = isl_space_dim(local_space, local_type);
+
+ if (n < 0)
+ return isl_printer_free(p);
+ if ((local_type == isl_dim_in || local_type == isl_dim_out)) {
+ name = isl_space_get_tuple_name(local_space, local_type);
+ if (name) {
+ if (data->latex)
+ p = isl_printer_print_str(p, "\\mathrm{");
+ p = isl_printer_print_str(p, name);
+ if (data->latex)
+ p = isl_printer_print_str(p, "}");
+ }
+ }
+ if (!data->latex || n != 1 || name)
+ p = isl_printer_print_str(p, s_open_list[data->latex]);
+ if ((local_type == isl_dim_in || local_type == isl_dim_out) &&
+ local_space->nested[local_type - isl_dim_in]) {
+ if (data->space != local_space && local_type == isl_dim_out)
+ offset += local_space->n_in;
+ p = print_nested_map_dim(p,
+ local_space->nested[local_type - isl_dim_in],
+ data, offset);
+ } else
+ p = print_nested_var_list(p, local_space, local_type, data,
+ offset);
+ if (!data->latex || n != 1 || name)
+ p = isl_printer_print_str(p, s_close_list[data->latex]);
+ return p;
+}
+
+static __isl_give isl_printer *print_tuple(__isl_keep isl_space *space,
+ __isl_take isl_printer *p, enum isl_dim_type type,
+ struct isl_print_space_data *data)
+{
+ data->space = space;
+ data->type = type;
+ return print_nested_tuple(p, space, type, data, 0);
+}
+
+static __isl_give isl_printer *print_nested_map_dim(__isl_take isl_printer *p,
+ __isl_keep isl_space *local_dim,
+ struct isl_print_space_data *data, int offset)
+{
+ p = print_nested_tuple(p, local_dim, isl_dim_in, data, offset);
+ p = isl_printer_print_str(p, s_to[data->latex]);
+ p = print_nested_tuple(p, local_dim, isl_dim_out, data, offset);
+
+ return p;
+}
+
+__isl_give isl_printer *isl_print_space(__isl_keep isl_space *space,
+ __isl_take isl_printer *p, int rational,
+ struct isl_print_space_data *data)
+{
+ if (rational && !data->latex)
+ p = isl_printer_print_str(p, "rat: ");
+ if (isl_space_is_params(space))
+ ;
+ else if (isl_space_is_set(space))
+ p = print_tuple(space, p, isl_dim_set, data);
+ else {
+ p = print_tuple(space, p, isl_dim_in, data);
+ p = isl_printer_print_str(p, s_to[data->latex]);
+ p = print_tuple(space, p, isl_dim_out, data);
+ }
+
+ return p;
+}
+
+static __isl_give isl_printer *print_omega_parameters(
+ __isl_keep isl_space *space, __isl_take isl_printer *p)
+{
+ isl_size nparam = isl_space_dim(space, isl_dim_param);
+
+ if (nparam < 0)
+ return isl_printer_free(p);
+ if (nparam == 0)
+ return p;
+
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "symbolic ");
+ p = print_var_list(p, space, isl_dim_param);
+ p = isl_printer_print_str(p, ";");
+ p = isl_printer_end_line(p);
+ return p;
+}
+
+/* Does the inequality constraint following "i" in "bmap"
+ * have an opposite value for the same last coefficient?
+ * "last" is the position of the last coefficient of inequality "i".
+ * If the next constraint is a div constraint, then it is ignored
+ * since div constraints are not printed.
+ */
+static isl_bool next_is_opposite(__isl_keep isl_basic_map *bmap, int i,
+ int last)
+{
+ int r;
+ isl_size total = isl_basic_map_dim(bmap, isl_dim_all);
+ unsigned o_div = isl_basic_map_offset(bmap, isl_dim_div);
+
+ if (total < 0)
+ return isl_bool_error;
+ if (i + 1 >= bmap->n_ineq)
+ return isl_bool_false;
+ if (isl_seq_last_non_zero(bmap->ineq[i + 1], 1 + total) != last)
+ return isl_bool_false;
+ if (last >= o_div) {
+ isl_bool is_div;
+ is_div = isl_basic_map_is_div_constraint(bmap,
+ bmap->ineq[i + 1], last - o_div);
+ if (is_div < 0)
+ return isl_bool_error;
+ if (is_div)
+ return isl_bool_false;
+ }
+ r = isl_int_abs_eq(bmap->ineq[i][last], bmap->ineq[i + 1][last]) &&
+ !isl_int_eq(bmap->ineq[i][last], bmap->ineq[i + 1][last]);
+ return isl_bool_ok(r);
+}
+
+/* Return a string representation of the operator used when
+ * printing a constraint where the LHS is greater than or equal to the LHS
+ * (sign > 0) or smaller than or equal to the LHS (sign < 0).
+ * If "strict" is set, then return the strict version of the comparison
+ * operator.
+ */
+static const char *constraint_op(int sign, int strict, int latex)
+{
+ if (strict)
+ return sign < 0 ? "<" : ">";
+ if (sign < 0)
+ return s_le[latex];
+ else
+ return s_ge[latex];
+}
+
+/* Print one side of a constraint "c" to "p", with
+ * the variable names taken from "space" and the integer division definitions
+ * taken from "div".
+ * "last" is the position of the last non-zero coefficient.
+ * Let c' be the result of zeroing out this coefficient, then
+ * the partial constraint
+ *
+ * c' op
+ *
+ * is printed.
+ */
+static __isl_give isl_printer *print_half_constraint(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div,
+ isl_int *c, int last, const char *op, int latex)
+{
+ isl_int_set_si(c[last], 0);
+ p = print_affine(p, space, div, c);
+
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_str(p, op);
+ p = isl_printer_print_str(p, " ");
+
+ return p;
+}
+
+/* Print a constraint "c" to "p", with the variable names
+ * taken from "space" and the integer division definitions taken from "div".
+ * "last" is the position of the last non-zero coefficient, which is
+ * moreover assumed to be negative.
+ * Let c' be the result of zeroing out this coefficient, then
+ * the constraint is printed in the form
+ *
+ * -c[last] op c'
+ */
+static __isl_give isl_printer *print_constraint(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div,
+ isl_int *c, int last, const char *op, int latex)
+{
+ isl_int_abs(c[last], c[last]);
+
+ p = print_term(space, div, c[last], last, p, latex);
+
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_str(p, op);
+ p = isl_printer_print_str(p, " ");
+
+ isl_int_set_si(c[last], 0);
+ p = print_affine(p, space, div, c);
+
+ return p;
+}
+
+/* Given an integer division
+ *
+ * floor(f/m)
+ *
+ * at position "pos" in "div", print the corresponding modulo expression
+ *
+ * (f) mod m
+ *
+ * to "p". The variable names are taken from "space", while any
+ * nested integer division definitions are taken from "div".
+ */
+static __isl_give isl_printer *print_mod(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div, int pos,
+ int latex)
+{
+ if (!p || !div)
+ return isl_printer_free(p);
+
+ p = isl_printer_print_str(p, "(");
+ p = print_affine_of_len(space, div, p,
+ div->row[pos] + 1, div->n_col - 1);
+ p = isl_printer_print_str(p, ") ");
+ p = isl_printer_print_str(p, s_mod[latex]);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_isl_int(p, div->row[pos][0]);
+ return p;
+}
+
+/* Given an equality constraint with a non-zero coefficient "c"
+ * in position "pos", is this term of the form
+ *
+ * a m floor(g/m),
+ *
+ * with c = a m?
+ * Return the position of the corresponding integer division if so.
+ * Return the number of integer divisions if not.
+ * Return isl_size_error on error.
+ *
+ * Modulo constraints are currently not printed in C format.
+ * Other than that, "pos" needs to correspond to an integer division
+ * with explicit representation and "c" needs to be a multiple
+ * of the denominator of the integer division.
+ */
+static isl_size print_as_modulo_pos(__isl_keep isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div, unsigned pos,
+ isl_int c)
+{
+ isl_bool can_print;
+ isl_size n_div;
+ enum isl_dim_type type;
+
+ n_div = isl_mat_rows(div);
+ if (!p || !space || n_div < 0)
+ return isl_size_error;
+ if (p->output_format == ISL_FORMAT_C)
+ return n_div;
+ if (pos2type(space, &type, &pos) < 0)
+ return isl_size_error;
+ if (type != isl_dim_div)
+ return n_div;
+ can_print = can_print_div_expr(p, div, pos);
+ if (can_print < 0)
+ return isl_size_error;
+ if (!can_print)
+ return n_div;
+ if (!isl_int_is_divisible_by(c, div->row[pos][0]))
+ return n_div;
+ return pos;
+}
+
+/* Print equality constraint "c" to "p" as a modulo constraint,
+ * with the variable names taken from "space" and
+ * the integer division definitions taken from "div".
+ * "last" is the position of the last non-zero coefficient, which is
+ * moreover assumed to be negative and a multiple of the denominator
+ * of the corresponding integer division. "div_pos" is the corresponding
+ * position in the sequence of integer divisions.
+ *
+ * The equality is of the form
+ *
+ * f - a m floor(g/m) = 0.
+ *
+ * Print it as
+ *
+ * a (g mod m) = -f + a g
+ */
+static __isl_give isl_printer *print_eq_mod_constraint(
+ __isl_take isl_printer *p, __isl_keep isl_space *space,
+ __isl_keep isl_mat *div, unsigned div_pos,
+ isl_int *c, int last, int latex)
+{
+ isl_ctx *ctx;
+ int multiple;
+
+ ctx = isl_printer_get_ctx(p);
+ isl_int_divexact(c[last], c[last], div->row[div_pos][0]);
+ isl_int_abs(c[last], c[last]);
+ multiple = !isl_int_is_one(c[last]);
+ if (multiple) {
+ p = isl_printer_print_isl_int(p, c[last]);
+ p = isl_printer_print_str(p, "*(");
+ }
+ p = print_mod(p, space, div, div_pos, latex);
+ if (multiple)
+ p = isl_printer_print_str(p, ")");
+ p = isl_printer_print_str(p, " = ");
+ isl_seq_combine(c, ctx->negone, c,
+ c[last], div->row[div_pos] + 1, last);
+ isl_int_set_si(c[last], 0);
+ p = print_affine(p, space, div, c);
+ return p;
+}
+
+/* Print equality constraint "c" to "p", with the variable names
+ * taken from "space" and the integer division definitions taken from "div".
+ * "last" is the position of the last non-zero coefficient, which is
+ * moreover assumed to be negative.
+ *
+ * If possible, print the equality constraint as a modulo constraint.
+ */
+static __isl_give isl_printer *print_eq_constraint(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div, isl_int *c,
+ int last, int latex)
+{
+ isl_size n_div;
+ isl_size div_pos;
+
+ n_div = isl_mat_rows(div);
+ div_pos = print_as_modulo_pos(p, space, div, last, c[last]);
+ if (n_div < 0 || div_pos < 0)
+ return isl_printer_free(p);
+ if (div_pos < n_div)
+ return print_eq_mod_constraint(p, space, div, div_pos,
+ c, last, latex);
+ return print_constraint(p, space, div, c, last, "=", latex);
+}
+
+/* Print the constraints of "bmap" to "p".
+ * The names of the variables are taken from "space" and
+ * the integer division definitions are taken from "div".
+ * Div constraints are only printed in "dump" mode.
+ * The constraints are sorted prior to printing (except in "dump" mode).
+ *
+ * If x is the last variable with a non-zero coefficient,
+ * then a lower bound
+ *
+ * f - a x >= 0
+ *
+ * is printed as
+ *
+ * a x <= f
+ *
+ * while an upper bound
+ *
+ * f + a x >= 0
+ *
+ * is printed as
+ *
+ * a x >= -f
+ *
+ * If the next constraint has an opposite sign for the same last coefficient,
+ * then it is printed as
+ *
+ * f >= a x
+ *
+ * or
+ *
+ * -f <= a x
+ *
+ * instead. In fact, the "a x" part is not printed explicitly, but
+ * reused from the next constraint, which is therefore treated as
+ * a first constraint in the conjunction.
+ *
+ * If the constant term of "f" is -1, then "f" is replaced by "f + 1" and
+ * the comparison operator is replaced by the strict variant.
+ * Essentially, ">= 1" is replaced by "> 0".
+ */
+static __isl_give isl_printer *print_constraints(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div,
+ __isl_take isl_printer *p, int latex)
+{
+ int i;
+ isl_vec *c = NULL;
+ int rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL);
+ isl_size total = isl_basic_map_dim(bmap, isl_dim_all);
+ unsigned o_div = isl_basic_map_offset(bmap, isl_dim_div);
+ int first = 1;
+ int dump;
+
+ if (total < 0 || !p)
+ return isl_printer_free(p);
+ bmap = isl_basic_map_copy(bmap);
+ dump = p->dump;
+ if (!dump)
+ bmap = isl_basic_map_sort_constraints(bmap);
+ if (!bmap)
+ goto error;
+
+ c = isl_vec_alloc(bmap->ctx, 1 + total);
+ if (!c)
+ goto error;
+
+ for (i = bmap->n_eq - 1; i >= 0; --i) {
+ int l = isl_seq_last_non_zero(bmap->eq[i], 1 + total);
+ if (l < 0) {
+ if (i != bmap->n_eq - 1)
+ p = isl_printer_print_str(p, s_and[latex]);
+ p = isl_printer_print_str(p, "0 = 0");
+ continue;
+ }
+ if (!first)
+ p = isl_printer_print_str(p, s_and[latex]);
+ if (isl_int_is_neg(bmap->eq[i][l]))
+ isl_seq_cpy(c->el, bmap->eq[i], 1 + total);
+ else
+ isl_seq_neg(c->el, bmap->eq[i], 1 + total);
+ p = print_eq_constraint(p, space, div, c->el, l, latex);
+ first = 0;
+ }
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ isl_bool combine;
+ int l = isl_seq_last_non_zero(bmap->ineq[i], 1 + total);
+ int strict;
+ int s;
+ const char *op;
+ if (l < 0)
+ continue;
+ if (!dump && l >= o_div &&
+ can_print_div_expr(p, div, l - o_div)) {
+ isl_bool is_div;
+ is_div = isl_basic_map_is_div_constraint(bmap,
+ bmap->ineq[i], l - o_div);
+ if (is_div < 0)
+ goto error;
+ if (is_div)
+ continue;
+ }
+ if (!first)
+ p = isl_printer_print_str(p, s_and[latex]);
+ s = isl_int_sgn(bmap->ineq[i][l]);
+ strict = !rational && isl_int_is_negone(bmap->ineq[i][0]);
+ if (s < 0)
+ isl_seq_cpy(c->el, bmap->ineq[i], 1 + total);
+ else
+ isl_seq_neg(c->el, bmap->ineq[i], 1 + total);
+ if (strict)
+ isl_int_set_si(c->el[0], 0);
+ combine = dump ? isl_bool_false : next_is_opposite(bmap, i, l);
+ if (combine < 0)
+ goto error;
+ if (combine) {
+ op = constraint_op(-s, strict, latex);
+ p = print_half_constraint(p, space, div, c->el, l,
+ op, latex);
+ first = 1;
+ } else {
+ op = constraint_op(s, strict, latex);
+ p = print_constraint(p, space, div, c->el, l,
+ op, latex);
+ first = 0;
+ }
+ }
+
+ isl_basic_map_free(bmap);
+ isl_vec_free(c);
+
+ return p;
+error:
+ isl_basic_map_free(bmap);
+ isl_vec_free(c);
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *print_div(__isl_keep isl_space *space,
+ __isl_keep isl_mat *div, int pos, __isl_take isl_printer *p)
+{
+ int c;
+
+ if (!p || !div)
+ return isl_printer_free(p);
+
+ c = p->output_format == ISL_FORMAT_C;
+ p = isl_printer_print_str(p, c ? "floord(" : "floor((");
+ p = print_affine_of_len(space, div, p,
+ div->row[pos] + 1, div->n_col - 1);
+ p = isl_printer_print_str(p, c ? ", " : ")/");
+ p = isl_printer_print_isl_int(p, div->row[pos][0]);
+ p = isl_printer_print_str(p, ")");
+ return p;
+}
+
+/* Print a comma separated list of div names, except those that have
+ * a definition that can be printed.
+ * If "print_defined_divs" is set, then those div names are printed
+ * as well, along with their definitions.
+ */
+static __isl_give isl_printer *print_div_list(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div, int latex,
+ int print_defined_divs)
+{
+ int i;
+ int first = 1;
+ isl_size n_div;
+
+ n_div = isl_mat_rows(div);
+ if (!p || !space || n_div < 0)
+ return isl_printer_free(p);
+
+ for (i = 0; i < n_div; ++i) {
+ if (!print_defined_divs && can_print_div_expr(p, div, i))
+ continue;
+ if (!first)
+ p = isl_printer_print_str(p, ", ");
+ p = print_name(space, p, isl_dim_div, i, latex);
+ first = 0;
+ if (!can_print_div_expr(p, div, i))
+ continue;
+ p = isl_printer_print_str(p, " = ");
+ p = print_div(space, div, i, p);
+ }
+
+ return p;
+}
+
+/* Does printing an object with local variables described by "div"
+ * require an "exists" clause?
+ * That is, are there any local variables without an explicit representation?
+ * An exists clause is also needed in "dump" mode because
+ * explicit div representations are not printed inline in that case.
+ */
+static isl_bool need_exists(__isl_keep isl_printer *p, __isl_keep isl_mat *div)
+{
+ int i;
+ isl_size n;
+
+ n = isl_mat_rows(div);
+ if (!p || n < 0)
+ return isl_bool_error;
+ if (n == 0)
+ return isl_bool_false;
+ if (p->dump)
+ return isl_bool_true;
+ for (i = 0; i < n; ++i)
+ if (!can_print_div_expr(p, div, i))
+ return isl_bool_true;
+ return isl_bool_false;
+}
+
+/* Print the start of an exists clause, i.e.,
+ *
+ * (exists variables:
+ *
+ * In dump mode, local variables with an explicit definition are printed
+ * as well because they will not be printed inline.
+ */
+static __isl_give isl_printer *open_exists(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div, int latex)
+{
+ int dump;
+
+ if (!p)
+ return NULL;
+
+ dump = p->dump;
+ p = isl_printer_print_str(p, s_open_exists[latex]);
+ p = print_div_list(p, space, div, latex, dump);
+ p = isl_printer_print_str(p, ": ");
+
+ return p;
+}
+
+/* Remove the explicit representations of all local variables in "div".
+ */
+static __isl_give isl_mat *mark_all_unknown(__isl_take isl_mat *div)
+{
+ int i;
+ isl_size n_div;
+
+ n_div = isl_mat_rows(div);
+ if (n_div < 0)
+ return isl_mat_free(div);
+
+ for (i = 0; i < n_div; ++i)
+ div = isl_mat_set_element_si(div, i, 0, 0);
+ return div;
+}
+
+/* Print the constraints of "bmap" to "p".
+ * The names of the variables are taken from "space".
+ * "latex" is set if the constraints should be printed in LaTeX format.
+ * Do not print inline explicit div representations in "dump" mode.
+ */
+static __isl_give isl_printer *print_disjunct(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_space *space, __isl_take isl_printer *p, int latex)
+{
+ int dump;
+ isl_mat *div;
+ isl_bool exists;
+
+ if (!p)
+ return NULL;
+ dump = p->dump;
+ div = isl_basic_map_get_divs(bmap);
+ exists = need_exists(p, div);
+ if (exists >= 0 && exists)
+ p = open_exists(p, space, div, latex);
+
+ if (dump)
+ div = mark_all_unknown(div);
+ p = print_constraints(bmap, space, div, p, latex);
+ isl_mat_free(div);
+
+ if (exists >= 0 && exists)
+ p = isl_printer_print_str(p, s_close_exists[latex]);
+ return p;
+}
+
+/* Print a colon followed by the constraints of "bmap"
+ * to "p", provided there are any constraints.
+ * The names of the variables are taken from "space".
+ * "latex" is set if the constraints should be printed in LaTeX format.
+ */
+static __isl_give isl_printer *print_optional_disjunct(
+ __isl_keep isl_basic_map *bmap, __isl_keep isl_space *space,
+ __isl_take isl_printer *p, int latex)
+{
+ if (isl_basic_map_plain_is_universe(bmap))
+ return p;
+
+ p = isl_printer_print_str(p, ": ");
+ p = print_disjunct(bmap, space, p, latex);
+
+ return p;
+}
+
+static __isl_give isl_printer *basic_map_print_omega(
+ __isl_keep isl_basic_map *bmap, __isl_take isl_printer *p)
+{
+ p = isl_printer_print_str(p, "{ [");
+ p = print_var_list(p, bmap->dim, isl_dim_in);
+ p = isl_printer_print_str(p, "] -> [");
+ p = print_var_list(p, bmap->dim, isl_dim_out);
+ p = isl_printer_print_str(p, "] ");
+ p = print_optional_disjunct(bmap, bmap->dim, p, 0);
+ p = isl_printer_print_str(p, " }");
+ return p;
+}
+
+static __isl_give isl_printer *basic_set_print_omega(
+ __isl_keep isl_basic_set *bset, __isl_take isl_printer *p)
+{
+ p = isl_printer_print_str(p, "{ [");
+ p = print_var_list(p, bset->dim, isl_dim_set);
+ p = isl_printer_print_str(p, "] ");
+ p = print_optional_disjunct(bset, bset->dim, p, 0);
+ p = isl_printer_print_str(p, " }");
+ return p;
+}
+
+static __isl_give isl_printer *isl_map_print_omega(__isl_keep isl_map *map,
+ __isl_take isl_printer *p)
+{
+ int i;
+
+ for (i = 0; i < map->n; ++i) {
+ if (i)
+ p = isl_printer_print_str(p, " union ");
+ p = basic_map_print_omega(map->p[i], p);
+ }
+ return p;
+}
+
+static __isl_give isl_printer *isl_set_print_omega(__isl_keep isl_set *set,
+ __isl_take isl_printer *p)
+{
+ int i;
+
+ for (i = 0; i < set->n; ++i) {
+ if (i)
+ p = isl_printer_print_str(p, " union ");
+ p = basic_set_print_omega(set->p[i], p);
+ }
+ return p;
+}
+
+/* Print the list of parameters in "space", followed by an arrow, to "p",
+ * if there are any parameters.
+ */
+static __isl_give isl_printer *print_param_tuple(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, struct isl_print_space_data *data)
+{
+ isl_size nparam;
+
+ nparam = isl_space_dim(space, isl_dim_param);
+ if (!p || nparam < 0)
+ return isl_printer_free(p);
+ if (nparam == 0)
+ return p;
+
+ p = print_tuple(space, p, isl_dim_param, data);
+ p = isl_printer_print_str(p, s_to[data->latex]);
+
+ return p;
+}
+
+static __isl_give isl_printer *isl_basic_map_print_isl(
+ __isl_keep isl_basic_map *bmap, __isl_take isl_printer *p,
+ int latex)
+{
+ struct isl_print_space_data data = { .latex = latex };
+ int rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL);
+
+ p = print_param_tuple(p, bmap->dim, &data);
+ p = isl_printer_print_str(p, "{ ");
+ p = isl_print_space(bmap->dim, p, rational, &data);
+ p = isl_printer_print_str(p, " : ");
+ p = print_disjunct(bmap, bmap->dim, p, latex);
+ p = isl_printer_print_str(p, " }");
+ return p;
+}
+
+/* Print the disjuncts of a map (or set) "map" to "p".
+ * The names of the variables are taken from "space".
+ * "latex" is set if the constraints should be printed in LaTeX format.
+ */
+static __isl_give isl_printer *print_disjuncts_core(__isl_keep isl_map *map,
+ __isl_keep isl_space *space, __isl_take isl_printer *p, int latex)
+{
+ int i;
+
+ if (map->n == 0)
+ p = isl_printer_print_str(p, "false");
+ for (i = 0; i < map->n; ++i) {
+ if (i)
+ p = isl_printer_print_str(p, s_or[latex]);
+ if (map->n > 1 && map->p[i]->n_eq + map->p[i]->n_ineq > 1)
+ p = isl_printer_print_str(p, "(");
+ p = print_disjunct(map->p[i], space, p, latex);
+ if (map->n > 1 && map->p[i]->n_eq + map->p[i]->n_ineq > 1)
+ p = isl_printer_print_str(p, ")");
+ }
+ return p;
+}
+
+/* Print the disjuncts of a map (or set) "map" to "p".
+ * The names of the variables are taken from "space".
+ * "hull" describes constraints shared by all disjuncts of "map".
+ * "latex" is set if the constraints should be printed in LaTeX format.
+ *
+ * Print the disjuncts as a conjunction of "hull" and
+ * the result of removing the constraints of "hull" from "map".
+ * If this result turns out to be the universe, then simply print "hull".
+ */
+static __isl_give isl_printer *print_disjuncts_in_hull(__isl_keep isl_map *map,
+ __isl_keep isl_space *space, __isl_take isl_basic_map *hull,
+ __isl_take isl_printer *p, int latex)
+{
+ isl_bool is_universe;
+
+ p = print_disjunct(hull, space, p, latex);
+ map = isl_map_plain_gist_basic_map(isl_map_copy(map), hull);
+ is_universe = isl_map_plain_is_universe(map);
+ if (is_universe < 0)
+ goto error;
+ if (!is_universe) {
+ p = isl_printer_print_str(p, s_and[latex]);
+ p = isl_printer_print_str(p, "(");
+ p = print_disjuncts_core(map, space, p, latex);
+ p = isl_printer_print_str(p, ")");
+ }
+ isl_map_free(map);
+
+ return p;
+error:
+ isl_map_free(map);
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print the disjuncts of a map (or set) "map" to "p".
+ * The names of the variables are taken from "space".
+ * "latex" is set if the constraints should be printed in LaTeX format.
+ *
+ * If there are at least two disjuncts and "dump" mode is not turned out,
+ * check for any shared constraints among all disjuncts.
+ * If there are any, then print them separately in print_disjuncts_in_hull.
+ */
+static __isl_give isl_printer *print_disjuncts(__isl_keep isl_map *map,
+ __isl_keep isl_space *space, __isl_take isl_printer *p, int latex)
+{
+ if (isl_map_plain_is_universe(map))
+ return p;
+
+ p = isl_printer_print_str(p, s_such_that[latex]);
+ if (!p)
+ return NULL;
+
+ if (!p->dump && map->n >= 2) {
+ isl_basic_map *hull;
+ isl_bool is_universe;
+
+ hull = isl_map_plain_unshifted_simple_hull(isl_map_copy(map));
+ is_universe = isl_basic_map_plain_is_universe(hull);
+ if (is_universe < 0)
+ p = isl_printer_free(p);
+ else if (!is_universe)
+ return print_disjuncts_in_hull(map, space, hull,
+ p, latex);
+ isl_basic_map_free(hull);
+ }
+
+ return print_disjuncts_core(map, space, p, latex);
+}
+
+/* Print the disjuncts of a map (or set).
+ * The names of the variables are taken from "space".
+ * "latex" is set if the constraints should be printed in LaTeX format.
+ *
+ * If the map turns out to be a universal parameter domain, then
+ * we need to print the colon. Otherwise, the output looks identical
+ * to the empty set.
+ */
+static __isl_give isl_printer *print_disjuncts_map(__isl_keep isl_map *map,
+ __isl_keep isl_space *space, __isl_take isl_printer *p, int latex)
+{
+ if (isl_map_plain_is_universe(map) && isl_space_is_params(map->dim))
+ return isl_printer_print_str(p, s_such_that[latex]);
+ else
+ return print_disjuncts(map, space, p, latex);
+}
+
+/* Print the disjuncts of a set.
+ * The names of the variables are taken from "space".
+ * "latex" is set if the constraints should be printed in LaTeX format.
+ */
+static __isl_give isl_printer *print_disjuncts_set(__isl_keep isl_set *set,
+ __isl_keep isl_space *space, __isl_take isl_printer *p, int latex)
+{
+ return print_disjuncts_map(set_to_map(set), space, p, latex);
+}
+
+struct isl_aff_split {
+ isl_basic_map *aff;
+ isl_map *map;
+};
+
+static void free_split(__isl_take struct isl_aff_split *split, int n)
+{
+ int i;
+
+ if (!split)
+ return;
+
+ for (i = 0; i < n; ++i) {
+ isl_basic_map_free(split[i].aff);
+ isl_map_free(split[i].map);
+ }
+
+ free(split);
+}
+
+static __isl_give isl_basic_map *get_aff(__isl_take isl_basic_map *bmap)
+{
+ int i, j;
+ isl_size nparam, n_in, n_out, total;
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ return NULL;
+ bmap = isl_basic_map_free_inequality(bmap, bmap->n_ineq);
+
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (n_in < 0 || n_out < 0 || nparam < 0 || total < 0)
+ return isl_basic_map_free(bmap);
+
+ for (i = bmap->n_eq - 1; i >= 0; --i) {
+ j = isl_seq_last_non_zero(bmap->eq[i] + 1, total);
+ if (j >= nparam && j < nparam + n_in + n_out &&
+ (isl_int_is_one(bmap->eq[i][1 + j]) ||
+ isl_int_is_negone(bmap->eq[i][1 + j])))
+ continue;
+ if (isl_basic_map_drop_equality(bmap, i) < 0)
+ goto error;
+ }
+
+ bmap = isl_basic_map_finalize(bmap);
+
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+static int aff_split_cmp(const void *p1, const void *p2, void *user)
+{
+ const struct isl_aff_split *s1, *s2;
+ s1 = (const struct isl_aff_split *) p1;
+ s2 = (const struct isl_aff_split *) p2;
+
+ return isl_basic_map_plain_cmp(s1->aff, s2->aff);
+}
+
+static __isl_give isl_basic_map *drop_aff(__isl_take isl_basic_map *bmap,
+ __isl_keep isl_basic_map *aff)
+{
+ int i, j;
+ isl_size v_div;
+
+ v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ if (v_div < 0 || !aff)
+ goto error;
+
+ for (i = bmap->n_eq - 1; i >= 0; --i) {
+ if (isl_seq_first_non_zero(bmap->eq[i] + 1 + v_div,
+ bmap->n_div) != -1)
+ continue;
+ for (j = 0; j < aff->n_eq; ++j) {
+ if (!isl_seq_eq(bmap->eq[i], aff->eq[j], 1 + v_div) &&
+ !isl_seq_is_neg(bmap->eq[i], aff->eq[j], 1 + v_div))
+ continue;
+ if (isl_basic_map_drop_equality(bmap, i) < 0)
+ goto error;
+ break;
+ }
+ }
+
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+static __isl_give struct isl_aff_split *split_aff(__isl_keep isl_map *map)
+{
+ int i, n;
+ struct isl_aff_split *split;
+ isl_ctx *ctx;
+
+ ctx = isl_map_get_ctx(map);
+ split = isl_calloc_array(ctx, struct isl_aff_split, map->n);
+ if (!split)
+ return NULL;
+
+ for (i = 0; i < map->n; ++i) {
+ isl_basic_map *bmap;
+ split[i].aff = get_aff(isl_basic_map_copy(map->p[i]));
+ bmap = isl_basic_map_copy(map->p[i]);
+ bmap = isl_basic_map_cow(bmap);
+ bmap = drop_aff(bmap, split[i].aff);
+ split[i].map = isl_map_from_basic_map(bmap);
+ if (!split[i].aff || !split[i].map)
+ goto error;
+ }
+
+ if (isl_sort(split, map->n, sizeof(struct isl_aff_split),
+ &aff_split_cmp, NULL) < 0)
+ goto error;
+
+ n = map->n;
+ for (i = n - 1; i >= 1; --i) {
+ if (!isl_basic_map_plain_is_equal(split[i - 1].aff,
+ split[i].aff))
+ continue;
+ isl_basic_map_free(split[i].aff);
+ split[i - 1].map = isl_map_union(split[i - 1].map,
+ split[i].map);
+ if (i != n - 1)
+ split[i] = split[n - 1];
+ split[n - 1].aff = NULL;
+ split[n - 1].map = NULL;
+ --n;
+ }
+
+ return split;
+error:
+ free_split(split, map->n);
+ return NULL;
+}
+
+static int defining_equality(__isl_keep isl_basic_map *eq,
+ __isl_keep isl_space *space, enum isl_dim_type type, int pos)
+{
+ int i;
+ isl_size total;
+
+ total = isl_basic_map_dim(eq, isl_dim_all);
+ if (total < 0)
+ return -1;
+
+ pos += isl_space_offset(space, type);
+
+ for (i = 0; i < eq->n_eq; ++i) {
+ if (isl_seq_last_non_zero(eq->eq[i] + 1, total) != pos)
+ continue;
+ if (isl_int_is_one(eq->eq[i][1 + pos]))
+ isl_seq_neg(eq->eq[i], eq->eq[i], 1 + total);
+ return i;
+ }
+
+ return -1;
+}
+
+/* Print dimension "pos" of data->space to "p".
+ *
+ * data->user is assumed to be an isl_basic_map keeping track of equalities.
+ *
+ * If the current dimension is defined by these equalities, then print
+ * the corresponding expression, assigned to the name of the dimension
+ * if there is any. Otherwise, print the name of the dimension.
+ */
+static __isl_give isl_printer *print_dim_eq(__isl_take isl_printer *p,
+ struct isl_print_space_data *data, unsigned pos)
+{
+ isl_basic_map *eq = data->user;
+ int j;
+
+ j = defining_equality(eq, data->space, data->type, pos);
+ if (j >= 0) {
+ if (isl_space_has_dim_name(data->space, data->type, pos)) {
+ p = print_name(data->space, p, data->type, pos,
+ data->latex);
+ p = isl_printer_print_str(p, " = ");
+ }
+ pos += 1 + isl_space_offset(data->space, data->type);
+ p = print_affine_of_len(data->space, NULL, p, eq->eq[j], pos);
+ } else {
+ p = print_name(data->space, p, data->type, pos, data->latex);
+ }
+
+ return p;
+}
+
+static __isl_give isl_printer *print_split_map(__isl_take isl_printer *p,
+ struct isl_aff_split *split, int n, __isl_keep isl_space *space)
+{
+ struct isl_print_space_data data = { 0 };
+ int i;
+ int rational;
+
+ data.print_dim = &print_dim_eq;
+ for (i = 0; i < n; ++i) {
+ if (!split[i].map)
+ break;
+ rational = split[i].map->n > 0 &&
+ ISL_F_ISSET(split[i].map->p[0], ISL_BASIC_MAP_RATIONAL);
+ if (i)
+ p = isl_printer_print_str(p, "; ");
+ data.user = split[i].aff;
+ p = isl_print_space(space, p, rational, &data);
+ p = print_disjuncts_map(split[i].map, space, p, 0);
+ }
+
+ return p;
+}
+
+static __isl_give isl_printer *isl_map_print_isl_body(__isl_keep isl_map *map,
+ __isl_take isl_printer *p)
+{
+ struct isl_print_space_data data = { 0 };
+ struct isl_aff_split *split = NULL;
+ int rational;
+
+ if (!p || !map)
+ return isl_printer_free(p);
+ if (!p->dump && map->n > 0)
+ split = split_aff(map);
+ if (split) {
+ p = print_split_map(p, split, map->n, map->dim);
+ } else {
+ rational = map->n > 0 &&
+ ISL_F_ISSET(map->p[0], ISL_BASIC_MAP_RATIONAL);
+ p = isl_print_space(map->dim, p, rational, &data);
+ p = print_disjuncts_map(map, map->dim, p, 0);
+ }
+ free_split(split, map->n);
+ return p;
+}
+
+static __isl_give isl_printer *isl_map_print_isl(__isl_keep isl_map *map,
+ __isl_take isl_printer *p)
+{
+ struct isl_print_space_data data = { 0 };
+
+ p = print_param_tuple(p, map->dim, &data);
+ p = isl_printer_print_str(p, s_open_set[0]);
+ p = isl_map_print_isl_body(map, p);
+ p = isl_printer_print_str(p, s_close_set[0]);
+ return p;
+}
+
+static __isl_give isl_printer *print_latex_map(__isl_keep isl_map *map,
+ __isl_take isl_printer *p, __isl_keep isl_basic_map *aff)
+{
+ struct isl_print_space_data data = { 0 };
+
+ data.latex = 1;
+ p = print_param_tuple(p, map->dim, &data);
+ p = isl_printer_print_str(p, s_open_set[1]);
+ data.print_dim = &print_dim_eq;
+ data.user = aff;
+ p = isl_print_space(map->dim, p, 0, &data);
+ p = print_disjuncts_map(map, map->dim, p, 1);
+ p = isl_printer_print_str(p, s_close_set[1]);
+
+ return p;
+}
+
+static __isl_give isl_printer *isl_map_print_latex(__isl_keep isl_map *map,
+ __isl_take isl_printer *p)
+{
+ int i;
+ struct isl_aff_split *split = NULL;
+
+ if (map->n > 0)
+ split = split_aff(map);
+
+ if (!split)
+ return print_latex_map(map, p, NULL);
+
+ for (i = 0; i < map->n; ++i) {
+ if (!split[i].map)
+ break;
+ if (i)
+ p = isl_printer_print_str(p, " \\cup ");
+ p = print_latex_map(split[i].map, p, split[i].aff);
+ }
+
+ free_split(split, map->n);
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_basic_map(__isl_take isl_printer *p,
+ __isl_keep isl_basic_map *bmap)
+{
+ if (!p || !bmap)
+ goto error;
+ if (p->output_format == ISL_FORMAT_ISL)
+ return isl_basic_map_print_isl(bmap, p, 0);
+ else if (p->output_format == ISL_FORMAT_OMEGA)
+ return basic_map_print_omega(bmap, p);
+ isl_assert(bmap->ctx, 0, goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+__isl_give isl_printer *isl_printer_print_basic_set(__isl_take isl_printer *p,
+ __isl_keep isl_basic_set *bset)
+{
+ if (!p || !bset)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return isl_basic_map_print_isl(bset, p, 0);
+ else if (p->output_format == ISL_FORMAT_POLYLIB)
+ return isl_basic_set_print_polylib(bset, p, 0);
+ else if (p->output_format == ISL_FORMAT_EXT_POLYLIB)
+ return isl_basic_set_print_polylib(bset, p, 1);
+ else if (p->output_format == ISL_FORMAT_POLYLIB_CONSTRAINTS)
+ return bset_print_constraints_polylib(bset, p);
+ else if (p->output_format == ISL_FORMAT_OMEGA)
+ return basic_set_print_omega(bset, p);
+ isl_assert(p->ctx, 0, goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+__isl_give isl_printer *isl_printer_print_set(__isl_take isl_printer *p,
+ __isl_keep isl_set *set)
+{
+ if (!p || !set)
+ goto error;
+ if (p->output_format == ISL_FORMAT_ISL)
+ return isl_map_print_isl(set_to_map(set), p);
+ else if (p->output_format == ISL_FORMAT_POLYLIB)
+ return isl_set_print_polylib(set, p, 0);
+ else if (p->output_format == ISL_FORMAT_EXT_POLYLIB)
+ return isl_set_print_polylib(set, p, 1);
+ else if (p->output_format == ISL_FORMAT_OMEGA)
+ return isl_set_print_omega(set, p);
+ else if (p->output_format == ISL_FORMAT_LATEX)
+ return isl_map_print_latex(set_to_map(set), p);
+ isl_assert(set->ctx, 0, goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+__isl_give isl_printer *isl_printer_print_map(__isl_take isl_printer *p,
+ __isl_keep isl_map *map)
+{
+ if (!p || !map)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return isl_map_print_isl(map, p);
+ else if (p->output_format == ISL_FORMAT_POLYLIB)
+ return isl_map_print_polylib(map, p, 0);
+ else if (p->output_format == ISL_FORMAT_EXT_POLYLIB)
+ return isl_map_print_polylib(map, p, 1);
+ else if (p->output_format == ISL_FORMAT_OMEGA)
+ return isl_map_print_omega(map, p);
+ else if (p->output_format == ISL_FORMAT_LATEX)
+ return isl_map_print_latex(map, p);
+ isl_assert(map->ctx, 0, goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+struct isl_union_print_data {
+ isl_printer *p;
+ int first;
+};
+
+static isl_stat print_map_body(__isl_take isl_map *map, void *user)
+{
+ struct isl_union_print_data *data;
+ data = (struct isl_union_print_data *)user;
+
+ if (!data->first)
+ data->p = isl_printer_print_str(data->p, "; ");
+ data->first = 0;
+
+ data->p = isl_map_print_isl_body(map, data->p);
+ isl_map_free(map);
+
+ return isl_stat_ok;
+}
+
+/* Print the body of "umap" (everything except the parameter declarations)
+ * to "p" in isl format.
+ */
+static __isl_give isl_printer *isl_printer_print_union_map_isl_body(
+ __isl_take isl_printer *p, __isl_keep isl_union_map *umap)
+{
+ struct isl_union_print_data data;
+
+ p = isl_printer_print_str(p, s_open_set[0]);
+ data.p = p;
+ data.first = 1;
+ isl_union_map_foreach_map(umap, &print_map_body, &data);
+ p = data.p;
+ p = isl_printer_print_str(p, s_close_set[0]);
+ return p;
+}
+
+/* Print the body of "uset" (everything except the parameter declarations)
+ * to "p" in isl format.
+ */
+static __isl_give isl_printer *isl_printer_print_union_set_isl_body(
+ __isl_take isl_printer *p, __isl_keep isl_union_set *uset)
+{
+ return isl_printer_print_union_map_isl_body(p, uset_to_umap(uset));
+}
+
+/* Print the isl_union_map "umap" to "p" in isl format.
+ */
+static __isl_give isl_printer *isl_union_map_print_isl(
+ __isl_keep isl_union_map *umap, __isl_take isl_printer *p)
+{
+ struct isl_print_space_data space_data = { 0 };
+ isl_space *space;
+
+ space = isl_union_map_get_space(umap);
+ p = print_param_tuple(p, space, &space_data);
+ isl_space_free(space);
+
+ p = isl_printer_print_union_map_isl_body(p, umap);
+
+ return p;
+}
+
+static isl_stat print_latex_map_body(__isl_take isl_map *map, void *user)
+{
+ struct isl_union_print_data *data;
+ data = (struct isl_union_print_data *)user;
+
+ if (!data->first)
+ data->p = isl_printer_print_str(data->p, " \\cup ");
+ data->first = 0;
+
+ data->p = isl_map_print_latex(map, data->p);
+ isl_map_free(map);
+
+ return isl_stat_ok;
+}
+
+static __isl_give isl_printer *isl_union_map_print_latex(
+ __isl_keep isl_union_map *umap, __isl_take isl_printer *p)
+{
+ struct isl_union_print_data data = { p, 1 };
+ isl_union_map_foreach_map(umap, &print_latex_map_body, &data);
+ p = data.p;
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_union_map(__isl_take isl_printer *p,
+ __isl_keep isl_union_map *umap)
+{
+ if (!p || !umap)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return isl_union_map_print_isl(umap, p);
+ if (p->output_format == ISL_FORMAT_LATEX)
+ return isl_union_map_print_latex(umap, p);
+
+ isl_die(p->ctx, isl_error_invalid,
+ "invalid output format for isl_union_map", goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+__isl_give isl_printer *isl_printer_print_union_set(__isl_take isl_printer *p,
+ __isl_keep isl_union_set *uset)
+{
+ if (!p || !uset)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return isl_union_map_print_isl(uset_to_umap(uset), p);
+ if (p->output_format == ISL_FORMAT_LATEX)
+ return isl_union_map_print_latex(uset_to_umap(uset), p);
+
+ isl_die(p->ctx, isl_error_invalid,
+ "invalid output format for isl_union_set", goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static isl_size poly_rec_n_non_zero(__isl_keep isl_poly_rec *rec)
+{
+ int i;
+ int n;
+
+ if (!rec)
+ return isl_size_error;
+
+ for (i = 0, n = 0; i < rec->n; ++i) {
+ isl_bool is_zero = isl_poly_is_zero(rec->p[i]);
+
+ if (is_zero < 0)
+ return isl_size_error;
+ if (!is_zero)
+ ++n;
+ }
+
+ return n;
+}
+
+static __isl_give isl_printer *poly_print_cst(__isl_keep isl_poly *poly,
+ __isl_take isl_printer *p, int first)
+{
+ isl_poly_cst *cst;
+ int neg;
+
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ goto error;
+ neg = !first && isl_int_is_neg(cst->n);
+ if (!first)
+ p = isl_printer_print_str(p, neg ? " - " : " + ");
+ if (neg)
+ isl_int_neg(cst->n, cst->n);
+ if (isl_int_is_zero(cst->d)) {
+ int sgn = isl_int_sgn(cst->n);
+ p = isl_printer_print_str(p, sgn < 0 ? "-infty" :
+ sgn == 0 ? "NaN" : "infty");
+ } else
+ p = isl_printer_print_isl_int(p, cst->n);
+ if (neg)
+ isl_int_neg(cst->n, cst->n);
+ if (!isl_int_is_zero(cst->d) && !isl_int_is_one(cst->d)) {
+ p = isl_printer_print_str(p, "/");
+ p = isl_printer_print_isl_int(p, cst->d);
+ }
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *print_base(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div, int var)
+{
+ isl_size total;
+
+ total = isl_space_dim(space, isl_dim_all);
+ if (total < 0)
+ return isl_printer_free(p);
+ if (var < total)
+ p = print_term(space, NULL, space->ctx->one, 1 + var, p, 0);
+ else
+ p = print_div(space, div, var - total, p);
+ return p;
+}
+
+static __isl_give isl_printer *print_pow(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div, int var, int exp)
+{
+ p = print_base(p, space, div, var);
+ if (exp == 1)
+ return p;
+ if (p->output_format == ISL_FORMAT_C) {
+ int i;
+ for (i = 1; i < exp; ++i) {
+ p = isl_printer_print_str(p, "*");
+ p = print_base(p, space, div, var);
+ }
+ } else {
+ p = isl_printer_print_str(p, "^");
+ p = isl_printer_print_int(p, exp);
+ }
+ return p;
+}
+
+/* Print the polynomial "poly" defined over the domain space "space" and
+ * local variables defined by "div" to "p".
+ */
+static __isl_give isl_printer *poly_print(__isl_keep isl_poly *poly,
+ __isl_keep isl_space *space, __isl_keep isl_mat *div,
+ __isl_take isl_printer *p)
+{
+ int i, first, print_parens;
+ isl_size n;
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (!p || is_cst < 0 || !space || !div)
+ goto error;
+
+ if (is_cst)
+ return poly_print_cst(poly, p, 1);
+
+ rec = isl_poly_as_rec(poly);
+ n = poly_rec_n_non_zero(rec);
+ if (n < 0)
+ return isl_printer_free(p);
+ print_parens = n > 1;
+ if (print_parens)
+ p = isl_printer_print_str(p, "(");
+ for (i = 0, first = 1; i < rec->n; ++i) {
+ isl_bool is_zero = isl_poly_is_zero(rec->p[i]);
+ isl_bool is_one = isl_poly_is_one(rec->p[i]);
+ isl_bool is_negone = isl_poly_is_negone(rec->p[i]);
+ isl_bool is_cst = isl_poly_is_cst(rec->p[i]);
+
+ if (is_zero < 0 || is_one < 0 || is_negone < 0)
+ return isl_printer_free(p);
+ if (is_zero)
+ continue;
+ if (is_negone) {
+ if (!i)
+ p = isl_printer_print_str(p, "-1");
+ else if (first)
+ p = isl_printer_print_str(p, "-");
+ else
+ p = isl_printer_print_str(p, " - ");
+ } else if (is_cst && !is_one)
+ p = poly_print_cst(rec->p[i], p, first);
+ else {
+ if (!first)
+ p = isl_printer_print_str(p, " + ");
+ if (i == 0 || !is_one)
+ p = poly_print(rec->p[i], space, div, p);
+ }
+ first = 0;
+ if (i == 0)
+ continue;
+ if (!is_one && !is_negone)
+ p = isl_printer_print_str(p, " * ");
+ p = print_pow(p, space, div, rec->poly.var, i);
+ }
+ if (print_parens)
+ p = isl_printer_print_str(p, ")");
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *print_qpolynomial(__isl_take isl_printer *p,
+ __isl_keep isl_qpolynomial *qp)
+{
+ if (!p || !qp)
+ goto error;
+ p = poly_print(qp->poly, qp->dim, qp->div, p);
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *print_qpolynomial_isl(__isl_take isl_printer *p,
+ __isl_keep isl_qpolynomial *qp)
+{
+ struct isl_print_space_data data = { 0 };
+
+ if (!p || !qp)
+ goto error;
+
+ p = print_param_tuple(p, qp->dim, &data);
+ p = isl_printer_print_str(p, "{ ");
+ if (!isl_space_is_params(qp->dim)) {
+ p = isl_print_space(qp->dim, p, 0, &data);
+ p = isl_printer_print_str(p, " -> ");
+ }
+ p = print_qpolynomial(p, qp);
+ p = isl_printer_print_str(p, " }");
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print the quasi-polynomial "qp" to "p" in C format, with the variable names
+ * taken from the domain space "space".
+ */
+static __isl_give isl_printer *print_qpolynomial_c(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_qpolynomial *qp)
+{
+ isl_bool is_one;
+ isl_val *den;
+
+ den = isl_qpolynomial_get_den(qp);
+ qp = isl_qpolynomial_copy(qp);
+ qp = isl_qpolynomial_scale_val(qp, isl_val_copy(den));
+ is_one = isl_val_is_one(den);
+ if (is_one < 0)
+ p = isl_printer_free(p);
+ if (!is_one)
+ p = isl_printer_print_str(p, "(");
+ if (qp)
+ p = poly_print(qp->poly, space, qp->div, p);
+ else
+ p = isl_printer_free(p);
+ if (!is_one) {
+ p = isl_printer_print_str(p, ")/");
+ p = isl_printer_print_val(p, den);
+ }
+ isl_qpolynomial_free(qp);
+ isl_val_free(den);
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_qpolynomial(
+ __isl_take isl_printer *p, __isl_keep isl_qpolynomial *qp)
+{
+ if (!p || !qp)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_qpolynomial_isl(p, qp);
+ else if (p->output_format == ISL_FORMAT_C)
+ return print_qpolynomial_c(p, qp->dim, qp);
+ else
+ isl_die(qp->dim->ctx, isl_error_unsupported,
+ "output format not supported for isl_qpolynomials",
+ goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+void isl_qpolynomial_print(__isl_keep isl_qpolynomial *qp, FILE *out,
+ unsigned output_format)
+{
+ isl_printer *p;
+
+ if (!qp)
+ return;
+
+ isl_assert(qp->dim->ctx, output_format == ISL_FORMAT_ISL, return);
+ p = isl_printer_to_file(qp->dim->ctx, out);
+ p = isl_printer_print_qpolynomial(p, qp);
+ isl_printer_free(p);
+}
+
+static __isl_give isl_printer *qpolynomial_fold_print(
+ __isl_keep isl_qpolynomial_fold *fold, __isl_take isl_printer *p)
+{
+ int i;
+ isl_qpolynomial_list *list;
+ isl_size n;
+
+ list = isl_qpolynomial_fold_peek_list(fold);
+ n = isl_qpolynomial_list_size(list);
+ if (n < 0)
+ return isl_printer_free(p);
+ if (fold->type == isl_fold_min)
+ p = isl_printer_print_str(p, "min");
+ else if (fold->type == isl_fold_max)
+ p = isl_printer_print_str(p, "max");
+ p = isl_printer_print_str(p, "(");
+ for (i = 0; i < n; ++i) {
+ isl_qpolynomial *qp;
+
+ if (i)
+ p = isl_printer_print_str(p, ", ");
+ qp = isl_qpolynomial_list_peek(list, i);
+ p = print_qpolynomial(p, qp);
+ }
+ p = isl_printer_print_str(p, ")");
+ return p;
+}
+
+void isl_qpolynomial_fold_print(__isl_keep isl_qpolynomial_fold *fold,
+ FILE *out, unsigned output_format)
+{
+ isl_printer *p;
+
+ if (!fold)
+ return;
+
+ isl_assert(fold->dim->ctx, output_format == ISL_FORMAT_ISL, return);
+
+ p = isl_printer_to_file(fold->dim->ctx, out);
+ p = isl_printer_print_qpolynomial_fold(p, fold);
+
+ isl_printer_free(p);
+}
+
+static __isl_give isl_printer *isl_pwqp_print_isl_body(
+ __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial *pwqp)
+{
+ struct isl_print_space_data data = { 0 };
+ int i = 0;
+
+ for (i = 0; i < pwqp->n; ++i) {
+ isl_space *space;
+
+ if (i)
+ p = isl_printer_print_str(p, "; ");
+ space = isl_qpolynomial_get_domain_space(pwqp->p[i].qp);
+ if (!isl_space_is_params(space)) {
+ p = isl_print_space(space, p, 0, &data);
+ p = isl_printer_print_str(p, " -> ");
+ }
+ p = print_qpolynomial(p, pwqp->p[i].qp);
+ p = print_disjuncts(set_to_map(pwqp->p[i].set), space, p, 0);
+ isl_space_free(space);
+ }
+
+ return p;
+}
+
+static __isl_give isl_printer *print_pw_qpolynomial_isl(
+ __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial *pwqp)
+{
+ struct isl_print_space_data data = { 0 };
+
+ if (!p || !pwqp)
+ goto error;
+
+ p = print_param_tuple(p, pwqp->dim, &data);
+ p = isl_printer_print_str(p, "{ ");
+ if (pwqp->n == 0) {
+ if (!isl_space_is_set(pwqp->dim)) {
+ p = print_tuple(pwqp->dim, p, isl_dim_in, &data);
+ p = isl_printer_print_str(p, " -> ");
+ }
+ p = isl_printer_print_str(p, "0");
+ }
+ p = isl_pwqp_print_isl_body(p, pwqp);
+ p = isl_printer_print_str(p, " }");
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+void isl_pw_qpolynomial_print(__isl_keep isl_pw_qpolynomial *pwqp, FILE *out,
+ unsigned output_format)
+{
+ isl_printer *p;
+
+ if (!pwqp)
+ return;
+
+ p = isl_printer_to_file(pwqp->dim->ctx, out);
+ p = isl_printer_set_output_format(p, output_format);
+ p = isl_printer_print_pw_qpolynomial(p, pwqp);
+
+ isl_printer_free(p);
+}
+
+static __isl_give isl_printer *isl_pwf_print_isl_body(
+ __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial_fold *pwf)
+{
+ struct isl_print_space_data data = { 0 };
+ int i = 0;
+
+ for (i = 0; i < pwf->n; ++i) {
+ isl_space *space;
+
+ if (i)
+ p = isl_printer_print_str(p, "; ");
+ space = isl_qpolynomial_fold_get_domain_space(pwf->p[i].fold);
+ if (!isl_space_is_params(space)) {
+ p = isl_print_space(space, p, 0, &data);
+ p = isl_printer_print_str(p, " -> ");
+ }
+ p = qpolynomial_fold_print(pwf->p[i].fold, p);
+ p = print_disjuncts(set_to_map(pwf->p[i].set), space, p, 0);
+ isl_space_free(space);
+ }
+
+ return p;
+}
+
+static __isl_give isl_printer *print_pw_qpolynomial_fold_isl(
+ __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial_fold *pwf)
+{
+ struct isl_print_space_data data = { 0 };
+
+ p = print_param_tuple(p, pwf->dim, &data);
+ p = isl_printer_print_str(p, "{ ");
+ if (pwf->n == 0) {
+ if (!isl_space_is_set(pwf->dim)) {
+ p = print_tuple(pwf->dim, p, isl_dim_in, &data);
+ p = isl_printer_print_str(p, " -> ");
+ }
+ p = isl_printer_print_str(p, "0");
+ }
+ p = isl_pwf_print_isl_body(p, pwf);
+ p = isl_printer_print_str(p, " }");
+ return p;
+}
+
+static __isl_give isl_printer *print_ls_affine_c(__isl_take isl_printer *p,
+ __isl_keep isl_local_space *ls, isl_int *c);
+
+/* We skip the constraint if it is implied by the div expression.
+ *
+ * *first indicates whether this is the first constraint in the conjunction and
+ * is updated if the constraint is actually printed.
+ */
+static __isl_give isl_printer *print_constraint_c(__isl_take isl_printer *p,
+ __isl_keep isl_local_space *ls, isl_int *c, const char *op, int *first)
+{
+ unsigned o_div;
+ isl_size n_div;
+ int div;
+
+ o_div = isl_local_space_offset(ls, isl_dim_div);
+ n_div = isl_local_space_dim(ls, isl_dim_div);
+ if (n_div < 0)
+ return isl_printer_free(p);
+ div = isl_seq_last_non_zero(c + o_div, n_div);
+ if (div >= 0) {
+ isl_bool is_div = isl_local_space_is_div_constraint(ls, c, div);
+ if (is_div < 0)
+ return isl_printer_free(p);
+ if (is_div)
+ return p;
+ }
+
+ if (!*first)
+ p = isl_printer_print_str(p, " && ");
+
+ p = print_ls_affine_c(p, ls, c);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_str(p, op);
+ p = isl_printer_print_str(p, " 0");
+
+ *first = 0;
+
+ return p;
+}
+
+static __isl_give isl_printer *print_ls_partial_affine_c(
+ __isl_take isl_printer *p, __isl_keep isl_local_space *ls,
+ isl_int *c, unsigned len);
+
+static __isl_give isl_printer *print_basic_set_c(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_basic_set *bset)
+{
+ int i, j;
+ int first = 1;
+ isl_size n_div = isl_basic_set_dim(bset, isl_dim_div);
+ isl_size total = isl_basic_set_dim(bset, isl_dim_all);
+ isl_mat *div;
+ isl_local_space *ls;
+
+ if (n_div < 0 || total < 0)
+ return isl_printer_free(p);
+
+ total -= n_div;
+ div = isl_basic_set_get_divs(bset);
+ ls = isl_local_space_alloc_div(isl_space_copy(space), div);
+ for (i = 0; i < bset->n_eq; ++i) {
+ j = isl_seq_last_non_zero(bset->eq[i] + 1 + total, n_div);
+ if (j < 0)
+ p = print_constraint_c(p, ls,
+ bset->eq[i], "==", &first);
+ else {
+ if (i)
+ p = isl_printer_print_str(p, " && ");
+ p = isl_printer_print_str(p, "(");
+ p = print_ls_partial_affine_c(p, ls, bset->eq[i],
+ 1 + total + j);
+ p = isl_printer_print_str(p, ") % ");
+ p = isl_printer_print_isl_int(p,
+ bset->eq[i][1 + total + j]);
+ p = isl_printer_print_str(p, " == 0");
+ first = 0;
+ }
+ }
+ for (i = 0; i < bset->n_ineq; ++i)
+ p = print_constraint_c(p, ls, bset->ineq[i], ">=", &first);
+ isl_local_space_free(ls);
+ return p;
+}
+
+static __isl_give isl_printer *print_set_c(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_set *set)
+{
+ int i;
+
+ if (!set)
+ return isl_printer_free(p);
+
+ if (set->n == 0)
+ p = isl_printer_print_str(p, "0");
+
+ for (i = 0; i < set->n; ++i) {
+ if (i)
+ p = isl_printer_print_str(p, " || ");
+ if (set->n > 1)
+ p = isl_printer_print_str(p, "(");
+ p = print_basic_set_c(p, space, set->p[i]);
+ if (set->n > 1)
+ p = isl_printer_print_str(p, ")");
+ }
+ return p;
+}
+
+/* Print the piecewise quasi-polynomial "pwqp" to "p" in C format.
+ */
+static __isl_give isl_printer *print_pw_qpolynomial_c(
+ __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial *pwqp)
+{
+ int i;
+ isl_space *space;
+
+ space = isl_pw_qpolynomial_get_domain_space(pwqp);
+ if (pwqp->n == 1 && isl_set_plain_is_universe(pwqp->p[0].set)) {
+ p = print_qpolynomial_c(p, space, pwqp->p[0].qp);
+ isl_space_free(space);
+ return p;
+ }
+
+ for (i = 0; i < pwqp->n; ++i) {
+ p = isl_printer_print_str(p, "(");
+ p = print_set_c(p, space, pwqp->p[i].set);
+ p = isl_printer_print_str(p, ") ? (");
+ p = print_qpolynomial_c(p, space, pwqp->p[i].qp);
+ p = isl_printer_print_str(p, ") : ");
+ }
+
+ isl_space_free(space);
+ p = isl_printer_print_str(p, "0");
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_pw_qpolynomial(
+ __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial *pwqp)
+{
+ if (!p || !pwqp)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_pw_qpolynomial_isl(p, pwqp);
+ else if (p->output_format == ISL_FORMAT_C)
+ return print_pw_qpolynomial_c(p, pwqp);
+ isl_assert(p->ctx, 0, goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static isl_stat print_pwqp_body(__isl_take isl_pw_qpolynomial *pwqp, void *user)
+{
+ struct isl_union_print_data *data;
+ data = (struct isl_union_print_data *)user;
+
+ if (!data->first)
+ data->p = isl_printer_print_str(data->p, "; ");
+ data->first = 0;
+
+ data->p = isl_pwqp_print_isl_body(data->p, pwqp);
+ isl_pw_qpolynomial_free(pwqp);
+
+ return isl_stat_ok;
+}
+
+static __isl_give isl_printer *print_union_pw_qpolynomial_isl(
+ __isl_take isl_printer *p, __isl_keep isl_union_pw_qpolynomial *upwqp)
+{
+ struct isl_union_print_data data;
+ struct isl_print_space_data space_data = { 0 };
+ isl_space *space;
+
+ space = isl_union_pw_qpolynomial_get_space(upwqp);
+ p = print_param_tuple(p, space, &space_data);
+ isl_space_free(space);
+ p = isl_printer_print_str(p, "{ ");
+ data.p = p;
+ data.first = 1;
+ isl_union_pw_qpolynomial_foreach_pw_qpolynomial(upwqp, &print_pwqp_body,
+ &data);
+ p = data.p;
+ p = isl_printer_print_str(p, " }");
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_union_pw_qpolynomial(
+ __isl_take isl_printer *p, __isl_keep isl_union_pw_qpolynomial *upwqp)
+{
+ if (!p || !upwqp)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_union_pw_qpolynomial_isl(p, upwqp);
+ isl_die(p->ctx, isl_error_invalid,
+ "invalid output format for isl_union_pw_qpolynomial",
+ goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print the quasi-polynomial reduction "fold" to "p" in C format,
+ * with the variable names taken from the domain space "space".
+ */
+static __isl_give isl_printer *print_qpolynomial_fold_c(
+ __isl_take isl_printer *p, __isl_keep isl_space *space,
+ __isl_keep isl_qpolynomial_fold *fold)
+{
+ int i;
+ isl_qpolynomial_list *list;
+ isl_size n;
+
+ list = isl_qpolynomial_fold_peek_list(fold);
+ n = isl_qpolynomial_list_size(list);
+ if (n < 0)
+ return isl_printer_free(p);
+ for (i = 0; i < n - 1; ++i)
+ if (fold->type == isl_fold_min)
+ p = isl_printer_print_str(p, "min(");
+ else if (fold->type == isl_fold_max)
+ p = isl_printer_print_str(p, "max(");
+
+ for (i = 0; i < n; ++i) {
+ isl_qpolynomial *qp;
+
+ if (i)
+ p = isl_printer_print_str(p, ", ");
+ qp = isl_qpolynomial_list_peek(list, i);
+ p = print_qpolynomial_c(p, space, qp);
+ if (i)
+ p = isl_printer_print_str(p, ")");
+ }
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_qpolynomial_fold(
+ __isl_take isl_printer *p, __isl_keep isl_qpolynomial_fold *fold)
+{
+ if (!p || !fold)
+ goto error;
+ if (p->output_format == ISL_FORMAT_ISL)
+ return qpolynomial_fold_print(fold, p);
+ else if (p->output_format == ISL_FORMAT_C)
+ return print_qpolynomial_fold_c(p, fold->dim, fold);
+ isl_die(p->ctx, isl_error_unsupported, "unsupported output format",
+ goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print the piecewise quasi-polynomial reduction "pwf" to "p" in C format.
+ */
+static __isl_give isl_printer *print_pw_qpolynomial_fold_c(
+ __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial_fold *pwf)
+{
+ int i;
+ isl_space *space;
+
+ space = isl_pw_qpolynomial_fold_get_domain_space(pwf);
+ if (pwf->n == 1 && isl_set_plain_is_universe(pwf->p[0].set)) {
+ p = print_qpolynomial_fold_c(p, space, pwf->p[0].fold);
+ isl_space_free(space);
+ return p;
+ }
+
+ for (i = 0; i < pwf->n; ++i) {
+ p = isl_printer_print_str(p, "(");
+ p = print_set_c(p, space, pwf->p[i].set);
+ p = isl_printer_print_str(p, ") ? (");
+ p = print_qpolynomial_fold_c(p, space, pwf->p[i].fold);
+ p = isl_printer_print_str(p, ") : ");
+ }
+
+ isl_space_free(space);
+ p = isl_printer_print_str(p, "0");
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_pw_qpolynomial_fold(
+ __isl_take isl_printer *p, __isl_keep isl_pw_qpolynomial_fold *pwf)
+{
+ if (!p || !pwf)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_pw_qpolynomial_fold_isl(p, pwf);
+ else if (p->output_format == ISL_FORMAT_C)
+ return print_pw_qpolynomial_fold_c(p, pwf);
+ isl_assert(p->ctx, 0, goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+void isl_pw_qpolynomial_fold_print(__isl_keep isl_pw_qpolynomial_fold *pwf,
+ FILE *out, unsigned output_format)
+{
+ isl_printer *p;
+
+ if (!pwf)
+ return;
+
+ p = isl_printer_to_file(pwf->dim->ctx, out);
+ p = isl_printer_set_output_format(p, output_format);
+ p = isl_printer_print_pw_qpolynomial_fold(p, pwf);
+
+ isl_printer_free(p);
+}
+
+static isl_stat print_pwf_body(__isl_take isl_pw_qpolynomial_fold *pwf,
+ void *user)
+{
+ struct isl_union_print_data *data;
+ data = (struct isl_union_print_data *)user;
+
+ if (!data->first)
+ data->p = isl_printer_print_str(data->p, "; ");
+ data->first = 0;
+
+ data->p = isl_pwf_print_isl_body(data->p, pwf);
+ isl_pw_qpolynomial_fold_free(pwf);
+
+ return isl_stat_ok;
+}
+
+static __isl_give isl_printer *print_union_pw_qpolynomial_fold_isl(
+ __isl_take isl_printer *p,
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf)
+{
+ struct isl_union_print_data data;
+ struct isl_print_space_data space_data = { 0 };
+ isl_space *space;
+
+ space = isl_union_pw_qpolynomial_fold_get_space(upwf);
+ p = print_param_tuple(p, space, &space_data);
+ isl_space_free(space);
+ p = isl_printer_print_str(p, "{ ");
+ data.p = p;
+ data.first = 1;
+ isl_union_pw_qpolynomial_fold_foreach_pw_qpolynomial_fold(upwf,
+ &print_pwf_body, &data);
+ p = data.p;
+ p = isl_printer_print_str(p, " }");
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_union_pw_qpolynomial_fold(
+ __isl_take isl_printer *p,
+ __isl_keep isl_union_pw_qpolynomial_fold *upwf)
+{
+ if (!p || !upwf)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_union_pw_qpolynomial_fold_isl(p, upwf);
+ isl_die(p->ctx, isl_error_invalid,
+ "invalid output format for isl_union_pw_qpolynomial_fold",
+ goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print the isl_constraint "c" to "p".
+ */
+__isl_give isl_printer *isl_printer_print_constraint(__isl_take isl_printer *p,
+ __isl_keep isl_constraint *c)
+{
+ struct isl_print_space_data data = { 0 };
+ isl_local_space *ls;
+ isl_space *space;
+ isl_bool exists;
+
+ if (!p || !c)
+ goto error;
+
+ ls = isl_constraint_get_local_space(c);
+ if (!ls)
+ return isl_printer_free(p);
+ space = isl_local_space_get_space(ls);
+ p = print_param_tuple(p, space, &data);
+ p = isl_printer_print_str(p, "{ ");
+ p = isl_print_space(space, p, 0, &data);
+ p = isl_printer_print_str(p, " : ");
+ exists = need_exists(p, ls->div);
+ if (exists < 0)
+ p = isl_printer_free(p);
+ if (exists >= 0 && exists)
+ p = open_exists(p, space, ls->div, 0);
+ p = print_affine_of_len(space, ls->div, p, c->v->el, c->v->size);
+ if (isl_constraint_is_equality(c))
+ p = isl_printer_print_str(p, " = 0");
+ else
+ p = isl_printer_print_str(p, " >= 0");
+ if (exists >= 0 && exists)
+ p = isl_printer_print_str(p, s_close_exists[0]);
+ p = isl_printer_print_str(p, " }");
+ isl_space_free(space);
+ isl_local_space_free(ls);
+
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *isl_printer_print_space_isl(
+ __isl_take isl_printer *p, __isl_keep isl_space *space)
+{
+ struct isl_print_space_data data = { 0 };
+
+ if (!space)
+ goto error;
+
+ p = print_param_tuple(p, space, &data);
+
+ p = isl_printer_print_str(p, "{ ");
+ if (isl_space_is_params(space))
+ p = isl_printer_print_str(p, s_such_that[0]);
+ else
+ p = isl_print_space(space, p, 0, &data);
+ p = isl_printer_print_str(p, " }");
+
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+__isl_give isl_printer *isl_printer_print_space(__isl_take isl_printer *p,
+ __isl_keep isl_space *space)
+{
+ if (!p || !space)
+ return isl_printer_free(p);
+ if (p->output_format == ISL_FORMAT_ISL)
+ return isl_printer_print_space_isl(p, space);
+ else if (p->output_format == ISL_FORMAT_OMEGA)
+ return print_omega_parameters(space, p);
+
+ isl_die(isl_space_get_ctx(space), isl_error_unsupported,
+ "output format not supported for space",
+ return isl_printer_free(p));
+}
+
+__isl_give isl_printer *isl_printer_print_local_space(__isl_take isl_printer *p,
+ __isl_keep isl_local_space *ls)
+{
+ struct isl_print_space_data data = { 0 };
+ isl_size n_div;
+
+ n_div = isl_local_space_dim(ls, isl_dim_div);
+ if (n_div < 0)
+ goto error;
+
+ p = print_param_tuple(p, ls->dim, &data);
+ p = isl_printer_print_str(p, "{ ");
+ p = isl_print_space(ls->dim, p, 0, &data);
+ if (n_div > 0) {
+ p = isl_printer_print_str(p, " : ");
+ p = isl_printer_print_str(p, s_open_exists[0]);
+ p = print_div_list(p, ls->dim, ls->div, 0, 1);
+ p = isl_printer_print_str(p, s_close_exists[0]);
+ } else if (isl_space_is_params(ls->dim))
+ p = isl_printer_print_str(p, s_such_that[0]);
+ p = isl_printer_print_str(p, " }");
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Look for the last of the "n" integer divisions that is used in "aff" and
+ * that can be printed as a modulo and
+ * return the position of this integer division.
+ * Return "n" if no such integer division can be found.
+ * Return isl_size_error on error.
+ *
+ * In particular, look for an integer division that appears in "aff"
+ * with a coefficient that is a multiple of the denominator
+ * of the integer division.
+ * That is, check if the numerator of "aff" is of the form
+ *
+ * f(...) + a m floor(g/m)
+ *
+ * and return the position of "floor(g/m)".
+ *
+ * Note that, unlike print_as_modulo_pos, no check needs to be made
+ * for whether the integer division can be printed, since it will
+ * need to be printed as an integer division anyway if it is not printed
+ * as a modulo.
+ */
+static isl_size last_modulo(__isl_keep isl_printer *p, __isl_keep isl_aff *aff,
+ unsigned n)
+{
+ isl_size o_div;
+ int i;
+
+ if (n == 0)
+ return n;
+ o_div = isl_aff_domain_offset(aff, isl_dim_div);
+ if (o_div < 0)
+ return isl_size_error;
+ for (i = n - 1; i >= 0; --i) {
+ if (isl_int_is_zero(aff->v->el[1 + o_div + i]))
+ continue;
+ if (isl_int_is_divisible_by(aff->v->el[1 + o_div + i],
+ aff->ls->div->row[i][0]))
+ return i;
+ }
+
+ return n;
+}
+
+/* Print the numerator of the affine expression "aff" to "p",
+ * with the variable names taken from "space".
+ */
+static __isl_give isl_printer *print_aff_num_base(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_aff *aff)
+{
+ isl_size total;
+
+ total = isl_aff_domain_dim(aff, isl_dim_all);
+ if (total < 0)
+ return isl_printer_free(p);
+ p = print_affine_of_len(space, aff->ls->div, p,
+ aff->v->el + 1, 1 + total);
+
+ return p;
+}
+
+static __isl_give isl_printer *print_aff_num(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_aff *aff);
+
+/* Print the modulo term "c" * ("aff" mod "mod") to "p",
+ * with the variable names taken from "space".
+ * If "first" is set, then this is the first term of an expression.
+ */
+static __isl_give isl_printer *print_mod_term(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_aff *aff, int first,
+ __isl_take isl_val *c, __isl_keep isl_val *mod)
+{
+ isl_bool is_one, is_neg;
+
+ is_neg = isl_val_is_neg(c);
+ if (is_neg < 0)
+ p = isl_printer_free(p);
+ if (!first) {
+ if (is_neg)
+ c = isl_val_neg(c);
+ p = isl_printer_print_str(p, is_neg ? " - " : " + ");
+ }
+ is_one = isl_val_is_one(c);
+ if (is_one < 0)
+ p = isl_printer_free(p);
+ if (!is_one) {
+ p = isl_printer_print_val(p, c);
+ p = isl_printer_print_str(p, "*(");
+ }
+ p = isl_printer_print_str(p, "(");
+ p = print_aff_num(p, space, aff);
+ p = isl_printer_print_str(p, ")");
+ p = isl_printer_print_str(p, " mod ");
+ p = isl_printer_print_val(p, mod);
+ if (!is_one)
+ p = isl_printer_print_str(p, ")");
+
+ isl_val_free(c);
+
+ return p;
+}
+
+/* Print the numerator of the affine expression "aff" to "p",
+ * with the variable names taken from "space",
+ * given that the numerator of "aff" is of the form
+ *
+ * f(...) + a m floor(g/m)
+ *
+ * with "floor(g/m)" the integer division at position "last".
+ *
+ * First replace "aff" by its numerator and rewrite it as
+ *
+ * f(...) + a g - a (g mod m)
+ *
+ * Recursively write out (the numerator of) "f(...) + a g"
+ * (which may involve other modulo expressions) and
+ * then write out "- a (g mod m)".
+ */
+static __isl_give isl_printer *print_aff_num_mod(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_aff *aff, unsigned last)
+{
+ isl_bool is_zero;
+ isl_val *a, *m;
+ isl_aff *div, *term;
+
+ aff = isl_aff_copy(aff);
+ aff = isl_aff_scale_val(aff, isl_aff_get_denominator_val(aff));
+ a = isl_aff_get_coefficient_val(aff, isl_dim_div, last);
+ aff = isl_aff_set_coefficient_si(aff, isl_dim_div, last, 0);
+ div = isl_aff_get_div(aff, last);
+ m = isl_aff_get_denominator_val(div);
+ a = isl_val_div(a, isl_val_copy(m));
+ div = isl_aff_scale_val(div, isl_val_copy(m));
+ term = isl_aff_scale_val(isl_aff_copy(div), isl_val_copy(a));
+ aff = isl_aff_add(aff, term);
+
+ is_zero = isl_aff_plain_is_zero(aff);
+ if (is_zero < 0) {
+ p = isl_printer_free(p);
+ } else {
+ if (!is_zero)
+ p = print_aff_num(p, space, aff);
+ a = isl_val_neg(a);
+ p = print_mod_term(p, space, div, is_zero, isl_val_copy(a), m);
+ }
+
+ isl_val_free(a);
+ isl_val_free(m);
+ isl_aff_free(aff);
+ isl_aff_free(div);
+
+ return p;
+}
+
+/* Print the numerator of the affine expression "aff" to "p",
+ * with the variable names taken from "space",
+ * separating out any (obvious) modulo expressions.
+ *
+ * In particular, look for modulo expressions in "aff",
+ * separating them out if found and simply printing out "aff" otherwise.
+ */
+static __isl_give isl_printer *print_aff_num(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_aff *aff)
+{
+ isl_size n_div, mod;
+
+ n_div = isl_aff_dim(aff, isl_dim_div);
+ if (n_div < 0)
+ return isl_printer_free(p);
+ mod = last_modulo(p, aff, n_div);
+ if (mod < 0)
+ return isl_printer_free(p);
+ if (mod < n_div)
+ return print_aff_num_mod(p, space, aff, mod);
+ else
+ return print_aff_num_base(p, space, aff);
+}
+
+/* Print the (potentially rational) affine expression "aff" to "p",
+ * with the variable names taken from "space".
+ */
+static __isl_give isl_printer *print_aff_body(__isl_take isl_printer *p,
+ __isl_keep isl_space *space, __isl_keep isl_aff *aff)
+{
+ if (isl_aff_is_nan(aff))
+ return isl_printer_print_str(p, "NaN");
+
+ p = isl_printer_print_str(p, "(");
+ p = print_aff_num(p, space, aff);
+ if (isl_int_is_one(aff->v->el[0]))
+ p = isl_printer_print_str(p, ")");
+ else {
+ p = isl_printer_print_str(p, ")/");
+ p = isl_printer_print_isl_int(p, aff->v->el[0]);
+ }
+
+ return p;
+}
+
+static __isl_give isl_printer *print_aff(__isl_take isl_printer *p,
+ __isl_keep isl_aff *aff)
+{
+ struct isl_print_space_data data = { 0 };
+
+ if (isl_space_is_params(aff->ls->dim))
+ ;
+ else {
+ p = print_tuple(aff->ls->dim, p, isl_dim_set, &data);
+ p = isl_printer_print_str(p, " -> ");
+ }
+ p = isl_printer_print_str(p, "[");
+ p = print_aff_body(p, aff->ls->dim, aff);
+ p = isl_printer_print_str(p, "]");
+
+ return p;
+}
+
+static __isl_give isl_printer *print_aff_isl(__isl_take isl_printer *p,
+ __isl_keep isl_aff *aff)
+{
+ struct isl_print_space_data data = { 0 };
+
+ if (!aff)
+ goto error;
+
+ p = print_param_tuple(p, aff->ls->dim, &data);
+ p = isl_printer_print_str(p, "{ ");
+ p = print_aff(p, aff);
+ p = isl_printer_print_str(p, " }");
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print the body of an isl_pw_aff, i.e., a semicolon delimited
+ * sequence of affine expressions, each followed by constraints.
+ */
+static __isl_give isl_printer *print_pw_aff_body(
+ __isl_take isl_printer *p, __isl_keep isl_pw_aff *pa)
+{
+ int i;
+
+ if (!pa)
+ return isl_printer_free(p);
+
+ for (i = 0; i < pa->n; ++i) {
+ isl_space *space;
+
+ if (i)
+ p = isl_printer_print_str(p, "; ");
+ p = print_aff(p, pa->p[i].aff);
+ space = isl_aff_get_domain_space(pa->p[i].aff);
+ p = print_disjuncts(set_to_map(pa->p[i].set), space, p, 0);
+ isl_space_free(space);
+ }
+
+ return p;
+}
+
+static __isl_give isl_printer *print_pw_aff_isl(__isl_take isl_printer *p,
+ __isl_keep isl_pw_aff *pwaff)
+{
+ struct isl_print_space_data data = { 0 };
+
+ if (!pwaff)
+ goto error;
+
+ p = print_param_tuple(p, pwaff->dim, &data);
+ p = isl_printer_print_str(p, "{ ");
+ p = print_pw_aff_body(p, pwaff);
+ p = isl_printer_print_str(p, " }");
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *print_ls_name_c(__isl_take isl_printer *p,
+ __isl_keep isl_local_space *ls, enum isl_dim_type type, unsigned pos)
+{
+ if (type == isl_dim_div) {
+ p = isl_printer_print_str(p, "floord(");
+ p = print_ls_affine_c(p, ls, ls->div->row[pos] + 1);
+ p = isl_printer_print_str(p, ", ");
+ p = isl_printer_print_isl_int(p, ls->div->row[pos][0]);
+ p = isl_printer_print_str(p, ")");
+ } else {
+ const char *name;
+
+ name = isl_space_get_dim_name(ls->dim, type, pos);
+ if (!name)
+ name = "UNNAMED";
+ p = isl_printer_print_str(p, name);
+ }
+ return p;
+}
+
+static __isl_give isl_printer *print_ls_term_c(__isl_take isl_printer *p,
+ __isl_keep isl_local_space *ls, isl_int c, unsigned pos)
+{
+ enum isl_dim_type type;
+
+ if (!p || !ls)
+ return isl_printer_free(p);
+
+ if (pos == 0)
+ return isl_printer_print_isl_int(p, c);
+
+ if (isl_int_is_one(c))
+ ;
+ else if (isl_int_is_negone(c))
+ p = isl_printer_print_str(p, "-");
+ else {
+ p = isl_printer_print_isl_int(p, c);
+ p = isl_printer_print_str(p, "*");
+ }
+ if (pos2type(ls->dim, &type, &pos) < 0)
+ return isl_printer_free(p);
+ p = print_ls_name_c(p, ls, type, pos);
+ return p;
+}
+
+static __isl_give isl_printer *print_ls_partial_affine_c(
+ __isl_take isl_printer *p, __isl_keep isl_local_space *ls,
+ isl_int *c, unsigned len)
+{
+ int i;
+ int first;
+
+ for (i = 0, first = 1; i < len; ++i) {
+ int flip = 0;
+ if (isl_int_is_zero(c[i]))
+ continue;
+ if (!first) {
+ if (isl_int_is_neg(c[i])) {
+ flip = 1;
+ isl_int_neg(c[i], c[i]);
+ p = isl_printer_print_str(p, " - ");
+ } else
+ p = isl_printer_print_str(p, " + ");
+ }
+ first = 0;
+ p = print_ls_term_c(p, ls, c[i], i);
+ if (flip)
+ isl_int_neg(c[i], c[i]);
+ }
+ if (first)
+ p = isl_printer_print_str(p, "0");
+ return p;
+}
+
+static __isl_give isl_printer *print_ls_affine_c(__isl_take isl_printer *p,
+ __isl_keep isl_local_space *ls, isl_int *c)
+{
+ isl_size total = isl_local_space_dim(ls, isl_dim_all);
+
+ if (total < 0)
+ return isl_printer_free(p);
+ return print_ls_partial_affine_c(p, ls, c, 1 + total);
+}
+
+static __isl_give isl_printer *print_aff_c(__isl_take isl_printer *p,
+ __isl_keep isl_aff *aff)
+{
+ isl_size total;
+
+ total = isl_aff_domain_dim(aff, isl_dim_all);
+ if (total < 0)
+ return isl_printer_free(p);
+ if (!isl_int_is_one(aff->v->el[0]))
+ p = isl_printer_print_str(p, "(");
+ p = print_ls_partial_affine_c(p, aff->ls, aff->v->el + 1, 1 + total);
+ if (!isl_int_is_one(aff->v->el[0])) {
+ p = isl_printer_print_str(p, ")/");
+ p = isl_printer_print_isl_int(p, aff->v->el[0]);
+ }
+ return p;
+}
+
+/* In the C format, we cannot express that "pwaff" may be undefined
+ * on parts of the domain space. We therefore assume that the expression
+ * will only be evaluated on its definition domain and compute the gist
+ * of each cell with respect to this domain.
+ */
+static __isl_give isl_printer *print_pw_aff_c(__isl_take isl_printer *p,
+ __isl_keep isl_pw_aff *pwaff)
+{
+ isl_set *domain;
+ isl_ast_build *build;
+ isl_ast_expr *expr;
+
+ if (pwaff->n < 1)
+ isl_die(p->ctx, isl_error_unsupported,
+ "cannot print empty isl_pw_aff in C format",
+ return isl_printer_free(p));
+
+ domain = isl_pw_aff_domain(isl_pw_aff_copy(pwaff));
+ build = isl_ast_build_from_context(domain);
+ expr = isl_ast_build_expr_from_pw_aff(build, isl_pw_aff_copy(pwaff));
+ p = isl_printer_print_ast_expr(p, expr);
+ isl_ast_expr_free(expr);
+ isl_ast_build_free(build);
+
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_aff(__isl_take isl_printer *p,
+ __isl_keep isl_aff *aff)
+{
+ if (!p || !aff)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_aff_isl(p, aff);
+ else if (p->output_format == ISL_FORMAT_C)
+ return print_aff_c(p, aff);
+ isl_die(p->ctx, isl_error_unsupported, "unsupported output format",
+ goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+__isl_give isl_printer *isl_printer_print_pw_aff(__isl_take isl_printer *p,
+ __isl_keep isl_pw_aff *pwaff)
+{
+ if (!p || !pwaff)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_pw_aff_isl(p, pwaff);
+ else if (p->output_format == ISL_FORMAT_C)
+ return print_pw_aff_c(p, pwaff);
+ isl_die(p->ctx, isl_error_unsupported, "unsupported output format",
+ goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print "pa" in a sequence of isl_pw_affs delimited by semicolons.
+ * Each isl_pw_aff itself is also printed as semicolon delimited
+ * sequence of pieces.
+ * If data->first = 1, then this is the first in the sequence.
+ * Update data->first to tell the next element that it is not the first.
+ */
+static isl_stat print_pw_aff_body_wrap(__isl_take isl_pw_aff *pa,
+ void *user)
+{
+ struct isl_union_print_data *data;
+ data = (struct isl_union_print_data *) user;
+
+ if (!data->first)
+ data->p = isl_printer_print_str(data->p, "; ");
+ data->first = 0;
+
+ data->p = print_pw_aff_body(data->p, pa);
+ isl_pw_aff_free(pa);
+
+ return data->p ? isl_stat_ok : isl_stat_error;
+}
+
+/* Print the body of an isl_union_pw_aff, i.e., a semicolon delimited
+ * sequence of affine expressions, each followed by constraints,
+ * with the sequence enclosed in braces.
+ */
+static __isl_give isl_printer *print_union_pw_aff_body(
+ __isl_take isl_printer *p, __isl_keep isl_union_pw_aff *upa)
+{
+ struct isl_union_print_data data = { p, 1 };
+
+ p = isl_printer_print_str(p, s_open_set[0]);
+ data.p = p;
+ if (isl_union_pw_aff_foreach_pw_aff(upa,
+ &print_pw_aff_body_wrap, &data) < 0)
+ data.p = isl_printer_free(data.p);
+ p = data.p;
+ p = isl_printer_print_str(p, s_close_set[0]);
+
+ return p;
+}
+
+/* Print the isl_union_pw_aff "upa" to "p" in isl format.
+ *
+ * The individual isl_pw_affs are delimited by a semicolon.
+ */
+static __isl_give isl_printer *print_union_pw_aff_isl(
+ __isl_take isl_printer *p, __isl_keep isl_union_pw_aff *upa)
+{
+ struct isl_print_space_data data = { 0 };
+ isl_space *space;
+
+ space = isl_union_pw_aff_get_space(upa);
+ p = print_param_tuple(p, space, &data);
+ isl_space_free(space);
+ p = print_union_pw_aff_body(p, upa);
+ return p;
+}
+
+/* Print the isl_union_pw_aff "upa" to "p".
+ *
+ * We currently only support an isl format.
+ */
+__isl_give isl_printer *isl_printer_print_union_pw_aff(
+ __isl_take isl_printer *p, __isl_keep isl_union_pw_aff *upa)
+{
+ if (!p || !upa)
+ return isl_printer_free(p);
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_union_pw_aff_isl(p, upa);
+ isl_die(isl_printer_get_ctx(p), isl_error_unsupported,
+ "unsupported output format", return isl_printer_free(p));
+}
+
+/* Print dimension "pos" of data->space to "p".
+ *
+ * data->user is assumed to be an isl_multi_aff.
+ *
+ * If the current dimension is an output dimension, then print
+ * the corresponding expression. Otherwise, print the name of the dimension.
+ * Make sure to use the domain space for printing names as
+ * that is the space that will be used for printing constraints (if any).
+ */
+static __isl_give isl_printer *print_dim_ma(__isl_take isl_printer *p,
+ struct isl_print_space_data *data, unsigned pos)
+{
+ isl_multi_aff *ma = data->user;
+ isl_space *space;
+
+ space = isl_multi_aff_get_domain_space(ma);
+ if (data->type == isl_dim_out) {
+ p = print_aff_body(p, space, ma->u.p[pos]);
+ } else {
+ enum isl_dim_type type = data->type;
+
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ p = print_name(space, p, type, pos, data->latex);
+ }
+ isl_space_free(space);
+
+ return p;
+}
+
+static __isl_give isl_printer *print_multi_aff(__isl_take isl_printer *p,
+ __isl_keep isl_multi_aff *maff)
+{
+ struct isl_print_space_data data = { 0 };
+
+ data.print_dim = &print_dim_ma;
+ data.user = maff;
+ return isl_print_space(maff->space, p, 0, &data);
+}
+
+static __isl_give isl_printer *print_multi_aff_isl(__isl_take isl_printer *p,
+ __isl_keep isl_multi_aff *maff)
+{
+ struct isl_print_space_data data = { 0 };
+
+ if (!maff)
+ goto error;
+
+ p = print_param_tuple(p, maff->space, &data);
+ p = isl_printer_print_str(p, "{ ");
+ p = print_multi_aff(p, maff);
+ p = isl_printer_print_str(p, " }");
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+__isl_give isl_printer *isl_printer_print_multi_aff(__isl_take isl_printer *p,
+ __isl_keep isl_multi_aff *maff)
+{
+ if (!p || !maff)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_multi_aff_isl(p, maff);
+ isl_die(p->ctx, isl_error_unsupported, "unsupported output format",
+ goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *print_pw_multi_aff_body(
+ __isl_take isl_printer *p, __isl_keep isl_pw_multi_aff *pma)
+{
+ int i;
+
+ if (!pma)
+ goto error;
+
+ for (i = 0; i < pma->n; ++i) {
+ isl_space *space;
+
+ if (i)
+ p = isl_printer_print_str(p, "; ");
+ p = print_multi_aff(p, pma->p[i].maff);
+ space = isl_multi_aff_get_domain_space(pma->p[i].maff);
+ p = print_disjuncts(set_to_map(pma->p[i].set), space, p, 0);
+ isl_space_free(space);
+ }
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *print_pw_multi_aff_isl(__isl_take isl_printer *p,
+ __isl_keep isl_pw_multi_aff *pma)
+{
+ struct isl_print_space_data data = { 0 };
+
+ if (!pma)
+ goto error;
+
+ p = print_param_tuple(p, pma->dim, &data);
+ p = isl_printer_print_str(p, "{ ");
+ p = print_pw_multi_aff_body(p, pma);
+ p = isl_printer_print_str(p, " }");
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print the unnamed, single-dimensional piecewise multi affine expression "pma"
+ * to "p".
+ */
+static __isl_give isl_printer *print_unnamed_pw_multi_aff_c(
+ __isl_take isl_printer *p, __isl_keep isl_pw_multi_aff *pma)
+{
+ int i;
+ isl_space *space;
+
+ space = isl_pw_multi_aff_get_domain_space(pma);
+ for (i = 0; i < pma->n - 1; ++i) {
+ p = isl_printer_print_str(p, "(");
+ p = print_set_c(p, space, pma->p[i].set);
+ p = isl_printer_print_str(p, ") ? (");
+ p = print_aff_c(p, pma->p[i].maff->u.p[0]);
+ p = isl_printer_print_str(p, ") : ");
+ }
+ isl_space_free(space);
+
+ return print_aff_c(p, pma->p[pma->n - 1].maff->u.p[0]);
+}
+
+static __isl_give isl_printer *print_pw_multi_aff_c(__isl_take isl_printer *p,
+ __isl_keep isl_pw_multi_aff *pma)
+{
+ isl_size n;
+ const char *name;
+
+ if (!pma)
+ goto error;
+ if (pma->n < 1)
+ isl_die(p->ctx, isl_error_unsupported,
+ "cannot print empty isl_pw_multi_aff in C format",
+ goto error);
+ n = isl_pw_multi_aff_dim(pma, isl_dim_out);
+ if (n < 0)
+ return isl_printer_free(p);
+ name = isl_pw_multi_aff_get_tuple_name(pma, isl_dim_out);
+ if (!name && n == 1)
+ return print_unnamed_pw_multi_aff_c(p, pma);
+ if (!name)
+ isl_die(p->ctx, isl_error_unsupported,
+ "cannot print unnamed isl_pw_multi_aff in C format",
+ goto error);
+
+ p = isl_printer_print_str(p, name);
+ if (n != 0)
+ isl_die(p->ctx, isl_error_unsupported,
+ "not supported yet", goto error);
+
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+__isl_give isl_printer *isl_printer_print_pw_multi_aff(
+ __isl_take isl_printer *p, __isl_keep isl_pw_multi_aff *pma)
+{
+ if (!p || !pma)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_pw_multi_aff_isl(p, pma);
+ if (p->output_format == ISL_FORMAT_C)
+ return print_pw_multi_aff_c(p, pma);
+ isl_die(p->ctx, isl_error_unsupported, "unsupported output format",
+ goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static isl_stat print_pw_multi_aff_body_wrap(__isl_take isl_pw_multi_aff *pma,
+ void *user)
+{
+ struct isl_union_print_data *data;
+ data = (struct isl_union_print_data *) user;
+
+ if (!data->first)
+ data->p = isl_printer_print_str(data->p, "; ");
+ data->first = 0;
+
+ data->p = print_pw_multi_aff_body(data->p, pma);
+ isl_pw_multi_aff_free(pma);
+
+ return isl_stat_ok;
+}
+
+static __isl_give isl_printer *print_union_pw_multi_aff_isl(
+ __isl_take isl_printer *p, __isl_keep isl_union_pw_multi_aff *upma)
+{
+ struct isl_union_print_data data;
+ struct isl_print_space_data space_data = { 0 };
+ isl_space *space;
+
+ space = isl_union_pw_multi_aff_get_space(upma);
+ p = print_param_tuple(p, space, &space_data);
+ isl_space_free(space);
+ p = isl_printer_print_str(p, s_open_set[0]);
+ data.p = p;
+ data.first = 1;
+ isl_union_pw_multi_aff_foreach_pw_multi_aff(upma,
+ &print_pw_multi_aff_body_wrap, &data);
+ p = data.p;
+ p = isl_printer_print_str(p, s_close_set[0]);
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_union_pw_multi_aff(
+ __isl_take isl_printer *p, __isl_keep isl_union_pw_multi_aff *upma)
+{
+ if (!p || !upma)
+ goto error;
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_union_pw_multi_aff_isl(p, upma);
+ isl_die(p->ctx, isl_error_unsupported, "unsupported output format",
+ goto error);
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+/* Print dimension "pos" of data->space to "p".
+ *
+ * data->user is assumed to be an isl_multi_pw_aff.
+ *
+ * If the current dimension is an output dimension, then print
+ * the corresponding piecewise affine expression.
+ * Otherwise, print the name of the dimension.
+ * Make sure to use the same space in both cases.
+ * In particular, use the domain space for printing names as
+ * that is the space that is used for printing constraints.
+ */
+static __isl_give isl_printer *print_dim_mpa(__isl_take isl_printer *p,
+ struct isl_print_space_data *data, unsigned pos)
+{
+ int i;
+ int need_parens;
+ isl_space *space;
+ isl_multi_pw_aff *mpa = data->user;
+ isl_pw_aff *pa;
+
+ if (data->type != isl_dim_out) {
+ enum isl_dim_type type = data->type;
+
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+ space = isl_multi_pw_aff_get_domain_space(mpa);
+ p = print_name(space, p, type, pos, data->latex);
+ isl_space_free(space);
+ return p;
+ }
+
+ pa = mpa->u.p[pos];
+ if (pa->n == 0)
+ return isl_printer_print_str(p, "(0 : false)");
+
+ need_parens = pa->n != 1 || !isl_set_plain_is_universe(pa->p[0].set);
+ if (need_parens)
+ p = isl_printer_print_str(p, "(");
+ space = isl_multi_pw_aff_get_domain_space(mpa);
+ for (i = 0; i < pa->n; ++i) {
+
+ if (i)
+ p = isl_printer_print_str(p, "; ");
+ p = print_aff_body(p, space, pa->p[i].aff);
+ p = print_disjuncts(pa->p[i].set, space, p, 0);
+ }
+ isl_space_free(space);
+ if (need_parens)
+ p = isl_printer_print_str(p, ")");
+
+ return p;
+}
+
+/* Print "mpa" to "p" in isl format.
+ *
+ * If "mpa" is zero-dimensional and has a non-trivial explicit domain,
+ * then it is printed after the tuple of affine expressions.
+ */
+static __isl_give isl_printer *print_multi_pw_aff_isl(__isl_take isl_printer *p,
+ __isl_keep isl_multi_pw_aff *mpa)
+{
+ struct isl_print_space_data data = { 0 };
+ isl_bool has_domain;
+
+ if (!mpa)
+ return isl_printer_free(p);
+
+ p = print_param_tuple(p, mpa->space, &data);
+ p = isl_printer_print_str(p, "{ ");
+ data.print_dim = &print_dim_mpa;
+ data.user = mpa;
+ p = isl_print_space(mpa->space, p, 0, &data);
+ has_domain = isl_multi_pw_aff_has_non_trivial_domain(mpa);
+ if (has_domain < 0)
+ return isl_printer_free(p);
+ if (has_domain) {
+ isl_space *space;
+
+ space = isl_space_domain(isl_space_copy(mpa->space));
+ p = print_disjuncts_set(mpa->u.dom, space, p, 0);
+ isl_space_free(space);
+ }
+ p = isl_printer_print_str(p, " }");
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_multi_pw_aff(
+ __isl_take isl_printer *p, __isl_keep isl_multi_pw_aff *mpa)
+{
+ if (!p || !mpa)
+ return isl_printer_free(p);
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_multi_pw_aff_isl(p, mpa);
+ isl_die(p->ctx, isl_error_unsupported, "unsupported output format",
+ return isl_printer_free(p));
+}
+
+/* Print dimension "pos" of data->space to "p".
+ *
+ * data->user is assumed to be an isl_multi_val.
+ *
+ * If the current dimension is an output dimension, then print
+ * the corresponding value. Otherwise, print the name of the dimension.
+ */
+static __isl_give isl_printer *print_dim_mv(__isl_take isl_printer *p,
+ struct isl_print_space_data *data, unsigned pos)
+{
+ isl_multi_val *mv = data->user;
+
+ if (data->type == isl_dim_out)
+ return isl_printer_print_val(p, mv->u.p[pos]);
+ else
+ return print_name(data->space, p, data->type, pos, data->latex);
+}
+
+/* Print the isl_multi_val "mv" to "p" in isl format.
+ */
+static __isl_give isl_printer *print_multi_val_isl(__isl_take isl_printer *p,
+ __isl_keep isl_multi_val *mv)
+{
+ struct isl_print_space_data data = { 0 };
+
+ if (!mv)
+ return isl_printer_free(p);
+
+ p = print_param_tuple(p, mv->space, &data);
+ p = isl_printer_print_str(p, "{ ");
+ data.print_dim = &print_dim_mv;
+ data.user = mv;
+ p = isl_print_space(mv->space, p, 0, &data);
+ p = isl_printer_print_str(p, " }");
+ return p;
+}
+
+/* Print the isl_multi_val "mv" to "p".
+ *
+ * Currently only supported in isl format.
+ */
+__isl_give isl_printer *isl_printer_print_multi_val(
+ __isl_take isl_printer *p, __isl_keep isl_multi_val *mv)
+{
+ if (!p || !mv)
+ return isl_printer_free(p);
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_multi_val_isl(p, mv);
+ isl_die(p->ctx, isl_error_unsupported, "unsupported output format",
+ return isl_printer_free(p));
+}
+
+/* Print dimension "pos" of data->space to "p".
+ *
+ * data->user is assumed to be an isl_multi_id.
+ *
+ * If the current dimension is an output dimension, then print
+ * the corresponding identifier. Otherwise, print the name of the dimension.
+ */
+static __isl_give isl_printer *print_dim_mi(__isl_take isl_printer *p,
+ struct isl_print_space_data *data, unsigned pos)
+{
+ isl_multi_id *mi = data->user;
+
+ if (data->type == isl_dim_out)
+ return isl_printer_print_id(p, mi->u.p[pos]);
+ else
+ return print_name(data->space, p, data->type, pos, data->latex);
+}
+
+/* Print the isl_multi_id "mi" to "p" in isl format.
+ */
+static __isl_give isl_printer *print_multi_id_isl(__isl_take isl_printer *p,
+ __isl_keep isl_multi_id *mi)
+{
+ isl_space *space;
+ struct isl_print_space_data data = { 0 };
+
+ space = isl_multi_id_peek_space(mi);
+ p = print_param_tuple(p, space, &data);
+ p = isl_printer_print_str(p, "{ ");
+ data.print_dim = &print_dim_mi;
+ data.user = mi;
+ p = isl_print_space(space, p, 0, &data);
+ p = isl_printer_print_str(p, " }");
+ return p;
+}
+
+/* Print the isl_multi_id "mi" to "p".
+ *
+ * Currently only supported in isl format.
+ */
+__isl_give isl_printer *isl_printer_print_multi_id(
+ __isl_take isl_printer *p, __isl_keep isl_multi_id *mi)
+{
+ if (!p || !mi)
+ return isl_printer_free(p);
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_multi_id_isl(p, mi);
+ isl_die(isl_printer_get_ctx(p), isl_error_unsupported,
+ "unsupported output format", return isl_printer_free(p));
+}
+
+/* Print dimension "pos" of data->space to "p".
+ *
+ * data->user is assumed to be an isl_multi_union_pw_aff.
+ *
+ * The current dimension is necessarily a set dimension, so
+ * we print the corresponding isl_union_pw_aff, including
+ * the braces.
+ */
+static __isl_give isl_printer *print_union_pw_aff_dim(__isl_take isl_printer *p,
+ struct isl_print_space_data *data, unsigned pos)
+{
+ isl_multi_union_pw_aff *mupa = data->user;
+ isl_union_pw_aff *upa;
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, pos);
+ p = print_union_pw_aff_body(p, upa);
+ isl_union_pw_aff_free(upa);
+
+ return p;
+}
+
+/* Print the isl_multi_union_pw_aff "mupa" to "p" in isl format.
+ *
+ * If "mupa" is zero-dimensional and has a non-trivial explicit domain,
+ * then it is printed after the tuple of affine expressions.
+ * In order to clarify that this domain belongs to the expression,
+ * the tuple along with the domain are placed inside parentheses.
+ * If "mupa" has any parameters, then the opening parenthesis
+ * appears after the parameter declarations.
+ */
+static __isl_give isl_printer *print_multi_union_pw_aff_isl(
+ __isl_take isl_printer *p, __isl_keep isl_multi_union_pw_aff *mupa)
+{
+ struct isl_print_space_data data = { 0 };
+ isl_bool has_domain;
+ isl_space *space;
+
+ if (!mupa)
+ return isl_printer_free(p);
+ has_domain = isl_multi_union_pw_aff_has_non_trivial_domain(mupa);
+ if (has_domain < 0)
+ return isl_printer_free(p);
+
+ space = isl_multi_union_pw_aff_get_space(mupa);
+ p = print_param_tuple(p, space, &data);
+
+ if (has_domain)
+ p = isl_printer_print_str(p, "(");
+
+ data.print_dim = &print_union_pw_aff_dim;
+ data.user = mupa;
+
+ p = isl_print_space(space, p, 0, &data);
+ isl_space_free(space);
+
+ if (has_domain) {
+ p = isl_printer_print_str(p, " : ");
+ p = isl_printer_print_union_set_isl_body(p, mupa->u.dom);
+ p = isl_printer_print_str(p, ")");
+ }
+
+ return p;
+}
+
+/* Print the isl_multi_union_pw_aff "mupa" to "p" in isl format.
+ *
+ * We currently only support an isl format.
+ */
+__isl_give isl_printer *isl_printer_print_multi_union_pw_aff(
+ __isl_take isl_printer *p, __isl_keep isl_multi_union_pw_aff *mupa)
+{
+ if (!p || !mupa)
+ return isl_printer_free(p);
+
+ if (p->output_format == ISL_FORMAT_ISL)
+ return print_multi_union_pw_aff_isl(p, mupa);
+ isl_die(isl_printer_get_ctx(p), isl_error_unsupported,
+ "unsupported output format", return isl_printer_free(p));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_output_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_output_private.h
new file mode 100644
index 00000000000..2e4701dda62
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_output_private.h
@@ -0,0 +1,27 @@
+#include <isl/space.h>
+#include <isl/printer.h>
+
+/* Internal data structure for isl_print_space.
+ *
+ * latex is set if that is the output format.
+ * print_dim (if not NULL) is called on each dimension.
+ * user is set by the caller of print_space and may be used inside print_dim.
+ *
+ * space is the global space that is being printed. This field is set by
+ * print_space.
+ * type is the tuple of the global space that is currently being printed.
+ * This field is set by print_space.
+ */
+struct isl_print_space_data {
+ int latex;
+ __isl_give isl_printer *(*print_dim)(__isl_take isl_printer *p,
+ struct isl_print_space_data *data, unsigned pos);
+ void *user;
+
+ isl_space *space;
+ enum isl_dim_type type;
+};
+
+__isl_give isl_printer *isl_print_space(__isl_keep isl_space *space,
+ __isl_take isl_printer *p, int rational,
+ struct isl_print_space_data *data);
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_point.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_point.c
new file mode 100644
index 00000000000..f7da70644ef
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_point.c
@@ -0,0 +1,818 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2013 Ecole Normale Superieure
+ * Copyright 2015 Sven Verdoolaege
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ * and Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_map_private.h>
+#include <isl_point_private.h>
+#include <isl/set.h>
+#include <isl/union_set.h>
+#include <isl_sample.h>
+#include <isl_scan.h>
+#include <isl_seq.h>
+#include <isl_space_private.h>
+#include <isl_local_private.h>
+#include <isl_val_private.h>
+#include <isl_vec_private.h>
+#include <isl_output_private.h>
+
+#include <set_to_map.c>
+
+isl_ctx *isl_point_get_ctx(__isl_keep isl_point *pnt)
+{
+ return pnt ? isl_space_get_ctx(pnt->dim) : NULL;
+}
+
+/* Return the space of "pnt".
+ */
+__isl_keep isl_space *isl_point_peek_space(__isl_keep isl_point *pnt)
+{
+ return pnt ? pnt->dim : NULL;
+}
+
+__isl_give isl_space *isl_point_get_space(__isl_keep isl_point *pnt)
+{
+ return isl_space_copy(isl_point_peek_space(pnt));
+}
+
+#undef TYPE1
+#define TYPE1 isl_basic_map
+#undef TYPE2
+#define TYPE2 isl_point
+#undef TYPE_PAIR
+#define TYPE_PAIR isl_basic_map_point
+
+static
+#include "isl_type_has_equal_space_templ.c"
+static
+#include "isl_type_check_equal_space_templ.c"
+
+__isl_give isl_point *isl_point_alloc(__isl_take isl_space *space,
+ __isl_take isl_vec *vec)
+{
+ struct isl_point *pnt;
+ isl_size dim;
+
+ dim = isl_space_dim(space, isl_dim_all);
+ if (dim < 0 || !vec)
+ goto error;
+
+ if (vec->size > 1 + dim) {
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ goto error;
+ vec->size = 1 + dim;
+ }
+
+ pnt = isl_alloc_type(space->ctx, struct isl_point);
+ if (!pnt)
+ goto error;
+
+ pnt->ref = 1;
+ pnt->dim = space;
+ pnt->vec = vec;
+
+ return pnt;
+error:
+ isl_space_free(space);
+ isl_vec_free(vec);
+ return NULL;
+}
+
+__isl_give isl_point *isl_point_zero(__isl_take isl_space *space)
+{
+ isl_vec *vec;
+ isl_size dim;
+
+ dim = isl_space_dim(space, isl_dim_all);
+ if (dim < 0)
+ goto error;
+ vec = isl_vec_alloc(space->ctx, 1 + dim);
+ if (!vec)
+ goto error;
+ isl_int_set_si(vec->el[0], 1);
+ isl_seq_clr(vec->el + 1, vec->size - 1);
+ return isl_point_alloc(space, vec);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_point *isl_point_dup(__isl_keep isl_point *pnt)
+{
+ struct isl_point *pnt2;
+
+ if (!pnt)
+ return NULL;
+ pnt2 = isl_point_alloc(isl_space_copy(pnt->dim), isl_vec_copy(pnt->vec));
+ return pnt2;
+}
+
+__isl_give isl_point *isl_point_cow(__isl_take isl_point *pnt)
+{
+ struct isl_point *pnt2;
+ if (!pnt)
+ return NULL;
+
+ if (pnt->ref == 1)
+ return pnt;
+
+ pnt2 = isl_point_dup(pnt);
+ isl_point_free(pnt);
+ return pnt2;
+}
+
+__isl_give isl_point *isl_point_copy(__isl_keep isl_point *pnt)
+{
+ if (!pnt)
+ return NULL;
+
+ pnt->ref++;
+ return pnt;
+}
+
+__isl_null isl_point *isl_point_free(__isl_take isl_point *pnt)
+{
+ if (!pnt)
+ return NULL;
+
+ if (--pnt->ref > 0)
+ return NULL;
+
+ isl_space_free(pnt->dim);
+ isl_vec_free(pnt->vec);
+ free(pnt);
+ return NULL;
+}
+
+__isl_give isl_point *isl_point_void(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+
+ return isl_point_alloc(space, isl_vec_alloc(space->ctx, 0));
+}
+
+isl_bool isl_point_is_void(__isl_keep isl_point *pnt)
+{
+ if (!pnt)
+ return isl_bool_error;
+
+ return isl_bool_ok(pnt->vec->size == 0);
+}
+
+/* Return the space of "pnt".
+ * This may be either a copy or the space itself
+ * if there is only one reference to "pnt".
+ * This allows the space to be modified inplace
+ * if both the point and its space have only a single reference.
+ * The caller is not allowed to modify "pnt" between this call and
+ * a subsequent call to isl_point_restore_space.
+ * The only exception is that isl_point_free can be called instead.
+ */
+__isl_give isl_space *isl_point_take_space(__isl_keep isl_point *pnt)
+{
+ isl_space *space;
+
+ if (!pnt)
+ return NULL;
+ if (pnt->ref != 1)
+ return isl_point_get_space(pnt);
+ space = pnt->dim;
+ pnt->dim = NULL;
+ return space;
+}
+
+/* Set the space of "pnt" to "space", where the space of "pnt" may be missing
+ * due to a preceding call to isl_point_take_space.
+ * However, in this case, "pnt" only has a single reference and
+ * then the call to isl_point_cow has no effect.
+ */
+__isl_give isl_point *isl_point_restore_space(__isl_take isl_point *pnt,
+ __isl_take isl_space *space)
+{
+ if (!pnt || !space)
+ goto error;
+
+ if (pnt->dim == space) {
+ isl_space_free(space);
+ return pnt;
+ }
+
+ pnt = isl_point_cow(pnt);
+ if (!pnt)
+ goto error;
+ isl_space_free(pnt->dim);
+ pnt->dim = space;
+
+ return pnt;
+error:
+ isl_point_free(pnt);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Return the coordinate vector of "pnt".
+ */
+__isl_keep isl_vec *isl_point_peek_vec(__isl_keep isl_point *pnt)
+{
+ return pnt ? pnt->vec : NULL;
+}
+
+/* Return a copy of the coordinate vector of "pnt".
+ */
+__isl_give isl_vec *isl_point_get_vec(__isl_keep isl_point *pnt)
+{
+ return isl_vec_copy(isl_point_peek_vec(pnt));
+}
+
+/* Return the coordinate vector of "pnt".
+ * This may be either a copy or the coordinate vector itself
+ * if there is only one reference to "pnt".
+ * This allows the coordinate vector to be modified inplace
+ * if both the point and its coordinate vector have only a single reference.
+ * The caller is not allowed to modify "pnt" between this call and
+ * a subsequent call to isl_point_restore_vec.
+ * The only exception is that isl_point_free can be called instead.
+ */
+__isl_give isl_vec *isl_point_take_vec(__isl_keep isl_point *pnt)
+{
+ isl_vec *vec;
+
+ if (!pnt)
+ return NULL;
+ if (pnt->ref != 1)
+ return isl_point_get_vec(pnt);
+ vec = pnt->vec;
+ pnt->vec = NULL;
+ return vec;
+}
+
+/* Set the coordinate vector of "pnt" to "vec",
+ * where the coordinate vector of "pnt" may be missing
+ * due to a preceding call to isl_point_take_vec.
+ * However, in this case, "pnt" only has a single reference and
+ * then the call to isl_point_cow has no effect.
+ */
+__isl_give isl_point *isl_point_restore_vec(__isl_take isl_point *pnt,
+ __isl_take isl_vec *vec)
+{
+ if (!pnt || !vec)
+ goto error;
+
+ if (pnt->vec == vec) {
+ isl_vec_free(vec);
+ return pnt;
+ }
+
+ pnt = isl_point_cow(pnt);
+ if (!pnt)
+ goto error;
+ isl_vec_free(pnt->vec);
+ pnt->vec = vec;
+
+ return pnt;
+error:
+ isl_point_free(pnt);
+ isl_vec_free(vec);
+ return NULL;
+}
+
+/* Return the number of variables of the given type.
+ */
+static isl_size isl_point_dim(__isl_keep isl_point *pnt, enum isl_dim_type type)
+{
+ return isl_space_dim(isl_point_peek_space(pnt), type);
+}
+
+/* Return the position of the coordinates of the given type
+ * within the sequence of coordinates of "pnt".
+ */
+static isl_size isl_point_var_offset(__isl_keep isl_point *pnt,
+ enum isl_dim_type type)
+{
+ return pnt ? isl_space_offset(pnt->dim, type) : isl_size_error;
+}
+
+#undef TYPE
+#define TYPE isl_point
+static
+#include "check_type_range_templ.c"
+
+/* Return the value of coordinate "pos" of type "type" of "pnt".
+ */
+__isl_give isl_val *isl_point_get_coordinate_val(__isl_keep isl_point *pnt,
+ enum isl_dim_type type, int pos)
+{
+ isl_ctx *ctx;
+ isl_val *v;
+ isl_size off;
+
+ if (!pnt)
+ return NULL;
+
+ ctx = isl_point_get_ctx(pnt);
+ if (isl_point_is_void(pnt))
+ isl_die(ctx, isl_error_invalid,
+ "void point does not have coordinates", return NULL);
+ if (isl_point_check_range(pnt, type, pos, 1) < 0)
+ return NULL;
+
+ off = isl_point_var_offset(pnt, type);
+ if (off < 0)
+ return NULL;
+ pos += off;
+
+ v = isl_val_rat_from_isl_int(ctx, pnt->vec->el[1 + pos],
+ pnt->vec->el[0]);
+ return isl_val_normalize(v);
+}
+
+/* Set all entries of "mv" to NaN.
+ */
+static __isl_give isl_multi_val *set_nan(__isl_take isl_multi_val *mv)
+{
+ int i;
+ isl_size n;
+ isl_val *v;
+
+ n = isl_multi_val_size(mv);
+ if (n < 0)
+ return isl_multi_val_free(mv);
+ v = isl_val_nan(isl_multi_val_get_ctx(mv));
+ for (i = 0; i < n; ++i)
+ mv = isl_multi_val_set_at(mv, i, isl_val_copy(v));
+ isl_val_free(v);
+
+ return mv;
+}
+
+/* Return the values of the set dimensions of "pnt".
+ * Return a sequence of NaNs in case of a void point.
+ */
+__isl_give isl_multi_val *isl_point_get_multi_val(__isl_keep isl_point *pnt)
+{
+ int i;
+ isl_bool is_void;
+ isl_size n;
+ isl_multi_val *mv;
+
+ is_void = isl_point_is_void(pnt);
+ if (is_void < 0)
+ return NULL;
+
+ mv = isl_multi_val_alloc(isl_point_get_space(pnt));
+ if (is_void)
+ return set_nan(mv);
+ n = isl_multi_val_size(mv);
+ if (n < 0)
+ return isl_multi_val_free(mv);
+ for (i = 0; i < n; ++i) {
+ isl_val *v;
+
+ v = isl_point_get_coordinate_val(pnt, isl_dim_set, i);
+ mv = isl_multi_val_set_at(mv, i, v);
+ }
+
+ return mv;
+}
+
+/* Replace coordinate "pos" of type "type" of "pnt" by "v".
+ */
+__isl_give isl_point *isl_point_set_coordinate_val(__isl_take isl_point *pnt,
+ enum isl_dim_type type, int pos, __isl_take isl_val *v)
+{
+ if (!pnt || !v)
+ goto error;
+ if (isl_point_is_void(pnt))
+ isl_die(isl_point_get_ctx(pnt), isl_error_invalid,
+ "void point does not have coordinates", goto error);
+ if (isl_point_check_range(pnt, type, pos, 1) < 0)
+ goto error;
+ if (!isl_val_is_rat(v))
+ isl_die(isl_point_get_ctx(pnt), isl_error_invalid,
+ "expecting rational value", goto error);
+
+ pos += isl_space_offset(isl_point_peek_space(pnt), type);
+ if (isl_int_eq(pnt->vec->el[1 + pos], v->n) &&
+ isl_int_eq(pnt->vec->el[0], v->d)) {
+ isl_val_free(v);
+ return pnt;
+ }
+
+ pnt = isl_point_cow(pnt);
+ if (!pnt)
+ goto error;
+ pnt->vec = isl_vec_cow(pnt->vec);
+ if (!pnt->vec)
+ goto error;
+
+ if (isl_int_eq(pnt->vec->el[0], v->d)) {
+ isl_int_set(pnt->vec->el[1 + pos], v->n);
+ } else if (isl_int_is_one(v->d)) {
+ isl_int_mul(pnt->vec->el[1 + pos], pnt->vec->el[0], v->n);
+ } else {
+ isl_seq_scale(pnt->vec->el + 1,
+ pnt->vec->el + 1, v->d, pnt->vec->size - 1);
+ isl_int_mul(pnt->vec->el[1 + pos], pnt->vec->el[0], v->n);
+ isl_int_mul(pnt->vec->el[0], pnt->vec->el[0], v->d);
+ pnt->vec = isl_vec_normalize(pnt->vec);
+ if (!pnt->vec)
+ goto error;
+ }
+
+ isl_val_free(v);
+ return pnt;
+error:
+ isl_val_free(v);
+ isl_point_free(pnt);
+ return NULL;
+}
+
+__isl_give isl_point *isl_point_add_ui(__isl_take isl_point *pnt,
+ enum isl_dim_type type, int pos, unsigned val)
+{
+ isl_size off;
+
+ if (!pnt || isl_point_is_void(pnt))
+ return pnt;
+
+ pnt = isl_point_cow(pnt);
+ if (!pnt)
+ return NULL;
+ pnt->vec = isl_vec_cow(pnt->vec);
+ if (!pnt->vec)
+ goto error;
+
+ off = isl_point_var_offset(pnt, type);
+ if (off < 0)
+ goto error;
+ pos += off;
+
+ isl_int_add_ui(pnt->vec->el[1 + pos], pnt->vec->el[1 + pos], val);
+
+ return pnt;
+error:
+ isl_point_free(pnt);
+ return NULL;
+}
+
+__isl_give isl_point *isl_point_sub_ui(__isl_take isl_point *pnt,
+ enum isl_dim_type type, int pos, unsigned val)
+{
+ isl_size off;
+
+ if (!pnt || isl_point_is_void(pnt))
+ return pnt;
+
+ pnt = isl_point_cow(pnt);
+ if (!pnt)
+ return NULL;
+ pnt->vec = isl_vec_cow(pnt->vec);
+ if (!pnt->vec)
+ goto error;
+
+ off = isl_point_var_offset(pnt, type);
+ if (off < 0)
+ goto error;
+ pos += off;
+
+ isl_int_sub_ui(pnt->vec->el[1 + pos], pnt->vec->el[1 + pos], val);
+
+ return pnt;
+error:
+ isl_point_free(pnt);
+ return NULL;
+}
+
+struct isl_foreach_point {
+ struct isl_scan_callback callback;
+ isl_stat (*fn)(__isl_take isl_point *pnt, void *user);
+ void *user;
+ isl_space *dim;
+};
+
+static isl_stat foreach_point(struct isl_scan_callback *cb,
+ __isl_take isl_vec *sample)
+{
+ struct isl_foreach_point *fp = (struct isl_foreach_point *)cb;
+ isl_point *pnt;
+
+ pnt = isl_point_alloc(isl_space_copy(fp->dim), sample);
+
+ return fp->fn(pnt, fp->user);
+}
+
+isl_stat isl_set_foreach_point(__isl_keep isl_set *set,
+ isl_stat (*fn)(__isl_take isl_point *pnt, void *user), void *user)
+{
+ struct isl_foreach_point fp = { { &foreach_point }, fn, user };
+ int i;
+
+ if (!set)
+ return isl_stat_error;
+
+ fp.dim = isl_set_get_space(set);
+ if (!fp.dim)
+ return isl_stat_error;
+
+ set = isl_set_copy(set);
+ set = isl_set_cow(set);
+ set = isl_set_make_disjoint(set);
+ set = isl_set_compute_divs(set);
+ if (!set)
+ goto error;
+
+ for (i = 0; i < set->n; ++i)
+ if (isl_basic_set_scan(isl_basic_set_copy(set->p[i]),
+ &fp.callback) < 0)
+ goto error;
+
+ isl_set_free(set);
+ isl_space_free(fp.dim);
+
+ return isl_stat_ok;
+error:
+ isl_set_free(set);
+ isl_space_free(fp.dim);
+ return isl_stat_error;
+}
+
+/* Return 1 if "bmap" contains the point "point".
+ * "bmap" is assumed to have known divs.
+ * The point is first extended with the divs and then passed
+ * to basic_map_contains.
+ */
+isl_bool isl_basic_map_contains_point(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_point *point)
+{
+ isl_local *local;
+ isl_vec *vec;
+ isl_bool contains;
+
+ if (isl_basic_map_point_check_equal_space(bmap, point) < 0)
+ return isl_bool_error;
+ if (bmap->n_div == 0)
+ return isl_basic_map_contains(bmap, point->vec);
+
+ local = isl_local_alloc_from_mat(isl_basic_map_get_divs(bmap));
+ vec = isl_point_get_vec(point);
+ vec = isl_local_extend_point_vec(local, vec);
+ isl_local_free(local);
+
+ contains = isl_basic_map_contains(bmap, vec);
+
+ isl_vec_free(vec);
+ return contains;
+}
+
+isl_bool isl_map_contains_point(__isl_keep isl_map *map,
+ __isl_keep isl_point *point)
+{
+ int i;
+ isl_bool found = isl_bool_false;
+
+ if (!map || !point)
+ return isl_bool_error;
+
+ map = isl_map_copy(map);
+ map = isl_map_compute_divs(map);
+ if (!map)
+ return isl_bool_error;
+
+ for (i = 0; i < map->n; ++i) {
+ found = isl_basic_map_contains_point(map->p[i], point);
+ if (found < 0)
+ goto error;
+ if (found)
+ break;
+ }
+ isl_map_free(map);
+
+ return found;
+error:
+ isl_map_free(map);
+ return isl_bool_error;
+}
+
+isl_bool isl_set_contains_point(__isl_keep isl_set *set,
+ __isl_keep isl_point *point)
+{
+ return isl_map_contains_point(set_to_map(set), point);
+}
+
+__isl_give isl_basic_set *isl_basic_set_from_point(__isl_take isl_point *pnt)
+{
+ isl_basic_set *bset;
+ isl_basic_set *model;
+
+ if (!pnt)
+ return NULL;
+
+ model = isl_basic_set_empty(isl_space_copy(pnt->dim));
+ bset = isl_basic_set_from_vec(isl_vec_copy(pnt->vec));
+ bset = isl_basic_set_from_underlying_set(bset, model);
+ isl_point_free(pnt);
+
+ return bset;
+}
+
+__isl_give isl_set *isl_set_from_point(__isl_take isl_point *pnt)
+{
+ isl_basic_set *bset;
+ bset = isl_basic_set_from_point(pnt);
+ return isl_set_from_basic_set(bset);
+}
+
+/* This function performs the same operation as isl_set_from_point,
+ * but is considered as a function on an isl_point when exported.
+ */
+__isl_give isl_set *isl_point_to_set(__isl_take isl_point *pnt)
+{
+ return isl_set_from_point(pnt);
+}
+
+/* Construct a union set, containing the single element "pnt".
+ * If "pnt" is void, then return an empty union set.
+ */
+__isl_give isl_union_set *isl_union_set_from_point(__isl_take isl_point *pnt)
+{
+ if (!pnt)
+ return NULL;
+ if (isl_point_is_void(pnt)) {
+ isl_space *space;
+
+ space = isl_point_get_space(pnt);
+ isl_point_free(pnt);
+ return isl_union_set_empty(space);
+ }
+
+ return isl_union_set_from_set(isl_set_from_point(pnt));
+}
+
+__isl_give isl_basic_set *isl_basic_set_box_from_points(
+ __isl_take isl_point *pnt1, __isl_take isl_point *pnt2)
+{
+ isl_basic_set *bset = NULL;
+ isl_size total;
+ int i;
+ int k;
+ isl_int t;
+
+ isl_int_init(t);
+
+ if (!pnt1 || !pnt2)
+ goto error;
+
+ isl_assert(pnt1->dim->ctx,
+ isl_space_is_equal(pnt1->dim, pnt2->dim), goto error);
+
+ if (isl_point_is_void(pnt1) && isl_point_is_void(pnt2)) {
+ isl_space *space = isl_space_copy(pnt1->dim);
+ isl_point_free(pnt1);
+ isl_point_free(pnt2);
+ isl_int_clear(t);
+ return isl_basic_set_empty(space);
+ }
+ if (isl_point_is_void(pnt1)) {
+ isl_point_free(pnt1);
+ isl_int_clear(t);
+ return isl_basic_set_from_point(pnt2);
+ }
+ if (isl_point_is_void(pnt2)) {
+ isl_point_free(pnt2);
+ isl_int_clear(t);
+ return isl_basic_set_from_point(pnt1);
+ }
+
+ total = isl_point_dim(pnt1, isl_dim_all);
+ if (total < 0)
+ goto error;
+ bset = isl_basic_set_alloc_space(isl_space_copy(pnt1->dim), 0, 0, 2 * total);
+
+ for (i = 0; i < total; ++i) {
+ isl_int_mul(t, pnt1->vec->el[1 + i], pnt2->vec->el[0]);
+ isl_int_submul(t, pnt2->vec->el[1 + i], pnt1->vec->el[0]);
+
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bset->ineq[k] + 1, total);
+ if (isl_int_is_pos(t)) {
+ isl_int_set_si(bset->ineq[k][1 + i], -1);
+ isl_int_set(bset->ineq[k][0], pnt1->vec->el[1 + i]);
+ } else {
+ isl_int_set_si(bset->ineq[k][1 + i], 1);
+ isl_int_neg(bset->ineq[k][0], pnt1->vec->el[1 + i]);
+ }
+ isl_int_fdiv_q(bset->ineq[k][0], bset->ineq[k][0], pnt1->vec->el[0]);
+
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bset->ineq[k] + 1, total);
+ if (isl_int_is_pos(t)) {
+ isl_int_set_si(bset->ineq[k][1 + i], 1);
+ isl_int_neg(bset->ineq[k][0], pnt2->vec->el[1 + i]);
+ } else {
+ isl_int_set_si(bset->ineq[k][1 + i], -1);
+ isl_int_set(bset->ineq[k][0], pnt2->vec->el[1 + i]);
+ }
+ isl_int_fdiv_q(bset->ineq[k][0], bset->ineq[k][0], pnt2->vec->el[0]);
+ }
+
+ bset = isl_basic_set_finalize(bset);
+
+ isl_point_free(pnt1);
+ isl_point_free(pnt2);
+
+ isl_int_clear(t);
+
+ return bset;
+error:
+ isl_point_free(pnt1);
+ isl_point_free(pnt2);
+ isl_int_clear(t);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+__isl_give isl_set *isl_set_box_from_points(__isl_take isl_point *pnt1,
+ __isl_take isl_point *pnt2)
+{
+ isl_basic_set *bset;
+ bset = isl_basic_set_box_from_points(pnt1, pnt2);
+ return isl_set_from_basic_set(bset);
+}
+
+/* Print the coordinate at position "pos" of the point "pnt".
+ */
+static __isl_give isl_printer *print_coordinate(__isl_take isl_printer *p,
+ struct isl_print_space_data *data, unsigned pos)
+{
+ isl_point *pnt = data->user;
+
+ pos += isl_space_offset(data->space, data->type);
+ p = isl_printer_print_isl_int(p, pnt->vec->el[1 + pos]);
+ if (!isl_int_is_one(pnt->vec->el[0])) {
+ p = isl_printer_print_str(p, "/");
+ p = isl_printer_print_isl_int(p, pnt->vec->el[0]);
+ }
+
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_point(
+ __isl_take isl_printer *p, __isl_keep isl_point *pnt)
+{
+ struct isl_print_space_data data = { 0 };
+ int i;
+ isl_size nparam;
+
+ if (!pnt)
+ return p;
+ if (isl_point_is_void(pnt)) {
+ p = isl_printer_print_str(p, "void");
+ return p;
+ }
+
+ nparam = isl_point_dim(pnt, isl_dim_param);
+ if (nparam < 0)
+ return isl_printer_free(p);
+ if (nparam > 0) {
+ p = isl_printer_print_str(p, "[");
+ for (i = 0; i < nparam; ++i) {
+ const char *name;
+ if (i)
+ p = isl_printer_print_str(p, ", ");
+ name = isl_space_get_dim_name(pnt->dim, isl_dim_param, i);
+ if (name) {
+ p = isl_printer_print_str(p, name);
+ p = isl_printer_print_str(p, " = ");
+ }
+ p = isl_printer_print_isl_int(p, pnt->vec->el[1 + i]);
+ if (!isl_int_is_one(pnt->vec->el[0])) {
+ p = isl_printer_print_str(p, "/");
+ p = isl_printer_print_isl_int(p, pnt->vec->el[0]);
+ }
+ }
+ p = isl_printer_print_str(p, "]");
+ p = isl_printer_print_str(p, " -> ");
+ }
+ data.print_dim = &print_coordinate;
+ data.user = pnt;
+ p = isl_printer_print_str(p, "{ ");
+ p = isl_print_space(pnt->dim, p, 0, &data);
+ p = isl_printer_print_str(p, " }");
+ return p;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_point_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_point_private.h
new file mode 100644
index 00000000000..a1e84494f35
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_point_private.h
@@ -0,0 +1,27 @@
+#ifndef ISL_POINT_PRIVATE_H
+#define ISL_POINT_PRIVATE_H
+
+#include <isl/space.h>
+#include <isl/point.h>
+#include <isl/vec.h>
+
+struct isl_point {
+ int ref;
+ isl_space *dim;
+ struct isl_vec *vec;
+};
+
+__isl_give isl_point *isl_point_alloc(__isl_take isl_space *space,
+ __isl_take isl_vec *vec);
+
+__isl_keep isl_space *isl_point_peek_space(__isl_keep isl_point *pnt);
+__isl_give isl_space *isl_point_take_space(__isl_keep isl_point *pnt);
+__isl_give isl_point *isl_point_restore_space(__isl_take isl_point *pnt,
+ __isl_take isl_space *space);
+__isl_keep isl_vec *isl_point_peek_vec(__isl_keep isl_point *pnt);
+__isl_give isl_vec *isl_point_get_vec(__isl_keep isl_point *pnt);
+__isl_give isl_vec *isl_point_take_vec(__isl_keep isl_point *pnt);
+__isl_give isl_point *isl_point_restore_vec(__isl_take isl_point *pnt,
+ __isl_take isl_vec *vec);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_polynomial.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_polynomial.c
new file mode 100644
index 00000000000..0aac613f0c6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_polynomial.c
@@ -0,0 +1,5228 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <stdlib.h>
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl_factorization.h>
+#include <isl_lp_private.h>
+#include <isl_seq.h>
+#include <isl_union_map_private.h>
+#include <isl_constraint_private.h>
+#include <isl_polynomial_private.h>
+#include <isl_point_private.h>
+#include <isl_space_private.h>
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+#include <isl_range.h>
+#include <isl_local.h>
+#include <isl_local_space_private.h>
+#include <isl_aff_private.h>
+#include <isl_val_private.h>
+#include <isl_config.h>
+
+#undef EL_BASE
+#define EL_BASE qpolynomial
+
+#include <isl_list_templ.c>
+
+#undef EL_BASE
+#define EL_BASE pw_qpolynomial
+
+#include <isl_list_templ.c>
+
+static unsigned pos(__isl_keep isl_space *space, enum isl_dim_type type)
+{
+ switch (type) {
+ case isl_dim_param: return 0;
+ case isl_dim_in: return space->nparam;
+ case isl_dim_out: return space->nparam + space->n_in;
+ default: return 0;
+ }
+}
+
+isl_bool isl_poly_is_cst(__isl_keep isl_poly *poly)
+{
+ if (!poly)
+ return isl_bool_error;
+
+ return isl_bool_ok(poly->var < 0);
+}
+
+__isl_keep isl_poly_cst *isl_poly_as_cst(__isl_keep isl_poly *poly)
+{
+ if (!poly)
+ return NULL;
+
+ isl_assert(poly->ctx, poly->var < 0, return NULL);
+
+ return (isl_poly_cst *) poly;
+}
+
+__isl_keep isl_poly_rec *isl_poly_as_rec(__isl_keep isl_poly *poly)
+{
+ if (!poly)
+ return NULL;
+
+ isl_assert(poly->ctx, poly->var >= 0, return NULL);
+
+ return (isl_poly_rec *) poly;
+}
+
+/* Compare two polynomials.
+ *
+ * Return -1 if "poly1" is "smaller" than "poly2", 1 if "poly1" is "greater"
+ * than "poly2" and 0 if they are equal.
+ */
+static int isl_poly_plain_cmp(__isl_keep isl_poly *poly1,
+ __isl_keep isl_poly *poly2)
+{
+ int i;
+ isl_bool is_cst1;
+ isl_poly_rec *rec1, *rec2;
+
+ if (poly1 == poly2)
+ return 0;
+ is_cst1 = isl_poly_is_cst(poly1);
+ if (is_cst1 < 0)
+ return -1;
+ if (!poly2)
+ return 1;
+ if (poly1->var != poly2->var)
+ return poly1->var - poly2->var;
+
+ if (is_cst1) {
+ isl_poly_cst *cst1, *cst2;
+ int cmp;
+
+ cst1 = isl_poly_as_cst(poly1);
+ cst2 = isl_poly_as_cst(poly2);
+ if (!cst1 || !cst2)
+ return 0;
+ cmp = isl_int_cmp(cst1->n, cst2->n);
+ if (cmp != 0)
+ return cmp;
+ return isl_int_cmp(cst1->d, cst2->d);
+ }
+
+ rec1 = isl_poly_as_rec(poly1);
+ rec2 = isl_poly_as_rec(poly2);
+ if (!rec1 || !rec2)
+ return 0;
+
+ if (rec1->n != rec2->n)
+ return rec1->n - rec2->n;
+
+ for (i = 0; i < rec1->n; ++i) {
+ int cmp = isl_poly_plain_cmp(rec1->p[i], rec2->p[i]);
+ if (cmp != 0)
+ return cmp;
+ }
+
+ return 0;
+}
+
+isl_bool isl_poly_is_equal(__isl_keep isl_poly *poly1,
+ __isl_keep isl_poly *poly2)
+{
+ int i;
+ isl_bool is_cst1;
+ isl_poly_rec *rec1, *rec2;
+
+ is_cst1 = isl_poly_is_cst(poly1);
+ if (is_cst1 < 0 || !poly2)
+ return isl_bool_error;
+ if (poly1 == poly2)
+ return isl_bool_true;
+ if (poly1->var != poly2->var)
+ return isl_bool_false;
+ if (is_cst1) {
+ isl_poly_cst *cst1, *cst2;
+ int r;
+ cst1 = isl_poly_as_cst(poly1);
+ cst2 = isl_poly_as_cst(poly2);
+ if (!cst1 || !cst2)
+ return isl_bool_error;
+ r = isl_int_eq(cst1->n, cst2->n) &&
+ isl_int_eq(cst1->d, cst2->d);
+ return isl_bool_ok(r);
+ }
+
+ rec1 = isl_poly_as_rec(poly1);
+ rec2 = isl_poly_as_rec(poly2);
+ if (!rec1 || !rec2)
+ return isl_bool_error;
+
+ if (rec1->n != rec2->n)
+ return isl_bool_false;
+
+ for (i = 0; i < rec1->n; ++i) {
+ isl_bool eq = isl_poly_is_equal(rec1->p[i], rec2->p[i]);
+ if (eq < 0 || !eq)
+ return eq;
+ }
+
+ return isl_bool_true;
+}
+
+isl_bool isl_poly_is_zero(__isl_keep isl_poly *poly)
+{
+ isl_bool is_cst;
+ isl_poly_cst *cst;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0 || !is_cst)
+ return is_cst;
+
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_zero(cst->n) && isl_int_is_pos(cst->d));
+}
+
+int isl_poly_sgn(__isl_keep isl_poly *poly)
+{
+ isl_bool is_cst;
+ isl_poly_cst *cst;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0 || !is_cst)
+ return 0;
+
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ return 0;
+
+ return isl_int_sgn(cst->n);
+}
+
+isl_bool isl_poly_is_nan(__isl_keep isl_poly *poly)
+{
+ isl_bool is_cst;
+ isl_poly_cst *cst;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0 || !is_cst)
+ return is_cst;
+
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_zero(cst->n) && isl_int_is_zero(cst->d));
+}
+
+isl_bool isl_poly_is_infty(__isl_keep isl_poly *poly)
+{
+ isl_bool is_cst;
+ isl_poly_cst *cst;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0 || !is_cst)
+ return is_cst;
+
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_pos(cst->n) && isl_int_is_zero(cst->d));
+}
+
+isl_bool isl_poly_is_neginfty(__isl_keep isl_poly *poly)
+{
+ isl_bool is_cst;
+ isl_poly_cst *cst;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0 || !is_cst)
+ return is_cst;
+
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_neg(cst->n) && isl_int_is_zero(cst->d));
+}
+
+isl_bool isl_poly_is_one(__isl_keep isl_poly *poly)
+{
+ isl_bool is_cst;
+ isl_poly_cst *cst;
+ int r;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0 || !is_cst)
+ return is_cst;
+
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ return isl_bool_error;
+
+ r = isl_int_eq(cst->n, cst->d) && isl_int_is_pos(cst->d);
+ return isl_bool_ok(r);
+}
+
+isl_bool isl_poly_is_negone(__isl_keep isl_poly *poly)
+{
+ isl_bool is_cst;
+ isl_poly_cst *cst;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0 || !is_cst)
+ return is_cst;
+
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_negone(cst->n) && isl_int_is_one(cst->d));
+}
+
+__isl_give isl_poly_cst *isl_poly_cst_alloc(isl_ctx *ctx)
+{
+ isl_poly_cst *cst;
+
+ cst = isl_alloc_type(ctx, struct isl_poly_cst);
+ if (!cst)
+ return NULL;
+
+ cst->poly.ref = 1;
+ cst->poly.ctx = ctx;
+ isl_ctx_ref(ctx);
+ cst->poly.var = -1;
+
+ isl_int_init(cst->n);
+ isl_int_init(cst->d);
+
+ return cst;
+}
+
+__isl_give isl_poly *isl_poly_zero(isl_ctx *ctx)
+{
+ isl_poly_cst *cst;
+
+ cst = isl_poly_cst_alloc(ctx);
+ if (!cst)
+ return NULL;
+
+ isl_int_set_si(cst->n, 0);
+ isl_int_set_si(cst->d, 1);
+
+ return &cst->poly;
+}
+
+__isl_give isl_poly *isl_poly_one(isl_ctx *ctx)
+{
+ isl_poly_cst *cst;
+
+ cst = isl_poly_cst_alloc(ctx);
+ if (!cst)
+ return NULL;
+
+ isl_int_set_si(cst->n, 1);
+ isl_int_set_si(cst->d, 1);
+
+ return &cst->poly;
+}
+
+__isl_give isl_poly *isl_poly_infty(isl_ctx *ctx)
+{
+ isl_poly_cst *cst;
+
+ cst = isl_poly_cst_alloc(ctx);
+ if (!cst)
+ return NULL;
+
+ isl_int_set_si(cst->n, 1);
+ isl_int_set_si(cst->d, 0);
+
+ return &cst->poly;
+}
+
+__isl_give isl_poly *isl_poly_neginfty(isl_ctx *ctx)
+{
+ isl_poly_cst *cst;
+
+ cst = isl_poly_cst_alloc(ctx);
+ if (!cst)
+ return NULL;
+
+ isl_int_set_si(cst->n, -1);
+ isl_int_set_si(cst->d, 0);
+
+ return &cst->poly;
+}
+
+__isl_give isl_poly *isl_poly_nan(isl_ctx *ctx)
+{
+ isl_poly_cst *cst;
+
+ cst = isl_poly_cst_alloc(ctx);
+ if (!cst)
+ return NULL;
+
+ isl_int_set_si(cst->n, 0);
+ isl_int_set_si(cst->d, 0);
+
+ return &cst->poly;
+}
+
+__isl_give isl_poly *isl_poly_rat_cst(isl_ctx *ctx, isl_int n, isl_int d)
+{
+ isl_poly_cst *cst;
+
+ cst = isl_poly_cst_alloc(ctx);
+ if (!cst)
+ return NULL;
+
+ isl_int_set(cst->n, n);
+ isl_int_set(cst->d, d);
+
+ return &cst->poly;
+}
+
+__isl_give isl_poly_rec *isl_poly_alloc_rec(isl_ctx *ctx, int var, int size)
+{
+ isl_poly_rec *rec;
+
+ isl_assert(ctx, var >= 0, return NULL);
+ isl_assert(ctx, size >= 0, return NULL);
+ rec = isl_calloc(ctx, struct isl_poly_rec,
+ sizeof(struct isl_poly_rec) +
+ size * sizeof(struct isl_poly *));
+ if (!rec)
+ return NULL;
+
+ rec->poly.ref = 1;
+ rec->poly.ctx = ctx;
+ isl_ctx_ref(ctx);
+ rec->poly.var = var;
+
+ rec->n = 0;
+ rec->size = size;
+
+ return rec;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_reset_domain_space(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_space *space)
+{
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp || !space)
+ goto error;
+
+ isl_space_free(qp->dim);
+ qp->dim = space;
+
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Reset the space of "qp". This function is called from isl_pw_templ.c
+ * and doesn't know if the space of an element object is represented
+ * directly or through its domain. It therefore passes along both.
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_reset_space_and_domain(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_space *space,
+ __isl_take isl_space *domain)
+{
+ isl_space_free(space);
+ return isl_qpolynomial_reset_domain_space(qp, domain);
+}
+
+isl_ctx *isl_qpolynomial_get_ctx(__isl_keep isl_qpolynomial *qp)
+{
+ return qp ? qp->dim->ctx : NULL;
+}
+
+/* Return the domain space of "qp".
+ */
+static __isl_keep isl_space *isl_qpolynomial_peek_domain_space(
+ __isl_keep isl_qpolynomial *qp)
+{
+ return qp ? qp->dim : NULL;
+}
+
+/* Return a copy of the domain space of "qp".
+ */
+__isl_give isl_space *isl_qpolynomial_get_domain_space(
+ __isl_keep isl_qpolynomial *qp)
+{
+ return isl_space_copy(isl_qpolynomial_peek_domain_space(qp));
+}
+
+#undef TYPE
+#define TYPE isl_qpolynomial
+#undef PEEK_SPACE
+#define PEEK_SPACE peek_domain_space
+
+static
+#include "isl_type_has_equal_space_bin_templ.c"
+static
+#include "isl_type_check_equal_space_templ.c"
+
+#undef PEEK_SPACE
+
+/* Return a copy of the local space on which "qp" is defined.
+ */
+static __isl_give isl_local_space *isl_qpolynomial_get_domain_local_space(
+ __isl_keep isl_qpolynomial *qp)
+{
+ isl_space *space;
+
+ if (!qp)
+ return NULL;
+
+ space = isl_qpolynomial_get_domain_space(qp);
+ return isl_local_space_alloc_div(space, isl_mat_copy(qp->div));
+}
+
+__isl_give isl_space *isl_qpolynomial_get_space(__isl_keep isl_qpolynomial *qp)
+{
+ isl_space *space;
+ if (!qp)
+ return NULL;
+ space = isl_space_copy(qp->dim);
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, 1);
+ return space;
+}
+
+/* Return the number of variables of the given type in the domain of "qp".
+ */
+isl_size isl_qpolynomial_domain_dim(__isl_keep isl_qpolynomial *qp,
+ enum isl_dim_type type)
+{
+ isl_space *space;
+ isl_size dim;
+
+ space = isl_qpolynomial_peek_domain_space(qp);
+
+ if (!space)
+ return isl_size_error;
+ if (type == isl_dim_div)
+ return qp->div->n_row;
+ dim = isl_space_dim(space, type);
+ if (dim < 0)
+ return isl_size_error;
+ if (type == isl_dim_all) {
+ isl_size n_div;
+
+ n_div = isl_qpolynomial_domain_dim(qp, isl_dim_div);
+ if (n_div < 0)
+ return isl_size_error;
+ dim += n_div;
+ }
+ return dim;
+}
+
+/* Given the type of a dimension of an isl_qpolynomial,
+ * return the type of the corresponding dimension in its domain.
+ * This function is only called for "type" equal to isl_dim_in or
+ * isl_dim_param.
+ */
+static enum isl_dim_type domain_type(enum isl_dim_type type)
+{
+ return type == isl_dim_in ? isl_dim_set : type;
+}
+
+/* Externally, an isl_qpolynomial has a map space, but internally, the
+ * ls field corresponds to the domain of that space.
+ */
+isl_size isl_qpolynomial_dim(__isl_keep isl_qpolynomial *qp,
+ enum isl_dim_type type)
+{
+ if (!qp)
+ return isl_size_error;
+ if (type == isl_dim_out)
+ return 1;
+ type = domain_type(type);
+ return isl_qpolynomial_domain_dim(qp, type);
+}
+
+/* Return the offset of the first variable of type "type" within
+ * the variables of the domain of "qp".
+ */
+static isl_size isl_qpolynomial_domain_var_offset(
+ __isl_keep isl_qpolynomial *qp, enum isl_dim_type type)
+{
+ isl_space *space;
+
+ space = isl_qpolynomial_peek_domain_space(qp);
+ if (!space)
+ return isl_size_error;
+
+ switch (type) {
+ case isl_dim_param:
+ case isl_dim_set: return isl_space_offset(space, type);
+ case isl_dim_div: return isl_space_dim(space, isl_dim_all);
+ case isl_dim_cst:
+ default:
+ isl_die(isl_qpolynomial_get_ctx(qp), isl_error_invalid,
+ "invalid dimension type", return isl_size_error);
+ }
+}
+
+/* Return the offset of the first coefficient of type "type" in
+ * the domain of "qp".
+ */
+unsigned isl_qpolynomial_domain_offset(__isl_keep isl_qpolynomial *qp,
+ enum isl_dim_type type)
+{
+ switch (type) {
+ case isl_dim_cst:
+ return 0;
+ case isl_dim_param:
+ case isl_dim_set:
+ case isl_dim_div:
+ return 1 + isl_qpolynomial_domain_var_offset(qp, type);
+ default:
+ return 0;
+ }
+}
+
+isl_bool isl_qpolynomial_is_zero(__isl_keep isl_qpolynomial *qp)
+{
+ return qp ? isl_poly_is_zero(qp->poly) : isl_bool_error;
+}
+
+isl_bool isl_qpolynomial_is_one(__isl_keep isl_qpolynomial *qp)
+{
+ return qp ? isl_poly_is_one(qp->poly) : isl_bool_error;
+}
+
+isl_bool isl_qpolynomial_is_nan(__isl_keep isl_qpolynomial *qp)
+{
+ return qp ? isl_poly_is_nan(qp->poly) : isl_bool_error;
+}
+
+isl_bool isl_qpolynomial_is_infty(__isl_keep isl_qpolynomial *qp)
+{
+ return qp ? isl_poly_is_infty(qp->poly) : isl_bool_error;
+}
+
+isl_bool isl_qpolynomial_is_neginfty(__isl_keep isl_qpolynomial *qp)
+{
+ return qp ? isl_poly_is_neginfty(qp->poly) : isl_bool_error;
+}
+
+int isl_qpolynomial_sgn(__isl_keep isl_qpolynomial *qp)
+{
+ return qp ? isl_poly_sgn(qp->poly) : 0;
+}
+
+static void poly_free_cst(__isl_take isl_poly_cst *cst)
+{
+ isl_int_clear(cst->n);
+ isl_int_clear(cst->d);
+}
+
+static void poly_free_rec(__isl_take isl_poly_rec *rec)
+{
+ int i;
+
+ for (i = 0; i < rec->n; ++i)
+ isl_poly_free(rec->p[i]);
+}
+
+__isl_give isl_poly *isl_poly_copy(__isl_keep isl_poly *poly)
+{
+ if (!poly)
+ return NULL;
+
+ poly->ref++;
+ return poly;
+}
+
+__isl_give isl_poly *isl_poly_dup_cst(__isl_keep isl_poly *poly)
+{
+ isl_poly_cst *cst;
+ isl_poly_cst *dup;
+
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ return NULL;
+
+ dup = isl_poly_as_cst(isl_poly_zero(poly->ctx));
+ if (!dup)
+ return NULL;
+ isl_int_set(dup->n, cst->n);
+ isl_int_set(dup->d, cst->d);
+
+ return &dup->poly;
+}
+
+__isl_give isl_poly *isl_poly_dup_rec(__isl_keep isl_poly *poly)
+{
+ int i;
+ isl_poly_rec *rec;
+ isl_poly_rec *dup;
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ return NULL;
+
+ dup = isl_poly_alloc_rec(poly->ctx, poly->var, rec->n);
+ if (!dup)
+ return NULL;
+
+ for (i = 0; i < rec->n; ++i) {
+ dup->p[i] = isl_poly_copy(rec->p[i]);
+ if (!dup->p[i])
+ goto error;
+ dup->n++;
+ }
+
+ return &dup->poly;
+error:
+ isl_poly_free(&dup->poly);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_dup(__isl_keep isl_poly *poly)
+{
+ isl_bool is_cst;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return NULL;
+ if (is_cst)
+ return isl_poly_dup_cst(poly);
+ else
+ return isl_poly_dup_rec(poly);
+}
+
+__isl_give isl_poly *isl_poly_cow(__isl_take isl_poly *poly)
+{
+ if (!poly)
+ return NULL;
+
+ if (poly->ref == 1)
+ return poly;
+ poly->ref--;
+ return isl_poly_dup(poly);
+}
+
+__isl_null isl_poly *isl_poly_free(__isl_take isl_poly *poly)
+{
+ if (!poly)
+ return NULL;
+
+ if (--poly->ref > 0)
+ return NULL;
+
+ if (poly->var < 0)
+ poly_free_cst((isl_poly_cst *) poly);
+ else
+ poly_free_rec((isl_poly_rec *) poly);
+
+ isl_ctx_deref(poly->ctx);
+ free(poly);
+ return NULL;
+}
+
+static void isl_poly_cst_reduce(__isl_keep isl_poly_cst *cst)
+{
+ isl_int gcd;
+
+ isl_int_init(gcd);
+ isl_int_gcd(gcd, cst->n, cst->d);
+ if (!isl_int_is_zero(gcd) && !isl_int_is_one(gcd)) {
+ isl_int_divexact(cst->n, cst->n, gcd);
+ isl_int_divexact(cst->d, cst->d, gcd);
+ }
+ isl_int_clear(gcd);
+}
+
+__isl_give isl_poly *isl_poly_sum_cst(__isl_take isl_poly *poly1,
+ __isl_take isl_poly *poly2)
+{
+ isl_poly_cst *cst1;
+ isl_poly_cst *cst2;
+
+ poly1 = isl_poly_cow(poly1);
+ if (!poly1 || !poly2)
+ goto error;
+
+ cst1 = isl_poly_as_cst(poly1);
+ cst2 = isl_poly_as_cst(poly2);
+
+ if (isl_int_eq(cst1->d, cst2->d))
+ isl_int_add(cst1->n, cst1->n, cst2->n);
+ else {
+ isl_int_mul(cst1->n, cst1->n, cst2->d);
+ isl_int_addmul(cst1->n, cst2->n, cst1->d);
+ isl_int_mul(cst1->d, cst1->d, cst2->d);
+ }
+
+ isl_poly_cst_reduce(cst1);
+
+ isl_poly_free(poly2);
+ return poly1;
+error:
+ isl_poly_free(poly1);
+ isl_poly_free(poly2);
+ return NULL;
+}
+
+static __isl_give isl_poly *replace_by_zero(__isl_take isl_poly *poly)
+{
+ struct isl_ctx *ctx;
+
+ if (!poly)
+ return NULL;
+ ctx = poly->ctx;
+ isl_poly_free(poly);
+ return isl_poly_zero(ctx);
+}
+
+static __isl_give isl_poly *replace_by_constant_term(__isl_take isl_poly *poly)
+{
+ isl_poly_rec *rec;
+ isl_poly *cst;
+
+ if (!poly)
+ return NULL;
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ goto error;
+ cst = isl_poly_copy(rec->p[0]);
+ isl_poly_free(poly);
+ return cst;
+error:
+ isl_poly_free(poly);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_sum(__isl_take isl_poly *poly1,
+ __isl_take isl_poly *poly2)
+{
+ int i;
+ isl_bool is_zero, is_nan, is_cst;
+ isl_poly_rec *rec1, *rec2;
+
+ if (!poly1 || !poly2)
+ goto error;
+
+ is_nan = isl_poly_is_nan(poly1);
+ if (is_nan < 0)
+ goto error;
+ if (is_nan) {
+ isl_poly_free(poly2);
+ return poly1;
+ }
+
+ is_nan = isl_poly_is_nan(poly2);
+ if (is_nan < 0)
+ goto error;
+ if (is_nan) {
+ isl_poly_free(poly1);
+ return poly2;
+ }
+
+ is_zero = isl_poly_is_zero(poly1);
+ if (is_zero < 0)
+ goto error;
+ if (is_zero) {
+ isl_poly_free(poly1);
+ return poly2;
+ }
+
+ is_zero = isl_poly_is_zero(poly2);
+ if (is_zero < 0)
+ goto error;
+ if (is_zero) {
+ isl_poly_free(poly2);
+ return poly1;
+ }
+
+ if (poly1->var < poly2->var)
+ return isl_poly_sum(poly2, poly1);
+
+ if (poly2->var < poly1->var) {
+ isl_poly_rec *rec;
+ isl_bool is_infty;
+
+ is_infty = isl_poly_is_infty(poly2);
+ if (is_infty >= 0 && !is_infty)
+ is_infty = isl_poly_is_neginfty(poly2);
+ if (is_infty < 0)
+ goto error;
+ if (is_infty) {
+ isl_poly_free(poly1);
+ return poly2;
+ }
+ poly1 = isl_poly_cow(poly1);
+ rec = isl_poly_as_rec(poly1);
+ if (!rec)
+ goto error;
+ rec->p[0] = isl_poly_sum(rec->p[0], poly2);
+ if (rec->n == 1)
+ poly1 = replace_by_constant_term(poly1);
+ return poly1;
+ }
+
+ is_cst = isl_poly_is_cst(poly1);
+ if (is_cst < 0)
+ goto error;
+ if (is_cst)
+ return isl_poly_sum_cst(poly1, poly2);
+
+ rec1 = isl_poly_as_rec(poly1);
+ rec2 = isl_poly_as_rec(poly2);
+ if (!rec1 || !rec2)
+ goto error;
+
+ if (rec1->n < rec2->n)
+ return isl_poly_sum(poly2, poly1);
+
+ poly1 = isl_poly_cow(poly1);
+ rec1 = isl_poly_as_rec(poly1);
+ if (!rec1)
+ goto error;
+
+ for (i = rec2->n - 1; i >= 0; --i) {
+ isl_bool is_zero;
+
+ rec1->p[i] = isl_poly_sum(rec1->p[i],
+ isl_poly_copy(rec2->p[i]));
+ if (!rec1->p[i])
+ goto error;
+ if (i != rec1->n - 1)
+ continue;
+ is_zero = isl_poly_is_zero(rec1->p[i]);
+ if (is_zero < 0)
+ goto error;
+ if (is_zero) {
+ isl_poly_free(rec1->p[i]);
+ rec1->n--;
+ }
+ }
+
+ if (rec1->n == 0)
+ poly1 = replace_by_zero(poly1);
+ else if (rec1->n == 1)
+ poly1 = replace_by_constant_term(poly1);
+
+ isl_poly_free(poly2);
+
+ return poly1;
+error:
+ isl_poly_free(poly1);
+ isl_poly_free(poly2);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_cst_add_isl_int(__isl_take isl_poly *poly,
+ isl_int v)
+{
+ isl_poly_cst *cst;
+
+ poly = isl_poly_cow(poly);
+ if (!poly)
+ return NULL;
+
+ cst = isl_poly_as_cst(poly);
+
+ isl_int_addmul(cst->n, cst->d, v);
+
+ return poly;
+}
+
+__isl_give isl_poly *isl_poly_add_isl_int(__isl_take isl_poly *poly, isl_int v)
+{
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return isl_poly_free(poly);
+ if (is_cst)
+ return isl_poly_cst_add_isl_int(poly, v);
+
+ poly = isl_poly_cow(poly);
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ goto error;
+
+ rec->p[0] = isl_poly_add_isl_int(rec->p[0], v);
+ if (!rec->p[0])
+ goto error;
+
+ return poly;
+error:
+ isl_poly_free(poly);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_cst_mul_isl_int(__isl_take isl_poly *poly,
+ isl_int v)
+{
+ isl_bool is_zero;
+ isl_poly_cst *cst;
+
+ is_zero = isl_poly_is_zero(poly);
+ if (is_zero < 0)
+ return isl_poly_free(poly);
+ if (is_zero)
+ return poly;
+
+ poly = isl_poly_cow(poly);
+ if (!poly)
+ return NULL;
+
+ cst = isl_poly_as_cst(poly);
+
+ isl_int_mul(cst->n, cst->n, v);
+
+ return poly;
+}
+
+__isl_give isl_poly *isl_poly_mul_isl_int(__isl_take isl_poly *poly, isl_int v)
+{
+ int i;
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return isl_poly_free(poly);
+ if (is_cst)
+ return isl_poly_cst_mul_isl_int(poly, v);
+
+ poly = isl_poly_cow(poly);
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ goto error;
+
+ for (i = 0; i < rec->n; ++i) {
+ rec->p[i] = isl_poly_mul_isl_int(rec->p[i], v);
+ if (!rec->p[i])
+ goto error;
+ }
+
+ return poly;
+error:
+ isl_poly_free(poly);
+ return NULL;
+}
+
+/* Multiply the constant polynomial "poly" by "v".
+ */
+static __isl_give isl_poly *isl_poly_cst_scale_val(__isl_take isl_poly *poly,
+ __isl_keep isl_val *v)
+{
+ isl_bool is_zero;
+ isl_poly_cst *cst;
+
+ is_zero = isl_poly_is_zero(poly);
+ if (is_zero < 0)
+ return isl_poly_free(poly);
+ if (is_zero)
+ return poly;
+
+ poly = isl_poly_cow(poly);
+ if (!poly)
+ return NULL;
+
+ cst = isl_poly_as_cst(poly);
+
+ isl_int_mul(cst->n, cst->n, v->n);
+ isl_int_mul(cst->d, cst->d, v->d);
+ isl_poly_cst_reduce(cst);
+
+ return poly;
+}
+
+/* Multiply the polynomial "poly" by "v".
+ */
+static __isl_give isl_poly *isl_poly_scale_val(__isl_take isl_poly *poly,
+ __isl_keep isl_val *v)
+{
+ int i;
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return isl_poly_free(poly);
+ if (is_cst)
+ return isl_poly_cst_scale_val(poly, v);
+
+ poly = isl_poly_cow(poly);
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ goto error;
+
+ for (i = 0; i < rec->n; ++i) {
+ rec->p[i] = isl_poly_scale_val(rec->p[i], v);
+ if (!rec->p[i])
+ goto error;
+ }
+
+ return poly;
+error:
+ isl_poly_free(poly);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_mul_cst(__isl_take isl_poly *poly1,
+ __isl_take isl_poly *poly2)
+{
+ isl_poly_cst *cst1;
+ isl_poly_cst *cst2;
+
+ poly1 = isl_poly_cow(poly1);
+ if (!poly1 || !poly2)
+ goto error;
+
+ cst1 = isl_poly_as_cst(poly1);
+ cst2 = isl_poly_as_cst(poly2);
+
+ isl_int_mul(cst1->n, cst1->n, cst2->n);
+ isl_int_mul(cst1->d, cst1->d, cst2->d);
+
+ isl_poly_cst_reduce(cst1);
+
+ isl_poly_free(poly2);
+ return poly1;
+error:
+ isl_poly_free(poly1);
+ isl_poly_free(poly2);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_mul_rec(__isl_take isl_poly *poly1,
+ __isl_take isl_poly *poly2)
+{
+ isl_poly_rec *rec1;
+ isl_poly_rec *rec2;
+ isl_poly_rec *res = NULL;
+ int i, j;
+ int size;
+
+ rec1 = isl_poly_as_rec(poly1);
+ rec2 = isl_poly_as_rec(poly2);
+ if (!rec1 || !rec2)
+ goto error;
+ size = rec1->n + rec2->n - 1;
+ res = isl_poly_alloc_rec(poly1->ctx, poly1->var, size);
+ if (!res)
+ goto error;
+
+ for (i = 0; i < rec1->n; ++i) {
+ res->p[i] = isl_poly_mul(isl_poly_copy(rec2->p[0]),
+ isl_poly_copy(rec1->p[i]));
+ if (!res->p[i])
+ goto error;
+ res->n++;
+ }
+ for (; i < size; ++i) {
+ res->p[i] = isl_poly_zero(poly1->ctx);
+ if (!res->p[i])
+ goto error;
+ res->n++;
+ }
+ for (i = 0; i < rec1->n; ++i) {
+ for (j = 1; j < rec2->n; ++j) {
+ isl_poly *poly;
+ poly = isl_poly_mul(isl_poly_copy(rec2->p[j]),
+ isl_poly_copy(rec1->p[i]));
+ res->p[i + j] = isl_poly_sum(res->p[i + j], poly);
+ if (!res->p[i + j])
+ goto error;
+ }
+ }
+
+ isl_poly_free(poly1);
+ isl_poly_free(poly2);
+
+ return &res->poly;
+error:
+ isl_poly_free(poly1);
+ isl_poly_free(poly2);
+ isl_poly_free(&res->poly);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_mul(__isl_take isl_poly *poly1,
+ __isl_take isl_poly *poly2)
+{
+ isl_bool is_zero, is_nan, is_one, is_cst;
+
+ if (!poly1 || !poly2)
+ goto error;
+
+ is_nan = isl_poly_is_nan(poly1);
+ if (is_nan < 0)
+ goto error;
+ if (is_nan) {
+ isl_poly_free(poly2);
+ return poly1;
+ }
+
+ is_nan = isl_poly_is_nan(poly2);
+ if (is_nan < 0)
+ goto error;
+ if (is_nan) {
+ isl_poly_free(poly1);
+ return poly2;
+ }
+
+ is_zero = isl_poly_is_zero(poly1);
+ if (is_zero < 0)
+ goto error;
+ if (is_zero) {
+ isl_poly_free(poly2);
+ return poly1;
+ }
+
+ is_zero = isl_poly_is_zero(poly2);
+ if (is_zero < 0)
+ goto error;
+ if (is_zero) {
+ isl_poly_free(poly1);
+ return poly2;
+ }
+
+ is_one = isl_poly_is_one(poly1);
+ if (is_one < 0)
+ goto error;
+ if (is_one) {
+ isl_poly_free(poly1);
+ return poly2;
+ }
+
+ is_one = isl_poly_is_one(poly2);
+ if (is_one < 0)
+ goto error;
+ if (is_one) {
+ isl_poly_free(poly2);
+ return poly1;
+ }
+
+ if (poly1->var < poly2->var)
+ return isl_poly_mul(poly2, poly1);
+
+ if (poly2->var < poly1->var) {
+ int i;
+ isl_poly_rec *rec;
+ isl_bool is_infty;
+
+ is_infty = isl_poly_is_infty(poly2);
+ if (is_infty >= 0 && !is_infty)
+ is_infty = isl_poly_is_neginfty(poly2);
+ if (is_infty < 0)
+ goto error;
+ if (is_infty) {
+ isl_ctx *ctx = poly1->ctx;
+ isl_poly_free(poly1);
+ isl_poly_free(poly2);
+ return isl_poly_nan(ctx);
+ }
+ poly1 = isl_poly_cow(poly1);
+ rec = isl_poly_as_rec(poly1);
+ if (!rec)
+ goto error;
+
+ for (i = 0; i < rec->n; ++i) {
+ rec->p[i] = isl_poly_mul(rec->p[i],
+ isl_poly_copy(poly2));
+ if (!rec->p[i])
+ goto error;
+ }
+ isl_poly_free(poly2);
+ return poly1;
+ }
+
+ is_cst = isl_poly_is_cst(poly1);
+ if (is_cst < 0)
+ goto error;
+ if (is_cst)
+ return isl_poly_mul_cst(poly1, poly2);
+
+ return isl_poly_mul_rec(poly1, poly2);
+error:
+ isl_poly_free(poly1);
+ isl_poly_free(poly2);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_pow(__isl_take isl_poly *poly, unsigned power)
+{
+ isl_poly *res;
+
+ if (!poly)
+ return NULL;
+ if (power == 1)
+ return poly;
+
+ if (power % 2)
+ res = isl_poly_copy(poly);
+ else
+ res = isl_poly_one(poly->ctx);
+
+ while (power >>= 1) {
+ poly = isl_poly_mul(poly, isl_poly_copy(poly));
+ if (power % 2)
+ res = isl_poly_mul(res, isl_poly_copy(poly));
+ }
+
+ isl_poly_free(poly);
+ return res;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_alloc(__isl_take isl_space *space,
+ unsigned n_div, __isl_take isl_poly *poly)
+{
+ struct isl_qpolynomial *qp = NULL;
+ isl_size total;
+
+ total = isl_space_dim(space, isl_dim_all);
+ if (total < 0 || !poly)
+ goto error;
+
+ if (!isl_space_is_set(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "domain of polynomial should be a set", goto error);
+
+ qp = isl_calloc_type(space->ctx, struct isl_qpolynomial);
+ if (!qp)
+ goto error;
+
+ qp->ref = 1;
+ qp->div = isl_mat_alloc(space->ctx, n_div, 1 + 1 + total + n_div);
+ if (!qp->div)
+ goto error;
+
+ qp->dim = space;
+ qp->poly = poly;
+
+ return qp;
+error:
+ isl_space_free(space);
+ isl_poly_free(poly);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_copy(__isl_keep isl_qpolynomial *qp)
+{
+ if (!qp)
+ return NULL;
+
+ qp->ref++;
+ return qp;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_dup(__isl_keep isl_qpolynomial *qp)
+{
+ struct isl_qpolynomial *dup;
+
+ if (!qp)
+ return NULL;
+
+ dup = isl_qpolynomial_alloc(isl_space_copy(qp->dim), qp->div->n_row,
+ isl_poly_copy(qp->poly));
+ if (!dup)
+ return NULL;
+ isl_mat_free(dup->div);
+ dup->div = isl_mat_copy(qp->div);
+ if (!dup->div)
+ goto error;
+
+ return dup;
+error:
+ isl_qpolynomial_free(dup);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_cow(__isl_take isl_qpolynomial *qp)
+{
+ if (!qp)
+ return NULL;
+
+ if (qp->ref == 1)
+ return qp;
+ qp->ref--;
+ return isl_qpolynomial_dup(qp);
+}
+
+__isl_null isl_qpolynomial *isl_qpolynomial_free(
+ __isl_take isl_qpolynomial *qp)
+{
+ if (!qp)
+ return NULL;
+
+ if (--qp->ref > 0)
+ return NULL;
+
+ isl_space_free(qp->dim);
+ isl_mat_free(qp->div);
+ isl_poly_free(qp->poly);
+
+ free(qp);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_var_pow(isl_ctx *ctx, int pos, int power)
+{
+ int i;
+ isl_poly_rec *rec;
+ isl_poly_cst *cst;
+
+ rec = isl_poly_alloc_rec(ctx, pos, 1 + power);
+ if (!rec)
+ return NULL;
+ for (i = 0; i < 1 + power; ++i) {
+ rec->p[i] = isl_poly_zero(ctx);
+ if (!rec->p[i])
+ goto error;
+ rec->n++;
+ }
+ cst = isl_poly_as_cst(rec->p[power]);
+ isl_int_set_si(cst->n, 1);
+
+ return &rec->poly;
+error:
+ isl_poly_free(&rec->poly);
+ return NULL;
+}
+
+/* r array maps original positions to new positions.
+ */
+static __isl_give isl_poly *reorder(__isl_take isl_poly *poly, int *r)
+{
+ int i;
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+ isl_poly *base;
+ isl_poly *res;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return isl_poly_free(poly);
+ if (is_cst)
+ return poly;
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ goto error;
+
+ isl_assert(poly->ctx, rec->n >= 1, goto error);
+
+ base = isl_poly_var_pow(poly->ctx, r[poly->var], 1);
+ res = reorder(isl_poly_copy(rec->p[rec->n - 1]), r);
+
+ for (i = rec->n - 2; i >= 0; --i) {
+ res = isl_poly_mul(res, isl_poly_copy(base));
+ res = isl_poly_sum(res, reorder(isl_poly_copy(rec->p[i]), r));
+ }
+
+ isl_poly_free(base);
+ isl_poly_free(poly);
+
+ return res;
+error:
+ isl_poly_free(poly);
+ return NULL;
+}
+
+static isl_bool compatible_divs(__isl_keep isl_mat *div1,
+ __isl_keep isl_mat *div2)
+{
+ int n_row, n_col;
+ isl_bool equal;
+
+ isl_assert(div1->ctx, div1->n_row >= div2->n_row &&
+ div1->n_col >= div2->n_col,
+ return isl_bool_error);
+
+ if (div1->n_row == div2->n_row)
+ return isl_mat_is_equal(div1, div2);
+
+ n_row = div1->n_row;
+ n_col = div1->n_col;
+ div1->n_row = div2->n_row;
+ div1->n_col = div2->n_col;
+
+ equal = isl_mat_is_equal(div1, div2);
+
+ div1->n_row = n_row;
+ div1->n_col = n_col;
+
+ return equal;
+}
+
+static int cmp_row(__isl_keep isl_mat *div, int i, int j)
+{
+ int li, lj;
+
+ li = isl_seq_last_non_zero(div->row[i], div->n_col);
+ lj = isl_seq_last_non_zero(div->row[j], div->n_col);
+
+ if (li != lj)
+ return li - lj;
+
+ return isl_seq_cmp(div->row[i], div->row[j], div->n_col);
+}
+
+struct isl_div_sort_info {
+ isl_mat *div;
+ int row;
+};
+
+static int div_sort_cmp(const void *p1, const void *p2)
+{
+ const struct isl_div_sort_info *i1, *i2;
+ i1 = (const struct isl_div_sort_info *) p1;
+ i2 = (const struct isl_div_sort_info *) p2;
+
+ return cmp_row(i1->div, i1->row, i2->row);
+}
+
+/* Sort divs and remove duplicates.
+ */
+static __isl_give isl_qpolynomial *sort_divs(__isl_take isl_qpolynomial *qp)
+{
+ int i;
+ int skip;
+ int len;
+ struct isl_div_sort_info *array = NULL;
+ int *pos = NULL, *at = NULL;
+ int *reordering = NULL;
+ isl_size div_pos;
+
+ if (!qp)
+ return NULL;
+ if (qp->div->n_row <= 1)
+ return qp;
+
+ div_pos = isl_qpolynomial_domain_var_offset(qp, isl_dim_div);
+ if (div_pos < 0)
+ return isl_qpolynomial_free(qp);
+
+ array = isl_alloc_array(qp->div->ctx, struct isl_div_sort_info,
+ qp->div->n_row);
+ pos = isl_alloc_array(qp->div->ctx, int, qp->div->n_row);
+ at = isl_alloc_array(qp->div->ctx, int, qp->div->n_row);
+ len = qp->div->n_col - 2;
+ reordering = isl_alloc_array(qp->div->ctx, int, len);
+ if (!array || !pos || !at || !reordering)
+ goto error;
+
+ for (i = 0; i < qp->div->n_row; ++i) {
+ array[i].div = qp->div;
+ array[i].row = i;
+ pos[i] = i;
+ at[i] = i;
+ }
+
+ qsort(array, qp->div->n_row, sizeof(struct isl_div_sort_info),
+ div_sort_cmp);
+
+ for (i = 0; i < div_pos; ++i)
+ reordering[i] = i;
+
+ for (i = 0; i < qp->div->n_row; ++i) {
+ if (pos[array[i].row] == i)
+ continue;
+ qp->div = isl_mat_swap_rows(qp->div, i, pos[array[i].row]);
+ pos[at[i]] = pos[array[i].row];
+ at[pos[array[i].row]] = at[i];
+ at[i] = array[i].row;
+ pos[array[i].row] = i;
+ }
+
+ skip = 0;
+ for (i = 0; i < len - div_pos; ++i) {
+ if (i > 0 &&
+ isl_seq_eq(qp->div->row[i - skip - 1],
+ qp->div->row[i - skip], qp->div->n_col)) {
+ qp->div = isl_mat_drop_rows(qp->div, i - skip, 1);
+ isl_mat_col_add(qp->div, 2 + div_pos + i - skip - 1,
+ 2 + div_pos + i - skip);
+ qp->div = isl_mat_drop_cols(qp->div,
+ 2 + div_pos + i - skip, 1);
+ skip++;
+ }
+ reordering[div_pos + array[i].row] = div_pos + i - skip;
+ }
+
+ qp->poly = reorder(qp->poly, reordering);
+
+ if (!qp->poly || !qp->div)
+ goto error;
+
+ free(at);
+ free(pos);
+ free(array);
+ free(reordering);
+
+ return qp;
+error:
+ free(at);
+ free(pos);
+ free(array);
+ free(reordering);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+static __isl_give isl_poly *expand(__isl_take isl_poly *poly, int *exp,
+ int first)
+{
+ int i;
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return isl_poly_free(poly);
+ if (is_cst)
+ return poly;
+
+ if (poly->var < first)
+ return poly;
+
+ if (exp[poly->var - first] == poly->var - first)
+ return poly;
+
+ poly = isl_poly_cow(poly);
+ if (!poly)
+ goto error;
+
+ poly->var = exp[poly->var - first] + first;
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ goto error;
+
+ for (i = 0; i < rec->n; ++i) {
+ rec->p[i] = expand(rec->p[i], exp, first);
+ if (!rec->p[i])
+ goto error;
+ }
+
+ return poly;
+error:
+ isl_poly_free(poly);
+ return NULL;
+}
+
+static __isl_give isl_qpolynomial *with_merged_divs(
+ __isl_give isl_qpolynomial *(*fn)(__isl_take isl_qpolynomial *qp1,
+ __isl_take isl_qpolynomial *qp2),
+ __isl_take isl_qpolynomial *qp1, __isl_take isl_qpolynomial *qp2)
+{
+ int *exp1 = NULL;
+ int *exp2 = NULL;
+ isl_mat *div = NULL;
+ int n_div1, n_div2;
+
+ qp1 = isl_qpolynomial_cow(qp1);
+ qp2 = isl_qpolynomial_cow(qp2);
+
+ if (!qp1 || !qp2)
+ goto error;
+
+ isl_assert(qp1->div->ctx, qp1->div->n_row >= qp2->div->n_row &&
+ qp1->div->n_col >= qp2->div->n_col, goto error);
+
+ n_div1 = qp1->div->n_row;
+ n_div2 = qp2->div->n_row;
+ exp1 = isl_alloc_array(qp1->div->ctx, int, n_div1);
+ exp2 = isl_alloc_array(qp2->div->ctx, int, n_div2);
+ if ((n_div1 && !exp1) || (n_div2 && !exp2))
+ goto error;
+
+ div = isl_merge_divs(qp1->div, qp2->div, exp1, exp2);
+ if (!div)
+ goto error;
+
+ isl_mat_free(qp1->div);
+ qp1->div = isl_mat_copy(div);
+ isl_mat_free(qp2->div);
+ qp2->div = isl_mat_copy(div);
+
+ qp1->poly = expand(qp1->poly, exp1, div->n_col - div->n_row - 2);
+ qp2->poly = expand(qp2->poly, exp2, div->n_col - div->n_row - 2);
+
+ if (!qp1->poly || !qp2->poly)
+ goto error;
+
+ isl_mat_free(div);
+ free(exp1);
+ free(exp2);
+
+ return fn(qp1, qp2);
+error:
+ isl_mat_free(div);
+ free(exp1);
+ free(exp2);
+ isl_qpolynomial_free(qp1);
+ isl_qpolynomial_free(qp2);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_add(__isl_take isl_qpolynomial *qp1,
+ __isl_take isl_qpolynomial *qp2)
+{
+ isl_bool compatible;
+
+ qp1 = isl_qpolynomial_cow(qp1);
+
+ if (isl_qpolynomial_check_equal_space(qp1, qp2) < 0)
+ goto error;
+
+ if (qp1->div->n_row < qp2->div->n_row)
+ return isl_qpolynomial_add(qp2, qp1);
+
+ compatible = compatible_divs(qp1->div, qp2->div);
+ if (compatible < 0)
+ goto error;
+ if (!compatible)
+ return with_merged_divs(isl_qpolynomial_add, qp1, qp2);
+
+ qp1->poly = isl_poly_sum(qp1->poly, isl_poly_copy(qp2->poly));
+ if (!qp1->poly)
+ goto error;
+
+ isl_qpolynomial_free(qp2);
+
+ return qp1;
+error:
+ isl_qpolynomial_free(qp1);
+ isl_qpolynomial_free(qp2);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_add_on_domain(
+ __isl_keep isl_set *dom,
+ __isl_take isl_qpolynomial *qp1,
+ __isl_take isl_qpolynomial *qp2)
+{
+ qp1 = isl_qpolynomial_add(qp1, qp2);
+ qp1 = isl_qpolynomial_gist(qp1, isl_set_copy(dom));
+ return qp1;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_sub(__isl_take isl_qpolynomial *qp1,
+ __isl_take isl_qpolynomial *qp2)
+{
+ return isl_qpolynomial_add(qp1, isl_qpolynomial_neg(qp2));
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_add_isl_int(
+ __isl_take isl_qpolynomial *qp, isl_int v)
+{
+ if (isl_int_is_zero(v))
+ return qp;
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ return NULL;
+
+ qp->poly = isl_poly_add_isl_int(qp->poly, v);
+ if (!qp->poly)
+ goto error;
+
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ return NULL;
+
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_neg(__isl_take isl_qpolynomial *qp)
+{
+ if (!qp)
+ return NULL;
+
+ return isl_qpolynomial_mul_isl_int(qp, qp->dim->ctx->negone);
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_mul_isl_int(
+ __isl_take isl_qpolynomial *qp, isl_int v)
+{
+ if (isl_int_is_one(v))
+ return qp;
+
+ if (qp && isl_int_is_zero(v)) {
+ isl_qpolynomial *zero;
+ zero = isl_qpolynomial_zero_on_domain(isl_space_copy(qp->dim));
+ isl_qpolynomial_free(qp);
+ return zero;
+ }
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ return NULL;
+
+ qp->poly = isl_poly_mul_isl_int(qp->poly, v);
+ if (!qp->poly)
+ goto error;
+
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_scale(
+ __isl_take isl_qpolynomial *qp, isl_int v)
+{
+ return isl_qpolynomial_mul_isl_int(qp, v);
+}
+
+/* Multiply "qp" by "v".
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_scale_val(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_val *v)
+{
+ if (!qp || !v)
+ goto error;
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_qpolynomial_get_ctx(qp), isl_error_invalid,
+ "expecting rational factor", goto error);
+
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return qp;
+ }
+
+ if (isl_val_is_zero(v)) {
+ isl_space *space;
+
+ space = isl_qpolynomial_get_domain_space(qp);
+ isl_qpolynomial_free(qp);
+ isl_val_free(v);
+ return isl_qpolynomial_zero_on_domain(space);
+ }
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ goto error;
+
+ qp->poly = isl_poly_scale_val(qp->poly, v);
+ if (!qp->poly)
+ qp = isl_qpolynomial_free(qp);
+
+ isl_val_free(v);
+ return qp;
+error:
+ isl_val_free(v);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+/* Divide "qp" by "v".
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_scale_down_val(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_val *v)
+{
+ if (!qp || !v)
+ goto error;
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_qpolynomial_get_ctx(qp), isl_error_invalid,
+ "expecting rational factor", goto error);
+ if (isl_val_is_zero(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "cannot scale down by zero", goto error);
+
+ return isl_qpolynomial_scale_val(qp, isl_val_inv(v));
+error:
+ isl_val_free(v);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_mul(__isl_take isl_qpolynomial *qp1,
+ __isl_take isl_qpolynomial *qp2)
+{
+ isl_bool compatible;
+
+ qp1 = isl_qpolynomial_cow(qp1);
+
+ if (isl_qpolynomial_check_equal_space(qp1, qp2) < 0)
+ goto error;
+
+ if (qp1->div->n_row < qp2->div->n_row)
+ return isl_qpolynomial_mul(qp2, qp1);
+
+ compatible = compatible_divs(qp1->div, qp2->div);
+ if (compatible < 0)
+ goto error;
+ if (!compatible)
+ return with_merged_divs(isl_qpolynomial_mul, qp1, qp2);
+
+ qp1->poly = isl_poly_mul(qp1->poly, isl_poly_copy(qp2->poly));
+ if (!qp1->poly)
+ goto error;
+
+ isl_qpolynomial_free(qp2);
+
+ return qp1;
+error:
+ isl_qpolynomial_free(qp1);
+ isl_qpolynomial_free(qp2);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_pow(__isl_take isl_qpolynomial *qp,
+ unsigned power)
+{
+ qp = isl_qpolynomial_cow(qp);
+
+ if (!qp)
+ return NULL;
+
+ qp->poly = isl_poly_pow(qp->poly, power);
+ if (!qp->poly)
+ goto error;
+
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_pow(
+ __isl_take isl_pw_qpolynomial *pwqp, unsigned power)
+{
+ int i;
+
+ if (power == 1)
+ return pwqp;
+
+ pwqp = isl_pw_qpolynomial_cow(pwqp);
+ if (!pwqp)
+ return NULL;
+
+ for (i = 0; i < pwqp->n; ++i) {
+ pwqp->p[i].qp = isl_qpolynomial_pow(pwqp->p[i].qp, power);
+ if (!pwqp->p[i].qp)
+ return isl_pw_qpolynomial_free(pwqp);
+ }
+
+ return pwqp;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_zero_on_domain(
+ __isl_take isl_space *domain)
+{
+ if (!domain)
+ return NULL;
+ return isl_qpolynomial_alloc(domain, 0, isl_poly_zero(domain->ctx));
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_one_on_domain(
+ __isl_take isl_space *domain)
+{
+ if (!domain)
+ return NULL;
+ return isl_qpolynomial_alloc(domain, 0, isl_poly_one(domain->ctx));
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_infty_on_domain(
+ __isl_take isl_space *domain)
+{
+ if (!domain)
+ return NULL;
+ return isl_qpolynomial_alloc(domain, 0, isl_poly_infty(domain->ctx));
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_neginfty_on_domain(
+ __isl_take isl_space *domain)
+{
+ if (!domain)
+ return NULL;
+ return isl_qpolynomial_alloc(domain, 0, isl_poly_neginfty(domain->ctx));
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_nan_on_domain(
+ __isl_take isl_space *domain)
+{
+ if (!domain)
+ return NULL;
+ return isl_qpolynomial_alloc(domain, 0, isl_poly_nan(domain->ctx));
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_cst_on_domain(
+ __isl_take isl_space *domain,
+ isl_int v)
+{
+ struct isl_qpolynomial *qp;
+ isl_poly_cst *cst;
+
+ qp = isl_qpolynomial_zero_on_domain(domain);
+ if (!qp)
+ return NULL;
+
+ cst = isl_poly_as_cst(qp->poly);
+ isl_int_set(cst->n, v);
+
+ return qp;
+}
+
+isl_bool isl_qpolynomial_is_cst(__isl_keep isl_qpolynomial *qp,
+ isl_int *n, isl_int *d)
+{
+ isl_bool is_cst;
+ isl_poly_cst *cst;
+
+ if (!qp)
+ return isl_bool_error;
+
+ is_cst = isl_poly_is_cst(qp->poly);
+ if (is_cst < 0 || !is_cst)
+ return is_cst;
+
+ cst = isl_poly_as_cst(qp->poly);
+ if (!cst)
+ return isl_bool_error;
+
+ if (n)
+ isl_int_set(*n, cst->n);
+ if (d)
+ isl_int_set(*d, cst->d);
+
+ return isl_bool_true;
+}
+
+/* Return the constant term of "poly".
+ */
+static __isl_give isl_val *isl_poly_get_constant_val(__isl_keep isl_poly *poly)
+{
+ isl_bool is_cst;
+ isl_poly_cst *cst;
+
+ if (!poly)
+ return NULL;
+
+ while ((is_cst = isl_poly_is_cst(poly)) == isl_bool_false) {
+ isl_poly_rec *rec;
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ return NULL;
+ poly = rec->p[0];
+ }
+ if (is_cst < 0)
+ return NULL;
+
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ return NULL;
+ return isl_val_rat_from_isl_int(cst->poly.ctx, cst->n, cst->d);
+}
+
+/* Return the constant term of "qp".
+ */
+__isl_give isl_val *isl_qpolynomial_get_constant_val(
+ __isl_keep isl_qpolynomial *qp)
+{
+ if (!qp)
+ return NULL;
+
+ return isl_poly_get_constant_val(qp->poly);
+}
+
+isl_bool isl_poly_is_affine(__isl_keep isl_poly *poly)
+{
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+
+ if (!poly)
+ return isl_bool_error;
+
+ if (poly->var < 0)
+ return isl_bool_true;
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ return isl_bool_error;
+
+ if (rec->n > 2)
+ return isl_bool_false;
+
+ isl_assert(poly->ctx, rec->n > 1, return isl_bool_error);
+
+ is_cst = isl_poly_is_cst(rec->p[1]);
+ if (is_cst < 0 || !is_cst)
+ return is_cst;
+
+ return isl_poly_is_affine(rec->p[0]);
+}
+
+isl_bool isl_qpolynomial_is_affine(__isl_keep isl_qpolynomial *qp)
+{
+ if (!qp)
+ return isl_bool_error;
+
+ if (qp->div->n_row > 0)
+ return isl_bool_false;
+
+ return isl_poly_is_affine(qp->poly);
+}
+
+static void update_coeff(__isl_keep isl_vec *aff,
+ __isl_keep isl_poly_cst *cst, int pos)
+{
+ isl_int gcd;
+ isl_int f;
+
+ if (isl_int_is_zero(cst->n))
+ return;
+
+ isl_int_init(gcd);
+ isl_int_init(f);
+ isl_int_gcd(gcd, cst->d, aff->el[0]);
+ isl_int_divexact(f, cst->d, gcd);
+ isl_int_divexact(gcd, aff->el[0], gcd);
+ isl_seq_scale(aff->el, aff->el, f, aff->size);
+ isl_int_mul(aff->el[1 + pos], gcd, cst->n);
+ isl_int_clear(gcd);
+ isl_int_clear(f);
+}
+
+int isl_poly_update_affine(__isl_keep isl_poly *poly, __isl_keep isl_vec *aff)
+{
+ isl_poly_cst *cst;
+ isl_poly_rec *rec;
+
+ if (!poly || !aff)
+ return -1;
+
+ if (poly->var < 0) {
+ isl_poly_cst *cst;
+
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ return -1;
+ update_coeff(aff, cst, 0);
+ return 0;
+ }
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ return -1;
+ isl_assert(poly->ctx, rec->n == 2, return -1);
+
+ cst = isl_poly_as_cst(rec->p[1]);
+ if (!cst)
+ return -1;
+ update_coeff(aff, cst, 1 + poly->var);
+
+ return isl_poly_update_affine(rec->p[0], aff);
+}
+
+__isl_give isl_vec *isl_qpolynomial_extract_affine(
+ __isl_keep isl_qpolynomial *qp)
+{
+ isl_vec *aff;
+ isl_size d;
+
+ d = isl_qpolynomial_domain_dim(qp, isl_dim_all);
+ if (d < 0)
+ return NULL;
+
+ aff = isl_vec_alloc(qp->div->ctx, 2 + d);
+ if (!aff)
+ return NULL;
+
+ isl_seq_clr(aff->el + 1, 1 + d);
+ isl_int_set_si(aff->el[0], 1);
+
+ if (isl_poly_update_affine(qp->poly, aff) < 0)
+ goto error;
+
+ return aff;
+error:
+ isl_vec_free(aff);
+ return NULL;
+}
+
+/* Compare two quasi-polynomials.
+ *
+ * Return -1 if "qp1" is "smaller" than "qp2", 1 if "qp1" is "greater"
+ * than "qp2" and 0 if they are equal.
+ */
+int isl_qpolynomial_plain_cmp(__isl_keep isl_qpolynomial *qp1,
+ __isl_keep isl_qpolynomial *qp2)
+{
+ int cmp;
+
+ if (qp1 == qp2)
+ return 0;
+ if (!qp1)
+ return -1;
+ if (!qp2)
+ return 1;
+
+ cmp = isl_space_cmp(qp1->dim, qp2->dim);
+ if (cmp != 0)
+ return cmp;
+
+ cmp = isl_local_cmp(qp1->div, qp2->div);
+ if (cmp != 0)
+ return cmp;
+
+ return isl_poly_plain_cmp(qp1->poly, qp2->poly);
+}
+
+/* Is "qp1" obviously equal to "qp2"?
+ *
+ * NaN is not equal to anything, not even to another NaN.
+ */
+isl_bool isl_qpolynomial_plain_is_equal(__isl_keep isl_qpolynomial *qp1,
+ __isl_keep isl_qpolynomial *qp2)
+{
+ isl_bool equal;
+
+ if (!qp1 || !qp2)
+ return isl_bool_error;
+
+ if (isl_qpolynomial_is_nan(qp1) || isl_qpolynomial_is_nan(qp2))
+ return isl_bool_false;
+
+ equal = isl_space_is_equal(qp1->dim, qp2->dim);
+ if (equal < 0 || !equal)
+ return equal;
+
+ equal = isl_mat_is_equal(qp1->div, qp2->div);
+ if (equal < 0 || !equal)
+ return equal;
+
+ return isl_poly_is_equal(qp1->poly, qp2->poly);
+}
+
+static isl_stat poly_update_den(__isl_keep isl_poly *poly, isl_int *d)
+{
+ int i;
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return isl_stat_error;
+ if (is_cst) {
+ isl_poly_cst *cst;
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ return isl_stat_error;
+ isl_int_lcm(*d, *d, cst->d);
+ return isl_stat_ok;
+ }
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ return isl_stat_error;
+
+ for (i = 0; i < rec->n; ++i)
+ poly_update_den(rec->p[i], d);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_val *isl_qpolynomial_get_den(__isl_keep isl_qpolynomial *qp)
+{
+ isl_val *d;
+
+ if (!qp)
+ return NULL;
+ d = isl_val_one(isl_qpolynomial_get_ctx(qp));
+ if (!d)
+ return NULL;
+ if (poly_update_den(qp->poly, &d->n) < 0)
+ return isl_val_free(d);
+ return d;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_var_pow_on_domain(
+ __isl_take isl_space *domain, int pos, int power)
+{
+ struct isl_ctx *ctx;
+
+ if (!domain)
+ return NULL;
+
+ ctx = domain->ctx;
+
+ return isl_qpolynomial_alloc(domain, 0,
+ isl_poly_var_pow(ctx, pos, power));
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_var_on_domain(
+ __isl_take isl_space *domain, enum isl_dim_type type, unsigned pos)
+{
+ if (isl_space_check_is_set(domain ) < 0)
+ goto error;
+ if (isl_space_check_range(domain, type, pos, 1) < 0)
+ goto error;
+
+ pos += isl_space_offset(domain, type);
+
+ return isl_qpolynomial_var_pow_on_domain(domain, pos, 1);
+error:
+ isl_space_free(domain);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_subs(__isl_take isl_poly *poly,
+ unsigned first, unsigned n, __isl_keep isl_poly **subs)
+{
+ int i;
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+ isl_poly *base, *res;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return isl_poly_free(poly);
+ if (is_cst)
+ return poly;
+
+ if (poly->var < first)
+ return poly;
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ goto error;
+
+ isl_assert(poly->ctx, rec->n >= 1, goto error);
+
+ if (poly->var >= first + n)
+ base = isl_poly_var_pow(poly->ctx, poly->var, 1);
+ else
+ base = isl_poly_copy(subs[poly->var - first]);
+
+ res = isl_poly_subs(isl_poly_copy(rec->p[rec->n - 1]), first, n, subs);
+ for (i = rec->n - 2; i >= 0; --i) {
+ isl_poly *t;
+ t = isl_poly_subs(isl_poly_copy(rec->p[i]), first, n, subs);
+ res = isl_poly_mul(res, isl_poly_copy(base));
+ res = isl_poly_sum(res, t);
+ }
+
+ isl_poly_free(base);
+ isl_poly_free(poly);
+
+ return res;
+error:
+ isl_poly_free(poly);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_from_affine(isl_ctx *ctx, isl_int *f,
+ isl_int denom, unsigned len)
+{
+ int i;
+ isl_poly *poly;
+
+ isl_assert(ctx, len >= 1, return NULL);
+
+ poly = isl_poly_rat_cst(ctx, f[0], denom);
+ for (i = 0; i < len - 1; ++i) {
+ isl_poly *t;
+ isl_poly *c;
+
+ if (isl_int_is_zero(f[1 + i]))
+ continue;
+
+ c = isl_poly_rat_cst(ctx, f[1 + i], denom);
+ t = isl_poly_var_pow(ctx, i, 1);
+ t = isl_poly_mul(c, t);
+ poly = isl_poly_sum(poly, t);
+ }
+
+ return poly;
+}
+
+/* Remove common factor of non-constant terms and denominator.
+ */
+static void normalize_div(__isl_keep isl_qpolynomial *qp, int div)
+{
+ isl_ctx *ctx = qp->div->ctx;
+ unsigned total = qp->div->n_col - 2;
+
+ isl_seq_gcd(qp->div->row[div] + 2, total, &ctx->normalize_gcd);
+ isl_int_gcd(ctx->normalize_gcd,
+ ctx->normalize_gcd, qp->div->row[div][0]);
+ if (isl_int_is_one(ctx->normalize_gcd))
+ return;
+
+ isl_seq_scale_down(qp->div->row[div] + 2, qp->div->row[div] + 2,
+ ctx->normalize_gcd, total);
+ isl_int_divexact(qp->div->row[div][0], qp->div->row[div][0],
+ ctx->normalize_gcd);
+ isl_int_fdiv_q(qp->div->row[div][1], qp->div->row[div][1],
+ ctx->normalize_gcd);
+}
+
+/* Replace the integer division identified by "div" by the polynomial "s".
+ * The integer division is assumed not to appear in the definition
+ * of any other integer divisions.
+ */
+static __isl_give isl_qpolynomial *substitute_div(
+ __isl_take isl_qpolynomial *qp, int div, __isl_take isl_poly *s)
+{
+ int i;
+ isl_size div_pos;
+ int *reordering;
+ isl_ctx *ctx;
+
+ if (!qp || !s)
+ goto error;
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ goto error;
+
+ div_pos = isl_qpolynomial_domain_var_offset(qp, isl_dim_div);
+ if (div_pos < 0)
+ goto error;
+ qp->poly = isl_poly_subs(qp->poly, div_pos + div, 1, &s);
+ if (!qp->poly)
+ goto error;
+
+ ctx = isl_qpolynomial_get_ctx(qp);
+ reordering = isl_alloc_array(ctx, int, div_pos + qp->div->n_row);
+ if (!reordering)
+ goto error;
+ for (i = 0; i < div_pos + div; ++i)
+ reordering[i] = i;
+ for (i = div_pos + div + 1; i < div_pos + qp->div->n_row; ++i)
+ reordering[i] = i - 1;
+ qp->div = isl_mat_drop_rows(qp->div, div, 1);
+ qp->div = isl_mat_drop_cols(qp->div, 2 + div_pos + div, 1);
+ qp->poly = reorder(qp->poly, reordering);
+ free(reordering);
+
+ if (!qp->poly || !qp->div)
+ goto error;
+
+ isl_poly_free(s);
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ isl_poly_free(s);
+ return NULL;
+}
+
+/* Replace all integer divisions [e/d] that turn out to not actually be integer
+ * divisions because d is equal to 1 by their definition, i.e., e.
+ */
+static __isl_give isl_qpolynomial *substitute_non_divs(
+ __isl_take isl_qpolynomial *qp)
+{
+ int i, j;
+ isl_size div_pos;
+ isl_poly *s;
+
+ div_pos = isl_qpolynomial_domain_var_offset(qp, isl_dim_div);
+ if (div_pos < 0)
+ return isl_qpolynomial_free(qp);
+
+ for (i = 0; qp && i < qp->div->n_row; ++i) {
+ if (!isl_int_is_one(qp->div->row[i][0]))
+ continue;
+ for (j = i + 1; j < qp->div->n_row; ++j) {
+ if (isl_int_is_zero(qp->div->row[j][2 + div_pos + i]))
+ continue;
+ isl_seq_combine(qp->div->row[j] + 1,
+ qp->div->ctx->one, qp->div->row[j] + 1,
+ qp->div->row[j][2 + div_pos + i],
+ qp->div->row[i] + 1, 1 + div_pos + i);
+ isl_int_set_si(qp->div->row[j][2 + div_pos + i], 0);
+ normalize_div(qp, j);
+ }
+ s = isl_poly_from_affine(qp->dim->ctx, qp->div->row[i] + 1,
+ qp->div->row[i][0], qp->div->n_col - 1);
+ qp = substitute_div(qp, i, s);
+ --i;
+ }
+
+ return qp;
+}
+
+/* Reduce the coefficients of div "div" to lie in the interval [0, d-1],
+ * with d the denominator. When replacing the coefficient e of x by
+ * d * frac(e/d) = e - d * floor(e/d), we are subtracting d * floor(e/d) * x
+ * inside the division, so we need to add floor(e/d) * x outside.
+ * That is, we replace q by q' + floor(e/d) * x and we therefore need
+ * to adjust the coefficient of x in each later div that depends on the
+ * current div "div" and also in the affine expressions in the rows of "mat"
+ * (if they too depend on "div").
+ */
+static void reduce_div(__isl_keep isl_qpolynomial *qp, int div,
+ __isl_keep isl_mat **mat)
+{
+ int i, j;
+ isl_int v;
+ unsigned total = qp->div->n_col - qp->div->n_row - 2;
+
+ isl_int_init(v);
+ for (i = 0; i < 1 + total + div; ++i) {
+ if (isl_int_is_nonneg(qp->div->row[div][1 + i]) &&
+ isl_int_lt(qp->div->row[div][1 + i], qp->div->row[div][0]))
+ continue;
+ isl_int_fdiv_q(v, qp->div->row[div][1 + i], qp->div->row[div][0]);
+ isl_int_fdiv_r(qp->div->row[div][1 + i],
+ qp->div->row[div][1 + i], qp->div->row[div][0]);
+ *mat = isl_mat_col_addmul(*mat, i, v, 1 + total + div);
+ for (j = div + 1; j < qp->div->n_row; ++j) {
+ if (isl_int_is_zero(qp->div->row[j][2 + total + div]))
+ continue;
+ isl_int_addmul(qp->div->row[j][1 + i],
+ v, qp->div->row[j][2 + total + div]);
+ }
+ }
+ isl_int_clear(v);
+}
+
+/* Check if the last non-zero coefficient is bigger that half of the
+ * denominator. If so, we will invert the div to further reduce the number
+ * of distinct divs that may appear.
+ * If the last non-zero coefficient is exactly half the denominator,
+ * then we continue looking for earlier coefficients that are bigger
+ * than half the denominator.
+ */
+static int needs_invert(__isl_keep isl_mat *div, int row)
+{
+ int i;
+ int cmp;
+
+ for (i = div->n_col - 1; i >= 1; --i) {
+ if (isl_int_is_zero(div->row[row][i]))
+ continue;
+ isl_int_mul_ui(div->row[row][i], div->row[row][i], 2);
+ cmp = isl_int_cmp(div->row[row][i], div->row[row][0]);
+ isl_int_divexact_ui(div->row[row][i], div->row[row][i], 2);
+ if (cmp)
+ return cmp > 0;
+ if (i == 1)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Replace div "div" q = [e/d] by -[(-e+(d-1))/d].
+ * We only invert the coefficients of e (and the coefficient of q in
+ * later divs and in the rows of "mat"). After calling this function, the
+ * coefficients of e should be reduced again.
+ */
+static void invert_div(__isl_keep isl_qpolynomial *qp, int div,
+ __isl_keep isl_mat **mat)
+{
+ unsigned total = qp->div->n_col - qp->div->n_row - 2;
+
+ isl_seq_neg(qp->div->row[div] + 1,
+ qp->div->row[div] + 1, qp->div->n_col - 1);
+ isl_int_sub_ui(qp->div->row[div][1], qp->div->row[div][1], 1);
+ isl_int_add(qp->div->row[div][1],
+ qp->div->row[div][1], qp->div->row[div][0]);
+ *mat = isl_mat_col_neg(*mat, 1 + total + div);
+ isl_mat_col_mul(qp->div, 2 + total + div,
+ qp->div->ctx->negone, 2 + total + div);
+}
+
+/* Reduce all divs of "qp" to have coefficients
+ * in the interval [0, d-1], with d the denominator and such that the
+ * last non-zero coefficient that is not equal to d/2 is smaller than d/2.
+ * The modifications to the integer divisions need to be reflected
+ * in the factors of the polynomial that refer to the original
+ * integer divisions. To this end, the modifications are collected
+ * as a set of affine expressions and then plugged into the polynomial.
+ *
+ * After the reduction, some divs may have become redundant or identical,
+ * so we call substitute_non_divs and sort_divs. If these functions
+ * eliminate divs or merge two or more divs into one, the coefficients
+ * of the enclosing divs may have to be reduced again, so we call
+ * ourselves recursively if the number of divs decreases.
+ */
+static __isl_give isl_qpolynomial *reduce_divs(__isl_take isl_qpolynomial *qp)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_mat *mat;
+ isl_poly **s;
+ unsigned o_div;
+ isl_size n_div, total, new_n_div;
+
+ total = isl_qpolynomial_domain_dim(qp, isl_dim_all);
+ n_div = isl_qpolynomial_domain_dim(qp, isl_dim_div);
+ o_div = isl_qpolynomial_domain_offset(qp, isl_dim_div);
+ if (total < 0 || n_div < 0)
+ return isl_qpolynomial_free(qp);
+ ctx = isl_qpolynomial_get_ctx(qp);
+ mat = isl_mat_zero(ctx, n_div, 1 + total);
+
+ for (i = 0; i < n_div; ++i)
+ mat = isl_mat_set_element_si(mat, i, o_div + i, 1);
+
+ for (i = 0; i < qp->div->n_row; ++i) {
+ normalize_div(qp, i);
+ reduce_div(qp, i, &mat);
+ if (needs_invert(qp->div, i)) {
+ invert_div(qp, i, &mat);
+ reduce_div(qp, i, &mat);
+ }
+ }
+ if (!mat)
+ goto error;
+
+ s = isl_alloc_array(ctx, struct isl_poly *, n_div);
+ if (n_div && !s)
+ goto error;
+ for (i = 0; i < n_div; ++i)
+ s[i] = isl_poly_from_affine(ctx, mat->row[i], ctx->one,
+ 1 + total);
+ qp->poly = isl_poly_subs(qp->poly, o_div - 1, n_div, s);
+ for (i = 0; i < n_div; ++i)
+ isl_poly_free(s[i]);
+ free(s);
+ if (!qp->poly)
+ goto error;
+
+ isl_mat_free(mat);
+
+ qp = substitute_non_divs(qp);
+ qp = sort_divs(qp);
+ new_n_div = isl_qpolynomial_domain_dim(qp, isl_dim_div);
+ if (new_n_div < 0)
+ return isl_qpolynomial_free(qp);
+ if (new_n_div < n_div)
+ return reduce_divs(qp);
+
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ isl_mat_free(mat);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_rat_cst_on_domain(
+ __isl_take isl_space *domain, const isl_int n, const isl_int d)
+{
+ struct isl_qpolynomial *qp;
+ isl_poly_cst *cst;
+
+ qp = isl_qpolynomial_zero_on_domain(domain);
+ if (!qp)
+ return NULL;
+
+ cst = isl_poly_as_cst(qp->poly);
+ isl_int_set(cst->n, n);
+ isl_int_set(cst->d, d);
+
+ return qp;
+}
+
+/* Return an isl_qpolynomial that is equal to "val" on domain space "domain".
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_val_on_domain(
+ __isl_take isl_space *domain, __isl_take isl_val *val)
+{
+ isl_qpolynomial *qp;
+ isl_poly_cst *cst;
+
+ qp = isl_qpolynomial_zero_on_domain(domain);
+ if (!qp || !val)
+ goto error;
+
+ cst = isl_poly_as_cst(qp->poly);
+ isl_int_set(cst->n, val->n);
+ isl_int_set(cst->d, val->d);
+
+ isl_val_free(val);
+ return qp;
+error:
+ isl_val_free(val);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+static isl_stat poly_set_active(__isl_keep isl_poly *poly, int *active, int d)
+{
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+ int i;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return isl_stat_error;
+ if (is_cst)
+ return isl_stat_ok;
+
+ if (poly->var < d)
+ active[poly->var] = 1;
+
+ rec = isl_poly_as_rec(poly);
+ for (i = 0; i < rec->n; ++i)
+ if (poly_set_active(rec->p[i], active, d) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+static isl_stat set_active(__isl_keep isl_qpolynomial *qp, int *active)
+{
+ int i, j;
+ isl_size d;
+ isl_space *space;
+
+ space = isl_qpolynomial_peek_domain_space(qp);
+ d = isl_space_dim(space, isl_dim_all);
+ if (d < 0 || !active)
+ return isl_stat_error;
+
+ for (i = 0; i < d; ++i)
+ for (j = 0; j < qp->div->n_row; ++j) {
+ if (isl_int_is_zero(qp->div->row[j][2 + i]))
+ continue;
+ active[i] = 1;
+ break;
+ }
+
+ return poly_set_active(qp->poly, active, d);
+}
+
+#undef TYPE
+#define TYPE isl_qpolynomial
+static
+#include "check_type_range_templ.c"
+
+isl_bool isl_qpolynomial_involves_dims(__isl_keep isl_qpolynomial *qp,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+ int *active = NULL;
+ isl_bool involves = isl_bool_false;
+ isl_size offset;
+ isl_size d;
+ isl_space *space;
+
+ if (!qp)
+ return isl_bool_error;
+ if (n == 0)
+ return isl_bool_false;
+
+ if (isl_qpolynomial_check_range(qp, type, first, n) < 0)
+ return isl_bool_error;
+ isl_assert(qp->dim->ctx, type == isl_dim_param ||
+ type == isl_dim_in, return isl_bool_error);
+
+ space = isl_qpolynomial_peek_domain_space(qp);
+ d = isl_space_dim(space, isl_dim_all);
+ if (d < 0)
+ return isl_bool_error;
+ active = isl_calloc_array(qp->dim->ctx, int, d);
+ if (set_active(qp, active) < 0)
+ goto error;
+
+ offset = isl_qpolynomial_domain_var_offset(qp, domain_type(type));
+ if (offset < 0)
+ goto error;
+ first += offset;
+ for (i = 0; i < n; ++i)
+ if (active[first + i]) {
+ involves = isl_bool_true;
+ break;
+ }
+
+ free(active);
+
+ return involves;
+error:
+ free(active);
+ return isl_bool_error;
+}
+
+/* Remove divs that do not appear in the quasi-polynomial, nor in any
+ * of the divs that do appear in the quasi-polynomial.
+ */
+static __isl_give isl_qpolynomial *remove_redundant_divs(
+ __isl_take isl_qpolynomial *qp)
+{
+ int i, j;
+ isl_size div_pos;
+ int len;
+ int skip;
+ int *active = NULL;
+ int *reordering = NULL;
+ int redundant = 0;
+ int n_div;
+ isl_ctx *ctx;
+
+ if (!qp)
+ return NULL;
+ if (qp->div->n_row == 0)
+ return qp;
+
+ div_pos = isl_qpolynomial_domain_var_offset(qp, isl_dim_div);
+ if (div_pos < 0)
+ return isl_qpolynomial_free(qp);
+ len = qp->div->n_col - 2;
+ ctx = isl_qpolynomial_get_ctx(qp);
+ active = isl_calloc_array(ctx, int, len);
+ if (!active)
+ goto error;
+
+ if (poly_set_active(qp->poly, active, len) < 0)
+ goto error;
+
+ for (i = qp->div->n_row - 1; i >= 0; --i) {
+ if (!active[div_pos + i]) {
+ redundant = 1;
+ continue;
+ }
+ for (j = 0; j < i; ++j) {
+ if (isl_int_is_zero(qp->div->row[i][2 + div_pos + j]))
+ continue;
+ active[div_pos + j] = 1;
+ break;
+ }
+ }
+
+ if (!redundant) {
+ free(active);
+ return qp;
+ }
+
+ reordering = isl_alloc_array(qp->div->ctx, int, len);
+ if (!reordering)
+ goto error;
+
+ for (i = 0; i < div_pos; ++i)
+ reordering[i] = i;
+
+ skip = 0;
+ n_div = qp->div->n_row;
+ for (i = 0; i < n_div; ++i) {
+ if (!active[div_pos + i]) {
+ qp->div = isl_mat_drop_rows(qp->div, i - skip, 1);
+ qp->div = isl_mat_drop_cols(qp->div,
+ 2 + div_pos + i - skip, 1);
+ skip++;
+ }
+ reordering[div_pos + i] = div_pos + i - skip;
+ }
+
+ qp->poly = reorder(qp->poly, reordering);
+
+ if (!qp->poly || !qp->div)
+ goto error;
+
+ free(active);
+ free(reordering);
+
+ return qp;
+error:
+ free(active);
+ free(reordering);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+__isl_give isl_poly *isl_poly_drop(__isl_take isl_poly *poly,
+ unsigned first, unsigned n)
+{
+ int i;
+ isl_poly_rec *rec;
+
+ if (!poly)
+ return NULL;
+ if (n == 0 || poly->var < 0 || poly->var < first)
+ return poly;
+ if (poly->var < first + n) {
+ poly = replace_by_constant_term(poly);
+ return isl_poly_drop(poly, first, n);
+ }
+ poly = isl_poly_cow(poly);
+ if (!poly)
+ return NULL;
+ poly->var -= n;
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ goto error;
+
+ for (i = 0; i < rec->n; ++i) {
+ rec->p[i] = isl_poly_drop(rec->p[i], first, n);
+ if (!rec->p[i])
+ goto error;
+ }
+
+ return poly;
+error:
+ isl_poly_free(poly);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_set_dim_name(
+ __isl_take isl_qpolynomial *qp,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ return NULL;
+ if (type == isl_dim_out)
+ isl_die(isl_qpolynomial_get_ctx(qp), isl_error_invalid,
+ "cannot set name of output/set dimension",
+ return isl_qpolynomial_free(qp));
+ type = domain_type(type);
+ qp->dim = isl_space_set_dim_name(qp->dim, type, pos, s);
+ if (!qp->dim)
+ goto error;
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_drop_dims(
+ __isl_take isl_qpolynomial *qp,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ isl_size offset;
+
+ if (!qp)
+ return NULL;
+ if (type == isl_dim_out)
+ isl_die(qp->dim->ctx, isl_error_invalid,
+ "cannot drop output/set dimension",
+ goto error);
+ if (isl_qpolynomial_check_range(qp, type, first, n) < 0)
+ return isl_qpolynomial_free(qp);
+ type = domain_type(type);
+ if (n == 0 && !isl_space_is_named_or_nested(qp->dim, type))
+ return qp;
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ return NULL;
+
+ isl_assert(qp->dim->ctx, type == isl_dim_param ||
+ type == isl_dim_set, goto error);
+
+ qp->dim = isl_space_drop_dims(qp->dim, type, first, n);
+ if (!qp->dim)
+ goto error;
+
+ offset = isl_qpolynomial_domain_var_offset(qp, type);
+ if (offset < 0)
+ goto error;
+ first += offset;
+
+ qp->div = isl_mat_drop_cols(qp->div, 2 + first, n);
+ if (!qp->div)
+ goto error;
+
+ qp->poly = isl_poly_drop(qp->poly, first, n);
+ if (!qp->poly)
+ goto error;
+
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+/* Project the domain of the quasi-polynomial onto its parameter space.
+ * The quasi-polynomial may not involve any of the domain dimensions.
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_project_domain_on_params(
+ __isl_take isl_qpolynomial *qp)
+{
+ isl_space *space;
+ isl_size n;
+ isl_bool involves;
+
+ n = isl_qpolynomial_dim(qp, isl_dim_in);
+ if (n < 0)
+ return isl_qpolynomial_free(qp);
+ involves = isl_qpolynomial_involves_dims(qp, isl_dim_in, 0, n);
+ if (involves < 0)
+ return isl_qpolynomial_free(qp);
+ if (involves)
+ isl_die(isl_qpolynomial_get_ctx(qp), isl_error_invalid,
+ "polynomial involves some of the domain dimensions",
+ return isl_qpolynomial_free(qp));
+ qp = isl_qpolynomial_drop_dims(qp, isl_dim_in, 0, n);
+ space = isl_qpolynomial_get_domain_space(qp);
+ space = isl_space_params(space);
+ qp = isl_qpolynomial_reset_domain_space(qp, space);
+ return qp;
+}
+
+static __isl_give isl_qpolynomial *isl_qpolynomial_substitute_equalities_lifted(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_basic_set *eq)
+{
+ int i, j, k;
+ isl_int denom;
+ unsigned total;
+ unsigned n_div;
+ isl_poly *poly;
+
+ if (!eq)
+ goto error;
+ if (eq->n_eq == 0) {
+ isl_basic_set_free(eq);
+ return qp;
+ }
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ goto error;
+ qp->div = isl_mat_cow(qp->div);
+ if (!qp->div)
+ goto error;
+
+ total = isl_basic_set_offset(eq, isl_dim_div);
+ n_div = eq->n_div;
+ isl_int_init(denom);
+ for (i = 0; i < eq->n_eq; ++i) {
+ j = isl_seq_last_non_zero(eq->eq[i], total + n_div);
+ if (j < 0 || j == 0 || j >= total)
+ continue;
+
+ for (k = 0; k < qp->div->n_row; ++k) {
+ if (isl_int_is_zero(qp->div->row[k][1 + j]))
+ continue;
+ isl_seq_elim(qp->div->row[k] + 1, eq->eq[i], j, total,
+ &qp->div->row[k][0]);
+ normalize_div(qp, k);
+ }
+
+ if (isl_int_is_pos(eq->eq[i][j]))
+ isl_seq_neg(eq->eq[i], eq->eq[i], total);
+ isl_int_abs(denom, eq->eq[i][j]);
+ isl_int_set_si(eq->eq[i][j], 0);
+
+ poly = isl_poly_from_affine(qp->dim->ctx,
+ eq->eq[i], denom, total);
+ qp->poly = isl_poly_subs(qp->poly, j - 1, 1, &poly);
+ isl_poly_free(poly);
+ }
+ isl_int_clear(denom);
+
+ if (!qp->poly)
+ goto error;
+
+ isl_basic_set_free(eq);
+
+ qp = substitute_non_divs(qp);
+ qp = sort_divs(qp);
+
+ return qp;
+error:
+ isl_basic_set_free(eq);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+/* Exploit the equalities in "eq" to simplify the quasi-polynomial.
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_substitute_equalities(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_basic_set *eq)
+{
+ if (!qp || !eq)
+ goto error;
+ if (qp->div->n_row > 0)
+ eq = isl_basic_set_add_dims(eq, isl_dim_set, qp->div->n_row);
+ return isl_qpolynomial_substitute_equalities_lifted(qp, eq);
+error:
+ isl_basic_set_free(eq);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+/* Look for equalities among the variables shared by context and qp
+ * and the integer divisions of qp, if any.
+ * The equalities are then used to eliminate variables and/or integer
+ * divisions from qp.
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_gist(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_set *context)
+{
+ isl_local_space *ls;
+ isl_basic_set *aff;
+
+ ls = isl_qpolynomial_get_domain_local_space(qp);
+ context = isl_local_space_lift_set(ls, context);
+
+ aff = isl_set_affine_hull(context);
+ return isl_qpolynomial_substitute_equalities_lifted(qp, aff);
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_gist_params(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_set *context)
+{
+ isl_space *space = isl_qpolynomial_get_domain_space(qp);
+ isl_set *dom_context = isl_set_universe(space);
+ dom_context = isl_set_intersect_params(dom_context, context);
+ return isl_qpolynomial_gist(qp, dom_context);
+}
+
+/* Return a zero isl_qpolynomial in the given space.
+ *
+ * This is a helper function for isl_pw_*_as_* that ensures a uniform
+ * interface over all piecewise types.
+ */
+static __isl_give isl_qpolynomial *isl_qpolynomial_zero_in_space(
+ __isl_take isl_space *space)
+{
+ return isl_qpolynomial_zero_on_domain(isl_space_domain(space));
+}
+
+#define isl_qpolynomial_involves_nan isl_qpolynomial_is_nan
+
+#undef PW
+#define PW isl_pw_qpolynomial
+#undef BASE
+#define BASE qpolynomial
+#undef EL_IS_ZERO
+#define EL_IS_ZERO is_zero
+#undef ZERO
+#define ZERO zero
+#undef IS_ZERO
+#define IS_ZERO is_zero
+#undef FIELD
+#define FIELD qp
+#undef DEFAULT_IS_ZERO
+#define DEFAULT_IS_ZERO 1
+
+#include <isl_pw_templ.c>
+#include <isl_pw_eval.c>
+#include <isl_pw_insert_dims_templ.c>
+#include <isl_pw_lift_templ.c>
+#include <isl_pw_morph_templ.c>
+#include <isl_pw_move_dims_templ.c>
+#include <isl_pw_neg_templ.c>
+#include <isl_pw_opt_templ.c>
+#include <isl_pw_sub_templ.c>
+
+#undef BASE
+#define BASE pw_qpolynomial
+
+#include <isl_union_single.c>
+#include <isl_union_eval.c>
+#include <isl_union_neg.c>
+
+int isl_pw_qpolynomial_is_one(__isl_keep isl_pw_qpolynomial *pwqp)
+{
+ if (!pwqp)
+ return -1;
+
+ if (pwqp->n != -1)
+ return 0;
+
+ if (!isl_set_plain_is_universe(pwqp->p[0].set))
+ return 0;
+
+ return isl_qpolynomial_is_one(pwqp->p[0].qp);
+}
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add(
+ __isl_take isl_pw_qpolynomial *pwqp1,
+ __isl_take isl_pw_qpolynomial *pwqp2)
+{
+ return isl_pw_qpolynomial_union_add_(pwqp1, pwqp2);
+}
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_mul(
+ __isl_take isl_pw_qpolynomial *pwqp1,
+ __isl_take isl_pw_qpolynomial *pwqp2)
+{
+ int i, j, n;
+ struct isl_pw_qpolynomial *res;
+
+ if (!pwqp1 || !pwqp2)
+ goto error;
+
+ isl_assert(pwqp1->dim->ctx, isl_space_is_equal(pwqp1->dim, pwqp2->dim),
+ goto error);
+
+ if (isl_pw_qpolynomial_is_zero(pwqp1)) {
+ isl_pw_qpolynomial_free(pwqp2);
+ return pwqp1;
+ }
+
+ if (isl_pw_qpolynomial_is_zero(pwqp2)) {
+ isl_pw_qpolynomial_free(pwqp1);
+ return pwqp2;
+ }
+
+ if (isl_pw_qpolynomial_is_one(pwqp1)) {
+ isl_pw_qpolynomial_free(pwqp1);
+ return pwqp2;
+ }
+
+ if (isl_pw_qpolynomial_is_one(pwqp2)) {
+ isl_pw_qpolynomial_free(pwqp2);
+ return pwqp1;
+ }
+
+ n = pwqp1->n * pwqp2->n;
+ res = isl_pw_qpolynomial_alloc_size(isl_space_copy(pwqp1->dim), n);
+
+ for (i = 0; i < pwqp1->n; ++i) {
+ for (j = 0; j < pwqp2->n; ++j) {
+ struct isl_set *common;
+ struct isl_qpolynomial *prod;
+ common = isl_set_intersect(isl_set_copy(pwqp1->p[i].set),
+ isl_set_copy(pwqp2->p[j].set));
+ if (isl_set_plain_is_empty(common)) {
+ isl_set_free(common);
+ continue;
+ }
+
+ prod = isl_qpolynomial_mul(
+ isl_qpolynomial_copy(pwqp1->p[i].qp),
+ isl_qpolynomial_copy(pwqp2->p[j].qp));
+
+ res = isl_pw_qpolynomial_add_piece(res, common, prod);
+ }
+ }
+
+ isl_pw_qpolynomial_free(pwqp1);
+ isl_pw_qpolynomial_free(pwqp2);
+
+ return res;
+error:
+ isl_pw_qpolynomial_free(pwqp1);
+ isl_pw_qpolynomial_free(pwqp2);
+ return NULL;
+}
+
+__isl_give isl_val *isl_poly_eval(__isl_take isl_poly *poly,
+ __isl_take isl_vec *vec)
+{
+ int i;
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+ isl_val *res;
+ isl_val *base;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ goto error;
+ if (is_cst) {
+ isl_vec_free(vec);
+ res = isl_poly_get_constant_val(poly);
+ isl_poly_free(poly);
+ return res;
+ }
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec || !vec)
+ goto error;
+
+ isl_assert(poly->ctx, rec->n >= 1, goto error);
+
+ base = isl_val_rat_from_isl_int(poly->ctx,
+ vec->el[1 + poly->var], vec->el[0]);
+
+ res = isl_poly_eval(isl_poly_copy(rec->p[rec->n - 1]),
+ isl_vec_copy(vec));
+
+ for (i = rec->n - 2; i >= 0; --i) {
+ res = isl_val_mul(res, isl_val_copy(base));
+ res = isl_val_add(res, isl_poly_eval(isl_poly_copy(rec->p[i]),
+ isl_vec_copy(vec)));
+ }
+
+ isl_val_free(base);
+ isl_poly_free(poly);
+ isl_vec_free(vec);
+ return res;
+error:
+ isl_poly_free(poly);
+ isl_vec_free(vec);
+ return NULL;
+}
+
+/* Evaluate "qp" in the void point "pnt".
+ * In particular, return the value NaN.
+ */
+static __isl_give isl_val *eval_void(__isl_take isl_qpolynomial *qp,
+ __isl_take isl_point *pnt)
+{
+ isl_ctx *ctx;
+
+ ctx = isl_point_get_ctx(pnt);
+ isl_qpolynomial_free(qp);
+ isl_point_free(pnt);
+ return isl_val_nan(ctx);
+}
+
+__isl_give isl_val *isl_qpolynomial_eval(__isl_take isl_qpolynomial *qp,
+ __isl_take isl_point *pnt)
+{
+ isl_bool is_void;
+ isl_vec *ext;
+ isl_val *v;
+
+ if (!qp || !pnt)
+ goto error;
+ isl_assert(pnt->dim->ctx, isl_space_is_equal(pnt->dim, qp->dim), goto error);
+ is_void = isl_point_is_void(pnt);
+ if (is_void < 0)
+ goto error;
+ if (is_void)
+ return eval_void(qp, pnt);
+
+ ext = isl_local_extend_point_vec(qp->div, isl_vec_copy(pnt->vec));
+
+ v = isl_poly_eval(isl_poly_copy(qp->poly), ext);
+
+ isl_qpolynomial_free(qp);
+ isl_point_free(pnt);
+
+ return v;
+error:
+ isl_qpolynomial_free(qp);
+ isl_point_free(pnt);
+ return NULL;
+}
+
+int isl_poly_cmp(__isl_keep isl_poly_cst *cst1, __isl_keep isl_poly_cst *cst2)
+{
+ int cmp;
+ isl_int t;
+ isl_int_init(t);
+ isl_int_mul(t, cst1->n, cst2->d);
+ isl_int_submul(t, cst2->n, cst1->d);
+ cmp = isl_int_sgn(t);
+ isl_int_clear(t);
+ return cmp;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_insert_dims(
+ __isl_take isl_qpolynomial *qp, enum isl_dim_type type,
+ unsigned first, unsigned n)
+{
+ unsigned total;
+ unsigned g_pos;
+ int *exp;
+
+ if (!qp)
+ return NULL;
+ if (type == isl_dim_out)
+ isl_die(qp->div->ctx, isl_error_invalid,
+ "cannot insert output/set dimensions",
+ goto error);
+ if (isl_qpolynomial_check_range(qp, type, first, 0) < 0)
+ return isl_qpolynomial_free(qp);
+ type = domain_type(type);
+ if (n == 0 && !isl_space_is_named_or_nested(qp->dim, type))
+ return qp;
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ return NULL;
+
+ g_pos = pos(qp->dim, type) + first;
+
+ qp->div = isl_mat_insert_zero_cols(qp->div, 2 + g_pos, n);
+ if (!qp->div)
+ goto error;
+
+ total = qp->div->n_col - 2;
+ if (total > g_pos) {
+ int i;
+ exp = isl_alloc_array(qp->div->ctx, int, total - g_pos);
+ if (!exp)
+ goto error;
+ for (i = 0; i < total - g_pos; ++i)
+ exp[i] = i + n;
+ qp->poly = expand(qp->poly, exp, g_pos);
+ free(exp);
+ if (!qp->poly)
+ goto error;
+ }
+
+ qp->dim = isl_space_insert_dims(qp->dim, type, first, n);
+ if (!qp->dim)
+ goto error;
+
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_add_dims(
+ __isl_take isl_qpolynomial *qp, enum isl_dim_type type, unsigned n)
+{
+ isl_size pos;
+
+ pos = isl_qpolynomial_dim(qp, type);
+ if (pos < 0)
+ return isl_qpolynomial_free(qp);
+
+ return isl_qpolynomial_insert_dims(qp, type, pos, n);
+}
+
+static int *reordering_move(isl_ctx *ctx,
+ unsigned len, unsigned dst, unsigned src, unsigned n)
+{
+ int i;
+ int *reordering;
+
+ reordering = isl_alloc_array(ctx, int, len);
+ if (!reordering)
+ return NULL;
+
+ if (dst <= src) {
+ for (i = 0; i < dst; ++i)
+ reordering[i] = i;
+ for (i = 0; i < n; ++i)
+ reordering[src + i] = dst + i;
+ for (i = 0; i < src - dst; ++i)
+ reordering[dst + i] = dst + n + i;
+ for (i = 0; i < len - src - n; ++i)
+ reordering[src + n + i] = src + n + i;
+ } else {
+ for (i = 0; i < src; ++i)
+ reordering[i] = i;
+ for (i = 0; i < n; ++i)
+ reordering[src + i] = dst + i;
+ for (i = 0; i < dst - src; ++i)
+ reordering[src + n + i] = src + i;
+ for (i = 0; i < len - dst - n; ++i)
+ reordering[dst + n + i] = dst + n + i;
+ }
+
+ return reordering;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_move_dims(
+ __isl_take isl_qpolynomial *qp,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ unsigned g_dst_pos;
+ unsigned g_src_pos;
+ int *reordering;
+
+ if (!qp)
+ return NULL;
+
+ if (dst_type == isl_dim_out || src_type == isl_dim_out)
+ isl_die(qp->dim->ctx, isl_error_invalid,
+ "cannot move output/set dimension",
+ goto error);
+ if (isl_qpolynomial_check_range(qp, src_type, src_pos, n) < 0)
+ return isl_qpolynomial_free(qp);
+ if (dst_type == isl_dim_in)
+ dst_type = isl_dim_set;
+ if (src_type == isl_dim_in)
+ src_type = isl_dim_set;
+
+ if (n == 0 &&
+ !isl_space_is_named_or_nested(qp->dim, src_type) &&
+ !isl_space_is_named_or_nested(qp->dim, dst_type))
+ return qp;
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ return NULL;
+
+ g_dst_pos = pos(qp->dim, dst_type) + dst_pos;
+ g_src_pos = pos(qp->dim, src_type) + src_pos;
+ if (dst_type > src_type)
+ g_dst_pos -= n;
+
+ qp->div = isl_mat_move_cols(qp->div, 2 + g_dst_pos, 2 + g_src_pos, n);
+ if (!qp->div)
+ goto error;
+ qp = sort_divs(qp);
+ if (!qp)
+ goto error;
+
+ reordering = reordering_move(qp->dim->ctx,
+ qp->div->n_col - 2, g_dst_pos, g_src_pos, n);
+ if (!reordering)
+ goto error;
+
+ qp->poly = reorder(qp->poly, reordering);
+ free(reordering);
+ if (!qp->poly)
+ goto error;
+
+ qp->dim = isl_space_move_dims(qp->dim, dst_type, dst_pos, src_type, src_pos, n);
+ if (!qp->dim)
+ goto error;
+
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_from_affine(
+ __isl_take isl_space *space, isl_int *f, isl_int denom)
+{
+ isl_size d;
+ isl_poly *poly;
+
+ space = isl_space_domain(space);
+ if (!space)
+ return NULL;
+
+ d = isl_space_dim(space, isl_dim_all);
+ poly = d < 0 ? NULL : isl_poly_from_affine(space->ctx, f, denom, 1 + d);
+
+ return isl_qpolynomial_alloc(space, 0, poly);
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_from_aff(__isl_take isl_aff *aff)
+{
+ isl_ctx *ctx;
+ isl_poly *poly;
+ isl_qpolynomial *qp;
+
+ if (!aff)
+ return NULL;
+
+ ctx = isl_aff_get_ctx(aff);
+ poly = isl_poly_from_affine(ctx, aff->v->el + 1, aff->v->el[0],
+ aff->v->size - 1);
+
+ qp = isl_qpolynomial_alloc(isl_aff_get_domain_space(aff),
+ aff->ls->div->n_row, poly);
+ if (!qp)
+ goto error;
+
+ isl_mat_free(qp->div);
+ qp->div = isl_mat_copy(aff->ls->div);
+ qp->div = isl_mat_cow(qp->div);
+ if (!qp->div)
+ goto error;
+
+ isl_aff_free(aff);
+ qp = reduce_divs(qp);
+ qp = remove_redundant_divs(qp);
+ return qp;
+error:
+ isl_aff_free(aff);
+ return isl_qpolynomial_free(qp);
+}
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_from_pw_aff(
+ __isl_take isl_pw_aff *pwaff)
+{
+ int i;
+ isl_pw_qpolynomial *pwqp;
+
+ if (!pwaff)
+ return NULL;
+
+ pwqp = isl_pw_qpolynomial_alloc_size(isl_pw_aff_get_space(pwaff),
+ pwaff->n);
+
+ for (i = 0; i < pwaff->n; ++i) {
+ isl_set *dom;
+ isl_qpolynomial *qp;
+
+ dom = isl_set_copy(pwaff->p[i].set);
+ qp = isl_qpolynomial_from_aff(isl_aff_copy(pwaff->p[i].aff));
+ pwqp = isl_pw_qpolynomial_add_piece(pwqp, dom, qp);
+ }
+
+ isl_pw_aff_free(pwaff);
+ return pwqp;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_from_constraint(
+ __isl_take isl_constraint *c, enum isl_dim_type type, unsigned pos)
+{
+ isl_aff *aff;
+
+ aff = isl_constraint_get_bound(c, type, pos);
+ isl_constraint_free(c);
+ return isl_qpolynomial_from_aff(aff);
+}
+
+/* For each 0 <= i < "n", replace variable "first" + i of type "type"
+ * in "qp" by subs[i].
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_substitute(
+ __isl_take isl_qpolynomial *qp,
+ enum isl_dim_type type, unsigned first, unsigned n,
+ __isl_keep isl_qpolynomial **subs)
+{
+ int i;
+ isl_poly **polys;
+
+ if (n == 0)
+ return qp;
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ return NULL;
+
+ if (type == isl_dim_out)
+ isl_die(qp->dim->ctx, isl_error_invalid,
+ "cannot substitute output/set dimension",
+ goto error);
+ if (isl_qpolynomial_check_range(qp, type, first, n) < 0)
+ return isl_qpolynomial_free(qp);
+ type = domain_type(type);
+
+ for (i = 0; i < n; ++i)
+ if (!subs[i])
+ goto error;
+
+ for (i = 0; i < n; ++i)
+ if (isl_qpolynomial_check_equal_space(qp, subs[i]) < 0)
+ goto error;
+
+ isl_assert(qp->dim->ctx, qp->div->n_row == 0, goto error);
+ for (i = 0; i < n; ++i)
+ isl_assert(qp->dim->ctx, subs[i]->div->n_row == 0, goto error);
+
+ first += pos(qp->dim, type);
+
+ polys = isl_alloc_array(qp->dim->ctx, struct isl_poly *, n);
+ if (!polys)
+ goto error;
+ for (i = 0; i < n; ++i)
+ polys[i] = subs[i]->poly;
+
+ qp->poly = isl_poly_subs(qp->poly, first, n, polys);
+
+ free(polys);
+
+ if (!qp->poly)
+ goto error;
+
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+/* Extend "bset" with extra set dimensions for each integer division
+ * in "qp" and then call "fn" with the extended bset and the polynomial
+ * that results from replacing each of the integer divisions by the
+ * corresponding extra set dimension.
+ */
+isl_stat isl_qpolynomial_as_polynomial_on_domain(__isl_keep isl_qpolynomial *qp,
+ __isl_keep isl_basic_set *bset,
+ isl_stat (*fn)(__isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, void *user), void *user)
+{
+ isl_space *space;
+ isl_local_space *ls;
+ isl_qpolynomial *poly;
+
+ if (!qp || !bset)
+ return isl_stat_error;
+ if (qp->div->n_row == 0)
+ return fn(isl_basic_set_copy(bset), isl_qpolynomial_copy(qp),
+ user);
+
+ space = isl_space_copy(qp->dim);
+ space = isl_space_add_dims(space, isl_dim_set, qp->div->n_row);
+ poly = isl_qpolynomial_alloc(space, 0, isl_poly_copy(qp->poly));
+ bset = isl_basic_set_copy(bset);
+ ls = isl_qpolynomial_get_domain_local_space(qp);
+ bset = isl_local_space_lift_basic_set(ls, bset);
+
+ return fn(bset, poly, user);
+}
+
+/* Return total degree in variables first (inclusive) up to last (exclusive).
+ */
+int isl_poly_degree(__isl_keep isl_poly *poly, int first, int last)
+{
+ int deg = -1;
+ int i;
+ isl_bool is_zero, is_cst;
+ isl_poly_rec *rec;
+
+ is_zero = isl_poly_is_zero(poly);
+ if (is_zero < 0)
+ return -2;
+ if (is_zero)
+ return -1;
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return -2;
+ if (is_cst || poly->var < first)
+ return 0;
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ return -2;
+
+ for (i = 0; i < rec->n; ++i) {
+ int d;
+
+ is_zero = isl_poly_is_zero(rec->p[i]);
+ if (is_zero < 0)
+ return -2;
+ if (is_zero)
+ continue;
+ d = isl_poly_degree(rec->p[i], first, last);
+ if (poly->var < last)
+ d += i;
+ if (d > deg)
+ deg = d;
+ }
+
+ return deg;
+}
+
+/* Return total degree in set variables.
+ */
+int isl_qpolynomial_degree(__isl_keep isl_qpolynomial *poly)
+{
+ unsigned ovar;
+ isl_size nvar;
+
+ if (!poly)
+ return -2;
+
+ ovar = isl_space_offset(poly->dim, isl_dim_set);
+ nvar = isl_space_dim(poly->dim, isl_dim_set);
+ if (nvar < 0)
+ return -2;
+ return isl_poly_degree(poly->poly, ovar, ovar + nvar);
+}
+
+__isl_give isl_poly *isl_poly_coeff(__isl_keep isl_poly *poly,
+ unsigned pos, int deg)
+{
+ int i;
+ isl_bool is_cst;
+ isl_poly_rec *rec;
+
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return NULL;
+ if (is_cst || poly->var < pos) {
+ if (deg == 0)
+ return isl_poly_copy(poly);
+ else
+ return isl_poly_zero(poly->ctx);
+ }
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ return NULL;
+
+ if (poly->var == pos) {
+ if (deg < rec->n)
+ return isl_poly_copy(rec->p[deg]);
+ else
+ return isl_poly_zero(poly->ctx);
+ }
+
+ poly = isl_poly_copy(poly);
+ poly = isl_poly_cow(poly);
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ goto error;
+
+ for (i = 0; i < rec->n; ++i) {
+ isl_poly *t;
+ t = isl_poly_coeff(rec->p[i], pos, deg);
+ if (!t)
+ goto error;
+ isl_poly_free(rec->p[i]);
+ rec->p[i] = t;
+ }
+
+ return poly;
+error:
+ isl_poly_free(poly);
+ return NULL;
+}
+
+/* Return coefficient of power "deg" of variable "t_pos" of type "type".
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_coeff(
+ __isl_keep isl_qpolynomial *qp,
+ enum isl_dim_type type, unsigned t_pos, int deg)
+{
+ unsigned g_pos;
+ isl_poly *poly;
+ isl_qpolynomial *c;
+
+ if (!qp)
+ return NULL;
+
+ if (type == isl_dim_out)
+ isl_die(qp->div->ctx, isl_error_invalid,
+ "output/set dimension does not have a coefficient",
+ return NULL);
+ if (isl_qpolynomial_check_range(qp, type, t_pos, 1) < 0)
+ return NULL;
+ type = domain_type(type);
+
+ g_pos = pos(qp->dim, type) + t_pos;
+ poly = isl_poly_coeff(qp->poly, g_pos, deg);
+
+ c = isl_qpolynomial_alloc(isl_space_copy(qp->dim),
+ qp->div->n_row, poly);
+ if (!c)
+ return NULL;
+ isl_mat_free(c->div);
+ c->div = isl_mat_copy(qp->div);
+ if (!c->div)
+ goto error;
+ return c;
+error:
+ isl_qpolynomial_free(c);
+ return NULL;
+}
+
+/* Homogenize the polynomial in the variables first (inclusive) up to
+ * last (exclusive) by inserting powers of variable first.
+ * Variable first is assumed not to appear in the input.
+ */
+__isl_give isl_poly *isl_poly_homogenize(__isl_take isl_poly *poly, int deg,
+ int target, int first, int last)
+{
+ int i;
+ isl_bool is_zero, is_cst;
+ isl_poly_rec *rec;
+
+ is_zero = isl_poly_is_zero(poly);
+ if (is_zero < 0)
+ return isl_poly_free(poly);
+ if (is_zero)
+ return poly;
+ if (deg == target)
+ return poly;
+ is_cst = isl_poly_is_cst(poly);
+ if (is_cst < 0)
+ return isl_poly_free(poly);
+ if (is_cst || poly->var < first) {
+ isl_poly *hom;
+
+ hom = isl_poly_var_pow(poly->ctx, first, target - deg);
+ if (!hom)
+ goto error;
+ rec = isl_poly_as_rec(hom);
+ rec->p[target - deg] = isl_poly_mul(rec->p[target - deg], poly);
+
+ return hom;
+ }
+
+ poly = isl_poly_cow(poly);
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ goto error;
+
+ for (i = 0; i < rec->n; ++i) {
+ is_zero = isl_poly_is_zero(rec->p[i]);
+ if (is_zero < 0)
+ return isl_poly_free(poly);
+ if (is_zero)
+ continue;
+ rec->p[i] = isl_poly_homogenize(rec->p[i],
+ poly->var < last ? deg + i : i, target,
+ first, last);
+ if (!rec->p[i])
+ goto error;
+ }
+
+ return poly;
+error:
+ isl_poly_free(poly);
+ return NULL;
+}
+
+/* Homogenize the polynomial in the set variables by introducing
+ * powers of an extra set variable at position 0.
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_homogenize(
+ __isl_take isl_qpolynomial *poly)
+{
+ unsigned ovar;
+ isl_size nvar;
+ int deg = isl_qpolynomial_degree(poly);
+
+ if (deg < -1)
+ goto error;
+
+ poly = isl_qpolynomial_insert_dims(poly, isl_dim_in, 0, 1);
+ poly = isl_qpolynomial_cow(poly);
+ if (!poly)
+ goto error;
+
+ ovar = isl_space_offset(poly->dim, isl_dim_set);
+ nvar = isl_space_dim(poly->dim, isl_dim_set);
+ if (nvar < 0)
+ return isl_qpolynomial_free(poly);
+ poly->poly = isl_poly_homogenize(poly->poly, 0, deg, ovar, ovar + nvar);
+ if (!poly->poly)
+ goto error;
+
+ return poly;
+error:
+ isl_qpolynomial_free(poly);
+ return NULL;
+}
+
+__isl_give isl_term *isl_term_alloc(__isl_take isl_space *space,
+ __isl_take isl_mat *div)
+{
+ isl_term *term;
+ isl_size d;
+ int n;
+
+ d = isl_space_dim(space, isl_dim_all);
+ if (d < 0 || !div)
+ goto error;
+
+ n = d + div->n_row;
+
+ term = isl_calloc(space->ctx, struct isl_term,
+ sizeof(struct isl_term) + (n - 1) * sizeof(int));
+ if (!term)
+ goto error;
+
+ term->ref = 1;
+ term->dim = space;
+ term->div = div;
+ isl_int_init(term->n);
+ isl_int_init(term->d);
+
+ return term;
+error:
+ isl_space_free(space);
+ isl_mat_free(div);
+ return NULL;
+}
+
+__isl_give isl_term *isl_term_copy(__isl_keep isl_term *term)
+{
+ if (!term)
+ return NULL;
+
+ term->ref++;
+ return term;
+}
+
+__isl_give isl_term *isl_term_dup(__isl_keep isl_term *term)
+{
+ int i;
+ isl_term *dup;
+ isl_size total;
+
+ total = isl_term_dim(term, isl_dim_all);
+ if (total < 0)
+ return NULL;
+
+ dup = isl_term_alloc(isl_space_copy(term->dim), isl_mat_copy(term->div));
+ if (!dup)
+ return NULL;
+
+ isl_int_set(dup->n, term->n);
+ isl_int_set(dup->d, term->d);
+
+ for (i = 0; i < total; ++i)
+ dup->pow[i] = term->pow[i];
+
+ return dup;
+}
+
+__isl_give isl_term *isl_term_cow(__isl_take isl_term *term)
+{
+ if (!term)
+ return NULL;
+
+ if (term->ref == 1)
+ return term;
+ term->ref--;
+ return isl_term_dup(term);
+}
+
+__isl_null isl_term *isl_term_free(__isl_take isl_term *term)
+{
+ if (!term)
+ return NULL;
+
+ if (--term->ref > 0)
+ return NULL;
+
+ isl_space_free(term->dim);
+ isl_mat_free(term->div);
+ isl_int_clear(term->n);
+ isl_int_clear(term->d);
+ free(term);
+
+ return NULL;
+}
+
+isl_size isl_term_dim(__isl_keep isl_term *term, enum isl_dim_type type)
+{
+ isl_size dim;
+
+ if (!term)
+ return isl_size_error;
+
+ switch (type) {
+ case isl_dim_param:
+ case isl_dim_in:
+ case isl_dim_out: return isl_space_dim(term->dim, type);
+ case isl_dim_div: return term->div->n_row;
+ case isl_dim_all: dim = isl_space_dim(term->dim, isl_dim_all);
+ if (dim < 0)
+ return isl_size_error;
+ return dim + term->div->n_row;
+ default: return isl_size_error;
+ }
+}
+
+/* Return the space of "term".
+ */
+static __isl_keep isl_space *isl_term_peek_space(__isl_keep isl_term *term)
+{
+ return term ? term->dim : NULL;
+}
+
+/* Return the offset of the first variable of type "type" within
+ * the variables of "term".
+ */
+static isl_size isl_term_offset(__isl_keep isl_term *term,
+ enum isl_dim_type type)
+{
+ isl_space *space;
+
+ space = isl_term_peek_space(term);
+ if (!space)
+ return isl_size_error;
+
+ switch (type) {
+ case isl_dim_param:
+ case isl_dim_set: return isl_space_offset(space, type);
+ case isl_dim_div: return isl_space_dim(space, isl_dim_all);
+ default:
+ isl_die(isl_term_get_ctx(term), isl_error_invalid,
+ "invalid dimension type", return isl_size_error);
+ }
+}
+
+isl_ctx *isl_term_get_ctx(__isl_keep isl_term *term)
+{
+ return term ? term->dim->ctx : NULL;
+}
+
+void isl_term_get_num(__isl_keep isl_term *term, isl_int *n)
+{
+ if (!term)
+ return;
+ isl_int_set(*n, term->n);
+}
+
+/* Return the coefficient of the term "term".
+ */
+__isl_give isl_val *isl_term_get_coefficient_val(__isl_keep isl_term *term)
+{
+ if (!term)
+ return NULL;
+
+ return isl_val_rat_from_isl_int(isl_term_get_ctx(term),
+ term->n, term->d);
+}
+
+#undef TYPE
+#define TYPE isl_term
+static
+#include "check_type_range_templ.c"
+
+isl_size isl_term_get_exp(__isl_keep isl_term *term,
+ enum isl_dim_type type, unsigned pos)
+{
+ isl_size offset;
+
+ if (isl_term_check_range(term, type, pos, 1) < 0)
+ return isl_size_error;
+ offset = isl_term_offset(term, type);
+ if (offset < 0)
+ return isl_size_error;
+
+ return term->pow[offset + pos];
+}
+
+__isl_give isl_aff *isl_term_get_div(__isl_keep isl_term *term, unsigned pos)
+{
+ isl_local_space *ls;
+ isl_aff *aff;
+
+ if (isl_term_check_range(term, isl_dim_div, pos, 1) < 0)
+ return NULL;
+
+ ls = isl_local_space_alloc_div(isl_space_copy(term->dim),
+ isl_mat_copy(term->div));
+ aff = isl_aff_alloc(ls);
+ if (!aff)
+ return NULL;
+
+ isl_seq_cpy(aff->v->el, term->div->row[pos], aff->v->size);
+
+ aff = isl_aff_normalize(aff);
+
+ return aff;
+}
+
+__isl_give isl_term *isl_poly_foreach_term(__isl_keep isl_poly *poly,
+ isl_stat (*fn)(__isl_take isl_term *term, void *user),
+ __isl_take isl_term *term, void *user)
+{
+ int i;
+ isl_bool is_zero, is_bad, is_cst;
+ isl_poly_rec *rec;
+
+ is_zero = isl_poly_is_zero(poly);
+ if (is_zero < 0 || !term)
+ goto error;
+
+ if (is_zero)
+ return term;
+
+ is_cst = isl_poly_is_cst(poly);
+ is_bad = isl_poly_is_nan(poly);
+ if (is_bad >= 0 && !is_bad)
+ is_bad = isl_poly_is_infty(poly);
+ if (is_bad >= 0 && !is_bad)
+ is_bad = isl_poly_is_neginfty(poly);
+ if (is_cst < 0 || is_bad < 0)
+ return isl_term_free(term);
+ if (is_bad)
+ isl_die(isl_term_get_ctx(term), isl_error_invalid,
+ "cannot handle NaN/infty polynomial",
+ return isl_term_free(term));
+
+ if (is_cst) {
+ isl_poly_cst *cst;
+ cst = isl_poly_as_cst(poly);
+ if (!cst)
+ goto error;
+ term = isl_term_cow(term);
+ if (!term)
+ goto error;
+ isl_int_set(term->n, cst->n);
+ isl_int_set(term->d, cst->d);
+ if (fn(isl_term_copy(term), user) < 0)
+ goto error;
+ return term;
+ }
+
+ rec = isl_poly_as_rec(poly);
+ if (!rec)
+ goto error;
+
+ for (i = 0; i < rec->n; ++i) {
+ term = isl_term_cow(term);
+ if (!term)
+ goto error;
+ term->pow[poly->var] = i;
+ term = isl_poly_foreach_term(rec->p[i], fn, term, user);
+ if (!term)
+ goto error;
+ }
+ term = isl_term_cow(term);
+ if (!term)
+ return NULL;
+ term->pow[poly->var] = 0;
+
+ return term;
+error:
+ isl_term_free(term);
+ return NULL;
+}
+
+isl_stat isl_qpolynomial_foreach_term(__isl_keep isl_qpolynomial *qp,
+ isl_stat (*fn)(__isl_take isl_term *term, void *user), void *user)
+{
+ isl_term *term;
+
+ if (!qp)
+ return isl_stat_error;
+
+ term = isl_term_alloc(isl_space_copy(qp->dim), isl_mat_copy(qp->div));
+ if (!term)
+ return isl_stat_error;
+
+ term = isl_poly_foreach_term(qp->poly, fn, term, user);
+
+ isl_term_free(term);
+
+ return term ? isl_stat_ok : isl_stat_error;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_from_term(__isl_take isl_term *term)
+{
+ isl_poly *poly;
+ isl_qpolynomial *qp;
+ int i;
+ isl_size n;
+
+ n = isl_term_dim(term, isl_dim_all);
+ if (n < 0)
+ term = isl_term_free(term);
+ if (!term)
+ return NULL;
+
+ poly = isl_poly_rat_cst(term->dim->ctx, term->n, term->d);
+ for (i = 0; i < n; ++i) {
+ if (!term->pow[i])
+ continue;
+ poly = isl_poly_mul(poly,
+ isl_poly_var_pow(term->dim->ctx, i, term->pow[i]));
+ }
+
+ qp = isl_qpolynomial_alloc(isl_space_copy(term->dim),
+ term->div->n_row, poly);
+ if (!qp)
+ goto error;
+ isl_mat_free(qp->div);
+ qp->div = isl_mat_copy(term->div);
+ if (!qp->div)
+ goto error;
+
+ isl_term_free(term);
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ isl_term_free(term);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_lift(__isl_take isl_qpolynomial *qp,
+ __isl_take isl_space *space)
+{
+ int i;
+ int extra;
+ isl_size total, d_set, d_qp;
+
+ if (!qp || !space)
+ goto error;
+
+ if (isl_space_is_equal(qp->dim, space)) {
+ isl_space_free(space);
+ return qp;
+ }
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ goto error;
+
+ d_set = isl_space_dim(space, isl_dim_set);
+ d_qp = isl_qpolynomial_domain_dim(qp, isl_dim_set);
+ extra = d_set - d_qp;
+ total = isl_space_dim(qp->dim, isl_dim_all);
+ if (d_set < 0 || d_qp < 0 || total < 0)
+ goto error;
+ if (qp->div->n_row) {
+ int *exp;
+
+ exp = isl_alloc_array(qp->div->ctx, int, qp->div->n_row);
+ if (!exp)
+ goto error;
+ for (i = 0; i < qp->div->n_row; ++i)
+ exp[i] = extra + i;
+ qp->poly = expand(qp->poly, exp, total);
+ free(exp);
+ if (!qp->poly)
+ goto error;
+ }
+ qp->div = isl_mat_insert_cols(qp->div, 2 + total, extra);
+ if (!qp->div)
+ goto error;
+ for (i = 0; i < qp->div->n_row; ++i)
+ isl_seq_clr(qp->div->row[i] + 2 + total, extra);
+
+ isl_space_free(qp->dim);
+ qp->dim = space;
+
+ return qp;
+error:
+ isl_space_free(space);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+/* For each parameter or variable that does not appear in qp,
+ * first eliminate the variable from all constraints and then set it to zero.
+ */
+static __isl_give isl_set *fix_inactive(__isl_take isl_set *set,
+ __isl_keep isl_qpolynomial *qp)
+{
+ int *active = NULL;
+ int i;
+ isl_size d;
+ isl_size nparam;
+ isl_size nvar;
+
+ d = isl_set_dim(set, isl_dim_all);
+ if (d < 0 || !qp)
+ goto error;
+
+ active = isl_calloc_array(set->ctx, int, d);
+ if (set_active(qp, active) < 0)
+ goto error;
+
+ for (i = 0; i < d; ++i)
+ if (!active[i])
+ break;
+
+ if (i == d) {
+ free(active);
+ return set;
+ }
+
+ nparam = isl_set_dim(set, isl_dim_param);
+ nvar = isl_set_dim(set, isl_dim_set);
+ if (nparam < 0 || nvar < 0)
+ goto error;
+ for (i = 0; i < nparam; ++i) {
+ if (active[i])
+ continue;
+ set = isl_set_eliminate(set, isl_dim_param, i, 1);
+ set = isl_set_fix_si(set, isl_dim_param, i, 0);
+ }
+ for (i = 0; i < nvar; ++i) {
+ if (active[nparam + i])
+ continue;
+ set = isl_set_eliminate(set, isl_dim_set, i, 1);
+ set = isl_set_fix_si(set, isl_dim_set, i, 0);
+ }
+
+ free(active);
+
+ return set;
+error:
+ free(active);
+ isl_set_free(set);
+ return NULL;
+}
+
+struct isl_opt_data {
+ isl_qpolynomial *qp;
+ int first;
+ isl_val *opt;
+ int max;
+};
+
+static isl_stat opt_fn(__isl_take isl_point *pnt, void *user)
+{
+ struct isl_opt_data *data = (struct isl_opt_data *)user;
+ isl_val *val;
+
+ val = isl_qpolynomial_eval(isl_qpolynomial_copy(data->qp), pnt);
+ if (data->first) {
+ data->first = 0;
+ data->opt = val;
+ } else if (data->max) {
+ data->opt = isl_val_max(data->opt, val);
+ } else {
+ data->opt = isl_val_min(data->opt, val);
+ }
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_val *isl_qpolynomial_opt_on_domain(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_set *set, int max)
+{
+ struct isl_opt_data data = { NULL, 1, NULL, max };
+ isl_bool is_cst;
+
+ if (!set || !qp)
+ goto error;
+
+ is_cst = isl_poly_is_cst(qp->poly);
+ if (is_cst < 0)
+ goto error;
+ if (is_cst) {
+ isl_set_free(set);
+ data.opt = isl_qpolynomial_get_constant_val(qp);
+ isl_qpolynomial_free(qp);
+ return data.opt;
+ }
+
+ set = fix_inactive(set, qp);
+
+ data.qp = qp;
+ if (isl_set_foreach_point(set, opt_fn, &data) < 0)
+ goto error;
+
+ if (data.first)
+ data.opt = isl_val_zero(isl_set_get_ctx(set));
+
+ isl_set_free(set);
+ isl_qpolynomial_free(qp);
+ return data.opt;
+error:
+ isl_set_free(set);
+ isl_qpolynomial_free(qp);
+ isl_val_free(data.opt);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_morph_domain(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_morph *morph)
+{
+ int i;
+ int n_sub;
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_poly **subs;
+ isl_mat *mat, *diag;
+
+ qp = isl_qpolynomial_cow(qp);
+
+ space = isl_qpolynomial_peek_domain_space(qp);
+ if (isl_morph_check_applies(morph, space) < 0)
+ goto error;
+
+ ctx = isl_qpolynomial_get_ctx(qp);
+ n_sub = morph->inv->n_row - 1;
+ if (morph->inv->n_row != morph->inv->n_col)
+ n_sub += qp->div->n_row;
+ subs = isl_calloc_array(ctx, struct isl_poly *, n_sub);
+ if (n_sub && !subs)
+ goto error;
+
+ for (i = 0; 1 + i < morph->inv->n_row; ++i)
+ subs[i] = isl_poly_from_affine(ctx, morph->inv->row[1 + i],
+ morph->inv->row[0][0], morph->inv->n_col);
+ if (morph->inv->n_row != morph->inv->n_col)
+ for (i = 0; i < qp->div->n_row; ++i)
+ subs[morph->inv->n_row - 1 + i] =
+ isl_poly_var_pow(ctx, morph->inv->n_col - 1 + i, 1);
+
+ qp->poly = isl_poly_subs(qp->poly, 0, n_sub, subs);
+
+ for (i = 0; i < n_sub; ++i)
+ isl_poly_free(subs[i]);
+ free(subs);
+
+ diag = isl_mat_diag(ctx, 1, morph->inv->row[0][0]);
+ mat = isl_mat_diagonal(diag, isl_mat_copy(morph->inv));
+ diag = isl_mat_diag(ctx, qp->div->n_row, morph->inv->row[0][0]);
+ mat = isl_mat_diagonal(mat, diag);
+ qp->div = isl_mat_product(qp->div, mat);
+ isl_space_free(qp->dim);
+ qp->dim = isl_space_copy(morph->ran->dim);
+
+ if (!qp->poly || !qp->div || !qp->dim)
+ goto error;
+
+ isl_morph_free(morph);
+
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ isl_morph_free(morph);
+ return NULL;
+}
+
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_mul(
+ __isl_take isl_union_pw_qpolynomial *upwqp1,
+ __isl_take isl_union_pw_qpolynomial *upwqp2)
+{
+ return isl_union_pw_qpolynomial_match_bin_op(upwqp1, upwqp2,
+ &isl_pw_qpolynomial_mul);
+}
+
+/* Reorder the dimension of "qp" according to the given reordering.
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_realign_domain(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_reordering *r)
+{
+ isl_space *space;
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ goto error;
+
+ r = isl_reordering_extend(r, qp->div->n_row);
+ if (!r)
+ goto error;
+
+ qp->div = isl_local_reorder(qp->div, isl_reordering_copy(r));
+ if (!qp->div)
+ goto error;
+
+ qp->poly = reorder(qp->poly, r->pos);
+ if (!qp->poly)
+ goto error;
+
+ space = isl_reordering_get_space(r);
+ qp = isl_qpolynomial_reset_domain_space(qp, space);
+
+ isl_reordering_free(r);
+ return qp;
+error:
+ isl_qpolynomial_free(qp);
+ isl_reordering_free(r);
+ return NULL;
+}
+
+__isl_give isl_qpolynomial *isl_qpolynomial_align_params(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_space *model)
+{
+ isl_bool equal_params;
+
+ if (!qp || !model)
+ goto error;
+
+ equal_params = isl_space_has_equal_params(qp->dim, model);
+ if (equal_params < 0)
+ goto error;
+ if (!equal_params) {
+ isl_reordering *exp;
+
+ exp = isl_parameter_alignment_reordering(qp->dim, model);
+ exp = isl_reordering_extend_space(exp,
+ isl_qpolynomial_get_domain_space(qp));
+ qp = isl_qpolynomial_realign_domain(qp, exp);
+ }
+
+ isl_space_free(model);
+ return qp;
+error:
+ isl_space_free(model);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+struct isl_split_periods_data {
+ int max_periods;
+ isl_pw_qpolynomial *res;
+};
+
+/* Create a slice where the integer division "div" has the fixed value "v".
+ * In particular, if "div" refers to floor(f/m), then create a slice
+ *
+ * m v <= f <= m v + (m - 1)
+ *
+ * or
+ *
+ * f - m v >= 0
+ * -f + m v + (m - 1) >= 0
+ */
+static __isl_give isl_set *set_div_slice(__isl_take isl_space *space,
+ __isl_keep isl_qpolynomial *qp, int div, isl_int v)
+{
+ isl_size total;
+ isl_basic_set *bset = NULL;
+ int k;
+
+ total = isl_space_dim(space, isl_dim_all);
+ if (total < 0 || !qp)
+ goto error;
+
+ bset = isl_basic_set_alloc_space(isl_space_copy(space), 0, 0, 2);
+
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bset->ineq[k], qp->div->row[div] + 1, 1 + total);
+ isl_int_submul(bset->ineq[k][0], v, qp->div->row[div][0]);
+
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_neg(bset->ineq[k], qp->div->row[div] + 1, 1 + total);
+ isl_int_addmul(bset->ineq[k][0], v, qp->div->row[div][0]);
+ isl_int_add(bset->ineq[k][0], bset->ineq[k][0], qp->div->row[div][0]);
+ isl_int_sub_ui(bset->ineq[k][0], bset->ineq[k][0], 1);
+
+ isl_space_free(space);
+ return isl_set_from_basic_set(bset);
+error:
+ isl_basic_set_free(bset);
+ isl_space_free(space);
+ return NULL;
+}
+
+static isl_stat split_periods(__isl_take isl_set *set,
+ __isl_take isl_qpolynomial *qp, void *user);
+
+/* Create a slice of the domain "set" such that integer division "div"
+ * has the fixed value "v" and add the results to data->res,
+ * replacing the integer division by "v" in "qp".
+ */
+static isl_stat set_div(__isl_take isl_set *set,
+ __isl_take isl_qpolynomial *qp, int div, isl_int v,
+ struct isl_split_periods_data *data)
+{
+ int i;
+ isl_size div_pos;
+ isl_set *slice;
+ isl_poly *cst;
+
+ slice = set_div_slice(isl_set_get_space(set), qp, div, v);
+ set = isl_set_intersect(set, slice);
+
+ div_pos = isl_qpolynomial_domain_var_offset(qp, isl_dim_div);
+ if (div_pos < 0)
+ goto error;
+
+ for (i = div + 1; i < qp->div->n_row; ++i) {
+ if (isl_int_is_zero(qp->div->row[i][2 + div_pos + div]))
+ continue;
+ isl_int_addmul(qp->div->row[i][1],
+ qp->div->row[i][2 + div_pos + div], v);
+ isl_int_set_si(qp->div->row[i][2 + div_pos + div], 0);
+ }
+
+ cst = isl_poly_rat_cst(qp->dim->ctx, v, qp->dim->ctx->one);
+ qp = substitute_div(qp, div, cst);
+
+ return split_periods(set, qp, data);
+error:
+ isl_set_free(set);
+ isl_qpolynomial_free(qp);
+ return isl_stat_error;
+}
+
+/* Split the domain "set" such that integer division "div"
+ * has a fixed value (ranging from "min" to "max") on each slice
+ * and add the results to data->res.
+ */
+static isl_stat split_div(__isl_take isl_set *set,
+ __isl_take isl_qpolynomial *qp, int div, isl_int min, isl_int max,
+ struct isl_split_periods_data *data)
+{
+ for (; isl_int_le(min, max); isl_int_add_ui(min, min, 1)) {
+ isl_set *set_i = isl_set_copy(set);
+ isl_qpolynomial *qp_i = isl_qpolynomial_copy(qp);
+
+ if (set_div(set_i, qp_i, div, min, data) < 0)
+ goto error;
+ }
+ isl_set_free(set);
+ isl_qpolynomial_free(qp);
+ return isl_stat_ok;
+error:
+ isl_set_free(set);
+ isl_qpolynomial_free(qp);
+ return isl_stat_error;
+}
+
+/* If "qp" refers to any integer division
+ * that can only attain "max_periods" distinct values on "set"
+ * then split the domain along those distinct values.
+ * Add the results (or the original if no splitting occurs)
+ * to data->res.
+ */
+static isl_stat split_periods(__isl_take isl_set *set,
+ __isl_take isl_qpolynomial *qp, void *user)
+{
+ int i;
+ isl_pw_qpolynomial *pwqp;
+ struct isl_split_periods_data *data;
+ isl_int min, max;
+ isl_size div_pos;
+ isl_stat r = isl_stat_ok;
+
+ data = (struct isl_split_periods_data *)user;
+
+ if (!set || !qp)
+ goto error;
+
+ if (qp->div->n_row == 0) {
+ pwqp = isl_pw_qpolynomial_alloc(set, qp);
+ data->res = isl_pw_qpolynomial_add_disjoint(data->res, pwqp);
+ return isl_stat_ok;
+ }
+
+ div_pos = isl_qpolynomial_domain_var_offset(qp, isl_dim_div);
+ if (div_pos < 0)
+ goto error;
+
+ isl_int_init(min);
+ isl_int_init(max);
+ for (i = 0; i < qp->div->n_row; ++i) {
+ enum isl_lp_result lp_res;
+
+ if (isl_seq_first_non_zero(qp->div->row[i] + 2 + div_pos,
+ qp->div->n_row) != -1)
+ continue;
+
+ lp_res = isl_set_solve_lp(set, 0, qp->div->row[i] + 1,
+ set->ctx->one, &min, NULL, NULL);
+ if (lp_res == isl_lp_error)
+ goto error2;
+ if (lp_res == isl_lp_unbounded || lp_res == isl_lp_empty)
+ continue;
+ isl_int_fdiv_q(min, min, qp->div->row[i][0]);
+
+ lp_res = isl_set_solve_lp(set, 1, qp->div->row[i] + 1,
+ set->ctx->one, &max, NULL, NULL);
+ if (lp_res == isl_lp_error)
+ goto error2;
+ if (lp_res == isl_lp_unbounded || lp_res == isl_lp_empty)
+ continue;
+ isl_int_fdiv_q(max, max, qp->div->row[i][0]);
+
+ isl_int_sub(max, max, min);
+ if (isl_int_cmp_si(max, data->max_periods) < 0) {
+ isl_int_add(max, max, min);
+ break;
+ }
+ }
+
+ if (i < qp->div->n_row) {
+ r = split_div(set, qp, i, min, max, data);
+ } else {
+ pwqp = isl_pw_qpolynomial_alloc(set, qp);
+ data->res = isl_pw_qpolynomial_add_disjoint(data->res, pwqp);
+ }
+
+ isl_int_clear(max);
+ isl_int_clear(min);
+
+ return r;
+error2:
+ isl_int_clear(max);
+ isl_int_clear(min);
+error:
+ isl_set_free(set);
+ isl_qpolynomial_free(qp);
+ return isl_stat_error;
+}
+
+/* If any quasi-polynomial in pwqp refers to any integer division
+ * that can only attain "max_periods" distinct values on its domain
+ * then split the domain along those distinct values.
+ */
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_split_periods(
+ __isl_take isl_pw_qpolynomial *pwqp, int max_periods)
+{
+ struct isl_split_periods_data data;
+
+ data.max_periods = max_periods;
+ data.res = isl_pw_qpolynomial_zero(isl_pw_qpolynomial_get_space(pwqp));
+
+ if (isl_pw_qpolynomial_foreach_piece(pwqp, &split_periods, &data) < 0)
+ goto error;
+
+ isl_pw_qpolynomial_free(pwqp);
+
+ return data.res;
+error:
+ isl_pw_qpolynomial_free(data.res);
+ isl_pw_qpolynomial_free(pwqp);
+ return NULL;
+}
+
+/* Construct a piecewise quasipolynomial that is constant on the given
+ * domain. In particular, it is
+ * 0 if cst == 0
+ * 1 if cst == 1
+ * infinity if cst == -1
+ *
+ * If cst == -1, then explicitly check whether the domain is empty and,
+ * if so, return 0 instead.
+ */
+static __isl_give isl_pw_qpolynomial *constant_on_domain(
+ __isl_take isl_basic_set *bset, int cst)
+{
+ isl_space *space;
+ isl_qpolynomial *qp;
+
+ if (cst < 0 && isl_basic_set_is_empty(bset) == isl_bool_true)
+ cst = 0;
+ if (!bset)
+ return NULL;
+
+ bset = isl_basic_set_params(bset);
+ space = isl_basic_set_get_space(bset);
+ if (cst < 0)
+ qp = isl_qpolynomial_infty_on_domain(space);
+ else if (cst == 0)
+ qp = isl_qpolynomial_zero_on_domain(space);
+ else
+ qp = isl_qpolynomial_one_on_domain(space);
+ return isl_pw_qpolynomial_alloc(isl_set_from_basic_set(bset), qp);
+}
+
+/* Internal data structure for multiplicative_call_factor_pw_qpolynomial.
+ * "fn" is the function that is called on each factor.
+ * "pwpq" collects the results.
+ */
+struct isl_multiplicative_call_data_pw_qpolynomial {
+ __isl_give isl_pw_qpolynomial *(*fn)(__isl_take isl_basic_set *bset);
+ isl_pw_qpolynomial *pwqp;
+};
+
+/* Call "fn" on "bset" and return the result,
+ * but first check if "bset" has any redundant constraints or
+ * implicit equality constraints.
+ * If so, there may be further opportunities for detecting factors or
+ * removing equality constraints, so recursively call
+ * the top-level isl_basic_set_multiplicative_call.
+ */
+static __isl_give isl_pw_qpolynomial *multiplicative_call_base(
+ __isl_take isl_basic_set *bset,
+ __isl_give isl_pw_qpolynomial *(*fn)(__isl_take isl_basic_set *bset))
+{
+ isl_size n1, n2, n_eq;
+
+ n1 = isl_basic_set_n_constraint(bset);
+ if (n1 < 0)
+ bset = isl_basic_set_free(bset);
+ bset = isl_basic_set_remove_redundancies(bset);
+ bset = isl_basic_set_detect_equalities(bset);
+ n2 = isl_basic_set_n_constraint(bset);
+ n_eq = isl_basic_set_n_equality(bset);
+ if (n2 < 0 || n_eq < 0)
+ bset = isl_basic_set_free(bset);
+ else if (n2 < n1 || n_eq > 0)
+ return isl_basic_set_multiplicative_call(bset, fn);
+ return fn(bset);
+}
+
+/* isl_factorizer_every_factor_basic_set callback that applies
+ * data->fn to the factor "bset" and multiplies in the result
+ * in data->pwqp.
+ */
+static isl_bool multiplicative_call_factor_pw_qpolynomial(
+ __isl_keep isl_basic_set *bset, void *user)
+{
+ struct isl_multiplicative_call_data_pw_qpolynomial *data = user;
+ isl_pw_qpolynomial *res;
+
+ bset = isl_basic_set_copy(bset);
+ res = multiplicative_call_base(bset, data->fn);
+ data->pwqp = isl_pw_qpolynomial_mul(data->pwqp, res);
+ if (!data->pwqp)
+ return isl_bool_error;
+
+ return isl_bool_true;
+}
+
+/* Factor bset, call fn on each of the factors and return the product.
+ *
+ * If no factors can be found, simply call fn on the input.
+ * Otherwise, construct the factors based on the factorizer,
+ * call fn on each factor and compute the product.
+ */
+static __isl_give isl_pw_qpolynomial *compressed_multiplicative_call(
+ __isl_take isl_basic_set *bset,
+ __isl_give isl_pw_qpolynomial *(*fn)(__isl_take isl_basic_set *bset))
+{
+ struct isl_multiplicative_call_data_pw_qpolynomial data = { fn };
+ isl_space *space;
+ isl_set *set;
+ isl_factorizer *f;
+ isl_qpolynomial *qp;
+ isl_bool every;
+
+ f = isl_basic_set_factorizer(bset);
+ if (!f)
+ goto error;
+ if (f->n_group == 0) {
+ isl_factorizer_free(f);
+ return multiplicative_call_base(bset, fn);
+ }
+
+ space = isl_basic_set_get_space(bset);
+ space = isl_space_params(space);
+ set = isl_set_universe(isl_space_copy(space));
+ qp = isl_qpolynomial_one_on_domain(space);
+ data.pwqp = isl_pw_qpolynomial_alloc(set, qp);
+
+ every = isl_factorizer_every_factor_basic_set(f,
+ &multiplicative_call_factor_pw_qpolynomial, &data);
+ if (every < 0)
+ data.pwqp = isl_pw_qpolynomial_free(data.pwqp);
+
+ isl_basic_set_free(bset);
+ isl_factorizer_free(f);
+
+ return data.pwqp;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Factor bset, call fn on each of the factors and return the product.
+ * The function is assumed to evaluate to zero on empty domains,
+ * to one on zero-dimensional domains and to infinity on unbounded domains
+ * and will not be called explicitly on zero-dimensional or unbounded domains.
+ *
+ * We first check for some special cases and remove all equalities.
+ * Then we hand over control to compressed_multiplicative_call.
+ */
+__isl_give isl_pw_qpolynomial *isl_basic_set_multiplicative_call(
+ __isl_take isl_basic_set *bset,
+ __isl_give isl_pw_qpolynomial *(*fn)(__isl_take isl_basic_set *bset))
+{
+ isl_bool bounded;
+ isl_size dim;
+ isl_morph *morph;
+ isl_pw_qpolynomial *pwqp;
+
+ if (!bset)
+ return NULL;
+
+ if (isl_basic_set_plain_is_empty(bset))
+ return constant_on_domain(bset, 0);
+
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ if (dim < 0)
+ goto error;
+ if (dim == 0)
+ return constant_on_domain(bset, 1);
+
+ bounded = isl_basic_set_is_bounded(bset);
+ if (bounded < 0)
+ goto error;
+ if (!bounded)
+ return constant_on_domain(bset, -1);
+
+ if (bset->n_eq == 0)
+ return compressed_multiplicative_call(bset, fn);
+
+ morph = isl_basic_set_full_compression(bset);
+ bset = isl_morph_basic_set(isl_morph_copy(morph), bset);
+
+ pwqp = compressed_multiplicative_call(bset, fn);
+
+ morph = isl_morph_dom_params(morph);
+ morph = isl_morph_ran_params(morph);
+ morph = isl_morph_inverse(morph);
+
+ pwqp = isl_pw_qpolynomial_morph_domain(pwqp, morph);
+
+ return pwqp;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Drop all floors in "qp", turning each integer division [a/m] into
+ * a rational division a/m. If "down" is set, then the integer division
+ * is replaced by (a-(m-1))/m instead.
+ */
+static __isl_give isl_qpolynomial *qp_drop_floors(
+ __isl_take isl_qpolynomial *qp, int down)
+{
+ int i;
+ isl_poly *s;
+
+ if (!qp)
+ return NULL;
+ if (qp->div->n_row == 0)
+ return qp;
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ return NULL;
+
+ for (i = qp->div->n_row - 1; i >= 0; --i) {
+ if (down) {
+ isl_int_sub(qp->div->row[i][1],
+ qp->div->row[i][1], qp->div->row[i][0]);
+ isl_int_add_ui(qp->div->row[i][1],
+ qp->div->row[i][1], 1);
+ }
+ s = isl_poly_from_affine(qp->dim->ctx, qp->div->row[i] + 1,
+ qp->div->row[i][0], qp->div->n_col - 1);
+ qp = substitute_div(qp, i, s);
+ if (!qp)
+ return NULL;
+ }
+
+ return qp;
+}
+
+/* Drop all floors in "pwqp", turning each integer division [a/m] into
+ * a rational division a/m.
+ */
+static __isl_give isl_pw_qpolynomial *pwqp_drop_floors(
+ __isl_take isl_pw_qpolynomial *pwqp)
+{
+ int i;
+
+ if (!pwqp)
+ return NULL;
+
+ if (isl_pw_qpolynomial_is_zero(pwqp))
+ return pwqp;
+
+ pwqp = isl_pw_qpolynomial_cow(pwqp);
+ if (!pwqp)
+ return NULL;
+
+ for (i = 0; i < pwqp->n; ++i) {
+ pwqp->p[i].qp = qp_drop_floors(pwqp->p[i].qp, 0);
+ if (!pwqp->p[i].qp)
+ goto error;
+ }
+
+ return pwqp;
+error:
+ isl_pw_qpolynomial_free(pwqp);
+ return NULL;
+}
+
+/* Adjust all the integer divisions in "qp" such that they are at least
+ * one over the given orthant (identified by "signs"). This ensures
+ * that they will still be non-negative even after subtracting (m-1)/m.
+ *
+ * In particular, f is replaced by f' + v, changing f = [a/m]
+ * to f' = [(a - m v)/m].
+ * If the constant term k in a is smaller than m,
+ * the constant term of v is set to floor(k/m) - 1.
+ * For any other term, if the coefficient c and the variable x have
+ * the same sign, then no changes are needed.
+ * Otherwise, if the variable is positive (and c is negative),
+ * then the coefficient of x in v is set to floor(c/m).
+ * If the variable is negative (and c is positive),
+ * then the coefficient of x in v is set to ceil(c/m).
+ */
+static __isl_give isl_qpolynomial *make_divs_pos(__isl_take isl_qpolynomial *qp,
+ int *signs)
+{
+ int i, j;
+ isl_size div_pos;
+ isl_vec *v = NULL;
+ isl_poly *s;
+
+ qp = isl_qpolynomial_cow(qp);
+ div_pos = isl_qpolynomial_domain_var_offset(qp, isl_dim_div);
+ if (div_pos < 0)
+ return isl_qpolynomial_free(qp);
+ qp->div = isl_mat_cow(qp->div);
+ if (!qp->div)
+ goto error;
+
+ v = isl_vec_alloc(qp->div->ctx, qp->div->n_col - 1);
+
+ for (i = 0; i < qp->div->n_row; ++i) {
+ isl_int *row = qp->div->row[i];
+ v = isl_vec_clr(v);
+ if (!v)
+ goto error;
+ if (isl_int_lt(row[1], row[0])) {
+ isl_int_fdiv_q(v->el[0], row[1], row[0]);
+ isl_int_sub_ui(v->el[0], v->el[0], 1);
+ isl_int_submul(row[1], row[0], v->el[0]);
+ }
+ for (j = 0; j < div_pos; ++j) {
+ if (isl_int_sgn(row[2 + j]) * signs[j] >= 0)
+ continue;
+ if (signs[j] < 0)
+ isl_int_cdiv_q(v->el[1 + j], row[2 + j], row[0]);
+ else
+ isl_int_fdiv_q(v->el[1 + j], row[2 + j], row[0]);
+ isl_int_submul(row[2 + j], row[0], v->el[1 + j]);
+ }
+ for (j = 0; j < i; ++j) {
+ if (isl_int_sgn(row[2 + div_pos + j]) >= 0)
+ continue;
+ isl_int_fdiv_q(v->el[1 + div_pos + j],
+ row[2 + div_pos + j], row[0]);
+ isl_int_submul(row[2 + div_pos + j],
+ row[0], v->el[1 + div_pos + j]);
+ }
+ for (j = i + 1; j < qp->div->n_row; ++j) {
+ if (isl_int_is_zero(qp->div->row[j][2 + div_pos + i]))
+ continue;
+ isl_seq_combine(qp->div->row[j] + 1,
+ qp->div->ctx->one, qp->div->row[j] + 1,
+ qp->div->row[j][2 + div_pos + i], v->el,
+ v->size);
+ }
+ isl_int_set_si(v->el[1 + div_pos + i], 1);
+ s = isl_poly_from_affine(qp->dim->ctx, v->el,
+ qp->div->ctx->one, v->size);
+ qp->poly = isl_poly_subs(qp->poly, div_pos + i, 1, &s);
+ isl_poly_free(s);
+ if (!qp->poly)
+ goto error;
+ }
+
+ isl_vec_free(v);
+ return qp;
+error:
+ isl_vec_free(v);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+struct isl_to_poly_data {
+ int sign;
+ isl_pw_qpolynomial *res;
+ isl_qpolynomial *qp;
+};
+
+/* Appoximate data->qp by a polynomial on the orthant identified by "signs".
+ * We first make all integer divisions positive and then split the
+ * quasipolynomials into terms with sign data->sign (the direction
+ * of the requested approximation) and terms with the opposite sign.
+ * In the first set of terms, each integer division [a/m] is
+ * overapproximated by a/m, while in the second it is underapproximated
+ * by (a-(m-1))/m.
+ */
+static isl_stat to_polynomial_on_orthant(__isl_take isl_set *orthant,
+ int *signs, void *user)
+{
+ struct isl_to_poly_data *data = user;
+ isl_pw_qpolynomial *t;
+ isl_qpolynomial *qp, *up, *down;
+
+ qp = isl_qpolynomial_copy(data->qp);
+ qp = make_divs_pos(qp, signs);
+
+ up = isl_qpolynomial_terms_of_sign(qp, signs, data->sign);
+ up = qp_drop_floors(up, 0);
+ down = isl_qpolynomial_terms_of_sign(qp, signs, -data->sign);
+ down = qp_drop_floors(down, 1);
+
+ isl_qpolynomial_free(qp);
+ qp = isl_qpolynomial_add(up, down);
+
+ t = isl_pw_qpolynomial_alloc(orthant, qp);
+ data->res = isl_pw_qpolynomial_add_disjoint(data->res, t);
+
+ return isl_stat_ok;
+}
+
+/* Approximate each quasipolynomial by a polynomial. If "sign" is positive,
+ * the polynomial will be an overapproximation. If "sign" is negative,
+ * it will be an underapproximation. If "sign" is zero, the approximation
+ * will lie somewhere in between.
+ *
+ * In particular, is sign == 0, we simply drop the floors, turning
+ * the integer divisions into rational divisions.
+ * Otherwise, we split the domains into orthants, make all integer divisions
+ * positive and then approximate each [a/m] by either a/m or (a-(m-1))/m,
+ * depending on the requested sign and the sign of the term in which
+ * the integer division appears.
+ */
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_to_polynomial(
+ __isl_take isl_pw_qpolynomial *pwqp, int sign)
+{
+ int i;
+ struct isl_to_poly_data data;
+
+ if (sign == 0)
+ return pwqp_drop_floors(pwqp);
+
+ if (!pwqp)
+ return NULL;
+
+ data.sign = sign;
+ data.res = isl_pw_qpolynomial_zero(isl_pw_qpolynomial_get_space(pwqp));
+
+ for (i = 0; i < pwqp->n; ++i) {
+ if (pwqp->p[i].qp->div->n_row == 0) {
+ isl_pw_qpolynomial *t;
+ t = isl_pw_qpolynomial_alloc(
+ isl_set_copy(pwqp->p[i].set),
+ isl_qpolynomial_copy(pwqp->p[i].qp));
+ data.res = isl_pw_qpolynomial_add_disjoint(data.res, t);
+ continue;
+ }
+ data.qp = pwqp->p[i].qp;
+ if (isl_set_foreach_orthant(pwqp->p[i].set,
+ &to_polynomial_on_orthant, &data) < 0)
+ goto error;
+ }
+
+ isl_pw_qpolynomial_free(pwqp);
+
+ return data.res;
+error:
+ isl_pw_qpolynomial_free(pwqp);
+ isl_pw_qpolynomial_free(data.res);
+ return NULL;
+}
+
+static __isl_give isl_pw_qpolynomial *poly_entry(
+ __isl_take isl_pw_qpolynomial *pwqp, void *user)
+{
+ int *sign = user;
+
+ return isl_pw_qpolynomial_to_polynomial(pwqp, *sign);
+}
+
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_to_polynomial(
+ __isl_take isl_union_pw_qpolynomial *upwqp, int sign)
+{
+ return isl_union_pw_qpolynomial_transform_inplace(upwqp,
+ &poly_entry, &sign);
+}
+
+__isl_give isl_basic_map *isl_basic_map_from_qpolynomial(
+ __isl_take isl_qpolynomial *qp)
+{
+ int i, k;
+ isl_space *space;
+ isl_vec *aff = NULL;
+ isl_basic_map *bmap = NULL;
+ isl_bool is_affine;
+ unsigned pos;
+ unsigned n_div;
+
+ if (!qp)
+ return NULL;
+ is_affine = isl_poly_is_affine(qp->poly);
+ if (is_affine < 0)
+ goto error;
+ if (!is_affine)
+ isl_die(qp->dim->ctx, isl_error_invalid,
+ "input quasi-polynomial not affine", goto error);
+ aff = isl_qpolynomial_extract_affine(qp);
+ if (!aff)
+ goto error;
+ space = isl_qpolynomial_get_space(qp);
+ pos = 1 + isl_space_offset(space, isl_dim_out);
+ n_div = qp->div->n_row;
+ bmap = isl_basic_map_alloc_space(space, n_div, 1, 2 * n_div);
+
+ for (i = 0; i < n_div; ++i) {
+ k = isl_basic_map_alloc_div(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bmap->div[k], qp->div->row[i], qp->div->n_col);
+ isl_int_set_si(bmap->div[k][qp->div->n_col], 0);
+ bmap = isl_basic_map_add_div_constraints(bmap, k);
+ }
+ k = isl_basic_map_alloc_equality(bmap);
+ if (k < 0)
+ goto error;
+ isl_int_neg(bmap->eq[k][pos], aff->el[0]);
+ isl_seq_cpy(bmap->eq[k], aff->el + 1, pos);
+ isl_seq_cpy(bmap->eq[k] + pos + 1, aff->el + 1 + pos, n_div);
+
+ isl_vec_free(aff);
+ isl_qpolynomial_free(qp);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+error:
+ isl_vec_free(aff);
+ isl_qpolynomial_free(qp);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_polynomial_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_polynomial_private.h
new file mode 100644
index 00000000000..acab0783859
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_polynomial_private.h
@@ -0,0 +1,299 @@
+#include <stdio.h>
+#include <isl_int.h>
+#include <isl/map.h>
+#include <isl/mat.h>
+#include <isl_morph.h>
+#include <isl/polynomial.h>
+#include <isl_reordering.h>
+#include "isl_list_private.h"
+
+struct isl_poly {
+ int ref;
+ struct isl_ctx *ctx;
+
+ int var;
+};
+typedef struct isl_poly isl_poly;
+
+struct isl_poly_cst {
+ struct isl_poly poly;
+ isl_int n;
+ isl_int d;
+};
+typedef struct isl_poly_cst isl_poly_cst;
+
+struct isl_poly_rec {
+ struct isl_poly poly;
+ int n;
+
+ size_t size;
+ isl_poly *p[];
+};
+typedef struct isl_poly_rec isl_poly_rec;
+
+/* dim represents the domain space.
+ */
+struct isl_qpolynomial {
+ int ref;
+
+ isl_space *dim;
+ struct isl_mat *div;
+ isl_poly *poly;
+};
+
+#undef EL
+#define EL isl_qpolynomial
+
+#include <isl_list_templ.h>
+
+struct isl_term {
+ int ref;
+
+ isl_int n;
+ isl_int d;
+
+ isl_space *dim;
+ struct isl_mat *div;
+
+ int pow[1];
+};
+
+struct isl_pw_qpolynomial_piece {
+ struct isl_set *set;
+ struct isl_qpolynomial *qp;
+};
+
+struct isl_pw_qpolynomial {
+ int ref;
+
+ isl_space *dim;
+
+ int n;
+
+ size_t size;
+ struct isl_pw_qpolynomial_piece p[1];
+};
+
+#undef PW
+#define PW isl_pw_qpolynomial
+
+#include <isl_pw_templ.h>
+
+#undef EL
+#define EL isl_pw_qpolynomial
+
+#include <isl_list_templ.h>
+
+/* dim represents the domain space.
+ */
+struct isl_qpolynomial_fold {
+ int ref;
+
+ enum isl_fold type;
+ isl_space *dim;
+
+ isl_qpolynomial_list *list;
+};
+
+struct isl_pw_qpolynomial_fold_piece {
+ struct isl_set *set;
+ struct isl_qpolynomial_fold *fold;
+};
+
+struct isl_pw_qpolynomial_fold {
+ int ref;
+
+ enum isl_fold type;
+ isl_space *dim;
+
+ int n;
+
+ size_t size;
+ struct isl_pw_qpolynomial_fold_piece p[1];
+};
+
+#undef PW
+#define PW isl_pw_qpolynomial_fold
+
+#include <isl_pw_templ.h>
+
+#undef EL
+#define EL isl_pw_qpolynomial_fold
+
+#include <isl_list_templ.h>
+
+void isl_term_get_num(__isl_keep isl_term *term, isl_int *n);
+
+__isl_give isl_poly *isl_poly_zero(struct isl_ctx *ctx);
+__isl_give isl_poly *isl_poly_copy(__isl_keep isl_poly *poly);
+__isl_give isl_poly *isl_poly_cow(__isl_take isl_poly *poly);
+__isl_give isl_poly *isl_poly_dup(__isl_keep isl_poly *poly);
+__isl_null isl_poly *isl_poly_free(__isl_take isl_poly *poly);
+__isl_give struct isl_poly *isl_poly_mul(__isl_take struct isl_poly *poly1,
+ __isl_take struct isl_poly *poly2);
+
+isl_bool isl_poly_is_cst(__isl_keep isl_poly *poly);
+isl_bool isl_poly_is_zero(__isl_keep isl_poly *poly);
+isl_bool isl_poly_is_one(__isl_keep isl_poly *poly);
+isl_bool isl_poly_is_negone(__isl_keep isl_poly *poly);
+__isl_keep isl_poly_cst *isl_poly_as_cst(__isl_keep isl_poly *poly);
+__isl_keep isl_poly_rec *isl_poly_as_rec(__isl_keep isl_poly *poly);
+
+__isl_give isl_poly *isl_poly_sum(__isl_take isl_poly *poly1,
+ __isl_take isl_poly *poly2);
+__isl_give struct isl_poly *isl_poly_mul_isl_int(
+ __isl_take isl_poly *poly, isl_int v);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_alloc(__isl_take isl_space *space,
+ unsigned n_div, __isl_take isl_poly *poly);
+__isl_give isl_qpolynomial *isl_qpolynomial_cow(__isl_take isl_qpolynomial *qp);
+__isl_give isl_qpolynomial *isl_qpolynomial_dup(__isl_keep isl_qpolynomial *qp);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_cst_on_domain(
+ __isl_take isl_space *domain,
+ isl_int v);
+__isl_give isl_qpolynomial *isl_qpolynomial_rat_cst_on_domain(
+ __isl_take isl_space *domain, const isl_int n, const isl_int d);
+__isl_give isl_qpolynomial *isl_qpolynomial_var_pow_on_domain(
+ __isl_take isl_space *domain,
+ int pos, int power);
+isl_bool isl_qpolynomial_is_one(__isl_keep isl_qpolynomial *qp);
+isl_bool isl_qpolynomial_is_affine(__isl_keep isl_qpolynomial *qp);
+isl_bool isl_qpolynomial_is_cst(__isl_keep isl_qpolynomial *qp,
+ isl_int *n, isl_int *d);
+
+unsigned isl_qpolynomial_domain_offset(__isl_keep isl_qpolynomial *qp,
+ enum isl_dim_type type);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_add_on_domain(
+ __isl_keep isl_set *dom,
+ __isl_take isl_qpolynomial *qp1,
+ __isl_take isl_qpolynomial *qp2);
+
+int isl_qpolynomial_plain_cmp(__isl_keep isl_qpolynomial *qp1,
+ __isl_keep isl_qpolynomial *qp2);
+
+int isl_qpolynomial_degree(__isl_keep isl_qpolynomial *poly);
+__isl_give isl_qpolynomial *isl_qpolynomial_coeff(
+ __isl_keep isl_qpolynomial *poly,
+ enum isl_dim_type type, unsigned pos, int deg);
+
+__isl_give isl_vec *isl_qpolynomial_extract_affine(
+ __isl_keep isl_qpolynomial *qp);
+__isl_give isl_qpolynomial *isl_qpolynomial_from_affine(
+ __isl_take isl_space *space, isl_int *f, isl_int denom);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_cow(
+ __isl_take isl_pw_qpolynomial *pwqp);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_add_piece(
+ __isl_take isl_pw_qpolynomial *pwqp,
+ __isl_take isl_set *set, __isl_take isl_qpolynomial *qp);
+int isl_pw_qpolynomial_is_one(__isl_keep isl_pw_qpolynomial *pwqp);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_project_out(
+ __isl_take isl_pw_qpolynomial *pwqp,
+ enum isl_dim_type type, unsigned first, unsigned n);
+
+__isl_give isl_val *isl_qpolynomial_opt_on_domain(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_set *set, int max);
+
+enum isl_fold isl_fold_type_negate(enum isl_fold type);
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_cow(
+ __isl_take isl_qpolynomial_fold *fold);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_dup(
+ __isl_keep isl_qpolynomial_fold *fold);
+
+__isl_keep isl_qpolynomial_list *isl_qpolynomial_fold_peek_list(
+ __isl_keep isl_qpolynomial_fold *fold);
+
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_cow(
+ __isl_take isl_pw_qpolynomial_fold *pwf);
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_add_on_domain(
+ __isl_keep isl_set *set,
+ __isl_take isl_qpolynomial_fold *fold1,
+ __isl_take isl_qpolynomial_fold *fold2);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_fold_on_domain(
+ __isl_keep isl_set *set,
+ __isl_take isl_qpolynomial_fold *fold1,
+ __isl_take isl_qpolynomial_fold *fold2);
+
+int isl_qpolynomial_fold_plain_cmp(__isl_keep isl_qpolynomial_fold *fold1,
+ __isl_keep isl_qpolynomial_fold *fold2);
+
+__isl_give isl_val *isl_qpolynomial_fold_opt_on_domain(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *set, int max);
+
+isl_bool isl_pw_qpolynomial_fold_covers(
+ __isl_keep isl_pw_qpolynomial_fold *pwf1,
+ __isl_keep isl_pw_qpolynomial_fold *pwf2);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_morph_domain(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_morph *morph);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_morph_domain(
+ __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_morph *morph);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_morph_domain(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_morph *morph);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_morph_domain(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_morph *morph);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_lift(__isl_take isl_qpolynomial *qp,
+ __isl_take isl_space *space);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_lift(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *space);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_substitute_equalities(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_basic_set *eq);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_substitute_equalities(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_basic_set *eq);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_gist(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_set *context);
+
+__isl_give isl_qpolynomial *isl_qpolynomial_realign_domain(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_reordering *r);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_realign_domain(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_reordering *r);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_realign_domain(
+ __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_reordering *r);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_realign_domain(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_reordering *r);
+
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_reset_space(
+ __isl_take isl_pw_qpolynomial *pwqp, __isl_take isl_space *space);
+__isl_give isl_qpolynomial *isl_qpolynomial_reset_domain_space(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_space *space);
+__isl_give isl_qpolynomial *isl_qpolynomial_reset_space_and_domain(
+ __isl_take isl_qpolynomial *qp, __isl_take isl_space *space,
+ __isl_take isl_space *domain);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_reset_domain_space(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *space);
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_reset_space_and_domain(
+ __isl_take isl_qpolynomial_fold *fold, __isl_take isl_space *space,
+ __isl_take isl_space *domain);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_reset_domain_space(
+ __isl_take isl_pw_qpolynomial_fold *pwf, __isl_take isl_space *space);
+
+__isl_give isl_val *isl_qpolynomial_get_den(__isl_keep isl_qpolynomial *qp);
+__isl_give isl_qpolynomial *isl_qpolynomial_add_isl_int(
+ __isl_take isl_qpolynomial *qp, isl_int v);
+__isl_give isl_qpolynomial *isl_qpolynomial_mul_isl_int(
+ __isl_take isl_qpolynomial *qp, isl_int v);
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_mul_isl_int(
+ __isl_take isl_pw_qpolynomial *pwqp, isl_int v);
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_scale(
+ __isl_take isl_qpolynomial_fold *fold, isl_int v);
+
+__isl_give isl_qpolynomial_fold *isl_qpolynomial_fold_mul_isl_int(
+ __isl_take isl_qpolynomial_fold *fold, isl_int v);
+__isl_give isl_pw_qpolynomial_fold *isl_pw_qpolynomial_fold_mul_isl_int(
+ __isl_take isl_pw_qpolynomial_fold *pwf, isl_int v);
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_mul_isl_int(
+ __isl_take isl_union_pw_qpolynomial *upwqp, isl_int v);
+__isl_give isl_union_pw_qpolynomial_fold *
+isl_union_pw_qpolynomial_fold_mul_isl_int(
+ __isl_take isl_union_pw_qpolynomial_fold *upwf, isl_int v);
+
+ISL_DECLARE_LIST_FN_PRIVATE(qpolynomial)
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_power_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_power_templ.c
new file mode 100644
index 00000000000..65253bdcb96
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_power_templ.c
@@ -0,0 +1,81 @@
+#include <isl_val_private.h>
+
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Compute the given non-zero power of "map" and return the result.
+ * If the exponent "exp" is negative, then the -exp th power of the inverse
+ * relation is computed.
+ */
+__isl_give TYPE *FN(TYPE,fixed_power)(__isl_take TYPE *map, isl_int exp)
+{
+ isl_ctx *ctx;
+ TYPE *res = NULL;
+ isl_int r;
+
+ if (!map)
+ return NULL;
+
+ ctx = FN(TYPE,get_ctx)(map);
+ if (isl_int_is_zero(exp))
+ isl_die(ctx, isl_error_invalid,
+ "expecting non-zero exponent", goto error);
+
+ if (isl_int_is_neg(exp)) {
+ isl_int_neg(exp, exp);
+ map = FN(TYPE,reverse)(map);
+ return FN(TYPE,fixed_power)(map, exp);
+ }
+
+ isl_int_init(r);
+ for (;;) {
+ isl_int_fdiv_r(r, exp, ctx->two);
+
+ if (!isl_int_is_zero(r)) {
+ if (!res)
+ res = FN(TYPE,copy)(map);
+ else {
+ res = FN(TYPE,apply_range)(res,
+ FN(TYPE,copy)(map));
+ res = FN(TYPE,coalesce)(res);
+ }
+ if (!res)
+ break;
+ }
+
+ isl_int_fdiv_q(exp, exp, ctx->two);
+ if (isl_int_is_zero(exp))
+ break;
+
+ map = FN(TYPE,apply_range)(map, FN(TYPE,copy)(map));
+ map = FN(TYPE,coalesce)(map);
+ }
+ isl_int_clear(r);
+
+ FN(TYPE,free)(map);
+ return res;
+error:
+ FN(TYPE,free)(map);
+ return NULL;
+}
+
+/* Compute the given non-zero power of "map" and return the result.
+ * If the exponent "exp" is negative, then the -exp th power of the inverse
+ * relation is computed.
+ */
+__isl_give TYPE *FN(TYPE,fixed_power_val)(__isl_take TYPE *map,
+ __isl_take isl_val *exp)
+{
+ if (!map || !exp)
+ goto error;
+ if (!isl_val_is_int(exp))
+ isl_die(FN(TYPE,get_ctx)(map), isl_error_invalid,
+ "expecting integer exponent", goto error);
+ map = FN(TYPE,fixed_power)(map, exp->n);
+ isl_val_free(exp);
+ return map;
+error:
+ FN(TYPE,free)(map);
+ isl_val_free(exp);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_printer.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_printer.c
new file mode 100644
index 00000000000..f58a8a7aaa7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_printer.c
@@ -0,0 +1,851 @@
+#include <string.h>
+#include <isl_int.h>
+#include <isl/id.h>
+#include <isl/id_to_id.h>
+#include <isl_printer_private.h>
+
+static __isl_give isl_printer *file_start_line(__isl_take isl_printer *p)
+{
+ fprintf(p->file, "%s%*s%s", p->indent_prefix ? p->indent_prefix : "",
+ p->indent, "", p->prefix ? p->prefix : "");
+ return p;
+}
+
+static __isl_give isl_printer *file_end_line(__isl_take isl_printer *p)
+{
+ fprintf(p->file, "%s\n", p->suffix ? p->suffix : "");
+ return p;
+}
+
+static __isl_give isl_printer *file_flush(__isl_take isl_printer *p)
+{
+ fflush(p->file);
+ return p;
+}
+
+static __isl_give isl_printer *file_print_str(__isl_take isl_printer *p,
+ const char *s)
+{
+ fprintf(p->file, "%s", s);
+ return p;
+}
+
+static __isl_give isl_printer *file_print_double(__isl_take isl_printer *p,
+ double d)
+{
+ fprintf(p->file, "%g", d);
+ return p;
+}
+
+static __isl_give isl_printer *file_print_int(__isl_take isl_printer *p, int i)
+{
+ fprintf(p->file, "%d", i);
+ return p;
+}
+
+static __isl_give isl_printer *file_print_isl_int(__isl_take isl_printer *p, isl_int i)
+{
+ isl_int_print(p->file, i, p->width);
+ return p;
+}
+
+static int grow_buf(__isl_keep isl_printer *p, int extra)
+{
+ int new_size;
+ char *new_buf;
+
+ if (p->buf_size == 0)
+ return -1;
+
+ new_size = ((p->buf_n + extra + 1) * 3) / 2;
+ new_buf = isl_realloc_array(p->ctx, p->buf, char, new_size);
+ if (!new_buf) {
+ p->buf_size = 0;
+ return -1;
+ }
+ p->buf = new_buf;
+ p->buf_size = new_size;
+
+ return 0;
+}
+
+static __isl_give isl_printer *str_print(__isl_take isl_printer *p,
+ const char *s, int len)
+{
+ if (p->buf_n + len + 1 >= p->buf_size && grow_buf(p, len))
+ goto error;
+ memcpy(p->buf + p->buf_n, s, len);
+ p->buf_n += len;
+
+ p->buf[p->buf_n] = '\0';
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *str_print_indent(__isl_take isl_printer *p,
+ int indent)
+{
+ int i;
+
+ if (p->buf_n + indent + 1 >= p->buf_size && grow_buf(p, indent))
+ goto error;
+ for (i = 0; i < indent; ++i)
+ p->buf[p->buf_n++] = ' ';
+ p->buf[p->buf_n] = '\0';
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *str_start_line(__isl_take isl_printer *p)
+{
+ if (p->indent_prefix)
+ p = str_print(p, p->indent_prefix, strlen(p->indent_prefix));
+ p = str_print_indent(p, p->indent);
+ if (p->prefix)
+ p = str_print(p, p->prefix, strlen(p->prefix));
+ return p;
+}
+
+static __isl_give isl_printer *str_end_line(__isl_take isl_printer *p)
+{
+ if (p->suffix)
+ p = str_print(p, p->suffix, strlen(p->suffix));
+ p = str_print(p, "\n", strlen("\n"));
+ return p;
+}
+
+static __isl_give isl_printer *str_flush(__isl_take isl_printer *p)
+{
+ p->buf_n = 0;
+ p->buf[p->buf_n] = '\0';
+ return p;
+}
+
+static __isl_give isl_printer *str_print_str(__isl_take isl_printer *p,
+ const char *s)
+{
+ return str_print(p, s, strlen(s));
+}
+
+static __isl_give isl_printer *str_print_double(__isl_take isl_printer *p,
+ double d)
+{
+ int left = p->buf_size - p->buf_n;
+ int need = snprintf(p->buf + p->buf_n, left, "%g", d);
+ if (need >= left) {
+ if (grow_buf(p, need))
+ goto error;
+ left = p->buf_size - p->buf_n;
+ need = snprintf(p->buf + p->buf_n, left, "%g", d);
+ }
+ p->buf_n += need;
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *str_print_int(__isl_take isl_printer *p, int i)
+{
+ int left = p->buf_size - p->buf_n;
+ int need = snprintf(p->buf + p->buf_n, left, "%d", i);
+ if (need >= left) {
+ if (grow_buf(p, need))
+ goto error;
+ left = p->buf_size - p->buf_n;
+ need = snprintf(p->buf + p->buf_n, left, "%d", i);
+ }
+ p->buf_n += need;
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+static __isl_give isl_printer *str_print_isl_int(__isl_take isl_printer *p,
+ isl_int i)
+{
+ char *s;
+ int len;
+
+ s = isl_int_get_str(i);
+ len = strlen(s);
+ if (len < p->width)
+ p = str_print_indent(p, p->width - len);
+ p = str_print(p, s, len);
+ isl_int_free_str(s);
+ return p;
+}
+
+struct isl_printer_ops {
+ __isl_give isl_printer *(*start_line)(__isl_take isl_printer *p);
+ __isl_give isl_printer *(*end_line)(__isl_take isl_printer *p);
+ __isl_give isl_printer *(*print_double)(__isl_take isl_printer *p,
+ double d);
+ __isl_give isl_printer *(*print_int)(__isl_take isl_printer *p, int i);
+ __isl_give isl_printer *(*print_isl_int)(__isl_take isl_printer *p,
+ isl_int i);
+ __isl_give isl_printer *(*print_str)(__isl_take isl_printer *p,
+ const char *s);
+ __isl_give isl_printer *(*flush)(__isl_take isl_printer *p);
+};
+
+static struct isl_printer_ops file_ops = {
+ file_start_line,
+ file_end_line,
+ file_print_double,
+ file_print_int,
+ file_print_isl_int,
+ file_print_str,
+ file_flush
+};
+
+static struct isl_printer_ops str_ops = {
+ str_start_line,
+ str_end_line,
+ str_print_double,
+ str_print_int,
+ str_print_isl_int,
+ str_print_str,
+ str_flush
+};
+
+__isl_give isl_printer *isl_printer_to_file(isl_ctx *ctx, FILE *file)
+{
+ struct isl_printer *p = isl_calloc_type(ctx, struct isl_printer);
+ if (!p)
+ return NULL;
+ p->ctx = ctx;
+ isl_ctx_ref(p->ctx);
+ p->ops = &file_ops;
+ p->file = file;
+ p->buf = NULL;
+ p->buf_n = 0;
+ p->buf_size = 0;
+ p->indent = 0;
+ p->output_format = ISL_FORMAT_ISL;
+ p->indent_prefix = NULL;
+ p->prefix = NULL;
+ p->suffix = NULL;
+ p->width = 0;
+ p->yaml_style = ISL_YAML_STYLE_FLOW;
+
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_to_str(isl_ctx *ctx)
+{
+ struct isl_printer *p = isl_calloc_type(ctx, struct isl_printer);
+ if (!p)
+ return NULL;
+ p->ctx = ctx;
+ isl_ctx_ref(p->ctx);
+ p->ops = &str_ops;
+ p->file = NULL;
+ p->buf = isl_alloc_array(ctx, char, 256);
+ if (!p->buf)
+ goto error;
+ p->buf_n = 0;
+ p->buf[0] = '\0';
+ p->buf_size = 256;
+ p->indent = 0;
+ p->output_format = ISL_FORMAT_ISL;
+ p->indent_prefix = NULL;
+ p->prefix = NULL;
+ p->suffix = NULL;
+ p->width = 0;
+ p->yaml_style = ISL_YAML_STYLE_FLOW;
+
+ return p;
+error:
+ isl_printer_free(p);
+ return NULL;
+}
+
+__isl_null isl_printer *isl_printer_free(__isl_take isl_printer *p)
+{
+ if (!p)
+ return NULL;
+ free(p->buf);
+ free(p->indent_prefix);
+ free(p->prefix);
+ free(p->suffix);
+ free(p->yaml_state);
+ isl_id_to_id_free(p->notes);
+ isl_ctx_deref(p->ctx);
+ free(p);
+
+ return NULL;
+}
+
+isl_ctx *isl_printer_get_ctx(__isl_keep isl_printer *printer)
+{
+ return printer ? printer->ctx : NULL;
+}
+
+FILE *isl_printer_get_file(__isl_keep isl_printer *printer)
+{
+ if (!printer)
+ return NULL;
+ if (!printer->file)
+ isl_die(isl_printer_get_ctx(printer), isl_error_invalid,
+ "not a file printer", return NULL);
+ return printer->file;
+}
+
+__isl_give isl_printer *isl_printer_set_isl_int_width(__isl_take isl_printer *p,
+ int width)
+{
+ if (!p)
+ return NULL;
+
+ p->width = width;
+
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_set_indent(__isl_take isl_printer *p,
+ int indent)
+{
+ if (!p)
+ return NULL;
+
+ p->indent = indent;
+
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_indent(__isl_take isl_printer *p,
+ int indent)
+{
+ if (!p)
+ return NULL;
+
+ p->indent += indent;
+ if (p->indent < 0)
+ p->indent = 0;
+
+ return p;
+}
+
+/* Replace the indent prefix of "p" by "prefix".
+ */
+__isl_give isl_printer *isl_printer_set_indent_prefix(__isl_take isl_printer *p,
+ const char *prefix)
+{
+ if (!p)
+ return NULL;
+
+ free(p->indent_prefix);
+ p->indent_prefix = prefix ? strdup(prefix) : NULL;
+
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_set_prefix(__isl_take isl_printer *p,
+ const char *prefix)
+{
+ if (!p)
+ return NULL;
+
+ free(p->prefix);
+ p->prefix = prefix ? strdup(prefix) : NULL;
+
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_set_suffix(__isl_take isl_printer *p,
+ const char *suffix)
+{
+ if (!p)
+ return NULL;
+
+ free(p->suffix);
+ p->suffix = suffix ? strdup(suffix) : NULL;
+
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_set_output_format(__isl_take isl_printer *p,
+ int output_format)
+{
+ if (!p)
+ return NULL;
+
+ p->output_format = output_format;
+
+ return p;
+}
+
+int isl_printer_get_output_format(__isl_keep isl_printer *p)
+{
+ if (!p)
+ return -1;
+ return p->output_format;
+}
+
+/* Does "p" have a note with identifier "id"?
+ */
+isl_bool isl_printer_has_note(__isl_keep isl_printer *p,
+ __isl_keep isl_id *id)
+{
+ if (!p || !id)
+ return isl_bool_error;
+ if (!p->notes)
+ return isl_bool_false;
+ return isl_id_to_id_has(p->notes, id);
+}
+
+/* Retrieve the note identified by "id" from "p".
+ * The note is assumed to exist.
+ */
+__isl_give isl_id *isl_printer_get_note(__isl_keep isl_printer *p,
+ __isl_take isl_id *id)
+{
+ isl_bool has_note;
+
+ has_note = isl_printer_has_note(p, id);
+ if (has_note < 0)
+ goto error;
+ if (!has_note)
+ isl_die(isl_printer_get_ctx(p), isl_error_invalid,
+ "no such note", goto error);
+
+ return isl_id_to_id_get(p->notes, id);
+error:
+ isl_id_free(id);
+ return NULL;
+}
+
+/* Associate "note" to the identifier "id" in "p",
+ * replacing the previous note associated to the identifier, if any.
+ */
+__isl_give isl_printer *isl_printer_set_note(__isl_take isl_printer *p,
+ __isl_take isl_id *id, __isl_take isl_id *note)
+{
+ if (!p || !id || !note)
+ goto error;
+ if (!p->notes) {
+ p->notes = isl_id_to_id_alloc(isl_printer_get_ctx(p), 1);
+ if (!p->notes)
+ goto error;
+ }
+ p->notes = isl_id_to_id_set(p->notes, id, note);
+ if (!p->notes)
+ return isl_printer_free(p);
+ return p;
+error:
+ isl_printer_free(p);
+ isl_id_free(id);
+ isl_id_free(note);
+ return NULL;
+}
+
+/* Keep track of whether the printing to "p" is being performed from
+ * an isl_*_dump function as specified by "dump".
+ */
+__isl_give isl_printer *isl_printer_set_dump(__isl_take isl_printer *p,
+ int dump)
+{
+ if (!p)
+ return NULL;
+
+ p->dump = dump;
+
+ return p;
+}
+
+/* Set the YAML style of "p" to "yaml_style" and return the updated printer.
+ */
+__isl_give isl_printer *isl_printer_set_yaml_style(__isl_take isl_printer *p,
+ int yaml_style)
+{
+ if (!p)
+ return NULL;
+
+ p->yaml_style = yaml_style;
+
+ return p;
+}
+
+/* Return the YAML style of "p" or -1 on error.
+ */
+int isl_printer_get_yaml_style(__isl_keep isl_printer *p)
+{
+ if (!p)
+ return -1;
+ return p->yaml_style;
+}
+
+/* Push "state" onto the stack of currently active YAML elements and
+ * return the updated printer.
+ */
+static __isl_give isl_printer *push_state(__isl_take isl_printer *p,
+ enum isl_yaml_state state)
+{
+ if (!p)
+ return NULL;
+
+ if (p->yaml_size < p->yaml_depth + 1) {
+ enum isl_yaml_state *state;
+ state = isl_realloc_array(p->ctx, p->yaml_state,
+ enum isl_yaml_state, p->yaml_depth + 1);
+ if (!state)
+ return isl_printer_free(p);
+ p->yaml_state = state;
+ p->yaml_size = p->yaml_depth + 1;
+ }
+
+ p->yaml_state[p->yaml_depth] = state;
+ p->yaml_depth++;
+
+ return p;
+}
+
+/* Remove the innermost active YAML element from the stack and
+ * return the updated printer.
+ */
+static __isl_give isl_printer *pop_state(__isl_take isl_printer *p)
+{
+ if (!p)
+ return NULL;
+ p->yaml_depth--;
+ return p;
+}
+
+/* Set the state of the innermost active YAML element to "state" and
+ * return the updated printer.
+ */
+static __isl_give isl_printer *update_state(__isl_take isl_printer *p,
+ enum isl_yaml_state state)
+{
+ if (!p)
+ return NULL;
+ if (p->yaml_depth < 1)
+ isl_die(isl_printer_get_ctx(p), isl_error_invalid,
+ "not in YAML construct", return isl_printer_free(p));
+
+ p->yaml_state[p->yaml_depth - 1] = state;
+
+ return p;
+}
+
+/* Return the state of the innermost active YAML element.
+ * Return isl_yaml_none if we are not inside any YAML element.
+ */
+static enum isl_yaml_state current_state(__isl_keep isl_printer *p)
+{
+ if (!p)
+ return isl_yaml_none;
+ if (p->yaml_depth < 1)
+ return isl_yaml_none;
+ return p->yaml_state[p->yaml_depth - 1];
+}
+
+/* If we are printing a YAML document and we are at the start of an element,
+ * print whatever is needed before we can print the actual element and
+ * keep track of the fact that we are now printing the element.
+ * If "eol" is set, then whatever we print is going to be the last
+ * thing that gets printed on this line.
+ *
+ * If we are about the print the first key of a mapping, then nothing
+ * extra needs to be printed. For any other key, however, we need
+ * to either move to the next line (in block format) or print a comma
+ * (in flow format).
+ * Before printing a value in a mapping, we need to print a colon.
+ *
+ * For sequences, in flow format, we only need to print a comma
+ * for each element except the first.
+ * In block format, before the first element in the sequence,
+ * we move to a new line, print a dash and increase the indentation.
+ * Before any other element, we print a dash on a new line,
+ * temporarily moving the indentation back.
+ */
+static __isl_give isl_printer *enter_state(__isl_take isl_printer *p,
+ int eol)
+{
+ enum isl_yaml_state state;
+
+ if (!p)
+ return NULL;
+
+ state = current_state(p);
+ if (state == isl_yaml_mapping_val_start) {
+ if (eol)
+ p = p->ops->print_str(p, ":");
+ else
+ p = p->ops->print_str(p, ": ");
+ p = update_state(p, isl_yaml_mapping_val);
+ } else if (state == isl_yaml_mapping_first_key_start) {
+ p = update_state(p, isl_yaml_mapping_key);
+ } else if (state == isl_yaml_mapping_key_start) {
+ if (p->yaml_style == ISL_YAML_STYLE_FLOW)
+ p = p->ops->print_str(p, ", ");
+ else {
+ p = p->ops->end_line(p);
+ p = p->ops->start_line(p);
+ }
+ p = update_state(p, isl_yaml_mapping_key);
+ } else if (state == isl_yaml_sequence_first_start) {
+ if (p->yaml_style != ISL_YAML_STYLE_FLOW) {
+ p = p->ops->end_line(p);
+ p = p->ops->start_line(p);
+ p = p->ops->print_str(p, "- ");
+ p = isl_printer_indent(p, 2);
+ }
+ p = update_state(p, isl_yaml_sequence);
+ } else if (state == isl_yaml_sequence_start) {
+ if (p->yaml_style == ISL_YAML_STYLE_FLOW)
+ p = p->ops->print_str(p, ", ");
+ else {
+ p = p->ops->end_line(p);
+ p = isl_printer_indent(p, -2);
+ p = p->ops->start_line(p);
+ p = p->ops->print_str(p, "- ");
+ p = isl_printer_indent(p, 2);
+ }
+ p = update_state(p, isl_yaml_sequence);
+ }
+
+ return p;
+}
+
+__isl_give isl_printer *isl_printer_print_str(__isl_take isl_printer *p,
+ const char *s)
+{
+ if (!p)
+ return NULL;
+ if (!s)
+ return isl_printer_free(p);
+ p = enter_state(p, 0);
+ if (!p)
+ return NULL;
+ return p->ops->print_str(p, s);
+}
+
+__isl_give isl_printer *isl_printer_print_double(__isl_take isl_printer *p,
+ double d)
+{
+ p = enter_state(p, 0);
+ if (!p)
+ return NULL;
+
+ return p->ops->print_double(p, d);
+}
+
+__isl_give isl_printer *isl_printer_print_int(__isl_take isl_printer *p, int i)
+{
+ p = enter_state(p, 0);
+ if (!p)
+ return NULL;
+
+ return p->ops->print_int(p, i);
+}
+
+__isl_give isl_printer *isl_printer_print_isl_int(__isl_take isl_printer *p,
+ isl_int i)
+{
+ p = enter_state(p, 0);
+ if (!p)
+ return NULL;
+
+ return p->ops->print_isl_int(p, i);
+}
+
+__isl_give isl_printer *isl_printer_start_line(__isl_take isl_printer *p)
+{
+ if (!p)
+ return NULL;
+
+ return p->ops->start_line(p);
+}
+
+__isl_give isl_printer *isl_printer_end_line(__isl_take isl_printer *p)
+{
+ if (!p)
+ return NULL;
+
+ return p->ops->end_line(p);
+}
+
+/* Return a copy of the string constructed by the string printer "printer".
+ */
+__isl_give char *isl_printer_get_str(__isl_keep isl_printer *printer)
+{
+ if (!printer)
+ return NULL;
+ if (printer->ops != &str_ops)
+ isl_die(isl_printer_get_ctx(printer), isl_error_invalid,
+ "isl_printer_get_str can only be called on a string "
+ "printer", return NULL);
+ if (!printer->buf)
+ return NULL;
+ return strdup(printer->buf);
+}
+
+__isl_give isl_printer *isl_printer_flush(__isl_take isl_printer *p)
+{
+ if (!p)
+ return NULL;
+
+ return p->ops->flush(p);
+}
+
+/* Start a YAML mapping and push a new state to reflect that we
+ * are about to print the first key in a mapping.
+ *
+ * In flow style, print the opening brace.
+ * In block style, move to the next line with an increased indentation,
+ * except if this is the outer mapping or if we are inside a sequence
+ * (in which case we have already increased the indentation and we want
+ * to print the first key on the same line as the dash).
+ */
+__isl_give isl_printer *isl_printer_yaml_start_mapping(
+ __isl_take isl_printer *p)
+{
+ enum isl_yaml_state state;
+
+ if (!p)
+ return NULL;
+ p = enter_state(p, p->yaml_style == ISL_YAML_STYLE_BLOCK);
+ if (!p)
+ return NULL;
+ state = current_state(p);
+ if (p->yaml_style == ISL_YAML_STYLE_FLOW)
+ p = p->ops->print_str(p, "{ ");
+ else if (state != isl_yaml_none && state != isl_yaml_sequence) {
+ p = p->ops->end_line(p);
+ p = isl_printer_indent(p, 2);
+ p = p->ops->start_line(p);
+ }
+ p = push_state(p, isl_yaml_mapping_first_key_start);
+ return p;
+}
+
+/* Finish a YAML mapping and pop it from the state stack.
+ *
+ * In flow style, print the closing brace.
+ *
+ * In block style, first check if we are still in the
+ * isl_yaml_mapping_first_key_start state. If so, we have not printed
+ * anything yet, so print "{}" to indicate an empty mapping.
+ * If we increased the indentation in isl_printer_yaml_start_mapping,
+ * then decrease it again.
+ * If this is the outer mapping then print a newline.
+ */
+__isl_give isl_printer *isl_printer_yaml_end_mapping(
+ __isl_take isl_printer *p)
+{
+ enum isl_yaml_state state;
+
+ state = current_state(p);
+ p = pop_state(p);
+ if (!p)
+ return NULL;
+ if (p->yaml_style == ISL_YAML_STYLE_FLOW)
+ return p->ops->print_str(p, " }");
+ if (state == isl_yaml_mapping_first_key_start)
+ p = p->ops->print_str(p, "{}");
+ if (!p)
+ return NULL;
+ state = current_state(p);
+ if (state != isl_yaml_none && state != isl_yaml_sequence)
+ p = isl_printer_indent(p, -2);
+ if (state == isl_yaml_none)
+ p = p->ops->end_line(p);
+ return p;
+}
+
+/* Start a YAML sequence and push a new state to reflect that we
+ * are about to print the first element in a sequence.
+ *
+ * In flow style, print the opening bracket.
+ */
+__isl_give isl_printer *isl_printer_yaml_start_sequence(
+ __isl_take isl_printer *p)
+{
+ if (!p)
+ return NULL;
+ p = enter_state(p, p->yaml_style == ISL_YAML_STYLE_BLOCK);
+ p = push_state(p, isl_yaml_sequence_first_start);
+ if (!p)
+ return NULL;
+ if (p->yaml_style == ISL_YAML_STYLE_FLOW)
+ p = p->ops->print_str(p, "[ ");
+ return p;
+}
+
+/* Finish a YAML sequence and pop it from the state stack.
+ *
+ * In flow style, print the closing bracket.
+ *
+ * In block style, check if we are still in the
+ * isl_yaml_sequence_first_start state. If so, we have not printed
+ * anything yet, so print "[]" or " []" to indicate an empty sequence.
+ * We print the extra space when we instructed enter_state not
+ * to print a space at the end of the line.
+ * Otherwise, undo the increase in indentation performed by
+ * enter_state when moving away from the isl_yaml_sequence_first_start
+ * state.
+ * If this is the outer sequence then print a newline.
+ */
+__isl_give isl_printer *isl_printer_yaml_end_sequence(
+ __isl_take isl_printer *p)
+{
+ enum isl_yaml_state state, up;
+
+ state = current_state(p);
+ p = pop_state(p);
+ if (!p)
+ return NULL;
+ if (p->yaml_style == ISL_YAML_STYLE_FLOW)
+ return p->ops->print_str(p, " ]");
+ up = current_state(p);
+ if (state == isl_yaml_sequence_first_start) {
+ if (up == isl_yaml_mapping_val)
+ p = p->ops->print_str(p, " []");
+ else
+ p = p->ops->print_str(p, "[]");
+ } else {
+ p = isl_printer_indent(p, -2);
+ }
+ if (!p)
+ return NULL;
+ state = current_state(p);
+ if (state == isl_yaml_none)
+ p = p->ops->end_line(p);
+ return p;
+}
+
+/* Mark the fact that the current element is finished and that
+ * the next output belongs to the next element.
+ * In particular, if we are printing a key, then prepare for
+ * printing the subsequent value. If we are printing a value,
+ * prepare for printing the next key. If we are printing an
+ * element in a sequence, prepare for printing the next element.
+ */
+__isl_give isl_printer *isl_printer_yaml_next(__isl_take isl_printer *p)
+{
+ enum isl_yaml_state state;
+
+ if (!p)
+ return NULL;
+ if (p->yaml_depth < 1)
+ isl_die(isl_printer_get_ctx(p), isl_error_invalid,
+ "not in YAML construct", return isl_printer_free(p));
+
+ state = current_state(p);
+ if (state == isl_yaml_mapping_key)
+ state = isl_yaml_mapping_val_start;
+ else if (state == isl_yaml_mapping_val)
+ state = isl_yaml_mapping_key_start;
+ else if (state == isl_yaml_sequence)
+ state = isl_yaml_sequence_start;
+ p = update_state(p, state);
+
+ return p;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_printer_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_printer_private.h
new file mode 100644
index 00000000000..6b852e8bc47
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_printer_private.h
@@ -0,0 +1,52 @@
+#ifndef ISL_PRINTER_PRIVATE_H
+#define ISL_PRINTER_PRIVATE_H
+
+#include <isl/printer.h>
+#include <isl_yaml.h>
+#include <isl/id_to_id.h>
+
+struct isl_printer_ops;
+
+/* A printer to a file or a string.
+ *
+ * "dump" is set if the printing is performed from an isl_*_dump function.
+ *
+ * yaml_style is the YAML style in which the next elements should
+ * be printed and may be either ISL_YAML_STYLE_BLOCK or ISL_YAML_STYLE_FLOW,
+ * with ISL_YAML_STYLE_FLOW being the default.
+ * yaml_state keeps track of the currently active YAML elements.
+ * yaml_size is the size of this arrays, while yaml_depth
+ * is the number of elements currently in use.
+ * yaml_state may be NULL if no YAML printing is being performed.
+ *
+ * notes keeps track of arbitrary notes as a mapping between
+ * name identifiers and note identifiers. It may be NULL
+ * if there are no notes yet.
+ */
+struct isl_printer {
+ struct isl_ctx *ctx;
+ struct isl_printer_ops *ops;
+ FILE *file;
+ int buf_n;
+ int buf_size;
+ char *buf;
+ int indent;
+ int output_format;
+ int dump;
+ char *indent_prefix;
+ char *prefix;
+ char *suffix;
+ int width;
+
+ int yaml_style;
+ int yaml_depth;
+ int yaml_size;
+ enum isl_yaml_state *yaml_state;
+
+ isl_id_to_id *notes;
+};
+
+__isl_give isl_printer *isl_printer_set_dump(__isl_take isl_printer *p,
+ int dump);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_project_out_all_params_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_project_out_all_params_templ.c
new file mode 100644
index 00000000000..73f9a8fa4d8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_project_out_all_params_templ.c
@@ -0,0 +1,21 @@
+/*
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege
+ */
+
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Project out all parameters from "obj" by existentially quantifying
+ * over them.
+ */
+__isl_give TYPE *FN(TYPE,project_out_all_params)(__isl_take TYPE *obj)
+{
+ isl_size n;
+
+ n = FN(TYPE,dim)(obj, isl_dim_param);
+ if (n < 0)
+ return FN(TYPE,free)(obj);
+ return FN(TYPE,project_out)(obj, isl_dim_param, 0, n);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_multi_val_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_multi_val_templ.c
new file mode 100644
index 00000000000..eccc356cb14
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_multi_val_templ.c
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#undef VAL_BASE
+#define VAL_BASE multi_val
+
+#include <isl_pw_add_constant_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_templ.c
new file mode 100644
index 00000000000..db60e4f7ab7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_templ.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_pw_macro.h>
+
+#undef VAL
+#define VAL CAT(isl_,VAL_BASE)
+
+/* Add "v" to the constant term of "pw" over its entire definition domain.
+ */
+__isl_give PW *FN(FN(PW,add_constant),VAL_BASE)(__isl_take PW *pw,
+ __isl_take VAL *v)
+{
+ isl_bool zero;
+ isl_size n;
+ int i;
+
+ zero = FN(VAL,is_zero)(v);
+ n = FN(PW,n_piece)(pw);
+ if (zero < 0 || n < 0)
+ goto error;
+ if (zero || n == 0) {
+ FN(VAL,free)(v);
+ return pw;
+ }
+
+ for (i = 0; i < n; ++i) {
+ EL *el;
+
+ el = FN(PW,take_base_at)(pw, i);
+ el = FN(FN(EL,add_constant),VAL_BASE)(el, FN(VAL,copy)(v));
+ pw = FN(PW,restore_base_at)(pw, i, el);
+ }
+
+ FN(VAL,free)(v);
+ return pw;
+error:
+ FN(PW,free)(pw);
+ FN(VAL,free)(v);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_val_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_val_templ.c
new file mode 100644
index 00000000000..d63ec04936e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_add_constant_val_templ.c
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#undef VAL_BASE
+#define VAL_BASE val
+
+#include <isl_pw_add_constant_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_bind_domain_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_bind_domain_templ.c
new file mode 100644
index 00000000000..2a01495b816
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_bind_domain_templ.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2018 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_pw_macro.h>
+
+#undef TYPE
+#define TYPE PW
+#include <isl_bind_domain_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_eval.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_eval.c
new file mode 100644
index 00000000000..f59b78726a8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_eval.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/val.h>
+#include <isl_space_private.h>
+#include <isl_point_private.h>
+
+#include <isl_pw_macro.h>
+
+/* Evaluate "pw" in the void point "pnt".
+ * In particular, return the value NaN.
+ */
+static __isl_give isl_val *FN(PW,eval_void)(__isl_take PW *pw,
+ __isl_take isl_point *pnt)
+{
+ isl_ctx *ctx;
+
+ ctx = isl_point_get_ctx(pnt);
+ FN(PW,free)(pw);
+ isl_point_free(pnt);
+ return isl_val_nan(ctx);
+}
+
+/* Evaluate the piecewise function "pw" in "pnt".
+ * If the point is void, then return NaN.
+ * If the point lies outside the domain of "pw", then return 0 or NaN
+ * depending on whether 0 is the default value for this type of function.
+ */
+__isl_give isl_val *FN(PW,eval)(__isl_take PW *pw, __isl_take isl_point *pnt)
+{
+ int i;
+ isl_bool is_void;
+ isl_bool found;
+ isl_ctx *ctx;
+ isl_bool ok;
+ isl_space *pnt_space, *pw_space;
+ isl_val *v;
+
+ pnt_space = isl_point_peek_space(pnt);
+ pw_space = FN(PW,peek_space)(pw);
+ ok = isl_space_is_domain_internal(pnt_space, pw_space);
+ if (ok < 0)
+ goto error;
+ ctx = isl_point_get_ctx(pnt);
+ if (!ok)
+ isl_die(ctx, isl_error_invalid,
+ "incompatible spaces", goto error);
+ is_void = isl_point_is_void(pnt);
+ if (is_void < 0)
+ goto error;
+ if (is_void)
+ return FN(PW,eval_void)(pw, pnt);
+
+ found = isl_bool_false;
+ for (i = 0; i < pw->n; ++i) {
+ found = isl_set_contains_point(pw->p[i].set, pnt);
+ if (found < 0)
+ goto error;
+ if (found)
+ break;
+ }
+ if (found) {
+ v = FN(EL,eval)(FN(EL,copy)(pw->p[i].FIELD),
+ isl_point_copy(pnt));
+ } else if (DEFAULT_IS_ZERO) {
+ v = isl_val_zero(ctx);
+ } else {
+ v = isl_val_nan(ctx);
+ }
+ FN(PW,free)(pw);
+ isl_point_free(pnt);
+ return v;
+error:
+ FN(PW,free)(pw);
+ isl_point_free(pnt);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_hash.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_hash.c
new file mode 100644
index 00000000000..4d121c07986
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_hash.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege
+ */
+
+#include <isl_pw_macro.h>
+#include <isl/hash.h>
+
+/* Return a hash value that digests "pw".
+ */
+uint32_t FN(PW,get_hash)(__isl_keep PW *pw)
+{
+ int i;
+ uint32_t hash;
+
+ if (!pw)
+ return 0;
+
+ hash = isl_hash_init();
+ for (i = 0; i < pw->n; ++i) {
+ uint32_t set_hash, el_hash;
+
+ set_hash = isl_set_get_hash(pw->p[i].set);
+ isl_hash_hash(hash, set_hash);
+ el_hash = FN(EL,get_hash)(pw->p[i].FIELD);
+ isl_hash_hash(hash, el_hash);
+ }
+
+ return hash;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_insert_dims_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_insert_dims_templ.c
new file mode 100644
index 00000000000..c451dcac113
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_insert_dims_templ.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+__isl_give PW *FN(PW,insert_dims)(__isl_take PW *pw, enum isl_dim_type type,
+ unsigned first, unsigned n)
+{
+ int i;
+ enum isl_dim_type set_type;
+
+ if (!pw)
+ return NULL;
+ if (n == 0 && !isl_space_is_named_or_nested(pw->dim, type))
+ return pw;
+
+ set_type = type == isl_dim_in ? isl_dim_set : type;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ return NULL;
+
+ pw->dim = isl_space_insert_dims(pw->dim, type, first, n);
+ if (!pw->dim)
+ goto error;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].set = isl_set_insert_dims(pw->p[i].set,
+ set_type, first, n);
+ if (!pw->p[i].set)
+ goto error;
+ pw->p[i].FIELD = FN(EL,insert_dims)(pw->p[i].FIELD,
+ type, first, n);
+ if (!pw->p[i].FIELD)
+ goto error;
+ }
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+__isl_give PW *FN(PW,add_dims)(__isl_take PW *pw, enum isl_dim_type type,
+ unsigned n)
+{
+ isl_size pos;
+
+ pos = FN(PW,dim)(pw, type);
+ if (pos < 0)
+ return FN(PW,free)(pw);
+
+ return FN(PW,insert_dims)(pw, type, pos, n);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_insert_domain_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_insert_domain_templ.c
new file mode 100644
index 00000000000..bd02d2682cb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_insert_domain_templ.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_pw_macro.h>
+
+#undef TYPE
+#define TYPE PW
+#include <isl_insert_domain_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_lift_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_lift_templ.c
new file mode 100644
index 00000000000..0ffe09afd87
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_lift_templ.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_pw_macro.h>
+
+static isl_stat foreach_lifted_subset(__isl_take isl_set *set,
+ __isl_take EL *el,
+ isl_stat (*fn)(__isl_take isl_set *set, __isl_take EL *el,
+ void *user), void *user)
+{
+ int i;
+
+ if (!set || !el)
+ goto error;
+
+ for (i = 0; i < set->n; ++i) {
+ isl_set *lift;
+ EL *copy;
+
+ lift = isl_set_from_basic_set(isl_basic_set_copy(set->p[i]));
+ lift = isl_set_lift(lift);
+
+ copy = FN(EL,copy)(el);
+ copy = FN(EL,lift)(copy, isl_set_get_space(lift));
+
+ if (fn(lift, copy, user) < 0)
+ goto error;
+ }
+
+ isl_set_free(set);
+ FN(EL,free)(el);
+
+ return isl_stat_ok;
+error:
+ isl_set_free(set);
+ FN(EL,free)(el);
+ return isl_stat_error;
+}
+
+isl_stat FN(PW,foreach_lifted_piece)(__isl_keep PW *pw,
+ isl_stat (*fn)(__isl_take isl_set *set, __isl_take EL *el,
+ void *user), void *user)
+{
+ int i;
+
+ if (!pw)
+ return isl_stat_error;
+
+ for (i = 0; i < pw->n; ++i) {
+ isl_bool any;
+ isl_set *set;
+ EL *el;
+
+ any = isl_set_involves_locals(pw->p[i].set);
+ if (any < 0)
+ return isl_stat_error;
+ set = isl_set_copy(pw->p[i].set);
+ el = FN(EL,copy)(pw->p[i].FIELD);
+ if (!any) {
+ if (fn(set, el, user) < 0)
+ return isl_stat_error;
+ continue;
+ }
+ if (foreach_lifted_subset(set, el, fn, user) < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_locals_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_locals_templ.c
new file mode 100644
index 00000000000..ecd3aefe31c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_locals_templ.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_pw_macro.h>
+
+/* isl_pw_*_every_piece callback that checks whether "set" and "el"
+ * are free of local variables.
+ */
+static isl_bool FN(PW,piece_no_local)(__isl_keep isl_set *set,
+ __isl_keep EL *el, void *user)
+{
+ isl_bool involves;
+
+ involves = isl_set_involves_locals(set);
+ if (involves >= 0 && !involves)
+ involves = FN(EL,involves_locals)(el);
+
+ return isl_bool_not(involves);
+}
+
+/* Does "pw" involve any local variables, i.e., integer divisions?
+ */
+isl_bool FN(PW,involves_locals)(__isl_keep PW *pw)
+{
+ isl_bool no_locals;
+
+ no_locals = FN(PW,every_piece)(pw, &FN(PW,piece_no_local), NULL);
+ return isl_bool_not(no_locals);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_macro.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_macro.h
new file mode 100644
index 00000000000..4f74978f4bb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_macro.h
@@ -0,0 +1,8 @@
+#define xCAT(A,B) A ## B
+#define CAT(A,B) xCAT(A,B)
+#undef EL
+#define EL CAT(isl_,BASE)
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+#define xS(TYPE,NAME) struct TYPE ## _ ## NAME
+#define S(TYPE,NAME) xS(TYPE,NAME)
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_morph_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_morph_templ.c
new file mode 100644
index 00000000000..a90fea12168
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_morph_templ.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+__isl_give PW *FN(PW,morph_domain)(__isl_take PW *pw,
+ __isl_take isl_morph *morph)
+{
+ int i;
+ isl_ctx *ctx;
+
+ if (!pw || !morph)
+ goto error;
+
+ ctx = isl_space_get_ctx(pw->dim);
+ isl_assert(ctx, isl_space_is_domain_internal(morph->dom->dim, pw->dim),
+ goto error);
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ goto error;
+ pw->dim = isl_space_extend_domain_with_range(
+ isl_space_copy(morph->ran->dim), pw->dim);
+ if (!pw->dim)
+ goto error;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].set = isl_morph_set(isl_morph_copy(morph), pw->p[i].set);
+ if (!pw->p[i].set)
+ goto error;
+ pw->p[i].FIELD = FN(EL,morph_domain)(pw->p[i].FIELD,
+ isl_morph_copy(morph));
+ if (!pw->p[i].FIELD)
+ goto error;
+ }
+
+ isl_morph_free(morph);
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ isl_morph_free(morph);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_move_dims_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_move_dims_templ.c
new file mode 100644
index 00000000000..31788d79a7f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_move_dims_templ.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+__isl_give PW *FN(PW,move_dims)(__isl_take PW *pw,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ int i;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ return NULL;
+
+ pw->dim = isl_space_move_dims(pw->dim, dst_type, dst_pos, src_type, src_pos, n);
+ if (!pw->dim)
+ goto error;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].FIELD = FN(EL,move_dims)(pw->p[i].FIELD,
+ dst_type, dst_pos, src_type, src_pos, n);
+ if (!pw->p[i].FIELD)
+ goto error;
+ }
+
+ if (dst_type == isl_dim_in)
+ dst_type = isl_dim_set;
+ if (src_type == isl_dim_in)
+ src_type = isl_dim_set;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].set = isl_set_move_dims(pw->p[i].set,
+ dst_type, dst_pos,
+ src_type, src_pos, n);
+ if (!pw->p[i].set)
+ goto error;
+ }
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_neg_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_neg_templ.c
new file mode 100644
index 00000000000..65970cf6416
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_neg_templ.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_pw_macro.h>
+
+__isl_give PW *FN(PW,neg)(__isl_take PW *pw)
+{
+ int i;
+
+ if (!pw)
+ return NULL;
+
+ if (FN(PW,IS_ZERO)(pw))
+ return pw;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ return NULL;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].FIELD = FN(EL,neg)(pw->p[i].FIELD);
+ if (!pw->p[i].FIELD)
+ return FN(PW,free)(pw);
+ }
+
+ return pw;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_opt_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_opt_templ.c
new file mode 100644
index 00000000000..421b4d52e22
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_opt_templ.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+/* Compute the maximal value attained by the piecewise quasipolynomial
+ * on its domain or zero if the domain is empty.
+ * In the worst case, the domain is scanned completely,
+ * so the domain is assumed to be bounded.
+ */
+__isl_give isl_val *FN(PW,opt)(__isl_take PW *pw, int max)
+{
+ int i;
+ isl_val *opt;
+
+ if (!pw)
+ return NULL;
+
+ if (pw->n == 0) {
+ opt = isl_val_zero(FN(PW,get_ctx)(pw));
+ FN(PW,free)(pw);
+ return opt;
+ }
+
+ opt = FN(EL,opt_on_domain)(FN(EL,copy)(pw->p[0].FIELD),
+ isl_set_copy(pw->p[0].set), max);
+ for (i = 1; i < pw->n; ++i) {
+ isl_val *opt_i;
+ opt_i = FN(EL,opt_on_domain)(FN(EL,copy)(pw->p[i].FIELD),
+ isl_set_copy(pw->p[i].set), max);
+ if (max)
+ opt = isl_val_max(opt, opt_i);
+ else
+ opt = isl_val_min(opt, opt_i);
+ }
+
+ FN(PW,free)(pw);
+ return opt;
+}
+
+__isl_give isl_val *FN(PW,max)(__isl_take PW *pw)
+{
+ return FN(PW,opt)(pw, 1);
+}
+
+__isl_give isl_val *FN(PW,min)(__isl_take PW *pw)
+{
+ return FN(PW,opt)(pw, 0);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_pullback_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_pullback_templ.c
new file mode 100644
index 00000000000..6f1bda9a6b9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_pullback_templ.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2012 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl_pw_macro.h>
+
+#undef SUFFIX
+#define SUFFIX multi_aff
+#undef ARG1
+#define ARG1 PW
+#undef ARG2
+#define ARG2 isl_multi_aff
+
+static
+#include "isl_align_params_templ.c"
+
+#undef SUFFIX
+#define SUFFIX pw_multi_aff
+#undef ARG1
+#define ARG1 PW
+#undef ARG2
+#define ARG2 isl_pw_multi_aff
+
+static
+#include "isl_align_params_templ.c"
+
+/* Compute the pullback of "pw" by the function represented by "ma".
+ * In other words, plug in "ma" in "pw".
+ */
+__isl_give PW *FN(PW,pullback_multi_aff)(__isl_take PW *pw,
+ __isl_take isl_multi_aff *ma)
+{
+ int i;
+ isl_space *space = NULL;
+
+ FN(PW,align_params_multi_aff)(&pw, &ma);
+ ma = isl_multi_aff_align_divs(ma);
+ pw = FN(PW,cow)(pw);
+ if (!pw || !ma)
+ goto error;
+
+ space = isl_space_join(isl_multi_aff_get_space(ma),
+ FN(PW,get_space)(pw));
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].set = isl_set_preimage_multi_aff(pw->p[i].set,
+ isl_multi_aff_copy(ma));
+ if (!pw->p[i].set)
+ goto error;
+ pw->p[i].FIELD = FN(EL,pullback_multi_aff)(pw->p[i].FIELD,
+ isl_multi_aff_copy(ma));
+ if (!pw->p[i].FIELD)
+ goto error;
+ }
+
+ pw = FN(PW,reset_space)(pw, space);
+ isl_multi_aff_free(ma);
+ return pw;
+error:
+ isl_space_free(space);
+ isl_multi_aff_free(ma);
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+/* Compute the pullback of "pw" by the function represented by "pma".
+ * In other words, plug in "pma" in "pw".
+ */
+static __isl_give PW *FN(PW,pullback_pw_multi_aff_aligned)(__isl_take PW *pw,
+ __isl_take isl_pw_multi_aff *pma)
+{
+ int i;
+ PW *res;
+
+ if (!pma)
+ goto error;
+
+ if (pma->n == 0) {
+ isl_space *space;
+ space = isl_space_join(isl_pw_multi_aff_get_space(pma),
+ FN(PW,get_space)(pw));
+ isl_pw_multi_aff_free(pma);
+ res = FN(PW,empty)(space);
+ FN(PW,free)(pw);
+ return res;
+ }
+
+ res = FN(PW,pullback_multi_aff)(FN(PW,copy)(pw),
+ isl_multi_aff_copy(pma->p[0].maff));
+ res = FN(PW,intersect_domain)(res, isl_set_copy(pma->p[0].set));
+
+ for (i = 1; i < pma->n; ++i) {
+ PW *res_i;
+
+ res_i = FN(PW,pullback_multi_aff)(FN(PW,copy)(pw),
+ isl_multi_aff_copy(pma->p[i].maff));
+ res_i = FN(PW,intersect_domain)(res_i,
+ isl_set_copy(pma->p[i].set));
+ res = FN(PW,add_disjoint)(res, res_i);
+ }
+
+ isl_pw_multi_aff_free(pma);
+ FN(PW,free)(pw);
+ return res;
+error:
+ isl_pw_multi_aff_free(pma);
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+__isl_give PW *FN(PW,pullback_pw_multi_aff)(__isl_take PW *pw,
+ __isl_take isl_pw_multi_aff *pma)
+{
+ FN(PW,align_params_pw_multi_aff)(&pw, &pma);
+ return FN(PW,pullback_pw_multi_aff_aligned)(pw, pma);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_range_tuple_id_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_range_tuple_id_templ.c
new file mode 100644
index 00000000000..37392f5e934
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_range_tuple_id_templ.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 Sven Verdoolaege
+ * Copyright 2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+/* Does the (range) tuple of "pw" have an identifier?
+ *
+ * Technically, the implementation should use isl_dim_set if "pw"
+ * lives in a set space and isl_dim_out if it lives in a map space.
+ * Internally, however, it can be assumed that isl_dim_set is equal
+ * to isl_dim_out.
+ */
+isl_bool FN(PW,has_range_tuple_id)(__isl_keep PW *pw)
+{
+ return FN(PW,has_tuple_id)(pw, isl_dim_out);
+}
+
+/* Return the identifier of the (range) tuple of "pw", assuming it has one.
+ *
+ * Technically, the implementation should use isl_dim_set if "pw"
+ * lives in a set space and isl_dim_out if it lives in a map space.
+ * Internally, however, it can be assumed that isl_dim_set is equal
+ * to isl_dim_out.
+ */
+__isl_give isl_id *FN(PW,get_range_tuple_id)(__isl_keep PW *pw)
+{
+ return FN(PW,get_tuple_id)(pw, isl_dim_out);
+}
+
+/* Replace the identifier of the (range) tuple of "pw" by "id".
+ *
+ * Technically, the implementation should use isl_dim_set if "pw"
+ * lives in a set space and isl_dim_out if it lives in a map space.
+ * Internally, however, it can be assumed that isl_dim_set is equal
+ * to isl_dim_out.
+ */
+__isl_give PW *FN(PW,set_range_tuple_id)(__isl_take PW *pw,
+ __isl_take isl_id *id)
+{
+ return FN(PW,set_tuple_id)(pw, isl_dim_out, id);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_sub_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_sub_templ.c
new file mode 100644
index 00000000000..34107948f77
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_sub_templ.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_pw_macro.h>
+
+__isl_give PW *FN(PW,sub)(__isl_take PW *pw1, __isl_take PW *pw2)
+{
+ return FN(PW,add)(pw1, FN(PW,neg)(pw2));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_templ.c
new file mode 100644
index 00000000000..3fa6faf1b5f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_templ.c
@@ -0,0 +1,2079 @@
+/*
+ * Copyright 2010-2011 INRIA Saclay
+ * Copyright 2011 Sven Verdoolaege
+ * Copyright 2012-2014 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <isl/id.h>
+#include <isl/aff.h>
+#include <isl_sort.h>
+#include <isl_val_private.h>
+
+#include <isl_pw_macro.h>
+
+#include "opt_type.h"
+
+__isl_give PW *FN(PW,alloc_size)(__isl_take isl_space *space
+ OPT_TYPE_PARAM, int n)
+{
+ isl_ctx *ctx;
+ struct PW *pw;
+
+ if (!space)
+ return NULL;
+ ctx = isl_space_get_ctx(space);
+ isl_assert(ctx, n >= 0, goto error);
+ pw = isl_alloc(ctx, struct PW,
+ sizeof(struct PW) + (n - 1) * sizeof(S(PW,piece)));
+ if (!pw)
+ goto error;
+
+ pw->ref = 1;
+ OPT_SET_TYPE(pw->, type);
+ pw->size = n;
+ pw->n = 0;
+ pw->dim = space;
+ return pw;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give PW *FN(PW,ZERO)(__isl_take isl_space *space OPT_TYPE_PARAM)
+{
+ return FN(PW,alloc_size)(space OPT_TYPE_ARG(NO_LOC), 0);
+}
+
+/* Add a piece with domain "set" and base expression "el"
+ * to the piecewise expression "pw".
+ *
+ * Do this independently of the values of "set" and "el",
+ * such that this function can be used by isl_pw_*_dup.
+ */
+__isl_give PW *FN(PW,add_dup_piece)(__isl_take PW *pw,
+ __isl_take isl_set *set, __isl_take EL *el)
+{
+ isl_ctx *ctx;
+ isl_space *el_dim = NULL;
+
+ if (!pw || !set || !el)
+ goto error;
+
+ ctx = isl_set_get_ctx(set);
+ if (!OPT_EQUAL_TYPES(pw->, el->))
+ isl_die(ctx, isl_error_invalid, "fold types don't match",
+ goto error);
+ el_dim = FN(EL,get_space(el));
+ isl_assert(ctx, isl_space_is_equal(pw->dim, el_dim), goto error);
+ isl_assert(ctx, pw->n < pw->size, goto error);
+
+ pw->p[pw->n].set = set;
+ pw->p[pw->n].FIELD = el;
+ pw->n++;
+
+ isl_space_free(el_dim);
+ return pw;
+error:
+ isl_space_free(el_dim);
+ FN(PW,free)(pw);
+ isl_set_free(set);
+ FN(EL,free)(el);
+ return NULL;
+}
+
+/* Add a piece with domain "set" and base expression "el"
+ * to the piecewise expression "pw", provided the domain
+ * is not obviously empty and the base expression
+ * is not equal to the default value.
+ */
+__isl_give PW *FN(PW,add_piece)(__isl_take PW *pw,
+ __isl_take isl_set *set, __isl_take EL *el)
+{
+ isl_bool skip;
+
+ skip = isl_set_plain_is_empty(set);
+ if (skip >= 0 && !skip)
+ skip = FN(EL,EL_IS_ZERO)(el);
+ if (skip >= 0 && !skip)
+ return FN(PW,add_dup_piece)(pw, set, el);
+
+ isl_set_free(set);
+ FN(EL,free)(el);
+ if (skip < 0)
+ return FN(PW,free)(pw);
+ return pw;
+}
+
+/* Does the space of "set" correspond to that of the domain of "el".
+ */
+static isl_bool FN(PW,compatible_domain)(__isl_keep EL *el,
+ __isl_keep isl_set *set)
+{
+ isl_bool ok;
+ isl_space *el_space, *set_space;
+
+ if (!set || !el)
+ return isl_bool_error;
+ set_space = isl_set_get_space(set);
+ el_space = FN(EL,get_space)(el);
+ ok = isl_space_is_domain_internal(set_space, el_space);
+ isl_space_free(el_space);
+ isl_space_free(set_space);
+ return ok;
+}
+
+/* Check that the space of "set" corresponds to that of the domain of "el".
+ */
+static isl_stat FN(PW,check_compatible_domain)(__isl_keep EL *el,
+ __isl_keep isl_set *set)
+{
+ isl_bool ok;
+
+ ok = FN(PW,compatible_domain)(el, set);
+ if (ok < 0)
+ return isl_stat_error;
+ if (!ok)
+ isl_die(isl_set_get_ctx(set), isl_error_invalid,
+ "incompatible spaces", return isl_stat_error);
+
+ return isl_stat_ok;
+}
+
+__isl_give PW *FN(PW,alloc)(OPT_TYPE_PARAM_FIRST
+ __isl_take isl_set *set, __isl_take EL *el)
+{
+ PW *pw;
+
+ if (FN(PW,check_compatible_domain)(el, set) < 0)
+ goto error;
+
+ pw = FN(PW,alloc_size)(FN(EL,get_space)(el) OPT_TYPE_ARG(NO_LOC), 1);
+
+ return FN(PW,add_piece)(pw, set, el);
+error:
+ isl_set_free(set);
+ FN(EL,free)(el);
+ return NULL;
+}
+
+__isl_give PW *FN(PW,dup)(__isl_keep PW *pw)
+{
+ int i;
+ PW *dup;
+
+ if (!pw)
+ return NULL;
+
+ dup = FN(PW,alloc_size)(isl_space_copy(pw->dim)
+ OPT_TYPE_ARG(pw->), pw->n);
+ if (!dup)
+ return NULL;
+
+ for (i = 0; i < pw->n; ++i)
+ dup = FN(PW,add_dup_piece)(dup, isl_set_copy(pw->p[i].set),
+ FN(EL,copy)(pw->p[i].FIELD));
+
+ return dup;
+}
+
+__isl_give PW *FN(PW,cow)(__isl_take PW *pw)
+{
+ if (!pw)
+ return NULL;
+
+ if (pw->ref == 1)
+ return pw;
+ pw->ref--;
+ return FN(PW,dup)(pw);
+}
+
+__isl_give PW *FN(PW,copy)(__isl_keep PW *pw)
+{
+ if (!pw)
+ return NULL;
+
+ pw->ref++;
+ return pw;
+}
+
+__isl_null PW *FN(PW,free)(__isl_take PW *pw)
+{
+ int i;
+
+ if (!pw)
+ return NULL;
+ if (--pw->ref > 0)
+ return NULL;
+
+ for (i = 0; i < pw->n; ++i) {
+ isl_set_free(pw->p[i].set);
+ FN(EL,free)(pw->p[i].FIELD);
+ }
+ isl_space_free(pw->dim);
+ free(pw);
+
+ return NULL;
+}
+
+/* Create a piecewise expression with the given base expression on a universe
+ * domain.
+ */
+static __isl_give PW *FN(FN(FN(PW,from),BASE),type_base)(__isl_take EL *el
+ OPT_TYPE_PARAM)
+{
+ isl_set *dom = isl_set_universe(FN(EL,get_domain_space)(el));
+ return FN(PW,alloc)(OPT_TYPE_ARG_FIRST(NO_LOC) dom, el);
+}
+
+/* Create a piecewise expression with the given base expression on a universe
+ * domain.
+ *
+ * If the default value of this piecewise type is zero and
+ * if "el" is effectively zero, then create an empty piecewise expression
+ * instead.
+ */
+static __isl_give PW *FN(FN(FN(PW,from),BASE),type)(__isl_take EL *el
+ OPT_TYPE_PARAM)
+{
+ isl_bool is_zero;
+ isl_space *space;
+
+ if (!DEFAULT_IS_ZERO)
+ return FN(FN(FN(PW,from),BASE),type_base)(el
+ OPT_TYPE_ARG(NO_LOC));
+ is_zero = FN(EL,EL_IS_ZERO)(el);
+ if (is_zero < 0)
+ goto error;
+ if (!is_zero)
+ return FN(FN(FN(PW,from),BASE),type_base)(el
+ OPT_TYPE_ARG(NO_LOC));
+ space = FN(EL,get_space)(el);
+ FN(EL,free)(el);
+ return FN(PW,ZERO)(space OPT_TYPE_ARG(NO_LOC));
+error:
+ FN(EL,free)(el);
+ return NULL;
+}
+
+#ifdef HAS_TYPE
+/* Create a piecewise expression with the given base expression on a universe
+ * domain.
+ *
+ * Pass along the type as an extra argument for improved uniformity
+ * with piecewise types that do not have a fold type.
+ */
+__isl_give PW *FN(FN(PW,from),BASE)(__isl_take EL *el)
+{
+ enum isl_fold type = FN(EL,get_type)(el);
+ return FN(FN(FN(PW,from),BASE),type)(el, type);
+}
+#else
+__isl_give PW *FN(FN(PW,from),BASE)(__isl_take EL *el)
+{
+ return FN(FN(FN(PW,from),BASE),type)(el);
+}
+#endif
+
+const char *FN(PW,get_dim_name)(__isl_keep PW *pw, enum isl_dim_type type,
+ unsigned pos)
+{
+ return pw ? isl_space_get_dim_name(pw->dim, type, pos) : NULL;
+}
+
+isl_bool FN(PW,has_dim_id)(__isl_keep PW *pw, enum isl_dim_type type,
+ unsigned pos)
+{
+ return pw ? isl_space_has_dim_id(pw->dim, type, pos) : isl_bool_error;
+}
+
+__isl_give isl_id *FN(PW,get_dim_id)(__isl_keep PW *pw, enum isl_dim_type type,
+ unsigned pos)
+{
+ return pw ? isl_space_get_dim_id(pw->dim, type, pos) : NULL;
+}
+
+isl_bool FN(PW,has_tuple_name)(__isl_keep PW *pw, enum isl_dim_type type)
+{
+ return pw ? isl_space_has_tuple_name(pw->dim, type) : isl_bool_error;
+}
+
+const char *FN(PW,get_tuple_name)(__isl_keep PW *pw, enum isl_dim_type type)
+{
+ return pw ? isl_space_get_tuple_name(pw->dim, type) : NULL;
+}
+
+isl_bool FN(PW,has_tuple_id)(__isl_keep PW *pw, enum isl_dim_type type)
+{
+ return pw ? isl_space_has_tuple_id(pw->dim, type) : isl_bool_error;
+}
+
+__isl_give isl_id *FN(PW,get_tuple_id)(__isl_keep PW *pw, enum isl_dim_type type)
+{
+ return pw ? isl_space_get_tuple_id(pw->dim, type) : NULL;
+}
+
+isl_bool FN(PW,IS_ZERO)(__isl_keep PW *pw)
+{
+ if (!pw)
+ return isl_bool_error;
+
+ return isl_bool_ok(pw->n == 0);
+}
+
+__isl_give PW *FN(PW,realign_domain)(__isl_take PW *pw,
+ __isl_take isl_reordering *exp)
+{
+ int i;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw || !exp)
+ goto error;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].set = isl_set_realign(pw->p[i].set,
+ isl_reordering_copy(exp));
+ if (!pw->p[i].set)
+ goto error;
+ pw->p[i].FIELD = FN(EL,realign_domain)(pw->p[i].FIELD,
+ isl_reordering_copy(exp));
+ if (!pw->p[i].FIELD)
+ goto error;
+ }
+
+ pw = FN(PW,reset_domain_space)(pw, isl_reordering_get_space(exp));
+
+ isl_reordering_free(exp);
+ return pw;
+error:
+ isl_reordering_free(exp);
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+#undef TYPE
+#define TYPE PW
+
+#include "isl_check_named_params_templ.c"
+
+/* Align the parameters of "pw" to those of "model".
+ */
+__isl_give PW *FN(PW,align_params)(__isl_take PW *pw, __isl_take isl_space *model)
+{
+ isl_ctx *ctx;
+ isl_bool equal_params;
+
+ if (!pw || !model)
+ goto error;
+
+ ctx = isl_space_get_ctx(model);
+ if (!isl_space_has_named_params(model))
+ isl_die(ctx, isl_error_invalid,
+ "model has unnamed parameters", goto error);
+ if (FN(PW,check_named_params)(pw) < 0)
+ goto error;
+ equal_params = isl_space_has_equal_params(pw->dim, model);
+ if (equal_params < 0)
+ goto error;
+ if (!equal_params) {
+ isl_reordering *exp;
+
+ exp = isl_parameter_alignment_reordering(pw->dim, model);
+ exp = isl_reordering_extend_space(exp,
+ FN(PW,get_domain_space)(pw));
+ pw = FN(PW,realign_domain)(pw, exp);
+ }
+
+ isl_space_free(model);
+ return pw;
+error:
+ isl_space_free(model);
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+#undef TYPE
+#define TYPE PW
+
+static
+#include "isl_align_params_bin_templ.c"
+
+#undef SUFFIX
+#define SUFFIX set
+#undef ARG1
+#define ARG1 PW
+#undef ARG2
+#define ARG2 isl_set
+
+static
+#include "isl_align_params_templ.c"
+
+#undef TYPE
+#define TYPE PW
+
+#include "isl_type_has_equal_space_bin_templ.c"
+#include "isl_type_check_equal_space_templ.c"
+
+/* Private version of "union_add". For isl_pw_qpolynomial and
+ * isl_pw_qpolynomial_fold, we prefer to simply call it "add".
+ */
+static __isl_give PW *FN(PW,union_add_)(__isl_take PW *pw1, __isl_take PW *pw2)
+{
+ int i, j, n;
+ struct PW *res;
+ isl_ctx *ctx;
+ isl_set *set;
+
+ if (FN(PW,align_params_bin)(&pw1, &pw2) < 0)
+ goto error;
+
+ ctx = isl_space_get_ctx(pw1->dim);
+ if (!OPT_EQUAL_TYPES(pw1->, pw2->))
+ isl_die(ctx, isl_error_invalid,
+ "fold types don't match", goto error);
+ if (FN(PW,check_equal_space)(pw1, pw2) < 0)
+ goto error;
+
+ if (FN(PW,IS_ZERO)(pw1)) {
+ FN(PW,free)(pw1);
+ return pw2;
+ }
+
+ if (FN(PW,IS_ZERO)(pw2)) {
+ FN(PW,free)(pw2);
+ return pw1;
+ }
+
+ n = (pw1->n + 1) * (pw2->n + 1);
+ res = FN(PW,alloc_size)(isl_space_copy(pw1->dim)
+ OPT_TYPE_ARG(pw1->), n);
+
+ for (i = 0; i < pw1->n; ++i) {
+ set = isl_set_copy(pw1->p[i].set);
+ for (j = 0; j < pw2->n; ++j) {
+ struct isl_set *common;
+ EL *sum;
+ common = isl_set_intersect(isl_set_copy(pw1->p[i].set),
+ isl_set_copy(pw2->p[j].set));
+ if (isl_set_plain_is_empty(common)) {
+ isl_set_free(common);
+ continue;
+ }
+ set = isl_set_subtract(set,
+ isl_set_copy(pw2->p[j].set));
+
+ sum = FN(EL,add_on_domain)(common,
+ FN(EL,copy)(pw1->p[i].FIELD),
+ FN(EL,copy)(pw2->p[j].FIELD));
+
+ res = FN(PW,add_piece)(res, common, sum);
+ }
+ res = FN(PW,add_piece)(res, set, FN(EL,copy)(pw1->p[i].FIELD));
+ }
+
+ for (j = 0; j < pw2->n; ++j) {
+ set = isl_set_copy(pw2->p[j].set);
+ for (i = 0; i < pw1->n; ++i)
+ set = isl_set_subtract(set,
+ isl_set_copy(pw1->p[i].set));
+ res = FN(PW,add_piece)(res, set, FN(EL,copy)(pw2->p[j].FIELD));
+ }
+
+ FN(PW,free)(pw1);
+ FN(PW,free)(pw2);
+
+ return res;
+error:
+ FN(PW,free)(pw1);
+ FN(PW,free)(pw2);
+ return NULL;
+}
+
+/* Make sure "pw" has room for at least "n" more pieces.
+ *
+ * If there is only one reference to pw, we extend it in place.
+ * Otherwise, we create a new PW and copy the pieces.
+ */
+static __isl_give PW *FN(PW,grow)(__isl_take PW *pw, int n)
+{
+ int i;
+ isl_ctx *ctx;
+ PW *res;
+
+ if (!pw)
+ return NULL;
+ if (pw->n + n <= pw->size)
+ return pw;
+ ctx = FN(PW,get_ctx)(pw);
+ n += pw->n;
+ if (pw->ref == 1) {
+ res = isl_realloc(ctx, pw, struct PW,
+ sizeof(struct PW) + (n - 1) * sizeof(S(PW,piece)));
+ if (!res)
+ return FN(PW,free)(pw);
+ res->size = n;
+ return res;
+ }
+ res = FN(PW,alloc_size)(isl_space_copy(pw->dim) OPT_TYPE_ARG(pw->), n);
+ if (!res)
+ return FN(PW,free)(pw);
+ for (i = 0; i < pw->n; ++i)
+ res = FN(PW,add_piece)(res, isl_set_copy(pw->p[i].set),
+ FN(EL,copy)(pw->p[i].FIELD));
+ FN(PW,free)(pw);
+ return res;
+}
+
+__isl_give PW *FN(PW,add_disjoint)(__isl_take PW *pw1, __isl_take PW *pw2)
+{
+ int i;
+ isl_ctx *ctx;
+
+ if (FN(PW,align_params_bin)(&pw1, &pw2) < 0)
+ goto error;
+
+ if (pw1->size < pw1->n + pw2->n && pw1->n < pw2->n)
+ return FN(PW,add_disjoint)(pw2, pw1);
+
+ ctx = isl_space_get_ctx(pw1->dim);
+ if (!OPT_EQUAL_TYPES(pw1->, pw2->))
+ isl_die(ctx, isl_error_invalid,
+ "fold types don't match", goto error);
+ if (FN(PW,check_equal_space)(pw1, pw2) < 0)
+ goto error;
+
+ if (FN(PW,IS_ZERO)(pw1)) {
+ FN(PW,free)(pw1);
+ return pw2;
+ }
+
+ if (FN(PW,IS_ZERO)(pw2)) {
+ FN(PW,free)(pw2);
+ return pw1;
+ }
+
+ pw1 = FN(PW,grow)(pw1, pw2->n);
+ if (!pw1)
+ goto error;
+
+ for (i = 0; i < pw2->n; ++i)
+ pw1 = FN(PW,add_piece)(pw1,
+ isl_set_copy(pw2->p[i].set),
+ FN(EL,copy)(pw2->p[i].FIELD));
+
+ FN(PW,free)(pw2);
+
+ return pw1;
+error:
+ FN(PW,free)(pw1);
+ FN(PW,free)(pw2);
+ return NULL;
+}
+
+/* This function is currently only used from isl_aff.c
+ */
+static __isl_give PW *FN(PW,on_shared_domain_in)(__isl_take PW *pw1,
+ __isl_take PW *pw2, __isl_take isl_space *space,
+ __isl_give EL *(*fn)(__isl_take EL *el1, __isl_take EL *el2))
+ __attribute__ ((unused));
+
+/* Apply "fn" to pairs of elements from pw1 and pw2 on shared domains.
+ * The result of "fn" (and therefore also of this function) lives in "space".
+ */
+static __isl_give PW *FN(PW,on_shared_domain_in)(__isl_take PW *pw1,
+ __isl_take PW *pw2, __isl_take isl_space *space,
+ __isl_give EL *(*fn)(__isl_take EL *el1, __isl_take EL *el2))
+{
+ int i, j, n;
+ PW *res = NULL;
+
+ if (!pw1 || !pw2)
+ goto error;
+
+ n = pw1->n * pw2->n;
+ res = FN(PW,alloc_size)(isl_space_copy(space) OPT_TYPE_ARG(pw1->), n);
+
+ for (i = 0; i < pw1->n; ++i) {
+ for (j = 0; j < pw2->n; ++j) {
+ isl_set *common;
+ EL *res_ij;
+ int empty;
+
+ common = isl_set_intersect(
+ isl_set_copy(pw1->p[i].set),
+ isl_set_copy(pw2->p[j].set));
+ empty = isl_set_plain_is_empty(common);
+ if (empty < 0 || empty) {
+ isl_set_free(common);
+ if (empty < 0)
+ goto error;
+ continue;
+ }
+
+ res_ij = fn(FN(EL,copy)(pw1->p[i].FIELD),
+ FN(EL,copy)(pw2->p[j].FIELD));
+ res_ij = FN(EL,gist)(res_ij, isl_set_copy(common));
+
+ res = FN(PW,add_piece)(res, common, res_ij);
+ }
+ }
+
+ isl_space_free(space);
+ FN(PW,free)(pw1);
+ FN(PW,free)(pw2);
+ return res;
+error:
+ isl_space_free(space);
+ FN(PW,free)(pw1);
+ FN(PW,free)(pw2);
+ FN(PW,free)(res);
+ return NULL;
+}
+
+/* This function is currently only used from isl_aff.c
+ */
+static __isl_give PW *FN(PW,on_shared_domain)(__isl_take PW *pw1,
+ __isl_take PW *pw2,
+ __isl_give EL *(*fn)(__isl_take EL *el1, __isl_take EL *el2))
+ __attribute__ ((unused));
+
+/* Apply "fn" to pairs of elements from pw1 and pw2 on shared domains.
+ * The result of "fn" is assumed to live in the same space as "pw1" and "pw2".
+ */
+static __isl_give PW *FN(PW,on_shared_domain)(__isl_take PW *pw1,
+ __isl_take PW *pw2,
+ __isl_give EL *(*fn)(__isl_take EL *el1, __isl_take EL *el2))
+{
+ isl_space *space;
+
+ if (FN(PW,check_equal_space)(pw1, pw2) < 0)
+ goto error;
+
+ space = isl_space_copy(pw1->dim);
+ return FN(PW,on_shared_domain_in)(pw1, pw2, space, fn);
+error:
+ FN(PW,free)(pw1);
+ FN(PW,free)(pw2);
+ return NULL;
+}
+
+/* Return the parameter domain of "pw".
+ */
+__isl_give isl_set *FN(PW,params)(__isl_take PW *pw)
+{
+ return isl_set_params(FN(PW,domain)(pw));
+}
+
+__isl_give isl_set *FN(PW,domain)(__isl_take PW *pw)
+{
+ int i;
+ isl_set *dom;
+
+ if (!pw)
+ return NULL;
+
+ dom = isl_set_empty(FN(PW,get_domain_space)(pw));
+ for (i = 0; i < pw->n; ++i)
+ dom = isl_set_union_disjoint(dom, isl_set_copy(pw->p[i].set));
+
+ FN(PW,free)(pw);
+
+ return dom;
+}
+
+/* Exploit the equalities in the domain of piece "i" of "pw"
+ * to simplify the associated function.
+ * If the domain of piece "i" is empty, then remove it entirely,
+ * replacing it with the final piece.
+ */
+static int FN(PW,exploit_equalities_and_remove_if_empty)(__isl_keep PW *pw,
+ int i)
+{
+ isl_basic_set *aff;
+ int empty = isl_set_plain_is_empty(pw->p[i].set);
+
+ if (empty < 0)
+ return -1;
+ if (empty) {
+ isl_set_free(pw->p[i].set);
+ FN(EL,free)(pw->p[i].FIELD);
+ if (i != pw->n - 1)
+ pw->p[i] = pw->p[pw->n - 1];
+ pw->n--;
+
+ return 0;
+ }
+
+ aff = isl_set_affine_hull(isl_set_copy(pw->p[i].set));
+ pw->p[i].FIELD = FN(EL,substitute_equalities)(pw->p[i].FIELD, aff);
+ if (!pw->p[i].FIELD)
+ return -1;
+
+ return 0;
+}
+
+/* Convert a piecewise expression defined over a parameter domain
+ * into one that is defined over a zero-dimensional set.
+ */
+__isl_give PW *FN(PW,from_range)(__isl_take PW *pw)
+{
+ isl_space *space;
+
+ if (!pw)
+ return NULL;
+ if (!isl_space_is_set(pw->dim))
+ isl_die(FN(PW,get_ctx)(pw), isl_error_invalid,
+ "not living in a set space", return FN(PW,free)(pw));
+
+ space = FN(PW,get_space)(pw);
+ space = isl_space_from_range(space);
+ pw = FN(PW,reset_space)(pw, space);
+
+ return pw;
+}
+
+/* Fix the value of the given parameter or domain dimension of "pw"
+ * to be equal to "value".
+ */
+__isl_give PW *FN(PW,fix_si)(__isl_take PW *pw, enum isl_dim_type type,
+ unsigned pos, int value)
+{
+ int i;
+
+ if (!pw)
+ return NULL;
+
+ if (type == isl_dim_out)
+ isl_die(FN(PW,get_ctx)(pw), isl_error_invalid,
+ "cannot fix output dimension", return FN(PW,free)(pw));
+
+ if (pw->n == 0)
+ return pw;
+
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ return FN(PW,free)(pw);
+
+ for (i = pw->n - 1; i >= 0; --i) {
+ pw->p[i].set = isl_set_fix_si(pw->p[i].set, type, pos, value);
+ if (FN(PW,exploit_equalities_and_remove_if_empty)(pw, i) < 0)
+ return FN(PW,free)(pw);
+ }
+
+ return pw;
+}
+
+/* Restrict the domain of "pw" by combining each cell
+ * with "set" through a call to "fn", where "fn" may be
+ * isl_set_intersect, isl_set_intersect_params, isl_set_intersect_factor_domain,
+ * isl_set_intersect_factor_range or isl_set_subtract.
+ */
+static __isl_give PW *FN(PW,restrict_domain_aligned)(__isl_take PW *pw,
+ __isl_take isl_set *set,
+ __isl_give isl_set *(*fn)(__isl_take isl_set *set1,
+ __isl_take isl_set *set2))
+{
+ int i;
+
+ if (!pw || !set)
+ goto error;
+
+ if (pw->n == 0) {
+ isl_set_free(set);
+ return pw;
+ }
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ goto error;
+
+ for (i = pw->n - 1; i >= 0; --i) {
+ pw->p[i].set = fn(pw->p[i].set, isl_set_copy(set));
+ if (FN(PW,exploit_equalities_and_remove_if_empty)(pw, i) < 0)
+ goto error;
+ }
+
+ isl_set_free(set);
+ return pw;
+error:
+ isl_set_free(set);
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+__isl_give PW *FN(PW,intersect_domain)(__isl_take PW *pw,
+ __isl_take isl_set *context)
+{
+ FN(PW,align_params_set)(&pw, &context);
+ return FN(PW,restrict_domain_aligned)(pw, context, &isl_set_intersect);
+}
+
+/* Intersect the domain of "pw" with the parameter domain "context".
+ */
+__isl_give PW *FN(PW,intersect_params)(__isl_take PW *pw,
+ __isl_take isl_set *context)
+{
+ FN(PW,align_params_set)(&pw, &context);
+ return FN(PW,restrict_domain_aligned)(pw, context,
+ &isl_set_intersect_params);
+}
+
+/* Given a piecewise expression "pw" with domain in a space [A -> B] and
+ * a set in the space A, intersect the domain with the set.
+ */
+__isl_give PW *FN(PW,intersect_domain_wrapped_domain)(__isl_take PW *pw,
+ __isl_take isl_set *set)
+{
+ FN(PW,align_params_set)(&pw, &set);
+ return FN(PW,restrict_domain_aligned)(pw, set,
+ &isl_set_intersect_factor_domain);
+}
+
+/* Given a piecewise expression "pw" with domain in a space [A -> B] and
+ * a set in the space B, intersect the domain with the set.
+ */
+__isl_give PW *FN(PW,intersect_domain_wrapped_range)(__isl_take PW *pw,
+ __isl_take isl_set *set)
+{
+ FN(PW,align_params_set)(&pw, &set);
+ return FN(PW,restrict_domain_aligned)(pw, set,
+ &isl_set_intersect_factor_range);
+}
+
+/* Subtract "domain' from the domain of "pw".
+ */
+__isl_give PW *FN(PW,subtract_domain)(__isl_take PW *pw,
+ __isl_take isl_set *domain)
+{
+ FN(PW,align_params_set)(&pw, &domain);
+ return FN(PW,restrict_domain_aligned)(pw, domain, &isl_set_subtract);
+}
+
+/* Compute the gist of "pw" with respect to the domain constraints
+ * of "context" for the case where the domain of the last element
+ * of "pw" is equal to "context".
+ * Call "fn_el" to compute the gist of this element, replace
+ * its domain by the universe and drop all other elements
+ * as their domains are necessarily disjoint from "context".
+ */
+static __isl_give PW *FN(PW,gist_last)(__isl_take PW *pw,
+ __isl_take isl_set *context,
+ __isl_give EL *(*fn_el)(__isl_take EL *el, __isl_take isl_set *set))
+{
+ int i;
+ isl_space *space;
+
+ for (i = 0; i < pw->n - 1; ++i) {
+ isl_set_free(pw->p[i].set);
+ FN(EL,free)(pw->p[i].FIELD);
+ }
+ pw->p[0].FIELD = pw->p[pw->n - 1].FIELD;
+ pw->p[0].set = pw->p[pw->n - 1].set;
+ pw->n = 1;
+
+ space = isl_set_get_space(context);
+ pw->p[0].FIELD = fn_el(pw->p[0].FIELD, context);
+ context = isl_set_universe(space);
+ isl_set_free(pw->p[0].set);
+ pw->p[0].set = context;
+
+ if (!pw->p[0].FIELD || !pw->p[0].set)
+ return FN(PW,free)(pw);
+
+ return pw;
+}
+
+/* Compute the gist of "pw" with respect to the domain constraints
+ * of "context". Call "fn_el" to compute the gist of the elements
+ * and "fn_dom" to compute the gist of the domains.
+ *
+ * If the piecewise expression is empty or the context is the universe,
+ * then nothing can be simplified.
+ */
+static __isl_give PW *FN(PW,gist_aligned)(__isl_take PW *pw,
+ __isl_take isl_set *context,
+ __isl_give EL *(*fn_el)(__isl_take EL *el,
+ __isl_take isl_set *set),
+ __isl_give isl_set *(*fn_dom)(__isl_take isl_set *set,
+ __isl_take isl_basic_set *bset))
+{
+ int i;
+ int is_universe;
+ isl_bool aligned;
+ isl_basic_set *hull = NULL;
+
+ if (!pw || !context)
+ goto error;
+
+ if (pw->n == 0) {
+ isl_set_free(context);
+ return pw;
+ }
+
+ is_universe = isl_set_plain_is_universe(context);
+ if (is_universe < 0)
+ goto error;
+ if (is_universe) {
+ isl_set_free(context);
+ return pw;
+ }
+
+ aligned = isl_set_space_has_equal_params(context, pw->dim);
+ if (aligned < 0)
+ goto error;
+ if (!aligned) {
+ pw = FN(PW,align_params)(pw, isl_set_get_space(context));
+ context = isl_set_align_params(context, FN(PW,get_space)(pw));
+ }
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ goto error;
+
+ if (pw->n == 1) {
+ int equal;
+
+ equal = isl_set_plain_is_equal(pw->p[0].set, context);
+ if (equal < 0)
+ goto error;
+ if (equal)
+ return FN(PW,gist_last)(pw, context, fn_el);
+ }
+
+ context = isl_set_compute_divs(context);
+ hull = isl_set_simple_hull(isl_set_copy(context));
+
+ for (i = pw->n - 1; i >= 0; --i) {
+ isl_set *set_i;
+ int empty;
+
+ if (i == pw->n - 1) {
+ int equal;
+ equal = isl_set_plain_is_equal(pw->p[i].set, context);
+ if (equal < 0)
+ goto error;
+ if (equal) {
+ isl_basic_set_free(hull);
+ return FN(PW,gist_last)(pw, context, fn_el);
+ }
+ }
+ set_i = isl_set_intersect(isl_set_copy(pw->p[i].set),
+ isl_set_copy(context));
+ empty = isl_set_plain_is_empty(set_i);
+ pw->p[i].FIELD = fn_el(pw->p[i].FIELD, set_i);
+ pw->p[i].set = fn_dom(pw->p[i].set, isl_basic_set_copy(hull));
+ if (empty < 0 || !pw->p[i].FIELD || !pw->p[i].set)
+ goto error;
+ if (empty) {
+ isl_set_free(pw->p[i].set);
+ FN(EL,free)(pw->p[i].FIELD);
+ if (i != pw->n - 1)
+ pw->p[i] = pw->p[pw->n - 1];
+ pw->n--;
+ }
+ }
+
+ isl_basic_set_free(hull);
+ isl_set_free(context);
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ isl_basic_set_free(hull);
+ isl_set_free(context);
+ return NULL;
+}
+
+__isl_give PW *FN(PW,gist)(__isl_take PW *pw, __isl_take isl_set *context)
+{
+ FN(PW,align_params_set)(&pw, &context);
+ return FN(PW,gist_aligned)(pw, context, &FN(EL,gist),
+ &isl_set_gist_basic_set);
+}
+
+__isl_give PW *FN(PW,gist_params)(__isl_take PW *pw,
+ __isl_take isl_set *context)
+{
+ FN(PW,align_params_set)(&pw, &context);
+ return FN(PW,gist_aligned)(pw, context, &FN(EL,gist_params),
+ &isl_set_gist_params_basic_set);
+}
+
+/* Return -1 if the piece "p1" should be sorted before "p2"
+ * and 1 if it should be sorted after "p2".
+ * Return 0 if they do not need to be sorted in a specific order.
+ *
+ * The two pieces are compared on the basis of their function value expressions.
+ */
+static int FN(PW,sort_field_cmp)(const void *p1, const void *p2, void *arg)
+{
+ struct FN(PW,piece) const *pc1 = p1;
+ struct FN(PW,piece) const *pc2 = p2;
+
+ return FN(EL,plain_cmp)(pc1->FIELD, pc2->FIELD);
+}
+
+/* Sort the pieces of "pw" according to their function value
+ * expressions and then combine pairs of adjacent pieces with
+ * the same such expression.
+ *
+ * The sorting is performed in place because it does not
+ * change the meaning of "pw", but care needs to be
+ * taken not to change any possible other copies of "pw"
+ * in case anything goes wrong.
+ */
+__isl_give PW *FN(PW,sort)(__isl_take PW *pw)
+{
+ int i, j;
+ isl_set *set;
+
+ if (!pw)
+ return NULL;
+ if (pw->n <= 1)
+ return pw;
+ if (isl_sort(pw->p, pw->n, sizeof(pw->p[0]),
+ &FN(PW,sort_field_cmp), NULL) < 0)
+ return FN(PW,free)(pw);
+ for (i = pw->n - 1; i >= 1; --i) {
+ if (!FN(EL,plain_is_equal)(pw->p[i - 1].FIELD, pw->p[i].FIELD))
+ continue;
+ set = isl_set_union(isl_set_copy(pw->p[i - 1].set),
+ isl_set_copy(pw->p[i].set));
+ if (!set)
+ return FN(PW,free)(pw);
+ isl_set_free(pw->p[i].set);
+ FN(EL,free)(pw->p[i].FIELD);
+ isl_set_free(pw->p[i - 1].set);
+ pw->p[i - 1].set = set;
+ for (j = i + 1; j < pw->n; ++j)
+ pw->p[j - 1] = pw->p[j];
+ pw->n--;
+ }
+
+ return pw;
+}
+
+/* Coalesce the domains of "pw".
+ *
+ * Prior to the actual coalescing, first sort the pieces such that
+ * pieces with the same function value expression are combined
+ * into a single piece, the combined domain of which can then
+ * be coalesced.
+ */
+__isl_give PW *FN(PW,coalesce)(__isl_take PW *pw)
+{
+ int i;
+
+ pw = FN(PW,sort)(pw);
+ if (!pw)
+ return NULL;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].set = isl_set_coalesce(pw->p[i].set);
+ if (!pw->p[i].set)
+ goto error;
+ }
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+isl_ctx *FN(PW,get_ctx)(__isl_keep PW *pw)
+{
+ return pw ? isl_space_get_ctx(pw->dim) : NULL;
+}
+
+isl_bool FN(PW,involves_dims)(__isl_keep PW *pw, enum isl_dim_type type,
+ unsigned first, unsigned n)
+{
+ int i;
+ enum isl_dim_type set_type;
+
+ if (!pw)
+ return isl_bool_error;
+ if (pw->n == 0 || n == 0)
+ return isl_bool_false;
+
+ set_type = type == isl_dim_in ? isl_dim_set : type;
+
+ for (i = 0; i < pw->n; ++i) {
+ isl_bool involves = FN(EL,involves_dims)(pw->p[i].FIELD,
+ type, first, n);
+ if (involves < 0 || involves)
+ return involves;
+ involves = isl_set_involves_dims(pw->p[i].set,
+ set_type, first, n);
+ if (involves < 0 || involves)
+ return involves;
+ }
+ return isl_bool_false;
+}
+
+__isl_give PW *FN(PW,set_dim_name)(__isl_take PW *pw,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ int i;
+ enum isl_dim_type set_type;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ return NULL;
+
+ set_type = type == isl_dim_in ? isl_dim_set : type;
+
+ pw->dim = isl_space_set_dim_name(pw->dim, type, pos, s);
+ if (!pw->dim)
+ goto error;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].set = isl_set_set_dim_name(pw->p[i].set,
+ set_type, pos, s);
+ if (!pw->p[i].set)
+ goto error;
+ pw->p[i].FIELD = FN(EL,set_dim_name)(pw->p[i].FIELD, type, pos, s);
+ if (!pw->p[i].FIELD)
+ goto error;
+ }
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+__isl_give PW *FN(PW,drop_dims)(__isl_take PW *pw,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+ enum isl_dim_type set_type;
+
+ if (!pw)
+ return NULL;
+ if (n == 0 && !isl_space_get_tuple_name(pw->dim, type))
+ return pw;
+
+ set_type = type == isl_dim_in ? isl_dim_set : type;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ return NULL;
+ pw->dim = isl_space_drop_dims(pw->dim, type, first, n);
+ if (!pw->dim)
+ goto error;
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].FIELD = FN(EL,drop_dims)(pw->p[i].FIELD, type, first, n);
+ if (!pw->p[i].FIELD)
+ goto error;
+ if (type == isl_dim_out)
+ continue;
+ pw->p[i].set = isl_set_drop(pw->p[i].set, set_type, first, n);
+ if (!pw->p[i].set)
+ goto error;
+ }
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+/* This function is very similar to drop_dims.
+ * The only difference is that the cells may still involve
+ * the specified dimensions. They are removed using
+ * isl_set_project_out instead of isl_set_drop.
+ */
+__isl_give PW *FN(PW,project_out)(__isl_take PW *pw,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+ enum isl_dim_type set_type;
+
+ if (!pw)
+ return NULL;
+ if (n == 0 && !isl_space_get_tuple_name(pw->dim, type))
+ return pw;
+
+ set_type = type == isl_dim_in ? isl_dim_set : type;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ return NULL;
+ pw->dim = isl_space_drop_dims(pw->dim, type, first, n);
+ if (!pw->dim)
+ goto error;
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].set = isl_set_project_out(pw->p[i].set,
+ set_type, first, n);
+ if (!pw->p[i].set)
+ goto error;
+ pw->p[i].FIELD = FN(EL,drop_dims)(pw->p[i].FIELD, type, first, n);
+ if (!pw->p[i].FIELD)
+ goto error;
+ }
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+/* Project the domain of pw onto its parameter space.
+ */
+__isl_give PW *FN(PW,project_domain_on_params)(__isl_take PW *pw)
+{
+ isl_space *space;
+ isl_size n;
+
+ n = FN(PW,dim)(pw, isl_dim_in);
+ if (n < 0)
+ return FN(PW,free)(pw);
+ pw = FN(PW,project_out)(pw, isl_dim_in, 0, n);
+ space = FN(PW,get_domain_space)(pw);
+ space = isl_space_params(space);
+ pw = FN(PW,reset_domain_space)(pw, space);
+ return pw;
+}
+
+/* Drop all parameters not referenced by "pw".
+ */
+__isl_give PW *FN(PW,drop_unused_params)(__isl_take PW *pw)
+{
+ isl_size n;
+ int i;
+
+ if (FN(PW,check_named_params)(pw) < 0)
+ return FN(PW,free)(pw);
+
+ n = FN(PW,dim)(pw, isl_dim_param);
+ if (n < 0)
+ return FN(PW,free)(pw);
+ for (i = n - 1; i >= 0; i--) {
+ isl_bool involves;
+
+ involves = FN(PW,involves_dims)(pw, isl_dim_param, i, 1);
+ if (involves < 0)
+ return FN(PW,free)(pw);
+ if (!involves)
+ pw = FN(PW,drop_dims)(pw, isl_dim_param, i, 1);
+ }
+
+ return pw;
+}
+
+__isl_give PW *FN(PW,fix_dim)(__isl_take PW *pw,
+ enum isl_dim_type type, unsigned pos, isl_int v)
+{
+ int i;
+
+ if (!pw)
+ return NULL;
+
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ return NULL;
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].set = isl_set_fix(pw->p[i].set, type, pos, v);
+ if (FN(PW,exploit_equalities_and_remove_if_empty)(pw, i) < 0)
+ return FN(PW,free)(pw);
+ }
+
+ return pw;
+}
+
+/* Fix the value of the variable at position "pos" of type "type" of "pw"
+ * to be equal to "v".
+ */
+__isl_give PW *FN(PW,fix_val)(__isl_take PW *pw,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_val *v)
+{
+ if (!v)
+ return FN(PW,free)(pw);
+ if (!isl_val_is_int(v))
+ isl_die(FN(PW,get_ctx)(pw), isl_error_invalid,
+ "expecting integer value", goto error);
+
+ pw = FN(PW,fix_dim)(pw, type, pos, v->n);
+ isl_val_free(v);
+
+ return pw;
+error:
+ isl_val_free(v);
+ return FN(PW,free)(pw);
+}
+
+isl_size FN(PW,dim)(__isl_keep PW *pw, enum isl_dim_type type)
+{
+ return isl_space_dim(FN(PW,peek_space)(pw), type);
+}
+
+__isl_give PW *FN(PW,split_dims)(__isl_take PW *pw,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ int i;
+
+ if (!pw)
+ return NULL;
+ if (n == 0)
+ return pw;
+
+ if (type == isl_dim_in)
+ type = isl_dim_set;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ return NULL;
+ if (!pw->dim)
+ goto error;
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].set = isl_set_split_dims(pw->p[i].set, type, first, n);
+ if (!pw->p[i].set)
+ goto error;
+ }
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+/* Return the space of "pw".
+ */
+__isl_keep isl_space *FN(PW,peek_space)(__isl_keep PW *pw)
+{
+ return pw ? pw->dim : NULL;
+}
+
+__isl_give isl_space *FN(PW,get_space)(__isl_keep PW *pw)
+{
+ return isl_space_copy(FN(PW,peek_space)(pw));
+}
+
+/* Return the space of "pw".
+ * This may be either a copy or the space itself
+ * if there is only one reference to "pw".
+ * This allows the space to be modified inplace
+ * if both the piecewise expression and its space have only a single reference.
+ * The caller is not allowed to modify "pw" between this call and
+ * a subsequent call to isl_pw_*_restore_*.
+ * The only exception is that isl_pw_*_free can be called instead.
+ */
+__isl_give isl_space *FN(PW,take_space)(__isl_keep PW *pw)
+{
+ isl_space *space;
+
+ if (!pw)
+ return NULL;
+ if (pw->ref != 1)
+ return FN(PW,get_space)(pw);
+ space = pw->dim;
+ pw->dim = NULL;
+ return space;
+}
+
+/* Set the space of "pw" to "space", where the space of "pw" may be missing
+ * due to a preceding call to isl_pw_*_take_space.
+ * However, in this case, "pw" only has a single reference and
+ * then the call to isl_pw_*_cow has no effect.
+ */
+__isl_give PW *FN(PW,restore_space)(__isl_take PW *pw,
+ __isl_take isl_space *space)
+{
+ if (!pw || !space)
+ goto error;
+
+ if (pw->dim == space) {
+ isl_space_free(space);
+ return pw;
+ }
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ goto error;
+ isl_space_free(pw->dim);
+ pw->dim = space;
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Check that "pos" is a valid position for a cell in "pw".
+ */
+static isl_stat FN(PW,check_pos)(__isl_keep PW *pw, int pos)
+{
+ if (!pw)
+ return isl_stat_error;
+ if (pos < 0 || pos >= pw->n)
+ isl_die(FN(PW,get_ctx)(pw), isl_error_internal,
+ "position out of bounds", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Return the cell at position "pos" in "pw".
+ */
+static __isl_keep isl_set *FN(PW,peek_domain_at)(__isl_keep PW *pw, int pos)
+{
+ if (FN(PW,check_pos)(pw, pos) < 0)
+ return NULL;
+ return pw->p[pos].set;
+}
+
+/* Return a copy of the cell at position "pos" in "pw".
+ */
+__isl_give isl_set *FN(PW,get_domain_at)(__isl_keep PW *pw, int pos)
+{
+ return isl_set_copy(FN(PW,peek_domain_at)(pw, pos));
+}
+
+/* Return the base expression associated to
+ * the cell at position "pos" in "pw".
+ */
+static __isl_keep EL *FN(PW,peek_base_at)(__isl_keep PW *pw, int pos)
+{
+ if (FN(PW,check_pos)(pw, pos) < 0)
+ return NULL;
+ return pw->p[pos].FIELD;
+}
+
+/* Return a copy of the base expression associated to
+ * the cell at position "pos" in "pw".
+ */
+__isl_give EL *FN(PW,get_base_at)(__isl_keep PW *pw, int pos)
+{
+ return FN(EL,copy)(FN(PW,peek_base_at)(pw, pos));
+}
+
+/* Return the base expression associated to
+ * the cell at position "pos" in "pw".
+ * This may be either a copy or the base expression itself
+ * if there is only one reference to "pw".
+ * This allows the base expression to be modified inplace
+ * if both the piecewise expression and this base expression
+ * have only a single reference.
+ * The caller is not allowed to modify "pw" between this call and
+ * a subsequent call to isl_pw_*_restore_*.
+ * The only exception is that isl_pw_*_free can be called instead.
+ */
+__isl_give EL *FN(PW,take_base_at)(__isl_keep PW *pw, int pos)
+{
+ EL *el;
+
+ if (!pw)
+ return NULL;
+ if (pw->ref != 1)
+ return FN(PW,get_base_at)(pw, pos);
+ if (FN(PW,check_pos)(pw, pos) < 0)
+ return NULL;
+ el = pw->p[pos].FIELD;
+ pw->p[pos].FIELD = NULL;
+ return el;
+}
+
+/* Set the base expression associated to
+ * the cell at position "pos" in "pw" to "el",
+ * where this base expression may be missing
+ * due to a preceding call to isl_pw_*_take_base_at.
+ * However, in this case, "pw" only has a single reference and
+ * then the call to isl_pw_*_cow has no effect.
+ */
+__isl_give PW *FN(PW,restore_base_at)(__isl_take PW *pw, int pos,
+ __isl_take EL *el)
+{
+ if (FN(PW,check_pos)(pw, pos) < 0 || !el)
+ goto error;
+
+ if (pw->p[pos].FIELD == el) {
+ FN(EL,free)(el);
+ return pw;
+ }
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ goto error;
+ FN(EL,free)(pw->p[pos].FIELD);
+ pw->p[pos].FIELD = el;
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ FN(EL,free)(el);
+ return NULL;
+}
+
+__isl_give isl_space *FN(PW,get_domain_space)(__isl_keep PW *pw)
+{
+ return pw ? isl_space_domain(isl_space_copy(pw->dim)) : NULL;
+}
+
+/* Return the position of the dimension of the given type and name
+ * in "pw".
+ * Return -1 if no such dimension can be found.
+ */
+int FN(PW,find_dim_by_name)(__isl_keep PW *pw,
+ enum isl_dim_type type, const char *name)
+{
+ if (!pw)
+ return -1;
+ return isl_space_find_dim_by_name(pw->dim, type, name);
+}
+
+/* Return the position of the dimension of the given type and identifier
+ * in "pw".
+ * Return -1 if no such dimension can be found.
+ */
+static int FN(PW,find_dim_by_id)(__isl_keep PW *pw,
+ enum isl_dim_type type, __isl_keep isl_id *id)
+{
+ isl_space *space;
+
+ space = FN(PW,peek_space)(pw);
+ return isl_space_find_dim_by_id(space, type, id);
+}
+
+/* Does the piecewise expression "pw" depend in any way
+ * on the parameter with identifier "id"?
+ */
+isl_bool FN(PW,involves_param_id)(__isl_keep PW *pw, __isl_keep isl_id *id)
+{
+ int pos;
+
+ if (!pw || !id)
+ return isl_bool_error;
+ if (pw->n == 0)
+ return isl_bool_false;
+
+ pos = FN(PW,find_dim_by_id)(pw, isl_dim_param, id);
+ if (pos < 0)
+ return isl_bool_false;
+ return FN(PW,involves_dims)(pw, isl_dim_param, pos, 1);
+}
+
+/* Reset the space of "pw". Since we don't know if the elements
+ * represent the spaces themselves or their domains, we pass along
+ * both when we call their reset_space_and_domain.
+ */
+static __isl_give PW *FN(PW,reset_space_and_domain)(__isl_take PW *pw,
+ __isl_take isl_space *space, __isl_take isl_space *domain)
+{
+ int i;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw || !space || !domain)
+ goto error;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].set = isl_set_reset_space(pw->p[i].set,
+ isl_space_copy(domain));
+ if (!pw->p[i].set)
+ goto error;
+ pw->p[i].FIELD = FN(EL,reset_space_and_domain)(pw->p[i].FIELD,
+ isl_space_copy(space), isl_space_copy(domain));
+ if (!pw->p[i].FIELD)
+ goto error;
+ }
+
+ isl_space_free(domain);
+
+ isl_space_free(pw->dim);
+ pw->dim = space;
+
+ return pw;
+error:
+ isl_space_free(domain);
+ isl_space_free(space);
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+__isl_give PW *FN(PW,reset_domain_space)(__isl_take PW *pw,
+ __isl_take isl_space *domain)
+{
+ isl_space *space;
+
+ space = isl_space_extend_domain_with_range(isl_space_copy(domain),
+ FN(PW,get_space)(pw));
+ return FN(PW,reset_space_and_domain)(pw, space, domain);
+}
+
+__isl_give PW *FN(PW,reset_space)(__isl_take PW *pw,
+ __isl_take isl_space *space)
+{
+ isl_space *domain;
+
+ domain = isl_space_domain(isl_space_copy(space));
+ return FN(PW,reset_space_and_domain)(pw, space, domain);
+}
+
+__isl_give PW *FN(PW,set_tuple_id)(__isl_take PW *pw, enum isl_dim_type type,
+ __isl_take isl_id *id)
+{
+ isl_space *space;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ goto error;
+
+ space = FN(PW,get_space)(pw);
+ space = isl_space_set_tuple_id(space, type, id);
+
+ return FN(PW,reset_space)(pw, space);
+error:
+ isl_id_free(id);
+ return FN(PW,free)(pw);
+}
+
+/* Drop the id on the specified tuple.
+ */
+__isl_give PW *FN(PW,reset_tuple_id)(__isl_take PW *pw, enum isl_dim_type type)
+{
+ isl_space *space;
+
+ if (!pw)
+ return NULL;
+ if (!FN(PW,has_tuple_id)(pw, type))
+ return pw;
+
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ return NULL;
+
+ space = FN(PW,get_space)(pw);
+ space = isl_space_reset_tuple_id(space, type);
+
+ return FN(PW,reset_space)(pw, space);
+}
+
+__isl_give PW *FN(PW,set_dim_id)(__isl_take PW *pw,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id)
+{
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ goto error;
+ pw->dim = isl_space_set_dim_id(pw->dim, type, pos, id);
+ return FN(PW,reset_space)(pw, isl_space_copy(pw->dim));
+error:
+ isl_id_free(id);
+ return FN(PW,free)(pw);
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * of the space of "pw".
+ */
+__isl_give PW *FN(PW,reset_user)(__isl_take PW *pw)
+{
+ isl_space *space;
+
+ space = FN(PW,get_space)(pw);
+ space = isl_space_reset_user(space);
+
+ return FN(PW,reset_space)(pw, space);
+}
+
+isl_size FN(PW,n_piece)(__isl_keep PW *pw)
+{
+ return pw ? pw->n : isl_size_error;
+}
+
+isl_stat FN(PW,foreach_piece)(__isl_keep PW *pw,
+ isl_stat (*fn)(__isl_take isl_set *set, __isl_take EL *el, void *user),
+ void *user)
+{
+ int i;
+
+ if (!pw)
+ return isl_stat_error;
+
+ for (i = 0; i < pw->n; ++i)
+ if (fn(isl_set_copy(pw->p[i].set),
+ FN(EL,copy)(pw->p[i].FIELD), user) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Does "test" succeed on every cell of "pw"?
+ */
+isl_bool FN(PW,every_piece)(__isl_keep PW *pw,
+ isl_bool (*test)(__isl_keep isl_set *set,
+ __isl_keep EL *el, void *user), void *user)
+{
+ int i;
+
+ if (!pw)
+ return isl_bool_error;
+
+ for (i = 0; i < pw->n; ++i) {
+ isl_bool r;
+
+ r = test(pw->p[i].set, pw->p[i].FIELD, user);
+ if (r < 0 || !r)
+ return r;
+ }
+
+ return isl_bool_true;
+}
+
+/* Is "pw" defined over a single universe domain?
+ *
+ * If the default value of this piecewise type is zero,
+ * then a "pw" with a zero number of cells is also accepted
+ * as it represents the default zero value.
+ */
+isl_bool FN(FN(PW,isa),BASE)(__isl_keep PW *pw)
+{
+ isl_size n;
+
+ n = FN(PW,n_piece)(pw);
+ if (n < 0)
+ return isl_bool_error;
+ if (DEFAULT_IS_ZERO && n == 0)
+ return isl_bool_true;
+ if (n != 1)
+ return isl_bool_false;
+ return isl_set_plain_is_universe(FN(PW,peek_domain_at)(pw, 0));
+}
+
+/* Return a zero base expression in the same space (and of the same type)
+ * as "pw".
+ */
+static __isl_give EL *FN(EL,zero_like_type)(__isl_take PW *pw OPT_TYPE_PARAM)
+{
+ isl_space *space;
+
+ space = FN(PW,get_space)(pw);
+ FN(PW,free)(pw);
+ return FN(EL,zero_in_space)(space OPT_TYPE_ARG(NO_LOC));
+}
+
+#ifndef HAS_TYPE
+/* Return a zero base expression in the same space as "pw".
+ */
+static __isl_give EL *FN(EL,zero_like)(__isl_take PW *pw)
+{
+ return FN(EL,zero_like_type)(pw);
+}
+#else
+/* Return a zero base expression in the same space and of the same type
+ * as "pw".
+ *
+ * Pass along the type as an explicit argument for uniform handling
+ * in isl_*_zero_like_type.
+ */
+static __isl_give EL *FN(EL,zero_like)(__isl_take PW *pw)
+{
+ enum isl_fold type;
+
+ type = FN(PW,get_type)(pw);
+ if (type < 0)
+ goto error;
+ return FN(EL,zero_like_type)(pw, type);
+error:
+ FN(PW,free)(pw);
+ return NULL;
+}
+#endif
+
+/* Given that "pw" is defined over a single universe domain,
+ * return the base expression associated to this domain.
+ *
+ * If the number of cells is zero, then "pw" is of a piecewise type
+ * with a default zero value and effectively represents zero.
+ * In this case, create a zero base expression in the same space
+ * (and with the same type).
+ * Otherwise, simply extract the associated base expression.
+ */
+__isl_give EL *FN(FN(PW,as),BASE)(__isl_take PW *pw)
+{
+ isl_bool is_total;
+ isl_size n;
+ EL *el;
+
+ is_total = FN(FN(PW,isa),BASE)(pw);
+ if (is_total < 0)
+ goto error;
+ if (!is_total)
+ isl_die(FN(PW,get_ctx)(pw), isl_error_invalid,
+ "expecting single total function", goto error);
+ n = FN(PW,n_piece)(pw);
+ if (n < 0)
+ goto error;
+ if (n == 0)
+ return FN(EL,zero_like)(pw);
+ el = FN(PW,take_base_at)(pw, 0);
+ FN(PW,free)(pw);
+ return el;
+error:
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+#ifdef HAS_TYPE
+/* Negate the type of "pw".
+ */
+static __isl_give PW *FN(PW,negate_type)(__isl_take PW *pw)
+{
+ pw = FN(PW,cow)(pw);
+ if (!pw)
+ return NULL;
+ pw->type = isl_fold_type_negate(pw->type);
+ return pw;
+}
+#else
+/* Negate the type of "pw".
+ * Since "pw" does not have a type, do nothing.
+ */
+static __isl_give PW *FN(PW,negate_type)(__isl_take PW *pw)
+{
+ return pw;
+}
+#endif
+
+__isl_give PW *FN(PW,mul_isl_int)(__isl_take PW *pw, isl_int v)
+{
+ int i;
+
+ if (isl_int_is_one(v))
+ return pw;
+ if (pw && DEFAULT_IS_ZERO && isl_int_is_zero(v)) {
+ PW *zero;
+ isl_space *space = FN(PW,get_space)(pw);
+ zero = FN(PW,ZERO)(space OPT_TYPE_ARG(pw->));
+ FN(PW,free)(pw);
+ return zero;
+ }
+ pw = FN(PW,cow)(pw);
+ if (isl_int_is_neg(v))
+ pw = FN(PW,negate_type)(pw);
+ if (!pw)
+ return NULL;
+ if (pw->n == 0)
+ return pw;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].FIELD = FN(EL,scale)(pw->p[i].FIELD, v);
+ if (!pw->p[i].FIELD)
+ goto error;
+ }
+
+ return pw;
+error:
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+/* Multiply the pieces of "pw" by "v" and return the result.
+ */
+__isl_give PW *FN(PW,scale_val)(__isl_take PW *pw, __isl_take isl_val *v)
+{
+ int i;
+
+ if (!pw || !v)
+ goto error;
+
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return pw;
+ }
+ if (pw && DEFAULT_IS_ZERO && isl_val_is_zero(v)) {
+ PW *zero;
+ isl_space *space = FN(PW,get_space)(pw);
+ zero = FN(PW,ZERO)(space OPT_TYPE_ARG(pw->));
+ FN(PW,free)(pw);
+ isl_val_free(v);
+ return zero;
+ }
+ if (pw->n == 0) {
+ isl_val_free(v);
+ return pw;
+ }
+ pw = FN(PW,cow)(pw);
+ if (isl_val_is_neg(v))
+ pw = FN(PW,negate_type)(pw);
+ if (!pw)
+ goto error;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].FIELD = FN(EL,scale_val)(pw->p[i].FIELD,
+ isl_val_copy(v));
+ if (!pw->p[i].FIELD)
+ goto error;
+ }
+
+ isl_val_free(v);
+ return pw;
+error:
+ isl_val_free(v);
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+/* Divide the pieces of "pw" by "v" and return the result.
+ */
+__isl_give PW *FN(PW,scale_down_val)(__isl_take PW *pw, __isl_take isl_val *v)
+{
+ int i;
+
+ if (!pw || !v)
+ goto error;
+
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return pw;
+ }
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational factor", goto error);
+ if (isl_val_is_zero(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "cannot scale down by zero", goto error);
+
+ if (pw->n == 0) {
+ isl_val_free(v);
+ return pw;
+ }
+ pw = FN(PW,cow)(pw);
+ if (isl_val_is_neg(v))
+ pw = FN(PW,negate_type)(pw);
+ if (!pw)
+ goto error;
+
+ for (i = 0; i < pw->n; ++i) {
+ pw->p[i].FIELD = FN(EL,scale_down_val)(pw->p[i].FIELD,
+ isl_val_copy(v));
+ if (!pw->p[i].FIELD)
+ goto error;
+ }
+
+ isl_val_free(v);
+ return pw;
+error:
+ isl_val_free(v);
+ FN(PW,free)(pw);
+ return NULL;
+}
+
+__isl_give PW *FN(PW,scale)(__isl_take PW *pw, isl_int v)
+{
+ return FN(PW,mul_isl_int)(pw, v);
+}
+
+/* Apply some normalization to "pw".
+ * In particular, sort the pieces according to their function value
+ * expressions, combining pairs of adjacent pieces with
+ * the same such expression, and then normalize the domains of the pieces.
+ *
+ * We normalize in place, but if anything goes wrong we need
+ * to return NULL, so we need to make sure we don't change the
+ * meaning of any possible other copies of "pw".
+ */
+__isl_give PW *FN(PW,normalize)(__isl_take PW *pw)
+{
+ int i;
+ isl_set *set;
+
+ pw = FN(PW,sort)(pw);
+ if (!pw)
+ return NULL;
+ for (i = 0; i < pw->n; ++i) {
+ set = isl_set_normalize(isl_set_copy(pw->p[i].set));
+ if (!set)
+ return FN(PW,free)(pw);
+ isl_set_free(pw->p[i].set);
+ pw->p[i].set = set;
+ }
+
+ return pw;
+}
+
+/* Is pw1 obviously equal to pw2?
+ * That is, do they have obviously identical cells and obviously identical
+ * elements on each cell?
+ *
+ * If "pw1" or "pw2" contain any NaNs, then they are considered
+ * not to be the same. A NaN is not equal to anything, not even
+ * to another NaN.
+ */
+isl_bool FN(PW,plain_is_equal)(__isl_keep PW *pw1, __isl_keep PW *pw2)
+{
+ int i;
+ isl_bool equal, has_nan;
+
+ if (!pw1 || !pw2)
+ return isl_bool_error;
+
+ has_nan = FN(PW,involves_nan)(pw1);
+ if (has_nan >= 0 && !has_nan)
+ has_nan = FN(PW,involves_nan)(pw2);
+ if (has_nan < 0 || has_nan)
+ return isl_bool_not(has_nan);
+
+ if (pw1 == pw2)
+ return isl_bool_true;
+ equal = FN(PW,has_equal_space)(pw1, pw2);
+ if (equal < 0 || !equal)
+ return equal;
+
+ pw1 = FN(PW,copy)(pw1);
+ pw2 = FN(PW,copy)(pw2);
+ pw1 = FN(PW,normalize)(pw1);
+ pw2 = FN(PW,normalize)(pw2);
+ if (!pw1 || !pw2)
+ goto error;
+
+ equal = isl_bool_ok(pw1->n == pw2->n);
+ for (i = 0; equal && i < pw1->n; ++i) {
+ equal = isl_set_plain_is_equal(pw1->p[i].set, pw2->p[i].set);
+ if (equal < 0)
+ goto error;
+ if (!equal)
+ break;
+ equal = FN(EL,plain_is_equal)(pw1->p[i].FIELD, pw2->p[i].FIELD);
+ if (equal < 0)
+ goto error;
+ }
+
+ FN(PW,free)(pw1);
+ FN(PW,free)(pw2);
+ return equal;
+error:
+ FN(PW,free)(pw1);
+ FN(PW,free)(pw2);
+ return isl_bool_error;
+}
+
+/* Does "pw" involve any NaNs?
+ */
+isl_bool FN(PW,involves_nan)(__isl_keep PW *pw)
+{
+ int i;
+
+ if (!pw)
+ return isl_bool_error;
+ if (pw->n == 0)
+ return isl_bool_false;
+
+ for (i = 0; i < pw->n; ++i) {
+ isl_bool has_nan = FN(EL,involves_nan)(pw->p[i].FIELD);
+ if (has_nan < 0 || has_nan)
+ return has_nan;
+ }
+
+ return isl_bool_false;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_templ.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_templ.h
new file mode 100644
index 00000000000..98db44af2ff
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_templ.h
@@ -0,0 +1,5 @@
+#include <isl/space.h>
+
+#include <isl_pw_macro.h>
+
+__isl_keep isl_space *FN(PW,peek_space)(__isl_keep PW *pw);
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_union_opt.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_union_opt.c
new file mode 100644
index 00000000000..d0fc38ce51f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_pw_union_opt.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright 2011 INRIA Saclay
+ * Copyright 2012 Ecole Normale Superieure
+ * Copyright 2020 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ * and Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <isl_pw_macro.h>
+
+/* Given a function "cmp" that returns the set of elements where
+ * "el1" is "better" than "el2", return this set.
+ */
+static __isl_give isl_set *FN(PW,better)(__isl_keep EL *el1, __isl_keep EL *el2,
+ __isl_give isl_set *(*cmp)(__isl_take EL *el1, __isl_take EL *el2))
+{
+ return cmp(FN(EL,copy)(el1), FN(EL,copy)(el2));
+}
+
+/* Return a list containing the domains of the pieces of "pw".
+ */
+static __isl_give isl_set_list *FN(PW,extract_domains)(__isl_keep PW *pw)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_set_list *list;
+
+ if (!pw)
+ return NULL;
+ ctx = FN(PW,get_ctx)(pw);
+ list = isl_set_list_alloc(ctx, pw->n);
+ for (i = 0; i < pw->n; ++i)
+ list = isl_set_list_add(list, isl_set_copy(pw->p[i].set));
+
+ return list;
+}
+
+/* Given sets B ("set"), C ("better") and A' ("out"), return
+ *
+ * (B \cap C) \cup ((B \setminus C) \setminus A')
+ */
+static __isl_give isl_set *FN(PW,better_or_out)(__isl_take isl_set *set,
+ __isl_take isl_set *better, __isl_take isl_set *out)
+{
+ isl_set *set_better, *set_out;
+
+ set_better = isl_set_intersect(isl_set_copy(set), isl_set_copy(better));
+ set_out = isl_set_subtract(isl_set_subtract(set, better), out);
+
+ return isl_set_union(set_better, set_out);
+}
+
+/* Given sets A ("set"), C ("better") and B' ("out"), return
+ *
+ * (A \setminus C) \cup ((A \cap C) \setminus B')
+ */
+static __isl_give isl_set *FN(PW,worse_or_out)(__isl_take isl_set *set,
+ __isl_take isl_set *better, __isl_take isl_set *out)
+{
+ isl_set *set_worse, *set_out;
+
+ set_worse = isl_set_subtract(isl_set_copy(set), isl_set_copy(better));
+ set_out = isl_set_subtract(isl_set_intersect(set, better), out);
+
+ return isl_set_union(set_worse, set_out);
+}
+
+/* Internal data structure used by isl_pw_*_union_opt_cmp
+ * that keeps track of a piecewise expression with updated cells.
+ * "pw" holds the original piecewise expression.
+ * "list" holds the updated cells.
+ */
+S(PW,union_opt_cmp_data) {
+ PW *pw;
+ isl_set_list *cell;
+};
+
+/* Free all memory allocated for "data".
+ */
+static void FN(PW,union_opt_cmp_data_clear)(S(PW,union_opt_cmp_data) *data)
+{
+ isl_set_list_free(data->cell);
+ FN(PW,free)(data->pw);
+}
+
+/* Given (potentially) updated cells "i" of data_i->pw and "j" of data_j->pw and
+ * a set "better" where the piece from data_j->pw is better
+ * than the piece from data_i->pw,
+ * (further) update the specified cells such that only the better elements
+ * remain on the (non-empty) intersection.
+ *
+ * Let C be the set "better".
+ * Let A be the cell data_i->cell[i] and B the cell data_j->cell[j].
+ *
+ * The elements in C need to be removed from A, except for those parts
+ * that lie outside of B. That is,
+ *
+ * A <- (A \setminus C) \cup ((A \cap C) \setminus B')
+ *
+ * Conversely, the elements in B need to be restricted to C, except
+ * for those parts that lie outside of A. That is
+ *
+ * B <- (B \cap C) \cup ((B \setminus C) \setminus A')
+ *
+ * Since all pairs of pieces are considered, the domains are updated
+ * several times. A and B refer to these updated domains
+ * (kept track of in data_i->cell[i] and data_j->cell[j]), while A' and B' refer
+ * to the original domains of the pieces. It is safe to use these
+ * original domains because the difference between, say, A' and A is
+ * the domains of pw2-pieces that have been removed before and
+ * those domains are disjoint from B. A' is used instead of A
+ * because the continued updating of A may result in this domain
+ * getting broken up into more disjuncts.
+ */
+static isl_stat FN(PW,union_opt_cmp_split)(S(PW,union_opt_cmp_data) *data_i,
+ int i, S(PW,union_opt_cmp_data) *data_j, int j,
+ __isl_take isl_set *better)
+{
+ isl_set *set_i, *set_j;
+
+ set_i = isl_set_list_get_set(data_i->cell, i);
+ set_j = FN(PW,get_domain_at)(data_j->pw, j);
+ set_i = FN(PW,worse_or_out)(set_i, isl_set_copy(better), set_j);
+ data_i->cell = isl_set_list_set_set(data_i->cell, i, set_i);
+ set_i = FN(PW,get_domain_at)(data_i->pw, i);
+ set_j = isl_set_list_get_set(data_j->cell, j);
+ set_j = FN(PW,better_or_out)(set_j, better, set_i);
+ data_j->cell = isl_set_list_set_set(data_j->cell, j, set_j);
+
+ return isl_stat_ok;
+}
+
+/* Given (potentially) updated cells "i" of data_i->pw and "j" of data_j->pw and
+ * a function "cmp" that returns the set of elements where
+ * "el1" is "better" than "el2",
+ * (further) update the specified cells such that only the "better" elements
+ * remain on the (non-empty) intersection.
+ */
+static isl_stat FN(PW,union_opt_cmp_pair)(S(PW,union_opt_cmp_data) *data_i,
+ int i, S(PW,union_opt_cmp_data) *data_j, int j,
+ __isl_give isl_set *(*cmp)(__isl_take EL *el1, __isl_take EL *el2))
+{
+ isl_set *better;
+ EL *el_i, *el_j;
+
+ el_i = FN(PW,peek_base_at)(data_i->pw, i);
+ el_j = FN(PW,peek_base_at)(data_j->pw, j);
+ better = FN(PW,better)(el_j, el_i, cmp);
+ return FN(PW,union_opt_cmp_split)(data_i, i, data_j, j, better);
+}
+
+/* Given (potentially) updated cells "i" of data_i->pw and "j" of data_j->pw and
+ * a function "cmp" that returns the set of elements where
+ * "el1" is "better" than "el2",
+ * (further) update the specified cells such that only the "better" elements
+ * remain on the (non-empty) intersection.
+ *
+ * The base computation is performed by isl_pw_*_union_opt_cmp_pair,
+ * which splits the cells according to the set of elements
+ * where the piece from data_j->pw is better than the piece from data_i->pw.
+ *
+ * In some cases, there may be a subset of the intersection
+ * where both pieces have the same value and can therefore
+ * both be considered to be "better" than the other.
+ * This can result in unnecessary splitting on this subset.
+ * Avoid some of these cases by checking whether
+ * data_i->pw is always better than data_j->pw on the intersection.
+ * In particular, do this for the special case where this intersection
+ * is equal to the cell "j" and data_i->pw is better on its entire cell.
+ *
+ * Similarly, if data_i->pw is never better than data_j->pw,
+ * then no splitting will occur and there is no need to check
+ * where data_j->pw is better than data_i->pw.
+ */
+static isl_stat FN(PW,union_opt_cmp_two)(S(PW,union_opt_cmp_data) *data_i,
+ int i, S(PW,union_opt_cmp_data) *data_j, int j,
+ __isl_give isl_set *(*cmp)(__isl_take EL *el1, __isl_take EL *el2))
+{
+ isl_bool is_subset, is_empty;
+ isl_set *better, *set_i, *set_j;
+ EL *el_i, *el_j;
+
+ set_i = FN(PW,peek_domain_at)(data_i->pw, i);
+ set_j = FN(PW,peek_domain_at)(data_j->pw, j);
+ is_subset = isl_set_is_subset(set_j, set_i);
+ if (is_subset < 0)
+ return isl_stat_error;
+ if (!is_subset)
+ return FN(PW,union_opt_cmp_pair)(data_i, i, data_j, j, cmp);
+
+ el_i = FN(PW,peek_base_at)(data_i->pw, i);
+ el_j = FN(PW,peek_base_at)(data_j->pw, j);
+ better = FN(PW,better)(el_i, el_j, cmp);
+ is_empty = isl_set_is_empty(better);
+ if (is_empty >= 0 && is_empty)
+ return FN(PW,union_opt_cmp_split)(data_j, j, data_i, i, better);
+ is_subset = isl_set_is_subset(set_i, better);
+ if (is_subset >= 0 && is_subset)
+ return FN(PW,union_opt_cmp_split)(data_j, j, data_i, i, better);
+ isl_set_free(better);
+ if (is_empty < 0 || is_subset < 0)
+ return isl_stat_error;
+
+ return FN(PW,union_opt_cmp_pair)(data_i, i, data_j, j, cmp);
+}
+
+/* Given two piecewise expressions data1->pw and data2->pw, replace
+ * their domains
+ * by the sets in data1->cell and data2->cell and combine the results into
+ * a single piecewise expression.
+ * The pieces of data1->pw and data2->pw are assumed to have been sorted
+ * according to the function value expressions.
+ * The pieces of the result are also sorted in this way.
+ *
+ * Run through the pieces of data1->pw and data2->pw in order until they
+ * have both been exhausted, picking the piece from data1->pw or data2->pw
+ * depending on which should come first, together with the corresponding
+ * domain from data1->cell or data2->cell. In cases where the next pieces
+ * in both data1->pw and data2->pw have the same function value expression,
+ * construct only a single piece in the result with as domain
+ * the union of the domains in data1->cell and data2->cell.
+ */
+static __isl_give PW *FN(PW,merge)(S(PW,union_opt_cmp_data) *data1,
+ S(PW,union_opt_cmp_data) *data2)
+{
+ int i, j;
+ PW *res;
+ PW *pw1 = data1->pw;
+ PW *pw2 = data2->pw;
+ isl_set_list *list1 = data1->cell;
+ isl_set_list *list2 = data2->cell;
+
+ if (!pw1 || !pw2)
+ return NULL;
+
+ res = FN(PW,alloc_size)(isl_space_copy(pw1->dim), pw1->n + pw2->n);
+
+ i = 0; j = 0;
+ while (i < pw1->n || j < pw2->n) {
+ int cmp;
+ isl_set *set;
+ EL *el;
+
+ if (i < pw1->n && j < pw2->n)
+ cmp = FN(EL,plain_cmp)(pw1->p[i].FIELD,
+ pw2->p[j].FIELD);
+ else
+ cmp = i < pw1->n ? -1 : 1;
+
+ if (cmp < 0) {
+ set = isl_set_list_get_set(list1, i);
+ el = FN(EL,copy)(pw1->p[i].FIELD);
+ ++i;
+ } else if (cmp > 0) {
+ set = isl_set_list_get_set(list2, j);
+ el = FN(EL,copy)(pw2->p[j].FIELD);
+ ++j;
+ } else {
+ set = isl_set_union(isl_set_list_get_set(list1, i),
+ isl_set_list_get_set(list2, j));
+ el = FN(EL,copy)(pw1->p[i].FIELD);
+ ++i;
+ ++j;
+ }
+ res = FN(PW,add_piece)(res, set, el);
+ }
+
+ return res;
+}
+
+/* Given a function "cmp" that returns the set of elements where
+ * "el1" is "better" than "el2", return a piecewise
+ * expression defined on the union of the definition domains
+ * of "pw1" and "pw2" that maps to the "best" of "pw1" and
+ * "pw2" on each cell. If only one of the two input functions
+ * is defined on a given cell, then it is considered the best.
+ *
+ * Run through all pairs of pieces in "pw1" and "pw2".
+ * If the domains of these pieces intersect, then the intersection
+ * needs to be distributed over the two pieces based on "cmp".
+ *
+ * After the updated domains have been computed, the result is constructed
+ * from "pw1", "pw2", data[0].cell and data[1].cell. If there are any pieces
+ * in "pw1" and "pw2" with the same function value expression, then
+ * they are combined into a single piece in the result.
+ * In order to be able to do this efficiently, the pieces of "pw1" and
+ * "pw2" are first sorted according to their function value expressions.
+ */
+static __isl_give PW *FN(PW,union_opt_cmp)(
+ __isl_take PW *pw1, __isl_take PW *pw2,
+ __isl_give isl_set *(*cmp)(__isl_take EL *el1, __isl_take EL *el2))
+{
+ S(PW,union_opt_cmp_data) data[2] = { { pw1, NULL }, { pw2, NULL } };
+ int i, j;
+ isl_size n1, n2;
+ PW *res = NULL;
+ isl_ctx *ctx;
+
+ if (!pw1 || !pw2)
+ goto error;
+
+ ctx = isl_space_get_ctx(pw1->dim);
+ if (!isl_space_is_equal(pw1->dim, pw2->dim))
+ isl_die(ctx, isl_error_invalid,
+ "arguments should live in the same space", goto error);
+
+ if (FN(PW,is_empty)(pw1)) {
+ FN(PW,free)(pw1);
+ return pw2;
+ }
+
+ if (FN(PW,is_empty)(pw2)) {
+ FN(PW,free)(pw2);
+ return pw1;
+ }
+
+ for (i = 0; i < 2; ++i) {
+ data[i].pw = FN(PW,sort)(data[i].pw);
+ data[i].cell = FN(PW,extract_domains)(data[i].pw);
+ }
+
+ n1 = FN(PW,n_piece)(data[0].pw);
+ n2 = FN(PW,n_piece)(data[1].pw);
+ if (n1 < 0 || n2 < 0)
+ goto error;
+ for (i = 0; i < n1; ++i) {
+ for (j = 0; j < n2; ++j) {
+ isl_bool disjoint;
+ isl_set *set_i, *set_j;
+
+ set_i = FN(PW,peek_domain_at)(data[0].pw, i);
+ set_j = FN(PW,peek_domain_at)(data[1].pw, j);
+ disjoint = isl_set_is_disjoint(set_i, set_j);
+ if (disjoint < 0)
+ goto error;
+ if (disjoint)
+ continue;
+ if (FN(PW,union_opt_cmp_two)(&data[0], i,
+ &data[1], j, cmp) < 0)
+ goto error;
+ }
+ }
+
+ res = FN(PW,merge)(&data[0], &data[1]);
+ for (i = 0; i < 2; ++i)
+ FN(PW,union_opt_cmp_data_clear)(&data[i]);
+
+ return res;
+error:
+ for (i = 0; i < 2; ++i)
+ FN(PW,union_opt_cmp_data_clear)(&data[i]);
+ return FN(PW,free)(res);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_range.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_range.c
new file mode 100644
index 00000000000..5bcfcffb7f4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_range.c
@@ -0,0 +1,562 @@
+#include <isl_ctx_private.h>
+#include <isl/val.h>
+#include <isl_constraint_private.h>
+#include <isl/set.h>
+#include <isl_polynomial_private.h>
+#include <isl_morph.h>
+#include <isl_range.h>
+
+struct range_data {
+ struct isl_bound *bound;
+ int *signs;
+ int sign;
+ int test_monotonicity;
+ int monotonicity;
+ int tight;
+ isl_qpolynomial *poly;
+ isl_pw_qpolynomial_fold *pwf;
+ isl_pw_qpolynomial_fold *pwf_tight;
+};
+
+static isl_stat propagate_on_domain(__isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, struct range_data *data);
+
+/* Check whether the polynomial "poly" has sign "sign" over "bset",
+ * i.e., if sign == 1, check that the lower bound on the polynomial
+ * is non-negative and if sign == -1, check that the upper bound on
+ * the polynomial is non-positive.
+ */
+static isl_bool has_sign(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_qpolynomial *poly, int sign, int *signs)
+{
+ struct range_data data_m;
+ isl_size nparam;
+ isl_space *space;
+ isl_val *opt;
+ isl_bool r;
+ enum isl_fold type;
+
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ if (nparam < 0)
+ return isl_bool_error;
+
+ bset = isl_basic_set_copy(bset);
+ poly = isl_qpolynomial_copy(poly);
+
+ bset = isl_basic_set_move_dims(bset, isl_dim_set, 0,
+ isl_dim_param, 0, nparam);
+ poly = isl_qpolynomial_move_dims(poly, isl_dim_in, 0,
+ isl_dim_param, 0, nparam);
+
+ space = isl_qpolynomial_get_space(poly);
+ space = isl_space_params(space);
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, 1);
+
+ data_m.test_monotonicity = 0;
+ data_m.signs = signs;
+ data_m.sign = -sign;
+ type = data_m.sign < 0 ? isl_fold_min : isl_fold_max;
+ data_m.pwf = isl_pw_qpolynomial_fold_zero(space, type);
+ data_m.tight = 0;
+ data_m.pwf_tight = NULL;
+
+ if (propagate_on_domain(bset, poly, &data_m) < 0)
+ goto error;
+
+ if (sign > 0)
+ opt = isl_pw_qpolynomial_fold_min(data_m.pwf);
+ else
+ opt = isl_pw_qpolynomial_fold_max(data_m.pwf);
+
+ if (!opt)
+ r = isl_bool_error;
+ else if (isl_val_is_nan(opt) ||
+ isl_val_is_infty(opt) ||
+ isl_val_is_neginfty(opt))
+ r = isl_bool_false;
+ else
+ r = isl_bool_ok(sign * isl_val_sgn(opt) >= 0);
+
+ isl_val_free(opt);
+
+ return r;
+error:
+ isl_pw_qpolynomial_fold_free(data_m.pwf);
+ return isl_bool_error;
+}
+
+/* Return 1 if poly is monotonically increasing in the last set variable,
+ * -1 if poly is monotonically decreasing in the last set variable,
+ * 0 if no conclusion,
+ * -2 on error.
+ *
+ * We simply check the sign of p(x+1)-p(x)
+ */
+static int monotonicity(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_qpolynomial *poly, struct range_data *data)
+{
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_qpolynomial *sub = NULL;
+ isl_qpolynomial *diff = NULL;
+ int result = 0;
+ isl_bool s;
+ isl_size nvar;
+
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+ if (nvar < 0)
+ return -2;
+
+ ctx = isl_qpolynomial_get_ctx(poly);
+ space = isl_qpolynomial_get_domain_space(poly);
+
+ sub = isl_qpolynomial_var_on_domain(isl_space_copy(space),
+ isl_dim_set, nvar - 1);
+ sub = isl_qpolynomial_add(sub,
+ isl_qpolynomial_rat_cst_on_domain(space, ctx->one, ctx->one));
+
+ diff = isl_qpolynomial_substitute(isl_qpolynomial_copy(poly),
+ isl_dim_in, nvar - 1, 1, &sub);
+ diff = isl_qpolynomial_sub(diff, isl_qpolynomial_copy(poly));
+
+ s = has_sign(bset, diff, 1, data->signs);
+ if (s < 0)
+ goto error;
+ if (s)
+ result = 1;
+ else {
+ s = has_sign(bset, diff, -1, data->signs);
+ if (s < 0)
+ goto error;
+ if (s)
+ result = -1;
+ }
+
+ isl_qpolynomial_free(diff);
+ isl_qpolynomial_free(sub);
+
+ return result;
+error:
+ isl_qpolynomial_free(diff);
+ isl_qpolynomial_free(sub);
+ return -2;
+}
+
+/* Return a positive ("sign" > 0) or negative ("sign" < 0) infinite polynomial
+ * with domain space "space".
+ */
+static __isl_give isl_qpolynomial *signed_infty(__isl_take isl_space *space,
+ int sign)
+{
+ if (sign > 0)
+ return isl_qpolynomial_infty_on_domain(space);
+ else
+ return isl_qpolynomial_neginfty_on_domain(space);
+}
+
+static __isl_give isl_qpolynomial *bound2poly(__isl_take isl_constraint *bound,
+ __isl_take isl_space *space, unsigned pos, int sign)
+{
+ if (!bound)
+ return signed_infty(space, sign);
+ isl_space_free(space);
+ return isl_qpolynomial_from_constraint(bound, isl_dim_set, pos);
+}
+
+static int bound_is_integer(__isl_keep isl_constraint *bound, unsigned pos)
+{
+ isl_int c;
+ int is_int;
+
+ if (!bound)
+ return 1;
+
+ isl_int_init(c);
+ isl_constraint_get_coefficient(bound, isl_dim_set, pos, &c);
+ is_int = isl_int_is_one(c) || isl_int_is_negone(c);
+ isl_int_clear(c);
+
+ return is_int;
+}
+
+struct isl_fixed_sign_data {
+ int *signs;
+ int sign;
+ isl_qpolynomial *poly;
+};
+
+/* Add term "term" to data->poly if it has sign data->sign.
+ * The sign is determined based on the signs of the parameters
+ * and variables in data->signs. The integer divisions, if
+ * any, are assumed to be non-negative.
+ */
+static isl_stat collect_fixed_sign_terms(__isl_take isl_term *term, void *user)
+{
+ struct isl_fixed_sign_data *data = (struct isl_fixed_sign_data *)user;
+ isl_int n;
+ int i;
+ int sign;
+ isl_size nparam;
+ isl_size nvar;
+ isl_size exp;
+
+ nparam = isl_term_dim(term, isl_dim_param);
+ nvar = isl_term_dim(term, isl_dim_set);
+ if (nparam < 0 || nvar < 0)
+ return isl_stat_error;
+
+ isl_int_init(n);
+ isl_term_get_num(term, &n);
+ sign = isl_int_sgn(n);
+ isl_int_clear(n);
+
+ for (i = 0; i < nparam; ++i) {
+ if (data->signs[i] > 0)
+ continue;
+ exp = isl_term_get_exp(term, isl_dim_param, i);
+ if (exp < 0)
+ return isl_stat_error;
+ if (exp % 2)
+ sign = -sign;
+ }
+ for (i = 0; i < nvar; ++i) {
+ if (data->signs[nparam + i] > 0)
+ continue;
+ exp = isl_term_get_exp(term, isl_dim_set, i);
+ if (exp < 0)
+ return isl_stat_error;
+ if (exp % 2)
+ sign = -sign;
+ }
+
+ if (sign == data->sign) {
+ isl_qpolynomial *t = isl_qpolynomial_from_term(term);
+
+ data->poly = isl_qpolynomial_add(data->poly, t);
+ } else
+ isl_term_free(term);
+
+ return isl_stat_ok;
+}
+
+/* Construct and return a polynomial that consists of the terms
+ * in "poly" that have sign "sign". The integer divisions, if
+ * any, are assumed to be non-negative.
+ */
+__isl_give isl_qpolynomial *isl_qpolynomial_terms_of_sign(
+ __isl_keep isl_qpolynomial *poly, int *signs, int sign)
+{
+ isl_space *space;
+ struct isl_fixed_sign_data data = { signs, sign };
+
+ space = isl_qpolynomial_get_domain_space(poly);
+ data.poly = isl_qpolynomial_zero_on_domain(space);
+
+ if (isl_qpolynomial_foreach_term(poly, collect_fixed_sign_terms, &data) < 0)
+ goto error;
+
+ return data.poly;
+error:
+ isl_qpolynomial_free(data.poly);
+ return NULL;
+}
+
+/* Helper function to add a guarded polynomial to either pwf_tight or pwf,
+ * depending on whether the result has been determined to be tight.
+ */
+static isl_stat add_guarded_poly(__isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, struct range_data *data)
+{
+ enum isl_fold type = data->sign < 0 ? isl_fold_min : isl_fold_max;
+ isl_set *set;
+ isl_qpolynomial_fold *fold;
+ isl_pw_qpolynomial_fold *pwf;
+
+ bset = isl_basic_set_params(bset);
+ poly = isl_qpolynomial_project_domain_on_params(poly);
+
+ fold = isl_qpolynomial_fold_alloc(type, poly);
+ set = isl_set_from_basic_set(bset);
+ pwf = isl_pw_qpolynomial_fold_alloc(type, set, fold);
+ if (data->tight)
+ data->pwf_tight = isl_pw_qpolynomial_fold_fold(
+ data->pwf_tight, pwf);
+ else
+ data->pwf = isl_pw_qpolynomial_fold_fold(data->pwf, pwf);
+
+ return isl_stat_ok;
+}
+
+/* Plug in "sub" for the variable at position "pos" in "poly".
+ *
+ * If "sub" is an infinite polynomial and if the variable actually
+ * appears in "poly", then calling isl_qpolynomial_substitute
+ * to perform the substitution may result in a NaN result.
+ * In such cases, return positive or negative infinity instead,
+ * depending on whether an upper bound or a lower bound is being computed,
+ * and mark the result as not being tight.
+ */
+static __isl_give isl_qpolynomial *plug_in_at_pos(
+ __isl_take isl_qpolynomial *poly, int pos,
+ __isl_take isl_qpolynomial *sub, struct range_data *data)
+{
+ isl_bool involves, infty;
+
+ involves = isl_qpolynomial_involves_dims(poly, isl_dim_in, pos, 1);
+ if (involves < 0)
+ goto error;
+ if (!involves) {
+ isl_qpolynomial_free(sub);
+ return poly;
+ }
+
+ infty = isl_qpolynomial_is_infty(sub);
+ if (infty >= 0 && !infty)
+ infty = isl_qpolynomial_is_neginfty(sub);
+ if (infty < 0)
+ goto error;
+ if (infty) {
+ isl_space *space = isl_qpolynomial_get_domain_space(poly);
+ data->tight = 0;
+ isl_qpolynomial_free(poly);
+ isl_qpolynomial_free(sub);
+ return signed_infty(space, data->sign);
+ }
+
+ poly = isl_qpolynomial_substitute(poly, isl_dim_in, pos, 1, &sub);
+ isl_qpolynomial_free(sub);
+
+ return poly;
+error:
+ isl_qpolynomial_free(poly);
+ isl_qpolynomial_free(sub);
+ return NULL;
+}
+
+/* Given a lower and upper bound on the final variable and constraints
+ * on the remaining variables where these bounds are active,
+ * eliminate the variable from data->poly based on these bounds.
+ * If the polynomial has been determined to be monotonic
+ * in the variable, then simply plug in the appropriate bound.
+ * If the current polynomial is tight and if this bound is integer,
+ * then the result is still tight. In all other cases, the results
+ * may not be tight.
+ * Otherwise, plug in the largest bound (in absolute value) in
+ * the positive terms (if an upper bound is wanted) or the negative terms
+ * (if a lower bounded is wanted) and the other bound in the other terms.
+ *
+ * If all variables have been eliminated, then record the result.
+ * Ohterwise, recurse on the next variable.
+ */
+static isl_stat propagate_on_bound_pair(__isl_take isl_constraint *lower,
+ __isl_take isl_constraint *upper, __isl_take isl_basic_set *bset,
+ void *user)
+{
+ struct range_data *data = (struct range_data *)user;
+ int save_tight = data->tight;
+ isl_qpolynomial *poly;
+ isl_stat r;
+ isl_size nvar, nparam;
+
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ if (nvar < 0 || nparam < 0)
+ goto error;
+
+ if (data->monotonicity) {
+ isl_qpolynomial *sub;
+ isl_space *space = isl_qpolynomial_get_domain_space(data->poly);
+ if (data->monotonicity * data->sign > 0) {
+ if (data->tight)
+ data->tight = bound_is_integer(upper, nvar);
+ sub = bound2poly(upper, space, nvar, 1);
+ isl_constraint_free(lower);
+ } else {
+ if (data->tight)
+ data->tight = bound_is_integer(lower, nvar);
+ sub = bound2poly(lower, space, nvar, -1);
+ isl_constraint_free(upper);
+ }
+ poly = isl_qpolynomial_copy(data->poly);
+ poly = plug_in_at_pos(poly, nvar, sub, data);
+ poly = isl_qpolynomial_drop_dims(poly, isl_dim_in, nvar, 1);
+ } else {
+ isl_qpolynomial *l, *u;
+ isl_qpolynomial *pos, *neg;
+ isl_space *space = isl_qpolynomial_get_domain_space(data->poly);
+ int sign = data->sign * data->signs[nparam + nvar];
+
+ data->tight = 0;
+
+ u = bound2poly(upper, isl_space_copy(space), nvar, 1);
+ l = bound2poly(lower, space, nvar, -1);
+
+ pos = isl_qpolynomial_terms_of_sign(data->poly, data->signs, sign);
+ neg = isl_qpolynomial_terms_of_sign(data->poly, data->signs, -sign);
+
+ pos = plug_in_at_pos(pos, nvar, u, data);
+ neg = plug_in_at_pos(neg, nvar, l, data);
+
+ poly = isl_qpolynomial_add(pos, neg);
+ poly = isl_qpolynomial_drop_dims(poly, isl_dim_in, nvar, 1);
+ }
+
+ if (nvar == 0)
+ r = add_guarded_poly(bset, poly, data);
+ else
+ r = propagate_on_domain(bset, poly, data);
+
+ data->tight = save_tight;
+
+ return r;
+error:
+ isl_constraint_free(lower);
+ isl_constraint_free(upper);
+ isl_basic_set_free(bset);
+ return isl_stat_error;
+}
+
+/* Recursively perform range propagation on the polynomial "poly"
+ * defined over the basic set "bset" and collect the results in "data".
+ */
+static isl_stat propagate_on_domain(__isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, struct range_data *data)
+{
+ isl_bool is_cst;
+ isl_ctx *ctx;
+ isl_qpolynomial *save_poly = data->poly;
+ int save_monotonicity = data->monotonicity;
+ isl_size d;
+
+ d = isl_basic_set_dim(bset, isl_dim_set);
+ is_cst = isl_qpolynomial_is_cst(poly, NULL, NULL);
+ if (d < 0 || is_cst < 0)
+ goto error;
+
+ ctx = isl_basic_set_get_ctx(bset);
+ isl_assert(ctx, d >= 1, goto error);
+
+ if (is_cst) {
+ bset = isl_basic_set_project_out(bset, isl_dim_set, 0, d);
+ poly = isl_qpolynomial_drop_dims(poly, isl_dim_in, 0, d);
+ return add_guarded_poly(bset, poly, data);
+ }
+
+ if (data->test_monotonicity)
+ data->monotonicity = monotonicity(bset, poly, data);
+ else
+ data->monotonicity = 0;
+ if (data->monotonicity < -1)
+ goto error;
+
+ data->poly = poly;
+ if (isl_basic_set_foreach_bound_pair(bset, isl_dim_set, d - 1,
+ &propagate_on_bound_pair, data) < 0)
+ goto error;
+
+ isl_basic_set_free(bset);
+ isl_qpolynomial_free(poly);
+ data->monotonicity = save_monotonicity;
+ data->poly = save_poly;
+
+ return isl_stat_ok;
+error:
+ isl_basic_set_free(bset);
+ isl_qpolynomial_free(poly);
+ data->monotonicity = save_monotonicity;
+ data->poly = save_poly;
+ return isl_stat_error;
+}
+
+static isl_stat basic_guarded_poly_bound(__isl_take isl_basic_set *bset,
+ void *user)
+{
+ struct range_data *data = (struct range_data *)user;
+ isl_ctx *ctx;
+ isl_size nparam = isl_basic_set_dim(bset, isl_dim_param);
+ isl_size dim = isl_basic_set_dim(bset, isl_dim_set);
+ isl_size total = isl_basic_set_dim(bset, isl_dim_all);
+ isl_stat r;
+
+ data->signs = NULL;
+
+ if (nparam < 0 || dim < 0 || total < 0)
+ goto error;
+
+ ctx = isl_basic_set_get_ctx(bset);
+ data->signs = isl_alloc_array(ctx, int, total);
+
+ if (isl_basic_set_dims_get_sign(bset, isl_dim_set, 0, dim,
+ data->signs + nparam) < 0)
+ goto error;
+ if (isl_basic_set_dims_get_sign(bset, isl_dim_param, 0, nparam,
+ data->signs) < 0)
+ goto error;
+
+ r = propagate_on_domain(bset, isl_qpolynomial_copy(data->poly), data);
+
+ free(data->signs);
+
+ return r;
+error:
+ free(data->signs);
+ isl_basic_set_free(bset);
+ return isl_stat_error;
+}
+
+static isl_stat qpolynomial_bound_on_domain_range(
+ __isl_take isl_basic_set *bset, __isl_take isl_qpolynomial *poly,
+ struct range_data *data)
+{
+ isl_size nparam = isl_basic_set_dim(bset, isl_dim_param);
+ isl_size nvar = isl_basic_set_dim(bset, isl_dim_set);
+ isl_set *set = NULL;
+
+ if (nparam < 0 || nvar < 0)
+ goto error;
+
+ if (nvar == 0)
+ return add_guarded_poly(bset, poly, data);
+
+ set = isl_set_from_basic_set(bset);
+ set = isl_set_split_dims(set, isl_dim_param, 0, nparam);
+ set = isl_set_split_dims(set, isl_dim_set, 0, nvar);
+
+ data->poly = poly;
+
+ data->test_monotonicity = 1;
+ if (isl_set_foreach_basic_set(set, &basic_guarded_poly_bound, data) < 0)
+ goto error;
+
+ isl_set_free(set);
+ isl_qpolynomial_free(poly);
+
+ return isl_stat_ok;
+error:
+ isl_set_free(set);
+ isl_qpolynomial_free(poly);
+ return isl_stat_error;
+}
+
+isl_stat isl_qpolynomial_bound_on_domain_range(__isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, struct isl_bound *bound)
+{
+ struct range_data data;
+ isl_stat r;
+
+ data.pwf = bound->pwf;
+ data.pwf_tight = bound->pwf_tight;
+ data.tight = bound->check_tight;
+ if (bound->type == isl_fold_min)
+ data.sign = -1;
+ else
+ data.sign = 1;
+
+ r = qpolynomial_bound_on_domain_range(bset, poly, &data);
+
+ bound->pwf = data.pwf;
+ bound->pwf_tight = data.pwf_tight;
+
+ return r;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_range.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_range.h
new file mode 100644
index 00000000000..6a5dd4a42e3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_range.h
@@ -0,0 +1,6 @@
+#include <isl_bound.h>
+
+isl_stat isl_qpolynomial_bound_on_domain_range(__isl_take isl_basic_set *bset,
+ __isl_take isl_qpolynomial *poly, struct isl_bound *bound);
+__isl_give isl_qpolynomial *isl_qpolynomial_terms_of_sign(
+ __isl_keep isl_qpolynomial *poly, int *signs, int sign);
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_reordering.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_reordering.c
new file mode 100644
index 00000000000..33a8976fbc6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_reordering.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl/id.h>
+#include <isl_space_private.h>
+#include <isl_reordering.h>
+
+__isl_give isl_reordering *isl_reordering_alloc(isl_ctx *ctx, int len)
+{
+ isl_reordering *exp;
+
+ exp = isl_alloc(ctx, struct isl_reordering,
+ sizeof(struct isl_reordering) + (len - 1) * sizeof(int));
+ if (!exp)
+ return NULL;
+
+ exp->ref = 1;
+ exp->len = len;
+ exp->space = NULL;
+
+ return exp;
+}
+
+__isl_give isl_reordering *isl_reordering_copy(__isl_keep isl_reordering *exp)
+{
+ if (!exp)
+ return NULL;
+
+ exp->ref++;
+ return exp;
+}
+
+__isl_give isl_reordering *isl_reordering_dup(__isl_keep isl_reordering *r)
+{
+ int i;
+ isl_reordering *dup;
+
+ if (!r)
+ return NULL;
+
+ dup = isl_reordering_alloc(isl_reordering_get_ctx(r), r->len);
+ if (!dup)
+ return NULL;
+
+ dup->space = isl_reordering_get_space(r);
+ if (!dup->space)
+ return isl_reordering_free(dup);
+ for (i = 0; i < dup->len; ++i)
+ dup->pos[i] = r->pos[i];
+
+ return dup;
+}
+
+__isl_give isl_reordering *isl_reordering_cow(__isl_take isl_reordering *r)
+{
+ if (!r)
+ return NULL;
+
+ if (r->ref == 1)
+ return r;
+ r->ref--;
+ return isl_reordering_dup(r);
+}
+
+__isl_null isl_reordering *isl_reordering_free(__isl_take isl_reordering *exp)
+{
+ if (!exp)
+ return NULL;
+
+ if (--exp->ref > 0)
+ return NULL;
+
+ isl_space_free(exp->space);
+ free(exp);
+ return NULL;
+}
+
+/* Return the isl_ctx to which "r" belongs.
+ */
+isl_ctx *isl_reordering_get_ctx(__isl_keep isl_reordering *r)
+{
+ return isl_space_get_ctx(isl_reordering_peek_space(r));
+}
+
+/* Return the space of "r".
+ */
+__isl_keep isl_space *isl_reordering_peek_space(__isl_keep isl_reordering *r)
+{
+ if (!r)
+ return NULL;
+ return r->space;
+}
+
+/* Return a copy of the space of "r".
+ */
+__isl_give isl_space *isl_reordering_get_space(__isl_keep isl_reordering *r)
+{
+ return isl_space_copy(isl_reordering_peek_space(r));
+}
+
+/* Construct a reordering that maps the parameters of "alignee"
+ * to the corresponding parameters in a new dimension specification
+ * that has the parameters of "aligner" first, followed by
+ * any remaining parameters of "alignee" that do not occur in "aligner".
+ */
+__isl_give isl_reordering *isl_parameter_alignment_reordering(
+ __isl_keep isl_space *alignee, __isl_keep isl_space *aligner)
+{
+ int i, j;
+ isl_reordering *exp;
+
+ if (!alignee || !aligner)
+ return NULL;
+
+ exp = isl_reordering_alloc(alignee->ctx, alignee->nparam);
+ if (!exp)
+ return NULL;
+
+ exp->space = isl_space_params(isl_space_copy(aligner));
+
+ for (i = 0; i < alignee->nparam; ++i) {
+ isl_id *id_i;
+ id_i = isl_space_get_dim_id(alignee, isl_dim_param, i);
+ if (!id_i)
+ isl_die(alignee->ctx, isl_error_invalid,
+ "cannot align unnamed parameters", goto error);
+ for (j = 0; j < aligner->nparam; ++j) {
+ isl_id *id_j;
+ id_j = isl_space_get_dim_id(aligner, isl_dim_param, j);
+ isl_id_free(id_j);
+ if (id_i == id_j)
+ break;
+ }
+ if (j < aligner->nparam) {
+ exp->pos[i] = j;
+ isl_id_free(id_i);
+ } else {
+ isl_size pos;
+ pos = isl_space_dim(exp->space, isl_dim_param);
+ if (pos < 0)
+ exp->space = isl_space_free(exp->space);
+ exp->space = isl_space_add_dims(exp->space,
+ isl_dim_param, 1);
+ exp->space = isl_space_set_dim_id(exp->space,
+ isl_dim_param, pos, id_i);
+ exp->pos[i] = pos;
+ }
+ }
+
+ if (!exp->space)
+ return isl_reordering_free(exp);
+ return exp;
+error:
+ isl_reordering_free(exp);
+ return NULL;
+}
+
+/* Return a reordering that moves the parameters identified by
+ * the elements of "tuple" to a domain tuple inserted into "space".
+ * The parameters that remain, are moved from their original positions
+ * in the list of parameters to their new positions in this list.
+ * The parameters that get removed, are moved to the corresponding
+ * positions in the new domain. Note that these set dimensions
+ * do not necessarily need to appear as parameters in "space".
+ * Any other dimensions are shifted by the number of extra dimensions
+ * introduced, i.e., the number of dimensions in the new domain
+ * that did not appear as parameters in "space".
+ */
+__isl_give isl_reordering *isl_reordering_unbind_params_insert_domain(
+ __isl_keep isl_space *space, __isl_keep isl_multi_id *tuple)
+{
+ int i, n;
+ int offset, first;
+ isl_ctx *ctx;
+ isl_reordering *r;
+
+ if (!space || !tuple)
+ return NULL;
+
+ ctx = isl_space_get_ctx(space);
+ r = isl_reordering_alloc(ctx, isl_space_dim(space, isl_dim_all));
+ if (!r)
+ return NULL;
+
+ r->space = isl_space_copy(space);
+ r->space = isl_space_unbind_params_insert_domain(r->space, tuple);
+ if (!r->space)
+ return isl_reordering_free(r);
+
+ n = isl_space_dim(r->space, isl_dim_param);
+ for (i = 0; i < n; ++i) {
+ int pos;
+ isl_id *id;
+
+ id = isl_space_get_dim_id(r->space, isl_dim_param, i);
+ if (!id)
+ return isl_reordering_free(r);
+ pos = isl_space_find_dim_by_id(space, isl_dim_param, id);
+ isl_id_free(id);
+ r->pos[pos] = i;
+ }
+
+ offset = isl_space_dim(r->space, isl_dim_param);
+ n = isl_multi_id_size(tuple);
+ for (i = 0; i < n; ++i) {
+ int pos;
+ isl_id *id;
+
+ id = isl_multi_id_get_id(tuple, i);
+ if (!id)
+ return isl_reordering_free(r);
+ pos = isl_space_find_dim_by_id(space, isl_dim_param, id);
+ isl_id_free(id);
+ if (pos < 0)
+ continue;
+ r->pos[pos] = offset + i;
+ }
+
+ offset = isl_space_dim(r->space, isl_dim_all) - r->len;
+ first = isl_space_dim(space, isl_dim_param);
+ n = r->len - first;
+ for (i = 0; i < n; ++i)
+ r->pos[first + i] = first + offset + i;
+
+ return r;
+}
+
+__isl_give isl_reordering *isl_reordering_extend(__isl_take isl_reordering *exp,
+ unsigned extra)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_reordering *res;
+ int offset;
+ isl_size dim;
+
+ if (!exp)
+ return NULL;
+ if (extra == 0)
+ return exp;
+
+ ctx = isl_reordering_get_ctx(exp);
+ space = isl_reordering_peek_space(exp);
+ dim = isl_space_dim(space, isl_dim_all);
+ if (dim < 0)
+ return isl_reordering_free(exp);
+ offset = dim - exp->len;
+ res = isl_reordering_alloc(ctx, exp->len + extra);
+ if (!res)
+ goto error;
+ res->space = isl_reordering_get_space(exp);
+ for (i = 0; i < exp->len; ++i)
+ res->pos[i] = exp->pos[i];
+ for (i = exp->len; i < res->len; ++i)
+ res->pos[i] = offset + i;
+
+ isl_reordering_free(exp);
+
+ return res;
+error:
+ isl_reordering_free(exp);
+ return NULL;
+}
+
+__isl_give isl_reordering *isl_reordering_extend_space(
+ __isl_take isl_reordering *exp, __isl_take isl_space *space)
+{
+ isl_space *exp_space;
+ isl_reordering *res;
+ isl_size dim;
+
+ dim = isl_space_dim(space, isl_dim_all);
+ if (!exp || dim < 0)
+ goto error;
+
+ res = isl_reordering_extend(isl_reordering_copy(exp), dim - exp->len);
+ res = isl_reordering_cow(res);
+ if (!res)
+ goto error;
+ isl_space_free(res->space);
+ exp_space = isl_reordering_peek_space(exp);
+ res->space = isl_space_replace_params(space, exp_space);
+
+ isl_reordering_free(exp);
+
+ if (!res->space)
+ return isl_reordering_free(res);
+
+ return res;
+error:
+ isl_reordering_free(exp);
+ isl_space_free(space);
+ return NULL;
+}
+
+void isl_reordering_dump(__isl_keep isl_reordering *exp)
+{
+ int i;
+
+ isl_space_dump(exp->space);
+ for (i = 0; i < exp->len; ++i)
+ fprintf(stderr, "%d -> %d; ", i, exp->pos[i]);
+ fprintf(stderr, "\n");
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_reordering.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_reordering.h
new file mode 100644
index 00000000000..6664749b0ec
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_reordering.h
@@ -0,0 +1,36 @@
+#ifndef ISL_REORDERING_H
+#define ISL_REORDERING_H
+
+#include <isl/space.h>
+
+/* pos maps original dimensions to new dimensions.
+ * The final space is given by "space".
+ * The number of dimensions (i.e., the range of values) in the result
+ * may be larger than the number of dimensions in the input.
+ * In particular, the possible values of the entries in pos ranges from 0 to
+ * the total dimension of dim - 1, unless isl_reordering_extend
+ * has been called.
+ */
+struct isl_reordering {
+ int ref;
+ isl_space *space;
+ unsigned len;
+ int pos[1];
+};
+typedef struct isl_reordering isl_reordering;
+
+isl_ctx *isl_reordering_get_ctx(__isl_keep isl_reordering *r);
+__isl_keep isl_space *isl_reordering_peek_space(__isl_keep isl_reordering *r);
+__isl_give isl_space *isl_reordering_get_space(__isl_keep isl_reordering *r);
+__isl_give isl_reordering *isl_parameter_alignment_reordering(
+ __isl_keep isl_space *alignee, __isl_keep isl_space *aligner);
+__isl_give isl_reordering *isl_reordering_unbind_params_insert_domain(
+ __isl_keep isl_space *space, __isl_keep isl_multi_id *tuple);
+__isl_give isl_reordering *isl_reordering_copy(__isl_keep isl_reordering *exp);
+__isl_null isl_reordering *isl_reordering_free(__isl_take isl_reordering *exp);
+__isl_give isl_reordering *isl_reordering_extend_space(
+ __isl_take isl_reordering *exp, __isl_take isl_space *space);
+__isl_give isl_reordering *isl_reordering_extend(__isl_take isl_reordering *exp,
+ unsigned extra);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sample.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sample.c
new file mode 100644
index 00000000000..e0e2c9bee8d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sample.c
@@ -0,0 +1,1335 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include "isl_sample.h"
+#include <isl/vec.h>
+#include <isl/mat.h>
+#include <isl_seq.h>
+#include "isl_equalities.h"
+#include "isl_tab.h"
+#include "isl_basis_reduction.h"
+#include <isl_factorization.h>
+#include <isl_point_private.h>
+#include <isl_options_private.h>
+#include <isl_vec_private.h>
+
+#include <bset_from_bmap.c>
+#include <set_to_map.c>
+
+static __isl_give isl_vec *empty_sample(__isl_take isl_basic_set *bset)
+{
+ struct isl_vec *vec;
+
+ vec = isl_vec_alloc(bset->ctx, 0);
+ isl_basic_set_free(bset);
+ return vec;
+}
+
+/* Construct a zero sample of the same dimension as bset.
+ * As a special case, if bset is zero-dimensional, this
+ * function creates a zero-dimensional sample point.
+ */
+static __isl_give isl_vec *zero_sample(__isl_take isl_basic_set *bset)
+{
+ isl_size dim;
+ struct isl_vec *sample;
+
+ dim = isl_basic_set_dim(bset, isl_dim_all);
+ if (dim < 0)
+ goto error;
+ sample = isl_vec_alloc(bset->ctx, 1 + dim);
+ if (sample) {
+ isl_int_set_si(sample->el[0], 1);
+ isl_seq_clr(sample->el + 1, dim);
+ }
+ isl_basic_set_free(bset);
+ return sample;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+static __isl_give isl_vec *interval_sample(__isl_take isl_basic_set *bset)
+{
+ int i;
+ isl_int t;
+ struct isl_vec *sample;
+
+ bset = isl_basic_set_simplify(bset);
+ if (!bset)
+ return NULL;
+ if (isl_basic_set_plain_is_empty(bset))
+ return empty_sample(bset);
+ if (bset->n_eq == 0 && bset->n_ineq == 0)
+ return zero_sample(bset);
+
+ sample = isl_vec_alloc(bset->ctx, 2);
+ if (!sample)
+ goto error;
+ if (!bset)
+ return NULL;
+ isl_int_set_si(sample->block.data[0], 1);
+
+ if (bset->n_eq > 0) {
+ isl_assert(bset->ctx, bset->n_eq == 1, goto error);
+ isl_assert(bset->ctx, bset->n_ineq == 0, goto error);
+ if (isl_int_is_one(bset->eq[0][1]))
+ isl_int_neg(sample->el[1], bset->eq[0][0]);
+ else {
+ isl_assert(bset->ctx, isl_int_is_negone(bset->eq[0][1]),
+ goto error);
+ isl_int_set(sample->el[1], bset->eq[0][0]);
+ }
+ isl_basic_set_free(bset);
+ return sample;
+ }
+
+ isl_int_init(t);
+ if (isl_int_is_one(bset->ineq[0][1]))
+ isl_int_neg(sample->block.data[1], bset->ineq[0][0]);
+ else
+ isl_int_set(sample->block.data[1], bset->ineq[0][0]);
+ for (i = 1; i < bset->n_ineq; ++i) {
+ isl_seq_inner_product(sample->block.data,
+ bset->ineq[i], 2, &t);
+ if (isl_int_is_neg(t))
+ break;
+ }
+ isl_int_clear(t);
+ if (i < bset->n_ineq) {
+ isl_vec_free(sample);
+ return empty_sample(bset);
+ }
+
+ isl_basic_set_free(bset);
+ return sample;
+error:
+ isl_basic_set_free(bset);
+ isl_vec_free(sample);
+ return NULL;
+}
+
+/* Find a sample integer point, if any, in bset, which is known
+ * to have equalities. If bset contains no integer points, then
+ * return a zero-length vector.
+ * We simply remove the known equalities, compute a sample
+ * in the resulting bset, using the specified recurse function,
+ * and then transform the sample back to the original space.
+ */
+static __isl_give isl_vec *sample_eq(__isl_take isl_basic_set *bset,
+ __isl_give isl_vec *(*recurse)(__isl_take isl_basic_set *))
+{
+ struct isl_mat *T;
+ struct isl_vec *sample;
+
+ if (!bset)
+ return NULL;
+
+ bset = isl_basic_set_remove_equalities(bset, &T, NULL);
+ sample = recurse(bset);
+ if (!sample || sample->size == 0)
+ isl_mat_free(T);
+ else
+ sample = isl_mat_vec_product(T, sample);
+ return sample;
+}
+
+/* Return a matrix containing the equalities of the tableau
+ * in constraint form. The tableau is assumed to have
+ * an associated bset that has been kept up-to-date.
+ */
+static struct isl_mat *tab_equalities(struct isl_tab *tab)
+{
+ int i, j;
+ int n_eq;
+ struct isl_mat *eq;
+ struct isl_basic_set *bset;
+
+ if (!tab)
+ return NULL;
+
+ bset = isl_tab_peek_bset(tab);
+ isl_assert(tab->mat->ctx, bset, return NULL);
+
+ n_eq = tab->n_var - tab->n_col + tab->n_dead;
+ if (tab->empty || n_eq == 0)
+ return isl_mat_alloc(tab->mat->ctx, 0, tab->n_var);
+ if (n_eq == tab->n_var)
+ return isl_mat_identity(tab->mat->ctx, tab->n_var);
+
+ eq = isl_mat_alloc(tab->mat->ctx, n_eq, tab->n_var);
+ if (!eq)
+ return NULL;
+ for (i = 0, j = 0; i < tab->n_con; ++i) {
+ if (tab->con[i].is_row)
+ continue;
+ if (tab->con[i].index >= 0 && tab->con[i].index >= tab->n_dead)
+ continue;
+ if (i < bset->n_eq)
+ isl_seq_cpy(eq->row[j], bset->eq[i] + 1, tab->n_var);
+ else
+ isl_seq_cpy(eq->row[j],
+ bset->ineq[i - bset->n_eq] + 1, tab->n_var);
+ ++j;
+ }
+ isl_assert(bset->ctx, j == n_eq, goto error);
+ return eq;
+error:
+ isl_mat_free(eq);
+ return NULL;
+}
+
+/* Compute and return an initial basis for the bounded tableau "tab".
+ *
+ * If the tableau is either full-dimensional or zero-dimensional,
+ * the we simply return an identity matrix.
+ * Otherwise, we construct a basis whose first directions correspond
+ * to equalities.
+ */
+static struct isl_mat *initial_basis(struct isl_tab *tab)
+{
+ int n_eq;
+ struct isl_mat *eq;
+ struct isl_mat *Q;
+
+ tab->n_unbounded = 0;
+ tab->n_zero = n_eq = tab->n_var - tab->n_col + tab->n_dead;
+ if (tab->empty || n_eq == 0 || n_eq == tab->n_var)
+ return isl_mat_identity(tab->mat->ctx, 1 + tab->n_var);
+
+ eq = tab_equalities(tab);
+ eq = isl_mat_left_hermite(eq, 0, NULL, &Q);
+ if (!eq)
+ return NULL;
+ isl_mat_free(eq);
+
+ Q = isl_mat_lin_to_aff(Q);
+ return Q;
+}
+
+/* Compute the minimum of the current ("level") basis row over "tab"
+ * and store the result in position "level" of "min".
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ */
+static enum isl_lp_result compute_min(isl_ctx *ctx, struct isl_tab *tab,
+ __isl_keep isl_vec *min, int level)
+{
+ return isl_tab_min(tab, tab->basis->row[1 + level],
+ ctx->one, &min->el[level], NULL, 0);
+}
+
+/* Compute the maximum of the current ("level") basis row over "tab"
+ * and store the result in position "level" of "max".
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ */
+static enum isl_lp_result compute_max(isl_ctx *ctx, struct isl_tab *tab,
+ __isl_keep isl_vec *max, int level)
+{
+ enum isl_lp_result res;
+ unsigned dim = tab->n_var;
+
+ isl_seq_neg(tab->basis->row[1 + level] + 1,
+ tab->basis->row[1 + level] + 1, dim);
+ res = isl_tab_min(tab, tab->basis->row[1 + level],
+ ctx->one, &max->el[level], NULL, 0);
+ isl_seq_neg(tab->basis->row[1 + level] + 1,
+ tab->basis->row[1 + level] + 1, dim);
+ isl_int_neg(max->el[level], max->el[level]);
+
+ return res;
+}
+
+/* Perform a greedy search for an integer point in the set represented
+ * by "tab", given that the minimal rational value (rounded up to the
+ * nearest integer) at "level" is smaller than the maximal rational
+ * value (rounded down to the nearest integer).
+ *
+ * Return 1 if we have found an integer point (if tab->n_unbounded > 0
+ * then we may have only found integer values for the bounded dimensions
+ * and it is the responsibility of the caller to extend this solution
+ * to the unbounded dimensions).
+ * Return 0 if greedy search did not result in a solution.
+ * Return -1 if some error occurred.
+ *
+ * We assign a value half-way between the minimum and the maximum
+ * to the current dimension and check if the minimal value of the
+ * next dimension is still smaller than (or equal) to the maximal value.
+ * We continue this process until either
+ * - the minimal value (rounded up) is greater than the maximal value
+ * (rounded down). In this case, greedy search has failed.
+ * - we have exhausted all bounded dimensions, meaning that we have
+ * found a solution.
+ * - the sample value of the tableau is integral.
+ * - some error has occurred.
+ */
+static int greedy_search(isl_ctx *ctx, struct isl_tab *tab,
+ __isl_keep isl_vec *min, __isl_keep isl_vec *max, int level)
+{
+ struct isl_tab_undo *snap;
+ enum isl_lp_result res;
+
+ snap = isl_tab_snap(tab);
+
+ do {
+ isl_int_add(tab->basis->row[1 + level][0],
+ min->el[level], max->el[level]);
+ isl_int_fdiv_q_ui(tab->basis->row[1 + level][0],
+ tab->basis->row[1 + level][0], 2);
+ isl_int_neg(tab->basis->row[1 + level][0],
+ tab->basis->row[1 + level][0]);
+ if (isl_tab_add_valid_eq(tab, tab->basis->row[1 + level]) < 0)
+ return -1;
+ isl_int_set_si(tab->basis->row[1 + level][0], 0);
+
+ if (++level >= tab->n_var - tab->n_unbounded)
+ return 1;
+ if (isl_tab_sample_is_integer(tab))
+ return 1;
+
+ res = compute_min(ctx, tab, min, level);
+ if (res == isl_lp_error)
+ return -1;
+ if (res != isl_lp_ok)
+ isl_die(ctx, isl_error_internal,
+ "expecting bounded rational solution",
+ return -1);
+ res = compute_max(ctx, tab, max, level);
+ if (res == isl_lp_error)
+ return -1;
+ if (res != isl_lp_ok)
+ isl_die(ctx, isl_error_internal,
+ "expecting bounded rational solution",
+ return -1);
+ } while (isl_int_le(min->el[level], max->el[level]));
+
+ if (isl_tab_rollback(tab, snap) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Given a tableau representing a set, find and return
+ * an integer point in the set, if there is any.
+ *
+ * We perform a depth first search
+ * for an integer point, by scanning all possible values in the range
+ * attained by a basis vector, where an initial basis may have been set
+ * by the calling function. Otherwise an initial basis that exploits
+ * the equalities in the tableau is created.
+ * tab->n_zero is currently ignored and is clobbered by this function.
+ *
+ * The tableau is allowed to have unbounded direction, but then
+ * the calling function needs to set an initial basis, with the
+ * unbounded directions last and with tab->n_unbounded set
+ * to the number of unbounded directions.
+ * Furthermore, the calling functions needs to add shifted copies
+ * of all constraints involving unbounded directions to ensure
+ * that any feasible rational value in these directions can be rounded
+ * up to yield a feasible integer value.
+ * In particular, let B define the given basis x' = B x
+ * and let T be the inverse of B, i.e., X = T x'.
+ * Let a x + c >= 0 be a constraint of the set represented by the tableau,
+ * or a T x' + c >= 0 in terms of the given basis. Assume that
+ * the bounded directions have an integer value, then we can safely
+ * round up the values for the unbounded directions if we make sure
+ * that x' not only satisfies the original constraint, but also
+ * the constraint "a T x' + c + s >= 0" with s the sum of all
+ * negative values in the last n_unbounded entries of "a T".
+ * The calling function therefore needs to add the constraint
+ * a x + c + s >= 0. The current function then scans the first
+ * directions for an integer value and once those have been found,
+ * it can compute "T ceil(B x)" to yield an integer point in the set.
+ * Note that during the search, the first rows of B may be changed
+ * by a basis reduction, but the last n_unbounded rows of B remain
+ * unaltered and are also not mixed into the first rows.
+ *
+ * The search is implemented iteratively. "level" identifies the current
+ * basis vector. "init" is true if we want the first value at the current
+ * level and false if we want the next value.
+ *
+ * At the start of each level, we first check if we can find a solution
+ * using greedy search. If not, we continue with the exhaustive search.
+ *
+ * The initial basis is the identity matrix. If the range in some direction
+ * contains more than one integer value, we perform basis reduction based
+ * on the value of ctx->opt->gbr
+ * - ISL_GBR_NEVER: never perform basis reduction
+ * - ISL_GBR_ONCE: only perform basis reduction the first
+ * time such a range is encountered
+ * - ISL_GBR_ALWAYS: always perform basis reduction when
+ * such a range is encountered
+ *
+ * When ctx->opt->gbr is set to ISL_GBR_ALWAYS, then we allow the basis
+ * reduction computation to return early. That is, as soon as it
+ * finds a reasonable first direction.
+ */
+__isl_give isl_vec *isl_tab_sample(struct isl_tab *tab)
+{
+ unsigned dim;
+ unsigned gbr;
+ struct isl_ctx *ctx;
+ struct isl_vec *sample;
+ struct isl_vec *min;
+ struct isl_vec *max;
+ enum isl_lp_result res;
+ int level;
+ int init;
+ int reduced;
+ struct isl_tab_undo **snap;
+
+ if (!tab)
+ return NULL;
+ if (tab->empty)
+ return isl_vec_alloc(tab->mat->ctx, 0);
+
+ if (!tab->basis)
+ tab->basis = initial_basis(tab);
+ if (!tab->basis)
+ return NULL;
+ isl_assert(tab->mat->ctx, tab->basis->n_row == tab->n_var + 1,
+ return NULL);
+ isl_assert(tab->mat->ctx, tab->basis->n_col == tab->n_var + 1,
+ return NULL);
+
+ ctx = tab->mat->ctx;
+ dim = tab->n_var;
+ gbr = ctx->opt->gbr;
+
+ if (tab->n_unbounded == tab->n_var) {
+ sample = isl_tab_get_sample_value(tab);
+ sample = isl_mat_vec_product(isl_mat_copy(tab->basis), sample);
+ sample = isl_vec_ceil(sample);
+ sample = isl_mat_vec_inverse_product(isl_mat_copy(tab->basis),
+ sample);
+ return sample;
+ }
+
+ if (isl_tab_extend_cons(tab, dim + 1) < 0)
+ return NULL;
+
+ min = isl_vec_alloc(ctx, dim);
+ max = isl_vec_alloc(ctx, dim);
+ snap = isl_alloc_array(ctx, struct isl_tab_undo *, dim);
+
+ if (!min || !max || !snap)
+ goto error;
+
+ level = 0;
+ init = 1;
+ reduced = 0;
+
+ while (level >= 0) {
+ if (init) {
+ int choice;
+
+ res = compute_min(ctx, tab, min, level);
+ if (res == isl_lp_error)
+ goto error;
+ if (res != isl_lp_ok)
+ isl_die(ctx, isl_error_internal,
+ "expecting bounded rational solution",
+ goto error);
+ if (isl_tab_sample_is_integer(tab))
+ break;
+ res = compute_max(ctx, tab, max, level);
+ if (res == isl_lp_error)
+ goto error;
+ if (res != isl_lp_ok)
+ isl_die(ctx, isl_error_internal,
+ "expecting bounded rational solution",
+ goto error);
+ if (isl_tab_sample_is_integer(tab))
+ break;
+ choice = isl_int_lt(min->el[level], max->el[level]);
+ if (choice) {
+ int g;
+ g = greedy_search(ctx, tab, min, max, level);
+ if (g < 0)
+ goto error;
+ if (g)
+ break;
+ }
+ if (!reduced && choice &&
+ ctx->opt->gbr != ISL_GBR_NEVER) {
+ unsigned gbr_only_first;
+ if (ctx->opt->gbr == ISL_GBR_ONCE)
+ ctx->opt->gbr = ISL_GBR_NEVER;
+ tab->n_zero = level;
+ gbr_only_first = ctx->opt->gbr_only_first;
+ ctx->opt->gbr_only_first =
+ ctx->opt->gbr == ISL_GBR_ALWAYS;
+ tab = isl_tab_compute_reduced_basis(tab);
+ ctx->opt->gbr_only_first = gbr_only_first;
+ if (!tab || !tab->basis)
+ goto error;
+ reduced = 1;
+ continue;
+ }
+ reduced = 0;
+ snap[level] = isl_tab_snap(tab);
+ } else
+ isl_int_add_ui(min->el[level], min->el[level], 1);
+
+ if (isl_int_gt(min->el[level], max->el[level])) {
+ level--;
+ init = 0;
+ if (level >= 0)
+ if (isl_tab_rollback(tab, snap[level]) < 0)
+ goto error;
+ continue;
+ }
+ isl_int_neg(tab->basis->row[1 + level][0], min->el[level]);
+ if (isl_tab_add_valid_eq(tab, tab->basis->row[1 + level]) < 0)
+ goto error;
+ isl_int_set_si(tab->basis->row[1 + level][0], 0);
+ if (level + tab->n_unbounded < dim - 1) {
+ ++level;
+ init = 1;
+ continue;
+ }
+ break;
+ }
+
+ if (level >= 0) {
+ sample = isl_tab_get_sample_value(tab);
+ if (!sample)
+ goto error;
+ if (tab->n_unbounded && !isl_int_is_one(sample->el[0])) {
+ sample = isl_mat_vec_product(isl_mat_copy(tab->basis),
+ sample);
+ sample = isl_vec_ceil(sample);
+ sample = isl_mat_vec_inverse_product(
+ isl_mat_copy(tab->basis), sample);
+ }
+ } else
+ sample = isl_vec_alloc(ctx, 0);
+
+ ctx->opt->gbr = gbr;
+ isl_vec_free(min);
+ isl_vec_free(max);
+ free(snap);
+ return sample;
+error:
+ ctx->opt->gbr = gbr;
+ isl_vec_free(min);
+ isl_vec_free(max);
+ free(snap);
+ return NULL;
+}
+
+static __isl_give isl_vec *sample_bounded(__isl_take isl_basic_set *bset);
+
+/* Internal data for factored_sample.
+ * "sample" collects the sample and may get reset to a zero-length vector
+ * signaling the absence of a sample vector.
+ * "pos" is the position of the contribution of the next factor.
+ */
+struct isl_factored_sample_data {
+ isl_vec *sample;
+ int pos;
+};
+
+/* isl_factorizer_every_factor_basic_set callback that extends
+ * the sample in data->sample with the contribution
+ * of the factor "bset".
+ * If "bset" turns out to be empty, then the product is empty too and
+ * no further factors need to be considered.
+ */
+static isl_bool factor_sample(__isl_keep isl_basic_set *bset, void *user)
+{
+ struct isl_factored_sample_data *data = user;
+ isl_vec *sample;
+ isl_size n;
+
+ n = isl_basic_set_dim(bset, isl_dim_set);
+ if (n < 0)
+ return isl_bool_error;
+
+ sample = sample_bounded(isl_basic_set_copy(bset));
+ if (!sample)
+ return isl_bool_error;
+ if (sample->size == 0) {
+ isl_vec_free(data->sample);
+ data->sample = sample;
+ return isl_bool_false;
+ }
+ isl_seq_cpy(data->sample->el + data->pos, sample->el + 1, n);
+ isl_vec_free(sample);
+ data->pos += n;
+
+ return isl_bool_true;
+}
+
+/* Compute a sample point of the given basic set, based on the given,
+ * non-trivial factorization.
+ */
+static __isl_give isl_vec *factored_sample(__isl_take isl_basic_set *bset,
+ __isl_take isl_factorizer *f)
+{
+ struct isl_factored_sample_data data = { NULL };
+ isl_ctx *ctx;
+ isl_size total;
+ isl_bool every;
+
+ ctx = isl_basic_set_get_ctx(bset);
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (!ctx || total < 0)
+ goto error;
+
+ data.sample = isl_vec_alloc(ctx, 1 + total);
+ if (!data.sample)
+ goto error;
+ isl_int_set_si(data.sample->el[0], 1);
+ data.pos = 1;
+
+ every = isl_factorizer_every_factor_basic_set(f, &factor_sample, &data);
+ if (every < 0) {
+ data.sample = isl_vec_free(data.sample);
+ } else if (every) {
+ isl_morph *morph;
+
+ morph = isl_morph_inverse(isl_morph_copy(f->morph));
+ data.sample = isl_morph_vec(morph, data.sample);
+ }
+
+ isl_basic_set_free(bset);
+ isl_factorizer_free(f);
+ return data.sample;
+error:
+ isl_basic_set_free(bset);
+ isl_factorizer_free(f);
+ isl_vec_free(data.sample);
+ return NULL;
+}
+
+/* Given a basic set that is known to be bounded, find and return
+ * an integer point in the basic set, if there is any.
+ *
+ * After handling some trivial cases, we construct a tableau
+ * and then use isl_tab_sample to find a sample, passing it
+ * the identity matrix as initial basis.
+ */
+static __isl_give isl_vec *sample_bounded(__isl_take isl_basic_set *bset)
+{
+ isl_size dim;
+ struct isl_vec *sample;
+ struct isl_tab *tab = NULL;
+ isl_factorizer *f;
+
+ if (!bset)
+ return NULL;
+
+ if (isl_basic_set_plain_is_empty(bset))
+ return empty_sample(bset);
+
+ dim = isl_basic_set_dim(bset, isl_dim_all);
+ if (dim < 0)
+ bset = isl_basic_set_free(bset);
+ if (dim == 0)
+ return zero_sample(bset);
+ if (dim == 1)
+ return interval_sample(bset);
+ if (bset->n_eq > 0)
+ return sample_eq(bset, sample_bounded);
+
+ f = isl_basic_set_factorizer(bset);
+ if (!f)
+ goto error;
+ if (f->n_group != 0)
+ return factored_sample(bset, f);
+ isl_factorizer_free(f);
+
+ tab = isl_tab_from_basic_set(bset, 1);
+ if (tab && tab->empty) {
+ isl_tab_free(tab);
+ ISL_F_SET(bset, ISL_BASIC_SET_EMPTY);
+ sample = isl_vec_alloc(isl_basic_set_get_ctx(bset), 0);
+ isl_basic_set_free(bset);
+ return sample;
+ }
+
+ if (!ISL_F_ISSET(bset, ISL_BASIC_SET_NO_IMPLICIT))
+ if (isl_tab_detect_implicit_equalities(tab) < 0)
+ goto error;
+
+ sample = isl_tab_sample(tab);
+ if (!sample)
+ goto error;
+
+ if (sample->size > 0) {
+ isl_vec_free(bset->sample);
+ bset->sample = isl_vec_copy(sample);
+ }
+
+ isl_basic_set_free(bset);
+ isl_tab_free(tab);
+ return sample;
+error:
+ isl_basic_set_free(bset);
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Given a basic set "bset" and a value "sample" for the first coordinates
+ * of bset, plug in these values and drop the corresponding coordinates.
+ *
+ * We do this by computing the preimage of the transformation
+ *
+ * [ 1 0 ]
+ * x = [ s 0 ] x'
+ * [ 0 I ]
+ *
+ * where [1 s] is the sample value and I is the identity matrix of the
+ * appropriate dimension.
+ */
+static __isl_give isl_basic_set *plug_in(__isl_take isl_basic_set *bset,
+ __isl_take isl_vec *sample)
+{
+ int i;
+ isl_size total;
+ struct isl_mat *T;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0 || !sample)
+ goto error;
+
+ T = isl_mat_alloc(bset->ctx, 1 + total, 1 + total - (sample->size - 1));
+ if (!T)
+ goto error;
+
+ for (i = 0; i < sample->size; ++i) {
+ isl_int_set(T->row[i][0], sample->el[i]);
+ isl_seq_clr(T->row[i] + 1, T->n_col - 1);
+ }
+ for (i = 0; i < T->n_col - 1; ++i) {
+ isl_seq_clr(T->row[sample->size + i], T->n_col);
+ isl_int_set_si(T->row[sample->size + i][1 + i], 1);
+ }
+ isl_vec_free(sample);
+
+ bset = isl_basic_set_preimage(bset, T);
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ isl_vec_free(sample);
+ return NULL;
+}
+
+/* Given a basic set "bset", return any (possibly non-integer) point
+ * in the basic set.
+ */
+static __isl_give isl_vec *rational_sample(__isl_take isl_basic_set *bset)
+{
+ struct isl_tab *tab;
+ struct isl_vec *sample;
+
+ if (!bset)
+ return NULL;
+
+ tab = isl_tab_from_basic_set(bset, 0);
+ sample = isl_tab_get_sample_value(tab);
+ isl_tab_free(tab);
+
+ isl_basic_set_free(bset);
+
+ return sample;
+}
+
+/* Given a linear cone "cone" and a rational point "vec",
+ * construct a polyhedron with shifted copies of the constraints in "cone",
+ * i.e., a polyhedron with "cone" as its recession cone, such that each
+ * point x in this polyhedron is such that the unit box positioned at x
+ * lies entirely inside the affine cone 'vec + cone'.
+ * Any rational point in this polyhedron may therefore be rounded up
+ * to yield an integer point that lies inside said affine cone.
+ *
+ * Denote the constraints of cone by "<a_i, x> >= 0" and the rational
+ * point "vec" by v/d.
+ * Let b_i = <a_i, v>. Then the affine cone 'vec + cone' is given
+ * by <a_i, x> - b/d >= 0.
+ * The polyhedron <a_i, x> - ceil{b/d} >= 0 is a subset of this affine cone.
+ * We prefer this polyhedron over the actual affine cone because it doesn't
+ * require a scaling of the constraints.
+ * If each of the vertices of the unit cube positioned at x lies inside
+ * this polyhedron, then the whole unit cube at x lies inside the affine cone.
+ * We therefore impose that x' = x + \sum e_i, for any selection of unit
+ * vectors lies inside the polyhedron, i.e.,
+ *
+ * <a_i, x'> - ceil{b/d} = <a_i, x> + sum a_i - ceil{b/d} >= 0
+ *
+ * The most stringent of these constraints is the one that selects
+ * all negative a_i, so the polyhedron we are looking for has constraints
+ *
+ * <a_i, x> + sum_{a_i < 0} a_i - ceil{b/d} >= 0
+ *
+ * Note that if cone were known to have only non-negative rays
+ * (which can be accomplished by a unimodular transformation),
+ * then we would only have to check the points x' = x + e_i
+ * and we only have to add the smallest negative a_i (if any)
+ * instead of the sum of all negative a_i.
+ */
+static __isl_give isl_basic_set *shift_cone(__isl_take isl_basic_set *cone,
+ __isl_take isl_vec *vec)
+{
+ int i, j, k;
+ isl_size total;
+
+ struct isl_basic_set *shift = NULL;
+
+ total = isl_basic_set_dim(cone, isl_dim_all);
+ if (total < 0 || !vec)
+ goto error;
+
+ isl_assert(cone->ctx, cone->n_eq == 0, goto error);
+
+ shift = isl_basic_set_alloc_space(isl_basic_set_get_space(cone),
+ 0, 0, cone->n_ineq);
+
+ for (i = 0; i < cone->n_ineq; ++i) {
+ k = isl_basic_set_alloc_inequality(shift);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(shift->ineq[k] + 1, cone->ineq[i] + 1, total);
+ isl_seq_inner_product(shift->ineq[k] + 1, vec->el + 1, total,
+ &shift->ineq[k][0]);
+ isl_int_cdiv_q(shift->ineq[k][0],
+ shift->ineq[k][0], vec->el[0]);
+ isl_int_neg(shift->ineq[k][0], shift->ineq[k][0]);
+ for (j = 0; j < total; ++j) {
+ if (isl_int_is_nonneg(shift->ineq[k][1 + j]))
+ continue;
+ isl_int_add(shift->ineq[k][0],
+ shift->ineq[k][0], shift->ineq[k][1 + j]);
+ }
+ }
+
+ isl_basic_set_free(cone);
+ isl_vec_free(vec);
+
+ return isl_basic_set_finalize(shift);
+error:
+ isl_basic_set_free(shift);
+ isl_basic_set_free(cone);
+ isl_vec_free(vec);
+ return NULL;
+}
+
+/* Given a rational point vec in a (transformed) basic set,
+ * such that cone is the recession cone of the original basic set,
+ * "round up" the rational point to an integer point.
+ *
+ * We first check if the rational point just happens to be integer.
+ * If not, we transform the cone in the same way as the basic set,
+ * pick a point x in this cone shifted to the rational point such that
+ * the whole unit cube at x is also inside this affine cone.
+ * Then we simply round up the coordinates of x and return the
+ * resulting integer point.
+ */
+static __isl_give isl_vec *round_up_in_cone(__isl_take isl_vec *vec,
+ __isl_take isl_basic_set *cone, __isl_take isl_mat *U)
+{
+ isl_size total;
+
+ if (!vec || !cone || !U)
+ goto error;
+
+ isl_assert(vec->ctx, vec->size != 0, goto error);
+ if (isl_int_is_one(vec->el[0])) {
+ isl_mat_free(U);
+ isl_basic_set_free(cone);
+ return vec;
+ }
+
+ total = isl_basic_set_dim(cone, isl_dim_all);
+ if (total < 0)
+ goto error;
+ cone = isl_basic_set_preimage(cone, U);
+ cone = isl_basic_set_remove_dims(cone, isl_dim_set,
+ 0, total - (vec->size - 1));
+
+ cone = shift_cone(cone, vec);
+
+ vec = rational_sample(cone);
+ vec = isl_vec_ceil(vec);
+ return vec;
+error:
+ isl_mat_free(U);
+ isl_vec_free(vec);
+ isl_basic_set_free(cone);
+ return NULL;
+}
+
+/* Concatenate two integer vectors, i.e., two vectors with denominator
+ * (stored in element 0) equal to 1.
+ */
+static __isl_give isl_vec *vec_concat(__isl_take isl_vec *vec1,
+ __isl_take isl_vec *vec2)
+{
+ struct isl_vec *vec;
+
+ if (!vec1 || !vec2)
+ goto error;
+ isl_assert(vec1->ctx, vec1->size > 0, goto error);
+ isl_assert(vec2->ctx, vec2->size > 0, goto error);
+ isl_assert(vec1->ctx, isl_int_is_one(vec1->el[0]), goto error);
+ isl_assert(vec2->ctx, isl_int_is_one(vec2->el[0]), goto error);
+
+ vec = isl_vec_alloc(vec1->ctx, vec1->size + vec2->size - 1);
+ if (!vec)
+ goto error;
+
+ isl_seq_cpy(vec->el, vec1->el, vec1->size);
+ isl_seq_cpy(vec->el + vec1->size, vec2->el + 1, vec2->size - 1);
+
+ isl_vec_free(vec1);
+ isl_vec_free(vec2);
+
+ return vec;
+error:
+ isl_vec_free(vec1);
+ isl_vec_free(vec2);
+ return NULL;
+}
+
+/* Give a basic set "bset" with recession cone "cone", compute and
+ * return an integer point in bset, if any.
+ *
+ * If the recession cone is full-dimensional, then we know that
+ * bset contains an infinite number of integer points and it is
+ * fairly easy to pick one of them.
+ * If the recession cone is not full-dimensional, then we first
+ * transform bset such that the bounded directions appear as
+ * the first dimensions of the transformed basic set.
+ * We do this by using a unimodular transformation that transforms
+ * the equalities in the recession cone to equalities on the first
+ * dimensions.
+ *
+ * The transformed set is then projected onto its bounded dimensions.
+ * Note that to compute this projection, we can simply drop all constraints
+ * involving any of the unbounded dimensions since these constraints
+ * cannot be combined to produce a constraint on the bounded dimensions.
+ * To see this, assume that there is such a combination of constraints
+ * that produces a constraint on the bounded dimensions. This means
+ * that some combination of the unbounded dimensions has both an upper
+ * bound and a lower bound in terms of the bounded dimensions, but then
+ * this combination would be a bounded direction too and would have been
+ * transformed into a bounded dimensions.
+ *
+ * We then compute a sample value in the bounded dimensions.
+ * If no such value can be found, then the original set did not contain
+ * any integer points and we are done.
+ * Otherwise, we plug in the value we found in the bounded dimensions,
+ * project out these bounded dimensions and end up with a set with
+ * a full-dimensional recession cone.
+ * A sample point in this set is computed by "rounding up" any
+ * rational point in the set.
+ *
+ * The sample points in the bounded and unbounded dimensions are
+ * then combined into a single sample point and transformed back
+ * to the original space.
+ */
+__isl_give isl_vec *isl_basic_set_sample_with_cone(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *cone)
+{
+ isl_size total;
+ unsigned cone_dim;
+ struct isl_mat *M, *U;
+ struct isl_vec *sample;
+ struct isl_vec *cone_sample;
+ struct isl_ctx *ctx;
+ struct isl_basic_set *bounded;
+
+ total = isl_basic_set_dim(cone, isl_dim_all);
+ if (!bset || total < 0)
+ goto error;
+
+ ctx = isl_basic_set_get_ctx(bset);
+ cone_dim = total - cone->n_eq;
+
+ M = isl_mat_sub_alloc6(ctx, cone->eq, 0, cone->n_eq, 1, total);
+ M = isl_mat_left_hermite(M, 0, &U, NULL);
+ if (!M)
+ goto error;
+ isl_mat_free(M);
+
+ U = isl_mat_lin_to_aff(U);
+ bset = isl_basic_set_preimage(bset, isl_mat_copy(U));
+
+ bounded = isl_basic_set_copy(bset);
+ bounded = isl_basic_set_drop_constraints_involving(bounded,
+ total - cone_dim, cone_dim);
+ bounded = isl_basic_set_drop_dims(bounded, total - cone_dim, cone_dim);
+ sample = sample_bounded(bounded);
+ if (!sample || sample->size == 0) {
+ isl_basic_set_free(bset);
+ isl_basic_set_free(cone);
+ isl_mat_free(U);
+ return sample;
+ }
+ bset = plug_in(bset, isl_vec_copy(sample));
+ cone_sample = rational_sample(bset);
+ cone_sample = round_up_in_cone(cone_sample, cone, isl_mat_copy(U));
+ sample = vec_concat(sample, cone_sample);
+ sample = isl_mat_vec_product(U, sample);
+ return sample;
+error:
+ isl_basic_set_free(cone);
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+static void vec_sum_of_neg(__isl_keep isl_vec *v, isl_int *s)
+{
+ int i;
+
+ isl_int_set_si(*s, 0);
+
+ for (i = 0; i < v->size; ++i)
+ if (isl_int_is_neg(v->el[i]))
+ isl_int_add(*s, *s, v->el[i]);
+}
+
+/* Given a tableau "tab", a tableau "tab_cone" that corresponds
+ * to the recession cone and the inverse of a new basis U = inv(B),
+ * with the unbounded directions in B last,
+ * add constraints to "tab" that ensure any rational value
+ * in the unbounded directions can be rounded up to an integer value.
+ *
+ * The new basis is given by x' = B x, i.e., x = U x'.
+ * For any rational value of the last tab->n_unbounded coordinates
+ * in the update tableau, the value that is obtained by rounding
+ * up this value should be contained in the original tableau.
+ * For any constraint "a x + c >= 0", we therefore need to add
+ * a constraint "a x + c + s >= 0", with s the sum of all negative
+ * entries in the last elements of "a U".
+ *
+ * Since we are not interested in the first entries of any of the "a U",
+ * we first drop the columns of U that correpond to bounded directions.
+ */
+static int tab_shift_cone(struct isl_tab *tab,
+ struct isl_tab *tab_cone, struct isl_mat *U)
+{
+ int i;
+ isl_int v;
+ struct isl_basic_set *bset = NULL;
+
+ if (tab && tab->n_unbounded == 0) {
+ isl_mat_free(U);
+ return 0;
+ }
+ isl_int_init(v);
+ if (!tab || !tab_cone || !U)
+ goto error;
+ bset = isl_tab_peek_bset(tab_cone);
+ U = isl_mat_drop_cols(U, 0, tab->n_var - tab->n_unbounded);
+ for (i = 0; i < bset->n_ineq; ++i) {
+ int ok;
+ struct isl_vec *row = NULL;
+ if (isl_tab_is_equality(tab_cone, tab_cone->n_eq + i))
+ continue;
+ row = isl_vec_alloc(bset->ctx, tab_cone->n_var);
+ if (!row)
+ goto error;
+ isl_seq_cpy(row->el, bset->ineq[i] + 1, tab_cone->n_var);
+ row = isl_vec_mat_product(row, isl_mat_copy(U));
+ if (!row)
+ goto error;
+ vec_sum_of_neg(row, &v);
+ isl_vec_free(row);
+ if (isl_int_is_zero(v))
+ continue;
+ if (isl_tab_extend_cons(tab, 1) < 0)
+ goto error;
+ isl_int_add(bset->ineq[i][0], bset->ineq[i][0], v);
+ ok = isl_tab_add_ineq(tab, bset->ineq[i]) >= 0;
+ isl_int_sub(bset->ineq[i][0], bset->ineq[i][0], v);
+ if (!ok)
+ goto error;
+ }
+
+ isl_mat_free(U);
+ isl_int_clear(v);
+ return 0;
+error:
+ isl_mat_free(U);
+ isl_int_clear(v);
+ return -1;
+}
+
+/* Compute and return an initial basis for the possibly
+ * unbounded tableau "tab". "tab_cone" is a tableau
+ * for the corresponding recession cone.
+ * Additionally, add constraints to "tab" that ensure
+ * that any rational value for the unbounded directions
+ * can be rounded up to an integer value.
+ *
+ * If the tableau is bounded, i.e., if the recession cone
+ * is zero-dimensional, then we just use inital_basis.
+ * Otherwise, we construct a basis whose first directions
+ * correspond to equalities, followed by bounded directions,
+ * i.e., equalities in the recession cone.
+ * The remaining directions are then unbounded.
+ */
+int isl_tab_set_initial_basis_with_cone(struct isl_tab *tab,
+ struct isl_tab *tab_cone)
+{
+ struct isl_mat *eq;
+ struct isl_mat *cone_eq;
+ struct isl_mat *U, *Q;
+
+ if (!tab || !tab_cone)
+ return -1;
+
+ if (tab_cone->n_col == tab_cone->n_dead) {
+ tab->basis = initial_basis(tab);
+ return tab->basis ? 0 : -1;
+ }
+
+ eq = tab_equalities(tab);
+ if (!eq)
+ return -1;
+ tab->n_zero = eq->n_row;
+ cone_eq = tab_equalities(tab_cone);
+ eq = isl_mat_concat(eq, cone_eq);
+ if (!eq)
+ return -1;
+ tab->n_unbounded = tab->n_var - (eq->n_row - tab->n_zero);
+ eq = isl_mat_left_hermite(eq, 0, &U, &Q);
+ if (!eq)
+ return -1;
+ isl_mat_free(eq);
+ tab->basis = isl_mat_lin_to_aff(Q);
+ if (tab_shift_cone(tab, tab_cone, U) < 0)
+ return -1;
+ if (!tab->basis)
+ return -1;
+ return 0;
+}
+
+/* Compute and return a sample point in bset using generalized basis
+ * reduction. We first check if the input set has a non-trivial
+ * recession cone. If so, we perform some extra preprocessing in
+ * sample_with_cone. Otherwise, we directly perform generalized basis
+ * reduction.
+ */
+static __isl_give isl_vec *gbr_sample(__isl_take isl_basic_set *bset)
+{
+ isl_size dim;
+ struct isl_basic_set *cone;
+
+ dim = isl_basic_set_dim(bset, isl_dim_all);
+ if (dim < 0)
+ goto error;
+
+ cone = isl_basic_set_recession_cone(isl_basic_set_copy(bset));
+ if (!cone)
+ goto error;
+
+ if (cone->n_eq < dim)
+ return isl_basic_set_sample_with_cone(bset, cone);
+
+ isl_basic_set_free(cone);
+ return sample_bounded(bset);
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+static __isl_give isl_vec *basic_set_sample(__isl_take isl_basic_set *bset,
+ int bounded)
+{
+ struct isl_ctx *ctx;
+ isl_size dim;
+ if (!bset)
+ return NULL;
+
+ ctx = bset->ctx;
+ if (isl_basic_set_plain_is_empty(bset))
+ return empty_sample(bset);
+
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ if (dim < 0 ||
+ isl_basic_set_check_no_params(bset) < 0 ||
+ isl_basic_set_check_no_locals(bset) < 0)
+ goto error;
+
+ if (bset->sample && bset->sample->size == 1 + dim) {
+ int contains = isl_basic_set_contains(bset, bset->sample);
+ if (contains < 0)
+ goto error;
+ if (contains) {
+ struct isl_vec *sample = isl_vec_copy(bset->sample);
+ isl_basic_set_free(bset);
+ return sample;
+ }
+ }
+ isl_vec_free(bset->sample);
+ bset->sample = NULL;
+
+ if (bset->n_eq > 0)
+ return sample_eq(bset, bounded ? isl_basic_set_sample_bounded
+ : isl_basic_set_sample_vec);
+ if (dim == 0)
+ return zero_sample(bset);
+ if (dim == 1)
+ return interval_sample(bset);
+
+ return bounded ? sample_bounded(bset) : gbr_sample(bset);
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+__isl_give isl_vec *isl_basic_set_sample_vec(__isl_take isl_basic_set *bset)
+{
+ return basic_set_sample(bset, 0);
+}
+
+/* Compute an integer sample in "bset", where the caller guarantees
+ * that "bset" is bounded.
+ */
+__isl_give isl_vec *isl_basic_set_sample_bounded(__isl_take isl_basic_set *bset)
+{
+ return basic_set_sample(bset, 1);
+}
+
+__isl_give isl_basic_set *isl_basic_set_from_vec(__isl_take isl_vec *vec)
+{
+ int i;
+ int k;
+ struct isl_basic_set *bset = NULL;
+ struct isl_ctx *ctx;
+ isl_size dim;
+
+ if (!vec)
+ return NULL;
+ ctx = vec->ctx;
+ isl_assert(ctx, vec->size != 0, goto error);
+
+ bset = isl_basic_set_alloc(ctx, 0, vec->size - 1, 0, vec->size - 1, 0);
+ dim = isl_basic_set_dim(bset, isl_dim_set);
+ if (dim < 0)
+ goto error;
+ for (i = dim - 1; i >= 0; --i) {
+ k = isl_basic_set_alloc_equality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bset->eq[k], 1 + dim);
+ isl_int_neg(bset->eq[k][0], vec->el[1 + i]);
+ isl_int_set(bset->eq[k][1 + i], vec->el[0]);
+ }
+ bset->sample = vec;
+
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ isl_vec_free(vec);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_sample(__isl_take isl_basic_map *bmap)
+{
+ struct isl_basic_set *bset;
+ struct isl_vec *sample_vec;
+
+ bset = isl_basic_map_underlying_set(isl_basic_map_copy(bmap));
+ sample_vec = isl_basic_set_sample_vec(bset);
+ if (!sample_vec)
+ goto error;
+ if (sample_vec->size == 0) {
+ isl_vec_free(sample_vec);
+ return isl_basic_map_set_to_empty(bmap);
+ }
+ isl_vec_free(bmap->sample);
+ bmap->sample = isl_vec_copy(sample_vec);
+ bset = isl_basic_set_from_vec(sample_vec);
+ return isl_basic_map_overlying_set(bset, bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_basic_set_sample(__isl_take isl_basic_set *bset)
+{
+ return isl_basic_map_sample(bset);
+}
+
+__isl_give isl_basic_map *isl_map_sample(__isl_take isl_map *map)
+{
+ int i;
+ isl_basic_map *sample = NULL;
+
+ if (!map)
+ goto error;
+
+ for (i = 0; i < map->n; ++i) {
+ sample = isl_basic_map_sample(isl_basic_map_copy(map->p[i]));
+ if (!sample)
+ goto error;
+ if (!ISL_F_ISSET(sample, ISL_BASIC_MAP_EMPTY))
+ break;
+ isl_basic_map_free(sample);
+ }
+ if (i == map->n)
+ sample = isl_basic_map_empty(isl_map_get_space(map));
+ isl_map_free(map);
+ return sample;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_set_sample(__isl_take isl_set *set)
+{
+ return bset_from_bmap(isl_map_sample(set_to_map(set)));
+}
+
+__isl_give isl_point *isl_basic_set_sample_point(__isl_take isl_basic_set *bset)
+{
+ isl_vec *vec;
+ isl_space *space;
+
+ space = isl_basic_set_get_space(bset);
+ bset = isl_basic_set_underlying_set(bset);
+ vec = isl_basic_set_sample_vec(bset);
+
+ return isl_point_alloc(space, vec);
+}
+
+__isl_give isl_point *isl_set_sample_point(__isl_take isl_set *set)
+{
+ int i;
+ isl_point *pnt;
+
+ if (!set)
+ return NULL;
+
+ for (i = 0; i < set->n; ++i) {
+ pnt = isl_basic_set_sample_point(isl_basic_set_copy(set->p[i]));
+ if (!pnt)
+ goto error;
+ if (!isl_point_is_void(pnt))
+ break;
+ isl_point_free(pnt);
+ }
+ if (i == set->n)
+ pnt = isl_point_void(isl_set_get_space(set));
+
+ isl_set_free(set);
+ return pnt;
+error:
+ isl_set_free(set);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sample.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sample.h
new file mode 100644
index 00000000000..13f0e5bef9f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sample.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_SAMPLE_H
+#define ISL_SAMPLE_H
+
+#include <isl/set.h>
+#include <isl_tab.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+__isl_give isl_vec *isl_basic_set_sample_vec(__isl_take isl_basic_set *bset);
+__isl_give isl_vec *isl_basic_set_sample_bounded(
+ __isl_take isl_basic_set *bset);
+__isl_give isl_vec *isl_basic_set_sample_with_cone(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *cone);
+
+__isl_give isl_basic_set *isl_basic_set_from_vec(__isl_take isl_vec *vec);
+
+int isl_tab_set_initial_basis_with_cone(struct isl_tab *tab,
+ struct isl_tab *tab_cone);
+__isl_give isl_vec *isl_tab_sample(struct isl_tab *tab);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scan.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scan.c
new file mode 100644
index 00000000000..c4666ed1b46
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scan.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include "isl_basis_reduction.h"
+#include "isl_scan.h"
+#include <isl_seq.h>
+#include "isl_tab.h"
+#include <isl_val_private.h>
+#include <isl_vec_private.h>
+
+struct isl_counter {
+ struct isl_scan_callback callback;
+ isl_int count;
+ isl_int max;
+};
+
+static isl_stat increment_counter(struct isl_scan_callback *cb,
+ __isl_take isl_vec *sample)
+{
+ struct isl_counter *cnt = (struct isl_counter *)cb;
+
+ isl_int_add_ui(cnt->count, cnt->count, 1);
+
+ isl_vec_free(sample);
+
+ if (isl_int_is_zero(cnt->max) || isl_int_lt(cnt->count, cnt->max))
+ return isl_stat_ok;
+ return isl_stat_error;
+}
+
+static int increment_range(struct isl_scan_callback *cb, isl_int min, isl_int max)
+{
+ struct isl_counter *cnt = (struct isl_counter *)cb;
+
+ isl_int_add(cnt->count, cnt->count, max);
+ isl_int_sub(cnt->count, cnt->count, min);
+ isl_int_add_ui(cnt->count, cnt->count, 1);
+
+ if (isl_int_is_zero(cnt->max) || isl_int_lt(cnt->count, cnt->max))
+ return 0;
+ isl_int_set(cnt->count, cnt->max);
+ return -1;
+}
+
+/* Call callback->add with the current sample value of the tableau "tab".
+ */
+static int add_solution(struct isl_tab *tab, struct isl_scan_callback *callback)
+{
+ struct isl_vec *sample;
+
+ if (!tab)
+ return -1;
+ sample = isl_tab_get_sample_value(tab);
+ if (!sample)
+ return -1;
+
+ return callback->add(callback, sample);
+}
+
+static isl_stat scan_0D(__isl_take isl_basic_set *bset,
+ struct isl_scan_callback *callback)
+{
+ struct isl_vec *sample;
+
+ sample = isl_vec_alloc(bset->ctx, 1);
+ isl_basic_set_free(bset);
+
+ if (!sample)
+ return isl_stat_error;
+
+ isl_int_set_si(sample->el[0], 1);
+
+ return callback->add(callback, sample);
+}
+
+/* Look for all integer points in "bset", which is assumed to be bounded,
+ * and call callback->add on each of them.
+ *
+ * We first compute a reduced basis for the set and then scan
+ * the set in the directions of this basis.
+ * We basically perform a depth first search, where in each level i
+ * we compute the range in the i-th basis vector direction, given
+ * fixed values in the directions of the previous basis vector.
+ * We then add an equality to the tableau fixing the value in the
+ * direction of the current basis vector to each value in the range
+ * in turn and then continue to the next level.
+ *
+ * The search is implemented iteratively. "level" identifies the current
+ * basis vector. "init" is true if we want the first value at the current
+ * level and false if we want the next value.
+ * Solutions are added in the leaves of the search tree, i.e., after
+ * we have fixed a value in each direction of the basis.
+ */
+isl_stat isl_basic_set_scan(__isl_take isl_basic_set *bset,
+ struct isl_scan_callback *callback)
+{
+ isl_size dim;
+ struct isl_mat *B = NULL;
+ struct isl_tab *tab = NULL;
+ struct isl_vec *min;
+ struct isl_vec *max;
+ struct isl_tab_undo **snap;
+ int level;
+ int init;
+ enum isl_lp_result res;
+
+ dim = isl_basic_set_dim(bset, isl_dim_all);
+ if (dim < 0) {
+ bset = isl_basic_set_free(bset);
+ return isl_stat_error;
+ }
+
+ if (dim == 0)
+ return scan_0D(bset, callback);
+
+ min = isl_vec_alloc(bset->ctx, dim);
+ max = isl_vec_alloc(bset->ctx, dim);
+ snap = isl_alloc_array(bset->ctx, struct isl_tab_undo *, dim);
+
+ if (!min || !max || !snap)
+ goto error;
+
+ tab = isl_tab_from_basic_set(bset, 0);
+ if (!tab)
+ goto error;
+ if (isl_tab_extend_cons(tab, dim + 1) < 0)
+ goto error;
+
+ tab->basis = isl_mat_identity(bset->ctx, 1 + dim);
+ if (1)
+ tab = isl_tab_compute_reduced_basis(tab);
+ if (!tab)
+ goto error;
+ B = isl_mat_copy(tab->basis);
+ if (!B)
+ goto error;
+
+ level = 0;
+ init = 1;
+
+ while (level >= 0) {
+ int empty = 0;
+ if (init) {
+ res = isl_tab_min(tab, B->row[1 + level],
+ bset->ctx->one, &min->el[level], NULL, 0);
+ if (res == isl_lp_empty)
+ empty = 1;
+ if (res == isl_lp_error || res == isl_lp_unbounded)
+ goto error;
+ isl_seq_neg(B->row[1 + level] + 1,
+ B->row[1 + level] + 1, dim);
+ res = isl_tab_min(tab, B->row[1 + level],
+ bset->ctx->one, &max->el[level], NULL, 0);
+ isl_seq_neg(B->row[1 + level] + 1,
+ B->row[1 + level] + 1, dim);
+ isl_int_neg(max->el[level], max->el[level]);
+ if (res == isl_lp_empty)
+ empty = 1;
+ if (res == isl_lp_error || res == isl_lp_unbounded)
+ goto error;
+ snap[level] = isl_tab_snap(tab);
+ } else
+ isl_int_add_ui(min->el[level], min->el[level], 1);
+
+ if (empty || isl_int_gt(min->el[level], max->el[level])) {
+ level--;
+ init = 0;
+ if (level >= 0)
+ if (isl_tab_rollback(tab, snap[level]) < 0)
+ goto error;
+ continue;
+ }
+ if (level == dim - 1 && callback->add == increment_counter) {
+ if (increment_range(callback,
+ min->el[level], max->el[level]))
+ goto error;
+ level--;
+ init = 0;
+ if (level >= 0)
+ if (isl_tab_rollback(tab, snap[level]) < 0)
+ goto error;
+ continue;
+ }
+ isl_int_neg(B->row[1 + level][0], min->el[level]);
+ if (isl_tab_add_valid_eq(tab, B->row[1 + level]) < 0)
+ goto error;
+ isl_int_set_si(B->row[1 + level][0], 0);
+ if (level < dim - 1) {
+ ++level;
+ init = 1;
+ continue;
+ }
+ if (add_solution(tab, callback) < 0)
+ goto error;
+ init = 0;
+ if (isl_tab_rollback(tab, snap[level]) < 0)
+ goto error;
+ }
+
+ isl_tab_free(tab);
+ free(snap);
+ isl_vec_free(min);
+ isl_vec_free(max);
+ isl_basic_set_free(bset);
+ isl_mat_free(B);
+ return isl_stat_ok;
+error:
+ isl_tab_free(tab);
+ free(snap);
+ isl_vec_free(min);
+ isl_vec_free(max);
+ isl_basic_set_free(bset);
+ isl_mat_free(B);
+ return isl_stat_error;
+}
+
+isl_stat isl_set_scan(__isl_take isl_set *set,
+ struct isl_scan_callback *callback)
+{
+ int i;
+
+ if (!set || !callback)
+ goto error;
+
+ set = isl_set_cow(set);
+ set = isl_set_make_disjoint(set);
+ set = isl_set_compute_divs(set);
+ if (!set)
+ goto error;
+
+ for (i = 0; i < set->n; ++i)
+ if (isl_basic_set_scan(isl_basic_set_copy(set->p[i]),
+ callback) < 0)
+ goto error;
+
+ isl_set_free(set);
+ return isl_stat_ok;
+error:
+ isl_set_free(set);
+ return isl_stat_error;
+}
+
+int isl_basic_set_count_upto(__isl_keep isl_basic_set *bset,
+ isl_int max, isl_int *count)
+{
+ struct isl_counter cnt = { { &increment_counter } };
+
+ if (!bset)
+ return -1;
+
+ isl_int_init(cnt.count);
+ isl_int_init(cnt.max);
+
+ isl_int_set_si(cnt.count, 0);
+ isl_int_set(cnt.max, max);
+ if (isl_basic_set_scan(isl_basic_set_copy(bset), &cnt.callback) < 0 &&
+ isl_int_lt(cnt.count, cnt.max))
+ goto error;
+
+ isl_int_set(*count, cnt.count);
+ isl_int_clear(cnt.max);
+ isl_int_clear(cnt.count);
+
+ return 0;
+error:
+ isl_int_clear(cnt.count);
+ return -1;
+}
+
+int isl_set_count_upto(__isl_keep isl_set *set, isl_int max, isl_int *count)
+{
+ struct isl_counter cnt = { { &increment_counter } };
+
+ if (!set)
+ return -1;
+
+ isl_int_init(cnt.count);
+ isl_int_init(cnt.max);
+
+ isl_int_set_si(cnt.count, 0);
+ isl_int_set(cnt.max, max);
+ if (isl_set_scan(isl_set_copy(set), &cnt.callback) < 0 &&
+ isl_int_lt(cnt.count, cnt.max))
+ goto error;
+
+ isl_int_set(*count, cnt.count);
+ isl_int_clear(cnt.max);
+ isl_int_clear(cnt.count);
+
+ return 0;
+error:
+ isl_int_clear(cnt.count);
+ return -1;
+}
+
+int isl_set_count(__isl_keep isl_set *set, isl_int *count)
+{
+ if (!set)
+ return -1;
+ return isl_set_count_upto(set, set->ctx->zero, count);
+}
+
+/* Count the total number of elements in "set" (in an inefficient way) and
+ * return the result.
+ */
+__isl_give isl_val *isl_set_count_val(__isl_keep isl_set *set)
+{
+ isl_val *v;
+
+ if (!set)
+ return NULL;
+ v = isl_val_zero(isl_set_get_ctx(set));
+ v = isl_val_cow(v);
+ if (!v)
+ return NULL;
+ if (isl_set_count(set, &v->n) < 0)
+ v = isl_val_free(v);
+ return v;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scan.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scan.h
new file mode 100644
index 00000000000..f2a5100e4c0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scan.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_SCAN_H
+#define ISL_SCAN_H
+
+#include <isl/set.h>
+#include <isl/vec.h>
+
+struct isl_scan_callback {
+ isl_stat (*add)(struct isl_scan_callback *cb,
+ __isl_take isl_vec *sample);
+};
+
+isl_stat isl_basic_set_scan(__isl_take isl_basic_set *bset,
+ struct isl_scan_callback *callback);
+isl_stat isl_set_scan(__isl_take isl_set *set,
+ struct isl_scan_callback *callback);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule.c
new file mode 100644
index 00000000000..bdeeaba001b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule.c
@@ -0,0 +1,684 @@
+/*
+ * Copyright 2011 INRIA Saclay
+ * Copyright 2012-2014 Ecole Normale Superieure
+ * Copyright 2016 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/ctx.h>
+#include <isl/val.h>
+#include <isl_aff_private.h>
+#include <isl/map.h>
+#include <isl/set.h>
+#include <isl/schedule.h>
+#include <isl/schedule_node.h>
+#include <isl_sort.h>
+#include <isl/printer.h>
+#include <isl_schedule_private.h>
+#include <isl_schedule_tree.h>
+#include <isl_schedule_node_private.h>
+
+/* Return a schedule encapsulating the given schedule tree.
+ *
+ * We currently only allow schedule trees with a domain or extension as root.
+ *
+ * The leaf field is initialized as a leaf node so that it can be
+ * used to represent leaves in the constructed schedule.
+ * The reference count is set to -1 since the isl_schedule_tree
+ * should never be freed. It is up to the (internal) users of
+ * these leaves to ensure that they are only used while the schedule
+ * is still alive.
+ */
+__isl_give isl_schedule *isl_schedule_from_schedule_tree(isl_ctx *ctx,
+ __isl_take isl_schedule_tree *tree)
+{
+ enum isl_schedule_node_type type;
+ isl_schedule *schedule;
+
+ if (!tree)
+ return NULL;
+ type = isl_schedule_tree_get_type(tree);
+ if (type != isl_schedule_node_domain &&
+ type != isl_schedule_node_extension)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_unsupported,
+ "root of schedule tree should be a domain or extension",
+ goto error);
+
+ schedule = isl_calloc_type(ctx, isl_schedule);
+ if (!schedule)
+ goto error;
+
+ schedule->ref = 1;
+ schedule->root = tree;
+ schedule->leaf = isl_schedule_tree_leaf(ctx);
+
+ if (!schedule->leaf)
+ return isl_schedule_free(schedule);
+ return schedule;
+error:
+ isl_schedule_tree_free(tree);
+ return NULL;
+}
+
+/* Return a pointer to a schedule with as single node
+ * a domain node with the given domain.
+ */
+__isl_give isl_schedule *isl_schedule_from_domain(
+ __isl_take isl_union_set *domain)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ ctx = isl_union_set_get_ctx(domain);
+ tree = isl_schedule_tree_from_domain(domain);
+ return isl_schedule_from_schedule_tree(ctx, tree);
+}
+
+/* Return a pointer to a schedule with as single node
+ * a domain node with an empty domain.
+ */
+__isl_give isl_schedule *isl_schedule_empty(__isl_take isl_space *space)
+{
+ return isl_schedule_from_domain(isl_union_set_empty(space));
+}
+
+/* Return a new reference to "sched".
+ */
+__isl_give isl_schedule *isl_schedule_copy(__isl_keep isl_schedule *sched)
+{
+ if (!sched)
+ return NULL;
+
+ sched->ref++;
+ return sched;
+}
+
+/* Return an isl_schedule that is equal to "schedule" and that has only
+ * a single reference.
+ */
+__isl_give isl_schedule *isl_schedule_cow(__isl_take isl_schedule *schedule)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ if (!schedule)
+ return NULL;
+ if (schedule->ref == 1)
+ return schedule;
+
+ ctx = isl_schedule_get_ctx(schedule);
+ schedule->ref--;
+ tree = isl_schedule_tree_copy(schedule->root);
+ return isl_schedule_from_schedule_tree(ctx, tree);
+}
+
+__isl_null isl_schedule *isl_schedule_free(__isl_take isl_schedule *sched)
+{
+ if (!sched)
+ return NULL;
+
+ if (--sched->ref > 0)
+ return NULL;
+
+ isl_schedule_tree_free(sched->root);
+ isl_schedule_tree_free(sched->leaf);
+ free(sched);
+ return NULL;
+}
+
+/* Replace the root of "schedule" by "tree".
+ */
+__isl_give isl_schedule *isl_schedule_set_root(
+ __isl_take isl_schedule *schedule, __isl_take isl_schedule_tree *tree)
+{
+ if (!schedule || !tree)
+ goto error;
+ if (schedule->root == tree) {
+ isl_schedule_tree_free(tree);
+ return schedule;
+ }
+
+ schedule = isl_schedule_cow(schedule);
+ if (!schedule)
+ goto error;
+ isl_schedule_tree_free(schedule->root);
+ schedule->root = tree;
+
+ return schedule;
+error:
+ isl_schedule_free(schedule);
+ isl_schedule_tree_free(tree);
+ return NULL;
+}
+
+isl_ctx *isl_schedule_get_ctx(__isl_keep isl_schedule *schedule)
+{
+ return schedule ? isl_schedule_tree_get_ctx(schedule->leaf) : NULL;
+}
+
+/* Return a pointer to the leaf of "schedule".
+ */
+__isl_keep isl_schedule_tree *isl_schedule_peek_leaf(
+ __isl_keep isl_schedule *schedule)
+{
+ return schedule ? schedule->leaf : NULL;
+}
+
+/* Are "schedule1" and "schedule2" obviously equal to each other?
+ */
+isl_bool isl_schedule_plain_is_equal(__isl_keep isl_schedule *schedule1,
+ __isl_keep isl_schedule *schedule2)
+{
+ if (!schedule1 || !schedule2)
+ return isl_bool_error;
+ if (schedule1 == schedule2)
+ return isl_bool_true;
+ return isl_schedule_tree_plain_is_equal(schedule1->root,
+ schedule2->root);
+}
+
+/* Return the (parameter) space of the schedule, i.e., the space
+ * of the root domain.
+ */
+__isl_give isl_space *isl_schedule_get_space(
+ __isl_keep isl_schedule *schedule)
+{
+ enum isl_schedule_node_type type;
+ isl_space *space;
+ isl_union_set *domain;
+
+ if (!schedule)
+ return NULL;
+ type = isl_schedule_tree_get_type(schedule->root);
+ if (type != isl_schedule_node_domain)
+ isl_die(isl_schedule_get_ctx(schedule), isl_error_internal,
+ "root node not a domain node", return NULL);
+
+ domain = isl_schedule_tree_domain_get_domain(schedule->root);
+ space = isl_union_set_get_space(domain);
+ isl_union_set_free(domain);
+
+ return space;
+}
+
+/* Return a pointer to the root of "schedule".
+ */
+__isl_give isl_schedule_node *isl_schedule_get_root(
+ __isl_keep isl_schedule *schedule)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+ isl_schedule_tree_list *ancestors;
+
+ if (!schedule)
+ return NULL;
+
+ ctx = isl_schedule_get_ctx(schedule);
+ tree = isl_schedule_tree_copy(schedule->root);
+ schedule = isl_schedule_copy(schedule);
+ ancestors = isl_schedule_tree_list_alloc(ctx, 0);
+ return isl_schedule_node_alloc(schedule, tree, ancestors, NULL);
+}
+
+/* Return the domain of the root domain node of "schedule".
+ */
+__isl_give isl_union_set *isl_schedule_get_domain(
+ __isl_keep isl_schedule *schedule)
+{
+ if (!schedule)
+ return NULL;
+ return isl_schedule_tree_domain_get_domain(schedule->root);
+}
+
+/* Traverse all nodes of "sched" in depth first preorder.
+ *
+ * If "fn" returns -1 on any of the nodes, then the traversal is aborted.
+ * If "fn" returns 0 on any of the nodes, then the subtree rooted
+ * at that node is skipped.
+ *
+ * Return 0 on success and -1 on failure.
+ */
+isl_stat isl_schedule_foreach_schedule_node_top_down(
+ __isl_keep isl_schedule *sched,
+ isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user),
+ void *user)
+{
+ isl_schedule_node *node;
+ isl_stat r;
+
+ if (!sched)
+ return isl_stat_error;
+
+ node = isl_schedule_get_root(sched);
+ r = isl_schedule_node_foreach_descendant_top_down(node, fn, user);
+ isl_schedule_node_free(node);
+
+ return r;
+}
+
+/* Traverse the node of "sched" in depth first postorder,
+ * allowing the user to modify the visited node.
+ * The traversal continues from the node returned by the callback function.
+ * It is the responsibility of the user to ensure that this does not
+ * lead to an infinite loop. It is safest to always return a pointer
+ * to the same position (same ancestors and child positions) as the input node.
+ */
+__isl_give isl_schedule *isl_schedule_map_schedule_node_bottom_up(
+ __isl_take isl_schedule *schedule,
+ __isl_give isl_schedule_node *(*fn)(
+ __isl_take isl_schedule_node *node, void *user), void *user)
+{
+ isl_schedule_node *node;
+
+ node = isl_schedule_get_root(schedule);
+ isl_schedule_free(schedule);
+
+ node = isl_schedule_node_map_descendant_bottom_up(node, fn, user);
+ schedule = isl_schedule_node_get_schedule(node);
+ isl_schedule_node_free(node);
+
+ return schedule;
+}
+
+/* Wrapper around isl_schedule_node_reset_user for use as
+ * an isl_schedule_map_schedule_node_bottom_up callback.
+ */
+static __isl_give isl_schedule_node *reset_user(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ return isl_schedule_node_reset_user(node);
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * in the schedule "schedule".
+ */
+__isl_give isl_schedule *isl_schedule_reset_user(
+ __isl_take isl_schedule *schedule)
+{
+ return isl_schedule_map_schedule_node_bottom_up(schedule, &reset_user,
+ NULL);
+}
+
+/* Wrapper around isl_schedule_node_align_params for use as
+ * an isl_schedule_map_schedule_node_bottom_up callback.
+ */
+static __isl_give isl_schedule_node *align_params(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ isl_space *space = user;
+
+ return isl_schedule_node_align_params(node, isl_space_copy(space));
+}
+
+/* Align the parameters of all nodes in schedule "schedule"
+ * to those of "space".
+ */
+__isl_give isl_schedule *isl_schedule_align_params(
+ __isl_take isl_schedule *schedule, __isl_take isl_space *space)
+{
+ schedule = isl_schedule_map_schedule_node_bottom_up(schedule,
+ &align_params, space);
+ isl_space_free(space);
+ return schedule;
+}
+
+/* Wrapper around isl_schedule_node_pullback_union_pw_multi_aff for use as
+ * an isl_schedule_map_schedule_node_bottom_up callback.
+ */
+static __isl_give isl_schedule_node *pullback_upma(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ isl_union_pw_multi_aff *upma = user;
+
+ return isl_schedule_node_pullback_union_pw_multi_aff(node,
+ isl_union_pw_multi_aff_copy(upma));
+}
+
+/* Compute the pullback of "schedule" by the function represented by "upma".
+ * In other words, plug in "upma" in the iteration domains of "schedule".
+ *
+ * The schedule tree is not allowed to contain any expansion nodes.
+ */
+__isl_give isl_schedule *isl_schedule_pullback_union_pw_multi_aff(
+ __isl_take isl_schedule *schedule,
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ schedule = isl_schedule_map_schedule_node_bottom_up(schedule,
+ &pullback_upma, upma);
+ isl_union_pw_multi_aff_free(upma);
+ return schedule;
+}
+
+/* Expand the schedule "schedule" by extending all leaves
+ * with an expansion node with as subtree the tree of "expansion".
+ * The expansion of the expansion node is determined by "contraction"
+ * and the domain of "expansion". That is, the domain of "expansion"
+ * is contracted according to "contraction".
+ *
+ * Call isl_schedule_node_expand after extracting the required
+ * information from "expansion".
+ */
+__isl_give isl_schedule *isl_schedule_expand(__isl_take isl_schedule *schedule,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_schedule *expansion)
+{
+ isl_union_set *domain;
+ isl_schedule_node *node;
+ isl_schedule_tree *tree;
+
+ domain = isl_schedule_get_domain(expansion);
+
+ node = isl_schedule_get_root(expansion);
+ node = isl_schedule_node_child(node, 0);
+ tree = isl_schedule_node_get_tree(node);
+ isl_schedule_node_free(node);
+ isl_schedule_free(expansion);
+
+ node = isl_schedule_get_root(schedule);
+ isl_schedule_free(schedule);
+ node = isl_schedule_node_expand(node, contraction, domain, tree);
+ schedule = isl_schedule_node_get_schedule(node);
+ isl_schedule_node_free(node);
+
+ return schedule;
+}
+
+/* Intersect the domain of the schedule "schedule" with "domain".
+ * The root of "schedule" is required to be a domain node.
+ */
+__isl_give isl_schedule *isl_schedule_intersect_domain(
+ __isl_take isl_schedule *schedule, __isl_take isl_union_set *domain)
+{
+ enum isl_schedule_node_type root_type;
+ isl_schedule_node *node;
+
+ if (!schedule || !domain)
+ goto error;
+
+ root_type = isl_schedule_tree_get_type(schedule->root);
+ if (root_type != isl_schedule_node_domain)
+ isl_die(isl_schedule_get_ctx(schedule), isl_error_invalid,
+ "root node must be a domain node", goto error);
+
+ node = isl_schedule_get_root(schedule);
+ isl_schedule_free(schedule);
+ node = isl_schedule_node_domain_intersect_domain(node, domain);
+ schedule = isl_schedule_node_get_schedule(node);
+ isl_schedule_node_free(node);
+
+ return schedule;
+error:
+ isl_schedule_free(schedule);
+ isl_union_set_free(domain);
+ return NULL;
+}
+
+/* Replace the domain of the schedule "schedule" with the gist
+ * of the original domain with respect to the parameter domain "context".
+ */
+__isl_give isl_schedule *isl_schedule_gist_domain_params(
+ __isl_take isl_schedule *schedule, __isl_take isl_set *context)
+{
+ enum isl_schedule_node_type root_type;
+ isl_schedule_node *node;
+
+ if (!schedule || !context)
+ goto error;
+
+ root_type = isl_schedule_tree_get_type(schedule->root);
+ if (root_type != isl_schedule_node_domain)
+ isl_die(isl_schedule_get_ctx(schedule), isl_error_invalid,
+ "root node must be a domain node", goto error);
+
+ node = isl_schedule_get_root(schedule);
+ isl_schedule_free(schedule);
+ node = isl_schedule_node_domain_gist_params(node, context);
+ schedule = isl_schedule_node_get_schedule(node);
+ isl_schedule_node_free(node);
+
+ return schedule;
+error:
+ isl_schedule_free(schedule);
+ isl_set_free(context);
+ return NULL;
+}
+
+/* Return an isl_union_map representation of the schedule. In particular,
+ * return an isl_union_map corresponding to the subtree schedule of the child
+ * of the root domain node. That is, we do not intersect the domain
+ * of the returned isl_union_map with the domain constraints.
+ */
+__isl_give isl_union_map *isl_schedule_get_map(__isl_keep isl_schedule *sched)
+{
+ enum isl_schedule_node_type type;
+ isl_schedule_node *node;
+ isl_union_map *umap;
+
+ if (!sched)
+ return NULL;
+ type = isl_schedule_tree_get_type(sched->root);
+ if (type != isl_schedule_node_domain)
+ isl_die(isl_schedule_get_ctx(sched), isl_error_internal,
+ "root node not a domain node", return NULL);
+
+ node = isl_schedule_get_root(sched);
+ node = isl_schedule_node_child(node, 0);
+ umap = isl_schedule_node_get_subtree_schedule_union_map(node);
+ isl_schedule_node_free(node);
+
+ return umap;
+}
+
+/* Insert a band node with partial schedule "partial" between the domain
+ * root node of "schedule" and its single child.
+ * Return a pointer to the updated schedule.
+ *
+ * If any of the nodes in the tree depend on the set of outer band nodes
+ * then we refuse to insert the band node.
+ */
+__isl_give isl_schedule *isl_schedule_insert_partial_schedule(
+ __isl_take isl_schedule *schedule,
+ __isl_take isl_multi_union_pw_aff *partial)
+{
+ isl_schedule_node *node;
+ int anchored;
+
+ node = isl_schedule_get_root(schedule);
+ isl_schedule_free(schedule);
+ if (!node)
+ goto error;
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_domain)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_internal,
+ "root node not a domain node", goto error);
+
+ node = isl_schedule_node_child(node, 0);
+ anchored = isl_schedule_node_is_subtree_anchored(node);
+ if (anchored < 0)
+ goto error;
+ if (anchored)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot insert band node in anchored subtree",
+ goto error);
+ node = isl_schedule_node_insert_partial_schedule(node, partial);
+
+ schedule = isl_schedule_node_get_schedule(node);
+ isl_schedule_node_free(node);
+
+ return schedule;
+error:
+ isl_schedule_node_free(node);
+ isl_multi_union_pw_aff_free(partial);
+ return NULL;
+}
+
+/* Insert a context node with constraints "context" between the domain
+ * root node of "schedule" and its single child.
+ * Return a pointer to the updated schedule.
+ */
+__isl_give isl_schedule *isl_schedule_insert_context(
+ __isl_take isl_schedule *schedule, __isl_take isl_set *context)
+{
+ isl_schedule_node *node;
+
+ node = isl_schedule_get_root(schedule);
+ isl_schedule_free(schedule);
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_insert_context(node, context);
+ schedule = isl_schedule_node_get_schedule(node);
+ isl_schedule_node_free(node);
+
+ return schedule;
+}
+
+/* Insert a guard node with constraints "guard" between the domain
+ * root node of "schedule" and its single child.
+ * Return a pointer to the updated schedule.
+ */
+__isl_give isl_schedule *isl_schedule_insert_guard(
+ __isl_take isl_schedule *schedule, __isl_take isl_set *guard)
+{
+ isl_schedule_node *node;
+
+ node = isl_schedule_get_root(schedule);
+ isl_schedule_free(schedule);
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_insert_guard(node, guard);
+ schedule = isl_schedule_node_get_schedule(node);
+ isl_schedule_node_free(node);
+
+ return schedule;
+}
+
+/* Return a tree with as top-level node a filter corresponding to "filter" and
+ * as child, the (single) child of "tree".
+ * However, if this single child is of type "type", then the filter is inserted
+ * in the children of this single child instead.
+ */
+static __isl_give isl_schedule_tree *insert_filter_in_child_of_type(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter,
+ enum isl_schedule_node_type type)
+{
+ if (!isl_schedule_tree_has_children(tree)) {
+ isl_schedule_tree_free(tree);
+ return isl_schedule_tree_from_filter(filter);
+ } else {
+ tree = isl_schedule_tree_child(tree, 0);
+ }
+
+ if (isl_schedule_tree_get_type(tree) == type)
+ tree = isl_schedule_tree_children_insert_filter(tree, filter);
+ else
+ tree = isl_schedule_tree_insert_filter(tree, filter);
+
+ return tree;
+}
+
+/* Construct a schedule that combines the schedules "schedule1" and "schedule2"
+ * with a top-level node (underneath the domain node) of type "type",
+ * either isl_schedule_node_sequence or isl_schedule_node_set.
+ * The domains of the two schedules are assumed to be disjoint.
+ *
+ * The new schedule has as domain the union of the domains of the two
+ * schedules. The child of the domain node is a node of type "type"
+ * with two filters corresponding to the domains of the input schedules.
+ * If one (or both) of the top-level nodes of the two schedules is itself
+ * of type "type", then the filter is pushed into the children of that
+ * node and the sequence or set is flattened.
+ */
+__isl_give isl_schedule *isl_schedule_pair(enum isl_schedule_node_type type,
+ __isl_take isl_schedule *schedule1, __isl_take isl_schedule *schedule2)
+{
+ int disjoint;
+ isl_ctx *ctx;
+ enum isl_schedule_node_type root_type;
+ isl_schedule_tree *tree1, *tree2;
+ isl_union_set *filter1, *filter2, *domain;
+
+ if (!schedule1 || !schedule2)
+ goto error;
+
+ root_type = isl_schedule_tree_get_type(schedule1->root);
+ if (root_type != isl_schedule_node_domain)
+ isl_die(isl_schedule_get_ctx(schedule1), isl_error_internal,
+ "root node not a domain node", goto error);
+ root_type = isl_schedule_tree_get_type(schedule2->root);
+ if (root_type != isl_schedule_node_domain)
+ isl_die(isl_schedule_get_ctx(schedule1), isl_error_internal,
+ "root node not a domain node", goto error);
+
+ ctx = isl_schedule_get_ctx(schedule1);
+ tree1 = isl_schedule_tree_copy(schedule1->root);
+ filter1 = isl_schedule_tree_domain_get_domain(tree1);
+ tree2 = isl_schedule_tree_copy(schedule2->root);
+ filter2 = isl_schedule_tree_domain_get_domain(tree2);
+
+ isl_schedule_free(schedule1);
+ isl_schedule_free(schedule2);
+
+ disjoint = isl_union_set_is_disjoint(filter1, filter2);
+ if (disjoint < 0)
+ filter1 = isl_union_set_free(filter1);
+ if (!disjoint)
+ isl_die(ctx, isl_error_invalid,
+ "schedule domains not disjoint",
+ filter1 = isl_union_set_free(filter1));
+
+ domain = isl_union_set_union(isl_union_set_copy(filter1),
+ isl_union_set_copy(filter2));
+ filter1 = isl_union_set_gist(filter1, isl_union_set_copy(domain));
+ filter2 = isl_union_set_gist(filter2, isl_union_set_copy(domain));
+
+ tree1 = insert_filter_in_child_of_type(tree1, filter1, type);
+ tree2 = insert_filter_in_child_of_type(tree2, filter2, type);
+
+ tree1 = isl_schedule_tree_from_pair(type, tree1, tree2);
+ tree1 = isl_schedule_tree_insert_domain(tree1, domain);
+
+ return isl_schedule_from_schedule_tree(ctx, tree1);
+error:
+ isl_schedule_free(schedule1);
+ isl_schedule_free(schedule2);
+ return NULL;
+}
+
+/* Construct a schedule that combines the schedules "schedule1" and "schedule2"
+ * through a sequence node.
+ * The domains of the input schedules are assumed to be disjoint.
+ */
+__isl_give isl_schedule *isl_schedule_sequence(
+ __isl_take isl_schedule *schedule1, __isl_take isl_schedule *schedule2)
+{
+ return isl_schedule_pair(isl_schedule_node_sequence,
+ schedule1, schedule2);
+}
+
+/* Construct a schedule that combines the schedules "schedule1" and "schedule2"
+ * through a set node.
+ * The domains of the input schedules are assumed to be disjoint.
+ */
+__isl_give isl_schedule *isl_schedule_set(
+ __isl_take isl_schedule *schedule1, __isl_take isl_schedule *schedule2)
+{
+ return isl_schedule_pair(isl_schedule_node_set, schedule1, schedule2);
+}
+
+/* Print "schedule" to "p".
+ */
+__isl_give isl_printer *isl_printer_print_schedule(__isl_take isl_printer *p,
+ __isl_keep isl_schedule *schedule)
+{
+ if (!schedule)
+ return isl_printer_free(p);
+
+ return isl_printer_print_schedule_tree(p, schedule->root);
+}
+
+#undef BASE
+#define BASE schedule
+#include <print_templ_yaml.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_band.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_band.c
new file mode 100644
index 00000000000..668579a42d7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_band.c
@@ -0,0 +1,1310 @@
+/*
+ * Copyright 2013-2014 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#include <string.h>
+#include <isl/val.h>
+#include <isl/space.h>
+#include <isl/map.h>
+#include <isl/schedule_node.h>
+#include <isl_schedule_band.h>
+#include <isl_schedule_private.h>
+
+isl_ctx *isl_schedule_band_get_ctx(__isl_keep isl_schedule_band *band)
+{
+ return band ? isl_multi_union_pw_aff_get_ctx(band->mupa) : NULL;
+}
+
+/* Return a new uninitialized isl_schedule_band.
+ */
+static __isl_give isl_schedule_band *isl_schedule_band_alloc(isl_ctx *ctx)
+{
+ isl_schedule_band *band;
+
+ band = isl_calloc_type(ctx, isl_schedule_band);
+ if (!band)
+ return NULL;
+
+ band->ref = 1;
+
+ return band;
+}
+
+/* Return a new isl_schedule_band with partial schedule "mupa".
+ * First replace "mupa" by its greatest integer part to ensure
+ * that the schedule is always integral.
+ * The band is not marked permutable, the dimensions are not
+ * marked coincident and the AST build options are empty.
+ * Since there are no build options, the node is not anchored.
+ */
+__isl_give isl_schedule_band *isl_schedule_band_from_multi_union_pw_aff(
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ isl_size dim;
+ isl_ctx *ctx;
+ isl_schedule_band *band;
+ isl_space *space;
+
+ mupa = isl_multi_union_pw_aff_floor(mupa);
+ dim = isl_multi_union_pw_aff_size(mupa);
+ if (dim < 0)
+ goto error;
+ ctx = isl_multi_union_pw_aff_get_ctx(mupa);
+ band = isl_schedule_band_alloc(ctx);
+ if (!band)
+ goto error;
+
+ band->n = dim;
+ band->coincident = isl_calloc_array(ctx, int, band->n);
+ band->mupa = mupa;
+ space = isl_space_params_alloc(ctx, 0);
+ band->ast_build_options = isl_union_set_empty(space);
+ band->anchored = 0;
+
+ if ((band->n && !band->coincident) || !band->ast_build_options)
+ return isl_schedule_band_free(band);
+
+ return band;
+error:
+ isl_multi_union_pw_aff_free(mupa);
+ return NULL;
+}
+
+/* Create a duplicate of the given isl_schedule_band.
+ */
+__isl_give isl_schedule_band *isl_schedule_band_dup(
+ __isl_keep isl_schedule_band *band)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_schedule_band *dup;
+
+ if (!band)
+ return NULL;
+
+ ctx = isl_schedule_band_get_ctx(band);
+ dup = isl_schedule_band_alloc(ctx);
+ if (!dup)
+ return NULL;
+
+ dup->n = band->n;
+ dup->coincident = isl_alloc_array(ctx, int, band->n);
+ if (band->n && !dup->coincident)
+ return isl_schedule_band_free(dup);
+
+ for (i = 0; i < band->n; ++i)
+ dup->coincident[i] = band->coincident[i];
+ dup->permutable = band->permutable;
+
+ dup->mupa = isl_multi_union_pw_aff_copy(band->mupa);
+ dup->ast_build_options = isl_union_set_copy(band->ast_build_options);
+ if (!dup->mupa || !dup->ast_build_options)
+ return isl_schedule_band_free(dup);
+
+ if (band->loop_type) {
+ dup->loop_type = isl_alloc_array(ctx,
+ enum isl_ast_loop_type, band->n);
+ if (band->n && !dup->loop_type)
+ return isl_schedule_band_free(dup);
+ for (i = 0; i < band->n; ++i)
+ dup->loop_type[i] = band->loop_type[i];
+ }
+ if (band->isolate_loop_type) {
+ dup->isolate_loop_type = isl_alloc_array(ctx,
+ enum isl_ast_loop_type, band->n);
+ if (band->n && !dup->isolate_loop_type)
+ return isl_schedule_band_free(dup);
+ for (i = 0; i < band->n; ++i)
+ dup->isolate_loop_type[i] = band->isolate_loop_type[i];
+ }
+
+ return dup;
+}
+
+/* Return an isl_schedule_band that is equal to "band" and that has only
+ * a single reference.
+ */
+__isl_give isl_schedule_band *isl_schedule_band_cow(
+ __isl_take isl_schedule_band *band)
+{
+ if (!band)
+ return NULL;
+
+ if (band->ref == 1)
+ return band;
+ band->ref--;
+ return isl_schedule_band_dup(band);
+}
+
+/* Return a new reference to "band".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_copy(
+ __isl_keep isl_schedule_band *band)
+{
+ if (!band)
+ return NULL;
+
+ band->ref++;
+ return band;
+}
+
+/* Free a reference to "band" and return NULL.
+ */
+__isl_null isl_schedule_band *isl_schedule_band_free(
+ __isl_take isl_schedule_band *band)
+{
+ if (!band)
+ return NULL;
+
+ if (--band->ref > 0)
+ return NULL;
+
+ isl_multi_union_pw_aff_free(band->mupa);
+ isl_union_set_free(band->ast_build_options);
+ free(band->loop_type);
+ free(band->isolate_loop_type);
+ free(band->coincident);
+ free(band);
+
+ return NULL;
+}
+
+/* Are "band1" and "band2" obviously equal?
+ */
+isl_bool isl_schedule_band_plain_is_equal(__isl_keep isl_schedule_band *band1,
+ __isl_keep isl_schedule_band *band2)
+{
+ int i;
+ isl_bool equal;
+
+ if (!band1 || !band2)
+ return isl_bool_error;
+ if (band1 == band2)
+ return isl_bool_true;
+
+ if (band1->n != band2->n)
+ return isl_bool_false;
+ for (i = 0; i < band1->n; ++i)
+ if (band1->coincident[i] != band2->coincident[i])
+ return isl_bool_false;
+ if (band1->permutable != band2->permutable)
+ return isl_bool_false;
+
+ equal = isl_multi_union_pw_aff_plain_is_equal(band1->mupa, band2->mupa);
+ if (equal < 0 || !equal)
+ return equal;
+
+ if (!band1->loop_type != !band2->loop_type)
+ return isl_bool_false;
+ if (band1->loop_type)
+ for (i = 0; i < band1->n; ++i)
+ if (band1->loop_type[i] != band2->loop_type[i])
+ return isl_bool_false;
+
+ if (!band1->isolate_loop_type != !band2->isolate_loop_type)
+ return isl_bool_false;
+ if (band1->isolate_loop_type)
+ for (i = 0; i < band1->n; ++i)
+ if (band1->isolate_loop_type[i] !=
+ band2->isolate_loop_type[i])
+ return isl_bool_false;
+
+ return isl_union_set_is_equal(band1->ast_build_options,
+ band2->ast_build_options);
+}
+
+/* Return the number of scheduling dimensions in the band.
+ */
+isl_size isl_schedule_band_n_member(__isl_keep isl_schedule_band *band)
+{
+ return band ? band->n : isl_size_error;
+}
+
+/* Is the given scheduling dimension coincident within the band and
+ * with respect to the coincidence constraints?
+ */
+isl_bool isl_schedule_band_member_get_coincident(
+ __isl_keep isl_schedule_band *band, int pos)
+{
+ if (!band)
+ return isl_bool_error;
+
+ if (pos < 0 || pos >= band->n)
+ isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid,
+ "invalid member position", return isl_bool_error);
+
+ return isl_bool_ok(band->coincident[pos]);
+}
+
+/* Mark the given scheduling dimension as being coincident or not
+ * according to "coincident".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_member_set_coincident(
+ __isl_take isl_schedule_band *band, int pos, int coincident)
+{
+ if (!band)
+ return NULL;
+ if (isl_schedule_band_member_get_coincident(band, pos) == coincident)
+ return band;
+ band = isl_schedule_band_cow(band);
+ if (!band)
+ return NULL;
+
+ if (pos < 0 || pos >= band->n)
+ isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid,
+ "invalid member position",
+ return isl_schedule_band_free(band));
+
+ band->coincident[pos] = coincident;
+
+ return band;
+}
+
+/* Is the schedule band mark permutable?
+ */
+isl_bool isl_schedule_band_get_permutable(__isl_keep isl_schedule_band *band)
+{
+ if (!band)
+ return isl_bool_error;
+ return isl_bool_ok(band->permutable);
+}
+
+/* Mark the schedule band permutable or not according to "permutable"?
+ */
+__isl_give isl_schedule_band *isl_schedule_band_set_permutable(
+ __isl_take isl_schedule_band *band, int permutable)
+{
+ if (!band)
+ return NULL;
+ if (band->permutable == permutable)
+ return band;
+ band = isl_schedule_band_cow(band);
+ if (!band)
+ return NULL;
+
+ band->permutable = permutable;
+
+ return band;
+}
+
+/* Is the band node "node" anchored? That is, does it reference
+ * the outer band nodes?
+ */
+int isl_schedule_band_is_anchored(__isl_keep isl_schedule_band *band)
+{
+ return band ? band->anchored : -1;
+}
+
+/* Return the schedule space of the band.
+ */
+__isl_give isl_space *isl_schedule_band_get_space(
+ __isl_keep isl_schedule_band *band)
+{
+ if (!band)
+ return NULL;
+ return isl_multi_union_pw_aff_get_space(band->mupa);
+}
+
+/* Intersect the domain of the band schedule of "band" with "domain".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_intersect_domain(
+ __isl_take isl_schedule_band *band, __isl_take isl_union_set *domain)
+{
+ band = isl_schedule_band_cow(band);
+ if (!band || !domain)
+ goto error;
+
+ band->mupa = isl_multi_union_pw_aff_intersect_domain(band->mupa,
+ domain);
+ if (!band->mupa)
+ return isl_schedule_band_free(band);
+
+ return band;
+error:
+ isl_schedule_band_free(band);
+ isl_union_set_free(domain);
+ return NULL;
+}
+
+/* Return the schedule of the band in isolation.
+ */
+__isl_give isl_multi_union_pw_aff *isl_schedule_band_get_partial_schedule(
+ __isl_keep isl_schedule_band *band)
+{
+ return band ? isl_multi_union_pw_aff_copy(band->mupa) : NULL;
+}
+
+/* Replace the schedule of "band" by "schedule".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_set_partial_schedule(
+ __isl_take isl_schedule_band *band,
+ __isl_take isl_multi_union_pw_aff *schedule)
+{
+ band = isl_schedule_band_cow(band);
+ if (!band || !schedule)
+ goto error;
+
+ isl_multi_union_pw_aff_free(band->mupa);
+ band->mupa = schedule;
+
+ return band;
+error:
+ isl_schedule_band_free(band);
+ isl_multi_union_pw_aff_free(schedule);
+ return NULL;
+}
+
+/* Return the loop AST generation type for the band member of "band"
+ * at position "pos".
+ */
+enum isl_ast_loop_type isl_schedule_band_member_get_ast_loop_type(
+ __isl_keep isl_schedule_band *band, int pos)
+{
+ if (!band)
+ return isl_ast_loop_error;
+
+ if (pos < 0 || pos >= band->n)
+ isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid,
+ "invalid member position", return isl_ast_loop_error);
+
+ if (!band->loop_type)
+ return isl_ast_loop_default;
+
+ return band->loop_type[pos];
+}
+
+/* Set the loop AST generation type for the band member of "band"
+ * at position "pos" to "type".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_member_set_ast_loop_type(
+ __isl_take isl_schedule_band *band, int pos,
+ enum isl_ast_loop_type type)
+{
+ if (!band)
+ return NULL;
+ if (isl_schedule_band_member_get_ast_loop_type(band, pos) == type)
+ return band;
+
+ if (pos < 0 || pos >= band->n)
+ isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid,
+ "invalid member position",
+ return isl_schedule_band_free(band));
+
+ band = isl_schedule_band_cow(band);
+ if (!band)
+ return isl_schedule_band_free(band);
+
+ if (!band->loop_type) {
+ isl_ctx *ctx;
+
+ ctx = isl_schedule_band_get_ctx(band);
+ band->loop_type = isl_calloc_array(ctx,
+ enum isl_ast_loop_type, band->n);
+ if (band->n && !band->loop_type)
+ return isl_schedule_band_free(band);
+ }
+
+ band->loop_type[pos] = type;
+
+ return band;
+}
+
+/* Return the loop AST generation type for the band member of "band"
+ * at position "pos" for the part that has been isolated by the isolate option.
+ */
+enum isl_ast_loop_type isl_schedule_band_member_get_isolate_ast_loop_type(
+ __isl_keep isl_schedule_band *band, int pos)
+{
+ if (!band)
+ return isl_ast_loop_error;
+
+ if (pos < 0 || pos >= band->n)
+ isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid,
+ "invalid member position", return isl_ast_loop_error);
+
+ if (!band->isolate_loop_type)
+ return isl_ast_loop_default;
+
+ return band->isolate_loop_type[pos];
+}
+
+/* Set the loop AST generation type for the band member of "band"
+ * at position "pos" to "type" for the part that has been isolated
+ * by the isolate option.
+ */
+__isl_give isl_schedule_band *
+isl_schedule_band_member_set_isolate_ast_loop_type(
+ __isl_take isl_schedule_band *band, int pos,
+ enum isl_ast_loop_type type)
+{
+ if (!band)
+ return NULL;
+ if (isl_schedule_band_member_get_isolate_ast_loop_type(band, pos) ==
+ type)
+ return band;
+
+ if (pos < 0 || pos >= band->n)
+ isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid,
+ "invalid member position",
+ return isl_schedule_band_free(band));
+
+ band = isl_schedule_band_cow(band);
+ if (!band)
+ return isl_schedule_band_free(band);
+
+ if (!band->isolate_loop_type) {
+ isl_ctx *ctx;
+
+ ctx = isl_schedule_band_get_ctx(band);
+ band->isolate_loop_type = isl_calloc_array(ctx,
+ enum isl_ast_loop_type, band->n);
+ if (band->n && !band->isolate_loop_type)
+ return isl_schedule_band_free(band);
+ }
+
+ band->isolate_loop_type[pos] = type;
+
+ return band;
+}
+
+static const char *option_str[] = {
+ [isl_ast_loop_atomic] = "atomic",
+ [isl_ast_loop_unroll] = "unroll",
+ [isl_ast_loop_separate] = "separate"
+};
+
+/* Given a parameter space "space", extend it to a set space
+ *
+ * { type[x] }
+ *
+ * or
+ *
+ * { [isolate[] -> type[x]] }
+ *
+ * depending on whether "isolate" is set.
+ * These can be used to encode loop AST generation options of the given type.
+ */
+static __isl_give isl_space *loop_type_space(__isl_take isl_space *space,
+ enum isl_ast_loop_type type, int isolate)
+{
+ const char *name;
+
+ name = option_str[type];
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, 1);
+ space = isl_space_set_tuple_name(space, isl_dim_set, name);
+ if (!isolate)
+ return space;
+ space = isl_space_from_range(space);
+ space = isl_space_set_tuple_name(space, isl_dim_in, "isolate");
+ space = isl_space_wrap(space);
+
+ return space;
+}
+
+/* Add encodings of the "n" loop AST generation options "type" to "options".
+ * If "isolate" is set, then these options refer to the isolated part.
+ *
+ * In particular, for each sequence of consecutive identical types "t",
+ * different from the default, add an option
+ *
+ * { t[x] : first <= x <= last }
+ *
+ * or
+ *
+ * { [isolate[] -> t[x]] : first <= x <= last }
+ */
+static __isl_give isl_union_set *add_loop_types(
+ __isl_take isl_union_set *options, int n, enum isl_ast_loop_type *type,
+ int isolate)
+{
+ int i;
+
+ if (!type)
+ return options;
+ if (!options)
+ return NULL;
+
+ for (i = 0; i < n; ++i) {
+ int first;
+ isl_space *space;
+ isl_set *option;
+
+ if (type[i] == isl_ast_loop_default)
+ continue;
+
+ first = i;
+ while (i + 1 < n && type[i + 1] == type[i])
+ ++i;
+
+ space = isl_union_set_get_space(options);
+ space = loop_type_space(space, type[i], isolate);
+ option = isl_set_universe(space);
+ option = isl_set_lower_bound_si(option, isl_dim_set, 0, first);
+ option = isl_set_upper_bound_si(option, isl_dim_set, 0, i);
+ options = isl_union_set_add_set(options, option);
+ }
+
+ return options;
+}
+
+/* Return the AST build options associated to "band".
+ */
+__isl_give isl_union_set *isl_schedule_band_get_ast_build_options(
+ __isl_keep isl_schedule_band *band)
+{
+ isl_union_set *options;
+
+ if (!band)
+ return NULL;
+
+ options = isl_union_set_copy(band->ast_build_options);
+ options = add_loop_types(options, band->n, band->loop_type, 0);
+ options = add_loop_types(options, band->n, band->isolate_loop_type, 1);
+
+ return options;
+}
+
+/* Internal data structure for not().
+ */
+struct isl_not_data {
+ isl_bool (*is)(__isl_keep isl_set *set);
+};
+
+/* Does "set" not satisfy data->is()?
+ */
+static isl_bool not(__isl_keep isl_set *set, void *user)
+{
+ struct isl_not_data *data = user;
+
+ return isl_bool_not(data->is(set));
+}
+
+/* Does "uset" contain any set that satisfies "is"?
+ * In other words, is it not the case that all of them do not satisfy "is"?
+ */
+static isl_bool has_any(__isl_keep isl_union_set *uset,
+ isl_bool (*is)(__isl_keep isl_set *set))
+{
+ struct isl_not_data data = { is };
+
+ return isl_bool_not(isl_union_set_every_set(uset, &not, &data));
+}
+
+/* Does "set" live in a space of the form
+ *
+ * isolate[[...] -> [...]]
+ *
+ * ?
+ */
+static isl_bool is_isolate(__isl_keep isl_set *set)
+{
+ if (isl_set_has_tuple_name(set)) {
+ const char *name;
+ name = isl_set_get_tuple_name(set);
+ if (isl_set_is_wrapping(set) && !strcmp(name, "isolate"))
+ return isl_bool_true;
+ }
+
+ return isl_bool_false;
+}
+
+/* Does "options" include an option of the ofrm
+ *
+ * isolate[[...] -> [...]]
+ *
+ * ?
+ */
+static isl_bool has_isolate_option(__isl_keep isl_union_set *options)
+{
+ return has_any(options, &is_isolate);
+}
+
+/* Does "set" encode a loop AST generation option?
+ */
+static isl_bool is_loop_type_option(__isl_keep isl_set *set)
+{
+ isl_size dim;
+
+ dim = isl_set_dim(set, isl_dim_set);
+ if (dim < 0)
+ return isl_bool_error;
+ if (dim == 1 && isl_set_has_tuple_name(set)) {
+ const char *name;
+ enum isl_ast_loop_type type;
+ name = isl_set_get_tuple_name(set);
+ for (type = isl_ast_loop_atomic;
+ type <= isl_ast_loop_separate; ++type) {
+ if (strcmp(name, option_str[type]))
+ continue;
+ return isl_bool_true;
+ }
+ }
+
+ return isl_bool_false;
+}
+
+/* Does "set" encode a loop AST generation option for the isolated part?
+ * That is, is of the form
+ *
+ * { [isolate[] -> t[x]] }
+ *
+ * with t equal to "atomic", "unroll" or "separate"?
+ */
+static isl_bool is_isolate_loop_type_option(__isl_keep isl_set *set)
+{
+ const char *name;
+ enum isl_ast_loop_type type;
+ isl_map *map;
+
+ if (!isl_set_is_wrapping(set))
+ return isl_bool_false;
+ map = isl_set_unwrap(isl_set_copy(set));
+ if (!isl_map_has_tuple_name(map, isl_dim_in) ||
+ !isl_map_has_tuple_name(map, isl_dim_out)) {
+ isl_map_free(map);
+ return isl_bool_false;
+ }
+ name = isl_map_get_tuple_name(map, isl_dim_in);
+ if (!strcmp(name, "isolate")) {
+ name = isl_map_get_tuple_name(map, isl_dim_out);
+ for (type = isl_ast_loop_atomic;
+ type <= isl_ast_loop_separate; ++type) {
+ if (strcmp(name, option_str[type]))
+ continue;
+ isl_map_free(map);
+ return isl_bool_true;
+ }
+ }
+ isl_map_free(map);
+
+ return isl_bool_false;
+}
+
+/* Does "options" encode any loop AST generation options
+ * for the isolated part?
+ */
+static isl_bool has_isolate_loop_type_options(__isl_keep isl_union_set *options)
+{
+ return has_any(options, &is_isolate_loop_type_option);
+}
+
+/* Does "options" encode any loop AST generation options?
+ */
+static isl_bool has_loop_type_options(__isl_keep isl_union_set *options)
+{
+ return has_any(options, &is_loop_type_option);
+}
+
+/* Extract the loop AST generation type for the band member
+ * at position "pos" from "options".
+ * If "isolate" is set, then extract the loop types for the isolated part.
+ */
+static enum isl_ast_loop_type extract_loop_type(
+ __isl_keep isl_union_set *options, int pos, int isolate)
+{
+ isl_ctx *ctx;
+ enum isl_ast_loop_type type, res = isl_ast_loop_default;
+
+ ctx = isl_union_set_get_ctx(options);
+ for (type = isl_ast_loop_atomic;
+ type <= isl_ast_loop_separate; ++type) {
+ isl_space *space;
+ isl_set *option;
+ int empty;
+
+ space = isl_union_set_get_space(options);
+ space = loop_type_space(space, type, isolate);
+ option = isl_union_set_extract_set(options, space);
+ option = isl_set_fix_si(option, isl_dim_set, 0, pos);
+ empty = isl_set_is_empty(option);
+ isl_set_free(option);
+
+ if (empty < 0)
+ return isl_ast_loop_error;
+ if (empty)
+ continue;
+ if (res != isl_ast_loop_default)
+ isl_die(ctx, isl_error_invalid,
+ "conflicting loop type options",
+ return isl_ast_loop_error);
+ res = type;
+ }
+
+ return res;
+}
+
+/* Extract the loop AST generation types for the members of "band"
+ * from "options" and store them in band->loop_type.
+ * Return -1 on error.
+ */
+static int extract_loop_types(__isl_keep isl_schedule_band *band,
+ __isl_keep isl_union_set *options)
+{
+ int i;
+
+ if (!band->loop_type) {
+ isl_ctx *ctx = isl_schedule_band_get_ctx(band);
+ band->loop_type = isl_alloc_array(ctx,
+ enum isl_ast_loop_type, band->n);
+ if (band->n && !band->loop_type)
+ return -1;
+ }
+ for (i = 0; i < band->n; ++i) {
+ band->loop_type[i] = extract_loop_type(options, i, 0);
+ if (band->loop_type[i] == isl_ast_loop_error)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Extract the loop AST generation types for the members of "band"
+ * from "options" for the isolated part and
+ * store them in band->isolate_loop_type.
+ * Return -1 on error.
+ */
+static int extract_isolate_loop_types(__isl_keep isl_schedule_band *band,
+ __isl_keep isl_union_set *options)
+{
+ int i;
+
+ if (!band->isolate_loop_type) {
+ isl_ctx *ctx = isl_schedule_band_get_ctx(band);
+ band->isolate_loop_type = isl_alloc_array(ctx,
+ enum isl_ast_loop_type, band->n);
+ if (band->n && !band->isolate_loop_type)
+ return -1;
+ }
+ for (i = 0; i < band->n; ++i) {
+ band->isolate_loop_type[i] = extract_loop_type(options, i, 1);
+ if (band->isolate_loop_type[i] == isl_ast_loop_error)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Construct universe sets of the spaces that encode loop AST generation
+ * types (for the isolated part if "isolate" is set). That is, construct
+ *
+ * { atomic[x]; separate[x]; unroll[x] }
+ *
+ * or
+ *
+ * { [isolate[] -> atomic[x]]; [isolate[] -> separate[x]];
+ * [isolate[] -> unroll[x]] }
+ */
+static __isl_give isl_union_set *loop_types(__isl_take isl_space *space,
+ int isolate)
+{
+ enum isl_ast_loop_type type;
+ isl_union_set *types;
+
+ types = isl_union_set_empty(space);
+ for (type = isl_ast_loop_atomic;
+ type <= isl_ast_loop_separate; ++type) {
+ isl_set *set;
+
+ space = isl_union_set_get_space(types);
+ space = loop_type_space(space, type, isolate);
+ set = isl_set_universe(space);
+ types = isl_union_set_add_set(types, set);
+ }
+
+ return types;
+}
+
+/* Remove all elements from spaces that encode loop AST generation types
+ * from "options".
+ */
+static __isl_give isl_union_set *clear_loop_types(
+ __isl_take isl_union_set *options)
+{
+ isl_union_set *types;
+
+ types = loop_types(isl_union_set_get_space(options), 0);
+ options = isl_union_set_subtract(options, types);
+
+ return options;
+}
+
+/* Remove all elements from spaces that encode loop AST generation types
+ * for the isolated part from "options".
+ */
+static __isl_give isl_union_set *clear_isolate_loop_types(
+ __isl_take isl_union_set *options)
+{
+ isl_union_set *types;
+
+ types = loop_types(isl_union_set_get_space(options), 1);
+ options = isl_union_set_subtract(options, types);
+
+ return options;
+}
+
+/* Replace the AST build options associated to "band" by "options".
+ * If there are any loop AST generation type options, then they
+ * are extracted and stored in band->loop_type. Otherwise,
+ * band->loop_type is removed to indicate that the default applies
+ * to all members. Similarly for the loop AST generation type options
+ * for the isolated part, which are stored in band->isolate_loop_type.
+ * The remaining options are stored in band->ast_build_options.
+ *
+ * Set anchored if the options include an isolate option since the
+ * domain of the wrapped map references the outer band node schedules.
+ */
+__isl_give isl_schedule_band *isl_schedule_band_set_ast_build_options(
+ __isl_take isl_schedule_band *band, __isl_take isl_union_set *options)
+{
+ isl_bool has_isolate, has_loop_type, has_isolate_loop_type;
+
+ band = isl_schedule_band_cow(band);
+ if (!band || !options)
+ goto error;
+ has_isolate = has_isolate_option(options);
+ if (has_isolate < 0)
+ goto error;
+ has_loop_type = has_loop_type_options(options);
+ if (has_loop_type < 0)
+ goto error;
+ has_isolate_loop_type = has_isolate_loop_type_options(options);
+ if (has_isolate_loop_type < 0)
+ goto error;
+
+ if (!has_loop_type) {
+ free(band->loop_type);
+ band->loop_type = NULL;
+ } else {
+ if (extract_loop_types(band, options) < 0)
+ goto error;
+ options = clear_loop_types(options);
+ if (!options)
+ goto error;
+ }
+
+ if (!has_isolate_loop_type) {
+ free(band->isolate_loop_type);
+ band->isolate_loop_type = NULL;
+ } else {
+ if (extract_isolate_loop_types(band, options) < 0)
+ goto error;
+ options = clear_isolate_loop_types(options);
+ if (!options)
+ goto error;
+ }
+
+ isl_union_set_free(band->ast_build_options);
+ band->ast_build_options = options;
+ band->anchored = has_isolate;
+
+ return band;
+error:
+ isl_schedule_band_free(band);
+ isl_union_set_free(options);
+ return NULL;
+}
+
+/* Return the "isolate" option associated to "band", assuming
+ * it at appears at schedule depth "depth".
+ *
+ * The isolate option is of the form
+ *
+ * isolate[[flattened outer bands] -> band]
+ */
+__isl_give isl_set *isl_schedule_band_get_ast_isolate_option(
+ __isl_keep isl_schedule_band *band, int depth)
+{
+ isl_space *space;
+ isl_set *isolate;
+
+ if (!band)
+ return NULL;
+
+ space = isl_schedule_band_get_space(band);
+ space = isl_space_from_range(space);
+ space = isl_space_add_dims(space, isl_dim_in, depth);
+ space = isl_space_wrap(space);
+ space = isl_space_set_tuple_name(space, isl_dim_set, "isolate");
+
+ isolate = isl_union_set_extract_set(band->ast_build_options, space);
+
+ return isolate;
+}
+
+/* Replace the option "drop" in the AST build options by "add".
+ * That is, remove "drop" and add "add".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_replace_ast_build_option(
+ __isl_take isl_schedule_band *band, __isl_take isl_set *drop,
+ __isl_take isl_set *add)
+{
+ isl_union_set *options;
+
+ band = isl_schedule_band_cow(band);
+ if (!band)
+ goto error;
+
+ options = band->ast_build_options;
+ options = isl_union_set_subtract(options, isl_union_set_from_set(drop));
+ options = isl_union_set_union(options, isl_union_set_from_set(add));
+ band->ast_build_options = options;
+
+ if (!band->ast_build_options)
+ return isl_schedule_band_free(band);
+
+ return band;
+error:
+ isl_schedule_band_free(band);
+ isl_set_free(drop);
+ isl_set_free(add);
+ return NULL;
+}
+
+/* Multiply the partial schedule of "band" with the factors in "mv".
+ * Replace the result by its greatest integer part to ensure
+ * that the schedule is always integral.
+ */
+__isl_give isl_schedule_band *isl_schedule_band_scale(
+ __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv)
+{
+ band = isl_schedule_band_cow(band);
+ if (!band || !mv)
+ goto error;
+ band->mupa = isl_multi_union_pw_aff_scale_multi_val(band->mupa, mv);
+ band->mupa = isl_multi_union_pw_aff_floor(band->mupa);
+ if (!band->mupa)
+ return isl_schedule_band_free(band);
+ return band;
+error:
+ isl_schedule_band_free(band);
+ isl_multi_val_free(mv);
+ return NULL;
+}
+
+/* Divide the partial schedule of "band" by the factors in "mv".
+ * Replace the result by its greatest integer part to ensure
+ * that the schedule is always integral.
+ */
+__isl_give isl_schedule_band *isl_schedule_band_scale_down(
+ __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv)
+{
+ band = isl_schedule_band_cow(band);
+ if (!band || !mv)
+ goto error;
+ band->mupa = isl_multi_union_pw_aff_scale_down_multi_val(band->mupa,
+ mv);
+ band->mupa = isl_multi_union_pw_aff_floor(band->mupa);
+ if (!band->mupa)
+ return isl_schedule_band_free(band);
+ return band;
+error:
+ isl_schedule_band_free(band);
+ isl_multi_val_free(mv);
+ return NULL;
+}
+
+/* Reduce the partial schedule of "band" modulo the factors in "mv".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_mod(
+ __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv)
+{
+ band = isl_schedule_band_cow(band);
+ if (!band || !mv)
+ goto error;
+ band->mupa = isl_multi_union_pw_aff_mod_multi_val(band->mupa, mv);
+ if (!band->mupa)
+ return isl_schedule_band_free(band);
+ return band;
+error:
+ isl_schedule_band_free(band);
+ isl_multi_val_free(mv);
+ return NULL;
+}
+
+/* Shift the partial schedule of "band" by "shift" after checking
+ * that the domain of the partial schedule would not be affected
+ * by this shift.
+ */
+__isl_give isl_schedule_band *isl_schedule_band_shift(
+ __isl_take isl_schedule_band *band,
+ __isl_take isl_multi_union_pw_aff *shift)
+{
+ isl_union_set *dom1, *dom2;
+ isl_bool subset;
+
+ band = isl_schedule_band_cow(band);
+ if (!band || !shift)
+ goto error;
+ dom1 = isl_multi_union_pw_aff_domain(
+ isl_multi_union_pw_aff_copy(band->mupa));
+ dom2 = isl_multi_union_pw_aff_domain(
+ isl_multi_union_pw_aff_copy(shift));
+ subset = isl_union_set_is_subset(dom1, dom2);
+ isl_union_set_free(dom1);
+ isl_union_set_free(dom2);
+ if (subset < 0)
+ goto error;
+ if (!subset)
+ isl_die(isl_schedule_band_get_ctx(band), isl_error_invalid,
+ "domain of shift needs to include domain of "
+ "partial schedule", goto error);
+ band->mupa = isl_multi_union_pw_aff_add(band->mupa, shift);
+ if (!band->mupa)
+ return isl_schedule_band_free(band);
+ return band;
+error:
+ isl_schedule_band_free(band);
+ isl_multi_union_pw_aff_free(shift);
+ return NULL;
+}
+
+/* Given the schedule of a band, construct the corresponding
+ * schedule for the tile loops based on the given tile sizes
+ * and return the result.
+ *
+ * If the scale tile loops options is set, then the tile loops
+ * are scaled by the tile sizes.
+ *
+ * That is replace each schedule dimension "i" by either
+ * "floor(i/s)" or "s * floor(i/s)".
+ */
+static isl_multi_union_pw_aff *isl_multi_union_pw_aff_tile(
+ __isl_take isl_multi_union_pw_aff *sched,
+ __isl_take isl_multi_val *sizes)
+{
+ isl_ctx *ctx;
+ int i;
+ isl_size n;
+ isl_val *v;
+ int scale;
+
+ ctx = isl_multi_val_get_ctx(sizes);
+ scale = isl_options_get_tile_scale_tile_loops(ctx);
+
+ n = isl_multi_union_pw_aff_size(sched);
+ if (n < 0)
+ sched = isl_multi_union_pw_aff_free(sched);
+ for (i = 0; i < n; ++i) {
+ isl_union_pw_aff *upa;
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(sched, i);
+ v = isl_multi_val_get_val(sizes, i);
+
+ upa = isl_union_pw_aff_scale_down_val(upa, isl_val_copy(v));
+ upa = isl_union_pw_aff_floor(upa);
+ if (scale)
+ upa = isl_union_pw_aff_scale_val(upa, isl_val_copy(v));
+ isl_val_free(v);
+
+ sched = isl_multi_union_pw_aff_set_union_pw_aff(sched, i, upa);
+ }
+
+ isl_multi_val_free(sizes);
+ return sched;
+}
+
+/* Replace "band" by a band corresponding to the tile loops of a tiling
+ * with the given tile sizes.
+ */
+__isl_give isl_schedule_band *isl_schedule_band_tile(
+ __isl_take isl_schedule_band *band, __isl_take isl_multi_val *sizes)
+{
+ band = isl_schedule_band_cow(band);
+ if (!band || !sizes)
+ goto error;
+ band->mupa = isl_multi_union_pw_aff_tile(band->mupa, sizes);
+ if (!band->mupa)
+ return isl_schedule_band_free(band);
+ return band;
+error:
+ isl_schedule_band_free(band);
+ isl_multi_val_free(sizes);
+ return NULL;
+}
+
+/* Replace "band" by a band corresponding to the point loops of a tiling
+ * with the given tile sizes.
+ * "tile" is the corresponding tile loop band.
+ *
+ * If the shift point loops option is set, then the point loops
+ * are shifted to start at zero. That is, each schedule dimension "i"
+ * is replaced by "i - s * floor(i/s)".
+ * The expression "floor(i/s)" (or "s * floor(i/s)") is extracted from
+ * the tile band.
+ *
+ * Otherwise, the band is left untouched.
+ */
+__isl_give isl_schedule_band *isl_schedule_band_point(
+ __isl_take isl_schedule_band *band, __isl_keep isl_schedule_band *tile,
+ __isl_take isl_multi_val *sizes)
+{
+ isl_ctx *ctx;
+ isl_multi_union_pw_aff *scaled;
+
+ if (!band || !sizes)
+ goto error;
+
+ ctx = isl_schedule_band_get_ctx(band);
+ if (!isl_options_get_tile_shift_point_loops(ctx)) {
+ isl_multi_val_free(sizes);
+ return band;
+ }
+ band = isl_schedule_band_cow(band);
+ if (!band)
+ goto error;
+
+ scaled = isl_schedule_band_get_partial_schedule(tile);
+ if (!isl_options_get_tile_scale_tile_loops(ctx))
+ scaled = isl_multi_union_pw_aff_scale_multi_val(scaled, sizes);
+ else
+ isl_multi_val_free(sizes);
+ band->mupa = isl_multi_union_pw_aff_sub(band->mupa, scaled);
+ if (!band->mupa)
+ return isl_schedule_band_free(band);
+ return band;
+error:
+ isl_schedule_band_free(band);
+ isl_multi_val_free(sizes);
+ return NULL;
+}
+
+/* Drop the "n" dimensions starting at "pos" from "band".
+ *
+ * We apply the transformation even if "n" is zero to ensure consistent
+ * behavior with respect to changes in the schedule space.
+ *
+ * The caller is responsible for updating the isolate option.
+ */
+__isl_give isl_schedule_band *isl_schedule_band_drop(
+ __isl_take isl_schedule_band *band, int pos, int n)
+{
+ int i;
+
+ if (pos < 0 || n < 0 || pos + n > band->n)
+ isl_die(isl_schedule_band_get_ctx(band), isl_error_internal,
+ "range out of bounds",
+ return isl_schedule_band_free(band));
+
+ band = isl_schedule_band_cow(band);
+ if (!band)
+ return NULL;
+
+ band->mupa = isl_multi_union_pw_aff_drop_dims(band->mupa,
+ isl_dim_set, pos, n);
+ if (!band->mupa)
+ return isl_schedule_band_free(band);
+
+ for (i = pos + n; i < band->n; ++i)
+ band->coincident[i - n] = band->coincident[i];
+ if (band->loop_type)
+ for (i = pos + n; i < band->n; ++i)
+ band->loop_type[i - n] = band->loop_type[i];
+ if (band->isolate_loop_type)
+ for (i = pos + n; i < band->n; ++i)
+ band->isolate_loop_type[i - n] =
+ band->isolate_loop_type[i];
+
+ band->n -= n;
+
+ return band;
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * in "band".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_reset_user(
+ __isl_take isl_schedule_band *band)
+{
+ band = isl_schedule_band_cow(band);
+ if (!band)
+ return NULL;
+
+ band->mupa = isl_multi_union_pw_aff_reset_user(band->mupa);
+ band->ast_build_options =
+ isl_union_set_reset_user(band->ast_build_options);
+ if (!band->mupa || !band->ast_build_options)
+ return isl_schedule_band_free(band);
+
+ return band;
+}
+
+/* Align the parameters of "band" to those of "space".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_align_params(
+ __isl_take isl_schedule_band *band, __isl_take isl_space *space)
+{
+ band = isl_schedule_band_cow(band);
+ if (!band || !space)
+ goto error;
+
+ band->mupa = isl_multi_union_pw_aff_align_params(band->mupa,
+ isl_space_copy(space));
+ band->ast_build_options =
+ isl_union_set_align_params(band->ast_build_options, space);
+ if (!band->mupa || !band->ast_build_options)
+ return isl_schedule_band_free(band);
+
+ return band;
+error:
+ isl_space_free(space);
+ isl_schedule_band_free(band);
+ return NULL;
+}
+
+/* Compute the pullback of "band" by the function represented by "upma".
+ * In other words, plug in "upma" in the iteration domains of "band".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_pullback_union_pw_multi_aff(
+ __isl_take isl_schedule_band *band,
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ band = isl_schedule_band_cow(band);
+ if (!band || !upma)
+ goto error;
+
+ band->mupa =
+ isl_multi_union_pw_aff_pullback_union_pw_multi_aff(band->mupa,
+ upma);
+ if (!band->mupa)
+ return isl_schedule_band_free(band);
+
+ return band;
+error:
+ isl_union_pw_multi_aff_free(upma);
+ isl_schedule_band_free(band);
+ return NULL;
+}
+
+/* Compute the gist of "band" with respect to "context".
+ * In particular, compute the gist of the associated partial schedule.
+ */
+__isl_give isl_schedule_band *isl_schedule_band_gist(
+ __isl_take isl_schedule_band *band, __isl_take isl_union_set *context)
+{
+ if (!band || !context)
+ goto error;
+ if (band->n == 0) {
+ isl_union_set_free(context);
+ return band;
+ }
+ band = isl_schedule_band_cow(band);
+ if (!band)
+ goto error;
+ band->mupa = isl_multi_union_pw_aff_gist(band->mupa, context);
+ if (!band->mupa)
+ return isl_schedule_band_free(band);
+ return band;
+error:
+ isl_union_set_free(context);
+ isl_schedule_band_free(band);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_band.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_band.h
new file mode 100644
index 00000000000..fa4e5ca3f6e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_band.h
@@ -0,0 +1,125 @@
+#ifndef ISL_SCHEDULE_BAND_H
+#define ISL_SCHEDULE_BAND_H
+
+#include <isl/aff.h>
+#include <isl/ast_type.h>
+#include <isl/union_map.h>
+
+/* Information about a band within a schedule.
+ *
+ * n is the number of scheduling dimensions within the band.
+ * coincident is an array of length n, indicating whether a scheduling dimension
+ * satisfies the coincidence constraints in the sense that
+ * the corresponding dependence distances are zero.
+ * permutable is set if the band is permutable.
+ * mupa is the partial schedule corresponding to this band. The dimension
+ * of mupa is equal to n.
+ * loop_type contains the loop AST generation types for the members
+ * in the band. It may be NULL, if all members are
+ * of type isl_ast_loop_default.
+ * isolate_loop_type contains the loop AST generation types for the members
+ * in the band for the isolated part. It may be NULL, if all members are
+ * of type isl_ast_loop_default.
+ * ast_build_options are the remaining AST build options associated
+ * to the band.
+ * anchored is set if the node depends on its position in the schedule tree.
+ * In particular, it is set if the AST build options include
+ * an isolate option.
+ */
+struct isl_schedule_band {
+ int ref;
+
+ int n;
+ int *coincident;
+ int permutable;
+
+ isl_multi_union_pw_aff *mupa;
+
+ int anchored;
+ isl_union_set *ast_build_options;
+ enum isl_ast_loop_type *loop_type;
+ enum isl_ast_loop_type *isolate_loop_type;
+};
+typedef struct isl_schedule_band isl_schedule_band;
+
+__isl_give isl_schedule_band *isl_schedule_band_from_multi_union_pw_aff(
+ __isl_take isl_multi_union_pw_aff *mupa);
+__isl_give isl_schedule_band *isl_schedule_band_copy(
+ __isl_keep isl_schedule_band *band);
+__isl_null isl_schedule_band *isl_schedule_band_free(
+ __isl_take isl_schedule_band *band);
+
+isl_ctx *isl_schedule_band_get_ctx(__isl_keep isl_schedule_band *band);
+
+isl_bool isl_schedule_band_plain_is_equal(__isl_keep isl_schedule_band *band1,
+ __isl_keep isl_schedule_band *band2);
+
+int isl_schedule_band_is_anchored(__isl_keep isl_schedule_band *band);
+
+__isl_give isl_space *isl_schedule_band_get_space(
+ __isl_keep isl_schedule_band *band);
+__isl_give isl_schedule_band *isl_schedule_band_intersect_domain(
+ __isl_take isl_schedule_band *band, __isl_take isl_union_set *domain);
+__isl_give isl_multi_union_pw_aff *isl_schedule_band_get_partial_schedule(
+ __isl_keep isl_schedule_band *band);
+__isl_give isl_schedule_band *isl_schedule_band_set_partial_schedule(
+ __isl_take isl_schedule_band *band,
+ __isl_take isl_multi_union_pw_aff *schedule);
+enum isl_ast_loop_type isl_schedule_band_member_get_ast_loop_type(
+ __isl_keep isl_schedule_band *band, int pos);
+__isl_give isl_schedule_band *isl_schedule_band_member_set_ast_loop_type(
+ __isl_take isl_schedule_band *band, int pos,
+ enum isl_ast_loop_type type);
+enum isl_ast_loop_type isl_schedule_band_member_get_isolate_ast_loop_type(
+ __isl_keep isl_schedule_band *band, int pos);
+__isl_give isl_schedule_band *
+isl_schedule_band_member_set_isolate_ast_loop_type(
+ __isl_take isl_schedule_band *band, int pos,
+ enum isl_ast_loop_type type);
+__isl_give isl_union_set *isl_schedule_band_get_ast_build_options(
+ __isl_keep isl_schedule_band *band);
+__isl_give isl_schedule_band *isl_schedule_band_set_ast_build_options(
+ __isl_take isl_schedule_band *band, __isl_take isl_union_set *options);
+__isl_give isl_set *isl_schedule_band_get_ast_isolate_option(
+ __isl_keep isl_schedule_band *band, int depth);
+__isl_give isl_schedule_band *isl_schedule_band_replace_ast_build_option(
+ __isl_take isl_schedule_band *band, __isl_take isl_set *drop,
+ __isl_take isl_set *add);
+
+isl_size isl_schedule_band_n_member(__isl_keep isl_schedule_band *band);
+isl_bool isl_schedule_band_member_get_coincident(
+ __isl_keep isl_schedule_band *band, int pos);
+__isl_give isl_schedule_band *isl_schedule_band_member_set_coincident(
+ __isl_take isl_schedule_band *band, int pos, int coincident);
+isl_bool isl_schedule_band_get_permutable(__isl_keep isl_schedule_band *band);
+__isl_give isl_schedule_band *isl_schedule_band_set_permutable(
+ __isl_take isl_schedule_band *band, int permutable);
+
+__isl_give isl_schedule_band *isl_schedule_band_scale(
+ __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv);
+__isl_give isl_schedule_band *isl_schedule_band_scale_down(
+ __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv);
+__isl_give isl_schedule_band *isl_schedule_band_mod(
+ __isl_take isl_schedule_band *band, __isl_take isl_multi_val *mv);
+__isl_give isl_schedule_band *isl_schedule_band_tile(
+ __isl_take isl_schedule_band *band, __isl_take isl_multi_val *sizes);
+__isl_give isl_schedule_band *isl_schedule_band_point(
+ __isl_take isl_schedule_band *band, __isl_keep isl_schedule_band *tile,
+ __isl_take isl_multi_val *sizes);
+__isl_give isl_schedule_band *isl_schedule_band_shift(
+ __isl_take isl_schedule_band *band,
+ __isl_take isl_multi_union_pw_aff *shift);
+__isl_give isl_schedule_band *isl_schedule_band_drop(
+ __isl_take isl_schedule_band *band, int pos, int n);
+__isl_give isl_schedule_band *isl_schedule_band_gist(
+ __isl_take isl_schedule_band *band, __isl_take isl_union_set *context);
+
+__isl_give isl_schedule_band *isl_schedule_band_reset_user(
+ __isl_take isl_schedule_band *band);
+__isl_give isl_schedule_band *isl_schedule_band_align_params(
+ __isl_take isl_schedule_band *band, __isl_take isl_space *space);
+__isl_give isl_schedule_band *isl_schedule_band_pullback_union_pw_multi_aff(
+ __isl_take isl_schedule_band *band,
+ __isl_take isl_union_pw_multi_aff *upma);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_constraints.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_constraints.c
new file mode 100644
index 00000000000..38a3c6f9d36
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_constraints.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright 2012 Ecole Normale Superieure
+ * Copyright 2015-2016 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl_schedule_constraints.h>
+#include <isl/schedule.h>
+#include <isl/space.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl/stream.h>
+
+/* The constraints that need to be satisfied by a schedule on "domain".
+ *
+ * "context" specifies extra constraints on the parameters.
+ *
+ * "validity" constraints map domain elements i to domain elements
+ * that should be scheduled after i. (Hard constraint)
+ * "proximity" constraints map domain elements i to domains elements
+ * that should be scheduled as early as possible after i (or before i).
+ * (Soft constraint)
+ *
+ * "condition" and "conditional_validity" constraints map possibly "tagged"
+ * domain elements i -> s to "tagged" domain elements j -> t.
+ * The elements of the "conditional_validity" constraints, but without the
+ * tags (i.e., the elements i -> j) are treated as validity constraints,
+ * except that during the construction of a tilable band,
+ * the elements of the "conditional_validity" constraints may be violated
+ * provided that all adjacent elements of the "condition" constraints
+ * are local within the band.
+ * A dependence is local within a band if domain and range are mapped
+ * to the same schedule point by the band.
+ */
+struct isl_schedule_constraints {
+ isl_union_set *domain;
+ isl_set *context;
+
+ isl_union_map *constraint[isl_edge_last + 1];
+};
+
+__isl_give isl_schedule_constraints *isl_schedule_constraints_copy(
+ __isl_keep isl_schedule_constraints *sc)
+{
+ isl_ctx *ctx;
+ isl_schedule_constraints *sc_copy;
+ enum isl_edge_type i;
+
+ ctx = isl_union_set_get_ctx(sc->domain);
+ sc_copy = isl_calloc_type(ctx, struct isl_schedule_constraints);
+ if (!sc_copy)
+ return NULL;
+
+ sc_copy->domain = isl_union_set_copy(sc->domain);
+ sc_copy->context = isl_set_copy(sc->context);
+ if (!sc_copy->domain || !sc_copy->context)
+ return isl_schedule_constraints_free(sc_copy);
+
+ for (i = isl_edge_first; i <= isl_edge_last; ++i) {
+ sc_copy->constraint[i] = isl_union_map_copy(sc->constraint[i]);
+ if (!sc_copy->constraint[i])
+ return isl_schedule_constraints_free(sc_copy);
+ }
+
+ return sc_copy;
+}
+
+/* Construct an empty (invalid) isl_schedule_constraints object.
+ * The caller is responsible for setting the domain and initializing
+ * all the other fields, e.g., by calling isl_schedule_constraints_init.
+ */
+static __isl_give isl_schedule_constraints *isl_schedule_constraints_alloc(
+ isl_ctx *ctx)
+{
+ return isl_calloc_type(ctx, struct isl_schedule_constraints);
+}
+
+/* Initialize all the fields of "sc", except domain, which is assumed
+ * to have been set by the caller.
+ */
+static __isl_give isl_schedule_constraints *isl_schedule_constraints_init(
+ __isl_take isl_schedule_constraints *sc)
+{
+ isl_space *space;
+ isl_union_map *empty;
+ enum isl_edge_type i;
+
+ if (!sc)
+ return NULL;
+ if (!sc->domain)
+ return isl_schedule_constraints_free(sc);
+ space = isl_union_set_get_space(sc->domain);
+ if (!sc->context)
+ sc->context = isl_set_universe(isl_space_copy(space));
+ empty = isl_union_map_empty(space);
+ for (i = isl_edge_first; i <= isl_edge_last; ++i) {
+ if (sc->constraint[i])
+ continue;
+ sc->constraint[i] = isl_union_map_copy(empty);
+ if (!sc->constraint[i])
+ sc->domain = isl_union_set_free(sc->domain);
+ }
+ isl_union_map_free(empty);
+
+ if (!sc->domain || !sc->context)
+ return isl_schedule_constraints_free(sc);
+
+ return sc;
+}
+
+/* Construct an isl_schedule_constraints object for computing a schedule
+ * on "domain". The initial object does not impose any constraints.
+ */
+__isl_give isl_schedule_constraints *isl_schedule_constraints_on_domain(
+ __isl_take isl_union_set *domain)
+{
+ isl_ctx *ctx;
+ isl_schedule_constraints *sc;
+
+ if (!domain)
+ return NULL;
+
+ ctx = isl_union_set_get_ctx(domain);
+ sc = isl_schedule_constraints_alloc(ctx);
+ if (!sc)
+ goto error;
+
+ sc->domain = domain;
+ return isl_schedule_constraints_init(sc);
+error:
+ isl_union_set_free(domain);
+ return NULL;
+}
+
+/* Replace the domain of "sc" by "domain".
+ */
+static __isl_give isl_schedule_constraints *isl_schedule_constraints_set_domain(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_take isl_union_set *domain)
+{
+ if (!sc || !domain)
+ goto error;
+
+ isl_union_set_free(sc->domain);
+ sc->domain = domain;
+
+ return sc;
+error:
+ isl_schedule_constraints_free(sc);
+ isl_union_set_free(domain);
+ return NULL;
+}
+
+/* Replace the context of "sc" by "context".
+ */
+__isl_give isl_schedule_constraints *isl_schedule_constraints_set_context(
+ __isl_take isl_schedule_constraints *sc, __isl_take isl_set *context)
+{
+ if (!sc || !context)
+ goto error;
+
+ isl_set_free(sc->context);
+ sc->context = context;
+
+ return sc;
+error:
+ isl_schedule_constraints_free(sc);
+ isl_set_free(context);
+ return NULL;
+}
+
+/* Replace the constraints of type "type" in "sc" by "c".
+ *
+ * First detect any equality constraints that may be implicit in "c"
+ * in order to try and improve the accuracy of the input (and therefore
+ * also the output) of the isl_set_coefficients calls
+ * that are eventually performed on (some of) these constraints.
+ */
+static __isl_give isl_schedule_constraints *isl_schedule_constraints_set(
+ __isl_take isl_schedule_constraints *sc, enum isl_edge_type type,
+ __isl_take isl_union_map *c)
+{
+ c = isl_union_map_detect_equalities(c);
+ if (!sc || !c)
+ goto error;
+
+ isl_union_map_free(sc->constraint[type]);
+ sc->constraint[type] = c;
+
+ return sc;
+error:
+ isl_schedule_constraints_free(sc);
+ isl_union_map_free(c);
+ return NULL;
+}
+
+/* Replace the validity constraints of "sc" by "validity".
+ */
+__isl_give isl_schedule_constraints *isl_schedule_constraints_set_validity(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_take isl_union_map *validity)
+{
+ return isl_schedule_constraints_set(sc, isl_edge_validity, validity);
+}
+
+/* Replace the coincidence constraints of "sc" by "coincidence".
+ */
+__isl_give isl_schedule_constraints *isl_schedule_constraints_set_coincidence(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_take isl_union_map *coincidence)
+{
+ return isl_schedule_constraints_set(sc, isl_edge_coincidence,
+ coincidence);
+}
+
+/* Replace the proximity constraints of "sc" by "proximity".
+ */
+__isl_give isl_schedule_constraints *isl_schedule_constraints_set_proximity(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_take isl_union_map *proximity)
+{
+ return isl_schedule_constraints_set(sc, isl_edge_proximity, proximity);
+}
+
+/* Replace the conditional validity constraints of "sc" by "condition"
+ * and "validity".
+ */
+__isl_give isl_schedule_constraints *
+isl_schedule_constraints_set_conditional_validity(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_take isl_union_map *condition,
+ __isl_take isl_union_map *validity)
+{
+ sc = isl_schedule_constraints_set(sc, isl_edge_condition, condition);
+ sc = isl_schedule_constraints_set(sc, isl_edge_conditional_validity,
+ validity);
+ return sc;
+}
+
+__isl_null isl_schedule_constraints *isl_schedule_constraints_free(
+ __isl_take isl_schedule_constraints *sc)
+{
+ enum isl_edge_type i;
+
+ if (!sc)
+ return NULL;
+
+ isl_union_set_free(sc->domain);
+ isl_set_free(sc->context);
+ for (i = isl_edge_first; i <= isl_edge_last; ++i)
+ isl_union_map_free(sc->constraint[i]);
+
+ free(sc);
+
+ return NULL;
+}
+
+isl_ctx *isl_schedule_constraints_get_ctx(
+ __isl_keep isl_schedule_constraints *sc)
+{
+ return sc ? isl_union_set_get_ctx(sc->domain) : NULL;
+}
+
+/* Return the domain of "sc".
+ */
+__isl_give isl_union_set *isl_schedule_constraints_get_domain(
+ __isl_keep isl_schedule_constraints *sc)
+{
+ if (!sc)
+ return NULL;
+
+ return isl_union_set_copy(sc->domain);
+}
+
+/* Return the context of "sc".
+ */
+__isl_give isl_set *isl_schedule_constraints_get_context(
+ __isl_keep isl_schedule_constraints *sc)
+{
+ if (!sc)
+ return NULL;
+
+ return isl_set_copy(sc->context);
+}
+
+/* Return the constraints of type "type" in "sc".
+ */
+__isl_give isl_union_map *isl_schedule_constraints_get(
+ __isl_keep isl_schedule_constraints *sc, enum isl_edge_type type)
+{
+ if (!sc)
+ return NULL;
+
+ return isl_union_map_copy(sc->constraint[type]);
+}
+
+/* Return the validity constraints of "sc".
+ */
+__isl_give isl_union_map *isl_schedule_constraints_get_validity(
+ __isl_keep isl_schedule_constraints *sc)
+{
+ return isl_schedule_constraints_get(sc, isl_edge_validity);
+}
+
+/* Return the coincidence constraints of "sc".
+ */
+__isl_give isl_union_map *isl_schedule_constraints_get_coincidence(
+ __isl_keep isl_schedule_constraints *sc)
+{
+ return isl_schedule_constraints_get(sc, isl_edge_coincidence);
+}
+
+/* Return the proximity constraints of "sc".
+ */
+__isl_give isl_union_map *isl_schedule_constraints_get_proximity(
+ __isl_keep isl_schedule_constraints *sc)
+{
+ return isl_schedule_constraints_get(sc, isl_edge_proximity);
+}
+
+/* Return the conditional validity constraints of "sc".
+ */
+__isl_give isl_union_map *isl_schedule_constraints_get_conditional_validity(
+ __isl_keep isl_schedule_constraints *sc)
+{
+ return isl_schedule_constraints_get(sc, isl_edge_conditional_validity);
+}
+
+/* Return the conditions for the conditional validity constraints of "sc".
+ */
+__isl_give isl_union_map *
+isl_schedule_constraints_get_conditional_validity_condition(
+ __isl_keep isl_schedule_constraints *sc)
+{
+ return isl_schedule_constraints_get(sc, isl_edge_condition);
+}
+
+/* Add "c" to the constraints of type "type" in "sc".
+ */
+__isl_give isl_schedule_constraints *isl_schedule_constraints_add(
+ __isl_take isl_schedule_constraints *sc, enum isl_edge_type type,
+ __isl_take isl_union_map *c)
+{
+ if (!sc || !c)
+ goto error;
+
+ c = isl_union_map_union(sc->constraint[type], c);
+ sc->constraint[type] = c;
+ if (!c)
+ return isl_schedule_constraints_free(sc);
+
+ return sc;
+error:
+ isl_schedule_constraints_free(sc);
+ isl_union_map_free(c);
+ return NULL;
+}
+
+/* Can a schedule constraint of type "type" be tagged?
+ */
+static int may_be_tagged(enum isl_edge_type type)
+{
+ if (type == isl_edge_condition || type == isl_edge_conditional_validity)
+ return 1;
+ return 0;
+}
+
+/* Apply "umap" to the domains of the wrapped relations
+ * inside the domain and range of "c".
+ *
+ * That is, for each map of the form
+ *
+ * [D -> S] -> [E -> T]
+ *
+ * in "c", apply "umap" to D and E.
+ *
+ * D is exposed by currying the relation to
+ *
+ * D -> [S -> [E -> T]]
+ *
+ * E is exposed by doing the same to the inverse of "c".
+ */
+static __isl_give isl_union_map *apply_factor_domain(
+ __isl_take isl_union_map *c, __isl_keep isl_union_map *umap)
+{
+ c = isl_union_map_curry(c);
+ c = isl_union_map_apply_domain(c, isl_union_map_copy(umap));
+ c = isl_union_map_uncurry(c);
+
+ c = isl_union_map_reverse(c);
+ c = isl_union_map_curry(c);
+ c = isl_union_map_apply_domain(c, isl_union_map_copy(umap));
+ c = isl_union_map_uncurry(c);
+ c = isl_union_map_reverse(c);
+
+ return c;
+}
+
+/* Apply "umap" to domain and range of "c".
+ * If "tag" is set, then "c" may contain tags and then "umap"
+ * needs to be applied to the domains of the wrapped relations
+ * inside the domain and range of "c".
+ */
+static __isl_give isl_union_map *apply(__isl_take isl_union_map *c,
+ __isl_keep isl_union_map *umap, int tag)
+{
+ isl_union_map *t;
+
+ if (tag)
+ t = isl_union_map_copy(c);
+ c = isl_union_map_apply_domain(c, isl_union_map_copy(umap));
+ c = isl_union_map_apply_range(c, isl_union_map_copy(umap));
+ if (!tag)
+ return c;
+ t = apply_factor_domain(t, umap);
+ c = isl_union_map_union(c, t);
+ return c;
+}
+
+/* Apply "umap" to the domain of the schedule constraints "sc".
+ *
+ * The two sides of the various schedule constraints are adjusted
+ * accordingly.
+ */
+__isl_give isl_schedule_constraints *isl_schedule_constraints_apply(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_take isl_union_map *umap)
+{
+ enum isl_edge_type i;
+
+ if (!sc || !umap)
+ goto error;
+
+ for (i = isl_edge_first; i <= isl_edge_last; ++i) {
+ int tag = may_be_tagged(i);
+
+ sc->constraint[i] = apply(sc->constraint[i], umap, tag);
+ if (!sc->constraint[i])
+ goto error;
+ }
+ sc->domain = isl_union_set_apply(sc->domain, umap);
+ if (!sc->domain)
+ return isl_schedule_constraints_free(sc);
+
+ return sc;
+error:
+ isl_schedule_constraints_free(sc);
+ isl_union_map_free(umap);
+ return NULL;
+}
+
+/* An enumeration of the various keys that may appear in a YAML mapping
+ * of an isl_schedule_constraints object.
+ * The keys for the edge types are assumed to have the same values
+ * as the edge types in isl_edge_type.
+ */
+enum isl_sc_key {
+ isl_sc_key_error = -1,
+ isl_sc_key_validity = isl_edge_validity,
+ isl_sc_key_coincidence = isl_edge_coincidence,
+ isl_sc_key_condition = isl_edge_condition,
+ isl_sc_key_conditional_validity = isl_edge_conditional_validity,
+ isl_sc_key_proximity = isl_edge_proximity,
+ isl_sc_key_domain,
+ isl_sc_key_context,
+ isl_sc_key_end
+};
+
+/* Textual representations of the YAML keys for an isl_schedule_constraints
+ * object.
+ */
+static char *key_str[] = {
+ [isl_sc_key_validity] = "validity",
+ [isl_sc_key_coincidence] = "coincidence",
+ [isl_sc_key_condition] = "condition",
+ [isl_sc_key_conditional_validity] = "conditional_validity",
+ [isl_sc_key_proximity] = "proximity",
+ [isl_sc_key_domain] = "domain",
+ [isl_sc_key_context] = "context",
+};
+
+#undef BASE
+#define BASE set
+#include "print_yaml_field_templ.c"
+
+#undef BASE
+#define BASE union_set
+#include "print_yaml_field_templ.c"
+
+#undef BASE
+#define BASE union_map
+#include "print_yaml_field_templ.c"
+
+/* Print a key, value pair for the edge of type "type" in "sc" to "p".
+ *
+ * If the edge relation is empty, then it is not printed since
+ * an empty relation is the default value.
+ */
+static __isl_give isl_printer *print_constraint(__isl_take isl_printer *p,
+ __isl_keep isl_schedule_constraints *sc, enum isl_edge_type type)
+{
+ isl_bool empty;
+
+ empty = isl_union_map_plain_is_empty(sc->constraint[type]);
+ if (empty < 0)
+ return isl_printer_free(p);
+ if (empty)
+ return p;
+
+ p = print_yaml_field_union_map(p, key_str[type], sc->constraint[type]);
+
+ return p;
+}
+
+/* Print "sc" to "p"
+ *
+ * In particular, print the isl_schedule_constraints object as a YAML document.
+ * Fields with values that are (obviously) equal to their default values
+ * are not printed.
+ */
+__isl_give isl_printer *isl_printer_print_schedule_constraints(
+ __isl_take isl_printer *p, __isl_keep isl_schedule_constraints *sc)
+{
+ isl_bool universe;
+
+ if (!sc)
+ return isl_printer_free(p);
+
+ p = isl_printer_yaml_start_mapping(p);
+ p = print_yaml_field_union_set(p, key_str[isl_sc_key_domain],
+ sc->domain);
+ universe = isl_set_plain_is_universe(sc->context);
+ if (universe < 0)
+ return isl_printer_free(p);
+ if (!universe)
+ p = print_yaml_field_set(p, key_str[isl_sc_key_context],
+ sc->context);
+ p = print_constraint(p, sc, isl_edge_validity);
+ p = print_constraint(p, sc, isl_edge_proximity);
+ p = print_constraint(p, sc, isl_edge_coincidence);
+ p = print_constraint(p, sc, isl_edge_condition);
+ p = print_constraint(p, sc, isl_edge_conditional_validity);
+ p = isl_printer_yaml_end_mapping(p);
+
+ return p;
+}
+
+#undef BASE
+#define BASE schedule_constraints
+#include <print_templ_yaml.c>
+
+#undef KEY
+#define KEY enum isl_sc_key
+#undef KEY_ERROR
+#define KEY_ERROR isl_sc_key_error
+#undef KEY_END
+#define KEY_END isl_sc_key_end
+#include "extract_key.c"
+
+#undef BASE
+#define BASE set
+#include "read_in_string_templ.c"
+
+#undef BASE
+#define BASE union_set
+#include "read_in_string_templ.c"
+
+#undef BASE
+#define BASE union_map
+#include "read_in_string_templ.c"
+
+/* Read an isl_schedule_constraints object from "s".
+ *
+ * Start off with an empty (invalid) isl_schedule_constraints object and
+ * then fill up the fields based on the input.
+ * The input needs to contain at least a description of the domain.
+ * The other fields are set to defaults by isl_schedule_constraints_init
+ * if they are not specified in the input.
+ */
+__isl_give isl_schedule_constraints *isl_stream_read_schedule_constraints(
+ isl_stream *s)
+{
+ isl_ctx *ctx;
+ isl_schedule_constraints *sc;
+ int more;
+ int domain_set = 0;
+
+ if (isl_stream_yaml_read_start_mapping(s))
+ return NULL;
+
+ ctx = isl_stream_get_ctx(s);
+ sc = isl_schedule_constraints_alloc(ctx);
+ while ((more = isl_stream_yaml_next(s)) > 0) {
+ enum isl_sc_key key;
+ isl_set *context;
+ isl_union_set *domain;
+ isl_union_map *constraints;
+
+ key = get_key(s);
+ if (isl_stream_yaml_next(s) < 0)
+ return isl_schedule_constraints_free(sc);
+ switch (key) {
+ case isl_sc_key_end:
+ case isl_sc_key_error:
+ return isl_schedule_constraints_free(sc);
+ case isl_sc_key_domain:
+ domain_set = 1;
+ domain = read_union_set(s);
+ sc = isl_schedule_constraints_set_domain(sc, domain);
+ if (!sc)
+ return NULL;
+ break;
+ case isl_sc_key_context:
+ context = read_set(s);
+ sc = isl_schedule_constraints_set_context(sc, context);
+ if (!sc)
+ return NULL;
+ break;
+ case isl_sc_key_validity:
+ case isl_sc_key_coincidence:
+ case isl_sc_key_condition:
+ case isl_sc_key_conditional_validity:
+ case isl_sc_key_proximity:
+ constraints = read_union_map(s);
+ sc = isl_schedule_constraints_set(sc, key, constraints);
+ if (!sc)
+ return NULL;
+ break;
+ }
+ }
+ if (more < 0)
+ return isl_schedule_constraints_free(sc);
+
+ if (isl_stream_yaml_read_end_mapping(s) < 0) {
+ isl_stream_error(s, NULL, "unexpected extra elements");
+ return isl_schedule_constraints_free(sc);
+ }
+
+ if (!domain_set) {
+ isl_stream_error(s, NULL, "no domain specified");
+ return isl_schedule_constraints_free(sc);
+ }
+
+ return isl_schedule_constraints_init(sc);
+}
+
+/* Read an isl_schedule_constraints object from the file "input".
+ */
+__isl_give isl_schedule_constraints *isl_schedule_constraints_read_from_file(
+ isl_ctx *ctx, FILE *input)
+{
+ struct isl_stream *s;
+ isl_schedule_constraints *sc;
+
+ s = isl_stream_new_file(ctx, input);
+ if (!s)
+ return NULL;
+ sc = isl_stream_read_schedule_constraints(s);
+ isl_stream_free(s);
+
+ return sc;
+}
+
+/* Read an isl_schedule_constraints object from the string "str".
+ */
+__isl_give isl_schedule_constraints *isl_schedule_constraints_read_from_str(
+ isl_ctx *ctx, const char *str)
+{
+ struct isl_stream *s;
+ isl_schedule_constraints *sc;
+
+ s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ sc = isl_stream_read_schedule_constraints(s);
+ isl_stream_free(s);
+
+ return sc;
+}
+
+/* Align the parameters of the fields of "sc".
+ */
+__isl_give isl_schedule_constraints *
+isl_schedule_constraints_align_params(__isl_take isl_schedule_constraints *sc)
+{
+ isl_space *space;
+ enum isl_edge_type i;
+
+ if (!sc)
+ return NULL;
+
+ space = isl_union_set_get_space(sc->domain);
+ space = isl_space_align_params(space, isl_set_get_space(sc->context));
+ for (i = isl_edge_first; i <= isl_edge_last; ++i)
+ space = isl_space_align_params(space,
+ isl_union_map_get_space(sc->constraint[i]));
+
+ for (i = isl_edge_first; i <= isl_edge_last; ++i) {
+ sc->constraint[i] = isl_union_map_align_params(
+ sc->constraint[i], isl_space_copy(space));
+ if (!sc->constraint[i])
+ space = isl_space_free(space);
+ }
+ sc->context = isl_set_align_params(sc->context, isl_space_copy(space));
+ sc->domain = isl_union_set_align_params(sc->domain, space);
+ if (!sc->context || !sc->domain)
+ return isl_schedule_constraints_free(sc);
+
+ return sc;
+}
+
+/* Add the number of basic maps in "map" to *n.
+ */
+static isl_stat add_n_basic_map(__isl_take isl_map *map, void *user)
+{
+ int *n = user;
+ isl_size n_basic_map;
+
+ n_basic_map = isl_map_n_basic_map(map);
+ *n += n_basic_map;
+ isl_map_free(map);
+
+ return n_basic_map < 0 ? isl_stat_error : isl_stat_ok;
+}
+
+/* Return the total number of isl_basic_maps in the constraints of "sc".
+ * Return -1 on error.
+ */
+int isl_schedule_constraints_n_basic_map(
+ __isl_keep isl_schedule_constraints *sc)
+{
+ enum isl_edge_type i;
+ int n = 0;
+
+ if (!sc)
+ return -1;
+ for (i = isl_edge_first; i <= isl_edge_last; ++i)
+ if (isl_union_map_foreach_map(sc->constraint[i],
+ &add_n_basic_map, &n) < 0)
+ return -1;
+
+ return n;
+}
+
+/* Return the total number of isl_maps in the constraints of "sc".
+ */
+isl_size isl_schedule_constraints_n_map(__isl_keep isl_schedule_constraints *sc)
+{
+ enum isl_edge_type i;
+ int n = 0;
+
+ for (i = isl_edge_first; i <= isl_edge_last; ++i) {
+ isl_size n_i;
+
+ n_i = isl_union_map_n_map(sc->constraint[i]);
+ if (n_i < 0)
+ return isl_size_error;
+ n += n_i;
+ }
+
+ return n;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_constraints.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_constraints.h
new file mode 100644
index 00000000000..8ec3864d5f4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_constraints.h
@@ -0,0 +1,31 @@
+#ifndef ISL_SCHEDULE_CONSTRAINTS_H
+#define ISL_SCHEDULE_CONSTRAINTS_H
+
+#include <isl/schedule.h>
+
+enum isl_edge_type {
+ isl_edge_validity = 0,
+ isl_edge_first = isl_edge_validity,
+ isl_edge_coincidence,
+ isl_edge_condition,
+ isl_edge_conditional_validity,
+ isl_edge_proximity,
+ isl_edge_last = isl_edge_proximity,
+ isl_edge_local
+};
+
+__isl_give isl_schedule_constraints *
+isl_schedule_constraints_align_params(__isl_take isl_schedule_constraints *sc);
+
+__isl_give isl_union_map *isl_schedule_constraints_get(
+ __isl_keep isl_schedule_constraints *sc, enum isl_edge_type type);
+__isl_give isl_schedule_constraints *isl_schedule_constraints_add(
+ __isl_take isl_schedule_constraints *sc, enum isl_edge_type type,
+ __isl_take isl_union_map *c);
+
+int isl_schedule_constraints_n_basic_map(
+ __isl_keep isl_schedule_constraints *sc);
+isl_size isl_schedule_constraints_n_map(
+ __isl_keep isl_schedule_constraints *sc);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_node.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_node.c
new file mode 100644
index 00000000000..5e37276253b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_node.c
@@ -0,0 +1,4906 @@
+/*
+ * Copyright 2013-2014 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ * Copyright 2016 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#include <isl/id.h>
+#include <isl/val.h>
+#include <isl/space.h>
+#include <isl/set.h>
+#include <isl_schedule_band.h>
+#include <isl_schedule_private.h>
+#include <isl_schedule_node_private.h>
+
+/* Create a new schedule node in the given schedule, point at the given
+ * tree with given ancestors and child positions.
+ * "child_pos" may be NULL if there are no ancestors.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_alloc(
+ __isl_take isl_schedule *schedule, __isl_take isl_schedule_tree *tree,
+ __isl_take isl_schedule_tree_list *ancestors, int *child_pos)
+{
+ isl_ctx *ctx;
+ isl_schedule_node *node;
+ int i;
+ isl_size n;
+
+ n = isl_schedule_tree_list_n_schedule_tree(ancestors);
+ if (!schedule || !tree || n < 0)
+ goto error;
+ if (n > 0 && !child_pos)
+ goto error;
+ ctx = isl_schedule_get_ctx(schedule);
+ node = isl_calloc_type(ctx, isl_schedule_node);
+ if (!node)
+ goto error;
+ node->ref = 1;
+ node->schedule = schedule;
+ node->tree = tree;
+ node->ancestors = ancestors;
+ node->child_pos = isl_alloc_array(ctx, int, n);
+ if (n && !node->child_pos)
+ return isl_schedule_node_free(node);
+ for (i = 0; i < n; ++i)
+ node->child_pos[i] = child_pos[i];
+
+ return node;
+error:
+ isl_schedule_free(schedule);
+ isl_schedule_tree_free(tree);
+ isl_schedule_tree_list_free(ancestors);
+ return NULL;
+}
+
+/* Return a pointer to the root of a schedule tree with as single
+ * node a domain node with the given domain.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_from_domain(
+ __isl_take isl_union_set *domain)
+{
+ isl_schedule *schedule;
+ isl_schedule_node *node;
+
+ schedule = isl_schedule_from_domain(domain);
+ node = isl_schedule_get_root(schedule);
+ isl_schedule_free(schedule);
+
+ return node;
+}
+
+/* Return a pointer to the root of a schedule tree with as single
+ * node a extension node with the given extension.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_from_extension(
+ __isl_take isl_union_map *extension)
+{
+ isl_ctx *ctx;
+ isl_schedule *schedule;
+ isl_schedule_tree *tree;
+ isl_schedule_node *node;
+
+ if (!extension)
+ return NULL;
+
+ ctx = isl_union_map_get_ctx(extension);
+ tree = isl_schedule_tree_from_extension(extension);
+ schedule = isl_schedule_from_schedule_tree(ctx, tree);
+ node = isl_schedule_get_root(schedule);
+ isl_schedule_free(schedule);
+
+ return node;
+}
+
+/* Return the isl_ctx to which "node" belongs.
+ */
+isl_ctx *isl_schedule_node_get_ctx(__isl_keep isl_schedule_node *node)
+{
+ return node ? isl_schedule_get_ctx(node->schedule) : NULL;
+}
+
+/* Return a pointer to the leaf of the schedule into which "node" points.
+ */
+__isl_keep isl_schedule_tree *isl_schedule_node_peek_leaf(
+ __isl_keep isl_schedule_node *node)
+{
+ return node ? isl_schedule_peek_leaf(node->schedule) : NULL;
+}
+
+/* Return a copy of the leaf of the schedule into which "node" points.
+ */
+__isl_give isl_schedule_tree *isl_schedule_node_get_leaf(
+ __isl_keep isl_schedule_node *node)
+{
+ return isl_schedule_tree_copy(isl_schedule_node_peek_leaf(node));
+}
+
+/* Return the type of the node or isl_schedule_node_error on error.
+ */
+enum isl_schedule_node_type isl_schedule_node_get_type(
+ __isl_keep isl_schedule_node *node)
+{
+ return node ? isl_schedule_tree_get_type(node->tree)
+ : isl_schedule_node_error;
+}
+
+/* Return the type of the parent of "node" or isl_schedule_node_error on error.
+ */
+enum isl_schedule_node_type isl_schedule_node_get_parent_type(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_size n;
+ int pos;
+ int has_parent;
+ isl_schedule_tree *parent;
+ enum isl_schedule_node_type type;
+
+ if (!node)
+ return isl_schedule_node_error;
+ has_parent = isl_schedule_node_has_parent(node);
+ if (has_parent < 0)
+ return isl_schedule_node_error;
+ if (!has_parent)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "node has no parent", return isl_schedule_node_error);
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0)
+ return isl_schedule_node_error;
+
+ pos = n - 1;
+ parent = isl_schedule_tree_list_get_schedule_tree(node->ancestors, pos);
+ type = isl_schedule_tree_get_type(parent);
+ isl_schedule_tree_free(parent);
+
+ return type;
+}
+
+/* Return a copy of the subtree that this node points to.
+ */
+__isl_give isl_schedule_tree *isl_schedule_node_get_tree(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_copy(node->tree);
+}
+
+/* Return a copy of the schedule into which "node" points.
+ */
+__isl_give isl_schedule *isl_schedule_node_get_schedule(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+ return isl_schedule_copy(node->schedule);
+}
+
+/* Return a fresh copy of "node".
+ */
+__isl_take isl_schedule_node *isl_schedule_node_dup(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_node_alloc(isl_schedule_copy(node->schedule),
+ isl_schedule_tree_copy(node->tree),
+ isl_schedule_tree_list_copy(node->ancestors),
+ node->child_pos);
+}
+
+/* Return an isl_schedule_node that is equal to "node" and that has only
+ * a single reference.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_cow(
+ __isl_take isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ if (node->ref == 1)
+ return node;
+ node->ref--;
+ return isl_schedule_node_dup(node);
+}
+
+/* Return a new reference to "node".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_copy(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ node->ref++;
+ return node;
+}
+
+/* Free "node" and return NULL.
+ */
+__isl_null isl_schedule_node *isl_schedule_node_free(
+ __isl_take isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+ if (--node->ref > 0)
+ return NULL;
+
+ isl_schedule_tree_list_free(node->ancestors);
+ free(node->child_pos);
+ isl_schedule_tree_free(node->tree);
+ isl_schedule_free(node->schedule);
+ free(node);
+
+ return NULL;
+}
+
+/* Do "node1" and "node2" point to the same position in the same
+ * schedule?
+ */
+isl_bool isl_schedule_node_is_equal(__isl_keep isl_schedule_node *node1,
+ __isl_keep isl_schedule_node *node2)
+{
+ int i;
+ isl_size n1, n2;
+
+ if (!node1 || !node2)
+ return isl_bool_error;
+ if (node1 == node2)
+ return isl_bool_true;
+ if (node1->schedule != node2->schedule)
+ return isl_bool_false;
+
+ n1 = isl_schedule_node_get_tree_depth(node1);
+ n2 = isl_schedule_node_get_tree_depth(node2);
+ if (n1 < 0 || n2 < 0)
+ return isl_bool_error;
+ if (n1 != n2)
+ return isl_bool_false;
+ for (i = 0; i < n1; ++i)
+ if (node1->child_pos[i] != node2->child_pos[i])
+ return isl_bool_false;
+
+ return isl_bool_true;
+}
+
+/* Return the number of outer schedule dimensions of "node"
+ * in its schedule tree.
+ *
+ * Return isl_size_error on error.
+ */
+isl_size isl_schedule_node_get_schedule_depth(
+ __isl_keep isl_schedule_node *node)
+{
+ int i;
+ isl_size n;
+ int depth = 0;
+
+ if (!node)
+ return isl_size_error;
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0)
+ return isl_size_error;
+ for (i = n - 1; i >= 0; --i) {
+ isl_schedule_tree *tree;
+ isl_size n;
+
+ tree = isl_schedule_tree_list_get_schedule_tree(
+ node->ancestors, i);
+ if (!tree)
+ return isl_size_error;
+ n = 0;
+ if (tree->type == isl_schedule_node_band)
+ n = isl_schedule_tree_band_n_member(tree);
+ depth += n;
+ isl_schedule_tree_free(tree);
+ if (n < 0)
+ return isl_size_error;
+ }
+
+ return depth;
+}
+
+/* Internal data structure for
+ * isl_schedule_node_get_prefix_schedule_union_pw_multi_aff
+ *
+ * "initialized" is set if the filter field has been initialized.
+ * If "universe_domain" is not set, then the collected filter is intersected
+ * with the domain of the root domain node.
+ * "universe_filter" is set if we are only collecting the universes of filters
+ * "collect_prefix" is set if we are collecting prefixes.
+ * "filter" collects all outer filters and is NULL until "initialized" is set.
+ * "prefix" collects all outer band partial schedules (if "collect_prefix"
+ * is set). If it is used, then it is initialized by the caller
+ * of collect_filter_prefix to a zero-dimensional function.
+ */
+struct isl_schedule_node_get_filter_prefix_data {
+ int initialized;
+ int universe_domain;
+ int universe_filter;
+ int collect_prefix;
+ isl_union_set *filter;
+ isl_multi_union_pw_aff *prefix;
+};
+
+static isl_stat collect_filter_prefix(__isl_keep isl_schedule_tree_list *list,
+ int n, struct isl_schedule_node_get_filter_prefix_data *data);
+
+/* Update the filter and prefix information in "data" based on the first "n"
+ * elements in "list" and the expansion tree root "tree".
+ *
+ * We first collect the information from the elements in "list",
+ * initializing the filter based on the domain of the expansion.
+ * Then we map the results to the expanded space and combined them
+ * with the results already in "data".
+ */
+static isl_stat collect_filter_prefix_expansion(
+ __isl_take isl_schedule_tree *tree,
+ __isl_keep isl_schedule_tree_list *list, int n,
+ struct isl_schedule_node_get_filter_prefix_data *data)
+{
+ struct isl_schedule_node_get_filter_prefix_data contracted;
+ isl_union_pw_multi_aff *c;
+ isl_union_map *exp, *universe;
+ isl_union_set *filter;
+
+ c = isl_schedule_tree_expansion_get_contraction(tree);
+ exp = isl_schedule_tree_expansion_get_expansion(tree);
+
+ contracted.initialized = 1;
+ contracted.universe_domain = data->universe_domain;
+ contracted.universe_filter = data->universe_filter;
+ contracted.collect_prefix = data->collect_prefix;
+ universe = isl_union_map_universe(isl_union_map_copy(exp));
+ filter = isl_union_map_domain(universe);
+ if (data->collect_prefix) {
+ isl_space *space = isl_union_set_get_space(filter);
+ space = isl_space_set_from_params(space);
+ contracted.prefix = isl_multi_union_pw_aff_zero(space);
+ }
+ contracted.filter = filter;
+
+ if (collect_filter_prefix(list, n, &contracted) < 0)
+ contracted.filter = isl_union_set_free(contracted.filter);
+ if (data->collect_prefix) {
+ isl_multi_union_pw_aff *prefix;
+
+ prefix = contracted.prefix;
+ prefix =
+ isl_multi_union_pw_aff_pullback_union_pw_multi_aff(prefix,
+ isl_union_pw_multi_aff_copy(c));
+ data->prefix = isl_multi_union_pw_aff_flat_range_product(
+ prefix, data->prefix);
+ }
+ filter = contracted.filter;
+ if (data->universe_domain)
+ filter = isl_union_set_preimage_union_pw_multi_aff(filter,
+ isl_union_pw_multi_aff_copy(c));
+ else
+ filter = isl_union_set_apply(filter, isl_union_map_copy(exp));
+ if (!data->initialized)
+ data->filter = filter;
+ else
+ data->filter = isl_union_set_intersect(filter, data->filter);
+ data->initialized = 1;
+
+ isl_union_pw_multi_aff_free(c);
+ isl_union_map_free(exp);
+ isl_schedule_tree_free(tree);
+
+ return isl_stat_ok;
+}
+
+/* Update the filter information in "data" based on the first "n"
+ * elements in "list" and the extension tree root "tree", in case
+ * data->universe_domain is set and data->collect_prefix is not.
+ *
+ * We collect the universe domain of the elements in "list" and
+ * add it to the universe range of the extension (intersected
+ * with the already collected filter, if any).
+ */
+static isl_stat collect_universe_domain_extension(
+ __isl_take isl_schedule_tree *tree,
+ __isl_keep isl_schedule_tree_list *list, int n,
+ struct isl_schedule_node_get_filter_prefix_data *data)
+{
+ struct isl_schedule_node_get_filter_prefix_data data_outer;
+ isl_union_map *extension;
+ isl_union_set *filter;
+
+ data_outer.initialized = 0;
+ data_outer.universe_domain = 1;
+ data_outer.universe_filter = data->universe_filter;
+ data_outer.collect_prefix = 0;
+ data_outer.filter = NULL;
+ data_outer.prefix = NULL;
+
+ if (collect_filter_prefix(list, n, &data_outer) < 0)
+ data_outer.filter = isl_union_set_free(data_outer.filter);
+
+ extension = isl_schedule_tree_extension_get_extension(tree);
+ extension = isl_union_map_universe(extension);
+ filter = isl_union_map_range(extension);
+ if (data_outer.initialized)
+ filter = isl_union_set_union(filter, data_outer.filter);
+ if (data->initialized)
+ filter = isl_union_set_intersect(filter, data->filter);
+
+ data->filter = filter;
+
+ isl_schedule_tree_free(tree);
+
+ return isl_stat_ok;
+}
+
+/* Update "data" based on the tree node "tree" in case "data" has
+ * not been initialized yet.
+ *
+ * Return 0 on success and -1 on error.
+ *
+ * If "tree" is a filter, then we set data->filter to this filter
+ * (or its universe).
+ * If "tree" is a domain, then this means we have reached the root
+ * of the schedule tree without being able to extract any information.
+ * We therefore initialize data->filter to the universe of the domain,
+ * or the domain itself if data->universe_domain is not set.
+ * If "tree" is a band with at least one member, then we set data->filter
+ * to the universe of the schedule domain and replace the zero-dimensional
+ * data->prefix by the band schedule (if data->collect_prefix is set).
+ */
+static isl_stat collect_filter_prefix_init(__isl_keep isl_schedule_tree *tree,
+ struct isl_schedule_node_get_filter_prefix_data *data)
+{
+ enum isl_schedule_node_type type;
+ isl_multi_union_pw_aff *mupa;
+ isl_union_set *filter;
+ isl_size n;
+
+ type = isl_schedule_tree_get_type(tree);
+ switch (type) {
+ case isl_schedule_node_error:
+ return isl_stat_error;
+ case isl_schedule_node_expansion:
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "should be handled by caller", return isl_stat_error);
+ case isl_schedule_node_extension:
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "cannot handle extension nodes", return isl_stat_error);
+ case isl_schedule_node_context:
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ return isl_stat_ok;
+ case isl_schedule_node_domain:
+ filter = isl_schedule_tree_domain_get_domain(tree);
+ if (data->universe_domain)
+ filter = isl_union_set_universe(filter);
+ data->filter = filter;
+ break;
+ case isl_schedule_node_band:
+ n = isl_schedule_tree_band_n_member(tree);
+ if (n < 0)
+ return isl_stat_error;
+ if (n == 0)
+ return isl_stat_ok;
+ mupa = isl_schedule_tree_band_get_partial_schedule(tree);
+ if (data->collect_prefix) {
+ isl_multi_union_pw_aff_free(data->prefix);
+ mupa = isl_multi_union_pw_aff_reset_tuple_id(mupa,
+ isl_dim_set);
+ data->prefix = isl_multi_union_pw_aff_copy(mupa);
+ }
+ filter = isl_multi_union_pw_aff_domain(mupa);
+ filter = isl_union_set_universe(filter);
+ data->filter = filter;
+ break;
+ case isl_schedule_node_filter:
+ filter = isl_schedule_tree_filter_get_filter(tree);
+ if (data->universe_filter)
+ filter = isl_union_set_universe(filter);
+ data->filter = filter;
+ break;
+ }
+
+ if ((data->collect_prefix && !data->prefix) || !data->filter)
+ return isl_stat_error;
+
+ data->initialized = 1;
+
+ return isl_stat_ok;
+}
+
+/* Update "data" based on the tree node "tree" in case "data" has
+ * already been initialized.
+ *
+ * Return 0 on success and -1 on error.
+ *
+ * If "tree" is a domain and data->universe_domain is not set, then
+ * intersect data->filter with the domain.
+ * If "tree" is a filter, then we intersect data->filter with this filter
+ * (or its universe).
+ * If "tree" is a band with at least one member and data->collect_prefix
+ * is set, then we extend data->prefix with the band schedule.
+ * If "tree" is an extension, then we make sure that we are not collecting
+ * information on any extended domain elements.
+ */
+static isl_stat collect_filter_prefix_update(__isl_keep isl_schedule_tree *tree,
+ struct isl_schedule_node_get_filter_prefix_data *data)
+{
+ enum isl_schedule_node_type type;
+ isl_multi_union_pw_aff *mupa;
+ isl_union_set *filter;
+ isl_union_map *extension;
+ isl_bool empty;
+ isl_size n;
+
+ type = isl_schedule_tree_get_type(tree);
+ switch (type) {
+ case isl_schedule_node_error:
+ return isl_stat_error;
+ case isl_schedule_node_expansion:
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "should be handled by caller", return isl_stat_error);
+ case isl_schedule_node_extension:
+ extension = isl_schedule_tree_extension_get_extension(tree);
+ extension = isl_union_map_intersect_range(extension,
+ isl_union_set_copy(data->filter));
+ empty = isl_union_map_is_empty(extension);
+ isl_union_map_free(extension);
+ if (empty < 0)
+ return isl_stat_error;
+ if (empty)
+ break;
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "cannot handle extension nodes", return isl_stat_error);
+ case isl_schedule_node_context:
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ break;
+ case isl_schedule_node_domain:
+ if (data->universe_domain)
+ break;
+ filter = isl_schedule_tree_domain_get_domain(tree);
+ data->filter = isl_union_set_intersect(data->filter, filter);
+ break;
+ case isl_schedule_node_band:
+ n = isl_schedule_tree_band_n_member(tree);
+ if (n < 0)
+ return isl_stat_error;
+ if (n == 0)
+ break;
+ if (!data->collect_prefix)
+ break;
+ mupa = isl_schedule_tree_band_get_partial_schedule(tree);
+ data->prefix = isl_multi_union_pw_aff_flat_range_product(mupa,
+ data->prefix);
+ if (!data->prefix)
+ return isl_stat_error;
+ break;
+ case isl_schedule_node_filter:
+ filter = isl_schedule_tree_filter_get_filter(tree);
+ if (data->universe_filter)
+ filter = isl_union_set_universe(filter);
+ data->filter = isl_union_set_intersect(data->filter, filter);
+ if (!data->filter)
+ return isl_stat_error;
+ break;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Collect filter and/or prefix information from the first "n"
+ * elements in "list" (which represent the ancestors of a node).
+ * Store the results in "data".
+ *
+ * Extension nodes are only supported if they do not affect the outcome,
+ * i.e., if we are collecting information on non-extended domain elements,
+ * or if we are collecting the universe domain (without prefix).
+ *
+ * Return 0 on success and -1 on error.
+ *
+ * We traverse the list from innermost ancestor (last element)
+ * to outermost ancestor (first element), calling collect_filter_prefix_init
+ * on each node as long as we have not been able to extract any information
+ * yet and collect_filter_prefix_update afterwards.
+ * If we come across an expansion node, then we interrupt the traversal
+ * and call collect_filter_prefix_expansion to restart the traversal
+ * over the remaining ancestors and to combine the results with those
+ * that have already been collected.
+ * If we come across an extension node and we are only computing
+ * the universe domain, then we interrupt the traversal and call
+ * collect_universe_domain_extension to restart the traversal
+ * over the remaining ancestors and to combine the results with those
+ * that have already been collected.
+ * On successful return, data->initialized will be set since the outermost
+ * ancestor is a domain node, which always results in an initialization.
+ */
+static isl_stat collect_filter_prefix(__isl_keep isl_schedule_tree_list *list,
+ int n, struct isl_schedule_node_get_filter_prefix_data *data)
+{
+ int i;
+
+ if (!list)
+ return isl_stat_error;
+
+ for (i = n - 1; i >= 0; --i) {
+ isl_schedule_tree *tree;
+ enum isl_schedule_node_type type;
+ isl_stat r;
+
+ tree = isl_schedule_tree_list_get_schedule_tree(list, i);
+ if (!tree)
+ return isl_stat_error;
+ type = isl_schedule_tree_get_type(tree);
+ if (type == isl_schedule_node_expansion)
+ return collect_filter_prefix_expansion(tree, list, i,
+ data);
+ if (type == isl_schedule_node_extension &&
+ data->universe_domain && !data->collect_prefix)
+ return collect_universe_domain_extension(tree, list, i,
+ data);
+ if (!data->initialized)
+ r = collect_filter_prefix_init(tree, data);
+ else
+ r = collect_filter_prefix_update(tree, data);
+ isl_schedule_tree_free(tree);
+ if (r < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Return the concatenation of the partial schedules of all outer band
+ * nodes of "node" interesected with all outer filters
+ * as an isl_multi_union_pw_aff.
+ * None of the ancestors of "node" may be an extension node, unless
+ * there is also a filter ancestor that filters out all the extended
+ * domain elements.
+ *
+ * If "node" is pointing at the root of the schedule tree, then
+ * there are no domain elements reaching the current node, so
+ * we return an empty result.
+ *
+ * We collect all the filters and partial schedules in collect_filter_prefix
+ * and intersect the domain of the combined schedule with the combined filter.
+ */
+__isl_give isl_multi_union_pw_aff *
+isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_size n;
+ isl_space *space;
+ struct isl_schedule_node_get_filter_prefix_data data;
+
+ if (!node)
+ return NULL;
+
+ space = isl_schedule_get_space(node->schedule);
+ space = isl_space_set_from_params(space);
+ if (node->tree == node->schedule->root)
+ return isl_multi_union_pw_aff_zero(space);
+
+ data.initialized = 0;
+ data.universe_domain = 1;
+ data.universe_filter = 0;
+ data.collect_prefix = 1;
+ data.filter = NULL;
+ data.prefix = isl_multi_union_pw_aff_zero(space);
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0 || collect_filter_prefix(node->ancestors, n, &data) < 0)
+ data.prefix = isl_multi_union_pw_aff_free(data.prefix);
+
+ data.prefix = isl_multi_union_pw_aff_intersect_domain(data.prefix,
+ data.filter);
+
+ return data.prefix;
+}
+
+/* Return the concatenation of the partial schedules of all outer band
+ * nodes of "node" interesected with all outer filters
+ * as an isl_union_pw_multi_aff.
+ * None of the ancestors of "node" may be an extension node, unless
+ * there is also a filter ancestor that filters out all the extended
+ * domain elements.
+ *
+ * If "node" is pointing at the root of the schedule tree, then
+ * there are no domain elements reaching the current node, so
+ * we return an empty result.
+ *
+ * We collect all the filters and partial schedules in collect_filter_prefix.
+ * The partial schedules are collected as an isl_multi_union_pw_aff.
+ * If this isl_multi_union_pw_aff is zero-dimensional, then it does not
+ * contain any domain information, so we construct the isl_union_pw_multi_aff
+ * result as a zero-dimensional function on the collected filter.
+ * Otherwise, we convert the isl_multi_union_pw_aff to
+ * an isl_multi_union_pw_aff and intersect the domain with the filter.
+ */
+__isl_give isl_union_pw_multi_aff *
+isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_size n, dim;
+ isl_space *space;
+ isl_union_pw_multi_aff *prefix;
+ struct isl_schedule_node_get_filter_prefix_data data;
+
+ if (!node)
+ return NULL;
+
+ space = isl_schedule_get_space(node->schedule);
+ if (node->tree == node->schedule->root)
+ return isl_union_pw_multi_aff_empty(space);
+
+ space = isl_space_set_from_params(space);
+ data.initialized = 0;
+ data.universe_domain = 1;
+ data.universe_filter = 0;
+ data.collect_prefix = 1;
+ data.filter = NULL;
+ data.prefix = isl_multi_union_pw_aff_zero(space);
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0 || collect_filter_prefix(node->ancestors, n, &data) < 0)
+ data.prefix = isl_multi_union_pw_aff_free(data.prefix);
+
+ dim = isl_multi_union_pw_aff_dim(data.prefix, isl_dim_set);
+ if (dim < 0)
+ data.prefix = isl_multi_union_pw_aff_free(data.prefix);
+ if (data.prefix && dim == 0) {
+ isl_multi_union_pw_aff_free(data.prefix);
+ prefix = isl_union_pw_multi_aff_from_domain(data.filter);
+ } else {
+ prefix =
+ isl_union_pw_multi_aff_from_multi_union_pw_aff(data.prefix);
+ prefix = isl_union_pw_multi_aff_intersect_domain(prefix,
+ data.filter);
+ }
+
+ return prefix;
+}
+
+/* Return the concatenation of the partial schedules of all outer band
+ * nodes of "node" interesected with all outer filters
+ * as an isl_union_map.
+ */
+__isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_union_map(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_union_pw_multi_aff *upma;
+
+ upma = isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(node);
+ return isl_union_map_from_union_pw_multi_aff(upma);
+}
+
+/* Return the concatenation of the partial schedules of all outer band
+ * nodes of "node" intersected with all outer domain constraints.
+ * None of the ancestors of "node" may be an extension node, unless
+ * there is also a filter ancestor that filters out all the extended
+ * domain elements.
+ *
+ * Essentially, this function intersects the domain of the output
+ * of isl_schedule_node_get_prefix_schedule_union_map with the output
+ * of isl_schedule_node_get_domain, except that it only traverses
+ * the ancestors of "node" once.
+ */
+__isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_relation(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_size n, dim;
+ isl_space *space;
+ isl_union_map *prefix;
+ struct isl_schedule_node_get_filter_prefix_data data;
+
+ if (!node)
+ return NULL;
+
+ space = isl_schedule_get_space(node->schedule);
+ if (node->tree == node->schedule->root)
+ return isl_union_map_empty(space);
+
+ space = isl_space_set_from_params(space);
+ data.initialized = 0;
+ data.universe_domain = 0;
+ data.universe_filter = 0;
+ data.collect_prefix = 1;
+ data.filter = NULL;
+ data.prefix = isl_multi_union_pw_aff_zero(space);
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0 || collect_filter_prefix(node->ancestors, n, &data) < 0)
+ data.prefix = isl_multi_union_pw_aff_free(data.prefix);
+
+ dim = isl_multi_union_pw_aff_dim(data.prefix, isl_dim_set);
+ if (dim < 0)
+ data.prefix = isl_multi_union_pw_aff_free(data.prefix);
+ if (data.prefix && dim == 0) {
+ isl_multi_union_pw_aff_free(data.prefix);
+ prefix = isl_union_map_from_domain(data.filter);
+ } else {
+ prefix = isl_union_map_from_multi_union_pw_aff(data.prefix);
+ prefix = isl_union_map_intersect_domain(prefix, data.filter);
+ }
+
+ return prefix;
+}
+
+/* Return the domain elements that reach "node".
+ *
+ * If "node" is pointing at the root of the schedule tree, then
+ * there are no domain elements reaching the current node, so
+ * we return an empty result.
+ * None of the ancestors of "node" may be an extension node, unless
+ * there is also a filter ancestor that filters out all the extended
+ * domain elements.
+ *
+ * Otherwise, we collect all filters reaching the node,
+ * intersected with the root domain in collect_filter_prefix.
+ */
+__isl_give isl_union_set *isl_schedule_node_get_domain(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_size n;
+ struct isl_schedule_node_get_filter_prefix_data data;
+
+ if (!node)
+ return NULL;
+
+ if (node->tree == node->schedule->root) {
+ isl_space *space;
+
+ space = isl_schedule_get_space(node->schedule);
+ return isl_union_set_empty(space);
+ }
+
+ data.initialized = 0;
+ data.universe_domain = 0;
+ data.universe_filter = 0;
+ data.collect_prefix = 0;
+ data.filter = NULL;
+ data.prefix = NULL;
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0 || collect_filter_prefix(node->ancestors, n, &data) < 0)
+ data.filter = isl_union_set_free(data.filter);
+
+ return data.filter;
+}
+
+/* Return the union of universe sets of the domain elements that reach "node".
+ *
+ * If "node" is pointing at the root of the schedule tree, then
+ * there are no domain elements reaching the current node, so
+ * we return an empty result.
+ *
+ * Otherwise, we collect the universes of all filters reaching the node
+ * in collect_filter_prefix.
+ */
+__isl_give isl_union_set *isl_schedule_node_get_universe_domain(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_size n;
+ struct isl_schedule_node_get_filter_prefix_data data;
+
+ if (!node)
+ return NULL;
+
+ if (node->tree == node->schedule->root) {
+ isl_space *space;
+
+ space = isl_schedule_get_space(node->schedule);
+ return isl_union_set_empty(space);
+ }
+
+ data.initialized = 0;
+ data.universe_domain = 1;
+ data.universe_filter = 1;
+ data.collect_prefix = 0;
+ data.filter = NULL;
+ data.prefix = NULL;
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0 || collect_filter_prefix(node->ancestors, n, &data) < 0)
+ data.filter = isl_union_set_free(data.filter);
+
+ return data.filter;
+}
+
+/* Return the subtree schedule of "node".
+ *
+ * Since isl_schedule_tree_get_subtree_schedule_union_map does not handle
+ * trees that do not contain any schedule information, we first
+ * move down to the first relevant descendant and handle leaves ourselves.
+ *
+ * If the subtree rooted at "node" contains any expansion nodes, then
+ * the returned subtree schedule is formulated in terms of the expanded
+ * domains.
+ * The subtree is not allowed to contain any extension nodes.
+ */
+__isl_give isl_union_map *isl_schedule_node_get_subtree_schedule_union_map(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_schedule_tree *tree, *leaf;
+ isl_union_map *umap;
+
+ tree = isl_schedule_node_get_tree(node);
+ leaf = isl_schedule_node_peek_leaf(node);
+ tree = isl_schedule_tree_first_schedule_descendant(tree, leaf);
+ if (!tree)
+ return NULL;
+ if (tree == leaf) {
+ isl_union_set *domain;
+ domain = isl_schedule_node_get_universe_domain(node);
+ isl_schedule_tree_free(tree);
+ return isl_union_map_from_domain(domain);
+ }
+
+ umap = isl_schedule_tree_get_subtree_schedule_union_map(tree);
+ isl_schedule_tree_free(tree);
+ return umap;
+}
+
+/* Return the number of ancestors of "node" in its schedule tree.
+ */
+isl_size isl_schedule_node_get_tree_depth(__isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return isl_size_error;
+ return isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+}
+
+/* Does "node" have a parent?
+ *
+ * That is, does it point to any node of the schedule other than the root?
+ */
+isl_bool isl_schedule_node_has_parent(__isl_keep isl_schedule_node *node)
+{
+ isl_size depth;
+
+ depth = isl_schedule_node_get_tree_depth(node);
+ if (depth < 0)
+ return isl_bool_error;
+ return isl_bool_ok(depth != 0);
+}
+
+/* Return the position of "node" among the children of its parent.
+ */
+isl_size isl_schedule_node_get_child_position(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_size n;
+ isl_bool has_parent;
+
+ if (!node)
+ return isl_size_error;
+ has_parent = isl_schedule_node_has_parent(node);
+ if (has_parent < 0)
+ return isl_size_error;
+ if (!has_parent)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "node has no parent", return isl_size_error);
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ return n < 0 ? isl_size_error : node->child_pos[n - 1];
+}
+
+/* Does the parent (if any) of "node" have any children with a smaller child
+ * position than this one?
+ */
+isl_bool isl_schedule_node_has_previous_sibling(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_size n;
+ isl_bool has_parent;
+
+ if (!node)
+ return isl_bool_error;
+ has_parent = isl_schedule_node_has_parent(node);
+ if (has_parent < 0 || !has_parent)
+ return has_parent;
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0)
+ return isl_bool_error;
+
+ return isl_bool_ok(node->child_pos[n - 1] > 0);
+}
+
+/* Does the parent (if any) of "node" have any children with a greater child
+ * position than this one?
+ */
+isl_bool isl_schedule_node_has_next_sibling(__isl_keep isl_schedule_node *node)
+{
+ isl_size n, n_child;
+ isl_bool has_parent;
+ isl_schedule_tree *tree;
+
+ if (!node)
+ return isl_bool_error;
+ has_parent = isl_schedule_node_has_parent(node);
+ if (has_parent < 0 || !has_parent)
+ return has_parent;
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0)
+ return isl_bool_error;
+ tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors, n - 1);
+ n_child = isl_schedule_tree_n_children(tree);
+ isl_schedule_tree_free(tree);
+ if (n_child < 0)
+ return isl_bool_error;
+
+ return isl_bool_ok(node->child_pos[n - 1] + 1 < n_child);
+}
+
+/* Does "node" have any children?
+ *
+ * Any node other than the leaf nodes is considered to have at least
+ * one child, even if the corresponding isl_schedule_tree does not
+ * have any children.
+ */
+isl_bool isl_schedule_node_has_children(__isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return isl_bool_error;
+ return isl_bool_ok(!isl_schedule_tree_is_leaf(node->tree));
+}
+
+/* Return the number of children of "node"?
+ *
+ * Any node other than the leaf nodes is considered to have at least
+ * one child, even if the corresponding isl_schedule_tree does not
+ * have any children. That is, the number of children of "node" is
+ * only zero if its tree is the explicit empty tree. Otherwise,
+ * if the isl_schedule_tree has any children, then it is equal
+ * to the number of children of "node". If it has zero children,
+ * then "node" still has a leaf node as child.
+ */
+isl_size isl_schedule_node_n_children(__isl_keep isl_schedule_node *node)
+{
+ isl_size n;
+
+ if (!node)
+ return isl_size_error;
+
+ if (isl_schedule_tree_is_leaf(node->tree))
+ return 0;
+
+ n = isl_schedule_tree_n_children(node->tree);
+ if (n < 0)
+ return isl_size_error;
+ if (n == 0)
+ return 1;
+
+ return n;
+}
+
+/* Move the "node" pointer to the ancestor of the given generation
+ * of the node it currently points to, where generation 0 is the node
+ * itself and generation 1 is its parent.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_ancestor(
+ __isl_take isl_schedule_node *node, int generation)
+{
+ isl_size n;
+ isl_schedule_tree *tree;
+
+ if (!node)
+ return NULL;
+ if (generation == 0)
+ return node;
+ n = isl_schedule_node_get_tree_depth(node);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ if (generation < 0 || generation > n)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "generation out of bounds",
+ return isl_schedule_node_free(node));
+ node = isl_schedule_node_cow(node);
+ if (!node)
+ return NULL;
+
+ tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors,
+ n - generation);
+ isl_schedule_tree_free(node->tree);
+ node->tree = tree;
+ node->ancestors = isl_schedule_tree_list_drop(node->ancestors,
+ n - generation, generation);
+ if (!node->ancestors || !node->tree)
+ return isl_schedule_node_free(node);
+
+ return node;
+}
+
+/* Move the "node" pointer to the parent of the node it currently points to.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_parent(
+ __isl_take isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+ if (!isl_schedule_node_has_parent(node))
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "node has no parent",
+ return isl_schedule_node_free(node));
+ return isl_schedule_node_ancestor(node, 1);
+}
+
+/* Move the "node" pointer to the root of its schedule tree.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_root(
+ __isl_take isl_schedule_node *node)
+{
+ isl_size n;
+
+ if (!node)
+ return NULL;
+ n = isl_schedule_node_get_tree_depth(node);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ return isl_schedule_node_ancestor(node, n);
+}
+
+/* Move the "node" pointer to the child at position "pos" of the node
+ * it currently points to.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_child(
+ __isl_take isl_schedule_node *node, int pos)
+{
+ isl_size n;
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+ int *child_pos;
+
+ node = isl_schedule_node_cow(node);
+ if (!node)
+ return NULL;
+ if (!isl_schedule_node_has_children(node))
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "node has no children",
+ return isl_schedule_node_free(node));
+
+ ctx = isl_schedule_node_get_ctx(node);
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ child_pos = isl_realloc_array(ctx, node->child_pos, int, n + 1);
+ if (!child_pos)
+ return isl_schedule_node_free(node);
+ node->child_pos = child_pos;
+ node->child_pos[n] = pos;
+
+ node->ancestors = isl_schedule_tree_list_add(node->ancestors,
+ isl_schedule_tree_copy(node->tree));
+ tree = node->tree;
+ if (isl_schedule_tree_has_children(tree))
+ tree = isl_schedule_tree_get_child(tree, pos);
+ else
+ tree = isl_schedule_node_get_leaf(node);
+ isl_schedule_tree_free(node->tree);
+ node->tree = tree;
+
+ if (!node->tree || !node->ancestors)
+ return isl_schedule_node_free(node);
+
+ return node;
+}
+
+/* Move the "node" pointer to the first child of the node
+ * it currently points to.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_first_child(
+ __isl_take isl_schedule_node *node)
+{
+ return isl_schedule_node_child(node, 0);
+}
+
+/* Move the "node" pointer to the child of this node's parent in
+ * the previous child position.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_previous_sibling(
+ __isl_take isl_schedule_node *node)
+{
+ isl_size n;
+ isl_schedule_tree *parent, *tree;
+
+ node = isl_schedule_node_cow(node);
+ if (!node)
+ return NULL;
+ if (!isl_schedule_node_has_previous_sibling(node))
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "node has no previous sibling",
+ return isl_schedule_node_free(node));
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ parent = isl_schedule_tree_list_get_schedule_tree(node->ancestors,
+ n - 1);
+ if (!parent)
+ return isl_schedule_node_free(node);
+ node->child_pos[n - 1]--;
+ tree = isl_schedule_tree_list_get_schedule_tree(parent->children,
+ node->child_pos[n - 1]);
+ isl_schedule_tree_free(parent);
+ if (!tree)
+ return isl_schedule_node_free(node);
+ isl_schedule_tree_free(node->tree);
+ node->tree = tree;
+
+ return node;
+}
+
+/* Move the "node" pointer to the child of this node's parent in
+ * the next child position.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_next_sibling(
+ __isl_take isl_schedule_node *node)
+{
+ isl_size n;
+ isl_schedule_tree *parent, *tree;
+
+ node = isl_schedule_node_cow(node);
+ if (!node)
+ return NULL;
+ if (!isl_schedule_node_has_next_sibling(node))
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "node has no next sibling",
+ return isl_schedule_node_free(node));
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ parent = isl_schedule_tree_list_get_schedule_tree(node->ancestors,
+ n - 1);
+ if (!parent)
+ return isl_schedule_node_free(node);
+ node->child_pos[n - 1]++;
+ tree = isl_schedule_tree_list_get_schedule_tree(parent->children,
+ node->child_pos[n - 1]);
+ isl_schedule_tree_free(parent);
+ if (!tree)
+ return isl_schedule_node_free(node);
+ isl_schedule_tree_free(node->tree);
+ node->tree = tree;
+
+ return node;
+}
+
+/* Return a copy to the child at position "pos" of "node".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_get_child(
+ __isl_keep isl_schedule_node *node, int pos)
+{
+ return isl_schedule_node_child(isl_schedule_node_copy(node), pos);
+}
+
+/* Traverse the descendant of "node" in depth-first order, including
+ * "node" itself. Call "enter" whenever a node is entered and "leave"
+ * whenever a node is left. The callback "enter" is responsible
+ * for moving to the deepest initial subtree of its argument that
+ * should be traversed.
+ */
+static __isl_give isl_schedule_node *traverse(
+ __isl_take isl_schedule_node *node,
+ __isl_give isl_schedule_node *(*enter)(
+ __isl_take isl_schedule_node *node, void *user),
+ __isl_give isl_schedule_node *(*leave)(
+ __isl_take isl_schedule_node *node, void *user),
+ void *user)
+{
+ isl_size depth;
+ isl_size node_depth;
+
+ depth = isl_schedule_node_get_tree_depth(node);
+ if (depth < 0)
+ return isl_schedule_node_free(node);
+
+ do {
+ node = enter(node, user);
+ node = leave(node, user);
+ while ((node_depth = isl_schedule_node_get_tree_depth(node)) >
+ depth &&
+ !isl_schedule_node_has_next_sibling(node)) {
+ node = isl_schedule_node_parent(node);
+ node = leave(node, user);
+ }
+ if (node_depth < 0)
+ return isl_schedule_node_free(node);
+ if (node_depth > depth)
+ node = isl_schedule_node_next_sibling(node);
+ } while (node_depth > depth);
+
+ return node;
+}
+
+/* Internal data structure for isl_schedule_node_foreach_descendant_top_down.
+ *
+ * "fn" is the user-specified callback function.
+ * "user" is the user-specified argument for the callback.
+ */
+struct isl_schedule_node_preorder_data {
+ isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user);
+ void *user;
+};
+
+/* Callback for "traverse" to enter a node and to move
+ * to the deepest initial subtree that should be traversed
+ * for use in a preorder visit.
+ *
+ * If the user callback returns a negative value, then we abort
+ * the traversal. If this callback returns zero, then we skip
+ * the subtree rooted at the current node. Otherwise, we move
+ * down to the first child and repeat the process until a leaf
+ * is reached.
+ */
+static __isl_give isl_schedule_node *preorder_enter(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ struct isl_schedule_node_preorder_data *data = user;
+
+ if (!node)
+ return NULL;
+
+ do {
+ isl_bool r;
+
+ r = data->fn(node, data->user);
+ if (r < 0)
+ return isl_schedule_node_free(node);
+ if (r == isl_bool_false)
+ return node;
+ } while (isl_schedule_node_has_children(node) &&
+ (node = isl_schedule_node_first_child(node)) != NULL);
+
+ return node;
+}
+
+/* Callback for "traverse" to leave a node
+ * for use in a preorder visit.
+ * Since we already visited the node when we entered it,
+ * we do not need to do anything here.
+ */
+static __isl_give isl_schedule_node *preorder_leave(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ return node;
+}
+
+/* Traverse the descendants of "node" (including the node itself)
+ * in depth first preorder.
+ *
+ * If "fn" returns isl_bool_error on any of the nodes,
+ * then the traversal is aborted.
+ * If "fn" returns isl_bool_false on any of the nodes, then the subtree rooted
+ * at that node is skipped.
+ *
+ * Return isl_stat_ok on success and isl_stat_error on failure.
+ */
+isl_stat isl_schedule_node_foreach_descendant_top_down(
+ __isl_keep isl_schedule_node *node,
+ isl_bool (*fn)(__isl_keep isl_schedule_node *node, void *user),
+ void *user)
+{
+ struct isl_schedule_node_preorder_data data = { fn, user };
+
+ node = isl_schedule_node_copy(node);
+ node = traverse(node, &preorder_enter, &preorder_leave, &data);
+ isl_schedule_node_free(node);
+
+ return node ? isl_stat_ok : isl_stat_error;
+}
+
+/* Internal data structure for isl_schedule_node_every_descendant.
+ *
+ * "test" is the user-specified callback function.
+ * "user" is the user-specified callback function argument.
+ *
+ * "failed" is initialized to 0 and set to 1 if "test" fails
+ * on any node.
+ */
+struct isl_union_map_every_data {
+ isl_bool (*test)(__isl_keep isl_schedule_node *node, void *user);
+ void *user;
+ int failed;
+};
+
+/* isl_schedule_node_foreach_descendant_top_down callback
+ * that sets data->failed if data->test returns false and
+ * subsequently aborts the traversal.
+ */
+static isl_bool call_every(__isl_keep isl_schedule_node *node, void *user)
+{
+ struct isl_union_map_every_data *data = user;
+ isl_bool r;
+
+ r = data->test(node, data->user);
+ if (r < 0)
+ return isl_bool_error;
+ if (r)
+ return isl_bool_true;
+ data->failed = 1;
+ return isl_bool_error;
+}
+
+/* Does "test" succeed on every descendant of "node" (including "node" itself)?
+ */
+isl_bool isl_schedule_node_every_descendant(__isl_keep isl_schedule_node *node,
+ isl_bool (*test)(__isl_keep isl_schedule_node *node, void *user),
+ void *user)
+{
+ struct isl_union_map_every_data data = { test, user, 0 };
+ isl_stat r;
+
+ r = isl_schedule_node_foreach_descendant_top_down(node, &call_every,
+ &data);
+ if (r >= 0)
+ return isl_bool_true;
+ if (data.failed)
+ return isl_bool_false;
+ return isl_bool_error;
+}
+
+/* Internal data structure for isl_schedule_node_map_descendant_bottom_up.
+ *
+ * "fn" is the user-specified callback function.
+ * "user" is the user-specified argument for the callback.
+ */
+struct isl_schedule_node_postorder_data {
+ __isl_give isl_schedule_node *(*fn)(__isl_take isl_schedule_node *node,
+ void *user);
+ void *user;
+};
+
+/* Callback for "traverse" to enter a node and to move
+ * to the deepest initial subtree that should be traversed
+ * for use in a postorder visit.
+ *
+ * Since we are performing a postorder visit, we only need
+ * to move to the deepest initial leaf here.
+ */
+static __isl_give isl_schedule_node *postorder_enter(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ while (node && isl_schedule_node_has_children(node))
+ node = isl_schedule_node_first_child(node);
+
+ return node;
+}
+
+/* Callback for "traverse" to leave a node
+ * for use in a postorder visit.
+ *
+ * Since we are performing a postorder visit, we need
+ * to call the user callback here.
+ */
+static __isl_give isl_schedule_node *postorder_leave(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ struct isl_schedule_node_postorder_data *data = user;
+
+ return data->fn(node, data->user);
+}
+
+/* Traverse the descendants of "node" (including the node itself)
+ * in depth first postorder, allowing the user to modify the visited node.
+ * The traversal continues from the node returned by the callback function.
+ * It is the responsibility of the user to ensure that this does not
+ * lead to an infinite loop. It is safest to always return a pointer
+ * to the same position (same ancestors and child positions) as the input node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_map_descendant_bottom_up(
+ __isl_take isl_schedule_node *node,
+ __isl_give isl_schedule_node *(*fn)(__isl_take isl_schedule_node *node,
+ void *user), void *user)
+{
+ struct isl_schedule_node_postorder_data data = { fn, user };
+
+ return traverse(node, &postorder_enter, &postorder_leave, &data);
+}
+
+/* Traverse the ancestors of "node" from the root down to and including
+ * the parent of "node", calling "fn" on each of them.
+ *
+ * If "fn" returns -1 on any of the nodes, then the traversal is aborted.
+ *
+ * Return 0 on success and -1 on failure.
+ */
+isl_stat isl_schedule_node_foreach_ancestor_top_down(
+ __isl_keep isl_schedule_node *node,
+ isl_stat (*fn)(__isl_keep isl_schedule_node *node, void *user),
+ void *user)
+{
+ int i;
+ isl_size n;
+
+ n = isl_schedule_node_get_tree_depth(node);
+ if (n < 0)
+ return isl_stat_error;
+
+ for (i = 0; i < n; ++i) {
+ isl_schedule_node *ancestor;
+ isl_stat r;
+
+ ancestor = isl_schedule_node_copy(node);
+ ancestor = isl_schedule_node_ancestor(ancestor, n - i);
+ r = fn(ancestor, user);
+ isl_schedule_node_free(ancestor);
+ if (r < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Is any node in the subtree rooted at "node" anchored?
+ * That is, do any of these nodes reference the outer band nodes?
+ */
+isl_bool isl_schedule_node_is_subtree_anchored(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return isl_bool_error;
+ return isl_schedule_tree_is_subtree_anchored(node->tree);
+}
+
+/* Return the number of members in the given band node.
+ */
+isl_size isl_schedule_node_band_n_member(__isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return isl_size_error;
+ return isl_schedule_tree_band_n_member(node->tree);
+}
+
+/* Is the band member at position "pos" of the band node "node"
+ * marked coincident?
+ */
+isl_bool isl_schedule_node_band_member_get_coincident(
+ __isl_keep isl_schedule_node *node, int pos)
+{
+ if (!node)
+ return isl_bool_error;
+ return isl_schedule_tree_band_member_get_coincident(node->tree, pos);
+}
+
+/* Mark the band member at position "pos" the band node "node"
+ * as being coincident or not according to "coincident".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_member_set_coincident(
+ __isl_take isl_schedule_node *node, int pos, int coincident)
+{
+ int c;
+ isl_schedule_tree *tree;
+
+ if (!node)
+ return NULL;
+ c = isl_schedule_node_band_member_get_coincident(node, pos);
+ if (c == coincident)
+ return node;
+
+ tree = isl_schedule_tree_copy(node->tree);
+ tree = isl_schedule_tree_band_member_set_coincident(tree, pos,
+ coincident);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Is the band node "node" marked permutable?
+ */
+isl_bool isl_schedule_node_band_get_permutable(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return isl_bool_error;
+
+ return isl_schedule_tree_band_get_permutable(node->tree);
+}
+
+/* Mark the band node "node" permutable or not according to "permutable"?
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_set_permutable(
+ __isl_take isl_schedule_node *node, int permutable)
+{
+ isl_schedule_tree *tree;
+
+ if (!node)
+ return NULL;
+ if (isl_schedule_node_band_get_permutable(node) == permutable)
+ return node;
+
+ tree = isl_schedule_tree_copy(node->tree);
+ tree = isl_schedule_tree_band_set_permutable(tree, permutable);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Return the schedule space of the band node.
+ */
+__isl_give isl_space *isl_schedule_node_band_get_space(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_band_get_space(node->tree);
+}
+
+/* Return the schedule of the band node in isolation.
+ */
+__isl_give isl_multi_union_pw_aff *isl_schedule_node_band_get_partial_schedule(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_band_get_partial_schedule(node->tree);
+}
+
+/* Return the schedule of the band node in isolation in the form of
+ * an isl_union_map.
+ *
+ * If the band does not have any members, then we construct a universe map
+ * with the universe of the domain elements reaching the node as domain.
+ * Otherwise, we extract an isl_multi_union_pw_aff representation and
+ * convert that to an isl_union_map.
+ */
+__isl_give isl_union_map *isl_schedule_node_band_get_partial_schedule_union_map(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_size n;
+ isl_multi_union_pw_aff *mupa;
+
+ if (!node)
+ return NULL;
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_band)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "not a band node", return NULL);
+ n = isl_schedule_node_band_n_member(node);
+ if (n < 0)
+ return NULL;
+ if (n == 0) {
+ isl_union_set *domain;
+
+ domain = isl_schedule_node_get_universe_domain(node);
+ return isl_union_map_from_domain(domain);
+ }
+
+ mupa = isl_schedule_node_band_get_partial_schedule(node);
+ return isl_union_map_from_multi_union_pw_aff(mupa);
+}
+
+/* Return the loop AST generation type for the band member of band node "node"
+ * at position "pos".
+ */
+enum isl_ast_loop_type isl_schedule_node_band_member_get_ast_loop_type(
+ __isl_keep isl_schedule_node *node, int pos)
+{
+ if (!node)
+ return isl_ast_loop_error;
+
+ return isl_schedule_tree_band_member_get_ast_loop_type(node->tree, pos);
+}
+
+/* Set the loop AST generation type for the band member of band node "node"
+ * at position "pos" to "type".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_member_set_ast_loop_type(
+ __isl_take isl_schedule_node *node, int pos,
+ enum isl_ast_loop_type type)
+{
+ isl_schedule_tree *tree;
+
+ if (!node)
+ return NULL;
+
+ tree = isl_schedule_tree_copy(node->tree);
+ tree = isl_schedule_tree_band_member_set_ast_loop_type(tree, pos, type);
+ return isl_schedule_node_graft_tree(node, tree);
+}
+
+/* Return the loop AST generation type for the band member of band node "node"
+ * at position "pos" for the isolated part.
+ */
+enum isl_ast_loop_type isl_schedule_node_band_member_get_isolate_ast_loop_type(
+ __isl_keep isl_schedule_node *node, int pos)
+{
+ if (!node)
+ return isl_ast_loop_error;
+
+ return isl_schedule_tree_band_member_get_isolate_ast_loop_type(
+ node->tree, pos);
+}
+
+/* Set the loop AST generation type for the band member of band node "node"
+ * at position "pos" for the isolated part to "type".
+ */
+__isl_give isl_schedule_node *
+isl_schedule_node_band_member_set_isolate_ast_loop_type(
+ __isl_take isl_schedule_node *node, int pos,
+ enum isl_ast_loop_type type)
+{
+ isl_schedule_tree *tree;
+
+ if (!node)
+ return NULL;
+
+ tree = isl_schedule_tree_copy(node->tree);
+ tree = isl_schedule_tree_band_member_set_isolate_ast_loop_type(tree,
+ pos, type);
+ return isl_schedule_node_graft_tree(node, tree);
+}
+
+/* Return the AST build options associated to band node "node".
+ */
+__isl_give isl_union_set *isl_schedule_node_band_get_ast_build_options(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_band_get_ast_build_options(node->tree);
+}
+
+/* Replace the AST build options associated to band node "node" by "options".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_set_ast_build_options(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *options)
+{
+ isl_schedule_tree *tree;
+
+ if (!node || !options)
+ goto error;
+
+ tree = isl_schedule_tree_copy(node->tree);
+ tree = isl_schedule_tree_band_set_ast_build_options(tree, options);
+ return isl_schedule_node_graft_tree(node, tree);
+error:
+ isl_schedule_node_free(node);
+ isl_union_set_free(options);
+ return NULL;
+}
+
+/* Return the "isolate" option associated to band node "node".
+ */
+__isl_give isl_set *isl_schedule_node_band_get_ast_isolate_option(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_size depth;
+
+ depth = isl_schedule_node_get_schedule_depth(node);
+ if (depth < 0)
+ return NULL;
+
+ return isl_schedule_tree_band_get_ast_isolate_option(node->tree, depth);
+}
+
+/* Make sure that that spaces of "node" and "mv" are the same.
+ * Return -1 on error, reporting the error to the user.
+ */
+static int check_space_multi_val(__isl_keep isl_schedule_node *node,
+ __isl_keep isl_multi_val *mv)
+{
+ isl_space *node_space, *mv_space;
+ int equal;
+
+ node_space = isl_schedule_node_band_get_space(node);
+ mv_space = isl_multi_val_get_space(mv);
+ equal = isl_space_tuple_is_equal(node_space, isl_dim_set,
+ mv_space, isl_dim_set);
+ isl_space_free(mv_space);
+ isl_space_free(node_space);
+ if (equal < 0)
+ return -1;
+ if (!equal)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "spaces don't match", return -1);
+
+ return 0;
+}
+
+/* Multiply the partial schedule of the band node "node"
+ * with the factors in "mv".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_scale(
+ __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv)
+{
+ isl_schedule_tree *tree;
+ int anchored;
+
+ if (!node || !mv)
+ goto error;
+ if (check_space_multi_val(node, mv) < 0)
+ goto error;
+ anchored = isl_schedule_node_is_subtree_anchored(node);
+ if (anchored < 0)
+ goto error;
+ if (anchored)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot scale band node with anchored subtree",
+ goto error);
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_band_scale(tree, mv);
+ return isl_schedule_node_graft_tree(node, tree);
+error:
+ isl_multi_val_free(mv);
+ isl_schedule_node_free(node);
+ return NULL;
+}
+
+/* Divide the partial schedule of the band node "node"
+ * by the factors in "mv".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_scale_down(
+ __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv)
+{
+ isl_schedule_tree *tree;
+ int anchored;
+
+ if (!node || !mv)
+ goto error;
+ if (check_space_multi_val(node, mv) < 0)
+ goto error;
+ anchored = isl_schedule_node_is_subtree_anchored(node);
+ if (anchored < 0)
+ goto error;
+ if (anchored)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot scale down band node with anchored subtree",
+ goto error);
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_band_scale_down(tree, mv);
+ return isl_schedule_node_graft_tree(node, tree);
+error:
+ isl_multi_val_free(mv);
+ isl_schedule_node_free(node);
+ return NULL;
+}
+
+/* Reduce the partial schedule of the band node "node"
+ * modulo the factors in "mv".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_mod(
+ __isl_take isl_schedule_node *node, __isl_take isl_multi_val *mv)
+{
+ isl_schedule_tree *tree;
+ isl_bool anchored;
+
+ if (!node || !mv)
+ goto error;
+ if (check_space_multi_val(node, mv) < 0)
+ goto error;
+ anchored = isl_schedule_node_is_subtree_anchored(node);
+ if (anchored < 0)
+ goto error;
+ if (anchored)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot perform mod on band node with anchored subtree",
+ goto error);
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_band_mod(tree, mv);
+ return isl_schedule_node_graft_tree(node, tree);
+error:
+ isl_multi_val_free(mv);
+ isl_schedule_node_free(node);
+ return NULL;
+}
+
+/* Make sure that that spaces of "node" and "mupa" are the same.
+ * Return isl_stat_error on error, reporting the error to the user.
+ */
+static isl_stat check_space_multi_union_pw_aff(
+ __isl_keep isl_schedule_node *node,
+ __isl_keep isl_multi_union_pw_aff *mupa)
+{
+ isl_space *node_space, *mupa_space;
+ isl_bool equal;
+
+ node_space = isl_schedule_node_band_get_space(node);
+ mupa_space = isl_multi_union_pw_aff_get_space(mupa);
+ equal = isl_space_tuple_is_equal(node_space, isl_dim_set,
+ mupa_space, isl_dim_set);
+ isl_space_free(mupa_space);
+ isl_space_free(node_space);
+ if (equal < 0)
+ return isl_stat_error;
+ if (!equal)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "spaces don't match", return isl_stat_error);
+
+ return isl_stat_ok;
+}
+
+/* Shift the partial schedule of the band node "node" by "shift".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_shift(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_multi_union_pw_aff *shift)
+{
+ isl_schedule_tree *tree;
+ int anchored;
+
+ if (!node || !shift)
+ goto error;
+ if (check_space_multi_union_pw_aff(node, shift) < 0)
+ goto error;
+ anchored = isl_schedule_node_is_subtree_anchored(node);
+ if (anchored < 0)
+ goto error;
+ if (anchored)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot shift band node with anchored subtree",
+ goto error);
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_band_shift(tree, shift);
+ return isl_schedule_node_graft_tree(node, tree);
+error:
+ isl_multi_union_pw_aff_free(shift);
+ isl_schedule_node_free(node);
+ return NULL;
+}
+
+/* Tile "node" with tile sizes "sizes".
+ *
+ * The current node is replaced by two nested nodes corresponding
+ * to the tile dimensions and the point dimensions.
+ *
+ * Return a pointer to the outer (tile) node.
+ *
+ * If any of the descendants of "node" depend on the set of outer band nodes,
+ * then we refuse to tile the node.
+ *
+ * If the scale tile loops option is set, then the tile loops
+ * are scaled by the tile sizes. If the shift point loops option is set,
+ * then the point loops are shifted to start at zero.
+ * In particular, these options affect the tile and point loop schedules
+ * as follows
+ *
+ * scale shift original tile point
+ *
+ * 0 0 i floor(i/s) i
+ * 1 0 i s * floor(i/s) i
+ * 0 1 i floor(i/s) i - s * floor(i/s)
+ * 1 1 i s * floor(i/s) i - s * floor(i/s)
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_tile(
+ __isl_take isl_schedule_node *node, __isl_take isl_multi_val *sizes)
+{
+ isl_schedule_tree *tree;
+ int anchored;
+
+ if (!node || !sizes)
+ goto error;
+ anchored = isl_schedule_node_is_subtree_anchored(node);
+ if (anchored < 0)
+ goto error;
+ if (anchored)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot tile band node with anchored subtree",
+ goto error);
+
+ if (check_space_multi_val(node, sizes) < 0)
+ goto error;
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_band_tile(tree, sizes);
+ return isl_schedule_node_graft_tree(node, tree);
+error:
+ isl_multi_val_free(sizes);
+ isl_schedule_node_free(node);
+ return NULL;
+}
+
+/* Move the band node "node" down to all the leaves in the subtree
+ * rooted at "node".
+ * Return a pointer to the node in the resulting tree that is in the same
+ * position as the node pointed to by "node" in the original tree.
+ *
+ * If the node only has a leaf child, then nothing needs to be done.
+ * Otherwise, the child of the node is removed and the result is
+ * appended to all the leaves in the subtree rooted at the original child.
+ * Since the node is moved to the leaves, it needs to be expanded
+ * according to the expansion, if any, defined by that subtree.
+ * In the end, the original node is replaced by the result of
+ * attaching copies of the expanded node to the leaves.
+ *
+ * If any of the nodes in the subtree rooted at "node" depend on
+ * the set of outer band nodes then we refuse to sink the band node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_sink(
+ __isl_take isl_schedule_node *node)
+{
+ enum isl_schedule_node_type type;
+ isl_schedule_tree *tree, *child;
+ isl_union_pw_multi_aff *contraction;
+ isl_bool anchored;
+ isl_size n;
+
+ if (!node)
+ return NULL;
+
+ type = isl_schedule_node_get_type(node);
+ if (type != isl_schedule_node_band)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "not a band node", return isl_schedule_node_free(node));
+ anchored = isl_schedule_node_is_subtree_anchored(node);
+ if (anchored < 0)
+ return isl_schedule_node_free(node);
+ if (anchored)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot sink band node in anchored subtree",
+ return isl_schedule_node_free(node));
+ n = isl_schedule_tree_n_children(node->tree);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ if (n == 0)
+ return node;
+
+ contraction = isl_schedule_node_get_subtree_contraction(node);
+
+ tree = isl_schedule_node_get_tree(node);
+ child = isl_schedule_tree_get_child(tree, 0);
+ tree = isl_schedule_tree_reset_children(tree);
+ tree = isl_schedule_tree_pullback_union_pw_multi_aff(tree, contraction);
+ tree = isl_schedule_tree_append_to_leaves(child, tree);
+
+ return isl_schedule_node_graft_tree(node, tree);
+}
+
+/* Split "node" into two nested band nodes, one with the first "pos"
+ * dimensions and one with the remaining dimensions.
+ * The schedules of the two band nodes live in anonymous spaces.
+ * The loop AST generation type options and the isolate option
+ * are split over the two band nodes.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_split(
+ __isl_take isl_schedule_node *node, int pos)
+{
+ isl_size depth;
+ isl_schedule_tree *tree;
+
+ depth = isl_schedule_node_get_schedule_depth(node);
+ if (depth < 0)
+ return isl_schedule_node_free(node);
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_band_split(tree, pos, depth);
+ return isl_schedule_node_graft_tree(node, tree);
+}
+
+/* Return the context of the context node "node".
+ */
+__isl_give isl_set *isl_schedule_node_context_get_context(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_context_get_context(node->tree);
+}
+
+/* Return the domain of the domain node "node".
+ */
+__isl_give isl_union_set *isl_schedule_node_domain_get_domain(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_domain_get_domain(node->tree);
+}
+
+/* Return the expansion map of expansion node "node".
+ */
+__isl_give isl_union_map *isl_schedule_node_expansion_get_expansion(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_expansion_get_expansion(node->tree);
+}
+
+/* Return the contraction of expansion node "node".
+ */
+__isl_give isl_union_pw_multi_aff *isl_schedule_node_expansion_get_contraction(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_expansion_get_contraction(node->tree);
+}
+
+/* Replace the contraction and the expansion of the expansion node "node"
+ * by "contraction" and "expansion".
+ */
+__isl_give isl_schedule_node *
+isl_schedule_node_expansion_set_contraction_and_expansion(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_union_map *expansion)
+{
+ isl_schedule_tree *tree;
+
+ if (!node || !contraction || !expansion)
+ goto error;
+
+ tree = isl_schedule_tree_copy(node->tree);
+ tree = isl_schedule_tree_expansion_set_contraction_and_expansion(tree,
+ contraction, expansion);
+ return isl_schedule_node_graft_tree(node, tree);
+error:
+ isl_schedule_node_free(node);
+ isl_union_pw_multi_aff_free(contraction);
+ isl_union_map_free(expansion);
+ return NULL;
+}
+
+/* Return the extension of the extension node "node".
+ */
+__isl_give isl_union_map *isl_schedule_node_extension_get_extension(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_extension_get_extension(node->tree);
+}
+
+/* Replace the extension of extension node "node" by "extension".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_extension_set_extension(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_map *extension)
+{
+ isl_schedule_tree *tree;
+
+ if (!node || !extension)
+ goto error;
+
+ tree = isl_schedule_tree_copy(node->tree);
+ tree = isl_schedule_tree_extension_set_extension(tree, extension);
+ return isl_schedule_node_graft_tree(node, tree);
+error:
+ isl_schedule_node_free(node);
+ isl_union_map_free(extension);
+ return NULL;
+}
+
+/* Return the filter of the filter node "node".
+ */
+__isl_give isl_union_set *isl_schedule_node_filter_get_filter(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_filter_get_filter(node->tree);
+}
+
+/* Replace the filter of filter node "node" by "filter".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_filter_set_filter(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter)
+{
+ isl_schedule_tree *tree;
+
+ if (!node || !filter)
+ goto error;
+
+ tree = isl_schedule_tree_copy(node->tree);
+ tree = isl_schedule_tree_filter_set_filter(tree, filter);
+ return isl_schedule_node_graft_tree(node, tree);
+error:
+ isl_schedule_node_free(node);
+ isl_union_set_free(filter);
+ return NULL;
+}
+
+/* Intersect the filter of filter node "node" with "filter".
+ *
+ * If the filter of the node is already a subset of "filter",
+ * then leave the node unchanged.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_filter_intersect_filter(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter)
+{
+ isl_union_set *node_filter = NULL;
+ isl_bool subset;
+
+ if (!node || !filter)
+ goto error;
+
+ node_filter = isl_schedule_node_filter_get_filter(node);
+ subset = isl_union_set_is_subset(node_filter, filter);
+ if (subset < 0)
+ goto error;
+ if (subset) {
+ isl_union_set_free(node_filter);
+ isl_union_set_free(filter);
+ return node;
+ }
+ node_filter = isl_union_set_intersect(node_filter, filter);
+ node = isl_schedule_node_filter_set_filter(node, node_filter);
+ return node;
+error:
+ isl_schedule_node_free(node);
+ isl_union_set_free(node_filter);
+ isl_union_set_free(filter);
+ return NULL;
+}
+
+/* Return the guard of the guard node "node".
+ */
+__isl_give isl_set *isl_schedule_node_guard_get_guard(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_guard_get_guard(node->tree);
+}
+
+/* Return the mark identifier of the mark node "node".
+ */
+__isl_give isl_id *isl_schedule_node_mark_get_id(
+ __isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return NULL;
+
+ return isl_schedule_tree_mark_get_id(node->tree);
+}
+
+/* Replace the child at position "pos" of the sequence node "node"
+ * by the children of sequence root node of "tree".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_sequence_splice(
+ __isl_take isl_schedule_node *node, int pos,
+ __isl_take isl_schedule_tree *tree)
+{
+ isl_schedule_tree *node_tree;
+
+ if (!node || !tree)
+ goto error;
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "not a sequence node", goto error);
+ if (isl_schedule_tree_get_type(tree) != isl_schedule_node_sequence)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "not a sequence node", goto error);
+ node_tree = isl_schedule_node_get_tree(node);
+ node_tree = isl_schedule_tree_sequence_splice(node_tree, pos, tree);
+ node = isl_schedule_node_graft_tree(node, node_tree);
+
+ return node;
+error:
+ isl_schedule_node_free(node);
+ isl_schedule_tree_free(tree);
+ return NULL;
+}
+
+/* Given a sequence node "node", with a child at position "pos" that
+ * is also a sequence node, attach the children of that node directly
+ * as children of "node" at that position, replacing the original child.
+ *
+ * The filters of these children are intersected with the filter
+ * of the child at position "pos".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_sequence_splice_child(
+ __isl_take isl_schedule_node *node, int pos)
+{
+ int i;
+ isl_size n;
+ isl_union_set *filter;
+ isl_schedule_node *child;
+ isl_schedule_tree *tree;
+
+ if (!node)
+ return NULL;
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "not a sequence node",
+ return isl_schedule_node_free(node));
+ node = isl_schedule_node_child(node, pos);
+ node = isl_schedule_node_child(node, 0);
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "not a sequence node",
+ return isl_schedule_node_free(node));
+ n = isl_schedule_node_n_children(node);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ child = isl_schedule_node_copy(node);
+ node = isl_schedule_node_parent(node);
+ filter = isl_schedule_node_filter_get_filter(node);
+ for (i = 0; i < n; ++i) {
+ child = isl_schedule_node_child(child, i);
+ child = isl_schedule_node_filter_intersect_filter(child,
+ isl_union_set_copy(filter));
+ child = isl_schedule_node_parent(child);
+ }
+ isl_union_set_free(filter);
+ tree = isl_schedule_node_get_tree(child);
+ isl_schedule_node_free(child);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_sequence_splice(node, pos, tree);
+
+ return node;
+}
+
+/* Update the ancestors of "node" to point to the tree that "node"
+ * now points to.
+ * That is, replace the child in the original parent that corresponds
+ * to the current tree position by node->tree and continue updating
+ * the ancestors in the same way until the root is reached.
+ *
+ * If "fn" is not NULL, then it is called on each ancestor as we move up
+ * the tree so that it can modify the ancestor before it is added
+ * to the list of ancestors of the modified node.
+ * The additional "pos" argument records the position
+ * of the "tree" argument in the original schedule tree.
+ *
+ * If "node" originally points to a leaf of the schedule tree, then make sure
+ * that in the end it points to a leaf in the updated schedule tree.
+ */
+static __isl_give isl_schedule_node *update_ancestors(
+ __isl_take isl_schedule_node *node,
+ __isl_give isl_schedule_tree *(*fn)(__isl_take isl_schedule_tree *tree,
+ __isl_keep isl_schedule_node *pos, void *user), void *user)
+{
+ int i;
+ isl_size n;
+ int is_leaf;
+ isl_schedule_tree *tree;
+ isl_schedule_node *pos = NULL;
+
+ if (fn)
+ pos = isl_schedule_node_copy(node);
+
+ node = isl_schedule_node_cow(node);
+ if (!node)
+ return isl_schedule_node_free(pos);
+
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0)
+ return isl_schedule_node_free(pos);
+ tree = isl_schedule_tree_copy(node->tree);
+
+ for (i = n - 1; i >= 0; --i) {
+ isl_schedule_tree *parent;
+
+ parent = isl_schedule_tree_list_get_schedule_tree(
+ node->ancestors, i);
+ parent = isl_schedule_tree_replace_child(parent,
+ node->child_pos[i], tree);
+ if (fn) {
+ pos = isl_schedule_node_parent(pos);
+ parent = fn(parent, pos, user);
+ }
+ node->ancestors = isl_schedule_tree_list_set_schedule_tree(
+ node->ancestors, i, isl_schedule_tree_copy(parent));
+
+ tree = parent;
+ }
+
+ if (fn)
+ isl_schedule_node_free(pos);
+
+ is_leaf = isl_schedule_tree_is_leaf(node->tree);
+ node->schedule = isl_schedule_set_root(node->schedule, tree);
+ if (is_leaf) {
+ isl_schedule_tree_free(node->tree);
+ node->tree = isl_schedule_node_get_leaf(node);
+ }
+
+ if (!node->schedule || !node->ancestors)
+ return isl_schedule_node_free(node);
+
+ return node;
+}
+
+/* Replace the subtree that "pos" points to by "tree", updating
+ * the ancestors to maintain a consistent state.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_graft_tree(
+ __isl_take isl_schedule_node *pos, __isl_take isl_schedule_tree *tree)
+{
+ if (!tree || !pos)
+ goto error;
+ if (pos->tree == tree) {
+ isl_schedule_tree_free(tree);
+ return pos;
+ }
+
+ pos = isl_schedule_node_cow(pos);
+ if (!pos)
+ goto error;
+
+ isl_schedule_tree_free(pos->tree);
+ pos->tree = tree;
+
+ return update_ancestors(pos, NULL, NULL);
+error:
+ isl_schedule_node_free(pos);
+ isl_schedule_tree_free(tree);
+ return NULL;
+}
+
+/* Make sure we can insert a node between "node" and its parent.
+ * Return -1 on error, reporting the reason why we cannot insert a node.
+ */
+static int check_insert(__isl_keep isl_schedule_node *node)
+{
+ int has_parent;
+ enum isl_schedule_node_type type;
+
+ has_parent = isl_schedule_node_has_parent(node);
+ if (has_parent < 0)
+ return -1;
+ if (!has_parent)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot insert node outside of root", return -1);
+
+ type = isl_schedule_node_get_parent_type(node);
+ if (type == isl_schedule_node_error)
+ return -1;
+ if (type == isl_schedule_node_set || type == isl_schedule_node_sequence)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot insert node between set or sequence node "
+ "and its filter children", return -1);
+
+ return 0;
+}
+
+/* Insert a band node with partial schedule "mupa" between "node" and
+ * its parent.
+ * Return a pointer to the new band node.
+ *
+ * If any of the nodes in the subtree rooted at "node" depend on
+ * the set of outer band nodes then we refuse to insert the band node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_partial_schedule(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ int anchored;
+ isl_schedule_band *band;
+ isl_schedule_tree *tree;
+
+ if (check_insert(node) < 0)
+ node = isl_schedule_node_free(node);
+ anchored = isl_schedule_node_is_subtree_anchored(node);
+ if (anchored < 0)
+ goto error;
+ if (anchored)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot insert band node in anchored subtree",
+ goto error);
+
+ tree = isl_schedule_node_get_tree(node);
+ band = isl_schedule_band_from_multi_union_pw_aff(mupa);
+ tree = isl_schedule_tree_insert_band(tree, band);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+error:
+ isl_schedule_node_free(node);
+ isl_multi_union_pw_aff_free(mupa);
+ return NULL;
+}
+
+/* Insert a context node with context "context" between "node" and its parent.
+ * Return a pointer to the new context node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_context(
+ __isl_take isl_schedule_node *node, __isl_take isl_set *context)
+{
+ isl_schedule_tree *tree;
+
+ if (check_insert(node) < 0)
+ node = isl_schedule_node_free(node);
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_insert_context(tree, context);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Insert an expansion node with the given "contraction" and "expansion"
+ * between "node" and its parent.
+ * Return a pointer to the new expansion node.
+ *
+ * Typically the domain and range spaces of the expansion are different.
+ * This means that only one of them can refer to the current domain space
+ * in a consistent tree. It is up to the caller to ensure that the tree
+ * returns to a consistent state.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_expansion(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_union_map *expansion)
+{
+ isl_schedule_tree *tree;
+
+ if (check_insert(node) < 0)
+ node = isl_schedule_node_free(node);
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_insert_expansion(tree, contraction, expansion);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Insert an extension node with extension "extension" between "node" and
+ * its parent.
+ * Return a pointer to the new extension node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_extension(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *extension)
+{
+ isl_schedule_tree *tree;
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_insert_extension(tree, extension);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Insert a filter node with filter "filter" between "node" and its parent.
+ * Return a pointer to the new filter node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_filter(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter)
+{
+ isl_schedule_tree *tree;
+
+ if (check_insert(node) < 0)
+ node = isl_schedule_node_free(node);
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_insert_filter(tree, filter);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Insert a guard node with guard "guard" between "node" and its parent.
+ * Return a pointer to the new guard node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_guard(
+ __isl_take isl_schedule_node *node, __isl_take isl_set *guard)
+{
+ isl_schedule_tree *tree;
+
+ if (check_insert(node) < 0)
+ node = isl_schedule_node_free(node);
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_insert_guard(tree, guard);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Insert a mark node with mark identifier "mark" between "node" and
+ * its parent.
+ * Return a pointer to the new mark node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_mark(
+ __isl_take isl_schedule_node *node, __isl_take isl_id *mark)
+{
+ isl_schedule_tree *tree;
+
+ if (check_insert(node) < 0)
+ node = isl_schedule_node_free(node);
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_insert_mark(tree, mark);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Attach the current subtree of "node" to a sequence of filter tree nodes
+ * with filters described by "filters", attach this sequence
+ * of filter tree nodes as children to a new tree of type "type" and
+ * replace the original subtree of "node" by this new tree.
+ * Each copy of the original subtree is simplified with respect
+ * to the corresponding filter.
+ */
+static __isl_give isl_schedule_node *isl_schedule_node_insert_children(
+ __isl_take isl_schedule_node *node,
+ enum isl_schedule_node_type type,
+ __isl_take isl_union_set_list *filters)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+ isl_schedule_tree_list *list;
+
+ if (check_insert(node) < 0)
+ node = isl_schedule_node_free(node);
+
+ n = isl_union_set_list_n_union_set(filters);
+ if (!node || n < 0)
+ goto error;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ list = isl_schedule_tree_list_alloc(ctx, n);
+ for (i = 0; i < n; ++i) {
+ isl_schedule_node *node_i;
+ isl_schedule_tree *tree;
+ isl_union_set *filter;
+
+ filter = isl_union_set_list_get_union_set(filters, i);
+ node_i = isl_schedule_node_copy(node);
+ node_i = isl_schedule_node_gist(node_i,
+ isl_union_set_copy(filter));
+ tree = isl_schedule_node_get_tree(node_i);
+ isl_schedule_node_free(node_i);
+ tree = isl_schedule_tree_insert_filter(tree, filter);
+ list = isl_schedule_tree_list_add(list, tree);
+ }
+ tree = isl_schedule_tree_from_children(type, list);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ isl_union_set_list_free(filters);
+ return node;
+error:
+ isl_union_set_list_free(filters);
+ isl_schedule_node_free(node);
+ return NULL;
+}
+
+/* Insert a sequence node with child filters "filters" between "node" and
+ * its parent. That is, the tree that "node" points to is attached
+ * to each of the child nodes of the filter nodes.
+ * Return a pointer to the new sequence node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_sequence(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_set_list *filters)
+{
+ return isl_schedule_node_insert_children(node,
+ isl_schedule_node_sequence, filters);
+}
+
+/* Insert a set node with child filters "filters" between "node" and
+ * its parent. That is, the tree that "node" points to is attached
+ * to each of the child nodes of the filter nodes.
+ * Return a pointer to the new set node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_set(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_set_list *filters)
+{
+ return isl_schedule_node_insert_children(node,
+ isl_schedule_node_set, filters);
+}
+
+/* Remove "node" from its schedule tree and return a pointer
+ * to the leaf at the same position in the updated schedule tree.
+ *
+ * It is not allowed to remove the root of a schedule tree or
+ * a child of a set or sequence node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_cut(
+ __isl_take isl_schedule_node *node)
+{
+ isl_schedule_tree *leaf;
+ enum isl_schedule_node_type parent_type;
+
+ if (!node)
+ return NULL;
+ if (!isl_schedule_node_has_parent(node))
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot cut root", return isl_schedule_node_free(node));
+
+ parent_type = isl_schedule_node_get_parent_type(node);
+ if (parent_type == isl_schedule_node_set ||
+ parent_type == isl_schedule_node_sequence)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot cut child of set or sequence",
+ return isl_schedule_node_free(node));
+
+ leaf = isl_schedule_node_get_leaf(node);
+ return isl_schedule_node_graft_tree(node, leaf);
+}
+
+/* Remove a single node from the schedule tree, attaching the child
+ * of "node" directly to its parent.
+ * Return a pointer to this former child or to the leaf the position
+ * of the original node if there was no child.
+ * It is not allowed to remove the root of a schedule tree,
+ * a set or sequence node, a child of a set or sequence node or
+ * a band node with an anchored subtree.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_delete(
+ __isl_take isl_schedule_node *node)
+{
+ isl_size n, depth;
+ isl_schedule_tree *tree;
+ enum isl_schedule_node_type type;
+
+ depth = isl_schedule_node_get_tree_depth(node);
+ n = isl_schedule_node_n_children(node);
+ if (depth < 0 || n < 0)
+ return isl_schedule_node_free(node);
+
+ if (depth == 0)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot delete root node",
+ return isl_schedule_node_free(node));
+ if (n != 1)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "can only delete node with a single child",
+ return isl_schedule_node_free(node));
+ type = isl_schedule_node_get_parent_type(node);
+ if (type == isl_schedule_node_sequence || type == isl_schedule_node_set)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "cannot delete child of set or sequence",
+ return isl_schedule_node_free(node));
+ if (isl_schedule_node_get_type(node) == isl_schedule_node_band) {
+ int anchored;
+
+ anchored = isl_schedule_node_is_subtree_anchored(node);
+ if (anchored < 0)
+ return isl_schedule_node_free(node);
+ if (anchored)
+ isl_die(isl_schedule_node_get_ctx(node),
+ isl_error_invalid,
+ "cannot delete band node with anchored subtree",
+ return isl_schedule_node_free(node));
+ }
+
+ tree = isl_schedule_node_get_tree(node);
+ if (!tree || isl_schedule_tree_has_children(tree)) {
+ tree = isl_schedule_tree_child(tree, 0);
+ } else {
+ isl_schedule_tree_free(tree);
+ tree = isl_schedule_node_get_leaf(node);
+ }
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Internal data structure for the group_ancestor callback.
+ *
+ * If "finished" is set, then we no longer need to modify
+ * any further ancestors.
+ *
+ * "contraction" and "expansion" represent the expansion
+ * that reflects the grouping.
+ *
+ * "domain" contains the domain elements that reach the position
+ * where the grouping is performed. That is, it is the range
+ * of the resulting expansion.
+ * "domain_universe" is the universe of "domain".
+ * "group" is the set of group elements, i.e., the domain
+ * of the resulting expansion.
+ * "group_universe" is the universe of "group".
+ *
+ * "sched" is the schedule for the group elements, in pratice
+ * an identity mapping on "group_universe".
+ * "dim" is the dimension of "sched".
+ */
+struct isl_schedule_group_data {
+ int finished;
+
+ isl_union_map *expansion;
+ isl_union_pw_multi_aff *contraction;
+
+ isl_union_set *domain;
+ isl_union_set *domain_universe;
+ isl_union_set *group;
+ isl_union_set *group_universe;
+
+ int dim;
+ isl_multi_aff *sched;
+};
+
+/* Is domain covered by data->domain within data->domain_universe?
+ */
+static isl_bool locally_covered_by_domain(__isl_keep isl_union_set *domain,
+ struct isl_schedule_group_data *data)
+{
+ isl_bool is_subset;
+ isl_union_set *test;
+
+ test = isl_union_set_copy(domain);
+ test = isl_union_set_intersect(test,
+ isl_union_set_copy(data->domain_universe));
+ is_subset = isl_union_set_is_subset(test, data->domain);
+ isl_union_set_free(test);
+
+ return is_subset;
+}
+
+/* Update the band tree root "tree" to refer to the group instances
+ * in data->group rather than the original domain elements in data->domain.
+ * "pos" is the position in the original schedule tree where the modified
+ * "tree" will be attached.
+ *
+ * Add the part of the identity schedule on the group instances data->sched
+ * that corresponds to this band node to the band schedule.
+ * If the domain elements that reach the node and that are part
+ * of data->domain_universe are all elements of data->domain (and therefore
+ * replaced by the group instances) then this data->domain_universe
+ * is removed from the domain of the band schedule.
+ */
+static __isl_give isl_schedule_tree *group_band(
+ __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
+ struct isl_schedule_group_data *data)
+{
+ isl_union_set *domain;
+ isl_multi_aff *ma;
+ isl_multi_union_pw_aff *mupa, *partial;
+ isl_bool is_covered;
+ isl_size depth, n;
+ isl_bool has_id;
+
+ domain = isl_schedule_node_get_domain(pos);
+ is_covered = locally_covered_by_domain(domain, data);
+ if (is_covered >= 0 && is_covered) {
+ domain = isl_union_set_universe(domain);
+ domain = isl_union_set_subtract(domain,
+ isl_union_set_copy(data->domain_universe));
+ tree = isl_schedule_tree_band_intersect_domain(tree, domain);
+ } else
+ isl_union_set_free(domain);
+ if (is_covered < 0)
+ return isl_schedule_tree_free(tree);
+ depth = isl_schedule_node_get_schedule_depth(pos);
+ n = isl_schedule_tree_band_n_member(tree);
+ if (depth < 0 || n < 0)
+ return isl_schedule_tree_free(tree);
+ ma = isl_multi_aff_copy(data->sched);
+ ma = isl_multi_aff_drop_dims(ma, isl_dim_out, 0, depth);
+ ma = isl_multi_aff_drop_dims(ma, isl_dim_out, n, data->dim - depth - n);
+ mupa = isl_multi_union_pw_aff_from_multi_aff(ma);
+ partial = isl_schedule_tree_band_get_partial_schedule(tree);
+ has_id = isl_multi_union_pw_aff_has_tuple_id(partial, isl_dim_set);
+ if (has_id < 0) {
+ partial = isl_multi_union_pw_aff_free(partial);
+ } else if (has_id) {
+ isl_id *id;
+ id = isl_multi_union_pw_aff_get_tuple_id(partial, isl_dim_set);
+ mupa = isl_multi_union_pw_aff_set_tuple_id(mupa,
+ isl_dim_set, id);
+ }
+ partial = isl_multi_union_pw_aff_union_add(partial, mupa);
+ tree = isl_schedule_tree_band_set_partial_schedule(tree, partial);
+
+ return tree;
+}
+
+/* Drop the parameters in "uset" that are not also in "space".
+ * "n" is the number of parameters in "space".
+ */
+static __isl_give isl_union_set *union_set_drop_extra_params(
+ __isl_take isl_union_set *uset, __isl_keep isl_space *space, int n)
+{
+ isl_size n2;
+
+ uset = isl_union_set_align_params(uset, isl_space_copy(space));
+ n2 = isl_union_set_dim(uset, isl_dim_param);
+ if (n2 < 0)
+ return isl_union_set_free(uset);
+ uset = isl_union_set_project_out(uset, isl_dim_param, n, n2 - n);
+
+ return uset;
+}
+
+/* Update the context tree root "tree" to refer to the group instances
+ * in data->group rather than the original domain elements in data->domain.
+ * "pos" is the position in the original schedule tree where the modified
+ * "tree" will be attached.
+ *
+ * We do not actually need to update "tree" since a context node only
+ * refers to the schedule space. However, we may need to update "data"
+ * to not refer to any parameters introduced by the context node.
+ */
+static __isl_give isl_schedule_tree *group_context(
+ __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
+ struct isl_schedule_group_data *data)
+{
+ isl_space *space;
+ isl_union_set *domain;
+ isl_size n1, n2;
+ isl_bool involves;
+ isl_size depth;
+
+ depth = isl_schedule_node_get_tree_depth(pos);
+ if (depth < 0)
+ return isl_schedule_tree_free(tree);
+ if (depth == 1)
+ return tree;
+
+ domain = isl_schedule_node_get_universe_domain(pos);
+ space = isl_union_set_get_space(domain);
+ isl_union_set_free(domain);
+
+ n1 = isl_space_dim(space, isl_dim_param);
+ data->expansion = isl_union_map_align_params(data->expansion, space);
+ n2 = isl_union_map_dim(data->expansion, isl_dim_param);
+
+ if (n1 < 0 || n2 < 0)
+ return isl_schedule_tree_free(tree);
+ if (n1 == n2)
+ return tree;
+
+ involves = isl_union_map_involves_dims(data->expansion,
+ isl_dim_param, n1, n2 - n1);
+ if (involves < 0)
+ return isl_schedule_tree_free(tree);
+ if (involves)
+ isl_die(isl_schedule_node_get_ctx(pos), isl_error_invalid,
+ "grouping cannot only refer to global parameters",
+ return isl_schedule_tree_free(tree));
+
+ data->expansion = isl_union_map_project_out(data->expansion,
+ isl_dim_param, n1, n2 - n1);
+ space = isl_union_map_get_space(data->expansion);
+
+ data->contraction = isl_union_pw_multi_aff_align_params(
+ data->contraction, isl_space_copy(space));
+ n2 = isl_union_pw_multi_aff_dim(data->contraction, isl_dim_param);
+ if (n2 < 0)
+ data->contraction =
+ isl_union_pw_multi_aff_free(data->contraction);
+ data->contraction = isl_union_pw_multi_aff_drop_dims(data->contraction,
+ isl_dim_param, n1, n2 - n1);
+
+ data->domain = union_set_drop_extra_params(data->domain, space, n1);
+ data->domain_universe =
+ union_set_drop_extra_params(data->domain_universe, space, n1);
+ data->group = union_set_drop_extra_params(data->group, space, n1);
+ data->group_universe =
+ union_set_drop_extra_params(data->group_universe, space, n1);
+
+ data->sched = isl_multi_aff_align_params(data->sched,
+ isl_space_copy(space));
+ n2 = isl_multi_aff_dim(data->sched, isl_dim_param);
+ if (n2 < 0)
+ data->sched = isl_multi_aff_free(data->sched);
+ data->sched = isl_multi_aff_drop_dims(data->sched,
+ isl_dim_param, n1, n2 - n1);
+
+ isl_space_free(space);
+
+ return tree;
+}
+
+/* Update the domain tree root "tree" to refer to the group instances
+ * in data->group rather than the original domain elements in data->domain.
+ * "pos" is the position in the original schedule tree where the modified
+ * "tree" will be attached.
+ *
+ * We first double-check that all grouped domain elements are actually
+ * part of the root domain and then replace those elements by the group
+ * instances.
+ */
+static __isl_give isl_schedule_tree *group_domain(
+ __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
+ struct isl_schedule_group_data *data)
+{
+ isl_union_set *domain;
+ isl_bool is_subset;
+
+ domain = isl_schedule_tree_domain_get_domain(tree);
+ is_subset = isl_union_set_is_subset(data->domain, domain);
+ isl_union_set_free(domain);
+ if (is_subset < 0)
+ return isl_schedule_tree_free(tree);
+ if (!is_subset)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "grouped domain should be part of outer domain",
+ return isl_schedule_tree_free(tree));
+ domain = isl_schedule_tree_domain_get_domain(tree);
+ domain = isl_union_set_subtract(domain,
+ isl_union_set_copy(data->domain));
+ domain = isl_union_set_union(domain, isl_union_set_copy(data->group));
+ tree = isl_schedule_tree_domain_set_domain(tree, domain);
+
+ return tree;
+}
+
+/* Update the expansion tree root "tree" to refer to the group instances
+ * in data->group rather than the original domain elements in data->domain.
+ * "pos" is the position in the original schedule tree where the modified
+ * "tree" will be attached.
+ *
+ * Let G_1 -> D_1 be the expansion of "tree" and G_2 -> D_2 the newly
+ * introduced expansion in a descendant of "tree".
+ * We first double-check that D_2 is a subset of D_1.
+ * Then we remove D_2 from the range of G_1 -> D_1 and add the mapping
+ * G_1 -> D_1 . D_2 -> G_2.
+ * Simmilarly, we restrict the domain of the contraction to the universe
+ * of the range of the updated expansion and add G_2 -> D_2 . D_1 -> G_1,
+ * attempting to remove the domain constraints of this additional part.
+ */
+static __isl_give isl_schedule_tree *group_expansion(
+ __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
+ struct isl_schedule_group_data *data)
+{
+ isl_union_set *domain;
+ isl_union_map *expansion, *umap;
+ isl_union_pw_multi_aff *contraction, *upma;
+ int is_subset;
+
+ expansion = isl_schedule_tree_expansion_get_expansion(tree);
+ domain = isl_union_map_range(expansion);
+ is_subset = isl_union_set_is_subset(data->domain, domain);
+ isl_union_set_free(domain);
+ if (is_subset < 0)
+ return isl_schedule_tree_free(tree);
+ if (!is_subset)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "grouped domain should be part "
+ "of outer expansion domain",
+ return isl_schedule_tree_free(tree));
+ expansion = isl_schedule_tree_expansion_get_expansion(tree);
+ umap = isl_union_map_from_union_pw_multi_aff(
+ isl_union_pw_multi_aff_copy(data->contraction));
+ umap = isl_union_map_apply_range(expansion, umap);
+ expansion = isl_schedule_tree_expansion_get_expansion(tree);
+ expansion = isl_union_map_subtract_range(expansion,
+ isl_union_set_copy(data->domain));
+ expansion = isl_union_map_union(expansion, umap);
+ umap = isl_union_map_universe(isl_union_map_copy(expansion));
+ domain = isl_union_map_range(umap);
+ contraction = isl_schedule_tree_expansion_get_contraction(tree);
+ umap = isl_union_map_from_union_pw_multi_aff(contraction);
+ umap = isl_union_map_apply_range(isl_union_map_copy(data->expansion),
+ umap);
+ upma = isl_union_pw_multi_aff_from_union_map(umap);
+ contraction = isl_schedule_tree_expansion_get_contraction(tree);
+ contraction = isl_union_pw_multi_aff_intersect_domain(contraction,
+ domain);
+ domain = isl_union_pw_multi_aff_domain(
+ isl_union_pw_multi_aff_copy(upma));
+ upma = isl_union_pw_multi_aff_gist(upma, domain);
+ contraction = isl_union_pw_multi_aff_union_add(contraction, upma);
+ tree = isl_schedule_tree_expansion_set_contraction_and_expansion(tree,
+ contraction, expansion);
+
+ return tree;
+}
+
+/* Update the tree root "tree" to refer to the group instances
+ * in data->group rather than the original domain elements in data->domain.
+ * "pos" is the position in the original schedule tree where the modified
+ * "tree" will be attached.
+ *
+ * If we have come across a domain or expansion node before (data->finished
+ * is set), then we no longer need perform any modifications.
+ *
+ * If "tree" is a filter, then we add data->group_universe to the filter.
+ * We also remove data->domain_universe from the filter if all the domain
+ * elements in this universe that reach the filter node are part of
+ * the elements that are being grouped by data->expansion.
+ * If "tree" is a band, domain or expansion, then it is handled
+ * in a separate function.
+ */
+static __isl_give isl_schedule_tree *group_ancestor(
+ __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
+ void *user)
+{
+ struct isl_schedule_group_data *data = user;
+ isl_union_set *domain;
+ isl_bool is_covered;
+
+ if (!tree || !pos)
+ return isl_schedule_tree_free(tree);
+
+ if (data->finished)
+ return tree;
+
+ switch (isl_schedule_tree_get_type(tree)) {
+ case isl_schedule_node_error:
+ return isl_schedule_tree_free(tree);
+ case isl_schedule_node_extension:
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_unsupported,
+ "grouping not allowed in extended tree",
+ return isl_schedule_tree_free(tree));
+ case isl_schedule_node_band:
+ tree = group_band(tree, pos, data);
+ break;
+ case isl_schedule_node_context:
+ tree = group_context(tree, pos, data);
+ break;
+ case isl_schedule_node_domain:
+ tree = group_domain(tree, pos, data);
+ data->finished = 1;
+ break;
+ case isl_schedule_node_filter:
+ domain = isl_schedule_node_get_domain(pos);
+ is_covered = locally_covered_by_domain(domain, data);
+ isl_union_set_free(domain);
+ if (is_covered < 0)
+ return isl_schedule_tree_free(tree);
+ domain = isl_schedule_tree_filter_get_filter(tree);
+ if (is_covered)
+ domain = isl_union_set_subtract(domain,
+ isl_union_set_copy(data->domain_universe));
+ domain = isl_union_set_union(domain,
+ isl_union_set_copy(data->group_universe));
+ tree = isl_schedule_tree_filter_set_filter(tree, domain);
+ break;
+ case isl_schedule_node_expansion:
+ tree = group_expansion(tree, pos, data);
+ data->finished = 1;
+ break;
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ break;
+ }
+
+ return tree;
+}
+
+/* Group the domain elements that reach "node" into instances
+ * of a single statement with identifier "group_id".
+ * In particular, group the domain elements according to their
+ * prefix schedule.
+ *
+ * That is, introduce an expansion node with as contraction
+ * the prefix schedule (with the target space replaced by "group_id")
+ * and as expansion the inverse of this contraction (with its range
+ * intersected with the domain elements that reach "node").
+ * The outer nodes are then modified to refer to the group instances
+ * instead of the original domain elements.
+ *
+ * No instance of "group_id" is allowed to reach "node" prior
+ * to the grouping.
+ * No ancestor of "node" is allowed to be an extension node.
+ *
+ * Return a pointer to original node in tree, i.e., the child
+ * of the newly introduced expansion node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_group(
+ __isl_take isl_schedule_node *node, __isl_take isl_id *group_id)
+{
+ struct isl_schedule_group_data data = { 0 };
+ isl_space *space;
+ isl_union_set *domain;
+ isl_union_pw_multi_aff *contraction;
+ isl_union_map *expansion;
+ isl_bool disjoint;
+ isl_size depth;
+
+ depth = isl_schedule_node_get_schedule_depth(node);
+ if (depth < 0 || !group_id)
+ goto error;
+ if (check_insert(node) < 0)
+ goto error;
+
+ domain = isl_schedule_node_get_domain(node);
+ data.domain = isl_union_set_copy(domain);
+ data.domain_universe = isl_union_set_copy(domain);
+ data.domain_universe = isl_union_set_universe(data.domain_universe);
+
+ data.dim = depth;
+ if (data.dim == 0) {
+ isl_ctx *ctx;
+ isl_set *set;
+ isl_union_set *group;
+ isl_union_map *univ;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ space = isl_space_set_alloc(ctx, 0, 0);
+ space = isl_space_set_tuple_id(space, isl_dim_set, group_id);
+ set = isl_set_universe(isl_space_copy(space));
+ group = isl_union_set_from_set(set);
+ expansion = isl_union_map_from_domain_and_range(domain, group);
+ univ = isl_union_map_universe(isl_union_map_copy(expansion));
+ contraction = isl_union_pw_multi_aff_from_union_map(univ);
+ expansion = isl_union_map_reverse(expansion);
+ } else {
+ isl_multi_union_pw_aff *prefix;
+ isl_union_set *univ;
+
+ prefix =
+ isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
+ prefix = isl_multi_union_pw_aff_set_tuple_id(prefix,
+ isl_dim_set, group_id);
+ space = isl_multi_union_pw_aff_get_space(prefix);
+ contraction = isl_union_pw_multi_aff_from_multi_union_pw_aff(
+ prefix);
+ univ = isl_union_set_universe(isl_union_set_copy(domain));
+ contraction =
+ isl_union_pw_multi_aff_intersect_domain(contraction, univ);
+ expansion = isl_union_map_from_union_pw_multi_aff(
+ isl_union_pw_multi_aff_copy(contraction));
+ expansion = isl_union_map_reverse(expansion);
+ expansion = isl_union_map_intersect_range(expansion, domain);
+ }
+ space = isl_space_map_from_set(space);
+ data.sched = isl_multi_aff_identity(space);
+ data.group = isl_union_map_domain(isl_union_map_copy(expansion));
+ data.group = isl_union_set_coalesce(data.group);
+ data.group_universe = isl_union_set_copy(data.group);
+ data.group_universe = isl_union_set_universe(data.group_universe);
+ data.expansion = isl_union_map_copy(expansion);
+ data.contraction = isl_union_pw_multi_aff_copy(contraction);
+ node = isl_schedule_node_insert_expansion(node, contraction, expansion);
+
+ disjoint = isl_union_set_is_disjoint(data.domain_universe,
+ data.group_universe);
+
+ node = update_ancestors(node, &group_ancestor, &data);
+
+ isl_union_set_free(data.domain);
+ isl_union_set_free(data.domain_universe);
+ isl_union_set_free(data.group);
+ isl_union_set_free(data.group_universe);
+ isl_multi_aff_free(data.sched);
+ isl_union_map_free(data.expansion);
+ isl_union_pw_multi_aff_free(data.contraction);
+
+ node = isl_schedule_node_child(node, 0);
+
+ if (!node || disjoint < 0)
+ return isl_schedule_node_free(node);
+ if (!disjoint)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "group instances already reach node",
+ return isl_schedule_node_free(node));
+
+ return node;
+error:
+ isl_schedule_node_free(node);
+ isl_id_free(group_id);
+ return NULL;
+}
+
+/* Compute the gist of the given band node with respect to "context".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_band_gist(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *context)
+{
+ isl_schedule_tree *tree;
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_band_gist(tree, context);
+ return isl_schedule_node_graft_tree(node, tree);
+}
+
+/* Internal data structure for isl_schedule_node_gist.
+ * "n_expansion" is the number of outer expansion nodes
+ * with respect to the current position
+ * "filters" contains an element for each outer filter, expansion or
+ * extension node with respect to the current position, each representing
+ * the intersection of the previous element and the filter on the filter node
+ * or the expansion/extension of the previous element.
+ * The first element in the original context passed to isl_schedule_node_gist.
+ */
+struct isl_node_gist_data {
+ int n_expansion;
+ isl_union_set_list *filters;
+};
+
+/* Enter the expansion node "node" during a isl_schedule_node_gist traversal.
+ *
+ * In particular, add an extra element to data->filters containing
+ * the expansion of the previous element and replace the expansion
+ * and contraction on "node" by the gist with respect to these filters.
+ * Also keep track of the fact that we have entered another expansion.
+ */
+static __isl_give isl_schedule_node *gist_enter_expansion(
+ __isl_take isl_schedule_node *node, struct isl_node_gist_data *data)
+{
+ isl_size n;
+ isl_union_set *inner;
+ isl_union_map *expansion;
+ isl_union_pw_multi_aff *contraction;
+
+ data->n_expansion++;
+
+ n = isl_union_set_list_n_union_set(data->filters);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ inner = isl_union_set_list_get_union_set(data->filters, n - 1);
+ expansion = isl_schedule_node_expansion_get_expansion(node);
+ inner = isl_union_set_apply(inner, expansion);
+
+ contraction = isl_schedule_node_expansion_get_contraction(node);
+ contraction = isl_union_pw_multi_aff_gist(contraction,
+ isl_union_set_copy(inner));
+
+ data->filters = isl_union_set_list_add(data->filters, inner);
+
+ inner = isl_union_set_list_get_union_set(data->filters, n - 1);
+ expansion = isl_schedule_node_expansion_get_expansion(node);
+ expansion = isl_union_map_gist_domain(expansion, inner);
+ node = isl_schedule_node_expansion_set_contraction_and_expansion(node,
+ contraction, expansion);
+
+ return node;
+}
+
+/* Leave the expansion node "node" during a isl_schedule_node_gist traversal.
+ *
+ * In particular, remove the element in data->filters that was added by
+ * gist_enter_expansion and decrement the number of outer expansions.
+ *
+ * The expansion has already been simplified in gist_enter_expansion.
+ * If this simplification results in an identity expansion, then
+ * it is removed here.
+ */
+static __isl_give isl_schedule_node *gist_leave_expansion(
+ __isl_take isl_schedule_node *node, struct isl_node_gist_data *data)
+{
+ isl_size n;
+ isl_bool identity;
+ isl_union_map *expansion;
+
+ expansion = isl_schedule_node_expansion_get_expansion(node);
+ identity = isl_union_map_is_identity(expansion);
+ isl_union_map_free(expansion);
+
+ if (identity < 0)
+ node = isl_schedule_node_free(node);
+ else if (identity)
+ node = isl_schedule_node_delete(node);
+
+ n = isl_union_set_list_n_union_set(data->filters);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ data->filters = isl_union_set_list_drop(data->filters, n - 1, 1);
+
+ data->n_expansion--;
+
+ return node;
+}
+
+/* Enter the extension node "node" during a isl_schedule_node_gist traversal.
+ *
+ * In particular, add an extra element to data->filters containing
+ * the union of the previous element with the additional domain elements
+ * introduced by the extension.
+ */
+static __isl_give isl_schedule_node *gist_enter_extension(
+ __isl_take isl_schedule_node *node, struct isl_node_gist_data *data)
+{
+ isl_size n;
+ isl_union_set *inner, *extra;
+ isl_union_map *extension;
+
+ n = isl_union_set_list_n_union_set(data->filters);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ inner = isl_union_set_list_get_union_set(data->filters, n - 1);
+ extension = isl_schedule_node_extension_get_extension(node);
+ extra = isl_union_map_range(extension);
+ inner = isl_union_set_union(inner, extra);
+
+ data->filters = isl_union_set_list_add(data->filters, inner);
+
+ return node;
+}
+
+/* Can we finish gisting at this node?
+ * That is, is the filter on the current filter node a subset of
+ * the original context passed to isl_schedule_node_gist?
+ * If we have gone through any expansions, then we cannot perform
+ * this test since the current domain elements are incomparable
+ * to the domain elements in the original context.
+ */
+static isl_bool gist_done(__isl_keep isl_schedule_node *node,
+ struct isl_node_gist_data *data)
+{
+ isl_union_set *filter, *outer;
+ isl_bool subset;
+
+ if (data->n_expansion != 0)
+ return isl_bool_false;
+
+ filter = isl_schedule_node_filter_get_filter(node);
+ outer = isl_union_set_list_get_union_set(data->filters, 0);
+ subset = isl_union_set_is_subset(filter, outer);
+ isl_union_set_free(outer);
+ isl_union_set_free(filter);
+
+ return subset;
+}
+
+/* Callback for "traverse" to enter a node and to move
+ * to the deepest initial subtree that should be traversed
+ * by isl_schedule_node_gist.
+ *
+ * The "filters" list is extended by one element each time
+ * we come across a filter node by the result of intersecting
+ * the last element in the list with the filter on the filter node.
+ *
+ * If the filter on the current filter node is a subset of
+ * the original context passed to isl_schedule_node_gist,
+ * then there is no need to go into its subtree since it cannot
+ * be further simplified by the context. The "filters" list is
+ * still extended for consistency, but the actual value of the
+ * added element is immaterial since it will not be used.
+ *
+ * Otherwise, the filter on the current filter node is replaced by
+ * the gist of the original filter with respect to the intersection
+ * of the original context with the intermediate filters.
+ *
+ * If the new element in the "filters" list is empty, then no elements
+ * can reach the descendants of the current filter node. The subtree
+ * underneath the filter node is therefore removed.
+ *
+ * Each expansion node we come across is handled by
+ * gist_enter_expansion.
+ *
+ * Each extension node we come across is handled by
+ * gist_enter_extension.
+ */
+static __isl_give isl_schedule_node *gist_enter(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ struct isl_node_gist_data *data = user;
+
+ do {
+ isl_union_set *filter, *inner;
+ isl_bool done, empty;
+ isl_size n;
+
+ switch (isl_schedule_node_get_type(node)) {
+ case isl_schedule_node_error:
+ return isl_schedule_node_free(node);
+ case isl_schedule_node_expansion:
+ node = gist_enter_expansion(node, data);
+ continue;
+ case isl_schedule_node_extension:
+ node = gist_enter_extension(node, data);
+ continue;
+ case isl_schedule_node_band:
+ case isl_schedule_node_context:
+ case isl_schedule_node_domain:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ continue;
+ case isl_schedule_node_filter:
+ break;
+ }
+ done = gist_done(node, data);
+ filter = isl_schedule_node_filter_get_filter(node);
+ n = isl_union_set_list_n_union_set(data->filters);
+ if (n < 0 || done < 0 || done) {
+ data->filters = isl_union_set_list_add(data->filters,
+ filter);
+ if (n < 0 || done < 0)
+ return isl_schedule_node_free(node);
+ return node;
+ }
+ inner = isl_union_set_list_get_union_set(data->filters, n - 1);
+ filter = isl_union_set_gist(filter, isl_union_set_copy(inner));
+ node = isl_schedule_node_filter_set_filter(node,
+ isl_union_set_copy(filter));
+ filter = isl_union_set_intersect(filter, inner);
+ empty = isl_union_set_is_empty(filter);
+ data->filters = isl_union_set_list_add(data->filters, filter);
+ if (empty < 0)
+ return isl_schedule_node_free(node);
+ if (!empty)
+ continue;
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_cut(node);
+ node = isl_schedule_node_parent(node);
+ return node;
+ } while (isl_schedule_node_has_children(node) &&
+ (node = isl_schedule_node_first_child(node)) != NULL);
+
+ return node;
+}
+
+/* Callback for "traverse" to leave a node for isl_schedule_node_gist.
+ *
+ * In particular, if the current node is a filter node, then we remove
+ * the element on the "filters" list that was added when we entered
+ * the node. There is no need to compute any gist here, since we
+ * already did that when we entered the node.
+ *
+ * Expansion nodes are handled by gist_leave_expansion.
+ *
+ * If the current node is an extension, then remove the element
+ * in data->filters that was added by gist_enter_extension.
+ *
+ * If the current node is a band node, then we compute the gist of
+ * the band node with respect to the intersection of the original context
+ * and the intermediate filters.
+ *
+ * If the current node is a sequence or set node, then some of
+ * the filter children may have become empty and so they are removed.
+ * If only one child is left, then the set or sequence node along with
+ * the single remaining child filter is removed. The filter can be
+ * removed because the filters on a sequence or set node are supposed
+ * to partition the incoming domain instances.
+ * In principle, it should then be impossible for there to be zero
+ * remaining children, but should this happen, we replace the entire
+ * subtree with an empty filter.
+ */
+static __isl_give isl_schedule_node *gist_leave(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ struct isl_node_gist_data *data = user;
+ isl_schedule_tree *tree;
+ int i;
+ isl_size n;
+ isl_union_set *filter;
+
+ switch (isl_schedule_node_get_type(node)) {
+ case isl_schedule_node_error:
+ return isl_schedule_node_free(node);
+ case isl_schedule_node_expansion:
+ node = gist_leave_expansion(node, data);
+ break;
+ case isl_schedule_node_extension:
+ case isl_schedule_node_filter:
+ n = isl_union_set_list_n_union_set(data->filters);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ data->filters = isl_union_set_list_drop(data->filters,
+ n - 1, 1);
+ break;
+ case isl_schedule_node_band:
+ n = isl_union_set_list_n_union_set(data->filters);
+ if (n < 0)
+ return isl_schedule_node_free(node);
+ filter = isl_union_set_list_get_union_set(data->filters, n - 1);
+ node = isl_schedule_node_band_gist(node, filter);
+ break;
+ case isl_schedule_node_set:
+ case isl_schedule_node_sequence:
+ tree = isl_schedule_node_get_tree(node);
+ n = isl_schedule_tree_n_children(tree);
+ if (n < 0)
+ tree = isl_schedule_tree_free(tree);
+ for (i = n - 1; i >= 0; --i) {
+ isl_schedule_tree *child;
+ isl_union_set *filter;
+ isl_bool empty;
+
+ child = isl_schedule_tree_get_child(tree, i);
+ filter = isl_schedule_tree_filter_get_filter(child);
+ empty = isl_union_set_is_empty(filter);
+ isl_union_set_free(filter);
+ isl_schedule_tree_free(child);
+ if (empty < 0)
+ tree = isl_schedule_tree_free(tree);
+ else if (empty)
+ tree = isl_schedule_tree_drop_child(tree, i);
+ }
+ n = isl_schedule_tree_n_children(tree);
+ if (n < 0)
+ tree = isl_schedule_tree_free(tree);
+ node = isl_schedule_node_graft_tree(node, tree);
+ if (n == 1) {
+ node = isl_schedule_node_delete(node);
+ node = isl_schedule_node_delete(node);
+ } else if (n == 0) {
+ isl_space *space;
+
+ filter =
+ isl_union_set_list_get_union_set(data->filters, 0);
+ space = isl_union_set_get_space(filter);
+ isl_union_set_free(filter);
+ filter = isl_union_set_empty(space);
+ node = isl_schedule_node_cut(node);
+ node = isl_schedule_node_insert_filter(node, filter);
+ }
+ break;
+ case isl_schedule_node_context:
+ case isl_schedule_node_domain:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_mark:
+ break;
+ }
+
+ return node;
+}
+
+/* Compute the gist of the subtree at "node" with respect to
+ * the reaching domain elements in "context".
+ * In particular, compute the gist of all band and filter nodes
+ * in the subtree with respect to "context". Children of set or sequence
+ * nodes that end up with an empty filter are removed completely.
+ *
+ * We keep track of the intersection of "context" with all outer filters
+ * of the current node within the subtree in the final element of "filters".
+ * Initially, this list contains the single element "context" and it is
+ * extended or shortened each time we enter or leave a filter node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_gist(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *context)
+{
+ struct isl_node_gist_data data;
+
+ data.n_expansion = 0;
+ data.filters = isl_union_set_list_from_union_set(context);
+ node = traverse(node, &gist_enter, &gist_leave, &data);
+ isl_union_set_list_free(data.filters);
+ return node;
+}
+
+/* Intersect the domain of domain node "node" with "domain".
+ *
+ * If the domain of "node" is already a subset of "domain",
+ * then nothing needs to be changed.
+ *
+ * Otherwise, we replace the domain of the domain node by the intersection
+ * and simplify the subtree rooted at "node" with respect to this intersection.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_domain_intersect_domain(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *domain)
+{
+ isl_schedule_tree *tree;
+ isl_union_set *uset;
+ int is_subset;
+
+ if (!node || !domain)
+ goto error;
+
+ uset = isl_schedule_tree_domain_get_domain(node->tree);
+ is_subset = isl_union_set_is_subset(uset, domain);
+ isl_union_set_free(uset);
+ if (is_subset < 0)
+ goto error;
+ if (is_subset) {
+ isl_union_set_free(domain);
+ return node;
+ }
+
+ tree = isl_schedule_tree_copy(node->tree);
+ uset = isl_schedule_tree_domain_get_domain(tree);
+ uset = isl_union_set_intersect(uset, domain);
+ tree = isl_schedule_tree_domain_set_domain(tree,
+ isl_union_set_copy(uset));
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_gist(node, uset);
+ node = isl_schedule_node_parent(node);
+
+ return node;
+error:
+ isl_schedule_node_free(node);
+ isl_union_set_free(domain);
+ return NULL;
+}
+
+/* Replace the domain of domain node "node" with the gist
+ * of the original domain with respect to the parameter domain "context".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_domain_gist_params(
+ __isl_take isl_schedule_node *node, __isl_take isl_set *context)
+{
+ isl_union_set *domain;
+ isl_schedule_tree *tree;
+
+ if (!node || !context)
+ goto error;
+
+ tree = isl_schedule_tree_copy(node->tree);
+ domain = isl_schedule_tree_domain_get_domain(node->tree);
+ domain = isl_union_set_gist_params(domain, context);
+ tree = isl_schedule_tree_domain_set_domain(tree, domain);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+error:
+ isl_schedule_node_free(node);
+ isl_set_free(context);
+ return NULL;
+}
+
+/* Internal data structure for isl_schedule_node_get_subtree_expansion.
+ * "expansions" contains a list of accumulated expansions
+ * for each outer expansion, set or sequence node. The first element
+ * in the list is an identity mapping on the reaching domain elements.
+ * "res" collects the results.
+ */
+struct isl_subtree_expansion_data {
+ isl_union_map_list *expansions;
+ isl_union_map *res;
+};
+
+/* Callback for "traverse" to enter a node and to move
+ * to the deepest initial subtree that should be traversed
+ * by isl_schedule_node_get_subtree_expansion.
+ *
+ * Whenever we come across an expansion node, the last element
+ * of data->expansions is combined with the expansion
+ * on the expansion node.
+ *
+ * Whenever we come across a filter node that is the child
+ * of a set or sequence node, data->expansions is extended
+ * with a new element that restricts the previous element
+ * to the elements selected by the filter.
+ * The previous element can then be reused while backtracking.
+ */
+static __isl_give isl_schedule_node *subtree_expansion_enter(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ struct isl_subtree_expansion_data *data = user;
+
+ do {
+ enum isl_schedule_node_type type;
+ isl_union_set *filter;
+ isl_union_map *inner, *expansion;
+ isl_size n;
+
+ switch (isl_schedule_node_get_type(node)) {
+ case isl_schedule_node_error:
+ return isl_schedule_node_free(node);
+ case isl_schedule_node_filter:
+ type = isl_schedule_node_get_parent_type(node);
+ if (type != isl_schedule_node_set &&
+ type != isl_schedule_node_sequence)
+ break;
+ filter = isl_schedule_node_filter_get_filter(node);
+ n = isl_union_map_list_n_union_map(data->expansions);
+ if (n < 0)
+ data->expansions =
+ isl_union_map_list_free(data->expansions);
+ inner =
+ isl_union_map_list_get_union_map(data->expansions,
+ n - 1);
+ inner = isl_union_map_intersect_range(inner, filter);
+ data->expansions =
+ isl_union_map_list_add(data->expansions, inner);
+ break;
+ case isl_schedule_node_expansion:
+ n = isl_union_map_list_n_union_map(data->expansions);
+ if (n < 0)
+ data->expansions =
+ isl_union_map_list_free(data->expansions);
+ expansion =
+ isl_schedule_node_expansion_get_expansion(node);
+ inner =
+ isl_union_map_list_get_union_map(data->expansions,
+ n - 1);
+ inner = isl_union_map_apply_range(inner, expansion);
+ data->expansions =
+ isl_union_map_list_set_union_map(data->expansions,
+ n - 1, inner);
+ break;
+ case isl_schedule_node_band:
+ case isl_schedule_node_context:
+ case isl_schedule_node_domain:
+ case isl_schedule_node_extension:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ break;
+ }
+ } while (isl_schedule_node_has_children(node) &&
+ (node = isl_schedule_node_first_child(node)) != NULL);
+
+ return node;
+}
+
+/* Callback for "traverse" to leave a node for
+ * isl_schedule_node_get_subtree_expansion.
+ *
+ * If we come across a filter node that is the child
+ * of a set or sequence node, then we remove the element
+ * of data->expansions that was added in subtree_expansion_enter.
+ *
+ * If we reach a leaf node, then the accumulated expansion is
+ * added to data->res.
+ */
+static __isl_give isl_schedule_node *subtree_expansion_leave(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ struct isl_subtree_expansion_data *data = user;
+ isl_size n;
+ isl_union_map *inner;
+ enum isl_schedule_node_type type;
+
+ switch (isl_schedule_node_get_type(node)) {
+ case isl_schedule_node_error:
+ return isl_schedule_node_free(node);
+ case isl_schedule_node_filter:
+ type = isl_schedule_node_get_parent_type(node);
+ if (type != isl_schedule_node_set &&
+ type != isl_schedule_node_sequence)
+ break;
+ n = isl_union_map_list_n_union_map(data->expansions);
+ if (n < 0)
+ data->expansions =
+ isl_union_map_list_free(data->expansions);
+ data->expansions = isl_union_map_list_drop(data->expansions,
+ n - 1, 1);
+ break;
+ case isl_schedule_node_leaf:
+ n = isl_union_map_list_n_union_map(data->expansions);
+ if (n < 0)
+ data->expansions =
+ isl_union_map_list_free(data->expansions);
+ inner = isl_union_map_list_get_union_map(data->expansions,
+ n - 1);
+ data->res = isl_union_map_union(data->res, inner);
+ break;
+ case isl_schedule_node_band:
+ case isl_schedule_node_context:
+ case isl_schedule_node_domain:
+ case isl_schedule_node_expansion:
+ case isl_schedule_node_extension:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ break;
+ }
+
+ return node;
+}
+
+/* Return a mapping from the domain elements that reach "node"
+ * to the corresponding domain elements in the leaves of the subtree
+ * rooted at "node" obtained by composing the intermediate expansions.
+ *
+ * We start out with an identity mapping between the domain elements
+ * that reach "node" and compose it with all the expansions
+ * on a path from "node" to a leaf while traversing the subtree.
+ * Within the children of an a sequence or set node, the
+ * accumulated expansion is restricted to the elements selected
+ * by the filter child.
+ */
+__isl_give isl_union_map *isl_schedule_node_get_subtree_expansion(
+ __isl_keep isl_schedule_node *node)
+{
+ struct isl_subtree_expansion_data data;
+ isl_space *space;
+ isl_union_set *domain;
+ isl_union_map *expansion;
+
+ if (!node)
+ return NULL;
+
+ domain = isl_schedule_node_get_universe_domain(node);
+ space = isl_union_set_get_space(domain);
+ expansion = isl_union_set_identity(domain);
+ data.res = isl_union_map_empty(space);
+ data.expansions = isl_union_map_list_from_union_map(expansion);
+
+ node = isl_schedule_node_copy(node);
+ node = traverse(node, &subtree_expansion_enter,
+ &subtree_expansion_leave, &data);
+ if (!node)
+ data.res = isl_union_map_free(data.res);
+ isl_schedule_node_free(node);
+
+ isl_union_map_list_free(data.expansions);
+
+ return data.res;
+}
+
+/* Internal data structure for isl_schedule_node_get_subtree_contraction.
+ * "contractions" contains a list of accumulated contractions
+ * for each outer expansion, set or sequence node. The first element
+ * in the list is an identity mapping on the reaching domain elements.
+ * "res" collects the results.
+ */
+struct isl_subtree_contraction_data {
+ isl_union_pw_multi_aff_list *contractions;
+ isl_union_pw_multi_aff *res;
+};
+
+/* Callback for "traverse" to enter a node and to move
+ * to the deepest initial subtree that should be traversed
+ * by isl_schedule_node_get_subtree_contraction.
+ *
+ * Whenever we come across an expansion node, the last element
+ * of data->contractions is combined with the contraction
+ * on the expansion node.
+ *
+ * Whenever we come across a filter node that is the child
+ * of a set or sequence node, data->contractions is extended
+ * with a new element that restricts the previous element
+ * to the elements selected by the filter.
+ * The previous element can then be reused while backtracking.
+ */
+static __isl_give isl_schedule_node *subtree_contraction_enter(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ struct isl_subtree_contraction_data *data = user;
+
+ do {
+ enum isl_schedule_node_type type;
+ isl_union_set *filter;
+ isl_union_pw_multi_aff *inner, *contraction;
+ isl_size n;
+
+ switch (isl_schedule_node_get_type(node)) {
+ case isl_schedule_node_error:
+ return isl_schedule_node_free(node);
+ case isl_schedule_node_filter:
+ type = isl_schedule_node_get_parent_type(node);
+ if (type != isl_schedule_node_set &&
+ type != isl_schedule_node_sequence)
+ break;
+ filter = isl_schedule_node_filter_get_filter(node);
+ n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
+ data->contractions);
+ if (n < 0)
+ data->contractions =
+ isl_union_pw_multi_aff_list_free(
+ data->contractions);
+ inner =
+ isl_union_pw_multi_aff_list_get_union_pw_multi_aff(
+ data->contractions, n - 1);
+ inner = isl_union_pw_multi_aff_intersect_domain(inner,
+ filter);
+ data->contractions =
+ isl_union_pw_multi_aff_list_add(data->contractions,
+ inner);
+ break;
+ case isl_schedule_node_expansion:
+ n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
+ data->contractions);
+ if (n < 0)
+ data->contractions =
+ isl_union_pw_multi_aff_list_free(
+ data->contractions);
+ contraction =
+ isl_schedule_node_expansion_get_contraction(node);
+ inner =
+ isl_union_pw_multi_aff_list_get_union_pw_multi_aff(
+ data->contractions, n - 1);
+ inner =
+ isl_union_pw_multi_aff_pullback_union_pw_multi_aff(
+ inner, contraction);
+ data->contractions =
+ isl_union_pw_multi_aff_list_set_union_pw_multi_aff(
+ data->contractions, n - 1, inner);
+ break;
+ case isl_schedule_node_band:
+ case isl_schedule_node_context:
+ case isl_schedule_node_domain:
+ case isl_schedule_node_extension:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ break;
+ }
+ } while (isl_schedule_node_has_children(node) &&
+ (node = isl_schedule_node_first_child(node)) != NULL);
+
+ return node;
+}
+
+/* Callback for "traverse" to leave a node for
+ * isl_schedule_node_get_subtree_contraction.
+ *
+ * If we come across a filter node that is the child
+ * of a set or sequence node, then we remove the element
+ * of data->contractions that was added in subtree_contraction_enter.
+ *
+ * If we reach a leaf node, then the accumulated contraction is
+ * added to data->res.
+ */
+static __isl_give isl_schedule_node *subtree_contraction_leave(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ struct isl_subtree_contraction_data *data = user;
+ isl_size n;
+ isl_union_pw_multi_aff *inner;
+ enum isl_schedule_node_type type;
+
+ switch (isl_schedule_node_get_type(node)) {
+ case isl_schedule_node_error:
+ return isl_schedule_node_free(node);
+ case isl_schedule_node_filter:
+ type = isl_schedule_node_get_parent_type(node);
+ if (type != isl_schedule_node_set &&
+ type != isl_schedule_node_sequence)
+ break;
+ n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
+ data->contractions);
+ if (n < 0)
+ data->contractions = isl_union_pw_multi_aff_list_free(
+ data->contractions);
+ data->contractions =
+ isl_union_pw_multi_aff_list_drop(data->contractions,
+ n - 1, 1);
+ break;
+ case isl_schedule_node_leaf:
+ n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
+ data->contractions);
+ if (n < 0)
+ data->contractions = isl_union_pw_multi_aff_list_free(
+ data->contractions);
+ inner = isl_union_pw_multi_aff_list_get_union_pw_multi_aff(
+ data->contractions, n - 1);
+ data->res = isl_union_pw_multi_aff_union_add(data->res, inner);
+ break;
+ case isl_schedule_node_band:
+ case isl_schedule_node_context:
+ case isl_schedule_node_domain:
+ case isl_schedule_node_expansion:
+ case isl_schedule_node_extension:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ break;
+ }
+
+ return node;
+}
+
+/* Return a mapping from the domain elements in the leaves of the subtree
+ * rooted at "node" to the corresponding domain elements that reach "node"
+ * obtained by composing the intermediate contractions.
+ *
+ * We start out with an identity mapping between the domain elements
+ * that reach "node" and compose it with all the contractions
+ * on a path from "node" to a leaf while traversing the subtree.
+ * Within the children of an a sequence or set node, the
+ * accumulated contraction is restricted to the elements selected
+ * by the filter child.
+ */
+__isl_give isl_union_pw_multi_aff *isl_schedule_node_get_subtree_contraction(
+ __isl_keep isl_schedule_node *node)
+{
+ struct isl_subtree_contraction_data data;
+ isl_space *space;
+ isl_union_set *domain;
+ isl_union_pw_multi_aff *contraction;
+
+ if (!node)
+ return NULL;
+
+ domain = isl_schedule_node_get_universe_domain(node);
+ space = isl_union_set_get_space(domain);
+ contraction = isl_union_set_identity_union_pw_multi_aff(domain);
+ data.res = isl_union_pw_multi_aff_empty(space);
+ data.contractions =
+ isl_union_pw_multi_aff_list_from_union_pw_multi_aff(contraction);
+
+ node = isl_schedule_node_copy(node);
+ node = traverse(node, &subtree_contraction_enter,
+ &subtree_contraction_leave, &data);
+ if (!node)
+ data.res = isl_union_pw_multi_aff_free(data.res);
+ isl_schedule_node_free(node);
+
+ isl_union_pw_multi_aff_list_free(data.contractions);
+
+ return data.res;
+}
+
+/* Do the nearest "n" ancestors of "node" have the types given in "types"
+ * (starting at the parent of "node")?
+ */
+static isl_bool has_ancestors(__isl_keep isl_schedule_node *node,
+ int n, enum isl_schedule_node_type *types)
+{
+ int i;
+ isl_size n_ancestor;
+
+ if (!node)
+ return isl_bool_error;
+
+ n_ancestor = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n_ancestor < 0)
+ return isl_bool_error;
+ if (n_ancestor < n)
+ return isl_bool_false;
+
+ for (i = 0; i < n; ++i) {
+ isl_schedule_tree *tree;
+ int correct_type;
+
+ tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors,
+ n_ancestor - 1 - i);
+ if (!tree)
+ return isl_bool_error;
+ correct_type = isl_schedule_tree_get_type(tree) == types[i];
+ isl_schedule_tree_free(tree);
+ if (!correct_type)
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+/* Given a node "node" that appears in an extension (i.e., it is the child
+ * of a filter in a sequence inside an extension node), are the spaces
+ * of the extension specified by "extension" disjoint from those
+ * of both the original extension and the domain elements that reach
+ * that original extension?
+ */
+static int is_disjoint_extension(__isl_keep isl_schedule_node *node,
+ __isl_keep isl_union_map *extension)
+{
+ isl_union_map *old;
+ isl_union_set *domain;
+ int empty;
+
+ node = isl_schedule_node_copy(node);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_parent(node);
+ old = isl_schedule_node_extension_get_extension(node);
+ domain = isl_schedule_node_get_universe_domain(node);
+ isl_schedule_node_free(node);
+ old = isl_union_map_universe(old);
+ domain = isl_union_set_union(domain, isl_union_map_range(old));
+ extension = isl_union_map_copy(extension);
+ extension = isl_union_map_intersect_range(extension, domain);
+ empty = isl_union_map_is_empty(extension);
+ isl_union_map_free(extension);
+
+ return empty;
+}
+
+/* Given a node "node" that is governed by an extension node, extend
+ * that extension node with "extension".
+ *
+ * In particular, "node" is the child of a filter in a sequence that
+ * is in turn a child of an extension node. Extend that extension node
+ * with "extension".
+ *
+ * Return a pointer to the parent of the original node (i.e., a filter).
+ */
+static __isl_give isl_schedule_node *extend_extension(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_map *extension)
+{
+ isl_size pos;
+ isl_bool disjoint;
+ isl_union_map *node_extension;
+
+ node = isl_schedule_node_parent(node);
+ pos = isl_schedule_node_get_child_position(node);
+ if (pos < 0)
+ node = isl_schedule_node_free(node);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_parent(node);
+ node_extension = isl_schedule_node_extension_get_extension(node);
+ disjoint = isl_union_map_is_disjoint(extension, node_extension);
+ extension = isl_union_map_union(extension, node_extension);
+ node = isl_schedule_node_extension_set_extension(node, extension);
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_child(node, pos);
+
+ if (disjoint < 0)
+ return isl_schedule_node_free(node);
+ if (!node)
+ return NULL;
+ if (!disjoint)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "extension domain should be disjoint from earlier "
+ "extensions", return isl_schedule_node_free(node));
+
+ return node;
+}
+
+/* Return the universe of "uset" if this universe is disjoint from "ref".
+ * Otherwise, return "uset".
+ *
+ * Also check if "uset" itself is disjoint from "ref", reporting
+ * an error if it is not.
+ */
+static __isl_give isl_union_set *replace_by_universe_if_disjoint(
+ __isl_take isl_union_set *uset, __isl_keep isl_union_set *ref)
+{
+ int disjoint;
+ isl_union_set *universe;
+
+ disjoint = isl_union_set_is_disjoint(uset, ref);
+ if (disjoint < 0)
+ return isl_union_set_free(uset);
+ if (!disjoint)
+ isl_die(isl_union_set_get_ctx(uset), isl_error_invalid,
+ "extension domain should be disjoint from "
+ "current domain", return isl_union_set_free(uset));
+
+ universe = isl_union_set_universe(isl_union_set_copy(uset));
+ disjoint = isl_union_set_is_disjoint(universe, ref);
+ if (disjoint >= 0 && disjoint) {
+ isl_union_set_free(uset);
+ return universe;
+ }
+ isl_union_set_free(universe);
+
+ if (disjoint < 0)
+ return isl_union_set_free(uset);
+ return uset;
+}
+
+/* Insert an extension node on top of "node" with extension "extension".
+ * In addition, insert a filter that separates node from the extension
+ * between the extension node and "node".
+ * Return a pointer to the inserted filter node.
+ *
+ * If "node" already appears in an extension (i.e., if it is the child
+ * of a filter in a sequence inside an extension node), then extend that
+ * extension with "extension" instead.
+ * In this case, a pointer to the original filter node is returned.
+ * Note that if some of the elements in the new extension live in the
+ * same space as those of the original extension or the domain elements
+ * reaching the original extension, then we insert a new extension anyway.
+ * Otherwise, we would have to adjust the filters in the sequence child
+ * of the extension to ensure that the elements in the new extension
+ * are filtered out.
+ */
+static __isl_give isl_schedule_node *insert_extension(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_map *extension)
+{
+ enum isl_schedule_node_type ancestors[] =
+ { isl_schedule_node_filter, isl_schedule_node_sequence,
+ isl_schedule_node_extension };
+ isl_union_set *domain;
+ isl_union_set *filter;
+ isl_bool in_ext;
+
+ in_ext = has_ancestors(node, 3, ancestors);
+ if (in_ext < 0)
+ goto error;
+ if (in_ext) {
+ int disjoint;
+
+ disjoint = is_disjoint_extension(node, extension);
+ if (disjoint < 0)
+ goto error;
+ if (disjoint)
+ return extend_extension(node, extension);
+ }
+
+ filter = isl_schedule_node_get_domain(node);
+ domain = isl_union_map_range(isl_union_map_copy(extension));
+ filter = replace_by_universe_if_disjoint(filter, domain);
+ isl_union_set_free(domain);
+
+ node = isl_schedule_node_insert_filter(node, filter);
+ node = isl_schedule_node_insert_extension(node, extension);
+ node = isl_schedule_node_child(node, 0);
+ return node;
+error:
+ isl_schedule_node_free(node);
+ isl_union_map_free(extension);
+ return NULL;
+}
+
+/* Replace the subtree that "node" points to by "tree" (which has
+ * a sequence root with two children), except if the parent of "node"
+ * is a sequence as well, in which case "tree" is spliced at the position
+ * of "node" in its parent.
+ * Return a pointer to the child of the "tree_pos" (filter) child of "tree"
+ * in the updated schedule tree.
+ */
+static __isl_give isl_schedule_node *graft_or_splice(
+ __isl_take isl_schedule_node *node, __isl_take isl_schedule_tree *tree,
+ int tree_pos)
+{
+ isl_size pos;
+
+ if (isl_schedule_node_get_parent_type(node) ==
+ isl_schedule_node_sequence) {
+ pos = isl_schedule_node_get_child_position(node);
+ if (pos < 0)
+ node = isl_schedule_node_free(node);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_sequence_splice(node, pos, tree);
+ } else {
+ pos = 0;
+ node = isl_schedule_node_graft_tree(node, tree);
+ }
+ node = isl_schedule_node_child(node, pos + tree_pos);
+ node = isl_schedule_node_child(node, 0);
+
+ return node;
+}
+
+/* Insert a node "graft" into the schedule tree of "node" such that it
+ * is executed before (if "before" is set) or after (if "before" is not set)
+ * the node that "node" points to.
+ * The root of "graft" is an extension node.
+ * Return a pointer to the node that "node" pointed to.
+ *
+ * We first insert an extension node on top of "node" (or extend
+ * the extension node if there already is one), with a filter on "node"
+ * separating it from the extension.
+ * We then insert a filter in the graft to separate it from the original
+ * domain elements and combine the original and new tree in a sequence.
+ * If we have extended an extension node, then the children of this
+ * sequence are spliced in the sequence of the extended extension
+ * at the position where "node" appears in the original extension.
+ * Otherwise, the sequence pair is attached to the new extension node.
+ */
+static __isl_give isl_schedule_node *graft_extension(
+ __isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft,
+ int before)
+{
+ isl_union_map *extension;
+ isl_union_set *graft_domain;
+ isl_union_set *node_domain;
+ isl_schedule_tree *tree, *tree_graft;
+
+ extension = isl_schedule_node_extension_get_extension(graft);
+ graft_domain = isl_union_map_range(isl_union_map_copy(extension));
+ node_domain = isl_schedule_node_get_universe_domain(node);
+ node = insert_extension(node, extension);
+
+ graft_domain = replace_by_universe_if_disjoint(graft_domain,
+ node_domain);
+ isl_union_set_free(node_domain);
+
+ tree = isl_schedule_node_get_tree(node);
+ if (!isl_schedule_node_has_children(graft)) {
+ tree_graft = isl_schedule_tree_from_filter(graft_domain);
+ } else {
+ graft = isl_schedule_node_child(graft, 0);
+ tree_graft = isl_schedule_node_get_tree(graft);
+ tree_graft = isl_schedule_tree_insert_filter(tree_graft,
+ graft_domain);
+ }
+ if (before)
+ tree = isl_schedule_tree_sequence_pair(tree_graft, tree);
+ else
+ tree = isl_schedule_tree_sequence_pair(tree, tree_graft);
+ node = graft_or_splice(node, tree, before);
+
+ isl_schedule_node_free(graft);
+
+ return node;
+}
+
+/* Replace the root domain node of "node" by an extension node suitable
+ * for insertion at "pos".
+ * That is, create an extension node that maps the outer band nodes
+ * at "pos" to the domain of the root node of "node" and attach
+ * the child of this root node to the extension node.
+ */
+static __isl_give isl_schedule_node *extension_from_domain(
+ __isl_take isl_schedule_node *node, __isl_keep isl_schedule_node *pos)
+{
+ isl_union_set *universe;
+ isl_union_set *domain;
+ isl_union_map *ext;
+ isl_size depth;
+ isl_bool anchored;
+ isl_space *space;
+ isl_schedule_node *res;
+ isl_schedule_tree *tree;
+
+ depth = isl_schedule_node_get_schedule_depth(pos);
+ anchored = isl_schedule_node_is_subtree_anchored(node);
+ if (depth < 0 || anchored < 0)
+ return isl_schedule_node_free(node);
+ if (anchored)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported,
+ "cannot graft anchored tree with domain root",
+ return isl_schedule_node_free(node));
+
+ domain = isl_schedule_node_domain_get_domain(node);
+ space = isl_union_set_get_space(domain);
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, depth);
+ universe = isl_union_set_from_set(isl_set_universe(space));
+ ext = isl_union_map_from_domain_and_range(universe, domain);
+ res = isl_schedule_node_from_extension(ext);
+ node = isl_schedule_node_child(node, 0);
+ if (!node)
+ return isl_schedule_node_free(res);
+ if (!isl_schedule_tree_is_leaf(node->tree)) {
+ tree = isl_schedule_node_get_tree(node);
+ res = isl_schedule_node_child(res, 0);
+ res = isl_schedule_node_graft_tree(res, tree);
+ res = isl_schedule_node_parent(res);
+ }
+ isl_schedule_node_free(node);
+
+ return res;
+}
+
+/* Insert a node "graft" into the schedule tree of "node" such that it
+ * is executed before (if "before" is set) or after (if "before" is not set)
+ * the node that "node" points to.
+ * The root of "graft" may be either a domain or an extension node.
+ * In the latter case, the domain of the extension needs to correspond
+ * to the outer band nodes of "node".
+ * The elements of the domain or the range of the extension may not
+ * intersect with the domain elements that reach "node".
+ * The schedule tree of "graft" may not be anchored.
+ *
+ * The schedule tree of "node" is modified to include an extension node
+ * corresponding to the root node of "graft" as a child of the original
+ * parent of "node". The original node that "node" points to and the
+ * child of the root node of "graft" are attached to this extension node
+ * through a sequence, with appropriate filters and with the child
+ * of "graft" appearing before or after the original "node".
+ *
+ * If "node" already appears inside a sequence that is the child of
+ * an extension node and if the spaces of the new domain elements
+ * do not overlap with those of the original domain elements,
+ * then that extension node is extended with the new extension
+ * rather than introducing a new segment of extension and sequence nodes.
+ *
+ * Return a pointer to the same node in the modified tree that
+ * "node" pointed to in the original tree.
+ */
+static __isl_give isl_schedule_node *isl_schedule_node_graft_before_or_after(
+ __isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft,
+ int before)
+{
+ if (!node || !graft)
+ goto error;
+ if (check_insert(node) < 0)
+ goto error;
+
+ if (isl_schedule_node_get_type(graft) == isl_schedule_node_domain)
+ graft = extension_from_domain(graft, node);
+
+ if (!graft)
+ goto error;
+ if (isl_schedule_node_get_type(graft) != isl_schedule_node_extension)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "expecting domain or extension as root of graft",
+ goto error);
+
+ return graft_extension(node, graft, before);
+error:
+ isl_schedule_node_free(node);
+ isl_schedule_node_free(graft);
+ return NULL;
+}
+
+/* Insert a node "graft" into the schedule tree of "node" such that it
+ * is executed before the node that "node" points to.
+ * The root of "graft" may be either a domain or an extension node.
+ * In the latter case, the domain of the extension needs to correspond
+ * to the outer band nodes of "node".
+ * The elements of the domain or the range of the extension may not
+ * intersect with the domain elements that reach "node".
+ * The schedule tree of "graft" may not be anchored.
+ *
+ * Return a pointer to the same node in the modified tree that
+ * "node" pointed to in the original tree.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_graft_before(
+ __isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft)
+{
+ return isl_schedule_node_graft_before_or_after(node, graft, 1);
+}
+
+/* Insert a node "graft" into the schedule tree of "node" such that it
+ * is executed after the node that "node" points to.
+ * The root of "graft" may be either a domain or an extension node.
+ * In the latter case, the domain of the extension needs to correspond
+ * to the outer band nodes of "node".
+ * The elements of the domain or the range of the extension may not
+ * intersect with the domain elements that reach "node".
+ * The schedule tree of "graft" may not be anchored.
+ *
+ * Return a pointer to the same node in the modified tree that
+ * "node" pointed to in the original tree.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_graft_after(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_schedule_node *graft)
+{
+ return isl_schedule_node_graft_before_or_after(node, graft, 0);
+}
+
+/* Split the domain elements that reach "node" into those that satisfy
+ * "filter" and those that do not. Arrange for the first subset to be
+ * executed before or after the second subset, depending on the value
+ * of "before".
+ * Return a pointer to the tree corresponding to the second subset,
+ * except when this subset is empty in which case the original pointer
+ * is returned.
+ * If both subsets are non-empty, then a sequence node is introduced
+ * to impose the order. If the grandparent of the original node was
+ * itself a sequence, then the original child is replaced by two children
+ * in this sequence instead.
+ * The children in the sequence are copies of the original subtree,
+ * simplified with respect to their filters.
+ */
+static __isl_give isl_schedule_node *isl_schedule_node_order_before_or_after(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter,
+ int before)
+{
+ enum isl_schedule_node_type ancestors[] =
+ { isl_schedule_node_filter, isl_schedule_node_sequence };
+ isl_union_set *node_domain, *node_filter = NULL, *parent_filter;
+ isl_schedule_node *node2;
+ isl_schedule_tree *tree1, *tree2;
+ isl_bool empty1, empty2;
+ isl_bool in_seq;
+
+ if (!node || !filter)
+ goto error;
+ if (check_insert(node) < 0)
+ goto error;
+
+ in_seq = has_ancestors(node, 2, ancestors);
+ if (in_seq < 0)
+ goto error;
+ node_domain = isl_schedule_node_get_domain(node);
+ filter = isl_union_set_gist(filter, isl_union_set_copy(node_domain));
+ node_filter = isl_union_set_copy(node_domain);
+ node_filter = isl_union_set_subtract(node_filter,
+ isl_union_set_copy(filter));
+ node_filter = isl_union_set_gist(node_filter, node_domain);
+ empty1 = isl_union_set_is_empty(filter);
+ empty2 = isl_union_set_is_empty(node_filter);
+ if (empty1 < 0 || empty2 < 0)
+ goto error;
+ if (empty1 || empty2) {
+ isl_union_set_free(filter);
+ isl_union_set_free(node_filter);
+ return node;
+ }
+
+ if (in_seq) {
+ node = isl_schedule_node_parent(node);
+ parent_filter = isl_schedule_node_filter_get_filter(node);
+ node_filter = isl_union_set_intersect(node_filter,
+ isl_union_set_copy(parent_filter));
+ filter = isl_union_set_intersect(filter, parent_filter);
+ }
+
+ node2 = isl_schedule_node_copy(node);
+ node = isl_schedule_node_gist(node, isl_union_set_copy(node_filter));
+ node2 = isl_schedule_node_gist(node2, isl_union_set_copy(filter));
+ tree1 = isl_schedule_node_get_tree(node);
+ tree2 = isl_schedule_node_get_tree(node2);
+ tree1 = isl_schedule_tree_insert_filter(tree1, node_filter);
+ tree2 = isl_schedule_tree_insert_filter(tree2, filter);
+ isl_schedule_node_free(node2);
+
+ if (before) {
+ tree1 = isl_schedule_tree_sequence_pair(tree2, tree1);
+ node = graft_or_splice(node, tree1, 1);
+ } else {
+ tree1 = isl_schedule_tree_sequence_pair(tree1, tree2);
+ node = graft_or_splice(node, tree1, 0);
+ }
+
+ return node;
+error:
+ isl_schedule_node_free(node);
+ isl_union_set_free(filter);
+ isl_union_set_free(node_filter);
+ return NULL;
+}
+
+/* Split the domain elements that reach "node" into those that satisfy
+ * "filter" and those that do not. Arrange for the first subset to be
+ * executed before the second subset.
+ * Return a pointer to the tree corresponding to the second subset,
+ * except when this subset is empty in which case the original pointer
+ * is returned.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_order_before(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter)
+{
+ return isl_schedule_node_order_before_or_after(node, filter, 1);
+}
+
+/* Split the domain elements that reach "node" into those that satisfy
+ * "filter" and those that do not. Arrange for the first subset to be
+ * executed after the second subset.
+ * Return a pointer to the tree corresponding to the second subset,
+ * except when this subset is empty in which case the original pointer
+ * is returned.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_order_after(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *filter)
+{
+ return isl_schedule_node_order_before_or_after(node, filter, 0);
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * in the schedule node "node".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_reset_user(
+ __isl_take isl_schedule_node *node)
+{
+ isl_schedule_tree *tree;
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_reset_user(tree);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Align the parameters of the schedule node "node" to those of "space".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_align_params(
+ __isl_take isl_schedule_node *node, __isl_take isl_space *space)
+{
+ isl_schedule_tree *tree;
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_align_params(tree, space);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Compute the pullback of schedule node "node"
+ * by the function represented by "upma".
+ * In other words, plug in "upma" in the iteration domains
+ * of schedule node "node".
+ * We currently do not handle expansion nodes.
+ *
+ * Note that this is only a helper function for
+ * isl_schedule_pullback_union_pw_multi_aff. In order to maintain consistency,
+ * this function should not be called on a single node without also
+ * calling it on all the other nodes.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_pullback_union_pw_multi_aff(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ isl_schedule_tree *tree;
+
+ tree = isl_schedule_node_get_tree(node);
+ tree = isl_schedule_tree_pullback_union_pw_multi_aff(tree, upma);
+ node = isl_schedule_node_graft_tree(node, tree);
+
+ return node;
+}
+
+/* Internal data structure for isl_schedule_node_expand.
+ * "tree" is the tree that needs to be plugged in in all the leaves.
+ * "domain" is the set of domain elements in the original leaves
+ * to which the tree applies.
+ */
+struct isl_schedule_expand_data {
+ isl_schedule_tree *tree;
+ isl_union_set *domain;
+};
+
+/* If "node" is a leaf, then plug in data->tree, simplifying it
+ * within its new context.
+ *
+ * If there are any domain elements at the leaf where the tree
+ * should not be plugged in (i.e., there are elements not in data->domain)
+ * then first extend the tree to only apply to the elements in data->domain
+ * by constructing a set node that selects data->tree for elements
+ * in data->domain and a leaf for the other elements.
+ */
+static __isl_give isl_schedule_node *expand(__isl_take isl_schedule_node *node,
+ void *user)
+{
+ struct isl_schedule_expand_data *data = user;
+ isl_schedule_tree *tree, *leaf;
+ isl_union_set *domain, *left;
+ isl_bool empty;
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_leaf)
+ return node;
+
+ domain = isl_schedule_node_get_domain(node);
+ tree = isl_schedule_tree_copy(data->tree);
+
+ left = isl_union_set_copy(domain);
+ left = isl_union_set_subtract(left, isl_union_set_copy(data->domain));
+ empty = isl_union_set_is_empty(left);
+ if (empty >= 0 && !empty) {
+ leaf = isl_schedule_node_get_leaf(node);
+ leaf = isl_schedule_tree_insert_filter(leaf, left);
+ left = isl_union_set_copy(data->domain);
+ tree = isl_schedule_tree_insert_filter(tree, left);
+ tree = isl_schedule_tree_set_pair(tree, leaf);
+ } else {
+ if (empty < 0)
+ node = isl_schedule_node_free(node);
+ isl_union_set_free(left);
+ }
+
+ node = isl_schedule_node_graft_tree(node, tree);
+ node = isl_schedule_node_gist(node, domain);
+
+ return node;
+}
+
+/* Expand the tree rooted at "node" by extending all leaves
+ * with an expansion node with as child "tree".
+ * The expansion is determined by "contraction" and "domain".
+ * That is, the elements of "domain" are contracted according
+ * to "contraction". The expansion relation is then the inverse
+ * of "contraction" with its range intersected with "domain".
+ *
+ * Insert the appropriate expansion node on top of "tree" and
+ * then plug in the result in all leaves of "node".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_expand(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_union_set *domain,
+ __isl_take isl_schedule_tree *tree)
+{
+ struct isl_schedule_expand_data data;
+ isl_union_map *expansion;
+ isl_union_pw_multi_aff *copy;
+
+ if (!node || !contraction || !tree)
+ node = isl_schedule_node_free(node);
+
+ copy = isl_union_pw_multi_aff_copy(contraction);
+ expansion = isl_union_map_from_union_pw_multi_aff(copy);
+ expansion = isl_union_map_reverse(expansion);
+ expansion = isl_union_map_intersect_range(expansion, domain);
+ data.domain = isl_union_map_domain(isl_union_map_copy(expansion));
+
+ tree = isl_schedule_tree_insert_expansion(tree, contraction, expansion);
+ data.tree = tree;
+
+ node = isl_schedule_node_map_descendant_bottom_up(node, &expand, &data);
+ isl_union_set_free(data.domain);
+ isl_schedule_tree_free(data.tree);
+ return node;
+}
+
+/* Return the position of the subtree containing "node" among the children
+ * of "ancestor". "node" is assumed to be a descendant of "ancestor".
+ * In particular, both nodes should point to the same schedule tree.
+ *
+ * Return isl_size_error on error.
+ */
+isl_size isl_schedule_node_get_ancestor_child_position(
+ __isl_keep isl_schedule_node *node,
+ __isl_keep isl_schedule_node *ancestor)
+{
+ isl_size n1, n2;
+ isl_schedule_tree *tree;
+
+ n1 = isl_schedule_node_get_tree_depth(ancestor);
+ n2 = isl_schedule_node_get_tree_depth(node);
+ if (n1 < 0 || n2 < 0)
+ return isl_size_error;
+
+ if (node->schedule != ancestor->schedule)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "not a descendant", return isl_size_error);
+
+ if (n1 >= n2)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "not a descendant", return isl_size_error);
+ tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors, n1);
+ isl_schedule_tree_free(tree);
+ if (tree != ancestor->tree)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "not a descendant", return isl_size_error);
+
+ return node->child_pos[n1];
+}
+
+/* Given two nodes that point to the same schedule tree, return their
+ * closest shared ancestor.
+ *
+ * Since the two nodes point to the same schedule, they share at least
+ * one ancestor, the root of the schedule. We move down from the root
+ * to the first ancestor where the respective children have a different
+ * child position. This is the requested ancestor.
+ * If there is no ancestor where the children have a different position,
+ * then one node is an ancestor of the other and then this node is
+ * the requested ancestor.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_get_shared_ancestor(
+ __isl_keep isl_schedule_node *node1,
+ __isl_keep isl_schedule_node *node2)
+{
+ int i;
+ isl_size n1, n2;
+
+ n1 = isl_schedule_node_get_tree_depth(node1);
+ n2 = isl_schedule_node_get_tree_depth(node2);
+ if (n1 < 0 || n2 < 0)
+ return NULL;
+ if (node1->schedule != node2->schedule)
+ isl_die(isl_schedule_node_get_ctx(node1), isl_error_invalid,
+ "not part of same schedule", return NULL);
+ if (n2 < n1)
+ return isl_schedule_node_get_shared_ancestor(node2, node1);
+ if (n1 == 0)
+ return isl_schedule_node_copy(node1);
+ if (isl_schedule_node_is_equal(node1, node2))
+ return isl_schedule_node_copy(node1);
+
+ for (i = 0; i < n1; ++i)
+ if (node1->child_pos[i] != node2->child_pos[i])
+ break;
+
+ node1 = isl_schedule_node_copy(node1);
+ return isl_schedule_node_ancestor(node1, n1 - i);
+}
+
+/* Print "node" to "p".
+ */
+__isl_give isl_printer *isl_printer_print_schedule_node(
+ __isl_take isl_printer *p, __isl_keep isl_schedule_node *node)
+{
+ isl_size n;
+
+ if (!node)
+ return isl_printer_free(p);
+ n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+ if (n < 0)
+ return isl_printer_free(p);
+ return isl_printer_print_schedule_tree_mark(p, node->schedule->root, n,
+ node->child_pos);
+}
+
+void isl_schedule_node_dump(__isl_keep isl_schedule_node *node)
+{
+ isl_ctx *ctx;
+ isl_printer *printer;
+
+ if (!node)
+ return;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ printer = isl_printer_to_file(ctx, stderr);
+ printer = isl_printer_set_yaml_style(printer, ISL_YAML_STYLE_BLOCK);
+ printer = isl_printer_print_schedule_node(printer, node);
+
+ isl_printer_free(printer);
+}
+
+/* Return a string representation of "node".
+ * Print the schedule node in block format as it would otherwise
+ * look identical to the entire schedule.
+ */
+__isl_give char *isl_schedule_node_to_str(__isl_keep isl_schedule_node *node)
+{
+ isl_printer *printer;
+ char *s;
+
+ if (!node)
+ return NULL;
+
+ printer = isl_printer_to_str(isl_schedule_node_get_ctx(node));
+ printer = isl_printer_set_yaml_style(printer, ISL_YAML_STYLE_BLOCK);
+ printer = isl_printer_print_schedule_node(printer, node);
+ s = isl_printer_get_str(printer);
+ isl_printer_free(printer);
+
+ return s;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_node_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_node_private.h
new file mode 100644
index 00000000000..c4ad6ef61cb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_node_private.h
@@ -0,0 +1,68 @@
+#ifndef ISL_SCHEDLUE_NODE_PRIVATE_H
+#define ISL_SCHEDLUE_NODE_PRIVATE_H
+
+#include <isl/schedule_node.h>
+#include <isl_schedule_band.h>
+#include <isl_schedule_tree.h>
+
+/* An isl_schedule_node points to a particular location in a schedule tree.
+ *
+ * "schedule" is the schedule that the node is pointing to.
+ * "ancestors" is a list of the n ancestors of the node
+ * that is being pointed to.
+ * The first ancestor is the root of "schedule", while the last ancestor
+ * is the parent of the specified location.
+ * "child_pos" is an array of child positions of the same length as "ancestors",
+ * where ancestor i (i > 0) appears in child_pos[i - 1] of ancestor i - 1 and
+ * "tree" appears in child_pos[n - 1] of ancestor n - 1.
+ * "tree" is the subtree at the specified location.
+ *
+ * Note that the same isl_schedule_tree object may appear several times
+ * in a schedule tree and therefore does not uniquely identify a position
+ * in the schedule tree.
+ */
+struct isl_schedule_node {
+ int ref;
+
+ isl_schedule *schedule;
+ isl_schedule_tree_list *ancestors;
+ int *child_pos;
+ isl_schedule_tree *tree;
+};
+
+__isl_give isl_schedule_node *isl_schedule_node_alloc(
+ __isl_take isl_schedule *schedule, __isl_take isl_schedule_tree *tree,
+ __isl_take isl_schedule_tree_list *ancestors, int *child_pos);
+__isl_give isl_schedule_node *isl_schedule_node_graft_tree(
+ __isl_take isl_schedule_node *pos, __isl_take isl_schedule_tree *tree);
+
+__isl_give isl_schedule_tree *isl_schedule_node_get_tree(
+ __isl_keep isl_schedule_node *node);
+
+__isl_give isl_schedule_node *isl_schedule_node_pullback_union_pw_multi_aff(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_give isl_schedule_node *isl_schedule_node_expand(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_union_set *domain,
+ __isl_take isl_schedule_tree *tree);
+
+__isl_give isl_schedule_node *isl_schedule_node_gist(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *context);
+
+__isl_give isl_schedule_node *isl_schedule_node_domain_intersect_domain(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *domain);
+__isl_give isl_schedule_node *isl_schedule_node_domain_gist_params(
+ __isl_take isl_schedule_node *node, __isl_take isl_set *context);
+
+__isl_give isl_schedule_node *isl_schedule_node_insert_expansion(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_union_map *expansion);
+__isl_give isl_schedule_node *isl_schedule_node_insert_extension(
+ __isl_take isl_schedule_node *node,
+ __isl_take isl_union_map *extension);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_private.h
new file mode 100644
index 00000000000..dcf15de2d9e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_private.h
@@ -0,0 +1,36 @@
+#ifndef ISL_SCHEDLUE_PRIVATE_H
+#define ISL_SCHEDLUE_PRIVATE_H
+
+#include <isl/aff.h>
+#include <isl/schedule.h>
+#include <isl_schedule_tree.h>
+
+/* A complete schedule tree.
+ *
+ * "root" is the root of the schedule tree.
+ *
+ * "leaf" may be used to represent a leaf of the schedule.
+ * It should not appear as a child to any other isl_schedule_tree objects,
+ * but an isl_schedule_node may have "leaf" as its tree if it refers to
+ * a leaf of this schedule tree.
+ */
+struct isl_schedule {
+ int ref;
+
+ isl_schedule_tree *root;
+
+ struct isl_schedule_tree *leaf;
+};
+
+__isl_give isl_schedule *isl_schedule_from_schedule_tree(isl_ctx *ctx,
+ __isl_take isl_schedule_tree *tree);
+__isl_give isl_schedule *isl_schedule_set_root(
+ __isl_take isl_schedule *schedule, __isl_take isl_schedule_tree *tree);
+__isl_give isl_space *isl_schedule_get_space(
+ __isl_keep isl_schedule *schedule);
+__isl_give isl_union_set *isl_schedule_get_domain(
+ __isl_keep isl_schedule *schedule);
+__isl_keep isl_schedule_tree *isl_schedule_peek_leaf(
+ __isl_keep isl_schedule *schedule);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_read.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_read.c
new file mode 100644
index 00000000000..71fdeed50d7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_read.c
@@ -0,0 +1,779 @@
+#include <isl/id.h>
+#include <isl/val.h>
+#include <isl/schedule.h>
+#include <isl/stream.h>
+#include <isl_schedule_private.h>
+#include <isl_schedule_tree.h>
+
+/* An enumeration of the various keys that may appear in a YAML mapping
+ * of a schedule.
+ */
+enum isl_schedule_key {
+ isl_schedule_key_error = -1,
+ isl_schedule_key_child,
+ isl_schedule_key_coincident,
+ isl_schedule_key_context,
+ isl_schedule_key_contraction,
+ isl_schedule_key_domain,
+ isl_schedule_key_expansion,
+ isl_schedule_key_extension,
+ isl_schedule_key_filter,
+ isl_schedule_key_guard,
+ isl_schedule_key_leaf,
+ isl_schedule_key_mark,
+ isl_schedule_key_options,
+ isl_schedule_key_permutable,
+ isl_schedule_key_schedule,
+ isl_schedule_key_sequence,
+ isl_schedule_key_set,
+ isl_schedule_key_end
+};
+
+/* Textual representations of the YAML keys for an isl_schedule object.
+ */
+static char *key_str[] = {
+ [isl_schedule_key_child] = "child",
+ [isl_schedule_key_coincident] = "coincident",
+ [isl_schedule_key_context] = "context",
+ [isl_schedule_key_contraction] = "contraction",
+ [isl_schedule_key_domain] = "domain",
+ [isl_schedule_key_expansion] = "expansion",
+ [isl_schedule_key_extension] = "extension",
+ [isl_schedule_key_filter] = "filter",
+ [isl_schedule_key_guard] = "guard",
+ [isl_schedule_key_leaf] = "leaf",
+ [isl_schedule_key_mark] = "mark",
+ [isl_schedule_key_options] = "options",
+ [isl_schedule_key_permutable] = "permutable",
+ [isl_schedule_key_schedule] = "schedule",
+ [isl_schedule_key_sequence] = "sequence",
+ [isl_schedule_key_set] = "set",
+};
+
+#undef KEY
+#define KEY enum isl_schedule_key
+#undef KEY_ERROR
+#define KEY_ERROR isl_schedule_key_error
+#undef KEY_END
+#define KEY_END isl_schedule_key_end
+#include "extract_key.c"
+
+static __isl_give isl_schedule_tree *isl_stream_read_schedule_tree(
+ __isl_keep isl_stream *s);
+
+/* Read a subtree with context root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_context(__isl_keep isl_stream *s)
+{
+ isl_set *context = NULL;
+ isl_schedule_tree *tree;
+ isl_ctx *ctx;
+ struct isl_token *tok;
+ enum isl_schedule_key key;
+ char *str;
+ int more;
+
+ ctx = isl_stream_get_ctx(s);
+
+ key = get_key(s);
+
+ if (isl_stream_yaml_next(s) < 0)
+ return NULL;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return NULL;
+ }
+ str = isl_token_get_str(ctx, tok);
+ context = isl_set_read_from_str(ctx, str);
+ free(str);
+ isl_token_free(tok);
+
+ more = isl_stream_yaml_next(s);
+ if (more < 0)
+ goto error;
+ if (!more) {
+ tree = isl_schedule_tree_from_context(context);
+ } else {
+ key = get_key(s);
+ if (key != isl_schedule_key_child)
+ isl_die(ctx, isl_error_invalid, "expecting child",
+ goto error);
+ if (isl_stream_yaml_next(s) < 0)
+ goto error;
+ tree = isl_stream_read_schedule_tree(s);
+ tree = isl_schedule_tree_insert_context(tree, context);
+ }
+
+ return tree;
+error:
+ isl_set_free(context);
+ return NULL;
+}
+
+/* Read a subtree with domain root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_domain(__isl_keep isl_stream *s)
+{
+ isl_union_set *domain = NULL;
+ isl_schedule_tree *tree;
+ isl_ctx *ctx;
+ struct isl_token *tok;
+ enum isl_schedule_key key;
+ char *str;
+ int more;
+
+ ctx = isl_stream_get_ctx(s);
+
+ key = get_key(s);
+
+ if (isl_stream_yaml_next(s) < 0)
+ return NULL;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return NULL;
+ }
+ str = isl_token_get_str(ctx, tok);
+ domain = isl_union_set_read_from_str(ctx, str);
+ free(str);
+ isl_token_free(tok);
+
+ more = isl_stream_yaml_next(s);
+ if (more < 0)
+ goto error;
+ if (!more) {
+ tree = isl_schedule_tree_from_domain(domain);
+ } else {
+ key = get_key(s);
+ if (key != isl_schedule_key_child)
+ isl_die(ctx, isl_error_invalid, "expecting child",
+ goto error);
+ if (isl_stream_yaml_next(s) < 0)
+ goto error;
+ tree = isl_stream_read_schedule_tree(s);
+ tree = isl_schedule_tree_insert_domain(tree, domain);
+ }
+
+ return tree;
+error:
+ isl_union_set_free(domain);
+ return NULL;
+}
+
+/* Read a subtree with expansion root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_expansion(isl_stream *s)
+{
+ isl_ctx *ctx;
+ isl_union_pw_multi_aff *contraction = NULL;
+ isl_union_map *expansion = NULL;
+ isl_schedule_tree *tree = NULL;
+ int more;
+
+ ctx = isl_stream_get_ctx(s);
+
+ do {
+ struct isl_token *tok;
+ enum isl_schedule_key key;
+ char *str;
+
+ key = get_key(s);
+ if (isl_stream_yaml_next(s) < 0)
+ goto error;
+
+ switch (key) {
+ case isl_schedule_key_contraction:
+ isl_union_pw_multi_aff_free(contraction);
+ tok = isl_stream_next_token(s);
+ str = isl_token_get_str(ctx, tok);
+ contraction = isl_union_pw_multi_aff_read_from_str(ctx,
+ str);
+ free(str);
+ isl_token_free(tok);
+ if (!contraction)
+ goto error;
+ break;
+ case isl_schedule_key_expansion:
+ isl_union_map_free(expansion);
+ tok = isl_stream_next_token(s);
+ str = isl_token_get_str(ctx, tok);
+ expansion = isl_union_map_read_from_str(ctx, str);
+ free(str);
+ isl_token_free(tok);
+ if (!expansion)
+ goto error;
+ break;
+ case isl_schedule_key_child:
+ isl_schedule_tree_free(tree);
+ tree = isl_stream_read_schedule_tree(s);
+ if (!tree)
+ goto error;
+ break;
+ default:
+ isl_die(ctx, isl_error_invalid, "unexpected key",
+ goto error);
+ }
+ } while ((more = isl_stream_yaml_next(s)) > 0);
+
+ if (more < 0)
+ goto error;
+
+ if (!contraction)
+ isl_die(ctx, isl_error_invalid, "missing contraction",
+ goto error);
+ if (!expansion)
+ isl_die(ctx, isl_error_invalid, "missing expansion",
+ goto error);
+
+ if (!tree)
+ return isl_schedule_tree_from_expansion(contraction, expansion);
+ return isl_schedule_tree_insert_expansion(tree, contraction, expansion);
+error:
+ isl_schedule_tree_free(tree);
+ isl_union_pw_multi_aff_free(contraction);
+ isl_union_map_free(expansion);
+ return NULL;
+}
+
+/* Read a subtree with extension root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_extension(isl_stream *s)
+{
+ isl_union_map *extension = NULL;
+ isl_schedule_tree *tree;
+ isl_ctx *ctx;
+ struct isl_token *tok;
+ enum isl_schedule_key key;
+ char *str;
+ int more;
+
+ ctx = isl_stream_get_ctx(s);
+
+ key = get_key(s);
+
+ if (isl_stream_yaml_next(s) < 0)
+ return NULL;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return NULL;
+ }
+ str = isl_token_get_str(ctx, tok);
+ extension = isl_union_map_read_from_str(ctx, str);
+ free(str);
+ isl_token_free(tok);
+
+ more = isl_stream_yaml_next(s);
+ if (more < 0)
+ goto error;
+ if (!more) {
+ tree = isl_schedule_tree_from_extension(extension);
+ } else {
+ key = get_key(s);
+ if (key != isl_schedule_key_child)
+ isl_die(ctx, isl_error_invalid, "expecting child",
+ goto error);
+ if (isl_stream_yaml_next(s) < 0)
+ goto error;
+ tree = isl_stream_read_schedule_tree(s);
+ tree = isl_schedule_tree_insert_extension(tree, extension);
+ }
+
+ return tree;
+error:
+ isl_union_map_free(extension);
+ return NULL;
+}
+
+/* Read a subtree with filter root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_filter(__isl_keep isl_stream *s)
+{
+ isl_union_set *filter = NULL;
+ isl_schedule_tree *tree;
+ isl_ctx *ctx;
+ struct isl_token *tok;
+ enum isl_schedule_key key;
+ char *str;
+ int more;
+
+ ctx = isl_stream_get_ctx(s);
+
+ key = get_key(s);
+
+ if (isl_stream_yaml_next(s) < 0)
+ return NULL;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return NULL;
+ }
+ str = isl_token_get_str(ctx, tok);
+ filter = isl_union_set_read_from_str(ctx, str);
+ free(str);
+ isl_token_free(tok);
+
+ more = isl_stream_yaml_next(s);
+ if (more < 0)
+ goto error;
+ if (!more) {
+ tree = isl_schedule_tree_from_filter(filter);
+ } else {
+ key = get_key(s);
+ if (key != isl_schedule_key_child)
+ isl_die(ctx, isl_error_invalid, "expecting child",
+ goto error);
+ if (isl_stream_yaml_next(s) < 0)
+ goto error;
+ tree = isl_stream_read_schedule_tree(s);
+ tree = isl_schedule_tree_insert_filter(tree, filter);
+ }
+
+ return tree;
+error:
+ isl_union_set_free(filter);
+ return NULL;
+}
+
+/* Read a subtree with guard root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_guard(isl_stream *s)
+{
+ isl_set *guard = NULL;
+ isl_schedule_tree *tree;
+ isl_ctx *ctx;
+ struct isl_token *tok;
+ enum isl_schedule_key key;
+ char *str;
+ int more;
+
+ ctx = isl_stream_get_ctx(s);
+
+ key = get_key(s);
+
+ if (isl_stream_yaml_next(s) < 0)
+ return NULL;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return NULL;
+ }
+ str = isl_token_get_str(ctx, tok);
+ guard = isl_set_read_from_str(ctx, str);
+ free(str);
+ isl_token_free(tok);
+
+ more = isl_stream_yaml_next(s);
+ if (more < 0)
+ goto error;
+ if (!more) {
+ tree = isl_schedule_tree_from_guard(guard);
+ } else {
+ key = get_key(s);
+ if (key != isl_schedule_key_child)
+ isl_die(ctx, isl_error_invalid, "expecting child",
+ goto error);
+ if (isl_stream_yaml_next(s) < 0)
+ goto error;
+ tree = isl_stream_read_schedule_tree(s);
+ tree = isl_schedule_tree_insert_guard(tree, guard);
+ }
+
+ return tree;
+error:
+ isl_set_free(guard);
+ return NULL;
+}
+
+/* Read a subtree with mark root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_mark(isl_stream *s)
+{
+ isl_id *mark;
+ isl_schedule_tree *tree;
+ isl_ctx *ctx;
+ struct isl_token *tok;
+ enum isl_schedule_key key;
+ char *str;
+ int more;
+
+ ctx = isl_stream_get_ctx(s);
+
+ key = get_key(s);
+
+ if (isl_stream_yaml_next(s) < 0)
+ return NULL;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return NULL;
+ }
+ str = isl_token_get_str(ctx, tok);
+ mark = isl_id_alloc(ctx, str, NULL);
+ free(str);
+ isl_token_free(tok);
+
+ more = isl_stream_yaml_next(s);
+ if (more < 0)
+ goto error;
+ if (!more) {
+ isl_die(ctx, isl_error_invalid, "expecting child",
+ goto error);
+ } else {
+ key = get_key(s);
+ if (key != isl_schedule_key_child)
+ isl_die(ctx, isl_error_invalid, "expecting child",
+ goto error);
+ if (isl_stream_yaml_next(s) < 0)
+ goto error;
+ tree = isl_stream_read_schedule_tree(s);
+ tree = isl_schedule_tree_insert_mark(tree, mark);
+ }
+
+ return tree;
+error:
+ isl_id_free(mark);
+ return NULL;
+}
+
+/* Read a sequence of integers from "s" (representing the coincident
+ * property of a band node).
+ */
+static __isl_give isl_val_list *read_coincident(__isl_keep isl_stream *s)
+{
+ isl_ctx *ctx;
+ isl_val_list *list;
+ int more;
+
+ ctx = isl_stream_get_ctx(s);
+
+ if (isl_stream_yaml_read_start_sequence(s) < 0)
+ return NULL;
+
+ list = isl_val_list_alloc(ctx, 0);
+ while ((more = isl_stream_yaml_next(s)) > 0) {
+ isl_val *val;
+
+ val = isl_stream_read_val(s);
+ list = isl_val_list_add(list, val);
+ }
+
+ if (more < 0 || isl_stream_yaml_read_end_sequence(s))
+ list = isl_val_list_free(list);
+
+ return list;
+}
+
+/* Set the (initial) coincident properties of "band" according to
+ * the (initial) elements of "coincident".
+ */
+static __isl_give isl_schedule_band *set_coincident(
+ __isl_take isl_schedule_band *band, __isl_take isl_val_list *coincident)
+{
+ int i;
+ isl_size n, m;
+
+ n = isl_schedule_band_n_member(band);
+ m = isl_val_list_n_val(coincident);
+ if (n < 0 || m < 0)
+ band = isl_schedule_band_free(band);
+
+ for (i = 0; i < n && i < m; ++i) {
+ isl_val *v;
+
+ v = isl_val_list_get_val(coincident, i);
+ if (!v)
+ band = isl_schedule_band_free(band);
+ band = isl_schedule_band_member_set_coincident(band, i,
+ !isl_val_is_zero(v));
+ isl_val_free(v);
+ }
+ isl_val_list_free(coincident);
+ return band;
+}
+
+/* Read a subtree with band root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_band(isl_stream *s)
+{
+ isl_multi_union_pw_aff *schedule = NULL;
+ isl_schedule_tree *tree = NULL;
+ isl_val_list *coincident = NULL;
+ isl_union_set *options = NULL;
+ isl_ctx *ctx;
+ isl_schedule_band *band;
+ int permutable = 0;
+ int more;
+
+ ctx = isl_stream_get_ctx(s);
+
+ do {
+ struct isl_token *tok;
+ enum isl_schedule_key key;
+ char *str;
+ isl_val *v;
+
+ key = get_key(s);
+ if (isl_stream_yaml_next(s) < 0)
+ goto error;
+
+ switch (key) {
+ case isl_schedule_key_schedule:
+ schedule = isl_multi_union_pw_aff_free(schedule);
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ isl_stream_error(s, NULL, "unexpected EOF");
+ goto error;
+ }
+ str = isl_token_get_str(ctx, tok);
+ schedule = isl_multi_union_pw_aff_read_from_str(ctx,
+ str);
+ free(str);
+ isl_token_free(tok);
+ if (!schedule)
+ goto error;
+ break;
+ case isl_schedule_key_coincident:
+ coincident = read_coincident(s);
+ if (!coincident)
+ goto error;
+ break;
+ case isl_schedule_key_permutable:
+ v = isl_stream_read_val(s);
+ permutable = !isl_val_is_zero(v);
+ isl_val_free(v);
+ break;
+ case isl_schedule_key_options:
+ isl_union_set_free(options);
+ tok = isl_stream_next_token(s);
+ str = isl_token_get_str(ctx, tok);
+ options = isl_union_set_read_from_str(ctx, str);
+ free(str);
+ isl_token_free(tok);
+ if (!options)
+ goto error;
+ break;
+ case isl_schedule_key_child:
+ isl_schedule_tree_free(tree);
+ tree = isl_stream_read_schedule_tree(s);
+ if (!tree)
+ goto error;
+ break;
+ default:
+ isl_die(ctx, isl_error_invalid, "unexpected key",
+ goto error);
+ }
+ } while ((more = isl_stream_yaml_next(s)) > 0);
+
+ if (more < 0)
+ goto error;
+
+ if (!schedule)
+ isl_die(ctx, isl_error_invalid, "missing schedule", goto error);
+
+ band = isl_schedule_band_from_multi_union_pw_aff(schedule);
+ band = isl_schedule_band_set_permutable(band, permutable);
+ if (coincident)
+ band = set_coincident(band, coincident);
+ if (options)
+ band = isl_schedule_band_set_ast_build_options(band, options);
+ if (tree)
+ tree = isl_schedule_tree_insert_band(tree, band);
+ else
+ tree = isl_schedule_tree_from_band(band);
+
+ return tree;
+error:
+ isl_val_list_free(coincident);
+ isl_union_set_free(options);
+ isl_schedule_tree_free(tree);
+ isl_multi_union_pw_aff_free(schedule);
+ return NULL;
+}
+
+/* Read a subtree with root node of type "type" from "s".
+ * The node is represented by a sequence of children.
+ */
+static __isl_give isl_schedule_tree *read_children(isl_stream *s,
+ enum isl_schedule_node_type type)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree_list *list;
+ int more;
+
+ ctx = isl_stream_get_ctx(s);
+
+ isl_token_free(isl_stream_next_token(s));
+
+ if (isl_stream_yaml_next(s) < 0)
+ return NULL;
+
+ if (isl_stream_yaml_read_start_sequence(s))
+ return NULL;
+
+ list = isl_schedule_tree_list_alloc(ctx, 0);
+ while ((more = isl_stream_yaml_next(s)) > 0) {
+ isl_schedule_tree *tree;
+
+ tree = isl_stream_read_schedule_tree(s);
+ list = isl_schedule_tree_list_add(list, tree);
+ }
+
+ if (more < 0 || isl_stream_yaml_read_end_sequence(s))
+ list = isl_schedule_tree_list_free(list);
+
+ return isl_schedule_tree_from_children(type, list);
+}
+
+/* Read a subtree with sequence root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_sequence(isl_stream *s)
+{
+ return read_children(s, isl_schedule_node_sequence);
+}
+
+/* Read a subtree with set root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_set(isl_stream *s)
+{
+ return read_children(s, isl_schedule_node_set);
+}
+
+/* Read a schedule (sub)tree from "s".
+ *
+ * We first determine the type of the root node based on the first
+ * mapping key and then hand over to a function tailored to reading
+ * nodes of this type.
+ */
+static __isl_give isl_schedule_tree *isl_stream_read_schedule_tree(
+ struct isl_stream *s)
+{
+ enum isl_schedule_key key;
+ struct isl_token *tok;
+ isl_schedule_tree *tree = NULL;
+ int more;
+
+ if (isl_stream_yaml_read_start_mapping(s))
+ return NULL;
+ more = isl_stream_yaml_next(s);
+ if (more < 0)
+ return NULL;
+ if (!more) {
+ isl_stream_error(s, NULL, "missing key");
+ return NULL;
+ }
+
+ tok = isl_stream_next_token(s);
+ key = extract_key(s, tok);
+ isl_stream_push_token(s, tok);
+ if (key < 0)
+ return NULL;
+ switch (key) {
+ case isl_schedule_key_context:
+ tree = read_context(s);
+ break;
+ case isl_schedule_key_domain:
+ tree = read_domain(s);
+ break;
+ case isl_schedule_key_contraction:
+ case isl_schedule_key_expansion:
+ tree = read_expansion(s);
+ break;
+ case isl_schedule_key_extension:
+ tree = read_extension(s);
+ break;
+ case isl_schedule_key_filter:
+ tree = read_filter(s);
+ break;
+ case isl_schedule_key_guard:
+ tree = read_guard(s);
+ break;
+ case isl_schedule_key_leaf:
+ isl_token_free(isl_stream_next_token(s));
+ tree = isl_schedule_tree_leaf(isl_stream_get_ctx(s));
+ break;
+ case isl_schedule_key_mark:
+ tree = read_mark(s);
+ break;
+ case isl_schedule_key_sequence:
+ tree = read_sequence(s);
+ break;
+ case isl_schedule_key_set:
+ tree = read_set(s);
+ break;
+ case isl_schedule_key_schedule:
+ case isl_schedule_key_coincident:
+ case isl_schedule_key_options:
+ case isl_schedule_key_permutable:
+ tree = read_band(s);
+ break;
+ case isl_schedule_key_child:
+ isl_die(isl_stream_get_ctx(s), isl_error_unsupported,
+ "cannot identify node type", return NULL);
+ case isl_schedule_key_end:
+ case isl_schedule_key_error:
+ return NULL;
+ }
+
+ if (isl_stream_yaml_read_end_mapping(s) < 0) {
+ isl_stream_error(s, NULL, "unexpected extra elements");
+ return isl_schedule_tree_free(tree);
+ }
+
+ return tree;
+}
+
+/* Read an isl_schedule from "s".
+ */
+__isl_give isl_schedule *isl_stream_read_schedule(isl_stream *s)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ if (!s)
+ return NULL;
+
+ ctx = isl_stream_get_ctx(s);
+ tree = isl_stream_read_schedule_tree(s);
+ return isl_schedule_from_schedule_tree(ctx, tree);
+}
+
+/* Read an isl_schedule from "input".
+ */
+__isl_give isl_schedule *isl_schedule_read_from_file(isl_ctx *ctx, FILE *input)
+{
+ struct isl_stream *s;
+ isl_schedule *schedule;
+
+ s = isl_stream_new_file(ctx, input);
+ if (!s)
+ return NULL;
+ schedule = isl_stream_read_schedule(s);
+ isl_stream_free(s);
+
+ return schedule;
+}
+
+/* Read an isl_schedule from "str".
+ */
+__isl_give isl_schedule *isl_schedule_read_from_str(isl_ctx *ctx,
+ const char *str)
+{
+ struct isl_stream *s;
+ isl_schedule *schedule;
+
+ s = isl_stream_new_str(ctx, str);
+ if (!s)
+ return NULL;
+ schedule = isl_stream_read_schedule(s);
+ isl_stream_free(s);
+
+ return schedule;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_tree.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_tree.c
new file mode 100644
index 00000000000..2a20a336ff2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_tree.c
@@ -0,0 +1,2906 @@
+/*
+ * Copyright 2013-2014 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ * Copyright 2016 INRIA Paris
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ * and Centre de Recherche Inria de Paris, 2 rue Simone Iff - Voie DQ12,
+ * CS 42112, 75589 Paris Cedex 12, France
+ */
+
+#include <isl/id.h>
+#include <isl/val.h>
+#include <isl/space.h>
+#include <isl/map.h>
+#include <isl_schedule_band.h>
+#include <isl_schedule_private.h>
+
+#undef EL
+#define EL isl_schedule_tree
+
+#include <isl_list_templ.h>
+
+#undef EL_BASE
+#define EL_BASE schedule_tree
+
+#include <isl_list_templ.c>
+
+/* Is "tree" the leaf of a schedule tree?
+ */
+int isl_schedule_tree_is_leaf(__isl_keep isl_schedule_tree *tree)
+{
+ return isl_schedule_tree_get_type(tree) == isl_schedule_node_leaf;
+}
+
+/* Create a new schedule tree of type "type".
+ * The caller is responsible for filling in the type specific fields and
+ * the children.
+ *
+ * By default, the single node tree does not have any anchored nodes.
+ * The caller is responsible for updating the anchored field if needed.
+ */
+static __isl_give isl_schedule_tree *isl_schedule_tree_alloc(isl_ctx *ctx,
+ enum isl_schedule_node_type type)
+{
+ isl_schedule_tree *tree;
+
+ if (type == isl_schedule_node_error)
+ return NULL;
+
+ tree = isl_calloc_type(ctx, isl_schedule_tree);
+ if (!tree)
+ return NULL;
+
+ tree->ref = 1;
+ tree->ctx = ctx;
+ isl_ctx_ref(ctx);
+ tree->type = type;
+ tree->anchored = 0;
+
+ return tree;
+}
+
+/* Return a fresh copy of "tree".
+ */
+__isl_take isl_schedule_tree *isl_schedule_tree_dup(
+ __isl_keep isl_schedule_tree *tree)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *dup;
+
+ if (!tree)
+ return NULL;
+
+ ctx = isl_schedule_tree_get_ctx(tree);
+ dup = isl_schedule_tree_alloc(ctx, tree->type);
+ if (!dup)
+ return NULL;
+
+ switch (tree->type) {
+ case isl_schedule_node_error:
+ isl_die(ctx, isl_error_internal,
+ "allocation should have failed",
+ return isl_schedule_tree_free(dup));
+ case isl_schedule_node_band:
+ dup->band = isl_schedule_band_copy(tree->band);
+ if (!dup->band)
+ return isl_schedule_tree_free(dup);
+ break;
+ case isl_schedule_node_context:
+ dup->context = isl_set_copy(tree->context);
+ if (!dup->context)
+ return isl_schedule_tree_free(dup);
+ break;
+ case isl_schedule_node_domain:
+ dup->domain = isl_union_set_copy(tree->domain);
+ if (!dup->domain)
+ return isl_schedule_tree_free(dup);
+ break;
+ case isl_schedule_node_expansion:
+ dup->contraction =
+ isl_union_pw_multi_aff_copy(tree->contraction);
+ dup->expansion = isl_union_map_copy(tree->expansion);
+ if (!dup->contraction || !dup->expansion)
+ return isl_schedule_tree_free(dup);
+ break;
+ case isl_schedule_node_extension:
+ dup->extension = isl_union_map_copy(tree->extension);
+ if (!dup->extension)
+ return isl_schedule_tree_free(dup);
+ break;
+ case isl_schedule_node_filter:
+ dup->filter = isl_union_set_copy(tree->filter);
+ if (!dup->filter)
+ return isl_schedule_tree_free(dup);
+ break;
+ case isl_schedule_node_guard:
+ dup->guard = isl_set_copy(tree->guard);
+ if (!dup->guard)
+ return isl_schedule_tree_free(dup);
+ break;
+ case isl_schedule_node_mark:
+ dup->mark = isl_id_copy(tree->mark);
+ if (!dup->mark)
+ return isl_schedule_tree_free(dup);
+ break;
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ break;
+ }
+
+ if (tree->children) {
+ dup->children = isl_schedule_tree_list_copy(tree->children);
+ if (!dup->children)
+ return isl_schedule_tree_free(dup);
+ }
+ dup->anchored = tree->anchored;
+
+ return dup;
+}
+
+/* Return an isl_schedule_tree that is equal to "tree" and that has only
+ * a single reference.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_cow(
+ __isl_take isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->ref == 1)
+ return tree;
+ tree->ref--;
+ return isl_schedule_tree_dup(tree);
+}
+
+/* Return a new reference to "tree".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_copy(
+ __isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ tree->ref++;
+ return tree;
+}
+
+/* Free "tree" and return NULL.
+ */
+__isl_null isl_schedule_tree *isl_schedule_tree_free(
+ __isl_take isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+ if (--tree->ref > 0)
+ return NULL;
+
+ switch (tree->type) {
+ case isl_schedule_node_band:
+ isl_schedule_band_free(tree->band);
+ break;
+ case isl_schedule_node_context:
+ isl_set_free(tree->context);
+ break;
+ case isl_schedule_node_domain:
+ isl_union_set_free(tree->domain);
+ break;
+ case isl_schedule_node_expansion:
+ isl_union_pw_multi_aff_free(tree->contraction);
+ isl_union_map_free(tree->expansion);
+ break;
+ case isl_schedule_node_extension:
+ isl_union_map_free(tree->extension);
+ break;
+ case isl_schedule_node_filter:
+ isl_union_set_free(tree->filter);
+ break;
+ case isl_schedule_node_guard:
+ isl_set_free(tree->guard);
+ break;
+ case isl_schedule_node_mark:
+ isl_id_free(tree->mark);
+ break;
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ case isl_schedule_node_error:
+ case isl_schedule_node_leaf:
+ break;
+ }
+ isl_schedule_tree_list_free(tree->children);
+ isl_ctx_deref(tree->ctx);
+ free(tree);
+
+ return NULL;
+}
+
+/* Create and return a new leaf schedule tree.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_leaf(isl_ctx *ctx)
+{
+ return isl_schedule_tree_alloc(ctx, isl_schedule_node_leaf);
+}
+
+/* Create a new band schedule tree referring to "band"
+ * with no children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_band(
+ __isl_take isl_schedule_band *band)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ if (!band)
+ return NULL;
+
+ ctx = isl_schedule_band_get_ctx(band);
+ tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_band);
+ if (!tree)
+ goto error;
+
+ tree->band = band;
+ tree->anchored = isl_schedule_band_is_anchored(band);
+
+ return tree;
+error:
+ isl_schedule_band_free(band);
+ return NULL;
+}
+
+/* Create a new context schedule tree with the given context and no children.
+ * Since the context references the outer schedule dimension,
+ * the tree is anchored.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_context(
+ __isl_take isl_set *context)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ if (!context)
+ return NULL;
+
+ ctx = isl_set_get_ctx(context);
+ tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_context);
+ if (!tree)
+ goto error;
+
+ tree->context = context;
+ tree->anchored = 1;
+
+ return tree;
+error:
+ isl_set_free(context);
+ return NULL;
+}
+
+/* Create a new domain schedule tree with the given domain and no children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_domain(
+ __isl_take isl_union_set *domain)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ if (!domain)
+ return NULL;
+
+ ctx = isl_union_set_get_ctx(domain);
+ tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_domain);
+ if (!tree)
+ goto error;
+
+ tree->domain = domain;
+
+ return tree;
+error:
+ isl_union_set_free(domain);
+ return NULL;
+}
+
+/* Create a new expansion schedule tree with the given contraction and
+ * expansion and no children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_expansion(
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_union_map *expansion)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ if (!contraction || !expansion)
+ goto error;
+
+ ctx = isl_union_map_get_ctx(expansion);
+ tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_expansion);
+ if (!tree)
+ goto error;
+
+ tree->contraction = contraction;
+ tree->expansion = expansion;
+
+ return tree;
+error:
+ isl_union_pw_multi_aff_free(contraction);
+ isl_union_map_free(expansion);
+ return NULL;
+}
+
+/* Create a new extension schedule tree with the given extension and
+ * no children.
+ * Since the domain of the extension refers to the outer schedule dimension,
+ * the tree is anchored.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_extension(
+ __isl_take isl_union_map *extension)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ if (!extension)
+ return NULL;
+
+ ctx = isl_union_map_get_ctx(extension);
+ tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_extension);
+ if (!tree)
+ goto error;
+
+ tree->extension = extension;
+ tree->anchored = 1;
+
+ return tree;
+error:
+ isl_union_map_free(extension);
+ return NULL;
+}
+
+/* Create a new filter schedule tree with the given filter and no children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_filter(
+ __isl_take isl_union_set *filter)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ if (!filter)
+ return NULL;
+
+ ctx = isl_union_set_get_ctx(filter);
+ tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_filter);
+ if (!tree)
+ goto error;
+
+ tree->filter = filter;
+
+ return tree;
+error:
+ isl_union_set_free(filter);
+ return NULL;
+}
+
+/* Create a new guard schedule tree with the given guard and no children.
+ * Since the guard references the outer schedule dimension,
+ * the tree is anchored.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_guard(
+ __isl_take isl_set *guard)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ if (!guard)
+ return NULL;
+
+ ctx = isl_set_get_ctx(guard);
+ tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_guard);
+ if (!tree)
+ goto error;
+
+ tree->guard = guard;
+ tree->anchored = 1;
+
+ return tree;
+error:
+ isl_set_free(guard);
+ return NULL;
+}
+
+/* Create a new mark schedule tree with the given mark identifier and
+ * no children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_mark(
+ __isl_take isl_id *mark)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ if (!mark)
+ return NULL;
+
+ ctx = isl_id_get_ctx(mark);
+ tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_mark);
+ if (!tree)
+ goto error;
+
+ tree->mark = mark;
+
+ return tree;
+error:
+ isl_id_free(mark);
+ return NULL;
+}
+
+/* Does "tree" have any node that depends on its position
+ * in the complete schedule tree?
+ */
+isl_bool isl_schedule_tree_is_subtree_anchored(
+ __isl_keep isl_schedule_tree *tree)
+{
+ return tree ? isl_bool_ok(tree->anchored) : isl_bool_error;
+}
+
+/* Does the root node of "tree" depend on its position in the complete
+ * schedule tree?
+ * Band nodes may be anchored depending on the associated AST build options.
+ * Context, extension and guard nodes are always anchored.
+ */
+int isl_schedule_tree_is_anchored(__isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return -1;
+
+ switch (isl_schedule_tree_get_type(tree)) {
+ case isl_schedule_node_error:
+ return -1;
+ case isl_schedule_node_band:
+ return isl_schedule_band_is_anchored(tree->band);
+ case isl_schedule_node_context:
+ case isl_schedule_node_extension:
+ case isl_schedule_node_guard:
+ return 1;
+ case isl_schedule_node_domain:
+ case isl_schedule_node_expansion:
+ case isl_schedule_node_filter:
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ return 0;
+ }
+
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "unhandled case", return -1);
+}
+
+/* Update the anchored field of "tree" based on whether the root node
+ * itself in anchored and the anchored fields of the children.
+ *
+ * This function should be called whenever the children of a tree node
+ * are changed or the anchoredness of the tree root itself changes.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_update_anchored(
+ __isl_take isl_schedule_tree *tree)
+{
+ int i;
+ isl_size n;
+ int anchored;
+
+ anchored = isl_schedule_tree_is_anchored(tree);
+ n = isl_schedule_tree_n_children(tree);
+ if (anchored < 0 || n < 0)
+ return isl_schedule_tree_free(tree);
+
+ for (i = 0; !anchored && i < n; ++i) {
+ isl_schedule_tree *child;
+
+ child = isl_schedule_tree_get_child(tree, i);
+ if (!child)
+ return isl_schedule_tree_free(tree);
+ anchored = child->anchored;
+ isl_schedule_tree_free(child);
+ }
+
+ if (anchored == tree->anchored)
+ return tree;
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ return NULL;
+ tree->anchored = anchored;
+ return tree;
+}
+
+/* Create a new tree of the given type (isl_schedule_node_sequence or
+ * isl_schedule_node_set) with the given children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_children(
+ enum isl_schedule_node_type type,
+ __isl_take isl_schedule_tree_list *list)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree *tree;
+
+ if (!list)
+ return NULL;
+
+ ctx = isl_schedule_tree_list_get_ctx(list);
+ tree = isl_schedule_tree_alloc(ctx, type);
+ if (!tree)
+ goto error;
+
+ tree->children = list;
+ tree = isl_schedule_tree_update_anchored(tree);
+
+ return tree;
+error:
+ isl_schedule_tree_list_free(list);
+ return NULL;
+}
+
+/* Construct a tree with a root node of type "type" and as children
+ * "tree1" and "tree2".
+ * If the root of one (or both) of the input trees is itself of type "type",
+ * then the tree is replaced by its children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_pair(
+ enum isl_schedule_node_type type, __isl_take isl_schedule_tree *tree1,
+ __isl_take isl_schedule_tree *tree2)
+{
+ isl_ctx *ctx;
+ isl_schedule_tree_list *list;
+
+ if (!tree1 || !tree2)
+ goto error;
+
+ ctx = isl_schedule_tree_get_ctx(tree1);
+ if (isl_schedule_tree_get_type(tree1) == type) {
+ list = isl_schedule_tree_list_copy(tree1->children);
+ isl_schedule_tree_free(tree1);
+ } else {
+ list = isl_schedule_tree_list_alloc(ctx, 2);
+ list = isl_schedule_tree_list_add(list, tree1);
+ }
+ if (isl_schedule_tree_get_type(tree2) == type) {
+ isl_schedule_tree_list *children;
+
+ children = isl_schedule_tree_list_copy(tree2->children);
+ list = isl_schedule_tree_list_concat(list, children);
+ isl_schedule_tree_free(tree2);
+ } else {
+ list = isl_schedule_tree_list_add(list, tree2);
+ }
+
+ return isl_schedule_tree_from_children(type, list);
+error:
+ isl_schedule_tree_free(tree1);
+ isl_schedule_tree_free(tree2);
+ return NULL;
+}
+
+/* Construct a tree with a sequence root node and as children
+ * "tree1" and "tree2".
+ * If the root of one (or both) of the input trees is itself a sequence,
+ * then the tree is replaced by its children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_sequence_pair(
+ __isl_take isl_schedule_tree *tree1,
+ __isl_take isl_schedule_tree *tree2)
+{
+ return isl_schedule_tree_from_pair(isl_schedule_node_sequence,
+ tree1, tree2);
+}
+
+/* Construct a tree with a set root node and as children
+ * "tree1" and "tree2".
+ * If the root of one (or both) of the input trees is itself a set,
+ * then the tree is replaced by its children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_set_pair(
+ __isl_take isl_schedule_tree *tree1,
+ __isl_take isl_schedule_tree *tree2)
+{
+ return isl_schedule_tree_from_pair(isl_schedule_node_set, tree1, tree2);
+}
+
+/* Return the isl_ctx to which "tree" belongs.
+ */
+isl_ctx *isl_schedule_tree_get_ctx(__isl_keep isl_schedule_tree *tree)
+{
+ return tree ? tree->ctx : NULL;
+}
+
+/* Return the type of the root of the tree or isl_schedule_node_error
+ * on error.
+ */
+enum isl_schedule_node_type isl_schedule_tree_get_type(
+ __isl_keep isl_schedule_tree *tree)
+{
+ return tree ? tree->type : isl_schedule_node_error;
+}
+
+/* Are "tree1" and "tree2" obviously equal to each other?
+ */
+isl_bool isl_schedule_tree_plain_is_equal(__isl_keep isl_schedule_tree *tree1,
+ __isl_keep isl_schedule_tree *tree2)
+{
+ isl_bool equal;
+ int i;
+ isl_size n1, n2;
+
+ if (!tree1 || !tree2)
+ return isl_bool_error;
+ if (tree1 == tree2)
+ return isl_bool_true;
+ if (tree1->type != tree2->type)
+ return isl_bool_false;
+
+ switch (tree1->type) {
+ case isl_schedule_node_band:
+ equal = isl_schedule_band_plain_is_equal(tree1->band,
+ tree2->band);
+ break;
+ case isl_schedule_node_context:
+ equal = isl_set_is_equal(tree1->context, tree2->context);
+ break;
+ case isl_schedule_node_domain:
+ equal = isl_union_set_is_equal(tree1->domain, tree2->domain);
+ break;
+ case isl_schedule_node_expansion:
+ equal = isl_union_map_is_equal(tree1->expansion,
+ tree2->expansion);
+ if (equal >= 0 && equal)
+ equal = isl_union_pw_multi_aff_plain_is_equal(
+ tree1->contraction, tree2->contraction);
+ break;
+ case isl_schedule_node_extension:
+ equal = isl_union_map_is_equal(tree1->extension,
+ tree2->extension);
+ break;
+ case isl_schedule_node_filter:
+ equal = isl_union_set_is_equal(tree1->filter, tree2->filter);
+ break;
+ case isl_schedule_node_guard:
+ equal = isl_set_is_equal(tree1->guard, tree2->guard);
+ break;
+ case isl_schedule_node_mark:
+ equal = isl_bool_ok(tree1->mark == tree2->mark);
+ break;
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ equal = isl_bool_true;
+ break;
+ case isl_schedule_node_error:
+ equal = isl_bool_error;
+ break;
+ }
+
+ if (equal < 0 || !equal)
+ return equal;
+
+ n1 = isl_schedule_tree_n_children(tree1);
+ n2 = isl_schedule_tree_n_children(tree2);
+ if (n1 < 0 || n2 < 0)
+ return isl_bool_error;
+ if (n1 != n2)
+ return isl_bool_false;
+ for (i = 0; i < n1; ++i) {
+ isl_schedule_tree *child1, *child2;
+
+ child1 = isl_schedule_tree_get_child(tree1, i);
+ child2 = isl_schedule_tree_get_child(tree2, i);
+ equal = isl_schedule_tree_plain_is_equal(child1, child2);
+ isl_schedule_tree_free(child1);
+ isl_schedule_tree_free(child2);
+
+ if (equal < 0 || !equal)
+ return equal;
+ }
+
+ return isl_bool_true;
+}
+
+/* Does "tree" have any children, other than an implicit leaf.
+ */
+int isl_schedule_tree_has_children(__isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return -1;
+
+ return tree->children != NULL;
+}
+
+/* Return the number of children of "tree", excluding implicit leaves.
+ * The "children" field is NULL if there are
+ * no children (except for the implicit leaves).
+ */
+isl_size isl_schedule_tree_n_children(__isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return isl_size_error;
+
+ if (!tree->children)
+ return 0;
+ return isl_schedule_tree_list_n_schedule_tree(tree->children);
+}
+
+/* Return a copy of the (explicit) child at position "pos" of "tree".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_get_child(
+ __isl_keep isl_schedule_tree *tree, int pos)
+{
+ if (!tree)
+ return NULL;
+ if (!tree->children)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "schedule tree has no explicit children", return NULL);
+ return isl_schedule_tree_list_get_schedule_tree(tree->children, pos);
+}
+
+/* Return a copy of the (explicit) child at position "pos" of "tree" and
+ * free "tree".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_child(
+ __isl_take isl_schedule_tree *tree, int pos)
+{
+ isl_schedule_tree *child;
+
+ child = isl_schedule_tree_get_child(tree, pos);
+ isl_schedule_tree_free(tree);
+ return child;
+}
+
+/* Remove all (explicit) children from "tree".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_reset_children(
+ __isl_take isl_schedule_tree *tree)
+{
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ return NULL;
+ tree->children = isl_schedule_tree_list_free(tree->children);
+ return tree;
+}
+
+/* Remove the child at position "pos" from the children of "tree".
+ * If there was only one child to begin with, then remove all children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_drop_child(
+ __isl_take isl_schedule_tree *tree, int pos)
+{
+ isl_size n;
+
+ tree = isl_schedule_tree_cow(tree);
+
+ n = isl_schedule_tree_n_children(tree);
+ if (n < 0)
+ return isl_schedule_tree_free(tree);
+ if (n == 0)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "tree does not have any explicit children",
+ return isl_schedule_tree_free(tree));
+ if (pos < 0 || pos >= n)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "position out of bounds",
+ return isl_schedule_tree_free(tree));
+ if (n == 1)
+ return isl_schedule_tree_reset_children(tree);
+
+ tree->children = isl_schedule_tree_list_drop(tree->children, pos, 1);
+ if (!tree->children)
+ return isl_schedule_tree_free(tree);
+
+ return tree;
+}
+
+/* Replace the child at position "pos" of "tree" by "child".
+ *
+ * If the new child is a leaf, then it is not explicitly
+ * recorded in the list of children. Instead, the list of children
+ * (which is assumed to have only one element) is removed.
+ * Note that the children of set and sequence nodes are always
+ * filters, so they cannot be replaced by empty trees.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_replace_child(
+ __isl_take isl_schedule_tree *tree, int pos,
+ __isl_take isl_schedule_tree *child)
+{
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree || !child)
+ goto error;
+
+ if (isl_schedule_tree_is_leaf(child)) {
+ isl_size n;
+
+ isl_schedule_tree_free(child);
+ if (!tree->children && pos == 0)
+ return tree;
+ n = isl_schedule_tree_n_children(tree);
+ if (n < 0)
+ return isl_schedule_tree_free(tree);
+ if (n != 1)
+ isl_die(isl_schedule_tree_get_ctx(tree),
+ isl_error_internal,
+ "can only replace single child by leaf",
+ goto error);
+ return isl_schedule_tree_reset_children(tree);
+ }
+
+ if (!tree->children && pos == 0)
+ tree->children =
+ isl_schedule_tree_list_from_schedule_tree(child);
+ else
+ tree->children = isl_schedule_tree_list_set_schedule_tree(
+ tree->children, pos, child);
+
+ if (!tree->children)
+ return isl_schedule_tree_free(tree);
+ tree = isl_schedule_tree_update_anchored(tree);
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_schedule_tree_free(child);
+ return NULL;
+}
+
+/* Replace the (explicit) children of "tree" by "children"?
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_set_children(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_schedule_tree_list *children)
+{
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree || !children)
+ goto error;
+ isl_schedule_tree_list_free(tree->children);
+ tree->children = children;
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_schedule_tree_list_free(children);
+ return NULL;
+}
+
+/* Create a new band schedule tree referring to "band"
+ * with "tree" as single child.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_band(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_schedule_band *band)
+{
+ isl_schedule_tree *res;
+
+ res = isl_schedule_tree_from_band(band);
+ return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
+/* Create a new context schedule tree with the given context and
+ * with "tree" as single child.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_context(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_set *context)
+{
+ isl_schedule_tree *res;
+
+ res = isl_schedule_tree_from_context(context);
+ return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
+/* Create a new domain schedule tree with the given domain and
+ * with "tree" as single child.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_domain(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain)
+{
+ isl_schedule_tree *res;
+
+ res = isl_schedule_tree_from_domain(domain);
+ return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
+/* Create a new expansion schedule tree with the given contraction and
+ * expansion and with "tree" as single child.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_expansion(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_union_map *expansion)
+{
+ isl_schedule_tree *res;
+
+ res = isl_schedule_tree_from_expansion(contraction, expansion);
+ return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
+/* Create a new extension schedule tree with the given extension and
+ * with "tree" as single child.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_extension(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_map *extension)
+{
+ isl_schedule_tree *res;
+
+ res = isl_schedule_tree_from_extension(extension);
+ return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
+/* Create a new filter schedule tree with the given filter and single child.
+ *
+ * If the root of "tree" is itself a filter node, then the two
+ * filter nodes are merged into one node.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_filter(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter)
+{
+ isl_schedule_tree *res;
+
+ if (isl_schedule_tree_get_type(tree) == isl_schedule_node_filter) {
+ isl_union_set *tree_filter;
+
+ tree_filter = isl_schedule_tree_filter_get_filter(tree);
+ tree_filter = isl_union_set_intersect(tree_filter, filter);
+ tree = isl_schedule_tree_filter_set_filter(tree, tree_filter);
+ return tree;
+ }
+
+ res = isl_schedule_tree_from_filter(filter);
+ return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
+/* Insert a filter node with filter set "filter"
+ * in each of the children of "tree".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_children_insert_filter(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter)
+{
+ int i;
+ isl_size n;
+
+ n = isl_schedule_tree_n_children(tree);
+ if (n < 0 || !filter)
+ goto error;
+
+ for (i = 0; i < n; ++i) {
+ isl_schedule_tree *child;
+
+ child = isl_schedule_tree_get_child(tree, i);
+ child = isl_schedule_tree_insert_filter(child,
+ isl_union_set_copy(filter));
+ tree = isl_schedule_tree_replace_child(tree, i, child);
+ }
+
+ isl_union_set_free(filter);
+ return tree;
+error:
+ isl_union_set_free(filter);
+ isl_schedule_tree_free(tree);
+ return NULL;
+}
+
+/* Create a new guard schedule tree with the given guard and
+ * with "tree" as single child.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_guard(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_set *guard)
+{
+ isl_schedule_tree *res;
+
+ res = isl_schedule_tree_from_guard(guard);
+ return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
+/* Create a new mark schedule tree with the given mark identifier and
+ * single child.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_mark(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_id *mark)
+{
+ isl_schedule_tree *res;
+
+ res = isl_schedule_tree_from_mark(mark);
+ return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
+/* Return the number of members in the band tree root.
+ */
+isl_size isl_schedule_tree_band_n_member(__isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return isl_size_error;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return isl_size_error);
+
+ return isl_schedule_band_n_member(tree->band);
+}
+
+/* Is the band member at position "pos" of the band tree root
+ * marked coincident?
+ */
+isl_bool isl_schedule_tree_band_member_get_coincident(
+ __isl_keep isl_schedule_tree *tree, int pos)
+{
+ if (!tree)
+ return isl_bool_error;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return isl_bool_error);
+
+ return isl_schedule_band_member_get_coincident(tree->band, pos);
+}
+
+/* Mark the given band member as being coincident or not
+ * according to "coincident".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_member_set_coincident(
+ __isl_take isl_schedule_tree *tree, int pos, int coincident)
+{
+ if (!tree)
+ return NULL;
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return isl_schedule_tree_free(tree));
+ if (isl_schedule_tree_band_member_get_coincident(tree, pos) ==
+ coincident)
+ return tree;
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ return NULL;
+
+ tree->band = isl_schedule_band_member_set_coincident(tree->band, pos,
+ coincident);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+ return tree;
+}
+
+/* Is the band tree root marked permutable?
+ */
+isl_bool isl_schedule_tree_band_get_permutable(
+ __isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return isl_bool_error;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return isl_bool_error);
+
+ return isl_schedule_band_get_permutable(tree->band);
+}
+
+/* Mark the band tree root permutable or not according to "permutable"?
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_set_permutable(
+ __isl_take isl_schedule_tree *tree, int permutable)
+{
+ if (!tree)
+ return NULL;
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return isl_schedule_tree_free(tree));
+ if (isl_schedule_tree_band_get_permutable(tree) == permutable)
+ return tree;
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ return NULL;
+
+ tree->band = isl_schedule_band_set_permutable(tree->band, permutable);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+ return tree;
+}
+
+/* Return the schedule space of the band tree root.
+ */
+__isl_give isl_space *isl_schedule_tree_band_get_space(
+ __isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return NULL);
+
+ return isl_schedule_band_get_space(tree->band);
+}
+
+/* Intersect the domain of the band schedule of the band tree root
+ * with "domain".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_intersect_domain(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain)
+{
+ if (!tree || !domain)
+ goto error;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", goto error);
+
+ tree->band = isl_schedule_band_intersect_domain(tree->band, domain);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_union_set_free(domain);
+ return NULL;
+}
+
+/* Return the schedule of the band tree root in isolation.
+ */
+__isl_give isl_multi_union_pw_aff *isl_schedule_tree_band_get_partial_schedule(
+ __isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return NULL);
+
+ return isl_schedule_band_get_partial_schedule(tree->band);
+}
+
+/* Replace the schedule of the band tree root by "schedule".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_set_partial_schedule(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_multi_union_pw_aff *schedule)
+{
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree || !schedule)
+ goto error;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return NULL);
+ tree->band = isl_schedule_band_set_partial_schedule(tree->band,
+ schedule);
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_multi_union_pw_aff_free(schedule);
+ return NULL;
+}
+
+/* Return the loop AST generation type for the band member
+ * of the band tree root at position "pos".
+ */
+enum isl_ast_loop_type isl_schedule_tree_band_member_get_ast_loop_type(
+ __isl_keep isl_schedule_tree *tree, int pos)
+{
+ if (!tree)
+ return isl_ast_loop_error;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return isl_ast_loop_error);
+
+ return isl_schedule_band_member_get_ast_loop_type(tree->band, pos);
+}
+
+/* Set the loop AST generation type for the band member of the band tree root
+ * at position "pos" to "type".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_member_set_ast_loop_type(
+ __isl_take isl_schedule_tree *tree, int pos,
+ enum isl_ast_loop_type type)
+{
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return isl_schedule_tree_free(tree));
+
+ tree->band = isl_schedule_band_member_set_ast_loop_type(tree->band,
+ pos, type);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+
+ return tree;
+}
+
+/* Return the loop AST generation type for the band member
+ * of the band tree root at position "pos" for the isolated part.
+ */
+enum isl_ast_loop_type isl_schedule_tree_band_member_get_isolate_ast_loop_type(
+ __isl_keep isl_schedule_tree *tree, int pos)
+{
+ if (!tree)
+ return isl_ast_loop_error;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return isl_ast_loop_error);
+
+ return isl_schedule_band_member_get_isolate_ast_loop_type(tree->band,
+ pos);
+}
+
+/* Set the loop AST generation type for the band member of the band tree root
+ * at position "pos" for the isolated part to "type".
+ */
+__isl_give isl_schedule_tree *
+isl_schedule_tree_band_member_set_isolate_ast_loop_type(
+ __isl_take isl_schedule_tree *tree, int pos,
+ enum isl_ast_loop_type type)
+{
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return isl_schedule_tree_free(tree));
+
+ tree->band = isl_schedule_band_member_set_isolate_ast_loop_type(
+ tree->band, pos, type);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+
+ return tree;
+}
+
+/* Return the AST build options associated to the band tree root.
+ */
+__isl_give isl_union_set *isl_schedule_tree_band_get_ast_build_options(
+ __isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return NULL);
+
+ return isl_schedule_band_get_ast_build_options(tree->band);
+}
+
+/* Replace the AST build options associated to band tree root by "options".
+ * Updated the anchored field if the anchoredness of the root node itself
+ * changes.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_set_ast_build_options(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *options)
+{
+ int was_anchored;
+
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree || !options)
+ goto error;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", goto error);
+
+ was_anchored = isl_schedule_tree_is_anchored(tree);
+ tree->band = isl_schedule_band_set_ast_build_options(tree->band,
+ options);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+ if (isl_schedule_tree_is_anchored(tree) != was_anchored)
+ tree = isl_schedule_tree_update_anchored(tree);
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_union_set_free(options);
+ return NULL;
+}
+
+/* Return the "isolate" option associated to the band tree root of "tree",
+ * which is assumed to appear at schedule depth "depth".
+ */
+__isl_give isl_set *isl_schedule_tree_band_get_ast_isolate_option(
+ __isl_keep isl_schedule_tree *tree, int depth)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return NULL);
+
+ return isl_schedule_band_get_ast_isolate_option(tree->band, depth);
+}
+
+/* Return the context of the context tree root.
+ */
+__isl_give isl_set *isl_schedule_tree_context_get_context(
+ __isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_context)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a context node", return NULL);
+
+ return isl_set_copy(tree->context);
+}
+
+/* Return the domain of the domain tree root.
+ */
+__isl_give isl_union_set *isl_schedule_tree_domain_get_domain(
+ __isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_domain)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a domain node", return NULL);
+
+ return isl_union_set_copy(tree->domain);
+}
+
+/* Replace the domain of domain tree root "tree" by "domain".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_domain_set_domain(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain)
+{
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree || !domain)
+ goto error;
+
+ if (tree->type != isl_schedule_node_domain)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a domain node", goto error);
+
+ isl_union_set_free(tree->domain);
+ tree->domain = domain;
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_union_set_free(domain);
+ return NULL;
+}
+
+/* Return the contraction of the expansion tree root.
+ */
+__isl_give isl_union_pw_multi_aff *isl_schedule_tree_expansion_get_contraction(
+ __isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_expansion)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not an expansion node", return NULL);
+
+ return isl_union_pw_multi_aff_copy(tree->contraction);
+}
+
+/* Return the expansion of the expansion tree root.
+ */
+__isl_give isl_union_map *isl_schedule_tree_expansion_get_expansion(
+ __isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_expansion)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not an expansion node", return NULL);
+
+ return isl_union_map_copy(tree->expansion);
+}
+
+/* Replace the contraction and the expansion of the expansion tree root "tree"
+ * by "contraction" and "expansion".
+ */
+__isl_give isl_schedule_tree *
+isl_schedule_tree_expansion_set_contraction_and_expansion(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_union_map *expansion)
+{
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree || !contraction || !expansion)
+ goto error;
+
+ if (tree->type != isl_schedule_node_expansion)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not an expansion node", return NULL);
+
+ isl_union_pw_multi_aff_free(tree->contraction);
+ tree->contraction = contraction;
+ isl_union_map_free(tree->expansion);
+ tree->expansion = expansion;
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_union_pw_multi_aff_free(contraction);
+ isl_union_map_free(expansion);
+ return NULL;
+}
+
+/* Return the extension of the extension tree root.
+ */
+__isl_give isl_union_map *isl_schedule_tree_extension_get_extension(
+ __isl_take isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_extension)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not an extension node", return NULL);
+
+ return isl_union_map_copy(tree->extension);
+}
+
+/* Replace the extension of extension tree root "tree" by "extension".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_extension_set_extension(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_map *extension)
+{
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree || !extension)
+ goto error;
+
+ if (tree->type != isl_schedule_node_extension)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not an extension node", return NULL);
+ isl_union_map_free(tree->extension);
+ tree->extension = extension;
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_union_map_free(extension);
+ return NULL;
+}
+
+/* Return the filter of the filter tree root.
+ */
+__isl_give isl_union_set *isl_schedule_tree_filter_get_filter(
+ __isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_filter)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a filter node", return NULL);
+
+ return isl_union_set_copy(tree->filter);
+}
+
+/* Replace the filter of the filter tree root by "filter".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_filter_set_filter(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter)
+{
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree || !filter)
+ goto error;
+
+ if (tree->type != isl_schedule_node_filter)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a filter node", return NULL);
+
+ isl_union_set_free(tree->filter);
+ tree->filter = filter;
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_union_set_free(filter);
+ return NULL;
+}
+
+/* Return the guard of the guard tree root.
+ */
+__isl_give isl_set *isl_schedule_tree_guard_get_guard(
+ __isl_take isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_guard)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a guard node", return NULL);
+
+ return isl_set_copy(tree->guard);
+}
+
+/* Return the mark identifier of the mark tree root "tree".
+ */
+__isl_give isl_id *isl_schedule_tree_mark_get_id(
+ __isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return NULL;
+
+ if (tree->type != isl_schedule_node_mark)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a mark node", return NULL);
+
+ return isl_id_copy(tree->mark);
+}
+
+/* Set dim to the range dimension of "map" and abort the search.
+ */
+static isl_stat set_range_dim(__isl_take isl_map *map, void *user)
+{
+ isl_size *dim = user;
+
+ *dim = isl_map_dim(map, isl_dim_out);
+ isl_map_free(map);
+
+ return isl_stat_error;
+}
+
+/* Return the dimension of the range of "umap".
+ * "umap" is assumed not to be empty and
+ * all maps inside "umap" are assumed to have the same range.
+ *
+ * We extract the range dimension from the first map in "umap".
+ */
+static isl_size range_dim(__isl_keep isl_union_map *umap)
+{
+ isl_size dim = isl_size_error;
+ isl_size n;
+
+ n = isl_union_map_n_map(umap);
+ if (n < 0)
+ return isl_size_error;
+ if (n == 0)
+ isl_die(isl_union_map_get_ctx(umap), isl_error_internal,
+ "unexpected empty input", return isl_size_error);
+
+ isl_union_map_foreach_map(umap, &set_range_dim, &dim);
+
+ return dim;
+}
+
+/* Append an "extra" number of zeros to the range of "umap" and
+ * return the result.
+ */
+static __isl_give isl_union_map *append_range(__isl_take isl_union_map *umap,
+ int extra)
+{
+ isl_union_set *dom;
+ isl_space *space;
+ isl_multi_val *mv;
+ isl_union_pw_multi_aff *suffix;
+ isl_union_map *universe;
+ isl_union_map *suffix_umap;
+
+ universe = isl_union_map_universe(isl_union_map_copy(umap));
+ dom = isl_union_map_domain(universe);
+ space = isl_union_set_get_space(dom);
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, extra);
+ mv = isl_multi_val_zero(space);
+
+ suffix = isl_union_pw_multi_aff_multi_val_on_domain(dom, mv);
+ suffix_umap = isl_union_map_from_union_pw_multi_aff(suffix);
+ umap = isl_union_map_flat_range_product(umap, suffix_umap);
+
+ return umap;
+}
+
+/* Should we skip the root of "tree" while looking for the first
+ * descendant with schedule information?
+ * That is, is it impossible to derive any information about
+ * the iteration domain from this node?
+ *
+ * We do not want to skip leaf or error nodes because there is
+ * no point in looking any deeper from these nodes.
+ * We can only extract partial iteration domain information
+ * from an extension node, but extension nodes are not supported
+ * by the caller and it will error out on them.
+ */
+static isl_bool domain_less(__isl_keep isl_schedule_tree *tree)
+{
+ enum isl_schedule_node_type type;
+ isl_size n;
+
+ type = isl_schedule_tree_get_type(tree);
+ switch (type) {
+ case isl_schedule_node_band:
+ n = isl_schedule_tree_band_n_member(tree);
+ return n < 0 ? isl_bool_error : isl_bool_ok(n == 0);
+ case isl_schedule_node_context:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_mark:
+ return isl_bool_true;
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_error:
+ case isl_schedule_node_domain:
+ case isl_schedule_node_expansion:
+ case isl_schedule_node_extension:
+ case isl_schedule_node_filter:
+ case isl_schedule_node_set:
+ case isl_schedule_node_sequence:
+ return isl_bool_false;
+ }
+
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "unhandled case", return isl_bool_error);
+}
+
+/* Move down to the first descendant of "tree" that contains any schedule
+ * information or return "leaf" if there is no such descendant.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_first_schedule_descendant(
+ __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_tree *leaf)
+{
+ isl_bool down;
+
+ while ((down = domain_less(tree)) == isl_bool_true) {
+ if (!isl_schedule_tree_has_children(tree)) {
+ isl_schedule_tree_free(tree);
+ return isl_schedule_tree_copy(leaf);
+ }
+ tree = isl_schedule_tree_child(tree, 0);
+ }
+
+ if (down < 0)
+ return isl_schedule_tree_free(tree);
+
+ return tree;
+}
+
+static __isl_give isl_union_map *subtree_schedule_extend(
+ __isl_keep isl_schedule_tree *tree, __isl_take isl_union_map *outer);
+
+/* Extend the schedule map "outer" with the subtree schedule
+ * of the (single) child of "tree", if any.
+ *
+ * If "tree" does not have any descendants (apart from those that
+ * do not carry any schedule information), then we simply return "outer".
+ * Otherwise, we extend the schedule map "outer" with the subtree schedule
+ * of the single child.
+ */
+static __isl_give isl_union_map *subtree_schedule_extend_child(
+ __isl_keep isl_schedule_tree *tree, __isl_take isl_union_map *outer)
+{
+ isl_schedule_tree *child;
+ isl_union_map *res;
+
+ if (!tree)
+ return isl_union_map_free(outer);
+ if (!isl_schedule_tree_has_children(tree))
+ return outer;
+ child = isl_schedule_tree_get_child(tree, 0);
+ if (!child)
+ return isl_union_map_free(outer);
+ res = subtree_schedule_extend(child, outer);
+ isl_schedule_tree_free(child);
+ return res;
+}
+
+/* Extract the parameter space from one of the children of "tree",
+ * which are assumed to be filters.
+ */
+static __isl_give isl_space *extract_space_from_filter_child(
+ __isl_keep isl_schedule_tree *tree)
+{
+ isl_space *space;
+ isl_union_set *dom;
+ isl_schedule_tree *child;
+
+ child = isl_schedule_tree_list_get_schedule_tree(tree->children, 0);
+ dom = isl_schedule_tree_filter_get_filter(child);
+ space = isl_union_set_get_space(dom);
+ isl_union_set_free(dom);
+ isl_schedule_tree_free(child);
+
+ return space;
+}
+
+/* Extend the schedule map "outer" with the subtree schedule
+ * of a set or sequence node.
+ *
+ * The schedule for the set or sequence node itself is composed of
+ * pieces of the form
+ *
+ * filter -> []
+ *
+ * or
+ *
+ * filter -> [index]
+ *
+ * The first form is used if there is only a single child or
+ * if the current node is a set node and the schedule_separate_components
+ * option is not set.
+ *
+ * Each of the pieces above is extended with the subtree schedule of
+ * the child of the corresponding filter, if any, padded with zeros
+ * to ensure that all pieces have the same range dimension.
+ */
+static __isl_give isl_union_map *subtree_schedule_extend_from_children(
+ __isl_keep isl_schedule_tree *tree, __isl_take isl_union_map *outer)
+{
+ int i;
+ isl_size n;
+ isl_size dim;
+ int separate;
+ isl_ctx *ctx;
+ isl_val *v = NULL;
+ isl_multi_val *mv;
+ isl_space *space;
+ isl_union_map *umap;
+
+ n = isl_schedule_tree_n_children(tree);
+ if (n < 0)
+ return isl_union_map_free(outer);
+ if (n == 0)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "missing children", return isl_union_map_free(outer));
+
+ ctx = isl_schedule_tree_get_ctx(tree);
+ separate = n > 1 && (tree->type == isl_schedule_node_sequence ||
+ isl_options_get_schedule_separate_components(ctx));
+
+ space = isl_space_params_alloc(ctx, 0);
+
+ umap = isl_union_map_empty(isl_space_copy(space));
+ space = isl_space_set_from_params(space);
+ if (separate) {
+ space = isl_space_add_dims(space, isl_dim_set, 1);
+ v = isl_val_zero(ctx);
+ }
+ mv = isl_multi_val_zero(space);
+
+ dim = isl_multi_val_dim(mv, isl_dim_set);
+ if (dim < 0)
+ umap = isl_union_map_free(umap);
+ for (i = 0; i < n; ++i) {
+ isl_multi_val *mv_copy;
+ isl_union_pw_multi_aff *upma;
+ isl_union_map *umap_i;
+ isl_union_set *dom;
+ isl_schedule_tree *child;
+ isl_size dim_i;
+ isl_bool empty;
+
+ child = isl_schedule_tree_list_get_schedule_tree(
+ tree->children, i);
+ dom = isl_schedule_tree_filter_get_filter(child);
+
+ if (separate) {
+ mv = isl_multi_val_set_val(mv, 0, isl_val_copy(v));
+ v = isl_val_add_ui(v, 1);
+ }
+ mv_copy = isl_multi_val_copy(mv);
+ space = isl_union_set_get_space(dom);
+ mv_copy = isl_multi_val_align_params(mv_copy, space);
+ upma = isl_union_pw_multi_aff_multi_val_on_domain(dom, mv_copy);
+ umap_i = isl_union_map_from_union_pw_multi_aff(upma);
+ umap_i = isl_union_map_flat_range_product(
+ isl_union_map_copy(outer), umap_i);
+ umap_i = subtree_schedule_extend_child(child, umap_i);
+ isl_schedule_tree_free(child);
+
+ empty = isl_union_map_is_empty(umap_i);
+ if (empty < 0)
+ umap_i = isl_union_map_free(umap_i);
+ else if (empty) {
+ isl_union_map_free(umap_i);
+ continue;
+ }
+
+ dim_i = range_dim(umap_i);
+ if (dim_i < 0) {
+ umap = isl_union_map_free(umap);
+ } else if (dim < dim_i) {
+ umap = append_range(umap, dim_i - dim);
+ dim = dim_i;
+ } else if (dim_i < dim) {
+ umap_i = append_range(umap_i, dim - dim_i);
+ }
+ umap = isl_union_map_union(umap, umap_i);
+ }
+
+ isl_val_free(v);
+ isl_multi_val_free(mv);
+ isl_union_map_free(outer);
+
+ return umap;
+}
+
+/* Extend the schedule map "outer" with the subtree schedule of "tree".
+ *
+ * If the root of the tree is a set or a sequence, then we extend
+ * the schedule map in subtree_schedule_extend_from_children.
+ * Otherwise, we extend the schedule map with the partial schedule
+ * corresponding to the root of the tree and then continue with
+ * the single child of this root.
+ * In the special case of an expansion, the schedule map is "extended"
+ * by applying the expansion to the domain of the schedule map.
+ */
+static __isl_give isl_union_map *subtree_schedule_extend(
+ __isl_keep isl_schedule_tree *tree, __isl_take isl_union_map *outer)
+{
+ isl_multi_union_pw_aff *mupa;
+ isl_union_map *umap;
+ isl_union_set *domain;
+ isl_size n;
+
+ if (!tree)
+ return NULL;
+
+ switch (tree->type) {
+ case isl_schedule_node_error:
+ return isl_union_map_free(outer);
+ case isl_schedule_node_extension:
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "cannot construct subtree schedule of tree "
+ "with extension nodes",
+ return isl_union_map_free(outer));
+ case isl_schedule_node_context:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_mark:
+ return subtree_schedule_extend_child(tree, outer);
+ case isl_schedule_node_band:
+ n = isl_schedule_tree_band_n_member(tree);
+ if (n < 0)
+ return isl_union_map_free(outer);
+ if (n == 0)
+ return subtree_schedule_extend_child(tree, outer);
+ mupa = isl_schedule_band_get_partial_schedule(tree->band);
+ umap = isl_union_map_from_multi_union_pw_aff(mupa);
+ outer = isl_union_map_flat_range_product(outer, umap);
+ umap = subtree_schedule_extend_child(tree, outer);
+ break;
+ case isl_schedule_node_domain:
+ domain = isl_schedule_tree_domain_get_domain(tree);
+ umap = isl_union_map_from_domain(domain);
+ outer = isl_union_map_flat_range_product(outer, umap);
+ umap = subtree_schedule_extend_child(tree, outer);
+ break;
+ case isl_schedule_node_expansion:
+ umap = isl_schedule_tree_expansion_get_expansion(tree);
+ outer = isl_union_map_apply_domain(outer, umap);
+ umap = subtree_schedule_extend_child(tree, outer);
+ break;
+ case isl_schedule_node_filter:
+ domain = isl_schedule_tree_filter_get_filter(tree);
+ umap = isl_union_map_from_domain(domain);
+ outer = isl_union_map_flat_range_product(outer, umap);
+ umap = subtree_schedule_extend_child(tree, outer);
+ break;
+ case isl_schedule_node_leaf:
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "leaf node should be handled by caller", return NULL);
+ case isl_schedule_node_set:
+ case isl_schedule_node_sequence:
+ umap = subtree_schedule_extend_from_children(tree, outer);
+ break;
+ }
+
+ return umap;
+}
+
+static __isl_give isl_union_set *initial_domain(
+ __isl_keep isl_schedule_tree *tree);
+
+/* Extract a universe domain from the children of the tree root "tree",
+ * which is a set or sequence, meaning that its children are filters.
+ * In particular, return the union of the universes of the filters.
+ */
+static __isl_give isl_union_set *initial_domain_from_children(
+ __isl_keep isl_schedule_tree *tree)
+{
+ int i;
+ isl_size n;
+ isl_space *space;
+ isl_union_set *domain;
+
+ n = isl_schedule_tree_n_children(tree);
+ if (n < 0)
+ return NULL;
+ if (n == 0)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "missing children", return NULL);
+
+ space = extract_space_from_filter_child(tree);
+ domain = isl_union_set_empty(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_schedule_tree *child;
+ isl_union_set *domain_i;
+
+ child = isl_schedule_tree_get_child(tree, i);
+ domain_i = initial_domain(child);
+ domain = isl_union_set_union(domain, domain_i);
+ isl_schedule_tree_free(child);
+ }
+
+ return domain;
+}
+
+/* Extract a universe domain from the tree root "tree".
+ * The caller is responsible for making sure that this node
+ * would not be skipped by isl_schedule_tree_first_schedule_descendant
+ * and that it is not a leaf node.
+ */
+static __isl_give isl_union_set *initial_domain(
+ __isl_keep isl_schedule_tree *tree)
+{
+ isl_multi_union_pw_aff *mupa;
+ isl_union_set *domain;
+ isl_union_map *exp;
+ isl_size n;
+
+ if (!tree)
+ return NULL;
+
+ switch (tree->type) {
+ case isl_schedule_node_error:
+ return NULL;
+ case isl_schedule_node_context:
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "context node should be handled by caller",
+ return NULL);
+ case isl_schedule_node_guard:
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "guard node should be handled by caller",
+ return NULL);
+ case isl_schedule_node_mark:
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "mark node should be handled by caller",
+ return NULL);
+ case isl_schedule_node_extension:
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "cannot construct subtree schedule of tree "
+ "with extension nodes", return NULL);
+ case isl_schedule_node_band:
+ n = isl_schedule_tree_band_n_member(tree);
+ if (n < 0)
+ return NULL;
+ if (n == 0)
+ isl_die(isl_schedule_tree_get_ctx(tree),
+ isl_error_internal,
+ "0D band should be handled by caller",
+ return NULL);
+ mupa = isl_schedule_band_get_partial_schedule(tree->band);
+ domain = isl_multi_union_pw_aff_domain(mupa);
+ domain = isl_union_set_universe(domain);
+ break;
+ case isl_schedule_node_domain:
+ domain = isl_schedule_tree_domain_get_domain(tree);
+ domain = isl_union_set_universe(domain);
+ break;
+ case isl_schedule_node_expansion:
+ exp = isl_schedule_tree_expansion_get_expansion(tree);
+ exp = isl_union_map_universe(exp);
+ domain = isl_union_map_domain(exp);
+ break;
+ case isl_schedule_node_filter:
+ domain = isl_schedule_tree_filter_get_filter(tree);
+ domain = isl_union_set_universe(domain);
+ break;
+ case isl_schedule_node_leaf:
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "leaf node should be handled by caller", return NULL);
+ case isl_schedule_node_set:
+ case isl_schedule_node_sequence:
+ domain = initial_domain_from_children(tree);
+ break;
+ }
+
+ return domain;
+}
+
+/* Return the subtree schedule of a node that contains some schedule
+ * information, i.e., a node that would not be skipped by
+ * isl_schedule_tree_first_schedule_descendant and that is not a leaf.
+ *
+ * If the tree contains any expansions, then the returned subtree
+ * schedule is formulated in terms of the expanded domains.
+ * The tree is not allowed to contain any extension nodes.
+ *
+ * We start with an initial zero-dimensional subtree schedule based
+ * on the domain information in the root node and then extend it
+ * based on the schedule information in the root node and its descendants.
+ */
+__isl_give isl_union_map *isl_schedule_tree_get_subtree_schedule_union_map(
+ __isl_keep isl_schedule_tree *tree)
+{
+ isl_union_set *domain;
+ isl_union_map *umap;
+
+ domain = initial_domain(tree);
+ umap = isl_union_map_from_domain(domain);
+ return subtree_schedule_extend(tree, umap);
+}
+
+/* Multiply the partial schedule of the band root node of "tree"
+ * with the factors in "mv".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_scale(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv)
+{
+ if (!tree || !mv)
+ goto error;
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", goto error);
+
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ goto error;
+
+ tree->band = isl_schedule_band_scale(tree->band, mv);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_multi_val_free(mv);
+ return NULL;
+}
+
+/* Divide the partial schedule of the band root node of "tree"
+ * by the factors in "mv".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_scale_down(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv)
+{
+ if (!tree || !mv)
+ goto error;
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", goto error);
+
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ goto error;
+
+ tree->band = isl_schedule_band_scale_down(tree->band, mv);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_multi_val_free(mv);
+ return NULL;
+}
+
+/* Reduce the partial schedule of the band root node of "tree"
+ * modulo the factors in "mv".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_mod(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv)
+{
+ if (!tree || !mv)
+ goto error;
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", goto error);
+
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ goto error;
+
+ tree->band = isl_schedule_band_mod(tree->band, mv);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_multi_val_free(mv);
+ return NULL;
+}
+
+/* Shift the partial schedule of the band root node of "tree" by "shift".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_shift(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_multi_union_pw_aff *shift)
+{
+ if (!tree || !shift)
+ goto error;
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", goto error);
+
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ goto error;
+
+ tree->band = isl_schedule_band_shift(tree->band, shift);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+
+ return tree;
+error:
+ isl_schedule_tree_free(tree);
+ isl_multi_union_pw_aff_free(shift);
+ return NULL;
+}
+
+/* Given two trees with sequence roots, replace the child at position
+ * "pos" of "tree" with the children of "child".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_sequence_splice(
+ __isl_take isl_schedule_tree *tree, int pos,
+ __isl_take isl_schedule_tree *child)
+{
+ isl_size n;
+ isl_schedule_tree_list *list1, *list2;
+
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree || !child)
+ goto error;
+ if (isl_schedule_tree_get_type(tree) != isl_schedule_node_sequence)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a sequence node", goto error);
+ n = isl_schedule_tree_n_children(tree);
+ if (n < 0)
+ goto error;
+ if (pos < 0 || pos >= n)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "position out of bounds", goto error);
+ if (isl_schedule_tree_get_type(child) != isl_schedule_node_sequence)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a sequence node", goto error);
+
+ list1 = isl_schedule_tree_list_copy(tree->children);
+ list1 = isl_schedule_tree_list_drop(list1, pos, n - pos);
+ list2 = isl_schedule_tree_list_copy(tree->children);
+ list2 = isl_schedule_tree_list_drop(list2, 0, pos + 1);
+ list1 = isl_schedule_tree_list_concat(list1,
+ isl_schedule_tree_list_copy(child->children));
+ list1 = isl_schedule_tree_list_concat(list1, list2);
+
+ isl_schedule_tree_free(tree);
+ isl_schedule_tree_free(child);
+ return isl_schedule_tree_from_children(isl_schedule_node_sequence,
+ list1);
+error:
+ isl_schedule_tree_free(tree);
+ isl_schedule_tree_free(child);
+ return NULL;
+}
+
+/* Tile the band root node of "tree" with tile sizes "sizes".
+ *
+ * We duplicate the band node, change the schedule of one of them
+ * to the tile schedule and the other to the point schedule and then
+ * attach the point band as a child to the tile band.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_tile(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *sizes)
+{
+ isl_schedule_tree *child = NULL;
+
+ if (!tree || !sizes)
+ goto error;
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", goto error);
+
+ child = isl_schedule_tree_copy(tree);
+ tree = isl_schedule_tree_cow(tree);
+ child = isl_schedule_tree_cow(child);
+ if (!tree || !child)
+ goto error;
+
+ tree->band = isl_schedule_band_tile(tree->band,
+ isl_multi_val_copy(sizes));
+ if (!tree->band)
+ goto error;
+ child->band = isl_schedule_band_point(child->band, tree->band, sizes);
+ if (!child->band)
+ child = isl_schedule_tree_free(child);
+
+ tree = isl_schedule_tree_replace_child(tree, 0, child);
+
+ return tree;
+error:
+ isl_schedule_tree_free(child);
+ isl_schedule_tree_free(tree);
+ isl_multi_val_free(sizes);
+ return NULL;
+}
+
+/* Given an isolate AST generation option "isolate" for a band of size pos + n,
+ * return the corresponding option for a band covering the first "pos"
+ * members.
+ *
+ * The input isolate option is of the form
+ *
+ * isolate[[flattened outer bands] -> [pos; n]]
+ *
+ * The output isolate option is of the form
+ *
+ * isolate[[flattened outer bands] -> [pos]]
+ */
+static __isl_give isl_set *isolate_initial(__isl_keep isl_set *isolate,
+ int pos, int n)
+{
+ isl_id *id;
+ isl_map *map;
+
+ isolate = isl_set_copy(isolate);
+ id = isl_set_get_tuple_id(isolate);
+ map = isl_set_unwrap(isolate);
+ map = isl_map_project_out(map, isl_dim_out, pos, n);
+ isolate = isl_map_wrap(map);
+ isolate = isl_set_set_tuple_id(isolate, id);
+
+ return isolate;
+}
+
+/* Given an isolate AST generation option "isolate" for a band of size pos + n,
+ * return the corresponding option for a band covering the final "n"
+ * members within a band covering the first "pos" members.
+ *
+ * The input isolate option is of the form
+ *
+ * isolate[[flattened outer bands] -> [pos; n]]
+ *
+ * The output isolate option is of the form
+ *
+ * isolate[[flattened outer bands; pos] -> [n]]
+ *
+ *
+ * The range is first split into
+ *
+ * isolate[[flattened outer bands] -> [[pos] -> [n]]]
+ *
+ * and then the first pos members are moved to the domain
+ *
+ * isolate[[[flattened outer bands] -> [pos]] -> [n]]
+ *
+ * after which the domain is flattened to obtain the desired output.
+ */
+static __isl_give isl_set *isolate_final(__isl_keep isl_set *isolate,
+ int pos, int n)
+{
+ isl_id *id;
+ isl_space *space;
+ isl_multi_aff *ma1, *ma2;
+ isl_map *map;
+
+ isolate = isl_set_copy(isolate);
+ id = isl_set_get_tuple_id(isolate);
+ map = isl_set_unwrap(isolate);
+ space = isl_space_range(isl_map_get_space(map));
+ ma1 = isl_multi_aff_project_out_map(isl_space_copy(space),
+ isl_dim_set, pos, n);
+ ma2 = isl_multi_aff_project_out_map(space, isl_dim_set, 0, pos);
+ ma1 = isl_multi_aff_range_product(ma1, ma2);
+ map = isl_map_apply_range(map, isl_map_from_multi_aff(ma1));
+ map = isl_map_uncurry(map);
+ map = isl_map_flatten_domain(map);
+ isolate = isl_map_wrap(map);
+ isolate = isl_set_set_tuple_id(isolate, id);
+
+ return isolate;
+}
+
+/* Split the band root node of "tree" into two nested band nodes,
+ * one with the first "pos" dimensions and
+ * one with the remaining dimensions.
+ * The tree is itself positioned at schedule depth "depth".
+ *
+ * The loop AST generation type options and the isolate option
+ * are split over the two band nodes.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_split(
+ __isl_take isl_schedule_tree *tree, int pos, int depth)
+{
+ isl_size n;
+ isl_set *isolate, *tree_isolate, *child_isolate;
+ isl_schedule_tree *child;
+
+ if (!tree)
+ return NULL;
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", return isl_schedule_tree_free(tree));
+
+ n = isl_schedule_tree_band_n_member(tree);
+ if (n < 0)
+ return isl_schedule_tree_free(tree);
+ if (pos < 0 || pos > n)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "position out of bounds",
+ return isl_schedule_tree_free(tree));
+
+ child = isl_schedule_tree_copy(tree);
+ tree = isl_schedule_tree_cow(tree);
+ child = isl_schedule_tree_cow(child);
+ if (!tree || !child)
+ goto error;
+
+ isolate = isl_schedule_tree_band_get_ast_isolate_option(tree, depth);
+ tree_isolate = isolate_initial(isolate, pos, n - pos);
+ child_isolate = isolate_final(isolate, pos, n - pos);
+ child->band = isl_schedule_band_drop(child->band, 0, pos);
+ child->band = isl_schedule_band_replace_ast_build_option(child->band,
+ isl_set_copy(isolate), child_isolate);
+ tree->band = isl_schedule_band_drop(tree->band, pos, n - pos);
+ tree->band = isl_schedule_band_replace_ast_build_option(tree->band,
+ isl_set_copy(isolate), tree_isolate);
+ isl_set_free(isolate);
+ if (!child->band || !tree->band)
+ goto error;
+
+ tree = isl_schedule_tree_replace_child(tree, 0, child);
+
+ return tree;
+error:
+ isl_schedule_tree_free(child);
+ isl_schedule_tree_free(tree);
+ return NULL;
+}
+
+/* Attach "tree2" at each of the leaves of "tree1".
+ *
+ * If "tree1" does not have any explicit children, then make "tree2"
+ * its single child. Otherwise, attach "tree2" to the leaves of
+ * each of the children of "tree1".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_append_to_leaves(
+ __isl_take isl_schedule_tree *tree1,
+ __isl_take isl_schedule_tree *tree2)
+{
+ int i;
+ isl_size n;
+
+ n = isl_schedule_tree_n_children(tree1);
+ if (n < 0 || !tree2)
+ goto error;
+ if (n == 0) {
+ isl_schedule_tree_list *list;
+ list = isl_schedule_tree_list_from_schedule_tree(tree2);
+ tree1 = isl_schedule_tree_set_children(tree1, list);
+ return tree1;
+ }
+ for (i = 0; i < n; ++i) {
+ isl_schedule_tree *child;
+
+ child = isl_schedule_tree_get_child(tree1, i);
+ child = isl_schedule_tree_append_to_leaves(child,
+ isl_schedule_tree_copy(tree2));
+ tree1 = isl_schedule_tree_replace_child(tree1, i, child);
+ }
+
+ isl_schedule_tree_free(tree2);
+ return tree1;
+error:
+ isl_schedule_tree_free(tree1);
+ isl_schedule_tree_free(tree2);
+ return NULL;
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * in the root of "tree".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_reset_user(
+ __isl_take isl_schedule_tree *tree)
+{
+ if (isl_schedule_tree_is_leaf(tree))
+ return tree;
+
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ return NULL;
+
+ switch (tree->type) {
+ case isl_schedule_node_error:
+ return isl_schedule_tree_free(tree);
+ case isl_schedule_node_band:
+ tree->band = isl_schedule_band_reset_user(tree->band);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_context:
+ tree->context = isl_set_reset_user(tree->context);
+ if (!tree->context)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_domain:
+ tree->domain = isl_union_set_reset_user(tree->domain);
+ if (!tree->domain)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_expansion:
+ tree->contraction =
+ isl_union_pw_multi_aff_reset_user(tree->contraction);
+ tree->expansion = isl_union_map_reset_user(tree->expansion);
+ if (!tree->contraction || !tree->expansion)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_extension:
+ tree->extension = isl_union_map_reset_user(tree->extension);
+ if (!tree->extension)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_filter:
+ tree->filter = isl_union_set_reset_user(tree->filter);
+ if (!tree->filter)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_guard:
+ tree->guard = isl_set_reset_user(tree->guard);
+ if (!tree->guard)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ break;
+ }
+
+ return tree;
+}
+
+/* Align the parameters of the root of "tree" to those of "space".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_align_params(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_space *space)
+{
+ if (!space)
+ goto error;
+
+ if (isl_schedule_tree_is_leaf(tree)) {
+ isl_space_free(space);
+ return tree;
+ }
+
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ goto error;
+
+ switch (tree->type) {
+ case isl_schedule_node_error:
+ goto error;
+ case isl_schedule_node_band:
+ tree->band = isl_schedule_band_align_params(tree->band, space);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_context:
+ tree->context = isl_set_align_params(tree->context, space);
+ if (!tree->context)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_domain:
+ tree->domain = isl_union_set_align_params(tree->domain, space);
+ if (!tree->domain)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_expansion:
+ tree->contraction =
+ isl_union_pw_multi_aff_align_params(tree->contraction,
+ isl_space_copy(space));
+ tree->expansion = isl_union_map_align_params(tree->expansion,
+ space);
+ if (!tree->contraction || !tree->expansion)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_extension:
+ tree->extension = isl_union_map_align_params(tree->extension,
+ space);
+ if (!tree->extension)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_filter:
+ tree->filter = isl_union_set_align_params(tree->filter, space);
+ if (!tree->filter)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_guard:
+ tree->guard = isl_set_align_params(tree->guard, space);
+ if (!tree->guard)
+ return isl_schedule_tree_free(tree);
+ break;
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ isl_space_free(space);
+ break;
+ }
+
+ return tree;
+error:
+ isl_space_free(space);
+ isl_schedule_tree_free(tree);
+ return NULL;
+}
+
+/* Does "tree" involve the iteration domain?
+ * That is, does it need to be modified
+ * by isl_schedule_tree_pullback_union_pw_multi_aff?
+ */
+static int involves_iteration_domain(__isl_keep isl_schedule_tree *tree)
+{
+ if (!tree)
+ return -1;
+
+ switch (tree->type) {
+ case isl_schedule_node_error:
+ return -1;
+ case isl_schedule_node_band:
+ case isl_schedule_node_domain:
+ case isl_schedule_node_expansion:
+ case isl_schedule_node_extension:
+ case isl_schedule_node_filter:
+ return 1;
+ case isl_schedule_node_context:
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ return 0;
+ }
+
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+ "unhandled case", return -1);
+}
+
+/* Compute the pullback of the root node of "tree" by the function
+ * represented by "upma".
+ * In other words, plug in "upma" in the iteration domains of
+ * the root node of "tree".
+ * We currently do not handle expansion nodes.
+ *
+ * We first check if the root node involves any iteration domains.
+ * If so, we handle the specific cases.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_pullback_union_pw_multi_aff(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ int involves;
+
+ if (!tree || !upma)
+ goto error;
+
+ involves = involves_iteration_domain(tree);
+ if (involves < 0)
+ goto error;
+ if (!involves) {
+ isl_union_pw_multi_aff_free(upma);
+ return tree;
+ }
+
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ goto error;
+
+ if (tree->type == isl_schedule_node_band) {
+ tree->band = isl_schedule_band_pullback_union_pw_multi_aff(
+ tree->band, upma);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+ } else if (tree->type == isl_schedule_node_domain) {
+ tree->domain =
+ isl_union_set_preimage_union_pw_multi_aff(tree->domain,
+ upma);
+ if (!tree->domain)
+ return isl_schedule_tree_free(tree);
+ } else if (tree->type == isl_schedule_node_expansion) {
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_unsupported,
+ "cannot pullback expansion node", goto error);
+ } else if (tree->type == isl_schedule_node_extension) {
+ tree->extension =
+ isl_union_map_preimage_range_union_pw_multi_aff(
+ tree->extension, upma);
+ if (!tree->extension)
+ return isl_schedule_tree_free(tree);
+ } else if (tree->type == isl_schedule_node_filter) {
+ tree->filter =
+ isl_union_set_preimage_union_pw_multi_aff(tree->filter,
+ upma);
+ if (!tree->filter)
+ return isl_schedule_tree_free(tree);
+ }
+
+ return tree;
+error:
+ isl_union_pw_multi_aff_free(upma);
+ isl_schedule_tree_free(tree);
+ return NULL;
+}
+
+/* Compute the gist of the band tree root with respect to "context".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_gist(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *context)
+{
+ if (!tree)
+ return NULL;
+ if (tree->type != isl_schedule_node_band)
+ isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+ "not a band node", goto error);
+ tree = isl_schedule_tree_cow(tree);
+ if (!tree)
+ goto error;
+
+ tree->band = isl_schedule_band_gist(tree->band, context);
+ if (!tree->band)
+ return isl_schedule_tree_free(tree);
+ return tree;
+error:
+ isl_union_set_free(context);
+ isl_schedule_tree_free(tree);
+ return NULL;
+}
+
+/* Are any members in "band" marked coincident?
+ */
+static isl_bool any_coincident(__isl_keep isl_schedule_band *band)
+{
+ int i;
+ isl_size n;
+
+ n = isl_schedule_band_n_member(band);
+ if (n < 0)
+ return isl_bool_error;
+ for (i = 0; i < n; ++i) {
+ isl_bool coincident;
+
+ coincident = isl_schedule_band_member_get_coincident(band, i);
+ if (coincident < 0 || coincident)
+ return coincident;
+ }
+
+ return isl_bool_false;
+}
+
+/* Print the band node "band" to "p".
+ *
+ * The permutable and coincident properties are only printed if they
+ * are different from the defaults.
+ * The coincident property is always printed in YAML flow style.
+ */
+static __isl_give isl_printer *print_tree_band(__isl_take isl_printer *p,
+ __isl_keep isl_schedule_band *band)
+{
+ isl_union_set *options;
+ isl_bool empty;
+ isl_bool coincident;
+
+ p = isl_printer_print_str(p, "schedule");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_str(p, "\"");
+ p = isl_printer_print_multi_union_pw_aff(p, band->mupa);
+ p = isl_printer_print_str(p, "\"");
+ if (isl_schedule_band_get_permutable(band)) {
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_str(p, "permutable");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_int(p, 1);
+ }
+ coincident = any_coincident(band);
+ if (coincident < 0)
+ return isl_printer_free(p);
+ if (coincident) {
+ int i;
+ isl_size n;
+ int style;
+
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_str(p, "coincident");
+ p = isl_printer_yaml_next(p);
+ style = isl_printer_get_yaml_style(p);
+ p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_FLOW);
+ p = isl_printer_yaml_start_sequence(p);
+ n = isl_schedule_band_n_member(band);
+ if (n < 0)
+ return isl_printer_free(p);
+ for (i = 0; i < n; ++i) {
+ p = isl_printer_print_int(p,
+ isl_schedule_band_member_get_coincident(band, i));
+ p = isl_printer_yaml_next(p);
+ }
+ p = isl_printer_yaml_end_sequence(p);
+ p = isl_printer_set_yaml_style(p, style);
+ }
+ options = isl_schedule_band_get_ast_build_options(band);
+ empty = isl_union_set_is_empty(options);
+ if (empty < 0)
+ p = isl_printer_free(p);
+ if (!empty) {
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_str(p, "options");
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_str(p, "\"");
+ p = isl_printer_print_union_set(p, options);
+ p = isl_printer_print_str(p, "\"");
+ }
+ isl_union_set_free(options);
+
+ return p;
+}
+
+#undef BASE
+#define BASE str
+#define isl_str const char
+#include "print_yaml_field_templ.c"
+
+#undef BASE
+#define BASE set
+#include "print_yaml_field_templ.c"
+
+#undef BASE
+#define BASE union_set
+#include "print_yaml_field_templ.c"
+
+#undef BASE
+#define BASE union_map
+#include "print_yaml_field_templ.c"
+
+#undef BASE
+#define BASE union_pw_multi_aff
+#include "print_yaml_field_templ.c"
+
+/* Print "tree" to "p".
+ *
+ * If "n_ancestor" is non-negative, then "child_pos" contains the child
+ * positions of a descendant of the current node that should be marked
+ * (by the comment "YOU ARE HERE"). In particular, if "n_ancestor"
+ * is zero, then the current node should be marked.
+ * The marking is only printed in YAML block format.
+ *
+ * Implicit leaf nodes are not printed, except if they correspond
+ * to the node that should be marked.
+ */
+__isl_give isl_printer *isl_printer_print_schedule_tree_mark(
+ __isl_take isl_printer *p, __isl_keep isl_schedule_tree *tree,
+ int n_ancestor, int *child_pos)
+{
+ int i;
+ isl_size n;
+ int sequence = 0;
+ int block;
+
+ block = isl_printer_get_yaml_style(p) == ISL_YAML_STYLE_BLOCK;
+
+ p = isl_printer_yaml_start_mapping(p);
+ if (n_ancestor == 0 && block) {
+ p = isl_printer_print_str(p, "# YOU ARE HERE");
+ p = isl_printer_end_line(p);
+ p = isl_printer_start_line(p);
+ }
+ switch (tree->type) {
+ case isl_schedule_node_error:
+ p = isl_printer_print_str(p, "ERROR");
+ p = isl_printer_yaml_next(p);
+ break;
+ case isl_schedule_node_leaf:
+ p = isl_printer_print_str(p, "leaf");
+ p = isl_printer_yaml_next(p);
+ break;
+ case isl_schedule_node_sequence:
+ p = isl_printer_print_str(p, "sequence");
+ p = isl_printer_yaml_next(p);
+ sequence = 1;
+ break;
+ case isl_schedule_node_set:
+ p = isl_printer_print_str(p, "set");
+ p = isl_printer_yaml_next(p);
+ sequence = 1;
+ break;
+ case isl_schedule_node_context:
+ p = print_yaml_field_set(p, "context", tree->context);
+ break;
+ case isl_schedule_node_domain:
+ p = print_yaml_field_union_set(p, "domain", tree->domain);
+ break;
+ case isl_schedule_node_expansion:
+ p = print_yaml_field_union_pw_multi_aff(p, "contraction",
+ tree->contraction);
+ p = print_yaml_field_union_map(p, "expansion", tree->expansion);
+ break;
+ case isl_schedule_node_extension:
+ p = print_yaml_field_union_map(p, "extension", tree->extension);
+ break;
+ case isl_schedule_node_filter:
+ p = print_yaml_field_union_set(p, "filter", tree->filter);
+ break;
+ case isl_schedule_node_guard:
+ p = print_yaml_field_set(p, "guard", tree->guard);
+ break;
+ case isl_schedule_node_mark:
+ p = print_yaml_field_str(p, "mark",
+ isl_id_get_name(tree->mark));
+ break;
+ case isl_schedule_node_band:
+ p = print_tree_band(p, tree->band);
+ p = isl_printer_yaml_next(p);
+ break;
+ }
+
+ n = isl_schedule_tree_n_children(tree);
+ if (n < 0)
+ return isl_printer_free(p);
+ if (n == 0) {
+ if (n_ancestor > 0 && block) {
+ isl_schedule_tree *leaf;
+
+ p = isl_printer_print_str(p, "child");
+ p = isl_printer_yaml_next(p);
+ leaf = isl_schedule_tree_leaf(isl_printer_get_ctx(p));
+ p = isl_printer_print_schedule_tree_mark(p,
+ leaf, 0, NULL);
+ isl_schedule_tree_free(leaf);
+ p = isl_printer_yaml_next(p);
+ }
+ return isl_printer_yaml_end_mapping(p);
+ }
+
+ if (sequence) {
+ p = isl_printer_yaml_start_sequence(p);
+ } else {
+ p = isl_printer_print_str(p, "child");
+ p = isl_printer_yaml_next(p);
+ }
+
+ for (i = 0; i < n; ++i) {
+ isl_schedule_tree *t;
+
+ t = isl_schedule_tree_get_child(tree, i);
+ if (n_ancestor > 0 && child_pos[0] == i)
+ p = isl_printer_print_schedule_tree_mark(p, t,
+ n_ancestor - 1, child_pos + 1);
+ else
+ p = isl_printer_print_schedule_tree_mark(p, t,
+ -1, NULL);
+ isl_schedule_tree_free(t);
+
+ p = isl_printer_yaml_next(p);
+ }
+
+ if (sequence)
+ p = isl_printer_yaml_end_sequence(p);
+ p = isl_printer_yaml_end_mapping(p);
+
+ return p;
+}
+
+/* Print "tree" to "p".
+ */
+__isl_give isl_printer *isl_printer_print_schedule_tree(
+ __isl_take isl_printer *p, __isl_keep isl_schedule_tree *tree)
+{
+ return isl_printer_print_schedule_tree_mark(p, tree, -1, NULL);
+}
+
+void isl_schedule_tree_dump(__isl_keep isl_schedule_tree *tree)
+{
+ isl_ctx *ctx;
+ isl_printer *printer;
+
+ if (!tree)
+ return;
+
+ ctx = isl_schedule_tree_get_ctx(tree);
+ printer = isl_printer_to_file(ctx, stderr);
+ printer = isl_printer_set_yaml_style(printer, ISL_YAML_STYLE_BLOCK);
+ printer = isl_printer_print_schedule_tree(printer, tree);
+
+ isl_printer_free(printer);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_tree.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_tree.h
new file mode 100644
index 00000000000..a1ecfadda52
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_schedule_tree.h
@@ -0,0 +1,266 @@
+#ifndef ISL_SCHEDLUE_TREE_H
+#define ISL_SCHEDLUE_TREE_H
+
+#include <isl_schedule_band.h>
+#include <isl/schedule.h>
+#include <isl/set.h>
+#include <isl/union_set.h>
+
+struct isl_schedule_tree;
+typedef struct isl_schedule_tree isl_schedule_tree;
+
+ISL_DECLARE_LIST(schedule_tree)
+
+/* A schedule (sub)tree.
+ *
+ * The leaves of a tree are not explicitly represented inside
+ * the isl_schedule_tree, except when the tree consists of only a leaf.
+ *
+ * The "band" field is valid when type is isl_schedule_node_band.
+ * The "context" field is valid when type is isl_schedule_node_context
+ * and represents constraints on the flat product of the outer band nodes,
+ * possibly introducing additional parameters.
+ * The "domain" field is valid when type is isl_schedule_node_domain
+ * and introduces the statement instances scheduled by the tree.
+ *
+ * The "contraction" and "expansion" fields are valid when type
+ * is isl_schedule_node_expansion.
+ * "expansion" expands the reaching domain elements to one or more
+ * domain elements for the subtree.
+ * "contraction" maps these elements back to the corresponding
+ * reaching domain element. It does not involve any domain constraints.
+ *
+ * The "extension" field is valid when the is isl_schedule_node_extension
+ * maps outer schedule dimensions (the flat product of the outer band nodes)
+ * to additional iteration domains.
+ *
+ * The "filter" field is valid when type is isl_schedule_node_filter
+ * and represents the statement instances selected by the node.
+ *
+ * The "guard" field is valid when type is isl_schedule_node_guard
+ * and represents constraints on the flat product of the outer band nodes
+ * that need to be enforced by the outer nodes in the generated AST.
+ *
+ * The "mark" field is valid when type is isl_schedule_node_mark and
+ * identifies the mark.
+ *
+ * The "children" field is valid for all types except
+ * isl_schedule_node_leaf. This field is NULL if there are
+ * no children (except for the implicit leaves).
+ *
+ * anchored is set if the node or any of its descendants depends
+ * on its position in the schedule tree.
+ */
+struct isl_schedule_tree {
+ int ref;
+ isl_ctx *ctx;
+ int anchored;
+ enum isl_schedule_node_type type;
+ union {
+ isl_schedule_band *band;
+ isl_set *context;
+ isl_union_set *domain;
+ struct {
+ isl_union_pw_multi_aff *contraction;
+ isl_union_map *expansion;
+ };
+ isl_union_map *extension;
+ isl_union_set *filter;
+ isl_set *guard;
+ isl_id *mark;
+ };
+ isl_schedule_tree_list *children;
+};
+
+isl_ctx *isl_schedule_tree_get_ctx(__isl_keep isl_schedule_tree *tree);
+enum isl_schedule_node_type isl_schedule_tree_get_type(
+ __isl_keep isl_schedule_tree *tree);
+
+__isl_give isl_schedule_tree *isl_schedule_tree_leaf(isl_ctx *ctx);
+int isl_schedule_tree_is_leaf(__isl_keep isl_schedule_tree *tree);
+
+isl_bool isl_schedule_tree_plain_is_equal(__isl_keep isl_schedule_tree *tree1,
+ __isl_keep isl_schedule_tree *tree2);
+
+__isl_give isl_schedule_tree *isl_schedule_tree_copy(
+ __isl_keep isl_schedule_tree *tree);
+__isl_null isl_schedule_tree *isl_schedule_tree_free(
+ __isl_take isl_schedule_tree *tree);
+
+__isl_give isl_schedule_tree *isl_schedule_tree_from_band(
+ __isl_take isl_schedule_band *band);
+__isl_give isl_schedule_tree *isl_schedule_tree_from_context(
+ __isl_take isl_set *context);
+__isl_give isl_schedule_tree *isl_schedule_tree_from_domain(
+ __isl_take isl_union_set *domain);
+__isl_give isl_schedule_tree *isl_schedule_tree_from_expansion(
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_union_map *expansion);
+__isl_give isl_schedule_tree *isl_schedule_tree_from_extension(
+ __isl_take isl_union_map *extension);
+__isl_give isl_schedule_tree *isl_schedule_tree_from_filter(
+ __isl_take isl_union_set *filter);
+__isl_give isl_schedule_tree *isl_schedule_tree_from_guard(
+ __isl_take isl_set *guard);
+__isl_give isl_schedule_tree *isl_schedule_tree_from_children(
+ enum isl_schedule_node_type type,
+ __isl_take isl_schedule_tree_list *list);
+__isl_give isl_schedule_tree *isl_schedule_tree_from_pair(
+ enum isl_schedule_node_type type, __isl_take isl_schedule_tree *tree1,
+ __isl_take isl_schedule_tree *tree2);
+__isl_give isl_schedule_tree *isl_schedule_tree_sequence_pair(
+ __isl_take isl_schedule_tree *tree1,
+ __isl_take isl_schedule_tree *tree2);
+__isl_give isl_schedule_tree *isl_schedule_tree_set_pair(
+ __isl_take isl_schedule_tree *tree1,
+ __isl_take isl_schedule_tree *tree2);
+
+isl_bool isl_schedule_tree_is_subtree_anchored(
+ __isl_keep isl_schedule_tree *tree);
+
+__isl_give isl_space *isl_schedule_tree_band_get_space(
+ __isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_intersect_domain(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain);
+__isl_give isl_multi_union_pw_aff *isl_schedule_tree_band_get_partial_schedule(
+ __isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_set_partial_schedule(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_multi_union_pw_aff *schedule);
+enum isl_ast_loop_type isl_schedule_tree_band_member_get_ast_loop_type(
+ __isl_keep isl_schedule_tree *tree, int pos);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_member_set_ast_loop_type(
+ __isl_take isl_schedule_tree *tree, int pos,
+ enum isl_ast_loop_type type);
+enum isl_ast_loop_type isl_schedule_tree_band_member_get_isolate_ast_loop_type(
+ __isl_keep isl_schedule_tree *tree, int pos);
+__isl_give isl_schedule_tree *
+isl_schedule_tree_band_member_set_isolate_ast_loop_type(
+ __isl_take isl_schedule_tree *tree, int pos,
+ enum isl_ast_loop_type type);
+__isl_give isl_union_set *isl_schedule_tree_band_get_ast_build_options(
+ __isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_set_ast_build_options(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *options);
+__isl_give isl_set *isl_schedule_tree_band_get_ast_isolate_option(
+ __isl_keep isl_schedule_tree *tree, int depth);
+__isl_give isl_set *isl_schedule_tree_context_get_context(
+ __isl_keep isl_schedule_tree *tree);
+__isl_give isl_union_set *isl_schedule_tree_domain_get_domain(
+ __isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_domain_set_domain(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain);
+__isl_give isl_union_pw_multi_aff *isl_schedule_tree_expansion_get_contraction(
+ __isl_keep isl_schedule_tree *tree);
+__isl_give isl_union_map *isl_schedule_tree_expansion_get_expansion(
+ __isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *
+isl_schedule_tree_expansion_set_contraction_and_expansion(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_union_map *expansion);
+__isl_give isl_union_map *isl_schedule_tree_extension_get_extension(
+ __isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_extension_set_extension(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_union_map *extension);
+__isl_give isl_union_set *isl_schedule_tree_filter_get_filter(
+ __isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_filter_set_filter(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter);
+__isl_give isl_set *isl_schedule_tree_guard_get_guard(
+ __isl_keep isl_schedule_tree *tree);
+__isl_give isl_id *isl_schedule_tree_mark_get_id(
+ __isl_keep isl_schedule_tree *tree);
+
+__isl_give isl_schedule_tree *isl_schedule_tree_first_schedule_descendant(
+ __isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_tree *leaf);
+__isl_give isl_union_map *isl_schedule_tree_get_subtree_schedule_union_map(
+ __isl_keep isl_schedule_tree *tree);
+
+isl_size isl_schedule_tree_band_n_member(__isl_keep isl_schedule_tree *tree);
+
+isl_bool isl_schedule_tree_band_member_get_coincident(
+ __isl_keep isl_schedule_tree *tree, int pos);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_member_set_coincident(
+ __isl_take isl_schedule_tree *tree, int pos, int coincident);
+isl_bool isl_schedule_tree_band_get_permutable(
+ __isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_set_permutable(
+ __isl_take isl_schedule_tree *tree, int permutable);
+
+int isl_schedule_tree_has_children(__isl_keep isl_schedule_tree *tree);
+isl_size isl_schedule_tree_n_children(__isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_get_child(
+ __isl_keep isl_schedule_tree *tree, int pos);
+
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_band(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_schedule_band *band);
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_context(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_set *context);
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_domain(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain);
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_expansion(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ __isl_take isl_union_map *expansion);
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_extension(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_union_map *extension);
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_filter(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter);
+__isl_give isl_schedule_tree *isl_schedule_tree_children_insert_filter(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter);
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_guard(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_set *guard);
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_mark(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_id *mark);
+
+__isl_give isl_schedule_tree *isl_schedule_tree_append_to_leaves(
+ __isl_take isl_schedule_tree *tree1,
+ __isl_take isl_schedule_tree *tree2);
+
+__isl_give isl_schedule_tree *isl_schedule_tree_band_scale(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_scale_down(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_mod(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *mv);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_tile(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_multi_val *sizes);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_shift(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_multi_union_pw_aff *shift);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_split(
+ __isl_take isl_schedule_tree *tree, int pos, int depth);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_gist(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_union_set *context);
+
+__isl_give isl_schedule_tree *isl_schedule_tree_child(
+ __isl_take isl_schedule_tree *tree, int pos);
+__isl_give isl_schedule_tree *isl_schedule_tree_reset_children(
+ __isl_take isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_drop_child(
+ __isl_take isl_schedule_tree *tree, int pos);
+__isl_give isl_schedule_tree *isl_schedule_tree_replace_child(
+ __isl_take isl_schedule_tree *tree, int pos,
+ __isl_take isl_schedule_tree *new_child);
+__isl_give isl_schedule_tree *isl_schedule_tree_sequence_splice(
+ __isl_take isl_schedule_tree *tree, int pos,
+ __isl_take isl_schedule_tree *child);
+
+__isl_give isl_schedule_tree *isl_schedule_tree_reset_user(
+ __isl_take isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_align_params(
+ __isl_take isl_schedule_tree *tree, __isl_take isl_space *space);
+__isl_give isl_schedule_tree *isl_schedule_tree_pullback_union_pw_multi_aff(
+ __isl_take isl_schedule_tree *tree,
+ __isl_take isl_union_pw_multi_aff *upma);
+
+__isl_give isl_printer *isl_printer_print_schedule_tree(
+ __isl_take isl_printer *p, __isl_keep isl_schedule_tree *tree);
+__isl_give isl_printer *isl_printer_print_schedule_tree_mark(
+ __isl_take isl_printer *p, __isl_keep isl_schedule_tree *tree,
+ int n_ancestor, int *child_pos);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scheduler.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scheduler.c
new file mode 100644
index 00000000000..5ab65d22522
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_scheduler.c
@@ -0,0 +1,7661 @@
+/*
+ * Copyright 2011 INRIA Saclay
+ * Copyright 2012-2014 Ecole Normale Superieure
+ * Copyright 2015-2016 Sven Verdoolaege
+ * Copyright 2016 INRIA Paris
+ * Copyright 2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ * and Centre de Recherche Inria de Paris, 2 rue Simone Iff - Voie DQ12,
+ * CS 42112, 75589 Paris Cedex 12, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl_space_private.h>
+#include <isl_aff_private.h>
+#include <isl/hash.h>
+#include <isl/id.h>
+#include <isl/constraint.h>
+#include <isl/schedule.h>
+#include <isl_schedule_constraints.h>
+#include <isl/schedule_node.h>
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+#include <isl/set.h>
+#include <isl_union_set_private.h>
+#include <isl_seq.h>
+#include <isl_tab.h>
+#include <isl_dim_map.h>
+#include <isl/map_to_basic_set.h>
+#include <isl_sort.h>
+#include <isl_options_private.h>
+#include <isl_tarjan.h>
+#include <isl_morph.h>
+#include <isl/ilp.h>
+#include <isl_val_private.h>
+
+/*
+ * The scheduling algorithm implemented in this file was inspired by
+ * Bondhugula et al., "Automatic Transformations for Communication-Minimized
+ * Parallelization and Locality Optimization in the Polyhedral Model".
+ *
+ * For a detailed description of the variant implemented in isl,
+ * see Verdoolaege and Janssens, "Scheduling for PPCG" (2017).
+ */
+
+
+/* Internal information about a node that is used during the construction
+ * of a schedule.
+ * space represents the original space in which the domain lives;
+ * that is, the space is not affected by compression
+ * sched is a matrix representation of the schedule being constructed
+ * for this node; if compressed is set, then this schedule is
+ * defined over the compressed domain space
+ * sched_map is an isl_map representation of the same (partial) schedule
+ * sched_map may be NULL; if compressed is set, then this map
+ * is defined over the uncompressed domain space
+ * rank is the number of linearly independent rows in the linear part
+ * of sched
+ * the rows of "vmap" represent a change of basis for the node
+ * variables; the first rank rows span the linear part of
+ * the schedule rows; the remaining rows are linearly independent
+ * the rows of "indep" represent linear combinations of the schedule
+ * coefficients that are non-zero when the schedule coefficients are
+ * linearly independent of previously computed schedule rows.
+ * start is the first variable in the LP problem in the sequences that
+ * represents the schedule coefficients of this node
+ * nvar is the dimension of the (compressed) domain
+ * nparam is the number of parameters or 0 if we are not constructing
+ * a parametric schedule
+ *
+ * If compressed is set, then hull represents the constraints
+ * that were used to derive the compression, while compress and
+ * decompress map the original space to the compressed space and
+ * vice versa.
+ *
+ * scc is the index of SCC (or WCC) this node belongs to
+ *
+ * "cluster" is only used inside extract_clusters and identifies
+ * the cluster of SCCs that the node belongs to.
+ *
+ * coincident contains a boolean for each of the rows of the schedule,
+ * indicating whether the corresponding scheduling dimension satisfies
+ * the coincidence constraints in the sense that the corresponding
+ * dependence distances are zero.
+ *
+ * If the schedule_treat_coalescing option is set, then
+ * "sizes" contains the sizes of the (compressed) instance set
+ * in each direction. If there is no fixed size in a given direction,
+ * then the corresponding size value is set to infinity.
+ * If the schedule_treat_coalescing option or the schedule_max_coefficient
+ * option is set, then "max" contains the maximal values for
+ * schedule coefficients of the (compressed) variables. If no bound
+ * needs to be imposed on a particular variable, then the corresponding
+ * value is negative.
+ * If not NULL, then "bounds" contains a non-parametric set
+ * in the compressed space that is bounded by the size in each direction.
+ */
+struct isl_sched_node {
+ isl_space *space;
+ int compressed;
+ isl_set *hull;
+ isl_multi_aff *compress;
+ isl_pw_multi_aff *decompress;
+ isl_mat *sched;
+ isl_map *sched_map;
+ int rank;
+ isl_mat *indep;
+ isl_mat *vmap;
+ int start;
+ int nvar;
+ int nparam;
+
+ int scc;
+ int cluster;
+
+ int *coincident;
+
+ isl_multi_val *sizes;
+ isl_basic_set *bounds;
+ isl_vec *max;
+};
+
+static isl_bool node_has_tuples(const void *entry, const void *val)
+{
+ struct isl_sched_node *node = (struct isl_sched_node *)entry;
+ isl_space *space = (isl_space *) val;
+
+ return isl_space_has_equal_tuples(node->space, space);
+}
+
+static int node_scc_exactly(struct isl_sched_node *node, int scc)
+{
+ return node->scc == scc;
+}
+
+static int node_scc_at_most(struct isl_sched_node *node, int scc)
+{
+ return node->scc <= scc;
+}
+
+static int node_scc_at_least(struct isl_sched_node *node, int scc)
+{
+ return node->scc >= scc;
+}
+
+/* An edge in the dependence graph. An edge may be used to
+ * ensure validity of the generated schedule, to minimize the dependence
+ * distance or both
+ *
+ * map is the dependence relation, with i -> j in the map if j depends on i
+ * tagged_condition and tagged_validity contain the union of all tagged
+ * condition or conditional validity dependence relations that
+ * specialize the dependence relation "map"; that is,
+ * if (i -> a) -> (j -> b) is an element of "tagged_condition"
+ * or "tagged_validity", then i -> j is an element of "map".
+ * If these fields are NULL, then they represent the empty relation.
+ * src is the source node
+ * dst is the sink node
+ *
+ * types is a bit vector containing the types of this edge.
+ * validity is set if the edge is used to ensure correctness
+ * coincidence is used to enforce zero dependence distances
+ * proximity is set if the edge is used to minimize dependence distances
+ * condition is set if the edge represents a condition
+ * for a conditional validity schedule constraint
+ * local can only be set for condition edges and indicates that
+ * the dependence distance over the edge should be zero
+ * conditional_validity is set if the edge is used to conditionally
+ * ensure correctness
+ *
+ * For validity edges, start and end mark the sequence of inequality
+ * constraints in the LP problem that encode the validity constraint
+ * corresponding to this edge.
+ *
+ * During clustering, an edge may be marked "no_merge" if it should
+ * not be used to merge clusters.
+ * The weight is also only used during clustering and it is
+ * an indication of how many schedule dimensions on either side
+ * of the schedule constraints can be aligned.
+ * If the weight is negative, then this means that this edge was postponed
+ * by has_bounded_distances or any_no_merge. The original weight can
+ * be retrieved by adding 1 + graph->max_weight, with "graph"
+ * the graph containing this edge.
+ */
+struct isl_sched_edge {
+ isl_map *map;
+ isl_union_map *tagged_condition;
+ isl_union_map *tagged_validity;
+
+ struct isl_sched_node *src;
+ struct isl_sched_node *dst;
+
+ unsigned types;
+
+ int start;
+ int end;
+
+ int no_merge;
+ int weight;
+};
+
+/* Is "edge" marked as being of type "type"?
+ */
+static int is_type(struct isl_sched_edge *edge, enum isl_edge_type type)
+{
+ return ISL_FL_ISSET(edge->types, 1 << type);
+}
+
+/* Mark "edge" as being of type "type".
+ */
+static void set_type(struct isl_sched_edge *edge, enum isl_edge_type type)
+{
+ ISL_FL_SET(edge->types, 1 << type);
+}
+
+/* No longer mark "edge" as being of type "type"?
+ */
+static void clear_type(struct isl_sched_edge *edge, enum isl_edge_type type)
+{
+ ISL_FL_CLR(edge->types, 1 << type);
+}
+
+/* Is "edge" marked as a validity edge?
+ */
+static int is_validity(struct isl_sched_edge *edge)
+{
+ return is_type(edge, isl_edge_validity);
+}
+
+/* Mark "edge" as a validity edge.
+ */
+static void set_validity(struct isl_sched_edge *edge)
+{
+ set_type(edge, isl_edge_validity);
+}
+
+/* Is "edge" marked as a proximity edge?
+ */
+static int is_proximity(struct isl_sched_edge *edge)
+{
+ return is_type(edge, isl_edge_proximity);
+}
+
+/* Is "edge" marked as a local edge?
+ */
+static int is_local(struct isl_sched_edge *edge)
+{
+ return is_type(edge, isl_edge_local);
+}
+
+/* Mark "edge" as a local edge.
+ */
+static void set_local(struct isl_sched_edge *edge)
+{
+ set_type(edge, isl_edge_local);
+}
+
+/* No longer mark "edge" as a local edge.
+ */
+static void clear_local(struct isl_sched_edge *edge)
+{
+ clear_type(edge, isl_edge_local);
+}
+
+/* Is "edge" marked as a coincidence edge?
+ */
+static int is_coincidence(struct isl_sched_edge *edge)
+{
+ return is_type(edge, isl_edge_coincidence);
+}
+
+/* Is "edge" marked as a condition edge?
+ */
+static int is_condition(struct isl_sched_edge *edge)
+{
+ return is_type(edge, isl_edge_condition);
+}
+
+/* Is "edge" marked as a conditional validity edge?
+ */
+static int is_conditional_validity(struct isl_sched_edge *edge)
+{
+ return is_type(edge, isl_edge_conditional_validity);
+}
+
+/* Is "edge" of a type that can appear multiple times between
+ * the same pair of nodes?
+ *
+ * Condition edges and conditional validity edges may have tagged
+ * dependence relations, in which case an edge is added for each
+ * pair of tags.
+ */
+static int is_multi_edge_type(struct isl_sched_edge *edge)
+{
+ return is_condition(edge) || is_conditional_validity(edge);
+}
+
+/* Internal information about the dependence graph used during
+ * the construction of the schedule.
+ *
+ * intra_hmap is a cache, mapping dependence relations to their dual,
+ * for dependences from a node to itself, possibly without
+ * coefficients for the parameters
+ * intra_hmap_param is a cache, mapping dependence relations to their dual,
+ * for dependences from a node to itself, including coefficients
+ * for the parameters
+ * inter_hmap is a cache, mapping dependence relations to their dual,
+ * for dependences between distinct nodes
+ * if compression is involved then the key for these maps
+ * is the original, uncompressed dependence relation, while
+ * the value is the dual of the compressed dependence relation.
+ *
+ * n is the number of nodes
+ * node is the list of nodes
+ * maxvar is the maximal number of variables over all nodes
+ * max_row is the allocated number of rows in the schedule
+ * n_row is the current (maximal) number of linearly independent
+ * rows in the node schedules
+ * n_total_row is the current number of rows in the node schedules
+ * band_start is the starting row in the node schedules of the current band
+ * root is set to the original dependence graph from which this graph
+ * is derived through splitting. If this graph is not the result of
+ * splitting, then the root field points to the graph itself.
+ *
+ * sorted contains a list of node indices sorted according to the
+ * SCC to which a node belongs
+ *
+ * n_edge is the number of edges
+ * edge is the list of edges
+ * max_edge contains the maximal number of edges of each type;
+ * in particular, it contains the number of edges in the inital graph.
+ * edge_table contains pointers into the edge array, hashed on the source
+ * and sink spaces; there is one such table for each type;
+ * a given edge may be referenced from more than one table
+ * if the corresponding relation appears in more than one of the
+ * sets of dependences; however, for each type there is only
+ * a single edge between a given pair of source and sink space
+ * in the entire graph
+ *
+ * node_table contains pointers into the node array, hashed on the space tuples
+ *
+ * region contains a list of variable sequences that should be non-trivial
+ *
+ * lp contains the (I)LP problem used to obtain new schedule rows
+ *
+ * src_scc and dst_scc are the source and sink SCCs of an edge with
+ * conflicting constraints
+ *
+ * scc represents the number of components
+ * weak is set if the components are weakly connected
+ *
+ * max_weight is used during clustering and represents the maximal
+ * weight of the relevant proximity edges.
+ */
+struct isl_sched_graph {
+ isl_map_to_basic_set *intra_hmap;
+ isl_map_to_basic_set *intra_hmap_param;
+ isl_map_to_basic_set *inter_hmap;
+
+ struct isl_sched_node *node;
+ int n;
+ int maxvar;
+ int max_row;
+ int n_row;
+
+ int *sorted;
+
+ int n_total_row;
+ int band_start;
+
+ struct isl_sched_graph *root;
+
+ struct isl_sched_edge *edge;
+ int n_edge;
+ int max_edge[isl_edge_last + 1];
+ struct isl_hash_table *edge_table[isl_edge_last + 1];
+
+ struct isl_hash_table *node_table;
+ struct isl_trivial_region *region;
+
+ isl_basic_set *lp;
+
+ int src_scc;
+ int dst_scc;
+
+ int scc;
+ int weak;
+
+ int max_weight;
+};
+
+/* Initialize node_table based on the list of nodes.
+ */
+static int graph_init_table(isl_ctx *ctx, struct isl_sched_graph *graph)
+{
+ int i;
+
+ graph->node_table = isl_hash_table_alloc(ctx, graph->n);
+ if (!graph->node_table)
+ return -1;
+
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_hash_table_entry *entry;
+ uint32_t hash;
+
+ hash = isl_space_get_tuple_hash(graph->node[i].space);
+ entry = isl_hash_table_find(ctx, graph->node_table, hash,
+ &node_has_tuples,
+ graph->node[i].space, 1);
+ if (!entry)
+ return -1;
+ entry->data = &graph->node[i];
+ }
+
+ return 0;
+}
+
+/* Return a pointer to the node that lives within the given space,
+ * an invalid node if there is no such node, or NULL in case of error.
+ */
+static struct isl_sched_node *graph_find_node(isl_ctx *ctx,
+ struct isl_sched_graph *graph, __isl_keep isl_space *space)
+{
+ struct isl_hash_table_entry *entry;
+ uint32_t hash;
+
+ if (!space)
+ return NULL;
+
+ hash = isl_space_get_tuple_hash(space);
+ entry = isl_hash_table_find(ctx, graph->node_table, hash,
+ &node_has_tuples, space, 0);
+ if (!entry)
+ return NULL;
+ if (entry == isl_hash_table_entry_none)
+ return graph->node + graph->n;
+
+ return entry->data;
+}
+
+/* Is "node" a node in "graph"?
+ */
+static int is_node(struct isl_sched_graph *graph,
+ struct isl_sched_node *node)
+{
+ return node && node >= &graph->node[0] && node < &graph->node[graph->n];
+}
+
+static isl_bool edge_has_src_and_dst(const void *entry, const void *val)
+{
+ const struct isl_sched_edge *edge = entry;
+ const struct isl_sched_edge *temp = val;
+
+ return isl_bool_ok(edge->src == temp->src && edge->dst == temp->dst);
+}
+
+/* Add the given edge to graph->edge_table[type].
+ */
+static isl_stat graph_edge_table_add(isl_ctx *ctx,
+ struct isl_sched_graph *graph, enum isl_edge_type type,
+ struct isl_sched_edge *edge)
+{
+ struct isl_hash_table_entry *entry;
+ uint32_t hash;
+
+ hash = isl_hash_init();
+ hash = isl_hash_builtin(hash, edge->src);
+ hash = isl_hash_builtin(hash, edge->dst);
+ entry = isl_hash_table_find(ctx, graph->edge_table[type], hash,
+ &edge_has_src_and_dst, edge, 1);
+ if (!entry)
+ return isl_stat_error;
+ entry->data = edge;
+
+ return isl_stat_ok;
+}
+
+/* Add "edge" to all relevant edge tables.
+ * That is, for every type of the edge, add it to the corresponding table.
+ */
+static isl_stat graph_edge_tables_add(isl_ctx *ctx,
+ struct isl_sched_graph *graph, struct isl_sched_edge *edge)
+{
+ enum isl_edge_type t;
+
+ for (t = isl_edge_first; t <= isl_edge_last; ++t) {
+ if (!is_type(edge, t))
+ continue;
+ if (graph_edge_table_add(ctx, graph, t, edge) < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Allocate the edge_tables based on the maximal number of edges of
+ * each type.
+ */
+static int graph_init_edge_tables(isl_ctx *ctx, struct isl_sched_graph *graph)
+{
+ int i;
+
+ for (i = 0; i <= isl_edge_last; ++i) {
+ graph->edge_table[i] = isl_hash_table_alloc(ctx,
+ graph->max_edge[i]);
+ if (!graph->edge_table[i])
+ return -1;
+ }
+
+ return 0;
+}
+
+/* If graph->edge_table[type] contains an edge from the given source
+ * to the given destination, then return the hash table entry of this edge.
+ * Otherwise, return NULL.
+ */
+static struct isl_hash_table_entry *graph_find_edge_entry(
+ struct isl_sched_graph *graph,
+ enum isl_edge_type type,
+ struct isl_sched_node *src, struct isl_sched_node *dst)
+{
+ isl_ctx *ctx = isl_space_get_ctx(src->space);
+ uint32_t hash;
+ struct isl_sched_edge temp = { .src = src, .dst = dst };
+
+ hash = isl_hash_init();
+ hash = isl_hash_builtin(hash, temp.src);
+ hash = isl_hash_builtin(hash, temp.dst);
+ return isl_hash_table_find(ctx, graph->edge_table[type], hash,
+ &edge_has_src_and_dst, &temp, 0);
+}
+
+
+/* If graph->edge_table[type] contains an edge from the given source
+ * to the given destination, then return this edge.
+ * Return "none" if no such edge can be found.
+ * Return NULL on error.
+ */
+static struct isl_sched_edge *graph_find_edge(struct isl_sched_graph *graph,
+ enum isl_edge_type type,
+ struct isl_sched_node *src, struct isl_sched_node *dst,
+ struct isl_sched_edge *none)
+{
+ struct isl_hash_table_entry *entry;
+
+ entry = graph_find_edge_entry(graph, type, src, dst);
+ if (!entry)
+ return NULL;
+ if (entry == isl_hash_table_entry_none)
+ return none;
+
+ return entry->data;
+}
+
+/* Check whether the dependence graph has an edge of the given type
+ * between the given two nodes.
+ */
+static isl_bool graph_has_edge(struct isl_sched_graph *graph,
+ enum isl_edge_type type,
+ struct isl_sched_node *src, struct isl_sched_node *dst)
+{
+ struct isl_sched_edge dummy;
+ struct isl_sched_edge *edge;
+ isl_bool empty;
+
+ edge = graph_find_edge(graph, type, src, dst, &dummy);
+ if (!edge)
+ return isl_bool_error;
+ if (edge == &dummy)
+ return isl_bool_false;
+
+ empty = isl_map_plain_is_empty(edge->map);
+
+ return isl_bool_not(empty);
+}
+
+/* Look for any edge with the same src, dst and map fields as "model".
+ *
+ * Return the matching edge if one can be found.
+ * Return "model" if no matching edge is found.
+ * Return NULL on error.
+ */
+static struct isl_sched_edge *graph_find_matching_edge(
+ struct isl_sched_graph *graph, struct isl_sched_edge *model)
+{
+ enum isl_edge_type i;
+ struct isl_sched_edge *edge;
+
+ for (i = isl_edge_first; i <= isl_edge_last; ++i) {
+ int is_equal;
+
+ edge = graph_find_edge(graph, i, model->src, model->dst, model);
+ if (!edge)
+ return NULL;
+ if (edge == model)
+ continue;
+ is_equal = isl_map_plain_is_equal(model->map, edge->map);
+ if (is_equal < 0)
+ return NULL;
+ if (is_equal)
+ return edge;
+ }
+
+ return model;
+}
+
+/* Remove the given edge from all the edge_tables that refer to it.
+ */
+static isl_stat graph_remove_edge(struct isl_sched_graph *graph,
+ struct isl_sched_edge *edge)
+{
+ isl_ctx *ctx = isl_map_get_ctx(edge->map);
+ enum isl_edge_type i;
+
+ for (i = isl_edge_first; i <= isl_edge_last; ++i) {
+ struct isl_hash_table_entry *entry;
+
+ entry = graph_find_edge_entry(graph, i, edge->src, edge->dst);
+ if (!entry)
+ return isl_stat_error;
+ if (entry == isl_hash_table_entry_none)
+ continue;
+ if (entry->data != edge)
+ continue;
+ isl_hash_table_remove(ctx, graph->edge_table[i], entry);
+ }
+
+ return isl_stat_ok;
+}
+
+/* Check whether the dependence graph has any edge
+ * between the given two nodes.
+ */
+static isl_bool graph_has_any_edge(struct isl_sched_graph *graph,
+ struct isl_sched_node *src, struct isl_sched_node *dst)
+{
+ enum isl_edge_type i;
+ isl_bool r;
+
+ for (i = isl_edge_first; i <= isl_edge_last; ++i) {
+ r = graph_has_edge(graph, i, src, dst);
+ if (r < 0 || r)
+ return r;
+ }
+
+ return r;
+}
+
+/* Check whether the dependence graph has a validity edge
+ * between the given two nodes.
+ *
+ * Conditional validity edges are essentially validity edges that
+ * can be ignored if the corresponding condition edges are iteration private.
+ * Here, we are only checking for the presence of validity
+ * edges, so we need to consider the conditional validity edges too.
+ * In particular, this function is used during the detection
+ * of strongly connected components and we cannot ignore
+ * conditional validity edges during this detection.
+ */
+static isl_bool graph_has_validity_edge(struct isl_sched_graph *graph,
+ struct isl_sched_node *src, struct isl_sched_node *dst)
+{
+ isl_bool r;
+
+ r = graph_has_edge(graph, isl_edge_validity, src, dst);
+ if (r < 0 || r)
+ return r;
+
+ return graph_has_edge(graph, isl_edge_conditional_validity, src, dst);
+}
+
+/* Perform all the required memory allocations for a schedule graph "graph"
+ * with "n_node" nodes and "n_edge" edge and initialize the corresponding
+ * fields.
+ */
+static isl_stat graph_alloc(isl_ctx *ctx, struct isl_sched_graph *graph,
+ int n_node, int n_edge)
+{
+ int i;
+
+ graph->n = n_node;
+ graph->n_edge = n_edge;
+ graph->node = isl_calloc_array(ctx, struct isl_sched_node, graph->n);
+ graph->sorted = isl_calloc_array(ctx, int, graph->n);
+ graph->region = isl_alloc_array(ctx,
+ struct isl_trivial_region, graph->n);
+ graph->edge = isl_calloc_array(ctx,
+ struct isl_sched_edge, graph->n_edge);
+
+ graph->intra_hmap = isl_map_to_basic_set_alloc(ctx, 2 * n_edge);
+ graph->intra_hmap_param = isl_map_to_basic_set_alloc(ctx, 2 * n_edge);
+ graph->inter_hmap = isl_map_to_basic_set_alloc(ctx, 2 * n_edge);
+
+ if (!graph->node || !graph->region || (graph->n_edge && !graph->edge) ||
+ !graph->sorted)
+ return isl_stat_error;
+
+ for(i = 0; i < graph->n; ++i)
+ graph->sorted[i] = i;
+
+ return isl_stat_ok;
+}
+
+/* Free the memory associated to node "node" in "graph".
+ * The "coincident" field is shared by nodes in a graph and its subgraph.
+ * It therefore only needs to be freed for the original dependence graph,
+ * i.e., one that is not the result of splitting.
+ */
+static void clear_node(struct isl_sched_graph *graph,
+ struct isl_sched_node *node)
+{
+ isl_space_free(node->space);
+ isl_set_free(node->hull);
+ isl_multi_aff_free(node->compress);
+ isl_pw_multi_aff_free(node->decompress);
+ isl_mat_free(node->sched);
+ isl_map_free(node->sched_map);
+ isl_mat_free(node->indep);
+ isl_mat_free(node->vmap);
+ if (graph->root == graph)
+ free(node->coincident);
+ isl_multi_val_free(node->sizes);
+ isl_basic_set_free(node->bounds);
+ isl_vec_free(node->max);
+}
+
+static void graph_free(isl_ctx *ctx, struct isl_sched_graph *graph)
+{
+ int i;
+
+ isl_map_to_basic_set_free(graph->intra_hmap);
+ isl_map_to_basic_set_free(graph->intra_hmap_param);
+ isl_map_to_basic_set_free(graph->inter_hmap);
+
+ if (graph->node)
+ for (i = 0; i < graph->n; ++i)
+ clear_node(graph, &graph->node[i]);
+ free(graph->node);
+ free(graph->sorted);
+ if (graph->edge)
+ for (i = 0; i < graph->n_edge; ++i) {
+ isl_map_free(graph->edge[i].map);
+ isl_union_map_free(graph->edge[i].tagged_condition);
+ isl_union_map_free(graph->edge[i].tagged_validity);
+ }
+ free(graph->edge);
+ free(graph->region);
+ for (i = 0; i <= isl_edge_last; ++i)
+ isl_hash_table_free(ctx, graph->edge_table[i]);
+ isl_hash_table_free(ctx, graph->node_table);
+ isl_basic_set_free(graph->lp);
+}
+
+/* For each "set" on which this function is called, increment
+ * graph->n by one and update graph->maxvar.
+ */
+static isl_stat init_n_maxvar(__isl_take isl_set *set, void *user)
+{
+ struct isl_sched_graph *graph = user;
+ isl_size nvar = isl_set_dim(set, isl_dim_set);
+
+ graph->n++;
+ if (nvar > graph->maxvar)
+ graph->maxvar = nvar;
+
+ isl_set_free(set);
+
+ if (nvar < 0)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Compute the number of rows that should be allocated for the schedule.
+ * In particular, we need one row for each variable or one row
+ * for each basic map in the dependences.
+ * Note that it is practically impossible to exhaust both
+ * the number of dependences and the number of variables.
+ */
+static isl_stat compute_max_row(struct isl_sched_graph *graph,
+ __isl_keep isl_schedule_constraints *sc)
+{
+ int n_edge;
+ isl_stat r;
+ isl_union_set *domain;
+
+ graph->n = 0;
+ graph->maxvar = 0;
+ domain = isl_schedule_constraints_get_domain(sc);
+ r = isl_union_set_foreach_set(domain, &init_n_maxvar, graph);
+ isl_union_set_free(domain);
+ if (r < 0)
+ return isl_stat_error;
+ n_edge = isl_schedule_constraints_n_basic_map(sc);
+ if (n_edge < 0)
+ return isl_stat_error;
+ graph->max_row = n_edge + graph->maxvar;
+
+ return isl_stat_ok;
+}
+
+/* Does "bset" have any defining equalities for its set variables?
+ */
+static isl_bool has_any_defining_equality(__isl_keep isl_basic_set *bset)
+{
+ int i;
+ isl_size n;
+
+ n = isl_basic_set_dim(bset, isl_dim_set);
+ if (n < 0)
+ return isl_bool_error;
+
+ for (i = 0; i < n; ++i) {
+ isl_bool has;
+
+ has = isl_basic_set_has_defining_equality(bset, isl_dim_set, i,
+ NULL);
+ if (has < 0 || has)
+ return has;
+ }
+
+ return isl_bool_false;
+}
+
+/* Set the entries of node->max to the value of the schedule_max_coefficient
+ * option, if set.
+ */
+static isl_stat set_max_coefficient(isl_ctx *ctx, struct isl_sched_node *node)
+{
+ int max;
+
+ max = isl_options_get_schedule_max_coefficient(ctx);
+ if (max == -1)
+ return isl_stat_ok;
+
+ node->max = isl_vec_alloc(ctx, node->nvar);
+ node->max = isl_vec_set_si(node->max, max);
+ if (!node->max)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Set the entries of node->max to the minimum of the schedule_max_coefficient
+ * option (if set) and half of the minimum of the sizes in the other
+ * dimensions. Round up when computing the half such that
+ * if the minimum of the sizes is one, half of the size is taken to be one
+ * rather than zero.
+ * If the global minimum is unbounded (i.e., if both
+ * the schedule_max_coefficient is not set and the sizes in the other
+ * dimensions are unbounded), then store a negative value.
+ * If the schedule coefficient is close to the size of the instance set
+ * in another dimension, then the schedule may represent a loop
+ * coalescing transformation (especially if the coefficient
+ * in that other dimension is one). Forcing the coefficient to be
+ * smaller than or equal to half the minimal size should avoid this
+ * situation.
+ */
+static isl_stat compute_max_coefficient(isl_ctx *ctx,
+ struct isl_sched_node *node)
+{
+ int max;
+ int i, j;
+ isl_vec *v;
+
+ max = isl_options_get_schedule_max_coefficient(ctx);
+ v = isl_vec_alloc(ctx, node->nvar);
+ if (!v)
+ return isl_stat_error;
+
+ for (i = 0; i < node->nvar; ++i) {
+ isl_int_set_si(v->el[i], max);
+ isl_int_mul_si(v->el[i], v->el[i], 2);
+ }
+
+ for (i = 0; i < node->nvar; ++i) {
+ isl_val *size;
+
+ size = isl_multi_val_get_val(node->sizes, i);
+ if (!size)
+ goto error;
+ if (!isl_val_is_int(size)) {
+ isl_val_free(size);
+ continue;
+ }
+ for (j = 0; j < node->nvar; ++j) {
+ if (j == i)
+ continue;
+ if (isl_int_is_neg(v->el[j]) ||
+ isl_int_gt(v->el[j], size->n))
+ isl_int_set(v->el[j], size->n);
+ }
+ isl_val_free(size);
+ }
+
+ for (i = 0; i < node->nvar; ++i)
+ isl_int_cdiv_q_ui(v->el[i], v->el[i], 2);
+
+ node->max = v;
+ return isl_stat_ok;
+error:
+ isl_vec_free(v);
+ return isl_stat_error;
+}
+
+/* Construct an identifier for node "node", which will represent "set".
+ * The name of the identifier is either "compressed" or
+ * "compressed_<name>", with <name> the name of the space of "set".
+ * The user pointer of the identifier points to "node".
+ */
+static __isl_give isl_id *construct_compressed_id(__isl_keep isl_set *set,
+ struct isl_sched_node *node)
+{
+ isl_bool has_name;
+ isl_ctx *ctx;
+ isl_id *id;
+ isl_printer *p;
+ const char *name;
+ char *id_name;
+
+ has_name = isl_set_has_tuple_name(set);
+ if (has_name < 0)
+ return NULL;
+
+ ctx = isl_set_get_ctx(set);
+ if (!has_name)
+ return isl_id_alloc(ctx, "compressed", node);
+
+ p = isl_printer_to_str(ctx);
+ name = isl_set_get_tuple_name(set);
+ p = isl_printer_print_str(p, "compressed_");
+ p = isl_printer_print_str(p, name);
+ id_name = isl_printer_get_str(p);
+ isl_printer_free(p);
+
+ id = isl_id_alloc(ctx, id_name, node);
+ free(id_name);
+
+ return id;
+}
+
+/* Construct a map that isolates the variable in position "pos" in "set".
+ *
+ * That is, construct
+ *
+ * [i_0, ..., i_pos-1, i_pos+1, ...] -> [i_pos]
+ */
+static __isl_give isl_map *isolate(__isl_take isl_set *set, int pos)
+{
+ isl_map *map;
+
+ map = isl_set_project_onto_map(set, isl_dim_set, pos, 1);
+ map = isl_map_project_out(map, isl_dim_in, pos, 1);
+ return map;
+}
+
+/* Compute and return the size of "set" in dimension "dim".
+ * The size is taken to be the difference in values for that variable
+ * for fixed values of the other variables.
+ * This assumes that "set" is convex.
+ * In particular, the variable is first isolated from the other variables
+ * in the range of a map
+ *
+ * [i_0, ..., i_dim-1, i_dim+1, ...] -> [i_dim]
+ *
+ * and then duplicated
+ *
+ * [i_0, ..., i_dim-1, i_dim+1, ...] -> [[i_dim] -> [i_dim']]
+ *
+ * The shared variables are then projected out and the maximal value
+ * of i_dim' - i_dim is computed.
+ */
+static __isl_give isl_val *compute_size(__isl_take isl_set *set, int dim)
+{
+ isl_map *map;
+ isl_local_space *ls;
+ isl_aff *obj;
+ isl_val *v;
+
+ map = isolate(set, dim);
+ map = isl_map_range_product(map, isl_map_copy(map));
+ map = isl_set_unwrap(isl_map_range(map));
+ set = isl_map_deltas(map);
+ ls = isl_local_space_from_space(isl_set_get_space(set));
+ obj = isl_aff_var_on_domain(ls, isl_dim_set, 0);
+ v = isl_set_max_val(set, obj);
+ isl_aff_free(obj);
+ isl_set_free(set);
+
+ return v;
+}
+
+/* Perform a compression on "node" where "hull" represents the constraints
+ * that were used to derive the compression, while "compress" and
+ * "decompress" map the original space to the compressed space and
+ * vice versa.
+ *
+ * If "node" was not compressed already, then simply store
+ * the compression information.
+ * Otherwise the "original" space is actually the result
+ * of a previous compression, which is then combined
+ * with the present compression.
+ *
+ * The dimensionality of the compressed domain is also adjusted.
+ * Other information, such as the sizes and the maximal coefficient values,
+ * has not been computed yet and therefore does not need to be adjusted.
+ */
+static isl_stat compress_node(struct isl_sched_node *node,
+ __isl_take isl_set *hull, __isl_take isl_multi_aff *compress,
+ __isl_take isl_pw_multi_aff *decompress)
+{
+ node->nvar = isl_multi_aff_dim(compress, isl_dim_out);
+ if (!node->compressed) {
+ node->compressed = 1;
+ node->hull = hull;
+ node->compress = compress;
+ node->decompress = decompress;
+ } else {
+ hull = isl_set_preimage_multi_aff(hull,
+ isl_multi_aff_copy(node->compress));
+ node->hull = isl_set_intersect(node->hull, hull);
+ node->compress = isl_multi_aff_pullback_multi_aff(
+ compress, node->compress);
+ node->decompress = isl_pw_multi_aff_pullback_pw_multi_aff(
+ node->decompress, decompress);
+ }
+
+ if (!node->hull || !node->compress || !node->decompress)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Given that dimension "pos" in "set" has a fixed value
+ * in terms of the other dimensions, (further) compress "node"
+ * by projecting out this dimension.
+ * "set" may be the result of a previous compression.
+ * "uncompressed" is the original domain (without compression).
+ *
+ * The compression function simply projects out the dimension.
+ * The decompression function adds back the dimension
+ * in the right position as an expression of the other dimensions
+ * derived from "set".
+ * As in extract_node, the compressed space has an identifier
+ * that references "node" such that each compressed space is unique and
+ * such that the node can be recovered from the compressed space.
+ *
+ * The constraint removed through the compression is added to the "hull"
+ * such that only edges that relate to the original domains
+ * are taken into account.
+ * In particular, it is obtained by composing compression and decompression and
+ * taking the relation among the variables in the range.
+ */
+static isl_stat project_out_fixed(struct isl_sched_node *node,
+ __isl_keep isl_set *uncompressed, __isl_take isl_set *set, int pos)
+{
+ isl_id *id;
+ isl_space *space;
+ isl_set *domain;
+ isl_map *map;
+ isl_multi_aff *compress;
+ isl_pw_multi_aff *decompress, *pma;
+ isl_multi_pw_aff *mpa;
+ isl_set *hull;
+
+ map = isolate(isl_set_copy(set), pos);
+ pma = isl_pw_multi_aff_from_map(map);
+ domain = isl_pw_multi_aff_domain(isl_pw_multi_aff_copy(pma));
+ pma = isl_pw_multi_aff_gist(pma, domain);
+ space = isl_pw_multi_aff_get_domain_space(pma);
+ mpa = isl_multi_pw_aff_identity(isl_space_map_from_set(space));
+ mpa = isl_multi_pw_aff_range_splice(mpa, pos,
+ isl_multi_pw_aff_from_pw_multi_aff(pma));
+ decompress = isl_pw_multi_aff_from_multi_pw_aff(mpa);
+ space = isl_set_get_space(set);
+ compress = isl_multi_aff_project_out_map(space, isl_dim_set, pos, 1);
+ id = construct_compressed_id(uncompressed, node);
+ compress = isl_multi_aff_set_tuple_id(compress, isl_dim_out, id);
+ space = isl_space_reverse(isl_multi_aff_get_space(compress));
+ decompress = isl_pw_multi_aff_reset_space(decompress, space);
+ pma = isl_pw_multi_aff_pullback_multi_aff(
+ isl_pw_multi_aff_copy(decompress), isl_multi_aff_copy(compress));
+ hull = isl_map_range(isl_map_from_pw_multi_aff(pma));
+
+ isl_set_free(set);
+
+ return compress_node(node, hull, compress, decompress);
+}
+
+/* Compute the size of the compressed domain in each dimension and
+ * store the results in node->sizes.
+ * "uncompressed" is the original domain (without compression).
+ *
+ * First compress the domain if needed and then compute the size
+ * in each direction.
+ * If the domain is not convex, then the sizes are computed
+ * on a convex superset in order to avoid picking up sizes
+ * that are valid for the individual disjuncts, but not for
+ * the domain as a whole.
+ *
+ * If any of the sizes turns out to be zero, then this means
+ * that this dimension has a fixed value in terms of
+ * the other dimensions. Perform an (extra) compression
+ * to remove this dimension.
+ */
+static isl_stat compute_sizes(struct isl_sched_node *node,
+ __isl_keep isl_set *uncompressed)
+{
+ int j;
+ isl_size n;
+ isl_multi_val *mv;
+ isl_set *set = isl_set_copy(uncompressed);
+
+ if (node->compressed)
+ set = isl_set_preimage_pw_multi_aff(set,
+ isl_pw_multi_aff_copy(node->decompress));
+ set = isl_set_from_basic_set(isl_set_simple_hull(set));
+ mv = isl_multi_val_zero(isl_set_get_space(set));
+ n = isl_set_dim(set, isl_dim_set);
+ if (n < 0)
+ mv = isl_multi_val_free(mv);
+ for (j = 0; j < n; ++j) {
+ isl_bool is_zero;
+ isl_val *v;
+
+ v = compute_size(isl_set_copy(set), j);
+ is_zero = isl_val_is_zero(v);
+ mv = isl_multi_val_set_val(mv, j, v);
+ if (is_zero >= 0 && is_zero) {
+ isl_multi_val_free(mv);
+ if (project_out_fixed(node, uncompressed, set, j) < 0)
+ return isl_stat_error;
+ return compute_sizes(node, uncompressed);
+ }
+ }
+ node->sizes = mv;
+ isl_set_free(set);
+ if (!node->sizes)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Compute the size of the instance set "set" of "node", after compression,
+ * as well as bounds on the corresponding coefficients, if needed.
+ *
+ * The sizes are needed when the schedule_treat_coalescing option is set.
+ * The bounds are needed when the schedule_treat_coalescing option or
+ * the schedule_max_coefficient option is set.
+ *
+ * If the schedule_treat_coalescing option is not set, then at most
+ * the bounds need to be set and this is done in set_max_coefficient.
+ * Otherwise, compute the size of the compressed domain
+ * in each direction and store the results in node->size.
+ * Finally, set the bounds on the coefficients based on the sizes
+ * and the schedule_max_coefficient option in compute_max_coefficient.
+ */
+static isl_stat compute_sizes_and_max(isl_ctx *ctx, struct isl_sched_node *node,
+ __isl_take isl_set *set)
+{
+ isl_stat r;
+
+ if (!isl_options_get_schedule_treat_coalescing(ctx)) {
+ isl_set_free(set);
+ return set_max_coefficient(ctx, node);
+ }
+
+ r = compute_sizes(node, set);
+ isl_set_free(set);
+ if (r < 0)
+ return isl_stat_error;
+ return compute_max_coefficient(ctx, node);
+}
+
+/* Add a new node to the graph representing the given instance set.
+ * "nvar" is the (possibly compressed) number of variables and
+ * may be smaller than then number of set variables in "set"
+ * if "compressed" is set.
+ * If "compressed" is set, then "hull" represents the constraints
+ * that were used to derive the compression, while "compress" and
+ * "decompress" map the original space to the compressed space and
+ * vice versa.
+ * If "compressed" is not set, then "hull", "compress" and "decompress"
+ * should be NULL.
+ *
+ * Compute the size of the instance set and bounds on the coefficients,
+ * if needed.
+ */
+static isl_stat add_node(struct isl_sched_graph *graph,
+ __isl_take isl_set *set, int nvar, int compressed,
+ __isl_take isl_set *hull, __isl_take isl_multi_aff *compress,
+ __isl_take isl_pw_multi_aff *decompress)
+{
+ isl_size nparam;
+ isl_ctx *ctx;
+ isl_mat *sched;
+ isl_space *space;
+ int *coincident;
+ struct isl_sched_node *node;
+
+ nparam = isl_set_dim(set, isl_dim_param);
+ if (nparam < 0)
+ goto error;
+
+ ctx = isl_set_get_ctx(set);
+ if (!ctx->opt->schedule_parametric)
+ nparam = 0;
+ sched = isl_mat_alloc(ctx, 0, 1 + nparam + nvar);
+ node = &graph->node[graph->n];
+ graph->n++;
+ space = isl_set_get_space(set);
+ node->space = space;
+ node->nvar = nvar;
+ node->nparam = nparam;
+ node->sched = sched;
+ node->sched_map = NULL;
+ coincident = isl_calloc_array(ctx, int, graph->max_row);
+ node->coincident = coincident;
+ node->compressed = compressed;
+ node->hull = hull;
+ node->compress = compress;
+ node->decompress = decompress;
+ if (compute_sizes_and_max(ctx, node, set) < 0)
+ return isl_stat_error;
+
+ if (!space || !sched || (graph->max_row && !coincident))
+ return isl_stat_error;
+ if (compressed && (!hull || !compress || !decompress))
+ return isl_stat_error;
+
+ return isl_stat_ok;
+error:
+ isl_set_free(set);
+ isl_set_free(hull);
+ isl_multi_aff_free(compress);
+ isl_pw_multi_aff_free(decompress);
+ return isl_stat_error;
+}
+
+/* Add a new node to the graph representing the given set.
+ *
+ * If any of the set variables is defined by an equality, then
+ * we perform variable compression such that we can perform
+ * the scheduling on the compressed domain.
+ * In this case, an identifier is used that references the new node
+ * such that each compressed space is unique and
+ * such that the node can be recovered from the compressed space.
+ */
+static isl_stat extract_node(__isl_take isl_set *set, void *user)
+{
+ isl_size nvar;
+ isl_bool has_equality;
+ isl_id *id;
+ isl_basic_set *hull;
+ isl_set *hull_set;
+ isl_morph *morph;
+ isl_multi_aff *compress, *decompress_ma;
+ isl_pw_multi_aff *decompress;
+ struct isl_sched_graph *graph = user;
+
+ hull = isl_set_affine_hull(isl_set_copy(set));
+ hull = isl_basic_set_remove_divs(hull);
+ nvar = isl_set_dim(set, isl_dim_set);
+ has_equality = has_any_defining_equality(hull);
+
+ if (nvar < 0 || has_equality < 0)
+ goto error;
+ if (!has_equality) {
+ isl_basic_set_free(hull);
+ return add_node(graph, set, nvar, 0, NULL, NULL, NULL);
+ }
+
+ id = construct_compressed_id(set, &graph->node[graph->n]);
+ morph = isl_basic_set_variable_compression_with_id(hull, id);
+ isl_id_free(id);
+ nvar = isl_morph_ran_dim(morph, isl_dim_set);
+ if (nvar < 0)
+ set = isl_set_free(set);
+ compress = isl_morph_get_var_multi_aff(morph);
+ morph = isl_morph_inverse(morph);
+ decompress_ma = isl_morph_get_var_multi_aff(morph);
+ decompress = isl_pw_multi_aff_from_multi_aff(decompress_ma);
+ isl_morph_free(morph);
+
+ hull_set = isl_set_from_basic_set(hull);
+ return add_node(graph, set, nvar, 1, hull_set, compress, decompress);
+error:
+ isl_basic_set_free(hull);
+ isl_set_free(set);
+ return isl_stat_error;
+}
+
+struct isl_extract_edge_data {
+ enum isl_edge_type type;
+ struct isl_sched_graph *graph;
+};
+
+/* Merge edge2 into edge1, freeing the contents of edge2.
+ * Return 0 on success and -1 on failure.
+ *
+ * edge1 and edge2 are assumed to have the same value for the map field.
+ */
+static int merge_edge(struct isl_sched_edge *edge1,
+ struct isl_sched_edge *edge2)
+{
+ edge1->types |= edge2->types;
+ isl_map_free(edge2->map);
+
+ if (is_condition(edge2)) {
+ if (!edge1->tagged_condition)
+ edge1->tagged_condition = edge2->tagged_condition;
+ else
+ edge1->tagged_condition =
+ isl_union_map_union(edge1->tagged_condition,
+ edge2->tagged_condition);
+ }
+
+ if (is_conditional_validity(edge2)) {
+ if (!edge1->tagged_validity)
+ edge1->tagged_validity = edge2->tagged_validity;
+ else
+ edge1->tagged_validity =
+ isl_union_map_union(edge1->tagged_validity,
+ edge2->tagged_validity);
+ }
+
+ if (is_condition(edge2) && !edge1->tagged_condition)
+ return -1;
+ if (is_conditional_validity(edge2) && !edge1->tagged_validity)
+ return -1;
+
+ return 0;
+}
+
+/* Insert dummy tags in domain and range of "map".
+ *
+ * In particular, if "map" is of the form
+ *
+ * A -> B
+ *
+ * then return
+ *
+ * [A -> dummy_tag] -> [B -> dummy_tag]
+ *
+ * where the dummy_tags are identical and equal to any dummy tags
+ * introduced by any other call to this function.
+ */
+static __isl_give isl_map *insert_dummy_tags(__isl_take isl_map *map)
+{
+ static char dummy;
+ isl_ctx *ctx;
+ isl_id *id;
+ isl_space *space;
+ isl_set *domain, *range;
+
+ ctx = isl_map_get_ctx(map);
+
+ id = isl_id_alloc(ctx, NULL, &dummy);
+ space = isl_space_params(isl_map_get_space(map));
+ space = isl_space_set_from_params(space);
+ space = isl_space_set_tuple_id(space, isl_dim_set, id);
+ space = isl_space_map_from_set(space);
+
+ domain = isl_map_wrap(map);
+ range = isl_map_wrap(isl_map_universe(space));
+ map = isl_map_from_domain_and_range(domain, range);
+ map = isl_map_zip(map);
+
+ return map;
+}
+
+/* Given that at least one of "src" or "dst" is compressed, return
+ * a map between the spaces of these nodes restricted to the affine
+ * hull that was used in the compression.
+ */
+static __isl_give isl_map *extract_hull(struct isl_sched_node *src,
+ struct isl_sched_node *dst)
+{
+ isl_set *dom, *ran;
+
+ if (src->compressed)
+ dom = isl_set_copy(src->hull);
+ else
+ dom = isl_set_universe(isl_space_copy(src->space));
+ if (dst->compressed)
+ ran = isl_set_copy(dst->hull);
+ else
+ ran = isl_set_universe(isl_space_copy(dst->space));
+
+ return isl_map_from_domain_and_range(dom, ran);
+}
+
+/* Intersect the domains of the nested relations in domain and range
+ * of "tagged" with "map".
+ */
+static __isl_give isl_map *map_intersect_domains(__isl_take isl_map *tagged,
+ __isl_keep isl_map *map)
+{
+ isl_set *set;
+
+ tagged = isl_map_zip(tagged);
+ set = isl_map_wrap(isl_map_copy(map));
+ tagged = isl_map_intersect_domain(tagged, set);
+ tagged = isl_map_zip(tagged);
+ return tagged;
+}
+
+/* Return a pointer to the node that lives in the domain space of "map",
+ * an invalid node if there is no such node, or NULL in case of error.
+ */
+static struct isl_sched_node *find_domain_node(isl_ctx *ctx,
+ struct isl_sched_graph *graph, __isl_keep isl_map *map)
+{
+ struct isl_sched_node *node;
+ isl_space *space;
+
+ space = isl_space_domain(isl_map_get_space(map));
+ node = graph_find_node(ctx, graph, space);
+ isl_space_free(space);
+
+ return node;
+}
+
+/* Return a pointer to the node that lives in the range space of "map",
+ * an invalid node if there is no such node, or NULL in case of error.
+ */
+static struct isl_sched_node *find_range_node(isl_ctx *ctx,
+ struct isl_sched_graph *graph, __isl_keep isl_map *map)
+{
+ struct isl_sched_node *node;
+ isl_space *space;
+
+ space = isl_space_range(isl_map_get_space(map));
+ node = graph_find_node(ctx, graph, space);
+ isl_space_free(space);
+
+ return node;
+}
+
+/* Refrain from adding a new edge based on "map".
+ * Instead, just free the map.
+ * "tagged" is either a copy of "map" with additional tags or NULL.
+ */
+static isl_stat skip_edge(__isl_take isl_map *map, __isl_take isl_map *tagged)
+{
+ isl_map_free(map);
+ isl_map_free(tagged);
+
+ return isl_stat_ok;
+}
+
+/* Add a new edge to the graph based on the given map
+ * and add it to data->graph->edge_table[data->type].
+ * If a dependence relation of a given type happens to be identical
+ * to one of the dependence relations of a type that was added before,
+ * then we don't create a new edge, but instead mark the original edge
+ * as also representing a dependence of the current type.
+ *
+ * Edges of type isl_edge_condition or isl_edge_conditional_validity
+ * may be specified as "tagged" dependence relations. That is, "map"
+ * may contain elements (i -> a) -> (j -> b), where i -> j denotes
+ * the dependence on iterations and a and b are tags.
+ * edge->map is set to the relation containing the elements i -> j,
+ * while edge->tagged_condition and edge->tagged_validity contain
+ * the union of all the "map" relations
+ * for which extract_edge is called that result in the same edge->map.
+ *
+ * If the source or the destination node is compressed, then
+ * intersect both "map" and "tagged" with the constraints that
+ * were used to construct the compression.
+ * This ensures that there are no schedule constraints defined
+ * outside of these domains, while the scheduler no longer has
+ * any control over those outside parts.
+ */
+static isl_stat extract_edge(__isl_take isl_map *map, void *user)
+{
+ isl_bool empty;
+ isl_ctx *ctx = isl_map_get_ctx(map);
+ struct isl_extract_edge_data *data = user;
+ struct isl_sched_graph *graph = data->graph;
+ struct isl_sched_node *src, *dst;
+ struct isl_sched_edge *edge;
+ isl_map *tagged = NULL;
+
+ if (data->type == isl_edge_condition ||
+ data->type == isl_edge_conditional_validity) {
+ if (isl_map_can_zip(map)) {
+ tagged = isl_map_copy(map);
+ map = isl_set_unwrap(isl_map_domain(isl_map_zip(map)));
+ } else {
+ tagged = insert_dummy_tags(isl_map_copy(map));
+ }
+ }
+
+ src = find_domain_node(ctx, graph, map);
+ dst = find_range_node(ctx, graph, map);
+
+ if (!src || !dst)
+ goto error;
+ if (!is_node(graph, src) || !is_node(graph, dst))
+ return skip_edge(map, tagged);
+
+ if (src->compressed || dst->compressed) {
+ isl_map *hull;
+ hull = extract_hull(src, dst);
+ if (tagged)
+ tagged = map_intersect_domains(tagged, hull);
+ map = isl_map_intersect(map, hull);
+ }
+
+ empty = isl_map_plain_is_empty(map);
+ if (empty < 0)
+ goto error;
+ if (empty)
+ return skip_edge(map, tagged);
+
+ graph->edge[graph->n_edge].src = src;
+ graph->edge[graph->n_edge].dst = dst;
+ graph->edge[graph->n_edge].map = map;
+ graph->edge[graph->n_edge].types = 0;
+ graph->edge[graph->n_edge].tagged_condition = NULL;
+ graph->edge[graph->n_edge].tagged_validity = NULL;
+ set_type(&graph->edge[graph->n_edge], data->type);
+ if (data->type == isl_edge_condition)
+ graph->edge[graph->n_edge].tagged_condition =
+ isl_union_map_from_map(tagged);
+ if (data->type == isl_edge_conditional_validity)
+ graph->edge[graph->n_edge].tagged_validity =
+ isl_union_map_from_map(tagged);
+
+ edge = graph_find_matching_edge(graph, &graph->edge[graph->n_edge]);
+ if (!edge) {
+ graph->n_edge++;
+ return isl_stat_error;
+ }
+ if (edge == &graph->edge[graph->n_edge])
+ return graph_edge_table_add(ctx, graph, data->type,
+ &graph->edge[graph->n_edge++]);
+
+ if (merge_edge(edge, &graph->edge[graph->n_edge]) < 0)
+ return isl_stat_error;
+
+ return graph_edge_table_add(ctx, graph, data->type, edge);
+error:
+ isl_map_free(map);
+ isl_map_free(tagged);
+ return isl_stat_error;
+}
+
+/* Initialize the schedule graph "graph" from the schedule constraints "sc".
+ *
+ * The context is included in the domain before the nodes of
+ * the graphs are extracted in order to be able to exploit
+ * any possible additional equalities.
+ * Note that this intersection is only performed locally here.
+ */
+static isl_stat graph_init(struct isl_sched_graph *graph,
+ __isl_keep isl_schedule_constraints *sc)
+{
+ isl_ctx *ctx;
+ isl_union_set *domain;
+ isl_union_map *c;
+ struct isl_extract_edge_data data;
+ enum isl_edge_type i;
+ isl_stat r;
+ isl_size n;
+
+ if (!sc)
+ return isl_stat_error;
+
+ ctx = isl_schedule_constraints_get_ctx(sc);
+
+ domain = isl_schedule_constraints_get_domain(sc);
+ n = isl_union_set_n_set(domain);
+ graph->n = n;
+ isl_union_set_free(domain);
+ if (n < 0)
+ return isl_stat_error;
+
+ n = isl_schedule_constraints_n_map(sc);
+ if (n < 0 || graph_alloc(ctx, graph, graph->n, n) < 0)
+ return isl_stat_error;
+
+ if (compute_max_row(graph, sc) < 0)
+ return isl_stat_error;
+ graph->root = graph;
+ graph->n = 0;
+ domain = isl_schedule_constraints_get_domain(sc);
+ domain = isl_union_set_intersect_params(domain,
+ isl_schedule_constraints_get_context(sc));
+ r = isl_union_set_foreach_set(domain, &extract_node, graph);
+ isl_union_set_free(domain);
+ if (r < 0)
+ return isl_stat_error;
+ if (graph_init_table(ctx, graph) < 0)
+ return isl_stat_error;
+ for (i = isl_edge_first; i <= isl_edge_last; ++i) {
+ isl_size n;
+
+ c = isl_schedule_constraints_get(sc, i);
+ n = isl_union_map_n_map(c);
+ graph->max_edge[i] = n;
+ isl_union_map_free(c);
+ if (n < 0)
+ return isl_stat_error;
+ }
+ if (graph_init_edge_tables(ctx, graph) < 0)
+ return isl_stat_error;
+ graph->n_edge = 0;
+ data.graph = graph;
+ for (i = isl_edge_first; i <= isl_edge_last; ++i) {
+ isl_stat r;
+
+ data.type = i;
+ c = isl_schedule_constraints_get(sc, i);
+ r = isl_union_map_foreach_map(c, &extract_edge, &data);
+ isl_union_map_free(c);
+ if (r < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Check whether there is any dependence from node[j] to node[i]
+ * or from node[i] to node[j].
+ */
+static isl_bool node_follows_weak(int i, int j, void *user)
+{
+ isl_bool f;
+ struct isl_sched_graph *graph = user;
+
+ f = graph_has_any_edge(graph, &graph->node[j], &graph->node[i]);
+ if (f < 0 || f)
+ return f;
+ return graph_has_any_edge(graph, &graph->node[i], &graph->node[j]);
+}
+
+/* Check whether there is a (conditional) validity dependence from node[j]
+ * to node[i], forcing node[i] to follow node[j].
+ */
+static isl_bool node_follows_strong(int i, int j, void *user)
+{
+ struct isl_sched_graph *graph = user;
+
+ return graph_has_validity_edge(graph, &graph->node[j], &graph->node[i]);
+}
+
+/* Use Tarjan's algorithm for computing the strongly connected components
+ * in the dependence graph only considering those edges defined by "follows".
+ */
+static isl_stat detect_ccs(isl_ctx *ctx, struct isl_sched_graph *graph,
+ isl_bool (*follows)(int i, int j, void *user))
+{
+ int i, n;
+ struct isl_tarjan_graph *g = NULL;
+
+ g = isl_tarjan_graph_init(ctx, graph->n, follows, graph);
+ if (!g)
+ return isl_stat_error;
+
+ graph->scc = 0;
+ i = 0;
+ n = graph->n;
+ while (n) {
+ while (g->order[i] != -1) {
+ graph->node[g->order[i]].scc = graph->scc;
+ --n;
+ ++i;
+ }
+ ++i;
+ graph->scc++;
+ }
+
+ isl_tarjan_graph_free(g);
+
+ return isl_stat_ok;
+}
+
+/* Apply Tarjan's algorithm to detect the strongly connected components
+ * in the dependence graph.
+ * Only consider the (conditional) validity dependences and clear "weak".
+ */
+static isl_stat detect_sccs(isl_ctx *ctx, struct isl_sched_graph *graph)
+{
+ graph->weak = 0;
+ return detect_ccs(ctx, graph, &node_follows_strong);
+}
+
+/* Apply Tarjan's algorithm to detect the (weakly) connected components
+ * in the dependence graph.
+ * Consider all dependences and set "weak".
+ */
+static isl_stat detect_wccs(isl_ctx *ctx, struct isl_sched_graph *graph)
+{
+ graph->weak = 1;
+ return detect_ccs(ctx, graph, &node_follows_weak);
+}
+
+static int cmp_scc(const void *a, const void *b, void *data)
+{
+ struct isl_sched_graph *graph = data;
+ const int *i1 = a;
+ const int *i2 = b;
+
+ return graph->node[*i1].scc - graph->node[*i2].scc;
+}
+
+/* Sort the elements of graph->sorted according to the corresponding SCCs.
+ */
+static int sort_sccs(struct isl_sched_graph *graph)
+{
+ return isl_sort(graph->sorted, graph->n, sizeof(int), &cmp_scc, graph);
+}
+
+/* Return a non-parametric set in the compressed space of "node" that is
+ * bounded by the size in each direction
+ *
+ * { [x] : -S_i <= x_i <= S_i }
+ *
+ * If S_i is infinity in direction i, then there are no constraints
+ * in that direction.
+ *
+ * Cache the result in node->bounds.
+ */
+static __isl_give isl_basic_set *get_size_bounds(struct isl_sched_node *node)
+{
+ isl_space *space;
+ isl_basic_set *bounds;
+ int i;
+
+ if (node->bounds)
+ return isl_basic_set_copy(node->bounds);
+
+ if (node->compressed)
+ space = isl_pw_multi_aff_get_domain_space(node->decompress);
+ else
+ space = isl_space_copy(node->space);
+ space = isl_space_drop_all_params(space);
+ bounds = isl_basic_set_universe(space);
+
+ for (i = 0; i < node->nvar; ++i) {
+ isl_val *size;
+
+ size = isl_multi_val_get_val(node->sizes, i);
+ if (!size)
+ return isl_basic_set_free(bounds);
+ if (!isl_val_is_int(size)) {
+ isl_val_free(size);
+ continue;
+ }
+ bounds = isl_basic_set_upper_bound_val(bounds, isl_dim_set, i,
+ isl_val_copy(size));
+ bounds = isl_basic_set_lower_bound_val(bounds, isl_dim_set, i,
+ isl_val_neg(size));
+ }
+
+ node->bounds = isl_basic_set_copy(bounds);
+ return bounds;
+}
+
+/* Compress the dependence relation "map", if needed, i.e.,
+ * when the source node "src" and/or the destination node "dst"
+ * has been compressed.
+ */
+static __isl_give isl_map *compress(__isl_take isl_map *map,
+ struct isl_sched_node *src, struct isl_sched_node *dst)
+{
+ if (src->compressed)
+ map = isl_map_preimage_domain_pw_multi_aff(map,
+ isl_pw_multi_aff_copy(src->decompress));
+ if (dst->compressed)
+ map = isl_map_preimage_range_pw_multi_aff(map,
+ isl_pw_multi_aff_copy(dst->decompress));
+ return map;
+}
+
+/* Drop some constraints from "delta" that could be exploited
+ * to construct loop coalescing schedules.
+ * In particular, drop those constraint that bound the difference
+ * to the size of the domain.
+ * First project out the parameters to improve the effectiveness.
+ */
+static __isl_give isl_set *drop_coalescing_constraints(
+ __isl_take isl_set *delta, struct isl_sched_node *node)
+{
+ isl_size nparam;
+ isl_basic_set *bounds;
+
+ nparam = isl_set_dim(delta, isl_dim_param);
+ if (nparam < 0)
+ return isl_set_free(delta);
+
+ bounds = get_size_bounds(node);
+
+ delta = isl_set_project_out(delta, isl_dim_param, 0, nparam);
+ delta = isl_set_remove_divs(delta);
+ delta = isl_set_plain_gist_basic_set(delta, bounds);
+ return delta;
+}
+
+/* Given a dependence relation R from "node" to itself,
+ * construct the set of coefficients of valid constraints for elements
+ * in that dependence relation.
+ * In particular, the result contains tuples of coefficients
+ * c_0, c_n, c_x such that
+ *
+ * c_0 + c_n n + c_x y - c_x x >= 0 for each (x,y) in R
+ *
+ * or, equivalently,
+ *
+ * c_0 + c_n n + c_x d >= 0 for each d in delta R = { y - x | (x,y) in R }
+ *
+ * We choose here to compute the dual of delta R.
+ * Alternatively, we could have computed the dual of R, resulting
+ * in a set of tuples c_0, c_n, c_x, c_y, and then
+ * plugged in (c_0, c_n, c_x, -c_x).
+ *
+ * If "need_param" is set, then the resulting coefficients effectively
+ * include coefficients for the parameters c_n. Otherwise, they may
+ * have been projected out already.
+ * Since the constraints may be different for these two cases,
+ * they are stored in separate caches.
+ * In particular, if no parameter coefficients are required and
+ * the schedule_treat_coalescing option is set, then the parameters
+ * are projected out and some constraints that could be exploited
+ * to construct coalescing schedules are removed before the dual
+ * is computed.
+ *
+ * If "node" has been compressed, then the dependence relation
+ * is also compressed before the set of coefficients is computed.
+ */
+static __isl_give isl_basic_set *intra_coefficients(
+ struct isl_sched_graph *graph, struct isl_sched_node *node,
+ __isl_take isl_map *map, int need_param)
+{
+ isl_ctx *ctx;
+ isl_set *delta;
+ isl_map *key;
+ isl_basic_set *coef;
+ isl_maybe_isl_basic_set m;
+ isl_map_to_basic_set **hmap = &graph->intra_hmap;
+ int treat;
+
+ if (!map)
+ return NULL;
+
+ ctx = isl_map_get_ctx(map);
+ treat = !need_param && isl_options_get_schedule_treat_coalescing(ctx);
+ if (!treat)
+ hmap = &graph->intra_hmap_param;
+ m = isl_map_to_basic_set_try_get(*hmap, map);
+ if (m.valid < 0 || m.valid) {
+ isl_map_free(map);
+ return m.value;
+ }
+
+ key = isl_map_copy(map);
+ map = compress(map, node, node);
+ delta = isl_map_deltas(map);
+ if (treat)
+ delta = drop_coalescing_constraints(delta, node);
+ delta = isl_set_remove_divs(delta);
+ coef = isl_set_coefficients(delta);
+ *hmap = isl_map_to_basic_set_set(*hmap, key, isl_basic_set_copy(coef));
+
+ return coef;
+}
+
+/* Given a dependence relation R, construct the set of coefficients
+ * of valid constraints for elements in that dependence relation.
+ * In particular, the result contains tuples of coefficients
+ * c_0, c_n, c_x, c_y such that
+ *
+ * c_0 + c_n n + c_x x + c_y y >= 0 for each (x,y) in R
+ *
+ * If the source or destination nodes of "edge" have been compressed,
+ * then the dependence relation is also compressed before
+ * the set of coefficients is computed.
+ */
+static __isl_give isl_basic_set *inter_coefficients(
+ struct isl_sched_graph *graph, struct isl_sched_edge *edge,
+ __isl_take isl_map *map)
+{
+ isl_set *set;
+ isl_map *key;
+ isl_basic_set *coef;
+ isl_maybe_isl_basic_set m;
+
+ m = isl_map_to_basic_set_try_get(graph->inter_hmap, map);
+ if (m.valid < 0 || m.valid) {
+ isl_map_free(map);
+ return m.value;
+ }
+
+ key = isl_map_copy(map);
+ map = compress(map, edge->src, edge->dst);
+ set = isl_map_wrap(isl_map_remove_divs(map));
+ coef = isl_set_coefficients(set);
+ graph->inter_hmap = isl_map_to_basic_set_set(graph->inter_hmap, key,
+ isl_basic_set_copy(coef));
+
+ return coef;
+}
+
+/* Return the position of the coefficients of the variables in
+ * the coefficients constraints "coef".
+ *
+ * The space of "coef" is of the form
+ *
+ * { coefficients[[cst, params] -> S] }
+ *
+ * Return the position of S.
+ */
+static isl_size coef_var_offset(__isl_keep isl_basic_set *coef)
+{
+ isl_size offset;
+ isl_space *space;
+
+ space = isl_space_unwrap(isl_basic_set_get_space(coef));
+ offset = isl_space_dim(space, isl_dim_in);
+ isl_space_free(space);
+
+ return offset;
+}
+
+/* Return the offset of the coefficient of the constant term of "node"
+ * within the (I)LP.
+ *
+ * Within each node, the coefficients have the following order:
+ * - positive and negative parts of c_i_x
+ * - c_i_n (if parametric)
+ * - c_i_0
+ */
+static int node_cst_coef_offset(struct isl_sched_node *node)
+{
+ return node->start + 2 * node->nvar + node->nparam;
+}
+
+/* Return the offset of the coefficients of the parameters of "node"
+ * within the (I)LP.
+ *
+ * Within each node, the coefficients have the following order:
+ * - positive and negative parts of c_i_x
+ * - c_i_n (if parametric)
+ * - c_i_0
+ */
+static int node_par_coef_offset(struct isl_sched_node *node)
+{
+ return node->start + 2 * node->nvar;
+}
+
+/* Return the offset of the coefficients of the variables of "node"
+ * within the (I)LP.
+ *
+ * Within each node, the coefficients have the following order:
+ * - positive and negative parts of c_i_x
+ * - c_i_n (if parametric)
+ * - c_i_0
+ */
+static int node_var_coef_offset(struct isl_sched_node *node)
+{
+ return node->start;
+}
+
+/* Return the position of the pair of variables encoding
+ * coefficient "i" of "node".
+ *
+ * The order of these variable pairs is the opposite of
+ * that of the coefficients, with 2 variables per coefficient.
+ */
+static int node_var_coef_pos(struct isl_sched_node *node, int i)
+{
+ return node_var_coef_offset(node) + 2 * (node->nvar - 1 - i);
+}
+
+/* Construct an isl_dim_map for mapping constraints on coefficients
+ * for "node" to the corresponding positions in graph->lp.
+ * "offset" is the offset of the coefficients for the variables
+ * in the input constraints.
+ * "s" is the sign of the mapping.
+ *
+ * The input constraints are given in terms of the coefficients
+ * (c_0, c_x) or (c_0, c_n, c_x).
+ * The mapping produced by this function essentially plugs in
+ * (0, c_i_x^+ - c_i_x^-) if s = 1 and
+ * (0, -c_i_x^+ + c_i_x^-) if s = -1 or
+ * (0, 0, c_i_x^+ - c_i_x^-) if s = 1 and
+ * (0, 0, -c_i_x^+ + c_i_x^-) if s = -1.
+ * In graph->lp, the c_i_x^- appear before their c_i_x^+ counterpart.
+ * Furthermore, the order of these pairs is the opposite of that
+ * of the corresponding coefficients.
+ *
+ * The caller can extend the mapping to also map the other coefficients
+ * (and therefore not plug in 0).
+ */
+static __isl_give isl_dim_map *intra_dim_map(isl_ctx *ctx,
+ struct isl_sched_graph *graph, struct isl_sched_node *node,
+ int offset, int s)
+{
+ int pos;
+ isl_size total;
+ isl_dim_map *dim_map;
+
+ total = isl_basic_set_dim(graph->lp, isl_dim_all);
+ if (!node || total < 0)
+ return NULL;
+
+ pos = node_var_coef_pos(node, 0);
+ dim_map = isl_dim_map_alloc(ctx, total);
+ isl_dim_map_range(dim_map, pos, -2, offset, 1, node->nvar, -s);
+ isl_dim_map_range(dim_map, pos + 1, -2, offset, 1, node->nvar, s);
+
+ return dim_map;
+}
+
+/* Construct an isl_dim_map for mapping constraints on coefficients
+ * for "src" (node i) and "dst" (node j) to the corresponding positions
+ * in graph->lp.
+ * "offset" is the offset of the coefficients for the variables of "src"
+ * in the input constraints.
+ * "s" is the sign of the mapping.
+ *
+ * The input constraints are given in terms of the coefficients
+ * (c_0, c_n, c_x, c_y).
+ * The mapping produced by this function essentially plugs in
+ * (c_j_0 - c_i_0, c_j_n - c_i_n,
+ * -(c_i_x^+ - c_i_x^-), c_j_x^+ - c_j_x^-) if s = 1 and
+ * (-c_j_0 + c_i_0, -c_j_n + c_i_n,
+ * c_i_x^+ - c_i_x^-, -(c_j_x^+ - c_j_x^-)) if s = -1.
+ * In graph->lp, the c_*^- appear before their c_*^+ counterpart.
+ * Furthermore, the order of these pairs is the opposite of that
+ * of the corresponding coefficients.
+ *
+ * The caller can further extend the mapping.
+ */
+static __isl_give isl_dim_map *inter_dim_map(isl_ctx *ctx,
+ struct isl_sched_graph *graph, struct isl_sched_node *src,
+ struct isl_sched_node *dst, int offset, int s)
+{
+ int pos;
+ isl_size total;
+ isl_dim_map *dim_map;
+
+ total = isl_basic_set_dim(graph->lp, isl_dim_all);
+ if (!src || !dst || total < 0)
+ return NULL;
+
+ dim_map = isl_dim_map_alloc(ctx, total);
+
+ pos = node_cst_coef_offset(dst);
+ isl_dim_map_range(dim_map, pos, 0, 0, 0, 1, s);
+ pos = node_par_coef_offset(dst);
+ isl_dim_map_range(dim_map, pos, 1, 1, 1, dst->nparam, s);
+ pos = node_var_coef_pos(dst, 0);
+ isl_dim_map_range(dim_map, pos, -2, offset + src->nvar, 1,
+ dst->nvar, -s);
+ isl_dim_map_range(dim_map, pos + 1, -2, offset + src->nvar, 1,
+ dst->nvar, s);
+
+ pos = node_cst_coef_offset(src);
+ isl_dim_map_range(dim_map, pos, 0, 0, 0, 1, -s);
+ pos = node_par_coef_offset(src);
+ isl_dim_map_range(dim_map, pos, 1, 1, 1, src->nparam, -s);
+ pos = node_var_coef_pos(src, 0);
+ isl_dim_map_range(dim_map, pos, -2, offset, 1, src->nvar, s);
+ isl_dim_map_range(dim_map, pos + 1, -2, offset, 1, src->nvar, -s);
+
+ return dim_map;
+}
+
+/* Add the constraints from "src" to "dst" using "dim_map",
+ * after making sure there is enough room in "dst" for the extra constraints.
+ */
+static __isl_give isl_basic_set *add_constraints_dim_map(
+ __isl_take isl_basic_set *dst, __isl_take isl_basic_set *src,
+ __isl_take isl_dim_map *dim_map)
+{
+ isl_size n_eq, n_ineq;
+
+ n_eq = isl_basic_set_n_equality(src);
+ n_ineq = isl_basic_set_n_inequality(src);
+ if (n_eq < 0 || n_ineq < 0)
+ dst = isl_basic_set_free(dst);
+ dst = isl_basic_set_extend_constraints(dst, n_eq, n_ineq);
+ dst = isl_basic_set_add_constraints_dim_map(dst, src, dim_map);
+ return dst;
+}
+
+/* Add constraints to graph->lp that force validity for the given
+ * dependence from a node i to itself.
+ * That is, add constraints that enforce
+ *
+ * (c_i_0 + c_i_n n + c_i_x y) - (c_i_0 + c_i_n n + c_i_x x)
+ * = c_i_x (y - x) >= 0
+ *
+ * for each (x,y) in R.
+ * We obtain general constraints on coefficients (c_0, c_x)
+ * of valid constraints for (y - x) and then plug in (0, c_i_x^+ - c_i_x^-),
+ * where c_i_x = c_i_x^+ - c_i_x^-, with c_i_x^+ and c_i_x^- non-negative.
+ * In graph->lp, the c_i_x^- appear before their c_i_x^+ counterpart.
+ * Note that the result of intra_coefficients may also contain
+ * parameter coefficients c_n, in which case 0 is plugged in for them as well.
+ */
+static isl_stat add_intra_validity_constraints(struct isl_sched_graph *graph,
+ struct isl_sched_edge *edge)
+{
+ isl_size offset;
+ isl_map *map = isl_map_copy(edge->map);
+ isl_ctx *ctx = isl_map_get_ctx(map);
+ isl_dim_map *dim_map;
+ isl_basic_set *coef;
+ struct isl_sched_node *node = edge->src;
+
+ coef = intra_coefficients(graph, node, map, 0);
+
+ offset = coef_var_offset(coef);
+ if (offset < 0)
+ coef = isl_basic_set_free(coef);
+ if (!coef)
+ return isl_stat_error;
+
+ dim_map = intra_dim_map(ctx, graph, node, offset, 1);
+ graph->lp = add_constraints_dim_map(graph->lp, coef, dim_map);
+
+ return isl_stat_ok;
+}
+
+/* Add constraints to graph->lp that force validity for the given
+ * dependence from node i to node j.
+ * That is, add constraints that enforce
+ *
+ * (c_j_0 + c_j_n n + c_j_x y) - (c_i_0 + c_i_n n + c_i_x x) >= 0
+ *
+ * for each (x,y) in R.
+ * We obtain general constraints on coefficients (c_0, c_n, c_x, c_y)
+ * of valid constraints for R and then plug in
+ * (c_j_0 - c_i_0, c_j_n - c_i_n, -(c_i_x^+ - c_i_x^-), c_j_x^+ - c_j_x^-),
+ * where c_* = c_*^+ - c_*^-, with c_*^+ and c_*^- non-negative.
+ * In graph->lp, the c_*^- appear before their c_*^+ counterpart.
+ */
+static isl_stat add_inter_validity_constraints(struct isl_sched_graph *graph,
+ struct isl_sched_edge *edge)
+{
+ isl_size offset;
+ isl_map *map;
+ isl_ctx *ctx;
+ isl_dim_map *dim_map;
+ isl_basic_set *coef;
+ struct isl_sched_node *src = edge->src;
+ struct isl_sched_node *dst = edge->dst;
+
+ if (!graph->lp)
+ return isl_stat_error;
+
+ map = isl_map_copy(edge->map);
+ ctx = isl_map_get_ctx(map);
+ coef = inter_coefficients(graph, edge, map);
+
+ offset = coef_var_offset(coef);
+ if (offset < 0)
+ coef = isl_basic_set_free(coef);
+ if (!coef)
+ return isl_stat_error;
+
+ dim_map = inter_dim_map(ctx, graph, src, dst, offset, 1);
+
+ edge->start = graph->lp->n_ineq;
+ graph->lp = add_constraints_dim_map(graph->lp, coef, dim_map);
+ if (!graph->lp)
+ return isl_stat_error;
+ edge->end = graph->lp->n_ineq;
+
+ return isl_stat_ok;
+}
+
+/* Add constraints to graph->lp that bound the dependence distance for the given
+ * dependence from a node i to itself.
+ * If s = 1, we add the constraint
+ *
+ * c_i_x (y - x) <= m_0 + m_n n
+ *
+ * or
+ *
+ * -c_i_x (y - x) + m_0 + m_n n >= 0
+ *
+ * for each (x,y) in R.
+ * If s = -1, we add the constraint
+ *
+ * -c_i_x (y - x) <= m_0 + m_n n
+ *
+ * or
+ *
+ * c_i_x (y - x) + m_0 + m_n n >= 0
+ *
+ * for each (x,y) in R.
+ * We obtain general constraints on coefficients (c_0, c_n, c_x)
+ * of valid constraints for (y - x) and then plug in (m_0, m_n, -s * c_i_x),
+ * with each coefficient (except m_0) represented as a pair of non-negative
+ * coefficients.
+ *
+ *
+ * If "local" is set, then we add constraints
+ *
+ * c_i_x (y - x) <= 0
+ *
+ * or
+ *
+ * -c_i_x (y - x) <= 0
+ *
+ * instead, forcing the dependence distance to be (less than or) equal to 0.
+ * That is, we plug in (0, 0, -s * c_i_x),
+ * intra_coefficients is not required to have c_n in its result when
+ * "local" is set. If they are missing, then (0, -s * c_i_x) is plugged in.
+ * Note that dependences marked local are treated as validity constraints
+ * by add_all_validity_constraints and therefore also have
+ * their distances bounded by 0 from below.
+ */
+static isl_stat add_intra_proximity_constraints(struct isl_sched_graph *graph,
+ struct isl_sched_edge *edge, int s, int local)
+{
+ isl_size offset;
+ isl_size nparam;
+ isl_map *map = isl_map_copy(edge->map);
+ isl_ctx *ctx = isl_map_get_ctx(map);
+ isl_dim_map *dim_map;
+ isl_basic_set *coef;
+ struct isl_sched_node *node = edge->src;
+
+ coef = intra_coefficients(graph, node, map, !local);
+ nparam = isl_space_dim(node->space, isl_dim_param);
+
+ offset = coef_var_offset(coef);
+ if (nparam < 0 || offset < 0)
+ coef = isl_basic_set_free(coef);
+ if (!coef)
+ return isl_stat_error;
+
+ dim_map = intra_dim_map(ctx, graph, node, offset, -s);
+
+ if (!local) {
+ isl_dim_map_range(dim_map, 1, 0, 0, 0, 1, 1);
+ isl_dim_map_range(dim_map, 4, 2, 1, 1, nparam, -1);
+ isl_dim_map_range(dim_map, 5, 2, 1, 1, nparam, 1);
+ }
+ graph->lp = add_constraints_dim_map(graph->lp, coef, dim_map);
+
+ return isl_stat_ok;
+}
+
+/* Add constraints to graph->lp that bound the dependence distance for the given
+ * dependence from node i to node j.
+ * If s = 1, we add the constraint
+ *
+ * (c_j_0 + c_j_n n + c_j_x y) - (c_i_0 + c_i_n n + c_i_x x)
+ * <= m_0 + m_n n
+ *
+ * or
+ *
+ * -(c_j_0 + c_j_n n + c_j_x y) + (c_i_0 + c_i_n n + c_i_x x) +
+ * m_0 + m_n n >= 0
+ *
+ * for each (x,y) in R.
+ * If s = -1, we add the constraint
+ *
+ * -((c_j_0 + c_j_n n + c_j_x y) - (c_i_0 + c_i_n n + c_i_x x))
+ * <= m_0 + m_n n
+ *
+ * or
+ *
+ * (c_j_0 + c_j_n n + c_j_x y) - (c_i_0 + c_i_n n + c_i_x x) +
+ * m_0 + m_n n >= 0
+ *
+ * for each (x,y) in R.
+ * We obtain general constraints on coefficients (c_0, c_n, c_x, c_y)
+ * of valid constraints for R and then plug in
+ * (m_0 - s*c_j_0 + s*c_i_0, m_n - s*c_j_n + s*c_i_n,
+ * s*c_i_x, -s*c_j_x)
+ * with each coefficient (except m_0, c_*_0 and c_*_n)
+ * represented as a pair of non-negative coefficients.
+ *
+ *
+ * If "local" is set (and s = 1), then we add constraints
+ *
+ * (c_j_0 + c_j_n n + c_j_x y) - (c_i_0 + c_i_n n + c_i_x x) <= 0
+ *
+ * or
+ *
+ * -((c_j_0 + c_j_n n + c_j_x y) + (c_i_0 + c_i_n n + c_i_x x)) >= 0
+ *
+ * instead, forcing the dependence distance to be (less than or) equal to 0.
+ * That is, we plug in
+ * (-s*c_j_0 + s*c_i_0, -s*c_j_n + s*c_i_n, s*c_i_x, -s*c_j_x).
+ * Note that dependences marked local are treated as validity constraints
+ * by add_all_validity_constraints and therefore also have
+ * their distances bounded by 0 from below.
+ */
+static isl_stat add_inter_proximity_constraints(struct isl_sched_graph *graph,
+ struct isl_sched_edge *edge, int s, int local)
+{
+ isl_size offset;
+ isl_size nparam;
+ isl_map *map = isl_map_copy(edge->map);
+ isl_ctx *ctx = isl_map_get_ctx(map);
+ isl_dim_map *dim_map;
+ isl_basic_set *coef;
+ struct isl_sched_node *src = edge->src;
+ struct isl_sched_node *dst = edge->dst;
+
+ coef = inter_coefficients(graph, edge, map);
+ nparam = isl_space_dim(src->space, isl_dim_param);
+
+ offset = coef_var_offset(coef);
+ if (nparam < 0 || offset < 0)
+ coef = isl_basic_set_free(coef);
+ if (!coef)
+ return isl_stat_error;
+
+ dim_map = inter_dim_map(ctx, graph, src, dst, offset, -s);
+
+ if (!local) {
+ isl_dim_map_range(dim_map, 1, 0, 0, 0, 1, 1);
+ isl_dim_map_range(dim_map, 4, 2, 1, 1, nparam, -1);
+ isl_dim_map_range(dim_map, 5, 2, 1, 1, nparam, 1);
+ }
+
+ graph->lp = add_constraints_dim_map(graph->lp, coef, dim_map);
+
+ return isl_stat_ok;
+}
+
+/* Should the distance over "edge" be forced to zero?
+ * That is, is it marked as a local edge?
+ * If "use_coincidence" is set, then coincidence edges are treated
+ * as local edges.
+ */
+static int force_zero(struct isl_sched_edge *edge, int use_coincidence)
+{
+ return is_local(edge) || (use_coincidence && is_coincidence(edge));
+}
+
+/* Add all validity constraints to graph->lp.
+ *
+ * An edge that is forced to be local needs to have its dependence
+ * distances equal to zero. We take care of bounding them by 0 from below
+ * here. add_all_proximity_constraints takes care of bounding them by 0
+ * from above.
+ *
+ * If "use_coincidence" is set, then we treat coincidence edges as local edges.
+ * Otherwise, we ignore them.
+ */
+static int add_all_validity_constraints(struct isl_sched_graph *graph,
+ int use_coincidence)
+{
+ int i;
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ struct isl_sched_edge *edge = &graph->edge[i];
+ int zero;
+
+ zero = force_zero(edge, use_coincidence);
+ if (!is_validity(edge) && !zero)
+ continue;
+ if (edge->src != edge->dst)
+ continue;
+ if (add_intra_validity_constraints(graph, edge) < 0)
+ return -1;
+ }
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ struct isl_sched_edge *edge = &graph->edge[i];
+ int zero;
+
+ zero = force_zero(edge, use_coincidence);
+ if (!is_validity(edge) && !zero)
+ continue;
+ if (edge->src == edge->dst)
+ continue;
+ if (add_inter_validity_constraints(graph, edge) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Add constraints to graph->lp that bound the dependence distance
+ * for all dependence relations.
+ * If a given proximity dependence is identical to a validity
+ * dependence, then the dependence distance is already bounded
+ * from below (by zero), so we only need to bound the distance
+ * from above. (This includes the case of "local" dependences
+ * which are treated as validity dependence by add_all_validity_constraints.)
+ * Otherwise, we need to bound the distance both from above and from below.
+ *
+ * If "use_coincidence" is set, then we treat coincidence edges as local edges.
+ * Otherwise, we ignore them.
+ */
+static int add_all_proximity_constraints(struct isl_sched_graph *graph,
+ int use_coincidence)
+{
+ int i;
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ struct isl_sched_edge *edge = &graph->edge[i];
+ int zero;
+
+ zero = force_zero(edge, use_coincidence);
+ if (!is_proximity(edge) && !zero)
+ continue;
+ if (edge->src == edge->dst &&
+ add_intra_proximity_constraints(graph, edge, 1, zero) < 0)
+ return -1;
+ if (edge->src != edge->dst &&
+ add_inter_proximity_constraints(graph, edge, 1, zero) < 0)
+ return -1;
+ if (is_validity(edge) || zero)
+ continue;
+ if (edge->src == edge->dst &&
+ add_intra_proximity_constraints(graph, edge, -1, 0) < 0)
+ return -1;
+ if (edge->src != edge->dst &&
+ add_inter_proximity_constraints(graph, edge, -1, 0) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Normalize the rows of "indep" such that all rows are lexicographically
+ * positive and such that each row contains as many final zeros as possible,
+ * given the choice for the previous rows.
+ * Do this by performing elementary row operations.
+ */
+static __isl_give isl_mat *normalize_independent(__isl_take isl_mat *indep)
+{
+ indep = isl_mat_reverse_gauss(indep);
+ indep = isl_mat_lexnonneg_rows(indep);
+ return indep;
+}
+
+/* Extract the linear part of the current schedule for node "node".
+ */
+static __isl_give isl_mat *extract_linear_schedule(struct isl_sched_node *node)
+{
+ isl_size n_row = isl_mat_rows(node->sched);
+
+ if (n_row < 0)
+ return NULL;
+ return isl_mat_sub_alloc(node->sched, 0, n_row,
+ 1 + node->nparam, node->nvar);
+}
+
+/* Compute a basis for the rows in the linear part of the schedule
+ * and extend this basis to a full basis. The remaining rows
+ * can then be used to force linear independence from the rows
+ * in the schedule.
+ *
+ * In particular, given the schedule rows S, we compute
+ *
+ * S = H Q
+ * S U = H
+ *
+ * with H the Hermite normal form of S. That is, all but the
+ * first rank columns of H are zero and so each row in S is
+ * a linear combination of the first rank rows of Q.
+ * The matrix Q can be used as a variable transformation
+ * that isolates the directions of S in the first rank rows.
+ * Transposing S U = H yields
+ *
+ * U^T S^T = H^T
+ *
+ * with all but the first rank rows of H^T zero.
+ * The last rows of U^T are therefore linear combinations
+ * of schedule coefficients that are all zero on schedule
+ * coefficients that are linearly dependent on the rows of S.
+ * At least one of these combinations is non-zero on
+ * linearly independent schedule coefficients.
+ * The rows are normalized to involve as few of the last
+ * coefficients as possible and to have a positive initial value.
+ */
+static int node_update_vmap(struct isl_sched_node *node)
+{
+ isl_mat *H, *U, *Q;
+
+ H = extract_linear_schedule(node);
+
+ H = isl_mat_left_hermite(H, 0, &U, &Q);
+ isl_mat_free(node->indep);
+ isl_mat_free(node->vmap);
+ node->vmap = Q;
+ node->indep = isl_mat_transpose(U);
+ node->rank = isl_mat_initial_non_zero_cols(H);
+ node->indep = isl_mat_drop_rows(node->indep, 0, node->rank);
+ node->indep = normalize_independent(node->indep);
+ isl_mat_free(H);
+
+ if (!node->indep || !node->vmap || node->rank < 0)
+ return -1;
+ return 0;
+}
+
+/* Is "edge" marked as a validity or a conditional validity edge?
+ */
+static int is_any_validity(struct isl_sched_edge *edge)
+{
+ return is_validity(edge) || is_conditional_validity(edge);
+}
+
+/* How many times should we count the constraints in "edge"?
+ *
+ * We count as follows
+ * validity -> 1 (>= 0)
+ * validity+proximity -> 2 (>= 0 and upper bound)
+ * proximity -> 2 (lower and upper bound)
+ * local(+any) -> 2 (>= 0 and <= 0)
+ *
+ * If an edge is only marked conditional_validity then it counts
+ * as zero since it is only checked afterwards.
+ *
+ * If "use_coincidence" is set, then we treat coincidence edges as local edges.
+ * Otherwise, we ignore them.
+ */
+static int edge_multiplicity(struct isl_sched_edge *edge, int use_coincidence)
+{
+ if (is_proximity(edge) || force_zero(edge, use_coincidence))
+ return 2;
+ if (is_validity(edge))
+ return 1;
+ return 0;
+}
+
+/* How many times should the constraints in "edge" be counted
+ * as a parametric intra-node constraint?
+ *
+ * Only proximity edges that are not forced zero need
+ * coefficient constraints that include coefficients for parameters.
+ * If the edge is also a validity edge, then only
+ * an upper bound is introduced. Otherwise, both lower and upper bounds
+ * are introduced.
+ */
+static int parametric_intra_edge_multiplicity(struct isl_sched_edge *edge,
+ int use_coincidence)
+{
+ if (edge->src != edge->dst)
+ return 0;
+ if (!is_proximity(edge))
+ return 0;
+ if (force_zero(edge, use_coincidence))
+ return 0;
+ if (is_validity(edge))
+ return 1;
+ else
+ return 2;
+}
+
+/* Add "f" times the number of equality and inequality constraints of "bset"
+ * to "n_eq" and "n_ineq" and free "bset".
+ */
+static isl_stat update_count(__isl_take isl_basic_set *bset,
+ int f, int *n_eq, int *n_ineq)
+{
+ isl_size eq, ineq;
+
+ eq = isl_basic_set_n_equality(bset);
+ ineq = isl_basic_set_n_inequality(bset);
+ isl_basic_set_free(bset);
+
+ if (eq < 0 || ineq < 0)
+ return isl_stat_error;
+
+ *n_eq += eq;
+ *n_ineq += ineq;
+
+ return isl_stat_ok;
+}
+
+/* Count the number of equality and inequality constraints
+ * that will be added for the given map.
+ *
+ * The edges that require parameter coefficients are counted separately.
+ *
+ * "use_coincidence" is set if we should take into account coincidence edges.
+ */
+static isl_stat count_map_constraints(struct isl_sched_graph *graph,
+ struct isl_sched_edge *edge, __isl_take isl_map *map,
+ int *n_eq, int *n_ineq, int use_coincidence)
+{
+ isl_map *copy;
+ isl_basic_set *coef;
+ int f = edge_multiplicity(edge, use_coincidence);
+ int fp = parametric_intra_edge_multiplicity(edge, use_coincidence);
+
+ if (f == 0) {
+ isl_map_free(map);
+ return isl_stat_ok;
+ }
+
+ if (edge->src != edge->dst) {
+ coef = inter_coefficients(graph, edge, map);
+ return update_count(coef, f, n_eq, n_ineq);
+ }
+
+ if (fp > 0) {
+ copy = isl_map_copy(map);
+ coef = intra_coefficients(graph, edge->src, copy, 1);
+ if (update_count(coef, fp, n_eq, n_ineq) < 0)
+ goto error;
+ }
+
+ if (f > fp) {
+ copy = isl_map_copy(map);
+ coef = intra_coefficients(graph, edge->src, copy, 0);
+ if (update_count(coef, f - fp, n_eq, n_ineq) < 0)
+ goto error;
+ }
+
+ isl_map_free(map);
+ return isl_stat_ok;
+error:
+ isl_map_free(map);
+ return isl_stat_error;
+}
+
+/* Count the number of equality and inequality constraints
+ * that will be added to the main lp problem.
+ * We count as follows
+ * validity -> 1 (>= 0)
+ * validity+proximity -> 2 (>= 0 and upper bound)
+ * proximity -> 2 (lower and upper bound)
+ * local(+any) -> 2 (>= 0 and <= 0)
+ *
+ * If "use_coincidence" is set, then we treat coincidence edges as local edges.
+ * Otherwise, we ignore them.
+ */
+static int count_constraints(struct isl_sched_graph *graph,
+ int *n_eq, int *n_ineq, int use_coincidence)
+{
+ int i;
+
+ *n_eq = *n_ineq = 0;
+ for (i = 0; i < graph->n_edge; ++i) {
+ struct isl_sched_edge *edge = &graph->edge[i];
+ isl_map *map = isl_map_copy(edge->map);
+
+ if (count_map_constraints(graph, edge, map, n_eq, n_ineq,
+ use_coincidence) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Count the number of constraints that will be added by
+ * add_bound_constant_constraints to bound the values of the constant terms
+ * and increment *n_eq and *n_ineq accordingly.
+ *
+ * In practice, add_bound_constant_constraints only adds inequalities.
+ */
+static isl_stat count_bound_constant_constraints(isl_ctx *ctx,
+ struct isl_sched_graph *graph, int *n_eq, int *n_ineq)
+{
+ if (isl_options_get_schedule_max_constant_term(ctx) == -1)
+ return isl_stat_ok;
+
+ *n_ineq += graph->n;
+
+ return isl_stat_ok;
+}
+
+/* Add constraints to bound the values of the constant terms in the schedule,
+ * if requested by the user.
+ *
+ * The maximal value of the constant terms is defined by the option
+ * "schedule_max_constant_term".
+ */
+static isl_stat add_bound_constant_constraints(isl_ctx *ctx,
+ struct isl_sched_graph *graph)
+{
+ int i, k;
+ int max;
+ isl_size total;
+
+ max = isl_options_get_schedule_max_constant_term(ctx);
+ if (max == -1)
+ return isl_stat_ok;
+
+ total = isl_basic_set_dim(graph->lp, isl_dim_set);
+ if (total < 0)
+ return isl_stat_error;
+
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+ int pos;
+
+ k = isl_basic_set_alloc_inequality(graph->lp);
+ if (k < 0)
+ return isl_stat_error;
+ isl_seq_clr(graph->lp->ineq[k], 1 + total);
+ pos = node_cst_coef_offset(node);
+ isl_int_set_si(graph->lp->ineq[k][1 + pos], -1);
+ isl_int_set_si(graph->lp->ineq[k][0], max);
+ }
+
+ return isl_stat_ok;
+}
+
+/* Count the number of constraints that will be added by
+ * add_bound_coefficient_constraints and increment *n_eq and *n_ineq
+ * accordingly.
+ *
+ * In practice, add_bound_coefficient_constraints only adds inequalities.
+ */
+static int count_bound_coefficient_constraints(isl_ctx *ctx,
+ struct isl_sched_graph *graph, int *n_eq, int *n_ineq)
+{
+ int i;
+
+ if (isl_options_get_schedule_max_coefficient(ctx) == -1 &&
+ !isl_options_get_schedule_treat_coalescing(ctx))
+ return 0;
+
+ for (i = 0; i < graph->n; ++i)
+ *n_ineq += graph->node[i].nparam + 2 * graph->node[i].nvar;
+
+ return 0;
+}
+
+/* Add constraints to graph->lp that bound the values of
+ * the parameter schedule coefficients of "node" to "max" and
+ * the variable schedule coefficients to the corresponding entry
+ * in node->max.
+ * In either case, a negative value means that no bound needs to be imposed.
+ *
+ * For parameter coefficients, this amounts to adding a constraint
+ *
+ * c_n <= max
+ *
+ * i.e.,
+ *
+ * -c_n + max >= 0
+ *
+ * The variables coefficients are, however, not represented directly.
+ * Instead, the variable coefficients c_x are written as differences
+ * c_x = c_x^+ - c_x^-.
+ * That is,
+ *
+ * -max_i <= c_x_i <= max_i
+ *
+ * is encoded as
+ *
+ * -max_i <= c_x_i^+ - c_x_i^- <= max_i
+ *
+ * or
+ *
+ * -(c_x_i^+ - c_x_i^-) + max_i >= 0
+ * c_x_i^+ - c_x_i^- + max_i >= 0
+ */
+static isl_stat node_add_coefficient_constraints(isl_ctx *ctx,
+ struct isl_sched_graph *graph, struct isl_sched_node *node, int max)
+{
+ int i, j, k;
+ isl_size total;
+ isl_vec *ineq;
+
+ total = isl_basic_set_dim(graph->lp, isl_dim_set);
+ if (total < 0)
+ return isl_stat_error;
+
+ for (j = 0; j < node->nparam; ++j) {
+ int dim;
+
+ if (max < 0)
+ continue;
+
+ k = isl_basic_set_alloc_inequality(graph->lp);
+ if (k < 0)
+ return isl_stat_error;
+ dim = 1 + node_par_coef_offset(node) + j;
+ isl_seq_clr(graph->lp->ineq[k], 1 + total);
+ isl_int_set_si(graph->lp->ineq[k][dim], -1);
+ isl_int_set_si(graph->lp->ineq[k][0], max);
+ }
+
+ ineq = isl_vec_alloc(ctx, 1 + total);
+ ineq = isl_vec_clr(ineq);
+ if (!ineq)
+ return isl_stat_error;
+ for (i = 0; i < node->nvar; ++i) {
+ int pos = 1 + node_var_coef_pos(node, i);
+
+ if (isl_int_is_neg(node->max->el[i]))
+ continue;
+
+ isl_int_set_si(ineq->el[pos], 1);
+ isl_int_set_si(ineq->el[pos + 1], -1);
+ isl_int_set(ineq->el[0], node->max->el[i]);
+
+ k = isl_basic_set_alloc_inequality(graph->lp);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(graph->lp->ineq[k], ineq->el, 1 + total);
+
+ isl_seq_neg(ineq->el + pos, ineq->el + pos, 2);
+ k = isl_basic_set_alloc_inequality(graph->lp);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(graph->lp->ineq[k], ineq->el, 1 + total);
+
+ isl_seq_clr(ineq->el + pos, 2);
+ }
+ isl_vec_free(ineq);
+
+ return isl_stat_ok;
+error:
+ isl_vec_free(ineq);
+ return isl_stat_error;
+}
+
+/* Add constraints that bound the values of the variable and parameter
+ * coefficients of the schedule.
+ *
+ * The maximal value of the coefficients is defined by the option
+ * 'schedule_max_coefficient' and the entries in node->max.
+ * These latter entries are only set if either the schedule_max_coefficient
+ * option or the schedule_treat_coalescing option is set.
+ */
+static isl_stat add_bound_coefficient_constraints(isl_ctx *ctx,
+ struct isl_sched_graph *graph)
+{
+ int i;
+ int max;
+
+ max = isl_options_get_schedule_max_coefficient(ctx);
+
+ if (max == -1 && !isl_options_get_schedule_treat_coalescing(ctx))
+ return isl_stat_ok;
+
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+
+ if (node_add_coefficient_constraints(ctx, graph, node, max) < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Add a constraint to graph->lp that equates the value at position
+ * "sum_pos" to the sum of the "n" values starting at "first".
+ */
+static isl_stat add_sum_constraint(struct isl_sched_graph *graph,
+ int sum_pos, int first, int n)
+{
+ int i, k;
+ isl_size total;
+
+ total = isl_basic_set_dim(graph->lp, isl_dim_set);
+ if (total < 0)
+ return isl_stat_error;
+
+ k = isl_basic_set_alloc_equality(graph->lp);
+ if (k < 0)
+ return isl_stat_error;
+ isl_seq_clr(graph->lp->eq[k], 1 + total);
+ isl_int_set_si(graph->lp->eq[k][1 + sum_pos], -1);
+ for (i = 0; i < n; ++i)
+ isl_int_set_si(graph->lp->eq[k][1 + first + i], 1);
+
+ return isl_stat_ok;
+}
+
+/* Add a constraint to graph->lp that equates the value at position
+ * "sum_pos" to the sum of the parameter coefficients of all nodes.
+ */
+static isl_stat add_param_sum_constraint(struct isl_sched_graph *graph,
+ int sum_pos)
+{
+ int i, j, k;
+ isl_size total;
+
+ total = isl_basic_set_dim(graph->lp, isl_dim_set);
+ if (total < 0)
+ return isl_stat_error;
+
+ k = isl_basic_set_alloc_equality(graph->lp);
+ if (k < 0)
+ return isl_stat_error;
+ isl_seq_clr(graph->lp->eq[k], 1 + total);
+ isl_int_set_si(graph->lp->eq[k][1 + sum_pos], -1);
+ for (i = 0; i < graph->n; ++i) {
+ int pos = 1 + node_par_coef_offset(&graph->node[i]);
+
+ for (j = 0; j < graph->node[i].nparam; ++j)
+ isl_int_set_si(graph->lp->eq[k][pos + j], 1);
+ }
+
+ return isl_stat_ok;
+}
+
+/* Add a constraint to graph->lp that equates the value at position
+ * "sum_pos" to the sum of the variable coefficients of all nodes.
+ */
+static isl_stat add_var_sum_constraint(struct isl_sched_graph *graph,
+ int sum_pos)
+{
+ int i, j, k;
+ isl_size total;
+
+ total = isl_basic_set_dim(graph->lp, isl_dim_set);
+ if (total < 0)
+ return isl_stat_error;
+
+ k = isl_basic_set_alloc_equality(graph->lp);
+ if (k < 0)
+ return isl_stat_error;
+ isl_seq_clr(graph->lp->eq[k], 1 + total);
+ isl_int_set_si(graph->lp->eq[k][1 + sum_pos], -1);
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+ int pos = 1 + node_var_coef_offset(node);
+
+ for (j = 0; j < 2 * node->nvar; ++j)
+ isl_int_set_si(graph->lp->eq[k][pos + j], 1);
+ }
+
+ return isl_stat_ok;
+}
+
+/* Construct an ILP problem for finding schedule coefficients
+ * that result in non-negative, but small dependence distances
+ * over all dependences.
+ * In particular, the dependence distances over proximity edges
+ * are bounded by m_0 + m_n n and we compute schedule coefficients
+ * with small values (preferably zero) of m_n and m_0.
+ *
+ * All variables of the ILP are non-negative. The actual coefficients
+ * may be negative, so each coefficient is represented as the difference
+ * of two non-negative variables. The negative part always appears
+ * immediately before the positive part.
+ * Other than that, the variables have the following order
+ *
+ * - sum of positive and negative parts of m_n coefficients
+ * - m_0
+ * - sum of all c_n coefficients
+ * (unconstrained when computing non-parametric schedules)
+ * - sum of positive and negative parts of all c_x coefficients
+ * - positive and negative parts of m_n coefficients
+ * - for each node
+ * - positive and negative parts of c_i_x, in opposite order
+ * - c_i_n (if parametric)
+ * - c_i_0
+ *
+ * The constraints are those from the edges plus two or three equalities
+ * to express the sums.
+ *
+ * If "use_coincidence" is set, then we treat coincidence edges as local edges.
+ * Otherwise, we ignore them.
+ */
+static isl_stat setup_lp(isl_ctx *ctx, struct isl_sched_graph *graph,
+ int use_coincidence)
+{
+ int i;
+ isl_size nparam;
+ unsigned total;
+ isl_space *space;
+ int parametric;
+ int param_pos;
+ int n_eq, n_ineq;
+
+ parametric = ctx->opt->schedule_parametric;
+ nparam = isl_space_dim(graph->node[0].space, isl_dim_param);
+ if (nparam < 0)
+ return isl_stat_error;
+ param_pos = 4;
+ total = param_pos + 2 * nparam;
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[graph->sorted[i]];
+ if (node_update_vmap(node) < 0)
+ return isl_stat_error;
+ node->start = total;
+ total += 1 + node->nparam + 2 * node->nvar;
+ }
+
+ if (count_constraints(graph, &n_eq, &n_ineq, use_coincidence) < 0)
+ return isl_stat_error;
+ if (count_bound_constant_constraints(ctx, graph, &n_eq, &n_ineq) < 0)
+ return isl_stat_error;
+ if (count_bound_coefficient_constraints(ctx, graph, &n_eq, &n_ineq) < 0)
+ return isl_stat_error;
+
+ space = isl_space_set_alloc(ctx, 0, total);
+ isl_basic_set_free(graph->lp);
+ n_eq += 2 + parametric;
+
+ graph->lp = isl_basic_set_alloc_space(space, 0, n_eq, n_ineq);
+
+ if (add_sum_constraint(graph, 0, param_pos, 2 * nparam) < 0)
+ return isl_stat_error;
+ if (parametric && add_param_sum_constraint(graph, 2) < 0)
+ return isl_stat_error;
+ if (add_var_sum_constraint(graph, 3) < 0)
+ return isl_stat_error;
+ if (add_bound_constant_constraints(ctx, graph) < 0)
+ return isl_stat_error;
+ if (add_bound_coefficient_constraints(ctx, graph) < 0)
+ return isl_stat_error;
+ if (add_all_validity_constraints(graph, use_coincidence) < 0)
+ return isl_stat_error;
+ if (add_all_proximity_constraints(graph, use_coincidence) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Analyze the conflicting constraint found by
+ * isl_tab_basic_set_non_trivial_lexmin. If it corresponds to the validity
+ * constraint of one of the edges between distinct nodes, living, moreover
+ * in distinct SCCs, then record the source and sink SCC as this may
+ * be a good place to cut between SCCs.
+ */
+static int check_conflict(int con, void *user)
+{
+ int i;
+ struct isl_sched_graph *graph = user;
+
+ if (graph->src_scc >= 0)
+ return 0;
+
+ con -= graph->lp->n_eq;
+
+ if (con >= graph->lp->n_ineq)
+ return 0;
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ if (!is_validity(&graph->edge[i]))
+ continue;
+ if (graph->edge[i].src == graph->edge[i].dst)
+ continue;
+ if (graph->edge[i].src->scc == graph->edge[i].dst->scc)
+ continue;
+ if (graph->edge[i].start > con)
+ continue;
+ if (graph->edge[i].end <= con)
+ continue;
+ graph->src_scc = graph->edge[i].src->scc;
+ graph->dst_scc = graph->edge[i].dst->scc;
+ }
+
+ return 0;
+}
+
+/* Check whether the next schedule row of the given node needs to be
+ * non-trivial. Lower-dimensional domains may have some trivial rows,
+ * but as soon as the number of remaining required non-trivial rows
+ * is as large as the number or remaining rows to be computed,
+ * all remaining rows need to be non-trivial.
+ */
+static int needs_row(struct isl_sched_graph *graph, struct isl_sched_node *node)
+{
+ return node->nvar - node->rank >= graph->maxvar - graph->n_row;
+}
+
+/* Construct a non-triviality region with triviality directions
+ * corresponding to the rows of "indep".
+ * The rows of "indep" are expressed in terms of the schedule coefficients c_i,
+ * while the triviality directions are expressed in terms of
+ * pairs of non-negative variables c^+_i - c^-_i, with c^-_i appearing
+ * before c^+_i. Furthermore,
+ * the pairs of non-negative variables representing the coefficients
+ * are stored in the opposite order.
+ */
+static __isl_give isl_mat *construct_trivial(__isl_keep isl_mat *indep)
+{
+ isl_ctx *ctx;
+ isl_mat *mat;
+ int i, j;
+ isl_size n, n_var;
+
+ n = isl_mat_rows(indep);
+ n_var = isl_mat_cols(indep);
+ if (n < 0 || n_var < 0)
+ return NULL;
+
+ ctx = isl_mat_get_ctx(indep);
+ mat = isl_mat_alloc(ctx, n, 2 * n_var);
+ if (!mat)
+ return NULL;
+ for (i = 0; i < n; ++i) {
+ for (j = 0; j < n_var; ++j) {
+ int nj = n_var - 1 - j;
+ isl_int_neg(mat->row[i][2 * nj], indep->row[i][j]);
+ isl_int_set(mat->row[i][2 * nj + 1], indep->row[i][j]);
+ }
+ }
+
+ return mat;
+}
+
+/* Solve the ILP problem constructed in setup_lp.
+ * For each node such that all the remaining rows of its schedule
+ * need to be non-trivial, we construct a non-triviality region.
+ * This region imposes that the next row is independent of previous rows.
+ * In particular, the non-triviality region enforces that at least
+ * one of the linear combinations in the rows of node->indep is non-zero.
+ */
+static __isl_give isl_vec *solve_lp(isl_ctx *ctx, struct isl_sched_graph *graph)
+{
+ int i;
+ isl_vec *sol;
+ isl_basic_set *lp;
+
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+ isl_mat *trivial;
+
+ graph->region[i].pos = node_var_coef_offset(node);
+ if (needs_row(graph, node))
+ trivial = construct_trivial(node->indep);
+ else
+ trivial = isl_mat_zero(ctx, 0, 0);
+ graph->region[i].trivial = trivial;
+ }
+ lp = isl_basic_set_copy(graph->lp);
+ sol = isl_tab_basic_set_non_trivial_lexmin(lp, 2, graph->n,
+ graph->region, &check_conflict, graph);
+ for (i = 0; i < graph->n; ++i)
+ isl_mat_free(graph->region[i].trivial);
+ return sol;
+}
+
+/* Extract the coefficients for the variables of "node" from "sol".
+ *
+ * Each schedule coefficient c_i_x is represented as the difference
+ * between two non-negative variables c_i_x^+ - c_i_x^-.
+ * The c_i_x^- appear before their c_i_x^+ counterpart.
+ * Furthermore, the order of these pairs is the opposite of that
+ * of the corresponding coefficients.
+ *
+ * Return c_i_x = c_i_x^+ - c_i_x^-
+ */
+static __isl_give isl_vec *extract_var_coef(struct isl_sched_node *node,
+ __isl_keep isl_vec *sol)
+{
+ int i;
+ int pos;
+ isl_vec *csol;
+
+ if (!sol)
+ return NULL;
+ csol = isl_vec_alloc(isl_vec_get_ctx(sol), node->nvar);
+ if (!csol)
+ return NULL;
+
+ pos = 1 + node_var_coef_offset(node);
+ for (i = 0; i < node->nvar; ++i)
+ isl_int_sub(csol->el[node->nvar - 1 - i],
+ sol->el[pos + 2 * i + 1], sol->el[pos + 2 * i]);
+
+ return csol;
+}
+
+/* Update the schedules of all nodes based on the given solution
+ * of the LP problem.
+ * The new row is added to the current band.
+ * All possibly negative coefficients are encoded as a difference
+ * of two non-negative variables, so we need to perform the subtraction
+ * here.
+ *
+ * If coincident is set, then the caller guarantees that the new
+ * row satisfies the coincidence constraints.
+ */
+static int update_schedule(struct isl_sched_graph *graph,
+ __isl_take isl_vec *sol, int coincident)
+{
+ int i, j;
+ isl_vec *csol = NULL;
+
+ if (!sol)
+ goto error;
+ if (sol->size == 0)
+ isl_die(sol->ctx, isl_error_internal,
+ "no solution found", goto error);
+ if (graph->n_total_row >= graph->max_row)
+ isl_die(sol->ctx, isl_error_internal,
+ "too many schedule rows", goto error);
+
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+ int pos;
+ isl_size row = isl_mat_rows(node->sched);
+
+ isl_vec_free(csol);
+ csol = extract_var_coef(node, sol);
+ if (row < 0 || !csol)
+ goto error;
+
+ isl_map_free(node->sched_map);
+ node->sched_map = NULL;
+ node->sched = isl_mat_add_rows(node->sched, 1);
+ if (!node->sched)
+ goto error;
+ pos = node_cst_coef_offset(node);
+ node->sched = isl_mat_set_element(node->sched,
+ row, 0, sol->el[1 + pos]);
+ pos = node_par_coef_offset(node);
+ for (j = 0; j < node->nparam; ++j)
+ node->sched = isl_mat_set_element(node->sched,
+ row, 1 + j, sol->el[1 + pos + j]);
+ for (j = 0; j < node->nvar; ++j)
+ node->sched = isl_mat_set_element(node->sched,
+ row, 1 + node->nparam + j, csol->el[j]);
+ node->coincident[graph->n_total_row] = coincident;
+ }
+ isl_vec_free(sol);
+ isl_vec_free(csol);
+
+ graph->n_row++;
+ graph->n_total_row++;
+
+ return 0;
+error:
+ isl_vec_free(sol);
+ isl_vec_free(csol);
+ return -1;
+}
+
+/* Convert row "row" of node->sched into an isl_aff living in "ls"
+ * and return this isl_aff.
+ */
+static __isl_give isl_aff *extract_schedule_row(__isl_take isl_local_space *ls,
+ struct isl_sched_node *node, int row)
+{
+ int j;
+ isl_int v;
+ isl_aff *aff;
+
+ isl_int_init(v);
+
+ aff = isl_aff_zero_on_domain(ls);
+ if (isl_mat_get_element(node->sched, row, 0, &v) < 0)
+ goto error;
+ aff = isl_aff_set_constant(aff, v);
+ for (j = 0; j < node->nparam; ++j) {
+ if (isl_mat_get_element(node->sched, row, 1 + j, &v) < 0)
+ goto error;
+ aff = isl_aff_set_coefficient(aff, isl_dim_param, j, v);
+ }
+ for (j = 0; j < node->nvar; ++j) {
+ if (isl_mat_get_element(node->sched, row,
+ 1 + node->nparam + j, &v) < 0)
+ goto error;
+ aff = isl_aff_set_coefficient(aff, isl_dim_in, j, v);
+ }
+
+ isl_int_clear(v);
+
+ return aff;
+error:
+ isl_int_clear(v);
+ isl_aff_free(aff);
+ return NULL;
+}
+
+/* Convert the "n" rows starting at "first" of node->sched into a multi_aff
+ * and return this multi_aff.
+ *
+ * The result is defined over the uncompressed node domain.
+ */
+static __isl_give isl_multi_aff *node_extract_partial_schedule_multi_aff(
+ struct isl_sched_node *node, int first, int n)
+{
+ int i;
+ isl_space *space;
+ isl_local_space *ls;
+ isl_aff *aff;
+ isl_multi_aff *ma;
+ isl_size nrow;
+
+ if (!node)
+ return NULL;
+ nrow = isl_mat_rows(node->sched);
+ if (nrow < 0)
+ return NULL;
+ if (node->compressed)
+ space = isl_pw_multi_aff_get_domain_space(node->decompress);
+ else
+ space = isl_space_copy(node->space);
+ ls = isl_local_space_from_space(isl_space_copy(space));
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, n);
+ ma = isl_multi_aff_zero(space);
+
+ for (i = first; i < first + n; ++i) {
+ aff = extract_schedule_row(isl_local_space_copy(ls), node, i);
+ ma = isl_multi_aff_set_aff(ma, i - first, aff);
+ }
+
+ isl_local_space_free(ls);
+
+ if (node->compressed)
+ ma = isl_multi_aff_pullback_multi_aff(ma,
+ isl_multi_aff_copy(node->compress));
+
+ return ma;
+}
+
+/* Convert node->sched into a multi_aff and return this multi_aff.
+ *
+ * The result is defined over the uncompressed node domain.
+ */
+static __isl_give isl_multi_aff *node_extract_schedule_multi_aff(
+ struct isl_sched_node *node)
+{
+ isl_size nrow;
+
+ nrow = isl_mat_rows(node->sched);
+ if (nrow < 0)
+ return NULL;
+ return node_extract_partial_schedule_multi_aff(node, 0, nrow);
+}
+
+/* Convert node->sched into a map and return this map.
+ *
+ * The result is cached in node->sched_map, which needs to be released
+ * whenever node->sched is updated.
+ * It is defined over the uncompressed node domain.
+ */
+static __isl_give isl_map *node_extract_schedule(struct isl_sched_node *node)
+{
+ if (!node->sched_map) {
+ isl_multi_aff *ma;
+
+ ma = node_extract_schedule_multi_aff(node);
+ node->sched_map = isl_map_from_multi_aff(ma);
+ }
+
+ return isl_map_copy(node->sched_map);
+}
+
+/* Construct a map that can be used to update a dependence relation
+ * based on the current schedule.
+ * That is, construct a map expressing that source and sink
+ * are executed within the same iteration of the current schedule.
+ * This map can then be intersected with the dependence relation.
+ * This is not the most efficient way, but this shouldn't be a critical
+ * operation.
+ */
+static __isl_give isl_map *specializer(struct isl_sched_node *src,
+ struct isl_sched_node *dst)
+{
+ isl_map *src_sched, *dst_sched;
+
+ src_sched = node_extract_schedule(src);
+ dst_sched = node_extract_schedule(dst);
+ return isl_map_apply_range(src_sched, isl_map_reverse(dst_sched));
+}
+
+/* Intersect the domains of the nested relations in domain and range
+ * of "umap" with "map".
+ */
+static __isl_give isl_union_map *intersect_domains(
+ __isl_take isl_union_map *umap, __isl_keep isl_map *map)
+{
+ isl_union_set *uset;
+
+ umap = isl_union_map_zip(umap);
+ uset = isl_union_set_from_set(isl_map_wrap(isl_map_copy(map)));
+ umap = isl_union_map_intersect_domain(umap, uset);
+ umap = isl_union_map_zip(umap);
+ return umap;
+}
+
+/* Update the dependence relation of the given edge based
+ * on the current schedule.
+ * If the dependence is carried completely by the current schedule, then
+ * it is removed from the edge_tables. It is kept in the list of edges
+ * as otherwise all edge_tables would have to be recomputed.
+ *
+ * If the edge is of a type that can appear multiple times
+ * between the same pair of nodes, then it is added to
+ * the edge table (again). This prevents the situation
+ * where none of these edges is referenced from the edge table
+ * because the one that was referenced turned out to be empty and
+ * was therefore removed from the table.
+ */
+static isl_stat update_edge(isl_ctx *ctx, struct isl_sched_graph *graph,
+ struct isl_sched_edge *edge)
+{
+ int empty;
+ isl_map *id;
+
+ id = specializer(edge->src, edge->dst);
+ edge->map = isl_map_intersect(edge->map, isl_map_copy(id));
+ if (!edge->map)
+ goto error;
+
+ if (edge->tagged_condition) {
+ edge->tagged_condition =
+ intersect_domains(edge->tagged_condition, id);
+ if (!edge->tagged_condition)
+ goto error;
+ }
+ if (edge->tagged_validity) {
+ edge->tagged_validity =
+ intersect_domains(edge->tagged_validity, id);
+ if (!edge->tagged_validity)
+ goto error;
+ }
+
+ empty = isl_map_plain_is_empty(edge->map);
+ if (empty < 0)
+ goto error;
+ if (empty) {
+ if (graph_remove_edge(graph, edge) < 0)
+ goto error;
+ } else if (is_multi_edge_type(edge)) {
+ if (graph_edge_tables_add(ctx, graph, edge) < 0)
+ goto error;
+ }
+
+ isl_map_free(id);
+ return isl_stat_ok;
+error:
+ isl_map_free(id);
+ return isl_stat_error;
+}
+
+/* Does the domain of "umap" intersect "uset"?
+ */
+static int domain_intersects(__isl_keep isl_union_map *umap,
+ __isl_keep isl_union_set *uset)
+{
+ int empty;
+
+ umap = isl_union_map_copy(umap);
+ umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(uset));
+ empty = isl_union_map_is_empty(umap);
+ isl_union_map_free(umap);
+
+ return empty < 0 ? -1 : !empty;
+}
+
+/* Does the range of "umap" intersect "uset"?
+ */
+static int range_intersects(__isl_keep isl_union_map *umap,
+ __isl_keep isl_union_set *uset)
+{
+ int empty;
+
+ umap = isl_union_map_copy(umap);
+ umap = isl_union_map_intersect_range(umap, isl_union_set_copy(uset));
+ empty = isl_union_map_is_empty(umap);
+ isl_union_map_free(umap);
+
+ return empty < 0 ? -1 : !empty;
+}
+
+/* Are the condition dependences of "edge" local with respect to
+ * the current schedule?
+ *
+ * That is, are domain and range of the condition dependences mapped
+ * to the same point?
+ *
+ * In other words, is the condition false?
+ */
+static int is_condition_false(struct isl_sched_edge *edge)
+{
+ isl_union_map *umap;
+ isl_map *map, *sched, *test;
+ int empty, local;
+
+ empty = isl_union_map_is_empty(edge->tagged_condition);
+ if (empty < 0 || empty)
+ return empty;
+
+ umap = isl_union_map_copy(edge->tagged_condition);
+ umap = isl_union_map_zip(umap);
+ umap = isl_union_set_unwrap(isl_union_map_domain(umap));
+ map = isl_map_from_union_map(umap);
+
+ sched = node_extract_schedule(edge->src);
+ map = isl_map_apply_domain(map, sched);
+ sched = node_extract_schedule(edge->dst);
+ map = isl_map_apply_range(map, sched);
+
+ test = isl_map_identity(isl_map_get_space(map));
+ local = isl_map_is_subset(map, test);
+ isl_map_free(map);
+ isl_map_free(test);
+
+ return local;
+}
+
+/* For each conditional validity constraint that is adjacent
+ * to a condition with domain in condition_source or range in condition_sink,
+ * turn it into an unconditional validity constraint.
+ */
+static int unconditionalize_adjacent_validity(struct isl_sched_graph *graph,
+ __isl_take isl_union_set *condition_source,
+ __isl_take isl_union_set *condition_sink)
+{
+ int i;
+
+ condition_source = isl_union_set_coalesce(condition_source);
+ condition_sink = isl_union_set_coalesce(condition_sink);
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ int adjacent;
+ isl_union_map *validity;
+
+ if (!is_conditional_validity(&graph->edge[i]))
+ continue;
+ if (is_validity(&graph->edge[i]))
+ continue;
+
+ validity = graph->edge[i].tagged_validity;
+ adjacent = domain_intersects(validity, condition_sink);
+ if (adjacent >= 0 && !adjacent)
+ adjacent = range_intersects(validity, condition_source);
+ if (adjacent < 0)
+ goto error;
+ if (!adjacent)
+ continue;
+
+ set_validity(&graph->edge[i]);
+ }
+
+ isl_union_set_free(condition_source);
+ isl_union_set_free(condition_sink);
+ return 0;
+error:
+ isl_union_set_free(condition_source);
+ isl_union_set_free(condition_sink);
+ return -1;
+}
+
+/* Update the dependence relations of all edges based on the current schedule
+ * and enforce conditional validity constraints that are adjacent
+ * to satisfied condition constraints.
+ *
+ * First check if any of the condition constraints are satisfied
+ * (i.e., not local to the outer schedule) and keep track of
+ * their domain and range.
+ * Then update all dependence relations (which removes the non-local
+ * constraints).
+ * Finally, if any condition constraints turned out to be satisfied,
+ * then turn all adjacent conditional validity constraints into
+ * unconditional validity constraints.
+ */
+static int update_edges(isl_ctx *ctx, struct isl_sched_graph *graph)
+{
+ int i;
+ int any = 0;
+ isl_union_set *source, *sink;
+
+ source = isl_union_set_empty(isl_space_params_alloc(ctx, 0));
+ sink = isl_union_set_empty(isl_space_params_alloc(ctx, 0));
+ for (i = 0; i < graph->n_edge; ++i) {
+ int local;
+ isl_union_set *uset;
+ isl_union_map *umap;
+
+ if (!is_condition(&graph->edge[i]))
+ continue;
+ if (is_local(&graph->edge[i]))
+ continue;
+ local = is_condition_false(&graph->edge[i]);
+ if (local < 0)
+ goto error;
+ if (local)
+ continue;
+
+ any = 1;
+
+ umap = isl_union_map_copy(graph->edge[i].tagged_condition);
+ uset = isl_union_map_domain(umap);
+ source = isl_union_set_union(source, uset);
+
+ umap = isl_union_map_copy(graph->edge[i].tagged_condition);
+ uset = isl_union_map_range(umap);
+ sink = isl_union_set_union(sink, uset);
+ }
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ if (update_edge(ctx, graph, &graph->edge[i]) < 0)
+ goto error;
+ }
+
+ if (any)
+ return unconditionalize_adjacent_validity(graph, source, sink);
+
+ isl_union_set_free(source);
+ isl_union_set_free(sink);
+ return 0;
+error:
+ isl_union_set_free(source);
+ isl_union_set_free(sink);
+ return -1;
+}
+
+static void next_band(struct isl_sched_graph *graph)
+{
+ graph->band_start = graph->n_total_row;
+}
+
+/* Return the union of the universe domains of the nodes in "graph"
+ * that satisfy "pred".
+ */
+static __isl_give isl_union_set *isl_sched_graph_domain(isl_ctx *ctx,
+ struct isl_sched_graph *graph,
+ int (*pred)(struct isl_sched_node *node, int data), int data)
+{
+ int i;
+ isl_set *set;
+ isl_union_set *dom;
+
+ for (i = 0; i < graph->n; ++i)
+ if (pred(&graph->node[i], data))
+ break;
+
+ if (i >= graph->n)
+ isl_die(ctx, isl_error_internal,
+ "empty component", return NULL);
+
+ set = isl_set_universe(isl_space_copy(graph->node[i].space));
+ dom = isl_union_set_from_set(set);
+
+ for (i = i + 1; i < graph->n; ++i) {
+ if (!pred(&graph->node[i], data))
+ continue;
+ set = isl_set_universe(isl_space_copy(graph->node[i].space));
+ dom = isl_union_set_union(dom, isl_union_set_from_set(set));
+ }
+
+ return dom;
+}
+
+/* Return a list of unions of universe domains, where each element
+ * in the list corresponds to an SCC (or WCC) indexed by node->scc.
+ */
+static __isl_give isl_union_set_list *extract_sccs(isl_ctx *ctx,
+ struct isl_sched_graph *graph)
+{
+ int i;
+ isl_union_set_list *filters;
+
+ filters = isl_union_set_list_alloc(ctx, graph->scc);
+ for (i = 0; i < graph->scc; ++i) {
+ isl_union_set *dom;
+
+ dom = isl_sched_graph_domain(ctx, graph, &node_scc_exactly, i);
+ filters = isl_union_set_list_add(filters, dom);
+ }
+
+ return filters;
+}
+
+/* Return a list of two unions of universe domains, one for the SCCs up
+ * to and including graph->src_scc and another for the other SCCs.
+ */
+static __isl_give isl_union_set_list *extract_split(isl_ctx *ctx,
+ struct isl_sched_graph *graph)
+{
+ isl_union_set *dom;
+ isl_union_set_list *filters;
+
+ filters = isl_union_set_list_alloc(ctx, 2);
+ dom = isl_sched_graph_domain(ctx, graph,
+ &node_scc_at_most, graph->src_scc);
+ filters = isl_union_set_list_add(filters, dom);
+ dom = isl_sched_graph_domain(ctx, graph,
+ &node_scc_at_least, graph->src_scc + 1);
+ filters = isl_union_set_list_add(filters, dom);
+
+ return filters;
+}
+
+/* Copy nodes that satisfy node_pred from the src dependence graph
+ * to the dst dependence graph.
+ */
+static isl_stat copy_nodes(struct isl_sched_graph *dst,
+ struct isl_sched_graph *src,
+ int (*node_pred)(struct isl_sched_node *node, int data), int data)
+{
+ int i;
+
+ dst->n = 0;
+ for (i = 0; i < src->n; ++i) {
+ int j;
+
+ if (!node_pred(&src->node[i], data))
+ continue;
+
+ j = dst->n;
+ dst->node[j].space = isl_space_copy(src->node[i].space);
+ dst->node[j].compressed = src->node[i].compressed;
+ dst->node[j].hull = isl_set_copy(src->node[i].hull);
+ dst->node[j].compress =
+ isl_multi_aff_copy(src->node[i].compress);
+ dst->node[j].decompress =
+ isl_pw_multi_aff_copy(src->node[i].decompress);
+ dst->node[j].nvar = src->node[i].nvar;
+ dst->node[j].nparam = src->node[i].nparam;
+ dst->node[j].sched = isl_mat_copy(src->node[i].sched);
+ dst->node[j].sched_map = isl_map_copy(src->node[i].sched_map);
+ dst->node[j].coincident = src->node[i].coincident;
+ dst->node[j].sizes = isl_multi_val_copy(src->node[i].sizes);
+ dst->node[j].bounds = isl_basic_set_copy(src->node[i].bounds);
+ dst->node[j].max = isl_vec_copy(src->node[i].max);
+ dst->n++;
+
+ if (!dst->node[j].space || !dst->node[j].sched)
+ return isl_stat_error;
+ if (dst->node[j].compressed &&
+ (!dst->node[j].hull || !dst->node[j].compress ||
+ !dst->node[j].decompress))
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Copy non-empty edges that satisfy edge_pred from the src dependence graph
+ * to the dst dependence graph.
+ * If the source or destination node of the edge is not in the destination
+ * graph, then it must be a backward proximity edge and it should simply
+ * be ignored.
+ */
+static isl_stat copy_edges(isl_ctx *ctx, struct isl_sched_graph *dst,
+ struct isl_sched_graph *src,
+ int (*edge_pred)(struct isl_sched_edge *edge, int data), int data)
+{
+ int i;
+
+ dst->n_edge = 0;
+ for (i = 0; i < src->n_edge; ++i) {
+ struct isl_sched_edge *edge = &src->edge[i];
+ isl_map *map;
+ isl_union_map *tagged_condition;
+ isl_union_map *tagged_validity;
+ struct isl_sched_node *dst_src, *dst_dst;
+
+ if (!edge_pred(edge, data))
+ continue;
+
+ if (isl_map_plain_is_empty(edge->map))
+ continue;
+
+ dst_src = graph_find_node(ctx, dst, edge->src->space);
+ dst_dst = graph_find_node(ctx, dst, edge->dst->space);
+ if (!dst_src || !dst_dst)
+ return isl_stat_error;
+ if (!is_node(dst, dst_src) || !is_node(dst, dst_dst)) {
+ if (is_validity(edge) || is_conditional_validity(edge))
+ isl_die(ctx, isl_error_internal,
+ "backward (conditional) validity edge",
+ return isl_stat_error);
+ continue;
+ }
+
+ map = isl_map_copy(edge->map);
+ tagged_condition = isl_union_map_copy(edge->tagged_condition);
+ tagged_validity = isl_union_map_copy(edge->tagged_validity);
+
+ dst->edge[dst->n_edge].src = dst_src;
+ dst->edge[dst->n_edge].dst = dst_dst;
+ dst->edge[dst->n_edge].map = map;
+ dst->edge[dst->n_edge].tagged_condition = tagged_condition;
+ dst->edge[dst->n_edge].tagged_validity = tagged_validity;
+ dst->edge[dst->n_edge].types = edge->types;
+ dst->n_edge++;
+
+ if (edge->tagged_condition && !tagged_condition)
+ return isl_stat_error;
+ if (edge->tagged_validity && !tagged_validity)
+ return isl_stat_error;
+
+ if (graph_edge_tables_add(ctx, dst,
+ &dst->edge[dst->n_edge - 1]) < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Compute the maximal number of variables over all nodes.
+ * This is the maximal number of linearly independent schedule
+ * rows that we need to compute.
+ * Just in case we end up in a part of the dependence graph
+ * with only lower-dimensional domains, we make sure we will
+ * compute the required amount of extra linearly independent rows.
+ */
+static int compute_maxvar(struct isl_sched_graph *graph)
+{
+ int i;
+
+ graph->maxvar = 0;
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+ int nvar;
+
+ if (node_update_vmap(node) < 0)
+ return -1;
+ nvar = node->nvar + graph->n_row - node->rank;
+ if (nvar > graph->maxvar)
+ graph->maxvar = nvar;
+ }
+
+ return 0;
+}
+
+/* Extract the subgraph of "graph" that consists of the nodes satisfying
+ * "node_pred" and the edges satisfying "edge_pred" and store
+ * the result in "sub".
+ */
+static isl_stat extract_sub_graph(isl_ctx *ctx, struct isl_sched_graph *graph,
+ int (*node_pred)(struct isl_sched_node *node, int data),
+ int (*edge_pred)(struct isl_sched_edge *edge, int data),
+ int data, struct isl_sched_graph *sub)
+{
+ int i, n = 0, n_edge = 0;
+ int t;
+
+ for (i = 0; i < graph->n; ++i)
+ if (node_pred(&graph->node[i], data))
+ ++n;
+ for (i = 0; i < graph->n_edge; ++i)
+ if (edge_pred(&graph->edge[i], data))
+ ++n_edge;
+ if (graph_alloc(ctx, sub, n, n_edge) < 0)
+ return isl_stat_error;
+ sub->root = graph->root;
+ if (copy_nodes(sub, graph, node_pred, data) < 0)
+ return isl_stat_error;
+ if (graph_init_table(ctx, sub) < 0)
+ return isl_stat_error;
+ for (t = 0; t <= isl_edge_last; ++t)
+ sub->max_edge[t] = graph->max_edge[t];
+ if (graph_init_edge_tables(ctx, sub) < 0)
+ return isl_stat_error;
+ if (copy_edges(ctx, sub, graph, edge_pred, data) < 0)
+ return isl_stat_error;
+ sub->n_row = graph->n_row;
+ sub->max_row = graph->max_row;
+ sub->n_total_row = graph->n_total_row;
+ sub->band_start = graph->band_start;
+
+ return isl_stat_ok;
+}
+
+static __isl_give isl_schedule_node *compute_schedule(isl_schedule_node *node,
+ struct isl_sched_graph *graph);
+static __isl_give isl_schedule_node *compute_schedule_wcc(
+ isl_schedule_node *node, struct isl_sched_graph *graph);
+
+/* Compute a schedule for a subgraph of "graph". In particular, for
+ * the graph composed of nodes that satisfy node_pred and edges that
+ * that satisfy edge_pred.
+ * If the subgraph is known to consist of a single component, then wcc should
+ * be set and then we call compute_schedule_wcc on the constructed subgraph.
+ * Otherwise, we call compute_schedule, which will check whether the subgraph
+ * is connected.
+ *
+ * The schedule is inserted at "node" and the updated schedule node
+ * is returned.
+ */
+static __isl_give isl_schedule_node *compute_sub_schedule(
+ __isl_take isl_schedule_node *node, isl_ctx *ctx,
+ struct isl_sched_graph *graph,
+ int (*node_pred)(struct isl_sched_node *node, int data),
+ int (*edge_pred)(struct isl_sched_edge *edge, int data),
+ int data, int wcc)
+{
+ struct isl_sched_graph split = { 0 };
+
+ if (extract_sub_graph(ctx, graph, node_pred, edge_pred, data,
+ &split) < 0)
+ goto error;
+
+ if (wcc)
+ node = compute_schedule_wcc(node, &split);
+ else
+ node = compute_schedule(node, &split);
+
+ graph_free(ctx, &split);
+ return node;
+error:
+ graph_free(ctx, &split);
+ return isl_schedule_node_free(node);
+}
+
+static int edge_scc_exactly(struct isl_sched_edge *edge, int scc)
+{
+ return edge->src->scc == scc && edge->dst->scc == scc;
+}
+
+static int edge_dst_scc_at_most(struct isl_sched_edge *edge, int scc)
+{
+ return edge->dst->scc <= scc;
+}
+
+static int edge_src_scc_at_least(struct isl_sched_edge *edge, int scc)
+{
+ return edge->src->scc >= scc;
+}
+
+/* Reset the current band by dropping all its schedule rows.
+ */
+static isl_stat reset_band(struct isl_sched_graph *graph)
+{
+ int i;
+ int drop;
+
+ drop = graph->n_total_row - graph->band_start;
+ graph->n_total_row -= drop;
+ graph->n_row -= drop;
+
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+
+ isl_map_free(node->sched_map);
+ node->sched_map = NULL;
+
+ node->sched = isl_mat_drop_rows(node->sched,
+ graph->band_start, drop);
+
+ if (!node->sched)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Split the current graph into two parts and compute a schedule for each
+ * part individually. In particular, one part consists of all SCCs up
+ * to and including graph->src_scc, while the other part contains the other
+ * SCCs. The split is enforced by a sequence node inserted at position "node"
+ * in the schedule tree. Return the updated schedule node.
+ * If either of these two parts consists of a sequence, then it is spliced
+ * into the sequence containing the two parts.
+ *
+ * The current band is reset. It would be possible to reuse
+ * the previously computed rows as the first rows in the next
+ * band, but recomputing them may result in better rows as we are looking
+ * at a smaller part of the dependence graph.
+ */
+static __isl_give isl_schedule_node *compute_split_schedule(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph)
+{
+ int is_seq;
+ isl_ctx *ctx;
+ isl_union_set_list *filters;
+
+ if (!node)
+ return NULL;
+
+ if (reset_band(graph) < 0)
+ return isl_schedule_node_free(node);
+
+ next_band(graph);
+
+ ctx = isl_schedule_node_get_ctx(node);
+ filters = extract_split(ctx, graph);
+ node = isl_schedule_node_insert_sequence(node, filters);
+ node = isl_schedule_node_child(node, 1);
+ node = isl_schedule_node_child(node, 0);
+
+ node = compute_sub_schedule(node, ctx, graph,
+ &node_scc_at_least, &edge_src_scc_at_least,
+ graph->src_scc + 1, 0);
+ is_seq = isl_schedule_node_get_type(node) == isl_schedule_node_sequence;
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_parent(node);
+ if (is_seq)
+ node = isl_schedule_node_sequence_splice_child(node, 1);
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_child(node, 0);
+ node = compute_sub_schedule(node, ctx, graph,
+ &node_scc_at_most, &edge_dst_scc_at_most,
+ graph->src_scc, 0);
+ is_seq = isl_schedule_node_get_type(node) == isl_schedule_node_sequence;
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_parent(node);
+ if (is_seq)
+ node = isl_schedule_node_sequence_splice_child(node, 0);
+
+ return node;
+}
+
+/* Insert a band node at position "node" in the schedule tree corresponding
+ * to the current band in "graph". Mark the band node permutable
+ * if "permutable" is set.
+ * The partial schedules and the coincidence property are extracted
+ * from the graph nodes.
+ * Return the updated schedule node.
+ */
+static __isl_give isl_schedule_node *insert_current_band(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph,
+ int permutable)
+{
+ int i;
+ int start, end, n;
+ isl_multi_aff *ma;
+ isl_multi_pw_aff *mpa;
+ isl_multi_union_pw_aff *mupa;
+
+ if (!node)
+ return NULL;
+
+ if (graph->n < 1)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_internal,
+ "graph should have at least one node",
+ return isl_schedule_node_free(node));
+
+ start = graph->band_start;
+ end = graph->n_total_row;
+ n = end - start;
+
+ ma = node_extract_partial_schedule_multi_aff(&graph->node[0], start, n);
+ mpa = isl_multi_pw_aff_from_multi_aff(ma);
+ mupa = isl_multi_union_pw_aff_from_multi_pw_aff(mpa);
+
+ for (i = 1; i < graph->n; ++i) {
+ isl_multi_union_pw_aff *mupa_i;
+
+ ma = node_extract_partial_schedule_multi_aff(&graph->node[i],
+ start, n);
+ mpa = isl_multi_pw_aff_from_multi_aff(ma);
+ mupa_i = isl_multi_union_pw_aff_from_multi_pw_aff(mpa);
+ mupa = isl_multi_union_pw_aff_union_add(mupa, mupa_i);
+ }
+ node = isl_schedule_node_insert_partial_schedule(node, mupa);
+
+ for (i = 0; i < n; ++i)
+ node = isl_schedule_node_band_member_set_coincident(node, i,
+ graph->node[0].coincident[start + i]);
+ node = isl_schedule_node_band_set_permutable(node, permutable);
+
+ return node;
+}
+
+/* Update the dependence relations based on the current schedule,
+ * add the current band to "node" and then continue with the computation
+ * of the next band.
+ * Return the updated schedule node.
+ */
+static __isl_give isl_schedule_node *compute_next_band(
+ __isl_take isl_schedule_node *node,
+ struct isl_sched_graph *graph, int permutable)
+{
+ isl_ctx *ctx;
+
+ if (!node)
+ return NULL;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ if (update_edges(ctx, graph) < 0)
+ return isl_schedule_node_free(node);
+ node = insert_current_band(node, graph, permutable);
+ next_band(graph);
+
+ node = isl_schedule_node_child(node, 0);
+ node = compute_schedule(node, graph);
+ node = isl_schedule_node_parent(node);
+
+ return node;
+}
+
+/* Add the constraints "coef" derived from an edge from "node" to itself
+ * to graph->lp in order to respect the dependences and to try and carry them.
+ * "pos" is the sequence number of the edge that needs to be carried.
+ * "coef" represents general constraints on coefficients (c_0, c_x)
+ * of valid constraints for (y - x) with x and y instances of the node.
+ *
+ * The constraints added to graph->lp need to enforce
+ *
+ * (c_j_0 + c_j_x y) - (c_j_0 + c_j_x x)
+ * = c_j_x (y - x) >= e_i
+ *
+ * for each (x,y) in the dependence relation of the edge.
+ * That is, (-e_i, c_j_x) needs to be plugged in for (c_0, c_x),
+ * taking into account that each coefficient in c_j_x is represented
+ * as a pair of non-negative coefficients.
+ */
+static isl_stat add_intra_constraints(struct isl_sched_graph *graph,
+ struct isl_sched_node *node, __isl_take isl_basic_set *coef, int pos)
+{
+ isl_size offset;
+ isl_ctx *ctx;
+ isl_dim_map *dim_map;
+
+ offset = coef_var_offset(coef);
+ if (offset < 0)
+ coef = isl_basic_set_free(coef);
+ if (!coef)
+ return isl_stat_error;
+
+ ctx = isl_basic_set_get_ctx(coef);
+ dim_map = intra_dim_map(ctx, graph, node, offset, 1);
+ isl_dim_map_range(dim_map, 3 + pos, 0, 0, 0, 1, -1);
+ graph->lp = add_constraints_dim_map(graph->lp, coef, dim_map);
+
+ return isl_stat_ok;
+}
+
+/* Add the constraints "coef" derived from an edge from "src" to "dst"
+ * to graph->lp in order to respect the dependences and to try and carry them.
+ * "pos" is the sequence number of the edge that needs to be carried or
+ * -1 if no attempt should be made to carry the dependences.
+ * "coef" represents general constraints on coefficients (c_0, c_n, c_x, c_y)
+ * of valid constraints for (x, y) with x and y instances of "src" and "dst".
+ *
+ * The constraints added to graph->lp need to enforce
+ *
+ * (c_k_0 + c_k_n n + c_k_x y) - (c_j_0 + c_j_n n + c_j_x x) >= e_i
+ *
+ * for each (x,y) in the dependence relation of the edge or
+ *
+ * (c_k_0 + c_k_n n + c_k_x y) - (c_j_0 + c_j_n n + c_j_x x) >= 0
+ *
+ * if pos is -1.
+ * That is,
+ * (-e_i + c_k_0 - c_j_0, c_k_n - c_j_n, -c_j_x, c_k_x)
+ * or
+ * (c_k_0 - c_j_0, c_k_n - c_j_n, -c_j_x, c_k_x)
+ * needs to be plugged in for (c_0, c_n, c_x, c_y),
+ * taking into account that each coefficient in c_j_x and c_k_x is represented
+ * as a pair of non-negative coefficients.
+ */
+static isl_stat add_inter_constraints(struct isl_sched_graph *graph,
+ struct isl_sched_node *src, struct isl_sched_node *dst,
+ __isl_take isl_basic_set *coef, int pos)
+{
+ isl_size offset;
+ isl_ctx *ctx;
+ isl_dim_map *dim_map;
+
+ offset = coef_var_offset(coef);
+ if (offset < 0)
+ coef = isl_basic_set_free(coef);
+ if (!coef)
+ return isl_stat_error;
+
+ ctx = isl_basic_set_get_ctx(coef);
+ dim_map = inter_dim_map(ctx, graph, src, dst, offset, 1);
+ if (pos >= 0)
+ isl_dim_map_range(dim_map, 3 + pos, 0, 0, 0, 1, -1);
+ graph->lp = add_constraints_dim_map(graph->lp, coef, dim_map);
+
+ return isl_stat_ok;
+}
+
+/* Data structure for keeping track of the data needed
+ * to exploit non-trivial lineality spaces.
+ *
+ * "any_non_trivial" is true if there are any non-trivial lineality spaces.
+ * If "any_non_trivial" is not true, then "equivalent" and "mask" may be NULL.
+ * "equivalent" connects instances to other instances on the same line(s).
+ * "mask" contains the domain spaces of "equivalent".
+ * Any instance set not in "mask" does not have a non-trivial lineality space.
+ */
+struct isl_exploit_lineality_data {
+ isl_bool any_non_trivial;
+ isl_union_map *equivalent;
+ isl_union_set *mask;
+};
+
+/* Data structure collecting information used during the construction
+ * of an LP for carrying dependences.
+ *
+ * "intra" is a sequence of coefficient constraints for intra-node edges.
+ * "inter" is a sequence of coefficient constraints for inter-node edges.
+ * "lineality" contains data used to exploit non-trivial lineality spaces.
+ */
+struct isl_carry {
+ isl_basic_set_list *intra;
+ isl_basic_set_list *inter;
+ struct isl_exploit_lineality_data lineality;
+};
+
+/* Free all the data stored in "carry".
+ */
+static void isl_carry_clear(struct isl_carry *carry)
+{
+ isl_basic_set_list_free(carry->intra);
+ isl_basic_set_list_free(carry->inter);
+ isl_union_map_free(carry->lineality.equivalent);
+ isl_union_set_free(carry->lineality.mask);
+}
+
+/* Return a pointer to the node in "graph" that lives in "space".
+ * If the requested node has been compressed, then "space"
+ * corresponds to the compressed space.
+ * The graph is assumed to have such a node.
+ * Return NULL in case of error.
+ *
+ * First try and see if "space" is the space of an uncompressed node.
+ * If so, return that node.
+ * Otherwise, "space" was constructed by construct_compressed_id and
+ * contains a user pointer pointing to the node in the tuple id.
+ * However, this node belongs to the original dependence graph.
+ * If "graph" is a subgraph of this original dependence graph,
+ * then the node with the same space still needs to be looked up
+ * in the current graph.
+ */
+static struct isl_sched_node *graph_find_compressed_node(isl_ctx *ctx,
+ struct isl_sched_graph *graph, __isl_keep isl_space *space)
+{
+ isl_id *id;
+ struct isl_sched_node *node;
+
+ if (!space)
+ return NULL;
+
+ node = graph_find_node(ctx, graph, space);
+ if (!node)
+ return NULL;
+ if (is_node(graph, node))
+ return node;
+
+ id = isl_space_get_tuple_id(space, isl_dim_set);
+ node = isl_id_get_user(id);
+ isl_id_free(id);
+
+ if (!node)
+ return NULL;
+
+ if (!is_node(graph->root, node))
+ isl_die(ctx, isl_error_internal,
+ "space points to invalid node", return NULL);
+ if (graph != graph->root)
+ node = graph_find_node(ctx, graph, node->space);
+ if (!is_node(graph, node))
+ isl_die(ctx, isl_error_internal,
+ "unable to find node", return NULL);
+
+ return node;
+}
+
+/* Internal data structure for add_all_constraints.
+ *
+ * "graph" is the schedule constraint graph for which an LP problem
+ * is being constructed.
+ * "carry_inter" indicates whether inter-node edges should be carried.
+ * "pos" is the position of the next edge that needs to be carried.
+ */
+struct isl_add_all_constraints_data {
+ isl_ctx *ctx;
+ struct isl_sched_graph *graph;
+ int carry_inter;
+ int pos;
+};
+
+/* Add the constraints "coef" derived from an edge from a node to itself
+ * to data->graph->lp in order to respect the dependences and
+ * to try and carry them.
+ *
+ * The space of "coef" is of the form
+ *
+ * coefficients[[c_cst] -> S[c_x]]
+ *
+ * with S[c_x] the (compressed) space of the node.
+ * Extract the node from the space and call add_intra_constraints.
+ */
+static isl_stat lp_add_intra(__isl_take isl_basic_set *coef, void *user)
+{
+ struct isl_add_all_constraints_data *data = user;
+ isl_space *space;
+ struct isl_sched_node *node;
+
+ space = isl_basic_set_get_space(coef);
+ space = isl_space_range(isl_space_unwrap(space));
+ node = graph_find_compressed_node(data->ctx, data->graph, space);
+ isl_space_free(space);
+ return add_intra_constraints(data->graph, node, coef, data->pos++);
+}
+
+/* Add the constraints "coef" derived from an edge from a node j
+ * to a node k to data->graph->lp in order to respect the dependences and
+ * to try and carry them (provided data->carry_inter is set).
+ *
+ * The space of "coef" is of the form
+ *
+ * coefficients[[c_cst, c_n] -> [S_j[c_x] -> S_k[c_y]]]
+ *
+ * with S_j[c_x] and S_k[c_y] the (compressed) spaces of the nodes.
+ * Extract the nodes from the space and call add_inter_constraints.
+ */
+static isl_stat lp_add_inter(__isl_take isl_basic_set *coef, void *user)
+{
+ struct isl_add_all_constraints_data *data = user;
+ isl_space *space, *dom;
+ struct isl_sched_node *src, *dst;
+ int pos;
+
+ space = isl_basic_set_get_space(coef);
+ space = isl_space_unwrap(isl_space_range(isl_space_unwrap(space)));
+ dom = isl_space_domain(isl_space_copy(space));
+ src = graph_find_compressed_node(data->ctx, data->graph, dom);
+ isl_space_free(dom);
+ space = isl_space_range(space);
+ dst = graph_find_compressed_node(data->ctx, data->graph, space);
+ isl_space_free(space);
+
+ pos = data->carry_inter ? data->pos++ : -1;
+ return add_inter_constraints(data->graph, src, dst, coef, pos);
+}
+
+/* Add constraints to graph->lp that force all (conditional) validity
+ * dependences to be respected and attempt to carry them.
+ * "intra" is the sequence of coefficient constraints for intra-node edges.
+ * "inter" is the sequence of coefficient constraints for inter-node edges.
+ * "carry_inter" indicates whether inter-node edges should be carried or
+ * only respected.
+ */
+static isl_stat add_all_constraints(isl_ctx *ctx, struct isl_sched_graph *graph,
+ __isl_keep isl_basic_set_list *intra,
+ __isl_keep isl_basic_set_list *inter, int carry_inter)
+{
+ struct isl_add_all_constraints_data data = { ctx, graph, carry_inter };
+
+ data.pos = 0;
+ if (isl_basic_set_list_foreach(intra, &lp_add_intra, &data) < 0)
+ return isl_stat_error;
+ if (isl_basic_set_list_foreach(inter, &lp_add_inter, &data) < 0)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Internal data structure for count_all_constraints
+ * for keeping track of the number of equality and inequality constraints.
+ */
+struct isl_sched_count {
+ int n_eq;
+ int n_ineq;
+};
+
+/* Add the number of equality and inequality constraints of "bset"
+ * to data->n_eq and data->n_ineq.
+ */
+static isl_stat bset_update_count(__isl_take isl_basic_set *bset, void *user)
+{
+ struct isl_sched_count *data = user;
+
+ return update_count(bset, 1, &data->n_eq, &data->n_ineq);
+}
+
+/* Count the number of equality and inequality constraints
+ * that will be added to the carry_lp problem.
+ * We count each edge exactly once.
+ * "intra" is the sequence of coefficient constraints for intra-node edges.
+ * "inter" is the sequence of coefficient constraints for inter-node edges.
+ */
+static isl_stat count_all_constraints(__isl_keep isl_basic_set_list *intra,
+ __isl_keep isl_basic_set_list *inter, int *n_eq, int *n_ineq)
+{
+ struct isl_sched_count data;
+
+ data.n_eq = data.n_ineq = 0;
+ if (isl_basic_set_list_foreach(inter, &bset_update_count, &data) < 0)
+ return isl_stat_error;
+ if (isl_basic_set_list_foreach(intra, &bset_update_count, &data) < 0)
+ return isl_stat_error;
+
+ *n_eq = data.n_eq;
+ *n_ineq = data.n_ineq;
+
+ return isl_stat_ok;
+}
+
+/* Construct an LP problem for finding schedule coefficients
+ * such that the schedule carries as many validity dependences as possible.
+ * In particular, for each dependence i, we bound the dependence distance
+ * from below by e_i, with 0 <= e_i <= 1 and then maximize the sum
+ * of all e_i's. Dependences with e_i = 0 in the solution are simply
+ * respected, while those with e_i > 0 (in practice e_i = 1) are carried.
+ * "intra" is the sequence of coefficient constraints for intra-node edges.
+ * "inter" is the sequence of coefficient constraints for inter-node edges.
+ * "n_edge" is the total number of edges.
+ * "carry_inter" indicates whether inter-node edges should be carried or
+ * only respected. That is, if "carry_inter" is not set, then
+ * no e_i variables are introduced for the inter-node edges.
+ *
+ * All variables of the LP are non-negative. The actual coefficients
+ * may be negative, so each coefficient is represented as the difference
+ * of two non-negative variables. The negative part always appears
+ * immediately before the positive part.
+ * Other than that, the variables have the following order
+ *
+ * - sum of (1 - e_i) over all edges
+ * - sum of all c_n coefficients
+ * (unconstrained when computing non-parametric schedules)
+ * - sum of positive and negative parts of all c_x coefficients
+ * - for each edge
+ * - e_i
+ * - for each node
+ * - positive and negative parts of c_i_x, in opposite order
+ * - c_i_n (if parametric)
+ * - c_i_0
+ *
+ * The constraints are those from the (validity) edges plus three equalities
+ * to express the sums and n_edge inequalities to express e_i <= 1.
+ */
+static isl_stat setup_carry_lp(isl_ctx *ctx, struct isl_sched_graph *graph,
+ int n_edge, __isl_keep isl_basic_set_list *intra,
+ __isl_keep isl_basic_set_list *inter, int carry_inter)
+{
+ int i;
+ int k;
+ isl_space *space;
+ unsigned total;
+ int n_eq, n_ineq;
+
+ total = 3 + n_edge;
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[graph->sorted[i]];
+ node->start = total;
+ total += 1 + node->nparam + 2 * node->nvar;
+ }
+
+ if (count_all_constraints(intra, inter, &n_eq, &n_ineq) < 0)
+ return isl_stat_error;
+
+ space = isl_space_set_alloc(ctx, 0, total);
+ isl_basic_set_free(graph->lp);
+ n_eq += 3;
+ n_ineq += n_edge;
+ graph->lp = isl_basic_set_alloc_space(space, 0, n_eq, n_ineq);
+ graph->lp = isl_basic_set_set_rational(graph->lp);
+
+ k = isl_basic_set_alloc_equality(graph->lp);
+ if (k < 0)
+ return isl_stat_error;
+ isl_seq_clr(graph->lp->eq[k], 1 + total);
+ isl_int_set_si(graph->lp->eq[k][0], -n_edge);
+ isl_int_set_si(graph->lp->eq[k][1], 1);
+ for (i = 0; i < n_edge; ++i)
+ isl_int_set_si(graph->lp->eq[k][4 + i], 1);
+
+ if (add_param_sum_constraint(graph, 1) < 0)
+ return isl_stat_error;
+ if (add_var_sum_constraint(graph, 2) < 0)
+ return isl_stat_error;
+
+ for (i = 0; i < n_edge; ++i) {
+ k = isl_basic_set_alloc_inequality(graph->lp);
+ if (k < 0)
+ return isl_stat_error;
+ isl_seq_clr(graph->lp->ineq[k], 1 + total);
+ isl_int_set_si(graph->lp->ineq[k][4 + i], -1);
+ isl_int_set_si(graph->lp->ineq[k][0], 1);
+ }
+
+ if (add_all_constraints(ctx, graph, intra, inter, carry_inter) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+static __isl_give isl_schedule_node *compute_component_schedule(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph,
+ int wcc);
+
+/* If the schedule_split_scaled option is set and if the linear
+ * parts of the scheduling rows for all nodes in the graphs have
+ * a non-trivial common divisor, then remove this
+ * common divisor from the linear part.
+ * Otherwise, insert a band node directly and continue with
+ * the construction of the schedule.
+ *
+ * If a non-trivial common divisor is found, then
+ * the linear part is reduced and the remainder is ignored.
+ * The pieces of the graph that are assigned different remainders
+ * form (groups of) strongly connected components within
+ * the scaled down band. If needed, they can therefore
+ * be ordered along this remainder in a sequence node.
+ * However, this ordering is not enforced here in order to allow
+ * the scheduler to combine some of the strongly connected components.
+ */
+static __isl_give isl_schedule_node *split_scaled(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph)
+{
+ int i;
+ int row;
+ isl_ctx *ctx;
+ isl_int gcd, gcd_i;
+ isl_size n_row;
+
+ if (!node)
+ return NULL;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ if (!ctx->opt->schedule_split_scaled)
+ return compute_next_band(node, graph, 0);
+ if (graph->n <= 1)
+ return compute_next_band(node, graph, 0);
+ n_row = isl_mat_rows(graph->node[0].sched);
+ if (n_row < 0)
+ return isl_schedule_node_free(node);
+
+ isl_int_init(gcd);
+ isl_int_init(gcd_i);
+
+ isl_int_set_si(gcd, 0);
+
+ row = n_row - 1;
+
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+ isl_size cols = isl_mat_cols(node->sched);
+
+ if (cols < 0)
+ break;
+ isl_seq_gcd(node->sched->row[row] + 1, cols - 1, &gcd_i);
+ isl_int_gcd(gcd, gcd, gcd_i);
+ }
+
+ isl_int_clear(gcd_i);
+ if (i < graph->n)
+ goto error;
+
+ if (isl_int_cmp_si(gcd, 1) <= 0) {
+ isl_int_clear(gcd);
+ return compute_next_band(node, graph, 0);
+ }
+
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+
+ isl_int_fdiv_q(node->sched->row[row][0],
+ node->sched->row[row][0], gcd);
+ isl_int_mul(node->sched->row[row][0],
+ node->sched->row[row][0], gcd);
+ node->sched = isl_mat_scale_down_row(node->sched, row, gcd);
+ if (!node->sched)
+ goto error;
+ }
+
+ isl_int_clear(gcd);
+
+ return compute_next_band(node, graph, 0);
+error:
+ isl_int_clear(gcd);
+ return isl_schedule_node_free(node);
+}
+
+/* Is the schedule row "sol" trivial on node "node"?
+ * That is, is the solution zero on the dimensions linearly independent of
+ * the previously found solutions?
+ * Return 1 if the solution is trivial, 0 if it is not and -1 on error.
+ *
+ * Each coefficient is represented as the difference between
+ * two non-negative values in "sol".
+ * We construct the schedule row s and check if it is linearly
+ * independent of previously computed schedule rows
+ * by computing T s, with T the linear combinations that are zero
+ * on linearly dependent schedule rows.
+ * If the result consists of all zeros, then the solution is trivial.
+ */
+static int is_trivial(struct isl_sched_node *node, __isl_keep isl_vec *sol)
+{
+ int trivial;
+ isl_vec *node_sol;
+
+ if (!sol)
+ return -1;
+ if (node->nvar == node->rank)
+ return 0;
+
+ node_sol = extract_var_coef(node, sol);
+ node_sol = isl_mat_vec_product(isl_mat_copy(node->indep), node_sol);
+ if (!node_sol)
+ return -1;
+
+ trivial = isl_seq_first_non_zero(node_sol->el,
+ node->nvar - node->rank) == -1;
+
+ isl_vec_free(node_sol);
+
+ return trivial;
+}
+
+/* Is the schedule row "sol" trivial on any node where it should
+ * not be trivial?
+ * Return 1 if any solution is trivial, 0 if they are not and -1 on error.
+ */
+static int is_any_trivial(struct isl_sched_graph *graph,
+ __isl_keep isl_vec *sol)
+{
+ int i;
+
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+ int trivial;
+
+ if (!needs_row(graph, node))
+ continue;
+ trivial = is_trivial(node, sol);
+ if (trivial < 0 || trivial)
+ return trivial;
+ }
+
+ return 0;
+}
+
+/* Does the schedule represented by "sol" perform loop coalescing on "node"?
+ * If so, return the position of the coalesced dimension.
+ * Otherwise, return node->nvar or -1 on error.
+ *
+ * In particular, look for pairs of coefficients c_i and c_j such that
+ * |c_j/c_i| > ceil(size_i/2), i.e., |c_j| > |c_i * ceil(size_i/2)|.
+ * If any such pair is found, then return i.
+ * If size_i is infinity, then no check on c_i needs to be performed.
+ */
+static int find_node_coalescing(struct isl_sched_node *node,
+ __isl_keep isl_vec *sol)
+{
+ int i, j;
+ isl_int max;
+ isl_vec *csol;
+
+ if (node->nvar <= 1)
+ return node->nvar;
+
+ csol = extract_var_coef(node, sol);
+ if (!csol)
+ return -1;
+ isl_int_init(max);
+ for (i = 0; i < node->nvar; ++i) {
+ isl_val *v;
+
+ if (isl_int_is_zero(csol->el[i]))
+ continue;
+ v = isl_multi_val_get_val(node->sizes, i);
+ if (!v)
+ goto error;
+ if (!isl_val_is_int(v)) {
+ isl_val_free(v);
+ continue;
+ }
+ v = isl_val_div_ui(v, 2);
+ v = isl_val_ceil(v);
+ if (!v)
+ goto error;
+ isl_int_mul(max, v->n, csol->el[i]);
+ isl_val_free(v);
+
+ for (j = 0; j < node->nvar; ++j) {
+ if (j == i)
+ continue;
+ if (isl_int_abs_gt(csol->el[j], max))
+ break;
+ }
+ if (j < node->nvar)
+ break;
+ }
+
+ isl_int_clear(max);
+ isl_vec_free(csol);
+ return i;
+error:
+ isl_int_clear(max);
+ isl_vec_free(csol);
+ return -1;
+}
+
+/* Force the schedule coefficient at position "pos" of "node" to be zero
+ * in "tl".
+ * The coefficient is encoded as the difference between two non-negative
+ * variables. Force these two variables to have the same value.
+ */
+static __isl_give isl_tab_lexmin *zero_out_node_coef(
+ __isl_take isl_tab_lexmin *tl, struct isl_sched_node *node, int pos)
+{
+ int dim;
+ isl_ctx *ctx;
+ isl_vec *eq;
+
+ ctx = isl_space_get_ctx(node->space);
+ dim = isl_tab_lexmin_dim(tl);
+ if (dim < 0)
+ return isl_tab_lexmin_free(tl);
+ eq = isl_vec_alloc(ctx, 1 + dim);
+ eq = isl_vec_clr(eq);
+ if (!eq)
+ return isl_tab_lexmin_free(tl);
+
+ pos = 1 + node_var_coef_pos(node, pos);
+ isl_int_set_si(eq->el[pos], 1);
+ isl_int_set_si(eq->el[pos + 1], -1);
+ tl = isl_tab_lexmin_add_eq(tl, eq->el);
+ isl_vec_free(eq);
+
+ return tl;
+}
+
+/* Return the lexicographically smallest rational point in the basic set
+ * from which "tl" was constructed, double checking that this input set
+ * was not empty.
+ */
+static __isl_give isl_vec *non_empty_solution(__isl_keep isl_tab_lexmin *tl)
+{
+ isl_vec *sol;
+
+ sol = isl_tab_lexmin_get_solution(tl);
+ if (!sol)
+ return NULL;
+ if (sol->size == 0)
+ isl_die(isl_vec_get_ctx(sol), isl_error_internal,
+ "error in schedule construction",
+ return isl_vec_free(sol));
+ return sol;
+}
+
+/* Does the solution "sol" of the LP problem constructed by setup_carry_lp
+ * carry any of the "n_edge" groups of dependences?
+ * The value in the first position is the sum of (1 - e_i) over all "n_edge"
+ * edges, with 0 <= e_i <= 1 equal to 1 when the dependences represented
+ * by the edge are carried by the solution.
+ * If the sum of the (1 - e_i) is smaller than "n_edge" then at least
+ * one of those is carried.
+ *
+ * Note that despite the fact that the problem is solved using a rational
+ * solver, the solution is guaranteed to be integral.
+ * Specifically, the dependence distance lower bounds e_i (and therefore
+ * also their sum) are integers. See Lemma 5 of [1].
+ *
+ * Any potential denominator of the sum is cleared by this function.
+ * The denominator is not relevant for any of the other elements
+ * in the solution.
+ *
+ * [1] P. Feautrier, Some Efficient Solutions to the Affine Scheduling
+ * Problem, Part II: Multi-Dimensional Time.
+ * In Intl. Journal of Parallel Programming, 1992.
+ */
+static int carries_dependences(__isl_keep isl_vec *sol, int n_edge)
+{
+ isl_int_divexact(sol->el[1], sol->el[1], sol->el[0]);
+ isl_int_set_si(sol->el[0], 1);
+ return isl_int_cmp_si(sol->el[1], n_edge) < 0;
+}
+
+/* Return the lexicographically smallest rational point in "lp",
+ * assuming that all variables are non-negative and performing some
+ * additional sanity checks.
+ * If "want_integral" is set, then compute the lexicographically smallest
+ * integer point instead.
+ * In particular, "lp" should not be empty by construction.
+ * Double check that this is the case.
+ * If dependences are not carried for any of the "n_edge" edges,
+ * then return an empty vector.
+ *
+ * If the schedule_treat_coalescing option is set and
+ * if the computed schedule performs loop coalescing on a given node,
+ * i.e., if it is of the form
+ *
+ * c_i i + c_j j + ...
+ *
+ * with |c_j/c_i| >= size_i, then force the coefficient c_i to be zero
+ * to cut out this solution. Repeat this process until no more loop
+ * coalescing occurs or until no more dependences can be carried.
+ * In the latter case, revert to the previously computed solution.
+ *
+ * If the caller requests an integral solution and if coalescing should
+ * be treated, then perform the coalescing treatment first as
+ * an integral solution computed before coalescing treatment
+ * would carry the same number of edges and would therefore probably
+ * also be coalescing.
+ *
+ * To allow the coalescing treatment to be performed first,
+ * the initial solution is allowed to be rational and it is only
+ * cut out (if needed) in the next iteration, if no coalescing measures
+ * were taken.
+ */
+static __isl_give isl_vec *non_neg_lexmin(struct isl_sched_graph *graph,
+ __isl_take isl_basic_set *lp, int n_edge, int want_integral)
+{
+ int i, pos, cut;
+ isl_ctx *ctx;
+ isl_tab_lexmin *tl;
+ isl_vec *sol = NULL, *prev;
+ int treat_coalescing;
+ int try_again;
+
+ if (!lp)
+ return NULL;
+ ctx = isl_basic_set_get_ctx(lp);
+ treat_coalescing = isl_options_get_schedule_treat_coalescing(ctx);
+ tl = isl_tab_lexmin_from_basic_set(lp);
+
+ cut = 0;
+ do {
+ int integral;
+
+ try_again = 0;
+ if (cut)
+ tl = isl_tab_lexmin_cut_to_integer(tl);
+ prev = sol;
+ sol = non_empty_solution(tl);
+ if (!sol)
+ goto error;
+
+ integral = isl_int_is_one(sol->el[0]);
+ if (!carries_dependences(sol, n_edge)) {
+ if (!prev)
+ prev = isl_vec_alloc(ctx, 0);
+ isl_vec_free(sol);
+ sol = prev;
+ break;
+ }
+ prev = isl_vec_free(prev);
+ cut = want_integral && !integral;
+ if (cut)
+ try_again = 1;
+ if (!treat_coalescing)
+ continue;
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+
+ pos = find_node_coalescing(node, sol);
+ if (pos < 0)
+ goto error;
+ if (pos < node->nvar)
+ break;
+ }
+ if (i < graph->n) {
+ try_again = 1;
+ tl = zero_out_node_coef(tl, &graph->node[i], pos);
+ cut = 0;
+ }
+ } while (try_again);
+
+ isl_tab_lexmin_free(tl);
+
+ return sol;
+error:
+ isl_tab_lexmin_free(tl);
+ isl_vec_free(prev);
+ isl_vec_free(sol);
+ return NULL;
+}
+
+/* If "edge" is an edge from a node to itself, then add the corresponding
+ * dependence relation to "umap".
+ * If "node" has been compressed, then the dependence relation
+ * is also compressed first.
+ */
+static __isl_give isl_union_map *add_intra(__isl_take isl_union_map *umap,
+ struct isl_sched_edge *edge)
+{
+ isl_map *map;
+ struct isl_sched_node *node = edge->src;
+
+ if (edge->src != edge->dst)
+ return umap;
+
+ map = isl_map_copy(edge->map);
+ map = compress(map, node, node);
+ umap = isl_union_map_add_map(umap, map);
+ return umap;
+}
+
+/* If "edge" is an edge from a node to another node, then add the corresponding
+ * dependence relation to "umap".
+ * If the source or destination nodes of "edge" have been compressed,
+ * then the dependence relation is also compressed first.
+ */
+static __isl_give isl_union_map *add_inter(__isl_take isl_union_map *umap,
+ struct isl_sched_edge *edge)
+{
+ isl_map *map;
+
+ if (edge->src == edge->dst)
+ return umap;
+
+ map = isl_map_copy(edge->map);
+ map = compress(map, edge->src, edge->dst);
+ umap = isl_union_map_add_map(umap, map);
+ return umap;
+}
+
+/* Internal data structure used by union_drop_coalescing_constraints
+ * to collect bounds on all relevant statements.
+ *
+ * "graph" is the schedule constraint graph for which an LP problem
+ * is being constructed.
+ * "bounds" collects the bounds.
+ */
+struct isl_collect_bounds_data {
+ isl_ctx *ctx;
+ struct isl_sched_graph *graph;
+ isl_union_set *bounds;
+};
+
+/* Add the size bounds for the node with instance deltas in "set"
+ * to data->bounds.
+ */
+static isl_stat collect_bounds(__isl_take isl_set *set, void *user)
+{
+ struct isl_collect_bounds_data *data = user;
+ struct isl_sched_node *node;
+ isl_space *space;
+ isl_set *bounds;
+
+ space = isl_set_get_space(set);
+ isl_set_free(set);
+
+ node = graph_find_compressed_node(data->ctx, data->graph, space);
+ isl_space_free(space);
+
+ bounds = isl_set_from_basic_set(get_size_bounds(node));
+ data->bounds = isl_union_set_add_set(data->bounds, bounds);
+
+ return isl_stat_ok;
+}
+
+/* Drop some constraints from "delta" that could be exploited
+ * to construct loop coalescing schedules.
+ * In particular, drop those constraint that bound the difference
+ * to the size of the domain.
+ * Do this for each set/node in "delta" separately.
+ * The parameters are assumed to have been projected out by the caller.
+ */
+static __isl_give isl_union_set *union_drop_coalescing_constraints(isl_ctx *ctx,
+ struct isl_sched_graph *graph, __isl_take isl_union_set *delta)
+{
+ struct isl_collect_bounds_data data = { ctx, graph };
+
+ data.bounds = isl_union_set_empty(isl_space_params_alloc(ctx, 0));
+ if (isl_union_set_foreach_set(delta, &collect_bounds, &data) < 0)
+ data.bounds = isl_union_set_free(data.bounds);
+ delta = isl_union_set_plain_gist(delta, data.bounds);
+
+ return delta;
+}
+
+/* Given a non-trivial lineality space "lineality", add the corresponding
+ * universe set to data->mask and add a map from elements to
+ * other elements along the lines in "lineality" to data->equivalent.
+ * If this is the first time this function gets called
+ * (data->any_non_trivial is still false), then set data->any_non_trivial and
+ * initialize data->mask and data->equivalent.
+ *
+ * In particular, if the lineality space is defined by equality constraints
+ *
+ * E x = 0
+ *
+ * then construct an affine mapping
+ *
+ * f : x -> E x
+ *
+ * and compute the equivalence relation of having the same image under f:
+ *
+ * { x -> x' : E x = E x' }
+ */
+static isl_stat add_non_trivial_lineality(__isl_take isl_basic_set *lineality,
+ struct isl_exploit_lineality_data *data)
+{
+ isl_mat *eq;
+ isl_space *space;
+ isl_set *univ;
+ isl_multi_aff *ma;
+ isl_multi_pw_aff *mpa;
+ isl_map *map;
+ isl_size n;
+
+ if (isl_basic_set_check_no_locals(lineality) < 0)
+ goto error;
+
+ space = isl_basic_set_get_space(lineality);
+ if (!data->any_non_trivial) {
+ data->equivalent = isl_union_map_empty(isl_space_copy(space));
+ data->mask = isl_union_set_empty(isl_space_copy(space));
+ }
+ data->any_non_trivial = isl_bool_true;
+
+ univ = isl_set_universe(isl_space_copy(space));
+ data->mask = isl_union_set_add_set(data->mask, univ);
+
+ eq = isl_basic_set_extract_equalities(lineality);
+ n = isl_mat_rows(eq);
+ if (n < 0)
+ space = isl_space_free(space);
+ eq = isl_mat_insert_zero_rows(eq, 0, 1);
+ eq = isl_mat_set_element_si(eq, 0, 0, 1);
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, n);
+ ma = isl_multi_aff_from_aff_mat(space, eq);
+ mpa = isl_multi_pw_aff_from_multi_aff(ma);
+ map = isl_multi_pw_aff_eq_map(mpa, isl_multi_pw_aff_copy(mpa));
+ data->equivalent = isl_union_map_add_map(data->equivalent, map);
+
+ isl_basic_set_free(lineality);
+ return isl_stat_ok;
+error:
+ isl_basic_set_free(lineality);
+ return isl_stat_error;
+}
+
+/* Check if the lineality space "set" is non-trivial (i.e., is not just
+ * the origin or, in other words, satisfies a number of equality constraints
+ * that is smaller than the dimension of the set).
+ * If so, extend data->mask and data->equivalent accordingly.
+ *
+ * The input should not have any local variables already, but
+ * isl_set_remove_divs is called to make sure it does not.
+ */
+static isl_stat add_lineality(__isl_take isl_set *set, void *user)
+{
+ struct isl_exploit_lineality_data *data = user;
+ isl_basic_set *hull;
+ isl_size dim;
+ isl_size n_eq;
+
+ set = isl_set_remove_divs(set);
+ hull = isl_set_unshifted_simple_hull(set);
+ dim = isl_basic_set_dim(hull, isl_dim_set);
+ n_eq = isl_basic_set_n_equality(hull);
+ if (dim < 0 || n_eq < 0)
+ goto error;
+ if (dim != n_eq)
+ return add_non_trivial_lineality(hull, data);
+ isl_basic_set_free(hull);
+ return isl_stat_ok;
+error:
+ isl_basic_set_free(hull);
+ return isl_stat_error;
+}
+
+/* Check if the difference set on intra-node schedule constraints "intra"
+ * has any non-trivial lineality space.
+ * If so, then extend the difference set to a difference set
+ * on equivalent elements. That is, if "intra" is
+ *
+ * { y - x : (x,y) \in V }
+ *
+ * and elements are equivalent if they have the same image under f,
+ * then return
+ *
+ * { y' - x' : (x,y) \in V and f(x) = f(x') and f(y) = f(y') }
+ *
+ * or, since f is linear,
+ *
+ * { y' - x' : (x,y) \in V and f(y - x) = f(y' - x') }
+ *
+ * The results of the search for non-trivial lineality spaces is stored
+ * in "data".
+ */
+static __isl_give isl_union_set *exploit_intra_lineality(
+ __isl_take isl_union_set *intra,
+ struct isl_exploit_lineality_data *data)
+{
+ isl_union_set *lineality;
+ isl_union_set *uset;
+
+ data->any_non_trivial = isl_bool_false;
+ lineality = isl_union_set_copy(intra);
+ lineality = isl_union_set_combined_lineality_space(lineality);
+ if (isl_union_set_foreach_set(lineality, &add_lineality, data) < 0)
+ data->any_non_trivial = isl_bool_error;
+ isl_union_set_free(lineality);
+
+ if (data->any_non_trivial < 0)
+ return isl_union_set_free(intra);
+ if (!data->any_non_trivial)
+ return intra;
+
+ uset = isl_union_set_copy(intra);
+ intra = isl_union_set_subtract(intra, isl_union_set_copy(data->mask));
+ uset = isl_union_set_apply(uset, isl_union_map_copy(data->equivalent));
+ intra = isl_union_set_union(intra, uset);
+
+ intra = isl_union_set_remove_divs(intra);
+
+ return intra;
+}
+
+/* If the difference set on intra-node schedule constraints was found to have
+ * any non-trivial lineality space by exploit_intra_lineality,
+ * as recorded in "data", then extend the inter-node
+ * schedule constraints "inter" to schedule constraints on equivalent elements.
+ * That is, if "inter" is V and
+ * elements are equivalent if they have the same image under f, then return
+ *
+ * { (x', y') : (x,y) \in V and f(x) = f(x') and f(y) = f(y') }
+ */
+static __isl_give isl_union_map *exploit_inter_lineality(
+ __isl_take isl_union_map *inter,
+ struct isl_exploit_lineality_data *data)
+{
+ isl_union_map *umap;
+
+ if (data->any_non_trivial < 0)
+ return isl_union_map_free(inter);
+ if (!data->any_non_trivial)
+ return inter;
+
+ umap = isl_union_map_copy(inter);
+ inter = isl_union_map_subtract_range(inter,
+ isl_union_set_copy(data->mask));
+ umap = isl_union_map_apply_range(umap,
+ isl_union_map_copy(data->equivalent));
+ inter = isl_union_map_union(inter, umap);
+ umap = isl_union_map_copy(inter);
+ inter = isl_union_map_subtract_domain(inter,
+ isl_union_set_copy(data->mask));
+ umap = isl_union_map_apply_range(isl_union_map_copy(data->equivalent),
+ umap);
+ inter = isl_union_map_union(inter, umap);
+
+ inter = isl_union_map_remove_divs(inter);
+
+ return inter;
+}
+
+/* For each (conditional) validity edge in "graph",
+ * add the corresponding dependence relation using "add"
+ * to a collection of dependence relations and return the result.
+ * If "coincidence" is set, then coincidence edges are considered as well.
+ */
+static __isl_give isl_union_map *collect_validity(struct isl_sched_graph *graph,
+ __isl_give isl_union_map *(*add)(__isl_take isl_union_map *umap,
+ struct isl_sched_edge *edge), int coincidence)
+{
+ int i;
+ isl_space *space;
+ isl_union_map *umap;
+
+ space = isl_space_copy(graph->node[0].space);
+ umap = isl_union_map_empty(space);
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ struct isl_sched_edge *edge = &graph->edge[i];
+
+ if (!is_any_validity(edge) &&
+ (!coincidence || !is_coincidence(edge)))
+ continue;
+
+ umap = add(umap, edge);
+ }
+
+ return umap;
+}
+
+/* For each dependence relation on a (conditional) validity edge
+ * from a node to itself,
+ * construct the set of coefficients of valid constraints for elements
+ * in that dependence relation and collect the results.
+ * If "coincidence" is set, then coincidence edges are considered as well.
+ *
+ * In particular, for each dependence relation R, constraints
+ * on coefficients (c_0, c_x) are constructed such that
+ *
+ * c_0 + c_x d >= 0 for each d in delta R = { y - x | (x,y) in R }
+ *
+ * If the schedule_treat_coalescing option is set, then some constraints
+ * that could be exploited to construct coalescing schedules
+ * are removed before the dual is computed, but after the parameters
+ * have been projected out.
+ * The entire computation is essentially the same as that performed
+ * by intra_coefficients, except that it operates on multiple
+ * edges together and that the parameters are always projected out.
+ *
+ * Additionally, exploit any non-trivial lineality space
+ * in the difference set after removing coalescing constraints and
+ * store the results of the non-trivial lineality space detection in "data".
+ * The procedure is currently run unconditionally, but it is unlikely
+ * to find any non-trivial lineality spaces if no coalescing constraints
+ * have been removed.
+ *
+ * Note that if a dependence relation is a union of basic maps,
+ * then each basic map needs to be treated individually as it may only
+ * be possible to carry the dependences expressed by some of those
+ * basic maps and not all of them.
+ * The collected validity constraints are therefore not coalesced and
+ * it is assumed that they are not coalesced automatically.
+ * Duplicate basic maps can be removed, however.
+ * In particular, if the same basic map appears as a disjunct
+ * in multiple edges, then it only needs to be carried once.
+ */
+static __isl_give isl_basic_set_list *collect_intra_validity(isl_ctx *ctx,
+ struct isl_sched_graph *graph, int coincidence,
+ struct isl_exploit_lineality_data *data)
+{
+ isl_union_map *intra;
+ isl_union_set *delta;
+ isl_basic_set_list *list;
+
+ intra = collect_validity(graph, &add_intra, coincidence);
+ delta = isl_union_map_deltas(intra);
+ delta = isl_union_set_project_out_all_params(delta);
+ delta = isl_union_set_remove_divs(delta);
+ if (isl_options_get_schedule_treat_coalescing(ctx))
+ delta = union_drop_coalescing_constraints(ctx, graph, delta);
+ delta = exploit_intra_lineality(delta, data);
+ list = isl_union_set_get_basic_set_list(delta);
+ isl_union_set_free(delta);
+
+ return isl_basic_set_list_coefficients(list);
+}
+
+/* For each dependence relation on a (conditional) validity edge
+ * from a node to some other node,
+ * construct the set of coefficients of valid constraints for elements
+ * in that dependence relation and collect the results.
+ * If "coincidence" is set, then coincidence edges are considered as well.
+ *
+ * In particular, for each dependence relation R, constraints
+ * on coefficients (c_0, c_n, c_x, c_y) are constructed such that
+ *
+ * c_0 + c_n n + c_x x + c_y y >= 0 for each (x,y) in R
+ *
+ * This computation is essentially the same as that performed
+ * by inter_coefficients, except that it operates on multiple
+ * edges together.
+ *
+ * Additionally, exploit any non-trivial lineality space
+ * that may have been discovered by collect_intra_validity
+ * (as stored in "data").
+ *
+ * Note that if a dependence relation is a union of basic maps,
+ * then each basic map needs to be treated individually as it may only
+ * be possible to carry the dependences expressed by some of those
+ * basic maps and not all of them.
+ * The collected validity constraints are therefore not coalesced and
+ * it is assumed that they are not coalesced automatically.
+ * Duplicate basic maps can be removed, however.
+ * In particular, if the same basic map appears as a disjunct
+ * in multiple edges, then it only needs to be carried once.
+ */
+static __isl_give isl_basic_set_list *collect_inter_validity(
+ struct isl_sched_graph *graph, int coincidence,
+ struct isl_exploit_lineality_data *data)
+{
+ isl_union_map *inter;
+ isl_union_set *wrap;
+ isl_basic_set_list *list;
+
+ inter = collect_validity(graph, &add_inter, coincidence);
+ inter = exploit_inter_lineality(inter, data);
+ inter = isl_union_map_remove_divs(inter);
+ wrap = isl_union_map_wrap(inter);
+ list = isl_union_set_get_basic_set_list(wrap);
+ isl_union_set_free(wrap);
+ return isl_basic_set_list_coefficients(list);
+}
+
+/* Construct an LP problem for finding schedule coefficients
+ * such that the schedule carries as many of the "n_edge" groups of
+ * dependences as possible based on the corresponding coefficient
+ * constraints and return the lexicographically smallest non-trivial solution.
+ * "intra" is the sequence of coefficient constraints for intra-node edges.
+ * "inter" is the sequence of coefficient constraints for inter-node edges.
+ * If "want_integral" is set, then compute an integral solution
+ * for the coefficients rather than using the numerators
+ * of a rational solution.
+ * "carry_inter" indicates whether inter-node edges should be carried or
+ * only respected.
+ *
+ * If none of the "n_edge" groups can be carried
+ * then return an empty vector.
+ */
+static __isl_give isl_vec *compute_carrying_sol_coef(isl_ctx *ctx,
+ struct isl_sched_graph *graph, int n_edge,
+ __isl_keep isl_basic_set_list *intra,
+ __isl_keep isl_basic_set_list *inter, int want_integral,
+ int carry_inter)
+{
+ isl_basic_set *lp;
+
+ if (setup_carry_lp(ctx, graph, n_edge, intra, inter, carry_inter) < 0)
+ return NULL;
+
+ lp = isl_basic_set_copy(graph->lp);
+ return non_neg_lexmin(graph, lp, n_edge, want_integral);
+}
+
+/* Construct an LP problem for finding schedule coefficients
+ * such that the schedule carries as many of the validity dependences
+ * as possible and
+ * return the lexicographically smallest non-trivial solution.
+ * If "fallback" is set, then the carrying is performed as a fallback
+ * for the Pluto-like scheduler.
+ * If "coincidence" is set, then try and carry coincidence edges as well.
+ *
+ * The variable "n_edge" stores the number of groups that should be carried.
+ * If none of the "n_edge" groups can be carried
+ * then return an empty vector.
+ * If, moreover, "n_edge" is zero, then the LP problem does not even
+ * need to be constructed.
+ *
+ * If a fallback solution is being computed, then compute an integral solution
+ * for the coefficients rather than using the numerators
+ * of a rational solution.
+ *
+ * If a fallback solution is being computed, if there are any intra-node
+ * dependences, and if requested by the user, then first try
+ * to only carry those intra-node dependences.
+ * If this fails to carry any dependences, then try again
+ * with the inter-node dependences included.
+ */
+static __isl_give isl_vec *compute_carrying_sol(isl_ctx *ctx,
+ struct isl_sched_graph *graph, int fallback, int coincidence)
+{
+ isl_size n_intra, n_inter;
+ int n_edge;
+ struct isl_carry carry = { 0 };
+ isl_vec *sol;
+
+ carry.intra = collect_intra_validity(ctx, graph, coincidence,
+ &carry.lineality);
+ carry.inter = collect_inter_validity(graph, coincidence,
+ &carry.lineality);
+ n_intra = isl_basic_set_list_n_basic_set(carry.intra);
+ n_inter = isl_basic_set_list_n_basic_set(carry.inter);
+ if (n_intra < 0 || n_inter < 0)
+ goto error;
+
+ if (fallback && n_intra > 0 &&
+ isl_options_get_schedule_carry_self_first(ctx)) {
+ sol = compute_carrying_sol_coef(ctx, graph, n_intra,
+ carry.intra, carry.inter, fallback, 0);
+ if (!sol || sol->size != 0 || n_inter == 0) {
+ isl_carry_clear(&carry);
+ return sol;
+ }
+ isl_vec_free(sol);
+ }
+
+ n_edge = n_intra + n_inter;
+ if (n_edge == 0) {
+ isl_carry_clear(&carry);
+ return isl_vec_alloc(ctx, 0);
+ }
+
+ sol = compute_carrying_sol_coef(ctx, graph, n_edge,
+ carry.intra, carry.inter, fallback, 1);
+ isl_carry_clear(&carry);
+ return sol;
+error:
+ isl_carry_clear(&carry);
+ return NULL;
+}
+
+/* Construct a schedule row for each node such that as many validity dependences
+ * as possible are carried and then continue with the next band.
+ * If "fallback" is set, then the carrying is performed as a fallback
+ * for the Pluto-like scheduler.
+ * If "coincidence" is set, then try and carry coincidence edges as well.
+ *
+ * If there are no validity dependences, then no dependence can be carried and
+ * the procedure is guaranteed to fail. If there is more than one component,
+ * then try computing a schedule on each component separately
+ * to prevent or at least postpone this failure.
+ *
+ * If a schedule row is computed, then check that dependences are carried
+ * for at least one of the edges.
+ *
+ * If the computed schedule row turns out to be trivial on one or
+ * more nodes where it should not be trivial, then we throw it away
+ * and try again on each component separately.
+ *
+ * If there is only one component, then we accept the schedule row anyway,
+ * but we do not consider it as a complete row and therefore do not
+ * increment graph->n_row. Note that the ranks of the nodes that
+ * do get a non-trivial schedule part will get updated regardless and
+ * graph->maxvar is computed based on these ranks. The test for
+ * whether more schedule rows are required in compute_schedule_wcc
+ * is therefore not affected.
+ *
+ * Insert a band corresponding to the schedule row at position "node"
+ * of the schedule tree and continue with the construction of the schedule.
+ * This insertion and the continued construction is performed by split_scaled
+ * after optionally checking for non-trivial common divisors.
+ */
+static __isl_give isl_schedule_node *carry(__isl_take isl_schedule_node *node,
+ struct isl_sched_graph *graph, int fallback, int coincidence)
+{
+ int trivial;
+ isl_ctx *ctx;
+ isl_vec *sol;
+
+ if (!node)
+ return NULL;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ sol = compute_carrying_sol(ctx, graph, fallback, coincidence);
+ if (!sol)
+ return isl_schedule_node_free(node);
+ if (sol->size == 0) {
+ isl_vec_free(sol);
+ if (graph->scc > 1)
+ return compute_component_schedule(node, graph, 1);
+ isl_die(ctx, isl_error_unknown, "unable to carry dependences",
+ return isl_schedule_node_free(node));
+ }
+
+ trivial = is_any_trivial(graph, sol);
+ if (trivial < 0) {
+ sol = isl_vec_free(sol);
+ } else if (trivial && graph->scc > 1) {
+ isl_vec_free(sol);
+ return compute_component_schedule(node, graph, 1);
+ }
+
+ if (update_schedule(graph, sol, 0) < 0)
+ return isl_schedule_node_free(node);
+ if (trivial)
+ graph->n_row--;
+
+ return split_scaled(node, graph);
+}
+
+/* Construct a schedule row for each node such that as many validity dependences
+ * as possible are carried and then continue with the next band.
+ * Do so as a fallback for the Pluto-like scheduler.
+ * If "coincidence" is set, then try and carry coincidence edges as well.
+ */
+static __isl_give isl_schedule_node *carry_fallback(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph,
+ int coincidence)
+{
+ return carry(node, graph, 1, coincidence);
+}
+
+/* Construct a schedule row for each node such that as many validity dependences
+ * as possible are carried and then continue with the next band.
+ * Do so for the case where the Feautrier scheduler was selected
+ * by the user.
+ */
+static __isl_give isl_schedule_node *carry_feautrier(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph)
+{
+ return carry(node, graph, 0, 0);
+}
+
+/* Construct a schedule row for each node such that as many validity dependences
+ * as possible are carried and then continue with the next band.
+ * Do so as a fallback for the Pluto-like scheduler.
+ */
+static __isl_give isl_schedule_node *carry_dependences(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph)
+{
+ return carry_fallback(node, graph, 0);
+}
+
+/* Construct a schedule row for each node such that as many validity or
+ * coincidence dependences as possible are carried and
+ * then continue with the next band.
+ * Do so as a fallback for the Pluto-like scheduler.
+ */
+static __isl_give isl_schedule_node *carry_coincidence(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph)
+{
+ return carry_fallback(node, graph, 1);
+}
+
+/* Topologically sort statements mapped to the same schedule iteration
+ * and add insert a sequence node in front of "node"
+ * corresponding to this order.
+ * If "initialized" is set, then it may be assumed that compute_maxvar
+ * has been called on the current band. Otherwise, call
+ * compute_maxvar if and before carry_dependences gets called.
+ *
+ * If it turns out to be impossible to sort the statements apart,
+ * because different dependences impose different orderings
+ * on the statements, then we extend the schedule such that
+ * it carries at least one more dependence.
+ */
+static __isl_give isl_schedule_node *sort_statements(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph,
+ int initialized)
+{
+ isl_ctx *ctx;
+ isl_union_set_list *filters;
+
+ if (!node)
+ return NULL;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ if (graph->n < 1)
+ isl_die(ctx, isl_error_internal,
+ "graph should have at least one node",
+ return isl_schedule_node_free(node));
+
+ if (graph->n == 1)
+ return node;
+
+ if (update_edges(ctx, graph) < 0)
+ return isl_schedule_node_free(node);
+
+ if (graph->n_edge == 0)
+ return node;
+
+ if (detect_sccs(ctx, graph) < 0)
+ return isl_schedule_node_free(node);
+
+ next_band(graph);
+ if (graph->scc < graph->n) {
+ if (!initialized && compute_maxvar(graph) < 0)
+ return isl_schedule_node_free(node);
+ return carry_dependences(node, graph);
+ }
+
+ filters = extract_sccs(ctx, graph);
+ node = isl_schedule_node_insert_sequence(node, filters);
+
+ return node;
+}
+
+/* Are there any (non-empty) (conditional) validity edges in the graph?
+ */
+static int has_validity_edges(struct isl_sched_graph *graph)
+{
+ int i;
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ int empty;
+
+ empty = isl_map_plain_is_empty(graph->edge[i].map);
+ if (empty < 0)
+ return -1;
+ if (empty)
+ continue;
+ if (is_any_validity(&graph->edge[i]))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Should we apply a Feautrier step?
+ * That is, did the user request the Feautrier algorithm and are
+ * there any validity dependences (left)?
+ */
+static int need_feautrier_step(isl_ctx *ctx, struct isl_sched_graph *graph)
+{
+ if (ctx->opt->schedule_algorithm != ISL_SCHEDULE_ALGORITHM_FEAUTRIER)
+ return 0;
+
+ return has_validity_edges(graph);
+}
+
+/* Compute a schedule for a connected dependence graph using Feautrier's
+ * multi-dimensional scheduling algorithm and return the updated schedule node.
+ *
+ * The original algorithm is described in [1].
+ * The main idea is to minimize the number of scheduling dimensions, by
+ * trying to satisfy as many dependences as possible per scheduling dimension.
+ *
+ * [1] P. Feautrier, Some Efficient Solutions to the Affine Scheduling
+ * Problem, Part II: Multi-Dimensional Time.
+ * In Intl. Journal of Parallel Programming, 1992.
+ */
+static __isl_give isl_schedule_node *compute_schedule_wcc_feautrier(
+ isl_schedule_node *node, struct isl_sched_graph *graph)
+{
+ return carry_feautrier(node, graph);
+}
+
+/* Turn off the "local" bit on all (condition) edges.
+ */
+static void clear_local_edges(struct isl_sched_graph *graph)
+{
+ int i;
+
+ for (i = 0; i < graph->n_edge; ++i)
+ if (is_condition(&graph->edge[i]))
+ clear_local(&graph->edge[i]);
+}
+
+/* Does "graph" have both condition and conditional validity edges?
+ */
+static int need_condition_check(struct isl_sched_graph *graph)
+{
+ int i;
+ int any_condition = 0;
+ int any_conditional_validity = 0;
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ if (is_condition(&graph->edge[i]))
+ any_condition = 1;
+ if (is_conditional_validity(&graph->edge[i]))
+ any_conditional_validity = 1;
+ }
+
+ return any_condition && any_conditional_validity;
+}
+
+/* Does "graph" contain any coincidence edge?
+ */
+static int has_any_coincidence(struct isl_sched_graph *graph)
+{
+ int i;
+
+ for (i = 0; i < graph->n_edge; ++i)
+ if (is_coincidence(&graph->edge[i]))
+ return 1;
+
+ return 0;
+}
+
+/* Extract the final schedule row as a map with the iteration domain
+ * of "node" as domain.
+ */
+static __isl_give isl_map *final_row(struct isl_sched_node *node)
+{
+ isl_multi_aff *ma;
+ isl_size n_row;
+
+ n_row = isl_mat_rows(node->sched);
+ if (n_row < 0)
+ return NULL;
+ ma = node_extract_partial_schedule_multi_aff(node, n_row - 1, 1);
+ return isl_map_from_multi_aff(ma);
+}
+
+/* Is the conditional validity dependence in the edge with index "edge_index"
+ * violated by the latest (i.e., final) row of the schedule?
+ * That is, is i scheduled after j
+ * for any conditional validity dependence i -> j?
+ */
+static int is_violated(struct isl_sched_graph *graph, int edge_index)
+{
+ isl_map *src_sched, *dst_sched, *map;
+ struct isl_sched_edge *edge = &graph->edge[edge_index];
+ int empty;
+
+ src_sched = final_row(edge->src);
+ dst_sched = final_row(edge->dst);
+ map = isl_map_copy(edge->map);
+ map = isl_map_apply_domain(map, src_sched);
+ map = isl_map_apply_range(map, dst_sched);
+ map = isl_map_order_gt(map, isl_dim_in, 0, isl_dim_out, 0);
+ empty = isl_map_is_empty(map);
+ isl_map_free(map);
+
+ if (empty < 0)
+ return -1;
+
+ return !empty;
+}
+
+/* Does "graph" have any satisfied condition edges that
+ * are adjacent to the conditional validity constraint with
+ * domain "conditional_source" and range "conditional_sink"?
+ *
+ * A satisfied condition is one that is not local.
+ * If a condition was forced to be local already (i.e., marked as local)
+ * then there is no need to check if it is in fact local.
+ *
+ * Additionally, mark all adjacent condition edges found as local.
+ */
+static int has_adjacent_true_conditions(struct isl_sched_graph *graph,
+ __isl_keep isl_union_set *conditional_source,
+ __isl_keep isl_union_set *conditional_sink)
+{
+ int i;
+ int any = 0;
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ int adjacent, local;
+ isl_union_map *condition;
+
+ if (!is_condition(&graph->edge[i]))
+ continue;
+ if (is_local(&graph->edge[i]))
+ continue;
+
+ condition = graph->edge[i].tagged_condition;
+ adjacent = domain_intersects(condition, conditional_sink);
+ if (adjacent >= 0 && !adjacent)
+ adjacent = range_intersects(condition,
+ conditional_source);
+ if (adjacent < 0)
+ return -1;
+ if (!adjacent)
+ continue;
+
+ set_local(&graph->edge[i]);
+
+ local = is_condition_false(&graph->edge[i]);
+ if (local < 0)
+ return -1;
+ if (!local)
+ any = 1;
+ }
+
+ return any;
+}
+
+/* Are there any violated conditional validity dependences with
+ * adjacent condition dependences that are not local with respect
+ * to the current schedule?
+ * That is, is the conditional validity constraint violated?
+ *
+ * Additionally, mark all those adjacent condition dependences as local.
+ * We also mark those adjacent condition dependences that were not marked
+ * as local before, but just happened to be local already. This ensures
+ * that they remain local if the schedule is recomputed.
+ *
+ * We first collect domain and range of all violated conditional validity
+ * dependences and then check if there are any adjacent non-local
+ * condition dependences.
+ */
+static int has_violated_conditional_constraint(isl_ctx *ctx,
+ struct isl_sched_graph *graph)
+{
+ int i;
+ int any = 0;
+ isl_union_set *source, *sink;
+
+ source = isl_union_set_empty(isl_space_params_alloc(ctx, 0));
+ sink = isl_union_set_empty(isl_space_params_alloc(ctx, 0));
+ for (i = 0; i < graph->n_edge; ++i) {
+ isl_union_set *uset;
+ isl_union_map *umap;
+ int violated;
+
+ if (!is_conditional_validity(&graph->edge[i]))
+ continue;
+
+ violated = is_violated(graph, i);
+ if (violated < 0)
+ goto error;
+ if (!violated)
+ continue;
+
+ any = 1;
+
+ umap = isl_union_map_copy(graph->edge[i].tagged_validity);
+ uset = isl_union_map_domain(umap);
+ source = isl_union_set_union(source, uset);
+ source = isl_union_set_coalesce(source);
+
+ umap = isl_union_map_copy(graph->edge[i].tagged_validity);
+ uset = isl_union_map_range(umap);
+ sink = isl_union_set_union(sink, uset);
+ sink = isl_union_set_coalesce(sink);
+ }
+
+ if (any)
+ any = has_adjacent_true_conditions(graph, source, sink);
+
+ isl_union_set_free(source);
+ isl_union_set_free(sink);
+ return any;
+error:
+ isl_union_set_free(source);
+ isl_union_set_free(sink);
+ return -1;
+}
+
+/* Examine the current band (the rows between graph->band_start and
+ * graph->n_total_row), deciding whether to drop it or add it to "node"
+ * and then continue with the computation of the next band, if any.
+ * If "initialized" is set, then it may be assumed that compute_maxvar
+ * has been called on the current band. Otherwise, call
+ * compute_maxvar if and before carry_dependences gets called.
+ *
+ * The caller keeps looking for a new row as long as
+ * graph->n_row < graph->maxvar. If the latest attempt to find
+ * such a row failed (i.e., we still have graph->n_row < graph->maxvar),
+ * then we either
+ * - split between SCCs and start over (assuming we found an interesting
+ * pair of SCCs between which to split)
+ * - continue with the next band (assuming the current band has at least
+ * one row)
+ * - if there is more than one SCC left, then split along all SCCs
+ * - if outer coincidence needs to be enforced, then try to carry as many
+ * validity or coincidence dependences as possible and
+ * continue with the next band
+ * - try to carry as many validity dependences as possible and
+ * continue with the next band
+ * In each case, we first insert a band node in the schedule tree
+ * if any rows have been computed.
+ *
+ * If the caller managed to complete the schedule and the current band
+ * is empty, then finish off by topologically
+ * sorting the statements based on the remaining dependences.
+ * If, on the other hand, the current band has at least one row,
+ * then continue with the next band. Note that this next band
+ * will necessarily be empty, but the graph may still be split up
+ * into weakly connected components before arriving back here.
+ */
+static __isl_give isl_schedule_node *compute_schedule_finish_band(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph,
+ int initialized)
+{
+ int empty;
+
+ if (!node)
+ return NULL;
+
+ empty = graph->n_total_row == graph->band_start;
+ if (graph->n_row < graph->maxvar) {
+ isl_ctx *ctx;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ if (!ctx->opt->schedule_maximize_band_depth && !empty)
+ return compute_next_band(node, graph, 1);
+ if (graph->src_scc >= 0)
+ return compute_split_schedule(node, graph);
+ if (!empty)
+ return compute_next_band(node, graph, 1);
+ if (graph->scc > 1)
+ return compute_component_schedule(node, graph, 1);
+ if (!initialized && compute_maxvar(graph) < 0)
+ return isl_schedule_node_free(node);
+ if (isl_options_get_schedule_outer_coincidence(ctx))
+ return carry_coincidence(node, graph);
+ return carry_dependences(node, graph);
+ }
+
+ if (!empty)
+ return compute_next_band(node, graph, 1);
+ return sort_statements(node, graph, initialized);
+}
+
+/* Construct a band of schedule rows for a connected dependence graph.
+ * The caller is responsible for determining the strongly connected
+ * components and calling compute_maxvar first.
+ *
+ * We try to find a sequence of as many schedule rows as possible that result
+ * in non-negative dependence distances (independent of the previous rows
+ * in the sequence, i.e., such that the sequence is tilable), with as
+ * many of the initial rows as possible satisfying the coincidence constraints.
+ * The computation stops if we can't find any more rows or if we have found
+ * all the rows we wanted to find.
+ *
+ * If ctx->opt->schedule_outer_coincidence is set, then we force the
+ * outermost dimension to satisfy the coincidence constraints. If this
+ * turns out to be impossible, we fall back on the general scheme above
+ * and try to carry as many dependences as possible.
+ *
+ * If "graph" contains both condition and conditional validity dependences,
+ * then we need to check that that the conditional schedule constraint
+ * is satisfied, i.e., there are no violated conditional validity dependences
+ * that are adjacent to any non-local condition dependences.
+ * If there are, then we mark all those adjacent condition dependences
+ * as local and recompute the current band. Those dependences that
+ * are marked local will then be forced to be local.
+ * The initial computation is performed with no dependences marked as local.
+ * If we are lucky, then there will be no violated conditional validity
+ * dependences adjacent to any non-local condition dependences.
+ * Otherwise, we mark some additional condition dependences as local and
+ * recompute. We continue this process until there are no violations left or
+ * until we are no longer able to compute a schedule.
+ * Since there are only a finite number of dependences,
+ * there will only be a finite number of iterations.
+ */
+static isl_stat compute_schedule_wcc_band(isl_ctx *ctx,
+ struct isl_sched_graph *graph)
+{
+ int has_coincidence;
+ int use_coincidence;
+ int force_coincidence = 0;
+ int check_conditional;
+
+ if (sort_sccs(graph) < 0)
+ return isl_stat_error;
+
+ clear_local_edges(graph);
+ check_conditional = need_condition_check(graph);
+ has_coincidence = has_any_coincidence(graph);
+
+ if (ctx->opt->schedule_outer_coincidence)
+ force_coincidence = 1;
+
+ use_coincidence = has_coincidence;
+ while (graph->n_row < graph->maxvar) {
+ isl_vec *sol;
+ int violated;
+ int coincident;
+
+ graph->src_scc = -1;
+ graph->dst_scc = -1;
+
+ if (setup_lp(ctx, graph, use_coincidence) < 0)
+ return isl_stat_error;
+ sol = solve_lp(ctx, graph);
+ if (!sol)
+ return isl_stat_error;
+ if (sol->size == 0) {
+ int empty = graph->n_total_row == graph->band_start;
+
+ isl_vec_free(sol);
+ if (use_coincidence && (!force_coincidence || !empty)) {
+ use_coincidence = 0;
+ continue;
+ }
+ return isl_stat_ok;
+ }
+ coincident = !has_coincidence || use_coincidence;
+ if (update_schedule(graph, sol, coincident) < 0)
+ return isl_stat_error;
+
+ if (!check_conditional)
+ continue;
+ violated = has_violated_conditional_constraint(ctx, graph);
+ if (violated < 0)
+ return isl_stat_error;
+ if (!violated)
+ continue;
+ if (reset_band(graph) < 0)
+ return isl_stat_error;
+ use_coincidence = has_coincidence;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Compute a schedule for a connected dependence graph by considering
+ * the graph as a whole and return the updated schedule node.
+ *
+ * The actual schedule rows of the current band are computed by
+ * compute_schedule_wcc_band. compute_schedule_finish_band takes
+ * care of integrating the band into "node" and continuing
+ * the computation.
+ */
+static __isl_give isl_schedule_node *compute_schedule_wcc_whole(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph)
+{
+ isl_ctx *ctx;
+
+ if (!node)
+ return NULL;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ if (compute_schedule_wcc_band(ctx, graph) < 0)
+ return isl_schedule_node_free(node);
+
+ return compute_schedule_finish_band(node, graph, 1);
+}
+
+/* Clustering information used by compute_schedule_wcc_clustering.
+ *
+ * "n" is the number of SCCs in the original dependence graph
+ * "scc" is an array of "n" elements, each representing an SCC
+ * of the original dependence graph. All entries in the same cluster
+ * have the same number of schedule rows.
+ * "scc_cluster" maps each SCC index to the cluster to which it belongs,
+ * where each cluster is represented by the index of the first SCC
+ * in the cluster. Initially, each SCC belongs to a cluster containing
+ * only that SCC.
+ *
+ * "scc_in_merge" is used by merge_clusters_along_edge to keep
+ * track of which SCCs need to be merged.
+ *
+ * "cluster" contains the merged clusters of SCCs after the clustering
+ * has completed.
+ *
+ * "scc_node" is a temporary data structure used inside copy_partial.
+ * For each SCC, it keeps track of the number of nodes in the SCC
+ * that have already been copied.
+ */
+struct isl_clustering {
+ int n;
+ struct isl_sched_graph *scc;
+ struct isl_sched_graph *cluster;
+ int *scc_cluster;
+ int *scc_node;
+ int *scc_in_merge;
+};
+
+/* Initialize the clustering data structure "c" from "graph".
+ *
+ * In particular, allocate memory, extract the SCCs from "graph"
+ * into c->scc, initialize scc_cluster and construct
+ * a band of schedule rows for each SCC.
+ * Within each SCC, there is only one SCC by definition.
+ * Each SCC initially belongs to a cluster containing only that SCC.
+ */
+static isl_stat clustering_init(isl_ctx *ctx, struct isl_clustering *c,
+ struct isl_sched_graph *graph)
+{
+ int i;
+
+ c->n = graph->scc;
+ c->scc = isl_calloc_array(ctx, struct isl_sched_graph, c->n);
+ c->cluster = isl_calloc_array(ctx, struct isl_sched_graph, c->n);
+ c->scc_cluster = isl_calloc_array(ctx, int, c->n);
+ c->scc_node = isl_calloc_array(ctx, int, c->n);
+ c->scc_in_merge = isl_calloc_array(ctx, int, c->n);
+ if (!c->scc || !c->cluster ||
+ !c->scc_cluster || !c->scc_node || !c->scc_in_merge)
+ return isl_stat_error;
+
+ for (i = 0; i < c->n; ++i) {
+ if (extract_sub_graph(ctx, graph, &node_scc_exactly,
+ &edge_scc_exactly, i, &c->scc[i]) < 0)
+ return isl_stat_error;
+ c->scc[i].scc = 1;
+ if (compute_maxvar(&c->scc[i]) < 0)
+ return isl_stat_error;
+ if (compute_schedule_wcc_band(ctx, &c->scc[i]) < 0)
+ return isl_stat_error;
+ c->scc_cluster[i] = i;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Free all memory allocated for "c".
+ */
+static void clustering_free(isl_ctx *ctx, struct isl_clustering *c)
+{
+ int i;
+
+ if (c->scc)
+ for (i = 0; i < c->n; ++i)
+ graph_free(ctx, &c->scc[i]);
+ free(c->scc);
+ if (c->cluster)
+ for (i = 0; i < c->n; ++i)
+ graph_free(ctx, &c->cluster[i]);
+ free(c->cluster);
+ free(c->scc_cluster);
+ free(c->scc_node);
+ free(c->scc_in_merge);
+}
+
+/* Should we refrain from merging the cluster in "graph" with
+ * any other cluster?
+ * In particular, is its current schedule band empty and incomplete.
+ */
+static int bad_cluster(struct isl_sched_graph *graph)
+{
+ return graph->n_row < graph->maxvar &&
+ graph->n_total_row == graph->band_start;
+}
+
+/* Is "edge" a proximity edge with a non-empty dependence relation?
+ */
+static isl_bool is_non_empty_proximity(struct isl_sched_edge *edge)
+{
+ if (!is_proximity(edge))
+ return isl_bool_false;
+ return isl_bool_not(isl_map_plain_is_empty(edge->map));
+}
+
+/* Return the index of an edge in "graph" that can be used to merge
+ * two clusters in "c".
+ * Return graph->n_edge if no such edge can be found.
+ * Return -1 on error.
+ *
+ * In particular, return a proximity edge between two clusters
+ * that is not marked "no_merge" and such that neither of the
+ * two clusters has an incomplete, empty band.
+ *
+ * If there are multiple such edges, then try and find the most
+ * appropriate edge to use for merging. In particular, pick the edge
+ * with the greatest weight. If there are multiple of those,
+ * then pick one with the shortest distance between
+ * the two cluster representatives.
+ */
+static int find_proximity(struct isl_sched_graph *graph,
+ struct isl_clustering *c)
+{
+ int i, best = graph->n_edge, best_dist, best_weight;
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ struct isl_sched_edge *edge = &graph->edge[i];
+ int dist, weight;
+ isl_bool prox;
+
+ prox = is_non_empty_proximity(edge);
+ if (prox < 0)
+ return -1;
+ if (!prox)
+ continue;
+ if (edge->no_merge)
+ continue;
+ if (bad_cluster(&c->scc[edge->src->scc]) ||
+ bad_cluster(&c->scc[edge->dst->scc]))
+ continue;
+ dist = c->scc_cluster[edge->dst->scc] -
+ c->scc_cluster[edge->src->scc];
+ if (dist == 0)
+ continue;
+ weight = edge->weight;
+ if (best < graph->n_edge) {
+ if (best_weight > weight)
+ continue;
+ if (best_weight == weight && best_dist <= dist)
+ continue;
+ }
+ best = i;
+ best_dist = dist;
+ best_weight = weight;
+ }
+
+ return best;
+}
+
+/* Internal data structure used in mark_merge_sccs.
+ *
+ * "graph" is the dependence graph in which a strongly connected
+ * component is constructed.
+ * "scc_cluster" maps each SCC index to the cluster to which it belongs.
+ * "src" and "dst" are the indices of the nodes that are being merged.
+ */
+struct isl_mark_merge_sccs_data {
+ struct isl_sched_graph *graph;
+ int *scc_cluster;
+ int src;
+ int dst;
+};
+
+/* Check whether the cluster containing node "i" depends on the cluster
+ * containing node "j". If "i" and "j" belong to the same cluster,
+ * then they are taken to depend on each other to ensure that
+ * the resulting strongly connected component consists of complete
+ * clusters. Furthermore, if "i" and "j" are the two nodes that
+ * are being merged, then they are taken to depend on each other as well.
+ * Otherwise, check if there is a (conditional) validity dependence
+ * from node[j] to node[i], forcing node[i] to follow node[j].
+ */
+static isl_bool cluster_follows(int i, int j, void *user)
+{
+ struct isl_mark_merge_sccs_data *data = user;
+ struct isl_sched_graph *graph = data->graph;
+ int *scc_cluster = data->scc_cluster;
+
+ if (data->src == i && data->dst == j)
+ return isl_bool_true;
+ if (data->src == j && data->dst == i)
+ return isl_bool_true;
+ if (scc_cluster[graph->node[i].scc] == scc_cluster[graph->node[j].scc])
+ return isl_bool_true;
+
+ return graph_has_validity_edge(graph, &graph->node[j], &graph->node[i]);
+}
+
+/* Mark all SCCs that belong to either of the two clusters in "c"
+ * connected by the edge in "graph" with index "edge", or to any
+ * of the intermediate clusters.
+ * The marking is recorded in c->scc_in_merge.
+ *
+ * The given edge has been selected for merging two clusters,
+ * meaning that there is at least a proximity edge between the two nodes.
+ * However, there may also be (indirect) validity dependences
+ * between the two nodes. When merging the two clusters, all clusters
+ * containing one or more of the intermediate nodes along the
+ * indirect validity dependences need to be merged in as well.
+ *
+ * First collect all such nodes by computing the strongly connected
+ * component (SCC) containing the two nodes connected by the edge, where
+ * the two nodes are considered to depend on each other to make
+ * sure they end up in the same SCC. Similarly, each node is considered
+ * to depend on every other node in the same cluster to ensure
+ * that the SCC consists of complete clusters.
+ *
+ * Then the original SCCs that contain any of these nodes are marked
+ * in c->scc_in_merge.
+ */
+static isl_stat mark_merge_sccs(isl_ctx *ctx, struct isl_sched_graph *graph,
+ int edge, struct isl_clustering *c)
+{
+ struct isl_mark_merge_sccs_data data;
+ struct isl_tarjan_graph *g;
+ int i;
+
+ for (i = 0; i < c->n; ++i)
+ c->scc_in_merge[i] = 0;
+
+ data.graph = graph;
+ data.scc_cluster = c->scc_cluster;
+ data.src = graph->edge[edge].src - graph->node;
+ data.dst = graph->edge[edge].dst - graph->node;
+
+ g = isl_tarjan_graph_component(ctx, graph->n, data.dst,
+ &cluster_follows, &data);
+ if (!g)
+ goto error;
+
+ i = g->op;
+ if (i < 3)
+ isl_die(ctx, isl_error_internal,
+ "expecting at least two nodes in component",
+ goto error);
+ if (g->order[--i] != -1)
+ isl_die(ctx, isl_error_internal,
+ "expecting end of component marker", goto error);
+
+ for (--i; i >= 0 && g->order[i] != -1; --i) {
+ int scc = graph->node[g->order[i]].scc;
+ c->scc_in_merge[scc] = 1;
+ }
+
+ isl_tarjan_graph_free(g);
+ return isl_stat_ok;
+error:
+ isl_tarjan_graph_free(g);
+ return isl_stat_error;
+}
+
+/* Construct the identifier "cluster_i".
+ */
+static __isl_give isl_id *cluster_id(isl_ctx *ctx, int i)
+{
+ char name[40];
+
+ snprintf(name, sizeof(name), "cluster_%d", i);
+ return isl_id_alloc(ctx, name, NULL);
+}
+
+/* Construct the space of the cluster with index "i" containing
+ * the strongly connected component "scc".
+ *
+ * In particular, construct a space called cluster_i with dimension equal
+ * to the number of schedule rows in the current band of "scc".
+ */
+static __isl_give isl_space *cluster_space(struct isl_sched_graph *scc, int i)
+{
+ int nvar;
+ isl_space *space;
+ isl_id *id;
+
+ nvar = scc->n_total_row - scc->band_start;
+ space = isl_space_copy(scc->node[0].space);
+ space = isl_space_params(space);
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, nvar);
+ id = cluster_id(isl_space_get_ctx(space), i);
+ space = isl_space_set_tuple_id(space, isl_dim_set, id);
+
+ return space;
+}
+
+/* Collect the domain of the graph for merging clusters.
+ *
+ * In particular, for each cluster with first SCC "i", construct
+ * a set in the space called cluster_i with dimension equal
+ * to the number of schedule rows in the current band of the cluster.
+ */
+static __isl_give isl_union_set *collect_domain(isl_ctx *ctx,
+ struct isl_sched_graph *graph, struct isl_clustering *c)
+{
+ int i;
+ isl_space *space;
+ isl_union_set *domain;
+
+ space = isl_space_params_alloc(ctx, 0);
+ domain = isl_union_set_empty(space);
+
+ for (i = 0; i < graph->scc; ++i) {
+ isl_space *space;
+
+ if (!c->scc_in_merge[i])
+ continue;
+ if (c->scc_cluster[i] != i)
+ continue;
+ space = cluster_space(&c->scc[i], i);
+ domain = isl_union_set_add_set(domain, isl_set_universe(space));
+ }
+
+ return domain;
+}
+
+/* Construct a map from the original instances to the corresponding
+ * cluster instance in the current bands of the clusters in "c".
+ */
+static __isl_give isl_union_map *collect_cluster_map(isl_ctx *ctx,
+ struct isl_sched_graph *graph, struct isl_clustering *c)
+{
+ int i, j;
+ isl_space *space;
+ isl_union_map *cluster_map;
+
+ space = isl_space_params_alloc(ctx, 0);
+ cluster_map = isl_union_map_empty(space);
+ for (i = 0; i < graph->scc; ++i) {
+ int start, n;
+ isl_id *id;
+
+ if (!c->scc_in_merge[i])
+ continue;
+
+ id = cluster_id(ctx, c->scc_cluster[i]);
+ start = c->scc[i].band_start;
+ n = c->scc[i].n_total_row - start;
+ for (j = 0; j < c->scc[i].n; ++j) {
+ isl_multi_aff *ma;
+ isl_map *map;
+ struct isl_sched_node *node = &c->scc[i].node[j];
+
+ ma = node_extract_partial_schedule_multi_aff(node,
+ start, n);
+ ma = isl_multi_aff_set_tuple_id(ma, isl_dim_out,
+ isl_id_copy(id));
+ map = isl_map_from_multi_aff(ma);
+ cluster_map = isl_union_map_add_map(cluster_map, map);
+ }
+ isl_id_free(id);
+ }
+
+ return cluster_map;
+}
+
+/* Add "umap" to the schedule constraints "sc" of all types of "edge"
+ * that are not isl_edge_condition or isl_edge_conditional_validity.
+ */
+static __isl_give isl_schedule_constraints *add_non_conditional_constraints(
+ struct isl_sched_edge *edge, __isl_keep isl_union_map *umap,
+ __isl_take isl_schedule_constraints *sc)
+{
+ enum isl_edge_type t;
+
+ if (!sc)
+ return NULL;
+
+ for (t = isl_edge_first; t <= isl_edge_last; ++t) {
+ if (t == isl_edge_condition ||
+ t == isl_edge_conditional_validity)
+ continue;
+ if (!is_type(edge, t))
+ continue;
+ sc = isl_schedule_constraints_add(sc, t,
+ isl_union_map_copy(umap));
+ }
+
+ return sc;
+}
+
+/* Add schedule constraints of types isl_edge_condition and
+ * isl_edge_conditional_validity to "sc" by applying "umap" to
+ * the domains of the wrapped relations in domain and range
+ * of the corresponding tagged constraints of "edge".
+ */
+static __isl_give isl_schedule_constraints *add_conditional_constraints(
+ struct isl_sched_edge *edge, __isl_keep isl_union_map *umap,
+ __isl_take isl_schedule_constraints *sc)
+{
+ enum isl_edge_type t;
+ isl_union_map *tagged;
+
+ for (t = isl_edge_condition; t <= isl_edge_conditional_validity; ++t) {
+ if (!is_type(edge, t))
+ continue;
+ if (t == isl_edge_condition)
+ tagged = isl_union_map_copy(edge->tagged_condition);
+ else
+ tagged = isl_union_map_copy(edge->tagged_validity);
+ tagged = isl_union_map_zip(tagged);
+ tagged = isl_union_map_apply_domain(tagged,
+ isl_union_map_copy(umap));
+ tagged = isl_union_map_zip(tagged);
+ sc = isl_schedule_constraints_add(sc, t, tagged);
+ if (!sc)
+ return NULL;
+ }
+
+ return sc;
+}
+
+/* Given a mapping "cluster_map" from the original instances to
+ * the cluster instances, add schedule constraints on the clusters
+ * to "sc" corresponding to the original constraints represented by "edge".
+ *
+ * For non-tagged dependence constraints, the cluster constraints
+ * are obtained by applying "cluster_map" to the edge->map.
+ *
+ * For tagged dependence constraints, "cluster_map" needs to be applied
+ * to the domains of the wrapped relations in domain and range
+ * of the tagged dependence constraints. Pick out the mappings
+ * from these domains from "cluster_map" and construct their product.
+ * This mapping can then be applied to the pair of domains.
+ */
+static __isl_give isl_schedule_constraints *collect_edge_constraints(
+ struct isl_sched_edge *edge, __isl_keep isl_union_map *cluster_map,
+ __isl_take isl_schedule_constraints *sc)
+{
+ isl_union_map *umap;
+ isl_space *space;
+ isl_union_set *uset;
+ isl_union_map *umap1, *umap2;
+
+ if (!sc)
+ return NULL;
+
+ umap = isl_union_map_from_map(isl_map_copy(edge->map));
+ umap = isl_union_map_apply_domain(umap,
+ isl_union_map_copy(cluster_map));
+ umap = isl_union_map_apply_range(umap,
+ isl_union_map_copy(cluster_map));
+ sc = add_non_conditional_constraints(edge, umap, sc);
+ isl_union_map_free(umap);
+
+ if (!sc || (!is_condition(edge) && !is_conditional_validity(edge)))
+ return sc;
+
+ space = isl_space_domain(isl_map_get_space(edge->map));
+ uset = isl_union_set_from_set(isl_set_universe(space));
+ umap1 = isl_union_map_copy(cluster_map);
+ umap1 = isl_union_map_intersect_domain(umap1, uset);
+ space = isl_space_range(isl_map_get_space(edge->map));
+ uset = isl_union_set_from_set(isl_set_universe(space));
+ umap2 = isl_union_map_copy(cluster_map);
+ umap2 = isl_union_map_intersect_domain(umap2, uset);
+ umap = isl_union_map_product(umap1, umap2);
+
+ sc = add_conditional_constraints(edge, umap, sc);
+
+ isl_union_map_free(umap);
+ return sc;
+}
+
+/* Given a mapping "cluster_map" from the original instances to
+ * the cluster instances, add schedule constraints on the clusters
+ * to "sc" corresponding to all edges in "graph" between nodes that
+ * belong to SCCs that are marked for merging in "scc_in_merge".
+ */
+static __isl_give isl_schedule_constraints *collect_constraints(
+ struct isl_sched_graph *graph, int *scc_in_merge,
+ __isl_keep isl_union_map *cluster_map,
+ __isl_take isl_schedule_constraints *sc)
+{
+ int i;
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ struct isl_sched_edge *edge = &graph->edge[i];
+
+ if (!scc_in_merge[edge->src->scc])
+ continue;
+ if (!scc_in_merge[edge->dst->scc])
+ continue;
+ sc = collect_edge_constraints(edge, cluster_map, sc);
+ }
+
+ return sc;
+}
+
+/* Construct a dependence graph for scheduling clusters with respect
+ * to each other and store the result in "merge_graph".
+ * In particular, the nodes of the graph correspond to the schedule
+ * dimensions of the current bands of those clusters that have been
+ * marked for merging in "c".
+ *
+ * First construct an isl_schedule_constraints object for this domain
+ * by transforming the edges in "graph" to the domain.
+ * Then initialize a dependence graph for scheduling from these
+ * constraints.
+ */
+static isl_stat init_merge_graph(isl_ctx *ctx, struct isl_sched_graph *graph,
+ struct isl_clustering *c, struct isl_sched_graph *merge_graph)
+{
+ isl_union_set *domain;
+ isl_union_map *cluster_map;
+ isl_schedule_constraints *sc;
+ isl_stat r;
+
+ domain = collect_domain(ctx, graph, c);
+ sc = isl_schedule_constraints_on_domain(domain);
+ if (!sc)
+ return isl_stat_error;
+ cluster_map = collect_cluster_map(ctx, graph, c);
+ sc = collect_constraints(graph, c->scc_in_merge, cluster_map, sc);
+ isl_union_map_free(cluster_map);
+
+ r = graph_init(merge_graph, sc);
+
+ isl_schedule_constraints_free(sc);
+
+ return r;
+}
+
+/* Compute the maximal number of remaining schedule rows that still need
+ * to be computed for the nodes that belong to clusters with the maximal
+ * dimension for the current band (i.e., the band that is to be merged).
+ * Only clusters that are about to be merged are considered.
+ * "maxvar" is the maximal dimension for the current band.
+ * "c" contains information about the clusters.
+ *
+ * Return the maximal number of remaining schedule rows or -1 on error.
+ */
+static int compute_maxvar_max_slack(int maxvar, struct isl_clustering *c)
+{
+ int i, j;
+ int max_slack;
+
+ max_slack = 0;
+ for (i = 0; i < c->n; ++i) {
+ int nvar;
+ struct isl_sched_graph *scc;
+
+ if (!c->scc_in_merge[i])
+ continue;
+ scc = &c->scc[i];
+ nvar = scc->n_total_row - scc->band_start;
+ if (nvar != maxvar)
+ continue;
+ for (j = 0; j < scc->n; ++j) {
+ struct isl_sched_node *node = &scc->node[j];
+ int slack;
+
+ if (node_update_vmap(node) < 0)
+ return -1;
+ slack = node->nvar - node->rank;
+ if (slack > max_slack)
+ max_slack = slack;
+ }
+ }
+
+ return max_slack;
+}
+
+/* If there are any clusters where the dimension of the current band
+ * (i.e., the band that is to be merged) is smaller than "maxvar" and
+ * if there are any nodes in such a cluster where the number
+ * of remaining schedule rows that still need to be computed
+ * is greater than "max_slack", then return the smallest current band
+ * dimension of all these clusters. Otherwise return the original value
+ * of "maxvar". Return -1 in case of any error.
+ * Only clusters that are about to be merged are considered.
+ * "c" contains information about the clusters.
+ */
+static int limit_maxvar_to_slack(int maxvar, int max_slack,
+ struct isl_clustering *c)
+{
+ int i, j;
+
+ for (i = 0; i < c->n; ++i) {
+ int nvar;
+ struct isl_sched_graph *scc;
+
+ if (!c->scc_in_merge[i])
+ continue;
+ scc = &c->scc[i];
+ nvar = scc->n_total_row - scc->band_start;
+ if (nvar >= maxvar)
+ continue;
+ for (j = 0; j < scc->n; ++j) {
+ struct isl_sched_node *node = &scc->node[j];
+ int slack;
+
+ if (node_update_vmap(node) < 0)
+ return -1;
+ slack = node->nvar - node->rank;
+ if (slack > max_slack) {
+ maxvar = nvar;
+ break;
+ }
+ }
+ }
+
+ return maxvar;
+}
+
+/* Adjust merge_graph->maxvar based on the number of remaining schedule rows
+ * that still need to be computed. In particular, if there is a node
+ * in a cluster where the dimension of the current band is smaller
+ * than merge_graph->maxvar, but the number of remaining schedule rows
+ * is greater than that of any node in a cluster with the maximal
+ * dimension for the current band (i.e., merge_graph->maxvar),
+ * then adjust merge_graph->maxvar to the (smallest) current band dimension
+ * of those clusters. Without this adjustment, the total number of
+ * schedule dimensions would be increased, resulting in a skewed view
+ * of the number of coincident dimensions.
+ * "c" contains information about the clusters.
+ *
+ * If the maximize_band_depth option is set and merge_graph->maxvar is reduced,
+ * then there is no point in attempting any merge since it will be rejected
+ * anyway. Set merge_graph->maxvar to zero in such cases.
+ */
+static isl_stat adjust_maxvar_to_slack(isl_ctx *ctx,
+ struct isl_sched_graph *merge_graph, struct isl_clustering *c)
+{
+ int max_slack, maxvar;
+
+ max_slack = compute_maxvar_max_slack(merge_graph->maxvar, c);
+ if (max_slack < 0)
+ return isl_stat_error;
+ maxvar = limit_maxvar_to_slack(merge_graph->maxvar, max_slack, c);
+ if (maxvar < 0)
+ return isl_stat_error;
+
+ if (maxvar < merge_graph->maxvar) {
+ if (isl_options_get_schedule_maximize_band_depth(ctx))
+ merge_graph->maxvar = 0;
+ else
+ merge_graph->maxvar = maxvar;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Return the number of coincident dimensions in the current band of "graph",
+ * where the nodes of "graph" are assumed to be scheduled by a single band.
+ */
+static int get_n_coincident(struct isl_sched_graph *graph)
+{
+ int i;
+
+ for (i = graph->band_start; i < graph->n_total_row; ++i)
+ if (!graph->node[0].coincident[i])
+ break;
+
+ return i - graph->band_start;
+}
+
+/* Should the clusters be merged based on the cluster schedule
+ * in the current (and only) band of "merge_graph", given that
+ * coincidence should be maximized?
+ *
+ * If the number of coincident schedule dimensions in the merged band
+ * would be less than the maximal number of coincident schedule dimensions
+ * in any of the merged clusters, then the clusters should not be merged.
+ */
+static isl_bool ok_to_merge_coincident(struct isl_clustering *c,
+ struct isl_sched_graph *merge_graph)
+{
+ int i;
+ int n_coincident;
+ int max_coincident;
+
+ max_coincident = 0;
+ for (i = 0; i < c->n; ++i) {
+ if (!c->scc_in_merge[i])
+ continue;
+ n_coincident = get_n_coincident(&c->scc[i]);
+ if (n_coincident > max_coincident)
+ max_coincident = n_coincident;
+ }
+
+ n_coincident = get_n_coincident(merge_graph);
+
+ return isl_bool_ok(n_coincident >= max_coincident);
+}
+
+/* Return the transformation on "node" expressed by the current (and only)
+ * band of "merge_graph" applied to the clusters in "c".
+ *
+ * First find the representation of "node" in its SCC in "c" and
+ * extract the transformation expressed by the current band.
+ * Then extract the transformation applied by "merge_graph"
+ * to the cluster to which this SCC belongs.
+ * Combine the two to obtain the complete transformation on the node.
+ *
+ * Note that the range of the first transformation is an anonymous space,
+ * while the domain of the second is named "cluster_X". The range
+ * of the former therefore needs to be adjusted before the two
+ * can be combined.
+ */
+static __isl_give isl_map *extract_node_transformation(isl_ctx *ctx,
+ struct isl_sched_node *node, struct isl_clustering *c,
+ struct isl_sched_graph *merge_graph)
+{
+ struct isl_sched_node *scc_node, *cluster_node;
+ int start, n;
+ isl_id *id;
+ isl_space *space;
+ isl_multi_aff *ma, *ma2;
+
+ scc_node = graph_find_node(ctx, &c->scc[node->scc], node->space);
+ if (scc_node && !is_node(&c->scc[node->scc], scc_node))
+ isl_die(ctx, isl_error_internal, "unable to find node",
+ return NULL);
+ start = c->scc[node->scc].band_start;
+ n = c->scc[node->scc].n_total_row - start;
+ ma = node_extract_partial_schedule_multi_aff(scc_node, start, n);
+ space = cluster_space(&c->scc[node->scc], c->scc_cluster[node->scc]);
+ cluster_node = graph_find_node(ctx, merge_graph, space);
+ if (cluster_node && !is_node(merge_graph, cluster_node))
+ isl_die(ctx, isl_error_internal, "unable to find cluster",
+ space = isl_space_free(space));
+ id = isl_space_get_tuple_id(space, isl_dim_set);
+ ma = isl_multi_aff_set_tuple_id(ma, isl_dim_out, id);
+ isl_space_free(space);
+ n = merge_graph->n_total_row;
+ ma2 = node_extract_partial_schedule_multi_aff(cluster_node, 0, n);
+ ma = isl_multi_aff_pullback_multi_aff(ma2, ma);
+
+ return isl_map_from_multi_aff(ma);
+}
+
+/* Give a set of distances "set", are they bounded by a small constant
+ * in direction "pos"?
+ * In practice, check if they are bounded by 2 by checking that there
+ * are no elements with a value greater than or equal to 3 or
+ * smaller than or equal to -3.
+ */
+static isl_bool distance_is_bounded(__isl_keep isl_set *set, int pos)
+{
+ isl_bool bounded;
+ isl_set *test;
+
+ if (!set)
+ return isl_bool_error;
+
+ test = isl_set_copy(set);
+ test = isl_set_lower_bound_si(test, isl_dim_set, pos, 3);
+ bounded = isl_set_is_empty(test);
+ isl_set_free(test);
+
+ if (bounded < 0 || !bounded)
+ return bounded;
+
+ test = isl_set_copy(set);
+ test = isl_set_upper_bound_si(test, isl_dim_set, pos, -3);
+ bounded = isl_set_is_empty(test);
+ isl_set_free(test);
+
+ return bounded;
+}
+
+/* Does the set "set" have a fixed (but possible parametric) value
+ * at dimension "pos"?
+ */
+static isl_bool has_single_value(__isl_keep isl_set *set, int pos)
+{
+ isl_size n;
+ isl_bool single;
+
+ n = isl_set_dim(set, isl_dim_set);
+ if (n < 0)
+ return isl_bool_error;
+ set = isl_set_copy(set);
+ set = isl_set_project_out(set, isl_dim_set, pos + 1, n - (pos + 1));
+ set = isl_set_project_out(set, isl_dim_set, 0, pos);
+ single = isl_set_is_singleton(set);
+ isl_set_free(set);
+
+ return single;
+}
+
+/* Does "map" have a fixed (but possible parametric) value
+ * at dimension "pos" of either its domain or its range?
+ */
+static isl_bool has_singular_src_or_dst(__isl_keep isl_map *map, int pos)
+{
+ isl_set *set;
+ isl_bool single;
+
+ set = isl_map_domain(isl_map_copy(map));
+ single = has_single_value(set, pos);
+ isl_set_free(set);
+
+ if (single < 0 || single)
+ return single;
+
+ set = isl_map_range(isl_map_copy(map));
+ single = has_single_value(set, pos);
+ isl_set_free(set);
+
+ return single;
+}
+
+/* Does the edge "edge" from "graph" have bounded dependence distances
+ * in the merged graph "merge_graph" of a selection of clusters in "c"?
+ *
+ * Extract the complete transformations of the source and destination
+ * nodes of the edge, apply them to the edge constraints and
+ * compute the differences. Finally, check if these differences are bounded
+ * in each direction.
+ *
+ * If the dimension of the band is greater than the number of
+ * dimensions that can be expected to be optimized by the edge
+ * (based on its weight), then also allow the differences to be unbounded
+ * in the remaining dimensions, but only if either the source or
+ * the destination has a fixed value in that direction.
+ * This allows a statement that produces values that are used by
+ * several instances of another statement to be merged with that
+ * other statement.
+ * However, merging such clusters will introduce an inherently
+ * large proximity distance inside the merged cluster, meaning
+ * that proximity distances will no longer be optimized in
+ * subsequent merges. These merges are therefore only allowed
+ * after all other possible merges have been tried.
+ * The first time such a merge is encountered, the weight of the edge
+ * is replaced by a negative weight. The second time (i.e., after
+ * all merges over edges with a non-negative weight have been tried),
+ * the merge is allowed.
+ */
+static isl_bool has_bounded_distances(isl_ctx *ctx, struct isl_sched_edge *edge,
+ struct isl_sched_graph *graph, struct isl_clustering *c,
+ struct isl_sched_graph *merge_graph)
+{
+ int i, n_slack;
+ isl_size n;
+ isl_bool bounded;
+ isl_map *map, *t;
+ isl_set *dist;
+
+ map = isl_map_copy(edge->map);
+ t = extract_node_transformation(ctx, edge->src, c, merge_graph);
+ map = isl_map_apply_domain(map, t);
+ t = extract_node_transformation(ctx, edge->dst, c, merge_graph);
+ map = isl_map_apply_range(map, t);
+ dist = isl_map_deltas(isl_map_copy(map));
+
+ bounded = isl_bool_true;
+ n = isl_set_dim(dist, isl_dim_set);
+ if (n < 0)
+ goto error;
+ n_slack = n - edge->weight;
+ if (edge->weight < 0)
+ n_slack -= graph->max_weight + 1;
+ for (i = 0; i < n; ++i) {
+ isl_bool bounded_i, singular_i;
+
+ bounded_i = distance_is_bounded(dist, i);
+ if (bounded_i < 0)
+ goto error;
+ if (bounded_i)
+ continue;
+ if (edge->weight >= 0)
+ bounded = isl_bool_false;
+ n_slack--;
+ if (n_slack < 0)
+ break;
+ singular_i = has_singular_src_or_dst(map, i);
+ if (singular_i < 0)
+ goto error;
+ if (singular_i)
+ continue;
+ bounded = isl_bool_false;
+ break;
+ }
+ if (!bounded && i >= n && edge->weight >= 0)
+ edge->weight -= graph->max_weight + 1;
+ isl_map_free(map);
+ isl_set_free(dist);
+
+ return bounded;
+error:
+ isl_map_free(map);
+ isl_set_free(dist);
+ return isl_bool_error;
+}
+
+/* Should the clusters be merged based on the cluster schedule
+ * in the current (and only) band of "merge_graph"?
+ * "graph" is the original dependence graph, while "c" records
+ * which SCCs are involved in the latest merge.
+ *
+ * In particular, is there at least one proximity constraint
+ * that is optimized by the merge?
+ *
+ * A proximity constraint is considered to be optimized
+ * if the dependence distances are small.
+ */
+static isl_bool ok_to_merge_proximity(isl_ctx *ctx,
+ struct isl_sched_graph *graph, struct isl_clustering *c,
+ struct isl_sched_graph *merge_graph)
+{
+ int i;
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ struct isl_sched_edge *edge = &graph->edge[i];
+ isl_bool bounded;
+
+ if (!is_proximity(edge))
+ continue;
+ if (!c->scc_in_merge[edge->src->scc])
+ continue;
+ if (!c->scc_in_merge[edge->dst->scc])
+ continue;
+ if (c->scc_cluster[edge->dst->scc] ==
+ c->scc_cluster[edge->src->scc])
+ continue;
+ bounded = has_bounded_distances(ctx, edge, graph, c,
+ merge_graph);
+ if (bounded < 0 || bounded)
+ return bounded;
+ }
+
+ return isl_bool_false;
+}
+
+/* Should the clusters be merged based on the cluster schedule
+ * in the current (and only) band of "merge_graph"?
+ * "graph" is the original dependence graph, while "c" records
+ * which SCCs are involved in the latest merge.
+ *
+ * If the current band is empty, then the clusters should not be merged.
+ *
+ * If the band depth should be maximized and the merge schedule
+ * is incomplete (meaning that the dimension of some of the schedule
+ * bands in the original schedule will be reduced), then the clusters
+ * should not be merged.
+ *
+ * If the schedule_maximize_coincidence option is set, then check that
+ * the number of coincident schedule dimensions is not reduced.
+ *
+ * Finally, only allow the merge if at least one proximity
+ * constraint is optimized.
+ */
+static isl_bool ok_to_merge(isl_ctx *ctx, struct isl_sched_graph *graph,
+ struct isl_clustering *c, struct isl_sched_graph *merge_graph)
+{
+ if (merge_graph->n_total_row == merge_graph->band_start)
+ return isl_bool_false;
+
+ if (isl_options_get_schedule_maximize_band_depth(ctx) &&
+ merge_graph->n_total_row < merge_graph->maxvar)
+ return isl_bool_false;
+
+ if (isl_options_get_schedule_maximize_coincidence(ctx)) {
+ isl_bool ok;
+
+ ok = ok_to_merge_coincident(c, merge_graph);
+ if (ok < 0 || !ok)
+ return ok;
+ }
+
+ return ok_to_merge_proximity(ctx, graph, c, merge_graph);
+}
+
+/* Apply the schedule in "t_node" to the "n" rows starting at "first"
+ * of the schedule in "node" and return the result.
+ *
+ * That is, essentially compute
+ *
+ * T * N(first:first+n-1)
+ *
+ * taking into account the constant term and the parameter coefficients
+ * in "t_node".
+ */
+static __isl_give isl_mat *node_transformation(isl_ctx *ctx,
+ struct isl_sched_node *t_node, struct isl_sched_node *node,
+ int first, int n)
+{
+ int i, j;
+ isl_mat *t;
+ isl_size n_row, n_col;
+ int n_param, n_var;
+
+ n_param = node->nparam;
+ n_var = node->nvar;
+ n_row = isl_mat_rows(t_node->sched);
+ n_col = isl_mat_cols(node->sched);
+ if (n_row < 0 || n_col < 0)
+ return NULL;
+ t = isl_mat_alloc(ctx, n_row, n_col);
+ if (!t)
+ return NULL;
+ for (i = 0; i < n_row; ++i) {
+ isl_seq_cpy(t->row[i], t_node->sched->row[i], 1 + n_param);
+ isl_seq_clr(t->row[i] + 1 + n_param, n_var);
+ for (j = 0; j < n; ++j)
+ isl_seq_addmul(t->row[i],
+ t_node->sched->row[i][1 + n_param + j],
+ node->sched->row[first + j],
+ 1 + n_param + n_var);
+ }
+ return t;
+}
+
+/* Apply the cluster schedule in "t_node" to the current band
+ * schedule of the nodes in "graph".
+ *
+ * In particular, replace the rows starting at band_start
+ * by the result of applying the cluster schedule in "t_node"
+ * to the original rows.
+ *
+ * The coincidence of the schedule is determined by the coincidence
+ * of the cluster schedule.
+ */
+static isl_stat transform(isl_ctx *ctx, struct isl_sched_graph *graph,
+ struct isl_sched_node *t_node)
+{
+ int i, j;
+ isl_size n_new;
+ int start, n;
+
+ start = graph->band_start;
+ n = graph->n_total_row - start;
+
+ n_new = isl_mat_rows(t_node->sched);
+ if (n_new < 0)
+ return isl_stat_error;
+ for (i = 0; i < graph->n; ++i) {
+ struct isl_sched_node *node = &graph->node[i];
+ isl_mat *t;
+
+ t = node_transformation(ctx, t_node, node, start, n);
+ node->sched = isl_mat_drop_rows(node->sched, start, n);
+ node->sched = isl_mat_concat(node->sched, t);
+ node->sched_map = isl_map_free(node->sched_map);
+ if (!node->sched)
+ return isl_stat_error;
+ for (j = 0; j < n_new; ++j)
+ node->coincident[start + j] = t_node->coincident[j];
+ }
+ graph->n_total_row -= n;
+ graph->n_row -= n;
+ graph->n_total_row += n_new;
+ graph->n_row += n_new;
+
+ return isl_stat_ok;
+}
+
+/* Merge the clusters marked for merging in "c" into a single
+ * cluster using the cluster schedule in the current band of "merge_graph".
+ * The representative SCC for the new cluster is the SCC with
+ * the smallest index.
+ *
+ * The current band schedule of each SCC in the new cluster is obtained
+ * by applying the schedule of the corresponding original cluster
+ * to the original band schedule.
+ * All SCCs in the new cluster have the same number of schedule rows.
+ */
+static isl_stat merge(isl_ctx *ctx, struct isl_clustering *c,
+ struct isl_sched_graph *merge_graph)
+{
+ int i;
+ int cluster = -1;
+ isl_space *space;
+
+ for (i = 0; i < c->n; ++i) {
+ struct isl_sched_node *node;
+
+ if (!c->scc_in_merge[i])
+ continue;
+ if (cluster < 0)
+ cluster = i;
+ space = cluster_space(&c->scc[i], c->scc_cluster[i]);
+ node = graph_find_node(ctx, merge_graph, space);
+ isl_space_free(space);
+ if (!node)
+ return isl_stat_error;
+ if (!is_node(merge_graph, node))
+ isl_die(ctx, isl_error_internal,
+ "unable to find cluster",
+ return isl_stat_error);
+ if (transform(ctx, &c->scc[i], node) < 0)
+ return isl_stat_error;
+ c->scc_cluster[i] = cluster;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Try and merge the clusters of SCCs marked in c->scc_in_merge
+ * by scheduling the current cluster bands with respect to each other.
+ *
+ * Construct a dependence graph with a space for each cluster and
+ * with the coordinates of each space corresponding to the schedule
+ * dimensions of the current band of that cluster.
+ * Construct a cluster schedule in this cluster dependence graph and
+ * apply it to the current cluster bands if it is applicable
+ * according to ok_to_merge.
+ *
+ * If the number of remaining schedule dimensions in a cluster
+ * with a non-maximal current schedule dimension is greater than
+ * the number of remaining schedule dimensions in clusters
+ * with a maximal current schedule dimension, then restrict
+ * the number of rows to be computed in the cluster schedule
+ * to the minimal such non-maximal current schedule dimension.
+ * Do this by adjusting merge_graph.maxvar.
+ *
+ * Return isl_bool_true if the clusters have effectively been merged
+ * into a single cluster.
+ *
+ * Note that since the standard scheduling algorithm minimizes the maximal
+ * distance over proximity constraints, the proximity constraints between
+ * the merged clusters may not be optimized any further than what is
+ * sufficient to bring the distances within the limits of the internal
+ * proximity constraints inside the individual clusters.
+ * It may therefore make sense to perform an additional translation step
+ * to bring the clusters closer to each other, while maintaining
+ * the linear part of the merging schedule found using the standard
+ * scheduling algorithm.
+ */
+static isl_bool try_merge(isl_ctx *ctx, struct isl_sched_graph *graph,
+ struct isl_clustering *c)
+{
+ struct isl_sched_graph merge_graph = { 0 };
+ isl_bool merged;
+
+ if (init_merge_graph(ctx, graph, c, &merge_graph) < 0)
+ goto error;
+
+ if (compute_maxvar(&merge_graph) < 0)
+ goto error;
+ if (adjust_maxvar_to_slack(ctx, &merge_graph,c) < 0)
+ goto error;
+ if (compute_schedule_wcc_band(ctx, &merge_graph) < 0)
+ goto error;
+ merged = ok_to_merge(ctx, graph, c, &merge_graph);
+ if (merged && merge(ctx, c, &merge_graph) < 0)
+ goto error;
+
+ graph_free(ctx, &merge_graph);
+ return merged;
+error:
+ graph_free(ctx, &merge_graph);
+ return isl_bool_error;
+}
+
+/* Is there any edge marked "no_merge" between two SCCs that are
+ * about to be merged (i.e., that are set in "scc_in_merge")?
+ * "merge_edge" is the proximity edge along which the clusters of SCCs
+ * are going to be merged.
+ *
+ * If there is any edge between two SCCs with a negative weight,
+ * while the weight of "merge_edge" is non-negative, then this
+ * means that the edge was postponed. "merge_edge" should then
+ * also be postponed since merging along the edge with negative weight should
+ * be postponed until all edges with non-negative weight have been tried.
+ * Replace the weight of "merge_edge" by a negative weight as well and
+ * tell the caller not to attempt a merge.
+ */
+static int any_no_merge(struct isl_sched_graph *graph, int *scc_in_merge,
+ struct isl_sched_edge *merge_edge)
+{
+ int i;
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ struct isl_sched_edge *edge = &graph->edge[i];
+
+ if (!scc_in_merge[edge->src->scc])
+ continue;
+ if (!scc_in_merge[edge->dst->scc])
+ continue;
+ if (edge->no_merge)
+ return 1;
+ if (merge_edge->weight >= 0 && edge->weight < 0) {
+ merge_edge->weight -= graph->max_weight + 1;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Merge the two clusters in "c" connected by the edge in "graph"
+ * with index "edge" into a single cluster.
+ * If it turns out to be impossible to merge these two clusters,
+ * then mark the edge as "no_merge" such that it will not be
+ * considered again.
+ *
+ * First mark all SCCs that need to be merged. This includes the SCCs
+ * in the two clusters, but it may also include the SCCs
+ * of intermediate clusters.
+ * If there is already a no_merge edge between any pair of such SCCs,
+ * then simply mark the current edge as no_merge as well.
+ * Likewise, if any of those edges was postponed by has_bounded_distances,
+ * then postpone the current edge as well.
+ * Otherwise, try and merge the clusters and mark "edge" as "no_merge"
+ * if the clusters did not end up getting merged, unless the non-merge
+ * is due to the fact that the edge was postponed. This postponement
+ * can be recognized by a change in weight (from non-negative to negative).
+ */
+static isl_stat merge_clusters_along_edge(isl_ctx *ctx,
+ struct isl_sched_graph *graph, int edge, struct isl_clustering *c)
+{
+ isl_bool merged;
+ int edge_weight = graph->edge[edge].weight;
+
+ if (mark_merge_sccs(ctx, graph, edge, c) < 0)
+ return isl_stat_error;
+
+ if (any_no_merge(graph, c->scc_in_merge, &graph->edge[edge]))
+ merged = isl_bool_false;
+ else
+ merged = try_merge(ctx, graph, c);
+ if (merged < 0)
+ return isl_stat_error;
+ if (!merged && edge_weight == graph->edge[edge].weight)
+ graph->edge[edge].no_merge = 1;
+
+ return isl_stat_ok;
+}
+
+/* Does "node" belong to the cluster identified by "cluster"?
+ */
+static int node_cluster_exactly(struct isl_sched_node *node, int cluster)
+{
+ return node->cluster == cluster;
+}
+
+/* Does "edge" connect two nodes belonging to the cluster
+ * identified by "cluster"?
+ */
+static int edge_cluster_exactly(struct isl_sched_edge *edge, int cluster)
+{
+ return edge->src->cluster == cluster && edge->dst->cluster == cluster;
+}
+
+/* Swap the schedule of "node1" and "node2".
+ * Both nodes have been derived from the same node in a common parent graph.
+ * Since the "coincident" field is shared with that node
+ * in the parent graph, there is no need to also swap this field.
+ */
+static void swap_sched(struct isl_sched_node *node1,
+ struct isl_sched_node *node2)
+{
+ isl_mat *sched;
+ isl_map *sched_map;
+
+ sched = node1->sched;
+ node1->sched = node2->sched;
+ node2->sched = sched;
+
+ sched_map = node1->sched_map;
+ node1->sched_map = node2->sched_map;
+ node2->sched_map = sched_map;
+}
+
+/* Copy the current band schedule from the SCCs that form the cluster
+ * with index "pos" to the actual cluster at position "pos".
+ * By construction, the index of the first SCC that belongs to the cluster
+ * is also "pos".
+ *
+ * The order of the nodes inside both the SCCs and the cluster
+ * is assumed to be same as the order in the original "graph".
+ *
+ * Since the SCC graphs will no longer be used after this function,
+ * the schedules are actually swapped rather than copied.
+ */
+static isl_stat copy_partial(struct isl_sched_graph *graph,
+ struct isl_clustering *c, int pos)
+{
+ int i, j;
+
+ c->cluster[pos].n_total_row = c->scc[pos].n_total_row;
+ c->cluster[pos].n_row = c->scc[pos].n_row;
+ c->cluster[pos].maxvar = c->scc[pos].maxvar;
+ j = 0;
+ for (i = 0; i < graph->n; ++i) {
+ int k;
+ int s;
+
+ if (graph->node[i].cluster != pos)
+ continue;
+ s = graph->node[i].scc;
+ k = c->scc_node[s]++;
+ swap_sched(&c->cluster[pos].node[j], &c->scc[s].node[k]);
+ if (c->scc[s].maxvar > c->cluster[pos].maxvar)
+ c->cluster[pos].maxvar = c->scc[s].maxvar;
+ ++j;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Is there a (conditional) validity dependence from node[j] to node[i],
+ * forcing node[i] to follow node[j] or do the nodes belong to the same
+ * cluster?
+ */
+static isl_bool node_follows_strong_or_same_cluster(int i, int j, void *user)
+{
+ struct isl_sched_graph *graph = user;
+
+ if (graph->node[i].cluster == graph->node[j].cluster)
+ return isl_bool_true;
+ return graph_has_validity_edge(graph, &graph->node[j], &graph->node[i]);
+}
+
+/* Extract the merged clusters of SCCs in "graph", sort them, and
+ * store them in c->clusters. Update c->scc_cluster accordingly.
+ *
+ * First keep track of the cluster containing the SCC to which a node
+ * belongs in the node itself.
+ * Then extract the clusters into c->clusters, copying the current
+ * band schedule from the SCCs that belong to the cluster.
+ * Do this only once per cluster.
+ *
+ * Finally, topologically sort the clusters and update c->scc_cluster
+ * to match the new scc numbering. While the SCCs were originally
+ * sorted already, some SCCs that depend on some other SCCs may
+ * have been merged with SCCs that appear before these other SCCs.
+ * A reordering may therefore be required.
+ */
+static isl_stat extract_clusters(isl_ctx *ctx, struct isl_sched_graph *graph,
+ struct isl_clustering *c)
+{
+ int i;
+
+ for (i = 0; i < graph->n; ++i)
+ graph->node[i].cluster = c->scc_cluster[graph->node[i].scc];
+
+ for (i = 0; i < graph->scc; ++i) {
+ if (c->scc_cluster[i] != i)
+ continue;
+ if (extract_sub_graph(ctx, graph, &node_cluster_exactly,
+ &edge_cluster_exactly, i, &c->cluster[i]) < 0)
+ return isl_stat_error;
+ c->cluster[i].src_scc = -1;
+ c->cluster[i].dst_scc = -1;
+ if (copy_partial(graph, c, i) < 0)
+ return isl_stat_error;
+ }
+
+ if (detect_ccs(ctx, graph, &node_follows_strong_or_same_cluster) < 0)
+ return isl_stat_error;
+ for (i = 0; i < graph->n; ++i)
+ c->scc_cluster[graph->node[i].scc] = graph->node[i].cluster;
+
+ return isl_stat_ok;
+}
+
+/* Compute weights on the proximity edges of "graph" that can
+ * be used by find_proximity to find the most appropriate
+ * proximity edge to use to merge two clusters in "c".
+ * The weights are also used by has_bounded_distances to determine
+ * whether the merge should be allowed.
+ * Store the maximum of the computed weights in graph->max_weight.
+ *
+ * The computed weight is a measure for the number of remaining schedule
+ * dimensions that can still be completely aligned.
+ * In particular, compute the number of equalities between
+ * input dimensions and output dimensions in the proximity constraints.
+ * The directions that are already handled by outer schedule bands
+ * are projected out prior to determining this number.
+ *
+ * Edges that will never be considered by find_proximity are ignored.
+ */
+static isl_stat compute_weights(struct isl_sched_graph *graph,
+ struct isl_clustering *c)
+{
+ int i;
+
+ graph->max_weight = 0;
+
+ for (i = 0; i < graph->n_edge; ++i) {
+ struct isl_sched_edge *edge = &graph->edge[i];
+ struct isl_sched_node *src = edge->src;
+ struct isl_sched_node *dst = edge->dst;
+ isl_basic_map *hull;
+ isl_bool prox;
+ isl_size n_in, n_out, n;
+
+ prox = is_non_empty_proximity(edge);
+ if (prox < 0)
+ return isl_stat_error;
+ if (!prox)
+ continue;
+ if (bad_cluster(&c->scc[edge->src->scc]) ||
+ bad_cluster(&c->scc[edge->dst->scc]))
+ continue;
+ if (c->scc_cluster[edge->dst->scc] ==
+ c->scc_cluster[edge->src->scc])
+ continue;
+
+ hull = isl_map_affine_hull(isl_map_copy(edge->map));
+ hull = isl_basic_map_transform_dims(hull, isl_dim_in, 0,
+ isl_mat_copy(src->vmap));
+ hull = isl_basic_map_transform_dims(hull, isl_dim_out, 0,
+ isl_mat_copy(dst->vmap));
+ hull = isl_basic_map_project_out(hull,
+ isl_dim_in, 0, src->rank);
+ hull = isl_basic_map_project_out(hull,
+ isl_dim_out, 0, dst->rank);
+ hull = isl_basic_map_remove_divs(hull);
+ n_in = isl_basic_map_dim(hull, isl_dim_in);
+ n_out = isl_basic_map_dim(hull, isl_dim_out);
+ if (n_in < 0 || n_out < 0)
+ hull = isl_basic_map_free(hull);
+ hull = isl_basic_map_drop_constraints_not_involving_dims(hull,
+ isl_dim_in, 0, n_in);
+ hull = isl_basic_map_drop_constraints_not_involving_dims(hull,
+ isl_dim_out, 0, n_out);
+ n = isl_basic_map_n_equality(hull);
+ isl_basic_map_free(hull);
+ if (n < 0)
+ return isl_stat_error;
+ edge->weight = n;
+
+ if (edge->weight > graph->max_weight)
+ graph->max_weight = edge->weight;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Call compute_schedule_finish_band on each of the clusters in "c"
+ * in their topological order. This order is determined by the scc
+ * fields of the nodes in "graph".
+ * Combine the results in a sequence expressing the topological order.
+ *
+ * If there is only one cluster left, then there is no need to introduce
+ * a sequence node. Also, in this case, the cluster necessarily contains
+ * the SCC at position 0 in the original graph and is therefore also
+ * stored in the first cluster of "c".
+ */
+static __isl_give isl_schedule_node *finish_bands_clustering(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph,
+ struct isl_clustering *c)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_union_set_list *filters;
+
+ if (graph->scc == 1)
+ return compute_schedule_finish_band(node, &c->cluster[0], 0);
+
+ ctx = isl_schedule_node_get_ctx(node);
+
+ filters = extract_sccs(ctx, graph);
+ node = isl_schedule_node_insert_sequence(node, filters);
+
+ for (i = 0; i < graph->scc; ++i) {
+ int j = c->scc_cluster[i];
+ node = isl_schedule_node_child(node, i);
+ node = isl_schedule_node_child(node, 0);
+ node = compute_schedule_finish_band(node, &c->cluster[j], 0);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_parent(node);
+ }
+
+ return node;
+}
+
+/* Compute a schedule for a connected dependence graph by first considering
+ * each strongly connected component (SCC) in the graph separately and then
+ * incrementally combining them into clusters.
+ * Return the updated schedule node.
+ *
+ * Initially, each cluster consists of a single SCC, each with its
+ * own band schedule. The algorithm then tries to merge pairs
+ * of clusters along a proximity edge until no more suitable
+ * proximity edges can be found. During this merging, the schedule
+ * is maintained in the individual SCCs.
+ * After the merging is completed, the full resulting clusters
+ * are extracted and in finish_bands_clustering,
+ * compute_schedule_finish_band is called on each of them to integrate
+ * the band into "node" and to continue the computation.
+ *
+ * compute_weights initializes the weights that are used by find_proximity.
+ */
+static __isl_give isl_schedule_node *compute_schedule_wcc_clustering(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph)
+{
+ isl_ctx *ctx;
+ struct isl_clustering c;
+ int i;
+
+ ctx = isl_schedule_node_get_ctx(node);
+
+ if (clustering_init(ctx, &c, graph) < 0)
+ goto error;
+
+ if (compute_weights(graph, &c) < 0)
+ goto error;
+
+ for (;;) {
+ i = find_proximity(graph, &c);
+ if (i < 0)
+ goto error;
+ if (i >= graph->n_edge)
+ break;
+ if (merge_clusters_along_edge(ctx, graph, i, &c) < 0)
+ goto error;
+ }
+
+ if (extract_clusters(ctx, graph, &c) < 0)
+ goto error;
+
+ node = finish_bands_clustering(node, graph, &c);
+
+ clustering_free(ctx, &c);
+ return node;
+error:
+ clustering_free(ctx, &c);
+ return isl_schedule_node_free(node);
+}
+
+/* Compute a schedule for a connected dependence graph and return
+ * the updated schedule node.
+ *
+ * If Feautrier's algorithm is selected, we first recursively try to satisfy
+ * as many validity dependences as possible. When all validity dependences
+ * are satisfied we extend the schedule to a full-dimensional schedule.
+ *
+ * Call compute_schedule_wcc_whole or compute_schedule_wcc_clustering
+ * depending on whether the user has selected the option to try and
+ * compute a schedule for the entire (weakly connected) component first.
+ * If there is only a single strongly connected component (SCC), then
+ * there is no point in trying to combine SCCs
+ * in compute_schedule_wcc_clustering, so compute_schedule_wcc_whole
+ * is called instead.
+ */
+static __isl_give isl_schedule_node *compute_schedule_wcc(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph)
+{
+ isl_ctx *ctx;
+
+ if (!node)
+ return NULL;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ if (detect_sccs(ctx, graph) < 0)
+ return isl_schedule_node_free(node);
+
+ if (compute_maxvar(graph) < 0)
+ return isl_schedule_node_free(node);
+
+ if (need_feautrier_step(ctx, graph))
+ return compute_schedule_wcc_feautrier(node, graph);
+
+ if (graph->scc <= 1 || isl_options_get_schedule_whole_component(ctx))
+ return compute_schedule_wcc_whole(node, graph);
+ else
+ return compute_schedule_wcc_clustering(node, graph);
+}
+
+/* Compute a schedule for each group of nodes identified by node->scc
+ * separately and then combine them in a sequence node (or as set node
+ * if graph->weak is set) inserted at position "node" of the schedule tree.
+ * Return the updated schedule node.
+ *
+ * If "wcc" is set then each of the groups belongs to a single
+ * weakly connected component in the dependence graph so that
+ * there is no need for compute_sub_schedule to look for weakly
+ * connected components.
+ *
+ * If a set node would be introduced and if the number of components
+ * is equal to the number of nodes, then check if the schedule
+ * is already complete. If so, a redundant set node would be introduced
+ * (without any further descendants) stating that the statements
+ * can be executed in arbitrary order, which is also expressed
+ * by the absence of any node. Refrain from inserting any nodes
+ * in this case and simply return.
+ */
+static __isl_give isl_schedule_node *compute_component_schedule(
+ __isl_take isl_schedule_node *node, struct isl_sched_graph *graph,
+ int wcc)
+{
+ int component;
+ isl_ctx *ctx;
+ isl_union_set_list *filters;
+
+ if (!node)
+ return NULL;
+
+ if (graph->weak && graph->scc == graph->n) {
+ if (compute_maxvar(graph) < 0)
+ return isl_schedule_node_free(node);
+ if (graph->n_row >= graph->maxvar)
+ return node;
+ }
+
+ ctx = isl_schedule_node_get_ctx(node);
+ filters = extract_sccs(ctx, graph);
+ if (graph->weak)
+ node = isl_schedule_node_insert_set(node, filters);
+ else
+ node = isl_schedule_node_insert_sequence(node, filters);
+
+ for (component = 0; component < graph->scc; ++component) {
+ node = isl_schedule_node_child(node, component);
+ node = isl_schedule_node_child(node, 0);
+ node = compute_sub_schedule(node, ctx, graph,
+ &node_scc_exactly,
+ &edge_scc_exactly, component, wcc);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_parent(node);
+ }
+
+ return node;
+}
+
+/* Compute a schedule for the given dependence graph and insert it at "node".
+ * Return the updated schedule node.
+ *
+ * We first check if the graph is connected (through validity and conditional
+ * validity dependences) and, if not, compute a schedule
+ * for each component separately.
+ * If the schedule_serialize_sccs option is set, then we check for strongly
+ * connected components instead and compute a separate schedule for
+ * each such strongly connected component.
+ */
+static __isl_give isl_schedule_node *compute_schedule(isl_schedule_node *node,
+ struct isl_sched_graph *graph)
+{
+ isl_ctx *ctx;
+
+ if (!node)
+ return NULL;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ if (isl_options_get_schedule_serialize_sccs(ctx)) {
+ if (detect_sccs(ctx, graph) < 0)
+ return isl_schedule_node_free(node);
+ } else {
+ if (detect_wccs(ctx, graph) < 0)
+ return isl_schedule_node_free(node);
+ }
+
+ if (graph->scc > 1)
+ return compute_component_schedule(node, graph, 1);
+
+ return compute_schedule_wcc(node, graph);
+}
+
+/* Compute a schedule on sc->domain that respects the given schedule
+ * constraints.
+ *
+ * In particular, the schedule respects all the validity dependences.
+ * If the default isl scheduling algorithm is used, it tries to minimize
+ * the dependence distances over the proximity dependences.
+ * If Feautrier's scheduling algorithm is used, the proximity dependence
+ * distances are only minimized during the extension to a full-dimensional
+ * schedule.
+ *
+ * If there are any condition and conditional validity dependences,
+ * then the conditional validity dependences may be violated inside
+ * a tilable band, provided they have no adjacent non-local
+ * condition dependences.
+ */
+__isl_give isl_schedule *isl_schedule_constraints_compute_schedule(
+ __isl_take isl_schedule_constraints *sc)
+{
+ isl_ctx *ctx = isl_schedule_constraints_get_ctx(sc);
+ struct isl_sched_graph graph = { 0 };
+ isl_schedule *sched;
+ isl_schedule_node *node;
+ isl_union_set *domain;
+ isl_size n;
+
+ sc = isl_schedule_constraints_align_params(sc);
+
+ domain = isl_schedule_constraints_get_domain(sc);
+ n = isl_union_set_n_set(domain);
+ if (n == 0) {
+ isl_schedule_constraints_free(sc);
+ return isl_schedule_from_domain(domain);
+ }
+
+ if (n < 0 || graph_init(&graph, sc) < 0)
+ domain = isl_union_set_free(domain);
+
+ node = isl_schedule_node_from_domain(domain);
+ node = isl_schedule_node_child(node, 0);
+ if (graph.n > 0)
+ node = compute_schedule(node, &graph);
+ sched = isl_schedule_node_get_schedule(node);
+ isl_schedule_node_free(node);
+
+ graph_free(ctx, &graph);
+ isl_schedule_constraints_free(sc);
+
+ return sched;
+}
+
+/* Compute a schedule for the given union of domains that respects
+ * all the validity dependences and minimizes
+ * the dependence distances over the proximity dependences.
+ *
+ * This function is kept for backward compatibility.
+ */
+__isl_give isl_schedule *isl_union_set_compute_schedule(
+ __isl_take isl_union_set *domain,
+ __isl_take isl_union_map *validity,
+ __isl_take isl_union_map *proximity)
+{
+ isl_schedule_constraints *sc;
+
+ sc = isl_schedule_constraints_on_domain(domain);
+ sc = isl_schedule_constraints_set_validity(sc, validity);
+ sc = isl_schedule_constraints_set_proximity(sc, proximity);
+
+ return isl_schedule_constraints_compute_schedule(sc);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_seq.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_seq.c
new file mode 100644
index 00000000000..fb2100edc9c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_seq.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2011 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_seq.h>
+
+void isl_seq_clr(isl_int *p, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_set_si(p[i], 0);
+}
+
+void isl_seq_set_si(isl_int *p, int v, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_set_si(p[i], v);
+}
+
+void isl_seq_set(isl_int *p, isl_int v, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_set(p[i], v);
+}
+
+void isl_seq_neg(isl_int *dst, isl_int *src, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_neg(dst[i], src[i]);
+}
+
+void isl_seq_cpy(isl_int *dst, isl_int *src, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_set(dst[i], src[i]);
+}
+
+void isl_seq_submul(isl_int *dst, isl_int f, isl_int *src, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_submul(dst[i], f, src[i]);
+}
+
+void isl_seq_addmul(isl_int *dst, isl_int f, isl_int *src, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_addmul(dst[i], f, src[i]);
+}
+
+void isl_seq_swp_or_cpy(isl_int *dst, isl_int *src, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_swap_or_set(dst[i], src[i]);
+}
+
+void isl_seq_scale(isl_int *dst, isl_int *src, isl_int m, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_mul(dst[i], src[i], m);
+}
+
+void isl_seq_scale_down(isl_int *dst, isl_int *src, isl_int m, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_divexact(dst[i], src[i], m);
+}
+
+void isl_seq_cdiv_q(isl_int *dst, isl_int *src, isl_int m, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_cdiv_q(dst[i], src[i], m);
+}
+
+void isl_seq_fdiv_q(isl_int *dst, isl_int *src, isl_int m, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_fdiv_q(dst[i], src[i], m);
+}
+
+void isl_seq_fdiv_r(isl_int *dst, isl_int *src, isl_int m, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ isl_int_fdiv_r(dst[i], src[i], m);
+}
+
+void isl_seq_combine(isl_int *dst, isl_int m1, isl_int *src1,
+ isl_int m2, isl_int *src2, unsigned len)
+{
+ int i;
+ isl_int tmp;
+
+ if (dst == src1 && isl_int_is_one(m1)) {
+ if (isl_int_is_zero(m2))
+ return;
+ for (i = 0; i < len; ++i)
+ isl_int_addmul(src1[i], m2, src2[i]);
+ return;
+ }
+
+ isl_int_init(tmp);
+ for (i = 0; i < len; ++i) {
+ isl_int_mul(tmp, m1, src1[i]);
+ isl_int_addmul(tmp, m2, src2[i]);
+ isl_int_set(dst[i], tmp);
+ }
+ isl_int_clear(tmp);
+}
+
+/* Eliminate element "pos" from "dst" using "src".
+ * In particular, let d = dst[pos] and s = src[pos], then
+ * dst is replaced by (|s| dst - sgn(s)d src)/gcd(s,d),
+ * such that dst[pos] is zero after the elimination.
+ * If "m" is not NULL, then *m is multiplied by |s|/gcd(s,d).
+ * That is, it is multiplied by the same factor as "dst".
+ */
+void isl_seq_elim(isl_int *dst, isl_int *src, unsigned pos, unsigned len,
+ isl_int *m)
+{
+ isl_int a;
+ isl_int b;
+
+ if (isl_int_is_zero(dst[pos]))
+ return;
+
+ isl_int_init(a);
+ isl_int_init(b);
+
+ isl_int_gcd(a, src[pos], dst[pos]);
+ isl_int_divexact(b, dst[pos], a);
+ if (isl_int_is_pos(src[pos]))
+ isl_int_neg(b, b);
+ isl_int_divexact(a, src[pos], a);
+ isl_int_abs(a, a);
+ isl_seq_combine(dst, a, dst, b, src, len);
+
+ if (m)
+ isl_int_mul(*m, *m, a);
+
+ isl_int_clear(a);
+ isl_int_clear(b);
+}
+
+int isl_seq_eq(isl_int *p1, isl_int *p2, unsigned len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ if (isl_int_ne(p1[i], p2[i]))
+ return 0;
+ return 1;
+}
+
+int isl_seq_cmp(isl_int *p1, isl_int *p2, unsigned len)
+{
+ int i;
+ int cmp;
+ for (i = 0; i < len; ++i)
+ if ((cmp = isl_int_cmp(p1[i], p2[i])) != 0)
+ return cmp;
+ return 0;
+}
+
+int isl_seq_is_neg(isl_int *p1, isl_int *p2, unsigned len)
+{
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ if (isl_int_abs_ne(p1[i], p2[i]))
+ return 0;
+ if (isl_int_is_zero(p1[i]))
+ continue;
+ if (isl_int_eq(p1[i], p2[i]))
+ return 0;
+ }
+ return 1;
+}
+
+int isl_seq_first_non_zero(isl_int *p, unsigned len)
+{
+ int i;
+
+ for (i = 0; i < len; ++i)
+ if (!isl_int_is_zero(p[i]))
+ return i;
+ return -1;
+}
+
+int isl_seq_last_non_zero(isl_int *p, unsigned len)
+{
+ int i;
+
+ for (i = len - 1; i >= 0; --i)
+ if (!isl_int_is_zero(p[i]))
+ return i;
+ return -1;
+}
+
+void isl_seq_abs_max(isl_int *p, unsigned len, isl_int *max)
+{
+ int i;
+
+ isl_int_set_si(*max, 0);
+
+ for (i = 0; i < len; ++i)
+ if (isl_int_abs_gt(p[i], *max))
+ isl_int_abs(*max, p[i]);
+}
+
+int isl_seq_abs_min_non_zero(isl_int *p, unsigned len)
+{
+ int i, min = isl_seq_first_non_zero(p, len);
+ if (min < 0)
+ return -1;
+ for (i = min + 1; i < len; ++i) {
+ if (isl_int_is_zero(p[i]))
+ continue;
+ if (isl_int_abs_lt(p[i], p[min]))
+ min = i;
+ }
+ return min;
+}
+
+void isl_seq_gcd(isl_int *p, unsigned len, isl_int *gcd)
+{
+ int i, min = isl_seq_abs_min_non_zero(p, len);
+
+ if (min < 0) {
+ isl_int_set_si(*gcd, 0);
+ return;
+ }
+ isl_int_abs(*gcd, p[min]);
+ for (i = 0; isl_int_cmp_si(*gcd, 1) > 0 && i < len; ++i) {
+ if (i == min)
+ continue;
+ if (isl_int_is_zero(p[i]))
+ continue;
+ isl_int_gcd(*gcd, *gcd, p[i]);
+ }
+}
+
+void isl_seq_normalize(struct isl_ctx *ctx, isl_int *p, unsigned len)
+{
+ if (len == 0)
+ return;
+ isl_seq_gcd(p, len, &ctx->normalize_gcd);
+ if (!isl_int_is_zero(ctx->normalize_gcd) &&
+ !isl_int_is_one(ctx->normalize_gcd))
+ isl_seq_scale_down(p, p, ctx->normalize_gcd, len);
+}
+
+void isl_seq_lcm(isl_int *p, unsigned len, isl_int *lcm)
+{
+ int i;
+
+ if (len == 0) {
+ isl_int_set_si(*lcm, 1);
+ return;
+ }
+ isl_int_set(*lcm, p[0]);
+ for (i = 1; i < len; ++i)
+ isl_int_lcm(*lcm, *lcm, p[i]);
+}
+
+void isl_seq_inner_product(isl_int *p1, isl_int *p2, unsigned len,
+ isl_int *prod)
+{
+ int i;
+ if (len == 0) {
+ isl_int_set_si(*prod, 0);
+ return;
+ }
+ isl_int_mul(*prod, p1[0], p2[0]);
+ for (i = 1; i < len; ++i)
+ isl_int_addmul(*prod, p1[i], p2[i]);
+}
+
+uint32_t isl_seq_hash(isl_int *p, unsigned len, uint32_t hash)
+{
+ int i;
+ for (i = 0; i < len; ++i) {
+ if (isl_int_is_zero(p[i]))
+ continue;
+ hash *= 16777619;
+ hash ^= (i & 0xFF);
+ hash = isl_int_hash(p[i], hash);
+ }
+ return hash;
+}
+
+/* Given two affine expressions "p" of length p_len (including the
+ * denominator and the constant term) and "subs" of length subs_len,
+ * plug in "subs" for the variable at position "pos".
+ * The variables of "subs" and "p" are assumed to match up to subs_len,
+ * but "p" may have additional variables.
+ * "v" is an initialized isl_int that can be used internally.
+ *
+ * In particular, if "p" represents the expression
+ *
+ * (a i + g)/m
+ *
+ * with i the variable at position "pos" and "subs" represents the expression
+ *
+ * f/d
+ *
+ * then the result represents the expression
+ *
+ * (a f + d g)/(m d)
+ *
+ */
+void isl_seq_substitute(isl_int *p, int pos, isl_int *subs,
+ int p_len, int subs_len, isl_int v)
+{
+ isl_int_set(v, p[1 + pos]);
+ isl_int_set_si(p[1 + pos], 0);
+ isl_seq_combine(p + 1, subs[0], p + 1, v, subs + 1, subs_len - 1);
+ isl_seq_scale(p + subs_len, p + subs_len, subs[0], p_len - subs_len);
+ isl_int_mul(p[0], p[0], subs[0]);
+}
+
+uint32_t isl_seq_get_hash(isl_int *p, unsigned len)
+{
+ uint32_t hash = isl_hash_init();
+
+ return isl_seq_hash(p, len, hash);
+}
+
+uint32_t isl_seq_get_hash_bits(isl_int *p, unsigned len, unsigned bits)
+{
+ uint32_t hash;
+
+ hash = isl_seq_get_hash(p, len);
+ return isl_hash_bits(hash, bits);
+}
+
+void isl_seq_dump(isl_int *p, unsigned len)
+{
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ if (i)
+ fprintf(stderr, " ");
+ isl_int_print(stderr, p[i], 0);
+ }
+ fprintf(stderr, "\n");
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_seq.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_seq.h
new file mode 100644
index 00000000000..de0d2e6e048
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_seq.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_SEQ_H
+#define ISL_SEQ_H
+
+#include <sys/types.h>
+#include <isl_int.h>
+#include <isl/ctx.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Some common operations on sequences of isl_int's */
+
+void isl_seq_clr(isl_int *p, unsigned len);
+void isl_seq_set(isl_int *p, isl_int v, unsigned len);
+void isl_seq_set_si(isl_int *p, int v, unsigned len);
+void isl_seq_neg(isl_int *dst, isl_int *src, unsigned len);
+void isl_seq_cpy(isl_int *dst, isl_int *src, unsigned len);
+void isl_seq_addmul(isl_int *dst, isl_int f, isl_int *src, unsigned len);
+void isl_seq_submul(isl_int *dst, isl_int f, isl_int *src, unsigned len);
+void isl_seq_swp_or_cpy(isl_int *dst, isl_int *src, unsigned len);
+void isl_seq_scale(isl_int *dst, isl_int *src, isl_int f, unsigned len);
+void isl_seq_scale_down(isl_int *dst, isl_int *src, isl_int f, unsigned len);
+void isl_seq_cdiv_q(isl_int *dst, isl_int *src, isl_int m, unsigned len);
+void isl_seq_fdiv_q(isl_int *dst, isl_int *src, isl_int m, unsigned len);
+void isl_seq_fdiv_r(isl_int *dst, isl_int *src, isl_int m, unsigned len);
+void isl_seq_combine(isl_int *dst, isl_int m1, isl_int *src1,
+ isl_int m2, isl_int *src2, unsigned len);
+void isl_seq_elim(isl_int *dst, isl_int *src, unsigned pos, unsigned len,
+ isl_int *m);
+void isl_seq_abs_max(isl_int *p, unsigned len, isl_int *max);
+void isl_seq_gcd(isl_int *p, unsigned len, isl_int *gcd);
+void isl_seq_lcm(isl_int *p, unsigned len, isl_int *lcm);
+void isl_seq_normalize(struct isl_ctx *ctx, isl_int *p, unsigned len);
+void isl_seq_inner_product(isl_int *p1, isl_int *p2, unsigned len,
+ isl_int *prod);
+int isl_seq_first_non_zero(isl_int *p, unsigned len);
+int isl_seq_last_non_zero(isl_int *p, unsigned len);
+int isl_seq_abs_min_non_zero(isl_int *p, unsigned len);
+int isl_seq_eq(isl_int *p1, isl_int *p2, unsigned len);
+int isl_seq_cmp(isl_int *p1, isl_int *p2, unsigned len);
+int isl_seq_is_neg(isl_int *p1, isl_int *p2, unsigned len);
+
+void isl_seq_substitute(isl_int *p, int pos, isl_int *subs,
+ int p_len, int subs_len, isl_int v);
+
+uint32_t isl_seq_get_hash(isl_int *p, unsigned len);
+uint32_t isl_seq_get_hash_bits(isl_int *p, unsigned len, unsigned bits);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_list.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_list.c
new file mode 100644
index 00000000000..e1bbcaf5685
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_list.c
@@ -0,0 +1,34 @@
+#include <isl/set.h>
+#include <isl/union_set.h>
+
+#undef EL
+#define EL isl_basic_set
+
+#include <isl_list_templ.h>
+
+#undef EL
+#define EL isl_set
+
+#include <isl_list_templ.h>
+
+#undef EL
+#define EL isl_union_set
+
+#include <isl_list_templ.h>
+
+#undef EL_BASE
+#define EL_BASE basic_set
+
+#include <isl_list_templ.c>
+
+#undef EL_BASE
+#define EL_BASE set
+
+#include <isl_list_templ.c>
+#include <isl_list_read_templ.c>
+
+#undef EL_BASE
+#define EL_BASE union_set
+
+#include <isl_list_templ.c>
+#include <isl_list_read_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_to_ast_graft_list.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_to_ast_graft_list.c
new file mode 100644
index 00000000000..86206d0e4ef
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_to_ast_graft_list.c
@@ -0,0 +1,17 @@
+#include <isl/ctx.h>
+#include <isl/set_type.h>
+#include "isl_ast_graft_private.h"
+#include "isl_set_to_ast_graft_list.h"
+
+#define isl_ast_graft_list_is_identical(a, b) isl_bool_ok(a == b)
+
+#define ISL_KEY isl_set
+#define ISL_VAL isl_ast_graft_list
+#define ISL_HMAP_SUFFIX set_to_ast_graft_list
+#define ISL_HMAP isl_set_to_ast_graft_list
+#define ISL_KEY_IS_EQUAL isl_set_plain_is_equal
+#define ISL_VAL_IS_EQUAL isl_ast_graft_list_is_identical
+#define ISL_KEY_PRINT isl_printer_print_set
+#define ISL_VAL_PRINT isl_printer_print_ast_graft_list
+
+#include <isl/hmap_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_to_ast_graft_list.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_to_ast_graft_list.h
new file mode 100644
index 00000000000..862afaab2c4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_set_to_ast_graft_list.h
@@ -0,0 +1,18 @@
+#ifndef ISL_SET_TO_GRAFT_LIST_H
+#define ISL_SET_TO_GRAFT_LIST_H
+
+#include <isl/set_type.h>
+#include "isl_ast_graft_private.h"
+#include "isl_maybe_ast_graft_list.h"
+
+#define ISL_KEY isl_set
+#define ISL_VAL isl_ast_graft_list
+#define ISL_HMAP_SUFFIX set_to_ast_graft_list
+#define ISL_HMAP isl_set_to_ast_graft_list
+#include <isl/hmap.h>
+#undef ISL_KEY
+#undef ISL_VAL
+#undef ISL_HMAP_SUFFIX
+#undef ISL_HMAP
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sort.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sort.c
new file mode 100644
index 00000000000..9ed273caa18
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sort.c
@@ -0,0 +1,157 @@
+/*
+ * The code of this file was taken from http://jeffreystedfast.blogspot.be,
+ * where it was posted in 2011 by Jeffrey Stedfast under the MIT license.
+ * The MIT license text is as follows:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <isl_sort.h>
+
+#define MID(lo, hi) (lo + ((hi - lo) >> 1))
+
+/* The code here is an optimized merge sort. Starting from a generic merge sort
+ * the following optimizations were applied:
+ *
+ * o Batching of memcpy() calls: Instead of calling memcpy() to copy each and
+ * every element into a temporary buffer, blocks of elements are copied
+ * at a time.
+ *
+ * o To reduce the number of memcpy() calls further, copying leading
+ * and trailing elements into our temporary buffer is avoided, in case it is
+ * not necessary to merge them.
+ *
+ * A further optimization could be to specialize memcpy calls based on the
+ * size of the types we compare. For now, this code does not include the
+ * relevant optimization, as clang e.g. inlines a very efficient memcpy()
+ * implementation. It is not clear, that the specialized version as provided in
+ * the blog post, is really superior to the one that will be inlined by
+ * default. So we decided to keep the code simple until this optimization was
+ * proven to be beneficial.
+ */
+
+static void
+msort (void *array, void *buf, size_t low, size_t high, size_t size,
+ int (* compare) (const void *, const void *, void *), void *arg)
+{
+ char *a1, *al, *am, *ah, *ls, *hs, *lo, *hi, *b;
+ size_t copied = 0;
+ size_t mid;
+
+ mid = MID (low, high);
+
+ if (mid + 1 < high)
+ msort (array, buf, mid + 1, high, size, compare, arg);
+
+ if (mid > low)
+ msort (array, buf, low, mid, size, compare, arg);
+
+ ah = ((char *) array) + ((high + 1) * size);
+ am = ((char *) array) + ((mid + 1) * size);
+ a1 = al = ((char *) array) + (low * size);
+
+ b = (char *) buf;
+ lo = al;
+ hi = am;
+
+ do {
+ ls = lo;
+ hs = hi;
+
+ if (lo > al || hi > am) {
+ /* our last loop already compared lo & hi and found lo <= hi */
+ lo += size;
+ }
+
+ while (lo < am && compare (lo, hi, arg) <= 0)
+ lo += size;
+
+ if (lo < am) {
+ if (copied == 0) {
+ /* avoid copying the leading items */
+ a1 = lo;
+ ls = lo;
+ }
+
+ /* our last compare tells us hi < lo */
+ hi += size;
+
+ while (hi < ah && compare (hi, lo, arg) < 0)
+ hi += size;
+
+ if (lo > ls) {
+ memcpy (b, ls, lo - ls);
+ copied += (lo - ls);
+ b += (lo - ls);
+ }
+
+ memcpy (b, hs, hi - hs);
+ copied += (hi - hs);
+ b += (hi - hs);
+ } else if (copied) {
+ memcpy (b, ls, lo - ls);
+ copied += (lo - ls);
+ b += (lo - ls);
+
+ /* copy everything we needed to re-order back into array */
+ memcpy (a1, buf, copied);
+ return;
+ } else {
+ /* everything already in order */
+ return;
+ }
+ } while (hi < ah);
+
+ if (lo < am) {
+ memcpy (b, lo, am - lo);
+ copied += (am - lo);
+ }
+
+ memcpy (a1, buf, copied);
+}
+
+static int
+MergeSort (void *base, size_t nmemb, size_t size,
+ int (* compare) (const void *, const void *, void *), void *arg)
+{
+ void *tmp;
+
+ if (nmemb < 2)
+ return 0;
+
+ if (!(tmp = malloc (nmemb * size))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ msort (base, tmp, 0, nmemb - 1, size, compare, arg);
+
+ free (tmp);
+
+ return 0;
+}
+
+int isl_sort(void *const pbase, size_t total_elems, size_t size,
+ int (*cmp)(const void *, const void *, void *arg), void *arg)
+{
+ return MergeSort (pbase, total_elems, size, cmp, arg);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sort.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sort.h
new file mode 100644
index 00000000000..b69fe01d991
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_sort.h
@@ -0,0 +1,9 @@
+#ifndef ISL_SORT_H
+#define ISL_SORT_H
+
+#include <stddef.h>
+
+int isl_sort(void *const pbase, size_t total_elems, size_t size,
+ int (*cmp)(const void *, const void *, void *arg), void *arg);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_space.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_space.c
new file mode 100644
index 00000000000..fd67a89c65a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_space.c
@@ -0,0 +1,3366 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2013-2014 Ecole Normale Superieure
+ * Copyright 2018-2019 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ * and Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <isl_space_private.h>
+#include <isl_id_private.h>
+#include <isl_reordering.h>
+
+isl_ctx *isl_space_get_ctx(__isl_keep isl_space *space)
+{
+ return space ? space->ctx : NULL;
+}
+
+__isl_give isl_space *isl_space_alloc(isl_ctx *ctx,
+ unsigned nparam, unsigned n_in, unsigned n_out)
+{
+ isl_space *space;
+
+ space = isl_alloc_type(ctx, struct isl_space);
+ if (!space)
+ return NULL;
+
+ space->ctx = ctx;
+ isl_ctx_ref(ctx);
+ space->ref = 1;
+ space->nparam = nparam;
+ space->n_in = n_in;
+ space->n_out = n_out;
+
+ space->tuple_id[0] = NULL;
+ space->tuple_id[1] = NULL;
+
+ space->nested[0] = NULL;
+ space->nested[1] = NULL;
+
+ space->n_id = 0;
+ space->ids = NULL;
+
+ return space;
+}
+
+/* Mark the space as being that of a set, by setting the domain tuple
+ * to isl_id_none.
+ */
+static __isl_give isl_space *mark_as_set(__isl_take isl_space *space)
+{
+ space = isl_space_cow(space);
+ if (!space)
+ return NULL;
+ space = isl_space_set_tuple_id(space, isl_dim_in, &isl_id_none);
+ return space;
+}
+
+/* Is the space that of a set?
+ */
+isl_bool isl_space_is_set(__isl_keep isl_space *space)
+{
+ if (!space)
+ return isl_bool_error;
+ if (space->n_in != 0 || space->nested[0])
+ return isl_bool_false;
+ if (space->tuple_id[0] != &isl_id_none)
+ return isl_bool_false;
+ return isl_bool_true;
+}
+
+/* Check that "space" is a set space.
+ */
+isl_stat isl_space_check_is_set(__isl_keep isl_space *space)
+{
+ isl_bool is_set;
+
+ is_set = isl_space_is_set(space);
+ if (is_set < 0)
+ return isl_stat_error;
+ if (!is_set)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "space is not a set", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Is the given space that of a map?
+ */
+isl_bool isl_space_is_map(__isl_keep isl_space *space)
+{
+ int r;
+
+ if (!space)
+ return isl_bool_error;
+ r = space->tuple_id[0] != &isl_id_none &&
+ space->tuple_id[1] != &isl_id_none;
+ return isl_bool_ok(r);
+}
+
+/* Check that "space" is the space of a map.
+ */
+static isl_stat isl_space_check_is_map(__isl_keep isl_space *space)
+{
+ isl_bool is_space;
+
+ is_space = isl_space_is_map(space);
+ if (is_space < 0)
+ return isl_stat_error;
+ if (!is_space)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "expecting map space", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Check that "space" is the space of a map
+ * where the domain is a wrapped map space.
+ */
+isl_stat isl_space_check_domain_is_wrapping(__isl_keep isl_space *space)
+{
+ isl_bool wrapping;
+
+ wrapping = isl_space_domain_is_wrapping(space);
+ if (wrapping < 0)
+ return isl_stat_error;
+ if (!wrapping)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "domain not a product", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Check that "space" is the space of a map
+ * where the range is a wrapped map space.
+ */
+isl_stat isl_space_check_range_is_wrapping(__isl_keep isl_space *space)
+{
+ isl_bool wrapping;
+
+ wrapping = isl_space_range_is_wrapping(space);
+ if (wrapping < 0)
+ return isl_stat_error;
+ if (!wrapping)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "range not a product", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+__isl_give isl_space *isl_space_set_alloc(isl_ctx *ctx,
+ unsigned nparam, unsigned dim)
+{
+ isl_space *space;
+ space = isl_space_alloc(ctx, nparam, 0, dim);
+ space = mark_as_set(space);
+ return space;
+}
+
+/* Mark the space as being that of a parameter domain, by setting
+ * both tuples to isl_id_none.
+ */
+static __isl_give isl_space *mark_as_params(isl_space *space)
+{
+ if (!space)
+ return NULL;
+ space = isl_space_set_tuple_id(space, isl_dim_in, &isl_id_none);
+ space = isl_space_set_tuple_id(space, isl_dim_out, &isl_id_none);
+ return space;
+}
+
+/* Is the space that of a parameter domain?
+ */
+isl_bool isl_space_is_params(__isl_keep isl_space *space)
+{
+ if (!space)
+ return isl_bool_error;
+ if (space->n_in != 0 || space->nested[0] ||
+ space->n_out != 0 || space->nested[1])
+ return isl_bool_false;
+ if (space->tuple_id[0] != &isl_id_none)
+ return isl_bool_false;
+ if (space->tuple_id[1] != &isl_id_none)
+ return isl_bool_false;
+ return isl_bool_true;
+}
+
+/* Create a space for a parameter domain.
+ */
+__isl_give isl_space *isl_space_params_alloc(isl_ctx *ctx, unsigned nparam)
+{
+ isl_space *space;
+ space = isl_space_alloc(ctx, nparam, 0, 0);
+ space = mark_as_params(space);
+ return space;
+}
+
+/* Create a space for a parameter domain, without any parameters.
+ */
+__isl_give isl_space *isl_space_unit(isl_ctx *ctx)
+{
+ return isl_space_params_alloc(ctx, 0);
+}
+
+static isl_size global_pos(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned pos)
+{
+ if (isl_space_check_range(space, type, pos, 1) < 0)
+ return isl_size_error;
+
+ switch (type) {
+ case isl_dim_param:
+ return pos;
+ case isl_dim_in:
+ return pos + space->nparam;
+ case isl_dim_out:
+ return pos + space->nparam + space->n_in;
+ default:
+ isl_assert(isl_space_get_ctx(space), 0, return isl_size_error);
+ }
+ return isl_size_error;
+}
+
+/* Extend length of ids array to the total number of dimensions.
+ */
+static __isl_give isl_space *extend_ids(__isl_take isl_space *space)
+{
+ isl_id **ids;
+ int i;
+ isl_size dim;
+
+ dim = isl_space_dim(space, isl_dim_all);
+ if (dim < 0)
+ return isl_space_free(space);
+ if (dim <= space->n_id)
+ return space;
+
+ if (!space->ids) {
+ space->ids = isl_calloc_array(space->ctx, isl_id *, dim);
+ if (!space->ids)
+ goto error;
+ } else {
+ ids = isl_realloc_array(space->ctx, space->ids, isl_id *, dim);
+ if (!ids)
+ goto error;
+ space->ids = ids;
+ for (i = space->n_id; i < dim; ++i)
+ space->ids[i] = NULL;
+ }
+
+ space->n_id = dim;
+
+ return space;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+static __isl_give isl_space *set_id(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id)
+{
+ isl_size gpos;
+
+ space = isl_space_cow(space);
+
+ gpos = global_pos(space, type, pos);
+ if (gpos < 0)
+ goto error;
+
+ if (gpos >= space->n_id) {
+ if (!id)
+ return space;
+ space = extend_ids(space);
+ if (!space)
+ goto error;
+ }
+
+ space->ids[gpos] = id;
+
+ return space;
+error:
+ isl_id_free(id);
+ isl_space_free(space);
+ return NULL;
+}
+
+static __isl_keep isl_id *get_id(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned pos)
+{
+ isl_size gpos;
+
+ gpos = global_pos(space, type, pos);
+ if (gpos < 0)
+ return NULL;
+ if (gpos >= space->n_id)
+ return NULL;
+ return space->ids[gpos];
+}
+
+/* Return the nested space at the given position.
+ */
+static __isl_keep isl_space *isl_space_peek_nested(__isl_keep isl_space *space,
+ int pos)
+{
+ if (!space)
+ return NULL;
+ if (!space->nested[pos])
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "no nested space", return NULL);
+ return space->nested[pos];
+}
+
+static unsigned offset(__isl_keep isl_space *space, enum isl_dim_type type)
+{
+ switch (type) {
+ case isl_dim_param: return 0;
+ case isl_dim_in: return space->nparam;
+ case isl_dim_out: return space->nparam + space->n_in;
+ default: return 0;
+ }
+}
+
+static unsigned n(__isl_keep isl_space *space, enum isl_dim_type type)
+{
+ switch (type) {
+ case isl_dim_param: return space->nparam;
+ case isl_dim_in: return space->n_in;
+ case isl_dim_out: return space->n_out;
+ case isl_dim_all:
+ return space->nparam + space->n_in + space->n_out;
+ default: return 0;
+ }
+}
+
+isl_size isl_space_dim(__isl_keep isl_space *space, enum isl_dim_type type)
+{
+ if (!space)
+ return isl_size_error;
+ return n(space, type);
+}
+
+/* Return the dimensionality of tuple "inner" within the wrapped relation
+ * inside tuple "outer".
+ */
+isl_size isl_space_wrapped_dim(__isl_keep isl_space *space,
+ enum isl_dim_type outer, enum isl_dim_type inner)
+{
+ int pos;
+
+ if (!space)
+ return isl_size_error;
+ if (outer != isl_dim_in && outer != isl_dim_out)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "only input, output and set tuples "
+ "can have nested relations", return isl_size_error);
+ pos = outer - isl_dim_in;
+ return isl_space_dim(isl_space_peek_nested(space, pos), inner);
+}
+
+unsigned isl_space_offset(__isl_keep isl_space *space, enum isl_dim_type type)
+{
+ if (!space)
+ return 0;
+ return offset(space, type);
+}
+
+static __isl_give isl_space *copy_ids(__isl_take isl_space *dst,
+ enum isl_dim_type dst_type, unsigned offset, __isl_keep isl_space *src,
+ enum isl_dim_type src_type)
+{
+ int i;
+ isl_id *id;
+
+ if (!dst)
+ return NULL;
+
+ for (i = 0; i < n(src, src_type); ++i) {
+ id = get_id(src, src_type, i);
+ if (!id)
+ continue;
+ dst = set_id(dst, dst_type, offset + i, isl_id_copy(id));
+ if (!dst)
+ return NULL;
+ }
+ return dst;
+}
+
+__isl_take isl_space *isl_space_dup(__isl_keep isl_space *space)
+{
+ isl_space *dup;
+ if (!space)
+ return NULL;
+ dup = isl_space_alloc(space->ctx,
+ space->nparam, space->n_in, space->n_out);
+ if (!dup)
+ return NULL;
+ if (space->tuple_id[0] &&
+ !(dup->tuple_id[0] = isl_id_copy(space->tuple_id[0])))
+ goto error;
+ if (space->tuple_id[1] &&
+ !(dup->tuple_id[1] = isl_id_copy(space->tuple_id[1])))
+ goto error;
+ if (space->nested[0] &&
+ !(dup->nested[0] = isl_space_copy(space->nested[0])))
+ goto error;
+ if (space->nested[1] &&
+ !(dup->nested[1] = isl_space_copy(space->nested[1])))
+ goto error;
+ if (!space->ids)
+ return dup;
+ dup = copy_ids(dup, isl_dim_param, 0, space, isl_dim_param);
+ dup = copy_ids(dup, isl_dim_in, 0, space, isl_dim_in);
+ dup = copy_ids(dup, isl_dim_out, 0, space, isl_dim_out);
+ return dup;
+error:
+ isl_space_free(dup);
+ return NULL;
+}
+
+__isl_give isl_space *isl_space_cow(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+
+ if (space->ref == 1)
+ return space;
+ space->ref--;
+ return isl_space_dup(space);
+}
+
+__isl_give isl_space *isl_space_copy(__isl_keep isl_space *space)
+{
+ if (!space)
+ return NULL;
+
+ space->ref++;
+ return space;
+}
+
+__isl_null isl_space *isl_space_free(__isl_take isl_space *space)
+{
+ int i;
+
+ if (!space)
+ return NULL;
+
+ if (--space->ref > 0)
+ return NULL;
+
+ isl_id_free(space->tuple_id[0]);
+ isl_id_free(space->tuple_id[1]);
+
+ isl_space_free(space->nested[0]);
+ isl_space_free(space->nested[1]);
+
+ for (i = 0; i < space->n_id; ++i)
+ isl_id_free(space->ids[i]);
+ free(space->ids);
+ isl_ctx_deref(space->ctx);
+
+ free(space);
+
+ return NULL;
+}
+
+/* Check if "s" is a valid dimension or tuple name.
+ * We currently only forbid names that look like a number.
+ *
+ * s is assumed to be non-NULL.
+ */
+static int name_ok(isl_ctx *ctx, const char *s)
+{
+ char *p;
+ long dummy;
+
+ dummy = strtol(s, &p, 0);
+ if (p != s)
+ isl_die(ctx, isl_error_invalid, "name looks like a number",
+ return 0);
+
+ return 1;
+}
+
+/* Return a copy of the nested space at the given position.
+ */
+static __isl_keep isl_space *isl_space_get_nested(__isl_keep isl_space *space,
+ int pos)
+{
+ return isl_space_copy(isl_space_peek_nested(space, pos));
+}
+
+/* Return the nested space at the given position.
+ * This may be either a copy or the nested space itself
+ * if there is only one reference to "space".
+ * This allows the nested space to be modified inplace
+ * if both "space" and the nested space have only a single reference.
+ * The caller is not allowed to modify "space" between this call and
+ * a subsequent call to isl_space_restore_nested.
+ * The only exception is that isl_space_free can be called instead.
+ */
+static __isl_give isl_space *isl_space_take_nested(__isl_keep isl_space *space,
+ int pos)
+{
+ isl_space *nested;
+
+ if (!space)
+ return NULL;
+ if (space->ref != 1)
+ return isl_space_get_nested(space, pos);
+ nested = space->nested[pos];
+ space->nested[pos] = NULL;
+ return nested;
+}
+
+/* Replace the nested space at the given position by "nested",
+ * where this nested space of "space" may be missing
+ * due to a preceding call to isl_space_take_nested.
+ * However, in this case, "space" only has a single reference and
+ * then the call to isl_space_cow has no effect.
+ */
+static __isl_give isl_space *isl_space_restore_nested(
+ __isl_take isl_space *space, int pos, __isl_take isl_space *nested)
+{
+ if (!space || !nested)
+ goto error;
+
+ if (space->nested[pos] == nested) {
+ isl_space_free(nested);
+ return space;
+ }
+
+ space = isl_space_cow(space);
+ if (!space)
+ goto error;
+ isl_space_free(space->nested[pos]);
+ space->nested[pos] = nested;
+
+ return space;
+error:
+ isl_space_free(space);
+ isl_space_free(nested);
+ return NULL;
+}
+
+/* Is it possible for the given dimension type to have a tuple id?
+ */
+static int space_can_have_id(__isl_keep isl_space *space,
+ enum isl_dim_type type)
+{
+ if (!space)
+ return 0;
+ if (isl_space_is_params(space))
+ isl_die(space->ctx, isl_error_invalid,
+ "parameter spaces don't have tuple ids", return 0);
+ if (isl_space_is_set(space) && type != isl_dim_set)
+ isl_die(space->ctx, isl_error_invalid,
+ "set spaces can only have a set id", return 0);
+ if (type != isl_dim_in && type != isl_dim_out)
+ isl_die(space->ctx, isl_error_invalid,
+ "only input, output and set tuples can have ids",
+ return 0);
+
+ return 1;
+}
+
+/* Does the tuple have an id?
+ */
+isl_bool isl_space_has_tuple_id(__isl_keep isl_space *space,
+ enum isl_dim_type type)
+{
+ if (!space_can_have_id(space, type))
+ return isl_bool_error;
+ return isl_bool_ok(space->tuple_id[type - isl_dim_in] != NULL);
+}
+
+/* Does the domain tuple of the map space "space" have an identifier?
+ */
+isl_bool isl_space_has_domain_tuple_id(__isl_keep isl_space *space)
+{
+ if (isl_space_check_is_map(space) < 0)
+ return isl_bool_error;
+ return isl_space_has_tuple_id(space, isl_dim_in);
+}
+
+/* Does the range tuple of the map space "space" have an identifier?
+ */
+isl_bool isl_space_has_range_tuple_id(__isl_keep isl_space *space)
+{
+ if (isl_space_check_is_map(space) < 0)
+ return isl_bool_error;
+ return isl_space_has_tuple_id(space, isl_dim_out);
+}
+
+__isl_give isl_id *isl_space_get_tuple_id(__isl_keep isl_space *space,
+ enum isl_dim_type type)
+{
+ int has_id;
+
+ if (!space)
+ return NULL;
+ has_id = isl_space_has_tuple_id(space, type);
+ if (has_id < 0)
+ return NULL;
+ if (!has_id)
+ isl_die(space->ctx, isl_error_invalid,
+ "tuple has no id", return NULL);
+ return isl_id_copy(space->tuple_id[type - isl_dim_in]);
+}
+
+/* Return the identifier of the domain tuple of the map space "space",
+ * assuming it has one.
+ */
+__isl_give isl_id *isl_space_get_domain_tuple_id(
+ __isl_keep isl_space *space)
+{
+ if (isl_space_check_is_map(space) < 0)
+ return NULL;
+ return isl_space_get_tuple_id(space, isl_dim_in);
+}
+
+/* Return the identifier of the range tuple of the map space "space",
+ * assuming it has one.
+ */
+__isl_give isl_id *isl_space_get_range_tuple_id(
+ __isl_keep isl_space *space)
+{
+ if (isl_space_check_is_map(space) < 0)
+ return NULL;
+ return isl_space_get_tuple_id(space, isl_dim_out);
+}
+
+__isl_give isl_space *isl_space_set_tuple_id(__isl_take isl_space *space,
+ enum isl_dim_type type, __isl_take isl_id *id)
+{
+ space = isl_space_cow(space);
+ if (!space || !id)
+ goto error;
+ if (type != isl_dim_in && type != isl_dim_out)
+ isl_die(space->ctx, isl_error_invalid,
+ "only input, output and set tuples can have names",
+ goto error);
+
+ isl_id_free(space->tuple_id[type - isl_dim_in]);
+ space->tuple_id[type - isl_dim_in] = id;
+
+ return space;
+error:
+ isl_id_free(id);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Replace the identifier of the domain tuple of the map space "space"
+ * by "id".
+ */
+__isl_give isl_space *isl_space_set_domain_tuple_id(
+ __isl_take isl_space *space, __isl_take isl_id *id)
+{
+ if (isl_space_check_is_map(space) < 0)
+ space = isl_space_free(space);
+ return isl_space_set_tuple_id(space, isl_dim_in, id);
+}
+
+/* Replace the identifier of the range tuple of the map space "space"
+ * by "id".
+ */
+__isl_give isl_space *isl_space_set_range_tuple_id(
+ __isl_take isl_space *space, __isl_take isl_id *id)
+{
+ if (isl_space_check_is_map(space) < 0)
+ space = isl_space_free(space);
+ return isl_space_set_tuple_id(space, isl_dim_out, id);
+}
+
+__isl_give isl_space *isl_space_reset_tuple_id(__isl_take isl_space *space,
+ enum isl_dim_type type)
+{
+ space = isl_space_cow(space);
+ if (!space)
+ return NULL;
+ if (type != isl_dim_in && type != isl_dim_out)
+ isl_die(space->ctx, isl_error_invalid,
+ "only input, output and set tuples can have names",
+ goto error);
+
+ isl_id_free(space->tuple_id[type - isl_dim_in]);
+ space->tuple_id[type - isl_dim_in] = NULL;
+
+ return space;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Set the id of the given dimension of "space" to "id".
+ * If the dimension already has an id, then it is replaced.
+ * If the dimension is a parameter, then we need to change it
+ * in the nested spaces (if any) as well.
+ */
+__isl_give isl_space *isl_space_set_dim_id(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned pos, __isl_take isl_id *id)
+{
+ space = isl_space_cow(space);
+ if (!space || !id)
+ goto error;
+
+ if (type == isl_dim_param) {
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ if (!space->nested[i])
+ continue;
+ space->nested[i] =
+ isl_space_set_dim_id(space->nested[i],
+ type, pos, isl_id_copy(id));
+ if (!space->nested[i])
+ goto error;
+ }
+ }
+
+ isl_id_free(get_id(space, type, pos));
+ return set_id(space, type, pos, id);
+error:
+ isl_id_free(id);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Reset the id of the given dimension of "space".
+ * If the dimension already has an id, then it is removed.
+ * If the dimension is a parameter, then we need to reset it
+ * in the nested spaces (if any) as well.
+ */
+__isl_give isl_space *isl_space_reset_dim_id(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned pos)
+{
+ space = isl_space_cow(space);
+ if (!space)
+ goto error;
+
+ if (type == isl_dim_param) {
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ if (!space->nested[i])
+ continue;
+ space->nested[i] =
+ isl_space_reset_dim_id(space->nested[i],
+ type, pos);
+ if (!space->nested[i])
+ goto error;
+ }
+ }
+
+ isl_id_free(get_id(space, type, pos));
+ return set_id(space, type, pos, NULL);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+isl_bool isl_space_has_dim_id(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned pos)
+{
+ if (!space)
+ return isl_bool_error;
+ return isl_bool_ok(get_id(space, type, pos) != NULL);
+}
+
+__isl_give isl_id *isl_space_get_dim_id(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned pos)
+{
+ if (!space)
+ return NULL;
+ if (!get_id(space, type, pos))
+ isl_die(space->ctx, isl_error_invalid,
+ "dim has no id", return NULL);
+ return isl_id_copy(get_id(space, type, pos));
+}
+
+__isl_give isl_space *isl_space_set_tuple_name(__isl_take isl_space *space,
+ enum isl_dim_type type, const char *s)
+{
+ isl_id *id;
+
+ if (!space)
+ return NULL;
+
+ if (!s)
+ return isl_space_reset_tuple_id(space, type);
+
+ if (!name_ok(space->ctx, s))
+ goto error;
+
+ id = isl_id_alloc(space->ctx, s, NULL);
+ return isl_space_set_tuple_id(space, type, id);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Does the tuple have a name?
+ */
+isl_bool isl_space_has_tuple_name(__isl_keep isl_space *space,
+ enum isl_dim_type type)
+{
+ isl_id *id;
+
+ if (!space_can_have_id(space, type))
+ return isl_bool_error;
+ id = space->tuple_id[type - isl_dim_in];
+ return isl_bool_ok(id && id->name);
+}
+
+__isl_keep const char *isl_space_get_tuple_name(__isl_keep isl_space *space,
+ enum isl_dim_type type)
+{
+ isl_id *id;
+ if (!space)
+ return NULL;
+ if (type != isl_dim_in && type != isl_dim_out)
+ return NULL;
+ id = space->tuple_id[type - isl_dim_in];
+ return id ? id->name : NULL;
+}
+
+__isl_give isl_space *isl_space_set_dim_name(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned pos,
+ const char *s)
+{
+ isl_id *id;
+
+ if (!space)
+ return NULL;
+ if (!s)
+ return isl_space_reset_dim_id(space, type, pos);
+ if (!name_ok(space->ctx, s))
+ goto error;
+ id = isl_id_alloc(space->ctx, s, NULL);
+ return isl_space_set_dim_id(space, type, pos, id);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Does the given dimension have a name?
+ */
+isl_bool isl_space_has_dim_name(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned pos)
+{
+ isl_id *id;
+
+ if (!space)
+ return isl_bool_error;
+ id = get_id(space, type, pos);
+ return isl_bool_ok(id && id->name);
+}
+
+__isl_keep const char *isl_space_get_dim_name(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned pos)
+{
+ isl_id *id = get_id(space, type, pos);
+ return id ? id->name : NULL;
+}
+
+int isl_space_find_dim_by_id(__isl_keep isl_space *space,
+ enum isl_dim_type type, __isl_keep isl_id *id)
+{
+ int i;
+ int offset;
+ isl_size n;
+
+ n = isl_space_dim(space, type);
+ if (n < 0 || !id)
+ return -1;
+
+ offset = isl_space_offset(space, type);
+ for (i = 0; i < n && offset + i < space->n_id; ++i)
+ if (space->ids[offset + i] == id)
+ return i;
+
+ return -1;
+}
+
+int isl_space_find_dim_by_name(__isl_keep isl_space *space,
+ enum isl_dim_type type, const char *name)
+{
+ int i;
+ int offset;
+ isl_size n;
+
+ n = isl_space_dim(space, type);
+ if (n < 0 || !name)
+ return -1;
+
+ offset = isl_space_offset(space, type);
+ for (i = 0; i < n && offset + i < space->n_id; ++i) {
+ isl_id *id = get_id(space, type, i);
+ if (id && id->name && !strcmp(id->name, name))
+ return i;
+ }
+
+ return -1;
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * of "space".
+ */
+__isl_give isl_space *isl_space_reset_user(__isl_take isl_space *space)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_id *id;
+ const char *name;
+
+ if (!space)
+ return NULL;
+
+ ctx = isl_space_get_ctx(space);
+
+ for (i = 0; i < space->nparam && i < space->n_id; ++i) {
+ if (!isl_id_get_user(space->ids[i]))
+ continue;
+ space = isl_space_cow(space);
+ if (!space)
+ return NULL;
+ name = isl_id_get_name(space->ids[i]);
+ id = isl_id_alloc(ctx, name, NULL);
+ isl_id_free(space->ids[i]);
+ space->ids[i] = id;
+ if (!id)
+ return isl_space_free(space);
+ }
+
+ for (i = 0; i < 2; ++i) {
+ if (!space->tuple_id[i])
+ continue;
+ if (!isl_id_get_user(space->tuple_id[i]))
+ continue;
+ space = isl_space_cow(space);
+ if (!space)
+ return NULL;
+ name = isl_id_get_name(space->tuple_id[i]);
+ id = isl_id_alloc(ctx, name, NULL);
+ isl_id_free(space->tuple_id[i]);
+ space->tuple_id[i] = id;
+ if (!id)
+ return isl_space_free(space);
+ }
+
+ for (i = 0; i < 2; ++i) {
+ isl_space *nested;
+
+ if (!space->nested[i])
+ continue;
+ nested = isl_space_take_nested(space, i);
+ nested = isl_space_reset_user(nested);
+ space = isl_space_restore_nested(space, i, nested);
+ if (!space)
+ return NULL;
+ }
+
+ return space;
+}
+
+static __isl_keep isl_id *tuple_id(__isl_keep isl_space *space,
+ enum isl_dim_type type)
+{
+ if (!space)
+ return NULL;
+ if (type == isl_dim_in)
+ return space->tuple_id[0];
+ if (type == isl_dim_out)
+ return space->tuple_id[1];
+ return NULL;
+}
+
+static __isl_keep isl_space *nested(__isl_keep isl_space *space,
+ enum isl_dim_type type)
+{
+ if (!space)
+ return NULL;
+ if (type == isl_dim_in)
+ return space->nested[0];
+ if (type == isl_dim_out)
+ return space->nested[1];
+ return NULL;
+}
+
+/* Are the two spaces the same, apart from positions and names of parameters?
+ */
+isl_bool isl_space_has_equal_tuples(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ if (!space1 || !space2)
+ return isl_bool_error;
+ if (space1 == space2)
+ return isl_bool_true;
+ return isl_space_tuple_is_equal(space1, isl_dim_in,
+ space2, isl_dim_in) &&
+ isl_space_tuple_is_equal(space1, isl_dim_out,
+ space2, isl_dim_out);
+}
+
+/* Check that a match involving "space" was successful.
+ * That is, check that "match" is equal to isl_bool_true.
+ */
+static isl_stat check_match(__isl_keep isl_space *space, isl_bool match)
+{
+ if (match < 0)
+ return isl_stat_error;
+ if (!match)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "incompatible spaces", return isl_stat_error);
+
+ return isl_stat_ok;
+}
+
+/* Check that the two spaces are the same,
+ * apart from positions and names of parameters.
+ */
+isl_stat isl_space_check_equal_tuples(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ isl_bool is_equal;
+
+ is_equal = isl_space_has_equal_tuples(space1, space2);
+ return check_match(space1, is_equal);
+}
+
+/* Check if the tuple of type "type1" of "space1" is the same as
+ * the tuple of type "type2" of "space2".
+ *
+ * That is, check if the tuples have the same identifier, the same dimension
+ * and the same internal structure.
+ * The identifiers of the dimensions inside the tuples do not affect the result.
+ *
+ * Note that this function only checks the tuples themselves.
+ * If nested tuples are involved, then we need to be careful not
+ * to have result affected by possibly differing parameters
+ * in those nested tuples.
+ */
+isl_bool isl_space_tuple_is_equal(__isl_keep isl_space *space1,
+ enum isl_dim_type type1, __isl_keep isl_space *space2,
+ enum isl_dim_type type2)
+{
+ isl_id *id1, *id2;
+ isl_space *nested1, *nested2;
+
+ if (!space1 || !space2)
+ return isl_bool_error;
+
+ if (space1 == space2 && type1 == type2)
+ return isl_bool_true;
+
+ if (n(space1, type1) != n(space2, type2))
+ return isl_bool_false;
+ id1 = tuple_id(space1, type1);
+ id2 = tuple_id(space2, type2);
+ if (!id1 ^ !id2)
+ return isl_bool_false;
+ if (id1 && id1 != id2)
+ return isl_bool_false;
+ nested1 = nested(space1, type1);
+ nested2 = nested(space2, type2);
+ if (!nested1 ^ !nested2)
+ return isl_bool_false;
+ if (nested1 && !isl_space_has_equal_tuples(nested1, nested2))
+ return isl_bool_false;
+ return isl_bool_true;
+}
+
+/* Is the tuple "inner" within the wrapped relation inside tuple "outer"
+ * of "space1" equal to tuple "type2" of "space2"?
+ */
+isl_bool isl_space_wrapped_tuple_is_equal(__isl_keep isl_space *space1,
+ enum isl_dim_type outer, enum isl_dim_type inner,
+ __isl_keep isl_space *space2, enum isl_dim_type type2)
+{
+ int pos;
+ isl_space *nested;
+
+ if (!space1)
+ return isl_bool_error;
+ if (outer != isl_dim_in && outer != isl_dim_out)
+ isl_die(isl_space_get_ctx(space1), isl_error_invalid,
+ "only input, output and set tuples "
+ "can have nested relations", return isl_bool_error);
+ pos = outer - isl_dim_in;
+ nested = isl_space_peek_nested(space1, pos);
+ return isl_space_tuple_is_equal(nested, inner, space2, type2);
+}
+
+/* Check that the tuple "inner" within the wrapped relation inside tuple "outer"
+ * of "space1" is equal to tuple "type2" of "space2".
+ */
+isl_stat isl_space_check_wrapped_tuple_is_equal(__isl_keep isl_space *space1,
+ enum isl_dim_type outer, enum isl_dim_type inner,
+ __isl_keep isl_space *space2, enum isl_dim_type type2)
+{
+ isl_bool is_equal;
+
+ is_equal = isl_space_wrapped_tuple_is_equal(space1, outer, inner,
+ space2, type2);
+ return check_match(space1, is_equal);
+}
+
+static isl_bool match(__isl_keep isl_space *space1, enum isl_dim_type type1,
+ __isl_keep isl_space *space2, enum isl_dim_type type2)
+{
+ int i;
+ isl_bool equal;
+
+ if (!space1 || !space2)
+ return isl_bool_error;
+
+ if (space1 == space2 && type1 == type2)
+ return isl_bool_true;
+
+ equal = isl_space_tuple_is_equal(space1, type1, space2, type2);
+ if (equal < 0 || !equal)
+ return equal;
+
+ if (!space1->ids && !space2->ids)
+ return isl_bool_true;
+
+ for (i = 0; i < n(space1, type1); ++i) {
+ if (get_id(space1, type1, i) != get_id(space2, type2, i))
+ return isl_bool_false;
+ }
+ return isl_bool_true;
+}
+
+/* Do "space1" and "space2" have the same parameters?
+ */
+isl_bool isl_space_has_equal_params(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ return match(space1, isl_dim_param, space2, isl_dim_param);
+}
+
+/* Do "space1" and "space2" have the same identifiers for all
+ * the tuple variables?
+ */
+isl_bool isl_space_has_equal_ids(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ isl_bool equal;
+
+ equal = match(space1, isl_dim_in, space2, isl_dim_in);
+ if (equal < 0 || !equal)
+ return equal;
+ return match(space1, isl_dim_out, space2, isl_dim_out);
+}
+
+isl_bool isl_space_match(__isl_keep isl_space *space1, enum isl_dim_type type1,
+ __isl_keep isl_space *space2, enum isl_dim_type type2)
+{
+ return match(space1, type1, space2, type2);
+}
+
+static void get_ids(__isl_keep isl_space *space, enum isl_dim_type type,
+ unsigned first, unsigned n, __isl_keep isl_id **ids)
+{
+ int i;
+
+ for (i = 0; i < n ; ++i)
+ ids[i] = get_id(space, type, first + i);
+}
+
+static __isl_give isl_space *space_extend(__isl_take isl_space *space,
+ unsigned nparam, unsigned n_in, unsigned n_out)
+{
+ isl_id **ids = NULL;
+
+ if (!space)
+ return NULL;
+ if (space->nparam == nparam &&
+ space->n_in == n_in && space->n_out == n_out)
+ return space;
+
+ isl_assert(space->ctx, space->nparam <= nparam, goto error);
+ isl_assert(space->ctx, space->n_in <= n_in, goto error);
+ isl_assert(space->ctx, space->n_out <= n_out, goto error);
+
+ space = isl_space_cow(space);
+ if (!space)
+ goto error;
+
+ if (space->ids) {
+ unsigned n;
+ n = nparam + n_in + n_out;
+ if (n < nparam || n < n_in || n < n_out)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "overflow in total number of dimensions",
+ goto error);
+ ids = isl_calloc_array(space->ctx, isl_id *, n);
+ if (!ids)
+ goto error;
+ get_ids(space, isl_dim_param, 0, space->nparam, ids);
+ get_ids(space, isl_dim_in, 0, space->n_in, ids + nparam);
+ get_ids(space, isl_dim_out, 0, space->n_out,
+ ids + nparam + n_in);
+ free(space->ids);
+ space->ids = ids;
+ space->n_id = nparam + n_in + n_out;
+ }
+ space->nparam = nparam;
+ space->n_in = n_in;
+ space->n_out = n_out;
+
+ return space;
+error:
+ free(ids);
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_space *isl_space_extend(__isl_take isl_space *space,
+ unsigned nparam, unsigned n_in, unsigned n_out)
+{
+ return space_extend(space, nparam, n_in, n_out);
+}
+
+__isl_give isl_space *isl_space_add_dims(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned n)
+{
+ space = isl_space_reset(space, type);
+ if (!space)
+ return NULL;
+ switch (type) {
+ case isl_dim_param:
+ space = space_extend(space,
+ space->nparam + n, space->n_in, space->n_out);
+ if (space && space->nested[0] &&
+ !(space->nested[0] = isl_space_add_dims(space->nested[0],
+ isl_dim_param, n)))
+ goto error;
+ if (space && space->nested[1] &&
+ !(space->nested[1] = isl_space_add_dims(space->nested[1],
+ isl_dim_param, n)))
+ goto error;
+ return space;
+ case isl_dim_in:
+ return space_extend(space,
+ space->nparam, space->n_in + n, space->n_out);
+ case isl_dim_out:
+ return space_extend(space,
+ space->nparam, space->n_in, space->n_out + n);
+ default:
+ isl_die(space->ctx, isl_error_invalid,
+ "cannot add dimensions of specified type", goto error);
+ }
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Add a parameter with identifier "id" to "space", provided
+ * it does not already appear in "space".
+ */
+__isl_give isl_space *isl_space_add_param_id(__isl_take isl_space *space,
+ __isl_take isl_id *id)
+{
+ isl_size pos;
+
+ if (!space || !id)
+ goto error;
+
+ if (isl_space_find_dim_by_id(space, isl_dim_param, id) >= 0) {
+ isl_id_free(id);
+ return space;
+ }
+
+ pos = isl_space_dim(space, isl_dim_param);
+ if (pos < 0)
+ goto error;
+ space = isl_space_add_dims(space, isl_dim_param, 1);
+ space = isl_space_set_dim_id(space, isl_dim_param, pos, id);
+
+ return space;
+error:
+ isl_space_free(space);
+ isl_id_free(id);
+ return NULL;
+}
+
+static int valid_dim_type(enum isl_dim_type type)
+{
+ switch (type) {
+ case isl_dim_param:
+ case isl_dim_in:
+ case isl_dim_out:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+#undef TYPE
+#define TYPE isl_space
+#include "check_type_range_templ.c"
+
+/* Insert "n" dimensions of type "type" at position "pos".
+ * If we are inserting parameters, then they are also inserted in
+ * any nested spaces.
+ */
+__isl_give isl_space *isl_space_insert_dims(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned pos, unsigned n)
+{
+ isl_ctx *ctx;
+ isl_id **ids = NULL;
+
+ if (!space)
+ return NULL;
+ if (n == 0)
+ return isl_space_reset(space, type);
+
+ ctx = isl_space_get_ctx(space);
+ if (!valid_dim_type(type))
+ isl_die(ctx, isl_error_invalid,
+ "cannot insert dimensions of specified type",
+ goto error);
+
+ if (isl_space_check_range(space, type, pos, 0) < 0)
+ return isl_space_free(space);
+
+ space = isl_space_cow(space);
+ if (!space)
+ return NULL;
+
+ if (space->ids) {
+ enum isl_dim_type t, o = isl_dim_param;
+ int off;
+ int s[3];
+ ids = isl_calloc_array(ctx, isl_id *,
+ space->nparam + space->n_in + space->n_out + n);
+ if (!ids)
+ goto error;
+ off = 0;
+ s[isl_dim_param - o] = space->nparam;
+ s[isl_dim_in - o] = space->n_in;
+ s[isl_dim_out - o] = space->n_out;
+ for (t = isl_dim_param; t <= isl_dim_out; ++t) {
+ if (t != type) {
+ get_ids(space, t, 0, s[t - o], ids + off);
+ off += s[t - o];
+ } else {
+ get_ids(space, t, 0, pos, ids + off);
+ off += pos + n;
+ get_ids(space, t, pos, s[t - o] - pos,
+ ids + off);
+ off += s[t - o] - pos;
+ }
+ }
+ free(space->ids);
+ space->ids = ids;
+ space->n_id = space->nparam + space->n_in + space->n_out + n;
+ }
+ switch (type) {
+ case isl_dim_param: space->nparam += n; break;
+ case isl_dim_in: space->n_in += n; break;
+ case isl_dim_out: space->n_out += n; break;
+ default: ;
+ }
+ space = isl_space_reset(space, type);
+
+ if (type == isl_dim_param) {
+ if (space && space->nested[0] &&
+ !(space->nested[0] = isl_space_insert_dims(space->nested[0],
+ isl_dim_param, pos, n)))
+ goto error;
+ if (space && space->nested[1] &&
+ !(space->nested[1] = isl_space_insert_dims(space->nested[1],
+ isl_dim_param, pos, n)))
+ goto error;
+ }
+
+ return space;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_space *isl_space_move_dims(__isl_take isl_space *space,
+ enum isl_dim_type dst_type, unsigned dst_pos,
+ enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+ int i;
+
+ space = isl_space_reset(space, src_type);
+ space = isl_space_reset(space, dst_type);
+ if (!space)
+ return NULL;
+ if (n == 0)
+ return space;
+
+ if (isl_space_check_range(space, src_type, src_pos, n) < 0)
+ return isl_space_free(space);
+
+ if (dst_type == src_type && dst_pos == src_pos)
+ return space;
+
+ isl_assert(space->ctx, dst_type != src_type, goto error);
+
+ space = isl_space_cow(space);
+ if (!space)
+ return NULL;
+
+ if (space->ids) {
+ isl_id **ids;
+ enum isl_dim_type t, o = isl_dim_param;
+ int off;
+ int s[3];
+ ids = isl_calloc_array(space->ctx, isl_id *,
+ space->nparam + space->n_in + space->n_out);
+ if (!ids)
+ goto error;
+ off = 0;
+ s[isl_dim_param - o] = space->nparam;
+ s[isl_dim_in - o] = space->n_in;
+ s[isl_dim_out - o] = space->n_out;
+ for (t = isl_dim_param; t <= isl_dim_out; ++t) {
+ if (t == dst_type) {
+ get_ids(space, t, 0, dst_pos, ids + off);
+ off += dst_pos;
+ get_ids(space, src_type, src_pos, n, ids + off);
+ off += n;
+ get_ids(space, t, dst_pos, s[t - o] - dst_pos,
+ ids + off);
+ off += s[t - o] - dst_pos;
+ } else if (t == src_type) {
+ get_ids(space, t, 0, src_pos, ids + off);
+ off += src_pos;
+ get_ids(space, t, src_pos + n,
+ s[t - o] - src_pos - n, ids + off);
+ off += s[t - o] - src_pos - n;
+ } else {
+ get_ids(space, t, 0, s[t - o], ids + off);
+ off += s[t - o];
+ }
+ }
+ free(space->ids);
+ space->ids = ids;
+ space->n_id = space->nparam + space->n_in + space->n_out;
+ }
+
+ switch (dst_type) {
+ case isl_dim_param: space->nparam += n; break;
+ case isl_dim_in: space->n_in += n; break;
+ case isl_dim_out: space->n_out += n; break;
+ default: ;
+ }
+
+ switch (src_type) {
+ case isl_dim_param: space->nparam -= n; break;
+ case isl_dim_in: space->n_in -= n; break;
+ case isl_dim_out: space->n_out -= n; break;
+ default: ;
+ }
+
+ if (dst_type != isl_dim_param && src_type != isl_dim_param)
+ return space;
+
+ for (i = 0; i < 2; ++i) {
+ isl_space *nested;
+
+ if (!space->nested[i])
+ continue;
+ nested = isl_space_take_nested(space, i);
+ nested = isl_space_replace_params(nested, space);
+ space = isl_space_restore_nested(space, i, nested);
+ if (!space)
+ return NULL;
+ }
+
+ return space;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Check that "space1" and "space2" have the same parameters,
+ * reporting an error if they do not.
+ */
+isl_stat isl_space_check_equal_params(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ isl_bool equal;
+
+ equal = isl_space_has_equal_params(space1, space2);
+ if (equal < 0)
+ return isl_stat_error;
+ if (!equal)
+ isl_die(isl_space_get_ctx(space1), isl_error_invalid,
+ "parameters need to match", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+__isl_give isl_space *isl_space_join(__isl_take isl_space *left,
+ __isl_take isl_space *right)
+{
+ isl_space *space;
+
+ if (isl_space_check_equal_params(left, right) < 0)
+ goto error;
+
+ isl_assert(left->ctx,
+ isl_space_tuple_is_equal(left, isl_dim_out, right, isl_dim_in),
+ goto error);
+
+ space = isl_space_alloc(left->ctx,
+ left->nparam, left->n_in, right->n_out);
+ if (!space)
+ goto error;
+
+ space = copy_ids(space, isl_dim_param, 0, left, isl_dim_param);
+ space = copy_ids(space, isl_dim_in, 0, left, isl_dim_in);
+ space = copy_ids(space, isl_dim_out, 0, right, isl_dim_out);
+
+ if (space && left->tuple_id[0] &&
+ !(space->tuple_id[0] = isl_id_copy(left->tuple_id[0])))
+ goto error;
+ if (space && right->tuple_id[1] &&
+ !(space->tuple_id[1] = isl_id_copy(right->tuple_id[1])))
+ goto error;
+ if (space && left->nested[0] &&
+ !(space->nested[0] = isl_space_copy(left->nested[0])))
+ goto error;
+ if (space && right->nested[1] &&
+ !(space->nested[1] = isl_space_copy(right->nested[1])))
+ goto error;
+
+ isl_space_free(left);
+ isl_space_free(right);
+
+ return space;
+error:
+ isl_space_free(left);
+ isl_space_free(right);
+ return NULL;
+}
+
+/* Given two map spaces { A -> C } and { B -> D }, construct the space
+ * { [A -> B] -> [C -> D] }.
+ * Given two set spaces { A } and { B }, construct the space { [A -> B] }.
+ */
+__isl_give isl_space *isl_space_product(__isl_take isl_space *left,
+ __isl_take isl_space *right)
+{
+ isl_space *dom1, *dom2, *nest1, *nest2;
+ int is_set;
+
+ if (!left || !right)
+ goto error;
+
+ is_set = isl_space_is_set(left);
+ if (is_set != isl_space_is_set(right))
+ isl_die(isl_space_get_ctx(left), isl_error_invalid,
+ "expecting either two set spaces or two map spaces",
+ goto error);
+ if (is_set)
+ return isl_space_range_product(left, right);
+
+ if (isl_space_check_equal_params(left, right) < 0)
+ goto error;
+
+ dom1 = isl_space_domain(isl_space_copy(left));
+ dom2 = isl_space_domain(isl_space_copy(right));
+ nest1 = isl_space_wrap(isl_space_join(isl_space_reverse(dom1), dom2));
+
+ dom1 = isl_space_range(left);
+ dom2 = isl_space_range(right);
+ nest2 = isl_space_wrap(isl_space_join(isl_space_reverse(dom1), dom2));
+
+ return isl_space_join(isl_space_reverse(nest1), nest2);
+error:
+ isl_space_free(left);
+ isl_space_free(right);
+ return NULL;
+}
+
+/* Given two spaces { A -> C } and { B -> C }, construct the space
+ * { [A -> B] -> C }
+ */
+__isl_give isl_space *isl_space_domain_product(__isl_take isl_space *left,
+ __isl_take isl_space *right)
+{
+ isl_space *ran, *dom1, *dom2, *nest;
+
+ if (isl_space_check_equal_params(left, right) < 0)
+ goto error;
+
+ if (!isl_space_tuple_is_equal(left, isl_dim_out, right, isl_dim_out))
+ isl_die(left->ctx, isl_error_invalid,
+ "ranges need to match", goto error);
+
+ ran = isl_space_range(isl_space_copy(left));
+
+ dom1 = isl_space_domain(left);
+ dom2 = isl_space_domain(right);
+ nest = isl_space_wrap(isl_space_join(isl_space_reverse(dom1), dom2));
+
+ return isl_space_join(isl_space_reverse(nest), ran);
+error:
+ isl_space_free(left);
+ isl_space_free(right);
+ return NULL;
+}
+
+__isl_give isl_space *isl_space_range_product(__isl_take isl_space *left,
+ __isl_take isl_space *right)
+{
+ isl_space *dom, *ran1, *ran2, *nest;
+
+ if (isl_space_check_equal_params(left, right) < 0)
+ goto error;
+
+ if (!isl_space_tuple_is_equal(left, isl_dim_in, right, isl_dim_in))
+ isl_die(left->ctx, isl_error_invalid,
+ "domains need to match", goto error);
+
+ dom = isl_space_domain(isl_space_copy(left));
+
+ ran1 = isl_space_range(left);
+ ran2 = isl_space_range(right);
+ nest = isl_space_wrap(isl_space_join(isl_space_reverse(ran1), ran2));
+
+ return isl_space_join(isl_space_reverse(dom), nest);
+error:
+ isl_space_free(left);
+ isl_space_free(right);
+ return NULL;
+}
+
+/* Given a space of the form [A -> B] -> C, return the space A -> C.
+ */
+__isl_give isl_space *isl_space_domain_factor_domain(
+ __isl_take isl_space *space)
+{
+ isl_space *nested;
+ isl_space *domain;
+
+ if (isl_space_check_domain_is_wrapping(space) < 0)
+ return isl_space_free(space);
+
+ nested = space->nested[0];
+ domain = isl_space_copy(space);
+ domain = isl_space_drop_dims(domain, isl_dim_in,
+ nested->n_in, nested->n_out);
+ if (!domain)
+ return isl_space_free(space);
+ if (nested->tuple_id[0]) {
+ domain->tuple_id[0] = isl_id_copy(nested->tuple_id[0]);
+ if (!domain->tuple_id[0])
+ goto error;
+ }
+ if (nested->nested[0]) {
+ domain->nested[0] = isl_space_copy(nested->nested[0]);
+ if (!domain->nested[0])
+ goto error;
+ }
+
+ isl_space_free(space);
+ return domain;
+error:
+ isl_space_free(space);
+ isl_space_free(domain);
+ return NULL;
+}
+
+/* Given a space of the form [A -> B] -> C, return the space B -> C.
+ */
+__isl_give isl_space *isl_space_domain_factor_range(
+ __isl_take isl_space *space)
+{
+ isl_space *nested;
+ isl_space *range;
+
+ if (isl_space_check_domain_is_wrapping(space) < 0)
+ return isl_space_free(space);
+
+ nested = space->nested[0];
+ range = isl_space_copy(space);
+ range = isl_space_drop_dims(range, isl_dim_in, 0, nested->n_in);
+ if (!range)
+ return isl_space_free(space);
+ if (nested->tuple_id[1]) {
+ range->tuple_id[0] = isl_id_copy(nested->tuple_id[1]);
+ if (!range->tuple_id[0])
+ goto error;
+ }
+ if (nested->nested[1]) {
+ range->nested[0] = isl_space_copy(nested->nested[1]);
+ if (!range->nested[0])
+ goto error;
+ }
+
+ isl_space_free(space);
+ return range;
+error:
+ isl_space_free(space);
+ isl_space_free(range);
+ return NULL;
+}
+
+/* Internal function that selects the domain of the map that is
+ * embedded in either a set space or the range of a map space.
+ * In particular, given a space of the form [A -> B], return the space A.
+ * Given a space of the form A -> [B -> C], return the space A -> B.
+ */
+static __isl_give isl_space *range_factor_domain(__isl_take isl_space *space)
+{
+ isl_space *nested;
+ isl_space *domain;
+
+ if (!space)
+ return NULL;
+
+ nested = space->nested[1];
+ domain = isl_space_copy(space);
+ domain = isl_space_drop_dims(domain, isl_dim_out,
+ nested->n_in, nested->n_out);
+ if (!domain)
+ return isl_space_free(space);
+ if (nested->tuple_id[0]) {
+ domain->tuple_id[1] = isl_id_copy(nested->tuple_id[0]);
+ if (!domain->tuple_id[1])
+ goto error;
+ }
+ if (nested->nested[0]) {
+ domain->nested[1] = isl_space_copy(nested->nested[0]);
+ if (!domain->nested[1])
+ goto error;
+ }
+
+ isl_space_free(space);
+ return domain;
+error:
+ isl_space_free(space);
+ isl_space_free(domain);
+ return NULL;
+}
+
+/* Given a space of the form A -> [B -> C], return the space A -> B.
+ */
+__isl_give isl_space *isl_space_range_factor_domain(
+ __isl_take isl_space *space)
+{
+ if (isl_space_check_range_is_wrapping(space) < 0)
+ return isl_space_free(space);
+
+ return range_factor_domain(space);
+}
+
+/* Given a space of the form [A -> B], return the space A.
+ */
+static __isl_give isl_space *set_factor_domain(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ if (!isl_space_is_wrapping(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "not a product", return isl_space_free(space));
+
+ return range_factor_domain(space);
+}
+
+/* Given a space of the form [A -> B] -> [C -> D], return the space A -> C.
+ * Given a space of the form [A -> B], return the space A.
+ */
+__isl_give isl_space *isl_space_factor_domain(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ if (isl_space_is_set(space))
+ return set_factor_domain(space);
+ space = isl_space_domain_factor_domain(space);
+ space = isl_space_range_factor_domain(space);
+ return space;
+}
+
+/* Internal function that selects the range of the map that is
+ * embedded in either a set space or the range of a map space.
+ * In particular, given a space of the form [A -> B], return the space B.
+ * Given a space of the form A -> [B -> C], return the space A -> C.
+ */
+static __isl_give isl_space *range_factor_range(__isl_take isl_space *space)
+{
+ isl_space *nested;
+ isl_space *range;
+
+ if (!space)
+ return NULL;
+
+ nested = space->nested[1];
+ range = isl_space_copy(space);
+ range = isl_space_drop_dims(range, isl_dim_out, 0, nested->n_in);
+ if (!range)
+ return isl_space_free(space);
+ if (nested->tuple_id[1]) {
+ range->tuple_id[1] = isl_id_copy(nested->tuple_id[1]);
+ if (!range->tuple_id[1])
+ goto error;
+ }
+ if (nested->nested[1]) {
+ range->nested[1] = isl_space_copy(nested->nested[1]);
+ if (!range->nested[1])
+ goto error;
+ }
+
+ isl_space_free(space);
+ return range;
+error:
+ isl_space_free(space);
+ isl_space_free(range);
+ return NULL;
+}
+
+/* Given a space of the form A -> [B -> C], return the space A -> C.
+ */
+__isl_give isl_space *isl_space_range_factor_range(
+ __isl_take isl_space *space)
+{
+ if (isl_space_check_range_is_wrapping(space) < 0)
+ return isl_space_free(space);
+
+ return range_factor_range(space);
+}
+
+/* Given a space of the form [A -> B], return the space B.
+ */
+static __isl_give isl_space *set_factor_range(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ if (!isl_space_is_wrapping(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "not a product", return isl_space_free(space));
+
+ return range_factor_range(space);
+}
+
+/* Given a space of the form [A -> B] -> [C -> D], return the space B -> D.
+ * Given a space of the form [A -> B], return the space B.
+ */
+__isl_give isl_space *isl_space_factor_range(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ if (isl_space_is_set(space))
+ return set_factor_range(space);
+ space = isl_space_domain_factor_range(space);
+ space = isl_space_range_factor_range(space);
+ return space;
+}
+
+__isl_give isl_space *isl_space_map_from_set(__isl_take isl_space *space)
+{
+ isl_ctx *ctx;
+ isl_id **ids = NULL;
+ int n_id;
+
+ if (!space)
+ return NULL;
+ ctx = isl_space_get_ctx(space);
+ if (!isl_space_is_set(space))
+ isl_die(ctx, isl_error_invalid, "not a set space", goto error);
+ space = isl_space_cow(space);
+ if (!space)
+ return NULL;
+ n_id = space->nparam + space->n_out + space->n_out;
+ if (n_id > 0 && space->ids) {
+ ids = isl_calloc_array(space->ctx, isl_id *, n_id);
+ if (!ids)
+ goto error;
+ get_ids(space, isl_dim_param, 0, space->nparam, ids);
+ get_ids(space, isl_dim_out, 0, space->n_out,
+ ids + space->nparam);
+ }
+ space->n_in = space->n_out;
+ if (ids) {
+ free(space->ids);
+ space->ids = ids;
+ space->n_id = n_id;
+ space = copy_ids(space, isl_dim_out, 0, space, isl_dim_in);
+ }
+ isl_id_free(space->tuple_id[0]);
+ space->tuple_id[0] = isl_id_copy(space->tuple_id[1]);
+ isl_space_free(space->nested[0]);
+ space->nested[0] = isl_space_copy(space->nested[1]);
+ return space;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_space *isl_space_map_from_domain_and_range(
+ __isl_take isl_space *domain, __isl_take isl_space *range)
+{
+ if (!domain || !range)
+ goto error;
+ if (!isl_space_is_set(domain))
+ isl_die(isl_space_get_ctx(domain), isl_error_invalid,
+ "domain is not a set space", goto error);
+ if (!isl_space_is_set(range))
+ isl_die(isl_space_get_ctx(range), isl_error_invalid,
+ "range is not a set space", goto error);
+ return isl_space_join(isl_space_reverse(domain), range);
+error:
+ isl_space_free(domain);
+ isl_space_free(range);
+ return NULL;
+}
+
+static __isl_give isl_space *set_ids(__isl_take isl_space *space,
+ enum isl_dim_type type,
+ unsigned first, unsigned n, __isl_take isl_id **ids)
+{
+ int i;
+
+ for (i = 0; i < n ; ++i)
+ space = set_id(space, type, first + i, ids[i]);
+
+ return space;
+}
+
+__isl_give isl_space *isl_space_reverse(__isl_take isl_space *space)
+{
+ unsigned t;
+ isl_bool equal;
+ isl_space *nested;
+ isl_id **ids = NULL;
+ isl_id *id;
+
+ equal = match(space, isl_dim_in, space, isl_dim_out);
+ if (equal < 0)
+ return isl_space_free(space);
+ if (equal)
+ return space;
+
+ space = isl_space_cow(space);
+ if (!space)
+ return NULL;
+
+ id = space->tuple_id[0];
+ space->tuple_id[0] = space->tuple_id[1];
+ space->tuple_id[1] = id;
+
+ nested = space->nested[0];
+ space->nested[0] = space->nested[1];
+ space->nested[1] = nested;
+
+ if (space->ids) {
+ int n_id = space->n_in + space->n_out;
+ ids = isl_alloc_array(space->ctx, isl_id *, n_id);
+ if (n_id && !ids)
+ goto error;
+ get_ids(space, isl_dim_in, 0, space->n_in, ids);
+ get_ids(space, isl_dim_out, 0, space->n_out, ids + space->n_in);
+ }
+
+ t = space->n_in;
+ space->n_in = space->n_out;
+ space->n_out = t;
+
+ if (space->ids) {
+ space = set_ids(space, isl_dim_out, 0, space->n_out, ids);
+ space = set_ids(space, isl_dim_in, 0, space->n_in,
+ ids + space->n_out);
+ free(ids);
+ }
+
+ return space;
+error:
+ free(ids);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Given a space A -> (B -> C), return the corresponding space
+ * A -> (C -> B).
+ *
+ * If the range tuple is named, then the name is only preserved
+ * if B and C are equal tuples, in which case the output
+ * of this function is identical to the input.
+ */
+__isl_give isl_space *isl_space_range_reverse(__isl_take isl_space *space)
+{
+ isl_space *nested;
+ isl_bool equal;
+
+ if (isl_space_check_range_is_wrapping(space) < 0)
+ return isl_space_free(space);
+
+ nested = isl_space_peek_nested(space, 1);
+ equal = isl_space_tuple_is_equal(nested, isl_dim_in,
+ nested, isl_dim_out);
+ if (equal < 0)
+ return isl_space_free(space);
+
+ nested = isl_space_take_nested(space, 1);
+ nested = isl_space_reverse(nested);
+ space = isl_space_restore_nested(space, 1, nested);
+ if (!equal)
+ space = isl_space_reset_tuple_id(space, isl_dim_out);
+
+ return space;
+}
+
+__isl_give isl_space *isl_space_drop_dims(__isl_take isl_space *space,
+ enum isl_dim_type type, unsigned first, unsigned num)
+{
+ int i;
+
+ if (!space)
+ return NULL;
+
+ if (num == 0)
+ return isl_space_reset(space, type);
+
+ if (!valid_dim_type(type))
+ isl_die(space->ctx, isl_error_invalid,
+ "cannot drop dimensions of specified type", goto error);
+
+ if (isl_space_check_range(space, type, first, num) < 0)
+ return isl_space_free(space);
+ space = isl_space_cow(space);
+ if (!space)
+ goto error;
+ if (space->ids) {
+ space = extend_ids(space);
+ if (!space)
+ goto error;
+ for (i = 0; i < num; ++i)
+ isl_id_free(get_id(space, type, first + i));
+ for (i = first+num; i < n(space, type); ++i)
+ set_id(space, type, i - num, get_id(space, type, i));
+ switch (type) {
+ case isl_dim_param:
+ get_ids(space, isl_dim_in, 0, space->n_in,
+ space->ids + offset(space, isl_dim_in) - num);
+ case isl_dim_in:
+ get_ids(space, isl_dim_out, 0, space->n_out,
+ space->ids + offset(space, isl_dim_out) - num);
+ default:
+ ;
+ }
+ space->n_id -= num;
+ }
+ switch (type) {
+ case isl_dim_param: space->nparam -= num; break;
+ case isl_dim_in: space->n_in -= num; break;
+ case isl_dim_out: space->n_out -= num; break;
+ default: ;
+ }
+ space = isl_space_reset(space, type);
+ if (type == isl_dim_param) {
+ if (space && space->nested[0] &&
+ !(space->nested[0] = isl_space_drop_dims(space->nested[0],
+ isl_dim_param, first, num)))
+ goto error;
+ if (space && space->nested[1] &&
+ !(space->nested[1] = isl_space_drop_dims(space->nested[1],
+ isl_dim_param, first, num)))
+ goto error;
+ }
+ return space;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_space *isl_space_drop_inputs(__isl_take isl_space *space,
+ unsigned first, unsigned n)
+{
+ if (!space)
+ return NULL;
+ return isl_space_drop_dims(space, isl_dim_in, first, n);
+}
+
+__isl_give isl_space *isl_space_drop_outputs(__isl_take isl_space *space,
+ unsigned first, unsigned n)
+{
+ if (!space)
+ return NULL;
+ return isl_space_drop_dims(space, isl_dim_out, first, n);
+}
+
+/* Remove all parameters from "space".
+ */
+__isl_give isl_space *isl_space_drop_all_params(__isl_take isl_space *space)
+{
+ isl_size nparam;
+
+ nparam = isl_space_dim(space, isl_dim_param);
+ if (nparam < 0)
+ return isl_space_free(space);
+ return isl_space_drop_dims(space, isl_dim_param, 0, nparam);
+}
+
+__isl_give isl_space *isl_space_domain(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ space = isl_space_drop_dims(space, isl_dim_out, 0, space->n_out);
+ space = isl_space_reverse(space);
+ space = mark_as_set(space);
+ return space;
+}
+
+__isl_give isl_space *isl_space_from_domain(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ if (!isl_space_is_set(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "not a set space", goto error);
+ space = isl_space_reverse(space);
+ space = isl_space_reset(space, isl_dim_out);
+ return space;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_space *isl_space_range(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ space = isl_space_drop_dims(space, isl_dim_in, 0, space->n_in);
+ space = mark_as_set(space);
+ return space;
+}
+
+__isl_give isl_space *isl_space_from_range(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ if (!isl_space_is_set(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "not a set space", goto error);
+ return isl_space_reset(space, isl_dim_in);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Given a map space A -> B, return the map space [A -> B] -> A.
+ */
+__isl_give isl_space *isl_space_domain_map(__isl_take isl_space *space)
+{
+ isl_space *domain;
+
+ domain = isl_space_from_range(isl_space_domain(isl_space_copy(space)));
+ space = isl_space_from_domain(isl_space_wrap(space));
+ space = isl_space_join(space, domain);
+
+ return space;
+}
+
+/* Given a map space A -> B, return the map space [A -> B] -> B.
+ */
+__isl_give isl_space *isl_space_range_map(__isl_take isl_space *space)
+{
+ isl_space *range;
+
+ range = isl_space_from_range(isl_space_range(isl_space_copy(space)));
+ space = isl_space_from_domain(isl_space_wrap(space));
+ space = isl_space_join(space, range);
+
+ return space;
+}
+
+__isl_give isl_space *isl_space_params(__isl_take isl_space *space)
+{
+ isl_size n_in, n_out;
+
+ if (isl_space_is_params(space))
+ return space;
+ n_in = isl_space_dim(space, isl_dim_in);
+ n_out = isl_space_dim(space, isl_dim_out);
+ if (n_in < 0 || n_out < 0)
+ return isl_space_free(space);
+ space = isl_space_drop_dims(space, isl_dim_in, 0, n_in);
+ space = isl_space_drop_dims(space, isl_dim_out, 0, n_out);
+ space = mark_as_params(space);
+ return space;
+}
+
+__isl_give isl_space *isl_space_set_from_params(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ if (!isl_space_is_params(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "not a parameter space", goto error);
+ return isl_space_reset(space, isl_dim_set);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Add an unnamed tuple of dimension "dim" to "space".
+ * This requires "space" to be a parameter or set space.
+ *
+ * In particular, if "space" is a parameter space, then return
+ * a set space with the given dimension.
+ * If "space" is a set space, then return a map space
+ * with "space" as domain and a range of the given dimension.
+ */
+__isl_give isl_space *isl_space_add_unnamed_tuple_ui(
+ __isl_take isl_space *space, unsigned dim)
+{
+ isl_bool is_params, is_set;
+
+ is_params = isl_space_is_params(space);
+ is_set = isl_space_is_set(space);
+ if (is_params < 0 || is_set < 0)
+ return isl_space_free(space);
+ if (!is_params && !is_set)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "cannot add tuple to map space",
+ return isl_space_free(space));
+ if (is_params)
+ space = isl_space_set_from_params(space);
+ else
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, dim);
+ return space;
+}
+
+/* Add a tuple of dimension "dim" and with tuple identifier "tuple_id"
+ * to "space".
+ * This requires "space" to be a parameter or set space.
+ */
+__isl_give isl_space *isl_space_add_named_tuple_id_ui(
+ __isl_take isl_space *space, __isl_take isl_id *tuple_id, unsigned dim)
+{
+ space = isl_space_add_unnamed_tuple_ui(space, dim);
+ space = isl_space_set_tuple_id(space, isl_dim_out, tuple_id);
+ return space;
+}
+
+/* Check that the identifiers in "tuple" do not appear as parameters
+ * in "space".
+ */
+static isl_stat check_fresh_params(__isl_keep isl_space *space,
+ __isl_keep isl_multi_id *tuple)
+{
+ int i;
+ isl_size n;
+
+ n = isl_multi_id_size(tuple);
+ if (n < 0)
+ return isl_stat_error;
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+ int pos;
+
+ id = isl_multi_id_get_at(tuple, i);
+ if (!id)
+ return isl_stat_error;
+ pos = isl_space_find_dim_by_id(space, isl_dim_param, id);
+ isl_id_free(id);
+ if (pos >= 0)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "parameters not unique", return isl_stat_error);
+ }
+
+ return isl_stat_ok;
+}
+
+/* Add the identifiers in "tuple" as parameters of "space"
+ * that are known to be fresh.
+ */
+static __isl_give isl_space *add_bind_params(__isl_take isl_space *space,
+ __isl_keep isl_multi_id *tuple)
+{
+ int i;
+ isl_size first, n;
+
+ first = isl_space_dim(space, isl_dim_param);
+ n = isl_multi_id_size(tuple);
+ if (first < 0 || n < 0)
+ return isl_space_free(space);
+ space = isl_space_add_dims(space, isl_dim_param, n);
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+
+ id = isl_multi_id_get_at(tuple, i);
+ space = isl_space_set_dim_id(space,
+ isl_dim_param, first + i, id);
+ }
+
+ return space;
+}
+
+/* Internal function that removes the set tuple of "space",
+ * which is assumed to correspond to the range space of "tuple", and
+ * adds the identifiers in "tuple" as fresh parameters.
+ * In other words, the set dimensions of "space" are reinterpreted
+ * as parameters, but stay in the same global positions.
+ */
+__isl_give isl_space *isl_space_bind_set(__isl_take isl_space *space,
+ __isl_keep isl_multi_id *tuple)
+{
+ isl_space *tuple_space;
+
+ if (isl_space_check_is_set(space) < 0)
+ return isl_space_free(space);
+ tuple_space = isl_multi_id_peek_space(tuple);
+ if (isl_space_check_equal_tuples(tuple_space, space) < 0)
+ return isl_space_free(space);
+ if (check_fresh_params(space, tuple) < 0)
+ return isl_space_free(space);
+ space = isl_space_params(space);
+ space = add_bind_params(space, tuple);
+ return space;
+}
+
+/* Internal function that removes the domain tuple of the map space "space",
+ * which is assumed to correspond to the range space of "tuple", and
+ * adds the identifiers in "tuple" as fresh parameters.
+ * In other words, the domain dimensions of "space" are reinterpreted
+ * as parameters, but stay in the same global positions.
+ */
+__isl_give isl_space *isl_space_bind_map_domain(__isl_take isl_space *space,
+ __isl_keep isl_multi_id *tuple)
+{
+ isl_space *tuple_space;
+
+ if (isl_space_check_is_map(space) < 0)
+ return isl_space_free(space);
+ tuple_space = isl_multi_id_peek_space(tuple);
+ if (isl_space_check_domain_tuples(tuple_space, space) < 0)
+ return isl_space_free(space);
+ if (check_fresh_params(space, tuple) < 0)
+ return isl_space_free(space);
+ space = isl_space_range(space);
+ space = add_bind_params(space, tuple);
+ return space;
+}
+
+/* Internal function that, given a space of the form [A -> B] -> C and
+ * a tuple of identifiers in A, returns a space B -> C with
+ * the identifiers in "tuple" added as fresh parameters.
+ * In other words, the domain dimensions of the wrapped relation
+ * in the domain of "space" are reinterpreted
+ * as parameters, but stay in the same global positions.
+ */
+__isl_give isl_space *isl_space_bind_domain_wrapped_domain(
+ __isl_take isl_space *space, __isl_keep isl_multi_id *tuple)
+{
+ isl_space *tuple_space;
+
+ if (isl_space_check_is_map(space) < 0)
+ return isl_space_free(space);
+ tuple_space = isl_multi_id_peek_space(tuple);
+ if (isl_space_check_domain_wrapped_domain_tuples(tuple_space,
+ space) < 0)
+ return isl_space_free(space);
+ if (check_fresh_params(space, tuple) < 0)
+ return isl_space_free(space);
+ space = isl_space_domain_factor_range(space);
+ space = add_bind_params(space, tuple);
+ return space;
+}
+
+/* Insert a domain tuple in "space" corresponding to the set space "domain".
+ * In particular, if "space" is a parameter space, then the result
+ * is the set space "domain" combined with the parameters of "space".
+ * If "space" is a set space, then the result
+ * is a map space with "domain" as domain and the original space as range.
+ */
+static __isl_give isl_space *isl_space_insert_domain(
+ __isl_take isl_space *space, __isl_take isl_space *domain)
+{
+ isl_bool is_params;
+
+ domain = isl_space_replace_params(domain, space);
+
+ is_params = isl_space_is_params(space);
+ if (is_params < 0) {
+ isl_space_free(domain);
+ space = isl_space_free(space);
+ } else if (is_params) {
+ isl_space_free(space);
+ space = domain;
+ } else {
+ space = isl_space_map_from_domain_and_range(domain, space);
+ }
+ return space;
+}
+
+/* Internal function that introduces a domain in "space"
+ * corresponding to the range space of "tuple".
+ * In particular, if "space" is a parameter space, then the result
+ * is a set space. If "space" is a set space, then the result
+ * is a map space with the original space as range.
+ * Parameters that correspond to the identifiers in "tuple" are removed.
+ *
+ * The parameters are removed in reverse order (under the assumption
+ * that they appear in the same order in "multi") because
+ * it is slightly more efficient to remove parameters at the end.
+ *
+ * For pretty-printing purposes, the identifiers of the set dimensions
+ * of the introduced domain are set to the identifiers in "tuple".
+ */
+__isl_give isl_space *isl_space_unbind_params_insert_domain(
+ __isl_take isl_space *space, __isl_keep isl_multi_id *tuple)
+{
+ int i;
+ isl_size n;
+ isl_space *tuple_space;
+
+ n = isl_multi_id_size(tuple);
+ if (!space || n < 0)
+ return isl_space_free(space);
+ for (i = n - 1; i >= 0; --i) {
+ isl_id *id;
+ int pos;
+
+ id = isl_multi_id_get_id(tuple, i);
+ if (!id)
+ return isl_space_free(space);
+ pos = isl_space_find_dim_by_id(space, isl_dim_param, id);
+ isl_id_free(id);
+ if (pos < 0)
+ continue;
+ space = isl_space_drop_dims(space, isl_dim_param, pos, 1);
+ }
+ tuple_space = isl_multi_id_get_space(tuple);
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+
+ id = isl_multi_id_get_id(tuple, i);
+ tuple_space = isl_space_set_dim_id(tuple_space,
+ isl_dim_set, i, id);
+ }
+ return isl_space_insert_domain(space, tuple_space);
+}
+
+__isl_give isl_space *isl_space_underlying(__isl_take isl_space *space,
+ unsigned n_div)
+{
+ int i;
+ isl_bool is_set;
+
+ is_set = isl_space_is_set(space);
+ if (is_set < 0)
+ return isl_space_free(space);
+ if (n_div == 0 && is_set &&
+ space->nparam == 0 && space->n_in == 0 && space->n_id == 0)
+ return isl_space_reset(space, isl_dim_out);
+ space = isl_space_cow(space);
+ if (!space)
+ return NULL;
+ space->n_out += space->nparam + space->n_in + n_div;
+ space->nparam = 0;
+ space->n_in = 0;
+
+ for (i = 0; i < space->n_id; ++i)
+ isl_id_free(get_id(space, isl_dim_out, i));
+ space->n_id = 0;
+ space = isl_space_reset(space, isl_dim_in);
+ space = isl_space_reset(space, isl_dim_out);
+ space = mark_as_set(space);
+
+ return space;
+}
+
+/* Are the two spaces the same, including positions and names of parameters?
+ */
+isl_bool isl_space_is_equal(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ isl_bool equal;
+
+ if (!space1 || !space2)
+ return isl_bool_error;
+ if (space1 == space2)
+ return isl_bool_true;
+ equal = isl_space_has_equal_params(space1, space2);
+ if (equal < 0 || !equal)
+ return equal;
+ return isl_space_has_equal_tuples(space1, space2);
+}
+
+/* Do the tuples of "space1" correspond to those of the domain of "space2"?
+ * That is, is "space1" equal to the domain of "space2", ignoring parameters.
+ *
+ * "space2" is allowed to be a set space, in which case "space1"
+ * should be a parameter space.
+ */
+isl_bool isl_space_has_domain_tuples(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ isl_bool is_set;
+
+ is_set = isl_space_is_set(space1);
+ if (is_set < 0 || !is_set)
+ return is_set;
+ return isl_space_tuple_is_equal(space1, isl_dim_set,
+ space2, isl_dim_in);
+}
+
+/* Do the tuples of "space1" correspond to those of the range of "space2"?
+ * That is, is "space1" equal to the range of "space2", ignoring parameters.
+ *
+ * "space2" is allowed to be the space of a set,
+ * in which case it should be equal to "space1", ignoring parameters.
+ */
+isl_bool isl_space_has_range_tuples(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ isl_bool is_set;
+
+ is_set = isl_space_is_set(space1);
+ if (is_set < 0 || !is_set)
+ return is_set;
+ return isl_space_tuple_is_equal(space1, isl_dim_set,
+ space2, isl_dim_out);
+}
+
+/* Check that the tuples of "space1" correspond to those
+ * of the domain of "space2".
+ * That is, check that "space1" is equal to the domain of "space2",
+ * ignoring parameters.
+ */
+isl_stat isl_space_check_domain_tuples(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ isl_bool is_equal;
+
+ is_equal = isl_space_has_domain_tuples(space1, space2);
+ return check_match(space1, is_equal);
+}
+
+/* Check that the tuples of "space1" correspond to those
+ * of the domain of the wrapped relation in the domain of "space2".
+ * That is, check that "space1" is equal to this domain,
+ * ignoring parameters.
+ */
+isl_stat isl_space_check_domain_wrapped_domain_tuples(
+ __isl_keep isl_space *space1, __isl_keep isl_space *space2)
+{
+ isl_space *domain;
+ isl_stat r;
+
+ domain = isl_space_unwrap(isl_space_domain(isl_space_copy(space2)));
+ r = isl_space_check_domain_tuples(space1, domain);
+ isl_space_free(domain);
+
+ return r;
+}
+
+/* Is space1 equal to the domain of space2?
+ *
+ * In the internal version we also allow space2 to be the space of a set,
+ * provided space1 is a parameter space.
+ */
+isl_bool isl_space_is_domain_internal(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ isl_bool equal_params;
+
+ if (!space1 || !space2)
+ return isl_bool_error;
+ equal_params = isl_space_has_equal_params(space1, space2);
+ if (equal_params < 0 || !equal_params)
+ return equal_params;
+ return isl_space_has_domain_tuples(space1, space2);
+}
+
+/* Is space1 equal to the domain of space2?
+ */
+isl_bool isl_space_is_domain(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ if (!space2)
+ return isl_bool_error;
+ if (!isl_space_is_map(space2))
+ return isl_bool_false;
+ return isl_space_is_domain_internal(space1, space2);
+}
+
+/* Is space1 equal to the range of space2?
+ *
+ * In the internal version, space2 is allowed to be the space of a set,
+ * in which case it should be equal to space1.
+ */
+isl_bool isl_space_is_range_internal(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ isl_bool equal_params;
+
+ if (!space1 || !space2)
+ return isl_bool_error;
+ equal_params = isl_space_has_equal_params(space1, space2);
+ if (equal_params < 0 || !equal_params)
+ return equal_params;
+ return isl_space_has_range_tuples(space1, space2);
+}
+
+/* Is space1 equal to the range of space2?
+ */
+isl_bool isl_space_is_range(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2)
+{
+ if (!space2)
+ return isl_bool_error;
+ if (!isl_space_is_map(space2))
+ return isl_bool_false;
+ return isl_space_is_range_internal(space1, space2);
+}
+
+/* Update "hash" by hashing in the parameters of "space".
+ */
+static uint32_t isl_hash_params(uint32_t hash, __isl_keep isl_space *space)
+{
+ int i;
+ isl_id *id;
+
+ if (!space)
+ return hash;
+
+ isl_hash_byte(hash, space->nparam % 256);
+
+ for (i = 0; i < space->nparam; ++i) {
+ id = get_id(space, isl_dim_param, i);
+ hash = isl_hash_id(hash, id);
+ }
+
+ return hash;
+}
+
+/* Update "hash" by hashing in the tuples of "space".
+ * Changes in this function should be reflected in isl_hash_tuples_domain.
+ */
+static uint32_t isl_hash_tuples(uint32_t hash, __isl_keep isl_space *space)
+{
+ isl_id *id;
+
+ if (!space)
+ return hash;
+
+ isl_hash_byte(hash, space->n_in % 256);
+ isl_hash_byte(hash, space->n_out % 256);
+
+ id = tuple_id(space, isl_dim_in);
+ hash = isl_hash_id(hash, id);
+ id = tuple_id(space, isl_dim_out);
+ hash = isl_hash_id(hash, id);
+
+ hash = isl_hash_tuples(hash, space->nested[0]);
+ hash = isl_hash_tuples(hash, space->nested[1]);
+
+ return hash;
+}
+
+/* Update "hash" by hashing in the domain tuple of "space".
+ * The result of this function is equal to the result of applying
+ * isl_hash_tuples to the domain of "space".
+ */
+static uint32_t isl_hash_tuples_domain(uint32_t hash,
+ __isl_keep isl_space *space)
+{
+ isl_id *id;
+
+ if (!space)
+ return hash;
+
+ isl_hash_byte(hash, 0);
+ isl_hash_byte(hash, space->n_in % 256);
+
+ hash = isl_hash_id(hash, &isl_id_none);
+ id = tuple_id(space, isl_dim_in);
+ hash = isl_hash_id(hash, id);
+
+ hash = isl_hash_tuples(hash, space->nested[0]);
+
+ return hash;
+}
+
+/* Return a hash value that digests the tuples of "space",
+ * i.e., that ignores the parameters.
+ * Changes in this function should be reflected
+ * in isl_space_get_tuple_domain_hash.
+ */
+uint32_t isl_space_get_tuple_hash(__isl_keep isl_space *space)
+{
+ uint32_t hash;
+
+ if (!space)
+ return 0;
+
+ hash = isl_hash_init();
+ hash = isl_hash_tuples(hash, space);
+
+ return hash;
+}
+
+/* Return the hash value of "space".
+ */
+uint32_t isl_space_get_full_hash(__isl_keep isl_space *space)
+{
+ uint32_t hash;
+
+ if (!space)
+ return 0;
+
+ hash = isl_hash_init();
+ hash = isl_hash_params(hash, space);
+ hash = isl_hash_tuples(hash, space);
+
+ return hash;
+}
+
+/* Return the hash value of the domain tuple of "space".
+ * That is, isl_space_get_tuple_domain_hash(space) is equal to
+ * isl_space_get_tuple_hash(isl_space_domain(space)).
+ */
+uint32_t isl_space_get_tuple_domain_hash(__isl_keep isl_space *space)
+{
+ uint32_t hash;
+
+ if (!space)
+ return 0;
+
+ hash = isl_hash_init();
+ hash = isl_hash_tuples_domain(hash, space);
+
+ return hash;
+}
+
+/* Is "space" the space of a set wrapping a map space?
+ */
+isl_bool isl_space_is_wrapping(__isl_keep isl_space *space)
+{
+ if (!space)
+ return isl_bool_error;
+
+ if (!isl_space_is_set(space))
+ return isl_bool_false;
+
+ return isl_bool_ok(space->nested[1] != NULL);
+}
+
+/* Is "space" the space of a map where the domain is a wrapped map space?
+ */
+isl_bool isl_space_domain_is_wrapping(__isl_keep isl_space *space)
+{
+ if (!space)
+ return isl_bool_error;
+
+ if (isl_space_is_set(space))
+ return isl_bool_false;
+
+ return isl_bool_ok(space->nested[0] != NULL);
+}
+
+/* Is "space" the space of a map where the range is a wrapped map space?
+ */
+isl_bool isl_space_range_is_wrapping(__isl_keep isl_space *space)
+{
+ if (!space)
+ return isl_bool_error;
+
+ if (isl_space_is_set(space))
+ return isl_bool_false;
+
+ return isl_bool_ok(space->nested[1] != NULL);
+}
+
+/* Is "space" a product of two spaces?
+ * That is, is it a wrapping set space or a map space
+ * with wrapping domain and range?
+ */
+isl_bool isl_space_is_product(__isl_keep isl_space *space)
+{
+ isl_bool is_set;
+ isl_bool is_product;
+
+ is_set = isl_space_is_set(space);
+ if (is_set < 0)
+ return isl_bool_error;
+ if (is_set)
+ return isl_space_is_wrapping(space);
+ is_product = isl_space_domain_is_wrapping(space);
+ if (is_product < 0 || !is_product)
+ return is_product;
+ return isl_space_range_is_wrapping(space);
+}
+
+__isl_give isl_space *isl_space_wrap(__isl_take isl_space *space)
+{
+ isl_space *wrap;
+
+ if (!space)
+ return NULL;
+
+ wrap = isl_space_set_alloc(space->ctx,
+ space->nparam, space->n_in + space->n_out);
+
+ wrap = copy_ids(wrap, isl_dim_param, 0, space, isl_dim_param);
+ wrap = copy_ids(wrap, isl_dim_set, 0, space, isl_dim_in);
+ wrap = copy_ids(wrap, isl_dim_set, space->n_in, space, isl_dim_out);
+
+ if (!wrap)
+ goto error;
+
+ wrap->nested[1] = space;
+
+ return wrap;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_space *isl_space_unwrap(__isl_take isl_space *space)
+{
+ isl_space *unwrap;
+
+ if (!space)
+ return NULL;
+
+ if (!isl_space_is_wrapping(space))
+ isl_die(space->ctx, isl_error_invalid, "not a wrapping space",
+ goto error);
+
+ unwrap = isl_space_copy(space->nested[1]);
+ isl_space_free(space);
+
+ return unwrap;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+isl_bool isl_space_is_named_or_nested(__isl_keep isl_space *space,
+ enum isl_dim_type type)
+{
+ if (type != isl_dim_in && type != isl_dim_out)
+ return isl_bool_false;
+ if (!space)
+ return isl_bool_error;
+ if (space->tuple_id[type - isl_dim_in])
+ return isl_bool_true;
+ if (space->nested[type - isl_dim_in])
+ return isl_bool_true;
+ return isl_bool_false;
+}
+
+isl_bool isl_space_may_be_set(__isl_keep isl_space *space)
+{
+ isl_bool nested;
+ isl_size n_in;
+
+ if (!space)
+ return isl_bool_error;
+ if (isl_space_is_set(space))
+ return isl_bool_true;
+ n_in = isl_space_dim(space, isl_dim_in);
+ if (n_in < 0)
+ return isl_bool_error;
+ if (n_in != 0)
+ return isl_bool_false;
+ nested = isl_space_is_named_or_nested(space, isl_dim_in);
+ if (nested < 0 || nested)
+ return isl_bool_not(nested);
+ return isl_bool_true;
+}
+
+__isl_give isl_space *isl_space_reset(__isl_take isl_space *space,
+ enum isl_dim_type type)
+{
+ if (!isl_space_is_named_or_nested(space, type))
+ return space;
+
+ space = isl_space_cow(space);
+ if (!space)
+ return NULL;
+
+ isl_id_free(space->tuple_id[type - isl_dim_in]);
+ space->tuple_id[type - isl_dim_in] = NULL;
+ isl_space_free(space->nested[type - isl_dim_in]);
+ space->nested[type - isl_dim_in] = NULL;
+
+ return space;
+}
+
+__isl_give isl_space *isl_space_flatten(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ if (!space->nested[0] && !space->nested[1])
+ return space;
+
+ if (space->nested[0])
+ space = isl_space_reset(space, isl_dim_in);
+ if (space && space->nested[1])
+ space = isl_space_reset(space, isl_dim_out);
+
+ return space;
+}
+
+__isl_give isl_space *isl_space_flatten_domain(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ if (!space->nested[0])
+ return space;
+
+ return isl_space_reset(space, isl_dim_in);
+}
+
+__isl_give isl_space *isl_space_flatten_range(__isl_take isl_space *space)
+{
+ if (!space)
+ return NULL;
+ if (!space->nested[1])
+ return space;
+
+ return isl_space_reset(space, isl_dim_out);
+}
+
+/* Replace the parameters of dst by those of src.
+ */
+__isl_give isl_space *isl_space_replace_params(__isl_take isl_space *dst,
+ __isl_keep isl_space *src)
+{
+ isl_size dst_dim, src_dim;
+ isl_bool equal_params;
+ enum isl_dim_type type = isl_dim_param;
+
+ equal_params = isl_space_has_equal_params(dst, src);
+ if (equal_params < 0)
+ return isl_space_free(dst);
+ if (equal_params)
+ return dst;
+
+ dst = isl_space_cow(dst);
+
+ dst_dim = isl_space_dim(dst, type);
+ src_dim = isl_space_dim(src, type);
+ if (dst_dim < 0 || src_dim < 0)
+ goto error;
+
+ dst = isl_space_drop_dims(dst, type, 0, dst_dim);
+ dst = isl_space_add_dims(dst, type, src_dim);
+ dst = copy_ids(dst, type, 0, src, type);
+
+ if (dst) {
+ int i;
+ for (i = 0; i <= 1; ++i) {
+ isl_space *nested;
+
+ if (!dst->nested[i])
+ continue;
+ nested = isl_space_take_nested(dst, i);
+ nested = isl_space_replace_params(nested, src);
+ dst = isl_space_restore_nested(dst, i, nested);
+ if (!dst)
+ return NULL;
+ }
+ }
+
+ return dst;
+error:
+ isl_space_free(dst);
+ return NULL;
+}
+
+/* Given two tuples ("dst_type" in "dst" and "src_type" in "src")
+ * of the same size, check if any of the dimensions in the "dst" tuple
+ * have no identifier, while the corresponding dimensions in "src"
+ * does have an identifier,
+ * If so, copy the identifier over to "dst".
+ */
+__isl_give isl_space *isl_space_copy_ids_if_unset(__isl_take isl_space *dst,
+ enum isl_dim_type dst_type, __isl_keep isl_space *src,
+ enum isl_dim_type src_type)
+{
+ int i;
+ isl_size n;
+
+ n = isl_space_dim(dst, dst_type);
+ if (n < 0)
+ return isl_space_free(dst);
+ for (i = 0; i < n; ++i) {
+ isl_bool set;
+ isl_id *id;
+
+ set = isl_space_has_dim_id(dst, dst_type, i);
+ if (set < 0)
+ return isl_space_free(dst);
+ if (set)
+ continue;
+
+ set = isl_space_has_dim_id(src, src_type, i);
+ if (set < 0)
+ return isl_space_free(dst);
+ if (!set)
+ continue;
+
+ id = isl_space_get_dim_id(src, src_type, i);
+ dst = isl_space_set_dim_id(dst, dst_type, i, id);
+ }
+
+ return dst;
+}
+
+/* Given a space "space" of a set, create a space
+ * for the lift of the set. In particular, the result
+ * is of the form lifted[space -> local[..]], with n_local variables in the
+ * range of the wrapped map.
+ */
+__isl_give isl_space *isl_space_lift(__isl_take isl_space *space,
+ unsigned n_local)
+{
+ isl_space *local_space;
+
+ if (!space)
+ return NULL;
+
+ local_space = isl_space_dup(space);
+ local_space = isl_space_drop_dims(local_space, isl_dim_set, 0,
+ space->n_out);
+ local_space = isl_space_add_dims(local_space, isl_dim_set, n_local);
+ local_space = isl_space_set_tuple_name(local_space,
+ isl_dim_set, "local");
+ space = isl_space_join(isl_space_from_domain(space),
+ isl_space_from_range(local_space));
+ space = isl_space_wrap(space);
+ space = isl_space_set_tuple_name(space, isl_dim_set, "lifted");
+
+ return space;
+}
+
+isl_bool isl_space_can_zip(__isl_keep isl_space *space)
+{
+ isl_bool is_set;
+
+ is_set = isl_space_is_set(space);
+ if (is_set < 0)
+ return isl_bool_error;
+ if (is_set)
+ return isl_bool_false;
+ return isl_space_is_product(space);
+}
+
+__isl_give isl_space *isl_space_zip(__isl_take isl_space *space)
+{
+ isl_space *dom, *ran;
+ isl_space *dom_dom, *dom_ran, *ran_dom, *ran_ran;
+
+ if (!isl_space_can_zip(space))
+ isl_die(space->ctx, isl_error_invalid, "space cannot be zipped",
+ goto error);
+
+ if (!space)
+ return NULL;
+ dom = isl_space_unwrap(isl_space_domain(isl_space_copy(space)));
+ ran = isl_space_unwrap(isl_space_range(space));
+ dom_dom = isl_space_domain(isl_space_copy(dom));
+ dom_ran = isl_space_range(dom);
+ ran_dom = isl_space_domain(isl_space_copy(ran));
+ ran_ran = isl_space_range(ran);
+ dom = isl_space_join(isl_space_from_domain(dom_dom),
+ isl_space_from_range(ran_dom));
+ ran = isl_space_join(isl_space_from_domain(dom_ran),
+ isl_space_from_range(ran_ran));
+ return isl_space_join(isl_space_from_domain(isl_space_wrap(dom)),
+ isl_space_from_range(isl_space_wrap(ran)));
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Can we apply isl_space_curry to "space"?
+ * That is, does is it have a map space with a nested relation in its domain?
+ */
+isl_bool isl_space_can_curry(__isl_keep isl_space *space)
+{
+ return isl_space_domain_is_wrapping(space);
+}
+
+/* Given a space (A -> B) -> C, return the corresponding space
+ * A -> (B -> C).
+ */
+__isl_give isl_space *isl_space_curry(__isl_take isl_space *space)
+{
+ isl_space *dom, *ran;
+ isl_space *dom_dom, *dom_ran;
+
+ if (!space)
+ return NULL;
+
+ if (!isl_space_can_curry(space))
+ isl_die(space->ctx, isl_error_invalid,
+ "space cannot be curried", goto error);
+
+ dom = isl_space_unwrap(isl_space_domain(isl_space_copy(space)));
+ ran = isl_space_range(space);
+ dom_dom = isl_space_domain(isl_space_copy(dom));
+ dom_ran = isl_space_range(dom);
+ ran = isl_space_join(isl_space_from_domain(dom_ran),
+ isl_space_from_range(ran));
+ return isl_space_join(isl_space_from_domain(dom_dom),
+ isl_space_from_range(isl_space_wrap(ran)));
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Can isl_space_range_curry be applied to "space"?
+ * That is, does it have a nested relation in its range,
+ * the domain of which is itself a nested relation?
+ */
+isl_bool isl_space_can_range_curry(__isl_keep isl_space *space)
+{
+ isl_bool can;
+
+ if (!space)
+ return isl_bool_error;
+ can = isl_space_range_is_wrapping(space);
+ if (can < 0 || !can)
+ return can;
+ return isl_space_can_curry(space->nested[1]);
+}
+
+/* Given a space A -> ((B -> C) -> D), return the corresponding space
+ * A -> (B -> (C -> D)).
+ */
+__isl_give isl_space *isl_space_range_curry(__isl_take isl_space *space)
+{
+ isl_space *nested;
+
+ if (!space)
+ return NULL;
+
+ if (!isl_space_can_range_curry(space))
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "space range cannot be curried",
+ return isl_space_free(space));
+
+ nested = isl_space_take_nested(space, 1);
+ nested = isl_space_curry(nested);
+ space = isl_space_restore_nested(space, 1, nested);
+
+ return space;
+}
+
+/* Can we apply isl_space_uncurry to "space"?
+ * That is, does it have a map space with a nested relation in its range?
+ */
+isl_bool isl_space_can_uncurry(__isl_keep isl_space *space)
+{
+ return isl_space_range_is_wrapping(space);
+}
+
+/* Given a space A -> (B -> C), return the corresponding space
+ * (A -> B) -> C.
+ */
+__isl_give isl_space *isl_space_uncurry(__isl_take isl_space *space)
+{
+ isl_space *dom, *ran;
+ isl_space *ran_dom, *ran_ran;
+
+ if (!space)
+ return NULL;
+
+ if (!isl_space_can_uncurry(space))
+ isl_die(space->ctx, isl_error_invalid,
+ "space cannot be uncurried",
+ return isl_space_free(space));
+
+ dom = isl_space_domain(isl_space_copy(space));
+ ran = isl_space_unwrap(isl_space_range(space));
+ ran_dom = isl_space_domain(isl_space_copy(ran));
+ ran_ran = isl_space_range(ran);
+ dom = isl_space_join(isl_space_from_domain(dom),
+ isl_space_from_range(ran_dom));
+ return isl_space_join(isl_space_from_domain(isl_space_wrap(dom)),
+ isl_space_from_range(ran_ran));
+}
+
+isl_bool isl_space_has_named_params(__isl_keep isl_space *space)
+{
+ int i;
+ unsigned off;
+
+ if (!space)
+ return isl_bool_error;
+ if (space->nparam == 0)
+ return isl_bool_true;
+ off = isl_space_offset(space, isl_dim_param);
+ if (off + space->nparam > space->n_id)
+ return isl_bool_false;
+ for (i = 0; i < space->nparam; ++i)
+ if (!space->ids[off + i])
+ return isl_bool_false;
+ return isl_bool_true;
+}
+
+/* Check that "space" has only named parameters, reporting an error
+ * if it does not.
+ */
+isl_stat isl_space_check_named_params(__isl_keep isl_space *space)
+{
+ isl_bool named;
+
+ named = isl_space_has_named_params(space);
+ if (named < 0)
+ return isl_stat_error;
+ if (!named)
+ isl_die(isl_space_get_ctx(space), isl_error_invalid,
+ "unexpected unnamed parameters", return isl_stat_error);
+
+ return isl_stat_ok;
+}
+
+/* Align the initial parameters of space1 to match the order in space2.
+ */
+__isl_give isl_space *isl_space_align_params(__isl_take isl_space *space1,
+ __isl_take isl_space *space2)
+{
+ isl_reordering *exp;
+
+ if (isl_space_check_named_params(space1) < 0 ||
+ isl_space_check_named_params(space2) < 0)
+ goto error;
+
+ exp = isl_parameter_alignment_reordering(space1, space2);
+ exp = isl_reordering_extend_space(exp, space1);
+ isl_space_free(space2);
+ space1 = isl_reordering_get_space(exp);
+ isl_reordering_free(exp);
+ return space1;
+error:
+ isl_space_free(space1);
+ isl_space_free(space2);
+ return NULL;
+}
+
+/* Given the space of set (domain), construct a space for a map
+ * with as domain the given space and as range the range of "model".
+ */
+__isl_give isl_space *isl_space_extend_domain_with_range(
+ __isl_take isl_space *space, __isl_take isl_space *model)
+{
+ isl_size n_out;
+
+ if (!model)
+ goto error;
+
+ space = isl_space_from_domain(space);
+ n_out = isl_space_dim(model, isl_dim_out);
+ if (n_out < 0)
+ goto error;
+ space = isl_space_add_dims(space, isl_dim_out, n_out);
+ if (isl_space_has_tuple_id(model, isl_dim_out))
+ space = isl_space_set_tuple_id(space, isl_dim_out,
+ isl_space_get_tuple_id(model, isl_dim_out));
+ if (!space)
+ goto error;
+ if (model->nested[1]) {
+ isl_space *nested = isl_space_copy(model->nested[1]);
+ isl_size n_nested, n_space;
+ nested = isl_space_align_params(nested, isl_space_copy(space));
+ n_nested = isl_space_dim(nested, isl_dim_param);
+ n_space = isl_space_dim(space, isl_dim_param);
+ if (n_nested < 0 || n_space < 0)
+ goto error;
+ if (n_nested > n_space)
+ nested = isl_space_drop_dims(nested, isl_dim_param,
+ n_space, n_nested - n_space);
+ if (!nested)
+ goto error;
+ space->nested[1] = nested;
+ }
+ isl_space_free(model);
+ return space;
+error:
+ isl_space_free(model);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Compare the "type" dimensions of two isl_spaces.
+ *
+ * The order is fairly arbitrary.
+ */
+static int isl_space_cmp_type(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2, enum isl_dim_type type)
+{
+ int cmp;
+ isl_size dim1, dim2;
+ isl_space *nested1, *nested2;
+
+ dim1 = isl_space_dim(space1, type);
+ dim2 = isl_space_dim(space2, type);
+ if (dim1 < 0 || dim2 < 0)
+ return 0;
+ if (dim1 != dim2)
+ return dim1 - dim2;
+
+ cmp = isl_id_cmp(tuple_id(space1, type), tuple_id(space2, type));
+ if (cmp != 0)
+ return cmp;
+
+ nested1 = nested(space1, type);
+ nested2 = nested(space2, type);
+ if (!nested1 != !nested2)
+ return !nested1 - !nested2;
+
+ if (nested1)
+ return isl_space_cmp(nested1, nested2);
+
+ return 0;
+}
+
+/* Compare two isl_spaces.
+ *
+ * The order is fairly arbitrary.
+ */
+int isl_space_cmp(__isl_keep isl_space *space1, __isl_keep isl_space *space2)
+{
+ int i;
+ int cmp;
+
+ if (space1 == space2)
+ return 0;
+ if (!space1)
+ return -1;
+ if (!space2)
+ return 1;
+
+ cmp = isl_space_cmp_type(space1, space2, isl_dim_param);
+ if (cmp != 0)
+ return cmp;
+ cmp = isl_space_cmp_type(space1, space2, isl_dim_in);
+ if (cmp != 0)
+ return cmp;
+ cmp = isl_space_cmp_type(space1, space2, isl_dim_out);
+ if (cmp != 0)
+ return cmp;
+
+ if (!space1->ids && !space2->ids)
+ return 0;
+
+ for (i = 0; i < n(space1, isl_dim_param); ++i) {
+ cmp = isl_id_cmp(get_id(space1, isl_dim_param, i),
+ get_id(space2, isl_dim_param, i));
+ if (cmp != 0)
+ return cmp;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_space_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_space_private.h
new file mode 100644
index 00000000000..140749e73a1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_space_private.h
@@ -0,0 +1,100 @@
+#ifndef ISL_SPACE_PRIVATE
+#define ISL_SPACE_PRIVATE
+
+#include <isl/space.h>
+#include <isl/hash.h>
+#include <isl/id_type.h>
+
+struct isl_name;
+struct isl_space {
+ int ref;
+
+ struct isl_ctx *ctx;
+
+ unsigned nparam;
+ unsigned n_in; /* zero for sets */
+ unsigned n_out; /* dim for sets */
+
+ isl_id *tuple_id[2];
+ isl_space *nested[2];
+
+ unsigned n_id;
+ isl_id **ids;
+};
+
+__isl_give isl_space *isl_space_cow(__isl_take isl_space *space);
+
+__isl_give isl_space *isl_space_underlying(__isl_take isl_space *space,
+ unsigned n_div);
+
+uint32_t isl_space_get_tuple_hash(__isl_keep isl_space *space);
+uint32_t isl_space_get_tuple_domain_hash(__isl_keep isl_space *space);
+uint32_t isl_space_get_full_hash(__isl_keep isl_space *space);
+
+isl_bool isl_space_has_domain_tuples(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+isl_bool isl_space_has_range_tuples(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+isl_stat isl_space_check_domain_tuples(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+isl_bool isl_space_is_domain_internal(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+isl_bool isl_space_is_range_internal(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+isl_stat isl_space_check_domain_wrapped_domain_tuples(
+ __isl_keep isl_space *space1, __isl_keep isl_space *space2);
+isl_bool isl_space_wrapped_tuple_is_equal(__isl_keep isl_space *space1,
+ enum isl_dim_type outer, enum isl_dim_type inner,
+ __isl_keep isl_space *space2, enum isl_dim_type type2);
+isl_stat isl_space_check_wrapped_tuple_is_equal(__isl_keep isl_space *space1,
+ enum isl_dim_type outer, enum isl_dim_type inner,
+ __isl_keep isl_space *space2, enum isl_dim_type type2);
+
+isl_size isl_space_wrapped_dim(__isl_keep isl_space *space,
+ enum isl_dim_type outer, enum isl_dim_type inner);
+unsigned isl_space_offset(__isl_keep isl_space *space, enum isl_dim_type type);
+
+isl_stat isl_space_check_range(__isl_keep isl_space *space,
+ enum isl_dim_type type, unsigned first, unsigned n);
+isl_stat isl_space_check_is_set(__isl_keep isl_space *space);
+isl_bool isl_space_may_be_set(__isl_keep isl_space *space);
+isl_bool isl_space_is_named_or_nested(__isl_keep isl_space *space,
+ enum isl_dim_type type);
+isl_bool isl_space_has_equal_ids(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+isl_bool isl_space_has_named_params(__isl_keep isl_space *space);
+isl_stat isl_space_check_named_params(__isl_keep isl_space *space);
+isl_stat isl_space_check_equal_params(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+isl_stat isl_space_check_equal_tuples(__isl_keep isl_space *space1,
+ __isl_keep isl_space *space2);
+__isl_give isl_space *isl_space_reset(__isl_take isl_space *space,
+ enum isl_dim_type type);
+__isl_give isl_space *isl_space_flatten(__isl_take isl_space *space);
+
+isl_stat isl_space_check_domain_is_wrapping(__isl_keep isl_space *space);
+isl_stat isl_space_check_range_is_wrapping(__isl_keep isl_space *space);
+
+__isl_give isl_space *isl_space_replace_params(__isl_take isl_space *dst,
+ __isl_keep isl_space *src);
+__isl_give isl_space *isl_space_copy_ids_if_unset(__isl_take isl_space *dst,
+ enum isl_dim_type dst_type, __isl_keep isl_space *src,
+ enum isl_dim_type src_type);
+
+__isl_give isl_space *isl_space_lift(__isl_take isl_space *space,
+ unsigned n_local);
+
+__isl_give isl_space *isl_space_extend_domain_with_range(
+ __isl_take isl_space *domain, __isl_take isl_space *model);
+__isl_give isl_space *isl_space_bind_set(__isl_take isl_space *space,
+ __isl_keep isl_multi_id *tuple);
+__isl_give isl_space *isl_space_bind_map_domain(__isl_take isl_space *space,
+ __isl_keep isl_multi_id *tuple);
+__isl_give isl_space *isl_space_bind_domain_wrapped_domain(
+ __isl_take isl_space *space, __isl_keep isl_multi_id *tuple);
+__isl_give isl_space *isl_space_unbind_params_insert_domain(
+ __isl_take isl_space *space, __isl_keep isl_multi_id *tuple);
+
+int isl_space_cmp(__isl_keep isl_space *space1, __isl_keep isl_space *space2);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stream.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stream.c
new file mode 100644
index 00000000000..1052360aa32
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stream.c
@@ -0,0 +1,1190 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <isl_ctx_private.h>
+#include <isl_stream_private.h>
+#include <isl/map.h>
+#include <isl/aff.h>
+#include <isl_val_private.h>
+#include <isl_options_private.h>
+
+struct isl_keyword {
+ char *name;
+ enum isl_token_type type;
+};
+
+static isl_bool same_name(const void *entry, const void *val)
+{
+ const struct isl_keyword *keyword = (const struct isl_keyword *)entry;
+
+ return isl_bool_ok(!strcmp(keyword->name, val));
+}
+
+enum isl_token_type isl_stream_register_keyword(__isl_keep isl_stream *s,
+ const char *name)
+{
+ struct isl_hash_table_entry *entry;
+ struct isl_keyword *keyword;
+ uint32_t name_hash;
+
+ if (!s->keywords) {
+ s->keywords = isl_hash_table_alloc(s->ctx, 10);
+ if (!s->keywords)
+ return ISL_TOKEN_ERROR;
+ s->next_type = ISL_TOKEN_LAST;
+ }
+
+ name_hash = isl_hash_string(isl_hash_init(), name);
+
+ entry = isl_hash_table_find(s->ctx, s->keywords, name_hash,
+ same_name, name, 1);
+ if (!entry)
+ return ISL_TOKEN_ERROR;
+ if (entry->data) {
+ keyword = entry->data;
+ return keyword->type;
+ }
+
+ keyword = isl_calloc_type(s->ctx, struct isl_keyword);
+ if (!keyword)
+ return ISL_TOKEN_ERROR;
+ keyword->type = s->next_type++;
+ keyword->name = strdup(name);
+ if (!keyword->name) {
+ free(keyword);
+ return ISL_TOKEN_ERROR;
+ }
+ entry->data = keyword;
+
+ return keyword->type;
+}
+
+struct isl_token *isl_token_new(isl_ctx *ctx,
+ int line, int col, unsigned on_new_line)
+{
+ struct isl_token *tok = isl_alloc_type(ctx, struct isl_token);
+ if (!tok)
+ return NULL;
+ tok->line = line;
+ tok->col = col;
+ tok->on_new_line = on_new_line;
+ tok->is_keyword = 0;
+ tok->u.s = NULL;
+ return tok;
+}
+
+/* Return the type of "tok".
+ */
+int isl_token_get_type(struct isl_token *tok)
+{
+ return tok ? tok->type : ISL_TOKEN_ERROR;
+}
+
+/* Given a token of type ISL_TOKEN_VALUE, return the value it represents.
+ */
+__isl_give isl_val *isl_token_get_val(isl_ctx *ctx, struct isl_token *tok)
+{
+ if (!tok)
+ return NULL;
+ if (tok->type != ISL_TOKEN_VALUE)
+ isl_die(ctx, isl_error_invalid, "not a value token",
+ return NULL);
+
+ return isl_val_int_from_isl_int(ctx, tok->u.v);
+}
+
+/* Given a token with a string representation, return a copy of this string.
+ */
+__isl_give char *isl_token_get_str(isl_ctx *ctx, struct isl_token *tok)
+{
+ if (!tok)
+ return NULL;
+ if (!tok->u.s)
+ isl_die(ctx, isl_error_invalid,
+ "token does not have a string representation",
+ return NULL);
+
+ return strdup(tok->u.s);
+}
+
+void isl_token_free(struct isl_token *tok)
+{
+ if (!tok)
+ return;
+ if (tok->type == ISL_TOKEN_VALUE)
+ isl_int_clear(tok->u.v);
+ else if (tok->type == ISL_TOKEN_MAP)
+ isl_map_free(tok->u.map);
+ else if (tok->type == ISL_TOKEN_AFF)
+ isl_pw_aff_free(tok->u.pwaff);
+ else
+ free(tok->u.s);
+ free(tok);
+}
+
+void isl_stream_error(__isl_keep isl_stream *s, struct isl_token *tok,
+ char *msg)
+{
+ int line = tok ? tok->line : s->line;
+ int col = tok ? tok->col : s->col;
+
+ isl_ctx_set_full_error(s->ctx, isl_error_invalid, "syntax error",
+ __FILE__, __LINE__);
+
+ if (s->ctx->opt->on_error == ISL_ON_ERROR_CONTINUE)
+ return;
+ fprintf(stderr, "syntax error (%d, %d): %s\n", line, col, msg);
+ if (tok) {
+ if (tok->type < 256)
+ fprintf(stderr, "got '%c'\n", tok->type);
+ else if (tok->type == ISL_TOKEN_IDENT)
+ fprintf(stderr, "got ident '%s'\n", tok->u.s);
+ else if (tok->is_keyword)
+ fprintf(stderr, "got keyword '%s'\n", tok->u.s);
+ else if (tok->type == ISL_TOKEN_VALUE) {
+ fprintf(stderr, "got value '");
+ isl_int_print(stderr, tok->u.v, 0);
+ fprintf(stderr, "'\n");
+ } else if (tok->type == ISL_TOKEN_MAP) {
+ isl_printer *p;
+ fprintf(stderr, "got map '");
+ p = isl_printer_to_file(s->ctx, stderr);
+ p = isl_printer_print_map(p, tok->u.map);
+ isl_printer_free(p);
+ fprintf(stderr, "'\n");
+ } else if (tok->type == ISL_TOKEN_AFF) {
+ isl_printer *p;
+ fprintf(stderr, "got affine expression '");
+ p = isl_printer_to_file(s->ctx, stderr);
+ p = isl_printer_print_pw_aff(p, tok->u.pwaff);
+ isl_printer_free(p);
+ fprintf(stderr, "'\n");
+ } else if (tok->u.s)
+ fprintf(stderr, "got token '%s'\n", tok->u.s);
+ else
+ fprintf(stderr, "got token type %d\n", tok->type);
+ }
+ if (s->ctx->opt->on_error == ISL_ON_ERROR_ABORT)
+ abort();
+}
+
+static __isl_give isl_stream* isl_stream_new(struct isl_ctx *ctx)
+{
+ int i;
+ isl_stream *s = isl_calloc_type(ctx, struct isl_stream);
+ if (!s)
+ return NULL;
+ s->ctx = ctx;
+ isl_ctx_ref(s->ctx);
+ s->file = NULL;
+ s->str = NULL;
+ s->len = 0;
+ s->line = 1;
+ s->col = 1;
+ s->eof = 0;
+ s->last_line = 0;
+ s->c = -1;
+ s->n_un = 0;
+ for (i = 0; i < 5; ++i)
+ s->tokens[i] = NULL;
+ s->n_token = 0;
+ s->keywords = NULL;
+ s->size = 256;
+ s->buffer = isl_alloc_array(ctx, char, s->size);
+ if (!s->buffer)
+ goto error;
+ return s;
+error:
+ isl_stream_free(s);
+ return NULL;
+}
+
+__isl_give isl_stream* isl_stream_new_file(struct isl_ctx *ctx, FILE *file)
+{
+ isl_stream *s = isl_stream_new(ctx);
+ if (!s)
+ return NULL;
+ s->file = file;
+ return s;
+}
+
+__isl_give isl_stream* isl_stream_new_str(struct isl_ctx *ctx, const char *str)
+{
+ isl_stream *s;
+ if (!str)
+ return NULL;
+ s = isl_stream_new(ctx);
+ if (!s)
+ return NULL;
+ s->str = str;
+ return s;
+}
+
+/* Read a character from the stream and advance s->line and s->col
+ * to point to the next character.
+ */
+static int stream_getc(__isl_keep isl_stream *s)
+{
+ int c;
+ if (s->eof)
+ return -1;
+ if (s->n_un)
+ return s->c = s->un[--s->n_un];
+ if (s->file)
+ c = fgetc(s->file);
+ else {
+ c = *s->str++;
+ if (c == '\0')
+ c = -1;
+ }
+ if (c == -1)
+ s->eof = 1;
+ else if (c == '\n') {
+ s->line++;
+ s->col = 1;
+ } else
+ s->col++;
+ s->c = c;
+ return c;
+}
+
+static void isl_stream_ungetc(__isl_keep isl_stream *s, int c)
+{
+ isl_assert(s->ctx, s->n_un < 5, return);
+ s->un[s->n_un++] = c;
+ s->c = -1;
+}
+
+/* Read a character from the stream, skipping pairs of '\\' and '\n'.
+ * Set s->start_line and s->start_col to the line and column
+ * of the returned character.
+ */
+static int isl_stream_getc(__isl_keep isl_stream *s)
+{
+ int c;
+
+ do {
+ s->start_line = s->line;
+ s->start_col = s->col;
+ c = stream_getc(s);
+ if (c != '\\')
+ return c;
+ c = stream_getc(s);
+ } while (c == '\n');
+
+ isl_stream_ungetc(s, c);
+
+ return '\\';
+}
+
+static int isl_stream_push_char(__isl_keep isl_stream *s, int c)
+{
+ if (s->len >= s->size) {
+ char *buffer;
+ s->size = (3*s->size)/2;
+ buffer = isl_realloc_array(s->ctx, s->buffer, char, s->size);
+ if (!buffer)
+ return -1;
+ s->buffer = buffer;
+ }
+ s->buffer[s->len++] = c;
+ return 0;
+}
+
+void isl_stream_push_token(__isl_keep isl_stream *s, struct isl_token *tok)
+{
+ isl_assert(s->ctx, s->n_token < 5, return);
+ s->tokens[s->n_token++] = tok;
+}
+
+static enum isl_token_type check_keywords(__isl_keep isl_stream *s)
+{
+ struct isl_hash_table_entry *entry;
+ struct isl_keyword *keyword;
+ uint32_t name_hash;
+
+ if (!strcasecmp(s->buffer, "exists"))
+ return ISL_TOKEN_EXISTS;
+ if (!strcasecmp(s->buffer, "and"))
+ return ISL_TOKEN_AND;
+ if (!strcasecmp(s->buffer, "or"))
+ return ISL_TOKEN_OR;
+ if (!strcasecmp(s->buffer, "implies"))
+ return ISL_TOKEN_IMPLIES;
+ if (!strcasecmp(s->buffer, "not"))
+ return ISL_TOKEN_NOT;
+ if (!strcasecmp(s->buffer, "infty"))
+ return ISL_TOKEN_INFTY;
+ if (!strcasecmp(s->buffer, "infinity"))
+ return ISL_TOKEN_INFTY;
+ if (!strcasecmp(s->buffer, "NaN"))
+ return ISL_TOKEN_NAN;
+ if (!strcasecmp(s->buffer, "min"))
+ return ISL_TOKEN_MIN;
+ if (!strcasecmp(s->buffer, "max"))
+ return ISL_TOKEN_MAX;
+ if (!strcasecmp(s->buffer, "rat"))
+ return ISL_TOKEN_RAT;
+ if (!strcasecmp(s->buffer, "true"))
+ return ISL_TOKEN_TRUE;
+ if (!strcasecmp(s->buffer, "false"))
+ return ISL_TOKEN_FALSE;
+ if (!strcasecmp(s->buffer, "ceild"))
+ return ISL_TOKEN_CEILD;
+ if (!strcasecmp(s->buffer, "floord"))
+ return ISL_TOKEN_FLOORD;
+ if (!strcasecmp(s->buffer, "mod"))
+ return ISL_TOKEN_MOD;
+ if (!strcasecmp(s->buffer, "ceil"))
+ return ISL_TOKEN_CEIL;
+ if (!strcasecmp(s->buffer, "floor"))
+ return ISL_TOKEN_FLOOR;
+
+ if (!s->keywords)
+ return ISL_TOKEN_IDENT;
+
+ name_hash = isl_hash_string(isl_hash_init(), s->buffer);
+ entry = isl_hash_table_find(s->ctx, s->keywords, name_hash, same_name,
+ s->buffer, 0);
+ if (!entry)
+ return ISL_TOKEN_ERROR;
+ if (entry != isl_hash_table_entry_none) {
+ keyword = entry->data;
+ return keyword->type;
+ }
+
+ return ISL_TOKEN_IDENT;
+}
+
+int isl_stream_skip_line(__isl_keep isl_stream *s)
+{
+ int c;
+
+ while ((c = isl_stream_getc(s)) != -1 && c != '\n')
+ /* nothing */
+ ;
+
+ return c == -1 ? -1 : 0;
+}
+
+static struct isl_token *next_token(__isl_keep isl_stream *s, int same_line)
+{
+ int c;
+ struct isl_token *tok = NULL;
+ int line, col;
+ int old_line = s->last_line;
+
+ if (s->n_token) {
+ if (same_line && s->tokens[s->n_token - 1]->on_new_line)
+ return NULL;
+ return s->tokens[--s->n_token];
+ }
+
+ if (same_line && s->c == '\n')
+ return NULL;
+
+ s->len = 0;
+
+ /* skip spaces and comment lines */
+ while ((c = isl_stream_getc(s)) != -1) {
+ if (c == '#') {
+ if (isl_stream_skip_line(s) < 0)
+ break;
+ c = '\n';
+ if (same_line)
+ break;
+ } else if (!isspace(c) || (same_line && c == '\n'))
+ break;
+ }
+
+ line = s->start_line;
+ col = s->start_col;
+
+ if (c == -1 || (same_line && c == '\n'))
+ return NULL;
+ s->last_line = line;
+
+ if (c == '(' ||
+ c == ')' ||
+ c == '+' ||
+ c == '*' ||
+ c == '%' ||
+ c == '?' ||
+ c == '^' ||
+ c == '@' ||
+ c == '$' ||
+ c == ',' ||
+ c == '.' ||
+ c == ';' ||
+ c == '[' ||
+ c == ']' ||
+ c == '{' ||
+ c == '}') {
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ tok->type = (enum isl_token_type)c;
+ return tok;
+ }
+ if (c == '-') {
+ int c;
+ if ((c = isl_stream_getc(s)) == '>') {
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ tok->u.s = strdup("->");
+ tok->type = ISL_TOKEN_TO;
+ return tok;
+ }
+ if (c != -1)
+ isl_stream_ungetc(s, c);
+ if (!isdigit(c)) {
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ tok->type = (enum isl_token_type) '-';
+ return tok;
+ }
+ }
+ if (c == '-' || isdigit(c)) {
+ int minus = c == '-';
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ tok->type = ISL_TOKEN_VALUE;
+ isl_int_init(tok->u.v);
+ if (isl_stream_push_char(s, c))
+ goto error;
+ while ((c = isl_stream_getc(s)) != -1 && isdigit(c))
+ if (isl_stream_push_char(s, c))
+ goto error;
+ if (c != -1)
+ isl_stream_ungetc(s, c);
+ isl_stream_push_char(s, '\0');
+ isl_int_read(tok->u.v, s->buffer);
+ if (minus && isl_int_is_zero(tok->u.v)) {
+ tok->col++;
+ tok->on_new_line = 0;
+ isl_stream_push_token(s, tok);
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ tok->type = (enum isl_token_type) '-';
+ }
+ return tok;
+ }
+ if (isalpha(c) || c == '_') {
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ isl_stream_push_char(s, c);
+ while ((c = isl_stream_getc(s)) != -1 &&
+ (isalnum(c) || c == '_'))
+ isl_stream_push_char(s, c);
+ if (c != -1)
+ isl_stream_ungetc(s, c);
+ while ((c = isl_stream_getc(s)) != -1 && c == '\'')
+ isl_stream_push_char(s, c);
+ if (c != -1)
+ isl_stream_ungetc(s, c);
+ isl_stream_push_char(s, '\0');
+ tok->type = check_keywords(s);
+ if (tok->type != ISL_TOKEN_IDENT)
+ tok->is_keyword = 1;
+ tok->u.s = strdup(s->buffer);
+ if (!tok->u.s)
+ goto error;
+ return tok;
+ }
+ if (c == '"') {
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ tok->type = ISL_TOKEN_STRING;
+ tok->u.s = NULL;
+ while ((c = isl_stream_getc(s)) != -1 && c != '"' && c != '\n')
+ isl_stream_push_char(s, c);
+ if (c != '"') {
+ isl_stream_error(s, NULL, "unterminated string");
+ goto error;
+ }
+ isl_stream_push_char(s, '\0');
+ tok->u.s = strdup(s->buffer);
+ return tok;
+ }
+ if (c == '=') {
+ int c;
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ if ((c = isl_stream_getc(s)) == '=') {
+ tok->u.s = strdup("==");
+ tok->type = ISL_TOKEN_EQ_EQ;
+ return tok;
+ }
+ if (c != -1)
+ isl_stream_ungetc(s, c);
+ tok->type = (enum isl_token_type) '=';
+ return tok;
+ }
+ if (c == ':') {
+ int c;
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ if ((c = isl_stream_getc(s)) == '=') {
+ tok->u.s = strdup(":=");
+ tok->type = ISL_TOKEN_DEF;
+ return tok;
+ }
+ if (c != -1)
+ isl_stream_ungetc(s, c);
+ tok->type = (enum isl_token_type) ':';
+ return tok;
+ }
+ if (c == '>') {
+ int c;
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ if ((c = isl_stream_getc(s)) == '=') {
+ tok->u.s = strdup(">=");
+ tok->type = ISL_TOKEN_GE;
+ return tok;
+ } else if (c == '>') {
+ if ((c = isl_stream_getc(s)) == '=') {
+ tok->u.s = strdup(">>=");
+ tok->type = ISL_TOKEN_LEX_GE;
+ return tok;
+ }
+ tok->u.s = strdup(">>");
+ tok->type = ISL_TOKEN_LEX_GT;
+ } else {
+ tok->u.s = strdup(">");
+ tok->type = ISL_TOKEN_GT;
+ }
+ if (c != -1)
+ isl_stream_ungetc(s, c);
+ return tok;
+ }
+ if (c == '<') {
+ int c;
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ if ((c = isl_stream_getc(s)) == '=') {
+ tok->u.s = strdup("<=");
+ tok->type = ISL_TOKEN_LE;
+ return tok;
+ } else if (c == '<') {
+ if ((c = isl_stream_getc(s)) == '=') {
+ tok->u.s = strdup("<<=");
+ tok->type = ISL_TOKEN_LEX_LE;
+ return tok;
+ }
+ tok->u.s = strdup("<<");
+ tok->type = ISL_TOKEN_LEX_LT;
+ } else {
+ tok->u.s = strdup("<");
+ tok->type = ISL_TOKEN_LT;
+ }
+ if (c != -1)
+ isl_stream_ungetc(s, c);
+ return tok;
+ }
+ if (c == '&') {
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ tok->type = ISL_TOKEN_AND;
+ if ((c = isl_stream_getc(s)) != '&' && c != -1) {
+ tok->u.s = strdup("&");
+ isl_stream_ungetc(s, c);
+ } else
+ tok->u.s = strdup("&&");
+ return tok;
+ }
+ if (c == '|') {
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ tok->type = ISL_TOKEN_OR;
+ if ((c = isl_stream_getc(s)) != '|' && c != -1) {
+ tok->u.s = strdup("|");
+ isl_stream_ungetc(s, c);
+ } else
+ tok->u.s = strdup("||");
+ return tok;
+ }
+ if (c == '/') {
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ if ((c = isl_stream_getc(s)) == '\\') {
+ tok->u.s = strdup("/\\");
+ tok->type = ISL_TOKEN_AND;
+ return tok;
+ } else if (c == '/') {
+ tok->u.s = strdup("//");
+ tok->type = ISL_TOKEN_INT_DIV;
+ return tok;
+ } else {
+ tok->type = (enum isl_token_type) '/';
+ }
+ if (c != -1)
+ isl_stream_ungetc(s, c);
+ return tok;
+ }
+ if (c == '\\') {
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ if ((c = isl_stream_getc(s)) != '/' && c != -1) {
+ tok->type = (enum isl_token_type) '\\';
+ isl_stream_ungetc(s, c);
+ } else {
+ tok->u.s = strdup("\\/");
+ tok->type = ISL_TOKEN_OR;
+ }
+ return tok;
+ }
+ if (c == '!') {
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ if ((c = isl_stream_getc(s)) == '=') {
+ tok->u.s = strdup("!=");
+ tok->type = ISL_TOKEN_NE;
+ return tok;
+ } else {
+ tok->type = ISL_TOKEN_NOT;
+ tok->u.s = strdup("!");
+ }
+ if (c != -1)
+ isl_stream_ungetc(s, c);
+ return tok;
+ }
+
+ tok = isl_token_new(s->ctx, line, col, old_line != line);
+ if (!tok)
+ return NULL;
+ tok->type = ISL_TOKEN_UNKNOWN;
+ return tok;
+error:
+ isl_token_free(tok);
+ return NULL;
+}
+
+struct isl_token *isl_stream_next_token(__isl_keep isl_stream *s)
+{
+ return next_token(s, 0);
+}
+
+struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s)
+{
+ return next_token(s, 1);
+}
+
+int isl_stream_eat_if_available(__isl_keep isl_stream *s, int type)
+{
+ struct isl_token *tok;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+ if (tok->type == type) {
+ isl_token_free(tok);
+ return 1;
+ }
+ isl_stream_push_token(s, tok);
+ return 0;
+}
+
+int isl_stream_next_token_is(__isl_keep isl_stream *s, int type)
+{
+ struct isl_token *tok;
+ int r;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+ r = tok->type == type;
+ isl_stream_push_token(s, tok);
+ return r;
+}
+
+char *isl_stream_read_ident_if_available(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return NULL;
+ if (tok->type == ISL_TOKEN_IDENT) {
+ char *ident = strdup(tok->u.s);
+ isl_token_free(tok);
+ return ident;
+ }
+ isl_stream_push_token(s, tok);
+ return NULL;
+}
+
+int isl_stream_eat(__isl_keep isl_stream *s, int type)
+{
+ struct isl_token *tok;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ if (s->eof)
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return -1;
+ }
+ if (tok->type == type) {
+ isl_token_free(tok);
+ return 0;
+ }
+ isl_stream_error(s, tok, "expecting other token");
+ isl_stream_push_token(s, tok);
+ return -1;
+}
+
+int isl_stream_is_empty(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+
+ tok = isl_stream_next_token(s);
+
+ if (!tok)
+ return 1;
+
+ isl_stream_push_token(s, tok);
+ return 0;
+}
+
+static isl_stat free_keyword(void **p, void *user)
+{
+ struct isl_keyword *keyword = *p;
+
+ free(keyword->name);
+ free(keyword);
+
+ return isl_stat_ok;
+}
+
+void isl_stream_flush_tokens(__isl_keep isl_stream *s)
+{
+ int i;
+
+ if (!s)
+ return;
+ for (i = 0; i < s->n_token; ++i)
+ isl_token_free(s->tokens[i]);
+ s->n_token = 0;
+}
+
+isl_ctx *isl_stream_get_ctx(__isl_keep isl_stream *s)
+{
+ return s ? s->ctx : NULL;
+}
+
+void isl_stream_free(__isl_take isl_stream *s)
+{
+ if (!s)
+ return;
+ free(s->buffer);
+ if (s->n_token != 0) {
+ struct isl_token *tok = isl_stream_next_token(s);
+ isl_stream_error(s, tok, "unexpected token");
+ isl_token_free(tok);
+ }
+ if (s->keywords) {
+ isl_hash_table_foreach(s->ctx, s->keywords, &free_keyword, NULL);
+ isl_hash_table_free(s->ctx, s->keywords);
+ }
+ free(s->yaml_state);
+ free(s->yaml_indent);
+ isl_ctx_deref(s->ctx);
+ free(s);
+}
+
+/* Push "state" onto the stack of currently active YAML elements.
+ * The caller is responsible for setting the corresponding indentation.
+ * Return 0 on success and -1 on failure.
+ */
+static int push_state(__isl_keep isl_stream *s, enum isl_yaml_state state)
+{
+ if (s->yaml_size < s->yaml_depth + 1) {
+ int *indent;
+ enum isl_yaml_state *state;
+
+ state = isl_realloc_array(s->ctx, s->yaml_state,
+ enum isl_yaml_state, s->yaml_depth + 1);
+ if (!state)
+ return -1;
+ s->yaml_state = state;
+
+ indent = isl_realloc_array(s->ctx, s->yaml_indent,
+ int, s->yaml_depth + 1);
+ if (!indent)
+ return -1;
+ s->yaml_indent = indent;
+
+ s->yaml_size = s->yaml_depth + 1;
+ }
+
+ s->yaml_state[s->yaml_depth] = state;
+ s->yaml_depth++;
+
+ return 0;
+}
+
+/* Remove the innermost active YAML element from the stack.
+ * Return 0 on success and -1 on failure.
+ */
+static int pop_state(__isl_keep isl_stream *s)
+{
+ if (!s)
+ return -1;
+ if (s->yaml_depth < 1)
+ isl_die(isl_stream_get_ctx(s), isl_error_invalid,
+ "not in YAML construct", return -1);
+
+ s->yaml_depth--;
+
+ return 0;
+}
+
+/* Set the state of the innermost active YAML element to "state".
+ * Return 0 on success and -1 on failure.
+ */
+static int update_state(__isl_keep isl_stream *s, enum isl_yaml_state state)
+{
+ if (!s)
+ return -1;
+ if (s->yaml_depth < 1)
+ isl_die(isl_stream_get_ctx(s), isl_error_invalid,
+ "not in YAML construct", return -1);
+
+ s->yaml_state[s->yaml_depth - 1] = state;
+
+ return 0;
+}
+
+/* Return the state of the innermost active YAML element.
+ * Return isl_yaml_none if we are not inside any YAML element.
+ */
+static enum isl_yaml_state current_state(__isl_keep isl_stream *s)
+{
+ if (!s)
+ return isl_yaml_none;
+ if (s->yaml_depth < 1)
+ return isl_yaml_none;
+ return s->yaml_state[s->yaml_depth - 1];
+}
+
+/* Set the indentation of the innermost active YAML element to "indent".
+ * If "indent" is equal to ISL_YAML_INDENT_FLOW, then this means
+ * that the current elemient is in flow format.
+ */
+static int set_yaml_indent(__isl_keep isl_stream *s, int indent)
+{
+ if (s->yaml_depth < 1)
+ isl_die(s->ctx, isl_error_internal,
+ "not in YAML element", return -1);
+
+ s->yaml_indent[s->yaml_depth - 1] = indent;
+
+ return 0;
+}
+
+/* Return the indentation of the innermost active YAML element
+ * of -1 on error.
+ */
+static int get_yaml_indent(__isl_keep isl_stream *s)
+{
+ if (s->yaml_depth < 1)
+ isl_die(s->ctx, isl_error_internal,
+ "not in YAML element", return -1);
+
+ return s->yaml_indent[s->yaml_depth - 1];
+}
+
+/* Move to the next state at the innermost level.
+ * Return 1 if successful.
+ * Return 0 if we are at the end of the innermost level.
+ * Return -1 on error.
+ *
+ * If we are in state isl_yaml_mapping_key_start, then we have just
+ * started a mapping and we are expecting a key. If the mapping started
+ * with a '{', then we check if the next token is a '}'. If so,
+ * then the mapping is empty and there is no next state at this level.
+ * Otherwise, we assume that there is at least one key (the one from
+ * which we derived the indentation in isl_stream_yaml_read_start_mapping.
+ *
+ * If we are in state isl_yaml_mapping_key, then the we expect a colon
+ * followed by a value, so there is always a next state unless
+ * some error occurs.
+ *
+ * If we are in state isl_yaml_mapping_val, then there may or may
+ * not be a subsequent key in the same mapping.
+ * In flow format, the next key is preceded by a comma.
+ * In block format, the next key has the same indentation as the first key.
+ * If the first token has a smaller indentation, then we have reached
+ * the end of the current mapping.
+ *
+ * If we are in state isl_yaml_sequence_start, then we have just
+ * started a sequence. If the sequence started with a '[',
+ * then we check if the next token is a ']'. If so, then the sequence
+ * is empty and there is no next state at this level.
+ * Otherwise, we assume that there is at least one element in the sequence
+ * (the one from which we derived the indentation in
+ * isl_stream_yaml_read_start_sequence.
+ *
+ * If we are in state isl_yaml_sequence, then there may or may
+ * not be a subsequent element in the same sequence.
+ * In flow format, the next element is preceded by a comma.
+ * In block format, the next element is introduced by a dash with
+ * the same indentation as that of the first element.
+ * If the first token is not a dash or if it has a smaller indentation,
+ * then we have reached the end of the current sequence.
+ */
+int isl_stream_yaml_next(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ enum isl_yaml_state state;
+ int indent;
+
+ state = current_state(s);
+ if (state == isl_yaml_none)
+ isl_die(s->ctx, isl_error_invalid,
+ "not in YAML element", return -1);
+ switch (state) {
+ case isl_yaml_mapping_key_start:
+ if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW &&
+ isl_stream_next_token_is(s, '}'))
+ return 0;
+ if (update_state(s, isl_yaml_mapping_key) < 0)
+ return -1;
+ return 1;
+ case isl_yaml_mapping_key:
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ if (s->eof)
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return -1;
+ }
+ if (tok->type == ':') {
+ isl_token_free(tok);
+ if (update_state(s, isl_yaml_mapping_val) < 0)
+ return -1;
+ return 1;
+ }
+ isl_stream_error(s, tok, "expecting ':'");
+ isl_stream_push_token(s, tok);
+ return -1;
+ case isl_yaml_mapping_val:
+ if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
+ if (!isl_stream_eat_if_available(s, ','))
+ return 0;
+ if (update_state(s, isl_yaml_mapping_key) < 0)
+ return -1;
+ return 1;
+ }
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+ indent = tok->col - 1;
+ isl_stream_push_token(s, tok);
+ if (indent < get_yaml_indent(s))
+ return 0;
+ if (update_state(s, isl_yaml_mapping_key) < 0)
+ return -1;
+ return 1;
+ case isl_yaml_sequence_start:
+ if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
+ if (isl_stream_next_token_is(s, ']'))
+ return 0;
+ if (update_state(s, isl_yaml_sequence) < 0)
+ return -1;
+ return 1;
+ }
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ if (s->eof)
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return -1;
+ }
+ if (tok->type == '-') {
+ isl_token_free(tok);
+ if (update_state(s, isl_yaml_sequence) < 0)
+ return -1;
+ return 1;
+ }
+ isl_stream_error(s, tok, "expecting '-'");
+ isl_stream_push_token(s, tok);
+ return 0;
+ case isl_yaml_sequence:
+ if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW)
+ return isl_stream_eat_if_available(s, ',');
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return 0;
+ indent = tok->col - 1;
+ if (indent < get_yaml_indent(s) || tok->type != '-') {
+ isl_stream_push_token(s, tok);
+ return 0;
+ }
+ isl_token_free(tok);
+ return 1;
+ default:
+ isl_die(s->ctx, isl_error_internal,
+ "unexpected state", return 0);
+ }
+}
+
+/* Start reading a YAML mapping.
+ * Return 0 on success and -1 on error.
+ *
+ * If the first token on the stream is a '{' then we remove this token
+ * from the stream and keep track of the fact that the mapping
+ * is given in flow format.
+ * Otherwise, we assume the first token is the first key of the mapping and
+ * keep track of its indentation, but keep the token on the stream.
+ * In both cases, the next token we expect is the first key of the mapping.
+ */
+int isl_stream_yaml_read_start_mapping(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ int indent;
+
+ if (push_state(s, isl_yaml_mapping_key_start) < 0)
+ return -1;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ if (s->eof)
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return -1;
+ }
+ if (isl_token_get_type(tok) == '{') {
+ isl_token_free(tok);
+ return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
+ }
+ indent = tok->col - 1;
+ isl_stream_push_token(s, tok);
+
+ return set_yaml_indent(s, indent);
+}
+
+/* Finish reading a YAML mapping.
+ * Return 0 on success and -1 on error.
+ *
+ * If the mapping started with a '{', then we expect a '}' to close
+ * the mapping.
+ * Otherwise, we double-check that the next token (if any)
+ * has a smaller indentation than that of the current mapping.
+ */
+int isl_stream_yaml_read_end_mapping(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ int indent;
+
+ if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
+ if (isl_stream_eat(s, '}') < 0)
+ return -1;
+ return pop_state(s);
+ }
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return pop_state(s);
+
+ indent = tok->col - 1;
+ isl_stream_push_token(s, tok);
+
+ if (indent >= get_yaml_indent(s))
+ isl_die(isl_stream_get_ctx(s), isl_error_invalid,
+ "mapping not finished", return -1);
+
+ return pop_state(s);
+}
+
+/* Start reading a YAML sequence.
+ * Return 0 on success and -1 on error.
+ *
+ * If the first token on the stream is a '[' then we remove this token
+ * from the stream and keep track of the fact that the sequence
+ * is given in flow format.
+ * Otherwise, we assume the first token is the dash that introduces
+ * the first element of the sequence and keep track of its indentation,
+ * but keep the token on the stream.
+ * In both cases, the next token we expect is the first element
+ * of the sequence.
+ */
+int isl_stream_yaml_read_start_sequence(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ int indent;
+
+ if (push_state(s, isl_yaml_sequence_start) < 0)
+ return -1;
+
+ tok = isl_stream_next_token(s);
+ if (!tok) {
+ if (s->eof)
+ isl_stream_error(s, NULL, "unexpected EOF");
+ return -1;
+ }
+ if (isl_token_get_type(tok) == '[') {
+ isl_token_free(tok);
+ return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
+ }
+ indent = tok->col - 1;
+ isl_stream_push_token(s, tok);
+
+ return set_yaml_indent(s, indent);
+}
+
+/* Finish reading a YAML sequence.
+ * Return 0 on success and -1 on error.
+ *
+ * If the sequence started with a '[', then we expect a ']' to close
+ * the sequence.
+ * Otherwise, we double-check that the next token (if any)
+ * is not a dash or that it has a smaller indentation than
+ * that of the current sequence.
+ */
+int isl_stream_yaml_read_end_sequence(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ int indent;
+ int dash;
+
+ if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
+ if (isl_stream_eat(s, ']') < 0)
+ return -1;
+ return pop_state(s);
+ }
+
+ tok = isl_stream_next_token(s);
+ if (!tok)
+ return pop_state(s);
+
+ indent = tok->col - 1;
+ dash = tok->type == '-';
+ isl_stream_push_token(s, tok);
+
+ if (indent >= get_yaml_indent(s) && dash)
+ isl_die(isl_stream_get_ctx(s), isl_error_invalid,
+ "sequence not finished", return -1);
+
+ return pop_state(s);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stream_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stream_private.h
new file mode 100644
index 00000000000..b199ec64dc2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stream_private.h
@@ -0,0 +1,69 @@
+#include <isl_int.h>
+#include <isl/stream.h>
+#include <isl_yaml.h>
+
+struct isl_token {
+ int type;
+
+ unsigned int on_new_line : 1;
+ unsigned is_keyword : 1;
+ int line;
+ int col;
+
+ union {
+ isl_int v;
+ char *s;
+ isl_map *map;
+ isl_pw_aff *pwaff;
+ } u;
+};
+
+struct isl_token *isl_token_new(isl_ctx *ctx,
+ int line, int col, unsigned on_new_line);
+
+/* An input stream that may be either a file or a string.
+ *
+ * line and col are the line and column number of the next character (1-based).
+ * start_line and start_col are set by isl_stream_getc to point
+ * to the position of the returned character.
+ * last_line is the line number of the previous token.
+ *
+ * yaml_state and yaml_indent keep track of the currently active YAML
+ * elements. yaml_size is the size of these arrays, while yaml_depth
+ * is the number of elements currently in use.
+ * yaml_state and yaml_indent may be NULL if no YAML parsing is being
+ * performed.
+ * yaml_state keeps track of what is expected next at each level.
+ * yaml_indent keeps track of the indentation at each level, with
+ * ISL_YAML_INDENT_FLOW meaning that the element is in flow format
+ * (such that the indentation is not relevant).
+ */
+struct isl_stream {
+ struct isl_ctx *ctx;
+ FILE *file;
+ const char *str;
+ int line;
+ int col;
+ int start_line;
+ int start_col;
+ int last_line;
+ int eof;
+
+ char *buffer;
+ size_t size;
+ size_t len;
+ int c;
+ int un[5];
+ int n_un;
+
+ struct isl_token *tokens[5];
+ int n_token;
+
+ struct isl_hash_table *keywords;
+ enum isl_token_type next_type;
+
+ int yaml_depth;
+ int yaml_size;
+ enum isl_yaml_state *yaml_state;
+ int *yaml_indent;
+};
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stride.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stride.c
new file mode 100644
index 00000000000..2d1c027062d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_stride.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2012-2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/val.h>
+#include <isl_map_private.h>
+#include <isl_aff_private.h>
+#include <isl/constraint.h>
+#include <isl/set.h>
+
+/* Stride information about a specific set dimension.
+ * The values of the set dimension are equal to
+ * "offset" plus a multiple of "stride".
+ */
+struct isl_stride_info {
+ isl_val *stride;
+ isl_aff *offset;
+};
+
+/* Return the ctx to which "si" belongs.
+ */
+isl_ctx *isl_stride_info_get_ctx(__isl_keep isl_stride_info *si)
+{
+ if (!si)
+ return NULL;
+
+ return isl_val_get_ctx(si->stride);
+}
+
+/* Free "si" and return NULL.
+ */
+__isl_null isl_stride_info *isl_stride_info_free(
+ __isl_take isl_stride_info *si)
+{
+ if (!si)
+ return NULL;
+ isl_val_free(si->stride);
+ isl_aff_free(si->offset);
+ free(si);
+ return NULL;
+}
+
+/* Construct an isl_stride_info object with given offset and stride.
+ */
+__isl_give isl_stride_info *isl_stride_info_alloc(
+ __isl_take isl_val *stride, __isl_take isl_aff *offset)
+{
+ struct isl_stride_info *si;
+
+ if (!stride || !offset)
+ goto error;
+ si = isl_alloc_type(isl_val_get_ctx(stride), struct isl_stride_info);
+ if (!si)
+ goto error;
+ si->stride = stride;
+ si->offset = offset;
+ return si;
+error:
+ isl_val_free(stride);
+ isl_aff_free(offset);
+ return NULL;
+}
+
+/* Make a copy of "si" and return it.
+ */
+__isl_give isl_stride_info *isl_stride_info_copy(
+ __isl_keep isl_stride_info *si)
+{
+ if (!si)
+ return NULL;
+
+ return isl_stride_info_alloc(isl_val_copy(si->stride),
+ isl_aff_copy(si->offset));
+}
+
+/* Return the stride of "si".
+ */
+__isl_give isl_val *isl_stride_info_get_stride(__isl_keep isl_stride_info *si)
+{
+ if (!si)
+ return NULL;
+ return isl_val_copy(si->stride);
+}
+
+/* Return the offset of "si".
+ */
+__isl_give isl_aff *isl_stride_info_get_offset(__isl_keep isl_stride_info *si)
+{
+ if (!si)
+ return NULL;
+ return isl_aff_copy(si->offset);
+}
+
+/* Information used inside detect_stride.
+ *
+ * "pos" is the set dimension at which the stride is being determined.
+ * "want_offset" is set if the offset should be computed.
+ * "found" is set if some stride was found already.
+ * "stride" and "offset" contain the (combined) stride and offset
+ * found so far and are NULL when "found" is not set.
+ * If "want_offset" is not set, then "offset" remains NULL.
+ */
+struct isl_detect_stride_data {
+ int pos;
+ int want_offset;
+ int found;
+ isl_val *stride;
+ isl_aff *offset;
+};
+
+/* Set the stride and offset of data->pos to the given
+ * value and expression.
+ *
+ * If we had already found a stride before, then the two strides
+ * are combined into a single stride.
+ *
+ * In particular, if the new stride information is of the form
+ *
+ * i = f + s (...)
+ *
+ * and the old stride information is of the form
+ *
+ * i = f2 + s2 (...)
+ *
+ * then we compute the extended gcd of s and s2
+ *
+ * a s + b s2 = g,
+ *
+ * with g = gcd(s,s2), multiply the first equation with t1 = b s2/g
+ * and the second with t2 = a s1/g.
+ * This results in
+ *
+ * i = (b s2 + a s1)/g i = t1 f + t2 f2 + (s s2)/g (...)
+ *
+ * so that t1 f + t2 f2 is the combined offset and (s s2)/g = lcm(s,s2)
+ * is the combined stride.
+ */
+static isl_stat set_stride(struct isl_detect_stride_data *data,
+ __isl_take isl_val *stride, __isl_take isl_aff *offset)
+{
+ int pos;
+
+ if (!stride || !offset)
+ goto error;
+
+ pos = data->pos;
+
+ if (data->found) {
+ isl_val *stride2, *a, *b, *g;
+ isl_aff *offset2;
+
+ stride2 = data->stride;
+ g = isl_val_gcdext(isl_val_copy(stride), isl_val_copy(stride2),
+ &a, &b);
+ a = isl_val_mul(a, isl_val_copy(stride));
+ a = isl_val_div(a, isl_val_copy(g));
+ stride2 = isl_val_div(stride2, g);
+ b = isl_val_mul(b, isl_val_copy(stride2));
+ stride = isl_val_mul(stride, stride2);
+
+ if (!data->want_offset) {
+ isl_val_free(a);
+ isl_val_free(b);
+ } else {
+ offset2 = data->offset;
+ offset2 = isl_aff_scale_val(offset2, a);
+ offset = isl_aff_scale_val(offset, b);
+ offset = isl_aff_add(offset, offset2);
+ }
+ }
+
+ data->found = 1;
+ data->stride = stride;
+ if (data->want_offset)
+ data->offset = offset;
+ else
+ isl_aff_free(offset);
+ if (!data->stride || (data->want_offset && !data->offset))
+ return isl_stat_error;
+
+ return isl_stat_ok;
+error:
+ isl_val_free(stride);
+ isl_aff_free(offset);
+ return isl_stat_error;
+}
+
+/* Check if constraint "c" imposes any stride on dimension data->pos
+ * and, if so, update the stride information in "data".
+ *
+ * In order to impose a stride on the dimension, "c" needs to be an equality
+ * and it needs to involve the dimension. Note that "c" may also be
+ * a div constraint and thus an inequality that we cannot use.
+ *
+ * Let c be of the form
+ *
+ * h(p) + g * v * i + g * stride * f(alpha) = 0
+ *
+ * with h(p) an expression in terms of the parameters and other dimensions
+ * and f(alpha) an expression in terms of the existentially quantified
+ * variables.
+ *
+ * If "stride" is not zero and not one, then it represents a non-trivial stride
+ * on "i". We compute a and b such that
+ *
+ * a v + b stride = 1
+ *
+ * We have
+ *
+ * g v i = -h(p) + g stride f(alpha)
+ *
+ * a g v i = -a h(p) + g stride f(alpha)
+ *
+ * a g v i + b g stride i = -a h(p) + g stride * (...)
+ *
+ * g i = -a h(p) + g stride * (...)
+ *
+ * i = -a h(p)/g + stride * (...)
+ *
+ * The expression "-a h(p)/g" can therefore be used as offset.
+ */
+static isl_stat detect_stride(__isl_take isl_constraint *c, void *user)
+{
+ struct isl_detect_stride_data *data = user;
+ int i;
+ isl_size n_div;
+ isl_ctx *ctx;
+ isl_stat r = isl_stat_ok;
+ isl_val *v, *stride, *m;
+ isl_bool is_eq, relevant, has_stride;
+
+ is_eq = isl_constraint_is_equality(c);
+ relevant = isl_constraint_involves_dims(c, isl_dim_set, data->pos, 1);
+ if (is_eq < 0 || relevant < 0)
+ goto error;
+ if (!is_eq || !relevant) {
+ isl_constraint_free(c);
+ return isl_stat_ok;
+ }
+
+ n_div = isl_constraint_dim(c, isl_dim_div);
+ if (n_div < 0)
+ goto error;
+ ctx = isl_constraint_get_ctx(c);
+ stride = isl_val_zero(ctx);
+ for (i = 0; i < n_div; ++i) {
+ v = isl_constraint_get_coefficient_val(c, isl_dim_div, i);
+ stride = isl_val_gcd(stride, v);
+ }
+
+ v = isl_constraint_get_coefficient_val(c, isl_dim_set, data->pos);
+ m = isl_val_gcd(isl_val_copy(stride), isl_val_copy(v));
+ stride = isl_val_div(stride, isl_val_copy(m));
+ v = isl_val_div(v, isl_val_copy(m));
+
+ has_stride = isl_val_gt_si(stride, 1);
+ if (has_stride >= 0 && has_stride) {
+ isl_aff *aff;
+ isl_val *gcd, *a, *b;
+
+ gcd = isl_val_gcdext(v, isl_val_copy(stride), &a, &b);
+ isl_val_free(gcd);
+ isl_val_free(b);
+
+ aff = isl_constraint_get_aff(c);
+ for (i = 0; i < n_div; ++i)
+ aff = isl_aff_set_coefficient_si(aff,
+ isl_dim_div, i, 0);
+ aff = isl_aff_set_coefficient_si(aff, isl_dim_in, data->pos, 0);
+ aff = isl_aff_remove_unused_divs(aff);
+ a = isl_val_neg(a);
+ aff = isl_aff_scale_val(aff, a);
+ aff = isl_aff_scale_down_val(aff, m);
+ r = set_stride(data, stride, aff);
+ } else {
+ isl_val_free(stride);
+ isl_val_free(m);
+ isl_val_free(v);
+ }
+
+ isl_constraint_free(c);
+ if (has_stride < 0)
+ return isl_stat_error;
+ return r;
+error:
+ isl_constraint_free(c);
+ return isl_stat_error;
+}
+
+/* Check if the constraints in "set" imply any stride on set dimension "pos" and
+ * store the results in data->stride and data->offset.
+ *
+ * In particular, compute the affine hull and then check if
+ * any of the constraints in the hull impose any stride on the dimension.
+ * If no such constraint can be found, then the offset is taken
+ * to be the zero expression and the stride is taken to be one.
+ */
+static void set_detect_stride(__isl_keep isl_set *set, int pos,
+ struct isl_detect_stride_data *data)
+{
+ isl_basic_set *hull;
+
+ hull = isl_set_affine_hull(isl_set_copy(set));
+
+ data->pos = pos;
+ data->found = 0;
+ data->stride = NULL;
+ data->offset = NULL;
+ if (isl_basic_set_foreach_constraint(hull, &detect_stride, data) < 0)
+ goto error;
+
+ if (!data->found) {
+ data->stride = isl_val_one(isl_set_get_ctx(set));
+ if (data->want_offset) {
+ isl_space *space;
+ isl_local_space *ls;
+
+ space = isl_set_get_space(set);
+ ls = isl_local_space_from_space(space);
+ data->offset = isl_aff_zero_on_domain(ls);
+ }
+ }
+ isl_basic_set_free(hull);
+ return;
+error:
+ isl_basic_set_free(hull);
+ data->stride = isl_val_free(data->stride);
+ data->offset = isl_aff_free(data->offset);
+}
+
+/* Check if the constraints in "set" imply any stride on set dimension "pos" and
+ * return the results in the form of an offset and a stride.
+ */
+__isl_give isl_stride_info *isl_set_get_stride_info(__isl_keep isl_set *set,
+ int pos)
+{
+ struct isl_detect_stride_data data;
+
+ data.want_offset = 1;
+ set_detect_stride(set, pos, &data);
+
+ return isl_stride_info_alloc(data.stride, data.offset);
+}
+
+/* Check if the constraints in "set" imply any stride on set dimension "pos" and
+ * return this stride.
+ */
+__isl_give isl_val *isl_set_get_stride(__isl_keep isl_set *set, int pos)
+{
+ struct isl_detect_stride_data data;
+
+ data.want_offset = 0;
+ set_detect_stride(set, pos, &data);
+
+ return data.stride;
+}
+
+/* Check if the constraints in "map" imply any stride on output dimension "pos",
+ * independently of any other output dimensions, and
+ * return the results in the form of an offset and a stride.
+ *
+ * Convert the input to a set with only the input dimensions and
+ * the single output dimension such that it be passed to
+ * isl_set_get_stride_info and convert the result back to
+ * an expression defined over the domain of "map".
+ */
+__isl_give isl_stride_info *isl_map_get_range_stride_info(
+ __isl_keep isl_map *map, int pos)
+{
+ isl_stride_info *si;
+ isl_set *set;
+ isl_size n_in;
+
+ n_in = isl_map_dim(map, isl_dim_in);
+ if (n_in < 0)
+ return NULL;
+ map = isl_map_copy(map);
+ map = isl_map_project_onto(map, isl_dim_out, pos, 1);
+ set = isl_map_wrap(map);
+ si = isl_set_get_stride_info(set, n_in);
+ isl_set_free(set);
+ if (!si)
+ return NULL;
+ si->offset = isl_aff_domain_factor_domain(si->offset);
+ if (!si->offset)
+ return isl_stride_info_free(si);
+ return si;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab.c
new file mode 100644
index 00000000000..4152735682f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab.c
@@ -0,0 +1,4259 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2013 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ * Copyright 2016 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+#include "isl_map_private.h"
+#include "isl_tab.h"
+#include <isl_seq.h>
+#include <isl_config.h>
+
+#include <bset_to_bmap.c>
+#include <bset_from_bmap.c>
+
+/*
+ * The implementation of tableaus in this file was inspired by Section 8
+ * of David Detlefs, Greg Nelson and James B. Saxe, "Simplify: a theorem
+ * prover for program checking".
+ */
+
+struct isl_tab *isl_tab_alloc(struct isl_ctx *ctx,
+ unsigned n_row, unsigned n_var, unsigned M)
+{
+ int i;
+ struct isl_tab *tab;
+ unsigned off = 2 + M;
+
+ tab = isl_calloc_type(ctx, struct isl_tab);
+ if (!tab)
+ return NULL;
+ tab->mat = isl_mat_alloc(ctx, n_row, off + n_var);
+ if (!tab->mat)
+ goto error;
+ tab->var = isl_alloc_array(ctx, struct isl_tab_var, n_var);
+ if (n_var && !tab->var)
+ goto error;
+ tab->con = isl_alloc_array(ctx, struct isl_tab_var, n_row);
+ if (n_row && !tab->con)
+ goto error;
+ tab->col_var = isl_alloc_array(ctx, int, n_var);
+ if (n_var && !tab->col_var)
+ goto error;
+ tab->row_var = isl_alloc_array(ctx, int, n_row);
+ if (n_row && !tab->row_var)
+ goto error;
+ for (i = 0; i < n_var; ++i) {
+ tab->var[i].index = i;
+ tab->var[i].is_row = 0;
+ tab->var[i].is_nonneg = 0;
+ tab->var[i].is_zero = 0;
+ tab->var[i].is_redundant = 0;
+ tab->var[i].frozen = 0;
+ tab->var[i].negated = 0;
+ tab->col_var[i] = i;
+ }
+ tab->n_row = 0;
+ tab->n_con = 0;
+ tab->n_eq = 0;
+ tab->max_con = n_row;
+ tab->n_col = n_var;
+ tab->n_var = n_var;
+ tab->max_var = n_var;
+ tab->n_param = 0;
+ tab->n_div = 0;
+ tab->n_dead = 0;
+ tab->n_redundant = 0;
+ tab->strict_redundant = 0;
+ tab->need_undo = 0;
+ tab->rational = 0;
+ tab->empty = 0;
+ tab->in_undo = 0;
+ tab->M = M;
+ tab->cone = 0;
+ tab->bottom.type = isl_tab_undo_bottom;
+ tab->bottom.next = NULL;
+ tab->top = &tab->bottom;
+
+ tab->n_zero = 0;
+ tab->n_unbounded = 0;
+ tab->basis = NULL;
+
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+isl_ctx *isl_tab_get_ctx(struct isl_tab *tab)
+{
+ return tab ? isl_mat_get_ctx(tab->mat) : NULL;
+}
+
+int isl_tab_extend_cons(struct isl_tab *tab, unsigned n_new)
+{
+ unsigned off;
+
+ if (!tab)
+ return -1;
+
+ off = 2 + tab->M;
+
+ if (tab->max_con < tab->n_con + n_new) {
+ struct isl_tab_var *con;
+
+ con = isl_realloc_array(tab->mat->ctx, tab->con,
+ struct isl_tab_var, tab->max_con + n_new);
+ if (!con)
+ return -1;
+ tab->con = con;
+ tab->max_con += n_new;
+ }
+ if (tab->mat->n_row < tab->n_row + n_new) {
+ int *row_var;
+
+ tab->mat = isl_mat_extend(tab->mat,
+ tab->n_row + n_new, off + tab->n_col);
+ if (!tab->mat)
+ return -1;
+ row_var = isl_realloc_array(tab->mat->ctx, tab->row_var,
+ int, tab->mat->n_row);
+ if (!row_var)
+ return -1;
+ tab->row_var = row_var;
+ if (tab->row_sign) {
+ enum isl_tab_row_sign *s;
+ s = isl_realloc_array(tab->mat->ctx, tab->row_sign,
+ enum isl_tab_row_sign, tab->mat->n_row);
+ if (!s)
+ return -1;
+ tab->row_sign = s;
+ }
+ }
+ return 0;
+}
+
+/* Make room for at least n_new extra variables.
+ * Return -1 if anything went wrong.
+ */
+int isl_tab_extend_vars(struct isl_tab *tab, unsigned n_new)
+{
+ struct isl_tab_var *var;
+ unsigned off = 2 + tab->M;
+
+ if (tab->max_var < tab->n_var + n_new) {
+ var = isl_realloc_array(tab->mat->ctx, tab->var,
+ struct isl_tab_var, tab->n_var + n_new);
+ if (!var)
+ return -1;
+ tab->var = var;
+ tab->max_var = tab->n_var + n_new;
+ }
+
+ if (tab->mat->n_col < off + tab->n_col + n_new) {
+ int *p;
+
+ tab->mat = isl_mat_extend(tab->mat,
+ tab->mat->n_row, off + tab->n_col + n_new);
+ if (!tab->mat)
+ return -1;
+ p = isl_realloc_array(tab->mat->ctx, tab->col_var,
+ int, tab->n_col + n_new);
+ if (!p)
+ return -1;
+ tab->col_var = p;
+ }
+
+ return 0;
+}
+
+static void free_undo_record(struct isl_tab_undo *undo)
+{
+ switch (undo->type) {
+ case isl_tab_undo_saved_basis:
+ free(undo->u.col_var);
+ break;
+ default:;
+ }
+ free(undo);
+}
+
+static void free_undo(struct isl_tab *tab)
+{
+ struct isl_tab_undo *undo, *next;
+
+ for (undo = tab->top; undo && undo != &tab->bottom; undo = next) {
+ next = undo->next;
+ free_undo_record(undo);
+ }
+ tab->top = undo;
+}
+
+void isl_tab_free(struct isl_tab *tab)
+{
+ if (!tab)
+ return;
+ free_undo(tab);
+ isl_mat_free(tab->mat);
+ isl_vec_free(tab->dual);
+ isl_basic_map_free(tab->bmap);
+ free(tab->var);
+ free(tab->con);
+ free(tab->row_var);
+ free(tab->col_var);
+ free(tab->row_sign);
+ isl_mat_free(tab->samples);
+ free(tab->sample_index);
+ isl_mat_free(tab->basis);
+ free(tab);
+}
+
+struct isl_tab *isl_tab_dup(struct isl_tab *tab)
+{
+ int i;
+ struct isl_tab *dup;
+ unsigned off;
+
+ if (!tab)
+ return NULL;
+
+ off = 2 + tab->M;
+ dup = isl_calloc_type(tab->mat->ctx, struct isl_tab);
+ if (!dup)
+ return NULL;
+ dup->mat = isl_mat_dup(tab->mat);
+ if (!dup->mat)
+ goto error;
+ dup->var = isl_alloc_array(tab->mat->ctx, struct isl_tab_var, tab->max_var);
+ if (tab->max_var && !dup->var)
+ goto error;
+ for (i = 0; i < tab->n_var; ++i)
+ dup->var[i] = tab->var[i];
+ dup->con = isl_alloc_array(tab->mat->ctx, struct isl_tab_var, tab->max_con);
+ if (tab->max_con && !dup->con)
+ goto error;
+ for (i = 0; i < tab->n_con; ++i)
+ dup->con[i] = tab->con[i];
+ dup->col_var = isl_alloc_array(tab->mat->ctx, int, tab->mat->n_col - off);
+ if ((tab->mat->n_col - off) && !dup->col_var)
+ goto error;
+ for (i = 0; i < tab->n_col; ++i)
+ dup->col_var[i] = tab->col_var[i];
+ dup->row_var = isl_alloc_array(tab->mat->ctx, int, tab->mat->n_row);
+ if (tab->mat->n_row && !dup->row_var)
+ goto error;
+ for (i = 0; i < tab->n_row; ++i)
+ dup->row_var[i] = tab->row_var[i];
+ if (tab->row_sign) {
+ dup->row_sign = isl_alloc_array(tab->mat->ctx, enum isl_tab_row_sign,
+ tab->mat->n_row);
+ if (tab->mat->n_row && !dup->row_sign)
+ goto error;
+ for (i = 0; i < tab->n_row; ++i)
+ dup->row_sign[i] = tab->row_sign[i];
+ }
+ if (tab->samples) {
+ dup->samples = isl_mat_dup(tab->samples);
+ if (!dup->samples)
+ goto error;
+ dup->sample_index = isl_alloc_array(tab->mat->ctx, int,
+ tab->samples->n_row);
+ if (tab->samples->n_row && !dup->sample_index)
+ goto error;
+ dup->n_sample = tab->n_sample;
+ dup->n_outside = tab->n_outside;
+ }
+ dup->n_row = tab->n_row;
+ dup->n_con = tab->n_con;
+ dup->n_eq = tab->n_eq;
+ dup->max_con = tab->max_con;
+ dup->n_col = tab->n_col;
+ dup->n_var = tab->n_var;
+ dup->max_var = tab->max_var;
+ dup->n_param = tab->n_param;
+ dup->n_div = tab->n_div;
+ dup->n_dead = tab->n_dead;
+ dup->n_redundant = tab->n_redundant;
+ dup->rational = tab->rational;
+ dup->empty = tab->empty;
+ dup->strict_redundant = 0;
+ dup->need_undo = 0;
+ dup->in_undo = 0;
+ dup->M = tab->M;
+ dup->cone = tab->cone;
+ dup->bottom.type = isl_tab_undo_bottom;
+ dup->bottom.next = NULL;
+ dup->top = &dup->bottom;
+
+ dup->n_zero = tab->n_zero;
+ dup->n_unbounded = tab->n_unbounded;
+ dup->basis = isl_mat_dup(tab->basis);
+
+ return dup;
+error:
+ isl_tab_free(dup);
+ return NULL;
+}
+
+/* Construct the coefficient matrix of the product tableau
+ * of two tableaus.
+ * mat{1,2} is the coefficient matrix of tableau {1,2}
+ * row{1,2} is the number of rows in tableau {1,2}
+ * col{1,2} is the number of columns in tableau {1,2}
+ * off is the offset to the coefficient column (skipping the
+ * denominator, the constant term and the big parameter if any)
+ * r{1,2} is the number of redundant rows in tableau {1,2}
+ * d{1,2} is the number of dead columns in tableau {1,2}
+ *
+ * The order of the rows and columns in the result is as explained
+ * in isl_tab_product.
+ */
+static __isl_give isl_mat *tab_mat_product(__isl_keep isl_mat *mat1,
+ __isl_keep isl_mat *mat2, unsigned row1, unsigned row2,
+ unsigned col1, unsigned col2,
+ unsigned off, unsigned r1, unsigned r2, unsigned d1, unsigned d2)
+{
+ int i;
+ struct isl_mat *prod;
+ unsigned n;
+
+ prod = isl_mat_alloc(mat1->ctx, mat1->n_row + mat2->n_row,
+ off + col1 + col2);
+ if (!prod)
+ return NULL;
+
+ n = 0;
+ for (i = 0; i < r1; ++i) {
+ isl_seq_cpy(prod->row[n + i], mat1->row[i], off + d1);
+ isl_seq_clr(prod->row[n + i] + off + d1, d2);
+ isl_seq_cpy(prod->row[n + i] + off + d1 + d2,
+ mat1->row[i] + off + d1, col1 - d1);
+ isl_seq_clr(prod->row[n + i] + off + col1 + d1, col2 - d2);
+ }
+
+ n += r1;
+ for (i = 0; i < r2; ++i) {
+ isl_seq_cpy(prod->row[n + i], mat2->row[i], off);
+ isl_seq_clr(prod->row[n + i] + off, d1);
+ isl_seq_cpy(prod->row[n + i] + off + d1,
+ mat2->row[i] + off, d2);
+ isl_seq_clr(prod->row[n + i] + off + d1 + d2, col1 - d1);
+ isl_seq_cpy(prod->row[n + i] + off + col1 + d1,
+ mat2->row[i] + off + d2, col2 - d2);
+ }
+
+ n += r2;
+ for (i = 0; i < row1 - r1; ++i) {
+ isl_seq_cpy(prod->row[n + i], mat1->row[r1 + i], off + d1);
+ isl_seq_clr(prod->row[n + i] + off + d1, d2);
+ isl_seq_cpy(prod->row[n + i] + off + d1 + d2,
+ mat1->row[r1 + i] + off + d1, col1 - d1);
+ isl_seq_clr(prod->row[n + i] + off + col1 + d1, col2 - d2);
+ }
+
+ n += row1 - r1;
+ for (i = 0; i < row2 - r2; ++i) {
+ isl_seq_cpy(prod->row[n + i], mat2->row[r2 + i], off);
+ isl_seq_clr(prod->row[n + i] + off, d1);
+ isl_seq_cpy(prod->row[n + i] + off + d1,
+ mat2->row[r2 + i] + off, d2);
+ isl_seq_clr(prod->row[n + i] + off + d1 + d2, col1 - d1);
+ isl_seq_cpy(prod->row[n + i] + off + col1 + d1,
+ mat2->row[r2 + i] + off + d2, col2 - d2);
+ }
+
+ return prod;
+}
+
+/* Update the row or column index of a variable that corresponds
+ * to a variable in the first input tableau.
+ */
+static void update_index1(struct isl_tab_var *var,
+ unsigned r1, unsigned r2, unsigned d1, unsigned d2)
+{
+ if (var->index == -1)
+ return;
+ if (var->is_row && var->index >= r1)
+ var->index += r2;
+ if (!var->is_row && var->index >= d1)
+ var->index += d2;
+}
+
+/* Update the row or column index of a variable that corresponds
+ * to a variable in the second input tableau.
+ */
+static void update_index2(struct isl_tab_var *var,
+ unsigned row1, unsigned col1,
+ unsigned r1, unsigned r2, unsigned d1, unsigned d2)
+{
+ if (var->index == -1)
+ return;
+ if (var->is_row) {
+ if (var->index < r2)
+ var->index += r1;
+ else
+ var->index += row1;
+ } else {
+ if (var->index < d2)
+ var->index += d1;
+ else
+ var->index += col1;
+ }
+}
+
+/* Create a tableau that represents the Cartesian product of the sets
+ * represented by tableaus tab1 and tab2.
+ * The order of the rows in the product is
+ * - redundant rows of tab1
+ * - redundant rows of tab2
+ * - non-redundant rows of tab1
+ * - non-redundant rows of tab2
+ * The order of the columns is
+ * - denominator
+ * - constant term
+ * - coefficient of big parameter, if any
+ * - dead columns of tab1
+ * - dead columns of tab2
+ * - live columns of tab1
+ * - live columns of tab2
+ * The order of the variables and the constraints is a concatenation
+ * of order in the two input tableaus.
+ */
+struct isl_tab *isl_tab_product(struct isl_tab *tab1, struct isl_tab *tab2)
+{
+ int i;
+ struct isl_tab *prod;
+ unsigned off;
+ unsigned r1, r2, d1, d2;
+
+ if (!tab1 || !tab2)
+ return NULL;
+
+ isl_assert(tab1->mat->ctx, tab1->M == tab2->M, return NULL);
+ isl_assert(tab1->mat->ctx, tab1->rational == tab2->rational, return NULL);
+ isl_assert(tab1->mat->ctx, tab1->cone == tab2->cone, return NULL);
+ isl_assert(tab1->mat->ctx, !tab1->row_sign, return NULL);
+ isl_assert(tab1->mat->ctx, !tab2->row_sign, return NULL);
+ isl_assert(tab1->mat->ctx, tab1->n_param == 0, return NULL);
+ isl_assert(tab1->mat->ctx, tab2->n_param == 0, return NULL);
+ isl_assert(tab1->mat->ctx, tab1->n_div == 0, return NULL);
+ isl_assert(tab1->mat->ctx, tab2->n_div == 0, return NULL);
+
+ off = 2 + tab1->M;
+ r1 = tab1->n_redundant;
+ r2 = tab2->n_redundant;
+ d1 = tab1->n_dead;
+ d2 = tab2->n_dead;
+ prod = isl_calloc_type(tab1->mat->ctx, struct isl_tab);
+ if (!prod)
+ return NULL;
+ prod->mat = tab_mat_product(tab1->mat, tab2->mat,
+ tab1->n_row, tab2->n_row,
+ tab1->n_col, tab2->n_col, off, r1, r2, d1, d2);
+ if (!prod->mat)
+ goto error;
+ prod->var = isl_alloc_array(tab1->mat->ctx, struct isl_tab_var,
+ tab1->max_var + tab2->max_var);
+ if ((tab1->max_var + tab2->max_var) && !prod->var)
+ goto error;
+ for (i = 0; i < tab1->n_var; ++i) {
+ prod->var[i] = tab1->var[i];
+ update_index1(&prod->var[i], r1, r2, d1, d2);
+ }
+ for (i = 0; i < tab2->n_var; ++i) {
+ prod->var[tab1->n_var + i] = tab2->var[i];
+ update_index2(&prod->var[tab1->n_var + i],
+ tab1->n_row, tab1->n_col,
+ r1, r2, d1, d2);
+ }
+ prod->con = isl_alloc_array(tab1->mat->ctx, struct isl_tab_var,
+ tab1->max_con + tab2->max_con);
+ if ((tab1->max_con + tab2->max_con) && !prod->con)
+ goto error;
+ for (i = 0; i < tab1->n_con; ++i) {
+ prod->con[i] = tab1->con[i];
+ update_index1(&prod->con[i], r1, r2, d1, d2);
+ }
+ for (i = 0; i < tab2->n_con; ++i) {
+ prod->con[tab1->n_con + i] = tab2->con[i];
+ update_index2(&prod->con[tab1->n_con + i],
+ tab1->n_row, tab1->n_col,
+ r1, r2, d1, d2);
+ }
+ prod->col_var = isl_alloc_array(tab1->mat->ctx, int,
+ tab1->n_col + tab2->n_col);
+ if ((tab1->n_col + tab2->n_col) && !prod->col_var)
+ goto error;
+ for (i = 0; i < tab1->n_col; ++i) {
+ int pos = i < d1 ? i : i + d2;
+ prod->col_var[pos] = tab1->col_var[i];
+ }
+ for (i = 0; i < tab2->n_col; ++i) {
+ int pos = i < d2 ? d1 + i : tab1->n_col + i;
+ int t = tab2->col_var[i];
+ if (t >= 0)
+ t += tab1->n_var;
+ else
+ t -= tab1->n_con;
+ prod->col_var[pos] = t;
+ }
+ prod->row_var = isl_alloc_array(tab1->mat->ctx, int,
+ tab1->mat->n_row + tab2->mat->n_row);
+ if ((tab1->mat->n_row + tab2->mat->n_row) && !prod->row_var)
+ goto error;
+ for (i = 0; i < tab1->n_row; ++i) {
+ int pos = i < r1 ? i : i + r2;
+ prod->row_var[pos] = tab1->row_var[i];
+ }
+ for (i = 0; i < tab2->n_row; ++i) {
+ int pos = i < r2 ? r1 + i : tab1->n_row + i;
+ int t = tab2->row_var[i];
+ if (t >= 0)
+ t += tab1->n_var;
+ else
+ t -= tab1->n_con;
+ prod->row_var[pos] = t;
+ }
+ prod->samples = NULL;
+ prod->sample_index = NULL;
+ prod->n_row = tab1->n_row + tab2->n_row;
+ prod->n_con = tab1->n_con + tab2->n_con;
+ prod->n_eq = 0;
+ prod->max_con = tab1->max_con + tab2->max_con;
+ prod->n_col = tab1->n_col + tab2->n_col;
+ prod->n_var = tab1->n_var + tab2->n_var;
+ prod->max_var = tab1->max_var + tab2->max_var;
+ prod->n_param = 0;
+ prod->n_div = 0;
+ prod->n_dead = tab1->n_dead + tab2->n_dead;
+ prod->n_redundant = tab1->n_redundant + tab2->n_redundant;
+ prod->rational = tab1->rational;
+ prod->empty = tab1->empty || tab2->empty;
+ prod->strict_redundant = tab1->strict_redundant || tab2->strict_redundant;
+ prod->need_undo = 0;
+ prod->in_undo = 0;
+ prod->M = tab1->M;
+ prod->cone = tab1->cone;
+ prod->bottom.type = isl_tab_undo_bottom;
+ prod->bottom.next = NULL;
+ prod->top = &prod->bottom;
+
+ prod->n_zero = 0;
+ prod->n_unbounded = 0;
+ prod->basis = NULL;
+
+ return prod;
+error:
+ isl_tab_free(prod);
+ return NULL;
+}
+
+static struct isl_tab_var *var_from_index(struct isl_tab *tab, int i)
+{
+ if (i >= 0)
+ return &tab->var[i];
+ else
+ return &tab->con[~i];
+}
+
+struct isl_tab_var *isl_tab_var_from_row(struct isl_tab *tab, int i)
+{
+ return var_from_index(tab, tab->row_var[i]);
+}
+
+static struct isl_tab_var *var_from_col(struct isl_tab *tab, int i)
+{
+ return var_from_index(tab, tab->col_var[i]);
+}
+
+/* Check if there are any upper bounds on column variable "var",
+ * i.e., non-negative rows where var appears with a negative coefficient.
+ * Return 1 if there are no such bounds.
+ */
+static int max_is_manifestly_unbounded(struct isl_tab *tab,
+ struct isl_tab_var *var)
+{
+ int i;
+ unsigned off = 2 + tab->M;
+
+ if (var->is_row)
+ return 0;
+ for (i = tab->n_redundant; i < tab->n_row; ++i) {
+ if (!isl_int_is_neg(tab->mat->row[i][off + var->index]))
+ continue;
+ if (isl_tab_var_from_row(tab, i)->is_nonneg)
+ return 0;
+ }
+ return 1;
+}
+
+/* Check if there are any lower bounds on column variable "var",
+ * i.e., non-negative rows where var appears with a positive coefficient.
+ * Return 1 if there are no such bounds.
+ */
+static int min_is_manifestly_unbounded(struct isl_tab *tab,
+ struct isl_tab_var *var)
+{
+ int i;
+ unsigned off = 2 + tab->M;
+
+ if (var->is_row)
+ return 0;
+ for (i = tab->n_redundant; i < tab->n_row; ++i) {
+ if (!isl_int_is_pos(tab->mat->row[i][off + var->index]))
+ continue;
+ if (isl_tab_var_from_row(tab, i)->is_nonneg)
+ return 0;
+ }
+ return 1;
+}
+
+static int row_cmp(struct isl_tab *tab, int r1, int r2, int c, isl_int *t)
+{
+ unsigned off = 2 + tab->M;
+
+ if (tab->M) {
+ int s;
+ isl_int_mul(*t, tab->mat->row[r1][2], tab->mat->row[r2][off+c]);
+ isl_int_submul(*t, tab->mat->row[r2][2], tab->mat->row[r1][off+c]);
+ s = isl_int_sgn(*t);
+ if (s)
+ return s;
+ }
+ isl_int_mul(*t, tab->mat->row[r1][1], tab->mat->row[r2][off + c]);
+ isl_int_submul(*t, tab->mat->row[r2][1], tab->mat->row[r1][off + c]);
+ return isl_int_sgn(*t);
+}
+
+/* Given the index of a column "c", return the index of a row
+ * that can be used to pivot the column in, with either an increase
+ * (sgn > 0) or a decrease (sgn < 0) of the corresponding variable.
+ * If "var" is not NULL, then the row returned will be different from
+ * the one associated with "var".
+ *
+ * Each row in the tableau is of the form
+ *
+ * x_r = a_r0 + \sum_i a_ri x_i
+ *
+ * Only rows with x_r >= 0 and with the sign of a_ri opposite to "sgn"
+ * impose any limit on the increase or decrease in the value of x_c
+ * and this bound is equal to a_r0 / |a_rc|. We are therefore looking
+ * for the row with the smallest (most stringent) such bound.
+ * Note that the common denominator of each row drops out of the fraction.
+ * To check if row j has a smaller bound than row r, i.e.,
+ * a_j0 / |a_jc| < a_r0 / |a_rc| or a_j0 |a_rc| < a_r0 |a_jc|,
+ * we check if -sign(a_jc) (a_j0 a_rc - a_r0 a_jc) < 0,
+ * where -sign(a_jc) is equal to "sgn".
+ */
+static int pivot_row(struct isl_tab *tab,
+ struct isl_tab_var *var, int sgn, int c)
+{
+ int j, r, tsgn;
+ isl_int t;
+ unsigned off = 2 + tab->M;
+
+ isl_int_init(t);
+ r = -1;
+ for (j = tab->n_redundant; j < tab->n_row; ++j) {
+ if (var && j == var->index)
+ continue;
+ if (!isl_tab_var_from_row(tab, j)->is_nonneg)
+ continue;
+ if (sgn * isl_int_sgn(tab->mat->row[j][off + c]) >= 0)
+ continue;
+ if (r < 0) {
+ r = j;
+ continue;
+ }
+ tsgn = sgn * row_cmp(tab, r, j, c, &t);
+ if (tsgn < 0 || (tsgn == 0 &&
+ tab->row_var[j] < tab->row_var[r]))
+ r = j;
+ }
+ isl_int_clear(t);
+ return r;
+}
+
+/* Find a pivot (row and col) that will increase (sgn > 0) or decrease
+ * (sgn < 0) the value of row variable var.
+ * If not NULL, then skip_var is a row variable that should be ignored
+ * while looking for a pivot row. It is usually equal to var.
+ *
+ * As the given row in the tableau is of the form
+ *
+ * x_r = a_r0 + \sum_i a_ri x_i
+ *
+ * we need to find a column such that the sign of a_ri is equal to "sgn"
+ * (such that an increase in x_i will have the desired effect) or a
+ * column with a variable that may attain negative values.
+ * If a_ri is positive, then we need to move x_i in the same direction
+ * to obtain the desired effect. Otherwise, x_i has to move in the
+ * opposite direction.
+ */
+static void find_pivot(struct isl_tab *tab,
+ struct isl_tab_var *var, struct isl_tab_var *skip_var,
+ int sgn, int *row, int *col)
+{
+ int j, r, c;
+ isl_int *tr;
+
+ *row = *col = -1;
+
+ isl_assert(tab->mat->ctx, var->is_row, return);
+ tr = tab->mat->row[var->index] + 2 + tab->M;
+
+ c = -1;
+ for (j = tab->n_dead; j < tab->n_col; ++j) {
+ if (isl_int_is_zero(tr[j]))
+ continue;
+ if (isl_int_sgn(tr[j]) != sgn &&
+ var_from_col(tab, j)->is_nonneg)
+ continue;
+ if (c < 0 || tab->col_var[j] < tab->col_var[c])
+ c = j;
+ }
+ if (c < 0)
+ return;
+
+ sgn *= isl_int_sgn(tr[c]);
+ r = pivot_row(tab, skip_var, sgn, c);
+ *row = r < 0 ? var->index : r;
+ *col = c;
+}
+
+/* Return 1 if row "row" represents an obviously redundant inequality.
+ * This means
+ * - it represents an inequality or a variable
+ * - that is the sum of a non-negative sample value and a positive
+ * combination of zero or more non-negative constraints.
+ */
+int isl_tab_row_is_redundant(struct isl_tab *tab, int row)
+{
+ int i;
+ unsigned off = 2 + tab->M;
+
+ if (tab->row_var[row] < 0 && !isl_tab_var_from_row(tab, row)->is_nonneg)
+ return 0;
+
+ if (isl_int_is_neg(tab->mat->row[row][1]))
+ return 0;
+ if (tab->strict_redundant && isl_int_is_zero(tab->mat->row[row][1]))
+ return 0;
+ if (tab->M && isl_int_is_neg(tab->mat->row[row][2]))
+ return 0;
+
+ for (i = tab->n_dead; i < tab->n_col; ++i) {
+ if (isl_int_is_zero(tab->mat->row[row][off + i]))
+ continue;
+ if (tab->col_var[i] >= 0)
+ return 0;
+ if (isl_int_is_neg(tab->mat->row[row][off + i]))
+ return 0;
+ if (!var_from_col(tab, i)->is_nonneg)
+ return 0;
+ }
+ return 1;
+}
+
+static void swap_rows(struct isl_tab *tab, int row1, int row2)
+{
+ int t;
+ enum isl_tab_row_sign s;
+
+ t = tab->row_var[row1];
+ tab->row_var[row1] = tab->row_var[row2];
+ tab->row_var[row2] = t;
+ isl_tab_var_from_row(tab, row1)->index = row1;
+ isl_tab_var_from_row(tab, row2)->index = row2;
+ tab->mat = isl_mat_swap_rows(tab->mat, row1, row2);
+
+ if (!tab->row_sign)
+ return;
+ s = tab->row_sign[row1];
+ tab->row_sign[row1] = tab->row_sign[row2];
+ tab->row_sign[row2] = s;
+}
+
+static isl_stat push_union(struct isl_tab *tab,
+ enum isl_tab_undo_type type, union isl_tab_undo_val u) WARN_UNUSED;
+
+/* Push record "u" onto the undo stack of "tab", provided "tab"
+ * keeps track of undo information.
+ *
+ * If the record cannot be pushed, then mark the undo stack as invalid
+ * such that a later rollback attempt will not try to undo earlier
+ * records without having been able to undo the current record.
+ */
+static isl_stat push_union(struct isl_tab *tab,
+ enum isl_tab_undo_type type, union isl_tab_undo_val u)
+{
+ struct isl_tab_undo *undo;
+
+ if (!tab)
+ return isl_stat_error;
+ if (!tab->need_undo)
+ return isl_stat_ok;
+
+ undo = isl_alloc_type(tab->mat->ctx, struct isl_tab_undo);
+ if (!undo)
+ goto error;
+ undo->type = type;
+ undo->u = u;
+ undo->next = tab->top;
+ tab->top = undo;
+
+ return isl_stat_ok;
+error:
+ free_undo(tab);
+ tab->top = NULL;
+ return isl_stat_error;
+}
+
+isl_stat isl_tab_push_var(struct isl_tab *tab,
+ enum isl_tab_undo_type type, struct isl_tab_var *var)
+{
+ union isl_tab_undo_val u;
+ if (var->is_row)
+ u.var_index = tab->row_var[var->index];
+ else
+ u.var_index = tab->col_var[var->index];
+ return push_union(tab, type, u);
+}
+
+isl_stat isl_tab_push(struct isl_tab *tab, enum isl_tab_undo_type type)
+{
+ union isl_tab_undo_val u = { 0 };
+ return push_union(tab, type, u);
+}
+
+/* Push a record on the undo stack describing the current basic
+ * variables, so that the this state can be restored during rollback.
+ */
+isl_stat isl_tab_push_basis(struct isl_tab *tab)
+{
+ int i;
+ union isl_tab_undo_val u;
+
+ u.col_var = isl_alloc_array(tab->mat->ctx, int, tab->n_col);
+ if (tab->n_col && !u.col_var)
+ return isl_stat_error;
+ for (i = 0; i < tab->n_col; ++i)
+ u.col_var[i] = tab->col_var[i];
+ return push_union(tab, isl_tab_undo_saved_basis, u);
+}
+
+isl_stat isl_tab_push_callback(struct isl_tab *tab,
+ struct isl_tab_callback *callback)
+{
+ union isl_tab_undo_val u;
+ u.callback = callback;
+ return push_union(tab, isl_tab_undo_callback, u);
+}
+
+struct isl_tab *isl_tab_init_samples(struct isl_tab *tab)
+{
+ if (!tab)
+ return NULL;
+
+ tab->n_sample = 0;
+ tab->n_outside = 0;
+ tab->samples = isl_mat_alloc(tab->mat->ctx, 1, 1 + tab->n_var);
+ if (!tab->samples)
+ goto error;
+ tab->sample_index = isl_alloc_array(tab->mat->ctx, int, 1);
+ if (!tab->sample_index)
+ goto error;
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+int isl_tab_add_sample(struct isl_tab *tab, __isl_take isl_vec *sample)
+{
+ if (!tab || !sample)
+ goto error;
+
+ if (tab->n_sample + 1 > tab->samples->n_row) {
+ int *t = isl_realloc_array(tab->mat->ctx,
+ tab->sample_index, int, tab->n_sample + 1);
+ if (!t)
+ goto error;
+ tab->sample_index = t;
+ }
+
+ tab->samples = isl_mat_extend(tab->samples,
+ tab->n_sample + 1, tab->samples->n_col);
+ if (!tab->samples)
+ goto error;
+
+ isl_seq_cpy(tab->samples->row[tab->n_sample], sample->el, sample->size);
+ isl_vec_free(sample);
+ tab->sample_index[tab->n_sample] = tab->n_sample;
+ tab->n_sample++;
+
+ return 0;
+error:
+ isl_vec_free(sample);
+ return -1;
+}
+
+struct isl_tab *isl_tab_drop_sample(struct isl_tab *tab, int s)
+{
+ if (s != tab->n_outside) {
+ int t = tab->sample_index[tab->n_outside];
+ tab->sample_index[tab->n_outside] = tab->sample_index[s];
+ tab->sample_index[s] = t;
+ isl_mat_swap_rows(tab->samples, tab->n_outside, s);
+ }
+ tab->n_outside++;
+ if (isl_tab_push(tab, isl_tab_undo_drop_sample) < 0) {
+ isl_tab_free(tab);
+ return NULL;
+ }
+
+ return tab;
+}
+
+/* Record the current number of samples so that we can remove newer
+ * samples during a rollback.
+ */
+isl_stat isl_tab_save_samples(struct isl_tab *tab)
+{
+ union isl_tab_undo_val u;
+
+ if (!tab)
+ return isl_stat_error;
+
+ u.n = tab->n_sample;
+ return push_union(tab, isl_tab_undo_saved_samples, u);
+}
+
+/* Mark row with index "row" as being redundant.
+ * If we may need to undo the operation or if the row represents
+ * a variable of the original problem, the row is kept,
+ * but no longer considered when looking for a pivot row.
+ * Otherwise, the row is simply removed.
+ *
+ * The row may be interchanged with some other row. If it
+ * is interchanged with a later row, return 1. Otherwise return 0.
+ * If the rows are checked in order in the calling function,
+ * then a return value of 1 means that the row with the given
+ * row number may now contain a different row that hasn't been checked yet.
+ */
+int isl_tab_mark_redundant(struct isl_tab *tab, int row)
+{
+ struct isl_tab_var *var = isl_tab_var_from_row(tab, row);
+ var->is_redundant = 1;
+ isl_assert(tab->mat->ctx, row >= tab->n_redundant, return -1);
+ if (tab->preserve || tab->need_undo || tab->row_var[row] >= 0) {
+ if (tab->row_var[row] >= 0 && !var->is_nonneg) {
+ var->is_nonneg = 1;
+ if (isl_tab_push_var(tab, isl_tab_undo_nonneg, var) < 0)
+ return -1;
+ }
+ if (row != tab->n_redundant)
+ swap_rows(tab, row, tab->n_redundant);
+ tab->n_redundant++;
+ return isl_tab_push_var(tab, isl_tab_undo_redundant, var);
+ } else {
+ if (row != tab->n_row - 1)
+ swap_rows(tab, row, tab->n_row - 1);
+ isl_tab_var_from_row(tab, tab->n_row - 1)->index = -1;
+ tab->n_row--;
+ return 1;
+ }
+}
+
+/* Mark "tab" as a rational tableau.
+ * If it wasn't marked as a rational tableau already and if we may
+ * need to undo changes, then arrange for the marking to be undone
+ * during the undo.
+ */
+int isl_tab_mark_rational(struct isl_tab *tab)
+{
+ if (!tab)
+ return -1;
+ if (!tab->rational && tab->need_undo)
+ if (isl_tab_push(tab, isl_tab_undo_rational) < 0)
+ return -1;
+ tab->rational = 1;
+ return 0;
+}
+
+isl_stat isl_tab_mark_empty(struct isl_tab *tab)
+{
+ if (!tab)
+ return isl_stat_error;
+ if (!tab->empty && tab->need_undo)
+ if (isl_tab_push(tab, isl_tab_undo_empty) < 0)
+ return isl_stat_error;
+ tab->empty = 1;
+ return isl_stat_ok;
+}
+
+int isl_tab_freeze_constraint(struct isl_tab *tab, int con)
+{
+ struct isl_tab_var *var;
+
+ if (!tab)
+ return -1;
+
+ var = &tab->con[con];
+ if (var->frozen)
+ return 0;
+ if (var->index < 0)
+ return 0;
+ var->frozen = 1;
+
+ if (tab->need_undo)
+ return isl_tab_push_var(tab, isl_tab_undo_freeze, var);
+
+ return 0;
+}
+
+/* Update the rows signs after a pivot of "row" and "col", with "row_sgn"
+ * the original sign of the pivot element.
+ * We only keep track of row signs during PILP solving and in this case
+ * we only pivot a row with negative sign (meaning the value is always
+ * non-positive) using a positive pivot element.
+ *
+ * For each row j, the new value of the parametric constant is equal to
+ *
+ * a_j0 - a_jc a_r0/a_rc
+ *
+ * where a_j0 is the original parametric constant, a_rc is the pivot element,
+ * a_r0 is the parametric constant of the pivot row and a_jc is the
+ * pivot column entry of the row j.
+ * Since a_r0 is non-positive and a_rc is positive, the sign of row j
+ * remains the same if a_jc has the same sign as the row j or if
+ * a_jc is zero. In all other cases, we reset the sign to "unknown".
+ */
+static void update_row_sign(struct isl_tab *tab, int row, int col, int row_sgn)
+{
+ int i;
+ struct isl_mat *mat = tab->mat;
+ unsigned off = 2 + tab->M;
+
+ if (!tab->row_sign)
+ return;
+
+ if (tab->row_sign[row] == 0)
+ return;
+ isl_assert(mat->ctx, row_sgn > 0, return);
+ isl_assert(mat->ctx, tab->row_sign[row] == isl_tab_row_neg, return);
+ tab->row_sign[row] = isl_tab_row_pos;
+ for (i = 0; i < tab->n_row; ++i) {
+ int s;
+ if (i == row)
+ continue;
+ s = isl_int_sgn(mat->row[i][off + col]);
+ if (!s)
+ continue;
+ if (!tab->row_sign[i])
+ continue;
+ if (s < 0 && tab->row_sign[i] == isl_tab_row_neg)
+ continue;
+ if (s > 0 && tab->row_sign[i] == isl_tab_row_pos)
+ continue;
+ tab->row_sign[i] = isl_tab_row_unknown;
+ }
+}
+
+/* Given a row number "row" and a column number "col", pivot the tableau
+ * such that the associated variables are interchanged.
+ * The given row in the tableau expresses
+ *
+ * x_r = a_r0 + \sum_i a_ri x_i
+ *
+ * or
+ *
+ * x_c = 1/a_rc x_r - a_r0/a_rc + sum_{i \ne r} -a_ri/a_rc
+ *
+ * Substituting this equality into the other rows
+ *
+ * x_j = a_j0 + \sum_i a_ji x_i
+ *
+ * with a_jc \ne 0, we obtain
+ *
+ * x_j = a_jc/a_rc x_r + a_j0 - a_jc a_r0/a_rc + sum a_ji - a_jc a_ri/a_rc
+ *
+ * The tableau
+ *
+ * n_rc/d_r n_ri/d_r
+ * n_jc/d_j n_ji/d_j
+ *
+ * where i is any other column and j is any other row,
+ * is therefore transformed into
+ *
+ * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc|
+ * s(n_rc)d_r n_jc/(|n_rc| d_j) (n_ji |n_rc| - s(n_rc)n_jc n_ri)/(|n_rc| d_j)
+ *
+ * The transformation is performed along the following steps
+ *
+ * d_r/n_rc n_ri/n_rc
+ * n_jc/d_j n_ji/d_j
+ *
+ * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc|
+ * n_jc/d_j n_ji/d_j
+ *
+ * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc|
+ * n_jc/(|n_rc| d_j) n_ji/(|n_rc| d_j)
+ *
+ * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc|
+ * n_jc/(|n_rc| d_j) (n_ji |n_rc|)/(|n_rc| d_j)
+ *
+ * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc|
+ * n_jc/(|n_rc| d_j) (n_ji |n_rc| - s(n_rc)n_jc n_ri)/(|n_rc| d_j)
+ *
+ * s(n_rc)d_r/|n_rc| -s(n_rc)n_ri/|n_rc|
+ * s(n_rc)d_r n_jc/(|n_rc| d_j) (n_ji |n_rc| - s(n_rc)n_jc n_ri)/(|n_rc| d_j)
+ *
+ */
+int isl_tab_pivot(struct isl_tab *tab, int row, int col)
+{
+ int i, j;
+ int sgn;
+ int t;
+ isl_ctx *ctx;
+ struct isl_mat *mat = tab->mat;
+ struct isl_tab_var *var;
+ unsigned off = 2 + tab->M;
+
+ ctx = isl_tab_get_ctx(tab);
+ if (isl_ctx_next_operation(ctx) < 0)
+ return -1;
+
+ isl_int_swap(mat->row[row][0], mat->row[row][off + col]);
+ sgn = isl_int_sgn(mat->row[row][0]);
+ if (sgn < 0) {
+ isl_int_neg(mat->row[row][0], mat->row[row][0]);
+ isl_int_neg(mat->row[row][off + col], mat->row[row][off + col]);
+ } else
+ for (j = 0; j < off - 1 + tab->n_col; ++j) {
+ if (j == off - 1 + col)
+ continue;
+ isl_int_neg(mat->row[row][1 + j], mat->row[row][1 + j]);
+ }
+ if (!isl_int_is_one(mat->row[row][0]))
+ isl_seq_normalize(mat->ctx, mat->row[row], off + tab->n_col);
+ for (i = 0; i < tab->n_row; ++i) {
+ if (i == row)
+ continue;
+ if (isl_int_is_zero(mat->row[i][off + col]))
+ continue;
+ isl_int_mul(mat->row[i][0], mat->row[i][0], mat->row[row][0]);
+ for (j = 0; j < off - 1 + tab->n_col; ++j) {
+ if (j == off - 1 + col)
+ continue;
+ isl_int_mul(mat->row[i][1 + j],
+ mat->row[i][1 + j], mat->row[row][0]);
+ isl_int_addmul(mat->row[i][1 + j],
+ mat->row[i][off + col], mat->row[row][1 + j]);
+ }
+ isl_int_mul(mat->row[i][off + col],
+ mat->row[i][off + col], mat->row[row][off + col]);
+ if (!isl_int_is_one(mat->row[i][0]))
+ isl_seq_normalize(mat->ctx, mat->row[i], off + tab->n_col);
+ }
+ t = tab->row_var[row];
+ tab->row_var[row] = tab->col_var[col];
+ tab->col_var[col] = t;
+ var = isl_tab_var_from_row(tab, row);
+ var->is_row = 1;
+ var->index = row;
+ var = var_from_col(tab, col);
+ var->is_row = 0;
+ var->index = col;
+ update_row_sign(tab, row, col, sgn);
+ if (tab->in_undo)
+ return 0;
+ for (i = tab->n_redundant; i < tab->n_row; ++i) {
+ if (isl_int_is_zero(mat->row[i][off + col]))
+ continue;
+ if (!isl_tab_var_from_row(tab, i)->frozen &&
+ isl_tab_row_is_redundant(tab, i)) {
+ int redo = isl_tab_mark_redundant(tab, i);
+ if (redo < 0)
+ return -1;
+ if (redo)
+ --i;
+ }
+ }
+ return 0;
+}
+
+/* If "var" represents a column variable, then pivot is up (sgn > 0)
+ * or down (sgn < 0) to a row. The variable is assumed not to be
+ * unbounded in the specified direction.
+ * If sgn = 0, then the variable is unbounded in both directions,
+ * and we pivot with any row we can find.
+ */
+static int to_row(struct isl_tab *tab, struct isl_tab_var *var, int sign) WARN_UNUSED;
+static int to_row(struct isl_tab *tab, struct isl_tab_var *var, int sign)
+{
+ int r;
+ unsigned off = 2 + tab->M;
+
+ if (var->is_row)
+ return 0;
+
+ if (sign == 0) {
+ for (r = tab->n_redundant; r < tab->n_row; ++r)
+ if (!isl_int_is_zero(tab->mat->row[r][off+var->index]))
+ break;
+ isl_assert(tab->mat->ctx, r < tab->n_row, return -1);
+ } else {
+ r = pivot_row(tab, NULL, sign, var->index);
+ isl_assert(tab->mat->ctx, r >= 0, return -1);
+ }
+
+ return isl_tab_pivot(tab, r, var->index);
+}
+
+/* Check whether all variables that are marked as non-negative
+ * also have a non-negative sample value. This function is not
+ * called from the current code but is useful during debugging.
+ */
+static void check_table(struct isl_tab *tab) __attribute__ ((unused));
+static void check_table(struct isl_tab *tab)
+{
+ int i;
+
+ if (tab->empty)
+ return;
+ for (i = tab->n_redundant; i < tab->n_row; ++i) {
+ struct isl_tab_var *var;
+ var = isl_tab_var_from_row(tab, i);
+ if (!var->is_nonneg)
+ continue;
+ if (tab->M) {
+ isl_assert(tab->mat->ctx,
+ !isl_int_is_neg(tab->mat->row[i][2]), abort());
+ if (isl_int_is_pos(tab->mat->row[i][2]))
+ continue;
+ }
+ isl_assert(tab->mat->ctx, !isl_int_is_neg(tab->mat->row[i][1]),
+ abort());
+ }
+}
+
+/* Return the sign of the maximal value of "var".
+ * If the sign is not negative, then on return from this function,
+ * the sample value will also be non-negative.
+ *
+ * If "var" is manifestly unbounded wrt positive values, we are done.
+ * Otherwise, we pivot the variable up to a row if needed
+ * Then we continue pivoting down until either
+ * - no more down pivots can be performed
+ * - the sample value is positive
+ * - the variable is pivoted into a manifestly unbounded column
+ */
+static int sign_of_max(struct isl_tab *tab, struct isl_tab_var *var)
+{
+ int row, col;
+
+ if (max_is_manifestly_unbounded(tab, var))
+ return 1;
+ if (to_row(tab, var, 1) < 0)
+ return -2;
+ while (!isl_int_is_pos(tab->mat->row[var->index][1])) {
+ find_pivot(tab, var, var, 1, &row, &col);
+ if (row == -1)
+ return isl_int_sgn(tab->mat->row[var->index][1]);
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -2;
+ if (!var->is_row) /* manifestly unbounded */
+ return 1;
+ }
+ return 1;
+}
+
+int isl_tab_sign_of_max(struct isl_tab *tab, int con)
+{
+ struct isl_tab_var *var;
+
+ if (!tab)
+ return -2;
+
+ var = &tab->con[con];
+ isl_assert(tab->mat->ctx, !var->is_redundant, return -2);
+ isl_assert(tab->mat->ctx, !var->is_zero, return -2);
+
+ return sign_of_max(tab, var);
+}
+
+static int row_is_neg(struct isl_tab *tab, int row)
+{
+ if (!tab->M)
+ return isl_int_is_neg(tab->mat->row[row][1]);
+ if (isl_int_is_pos(tab->mat->row[row][2]))
+ return 0;
+ if (isl_int_is_neg(tab->mat->row[row][2]))
+ return 1;
+ return isl_int_is_neg(tab->mat->row[row][1]);
+}
+
+static int row_sgn(struct isl_tab *tab, int row)
+{
+ if (!tab->M)
+ return isl_int_sgn(tab->mat->row[row][1]);
+ if (!isl_int_is_zero(tab->mat->row[row][2]))
+ return isl_int_sgn(tab->mat->row[row][2]);
+ else
+ return isl_int_sgn(tab->mat->row[row][1]);
+}
+
+/* Perform pivots until the row variable "var" has a non-negative
+ * sample value or until no more upward pivots can be performed.
+ * Return the sign of the sample value after the pivots have been
+ * performed.
+ */
+static int restore_row(struct isl_tab *tab, struct isl_tab_var *var)
+{
+ int row, col;
+
+ while (row_is_neg(tab, var->index)) {
+ find_pivot(tab, var, var, 1, &row, &col);
+ if (row == -1)
+ break;
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -2;
+ if (!var->is_row) /* manifestly unbounded */
+ return 1;
+ }
+ return row_sgn(tab, var->index);
+}
+
+/* Perform pivots until we are sure that the row variable "var"
+ * can attain non-negative values. After return from this
+ * function, "var" is still a row variable, but its sample
+ * value may not be non-negative, even if the function returns 1.
+ */
+static int at_least_zero(struct isl_tab *tab, struct isl_tab_var *var)
+{
+ int row, col;
+
+ while (isl_int_is_neg(tab->mat->row[var->index][1])) {
+ find_pivot(tab, var, var, 1, &row, &col);
+ if (row == -1)
+ break;
+ if (row == var->index) /* manifestly unbounded */
+ return 1;
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -1;
+ }
+ return !isl_int_is_neg(tab->mat->row[var->index][1]);
+}
+
+/* Return a negative value if "var" can attain negative values.
+ * Return a non-negative value otherwise.
+ *
+ * If "var" is manifestly unbounded wrt negative values, we are done.
+ * Otherwise, if var is in a column, we can pivot it down to a row.
+ * Then we continue pivoting down until either
+ * - the pivot would result in a manifestly unbounded column
+ * => we don't perform the pivot, but simply return -1
+ * - no more down pivots can be performed
+ * - the sample value is negative
+ * If the sample value becomes negative and the variable is supposed
+ * to be nonnegative, then we undo the last pivot.
+ * However, if the last pivot has made the pivoting variable
+ * obviously redundant, then it may have moved to another row.
+ * In that case we look for upward pivots until we reach a non-negative
+ * value again.
+ */
+static int sign_of_min(struct isl_tab *tab, struct isl_tab_var *var)
+{
+ int row, col;
+ struct isl_tab_var *pivot_var = NULL;
+
+ if (min_is_manifestly_unbounded(tab, var))
+ return -1;
+ if (!var->is_row) {
+ col = var->index;
+ row = pivot_row(tab, NULL, -1, col);
+ pivot_var = var_from_col(tab, col);
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -2;
+ if (var->is_redundant)
+ return 0;
+ if (isl_int_is_neg(tab->mat->row[var->index][1])) {
+ if (var->is_nonneg) {
+ if (!pivot_var->is_redundant &&
+ pivot_var->index == row) {
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -2;
+ } else
+ if (restore_row(tab, var) < -1)
+ return -2;
+ }
+ return -1;
+ }
+ }
+ if (var->is_redundant)
+ return 0;
+ while (!isl_int_is_neg(tab->mat->row[var->index][1])) {
+ find_pivot(tab, var, var, -1, &row, &col);
+ if (row == var->index)
+ return -1;
+ if (row == -1)
+ return isl_int_sgn(tab->mat->row[var->index][1]);
+ pivot_var = var_from_col(tab, col);
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -2;
+ if (var->is_redundant)
+ return 0;
+ }
+ if (pivot_var && var->is_nonneg) {
+ /* pivot back to non-negative value */
+ if (!pivot_var->is_redundant && pivot_var->index == row) {
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -2;
+ } else
+ if (restore_row(tab, var) < -1)
+ return -2;
+ }
+ return -1;
+}
+
+static int row_at_most_neg_one(struct isl_tab *tab, int row)
+{
+ if (tab->M) {
+ if (isl_int_is_pos(tab->mat->row[row][2]))
+ return 0;
+ if (isl_int_is_neg(tab->mat->row[row][2]))
+ return 1;
+ }
+ return isl_int_is_neg(tab->mat->row[row][1]) &&
+ isl_int_abs_ge(tab->mat->row[row][1],
+ tab->mat->row[row][0]);
+}
+
+/* Return 1 if "var" can attain values <= -1.
+ * Return 0 otherwise.
+ *
+ * If the variable "var" is supposed to be non-negative (is_nonneg is set),
+ * then the sample value of "var" is assumed to be non-negative when the
+ * the function is called. If 1 is returned then the constraint
+ * is not redundant and the sample value is made non-negative again before
+ * the function returns.
+ */
+int isl_tab_min_at_most_neg_one(struct isl_tab *tab, struct isl_tab_var *var)
+{
+ int row, col;
+ struct isl_tab_var *pivot_var;
+
+ if (min_is_manifestly_unbounded(tab, var))
+ return 1;
+ if (!var->is_row) {
+ col = var->index;
+ row = pivot_row(tab, NULL, -1, col);
+ pivot_var = var_from_col(tab, col);
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -1;
+ if (var->is_redundant)
+ return 0;
+ if (row_at_most_neg_one(tab, var->index)) {
+ if (var->is_nonneg) {
+ if (!pivot_var->is_redundant &&
+ pivot_var->index == row) {
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -1;
+ } else
+ if (restore_row(tab, var) < -1)
+ return -1;
+ }
+ return 1;
+ }
+ }
+ if (var->is_redundant)
+ return 0;
+ do {
+ find_pivot(tab, var, var, -1, &row, &col);
+ if (row == var->index) {
+ if (var->is_nonneg && restore_row(tab, var) < -1)
+ return -1;
+ return 1;
+ }
+ if (row == -1)
+ return 0;
+ pivot_var = var_from_col(tab, col);
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -1;
+ if (var->is_redundant)
+ return 0;
+ } while (!row_at_most_neg_one(tab, var->index));
+ if (var->is_nonneg) {
+ /* pivot back to non-negative value */
+ if (!pivot_var->is_redundant && pivot_var->index == row)
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -1;
+ if (restore_row(tab, var) < -1)
+ return -1;
+ }
+ return 1;
+}
+
+/* Return 1 if "var" can attain values >= 1.
+ * Return 0 otherwise.
+ */
+static int at_least_one(struct isl_tab *tab, struct isl_tab_var *var)
+{
+ int row, col;
+ isl_int *r;
+
+ if (max_is_manifestly_unbounded(tab, var))
+ return 1;
+ if (to_row(tab, var, 1) < 0)
+ return -1;
+ r = tab->mat->row[var->index];
+ while (isl_int_lt(r[1], r[0])) {
+ find_pivot(tab, var, var, 1, &row, &col);
+ if (row == -1)
+ return isl_int_ge(r[1], r[0]);
+ if (row == var->index) /* manifestly unbounded */
+ return 1;
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -1;
+ }
+ return 1;
+}
+
+static void swap_cols(struct isl_tab *tab, int col1, int col2)
+{
+ int t;
+ unsigned off = 2 + tab->M;
+ t = tab->col_var[col1];
+ tab->col_var[col1] = tab->col_var[col2];
+ tab->col_var[col2] = t;
+ var_from_col(tab, col1)->index = col1;
+ var_from_col(tab, col2)->index = col2;
+ tab->mat = isl_mat_swap_cols(tab->mat, off + col1, off + col2);
+}
+
+/* Mark column with index "col" as representing a zero variable.
+ * If we may need to undo the operation the column is kept,
+ * but no longer considered.
+ * Otherwise, the column is simply removed.
+ *
+ * The column may be interchanged with some other column. If it
+ * is interchanged with a later column, return 1. Otherwise return 0.
+ * If the columns are checked in order in the calling function,
+ * then a return value of 1 means that the column with the given
+ * column number may now contain a different column that
+ * hasn't been checked yet.
+ */
+int isl_tab_kill_col(struct isl_tab *tab, int col)
+{
+ var_from_col(tab, col)->is_zero = 1;
+ if (tab->need_undo) {
+ if (isl_tab_push_var(tab, isl_tab_undo_zero,
+ var_from_col(tab, col)) < 0)
+ return -1;
+ if (col != tab->n_dead)
+ swap_cols(tab, col, tab->n_dead);
+ tab->n_dead++;
+ return 0;
+ } else {
+ if (col != tab->n_col - 1)
+ swap_cols(tab, col, tab->n_col - 1);
+ var_from_col(tab, tab->n_col - 1)->index = -1;
+ tab->n_col--;
+ return 1;
+ }
+}
+
+static int row_is_manifestly_non_integral(struct isl_tab *tab, int row)
+{
+ unsigned off = 2 + tab->M;
+
+ if (tab->M && !isl_int_eq(tab->mat->row[row][2],
+ tab->mat->row[row][0]))
+ return 0;
+ if (isl_seq_first_non_zero(tab->mat->row[row] + off + tab->n_dead,
+ tab->n_col - tab->n_dead) != -1)
+ return 0;
+
+ return !isl_int_is_divisible_by(tab->mat->row[row][1],
+ tab->mat->row[row][0]);
+}
+
+/* For integer tableaus, check if any of the coordinates are stuck
+ * at a non-integral value.
+ */
+static int tab_is_manifestly_empty(struct isl_tab *tab)
+{
+ int i;
+
+ if (tab->empty)
+ return 1;
+ if (tab->rational)
+ return 0;
+
+ for (i = 0; i < tab->n_var; ++i) {
+ if (!tab->var[i].is_row)
+ continue;
+ if (row_is_manifestly_non_integral(tab, tab->var[i].index))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Row variable "var" is non-negative and cannot attain any values
+ * larger than zero. This means that the coefficients of the unrestricted
+ * column variables are zero and that the coefficients of the non-negative
+ * column variables are zero or negative.
+ * Each of the non-negative variables with a negative coefficient can
+ * then also be written as the negative sum of non-negative variables
+ * and must therefore also be zero.
+ *
+ * If "temp_var" is set, then "var" is a temporary variable that
+ * will be removed after this function returns and for which
+ * no information is recorded on the undo stack.
+ * Do not add any undo records involving this variable in this case
+ * since the variable will have been removed before any future undo
+ * operations. Also avoid marking the variable as redundant,
+ * since that either adds an undo record or needlessly removes the row
+ * (the caller will take care of removing the row).
+ */
+static isl_stat close_row(struct isl_tab *tab, struct isl_tab_var *var,
+ int temp_var) WARN_UNUSED;
+static isl_stat close_row(struct isl_tab *tab, struct isl_tab_var *var,
+ int temp_var)
+{
+ int j;
+ struct isl_mat *mat = tab->mat;
+ unsigned off = 2 + tab->M;
+
+ if (!var->is_nonneg)
+ isl_die(isl_tab_get_ctx(tab), isl_error_internal,
+ "expecting non-negative variable",
+ return isl_stat_error);
+ var->is_zero = 1;
+ if (!temp_var && tab->need_undo)
+ if (isl_tab_push_var(tab, isl_tab_undo_zero, var) < 0)
+ return isl_stat_error;
+ for (j = tab->n_dead; j < tab->n_col; ++j) {
+ int recheck;
+ if (isl_int_is_zero(mat->row[var->index][off + j]))
+ continue;
+ if (isl_int_is_pos(mat->row[var->index][off + j]))
+ isl_die(isl_tab_get_ctx(tab), isl_error_internal,
+ "row cannot have positive coefficients",
+ return isl_stat_error);
+ recheck = isl_tab_kill_col(tab, j);
+ if (recheck < 0)
+ return isl_stat_error;
+ if (recheck)
+ --j;
+ }
+ if (!temp_var && isl_tab_mark_redundant(tab, var->index) < 0)
+ return isl_stat_error;
+ if (tab_is_manifestly_empty(tab) && isl_tab_mark_empty(tab) < 0)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Add a constraint to the tableau and allocate a row for it.
+ * Return the index into the constraint array "con".
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ */
+int isl_tab_allocate_con(struct isl_tab *tab)
+{
+ int r;
+
+ isl_assert(tab->mat->ctx, tab->n_row < tab->mat->n_row, return -1);
+ isl_assert(tab->mat->ctx, tab->n_con < tab->max_con, return -1);
+
+ r = tab->n_con;
+ tab->con[r].index = tab->n_row;
+ tab->con[r].is_row = 1;
+ tab->con[r].is_nonneg = 0;
+ tab->con[r].is_zero = 0;
+ tab->con[r].is_redundant = 0;
+ tab->con[r].frozen = 0;
+ tab->con[r].negated = 0;
+ tab->row_var[tab->n_row] = ~r;
+
+ tab->n_row++;
+ tab->n_con++;
+ if (isl_tab_push_var(tab, isl_tab_undo_allocate, &tab->con[r]) < 0)
+ return -1;
+
+ return r;
+}
+
+/* Move the entries in tab->var up one position, starting at "first",
+ * creating room for an extra entry at position "first".
+ * Since some of the entries of tab->row_var and tab->col_var contain
+ * indices into this array, they have to be updated accordingly.
+ */
+static int var_insert_entry(struct isl_tab *tab, int first)
+{
+ int i;
+
+ if (tab->n_var >= tab->max_var)
+ isl_die(isl_tab_get_ctx(tab), isl_error_internal,
+ "not enough room for new variable", return -1);
+ if (first > tab->n_var)
+ isl_die(isl_tab_get_ctx(tab), isl_error_internal,
+ "invalid initial position", return -1);
+
+ for (i = tab->n_var - 1; i >= first; --i) {
+ tab->var[i + 1] = tab->var[i];
+ if (tab->var[i + 1].is_row)
+ tab->row_var[tab->var[i + 1].index]++;
+ else
+ tab->col_var[tab->var[i + 1].index]++;
+ }
+
+ tab->n_var++;
+
+ return 0;
+}
+
+/* Drop the entry at position "first" in tab->var, moving all
+ * subsequent entries down.
+ * Since some of the entries of tab->row_var and tab->col_var contain
+ * indices into this array, they have to be updated accordingly.
+ */
+static int var_drop_entry(struct isl_tab *tab, int first)
+{
+ int i;
+
+ if (first >= tab->n_var)
+ isl_die(isl_tab_get_ctx(tab), isl_error_internal,
+ "invalid initial position", return -1);
+
+ tab->n_var--;
+
+ for (i = first; i < tab->n_var; ++i) {
+ tab->var[i] = tab->var[i + 1];
+ if (tab->var[i + 1].is_row)
+ tab->row_var[tab->var[i].index]--;
+ else
+ tab->col_var[tab->var[i].index]--;
+ }
+
+ return 0;
+}
+
+/* Add a variable to the tableau at position "r" and allocate a column for it.
+ * Return the index into the variable array "var", i.e., "r",
+ * or -1 on error.
+ */
+int isl_tab_insert_var(struct isl_tab *tab, int r)
+{
+ int i;
+ unsigned off = 2 + tab->M;
+
+ isl_assert(tab->mat->ctx, tab->n_col < tab->mat->n_col, return -1);
+
+ if (var_insert_entry(tab, r) < 0)
+ return -1;
+
+ tab->var[r].index = tab->n_col;
+ tab->var[r].is_row = 0;
+ tab->var[r].is_nonneg = 0;
+ tab->var[r].is_zero = 0;
+ tab->var[r].is_redundant = 0;
+ tab->var[r].frozen = 0;
+ tab->var[r].negated = 0;
+ tab->col_var[tab->n_col] = r;
+
+ for (i = 0; i < tab->n_row; ++i)
+ isl_int_set_si(tab->mat->row[i][off + tab->n_col], 0);
+
+ tab->n_col++;
+ if (isl_tab_push_var(tab, isl_tab_undo_allocate, &tab->var[r]) < 0)
+ return -1;
+
+ return r;
+}
+
+/* Add a row to the tableau. The row is given as an affine combination
+ * of the original variables and needs to be expressed in terms of the
+ * column variables.
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ *
+ * We add each term in turn.
+ * If r = n/d_r is the current sum and we need to add k x, then
+ * if x is a column variable, we increase the numerator of
+ * this column by k d_r
+ * if x = f/d_x is a row variable, then the new representation of r is
+ *
+ * n k f d_x/g n + d_r/g k f m/d_r n + m/d_g k f
+ * --- + --- = ------------------- = -------------------
+ * d_r d_r d_r d_x/g m
+ *
+ * with g the gcd of d_r and d_x and m the lcm of d_r and d_x.
+ *
+ * If tab->M is set, then, internally, each variable x is represented
+ * as x' - M. We then also need no subtract k d_r from the coefficient of M.
+ */
+int isl_tab_add_row(struct isl_tab *tab, isl_int *line)
+{
+ int i;
+ int r;
+ isl_int *row;
+ isl_int a, b;
+ unsigned off = 2 + tab->M;
+
+ r = isl_tab_allocate_con(tab);
+ if (r < 0)
+ return -1;
+
+ isl_int_init(a);
+ isl_int_init(b);
+ row = tab->mat->row[tab->con[r].index];
+ isl_int_set_si(row[0], 1);
+ isl_int_set(row[1], line[0]);
+ isl_seq_clr(row + 2, tab->M + tab->n_col);
+ for (i = 0; i < tab->n_var; ++i) {
+ if (tab->var[i].is_zero)
+ continue;
+ if (tab->var[i].is_row) {
+ isl_int_lcm(a,
+ row[0], tab->mat->row[tab->var[i].index][0]);
+ isl_int_swap(a, row[0]);
+ isl_int_divexact(a, row[0], a);
+ isl_int_divexact(b,
+ row[0], tab->mat->row[tab->var[i].index][0]);
+ isl_int_mul(b, b, line[1 + i]);
+ isl_seq_combine(row + 1, a, row + 1,
+ b, tab->mat->row[tab->var[i].index] + 1,
+ 1 + tab->M + tab->n_col);
+ } else
+ isl_int_addmul(row[off + tab->var[i].index],
+ line[1 + i], row[0]);
+ if (tab->M && i >= tab->n_param && i < tab->n_var - tab->n_div)
+ isl_int_submul(row[2], line[1 + i], row[0]);
+ }
+ isl_seq_normalize(tab->mat->ctx, row, off + tab->n_col);
+ isl_int_clear(a);
+ isl_int_clear(b);
+
+ if (tab->row_sign)
+ tab->row_sign[tab->con[r].index] = isl_tab_row_unknown;
+
+ return r;
+}
+
+static isl_stat drop_row(struct isl_tab *tab, int row)
+{
+ isl_assert(tab->mat->ctx, ~tab->row_var[row] == tab->n_con - 1,
+ return isl_stat_error);
+ if (row != tab->n_row - 1)
+ swap_rows(tab, row, tab->n_row - 1);
+ tab->n_row--;
+ tab->n_con--;
+ return isl_stat_ok;
+}
+
+/* Drop the variable in column "col" along with the column.
+ * The column is removed first because it may need to be moved
+ * into the last position and this process requires
+ * the contents of the col_var array in a state
+ * before the removal of the variable.
+ */
+static isl_stat drop_col(struct isl_tab *tab, int col)
+{
+ int var;
+
+ var = tab->col_var[col];
+ if (col != tab->n_col - 1)
+ swap_cols(tab, col, tab->n_col - 1);
+ tab->n_col--;
+ if (var_drop_entry(tab, var) < 0)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Add inequality "ineq" and check if it conflicts with the
+ * previously added constraints or if it is obviously redundant.
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ */
+isl_stat isl_tab_add_ineq(struct isl_tab *tab, isl_int *ineq)
+{
+ int r;
+ int sgn;
+ isl_int cst;
+
+ if (!tab)
+ return isl_stat_error;
+ if (tab->bmap) {
+ struct isl_basic_map *bmap = tab->bmap;
+
+ isl_assert(tab->mat->ctx, tab->n_eq == bmap->n_eq,
+ return isl_stat_error);
+ isl_assert(tab->mat->ctx,
+ tab->n_con == bmap->n_eq + bmap->n_ineq,
+ return isl_stat_error);
+ tab->bmap = isl_basic_map_add_ineq(tab->bmap, ineq);
+ if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0)
+ return isl_stat_error;
+ if (!tab->bmap)
+ return isl_stat_error;
+ }
+ if (tab->cone) {
+ isl_int_init(cst);
+ isl_int_set_si(cst, 0);
+ isl_int_swap(ineq[0], cst);
+ }
+ r = isl_tab_add_row(tab, ineq);
+ if (tab->cone) {
+ isl_int_swap(ineq[0], cst);
+ isl_int_clear(cst);
+ }
+ if (r < 0)
+ return isl_stat_error;
+ tab->con[r].is_nonneg = 1;
+ if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0)
+ return isl_stat_error;
+ if (isl_tab_row_is_redundant(tab, tab->con[r].index)) {
+ if (isl_tab_mark_redundant(tab, tab->con[r].index) < 0)
+ return isl_stat_error;
+ return isl_stat_ok;
+ }
+
+ sgn = restore_row(tab, &tab->con[r]);
+ if (sgn < -1)
+ return isl_stat_error;
+ if (sgn < 0)
+ return isl_tab_mark_empty(tab);
+ if (tab->con[r].is_row && isl_tab_row_is_redundant(tab, tab->con[r].index))
+ if (isl_tab_mark_redundant(tab, tab->con[r].index) < 0)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Pivot a non-negative variable down until it reaches the value zero
+ * and then pivot the variable into a column position.
+ */
+static int to_col(struct isl_tab *tab, struct isl_tab_var *var) WARN_UNUSED;
+static int to_col(struct isl_tab *tab, struct isl_tab_var *var)
+{
+ int i;
+ int row, col;
+ unsigned off = 2 + tab->M;
+
+ if (!var->is_row)
+ return 0;
+
+ while (isl_int_is_pos(tab->mat->row[var->index][1])) {
+ find_pivot(tab, var, NULL, -1, &row, &col);
+ isl_assert(tab->mat->ctx, row != -1, return -1);
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -1;
+ if (!var->is_row)
+ return 0;
+ }
+
+ for (i = tab->n_dead; i < tab->n_col; ++i)
+ if (!isl_int_is_zero(tab->mat->row[var->index][off + i]))
+ break;
+
+ isl_assert(tab->mat->ctx, i < tab->n_col, return -1);
+ if (isl_tab_pivot(tab, var->index, i) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* We assume Gaussian elimination has been performed on the equalities.
+ * The equalities can therefore never conflict.
+ * Adding the equalities is currently only really useful for a later call
+ * to isl_tab_ineq_type.
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ */
+static struct isl_tab *add_eq(struct isl_tab *tab, isl_int *eq)
+{
+ int i;
+ int r;
+
+ if (!tab)
+ return NULL;
+ r = isl_tab_add_row(tab, eq);
+ if (r < 0)
+ goto error;
+
+ r = tab->con[r].index;
+ i = isl_seq_first_non_zero(tab->mat->row[r] + 2 + tab->M + tab->n_dead,
+ tab->n_col - tab->n_dead);
+ isl_assert(tab->mat->ctx, i >= 0, goto error);
+ i += tab->n_dead;
+ if (isl_tab_pivot(tab, r, i) < 0)
+ goto error;
+ if (isl_tab_kill_col(tab, i) < 0)
+ goto error;
+ tab->n_eq++;
+
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Does the sample value of row "row" of "tab" involve the big parameter,
+ * if any?
+ */
+static int row_is_big(struct isl_tab *tab, int row)
+{
+ return tab->M && !isl_int_is_zero(tab->mat->row[row][2]);
+}
+
+static int row_is_manifestly_zero(struct isl_tab *tab, int row)
+{
+ unsigned off = 2 + tab->M;
+
+ if (!isl_int_is_zero(tab->mat->row[row][1]))
+ return 0;
+ if (row_is_big(tab, row))
+ return 0;
+ return isl_seq_first_non_zero(tab->mat->row[row] + off + tab->n_dead,
+ tab->n_col - tab->n_dead) == -1;
+}
+
+/* Add an equality that is known to be valid for the given tableau.
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ */
+int isl_tab_add_valid_eq(struct isl_tab *tab, isl_int *eq)
+{
+ struct isl_tab_var *var;
+ int r;
+
+ if (!tab)
+ return -1;
+ r = isl_tab_add_row(tab, eq);
+ if (r < 0)
+ return -1;
+
+ var = &tab->con[r];
+ r = var->index;
+ if (row_is_manifestly_zero(tab, r)) {
+ var->is_zero = 1;
+ if (isl_tab_mark_redundant(tab, r) < 0)
+ return -1;
+ return 0;
+ }
+
+ if (isl_int_is_neg(tab->mat->row[r][1])) {
+ isl_seq_neg(tab->mat->row[r] + 1, tab->mat->row[r] + 1,
+ 1 + tab->n_col);
+ var->negated = 1;
+ }
+ var->is_nonneg = 1;
+ if (to_col(tab, var) < 0)
+ return -1;
+ var->is_nonneg = 0;
+ if (isl_tab_kill_col(tab, var->index) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Add a zero row to "tab" and return the corresponding index
+ * in the constraint array.
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ */
+static int add_zero_row(struct isl_tab *tab)
+{
+ int r;
+ isl_int *row;
+
+ r = isl_tab_allocate_con(tab);
+ if (r < 0)
+ return -1;
+
+ row = tab->mat->row[tab->con[r].index];
+ isl_seq_clr(row + 1, 1 + tab->M + tab->n_col);
+ isl_int_set_si(row[0], 1);
+
+ return r;
+}
+
+/* Add equality "eq" and check if it conflicts with the
+ * previously added constraints or if it is obviously redundant.
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ * If tab->bmap is set, then two rows are needed instead of one.
+ */
+isl_stat isl_tab_add_eq(struct isl_tab *tab, isl_int *eq)
+{
+ struct isl_tab_undo *snap = NULL;
+ struct isl_tab_var *var;
+ int r;
+ int row;
+ int sgn;
+ isl_int cst;
+
+ if (!tab)
+ return isl_stat_error;
+ isl_assert(tab->mat->ctx, !tab->M, return isl_stat_error);
+
+ if (tab->need_undo)
+ snap = isl_tab_snap(tab);
+
+ if (tab->cone) {
+ isl_int_init(cst);
+ isl_int_set_si(cst, 0);
+ isl_int_swap(eq[0], cst);
+ }
+ r = isl_tab_add_row(tab, eq);
+ if (tab->cone) {
+ isl_int_swap(eq[0], cst);
+ isl_int_clear(cst);
+ }
+ if (r < 0)
+ return isl_stat_error;
+
+ var = &tab->con[r];
+ row = var->index;
+ if (row_is_manifestly_zero(tab, row)) {
+ if (snap)
+ return isl_tab_rollback(tab, snap);
+ return drop_row(tab, row);
+ }
+
+ if (tab->bmap) {
+ tab->bmap = isl_basic_map_add_ineq(tab->bmap, eq);
+ if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0)
+ return isl_stat_error;
+ isl_seq_neg(eq, eq, 1 + tab->n_var);
+ tab->bmap = isl_basic_map_add_ineq(tab->bmap, eq);
+ isl_seq_neg(eq, eq, 1 + tab->n_var);
+ if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0)
+ return isl_stat_error;
+ if (!tab->bmap)
+ return isl_stat_error;
+ if (add_zero_row(tab) < 0)
+ return isl_stat_error;
+ }
+
+ sgn = isl_int_sgn(tab->mat->row[row][1]);
+
+ if (sgn > 0) {
+ isl_seq_neg(tab->mat->row[row] + 1, tab->mat->row[row] + 1,
+ 1 + tab->n_col);
+ var->negated = 1;
+ sgn = -1;
+ }
+
+ if (sgn < 0) {
+ sgn = sign_of_max(tab, var);
+ if (sgn < -1)
+ return isl_stat_error;
+ if (sgn < 0) {
+ if (isl_tab_mark_empty(tab) < 0)
+ return isl_stat_error;
+ return isl_stat_ok;
+ }
+ }
+
+ var->is_nonneg = 1;
+ if (to_col(tab, var) < 0)
+ return isl_stat_error;
+ var->is_nonneg = 0;
+ if (isl_tab_kill_col(tab, var->index) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Construct and return an inequality that expresses an upper bound
+ * on the given div.
+ * In particular, if the div is given by
+ *
+ * d = floor(e/m)
+ *
+ * then the inequality expresses
+ *
+ * m d <= e
+ */
+static __isl_give isl_vec *ineq_for_div(__isl_keep isl_basic_map *bmap,
+ unsigned div)
+{
+ isl_size total;
+ unsigned div_pos;
+ struct isl_vec *ineq;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return NULL;
+
+ div_pos = 1 + total - bmap->n_div + div;
+
+ ineq = isl_vec_alloc(bmap->ctx, 1 + total);
+ if (!ineq)
+ return NULL;
+
+ isl_seq_cpy(ineq->el, bmap->div[div] + 1, 1 + total);
+ isl_int_neg(ineq->el[div_pos], bmap->div[div][0]);
+ return ineq;
+}
+
+/* For a div d = floor(f/m), add the constraints
+ *
+ * f - m d >= 0
+ * -(f-(m-1)) + m d >= 0
+ *
+ * Note that the second constraint is the negation of
+ *
+ * f - m d >= m
+ *
+ * If add_ineq is not NULL, then this function is used
+ * instead of isl_tab_add_ineq to effectively add the inequalities.
+ *
+ * This function assumes that at least two more rows and at least
+ * two more elements in the constraint array are available in the tableau.
+ */
+static isl_stat add_div_constraints(struct isl_tab *tab, unsigned div,
+ isl_stat (*add_ineq)(void *user, isl_int *), void *user)
+{
+ isl_size total;
+ unsigned div_pos;
+ struct isl_vec *ineq;
+
+ total = isl_basic_map_dim(tab->bmap, isl_dim_all);
+ if (total < 0)
+ return isl_stat_error;
+ div_pos = 1 + total - tab->bmap->n_div + div;
+
+ ineq = ineq_for_div(tab->bmap, div);
+ if (!ineq)
+ goto error;
+
+ if (add_ineq) {
+ if (add_ineq(user, ineq->el) < 0)
+ goto error;
+ } else {
+ if (isl_tab_add_ineq(tab, ineq->el) < 0)
+ goto error;
+ }
+
+ isl_seq_neg(ineq->el, tab->bmap->div[div] + 1, 1 + total);
+ isl_int_set(ineq->el[div_pos], tab->bmap->div[div][0]);
+ isl_int_add(ineq->el[0], ineq->el[0], ineq->el[div_pos]);
+ isl_int_sub_ui(ineq->el[0], ineq->el[0], 1);
+
+ if (add_ineq) {
+ if (add_ineq(user, ineq->el) < 0)
+ goto error;
+ } else {
+ if (isl_tab_add_ineq(tab, ineq->el) < 0)
+ goto error;
+ }
+
+ isl_vec_free(ineq);
+
+ return isl_stat_ok;
+error:
+ isl_vec_free(ineq);
+ return isl_stat_error;
+}
+
+/* Check whether the div described by "div" is obviously non-negative.
+ * If we are using a big parameter, then we will encode the div
+ * as div' = M + div, which is always non-negative.
+ * Otherwise, we check whether div is a non-negative affine combination
+ * of non-negative variables.
+ */
+static int div_is_nonneg(struct isl_tab *tab, __isl_keep isl_vec *div)
+{
+ int i;
+
+ if (tab->M)
+ return 1;
+
+ if (isl_int_is_neg(div->el[1]))
+ return 0;
+
+ for (i = 0; i < tab->n_var; ++i) {
+ if (isl_int_is_neg(div->el[2 + i]))
+ return 0;
+ if (isl_int_is_zero(div->el[2 + i]))
+ continue;
+ if (!tab->var[i].is_nonneg)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Insert an extra div, prescribed by "div", to the tableau and
+ * the associated bmap (which is assumed to be non-NULL).
+ * The extra integer division is inserted at (tableau) position "pos".
+ * Return "pos" or -1 if an error occurred.
+ *
+ * If add_ineq is not NULL, then this function is used instead
+ * of isl_tab_add_ineq to add the div constraints.
+ * This complication is needed because the code in isl_tab_pip
+ * wants to perform some extra processing when an inequality
+ * is added to the tableau.
+ */
+int isl_tab_insert_div(struct isl_tab *tab, int pos, __isl_keep isl_vec *div,
+ isl_stat (*add_ineq)(void *user, isl_int *), void *user)
+{
+ int r;
+ int nonneg;
+ isl_size n_div;
+ int o_div;
+
+ if (!tab || !div)
+ return -1;
+
+ if (div->size != 1 + 1 + tab->n_var)
+ isl_die(isl_tab_get_ctx(tab), isl_error_invalid,
+ "unexpected size", return -1);
+
+ n_div = isl_basic_map_dim(tab->bmap, isl_dim_div);
+ if (n_div < 0)
+ return -1;
+ o_div = tab->n_var - n_div;
+ if (pos < o_div || pos > tab->n_var)
+ isl_die(isl_tab_get_ctx(tab), isl_error_invalid,
+ "invalid position", return -1);
+
+ nonneg = div_is_nonneg(tab, div);
+
+ if (isl_tab_extend_cons(tab, 3) < 0)
+ return -1;
+ if (isl_tab_extend_vars(tab, 1) < 0)
+ return -1;
+ r = isl_tab_insert_var(tab, pos);
+ if (r < 0)
+ return -1;
+
+ if (nonneg)
+ tab->var[r].is_nonneg = 1;
+
+ tab->bmap = isl_basic_map_insert_div(tab->bmap, pos - o_div, div);
+ if (!tab->bmap)
+ return -1;
+ if (isl_tab_push_var(tab, isl_tab_undo_bmap_div, &tab->var[r]) < 0)
+ return -1;
+
+ if (add_div_constraints(tab, pos - o_div, add_ineq, user) < 0)
+ return -1;
+
+ return r;
+}
+
+/* Add an extra div, prescribed by "div", to the tableau and
+ * the associated bmap (which is assumed to be non-NULL).
+ */
+int isl_tab_add_div(struct isl_tab *tab, __isl_keep isl_vec *div)
+{
+ if (!tab)
+ return -1;
+ return isl_tab_insert_div(tab, tab->n_var, div, NULL, NULL);
+}
+
+/* If "track" is set, then we want to keep track of all constraints in tab
+ * in its bmap field. This field is initialized from a copy of "bmap",
+ * so we need to make sure that all constraints in "bmap" also appear
+ * in the constructed tab.
+ */
+__isl_give struct isl_tab *isl_tab_from_basic_map(
+ __isl_keep isl_basic_map *bmap, int track)
+{
+ int i;
+ struct isl_tab *tab;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return NULL;
+ tab = isl_tab_alloc(bmap->ctx, total + bmap->n_ineq + 1, total, 0);
+ if (!tab)
+ return NULL;
+ tab->preserve = track;
+ tab->rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL);
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) {
+ if (isl_tab_mark_empty(tab) < 0)
+ goto error;
+ goto done;
+ }
+ for (i = 0; i < bmap->n_eq; ++i) {
+ tab = add_eq(tab, bmap->eq[i]);
+ if (!tab)
+ return tab;
+ }
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ if (isl_tab_add_ineq(tab, bmap->ineq[i]) < 0)
+ goto error;
+ if (tab->empty)
+ goto done;
+ }
+done:
+ if (track && isl_tab_track_bmap(tab, isl_basic_map_copy(bmap)) < 0)
+ goto error;
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+__isl_give struct isl_tab *isl_tab_from_basic_set(
+ __isl_keep isl_basic_set *bset, int track)
+{
+ return isl_tab_from_basic_map(bset, track);
+}
+
+/* Construct a tableau corresponding to the recession cone of "bset".
+ */
+struct isl_tab *isl_tab_from_recession_cone(__isl_keep isl_basic_set *bset,
+ int parametric)
+{
+ isl_int cst;
+ int i;
+ struct isl_tab *tab;
+ isl_size offset = 0;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (parametric)
+ offset = isl_basic_set_dim(bset, isl_dim_param);
+ if (total < 0 || offset < 0)
+ return NULL;
+ tab = isl_tab_alloc(bset->ctx, bset->n_eq + bset->n_ineq,
+ total - offset, 0);
+ if (!tab)
+ return NULL;
+ tab->rational = ISL_F_ISSET(bset, ISL_BASIC_SET_RATIONAL);
+ tab->cone = 1;
+
+ isl_int_init(cst);
+ isl_int_set_si(cst, 0);
+ for (i = 0; i < bset->n_eq; ++i) {
+ isl_int_swap(bset->eq[i][offset], cst);
+ if (offset > 0) {
+ if (isl_tab_add_eq(tab, bset->eq[i] + offset) < 0)
+ goto error;
+ } else
+ tab = add_eq(tab, bset->eq[i]);
+ isl_int_swap(bset->eq[i][offset], cst);
+ if (!tab)
+ goto done;
+ }
+ for (i = 0; i < bset->n_ineq; ++i) {
+ int r;
+ isl_int_swap(bset->ineq[i][offset], cst);
+ r = isl_tab_add_row(tab, bset->ineq[i] + offset);
+ isl_int_swap(bset->ineq[i][offset], cst);
+ if (r < 0)
+ goto error;
+ tab->con[r].is_nonneg = 1;
+ if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0)
+ goto error;
+ }
+done:
+ isl_int_clear(cst);
+ return tab;
+error:
+ isl_int_clear(cst);
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Assuming "tab" is the tableau of a cone, check if the cone is
+ * bounded, i.e., if it is empty or only contains the origin.
+ */
+isl_bool isl_tab_cone_is_bounded(struct isl_tab *tab)
+{
+ int i;
+
+ if (!tab)
+ return isl_bool_error;
+ if (tab->empty)
+ return isl_bool_true;
+ if (tab->n_dead == tab->n_col)
+ return isl_bool_true;
+
+ for (;;) {
+ for (i = tab->n_redundant; i < tab->n_row; ++i) {
+ struct isl_tab_var *var;
+ int sgn;
+ var = isl_tab_var_from_row(tab, i);
+ if (!var->is_nonneg)
+ continue;
+ sgn = sign_of_max(tab, var);
+ if (sgn < -1)
+ return isl_bool_error;
+ if (sgn != 0)
+ return isl_bool_false;
+ if (close_row(tab, var, 0) < 0)
+ return isl_bool_error;
+ break;
+ }
+ if (tab->n_dead == tab->n_col)
+ return isl_bool_true;
+ if (i == tab->n_row)
+ return isl_bool_false;
+ }
+}
+
+int isl_tab_sample_is_integer(struct isl_tab *tab)
+{
+ int i;
+
+ if (!tab)
+ return -1;
+
+ for (i = 0; i < tab->n_var; ++i) {
+ int row;
+ if (!tab->var[i].is_row)
+ continue;
+ row = tab->var[i].index;
+ if (!isl_int_is_divisible_by(tab->mat->row[row][1],
+ tab->mat->row[row][0]))
+ return 0;
+ }
+ return 1;
+}
+
+static struct isl_vec *extract_integer_sample(struct isl_tab *tab)
+{
+ int i;
+ struct isl_vec *vec;
+
+ vec = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_var);
+ if (!vec)
+ return NULL;
+
+ isl_int_set_si(vec->block.data[0], 1);
+ for (i = 0; i < tab->n_var; ++i) {
+ if (!tab->var[i].is_row)
+ isl_int_set_si(vec->block.data[1 + i], 0);
+ else {
+ int row = tab->var[i].index;
+ isl_int_divexact(vec->block.data[1 + i],
+ tab->mat->row[row][1], tab->mat->row[row][0]);
+ }
+ }
+
+ return vec;
+}
+
+__isl_give isl_vec *isl_tab_get_sample_value(struct isl_tab *tab)
+{
+ int i;
+ struct isl_vec *vec;
+ isl_int m;
+
+ if (!tab)
+ return NULL;
+
+ vec = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_var);
+ if (!vec)
+ return NULL;
+
+ isl_int_init(m);
+
+ isl_int_set_si(vec->block.data[0], 1);
+ for (i = 0; i < tab->n_var; ++i) {
+ int row;
+ if (!tab->var[i].is_row) {
+ isl_int_set_si(vec->block.data[1 + i], 0);
+ continue;
+ }
+ row = tab->var[i].index;
+ isl_int_gcd(m, vec->block.data[0], tab->mat->row[row][0]);
+ isl_int_divexact(m, tab->mat->row[row][0], m);
+ isl_seq_scale(vec->block.data, vec->block.data, m, 1 + i);
+ isl_int_divexact(m, vec->block.data[0], tab->mat->row[row][0]);
+ isl_int_mul(vec->block.data[1 + i], m, tab->mat->row[row][1]);
+ }
+ vec = isl_vec_normalize(vec);
+
+ isl_int_clear(m);
+ return vec;
+}
+
+/* Store the sample value of "var" of "tab" rounded up (if sgn > 0)
+ * or down (if sgn < 0) to the nearest integer in *v.
+ */
+static void get_rounded_sample_value(struct isl_tab *tab,
+ struct isl_tab_var *var, int sgn, isl_int *v)
+{
+ if (!var->is_row)
+ isl_int_set_si(*v, 0);
+ else if (sgn > 0)
+ isl_int_cdiv_q(*v, tab->mat->row[var->index][1],
+ tab->mat->row[var->index][0]);
+ else
+ isl_int_fdiv_q(*v, tab->mat->row[var->index][1],
+ tab->mat->row[var->index][0]);
+}
+
+/* Update "bmap" based on the results of the tableau "tab".
+ * In particular, implicit equalities are made explicit, redundant constraints
+ * are removed and if the sample value happens to be integer, it is stored
+ * in "bmap" (unless "bmap" already had an integer sample).
+ *
+ * The tableau is assumed to have been created from "bmap" using
+ * isl_tab_from_basic_map.
+ */
+__isl_give isl_basic_map *isl_basic_map_update_from_tab(
+ __isl_take isl_basic_map *bmap, struct isl_tab *tab)
+{
+ int i;
+ unsigned n_eq;
+
+ if (!bmap)
+ return NULL;
+ if (!tab)
+ return bmap;
+
+ n_eq = tab->n_eq;
+ if (tab->empty)
+ bmap = isl_basic_map_set_to_empty(bmap);
+ else
+ for (i = bmap->n_ineq - 1; i >= 0; --i) {
+ if (isl_tab_is_equality(tab, n_eq + i))
+ isl_basic_map_inequality_to_equality(bmap, i);
+ else if (isl_tab_is_redundant(tab, n_eq + i))
+ isl_basic_map_drop_inequality(bmap, i);
+ }
+ if (bmap->n_eq != n_eq)
+ bmap = isl_basic_map_gauss(bmap, NULL);
+ if (!tab->rational &&
+ bmap && !bmap->sample && isl_tab_sample_is_integer(tab))
+ bmap->sample = extract_integer_sample(tab);
+ return bmap;
+}
+
+__isl_give isl_basic_set *isl_basic_set_update_from_tab(
+ __isl_take isl_basic_set *bset, struct isl_tab *tab)
+{
+ return bset_from_bmap(isl_basic_map_update_from_tab(bset_to_bmap(bset),
+ tab));
+}
+
+/* Drop the last constraint added to "tab" in position "r".
+ * The constraint is expected to have remained in a row.
+ */
+static isl_stat drop_last_con_in_row(struct isl_tab *tab, int r)
+{
+ if (!tab->con[r].is_row)
+ isl_die(isl_tab_get_ctx(tab), isl_error_internal,
+ "row unexpectedly moved to column",
+ return isl_stat_error);
+ if (r + 1 != tab->n_con)
+ isl_die(isl_tab_get_ctx(tab), isl_error_internal,
+ "additional constraints added", return isl_stat_error);
+ if (drop_row(tab, tab->con[r].index) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Given a non-negative variable "var", temporarily add a new non-negative
+ * variable that is the opposite of "var", ensuring that "var" can only attain
+ * the value zero. The new variable is removed again before this function
+ * returns. However, the effect of forcing "var" to be zero remains.
+ * If var = n/d is a row variable, then the new variable = -n/d.
+ * If var is a column variables, then the new variable = -var.
+ * If the new variable cannot attain non-negative values, then
+ * the resulting tableau is empty.
+ * Otherwise, we know the value will be zero and we close the row.
+ */
+static isl_stat cut_to_hyperplane(struct isl_tab *tab, struct isl_tab_var *var)
+{
+ unsigned r;
+ isl_int *row;
+ int sgn;
+ unsigned off = 2 + tab->M;
+
+ if (var->is_zero)
+ return isl_stat_ok;
+ if (var->is_redundant || !var->is_nonneg)
+ isl_die(isl_tab_get_ctx(tab), isl_error_invalid,
+ "expecting non-redundant non-negative variable",
+ return isl_stat_error);
+
+ if (isl_tab_extend_cons(tab, 1) < 0)
+ return isl_stat_error;
+
+ r = tab->n_con;
+ tab->con[r].index = tab->n_row;
+ tab->con[r].is_row = 1;
+ tab->con[r].is_nonneg = 0;
+ tab->con[r].is_zero = 0;
+ tab->con[r].is_redundant = 0;
+ tab->con[r].frozen = 0;
+ tab->con[r].negated = 0;
+ tab->row_var[tab->n_row] = ~r;
+ row = tab->mat->row[tab->n_row];
+
+ if (var->is_row) {
+ isl_int_set(row[0], tab->mat->row[var->index][0]);
+ isl_seq_neg(row + 1,
+ tab->mat->row[var->index] + 1, 1 + tab->n_col);
+ } else {
+ isl_int_set_si(row[0], 1);
+ isl_seq_clr(row + 1, 1 + tab->n_col);
+ isl_int_set_si(row[off + var->index], -1);
+ }
+
+ tab->n_row++;
+ tab->n_con++;
+
+ sgn = sign_of_max(tab, &tab->con[r]);
+ if (sgn < -1)
+ return isl_stat_error;
+ if (sgn < 0) {
+ if (drop_last_con_in_row(tab, r) < 0)
+ return isl_stat_error;
+ if (isl_tab_mark_empty(tab) < 0)
+ return isl_stat_error;
+ return isl_stat_ok;
+ }
+ tab->con[r].is_nonneg = 1;
+ /* sgn == 0 */
+ if (close_row(tab, &tab->con[r], 1) < 0)
+ return isl_stat_error;
+ if (drop_last_con_in_row(tab, r) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Check that "con" is a valid constraint position for "tab".
+ */
+static isl_stat isl_tab_check_con(struct isl_tab *tab, int con)
+{
+ if (!tab)
+ return isl_stat_error;
+ if (con < 0 || con >= tab->n_con)
+ isl_die(isl_tab_get_ctx(tab), isl_error_invalid,
+ "position out of bounds", return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Given a tableau "tab" and an inequality constraint "con" of the tableau,
+ * relax the inequality by one. That is, the inequality r >= 0 is replaced
+ * by r' = r + 1 >= 0.
+ * If r is a row variable, we simply increase the constant term by one
+ * (taking into account the denominator).
+ * If r is a column variable, then we need to modify each row that
+ * refers to r = r' - 1 by substituting this equality, effectively
+ * subtracting the coefficient of the column from the constant.
+ * We should only do this if the minimum is manifestly unbounded,
+ * however. Otherwise, we may end up with negative sample values
+ * for non-negative variables.
+ * So, if r is a column variable with a minimum that is not
+ * manifestly unbounded, then we need to move it to a row.
+ * However, the sample value of this row may be negative,
+ * even after the relaxation, so we need to restore it.
+ * We therefore prefer to pivot a column up to a row, if possible.
+ */
+int isl_tab_relax(struct isl_tab *tab, int con)
+{
+ struct isl_tab_var *var;
+
+ if (!tab)
+ return -1;
+
+ var = &tab->con[con];
+
+ if (var->is_row && (var->index < 0 || var->index < tab->n_redundant))
+ isl_die(tab->mat->ctx, isl_error_invalid,
+ "cannot relax redundant constraint", return -1);
+ if (!var->is_row && (var->index < 0 || var->index < tab->n_dead))
+ isl_die(tab->mat->ctx, isl_error_invalid,
+ "cannot relax dead constraint", return -1);
+
+ if (!var->is_row && !max_is_manifestly_unbounded(tab, var))
+ if (to_row(tab, var, 1) < 0)
+ return -1;
+ if (!var->is_row && !min_is_manifestly_unbounded(tab, var))
+ if (to_row(tab, var, -1) < 0)
+ return -1;
+
+ if (var->is_row) {
+ isl_int_add(tab->mat->row[var->index][1],
+ tab->mat->row[var->index][1], tab->mat->row[var->index][0]);
+ if (restore_row(tab, var) < 0)
+ return -1;
+ } else {
+ int i;
+ unsigned off = 2 + tab->M;
+
+ for (i = 0; i < tab->n_row; ++i) {
+ if (isl_int_is_zero(tab->mat->row[i][off + var->index]))
+ continue;
+ isl_int_sub(tab->mat->row[i][1], tab->mat->row[i][1],
+ tab->mat->row[i][off + var->index]);
+ }
+
+ }
+
+ if (isl_tab_push_var(tab, isl_tab_undo_relax, var) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Replace the variable v at position "pos" in the tableau "tab"
+ * by v' = v + shift.
+ *
+ * If the variable is in a column, then we first check if we can
+ * simply plug in v = v' - shift. The effect on a row with
+ * coefficient f/d for variable v is that the constant term c/d
+ * is replaced by (c - f * shift)/d. If shift is positive and
+ * f is negative for each row that needs to remain non-negative,
+ * then this is clearly safe. In other words, if the minimum of v
+ * is manifestly unbounded, then we can keep v in a column position.
+ * Otherwise, we can pivot it down to a row.
+ * Similarly, if shift is negative, we need to check if the maximum
+ * of is manifestly unbounded.
+ *
+ * If the variable is in a row (from the start or after pivoting),
+ * then the constant term c/d is replaced by (c + d * shift)/d.
+ */
+int isl_tab_shift_var(struct isl_tab *tab, int pos, isl_int shift)
+{
+ struct isl_tab_var *var;
+
+ if (!tab)
+ return -1;
+ if (isl_int_is_zero(shift))
+ return 0;
+
+ var = &tab->var[pos];
+ if (!var->is_row) {
+ if (isl_int_is_neg(shift)) {
+ if (!max_is_manifestly_unbounded(tab, var))
+ if (to_row(tab, var, 1) < 0)
+ return -1;
+ } else {
+ if (!min_is_manifestly_unbounded(tab, var))
+ if (to_row(tab, var, -1) < 0)
+ return -1;
+ }
+ }
+
+ if (var->is_row) {
+ isl_int_addmul(tab->mat->row[var->index][1],
+ shift, tab->mat->row[var->index][0]);
+ } else {
+ int i;
+ unsigned off = 2 + tab->M;
+
+ for (i = 0; i < tab->n_row; ++i) {
+ if (isl_int_is_zero(tab->mat->row[i][off + var->index]))
+ continue;
+ isl_int_submul(tab->mat->row[i][1],
+ shift, tab->mat->row[i][off + var->index]);
+ }
+
+ }
+
+ return 0;
+}
+
+/* Remove the sign constraint from constraint "con".
+ *
+ * If the constraint variable was originally marked non-negative,
+ * then we make sure we mark it non-negative again during rollback.
+ */
+int isl_tab_unrestrict(struct isl_tab *tab, int con)
+{
+ struct isl_tab_var *var;
+
+ if (!tab)
+ return -1;
+
+ var = &tab->con[con];
+ if (!var->is_nonneg)
+ return 0;
+
+ var->is_nonneg = 0;
+ if (isl_tab_push_var(tab, isl_tab_undo_unrestrict, var) < 0)
+ return -1;
+
+ return 0;
+}
+
+int isl_tab_select_facet(struct isl_tab *tab, int con)
+{
+ if (!tab)
+ return -1;
+
+ return cut_to_hyperplane(tab, &tab->con[con]);
+}
+
+static int may_be_equality(struct isl_tab *tab, int row)
+{
+ return tab->rational ? isl_int_is_zero(tab->mat->row[row][1])
+ : isl_int_lt(tab->mat->row[row][1],
+ tab->mat->row[row][0]);
+}
+
+/* Return an isl_tab_var that has been marked or NULL if no such
+ * variable can be found.
+ * The marked field has only been set for variables that
+ * appear in non-redundant rows or non-dead columns.
+ *
+ * Pick the last constraint variable that is marked and
+ * that appears in either a non-redundant row or a non-dead columns.
+ * Since the returned variable is tested for being a redundant constraint or
+ * an implicit equality, there is no need to return any tab variable that
+ * corresponds to a variable.
+ */
+static struct isl_tab_var *select_marked(struct isl_tab *tab)
+{
+ int i;
+ struct isl_tab_var *var;
+
+ for (i = tab->n_con - 1; i >= 0; --i) {
+ var = &tab->con[i];
+ if (var->index < 0)
+ continue;
+ if (var->is_row && var->index < tab->n_redundant)
+ continue;
+ if (!var->is_row && var->index < tab->n_dead)
+ continue;
+ if (var->marked)
+ return var;
+ }
+
+ return NULL;
+}
+
+/* Check for (near) equalities among the constraints.
+ * A constraint is an equality if it is non-negative and if
+ * its maximal value is either
+ * - zero (in case of rational tableaus), or
+ * - strictly less than 1 (in case of integer tableaus)
+ *
+ * We first mark all non-redundant and non-dead variables that
+ * are not frozen and not obviously not an equality.
+ * Then we iterate over all marked variables if they can attain
+ * any values larger than zero or at least one.
+ * If the maximal value is zero, we mark any column variables
+ * that appear in the row as being zero and mark the row as being redundant.
+ * Otherwise, if the maximal value is strictly less than one (and the
+ * tableau is integer), then we restrict the value to being zero
+ * by adding an opposite non-negative variable.
+ * The order in which the variables are considered is not important.
+ */
+int isl_tab_detect_implicit_equalities(struct isl_tab *tab)
+{
+ int i;
+ unsigned n_marked;
+
+ if (!tab)
+ return -1;
+ if (tab->empty)
+ return 0;
+ if (tab->n_dead == tab->n_col)
+ return 0;
+
+ n_marked = 0;
+ for (i = tab->n_redundant; i < tab->n_row; ++i) {
+ struct isl_tab_var *var = isl_tab_var_from_row(tab, i);
+ var->marked = !var->frozen && var->is_nonneg &&
+ may_be_equality(tab, i);
+ if (var->marked)
+ n_marked++;
+ }
+ for (i = tab->n_dead; i < tab->n_col; ++i) {
+ struct isl_tab_var *var = var_from_col(tab, i);
+ var->marked = !var->frozen && var->is_nonneg;
+ if (var->marked)
+ n_marked++;
+ }
+ while (n_marked) {
+ struct isl_tab_var *var;
+ int sgn;
+ var = select_marked(tab);
+ if (!var)
+ break;
+ var->marked = 0;
+ n_marked--;
+ sgn = sign_of_max(tab, var);
+ if (sgn < 0)
+ return -1;
+ if (sgn == 0) {
+ if (close_row(tab, var, 0) < 0)
+ return -1;
+ } else if (!tab->rational && !at_least_one(tab, var)) {
+ if (cut_to_hyperplane(tab, var) < 0)
+ return -1;
+ return isl_tab_detect_implicit_equalities(tab);
+ }
+ for (i = tab->n_redundant; i < tab->n_row; ++i) {
+ var = isl_tab_var_from_row(tab, i);
+ if (!var->marked)
+ continue;
+ if (may_be_equality(tab, i))
+ continue;
+ var->marked = 0;
+ n_marked--;
+ }
+ }
+
+ return 0;
+}
+
+/* Update the element of row_var or col_var that corresponds to
+ * constraint tab->con[i] to a move from position "old" to position "i".
+ */
+static int update_con_after_move(struct isl_tab *tab, int i, int old)
+{
+ int *p;
+ int index;
+
+ index = tab->con[i].index;
+ if (index == -1)
+ return 0;
+ p = tab->con[i].is_row ? tab->row_var : tab->col_var;
+ if (p[index] != ~old)
+ isl_die(tab->mat->ctx, isl_error_internal,
+ "broken internal state", return -1);
+ p[index] = ~i;
+
+ return 0;
+}
+
+/* Interchange constraints "con1" and "con2" in "tab".
+ * In particular, interchange the contents of these entries in tab->con.
+ * Since tab->col_var and tab->row_var point back into this array,
+ * they need to be updated accordingly.
+ */
+isl_stat isl_tab_swap_constraints(struct isl_tab *tab, int con1, int con2)
+{
+ struct isl_tab_var var;
+
+ if (isl_tab_check_con(tab, con1) < 0 ||
+ isl_tab_check_con(tab, con2) < 0)
+ return isl_stat_error;
+
+ var = tab->con[con1];
+ tab->con[con1] = tab->con[con2];
+ if (update_con_after_move(tab, con1, con2) < 0)
+ return isl_stat_error;
+ tab->con[con2] = var;
+ if (update_con_after_move(tab, con2, con1) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Rotate the "n" constraints starting at "first" to the right,
+ * putting the last constraint in the position of the first constraint.
+ */
+static int rotate_constraints(struct isl_tab *tab, int first, int n)
+{
+ int i, last;
+ struct isl_tab_var var;
+
+ if (n <= 1)
+ return 0;
+
+ last = first + n - 1;
+ var = tab->con[last];
+ for (i = last; i > first; --i) {
+ tab->con[i] = tab->con[i - 1];
+ if (update_con_after_move(tab, i, i - 1) < 0)
+ return -1;
+ }
+ tab->con[first] = var;
+ if (update_con_after_move(tab, first, last) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Drop the "n" entries starting at position "first" in tab->con, moving all
+ * subsequent entries down.
+ * Since some of the entries of tab->row_var and tab->col_var contain
+ * indices into this array, they have to be updated accordingly.
+ */
+static isl_stat con_drop_entries(struct isl_tab *tab,
+ unsigned first, unsigned n)
+{
+ int i;
+
+ if (first + n > tab->n_con || first + n < first)
+ isl_die(isl_tab_get_ctx(tab), isl_error_internal,
+ "invalid range", return isl_stat_error);
+
+ tab->n_con -= n;
+
+ for (i = first; i < tab->n_con; ++i) {
+ tab->con[i] = tab->con[i + n];
+ if (update_con_after_move(tab, i, i + n) < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* isl_basic_map_gauss5 callback that gets called when
+ * two (equality) constraints "a" and "b" get interchanged
+ * in the basic map. Perform the same interchange in "tab".
+ */
+static isl_stat swap_eq(unsigned a, unsigned b, void *user)
+{
+ struct isl_tab *tab = user;
+
+ return isl_tab_swap_constraints(tab, a, b);
+}
+
+/* isl_basic_map_gauss5 callback that gets called when
+ * the final "n" equality constraints get removed.
+ * As a special case, if "n" is equal to the total number
+ * of equality constraints, then this means the basic map
+ * turned out to be empty.
+ * Drop the same number of equality constraints from "tab" or
+ * mark it empty in the special case.
+ */
+static isl_stat drop_eq(unsigned n, void *user)
+{
+ struct isl_tab *tab = user;
+
+ if (tab->n_eq == n)
+ return isl_tab_mark_empty(tab);
+
+ tab->n_eq -= n;
+ return con_drop_entries(tab, tab->n_eq, n);
+}
+
+/* If "bmap" has more than a single reference, then call
+ * isl_basic_map_gauss on it, updating "tab" accordingly.
+ */
+static __isl_give isl_basic_map *gauss_if_shared(__isl_take isl_basic_map *bmap,
+ struct isl_tab *tab)
+{
+ isl_bool single;
+
+ single = isl_basic_map_has_single_reference(bmap);
+ if (single < 0)
+ return isl_basic_map_free(bmap);
+ if (single)
+ return bmap;
+ return isl_basic_map_gauss5(bmap, NULL, &swap_eq, &drop_eq, tab);
+}
+
+/* Make the equalities that are implicit in "bmap" but that have been
+ * detected in the corresponding "tab" explicit in "bmap" and update
+ * "tab" to reflect the new order of the constraints.
+ *
+ * In particular, if inequality i is an implicit equality then
+ * isl_basic_map_inequality_to_equality will move the inequality
+ * in front of the other equality and it will move the last inequality
+ * in the position of inequality i.
+ * In the tableau, the inequalities of "bmap" are stored after the equalities
+ * and so the original order
+ *
+ * E E E E E A A A I B B B B L
+ *
+ * is changed into
+ *
+ * I E E E E E A A A L B B B B
+ *
+ * where I is the implicit equality, the E are equalities,
+ * the A inequalities before I, the B inequalities after I and
+ * L the last inequality.
+ * We therefore need to rotate to the right two sets of constraints,
+ * those up to and including I and those after I.
+ *
+ * If "tab" contains any constraints that are not in "bmap" then they
+ * appear after those in "bmap" and they should be left untouched.
+ *
+ * Note that this function only calls isl_basic_map_gauss
+ * (in case some equality constraints got detected)
+ * if "bmap" has more than one reference.
+ * If it only has a single reference, then it is left in a temporary state,
+ * because the caller may require this state.
+ * Calling isl_basic_map_gauss is then the responsibility of the caller.
+ */
+__isl_give isl_basic_map *isl_tab_make_equalities_explicit(struct isl_tab *tab,
+ __isl_take isl_basic_map *bmap)
+{
+ int i;
+ unsigned n_eq;
+
+ if (!tab || !bmap)
+ return isl_basic_map_free(bmap);
+ if (tab->empty)
+ return bmap;
+
+ n_eq = tab->n_eq;
+ for (i = bmap->n_ineq - 1; i >= 0; --i) {
+ if (!isl_tab_is_equality(tab, bmap->n_eq + i))
+ continue;
+ isl_basic_map_inequality_to_equality(bmap, i);
+ if (rotate_constraints(tab, 0, tab->n_eq + i + 1) < 0)
+ return isl_basic_map_free(bmap);
+ if (rotate_constraints(tab, tab->n_eq + i + 1,
+ bmap->n_ineq - i) < 0)
+ return isl_basic_map_free(bmap);
+ tab->n_eq++;
+ }
+
+ if (n_eq != tab->n_eq)
+ bmap = gauss_if_shared(bmap, tab);
+
+ return bmap;
+}
+
+static int con_is_redundant(struct isl_tab *tab, struct isl_tab_var *var)
+{
+ if (!tab)
+ return -1;
+ if (tab->rational) {
+ int sgn = sign_of_min(tab, var);
+ if (sgn < -1)
+ return -1;
+ return sgn >= 0;
+ } else {
+ int irred = isl_tab_min_at_most_neg_one(tab, var);
+ if (irred < 0)
+ return -1;
+ return !irred;
+ }
+}
+
+/* Check for (near) redundant constraints.
+ * A constraint is redundant if it is non-negative and if
+ * its minimal value (temporarily ignoring the non-negativity) is either
+ * - zero (in case of rational tableaus), or
+ * - strictly larger than -1 (in case of integer tableaus)
+ *
+ * We first mark all non-redundant and non-dead variables that
+ * are not frozen and not obviously negatively unbounded.
+ * Then we iterate over all marked variables if they can attain
+ * any values smaller than zero or at most negative one.
+ * If not, we mark the row as being redundant (assuming it hasn't
+ * been detected as being obviously redundant in the mean time).
+ */
+int isl_tab_detect_redundant(struct isl_tab *tab)
+{
+ int i;
+ unsigned n_marked;
+
+ if (!tab)
+ return -1;
+ if (tab->empty)
+ return 0;
+ if (tab->n_redundant == tab->n_row)
+ return 0;
+
+ n_marked = 0;
+ for (i = tab->n_redundant; i < tab->n_row; ++i) {
+ struct isl_tab_var *var = isl_tab_var_from_row(tab, i);
+ var->marked = !var->frozen && var->is_nonneg;
+ if (var->marked)
+ n_marked++;
+ }
+ for (i = tab->n_dead; i < tab->n_col; ++i) {
+ struct isl_tab_var *var = var_from_col(tab, i);
+ var->marked = !var->frozen && var->is_nonneg &&
+ !min_is_manifestly_unbounded(tab, var);
+ if (var->marked)
+ n_marked++;
+ }
+ while (n_marked) {
+ struct isl_tab_var *var;
+ int red;
+ var = select_marked(tab);
+ if (!var)
+ break;
+ var->marked = 0;
+ n_marked--;
+ red = con_is_redundant(tab, var);
+ if (red < 0)
+ return -1;
+ if (red && !var->is_redundant)
+ if (isl_tab_mark_redundant(tab, var->index) < 0)
+ return -1;
+ for (i = tab->n_dead; i < tab->n_col; ++i) {
+ var = var_from_col(tab, i);
+ if (!var->marked)
+ continue;
+ if (!min_is_manifestly_unbounded(tab, var))
+ continue;
+ var->marked = 0;
+ n_marked--;
+ }
+ }
+
+ return 0;
+}
+
+int isl_tab_is_equality(struct isl_tab *tab, int con)
+{
+ int row;
+ unsigned off;
+
+ if (!tab)
+ return -1;
+ if (tab->con[con].is_zero)
+ return 1;
+ if (tab->con[con].is_redundant)
+ return 0;
+ if (!tab->con[con].is_row)
+ return tab->con[con].index < tab->n_dead;
+
+ row = tab->con[con].index;
+
+ off = 2 + tab->M;
+ return isl_int_is_zero(tab->mat->row[row][1]) &&
+ !row_is_big(tab, row) &&
+ isl_seq_first_non_zero(tab->mat->row[row] + off + tab->n_dead,
+ tab->n_col - tab->n_dead) == -1;
+}
+
+/* Return the minimal value of the affine expression "f" with denominator
+ * "denom" in *opt, *opt_denom, assuming the tableau is not empty and
+ * the expression cannot attain arbitrarily small values.
+ * If opt_denom is NULL, then *opt is rounded up to the nearest integer.
+ * The return value reflects the nature of the result (empty, unbounded,
+ * minimal value returned in *opt).
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ */
+enum isl_lp_result isl_tab_min(struct isl_tab *tab,
+ isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom,
+ unsigned flags)
+{
+ int r;
+ enum isl_lp_result res = isl_lp_ok;
+ struct isl_tab_var *var;
+ struct isl_tab_undo *snap;
+
+ if (!tab)
+ return isl_lp_error;
+
+ if (tab->empty)
+ return isl_lp_empty;
+
+ snap = isl_tab_snap(tab);
+ r = isl_tab_add_row(tab, f);
+ if (r < 0)
+ return isl_lp_error;
+ var = &tab->con[r];
+ for (;;) {
+ int row, col;
+ find_pivot(tab, var, var, -1, &row, &col);
+ if (row == var->index) {
+ res = isl_lp_unbounded;
+ break;
+ }
+ if (row == -1)
+ break;
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return isl_lp_error;
+ }
+ isl_int_mul(tab->mat->row[var->index][0],
+ tab->mat->row[var->index][0], denom);
+ if (ISL_FL_ISSET(flags, ISL_TAB_SAVE_DUAL)) {
+ int i;
+
+ isl_vec_free(tab->dual);
+ tab->dual = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_con);
+ if (!tab->dual)
+ return isl_lp_error;
+ isl_int_set(tab->dual->el[0], tab->mat->row[var->index][0]);
+ for (i = 0; i < tab->n_con; ++i) {
+ int pos;
+ if (tab->con[i].is_row) {
+ isl_int_set_si(tab->dual->el[1 + i], 0);
+ continue;
+ }
+ pos = 2 + tab->M + tab->con[i].index;
+ if (tab->con[i].negated)
+ isl_int_neg(tab->dual->el[1 + i],
+ tab->mat->row[var->index][pos]);
+ else
+ isl_int_set(tab->dual->el[1 + i],
+ tab->mat->row[var->index][pos]);
+ }
+ }
+ if (opt && res == isl_lp_ok) {
+ if (opt_denom) {
+ isl_int_set(*opt, tab->mat->row[var->index][1]);
+ isl_int_set(*opt_denom, tab->mat->row[var->index][0]);
+ } else
+ get_rounded_sample_value(tab, var, 1, opt);
+ }
+ if (isl_tab_rollback(tab, snap) < 0)
+ return isl_lp_error;
+ return res;
+}
+
+/* Is the constraint at position "con" marked as being redundant?
+ * If it is marked as representing an equality, then it is not
+ * considered to be redundant.
+ * Note that isl_tab_mark_redundant marks both the isl_tab_var as
+ * redundant and moves the corresponding row into the first
+ * tab->n_redundant positions (or removes the row, assigning it index -1),
+ * so the final test is actually redundant itself.
+ */
+int isl_tab_is_redundant(struct isl_tab *tab, int con)
+{
+ if (isl_tab_check_con(tab, con) < 0)
+ return -1;
+ if (tab->con[con].is_zero)
+ return 0;
+ if (tab->con[con].is_redundant)
+ return 1;
+ return tab->con[con].is_row && tab->con[con].index < tab->n_redundant;
+}
+
+/* Is variable "var" of "tab" fixed to a constant value by its row
+ * in the tableau?
+ * If so and if "value" is not NULL, then store this constant value
+ * in "value".
+ *
+ * That is, is it a row variable that only has non-zero coefficients
+ * for dead columns?
+ */
+static isl_bool is_constant(struct isl_tab *tab, struct isl_tab_var *var,
+ isl_int *value)
+{
+ unsigned off = 2 + tab->M;
+ isl_mat *mat = tab->mat;
+ int n;
+ int row;
+ int pos;
+
+ if (!var->is_row)
+ return isl_bool_false;
+ row = var->index;
+ if (row_is_big(tab, row))
+ return isl_bool_false;
+ n = tab->n_col - tab->n_dead;
+ pos = isl_seq_first_non_zero(mat->row[row] + off + tab->n_dead, n);
+ if (pos != -1)
+ return isl_bool_false;
+ if (value)
+ isl_int_divexact(*value, mat->row[row][1], mat->row[row][0]);
+ return isl_bool_true;
+}
+
+/* Has the variable "var' of "tab" reached a value that is greater than
+ * or equal (if sgn > 0) or smaller than or equal (if sgn < 0) to "target"?
+ * "tmp" has been initialized by the caller and can be used
+ * to perform local computations.
+ *
+ * If the sample value involves the big parameter, then any value
+ * is reached.
+ * Otherwise check if n/d >= t, i.e., n >= d * t (if sgn > 0)
+ * or n/d <= t, i.e., n <= d * t (if sgn < 0).
+ */
+static int reached(struct isl_tab *tab, struct isl_tab_var *var, int sgn,
+ isl_int target, isl_int *tmp)
+{
+ if (row_is_big(tab, var->index))
+ return 1;
+ isl_int_mul(*tmp, tab->mat->row[var->index][0], target);
+ if (sgn > 0)
+ return isl_int_ge(tab->mat->row[var->index][1], *tmp);
+ else
+ return isl_int_le(tab->mat->row[var->index][1], *tmp);
+}
+
+/* Can variable "var" of "tab" attain the value "target" by
+ * pivoting up (if sgn > 0) or down (if sgn < 0)?
+ * If not, then pivot up [down] to the greatest [smallest]
+ * rational value.
+ * "tmp" has been initialized by the caller and can be used
+ * to perform local computations.
+ *
+ * If the variable is manifestly unbounded in the desired direction,
+ * then it can attain any value.
+ * Otherwise, it can be moved to a row.
+ * Continue pivoting until the target is reached.
+ * If no more pivoting can be performed, the maximal [minimal]
+ * rational value has been reached and the target cannot be reached.
+ * If the variable would be pivoted into a manifestly unbounded column,
+ * then the target can be reached.
+ */
+static isl_bool var_reaches(struct isl_tab *tab, struct isl_tab_var *var,
+ int sgn, isl_int target, isl_int *tmp)
+{
+ int row, col;
+
+ if (sgn < 0 && min_is_manifestly_unbounded(tab, var))
+ return isl_bool_true;
+ if (sgn > 0 && max_is_manifestly_unbounded(tab, var))
+ return isl_bool_true;
+ if (to_row(tab, var, sgn) < 0)
+ return isl_bool_error;
+ while (!reached(tab, var, sgn, target, tmp)) {
+ find_pivot(tab, var, var, sgn, &row, &col);
+ if (row == -1)
+ return isl_bool_false;
+ if (row == var->index)
+ return isl_bool_true;
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return isl_bool_error;
+ }
+
+ return isl_bool_true;
+}
+
+/* Check if variable "var" of "tab" can only attain a single (integer)
+ * value, and, if so, add an equality constraint to fix the variable
+ * to this single value and store the result in "target".
+ * "target" and "tmp" have been initialized by the caller.
+ *
+ * Given the current sample value, round it down and check
+ * whether it is possible to attain a strictly smaller integer value.
+ * If so, the variable is not restricted to a single integer value.
+ * Otherwise, the search stops at the smallest rational value.
+ * Round up this value and check whether it is possible to attain
+ * a strictly greater integer value.
+ * If so, the variable is not restricted to a single integer value.
+ * Otherwise, the search stops at the greatest rational value.
+ * If rounding down this value yields a value that is different
+ * from rounding up the smallest rational value, then the variable
+ * cannot attain any integer value. Mark the tableau empty.
+ * Otherwise, add an equality constraint that fixes the variable
+ * to the single integer value found.
+ */
+static isl_bool detect_constant_with_tmp(struct isl_tab *tab,
+ struct isl_tab_var *var, isl_int *target, isl_int *tmp)
+{
+ isl_bool reached;
+ isl_vec *eq;
+ int pos;
+ isl_stat r;
+
+ get_rounded_sample_value(tab, var, -1, target);
+ isl_int_sub_ui(*target, *target, 1);
+ reached = var_reaches(tab, var, -1, *target, tmp);
+ if (reached < 0 || reached)
+ return isl_bool_not(reached);
+ get_rounded_sample_value(tab, var, 1, target);
+ isl_int_add_ui(*target, *target, 1);
+ reached = var_reaches(tab, var, 1, *target, tmp);
+ if (reached < 0 || reached)
+ return isl_bool_not(reached);
+ get_rounded_sample_value(tab, var, -1, tmp);
+ isl_int_sub_ui(*target, *target, 1);
+ if (isl_int_ne(*target, *tmp)) {
+ if (isl_tab_mark_empty(tab) < 0)
+ return isl_bool_error;
+ return isl_bool_false;
+ }
+
+ if (isl_tab_extend_cons(tab, 1) < 0)
+ return isl_bool_error;
+ eq = isl_vec_alloc(isl_tab_get_ctx(tab), 1 + tab->n_var);
+ if (!eq)
+ return isl_bool_error;
+ pos = var - tab->var;
+ isl_seq_clr(eq->el + 1, tab->n_var);
+ isl_int_set_si(eq->el[1 + pos], -1);
+ isl_int_set(eq->el[0], *target);
+ r = isl_tab_add_eq(tab, eq->el);
+ isl_vec_free(eq);
+
+ return r < 0 ? isl_bool_error : isl_bool_true;
+}
+
+/* Check if variable "var" of "tab" can only attain a single (integer)
+ * value, and, if so, add an equality constraint to fix the variable
+ * to this single value and store the result in "value" (if "value"
+ * is not NULL).
+ *
+ * If the current sample value involves the big parameter,
+ * then the variable cannot have a fixed integer value.
+ * If the variable is already fixed to a single value by its row, then
+ * there is no need to add another equality constraint.
+ *
+ * Otherwise, allocate some temporary variables and continue
+ * with detect_constant_with_tmp.
+ */
+static isl_bool get_constant(struct isl_tab *tab, struct isl_tab_var *var,
+ isl_int *value)
+{
+ isl_int target, tmp;
+ isl_bool is_cst;
+
+ if (var->is_row && row_is_big(tab, var->index))
+ return isl_bool_false;
+ is_cst = is_constant(tab, var, value);
+ if (is_cst < 0 || is_cst)
+ return is_cst;
+
+ if (!value)
+ isl_int_init(target);
+ isl_int_init(tmp);
+
+ is_cst = detect_constant_with_tmp(tab, var,
+ value ? value : &target, &tmp);
+
+ isl_int_clear(tmp);
+ if (!value)
+ isl_int_clear(target);
+
+ return is_cst;
+}
+
+/* Check if variable "var" of "tab" can only attain a single (integer)
+ * value, and, if so, add an equality constraint to fix the variable
+ * to this single value and store the result in "value" (if "value"
+ * is not NULL).
+ *
+ * For rational tableaus, nothing needs to be done.
+ */
+isl_bool isl_tab_is_constant(struct isl_tab *tab, int var, isl_int *value)
+{
+ if (!tab)
+ return isl_bool_error;
+ if (var < 0 || var >= tab->n_var)
+ isl_die(isl_tab_get_ctx(tab), isl_error_invalid,
+ "position out of bounds", return isl_bool_error);
+ if (tab->rational)
+ return isl_bool_false;
+
+ return get_constant(tab, &tab->var[var], value);
+}
+
+/* Check if any of the variables of "tab" can only attain a single (integer)
+ * value, and, if so, add equality constraints to fix those variables
+ * to these single values.
+ *
+ * For rational tableaus, nothing needs to be done.
+ */
+isl_stat isl_tab_detect_constants(struct isl_tab *tab)
+{
+ int i;
+
+ if (!tab)
+ return isl_stat_error;
+ if (tab->rational)
+ return isl_stat_ok;
+
+ for (i = 0; i < tab->n_var; ++i) {
+ if (get_constant(tab, &tab->var[i], NULL) < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Take a snapshot of the tableau that can be restored by a call to
+ * isl_tab_rollback.
+ */
+struct isl_tab_undo *isl_tab_snap(struct isl_tab *tab)
+{
+ if (!tab)
+ return NULL;
+ tab->need_undo = 1;
+ return tab->top;
+}
+
+/* Does "tab" need to keep track of undo information?
+ * That is, was a snapshot taken that may need to be restored?
+ */
+isl_bool isl_tab_need_undo(struct isl_tab *tab)
+{
+ if (!tab)
+ return isl_bool_error;
+
+ return isl_bool_ok(tab->need_undo);
+}
+
+/* Remove all tracking of undo information from "tab", invalidating
+ * any snapshots that may have been taken of the tableau.
+ * Since all snapshots have been invalidated, there is also
+ * no need to start keeping track of undo information again.
+ */
+void isl_tab_clear_undo(struct isl_tab *tab)
+{
+ if (!tab)
+ return;
+
+ free_undo(tab);
+ tab->need_undo = 0;
+}
+
+/* Undo the operation performed by isl_tab_relax.
+ */
+static isl_stat unrelax(struct isl_tab *tab, struct isl_tab_var *var)
+ WARN_UNUSED;
+static isl_stat unrelax(struct isl_tab *tab, struct isl_tab_var *var)
+{
+ unsigned off = 2 + tab->M;
+
+ if (!var->is_row && !max_is_manifestly_unbounded(tab, var))
+ if (to_row(tab, var, 1) < 0)
+ return isl_stat_error;
+
+ if (var->is_row) {
+ isl_int_sub(tab->mat->row[var->index][1],
+ tab->mat->row[var->index][1], tab->mat->row[var->index][0]);
+ if (var->is_nonneg) {
+ int sgn = restore_row(tab, var);
+ isl_assert(tab->mat->ctx, sgn >= 0,
+ return isl_stat_error);
+ }
+ } else {
+ int i;
+
+ for (i = 0; i < tab->n_row; ++i) {
+ if (isl_int_is_zero(tab->mat->row[i][off + var->index]))
+ continue;
+ isl_int_add(tab->mat->row[i][1], tab->mat->row[i][1],
+ tab->mat->row[i][off + var->index]);
+ }
+
+ }
+
+ return isl_stat_ok;
+}
+
+/* Undo the operation performed by isl_tab_unrestrict.
+ *
+ * In particular, mark the variable as being non-negative and make
+ * sure the sample value respects this constraint.
+ */
+static isl_stat ununrestrict(struct isl_tab *tab, struct isl_tab_var *var)
+{
+ var->is_nonneg = 1;
+
+ if (var->is_row && restore_row(tab, var) < -1)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Unmark the last redundant row in "tab" as being redundant.
+ * This undoes part of the modifications performed by isl_tab_mark_redundant.
+ * In particular, remove the redundant mark and make
+ * sure the sample value respects the constraint again.
+ * A variable that is marked non-negative by isl_tab_mark_redundant
+ * is covered by a separate undo record.
+ */
+static isl_stat restore_last_redundant(struct isl_tab *tab)
+{
+ struct isl_tab_var *var;
+
+ if (tab->n_redundant < 1)
+ isl_die(isl_tab_get_ctx(tab), isl_error_internal,
+ "no redundant rows", return isl_stat_error);
+
+ var = isl_tab_var_from_row(tab, tab->n_redundant - 1);
+ var->is_redundant = 0;
+ tab->n_redundant--;
+ restore_row(tab, var);
+
+ return isl_stat_ok;
+}
+
+static isl_stat perform_undo_var(struct isl_tab *tab, struct isl_tab_undo *undo)
+ WARN_UNUSED;
+static isl_stat perform_undo_var(struct isl_tab *tab, struct isl_tab_undo *undo)
+{
+ struct isl_tab_var *var = var_from_index(tab, undo->u.var_index);
+ switch (undo->type) {
+ case isl_tab_undo_nonneg:
+ var->is_nonneg = 0;
+ break;
+ case isl_tab_undo_redundant:
+ if (!var->is_row || var->index != tab->n_redundant - 1)
+ isl_die(isl_tab_get_ctx(tab), isl_error_internal,
+ "not undoing last redundant row",
+ return isl_stat_error);
+ return restore_last_redundant(tab);
+ case isl_tab_undo_freeze:
+ var->frozen = 0;
+ break;
+ case isl_tab_undo_zero:
+ var->is_zero = 0;
+ if (!var->is_row)
+ tab->n_dead--;
+ break;
+ case isl_tab_undo_allocate:
+ if (undo->u.var_index >= 0) {
+ isl_assert(tab->mat->ctx, !var->is_row,
+ return isl_stat_error);
+ return drop_col(tab, var->index);
+ }
+ if (!var->is_row) {
+ if (!max_is_manifestly_unbounded(tab, var)) {
+ if (to_row(tab, var, 1) < 0)
+ return isl_stat_error;
+ } else if (!min_is_manifestly_unbounded(tab, var)) {
+ if (to_row(tab, var, -1) < 0)
+ return isl_stat_error;
+ } else
+ if (to_row(tab, var, 0) < 0)
+ return isl_stat_error;
+ }
+ return drop_row(tab, var->index);
+ case isl_tab_undo_relax:
+ return unrelax(tab, var);
+ case isl_tab_undo_unrestrict:
+ return ununrestrict(tab, var);
+ default:
+ isl_die(tab->mat->ctx, isl_error_internal,
+ "perform_undo_var called on invalid undo record",
+ return isl_stat_error);
+ }
+
+ return isl_stat_ok;
+}
+
+/* Restore all rows that have been marked redundant by isl_tab_mark_redundant
+ * and that have been preserved in the tableau.
+ * Note that isl_tab_mark_redundant may also have marked some variables
+ * as being non-negative before marking them redundant. These need
+ * to be removed as well as otherwise some constraints could end up
+ * getting marked redundant with respect to the variable.
+ */
+isl_stat isl_tab_restore_redundant(struct isl_tab *tab)
+{
+ if (!tab)
+ return isl_stat_error;
+
+ if (tab->need_undo)
+ isl_die(isl_tab_get_ctx(tab), isl_error_invalid,
+ "manually restoring redundant constraints "
+ "interferes with undo history",
+ return isl_stat_error);
+
+ while (tab->n_redundant > 0) {
+ if (tab->row_var[tab->n_redundant - 1] >= 0) {
+ struct isl_tab_var *var;
+
+ var = isl_tab_var_from_row(tab, tab->n_redundant - 1);
+ var->is_nonneg = 0;
+ }
+ restore_last_redundant(tab);
+ }
+ return isl_stat_ok;
+}
+
+/* Undo the addition of an integer division to the basic map representation
+ * of "tab" in position "pos".
+ */
+static isl_stat drop_bmap_div(struct isl_tab *tab, int pos)
+{
+ int off;
+ isl_size n_div;
+
+ n_div = isl_basic_map_dim(tab->bmap, isl_dim_div);
+ if (n_div < 0)
+ return isl_stat_error;
+ off = tab->n_var - n_div;
+ tab->bmap = isl_basic_map_drop_div(tab->bmap, pos - off);
+ if (!tab->bmap)
+ return isl_stat_error;
+ if (tab->samples) {
+ tab->samples = isl_mat_drop_cols(tab->samples, 1 + pos, 1);
+ if (!tab->samples)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Restore the tableau to the state where the basic variables
+ * are those in "col_var".
+ * We first construct a list of variables that are currently in
+ * the basis, but shouldn't. Then we iterate over all variables
+ * that should be in the basis and for each one that is currently
+ * not in the basis, we exchange it with one of the elements of the
+ * list constructed before.
+ * We can always find an appropriate variable to pivot with because
+ * the current basis is mapped to the old basis by a non-singular
+ * matrix and so we can never end up with a zero row.
+ */
+static int restore_basis(struct isl_tab *tab, int *col_var)
+{
+ int i, j;
+ int n_extra = 0;
+ int *extra = NULL; /* current columns that contain bad stuff */
+ unsigned off = 2 + tab->M;
+
+ extra = isl_alloc_array(tab->mat->ctx, int, tab->n_col);
+ if (tab->n_col && !extra)
+ goto error;
+ for (i = 0; i < tab->n_col; ++i) {
+ for (j = 0; j < tab->n_col; ++j)
+ if (tab->col_var[i] == col_var[j])
+ break;
+ if (j < tab->n_col)
+ continue;
+ extra[n_extra++] = i;
+ }
+ for (i = 0; i < tab->n_col && n_extra > 0; ++i) {
+ struct isl_tab_var *var;
+ int row;
+
+ for (j = 0; j < tab->n_col; ++j)
+ if (col_var[i] == tab->col_var[j])
+ break;
+ if (j < tab->n_col)
+ continue;
+ var = var_from_index(tab, col_var[i]);
+ row = var->index;
+ for (j = 0; j < n_extra; ++j)
+ if (!isl_int_is_zero(tab->mat->row[row][off+extra[j]]))
+ break;
+ isl_assert(tab->mat->ctx, j < n_extra, goto error);
+ if (isl_tab_pivot(tab, row, extra[j]) < 0)
+ goto error;
+ extra[j] = extra[--n_extra];
+ }
+
+ free(extra);
+ return 0;
+error:
+ free(extra);
+ return -1;
+}
+
+/* Remove all samples with index n or greater, i.e., those samples
+ * that were added since we saved this number of samples in
+ * isl_tab_save_samples.
+ */
+static void drop_samples_since(struct isl_tab *tab, int n)
+{
+ int i;
+
+ for (i = tab->n_sample - 1; i >= 0 && tab->n_sample > n; --i) {
+ if (tab->sample_index[i] < n)
+ continue;
+
+ if (i != tab->n_sample - 1) {
+ int t = tab->sample_index[tab->n_sample-1];
+ tab->sample_index[tab->n_sample-1] = tab->sample_index[i];
+ tab->sample_index[i] = t;
+ isl_mat_swap_rows(tab->samples, tab->n_sample-1, i);
+ }
+ tab->n_sample--;
+ }
+}
+
+static isl_stat perform_undo(struct isl_tab *tab, struct isl_tab_undo *undo)
+ WARN_UNUSED;
+static isl_stat perform_undo(struct isl_tab *tab, struct isl_tab_undo *undo)
+{
+ switch (undo->type) {
+ case isl_tab_undo_rational:
+ tab->rational = 0;
+ break;
+ case isl_tab_undo_empty:
+ tab->empty = 0;
+ break;
+ case isl_tab_undo_nonneg:
+ case isl_tab_undo_redundant:
+ case isl_tab_undo_freeze:
+ case isl_tab_undo_zero:
+ case isl_tab_undo_allocate:
+ case isl_tab_undo_relax:
+ case isl_tab_undo_unrestrict:
+ return perform_undo_var(tab, undo);
+ case isl_tab_undo_bmap_eq:
+ tab->bmap = isl_basic_map_free_equality(tab->bmap, 1);
+ return tab->bmap ? isl_stat_ok : isl_stat_error;
+ case isl_tab_undo_bmap_ineq:
+ tab->bmap = isl_basic_map_free_inequality(tab->bmap, 1);
+ return tab->bmap ? isl_stat_ok : isl_stat_error;
+ case isl_tab_undo_bmap_div:
+ return drop_bmap_div(tab, undo->u.var_index);
+ case isl_tab_undo_saved_basis:
+ if (restore_basis(tab, undo->u.col_var) < 0)
+ return isl_stat_error;
+ break;
+ case isl_tab_undo_drop_sample:
+ tab->n_outside--;
+ break;
+ case isl_tab_undo_saved_samples:
+ drop_samples_since(tab, undo->u.n);
+ break;
+ case isl_tab_undo_callback:
+ return undo->u.callback->run(undo->u.callback);
+ default:
+ isl_assert(tab->mat->ctx, 0, return isl_stat_error);
+ }
+ return isl_stat_ok;
+}
+
+/* Return the tableau to the state it was in when the snapshot "snap"
+ * was taken.
+ */
+isl_stat isl_tab_rollback(struct isl_tab *tab, struct isl_tab_undo *snap)
+{
+ struct isl_tab_undo *undo, *next;
+
+ if (!tab)
+ return isl_stat_error;
+
+ tab->in_undo = 1;
+ for (undo = tab->top; undo && undo != &tab->bottom; undo = next) {
+ next = undo->next;
+ if (undo == snap)
+ break;
+ if (perform_undo(tab, undo) < 0) {
+ tab->top = undo;
+ free_undo(tab);
+ tab->in_undo = 0;
+ return isl_stat_error;
+ }
+ free_undo_record(undo);
+ }
+ tab->in_undo = 0;
+ tab->top = undo;
+ if (!undo)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* The given row "row" represents an inequality violated by all
+ * points in the tableau. Check for some special cases of such
+ * separating constraints.
+ * In particular, if the row has been reduced to the constant -1,
+ * then we know the inequality is adjacent (but opposite) to
+ * an equality in the tableau.
+ * If the row has been reduced to r = c*(-1 -r'), with r' an inequality
+ * of the tableau and c a positive constant, then the inequality
+ * is adjacent (but opposite) to the inequality r'.
+ */
+static enum isl_ineq_type separation_type(struct isl_tab *tab, unsigned row)
+{
+ int pos;
+ unsigned off = 2 + tab->M;
+
+ if (tab->rational)
+ return isl_ineq_separate;
+
+ if (!isl_int_is_one(tab->mat->row[row][0]))
+ return isl_ineq_separate;
+
+ pos = isl_seq_first_non_zero(tab->mat->row[row] + off + tab->n_dead,
+ tab->n_col - tab->n_dead);
+ if (pos == -1) {
+ if (isl_int_is_negone(tab->mat->row[row][1]))
+ return isl_ineq_adj_eq;
+ else
+ return isl_ineq_separate;
+ }
+
+ if (!isl_int_eq(tab->mat->row[row][1],
+ tab->mat->row[row][off + tab->n_dead + pos]))
+ return isl_ineq_separate;
+
+ pos = isl_seq_first_non_zero(
+ tab->mat->row[row] + off + tab->n_dead + pos + 1,
+ tab->n_col - tab->n_dead - pos - 1);
+
+ return pos == -1 ? isl_ineq_adj_ineq : isl_ineq_separate;
+}
+
+/* Check the effect of inequality "ineq" on the tableau "tab".
+ * The result may be
+ * isl_ineq_redundant: satisfied by all points in the tableau
+ * isl_ineq_separate: satisfied by no point in the tableau
+ * isl_ineq_cut: satisfied by some by not all points
+ * isl_ineq_adj_eq: adjacent to an equality
+ * isl_ineq_adj_ineq: adjacent to an inequality.
+ */
+enum isl_ineq_type isl_tab_ineq_type(struct isl_tab *tab, isl_int *ineq)
+{
+ enum isl_ineq_type type = isl_ineq_error;
+ struct isl_tab_undo *snap = NULL;
+ int con;
+ int row;
+
+ if (!tab)
+ return isl_ineq_error;
+
+ if (isl_tab_extend_cons(tab, 1) < 0)
+ return isl_ineq_error;
+
+ snap = isl_tab_snap(tab);
+
+ con = isl_tab_add_row(tab, ineq);
+ if (con < 0)
+ goto error;
+
+ row = tab->con[con].index;
+ if (isl_tab_row_is_redundant(tab, row))
+ type = isl_ineq_redundant;
+ else if (isl_int_is_neg(tab->mat->row[row][1]) &&
+ (tab->rational ||
+ isl_int_abs_ge(tab->mat->row[row][1],
+ tab->mat->row[row][0]))) {
+ int nonneg = at_least_zero(tab, &tab->con[con]);
+ if (nonneg < 0)
+ goto error;
+ if (nonneg)
+ type = isl_ineq_cut;
+ else
+ type = separation_type(tab, row);
+ } else {
+ int red = con_is_redundant(tab, &tab->con[con]);
+ if (red < 0)
+ goto error;
+ if (!red)
+ type = isl_ineq_cut;
+ else
+ type = isl_ineq_redundant;
+ }
+
+ if (isl_tab_rollback(tab, snap))
+ return isl_ineq_error;
+ return type;
+error:
+ return isl_ineq_error;
+}
+
+isl_stat isl_tab_track_bmap(struct isl_tab *tab, __isl_take isl_basic_map *bmap)
+{
+ bmap = isl_basic_map_cow(bmap);
+ if (!tab || !bmap)
+ goto error;
+
+ if (tab->empty) {
+ bmap = isl_basic_map_set_to_empty(bmap);
+ if (!bmap)
+ goto error;
+ tab->bmap = bmap;
+ return isl_stat_ok;
+ }
+
+ isl_assert(tab->mat->ctx, tab->n_eq == bmap->n_eq, goto error);
+ isl_assert(tab->mat->ctx,
+ tab->n_con == bmap->n_eq + bmap->n_ineq, goto error);
+
+ tab->bmap = bmap;
+
+ return isl_stat_ok;
+error:
+ isl_basic_map_free(bmap);
+ return isl_stat_error;
+}
+
+isl_stat isl_tab_track_bset(struct isl_tab *tab, __isl_take isl_basic_set *bset)
+{
+ return isl_tab_track_bmap(tab, bset_to_bmap(bset));
+}
+
+__isl_keep isl_basic_set *isl_tab_peek_bset(struct isl_tab *tab)
+{
+ if (!tab)
+ return NULL;
+
+ return bset_from_bmap(tab->bmap);
+}
+
+static void isl_tab_print_internal(__isl_keep struct isl_tab *tab,
+ FILE *out, int indent)
+{
+ unsigned r, c;
+ int i;
+
+ if (!tab) {
+ fprintf(out, "%*snull tab\n", indent, "");
+ return;
+ }
+ fprintf(out, "%*sn_redundant: %d, n_dead: %d", indent, "",
+ tab->n_redundant, tab->n_dead);
+ if (tab->rational)
+ fprintf(out, ", rational");
+ if (tab->empty)
+ fprintf(out, ", empty");
+ fprintf(out, "\n");
+ fprintf(out, "%*s[", indent, "");
+ for (i = 0; i < tab->n_var; ++i) {
+ if (i)
+ fprintf(out, (i == tab->n_param ||
+ i == tab->n_var - tab->n_div) ? "; "
+ : ", ");
+ fprintf(out, "%c%d%s", tab->var[i].is_row ? 'r' : 'c',
+ tab->var[i].index,
+ tab->var[i].is_zero ? " [=0]" :
+ tab->var[i].is_redundant ? " [R]" : "");
+ }
+ fprintf(out, "]\n");
+ fprintf(out, "%*s[", indent, "");
+ for (i = 0; i < tab->n_con; ++i) {
+ if (i)
+ fprintf(out, ", ");
+ fprintf(out, "%c%d%s", tab->con[i].is_row ? 'r' : 'c',
+ tab->con[i].index,
+ tab->con[i].is_zero ? " [=0]" :
+ tab->con[i].is_redundant ? " [R]" : "");
+ }
+ fprintf(out, "]\n");
+ fprintf(out, "%*s[", indent, "");
+ for (i = 0; i < tab->n_row; ++i) {
+ const char *sign = "";
+ if (i)
+ fprintf(out, ", ");
+ if (tab->row_sign) {
+ if (tab->row_sign[i] == isl_tab_row_unknown)
+ sign = "?";
+ else if (tab->row_sign[i] == isl_tab_row_neg)
+ sign = "-";
+ else if (tab->row_sign[i] == isl_tab_row_pos)
+ sign = "+";
+ else
+ sign = "+-";
+ }
+ fprintf(out, "r%d: %d%s%s", i, tab->row_var[i],
+ isl_tab_var_from_row(tab, i)->is_nonneg ? " [>=0]" : "", sign);
+ }
+ fprintf(out, "]\n");
+ fprintf(out, "%*s[", indent, "");
+ for (i = 0; i < tab->n_col; ++i) {
+ if (i)
+ fprintf(out, ", ");
+ fprintf(out, "c%d: %d%s", i, tab->col_var[i],
+ var_from_col(tab, i)->is_nonneg ? " [>=0]" : "");
+ }
+ fprintf(out, "]\n");
+ r = tab->mat->n_row;
+ tab->mat->n_row = tab->n_row;
+ c = tab->mat->n_col;
+ tab->mat->n_col = 2 + tab->M + tab->n_col;
+ isl_mat_print_internal(tab->mat, out, indent);
+ tab->mat->n_row = r;
+ tab->mat->n_col = c;
+ if (tab->bmap)
+ isl_basic_map_print_internal(tab->bmap, out, indent);
+}
+
+void isl_tab_dump(__isl_keep struct isl_tab *tab)
+{
+ isl_tab_print_internal(tab, stderr, 0);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab.h
new file mode 100644
index 00000000000..b580245b628
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab.h
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ */
+
+#ifndef ISL_TAB_H
+#define ISL_TAB_H
+
+#include <isl/lp.h>
+#include <isl/map.h>
+#include <isl/mat.h>
+#include <isl/set.h>
+#include <isl_config.h>
+
+struct isl_tab_var {
+ int index;
+ unsigned is_row : 1;
+ unsigned is_nonneg : 1;
+ unsigned is_zero : 1;
+ unsigned is_redundant : 1;
+ unsigned marked : 1;
+ unsigned frozen : 1;
+ unsigned negated : 1;
+};
+
+enum isl_tab_undo_type {
+ isl_tab_undo_bottom,
+ isl_tab_undo_rational,
+ isl_tab_undo_empty,
+ isl_tab_undo_nonneg,
+ isl_tab_undo_redundant,
+ isl_tab_undo_freeze,
+ isl_tab_undo_zero,
+ isl_tab_undo_allocate,
+ isl_tab_undo_relax,
+ isl_tab_undo_unrestrict,
+ isl_tab_undo_bmap_ineq,
+ isl_tab_undo_bmap_eq,
+ isl_tab_undo_bmap_div,
+ isl_tab_undo_saved_basis,
+ isl_tab_undo_drop_sample,
+ isl_tab_undo_saved_samples,
+ isl_tab_undo_callback,
+};
+
+struct isl_tab_callback {
+ isl_stat (*run)(struct isl_tab_callback *cb);
+};
+
+union isl_tab_undo_val {
+ int var_index;
+ int *col_var;
+ int n;
+ struct isl_tab_callback *callback;
+};
+
+struct isl_tab_undo {
+ enum isl_tab_undo_type type;
+ union isl_tab_undo_val u;
+ struct isl_tab_undo *next;
+};
+
+/* The tableau maintains equality relations.
+ * Each column and each row is associated to a variable or a constraint.
+ * The "value" of an inequality constraint is the value of the corresponding
+ * slack variable.
+ * The "row_var" and "col_var" arrays map column and row indices
+ * to indices in the "var" and "con" arrays. The elements of these
+ * arrays maintain extra information about the variables and the constraints.
+ * Each row expresses the corresponding row variable as an affine expression
+ * of the column variables.
+ * The first two columns in the matrix contain the common denominator of
+ * the row and the numerator of the constant term.
+ * If "M" is set, then the third column represents the "big parameter".
+ * The third (M = 0) or fourth (M = 1) column
+ * in the matrix is called column 0 with respect to the col_var array.
+ * The sample value of the tableau is the value that assigns zero
+ * to all the column variables and the constant term of each affine
+ * expression to the corresponding row variable.
+ * The operations on the tableau maintain the property that the sample
+ * value satisfies the non-negativity constraints (usually on the slack
+ * variables).
+ *
+ * The big parameter represents an arbitrarily big (and divisible)
+ * positive number. If present, then the sign of a row is determined
+ * lexicographically, with the sign of the big parameter coefficient
+ * considered first. The big parameter is only used while
+ * solving PILP problems.
+ *
+ * The first n_dead column variables have their values fixed to zero.
+ * The corresponding tab_vars are flagged "is_zero".
+ * Some of the rows that have have zero coefficients in all but
+ * the dead columns are also flagged "is_zero".
+ *
+ * The first n_redundant rows correspond to inequality constraints
+ * that are always satisfied for any value satisfying the non-redundant
+ * rows. The corresponding tab_vars are flagged "is_redundant".
+ * A row variable that is flagged "is_zero" is also flagged "is_redundant"
+ * since the constraint has been reduced to 0 = 0 and is therefore always
+ * satisfied.
+ *
+ * There are "n_var" variables in total. The first "n_param" of these
+ * are called parameters and the last "n_div" of these are called divs.
+ * The basic tableau operations makes no distinction between different
+ * kinds of variables. These special variables are only used while
+ * solving PILP problems.
+ *
+ * Dead columns and redundant rows are detected on the fly.
+ * However, the basic operations do not ensure that all dead columns
+ * or all redundant rows are detected.
+ * isl_tab_detect_implicit_equalities and isl_tab_detect_redundant can be used
+ * to perform an exhaustive search for dead columns and redundant rows.
+ *
+ * The samples matrix contains "n_sample" integer points that have at some
+ * point been elements satisfying the tableau. The first "n_outside"
+ * of them no longer satisfy the tableau. They are kept because they
+ * can be reinstated during rollback when the constraint that cut them
+ * out is removed. These samples are only maintained for the context
+ * tableau while solving PILP problems.
+ *
+ * If "preserve" is set, then we want to keep all constraints in the
+ * tableau, even if they turn out to be redundant.
+ */
+enum isl_tab_row_sign {
+ isl_tab_row_unknown = 0,
+ isl_tab_row_pos,
+ isl_tab_row_neg,
+ isl_tab_row_any,
+};
+struct isl_tab {
+ struct isl_mat *mat;
+
+ unsigned n_row;
+ unsigned n_col;
+ unsigned n_dead;
+ unsigned n_redundant;
+
+ unsigned n_var;
+ unsigned n_param;
+ unsigned n_div;
+ unsigned max_var;
+ unsigned n_con;
+ unsigned n_eq;
+ unsigned max_con;
+ struct isl_tab_var *var;
+ struct isl_tab_var *con;
+ int *row_var; /* v >= 0 -> var v; v < 0 -> con ~v */
+ int *col_var; /* v >= 0 -> var v; v < 0 -> con ~v */
+ enum isl_tab_row_sign *row_sign;
+
+ struct isl_tab_undo bottom;
+ struct isl_tab_undo *top;
+
+ struct isl_vec *dual;
+ struct isl_basic_map *bmap;
+
+ unsigned n_sample;
+ unsigned n_outside;
+ int *sample_index;
+ struct isl_mat *samples;
+
+ int n_zero;
+ int n_unbounded;
+ struct isl_mat *basis;
+
+ int (*conflict)(int con, void *user);
+ void *conflict_user;
+
+ unsigned strict_redundant : 1;
+ unsigned need_undo : 1;
+ unsigned preserve : 1;
+ unsigned rational : 1;
+ unsigned empty : 1;
+ unsigned in_undo : 1;
+ unsigned M : 1;
+ unsigned cone : 1;
+};
+
+struct isl_tab *isl_tab_alloc(struct isl_ctx *ctx,
+ unsigned n_row, unsigned n_var, unsigned M);
+void isl_tab_free(struct isl_tab *tab);
+
+isl_ctx *isl_tab_get_ctx(struct isl_tab *tab);
+
+__isl_give struct isl_tab *isl_tab_from_basic_map(
+ __isl_keep isl_basic_map *bmap, int track);
+__isl_give struct isl_tab *isl_tab_from_basic_set(
+ __isl_keep isl_basic_set *bset, int track);
+struct isl_tab *isl_tab_from_recession_cone(__isl_keep isl_basic_set *bset,
+ int parametric);
+isl_bool isl_tab_cone_is_bounded(struct isl_tab *tab);
+__isl_give isl_basic_map *isl_basic_map_update_from_tab(
+ __isl_take isl_basic_map *bmap, struct isl_tab *tab);
+__isl_give isl_basic_set *isl_basic_set_update_from_tab(
+ __isl_take isl_basic_set *bset, struct isl_tab *tab);
+int isl_tab_detect_implicit_equalities(struct isl_tab *tab) WARN_UNUSED;
+__isl_give isl_basic_map *isl_tab_make_equalities_explicit(struct isl_tab *tab,
+ __isl_take isl_basic_map *bmap);
+int isl_tab_detect_redundant(struct isl_tab *tab) WARN_UNUSED;
+isl_stat isl_tab_restore_redundant(struct isl_tab *tab);
+#define ISL_TAB_SAVE_DUAL (1 << 0)
+enum isl_lp_result isl_tab_min(struct isl_tab *tab,
+ isl_int *f, isl_int denom, isl_int *opt, isl_int *opt_denom,
+ unsigned flags) WARN_UNUSED;
+
+isl_stat isl_tab_add_ineq(struct isl_tab *tab, isl_int *ineq) WARN_UNUSED;
+isl_stat isl_tab_add_eq(struct isl_tab *tab, isl_int *eq) WARN_UNUSED;
+int isl_tab_add_valid_eq(struct isl_tab *tab, isl_int *eq) WARN_UNUSED;
+
+int isl_tab_freeze_constraint(struct isl_tab *tab, int con) WARN_UNUSED;
+
+isl_stat isl_tab_track_bmap(struct isl_tab *tab, __isl_take isl_basic_map *bmap)
+ WARN_UNUSED;
+isl_stat isl_tab_track_bset(struct isl_tab *tab, __isl_take isl_basic_set *bset)
+ WARN_UNUSED;
+__isl_keep isl_basic_set *isl_tab_peek_bset(struct isl_tab *tab);
+
+int isl_tab_is_equality(struct isl_tab *tab, int con);
+int isl_tab_is_redundant(struct isl_tab *tab, int con);
+
+int isl_tab_sample_is_integer(struct isl_tab *tab);
+__isl_give isl_vec *isl_tab_get_sample_value(struct isl_tab *tab);
+
+enum isl_ineq_type {
+ isl_ineq_error = -1,
+ isl_ineq_redundant,
+ isl_ineq_separate,
+ isl_ineq_cut,
+ isl_ineq_adj_eq,
+ isl_ineq_adj_ineq,
+};
+
+enum isl_ineq_type isl_tab_ineq_type(struct isl_tab *tab, isl_int *ineq);
+
+struct isl_tab_undo *isl_tab_snap(struct isl_tab *tab);
+isl_stat isl_tab_rollback(struct isl_tab *tab, struct isl_tab_undo *snap) WARN_UNUSED;
+isl_bool isl_tab_need_undo(struct isl_tab *tab);
+void isl_tab_clear_undo(struct isl_tab *tab);
+
+int isl_tab_relax(struct isl_tab *tab, int con) WARN_UNUSED;
+int isl_tab_select_facet(struct isl_tab *tab, int con) WARN_UNUSED;
+int isl_tab_unrestrict(struct isl_tab *tab, int con) WARN_UNUSED;
+
+void isl_tab_dump(__isl_keep struct isl_tab *tab);
+
+/* Compute maximum instead of minimum. */
+#define ISL_OPT_MAX (1 << 0)
+/* Compute full instead of partial optimum; also, domain argument is NULL. */
+#define ISL_OPT_FULL (1 << 1)
+/* Result should be free of (unknown) quantified variables. */
+#define ISL_OPT_QE (1 << 2)
+__isl_give isl_map *isl_tab_basic_map_partial_lexopt(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, unsigned flags);
+__isl_give isl_pw_multi_aff *isl_tab_basic_map_partial_lexopt_pw_multi_aff(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, unsigned flags);
+
+/* An isl_trivial_region represents a non-triviality region.
+ * The region is trivial if applying "trivial" to a given sequence
+ * of variables results in a zero vector.
+ * pos is the location (starting at 0) of the first variable in the sequence.
+ */
+struct isl_trivial_region {
+ int pos;
+ isl_mat *trivial;
+};
+
+__isl_give isl_vec *isl_tab_basic_set_non_trivial_lexmin(
+ __isl_take isl_basic_set *bset, int n_op, int n_region,
+ struct isl_trivial_region *region,
+ int (*conflict)(int con, void *user), void *user);
+
+struct isl_tab_lexmin;
+typedef struct isl_tab_lexmin isl_tab_lexmin;
+
+__isl_give isl_tab_lexmin *isl_tab_lexmin_from_basic_set(
+ __isl_take isl_basic_set *bset);
+int isl_tab_lexmin_dim(__isl_keep isl_tab_lexmin *tl);
+__isl_give isl_tab_lexmin *isl_tab_lexmin_add_eq(__isl_take isl_tab_lexmin *tl,
+ isl_int *eq);
+__isl_give isl_tab_lexmin *isl_tab_lexmin_cut_to_integer(
+ __isl_take isl_tab_lexmin *tl);
+__isl_give isl_vec *isl_tab_lexmin_get_solution(__isl_keep isl_tab_lexmin *tl);
+__isl_null isl_tab_lexmin *isl_tab_lexmin_free(__isl_take isl_tab_lexmin *tl);
+
+/* private */
+
+struct isl_tab_var *isl_tab_var_from_row(struct isl_tab *tab, int i);
+int isl_tab_mark_redundant(struct isl_tab *tab, int row) WARN_UNUSED;
+int isl_tab_mark_rational(struct isl_tab *tab) WARN_UNUSED;
+isl_stat isl_tab_mark_empty(struct isl_tab *tab) WARN_UNUSED;
+struct isl_tab *isl_tab_dup(struct isl_tab *tab);
+struct isl_tab *isl_tab_product(struct isl_tab *tab1, struct isl_tab *tab2);
+int isl_tab_extend_cons(struct isl_tab *tab, unsigned n_new) WARN_UNUSED;
+int isl_tab_allocate_con(struct isl_tab *tab) WARN_UNUSED;
+int isl_tab_extend_vars(struct isl_tab *tab, unsigned n_new) WARN_UNUSED;
+int isl_tab_insert_var(struct isl_tab *tab, int pos) WARN_UNUSED;
+int isl_tab_pivot(struct isl_tab *tab, int row, int col) WARN_UNUSED;
+int isl_tab_add_row(struct isl_tab *tab, isl_int *line) WARN_UNUSED;
+int isl_tab_row_is_redundant(struct isl_tab *tab, int row);
+int isl_tab_min_at_most_neg_one(struct isl_tab *tab, struct isl_tab_var *var);
+int isl_tab_sign_of_max(struct isl_tab *tab, int con);
+int isl_tab_kill_col(struct isl_tab *tab, int col) WARN_UNUSED;
+
+isl_stat isl_tab_push(struct isl_tab *tab, enum isl_tab_undo_type type)
+ WARN_UNUSED;
+isl_stat isl_tab_push_var(struct isl_tab *tab,
+ enum isl_tab_undo_type type, struct isl_tab_var *var) WARN_UNUSED;
+isl_stat isl_tab_push_basis(struct isl_tab *tab) WARN_UNUSED;
+
+struct isl_tab *isl_tab_init_samples(struct isl_tab *tab) WARN_UNUSED;
+int isl_tab_add_sample(struct isl_tab *tab,
+ __isl_take isl_vec *sample) WARN_UNUSED;
+struct isl_tab *isl_tab_drop_sample(struct isl_tab *tab, int s);
+isl_stat isl_tab_save_samples(struct isl_tab *tab) WARN_UNUSED;
+
+struct isl_tab *isl_tab_detect_equalities(struct isl_tab *tab,
+ struct isl_tab *tab_cone) WARN_UNUSED;
+isl_bool isl_tab_is_constant(struct isl_tab *tab, int var, isl_int *value);
+isl_stat isl_tab_detect_constants(struct isl_tab *tab);
+
+isl_stat isl_tab_push_callback(struct isl_tab *tab,
+ struct isl_tab_callback *callback) WARN_UNUSED;
+
+int isl_tab_insert_div(struct isl_tab *tab, int pos, __isl_keep isl_vec *div,
+ isl_stat (*add_ineq)(void *user, isl_int *), void *user);
+int isl_tab_add_div(struct isl_tab *tab, __isl_keep isl_vec *div);
+
+int isl_tab_shift_var(struct isl_tab *tab, int pos, isl_int shift) WARN_UNUSED;
+
+isl_stat isl_tab_swap_constraints(struct isl_tab *tab, int con1, int con2);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab_lexopt_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab_lexopt_templ.c
new file mode 100644
index 00000000000..45eba10631c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab_lexopt_templ.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2011 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ */
+
+#define xSF(TYPE,SUFFIX) TYPE ## SUFFIX
+#define SF(TYPE,SUFFIX) xSF(TYPE,SUFFIX)
+
+/* Given a basic map with at least two parallel constraints (as found
+ * by the function parallel_constraints), first look for more constraints
+ * parallel to the two constraint and replace the found list of parallel
+ * constraints by a single constraint with as "input" part the minimum
+ * of the input parts of the list of constraints. Then, recursively call
+ * basic_map_partial_lexopt (possibly finding more parallel constraints)
+ * and plug in the definition of the minimum in the result.
+ *
+ * As in parallel_constraints, only inequality constraints that only
+ * involve input variables that do not occur in any other inequality
+ * constraints are considered.
+ *
+ * More specifically, given a set of constraints
+ *
+ * a x + b_i(p) >= 0
+ *
+ * Replace this set by a single constraint
+ *
+ * a x + u >= 0
+ *
+ * with u a new parameter with constraints
+ *
+ * u <= b_i(p)
+ *
+ * Any solution to the new system is also a solution for the original system
+ * since
+ *
+ * a x >= -u >= -b_i(p)
+ *
+ * Moreover, m = min_i(b_i(p)) satisfies the constraints on u and can
+ * therefore be plugged into the solution.
+ */
+static TYPE *SF(basic_map_partial_lexopt_symm,SUFFIX)(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, int max, int first, int second)
+{
+ int i, n, k;
+ int *list = NULL;
+ isl_size bmap_in, bmap_param, bmap_all;
+ unsigned n_in, n_out, n_div;
+ isl_ctx *ctx;
+ isl_vec *var = NULL;
+ isl_mat *cst = NULL;
+ isl_space *map_space, *set_space;
+
+ map_space = isl_basic_map_get_space(bmap);
+ set_space = empty ? isl_basic_set_get_space(dom) : NULL;
+
+ bmap_in = isl_basic_map_dim(bmap, isl_dim_in);
+ bmap_param = isl_basic_map_dim(bmap, isl_dim_param);
+ bmap_all = isl_basic_map_dim(bmap, isl_dim_all);
+ if (bmap_in < 0 || bmap_param < 0 || bmap_all < 0)
+ goto error;
+ n_in = bmap_param + bmap_in;
+ n_out = bmap_all - n_in;
+
+ ctx = isl_basic_map_get_ctx(bmap);
+ list = isl_alloc_array(ctx, int, bmap->n_ineq);
+ var = isl_vec_alloc(ctx, n_out);
+ if ((bmap->n_ineq && !list) || (n_out && !var))
+ goto error;
+
+ list[0] = first;
+ list[1] = second;
+ isl_seq_cpy(var->el, bmap->ineq[first] + 1 + n_in, n_out);
+ for (i = second + 1, n = 2; i < bmap->n_ineq; ++i) {
+ if (isl_seq_eq(var->el, bmap->ineq[i] + 1 + n_in, n_out) &&
+ all_single_occurrence(bmap, i, n_in))
+ list[n++] = i;
+ }
+
+ cst = isl_mat_alloc(ctx, n, 1 + n_in);
+ if (!cst)
+ goto error;
+
+ for (i = 0; i < n; ++i)
+ isl_seq_cpy(cst->row[i], bmap->ineq[list[i]], 1 + n_in);
+
+ bmap = isl_basic_map_cow(bmap);
+ if (!bmap)
+ goto error;
+ for (i = n - 1; i >= 0; --i)
+ if (isl_basic_map_drop_inequality(bmap, list[i]) < 0)
+ goto error;
+
+ bmap = isl_basic_map_add_dims(bmap, isl_dim_in, 1);
+ bmap = isl_basic_map_extend_constraints(bmap, 0, 1);
+ k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[k], 1 + n_in);
+ isl_int_set_si(bmap->ineq[k][1 + n_in], 1);
+ isl_seq_cpy(bmap->ineq[k] + 1 + n_in + 1, var->el, n_out);
+ bmap = isl_basic_map_finalize(bmap);
+
+ n_div = isl_basic_set_dim(dom, isl_dim_div);
+ dom = isl_basic_set_add_dims(dom, isl_dim_set, 1);
+ dom = isl_basic_set_extend_constraints(dom, 0, n);
+ for (i = 0; i < n; ++i) {
+ k = isl_basic_set_alloc_inequality(dom);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(dom->ineq[k], cst->row[i], 1 + n_in);
+ isl_int_set_si(dom->ineq[k][1 + n_in], -1);
+ isl_seq_clr(dom->ineq[k] + 1 + n_in + 1, n_div);
+ }
+
+ isl_vec_free(var);
+ free(list);
+
+ return SF(basic_map_partial_lexopt_symm_core,SUFFIX)(bmap, dom, empty,
+ max, cst, map_space, set_space);
+error:
+ isl_space_free(map_space);
+ isl_space_free(set_space);
+ isl_mat_free(cst);
+ isl_vec_free(var);
+ free(list);
+ isl_basic_set_free(dom);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Recursive part of isl_tab_basic_map_partial_lexopt*, after detecting
+ * equalities and removing redundant constraints.
+ *
+ * We first check if there are any parallel constraints (left).
+ * If not, we are in the base case.
+ * If there are parallel constraints, we replace them by a single
+ * constraint in basic_map_partial_lexopt_symm_pma and then call
+ * this function recursively to look for more parallel constraints.
+ */
+static __isl_give TYPE *SF(basic_map_partial_lexopt,SUFFIX)(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, int max)
+{
+ isl_bool par = isl_bool_false;
+ int first, second;
+
+ if (!bmap)
+ goto error;
+
+ if (bmap->ctx->opt->pip_symmetry)
+ par = parallel_constraints(bmap, &first, &second);
+ if (par < 0)
+ goto error;
+ if (!par)
+ return SF(basic_map_partial_lexopt_base,SUFFIX)(bmap, dom,
+ empty, max);
+
+ return SF(basic_map_partial_lexopt_symm,SUFFIX)(bmap, dom, empty, max,
+ first, second);
+error:
+ isl_basic_set_free(dom);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Compute the lexicographic minimum (or maximum if "flags" includes
+ * ISL_OPT_MAX) of "bmap" over the domain "dom" and return the result as
+ * either a map or a piecewise multi-affine expression depending on TYPE.
+ * If "empty" is not NULL, then *empty is assigned a set that
+ * contains those parts of the domain where there is no solution.
+ * If "flags" includes ISL_OPT_FULL, then "dom" is NULL and the optimum
+ * should be computed over the domain of "bmap". "empty" is also NULL
+ * in this case.
+ * If "bmap" is marked as rational (ISL_BASIC_MAP_RATIONAL),
+ * then we compute the rational optimum. Otherwise, we compute
+ * the integral optimum.
+ *
+ * We perform some preprocessing. As the PILP solver does not
+ * handle implicit equalities very well, we first make sure all
+ * the equalities are explicitly available.
+ *
+ * We also add context constraints to the basic map and remove
+ * redundant constraints. This is only needed because of the
+ * way we handle simple symmetries. In particular, we currently look
+ * for symmetries on the constraints, before we set up the main tableau.
+ * It is then no good to look for symmetries on possibly redundant constraints.
+ * If the domain was extracted from the basic map, then there is
+ * no need to add back those constraints again.
+ */
+__isl_give TYPE *SF(isl_tab_basic_map_partial_lexopt,SUFFIX)(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, unsigned flags)
+{
+ int max, full;
+ isl_bool compatible;
+
+ if (empty)
+ *empty = NULL;
+
+ full = ISL_FL_ISSET(flags, ISL_OPT_FULL);
+ if (full)
+ dom = extract_domain(bmap, flags);
+ compatible = isl_basic_map_compatible_domain(bmap, dom);
+ if (compatible < 0)
+ goto error;
+ if (!compatible)
+ isl_die(isl_basic_map_get_ctx(bmap), isl_error_invalid,
+ "domain does not match input", goto error);
+
+ max = ISL_FL_ISSET(flags, ISL_OPT_MAX);
+ if (isl_basic_set_dim(dom, isl_dim_all) == 0)
+ return SF(basic_map_partial_lexopt,SUFFIX)(bmap, dom, empty,
+ max);
+
+ if (!full)
+ bmap = isl_basic_map_intersect_domain(bmap,
+ isl_basic_set_copy(dom));
+ bmap = isl_basic_map_detect_equalities(bmap);
+ bmap = isl_basic_map_remove_redundancies(bmap);
+
+ return SF(basic_map_partial_lexopt,SUFFIX)(bmap, dom, empty, max);
+error:
+ isl_basic_set_free(dom);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab_pip.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab_pip.c
new file mode 100644
index 00000000000..5a3148b78e7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tab_pip.c
@@ -0,0 +1,6011 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2016-2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ */
+
+#include <isl_ctx_private.h>
+#include "isl_map_private.h"
+#include <isl_seq.h>
+#include "isl_tab.h"
+#include "isl_sample.h"
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+#include <isl_aff_private.h>
+#include <isl_constraint_private.h>
+#include <isl_options_private.h>
+#include <isl_config.h>
+
+#include <bset_to_bmap.c>
+
+/*
+ * The implementation of parametric integer linear programming in this file
+ * was inspired by the paper "Parametric Integer Programming" and the
+ * report "Solving systems of affine (in)equalities" by Paul Feautrier
+ * (and others).
+ *
+ * The strategy used for obtaining a feasible solution is different
+ * from the one used in isl_tab.c. In particular, in isl_tab.c,
+ * upon finding a constraint that is not yet satisfied, we pivot
+ * in a row that increases the constant term of the row holding the
+ * constraint, making sure the sample solution remains feasible
+ * for all the constraints it already satisfied.
+ * Here, we always pivot in the row holding the constraint,
+ * choosing a column that induces the lexicographically smallest
+ * increment to the sample solution.
+ *
+ * By starting out from a sample value that is lexicographically
+ * smaller than any integer point in the problem space, the first
+ * feasible integer sample point we find will also be the lexicographically
+ * smallest. If all variables can be assumed to be non-negative,
+ * then the initial sample value may be chosen equal to zero.
+ * However, we will not make this assumption. Instead, we apply
+ * the "big parameter" trick. Any variable x is then not directly
+ * used in the tableau, but instead it is represented by another
+ * variable x' = M + x, where M is an arbitrarily large (positive)
+ * value. x' is therefore always non-negative, whatever the value of x.
+ * Taking as initial sample value x' = 0 corresponds to x = -M,
+ * which is always smaller than any possible value of x.
+ *
+ * The big parameter trick is used in the main tableau and
+ * also in the context tableau if isl_context_lex is used.
+ * In this case, each tableaus has its own big parameter.
+ * Before doing any real work, we check if all the parameters
+ * happen to be non-negative. If so, we drop the column corresponding
+ * to M from the initial context tableau.
+ * If isl_context_gbr is used, then the big parameter trick is only
+ * used in the main tableau.
+ */
+
+struct isl_context;
+struct isl_context_op {
+ /* detect nonnegative parameters in context and mark them in tab */
+ struct isl_tab *(*detect_nonnegative_parameters)(
+ struct isl_context *context, struct isl_tab *tab);
+ /* return temporary reference to basic set representation of context */
+ struct isl_basic_set *(*peek_basic_set)(struct isl_context *context);
+ /* return temporary reference to tableau representation of context */
+ struct isl_tab *(*peek_tab)(struct isl_context *context);
+ /* add equality; check is 1 if eq may not be valid;
+ * update is 1 if we may want to call ineq_sign on context later.
+ */
+ void (*add_eq)(struct isl_context *context, isl_int *eq,
+ int check, int update);
+ /* add inequality; check is 1 if ineq may not be valid;
+ * update is 1 if we may want to call ineq_sign on context later.
+ */
+ void (*add_ineq)(struct isl_context *context, isl_int *ineq,
+ int check, int update);
+ /* check sign of ineq based on previous information.
+ * strict is 1 if saturation should be treated as a positive sign.
+ */
+ enum isl_tab_row_sign (*ineq_sign)(struct isl_context *context,
+ isl_int *ineq, int strict);
+ /* check if inequality maintains feasibility */
+ int (*test_ineq)(struct isl_context *context, isl_int *ineq);
+ /* return index of a div that corresponds to "div" */
+ int (*get_div)(struct isl_context *context, struct isl_tab *tab,
+ struct isl_vec *div);
+ /* insert div "div" to context at "pos" and return non-negativity */
+ isl_bool (*insert_div)(struct isl_context *context, int pos,
+ __isl_keep isl_vec *div);
+ int (*detect_equalities)(struct isl_context *context,
+ struct isl_tab *tab);
+ /* return row index of "best" split */
+ int (*best_split)(struct isl_context *context, struct isl_tab *tab);
+ /* check if context has already been determined to be empty */
+ int (*is_empty)(struct isl_context *context);
+ /* check if context is still usable */
+ int (*is_ok)(struct isl_context *context);
+ /* save a copy/snapshot of context */
+ void *(*save)(struct isl_context *context);
+ /* restore saved context */
+ void (*restore)(struct isl_context *context, void *);
+ /* discard saved context */
+ void (*discard)(void *);
+ /* invalidate context */
+ void (*invalidate)(struct isl_context *context);
+ /* free context */
+ __isl_null struct isl_context *(*free)(struct isl_context *context);
+};
+
+/* Shared parts of context representation.
+ *
+ * "n_unknown" is the number of final unknown integer divisions
+ * in the input domain.
+ */
+struct isl_context {
+ struct isl_context_op *op;
+ int n_unknown;
+};
+
+struct isl_context_lex {
+ struct isl_context context;
+ struct isl_tab *tab;
+};
+
+/* A stack (linked list) of solutions of subtrees of the search space.
+ *
+ * "ma" describes the solution as a function of "dom".
+ * In particular, the domain space of "ma" is equal to the space of "dom".
+ *
+ * If "ma" is NULL, then there is no solution on "dom".
+ */
+struct isl_partial_sol {
+ int level;
+ struct isl_basic_set *dom;
+ isl_multi_aff *ma;
+
+ struct isl_partial_sol *next;
+};
+
+struct isl_sol;
+struct isl_sol_callback {
+ struct isl_tab_callback callback;
+ struct isl_sol *sol;
+};
+
+/* isl_sol is an interface for constructing a solution to
+ * a parametric integer linear programming problem.
+ * Every time the algorithm reaches a state where a solution
+ * can be read off from the tableau, the function "add" is called
+ * on the isl_sol passed to find_solutions_main. In a state where
+ * the tableau is empty, "add_empty" is called instead.
+ * "free" is called to free the implementation specific fields, if any.
+ *
+ * "error" is set if some error has occurred. This flag invalidates
+ * the remainder of the data structure.
+ * If "rational" is set, then a rational optimization is being performed.
+ * "level" is the current level in the tree with nodes for each
+ * split in the context.
+ * If "max" is set, then a maximization problem is being solved, rather than
+ * a minimization problem, which means that the variables in the
+ * tableau have value "M - x" rather than "M + x".
+ * "n_out" is the number of output dimensions in the input.
+ * "space" is the space in which the solution (and also the input) lives.
+ *
+ * The context tableau is owned by isl_sol and is updated incrementally.
+ *
+ * There are currently two implementations of this interface,
+ * isl_sol_map, which simply collects the solutions in an isl_map
+ * and (optionally) the parts of the context where there is no solution
+ * in an isl_set, and
+ * isl_sol_pma, which collects an isl_pw_multi_aff instead.
+ */
+struct isl_sol {
+ int error;
+ int rational;
+ int level;
+ int max;
+ isl_size n_out;
+ isl_space *space;
+ struct isl_context *context;
+ struct isl_partial_sol *partial;
+ void (*add)(struct isl_sol *sol,
+ __isl_take isl_basic_set *dom, __isl_take isl_multi_aff *ma);
+ void (*add_empty)(struct isl_sol *sol, struct isl_basic_set *bset);
+ void (*free)(struct isl_sol *sol);
+ struct isl_sol_callback dec_level;
+};
+
+static void sol_free(struct isl_sol *sol)
+{
+ struct isl_partial_sol *partial, *next;
+ if (!sol)
+ return;
+ for (partial = sol->partial; partial; partial = next) {
+ next = partial->next;
+ isl_basic_set_free(partial->dom);
+ isl_multi_aff_free(partial->ma);
+ free(partial);
+ }
+ isl_space_free(sol->space);
+ if (sol->context)
+ sol->context->op->free(sol->context);
+ sol->free(sol);
+ free(sol);
+}
+
+/* Push a partial solution represented by a domain and function "ma"
+ * onto the stack of partial solutions.
+ * If "ma" is NULL, then "dom" represents a part of the domain
+ * with no solution.
+ */
+static void sol_push_sol(struct isl_sol *sol,
+ __isl_take isl_basic_set *dom, __isl_take isl_multi_aff *ma)
+{
+ struct isl_partial_sol *partial;
+
+ if (sol->error || !dom)
+ goto error;
+
+ partial = isl_alloc_type(dom->ctx, struct isl_partial_sol);
+ if (!partial)
+ goto error;
+
+ partial->level = sol->level;
+ partial->dom = dom;
+ partial->ma = ma;
+ partial->next = sol->partial;
+
+ sol->partial = partial;
+
+ return;
+error:
+ isl_basic_set_free(dom);
+ isl_multi_aff_free(ma);
+ sol->error = 1;
+}
+
+/* Check that the final columns of "M", starting at "first", are zero.
+ */
+static isl_stat check_final_columns_are_zero(__isl_keep isl_mat *M,
+ unsigned first)
+{
+ int i;
+ isl_size rows, cols;
+ unsigned n;
+
+ rows = isl_mat_rows(M);
+ cols = isl_mat_cols(M);
+ if (rows < 0 || cols < 0)
+ return isl_stat_error;
+ n = cols - first;
+ for (i = 0; i < rows; ++i)
+ if (isl_seq_first_non_zero(M->row[i] + first, n) != -1)
+ isl_die(isl_mat_get_ctx(M), isl_error_internal,
+ "final columns should be zero",
+ return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Set the affine expressions in "ma" according to the rows in "M", which
+ * are defined over the local space "ls".
+ * The matrix "M" may have extra (zero) columns beyond the number
+ * of variables in "ls".
+ */
+static __isl_give isl_multi_aff *set_from_affine_matrix(
+ __isl_take isl_multi_aff *ma, __isl_take isl_local_space *ls,
+ __isl_take isl_mat *M)
+{
+ int i;
+ isl_size dim;
+ isl_aff *aff;
+
+ dim = isl_local_space_dim(ls, isl_dim_all);
+ if (!ma || dim < 0 || !M)
+ goto error;
+
+ if (check_final_columns_are_zero(M, 1 + dim) < 0)
+ goto error;
+ for (i = 1; i < M->n_row; ++i) {
+ aff = isl_aff_alloc(isl_local_space_copy(ls));
+ if (aff) {
+ isl_int_set(aff->v->el[0], M->row[0][0]);
+ isl_seq_cpy(aff->v->el + 1, M->row[i], 1 + dim);
+ }
+ aff = isl_aff_normalize(aff);
+ ma = isl_multi_aff_set_aff(ma, i - 1, aff);
+ }
+ isl_local_space_free(ls);
+ isl_mat_free(M);
+
+ return ma;
+error:
+ isl_local_space_free(ls);
+ isl_mat_free(M);
+ isl_multi_aff_free(ma);
+ return NULL;
+}
+
+/* Push a partial solution represented by a domain and mapping M
+ * onto the stack of partial solutions.
+ *
+ * The affine matrix "M" maps the dimensions of the context
+ * to the output variables. Convert it into an isl_multi_aff and
+ * then call sol_push_sol.
+ *
+ * Note that the description of the initial context may have involved
+ * existentially quantified variables, in which case they also appear
+ * in "dom". These need to be removed before creating the affine
+ * expression because an affine expression cannot be defined in terms
+ * of existentially quantified variables without a known representation.
+ * Since newly added integer divisions are inserted before these
+ * existentially quantified variables, they are still in the final
+ * positions and the corresponding final columns of "M" are zero
+ * because align_context_divs adds the existentially quantified
+ * variables of the context to the main tableau without any constraints and
+ * any equality constraints that are added later on can only serve
+ * to eliminate these existentially quantified variables.
+ */
+static void sol_push_sol_mat(struct isl_sol *sol,
+ __isl_take isl_basic_set *dom, __isl_take isl_mat *M)
+{
+ isl_local_space *ls;
+ isl_multi_aff *ma;
+ isl_size n_div;
+ int n_known;
+
+ n_div = isl_basic_set_dim(dom, isl_dim_div);
+ if (n_div < 0)
+ goto error;
+ n_known = n_div - sol->context->n_unknown;
+
+ ma = isl_multi_aff_alloc(isl_space_copy(sol->space));
+ ls = isl_basic_set_get_local_space(dom);
+ ls = isl_local_space_drop_dims(ls, isl_dim_div,
+ n_known, n_div - n_known);
+ ma = set_from_affine_matrix(ma, ls, M);
+
+ if (!ma)
+ dom = isl_basic_set_free(dom);
+ sol_push_sol(sol, dom, ma);
+ return;
+error:
+ isl_basic_set_free(dom);
+ isl_mat_free(M);
+ sol_push_sol(sol, NULL, NULL);
+}
+
+/* Pop one partial solution from the partial solution stack and
+ * pass it on to sol->add or sol->add_empty.
+ */
+static void sol_pop_one(struct isl_sol *sol)
+{
+ struct isl_partial_sol *partial;
+
+ partial = sol->partial;
+ sol->partial = partial->next;
+
+ if (partial->ma)
+ sol->add(sol, partial->dom, partial->ma);
+ else
+ sol->add_empty(sol, partial->dom);
+ free(partial);
+}
+
+/* Return a fresh copy of the domain represented by the context tableau.
+ */
+static struct isl_basic_set *sol_domain(struct isl_sol *sol)
+{
+ struct isl_basic_set *bset;
+
+ if (sol->error)
+ return NULL;
+
+ bset = isl_basic_set_dup(sol->context->op->peek_basic_set(sol->context));
+ bset = isl_basic_set_update_from_tab(bset,
+ sol->context->op->peek_tab(sol->context));
+
+ return bset;
+}
+
+/* Check whether two partial solutions have the same affine expressions.
+ */
+static isl_bool same_solution(struct isl_partial_sol *s1,
+ struct isl_partial_sol *s2)
+{
+ if (!s1->ma != !s2->ma)
+ return isl_bool_false;
+ if (!s1->ma)
+ return isl_bool_true;
+
+ return isl_multi_aff_plain_is_equal(s1->ma, s2->ma);
+}
+
+/* Swap the initial two partial solutions in "sol".
+ *
+ * That is, go from
+ *
+ * sol->partial = p1; p1->next = p2; p2->next = p3
+ *
+ * to
+ *
+ * sol->partial = p2; p2->next = p1; p1->next = p3
+ */
+static void swap_initial(struct isl_sol *sol)
+{
+ struct isl_partial_sol *partial;
+
+ partial = sol->partial;
+ sol->partial = partial->next;
+ partial->next = partial->next->next;
+ sol->partial->next = partial;
+}
+
+/* Combine the initial two partial solution of "sol" into
+ * a partial solution with the current context domain of "sol" and
+ * the function description of the second partial solution in the list.
+ * The level of the new partial solution is set to the current level.
+ *
+ * That is, the first two partial solutions (D1,M1) and (D2,M2) are
+ * replaced by (D,M2), where D is the domain of "sol", which is assumed
+ * to be the union of D1 and D2, while M1 is assumed to be equal to M2
+ * (at least on D1).
+ */
+static isl_stat combine_initial_into_second(struct isl_sol *sol)
+{
+ struct isl_partial_sol *partial;
+ isl_basic_set *bset;
+
+ partial = sol->partial;
+
+ bset = sol_domain(sol);
+ isl_basic_set_free(partial->next->dom);
+ partial->next->dom = bset;
+ partial->next->level = sol->level;
+
+ if (!bset)
+ return isl_stat_error;
+
+ sol->partial = partial->next;
+ isl_basic_set_free(partial->dom);
+ isl_multi_aff_free(partial->ma);
+ free(partial);
+
+ return isl_stat_ok;
+}
+
+/* Are "ma1" and "ma2" equal to each other on "dom"?
+ *
+ * Combine "ma1" and "ma2" with "dom" and check if the results are the same.
+ * "dom" may have existentially quantified variables. Eliminate them first
+ * as otherwise they would have to be eliminated twice, in a more complicated
+ * context.
+ */
+static isl_bool equal_on_domain(__isl_keep isl_multi_aff *ma1,
+ __isl_keep isl_multi_aff *ma2, __isl_keep isl_basic_set *dom)
+{
+ isl_set *set;
+ isl_pw_multi_aff *pma1, *pma2;
+ isl_bool equal;
+
+ set = isl_basic_set_compute_divs(isl_basic_set_copy(dom));
+ pma1 = isl_pw_multi_aff_alloc(isl_set_copy(set),
+ isl_multi_aff_copy(ma1));
+ pma2 = isl_pw_multi_aff_alloc(set, isl_multi_aff_copy(ma2));
+ equal = isl_pw_multi_aff_is_equal(pma1, pma2);
+ isl_pw_multi_aff_free(pma1);
+ isl_pw_multi_aff_free(pma2);
+
+ return equal;
+}
+
+/* The initial two partial solutions of "sol" are known to be at
+ * the same level.
+ * If they represent the same solution (on different parts of the domain),
+ * then combine them into a single solution at the current level.
+ * Otherwise, pop them both.
+ *
+ * Even if the two partial solution are not obviously the same,
+ * one may still be a simplification of the other over its own domain.
+ * Also check if the two sets of affine functions are equal when
+ * restricted to one of the domains. If so, combine the two
+ * using the set of affine functions on the other domain.
+ * That is, for two partial solutions (D1,M1) and (D2,M2),
+ * if M1 = M2 on D1, then the pair of partial solutions can
+ * be replaced by (D1+D2,M2) and similarly when M1 = M2 on D2.
+ */
+static isl_stat combine_initial_if_equal(struct isl_sol *sol)
+{
+ struct isl_partial_sol *partial;
+ isl_bool same;
+
+ partial = sol->partial;
+
+ same = same_solution(partial, partial->next);
+ if (same < 0)
+ return isl_stat_error;
+ if (same)
+ return combine_initial_into_second(sol);
+ if (partial->ma && partial->next->ma) {
+ same = equal_on_domain(partial->ma, partial->next->ma,
+ partial->dom);
+ if (same < 0)
+ return isl_stat_error;
+ if (same)
+ return combine_initial_into_second(sol);
+ same = equal_on_domain(partial->ma, partial->next->ma,
+ partial->next->dom);
+ if (same) {
+ swap_initial(sol);
+ return combine_initial_into_second(sol);
+ }
+ }
+
+ sol_pop_one(sol);
+ sol_pop_one(sol);
+
+ return isl_stat_ok;
+}
+
+/* Pop all solutions from the partial solution stack that were pushed onto
+ * the stack at levels that are deeper than the current level.
+ * If the two topmost elements on the stack have the same level
+ * and represent the same solution, then their domains are combined.
+ * This combined domain is the same as the current context domain
+ * as sol_pop is called each time we move back to a higher level.
+ * If the outer level (0) has been reached, then all partial solutions
+ * at the current level are also popped off.
+ */
+static void sol_pop(struct isl_sol *sol)
+{
+ struct isl_partial_sol *partial;
+
+ if (sol->error)
+ return;
+
+ partial = sol->partial;
+ if (!partial)
+ return;
+
+ if (partial->level == 0 && sol->level == 0) {
+ for (partial = sol->partial; partial; partial = sol->partial)
+ sol_pop_one(sol);
+ return;
+ }
+
+ if (partial->level <= sol->level)
+ return;
+
+ if (partial->next && partial->next->level == partial->level) {
+ if (combine_initial_if_equal(sol) < 0)
+ goto error;
+ } else
+ sol_pop_one(sol);
+
+ if (sol->level == 0) {
+ for (partial = sol->partial; partial; partial = sol->partial)
+ sol_pop_one(sol);
+ return;
+ }
+
+ if (0)
+error: sol->error = 1;
+}
+
+static void sol_dec_level(struct isl_sol *sol)
+{
+ if (sol->error)
+ return;
+
+ sol->level--;
+
+ sol_pop(sol);
+}
+
+static isl_stat sol_dec_level_wrap(struct isl_tab_callback *cb)
+{
+ struct isl_sol_callback *callback = (struct isl_sol_callback *)cb;
+
+ sol_dec_level(callback->sol);
+
+ return callback->sol->error ? isl_stat_error : isl_stat_ok;
+}
+
+/* Move down to next level and push callback onto context tableau
+ * to decrease the level again when it gets rolled back across
+ * the current state. That is, dec_level will be called with
+ * the context tableau in the same state as it is when inc_level
+ * is called.
+ */
+static void sol_inc_level(struct isl_sol *sol)
+{
+ struct isl_tab *tab;
+
+ if (sol->error)
+ return;
+
+ sol->level++;
+ tab = sol->context->op->peek_tab(sol->context);
+ if (isl_tab_push_callback(tab, &sol->dec_level.callback) < 0)
+ sol->error = 1;
+}
+
+static void scale_rows(struct isl_mat *mat, isl_int m, int n_row)
+{
+ int i;
+
+ if (isl_int_is_one(m))
+ return;
+
+ for (i = 0; i < n_row; ++i)
+ isl_seq_scale(mat->row[i], mat->row[i], m, mat->n_col);
+}
+
+/* Add the solution identified by the tableau and the context tableau.
+ *
+ * The layout of the variables is as follows.
+ * tab->n_var is equal to the total number of variables in the input
+ * map (including divs that were copied from the context)
+ * + the number of extra divs constructed
+ * Of these, the first tab->n_param and the last tab->n_div variables
+ * correspond to the variables in the context, i.e.,
+ * tab->n_param + tab->n_div = context_tab->n_var
+ * tab->n_param is equal to the number of parameters and input
+ * dimensions in the input map
+ * tab->n_div is equal to the number of divs in the context
+ *
+ * If there is no solution, then call add_empty with a basic set
+ * that corresponds to the context tableau. (If add_empty is NULL,
+ * then do nothing).
+ *
+ * If there is a solution, then first construct a matrix that maps
+ * all dimensions of the context to the output variables, i.e.,
+ * the output dimensions in the input map.
+ * The divs in the input map (if any) that do not correspond to any
+ * div in the context do not appear in the solution.
+ * The algorithm will make sure that they have an integer value,
+ * but these values themselves are of no interest.
+ * We have to be careful not to drop or rearrange any divs in the
+ * context because that would change the meaning of the matrix.
+ *
+ * To extract the value of the output variables, it should be noted
+ * that we always use a big parameter M in the main tableau and so
+ * the variable stored in this tableau is not an output variable x itself, but
+ * x' = M + x (in case of minimization)
+ * or
+ * x' = M - x (in case of maximization)
+ * If x' appears in a column, then its optimal value is zero,
+ * which means that the optimal value of x is an unbounded number
+ * (-M for minimization and M for maximization).
+ * We currently assume that the output dimensions in the original map
+ * are bounded, so this cannot occur.
+ * Similarly, when x' appears in a row, then the coefficient of M in that
+ * row is necessarily 1.
+ * If the row in the tableau represents
+ * d x' = c + d M + e(y)
+ * then, in case of minimization, the corresponding row in the matrix
+ * will be
+ * a c + a e(y)
+ * with a d = m, the (updated) common denominator of the matrix.
+ * In case of maximization, the row will be
+ * -a c - a e(y)
+ */
+static void sol_add(struct isl_sol *sol, struct isl_tab *tab)
+{
+ struct isl_basic_set *bset = NULL;
+ struct isl_mat *mat = NULL;
+ unsigned off;
+ int row;
+ isl_int m;
+
+ if (sol->error || !tab)
+ goto error;
+
+ if (tab->empty && !sol->add_empty)
+ return;
+ if (sol->context->op->is_empty(sol->context))
+ return;
+
+ bset = sol_domain(sol);
+
+ if (tab->empty) {
+ sol_push_sol(sol, bset, NULL);
+ return;
+ }
+
+ off = 2 + tab->M;
+
+ mat = isl_mat_alloc(tab->mat->ctx, 1 + sol->n_out,
+ 1 + tab->n_param + tab->n_div);
+ if (!mat)
+ goto error;
+
+ isl_int_init(m);
+
+ isl_seq_clr(mat->row[0] + 1, mat->n_col - 1);
+ isl_int_set_si(mat->row[0][0], 1);
+ for (row = 0; row < sol->n_out; ++row) {
+ int i = tab->n_param + row;
+ int r, j;
+
+ isl_seq_clr(mat->row[1 + row], mat->n_col);
+ if (!tab->var[i].is_row) {
+ if (tab->M)
+ isl_die(mat->ctx, isl_error_invalid,
+ "unbounded optimum", goto error2);
+ continue;
+ }
+
+ r = tab->var[i].index;
+ if (tab->M &&
+ isl_int_ne(tab->mat->row[r][2], tab->mat->row[r][0]))
+ isl_die(mat->ctx, isl_error_invalid,
+ "unbounded optimum", goto error2);
+ isl_int_gcd(m, mat->row[0][0], tab->mat->row[r][0]);
+ isl_int_divexact(m, tab->mat->row[r][0], m);
+ scale_rows(mat, m, 1 + row);
+ isl_int_divexact(m, mat->row[0][0], tab->mat->row[r][0]);
+ isl_int_mul(mat->row[1 + row][0], m, tab->mat->row[r][1]);
+ for (j = 0; j < tab->n_param; ++j) {
+ int col;
+ if (tab->var[j].is_row)
+ continue;
+ col = tab->var[j].index;
+ isl_int_mul(mat->row[1 + row][1 + j], m,
+ tab->mat->row[r][off + col]);
+ }
+ for (j = 0; j < tab->n_div; ++j) {
+ int col;
+ if (tab->var[tab->n_var - tab->n_div+j].is_row)
+ continue;
+ col = tab->var[tab->n_var - tab->n_div+j].index;
+ isl_int_mul(mat->row[1 + row][1 + tab->n_param + j], m,
+ tab->mat->row[r][off + col]);
+ }
+ if (sol->max)
+ isl_seq_neg(mat->row[1 + row], mat->row[1 + row],
+ mat->n_col);
+ }
+
+ isl_int_clear(m);
+
+ sol_push_sol_mat(sol, bset, mat);
+ return;
+error2:
+ isl_int_clear(m);
+error:
+ isl_basic_set_free(bset);
+ isl_mat_free(mat);
+ sol->error = 1;
+}
+
+struct isl_sol_map {
+ struct isl_sol sol;
+ struct isl_map *map;
+ struct isl_set *empty;
+};
+
+static void sol_map_free(struct isl_sol *sol)
+{
+ struct isl_sol_map *sol_map = (struct isl_sol_map *) sol;
+ isl_map_free(sol_map->map);
+ isl_set_free(sol_map->empty);
+}
+
+/* This function is called for parts of the context where there is
+ * no solution, with "bset" corresponding to the context tableau.
+ * Simply add the basic set to the set "empty".
+ */
+static void sol_map_add_empty(struct isl_sol_map *sol,
+ struct isl_basic_set *bset)
+{
+ if (!bset || !sol->empty)
+ goto error;
+
+ sol->empty = isl_set_grow(sol->empty, 1);
+ bset = isl_basic_set_simplify(bset);
+ bset = isl_basic_set_finalize(bset);
+ sol->empty = isl_set_add_basic_set(sol->empty, isl_basic_set_copy(bset));
+ if (!sol->empty)
+ goto error;
+ isl_basic_set_free(bset);
+ return;
+error:
+ isl_basic_set_free(bset);
+ sol->sol.error = 1;
+}
+
+static void sol_map_add_empty_wrap(struct isl_sol *sol,
+ struct isl_basic_set *bset)
+{
+ sol_map_add_empty((struct isl_sol_map *)sol, bset);
+}
+
+/* Given a basic set "dom" that represents the context and a tuple of
+ * affine expressions "ma" defined over this domain, construct a basic map
+ * that expresses this function on the domain.
+ */
+static void sol_map_add(struct isl_sol_map *sol,
+ __isl_take isl_basic_set *dom, __isl_take isl_multi_aff *ma)
+{
+ isl_basic_map *bmap;
+
+ if (sol->sol.error || !dom || !ma)
+ goto error;
+
+ bmap = isl_basic_map_from_multi_aff2(ma, sol->sol.rational);
+ bmap = isl_basic_map_intersect_domain(bmap, dom);
+ sol->map = isl_map_grow(sol->map, 1);
+ sol->map = isl_map_add_basic_map(sol->map, bmap);
+ if (!sol->map)
+ sol->sol.error = 1;
+ return;
+error:
+ isl_basic_set_free(dom);
+ isl_multi_aff_free(ma);
+ sol->sol.error = 1;
+}
+
+static void sol_map_add_wrap(struct isl_sol *sol,
+ __isl_take isl_basic_set *dom, __isl_take isl_multi_aff *ma)
+{
+ sol_map_add((struct isl_sol_map *)sol, dom, ma);
+}
+
+
+/* Store the "parametric constant" of row "row" of tableau "tab" in "line",
+ * i.e., the constant term and the coefficients of all variables that
+ * appear in the context tableau.
+ * Note that the coefficient of the big parameter M is NOT copied.
+ * The context tableau may not have a big parameter and even when it
+ * does, it is a different big parameter.
+ */
+static void get_row_parameter_line(struct isl_tab *tab, int row, isl_int *line)
+{
+ int i;
+ unsigned off = 2 + tab->M;
+
+ isl_int_set(line[0], tab->mat->row[row][1]);
+ for (i = 0; i < tab->n_param; ++i) {
+ if (tab->var[i].is_row)
+ isl_int_set_si(line[1 + i], 0);
+ else {
+ int col = tab->var[i].index;
+ isl_int_set(line[1 + i], tab->mat->row[row][off + col]);
+ }
+ }
+ for (i = 0; i < tab->n_div; ++i) {
+ if (tab->var[tab->n_var - tab->n_div + i].is_row)
+ isl_int_set_si(line[1 + tab->n_param + i], 0);
+ else {
+ int col = tab->var[tab->n_var - tab->n_div + i].index;
+ isl_int_set(line[1 + tab->n_param + i],
+ tab->mat->row[row][off + col]);
+ }
+ }
+}
+
+/* Check if rows "row1" and "row2" have identical "parametric constants",
+ * as explained above.
+ * In this case, we also insist that the coefficients of the big parameter
+ * be the same as the values of the constants will only be the same
+ * if these coefficients are also the same.
+ */
+static int identical_parameter_line(struct isl_tab *tab, int row1, int row2)
+{
+ int i;
+ unsigned off = 2 + tab->M;
+
+ if (isl_int_ne(tab->mat->row[row1][1], tab->mat->row[row2][1]))
+ return 0;
+
+ if (tab->M && isl_int_ne(tab->mat->row[row1][2],
+ tab->mat->row[row2][2]))
+ return 0;
+
+ for (i = 0; i < tab->n_param + tab->n_div; ++i) {
+ int pos = i < tab->n_param ? i :
+ tab->n_var - tab->n_div + i - tab->n_param;
+ int col;
+
+ if (tab->var[pos].is_row)
+ continue;
+ col = tab->var[pos].index;
+ if (isl_int_ne(tab->mat->row[row1][off + col],
+ tab->mat->row[row2][off + col]))
+ return 0;
+ }
+ return 1;
+}
+
+/* Return an inequality that expresses that the "parametric constant"
+ * should be non-negative.
+ * This function is only called when the coefficient of the big parameter
+ * is equal to zero.
+ */
+static struct isl_vec *get_row_parameter_ineq(struct isl_tab *tab, int row)
+{
+ struct isl_vec *ineq;
+
+ ineq = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_param + tab->n_div);
+ if (!ineq)
+ return NULL;
+
+ get_row_parameter_line(tab, row, ineq->el);
+ if (ineq)
+ ineq = isl_vec_normalize(ineq);
+
+ return ineq;
+}
+
+/* Normalize a div expression of the form
+ *
+ * [(g*f(x) + c)/(g * m)]
+ *
+ * with c the constant term and f(x) the remaining coefficients, to
+ *
+ * [(f(x) + [c/g])/m]
+ */
+static void normalize_div(__isl_keep isl_vec *div)
+{
+ isl_ctx *ctx = isl_vec_get_ctx(div);
+ int len = div->size - 2;
+
+ isl_seq_gcd(div->el + 2, len, &ctx->normalize_gcd);
+ isl_int_gcd(ctx->normalize_gcd, ctx->normalize_gcd, div->el[0]);
+
+ if (isl_int_is_one(ctx->normalize_gcd))
+ return;
+
+ isl_int_divexact(div->el[0], div->el[0], ctx->normalize_gcd);
+ isl_int_fdiv_q(div->el[1], div->el[1], ctx->normalize_gcd);
+ isl_seq_scale_down(div->el + 2, div->el + 2, ctx->normalize_gcd, len);
+}
+
+/* Return an integer division for use in a parametric cut based
+ * on the given row.
+ * In particular, let the parametric constant of the row be
+ *
+ * \sum_i a_i y_i
+ *
+ * where y_0 = 1, but none of the y_i corresponds to the big parameter M.
+ * The div returned is equal to
+ *
+ * floor(\sum_i {-a_i} y_i) = floor((\sum_i (-a_i mod d) y_i)/d)
+ */
+static struct isl_vec *get_row_parameter_div(struct isl_tab *tab, int row)
+{
+ struct isl_vec *div;
+
+ div = isl_vec_alloc(tab->mat->ctx, 1 + 1 + tab->n_param + tab->n_div);
+ if (!div)
+ return NULL;
+
+ isl_int_set(div->el[0], tab->mat->row[row][0]);
+ get_row_parameter_line(tab, row, div->el + 1);
+ isl_seq_neg(div->el + 1, div->el + 1, div->size - 1);
+ normalize_div(div);
+ isl_seq_fdiv_r(div->el + 1, div->el + 1, div->el[0], div->size - 1);
+
+ return div;
+}
+
+/* Return an integer division for use in transferring an integrality constraint
+ * to the context.
+ * In particular, let the parametric constant of the row be
+ *
+ * \sum_i a_i y_i
+ *
+ * where y_0 = 1, but none of the y_i corresponds to the big parameter M.
+ * The the returned div is equal to
+ *
+ * floor(\sum_i {a_i} y_i) = floor((\sum_i (a_i mod d) y_i)/d)
+ */
+static struct isl_vec *get_row_split_div(struct isl_tab *tab, int row)
+{
+ struct isl_vec *div;
+
+ div = isl_vec_alloc(tab->mat->ctx, 1 + 1 + tab->n_param + tab->n_div);
+ if (!div)
+ return NULL;
+
+ isl_int_set(div->el[0], tab->mat->row[row][0]);
+ get_row_parameter_line(tab, row, div->el + 1);
+ normalize_div(div);
+ isl_seq_fdiv_r(div->el + 1, div->el + 1, div->el[0], div->size - 1);
+
+ return div;
+}
+
+/* Construct and return an inequality that expresses an upper bound
+ * on the given div.
+ * In particular, if the div is given by
+ *
+ * d = floor(e/m)
+ *
+ * then the inequality expresses
+ *
+ * m d <= e
+ */
+static __isl_give isl_vec *ineq_for_div(__isl_keep isl_basic_set *bset,
+ unsigned div)
+{
+ isl_size total;
+ unsigned div_pos;
+ struct isl_vec *ineq;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ return NULL;
+
+ div_pos = 1 + total - bset->n_div + div;
+
+ ineq = isl_vec_alloc(bset->ctx, 1 + total);
+ if (!ineq)
+ return NULL;
+
+ isl_seq_cpy(ineq->el, bset->div[div] + 1, 1 + total);
+ isl_int_neg(ineq->el[div_pos], bset->div[div][0]);
+ return ineq;
+}
+
+/* Given a row in the tableau and a div that was created
+ * using get_row_split_div and that has been constrained to equality, i.e.,
+ *
+ * d = floor(\sum_i {a_i} y_i) = \sum_i {a_i} y_i
+ *
+ * replace the expression "\sum_i {a_i} y_i" in the row by d,
+ * i.e., we subtract "\sum_i {a_i} y_i" and add 1 d.
+ * The coefficients of the non-parameters in the tableau have been
+ * verified to be integral. We can therefore simply replace coefficient b
+ * by floor(b). For the coefficients of the parameters we have
+ * floor(a_i) = a_i - {a_i}, while for the other coefficients, we have
+ * floor(b) = b.
+ */
+static struct isl_tab *set_row_cst_to_div(struct isl_tab *tab, int row, int div)
+{
+ isl_seq_fdiv_q(tab->mat->row[row] + 1, tab->mat->row[row] + 1,
+ tab->mat->row[row][0], 1 + tab->M + tab->n_col);
+
+ isl_int_set_si(tab->mat->row[row][0], 1);
+
+ if (tab->var[tab->n_var - tab->n_div + div].is_row) {
+ int drow = tab->var[tab->n_var - tab->n_div + div].index;
+
+ isl_assert(tab->mat->ctx,
+ isl_int_is_one(tab->mat->row[drow][0]), goto error);
+ isl_seq_combine(tab->mat->row[row] + 1,
+ tab->mat->ctx->one, tab->mat->row[row] + 1,
+ tab->mat->ctx->one, tab->mat->row[drow] + 1,
+ 1 + tab->M + tab->n_col);
+ } else {
+ int dcol = tab->var[tab->n_var - tab->n_div + div].index;
+
+ isl_int_add_ui(tab->mat->row[row][2 + tab->M + dcol],
+ tab->mat->row[row][2 + tab->M + dcol], 1);
+ }
+
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Check if the (parametric) constant of the given row is obviously
+ * negative, meaning that we don't need to consult the context tableau.
+ * If there is a big parameter and its coefficient is non-zero,
+ * then this coefficient determines the outcome.
+ * Otherwise, we check whether the constant is negative and
+ * all non-zero coefficients of parameters are negative and
+ * belong to non-negative parameters.
+ */
+static int is_obviously_neg(struct isl_tab *tab, int row)
+{
+ int i;
+ int col;
+ unsigned off = 2 + tab->M;
+
+ if (tab->M) {
+ if (isl_int_is_pos(tab->mat->row[row][2]))
+ return 0;
+ if (isl_int_is_neg(tab->mat->row[row][2]))
+ return 1;
+ }
+
+ if (isl_int_is_nonneg(tab->mat->row[row][1]))
+ return 0;
+ for (i = 0; i < tab->n_param; ++i) {
+ /* Eliminated parameter */
+ if (tab->var[i].is_row)
+ continue;
+ col = tab->var[i].index;
+ if (isl_int_is_zero(tab->mat->row[row][off + col]))
+ continue;
+ if (!tab->var[i].is_nonneg)
+ return 0;
+ if (isl_int_is_pos(tab->mat->row[row][off + col]))
+ return 0;
+ }
+ for (i = 0; i < tab->n_div; ++i) {
+ if (tab->var[tab->n_var - tab->n_div + i].is_row)
+ continue;
+ col = tab->var[tab->n_var - tab->n_div + i].index;
+ if (isl_int_is_zero(tab->mat->row[row][off + col]))
+ continue;
+ if (!tab->var[tab->n_var - tab->n_div + i].is_nonneg)
+ return 0;
+ if (isl_int_is_pos(tab->mat->row[row][off + col]))
+ return 0;
+ }
+ return 1;
+}
+
+/* Check if the (parametric) constant of the given row is obviously
+ * non-negative, meaning that we don't need to consult the context tableau.
+ * If there is a big parameter and its coefficient is non-zero,
+ * then this coefficient determines the outcome.
+ * Otherwise, we check whether the constant is non-negative and
+ * all non-zero coefficients of parameters are positive and
+ * belong to non-negative parameters.
+ */
+static int is_obviously_nonneg(struct isl_tab *tab, int row)
+{
+ int i;
+ int col;
+ unsigned off = 2 + tab->M;
+
+ if (tab->M) {
+ if (isl_int_is_pos(tab->mat->row[row][2]))
+ return 1;
+ if (isl_int_is_neg(tab->mat->row[row][2]))
+ return 0;
+ }
+
+ if (isl_int_is_neg(tab->mat->row[row][1]))
+ return 0;
+ for (i = 0; i < tab->n_param; ++i) {
+ /* Eliminated parameter */
+ if (tab->var[i].is_row)
+ continue;
+ col = tab->var[i].index;
+ if (isl_int_is_zero(tab->mat->row[row][off + col]))
+ continue;
+ if (!tab->var[i].is_nonneg)
+ return 0;
+ if (isl_int_is_neg(tab->mat->row[row][off + col]))
+ return 0;
+ }
+ for (i = 0; i < tab->n_div; ++i) {
+ if (tab->var[tab->n_var - tab->n_div + i].is_row)
+ continue;
+ col = tab->var[tab->n_var - tab->n_div + i].index;
+ if (isl_int_is_zero(tab->mat->row[row][off + col]))
+ continue;
+ if (!tab->var[tab->n_var - tab->n_div + i].is_nonneg)
+ return 0;
+ if (isl_int_is_neg(tab->mat->row[row][off + col]))
+ return 0;
+ }
+ return 1;
+}
+
+/* Given a row r and two columns, return the column that would
+ * lead to the lexicographically smallest increment in the sample
+ * solution when leaving the basis in favor of the row.
+ * Pivoting with column c will increment the sample value by a non-negative
+ * constant times a_{V,c}/a_{r,c}, with a_{V,c} the elements of column c
+ * corresponding to the non-parametric variables.
+ * If variable v appears in a column c_v, then a_{v,c} = 1 iff c = c_v,
+ * with all other entries in this virtual row equal to zero.
+ * If variable v appears in a row, then a_{v,c} is the element in column c
+ * of that row.
+ *
+ * Let v be the first variable with a_{v,c1}/a_{r,c1} != a_{v,c2}/a_{r,c2}.
+ * Then if a_{v,c1}/a_{r,c1} < a_{v,c2}/a_{r,c2}, i.e.,
+ * a_{v,c2} a_{r,c1} - a_{v,c1} a_{r,c2} > 0, c1 results in the minimal
+ * increment. Otherwise, it's c2.
+ */
+static int lexmin_col_pair(struct isl_tab *tab,
+ int row, int col1, int col2, isl_int tmp)
+{
+ int i;
+ isl_int *tr;
+
+ tr = tab->mat->row[row] + 2 + tab->M;
+
+ for (i = tab->n_param; i < tab->n_var - tab->n_div; ++i) {
+ int s1, s2;
+ isl_int *r;
+
+ if (!tab->var[i].is_row) {
+ if (tab->var[i].index == col1)
+ return col2;
+ if (tab->var[i].index == col2)
+ return col1;
+ continue;
+ }
+
+ if (tab->var[i].index == row)
+ continue;
+
+ r = tab->mat->row[tab->var[i].index] + 2 + tab->M;
+ s1 = isl_int_sgn(r[col1]);
+ s2 = isl_int_sgn(r[col2]);
+ if (s1 == 0 && s2 == 0)
+ continue;
+ if (s1 < s2)
+ return col1;
+ if (s2 < s1)
+ return col2;
+
+ isl_int_mul(tmp, r[col2], tr[col1]);
+ isl_int_submul(tmp, r[col1], tr[col2]);
+ if (isl_int_is_pos(tmp))
+ return col1;
+ if (isl_int_is_neg(tmp))
+ return col2;
+ }
+ return -1;
+}
+
+/* Does the index into the tab->var or tab->con array "index"
+ * correspond to a variable in the context tableau?
+ * In particular, it needs to be an index into the tab->var array and
+ * it needs to refer to either one of the first tab->n_param variables or
+ * one of the last tab->n_div variables.
+ */
+static int is_parameter_var(struct isl_tab *tab, int index)
+{
+ if (index < 0)
+ return 0;
+ if (index < tab->n_param)
+ return 1;
+ if (index >= tab->n_var - tab->n_div)
+ return 1;
+ return 0;
+}
+
+/* Does column "col" of "tab" refer to a variable in the context tableau?
+ */
+static int col_is_parameter_var(struct isl_tab *tab, int col)
+{
+ return is_parameter_var(tab, tab->col_var[col]);
+}
+
+/* Does row "row" of "tab" refer to a variable in the context tableau?
+ */
+static int row_is_parameter_var(struct isl_tab *tab, int row)
+{
+ return is_parameter_var(tab, tab->row_var[row]);
+}
+
+/* Given a row in the tableau, find and return the column that would
+ * result in the lexicographically smallest, but positive, increment
+ * in the sample point.
+ * If there is no such column, then return tab->n_col.
+ * If anything goes wrong, return -1.
+ */
+static int lexmin_pivot_col(struct isl_tab *tab, int row)
+{
+ int j;
+ int col = tab->n_col;
+ isl_int *tr;
+ isl_int tmp;
+
+ tr = tab->mat->row[row] + 2 + tab->M;
+
+ isl_int_init(tmp);
+
+ for (j = tab->n_dead; j < tab->n_col; ++j) {
+ if (col_is_parameter_var(tab, j))
+ continue;
+
+ if (!isl_int_is_pos(tr[j]))
+ continue;
+
+ if (col == tab->n_col)
+ col = j;
+ else
+ col = lexmin_col_pair(tab, row, col, j, tmp);
+ isl_assert(tab->mat->ctx, col >= 0, goto error);
+ }
+
+ isl_int_clear(tmp);
+ return col;
+error:
+ isl_int_clear(tmp);
+ return -1;
+}
+
+/* Return the first known violated constraint, i.e., a non-negative
+ * constraint that currently has an either obviously negative value
+ * or a previously determined to be negative value.
+ *
+ * If any constraint has a negative coefficient for the big parameter,
+ * if any, then we return one of these first.
+ */
+static int first_neg(struct isl_tab *tab)
+{
+ int row;
+
+ if (tab->M)
+ for (row = tab->n_redundant; row < tab->n_row; ++row) {
+ if (!isl_tab_var_from_row(tab, row)->is_nonneg)
+ continue;
+ if (!isl_int_is_neg(tab->mat->row[row][2]))
+ continue;
+ if (tab->row_sign)
+ tab->row_sign[row] = isl_tab_row_neg;
+ return row;
+ }
+ for (row = tab->n_redundant; row < tab->n_row; ++row) {
+ if (!isl_tab_var_from_row(tab, row)->is_nonneg)
+ continue;
+ if (tab->row_sign) {
+ if (tab->row_sign[row] == 0 &&
+ is_obviously_neg(tab, row))
+ tab->row_sign[row] = isl_tab_row_neg;
+ if (tab->row_sign[row] != isl_tab_row_neg)
+ continue;
+ } else if (!is_obviously_neg(tab, row))
+ continue;
+ return row;
+ }
+ return -1;
+}
+
+/* Check whether the invariant that all columns are lexico-positive
+ * is satisfied. This function is not called from the current code
+ * but is useful during debugging.
+ */
+static void check_lexpos(struct isl_tab *tab) __attribute__ ((unused));
+static void check_lexpos(struct isl_tab *tab)
+{
+ unsigned off = 2 + tab->M;
+ int col;
+ int var;
+ int row;
+
+ for (col = tab->n_dead; col < tab->n_col; ++col) {
+ if (col_is_parameter_var(tab, col))
+ continue;
+ for (var = tab->n_param; var < tab->n_var - tab->n_div; ++var) {
+ if (!tab->var[var].is_row) {
+ if (tab->var[var].index == col)
+ break;
+ else
+ continue;
+ }
+ row = tab->var[var].index;
+ if (isl_int_is_zero(tab->mat->row[row][off + col]))
+ continue;
+ if (isl_int_is_pos(tab->mat->row[row][off + col]))
+ break;
+ fprintf(stderr, "lexneg column %d (row %d)\n",
+ col, row);
+ }
+ if (var >= tab->n_var - tab->n_div)
+ fprintf(stderr, "zero column %d\n", col);
+ }
+}
+
+/* Report to the caller that the given constraint is part of an encountered
+ * conflict.
+ */
+static int report_conflicting_constraint(struct isl_tab *tab, int con)
+{
+ return tab->conflict(con, tab->conflict_user);
+}
+
+/* Given a conflicting row in the tableau, report all constraints
+ * involved in the row to the caller. That is, the row itself
+ * (if it represents a constraint) and all constraint columns with
+ * non-zero (and therefore negative) coefficients.
+ */
+static int report_conflict(struct isl_tab *tab, int row)
+{
+ int j;
+ isl_int *tr;
+
+ if (!tab->conflict)
+ return 0;
+
+ if (tab->row_var[row] < 0 &&
+ report_conflicting_constraint(tab, ~tab->row_var[row]) < 0)
+ return -1;
+
+ tr = tab->mat->row[row] + 2 + tab->M;
+
+ for (j = tab->n_dead; j < tab->n_col; ++j) {
+ if (col_is_parameter_var(tab, j))
+ continue;
+
+ if (!isl_int_is_neg(tr[j]))
+ continue;
+
+ if (tab->col_var[j] < 0 &&
+ report_conflicting_constraint(tab, ~tab->col_var[j]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Resolve all known or obviously violated constraints through pivoting.
+ * In particular, as long as we can find any violated constraint, we
+ * look for a pivoting column that would result in the lexicographically
+ * smallest increment in the sample point. If there is no such column
+ * then the tableau is infeasible.
+ */
+static int restore_lexmin(struct isl_tab *tab) WARN_UNUSED;
+static int restore_lexmin(struct isl_tab *tab)
+{
+ int row, col;
+
+ if (!tab)
+ return -1;
+ if (tab->empty)
+ return 0;
+ while ((row = first_neg(tab)) != -1) {
+ col = lexmin_pivot_col(tab, row);
+ if (col >= tab->n_col) {
+ if (report_conflict(tab, row) < 0)
+ return -1;
+ if (isl_tab_mark_empty(tab) < 0)
+ return -1;
+ return 0;
+ }
+ if (col < 0)
+ return -1;
+ if (isl_tab_pivot(tab, row, col) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/* Given a row that represents an equality, look for an appropriate
+ * pivoting column.
+ * In particular, if there are any non-zero coefficients among
+ * the non-parameter variables, then we take the last of these
+ * variables. Eliminating this variable in terms of the other
+ * variables and/or parameters does not influence the property
+ * that all column in the initial tableau are lexicographically
+ * positive. The row corresponding to the eliminated variable
+ * will only have non-zero entries below the diagonal of the
+ * initial tableau. That is, we transform
+ *
+ * I I
+ * 1 into a
+ * I I
+ *
+ * If there is no such non-parameter variable, then we are dealing with
+ * pure parameter equality and we pick any parameter with coefficient 1 or -1
+ * for elimination. This will ensure that the eliminated parameter
+ * always has an integer value whenever all the other parameters are integral.
+ * If there is no such parameter then we return -1.
+ */
+static int last_var_col_or_int_par_col(struct isl_tab *tab, int row)
+{
+ unsigned off = 2 + tab->M;
+ int i;
+
+ for (i = tab->n_var - tab->n_div - 1; i >= 0 && i >= tab->n_param; --i) {
+ int col;
+ if (tab->var[i].is_row)
+ continue;
+ col = tab->var[i].index;
+ if (col <= tab->n_dead)
+ continue;
+ if (!isl_int_is_zero(tab->mat->row[row][off + col]))
+ return col;
+ }
+ for (i = tab->n_dead; i < tab->n_col; ++i) {
+ if (isl_int_is_one(tab->mat->row[row][off + i]))
+ return i;
+ if (isl_int_is_negone(tab->mat->row[row][off + i]))
+ return i;
+ }
+ return -1;
+}
+
+/* Add an equality that is known to be valid to the tableau.
+ * We first check if we can eliminate a variable or a parameter.
+ * If not, we add the equality as two inequalities.
+ * In this case, the equality was a pure parameter equality and there
+ * is no need to resolve any constraint violations.
+ *
+ * This function assumes that at least two more rows and at least
+ * two more elements in the constraint array are available in the tableau.
+ */
+static struct isl_tab *add_lexmin_valid_eq(struct isl_tab *tab, isl_int *eq)
+{
+ int i;
+ int r;
+
+ if (!tab)
+ return NULL;
+ r = isl_tab_add_row(tab, eq);
+ if (r < 0)
+ goto error;
+
+ r = tab->con[r].index;
+ i = last_var_col_or_int_par_col(tab, r);
+ if (i < 0) {
+ tab->con[r].is_nonneg = 1;
+ if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0)
+ goto error;
+ isl_seq_neg(eq, eq, 1 + tab->n_var);
+ r = isl_tab_add_row(tab, eq);
+ if (r < 0)
+ goto error;
+ tab->con[r].is_nonneg = 1;
+ if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0)
+ goto error;
+ } else {
+ if (isl_tab_pivot(tab, r, i) < 0)
+ goto error;
+ if (isl_tab_kill_col(tab, i) < 0)
+ goto error;
+ tab->n_eq++;
+ }
+
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Check if the given row is a pure constant.
+ */
+static int is_constant(struct isl_tab *tab, int row)
+{
+ unsigned off = 2 + tab->M;
+
+ return isl_seq_first_non_zero(tab->mat->row[row] + off + tab->n_dead,
+ tab->n_col - tab->n_dead) == -1;
+}
+
+/* Is the given row a parametric constant?
+ * That is, does it only involve variables that also appear in the context?
+ */
+static int is_parametric_constant(struct isl_tab *tab, int row)
+{
+ unsigned off = 2 + tab->M;
+ int col;
+
+ for (col = tab->n_dead; col < tab->n_col; ++col) {
+ if (col_is_parameter_var(tab, col))
+ continue;
+ if (isl_int_is_zero(tab->mat->row[row][off + col]))
+ continue;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Add an equality that may or may not be valid to the tableau.
+ * If the resulting row is a pure constant, then it must be zero.
+ * Otherwise, the resulting tableau is empty.
+ *
+ * If the row is not a pure constant, then we add two inequalities,
+ * each time checking that they can be satisfied.
+ * In the end we try to use one of the two constraints to eliminate
+ * a column.
+ *
+ * This function assumes that at least two more rows and at least
+ * two more elements in the constraint array are available in the tableau.
+ */
+static int add_lexmin_eq(struct isl_tab *tab, isl_int *eq) WARN_UNUSED;
+static int add_lexmin_eq(struct isl_tab *tab, isl_int *eq)
+{
+ int r1, r2;
+ int row;
+ struct isl_tab_undo *snap;
+
+ if (!tab)
+ return -1;
+ snap = isl_tab_snap(tab);
+ r1 = isl_tab_add_row(tab, eq);
+ if (r1 < 0)
+ return -1;
+ tab->con[r1].is_nonneg = 1;
+ if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r1]) < 0)
+ return -1;
+
+ row = tab->con[r1].index;
+ if (is_constant(tab, row)) {
+ if (!isl_int_is_zero(tab->mat->row[row][1]) ||
+ (tab->M && !isl_int_is_zero(tab->mat->row[row][2]))) {
+ if (isl_tab_mark_empty(tab) < 0)
+ return -1;
+ return 0;
+ }
+ if (isl_tab_rollback(tab, snap) < 0)
+ return -1;
+ return 0;
+ }
+
+ if (restore_lexmin(tab) < 0)
+ return -1;
+ if (tab->empty)
+ return 0;
+
+ isl_seq_neg(eq, eq, 1 + tab->n_var);
+
+ r2 = isl_tab_add_row(tab, eq);
+ if (r2 < 0)
+ return -1;
+ tab->con[r2].is_nonneg = 1;
+ if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r2]) < 0)
+ return -1;
+
+ if (restore_lexmin(tab) < 0)
+ return -1;
+ if (tab->empty)
+ return 0;
+
+ if (!tab->con[r1].is_row) {
+ if (isl_tab_kill_col(tab, tab->con[r1].index) < 0)
+ return -1;
+ } else if (!tab->con[r2].is_row) {
+ if (isl_tab_kill_col(tab, tab->con[r2].index) < 0)
+ return -1;
+ }
+
+ if (tab->bmap) {
+ tab->bmap = isl_basic_map_add_ineq(tab->bmap, eq);
+ if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0)
+ return -1;
+ isl_seq_neg(eq, eq, 1 + tab->n_var);
+ tab->bmap = isl_basic_map_add_ineq(tab->bmap, eq);
+ isl_seq_neg(eq, eq, 1 + tab->n_var);
+ if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0)
+ return -1;
+ if (!tab->bmap)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Add an inequality to the tableau, resolving violations using
+ * restore_lexmin.
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ */
+static struct isl_tab *add_lexmin_ineq(struct isl_tab *tab, isl_int *ineq)
+{
+ int r;
+
+ if (!tab)
+ return NULL;
+ if (tab->bmap) {
+ tab->bmap = isl_basic_map_add_ineq(tab->bmap, ineq);
+ if (isl_tab_push(tab, isl_tab_undo_bmap_ineq) < 0)
+ goto error;
+ if (!tab->bmap)
+ goto error;
+ }
+ r = isl_tab_add_row(tab, ineq);
+ if (r < 0)
+ goto error;
+ tab->con[r].is_nonneg = 1;
+ if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0)
+ goto error;
+ if (isl_tab_row_is_redundant(tab, tab->con[r].index)) {
+ if (isl_tab_mark_redundant(tab, tab->con[r].index) < 0)
+ goto error;
+ return tab;
+ }
+
+ if (restore_lexmin(tab) < 0)
+ goto error;
+ if (!tab->empty && tab->con[r].is_row &&
+ isl_tab_row_is_redundant(tab, tab->con[r].index))
+ if (isl_tab_mark_redundant(tab, tab->con[r].index) < 0)
+ goto error;
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Check if the coefficients of the parameters are all integral.
+ */
+static int integer_parameter(struct isl_tab *tab, int row)
+{
+ int i;
+ int col;
+ unsigned off = 2 + tab->M;
+
+ for (i = 0; i < tab->n_param; ++i) {
+ /* Eliminated parameter */
+ if (tab->var[i].is_row)
+ continue;
+ col = tab->var[i].index;
+ if (!isl_int_is_divisible_by(tab->mat->row[row][off + col],
+ tab->mat->row[row][0]))
+ return 0;
+ }
+ for (i = 0; i < tab->n_div; ++i) {
+ if (tab->var[tab->n_var - tab->n_div + i].is_row)
+ continue;
+ col = tab->var[tab->n_var - tab->n_div + i].index;
+ if (!isl_int_is_divisible_by(tab->mat->row[row][off + col],
+ tab->mat->row[row][0]))
+ return 0;
+ }
+ return 1;
+}
+
+/* Check if the coefficients of the non-parameter variables are all integral.
+ */
+static int integer_variable(struct isl_tab *tab, int row)
+{
+ int i;
+ unsigned off = 2 + tab->M;
+
+ for (i = tab->n_dead; i < tab->n_col; ++i) {
+ if (col_is_parameter_var(tab, i))
+ continue;
+ if (!isl_int_is_divisible_by(tab->mat->row[row][off + i],
+ tab->mat->row[row][0]))
+ return 0;
+ }
+ return 1;
+}
+
+/* Check if the constant term is integral.
+ */
+static int integer_constant(struct isl_tab *tab, int row)
+{
+ return isl_int_is_divisible_by(tab->mat->row[row][1],
+ tab->mat->row[row][0]);
+}
+
+#define I_CST 1 << 0
+#define I_PAR 1 << 1
+#define I_VAR 1 << 2
+
+/* Check for next (non-parameter) variable after "var" (first if var == -1)
+ * that is non-integer and therefore requires a cut and return
+ * the index of the variable.
+ * For parametric tableaus, there are three parts in a row,
+ * the constant, the coefficients of the parameters and the rest.
+ * For each part, we check whether the coefficients in that part
+ * are all integral and if so, set the corresponding flag in *f.
+ * If the constant and the parameter part are integral, then the
+ * current sample value is integral and no cut is required
+ * (irrespective of whether the variable part is integral).
+ */
+static int next_non_integer_var(struct isl_tab *tab, int var, int *f)
+{
+ var = var < 0 ? tab->n_param : var + 1;
+
+ for (; var < tab->n_var - tab->n_div; ++var) {
+ int flags = 0;
+ int row;
+ if (!tab->var[var].is_row)
+ continue;
+ row = tab->var[var].index;
+ if (integer_constant(tab, row))
+ ISL_FL_SET(flags, I_CST);
+ if (integer_parameter(tab, row))
+ ISL_FL_SET(flags, I_PAR);
+ if (ISL_FL_ISSET(flags, I_CST) && ISL_FL_ISSET(flags, I_PAR))
+ continue;
+ if (integer_variable(tab, row))
+ ISL_FL_SET(flags, I_VAR);
+ *f = flags;
+ return var;
+ }
+ return -1;
+}
+
+/* Check for first (non-parameter) variable that is non-integer and
+ * therefore requires a cut and return the corresponding row.
+ * For parametric tableaus, there are three parts in a row,
+ * the constant, the coefficients of the parameters and the rest.
+ * For each part, we check whether the coefficients in that part
+ * are all integral and if so, set the corresponding flag in *f.
+ * If the constant and the parameter part are integral, then the
+ * current sample value is integral and no cut is required
+ * (irrespective of whether the variable part is integral).
+ */
+static int first_non_integer_row(struct isl_tab *tab, int *f)
+{
+ int var = next_non_integer_var(tab, -1, f);
+
+ return var < 0 ? -1 : tab->var[var].index;
+}
+
+/* Add a (non-parametric) cut to cut away the non-integral sample
+ * value of the given row.
+ *
+ * If the row is given by
+ *
+ * m r = f + \sum_i a_i y_i
+ *
+ * then the cut is
+ *
+ * c = - {-f/m} + \sum_i {a_i/m} y_i >= 0
+ *
+ * The big parameter, if any, is ignored, since it is assumed to be big
+ * enough to be divisible by any integer.
+ * If the tableau is actually a parametric tableau, then this function
+ * is only called when all coefficients of the parameters are integral.
+ * The cut therefore has zero coefficients for the parameters.
+ *
+ * The current value is known to be negative, so row_sign, if it
+ * exists, is set accordingly.
+ *
+ * Return the row of the cut or -1.
+ */
+static int add_cut(struct isl_tab *tab, int row)
+{
+ int i;
+ int r;
+ isl_int *r_row;
+ unsigned off = 2 + tab->M;
+
+ if (isl_tab_extend_cons(tab, 1) < 0)
+ return -1;
+ r = isl_tab_allocate_con(tab);
+ if (r < 0)
+ return -1;
+
+ r_row = tab->mat->row[tab->con[r].index];
+ isl_int_set(r_row[0], tab->mat->row[row][0]);
+ isl_int_neg(r_row[1], tab->mat->row[row][1]);
+ isl_int_fdiv_r(r_row[1], r_row[1], tab->mat->row[row][0]);
+ isl_int_neg(r_row[1], r_row[1]);
+ if (tab->M)
+ isl_int_set_si(r_row[2], 0);
+ for (i = 0; i < tab->n_col; ++i)
+ isl_int_fdiv_r(r_row[off + i],
+ tab->mat->row[row][off + i], tab->mat->row[row][0]);
+
+ tab->con[r].is_nonneg = 1;
+ if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0)
+ return -1;
+ if (tab->row_sign)
+ tab->row_sign[tab->con[r].index] = isl_tab_row_neg;
+
+ return tab->con[r].index;
+}
+
+#define CUT_ALL 1
+#define CUT_ONE 0
+
+/* Given a non-parametric tableau, add cuts until an integer
+ * sample point is obtained or until the tableau is determined
+ * to be integer infeasible.
+ * As long as there is any non-integer value in the sample point,
+ * we add appropriate cuts, if possible, for each of these
+ * non-integer values and then resolve the violated
+ * cut constraints using restore_lexmin.
+ * If one of the corresponding rows is equal to an integral
+ * combination of variables/constraints plus a non-integral constant,
+ * then there is no way to obtain an integer point and we return
+ * a tableau that is marked empty.
+ * The parameter cutting_strategy controls the strategy used when adding cuts
+ * to remove non-integer points. CUT_ALL adds all possible cuts
+ * before continuing the search. CUT_ONE adds only one cut at a time.
+ */
+static struct isl_tab *cut_to_integer_lexmin(struct isl_tab *tab,
+ int cutting_strategy)
+{
+ int var;
+ int row;
+ int flags;
+
+ if (!tab)
+ return NULL;
+ if (tab->empty)
+ return tab;
+
+ while ((var = next_non_integer_var(tab, -1, &flags)) != -1) {
+ do {
+ if (ISL_FL_ISSET(flags, I_VAR)) {
+ if (isl_tab_mark_empty(tab) < 0)
+ goto error;
+ return tab;
+ }
+ row = tab->var[var].index;
+ row = add_cut(tab, row);
+ if (row < 0)
+ goto error;
+ if (cutting_strategy == CUT_ONE)
+ break;
+ } while ((var = next_non_integer_var(tab, var, &flags)) != -1);
+ if (restore_lexmin(tab) < 0)
+ goto error;
+ if (tab->empty)
+ break;
+ }
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Check whether all the currently active samples also satisfy the inequality
+ * "ineq" (treated as an equality if eq is set).
+ * Remove those samples that do not.
+ */
+static struct isl_tab *check_samples(struct isl_tab *tab, isl_int *ineq, int eq)
+{
+ int i;
+ isl_int v;
+
+ if (!tab)
+ return NULL;
+
+ isl_assert(tab->mat->ctx, tab->bmap, goto error);
+ isl_assert(tab->mat->ctx, tab->samples, goto error);
+ isl_assert(tab->mat->ctx, tab->samples->n_col == 1 + tab->n_var, goto error);
+
+ isl_int_init(v);
+ for (i = tab->n_outside; i < tab->n_sample; ++i) {
+ int sgn;
+ isl_seq_inner_product(ineq, tab->samples->row[i],
+ 1 + tab->n_var, &v);
+ sgn = isl_int_sgn(v);
+ if (eq ? (sgn == 0) : (sgn >= 0))
+ continue;
+ tab = isl_tab_drop_sample(tab, i);
+ if (!tab)
+ break;
+ }
+ isl_int_clear(v);
+
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Check whether the sample value of the tableau is finite,
+ * i.e., either the tableau does not use a big parameter, or
+ * all values of the variables are equal to the big parameter plus
+ * some constant. This constant is the actual sample value.
+ */
+static int sample_is_finite(struct isl_tab *tab)
+{
+ int i;
+
+ if (!tab->M)
+ return 1;
+
+ for (i = 0; i < tab->n_var; ++i) {
+ int row;
+ if (!tab->var[i].is_row)
+ return 0;
+ row = tab->var[i].index;
+ if (isl_int_ne(tab->mat->row[row][0], tab->mat->row[row][2]))
+ return 0;
+ }
+ return 1;
+}
+
+/* Check if the context tableau of sol has any integer points.
+ * Leave tab in empty state if no integer point can be found.
+ * If an integer point can be found and if moreover it is finite,
+ * then it is added to the list of sample values.
+ *
+ * This function is only called when none of the currently active sample
+ * values satisfies the most recently added constraint.
+ */
+static struct isl_tab *check_integer_feasible(struct isl_tab *tab)
+{
+ struct isl_tab_undo *snap;
+
+ if (!tab)
+ return NULL;
+
+ snap = isl_tab_snap(tab);
+ if (isl_tab_push_basis(tab) < 0)
+ goto error;
+
+ tab = cut_to_integer_lexmin(tab, CUT_ALL);
+ if (!tab)
+ goto error;
+
+ if (!tab->empty && sample_is_finite(tab)) {
+ struct isl_vec *sample;
+
+ sample = isl_tab_get_sample_value(tab);
+
+ if (isl_tab_add_sample(tab, sample) < 0)
+ goto error;
+ }
+
+ if (!tab->empty && isl_tab_rollback(tab, snap) < 0)
+ goto error;
+
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Check if any of the currently active sample values satisfies
+ * the inequality "ineq" (an equality if eq is set).
+ */
+static int tab_has_valid_sample(struct isl_tab *tab, isl_int *ineq, int eq)
+{
+ int i;
+ isl_int v;
+
+ if (!tab)
+ return -1;
+
+ isl_assert(tab->mat->ctx, tab->bmap, return -1);
+ isl_assert(tab->mat->ctx, tab->samples, return -1);
+ isl_assert(tab->mat->ctx, tab->samples->n_col == 1 + tab->n_var, return -1);
+
+ isl_int_init(v);
+ for (i = tab->n_outside; i < tab->n_sample; ++i) {
+ int sgn;
+ isl_seq_inner_product(ineq, tab->samples->row[i],
+ 1 + tab->n_var, &v);
+ sgn = isl_int_sgn(v);
+ if (eq ? (sgn == 0) : (sgn >= 0))
+ break;
+ }
+ isl_int_clear(v);
+
+ return i < tab->n_sample;
+}
+
+/* Insert a div specified by "div" to the tableau "tab" at position "pos" and
+ * return isl_bool_true if the div is obviously non-negative.
+ */
+static isl_bool context_tab_insert_div(struct isl_tab *tab, int pos,
+ __isl_keep isl_vec *div,
+ isl_stat (*add_ineq)(void *user, isl_int *), void *user)
+{
+ int i;
+ int r;
+ struct isl_mat *samples;
+ int nonneg;
+
+ r = isl_tab_insert_div(tab, pos, div, add_ineq, user);
+ if (r < 0)
+ return isl_bool_error;
+ nonneg = tab->var[r].is_nonneg;
+ tab->var[r].frozen = 1;
+
+ samples = isl_mat_extend(tab->samples,
+ tab->n_sample, 1 + tab->n_var);
+ tab->samples = samples;
+ if (!samples)
+ return isl_bool_error;
+ for (i = tab->n_outside; i < samples->n_row; ++i) {
+ isl_seq_inner_product(div->el + 1, samples->row[i],
+ div->size - 1, &samples->row[i][samples->n_col - 1]);
+ isl_int_fdiv_q(samples->row[i][samples->n_col - 1],
+ samples->row[i][samples->n_col - 1], div->el[0]);
+ }
+ tab->samples = isl_mat_move_cols(tab->samples, 1 + pos,
+ 1 + tab->n_var - 1, 1);
+ if (!tab->samples)
+ return isl_bool_error;
+
+ return isl_bool_ok(nonneg);
+}
+
+/* Add a div specified by "div" to both the main tableau and
+ * the context tableau. In case of the main tableau, we only
+ * need to add an extra div. In the context tableau, we also
+ * need to express the meaning of the div.
+ * Return the index of the div or -1 if anything went wrong.
+ *
+ * The new integer division is added before any unknown integer
+ * divisions in the context to ensure that it does not get
+ * equated to some linear combination involving unknown integer
+ * divisions.
+ */
+static int add_div(struct isl_tab *tab, struct isl_context *context,
+ __isl_keep isl_vec *div)
+{
+ int r;
+ int pos;
+ isl_bool nonneg;
+ struct isl_tab *context_tab = context->op->peek_tab(context);
+
+ if (!tab || !context_tab)
+ goto error;
+
+ pos = context_tab->n_var - context->n_unknown;
+ if ((nonneg = context->op->insert_div(context, pos, div)) < 0)
+ goto error;
+
+ if (!context->op->is_ok(context))
+ goto error;
+
+ pos = tab->n_var - context->n_unknown;
+ if (isl_tab_extend_vars(tab, 1) < 0)
+ goto error;
+ r = isl_tab_insert_var(tab, pos);
+ if (r < 0)
+ goto error;
+ if (nonneg)
+ tab->var[r].is_nonneg = 1;
+ tab->var[r].frozen = 1;
+ tab->n_div++;
+
+ return tab->n_div - 1 - context->n_unknown;
+error:
+ context->op->invalidate(context);
+ return -1;
+}
+
+/* Return the position of the integer division that is equal to div/denom
+ * if there is one. Otherwise, return a position beyond the integer divisions.
+ */
+static int find_div(struct isl_tab *tab, isl_int *div, isl_int denom)
+{
+ int i;
+ isl_size total = isl_basic_map_dim(tab->bmap, isl_dim_all);
+ isl_size n_div;
+
+ n_div = isl_basic_map_dim(tab->bmap, isl_dim_div);
+ if (total < 0 || n_div < 0)
+ return -1;
+ for (i = 0; i < n_div; ++i) {
+ if (isl_int_ne(tab->bmap->div[i][0], denom))
+ continue;
+ if (!isl_seq_eq(tab->bmap->div[i] + 1, div, 1 + total))
+ continue;
+ return i;
+ }
+ return n_div;
+}
+
+/* Return the index of a div that corresponds to "div".
+ * We first check if we already have such a div and if not, we create one.
+ */
+static int get_div(struct isl_tab *tab, struct isl_context *context,
+ struct isl_vec *div)
+{
+ int d;
+ struct isl_tab *context_tab = context->op->peek_tab(context);
+ unsigned n_div;
+
+ if (!context_tab)
+ return -1;
+
+ n_div = isl_basic_map_dim(context_tab->bmap, isl_dim_div);
+ d = find_div(context_tab, div->el + 1, div->el[0]);
+ if (d < 0)
+ return -1;
+ if (d < n_div)
+ return d;
+
+ return add_div(tab, context, div);
+}
+
+/* Add a parametric cut to cut away the non-integral sample value
+ * of the given row.
+ * Let a_i be the coefficients of the constant term and the parameters
+ * and let b_i be the coefficients of the variables or constraints
+ * in basis of the tableau.
+ * Let q be the div q = floor(\sum_i {-a_i} y_i).
+ *
+ * The cut is expressed as
+ *
+ * c = \sum_i -{-a_i} y_i + \sum_i {b_i} x_i + q >= 0
+ *
+ * If q did not already exist in the context tableau, then it is added first.
+ * If q is in a column of the main tableau then the "+ q" can be accomplished
+ * by setting the corresponding entry to the denominator of the constraint.
+ * If q happens to be in a row of the main tableau, then the corresponding
+ * row needs to be added instead (taking care of the denominators).
+ * Note that this is very unlikely, but perhaps not entirely impossible.
+ *
+ * The current value of the cut is known to be negative (or at least
+ * non-positive), so row_sign is set accordingly.
+ *
+ * Return the row of the cut or -1.
+ */
+static int add_parametric_cut(struct isl_tab *tab, int row,
+ struct isl_context *context)
+{
+ struct isl_vec *div;
+ int d;
+ int i;
+ int r;
+ isl_int *r_row;
+ int col;
+ int n;
+ unsigned off = 2 + tab->M;
+
+ if (!context)
+ return -1;
+
+ div = get_row_parameter_div(tab, row);
+ if (!div)
+ return -1;
+
+ n = tab->n_div - context->n_unknown;
+ d = context->op->get_div(context, tab, div);
+ isl_vec_free(div);
+ if (d < 0)
+ return -1;
+
+ if (isl_tab_extend_cons(tab, 1) < 0)
+ return -1;
+ r = isl_tab_allocate_con(tab);
+ if (r < 0)
+ return -1;
+
+ r_row = tab->mat->row[tab->con[r].index];
+ isl_int_set(r_row[0], tab->mat->row[row][0]);
+ isl_int_neg(r_row[1], tab->mat->row[row][1]);
+ isl_int_fdiv_r(r_row[1], r_row[1], tab->mat->row[row][0]);
+ isl_int_neg(r_row[1], r_row[1]);
+ if (tab->M)
+ isl_int_set_si(r_row[2], 0);
+ for (i = 0; i < tab->n_param; ++i) {
+ if (tab->var[i].is_row)
+ continue;
+ col = tab->var[i].index;
+ isl_int_neg(r_row[off + col], tab->mat->row[row][off + col]);
+ isl_int_fdiv_r(r_row[off + col], r_row[off + col],
+ tab->mat->row[row][0]);
+ isl_int_neg(r_row[off + col], r_row[off + col]);
+ }
+ for (i = 0; i < tab->n_div; ++i) {
+ if (tab->var[tab->n_var - tab->n_div + i].is_row)
+ continue;
+ col = tab->var[tab->n_var - tab->n_div + i].index;
+ isl_int_neg(r_row[off + col], tab->mat->row[row][off + col]);
+ isl_int_fdiv_r(r_row[off + col], r_row[off + col],
+ tab->mat->row[row][0]);
+ isl_int_neg(r_row[off + col], r_row[off + col]);
+ }
+ for (i = 0; i < tab->n_col; ++i) {
+ if (tab->col_var[i] >= 0 &&
+ (tab->col_var[i] < tab->n_param ||
+ tab->col_var[i] >= tab->n_var - tab->n_div))
+ continue;
+ isl_int_fdiv_r(r_row[off + i],
+ tab->mat->row[row][off + i], tab->mat->row[row][0]);
+ }
+ if (tab->var[tab->n_var - tab->n_div + d].is_row) {
+ isl_int gcd;
+ int d_row = tab->var[tab->n_var - tab->n_div + d].index;
+ isl_int_init(gcd);
+ isl_int_gcd(gcd, tab->mat->row[d_row][0], r_row[0]);
+ isl_int_divexact(r_row[0], r_row[0], gcd);
+ isl_int_divexact(gcd, tab->mat->row[d_row][0], gcd);
+ isl_seq_combine(r_row + 1, gcd, r_row + 1,
+ r_row[0], tab->mat->row[d_row] + 1,
+ off - 1 + tab->n_col);
+ isl_int_mul(r_row[0], r_row[0], tab->mat->row[d_row][0]);
+ isl_int_clear(gcd);
+ } else {
+ col = tab->var[tab->n_var - tab->n_div + d].index;
+ isl_int_set(r_row[off + col], tab->mat->row[row][0]);
+ }
+
+ tab->con[r].is_nonneg = 1;
+ if (isl_tab_push_var(tab, isl_tab_undo_nonneg, &tab->con[r]) < 0)
+ return -1;
+ if (tab->row_sign)
+ tab->row_sign[tab->con[r].index] = isl_tab_row_neg;
+
+ row = tab->con[r].index;
+
+ if (d >= n && context->op->detect_equalities(context, tab) < 0)
+ return -1;
+
+ return row;
+}
+
+/* Construct a tableau for bmap that can be used for computing
+ * the lexicographic minimum (or maximum) of bmap.
+ * If not NULL, then dom is the domain where the minimum
+ * should be computed. In this case, we set up a parametric
+ * tableau with row signs (initialized to "unknown").
+ * If M is set, then the tableau will use a big parameter.
+ * If max is set, then a maximum should be computed instead of a minimum.
+ * This means that for each variable x, the tableau will contain the variable
+ * x' = M - x, rather than x' = M + x. This in turn means that the coefficient
+ * of the variables in all constraints are negated prior to adding them
+ * to the tableau.
+ */
+static __isl_give struct isl_tab *tab_for_lexmin(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_basic_set *dom, unsigned M, int max)
+{
+ int i;
+ struct isl_tab *tab;
+ unsigned n_var;
+ unsigned o_var;
+ isl_size total;
+
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return NULL;
+ tab = isl_tab_alloc(bmap->ctx, 2 * bmap->n_eq + bmap->n_ineq + 1,
+ total, M);
+ if (!tab)
+ return NULL;
+
+ tab->rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL);
+ if (dom) {
+ isl_size dom_total;
+ dom_total = isl_basic_set_dim(dom, isl_dim_all);
+ if (dom_total < 0)
+ goto error;
+ tab->n_param = dom_total - dom->n_div;
+ tab->n_div = dom->n_div;
+ tab->row_sign = isl_calloc_array(bmap->ctx,
+ enum isl_tab_row_sign, tab->mat->n_row);
+ if (tab->mat->n_row && !tab->row_sign)
+ goto error;
+ }
+ if (ISL_F_ISSET(bmap, ISL_BASIC_MAP_EMPTY)) {
+ if (isl_tab_mark_empty(tab) < 0)
+ goto error;
+ return tab;
+ }
+
+ for (i = tab->n_param; i < tab->n_var - tab->n_div; ++i) {
+ tab->var[i].is_nonneg = 1;
+ tab->var[i].frozen = 1;
+ }
+ o_var = 1 + tab->n_param;
+ n_var = tab->n_var - tab->n_param - tab->n_div;
+ for (i = 0; i < bmap->n_eq; ++i) {
+ if (max)
+ isl_seq_neg(bmap->eq[i] + o_var,
+ bmap->eq[i] + o_var, n_var);
+ tab = add_lexmin_valid_eq(tab, bmap->eq[i]);
+ if (max)
+ isl_seq_neg(bmap->eq[i] + o_var,
+ bmap->eq[i] + o_var, n_var);
+ if (!tab || tab->empty)
+ return tab;
+ }
+ if (bmap->n_eq && restore_lexmin(tab) < 0)
+ goto error;
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ if (max)
+ isl_seq_neg(bmap->ineq[i] + o_var,
+ bmap->ineq[i] + o_var, n_var);
+ tab = add_lexmin_ineq(tab, bmap->ineq[i]);
+ if (max)
+ isl_seq_neg(bmap->ineq[i] + o_var,
+ bmap->ineq[i] + o_var, n_var);
+ if (!tab || tab->empty)
+ return tab;
+ }
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Given a main tableau where more than one row requires a split,
+ * determine and return the "best" row to split on.
+ *
+ * If any of the rows requiring a split only involves
+ * variables that also appear in the context tableau,
+ * then the negative part is guaranteed not to have a solution.
+ * It is therefore best to split on any of these rows first.
+ *
+ * Otherwise,
+ * given two rows in the main tableau, if the inequality corresponding
+ * to the first row is redundant with respect to that of the second row
+ * in the current tableau, then it is better to split on the second row,
+ * since in the positive part, both rows will be positive.
+ * (In the negative part a pivot will have to be performed and just about
+ * anything can happen to the sign of the other row.)
+ *
+ * As a simple heuristic, we therefore select the row that makes the most
+ * of the other rows redundant.
+ *
+ * Perhaps it would also be useful to look at the number of constraints
+ * that conflict with any given constraint.
+ *
+ * best is the best row so far (-1 when we have not found any row yet).
+ * best_r is the number of other rows made redundant by row best.
+ * When best is still -1, bset_r is meaningless, but it is initialized
+ * to some arbitrary value (0) anyway. Without this redundant initialization
+ * valgrind may warn about uninitialized memory accesses when isl
+ * is compiled with some versions of gcc.
+ */
+static int best_split(struct isl_tab *tab, struct isl_tab *context_tab)
+{
+ struct isl_tab_undo *snap;
+ int split;
+ int row;
+ int best = -1;
+ int best_r = 0;
+
+ if (isl_tab_extend_cons(context_tab, 2) < 0)
+ return -1;
+
+ snap = isl_tab_snap(context_tab);
+
+ for (split = tab->n_redundant; split < tab->n_row; ++split) {
+ struct isl_tab_undo *snap2;
+ struct isl_vec *ineq = NULL;
+ int r = 0;
+ int ok;
+
+ if (!isl_tab_var_from_row(tab, split)->is_nonneg)
+ continue;
+ if (tab->row_sign[split] != isl_tab_row_any)
+ continue;
+
+ if (is_parametric_constant(tab, split))
+ return split;
+
+ ineq = get_row_parameter_ineq(tab, split);
+ if (!ineq)
+ return -1;
+ ok = isl_tab_add_ineq(context_tab, ineq->el) >= 0;
+ isl_vec_free(ineq);
+ if (!ok)
+ return -1;
+
+ snap2 = isl_tab_snap(context_tab);
+
+ for (row = tab->n_redundant; row < tab->n_row; ++row) {
+ struct isl_tab_var *var;
+
+ if (row == split)
+ continue;
+ if (!isl_tab_var_from_row(tab, row)->is_nonneg)
+ continue;
+ if (tab->row_sign[row] != isl_tab_row_any)
+ continue;
+
+ ineq = get_row_parameter_ineq(tab, row);
+ if (!ineq)
+ return -1;
+ ok = isl_tab_add_ineq(context_tab, ineq->el) >= 0;
+ isl_vec_free(ineq);
+ if (!ok)
+ return -1;
+ var = &context_tab->con[context_tab->n_con - 1];
+ if (!context_tab->empty &&
+ !isl_tab_min_at_most_neg_one(context_tab, var))
+ r++;
+ if (isl_tab_rollback(context_tab, snap2) < 0)
+ return -1;
+ }
+ if (best == -1 || r > best_r) {
+ best = split;
+ best_r = r;
+ }
+ if (isl_tab_rollback(context_tab, snap) < 0)
+ return -1;
+ }
+
+ return best;
+}
+
+static struct isl_basic_set *context_lex_peek_basic_set(
+ struct isl_context *context)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ if (!clex->tab)
+ return NULL;
+ return isl_tab_peek_bset(clex->tab);
+}
+
+static struct isl_tab *context_lex_peek_tab(struct isl_context *context)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ return clex->tab;
+}
+
+static void context_lex_add_eq(struct isl_context *context, isl_int *eq,
+ int check, int update)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ if (isl_tab_extend_cons(clex->tab, 2) < 0)
+ goto error;
+ if (add_lexmin_eq(clex->tab, eq) < 0)
+ goto error;
+ if (check) {
+ int v = tab_has_valid_sample(clex->tab, eq, 1);
+ if (v < 0)
+ goto error;
+ if (!v)
+ clex->tab = check_integer_feasible(clex->tab);
+ }
+ if (update)
+ clex->tab = check_samples(clex->tab, eq, 1);
+ return;
+error:
+ isl_tab_free(clex->tab);
+ clex->tab = NULL;
+}
+
+static void context_lex_add_ineq(struct isl_context *context, isl_int *ineq,
+ int check, int update)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ if (isl_tab_extend_cons(clex->tab, 1) < 0)
+ goto error;
+ clex->tab = add_lexmin_ineq(clex->tab, ineq);
+ if (check) {
+ int v = tab_has_valid_sample(clex->tab, ineq, 0);
+ if (v < 0)
+ goto error;
+ if (!v)
+ clex->tab = check_integer_feasible(clex->tab);
+ }
+ if (update)
+ clex->tab = check_samples(clex->tab, ineq, 0);
+ return;
+error:
+ isl_tab_free(clex->tab);
+ clex->tab = NULL;
+}
+
+static isl_stat context_lex_add_ineq_wrap(void *user, isl_int *ineq)
+{
+ struct isl_context *context = (struct isl_context *)user;
+ context_lex_add_ineq(context, ineq, 0, 0);
+ return context->op->is_ok(context) ? isl_stat_ok : isl_stat_error;
+}
+
+/* Check which signs can be obtained by "ineq" on all the currently
+ * active sample values. See row_sign for more information.
+ */
+static enum isl_tab_row_sign tab_ineq_sign(struct isl_tab *tab, isl_int *ineq,
+ int strict)
+{
+ int i;
+ int sgn;
+ isl_int tmp;
+ enum isl_tab_row_sign res = isl_tab_row_unknown;
+
+ isl_assert(tab->mat->ctx, tab->samples, return isl_tab_row_unknown);
+ isl_assert(tab->mat->ctx, tab->samples->n_col == 1 + tab->n_var,
+ return isl_tab_row_unknown);
+
+ isl_int_init(tmp);
+ for (i = tab->n_outside; i < tab->n_sample; ++i) {
+ isl_seq_inner_product(tab->samples->row[i], ineq,
+ 1 + tab->n_var, &tmp);
+ sgn = isl_int_sgn(tmp);
+ if (sgn > 0 || (sgn == 0 && strict)) {
+ if (res == isl_tab_row_unknown)
+ res = isl_tab_row_pos;
+ if (res == isl_tab_row_neg)
+ res = isl_tab_row_any;
+ }
+ if (sgn < 0) {
+ if (res == isl_tab_row_unknown)
+ res = isl_tab_row_neg;
+ if (res == isl_tab_row_pos)
+ res = isl_tab_row_any;
+ }
+ if (res == isl_tab_row_any)
+ break;
+ }
+ isl_int_clear(tmp);
+
+ return res;
+}
+
+static enum isl_tab_row_sign context_lex_ineq_sign(struct isl_context *context,
+ isl_int *ineq, int strict)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ return tab_ineq_sign(clex->tab, ineq, strict);
+}
+
+/* Check whether "ineq" can be added to the tableau without rendering
+ * it infeasible.
+ */
+static int context_lex_test_ineq(struct isl_context *context, isl_int *ineq)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ struct isl_tab_undo *snap;
+ int feasible;
+
+ if (!clex->tab)
+ return -1;
+
+ if (isl_tab_extend_cons(clex->tab, 1) < 0)
+ return -1;
+
+ snap = isl_tab_snap(clex->tab);
+ if (isl_tab_push_basis(clex->tab) < 0)
+ return -1;
+ clex->tab = add_lexmin_ineq(clex->tab, ineq);
+ clex->tab = check_integer_feasible(clex->tab);
+ if (!clex->tab)
+ return -1;
+ feasible = !clex->tab->empty;
+ if (isl_tab_rollback(clex->tab, snap) < 0)
+ return -1;
+
+ return feasible;
+}
+
+static int context_lex_get_div(struct isl_context *context, struct isl_tab *tab,
+ struct isl_vec *div)
+{
+ return get_div(tab, context, div);
+}
+
+/* Insert a div specified by "div" to the context tableau at position "pos" and
+ * return isl_bool_true if the div is obviously non-negative.
+ * context_tab_add_div will always return isl_bool_true, because all variables
+ * in a isl_context_lex tableau are non-negative.
+ * However, if we are using a big parameter in the context, then this only
+ * reflects the non-negativity of the variable used to _encode_ the
+ * div, i.e., div' = M + div, so we can't draw any conclusions.
+ */
+static isl_bool context_lex_insert_div(struct isl_context *context, int pos,
+ __isl_keep isl_vec *div)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ isl_bool nonneg;
+ nonneg = context_tab_insert_div(clex->tab, pos, div,
+ context_lex_add_ineq_wrap, context);
+ if (nonneg < 0)
+ return isl_bool_error;
+ if (clex->tab->M)
+ return isl_bool_false;
+ return nonneg;
+}
+
+static int context_lex_detect_equalities(struct isl_context *context,
+ struct isl_tab *tab)
+{
+ return 0;
+}
+
+static int context_lex_best_split(struct isl_context *context,
+ struct isl_tab *tab)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ struct isl_tab_undo *snap;
+ int r;
+
+ snap = isl_tab_snap(clex->tab);
+ if (isl_tab_push_basis(clex->tab) < 0)
+ return -1;
+ r = best_split(tab, clex->tab);
+
+ if (r >= 0 && isl_tab_rollback(clex->tab, snap) < 0)
+ return -1;
+
+ return r;
+}
+
+static int context_lex_is_empty(struct isl_context *context)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ if (!clex->tab)
+ return -1;
+ return clex->tab->empty;
+}
+
+static void *context_lex_save(struct isl_context *context)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ struct isl_tab_undo *snap;
+
+ snap = isl_tab_snap(clex->tab);
+ if (isl_tab_push_basis(clex->tab) < 0)
+ return NULL;
+ if (isl_tab_save_samples(clex->tab) < 0)
+ return NULL;
+
+ return snap;
+}
+
+static void context_lex_restore(struct isl_context *context, void *save)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ if (isl_tab_rollback(clex->tab, (struct isl_tab_undo *)save) < 0) {
+ isl_tab_free(clex->tab);
+ clex->tab = NULL;
+ }
+}
+
+static void context_lex_discard(void *save)
+{
+}
+
+static int context_lex_is_ok(struct isl_context *context)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ return !!clex->tab;
+}
+
+/* For each variable in the context tableau, check if the variable can
+ * only attain non-negative values. If so, mark the parameter as non-negative
+ * in the main tableau. This allows for a more direct identification of some
+ * cases of violated constraints.
+ */
+static struct isl_tab *tab_detect_nonnegative_parameters(struct isl_tab *tab,
+ struct isl_tab *context_tab)
+{
+ int i;
+ struct isl_tab_undo *snap;
+ struct isl_vec *ineq = NULL;
+ struct isl_tab_var *var;
+ int n;
+
+ if (context_tab->n_var == 0)
+ return tab;
+
+ ineq = isl_vec_alloc(tab->mat->ctx, 1 + context_tab->n_var);
+ if (!ineq)
+ goto error;
+
+ if (isl_tab_extend_cons(context_tab, 1) < 0)
+ goto error;
+
+ snap = isl_tab_snap(context_tab);
+
+ n = 0;
+ isl_seq_clr(ineq->el, ineq->size);
+ for (i = 0; i < context_tab->n_var; ++i) {
+ isl_int_set_si(ineq->el[1 + i], 1);
+ if (isl_tab_add_ineq(context_tab, ineq->el) < 0)
+ goto error;
+ var = &context_tab->con[context_tab->n_con - 1];
+ if (!context_tab->empty &&
+ !isl_tab_min_at_most_neg_one(context_tab, var)) {
+ int j = i;
+ if (i >= tab->n_param)
+ j = i - tab->n_param + tab->n_var - tab->n_div;
+ tab->var[j].is_nonneg = 1;
+ n++;
+ }
+ isl_int_set_si(ineq->el[1 + i], 0);
+ if (isl_tab_rollback(context_tab, snap) < 0)
+ goto error;
+ }
+
+ if (context_tab->M && n == context_tab->n_var) {
+ context_tab->mat = isl_mat_drop_cols(context_tab->mat, 2, 1);
+ context_tab->M = 0;
+ }
+
+ isl_vec_free(ineq);
+ return tab;
+error:
+ isl_vec_free(ineq);
+ isl_tab_free(tab);
+ return NULL;
+}
+
+static struct isl_tab *context_lex_detect_nonnegative_parameters(
+ struct isl_context *context, struct isl_tab *tab)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ struct isl_tab_undo *snap;
+
+ if (!tab)
+ return NULL;
+
+ snap = isl_tab_snap(clex->tab);
+ if (isl_tab_push_basis(clex->tab) < 0)
+ goto error;
+
+ tab = tab_detect_nonnegative_parameters(tab, clex->tab);
+
+ if (isl_tab_rollback(clex->tab, snap) < 0)
+ goto error;
+
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+static void context_lex_invalidate(struct isl_context *context)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ isl_tab_free(clex->tab);
+ clex->tab = NULL;
+}
+
+static __isl_null struct isl_context *context_lex_free(
+ struct isl_context *context)
+{
+ struct isl_context_lex *clex = (struct isl_context_lex *)context;
+ isl_tab_free(clex->tab);
+ free(clex);
+
+ return NULL;
+}
+
+struct isl_context_op isl_context_lex_op = {
+ context_lex_detect_nonnegative_parameters,
+ context_lex_peek_basic_set,
+ context_lex_peek_tab,
+ context_lex_add_eq,
+ context_lex_add_ineq,
+ context_lex_ineq_sign,
+ context_lex_test_ineq,
+ context_lex_get_div,
+ context_lex_insert_div,
+ context_lex_detect_equalities,
+ context_lex_best_split,
+ context_lex_is_empty,
+ context_lex_is_ok,
+ context_lex_save,
+ context_lex_restore,
+ context_lex_discard,
+ context_lex_invalidate,
+ context_lex_free,
+};
+
+static struct isl_tab *context_tab_for_lexmin(__isl_take isl_basic_set *bset)
+{
+ struct isl_tab *tab;
+
+ if (!bset)
+ return NULL;
+ tab = tab_for_lexmin(bset_to_bmap(bset), NULL, 1, 0);
+ if (isl_tab_track_bset(tab, bset) < 0)
+ goto error;
+ tab = isl_tab_init_samples(tab);
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+static struct isl_context *isl_context_lex_alloc(struct isl_basic_set *dom)
+{
+ struct isl_context_lex *clex;
+
+ if (!dom)
+ return NULL;
+
+ clex = isl_alloc_type(dom->ctx, struct isl_context_lex);
+ if (!clex)
+ return NULL;
+
+ clex->context.op = &isl_context_lex_op;
+
+ clex->tab = context_tab_for_lexmin(isl_basic_set_copy(dom));
+ if (restore_lexmin(clex->tab) < 0)
+ goto error;
+ clex->tab = check_integer_feasible(clex->tab);
+ if (!clex->tab)
+ goto error;
+
+ return &clex->context;
+error:
+ clex->context.op->free(&clex->context);
+ return NULL;
+}
+
+/* Representation of the context when using generalized basis reduction.
+ *
+ * "shifted" contains the offsets of the unit hypercubes that lie inside the
+ * context. Any rational point in "shifted" can therefore be rounded
+ * up to an integer point in the context.
+ * If the context is constrained by any equality, then "shifted" is not used
+ * as it would be empty.
+ */
+struct isl_context_gbr {
+ struct isl_context context;
+ struct isl_tab *tab;
+ struct isl_tab *shifted;
+ struct isl_tab *cone;
+};
+
+static struct isl_tab *context_gbr_detect_nonnegative_parameters(
+ struct isl_context *context, struct isl_tab *tab)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ if (!tab)
+ return NULL;
+ return tab_detect_nonnegative_parameters(tab, cgbr->tab);
+}
+
+static struct isl_basic_set *context_gbr_peek_basic_set(
+ struct isl_context *context)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ if (!cgbr->tab)
+ return NULL;
+ return isl_tab_peek_bset(cgbr->tab);
+}
+
+static struct isl_tab *context_gbr_peek_tab(struct isl_context *context)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ return cgbr->tab;
+}
+
+/* Initialize the "shifted" tableau of the context, which
+ * contains the constraints of the original tableau shifted
+ * by the sum of all negative coefficients. This ensures
+ * that any rational point in the shifted tableau can
+ * be rounded up to yield an integer point in the original tableau.
+ */
+static void gbr_init_shifted(struct isl_context_gbr *cgbr)
+{
+ int i, j;
+ struct isl_vec *cst;
+ struct isl_basic_set *bset = isl_tab_peek_bset(cgbr->tab);
+ isl_size dim = isl_basic_set_dim(bset, isl_dim_all);
+
+ if (dim < 0)
+ return;
+ cst = isl_vec_alloc(cgbr->tab->mat->ctx, bset->n_ineq);
+ if (!cst)
+ return;
+
+ for (i = 0; i < bset->n_ineq; ++i) {
+ isl_int_set(cst->el[i], bset->ineq[i][0]);
+ for (j = 0; j < dim; ++j) {
+ if (!isl_int_is_neg(bset->ineq[i][1 + j]))
+ continue;
+ isl_int_add(bset->ineq[i][0], bset->ineq[i][0],
+ bset->ineq[i][1 + j]);
+ }
+ }
+
+ cgbr->shifted = isl_tab_from_basic_set(bset, 0);
+
+ for (i = 0; i < bset->n_ineq; ++i)
+ isl_int_set(bset->ineq[i][0], cst->el[i]);
+
+ isl_vec_free(cst);
+}
+
+/* Check if the shifted tableau is non-empty, and if so
+ * use the sample point to construct an integer point
+ * of the context tableau.
+ */
+static struct isl_vec *gbr_get_shifted_sample(struct isl_context_gbr *cgbr)
+{
+ struct isl_vec *sample;
+
+ if (!cgbr->shifted)
+ gbr_init_shifted(cgbr);
+ if (!cgbr->shifted)
+ return NULL;
+ if (cgbr->shifted->empty)
+ return isl_vec_alloc(cgbr->tab->mat->ctx, 0);
+
+ sample = isl_tab_get_sample_value(cgbr->shifted);
+ sample = isl_vec_ceil(sample);
+
+ return sample;
+}
+
+static __isl_give isl_basic_set *drop_constant_terms(
+ __isl_take isl_basic_set *bset)
+{
+ int i;
+
+ if (!bset)
+ return NULL;
+
+ for (i = 0; i < bset->n_eq; ++i)
+ isl_int_set_si(bset->eq[i][0], 0);
+
+ for (i = 0; i < bset->n_ineq; ++i)
+ isl_int_set_si(bset->ineq[i][0], 0);
+
+ return bset;
+}
+
+static int use_shifted(struct isl_context_gbr *cgbr)
+{
+ if (!cgbr->tab)
+ return 0;
+ return cgbr->tab->bmap->n_eq == 0 && cgbr->tab->bmap->n_div == 0;
+}
+
+static struct isl_vec *gbr_get_sample(struct isl_context_gbr *cgbr)
+{
+ struct isl_basic_set *bset;
+ struct isl_basic_set *cone;
+
+ if (isl_tab_sample_is_integer(cgbr->tab))
+ return isl_tab_get_sample_value(cgbr->tab);
+
+ if (use_shifted(cgbr)) {
+ struct isl_vec *sample;
+
+ sample = gbr_get_shifted_sample(cgbr);
+ if (!sample || sample->size > 0)
+ return sample;
+
+ isl_vec_free(sample);
+ }
+
+ if (!cgbr->cone) {
+ bset = isl_tab_peek_bset(cgbr->tab);
+ cgbr->cone = isl_tab_from_recession_cone(bset, 0);
+ if (!cgbr->cone)
+ return NULL;
+ if (isl_tab_track_bset(cgbr->cone,
+ isl_basic_set_copy(bset)) < 0)
+ return NULL;
+ }
+ if (isl_tab_detect_implicit_equalities(cgbr->cone) < 0)
+ return NULL;
+
+ if (cgbr->cone->n_dead == cgbr->cone->n_col) {
+ struct isl_vec *sample;
+ struct isl_tab_undo *snap;
+
+ if (cgbr->tab->basis) {
+ if (cgbr->tab->basis->n_col != 1 + cgbr->tab->n_var) {
+ isl_mat_free(cgbr->tab->basis);
+ cgbr->tab->basis = NULL;
+ }
+ cgbr->tab->n_zero = 0;
+ cgbr->tab->n_unbounded = 0;
+ }
+
+ snap = isl_tab_snap(cgbr->tab);
+
+ sample = isl_tab_sample(cgbr->tab);
+
+ if (!sample || isl_tab_rollback(cgbr->tab, snap) < 0) {
+ isl_vec_free(sample);
+ return NULL;
+ }
+
+ return sample;
+ }
+
+ cone = isl_basic_set_dup(isl_tab_peek_bset(cgbr->cone));
+ cone = drop_constant_terms(cone);
+ cone = isl_basic_set_update_from_tab(cone, cgbr->cone);
+ cone = isl_basic_set_underlying_set(cone);
+ cone = isl_basic_set_gauss(cone, NULL);
+
+ bset = isl_basic_set_dup(isl_tab_peek_bset(cgbr->tab));
+ bset = isl_basic_set_update_from_tab(bset, cgbr->tab);
+ bset = isl_basic_set_underlying_set(bset);
+ bset = isl_basic_set_gauss(bset, NULL);
+
+ return isl_basic_set_sample_with_cone(bset, cone);
+}
+
+static void check_gbr_integer_feasible(struct isl_context_gbr *cgbr)
+{
+ struct isl_vec *sample;
+
+ if (!cgbr->tab)
+ return;
+
+ if (cgbr->tab->empty)
+ return;
+
+ sample = gbr_get_sample(cgbr);
+ if (!sample)
+ goto error;
+
+ if (sample->size == 0) {
+ isl_vec_free(sample);
+ if (isl_tab_mark_empty(cgbr->tab) < 0)
+ goto error;
+ return;
+ }
+
+ if (isl_tab_add_sample(cgbr->tab, sample) < 0)
+ goto error;
+
+ return;
+error:
+ isl_tab_free(cgbr->tab);
+ cgbr->tab = NULL;
+}
+
+static struct isl_tab *add_gbr_eq(struct isl_tab *tab, isl_int *eq)
+{
+ if (!tab)
+ return NULL;
+
+ if (isl_tab_extend_cons(tab, 2) < 0)
+ goto error;
+
+ if (isl_tab_add_eq(tab, eq) < 0)
+ goto error;
+
+ return tab;
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Add the equality described by "eq" to the context.
+ * If "check" is set, then we check if the context is empty after
+ * adding the equality.
+ * If "update" is set, then we check if the samples are still valid.
+ *
+ * We do not explicitly add shifted copies of the equality to
+ * cgbr->shifted since they would conflict with each other.
+ * Instead, we directly mark cgbr->shifted empty.
+ */
+static void context_gbr_add_eq(struct isl_context *context, isl_int *eq,
+ int check, int update)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+
+ cgbr->tab = add_gbr_eq(cgbr->tab, eq);
+
+ if (cgbr->shifted && !cgbr->shifted->empty && use_shifted(cgbr)) {
+ if (isl_tab_mark_empty(cgbr->shifted) < 0)
+ goto error;
+ }
+
+ if (cgbr->cone && cgbr->cone->n_col != cgbr->cone->n_dead) {
+ if (isl_tab_extend_cons(cgbr->cone, 2) < 0)
+ goto error;
+ if (isl_tab_add_eq(cgbr->cone, eq) < 0)
+ goto error;
+ }
+
+ if (check) {
+ int v = tab_has_valid_sample(cgbr->tab, eq, 1);
+ if (v < 0)
+ goto error;
+ if (!v)
+ check_gbr_integer_feasible(cgbr);
+ }
+ if (update)
+ cgbr->tab = check_samples(cgbr->tab, eq, 1);
+ return;
+error:
+ isl_tab_free(cgbr->tab);
+ cgbr->tab = NULL;
+}
+
+static void add_gbr_ineq(struct isl_context_gbr *cgbr, isl_int *ineq)
+{
+ if (!cgbr->tab)
+ return;
+
+ if (isl_tab_extend_cons(cgbr->tab, 1) < 0)
+ goto error;
+
+ if (isl_tab_add_ineq(cgbr->tab, ineq) < 0)
+ goto error;
+
+ if (cgbr->shifted && !cgbr->shifted->empty && use_shifted(cgbr)) {
+ int i;
+ isl_size dim;
+ dim = isl_basic_map_dim(cgbr->tab->bmap, isl_dim_all);
+ if (dim < 0)
+ goto error;
+
+ if (isl_tab_extend_cons(cgbr->shifted, 1) < 0)
+ goto error;
+
+ for (i = 0; i < dim; ++i) {
+ if (!isl_int_is_neg(ineq[1 + i]))
+ continue;
+ isl_int_add(ineq[0], ineq[0], ineq[1 + i]);
+ }
+
+ if (isl_tab_add_ineq(cgbr->shifted, ineq) < 0)
+ goto error;
+
+ for (i = 0; i < dim; ++i) {
+ if (!isl_int_is_neg(ineq[1 + i]))
+ continue;
+ isl_int_sub(ineq[0], ineq[0], ineq[1 + i]);
+ }
+ }
+
+ if (cgbr->cone && cgbr->cone->n_col != cgbr->cone->n_dead) {
+ if (isl_tab_extend_cons(cgbr->cone, 1) < 0)
+ goto error;
+ if (isl_tab_add_ineq(cgbr->cone, ineq) < 0)
+ goto error;
+ }
+
+ return;
+error:
+ isl_tab_free(cgbr->tab);
+ cgbr->tab = NULL;
+}
+
+static void context_gbr_add_ineq(struct isl_context *context, isl_int *ineq,
+ int check, int update)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+
+ add_gbr_ineq(cgbr, ineq);
+ if (!cgbr->tab)
+ return;
+
+ if (check) {
+ int v = tab_has_valid_sample(cgbr->tab, ineq, 0);
+ if (v < 0)
+ goto error;
+ if (!v)
+ check_gbr_integer_feasible(cgbr);
+ }
+ if (update)
+ cgbr->tab = check_samples(cgbr->tab, ineq, 0);
+ return;
+error:
+ isl_tab_free(cgbr->tab);
+ cgbr->tab = NULL;
+}
+
+static isl_stat context_gbr_add_ineq_wrap(void *user, isl_int *ineq)
+{
+ struct isl_context *context = (struct isl_context *)user;
+ context_gbr_add_ineq(context, ineq, 0, 0);
+ return context->op->is_ok(context) ? isl_stat_ok : isl_stat_error;
+}
+
+static enum isl_tab_row_sign context_gbr_ineq_sign(struct isl_context *context,
+ isl_int *ineq, int strict)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ return tab_ineq_sign(cgbr->tab, ineq, strict);
+}
+
+/* Check whether "ineq" can be added to the tableau without rendering
+ * it infeasible.
+ */
+static int context_gbr_test_ineq(struct isl_context *context, isl_int *ineq)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ struct isl_tab_undo *snap;
+ struct isl_tab_undo *shifted_snap = NULL;
+ struct isl_tab_undo *cone_snap = NULL;
+ int feasible;
+
+ if (!cgbr->tab)
+ return -1;
+
+ if (isl_tab_extend_cons(cgbr->tab, 1) < 0)
+ return -1;
+
+ snap = isl_tab_snap(cgbr->tab);
+ if (cgbr->shifted)
+ shifted_snap = isl_tab_snap(cgbr->shifted);
+ if (cgbr->cone)
+ cone_snap = isl_tab_snap(cgbr->cone);
+ add_gbr_ineq(cgbr, ineq);
+ check_gbr_integer_feasible(cgbr);
+ if (!cgbr->tab)
+ return -1;
+ feasible = !cgbr->tab->empty;
+ if (isl_tab_rollback(cgbr->tab, snap) < 0)
+ return -1;
+ if (shifted_snap) {
+ if (isl_tab_rollback(cgbr->shifted, shifted_snap))
+ return -1;
+ } else if (cgbr->shifted) {
+ isl_tab_free(cgbr->shifted);
+ cgbr->shifted = NULL;
+ }
+ if (cone_snap) {
+ if (isl_tab_rollback(cgbr->cone, cone_snap))
+ return -1;
+ } else if (cgbr->cone) {
+ isl_tab_free(cgbr->cone);
+ cgbr->cone = NULL;
+ }
+
+ return feasible;
+}
+
+/* Return the column of the last of the variables associated to
+ * a column that has a non-zero coefficient.
+ * This function is called in a context where only coefficients
+ * of parameters or divs can be non-zero.
+ */
+static int last_non_zero_var_col(struct isl_tab *tab, isl_int *p)
+{
+ int i;
+ int col;
+
+ if (tab->n_var == 0)
+ return -1;
+
+ for (i = tab->n_var - 1; i >= 0; --i) {
+ if (i >= tab->n_param && i < tab->n_var - tab->n_div)
+ continue;
+ if (tab->var[i].is_row)
+ continue;
+ col = tab->var[i].index;
+ if (!isl_int_is_zero(p[col]))
+ return col;
+ }
+
+ return -1;
+}
+
+/* Look through all the recently added equalities in the context
+ * to see if we can propagate any of them to the main tableau.
+ *
+ * The newly added equalities in the context are encoded as pairs
+ * of inequalities starting at inequality "first".
+ *
+ * We tentatively add each of these equalities to the main tableau
+ * and if this happens to result in a row with a final coefficient
+ * that is one or negative one, we use it to kill a column
+ * in the main tableau. Otherwise, we discard the tentatively
+ * added row.
+ * This tentative addition of equality constraints turns
+ * on the undo facility of the tableau. Turn it off again
+ * at the end, assuming it was turned off to begin with.
+ *
+ * Return 0 on success and -1 on failure.
+ */
+static int propagate_equalities(struct isl_context_gbr *cgbr,
+ struct isl_tab *tab, unsigned first)
+{
+ int i;
+ struct isl_vec *eq = NULL;
+ isl_bool needs_undo;
+
+ needs_undo = isl_tab_need_undo(tab);
+ if (needs_undo < 0)
+ goto error;
+ eq = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_var);
+ if (!eq)
+ goto error;
+
+ if (isl_tab_extend_cons(tab, (cgbr->tab->bmap->n_ineq - first)/2) < 0)
+ goto error;
+
+ isl_seq_clr(eq->el + 1 + tab->n_param,
+ tab->n_var - tab->n_param - tab->n_div);
+ for (i = first; i < cgbr->tab->bmap->n_ineq; i += 2) {
+ int j;
+ int r;
+ struct isl_tab_undo *snap;
+ snap = isl_tab_snap(tab);
+
+ isl_seq_cpy(eq->el, cgbr->tab->bmap->ineq[i], 1 + tab->n_param);
+ isl_seq_cpy(eq->el + 1 + tab->n_var - tab->n_div,
+ cgbr->tab->bmap->ineq[i] + 1 + tab->n_param,
+ tab->n_div);
+
+ r = isl_tab_add_row(tab, eq->el);
+ if (r < 0)
+ goto error;
+ r = tab->con[r].index;
+ j = last_non_zero_var_col(tab, tab->mat->row[r] + 2 + tab->M);
+ if (j < 0 || j < tab->n_dead ||
+ !isl_int_is_one(tab->mat->row[r][0]) ||
+ (!isl_int_is_one(tab->mat->row[r][2 + tab->M + j]) &&
+ !isl_int_is_negone(tab->mat->row[r][2 + tab->M + j]))) {
+ if (isl_tab_rollback(tab, snap) < 0)
+ goto error;
+ continue;
+ }
+ if (isl_tab_pivot(tab, r, j) < 0)
+ goto error;
+ if (isl_tab_kill_col(tab, j) < 0)
+ goto error;
+
+ if (restore_lexmin(tab) < 0)
+ goto error;
+ }
+
+ if (!needs_undo)
+ isl_tab_clear_undo(tab);
+ isl_vec_free(eq);
+
+ return 0;
+error:
+ isl_vec_free(eq);
+ isl_tab_free(cgbr->tab);
+ cgbr->tab = NULL;
+ return -1;
+}
+
+static int context_gbr_detect_equalities(struct isl_context *context,
+ struct isl_tab *tab)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ unsigned n_ineq;
+
+ if (!cgbr->cone) {
+ struct isl_basic_set *bset = isl_tab_peek_bset(cgbr->tab);
+ cgbr->cone = isl_tab_from_recession_cone(bset, 0);
+ if (!cgbr->cone)
+ goto error;
+ if (isl_tab_track_bset(cgbr->cone,
+ isl_basic_set_copy(bset)) < 0)
+ goto error;
+ }
+ if (isl_tab_detect_implicit_equalities(cgbr->cone) < 0)
+ goto error;
+
+ n_ineq = cgbr->tab->bmap->n_ineq;
+ cgbr->tab = isl_tab_detect_equalities(cgbr->tab, cgbr->cone);
+ if (!cgbr->tab)
+ return -1;
+ if (cgbr->tab->bmap->n_ineq > n_ineq &&
+ propagate_equalities(cgbr, tab, n_ineq) < 0)
+ return -1;
+
+ return 0;
+error:
+ isl_tab_free(cgbr->tab);
+ cgbr->tab = NULL;
+ return -1;
+}
+
+static int context_gbr_get_div(struct isl_context *context, struct isl_tab *tab,
+ struct isl_vec *div)
+{
+ return get_div(tab, context, div);
+}
+
+static isl_bool context_gbr_insert_div(struct isl_context *context, int pos,
+ __isl_keep isl_vec *div)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ if (cgbr->cone) {
+ int r, o_div;
+ isl_size n_div;
+
+ n_div = isl_basic_map_dim(cgbr->cone->bmap, isl_dim_div);
+ if (n_div < 0)
+ return isl_bool_error;
+ o_div = cgbr->cone->n_var - n_div;
+
+ if (isl_tab_extend_cons(cgbr->cone, 3) < 0)
+ return isl_bool_error;
+ if (isl_tab_extend_vars(cgbr->cone, 1) < 0)
+ return isl_bool_error;
+ if ((r = isl_tab_insert_var(cgbr->cone, pos)) <0)
+ return isl_bool_error;
+
+ cgbr->cone->bmap = isl_basic_map_insert_div(cgbr->cone->bmap,
+ r - o_div, div);
+ if (!cgbr->cone->bmap)
+ return isl_bool_error;
+ if (isl_tab_push_var(cgbr->cone, isl_tab_undo_bmap_div,
+ &cgbr->cone->var[r]) < 0)
+ return isl_bool_error;
+ }
+ return context_tab_insert_div(cgbr->tab, pos, div,
+ context_gbr_add_ineq_wrap, context);
+}
+
+static int context_gbr_best_split(struct isl_context *context,
+ struct isl_tab *tab)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ struct isl_tab_undo *snap;
+ int r;
+
+ snap = isl_tab_snap(cgbr->tab);
+ r = best_split(tab, cgbr->tab);
+
+ if (r >= 0 && isl_tab_rollback(cgbr->tab, snap) < 0)
+ return -1;
+
+ return r;
+}
+
+static int context_gbr_is_empty(struct isl_context *context)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ if (!cgbr->tab)
+ return -1;
+ return cgbr->tab->empty;
+}
+
+struct isl_gbr_tab_undo {
+ struct isl_tab_undo *tab_snap;
+ struct isl_tab_undo *shifted_snap;
+ struct isl_tab_undo *cone_snap;
+};
+
+static void *context_gbr_save(struct isl_context *context)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ struct isl_gbr_tab_undo *snap;
+
+ if (!cgbr->tab)
+ return NULL;
+
+ snap = isl_alloc_type(cgbr->tab->mat->ctx, struct isl_gbr_tab_undo);
+ if (!snap)
+ return NULL;
+
+ snap->tab_snap = isl_tab_snap(cgbr->tab);
+ if (isl_tab_save_samples(cgbr->tab) < 0)
+ goto error;
+
+ if (cgbr->shifted)
+ snap->shifted_snap = isl_tab_snap(cgbr->shifted);
+ else
+ snap->shifted_snap = NULL;
+
+ if (cgbr->cone)
+ snap->cone_snap = isl_tab_snap(cgbr->cone);
+ else
+ snap->cone_snap = NULL;
+
+ return snap;
+error:
+ free(snap);
+ return NULL;
+}
+
+static void context_gbr_restore(struct isl_context *context, void *save)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ struct isl_gbr_tab_undo *snap = (struct isl_gbr_tab_undo *)save;
+ if (!snap)
+ goto error;
+ if (isl_tab_rollback(cgbr->tab, snap->tab_snap) < 0)
+ goto error;
+
+ if (snap->shifted_snap) {
+ if (isl_tab_rollback(cgbr->shifted, snap->shifted_snap) < 0)
+ goto error;
+ } else if (cgbr->shifted) {
+ isl_tab_free(cgbr->shifted);
+ cgbr->shifted = NULL;
+ }
+
+ if (snap->cone_snap) {
+ if (isl_tab_rollback(cgbr->cone, snap->cone_snap) < 0)
+ goto error;
+ } else if (cgbr->cone) {
+ isl_tab_free(cgbr->cone);
+ cgbr->cone = NULL;
+ }
+
+ free(snap);
+
+ return;
+error:
+ free(snap);
+ isl_tab_free(cgbr->tab);
+ cgbr->tab = NULL;
+}
+
+static void context_gbr_discard(void *save)
+{
+ struct isl_gbr_tab_undo *snap = (struct isl_gbr_tab_undo *)save;
+ free(snap);
+}
+
+static int context_gbr_is_ok(struct isl_context *context)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ return !!cgbr->tab;
+}
+
+static void context_gbr_invalidate(struct isl_context *context)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ isl_tab_free(cgbr->tab);
+ cgbr->tab = NULL;
+}
+
+static __isl_null struct isl_context *context_gbr_free(
+ struct isl_context *context)
+{
+ struct isl_context_gbr *cgbr = (struct isl_context_gbr *)context;
+ isl_tab_free(cgbr->tab);
+ isl_tab_free(cgbr->shifted);
+ isl_tab_free(cgbr->cone);
+ free(cgbr);
+
+ return NULL;
+}
+
+struct isl_context_op isl_context_gbr_op = {
+ context_gbr_detect_nonnegative_parameters,
+ context_gbr_peek_basic_set,
+ context_gbr_peek_tab,
+ context_gbr_add_eq,
+ context_gbr_add_ineq,
+ context_gbr_ineq_sign,
+ context_gbr_test_ineq,
+ context_gbr_get_div,
+ context_gbr_insert_div,
+ context_gbr_detect_equalities,
+ context_gbr_best_split,
+ context_gbr_is_empty,
+ context_gbr_is_ok,
+ context_gbr_save,
+ context_gbr_restore,
+ context_gbr_discard,
+ context_gbr_invalidate,
+ context_gbr_free,
+};
+
+static struct isl_context *isl_context_gbr_alloc(__isl_keep isl_basic_set *dom)
+{
+ struct isl_context_gbr *cgbr;
+
+ if (!dom)
+ return NULL;
+
+ cgbr = isl_calloc_type(dom->ctx, struct isl_context_gbr);
+ if (!cgbr)
+ return NULL;
+
+ cgbr->context.op = &isl_context_gbr_op;
+
+ cgbr->shifted = NULL;
+ cgbr->cone = NULL;
+ cgbr->tab = isl_tab_from_basic_set(dom, 1);
+ cgbr->tab = isl_tab_init_samples(cgbr->tab);
+ if (!cgbr->tab)
+ goto error;
+ check_gbr_integer_feasible(cgbr);
+
+ return &cgbr->context;
+error:
+ cgbr->context.op->free(&cgbr->context);
+ return NULL;
+}
+
+/* Allocate a context corresponding to "dom".
+ * The representation specific fields are initialized by
+ * isl_context_lex_alloc or isl_context_gbr_alloc.
+ * The shared "n_unknown" field is initialized to the number
+ * of final unknown integer divisions in "dom".
+ */
+static struct isl_context *isl_context_alloc(__isl_keep isl_basic_set *dom)
+{
+ struct isl_context *context;
+ int first;
+ isl_size n_div;
+
+ if (!dom)
+ return NULL;
+
+ if (dom->ctx->opt->context == ISL_CONTEXT_LEXMIN)
+ context = isl_context_lex_alloc(dom);
+ else
+ context = isl_context_gbr_alloc(dom);
+
+ if (!context)
+ return NULL;
+
+ first = isl_basic_set_first_unknown_div(dom);
+ n_div = isl_basic_set_dim(dom, isl_dim_div);
+ if (first < 0 || n_div < 0)
+ return context->op->free(context);
+ context->n_unknown = n_div - first;
+
+ return context;
+}
+
+/* Initialize some common fields of "sol", which keeps track
+ * of the solution of an optimization problem on "bmap" over
+ * the domain "dom".
+ * If "max" is set, then a maximization problem is being solved, rather than
+ * a minimization problem, which means that the variables in the
+ * tableau have value "M - x" rather than "M + x".
+ */
+static isl_stat sol_init(struct isl_sol *sol, __isl_keep isl_basic_map *bmap,
+ __isl_keep isl_basic_set *dom, int max)
+{
+ sol->rational = ISL_F_ISSET(bmap, ISL_BASIC_MAP_RATIONAL);
+ sol->dec_level.callback.run = &sol_dec_level_wrap;
+ sol->dec_level.sol = sol;
+ sol->max = max;
+ sol->n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ sol->space = isl_basic_map_get_space(bmap);
+
+ sol->context = isl_context_alloc(dom);
+ if (sol->n_out < 0 || !sol->space || !sol->context)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Construct an isl_sol_map structure for accumulating the solution.
+ * If track_empty is set, then we also keep track of the parts
+ * of the context where there is no solution.
+ * If max is set, then we are solving a maximization, rather than
+ * a minimization problem, which means that the variables in the
+ * tableau have value "M - x" rather than "M + x".
+ */
+static struct isl_sol *sol_map_init(__isl_keep isl_basic_map *bmap,
+ __isl_take isl_basic_set *dom, int track_empty, int max)
+{
+ struct isl_sol_map *sol_map = NULL;
+ isl_space *space;
+
+ if (!bmap)
+ goto error;
+
+ sol_map = isl_calloc_type(bmap->ctx, struct isl_sol_map);
+ if (!sol_map)
+ goto error;
+
+ sol_map->sol.free = &sol_map_free;
+ if (sol_init(&sol_map->sol, bmap, dom, max) < 0)
+ goto error;
+ sol_map->sol.add = &sol_map_add_wrap;
+ sol_map->sol.add_empty = track_empty ? &sol_map_add_empty_wrap : NULL;
+ space = isl_space_copy(sol_map->sol.space);
+ sol_map->map = isl_map_alloc_space(space, 1, ISL_MAP_DISJOINT);
+ if (!sol_map->map)
+ goto error;
+
+ if (track_empty) {
+ sol_map->empty = isl_set_alloc_space(isl_basic_set_get_space(dom),
+ 1, ISL_SET_DISJOINT);
+ if (!sol_map->empty)
+ goto error;
+ }
+
+ isl_basic_set_free(dom);
+ return &sol_map->sol;
+error:
+ isl_basic_set_free(dom);
+ sol_free(&sol_map->sol);
+ return NULL;
+}
+
+/* Check whether all coefficients of (non-parameter) variables
+ * are non-positive, meaning that no pivots can be performed on the row.
+ */
+static int is_critical(struct isl_tab *tab, int row)
+{
+ int j;
+ unsigned off = 2 + tab->M;
+
+ for (j = tab->n_dead; j < tab->n_col; ++j) {
+ if (col_is_parameter_var(tab, j))
+ continue;
+
+ if (isl_int_is_pos(tab->mat->row[row][off + j]))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Check whether the inequality represented by vec is strict over the integers,
+ * i.e., there are no integer values satisfying the constraint with
+ * equality. This happens if the gcd of the coefficients is not a divisor
+ * of the constant term. If so, scale the constraint down by the gcd
+ * of the coefficients.
+ */
+static int is_strict(struct isl_vec *vec)
+{
+ isl_int gcd;
+ int strict = 0;
+
+ isl_int_init(gcd);
+ isl_seq_gcd(vec->el + 1, vec->size - 1, &gcd);
+ if (!isl_int_is_one(gcd)) {
+ strict = !isl_int_is_divisible_by(vec->el[0], gcd);
+ isl_int_fdiv_q(vec->el[0], vec->el[0], gcd);
+ isl_seq_scale_down(vec->el + 1, vec->el + 1, gcd, vec->size-1);
+ }
+ isl_int_clear(gcd);
+
+ return strict;
+}
+
+/* Determine the sign of the given row of the main tableau.
+ * The result is one of
+ * isl_tab_row_pos: always non-negative; no pivot needed
+ * isl_tab_row_neg: always non-positive; pivot
+ * isl_tab_row_any: can be both positive and negative; split
+ *
+ * We first handle some simple cases
+ * - the row sign may be known already
+ * - the row may be obviously non-negative
+ * - the parametric constant may be equal to that of another row
+ * for which we know the sign. This sign will be either "pos" or
+ * "any". If it had been "neg" then we would have pivoted before.
+ *
+ * If none of these cases hold, we check the value of the row for each
+ * of the currently active samples. Based on the signs of these values
+ * we make an initial determination of the sign of the row.
+ *
+ * all zero -> unk(nown)
+ * all non-negative -> pos
+ * all non-positive -> neg
+ * both negative and positive -> all
+ *
+ * If we end up with "all", we are done.
+ * Otherwise, we perform a check for positive and/or negative
+ * values as follows.
+ *
+ * samples neg unk pos
+ * <0 ? Y N Y N
+ * pos any pos
+ * >0 ? Y N Y N
+ * any neg any neg
+ *
+ * There is no special sign for "zero", because we can usually treat zero
+ * as either non-negative or non-positive, whatever works out best.
+ * However, if the row is "critical", meaning that pivoting is impossible
+ * then we don't want to limp zero with the non-positive case, because
+ * then we we would lose the solution for those values of the parameters
+ * where the value of the row is zero. Instead, we treat 0 as non-negative
+ * ensuring a split if the row can attain both zero and negative values.
+ * The same happens when the original constraint was one that could not
+ * be satisfied with equality by any integer values of the parameters.
+ * In this case, we normalize the constraint, but then a value of zero
+ * for the normalized constraint is actually a positive value for the
+ * original constraint, so again we need to treat zero as non-negative.
+ * In both these cases, we have the following decision tree instead:
+ *
+ * all non-negative -> pos
+ * all negative -> neg
+ * both negative and non-negative -> all
+ *
+ * samples neg pos
+ * <0 ? Y N
+ * any pos
+ * >=0 ? Y N
+ * any neg
+ */
+static enum isl_tab_row_sign row_sign(struct isl_tab *tab,
+ struct isl_sol *sol, int row)
+{
+ struct isl_vec *ineq = NULL;
+ enum isl_tab_row_sign res = isl_tab_row_unknown;
+ int critical;
+ int strict;
+ int row2;
+
+ if (tab->row_sign[row] != isl_tab_row_unknown)
+ return tab->row_sign[row];
+ if (is_obviously_nonneg(tab, row))
+ return isl_tab_row_pos;
+ for (row2 = tab->n_redundant; row2 < tab->n_row; ++row2) {
+ if (tab->row_sign[row2] == isl_tab_row_unknown)
+ continue;
+ if (identical_parameter_line(tab, row, row2))
+ return tab->row_sign[row2];
+ }
+
+ critical = is_critical(tab, row);
+
+ ineq = get_row_parameter_ineq(tab, row);
+ if (!ineq)
+ goto error;
+
+ strict = is_strict(ineq);
+
+ res = sol->context->op->ineq_sign(sol->context, ineq->el,
+ critical || strict);
+
+ if (res == isl_tab_row_unknown || res == isl_tab_row_pos) {
+ /* test for negative values */
+ int feasible;
+ isl_seq_neg(ineq->el, ineq->el, ineq->size);
+ isl_int_sub_ui(ineq->el[0], ineq->el[0], 1);
+
+ feasible = sol->context->op->test_ineq(sol->context, ineq->el);
+ if (feasible < 0)
+ goto error;
+ if (!feasible)
+ res = isl_tab_row_pos;
+ else
+ res = (res == isl_tab_row_unknown) ? isl_tab_row_neg
+ : isl_tab_row_any;
+ if (res == isl_tab_row_neg) {
+ isl_seq_neg(ineq->el, ineq->el, ineq->size);
+ isl_int_sub_ui(ineq->el[0], ineq->el[0], 1);
+ }
+ }
+
+ if (res == isl_tab_row_neg) {
+ /* test for positive values */
+ int feasible;
+ if (!critical && !strict)
+ isl_int_sub_ui(ineq->el[0], ineq->el[0], 1);
+
+ feasible = sol->context->op->test_ineq(sol->context, ineq->el);
+ if (feasible < 0)
+ goto error;
+ if (feasible)
+ res = isl_tab_row_any;
+ }
+
+ isl_vec_free(ineq);
+ return res;
+error:
+ isl_vec_free(ineq);
+ return isl_tab_row_unknown;
+}
+
+static void find_solutions(struct isl_sol *sol, struct isl_tab *tab);
+
+/* Find solutions for values of the parameters that satisfy the given
+ * inequality.
+ *
+ * We currently take a snapshot of the context tableau that is reset
+ * when we return from this function, while we make a copy of the main
+ * tableau, leaving the original main tableau untouched.
+ * These are fairly arbitrary choices. Making a copy also of the context
+ * tableau would obviate the need to undo any changes made to it later,
+ * while taking a snapshot of the main tableau could reduce memory usage.
+ * If we were to switch to taking a snapshot of the main tableau,
+ * we would have to keep in mind that we need to save the row signs
+ * and that we need to do this before saving the current basis
+ * such that the basis has been restore before we restore the row signs.
+ */
+static void find_in_pos(struct isl_sol *sol, struct isl_tab *tab, isl_int *ineq)
+{
+ void *saved;
+
+ if (!sol->context)
+ goto error;
+ saved = sol->context->op->save(sol->context);
+
+ tab = isl_tab_dup(tab);
+ if (!tab)
+ goto error;
+
+ sol->context->op->add_ineq(sol->context, ineq, 0, 1);
+
+ find_solutions(sol, tab);
+
+ if (!sol->error)
+ sol->context->op->restore(sol->context, saved);
+ else
+ sol->context->op->discard(saved);
+ return;
+error:
+ sol->error = 1;
+}
+
+/* Record the absence of solutions for those values of the parameters
+ * that do not satisfy the given inequality with equality.
+ */
+static void no_sol_in_strict(struct isl_sol *sol,
+ struct isl_tab *tab, struct isl_vec *ineq)
+{
+ int empty;
+ void *saved;
+
+ if (!sol->context || sol->error)
+ goto error;
+ saved = sol->context->op->save(sol->context);
+
+ isl_int_sub_ui(ineq->el[0], ineq->el[0], 1);
+
+ sol->context->op->add_ineq(sol->context, ineq->el, 1, 0);
+ if (!sol->context)
+ goto error;
+
+ empty = tab->empty;
+ tab->empty = 1;
+ sol_add(sol, tab);
+ tab->empty = empty;
+
+ isl_int_add_ui(ineq->el[0], ineq->el[0], 1);
+
+ sol->context->op->restore(sol->context, saved);
+ return;
+error:
+ sol->error = 1;
+}
+
+/* Reset all row variables that are marked to have a sign that may
+ * be both positive and negative to have an unknown sign.
+ */
+static void reset_any_to_unknown(struct isl_tab *tab)
+{
+ int row;
+
+ for (row = tab->n_redundant; row < tab->n_row; ++row) {
+ if (!isl_tab_var_from_row(tab, row)->is_nonneg)
+ continue;
+ if (tab->row_sign[row] == isl_tab_row_any)
+ tab->row_sign[row] = isl_tab_row_unknown;
+ }
+}
+
+/* Compute the lexicographic minimum of the set represented by the main
+ * tableau "tab" within the context "sol->context_tab".
+ * On entry the sample value of the main tableau is lexicographically
+ * less than or equal to this lexicographic minimum.
+ * Pivots are performed until a feasible point is found, which is then
+ * necessarily equal to the minimum, or until the tableau is found to
+ * be infeasible. Some pivots may need to be performed for only some
+ * feasible values of the context tableau. If so, the context tableau
+ * is split into a part where the pivot is needed and a part where it is not.
+ *
+ * Whenever we enter the main loop, the main tableau is such that no
+ * "obvious" pivots need to be performed on it, where "obvious" means
+ * that the given row can be seen to be negative without looking at
+ * the context tableau. In particular, for non-parametric problems,
+ * no pivots need to be performed on the main tableau.
+ * The caller of find_solutions is responsible for making this property
+ * hold prior to the first iteration of the loop, while restore_lexmin
+ * is called before every other iteration.
+ *
+ * Inside the main loop, we first examine the signs of the rows of
+ * the main tableau within the context of the context tableau.
+ * If we find a row that is always non-positive for all values of
+ * the parameters satisfying the context tableau and negative for at
+ * least one value of the parameters, we perform the appropriate pivot
+ * and start over. An exception is the case where no pivot can be
+ * performed on the row. In this case, we require that the sign of
+ * the row is negative for all values of the parameters (rather than just
+ * non-positive). This special case is handled inside row_sign, which
+ * will say that the row can have any sign if it determines that it can
+ * attain both negative and zero values.
+ *
+ * If we can't find a row that always requires a pivot, but we can find
+ * one or more rows that require a pivot for some values of the parameters
+ * (i.e., the row can attain both positive and negative signs), then we split
+ * the context tableau into two parts, one where we force the sign to be
+ * non-negative and one where we force is to be negative.
+ * The non-negative part is handled by a recursive call (through find_in_pos).
+ * Upon returning from this call, we continue with the negative part and
+ * perform the required pivot.
+ *
+ * If no such rows can be found, all rows are non-negative and we have
+ * found a (rational) feasible point. If we only wanted a rational point
+ * then we are done.
+ * Otherwise, we check if all values of the sample point of the tableau
+ * are integral for the variables. If so, we have found the minimal
+ * integral point and we are done.
+ * If the sample point is not integral, then we need to make a distinction
+ * based on whether the constant term is non-integral or the coefficients
+ * of the parameters. Furthermore, in order to decide how to handle
+ * the non-integrality, we also need to know whether the coefficients
+ * of the other columns in the tableau are integral. This leads
+ * to the following table. The first two rows do not correspond
+ * to a non-integral sample point and are only mentioned for completeness.
+ *
+ * constant parameters other
+ *
+ * int int int |
+ * int int rat | -> no problem
+ *
+ * rat int int -> fail
+ *
+ * rat int rat -> cut
+ *
+ * int rat rat |
+ * rat rat rat | -> parametric cut
+ *
+ * int rat int |
+ * rat rat int | -> split context
+ *
+ * If the parametric constant is completely integral, then there is nothing
+ * to be done. If the constant term is non-integral, but all the other
+ * coefficient are integral, then there is nothing that can be done
+ * and the tableau has no integral solution.
+ * If, on the other hand, one or more of the other columns have rational
+ * coefficients, but the parameter coefficients are all integral, then
+ * we can perform a regular (non-parametric) cut.
+ * Finally, if there is any parameter coefficient that is non-integral,
+ * then we need to involve the context tableau. There are two cases here.
+ * If at least one other column has a rational coefficient, then we
+ * can perform a parametric cut in the main tableau by adding a new
+ * integer division in the context tableau.
+ * If all other columns have integral coefficients, then we need to
+ * enforce that the rational combination of parameters (c + \sum a_i y_i)/m
+ * is always integral. We do this by introducing an integer division
+ * q = floor((c + \sum a_i y_i)/m) and stipulating that its argument should
+ * always be integral in the context tableau, i.e., m q = c + \sum a_i y_i.
+ * Since q is expressed in the tableau as
+ * c + \sum a_i y_i - m q >= 0
+ * -c - \sum a_i y_i + m q + m - 1 >= 0
+ * it is sufficient to add the inequality
+ * -c - \sum a_i y_i + m q >= 0
+ * In the part of the context where this inequality does not hold, the
+ * main tableau is marked as being empty.
+ */
+static void find_solutions(struct isl_sol *sol, struct isl_tab *tab)
+{
+ struct isl_context *context;
+ int r;
+
+ if (!tab || sol->error)
+ goto error;
+
+ context = sol->context;
+
+ if (tab->empty)
+ goto done;
+ if (context->op->is_empty(context))
+ goto done;
+
+ for (r = 0; r >= 0 && tab && !tab->empty; r = restore_lexmin(tab)) {
+ int flags;
+ int row;
+ enum isl_tab_row_sign sgn;
+ int split = -1;
+ int n_split = 0;
+
+ for (row = tab->n_redundant; row < tab->n_row; ++row) {
+ if (!isl_tab_var_from_row(tab, row)->is_nonneg)
+ continue;
+ sgn = row_sign(tab, sol, row);
+ if (!sgn)
+ goto error;
+ tab->row_sign[row] = sgn;
+ if (sgn == isl_tab_row_any)
+ n_split++;
+ if (sgn == isl_tab_row_any && split == -1)
+ split = row;
+ if (sgn == isl_tab_row_neg)
+ break;
+ }
+ if (row < tab->n_row)
+ continue;
+ if (split != -1) {
+ struct isl_vec *ineq;
+ if (n_split != 1)
+ split = context->op->best_split(context, tab);
+ if (split < 0)
+ goto error;
+ ineq = get_row_parameter_ineq(tab, split);
+ if (!ineq)
+ goto error;
+ is_strict(ineq);
+ reset_any_to_unknown(tab);
+ tab->row_sign[split] = isl_tab_row_pos;
+ sol_inc_level(sol);
+ find_in_pos(sol, tab, ineq->el);
+ tab->row_sign[split] = isl_tab_row_neg;
+ isl_seq_neg(ineq->el, ineq->el, ineq->size);
+ isl_int_sub_ui(ineq->el[0], ineq->el[0], 1);
+ if (!sol->error)
+ context->op->add_ineq(context, ineq->el, 0, 1);
+ isl_vec_free(ineq);
+ if (sol->error)
+ goto error;
+ continue;
+ }
+ if (tab->rational)
+ break;
+ row = first_non_integer_row(tab, &flags);
+ if (row < 0)
+ break;
+ if (ISL_FL_ISSET(flags, I_PAR)) {
+ if (ISL_FL_ISSET(flags, I_VAR)) {
+ if (isl_tab_mark_empty(tab) < 0)
+ goto error;
+ break;
+ }
+ row = add_cut(tab, row);
+ } else if (ISL_FL_ISSET(flags, I_VAR)) {
+ struct isl_vec *div;
+ struct isl_vec *ineq;
+ int d;
+ div = get_row_split_div(tab, row);
+ if (!div)
+ goto error;
+ d = context->op->get_div(context, tab, div);
+ isl_vec_free(div);
+ if (d < 0)
+ goto error;
+ ineq = ineq_for_div(context->op->peek_basic_set(context), d);
+ if (!ineq)
+ goto error;
+ sol_inc_level(sol);
+ no_sol_in_strict(sol, tab, ineq);
+ isl_seq_neg(ineq->el, ineq->el, ineq->size);
+ context->op->add_ineq(context, ineq->el, 1, 1);
+ isl_vec_free(ineq);
+ if (sol->error || !context->op->is_ok(context))
+ goto error;
+ tab = set_row_cst_to_div(tab, row, d);
+ if (context->op->is_empty(context))
+ break;
+ } else
+ row = add_parametric_cut(tab, row, context);
+ if (row < 0)
+ goto error;
+ }
+ if (r < 0)
+ goto error;
+done:
+ sol_add(sol, tab);
+ isl_tab_free(tab);
+ return;
+error:
+ isl_tab_free(tab);
+ sol->error = 1;
+}
+
+/* Does "sol" contain a pair of partial solutions that could potentially
+ * be merged?
+ *
+ * We currently only check that "sol" is not in an error state
+ * and that there are at least two partial solutions of which the final two
+ * are defined at the same level.
+ */
+static int sol_has_mergeable_solutions(struct isl_sol *sol)
+{
+ if (sol->error)
+ return 0;
+ if (!sol->partial)
+ return 0;
+ if (!sol->partial->next)
+ return 0;
+ return sol->partial->level == sol->partial->next->level;
+}
+
+/* Compute the lexicographic minimum of the set represented by the main
+ * tableau "tab" within the context "sol->context_tab".
+ *
+ * As a preprocessing step, we first transfer all the purely parametric
+ * equalities from the main tableau to the context tableau, i.e.,
+ * parameters that have been pivoted to a row.
+ * These equalities are ignored by the main algorithm, because the
+ * corresponding rows may not be marked as being non-negative.
+ * In parts of the context where the added equality does not hold,
+ * the main tableau is marked as being empty.
+ *
+ * Before we embark on the actual computation, we save a copy
+ * of the context. When we return, we check if there are any
+ * partial solutions that can potentially be merged. If so,
+ * we perform a rollback to the initial state of the context.
+ * The merging of partial solutions happens inside calls to
+ * sol_dec_level that are pushed onto the undo stack of the context.
+ * If there are no partial solutions that can potentially be merged
+ * then the rollback is skipped as it would just be wasted effort.
+ */
+static void find_solutions_main(struct isl_sol *sol, struct isl_tab *tab)
+{
+ int row;
+ void *saved;
+
+ if (!tab)
+ goto error;
+
+ sol->level = 0;
+
+ for (row = tab->n_redundant; row < tab->n_row; ++row) {
+ int p;
+ struct isl_vec *eq;
+
+ if (!row_is_parameter_var(tab, row))
+ continue;
+ if (tab->row_var[row] < tab->n_param)
+ p = tab->row_var[row];
+ else
+ p = tab->row_var[row]
+ + tab->n_param - (tab->n_var - tab->n_div);
+
+ eq = isl_vec_alloc(tab->mat->ctx, 1+tab->n_param+tab->n_div);
+ if (!eq)
+ goto error;
+ get_row_parameter_line(tab, row, eq->el);
+ isl_int_neg(eq->el[1 + p], tab->mat->row[row][0]);
+ eq = isl_vec_normalize(eq);
+
+ sol_inc_level(sol);
+ no_sol_in_strict(sol, tab, eq);
+
+ isl_seq_neg(eq->el, eq->el, eq->size);
+ sol_inc_level(sol);
+ no_sol_in_strict(sol, tab, eq);
+ isl_seq_neg(eq->el, eq->el, eq->size);
+
+ sol->context->op->add_eq(sol->context, eq->el, 1, 1);
+
+ isl_vec_free(eq);
+
+ if (isl_tab_mark_redundant(tab, row) < 0)
+ goto error;
+
+ if (sol->context->op->is_empty(sol->context))
+ break;
+
+ row = tab->n_redundant - 1;
+ }
+
+ saved = sol->context->op->save(sol->context);
+
+ find_solutions(sol, tab);
+
+ if (sol_has_mergeable_solutions(sol))
+ sol->context->op->restore(sol->context, saved);
+ else
+ sol->context->op->discard(saved);
+
+ sol->level = 0;
+ sol_pop(sol);
+
+ return;
+error:
+ isl_tab_free(tab);
+ sol->error = 1;
+}
+
+/* Check if integer division "div" of "dom" also occurs in "bmap".
+ * If so, return its position within the divs.
+ * Otherwise, return a position beyond the integer divisions.
+ */
+static int find_context_div(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_basic_set *dom, unsigned div)
+{
+ int i;
+ isl_size b_v_div, d_v_div;
+ isl_size n_div;
+
+ b_v_div = isl_basic_map_var_offset(bmap, isl_dim_div);
+ d_v_div = isl_basic_set_var_offset(dom, isl_dim_div);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (b_v_div < 0 || d_v_div < 0 || n_div < 0)
+ return -1;
+
+ if (isl_int_is_zero(dom->div[div][0]))
+ return n_div;
+ if (isl_seq_first_non_zero(dom->div[div] + 2 + d_v_div,
+ dom->n_div) != -1)
+ return n_div;
+
+ for (i = 0; i < n_div; ++i) {
+ if (isl_int_is_zero(bmap->div[i][0]))
+ continue;
+ if (isl_seq_first_non_zero(bmap->div[i] + 2 + d_v_div,
+ (b_v_div - d_v_div) + n_div) != -1)
+ continue;
+ if (isl_seq_eq(bmap->div[i], dom->div[div], 2 + d_v_div))
+ return i;
+ }
+ return n_div;
+}
+
+/* The correspondence between the variables in the main tableau,
+ * the context tableau, and the input map and domain is as follows.
+ * The first n_param and the last n_div variables of the main tableau
+ * form the variables of the context tableau.
+ * In the basic map, these n_param variables correspond to the
+ * parameters and the input dimensions. In the domain, they correspond
+ * to the parameters and the set dimensions.
+ * The n_div variables correspond to the integer divisions in the domain.
+ * To ensure that everything lines up, we may need to copy some of the
+ * integer divisions of the domain to the map. These have to be placed
+ * in the same order as those in the context and they have to be placed
+ * after any other integer divisions that the map may have.
+ * This function performs the required reordering.
+ */
+static __isl_give isl_basic_map *align_context_divs(
+ __isl_take isl_basic_map *bmap, __isl_keep isl_basic_set *dom)
+{
+ int i;
+ int common = 0;
+ int other;
+ unsigned bmap_n_div;
+
+ bmap_n_div = isl_basic_map_dim(bmap, isl_dim_div);
+
+ for (i = 0; i < dom->n_div; ++i) {
+ int pos;
+
+ pos = find_context_div(bmap, dom, i);
+ if (pos < 0)
+ return isl_basic_map_free(bmap);
+ if (pos < bmap_n_div)
+ common++;
+ }
+ other = bmap_n_div - common;
+ if (dom->n_div - common > 0) {
+ bmap = isl_basic_map_cow(bmap);
+ bmap = isl_basic_map_extend(bmap, dom->n_div - common, 0, 0);
+ if (!bmap)
+ return NULL;
+ }
+ for (i = 0; i < dom->n_div; ++i) {
+ int pos = find_context_div(bmap, dom, i);
+ if (pos < 0)
+ bmap = isl_basic_map_free(bmap);
+ if (pos >= bmap_n_div) {
+ pos = isl_basic_map_alloc_div(bmap);
+ if (pos < 0)
+ goto error;
+ isl_int_set_si(bmap->div[pos][0], 0);
+ bmap_n_div++;
+ }
+ if (pos != other + i)
+ bmap = isl_basic_map_swap_div(bmap, pos, other + i);
+ }
+ return bmap;
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Base case of isl_tab_basic_map_partial_lexopt, after removing
+ * some obvious symmetries.
+ *
+ * We make sure the divs in the domain are properly ordered,
+ * because they will be added one by one in the given order
+ * during the construction of the solution map.
+ * Furthermore, make sure that the known integer divisions
+ * appear before any unknown integer division because the solution
+ * may depend on the known integer divisions, while anything that
+ * depends on any variable starting from the first unknown integer
+ * division is ignored in sol_pma_add.
+ */
+static struct isl_sol *basic_map_partial_lexopt_base_sol(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, int max,
+ struct isl_sol *(*init)(__isl_keep isl_basic_map *bmap,
+ __isl_take isl_basic_set *dom, int track_empty, int max))
+{
+ struct isl_tab *tab;
+ struct isl_sol *sol = NULL;
+ struct isl_context *context;
+
+ if (dom->n_div) {
+ dom = isl_basic_set_sort_divs(dom);
+ bmap = align_context_divs(bmap, dom);
+ }
+ sol = init(bmap, dom, !!empty, max);
+ if (!sol)
+ goto error;
+
+ context = sol->context;
+ if (isl_basic_set_plain_is_empty(context->op->peek_basic_set(context)))
+ /* nothing */;
+ else if (isl_basic_map_plain_is_empty(bmap)) {
+ if (sol->add_empty)
+ sol->add_empty(sol,
+ isl_basic_set_copy(context->op->peek_basic_set(context)));
+ } else {
+ tab = tab_for_lexmin(bmap,
+ context->op->peek_basic_set(context), 1, max);
+ tab = context->op->detect_nonnegative_parameters(context, tab);
+ find_solutions_main(sol, tab);
+ }
+ if (sol->error)
+ goto error;
+
+ isl_basic_map_free(bmap);
+ return sol;
+error:
+ sol_free(sol);
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Base case of isl_tab_basic_map_partial_lexopt, after removing
+ * some obvious symmetries.
+ *
+ * We call basic_map_partial_lexopt_base_sol and extract the results.
+ */
+static __isl_give isl_map *basic_map_partial_lexopt_base(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, int max)
+{
+ isl_map *result = NULL;
+ struct isl_sol *sol;
+ struct isl_sol_map *sol_map;
+
+ sol = basic_map_partial_lexopt_base_sol(bmap, dom, empty, max,
+ &sol_map_init);
+ if (!sol)
+ return NULL;
+ sol_map = (struct isl_sol_map *) sol;
+
+ result = isl_map_copy(sol_map->map);
+ if (empty)
+ *empty = isl_set_copy(sol_map->empty);
+ sol_free(&sol_map->sol);
+ return result;
+}
+
+/* Return a count of the number of occurrences of the "n" first
+ * variables in the inequality constraints of "bmap".
+ */
+static __isl_give int *count_occurrences(__isl_keep isl_basic_map *bmap,
+ int n)
+{
+ int i, j;
+ isl_ctx *ctx;
+ int *occurrences;
+
+ if (!bmap)
+ return NULL;
+ ctx = isl_basic_map_get_ctx(bmap);
+ occurrences = isl_calloc_array(ctx, int, n);
+ if (!occurrences)
+ return NULL;
+
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ for (j = 0; j < n; ++j) {
+ if (!isl_int_is_zero(bmap->ineq[i][1 + j]))
+ occurrences[j]++;
+ }
+ }
+
+ return occurrences;
+}
+
+/* Do all of the "n" variables with non-zero coefficients in "c"
+ * occur in exactly a single constraint.
+ * "occurrences" is an array of length "n" containing the number
+ * of occurrences of each of the variables in the inequality constraints.
+ */
+static int single_occurrence(int n, isl_int *c, int *occurrences)
+{
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ if (isl_int_is_zero(c[i]))
+ continue;
+ if (occurrences[i] != 1)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Do all of the "n" initial variables that occur in inequality constraint
+ * "ineq" of "bmap" only occur in that constraint?
+ */
+static int all_single_occurrence(__isl_keep isl_basic_map *bmap, int ineq,
+ int n)
+{
+ int i, j;
+
+ for (i = 0; i < n; ++i) {
+ if (isl_int_is_zero(bmap->ineq[ineq][1 + i]))
+ continue;
+ for (j = 0; j < bmap->n_ineq; ++j) {
+ if (j == ineq)
+ continue;
+ if (!isl_int_is_zero(bmap->ineq[j][1 + i]))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Structure used during detection of parallel constraints.
+ * n_in: number of "input" variables: isl_dim_param + isl_dim_in
+ * n_out: number of "output" variables: isl_dim_out + isl_dim_div
+ * val: the coefficients of the output variables
+ */
+struct isl_constraint_equal_info {
+ unsigned n_in;
+ unsigned n_out;
+ isl_int *val;
+};
+
+/* Check whether the coefficients of the output variables
+ * of the constraint in "entry" are equal to info->val.
+ */
+static isl_bool constraint_equal(const void *entry, const void *val)
+{
+ isl_int **row = (isl_int **)entry;
+ const struct isl_constraint_equal_info *info = val;
+ int eq;
+
+ eq = isl_seq_eq((*row) + 1 + info->n_in, info->val, info->n_out);
+ return isl_bool_ok(eq);
+}
+
+/* Check whether "bmap" has a pair of constraints that have
+ * the same coefficients for the output variables.
+ * Note that the coefficients of the existentially quantified
+ * variables need to be zero since the existentially quantified
+ * of the result are usually not the same as those of the input.
+ * Furthermore, check that each of the input variables that occur
+ * in those constraints does not occur in any other constraint.
+ * If so, return true and return the row indices of the two constraints
+ * in *first and *second.
+ */
+static isl_bool parallel_constraints(__isl_keep isl_basic_map *bmap,
+ int *first, int *second)
+{
+ int i;
+ isl_ctx *ctx;
+ int *occurrences = NULL;
+ struct isl_hash_table *table = NULL;
+ struct isl_hash_table_entry *entry;
+ struct isl_constraint_equal_info info;
+ isl_size nparam, n_in, n_out, n_div;
+
+ ctx = isl_basic_map_get_ctx(bmap);
+ table = isl_hash_table_alloc(ctx, bmap->n_ineq);
+ if (!table)
+ goto error;
+
+ nparam = isl_basic_map_dim(bmap, isl_dim_param);
+ n_in = isl_basic_map_dim(bmap, isl_dim_in);
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ if (nparam < 0 || n_in < 0 || n_out < 0 || n_div < 0)
+ goto error;
+ info.n_in = nparam + n_in;
+ occurrences = count_occurrences(bmap, info.n_in);
+ if (info.n_in && !occurrences)
+ goto error;
+ info.n_out = n_out + n_div;
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ uint32_t hash;
+
+ info.val = bmap->ineq[i] + 1 + info.n_in;
+ if (isl_seq_first_non_zero(info.val, n_out) < 0)
+ continue;
+ if (isl_seq_first_non_zero(info.val + n_out, n_div) >= 0)
+ continue;
+ if (!single_occurrence(info.n_in, bmap->ineq[i] + 1,
+ occurrences))
+ continue;
+ hash = isl_seq_get_hash(info.val, info.n_out);
+ entry = isl_hash_table_find(ctx, table, hash,
+ constraint_equal, &info, 1);
+ if (!entry)
+ goto error;
+ if (entry->data)
+ break;
+ entry->data = &bmap->ineq[i];
+ }
+
+ if (i < bmap->n_ineq) {
+ *first = ((isl_int **)entry->data) - bmap->ineq;
+ *second = i;
+ }
+
+ isl_hash_table_free(ctx, table);
+ free(occurrences);
+
+ return isl_bool_ok(i < bmap->n_ineq);
+error:
+ isl_hash_table_free(ctx, table);
+ free(occurrences);
+ return isl_bool_error;
+}
+
+/* Given a set of upper bounds in "var", add constraints to "bset"
+ * that make the i-th bound smallest.
+ *
+ * In particular, if there are n bounds b_i, then add the constraints
+ *
+ * b_i <= b_j for j > i
+ * b_i < b_j for j < i
+ */
+static __isl_give isl_basic_set *select_minimum(__isl_take isl_basic_set *bset,
+ __isl_keep isl_mat *var, int i)
+{
+ isl_ctx *ctx;
+ int j, k;
+
+ ctx = isl_mat_get_ctx(var);
+
+ for (j = 0; j < var->n_row; ++j) {
+ if (j == i)
+ continue;
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_combine(bset->ineq[k], ctx->one, var->row[j],
+ ctx->negone, var->row[i], var->n_col);
+ isl_int_set_si(bset->ineq[k][var->n_col], 0);
+ if (j < i)
+ isl_int_sub_ui(bset->ineq[k][0], bset->ineq[k][0], 1);
+ }
+
+ bset = isl_basic_set_finalize(bset);
+
+ return bset;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Given a set of upper bounds on the last "input" variable m,
+ * construct a set that assigns the minimal upper bound to m, i.e.,
+ * construct a set that divides the space into cells where one
+ * of the upper bounds is smaller than all the others and assign
+ * this upper bound to m.
+ *
+ * In particular, if there are n bounds b_i, then the result
+ * consists of n basic sets, each one of the form
+ *
+ * m = b_i
+ * b_i <= b_j for j > i
+ * b_i < b_j for j < i
+ */
+static __isl_give isl_set *set_minimum(__isl_take isl_space *space,
+ __isl_take isl_mat *var)
+{
+ int i, k;
+ isl_basic_set *bset = NULL;
+ isl_set *set = NULL;
+
+ if (!space || !var)
+ goto error;
+
+ set = isl_set_alloc_space(isl_space_copy(space),
+ var->n_row, ISL_SET_DISJOINT);
+
+ for (i = 0; i < var->n_row; ++i) {
+ bset = isl_basic_set_alloc_space(isl_space_copy(space), 0,
+ 1, var->n_row - 1);
+ k = isl_basic_set_alloc_equality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bset->eq[k], var->row[i], var->n_col);
+ isl_int_set_si(bset->eq[k][var->n_col], -1);
+ bset = select_minimum(bset, var, i);
+ set = isl_set_add_basic_set(set, bset);
+ }
+
+ isl_space_free(space);
+ isl_mat_free(var);
+ return set;
+error:
+ isl_basic_set_free(bset);
+ isl_set_free(set);
+ isl_space_free(space);
+ isl_mat_free(var);
+ return NULL;
+}
+
+/* Given that the last input variable of "bmap" represents the minimum
+ * of the bounds in "cst", check whether we need to split the domain
+ * based on which bound attains the minimum.
+ *
+ * A split is needed when the minimum appears in an integer division
+ * or in an equality. Otherwise, it is only needed if it appears in
+ * an upper bound that is different from the upper bounds on which it
+ * is defined.
+ */
+static isl_bool need_split_basic_map(__isl_keep isl_basic_map *bmap,
+ __isl_keep isl_mat *cst)
+{
+ int i, j;
+ isl_size total;
+ unsigned pos;
+
+ pos = cst->n_col - 1;
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ if (total < 0)
+ return isl_bool_error;
+
+ for (i = 0; i < bmap->n_div; ++i)
+ if (!isl_int_is_zero(bmap->div[i][2 + pos]))
+ return isl_bool_true;
+
+ for (i = 0; i < bmap->n_eq; ++i)
+ if (!isl_int_is_zero(bmap->eq[i][1 + pos]))
+ return isl_bool_true;
+
+ for (i = 0; i < bmap->n_ineq; ++i) {
+ if (isl_int_is_nonneg(bmap->ineq[i][1 + pos]))
+ continue;
+ if (!isl_int_is_negone(bmap->ineq[i][1 + pos]))
+ return isl_bool_true;
+ if (isl_seq_first_non_zero(bmap->ineq[i] + 1 + pos + 1,
+ total - pos - 1) >= 0)
+ return isl_bool_true;
+
+ for (j = 0; j < cst->n_row; ++j)
+ if (isl_seq_eq(bmap->ineq[i], cst->row[j], cst->n_col))
+ break;
+ if (j >= cst->n_row)
+ return isl_bool_true;
+ }
+
+ return isl_bool_false;
+}
+
+/* Given that the last set variable of "bset" represents the minimum
+ * of the bounds in "cst", check whether we need to split the domain
+ * based on which bound attains the minimum.
+ *
+ * We simply call need_split_basic_map here. This is safe because
+ * the position of the minimum is computed from "cst" and not
+ * from "bmap".
+ */
+static isl_bool need_split_basic_set(__isl_keep isl_basic_set *bset,
+ __isl_keep isl_mat *cst)
+{
+ return need_split_basic_map(bset_to_bmap(bset), cst);
+}
+
+/* Given that the last set variable of "set" represents the minimum
+ * of the bounds in "cst", check whether we need to split the domain
+ * based on which bound attains the minimum.
+ */
+static isl_bool need_split_set(__isl_keep isl_set *set, __isl_keep isl_mat *cst)
+{
+ int i;
+
+ for (i = 0; i < set->n; ++i) {
+ isl_bool split;
+
+ split = need_split_basic_set(set->p[i], cst);
+ if (split < 0 || split)
+ return split;
+ }
+
+ return isl_bool_false;
+}
+
+/* Given a map of which the last input variable is the minimum
+ * of the bounds in "cst", split each basic set in the set
+ * in pieces where one of the bounds is (strictly) smaller than the others.
+ * This subdivision is given in "min_expr".
+ * The variable is subsequently projected out.
+ *
+ * We only do the split when it is needed.
+ * For example if the last input variable m = min(a,b) and the only
+ * constraints in the given basic set are lower bounds on m,
+ * i.e., l <= m = min(a,b), then we can simply project out m
+ * to obtain l <= a and l <= b, without having to split on whether
+ * m is equal to a or b.
+ */
+static __isl_give isl_map *split_domain(__isl_take isl_map *opt,
+ __isl_take isl_set *min_expr, __isl_take isl_mat *cst)
+{
+ isl_size n_in;
+ int i;
+ isl_space *space;
+ isl_map *res;
+
+ n_in = isl_map_dim(opt, isl_dim_in);
+ if (n_in < 0 || !min_expr || !cst)
+ goto error;
+
+ space = isl_map_get_space(opt);
+ space = isl_space_drop_dims(space, isl_dim_in, n_in - 1, 1);
+ res = isl_map_empty(space);
+
+ for (i = 0; i < opt->n; ++i) {
+ isl_map *map;
+ isl_bool split;
+
+ map = isl_map_from_basic_map(isl_basic_map_copy(opt->p[i]));
+ split = need_split_basic_map(opt->p[i], cst);
+ if (split < 0)
+ map = isl_map_free(map);
+ else if (split)
+ map = isl_map_intersect_domain(map,
+ isl_set_copy(min_expr));
+ map = isl_map_remove_dims(map, isl_dim_in, n_in - 1, 1);
+
+ res = isl_map_union_disjoint(res, map);
+ }
+
+ isl_map_free(opt);
+ isl_set_free(min_expr);
+ isl_mat_free(cst);
+ return res;
+error:
+ isl_map_free(opt);
+ isl_set_free(min_expr);
+ isl_mat_free(cst);
+ return NULL;
+}
+
+/* Given a set of which the last set variable is the minimum
+ * of the bounds in "cst", split each basic set in the set
+ * in pieces where one of the bounds is (strictly) smaller than the others.
+ * This subdivision is given in "min_expr".
+ * The variable is subsequently projected out.
+ */
+static __isl_give isl_set *split(__isl_take isl_set *empty,
+ __isl_take isl_set *min_expr, __isl_take isl_mat *cst)
+{
+ isl_map *map;
+
+ map = isl_map_from_domain(empty);
+ map = split_domain(map, min_expr, cst);
+ empty = isl_map_domain(map);
+
+ return empty;
+}
+
+static __isl_give isl_map *basic_map_partial_lexopt(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, int max);
+
+/* This function is called from basic_map_partial_lexopt_symm.
+ * The last variable of "bmap" and "dom" corresponds to the minimum
+ * of the bounds in "cst". "map_space" is the space of the original
+ * input relation (of basic_map_partial_lexopt_symm) and "set_space"
+ * is the space of the original domain.
+ *
+ * We recursively call basic_map_partial_lexopt and then plug in
+ * the definition of the minimum in the result.
+ */
+static __isl_give isl_map *basic_map_partial_lexopt_symm_core(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, int max, __isl_take isl_mat *cst,
+ __isl_take isl_space *map_space, __isl_take isl_space *set_space)
+{
+ isl_map *opt;
+ isl_set *min_expr;
+
+ min_expr = set_minimum(isl_basic_set_get_space(dom), isl_mat_copy(cst));
+
+ opt = basic_map_partial_lexopt(bmap, dom, empty, max);
+
+ if (empty) {
+ *empty = split(*empty,
+ isl_set_copy(min_expr), isl_mat_copy(cst));
+ *empty = isl_set_reset_space(*empty, set_space);
+ }
+
+ opt = split_domain(opt, min_expr, cst);
+ opt = isl_map_reset_space(opt, map_space);
+
+ return opt;
+}
+
+/* Extract a domain from "bmap" for the purpose of computing
+ * a lexicographic optimum.
+ *
+ * This function is only called when the caller wants to compute a full
+ * lexicographic optimum, i.e., without specifying a domain. In this case,
+ * the caller is not interested in the part of the domain space where
+ * there is no solution and the domain can be initialized to those constraints
+ * of "bmap" that only involve the parameters and the input dimensions.
+ * This relieves the parametric programming engine from detecting those
+ * inequalities and transferring them to the context. More importantly,
+ * it ensures that those inequalities are transferred first and not
+ * intermixed with inequalities that actually split the domain.
+ *
+ * If the caller does not require the absence of existentially quantified
+ * variables in the result (i.e., if ISL_OPT_QE is not set in "flags"),
+ * then the actual domain of "bmap" can be used. This ensures that
+ * the domain does not need to be split at all just to separate out
+ * pieces of the domain that do not have a solution from piece that do.
+ * This domain cannot be used in general because it may involve
+ * (unknown) existentially quantified variables which will then also
+ * appear in the solution.
+ */
+static __isl_give isl_basic_set *extract_domain(__isl_keep isl_basic_map *bmap,
+ unsigned flags)
+{
+ isl_size n_div;
+ isl_size n_out;
+
+ n_div = isl_basic_map_dim(bmap, isl_dim_div);
+ n_out = isl_basic_map_dim(bmap, isl_dim_out);
+ if (n_div < 0 || n_out < 0)
+ return NULL;
+ bmap = isl_basic_map_copy(bmap);
+ if (ISL_FL_ISSET(flags, ISL_OPT_QE)) {
+ bmap = isl_basic_map_drop_constraints_involving_dims(bmap,
+ isl_dim_div, 0, n_div);
+ bmap = isl_basic_map_drop_constraints_involving_dims(bmap,
+ isl_dim_out, 0, n_out);
+ }
+ return isl_basic_map_domain(bmap);
+}
+
+#undef TYPE
+#define TYPE isl_map
+#undef SUFFIX
+#define SUFFIX
+#include "isl_tab_lexopt_templ.c"
+
+/* Extract the subsequence of the sample value of "tab"
+ * starting at "pos" and of length "len".
+ */
+static __isl_give isl_vec *extract_sample_sequence(struct isl_tab *tab,
+ int pos, int len)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_vec *v;
+
+ ctx = isl_tab_get_ctx(tab);
+ v = isl_vec_alloc(ctx, len);
+ if (!v)
+ return NULL;
+ for (i = 0; i < len; ++i) {
+ if (!tab->var[pos + i].is_row) {
+ isl_int_set_si(v->el[i], 0);
+ } else {
+ int row;
+
+ row = tab->var[pos + i].index;
+ isl_int_divexact(v->el[i], tab->mat->row[row][1],
+ tab->mat->row[row][0]);
+ }
+ }
+
+ return v;
+}
+
+/* Check if the sequence of variables starting at "pos"
+ * represents a trivial solution according to "trivial".
+ * That is, is the result of applying "trivial" to this sequence
+ * equal to the zero vector?
+ */
+static isl_bool region_is_trivial(struct isl_tab *tab, int pos,
+ __isl_keep isl_mat *trivial)
+{
+ isl_size n, len;
+ isl_vec *v;
+ isl_bool is_trivial;
+
+ n = isl_mat_rows(trivial);
+ if (n < 0)
+ return isl_bool_error;
+
+ if (n == 0)
+ return isl_bool_false;
+
+ len = isl_mat_cols(trivial);
+ if (len < 0)
+ return isl_bool_error;
+ v = extract_sample_sequence(tab, pos, len);
+ v = isl_mat_vec_product(isl_mat_copy(trivial), v);
+ is_trivial = isl_vec_is_zero(v);
+ isl_vec_free(v);
+
+ return is_trivial;
+}
+
+/* Global internal data for isl_tab_basic_set_non_trivial_lexmin.
+ *
+ * "n_op" is the number of initial coordinates to optimize,
+ * as passed to isl_tab_basic_set_non_trivial_lexmin.
+ * "region" is the "n_region"-sized array of regions passed
+ * to isl_tab_basic_set_non_trivial_lexmin.
+ *
+ * "tab" is the tableau that corresponds to the ILP problem.
+ * "local" is an array of local data structure, one for each
+ * (potential) level of the backtracking procedure of
+ * isl_tab_basic_set_non_trivial_lexmin.
+ * "v" is a pre-allocated vector that can be used for adding
+ * constraints to the tableau.
+ *
+ * "sol" contains the best solution found so far.
+ * It is initialized to a vector of size zero.
+ */
+struct isl_lexmin_data {
+ int n_op;
+ int n_region;
+ struct isl_trivial_region *region;
+
+ struct isl_tab *tab;
+ struct isl_local_region *local;
+ isl_vec *v;
+
+ isl_vec *sol;
+};
+
+/* Return the index of the first trivial region, "n_region" if all regions
+ * are non-trivial or -1 in case of error.
+ */
+static int first_trivial_region(struct isl_lexmin_data *data)
+{
+ int i;
+
+ for (i = 0; i < data->n_region; ++i) {
+ isl_bool trivial;
+ trivial = region_is_trivial(data->tab, data->region[i].pos,
+ data->region[i].trivial);
+ if (trivial < 0)
+ return -1;
+ if (trivial)
+ return i;
+ }
+
+ return data->n_region;
+}
+
+/* Check if the solution is optimal, i.e., whether the first
+ * n_op entries are zero.
+ */
+static int is_optimal(__isl_keep isl_vec *sol, int n_op)
+{
+ int i;
+
+ for (i = 0; i < n_op; ++i)
+ if (!isl_int_is_zero(sol->el[1 + i]))
+ return 0;
+ return 1;
+}
+
+/* Add constraints to "tab" that ensure that any solution is significantly
+ * better than that represented by "sol". That is, find the first
+ * relevant (within first n_op) non-zero coefficient and force it (along
+ * with all previous coefficients) to be zero.
+ * If the solution is already optimal (all relevant coefficients are zero),
+ * then just mark the table as empty.
+ * "n_zero" is the number of coefficients that have been forced zero
+ * by previous calls to this function at the same level.
+ * Return the updated number of forced zero coefficients or -1 on error.
+ *
+ * This function assumes that at least 2 * (n_op - n_zero) more rows and
+ * at least 2 * (n_op - n_zero) more elements in the constraint array
+ * are available in the tableau.
+ */
+static int force_better_solution(struct isl_tab *tab,
+ __isl_keep isl_vec *sol, int n_op, int n_zero)
+{
+ int i, n;
+ isl_ctx *ctx;
+ isl_vec *v = NULL;
+
+ if (!sol)
+ return -1;
+
+ for (i = n_zero; i < n_op; ++i)
+ if (!isl_int_is_zero(sol->el[1 + i]))
+ break;
+
+ if (i == n_op) {
+ if (isl_tab_mark_empty(tab) < 0)
+ return -1;
+ return n_op;
+ }
+
+ ctx = isl_vec_get_ctx(sol);
+ v = isl_vec_alloc(ctx, 1 + tab->n_var);
+ if (!v)
+ return -1;
+
+ n = i + 1;
+ for (; i >= n_zero; --i) {
+ v = isl_vec_clr(v);
+ isl_int_set_si(v->el[1 + i], -1);
+ if (add_lexmin_eq(tab, v->el) < 0)
+ goto error;
+ }
+
+ isl_vec_free(v);
+ return n;
+error:
+ isl_vec_free(v);
+ return -1;
+}
+
+/* Fix triviality direction "dir" of the given region to zero.
+ *
+ * This function assumes that at least two more rows and at least
+ * two more elements in the constraint array are available in the tableau.
+ */
+static isl_stat fix_zero(struct isl_tab *tab, struct isl_trivial_region *region,
+ int dir, struct isl_lexmin_data *data)
+{
+ isl_size len;
+
+ data->v = isl_vec_clr(data->v);
+ if (!data->v)
+ return isl_stat_error;
+ len = isl_mat_cols(region->trivial);
+ if (len < 0)
+ return isl_stat_error;
+ isl_seq_cpy(data->v->el + 1 + region->pos, region->trivial->row[dir],
+ len);
+ if (add_lexmin_eq(tab, data->v->el) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* This function selects case "side" for non-triviality region "region",
+ * assuming all the equality constraints have been imposed already.
+ * In particular, the triviality direction side/2 is made positive
+ * if side is even and made negative if side is odd.
+ *
+ * This function assumes that at least one more row and at least
+ * one more element in the constraint array are available in the tableau.
+ */
+static struct isl_tab *pos_neg(struct isl_tab *tab,
+ struct isl_trivial_region *region,
+ int side, struct isl_lexmin_data *data)
+{
+ isl_size len;
+
+ data->v = isl_vec_clr(data->v);
+ if (!data->v)
+ goto error;
+ isl_int_set_si(data->v->el[0], -1);
+ len = isl_mat_cols(region->trivial);
+ if (len < 0)
+ goto error;
+ if (side % 2 == 0)
+ isl_seq_cpy(data->v->el + 1 + region->pos,
+ region->trivial->row[side / 2], len);
+ else
+ isl_seq_neg(data->v->el + 1 + region->pos,
+ region->trivial->row[side / 2], len);
+ return add_lexmin_ineq(tab, data->v->el);
+error:
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Local data at each level of the backtracking procedure of
+ * isl_tab_basic_set_non_trivial_lexmin.
+ *
+ * "update" is set if a solution has been found in the current case
+ * of this level, such that a better solution needs to be enforced
+ * in the next case.
+ * "n_zero" is the number of initial coordinates that have already
+ * been forced to be zero at this level.
+ * "region" is the non-triviality region considered at this level.
+ * "side" is the index of the current case at this level.
+ * "n" is the number of triviality directions.
+ * "snap" is a snapshot of the tableau holding a state that needs
+ * to be satisfied by all subsequent cases.
+ */
+struct isl_local_region {
+ int update;
+ int n_zero;
+ int region;
+ int side;
+ int n;
+ struct isl_tab_undo *snap;
+};
+
+/* Initialize the global data structure "data" used while solving
+ * the ILP problem "bset".
+ */
+static isl_stat init_lexmin_data(struct isl_lexmin_data *data,
+ __isl_keep isl_basic_set *bset)
+{
+ isl_ctx *ctx;
+
+ ctx = isl_basic_set_get_ctx(bset);
+
+ data->tab = tab_for_lexmin(bset, NULL, 0, 0);
+ if (!data->tab)
+ return isl_stat_error;
+
+ data->v = isl_vec_alloc(ctx, 1 + data->tab->n_var);
+ if (!data->v)
+ return isl_stat_error;
+ data->local = isl_calloc_array(ctx, struct isl_local_region,
+ data->n_region);
+ if (data->n_region && !data->local)
+ return isl_stat_error;
+
+ data->sol = isl_vec_alloc(ctx, 0);
+
+ return isl_stat_ok;
+}
+
+/* Mark all outer levels as requiring a better solution
+ * in the next cases.
+ */
+static void update_outer_levels(struct isl_lexmin_data *data, int level)
+{
+ int i;
+
+ for (i = 0; i < level; ++i)
+ data->local[i].update = 1;
+}
+
+/* Initialize "local" to refer to region "region" and
+ * to initiate processing at this level.
+ */
+static isl_stat init_local_region(struct isl_local_region *local, int region,
+ struct isl_lexmin_data *data)
+{
+ isl_size n = isl_mat_rows(data->region[region].trivial);
+
+ if (n < 0)
+ return isl_stat_error;
+ local->n = n;
+ local->region = region;
+ local->side = 0;
+ local->update = 0;
+ local->n_zero = 0;
+
+ return isl_stat_ok;
+}
+
+/* What to do next after entering a level of the backtracking procedure.
+ *
+ * error: some error has occurred; abort
+ * done: an optimal solution has been found; stop search
+ * backtrack: backtrack to the previous level
+ * handle: add the constraints for the current level and
+ * move to the next level
+ */
+enum isl_next {
+ isl_next_error = -1,
+ isl_next_done,
+ isl_next_backtrack,
+ isl_next_handle,
+};
+
+/* Have all cases of the current region been considered?
+ * If there are n directions, then there are 2n cases.
+ *
+ * The constraints in the current tableau are imposed
+ * in all subsequent cases. This means that if the current
+ * tableau is empty, then none of those cases should be considered
+ * anymore and all cases have effectively been considered.
+ */
+static int finished_all_cases(struct isl_local_region *local,
+ struct isl_lexmin_data *data)
+{
+ if (data->tab->empty)
+ return 1;
+ return local->side >= 2 * local->n;
+}
+
+/* Enter level "level" of the backtracking search and figure out
+ * what to do next. "init" is set if the level was entered
+ * from a higher level and needs to be initialized.
+ * Otherwise, the level is entered as a result of backtracking and
+ * the tableau needs to be restored to a position that can
+ * be used for the next case at this level.
+ * The snapshot is assumed to have been saved in the previous case,
+ * before the constraints specific to that case were added.
+ *
+ * In the initialization case, the local region is initialized
+ * to point to the first violated region.
+ * If the constraints of all regions are satisfied by the current
+ * sample of the tableau, then tell the caller to continue looking
+ * for a better solution or to stop searching if an optimal solution
+ * has been found.
+ *
+ * If the tableau is empty or if all cases at the current level
+ * have been considered, then the caller needs to backtrack as well.
+ */
+static enum isl_next enter_level(int level, int init,
+ struct isl_lexmin_data *data)
+{
+ struct isl_local_region *local = &data->local[level];
+
+ if (init) {
+ int r;
+
+ data->tab = cut_to_integer_lexmin(data->tab, CUT_ONE);
+ if (!data->tab)
+ return isl_next_error;
+ if (data->tab->empty)
+ return isl_next_backtrack;
+ r = first_trivial_region(data);
+ if (r < 0)
+ return isl_next_error;
+ if (r == data->n_region) {
+ update_outer_levels(data, level);
+ isl_vec_free(data->sol);
+ data->sol = isl_tab_get_sample_value(data->tab);
+ if (!data->sol)
+ return isl_next_error;
+ if (is_optimal(data->sol, data->n_op))
+ return isl_next_done;
+ return isl_next_backtrack;
+ }
+ if (level >= data->n_region)
+ isl_die(isl_vec_get_ctx(data->v), isl_error_internal,
+ "nesting level too deep",
+ return isl_next_error);
+ if (init_local_region(local, r, data) < 0)
+ return isl_next_error;
+ if (isl_tab_extend_cons(data->tab,
+ 2 * local->n + 2 * data->n_op) < 0)
+ return isl_next_error;
+ } else {
+ if (isl_tab_rollback(data->tab, local->snap) < 0)
+ return isl_next_error;
+ }
+
+ if (finished_all_cases(local, data))
+ return isl_next_backtrack;
+ return isl_next_handle;
+}
+
+/* If a solution has been found in the previous case at this level
+ * (marked by local->update being set), then add constraints
+ * that enforce a better solution in the present and all following cases.
+ * The constraints only need to be imposed once because they are
+ * included in the snapshot (taken in pick_side) that will be used in
+ * subsequent cases.
+ */
+static isl_stat better_next_side(struct isl_local_region *local,
+ struct isl_lexmin_data *data)
+{
+ if (!local->update)
+ return isl_stat_ok;
+
+ local->n_zero = force_better_solution(data->tab,
+ data->sol, data->n_op, local->n_zero);
+ if (local->n_zero < 0)
+ return isl_stat_error;
+
+ local->update = 0;
+
+ return isl_stat_ok;
+}
+
+/* Add constraints to data->tab that select the current case (local->side)
+ * at the current level.
+ *
+ * If the linear combinations v should not be zero, then the cases are
+ * v_0 >= 1
+ * v_0 <= -1
+ * v_0 = 0 and v_1 >= 1
+ * v_0 = 0 and v_1 <= -1
+ * v_0 = 0 and v_1 = 0 and v_2 >= 1
+ * v_0 = 0 and v_1 = 0 and v_2 <= -1
+ * ...
+ * in this order.
+ *
+ * A snapshot is taken after the equality constraint (if any) has been added
+ * such that the next case can start off from this position.
+ * The rollback to this position is performed in enter_level.
+ */
+static isl_stat pick_side(struct isl_local_region *local,
+ struct isl_lexmin_data *data)
+{
+ struct isl_trivial_region *region;
+ int side, base;
+
+ region = &data->region[local->region];
+ side = local->side;
+ base = 2 * (side/2);
+
+ if (side == base && base >= 2 &&
+ fix_zero(data->tab, region, base / 2 - 1, data) < 0)
+ return isl_stat_error;
+
+ local->snap = isl_tab_snap(data->tab);
+ if (isl_tab_push_basis(data->tab) < 0)
+ return isl_stat_error;
+
+ data->tab = pos_neg(data->tab, region, side, data);
+ if (!data->tab)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Free the memory associated to "data".
+ */
+static void clear_lexmin_data(struct isl_lexmin_data *data)
+{
+ free(data->local);
+ isl_vec_free(data->v);
+ isl_tab_free(data->tab);
+}
+
+/* Return the lexicographically smallest non-trivial solution of the
+ * given ILP problem.
+ *
+ * All variables are assumed to be non-negative.
+ *
+ * n_op is the number of initial coordinates to optimize.
+ * That is, once a solution has been found, we will only continue looking
+ * for solutions that result in significantly better values for those
+ * initial coordinates. That is, we only continue looking for solutions
+ * that increase the number of initial zeros in this sequence.
+ *
+ * A solution is non-trivial, if it is non-trivial on each of the
+ * specified regions. Each region represents a sequence of
+ * triviality directions on a sequence of variables that starts
+ * at a given position. A solution is non-trivial on such a region if
+ * at least one of the triviality directions is non-zero
+ * on that sequence of variables.
+ *
+ * Whenever a conflict is encountered, all constraints involved are
+ * reported to the caller through a call to "conflict".
+ *
+ * We perform a simple branch-and-bound backtracking search.
+ * Each level in the search represents an initially trivial region
+ * that is forced to be non-trivial.
+ * At each level we consider 2 * n cases, where n
+ * is the number of triviality directions.
+ * In terms of those n directions v_i, we consider the cases
+ * v_0 >= 1
+ * v_0 <= -1
+ * v_0 = 0 and v_1 >= 1
+ * v_0 = 0 and v_1 <= -1
+ * v_0 = 0 and v_1 = 0 and v_2 >= 1
+ * v_0 = 0 and v_1 = 0 and v_2 <= -1
+ * ...
+ * in this order.
+ */
+__isl_give isl_vec *isl_tab_basic_set_non_trivial_lexmin(
+ __isl_take isl_basic_set *bset, int n_op, int n_region,
+ struct isl_trivial_region *region,
+ int (*conflict)(int con, void *user), void *user)
+{
+ struct isl_lexmin_data data = { n_op, n_region, region };
+ int level, init;
+
+ if (!bset)
+ return NULL;
+
+ if (init_lexmin_data(&data, bset) < 0)
+ goto error;
+ data.tab->conflict = conflict;
+ data.tab->conflict_user = user;
+
+ level = 0;
+ init = 1;
+
+ while (level >= 0) {
+ enum isl_next next;
+ struct isl_local_region *local = &data.local[level];
+
+ next = enter_level(level, init, &data);
+ if (next < 0)
+ goto error;
+ if (next == isl_next_done)
+ break;
+ if (next == isl_next_backtrack) {
+ level--;
+ init = 0;
+ continue;
+ }
+
+ if (better_next_side(local, &data) < 0)
+ goto error;
+ if (pick_side(local, &data) < 0)
+ goto error;
+
+ local->side++;
+ level++;
+ init = 1;
+ }
+
+ clear_lexmin_data(&data);
+ isl_basic_set_free(bset);
+
+ return data.sol;
+error:
+ clear_lexmin_data(&data);
+ isl_basic_set_free(bset);
+ isl_vec_free(data.sol);
+ return NULL;
+}
+
+/* Wrapper for a tableau that is used for computing
+ * the lexicographically smallest rational point of a non-negative set.
+ * This point is represented by the sample value of "tab",
+ * unless "tab" is empty.
+ */
+struct isl_tab_lexmin {
+ isl_ctx *ctx;
+ struct isl_tab *tab;
+};
+
+/* Free "tl" and return NULL.
+ */
+__isl_null isl_tab_lexmin *isl_tab_lexmin_free(__isl_take isl_tab_lexmin *tl)
+{
+ if (!tl)
+ return NULL;
+ isl_ctx_deref(tl->ctx);
+ isl_tab_free(tl->tab);
+ free(tl);
+
+ return NULL;
+}
+
+/* Construct an isl_tab_lexmin for computing
+ * the lexicographically smallest rational point in "bset",
+ * assuming that all variables are non-negative.
+ */
+__isl_give isl_tab_lexmin *isl_tab_lexmin_from_basic_set(
+ __isl_take isl_basic_set *bset)
+{
+ isl_ctx *ctx;
+ isl_tab_lexmin *tl;
+
+ if (!bset)
+ return NULL;
+
+ ctx = isl_basic_set_get_ctx(bset);
+ tl = isl_calloc_type(ctx, struct isl_tab_lexmin);
+ if (!tl)
+ goto error;
+ tl->ctx = ctx;
+ isl_ctx_ref(ctx);
+ tl->tab = tab_for_lexmin(bset, NULL, 0, 0);
+ isl_basic_set_free(bset);
+ if (!tl->tab)
+ return isl_tab_lexmin_free(tl);
+ return tl;
+error:
+ isl_basic_set_free(bset);
+ isl_tab_lexmin_free(tl);
+ return NULL;
+}
+
+/* Return the dimension of the set represented by "tl".
+ */
+int isl_tab_lexmin_dim(__isl_keep isl_tab_lexmin *tl)
+{
+ return tl ? tl->tab->n_var : -1;
+}
+
+/* Add the equality with coefficients "eq" to "tl", updating the optimal
+ * solution if needed.
+ * The equality is added as two opposite inequality constraints.
+ */
+__isl_give isl_tab_lexmin *isl_tab_lexmin_add_eq(__isl_take isl_tab_lexmin *tl,
+ isl_int *eq)
+{
+ unsigned n_var;
+
+ if (!tl || !eq)
+ return isl_tab_lexmin_free(tl);
+
+ if (isl_tab_extend_cons(tl->tab, 2) < 0)
+ return isl_tab_lexmin_free(tl);
+ n_var = tl->tab->n_var;
+ isl_seq_neg(eq, eq, 1 + n_var);
+ tl->tab = add_lexmin_ineq(tl->tab, eq);
+ isl_seq_neg(eq, eq, 1 + n_var);
+ tl->tab = add_lexmin_ineq(tl->tab, eq);
+
+ if (!tl->tab)
+ return isl_tab_lexmin_free(tl);
+
+ return tl;
+}
+
+/* Add cuts to "tl" until the sample value reaches an integer value or
+ * until the result becomes empty.
+ */
+__isl_give isl_tab_lexmin *isl_tab_lexmin_cut_to_integer(
+ __isl_take isl_tab_lexmin *tl)
+{
+ if (!tl)
+ return NULL;
+ tl->tab = cut_to_integer_lexmin(tl->tab, CUT_ONE);
+ if (!tl->tab)
+ return isl_tab_lexmin_free(tl);
+ return tl;
+}
+
+/* Return the lexicographically smallest rational point in the basic set
+ * from which "tl" was constructed.
+ * If the original input was empty, then return a zero-length vector.
+ */
+__isl_give isl_vec *isl_tab_lexmin_get_solution(__isl_keep isl_tab_lexmin *tl)
+{
+ if (!tl)
+ return NULL;
+ if (tl->tab->empty)
+ return isl_vec_alloc(tl->ctx, 0);
+ else
+ return isl_tab_get_sample_value(tl->tab);
+}
+
+struct isl_sol_pma {
+ struct isl_sol sol;
+ isl_pw_multi_aff *pma;
+ isl_set *empty;
+};
+
+static void sol_pma_free(struct isl_sol *sol)
+{
+ struct isl_sol_pma *sol_pma = (struct isl_sol_pma *) sol;
+ isl_pw_multi_aff_free(sol_pma->pma);
+ isl_set_free(sol_pma->empty);
+}
+
+/* This function is called for parts of the context where there is
+ * no solution, with "bset" corresponding to the context tableau.
+ * Simply add the basic set to the set "empty".
+ */
+static void sol_pma_add_empty(struct isl_sol_pma *sol,
+ __isl_take isl_basic_set *bset)
+{
+ if (!bset || !sol->empty)
+ goto error;
+
+ sol->empty = isl_set_grow(sol->empty, 1);
+ bset = isl_basic_set_simplify(bset);
+ bset = isl_basic_set_finalize(bset);
+ sol->empty = isl_set_add_basic_set(sol->empty, bset);
+ if (!sol->empty)
+ sol->sol.error = 1;
+ return;
+error:
+ isl_basic_set_free(bset);
+ sol->sol.error = 1;
+}
+
+/* Given a basic set "dom" that represents the context and a tuple of
+ * affine expressions "maff" defined over this domain, construct
+ * an isl_pw_multi_aff with a single cell corresponding to "dom" and
+ * the affine expressions in "maff".
+ */
+static void sol_pma_add(struct isl_sol_pma *sol,
+ __isl_take isl_basic_set *dom, __isl_take isl_multi_aff *maff)
+{
+ isl_pw_multi_aff *pma;
+
+ dom = isl_basic_set_simplify(dom);
+ dom = isl_basic_set_finalize(dom);
+ pma = isl_pw_multi_aff_alloc(isl_set_from_basic_set(dom), maff);
+ sol->pma = isl_pw_multi_aff_add_disjoint(sol->pma, pma);
+ if (!sol->pma)
+ sol->sol.error = 1;
+}
+
+static void sol_pma_add_empty_wrap(struct isl_sol *sol,
+ __isl_take isl_basic_set *bset)
+{
+ sol_pma_add_empty((struct isl_sol_pma *)sol, bset);
+}
+
+static void sol_pma_add_wrap(struct isl_sol *sol,
+ __isl_take isl_basic_set *dom, __isl_take isl_multi_aff *ma)
+{
+ sol_pma_add((struct isl_sol_pma *)sol, dom, ma);
+}
+
+/* Construct an isl_sol_pma structure for accumulating the solution.
+ * If track_empty is set, then we also keep track of the parts
+ * of the context where there is no solution.
+ * If max is set, then we are solving a maximization, rather than
+ * a minimization problem, which means that the variables in the
+ * tableau have value "M - x" rather than "M + x".
+ */
+static struct isl_sol *sol_pma_init(__isl_keep isl_basic_map *bmap,
+ __isl_take isl_basic_set *dom, int track_empty, int max)
+{
+ struct isl_sol_pma *sol_pma = NULL;
+ isl_space *space;
+
+ if (!bmap)
+ goto error;
+
+ sol_pma = isl_calloc_type(bmap->ctx, struct isl_sol_pma);
+ if (!sol_pma)
+ goto error;
+
+ sol_pma->sol.free = &sol_pma_free;
+ if (sol_init(&sol_pma->sol, bmap, dom, max) < 0)
+ goto error;
+ sol_pma->sol.add = &sol_pma_add_wrap;
+ sol_pma->sol.add_empty = track_empty ? &sol_pma_add_empty_wrap : NULL;
+ space = isl_space_copy(sol_pma->sol.space);
+ sol_pma->pma = isl_pw_multi_aff_empty(space);
+ if (!sol_pma->pma)
+ goto error;
+
+ if (track_empty) {
+ sol_pma->empty = isl_set_alloc_space(isl_basic_set_get_space(dom),
+ 1, ISL_SET_DISJOINT);
+ if (!sol_pma->empty)
+ goto error;
+ }
+
+ isl_basic_set_free(dom);
+ return &sol_pma->sol;
+error:
+ isl_basic_set_free(dom);
+ sol_free(&sol_pma->sol);
+ return NULL;
+}
+
+/* Base case of isl_tab_basic_map_partial_lexopt, after removing
+ * some obvious symmetries.
+ *
+ * We call basic_map_partial_lexopt_base_sol and extract the results.
+ */
+static __isl_give isl_pw_multi_aff *basic_map_partial_lexopt_base_pw_multi_aff(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, int max)
+{
+ isl_pw_multi_aff *result = NULL;
+ struct isl_sol *sol;
+ struct isl_sol_pma *sol_pma;
+
+ sol = basic_map_partial_lexopt_base_sol(bmap, dom, empty, max,
+ &sol_pma_init);
+ if (!sol)
+ return NULL;
+ sol_pma = (struct isl_sol_pma *) sol;
+
+ result = isl_pw_multi_aff_copy(sol_pma->pma);
+ if (empty)
+ *empty = isl_set_copy(sol_pma->empty);
+ sol_free(&sol_pma->sol);
+ return result;
+}
+
+/* Given that the last input variable of "maff" represents the minimum
+ * of some bounds, check whether we need to plug in the expression
+ * of the minimum.
+ *
+ * In particular, check if the last input variable appears in any
+ * of the expressions in "maff".
+ */
+static isl_bool need_substitution(__isl_keep isl_multi_aff *maff)
+{
+ int i;
+ isl_size n_in;
+ unsigned pos;
+
+ n_in = isl_multi_aff_dim(maff, isl_dim_in);
+ if (n_in < 0)
+ return isl_bool_error;
+ pos = n_in - 1;
+
+ for (i = 0; i < maff->n; ++i) {
+ isl_bool involves;
+
+ involves = isl_aff_involves_dims(maff->u.p[i],
+ isl_dim_in, pos, 1);
+ if (involves < 0 || involves)
+ return involves;
+ }
+
+ return isl_bool_false;
+}
+
+/* Given a set of upper bounds on the last "input" variable m,
+ * construct a piecewise affine expression that selects
+ * the minimal upper bound to m, i.e.,
+ * divide the space into cells where one
+ * of the upper bounds is smaller than all the others and select
+ * this upper bound on that cell.
+ *
+ * In particular, if there are n bounds b_i, then the result
+ * consists of n cell, each one of the form
+ *
+ * b_i <= b_j for j > i
+ * b_i < b_j for j < i
+ *
+ * The affine expression on this cell is
+ *
+ * b_i
+ */
+static __isl_give isl_pw_aff *set_minimum_pa(__isl_take isl_space *space,
+ __isl_take isl_mat *var)
+{
+ int i;
+ isl_aff *aff = NULL;
+ isl_basic_set *bset = NULL;
+ isl_pw_aff *paff = NULL;
+ isl_space *pw_space;
+ isl_local_space *ls = NULL;
+
+ if (!space || !var)
+ goto error;
+
+ ls = isl_local_space_from_space(isl_space_copy(space));
+ pw_space = isl_space_copy(space);
+ pw_space = isl_space_from_domain(pw_space);
+ pw_space = isl_space_add_dims(pw_space, isl_dim_out, 1);
+ paff = isl_pw_aff_alloc_size(pw_space, var->n_row);
+
+ for (i = 0; i < var->n_row; ++i) {
+ isl_pw_aff *paff_i;
+
+ aff = isl_aff_alloc(isl_local_space_copy(ls));
+ bset = isl_basic_set_alloc_space(isl_space_copy(space), 0,
+ 0, var->n_row - 1);
+ if (!aff || !bset)
+ goto error;
+ isl_int_set_si(aff->v->el[0], 1);
+ isl_seq_cpy(aff->v->el + 1, var->row[i], var->n_col);
+ isl_int_set_si(aff->v->el[1 + var->n_col], 0);
+ bset = select_minimum(bset, var, i);
+ paff_i = isl_pw_aff_alloc(isl_set_from_basic_set(bset), aff);
+ paff = isl_pw_aff_add_disjoint(paff, paff_i);
+ }
+
+ isl_local_space_free(ls);
+ isl_space_free(space);
+ isl_mat_free(var);
+ return paff;
+error:
+ isl_aff_free(aff);
+ isl_basic_set_free(bset);
+ isl_pw_aff_free(paff);
+ isl_local_space_free(ls);
+ isl_space_free(space);
+ isl_mat_free(var);
+ return NULL;
+}
+
+/* Given a piecewise multi-affine expression of which the last input variable
+ * is the minimum of the bounds in "cst", plug in the value of the minimum.
+ * This minimum expression is given in "min_expr_pa".
+ * The set "min_expr" contains the same information, but in the form of a set.
+ * The variable is subsequently projected out.
+ *
+ * The implementation is similar to those of "split" and "split_domain".
+ * If the variable appears in a given expression, then minimum expression
+ * is plugged in. Otherwise, if the variable appears in the constraints
+ * and a split is required, then the domain is split. Otherwise, no split
+ * is performed.
+ */
+static __isl_give isl_pw_multi_aff *split_domain_pma(
+ __isl_take isl_pw_multi_aff *opt, __isl_take isl_pw_aff *min_expr_pa,
+ __isl_take isl_set *min_expr, __isl_take isl_mat *cst)
+{
+ isl_size n_in;
+ int i;
+ isl_space *space;
+ isl_pw_multi_aff *res;
+
+ if (!opt || !min_expr || !cst)
+ goto error;
+
+ n_in = isl_pw_multi_aff_dim(opt, isl_dim_in);
+ if (n_in < 0)
+ goto error;
+ space = isl_pw_multi_aff_get_space(opt);
+ space = isl_space_drop_dims(space, isl_dim_in, n_in - 1, 1);
+ res = isl_pw_multi_aff_empty(space);
+
+ for (i = 0; i < opt->n; ++i) {
+ isl_bool subs;
+ isl_pw_multi_aff *pma;
+
+ pma = isl_pw_multi_aff_alloc(isl_set_copy(opt->p[i].set),
+ isl_multi_aff_copy(opt->p[i].maff));
+ subs = need_substitution(opt->p[i].maff);
+ if (subs < 0) {
+ pma = isl_pw_multi_aff_free(pma);
+ } else if (subs) {
+ pma = isl_pw_multi_aff_substitute(pma,
+ n_in - 1, min_expr_pa);
+ } else {
+ isl_bool split;
+ split = need_split_set(opt->p[i].set, cst);
+ if (split < 0)
+ pma = isl_pw_multi_aff_free(pma);
+ else if (split)
+ pma = isl_pw_multi_aff_intersect_domain(pma,
+ isl_set_copy(min_expr));
+ }
+ pma = isl_pw_multi_aff_project_out(pma,
+ isl_dim_in, n_in - 1, 1);
+
+ res = isl_pw_multi_aff_add_disjoint(res, pma);
+ }
+
+ isl_pw_multi_aff_free(opt);
+ isl_pw_aff_free(min_expr_pa);
+ isl_set_free(min_expr);
+ isl_mat_free(cst);
+ return res;
+error:
+ isl_pw_multi_aff_free(opt);
+ isl_pw_aff_free(min_expr_pa);
+ isl_set_free(min_expr);
+ isl_mat_free(cst);
+ return NULL;
+}
+
+static __isl_give isl_pw_multi_aff *basic_map_partial_lexopt_pw_multi_aff(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, int max);
+
+/* This function is called from basic_map_partial_lexopt_symm.
+ * The last variable of "bmap" and "dom" corresponds to the minimum
+ * of the bounds in "cst". "map_space" is the space of the original
+ * input relation (of basic_map_partial_lexopt_symm) and "set_space"
+ * is the space of the original domain.
+ *
+ * We recursively call basic_map_partial_lexopt and then plug in
+ * the definition of the minimum in the result.
+ */
+static __isl_give isl_pw_multi_aff *
+basic_map_partial_lexopt_symm_core_pw_multi_aff(
+ __isl_take isl_basic_map *bmap, __isl_take isl_basic_set *dom,
+ __isl_give isl_set **empty, int max, __isl_take isl_mat *cst,
+ __isl_take isl_space *map_space, __isl_take isl_space *set_space)
+{
+ isl_pw_multi_aff *opt;
+ isl_pw_aff *min_expr_pa;
+ isl_set *min_expr;
+
+ min_expr = set_minimum(isl_basic_set_get_space(dom), isl_mat_copy(cst));
+ min_expr_pa = set_minimum_pa(isl_basic_set_get_space(dom),
+ isl_mat_copy(cst));
+
+ opt = basic_map_partial_lexopt_pw_multi_aff(bmap, dom, empty, max);
+
+ if (empty) {
+ *empty = split(*empty,
+ isl_set_copy(min_expr), isl_mat_copy(cst));
+ *empty = isl_set_reset_space(*empty, set_space);
+ }
+
+ opt = split_domain_pma(opt, min_expr_pa, min_expr, cst);
+ opt = isl_pw_multi_aff_reset_space(opt, map_space);
+
+ return opt;
+}
+
+#undef TYPE
+#define TYPE isl_pw_multi_aff
+#undef SUFFIX
+#define SUFFIX _pw_multi_aff
+#include "isl_tab_lexopt_templ.c"
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tarjan.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tarjan.c
new file mode 100644
index 00000000000..a958c016e0b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tarjan.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2010-2011 INRIA Saclay
+ * Copyright 2012 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <stdlib.h>
+#include <isl/ctx.h>
+#include <isl_tarjan.h>
+
+struct isl_tarjan_graph *isl_tarjan_graph_free(struct isl_tarjan_graph *g)
+{
+ if (!g)
+ return NULL;
+ free(g->node);
+ free(g->stack);
+ free(g->order);
+ free(g);
+ return NULL;
+}
+
+static struct isl_tarjan_graph *isl_tarjan_graph_alloc(isl_ctx *ctx, int len)
+{
+ struct isl_tarjan_graph *g;
+ int i;
+
+ g = isl_calloc_type(ctx, struct isl_tarjan_graph);
+ if (!g)
+ return NULL;
+ g->len = len;
+ g->node = isl_alloc_array(ctx, struct isl_tarjan_node, len);
+ if (len && !g->node)
+ goto error;
+ for (i = 0; i < len; ++i)
+ g->node[i].index = -1;
+ g->stack = isl_alloc_array(ctx, int, len);
+ if (len && !g->stack)
+ goto error;
+ g->order = isl_alloc_array(ctx, int, 2 * len);
+ if (len && !g->order)
+ goto error;
+
+ g->sp = 0;
+ g->index = 0;
+ g->op = 0;
+
+ return g;
+error:
+ isl_tarjan_graph_free(g);
+ return NULL;
+}
+
+/* Perform Tarjan's algorithm for computing the strongly connected components
+ * in the graph with g->len nodes and with edges defined by "follows".
+ */
+static isl_stat isl_tarjan_components(struct isl_tarjan_graph *g, int i,
+ isl_bool (*follows)(int i, int j, void *user), void *user)
+{
+ int j;
+
+ g->node[i].index = g->index;
+ g->node[i].min_index = g->index;
+ g->node[i].on_stack = 1;
+ g->index++;
+ g->stack[g->sp++] = i;
+
+ for (j = g->len - 1; j >= 0; --j) {
+ isl_bool f;
+
+ if (j == i)
+ continue;
+ if (g->node[j].index >= 0 &&
+ (!g->node[j].on_stack ||
+ g->node[j].index > g->node[i].min_index))
+ continue;
+
+ f = follows(i, j, user);
+ if (f < 0)
+ return isl_stat_error;
+ if (!f)
+ continue;
+
+ if (g->node[j].index < 0) {
+ isl_tarjan_components(g, j, follows, user);
+ if (g->node[j].min_index < g->node[i].min_index)
+ g->node[i].min_index = g->node[j].min_index;
+ } else if (g->node[j].index < g->node[i].min_index)
+ g->node[i].min_index = g->node[j].index;
+ }
+
+ if (g->node[i].index != g->node[i].min_index)
+ return isl_stat_ok;
+
+ do {
+ j = g->stack[--g->sp];
+ g->node[j].on_stack = 0;
+ g->order[g->op++] = j;
+ } while (j != i);
+ g->order[g->op++] = -1;
+
+ return isl_stat_ok;
+}
+
+/* Decompose the graph with "len" nodes and edges defined by "follows"
+ * into strongly connected components (SCCs).
+ * follows(i, j, user) should return 1 if "i" follows "j" and 0 otherwise.
+ * It should return -1 on error.
+ *
+ * If SCC a contains a node i that follows a node j in another SCC b
+ * (i.e., follows(i, j, user) returns 1), then SCC a will appear after SCC b
+ * in the result.
+ */
+struct isl_tarjan_graph *isl_tarjan_graph_init(isl_ctx *ctx, int len,
+ isl_bool (*follows)(int i, int j, void *user), void *user)
+{
+ int i;
+ struct isl_tarjan_graph *g = NULL;
+
+ g = isl_tarjan_graph_alloc(ctx, len);
+ if (!g)
+ return NULL;
+ for (i = len - 1; i >= 0; --i) {
+ if (g->node[i].index >= 0)
+ continue;
+ if (isl_tarjan_components(g, i, follows, user) < 0)
+ return isl_tarjan_graph_free(g);
+ }
+
+ return g;
+}
+
+/* Decompose the graph with "len" nodes and edges defined by "follows"
+ * into the strongly connected component (SCC) that contains "node"
+ * as well as all SCCs that are followed by this SCC.
+ * follows(i, j, user) should return 1 if "i" follows "j" and 0 otherwise.
+ * It should return -1 on error.
+ *
+ * The SCC containing "node" will appear as the last component
+ * in g->order.
+ */
+struct isl_tarjan_graph *isl_tarjan_graph_component(isl_ctx *ctx, int len,
+ int node, isl_bool (*follows)(int i, int j, void *user), void *user)
+{
+ struct isl_tarjan_graph *g;
+
+ g = isl_tarjan_graph_alloc(ctx, len);
+ if (!g)
+ return NULL;
+ if (isl_tarjan_components(g, node, follows, user) < 0)
+ return isl_tarjan_graph_free(g);
+
+ return g;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tarjan.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tarjan.h
new file mode 100644
index 00000000000..af3452cc867
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_tarjan.h
@@ -0,0 +1,42 @@
+#ifndef ISL_TARJAN_H
+#define ISL_TARJAN_H
+
+/* Structure for representing the nodes in the graph being traversed
+ * using Tarjan's algorithm.
+ * index represents the order in which nodes are visited.
+ * min_index is the index of the root of a (sub)component.
+ * on_stack indicates whether the node is currently on the stack.
+ */
+struct isl_tarjan_node {
+ int index;
+ int min_index;
+ int on_stack;
+};
+
+/* Structure for representing the graph being traversed
+ * using Tarjan's algorithm.
+ * len is the number of nodes
+ * node is an array of nodes
+ * stack contains the nodes on the path from the root to the current node
+ * sp is the stack pointer
+ * index is the index of the last node visited
+ * order contains the elements of the components separated by -1
+ * op represents the current position in order
+ */
+struct isl_tarjan_graph {
+ int len;
+ struct isl_tarjan_node *node;
+ int *stack;
+ int sp;
+ int index;
+ int *order;
+ int op;
+};
+
+struct isl_tarjan_graph *isl_tarjan_graph_init(isl_ctx *ctx, int len,
+ isl_bool (*follows)(int i, int j, void *user), void *user);
+struct isl_tarjan_graph *isl_tarjan_graph_component(isl_ctx *ctx, int len,
+ int node, isl_bool (*follows)(int i, int j, void *user), void *user);
+struct isl_tarjan_graph *isl_tarjan_graph_free(struct isl_tarjan_graph *g);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_transitive_closure.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_transitive_closure.c
new file mode 100644
index 00000000000..2733cee563d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_transitive_closure.c
@@ -0,0 +1,2947 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_map_private.h>
+#include <isl/map.h>
+#include <isl_seq.h>
+#include <isl_space_private.h>
+#include <isl_lp_private.h>
+#include <isl/union_map.h>
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+#include <isl_options_private.h>
+#include <isl_tarjan.h>
+
+isl_bool isl_map_is_transitively_closed(__isl_keep isl_map *map)
+{
+ isl_map *map2;
+ isl_bool closed;
+
+ map2 = isl_map_apply_range(isl_map_copy(map), isl_map_copy(map));
+ closed = isl_map_is_subset(map2, map);
+ isl_map_free(map2);
+
+ return closed;
+}
+
+isl_bool isl_union_map_is_transitively_closed(__isl_keep isl_union_map *umap)
+{
+ isl_union_map *umap2;
+ isl_bool closed;
+
+ umap2 = isl_union_map_apply_range(isl_union_map_copy(umap),
+ isl_union_map_copy(umap));
+ closed = isl_union_map_is_subset(umap2, umap);
+ isl_union_map_free(umap2);
+
+ return closed;
+}
+
+/* Given a map that represents a path with the length of the path
+ * encoded as the difference between the last output coordindate
+ * and the last input coordinate, set this length to either
+ * exactly "length" (if "exactly" is set) or at least "length"
+ * (if "exactly" is not set).
+ */
+static __isl_give isl_map *set_path_length(__isl_take isl_map *map,
+ int exactly, int length)
+{
+ isl_space *space;
+ struct isl_basic_map *bmap;
+ isl_size d;
+ isl_size nparam;
+ isl_size total;
+ int k;
+ isl_int *c;
+
+ if (!map)
+ return NULL;
+
+ space = isl_map_get_space(map);
+ d = isl_space_dim(space, isl_dim_in);
+ nparam = isl_space_dim(space, isl_dim_param);
+ total = isl_space_dim(space, isl_dim_all);
+ if (d < 0 || nparam < 0 || total < 0)
+ space = isl_space_free(space);
+ bmap = isl_basic_map_alloc_space(space, 0, 1, 1);
+ if (exactly) {
+ k = isl_basic_map_alloc_equality(bmap);
+ if (k < 0)
+ goto error;
+ c = bmap->eq[k];
+ } else {
+ k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ c = bmap->ineq[k];
+ }
+ isl_seq_clr(c, 1 + total);
+ isl_int_set_si(c[0], -length);
+ isl_int_set_si(c[1 + nparam + d - 1], -1);
+ isl_int_set_si(c[1 + nparam + d + d - 1], 1);
+
+ bmap = isl_basic_map_finalize(bmap);
+ map = isl_map_intersect(map, isl_map_from_basic_map(bmap));
+
+ return map;
+error:
+ isl_basic_map_free(bmap);
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Check whether the overapproximation of the power of "map" is exactly
+ * the power of "map". Let R be "map" and A_k the overapproximation.
+ * The approximation is exact if
+ *
+ * A_1 = R
+ * A_k = A_{k-1} \circ R k >= 2
+ *
+ * Since A_k is known to be an overapproximation, we only need to check
+ *
+ * A_1 \subset R
+ * A_k \subset A_{k-1} \circ R k >= 2
+ *
+ * In practice, "app" has an extra input and output coordinate
+ * to encode the length of the path. So, we first need to add
+ * this coordinate to "map" and set the length of the path to
+ * one.
+ */
+static isl_bool check_power_exactness(__isl_take isl_map *map,
+ __isl_take isl_map *app)
+{
+ isl_bool exact;
+ isl_map *app_1;
+ isl_map *app_2;
+
+ map = isl_map_add_dims(map, isl_dim_in, 1);
+ map = isl_map_add_dims(map, isl_dim_out, 1);
+ map = set_path_length(map, 1, 1);
+
+ app_1 = set_path_length(isl_map_copy(app), 1, 1);
+
+ exact = isl_map_is_subset(app_1, map);
+ isl_map_free(app_1);
+
+ if (!exact || exact < 0) {
+ isl_map_free(app);
+ isl_map_free(map);
+ return exact;
+ }
+
+ app_1 = set_path_length(isl_map_copy(app), 0, 1);
+ app_2 = set_path_length(app, 0, 2);
+ app_1 = isl_map_apply_range(map, app_1);
+
+ exact = isl_map_is_subset(app_2, app_1);
+
+ isl_map_free(app_1);
+ isl_map_free(app_2);
+
+ return exact;
+}
+
+/* Check whether the overapproximation of the power of "map" is exactly
+ * the power of "map", possibly after projecting out the power (if "project"
+ * is set).
+ *
+ * If "project" is set and if "steps" can only result in acyclic paths,
+ * then we check
+ *
+ * A = R \cup (A \circ R)
+ *
+ * where A is the overapproximation with the power projected out, i.e.,
+ * an overapproximation of the transitive closure.
+ * More specifically, since A is known to be an overapproximation, we check
+ *
+ * A \subset R \cup (A \circ R)
+ *
+ * Otherwise, we check if the power is exact.
+ *
+ * Note that "app" has an extra input and output coordinate to encode
+ * the length of the part. If we are only interested in the transitive
+ * closure, then we can simply project out these coordinates first.
+ */
+static isl_bool check_exactness(__isl_take isl_map *map,
+ __isl_take isl_map *app, int project)
+{
+ isl_map *test;
+ isl_bool exact;
+ isl_size d;
+
+ if (!project)
+ return check_power_exactness(map, app);
+
+ d = isl_map_dim(map, isl_dim_in);
+ if (d < 0)
+ app = isl_map_free(app);
+ app = set_path_length(app, 0, 1);
+ app = isl_map_project_out(app, isl_dim_in, d, 1);
+ app = isl_map_project_out(app, isl_dim_out, d, 1);
+
+ app = isl_map_reset_space(app, isl_map_get_space(map));
+
+ test = isl_map_apply_range(isl_map_copy(map), isl_map_copy(app));
+ test = isl_map_union(test, isl_map_copy(map));
+
+ exact = isl_map_is_subset(app, test);
+
+ isl_map_free(app);
+ isl_map_free(test);
+
+ isl_map_free(map);
+
+ return exact;
+}
+
+/*
+ * The transitive closure implementation is based on the paper
+ * "Computing the Transitive Closure of a Union of Affine Integer
+ * Tuple Relations" by Anna Beletska, Denis Barthou, Wlodzimierz Bielecki and
+ * Albert Cohen.
+ */
+
+/* Given a set of n offsets v_i (the rows of "steps"), construct a relation
+ * of the given dimension specification (Z^{n+1} -> Z^{n+1})
+ * that maps an element x to any element that can be reached
+ * by taking a non-negative number of steps along any of
+ * the extended offsets v'_i = [v_i 1].
+ * That is, construct
+ *
+ * { [x] -> [y] : exists k_i >= 0, y = x + \sum_i k_i v'_i }
+ *
+ * For any element in this relation, the number of steps taken
+ * is equal to the difference in the final coordinates.
+ */
+static __isl_give isl_map *path_along_steps(__isl_take isl_space *space,
+ __isl_keep isl_mat *steps)
+{
+ int i, j, k;
+ struct isl_basic_map *path = NULL;
+ isl_size d;
+ unsigned n;
+ isl_size nparam;
+ isl_size total;
+
+ d = isl_space_dim(space, isl_dim_in);
+ nparam = isl_space_dim(space, isl_dim_param);
+ if (d < 0 || nparam < 0 || !steps)
+ goto error;
+
+ n = steps->n_row;
+
+ path = isl_basic_map_alloc_space(isl_space_copy(space), n, d, n);
+
+ for (i = 0; i < n; ++i) {
+ k = isl_basic_map_alloc_div(path);
+ if (k < 0)
+ goto error;
+ isl_assert(steps->ctx, i == k, goto error);
+ isl_int_set_si(path->div[k][0], 0);
+ }
+
+ total = isl_basic_map_dim(path, isl_dim_all);
+ if (total < 0)
+ goto error;
+ for (i = 0; i < d; ++i) {
+ k = isl_basic_map_alloc_equality(path);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(path->eq[k], 1 + total);
+ isl_int_set_si(path->eq[k][1 + nparam + i], 1);
+ isl_int_set_si(path->eq[k][1 + nparam + d + i], -1);
+ if (i == d - 1)
+ for (j = 0; j < n; ++j)
+ isl_int_set_si(path->eq[k][1 + nparam + 2 * d + j], 1);
+ else
+ for (j = 0; j < n; ++j)
+ isl_int_set(path->eq[k][1 + nparam + 2 * d + j],
+ steps->row[j][i]);
+ }
+
+ for (i = 0; i < n; ++i) {
+ k = isl_basic_map_alloc_inequality(path);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(path->ineq[k], 1 + total);
+ isl_int_set_si(path->ineq[k][1 + nparam + 2 * d + i], 1);
+ }
+
+ isl_space_free(space);
+
+ path = isl_basic_map_simplify(path);
+ path = isl_basic_map_finalize(path);
+ return isl_map_from_basic_map(path);
+error:
+ isl_space_free(space);
+ isl_basic_map_free(path);
+ return NULL;
+}
+
+#define IMPURE 0
+#define PURE_PARAM 1
+#define PURE_VAR 2
+#define MIXED 3
+
+/* Check whether the parametric constant term of constraint c is never
+ * positive in "bset".
+ */
+static isl_bool parametric_constant_never_positive(
+ __isl_keep isl_basic_set *bset, isl_int *c, int *div_purity)
+{
+ isl_size d;
+ isl_size n_div;
+ isl_size nparam;
+ isl_size total;
+ int i;
+ int k;
+ isl_bool empty;
+
+ n_div = isl_basic_set_dim(bset, isl_dim_div);
+ d = isl_basic_set_dim(bset, isl_dim_set);
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (n_div < 0 || d < 0 || nparam < 0 || total < 0)
+ return isl_bool_error;
+
+ bset = isl_basic_set_copy(bset);
+ bset = isl_basic_set_cow(bset);
+ bset = isl_basic_set_extend_constraints(bset, 0, 1);
+ k = isl_basic_set_alloc_inequality(bset);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bset->ineq[k], 1 + total);
+ isl_seq_cpy(bset->ineq[k], c, 1 + nparam);
+ for (i = 0; i < n_div; ++i) {
+ if (div_purity[i] != PURE_PARAM)
+ continue;
+ isl_int_set(bset->ineq[k][1 + nparam + d + i],
+ c[1 + nparam + d + i]);
+ }
+ isl_int_sub_ui(bset->ineq[k][0], bset->ineq[k][0], 1);
+ empty = isl_basic_set_is_empty(bset);
+ isl_basic_set_free(bset);
+
+ return empty;
+error:
+ isl_basic_set_free(bset);
+ return isl_bool_error;
+}
+
+/* Return PURE_PARAM if only the coefficients of the parameters are non-zero.
+ * Return PURE_VAR if only the coefficients of the set variables are non-zero.
+ * Return MIXED if only the coefficients of the parameters and the set
+ * variables are non-zero and if moreover the parametric constant
+ * can never attain positive values.
+ * Return IMPURE otherwise.
+ */
+static int purity(__isl_keep isl_basic_set *bset, isl_int *c, int *div_purity,
+ int eq)
+{
+ isl_size d;
+ isl_size n_div;
+ isl_size nparam;
+ isl_bool empty;
+ int i;
+ int p = 0, v = 0;
+
+ n_div = isl_basic_set_dim(bset, isl_dim_div);
+ d = isl_basic_set_dim(bset, isl_dim_set);
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ if (n_div < 0 || d < 0 || nparam < 0)
+ return -1;
+
+ for (i = 0; i < n_div; ++i) {
+ if (isl_int_is_zero(c[1 + nparam + d + i]))
+ continue;
+ switch (div_purity[i]) {
+ case PURE_PARAM: p = 1; break;
+ case PURE_VAR: v = 1; break;
+ default: return IMPURE;
+ }
+ }
+ if (!p && isl_seq_first_non_zero(c + 1, nparam) == -1)
+ return PURE_VAR;
+ if (!v && isl_seq_first_non_zero(c + 1 + nparam, d) == -1)
+ return PURE_PARAM;
+
+ empty = parametric_constant_never_positive(bset, c, div_purity);
+ if (eq && empty >= 0 && !empty) {
+ isl_seq_neg(c, c, 1 + nparam + d + n_div);
+ empty = parametric_constant_never_positive(bset, c, div_purity);
+ }
+
+ return empty < 0 ? -1 : empty ? MIXED : IMPURE;
+}
+
+/* Return an array of integers indicating the type of each div in bset.
+ * If the div is (recursively) defined in terms of only the parameters,
+ * then the type is PURE_PARAM.
+ * If the div is (recursively) defined in terms of only the set variables,
+ * then the type is PURE_VAR.
+ * Otherwise, the type is IMPURE.
+ */
+static __isl_give int *get_div_purity(__isl_keep isl_basic_set *bset)
+{
+ int i, j;
+ int *div_purity;
+ isl_size d;
+ isl_size n_div;
+ isl_size nparam;
+
+ n_div = isl_basic_set_dim(bset, isl_dim_div);
+ d = isl_basic_set_dim(bset, isl_dim_set);
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ if (n_div < 0 || d < 0 || nparam < 0)
+ return NULL;
+
+ div_purity = isl_alloc_array(bset->ctx, int, n_div);
+ if (n_div && !div_purity)
+ return NULL;
+
+ for (i = 0; i < bset->n_div; ++i) {
+ int p = 0, v = 0;
+ if (isl_int_is_zero(bset->div[i][0])) {
+ div_purity[i] = IMPURE;
+ continue;
+ }
+ if (isl_seq_first_non_zero(bset->div[i] + 2, nparam) != -1)
+ p = 1;
+ if (isl_seq_first_non_zero(bset->div[i] + 2 + nparam, d) != -1)
+ v = 1;
+ for (j = 0; j < i; ++j) {
+ if (isl_int_is_zero(bset->div[i][2 + nparam + d + j]))
+ continue;
+ switch (div_purity[j]) {
+ case PURE_PARAM: p = 1; break;
+ case PURE_VAR: v = 1; break;
+ default: p = v = 1; break;
+ }
+ }
+ div_purity[i] = v ? p ? IMPURE : PURE_VAR : PURE_PARAM;
+ }
+
+ return div_purity;
+}
+
+/* Given a path with the as yet unconstrained length at div position "pos",
+ * check if setting the length to zero results in only the identity
+ * mapping.
+ */
+static isl_bool empty_path_is_identity(__isl_keep isl_basic_map *path,
+ unsigned pos)
+{
+ isl_basic_map *test = NULL;
+ isl_basic_map *id = NULL;
+ isl_bool is_id;
+
+ test = isl_basic_map_copy(path);
+ test = isl_basic_map_fix_si(test, isl_dim_div, pos, 0);
+ id = isl_basic_map_identity(isl_basic_map_get_space(path));
+ is_id = isl_basic_map_is_equal(test, id);
+ isl_basic_map_free(test);
+ isl_basic_map_free(id);
+ return is_id;
+}
+
+/* If any of the constraints is found to be impure then this function
+ * sets *impurity to 1.
+ *
+ * If impurity is NULL then we are dealing with a non-parametric set
+ * and so the constraints are obviously PURE_VAR.
+ */
+static __isl_give isl_basic_map *add_delta_constraints(
+ __isl_take isl_basic_map *path,
+ __isl_keep isl_basic_set *delta, unsigned off, unsigned nparam,
+ unsigned d, int *div_purity, int eq, int *impurity)
+{
+ int i, k;
+ int n = eq ? delta->n_eq : delta->n_ineq;
+ isl_int **delta_c = eq ? delta->eq : delta->ineq;
+ isl_size n_div, total;
+
+ n_div = isl_basic_set_dim(delta, isl_dim_div);
+ total = isl_basic_map_dim(path, isl_dim_all);
+ if (n_div < 0 || total < 0)
+ return isl_basic_map_free(path);
+
+ for (i = 0; i < n; ++i) {
+ isl_int *path_c;
+ int p = PURE_VAR;
+ if (impurity)
+ p = purity(delta, delta_c[i], div_purity, eq);
+ if (p < 0)
+ goto error;
+ if (p != PURE_VAR && p != PURE_PARAM && !*impurity)
+ *impurity = 1;
+ if (p == IMPURE)
+ continue;
+ if (eq && p != MIXED) {
+ k = isl_basic_map_alloc_equality(path);
+ if (k < 0)
+ goto error;
+ path_c = path->eq[k];
+ } else {
+ k = isl_basic_map_alloc_inequality(path);
+ if (k < 0)
+ goto error;
+ path_c = path->ineq[k];
+ }
+ isl_seq_clr(path_c, 1 + total);
+ if (p == PURE_VAR) {
+ isl_seq_cpy(path_c + off,
+ delta_c[i] + 1 + nparam, d);
+ isl_int_set(path_c[off + d], delta_c[i][0]);
+ } else if (p == PURE_PARAM) {
+ isl_seq_cpy(path_c, delta_c[i], 1 + nparam);
+ } else {
+ isl_seq_cpy(path_c + off,
+ delta_c[i] + 1 + nparam, d);
+ isl_seq_cpy(path_c, delta_c[i], 1 + nparam);
+ }
+ isl_seq_cpy(path_c + off - n_div,
+ delta_c[i] + 1 + nparam + d, n_div);
+ }
+
+ return path;
+error:
+ isl_basic_map_free(path);
+ return NULL;
+}
+
+/* Given a set of offsets "delta", construct a relation of the
+ * given dimension specification (Z^{n+1} -> Z^{n+1}) that
+ * is an overapproximation of the relations that
+ * maps an element x to any element that can be reached
+ * by taking a non-negative number of steps along any of
+ * the elements in "delta".
+ * That is, construct an approximation of
+ *
+ * { [x] -> [y] : exists f \in \delta, k \in Z :
+ * y = x + k [f, 1] and k >= 0 }
+ *
+ * For any element in this relation, the number of steps taken
+ * is equal to the difference in the final coordinates.
+ *
+ * In particular, let delta be defined as
+ *
+ * \delta = [p] -> { [x] : A x + a >= 0 and B p + b >= 0 and
+ * C x + C'p + c >= 0 and
+ * D x + D'p + d >= 0 }
+ *
+ * where the constraints C x + C'p + c >= 0 are such that the parametric
+ * constant term of each constraint j, "C_j x + C'_j p + c_j",
+ * can never attain positive values, then the relation is constructed as
+ *
+ * { [x] -> [y] : exists [f, k] \in Z^{n+1} : y = x + f and
+ * A f + k a >= 0 and B p + b >= 0 and
+ * C f + C'p + c >= 0 and k >= 1 }
+ * union { [x] -> [x] }
+ *
+ * If the zero-length paths happen to correspond exactly to the identity
+ * mapping, then we return
+ *
+ * { [x] -> [y] : exists [f, k] \in Z^{n+1} : y = x + f and
+ * A f + k a >= 0 and B p + b >= 0 and
+ * C f + C'p + c >= 0 and k >= 0 }
+ *
+ * instead.
+ *
+ * Existentially quantified variables in \delta are handled by
+ * classifying them as independent of the parameters, purely
+ * parameter dependent and others. Constraints containing
+ * any of the other existentially quantified variables are removed.
+ * This is safe, but leads to an additional overapproximation.
+ *
+ * If there are any impure constraints, then we also eliminate
+ * the parameters from \delta, resulting in a set
+ *
+ * \delta' = { [x] : E x + e >= 0 }
+ *
+ * and add the constraints
+ *
+ * E f + k e >= 0
+ *
+ * to the constructed relation.
+ */
+static __isl_give isl_map *path_along_delta(__isl_take isl_space *space,
+ __isl_take isl_basic_set *delta)
+{
+ isl_basic_map *path = NULL;
+ isl_size d;
+ isl_size n_div;
+ isl_size nparam;
+ isl_size total;
+ unsigned off;
+ int i, k;
+ isl_bool is_id;
+ int *div_purity = NULL;
+ int impurity = 0;
+
+ n_div = isl_basic_set_dim(delta, isl_dim_div);
+ d = isl_basic_set_dim(delta, isl_dim_set);
+ nparam = isl_basic_set_dim(delta, isl_dim_param);
+ if (n_div < 0 || d < 0 || nparam < 0)
+ goto error;
+ path = isl_basic_map_alloc_space(isl_space_copy(space), n_div + d + 1,
+ d + 1 + delta->n_eq, delta->n_eq + delta->n_ineq + 1);
+ off = 1 + nparam + 2 * (d + 1) + n_div;
+
+ for (i = 0; i < n_div + d + 1; ++i) {
+ k = isl_basic_map_alloc_div(path);
+ if (k < 0)
+ goto error;
+ isl_int_set_si(path->div[k][0], 0);
+ }
+
+ total = isl_basic_map_dim(path, isl_dim_all);
+ if (total < 0)
+ goto error;
+ for (i = 0; i < d + 1; ++i) {
+ k = isl_basic_map_alloc_equality(path);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(path->eq[k], 1 + total);
+ isl_int_set_si(path->eq[k][1 + nparam + i], 1);
+ isl_int_set_si(path->eq[k][1 + nparam + d + 1 + i], -1);
+ isl_int_set_si(path->eq[k][off + i], 1);
+ }
+
+ div_purity = get_div_purity(delta);
+ if (n_div && !div_purity)
+ goto error;
+
+ path = add_delta_constraints(path, delta, off, nparam, d,
+ div_purity, 1, &impurity);
+ path = add_delta_constraints(path, delta, off, nparam, d,
+ div_purity, 0, &impurity);
+ if (impurity) {
+ isl_space *space = isl_basic_set_get_space(delta);
+ delta = isl_basic_set_project_out(delta,
+ isl_dim_param, 0, nparam);
+ delta = isl_basic_set_add_dims(delta, isl_dim_param, nparam);
+ delta = isl_basic_set_reset_space(delta, space);
+ if (!delta)
+ goto error;
+ path = isl_basic_map_extend_constraints(path, delta->n_eq,
+ delta->n_ineq + 1);
+ path = add_delta_constraints(path, delta, off, nparam, d,
+ NULL, 1, NULL);
+ path = add_delta_constraints(path, delta, off, nparam, d,
+ NULL, 0, NULL);
+ path = isl_basic_map_gauss(path, NULL);
+ }
+
+ is_id = empty_path_is_identity(path, n_div + d);
+ if (is_id < 0)
+ goto error;
+
+ k = isl_basic_map_alloc_inequality(path);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(path->ineq[k], 1 + total);
+ if (!is_id)
+ isl_int_set_si(path->ineq[k][0], -1);
+ isl_int_set_si(path->ineq[k][off + d], 1);
+
+ free(div_purity);
+ isl_basic_set_free(delta);
+ path = isl_basic_map_finalize(path);
+ if (is_id) {
+ isl_space_free(space);
+ return isl_map_from_basic_map(path);
+ }
+ return isl_basic_map_union(path, isl_basic_map_identity(space));
+error:
+ free(div_purity);
+ isl_space_free(space);
+ isl_basic_set_free(delta);
+ isl_basic_map_free(path);
+ return NULL;
+}
+
+/* Given a dimension specification Z^{n+1} -> Z^{n+1} and a parameter "param",
+ * construct a map that equates the parameter to the difference
+ * in the final coordinates and imposes that this difference is positive.
+ * That is, construct
+ *
+ * { [x,x_s] -> [y,y_s] : k = y_s - x_s > 0 }
+ */
+static __isl_give isl_map *equate_parameter_to_length(
+ __isl_take isl_space *space, unsigned param)
+{
+ struct isl_basic_map *bmap;
+ isl_size d;
+ isl_size nparam;
+ isl_size total;
+ int k;
+
+ d = isl_space_dim(space, isl_dim_in);
+ nparam = isl_space_dim(space, isl_dim_param);
+ total = isl_space_dim(space, isl_dim_all);
+ if (d < 0 || nparam < 0 || total < 0)
+ space = isl_space_free(space);
+ bmap = isl_basic_map_alloc_space(space, 0, 1, 1);
+ k = isl_basic_map_alloc_equality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bmap->eq[k], 1 + total);
+ isl_int_set_si(bmap->eq[k][1 + param], -1);
+ isl_int_set_si(bmap->eq[k][1 + nparam + d - 1], -1);
+ isl_int_set_si(bmap->eq[k][1 + nparam + d + d - 1], 1);
+
+ k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[k], 1 + total);
+ isl_int_set_si(bmap->ineq[k][1 + param], 1);
+ isl_int_set_si(bmap->ineq[k][0], -1);
+
+ bmap = isl_basic_map_finalize(bmap);
+ return isl_map_from_basic_map(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Check whether "path" is acyclic, where the last coordinates of domain
+ * and range of path encode the number of steps taken.
+ * That is, check whether
+ *
+ * { d | d = y - x and (x,y) in path }
+ *
+ * does not contain any element with positive last coordinate (positive length)
+ * and zero remaining coordinates (cycle).
+ */
+static isl_bool is_acyclic(__isl_take isl_map *path)
+{
+ int i;
+ isl_bool acyclic;
+ isl_size dim;
+ struct isl_set *delta;
+
+ delta = isl_map_deltas(path);
+ dim = isl_set_dim(delta, isl_dim_set);
+ if (dim < 0)
+ delta = isl_set_free(delta);
+ for (i = 0; i < dim; ++i) {
+ if (i == dim -1)
+ delta = isl_set_lower_bound_si(delta, isl_dim_set, i, 1);
+ else
+ delta = isl_set_fix_si(delta, isl_dim_set, i, 0);
+ }
+
+ acyclic = isl_set_is_empty(delta);
+ isl_set_free(delta);
+
+ return acyclic;
+}
+
+/* Given a union of basic maps R = \cup_i R_i \subseteq D \times D
+ * and a dimension specification (Z^{n+1} -> Z^{n+1}),
+ * construct a map that is an overapproximation of the map
+ * that takes an element from the space D \times Z to another
+ * element from the same space, such that the first n coordinates of the
+ * difference between them is a sum of differences between images
+ * and pre-images in one of the R_i and such that the last coordinate
+ * is equal to the number of steps taken.
+ * That is, let
+ *
+ * \Delta_i = { y - x | (x, y) in R_i }
+ *
+ * then the constructed map is an overapproximation of
+ *
+ * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i :
+ * d = (\sum_i k_i \delta_i, \sum_i k_i) }
+ *
+ * The elements of the singleton \Delta_i's are collected as the
+ * rows of the steps matrix. For all these \Delta_i's together,
+ * a single path is constructed.
+ * For each of the other \Delta_i's, we compute an overapproximation
+ * of the paths along elements of \Delta_i.
+ * Since each of these paths performs an addition, composition is
+ * symmetric and we can simply compose all resulting paths in any order.
+ */
+static __isl_give isl_map *construct_extended_path(__isl_take isl_space *space,
+ __isl_keep isl_map *map, int *project)
+{
+ struct isl_mat *steps = NULL;
+ struct isl_map *path = NULL;
+ isl_size d;
+ int i, j, n;
+
+ d = isl_map_dim(map, isl_dim_in);
+ if (d < 0)
+ goto error;
+
+ path = isl_map_identity(isl_space_copy(space));
+
+ steps = isl_mat_alloc(map->ctx, map->n, d);
+ if (!steps)
+ goto error;
+
+ n = 0;
+ for (i = 0; i < map->n; ++i) {
+ struct isl_basic_set *delta;
+
+ delta = isl_basic_map_deltas(isl_basic_map_copy(map->p[i]));
+
+ for (j = 0; j < d; ++j) {
+ isl_bool fixed;
+
+ fixed = isl_basic_set_plain_dim_is_fixed(delta, j,
+ &steps->row[n][j]);
+ if (fixed < 0) {
+ isl_basic_set_free(delta);
+ goto error;
+ }
+ if (!fixed)
+ break;
+ }
+
+
+ if (j < d) {
+ path = isl_map_apply_range(path,
+ path_along_delta(isl_space_copy(space), delta));
+ path = isl_map_coalesce(path);
+ } else {
+ isl_basic_set_free(delta);
+ ++n;
+ }
+ }
+
+ if (n > 0) {
+ steps->n_row = n;
+ path = isl_map_apply_range(path,
+ path_along_steps(isl_space_copy(space), steps));
+ }
+
+ if (project && *project) {
+ *project = is_acyclic(isl_map_copy(path));
+ if (*project < 0)
+ goto error;
+ }
+
+ isl_space_free(space);
+ isl_mat_free(steps);
+ return path;
+error:
+ isl_space_free(space);
+ isl_mat_free(steps);
+ isl_map_free(path);
+ return NULL;
+}
+
+static isl_bool isl_set_overlaps(__isl_keep isl_set *set1,
+ __isl_keep isl_set *set2)
+{
+ return isl_bool_not(isl_set_is_disjoint(set1, set2));
+}
+
+/* Given a union of basic maps R = \cup_i R_i \subseteq D \times D
+ * and a dimension specification (Z^{n+1} -> Z^{n+1}),
+ * construct a map that is an overapproximation of the map
+ * that takes an element from the dom R \times Z to an
+ * element from ran R \times Z, such that the first n coordinates of the
+ * difference between them is a sum of differences between images
+ * and pre-images in one of the R_i and such that the last coordinate
+ * is equal to the number of steps taken.
+ * That is, let
+ *
+ * \Delta_i = { y - x | (x, y) in R_i }
+ *
+ * then the constructed map is an overapproximation of
+ *
+ * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i :
+ * d = (\sum_i k_i \delta_i, \sum_i k_i) and
+ * x in dom R and x + d in ran R and
+ * \sum_i k_i >= 1 }
+ */
+static __isl_give isl_map *construct_component(__isl_take isl_space *space,
+ __isl_keep isl_map *map, isl_bool *exact, int project)
+{
+ struct isl_set *domain = NULL;
+ struct isl_set *range = NULL;
+ struct isl_map *app = NULL;
+ struct isl_map *path = NULL;
+ isl_bool overlaps;
+ int check;
+
+ domain = isl_map_domain(isl_map_copy(map));
+ domain = isl_set_coalesce(domain);
+ range = isl_map_range(isl_map_copy(map));
+ range = isl_set_coalesce(range);
+ overlaps = isl_set_overlaps(domain, range);
+ if (overlaps < 0 || !overlaps) {
+ isl_set_free(domain);
+ isl_set_free(range);
+ isl_space_free(space);
+
+ if (overlaps < 0)
+ map = NULL;
+ map = isl_map_copy(map);
+ map = isl_map_add_dims(map, isl_dim_in, 1);
+ map = isl_map_add_dims(map, isl_dim_out, 1);
+ map = set_path_length(map, 1, 1);
+ return map;
+ }
+ app = isl_map_from_domain_and_range(domain, range);
+ app = isl_map_add_dims(app, isl_dim_in, 1);
+ app = isl_map_add_dims(app, isl_dim_out, 1);
+
+ check = exact && *exact == isl_bool_true;
+ path = construct_extended_path(isl_space_copy(space), map,
+ check ? &project : NULL);
+ app = isl_map_intersect(app, path);
+
+ if (check &&
+ (*exact = check_exactness(isl_map_copy(map), isl_map_copy(app),
+ project)) < 0)
+ goto error;
+
+ isl_space_free(space);
+ app = set_path_length(app, 0, 1);
+ return app;
+error:
+ isl_space_free(space);
+ isl_map_free(app);
+ return NULL;
+}
+
+/* Call construct_component and, if "project" is set, project out
+ * the final coordinates.
+ */
+static __isl_give isl_map *construct_projected_component(
+ __isl_take isl_space *space,
+ __isl_keep isl_map *map, isl_bool *exact, int project)
+{
+ isl_map *app;
+ unsigned d;
+
+ if (!space)
+ return NULL;
+ d = isl_space_dim(space, isl_dim_in);
+
+ app = construct_component(space, map, exact, project);
+ if (project) {
+ app = isl_map_project_out(app, isl_dim_in, d - 1, 1);
+ app = isl_map_project_out(app, isl_dim_out, d - 1, 1);
+ }
+ return app;
+}
+
+/* Compute an extended version, i.e., with path lengths, of
+ * an overapproximation of the transitive closure of "bmap"
+ * with path lengths greater than or equal to zero and with
+ * domain and range equal to "dom".
+ */
+static __isl_give isl_map *q_closure(__isl_take isl_space *space,
+ __isl_take isl_set *dom, __isl_keep isl_basic_map *bmap,
+ isl_bool *exact)
+{
+ int project = 1;
+ isl_map *path;
+ isl_map *map;
+ isl_map *app;
+
+ dom = isl_set_add_dims(dom, isl_dim_set, 1);
+ app = isl_map_from_domain_and_range(dom, isl_set_copy(dom));
+ map = isl_map_from_basic_map(isl_basic_map_copy(bmap));
+ path = construct_extended_path(space, map, &project);
+ app = isl_map_intersect(app, path);
+
+ if ((*exact = check_exactness(map, isl_map_copy(app), project)) < 0)
+ goto error;
+
+ return app;
+error:
+ isl_map_free(app);
+ return NULL;
+}
+
+/* Check whether qc has any elements of length at least one
+ * with domain and/or range outside of dom and ran.
+ */
+static isl_bool has_spurious_elements(__isl_keep isl_map *qc,
+ __isl_keep isl_set *dom, __isl_keep isl_set *ran)
+{
+ isl_set *s;
+ isl_bool subset;
+ isl_size d;
+
+ d = isl_map_dim(qc, isl_dim_in);
+ if (d < 0 || !dom || !ran)
+ return isl_bool_error;
+
+ qc = isl_map_copy(qc);
+ qc = set_path_length(qc, 0, 1);
+ qc = isl_map_project_out(qc, isl_dim_in, d - 1, 1);
+ qc = isl_map_project_out(qc, isl_dim_out, d - 1, 1);
+
+ s = isl_map_domain(isl_map_copy(qc));
+ subset = isl_set_is_subset(s, dom);
+ isl_set_free(s);
+ if (subset < 0)
+ goto error;
+ if (!subset) {
+ isl_map_free(qc);
+ return isl_bool_true;
+ }
+
+ s = isl_map_range(qc);
+ subset = isl_set_is_subset(s, ran);
+ isl_set_free(s);
+
+ return isl_bool_not(subset);
+error:
+ isl_map_free(qc);
+ return isl_bool_error;
+}
+
+#define LEFT 2
+#define RIGHT 1
+
+/* For each basic map in "map", except i, check whether it combines
+ * with the transitive closure that is reflexive on C combines
+ * to the left and to the right.
+ *
+ * In particular, if
+ *
+ * dom map_j \subseteq C
+ *
+ * then right[j] is set to 1. Otherwise, if
+ *
+ * ran map_i \cap dom map_j = \emptyset
+ *
+ * then right[j] is set to 0. Otherwise, composing to the right
+ * is impossible.
+ *
+ * Similar, for composing to the left, we have if
+ *
+ * ran map_j \subseteq C
+ *
+ * then left[j] is set to 1. Otherwise, if
+ *
+ * dom map_i \cap ran map_j = \emptyset
+ *
+ * then left[j] is set to 0. Otherwise, composing to the left
+ * is impossible.
+ *
+ * The return value is or'd with LEFT if composing to the left
+ * is possible and with RIGHT if composing to the right is possible.
+ */
+static int composability(__isl_keep isl_set *C, int i,
+ isl_set **dom, isl_set **ran, int *left, int *right,
+ __isl_keep isl_map *map)
+{
+ int j;
+ int ok;
+
+ ok = LEFT | RIGHT;
+ for (j = 0; j < map->n && ok; ++j) {
+ isl_bool overlaps, subset;
+ if (j == i)
+ continue;
+
+ if (ok & RIGHT) {
+ if (!dom[j])
+ dom[j] = isl_set_from_basic_set(
+ isl_basic_map_domain(
+ isl_basic_map_copy(map->p[j])));
+ if (!dom[j])
+ return -1;
+ overlaps = isl_set_overlaps(ran[i], dom[j]);
+ if (overlaps < 0)
+ return -1;
+ if (!overlaps)
+ right[j] = 0;
+ else {
+ subset = isl_set_is_subset(dom[j], C);
+ if (subset < 0)
+ return -1;
+ if (subset)
+ right[j] = 1;
+ else
+ ok &= ~RIGHT;
+ }
+ }
+
+ if (ok & LEFT) {
+ if (!ran[j])
+ ran[j] = isl_set_from_basic_set(
+ isl_basic_map_range(
+ isl_basic_map_copy(map->p[j])));
+ if (!ran[j])
+ return -1;
+ overlaps = isl_set_overlaps(dom[i], ran[j]);
+ if (overlaps < 0)
+ return -1;
+ if (!overlaps)
+ left[j] = 0;
+ else {
+ subset = isl_set_is_subset(ran[j], C);
+ if (subset < 0)
+ return -1;
+ if (subset)
+ left[j] = 1;
+ else
+ ok &= ~LEFT;
+ }
+ }
+ }
+
+ return ok;
+}
+
+static __isl_give isl_map *anonymize(__isl_take isl_map *map)
+{
+ map = isl_map_reset(map, isl_dim_in);
+ map = isl_map_reset(map, isl_dim_out);
+ return map;
+}
+
+/* Return a map that is a union of the basic maps in "map", except i,
+ * composed to left and right with qc based on the entries of "left"
+ * and "right".
+ */
+static __isl_give isl_map *compose(__isl_keep isl_map *map, int i,
+ __isl_take isl_map *qc, int *left, int *right)
+{
+ int j;
+ isl_map *comp;
+
+ comp = isl_map_empty(isl_map_get_space(map));
+ for (j = 0; j < map->n; ++j) {
+ isl_map *map_j;
+
+ if (j == i)
+ continue;
+
+ map_j = isl_map_from_basic_map(isl_basic_map_copy(map->p[j]));
+ map_j = anonymize(map_j);
+ if (left && left[j])
+ map_j = isl_map_apply_range(map_j, isl_map_copy(qc));
+ if (right && right[j])
+ map_j = isl_map_apply_range(isl_map_copy(qc), map_j);
+ comp = isl_map_union(comp, map_j);
+ }
+
+ comp = isl_map_compute_divs(comp);
+ comp = isl_map_coalesce(comp);
+
+ isl_map_free(qc);
+
+ return comp;
+}
+
+/* Compute the transitive closure of "map" incrementally by
+ * computing
+ *
+ * map_i^+ \cup qc^+
+ *
+ * or
+ *
+ * map_i^+ \cup ((id \cup map_i^) \circ qc^+)
+ *
+ * or
+ *
+ * map_i^+ \cup (qc^+ \circ (id \cup map_i^))
+ *
+ * depending on whether left or right are NULL.
+ */
+static __isl_give isl_map *compute_incremental(
+ __isl_take isl_space *space, __isl_keep isl_map *map,
+ int i, __isl_take isl_map *qc, int *left, int *right, isl_bool *exact)
+{
+ isl_map *map_i;
+ isl_map *tc;
+ isl_map *rtc = NULL;
+
+ if (!map)
+ goto error;
+ isl_assert(map->ctx, left || right, goto error);
+
+ map_i = isl_map_from_basic_map(isl_basic_map_copy(map->p[i]));
+ tc = construct_projected_component(isl_space_copy(space), map_i,
+ exact, 1);
+ isl_map_free(map_i);
+
+ if (*exact)
+ qc = isl_map_transitive_closure(qc, exact);
+
+ if (!*exact) {
+ isl_space_free(space);
+ isl_map_free(tc);
+ isl_map_free(qc);
+ return isl_map_universe(isl_map_get_space(map));
+ }
+
+ if (!left || !right)
+ rtc = isl_map_union(isl_map_copy(tc),
+ isl_map_identity(isl_map_get_space(tc)));
+ if (!right)
+ qc = isl_map_apply_range(rtc, qc);
+ if (!left)
+ qc = isl_map_apply_range(qc, rtc);
+ qc = isl_map_union(tc, qc);
+
+ isl_space_free(space);
+
+ return qc;
+error:
+ isl_space_free(space);
+ isl_map_free(qc);
+ return NULL;
+}
+
+/* Given a map "map", try to find a basic map such that
+ * map^+ can be computed as
+ *
+ * map^+ = map_i^+ \cup
+ * \bigcup_j ((map_i^+ \cup Id_C)^+ \circ map_j \circ (map_i^+ \cup Id_C))^+
+ *
+ * with C the simple hull of the domain and range of the input map.
+ * map_i^ \cup Id_C is computed by allowing the path lengths to be zero
+ * and by intersecting domain and range with C.
+ * Of course, we need to check that this is actually equal to map_i^ \cup Id_C.
+ * Also, we only use the incremental computation if all the transitive
+ * closures are exact and if the number of basic maps in the union,
+ * after computing the integer divisions, is smaller than the number
+ * of basic maps in the input map.
+ */
+static isl_bool incremental_on_entire_domain(__isl_keep isl_space *space,
+ __isl_keep isl_map *map,
+ isl_set **dom, isl_set **ran, int *left, int *right,
+ __isl_give isl_map **res)
+{
+ int i;
+ isl_set *C;
+ isl_size d;
+
+ *res = NULL;
+
+ d = isl_map_dim(map, isl_dim_in);
+ if (d < 0)
+ return isl_bool_error;
+
+ C = isl_set_union(isl_map_domain(isl_map_copy(map)),
+ isl_map_range(isl_map_copy(map)));
+ C = isl_set_from_basic_set(isl_set_simple_hull(C));
+ if (!C)
+ return isl_bool_error;
+ if (C->n != 1) {
+ isl_set_free(C);
+ return isl_bool_false;
+ }
+
+ for (i = 0; i < map->n; ++i) {
+ isl_map *qc;
+ isl_bool exact_i;
+ isl_bool spurious;
+ int j;
+ dom[i] = isl_set_from_basic_set(isl_basic_map_domain(
+ isl_basic_map_copy(map->p[i])));
+ ran[i] = isl_set_from_basic_set(isl_basic_map_range(
+ isl_basic_map_copy(map->p[i])));
+ qc = q_closure(isl_space_copy(space), isl_set_copy(C),
+ map->p[i], &exact_i);
+ if (!qc)
+ goto error;
+ if (!exact_i) {
+ isl_map_free(qc);
+ continue;
+ }
+ spurious = has_spurious_elements(qc, dom[i], ran[i]);
+ if (spurious) {
+ isl_map_free(qc);
+ if (spurious < 0)
+ goto error;
+ continue;
+ }
+ qc = isl_map_project_out(qc, isl_dim_in, d, 1);
+ qc = isl_map_project_out(qc, isl_dim_out, d, 1);
+ qc = isl_map_compute_divs(qc);
+ for (j = 0; j < map->n; ++j)
+ left[j] = right[j] = 1;
+ qc = compose(map, i, qc, left, right);
+ if (!qc)
+ goto error;
+ if (qc->n >= map->n) {
+ isl_map_free(qc);
+ continue;
+ }
+ *res = compute_incremental(isl_space_copy(space), map, i, qc,
+ left, right, &exact_i);
+ if (!*res)
+ goto error;
+ if (exact_i)
+ break;
+ isl_map_free(*res);
+ *res = NULL;
+ }
+
+ isl_set_free(C);
+
+ return isl_bool_ok(*res != NULL);
+error:
+ isl_set_free(C);
+ return isl_bool_error;
+}
+
+/* Try and compute the transitive closure of "map" as
+ *
+ * map^+ = map_i^+ \cup
+ * \bigcup_j ((map_i^+ \cup Id_C)^+ \circ map_j \circ (map_i^+ \cup Id_C))^+
+ *
+ * with C either the simple hull of the domain and range of the entire
+ * map or the simple hull of domain and range of map_i.
+ */
+static __isl_give isl_map *incremental_closure(__isl_take isl_space *space,
+ __isl_keep isl_map *map, isl_bool *exact, int project)
+{
+ int i;
+ isl_set **dom = NULL;
+ isl_set **ran = NULL;
+ int *left = NULL;
+ int *right = NULL;
+ isl_set *C;
+ isl_size d;
+ isl_map *res = NULL;
+
+ if (!project)
+ return construct_projected_component(space, map, exact,
+ project);
+
+ if (!map)
+ goto error;
+ if (map->n <= 1)
+ return construct_projected_component(space, map, exact,
+ project);
+
+ d = isl_map_dim(map, isl_dim_in);
+ if (d < 0)
+ goto error;
+
+ dom = isl_calloc_array(map->ctx, isl_set *, map->n);
+ ran = isl_calloc_array(map->ctx, isl_set *, map->n);
+ left = isl_calloc_array(map->ctx, int, map->n);
+ right = isl_calloc_array(map->ctx, int, map->n);
+ if (!ran || !dom || !left || !right)
+ goto error;
+
+ if (incremental_on_entire_domain(space, map, dom, ran, left, right,
+ &res) < 0)
+ goto error;
+
+ for (i = 0; !res && i < map->n; ++i) {
+ isl_map *qc;
+ int comp;
+ isl_bool exact_i, spurious;
+ if (!dom[i])
+ dom[i] = isl_set_from_basic_set(
+ isl_basic_map_domain(
+ isl_basic_map_copy(map->p[i])));
+ if (!dom[i])
+ goto error;
+ if (!ran[i])
+ ran[i] = isl_set_from_basic_set(
+ isl_basic_map_range(
+ isl_basic_map_copy(map->p[i])));
+ if (!ran[i])
+ goto error;
+ C = isl_set_union(isl_set_copy(dom[i]),
+ isl_set_copy(ran[i]));
+ C = isl_set_from_basic_set(isl_set_simple_hull(C));
+ if (!C)
+ goto error;
+ if (C->n != 1) {
+ isl_set_free(C);
+ continue;
+ }
+ comp = composability(C, i, dom, ran, left, right, map);
+ if (!comp || comp < 0) {
+ isl_set_free(C);
+ if (comp < 0)
+ goto error;
+ continue;
+ }
+ qc = q_closure(isl_space_copy(space), C, map->p[i], &exact_i);
+ if (!qc)
+ goto error;
+ if (!exact_i) {
+ isl_map_free(qc);
+ continue;
+ }
+ spurious = has_spurious_elements(qc, dom[i], ran[i]);
+ if (spurious) {
+ isl_map_free(qc);
+ if (spurious < 0)
+ goto error;
+ continue;
+ }
+ qc = isl_map_project_out(qc, isl_dim_in, d, 1);
+ qc = isl_map_project_out(qc, isl_dim_out, d, 1);
+ qc = isl_map_compute_divs(qc);
+ qc = compose(map, i, qc, (comp & LEFT) ? left : NULL,
+ (comp & RIGHT) ? right : NULL);
+ if (!qc)
+ goto error;
+ if (qc->n >= map->n) {
+ isl_map_free(qc);
+ continue;
+ }
+ res = compute_incremental(isl_space_copy(space), map, i, qc,
+ (comp & LEFT) ? left : NULL,
+ (comp & RIGHT) ? right : NULL, &exact_i);
+ if (!res)
+ goto error;
+ if (exact_i)
+ break;
+ isl_map_free(res);
+ res = NULL;
+ }
+
+ for (i = 0; i < map->n; ++i) {
+ isl_set_free(dom[i]);
+ isl_set_free(ran[i]);
+ }
+ free(dom);
+ free(ran);
+ free(left);
+ free(right);
+
+ if (res) {
+ isl_space_free(space);
+ return res;
+ }
+
+ return construct_projected_component(space, map, exact, project);
+error:
+ if (dom)
+ for (i = 0; i < map->n; ++i)
+ isl_set_free(dom[i]);
+ free(dom);
+ if (ran)
+ for (i = 0; i < map->n; ++i)
+ isl_set_free(ran[i]);
+ free(ran);
+ free(left);
+ free(right);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Given an array of sets "set", add "dom" at position "pos"
+ * and search for elements at earlier positions that overlap with "dom".
+ * If any can be found, then merge all of them, together with "dom", into
+ * a single set and assign the union to the first in the array,
+ * which becomes the new group leader for all groups involved in the merge.
+ * During the search, we only consider group leaders, i.e., those with
+ * group[i] = i, as the other sets have already been combined
+ * with one of the group leaders.
+ */
+static int merge(isl_set **set, int *group, __isl_take isl_set *dom, int pos)
+{
+ int i;
+
+ group[pos] = pos;
+ set[pos] = isl_set_copy(dom);
+
+ for (i = pos - 1; i >= 0; --i) {
+ isl_bool o;
+
+ if (group[i] != i)
+ continue;
+
+ o = isl_set_overlaps(set[i], dom);
+ if (o < 0)
+ goto error;
+ if (!o)
+ continue;
+
+ set[i] = isl_set_union(set[i], set[group[pos]]);
+ set[group[pos]] = NULL;
+ if (!set[i])
+ goto error;
+ group[group[pos]] = i;
+ group[pos] = i;
+ }
+
+ isl_set_free(dom);
+ return 0;
+error:
+ isl_set_free(dom);
+ return -1;
+}
+
+/* Construct a map [x] -> [x+1], with parameters prescribed by "space".
+ */
+static __isl_give isl_map *increment(__isl_take isl_space *space)
+{
+ int k;
+ isl_basic_map *bmap;
+ isl_size total;
+
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, 1);
+ space = isl_space_map_from_set(space);
+ bmap = isl_basic_map_alloc_space(space, 0, 1, 0);
+ total = isl_basic_map_dim(bmap, isl_dim_all);
+ k = isl_basic_map_alloc_equality(bmap);
+ if (total < 0 || k < 0)
+ goto error;
+ isl_seq_clr(bmap->eq[k], 1 + total);
+ isl_int_set_si(bmap->eq[k][0], 1);
+ isl_int_set_si(bmap->eq[k][isl_basic_map_offset(bmap, isl_dim_in)], 1);
+ isl_int_set_si(bmap->eq[k][isl_basic_map_offset(bmap, isl_dim_out)], -1);
+ return isl_map_from_basic_map(bmap);
+error:
+ isl_basic_map_free(bmap);
+ return NULL;
+}
+
+/* Replace each entry in the n by n grid of maps by the cross product
+ * with the relation { [i] -> [i + 1] }.
+ */
+static isl_stat add_length(__isl_keep isl_map *map, isl_map ***grid, int n)
+{
+ int i, j;
+ isl_space *space;
+ isl_map *step;
+
+ space = isl_space_params(isl_map_get_space(map));
+ step = increment(space);
+
+ if (!step)
+ return isl_stat_error;
+
+ for (i = 0; i < n; ++i)
+ for (j = 0; j < n; ++j)
+ grid[i][j] = isl_map_product(grid[i][j],
+ isl_map_copy(step));
+
+ isl_map_free(step);
+
+ return isl_stat_ok;
+}
+
+/* The core of the Floyd-Warshall algorithm.
+ * Updates the given n x x matrix of relations in place.
+ *
+ * The algorithm iterates over all vertices. In each step, the whole
+ * matrix is updated to include all paths that go to the current vertex,
+ * possibly stay there a while (including passing through earlier vertices)
+ * and then come back. At the start of each iteration, the diagonal
+ * element corresponding to the current vertex is replaced by its
+ * transitive closure to account for all indirect paths that stay
+ * in the current vertex.
+ */
+static void floyd_warshall_iterate(isl_map ***grid, int n, isl_bool *exact)
+{
+ int r, p, q;
+
+ for (r = 0; r < n; ++r) {
+ isl_bool r_exact;
+ int check = exact && *exact == isl_bool_true;
+ grid[r][r] = isl_map_transitive_closure(grid[r][r],
+ check ? &r_exact : NULL);
+ if (check && !r_exact)
+ *exact = isl_bool_false;
+
+ for (p = 0; p < n; ++p)
+ for (q = 0; q < n; ++q) {
+ isl_map *loop;
+ if (p == r && q == r)
+ continue;
+ loop = isl_map_apply_range(
+ isl_map_copy(grid[p][r]),
+ isl_map_copy(grid[r][q]));
+ grid[p][q] = isl_map_union(grid[p][q], loop);
+ loop = isl_map_apply_range(
+ isl_map_copy(grid[p][r]),
+ isl_map_apply_range(
+ isl_map_copy(grid[r][r]),
+ isl_map_copy(grid[r][q])));
+ grid[p][q] = isl_map_union(grid[p][q], loop);
+ grid[p][q] = isl_map_coalesce(grid[p][q]);
+ }
+ }
+}
+
+/* Given a partition of the domains and ranges of the basic maps in "map",
+ * apply the Floyd-Warshall algorithm with the elements in the partition
+ * as vertices.
+ *
+ * In particular, there are "n" elements in the partition and "group" is
+ * an array of length 2 * map->n with entries in [0,n-1].
+ *
+ * We first construct a matrix of relations based on the partition information,
+ * apply Floyd-Warshall on this matrix of relations and then take the
+ * union of all entries in the matrix as the final result.
+ *
+ * If we are actually computing the power instead of the transitive closure,
+ * i.e., when "project" is not set, then the result should have the
+ * path lengths encoded as the difference between an extra pair of
+ * coordinates. We therefore apply the nested transitive closures
+ * to relations that include these lengths. In particular, we replace
+ * the input relation by the cross product with the unit length relation
+ * { [i] -> [i + 1] }.
+ */
+static __isl_give isl_map *floyd_warshall_with_groups(
+ __isl_take isl_space *space, __isl_keep isl_map *map,
+ isl_bool *exact, int project, int *group, int n)
+{
+ int i, j, k;
+ isl_map ***grid = NULL;
+ isl_map *app;
+
+ if (!map)
+ goto error;
+
+ if (n == 1) {
+ free(group);
+ return incremental_closure(space, map, exact, project);
+ }
+
+ grid = isl_calloc_array(map->ctx, isl_map **, n);
+ if (!grid)
+ goto error;
+ for (i = 0; i < n; ++i) {
+ grid[i] = isl_calloc_array(map->ctx, isl_map *, n);
+ if (!grid[i])
+ goto error;
+ for (j = 0; j < n; ++j)
+ grid[i][j] = isl_map_empty(isl_map_get_space(map));
+ }
+
+ for (k = 0; k < map->n; ++k) {
+ i = group[2 * k];
+ j = group[2 * k + 1];
+ grid[i][j] = isl_map_union(grid[i][j],
+ isl_map_from_basic_map(
+ isl_basic_map_copy(map->p[k])));
+ }
+
+ if (!project && add_length(map, grid, n) < 0)
+ goto error;
+
+ floyd_warshall_iterate(grid, n, exact);
+
+ app = isl_map_empty(isl_map_get_space(grid[0][0]));
+
+ for (i = 0; i < n; ++i) {
+ for (j = 0; j < n; ++j)
+ app = isl_map_union(app, grid[i][j]);
+ free(grid[i]);
+ }
+ free(grid);
+
+ free(group);
+ isl_space_free(space);
+
+ return app;
+error:
+ if (grid)
+ for (i = 0; i < n; ++i) {
+ if (!grid[i])
+ continue;
+ for (j = 0; j < n; ++j)
+ isl_map_free(grid[i][j]);
+ free(grid[i]);
+ }
+ free(grid);
+ free(group);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Partition the domains and ranges of the n basic relations in list
+ * into disjoint cells.
+ *
+ * To find the partition, we simply consider all of the domains
+ * and ranges in turn and combine those that overlap.
+ * "set" contains the partition elements and "group" indicates
+ * to which partition element a given domain or range belongs.
+ * The domain of basic map i corresponds to element 2 * i in these arrays,
+ * while the domain corresponds to element 2 * i + 1.
+ * During the construction group[k] is either equal to k,
+ * in which case set[k] contains the union of all the domains and
+ * ranges in the corresponding group, or is equal to some l < k,
+ * with l another domain or range in the same group.
+ */
+static int *setup_groups(isl_ctx *ctx, __isl_keep isl_basic_map **list, int n,
+ isl_set ***set, int *n_group)
+{
+ int i;
+ int *group = NULL;
+ int g;
+
+ *set = isl_calloc_array(ctx, isl_set *, 2 * n);
+ group = isl_alloc_array(ctx, int, 2 * n);
+
+ if (!*set || !group)
+ goto error;
+
+ for (i = 0; i < n; ++i) {
+ isl_set *dom;
+ dom = isl_set_from_basic_set(isl_basic_map_domain(
+ isl_basic_map_copy(list[i])));
+ if (merge(*set, group, dom, 2 * i) < 0)
+ goto error;
+ dom = isl_set_from_basic_set(isl_basic_map_range(
+ isl_basic_map_copy(list[i])));
+ if (merge(*set, group, dom, 2 * i + 1) < 0)
+ goto error;
+ }
+
+ g = 0;
+ for (i = 0; i < 2 * n; ++i)
+ if (group[i] == i) {
+ if (g != i) {
+ (*set)[g] = (*set)[i];
+ (*set)[i] = NULL;
+ }
+ group[i] = g++;
+ } else
+ group[i] = group[group[i]];
+
+ *n_group = g;
+
+ return group;
+error:
+ if (*set) {
+ for (i = 0; i < 2 * n; ++i)
+ isl_set_free((*set)[i]);
+ free(*set);
+ *set = NULL;
+ }
+ free(group);
+ return NULL;
+}
+
+/* Check if the domains and ranges of the basic maps in "map" can
+ * be partitioned, and if so, apply Floyd-Warshall on the elements
+ * of the partition. Note that we also apply this algorithm
+ * if we want to compute the power, i.e., when "project" is not set.
+ * However, the results are unlikely to be exact since the recursive
+ * calls inside the Floyd-Warshall algorithm typically result in
+ * non-linear path lengths quite quickly.
+ */
+static __isl_give isl_map *floyd_warshall(__isl_take isl_space *space,
+ __isl_keep isl_map *map, isl_bool *exact, int project)
+{
+ int i;
+ isl_set **set = NULL;
+ int *group = NULL;
+ int n;
+
+ if (!map)
+ goto error;
+ if (map->n <= 1)
+ return incremental_closure(space, map, exact, project);
+
+ group = setup_groups(map->ctx, map->p, map->n, &set, &n);
+ if (!group)
+ goto error;
+
+ for (i = 0; i < 2 * map->n; ++i)
+ isl_set_free(set[i]);
+
+ free(set);
+
+ return floyd_warshall_with_groups(space, map, exact, project, group, n);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Structure for representing the nodes of the graph of which
+ * strongly connected components are being computed.
+ *
+ * list contains the actual nodes
+ * check_closed is set if we may have used the fact that
+ * a pair of basic maps can be interchanged
+ */
+struct isl_tc_follows_data {
+ isl_basic_map **list;
+ int check_closed;
+};
+
+/* Check whether in the computation of the transitive closure
+ * "list[i]" (R_1) should follow (or be part of the same component as)
+ * "list[j]" (R_2).
+ *
+ * That is check whether
+ *
+ * R_1 \circ R_2
+ *
+ * is a subset of
+ *
+ * R_2 \circ R_1
+ *
+ * If so, then there is no reason for R_1 to immediately follow R_2
+ * in any path.
+ *
+ * *check_closed is set if the subset relation holds while
+ * R_1 \circ R_2 is not empty.
+ */
+static isl_bool basic_map_follows(int i, int j, void *user)
+{
+ struct isl_tc_follows_data *data = user;
+ struct isl_map *map12 = NULL;
+ struct isl_map *map21 = NULL;
+ isl_bool applies, subset;
+
+ applies = isl_basic_map_applies_range(data->list[j], data->list[i]);
+ if (applies < 0)
+ return isl_bool_error;
+ if (!applies)
+ return isl_bool_false;
+
+ map21 = isl_map_from_basic_map(
+ isl_basic_map_apply_range(
+ isl_basic_map_copy(data->list[j]),
+ isl_basic_map_copy(data->list[i])));
+ subset = isl_map_is_empty(map21);
+ if (subset < 0)
+ goto error;
+ if (subset) {
+ isl_map_free(map21);
+ return isl_bool_false;
+ }
+
+ if (!isl_basic_map_is_transformation(data->list[i]) ||
+ !isl_basic_map_is_transformation(data->list[j])) {
+ isl_map_free(map21);
+ return isl_bool_true;
+ }
+
+ map12 = isl_map_from_basic_map(
+ isl_basic_map_apply_range(
+ isl_basic_map_copy(data->list[i]),
+ isl_basic_map_copy(data->list[j])));
+
+ subset = isl_map_is_subset(map21, map12);
+
+ isl_map_free(map12);
+ isl_map_free(map21);
+
+ if (subset)
+ data->check_closed = 1;
+
+ return isl_bool_not(subset);
+error:
+ isl_map_free(map21);
+ return isl_bool_error;
+}
+
+/* Given a union of basic maps R = \cup_i R_i \subseteq D \times D
+ * and a dimension specification (Z^{n+1} -> Z^{n+1}),
+ * construct a map that is an overapproximation of the map
+ * that takes an element from the dom R \times Z to an
+ * element from ran R \times Z, such that the first n coordinates of the
+ * difference between them is a sum of differences between images
+ * and pre-images in one of the R_i and such that the last coordinate
+ * is equal to the number of steps taken.
+ * If "project" is set, then these final coordinates are not included,
+ * i.e., a relation of type Z^n -> Z^n is returned.
+ * That is, let
+ *
+ * \Delta_i = { y - x | (x, y) in R_i }
+ *
+ * then the constructed map is an overapproximation of
+ *
+ * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i :
+ * d = (\sum_i k_i \delta_i, \sum_i k_i) and
+ * x in dom R and x + d in ran R }
+ *
+ * or
+ *
+ * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i :
+ * d = (\sum_i k_i \delta_i) and
+ * x in dom R and x + d in ran R }
+ *
+ * if "project" is set.
+ *
+ * We first split the map into strongly connected components, perform
+ * the above on each component and then join the results in the correct
+ * order, at each join also taking in the union of both arguments
+ * to allow for paths that do not go through one of the two arguments.
+ */
+static __isl_give isl_map *construct_power_components(
+ __isl_take isl_space *space, __isl_keep isl_map *map, isl_bool *exact,
+ int project)
+{
+ int i, n, c;
+ struct isl_map *path = NULL;
+ struct isl_tc_follows_data data;
+ struct isl_tarjan_graph *g = NULL;
+ isl_bool *orig_exact;
+ isl_bool local_exact;
+
+ if (!map)
+ goto error;
+ if (map->n <= 1)
+ return floyd_warshall(space, map, exact, project);
+
+ data.list = map->p;
+ data.check_closed = 0;
+ g = isl_tarjan_graph_init(map->ctx, map->n, &basic_map_follows, &data);
+ if (!g)
+ goto error;
+
+ orig_exact = exact;
+ if (data.check_closed && !exact)
+ exact = &local_exact;
+
+ c = 0;
+ i = 0;
+ n = map->n;
+ if (project)
+ path = isl_map_empty(isl_map_get_space(map));
+ else
+ path = isl_map_empty(isl_space_copy(space));
+ path = anonymize(path);
+ while (n) {
+ struct isl_map *comp;
+ isl_map *path_comp, *path_comb;
+ comp = isl_map_alloc_space(isl_map_get_space(map), n, 0);
+ while (g->order[i] != -1) {
+ comp = isl_map_add_basic_map(comp,
+ isl_basic_map_copy(map->p[g->order[i]]));
+ --n;
+ ++i;
+ }
+ path_comp = floyd_warshall(isl_space_copy(space),
+ comp, exact, project);
+ path_comp = anonymize(path_comp);
+ path_comb = isl_map_apply_range(isl_map_copy(path),
+ isl_map_copy(path_comp));
+ path = isl_map_union(path, path_comp);
+ path = isl_map_union(path, path_comb);
+ isl_map_free(comp);
+ ++i;
+ ++c;
+ }
+
+ if (c > 1 && data.check_closed && !*exact) {
+ isl_bool closed;
+
+ closed = isl_map_is_transitively_closed(path);
+ if (closed < 0)
+ goto error;
+ if (!closed) {
+ isl_tarjan_graph_free(g);
+ isl_map_free(path);
+ return floyd_warshall(space, map, orig_exact, project);
+ }
+ }
+
+ isl_tarjan_graph_free(g);
+ isl_space_free(space);
+
+ return path;
+error:
+ isl_tarjan_graph_free(g);
+ isl_space_free(space);
+ isl_map_free(path);
+ return NULL;
+}
+
+/* Given a union of basic maps R = \cup_i R_i \subseteq D \times D,
+ * construct a map that is an overapproximation of the map
+ * that takes an element from the space D to another
+ * element from the same space, such that the difference between
+ * them is a strictly positive sum of differences between images
+ * and pre-images in one of the R_i.
+ * The number of differences in the sum is equated to parameter "param".
+ * That is, let
+ *
+ * \Delta_i = { y - x | (x, y) in R_i }
+ *
+ * then the constructed map is an overapproximation of
+ *
+ * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i :
+ * d = \sum_i k_i \delta_i and k = \sum_i k_i > 0 }
+ * or
+ *
+ * { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i :
+ * d = \sum_i k_i \delta_i and \sum_i k_i > 0 }
+ *
+ * if "project" is set.
+ *
+ * If "project" is not set, then
+ * we construct an extended mapping with an extra coordinate
+ * that indicates the number of steps taken. In particular,
+ * the difference in the last coordinate is equal to the number
+ * of steps taken to move from a domain element to the corresponding
+ * image element(s).
+ */
+static __isl_give isl_map *construct_power(__isl_keep isl_map *map,
+ isl_bool *exact, int project)
+{
+ struct isl_map *app = NULL;
+ isl_space *space = NULL;
+
+ if (!map)
+ return NULL;
+
+ space = isl_map_get_space(map);
+
+ space = isl_space_add_dims(space, isl_dim_in, 1);
+ space = isl_space_add_dims(space, isl_dim_out, 1);
+
+ app = construct_power_components(isl_space_copy(space), map,
+ exact, project);
+
+ isl_space_free(space);
+
+ return app;
+}
+
+/* Compute the positive powers of "map", or an overapproximation.
+ * If the result is exact, then *exact is set to 1.
+ *
+ * If project is set, then we are actually interested in the transitive
+ * closure, so we can use a more relaxed exactness check.
+ * The lengths of the paths are also projected out instead of being
+ * encoded as the difference between an extra pair of final coordinates.
+ */
+static __isl_give isl_map *map_power(__isl_take isl_map *map,
+ isl_bool *exact, int project)
+{
+ struct isl_map *app = NULL;
+
+ if (exact)
+ *exact = isl_bool_true;
+
+ if (isl_map_check_transformation(map) < 0)
+ return isl_map_free(map);
+
+ app = construct_power(map, exact, project);
+
+ isl_map_free(map);
+ return app;
+}
+
+/* Compute the positive powers of "map", or an overapproximation.
+ * The result maps the exponent to a nested copy of the corresponding power.
+ * If the result is exact, then *exact is set to 1.
+ * map_power constructs an extended relation with the path lengths
+ * encoded as the difference between the final coordinates.
+ * In the final step, this difference is equated to an extra parameter
+ * and made positive. The extra coordinates are subsequently projected out
+ * and the parameter is turned into the domain of the result.
+ */
+__isl_give isl_map *isl_map_power(__isl_take isl_map *map, isl_bool *exact)
+{
+ isl_space *target_space;
+ isl_space *space;
+ isl_map *diff;
+ isl_size d;
+ isl_size param;
+
+ d = isl_map_dim(map, isl_dim_in);
+ param = isl_map_dim(map, isl_dim_param);
+ if (d < 0 || param < 0)
+ return isl_map_free(map);
+
+ map = isl_map_compute_divs(map);
+ map = isl_map_coalesce(map);
+
+ if (isl_map_plain_is_empty(map)) {
+ map = isl_map_from_range(isl_map_wrap(map));
+ map = isl_map_add_dims(map, isl_dim_in, 1);
+ map = isl_map_set_dim_name(map, isl_dim_in, 0, "k");
+ return map;
+ }
+
+ target_space = isl_map_get_space(map);
+ target_space = isl_space_from_range(isl_space_wrap(target_space));
+ target_space = isl_space_add_dims(target_space, isl_dim_in, 1);
+ target_space = isl_space_set_dim_name(target_space, isl_dim_in, 0, "k");
+
+ map = map_power(map, exact, 0);
+
+ map = isl_map_add_dims(map, isl_dim_param, 1);
+ space = isl_map_get_space(map);
+ diff = equate_parameter_to_length(space, param);
+ map = isl_map_intersect(map, diff);
+ map = isl_map_project_out(map, isl_dim_in, d, 1);
+ map = isl_map_project_out(map, isl_dim_out, d, 1);
+ map = isl_map_from_range(isl_map_wrap(map));
+ map = isl_map_move_dims(map, isl_dim_in, 0, isl_dim_param, param, 1);
+
+ map = isl_map_reset_space(map, target_space);
+
+ return map;
+}
+
+/* Compute a relation that maps each element in the range of the input
+ * relation to the lengths of all paths composed of edges in the input
+ * relation that end up in the given range element.
+ * The result may be an overapproximation, in which case *exact is set to 0.
+ * The resulting relation is very similar to the power relation.
+ * The difference are that the domain has been projected out, the
+ * range has become the domain and the exponent is the range instead
+ * of a parameter.
+ */
+__isl_give isl_map *isl_map_reaching_path_lengths(__isl_take isl_map *map,
+ isl_bool *exact)
+{
+ isl_space *space;
+ isl_map *diff;
+ isl_size d;
+ isl_size param;
+
+ d = isl_map_dim(map, isl_dim_in);
+ param = isl_map_dim(map, isl_dim_param);
+ if (d < 0 || param < 0)
+ return isl_map_free(map);
+
+ map = isl_map_compute_divs(map);
+ map = isl_map_coalesce(map);
+
+ if (isl_map_plain_is_empty(map)) {
+ if (exact)
+ *exact = isl_bool_true;
+ map = isl_map_project_out(map, isl_dim_out, 0, d);
+ map = isl_map_add_dims(map, isl_dim_out, 1);
+ return map;
+ }
+
+ map = map_power(map, exact, 0);
+
+ map = isl_map_add_dims(map, isl_dim_param, 1);
+ space = isl_map_get_space(map);
+ diff = equate_parameter_to_length(space, param);
+ map = isl_map_intersect(map, diff);
+ map = isl_map_project_out(map, isl_dim_in, 0, d + 1);
+ map = isl_map_project_out(map, isl_dim_out, d, 1);
+ map = isl_map_reverse(map);
+ map = isl_map_move_dims(map, isl_dim_out, 0, isl_dim_param, param, 1);
+
+ return map;
+}
+
+/* Given a map, compute the smallest superset of this map that is of the form
+ *
+ * { i -> j : L <= j - i <= U and exists a_p: j_p - i_p = M_p a_p }
+ *
+ * (where p ranges over the (non-parametric) dimensions),
+ * compute the transitive closure of this map, i.e.,
+ *
+ * { i -> j : exists k > 0:
+ * k L <= j - i <= k U and exists a: j_p - i_p = M_p a_p }
+ *
+ * and intersect domain and range of this transitive closure with
+ * the given domain and range.
+ *
+ * If with_id is set, then try to include as much of the identity mapping
+ * as possible, by computing
+ *
+ * { i -> j : exists k >= 0:
+ * k L <= j - i <= k U and exists a: j_p - i_p = M_p a_p }
+ *
+ * instead (i.e., allow k = 0).
+ *
+ * In practice, we compute the difference set
+ *
+ * delta = { j - i | i -> j in map },
+ *
+ * look for stride constraint on the individual dimensions and compute
+ * (constant) lower and upper bounds for each individual dimension,
+ * adding a constraint for each bound not equal to infinity.
+ */
+static __isl_give isl_map *box_closure_on_domain(__isl_take isl_map *map,
+ __isl_take isl_set *dom, __isl_take isl_set *ran, int with_id)
+{
+ int i;
+ int k;
+ unsigned d;
+ unsigned nparam;
+ unsigned total;
+ isl_space *space;
+ isl_set *delta;
+ isl_map *app = NULL;
+ isl_basic_set *aff = NULL;
+ isl_basic_map *bmap = NULL;
+ isl_vec *obj = NULL;
+ isl_int opt;
+
+ isl_int_init(opt);
+
+ delta = isl_map_deltas(isl_map_copy(map));
+
+ aff = isl_set_affine_hull(isl_set_copy(delta));
+ if (!aff)
+ goto error;
+ space = isl_map_get_space(map);
+ d = isl_space_dim(space, isl_dim_in);
+ nparam = isl_space_dim(space, isl_dim_param);
+ total = isl_space_dim(space, isl_dim_all);
+ bmap = isl_basic_map_alloc_space(space,
+ aff->n_div + 1, aff->n_div, 2 * d + 1);
+ for (i = 0; i < aff->n_div + 1; ++i) {
+ k = isl_basic_map_alloc_div(bmap);
+ if (k < 0)
+ goto error;
+ isl_int_set_si(bmap->div[k][0], 0);
+ }
+ for (i = 0; i < aff->n_eq; ++i) {
+ if (!isl_basic_set_eq_is_stride(aff, i))
+ continue;
+ k = isl_basic_map_alloc_equality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bmap->eq[k], 1 + nparam);
+ isl_seq_cpy(bmap->eq[k] + 1 + nparam + d,
+ aff->eq[i] + 1 + nparam, d);
+ isl_seq_neg(bmap->eq[k] + 1 + nparam,
+ aff->eq[i] + 1 + nparam, d);
+ isl_seq_cpy(bmap->eq[k] + 1 + nparam + 2 * d,
+ aff->eq[i] + 1 + nparam + d, aff->n_div);
+ isl_int_set_si(bmap->eq[k][1 + total + aff->n_div], 0);
+ }
+ obj = isl_vec_alloc(map->ctx, 1 + nparam + d);
+ if (!obj)
+ goto error;
+ isl_seq_clr(obj->el, 1 + nparam + d);
+ for (i = 0; i < d; ++ i) {
+ enum isl_lp_result res;
+
+ isl_int_set_si(obj->el[1 + nparam + i], 1);
+
+ res = isl_set_solve_lp(delta, 0, obj->el, map->ctx->one, &opt,
+ NULL, NULL);
+ if (res == isl_lp_error)
+ goto error;
+ if (res == isl_lp_ok) {
+ k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[k],
+ 1 + nparam + 2 * d + bmap->n_div);
+ isl_int_set_si(bmap->ineq[k][1 + nparam + i], -1);
+ isl_int_set_si(bmap->ineq[k][1 + nparam + d + i], 1);
+ isl_int_neg(bmap->ineq[k][1 + nparam + 2 * d + aff->n_div], opt);
+ }
+
+ res = isl_set_solve_lp(delta, 1, obj->el, map->ctx->one, &opt,
+ NULL, NULL);
+ if (res == isl_lp_error)
+ goto error;
+ if (res == isl_lp_ok) {
+ k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[k],
+ 1 + nparam + 2 * d + bmap->n_div);
+ isl_int_set_si(bmap->ineq[k][1 + nparam + i], 1);
+ isl_int_set_si(bmap->ineq[k][1 + nparam + d + i], -1);
+ isl_int_set(bmap->ineq[k][1 + nparam + 2 * d + aff->n_div], opt);
+ }
+
+ isl_int_set_si(obj->el[1 + nparam + i], 0);
+ }
+ k = isl_basic_map_alloc_inequality(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_clr(bmap->ineq[k],
+ 1 + nparam + 2 * d + bmap->n_div);
+ if (!with_id)
+ isl_int_set_si(bmap->ineq[k][0], -1);
+ isl_int_set_si(bmap->ineq[k][1 + nparam + 2 * d + aff->n_div], 1);
+
+ app = isl_map_from_domain_and_range(dom, ran);
+
+ isl_vec_free(obj);
+ isl_basic_set_free(aff);
+ isl_map_free(map);
+ bmap = isl_basic_map_finalize(bmap);
+ isl_set_free(delta);
+ isl_int_clear(opt);
+
+ map = isl_map_from_basic_map(bmap);
+ map = isl_map_intersect(map, app);
+
+ return map;
+error:
+ isl_vec_free(obj);
+ isl_basic_map_free(bmap);
+ isl_basic_set_free(aff);
+ isl_set_free(dom);
+ isl_set_free(ran);
+ isl_map_free(map);
+ isl_set_free(delta);
+ isl_int_clear(opt);
+ return NULL;
+}
+
+/* Given a map, compute the smallest superset of this map that is of the form
+ *
+ * { i -> j : L <= j - i <= U and exists a_p: j_p - i_p = M_p a_p }
+ *
+ * (where p ranges over the (non-parametric) dimensions),
+ * compute the transitive closure of this map, i.e.,
+ *
+ * { i -> j : exists k > 0:
+ * k L <= j - i <= k U and exists a: j_p - i_p = M_p a_p }
+ *
+ * and intersect domain and range of this transitive closure with
+ * domain and range of the original map.
+ */
+static __isl_give isl_map *box_closure(__isl_take isl_map *map)
+{
+ isl_set *domain;
+ isl_set *range;
+
+ domain = isl_map_domain(isl_map_copy(map));
+ domain = isl_set_coalesce(domain);
+ range = isl_map_range(isl_map_copy(map));
+ range = isl_set_coalesce(range);
+
+ return box_closure_on_domain(map, domain, range, 0);
+}
+
+/* Given a map, compute the smallest superset of this map that is of the form
+ *
+ * { i -> j : L <= j - i <= U and exists a_p: j_p - i_p = M_p a_p }
+ *
+ * (where p ranges over the (non-parametric) dimensions),
+ * compute the transitive and partially reflexive closure of this map, i.e.,
+ *
+ * { i -> j : exists k >= 0:
+ * k L <= j - i <= k U and exists a: j_p - i_p = M_p a_p }
+ *
+ * and intersect domain and range of this transitive closure with
+ * the given domain.
+ */
+static __isl_give isl_map *box_closure_with_identity(__isl_take isl_map *map,
+ __isl_take isl_set *dom)
+{
+ return box_closure_on_domain(map, dom, isl_set_copy(dom), 1);
+}
+
+/* Check whether app is the transitive closure of map.
+ * In particular, check that app is acyclic and, if so,
+ * check that
+ *
+ * app \subset (map \cup (map \circ app))
+ */
+static isl_bool check_exactness_omega(__isl_keep isl_map *map,
+ __isl_keep isl_map *app)
+{
+ isl_set *delta;
+ int i;
+ isl_bool is_empty, is_exact;
+ isl_size d;
+ isl_map *test;
+
+ delta = isl_map_deltas(isl_map_copy(app));
+ d = isl_set_dim(delta, isl_dim_set);
+ if (d < 0)
+ delta = isl_set_free(delta);
+ for (i = 0; i < d; ++i)
+ delta = isl_set_fix_si(delta, isl_dim_set, i, 0);
+ is_empty = isl_set_is_empty(delta);
+ isl_set_free(delta);
+ if (is_empty < 0 || !is_empty)
+ return is_empty;
+
+ test = isl_map_apply_range(isl_map_copy(app), isl_map_copy(map));
+ test = isl_map_union(test, isl_map_copy(map));
+ is_exact = isl_map_is_subset(app, test);
+ isl_map_free(test);
+
+ return is_exact;
+}
+
+/* Check if basic map M_i can be combined with all the other
+ * basic maps such that
+ *
+ * (\cup_j M_j)^+
+ *
+ * can be computed as
+ *
+ * M_i \cup (\cup_{j \ne i} M_i^* \circ M_j \circ M_i^*)^+
+ *
+ * In particular, check if we can compute a compact representation
+ * of
+ *
+ * M_i^* \circ M_j \circ M_i^*
+ *
+ * for each j != i.
+ * Let M_i^? be an extension of M_i^+ that allows paths
+ * of length zero, i.e., the result of box_closure(., 1).
+ * The criterion, as proposed by Kelly et al., is that
+ * id = M_i^? - M_i^+ can be represented as a basic map
+ * and that
+ *
+ * id \circ M_j \circ id = M_j
+ *
+ * for each j != i.
+ *
+ * If this function returns 1, then tc and qc are set to
+ * M_i^+ and M_i^?, respectively.
+ */
+static int can_be_split_off(__isl_keep isl_map *map, int i,
+ __isl_give isl_map **tc, __isl_give isl_map **qc)
+{
+ isl_map *map_i, *id = NULL;
+ int j = -1;
+ isl_set *C;
+
+ *tc = NULL;
+ *qc = NULL;
+
+ C = isl_set_union(isl_map_domain(isl_map_copy(map)),
+ isl_map_range(isl_map_copy(map)));
+ C = isl_set_from_basic_set(isl_set_simple_hull(C));
+ if (!C)
+ goto error;
+
+ map_i = isl_map_from_basic_map(isl_basic_map_copy(map->p[i]));
+ *tc = box_closure(isl_map_copy(map_i));
+ *qc = box_closure_with_identity(map_i, C);
+ id = isl_map_subtract(isl_map_copy(*qc), isl_map_copy(*tc));
+
+ if (!id || !*qc)
+ goto error;
+ if (id->n != 1 || (*qc)->n != 1)
+ goto done;
+
+ for (j = 0; j < map->n; ++j) {
+ isl_map *map_j, *test;
+ int is_ok;
+
+ if (i == j)
+ continue;
+ map_j = isl_map_from_basic_map(
+ isl_basic_map_copy(map->p[j]));
+ test = isl_map_apply_range(isl_map_copy(id),
+ isl_map_copy(map_j));
+ test = isl_map_apply_range(test, isl_map_copy(id));
+ is_ok = isl_map_is_equal(test, map_j);
+ isl_map_free(map_j);
+ isl_map_free(test);
+ if (is_ok < 0)
+ goto error;
+ if (!is_ok)
+ break;
+ }
+
+done:
+ isl_map_free(id);
+ if (j == map->n)
+ return 1;
+
+ isl_map_free(*qc);
+ isl_map_free(*tc);
+ *qc = NULL;
+ *tc = NULL;
+
+ return 0;
+error:
+ isl_map_free(id);
+ isl_map_free(*qc);
+ isl_map_free(*tc);
+ *qc = NULL;
+ *tc = NULL;
+ return -1;
+}
+
+static __isl_give isl_map *box_closure_with_check(__isl_take isl_map *map,
+ isl_bool *exact)
+{
+ isl_map *app;
+
+ app = box_closure(isl_map_copy(map));
+ if (exact) {
+ isl_bool is_exact = check_exactness_omega(map, app);
+
+ if (is_exact < 0)
+ app = isl_map_free(app);
+ else
+ *exact = is_exact;
+ }
+
+ isl_map_free(map);
+ return app;
+}
+
+/* Compute an overapproximation of the transitive closure of "map"
+ * using a variation of the algorithm from
+ * "Transitive Closure of Infinite Graphs and its Applications"
+ * by Kelly et al.
+ *
+ * We first check whether we can can split of any basic map M_i and
+ * compute
+ *
+ * (\cup_j M_j)^+
+ *
+ * as
+ *
+ * M_i \cup (\cup_{j \ne i} M_i^* \circ M_j \circ M_i^*)^+
+ *
+ * using a recursive call on the remaining map.
+ *
+ * If not, we simply call box_closure on the whole map.
+ */
+static __isl_give isl_map *transitive_closure_omega(__isl_take isl_map *map,
+ isl_bool *exact)
+{
+ int i, j;
+ isl_bool exact_i;
+ isl_map *app;
+
+ if (!map)
+ return NULL;
+ if (map->n == 1)
+ return box_closure_with_check(map, exact);
+
+ for (i = 0; i < map->n; ++i) {
+ int ok;
+ isl_map *qc, *tc;
+ ok = can_be_split_off(map, i, &tc, &qc);
+ if (ok < 0)
+ goto error;
+ if (!ok)
+ continue;
+
+ app = isl_map_alloc_space(isl_map_get_space(map), map->n - 1, 0);
+
+ for (j = 0; j < map->n; ++j) {
+ if (j == i)
+ continue;
+ app = isl_map_add_basic_map(app,
+ isl_basic_map_copy(map->p[j]));
+ }
+
+ app = isl_map_apply_range(isl_map_copy(qc), app);
+ app = isl_map_apply_range(app, qc);
+
+ app = isl_map_union(tc, transitive_closure_omega(app, NULL));
+ exact_i = check_exactness_omega(map, app);
+ if (exact_i == isl_bool_true) {
+ if (exact)
+ *exact = exact_i;
+ isl_map_free(map);
+ return app;
+ }
+ isl_map_free(app);
+ if (exact_i < 0)
+ goto error;
+ }
+
+ return box_closure_with_check(map, exact);
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+/* Compute the transitive closure of "map", or an overapproximation.
+ * If the result is exact, then *exact is set to 1.
+ * Simply use map_power to compute the powers of map, but tell
+ * it to project out the lengths of the paths instead of equating
+ * the length to a parameter.
+ */
+__isl_give isl_map *isl_map_transitive_closure(__isl_take isl_map *map,
+ isl_bool *exact)
+{
+ isl_space *target_dim;
+ isl_bool closed;
+
+ if (!map)
+ goto error;
+
+ if (map->ctx->opt->closure == ISL_CLOSURE_BOX)
+ return transitive_closure_omega(map, exact);
+
+ map = isl_map_compute_divs(map);
+ map = isl_map_coalesce(map);
+ closed = isl_map_is_transitively_closed(map);
+ if (closed < 0)
+ goto error;
+ if (closed) {
+ if (exact)
+ *exact = isl_bool_true;
+ return map;
+ }
+
+ target_dim = isl_map_get_space(map);
+ map = map_power(map, exact, 1);
+ map = isl_map_reset_space(map, target_dim);
+
+ return map;
+error:
+ isl_map_free(map);
+ return NULL;
+}
+
+static isl_stat inc_count(__isl_take isl_map *map, void *user)
+{
+ int *n = user;
+
+ *n += map->n;
+
+ isl_map_free(map);
+
+ return isl_stat_ok;
+}
+
+static isl_stat collect_basic_map(__isl_take isl_map *map, void *user)
+{
+ int i;
+ isl_basic_map ***next = user;
+
+ for (i = 0; i < map->n; ++i) {
+ **next = isl_basic_map_copy(map->p[i]);
+ if (!**next)
+ goto error;
+ (*next)++;
+ }
+
+ isl_map_free(map);
+ return isl_stat_ok;
+error:
+ isl_map_free(map);
+ return isl_stat_error;
+}
+
+/* Perform Floyd-Warshall on the given list of basic relations.
+ * The basic relations may live in different dimensions,
+ * but basic relations that get assigned to the diagonal of the
+ * grid have domains and ranges of the same dimension and so
+ * the standard algorithm can be used because the nested transitive
+ * closures are only applied to diagonal elements and because all
+ * compositions are performed on relations with compatible domains and ranges.
+ */
+static __isl_give isl_union_map *union_floyd_warshall_on_list(isl_ctx *ctx,
+ __isl_keep isl_basic_map **list, int n, isl_bool *exact)
+{
+ int i, j, k;
+ int n_group;
+ int *group = NULL;
+ isl_set **set = NULL;
+ isl_map ***grid = NULL;
+ isl_union_map *app;
+
+ group = setup_groups(ctx, list, n, &set, &n_group);
+ if (!group)
+ goto error;
+
+ grid = isl_calloc_array(ctx, isl_map **, n_group);
+ if (!grid)
+ goto error;
+ for (i = 0; i < n_group; ++i) {
+ grid[i] = isl_calloc_array(ctx, isl_map *, n_group);
+ if (!grid[i])
+ goto error;
+ for (j = 0; j < n_group; ++j) {
+ isl_space *space1, *space2, *space;
+ space1 = isl_space_reverse(isl_set_get_space(set[i]));
+ space2 = isl_set_get_space(set[j]);
+ space = isl_space_join(space1, space2);
+ grid[i][j] = isl_map_empty(space);
+ }
+ }
+
+ for (k = 0; k < n; ++k) {
+ i = group[2 * k];
+ j = group[2 * k + 1];
+ grid[i][j] = isl_map_union(grid[i][j],
+ isl_map_from_basic_map(
+ isl_basic_map_copy(list[k])));
+ }
+
+ floyd_warshall_iterate(grid, n_group, exact);
+
+ app = isl_union_map_empty(isl_map_get_space(grid[0][0]));
+
+ for (i = 0; i < n_group; ++i) {
+ for (j = 0; j < n_group; ++j)
+ app = isl_union_map_add_map(app, grid[i][j]);
+ free(grid[i]);
+ }
+ free(grid);
+
+ for (i = 0; i < 2 * n; ++i)
+ isl_set_free(set[i]);
+ free(set);
+
+ free(group);
+ return app;
+error:
+ if (grid)
+ for (i = 0; i < n_group; ++i) {
+ if (!grid[i])
+ continue;
+ for (j = 0; j < n_group; ++j)
+ isl_map_free(grid[i][j]);
+ free(grid[i]);
+ }
+ free(grid);
+ if (set) {
+ for (i = 0; i < 2 * n; ++i)
+ isl_set_free(set[i]);
+ free(set);
+ }
+ free(group);
+ return NULL;
+}
+
+/* Perform Floyd-Warshall on the given union relation.
+ * The implementation is very similar to that for non-unions.
+ * The main difference is that it is applied unconditionally.
+ * We first extract a list of basic maps from the union map
+ * and then perform the algorithm on this list.
+ */
+static __isl_give isl_union_map *union_floyd_warshall(
+ __isl_take isl_union_map *umap, isl_bool *exact)
+{
+ int i, n;
+ isl_ctx *ctx;
+ isl_basic_map **list = NULL;
+ isl_basic_map **next;
+ isl_union_map *res;
+
+ n = 0;
+ if (isl_union_map_foreach_map(umap, inc_count, &n) < 0)
+ goto error;
+
+ ctx = isl_union_map_get_ctx(umap);
+ list = isl_calloc_array(ctx, isl_basic_map *, n);
+ if (!list)
+ goto error;
+
+ next = list;
+ if (isl_union_map_foreach_map(umap, collect_basic_map, &next) < 0)
+ goto error;
+
+ res = union_floyd_warshall_on_list(ctx, list, n, exact);
+
+ if (list) {
+ for (i = 0; i < n; ++i)
+ isl_basic_map_free(list[i]);
+ free(list);
+ }
+
+ isl_union_map_free(umap);
+ return res;
+error:
+ if (list) {
+ for (i = 0; i < n; ++i)
+ isl_basic_map_free(list[i]);
+ free(list);
+ }
+ isl_union_map_free(umap);
+ return NULL;
+}
+
+/* Decompose the give union relation into strongly connected components.
+ * The implementation is essentially the same as that of
+ * construct_power_components with the major difference that all
+ * operations are performed on union maps.
+ */
+static __isl_give isl_union_map *union_components(
+ __isl_take isl_union_map *umap, isl_bool *exact)
+{
+ int i;
+ int n;
+ isl_ctx *ctx;
+ isl_basic_map **list = NULL;
+ isl_basic_map **next;
+ isl_union_map *path = NULL;
+ struct isl_tc_follows_data data;
+ struct isl_tarjan_graph *g = NULL;
+ int c, l;
+ int recheck = 0;
+
+ n = 0;
+ if (isl_union_map_foreach_map(umap, inc_count, &n) < 0)
+ goto error;
+
+ if (n == 0)
+ return umap;
+ if (n <= 1)
+ return union_floyd_warshall(umap, exact);
+
+ ctx = isl_union_map_get_ctx(umap);
+ list = isl_calloc_array(ctx, isl_basic_map *, n);
+ if (!list)
+ goto error;
+
+ next = list;
+ if (isl_union_map_foreach_map(umap, collect_basic_map, &next) < 0)
+ goto error;
+
+ data.list = list;
+ data.check_closed = 0;
+ g = isl_tarjan_graph_init(ctx, n, &basic_map_follows, &data);
+ if (!g)
+ goto error;
+
+ c = 0;
+ i = 0;
+ l = n;
+ path = isl_union_map_empty(isl_union_map_get_space(umap));
+ while (l) {
+ isl_union_map *comp;
+ isl_union_map *path_comp, *path_comb;
+ comp = isl_union_map_empty(isl_union_map_get_space(umap));
+ while (g->order[i] != -1) {
+ comp = isl_union_map_add_map(comp,
+ isl_map_from_basic_map(
+ isl_basic_map_copy(list[g->order[i]])));
+ --l;
+ ++i;
+ }
+ path_comp = union_floyd_warshall(comp, exact);
+ path_comb = isl_union_map_apply_range(isl_union_map_copy(path),
+ isl_union_map_copy(path_comp));
+ path = isl_union_map_union(path, path_comp);
+ path = isl_union_map_union(path, path_comb);
+ ++i;
+ ++c;
+ }
+
+ if (c > 1 && data.check_closed && !*exact) {
+ isl_bool closed;
+
+ closed = isl_union_map_is_transitively_closed(path);
+ if (closed < 0)
+ goto error;
+ recheck = !closed;
+ }
+
+ isl_tarjan_graph_free(g);
+
+ for (i = 0; i < n; ++i)
+ isl_basic_map_free(list[i]);
+ free(list);
+
+ if (recheck) {
+ isl_union_map_free(path);
+ return union_floyd_warshall(umap, exact);
+ }
+
+ isl_union_map_free(umap);
+
+ return path;
+error:
+ isl_tarjan_graph_free(g);
+ if (list) {
+ for (i = 0; i < n; ++i)
+ isl_basic_map_free(list[i]);
+ free(list);
+ }
+ isl_union_map_free(umap);
+ isl_union_map_free(path);
+ return NULL;
+}
+
+/* Compute the transitive closure of "umap", or an overapproximation.
+ * If the result is exact, then *exact is set to 1.
+ */
+__isl_give isl_union_map *isl_union_map_transitive_closure(
+ __isl_take isl_union_map *umap, isl_bool *exact)
+{
+ isl_bool closed;
+
+ if (!umap)
+ return NULL;
+
+ if (exact)
+ *exact = isl_bool_true;
+
+ umap = isl_union_map_compute_divs(umap);
+ umap = isl_union_map_coalesce(umap);
+ closed = isl_union_map_is_transitively_closed(umap);
+ if (closed < 0)
+ goto error;
+ if (closed)
+ return umap;
+ umap = union_components(umap, exact);
+ return umap;
+error:
+ isl_union_map_free(umap);
+ return NULL;
+}
+
+struct isl_union_power {
+ isl_union_map *pow;
+ isl_bool *exact;
+};
+
+static isl_stat power(__isl_take isl_map *map, void *user)
+{
+ struct isl_union_power *up = user;
+
+ map = isl_map_power(map, up->exact);
+ up->pow = isl_union_map_from_map(map);
+
+ return isl_stat_error;
+}
+
+/* Construct a map [[x]->[y]] -> [y-x], with parameters prescribed by "space".
+ */
+static __isl_give isl_union_map *deltas_map(__isl_take isl_space *space)
+{
+ isl_basic_map *bmap;
+
+ space = isl_space_add_dims(space, isl_dim_in, 1);
+ space = isl_space_add_dims(space, isl_dim_out, 1);
+ bmap = isl_basic_map_universe(space);
+ bmap = isl_basic_map_deltas_map(bmap);
+
+ return isl_union_map_from_map(isl_map_from_basic_map(bmap));
+}
+
+/* Compute the positive powers of "map", or an overapproximation.
+ * The result maps the exponent to a nested copy of the corresponding power.
+ * If the result is exact, then *exact is set to 1.
+ */
+__isl_give isl_union_map *isl_union_map_power(__isl_take isl_union_map *umap,
+ isl_bool *exact)
+{
+ isl_size n;
+ isl_union_map *inc;
+ isl_union_map *dm;
+
+ n = isl_union_map_n_map(umap);
+ if (n < 0)
+ return isl_union_map_free(umap);
+ if (n == 0)
+ return umap;
+ if (n == 1) {
+ struct isl_union_power up = { NULL, exact };
+ isl_union_map_foreach_map(umap, &power, &up);
+ isl_union_map_free(umap);
+ return up.pow;
+ }
+ inc = isl_union_map_from_map(increment(isl_union_map_get_space(umap)));
+ umap = isl_union_map_product(inc, umap);
+ umap = isl_union_map_transitive_closure(umap, exact);
+ umap = isl_union_map_zip(umap);
+ dm = deltas_map(isl_union_map_get_space(umap));
+ umap = isl_union_map_apply_domain(umap, dm);
+
+ return umap;
+}
+
+#undef TYPE
+#define TYPE isl_map
+#include "isl_power_templ.c"
+
+#undef TYPE
+#define TYPE isl_union_map
+#include "isl_power_templ.c"
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_check_equal_space_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_check_equal_space_templ.c
new file mode 100644
index 00000000000..97a0a354a07
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_check_equal_space_templ.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2011 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege
+ */
+
+/* Check that "obj1" and "obj2" live in the same space,
+ * reporting an error if they do not.
+ */
+isl_stat FN(TYPE_PAIR,check_equal_space)(__isl_keep TYPE1 *obj1,
+ __isl_keep TYPE2 *obj2)
+{
+ isl_bool equal;
+
+ equal = FN(TYPE_PAIR,has_equal_space)(obj1, obj2);
+ if (equal < 0)
+ return isl_stat_error;
+ if (!equal)
+ isl_die(FN(TYPE1,get_ctx)(obj1), isl_error_invalid,
+ "spaces don't match", return isl_stat_error);
+
+ return isl_stat_ok;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_equal_space_bin_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_equal_space_bin_templ.c
new file mode 100644
index 00000000000..f435753aec7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_equal_space_bin_templ.c
@@ -0,0 +1,8 @@
+#undef TYPE1
+#define TYPE1 TYPE
+#undef TYPE2
+#define TYPE2 TYPE
+#undef TYPE_PAIR
+#define TYPE_PAIR TYPE
+
+#include "isl_type_has_equal_space_templ.c"
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_equal_space_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_equal_space_templ.c
new file mode 100644
index 00000000000..2753d4599a4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_equal_space_templ.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
+ * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
+ */
+
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+#ifndef PEEK_SPACE
+#define PEEK_SPACE peek_space
+#endif
+
+/* Do "obj1" and "obj2" have the same space?
+ */
+isl_bool FN(TYPE_PAIR,has_equal_space)(__isl_keep TYPE1 *obj1,
+ __isl_keep TYPE2 *obj2)
+{
+ isl_space *space1, *space2;
+
+ space1 = FN(TYPE1,PEEK_SPACE)(obj1);
+ space2 = FN(TYPE2,PEEK_SPACE)(obj2);
+ return isl_space_is_equal(space1, space2);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_space_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_space_templ.c
new file mode 100644
index 00000000000..313b27a47cc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_type_has_space_templ.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Is the space of "obj" equal to "space"?
+ */
+isl_bool FN(TYPE,has_space)(__isl_keep TYPE *obj, __isl_keep isl_space *space)
+{
+ return isl_space_is_equal(FN(TYPE,peek_space)(obj), space);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_unbind_params_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_unbind_params_templ.c
new file mode 100644
index 00000000000..3365c7a686c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_unbind_params_templ.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+/* Given a function "obj" defined over a parameter domain,
+ * convert it to a function defined over a domain corresponding
+ * to "domain".
+ * Any parameters with identifiers in "domain" are reinterpreted
+ * as the corresponding domain dimensions.
+ */
+__isl_give TYPE *FN(TYPE,unbind_params_insert_domain)(
+ __isl_take TYPE *obj, __isl_take isl_multi_id *domain)
+{
+ isl_bool is_params;
+ isl_space *space;
+ isl_reordering *r;
+
+ space = FN(TYPE,get_domain_space)(obj);
+ is_params = isl_space_is_params(space);
+ if (is_params < 0)
+ domain = isl_multi_id_free(domain);
+ else if (!is_params)
+ isl_die(FN(TYPE,get_ctx)(obj), isl_error_invalid,
+ "expecting function with parameter domain",
+ domain = isl_multi_id_free(domain));
+ r = isl_reordering_unbind_params_insert_domain(space, domain);
+ isl_space_free(space);
+ isl_multi_id_free(domain);
+
+ return FN(TYPE,realign_domain)(obj, r);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_eval.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_eval.c
new file mode 100644
index 00000000000..050eb8af289
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_eval.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_union_macro.h>
+
+/* Evaluate "u" in the void point "pnt".
+ * In particular, return the value NaN.
+ */
+static __isl_give isl_val *FN(UNION,eval_void)(__isl_take UNION *u,
+ __isl_take isl_point *pnt)
+{
+ isl_ctx *ctx;
+
+ ctx = isl_point_get_ctx(pnt);
+ FN(UNION,free)(u);
+ isl_point_free(pnt);
+ return isl_val_nan(ctx);
+}
+
+/* Internal data structure for isl_union_*_eval.
+ *
+ * "pnt" is the point in which the function is evaluated.
+ * "v" stores the result and is initialized to zero.
+ */
+S(UNION,eval_data) {
+ isl_point *pnt;
+ isl_val *v;
+};
+
+/* Update the evaluation in data->v based on the evaluation of "part".
+ *
+ * Only (at most) a single part on which this function is called
+ * is assumed to evaluate to anything other than zero.
+ * Since the value is initialized to zero, the evaluation of "part"
+ * can simply be added.
+ */
+static isl_stat FN(UNION,eval_entry)(__isl_take PART *part, void *user)
+{
+ S(UNION,eval_data) *data = user;
+ isl_val *v;
+
+ v = FN(PART,eval)(part, isl_point_copy(data->pnt));
+ data->v = isl_val_add(data->v, v);
+
+ return isl_stat_non_null(data->v);
+}
+
+/* Evaluate "u" in the point "pnt".
+ */
+__isl_give isl_val *FN(UNION,eval)(__isl_take UNION *u,
+ __isl_take isl_point *pnt)
+{
+ S(UNION,eval_data) data = { pnt };
+ isl_bool is_void;
+ isl_space *space;
+
+ is_void = isl_point_is_void(pnt);
+ if (is_void < 0)
+ goto error;
+ if (is_void)
+ return FN(UNION,eval_void)(u, pnt);
+
+ data.v = isl_val_zero(isl_point_get_ctx(pnt));
+ space = isl_point_peek_space(pnt);
+ if (FN(UNION,foreach_on_domain)(u, space,
+ &FN(UNION,eval_entry), &data) < 0)
+ data.v = isl_val_free(data.v);
+ FN(UNION,free)(u);
+ isl_point_free(pnt);
+ return data.v;
+error:
+ FN(UNION,free)(u);
+ isl_point_free(pnt);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_locals_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_locals_templ.c
new file mode 100644
index 00000000000..c7387bb26f7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_locals_templ.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 Cerebras Systems
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Cerebras Systems, 175 S San Antonio Rd, Los Altos, CA, USA
+ */
+
+/* isl_union_*_every_* callback that checks whether "pw"
+ * is free of local variables.
+ */
+static isl_bool FN(UNION,no_locals_el)(__isl_keep PW *pw, void *user)
+{
+ return isl_bool_not(FN(PW,involves_locals)(pw));
+}
+
+/* Does "u" involve any local variables, i.e., integer divisions?
+ */
+isl_bool FN(UNION,involves_locals)(__isl_keep UNION *u)
+{
+ isl_bool no_locals;
+
+ no_locals = FN(FN(UNION,every),BASE)(u, &FN(UNION,no_locals_el), NULL);
+
+ return isl_bool_not(no_locals);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_macro.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_macro.h
new file mode 100644
index 00000000000..2cc21a90a84
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_macro.h
@@ -0,0 +1,10 @@
+#define xCAT(A,B) A ## B
+#define CAT(A,B) xCAT(A,B)
+#undef PART
+#define PART CAT(isl_,BASE)
+#undef UNION
+#define UNION CAT(isl_union_,BASE)
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+#define xS(TYPE,NAME) struct TYPE ## _ ## NAME
+#define S(TYPE,NAME) xS(TYPE,NAME)
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map.c
new file mode 100644
index 00000000000..2ea129b5144
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map.c
@@ -0,0 +1,4564 @@
+/*
+ * Copyright 2010-2011 INRIA Saclay
+ * Copyright 2013-2014 Ecole Normale Superieure
+ * Copyright 2014 INRIA Rocquencourt
+ * Copyright 2016-2017 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#include <isl_map_private.h>
+#include <isl_union_map_private.h>
+#include <isl/ctx.h>
+#include <isl/hash.h>
+#include <isl_aff_private.h>
+#include <isl/map.h>
+#include <isl/set.h>
+#include <isl_space_private.h>
+#include <isl/union_set.h>
+#include <isl_maybe_map.h>
+#include <isl_id_private.h>
+
+#include <bset_from_bmap.c>
+#include <set_to_map.c>
+#include <set_from_map.c>
+#include <uset_to_umap.c>
+#include <uset_from_umap.c>
+#include <set_list_from_map_list_inl.c>
+
+#undef TYPE
+#define TYPE isl_union_map
+static
+#include "has_single_reference_templ.c"
+static
+#include "check_single_reference_templ.c"
+
+/* Return the number of parameters of "umap", where "type"
+ * is required to be set to isl_dim_param.
+ */
+isl_size isl_union_map_dim(__isl_keep isl_union_map *umap,
+ enum isl_dim_type type)
+{
+ if (!umap)
+ return isl_size_error;
+
+ if (type != isl_dim_param)
+ isl_die(isl_union_map_get_ctx(umap), isl_error_invalid,
+ "can only reference parameters", return isl_size_error);
+
+ return isl_space_dim(umap->dim, type);
+}
+
+/* Return the number of parameters of "uset", where "type"
+ * is required to be set to isl_dim_param.
+ */
+isl_size isl_union_set_dim(__isl_keep isl_union_set *uset,
+ enum isl_dim_type type)
+{
+ return isl_union_map_dim(uset, type);
+}
+
+/* Return the id of the specified dimension.
+ */
+__isl_give isl_id *isl_union_map_get_dim_id(__isl_keep isl_union_map *umap,
+ enum isl_dim_type type, unsigned pos)
+{
+ if (!umap)
+ return NULL;
+
+ if (type != isl_dim_param)
+ isl_die(isl_union_map_get_ctx(umap), isl_error_invalid,
+ "can only reference parameters", return NULL);
+
+ return isl_space_get_dim_id(umap->dim, type, pos);
+}
+
+/* Is this union set a parameter domain?
+ */
+isl_bool isl_union_set_is_params(__isl_keep isl_union_set *uset)
+{
+ isl_set *set;
+ isl_bool params;
+
+ if (!uset)
+ return isl_bool_error;
+ if (uset->table.n != 1)
+ return isl_bool_false;
+
+ set = isl_set_from_union_set(isl_union_set_copy(uset));
+ params = isl_set_is_params(set);
+ isl_set_free(set);
+ return params;
+}
+
+/* Is this union map actually a parameter domain?
+ * Users should never call this function. Outside of isl,
+ * a union map can never be a parameter domain.
+ */
+isl_bool isl_union_map_is_params(__isl_keep isl_union_map *umap)
+{
+ return isl_union_set_is_params(uset_from_umap(umap));
+}
+
+static __isl_give isl_union_map *isl_union_map_alloc(
+ __isl_take isl_space *space, int size)
+{
+ isl_union_map *umap;
+
+ space = isl_space_params(space);
+ if (!space)
+ return NULL;
+
+ umap = isl_calloc_type(space->ctx, isl_union_map);
+ if (!umap) {
+ isl_space_free(space);
+ return NULL;
+ }
+
+ umap->ref = 1;
+ umap->dim = space;
+ if (isl_hash_table_init(space->ctx, &umap->table, size) < 0)
+ return isl_union_map_free(umap);
+
+ return umap;
+}
+
+/* Create an empty union map without specifying any parameters.
+ */
+__isl_give isl_union_map *isl_union_map_empty_ctx(isl_ctx *ctx)
+{
+ return isl_union_map_empty_space(isl_space_unit(ctx));
+}
+
+__isl_give isl_union_map *isl_union_map_empty_space(__isl_take isl_space *space)
+{
+ return isl_union_map_alloc(space, 16);
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_union_map *isl_union_map_empty(__isl_take isl_space *space)
+{
+ return isl_union_map_empty_space(space);
+}
+
+/* Create an empty union set without specifying any parameters.
+ */
+__isl_give isl_union_set *isl_union_set_empty_ctx(isl_ctx *ctx)
+{
+ return uset_from_umap(isl_union_map_empty_ctx(ctx));
+}
+
+__isl_give isl_union_set *isl_union_set_empty_space(__isl_take isl_space *space)
+{
+ return uset_from_umap(isl_union_map_empty_space(space));
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_union_set *isl_union_set_empty(__isl_take isl_space *space)
+{
+ return isl_union_set_empty_space(space);
+}
+
+isl_ctx *isl_union_map_get_ctx(__isl_keep isl_union_map *umap)
+{
+ return umap ? umap->dim->ctx : NULL;
+}
+
+isl_ctx *isl_union_set_get_ctx(__isl_keep isl_union_set *uset)
+{
+ return uset ? uset->dim->ctx : NULL;
+}
+
+/* Return the space of "umap".
+ */
+__isl_keep isl_space *isl_union_map_peek_space(__isl_keep isl_union_map *umap)
+{
+ return umap ? umap->dim : NULL;
+}
+
+/* Return the space of "uset".
+ */
+__isl_keep isl_space *isl_union_set_peek_space(__isl_keep isl_union_set *uset)
+{
+ return isl_union_map_peek_space(uset_to_umap(uset));
+}
+
+__isl_give isl_space *isl_union_map_get_space(__isl_keep isl_union_map *umap)
+{
+ return isl_space_copy(isl_union_map_peek_space(umap));
+}
+
+/* Return the position of the parameter with the given name
+ * in "umap".
+ * Return -1 if no such dimension can be found.
+ */
+int isl_union_map_find_dim_by_name(__isl_keep isl_union_map *umap,
+ enum isl_dim_type type, const char *name)
+{
+ if (!umap)
+ return -1;
+ return isl_space_find_dim_by_name(umap->dim, type, name);
+}
+
+__isl_give isl_space *isl_union_set_get_space(__isl_keep isl_union_set *uset)
+{
+ return isl_union_map_get_space(uset);
+}
+
+static isl_stat free_umap_entry(void **entry, void *user)
+{
+ isl_map *map = *entry;
+ isl_map_free(map);
+ return isl_stat_ok;
+}
+
+static isl_stat add_map(__isl_take isl_map *map, void *user)
+{
+ isl_union_map **umap = (isl_union_map **)user;
+
+ *umap = isl_union_map_add_map(*umap, map);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_map *isl_union_map_dup(__isl_keep isl_union_map *umap)
+{
+ isl_union_map *dup;
+
+ if (!umap)
+ return NULL;
+
+ dup = isl_union_map_empty(isl_space_copy(umap->dim));
+ if (isl_union_map_foreach_map(umap, &add_map, &dup) < 0)
+ goto error;
+ return dup;
+error:
+ isl_union_map_free(dup);
+ return NULL;
+}
+
+__isl_give isl_union_map *isl_union_map_cow(__isl_take isl_union_map *umap)
+{
+ if (!umap)
+ return NULL;
+
+ if (umap->ref == 1)
+ return umap;
+ umap->ref--;
+ return isl_union_map_dup(umap);
+}
+
+struct isl_union_align {
+ isl_reordering *exp;
+ isl_union_map *res;
+};
+
+static isl_stat align_entry(void **entry, void *user)
+{
+ isl_map *map = *entry;
+ isl_reordering *exp;
+ struct isl_union_align *data = user;
+
+ exp = isl_reordering_extend_space(isl_reordering_copy(data->exp),
+ isl_map_get_space(map));
+
+ data->res = isl_union_map_add_map(data->res,
+ isl_map_realign(isl_map_copy(map), exp));
+
+ return isl_stat_ok;
+}
+
+/* Align the parameters of umap along those of model.
+ * The result has the parameters of model first, in the same order
+ * as they appear in model, followed by any remaining parameters of
+ * umap that do not appear in model.
+ */
+__isl_give isl_union_map *isl_union_map_align_params(
+ __isl_take isl_union_map *umap, __isl_take isl_space *model)
+{
+ struct isl_union_align data = { NULL, NULL };
+ isl_bool equal_params;
+
+ if (!umap || !model)
+ goto error;
+
+ equal_params = isl_space_has_equal_params(umap->dim, model);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params) {
+ isl_space_free(model);
+ return umap;
+ }
+
+ data.exp = isl_parameter_alignment_reordering(umap->dim, model);
+ if (!data.exp)
+ goto error;
+
+ data.res = isl_union_map_alloc(isl_reordering_get_space(data.exp),
+ umap->table.n);
+ if (isl_hash_table_foreach(umap->dim->ctx, &umap->table,
+ &align_entry, &data) < 0)
+ goto error;
+
+ isl_reordering_free(data.exp);
+ isl_union_map_free(umap);
+ isl_space_free(model);
+ return data.res;
+error:
+ isl_reordering_free(data.exp);
+ isl_union_map_free(umap);
+ isl_union_map_free(data.res);
+ isl_space_free(model);
+ return NULL;
+}
+
+__isl_give isl_union_set *isl_union_set_align_params(
+ __isl_take isl_union_set *uset, __isl_take isl_space *model)
+{
+ return isl_union_map_align_params(uset, model);
+}
+
+__isl_give isl_union_map *isl_union_map_union(__isl_take isl_union_map *umap1,
+ __isl_take isl_union_map *umap2)
+{
+ umap1 = isl_union_map_align_params(umap1, isl_union_map_get_space(umap2));
+ umap2 = isl_union_map_align_params(umap2, isl_union_map_get_space(umap1));
+
+ umap1 = isl_union_map_cow(umap1);
+
+ if (!umap1 || !umap2)
+ goto error;
+
+ if (isl_union_map_foreach_map(umap2, &add_map, &umap1) < 0)
+ goto error;
+
+ isl_union_map_free(umap2);
+
+ return umap1;
+error:
+ isl_union_map_free(umap1);
+ isl_union_map_free(umap2);
+ return NULL;
+}
+
+__isl_give isl_union_set *isl_union_set_union(__isl_take isl_union_set *uset1,
+ __isl_take isl_union_set *uset2)
+{
+ return isl_union_map_union(uset1, uset2);
+}
+
+__isl_give isl_union_map *isl_union_map_copy(__isl_keep isl_union_map *umap)
+{
+ if (!umap)
+ return NULL;
+
+ umap->ref++;
+ return umap;
+}
+
+__isl_give isl_union_set *isl_union_set_copy(__isl_keep isl_union_set *uset)
+{
+ return isl_union_map_copy(uset);
+}
+
+__isl_null isl_union_map *isl_union_map_free(__isl_take isl_union_map *umap)
+{
+ if (!umap)
+ return NULL;
+
+ if (--umap->ref > 0)
+ return NULL;
+
+ isl_hash_table_foreach(umap->dim->ctx, &umap->table,
+ &free_umap_entry, NULL);
+ isl_hash_table_clear(&umap->table);
+ isl_space_free(umap->dim);
+ free(umap);
+ return NULL;
+}
+
+__isl_null isl_union_set *isl_union_set_free(__isl_take isl_union_set *uset)
+{
+ return isl_union_map_free(uset);
+}
+
+/* Do "umap" and "space" have the same parameters?
+ */
+isl_bool isl_union_map_space_has_equal_params(__isl_keep isl_union_map *umap,
+ __isl_keep isl_space *space)
+{
+ isl_space *umap_space;
+
+ umap_space = isl_union_map_peek_space(umap);
+ return isl_space_has_equal_params(umap_space, space);
+}
+
+/* Do "uset" and "space" have the same parameters?
+ */
+isl_bool isl_union_set_space_has_equal_params(__isl_keep isl_union_set *uset,
+ __isl_keep isl_space *space)
+{
+ return isl_union_map_space_has_equal_params(uset_to_umap(uset), space);
+}
+
+/* Is the space of the map at "entry" equal to "space", ignoring parameters?
+ */
+static isl_bool has_space_tuples(const void *entry, const void *val)
+{
+ isl_map *map = (isl_map *)entry;
+ isl_space *space = (isl_space *) val;
+
+ return isl_map_has_space_tuples(map, space);
+}
+
+/* Find the entry in "umap" with space "space" (ignoring parameters),
+ * returning isl_hash_table_entry_none if no such entry appears in "umap" and
+ * NULL on error.
+ * If "reserve" is set, then an entry is created if it does
+ * not exist already. Since this modifies the hash table in-place,
+ * this means "umap" must have a single reference when "reserve" is set.
+ */
+static struct isl_hash_table_entry *isl_union_map_find_entry(
+ __isl_keep isl_union_map *umap, __isl_keep isl_space *space,
+ int reserve)
+{
+ uint32_t hash;
+
+ if (!umap || !space)
+ return NULL;
+ if (reserve && isl_union_map_check_single_reference(umap) < 0)
+ return NULL;
+
+ hash = isl_space_get_tuple_hash(space);
+ return isl_hash_table_find(isl_union_map_get_ctx(umap), &umap->table,
+ hash, &has_space_tuples, space, reserve);
+}
+
+/* Find the entry in "uset" with space "space" (ignoring parameters),
+ * returning isl_hash_table_entry_none if no such entry appears in "uset" and
+ * NULL on error.
+ * If "reserve" is set, then an entry is created if it does
+ * not exist already. In this case, a NULL return indicates an error.
+ */
+struct isl_hash_table_entry *isl_union_set_find_entry(
+ __isl_keep isl_union_set *uset, __isl_keep isl_space *space,
+ int reserve)
+{
+ return isl_union_map_find_entry(uset_to_umap(uset), space, reserve);
+}
+
+__isl_give isl_union_map *isl_union_map_add_map(__isl_take isl_union_map *umap,
+ __isl_take isl_map *map)
+{
+ struct isl_hash_table_entry *entry;
+ isl_bool aligned;
+ isl_space *space;
+
+ if (!map || !umap)
+ goto error;
+
+ if (isl_map_plain_is_empty(map)) {
+ isl_map_free(map);
+ return umap;
+ }
+
+ aligned = isl_map_space_has_equal_params(map, umap->dim);
+ if (aligned < 0)
+ goto error;
+ if (!aligned) {
+ umap = isl_union_map_align_params(umap, isl_map_get_space(map));
+ map = isl_map_align_params(map, isl_union_map_get_space(umap));
+ }
+
+ umap = isl_union_map_cow(umap);
+
+ space = isl_map_peek_space(map);
+ entry = isl_union_map_find_entry(umap, space, 1);
+ if (!entry)
+ goto error;
+
+ if (!entry->data)
+ entry->data = map;
+ else {
+ entry->data = isl_map_union(entry->data, isl_map_copy(map));
+ if (!entry->data)
+ goto error;
+ isl_map_free(map);
+ }
+
+ return umap;
+error:
+ isl_map_free(map);
+ isl_union_map_free(umap);
+ return NULL;
+}
+
+__isl_give isl_union_set *isl_union_set_add_set(__isl_take isl_union_set *uset,
+ __isl_take isl_set *set)
+{
+ return isl_union_map_add_map(uset, set_to_map(set));
+}
+
+__isl_give isl_union_map *isl_union_map_from_map(__isl_take isl_map *map)
+{
+ isl_space *space;
+ isl_union_map *umap;
+
+ if (!map)
+ return NULL;
+
+ space = isl_map_get_space(map);
+ space = isl_space_params(space);
+ umap = isl_union_map_empty(space);
+ umap = isl_union_map_add_map(umap, map);
+
+ return umap;
+}
+
+/* This function performs the same operation as isl_union_map_from_map,
+ * but is considered as a function on an isl_map when exported.
+ */
+__isl_give isl_union_map *isl_map_to_union_map(__isl_take isl_map *map)
+{
+ return isl_union_map_from_map(map);
+}
+
+__isl_give isl_union_set *isl_union_set_from_set(__isl_take isl_set *set)
+{
+ return isl_union_map_from_map(set_to_map(set));
+}
+
+/* This function performs the same operation as isl_union_set_from_set,
+ * but is considered as a function on an isl_set when exported.
+ */
+__isl_give isl_union_set *isl_set_to_union_set(__isl_take isl_set *set)
+{
+ return isl_union_set_from_set(set);
+}
+
+__isl_give isl_union_map *isl_union_map_from_basic_map(
+ __isl_take isl_basic_map *bmap)
+{
+ return isl_union_map_from_map(isl_map_from_basic_map(bmap));
+}
+
+__isl_give isl_union_set *isl_union_set_from_basic_set(
+ __isl_take isl_basic_set *bset)
+{
+ return isl_union_map_from_basic_map(bset);
+}
+
+struct isl_union_map_foreach_data
+{
+ isl_stat (*fn)(__isl_take isl_map *map, void *user);
+ void *user;
+};
+
+static isl_stat call_on_copy(void **entry, void *user)
+{
+ isl_map *map = *entry;
+ struct isl_union_map_foreach_data *data;
+ data = (struct isl_union_map_foreach_data *)user;
+
+ return data->fn(isl_map_copy(map), data->user);
+}
+
+isl_size isl_union_map_n_map(__isl_keep isl_union_map *umap)
+{
+ return umap ? umap->table.n : isl_size_error;
+}
+
+isl_size isl_union_set_n_set(__isl_keep isl_union_set *uset)
+{
+ return uset ? uset->table.n : isl_size_error;
+}
+
+isl_stat isl_union_map_foreach_map(__isl_keep isl_union_map *umap,
+ isl_stat (*fn)(__isl_take isl_map *map, void *user), void *user)
+{
+ struct isl_union_map_foreach_data data = { fn, user };
+
+ if (!umap)
+ return isl_stat_error;
+
+ return isl_hash_table_foreach(umap->dim->ctx, &umap->table,
+ &call_on_copy, &data);
+}
+
+/* Internal data structure for isl_union_map_every_map.
+ *
+ * "test" is the user-specified callback function.
+ * "user" is the user-specified callback function argument.
+ *
+ * "failed" is initialized to 0 and set to 1 if "test" fails
+ * on any map.
+ */
+struct isl_union_map_every_data {
+ isl_bool (*test)(__isl_keep isl_map *map, void *user);
+ void *user;
+ int failed;
+};
+
+/* Call data->test on "map".
+ * If this fails, then set data->failed and abort.
+ */
+static isl_stat call_every(void **entry, void *user)
+{
+ isl_map *map = *entry;
+ struct isl_union_map_every_data *data = user;
+ isl_bool r;
+
+ r = data->test(map, data->user);
+ if (r < 0)
+ return isl_stat_error;
+ if (r)
+ return isl_stat_ok;
+ data->failed = 1;
+ return isl_stat_error;
+}
+
+/* Does "test" succeed on every map in "umap"?
+ */
+isl_bool isl_union_map_every_map(__isl_keep isl_union_map *umap,
+ isl_bool (*test)(__isl_keep isl_map *map, void *user), void *user)
+{
+ struct isl_union_map_every_data data = { test, user, 0 };
+ isl_stat r;
+
+ if (!umap)
+ return isl_bool_error;
+
+ r = isl_hash_table_foreach(isl_union_map_get_ctx(umap), &umap->table,
+ &call_every, &data);
+ if (r >= 0)
+ return isl_bool_true;
+ if (data.failed)
+ return isl_bool_false;
+ return isl_bool_error;
+}
+
+/* Add "map" to "list".
+ */
+static isl_stat add_list_map(__isl_take isl_map *map, void *user)
+{
+ isl_map_list **list = user;
+
+ *list = isl_map_list_add(*list, map);
+
+ if (!*list)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Return the maps in "umap" as a list.
+ *
+ * First construct a list of the appropriate size and then add all the
+ * elements.
+ */
+__isl_give isl_map_list *isl_union_map_get_map_list(
+ __isl_keep isl_union_map *umap)
+{
+ isl_size n_maps;
+ isl_ctx *ctx;
+ isl_map_list *list;
+
+ n_maps = isl_union_map_n_map(umap);
+ if (n_maps < 0)
+ return NULL;
+ ctx = isl_union_map_get_ctx(umap);
+ list = isl_map_list_alloc(ctx, n_maps);
+
+ if (isl_union_map_foreach_map(umap, &add_list_map, &list) < 0)
+ list = isl_map_list_free(list);
+
+ return list;
+}
+
+/* Return the sets in "uset" as a list.
+ */
+__isl_give isl_set_list *isl_union_set_get_set_list(
+ __isl_keep isl_union_set *uset)
+{
+ return set_list_from_map_list(
+ isl_union_map_get_map_list(uset_to_umap(uset)));
+}
+
+/* Can "umap" be converted to an isl_map?
+ * That is, does it contain elements in exactly one space?
+ */
+isl_bool isl_union_map_isa_map(__isl_keep isl_union_map *umap)
+{
+ isl_size n;
+
+ n = isl_union_map_n_map(umap);
+ if (n < 0)
+ return isl_bool_error;
+ return isl_bool_ok(n == 1);
+}
+
+/* Can "uset" be converted to an isl_set?
+ * That is, does it contain elements in exactly one space?
+ */
+isl_bool isl_union_set_isa_set(__isl_keep isl_union_set *uset)
+{
+ return isl_union_map_isa_map(uset_to_umap(uset));
+}
+
+static isl_stat copy_map(void **entry, void *user)
+{
+ isl_map *map = *entry;
+ isl_map **map_p = user;
+
+ *map_p = isl_map_copy(map);
+
+ return isl_stat_error;
+}
+
+__isl_give isl_map *isl_map_from_union_map(__isl_take isl_union_map *umap)
+{
+ isl_bool is_map;
+ isl_ctx *ctx;
+ isl_map *map = NULL;
+
+ is_map = isl_union_map_isa_map(umap);
+ if (is_map < 0)
+ goto error;
+ ctx = isl_union_map_get_ctx(umap);
+ if (!is_map)
+ isl_die(ctx, isl_error_invalid,
+ "union map needs to contain elements in exactly "
+ "one space", goto error);
+
+ isl_hash_table_foreach(ctx, &umap->table, &copy_map, &map);
+
+ isl_union_map_free(umap);
+
+ return map;
+error:
+ isl_union_map_free(umap);
+ return NULL;
+}
+
+/* This function performs the same operation as isl_map_from_union_map,
+ * but is considered as a function on an isl_union_map when exported.
+ */
+__isl_give isl_map *isl_union_map_as_map(__isl_take isl_union_map *umap)
+{
+ return isl_map_from_union_map(umap);
+}
+
+__isl_give isl_set *isl_set_from_union_set(__isl_take isl_union_set *uset)
+{
+ return isl_map_from_union_map(uset);
+}
+
+/* This function performs the same operation as isl_set_from_union_set,
+ * but is considered as a function on an isl_union_set when exported.
+ */
+__isl_give isl_set *isl_union_set_as_set(__isl_take isl_union_set *uset)
+{
+ return isl_set_from_union_set(uset);
+}
+
+/* Extract the map in "umap" that lives in the given space (ignoring
+ * parameters).
+ */
+__isl_give isl_map *isl_union_map_extract_map(__isl_keep isl_union_map *umap,
+ __isl_take isl_space *space)
+{
+ struct isl_hash_table_entry *entry;
+
+ entry = isl_union_map_find_entry(umap, space, 0);
+ if (!entry)
+ goto error;
+ if (entry == isl_hash_table_entry_none)
+ return isl_map_empty(space);
+ isl_space_free(space);
+ return isl_map_copy(entry->data);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+__isl_give isl_set *isl_union_set_extract_set(__isl_keep isl_union_set *uset,
+ __isl_take isl_space *space)
+{
+ return set_from_map(isl_union_map_extract_map(uset, space));
+}
+
+/* Check if umap contains a map in the given space (ignoring parameters).
+ */
+isl_bool isl_union_map_contains(__isl_keep isl_union_map *umap,
+ __isl_keep isl_space *space)
+{
+ struct isl_hash_table_entry *entry;
+
+ space = isl_space_drop_all_params(isl_space_copy(space));
+ space = isl_space_align_params(space, isl_union_map_get_space(umap));
+ entry = isl_union_map_find_entry(umap, space, 0);
+ isl_space_free(space);
+ if (!entry)
+ return isl_bool_error;
+ return isl_bool_ok(entry != isl_hash_table_entry_none);
+}
+
+isl_bool isl_union_set_contains(__isl_keep isl_union_set *uset,
+ __isl_keep isl_space *space)
+{
+ return isl_union_map_contains(uset, space);
+}
+
+isl_stat isl_union_set_foreach_set(__isl_keep isl_union_set *uset,
+ isl_stat (*fn)(__isl_take isl_set *set, void *user), void *user)
+{
+ return isl_union_map_foreach_map(uset,
+ (isl_stat(*)(__isl_take isl_map *, void*))fn, user);
+}
+
+/* Internal data structure for isl_union_set_every_set.
+ *
+ * "test" is the user-specified callback function.
+ * "user" is the user-specified callback function argument.
+ */
+struct isl_test_set_from_map_data {
+ isl_bool (*test)(__isl_keep isl_set *set, void *user);
+ void *user;
+};
+
+/* Call data->test on "map", which is part of an isl_union_set and
+ * therefore known to be an isl_set.
+ */
+static isl_bool test_set_from_map(__isl_keep isl_map *map, void *user)
+{
+ struct isl_test_set_from_map_data *data = user;
+
+ return data->test(set_from_map(map), data->user);
+}
+
+/* Does "test" succeed on every set in "uset"?
+ */
+isl_bool isl_union_set_every_set(__isl_keep isl_union_set *uset,
+ isl_bool (*test)(__isl_keep isl_set *set, void *user), void *user)
+{
+ struct isl_test_set_from_map_data data = { test, user };
+
+ return isl_union_map_every_map(uset_to_umap(uset),
+ &test_set_from_map, &data);
+}
+
+struct isl_union_set_foreach_point_data {
+ isl_stat (*fn)(__isl_take isl_point *pnt, void *user);
+ void *user;
+};
+
+static isl_stat foreach_point(__isl_take isl_set *set, void *user)
+{
+ struct isl_union_set_foreach_point_data *data = user;
+ isl_stat r;
+
+ r = isl_set_foreach_point(set, data->fn, data->user);
+ isl_set_free(set);
+
+ return r;
+}
+
+isl_stat isl_union_set_foreach_point(__isl_keep isl_union_set *uset,
+ isl_stat (*fn)(__isl_take isl_point *pnt, void *user), void *user)
+{
+ struct isl_union_set_foreach_point_data data = { fn, user };
+ return isl_union_set_foreach_set(uset, &foreach_point, &data);
+}
+
+/* Data structure that specifies how gen_bin_op should
+ * construct results from the inputs.
+ *
+ * If "subtract" is set, then a map in the first input is copied to the result
+ * if there is no corresponding map in the second input.
+ * Otherwise, a map in the first input with no corresponding map
+ * in the second input is ignored.
+ * If "filter" is not NULL, then it specifies which maps in the first
+ * input may have a matching map in the second input.
+ * In particular, it makes sure that "match_space" can be called
+ * on the space of the map.
+ * "match_space" specifies how to transform the space of a map
+ * in the first input to the space of the corresponding map
+ * in the second input.
+ * "fn_map" specifies how the matching maps, one from each input,
+ * should be combined to form a map in the result.
+ */
+struct isl_bin_op_control {
+ int subtract;
+ isl_bool (*filter)(__isl_keep isl_map *map);
+ __isl_give isl_space *(*match_space)(__isl_take isl_space *space);
+ __isl_give isl_map *(*fn_map)(__isl_take isl_map *map1,
+ __isl_take isl_map *map2);
+};
+
+/* Internal data structure for gen_bin_op.
+ * "control" specifies how the maps in the result should be constructed.
+ * "umap2" is a pointer to the second argument.
+ * "res" collects the results.
+ */
+struct isl_union_map_gen_bin_data {
+ struct isl_bin_op_control *control;
+ isl_union_map *umap2;
+ isl_union_map *res;
+};
+
+/* Add a copy of "map" to "res" and return the result.
+ */
+static __isl_give isl_union_map *bin_add_map(__isl_take isl_union_map *res,
+ __isl_keep isl_map *map)
+{
+ return isl_union_map_add_map(res, isl_map_copy(map));
+}
+
+/* Combine "map1" and "map2", add the result to "res" and return the result.
+ * Check whether the result is empty before adding it to "res".
+ */
+static __isl_give isl_union_map *bin_add_pair(__isl_take isl_union_map *res,
+ __isl_keep isl_map *map1, __isl_keep isl_map *map2,
+ struct isl_union_map_gen_bin_data *data)
+{
+ isl_bool empty;
+ isl_map *map;
+
+ map = data->control->fn_map(isl_map_copy(map1), isl_map_copy(map2));
+ empty = isl_map_is_empty(map);
+ if (empty < 0 || empty) {
+ isl_map_free(map);
+ if (empty < 0)
+ return isl_union_map_free(res);
+ return res;
+ }
+ return isl_union_map_add_map(res, map);
+}
+
+/* Dummy match_space function that simply returns the input space.
+ */
+static __isl_give isl_space *identity(__isl_take isl_space *space)
+{
+ return space;
+}
+
+/* Look for the map in data->umap2 that corresponds to "map", if any.
+ * Return (isl_bool_true, matching map) if there is one,
+ * (isl_bool_false, NULL) if there is no matching map and
+ * (isl_bool_error, NULL) on error.
+ *
+ * If not NULL, then data->control->filter specifies whether "map"
+ * can have any matching map. If so,
+ * data->control->match_space specifies which map in data->umap2
+ * corresponds to "map".
+ */
+static __isl_keep isl_maybe_isl_map bin_try_get_match(
+ struct isl_union_map_gen_bin_data *data, __isl_keep isl_map *map)
+{
+ struct isl_hash_table_entry *entry2;
+ isl_space *space;
+ isl_maybe_isl_map res = { isl_bool_error, NULL };
+
+ if (data->control->filter) {
+ res.valid = data->control->filter(map);
+ if (res.valid < 0 || !res.valid)
+ return res;
+ res.valid = isl_bool_error;
+ }
+
+ space = isl_map_get_space(map);
+ if (data->control->match_space != &identity)
+ space = data->control->match_space(space);
+ entry2 = isl_union_map_find_entry(data->umap2, space, 0);
+ isl_space_free(space);
+ if (entry2)
+ res.valid = isl_bool_ok(entry2 != isl_hash_table_entry_none);
+ if (res.valid >= 0 && res.valid)
+ res.value = entry2->data;
+
+ return res;
+}
+
+/* isl_hash_table_foreach callback for gen_bin_op.
+ * Look for the map in data->umap2 that corresponds
+ * to the map that "entry" points to, apply the binary operation and
+ * add the result to data->res.
+ *
+ * If no corresponding map can be found, then the effect depends
+ * on data->control->subtract. If it is set, then the current map
+ * is added directly to the result. Otherwise, it is ignored.
+ */
+static isl_stat gen_bin_entry(void **entry, void *user)
+{
+ struct isl_union_map_gen_bin_data *data = user;
+ isl_map *map = *entry;
+ isl_maybe_isl_map m;
+
+ m = bin_try_get_match(data, map);
+ if (m.valid < 0)
+ return isl_stat_error;
+ if (!m.valid && !data->control->subtract)
+ return isl_stat_ok;
+
+ if (!m.valid)
+ data->res = bin_add_map(data->res, map);
+ else
+ data->res = bin_add_pair(data->res, map, m.value, data);
+ if (!data->res)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Apply a binary operation to "umap1" and "umap2" based on "control".
+ * Run over all maps in "umap1" and look for the corresponding map in "umap2"
+ * in gen_bin_entry.
+ */
+static __isl_give isl_union_map *gen_bin_op(__isl_take isl_union_map *umap1,
+ __isl_take isl_union_map *umap2, struct isl_bin_op_control *control)
+{
+ struct isl_union_map_gen_bin_data data = { control, NULL, NULL };
+
+ umap1 = isl_union_map_align_params(umap1, isl_union_map_get_space(umap2));
+ umap2 = isl_union_map_align_params(umap2, isl_union_map_get_space(umap1));
+
+ if (!umap1 || !umap2)
+ goto error;
+
+ data.umap2 = umap2;
+ data.res = isl_union_map_alloc(isl_space_copy(umap1->dim),
+ umap1->table.n);
+ if (isl_hash_table_foreach(umap1->dim->ctx, &umap1->table,
+ &gen_bin_entry, &data) < 0)
+ goto error;
+
+ isl_union_map_free(umap1);
+ isl_union_map_free(umap2);
+ return data.res;
+error:
+ isl_union_map_free(umap1);
+ isl_union_map_free(umap2);
+ isl_union_map_free(data.res);
+ return NULL;
+}
+
+__isl_give isl_union_map *isl_union_map_subtract(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ struct isl_bin_op_control control = {
+ .subtract = 1,
+ .match_space = &identity,
+ .fn_map = &isl_map_subtract,
+ };
+
+ return gen_bin_op(umap1, umap2, &control);
+}
+
+__isl_give isl_union_set *isl_union_set_subtract(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2)
+{
+ return isl_union_map_subtract(uset1, uset2);
+}
+
+struct isl_union_map_gen_bin_set_data {
+ isl_set *set;
+ isl_union_map *res;
+};
+
+static isl_stat intersect_params_entry(void **entry, void *user)
+{
+ struct isl_union_map_gen_bin_set_data *data = user;
+ isl_map *map = *entry;
+ int empty;
+
+ map = isl_map_copy(map);
+ map = isl_map_intersect_params(map, isl_set_copy(data->set));
+
+ empty = isl_map_is_empty(map);
+ if (empty < 0) {
+ isl_map_free(map);
+ return isl_stat_error;
+ }
+
+ data->res = isl_union_map_add_map(data->res, map);
+
+ return isl_stat_ok;
+}
+
+static __isl_give isl_union_map *gen_bin_set_op(__isl_take isl_union_map *umap,
+ __isl_take isl_set *set, isl_stat (*fn)(void **, void *))
+{
+ struct isl_union_map_gen_bin_set_data data = { NULL, NULL };
+
+ umap = isl_union_map_align_params(umap, isl_set_get_space(set));
+ set = isl_set_align_params(set, isl_union_map_get_space(umap));
+
+ if (!umap || !set)
+ goto error;
+
+ data.set = set;
+ data.res = isl_union_map_alloc(isl_space_copy(umap->dim),
+ umap->table.n);
+ if (isl_hash_table_foreach(umap->dim->ctx, &umap->table,
+ fn, &data) < 0)
+ goto error;
+
+ isl_union_map_free(umap);
+ isl_set_free(set);
+ return data.res;
+error:
+ isl_union_map_free(umap);
+ isl_set_free(set);
+ isl_union_map_free(data.res);
+ return NULL;
+}
+
+/* Intersect "umap" with the parameter domain "set".
+ *
+ * If "set" does not have any constraints, then we can return immediately.
+ */
+__isl_give isl_union_map *isl_union_map_intersect_params(
+ __isl_take isl_union_map *umap, __isl_take isl_set *set)
+{
+ int is_universe;
+
+ is_universe = isl_set_plain_is_universe(set);
+ if (is_universe < 0)
+ goto error;
+ if (is_universe) {
+ isl_set_free(set);
+ return umap;
+ }
+
+ return gen_bin_set_op(umap, set, &intersect_params_entry);
+error:
+ isl_union_map_free(umap);
+ isl_set_free(set);
+ return NULL;
+}
+
+__isl_give isl_union_set *isl_union_set_intersect_params(
+ __isl_take isl_union_set *uset, __isl_take isl_set *set)
+{
+ return isl_union_map_intersect_params(uset, set);
+}
+
+static __isl_give isl_union_map *union_map_intersect_params(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset)
+{
+ return isl_union_map_intersect_params(umap,
+ isl_set_from_union_set(uset));
+}
+
+static __isl_give isl_union_map *union_map_gist_params(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset)
+{
+ return isl_union_map_gist_params(umap, isl_set_from_union_set(uset));
+}
+
+struct isl_union_map_match_bin_data {
+ isl_union_map *umap2;
+ isl_union_map *res;
+ __isl_give isl_map *(*fn)(__isl_take isl_map*, __isl_take isl_map*);
+};
+
+static isl_stat match_bin_entry(void **entry, void *user)
+{
+ struct isl_union_map_match_bin_data *data = user;
+ struct isl_hash_table_entry *entry2;
+ isl_space *space;
+ isl_map *map = *entry;
+ int empty;
+
+ space = isl_map_peek_space(map);
+ entry2 = isl_union_map_find_entry(data->umap2, space, 0);
+ if (!entry2)
+ return isl_stat_error;
+ if (entry2 == isl_hash_table_entry_none)
+ return isl_stat_ok;
+
+ map = isl_map_copy(map);
+ map = data->fn(map, isl_map_copy(entry2->data));
+
+ empty = isl_map_is_empty(map);
+ if (empty < 0) {
+ isl_map_free(map);
+ return isl_stat_error;
+ }
+ if (empty) {
+ isl_map_free(map);
+ return isl_stat_ok;
+ }
+
+ data->res = isl_union_map_add_map(data->res, map);
+
+ return isl_stat_ok;
+}
+
+static __isl_give isl_union_map *match_bin_op(__isl_take isl_union_map *umap1,
+ __isl_take isl_union_map *umap2,
+ __isl_give isl_map *(*fn)(__isl_take isl_map*, __isl_take isl_map*))
+{
+ struct isl_union_map_match_bin_data data = { NULL, NULL, fn };
+
+ umap1 = isl_union_map_align_params(umap1, isl_union_map_get_space(umap2));
+ umap2 = isl_union_map_align_params(umap2, isl_union_map_get_space(umap1));
+
+ if (!umap1 || !umap2)
+ goto error;
+
+ data.umap2 = umap2;
+ data.res = isl_union_map_alloc(isl_space_copy(umap1->dim),
+ umap1->table.n);
+ if (isl_hash_table_foreach(umap1->dim->ctx, &umap1->table,
+ &match_bin_entry, &data) < 0)
+ goto error;
+
+ isl_union_map_free(umap1);
+ isl_union_map_free(umap2);
+ return data.res;
+error:
+ isl_union_map_free(umap1);
+ isl_union_map_free(umap2);
+ isl_union_map_free(data.res);
+ return NULL;
+}
+
+__isl_give isl_union_map *isl_union_map_intersect(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ return match_bin_op(umap1, umap2, &isl_map_intersect);
+}
+
+/* Compute the intersection of the two union_sets.
+ * As a special case, if exactly one of the two union_sets
+ * is a parameter domain, then intersect the parameter domain
+ * of the other one with this set.
+ */
+__isl_give isl_union_set *isl_union_set_intersect(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2)
+{
+ int p1, p2;
+
+ p1 = isl_union_set_is_params(uset1);
+ p2 = isl_union_set_is_params(uset2);
+ if (p1 < 0 || p2 < 0)
+ goto error;
+ if (!p1 && p2)
+ return union_map_intersect_params(uset1, uset2);
+ if (p1 && !p2)
+ return union_map_intersect_params(uset2, uset1);
+ return isl_union_map_intersect(uset1, uset2);
+error:
+ isl_union_set_free(uset1);
+ isl_union_set_free(uset2);
+ return NULL;
+}
+
+static isl_stat gist_params_entry(void **entry, void *user)
+{
+ struct isl_union_map_gen_bin_set_data *data = user;
+ isl_map *map = *entry;
+ int empty;
+
+ map = isl_map_copy(map);
+ map = isl_map_gist_params(map, isl_set_copy(data->set));
+
+ empty = isl_map_is_empty(map);
+ if (empty < 0) {
+ isl_map_free(map);
+ return isl_stat_error;
+ }
+
+ data->res = isl_union_map_add_map(data->res, map);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_map *isl_union_map_gist_params(
+ __isl_take isl_union_map *umap, __isl_take isl_set *set)
+{
+ return gen_bin_set_op(umap, set, &gist_params_entry);
+}
+
+__isl_give isl_union_set *isl_union_set_gist_params(
+ __isl_take isl_union_set *uset, __isl_take isl_set *set)
+{
+ return isl_union_map_gist_params(uset, set);
+}
+
+__isl_give isl_union_map *isl_union_map_gist(__isl_take isl_union_map *umap,
+ __isl_take isl_union_map *context)
+{
+ return match_bin_op(umap, context, &isl_map_gist);
+}
+
+__isl_give isl_union_set *isl_union_set_gist(__isl_take isl_union_set *uset,
+ __isl_take isl_union_set *context)
+{
+ if (isl_union_set_is_params(context))
+ return union_map_gist_params(uset, context);
+ return isl_union_map_gist(uset, context);
+}
+
+/* For each map in "umap", remove the constraints in the corresponding map
+ * of "context".
+ * Each map in "context" is assumed to consist of a single disjunct and
+ * to have explicit representations for all local variables.
+ */
+__isl_give isl_union_map *isl_union_map_plain_gist(
+ __isl_take isl_union_map *umap, __isl_take isl_union_map *context)
+{
+ return match_bin_op(umap, context, &isl_map_plain_gist);
+}
+
+/* For each set in "uset", remove the constraints in the corresponding set
+ * of "context".
+ * Each set in "context" is assumed to consist of a single disjunct and
+ * to have explicit representations for all local variables.
+ */
+__isl_give isl_union_set *isl_union_set_plain_gist(
+ __isl_take isl_union_set *uset, __isl_take isl_union_set *context)
+{
+ return isl_union_map_plain_gist(uset, context);
+}
+
+static __isl_give isl_map *lex_le_set(__isl_take isl_map *set1,
+ __isl_take isl_map *set2)
+{
+ return isl_set_lex_le_set(set_from_map(set1), set_from_map(set2));
+}
+
+static __isl_give isl_map *lex_lt_set(__isl_take isl_map *set1,
+ __isl_take isl_map *set2)
+{
+ return isl_set_lex_lt_set(set_from_map(set1), set_from_map(set2));
+}
+
+__isl_give isl_union_map *isl_union_set_lex_lt_union_set(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2)
+{
+ return match_bin_op(uset1, uset2, &lex_lt_set);
+}
+
+__isl_give isl_union_map *isl_union_set_lex_le_union_set(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2)
+{
+ return match_bin_op(uset1, uset2, &lex_le_set);
+}
+
+__isl_give isl_union_map *isl_union_set_lex_gt_union_set(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2)
+{
+ return isl_union_map_reverse(isl_union_set_lex_lt_union_set(uset2, uset1));
+}
+
+__isl_give isl_union_map *isl_union_set_lex_ge_union_set(
+ __isl_take isl_union_set *uset1, __isl_take isl_union_set *uset2)
+{
+ return isl_union_map_reverse(isl_union_set_lex_le_union_set(uset2, uset1));
+}
+
+__isl_give isl_union_map *isl_union_map_lex_gt_union_map(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ return isl_union_map_reverse(isl_union_map_lex_lt_union_map(umap2, umap1));
+}
+
+__isl_give isl_union_map *isl_union_map_lex_ge_union_map(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ return isl_union_map_reverse(isl_union_map_lex_le_union_map(umap2, umap1));
+}
+
+/* Intersect the domain of "umap" with "uset".
+ */
+static __isl_give isl_union_map *union_map_intersect_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset)
+{
+ struct isl_bin_op_control control = {
+ .match_space = &isl_space_domain,
+ .fn_map = &isl_map_intersect_domain,
+ };
+
+ return gen_bin_op(umap, uset, &control);
+}
+
+/* Intersect the domain of "umap" with "uset".
+ * If "uset" is a parameters domain, then intersect the parameter
+ * domain of "umap" with this set.
+ */
+__isl_give isl_union_map *isl_union_map_intersect_domain_union_set(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset)
+{
+ if (isl_union_set_is_params(uset))
+ return union_map_intersect_params(umap, uset);
+ else
+ return union_map_intersect_domain(umap, uset);
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_union_map *isl_union_map_intersect_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset)
+{
+ return isl_union_map_intersect_domain_union_set(umap, uset);
+}
+
+/* Remove the elements of "uset" from the domain of "umap".
+ */
+__isl_give isl_union_map *isl_union_map_subtract_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *dom)
+{
+ struct isl_bin_op_control control = {
+ .subtract = 1,
+ .match_space = &isl_space_domain,
+ .fn_map = &isl_map_subtract_domain,
+ };
+
+ return gen_bin_op(umap, dom, &control);
+}
+
+/* Remove the elements of "uset" from the range of "umap".
+ */
+__isl_give isl_union_map *isl_union_map_subtract_range(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *dom)
+{
+ struct isl_bin_op_control control = {
+ .subtract = 1,
+ .match_space = &isl_space_range,
+ .fn_map = &isl_map_subtract_range,
+ };
+
+ return gen_bin_op(umap, dom, &control);
+}
+
+/* Compute the gist of "umap" with respect to the domain "uset".
+ */
+static __isl_give isl_union_map *union_map_gist_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset)
+{
+ struct isl_bin_op_control control = {
+ .match_space = &isl_space_domain,
+ .fn_map = &isl_map_gist_domain,
+ };
+
+ return gen_bin_op(umap, uset, &control);
+}
+
+/* Compute the gist of "umap" with respect to the domain "uset".
+ * If "uset" is a parameters domain, then compute the gist
+ * with respect to this parameter domain.
+ */
+__isl_give isl_union_map *isl_union_map_gist_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset)
+{
+ if (isl_union_set_is_params(uset))
+ return union_map_gist_params(umap, uset);
+ else
+ return union_map_gist_domain(umap, uset);
+}
+
+/* Compute the gist of "umap" with respect to the range "uset".
+ */
+__isl_give isl_union_map *isl_union_map_gist_range(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset)
+{
+ struct isl_bin_op_control control = {
+ .match_space = &isl_space_range,
+ .fn_map = &isl_map_gist_range,
+ };
+
+ return gen_bin_op(umap, uset, &control);
+}
+
+__isl_give isl_union_map *isl_union_map_intersect_range_union_set(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset)
+{
+ struct isl_bin_op_control control = {
+ .match_space = &isl_space_range,
+ .fn_map = &isl_map_intersect_range,
+ };
+
+ return gen_bin_op(umap, uset, &control);
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_union_map *isl_union_map_intersect_range(
+ __isl_take isl_union_map *umap, __isl_take isl_union_set *uset)
+{
+ return isl_union_map_intersect_range_union_set(umap, uset);
+}
+
+/* Intersect each map in "umap" in a space [A -> B] -> C
+ * with the corresponding map in "factor" in the space A -> C and
+ * collect the results.
+ */
+__isl_give isl_union_map *isl_union_map_intersect_domain_factor_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_map *factor)
+{
+ struct isl_bin_op_control control = {
+ .filter = &isl_map_domain_is_wrapping,
+ .match_space = &isl_space_domain_factor_domain,
+ .fn_map = &isl_map_intersect_domain_factor_domain,
+ };
+
+ return gen_bin_op(umap, factor, &control);
+}
+
+/* Intersect each map in "umap" in a space [A -> B] -> C
+ * with the corresponding map in "factor" in the space B -> C and
+ * collect the results.
+ */
+__isl_give isl_union_map *isl_union_map_intersect_domain_factor_range(
+ __isl_take isl_union_map *umap, __isl_take isl_union_map *factor)
+{
+ struct isl_bin_op_control control = {
+ .filter = &isl_map_domain_is_wrapping,
+ .match_space = &isl_space_domain_factor_range,
+ .fn_map = &isl_map_intersect_domain_factor_range,
+ };
+
+ return gen_bin_op(umap, factor, &control);
+}
+
+/* Intersect each map in "umap" in a space A -> [B -> C]
+ * with the corresponding map in "factor" in the space A -> B and
+ * collect the results.
+ */
+__isl_give isl_union_map *isl_union_map_intersect_range_factor_domain(
+ __isl_take isl_union_map *umap, __isl_take isl_union_map *factor)
+{
+ struct isl_bin_op_control control = {
+ .filter = &isl_map_range_is_wrapping,
+ .match_space = &isl_space_range_factor_domain,
+ .fn_map = &isl_map_intersect_range_factor_domain,
+ };
+
+ return gen_bin_op(umap, factor, &control);
+}
+
+/* Intersect each map in "umap" in a space A -> [B -> C]
+ * with the corresponding map in "factor" in the space A -> C and
+ * collect the results.
+ */
+__isl_give isl_union_map *isl_union_map_intersect_range_factor_range(
+ __isl_take isl_union_map *umap, __isl_take isl_union_map *factor)
+{
+ struct isl_bin_op_control control = {
+ .filter = &isl_map_range_is_wrapping,
+ .match_space = &isl_space_range_factor_range,
+ .fn_map = &isl_map_intersect_range_factor_range,
+ };
+
+ return gen_bin_op(umap, factor, &control);
+}
+
+struct isl_union_map_bin_data {
+ isl_union_map *umap2;
+ isl_union_map *res;
+ isl_map *map;
+ isl_stat (*fn)(void **entry, void *user);
+};
+
+static isl_stat apply_range_entry(void **entry, void *user)
+{
+ struct isl_union_map_bin_data *data = user;
+ isl_map *map2 = *entry;
+ isl_bool empty, match;
+
+ match = isl_map_tuple_is_equal(data->map, isl_dim_out,
+ map2, isl_dim_in);
+ if (match < 0)
+ return isl_stat_error;
+ if (!match)
+ return isl_stat_ok;
+
+ map2 = isl_map_apply_range(isl_map_copy(data->map), isl_map_copy(map2));
+
+ empty = isl_map_is_empty(map2);
+ if (empty < 0) {
+ isl_map_free(map2);
+ return isl_stat_error;
+ }
+ if (empty) {
+ isl_map_free(map2);
+ return isl_stat_ok;
+ }
+
+ data->res = isl_union_map_add_map(data->res, map2);
+
+ return isl_stat_ok;
+}
+
+static isl_stat bin_entry(void **entry, void *user)
+{
+ struct isl_union_map_bin_data *data = user;
+ isl_map *map = *entry;
+
+ data->map = map;
+ if (isl_hash_table_foreach(data->umap2->dim->ctx, &data->umap2->table,
+ data->fn, data) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+static __isl_give isl_union_map *bin_op(__isl_take isl_union_map *umap1,
+ __isl_take isl_union_map *umap2,
+ isl_stat (*fn)(void **entry, void *user))
+{
+ struct isl_union_map_bin_data data = { NULL, NULL, NULL, fn };
+
+ umap1 = isl_union_map_align_params(umap1, isl_union_map_get_space(umap2));
+ umap2 = isl_union_map_align_params(umap2, isl_union_map_get_space(umap1));
+
+ if (!umap1 || !umap2)
+ goto error;
+
+ data.umap2 = umap2;
+ data.res = isl_union_map_alloc(isl_space_copy(umap1->dim),
+ umap1->table.n);
+ if (isl_hash_table_foreach(umap1->dim->ctx, &umap1->table,
+ &bin_entry, &data) < 0)
+ goto error;
+
+ isl_union_map_free(umap1);
+ isl_union_map_free(umap2);
+ return data.res;
+error:
+ isl_union_map_free(umap1);
+ isl_union_map_free(umap2);
+ isl_union_map_free(data.res);
+ return NULL;
+}
+
+__isl_give isl_union_map *isl_union_map_apply_range(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ return bin_op(umap1, umap2, &apply_range_entry);
+}
+
+__isl_give isl_union_map *isl_union_map_apply_domain(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ umap1 = isl_union_map_reverse(umap1);
+ umap1 = isl_union_map_apply_range(umap1, umap2);
+ return isl_union_map_reverse(umap1);
+}
+
+__isl_give isl_union_set *isl_union_set_apply(
+ __isl_take isl_union_set *uset, __isl_take isl_union_map *umap)
+{
+ return isl_union_map_apply_range(uset, umap);
+}
+
+static isl_stat map_lex_lt_entry(void **entry, void *user)
+{
+ struct isl_union_map_bin_data *data = user;
+ isl_map *map2 = *entry;
+ isl_bool match;
+
+ match = isl_map_tuple_is_equal(data->map, isl_dim_out,
+ map2, isl_dim_out);
+ if (match < 0)
+ return isl_stat_error;
+ if (!match)
+ return isl_stat_ok;
+
+ map2 = isl_map_lex_lt_map(isl_map_copy(data->map), isl_map_copy(map2));
+
+ data->res = isl_union_map_add_map(data->res, map2);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_map *isl_union_map_lex_lt_union_map(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ return bin_op(umap1, umap2, &map_lex_lt_entry);
+}
+
+static isl_stat map_lex_le_entry(void **entry, void *user)
+{
+ struct isl_union_map_bin_data *data = user;
+ isl_map *map2 = *entry;
+ isl_bool match;
+
+ match = isl_map_tuple_is_equal(data->map, isl_dim_out,
+ map2, isl_dim_out);
+ if (match < 0)
+ return isl_stat_error;
+ if (!match)
+ return isl_stat_ok;
+
+ map2 = isl_map_lex_le_map(isl_map_copy(data->map), isl_map_copy(map2));
+
+ data->res = isl_union_map_add_map(data->res, map2);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_map *isl_union_map_lex_le_union_map(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ return bin_op(umap1, umap2, &map_lex_le_entry);
+}
+
+static isl_stat product_entry(void **entry, void *user)
+{
+ struct isl_union_map_bin_data *data = user;
+ isl_map *map2 = *entry;
+
+ map2 = isl_map_product(isl_map_copy(data->map), isl_map_copy(map2));
+
+ data->res = isl_union_map_add_map(data->res, map2);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_map *isl_union_map_product(__isl_take isl_union_map *umap1,
+ __isl_take isl_union_map *umap2)
+{
+ return bin_op(umap1, umap2, &product_entry);
+}
+
+static isl_stat set_product_entry(void **entry, void *user)
+{
+ struct isl_union_map_bin_data *data = user;
+ isl_set *set2 = *entry;
+
+ set2 = isl_set_product(isl_set_copy(data->map), isl_set_copy(set2));
+
+ data->res = isl_union_set_add_set(data->res, set2);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_set *isl_union_set_product(__isl_take isl_union_set *uset1,
+ __isl_take isl_union_set *uset2)
+{
+ return bin_op(uset1, uset2, &set_product_entry);
+}
+
+static isl_stat domain_product_entry(void **entry, void *user)
+{
+ struct isl_union_map_bin_data *data = user;
+ isl_map *map2 = *entry;
+ isl_bool match;
+
+ match = isl_map_tuple_is_equal(data->map, isl_dim_out,
+ map2, isl_dim_out);
+ if (match < 0)
+ return isl_stat_error;
+ if (!match)
+ return isl_stat_ok;
+
+ map2 = isl_map_domain_product(isl_map_copy(data->map),
+ isl_map_copy(map2));
+
+ data->res = isl_union_map_add_map(data->res, map2);
+
+ return isl_stat_ok;
+}
+
+/* Given two maps A -> B and C -> D, construct a map [A -> C] -> (B * D)
+ */
+__isl_give isl_union_map *isl_union_map_domain_product(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ return bin_op(umap1, umap2, &domain_product_entry);
+}
+
+static isl_stat range_product_entry(void **entry, void *user)
+{
+ struct isl_union_map_bin_data *data = user;
+ isl_map *map2 = *entry;
+ isl_bool match;
+
+ match = isl_map_tuple_is_equal(data->map, isl_dim_in, map2, isl_dim_in);
+ if (match < 0)
+ return isl_stat_error;
+ if (!match)
+ return isl_stat_ok;
+
+ map2 = isl_map_range_product(isl_map_copy(data->map),
+ isl_map_copy(map2));
+
+ data->res = isl_union_map_add_map(data->res, map2);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_map *isl_union_map_range_product(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ return bin_op(umap1, umap2, &range_product_entry);
+}
+
+/* If data->map A -> B and "map2" C -> D have the same range space,
+ * then add (A, C) -> (B * D) to data->res.
+ */
+static isl_stat flat_domain_product_entry(void **entry, void *user)
+{
+ struct isl_union_map_bin_data *data = user;
+ isl_map *map2 = *entry;
+ isl_bool match;
+
+ match = isl_map_tuple_is_equal(data->map, isl_dim_out,
+ map2, isl_dim_out);
+ if (match < 0)
+ return isl_stat_error;
+ if (!match)
+ return isl_stat_ok;
+
+ map2 = isl_map_flat_domain_product(isl_map_copy(data->map),
+ isl_map_copy(map2));
+
+ data->res = isl_union_map_add_map(data->res, map2);
+
+ return isl_stat_ok;
+}
+
+/* Given two maps A -> B and C -> D, construct a map (A, C) -> (B * D).
+ */
+__isl_give isl_union_map *isl_union_map_flat_domain_product(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ return bin_op(umap1, umap2, &flat_domain_product_entry);
+}
+
+static isl_stat flat_range_product_entry(void **entry, void *user)
+{
+ struct isl_union_map_bin_data *data = user;
+ isl_map *map2 = *entry;
+ isl_bool match;
+
+ match = isl_map_tuple_is_equal(data->map, isl_dim_in, map2, isl_dim_in);
+ if (match < 0)
+ return isl_stat_error;
+ if (!match)
+ return isl_stat_ok;
+
+ map2 = isl_map_flat_range_product(isl_map_copy(data->map),
+ isl_map_copy(map2));
+
+ data->res = isl_union_map_add_map(data->res, map2);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_map *isl_union_map_flat_range_product(
+ __isl_take isl_union_map *umap1, __isl_take isl_union_map *umap2)
+{
+ return bin_op(umap1, umap2, &flat_range_product_entry);
+}
+
+/* Data structure that specifies how un_op should modify
+ * the maps in the union map.
+ *
+ * If "inplace" is set, then the maps in the input union map
+ * are modified in place. This means that "fn_map" should not
+ * change the meaning of the map or that the union map only
+ * has a single reference.
+ * If "total" is set, then all maps need to be modified and
+ * the results need to live in the same space.
+ * Otherwise, a new union map is constructed to store the results.
+ * If "filter" is not NULL, then only the input maps that satisfy "filter"
+ * are taken into account. "filter_user" is passed as the second argument
+ * to "filter". No filter can be set if "inplace" or
+ * "total" is set.
+ * At most one of "fn_map" or "fn_map2" can be set, specifying
+ * how the maps (selected by "filter") should be transformed.
+ * If "fn_map2" is set, then "fn_map2_user" is passed as the second argument.
+ */
+struct isl_un_op_control {
+ int inplace;
+ int total;
+ isl_bool (*filter)(__isl_keep isl_map *map, void *user);
+ void *filter_user;
+ __isl_give isl_map *(*fn_map)(__isl_take isl_map *map);
+ __isl_give isl_map *(*fn_map2)(__isl_take isl_map *map, void *user);
+ void *fn_map2_user;
+};
+
+/* Data structure for wrapping the data for un_op_filter_drop_user.
+ * "filter" is the function that is being wrapped.
+ */
+struct isl_un_op_drop_user_data {
+ isl_bool (*filter)(__isl_keep isl_map *map);
+};
+
+/* Wrapper for isl_un_op_control filters that do not require
+ * a second argument.
+ * Simply call data->filter without the second argument.
+ */
+static isl_bool un_op_filter_drop_user(__isl_keep isl_map *map, void *user)
+{
+ struct isl_un_op_drop_user_data *data = user;
+ return data->filter(map);
+}
+
+/* Internal data structure for "un_op".
+ * "control" specifies how the maps in the union map should be modified.
+ * "res" collects the results.
+ */
+struct isl_union_map_un_data {
+ struct isl_un_op_control *control;
+ isl_union_map *res;
+};
+
+/* isl_hash_table_foreach callback for un_op.
+ * Handle the map that "entry" points to.
+ *
+ * If control->filter is set, then check if this map satisfies the filter.
+ * If so (or if control->filter is not set), modify the map
+ * by calling control->fn_map or control->fn_map2 (if set) and
+ * either add the result to data->res or
+ * replace the original entry by the result (if control->inplace is set).
+ */
+static isl_stat un_entry(void **entry, void *user)
+{
+ struct isl_union_map_un_data *data = user;
+ struct isl_un_op_control *control = data->control;
+ isl_map *map = *entry;
+
+ if (control->filter) {
+ isl_bool ok;
+
+ ok = control->filter(map, control->filter_user);
+ if (ok < 0)
+ return isl_stat_error;
+ if (!ok)
+ return isl_stat_ok;
+ }
+
+ map = isl_map_copy(map);
+ if (control->fn_map2 != NULL)
+ map = control->fn_map2(map, control->fn_map2_user);
+ else if (control->fn_map != NULL)
+ map = control->fn_map(map);
+ if (!map)
+ return isl_stat_error;
+ if (control->inplace) {
+ isl_map_free(*entry);
+ *entry = map;
+ } else {
+ data->res = isl_union_map_add_map(data->res, map);
+ if (!data->res)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Modify the maps in "umap" based on "control".
+ * If control->inplace is set, then modify the maps in "umap" in-place.
+ * Otherwise, create a new union map to hold the results.
+ * If control->total is set, then perform an inplace computation
+ * if "umap" is only referenced once. Otherwise, create a new union map
+ * to store the results.
+ */
+static __isl_give isl_union_map *un_op(__isl_take isl_union_map *umap,
+ struct isl_un_op_control *control)
+{
+ struct isl_union_map_un_data data = { control };
+
+ if (!umap)
+ return NULL;
+ if (!!control->fn_map && !!control->fn_map2)
+ isl_die(isl_union_map_get_ctx(umap), isl_error_internal,
+ "at most one mapping function can be specified",
+ return isl_union_map_free(umap));
+ if ((control->inplace || control->total) && control->filter)
+ isl_die(isl_union_map_get_ctx(umap), isl_error_invalid,
+ "inplace/total modification cannot be filtered",
+ return isl_union_map_free(umap));
+
+ if (control->total && umap->ref == 1)
+ control->inplace = 1;
+ if (control->inplace) {
+ data.res = umap;
+ } else {
+ isl_space *space;
+
+ space = isl_union_map_get_space(umap);
+ data.res = isl_union_map_alloc(space, umap->table.n);
+ }
+ if (isl_hash_table_foreach(isl_union_map_get_ctx(umap),
+ &umap->table, &un_entry, &data) < 0)
+ data.res = isl_union_map_free(data.res);
+
+ if (control->inplace)
+ return data.res;
+ isl_union_map_free(umap);
+ return data.res;
+}
+
+__isl_give isl_union_map *isl_union_map_from_range(
+ __isl_take isl_union_set *uset)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &isl_map_from_range,
+ };
+ return un_op(uset, &control);
+}
+
+__isl_give isl_union_map *isl_union_map_from_domain(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_reverse(isl_union_map_from_range(uset));
+}
+
+__isl_give isl_union_map *isl_union_map_from_domain_and_range(
+ __isl_take isl_union_set *domain, __isl_take isl_union_set *range)
+{
+ return isl_union_map_apply_range(isl_union_map_from_domain(domain),
+ isl_union_map_from_range(range));
+}
+
+/* Modify the maps in "umap" by applying "fn" on them.
+ * "fn" should apply to all maps in "umap" and should not modify the space.
+ */
+static __isl_give isl_union_map *total(__isl_take isl_union_map *umap,
+ __isl_give isl_map *(*fn)(__isl_take isl_map *))
+{
+ struct isl_un_op_control control = {
+ .total = 1,
+ .fn_map = fn,
+ };
+
+ return un_op(umap, &control);
+}
+
+/* Compute the affine hull of "map" and return the result as an isl_map.
+ */
+static __isl_give isl_map *isl_map_affine_hull_map(__isl_take isl_map *map)
+{
+ return isl_map_from_basic_map(isl_map_affine_hull(map));
+}
+
+__isl_give isl_union_map *isl_union_map_affine_hull(
+ __isl_take isl_union_map *umap)
+{
+ return total(umap, &isl_map_affine_hull_map);
+}
+
+__isl_give isl_union_set *isl_union_set_affine_hull(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_affine_hull(uset);
+}
+
+/* Wrapper around isl_set_combined_lineality_space
+ * that returns the combined lineality space in the form of an isl_set
+ * instead of an isl_basic_set.
+ */
+static __isl_give isl_set *combined_lineality_space(__isl_take isl_set *set)
+{
+ return isl_set_from_basic_set(isl_set_combined_lineality_space(set));
+}
+
+/* For each set in "uset", compute the (linear) hull
+ * of the lineality spaces of its basic sets and
+ * collect and return the results.
+ */
+__isl_give isl_union_set *isl_union_set_combined_lineality_space(
+ __isl_take isl_union_set *uset)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &combined_lineality_space,
+ };
+ return un_op(uset, &control);
+}
+
+/* Compute the polyhedral hull of "map" and return the result as an isl_map.
+ */
+static __isl_give isl_map *isl_map_polyhedral_hull_map(__isl_take isl_map *map)
+{
+ return isl_map_from_basic_map(isl_map_polyhedral_hull(map));
+}
+
+__isl_give isl_union_map *isl_union_map_polyhedral_hull(
+ __isl_take isl_union_map *umap)
+{
+ return total(umap, &isl_map_polyhedral_hull_map);
+}
+
+__isl_give isl_union_set *isl_union_set_polyhedral_hull(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_polyhedral_hull(uset);
+}
+
+/* Compute a superset of the convex hull of "map" that is described
+ * by only translates of the constraints in the constituents of "map" and
+ * return the result as an isl_map.
+ */
+static __isl_give isl_map *isl_map_simple_hull_map(__isl_take isl_map *map)
+{
+ return isl_map_from_basic_map(isl_map_simple_hull(map));
+}
+
+__isl_give isl_union_map *isl_union_map_simple_hull(
+ __isl_take isl_union_map *umap)
+{
+ return total(umap, &isl_map_simple_hull_map);
+}
+
+__isl_give isl_union_set *isl_union_set_simple_hull(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_simple_hull(uset);
+}
+
+static __isl_give isl_union_map *inplace(__isl_take isl_union_map *umap,
+ __isl_give isl_map *(*fn)(__isl_take isl_map *))
+{
+ struct isl_un_op_control control = {
+ .inplace = 1,
+ .fn_map = fn,
+ };
+
+ return un_op(umap, &control);
+}
+
+/* Remove redundant constraints in each of the basic maps of "umap".
+ * Since removing redundant constraints does not change the meaning
+ * or the space, the operation can be performed in-place.
+ */
+__isl_give isl_union_map *isl_union_map_remove_redundancies(
+ __isl_take isl_union_map *umap)
+{
+ return inplace(umap, &isl_map_remove_redundancies);
+}
+
+/* Remove redundant constraints in each of the basic sets of "uset".
+ */
+__isl_give isl_union_set *isl_union_set_remove_redundancies(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_remove_redundancies(uset);
+}
+
+__isl_give isl_union_map *isl_union_map_coalesce(
+ __isl_take isl_union_map *umap)
+{
+ return inplace(umap, &isl_map_coalesce);
+}
+
+__isl_give isl_union_set *isl_union_set_coalesce(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_coalesce(uset);
+}
+
+__isl_give isl_union_map *isl_union_map_detect_equalities(
+ __isl_take isl_union_map *umap)
+{
+ return inplace(umap, &isl_map_detect_equalities);
+}
+
+__isl_give isl_union_set *isl_union_set_detect_equalities(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_detect_equalities(uset);
+}
+
+__isl_give isl_union_map *isl_union_map_compute_divs(
+ __isl_take isl_union_map *umap)
+{
+ return inplace(umap, &isl_map_compute_divs);
+}
+
+__isl_give isl_union_set *isl_union_set_compute_divs(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_compute_divs(uset);
+}
+
+__isl_give isl_union_map *isl_union_map_lexmin(
+ __isl_take isl_union_map *umap)
+{
+ return total(umap, &isl_map_lexmin);
+}
+
+__isl_give isl_union_set *isl_union_set_lexmin(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_lexmin(uset);
+}
+
+__isl_give isl_union_map *isl_union_map_lexmax(
+ __isl_take isl_union_map *umap)
+{
+ return total(umap, &isl_map_lexmax);
+}
+
+__isl_give isl_union_set *isl_union_set_lexmax(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_lexmax(uset);
+}
+
+/* Return the universe in the space of "map".
+ */
+static __isl_give isl_map *universe(__isl_take isl_map *map)
+{
+ isl_space *space;
+
+ space = isl_map_get_space(map);
+ isl_map_free(map);
+ return isl_map_universe(space);
+}
+
+__isl_give isl_union_map *isl_union_map_universe(__isl_take isl_union_map *umap)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &universe,
+ };
+ return un_op(umap, &control);
+}
+
+__isl_give isl_union_set *isl_union_set_universe(__isl_take isl_union_set *uset)
+{
+ return isl_union_map_universe(uset);
+}
+
+__isl_give isl_union_map *isl_union_map_reverse(__isl_take isl_union_map *umap)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &isl_map_reverse,
+ };
+ return un_op(umap, &control);
+}
+
+/* Given a union map, take the maps of the form A -> (B -> C) and
+ * return the union of the corresponding maps A -> (C -> B).
+ */
+__isl_give isl_union_map *isl_union_map_range_reverse(
+ __isl_take isl_union_map *umap)
+{
+ struct isl_un_op_drop_user_data data = { &isl_map_range_is_wrapping };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_map_range_reverse,
+ };
+ return un_op(umap, &control);
+}
+
+/* Compute the parameter domain of the given union map.
+ */
+__isl_give isl_set *isl_union_map_params(__isl_take isl_union_map *umap)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &isl_map_params,
+ };
+ int empty;
+
+ empty = isl_union_map_is_empty(umap);
+ if (empty < 0)
+ goto error;
+ if (empty) {
+ isl_space *space;
+ space = isl_union_map_get_space(umap);
+ isl_union_map_free(umap);
+ return isl_set_empty(space);
+ }
+ return isl_set_from_union_set(un_op(umap, &control));
+error:
+ isl_union_map_free(umap);
+ return NULL;
+}
+
+/* Compute the parameter domain of the given union set.
+ */
+__isl_give isl_set *isl_union_set_params(__isl_take isl_union_set *uset)
+{
+ return isl_union_map_params(uset);
+}
+
+__isl_give isl_union_set *isl_union_map_domain(__isl_take isl_union_map *umap)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &isl_map_domain,
+ };
+ return un_op(umap, &control);
+}
+
+__isl_give isl_union_set *isl_union_map_range(__isl_take isl_union_map *umap)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &isl_map_range,
+ };
+ return un_op(umap, &control);
+}
+
+__isl_give isl_union_map *isl_union_map_domain_map(
+ __isl_take isl_union_map *umap)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &isl_map_domain_map,
+ };
+ return un_op(umap, &control);
+}
+
+/* Construct an isl_pw_multi_aff that maps "map" to its domain and
+ * add the result to "res".
+ */
+static isl_stat domain_map_upma(__isl_take isl_map *map, void *user)
+{
+ isl_union_pw_multi_aff **res = user;
+ isl_multi_aff *ma;
+ isl_pw_multi_aff *pma;
+
+ ma = isl_multi_aff_domain_map(isl_map_get_space(map));
+ pma = isl_pw_multi_aff_alloc(isl_map_wrap(map), ma);
+ *res = isl_union_pw_multi_aff_add_pw_multi_aff(*res, pma);
+
+ return *res ? isl_stat_ok : isl_stat_error;
+
+}
+
+/* Return an isl_union_pw_multi_aff that maps a wrapped copy of "umap"
+ * to its domain.
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_map_domain_map_union_pw_multi_aff(
+ __isl_take isl_union_map *umap)
+{
+ isl_union_pw_multi_aff *res;
+
+ res = isl_union_pw_multi_aff_empty(isl_union_map_get_space(umap));
+ if (isl_union_map_foreach_map(umap, &domain_map_upma, &res) < 0)
+ res = isl_union_pw_multi_aff_free(res);
+
+ isl_union_map_free(umap);
+ return res;
+}
+
+__isl_give isl_union_map *isl_union_map_range_map(
+ __isl_take isl_union_map *umap)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &isl_map_range_map,
+ };
+ return un_op(umap, &control);
+}
+
+/* Given a collection of wrapped maps of the form A[B -> C],
+ * return the collection of maps A[B -> C] -> B.
+ */
+__isl_give isl_union_map *isl_union_set_wrapped_domain_map(
+ __isl_take isl_union_set *uset)
+{
+ struct isl_un_op_drop_user_data data = { &isl_set_is_wrapping };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_set_wrapped_domain_map,
+ };
+ return un_op(uset, &control);
+}
+
+/* Does "map" relate elements from the same space?
+ */
+static isl_bool equal_tuples(__isl_keep isl_map *map, void *user)
+{
+ return isl_map_tuple_is_equal(map, isl_dim_in, map, isl_dim_out);
+}
+
+__isl_give isl_union_set *isl_union_map_deltas(__isl_take isl_union_map *umap)
+{
+ struct isl_un_op_control control = {
+ .filter = &equal_tuples,
+ .fn_map = &isl_map_deltas,
+ };
+ return un_op(umap, &control);
+}
+
+__isl_give isl_union_map *isl_union_map_deltas_map(
+ __isl_take isl_union_map *umap)
+{
+ struct isl_un_op_control control = {
+ .filter = &equal_tuples,
+ .fn_map = &isl_map_deltas_map,
+ };
+ return un_op(umap, &control);
+}
+
+__isl_give isl_union_map *isl_union_set_identity(__isl_take isl_union_set *uset)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &isl_set_identity,
+ };
+ return un_op(uset, &control);
+}
+
+/* Construct an identity isl_pw_multi_aff on "set" and add it to *res.
+ */
+static isl_stat identity_upma(__isl_take isl_set *set, void *user)
+{
+ isl_union_pw_multi_aff **res = user;
+ isl_space *space;
+ isl_pw_multi_aff *pma;
+
+ space = isl_space_map_from_set(isl_set_get_space(set));
+ pma = isl_pw_multi_aff_identity(space);
+ pma = isl_pw_multi_aff_intersect_domain(pma, set);
+ *res = isl_union_pw_multi_aff_add_pw_multi_aff(*res, pma);
+
+ return *res ? isl_stat_ok : isl_stat_error;
+}
+
+/* Return an identity function on "uset" in the form
+ * of an isl_union_pw_multi_aff.
+ */
+__isl_give isl_union_pw_multi_aff *isl_union_set_identity_union_pw_multi_aff(
+ __isl_take isl_union_set *uset)
+{
+ isl_union_pw_multi_aff *res;
+
+ res = isl_union_pw_multi_aff_empty(isl_union_set_get_space(uset));
+ if (isl_union_set_foreach_set(uset, &identity_upma, &res) < 0)
+ res = isl_union_pw_multi_aff_free(res);
+
+ isl_union_set_free(uset);
+ return res;
+}
+
+/* For each map in "umap" of the form [A -> B] -> C,
+ * construct the map A -> C and collect the results.
+ */
+__isl_give isl_union_map *isl_union_map_domain_factor_domain(
+ __isl_take isl_union_map *umap)
+{
+ struct isl_un_op_drop_user_data data = { &isl_map_domain_is_wrapping };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_map_domain_factor_domain,
+ };
+ return un_op(umap, &control);
+}
+
+/* For each map in "umap" of the form [A -> B] -> C,
+ * construct the map B -> C and collect the results.
+ */
+__isl_give isl_union_map *isl_union_map_domain_factor_range(
+ __isl_take isl_union_map *umap)
+{
+ struct isl_un_op_drop_user_data data = { &isl_map_domain_is_wrapping };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_map_domain_factor_range,
+ };
+ return un_op(umap, &control);
+}
+
+/* For each map in "umap" of the form A -> [B -> C],
+ * construct the map A -> B and collect the results.
+ */
+__isl_give isl_union_map *isl_union_map_range_factor_domain(
+ __isl_take isl_union_map *umap)
+{
+ struct isl_un_op_drop_user_data data = { &isl_map_range_is_wrapping };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_map_range_factor_domain,
+ };
+ return un_op(umap, &control);
+}
+
+/* For each map in "umap" of the form A -> [B -> C],
+ * construct the map A -> C and collect the results.
+ */
+__isl_give isl_union_map *isl_union_map_range_factor_range(
+ __isl_take isl_union_map *umap)
+{
+ struct isl_un_op_drop_user_data data = { &isl_map_range_is_wrapping };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_map_range_factor_range,
+ };
+ return un_op(umap, &control);
+}
+
+/* For each map in "umap" of the form [A -> B] -> [C -> D],
+ * construct the map A -> C and collect the results.
+ */
+__isl_give isl_union_map *isl_union_map_factor_domain(
+ __isl_take isl_union_map *umap)
+{
+ struct isl_un_op_drop_user_data data = { &isl_map_is_product };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_map_factor_domain,
+ };
+ return un_op(umap, &control);
+}
+
+/* For each map in "umap" of the form [A -> B] -> [C -> D],
+ * construct the map B -> D and collect the results.
+ */
+__isl_give isl_union_map *isl_union_map_factor_range(
+ __isl_take isl_union_map *umap)
+{
+ struct isl_un_op_drop_user_data data = { &isl_map_is_product };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_map_factor_range,
+ };
+ return un_op(umap, &control);
+}
+
+__isl_give isl_union_map *isl_union_set_unwrap(__isl_take isl_union_set *uset)
+{
+ struct isl_un_op_drop_user_data data = { &isl_set_is_wrapping };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_set_unwrap,
+ };
+ return un_op(uset, &control);
+}
+
+__isl_give isl_union_set *isl_union_map_wrap(__isl_take isl_union_map *umap)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &isl_map_wrap,
+ };
+ return un_op(umap, &control);
+}
+
+struct isl_union_map_is_subset_data {
+ isl_union_map *umap2;
+ isl_bool is_subset;
+};
+
+static isl_stat is_subset_entry(void **entry, void *user)
+{
+ struct isl_union_map_is_subset_data *data = user;
+ struct isl_hash_table_entry *entry2;
+ isl_space *space;
+ isl_map *map = *entry;
+
+ space = isl_map_peek_space(map);
+ entry2 = isl_union_map_find_entry(data->umap2, space, 0);
+ if (!entry2)
+ return isl_stat_error;
+ if (entry2 == isl_hash_table_entry_none) {
+ int empty = isl_map_is_empty(map);
+ if (empty < 0)
+ return isl_stat_error;
+ if (empty)
+ return isl_stat_ok;
+ data->is_subset = isl_bool_false;
+ return isl_stat_error;
+ }
+
+ data->is_subset = isl_map_is_subset(map, entry2->data);
+ if (data->is_subset < 0 || !data->is_subset)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+isl_bool isl_union_map_is_subset(__isl_keep isl_union_map *umap1,
+ __isl_keep isl_union_map *umap2)
+{
+ struct isl_union_map_is_subset_data data = { NULL, isl_bool_true };
+
+ if (!umap1 || !umap2)
+ return isl_bool_error;
+
+ data.umap2 = umap2;
+ if (isl_hash_table_foreach(umap1->dim->ctx, &umap1->table,
+ &is_subset_entry, &data) < 0 &&
+ data.is_subset)
+ return isl_bool_error;
+
+ return data.is_subset;
+}
+
+isl_bool isl_union_set_is_subset(__isl_keep isl_union_set *uset1,
+ __isl_keep isl_union_set *uset2)
+{
+ return isl_union_map_is_subset(uset1, uset2);
+}
+
+isl_bool isl_union_map_is_equal(__isl_keep isl_union_map *umap1,
+ __isl_keep isl_union_map *umap2)
+{
+ isl_bool is_subset;
+
+ if (!umap1 || !umap2)
+ return isl_bool_error;
+ is_subset = isl_union_map_is_subset(umap1, umap2);
+ if (is_subset != isl_bool_true)
+ return is_subset;
+ is_subset = isl_union_map_is_subset(umap2, umap1);
+ return is_subset;
+}
+
+isl_bool isl_union_set_is_equal(__isl_keep isl_union_set *uset1,
+ __isl_keep isl_union_set *uset2)
+{
+ return isl_union_map_is_equal(uset1, uset2);
+}
+
+isl_bool isl_union_map_is_strict_subset(__isl_keep isl_union_map *umap1,
+ __isl_keep isl_union_map *umap2)
+{
+ isl_bool is_subset;
+
+ if (!umap1 || !umap2)
+ return isl_bool_error;
+ is_subset = isl_union_map_is_subset(umap1, umap2);
+ if (is_subset != isl_bool_true)
+ return is_subset;
+ is_subset = isl_union_map_is_subset(umap2, umap1);
+ return isl_bool_not(is_subset);
+}
+
+isl_bool isl_union_set_is_strict_subset(__isl_keep isl_union_set *uset1,
+ __isl_keep isl_union_set *uset2)
+{
+ return isl_union_map_is_strict_subset(uset1, uset2);
+}
+
+/* Internal data structure for isl_union_map_is_disjoint.
+ * umap2 is the union map with which we are comparing.
+ * is_disjoint is initialized to 1 and is set to 0 as soon
+ * as the union maps turn out not to be disjoint.
+ */
+struct isl_union_map_is_disjoint_data {
+ isl_union_map *umap2;
+ isl_bool is_disjoint;
+};
+
+/* Check if "map" is disjoint from data->umap2 and abort
+ * the search if it is not.
+ */
+static isl_stat is_disjoint_entry(void **entry, void *user)
+{
+ struct isl_union_map_is_disjoint_data *data = user;
+ struct isl_hash_table_entry *entry2;
+ isl_space *space;
+ isl_map *map = *entry;
+
+ space = isl_map_peek_space(map);
+ entry2 = isl_union_map_find_entry(data->umap2, space, 0);
+ if (!entry2)
+ return isl_stat_error;
+ if (entry2 == isl_hash_table_entry_none)
+ return isl_stat_ok;
+
+ data->is_disjoint = isl_map_is_disjoint(map, entry2->data);
+ if (data->is_disjoint < 0 || !data->is_disjoint)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Are "umap1" and "umap2" disjoint?
+ */
+isl_bool isl_union_map_is_disjoint(__isl_keep isl_union_map *umap1,
+ __isl_keep isl_union_map *umap2)
+{
+ struct isl_union_map_is_disjoint_data data = { NULL, isl_bool_true };
+
+ umap1 = isl_union_map_copy(umap1);
+ umap2 = isl_union_map_copy(umap2);
+ umap1 = isl_union_map_align_params(umap1,
+ isl_union_map_get_space(umap2));
+ umap2 = isl_union_map_align_params(umap2,
+ isl_union_map_get_space(umap1));
+
+ if (!umap1 || !umap2)
+ goto error;
+
+ data.umap2 = umap2;
+ if (isl_hash_table_foreach(umap1->dim->ctx, &umap1->table,
+ &is_disjoint_entry, &data) < 0 &&
+ data.is_disjoint)
+ goto error;
+
+ isl_union_map_free(umap1);
+ isl_union_map_free(umap2);
+
+ return data.is_disjoint;
+error:
+ isl_union_map_free(umap1);
+ isl_union_map_free(umap2);
+ return isl_bool_error;
+}
+
+/* Are "uset1" and "uset2" disjoint?
+ */
+isl_bool isl_union_set_is_disjoint(__isl_keep isl_union_set *uset1,
+ __isl_keep isl_union_set *uset2)
+{
+ return isl_union_map_is_disjoint(uset1, uset2);
+}
+
+static isl_stat sample_entry(void **entry, void *user)
+{
+ isl_basic_map **sample = (isl_basic_map **)user;
+ isl_map *map = *entry;
+
+ *sample = isl_map_sample(isl_map_copy(map));
+ if (!*sample)
+ return isl_stat_error;
+ if (!isl_basic_map_plain_is_empty(*sample))
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+__isl_give isl_basic_map *isl_union_map_sample(__isl_take isl_union_map *umap)
+{
+ isl_basic_map *sample = NULL;
+
+ if (!umap)
+ return NULL;
+
+ if (isl_hash_table_foreach(umap->dim->ctx, &umap->table,
+ &sample_entry, &sample) < 0 &&
+ !sample)
+ goto error;
+
+ if (!sample)
+ sample = isl_basic_map_empty(isl_union_map_get_space(umap));
+
+ isl_union_map_free(umap);
+
+ return sample;
+error:
+ isl_union_map_free(umap);
+ return NULL;
+}
+
+__isl_give isl_basic_set *isl_union_set_sample(__isl_take isl_union_set *uset)
+{
+ return bset_from_bmap(isl_union_map_sample(uset));
+}
+
+/* Return an element in "uset" in the form of an isl_point.
+ * Return a void isl_point if "uset" is empty.
+ */
+__isl_give isl_point *isl_union_set_sample_point(__isl_take isl_union_set *uset)
+{
+ return isl_basic_set_sample_point(isl_union_set_sample(uset));
+}
+
+struct isl_forall_data {
+ isl_bool res;
+ isl_bool (*fn)(__isl_keep isl_map *map);
+};
+
+static isl_stat forall_entry(void **entry, void *user)
+{
+ struct isl_forall_data *data = user;
+ isl_map *map = *entry;
+
+ data->res = data->fn(map);
+ if (data->res < 0)
+ return isl_stat_error;
+
+ if (!data->res)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+static isl_bool union_map_forall(__isl_keep isl_union_map *umap,
+ isl_bool (*fn)(__isl_keep isl_map *map))
+{
+ struct isl_forall_data data = { isl_bool_true, fn };
+
+ if (!umap)
+ return isl_bool_error;
+
+ if (isl_hash_table_foreach(umap->dim->ctx, &umap->table,
+ &forall_entry, &data) < 0 && data.res)
+ return isl_bool_error;
+
+ return data.res;
+}
+
+struct isl_forall_user_data {
+ isl_bool res;
+ isl_bool (*fn)(__isl_keep isl_map *map, void *user);
+ void *user;
+};
+
+static isl_stat forall_user_entry(void **entry, void *user)
+{
+ struct isl_forall_user_data *data = user;
+ isl_map *map = *entry;
+
+ data->res = data->fn(map, data->user);
+ if (data->res < 0)
+ return isl_stat_error;
+
+ if (!data->res)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Check if fn(map, user) returns true for all maps "map" in umap.
+ */
+static isl_bool union_map_forall_user(__isl_keep isl_union_map *umap,
+ isl_bool (*fn)(__isl_keep isl_map *map, void *user), void *user)
+{
+ struct isl_forall_user_data data = { isl_bool_true, fn, user };
+
+ if (!umap)
+ return isl_bool_error;
+
+ if (isl_hash_table_foreach(umap->dim->ctx, &umap->table,
+ &forall_user_entry, &data) < 0 && data.res)
+ return isl_bool_error;
+
+ return data.res;
+}
+
+/* Is "umap" obviously empty?
+ */
+isl_bool isl_union_map_plain_is_empty(__isl_keep isl_union_map *umap)
+{
+ isl_size n;
+
+ n = isl_union_map_n_map(umap);
+ if (n < 0)
+ return isl_bool_error;
+ return n == 0;
+}
+
+isl_bool isl_union_map_is_empty(__isl_keep isl_union_map *umap)
+{
+ return union_map_forall(umap, &isl_map_is_empty);
+}
+
+isl_bool isl_union_set_is_empty(__isl_keep isl_union_set *uset)
+{
+ return isl_union_map_is_empty(uset);
+}
+
+static isl_bool is_subset_of_identity(__isl_keep isl_map *map)
+{
+ isl_bool is_subset;
+ isl_space *space;
+ isl_map *id;
+ isl_bool match;
+
+ match = isl_map_tuple_is_equal(map, isl_dim_in, map, isl_dim_out);
+ if (match < 0)
+ return isl_bool_error;
+ if (!match)
+ return isl_bool_false;
+
+ space = isl_map_get_space(map);
+ id = isl_map_identity(space);
+
+ is_subset = isl_map_is_subset(map, id);
+
+ isl_map_free(id);
+
+ return is_subset;
+}
+
+/* Given an isl_union_map that consists of a single map, check
+ * if it is single-valued.
+ */
+static isl_bool single_map_is_single_valued(__isl_keep isl_union_map *umap)
+{
+ isl_map *map;
+ isl_bool sv;
+
+ umap = isl_union_map_copy(umap);
+ map = isl_map_from_union_map(umap);
+ sv = isl_map_is_single_valued(map);
+ isl_map_free(map);
+
+ return sv;
+}
+
+/* Internal data structure for single_valued_on_domain.
+ *
+ * "umap" is the union map to be tested.
+ * "sv" is set to 1 as long as "umap" may still be single-valued.
+ */
+struct isl_union_map_is_sv_data {
+ isl_union_map *umap;
+ isl_bool sv;
+};
+
+/* Check if the data->umap is single-valued on "set".
+ *
+ * If data->umap consists of a single map on "set", then test it
+ * as an isl_map.
+ *
+ * Otherwise, compute
+ *
+ * M \circ M^-1
+ *
+ * check if the result is a subset of the identity mapping and
+ * store the result in data->sv.
+ *
+ * Terminate as soon as data->umap has been determined not to
+ * be single-valued.
+ */
+static isl_stat single_valued_on_domain(__isl_take isl_set *set, void *user)
+{
+ struct isl_union_map_is_sv_data *data = user;
+ isl_union_map *umap, *test;
+ isl_size n;
+
+ umap = isl_union_map_copy(data->umap);
+ umap = isl_union_map_intersect_domain(umap,
+ isl_union_set_from_set(set));
+
+ n = isl_union_map_n_map(umap);
+ if (n < 0) {
+ data->sv = isl_bool_error;
+ } else if (n == 1) {
+ data->sv = single_map_is_single_valued(umap);
+ isl_union_map_free(umap);
+ } else {
+ test = isl_union_map_reverse(isl_union_map_copy(umap));
+ test = isl_union_map_apply_range(test, umap);
+
+ data->sv = union_map_forall(test, &is_subset_of_identity);
+
+ isl_union_map_free(test);
+ }
+
+ if (data->sv < 0 || !data->sv)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Check if the given map is single-valued.
+ *
+ * If the union map consists of a single map, then test it as an isl_map.
+ * Otherwise, check if the union map is single-valued on each of its
+ * domain spaces.
+ */
+isl_bool isl_union_map_is_single_valued(__isl_keep isl_union_map *umap)
+{
+ isl_union_map *universe;
+ isl_union_set *domain;
+ struct isl_union_map_is_sv_data data;
+ isl_size n;
+
+ n = isl_union_map_n_map(umap);
+ if (n < 0)
+ return isl_bool_error;
+ if (n == 1)
+ return single_map_is_single_valued(umap);
+
+ universe = isl_union_map_universe(isl_union_map_copy(umap));
+ domain = isl_union_map_domain(universe);
+
+ data.sv = isl_bool_true;
+ data.umap = umap;
+ if (isl_union_set_foreach_set(domain,
+ &single_valued_on_domain, &data) < 0 && data.sv)
+ data.sv = isl_bool_error;
+ isl_union_set_free(domain);
+
+ return data.sv;
+}
+
+isl_bool isl_union_map_is_injective(__isl_keep isl_union_map *umap)
+{
+ isl_bool in;
+
+ umap = isl_union_map_copy(umap);
+ umap = isl_union_map_reverse(umap);
+ in = isl_union_map_is_single_valued(umap);
+ isl_union_map_free(umap);
+
+ return in;
+}
+
+/* Is "map" obviously not an identity relation because
+ * it maps elements from one space to another space?
+ * Update *non_identity accordingly.
+ *
+ * In particular, if the domain and range spaces are the same,
+ * then the map is not considered to obviously not be an identity relation.
+ * Otherwise, the map is considered to obviously not be an identity relation
+ * if it is is non-empty.
+ *
+ * If "map" is determined to obviously not be an identity relation,
+ * then the search is aborted.
+ */
+static isl_stat map_plain_is_not_identity(__isl_take isl_map *map, void *user)
+{
+ isl_bool *non_identity = user;
+ isl_bool equal;
+
+ equal = isl_map_tuple_is_equal(map, isl_dim_in, map, isl_dim_out);
+ if (equal >= 0 && !equal)
+ *non_identity = isl_bool_not(isl_map_is_empty(map));
+ else
+ *non_identity = isl_bool_not(equal);
+ isl_map_free(map);
+
+ if (*non_identity < 0 || *non_identity)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Is "umap" obviously not an identity relation because
+ * it maps elements from one space to another space?
+ *
+ * As soon as a map has been found that maps elements to a different space,
+ * non_identity is changed and the search is aborted.
+ */
+static isl_bool isl_union_map_plain_is_not_identity(
+ __isl_keep isl_union_map *umap)
+{
+ isl_bool non_identity;
+
+ non_identity = isl_bool_false;
+ if (isl_union_map_foreach_map(umap, &map_plain_is_not_identity,
+ &non_identity) < 0 &&
+ non_identity == isl_bool_false)
+ return isl_bool_error;
+
+ return non_identity;
+}
+
+/* Does "map" only map elements to themselves?
+ * Update *identity accordingly.
+ *
+ * If "map" is determined not to be an identity relation,
+ * then the search is aborted.
+ */
+static isl_stat map_is_identity(__isl_take isl_map *map, void *user)
+{
+ isl_bool *identity = user;
+
+ *identity = isl_map_is_identity(map);
+ isl_map_free(map);
+
+ if (*identity < 0 || !*identity)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Does "umap" only map elements to themselves?
+ *
+ * First check if there are any maps that map elements to different spaces.
+ * If not, then check that all the maps (between identical spaces)
+ * are identity relations.
+ */
+isl_bool isl_union_map_is_identity(__isl_keep isl_union_map *umap)
+{
+ isl_bool non_identity;
+ isl_bool identity;
+
+ non_identity = isl_union_map_plain_is_not_identity(umap);
+ if (non_identity < 0 || non_identity)
+ return isl_bool_not(non_identity);
+
+ identity = isl_bool_true;
+ if (isl_union_map_foreach_map(umap, &map_is_identity, &identity) < 0 &&
+ identity == isl_bool_true)
+ return isl_bool_error;
+
+ return identity;
+}
+
+/* Represents a map that has a fixed value (v) for one of its
+ * range dimensions.
+ * The map in this structure is not reference counted, so it
+ * is only valid while the isl_union_map from which it was
+ * obtained is still alive.
+ */
+struct isl_fixed_map {
+ isl_int v;
+ isl_map *map;
+};
+
+static struct isl_fixed_map *alloc_isl_fixed_map_array(isl_ctx *ctx,
+ int n)
+{
+ int i;
+ struct isl_fixed_map *v;
+
+ v = isl_calloc_array(ctx, struct isl_fixed_map, n);
+ if (!v)
+ return NULL;
+ for (i = 0; i < n; ++i)
+ isl_int_init(v[i].v);
+ return v;
+}
+
+static void free_isl_fixed_map_array(struct isl_fixed_map *v, int n)
+{
+ int i;
+
+ if (!v)
+ return;
+ for (i = 0; i < n; ++i)
+ isl_int_clear(v[i].v);
+ free(v);
+}
+
+/* Compare the "v" field of two isl_fixed_map structs.
+ */
+static int qsort_fixed_map_cmp(const void *p1, const void *p2)
+{
+ const struct isl_fixed_map *e1 = (const struct isl_fixed_map *) p1;
+ const struct isl_fixed_map *e2 = (const struct isl_fixed_map *) p2;
+
+ return isl_int_cmp(e1->v, e2->v);
+}
+
+/* Internal data structure used while checking whether all maps
+ * in a union_map have a fixed value for a given output dimension.
+ * v is the list of maps, with the fixed value for the dimension
+ * n is the number of maps considered so far
+ * pos is the output dimension under investigation
+ */
+struct isl_fixed_dim_data {
+ struct isl_fixed_map *v;
+ int n;
+ int pos;
+};
+
+static isl_bool fixed_at_pos(__isl_keep isl_map *map, void *user)
+{
+ struct isl_fixed_dim_data *data = user;
+
+ data->v[data->n].map = map;
+ return isl_map_plain_is_fixed(map, isl_dim_out, data->pos,
+ &data->v[data->n++].v);
+}
+
+static isl_bool plain_injective_on_range(__isl_take isl_union_map *umap,
+ int first, int n_range);
+
+/* Given a list of the maps, with their fixed values at output dimension "pos",
+ * check whether the ranges of the maps form an obvious partition.
+ *
+ * We first sort the maps according to their fixed values.
+ * If all maps have a different value, then we know the ranges form
+ * a partition.
+ * Otherwise, we collect the maps with the same fixed value and
+ * check whether each such collection is obviously injective
+ * based on later dimensions.
+ */
+static int separates(struct isl_fixed_map *v, int n,
+ __isl_take isl_space *space, int pos, int n_range)
+{
+ int i;
+
+ if (!v)
+ goto error;
+
+ qsort(v, n, sizeof(*v), &qsort_fixed_map_cmp);
+
+ for (i = 0; i + 1 < n; ++i) {
+ int j, k;
+ isl_union_map *part;
+ int injective;
+
+ for (j = i + 1; j < n; ++j)
+ if (isl_int_ne(v[i].v, v[j].v))
+ break;
+
+ if (j == i + 1)
+ continue;
+
+ part = isl_union_map_alloc(isl_space_copy(space), j - i);
+ for (k = i; k < j; ++k)
+ part = isl_union_map_add_map(part,
+ isl_map_copy(v[k].map));
+
+ injective = plain_injective_on_range(part, pos + 1, n_range);
+ if (injective < 0)
+ goto error;
+ if (!injective)
+ break;
+
+ i = j - 1;
+ }
+
+ isl_space_free(space);
+ free_isl_fixed_map_array(v, n);
+ return i + 1 >= n;
+error:
+ isl_space_free(space);
+ free_isl_fixed_map_array(v, n);
+ return -1;
+}
+
+/* Check whether the maps in umap have obviously distinct ranges.
+ * In particular, check for an output dimension in the range
+ * [first,n_range) for which all maps have a fixed value
+ * and then check if these values, possibly along with fixed values
+ * at later dimensions, entail distinct ranges.
+ */
+static isl_bool plain_injective_on_range(__isl_take isl_union_map *umap,
+ int first, int n_range)
+{
+ isl_ctx *ctx;
+ isl_size n;
+ struct isl_fixed_dim_data data = { NULL };
+
+ ctx = isl_union_map_get_ctx(umap);
+
+ n = isl_union_map_n_map(umap);
+ if (n < 0)
+ goto error;
+
+ if (n <= 1) {
+ isl_union_map_free(umap);
+ return isl_bool_true;
+ }
+
+ if (first >= n_range) {
+ isl_union_map_free(umap);
+ return isl_bool_false;
+ }
+
+ data.v = alloc_isl_fixed_map_array(ctx, n);
+ if (!data.v)
+ goto error;
+
+ for (data.pos = first; data.pos < n_range; ++data.pos) {
+ isl_bool fixed;
+ int injective;
+ isl_space *space;
+
+ data.n = 0;
+ fixed = union_map_forall_user(umap, &fixed_at_pos, &data);
+ if (fixed < 0)
+ goto error;
+ if (!fixed)
+ continue;
+ space = isl_union_map_get_space(umap);
+ injective = separates(data.v, n, space, data.pos, n_range);
+ isl_union_map_free(umap);
+ return injective;
+ }
+
+ free_isl_fixed_map_array(data.v, n);
+ isl_union_map_free(umap);
+
+ return isl_bool_false;
+error:
+ free_isl_fixed_map_array(data.v, n);
+ isl_union_map_free(umap);
+ return isl_bool_error;
+}
+
+/* Check whether the maps in umap that map to subsets of "ran"
+ * have obviously distinct ranges.
+ */
+static isl_bool plain_injective_on_range_wrap(__isl_keep isl_set *ran,
+ void *user)
+{
+ isl_size dim;
+ isl_union_map *umap = user;
+
+ dim = isl_set_dim(ran, isl_dim_set);
+ if (dim < 0)
+ return isl_bool_error;
+
+ umap = isl_union_map_copy(umap);
+ umap = isl_union_map_intersect_range(umap,
+ isl_union_set_from_set(isl_set_copy(ran)));
+ return plain_injective_on_range(umap, 0, dim);
+}
+
+/* Check if the given union_map is obviously injective.
+ *
+ * In particular, we first check if all individual maps are obviously
+ * injective and then check if all the ranges of these maps are
+ * obviously disjoint.
+ */
+isl_bool isl_union_map_plain_is_injective(__isl_keep isl_union_map *umap)
+{
+ isl_bool in;
+ isl_union_map *univ;
+ isl_union_set *ran;
+
+ in = union_map_forall(umap, &isl_map_plain_is_injective);
+ if (in < 0)
+ return isl_bool_error;
+ if (!in)
+ return isl_bool_false;
+
+ univ = isl_union_map_universe(isl_union_map_copy(umap));
+ ran = isl_union_map_range(univ);
+
+ in = union_map_forall_user(ran, &plain_injective_on_range_wrap, umap);
+
+ isl_union_set_free(ran);
+
+ return in;
+}
+
+isl_bool isl_union_map_is_bijective(__isl_keep isl_union_map *umap)
+{
+ isl_bool sv;
+
+ sv = isl_union_map_is_single_valued(umap);
+ if (sv < 0 || !sv)
+ return sv;
+
+ return isl_union_map_is_injective(umap);
+}
+
+__isl_give isl_union_map *isl_union_map_zip(__isl_take isl_union_map *umap)
+{
+ struct isl_un_op_drop_user_data data = { &isl_map_can_zip };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_map_zip,
+ };
+ return un_op(umap, &control);
+}
+
+/* Given a union map, take the maps of the form A -> (B -> C) and
+ * return the union of the corresponding maps (A -> B) -> C.
+ */
+__isl_give isl_union_map *isl_union_map_uncurry(__isl_take isl_union_map *umap)
+{
+ struct isl_un_op_drop_user_data data = { &isl_map_can_uncurry };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_map_uncurry,
+ };
+ return un_op(umap, &control);
+}
+
+/* Given a union map, take the maps of the form (A -> B) -> C and
+ * return the union of the corresponding maps A -> (B -> C).
+ */
+__isl_give isl_union_map *isl_union_map_curry(__isl_take isl_union_map *umap)
+{
+ struct isl_un_op_drop_user_data data = { &isl_map_can_curry };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_map_curry,
+ };
+ return un_op(umap, &control);
+}
+
+/* Given a union map, take the maps of the form A -> ((B -> C) -> D) and
+ * return the union of the corresponding maps A -> (B -> (C -> D)).
+ */
+__isl_give isl_union_map *isl_union_map_range_curry(
+ __isl_take isl_union_map *umap)
+{
+ struct isl_un_op_drop_user_data data = { &isl_map_can_range_curry };
+ struct isl_un_op_control control = {
+ .filter = &un_op_filter_drop_user,
+ .filter_user = &data,
+ .fn_map = &isl_map_range_curry,
+ };
+ return un_op(umap, &control);
+}
+
+__isl_give isl_union_set *isl_union_set_lift(__isl_take isl_union_set *uset)
+{
+ struct isl_un_op_control control = {
+ .fn_map = &isl_set_lift,
+ };
+ return un_op(uset, &control);
+}
+
+static isl_stat coefficients_entry(void **entry, void *user)
+{
+ isl_set *set = *entry;
+ isl_union_set **res = user;
+
+ set = isl_set_copy(set);
+ set = isl_set_from_basic_set(isl_set_coefficients(set));
+ *res = isl_union_set_add_set(*res, set);
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_set *isl_union_set_coefficients(
+ __isl_take isl_union_set *uset)
+{
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_union_set *res;
+
+ if (!uset)
+ return NULL;
+
+ ctx = isl_union_set_get_ctx(uset);
+ space = isl_space_set_alloc(ctx, 0, 0);
+ res = isl_union_map_alloc(space, uset->table.n);
+ if (isl_hash_table_foreach(uset->dim->ctx, &uset->table,
+ &coefficients_entry, &res) < 0)
+ goto error;
+
+ isl_union_set_free(uset);
+ return res;
+error:
+ isl_union_set_free(uset);
+ isl_union_set_free(res);
+ return NULL;
+}
+
+static isl_stat solutions_entry(void **entry, void *user)
+{
+ isl_set *set = *entry;
+ isl_union_set **res = user;
+
+ set = isl_set_copy(set);
+ set = isl_set_from_basic_set(isl_set_solutions(set));
+ if (!*res)
+ *res = isl_union_set_from_set(set);
+ else
+ *res = isl_union_set_add_set(*res, set);
+
+ if (!*res)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_set *isl_union_set_solutions(
+ __isl_take isl_union_set *uset)
+{
+ isl_union_set *res = NULL;
+
+ if (!uset)
+ return NULL;
+
+ if (uset->table.n == 0) {
+ res = isl_union_set_empty(isl_union_set_get_space(uset));
+ isl_union_set_free(uset);
+ return res;
+ }
+
+ if (isl_hash_table_foreach(uset->dim->ctx, &uset->table,
+ &solutions_entry, &res) < 0)
+ goto error;
+
+ isl_union_set_free(uset);
+ return res;
+error:
+ isl_union_set_free(uset);
+ isl_union_set_free(res);
+ return NULL;
+}
+
+/* Is the domain space of "map" equal to "space"?
+ */
+static int domain_match(__isl_keep isl_map *map, __isl_keep isl_space *space)
+{
+ return isl_map_space_tuple_is_equal(map, isl_dim_in,
+ space, isl_dim_out);
+}
+
+/* Is the range space of "map" equal to "space"?
+ */
+static int range_match(__isl_keep isl_map *map, __isl_keep isl_space *space)
+{
+ return isl_map_space_tuple_is_equal(map, isl_dim_out,
+ space, isl_dim_out);
+}
+
+/* Is the set space of "map" equal to "space"?
+ */
+static int set_match(__isl_keep isl_map *map, __isl_keep isl_space *space)
+{
+ return isl_map_space_tuple_is_equal(map, isl_dim_set,
+ space, isl_dim_out);
+}
+
+/* Internal data structure for preimage_pw_multi_aff.
+ *
+ * "pma" is the function under which the preimage should be taken.
+ * "space" is the space of "pma".
+ * "res" collects the results.
+ * "fn" computes the preimage for a given map.
+ * "match" returns true if "fn" can be called.
+ */
+struct isl_union_map_preimage_data {
+ isl_space *space;
+ isl_pw_multi_aff *pma;
+ isl_union_map *res;
+ int (*match)(__isl_keep isl_map *map, __isl_keep isl_space *space);
+ __isl_give isl_map *(*fn)(__isl_take isl_map *map,
+ __isl_take isl_pw_multi_aff *pma);
+};
+
+/* Call data->fn to compute the preimage of the domain or range of *entry
+ * under the function represented by data->pma, provided the domain/range
+ * space of *entry matches the target space of data->pma
+ * (as given by data->match), and add the result to data->res.
+ */
+static isl_stat preimage_entry(void **entry, void *user)
+{
+ int m;
+ isl_map *map = *entry;
+ struct isl_union_map_preimage_data *data = user;
+ isl_bool empty;
+
+ m = data->match(map, data->space);
+ if (m < 0)
+ return isl_stat_error;
+ if (!m)
+ return isl_stat_ok;
+
+ map = isl_map_copy(map);
+ map = data->fn(map, isl_pw_multi_aff_copy(data->pma));
+
+ empty = isl_map_is_empty(map);
+ if (empty < 0 || empty) {
+ isl_map_free(map);
+ return empty < 0 ? isl_stat_error : isl_stat_ok;
+ }
+
+ data->res = isl_union_map_add_map(data->res, map);
+
+ return isl_stat_ok;
+}
+
+/* Compute the preimage of the domain or range of "umap" under the function
+ * represented by "pma".
+ * In other words, plug in "pma" in the domain or range of "umap".
+ * The function "fn" performs the actual preimage computation on a map,
+ * while "match" determines to which maps the function should be applied.
+ */
+static __isl_give isl_union_map *preimage_pw_multi_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_pw_multi_aff *pma,
+ int (*match)(__isl_keep isl_map *map, __isl_keep isl_space *space),
+ __isl_give isl_map *(*fn)(__isl_take isl_map *map,
+ __isl_take isl_pw_multi_aff *pma))
+{
+ isl_ctx *ctx;
+ isl_space *space;
+ struct isl_union_map_preimage_data data;
+
+ umap = isl_union_map_align_params(umap,
+ isl_pw_multi_aff_get_space(pma));
+ pma = isl_pw_multi_aff_align_params(pma, isl_union_map_get_space(umap));
+
+ if (!umap || !pma)
+ goto error;
+
+ ctx = isl_union_map_get_ctx(umap);
+ space = isl_union_map_get_space(umap);
+ data.space = isl_pw_multi_aff_get_space(pma);
+ data.pma = pma;
+ data.res = isl_union_map_alloc(space, umap->table.n);
+ data.match = match;
+ data.fn = fn;
+ if (isl_hash_table_foreach(ctx, &umap->table, &preimage_entry,
+ &data) < 0)
+ data.res = isl_union_map_free(data.res);
+
+ isl_space_free(data.space);
+ isl_union_map_free(umap);
+ isl_pw_multi_aff_free(pma);
+ return data.res;
+error:
+ isl_union_map_free(umap);
+ isl_pw_multi_aff_free(pma);
+ return NULL;
+}
+
+/* Compute the preimage of the domain of "umap" under the function
+ * represented by "pma".
+ * In other words, plug in "pma" in the domain of "umap".
+ * The result contains maps that live in the same spaces as the maps of "umap"
+ * with domain space equal to the target space of "pma",
+ * except that the domain has been replaced by the domain space of "pma".
+ */
+__isl_give isl_union_map *isl_union_map_preimage_domain_pw_multi_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_pw_multi_aff *pma)
+{
+ return preimage_pw_multi_aff(umap, pma, &domain_match,
+ &isl_map_preimage_domain_pw_multi_aff);
+}
+
+/* Compute the preimage of the range of "umap" under the function
+ * represented by "pma".
+ * In other words, plug in "pma" in the range of "umap".
+ * The result contains maps that live in the same spaces as the maps of "umap"
+ * with range space equal to the target space of "pma",
+ * except that the range has been replaced by the domain space of "pma".
+ */
+__isl_give isl_union_map *isl_union_map_preimage_range_pw_multi_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_pw_multi_aff *pma)
+{
+ return preimage_pw_multi_aff(umap, pma, &range_match,
+ &isl_map_preimage_range_pw_multi_aff);
+}
+
+/* Compute the preimage of "uset" under the function represented by "pma".
+ * In other words, plug in "pma" in "uset".
+ * The result contains sets that live in the same spaces as the sets of "uset"
+ * with space equal to the target space of "pma",
+ * except that the space has been replaced by the domain space of "pma".
+ */
+__isl_give isl_union_set *isl_union_set_preimage_pw_multi_aff(
+ __isl_take isl_union_set *uset, __isl_take isl_pw_multi_aff *pma)
+{
+ return preimage_pw_multi_aff(uset, pma, &set_match,
+ &isl_set_preimage_pw_multi_aff);
+}
+
+/* Compute the preimage of the domain of "umap" under the function
+ * represented by "ma".
+ * In other words, plug in "ma" in the domain of "umap".
+ * The result contains maps that live in the same spaces as the maps of "umap"
+ * with domain space equal to the target space of "ma",
+ * except that the domain has been replaced by the domain space of "ma".
+ */
+__isl_give isl_union_map *isl_union_map_preimage_domain_multi_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_multi_aff *ma)
+{
+ return isl_union_map_preimage_domain_pw_multi_aff(umap,
+ isl_pw_multi_aff_from_multi_aff(ma));
+}
+
+/* Compute the preimage of the range of "umap" under the function
+ * represented by "ma".
+ * In other words, plug in "ma" in the range of "umap".
+ * The result contains maps that live in the same spaces as the maps of "umap"
+ * with range space equal to the target space of "ma",
+ * except that the range has been replaced by the domain space of "ma".
+ */
+__isl_give isl_union_map *isl_union_map_preimage_range_multi_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_multi_aff *ma)
+{
+ return isl_union_map_preimage_range_pw_multi_aff(umap,
+ isl_pw_multi_aff_from_multi_aff(ma));
+}
+
+/* Compute the preimage of "uset" under the function represented by "ma".
+ * In other words, plug in "ma" in "uset".
+ * The result contains sets that live in the same spaces as the sets of "uset"
+ * with space equal to the target space of "ma",
+ * except that the space has been replaced by the domain space of "ma".
+ */
+__isl_give isl_union_map *isl_union_set_preimage_multi_aff(
+ __isl_take isl_union_set *uset, __isl_take isl_multi_aff *ma)
+{
+ return isl_union_set_preimage_pw_multi_aff(uset,
+ isl_pw_multi_aff_from_multi_aff(ma));
+}
+
+/* Internal data structure for preimage_multi_pw_aff.
+ *
+ * "mpa" is the function under which the preimage should be taken.
+ * "space" is the space of "mpa".
+ * "res" collects the results.
+ * "fn" computes the preimage for a given map.
+ * "match" returns true if "fn" can be called.
+ */
+struct isl_union_map_preimage_mpa_data {
+ isl_space *space;
+ isl_multi_pw_aff *mpa;
+ isl_union_map *res;
+ int (*match)(__isl_keep isl_map *map, __isl_keep isl_space *space);
+ __isl_give isl_map *(*fn)(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *mpa);
+};
+
+/* Call data->fn to compute the preimage of the domain or range of *entry
+ * under the function represented by data->mpa, provided the domain/range
+ * space of *entry matches the target space of data->mpa
+ * (as given by data->match), and add the result to data->res.
+ */
+static isl_stat preimage_mpa_entry(void **entry, void *user)
+{
+ int m;
+ isl_map *map = *entry;
+ struct isl_union_map_preimage_mpa_data *data = user;
+ isl_bool empty;
+
+ m = data->match(map, data->space);
+ if (m < 0)
+ return isl_stat_error;
+ if (!m)
+ return isl_stat_ok;
+
+ map = isl_map_copy(map);
+ map = data->fn(map, isl_multi_pw_aff_copy(data->mpa));
+
+ empty = isl_map_is_empty(map);
+ if (empty < 0 || empty) {
+ isl_map_free(map);
+ return empty < 0 ? isl_stat_error : isl_stat_ok;
+ }
+
+ data->res = isl_union_map_add_map(data->res, map);
+
+ return isl_stat_ok;
+}
+
+/* Compute the preimage of the domain or range of "umap" under the function
+ * represented by "mpa".
+ * In other words, plug in "mpa" in the domain or range of "umap".
+ * The function "fn" performs the actual preimage computation on a map,
+ * while "match" determines to which maps the function should be applied.
+ */
+static __isl_give isl_union_map *preimage_multi_pw_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_multi_pw_aff *mpa,
+ int (*match)(__isl_keep isl_map *map, __isl_keep isl_space *space),
+ __isl_give isl_map *(*fn)(__isl_take isl_map *map,
+ __isl_take isl_multi_pw_aff *mpa))
+{
+ isl_ctx *ctx;
+ isl_space *space;
+ struct isl_union_map_preimage_mpa_data data;
+
+ umap = isl_union_map_align_params(umap,
+ isl_multi_pw_aff_get_space(mpa));
+ mpa = isl_multi_pw_aff_align_params(mpa, isl_union_map_get_space(umap));
+
+ if (!umap || !mpa)
+ goto error;
+
+ ctx = isl_union_map_get_ctx(umap);
+ space = isl_union_map_get_space(umap);
+ data.space = isl_multi_pw_aff_get_space(mpa);
+ data.mpa = mpa;
+ data.res = isl_union_map_alloc(space, umap->table.n);
+ data.match = match;
+ data.fn = fn;
+ if (isl_hash_table_foreach(ctx, &umap->table, &preimage_mpa_entry,
+ &data) < 0)
+ data.res = isl_union_map_free(data.res);
+
+ isl_space_free(data.space);
+ isl_union_map_free(umap);
+ isl_multi_pw_aff_free(mpa);
+ return data.res;
+error:
+ isl_union_map_free(umap);
+ isl_multi_pw_aff_free(mpa);
+ return NULL;
+}
+
+/* Compute the preimage of the domain of "umap" under the function
+ * represented by "mpa".
+ * In other words, plug in "mpa" in the domain of "umap".
+ * The result contains maps that live in the same spaces as the maps of "umap"
+ * with domain space equal to the target space of "mpa",
+ * except that the domain has been replaced by the domain space of "mpa".
+ */
+__isl_give isl_union_map *isl_union_map_preimage_domain_multi_pw_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_multi_pw_aff *mpa)
+{
+ return preimage_multi_pw_aff(umap, mpa, &domain_match,
+ &isl_map_preimage_domain_multi_pw_aff);
+}
+
+/* Internal data structure for preimage_upma.
+ *
+ * "umap" is the map of which the preimage should be computed.
+ * "res" collects the results.
+ * "fn" computes the preimage for a given piecewise multi-affine function.
+ */
+struct isl_union_map_preimage_upma_data {
+ isl_union_map *umap;
+ isl_union_map *res;
+ __isl_give isl_union_map *(*fn)(__isl_take isl_union_map *umap,
+ __isl_take isl_pw_multi_aff *pma);
+};
+
+/* Call data->fn to compute the preimage of the domain or range of data->umap
+ * under the function represented by pma and add the result to data->res.
+ */
+static isl_stat preimage_upma(__isl_take isl_pw_multi_aff *pma, void *user)
+{
+ struct isl_union_map_preimage_upma_data *data = user;
+ isl_union_map *umap;
+
+ umap = isl_union_map_copy(data->umap);
+ umap = data->fn(umap, pma);
+ data->res = isl_union_map_union(data->res, umap);
+
+ return data->res ? isl_stat_ok : isl_stat_error;
+}
+
+/* Compute the preimage of the domain or range of "umap" under the function
+ * represented by "upma".
+ * In other words, plug in "upma" in the domain or range of "umap".
+ * The function "fn" performs the actual preimage computation
+ * on a piecewise multi-affine function.
+ */
+static __isl_give isl_union_map *preimage_union_pw_multi_aff(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_union_pw_multi_aff *upma,
+ __isl_give isl_union_map *(*fn)(__isl_take isl_union_map *umap,
+ __isl_take isl_pw_multi_aff *pma))
+{
+ struct isl_union_map_preimage_upma_data data;
+
+ data.umap = umap;
+ data.res = isl_union_map_empty(isl_union_map_get_space(umap));
+ data.fn = fn;
+ if (isl_union_pw_multi_aff_foreach_pw_multi_aff(upma,
+ &preimage_upma, &data) < 0)
+ data.res = isl_union_map_free(data.res);
+
+ isl_union_map_free(umap);
+ isl_union_pw_multi_aff_free(upma);
+
+ return data.res;
+}
+
+/* Compute the preimage of the domain of "umap" under the function
+ * represented by "upma".
+ * In other words, plug in "upma" in the domain of "umap".
+ * The result contains maps that live in the same spaces as the maps of "umap"
+ * with domain space equal to one of the target spaces of "upma",
+ * except that the domain has been replaced by one of the domain spaces that
+ * correspond to that target space of "upma".
+ */
+__isl_give isl_union_map *isl_union_map_preimage_domain_union_pw_multi_aff(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ return preimage_union_pw_multi_aff(umap, upma,
+ &isl_union_map_preimage_domain_pw_multi_aff);
+}
+
+/* Compute the preimage of the range of "umap" under the function
+ * represented by "upma".
+ * In other words, plug in "upma" in the range of "umap".
+ * The result contains maps that live in the same spaces as the maps of "umap"
+ * with range space equal to one of the target spaces of "upma",
+ * except that the range has been replaced by one of the domain spaces that
+ * correspond to that target space of "upma".
+ */
+__isl_give isl_union_map *isl_union_map_preimage_range_union_pw_multi_aff(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ return preimage_union_pw_multi_aff(umap, upma,
+ &isl_union_map_preimage_range_pw_multi_aff);
+}
+
+/* Compute the preimage of "uset" under the function represented by "upma".
+ * In other words, plug in "upma" in the range of "uset".
+ * The result contains sets that live in the same spaces as the sets of "uset"
+ * with space equal to one of the target spaces of "upma",
+ * except that the space has been replaced by one of the domain spaces that
+ * correspond to that target space of "upma".
+ */
+__isl_give isl_union_set *isl_union_set_preimage_union_pw_multi_aff(
+ __isl_take isl_union_set *uset,
+ __isl_take isl_union_pw_multi_aff *upma)
+{
+ return preimage_union_pw_multi_aff(uset, upma,
+ &isl_union_set_preimage_pw_multi_aff);
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * of the spaces of "umap".
+ */
+__isl_give isl_union_map *isl_union_map_reset_user(
+ __isl_take isl_union_map *umap)
+{
+ umap = isl_union_map_cow(umap);
+ if (!umap)
+ return NULL;
+ umap->dim = isl_space_reset_user(umap->dim);
+ if (!umap->dim)
+ return isl_union_map_free(umap);
+ return total(umap, &isl_map_reset_user);
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * of the spaces of "uset".
+ */
+__isl_give isl_union_set *isl_union_set_reset_user(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_reset_user(uset);
+}
+
+/* Remove all existentially quantified variables and integer divisions
+ * from "umap" using Fourier-Motzkin elimination.
+ */
+__isl_give isl_union_map *isl_union_map_remove_divs(
+ __isl_take isl_union_map *umap)
+{
+ return total(umap, &isl_map_remove_divs);
+}
+
+/* Remove all existentially quantified variables and integer divisions
+ * from "uset" using Fourier-Motzkin elimination.
+ */
+__isl_give isl_union_set *isl_union_set_remove_divs(
+ __isl_take isl_union_set *uset)
+{
+ return isl_union_map_remove_divs(uset);
+}
+
+/* Internal data structure for isl_union_map_project_out.
+ * "type", "first" and "n" are the arguments for the isl_map_project_out
+ * call.
+ * "res" collects the results.
+ */
+struct isl_union_map_project_out_data {
+ enum isl_dim_type type;
+ unsigned first;
+ unsigned n;
+
+ isl_union_map *res;
+};
+
+/* Turn the data->n dimensions of type data->type, starting at data->first
+ * into existentially quantified variables and add the result to data->res.
+ */
+static isl_stat project_out(__isl_take isl_map *map, void *user)
+{
+ struct isl_union_map_project_out_data *data = user;
+
+ map = isl_map_project_out(map, data->type, data->first, data->n);
+ data->res = isl_union_map_add_map(data->res, map);
+
+ return isl_stat_ok;
+}
+
+/* Turn the "n" dimensions of type "type", starting at "first"
+ * into existentially quantified variables.
+ * Since the space of an isl_union_map only contains parameters,
+ * type is required to be equal to isl_dim_param.
+ */
+__isl_give isl_union_map *isl_union_map_project_out(
+ __isl_take isl_union_map *umap,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ isl_space *space;
+ struct isl_union_map_project_out_data data = { type, first, n };
+
+ if (!umap)
+ return NULL;
+
+ if (type != isl_dim_param)
+ isl_die(isl_union_map_get_ctx(umap), isl_error_invalid,
+ "can only project out parameters",
+ return isl_union_map_free(umap));
+
+ space = isl_union_map_get_space(umap);
+ space = isl_space_drop_dims(space, type, first, n);
+ data.res = isl_union_map_empty(space);
+ if (isl_union_map_foreach_map(umap, &project_out, &data) < 0)
+ data.res = isl_union_map_free(data.res);
+
+ isl_union_map_free(umap);
+
+ return data.res;
+}
+
+#undef TYPE
+#define TYPE isl_union_map
+#include "isl_project_out_all_params_templ.c"
+
+/* Turn the "n" dimensions of type "type", starting at "first"
+ * into existentially quantified variables.
+ * Since the space of an isl_union_set only contains parameters,
+ * "type" is required to be equal to isl_dim_param.
+ */
+__isl_give isl_union_set *isl_union_set_project_out(
+ __isl_take isl_union_set *uset,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return isl_union_map_project_out(uset, type, first, n);
+}
+
+/* Project out all parameters from "uset" by existentially quantifying
+ * over them.
+ */
+__isl_give isl_union_set *isl_union_set_project_out_all_params(
+ __isl_take isl_union_set *uset)
+{
+ return uset_from_umap(
+ isl_union_map_project_out_all_params(uset_to_umap(uset)));
+}
+
+/* Internal data structure for isl_union_map_involves_dims.
+ * "first" and "n" are the arguments for the isl_map_involves_dims calls.
+ */
+struct isl_union_map_involves_dims_data {
+ unsigned first;
+ unsigned n;
+};
+
+/* Does "map" _not_ involve the data->n parameters starting at data->first?
+ */
+static isl_bool map_excludes(__isl_keep isl_map *map, void *user)
+{
+ struct isl_union_map_involves_dims_data *data = user;
+ isl_bool involves;
+
+ involves = isl_map_involves_dims(map,
+ isl_dim_param, data->first, data->n);
+ return isl_bool_not(involves);
+}
+
+/* Does "umap" involve any of the n parameters starting at first?
+ * "type" is required to be set to isl_dim_param.
+ *
+ * "umap" involves any of those parameters if any of its maps
+ * involve the parameters. In other words, "umap" does not
+ * involve any of the parameters if all its maps to not
+ * involve the parameters.
+ */
+isl_bool isl_union_map_involves_dims(__isl_keep isl_union_map *umap,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ struct isl_union_map_involves_dims_data data = { first, n };
+ isl_bool excludes;
+
+ if (type != isl_dim_param)
+ isl_die(isl_union_map_get_ctx(umap), isl_error_invalid,
+ "can only reference parameters", return isl_bool_error);
+
+ excludes = union_map_forall_user(umap, &map_excludes, &data);
+
+ return isl_bool_not(excludes);
+}
+
+/* Internal data structure for isl_union_map_reset_range_space.
+ * "range" is the space from which to set the range space.
+ * "res" collects the results.
+ */
+struct isl_union_map_reset_range_space_data {
+ isl_space *range;
+ isl_union_map *res;
+};
+
+/* Replace the range space of "map" by the range space of data->range and
+ * add the result to data->res.
+ */
+static isl_stat reset_range_space(__isl_take isl_map *map, void *user)
+{
+ struct isl_union_map_reset_range_space_data *data = user;
+ isl_space *space;
+
+ space = isl_map_get_space(map);
+ space = isl_space_domain(space);
+ space = isl_space_extend_domain_with_range(space,
+ isl_space_copy(data->range));
+ map = isl_map_reset_space(map, space);
+ data->res = isl_union_map_add_map(data->res, map);
+
+ return data->res ? isl_stat_ok : isl_stat_error;
+}
+
+/* Replace the range space of all the maps in "umap" by
+ * the range space of "space".
+ *
+ * This assumes that all maps have the same output dimension.
+ * This function should therefore not be made publicly available.
+ *
+ * Since the spaces of the maps change, so do their hash value.
+ * We therefore need to create a new isl_union_map.
+ */
+__isl_give isl_union_map *isl_union_map_reset_range_space(
+ __isl_take isl_union_map *umap, __isl_take isl_space *space)
+{
+ struct isl_union_map_reset_range_space_data data = { space };
+
+ data.res = isl_union_map_empty(isl_union_map_get_space(umap));
+ if (isl_union_map_foreach_map(umap, &reset_range_space, &data) < 0)
+ data.res = isl_union_map_free(data.res);
+
+ isl_space_free(space);
+ isl_union_map_free(umap);
+ return data.res;
+}
+
+/* Check that "umap" and "space" have the same number of parameters.
+ */
+static isl_stat check_union_map_space_equal_dim(__isl_keep isl_union_map *umap,
+ __isl_keep isl_space *space)
+{
+ isl_size dim1, dim2;
+
+ dim1 = isl_union_map_dim(umap, isl_dim_param);
+ dim2 = isl_space_dim(space, isl_dim_param);
+ if (dim1 < 0 || dim2 < 0)
+ return isl_stat_error;
+ if (dim1 == dim2)
+ return isl_stat_ok;
+ isl_die(isl_union_map_get_ctx(umap), isl_error_invalid,
+ "number of parameters does not match", return isl_stat_error);
+}
+
+/* Internal data structure for isl_union_map_reset_equal_dim_space.
+ * "space" is the target space.
+ * "res" collects the results.
+ */
+struct isl_union_map_reset_params_data {
+ isl_space *space;
+ isl_union_map *res;
+};
+
+/* Replace the parameters of "map" by those of data->space and
+ * add the result to data->res.
+ */
+static isl_stat reset_params(__isl_take isl_map *map, void *user)
+{
+ struct isl_union_map_reset_params_data *data = user;
+ isl_space *space;
+
+ space = isl_map_get_space(map);
+ space = isl_space_replace_params(space, data->space);
+ map = isl_map_reset_equal_dim_space(map, space);
+ data->res = isl_union_map_add_map(data->res, map);
+
+ return data->res ? isl_stat_ok : isl_stat_error;
+}
+
+/* Replace the space of "umap" by "space", without modifying
+ * the dimension of "umap", i.e., the number of parameters of "umap".
+ *
+ * Since the hash values of the maps in the union map depend
+ * on the parameters, a new union map needs to be constructed.
+ */
+__isl_give isl_union_map *isl_union_map_reset_equal_dim_space(
+ __isl_take isl_union_map *umap, __isl_take isl_space *space)
+{
+ struct isl_union_map_reset_params_data data = { space };
+ isl_bool equal;
+ isl_space *umap_space;
+
+ umap_space = isl_union_map_peek_space(umap);
+ equal = isl_space_is_equal(umap_space, space);
+ if (equal < 0)
+ goto error;
+ if (equal) {
+ isl_space_free(space);
+ return umap;
+ }
+ if (check_union_map_space_equal_dim(umap, space) < 0)
+ goto error;
+
+ data.res = isl_union_map_empty(isl_space_copy(space));
+ if (isl_union_map_foreach_map(umap, &reset_params, &data) < 0)
+ data.res = isl_union_map_free(data.res);
+
+ isl_space_free(space);
+ isl_union_map_free(umap);
+ return data.res;
+error:
+ isl_union_map_free(umap);
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Internal data structure for isl_union_map_order_at_multi_union_pw_aff.
+ * "mupa" is the function from which the isl_multi_pw_affs are extracted.
+ * "order" is applied to the extracted isl_multi_pw_affs that correspond
+ * to the domain and the range of each map.
+ * "res" collects the results.
+ */
+struct isl_union_order_at_data {
+ isl_multi_union_pw_aff *mupa;
+ __isl_give isl_map *(*order)(__isl_take isl_multi_pw_aff *mpa1,
+ __isl_take isl_multi_pw_aff *mpa2);
+ isl_union_map *res;
+};
+
+/* Intersect "map" with the result of applying data->order to
+ * the functions in data->mupa that apply to the domain and the range
+ * of "map" and add the result to data->res.
+ */
+static isl_stat order_at(__isl_take isl_map *map, void *user)
+{
+ struct isl_union_order_at_data *data = user;
+ isl_space *space;
+ isl_multi_pw_aff *mpa1, *mpa2;
+ isl_map *order;
+
+ space = isl_space_domain(isl_map_get_space(map));
+ mpa1 = isl_multi_union_pw_aff_extract_multi_pw_aff(data->mupa, space);
+ space = isl_space_range(isl_map_get_space(map));
+ mpa2 = isl_multi_union_pw_aff_extract_multi_pw_aff(data->mupa, space);
+ order = data->order(mpa1, mpa2);
+ map = isl_map_intersect(map, order);
+ data->res = isl_union_map_add_map(data->res, map);
+
+ return data->res ? isl_stat_ok : isl_stat_error;
+}
+
+/* If "mupa" has a non-trivial explicit domain, then intersect
+ * domain and range of "umap" with this explicit domain.
+ * If the explicit domain only describes constraints on the parameters,
+ * then the intersection only needs to be performed once.
+ */
+static __isl_give isl_union_map *intersect_explicit_domain(
+ __isl_take isl_union_map *umap, __isl_keep isl_multi_union_pw_aff *mupa)
+{
+ isl_bool non_trivial, is_params;
+ isl_union_set *dom;
+
+ non_trivial = isl_multi_union_pw_aff_has_non_trivial_domain(mupa);
+ if (non_trivial < 0)
+ return isl_union_map_free(umap);
+ if (!non_trivial)
+ return umap;
+ mupa = isl_multi_union_pw_aff_copy(mupa);
+ dom = isl_multi_union_pw_aff_domain(mupa);
+ is_params = isl_union_set_is_params(dom);
+ if (is_params < 0) {
+ isl_union_set_free(dom);
+ return isl_union_map_free(umap);
+ }
+ if (is_params) {
+ isl_set *set;
+
+ set = isl_union_set_params(dom);
+ umap = isl_union_map_intersect_params(umap, set);
+ return umap;
+ }
+ umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(dom));
+ umap = isl_union_map_intersect_range(umap, dom);
+ return umap;
+}
+
+/* Intersect each map in "umap" with the result of calling "order"
+ * on the functions is "mupa" that apply to the domain and the range
+ * of the map.
+ */
+static __isl_give isl_union_map *isl_union_map_order_at_multi_union_pw_aff(
+ __isl_take isl_union_map *umap, __isl_take isl_multi_union_pw_aff *mupa,
+ __isl_give isl_map *(*order)(__isl_take isl_multi_pw_aff *mpa1,
+ __isl_take isl_multi_pw_aff *mpa2))
+{
+ struct isl_union_order_at_data data;
+
+ umap = isl_union_map_align_params(umap,
+ isl_multi_union_pw_aff_get_space(mupa));
+ mupa = isl_multi_union_pw_aff_align_params(mupa,
+ isl_union_map_get_space(umap));
+ umap = intersect_explicit_domain(umap, mupa);
+ data.mupa = mupa;
+ data.order = order;
+ data.res = isl_union_map_empty(isl_union_map_get_space(umap));
+ if (isl_union_map_foreach_map(umap, &order_at, &data) < 0)
+ data.res = isl_union_map_free(data.res);
+
+ isl_multi_union_pw_aff_free(mupa);
+ isl_union_map_free(umap);
+ return data.res;
+}
+
+/* Return the subset of "umap" where the domain and the range
+ * have equal "mupa" values.
+ */
+__isl_give isl_union_map *isl_union_map_eq_at_multi_union_pw_aff(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ return isl_union_map_order_at_multi_union_pw_aff(umap, mupa,
+ &isl_multi_pw_aff_eq_map);
+}
+
+#undef ORDER
+#define ORDER le
+#include "isl_union_map_lex_templ.c"
+
+#undef ORDER
+#define ORDER lt
+#include "isl_union_map_lex_templ.c"
+
+#undef ORDER
+#define ORDER ge
+#include "isl_union_map_lex_templ.c"
+
+#undef ORDER
+#define ORDER gt
+#include "isl_union_map_lex_templ.c"
+
+/* Return the union of the elements in the list "list".
+ */
+__isl_give isl_union_set *isl_union_set_list_union(
+ __isl_take isl_union_set_list *list)
+{
+ int i;
+ isl_size n;
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_union_set *res;
+
+ n = isl_union_set_list_n_union_set(list);
+ if (n < 0)
+ goto error;
+
+ ctx = isl_union_set_list_get_ctx(list);
+ space = isl_space_params_alloc(ctx, 0);
+ res = isl_union_set_empty(space);
+
+ for (i = 0; i < n; ++i) {
+ isl_union_set *uset_i;
+
+ uset_i = isl_union_set_list_get_union_set(list, i);
+ res = isl_union_set_union(res, uset_i);
+ }
+
+ isl_union_set_list_free(list);
+ return res;
+error:
+ isl_union_set_list_free(list);
+ return NULL;
+}
+
+/* Update *hash with the hash value of "map".
+ */
+static isl_stat add_hash(__isl_take isl_map *map, void *user)
+{
+ uint32_t *hash = user;
+ uint32_t map_hash;
+
+ map_hash = isl_map_get_hash(map);
+ isl_hash_hash(*hash, map_hash);
+
+ isl_map_free(map);
+ return isl_stat_ok;
+}
+
+/* Return a hash value that digests "umap".
+ */
+uint32_t isl_union_map_get_hash(__isl_keep isl_union_map *umap)
+{
+ uint32_t hash;
+
+ if (!umap)
+ return 0;
+
+ hash = isl_hash_init();
+ if (isl_union_map_foreach_map(umap, &add_hash, &hash) < 0)
+ return 0;
+
+ return hash;
+}
+
+/* Return a hash value that digests "uset".
+ */
+uint32_t isl_union_set_get_hash(__isl_keep isl_union_set *uset)
+{
+ return isl_union_map_get_hash(uset);
+}
+
+/* Add the number of basic sets in "set" to "n".
+ */
+static isl_stat add_n(__isl_take isl_set *set, void *user)
+{
+ int *n = user;
+ isl_size set_n;
+
+ set_n = isl_set_n_basic_set(set);
+ *n += set_n;
+ isl_set_free(set);
+
+ return set_n < 0 ? isl_stat_error : isl_stat_ok;
+}
+
+/* Return the total number of basic sets in "uset".
+ */
+int isl_union_set_n_basic_set(__isl_keep isl_union_set *uset)
+{
+ int n = 0;
+
+ if (isl_union_set_foreach_set(uset, &add_n, &n) < 0)
+ return -1;
+
+ return n;
+}
+
+/* Add the basic sets in "set" to "list".
+ */
+static isl_stat add_list(__isl_take isl_set *set, void *user)
+{
+ isl_basic_set_list **list = user;
+ isl_basic_set_list *list_i;
+
+ list_i = isl_set_get_basic_set_list(set);
+ *list = isl_basic_set_list_concat(*list, list_i);
+ isl_set_free(set);
+
+ if (!*list)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Return a list containing all the basic sets in "uset".
+ *
+ * First construct a list of the appropriate size and
+ * then add all the elements.
+ */
+__isl_give isl_basic_set_list *isl_union_set_get_basic_set_list(
+ __isl_keep isl_union_set *uset)
+{
+ int n;
+ isl_ctx *ctx;
+ isl_basic_set_list *list;
+
+ if (!uset)
+ return NULL;
+ ctx = isl_union_set_get_ctx(uset);
+ n = isl_union_set_n_basic_set(uset);
+ if (n < 0)
+ return NULL;
+ list = isl_basic_set_list_alloc(ctx, n);
+ if (isl_union_set_foreach_set(uset, &add_list, &list) < 0)
+ list = isl_basic_set_list_free(list);
+
+ return list;
+}
+
+/* Internal data structure for isl_union_map_remove_map_if.
+ * "fn" and "user" are the arguments to isl_union_map_remove_map_if.
+ */
+struct isl_union_map_remove_map_if_data {
+ isl_bool (*fn)(__isl_keep isl_map *map, void *user);
+ void *user;
+};
+
+/* isl_un_op_control filter that negates the result of data->fn
+ * called on "map".
+ */
+static isl_bool not(__isl_keep isl_map *map, void *user)
+{
+ struct isl_union_map_remove_map_if_data *data = user;
+
+ return isl_bool_not(data->fn(map, data->user));
+}
+
+/* Dummy isl_un_op_control transformation callback that
+ * simply returns the input.
+ */
+static __isl_give isl_map *map_id(__isl_take isl_map *map)
+{
+ return map;
+}
+
+/* Call "fn" on every map in "umap" and remove those maps
+ * for which the callback returns true.
+ *
+ * Use un_op to keep only those maps that are not filtered out,
+ * applying an identity transformation on them.
+ */
+__isl_give isl_union_map *isl_union_map_remove_map_if(
+ __isl_take isl_union_map *umap,
+ isl_bool (*fn)(__isl_keep isl_map *map, void *user), void *user)
+{
+ struct isl_union_map_remove_map_if_data data = { fn, user };
+ struct isl_un_op_control control = {
+ .filter = &not,
+ .filter_user = &data,
+ .fn_map = &map_id,
+ };
+ return un_op(umap, &control);
+}
+
+/* Does "map" have "space" as domain (ignoring parameters)?
+ */
+static isl_bool has_domain_space_tuples(__isl_keep isl_map *map, void *user)
+{
+ isl_space *space = user;
+
+ return isl_space_has_domain_tuples(space, isl_map_peek_space(map));
+}
+
+/* Does "map" have "space" as range (ignoring parameters)?
+ */
+static isl_bool has_range_space_tuples(__isl_keep isl_map *map, void *user)
+{
+ isl_space *space = user;
+
+ return isl_space_has_range_tuples(space, isl_map_peek_space(map));
+}
+
+/* Wrapper around isl_map_bind_range for use as a un_op callback.
+ */
+static __isl_give isl_map *bind_range(__isl_take isl_map *map, void *user)
+{
+ isl_multi_id *tuple = user;
+
+ return isl_map_bind_range(map, isl_multi_id_copy(tuple));
+}
+
+/* Bind the output dimensions of "umap" to parameters with identifiers
+ * specified by "tuple", living in the range space of "umap",
+ * for those maps that have a matching range space.
+ */
+__isl_give isl_union_set *isl_union_map_bind_range(
+ __isl_take isl_union_map *umap, __isl_take isl_multi_id *tuple)
+{
+ struct isl_un_op_control control = {
+ .filter = &has_range_space_tuples,
+ .filter_user = isl_multi_id_peek_space(tuple),
+ .fn_map2 = &bind_range,
+ .fn_map2_user = tuple,
+ };
+ isl_union_set *bound;
+
+ bound = uset_from_umap(un_op(umap, &control));
+ isl_multi_id_free(tuple);
+ return bound;
+}
+
+/* Only keep those elements in "umap" that have a domain in "space".
+ */
+__isl_give isl_union_map *isl_union_map_intersect_domain_space(
+ __isl_take isl_union_map *umap, __isl_take isl_space *space)
+{
+ struct isl_un_op_control control = {
+ .filter = &has_domain_space_tuples,
+ .filter_user = space,
+ };
+
+ umap = un_op(umap, &control);
+ isl_space_free(space);
+ return umap;
+}
+
+/* Only keep those elements in "umap" that have a range in "space".
+ */
+__isl_give isl_union_map *isl_union_map_intersect_range_space(
+ __isl_take isl_union_map *umap, __isl_take isl_space *space)
+{
+ struct isl_un_op_control control = {
+ .filter = &has_range_space_tuples,
+ .filter_user = space,
+ };
+
+ umap = un_op(umap, &control);
+ isl_space_free(space);
+ return umap;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map_lex_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map_lex_templ.c
new file mode 100644
index 00000000000..b9df71261d2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map_lex_templ.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 INRIA Rocquencourt
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,
+ * B.P. 105 - 78153 Le Chesnay, France
+ */
+
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Return the subset of "umap" where the domain and the range
+ * have "mupa" values that lexicographically compare as "ORDER".
+ */
+__isl_give isl_union_map *FN(FN(isl_union_map_lex,ORDER),at_multi_union_pw_aff)(
+ __isl_take isl_union_map *umap,
+ __isl_take isl_multi_union_pw_aff *mupa)
+{
+ return isl_union_map_order_at_multi_union_pw_aff(umap, mupa,
+ &FN(FN(isl_multi_pw_aff_lex,ORDER),map));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map_private.h
new file mode 100644
index 00000000000..af52d9f923b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_map_private.h
@@ -0,0 +1,27 @@
+#define isl_union_set_list isl_union_map_list
+#define isl_union_set isl_union_map
+#include <isl/union_map.h>
+#include <isl/union_set.h>
+
+struct isl_union_map {
+ int ref;
+ isl_space *dim;
+
+ struct isl_hash_table table;
+};
+
+struct isl_hash_table_entry *isl_union_set_find_entry(
+ __isl_keep isl_union_set *uset, __isl_keep isl_space *space,
+ int reserve);
+
+__isl_keep isl_space *isl_union_map_peek_space(__isl_keep isl_union_map *umap);
+__isl_keep isl_space *isl_union_set_peek_space(__isl_keep isl_union_set *uset);
+isl_bool isl_union_map_is_params(__isl_keep isl_union_map *umap);
+isl_bool isl_union_map_space_has_equal_params(__isl_keep isl_union_map *umap,
+ __isl_keep isl_space *space);
+isl_bool isl_union_set_space_has_equal_params(__isl_keep isl_union_set *uset,
+ __isl_keep isl_space *space);
+__isl_give isl_union_map *isl_union_map_reset_range_space(
+ __isl_take isl_union_map *umap, __isl_take isl_space *space);
+__isl_give isl_union_map *isl_union_map_reset_equal_dim_space(
+ __isl_take isl_union_map *umap, __isl_take isl_space *space);
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_multi.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_multi.c
new file mode 100644
index 00000000000..b630b18b89e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_multi.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2013 Ecole Normale Superieure
+ * Copyright 2015 INRIA Paris-Rocquencourt
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ * and INRIA Paris-Rocquencourt, Domaine de Voluceau, Rocquenqourt, B.P. 105,
+ * 78153 Le Chesnay Cedex France
+ */
+
+#include <isl/hash.h>
+#include <isl_union_macro.h>
+
+/* A group of expressions defined over the same domain space "domain_space".
+ * The entries of "part_table" are the individual expressions,
+ * keyed on the entire space of the expression (ignoring parameters).
+ *
+ * Each UNION has its own groups, so there can only ever be a single
+ * reference to each group.
+ */
+S(UNION,group) {
+ isl_space *domain_space;
+ struct isl_hash_table part_table;
+};
+
+/* A union of expressions defined over different disjoint domains.
+ * "space" describes the parameters.
+ * The entries of "table" are keyed on the domain space of the entry
+ * (ignoring parameters) and
+ * contain groups of expressions that are defined over the same domain space.
+ */
+struct UNION {
+ int ref;
+ isl_space *space;
+
+ struct isl_hash_table table;
+};
+
+/* Internal data structure for isl_union_*_foreach_group.
+ * "fn" is the function that needs to be called on each group.
+ */
+S(UNION,foreach_group_data)
+{
+ isl_stat (*fn)(__isl_keep S(UNION,group) *group, void *user);
+ void *user;
+};
+
+/* Call data->fn on the group stored at *entry.
+ */
+static isl_stat FN(UNION,call_on_group)(void **entry, void *user)
+{
+ S(UNION,group) *group = *entry;
+ S(UNION,foreach_group_data) *data;
+
+ data = (S(UNION,foreach_group_data) *) user;
+ return data->fn(group, data->user);
+}
+
+/* Call "fn" on each group of expressions in "u".
+ */
+static isl_stat FN(UNION,foreach_group)(__isl_keep UNION *u,
+ isl_stat (*fn)(__isl_keep S(UNION,group) *group, void *user),
+ void *user)
+{
+ S(UNION,foreach_group_data) data = { fn, user };
+
+ if (!u)
+ return isl_stat_error;
+
+ return isl_hash_table_foreach(u->space->ctx, &u->table,
+ &FN(UNION,call_on_group), &data);
+}
+
+/* A isl_union_*_foreach_group callback for counting the total number
+ * of expressions in a UNION. Add the number of expressions in "group"
+ * to *n.
+ */
+static isl_stat FN(UNION,count_part)(__isl_keep S(UNION,group) *group,
+ void *user)
+{
+ int *n = user;
+
+ if (!group)
+ return isl_stat_error;
+
+ *n += group->part_table.n;
+ return isl_stat_ok;
+}
+
+/* Return the number of base expressions in "u".
+ */
+isl_size FN(FN(UNION,n),BASE)(__isl_keep UNION *u)
+{
+ int n;
+
+ n = 0;
+ if (FN(UNION,foreach_group)(u, &FN(UNION,count_part), &n) < 0)
+ return isl_size_error;
+ return n;
+}
+
+/* Free an entry in a group of expressions.
+ * Each entry in such a group is a single expression.
+ */
+static isl_stat FN(UNION,free_group_entry)(void **entry, void *user)
+{
+ PART *part = *entry;
+
+ FN(PART,free)(part);
+ return isl_stat_ok;
+}
+
+/* Free all memory allocated for "group" and return NULL.
+ */
+static __isl_null S(UNION,group) *FN(UNION,group_free)(
+ __isl_take S(UNION,group) *group)
+{
+ isl_ctx *ctx;
+
+ if (!group)
+ return NULL;
+
+ ctx = isl_space_get_ctx(group->domain_space);
+ isl_hash_table_foreach(ctx, &group->part_table,
+ &FN(UNION,free_group_entry), NULL);
+ isl_hash_table_clear(&group->part_table);
+ isl_space_free(group->domain_space);
+ free(group);
+ return NULL;
+}
+
+/* Allocate a group of expressions defined over the same domain space
+ * with domain space "domain_space" and initial size "size".
+ */
+static __isl_give S(UNION,group) *FN(UNION,group_alloc)(
+ __isl_take isl_space *domain_space, int size)
+{
+ isl_ctx *ctx;
+ S(UNION,group) *group;
+
+ if (!domain_space)
+ return NULL;
+ ctx = isl_space_get_ctx(domain_space);
+ group = isl_calloc_type(ctx, S(UNION,group));
+ if (!group)
+ goto error;
+ group->domain_space = domain_space;
+ if (isl_hash_table_init(ctx, &group->part_table, size) < 0)
+ return FN(UNION,group_free)(group);
+
+ return group;
+error:
+ isl_space_free(domain_space);
+ return NULL;
+}
+
+/* Is the space of "entry" equal to "space", ignoring parameters?
+ */
+static isl_bool FN(UNION,has_space_tuples)(const void *entry, const void *val)
+{
+ PART *part = (PART *) entry;
+ isl_space *space = (isl_space *) val;
+ isl_space *part_space;
+
+ part_space = FN(PART,peek_space)(part);
+ return isl_space_has_equal_tuples(part_space, space);
+}
+
+/* Return a group equal to "group", but with a single reference.
+ * Since all groups have only a single reference, simply return "group".
+ */
+static __isl_give S(UNION,group) *FN(UNION,group_cow)(
+ __isl_take S(UNION,group) *group)
+{
+ return group;
+}
+
+S(UNION,foreach_data)
+{
+ isl_stat (*fn)(__isl_take PART *part, void *user);
+ void *user;
+};
+
+static isl_stat FN(UNION,call_on_copy)(void **entry, void *user)
+{
+ PART *part = *entry;
+ S(UNION,foreach_data) *data = (S(UNION,foreach_data) *) user;
+
+ part = FN(PART,copy)(part);
+ if (!part)
+ return isl_stat_error;
+ return data->fn(part, data->user);
+}
+
+/* Call data->fn on a copy of each expression in "group".
+ */
+static isl_stat FN(UNION,group_call_on_copy)(__isl_keep S(UNION,group) *group,
+ void *user)
+{
+ isl_ctx *ctx;
+
+ if (!group)
+ return isl_stat_error;
+
+ ctx = isl_space_get_ctx(group->domain_space);
+ return isl_hash_table_foreach(ctx, &group->part_table,
+ &FN(UNION,call_on_copy), user);
+}
+
+isl_stat FN(FN(UNION,foreach),BASE)(__isl_keep UNION *u,
+ isl_stat (*fn)(__isl_take PART *part, void *user), void *user)
+{
+ S(UNION,foreach_data) data = { fn, user };
+
+ if (!u)
+ return isl_stat_error;
+
+ return FN(UNION,foreach_group)(u, &FN(UNION,group_call_on_copy), &data);
+}
+
+/* Is the domain space of the group of expressions at "entry"
+ * equal to that of "space", ignoring parameters?
+ */
+static isl_bool FN(UNION,group_has_same_domain_space_tuples)(const void *entry,
+ const void *val)
+{
+ S(UNION,group) *group = (S(UNION,group) *) entry;
+ isl_space *space = (isl_space *) val;
+
+ return isl_space_has_domain_tuples(group->domain_space, space);
+}
+
+/* Return the entry, if any, in "u" that lives in "space".
+ * If "reserve" is set, then an entry is created if it does not exist yet.
+ * Return NULL on error and isl_hash_table_entry_none if no entry was found.
+ * Note that when "reserve" is set, the function will never return
+ * isl_hash_table_entry_none.
+ *
+ * First look for the group of expressions with the same domain space,
+ * creating one if needed.
+ * Then look for the expression living in the specified space in that group.
+ */
+static struct isl_hash_table_entry *FN(UNION,find_part_entry)(
+ __isl_keep UNION *u, __isl_keep isl_space *space, int reserve)
+{
+ isl_ctx *ctx;
+ uint32_t hash;
+ struct isl_hash_table_entry *group_entry;
+ S(UNION,group) *group;
+
+ if (!u || !space)
+ return NULL;
+
+ ctx = FN(UNION,get_ctx)(u);
+ hash = isl_space_get_tuple_domain_hash(space);
+ group_entry = isl_hash_table_find(ctx, &u->table, hash,
+ &FN(UNION,group_has_same_domain_space_tuples), space, reserve);
+ if (!group_entry || group_entry == isl_hash_table_entry_none)
+ return group_entry;
+ if (reserve && !group_entry->data) {
+ isl_space *domain = isl_space_domain(isl_space_copy(space));
+ group = FN(UNION,group_alloc)(domain, 1);
+ group_entry->data = group;
+ } else {
+ group = group_entry->data;
+ if (reserve)
+ group = FN(UNION,group_cow)(group);
+ }
+ if (!group)
+ return NULL;
+ hash = isl_space_get_tuple_hash(space);
+ return isl_hash_table_find(ctx, &group->part_table, hash,
+ &FN(UNION,has_space_tuples), space, reserve);
+}
+
+/* Remove "part_entry" from the hash table of "u".
+ *
+ * First look the group_entry in "u" holding the group that
+ * contains "part_entry". Remove "part_entry" from that group.
+ * If the group becomes empty, then also remove the group_entry from "u".
+ */
+static __isl_give UNION *FN(UNION,remove_part_entry)(__isl_take UNION *u,
+ struct isl_hash_table_entry *part_entry)
+{
+ isl_ctx *ctx;
+ uint32_t hash;
+ isl_space *space;
+ PART *part;
+ struct isl_hash_table_entry *group_entry;
+ S(UNION,group) *group;
+
+ if (!u || !part_entry)
+ return FN(UNION,free)(u);
+
+ part = part_entry->data;
+ ctx = FN(UNION,get_ctx)(u);
+ space = FN(PART,peek_space)(part);
+ hash = isl_space_get_tuple_domain_hash(space);
+ group_entry = isl_hash_table_find(ctx, &u->table, hash,
+ &FN(UNION,group_has_same_domain_space_tuples), space, 0);
+ if (!group_entry)
+ return FN(UNION,free)(u);
+ if (group_entry == isl_hash_table_entry_none)
+ isl_die(ctx, isl_error_internal, "missing group",
+ return FN(UNION,free)(u));
+ group = group_entry->data;
+ isl_hash_table_remove(ctx, &group->part_table, part_entry);
+ FN(PART,free)(part);
+
+ if (group->part_table.n != 0)
+ return u;
+
+ isl_hash_table_remove(ctx, &u->table, group_entry);
+ FN(UNION,group_free)(group);
+
+ return u;
+}
+
+/* Are the domains of "part1" and "part2" disjoint?
+ */
+static isl_bool FN(UNION,disjoint_domain)(__isl_keep PART *part1,
+ __isl_keep PART *part2)
+{
+ isl_set *dom1, *dom2;
+ isl_bool disjoint;
+
+ if (!part1 || !part2)
+ return isl_bool_error;
+ dom1 = FN(PART,domain)(FN(PART,copy)(part1));
+ dom2 = FN(PART,domain)(FN(PART,copy)(part2));
+ disjoint = isl_set_is_disjoint(dom1, dom2);
+ isl_set_free(dom1);
+ isl_set_free(dom2);
+
+ return disjoint;
+}
+
+/* Check that the expression at *entry has a domain that is disjoint
+ * from that of "part", unless they also have the same target space.
+ */
+static isl_stat FN(UNION,check_disjoint_domain_entry)(void **entry, void *user)
+{
+ PART *part = user;
+ PART *other = *entry;
+ isl_bool equal;
+ isl_bool disjoint;
+
+ equal = isl_space_is_equal(part->dim, other->dim);
+ if (equal < 0)
+ return isl_stat_error;
+ if (equal)
+ return isl_stat_ok;
+
+ disjoint = FN(UNION,disjoint_domain)(part, other);
+ if (disjoint < 0)
+ return isl_stat_error;
+ if (!disjoint)
+ isl_die(FN(PART,get_ctx)(part), isl_error_invalid,
+ "overlapping domain with other part",
+ return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Check that the domain of "part" is disjoint from the domain of the entries
+ * in "u" that are defined on the same domain space, but have a different
+ * target space.
+ * If there is no group of expressions in "u" with the same domain space,
+ * then everything is fine. Otherwise, check the individual expressions
+ * in that group.
+ */
+static isl_stat FN(UNION,check_disjoint_domain_other)(__isl_keep UNION *u,
+ __isl_keep PART *part)
+{
+ isl_ctx *ctx;
+ uint32_t hash;
+ isl_space *space;
+ struct isl_hash_table_entry *group_entry;
+ S(UNION,group) *group;
+
+ if (!u || !part)
+ return isl_stat_error;
+ ctx = FN(UNION,get_ctx)(u);
+ space = FN(PART,peek_space)(part);
+ hash = isl_space_get_tuple_domain_hash(space);
+ group_entry = isl_hash_table_find(ctx, &u->table, hash,
+ &FN(UNION,group_has_same_domain_space_tuples), space, 0);
+ if (!group_entry)
+ return isl_stat_error;
+ if (group_entry == isl_hash_table_entry_none)
+ return isl_stat_ok;
+ group = group_entry->data;
+ return isl_hash_table_foreach(ctx, &group->part_table,
+ &FN(UNION,check_disjoint_domain_entry), part);
+}
+
+/* Check that the domain of "part1" is disjoint from the domain of "part2".
+ * This check is performed before "part2" is added to a UNION to ensure
+ * that the UNION expression remains a function.
+ */
+static isl_stat FN(UNION,check_disjoint_domain)(__isl_keep PART *part1,
+ __isl_keep PART *part2)
+{
+ isl_bool disjoint;
+
+ disjoint = FN(UNION,disjoint_domain)(part1, part2);
+ if (disjoint < 0)
+ return isl_stat_error;
+ if (!disjoint)
+ isl_die(FN(PART,get_ctx)(part1), isl_error_invalid,
+ "domain of additional part should be disjoint",
+ return isl_stat_error);
+ return isl_stat_ok;
+}
+
+/* Internal data structure for isl_union_*_foreach_inplace.
+ * "fn" is the function that needs to be called on each entry.
+ */
+S(UNION,foreach_inplace_data)
+{
+ isl_stat (*fn)(void **entry, void *user);
+ void *user;
+};
+
+/* isl_union_*_foreach_group callback for calling data->fn on
+ * each part entry in the group.
+ */
+static isl_stat FN(UNION,group_call_inplace)(__isl_keep S(UNION,group) *group,
+ void *user)
+{
+ isl_ctx *ctx;
+ S(UNION,foreach_inplace_data) *data;
+
+ if (!group)
+ return isl_stat_error;
+
+ data = (S(UNION,foreach_inplace_data) *) user;
+ ctx = isl_space_get_ctx(group->domain_space);
+ return isl_hash_table_foreach(ctx, &group->part_table,
+ data->fn, data->user);
+}
+
+/* Call "fn" on each part entry of "u".
+ */
+static isl_stat FN(UNION,foreach_inplace)(__isl_keep UNION *u,
+ isl_stat (*fn)(void **part, void *user), void *user)
+{
+ S(UNION,foreach_inplace_data) data = { fn, user };
+
+ return FN(UNION,foreach_group)(u, &FN(UNION,group_call_inplace), &data);
+}
+
+static isl_stat FN(UNION,free_u_entry)(void **entry, void *user)
+{
+ S(UNION,group) *group = *entry;
+ FN(UNION,group_free)(group);
+ return isl_stat_ok;
+}
+
+/* Does "u" have an obviously empty definition domain?
+ */
+isl_bool FN(UNION,plain_is_empty)(__isl_take UNION *u)
+{
+ if (!u)
+ return isl_bool_error;
+ return isl_bool_ok(u->table.n == 0);
+}
+
+/* Set "single" to true if this group of expressions
+ * contains an expression living in exactly one space.
+ */
+static isl_stat FN(UNION,group_single_space)(__isl_keep S(UNION,group) *group,
+ void *user)
+{
+ isl_bool *single = user;
+
+ if (!group)
+ return isl_stat_error;
+ *single = isl_bool_ok(group->part_table.n == 1);
+ return isl_stat_ok;
+}
+
+/* Can this union expression be converted to a single base expression?
+ * That is, does it contain a base expression in exactly one space?
+ * In particular, is only one domain space involved and
+ * is only a single expression associated to that domain?
+ */
+isl_bool FN(FN(UNION,isa),BASE)(__isl_take UNION *u)
+{
+ isl_bool single;
+
+ if (!u)
+ return isl_bool_error;
+ if (u->table.n != 1)
+ return isl_bool_false;
+ if (FN(UNION,foreach_group)(u,
+ &FN(UNION,group_single_space), &single) < 0)
+ return isl_bool_error;
+ return single;
+}
+
+/* Callback for isl_union_*_foreach_inplace call
+ * on a union expression with a single base expression.
+ * Store that base expression in "user".
+ * This callback should only be called once
+ * for any given isl_union_*_foreach_inplace call.
+ */
+static isl_stat FN(UNION,extract_part)(void **entry, void *user)
+{
+ PART **part_p = user;
+ PART *part = *entry;
+
+ if (*part_p)
+ isl_die(FN(PART,get_ctx)(part), isl_error_internal,
+ "more than one part", return isl_stat_error);
+ *part_p = FN(PART,copy)(part);
+ if (!*part_p)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Convert the union expression to its single base expression.
+ */
+__isl_give PART *FN(FN(UNION,as),BASE)(__isl_take UNION *u)
+{
+ isl_bool has_single_space;
+ PART *part = NULL;
+
+ has_single_space = FN(FN(UNION,isa),BASE)(u);
+ if (has_single_space < 0)
+ goto error;
+ if (!has_single_space)
+ isl_die(FN(UNION,get_ctx)(u), isl_error_invalid,
+ "expecting elements in exactly one space",
+ goto error);
+ if (FN(UNION,foreach_inplace)(u, &FN(UNION,extract_part), &part) < 0)
+ part = FN(PART,free)(part);
+ FN(UNION,free)(u);
+ return part;
+error:
+ FN(UNION,free)(u);
+ return NULL;
+}
+
+#include <isl_union_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_neg.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_neg.c
new file mode 100644
index 00000000000..386b8dcd801
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_neg.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_union_macro.h>
+
+/* Return the opposite of "part".
+ */
+static __isl_give PART *FN(UNION,neg_entry)(__isl_take PART *part, void *user)
+{
+ return FN(PART,neg)(part);
+}
+
+/* Return the opposite of "u".
+ */
+__isl_give UNION *FN(UNION,neg)(__isl_take UNION *u)
+{
+ return FN(UNION,transform_inplace)(u, &FN(UNION,neg_entry), NULL);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_pw_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_pw_templ.c
new file mode 100644
index 00000000000..e226ad72dfd
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_pw_templ.c
@@ -0,0 +1,22 @@
+#define xCAT(A,B) A ## B
+#define CAT(A,B) xCAT(A,B)
+#undef EL
+#define EL CAT(isl_,BASE)
+#undef PW_BASE
+#define PW_BASE CAT(pw_,BASE)
+#undef PW
+#define PW CAT(isl_,PW_BASE)
+#undef UNION_BASE
+#define UNION_BASE CAT(union_,PW_BASE)
+#undef UNION
+#define UNION CAT(isl_,UNION_BASE)
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Create a union piecewise expression
+ * with the given base expression on a universe domain.
+ */
+__isl_give UNION *FN(FN(UNION,from),BASE)(__isl_take EL *el)
+{
+ return FN(FN(UNION,from),PW_BASE)(FN(FN(PW,from),BASE)(el));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_set_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_set_private.h
new file mode 100644
index 00000000000..32a70732ae7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_set_private.h
@@ -0,0 +1,11 @@
+#ifndef ISL_UNION_SET_PRIVATE_H
+#define ISL_UNION_SET_PRIVATE_H
+
+#include <isl/union_set.h>
+
+__isl_give isl_union_set *isl_union_set_combined_lineality_space(
+ __isl_take isl_union_set *uset);
+__isl_give isl_union_set *isl_union_set_plain_gist(
+ __isl_take isl_union_set *uset, __isl_take isl_union_set *context);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_single.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_single.c
new file mode 100644
index 00000000000..e84706c53ba
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_single.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/hash.h>
+#include <isl_union_macro.h>
+
+/* A union of expressions defined over different domain spaces.
+ * "space" describes the parameters.
+ * The entries of "table" are keyed on the domain space of the entry
+ * (ignoring parameters).
+ */
+struct UNION {
+ int ref;
+#ifdef HAS_TYPE
+ enum isl_fold type;
+#endif
+ isl_space *space;
+
+ struct isl_hash_table table;
+};
+
+/* Return the number of base expressions in "u".
+ */
+isl_size FN(FN(UNION,n),BASE)(__isl_keep UNION *u)
+{
+ return u ? u->table.n : isl_size_error;
+}
+
+S(UNION,foreach_data)
+{
+ isl_stat (*fn)(__isl_take PART *part, void *user);
+ void *user;
+};
+
+static isl_stat FN(UNION,call_on_copy)(void **entry, void *user)
+{
+ PART *part = *entry;
+ S(UNION,foreach_data) *data = (S(UNION,foreach_data) *)user;
+
+ part = FN(PART,copy)(part);
+ if (!part)
+ return isl_stat_error;
+ return data->fn(part, data->user);
+}
+
+isl_stat FN(FN(UNION,foreach),BASE)(__isl_keep UNION *u,
+ isl_stat (*fn)(__isl_take PART *part, void *user), void *user)
+{
+ S(UNION,foreach_data) data = { fn, user };
+
+ if (!u)
+ return isl_stat_error;
+
+ return isl_hash_table_foreach(u->space->ctx, &u->table,
+ &FN(UNION,call_on_copy), &data);
+}
+
+/* Is the domain space of "entry" equal to the domain of "space",
+ * ignoring parameters?
+ */
+static isl_bool FN(UNION,has_same_domain_space_tuples)(const void *entry,
+ const void *val)
+{
+ PART *part = (PART *)entry;
+ isl_space *space = (isl_space *) val;
+
+ if (isl_space_is_set(space))
+ return isl_space_is_set(part->dim);
+
+ return isl_space_tuple_is_equal(part->dim, isl_dim_in,
+ space, isl_dim_in);
+}
+
+/* Return the entry, if any, in "u" that lives in "space".
+ * If "reserve" is set, then an entry is created if it does not exist yet.
+ * Return NULL on error and isl_hash_table_entry_none if no entry was found.
+ * Note that when "reserve" is set, the function will never return
+ * isl_hash_table_entry_none.
+ *
+ * First look for the entry (if any) with the same domain space.
+ * If it exists, then check if the range space also matches.
+ */
+static struct isl_hash_table_entry *FN(UNION,find_part_entry)(
+ __isl_keep UNION *u, __isl_keep isl_space *space, int reserve)
+{
+ isl_ctx *ctx;
+ uint32_t hash;
+ struct isl_hash_table_entry *entry;
+ isl_bool equal;
+ PART *part;
+
+ if (!u || !space)
+ return NULL;
+
+ ctx = FN(UNION,get_ctx)(u);
+ hash = isl_space_get_tuple_domain_hash(space);
+ entry = isl_hash_table_find(ctx, &u->table, hash,
+ &FN(UNION,has_same_domain_space_tuples), space, reserve);
+ if (!entry || entry == isl_hash_table_entry_none)
+ return entry;
+ if (reserve && !entry->data)
+ return entry;
+ part = entry->data;
+ equal = isl_space_tuple_is_equal(part->dim, isl_dim_out,
+ space, isl_dim_out);
+ if (equal < 0)
+ return NULL;
+ if (equal)
+ return entry;
+ if (!reserve)
+ return isl_hash_table_entry_none;
+ isl_die(FN(UNION,get_ctx)(u), isl_error_invalid,
+ "union expression can only contain a single "
+ "expression over a given domain", return NULL);
+}
+
+/* Remove "part_entry" from the hash table of "u".
+ */
+static __isl_give UNION *FN(UNION,remove_part_entry)(__isl_take UNION *u,
+ struct isl_hash_table_entry *part_entry)
+{
+ isl_ctx *ctx;
+
+ if (!u || !part_entry)
+ return FN(UNION,free)(u);
+
+ ctx = FN(UNION,get_ctx)(u);
+ isl_hash_table_remove(ctx, &u->table, part_entry);
+ FN(PART,free)(part_entry->data);
+
+ return u;
+}
+
+/* Check that the domain of "part" is disjoint from the domain of the entries
+ * in "u" that are defined on the same domain space, but have a different
+ * target space.
+ * Since a UNION with a single entry per domain space is not allowed
+ * to contain two entries with the same domain space, there cannot be
+ * any such other entry.
+ */
+static isl_stat FN(UNION,check_disjoint_domain_other)(__isl_keep UNION *u,
+ __isl_keep PART *part)
+{
+ return isl_stat_ok;
+}
+
+/* Check that the domain of "part1" is disjoint from the domain of "part2".
+ * This check is performed before "part2" is added to a UNION to ensure
+ * that the UNION expression remains a function.
+ * Since a UNION with a single entry per domain space is not allowed
+ * to contain two entries with the same domain space, fail unconditionally.
+ */
+static isl_stat FN(UNION,check_disjoint_domain)(__isl_keep PART *part1,
+ __isl_keep PART *part2)
+{
+ isl_die(FN(PART,get_ctx)(part1), isl_error_invalid,
+ "additional part should live on separate space",
+ return isl_stat_error);
+}
+
+/* Call "fn" on each part entry of "u".
+ */
+static isl_stat FN(UNION,foreach_inplace)(__isl_keep UNION *u,
+ isl_stat (*fn)(void **part, void *user), void *user)
+{
+ isl_ctx *ctx;
+
+ if (!u)
+ return isl_stat_error;
+ ctx = FN(UNION,get_ctx)(u);
+ return isl_hash_table_foreach(ctx, &u->table, fn, user);
+}
+
+static isl_bool FN(PART,has_domain_space_tuples)(__isl_keep PART *part,
+ __isl_keep isl_space *space);
+
+/* Do the tuples of "space" correspond to those of the domain of "part"?
+ * That is, is the domain space of "part" equal to "space", ignoring parameters?
+ */
+static isl_bool FN(UNION,has_domain_space_tuples)(const void *entry,
+ const void *val)
+{
+ PART *part = (PART *)entry;
+ isl_space *space = (isl_space *) val;
+
+ return FN(PART,has_domain_space_tuples)(part, space);
+}
+
+/* Call "fn" on each part of "u" that has domain space "space".
+ *
+ * Since each entry is defined over a different domain space,
+ * simply look for an entry with the given domain space and
+ * call "fn" on the corresponding part if there is one.
+ */
+isl_stat FN(UNION,foreach_on_domain)(__isl_keep UNION *u,
+ __isl_keep isl_space *space,
+ isl_stat (*fn)(__isl_take PART *part, void *user), void *user)
+{
+ uint32_t hash;
+ struct isl_hash_table_entry *entry;
+
+ if (!u || !space)
+ return isl_stat_error;
+ hash = isl_space_get_tuple_hash(space);
+ entry = isl_hash_table_find(FN(UNION,get_ctx)(u), &u->table,
+ hash, &FN(UNION,has_domain_space_tuples),
+ space, 0);
+ if (!entry)
+ return isl_stat_error;
+ if (entry == isl_hash_table_entry_none)
+ return isl_stat_ok;
+ return fn(FN(PART,copy)(entry->data), user);
+}
+
+static isl_stat FN(UNION,free_u_entry)(void **entry, void *user)
+{
+ PART *part = *entry;
+ FN(PART,free)(part);
+ return isl_stat_ok;
+}
+
+#include <isl_union_templ.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_templ.c
new file mode 100644
index 00000000000..d16ccd94aa3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_union_templ.c
@@ -0,0 +1,1419 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#undef TYPE
+#define TYPE UNION
+static
+#include "has_single_reference_templ.c"
+
+__isl_give UNION *FN(UNION,cow)(__isl_take UNION *u);
+
+isl_ctx *FN(UNION,get_ctx)(__isl_keep UNION *u)
+{
+ return u ? u->space->ctx : NULL;
+}
+
+/* Return the space of "u".
+ */
+static __isl_keep isl_space *FN(UNION,peek_space)(__isl_keep UNION *u)
+{
+ if (!u)
+ return NULL;
+ return u->space;
+}
+
+/* Return a copy of the space of "u".
+ */
+__isl_give isl_space *FN(UNION,get_space)(__isl_keep UNION *u)
+{
+ return isl_space_copy(FN(UNION,peek_space)(u));
+}
+
+/* Return the number of parameters of "u", where "type"
+ * is required to be set to isl_dim_param.
+ */
+isl_size FN(UNION,dim)(__isl_keep UNION *u, enum isl_dim_type type)
+{
+ if (!u)
+ return isl_size_error;
+
+ if (type != isl_dim_param)
+ isl_die(FN(UNION,get_ctx)(u), isl_error_invalid,
+ "can only reference parameters", return isl_size_error);
+
+ return isl_space_dim(u->space, type);
+}
+
+/* Return the position of the parameter with the given name
+ * in "u".
+ * Return -1 if no such dimension can be found.
+ */
+int FN(UNION,find_dim_by_name)(__isl_keep UNION *u, enum isl_dim_type type,
+ const char *name)
+{
+ if (!u)
+ return -1;
+ return isl_space_find_dim_by_name(u->space, type, name);
+}
+
+#include "opt_type.h"
+
+static __isl_give UNION *FN(UNION,alloc)(__isl_take isl_space *space
+ OPT_TYPE_PARAM, int size)
+{
+ UNION *u;
+
+ space = isl_space_params(space);
+ if (!space)
+ return NULL;
+
+ u = isl_calloc_type(space->ctx, UNION);
+ if (!u)
+ goto error;
+
+ u->ref = 1;
+ OPT_SET_TYPE(u->, type);
+ u->space = space;
+ if (isl_hash_table_init(space->ctx, &u->table, size) < 0)
+ return FN(UNION,free)(u);
+
+ return u;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Create an empty/zero union without specifying any parameters.
+ */
+__isl_give UNION *FN(FN(UNION,ZERO),ctx)(isl_ctx *ctx OPT_TYPE_PARAM)
+{
+ isl_space *space;
+
+ space = isl_space_unit(ctx);
+ return FN(FN(UNION,ZERO),space)(space OPT_TYPE_ARG(NO_LOC));
+}
+
+__isl_give UNION *FN(FN(UNION,ZERO),space)(__isl_take isl_space *space
+ OPT_TYPE_PARAM)
+{
+ return FN(UNION,alloc)(space OPT_TYPE_ARG(NO_LOC), 16);
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give UNION *FN(UNION,ZERO)(__isl_take isl_space *space OPT_TYPE_PARAM)
+{
+ return FN(FN(UNION,ZERO),space)(space OPT_TYPE_ARG(NO_LOC));
+}
+
+__isl_give UNION *FN(UNION,copy)(__isl_keep UNION *u)
+{
+ if (!u)
+ return NULL;
+
+ u->ref++;
+ return u;
+}
+
+/* Do the tuples of "space" correspond to those of the domain of "part"?
+ * That is, is the domain space of "part" equal to "space", ignoring parameters?
+ */
+static isl_bool FN(PART,has_domain_space_tuples)(__isl_keep PART *part,
+ __isl_keep isl_space *space)
+{
+ return isl_space_has_domain_tuples(space, FN(PART,peek_space)(part));
+}
+
+/* Extract the element of "u" living in "space" (ignoring parameters).
+ *
+ * Return the ZERO element if "u" does not contain any element
+ * living in "space".
+ */
+__isl_give PART *FN(FN(UNION,extract),BASE)(__isl_keep UNION *u,
+ __isl_take isl_space *space)
+{
+ struct isl_hash_table_entry *entry;
+
+ entry = FN(UNION,find_part_entry)(u, space, 0);
+ if (!entry)
+ goto error;
+ if (entry == isl_hash_table_entry_none)
+ return FN(PART,ZERO)(space OPT_TYPE_ARG(u->));
+ isl_space_free(space);
+ return FN(PART,copy)(entry->data);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Add "part" to "u".
+ * If "disjoint" is set, then "u" is not allowed to already have
+ * a part that is defined over a domain that overlaps with the domain
+ * of "part".
+ * Otherwise, compute the union sum of "part" and the part in "u"
+ * defined on the same space.
+ */
+static __isl_give UNION *FN(UNION,add_part_generic)(__isl_take UNION *u,
+ __isl_take PART *part, int disjoint)
+{
+ int empty;
+ struct isl_hash_table_entry *entry;
+
+ if (!part)
+ goto error;
+
+ empty = FN(PART,IS_ZERO)(part);
+ if (empty < 0)
+ goto error;
+ if (empty) {
+ FN(PART,free)(part);
+ return u;
+ }
+
+ u = FN(UNION,align_params)(u, FN(PART,get_space)(part));
+ part = FN(PART,align_params)(part, FN(UNION,get_space)(u));
+
+ u = FN(UNION,cow)(u);
+
+ if (!u)
+ goto error;
+
+ if (FN(UNION,check_disjoint_domain_other)(u, part) < 0)
+ goto error;
+ entry = FN(UNION,find_part_entry)(u, part->dim, 1);
+ if (!entry)
+ goto error;
+
+ if (!entry->data)
+ entry->data = part;
+ else {
+ if (disjoint &&
+ FN(UNION,check_disjoint_domain)(entry->data, part) < 0)
+ goto error;
+ entry->data = FN(PART,union_add_)(entry->data,
+ FN(PART,copy)(part));
+ if (!entry->data)
+ goto error;
+ empty = FN(PART,IS_ZERO)(part);
+ if (empty < 0)
+ goto error;
+ if (empty)
+ u = FN(UNION,remove_part_entry)(u, entry);
+ FN(PART,free)(part);
+ }
+
+ return u;
+error:
+ FN(PART,free)(part);
+ FN(UNION,free)(u);
+ return NULL;
+}
+
+/* Add "part" to "u", where "u" is assumed not to already have
+ * a part that is defined on the same space as "part".
+ */
+__isl_give UNION *FN(FN(UNION,add),BASE)(__isl_take UNION *u,
+ __isl_take PART *part)
+{
+ return FN(UNION,add_part_generic)(u, part, 1);
+}
+
+/* Allocate a UNION with the same type (if any) and the same size as "u" and
+ * with space "space".
+ */
+static __isl_give UNION *FN(UNION,alloc_same_size_on_space)(__isl_keep UNION *u,
+ __isl_take isl_space *space)
+{
+ if (!u)
+ goto error;
+ return FN(UNION,alloc)(space OPT_TYPE_ARG(u->), u->table.n);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Allocate a UNION with the same space, the same type (if any) and
+ * the same size as "u".
+ */
+static __isl_give UNION *FN(UNION,alloc_same_size)(__isl_keep UNION *u)
+{
+ return FN(UNION,alloc_same_size_on_space)(u, FN(UNION,get_space)(u));
+}
+
+/* Data structure that specifies how isl_union_*_transform
+ * should modify the base expressions in the union expression.
+ *
+ * If "inplace" is set, then the base expression in the input union
+ * are modified in place. This means that "fn" should not
+ * change the meaning of the union or that the union only
+ * has a single reference.
+ * If "space" is not NULL, then a new union is created in this space.
+ * If "filter" is not NULL, then only the base expressions that satisfy "filter"
+ * are taken into account.
+ * "filter_user" is passed as the second argument to "filter".
+ * If "fn" it not NULL, then it is applied to each entry in the input.
+ * "fn_user" is passed as the second argument to "fn".
+ */
+S(UNION,transform_control) {
+ int inplace;
+ isl_space *space;
+ isl_bool (*filter)(__isl_keep PART *part, void *user);
+ void *filter_user;
+ __isl_give PART *(*fn)(__isl_take PART *part, void *user);
+ void *fn_user;
+};
+
+/* Internal data structure for isl_union_*_transform_space.
+ * "control" specifies how the base expressions should be modified.
+ * "res" collects the results (if control->inplace is not set).
+ */
+S(UNION,transform_data)
+{
+ S(UNION,transform_control) *control;
+ UNION *res;
+};
+
+/* Apply control->fn to "part" and add the result to data->res or
+ * place it back into the input union if control->inplace is set.
+ */
+static isl_stat FN(UNION,transform_entry)(void **entry, void *user)
+{
+ S(UNION,transform_data) *data = (S(UNION,transform_data) *)user;
+ S(UNION,transform_control) *control = data->control;
+ PART *part = *entry;
+
+ if (control->filter) {
+ isl_bool handle;
+
+ handle = control->filter(part, control->filter_user);
+ if (handle < 0)
+ return isl_stat_error;
+ if (!handle)
+ return isl_stat_ok;
+ }
+
+ if (!control->inplace)
+ part = FN(PART,copy)(part);
+ if (control->fn)
+ part = control->fn(part, control->fn_user);
+ if (control->inplace)
+ *entry = part;
+ else
+ data->res = FN(FN(UNION,add),BASE)(data->res, part);
+ if (!part || !data->res)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Return a UNION that is obtained by modifying "u" according to "control".
+ */
+static __isl_give UNION *FN(UNION,transform)(__isl_take UNION *u,
+ S(UNION,transform_control) *control)
+{
+ S(UNION,transform_data) data = { control };
+ isl_space *space;
+
+ if (control->inplace) {
+ data.res = u;
+ } else {
+ if (control->space)
+ space = isl_space_copy(control->space);
+ else
+ space = FN(UNION,get_space)(u);
+ data.res = FN(UNION,alloc_same_size_on_space)(u, space);
+ }
+ if (FN(UNION,foreach_inplace)(u, &FN(UNION,transform_entry), &data) < 0)
+ data.res = FN(UNION,free)(data.res);
+ if (!control->inplace)
+ FN(UNION,free)(u);
+ return data.res;
+}
+
+/* Return a UNION living in "space" that is otherwise obtained by modifying "u"
+ * according to "control".
+ */
+static __isl_give UNION *FN(UNION,transform_space)(__isl_take UNION *u,
+ __isl_take isl_space *space, S(UNION,transform_control) *control)
+{
+ if (!space)
+ return FN(UNION,free)(u);
+ control->space = space;
+ u = FN(UNION,transform)(u, control);
+ isl_space_free(space);
+ return u;
+}
+
+/* Update "u" by applying "fn" to each entry.
+ * This operation is assumed not to change the number of entries nor
+ * the spaces of the entries.
+ *
+ * If there is only one reference to "u", then change "u" inplace.
+ * Otherwise, create a new UNION from "u" and discard the original.
+ */
+static __isl_give UNION *FN(UNION,transform_inplace)(__isl_take UNION *u,
+ __isl_give PART *(*fn)(__isl_take PART *part, void *user), void *user)
+{
+ S(UNION,transform_control) control = { .fn = fn, .fn_user = user };
+ isl_bool single_ref;
+
+ single_ref = FN(UNION,has_single_reference)(u);
+ if (single_ref < 0)
+ return FN(UNION,free)(u);
+ if (single_ref)
+ control.inplace = 1;
+ return FN(UNION,transform)(u, &control);
+}
+
+/* An isl_union_*_transform callback for use in isl_union_*_dup
+ * that simply returns "part".
+ */
+static __isl_give PART *FN(UNION,copy_part)(__isl_take PART *part, void *user)
+{
+ return part;
+}
+
+__isl_give UNION *FN(UNION,dup)(__isl_keep UNION *u)
+{
+ S(UNION,transform_control) control = { .fn = &FN(UNION,copy_part) };
+
+ u = FN(UNION,copy)(u);
+ return FN(UNION,transform)(u, &control);
+}
+
+__isl_give UNION *FN(UNION,cow)(__isl_take UNION *u)
+{
+ if (!u)
+ return NULL;
+
+ if (u->ref == 1)
+ return u;
+ u->ref--;
+ return FN(UNION,dup)(u);
+}
+
+__isl_null UNION *FN(UNION,free)(__isl_take UNION *u)
+{
+ if (!u)
+ return NULL;
+
+ if (--u->ref > 0)
+ return NULL;
+
+ isl_hash_table_foreach(u->space->ctx, &u->table,
+ &FN(UNION,free_u_entry), NULL);
+ isl_hash_table_clear(&u->table);
+ isl_space_free(u->space);
+ free(u);
+ return NULL;
+}
+
+static __isl_give PART *FN(UNION,align_entry)(__isl_take PART *part, void *user)
+{
+ isl_reordering *exp = user;
+
+ exp = isl_reordering_extend_space(isl_reordering_copy(exp),
+ FN(PART,get_domain_space)(part));
+ return FN(PART,realign_domain)(part, exp);
+}
+
+/* Reorder the parameters of "u" according to the given reordering.
+ */
+static __isl_give UNION *FN(UNION,realign_domain)(__isl_take UNION *u,
+ __isl_take isl_reordering *r)
+{
+ S(UNION,transform_control) control = {
+ .fn = &FN(UNION,align_entry),
+ .fn_user = r,
+ };
+ isl_space *space;
+
+ if (!u || !r)
+ goto error;
+
+ space = isl_reordering_get_space(r);
+ u = FN(UNION,transform_space)(u, space, &control);
+ isl_reordering_free(r);
+ return u;
+error:
+ FN(UNION,free)(u);
+ isl_reordering_free(r);
+ return NULL;
+}
+
+/* Align the parameters of "u" to those of "model".
+ */
+__isl_give UNION *FN(UNION,align_params)(__isl_take UNION *u,
+ __isl_take isl_space *model)
+{
+ isl_bool equal_params;
+ isl_reordering *r;
+
+ if (!u || !model)
+ goto error;
+
+ equal_params = isl_space_has_equal_params(u->space, model);
+ if (equal_params < 0)
+ goto error;
+ if (equal_params) {
+ isl_space_free(model);
+ return u;
+ }
+
+ r = isl_parameter_alignment_reordering(u->space, model);
+ isl_space_free(model);
+
+ return FN(UNION,realign_domain)(u, r);
+error:
+ isl_space_free(model);
+ FN(UNION,free)(u);
+ return NULL;
+}
+
+/* Add "part" to *u, taking the union sum if "u" already has
+ * a part defined on the same space as "part".
+ */
+static isl_stat FN(UNION,union_add_part)(__isl_take PART *part, void *user)
+{
+ UNION **u = (UNION **)user;
+
+ *u = FN(UNION,add_part_generic)(*u, part, 0);
+
+ return isl_stat_ok;
+}
+
+/* Compute the sum of "u1" and "u2" on the union of their domains,
+ * with the actual sum on the shared domain and
+ * the defined expression on the symmetric difference of the domains.
+ *
+ * This is an internal function that is exposed under different
+ * names depending on whether the base expressions have a zero default
+ * value.
+ * If they do, then this function is called "add".
+ * Otherwise, it is called "union_add".
+ */
+static __isl_give UNION *FN(UNION,union_add_)(__isl_take UNION *u1,
+ __isl_take UNION *u2)
+{
+ u1 = FN(UNION,align_params)(u1, FN(UNION,get_space)(u2));
+ u2 = FN(UNION,align_params)(u2, FN(UNION,get_space)(u1));
+
+ u1 = FN(UNION,cow)(u1);
+
+ if (!u1 || !u2)
+ goto error;
+
+ if (FN(FN(UNION,foreach),BASE)(u2, &FN(UNION,union_add_part), &u1) < 0)
+ goto error;
+
+ FN(UNION,free)(u2);
+
+ return u1;
+error:
+ FN(UNION,free)(u1);
+ FN(UNION,free)(u2);
+ return NULL;
+}
+
+__isl_give UNION *FN(FN(UNION,from),BASE)(__isl_take PART *part)
+{
+ isl_space *space;
+ UNION *u;
+
+ if (!part)
+ return NULL;
+
+ space = FN(PART,get_space)(part);
+ space = isl_space_drop_dims(space, isl_dim_in, 0,
+ isl_space_dim(space, isl_dim_in));
+ space = isl_space_drop_dims(space, isl_dim_out, 0,
+ isl_space_dim(space, isl_dim_out));
+ u = FN(UNION,ZERO)(space OPT_TYPE_ARG(part->));
+ u = FN(FN(UNION,add),BASE)(u, part);
+
+ return u;
+}
+
+/* This function performs the same operation as isl_union_pw_*_from_pw_*,
+ * but is considered as a function on an isl_pw_* when exported.
+ */
+__isl_give UNION *FN(FN(PART,to_union),BASE)(__isl_take PART *part)
+{
+ return FN(FN(UNION,from),BASE)(part);
+}
+
+S(UNION,match_bin_data) {
+ UNION *u2;
+ UNION *res;
+ __isl_give PART *(*fn)(__isl_take PART *, __isl_take PART *);
+};
+
+/* Check if data->u2 has an element living in the same space as "part".
+ * If so, call data->fn on the two elements and add the result to
+ * data->res.
+ */
+static isl_stat FN(UNION,match_bin_entry)(__isl_take PART *part, void *user)
+{
+ S(UNION,match_bin_data) *data = user;
+ struct isl_hash_table_entry *entry2;
+ isl_space *space;
+ PART *part2;
+
+ space = FN(PART,get_space)(part);
+ entry2 = FN(UNION,find_part_entry)(data->u2, space, 0);
+ isl_space_free(space);
+ if (!entry2)
+ goto error;
+ if (entry2 == isl_hash_table_entry_none) {
+ FN(PART,free)(part);
+ return isl_stat_ok;
+ }
+
+ part2 = entry2->data;
+ if (!isl_space_tuple_is_equal(part->dim, isl_dim_out,
+ part2->dim, isl_dim_out))
+ isl_die(FN(UNION,get_ctx)(data->u2), isl_error_invalid,
+ "entries should have the same range space",
+ goto error);
+
+ part = data->fn(part, FN(PART, copy)(entry2->data));
+
+ data->res = FN(FN(UNION,add),BASE)(data->res, part);
+ if (!data->res)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+error:
+ FN(PART,free)(part);
+ return isl_stat_error;
+}
+
+/* This function is currently only used from isl_polynomial.c
+ * and not from isl_fold.c.
+ */
+static __isl_give UNION *FN(UNION,match_bin_op)(__isl_take UNION *u1,
+ __isl_take UNION *u2,
+ __isl_give PART *(*fn)(__isl_take PART *, __isl_take PART *))
+ __attribute__ ((unused));
+/* For each pair of elements in "u1" and "u2" living in the same space,
+ * call "fn" and collect the results.
+ */
+static __isl_give UNION *FN(UNION,match_bin_op)(__isl_take UNION *u1,
+ __isl_take UNION *u2,
+ __isl_give PART *(*fn)(__isl_take PART *, __isl_take PART *))
+{
+ S(UNION,match_bin_data) data = { NULL, NULL, fn };
+
+ u1 = FN(UNION,align_params)(u1, FN(UNION,get_space)(u2));
+ u2 = FN(UNION,align_params)(u2, FN(UNION,get_space)(u1));
+
+ if (!u1 || !u2)
+ goto error;
+
+ data.u2 = u2;
+ data.res = FN(UNION,alloc_same_size)(u1);
+ if (FN(FN(UNION,foreach),BASE)(u1,
+ &FN(UNION,match_bin_entry), &data) < 0)
+ goto error;
+
+ FN(UNION,free)(u1);
+ FN(UNION,free)(u2);
+ return data.res;
+error:
+ FN(UNION,free)(u1);
+ FN(UNION,free)(u2);
+ FN(UNION,free)(data.res);
+ return NULL;
+}
+
+/* Compute the sum of "u1" and "u2".
+ *
+ * If the base expressions have a default zero value, then the sum
+ * is computed on the union of the domains of "u1" and "u2".
+ * Otherwise, it is computed on their shared domains.
+ */
+__isl_give UNION *FN(UNION,add)(__isl_take UNION *u1, __isl_take UNION *u2)
+{
+#if DEFAULT_IS_ZERO
+ return FN(UNION,union_add_)(u1, u2);
+#else
+ return FN(UNION,match_bin_op)(u1, u2, &FN(PART,add));
+#endif
+}
+
+#ifndef NO_SUB
+/* Subtract "u2" from "u1" and return the result.
+ */
+__isl_give UNION *FN(UNION,sub)(__isl_take UNION *u1, __isl_take UNION *u2)
+{
+ return FN(UNION,match_bin_op)(u1, u2, &FN(PART,sub));
+}
+#endif
+
+S(UNION,any_set_data) {
+ isl_set *set;
+ __isl_give PW *(*fn)(__isl_take PW*, __isl_take isl_set*);
+};
+
+static __isl_give PART *FN(UNION,any_set_entry)(__isl_take PART *part,
+ void *user)
+{
+ S(UNION,any_set_data) *data = user;
+
+ return data->fn(part, isl_set_copy(data->set));
+}
+
+/* Update each element of "u" by calling "fn" on the element and "set".
+ */
+static __isl_give UNION *FN(UNION,any_set_op)(__isl_take UNION *u,
+ __isl_take isl_set *set,
+ __isl_give PW *(*fn)(__isl_take PW*, __isl_take isl_set*))
+{
+ S(UNION,any_set_data) data = { NULL, fn };
+ S(UNION,transform_control) control = {
+ .fn = &FN(UNION,any_set_entry),
+ .fn_user = &data,
+ };
+
+ u = FN(UNION,align_params)(u, isl_set_get_space(set));
+ set = isl_set_align_params(set, FN(UNION,get_space)(u));
+
+ if (!u || !set)
+ goto error;
+
+ data.set = set;
+ u = FN(UNION,transform)(u, &control);
+ isl_set_free(set);
+ return u;
+error:
+ FN(UNION,free)(u);
+ isl_set_free(set);
+ return NULL;
+}
+
+/* Intersect the domain of "u" with the parameter domain "context".
+ */
+__isl_give UNION *FN(UNION,intersect_params)(__isl_take UNION *u,
+ __isl_take isl_set *set)
+{
+ return FN(UNION,any_set_op)(u, set, &FN(PW,intersect_params));
+}
+
+/* Compute the gist of the domain of "u" with respect to
+ * the parameter domain "context".
+ */
+__isl_give UNION *FN(UNION,gist_params)(__isl_take UNION *u,
+ __isl_take isl_set *set)
+{
+ return FN(UNION,any_set_op)(u, set, &FN(PW,gist_params));
+}
+
+/* Data structure that specifies how isl_union_*_match_domain_op
+ * should combine its arguments.
+ *
+ * If "filter" is not NULL, then only parts that pass the given
+ * filter are considered for matching.
+ * "fn" is applied to each part in the union and each corresponding
+ * set in the union set, i.e., such that the set lives in the same space
+ * as the domain of the part.
+ * If "match_space" is not NULL, then the set extracted from the union set
+ * does not live in the same space as the domain of the part,
+ * but rather in the space that results from calling "match_space"
+ * on this domain space.
+ */
+S(UNION,match_domain_control) {
+ isl_bool (*filter)(__isl_keep PART *part);
+ __isl_give isl_space *(*match_space)(__isl_take isl_space *space);
+ __isl_give PW *(*fn)(__isl_take PW*, __isl_take isl_set*);
+};
+
+S(UNION,match_domain_data) {
+ isl_union_set *uset;
+ UNION *res;
+ S(UNION,match_domain_control) *control;
+};
+
+/* Find the set in data->uset that lives in the same space as the domain
+ * of "part" (ignoring parameters), apply data->fn to *entry and this set
+ * (if any), and add the result to data->res.
+ */
+static isl_stat FN(UNION,match_domain_entry)(__isl_take PART *part, void *user)
+{
+ S(UNION,match_domain_data) *data = user;
+ struct isl_hash_table_entry *entry2;
+ isl_space *space;
+
+ if (data->control->filter) {
+ isl_bool pass = data->control->filter(part);
+ if (pass < 0 || !pass) {
+ FN(PART,free)(part);
+ return pass < 0 ? isl_stat_error : isl_stat_ok;
+ }
+ }
+
+ space = FN(PART,get_domain_space)(part);
+ if (data->control->match_space)
+ space = data->control->match_space(space);
+ entry2 = isl_union_set_find_entry(data->uset, space, 0);
+ isl_space_free(space);
+ if (!entry2 || entry2 == isl_hash_table_entry_none) {
+ FN(PART,free)(part);
+ return isl_stat_non_null(entry2);
+ }
+
+ part = data->control->fn(part, isl_set_copy(entry2->data));
+
+ data->res = FN(FN(UNION,add),BASE)(data->res, part);
+ if (!data->res)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Combine "u" and "uset" according to "control"
+ * and collect the results.
+ */
+static __isl_give UNION *FN(UNION,match_domain_op)(__isl_take UNION *u,
+ __isl_take isl_union_set *uset, S(UNION,match_domain_control) *control)
+{
+ S(UNION,match_domain_data) data = { NULL, NULL, control };
+
+ if (!u || !uset)
+ goto error;
+
+ data.uset = uset;
+ data.res = FN(UNION,alloc_same_size)(u);
+ if (FN(FN(UNION,foreach),BASE)(u,
+ &FN(UNION,match_domain_entry), &data) < 0)
+ goto error;
+
+ FN(UNION,free)(u);
+ isl_union_set_free(uset);
+ return data.res;
+error:
+ FN(UNION,free)(u);
+ isl_union_set_free(uset);
+ FN(UNION,free)(data.res);
+ return NULL;
+}
+
+/* Intersect the domain of "u" with "uset".
+ * If "uset" is a parameters domain, then intersect the parameter
+ * domain of "u" with this set.
+ */
+__isl_give UNION *FN(UNION,intersect_domain_union_set)(__isl_take UNION *u,
+ __isl_take isl_union_set *uset)
+{
+ S(UNION,match_domain_control) control = {
+ .fn = &FN(PW,intersect_domain),
+ };
+
+ if (isl_union_set_is_params(uset))
+ return FN(UNION,intersect_params)(u,
+ isl_set_from_union_set(uset));
+ return FN(UNION,match_domain_op)(u, uset, &control);
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give UNION *FN(UNION,intersect_domain)(__isl_take UNION *u,
+ __isl_take isl_union_set *uset)
+{
+ return FN(UNION,intersect_domain_union_set)(u, uset);
+}
+
+/* Return true if this part should be kept.
+ *
+ * In particular, it should be kept if its domain space
+ * corresponds to "space".
+ */
+static isl_bool FN(UNION,select_entry)(__isl_keep PART *part, void *user)
+{
+ isl_space *space = user;
+
+ return FN(PW,has_domain_space_tuples)(part, space);
+}
+
+/* Remove any not element in "space" from the domain of "u".
+ *
+ * In particular, select any part of the function defined
+ * on this domain space.
+ */
+__isl_give UNION *FN(UNION,intersect_domain_space)(__isl_take UNION *u,
+ __isl_take isl_space *space)
+{
+ S(UNION,transform_control) control = {
+ .filter = &FN(UNION,select_entry),
+ .filter_user = space,
+ };
+
+ u = FN(UNION,transform)(u, &control);
+ isl_space_free(space);
+ return u;
+}
+
+/* Is the domain of "pw" a wrapped relation?
+ */
+static isl_bool FN(PW,domain_is_wrapping)(__isl_keep PW *pw)
+{
+ return isl_space_domain_is_wrapping(FN(PW,peek_space)(pw));
+}
+
+/* Intersect the domain of the wrapped relation inside the domain of "u"
+ * with "uset".
+ */
+__isl_give UNION *FN(UNION,intersect_domain_wrapped_domain)(__isl_take UNION *u,
+ __isl_take isl_union_set *uset)
+{
+ S(UNION,match_domain_control) control = {
+ .filter = &FN(PART,domain_is_wrapping),
+ .match_space = &isl_space_factor_domain,
+ .fn = &FN(PW,intersect_domain_wrapped_domain),
+ };
+
+ return FN(UNION,match_domain_op)(u, uset, &control);
+}
+
+/* Intersect the range of the wrapped relation inside the domain of "u"
+ * with "uset".
+ */
+__isl_give UNION *FN(UNION,intersect_domain_wrapped_range)(__isl_take UNION *u,
+ __isl_take isl_union_set *uset)
+{
+ S(UNION,match_domain_control) control = {
+ .filter = &FN(PART,domain_is_wrapping),
+ .match_space = &isl_space_factor_range,
+ .fn = &FN(PW,intersect_domain_wrapped_range),
+ };
+
+ return FN(UNION,match_domain_op)(u, uset, &control);
+}
+
+/* Take the set (which may be empty) in data->uset that lives
+ * in the same space as the domain of "pw", subtract it from the domain
+ * of "part" and return the result.
+ */
+static __isl_give PART *FN(UNION,subtract_domain_entry)(__isl_take PART *part,
+ void *user)
+{
+ isl_union_set *uset = user;
+ isl_space *space;
+ isl_set *set;
+
+ space = FN(PART,get_domain_space)(part);
+ set = isl_union_set_extract_set(uset, space);
+ return FN(PART,subtract_domain)(part, set);
+}
+
+/* Subtract "uset" from the domain of "u".
+ */
+__isl_give UNION *FN(UNION,subtract_domain_union_set)(__isl_take UNION *u,
+ __isl_take isl_union_set *uset)
+{
+ S(UNION,transform_control) control = {
+ .fn = &FN(UNION,subtract_domain_entry),
+ .fn_user = uset,
+ };
+
+ u = FN(UNION,transform)(u, &control);
+ isl_union_set_free(uset);
+ return u;
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give UNION *FN(UNION,subtract_domain)(__isl_take UNION *u,
+ __isl_take isl_union_set *uset)
+{
+ return FN(UNION,subtract_domain_union_set)(u, uset);
+}
+
+/* Return true if this part should be kept.
+ *
+ * In particular, it should be kept if its domain space
+ * does not correspond to "space".
+ */
+static isl_bool FN(UNION,filter_out_entry)(__isl_keep PART *part, void *user)
+{
+ isl_space *space = user;
+
+ return isl_bool_not(FN(PW,has_domain_space_tuples)(part, space));
+}
+
+/* Remove any element in "space" from the domain of "u".
+ *
+ * In particular, filter out any part of the function defined
+ * on this domain space.
+ */
+__isl_give UNION *FN(UNION,subtract_domain_space)(__isl_take UNION *u,
+ __isl_take isl_space *space)
+{
+ S(UNION,transform_control) control = {
+ .filter = &FN(UNION,filter_out_entry),
+ .filter_user = space,
+ };
+
+ u = FN(UNION,transform)(u, &control);
+ isl_space_free(space);
+ return u;
+}
+
+__isl_give UNION *FN(UNION,gist)(__isl_take UNION *u,
+ __isl_take isl_union_set *uset)
+{
+ S(UNION,match_domain_control) control = {
+ .fn = &FN(PW,gist),
+ };
+
+ if (isl_union_set_is_params(uset))
+ return FN(UNION,gist_params)(u, isl_set_from_union_set(uset));
+ return FN(UNION,match_domain_op)(u, uset, &control);
+}
+
+/* Coalesce an entry in a UNION. Coalescing is performed in-place.
+ * Since the UNION may have several references, the entry is only
+ * replaced if the coalescing is successful.
+ */
+static isl_stat FN(UNION,coalesce_entry)(void **entry, void *user)
+{
+ PART **part_p = (PART **) entry;
+ PART *part;
+
+ part = FN(PART,copy)(*part_p);
+ part = FN(PW,coalesce)(part);
+ if (!part)
+ return isl_stat_error;
+ FN(PART,free)(*part_p);
+ *part_p = part;
+
+ return isl_stat_ok;
+}
+
+__isl_give UNION *FN(UNION,coalesce)(__isl_take UNION *u)
+{
+ if (FN(UNION,foreach_inplace)(u, &FN(UNION,coalesce_entry), NULL) < 0)
+ goto error;
+
+ return u;
+error:
+ FN(UNION,free)(u);
+ return NULL;
+}
+
+static isl_stat FN(UNION,domain_entry)(__isl_take PART *part, void *user)
+{
+ isl_union_set **uset = (isl_union_set **)user;
+
+ *uset = isl_union_set_add_set(*uset, FN(PART,domain)(part));
+
+ return isl_stat_ok;
+}
+
+__isl_give isl_union_set *FN(UNION,domain)(__isl_take UNION *u)
+{
+ isl_union_set *uset;
+
+ uset = isl_union_set_empty(FN(UNION,get_space)(u));
+ if (FN(FN(UNION,foreach),BASE)(u, &FN(UNION,domain_entry), &uset) < 0)
+ goto error;
+
+ FN(UNION,free)(u);
+
+ return uset;
+error:
+ isl_union_set_free(uset);
+ FN(UNION,free)(u);
+ return NULL;
+}
+
+#ifdef HAS_TYPE
+/* Negate the type of "u".
+ */
+static __isl_give UNION *FN(UNION,negate_type)(__isl_take UNION *u)
+{
+ u = FN(UNION,cow)(u);
+ if (!u)
+ return NULL;
+ u->type = isl_fold_type_negate(u->type);
+ return u;
+}
+#else
+/* Negate the type of "u".
+ * Since "u" does not have a type, do nothing.
+ */
+static __isl_give UNION *FN(UNION,negate_type)(__isl_take UNION *u)
+{
+ return u;
+}
+#endif
+
+/* Multiply "part" by the isl_val "user" and return the result.
+ */
+static __isl_give PART *FN(UNION,scale_val_entry)(__isl_take PART *part,
+ void *user)
+{
+ isl_val *v = user;
+
+ return FN(PART,scale_val)(part, isl_val_copy(v));
+}
+
+/* Multiply "u" by "v" and return the result.
+ */
+__isl_give UNION *FN(UNION,scale_val)(__isl_take UNION *u,
+ __isl_take isl_val *v)
+{
+ if (!u || !v)
+ goto error;
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return u;
+ }
+
+ if (DEFAULT_IS_ZERO && u && isl_val_is_zero(v)) {
+ UNION *zero;
+ isl_space *space = FN(UNION,get_space)(u);
+ zero = FN(UNION,ZERO)(space OPT_TYPE_ARG(u->));
+ FN(UNION,free)(u);
+ isl_val_free(v);
+ return zero;
+ }
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational factor", goto error);
+
+ u = FN(UNION,transform_inplace)(u, &FN(UNION,scale_val_entry), v);
+ if (isl_val_is_neg(v))
+ u = FN(UNION,negate_type)(u);
+
+ isl_val_free(v);
+ return u;
+error:
+ isl_val_free(v);
+ FN(UNION,free)(u);
+ return NULL;
+}
+
+/* Divide "part" by the isl_val "user" and return the result.
+ */
+static __isl_give PART *FN(UNION,scale_down_val_entry)(__isl_take PART *part,
+ void *user)
+{
+ isl_val *v = user;
+
+ return FN(PART,scale_down_val)(part, isl_val_copy(v));
+}
+
+/* Divide "u" by "v" and return the result.
+ */
+__isl_give UNION *FN(UNION,scale_down_val)(__isl_take UNION *u,
+ __isl_take isl_val *v)
+{
+ if (!u || !v)
+ goto error;
+ if (isl_val_is_one(v)) {
+ isl_val_free(v);
+ return u;
+ }
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational factor", goto error);
+ if (isl_val_is_zero(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "cannot scale down by zero", goto error);
+
+ u = FN(UNION,transform_inplace)(u, &FN(UNION,scale_down_val_entry), v);
+ if (isl_val_is_neg(v))
+ u = FN(UNION,negate_type)(u);
+
+ isl_val_free(v);
+ return u;
+error:
+ isl_val_free(v);
+ FN(UNION,free)(u);
+ return NULL;
+}
+
+/* Internal data structure for isl_union_*_every_*.
+ *
+ * "test" is the user-specified callback function.
+ * "user" is the user-specified callback function argument.
+ *
+ * "res" is the final result, initialized to isl_bool_true.
+ */
+S(UNION,every_data) {
+ isl_bool (*test)(__isl_keep PW *pw, void *user);
+ void *user;
+
+ isl_bool res;
+};
+
+/* Call data->test on the piecewise expression at *entry,
+ * updating the result in data->res.
+ * Abort if this result is no longer isl_bool_true.
+ */
+static isl_stat FN(UNION,every_entry)(void **entry, void *user)
+{
+ S(UNION,every_data) *data = user;
+ PW *pw = *entry;
+
+ data->res = data->test(pw, data->user);
+ if (data->res < 0 || !data->res)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Does "test" succeed on every piecewise expression in "u"?
+ */
+isl_bool FN(FN(UNION,every),BASE)(__isl_keep UNION *u,
+ isl_bool (*test)(__isl_keep PW *pw, void *user), void *user)
+{
+ S(UNION,every_data) data = { test, user };
+
+ data.res = isl_bool_true;
+ if (FN(UNION,foreach_inplace)(u, &FN(UNION,every_entry), &data) < 0 &&
+ data.res == isl_bool_true)
+ return isl_bool_error;
+
+ return data.res;
+}
+
+S(UNION,plain_is_equal_data)
+{
+ UNION *u2;
+};
+
+static isl_bool FN(UNION,plain_is_equal_el)(__isl_keep PW *pw, void *user)
+{
+ S(UNION,plain_is_equal_data) *data = user;
+ struct isl_hash_table_entry *entry;
+
+ entry = FN(UNION,find_part_entry)(data->u2, pw->dim, 0);
+ if (!entry)
+ return isl_bool_error;
+ if (entry == isl_hash_table_entry_none)
+ return isl_bool_false;
+
+ return FN(PW,plain_is_equal)(pw, entry->data);
+}
+
+isl_bool FN(UNION,plain_is_equal)(__isl_keep UNION *u1, __isl_keep UNION *u2)
+{
+ S(UNION,plain_is_equal_data) data;
+ isl_size n1, n2;
+ isl_bool is_equal;
+
+ if (!u1 || !u2)
+ return isl_bool_error;
+ if (u1 == u2)
+ return isl_bool_true;
+ if (u1->table.n != u2->table.n)
+ return isl_bool_false;
+ n1 = FN(FN(UNION,n),BASE)(u1);
+ n2 = FN(FN(UNION,n),BASE)(u2);
+ if (n1 < 0 || n2 < 0)
+ return isl_bool_error;
+ if (n1 != n2)
+ return isl_bool_false;
+
+ u1 = FN(UNION,copy)(u1);
+ u2 = FN(UNION,copy)(u2);
+ u1 = FN(UNION,align_params)(u1, FN(UNION,get_space)(u2));
+ u2 = FN(UNION,align_params)(u2, FN(UNION,get_space)(u1));
+ if (!u1 || !u2)
+ goto error;
+
+ data.u2 = u2;
+ is_equal = FN(FN(UNION,every),BASE)(u1,
+ &FN(UNION,plain_is_equal_el), &data);
+
+ FN(UNION,free)(u1);
+ FN(UNION,free)(u2);
+
+ return is_equal;
+error:
+ FN(UNION,free)(u1);
+ FN(UNION,free)(u2);
+ return isl_bool_error;
+}
+
+/* An isl_union_*_every_* callback that checks whether "pw"
+ * does not involve any NaNs.
+ */
+static isl_bool FN(UNION,no_nan_el)(__isl_keep PW *pw, void *user)
+{
+ return isl_bool_not(FN(PW,involves_nan)(pw));
+}
+
+/* Does "u" involve any NaNs?
+ */
+isl_bool FN(UNION,involves_nan)(__isl_keep UNION *u)
+{
+ isl_bool no_nan;
+
+ no_nan = FN(FN(UNION,every),BASE)(u, &FN(UNION,no_nan_el), NULL);
+
+ return isl_bool_not(no_nan);
+}
+
+/* Internal data structure for isl_union_*_drop_dims.
+ * type, first and n are passed to isl_*_drop_dims.
+ */
+S(UNION,drop_dims_data) {
+ enum isl_dim_type type;
+ unsigned first;
+ unsigned n;
+};
+
+/* Drop the parameters specified by "data" from "part" and return the result.
+ */
+static __isl_give PART *FN(UNION,drop_dims_entry)(__isl_take PART *part,
+ void *user)
+{
+ S(UNION,drop_dims_data) *data = user;
+
+ return FN(PART,drop_dims)(part, data->type, data->first, data->n);
+}
+
+/* Drop the specified parameters from "u".
+ * That is, type is required to be isl_dim_param.
+ */
+__isl_give UNION *FN(UNION,drop_dims)( __isl_take UNION *u,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ isl_space *space;
+ S(UNION,drop_dims_data) data = { type, first, n };
+ S(UNION,transform_control) control = {
+ .fn = &FN(UNION,drop_dims_entry),
+ .fn_user = &data,
+ };
+
+ if (!u)
+ return NULL;
+
+ if (type != isl_dim_param)
+ isl_die(FN(UNION,get_ctx)(u), isl_error_invalid,
+ "can only project out parameters",
+ return FN(UNION,free)(u));
+
+ space = FN(UNION,get_space)(u);
+ space = isl_space_drop_dims(space, type, first, n);
+ return FN(UNION,transform_space)(u, space, &control);
+}
+
+/* Internal data structure for isl_union_*_set_dim_name.
+ * pos is the position of the parameter that needs to be renamed.
+ * s is the new name.
+ */
+S(UNION,set_dim_name_data) {
+ unsigned pos;
+ const char *s;
+};
+
+/* Change the name of the parameter at position data->pos of "part" to data->s
+ * and return the result.
+ */
+static __isl_give PART *FN(UNION,set_dim_name_entry)(__isl_take PART *part,
+ void *user)
+{
+ S(UNION,set_dim_name_data) *data = user;
+
+ return FN(PART,set_dim_name)(part, isl_dim_param, data->pos, data->s);
+}
+
+/* Change the name of the parameter at position "pos" to "s".
+ * That is, type is required to be isl_dim_param.
+ */
+__isl_give UNION *FN(UNION,set_dim_name)(__isl_take UNION *u,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ S(UNION,set_dim_name_data) data = { pos, s };
+ S(UNION,transform_control) control = {
+ .fn = &FN(UNION,set_dim_name_entry),
+ .fn_user = &data,
+ };
+ isl_space *space;
+
+ if (!u)
+ return NULL;
+
+ if (type != isl_dim_param)
+ isl_die(FN(UNION,get_ctx)(u), isl_error_invalid,
+ "can only set parameter names",
+ return FN(UNION,free)(u));
+
+ space = FN(UNION,get_space)(u);
+ space = isl_space_set_dim_name(space, type, pos, s);
+ return FN(UNION,transform_space)(u, space, &control);
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * of the space of "part" and return the result.
+ */
+static __isl_give PART *FN(UNION,reset_user_entry)(__isl_take PART *part,
+ void *user)
+{
+ return FN(PART,reset_user)(part);
+}
+
+/* Reset the user pointer on all identifiers of parameters and tuples
+ * of the spaces of "u".
+ */
+__isl_give UNION *FN(UNION,reset_user)(__isl_take UNION *u)
+{
+ S(UNION,transform_control) control = {
+ .fn = &FN(UNION,reset_user_entry),
+ };
+ isl_space *space;
+
+ space = FN(UNION,get_space)(u);
+ space = isl_space_reset_user(space);
+ return FN(UNION,transform_space)(u, space, &control);
+}
+
+/* Add the base expression held by "entry" to "list".
+ */
+static isl_stat FN(UNION,add_to_list)(void **entry, void *user)
+{
+ PW *pw = *entry;
+ LIST(PART) **list = user;
+
+ *list = FN(LIST(PART),add)(*list, FN(PART,copy)(pw));
+ if (!*list)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Return a list containing all the base expressions in "u".
+ *
+ * First construct a list of the appropriate size and
+ * then add all the elements.
+ */
+__isl_give LIST(PART) *FN(FN(UNION,get),LIST(BASE))(__isl_keep UNION *u)
+{
+ isl_size n;
+ LIST(PART) *list;
+
+ if (!u)
+ return NULL;
+ n = FN(FN(UNION,n),BASE)(u);
+ if (n < 0)
+ return NULL;
+ list = FN(LIST(PART),alloc)(FN(UNION,get_ctx(u)), n);
+ if (FN(UNION,foreach_inplace)(u, &FN(UNION,add_to_list), &list) < 0)
+ return FN(LIST(PART),free)(list);
+
+ return list;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val.c
new file mode 100644
index 00000000000..3744aa74d08
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val.c
@@ -0,0 +1,1645 @@
+/*
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl_int.h>
+#include <isl_ctx_private.h>
+#include <isl_val_private.h>
+
+#undef EL_BASE
+#define EL_BASE val
+
+#include <isl_list_templ.c>
+#include <isl_list_read_templ.c>
+
+/* Allocate an isl_val object with indeterminate value.
+ */
+__isl_give isl_val *isl_val_alloc(isl_ctx *ctx)
+{
+ isl_val *v;
+
+ v = isl_alloc_type(ctx, struct isl_val);
+ if (!v)
+ return NULL;
+
+ v->ctx = ctx;
+ isl_ctx_ref(ctx);
+ v->ref = 1;
+ isl_int_init(v->n);
+ isl_int_init(v->d);
+
+ return v;
+}
+
+/* Return a reference to an isl_val representing zero.
+ */
+__isl_give isl_val *isl_val_zero(isl_ctx *ctx)
+{
+ return isl_val_int_from_si(ctx, 0);
+}
+
+/* Return a reference to an isl_val representing one.
+ */
+__isl_give isl_val *isl_val_one(isl_ctx *ctx)
+{
+ return isl_val_int_from_si(ctx, 1);
+}
+
+/* Return a reference to an isl_val representing negative one.
+ */
+__isl_give isl_val *isl_val_negone(isl_ctx *ctx)
+{
+ return isl_val_int_from_si(ctx, -1);
+}
+
+/* Return a reference to an isl_val representing NaN.
+ */
+__isl_give isl_val *isl_val_nan(isl_ctx *ctx)
+{
+ isl_val *v;
+
+ v = isl_val_alloc(ctx);
+ if (!v)
+ return NULL;
+
+ isl_int_set_si(v->n, 0);
+ isl_int_set_si(v->d, 0);
+
+ return v;
+}
+
+/* Change "v" into a NaN.
+ */
+__isl_give isl_val *isl_val_set_nan(__isl_take isl_val *v)
+{
+ if (!v)
+ return NULL;
+ if (isl_val_is_nan(v))
+ return v;
+ v = isl_val_cow(v);
+ if (!v)
+ return NULL;
+
+ isl_int_set_si(v->n, 0);
+ isl_int_set_si(v->d, 0);
+
+ return v;
+}
+
+/* Return a reference to an isl_val representing +infinity.
+ */
+__isl_give isl_val *isl_val_infty(isl_ctx *ctx)
+{
+ isl_val *v;
+
+ v = isl_val_alloc(ctx);
+ if (!v)
+ return NULL;
+
+ isl_int_set_si(v->n, 1);
+ isl_int_set_si(v->d, 0);
+
+ return v;
+}
+
+/* Return a reference to an isl_val representing -infinity.
+ */
+__isl_give isl_val *isl_val_neginfty(isl_ctx *ctx)
+{
+ isl_val *v;
+
+ v = isl_val_alloc(ctx);
+ if (!v)
+ return NULL;
+
+ isl_int_set_si(v->n, -1);
+ isl_int_set_si(v->d, 0);
+
+ return v;
+}
+
+/* Return a reference to an isl_val representing the integer "i".
+ */
+__isl_give isl_val *isl_val_int_from_si(isl_ctx *ctx, long i)
+{
+ isl_val *v;
+
+ v = isl_val_alloc(ctx);
+ if (!v)
+ return NULL;
+
+ isl_int_set_si(v->n, i);
+ isl_int_set_si(v->d, 1);
+
+ return v;
+}
+
+/* Change the value of "v" to be equal to the integer "i".
+ */
+__isl_give isl_val *isl_val_set_si(__isl_take isl_val *v, long i)
+{
+ if (!v)
+ return NULL;
+ if (isl_val_is_int(v) && isl_int_cmp_si(v->n, i) == 0)
+ return v;
+ v = isl_val_cow(v);
+ if (!v)
+ return NULL;
+
+ isl_int_set_si(v->n, i);
+ isl_int_set_si(v->d, 1);
+
+ return v;
+}
+
+/* Change the value of "v" to be equal to zero.
+ */
+__isl_give isl_val *isl_val_set_zero(__isl_take isl_val *v)
+{
+ return isl_val_set_si(v, 0);
+}
+
+/* Return a reference to an isl_val representing the unsigned integer "u".
+ */
+__isl_give isl_val *isl_val_int_from_ui(isl_ctx *ctx, unsigned long u)
+{
+ isl_val *v;
+
+ v = isl_val_alloc(ctx);
+ if (!v)
+ return NULL;
+
+ isl_int_set_ui(v->n, u);
+ isl_int_set_si(v->d, 1);
+
+ return v;
+}
+
+/* Return a reference to an isl_val representing the integer "n".
+ */
+__isl_give isl_val *isl_val_int_from_isl_int(isl_ctx *ctx, isl_int n)
+{
+ isl_val *v;
+
+ v = isl_val_alloc(ctx);
+ if (!v)
+ return NULL;
+
+ isl_int_set(v->n, n);
+ isl_int_set_si(v->d, 1);
+
+ return v;
+}
+
+/* Return a reference to an isl_val representing the rational value "n"/"d".
+ * Normalizing the isl_val (if needed) is left to the caller.
+ */
+__isl_give isl_val *isl_val_rat_from_isl_int(isl_ctx *ctx,
+ isl_int n, isl_int d)
+{
+ isl_val *v;
+
+ v = isl_val_alloc(ctx);
+ if (!v)
+ return NULL;
+
+ isl_int_set(v->n, n);
+ isl_int_set(v->d, d);
+
+ return v;
+}
+
+/* Return a new reference to "v".
+ */
+__isl_give isl_val *isl_val_copy(__isl_keep isl_val *v)
+{
+ if (!v)
+ return NULL;
+
+ v->ref++;
+ return v;
+}
+
+/* Return a fresh copy of "val".
+ */
+__isl_give isl_val *isl_val_dup(__isl_keep isl_val *val)
+{
+ isl_val *dup;
+
+ if (!val)
+ return NULL;
+
+ dup = isl_val_alloc(isl_val_get_ctx(val));
+ if (!dup)
+ return NULL;
+
+ isl_int_set(dup->n, val->n);
+ isl_int_set(dup->d, val->d);
+
+ return dup;
+}
+
+/* Return an isl_val that is equal to "val" and that has only
+ * a single reference.
+ */
+__isl_give isl_val *isl_val_cow(__isl_take isl_val *val)
+{
+ if (!val)
+ return NULL;
+
+ if (val->ref == 1)
+ return val;
+ val->ref--;
+ return isl_val_dup(val);
+}
+
+/* Free "v" and return NULL.
+ */
+__isl_null isl_val *isl_val_free(__isl_take isl_val *v)
+{
+ if (!v)
+ return NULL;
+
+ if (--v->ref > 0)
+ return NULL;
+
+ isl_ctx_deref(v->ctx);
+ isl_int_clear(v->n);
+ isl_int_clear(v->d);
+ free(v);
+ return NULL;
+}
+
+/* Extract the numerator of a rational value "v" as an integer.
+ *
+ * If "v" is not a rational value, then the result is undefined.
+ */
+long isl_val_get_num_si(__isl_keep isl_val *v)
+{
+ if (!v)
+ return 0;
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational value", return 0);
+ if (!isl_int_fits_slong(v->n))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "numerator too large", return 0);
+ return isl_int_get_si(v->n);
+}
+
+/* Extract the numerator of a rational value "v" as an isl_int.
+ *
+ * If "v" is not a rational value, then the result is undefined.
+ */
+isl_stat isl_val_get_num_isl_int(__isl_keep isl_val *v, isl_int *n)
+{
+ if (!v)
+ return isl_stat_error;
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational value", return isl_stat_error);
+ isl_int_set(*n, v->n);
+ return isl_stat_ok;
+}
+
+/* Extract the denominator of a rational value "v" as an integer.
+ *
+ * If "v" is not a rational value, then the result is undefined.
+ */
+long isl_val_get_den_si(__isl_keep isl_val *v)
+{
+ if (!v)
+ return 0;
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational value", return 0);
+ if (!isl_int_fits_slong(v->d))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "denominator too large", return 0);
+ return isl_int_get_si(v->d);
+}
+
+/* Extract the denominator of a rational value "v" as an isl_val.
+ *
+ * If "v" is not a rational value, then the result is undefined.
+ */
+__isl_give isl_val *isl_val_get_den_val(__isl_keep isl_val *v)
+{
+ if (!v)
+ return NULL;
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational value", return NULL);
+ return isl_val_int_from_isl_int(isl_val_get_ctx(v), v->d);
+}
+
+/* Return an approximation of "v" as a double.
+ */
+double isl_val_get_d(__isl_keep isl_val *v)
+{
+ if (!v)
+ return 0;
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational value", return 0);
+ return isl_int_get_d(v->n) / isl_int_get_d(v->d);
+}
+
+/* Return the isl_ctx to which "val" belongs.
+ */
+isl_ctx *isl_val_get_ctx(__isl_keep isl_val *val)
+{
+ return val ? val->ctx : NULL;
+}
+
+/* Return a hash value that digests "val".
+ */
+uint32_t isl_val_get_hash(__isl_keep isl_val *val)
+{
+ uint32_t hash;
+
+ if (!val)
+ return 0;
+
+ hash = isl_hash_init();
+ hash = isl_int_hash(val->n, hash);
+ hash = isl_int_hash(val->d, hash);
+
+ return hash;
+}
+
+/* Normalize "v".
+ *
+ * In particular, make sure that the denominator of a rational value
+ * is positive and the numerator and denominator do not have any
+ * common divisors.
+ *
+ * This function should not be called by an external user
+ * since it will only be given normalized values.
+ */
+__isl_give isl_val *isl_val_normalize(__isl_take isl_val *v)
+{
+ isl_ctx *ctx;
+
+ if (!v)
+ return NULL;
+ if (isl_val_is_int(v))
+ return v;
+ if (!isl_val_is_rat(v))
+ return v;
+ if (isl_int_is_neg(v->d)) {
+ isl_int_neg(v->d, v->d);
+ isl_int_neg(v->n, v->n);
+ }
+ ctx = isl_val_get_ctx(v);
+ isl_int_gcd(ctx->normalize_gcd, v->n, v->d);
+ if (isl_int_is_one(ctx->normalize_gcd))
+ return v;
+ isl_int_divexact(v->n, v->n, ctx->normalize_gcd);
+ isl_int_divexact(v->d, v->d, ctx->normalize_gcd);
+ return v;
+}
+
+/* Return the opposite of "v".
+ */
+__isl_give isl_val *isl_val_neg(__isl_take isl_val *v)
+{
+ if (!v)
+ return NULL;
+ if (isl_val_is_nan(v))
+ return v;
+ if (isl_val_is_zero(v))
+ return v;
+
+ v = isl_val_cow(v);
+ if (!v)
+ return NULL;
+ isl_int_neg(v->n, v->n);
+
+ return v;
+}
+
+/* Return the inverse of "v".
+ */
+__isl_give isl_val *isl_val_inv(__isl_take isl_val *v)
+{
+ if (!v)
+ return NULL;
+ if (isl_val_is_nan(v))
+ return v;
+ if (isl_val_is_zero(v)) {
+ isl_ctx *ctx = isl_val_get_ctx(v);
+ isl_val_free(v);
+ return isl_val_nan(ctx);
+ }
+ if (isl_val_is_infty(v) || isl_val_is_neginfty(v)) {
+ isl_ctx *ctx = isl_val_get_ctx(v);
+ isl_val_free(v);
+ return isl_val_zero(ctx);
+ }
+
+ v = isl_val_cow(v);
+ if (!v)
+ return NULL;
+ isl_int_swap(v->n, v->d);
+
+ return isl_val_normalize(v);
+}
+
+/* Return the absolute value of "v".
+ */
+__isl_give isl_val *isl_val_abs(__isl_take isl_val *v)
+{
+ if (!v)
+ return NULL;
+ if (isl_val_is_nan(v))
+ return v;
+ if (isl_val_is_nonneg(v))
+ return v;
+ return isl_val_neg(v);
+}
+
+/* Return the "floor" (greatest integer part) of "v".
+ * That is, return the result of rounding towards -infinity.
+ */
+__isl_give isl_val *isl_val_floor(__isl_take isl_val *v)
+{
+ if (!v)
+ return NULL;
+ if (isl_val_is_int(v))
+ return v;
+ if (!isl_val_is_rat(v))
+ return v;
+
+ v = isl_val_cow(v);
+ if (!v)
+ return NULL;
+ isl_int_fdiv_q(v->n, v->n, v->d);
+ isl_int_set_si(v->d, 1);
+
+ return v;
+}
+
+/* Return the "ceiling" of "v".
+ * That is, return the result of rounding towards +infinity.
+ */
+__isl_give isl_val *isl_val_ceil(__isl_take isl_val *v)
+{
+ if (!v)
+ return NULL;
+ if (isl_val_is_int(v))
+ return v;
+ if (!isl_val_is_rat(v))
+ return v;
+
+ v = isl_val_cow(v);
+ if (!v)
+ return NULL;
+ isl_int_cdiv_q(v->n, v->n, v->d);
+ isl_int_set_si(v->d, 1);
+
+ return v;
+}
+
+/* Truncate "v".
+ * That is, return the result of rounding towards zero.
+ */
+__isl_give isl_val *isl_val_trunc(__isl_take isl_val *v)
+{
+ if (!v)
+ return NULL;
+ if (isl_val_is_int(v))
+ return v;
+ if (!isl_val_is_rat(v))
+ return v;
+
+ v = isl_val_cow(v);
+ if (!v)
+ return NULL;
+ isl_int_tdiv_q(v->n, v->n, v->d);
+ isl_int_set_si(v->d, 1);
+
+ return v;
+}
+
+/* Return 2^v, where v is an integer (that is not too large).
+ */
+__isl_give isl_val *isl_val_pow2(__isl_take isl_val *v)
+{
+ unsigned long exp;
+ int neg;
+
+ v = isl_val_cow(v);
+ if (!v)
+ return NULL;
+ if (!isl_val_is_int(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "can only compute integer powers",
+ return isl_val_free(v));
+ neg = isl_val_is_neg(v);
+ if (neg)
+ isl_int_neg(v->n, v->n);
+ if (!isl_int_fits_ulong(v->n))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "exponent too large", return isl_val_free(v));
+ exp = isl_int_get_ui(v->n);
+ if (neg) {
+ isl_int_mul_2exp(v->d, v->d, exp);
+ isl_int_set_si(v->n, 1);
+ } else {
+ isl_int_mul_2exp(v->n, v->d, exp);
+ }
+
+ return v;
+}
+
+/* This is an alternative name for the function above.
+ */
+__isl_give isl_val *isl_val_2exp(__isl_take isl_val *v)
+{
+ return isl_val_pow2(v);
+}
+
+/* Return the minimum of "v1" and "v2".
+ */
+__isl_give isl_val *isl_val_min(__isl_take isl_val *v1, __isl_take isl_val *v2)
+{
+ if (!v1 || !v2)
+ goto error;
+
+ if (isl_val_is_nan(v1)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_nan(v2)) {
+ isl_val_free(v1);
+ return v2;
+ }
+ if (isl_val_le(v1, v2)) {
+ isl_val_free(v2);
+ return v1;
+ } else {
+ isl_val_free(v1);
+ return v2;
+ }
+error:
+ isl_val_free(v1);
+ isl_val_free(v2);
+ return NULL;
+}
+
+/* Return the maximum of "v1" and "v2".
+ */
+__isl_give isl_val *isl_val_max(__isl_take isl_val *v1, __isl_take isl_val *v2)
+{
+ if (!v1 || !v2)
+ goto error;
+
+ if (isl_val_is_nan(v1)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_nan(v2)) {
+ isl_val_free(v1);
+ return v2;
+ }
+ if (isl_val_ge(v1, v2)) {
+ isl_val_free(v2);
+ return v1;
+ } else {
+ isl_val_free(v1);
+ return v2;
+ }
+error:
+ isl_val_free(v1);
+ isl_val_free(v2);
+ return NULL;
+}
+
+/* Return the sum of "v1" and "v2".
+ */
+__isl_give isl_val *isl_val_add(__isl_take isl_val *v1, __isl_take isl_val *v2)
+{
+ if (!v1 || !v2)
+ goto error;
+ if (isl_val_is_nan(v1)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_nan(v2)) {
+ isl_val_free(v1);
+ return v2;
+ }
+ if ((isl_val_is_infty(v1) && isl_val_is_neginfty(v2)) ||
+ (isl_val_is_neginfty(v1) && isl_val_is_infty(v2))) {
+ isl_val_free(v2);
+ return isl_val_set_nan(v1);
+ }
+ if (isl_val_is_infty(v1) || isl_val_is_neginfty(v1)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_infty(v2) || isl_val_is_neginfty(v2)) {
+ isl_val_free(v1);
+ return v2;
+ }
+ if (isl_val_is_zero(v1)) {
+ isl_val_free(v1);
+ return v2;
+ }
+ if (isl_val_is_zero(v2)) {
+ isl_val_free(v2);
+ return v1;
+ }
+
+ v1 = isl_val_cow(v1);
+ if (!v1)
+ goto error;
+ if (isl_val_is_int(v1) && isl_val_is_int(v2))
+ isl_int_add(v1->n, v1->n, v2->n);
+ else {
+ if (isl_int_eq(v1->d, v2->d))
+ isl_int_add(v1->n, v1->n, v2->n);
+ else {
+ isl_int_mul(v1->n, v1->n, v2->d);
+ isl_int_addmul(v1->n, v2->n, v1->d);
+ isl_int_mul(v1->d, v1->d, v2->d);
+ }
+ v1 = isl_val_normalize(v1);
+ }
+ isl_val_free(v2);
+ return v1;
+error:
+ isl_val_free(v1);
+ isl_val_free(v2);
+ return NULL;
+}
+
+/* Return the sum of "v1" and "v2".
+ */
+__isl_give isl_val *isl_val_add_ui(__isl_take isl_val *v1, unsigned long v2)
+{
+ if (!v1)
+ return NULL;
+ if (!isl_val_is_rat(v1))
+ return v1;
+ if (v2 == 0)
+ return v1;
+ v1 = isl_val_cow(v1);
+ if (!v1)
+ return NULL;
+
+ isl_int_addmul_ui(v1->n, v1->d, v2);
+
+ return v1;
+}
+
+/* Subtract "v2" from "v1".
+ */
+__isl_give isl_val *isl_val_sub(__isl_take isl_val *v1, __isl_take isl_val *v2)
+{
+ if (!v1 || !v2)
+ goto error;
+ if (isl_val_is_nan(v1)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_nan(v2)) {
+ isl_val_free(v1);
+ return v2;
+ }
+ if ((isl_val_is_infty(v1) && isl_val_is_infty(v2)) ||
+ (isl_val_is_neginfty(v1) && isl_val_is_neginfty(v2))) {
+ isl_val_free(v2);
+ return isl_val_set_nan(v1);
+ }
+ if (isl_val_is_infty(v1) || isl_val_is_neginfty(v1)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_infty(v2) || isl_val_is_neginfty(v2)) {
+ isl_val_free(v1);
+ return isl_val_neg(v2);
+ }
+ if (isl_val_is_zero(v2)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_zero(v1)) {
+ isl_val_free(v1);
+ return isl_val_neg(v2);
+ }
+
+ v1 = isl_val_cow(v1);
+ if (!v1)
+ goto error;
+ if (isl_val_is_int(v1) && isl_val_is_int(v2))
+ isl_int_sub(v1->n, v1->n, v2->n);
+ else {
+ if (isl_int_eq(v1->d, v2->d))
+ isl_int_sub(v1->n, v1->n, v2->n);
+ else {
+ isl_int_mul(v1->n, v1->n, v2->d);
+ isl_int_submul(v1->n, v2->n, v1->d);
+ isl_int_mul(v1->d, v1->d, v2->d);
+ }
+ v1 = isl_val_normalize(v1);
+ }
+ isl_val_free(v2);
+ return v1;
+error:
+ isl_val_free(v1);
+ isl_val_free(v2);
+ return NULL;
+}
+
+/* Subtract "v2" from "v1".
+ */
+__isl_give isl_val *isl_val_sub_ui(__isl_take isl_val *v1, unsigned long v2)
+{
+ if (!v1)
+ return NULL;
+ if (!isl_val_is_rat(v1))
+ return v1;
+ if (v2 == 0)
+ return v1;
+ v1 = isl_val_cow(v1);
+ if (!v1)
+ return NULL;
+
+ isl_int_submul_ui(v1->n, v1->d, v2);
+
+ return v1;
+}
+
+/* Return the product of "v1" and "v2".
+ */
+__isl_give isl_val *isl_val_mul(__isl_take isl_val *v1, __isl_take isl_val *v2)
+{
+ if (!v1 || !v2)
+ goto error;
+ if (isl_val_is_nan(v1)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_nan(v2)) {
+ isl_val_free(v1);
+ return v2;
+ }
+ if ((!isl_val_is_rat(v1) && isl_val_is_zero(v2)) ||
+ (isl_val_is_zero(v1) && !isl_val_is_rat(v2))) {
+ isl_val_free(v2);
+ return isl_val_set_nan(v1);
+ }
+ if (isl_val_is_zero(v1)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_zero(v2)) {
+ isl_val_free(v1);
+ return v2;
+ }
+ if (isl_val_is_infty(v1) || isl_val_is_neginfty(v1)) {
+ if (isl_val_is_neg(v2))
+ v1 = isl_val_neg(v1);
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_infty(v2) || isl_val_is_neginfty(v2)) {
+ if (isl_val_is_neg(v1))
+ v2 = isl_val_neg(v2);
+ isl_val_free(v1);
+ return v2;
+ }
+
+ v1 = isl_val_cow(v1);
+ if (!v1)
+ goto error;
+ if (isl_val_is_int(v1) && isl_val_is_int(v2))
+ isl_int_mul(v1->n, v1->n, v2->n);
+ else {
+ isl_int_mul(v1->n, v1->n, v2->n);
+ isl_int_mul(v1->d, v1->d, v2->d);
+ v1 = isl_val_normalize(v1);
+ }
+ isl_val_free(v2);
+ return v1;
+error:
+ isl_val_free(v1);
+ isl_val_free(v2);
+ return NULL;
+}
+
+/* Return the product of "v1" and "v2".
+ *
+ * This is a private copy of isl_val_mul for use in the generic
+ * isl_multi_*_scale_val instantiated for isl_val.
+ */
+__isl_give isl_val *isl_val_scale_val(__isl_take isl_val *v1,
+ __isl_take isl_val *v2)
+{
+ return isl_val_mul(v1, v2);
+}
+
+/* Return the product of "v1" and "v2".
+ */
+__isl_give isl_val *isl_val_mul_ui(__isl_take isl_val *v1, unsigned long v2)
+{
+ if (!v1)
+ return NULL;
+ if (isl_val_is_nan(v1))
+ return v1;
+ if (!isl_val_is_rat(v1)) {
+ if (v2 == 0)
+ v1 = isl_val_set_nan(v1);
+ return v1;
+ }
+ if (v2 == 1)
+ return v1;
+ v1 = isl_val_cow(v1);
+ if (!v1)
+ return NULL;
+
+ isl_int_mul_ui(v1->n, v1->n, v2);
+
+ return isl_val_normalize(v1);
+}
+
+/* Divide "v1" by "v2".
+ */
+__isl_give isl_val *isl_val_div(__isl_take isl_val *v1, __isl_take isl_val *v2)
+{
+ if (!v1 || !v2)
+ goto error;
+ if (isl_val_is_nan(v1)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_nan(v2)) {
+ isl_val_free(v1);
+ return v2;
+ }
+ if (isl_val_is_zero(v2) ||
+ (!isl_val_is_rat(v1) && !isl_val_is_rat(v2))) {
+ isl_val_free(v2);
+ return isl_val_set_nan(v1);
+ }
+ if (isl_val_is_zero(v1)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_infty(v1) || isl_val_is_neginfty(v1)) {
+ if (isl_val_is_neg(v2))
+ v1 = isl_val_neg(v1);
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_infty(v2) || isl_val_is_neginfty(v2)) {
+ isl_val_free(v2);
+ return isl_val_set_zero(v1);
+ }
+
+ v1 = isl_val_cow(v1);
+ if (!v1)
+ goto error;
+ if (isl_val_is_int(v2)) {
+ isl_int_mul(v1->d, v1->d, v2->n);
+ v1 = isl_val_normalize(v1);
+ } else {
+ isl_int_mul(v1->d, v1->d, v2->n);
+ isl_int_mul(v1->n, v1->n, v2->d);
+ v1 = isl_val_normalize(v1);
+ }
+ isl_val_free(v2);
+ return v1;
+error:
+ isl_val_free(v1);
+ isl_val_free(v2);
+ return NULL;
+}
+
+/* Divide "v1" by "v2".
+ */
+__isl_give isl_val *isl_val_div_ui(__isl_take isl_val *v1, unsigned long v2)
+{
+ if (!v1)
+ return NULL;
+ if (isl_val_is_nan(v1))
+ return v1;
+ if (v2 == 0)
+ return isl_val_set_nan(v1);
+ if (v2 == 1)
+ return v1;
+ if (isl_val_is_zero(v1))
+ return v1;
+ if (isl_val_is_infty(v1) || isl_val_is_neginfty(v1))
+ return v1;
+ v1 = isl_val_cow(v1);
+ if (!v1)
+ return NULL;
+
+ isl_int_mul_ui(v1->d, v1->d, v2);
+
+ return isl_val_normalize(v1);
+}
+
+/* Divide "v1" by "v2".
+ *
+ * This is a private copy of isl_val_div for use in the generic
+ * isl_multi_*_scale_down_val instantiated for isl_val.
+ */
+__isl_give isl_val *isl_val_scale_down_val(__isl_take isl_val *v1,
+ __isl_take isl_val *v2)
+{
+ return isl_val_div(v1, v2);
+}
+
+/* Given two integer values "v1" and "v2", check if "v1" is divisible by "v2".
+ */
+isl_bool isl_val_is_divisible_by(__isl_keep isl_val *v1, __isl_keep isl_val *v2)
+{
+ if (!v1 || !v2)
+ return isl_bool_error;
+
+ if (!isl_val_is_int(v1) || !isl_val_is_int(v2))
+ isl_die(isl_val_get_ctx(v1), isl_error_invalid,
+ "expecting two integers", return isl_bool_error);
+
+ return isl_bool_ok(isl_int_is_divisible_by(v1->n, v2->n));
+}
+
+/* Given two integer values "v1" and "v2", return the residue of "v1"
+ * modulo "v2".
+ */
+__isl_give isl_val *isl_val_mod(__isl_take isl_val *v1, __isl_take isl_val *v2)
+{
+ if (!v1 || !v2)
+ goto error;
+ if (!isl_val_is_int(v1) || !isl_val_is_int(v2))
+ isl_die(isl_val_get_ctx(v1), isl_error_invalid,
+ "expecting two integers", goto error);
+ if (isl_val_is_nonneg(v1) && isl_val_lt(v1, v2)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ v1 = isl_val_cow(v1);
+ if (!v1)
+ goto error;
+ isl_int_fdiv_r(v1->n, v1->n, v2->n);
+ isl_val_free(v2);
+ return v1;
+error:
+ isl_val_free(v1);
+ isl_val_free(v2);
+ return NULL;
+}
+
+/* Given two integer values "v1" and "v2", return the residue of "v1"
+ * modulo "v2".
+ *
+ * This is a private copy of isl_val_mod for use in the generic
+ * isl_multi_*_mod_multi_val instantiated for isl_val.
+ */
+__isl_give isl_val *isl_val_mod_val(__isl_take isl_val *v1,
+ __isl_take isl_val *v2)
+{
+ return isl_val_mod(v1, v2);
+}
+
+/* Given two integer values, return their greatest common divisor.
+ */
+__isl_give isl_val *isl_val_gcd(__isl_take isl_val *v1, __isl_take isl_val *v2)
+{
+ if (!v1 || !v2)
+ goto error;
+ if (!isl_val_is_int(v1) || !isl_val_is_int(v2))
+ isl_die(isl_val_get_ctx(v1), isl_error_invalid,
+ "expecting two integers", goto error);
+ if (isl_val_eq(v1, v2)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_one(v1)) {
+ isl_val_free(v2);
+ return v1;
+ }
+ if (isl_val_is_one(v2)) {
+ isl_val_free(v1);
+ return v2;
+ }
+ v1 = isl_val_cow(v1);
+ if (!v1)
+ goto error;
+ isl_int_gcd(v1->n, v1->n, v2->n);
+ isl_val_free(v2);
+ return v1;
+error:
+ isl_val_free(v1);
+ isl_val_free(v2);
+ return NULL;
+}
+
+/* Compute x, y and g such that g = gcd(a,b) and a*x+b*y = g.
+ */
+static void isl_int_gcdext(isl_int *g, isl_int *x, isl_int *y,
+ isl_int a, isl_int b)
+{
+ isl_int d, tmp;
+ isl_int a_copy, b_copy;
+
+ isl_int_init(a_copy);
+ isl_int_init(b_copy);
+ isl_int_init(d);
+ isl_int_init(tmp);
+ isl_int_set(a_copy, a);
+ isl_int_set(b_copy, b);
+ isl_int_abs(*g, a_copy);
+ isl_int_abs(d, b_copy);
+ isl_int_set_si(*x, 1);
+ isl_int_set_si(*y, 0);
+ while (isl_int_is_pos(d)) {
+ isl_int_fdiv_q(tmp, *g, d);
+ isl_int_submul(*x, tmp, *y);
+ isl_int_submul(*g, tmp, d);
+ isl_int_swap(*g, d);
+ isl_int_swap(*x, *y);
+ }
+ if (isl_int_is_zero(a_copy))
+ isl_int_set_si(*x, 0);
+ else if (isl_int_is_neg(a_copy))
+ isl_int_neg(*x, *x);
+ if (isl_int_is_zero(b_copy))
+ isl_int_set_si(*y, 0);
+ else {
+ isl_int_mul(tmp, a_copy, *x);
+ isl_int_sub(tmp, *g, tmp);
+ isl_int_divexact(*y, tmp, b_copy);
+ }
+ isl_int_clear(d);
+ isl_int_clear(tmp);
+ isl_int_clear(a_copy);
+ isl_int_clear(b_copy);
+}
+
+/* Given two integer values v1 and v2, return their greatest common divisor g,
+ * as well as two integers x and y such that x * v1 + y * v2 = g.
+ */
+__isl_give isl_val *isl_val_gcdext(__isl_take isl_val *v1,
+ __isl_take isl_val *v2, __isl_give isl_val **x, __isl_give isl_val **y)
+{
+ isl_ctx *ctx;
+ isl_val *a = NULL, *b = NULL;
+
+ if (!x && !y)
+ return isl_val_gcd(v1, v2);
+
+ if (!v1 || !v2)
+ goto error;
+
+ ctx = isl_val_get_ctx(v1);
+ if (!isl_val_is_int(v1) || !isl_val_is_int(v2))
+ isl_die(ctx, isl_error_invalid,
+ "expecting two integers", goto error);
+
+ v1 = isl_val_cow(v1);
+ a = isl_val_alloc(ctx);
+ b = isl_val_alloc(ctx);
+ if (!v1 || !a || !b)
+ goto error;
+ isl_int_gcdext(&v1->n, &a->n, &b->n, v1->n, v2->n);
+ if (x) {
+ isl_int_set_si(a->d, 1);
+ *x = a;
+ } else
+ isl_val_free(a);
+ if (y) {
+ isl_int_set_si(b->d, 1);
+ *y = b;
+ } else
+ isl_val_free(b);
+ isl_val_free(v2);
+ return v1;
+error:
+ isl_val_free(v1);
+ isl_val_free(v2);
+ isl_val_free(a);
+ isl_val_free(b);
+ if (x)
+ *x = NULL;
+ if (y)
+ *y = NULL;
+ return NULL;
+}
+
+/* Does "v" represent an integer value?
+ */
+isl_bool isl_val_is_int(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_one(v->d));
+}
+
+/* Does "v" represent a rational value?
+ */
+isl_bool isl_val_is_rat(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ return isl_bool_ok(!isl_int_is_zero(v->d));
+}
+
+/* Does "v" represent NaN?
+ */
+isl_bool isl_val_is_nan(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_zero(v->n) && isl_int_is_zero(v->d));
+}
+
+/* Does "v" represent +infinity?
+ */
+isl_bool isl_val_is_infty(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_pos(v->n) && isl_int_is_zero(v->d));
+}
+
+/* Does "v" represent -infinity?
+ */
+isl_bool isl_val_is_neginfty(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_neg(v->n) && isl_int_is_zero(v->d));
+}
+
+/* Does "v" represent the integer zero?
+ */
+isl_bool isl_val_is_zero(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_zero(v->n) && !isl_int_is_zero(v->d));
+}
+
+/* Does "v" represent the integer one?
+ */
+isl_bool isl_val_is_one(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ if (isl_val_is_nan(v))
+ return isl_bool_false;
+
+ return isl_bool_ok(isl_int_eq(v->n, v->d));
+}
+
+/* Does "v" represent the integer negative one?
+ */
+isl_bool isl_val_is_negone(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_neg(v->n) && isl_int_abs_eq(v->n, v->d));
+}
+
+/* Is "v" (strictly) positive?
+ */
+isl_bool isl_val_is_pos(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_pos(v->n));
+}
+
+/* Is "v" (strictly) negative?
+ */
+isl_bool isl_val_is_neg(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ return isl_bool_ok(isl_int_is_neg(v->n));
+}
+
+/* Is "v" non-negative?
+ */
+isl_bool isl_val_is_nonneg(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ if (isl_val_is_nan(v))
+ return isl_bool_false;
+
+ return isl_bool_ok(isl_int_is_nonneg(v->n));
+}
+
+/* Is "v" non-positive?
+ */
+isl_bool isl_val_is_nonpos(__isl_keep isl_val *v)
+{
+ if (!v)
+ return isl_bool_error;
+
+ if (isl_val_is_nan(v))
+ return isl_bool_false;
+
+ return isl_bool_ok(isl_int_is_nonpos(v->n));
+}
+
+/* Return the sign of "v".
+ *
+ * The sign of NaN is undefined.
+ */
+int isl_val_sgn(__isl_keep isl_val *v)
+{
+ if (!v)
+ return 0;
+ if (isl_val_is_zero(v))
+ return 0;
+ if (isl_val_is_pos(v))
+ return 1;
+ return -1;
+}
+
+/* Is "v1" (strictly) less than "v2"?
+ */
+isl_bool isl_val_lt(__isl_keep isl_val *v1, __isl_keep isl_val *v2)
+{
+ isl_int t;
+ isl_bool lt;
+
+ if (!v1 || !v2)
+ return isl_bool_error;
+ if (isl_val_is_int(v1) && isl_val_is_int(v2))
+ return isl_bool_ok(isl_int_lt(v1->n, v2->n));
+ if (isl_val_is_nan(v1) || isl_val_is_nan(v2))
+ return isl_bool_false;
+ if (isl_val_eq(v1, v2))
+ return isl_bool_false;
+ if (isl_val_is_infty(v2))
+ return isl_bool_true;
+ if (isl_val_is_infty(v1))
+ return isl_bool_false;
+ if (isl_val_is_neginfty(v1))
+ return isl_bool_true;
+ if (isl_val_is_neginfty(v2))
+ return isl_bool_false;
+
+ isl_int_init(t);
+ isl_int_mul(t, v1->n, v2->d);
+ isl_int_submul(t, v2->n, v1->d);
+ lt = isl_bool_ok(isl_int_is_neg(t));
+ isl_int_clear(t);
+
+ return lt;
+}
+
+/* Is "v1" (strictly) greater than "v2"?
+ */
+isl_bool isl_val_gt(__isl_keep isl_val *v1, __isl_keep isl_val *v2)
+{
+ return isl_val_lt(v2, v1);
+}
+
+/* Is "v" (strictly) greater than "i"?
+ */
+isl_bool isl_val_gt_si(__isl_keep isl_val *v, long i)
+{
+ isl_val *vi;
+ isl_bool res;
+
+ if (!v)
+ return isl_bool_error;
+ if (isl_val_is_int(v))
+ return isl_bool_ok(isl_int_cmp_si(v->n, i) > 0);
+ if (isl_val_is_nan(v))
+ return isl_bool_false;
+ if (isl_val_is_infty(v))
+ return isl_bool_true;
+ if (isl_val_is_neginfty(v))
+ return isl_bool_false;
+
+ vi = isl_val_int_from_si(isl_val_get_ctx(v), i);
+ res = isl_bool_ok(isl_val_gt(v, vi));
+ isl_val_free(vi);
+
+ return res;
+}
+
+/* Is "v1" less than or equal to "v2"?
+ */
+isl_bool isl_val_le(__isl_keep isl_val *v1, __isl_keep isl_val *v2)
+{
+ isl_int t;
+ isl_bool le;
+
+ if (!v1 || !v2)
+ return isl_bool_error;
+ if (isl_val_is_int(v1) && isl_val_is_int(v2))
+ return isl_bool_ok(isl_int_le(v1->n, v2->n));
+ if (isl_val_is_nan(v1) || isl_val_is_nan(v2))
+ return isl_bool_false;
+ if (isl_val_eq(v1, v2))
+ return isl_bool_true;
+ if (isl_val_is_infty(v2))
+ return isl_bool_true;
+ if (isl_val_is_infty(v1))
+ return isl_bool_false;
+ if (isl_val_is_neginfty(v1))
+ return isl_bool_true;
+ if (isl_val_is_neginfty(v2))
+ return isl_bool_false;
+
+ isl_int_init(t);
+ isl_int_mul(t, v1->n, v2->d);
+ isl_int_submul(t, v2->n, v1->d);
+ le = isl_bool_ok(isl_int_is_nonpos(t));
+ isl_int_clear(t);
+
+ return le;
+}
+
+/* Is "v1" greater than or equal to "v2"?
+ */
+isl_bool isl_val_ge(__isl_keep isl_val *v1, __isl_keep isl_val *v2)
+{
+ return isl_val_le(v2, v1);
+}
+
+/* How does "v" compare to "i"?
+ *
+ * Return 1 if v is greater, -1 if v is smaller and 0 if v is equal to i.
+ *
+ * If v is NaN (or NULL), then the result is undefined.
+ */
+int isl_val_cmp_si(__isl_keep isl_val *v, long i)
+{
+ isl_int t;
+ int cmp;
+
+ if (!v)
+ return 0;
+ if (isl_val_is_int(v))
+ return isl_int_cmp_si(v->n, i);
+ if (isl_val_is_nan(v))
+ return 0;
+ if (isl_val_is_infty(v))
+ return 1;
+ if (isl_val_is_neginfty(v))
+ return -1;
+
+ isl_int_init(t);
+ isl_int_mul_si(t, v->d, i);
+ isl_int_sub(t, v->n, t);
+ cmp = isl_int_sgn(t);
+ isl_int_clear(t);
+
+ return cmp;
+}
+
+/* Is "v1" equal to "v2"?
+ */
+isl_bool isl_val_eq(__isl_keep isl_val *v1, __isl_keep isl_val *v2)
+{
+ if (!v1 || !v2)
+ return isl_bool_error;
+ if (isl_val_is_nan(v1) || isl_val_is_nan(v2))
+ return isl_bool_false;
+
+ return isl_bool_ok(isl_int_eq(v1->n, v2->n) &&
+ isl_int_eq(v1->d, v2->d));
+}
+
+/* Is "v" equal to "i"?
+ */
+isl_bool isl_val_eq_si(__isl_keep isl_val *v, long i)
+{
+ if (!v)
+ return isl_bool_error;
+ if (!isl_val_is_int(v))
+ return isl_bool_false;
+ return isl_bool_ok(isl_int_cmp_si(v->n, i) == 0);
+}
+
+/* Is "v1" equal to "v2" in absolute value?
+ */
+isl_bool isl_val_abs_eq(__isl_keep isl_val *v1, __isl_keep isl_val *v2)
+{
+ if (!v1 || !v2)
+ return isl_bool_error;
+ if (isl_val_is_nan(v1) || isl_val_is_nan(v2))
+ return isl_bool_false;
+
+ return isl_bool_ok(isl_int_abs_eq(v1->n, v2->n) &&
+ isl_int_eq(v1->d, v2->d));
+}
+
+/* Is "v1" different from "v2"?
+ */
+isl_bool isl_val_ne(__isl_keep isl_val *v1, __isl_keep isl_val *v2)
+{
+ if (!v1 || !v2)
+ return isl_bool_error;
+ if (isl_val_is_nan(v1) || isl_val_is_nan(v2))
+ return isl_bool_false;
+
+ return isl_bool_ok(isl_int_ne(v1->n, v2->n) ||
+ isl_int_ne(v1->d, v2->d));
+}
+
+/* Print a textual representation of "v" onto "p".
+ */
+__isl_give isl_printer *isl_printer_print_val(__isl_take isl_printer *p,
+ __isl_keep isl_val *v)
+{
+ int neg;
+
+ if (!p || !v)
+ return isl_printer_free(p);
+
+ neg = isl_int_is_neg(v->n);
+ if (neg) {
+ p = isl_printer_print_str(p, "-");
+ isl_int_neg(v->n, v->n);
+ }
+ if (isl_int_is_zero(v->d)) {
+ int sgn = isl_int_sgn(v->n);
+ p = isl_printer_print_str(p, sgn < 0 ? "-infty" :
+ sgn == 0 ? "NaN" : "infty");
+ } else
+ p = isl_printer_print_isl_int(p, v->n);
+ if (neg)
+ isl_int_neg(v->n, v->n);
+ if (!isl_int_is_zero(v->d) && !isl_int_is_one(v->d)) {
+ p = isl_printer_print_str(p, "/");
+ p = isl_printer_print_isl_int(p, v->d);
+ }
+
+ return p;
+}
+
+/* Is "val1" (obviously) equal to "val2"?
+ *
+ * This is a private copy of isl_val_eq for use in the generic
+ * isl_multi_*_plain_is_equal instantiated for isl_val.
+ */
+isl_bool isl_val_plain_is_equal(__isl_keep isl_val *val1,
+ __isl_keep isl_val *val2)
+{
+ return isl_val_eq(val1, val2);
+}
+
+/* Does "v" have any non-zero coefficients
+ * for any dimension in the given range?
+ *
+ * This function is only meant to be used in the generic isl_multi_*
+ * functions which have to deal with base objects that have an associated
+ * space. Since an isl_val does not have any coefficients, this function
+ * always returns isl_bool_false.
+ */
+isl_bool isl_val_involves_dims(__isl_keep isl_val *v, enum isl_dim_type type,
+ unsigned first, unsigned n)
+{
+ if (!v)
+ return isl_bool_error;
+
+ return isl_bool_false;
+}
+
+/* Insert "n" dimensions of type "type" at position "first".
+ *
+ * This function is only meant to be used in the generic isl_multi_*
+ * functions which have to deal with base objects that have an associated
+ * space. Since an isl_val does not have an associated space, this function
+ * does not do anything.
+ */
+__isl_give isl_val *isl_val_insert_dims(__isl_take isl_val *v,
+ enum isl_dim_type type, unsigned first, unsigned n)
+{
+ return v;
+}
+
+/* Change the name of the dimension of type "type" at position "pos" to "s".
+ *
+ * This function is only meant to be used in the generic isl_multi_*
+ * functions which have to deal with base objects that have an associated
+ * space. Since an isl_val does not have an associated space, this function
+ * does not do anything.
+ */
+__isl_give isl_val *isl_val_set_dim_name(__isl_take isl_val *v,
+ enum isl_dim_type type, unsigned pos, const char *s)
+{
+ return v;
+}
+
+/* Return an isl_val that is zero on "ls".
+ *
+ * This function is only meant to be used in the generic isl_multi_*
+ * functions which have to deal with base objects that have an associated
+ * space. Since an isl_val does not have an associated space, this function
+ * simply returns a zero isl_val in the same context as "ls".
+ */
+__isl_give isl_val *isl_val_zero_on_domain(__isl_take isl_local_space *ls)
+{
+ isl_ctx *ctx;
+
+ if (!ls)
+ return NULL;
+ ctx = isl_local_space_get_ctx(ls);
+ isl_local_space_free(ls);
+ return isl_val_zero(ctx);
+}
+
+#define isl_val_involves_nan isl_val_is_nan
+
+#undef BASE
+#define BASE val
+
+#include <isl_multi_no_domain_templ.c>
+#include <isl_multi_no_explicit_domain.c>
+#include <isl_multi_templ.c>
+#include <isl_multi_arith_templ.c>
+#include <isl_multi_dim_id_templ.c>
+#include <isl_multi_dims.c>
+#include <isl_multi_min_max_templ.c>
+#include <isl_multi_nan_templ.c>
+#include <isl_multi_product_templ.c>
+#include <isl_multi_splice_templ.c>
+#include <isl_multi_tuple_id_templ.c>
+#include <isl_multi_zero_templ.c>
+
+/* Does "mv" consist of only zeros?
+ */
+isl_bool isl_multi_val_is_zero(__isl_keep isl_multi_val *mv)
+{
+ return isl_multi_val_every(mv, &isl_val_is_zero);
+}
+
+/* Apply "fn" to each of the elements of "mv" with as second argument "v".
+ */
+static __isl_give isl_multi_val *isl_multi_val_fn_val(
+ __isl_take isl_multi_val *mv,
+ __isl_give isl_val *(*fn)(__isl_take isl_val *v1,
+ __isl_take isl_val *v2),
+ __isl_take isl_val *v)
+{
+ int i;
+
+ mv = isl_multi_val_cow(mv);
+ if (!mv || !v)
+ goto error;
+
+ for (i = 0; i < mv->n; ++i) {
+ mv->u.p[i] = fn(mv->u.p[i], isl_val_copy(v));
+ if (!mv->u.p[i])
+ goto error;
+ }
+
+ isl_val_free(v);
+ return mv;
+error:
+ isl_val_free(v);
+ isl_multi_val_free(mv);
+ return NULL;
+}
+
+/* Add "v" to each of the elements of "mv".
+ */
+__isl_give isl_multi_val *isl_multi_val_add_val(__isl_take isl_multi_val *mv,
+ __isl_take isl_val *v)
+{
+ if (!v)
+ return isl_multi_val_free(mv);
+ if (isl_val_is_zero(v)) {
+ isl_val_free(v);
+ return mv;
+ }
+ return isl_multi_val_fn_val(mv, &isl_val_add, v);
+}
+
+/* Reduce the elements of "mv" modulo "v".
+ */
+__isl_give isl_multi_val *isl_multi_val_mod_val(__isl_take isl_multi_val *mv,
+ __isl_take isl_val *v)
+{
+ return isl_multi_val_fn_val(mv, &isl_val_mod, v);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val_private.h
new file mode 100644
index 00000000000..59d85199050
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val_private.h
@@ -0,0 +1,61 @@
+#ifndef ISL_VAL_PRIVATE_H
+#define ISL_VAL_PRIVATE_H
+
+#include <isl_int.h>
+#include <isl/val.h>
+#include <isl/local_space.h>
+#include <isl_reordering.h>
+
+/* Represents a "value", which may be an integer value, a rational value,
+ * plus or minus infinity or "not a number".
+ *
+ * Internally, +infinity is represented as 1/0,
+ * -infinity as -1/0 and NaN as 0/0.
+ *
+ * A rational value is always normalized before it is passed to the user.
+ */
+struct isl_val {
+ int ref;
+ isl_ctx *ctx;
+
+ isl_int n;
+ isl_int d;
+};
+
+#undef EL
+#define EL isl_val
+
+#include <isl_list_templ.h>
+
+__isl_give isl_val *isl_val_alloc(isl_ctx *ctx);
+__isl_give isl_val *isl_val_normalize(__isl_take isl_val *v);
+__isl_give isl_val *isl_val_int_from_isl_int(isl_ctx *ctx, isl_int n);
+__isl_give isl_val *isl_val_rat_from_isl_int(isl_ctx *ctx,
+ isl_int n, isl_int d);
+__isl_give isl_val *isl_val_cow(__isl_take isl_val *val);
+
+isl_stat isl_val_get_num_isl_int(__isl_keep isl_val *v, isl_int *n);
+
+isl_bool isl_val_involves_dims(__isl_keep isl_val *v, enum isl_dim_type type,
+ unsigned first, unsigned n);
+__isl_give isl_val *isl_val_insert_dims(__isl_take isl_val *v,
+ enum isl_dim_type type, unsigned first, unsigned n);
+__isl_give isl_val *isl_val_set_dim_name(__isl_take isl_val *v,
+ enum isl_dim_type type, unsigned pos, const char *s);
+
+__isl_give isl_val *isl_val_scale_val(__isl_take isl_val *v1,
+ __isl_take isl_val *v2);
+__isl_give isl_val *isl_val_scale_down_val(__isl_take isl_val *v1,
+ __isl_take isl_val *v2);
+__isl_give isl_val *isl_val_mod_val(__isl_take isl_val *v1,
+ __isl_take isl_val *v2);
+
+isl_bool isl_val_plain_is_equal(__isl_keep isl_val *val1,
+ __isl_keep isl_val *val2);
+
+#undef BASE
+#define BASE val
+
+#include <isl_multi_templ.h>
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val_sioimath.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val_sioimath.c
new file mode 100644
index 00000000000..9b9e73d817a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_val_sioimath.c
@@ -0,0 +1,68 @@
+#include <isl_val_private.h>
+
+/* Return a reference to an isl_val representing the unsigned
+ * integer value stored in the "n" chunks of size "size" at "chunks".
+ * The least significant chunk is assumed to be stored first.
+ */
+__isl_give isl_val *isl_val_int_from_chunks(isl_ctx *ctx, size_t n,
+ size_t size, const void *chunks)
+{
+ isl_val *v;
+
+ v = isl_val_alloc(ctx);
+ if (!v)
+ return NULL;
+
+ impz_import(isl_sioimath_reinit_big(v->n), n, -1, size, 0, 0, chunks);
+ isl_sioimath_try_demote(v->n);
+ isl_int_set_si(v->d, 1);
+
+ return v;
+}
+
+/* Store a representation of the absolute value of the numerator of "v"
+ * in terms of chunks of size "size" at "chunks".
+ * The least significant chunk is stored first.
+ * The number of chunks in the result can be obtained by calling
+ * isl_val_n_abs_num_chunks. The user is responsible for allocating
+ * enough memory to store the results.
+ *
+ * In the special case of a zero value, isl_val_n_abs_num_chunks will
+ * return one, while impz_export will not fill in any chunks. We therefore
+ * do it ourselves.
+ */
+isl_stat isl_val_get_abs_num_chunks(__isl_keep isl_val *v, size_t size,
+ void *chunks)
+{
+ isl_sioimath_scratchspace_t scratch;
+
+ if (!v || !chunks)
+ return isl_stat_error;
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational value", return isl_stat_error);
+
+ impz_export(chunks, NULL, -1, size, 0, 0,
+ isl_sioimath_bigarg_src(*v->n, &scratch));
+ if (isl_val_is_zero(v))
+ memset(chunks, 0, size);
+
+ return isl_stat_ok;
+}
+
+/* Return the number of chunks of size "size" required to
+ * store the absolute value of the numerator of "v".
+ */
+isl_size isl_val_n_abs_num_chunks(__isl_keep isl_val *v, size_t size)
+{
+ if (!v)
+ return isl_size_error;
+
+ if (!isl_val_is_rat(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting rational value", return isl_size_error);
+
+ size *= 8;
+ return (isl_sioimath_sizeinbase(*v->n, 2) + size - 1) / size;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vec.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vec.c
new file mode 100644
index 00000000000..7d0ea18c11a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vec.c
@@ -0,0 +1,655 @@
+/*
+ * Copyright 2008-2009 Katholieke Universiteit Leuven
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, K.U.Leuven, Departement
+ * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <isl_ctx_private.h>
+#include <isl_seq.h>
+#include <isl_val_private.h>
+#include <isl_vec_private.h>
+
+isl_ctx *isl_vec_get_ctx(__isl_keep isl_vec *vec)
+{
+ return vec ? vec->ctx : NULL;
+}
+
+/* Return a hash value that digests "vec".
+ */
+uint32_t isl_vec_get_hash(__isl_keep isl_vec *vec)
+{
+ if (!vec)
+ return 0;
+
+ return isl_seq_get_hash(vec->el, vec->size);
+}
+
+__isl_give isl_vec *isl_vec_alloc(struct isl_ctx *ctx, unsigned size)
+{
+ struct isl_vec *vec;
+
+ vec = isl_alloc_type(ctx, struct isl_vec);
+ if (!vec)
+ return NULL;
+
+ vec->block = isl_blk_alloc(ctx, size);
+ if (isl_blk_is_error(vec->block))
+ goto error;
+
+ vec->ctx = ctx;
+ isl_ctx_ref(ctx);
+ vec->ref = 1;
+ vec->size = size;
+ vec->el = vec->block.data;
+
+ return vec;
+error:
+ isl_blk_free(ctx, vec->block);
+ free(vec);
+ return NULL;
+}
+
+__isl_give isl_vec *isl_vec_extend(__isl_take isl_vec *vec, unsigned size)
+{
+ if (!vec)
+ return NULL;
+ if (size <= vec->size)
+ return vec;
+
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+
+ vec->block = isl_blk_extend(vec->ctx, vec->block, size);
+ if (!vec->block.data)
+ goto error;
+
+ vec->size = size;
+ vec->el = vec->block.data;
+
+ return vec;
+error:
+ isl_vec_free(vec);
+ return NULL;
+}
+
+/* Apply the expansion specified by "exp" to the "n" elements starting at "pos".
+ * "expanded" it the number of elements that need to replace those "n"
+ * elements. The entries in "exp" have increasing values between
+ * 0 and "expanded".
+ */
+__isl_give isl_vec *isl_vec_expand(__isl_take isl_vec *vec, int pos, int n,
+ int *exp, int expanded)
+{
+ int i, j;
+ int old_size, extra;
+
+ if (!vec)
+ return NULL;
+ if (expanded < n)
+ isl_die(isl_vec_get_ctx(vec), isl_error_invalid,
+ "not an expansion", return isl_vec_free(vec));
+ if (expanded == n)
+ return vec;
+ if (pos < 0 || n < 0 || pos + n > vec->size)
+ isl_die(isl_vec_get_ctx(vec), isl_error_invalid,
+ "position out of bounds", return isl_vec_free(vec));
+
+ old_size = vec->size;
+ extra = expanded - n;
+ vec = isl_vec_extend(vec, old_size + extra);
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+
+ for (i = old_size - 1; i >= pos + n; --i)
+ isl_int_set(vec->el[i + extra], vec->el[i]);
+
+ j = n - 1;
+ for (i = expanded - 1; i >= 0; --i) {
+ if (j >= 0 && exp[j] == i) {
+ if (i != j)
+ isl_int_swap(vec->el[pos + i],
+ vec->el[pos + j]);
+ j--;
+ } else {
+ isl_int_set_si(vec->el[pos + i], 0);
+ }
+ }
+
+ return vec;
+}
+
+/* Create a vector of size "size" with zero-valued elements.
+ */
+__isl_give isl_vec *isl_vec_zero(isl_ctx *ctx, unsigned size)
+{
+ isl_vec *vec;
+
+ vec = isl_vec_alloc(ctx, size);
+ if (!vec)
+ return NULL;
+ isl_seq_clr(vec->el, size);
+ return vec;
+}
+
+__isl_give isl_vec *isl_vec_zero_extend(__isl_take isl_vec *vec, unsigned size)
+{
+ int extra;
+
+ if (!vec)
+ return NULL;
+ if (size <= vec->size)
+ return vec;
+
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+
+ extra = size - vec->size;
+ vec = isl_vec_extend(vec, size);
+ if (!vec)
+ return NULL;
+
+ isl_seq_clr(vec->el + size - extra, extra);
+
+ return vec;
+}
+
+/* Return a vector containing the elements of "vec1" followed by
+ * those of "vec2".
+ */
+__isl_give isl_vec *isl_vec_concat(__isl_take isl_vec *vec1,
+ __isl_take isl_vec *vec2)
+{
+ if (!vec1 || !vec2)
+ goto error;
+
+ if (vec2->size == 0) {
+ isl_vec_free(vec2);
+ return vec1;
+ }
+
+ if (vec1->size == 0) {
+ isl_vec_free(vec1);
+ return vec2;
+ }
+
+ vec1 = isl_vec_extend(vec1, vec1->size + vec2->size);
+ if (!vec1)
+ goto error;
+
+ isl_seq_cpy(vec1->el + vec1->size - vec2->size, vec2->el, vec2->size);
+
+ isl_vec_free(vec2);
+ return vec1;
+error:
+ isl_vec_free(vec1);
+ isl_vec_free(vec2);
+ return NULL;
+}
+
+__isl_give isl_vec *isl_vec_copy(__isl_keep isl_vec *vec)
+{
+ if (!vec)
+ return NULL;
+
+ vec->ref++;
+ return vec;
+}
+
+__isl_give isl_vec *isl_vec_dup(__isl_keep isl_vec *vec)
+{
+ struct isl_vec *vec2;
+
+ if (!vec)
+ return NULL;
+ vec2 = isl_vec_alloc(vec->ctx, vec->size);
+ if (!vec2)
+ return NULL;
+ isl_seq_cpy(vec2->el, vec->el, vec->size);
+ return vec2;
+}
+
+__isl_give isl_vec *isl_vec_cow(__isl_take isl_vec *vec)
+{
+ struct isl_vec *vec2;
+ if (!vec)
+ return NULL;
+
+ if (vec->ref == 1)
+ return vec;
+
+ vec2 = isl_vec_dup(vec);
+ isl_vec_free(vec);
+ return vec2;
+}
+
+__isl_null isl_vec *isl_vec_free(__isl_take isl_vec *vec)
+{
+ if (!vec)
+ return NULL;
+
+ if (--vec->ref > 0)
+ return NULL;
+
+ isl_ctx_deref(vec->ctx);
+ isl_blk_free(vec->ctx, vec->block);
+ free(vec);
+
+ return NULL;
+}
+
+isl_size isl_vec_size(__isl_keep isl_vec *vec)
+{
+ return vec ? vec->size : isl_size_error;
+}
+
+/* Extract the element at position "pos" of "vec".
+ */
+__isl_give isl_val *isl_vec_get_element_val(__isl_keep isl_vec *vec, int pos)
+{
+ isl_ctx *ctx;
+
+ if (!vec)
+ return NULL;
+ ctx = isl_vec_get_ctx(vec);
+ if (pos < 0 || pos >= vec->size)
+ isl_die(ctx, isl_error_invalid, "position out of range",
+ return NULL);
+ return isl_val_int_from_isl_int(ctx, vec->el[pos]);
+}
+
+__isl_give isl_vec *isl_vec_set_element(__isl_take isl_vec *vec,
+ int pos, isl_int v)
+{
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+ if (pos < 0 || pos >= vec->size)
+ isl_die(vec->ctx, isl_error_invalid, "position out of range",
+ goto error);
+ isl_int_set(vec->el[pos], v);
+ return vec;
+error:
+ isl_vec_free(vec);
+ return NULL;
+}
+
+__isl_give isl_vec *isl_vec_set_element_si(__isl_take isl_vec *vec,
+ int pos, int v)
+{
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+ if (pos < 0 || pos >= vec->size)
+ isl_die(vec->ctx, isl_error_invalid, "position out of range",
+ goto error);
+ isl_int_set_si(vec->el[pos], v);
+ return vec;
+error:
+ isl_vec_free(vec);
+ return NULL;
+}
+
+/* Replace the element at position "pos" of "vec" by "v".
+ */
+__isl_give isl_vec *isl_vec_set_element_val(__isl_take isl_vec *vec,
+ int pos, __isl_take isl_val *v)
+{
+ if (!v)
+ return isl_vec_free(vec);
+ if (!isl_val_is_int(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting integer value", goto error);
+ vec = isl_vec_set_element(vec, pos, v->n);
+ isl_val_free(v);
+ return vec;
+error:
+ isl_val_free(v);
+ return isl_vec_free(vec);
+}
+
+/* Compare the elements of "vec1" and "vec2" at position "pos".
+ */
+int isl_vec_cmp_element(__isl_keep isl_vec *vec1, __isl_keep isl_vec *vec2,
+ int pos)
+{
+ if (!vec1 || !vec2)
+ return 0;
+ if (pos < 0 || pos >= vec1->size || pos >= vec2->size)
+ isl_die(isl_vec_get_ctx(vec1), isl_error_invalid,
+ "position out of range", return 0);
+ return isl_int_cmp(vec1->el[pos], vec2->el[pos]);
+}
+
+/* Does "vec" contain only zero elements?
+ */
+isl_bool isl_vec_is_zero(__isl_keep isl_vec *vec)
+{
+ if (!vec)
+ return isl_bool_error;
+ return isl_bool_ok(isl_seq_first_non_zero(vec->el, vec->size) < 0);
+}
+
+isl_bool isl_vec_is_equal(__isl_keep isl_vec *vec1, __isl_keep isl_vec *vec2)
+{
+ if (!vec1 || !vec2)
+ return isl_bool_error;
+
+ if (vec1->size != vec2->size)
+ return isl_bool_false;
+
+ return isl_bool_ok(isl_seq_eq(vec1->el, vec2->el, vec1->size));
+}
+
+__isl_give isl_printer *isl_printer_print_vec(__isl_take isl_printer *printer,
+ __isl_keep isl_vec *vec)
+{
+ int i;
+
+ if (!printer || !vec)
+ goto error;
+
+ printer = isl_printer_print_str(printer, "[");
+ for (i = 0; i < vec->size; ++i) {
+ if (i)
+ printer = isl_printer_print_str(printer, ",");
+ printer = isl_printer_print_isl_int(printer, vec->el[i]);
+ }
+ printer = isl_printer_print_str(printer, "]");
+
+ return printer;
+error:
+ isl_printer_free(printer);
+ return NULL;
+}
+
+void isl_vec_dump(__isl_keep isl_vec *vec)
+{
+ isl_printer *printer;
+
+ if (!vec)
+ return;
+
+ printer = isl_printer_to_file(vec->ctx, stderr);
+ printer = isl_printer_print_vec(printer, vec);
+ printer = isl_printer_end_line(printer);
+
+ isl_printer_free(printer);
+}
+
+__isl_give isl_vec *isl_vec_set(__isl_take isl_vec *vec, isl_int v)
+{
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+ isl_seq_set(vec->el, v, vec->size);
+ return vec;
+}
+
+__isl_give isl_vec *isl_vec_set_si(__isl_take isl_vec *vec, int v)
+{
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+ isl_seq_set_si(vec->el, v, vec->size);
+ return vec;
+}
+
+/* Replace all elements of "vec" by "v".
+ */
+__isl_give isl_vec *isl_vec_set_val(__isl_take isl_vec *vec,
+ __isl_take isl_val *v)
+{
+ vec = isl_vec_cow(vec);
+ if (!vec || !v)
+ goto error;
+ if (!isl_val_is_int(v))
+ isl_die(isl_val_get_ctx(v), isl_error_invalid,
+ "expecting integer value", goto error);
+ isl_seq_set(vec->el, v->n, vec->size);
+ isl_val_free(v);
+ return vec;
+error:
+ isl_vec_free(vec);
+ isl_val_free(v);
+ return NULL;
+}
+
+__isl_give isl_vec *isl_vec_clr(__isl_take isl_vec *vec)
+{
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+ isl_seq_clr(vec->el, vec->size);
+ return vec;
+}
+
+void isl_vec_lcm(__isl_keep isl_vec *vec, isl_int *lcm)
+{
+ isl_seq_lcm(vec->block.data, vec->size, lcm);
+}
+
+/* Given a rational vector, with the denominator in the first element
+ * of the vector, round up all coordinates.
+ */
+__isl_give isl_vec *isl_vec_ceil(__isl_take isl_vec *vec)
+{
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+
+ isl_seq_cdiv_q(vec->el + 1, vec->el + 1, vec->el[0], vec->size - 1);
+
+ isl_int_set_si(vec->el[0], 1);
+
+ return vec;
+}
+
+__isl_give isl_vec *isl_vec_normalize(__isl_take isl_vec *vec)
+{
+ if (!vec)
+ return NULL;
+ isl_seq_normalize(vec->ctx, vec->el, vec->size);
+ return vec;
+}
+
+__isl_give isl_vec *isl_vec_neg(__isl_take isl_vec *vec)
+{
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+ isl_seq_neg(vec->el, vec->el, vec->size);
+ return vec;
+}
+
+__isl_give isl_vec *isl_vec_scale(__isl_take isl_vec *vec, isl_int m)
+{
+ if (isl_int_is_one(m))
+ return vec;
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+ isl_seq_scale(vec->el, vec->el, m, vec->size);
+ return vec;
+}
+
+/* Reduce the elements of "vec" modulo "m".
+ */
+__isl_give isl_vec *isl_vec_fdiv_r(__isl_take isl_vec *vec, isl_int m)
+{
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+
+ isl_seq_fdiv_r(vec->el, vec->el, m, vec->size);
+
+ return vec;
+}
+
+__isl_give isl_vec *isl_vec_add(__isl_take isl_vec *vec1,
+ __isl_take isl_vec *vec2)
+{
+ vec1 = isl_vec_cow(vec1);
+ if (!vec1 || !vec2)
+ goto error;
+
+ isl_assert(vec1->ctx, vec1->size == vec2->size, goto error);
+
+ isl_seq_combine(vec1->el, vec1->ctx->one, vec1->el,
+ vec1->ctx->one, vec2->el, vec1->size);
+
+ isl_vec_free(vec2);
+ return vec1;
+error:
+ isl_vec_free(vec1);
+ isl_vec_free(vec2);
+ return NULL;
+}
+
+static int qsort_int_cmp(const void *p1, const void *p2)
+{
+ const isl_int *i1 = (const isl_int *) p1;
+ const isl_int *i2 = (const isl_int *) p2;
+
+ return isl_int_cmp(*i1, *i2);
+}
+
+__isl_give isl_vec *isl_vec_sort(__isl_take isl_vec *vec)
+{
+ if (!vec)
+ return NULL;
+
+ qsort(vec->el, vec->size, sizeof(*vec->el), &qsort_int_cmp);
+
+ return vec;
+}
+
+__isl_give isl_vec *isl_vec_drop_els(__isl_take isl_vec *vec,
+ unsigned pos, unsigned n)
+{
+ if (n == 0)
+ return vec;
+ vec = isl_vec_cow(vec);
+ if (!vec)
+ return NULL;
+
+ if (pos + n > vec->size)
+ isl_die(vec->ctx, isl_error_invalid,
+ "range out of bounds", goto error);
+
+ if (pos + n != vec->size)
+ isl_seq_cpy(vec->el + pos, vec->el + pos + n,
+ vec->size - pos - n);
+
+ vec->size -= n;
+
+ return vec;
+error:
+ isl_vec_free(vec);
+ return NULL;
+}
+
+__isl_give isl_vec *isl_vec_insert_els(__isl_take isl_vec *vec,
+ unsigned pos, unsigned n)
+{
+ isl_vec *ext = NULL;
+
+ if (n == 0)
+ return vec;
+ if (!vec)
+ return NULL;
+
+ if (pos > vec->size)
+ isl_die(vec->ctx, isl_error_invalid,
+ "position out of bounds", goto error);
+
+ ext = isl_vec_alloc(vec->ctx, vec->size + n);
+ if (!ext)
+ goto error;
+
+ isl_seq_cpy(ext->el, vec->el, pos);
+ isl_seq_cpy(ext->el + pos + n, vec->el + pos, vec->size - pos);
+
+ isl_vec_free(vec);
+ return ext;
+error:
+ isl_vec_free(vec);
+ isl_vec_free(ext);
+ return NULL;
+}
+
+/* Add "n" elements at the end of "vec".
+ */
+__isl_give isl_vec *isl_vec_add_els(__isl_take isl_vec *vec, unsigned n)
+{
+ if (!vec)
+ return NULL;
+ return isl_vec_insert_els(vec, vec->size, n);
+}
+
+__isl_give isl_vec *isl_vec_insert_zero_els(__isl_take isl_vec *vec,
+ unsigned pos, unsigned n)
+{
+ vec = isl_vec_insert_els(vec, pos, n);
+ if (!vec)
+ return NULL;
+
+ isl_seq_clr(vec->el + pos, n);
+
+ return vec;
+}
+
+/* Move the "n" elements starting as "src_pos" of "vec"
+ * to "dst_pos". The elements originally at "dst_pos" are moved
+ * up or down depending on whether "dst_pos" is smaller or greater
+ * than "src_pos".
+ */
+__isl_give isl_vec *isl_vec_move_els(__isl_take isl_vec *vec,
+ unsigned dst_pos, unsigned src_pos, unsigned n)
+{
+ isl_vec *res;
+
+ if (!vec)
+ return NULL;
+
+ if (src_pos + n > vec->size)
+ isl_die(vec->ctx, isl_error_invalid,
+ "source range out of bounds", return isl_vec_free(vec));
+ if (dst_pos + n > vec->size)
+ isl_die(vec->ctx, isl_error_invalid,
+ "destination range out of bounds",
+ return isl_vec_free(vec));
+
+ if (n == 0 || dst_pos == src_pos)
+ return vec;
+
+ res = isl_vec_alloc(vec->ctx, vec->size);
+ if (!res)
+ return isl_vec_free(vec);
+
+ if (dst_pos < src_pos) {
+ isl_seq_cpy(res->el, vec->el, dst_pos);
+ isl_seq_cpy(res->el + dst_pos, vec->el + src_pos, n);
+ isl_seq_cpy(res->el + dst_pos + n,
+ vec->el + dst_pos, src_pos - dst_pos);
+ isl_seq_cpy(res->el + src_pos + n,
+ vec->el + src_pos + n, res->size - src_pos - n);
+ } else {
+ isl_seq_cpy(res->el, vec->el, src_pos);
+ isl_seq_cpy(res->el + src_pos,
+ vec->el + src_pos + n, dst_pos - src_pos);
+ isl_seq_cpy(res->el + dst_pos, vec->el + src_pos, n);
+ isl_seq_cpy(res->el + dst_pos + n,
+ vec->el + dst_pos + n, res->size - dst_pos - n);
+ }
+
+ isl_vec_free(vec);
+ return res;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vec_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vec_private.h
new file mode 100644
index 00000000000..49bcef81f60
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vec_private.h
@@ -0,0 +1,30 @@
+#ifndef ISL_VEC_PRIVATE_H
+#define ISL_VEC_PRIVATE_H
+
+#include <isl_blk.h>
+#include <isl/vec.h>
+
+struct isl_vec {
+ int ref;
+
+ struct isl_ctx *ctx;
+
+ unsigned size;
+ isl_int *el;
+
+ struct isl_blk block;
+};
+
+uint32_t isl_vec_get_hash(__isl_keep isl_vec *vec);
+
+__isl_give isl_vec *isl_vec_cow(__isl_take isl_vec *vec);
+
+void isl_vec_lcm(__isl_keep isl_vec *vec, isl_int *lcm);
+__isl_give isl_vec *isl_vec_set(__isl_take isl_vec *vec, isl_int v);
+
+isl_bool isl_vec_is_zero(__isl_keep isl_vec *vec);
+
+__isl_give isl_vec *isl_vec_expand(__isl_take isl_vec *vec, int pos, int n,
+ int *exp, int expanded);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_version.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_version.c
new file mode 100644
index 00000000000..114323d64f2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_version.c
@@ -0,0 +1,17 @@
+#include "isl_config.h"
+#include "gitversion.h"
+
+const char *isl_version(void)
+{
+ return GIT_HEAD_ID
+#ifdef USE_GMP_FOR_MP
+ "-GMP"
+#endif
+#ifdef USE_IMATH_FOR_MP
+ "-IMath"
+#ifdef USE_SMALL_INT_OPT
+ "-32"
+#endif
+#endif
+ "\n";
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vertices.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vertices.c
new file mode 100644
index 00000000000..fd1a2ee088e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vertices.c
@@ -0,0 +1,1624 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <isl_map_private.h>
+#include <isl_aff_private.h>
+#include <isl/set.h>
+#include <isl_seq.h>
+#include <isl_tab.h>
+#include <isl_space_private.h>
+#include <isl_morph.h>
+#include <isl_vertices_private.h>
+#include <isl_mat_private.h>
+#include <isl_vec_private.h>
+
+#define SELECTED 1
+#define DESELECTED -1
+#define UNSELECTED 0
+
+static __isl_give isl_vertices *compute_chambers(__isl_take isl_basic_set *bset,
+ __isl_take isl_vertices *vertices);
+
+__isl_give isl_vertices *isl_vertices_copy(__isl_keep isl_vertices *vertices)
+{
+ if (!vertices)
+ return NULL;
+
+ vertices->ref++;
+ return vertices;
+}
+
+__isl_null isl_vertices *isl_vertices_free(__isl_take isl_vertices *vertices)
+{
+ int i;
+
+ if (!vertices)
+ return NULL;
+
+ if (--vertices->ref > 0)
+ return NULL;
+
+ for (i = 0; i < vertices->n_vertices; ++i) {
+ isl_basic_set_free(vertices->v[i].vertex);
+ isl_basic_set_free(vertices->v[i].dom);
+ }
+ free(vertices->v);
+
+ for (i = 0; i < vertices->n_chambers; ++i) {
+ free(vertices->c[i].vertices);
+ isl_basic_set_free(vertices->c[i].dom);
+ }
+ free(vertices->c);
+
+ isl_basic_set_free(vertices->bset);
+ free(vertices);
+
+ return NULL;
+}
+
+struct isl_vertex_list {
+ struct isl_vertex v;
+ struct isl_vertex_list *next;
+};
+
+static struct isl_vertex_list *free_vertex_list(struct isl_vertex_list *list)
+{
+ struct isl_vertex_list *next;
+
+ for (; list; list = next) {
+ next = list->next;
+ isl_basic_set_free(list->v.vertex);
+ isl_basic_set_free(list->v.dom);
+ free(list);
+ }
+
+ return NULL;
+}
+
+static __isl_give isl_vertices *vertices_from_list(__isl_keep isl_basic_set *bset,
+ int n_vertices, struct isl_vertex_list *list)
+{
+ int i;
+ struct isl_vertex_list *next;
+ isl_vertices *vertices;
+
+ vertices = isl_calloc_type(bset->ctx, isl_vertices);
+ if (!vertices)
+ goto error;
+ vertices->ref = 1;
+ vertices->bset = isl_basic_set_copy(bset);
+ vertices->v = isl_alloc_array(bset->ctx, struct isl_vertex, n_vertices);
+ if (n_vertices && !vertices->v)
+ goto error;
+ vertices->n_vertices = n_vertices;
+
+ for (i = 0; list; list = next, i++) {
+ next = list->next;
+ vertices->v[i] = list->v;
+ free(list);
+ }
+
+ return vertices;
+error:
+ isl_vertices_free(vertices);
+ free_vertex_list(list);
+ return NULL;
+}
+
+/* Prepend a vertex to the linked list "list" based on the equalities in "tab".
+ * Return isl_bool_true if the vertex was actually added and
+ * isl_bool_false otherwise.
+ * In particular, vertices with a lower-dimensional activity domain are
+ * not added to the list because they would not be included in any chamber.
+ * Return isl_bool_error on error.
+ */
+static isl_bool add_vertex(struct isl_vertex_list **list,
+ __isl_keep isl_basic_set *bset, struct isl_tab *tab)
+{
+ isl_size nvar;
+ struct isl_vertex_list *v = NULL;
+
+ if (isl_tab_detect_implicit_equalities(tab) < 0)
+ return isl_bool_error;
+
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+ if (nvar < 0)
+ return isl_bool_error;
+
+ v = isl_calloc_type(tab->mat->ctx, struct isl_vertex_list);
+ if (!v)
+ goto error;
+
+ v->v.vertex = isl_basic_set_copy(bset);
+ v->v.vertex = isl_basic_set_cow(v->v.vertex);
+ v->v.vertex = isl_basic_set_update_from_tab(v->v.vertex, tab);
+ v->v.vertex = isl_basic_set_simplify(v->v.vertex);
+ v->v.vertex = isl_basic_set_finalize(v->v.vertex);
+ if (!v->v.vertex)
+ goto error;
+ isl_assert(bset->ctx, v->v.vertex->n_eq >= nvar, goto error);
+ v->v.dom = isl_basic_set_copy(v->v.vertex);
+ v->v.dom = isl_basic_set_params(v->v.dom);
+ if (!v->v.dom)
+ goto error;
+
+ if (v->v.dom->n_eq > 0) {
+ free_vertex_list(v);
+ return isl_bool_false;
+ }
+
+ v->next = *list;
+ *list = v;
+
+ return isl_bool_true;
+error:
+ free_vertex_list(v);
+ return isl_bool_error;
+}
+
+/* Compute the parametric vertices and the chamber decomposition
+ * of an empty parametric polytope.
+ */
+static __isl_give isl_vertices *vertices_empty(__isl_keep isl_basic_set *bset)
+{
+ isl_vertices *vertices;
+
+ if (!bset)
+ return NULL;
+
+ vertices = isl_calloc_type(bset->ctx, isl_vertices);
+ if (!vertices)
+ return NULL;
+ vertices->bset = isl_basic_set_copy(bset);
+ vertices->ref = 1;
+
+ vertices->n_vertices = 0;
+ vertices->n_chambers = 0;
+
+ return vertices;
+}
+
+/* Compute the parametric vertices and the chamber decomposition
+ * of the parametric polytope defined using the same constraints
+ * as "bset" in the 0D case.
+ * There is exactly one 0D vertex and a single chamber containing
+ * the vertex.
+ */
+static __isl_give isl_vertices *vertices_0D(__isl_keep isl_basic_set *bset)
+{
+ isl_vertices *vertices;
+
+ if (!bset)
+ return NULL;
+
+ vertices = isl_calloc_type(bset->ctx, isl_vertices);
+ if (!vertices)
+ return NULL;
+ vertices->ref = 1;
+ vertices->bset = isl_basic_set_copy(bset);
+
+ vertices->v = isl_calloc_array(bset->ctx, struct isl_vertex, 1);
+ if (!vertices->v)
+ goto error;
+ vertices->n_vertices = 1;
+ vertices->v[0].vertex = isl_basic_set_copy(bset);
+ vertices->v[0].dom = isl_basic_set_params(isl_basic_set_copy(bset));
+ if (!vertices->v[0].vertex || !vertices->v[0].dom)
+ goto error;
+
+ vertices->c = isl_calloc_array(bset->ctx, struct isl_chamber, 1);
+ if (!vertices->c)
+ goto error;
+ vertices->n_chambers = 1;
+ vertices->c[0].n_vertices = 1;
+ vertices->c[0].vertices = isl_calloc_array(bset->ctx, int, 1);
+ if (!vertices->c[0].vertices)
+ goto error;
+ vertices->c[0].dom = isl_basic_set_copy(vertices->v[0].dom);
+ if (!vertices->c[0].dom)
+ goto error;
+
+ return vertices;
+error:
+ isl_vertices_free(vertices);
+ return NULL;
+}
+
+/* Is the row pointed to by "f" linearly independent of the "n" first
+ * rows in "facets"?
+ */
+static isl_bool is_independent(__isl_keep isl_mat *facets, int n, isl_int *f)
+{
+ isl_size rank;
+
+ if (isl_seq_first_non_zero(f, facets->n_col) < 0)
+ return isl_bool_false;
+
+ isl_seq_cpy(facets->row[n], f, facets->n_col);
+ facets->n_row = n + 1;
+ rank = isl_mat_rank(facets);
+ if (rank < 0)
+ return isl_bool_error;
+
+ return isl_bool_ok(rank == n + 1);
+}
+
+/* Check whether we can select constraint "level", given the current selection
+ * reflected by facets in "tab", the rows of "facets" and the earlier
+ * "selected" elements of "selection".
+ *
+ * If the constraint is (strictly) redundant in the tableau, selecting it would
+ * result in an empty tableau, so it can't be selected.
+ * If the set variable part of the constraint is not linearly independent
+ * of the set variable parts of the already selected constraints,
+ * the constraint cannot be selected.
+ * If selecting the constraint results in an empty tableau, the constraint
+ * cannot be selected.
+ * Finally, if selecting the constraint results in some explicitly
+ * deselected constraints turning into equalities, then the corresponding
+ * vertices have already been generated, so the constraint cannot be selected.
+ */
+static isl_bool can_select(__isl_keep isl_basic_set *bset, int level,
+ struct isl_tab *tab, __isl_keep isl_mat *facets, int selected,
+ int *selection)
+{
+ int i;
+ isl_bool indep;
+ unsigned ovar;
+ struct isl_tab_undo *snap;
+
+ if (isl_tab_is_redundant(tab, level))
+ return isl_bool_false;
+
+ ovar = isl_space_offset(bset->dim, isl_dim_set);
+
+ indep = is_independent(facets, selected, bset->ineq[level] + 1 + ovar);
+ if (indep < 0 || !indep)
+ return indep;
+
+ snap = isl_tab_snap(tab);
+ if (isl_tab_select_facet(tab, level) < 0)
+ return isl_bool_error;
+
+ if (tab->empty) {
+ if (isl_tab_rollback(tab, snap) < 0)
+ return isl_bool_error;
+ return isl_bool_false;
+ }
+
+ for (i = 0; i < level; ++i) {
+ int sgn;
+
+ if (selection[i] != DESELECTED)
+ continue;
+
+ if (isl_tab_is_equality(tab, i))
+ sgn = 0;
+ else if (isl_tab_is_redundant(tab, i))
+ sgn = 1;
+ else
+ sgn = isl_tab_sign_of_max(tab, i);
+ if (sgn < -1)
+ return isl_bool_error;
+ if (sgn <= 0) {
+ if (isl_tab_rollback(tab, snap) < 0)
+ return isl_bool_error;
+ return isl_bool_false;
+ }
+ }
+
+ return isl_bool_true;
+}
+
+/* Compute the parametric vertices and the chamber decomposition
+ * of a parametric polytope that is not full-dimensional.
+ *
+ * Simply map the parametric polytope to a lower dimensional space
+ * and map the resulting vertices back.
+ */
+static __isl_give isl_vertices *lower_dim_vertices(
+ __isl_take isl_basic_set *bset)
+{
+ isl_morph *morph;
+ isl_vertices *vertices;
+
+ morph = isl_basic_set_full_compression(bset);
+ bset = isl_morph_basic_set(isl_morph_copy(morph), bset);
+
+ vertices = isl_basic_set_compute_vertices(bset);
+ isl_basic_set_free(bset);
+
+ morph = isl_morph_inverse(morph);
+
+ vertices = isl_morph_vertices(morph, vertices);
+
+ return vertices;
+}
+
+/* Compute the parametric vertices and the chamber decomposition
+ * of a parametric polytope "bset" that is not full-dimensional.
+ * Additionally, free both "copy" and "tab".
+ */
+static __isl_give isl_vertices *lower_dim_vertices_free(
+ __isl_take isl_basic_set *bset, __isl_take isl_basic_set *copy,
+ struct isl_tab *tab)
+{
+ isl_basic_set_free(copy);
+ isl_tab_free(tab);
+ return lower_dim_vertices(bset);
+}
+
+/* Detect implicit equality constraints in "bset" using the tableau
+ * representation "tab".
+ * Return a copy of "bset" with the implicit equality constraints
+ * made explicit, leaving the original "bset" unmodified.
+ */
+static __isl_give isl_basic_set *detect_implicit_equality_constraints(
+ __isl_keep isl_basic_set *bset, struct isl_tab *tab)
+{
+ if (isl_tab_detect_implicit_equalities(tab) < 0)
+ return NULL;
+
+ bset = isl_basic_set_copy(bset);
+ bset = isl_basic_set_cow(bset);
+ bset = isl_basic_set_update_from_tab(bset, tab);
+
+ return bset;
+}
+
+/* Compute the parametric vertices and the chamber decomposition
+ * of the parametric polytope defined using the same constraints
+ * as "bset". "bset" is assumed to have no existentially quantified
+ * variables.
+ *
+ * The vertices themselves are computed in a fairly simplistic way.
+ * We simply run through all combinations of d constraints,
+ * with d the number of set variables, and check if those d constraints
+ * define a vertex. To avoid the generation of duplicate vertices,
+ * which may happen if a vertex is defined by more than d constraints,
+ * we make sure we only generate the vertex for the d constraints with
+ * smallest index.
+ *
+ * Only potential vertices with a full-dimensional activity domain
+ * are considered. However, if the input has (implicit) equality
+ * constraints among the parameters, then activity domain
+ * should be considered full-dimensional if it does not satisfy
+ * any extra equality constraints beyond those of the input.
+ * The implicit equality constraints of the input are therefore first detected.
+ * If there are any, then the input is mapped to a lower dimensional space
+ * such that the check for full-dimensional activity domains
+ * can be performed with respect to a full-dimensional space.
+ * Note that it is important to leave "bset" unmodified while detecting
+ * equality constraints since the inequality constraints of "bset"
+ * are assumed to correspond to those of the tableau.
+ *
+ * We set up a tableau and keep track of which facets have been
+ * selected. The tableau is marked strict_redundant so that we can be
+ * sure that any constraint that is marked redundant (and that is not
+ * also marked zero) is not an equality.
+ * If a constraint is marked DESELECTED, it means the constraint was
+ * SELECTED before (in combination with the same selection of earlier
+ * constraints). If such a deselected constraint turns out to be an
+ * equality, then any vertex that may still be found with the current
+ * selection has already been generated when the constraint was selected.
+ * A constraint is marked UNSELECTED when there is no way selecting
+ * the constraint could lead to a vertex (in combination with the current
+ * selection of earlier constraints).
+ *
+ * The set variable coefficients of the selected constraints are stored
+ * in the facets matrix.
+ */
+__isl_give isl_vertices *isl_basic_set_compute_vertices(
+ __isl_keep isl_basic_set *bset)
+{
+ struct isl_tab *tab;
+ int level;
+ int init;
+ isl_size n_eq;
+ isl_size nvar;
+ int *selection = NULL;
+ int selected;
+ struct isl_tab_undo **snap = NULL;
+ isl_mat *facets = NULL;
+ struct isl_vertex_list *list = NULL;
+ int n_vertices = 0;
+ isl_vertices *vertices;
+ isl_basic_set *copy;
+ isl_basic_set *test;
+
+ if (!bset)
+ return NULL;
+
+ if (isl_basic_set_plain_is_empty(bset))
+ return vertices_empty(bset);
+
+ if (bset->n_eq != 0)
+ return lower_dim_vertices(isl_basic_set_copy(bset));
+
+ if (isl_basic_set_check_no_locals(bset) < 0)
+ return NULL;
+
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+ if (nvar < 0)
+ return NULL;
+ if (nvar == 0)
+ return vertices_0D(bset);
+
+ copy = isl_basic_set_copy(bset);
+ copy = isl_basic_set_set_rational(copy);
+ if (!copy)
+ return NULL;
+
+ tab = isl_tab_from_basic_set(copy, 0);
+ if (!tab)
+ goto error;
+ tab->strict_redundant = 1;
+
+ if (tab->empty) {
+ vertices = vertices_empty(copy);
+ isl_basic_set_free(copy);
+ isl_tab_free(tab);
+ return vertices;
+ }
+
+ test = detect_implicit_equality_constraints(bset, tab);
+ n_eq = isl_basic_set_n_equality(test);
+ if (n_eq < 0)
+ test = isl_basic_set_free(test);
+ if (n_eq < 0 || n_eq > 0)
+ return lower_dim_vertices_free(test, copy, tab);
+ isl_basic_set_free(test);
+
+ selection = isl_alloc_array(copy->ctx, int, copy->n_ineq);
+ snap = isl_alloc_array(copy->ctx, struct isl_tab_undo *, copy->n_ineq);
+ facets = isl_mat_alloc(copy->ctx, nvar, nvar);
+ if ((copy->n_ineq && (!selection || !snap)) || !facets)
+ goto error;
+
+ level = 0;
+ init = 1;
+ selected = 0;
+
+ while (level >= 0) {
+ if (level >= copy->n_ineq ||
+ (!init && selection[level] != SELECTED)) {
+ --level;
+ init = 0;
+ continue;
+ }
+ if (init) {
+ isl_bool ok;
+ snap[level] = isl_tab_snap(tab);
+ ok = can_select(copy, level, tab, facets, selected,
+ selection);
+ if (ok < 0)
+ goto error;
+ if (ok) {
+ selection[level] = SELECTED;
+ selected++;
+ } else
+ selection[level] = UNSELECTED;
+ } else {
+ selection[level] = DESELECTED;
+ selected--;
+ if (isl_tab_rollback(tab, snap[level]) < 0)
+ goto error;
+ }
+ if (selected == nvar) {
+ if (tab->n_dead == nvar) {
+ isl_bool added = add_vertex(&list, copy, tab);
+ if (added < 0)
+ goto error;
+ if (added)
+ n_vertices++;
+ }
+ init = 0;
+ continue;
+ }
+ ++level;
+ init = 1;
+ }
+
+ isl_mat_free(facets);
+ free(selection);
+ free(snap);
+
+ isl_tab_free(tab);
+
+ vertices = vertices_from_list(copy, n_vertices, list);
+
+ vertices = compute_chambers(copy, vertices);
+
+ return vertices;
+error:
+ free_vertex_list(list);
+ isl_mat_free(facets);
+ free(selection);
+ free(snap);
+ isl_tab_free(tab);
+ isl_basic_set_free(copy);
+ return NULL;
+}
+
+struct isl_chamber_list {
+ struct isl_chamber c;
+ struct isl_chamber_list *next;
+};
+
+static void free_chamber_list(struct isl_chamber_list *list)
+{
+ struct isl_chamber_list *next;
+
+ for (; list; list = next) {
+ next = list->next;
+ isl_basic_set_free(list->c.dom);
+ free(list->c.vertices);
+ free(list);
+ }
+}
+
+/* Check whether the basic set "bset" is a superset of the basic set described
+ * by "tab", i.e., check whether all constraints of "bset" are redundant.
+ */
+static isl_bool bset_covers_tab(__isl_keep isl_basic_set *bset,
+ struct isl_tab *tab)
+{
+ int i;
+
+ if (!bset || !tab)
+ return isl_bool_error;
+
+ for (i = 0; i < bset->n_ineq; ++i) {
+ enum isl_ineq_type type = isl_tab_ineq_type(tab, bset->ineq[i]);
+ switch (type) {
+ case isl_ineq_error: return isl_bool_error;
+ case isl_ineq_redundant: continue;
+ default: return isl_bool_false;
+ }
+ }
+
+ return isl_bool_true;
+}
+
+static __isl_give isl_vertices *vertices_add_chambers(
+ __isl_take isl_vertices *vertices, int n_chambers,
+ struct isl_chamber_list *list)
+{
+ int i;
+ isl_ctx *ctx;
+ struct isl_chamber_list *next;
+
+ ctx = isl_vertices_get_ctx(vertices);
+ vertices->c = isl_alloc_array(ctx, struct isl_chamber, n_chambers);
+ if (!vertices->c)
+ goto error;
+ vertices->n_chambers = n_chambers;
+
+ for (i = 0; list; list = next, i++) {
+ next = list->next;
+ vertices->c[i] = list->c;
+ free(list);
+ }
+
+ return vertices;
+error:
+ isl_vertices_free(vertices);
+ free_chamber_list(list);
+ return NULL;
+}
+
+/* Can "tab" be intersected with "bset" without resulting in
+ * a lower-dimensional set.
+ * "bset" itself is assumed to be full-dimensional.
+ */
+static isl_bool can_intersect(struct isl_tab *tab,
+ __isl_keep isl_basic_set *bset)
+{
+ int i;
+ struct isl_tab_undo *snap;
+
+ if (bset->n_eq > 0)
+ isl_die(isl_basic_set_get_ctx(bset), isl_error_internal,
+ "expecting full-dimensional input",
+ return isl_bool_error);
+
+ if (isl_tab_extend_cons(tab, bset->n_ineq) < 0)
+ return isl_bool_error;
+
+ snap = isl_tab_snap(tab);
+
+ for (i = 0; i < bset->n_ineq; ++i) {
+ enum isl_ineq_type type;
+
+ type = isl_tab_ineq_type(tab, bset->ineq[i]);
+ if (type < 0)
+ return isl_bool_error;
+ if (type == isl_ineq_redundant)
+ continue;
+ if (isl_tab_add_ineq(tab, bset->ineq[i]) < 0)
+ return isl_bool_error;
+ }
+
+ if (isl_tab_detect_implicit_equalities(tab) < 0)
+ return isl_bool_error;
+ if (tab->n_dead) {
+ if (isl_tab_rollback(tab, snap) < 0)
+ return isl_bool_error;
+ return isl_bool_false;
+ }
+
+ return isl_bool_true;
+}
+
+static int add_chamber(struct isl_chamber_list **list,
+ __isl_keep isl_vertices *vertices, struct isl_tab *tab, int *selection)
+{
+ int n_frozen;
+ int i, j;
+ int n_vertices = 0;
+ struct isl_tab_undo *snap;
+ struct isl_chamber_list *c = NULL;
+
+ for (i = 0; i < vertices->n_vertices; ++i)
+ if (selection[i])
+ n_vertices++;
+
+ snap = isl_tab_snap(tab);
+
+ for (i = 0; i < tab->n_con && tab->con[i].frozen; ++i)
+ tab->con[i].frozen = 0;
+ n_frozen = i;
+
+ if (isl_tab_detect_redundant(tab) < 0)
+ return -1;
+
+ c = isl_calloc_type(tab->mat->ctx, struct isl_chamber_list);
+ if (!c)
+ goto error;
+ c->c.vertices = isl_alloc_array(tab->mat->ctx, int, n_vertices);
+ if (n_vertices && !c->c.vertices)
+ goto error;
+ c->c.dom = isl_basic_set_copy(isl_tab_peek_bset(tab));
+ c->c.dom = isl_basic_set_set_rational(c->c.dom);
+ c->c.dom = isl_basic_set_cow(c->c.dom);
+ c->c.dom = isl_basic_set_update_from_tab(c->c.dom, tab);
+ c->c.dom = isl_basic_set_simplify(c->c.dom);
+ c->c.dom = isl_basic_set_finalize(c->c.dom);
+ if (!c->c.dom)
+ goto error;
+
+ c->c.n_vertices = n_vertices;
+
+ for (i = 0, j = 0; i < vertices->n_vertices; ++i)
+ if (selection[i]) {
+ c->c.vertices[j] = i;
+ j++;
+ }
+
+ c->next = *list;
+ *list = c;
+
+ for (i = 0; i < n_frozen; ++i)
+ tab->con[i].frozen = 1;
+
+ if (isl_tab_rollback(tab, snap) < 0)
+ return -1;
+
+ return 0;
+error:
+ free_chamber_list(c);
+ return -1;
+}
+
+struct isl_facet_todo {
+ struct isl_tab *tab; /* A tableau representation of the facet */
+ isl_basic_set *bset; /* A normalized basic set representation */
+ isl_vec *constraint; /* Constraint pointing to the other side */
+ struct isl_facet_todo *next;
+};
+
+static void free_todo(struct isl_facet_todo *todo)
+{
+ while (todo) {
+ struct isl_facet_todo *next = todo->next;
+
+ isl_tab_free(todo->tab);
+ isl_basic_set_free(todo->bset);
+ isl_vec_free(todo->constraint);
+ free(todo);
+
+ todo = next;
+ }
+}
+
+static struct isl_facet_todo *create_todo(struct isl_tab *tab, int con)
+{
+ int i;
+ int n_frozen;
+ struct isl_tab_undo *snap;
+ struct isl_facet_todo *todo;
+
+ snap = isl_tab_snap(tab);
+
+ for (i = 0; i < tab->n_con && tab->con[i].frozen; ++i)
+ tab->con[i].frozen = 0;
+ n_frozen = i;
+
+ if (isl_tab_detect_redundant(tab) < 0)
+ return NULL;
+
+ todo = isl_calloc_type(tab->mat->ctx, struct isl_facet_todo);
+ if (!todo)
+ return NULL;
+
+ todo->constraint = isl_vec_alloc(tab->mat->ctx, 1 + tab->n_var);
+ if (!todo->constraint)
+ goto error;
+ isl_seq_neg(todo->constraint->el, tab->bmap->ineq[con], 1 + tab->n_var);
+ todo->bset = isl_basic_set_copy(isl_tab_peek_bset(tab));
+ todo->bset = isl_basic_set_set_rational(todo->bset);
+ todo->bset = isl_basic_set_cow(todo->bset);
+ todo->bset = isl_basic_set_update_from_tab(todo->bset, tab);
+ todo->bset = isl_basic_set_simplify(todo->bset);
+ todo->bset = isl_basic_set_sort_constraints(todo->bset);
+ if (!todo->bset)
+ goto error;
+ ISL_F_SET(todo->bset, ISL_BASIC_SET_NO_REDUNDANT);
+ todo->tab = isl_tab_dup(tab);
+ if (!todo->tab)
+ goto error;
+
+ for (i = 0; i < n_frozen; ++i)
+ tab->con[i].frozen = 1;
+
+ if (isl_tab_rollback(tab, snap) < 0)
+ goto error;
+
+ return todo;
+error:
+ free_todo(todo);
+ return NULL;
+}
+
+/* Create todo items for all interior facets of the chamber represented
+ * by "tab" and collect them in "next".
+ */
+static int init_todo(struct isl_facet_todo **next, struct isl_tab *tab)
+{
+ int i;
+ struct isl_tab_undo *snap;
+ struct isl_facet_todo *todo;
+
+ snap = isl_tab_snap(tab);
+
+ for (i = 0; i < tab->n_con; ++i) {
+ if (tab->con[i].frozen)
+ continue;
+ if (tab->con[i].is_redundant)
+ continue;
+
+ if (isl_tab_select_facet(tab, i) < 0)
+ return -1;
+
+ todo = create_todo(tab, i);
+ if (!todo)
+ return -1;
+
+ todo->next = *next;
+ *next = todo;
+
+ if (isl_tab_rollback(tab, snap) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Does the linked list contain a todo item that is the opposite of "todo".
+ * If so, return 1 and remove the opposite todo item.
+ */
+static int has_opposite(struct isl_facet_todo *todo,
+ struct isl_facet_todo **list)
+{
+ for (; *list; list = &(*list)->next) {
+ int eq;
+ eq = isl_basic_set_plain_is_equal(todo->bset, (*list)->bset);
+ if (eq < 0)
+ return -1;
+ if (!eq)
+ continue;
+ todo = *list;
+ *list = todo->next;
+ todo->next = NULL;
+ free_todo(todo);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Create todo items for all interior facets of the chamber represented
+ * by "tab" and collect them in first->next, taking care to cancel
+ * opposite todo items.
+ */
+static int update_todo(struct isl_facet_todo *first, struct isl_tab *tab)
+{
+ int i;
+ struct isl_tab_undo *snap;
+ struct isl_facet_todo *todo;
+
+ snap = isl_tab_snap(tab);
+
+ for (i = 0; i < tab->n_con; ++i) {
+ int drop;
+
+ if (tab->con[i].frozen)
+ continue;
+ if (tab->con[i].is_redundant)
+ continue;
+
+ if (isl_tab_select_facet(tab, i) < 0)
+ return -1;
+
+ todo = create_todo(tab, i);
+ if (!todo)
+ return -1;
+
+ drop = has_opposite(todo, &first->next);
+ if (drop < 0)
+ return -1;
+
+ if (drop)
+ free_todo(todo);
+ else {
+ todo->next = first->next;
+ first->next = todo;
+ }
+
+ if (isl_tab_rollback(tab, snap) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Compute the chamber decomposition of the parametric polytope respresented
+ * by "bset" given the parametric vertices and their activity domains.
+ *
+ * We are only interested in full-dimensional chambers.
+ * Each of these chambers is the intersection of the activity domains of
+ * one or more vertices and the union of all chambers is equal to the
+ * projection of the entire parametric polytope onto the parameter space.
+ *
+ * We first create an initial chamber by intersecting as many activity
+ * domains as possible without ending up with an empty or lower-dimensional
+ * set. As a minor optimization, we only consider those activity domains
+ * that contain some arbitrary point.
+ *
+ * For each of the interior facets of the chamber, we construct a todo item,
+ * containing the facet and a constraint containing the other side of the facet,
+ * for constructing the chamber on the other side.
+ * While their are any todo items left, we pick a todo item and
+ * create the required chamber by intersecting all activity domains
+ * that contain the facet and have a full-dimensional intersection with
+ * the other side of the facet. For each of the interior facets, we
+ * again create todo items, taking care to cancel opposite todo items.
+ */
+static __isl_give isl_vertices *compute_chambers(__isl_take isl_basic_set *bset,
+ __isl_take isl_vertices *vertices)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_size n_eq;
+ isl_vec *sample = NULL;
+ struct isl_tab *tab = NULL;
+ struct isl_tab_undo *snap;
+ int *selection = NULL;
+ int n_chambers = 0;
+ struct isl_chamber_list *list = NULL;
+ struct isl_facet_todo *todo = NULL;
+
+ if (!bset || !vertices)
+ goto error;
+
+ ctx = isl_vertices_get_ctx(vertices);
+ selection = isl_alloc_array(ctx, int, vertices->n_vertices);
+ if (vertices->n_vertices && !selection)
+ goto error;
+
+ bset = isl_basic_set_params(bset);
+ n_eq = isl_basic_set_n_equality(bset);
+ if (n_eq < 0)
+ goto error;
+ if (n_eq > 0)
+ isl_die(isl_basic_set_get_ctx(bset), isl_error_internal,
+ "expecting full-dimensional input", goto error);
+
+ tab = isl_tab_from_basic_set(bset, 1);
+ if (!tab)
+ goto error;
+ for (i = 0; i < bset->n_ineq; ++i)
+ if (isl_tab_freeze_constraint(tab, i) < 0)
+ goto error;
+ isl_basic_set_free(bset);
+
+ snap = isl_tab_snap(tab);
+
+ sample = isl_tab_get_sample_value(tab);
+
+ for (i = 0; i < vertices->n_vertices; ++i) {
+ selection[i] = isl_basic_set_contains(vertices->v[i].dom, sample);
+ if (selection[i] < 0)
+ goto error;
+ if (!selection[i])
+ continue;
+ selection[i] = can_intersect(tab, vertices->v[i].dom);
+ if (selection[i] < 0)
+ goto error;
+ }
+
+ if (isl_tab_detect_redundant(tab) < 0)
+ goto error;
+
+ if (add_chamber(&list, vertices, tab, selection) < 0)
+ goto error;
+ n_chambers++;
+
+ if (init_todo(&todo, tab) < 0)
+ goto error;
+
+ while (todo) {
+ struct isl_facet_todo *next;
+
+ if (isl_tab_rollback(tab, snap) < 0)
+ goto error;
+
+ if (isl_tab_add_ineq(tab, todo->constraint->el) < 0)
+ goto error;
+ if (isl_tab_freeze_constraint(tab, tab->n_con - 1) < 0)
+ goto error;
+
+ for (i = 0; i < vertices->n_vertices; ++i) {
+ selection[i] = bset_covers_tab(vertices->v[i].dom,
+ todo->tab);
+ if (selection[i] < 0)
+ goto error;
+ if (!selection[i])
+ continue;
+ selection[i] = can_intersect(tab, vertices->v[i].dom);
+ if (selection[i] < 0)
+ goto error;
+ }
+
+ if (isl_tab_detect_redundant(tab) < 0)
+ goto error;
+
+ if (add_chamber(&list, vertices, tab, selection) < 0)
+ goto error;
+ n_chambers++;
+
+ if (update_todo(todo, tab) < 0)
+ goto error;
+
+ next = todo->next;
+ todo->next = NULL;
+ free_todo(todo);
+ todo = next;
+ }
+
+ isl_vec_free(sample);
+
+ isl_tab_free(tab);
+ free(selection);
+
+ vertices = vertices_add_chambers(vertices, n_chambers, list);
+
+ for (i = 0; vertices && i < vertices->n_vertices; ++i) {
+ isl_basic_set_free(vertices->v[i].dom);
+ vertices->v[i].dom = NULL;
+ }
+
+ return vertices;
+error:
+ free_chamber_list(list);
+ free_todo(todo);
+ isl_vec_free(sample);
+ isl_tab_free(tab);
+ free(selection);
+ if (!tab)
+ isl_basic_set_free(bset);
+ isl_vertices_free(vertices);
+ return NULL;
+}
+
+isl_ctx *isl_vertex_get_ctx(__isl_keep isl_vertex *vertex)
+{
+ return vertex ? isl_vertices_get_ctx(vertex->vertices) : NULL;
+}
+
+isl_size isl_vertex_get_id(__isl_keep isl_vertex *vertex)
+{
+ return vertex ? vertex->id : isl_size_error;
+}
+
+/* Return the activity domain of the vertex "vertex".
+ */
+__isl_give isl_basic_set *isl_vertex_get_domain(__isl_keep isl_vertex *vertex)
+{
+ struct isl_vertex *v;
+
+ if (!vertex)
+ return NULL;
+
+ v = &vertex->vertices->v[vertex->id];
+ if (!v->dom) {
+ v->dom = isl_basic_set_copy(v->vertex);
+ v->dom = isl_basic_set_params(v->dom);
+ v->dom = isl_basic_set_set_integral(v->dom);
+ }
+
+ return isl_basic_set_copy(v->dom);
+}
+
+/* Return a multiple quasi-affine expression describing the vertex "vertex"
+ * in terms of the parameters,
+ */
+__isl_give isl_multi_aff *isl_vertex_get_expr(__isl_keep isl_vertex *vertex)
+{
+ struct isl_vertex *v;
+ isl_basic_set *bset;
+
+ if (!vertex)
+ return NULL;
+
+ v = &vertex->vertices->v[vertex->id];
+
+ bset = isl_basic_set_copy(v->vertex);
+ return isl_multi_aff_from_basic_set_equalities(bset);
+}
+
+static __isl_give isl_vertex *isl_vertex_alloc(__isl_take isl_vertices *vertices,
+ int id)
+{
+ isl_ctx *ctx;
+ isl_vertex *vertex;
+
+ if (!vertices)
+ return NULL;
+
+ ctx = isl_vertices_get_ctx(vertices);
+ vertex = isl_alloc_type(ctx, isl_vertex);
+ if (!vertex)
+ goto error;
+
+ vertex->vertices = vertices;
+ vertex->id = id;
+
+ return vertex;
+error:
+ isl_vertices_free(vertices);
+ return NULL;
+}
+
+__isl_null isl_vertex *isl_vertex_free(__isl_take isl_vertex *vertex)
+{
+ if (!vertex)
+ return NULL;
+ isl_vertices_free(vertex->vertices);
+ free(vertex);
+
+ return NULL;
+}
+
+isl_ctx *isl_cell_get_ctx(__isl_keep isl_cell *cell)
+{
+ return cell ? cell->dom->ctx : NULL;
+}
+
+__isl_give isl_basic_set *isl_cell_get_domain(__isl_keep isl_cell *cell)
+{
+ return cell ? isl_basic_set_copy(cell->dom) : NULL;
+}
+
+static __isl_give isl_cell *isl_cell_alloc(__isl_take isl_vertices *vertices,
+ __isl_take isl_basic_set *dom, int id)
+{
+ int i;
+ isl_cell *cell = NULL;
+
+ if (!vertices || !dom)
+ goto error;
+
+ cell = isl_calloc_type(dom->ctx, isl_cell);
+ if (!cell)
+ goto error;
+
+ cell->n_vertices = vertices->c[id].n_vertices;
+ cell->ids = isl_alloc_array(dom->ctx, int, cell->n_vertices);
+ if (cell->n_vertices && !cell->ids)
+ goto error;
+ for (i = 0; i < cell->n_vertices; ++i)
+ cell->ids[i] = vertices->c[id].vertices[i];
+ cell->vertices = vertices;
+ cell->dom = dom;
+
+ return cell;
+error:
+ isl_cell_free(cell);
+ isl_vertices_free(vertices);
+ isl_basic_set_free(dom);
+ return NULL;
+}
+
+__isl_null isl_cell *isl_cell_free(__isl_take isl_cell *cell)
+{
+ if (!cell)
+ return NULL;
+
+ isl_vertices_free(cell->vertices);
+ free(cell->ids);
+ isl_basic_set_free(cell->dom);
+ free(cell);
+
+ return NULL;
+}
+
+/* Create a tableau of the cone obtained by first homogenizing the given
+ * polytope and then making all inequalities strict by setting the
+ * constant term to -1.
+ */
+static struct isl_tab *tab_for_shifted_cone(__isl_keep isl_basic_set *bset)
+{
+ int i;
+ isl_vec *c = NULL;
+ struct isl_tab *tab;
+ isl_size total;
+
+ total = isl_basic_set_dim(bset, isl_dim_all);
+ if (total < 0)
+ return NULL;
+ tab = isl_tab_alloc(bset->ctx, bset->n_eq + bset->n_ineq + 1,
+ 1 + total, 0);
+ if (!tab)
+ return NULL;
+ tab->rational = ISL_F_ISSET(bset, ISL_BASIC_SET_RATIONAL);
+ if (ISL_F_ISSET(bset, ISL_BASIC_MAP_EMPTY)) {
+ if (isl_tab_mark_empty(tab) < 0)
+ goto error;
+ return tab;
+ }
+
+ c = isl_vec_alloc(bset->ctx, 1 + 1 + total);
+ if (!c)
+ goto error;
+
+ isl_int_set_si(c->el[0], 0);
+ for (i = 0; i < bset->n_eq; ++i) {
+ isl_seq_cpy(c->el + 1, bset->eq[i], c->size - 1);
+ if (isl_tab_add_eq(tab, c->el) < 0)
+ goto error;
+ }
+
+ isl_int_set_si(c->el[0], -1);
+ for (i = 0; i < bset->n_ineq; ++i) {
+ isl_seq_cpy(c->el + 1, bset->ineq[i], c->size - 1);
+ if (isl_tab_add_ineq(tab, c->el) < 0)
+ goto error;
+ if (tab->empty) {
+ isl_vec_free(c);
+ return tab;
+ }
+ }
+
+ isl_seq_clr(c->el + 1, c->size - 1);
+ isl_int_set_si(c->el[1], 1);
+ if (isl_tab_add_ineq(tab, c->el) < 0)
+ goto error;
+
+ isl_vec_free(c);
+ return tab;
+error:
+ isl_vec_free(c);
+ isl_tab_free(tab);
+ return NULL;
+}
+
+/* Compute an interior point of "bset" by selecting an interior
+ * point in homogeneous space and projecting the point back down.
+ */
+static __isl_give isl_vec *isl_basic_set_interior_point(
+ __isl_keep isl_basic_set *bset)
+{
+ isl_vec *vec;
+ struct isl_tab *tab;
+
+ tab = tab_for_shifted_cone(bset);
+ vec = isl_tab_get_sample_value(tab);
+ isl_tab_free(tab);
+ if (!vec)
+ return NULL;
+
+ isl_seq_cpy(vec->el, vec->el + 1, vec->size - 1);
+ vec->size--;
+
+ return vec;
+}
+
+/* Call "fn" on all chambers of the parametric polytope with the shared
+ * facets of neighboring chambers only appearing in one of the chambers.
+ *
+ * We pick an interior point from one of the chambers and then make
+ * all constraints that do not satisfy this point strict.
+ * For constraints that saturate the interior point, the sign
+ * of the first non-zero coefficient is used to determine which
+ * of the two (internal) constraints should be tightened.
+ */
+isl_stat isl_vertices_foreach_disjoint_cell(__isl_keep isl_vertices *vertices,
+ isl_stat (*fn)(__isl_take isl_cell *cell, void *user), void *user)
+{
+ int i;
+ isl_vec *vec;
+ isl_cell *cell;
+
+ if (!vertices)
+ return isl_stat_error;
+
+ if (vertices->n_chambers == 0)
+ return isl_stat_ok;
+
+ if (vertices->n_chambers == 1) {
+ isl_basic_set *dom = isl_basic_set_copy(vertices->c[0].dom);
+ dom = isl_basic_set_set_integral(dom);
+ cell = isl_cell_alloc(isl_vertices_copy(vertices), dom, 0);
+ if (!cell)
+ return isl_stat_error;
+ return fn(cell, user);
+ }
+
+ vec = isl_basic_set_interior_point(vertices->c[0].dom);
+ if (!vec)
+ return isl_stat_error;
+
+ for (i = 0; i < vertices->n_chambers; ++i) {
+ int r;
+ isl_basic_set *dom = isl_basic_set_copy(vertices->c[i].dom);
+ if (i)
+ dom = isl_basic_set_tighten_outward(dom, vec);
+ dom = isl_basic_set_set_integral(dom);
+ cell = isl_cell_alloc(isl_vertices_copy(vertices), dom, i);
+ if (!cell)
+ goto error;
+ r = fn(cell, user);
+ if (r < 0)
+ goto error;
+ }
+
+ isl_vec_free(vec);
+
+ return isl_stat_ok;
+error:
+ isl_vec_free(vec);
+ return isl_stat_error;
+}
+
+isl_stat isl_vertices_foreach_cell(__isl_keep isl_vertices *vertices,
+ isl_stat (*fn)(__isl_take isl_cell *cell, void *user), void *user)
+{
+ int i;
+ isl_cell *cell;
+
+ if (!vertices)
+ return isl_stat_error;
+
+ if (vertices->n_chambers == 0)
+ return isl_stat_ok;
+
+ for (i = 0; i < vertices->n_chambers; ++i) {
+ isl_stat r;
+ isl_basic_set *dom = isl_basic_set_copy(vertices->c[i].dom);
+
+ cell = isl_cell_alloc(isl_vertices_copy(vertices), dom, i);
+ if (!cell)
+ return isl_stat_error;
+
+ r = fn(cell, user);
+ if (r < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+isl_stat isl_vertices_foreach_vertex(__isl_keep isl_vertices *vertices,
+ isl_stat (*fn)(__isl_take isl_vertex *vertex, void *user), void *user)
+{
+ int i;
+ isl_vertex *vertex;
+
+ if (!vertices)
+ return isl_stat_error;
+
+ if (vertices->n_vertices == 0)
+ return isl_stat_ok;
+
+ for (i = 0; i < vertices->n_vertices; ++i) {
+ isl_stat r;
+
+ vertex = isl_vertex_alloc(isl_vertices_copy(vertices), i);
+ if (!vertex)
+ return isl_stat_error;
+
+ r = fn(vertex, user);
+ if (r < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+isl_stat isl_cell_foreach_vertex(__isl_keep isl_cell *cell,
+ isl_stat (*fn)(__isl_take isl_vertex *vertex, void *user), void *user)
+{
+ int i;
+ isl_vertex *vertex;
+
+ if (!cell)
+ return isl_stat_error;
+
+ if (cell->n_vertices == 0)
+ return isl_stat_ok;
+
+ for (i = 0; i < cell->n_vertices; ++i) {
+ isl_stat r;
+
+ vertex = isl_vertex_alloc(isl_vertices_copy(cell->vertices),
+ cell->ids[i]);
+ if (!vertex)
+ return isl_stat_error;
+
+ r = fn(vertex, user);
+ if (r < 0)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+isl_ctx *isl_vertices_get_ctx(__isl_keep isl_vertices *vertices)
+{
+ return vertices ? vertices->bset->ctx : NULL;
+}
+
+isl_size isl_vertices_get_n_vertices(__isl_keep isl_vertices *vertices)
+{
+ return vertices ? vertices->n_vertices : isl_size_error;
+}
+
+__isl_give isl_vertices *isl_morph_vertices(__isl_take isl_morph *morph,
+ __isl_take isl_vertices *vertices)
+{
+ int i;
+ isl_morph *param_morph = NULL;
+
+ if (!morph || !vertices)
+ goto error;
+
+ isl_assert(vertices->bset->ctx, vertices->ref == 1, goto error);
+
+ param_morph = isl_morph_copy(morph);
+ param_morph = isl_morph_dom_params(param_morph);
+ param_morph = isl_morph_ran_params(param_morph);
+
+ for (i = 0; i < vertices->n_vertices; ++i) {
+ vertices->v[i].dom = isl_morph_basic_set(
+ isl_morph_copy(param_morph), vertices->v[i].dom);
+ vertices->v[i].vertex = isl_morph_basic_set(
+ isl_morph_copy(morph), vertices->v[i].vertex);
+ if (!vertices->v[i].vertex)
+ goto error;
+ }
+
+ for (i = 0; i < vertices->n_chambers; ++i) {
+ vertices->c[i].dom = isl_morph_basic_set(
+ isl_morph_copy(param_morph), vertices->c[i].dom);
+ if (!vertices->c[i].dom)
+ goto error;
+ }
+
+ isl_morph_free(param_morph);
+ isl_morph_free(morph);
+ return vertices;
+error:
+ isl_morph_free(param_morph);
+ isl_morph_free(morph);
+ isl_vertices_free(vertices);
+ return NULL;
+}
+
+/* Construct a simplex isl_cell spanned by the vertices with indices in
+ * "simplex_ids" and "other_ids" and call "fn" on this isl_cell.
+ */
+static isl_stat call_on_simplex(__isl_keep isl_cell *cell,
+ int *simplex_ids, int n_simplex, int *other_ids, int n_other,
+ isl_stat (*fn)(__isl_take isl_cell *simplex, void *user), void *user)
+{
+ int i;
+ isl_ctx *ctx;
+ struct isl_cell *simplex;
+
+ ctx = isl_cell_get_ctx(cell);
+
+ simplex = isl_calloc_type(ctx, struct isl_cell);
+ if (!simplex)
+ return isl_stat_error;
+ simplex->vertices = isl_vertices_copy(cell->vertices);
+ if (!simplex->vertices)
+ goto error;
+ simplex->dom = isl_basic_set_copy(cell->dom);
+ if (!simplex->dom)
+ goto error;
+ simplex->n_vertices = n_simplex + n_other;
+ simplex->ids = isl_alloc_array(ctx, int, simplex->n_vertices);
+ if (!simplex->ids)
+ goto error;
+
+ for (i = 0; i < n_simplex; ++i)
+ simplex->ids[i] = simplex_ids[i];
+ for (i = 0; i < n_other; ++i)
+ simplex->ids[n_simplex + i] = other_ids[i];
+
+ return fn(simplex, user);
+error:
+ isl_cell_free(simplex);
+ return isl_stat_error;
+}
+
+/* Check whether the parametric vertex described by "vertex"
+ * lies on the facet corresponding to constraint "facet" of "bset".
+ * The isl_vec "v" is a temporary vector than can be used by this function.
+ *
+ * We eliminate the variables from the facet constraint using the
+ * equalities defining the vertex and check if the result is identical
+ * to zero.
+ *
+ * It would probably be better to keep track of the constraints defining
+ * a vertex during the vertex construction so that we could simply look
+ * it up here.
+ */
+static int vertex_on_facet(__isl_keep isl_basic_set *vertex,
+ __isl_keep isl_basic_set *bset, int facet, __isl_keep isl_vec *v)
+{
+ int i;
+ isl_int m;
+
+ isl_seq_cpy(v->el, bset->ineq[facet], v->size);
+
+ isl_int_init(m);
+ for (i = 0; i < vertex->n_eq; ++i) {
+ int k = isl_seq_last_non_zero(vertex->eq[i], v->size);
+ isl_seq_elim(v->el, vertex->eq[i], k, v->size, &m);
+ }
+ isl_int_clear(m);
+
+ return isl_seq_first_non_zero(v->el, v->size) == -1;
+}
+
+/* Triangulate the polytope spanned by the vertices with ids
+ * in "simplex_ids" and "other_ids" and call "fn" on each of
+ * the resulting simplices.
+ * If the input polytope is already a simplex, we simply call "fn".
+ * Otherwise, we pick a point from "other_ids" and add it to "simplex_ids".
+ * Then we consider each facet of "bset" that does not contain the point
+ * we just picked, but does contain some of the other points in "other_ids"
+ * and call ourselves recursively on the polytope spanned by the new
+ * "simplex_ids" and those points in "other_ids" that lie on the facet.
+ */
+static isl_stat triangulate(__isl_keep isl_cell *cell, __isl_keep isl_vec *v,
+ int *simplex_ids, int n_simplex, int *other_ids, int n_other,
+ isl_stat (*fn)(__isl_take isl_cell *simplex, void *user), void *user)
+{
+ int i, j, k;
+ isl_size d, nparam;
+ int *ids;
+ isl_ctx *ctx;
+ isl_basic_set *vertex;
+ isl_basic_set *bset;
+
+ ctx = isl_cell_get_ctx(cell);
+ d = isl_basic_set_dim(cell->vertices->bset, isl_dim_set);
+ nparam = isl_basic_set_dim(cell->vertices->bset, isl_dim_param);
+ if (d < 0 || nparam < 0)
+ return isl_stat_error;
+
+ if (n_simplex + n_other == d + 1)
+ return call_on_simplex(cell, simplex_ids, n_simplex,
+ other_ids, n_other, fn, user);
+
+ simplex_ids[n_simplex] = other_ids[0];
+ vertex = cell->vertices->v[other_ids[0]].vertex;
+ bset = cell->vertices->bset;
+
+ ids = isl_alloc_array(ctx, int, n_other - 1);
+ if (!ids)
+ goto error;
+ for (i = 0; i < bset->n_ineq; ++i) {
+ if (isl_seq_first_non_zero(bset->ineq[i] + 1 + nparam, d) == -1)
+ continue;
+ if (vertex_on_facet(vertex, bset, i, v))
+ continue;
+
+ for (j = 1, k = 0; j < n_other; ++j) {
+ isl_basic_set *ov;
+ ov = cell->vertices->v[other_ids[j]].vertex;
+ if (vertex_on_facet(ov, bset, i, v))
+ ids[k++] = other_ids[j];
+ }
+ if (k == 0)
+ continue;
+
+ if (triangulate(cell, v, simplex_ids, n_simplex + 1,
+ ids, k, fn, user) < 0)
+ goto error;
+ }
+ free(ids);
+
+ return isl_stat_ok;
+error:
+ free(ids);
+ return isl_stat_error;
+}
+
+/* Triangulate the given cell and call "fn" on each of the resulting
+ * simplices.
+ */
+isl_stat isl_cell_foreach_simplex(__isl_take isl_cell *cell,
+ isl_stat (*fn)(__isl_take isl_cell *simplex, void *user), void *user)
+{
+ isl_size d, total;
+ isl_stat r;
+ isl_ctx *ctx;
+ isl_vec *v = NULL;
+ int *simplex_ids = NULL;
+
+ if (!cell)
+ return isl_stat_error;
+
+ d = isl_basic_set_dim(cell->vertices->bset, isl_dim_set);
+ total = isl_basic_set_dim(cell->vertices->bset, isl_dim_all);
+ if (d < 0 || total < 0)
+ return isl_stat_error;
+
+ if (cell->n_vertices == d + 1)
+ return fn(cell, user);
+
+ ctx = isl_cell_get_ctx(cell);
+ simplex_ids = isl_alloc_array(ctx, int, d + 1);
+ if (!simplex_ids)
+ goto error;
+
+ v = isl_vec_alloc(ctx, 1 + total);
+ if (!v)
+ goto error;
+
+ r = triangulate(cell, v, simplex_ids, 0,
+ cell->ids, cell->n_vertices, fn, user);
+
+ isl_vec_free(v);
+ free(simplex_ids);
+
+ isl_cell_free(cell);
+
+ return r;
+error:
+ free(simplex_ids);
+ isl_vec_free(v);
+ isl_cell_free(cell);
+ return isl_stat_error;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vertices_private.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vertices_private.h
new file mode 100644
index 00000000000..37e64075508
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_vertices_private.h
@@ -0,0 +1,71 @@
+#ifndef ISL_VERTICES_PRIVATE_H
+#define ISL_VERTICES_PRIVATE_H
+
+#include <isl/set.h>
+#include <isl/vertices.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct isl_morph;
+
+/* A parametric vertex. "vertex" contains the actual description
+ * of the vertex as a singleton parametric set. "dom" is the projection
+ * of "vertex" onto the parameter space, i.e., the activity domain
+ * of the vertex.
+ * During the construction of vertices and chambers, the activity domain
+ * of every parametric vertex is full-dimensional.
+ */
+struct isl_vertex {
+ isl_basic_set *dom;
+ isl_basic_set *vertex;
+};
+
+/* A chamber in the chamber decomposition. The indices of the "n_vertices"
+ * active vertices are stored in "vertices".
+ */
+struct isl_chamber {
+ int n_vertices;
+ int *vertices;
+ isl_basic_set *dom;
+};
+
+struct isl_vertices {
+ int ref;
+
+ /* The rational basic set spanned by the vertices. */
+ isl_basic_set *bset;
+
+ int n_vertices;
+ struct isl_vertex *v;
+
+ int n_chambers;
+ struct isl_chamber *c;
+};
+
+struct isl_cell {
+ int n_vertices;
+ int *ids;
+ isl_vertices *vertices;
+ isl_basic_set *dom;
+};
+
+struct isl_external_vertex {
+ isl_vertices *vertices;
+ int id;
+};
+
+isl_stat isl_vertices_foreach_disjoint_cell(__isl_keep isl_vertices *vertices,
+ isl_stat (*fn)(__isl_take isl_cell *cell, void *user), void *user);
+isl_stat isl_cell_foreach_simplex(__isl_take isl_cell *cell,
+ isl_stat (*fn)(__isl_take isl_cell *simplex, void *user), void *user);
+
+__isl_give isl_vertices *isl_morph_vertices(__isl_take struct isl_morph *morph,
+ __isl_take isl_vertices *vertices);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_yaml.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_yaml.h
new file mode 100644
index 00000000000..4d2c442ac50
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/isl_yaml.h
@@ -0,0 +1,18 @@
+#ifndef ISL_YAML_H
+#define ISL_YAML_H
+
+#define ISL_YAML_INDENT_FLOW -1
+
+enum isl_yaml_state {
+ isl_yaml_none,
+ isl_yaml_mapping_first_key_start,
+ isl_yaml_mapping_key_start,
+ isl_yaml_mapping_key,
+ isl_yaml_mapping_val_start,
+ isl_yaml_mapping_val,
+ isl_yaml_sequence_first_start,
+ isl_yaml_sequence_start,
+ isl_yaml_sequence
+};
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/opt_type.h b/contrib/libs/llvm14/tools/polly/lib/External/isl/opt_type.h
new file mode 100644
index 00000000000..9505e72c20b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/opt_type.h
@@ -0,0 +1,16 @@
+#define NO_LOC
+#ifdef HAS_TYPE
+#define OPT_TYPE_PARAM , enum isl_fold type
+#define OPT_TYPE_PARAM_FIRST enum isl_fold type,
+#define OPT_TYPE_ARG(loc) , loc type
+#define OPT_TYPE_ARG_FIRST(loc) loc type,
+#define OPT_SET_TYPE(loc,val) loc type = (val);
+#define OPT_EQUAL_TYPES(loc1, loc2) ((loc1 type) == (loc2 type))
+#else
+#define OPT_TYPE_PARAM
+#define OPT_TYPE_PARAM_FIRST
+#define OPT_TYPE_ARG(loc)
+#define OPT_TYPE_ARG_FIRST(loc)
+#define OPT_SET_TYPE(loc,val)
+#define OPT_EQUAL_TYPES(loc1, loc2) 1
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/print.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/print.c
new file mode 100644
index 00000000000..a87f9221a37
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/print.c
@@ -0,0 +1,105 @@
+#include <isl/ctx.h>
+#include <isl/id.h>
+#include <isl/space.h>
+#include <isl/local_space.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl/polynomial.h>
+#include <isl/constraint.h>
+#include <isl/aff.h>
+#include <isl/ast.h>
+#include <isl/printer.h>
+#include <isl/val.h>
+
+#undef BASE
+#define BASE id
+#include <print_templ.c>
+#undef BASE
+#define BASE multi_id
+#include <print_templ.c>
+#undef BASE
+#define BASE val
+#include <print_templ.c>
+#undef BASE
+#define BASE multi_val
+#include <print_templ.c>
+#undef BASE
+#define BASE space
+#include <print_templ.c>
+#undef BASE
+#define BASE local_space
+#include <print_templ.c>
+#undef BASE
+#define BASE basic_set
+#include <print_templ.c>
+#undef BASE
+#define BASE basic_map
+#include <print_templ.c>
+#undef BASE
+#define BASE set
+#include <print_templ.c>
+#undef BASE
+#define BASE map
+#include <print_templ.c>
+#undef BASE
+#define BASE union_set
+#include <print_templ.c>
+#undef BASE
+#define BASE union_map
+#include <print_templ.c>
+#undef BASE
+#define BASE qpolynomial
+#include <print_templ.c>
+#undef BASE
+#define BASE qpolynomial_fold
+#include <print_templ.c>
+#undef BASE
+#define BASE pw_qpolynomial
+#include <print_templ.c>
+#undef BASE
+#define BASE pw_qpolynomial_fold
+#include <print_templ.c>
+#undef BASE
+#define BASE union_pw_qpolynomial
+#include <print_templ.c>
+#undef BASE
+#define BASE union_pw_qpolynomial_fold
+#include <print_templ.c>
+#undef BASE
+#define BASE constraint
+#include <print_templ.c>
+#undef BASE
+#define BASE aff
+#include <print_templ.c>
+#undef BASE
+#define BASE pw_aff
+#include <print_templ.c>
+#undef BASE
+#define BASE multi_aff
+#include <print_templ.c>
+#undef BASE
+#define BASE pw_multi_aff
+#include <print_templ.c>
+#undef BASE
+#define BASE union_pw_multi_aff
+#include <print_templ.c>
+#undef BASE
+#define BASE multi_pw_aff
+#include <print_templ.c>
+#undef BASE
+#define BASE union_pw_aff
+#include <print_templ.c>
+#undef BASE
+#define BASE multi_union_pw_aff
+#include <print_templ.c>
+#undef BASE
+#define BASE point
+#include <print_templ.c>
+#undef BASE
+#define BASE ast_expr
+#include <print_templ_yaml.c>
+#undef BASE
+#define BASE ast_node
+#include <print_templ_yaml.c>
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/print_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/print_templ.c
new file mode 100644
index 00000000000..9ed93f14385
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/print_templ.c
@@ -0,0 +1,42 @@
+#include <isl_printer_private.h>
+
+#define xCAT(A,B) A ## B
+#define CAT(A,B) xCAT(A,B)
+#undef TYPE
+#define TYPE CAT(isl_,BASE)
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+#ifndef PRINT_DUMP_DEFAULT
+#define PRINT_DUMP_DEFAULT 1
+#endif
+
+void FN(TYPE,dump)(__isl_keep TYPE *obj)
+{
+ isl_printer *p;
+
+ if (!obj)
+ return;
+ p = isl_printer_to_file(FN(TYPE,get_ctx)(obj), stderr);
+ p = isl_printer_set_dump(p, PRINT_DUMP_DEFAULT);
+ p = FN(isl_printer_print,BASE)(p, obj);
+ p = isl_printer_end_line(p);
+ isl_printer_free(p);
+}
+
+#undef PRINT_DUMP_DEFAULT
+
+__isl_give char *FN(TYPE,to_str)(__isl_keep TYPE *obj)
+{
+ isl_printer *p;
+ char *s;
+
+ if (!obj)
+ return NULL;
+ p = isl_printer_to_str(FN(TYPE,get_ctx)(obj));
+ p = FN(isl_printer_print,BASE)(p, obj);
+ s = isl_printer_get_str(p);
+ isl_printer_free(p);
+
+ return s;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/print_templ_yaml.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/print_templ_yaml.c
new file mode 100644
index 00000000000..50f7f1f0487
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/print_templ_yaml.c
@@ -0,0 +1,39 @@
+#define xCAT(A,B) A ## B
+#define CAT(A,B) xCAT(A,B)
+#undef TYPE
+#define TYPE CAT(isl_,BASE)
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+void FN(TYPE,dump)(__isl_keep TYPE *obj)
+{
+ isl_printer *p;
+
+ if (!obj)
+ return;
+
+ p = isl_printer_to_file(FN(TYPE,get_ctx)(obj), stderr);
+ p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_BLOCK);
+ p = FN(isl_printer_print,BASE)(p, obj);
+ isl_printer_free(p);
+}
+
+/* Return a string representation of "obj".
+ * Print the object in flow format.
+ */
+__isl_give char *FN(TYPE,to_str)(__isl_keep TYPE *obj)
+{
+ isl_printer *p;
+ char *s;
+
+ if (!obj)
+ return NULL;
+
+ p = isl_printer_to_str(FN(TYPE,get_ctx)(obj));
+ p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_FLOW);
+ p = FN(isl_printer_print,BASE)(p, obj);
+ s = isl_printer_get_str(p);
+ isl_printer_free(p);
+
+ return s;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/print_yaml_field_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/print_yaml_field_templ.c
new file mode 100644
index 00000000000..088e701d969
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/print_yaml_field_templ.c
@@ -0,0 +1,22 @@
+#define xCAT(A,B) A ## B
+#define CAT(A,B) xCAT(A,B)
+#undef TYPE
+#define TYPE CAT(isl_,BASE)
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Print a key-value pair of a YAML mapping to "p",
+ * with key "name" and value "val".
+ */
+static __isl_give isl_printer *FN(print_yaml_field,BASE)(
+ __isl_take isl_printer *p, const char *name, __isl_keep TYPE *val)
+{
+ p = isl_printer_print_str(p, name);
+ p = isl_printer_yaml_next(p);
+ p = isl_printer_print_str(p, "\"");
+ p = FN(isl_printer_print,BASE)(p, val);
+ p = isl_printer_print_str(p, "\"");
+ p = isl_printer_yaml_next(p);
+
+ return p;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/read_in_string_templ.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/read_in_string_templ.c
new file mode 100644
index 00000000000..3dc630ee036
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/read_in_string_templ.c
@@ -0,0 +1,38 @@
+#include <isl/stream.h>
+
+#define xCAT(A,B) A ## B
+#define CAT(A,B) xCAT(A,B)
+#undef TYPE
+#define TYPE CAT(isl_,BASE)
+#define xFN(TYPE,NAME) TYPE ## _ ## NAME
+#define FN(TYPE,NAME) xFN(TYPE,NAME)
+
+/* Read an object of type TYPE from "s", where the object may
+ * either be specified directly or as a string.
+ *
+ * First check if the next token in "s" is a string. If so, try and
+ * extract the object from the string.
+ * Otherwise, try and read the object directly from "s".
+ */
+static __isl_give TYPE *FN(read,BASE)(__isl_keep isl_stream *s)
+{
+ struct isl_token *tok;
+ int type;
+
+ tok = isl_stream_next_token(s);
+ type = isl_token_get_type(tok);
+ if (type == ISL_TOKEN_STRING) {
+ char *str;
+ isl_ctx *ctx;
+ TYPE *res;
+
+ ctx = isl_stream_get_ctx(s);
+ str = isl_token_get_str(ctx, tok);
+ res = FN(TYPE,read_from_str)(ctx, str);
+ free(str);
+ isl_token_free(tok);
+ return res;
+ }
+ isl_stream_push_token(s, tok);
+ return FN(isl_stream_read,BASE)(s);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/set_from_map.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/set_from_map.c
new file mode 100644
index 00000000000..cf6f8148a9b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/set_from_map.c
@@ -0,0 +1,8 @@
+#include <isl/map_type.h>
+
+/* Return the set that was treated as the map "map".
+ */
+static __isl_give isl_set *set_from_map(__isl_take isl_map *map)
+{
+ return (isl_set *) map;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/set_list_from_map_list_inl.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/set_list_from_map_list_inl.c
new file mode 100644
index 00000000000..108c53476ff
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/set_list_from_map_list_inl.c
@@ -0,0 +1,9 @@
+#include <isl/map_type.h>
+
+/* Return the set list that was treated as the map list "list".
+ */
+static __isl_give isl_set_list *set_list_from_map_list(
+ __isl_take isl_map_list *list)
+{
+ return (isl_set_list *) list;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/set_to_map.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/set_to_map.c
new file mode 100644
index 00000000000..dc67ed24bab
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/set_to_map.c
@@ -0,0 +1,10 @@
+#include <isl/map_type.h>
+
+/* Treat "set" as a map.
+ * Internally, isl_set is defined to isl_map, so in practice,
+ * this function performs a redundant cast.
+ */
+static __isl_give isl_map *set_to_map(__isl_take isl_set *set)
+{
+ return (isl_map *) set;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/uset_from_umap.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/uset_from_umap.c
new file mode 100644
index 00000000000..cad60600746
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/uset_from_umap.c
@@ -0,0 +1,8 @@
+#include <isl/union_map_type.h>
+
+/* Return the union set that was treated as the union map "umap".
+ */
+static __isl_give isl_union_set *uset_from_umap(__isl_take isl_union_map *umap)
+{
+ return (isl_union_set *) umap;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/uset_to_umap.c b/contrib/libs/llvm14/tools/polly/lib/External/isl/uset_to_umap.c
new file mode 100644
index 00000000000..68dcd01c957
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/uset_to_umap.c
@@ -0,0 +1,10 @@
+#include <isl/union_map_type.h>
+
+/* Treat "uset" as a union map.
+ * Internally, isl_union_set is defined to isl_union_map, so in practice,
+ * this function performs a redundant cast.
+ */
+static __isl_give isl_union_map *uset_to_umap(__isl_take isl_union_set *uset)
+{
+ return (isl_union_map *) uset;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/isl/ya.make b/contrib/libs/llvm14/tools/polly/lib/External/isl/ya.make
new file mode 100644
index 00000000000..e0da639ca52
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/isl/ya.make
@@ -0,0 +1,113 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(
+ BSD-3-Clause AND
+ MIT
+)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/polly/lib/External
+ contrib/libs/llvm14/tools/polly/lib/External/isl
+ contrib/libs/llvm14/tools/polly/lib/External/isl/imath
+ contrib/libs/llvm14/tools/polly/lib/External/isl/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+SRCS(
+ basis_reduction_tab.c
+ imath/gmp_compat.c
+ imath/imath.c
+ imath/imrat.c
+ isl_aff.c
+ isl_aff_map.c
+ isl_affine_hull.c
+ isl_arg.c
+ isl_ast.c
+ isl_ast_build.c
+ isl_ast_build_expr.c
+ isl_ast_codegen.c
+ isl_ast_graft.c
+ isl_bernstein.c
+ isl_blk.c
+ isl_bound.c
+ isl_box.c
+ isl_coalesce.c
+ isl_constraint.c
+ isl_convex_hull.c
+ isl_ctx.c
+ isl_deprecated.c
+ isl_dim_map.c
+ isl_equalities.c
+ isl_factorization.c
+ isl_farkas.c
+ isl_ffs.c
+ isl_flow.c
+ isl_fold.c
+ isl_hash.c
+ isl_id.c
+ isl_id_to_ast_expr.c
+ isl_id_to_id.c
+ isl_id_to_pw_aff.c
+ isl_ilp.c
+ isl_imath.c
+ isl_input.c
+ isl_int_sioimath.c
+ isl_local.c
+ isl_local_space.c
+ isl_lp.c
+ isl_map.c
+ isl_map_list.c
+ isl_map_simplify.c
+ isl_map_subtract.c
+ isl_map_to_basic_set.c
+ isl_mat.c
+ isl_morph.c
+ isl_obj.c
+ isl_options.c
+ isl_output.c
+ isl_point.c
+ isl_polynomial.c
+ isl_printer.c
+ isl_range.c
+ isl_reordering.c
+ isl_sample.c
+ isl_scan.c
+ isl_schedule.c
+ isl_schedule_band.c
+ isl_schedule_constraints.c
+ isl_schedule_node.c
+ isl_schedule_read.c
+ isl_schedule_tree.c
+ isl_scheduler.c
+ isl_seq.c
+ isl_set_list.c
+ isl_set_to_ast_graft_list.c
+ isl_sort.c
+ isl_space.c
+ isl_stream.c
+ isl_stride.c
+ isl_tab.c
+ isl_tab_pip.c
+ isl_tarjan.c
+ isl_transitive_closure.c
+ isl_union_map.c
+ isl_val.c
+ isl_val_sioimath.c
+ isl_vec.c
+ isl_version.c
+ isl_vertices.c
+ print.c
+ set_from_map.c
+ set_to_map.c
+ uset_from_umap.c
+ uset_to_umap.c
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/pet/include/pet.h b/contrib/libs/llvm14/tools/polly/lib/External/pet/include/pet.h
new file mode 100644
index 00000000000..28a28b8bf0a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/pet/include/pet.h
@@ -0,0 +1,622 @@
+#ifndef PET_H
+#define PET_H
+
+#include <isl/aff.h>
+#include <isl/arg.h>
+#include <isl/ast_build.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl/union_map.h>
+#include <isl/printer.h>
+#include <isl/id_to_ast_expr.h>
+#include <isl/id_to_pw_aff.h>
+#include <isl/schedule.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct pet_options;
+ISL_ARG_DECL(pet_options, struct pet_options, pet_options_args)
+
+/* Create an isl_ctx that references the pet options. */
+isl_ctx *isl_ctx_alloc_with_pet_options();
+
+/* If autodetect is set, any valid scop is extracted.
+ * Otherwise, the scop needs to be delimited by pragmas.
+ */
+int pet_options_set_autodetect(isl_ctx *ctx, int val);
+int pet_options_get_autodetect(isl_ctx *ctx);
+
+int pet_options_set_detect_conditional_assignment(isl_ctx *ctx, int val);
+int pet_options_get_detect_conditional_assignment(isl_ctx *ctx);
+
+/* If encapsulate-dynamic-control is set, then any dynamic control
+ * in the input program will be encapsulated in macro statements.
+ * This means in particular that no statements with arguments
+ * will be created.
+ */
+int pet_options_set_encapsulate_dynamic_control(isl_ctx *ctx, int val);
+int pet_options_get_encapsulate_dynamic_control(isl_ctx *ctx);
+
+#define PET_OVERFLOW_AVOID 0
+#define PET_OVERFLOW_IGNORE 1
+int pet_options_set_signed_overflow(isl_ctx *ctx, int val);
+int pet_options_get_signed_overflow(isl_ctx *ctx);
+
+struct pet_loc;
+typedef struct pet_loc pet_loc;
+
+/* Return an additional reference to "loc". */
+__isl_give pet_loc *pet_loc_copy(__isl_keep pet_loc *loc);
+/* Free a reference to "loc". */
+pet_loc *pet_loc_free(__isl_take pet_loc *loc);
+
+/* Return the offset in the input file of the start of "loc". */
+unsigned pet_loc_get_start(__isl_keep pet_loc *loc);
+/* Return the offset in the input file of the character after "loc". */
+unsigned pet_loc_get_end(__isl_keep pet_loc *loc);
+/* Return the line number of a line within the "loc" region. */
+int pet_loc_get_line(__isl_keep pet_loc *loc);
+/* Return the indentation of the "loc" region. */
+__isl_keep const char *pet_loc_get_indent(__isl_keep pet_loc *loc);
+
+enum pet_expr_type {
+ pet_expr_error = -1,
+ pet_expr_access,
+ pet_expr_call,
+ pet_expr_cast,
+ pet_expr_int,
+ pet_expr_double,
+ pet_expr_op
+};
+
+enum pet_op_type {
+ /* only compound assignments operators before assignment */
+ pet_op_add_assign,
+ pet_op_sub_assign,
+ pet_op_mul_assign,
+ pet_op_div_assign,
+ pet_op_and_assign,
+ pet_op_xor_assign,
+ pet_op_or_assign,
+ pet_op_assign,
+ pet_op_add,
+ pet_op_sub,
+ pet_op_mul,
+ pet_op_div,
+ pet_op_mod,
+ pet_op_shl,
+ pet_op_shr,
+ pet_op_eq,
+ pet_op_ne,
+ pet_op_le,
+ pet_op_ge,
+ pet_op_lt,
+ pet_op_gt,
+ pet_op_minus,
+ pet_op_post_inc,
+ pet_op_post_dec,
+ pet_op_pre_inc,
+ pet_op_pre_dec,
+ pet_op_address_of,
+ pet_op_assume,
+ pet_op_kill,
+ pet_op_and,
+ pet_op_xor,
+ pet_op_or,
+ pet_op_not,
+ pet_op_land,
+ pet_op_lor,
+ pet_op_lnot,
+ pet_op_cond,
+ pet_op_last
+};
+
+/* Index into the pet_expr->args array when pet_expr->type == pet_expr_unary
+ */
+enum pet_un_arg_type {
+ pet_un_arg
+};
+
+/* Indices into the pet_expr->args array when
+ * pet_expr->type == pet_expr_binary
+ */
+enum pet_bin_arg_type {
+ pet_bin_lhs,
+ pet_bin_rhs
+};
+
+/* Indices into the pet_expr->args array when
+ * pet_expr->type == pet_expr_ternary
+ */
+enum pet_ter_arg_type {
+ pet_ter_cond,
+ pet_ter_true,
+ pet_ter_false
+};
+
+struct pet_expr;
+typedef struct pet_expr pet_expr;
+
+/* Return an additional reference to "expr". */
+__isl_give pet_expr *pet_expr_copy(__isl_keep pet_expr *expr);
+/* Free a reference to "expr". */
+__isl_null pet_expr *pet_expr_free(__isl_take pet_expr *expr);
+
+/* Return the isl_ctx in which "expr" was created. */
+isl_ctx *pet_expr_get_ctx(__isl_keep pet_expr *expr);
+
+/* Return the type of "expr". */
+enum pet_expr_type pet_expr_get_type(__isl_keep pet_expr *expr);
+/* Return the number of arguments of "expr". */
+int pet_expr_get_n_arg(__isl_keep pet_expr *expr);
+/* Set the number of arguments of "expr" to "n". */
+__isl_give pet_expr *pet_expr_set_n_arg(__isl_take pet_expr *expr, int n);
+/* Return the argument of "expr" at position "pos". */
+__isl_give pet_expr *pet_expr_get_arg(__isl_keep pet_expr *expr, int pos);
+/* Replace the argument of "expr" at position "pos" by "arg". */
+__isl_give pet_expr *pet_expr_set_arg(__isl_take pet_expr *expr, int pos,
+ __isl_take pet_expr *arg);
+
+/* Return the operation type of operation expression "expr". */
+enum pet_op_type pet_expr_op_get_type(__isl_keep pet_expr *expr);
+/* Replace the operation type of operation expression "expr" by "type". */
+__isl_give pet_expr *pet_expr_op_set_type(__isl_take pet_expr *expr,
+ enum pet_op_type type);
+
+/* Construct a (read) access pet_expr from an index expression. */
+__isl_give pet_expr *pet_expr_from_index(__isl_take isl_multi_pw_aff *index);
+
+/* Does "expr" represent an affine expression? */
+isl_bool pet_expr_is_affine(__isl_keep pet_expr *expr);
+/* Does the access expression "expr" read the accessed elements? */
+isl_bool pet_expr_access_is_read(__isl_keep pet_expr *expr);
+/* Does the access expression "expr" write to the accessed elements? */
+isl_bool pet_expr_access_is_write(__isl_keep pet_expr *expr);
+/* Does the access expression "expr" kill the accessed elements? */
+isl_bool pet_expr_access_is_kill(__isl_keep pet_expr *expr);
+/* Mark "expr" as a read depending on "read". */
+__isl_give pet_expr *pet_expr_access_set_read(__isl_take pet_expr *expr,
+ int read);
+/* Mark "expr" as a write depending on "write". */
+__isl_give pet_expr *pet_expr_access_set_write(__isl_take pet_expr *expr,
+ int write);
+/* Mark "expr" as a kill depending on "kill". */
+__isl_give pet_expr *pet_expr_access_set_kill(__isl_take pet_expr *expr,
+ int kill);
+/* Return the reference identifier of access expression "expr". */
+__isl_give isl_id *pet_expr_access_get_ref_id(__isl_keep pet_expr *expr);
+/* Replace the reference identifier of access expression "expr" by "ref_id". */
+__isl_give pet_expr *pet_expr_access_set_ref_id(__isl_take pet_expr *expr,
+ __isl_take isl_id *ref_id);
+/* Return the identifier of the outer array accessed by "expr". */
+__isl_give isl_id *pet_expr_access_get_id(__isl_keep pet_expr *expr);
+/* Return the index expression of access expression "expr". */
+__isl_give isl_multi_pw_aff *pet_expr_access_get_index(
+ __isl_keep pet_expr *expr);
+
+/* Return the potential read access relation of access expression "expr". */
+__isl_give isl_union_map *pet_expr_access_get_may_read(
+ __isl_keep pet_expr *expr);
+/* Return the potential write access relation of access expression "expr". */
+__isl_give isl_union_map *pet_expr_access_get_may_write(
+ __isl_keep pet_expr *expr);
+/* Return the definite write access relation of access expression "expr". */
+__isl_give isl_union_map *pet_expr_access_get_must_write(
+ __isl_keep pet_expr *expr);
+/* Return the argument dependent potential read access relation of "expr". */
+__isl_give isl_union_map *pet_expr_access_get_dependent_may_read(
+ __isl_keep pet_expr *expr);
+/* Return the argument dependent potential write access relation of "expr". */
+__isl_give isl_union_map *pet_expr_access_get_dependent_may_write(
+ __isl_keep pet_expr *expr);
+/* Return the argument dependent definite write access relation of "expr". */
+__isl_give isl_union_map *pet_expr_access_get_dependent_must_write(
+ __isl_keep pet_expr *expr);
+/* Return the tagged potential read access relation of access "expr". */
+__isl_give isl_union_map *pet_expr_access_get_tagged_may_read(
+ __isl_keep pet_expr *expr);
+/* Return the tagged potential write access relation of access "expr". */
+__isl_give isl_union_map *pet_expr_access_get_tagged_may_write(
+ __isl_keep pet_expr *expr);
+
+/* Return the name of the function called by "expr". */
+__isl_keep const char *pet_expr_call_get_name(__isl_keep pet_expr *expr);
+/* Replace the name of the function called by "expr" by "name". */
+__isl_give pet_expr *pet_expr_call_set_name(__isl_take pet_expr *expr,
+ __isl_keep const char *name);
+
+/* Create a pet_expr representing a cast of "arg" to "type_name". */
+__isl_give pet_expr *pet_expr_new_cast(const char *type_name,
+ __isl_take pet_expr *arg);
+/* Replace the type of the cast performed by "expr" by "name". */
+__isl_give pet_expr *pet_expr_cast_set_type_name(__isl_take pet_expr *expr,
+ __isl_keep const char *name);
+
+/* Return the value of the integer represented by "expr". */
+__isl_give isl_val *pet_expr_int_get_val(__isl_keep pet_expr *expr);
+/* Replace the value of the integer represented by "expr" by "v". */
+__isl_give pet_expr *pet_expr_int_set_val(__isl_take pet_expr *expr,
+ __isl_take isl_val *v);
+
+/* Return a string representation of the double expression "expr". */
+__isl_give char *pet_expr_double_get_str(__isl_keep pet_expr *expr);
+/* Replace value and string representation of the double expression "expr" */
+__isl_give pet_expr *pet_expr_double_set(__isl_take pet_expr *expr,
+ double d, __isl_keep const char *s);
+
+/* Call "fn" on each of the subexpressions of "expr" of type pet_expr_access. */
+int pet_expr_foreach_access_expr(__isl_keep pet_expr *expr,
+ int (*fn)(__isl_keep pet_expr *expr, void *user), void *user);
+/* Call "fn" on each of the subexpressions of "expr" of type pet_expr_call. */
+int pet_expr_foreach_call_expr(__isl_keep pet_expr *expr,
+ int (*fn)(__isl_keep pet_expr *expr, void *user), void *user);
+
+struct pet_context;
+typedef struct pet_context pet_context;
+
+/* Create a context with the given domain. */
+__isl_give pet_context *pet_context_alloc(__isl_take isl_set *domain);
+/* Return an additional reference to "pc". */
+__isl_give pet_context *pet_context_copy(__isl_keep pet_context *pc);
+/* Free a reference to "pc". */
+__isl_null pet_context *pet_context_free(__isl_take pet_context *pc);
+
+/* Return the isl_ctx in which "pc" was created. */
+isl_ctx *pet_context_get_ctx(__isl_keep pet_context *pc);
+
+/* Extract an affine expression defined over the domain of "pc" from "expr"
+ * or return NaN.
+ */
+__isl_give isl_pw_aff *pet_expr_extract_affine(__isl_keep pet_expr *expr,
+ __isl_keep pet_context *pc);
+
+void pet_expr_dump(__isl_keep pet_expr *expr);
+
+enum pet_tree_type {
+ pet_tree_error = -1,
+ pet_tree_expr,
+ pet_tree_block,
+ pet_tree_break,
+ pet_tree_continue,
+ pet_tree_decl, /* A declaration without initialization */
+ pet_tree_decl_init, /* A declaration with initialization */
+ pet_tree_if, /* An if without an else branch */
+ pet_tree_if_else, /* An if with an else branch */
+ pet_tree_for,
+ pet_tree_infinite_loop,
+ pet_tree_while,
+ pet_tree_return,
+};
+
+struct pet_tree;
+typedef struct pet_tree pet_tree;
+
+/* Return the isl_ctx in which "tree" was created. */
+isl_ctx *pet_tree_get_ctx(__isl_keep pet_tree *tree);
+
+/* Return an additional reference to "tree". */
+__isl_give pet_tree *pet_tree_copy(__isl_keep pet_tree *tree);
+/* Free a reference to "tree". */
+__isl_null pet_tree *pet_tree_free(__isl_take pet_tree *tree);
+
+/* Return the location of "tree". */
+__isl_give pet_loc *pet_tree_get_loc(__isl_keep pet_tree *tree);
+
+/* Return the type of "tree". */
+enum pet_tree_type pet_tree_get_type(__isl_keep pet_tree *tree);
+
+/* Return the expression of the expression tree "tree". */
+__isl_give pet_expr *pet_tree_expr_get_expr(__isl_keep pet_tree *tree);
+
+/* Return the expression returned by the return tree "tree". */
+__isl_give pet_expr *pet_tree_return_get_expr(__isl_keep pet_tree *tree);
+
+/* Return the number of children of the block tree "tree". */
+int pet_tree_block_n_child(__isl_keep pet_tree *tree);
+/* Return child "pos" of the block tree "tree". */
+__isl_give pet_tree *pet_tree_block_get_child(__isl_keep pet_tree *tree,
+ int pos);
+
+/* Is "tree" a declaration (with or without initialization)? */
+int pet_tree_is_decl(__isl_keep pet_tree *tree);
+/* Return the variable declared by the declaration tree "tree". */
+__isl_give pet_expr *pet_tree_decl_get_var(__isl_keep pet_tree *tree);
+/* Return the initial value of the pet_tree_decl_init tree "tree". */
+__isl_give pet_expr *pet_tree_decl_get_init(__isl_keep pet_tree *tree);
+
+/* Return the condition of the if tree "tree". */
+__isl_give pet_expr *pet_tree_if_get_cond(__isl_keep pet_tree *tree);
+/* Return the then branch of the if tree "tree". */
+__isl_give pet_tree *pet_tree_if_get_then(__isl_keep pet_tree *tree);
+/* Return the else branch of the if tree with else branch "tree". */
+__isl_give pet_tree *pet_tree_if_get_else(__isl_keep pet_tree *tree);
+
+/* Is "tree" a for loop, a while loop or an infinite loop? */
+int pet_tree_is_loop(__isl_keep pet_tree *tree);
+/* Return the induction variable of the for loop "tree" */
+__isl_give pet_expr *pet_tree_loop_get_var(__isl_keep pet_tree *tree);
+/* Return the initial value of the induction variable of the for loop "tree" */
+__isl_give pet_expr *pet_tree_loop_get_init(__isl_keep pet_tree *tree);
+/* Return the condition of the loop tree "tree" */
+__isl_give pet_expr *pet_tree_loop_get_cond(__isl_keep pet_tree *tree);
+/* Return the induction variable of the for loop "tree" */
+__isl_give pet_expr *pet_tree_loop_get_inc(__isl_keep pet_tree *tree);
+/* Return the body of the loop tree "tree" */
+__isl_give pet_tree *pet_tree_loop_get_body(__isl_keep pet_tree *tree);
+
+/* Call "fn" on each top-level expression in the nodes of "tree" */
+int pet_tree_foreach_expr(__isl_keep pet_tree *tree,
+ int (*fn)(__isl_keep pet_expr *expr, void *user), void *user);
+/* Call "fn" on each access subexpression in the nodes of "tree" */
+int pet_tree_foreach_access_expr(__isl_keep pet_tree *tree,
+ int (*fn)(__isl_keep pet_expr *expr, void *user), void *user);
+/* Modify all call subexpressions in the nodes of "tree" through "fn". */
+__isl_give pet_tree *pet_tree_map_call_expr(__isl_take pet_tree *tree,
+ __isl_give pet_expr *(*fn)(__isl_take pet_expr *expr, void *user),
+ void *user);
+
+void pet_tree_dump(__isl_keep pet_tree *tree);
+
+/* "loc" represents the region of the source code that is represented
+ * by this statement.
+ *
+ * If the statement has arguments, i.e., n_arg != 0, then
+ * "domain" is a wrapped map, mapping the iteration domain
+ * to the values of the arguments for which this statement
+ * is executed.
+ * Otherwise, it is simply the iteration domain.
+ *
+ * If one of the arguments is an access expression that accesses
+ * more than one element for a given iteration, then the constraints
+ * on the value of this argument (encoded in "domain") should be satisfied
+ * for all of those accessed elements.
+ */
+struct pet_stmt {
+ pet_loc *loc;
+ isl_set *domain;
+ pet_tree *body;
+
+ unsigned n_arg;
+ pet_expr **args;
+};
+
+/* Return the iteration space of "stmt". */
+__isl_give isl_space *pet_stmt_get_space(struct pet_stmt *stmt);
+
+/* Is "stmt" an assignment statement? */
+int pet_stmt_is_assign(struct pet_stmt *stmt);
+/* Is "stmt" a kill statement? */
+int pet_stmt_is_kill(struct pet_stmt *stmt);
+
+/* pet_stmt_build_ast_exprs is currently limited to only handle
+ * some forms of data dependent accesses.
+ * If pet_stmt_can_build_ast_exprs returns 1, then pet_stmt_build_ast_exprs
+ * can safely be called on "stmt".
+ */
+int pet_stmt_can_build_ast_exprs(struct pet_stmt *stmt);
+/* Construct an associative array from reference identifiers of
+ * access expressions in "stmt" to the corresponding isl_ast_expr.
+ * Each index expression is first transformed through "fn_index"
+ * (if not NULL). Then an AST expression is generated using "build".
+ * Finally, the AST expression is transformed using "fn_expr"
+ * (if not NULL).
+ */
+__isl_give isl_id_to_ast_expr *pet_stmt_build_ast_exprs(struct pet_stmt *stmt,
+ __isl_keep isl_ast_build *build,
+ __isl_give isl_multi_pw_aff *(*fn_index)(
+ __isl_take isl_multi_pw_aff *mpa, __isl_keep isl_id *id,
+ void *user), void *user_index,
+ __isl_give isl_ast_expr *(*fn_expr)(__isl_take isl_ast_expr *expr,
+ __isl_keep isl_id *id, void *user), void *user_expr);
+
+/* Print "stmt" to "p".
+ *
+ * The access expressions in "stmt" are replaced by the isl_ast_expr
+ * associated to its reference identifier in "ref2expr".
+ */
+__isl_give isl_printer *pet_stmt_print_body(struct pet_stmt *stmt,
+ __isl_take isl_printer *p, __isl_keep isl_id_to_ast_expr *ref2expr);
+
+/* This structure represents a defined type.
+ * "name" is the name of the type, while "definition" is a string
+ * representation of its definition.
+ */
+struct pet_type {
+ char *name;
+ char *definition;
+};
+
+/* context holds constraints on the parameter that ensure that
+ * this array has a valid (i.e., non-negative) size
+ *
+ * extent holds constraints on the indices
+ *
+ * value_bounds holds constraints on the elements of the array
+ * and may be NULL if no such constraints were specified by the user
+ *
+ * element_size is the size in bytes of each array element
+ * element_type is the type of the array elements.
+ * element_is_record is set if this type is a record type.
+ *
+ * live_out is set if the array appears in a live-out pragma
+ *
+ * if uniquely_defined is set then the array is written by a single access
+ * such that any element that is ever read
+ * is known to be assigned exactly once before the read
+ *
+ * declared is set if the array was declared somewhere inside the scop.
+ * exposed is set if the declared array is visible outside the scop.
+ * outer is set if the type of the array elements is a record and
+ * the fields of this record are represented by separate pet_array structures.
+ */
+struct pet_array {
+ isl_set *context;
+ isl_set *extent;
+ isl_set *value_bounds;
+ char *element_type;
+ int element_is_record;
+ int element_size;
+ int live_out;
+ int uniquely_defined;
+ int declared;
+ int exposed;
+ int outer;
+};
+
+/* This structure represents an implication on a boolean filter.
+ * In particular, if the filter value of an element in the domain
+ * of "extension" is equal to "satisfied", then the filter values
+ * of the corresponding images in "extension" are also equal
+ * to "satisfied".
+ */
+struct pet_implication {
+ int satisfied;
+ isl_map *extension;
+};
+
+/* This structure represents an independence implied by a for loop
+ * that is marked as independent in the source code.
+ * "filter" contains pairs of statement instances that are guaranteed
+ * not to be dependent on each other based on the independent for loop,
+ * assuming that no dependences carried by this loop are implied
+ * by the variables in "local".
+ * "local" contains the variables that are local to the loop that was
+ * marked independent.
+ */
+struct pet_independence {
+ isl_union_map *filter;
+ isl_union_set *local;
+};
+
+/* "loc" represents the region of the source code that is represented
+ * by this scop.
+ * If the scop was detected based on scop and endscop pragmas, then
+ * the lines containing these pragmas are included in this region.
+ * In the final result, the context describes the set of parameter values
+ * for which the scop can be executed.
+ * During the construction of the pet_scop, the context lives in a set space
+ * where each dimension refers to an outer loop.
+ * context_value describes assignments to the parameters (if any)
+ * outside of the scop.
+ *
+ * "schedule" is the schedule of the statements in the scop.
+ *
+ * The n_type types define types that may be referenced from by the arrays.
+ *
+ * The n_implication implications describe implications on boolean filters.
+ *
+ * The n_independence independences describe independences implied
+ * by for loops that are marked independent in the source code.
+ */
+struct pet_scop {
+ pet_loc *loc;
+
+ isl_set *context;
+ isl_set *context_value;
+ isl_schedule *schedule;
+
+ int n_type;
+ struct pet_type **types;
+
+ int n_array;
+ struct pet_array **arrays;
+
+ int n_stmt;
+ struct pet_stmt **stmts;
+
+ int n_implication;
+ struct pet_implication **implications;
+
+ int n_independence;
+ struct pet_independence **independences;
+};
+typedef struct pet_scop pet_scop;
+
+/* Return a textual representation of the operator. */
+const char *pet_op_str(enum pet_op_type op);
+int pet_op_is_inc_dec(enum pet_op_type op);
+
+/* Extract a pet_scop from a C source file.
+ * If function is not NULL, then the pet_scop is extracted from
+ * a function with that name.
+ */
+__isl_give pet_scop *pet_scop_extract_from_C_source(isl_ctx *ctx,
+ const char *filename, const char *function);
+
+/* Transform the C source file "input" by rewriting each scop
+ * When autodetecting scops, at most one scop per function is rewritten.
+ * The transformed C code is written to "output".
+ */
+int pet_transform_C_source(isl_ctx *ctx, const char *input, FILE *output,
+ __isl_give isl_printer *(*transform)(__isl_take isl_printer *p,
+ __isl_take pet_scop *scop, void *user), void *user);
+/* Given a scop and a printer passed to a pet_transform_C_source callback,
+ * print the original corresponding code to the printer.
+ */
+__isl_give isl_printer *pet_scop_print_original(__isl_keep pet_scop *scop,
+ __isl_take isl_printer *p);
+
+/* Update all isl_sets and isl_maps such that they all have the same
+ * parameters in the same order.
+ */
+__isl_give pet_scop *pet_scop_align_params(__isl_take pet_scop *scop);
+
+/* Does "scop" contain any data dependent accesses? */
+int pet_scop_has_data_dependent_accesses(__isl_keep pet_scop *scop);
+/* Does "scop" contain any data dependent conditions? */
+int pet_scop_has_data_dependent_conditions(__isl_keep pet_scop *scop);
+/* pet_stmt_build_ast_exprs is currently limited to only handle
+ * some forms of data dependent accesses.
+ * If pet_scop_can_build_ast_exprs returns 1, then pet_stmt_build_ast_exprs
+ * can safely be called on all statements in the scop.
+ */
+int pet_scop_can_build_ast_exprs(__isl_keep pet_scop *scop);
+
+void pet_scop_dump(__isl_keep pet_scop *scop);
+__isl_null pet_scop *pet_scop_free(__isl_take pet_scop *scop);
+
+/* Return the context of "scop". */
+__isl_give isl_set *pet_scop_get_context(__isl_keep pet_scop *scop);
+/* Return the schedule of "scop". */
+__isl_give isl_schedule *pet_scop_get_schedule(__isl_keep pet_scop *scop);
+/* Return the set of all statement instances. */
+__isl_give isl_union_set *pet_scop_get_instance_set(__isl_keep pet_scop *scop);
+/* Return the potential read access relation. */
+__isl_give isl_union_map *pet_scop_get_may_reads(__isl_keep pet_scop *scop);
+/* Return the tagged potential read access relation. */
+__isl_give isl_union_map *pet_scop_get_tagged_may_reads(
+ __isl_keep pet_scop *scop);
+/* Return the potential write access relation. */
+__isl_give isl_union_map *pet_scop_get_may_writes(__isl_keep pet_scop *scop);
+/* Return the definite write access relation. */
+__isl_give isl_union_map *pet_scop_get_must_writes(__isl_keep pet_scop *scop);
+/* Return the tagged potential write access relation. */
+__isl_give isl_union_map *pet_scop_get_tagged_may_writes(
+ __isl_keep pet_scop *scop);
+/* Return the tagged definite write access relation. */
+__isl_give isl_union_map *pet_scop_get_tagged_must_writes(
+ __isl_keep pet_scop *scop);
+/* Return the definite kill access relation. */
+__isl_give isl_union_map *pet_scop_get_must_kills(__isl_keep pet_scop *scop);
+/* Return the tagged definite kill access relation. */
+__isl_give isl_union_map *pet_scop_get_tagged_must_kills(
+ __isl_keep pet_scop *scop);
+
+/* Compute a mapping from all outermost arrays (of structs) in scop
+ * to their innermost members.
+ */
+__isl_give isl_union_map *pet_scop_compute_outer_to_inner(
+ __isl_keep pet_scop *scop);
+/* Compute a mapping from all outermost arrays (of structs) in scop
+ * to their members, including the outermost arrays themselves.
+ */
+__isl_give isl_union_map *pet_scop_compute_outer_to_any(
+ __isl_keep pet_scop *scop);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ChangeLog b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ChangeLog
new file mode 100644
index 00000000000..e9027019033
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ChangeLog
@@ -0,0 +1,29 @@
+version: 0.07
+date: Tue Feb 7 17:23:22 CET 2017
+changes:
+ - support hybrid tiling
+---
+version: 0.06
+date: Fri May 6 12:08:50 CEST 2016
+changes:
+ - use PPCG specific macro names in generated code
+ - complete transition to schedule trees
+ - maximize coincidence by default
+ - map arrays with constant index expressions to private memory
+ - optionally group chains of statements
+---
+version: 0.05
+date: Fri Jan 15 09:30:23 CET 2016
+changes:
+ - fix live-out computation
+ - optionally compute schedule for C target
+ - optionally perform tiling for C target
+ - create single kernel for non-permutable subtree
+---
+version: 0.04
+date: Wed Jun 17 10:52:58 CEST 2015
+changes:
+ - use schedule trees
+ - fix live-range reordering
+ - improve generation of synchronization
+ - exploit independences during dependence analysis
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/README b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/README
new file mode 100644
index 00000000000..450786ab443
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/README
@@ -0,0 +1,246 @@
+Requirements:
+
+- automake, autoconf, libtool
+ (not needed when compiling a release)
+- pkg-config (http://www.freedesktop.org/wiki/Software/pkg-config)
+ (not needed when compiling a release using the included isl and pet)
+- gmp (http://gmplib.org/)
+- libyaml (http://pyyaml.org/wiki/LibYAML)
+ (only needed if you want to compile the pet executable)
+- LLVM/clang libraries, 2.9 or higher (http://clang.llvm.org/get_started.html)
+ Unless you have some other reasons for wanting to use the svn version,
+ it is best to install the latest release (3.9).
+ For more details, see pet/README.
+
+If you are installing on Ubuntu, then you can install the following packages:
+
+automake autoconf libtool pkg-config libgmp3-dev libyaml-dev libclang-dev llvm
+
+Note that you need at least version 3.2 of libclang-dev (ubuntu raring).
+Older versions of this package did not include the required libraries.
+If you are using an older version of ubuntu, then you need to compile and
+install LLVM/clang from source.
+
+
+Preparing:
+
+Grab the latest release and extract it or get the source from
+the git repository as follows. This process requires autoconf,
+automake, libtool and pkg-config.
+
+ git clone git://repo.or.cz/ppcg.git
+ cd ppcg
+ ./get_submodules.sh
+ ./autogen.sh
+
+
+Compilation:
+
+ ./configure
+ make
+ make check
+
+If you have installed any of the required libraries in a non-standard
+location, then you may need to use the --with-gmp-prefix,
+--with-libyaml-prefix and/or --with-clang-prefix options
+when calling "./configure".
+
+
+Using PPCG to generate CUDA or OpenCL code
+
+To convert a fragment of a C program to CUDA, insert a line containing
+
+ #pragma scop
+
+before the fragment and add a line containing
+
+ #pragma endscop
+
+after the fragment. To generate CUDA code run
+
+ ppcg --target=cuda file.c
+
+where file.c is the file containing the fragment. The generated
+code is stored in file_host.cu and file_kernel.cu.
+
+To generate OpenCL code run
+
+ ppcg --target=opencl file.c
+
+where file.c is the file containing the fragment. The generated code
+is stored in file_host.c and file_kernel.cl.
+
+
+Specifying tile, grid and block sizes
+
+The iterations space tile size, grid size and block size can
+be specified using the --sizes option. The argument is a union map
+in isl notation mapping kernels identified by their sequence number
+in a "kernel" space to singleton sets in the "tile", "grid" and "block"
+spaces. The sizes are specified outermost to innermost.
+
+The dimension of the "tile" space indicates the (maximal) number of loop
+dimensions to tile. The elements of the single integer tuple
+specify the tile sizes in each dimension.
+In case of hybrid tiling, the first element is half the size of
+the tile in the time (sequential) dimension. The second element
+specifies the number of elements in the base of the hexagon.
+The remaining elements specify the tile sizes in the remaining space
+dimensions.
+
+The dimension of the "grid" space indicates the (maximal) number of block
+dimensions in the grid. The elements of the single integer tuple
+specify the number of blocks in each dimension.
+
+The dimension of the "block" space indicates the (maximal) number of thread
+dimensions in the grid. The elements of the single integer tuple
+specify the number of threads in each dimension.
+
+For example,
+
+ { kernel[0] -> tile[64,64]; kernel[i] -> block[16] : i != 4 }
+
+specifies that in kernel 0, two loops should be tiled with a tile
+size of 64 in both dimensions and that all kernels except kernel 4
+should be run using a block of 16 threads.
+
+Since PPCG performs some scheduling, it can be difficult to predict
+what exactly will end up in a kernel. If you want to specify
+tile, grid or block sizes, you may want to run PPCG first with the defaults,
+examine the kernels and then run PPCG again with the desired sizes.
+Instead of examining the kernels, you can also specify the option
+--dump-sizes on the first run to obtain the effectively used default sizes.
+
+
+Compiling the generated CUDA code with nvcc
+
+To get optimal performance from nvcc, it is important to choose --arch
+according to your target GPU. Specifically, use the flag "--arch sm_20"
+for fermi, "--arch sm_30" for GK10x Kepler and "--arch sm_35" for
+GK110 Kepler. We discourage the use of older cards as we have seen
+correctness issues with compilation for older architectures.
+Note that in the absence of any --arch flag, nvcc defaults to
+"--arch sm_13". This will not only be slower, but can also cause
+correctness issues.
+If you want to obtain results that are identical to those obtained
+by the original code, then you may need to disable some optimizations
+by passing the "--fmad=false" option.
+
+
+Compiling the generated OpenCL code with gcc
+
+To compile the host code you need to link against the file
+ocl_utilities.c which contains utility functions used by the generated
+OpenCL host code. To compile the host code with gcc, run
+
+ gcc -std=c99 file_host.c ocl_utilities.c -lOpenCL
+
+Note that we have experienced the generated OpenCL code freezing
+on some inputs (e.g., the PolyBench symm benchmark) when using
+at least some version of the Nvidia OpenCL library, while the
+corresponding CUDA code runs fine.
+We have experienced no such freezes when using AMD, ARM or Intel
+OpenCL libraries.
+
+By default, the compiled executable will need the _kernel.cl file at
+run time. Alternatively, the option --opencl-embed-kernel-code may be
+given to place the kernel code in a string literal. The kernel code is
+then compiled into the host binary, such that the _kernel.cl file is no
+longer needed at run time. Any kernel include files, in particular
+those supplied using --opencl-include-file, will still be required at
+run time.
+
+
+Function calls
+
+Function calls inside the analyzed fragment are reproduced
+in the CUDA or OpenCL code, but for now it is left to the user
+to make sure that the functions that are being called are
+available from the generated kernels.
+
+In the case of OpenCL code, the --opencl-include-file option
+may be used to specify one or more files to be #include'd
+from the generated code. These files may then contain
+the definitions of the functions being called from the
+program fragment. If the pathnames of the included files
+are relative to the current directory, then you may need
+to additionally specify the --opencl-compiler-options=-I.
+to make sure that the files can be found by the OpenCL compiler.
+The included files may contain definitions of types used by the
+generated kernels. By default, PPCG generates definitions for
+types as needed, but these definitions may collide with those in
+the included files, as PPCG does not consider the contents of the
+included files. The --no-opencl-print-kernel-types will prevent
+PPCG from generating type definitions.
+
+
+GNU extensions
+
+By default, PPCG may print out macro definitions that involve
+GNU extensions such as __typeof__ and statement expressions.
+Some compilers may not support these extensions.
+In particular, OpenCL 1.2 beignet 1.1.1 (git-6de6918)
+has been reported not to support __typeof__.
+The use of these extensions can be turned off with the
+--no-allow-gnu-extensions option.
+
+
+Processing PolyBench
+
+When processing a PolyBench/C 3.2 benchmark, you should always specify
+-DPOLYBENCH_USE_C99_PROTO on the ppcg command line. Otherwise, the source
+files are inconsistent, having fixed size arrays but parametrically
+bounded loops iterating over them.
+However, you should not specify this define when compiling
+the PPCG generated code using nvcc since CUDA does not support VLAs.
+
+
+CUDA and function overloading
+
+While CUDA supports function overloading based on the arguments types,
+no such function overloading exists in the input language C. Since PPCG
+simply prints out the same function name as in the original code, this
+may result in a different function being called based on the types
+of the arguments. For example, if the original code contains a call
+to the function sqrt() with a float argument, then the argument will
+be promoted to a double and the sqrt() function will be called.
+In the transformed (CUDA) code, however, overloading will cause the
+function sqrtf() to be called. Until this issue has been resolved in PPCG,
+we recommend that users either explicitly call the function sqrtf() or
+explicitly cast the argument to double in the input code.
+
+
+Contact
+
+For bug reports, feature requests and questions,
+contact http://groups.google.com/group/isl-development
+
+Whenever you report a bug, please mention the exact version of PPCG
+that you are using (output of "./ppcg --version"). If you are unable
+to compile PPCG, then report the git version (output of "git describe")
+or the version number included in the name of the tarball.
+
+
+Citing PPCG
+
+If you use PPCG for your research, you are invited to cite
+the following paper.
+
+@article{Verdoolaege2013PPCG,
+ author = {Verdoolaege, Sven and Juega, Juan Carlos and Cohen, Albert and
+ G\'{o}mez, Jos{\'e} Ignacio and Tenllado, Christian and
+ Catthoor, Francky},
+ title = {Polyhedral parallel code generation for CUDA},
+ journal = {ACM Trans. Archit. Code Optim.},
+ issue_date = {January 2013},
+ volume = {9},
+ number = {4},
+ month = jan,
+ year = {2013},
+ issn = {1544-3566},
+ pages = {54:1--54:23},
+ doi = {10.1145/2400682.2400713},
+ acmid = {2400713},
+ publisher = {ACM},
+ address = {New York, NY, USA},
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cpu.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cpu.h
new file mode 100644
index 00000000000..6caf993aad5
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cpu.h
@@ -0,0 +1,15 @@
+#ifndef _CPU_H
+#define _CPU_H
+
+#include <isl/ctx.h>
+
+#include "ppcg.h"
+
+struct ppcg_options;
+
+__isl_give isl_printer *print_cpu(__isl_take isl_printer *p,
+ struct ppcg_scop *ps, struct ppcg_options *options);
+int generate_cpu(isl_ctx *ctx, struct ppcg_options *options,
+ const char *input, const char *output);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda.c
new file mode 100644
index 00000000000..380d6526bb1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda.c
@@ -0,0 +1,730 @@
+/*
+ * Copyright 2012 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <isl/aff.h>
+#include <isl/ast.h>
+
+#include "cuda_common.h"
+#include "cuda.h"
+#include "gpu.h"
+#include "gpu_print.h"
+#include "print.h"
+#include "util.h"
+
+static __isl_give isl_printer *print_cuda_macros(__isl_take isl_printer *p)
+{
+ const char *macros =
+ "#define cudaCheckReturn(ret) \\\n"
+ " do { \\\n"
+ " cudaError_t cudaCheckReturn_e = (ret); \\\n"
+ " if (cudaCheckReturn_e != cudaSuccess) { \\\n"
+ " fprintf(stderr, \"CUDA error: %s\\n\", "
+ "cudaGetErrorString(cudaCheckReturn_e)); \\\n"
+ " fflush(stderr); \\\n"
+ " } \\\n"
+ " assert(cudaCheckReturn_e == cudaSuccess); \\\n"
+ " } while(0)\n"
+ "#define cudaCheckKernel() \\\n"
+ " do { \\\n"
+ " cudaCheckReturn(cudaGetLastError()); \\\n"
+ " } while(0)\n\n";
+
+ p = isl_printer_print_str(p, macros);
+ return p;
+}
+
+/* Print a declaration for the device array corresponding to "array" on "p".
+ */
+static __isl_give isl_printer *declare_device_array(__isl_take isl_printer *p,
+ struct gpu_array_info *array)
+{
+ int i;
+
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, array->type);
+ p = isl_printer_print_str(p, " ");
+ if (!array->linearize && array->n_index > 1)
+ p = isl_printer_print_str(p, "(");
+ p = isl_printer_print_str(p, "*dev_");
+ p = isl_printer_print_str(p, array->name);
+ if (!array->linearize && array->n_index > 1) {
+ p = isl_printer_print_str(p, ")");
+ for (i = 1; i < array->n_index; i++) {
+ isl_ast_expr *bound;
+ bound = isl_ast_expr_get_op_arg(array->bound_expr,
+ 1 + i);
+ p = isl_printer_print_str(p, "[");
+ p = isl_printer_print_ast_expr(p, bound);
+ p = isl_printer_print_str(p, "]");
+ isl_ast_expr_free(bound);
+ }
+ }
+ p = isl_printer_print_str(p, ";");
+ p = isl_printer_end_line(p);
+
+ return p;
+}
+
+static __isl_give isl_printer *declare_device_arrays(__isl_take isl_printer *p,
+ struct gpu_prog *prog)
+{
+ int i;
+
+ for (i = 0; i < prog->n_array; ++i) {
+ if (!gpu_array_requires_device_allocation(&prog->array[i]))
+ continue;
+
+ p = declare_device_array(p, &prog->array[i]);
+ }
+ p = isl_printer_start_line(p);
+ p = isl_printer_end_line(p);
+ return p;
+}
+
+static __isl_give isl_printer *allocate_device_arrays(
+ __isl_take isl_printer *p, struct gpu_prog *prog)
+{
+ int i;
+
+ for (i = 0; i < prog->n_array; ++i) {
+ struct gpu_array_info *array = &prog->array[i];
+
+ if (!gpu_array_requires_device_allocation(&prog->array[i]))
+ continue;
+ p = ppcg_ast_expr_print_macros(array->bound_expr, p);
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p,
+ "cudaCheckReturn(cudaMalloc((void **) &dev_");
+ p = isl_printer_print_str(p, prog->array[i].name);
+ p = isl_printer_print_str(p, ", ");
+ p = gpu_array_info_print_size(p, &prog->array[i]);
+ p = isl_printer_print_str(p, "));");
+ p = isl_printer_end_line(p);
+ }
+ p = isl_printer_start_line(p);
+ p = isl_printer_end_line(p);
+ return p;
+}
+
+static __isl_give isl_printer *free_device_arrays(__isl_take isl_printer *p,
+ struct gpu_prog *prog)
+{
+ int i;
+
+ for (i = 0; i < prog->n_array; ++i) {
+ if (!gpu_array_requires_device_allocation(&prog->array[i]))
+ continue;
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "cudaCheckReturn(cudaFree(dev_");
+ p = isl_printer_print_str(p, prog->array[i].name);
+ p = isl_printer_print_str(p, "));");
+ p = isl_printer_end_line(p);
+ }
+
+ return p;
+}
+
+/* Print code to "p" for copying "array" from the host to the device
+ * in its entirety. The bounds on the extent of "array" have
+ * been precomputed in extract_array_info and are used in
+ * gpu_array_info_print_size.
+ */
+static __isl_give isl_printer *copy_array_to_device(__isl_take isl_printer *p,
+ struct gpu_array_info *array)
+{
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "cudaCheckReturn(cudaMemcpy(dev_");
+ p = isl_printer_print_str(p, array->name);
+ p = isl_printer_print_str(p, ", ");
+
+ if (gpu_array_is_scalar(array))
+ p = isl_printer_print_str(p, "&");
+ p = isl_printer_print_str(p, array->name);
+ p = isl_printer_print_str(p, ", ");
+
+ p = gpu_array_info_print_size(p, array);
+ p = isl_printer_print_str(p, ", cudaMemcpyHostToDevice));");
+ p = isl_printer_end_line(p);
+
+ return p;
+}
+
+/* Print code to "p" for copying "array" back from the device to the host
+ * in its entirety. The bounds on the extent of "array" have
+ * been precomputed in extract_array_info and are used in
+ * gpu_array_info_print_size.
+ */
+static __isl_give isl_printer *copy_array_from_device(
+ __isl_take isl_printer *p, struct gpu_array_info *array)
+{
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "cudaCheckReturn(cudaMemcpy(");
+ if (gpu_array_is_scalar(array))
+ p = isl_printer_print_str(p, "&");
+ p = isl_printer_print_str(p, array->name);
+ p = isl_printer_print_str(p, ", dev_");
+ p = isl_printer_print_str(p, array->name);
+ p = isl_printer_print_str(p, ", ");
+ p = gpu_array_info_print_size(p, array);
+ p = isl_printer_print_str(p, ", cudaMemcpyDeviceToHost));");
+ p = isl_printer_end_line(p);
+
+ return p;
+}
+
+static __isl_give isl_printer* print_reverse_list(__isl_take isl_printer *p, int len, int *list)
+{
+ int i;
+
+ if (len == 0)
+ return p;
+
+ p = isl_printer_print_str(p, "(");
+ for (i = 0; i < len; ++i) {
+ if (i)
+ p = isl_printer_print_str(p, ", ");
+ p = isl_printer_print_int(p, list[len - 1 - i]);
+ }
+ return isl_printer_print_str(p, ")");
+}
+
+/* Print the effective grid size as a list of the sizes in each
+ * dimension, from innermost to outermost.
+ */
+static __isl_give isl_printer *print_grid_size(__isl_take isl_printer *p,
+ struct ppcg_kernel *kernel)
+{
+ int i;
+ int dim;
+
+ dim = isl_multi_pw_aff_dim(kernel->grid_size, isl_dim_set);
+ if (dim == 0)
+ return p;
+
+ p = isl_printer_print_str(p, "(");
+ for (i = dim - 1; i >= 0; --i) {
+ isl_ast_expr *bound;
+
+ bound = isl_ast_expr_get_op_arg(kernel->grid_size_expr, 1 + i);
+ p = isl_printer_print_ast_expr(p, bound);
+ isl_ast_expr_free(bound);
+
+ if (i > 0)
+ p = isl_printer_print_str(p, ", ");
+ }
+
+ p = isl_printer_print_str(p, ")");
+
+ return p;
+}
+
+/* Print the grid definition.
+ */
+static __isl_give isl_printer *print_grid(__isl_take isl_printer *p,
+ struct ppcg_kernel *kernel)
+{
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "dim3 k");
+ p = isl_printer_print_int(p, kernel->id);
+ p = isl_printer_print_str(p, "_dimGrid");
+ p = print_grid_size(p, kernel);
+ p = isl_printer_print_str(p, ";");
+ p = isl_printer_end_line(p);
+
+ return p;
+}
+
+/* Print the arguments to a kernel declaration or call. If "types" is set,
+ * then print a declaration (including the types of the arguments).
+ *
+ * The arguments are printed in the following order
+ * - the arrays accessed by the kernel
+ * - the parameters
+ * - the host loop iterators
+ */
+static __isl_give isl_printer *print_kernel_arguments(__isl_take isl_printer *p,
+ struct gpu_prog *prog, struct ppcg_kernel *kernel, int types)
+{
+ int i, n;
+ int first = 1;
+ unsigned nparam;
+ isl_space *space;
+ const char *type;
+
+ for (i = 0; i < prog->n_array; ++i) {
+ int required;
+
+ required = ppcg_kernel_requires_array_argument(kernel, i);
+ if (required < 0)
+ return isl_printer_free(p);
+ if (!required)
+ continue;
+
+ if (!first)
+ p = isl_printer_print_str(p, ", ");
+
+ if (types)
+ p = gpu_array_info_print_declaration_argument(p,
+ &prog->array[i], NULL);
+ else
+ p = gpu_array_info_print_call_argument(p,
+ &prog->array[i]);
+
+ first = 0;
+ }
+
+ space = isl_union_set_get_space(kernel->arrays);
+ nparam = isl_space_dim(space, isl_dim_param);
+ for (i = 0; i < nparam; ++i) {
+ const char *name;
+
+ name = isl_space_get_dim_name(space, isl_dim_param, i);
+
+ if (!first)
+ p = isl_printer_print_str(p, ", ");
+ if (types)
+ p = isl_printer_print_str(p, "int ");
+ p = isl_printer_print_str(p, name);
+
+ first = 0;
+ }
+ isl_space_free(space);
+
+ n = isl_space_dim(kernel->space, isl_dim_set);
+ type = isl_options_get_ast_iterator_type(prog->ctx);
+ for (i = 0; i < n; ++i) {
+ const char *name;
+
+ if (!first)
+ p = isl_printer_print_str(p, ", ");
+ name = isl_space_get_dim_name(kernel->space, isl_dim_set, i);
+ if (types) {
+ p = isl_printer_print_str(p, type);
+ p = isl_printer_print_str(p, " ");
+ }
+ p = isl_printer_print_str(p, name);
+
+ first = 0;
+ }
+
+ return p;
+}
+
+/* Print the header of the given kernel.
+ */
+static __isl_give isl_printer *print_kernel_header(__isl_take isl_printer *p,
+ struct gpu_prog *prog, struct ppcg_kernel *kernel)
+{
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "__global__ void kernel");
+ p = isl_printer_print_int(p, kernel->id);
+ p = isl_printer_print_str(p, "(");
+ p = print_kernel_arguments(p, prog, kernel, 1);
+ p = isl_printer_print_str(p, ")");
+
+ return p;
+}
+
+/* Print the header of the given kernel to both gen->cuda.kernel_h
+ * and gen->cuda.kernel_c.
+ */
+static void print_kernel_headers(struct gpu_prog *prog,
+ struct ppcg_kernel *kernel, struct cuda_info *cuda)
+{
+ isl_printer *p;
+
+ p = isl_printer_to_file(prog->ctx, cuda->kernel_h);
+ p = isl_printer_set_output_format(p, ISL_FORMAT_C);
+ p = print_kernel_header(p, prog, kernel);
+ p = isl_printer_print_str(p, ";");
+ p = isl_printer_end_line(p);
+ isl_printer_free(p);
+
+ p = isl_printer_to_file(prog->ctx, cuda->kernel_c);
+ p = isl_printer_set_output_format(p, ISL_FORMAT_C);
+ p = print_kernel_header(p, prog, kernel);
+ p = isl_printer_end_line(p);
+ isl_printer_free(p);
+}
+
+static void print_indent(FILE *dst, int indent)
+{
+ fprintf(dst, "%*s", indent, "");
+}
+
+/* Print a list of iterators of type "type" with names "ids" to "out".
+ * Each iterator is assigned one of the cuda identifiers in cuda_dims.
+ * In particular, the last iterator is assigned the x identifier
+ * (the first in the list of cuda identifiers).
+ */
+static void print_iterators(FILE *out, const char *type,
+ __isl_keep isl_id_list *ids, const char *cuda_dims[])
+{
+ int i, n;
+
+ n = isl_id_list_n_id(ids);
+ if (n <= 0)
+ return;
+ print_indent(out, 4);
+ fprintf(out, "%s ", type);
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+
+ if (i)
+ fprintf(out, ", ");
+ id = isl_id_list_get_id(ids, i);
+ fprintf(out, "%s = %s", isl_id_get_name(id),
+ cuda_dims[n - 1 - i]);
+ isl_id_free(id);
+ }
+ fprintf(out, ";\n");
+}
+
+static void print_kernel_iterators(FILE *out, struct ppcg_kernel *kernel)
+{
+ isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
+ const char *type;
+ const char *block_dims[] = { "blockIdx.x", "blockIdx.y" };
+ const char *thread_dims[] = { "threadIdx.x", "threadIdx.y",
+ "threadIdx.z" };
+
+ type = isl_options_get_ast_iterator_type(ctx);
+
+ print_iterators(out, type, kernel->block_ids, block_dims);
+ print_iterators(out, type, kernel->thread_ids, thread_dims);
+}
+
+static __isl_give isl_printer *print_kernel_var(__isl_take isl_printer *p,
+ struct ppcg_kernel_var *var)
+{
+ int j;
+
+ p = isl_printer_start_line(p);
+ if (var->type == ppcg_access_shared)
+ p = isl_printer_print_str(p, "__shared__ ");
+ p = isl_printer_print_str(p, var->array->type);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_str(p, var->name);
+ for (j = 0; j < var->array->n_index; ++j) {
+ isl_val *v;
+
+ p = isl_printer_print_str(p, "[");
+ v = isl_vec_get_element_val(var->size, j);
+ p = isl_printer_print_val(p, v);
+ isl_val_free(v);
+ p = isl_printer_print_str(p, "]");
+ }
+ p = isl_printer_print_str(p, ";");
+ p = isl_printer_end_line(p);
+
+ return p;
+}
+
+static __isl_give isl_printer *print_kernel_vars(__isl_take isl_printer *p,
+ struct ppcg_kernel *kernel)
+{
+ int i;
+
+ for (i = 0; i < kernel->n_var; ++i)
+ p = print_kernel_var(p, &kernel->var[i]);
+
+ return p;
+}
+
+/* Print a sync statement.
+ */
+static __isl_give isl_printer *print_sync(__isl_take isl_printer *p,
+ struct ppcg_kernel_stmt *stmt)
+{
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "__syncthreads();");
+ p = isl_printer_end_line(p);
+
+ return p;
+}
+
+/* This function is called for each user statement in the AST,
+ * i.e., for each kernel body statement, copy statement or sync statement.
+ */
+static __isl_give isl_printer *print_kernel_stmt(__isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *print_options,
+ __isl_keep isl_ast_node *node, void *user)
+{
+ isl_id *id;
+ struct ppcg_kernel_stmt *stmt;
+
+ id = isl_ast_node_get_annotation(node);
+ stmt = isl_id_get_user(id);
+ isl_id_free(id);
+
+ isl_ast_print_options_free(print_options);
+
+ switch (stmt->type) {
+ case ppcg_kernel_copy:
+ return ppcg_kernel_print_copy(p, stmt);
+ case ppcg_kernel_sync:
+ return print_sync(p, stmt);
+ case ppcg_kernel_domain:
+ return ppcg_kernel_print_domain(p, stmt);
+ }
+
+ return p;
+}
+
+static void print_kernel(struct gpu_prog *prog, struct ppcg_kernel *kernel,
+ struct cuda_info *cuda)
+{
+ isl_ctx *ctx = isl_ast_node_get_ctx(kernel->tree);
+ isl_ast_print_options *print_options;
+ isl_printer *p;
+
+ print_kernel_headers(prog, kernel, cuda);
+ fprintf(cuda->kernel_c, "{\n");
+ print_kernel_iterators(cuda->kernel_c, kernel);
+
+ p = isl_printer_to_file(ctx, cuda->kernel_c);
+ p = isl_printer_set_output_format(p, ISL_FORMAT_C);
+ p = isl_printer_indent(p, 4);
+
+ p = print_kernel_vars(p, kernel);
+ p = isl_printer_end_line(p);
+ p = ppcg_set_macro_names(p);
+ p = gpu_print_macros(p, kernel->tree);
+
+ print_options = isl_ast_print_options_alloc(ctx);
+ print_options = isl_ast_print_options_set_print_user(print_options,
+ &print_kernel_stmt, NULL);
+ p = isl_ast_node_print(kernel->tree, p, print_options);
+ isl_printer_free(p);
+
+ fprintf(cuda->kernel_c, "}\n");
+}
+
+/* Print code for initializing the device for execution of the transformed
+ * code. This includes declaring locally defined variables as well as
+ * declaring and allocating the required copies of arrays on the device.
+ */
+static __isl_give isl_printer *init_device(__isl_take isl_printer *p,
+ struct gpu_prog *prog)
+{
+ p = print_cuda_macros(p);
+
+ p = gpu_print_local_declarations(p, prog);
+ p = declare_device_arrays(p, prog);
+ p = allocate_device_arrays(p, prog);
+
+ return p;
+}
+
+/* Print code for clearing the device after execution of the transformed code.
+ * In particular, free the memory that was allocated on the device.
+ */
+static __isl_give isl_printer *clear_device(__isl_take isl_printer *p,
+ struct gpu_prog *prog)
+{
+ p = free_device_arrays(p, prog);
+
+ return p;
+}
+
+/* Print a statement for copying an array to or from the device,
+ * or for initializing or clearing the device.
+ * The statement identifier of a copying node is called
+ * "to_device_<array name>" or "from_device_<array name>" and
+ * its user pointer points to the gpu_array_info of the array
+ * that needs to be copied.
+ * The node for initializing the device is called "init_device".
+ * The node for clearing the device is called "clear_device".
+ *
+ * Extract the array (if any) from the identifier and call
+ * init_device, clear_device, copy_array_to_device or copy_array_from_device.
+ */
+static __isl_give isl_printer *print_device_node(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node, struct gpu_prog *prog)
+{
+ isl_ast_expr *expr, *arg;
+ isl_id *id;
+ const char *name;
+ struct gpu_array_info *array;
+
+ expr = isl_ast_node_user_get_expr(node);
+ arg = isl_ast_expr_get_op_arg(expr, 0);
+ id = isl_ast_expr_get_id(arg);
+ name = isl_id_get_name(id);
+ array = isl_id_get_user(id);
+ isl_id_free(id);
+ isl_ast_expr_free(arg);
+ isl_ast_expr_free(expr);
+
+ if (!name)
+ return isl_printer_free(p);
+ if (!strcmp(name, "init_device"))
+ return init_device(p, prog);
+ if (!strcmp(name, "clear_device"))
+ return clear_device(p, prog);
+ if (!array)
+ return isl_printer_free(p);
+
+ if (!prefixcmp(name, "to_device"))
+ return copy_array_to_device(p, array);
+ else
+ return copy_array_from_device(p, array);
+}
+
+struct print_host_user_data {
+ struct cuda_info *cuda;
+ struct gpu_prog *prog;
+};
+
+/* Print the user statement of the host code to "p".
+ *
+ * The host code may contain original user statements, kernel launches,
+ * statements that copy data to/from the device and statements
+ * the initialize or clear the device.
+ * The original user statements and the kernel launches have
+ * an associated annotation, while the other statements do not.
+ * The latter are handled by print_device_node.
+ * The annotation on the user statements is called "user".
+ *
+ * In case of a kernel launch, print a block of statements that
+ * defines the grid and the block and then launches the kernel.
+ */
+__isl_give isl_printer *print_host_user(__isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *print_options,
+ __isl_keep isl_ast_node *node, void *user)
+{
+ isl_id *id;
+ int is_user;
+ struct ppcg_kernel *kernel;
+ struct ppcg_kernel_stmt *stmt;
+ struct print_host_user_data *data;
+
+ isl_ast_print_options_free(print_options);
+
+ data = (struct print_host_user_data *) user;
+
+ id = isl_ast_node_get_annotation(node);
+ if (!id)
+ return print_device_node(p, node, data->prog);
+
+ is_user = !strcmp(isl_id_get_name(id), "user");
+ kernel = is_user ? NULL : isl_id_get_user(id);
+ stmt = is_user ? isl_id_get_user(id) : NULL;
+ isl_id_free(id);
+
+ if (is_user)
+ return ppcg_kernel_print_domain(p, stmt);
+
+ p = ppcg_start_block(p);
+
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "dim3 k");
+ p = isl_printer_print_int(p, kernel->id);
+ p = isl_printer_print_str(p, "_dimBlock");
+ p = print_reverse_list(p, kernel->n_block, kernel->block_dim);
+ p = isl_printer_print_str(p, ";");
+ p = isl_printer_end_line(p);
+
+ p = print_grid(p, kernel);
+
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "kernel");
+ p = isl_printer_print_int(p, kernel->id);
+ p = isl_printer_print_str(p, " <<<k");
+ p = isl_printer_print_int(p, kernel->id);
+ p = isl_printer_print_str(p, "_dimGrid, k");
+ p = isl_printer_print_int(p, kernel->id);
+ p = isl_printer_print_str(p, "_dimBlock>>> (");
+ p = print_kernel_arguments(p, data->prog, kernel, 0);
+ p = isl_printer_print_str(p, ");");
+ p = isl_printer_end_line(p);
+
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "cudaCheckKernel();");
+ p = isl_printer_end_line(p);
+
+ p = ppcg_end_block(p);
+
+ p = isl_printer_start_line(p);
+ p = isl_printer_end_line(p);
+
+#if 0
+ print_kernel(data->prog, kernel, data->cuda);
+#endif
+
+ return p;
+}
+
+static __isl_give isl_printer *print_host_code(__isl_take isl_printer *p,
+ struct gpu_prog *prog, __isl_keep isl_ast_node *tree,
+ struct cuda_info *cuda)
+{
+ isl_ast_print_options *print_options;
+ isl_ctx *ctx = isl_ast_node_get_ctx(tree);
+ struct print_host_user_data data = { cuda, prog };
+
+ print_options = isl_ast_print_options_alloc(ctx);
+ print_options = isl_ast_print_options_set_print_user(print_options,
+ &print_host_user, &data);
+
+ p = gpu_print_macros(p, tree);
+ p = isl_ast_node_print(tree, p, print_options);
+
+ return p;
+}
+
+/* Given a gpu_prog "prog" and the corresponding transformed AST
+ * "tree", print the entire CUDA code to "p".
+ * "types" collects the types for which a definition has already
+ * been printed.
+ */
+static __isl_give isl_printer *print_cuda(__isl_take isl_printer *p,
+ struct gpu_prog *prog, __isl_keep isl_ast_node *tree,
+ struct gpu_types *types, void *user)
+{
+ struct cuda_info *cuda = user;
+ isl_printer *kernel;
+
+ kernel = isl_printer_to_file(isl_printer_get_ctx(p), cuda->kernel_c);
+ kernel = isl_printer_set_output_format(kernel, ISL_FORMAT_C);
+ kernel = gpu_print_types(kernel, types, prog);
+ isl_printer_free(kernel);
+
+ if (!kernel)
+ return isl_printer_free(p);
+
+ p = print_host_code(p, prog, tree, cuda);
+
+ return p;
+}
+
+/* Transform the code in the file called "input" by replacing
+ * all scops by corresponding CUDA code.
+ * The names of the output files are derived from "input".
+ *
+ * We let generate_gpu do all the hard work and then let it call
+ * us back for printing the AST in print_cuda.
+ *
+ * To prepare for this printing, we first open the output files
+ * and we close them after generate_gpu has finished.
+ */
+int generate_cuda(isl_ctx *ctx, struct ppcg_options *options,
+ const char *input)
+{
+ struct cuda_info cuda;
+ int r;
+
+ cuda_open_files(&cuda, input);
+
+ r = generate_gpu(ctx, input, cuda.host_c, options, &print_cuda, &cuda);
+
+ cuda_close_files(&cuda);
+
+ return r;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda.h
new file mode 100644
index 00000000000..450fc697ca0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda.h
@@ -0,0 +1,13 @@
+#ifndef _CUDA_H
+#define _CUDA_H
+
+#include "ppcg_options.h"
+#include "ppcg.h"
+
+int generate_cuda(isl_ctx *ctx, struct ppcg_options *options,
+ const char *input);
+
+__isl_give isl_printer *print_host_user(__isl_take isl_printer *p,
+ __isl_take isl_ast_print_options *print_options,
+ __isl_keep isl_ast_node *node, void *user);
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda_common.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda_common.c
new file mode 100644
index 00000000000..497353f4319
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda_common.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <ctype.h>
+#include <limits.h>
+#include <string.h>
+
+#include "cuda_common.h"
+#include "ppcg.h"
+
+/* Open the host .cu file and the kernel .hu and .cu files for writing.
+ * Add the necessary includes.
+ */
+void cuda_open_files(struct cuda_info *info, const char *input)
+{
+ char name[PATH_MAX];
+ int len;
+
+ len = ppcg_extract_base_name(name, input);
+
+ strcpy(name + len, "_host.cu");
+ info->host_c = fopen(name, "w");
+
+ strcpy(name + len, "_kernel.cu");
+ info->kernel_c = fopen(name, "w");
+
+ strcpy(name + len, "_kernel.hu");
+ info->kernel_h = fopen(name, "w");
+ fprintf(info->host_c, "#include <assert.h>\n");
+ fprintf(info->host_c, "#include <stdio.h>\n");
+ fprintf(info->host_c, "#include \"%s\"\n", name);
+ fprintf(info->kernel_c, "#include \"%s\"\n", name);
+ fprintf(info->kernel_h, "#include \"cuda.h\"\n\n");
+}
+
+/* Close all output files.
+ */
+void cuda_close_files(struct cuda_info *info)
+{
+ fclose(info->kernel_c);
+ fclose(info->kernel_h);
+ fclose(info->host_c);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda_common.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda_common.h
new file mode 100644
index 00000000000..2a7db952cdf
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/cuda_common.h
@@ -0,0 +1,15 @@
+#ifndef _CUDA_COMMON_H_
+#define _CUDA_COMMON_H_
+
+#include <stdio.h>
+
+struct cuda_info {
+ FILE *host_c;
+ FILE *kernel_c;
+ FILE *kernel_h;
+};
+
+void cuda_open_files(struct cuda_info *info, const char *input);
+void cuda_close_files(struct cuda_info *info);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/external.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/external.c
new file mode 100644
index 00000000000..c5ef6320e64
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/external.c
@@ -0,0 +1,192 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pet.h>
+#include "cpu.h"
+#include "opencl.h"
+
+
+#define die() { \
+ fprintf(stderr, "Dummy function %s called\n", __FUNCTION__); \
+ abort(); \
+}
+
+__isl_give isl_union_map *pet_scop_compute_outer_to_any(
+ __isl_keep pet_scop *scop) {
+ die();
+}
+__isl_give isl_union_map *pet_scop_compute_outer_to_inner(
+ __isl_keep pet_scop *scop) {
+ die();
+}
+enum pet_tree_type pet_tree_get_type(__isl_keep pet_tree *tree) {
+ die();
+}
+int pet_tree_foreach_access_expr(__isl_keep pet_tree *tree,
+ int (*fn)(__isl_keep pet_expr *expr, void *user), void *user) {
+ die();
+}
+isl_ctx *pet_expr_get_ctx(__isl_keep pet_expr *expr) {
+ die();
+}
+isl_bool pet_expr_access_is_read(__isl_keep pet_expr *expr) {
+ die();
+}
+isl_bool pet_expr_access_is_write(__isl_keep pet_expr *expr) {
+ die();
+}
+__isl_give isl_union_map *pet_expr_access_get_tagged_may_read(
+ __isl_keep pet_expr *expr) {
+ die();
+}
+__isl_give isl_union_map *pet_expr_access_get_tagged_may_write(
+ __isl_keep pet_expr *expr) {
+ die();
+}
+__isl_give isl_union_map *pet_expr_access_get_must_write(
+ __isl_keep pet_expr *expr) {
+ die();
+}
+__isl_give isl_multi_pw_aff *pet_expr_access_get_index(
+ __isl_keep pet_expr *expr) {
+ die();
+}
+__isl_give isl_id *pet_expr_access_get_ref_id(__isl_keep pet_expr *expr) {
+ die();
+}
+__isl_give isl_printer *print_cpu(__isl_take isl_printer *p,
+ struct ppcg_scop *ps, struct ppcg_options *options) {
+ die();
+}
+
+__isl_give isl_printer *pet_stmt_print_body(struct pet_stmt *stmt,
+ __isl_take isl_printer *p, __isl_keep isl_id_to_ast_expr *ref2expr) {
+ die();
+}
+unsigned pet_loc_get_start(__isl_keep pet_loc *loc) {
+ die();
+}
+unsigned pet_loc_get_end(__isl_keep pet_loc *loc) {
+ die();
+}
+int pet_transform_C_source(isl_ctx *ctx, const char *input, FILE *output,
+ __isl_give isl_printer *(*transform)(__isl_take isl_printer *p,
+ __isl_take pet_scop *scop, void *user), void *user) {
+ die();
+}
+__isl_give isl_printer *pet_scop_print_original(__isl_keep pet_scop *scop,
+ __isl_take isl_printer *p) {
+ die();
+}
+__isl_null pet_scop *pet_scop_free(__isl_take pet_scop *scop) {
+ die();
+}
+__isl_give pet_scop *pet_scop_align_params(__isl_take pet_scop *scop) {
+ die();
+}
+int pet_scop_can_build_ast_exprs(__isl_keep pet_scop *scop) {
+ die();
+}
+int pet_scop_has_data_dependent_conditions(__isl_keep pet_scop *scop) {
+ die();
+}
+int pet_tree_foreach_expr(__isl_keep pet_tree *tree,
+ int (*fn)(__isl_keep pet_expr *expr, void *user), void *user) {
+ die();
+}
+int pet_expr_foreach_call_expr(__isl_keep pet_expr *expr,
+ int (*fn)(__isl_keep pet_expr *expr, void *user), void *user) {
+ die();
+}
+int pet_stmt_is_kill(struct pet_stmt *stmt) {
+ die();
+}
+struct isl_args pet_options_args;
+const char *ppcg_version(void) {
+ die();
+}
+int pet_options_set_encapsulate_dynamic_control(isl_ctx *ctx, int val) {
+ die();
+}
+int generate_opencl(isl_ctx *ctx, struct ppcg_options *options,
+ const char *input, const char *output) {
+ die();
+}
+int generate_cpu(isl_ctx *ctx, struct ppcg_options *options,
+ const char *input, const char *output) {
+ die();
+}
+__isl_give isl_id_to_ast_expr *pet_stmt_build_ast_exprs(struct pet_stmt *stmt,
+ __isl_keep isl_ast_build *build,
+ __isl_give isl_multi_pw_aff *(*fn_index)(
+ __isl_take isl_multi_pw_aff *mpa, __isl_keep isl_id *id,
+ void *user), void *user_index,
+ __isl_give isl_ast_expr *(*fn_expr)(__isl_take isl_ast_expr *expr,
+ __isl_keep isl_id *id, void *user), void *user_expr) {
+ die();
+}
+__isl_give isl_union_map *pet_scop_get_tagged_may_reads(
+ __isl_keep pet_scop *scop) {
+ die();
+}
+__isl_give isl_union_map *pet_scop_get_may_reads(__isl_keep pet_scop *scop) {
+ die();
+}
+__isl_give isl_union_map *pet_scop_get_may_writes(__isl_keep pet_scop *scop) {
+ die();
+}
+__isl_give isl_union_map *pet_scop_get_must_writes(__isl_keep pet_scop *scop) {
+ die();
+}
+__isl_give isl_union_map *pet_scop_get_tagged_may_writes(
+ __isl_keep pet_scop *scop) {
+ die();
+}
+__isl_give isl_union_map *pet_scop_get_tagged_must_writes(
+ __isl_keep pet_scop *scop) {
+ die();
+}
+__isl_give isl_union_map *pet_scop_get_must_kills(__isl_keep pet_scop *scop) {
+ die();
+}
+__isl_give isl_union_map *pet_scop_get_tagged_must_kills(
+ __isl_keep pet_scop *scop) {
+ die();
+}
+__isl_keep const char *pet_expr_call_get_name(__isl_keep pet_expr *expr) {
+ die();
+}
+__isl_give pet_expr *pet_expr_call_set_name(__isl_take pet_expr *expr,
+ __isl_keep const char *name) {
+ die();
+}
+__isl_give pet_expr *pet_expr_get_arg(__isl_keep pet_expr *expr, int pos) {
+ die();
+}
+__isl_give pet_expr *pet_expr_new_cast(const char *type_name,
+ __isl_take pet_expr *arg) {
+ die();
+}
+__isl_give pet_expr *pet_expr_set_arg(__isl_take pet_expr *expr, int pos,
+ __isl_take pet_expr *arg) {
+ die();
+}
+__isl_give pet_tree *pet_tree_copy(__isl_keep pet_tree *tree) {
+ die();
+}
+__isl_null pet_tree *pet_tree_free(__isl_take pet_tree *tree) {
+ die();
+}
+__isl_give pet_tree *pet_tree_map_call_expr(__isl_take pet_tree *tree,
+ __isl_give pet_expr *(*fn)(__isl_take pet_expr *expr, void *user),
+ void *user) {
+ die();
+}
+__isl_give isl_union_map *pet_expr_access_get_may_read(
+ __isl_keep pet_expr *expr) {
+ die();
+}
+__isl_give isl_union_map *pet_expr_access_get_may_write(
+ __isl_keep pet_expr *expr) {
+ die();
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu.c
new file mode 100644
index 00000000000..cfd998f1c31
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu.c
@@ -0,0 +1,5849 @@
+/*
+ * Copyright 2010-2011 INRIA Saclay
+ * Copyright 2012-2013 Ecole Normale Superieure
+ * Copyright 2015-2016 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isl/polynomial.h>
+#include <isl/union_set.h>
+#include <isl/aff.h>
+#include <isl/ilp.h>
+#include <isl/flow.h>
+#include <isl/schedule.h>
+#include <isl/schedule_node.h>
+#include <isl/options.h>
+#include <isl/ast_build.h>
+
+#include "cpu.h"
+#include "gpu.h"
+#include "gpu_array_tile.h"
+#include "gpu_group.h"
+#include "gpu_hybrid.h"
+#include "gpu_tree.h"
+#include "hybrid.h"
+#include "schedule.h"
+#include "ppcg_options.h"
+#include "print.h"
+#include "util.h"
+
+struct gpu_array_info;
+
+/* Return the name of the outer array (of structs) accessed by "access".
+ */
+static const char *get_outer_array_name(__isl_keep isl_map *access)
+{
+ isl_space *space;
+ const char *name;
+
+ space = isl_space_range(isl_map_get_space(access));
+ while (space && isl_space_is_wrapping(space))
+ space = isl_space_domain(isl_space_unwrap(space));
+ name = isl_space_get_tuple_name(space, isl_dim_set);
+ isl_space_free(space);
+
+ return name;
+}
+
+/* Collect all references to the given array and store pointers to them
+ * in array->refs.
+ */
+void collect_references(struct gpu_prog *prog,
+ struct gpu_array_info *array)
+{
+ int i;
+ int n;
+
+ n = 0;
+ for (i = 0; i < prog->n_stmts; ++i) {
+ struct gpu_stmt *stmt = &prog->stmts[i];
+ struct gpu_stmt_access *access;
+
+ for (access = stmt->accesses; access; access = access->next) {
+ const char *name;
+ name = get_outer_array_name(access->access);
+ if (name && !strcmp(array->name, name))
+ n++;
+ }
+ }
+
+ array->n_ref = n;
+ array->refs = isl_alloc_array(prog->ctx, struct gpu_stmt_access *, n);
+ assert(array->refs);
+
+ n = 0;
+ for (i = 0; i < prog->n_stmts; ++i) {
+ struct gpu_stmt *stmt = &prog->stmts[i];
+ struct gpu_stmt_access *access;
+
+ for (access = stmt->accesses; access; access = access->next) {
+ const char *name;
+ name = get_outer_array_name(access->access);
+ if (!name || strcmp(array->name, name))
+ continue;
+
+ array->refs[n++] = access;
+ }
+ }
+}
+
+/* Compute and return the extent of "array", taking into account the set of
+ * accessed elements.
+ *
+ * In particular, the extent in the outer dimension is taken
+ * from "accessed", while the extents in the remaining dimensions
+ * are taken from array->extent.
+ *
+ * The extent in the outer dimension cannot be taken from array->extent
+ * because that may be unbounded. Furthermore, even if it is bounded,
+ * it may be larger than the piece of the array that is being accessed.
+ */
+static __isl_give isl_set *compute_extent(struct pet_array *array,
+ __isl_keep isl_set *accessed)
+{
+ int n_index;
+ isl_id *id;
+ isl_set *outer;
+ isl_set *extent;
+
+ extent = isl_set_copy(array->extent);
+
+ n_index = isl_set_dim(accessed, isl_dim_set);
+ if (n_index == 0)
+ return extent;
+
+ extent = isl_set_project_out(extent, isl_dim_set, 0, 1);
+ outer = isl_set_copy(accessed);
+ outer = isl_set_project_out(outer, isl_dim_set, 1, n_index - 1);
+ extent = isl_set_flat_product(outer, extent);
+ id = isl_set_get_tuple_id(accessed);
+ extent = isl_set_set_tuple_id(extent, id);
+
+ return extent;
+}
+
+/* Is the array "array" being extracted a read-only scalar?
+ *
+ * That is, is "array" a scalar that is never possibly written to.
+ * An array containing structures is never considered to be a scalar.
+ */
+static int is_read_only_scalar(struct gpu_array_info *array,
+ struct gpu_prog *prog)
+{
+ isl_set *space;
+ isl_union_map *write;
+ int empty;
+
+ if (array->has_compound_element)
+ return 0;
+ if (array->n_index != 0)
+ return 0;
+
+ write = isl_union_map_copy(prog->may_write);
+ space = isl_set_universe(isl_space_copy(array->space));
+ write = isl_union_map_intersect_range(write,
+ isl_union_set_from_set(space));
+ empty = isl_union_map_is_empty(write);
+ isl_union_map_free(write);
+
+ return empty;
+}
+
+/* Is "array" only accessed as individual, fixed elements?
+ * That is, does each access to "array" access a single, fixed element?
+ */
+isl_bool only_fixed_element_accessed(struct gpu_array_info *array)
+{
+ int i;
+
+ for (i = 0; i < array->n_ref; ++i)
+ if (!array->refs[i]->fixed_element)
+ return isl_bool_false;
+
+ return isl_bool_true;
+}
+
+/* Compute bounds on the host array "pa" based on the corresponding
+ * accessed elements in "arrays"
+ * and collect all references to the array.
+ * Store the results in "info".
+ *
+ * If the array is zero-dimensional and does not contain structures,
+ * i.e., if the array is a scalar, we check whether it is read-only.
+ * We also check whether the array is accessed at all.
+ */
+static int extract_array_info(struct gpu_prog *prog,
+ struct gpu_array_info *info, struct pet_array *pa,
+ __isl_keep isl_union_set *arrays)
+{
+ int empty;
+ const char *name;
+ int n_index;
+ isl_multi_pw_aff *bounds;
+ isl_set *accessed, *extent;
+
+ n_index = isl_set_dim(pa->extent, isl_dim_set);
+ name = isl_set_get_tuple_name(pa->extent);
+
+ info->space = isl_set_get_space(pa->extent);
+ info->name = strdup(name);
+ info->n_index = n_index;
+ info->linearize = prog->scop->options->linearize_device_arrays;
+
+ info->type = strdup(pa->element_type);
+ info->size = pa->element_size;
+ info->local = pa->declared && !pa->exposed;
+ info->has_compound_element = pa->element_is_record;
+ info->read_only_scalar = is_read_only_scalar(info, prog);
+
+ info->declared_extent = isl_set_copy(pa->extent);
+ accessed = isl_union_set_extract_set(arrays,
+ isl_space_copy(info->space));
+ empty = isl_set_is_empty(accessed);
+ extent = compute_extent(pa, accessed);
+ isl_set_free(accessed);
+ info->extent = extent;
+ if (empty < 0)
+ return -1;
+ info->accessed = !empty;
+ bounds = ppcg_size_from_extent(isl_set_copy(extent));
+ bounds = isl_multi_pw_aff_gist(bounds, isl_set_copy(prog->context));
+ if (!bounds)
+ return -1;
+ if (!isl_multi_pw_aff_is_cst(bounds))
+ info->linearize = 1;
+ info->bound = bounds;
+
+ collect_references(prog, info);
+ info->only_fixed_element = only_fixed_element_accessed(info);
+
+ return 0;
+}
+
+/* Remove independence from the order constraints "order" on array "array".
+ * Since the pairs of iterations in the filter relation of an independence
+ * are guaranteed to be completely independent by the user, there is
+ * no need to ensure that live ranges are ordered along those pairs.
+ * We make an exception for local variables, though, as the independence
+ * guarantee does not apply to those.
+ *
+ * The order constraints are used in two places.
+ * Those on scalars are used in check_scalar_live_ranges to check if
+ * we need to force the scalar to be private. Any non-local scalar
+ * should not be forced scalar if it only appears in independent loops.
+ * Those on non-scalars are added to the coincidence constraints
+ * in compute_schedule because we do not support any array expansion.
+ * Accesses to non-local arrays should not prevent a loop from being
+ * considered coincident so we should indeed remove those constraints
+ * from the order constraints.
+ */
+static __isl_give isl_union_map *remove_independences(struct gpu_prog *prog,
+ struct gpu_array_info *array, __isl_take isl_union_map *order)
+{
+ // We do not have independence information in Polly. Hence, make this
+ // function a no-op.
+ return order;
+ int i;
+
+ for (i = 0; i < prog->scop->pet->n_independence; ++i) {
+ struct pet_independence *pi = prog->scop->pet->independences[i];
+ if (isl_union_set_contains(pi->local, array->space))
+ continue;
+
+ order = isl_union_map_subtract(order,
+ isl_union_map_copy(pi->filter));
+ }
+
+ return order;
+}
+
+/* For each array in "prog", store the (untagged) order dependences
+ * derived from the array in array->dep_order.
+ * In particular, consider all references that access the given array
+ * and take the order dependences that have one of these references
+ * as source. (Since an order dependence relates two references to
+ * the same array, the target of these order dependences will also
+ * be one of these references.)
+ * Additionally, store the union of these array->dep_order relations
+ * for all arrays that cannot be mapped to private memory in prog->array_order.
+ */
+void collect_order_dependences(struct gpu_prog *prog)
+{
+ int i;
+ isl_space *space;
+ isl_union_map *accesses;
+
+ space = isl_union_map_get_space(prog->read);
+ prog->array_order = isl_union_map_empty(space);
+
+ accesses = isl_union_map_copy(prog->scop->tagged_reads);
+ accesses = isl_union_map_union(accesses,
+ isl_union_map_copy(prog->scop->tagged_may_writes));
+ accesses = isl_union_map_universe(accesses);
+ accesses = isl_union_map_apply_range(accesses,
+ isl_union_map_copy(prog->to_outer));
+
+ for (i = 0; i < prog->n_array; ++i) {
+ struct gpu_array_info *array = &prog->array[i];
+ isl_set *set;
+ isl_union_set *uset;
+ isl_union_map *order;
+
+ set = isl_set_universe(isl_space_copy(array->space));
+ uset = isl_union_set_from_set(set);
+ uset = isl_union_map_domain(
+ isl_union_map_intersect_range(isl_union_map_copy(accesses),
+ uset));
+ order = isl_union_map_copy(prog->scop->tagged_dep_order);
+ order = isl_union_map_intersect_domain(order, uset);
+ order = isl_union_map_zip(order);
+ order = isl_union_set_unwrap(isl_union_map_domain(order));
+ order = remove_independences(prog, array, order);
+ array->dep_order = order;
+
+ if (gpu_array_can_be_private(array))
+ continue;
+
+ prog->array_order = isl_union_map_union(prog->array_order,
+ isl_union_map_copy(array->dep_order));
+ }
+
+ isl_union_map_free(accesses);
+}
+
+/* Construct a gpu_array_info for each array referenced by prog->scop and
+ * collect them in prog->array.
+ *
+ * The sizes are based on the extents and the set of possibly accessed
+ * elements by "prog".
+ * If there are any member accesses involved, then they are first mapped
+ * to the outer arrays of structs.
+ * Only extract gpu_array_info entries for these outer arrays.
+ *
+ * If we are allowing live range reordering, then also set
+ * the dep_order field. Otherwise leave it NULL.
+ */
+static int collect_array_info(struct gpu_prog *prog)
+{
+ int i;
+ int r = 0;
+ isl_union_set *arrays;
+
+ arrays = isl_union_map_range(isl_union_map_copy(prog->read));
+ arrays = isl_union_set_union(arrays,
+ isl_union_map_range(isl_union_map_copy(prog->may_write)));
+
+ arrays = isl_union_set_apply(arrays,
+ isl_union_map_copy(prog->to_outer));
+
+ arrays = isl_union_set_coalesce(arrays);
+
+ prog->n_array = prog->scop->pet->n_array;
+ prog->array = isl_calloc_array(prog->ctx,
+ struct gpu_array_info, prog->n_array);
+ assert(prog->array);
+ prog->n_array = 0;
+ for (i = 0; i < prog->scop->pet->n_array; ++i) {
+ isl_bool field;
+
+ field = isl_set_is_wrapping(prog->scop->pet->arrays[i]->extent);
+ if (field < 0)
+ break;
+ if (field)
+ continue;
+ if (extract_array_info(prog, &prog->array[prog->n_array++],
+ prog->scop->pet->arrays[i], arrays) < 0)
+ r = -1;
+ }
+ if (i < prog->scop->pet->n_array)
+ r = -1;
+
+ isl_union_set_free(arrays);
+
+ if (prog->scop->options->live_range_reordering)
+ collect_order_dependences(prog);
+
+ return r;
+}
+
+static void free_array_info(struct gpu_prog *prog)
+{
+ int i;
+
+ for (i = 0; i < prog->n_array; ++i) {
+ free(prog->array[i].type);
+ free(prog->array[i].name);
+ isl_multi_pw_aff_free(prog->array[i].bound);
+ isl_ast_expr_free(prog->array[i].bound_expr);
+ isl_space_free(prog->array[i].space);
+ isl_set_free(prog->array[i].declared_extent);
+ isl_set_free(prog->array[i].extent);
+ isl_ast_expr_free(prog->array[i].declared_size);
+ free(prog->array[i].refs);
+ isl_union_map_free(prog->array[i].dep_order);
+ }
+ free(prog->array);
+}
+
+/* Check if a gpu array is a scalar. A scalar is a value that is not stored
+ * as an array or through a pointer reference, but as a single data element.
+ * At the moment, scalars are represented as zero-dimensional arrays.
+ * Note that the single data element may be an entire structure.
+ */
+int gpu_array_is_scalar(struct gpu_array_info *array)
+{
+ return array->n_index == 0;
+}
+
+/* Can "array" be mapped to private memory?
+ * That is, is it only accessed as individual elements with
+ * constant index expressions?
+ */
+isl_bool gpu_array_can_be_private(struct gpu_array_info *array)
+{
+ if (!array)
+ return isl_bool_error;
+ return array->only_fixed_element;
+}
+
+/* Is "array" a read-only scalar?
+ */
+int gpu_array_is_read_only_scalar(struct gpu_array_info *array)
+{
+ return array->read_only_scalar;
+}
+
+/* Does "array" need to be allocated on the device?
+ * If it is a read-only scalar, then it will be passed as an argument
+ * to the kernel and therefore does not require any allocation.
+ * If this device memory is not accessed at all, then it does not
+ * need to be allocated either.
+ */
+int gpu_array_requires_device_allocation(struct gpu_array_info *array)
+{
+ if (gpu_array_is_read_only_scalar(array))
+ return 0;
+ if (!array->global)
+ return 0;
+ return 1;
+}
+
+/* Return the set of parameter values for which the array has a positive
+ * size in all dimensions.
+ * If the sizes are only valid for some parameter values, then those
+ * constraints are also taken into account.
+ */
+__isl_give isl_set *gpu_array_positive_size_guard(struct gpu_array_info *array)
+{
+ int i;
+ isl_space *space;
+ isl_set *guard;
+
+ if (!array)
+ return NULL;
+
+ space = isl_space_params(isl_space_copy(array->space));
+ guard = isl_set_universe(space);
+
+ for (i = 0; i < array->n_index; ++i) {
+ isl_pw_aff *bound;
+ isl_set *guard_i, *zero;
+
+ bound = isl_multi_pw_aff_get_pw_aff(array->bound, i);
+ guard_i = isl_pw_aff_nonneg_set(isl_pw_aff_copy(bound));
+ zero = isl_pw_aff_zero_set(bound);
+ guard_i = isl_set_subtract(guard_i, zero);
+ guard = isl_set_intersect(guard, guard_i);
+ }
+
+ return guard;
+}
+
+/* Internal data structure for extract_size_of_type.
+ * "type" specifies the name of the space that we want to extract.
+ * "res" is used to store the subset of that space.
+ */
+struct ppcg_extract_size_data {
+ const char *type;
+ isl_set *res;
+};
+
+/* This function is called for each set in a union_set.
+ * If the name of the set matches data->type, we store the
+ * set in data->res.
+ */
+static isl_stat extract_size_of_type(__isl_take isl_set *size, void *user)
+{
+ struct ppcg_extract_size_data *data = user;
+ const char *name;
+
+ name = isl_set_get_tuple_name(size);
+ if (name && !strcmp(name, data->type)) {
+ data->res = size;
+ return isl_stat_error;
+ }
+
+ isl_set_free(size);
+ return isl_stat_ok;
+}
+
+/* Given a union map { kernel[i] -> *[...] },
+ * return the range in the space called "type" for the kernel with
+ * sequence number "id".
+ */
+static __isl_give isl_set *extract_sizes(__isl_keep isl_union_map *sizes,
+ const char *type, int id)
+{
+ isl_space *space;
+ isl_set *dom;
+ isl_union_set *local_sizes;
+ struct ppcg_extract_size_data data = { type, NULL };
+
+ if (!sizes)
+ return NULL;
+
+ space = isl_union_map_get_space(sizes);
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, 1);
+ space = isl_space_set_tuple_name(space, isl_dim_set, "kernel");
+ dom = isl_set_universe(space);
+ dom = isl_set_fix_si(dom, isl_dim_set, 0, id);
+
+ local_sizes = isl_union_set_apply(isl_union_set_from_set(dom),
+ isl_union_map_copy(sizes));
+ isl_union_set_foreach_set(local_sizes, &extract_size_of_type, &data);
+ isl_union_set_free(local_sizes);
+ return data.res;
+}
+
+/* Given a singleton set, extract the first (at most *len) elements
+ * of the single integer tuple into *sizes and update *len if needed.
+ */
+static void read_sizes_from_set(__isl_take isl_set *set, int *sizes, int *len)
+{
+ int i;
+ int dim;
+
+ if (!set)
+ return;
+
+ dim = isl_set_dim(set, isl_dim_set);
+ if (dim < *len)
+ *len = dim;
+
+ for (i = 0; i < *len; ++i) {
+ isl_val *v;
+
+ v = isl_set_plain_get_val_if_fixed(set, isl_dim_set, i);
+ assert(v);
+
+ sizes[i] = isl_val_get_num_si(v);
+ isl_val_free(v);
+ }
+
+ isl_set_free(set);
+}
+
+/* Add the map { kernel[id] -> type[sizes] } to gen->used_sizes,
+ * if the option debug->dump_sizes is set.
+ */
+static void set_used_sizes(struct gpu_gen *gen, const char *type, int id,
+ int *sizes, int len)
+{
+ int i;
+ isl_space *space;
+ isl_map *map;
+
+ if (!gen->options->debug->dump_sizes)
+ return;
+
+ space = isl_union_map_get_space(gen->used_sizes);
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, 1);
+ space = isl_space_set_tuple_name(space, isl_dim_set, "kernel");
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, len);
+ space = isl_space_set_tuple_name(space, isl_dim_out, type);
+
+ map = isl_map_universe(space);
+ map = isl_map_fix_si(map, isl_dim_in, 0, id);
+ for (i = 0; i < len; ++i)
+ map = isl_map_fix_si(map, isl_dim_out, i, sizes[i]);
+
+ gen->used_sizes = isl_union_map_add_map(gen->used_sizes, map);
+}
+
+/* Extract user specified "tile" sizes from the "sizes" command line option,
+ * defaulting to option->tile_size in each dimension.
+ * *tile_len contains the maximum number of tile sizes needed.
+ * Update *tile_len to the number of specified tile sizes, if any, and
+ * return a pointer to the tile sizes (or NULL on error).
+ * Add the effectively used sizes to gen->used_sizes.
+ */
+static int *read_tile_sizes(struct gpu_gen *gen, int *tile_len)
+{
+ int n;
+ int *tile_size;
+ isl_set *size;
+
+ tile_size = isl_alloc_array(gen->ctx, int, *tile_len);
+ if (!tile_size)
+ return NULL;
+ for (n = 0; n < *tile_len; ++n)
+ tile_size[n] = gen->options->tile_size;
+
+ size = extract_sizes(gen->sizes, "tile", gen->kernel_id);
+ read_sizes_from_set(size, tile_size, tile_len);
+ set_used_sizes(gen, "tile", gen->kernel_id, tile_size, *tile_len);
+
+ return tile_size;
+}
+
+/* Extract user specified "block" sizes from the "sizes" command line option,
+ * after filling in some potentially useful defaults.
+ */
+static void read_block_sizes(struct ppcg_kernel *kernel,
+ __isl_keep isl_union_map *sizes)
+{
+ isl_set *size;
+
+ if (kernel->n_block > 3)
+ kernel->n_block = 3;
+ switch (kernel->n_block) {
+ case 1:
+ kernel->block_dim[0] = 512;
+ break;
+ case 2:
+ kernel->block_dim[0] = 32;
+ kernel->block_dim[1] = 16;
+ break;
+ default:
+ kernel->block_dim[0] = 32;
+ kernel->block_dim[1] = 4;
+ kernel->block_dim[2] = 4;
+ break;
+ }
+
+ size = extract_sizes(sizes, "block", kernel->id);
+ read_sizes_from_set(size, kernel->block_dim, &kernel->n_block);
+}
+
+/* Extract user specified "grid" sizes from the "sizes" command line option,
+ * after filling in some potentially useful defaults.
+ */
+static void read_grid_sizes(struct ppcg_kernel *kernel,
+ __isl_keep isl_union_map *sizes)
+{
+ isl_set *size;
+
+ if (kernel->n_grid > 2)
+ kernel->n_grid = 2;
+ switch (kernel->n_grid) {
+ case 1:
+ kernel->grid_dim[0] = 32768;
+ break;
+ default:
+ kernel->grid_dim[0] = 256;
+ kernel->grid_dim[1] = 256;
+ break;
+ }
+
+ size = extract_sizes(sizes, "grid", kernel->id);
+ read_sizes_from_set(size, kernel->grid_dim, &kernel->n_grid);
+}
+
+/* Extract user specified grid and block sizes from the gen->sizes
+ * command line option after filling in some potentially useful defaults.
+ * Store the extracted sizes in "kernel".
+ * Add the effectively used sizes to gen->used_sizes.
+ */
+static void read_grid_and_block_sizes(struct ppcg_kernel *kernel,
+ struct gpu_gen *gen)
+{
+ read_block_sizes(kernel, gen->sizes);
+ read_grid_sizes(kernel, gen->sizes);
+ set_used_sizes(gen, "block", kernel->id,
+ kernel->block_dim, kernel->n_block);
+ set_used_sizes(gen, "grid", kernel->id,
+ kernel->grid_dim, kernel->n_grid);
+}
+
+static void *free_stmts(struct gpu_stmt *stmts, int n)
+{
+ int i;
+
+ if (!stmts)
+ return NULL;
+
+ for (i = 0; i < n; ++i) {
+ struct gpu_stmt_access *access, *next;
+
+ for (access = stmts[i].accesses; access; access = next) {
+ next = access->next;
+ isl_id_free(access->ref_id);
+ isl_map_free(access->access);
+ isl_map_free(access->tagged_access);
+ free(access);
+ }
+
+ isl_id_free(stmts[i].id);
+ }
+ free(stmts);
+
+ return NULL;
+}
+
+/* Add parameters p[i] with identifiers "ids" to "set",
+ * with bounds to 0 <= p[i] < size[i].
+ */
+__isl_give isl_set *add_bounded_parameters(__isl_take isl_set *set,
+ int *size, __isl_keep isl_id_list *ids)
+{
+ int i, len;
+ unsigned nparam;
+
+ len = isl_id_list_n_id(ids);
+ nparam = isl_set_dim(set, isl_dim_param);
+ set = isl_set_add_dims(set, isl_dim_param, len);
+
+ for (i = 0; i < len; ++i) {
+ isl_id *id;
+
+ id = isl_id_list_get_id(ids, i);
+ set = isl_set_set_dim_id(set, isl_dim_param, nparam + i, id);
+ set = isl_set_lower_bound_si(set, isl_dim_param, nparam + i, 0);
+ set = isl_set_upper_bound_si(set, isl_dim_param,
+ nparam + i, size[i] - 1);
+ }
+
+ return set;
+}
+
+/* Add "len" parameters p[i] with identifiers "ids" and intersect "set"
+ * with
+ *
+ * { : 0 <= p[i] < size[i] }
+ *
+ * or an overapproximation.
+ */
+static __isl_give isl_set *add_bounded_parameters_dynamic(
+ __isl_take isl_set *set, __isl_keep isl_multi_pw_aff *size,
+ __isl_keep isl_id_list *ids)
+{
+ int i, len;
+ unsigned nparam;
+ isl_space *space;
+ isl_local_space *ls;
+
+ len = isl_multi_pw_aff_dim(size, isl_dim_out);
+ nparam = isl_set_dim(set, isl_dim_param);
+ set = isl_set_add_dims(set, isl_dim_param, len);
+
+ for (i = 0; i < len; ++i) {
+ isl_id *id;
+
+ id = isl_id_list_get_id(ids, i);
+ set = isl_set_set_dim_id(set, isl_dim_param, nparam + i, id);
+ }
+
+ space = isl_space_params(isl_set_get_space(set));
+ ls = isl_local_space_from_space(space);
+ for (i = 0; i < len; ++i) {
+ isl_pw_aff *param, *size_i, *zero;
+ isl_set *bound;
+
+ param = isl_pw_aff_var_on_domain(isl_local_space_copy(ls),
+ isl_dim_param, nparam + i);
+
+ size_i = isl_multi_pw_aff_get_pw_aff(size, i);
+ bound = isl_pw_aff_lt_set(isl_pw_aff_copy(param), size_i);
+ bound = isl_set_from_basic_set(isl_set_simple_hull(bound));
+ set = isl_set_intersect_params(set, bound);
+
+ zero = isl_pw_aff_zero_on_domain(isl_local_space_copy(ls));
+ bound = isl_pw_aff_ge_set(param, zero);
+ set = isl_set_intersect_params(set, bound);
+ }
+ isl_local_space_free(ls);
+
+ return set;
+}
+
+/* Return the union of all tagged access relations in the group.
+ */
+static __isl_give isl_union_map *group_tagged_access_relation(
+ struct gpu_array_ref_group *group)
+{
+ int i;
+ isl_union_map *access;
+
+ access = isl_union_map_empty(isl_map_get_space(group->access));
+ for (i = 0; i < group->n_ref; ++i) {
+ isl_map *map_i;
+
+ map_i = isl_map_copy(group->refs[i]->tagged_access);
+ access = isl_union_map_union(access,
+ isl_union_map_from_map(map_i));
+ }
+
+ return access;
+}
+
+/* Return the extent of "array", recomputed from the bounds.
+ * The recomputed extent may be simpler than the original extent.
+ */
+static __isl_give isl_set *array_extent(struct gpu_array_info *array)
+{
+ int i;
+ isl_id *id;
+ isl_space *space;
+ isl_local_space *ls;
+ isl_set *extent;
+
+ id = isl_set_get_tuple_id(array->extent);
+ space = isl_set_get_space(array->extent);
+ extent = isl_set_universe(isl_space_copy(space));
+ ls = isl_local_space_from_space(space);
+ for (i = 0; i < array->n_index; ++i) {
+ isl_pw_aff *bound;
+ isl_aff *aff;
+ isl_pw_aff *index;
+ isl_set *lt;
+
+ extent = isl_set_lower_bound_si(extent, isl_dim_set, i, 0);
+
+ aff = isl_aff_var_on_domain(isl_local_space_copy(ls),
+ isl_dim_set, i);
+ index = isl_pw_aff_from_aff(aff);
+ bound = isl_multi_pw_aff_get_pw_aff(array->bound, i);
+ bound = isl_pw_aff_from_range(bound);
+ bound = isl_pw_aff_add_dims(bound, isl_dim_in, array->n_index);
+ bound = isl_pw_aff_set_tuple_id(bound, isl_dim_in,
+ isl_id_copy(id));
+ lt = isl_pw_aff_lt_set(index, bound);
+ extent = isl_set_intersect(extent, lt);
+ }
+ isl_local_space_free(ls);
+ isl_id_free(id);
+
+ return extent;
+}
+
+/* Return a map from the first group->shared_tile->depth dimensions
+ * of the computed schedule to the array tile in
+ * global memory that corresponds to the shared memory copy.
+ *
+ * In particular, return a map
+ *
+ * { D[i] -> A[a] }
+ *
+ * with constraints
+ *
+ * tile_offset(i) <= a <= tile_offset(i) + tile_size - 1 (1)
+ *
+ * and
+ *
+ * 0 <= a <= array_size - 1 (2)
+ *
+ * Note that if some stride has been detected (i.e., when
+ * group->shared_tile->bound[i].shift is set), then a in (1) refers
+ * to the shifted and scaled down version.
+ *
+ * Constraints (1) are obtained by mapping the size constraints on the
+ * shared/private memory tile back to the access relation.
+ * Constraints (2) are obtained from the (recomputed) extent.
+ */
+static __isl_give isl_map *group_tile(struct gpu_array_ref_group *group)
+{
+ int i;
+ int n_index = group->array->n_index;
+ isl_map *tile;
+ isl_space *space;
+ isl_set *local;
+ isl_set *extent;
+
+ space = isl_multi_aff_get_space(group->shared_tile->tiling);
+ space = isl_space_range(space);
+ local = isl_set_universe(space);
+ for (i = 0; i < n_index; ++i) {
+ isl_val *bound;
+
+ local = isl_set_lower_bound_si(local, isl_dim_set, i, 0);
+ bound = isl_val_copy(group->shared_tile->bound[i].size);
+ bound = isl_val_sub_ui(bound, 1);
+ local = isl_set_upper_bound_val(local, isl_dim_set, i, bound);
+ }
+ local = isl_set_preimage_multi_aff(local,
+ isl_multi_aff_copy(group->shared_tile->tiling));
+ tile = isl_set_unwrap(local);
+ extent = array_extent(group->array);
+ tile = isl_map_intersect_range(tile, extent);
+
+ return tile;
+}
+
+/* Given a mapping "iterator_map" from the AST schedule to a domain,
+ * return the corresponding mapping from the AST schedule to
+ * to the outer kernel->copy_schedule_dim dimensions of
+ * the schedule computed by PPCG for this kernel.
+ *
+ * Note that kernel->copy_schedule_dim is at least as large as
+ * the largest depth of any array reference group associated to the kernel.
+ * This is needed as the returned schedule is used to extract a mapping
+ * to the outer tile->depth dimensions in transform_index.
+ */
+static __isl_give isl_pw_multi_aff *compute_sched_to_copy(
+ struct ppcg_kernel *kernel, __isl_take isl_pw_multi_aff *iterator_map)
+{
+ isl_union_pw_multi_aff *upma;
+ isl_pw_multi_aff *pma;
+ isl_space *space;
+
+ space = isl_space_range(isl_pw_multi_aff_get_space(iterator_map));
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out,
+ kernel->copy_schedule_dim);
+
+ upma = isl_union_pw_multi_aff_copy(kernel->copy_schedule);
+ pma = isl_union_pw_multi_aff_extract_pw_multi_aff(upma, space);
+ isl_union_pw_multi_aff_free(upma);
+
+ return isl_pw_multi_aff_pullback_pw_multi_aff(pma, iterator_map);
+}
+
+/* If max_shared_memory is not set to infinity (-1), then make
+ * sure that the total amount of shared memory required by the
+ * array reference groups mapped to shared memory by "kernel"
+ * is no larger than this maximum.
+ *
+ * We apply a greedy approach and discard (keep in global memory)
+ * those groups that would result in a total memory size that
+ * is larger than the maximum.
+ *
+ * This function should be called after any function that may
+ * affect the decision on whether to place a reference group
+ * in private, shared or global memory.
+ */
+static void check_shared_memory_bound(struct ppcg_kernel *kernel)
+{
+ int i, j;
+ isl_val *left, *size;
+
+ if (kernel->options->max_shared_memory < 0)
+ return;
+
+ left = isl_val_int_from_si(kernel->ctx,
+ kernel->options->max_shared_memory);
+
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *local = &kernel->array[i];
+
+ for (j = 0; j < local->n_group; ++j) {
+ struct gpu_array_ref_group *group;
+ enum ppcg_group_access_type type;
+
+ group = local->groups[j];
+ type = gpu_array_ref_group_type(group);
+ if (type != ppcg_access_shared)
+ continue;
+
+ size = gpu_array_tile_size(group->shared_tile);
+ size = isl_val_mul_ui(size, local->array->size);
+
+ if (isl_val_le(size, left)) {
+ left = isl_val_sub(left, size);
+ continue;
+ }
+ isl_val_free(size);
+
+ group->shared_tile =
+ gpu_array_tile_free(group->shared_tile);
+ }
+ }
+
+ isl_val_free(left);
+}
+
+/* Mark all arrays of "kernel" that have an array reference group
+ * that is not mapped to private or shared memory as
+ * accessing the corresponding global device memory.
+ */
+static void mark_global_arrays(struct ppcg_kernel *kernel)
+{
+ int i, j;
+
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *local = &kernel->array[i];
+
+ if (local->global)
+ continue;
+ for (j = 0; j < local->n_group; ++j) {
+ if (gpu_array_ref_group_tile(local->groups[j]))
+ continue;
+
+ local->global = 1;
+ local->array->global = 1;
+ break;
+ }
+ }
+}
+
+/* Compute a tiling for all the array reference groups in "kernel".
+ */
+static void compute_group_tilings(struct ppcg_kernel *kernel)
+{
+ int i, j;
+
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *array = &kernel->array[i];
+
+ for (j = 0; j < array->n_group; ++j)
+ gpu_array_ref_group_compute_tiling(array->groups[j]);
+ }
+}
+
+/* Compute the effective grid size as a list of the sizes in each dimension.
+ *
+ * The grid size specified by the user or set by default
+ * in read_grid_sizes() and applied by the block filter,
+ * may be too large for the given code in the sense that
+ * it may contain blocks that don't need to execute anything.
+ * We therefore don't return this grid size, but instead the
+ * smallest grid size that ensures that all blocks that actually
+ * execute code are included in the grid.
+ *
+ * We first extract a description of the grid, i.e., the possible values
+ * of the block ids, from the domain elements in "domain" and
+ * kernel->block_filter.
+ * The block ids are parameters in kernel->block_filter.
+ * We simply need to change them into set dimensions.
+ *
+ * Then, for each block dimension, we compute the maximal value of the block id
+ * and add one.
+ */
+static __isl_give isl_multi_pw_aff *extract_grid_size(
+ struct ppcg_kernel *kernel, __isl_take isl_union_set *domain)
+{
+ int i;
+ isl_set *grid;
+ isl_set *context;
+ isl_multi_pw_aff *size;
+
+ domain = isl_union_set_intersect(domain,
+ isl_union_set_copy(kernel->block_filter));
+ grid = isl_union_set_params(domain);
+ grid = isl_set_from_params(grid);
+ grid = isl_set_add_dims(grid, isl_dim_set, kernel->n_grid);
+ for (i = 0; i < kernel->n_grid; ++i) {
+ int pos;
+ isl_id *id;
+
+ id = isl_id_list_get_id(kernel->block_ids, i);
+ pos = isl_set_find_dim_by_id(grid, isl_dim_param, id);
+ isl_id_free(id);
+ assert(pos >= 0);
+ grid = isl_set_equate(grid, isl_dim_param, pos, isl_dim_set, i);
+ grid = isl_set_project_out(grid, isl_dim_param, pos, 1);
+ }
+
+ grid = isl_set_coalesce(grid);
+ size = ppcg_size_from_extent(grid);
+ context = isl_set_params(isl_set_copy(kernel->context));
+ return isl_multi_pw_aff_gist(size, context);
+}
+
+/* Compute the size of a fixed bounding box around the origin and "set",
+ * where "set" is assumed to contain only non-negative elements,
+ * and store the results in "size".
+ * In particular, compute the maximal value of "set" in each direction
+ * and add one.
+ */
+static void extract_fixed_size(__isl_take isl_set *set, int *size)
+{
+ int i, n;
+ isl_local_space *ls;
+ isl_aff *obj;
+
+ n = isl_set_dim(set, isl_dim_set);
+ ls = isl_local_space_from_space(isl_set_get_space(set));
+ obj = isl_aff_zero_on_domain(ls);
+ for (i = 0; i < n; ++i) {
+ isl_val *max;
+
+ obj = isl_aff_set_coefficient_si(obj, isl_dim_in, i, 1);
+ max = isl_set_max_val(set, obj);
+ size[i] = isl_val_get_num_si(max) + 1;
+ isl_val_free(max);
+ obj = isl_aff_set_coefficient_si(obj, isl_dim_in, i, 0);
+ }
+ isl_aff_free(obj);
+ isl_set_free(set);
+}
+
+/* Compute the effective block size as a list of the sizes in each dimension
+ * and store the sizes in kernel->block_dim.
+ *
+ * The block size specified by the user or set by default
+ * in read_block_sizes() and applied by the thread filter,
+ * may be too large for the given code in the sense that
+ * it may contain threads that don't need to execute anything.
+ * We therefore update this block size in kernel->block_dim
+ * to the smallest block size that ensures that all threads
+ * that actually execute code are included in the block.
+ *
+ * The set of possible values of the thread ids is obtained from
+ * the domain elements "domain" and kernel->thread_filter.
+ * The current implementation eliminates all parameters, ensuring
+ * that the size is a fixed constant in each dimension.
+ * In principle we could also compute parametric sizes.
+ * We would have to make sure to project out all b%d and t%d parameters,
+ * however.
+ */
+static isl_stat extract_block_size(struct ppcg_kernel *kernel,
+ __isl_take isl_union_set *domain)
+{
+ int i;
+ int nparam;
+ isl_set *block;
+
+ domain = isl_union_set_intersect(domain,
+ isl_union_set_copy(kernel->thread_filter));
+ block = isl_union_set_params(domain);
+ block = isl_set_from_params(block);
+ block = isl_set_add_dims(block, isl_dim_set, kernel->n_block);
+ for (i = 0; i < kernel->n_block; ++i) {
+ int pos;
+ isl_id *id;
+
+ if (!block)
+ return isl_stat_error;
+
+ id = isl_id_list_get_id(kernel->thread_ids, i);
+ pos = isl_set_find_dim_by_id(block, isl_dim_param, id);
+ isl_id_free(id);
+ if (pos < 0)
+ isl_die(isl_set_get_ctx(block), isl_error_internal,
+ "missing constraints on thread identifier",
+ block = isl_set_free(block));
+ block = isl_set_equate(block, isl_dim_param, pos,
+ isl_dim_set, i);
+ }
+ nparam = isl_set_dim(block, isl_dim_param);
+ block = isl_set_project_out(block, isl_dim_param, 0, nparam);
+
+ if (!block)
+ return isl_stat_error;
+
+ extract_fixed_size(block, kernel->block_dim);
+
+ return isl_stat_ok;
+}
+
+struct ppcg_kernel *ppcg_kernel_free(struct ppcg_kernel *kernel)
+{
+ int i, j;
+
+ if (!kernel)
+ return NULL;
+
+ isl_id_list_free(kernel->block_ids);
+ isl_id_list_free(kernel->thread_ids);
+ isl_multi_pw_aff_free(kernel->grid_size);
+ isl_ast_expr_free(kernel->grid_size_expr);
+ isl_set_free(kernel->context);
+ isl_union_set_free(kernel->core);
+ isl_union_set_free(kernel->arrays);
+ isl_union_pw_multi_aff_free(kernel->contraction);
+ isl_union_set_free(kernel->expanded_domain);
+ isl_space_free(kernel->space);
+ isl_ast_node_free(kernel->tree);
+ isl_union_set_free(kernel->block_filter);
+ isl_union_set_free(kernel->thread_filter);
+ isl_union_pw_multi_aff_free(kernel->copy_schedule);
+ isl_union_set_free(kernel->sync_writes);
+
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *array = &kernel->array[i];
+
+ for (j = 0; j < array->n_group; ++j)
+ gpu_array_ref_group_free(array->groups[j]);
+ free(array->groups);
+
+ isl_multi_pw_aff_free(array->bound);
+ isl_ast_expr_free(array->bound_expr);
+ }
+ free(kernel->array);
+
+ for (i = 0; i < kernel->n_var; ++i) {
+ free(kernel->var[i].name);
+ isl_vec_free(kernel->var[i].size);
+ }
+ free(kernel->var);
+
+ free(kernel);
+
+ return NULL;
+}
+
+/* Wrapper around ppcg_kernel_free for use as a isl_id_set_free_user callback.
+ */
+static void ppcg_kernel_free_wrap(void *user)
+{
+ struct ppcg_kernel *kernel = user;
+
+ ppcg_kernel_free(kernel);
+}
+
+static void create_kernel_var(isl_ctx *ctx, struct gpu_array_ref_group *group,
+ struct ppcg_kernel_var *var)
+{
+ int j;
+ struct gpu_array_tile *tile;
+ isl_printer *p;
+
+ var->array = group->array;
+
+ var->type = gpu_array_ref_group_type(group);
+ tile = gpu_array_ref_group_tile(group);
+
+ p = isl_printer_to_str(ctx);
+ p = gpu_array_ref_group_print_name(group, p);
+ var->name = isl_printer_get_str(p);
+ isl_printer_free(p);
+
+ var->size = isl_vec_alloc(ctx, group->array->n_index);
+
+ for (j = 0; j < group->array->n_index; ++j)
+ var->size = isl_vec_set_element_val(var->size, j,
+ isl_val_copy(tile->bound[j].size));
+}
+
+static int create_kernel_vars(struct ppcg_kernel *kernel)
+{
+ int i, j, n;
+
+ n = 0;
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *array = &kernel->array[i];
+
+ for (j = 0; j < array->n_group; ++j) {
+ struct gpu_array_ref_group *group = array->groups[j];
+ enum ppcg_group_access_type type;
+
+ type = gpu_array_ref_group_type(group);
+ if (type != ppcg_access_global)
+ ++n;
+ }
+ }
+
+ kernel->n_var = n;
+ kernel->var = isl_calloc_array(kernel->ctx, struct ppcg_kernel_var, n);
+ if (!kernel->var)
+ return -1;
+
+ n = 0;
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *array = &kernel->array[i];
+
+ for (j = 0; j < array->n_group; ++j) {
+ struct gpu_array_ref_group *group = array->groups[j];
+ enum ppcg_group_access_type type;
+
+ type = gpu_array_ref_group_type(group);
+ if (type == ppcg_access_global)
+ continue;
+ create_kernel_var(kernel->ctx, group, &kernel->var[n]);
+ ++n;
+ }
+ }
+
+ return 0;
+}
+
+/* Replace "pa" by the zero function defined over the universe domain
+ * in the space of "pa".
+ */
+static __isl_give isl_pw_aff *set_universally_zero(__isl_take isl_pw_aff *pa)
+{
+ isl_space *space;
+ isl_aff *zero;
+
+ space = isl_space_domain(isl_pw_aff_get_space(pa));
+ isl_pw_aff_free(pa);
+ zero = isl_aff_zero_on_domain(isl_local_space_from_space(space));
+
+ return isl_pw_aff_from_aff(zero);
+}
+
+/* The sizes of the arrays on the host that have been computed by
+ * extract_array_info may depend on the parameters. Use the extra
+ * constraints on the parameters that are valid at "host_domain"
+ * to simplify these expressions and store the results in kernel->array.
+ *
+ * We only need these localized bounds for arrays that are accessed
+ * by the current kernel. If we have found at least one reference group
+ * then the array is accessed by the kernel.
+ *
+ * The resulting sizes may be functions that are nowhere defined
+ * in case the access function cannot possibly access anything inside
+ * the kernel for some reason. If so, they are replaced by the zero
+ * function. Since the access function cannot actually access anything,
+ * there is no harm in printing the array sizes as zero.
+ */
+static void localize_bounds(struct ppcg_kernel *kernel,
+ __isl_keep isl_set *host_domain)
+{
+ int i, j;
+ isl_set *context;
+
+ context = isl_set_copy(host_domain);
+ context = isl_set_params(context);
+
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *local = &kernel->array[i];
+ isl_multi_pw_aff *bound;
+ int n_index;
+
+ if (local->n_group == 0)
+ continue;
+
+ n_index = local->array->n_index;
+ bound = isl_multi_pw_aff_copy(local->array->bound);
+
+ for (j = 0; j < n_index; ++j) {
+ isl_pw_aff *pwaff;
+ int empty;
+
+ pwaff = isl_multi_pw_aff_get_pw_aff(bound, j);
+ pwaff = isl_pw_aff_gist(pwaff, isl_set_copy(context));
+ empty = isl_pw_aff_is_empty(pwaff);
+ if (empty < 0)
+ pwaff = isl_pw_aff_free(pwaff);
+ else if (empty)
+ pwaff = set_universally_zero(pwaff);
+ bound = isl_multi_pw_aff_set_pw_aff(bound, j, pwaff);
+ }
+
+ local->n_index = n_index;
+ local->bound = bound;
+ }
+ isl_set_free(context);
+}
+
+/* Create the array of gpu_local_array_info structures "array"
+ * inside "kernel". The number of elements in this array is
+ * the same as the number of arrays in "prog".
+ * Initialize the "array" field of each local array to point
+ * to the corresponding array in "prog".
+ */
+static struct ppcg_kernel *ppcg_kernel_create_local_arrays(
+ struct ppcg_kernel *kernel, struct gpu_prog *prog)
+{
+ int i;
+ isl_ctx *ctx;
+
+ ctx = isl_set_get_ctx(prog->context);
+ kernel->array = isl_calloc_array(ctx,
+ struct gpu_local_array_info, prog->n_array);
+ if (!kernel->array)
+ return ppcg_kernel_free(kernel);
+ kernel->n_array = prog->n_array;
+
+ for (i = 0; i < prog->n_array; ++i)
+ kernel->array[i].array = &prog->array[i];
+
+ return kernel;
+}
+
+/* Does "kernel" need to be passed an argument corresponding to array "i"?
+ *
+ * The argument is only needed if the kernel accesses this device memory.
+ */
+int ppcg_kernel_requires_array_argument(struct ppcg_kernel *kernel, int i)
+{
+ return kernel->array[i].global;
+}
+
+/* Find the element in gen->stmt that has the given "id".
+ * Return NULL if no such gpu_stmt can be found.
+ */
+static struct gpu_stmt *find_stmt(struct gpu_prog *prog, __isl_keep isl_id *id)
+{
+ int i;
+
+ for (i = 0; i < prog->n_stmts; ++i) {
+ if (id == prog->stmts[i].id)
+ break;
+ }
+
+ return i < prog->n_stmts ? &prog->stmts[i] : NULL;
+}
+
+void ppcg_kernel_stmt_free(void *user)
+{
+ struct ppcg_kernel_stmt *stmt = user;
+
+ if (!stmt)
+ return;
+
+ switch (stmt->type) {
+ case ppcg_kernel_copy:
+ isl_ast_expr_free(stmt->u.c.index);
+ isl_ast_expr_free(stmt->u.c.local_index);
+ break;
+ case ppcg_kernel_domain:
+ isl_id_to_ast_expr_free(stmt->u.d.ref2expr);
+ break;
+ case ppcg_kernel_sync:
+ break;
+ }
+
+ free(stmt);
+}
+
+/* Return the gpu_stmt_access in the list "accesses" that corresponds
+ * to "ref_id".
+ */
+static struct gpu_stmt_access *find_access(struct gpu_stmt_access *accesses,
+ __isl_keep isl_id *ref_id)
+{
+ struct gpu_stmt_access *access;
+
+ for (access = accesses; access; access = access->next)
+ if (access->ref_id == ref_id)
+ return access;
+
+ return NULL;
+}
+
+/* Return the index of the array called "name" in the list of arrays.
+ */
+static int find_array_index(struct ppcg_kernel *kernel, const char *name)
+{
+ int i;
+
+ for (i = 0; i < kernel->n_array; ++i)
+ if (!strcmp(name, kernel->array[i].array->name))
+ return i;
+
+ return -1;
+}
+
+/* Internal data structure for the index and AST expression transformation
+ * callbacks for pet_stmt_build_ast_exprs.
+ *
+ * "kernel" is the kernel for which are computing AST expressions and
+ * may be NULL if we are not inside a kernel.
+ * "accesses" is the list of gpu_stmt_access in the statement.
+ * "iterator_map" expresses the statement iterators in terms of
+ * the AST loop iterators.
+ * "sched2copy" expresses the outer copy_schedule_dim dimensions of
+ * the kernel schedule in terms of the AST loop iterators and
+ * may be NULL if we are not inside a kernel.
+ *
+ * The following fields are set in transform_index and used in transform_expr.
+ * "array" is the array that is being accessed.
+ * "global" is set if the global array is accessed (rather than
+ * shared/private memory).
+ * "local_array" refers to information on the array specialized
+ * to the current kernel.
+ */
+struct ppcg_transform_data {
+ struct ppcg_options *options;
+ struct ppcg_kernel *kernel;
+ struct gpu_stmt_access *accesses;
+ isl_pw_multi_aff *iterator_map;
+ isl_pw_multi_aff *sched2copy;
+
+ struct gpu_array_info *array;
+ int global;
+ struct gpu_local_array_info *local_array;
+};
+
+/* Return a pointer to the gpu_array_ref_group in "local"
+ * that contains the reference "access".
+ * Return NULL if no such group can be found.
+ */
+static struct gpu_array_ref_group *find_ref_group(
+ struct gpu_local_array_info *local, struct gpu_stmt_access *access)
+{
+ int i, j;
+
+ for (i = 0; i < local->n_group; ++i) {
+ struct gpu_array_ref_group *group = local->groups[i];
+
+ for (j = 0; j < group->n_ref; ++j)
+ if (group->refs[j] == access)
+ return group;
+ }
+
+ return NULL;
+}
+
+/* Given an index expression "index" of the form
+ *
+ * L -> F(A),
+ *
+ * with F(A) either A or some subfield of A and L the AST loop iterators,
+ * and a tiling "tiling" of the form
+ *
+ * [L -> A] -> T
+ *
+ * apply the tiling to the outer array in the index expression to obtain
+ *
+ * L -> T(A)
+ *
+ * If F(A) is some subfield of A, then separate the member access
+ * into the base index expression and the field index expression,
+ * apply the tiling to the base index expression and combine the result
+ * with the field index expression.
+ *
+ * If F(A) is A, then modify index to keep track of the iterators
+ *
+ * L -> [L -> A]
+ *
+ * and combine the result with the tiling to obtain a tiled index expression
+ * in terms of the AST loop iterators
+ *
+ * L -> T
+ */
+static __isl_give isl_multi_pw_aff *tile_outer(
+ __isl_take isl_multi_pw_aff *index, __isl_take isl_multi_pw_aff *tiling)
+{
+ isl_bool is_wrapping;
+ isl_space *space;
+ isl_multi_pw_aff *mpa;
+
+ is_wrapping = isl_multi_pw_aff_range_is_wrapping(index);
+ if (is_wrapping < 0)
+ goto error;
+ if (is_wrapping) {
+ isl_multi_pw_aff *field;
+
+ field = isl_multi_pw_aff_copy(index);
+ field = isl_multi_pw_aff_range_factor_range(field);
+ index = isl_multi_pw_aff_range_factor_domain(index);
+ index = tile_outer(index, tiling);
+ return isl_multi_pw_aff_range_product(index, field);
+ }
+
+ space = isl_space_domain(isl_multi_pw_aff_get_space(index));
+ space = isl_space_map_from_set(space);
+ mpa = isl_multi_pw_aff_identity(space);
+ index = isl_multi_pw_aff_range_product(mpa, index);
+ index = isl_multi_pw_aff_pullback_multi_pw_aff(tiling, index);
+
+ return index;
+error:
+ isl_multi_pw_aff_free(index);
+ isl_multi_pw_aff_free(tiling);
+ return NULL;
+}
+
+/* Index transformation callback for pet_stmt_build_ast_exprs.
+ *
+ * "index" expresses the array indices in terms of statement iterators
+ *
+ * We first reformulate "index" in terms of the AST loop iterators.
+ * Then we check if we are accessing the global array or
+ * a shared/private copy. In particular, if we are not inside a kernel
+ * then we must be accessing a global array.
+ * In the former case, we simply return
+ * the updated index. If "index" is an affine expression rather
+ * than an array access, then we also return the updated index here.
+ *
+ * If no reference groups have been computed for the array,
+ * then we can only be accessing the global array.
+ *
+ * Otherwise, we apply the tiling to the index.
+ * This tiling is of the form
+ *
+ * [D -> A] -> T
+ *
+ * where D corresponds to the outer tile->depth dimensions of
+ * the kernel schedule.
+ * The index is of the form
+ *
+ * L -> A
+ *
+ * We update the tiling to refer to the AST loop iterators
+ *
+ * [L -> A] -> T
+ *
+ * and combine it with the index to obtain a tiled index expression in terms
+ * of the AST loop iterators
+ *
+ * L -> T
+ *
+ * Note that while the tiling applies directly to an outer array.
+ * the index may refer to some subfield of this outer array.
+ * In such cases, the result will refer to the same subfield of the tile.
+ * That is, an index expression of the form L -> F(A) will be transformed
+ * into an index expression of the form L -> F(T).
+ */
+static __isl_give isl_multi_pw_aff *transform_index(
+ __isl_take isl_multi_pw_aff *index, __isl_keep isl_id *ref_id,
+ void *user)
+{
+ struct ppcg_transform_data *data = user;
+ struct gpu_stmt_access *access;
+ struct gpu_array_ref_group *group;
+ struct gpu_array_tile *tile;
+ isl_pw_multi_aff *iterator_map;
+ int i;
+ int dim;
+ const char *name;
+ isl_space *space;
+ isl_multi_pw_aff *tiling;
+ isl_pw_multi_aff *pma;
+ isl_pw_multi_aff *sched2depth;
+
+ data->array = NULL;
+
+ iterator_map = isl_pw_multi_aff_copy(data->iterator_map);
+ index = isl_multi_pw_aff_pullback_pw_multi_aff(index, iterator_map);
+
+ if (!data->kernel)
+ return index;
+
+ access = find_access(data->accesses, ref_id);
+ if (!access)
+ return index;
+ if (!isl_map_has_tuple_name(access->access, isl_dim_out))
+ return index;
+
+ name = get_outer_array_name(access->access);
+ i = find_array_index(data->kernel, name);
+ if (i < 0)
+ isl_die(isl_multi_pw_aff_get_ctx(index), isl_error_internal,
+ "cannot find array",
+ return isl_multi_pw_aff_free(index));
+ data->local_array = &data->kernel->array[i];
+ data->array = data->local_array->array;
+
+ group = find_ref_group(data->local_array, access);
+ if (!group) {
+ data->global = 1;
+ return index;
+ }
+
+ tile = gpu_array_ref_group_tile(group);
+ data->global = !tile;
+ if (!tile)
+ return index;
+
+ space = isl_space_domain(isl_multi_aff_get_space(tile->tiling));
+ space = isl_space_range(isl_space_unwrap(space));
+ space = isl_space_map_from_set(space);
+ pma = isl_pw_multi_aff_identity(space);
+ sched2depth = isl_pw_multi_aff_copy(data->sched2copy);
+ dim = isl_pw_multi_aff_dim(sched2depth, isl_dim_out);
+ sched2depth = isl_pw_multi_aff_drop_dims(sched2depth, isl_dim_out,
+ tile->depth, dim - tile->depth);
+ pma = isl_pw_multi_aff_product(sched2depth, pma);
+ tiling = isl_multi_pw_aff_from_multi_aff(
+ isl_multi_aff_copy(tile->tiling));
+ tiling = isl_multi_pw_aff_pullback_pw_multi_aff(tiling, pma);
+
+ index = tile_outer(index, tiling);
+
+ return index;
+}
+
+/* Dereference "expr" by adding an index [0].
+ * The original "expr" is assumed not to have any indices.
+ *
+ * If "expr" is a member access, then the dereferencing needs
+ * to be applied to the structure argument of this member access.
+ */
+static __isl_give isl_ast_expr *dereference(__isl_take isl_ast_expr *expr)
+{
+ isl_ctx *ctx;
+ isl_ast_expr *arg0, *res;
+ isl_ast_expr_list *list;
+
+ arg0 = isl_ast_expr_get_op_arg(expr, 0);
+ if (!arg0)
+ return isl_ast_expr_free(expr);
+ if (isl_ast_expr_get_type(arg0) == isl_ast_expr_op &&
+ isl_ast_expr_get_op_type(arg0) == isl_ast_op_member) {
+ isl_ast_expr *arg;
+
+ arg = isl_ast_expr_get_op_arg(arg0, 0);
+ arg = dereference(arg);
+ arg0 = isl_ast_expr_set_op_arg(arg0, 0, arg);
+ expr = isl_ast_expr_set_op_arg(expr, 0, arg0);
+
+ return expr;
+ }
+ isl_ast_expr_free(arg0);
+
+ ctx = isl_ast_expr_get_ctx(expr);
+ res = isl_ast_expr_from_val(isl_val_zero(ctx));
+ list = isl_ast_expr_list_from_ast_expr(res);
+ res = isl_ast_expr_get_op_arg(expr, 0);
+ res = isl_ast_expr_access(res, list);
+ isl_ast_expr_free(expr);
+
+ return res;
+}
+
+/* Linearize the index expression "expr" based on the array bounds
+ * of "array".
+ *
+ * That is, transform expression
+ *
+ * A[i_0][i_1]...[i_n]
+ *
+ * to
+ *
+ * A[(..((i_0 * b_1 + i_1) ... ) * b_n + i_n]
+ *
+ * where b_0, b_1, ..., b_n are the bounds on the array.
+ *
+ * If the base of "expr" is a member access, then the linearization needs
+ * to be applied to the structure argument of this member access.
+ *
+ * In the base case, if "expr" has no arguments (other than the name of
+ * the array), then we are passing an entire array to a function.
+ * In this case, there is nothing to linearize.
+ * Note that at this point an expression with no arguments can
+ * only be an entire array because the scalar case and
+ * the case of single struct are handled by the caller.
+ *
+ * If the number of specified index expressions in "expr"
+ * is smaller than the dimension of the accessed array,
+ * then the missing i_j also do not appear in the linearized expression.
+ * Furthermore, since such an expression does not refer to a single
+ * element while the default linearized expression would refer to
+ * a single element, we return the expression
+ *
+ * A + (..((i_0 * b_1 + i_1) ... ) * b_l + i_l)
+ *
+ * instead. Note that because of the special case handling above,
+ * we can assume here that there is at least one index expression.
+ */
+__isl_give isl_ast_expr *gpu_local_array_info_linearize_index(
+ struct gpu_local_array_info *array, __isl_take isl_ast_expr *expr)
+{
+ int i, n;
+ isl_ast_expr *arg0;
+ isl_ast_expr *res;
+ isl_ast_expr_list *list;
+
+ arg0 = isl_ast_expr_get_op_arg(expr, 0);
+ if (isl_ast_expr_get_type(arg0) == isl_ast_expr_op &&
+ isl_ast_expr_get_op_type(arg0) == isl_ast_op_member) {
+ isl_ast_expr *arg;
+
+ arg = isl_ast_expr_get_op_arg(arg0, 0);
+ arg = gpu_local_array_info_linearize_index(array, arg);
+ arg0 = isl_ast_expr_set_op_arg(arg0, 0, arg);
+ expr = isl_ast_expr_set_op_arg(expr, 0, arg0);
+
+ return expr;
+ }
+ isl_ast_expr_free(arg0);
+
+ if (isl_ast_expr_get_op_n_arg(expr) == 1)
+ return expr;
+
+ n = isl_ast_expr_get_op_n_arg(expr);
+ res = isl_ast_expr_get_op_arg(expr, 1);
+ for (i = 1; i < array->n_index; ++i) {
+ isl_ast_expr *expr_i;
+
+ expr_i = isl_ast_expr_get_op_arg(array->bound_expr, 1 + i);
+ res = isl_ast_expr_mul(res, expr_i);
+
+ if (i + 1 >= n)
+ continue;
+ expr_i = isl_ast_expr_get_op_arg(expr, i + 1);
+ res = isl_ast_expr_add(res, expr_i);
+ }
+
+ if (1 + array->n_index > n) {
+ res = isl_ast_expr_add(isl_ast_expr_get_op_arg(expr, 0), res);
+ } else {
+ list = isl_ast_expr_list_from_ast_expr(res);
+ res = isl_ast_expr_get_op_arg(expr, 0);
+ res = isl_ast_expr_access(res, list);
+ }
+
+ isl_ast_expr_free(expr);
+
+ return res;
+}
+
+/* AST expression transformation callback for pet_stmt_build_ast_exprs.
+ *
+ * If the AST expression refers to an array that is not accessed
+ * at all, then this means the value of the expression is not used,
+ * so we might as well print zero (NULL pointer) instead.
+ *
+ * If the AST expression refers to a global scalar that is not
+ * a read-only scalar, then its address was passed to the kernel and
+ * we need to dereference it.
+ *
+ * If the AST expression refers to an access to a global array,
+ * then we linearize the access exploiting the bounds in data->local_array.
+ */
+static __isl_give isl_ast_expr *transform_expr(__isl_take isl_ast_expr *expr,
+ __isl_keep isl_id *id, void *user)
+{
+ struct ppcg_transform_data *data = user;
+
+ if (!data->array)
+ return expr;
+ if (!data->array->accessed) {
+ isl_ctx *ctx;
+
+ ctx = isl_ast_expr_get_ctx(expr);
+ isl_ast_expr_free(expr);
+ return isl_ast_expr_from_val(isl_val_zero(ctx));
+ }
+ if (gpu_array_is_read_only_scalar(data->array))
+ return expr;
+ if (!data->global)
+ return expr;
+ if (data->array->n_index == 0)
+ return dereference(expr);
+ if (!data->array->linearize)
+ return expr;
+
+ return gpu_local_array_info_linearize_index(data->local_array, expr);
+}
+
+/* This function is called for each instance of a user statement
+ * in the kernel "kernel", identified by "gpu_stmt".
+ * "kernel" may be NULL if we are not inside a kernel.
+ *
+ * We attach a struct ppcg_kernel_stmt to the "node", containing
+ * a computed AST expression for each access, through an annotation
+ * with name "user".
+ * These AST expressions are computed from iterator_map,
+ * which expresses the domain
+ * elements in terms of the generated loops, and sched2copy,
+ * which expresses the outer copy_schedule_dim dimensions of
+ * the kernel schedule computed by PPCG in terms of the generated loops.
+ */
+static __isl_give isl_ast_node *create_domain_leaf(
+ struct ppcg_kernel *kernel, __isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build, struct gpu_stmt *gpu_stmt,
+ struct gpu_gen *gen)
+{
+ struct ppcg_transform_data data;
+ struct ppcg_kernel_stmt *stmt;
+ isl_ctx *ctx;
+ isl_id *id;
+ isl_pw_multi_aff *sched2copy;
+ isl_map *map;
+ isl_pw_multi_aff *iterator_map;
+ isl_union_map *schedule;
+
+ if (!node)
+ return NULL;
+ ctx = isl_ast_node_get_ctx(node);
+
+ stmt = isl_calloc_type(ctx, struct ppcg_kernel_stmt);
+ if (!stmt)
+ return isl_ast_node_free(node);
+
+ schedule = isl_ast_build_get_schedule(build);
+ map = isl_map_reverse(isl_map_from_union_map(schedule));
+ iterator_map = isl_pw_multi_aff_from_map(map);
+ if (kernel)
+ sched2copy = compute_sched_to_copy(kernel,
+ isl_pw_multi_aff_copy(iterator_map));
+ else
+ sched2copy = NULL;
+
+ stmt->type = ppcg_kernel_domain;
+ stmt->u.d.stmt = gpu_stmt;
+
+ data.kernel = kernel;
+ data.accesses = stmt->u.d.stmt->accesses;
+ data.iterator_map = iterator_map;
+ data.sched2copy = sched2copy;
+ stmt->u.d.ref2expr = gen->build_ast_expr(stmt->u.d.stmt->stmt,
+ build, &transform_index, &data,
+ &transform_expr, &data);
+
+ isl_pw_multi_aff_free(iterator_map);
+ isl_pw_multi_aff_free(sched2copy);
+
+ id = isl_id_alloc(ctx, "user", stmt);
+ id = isl_id_set_free_user(id, &ppcg_kernel_stmt_free);
+ return isl_ast_node_set_annotation(node, id);
+}
+
+/* This function is called for each statement node in the AST
+ * for copying to or from shared/private memory.
+ * Attach a pointer to a ppcg_kernel_stmt representing the copy
+ * statement to the node.
+ * The statement name is "read" or "write", depending on whether we are
+ * reading from global memory or writing to global memory.
+ *
+ * The schedule is of the form
+ *
+ * type[D -> A] -> L
+ *
+ * where D corresponds to the outer tile->depth dimensions of
+ * the kernel schedule, A to the global array and L to the outer
+ * generated AST schedule.
+ * We compute the inverse and strip off the type, resulting in
+ *
+ * L -> [D -> A]
+ *
+ * We combine this mapping with on the one hand the projection
+ *
+ * [D -> A] -> A
+ *
+ * and on the other hand the group tiling
+ *
+ * [D -> A] -> T
+ *
+ * resulting in
+ *
+ * L -> A and L -> T
+ *
+ * and store the corresponding expressions in stmt->index and stmt->local_index,
+ * where stmt points to the ppcg_kernel_stmt that is attached to the node.
+ * stmt->index is linearized if the global memory array is linearized.
+ */
+static __isl_give isl_ast_node *create_access_leaf(struct ppcg_kernel *kernel,
+ struct gpu_array_ref_group *group, __isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build)
+{
+ struct ppcg_kernel_stmt *stmt;
+ struct gpu_array_tile *tile;
+ isl_id *id;
+ isl_ast_expr *expr;
+ isl_space *space;
+ isl_map *access;
+ isl_pw_multi_aff *pma, *pma2;
+ const char *type;
+
+ stmt = isl_calloc_type(kernel->ctx, struct ppcg_kernel_stmt);
+ if (!stmt)
+ return isl_ast_node_free(node);
+
+ access = isl_map_from_union_map(isl_ast_build_get_schedule(build));
+ type = isl_map_get_tuple_name(access, isl_dim_in);
+ stmt->u.c.read = !strcmp(type, "read");
+ access = isl_map_reverse(access);
+ pma = isl_pw_multi_aff_from_map(access);
+ pma = isl_pw_multi_aff_reset_tuple_id(pma, isl_dim_out);
+
+ space = isl_space_range(isl_pw_multi_aff_get_space(pma));
+ space = isl_space_unwrap(space);
+ pma2 = isl_pw_multi_aff_range_map(space);
+ pma2 = isl_pw_multi_aff_pullback_pw_multi_aff(pma2,
+ isl_pw_multi_aff_copy(pma));
+ expr = isl_ast_build_access_from_pw_multi_aff(build, pma2);
+ if (group->array->linearize)
+ expr = gpu_local_array_info_linearize_index(group->local_array,
+ expr);
+ stmt->u.c.index = expr;
+
+ tile = gpu_array_ref_group_tile(group);
+ pma2 = isl_pw_multi_aff_from_multi_aff(
+ isl_multi_aff_copy(tile->tiling));
+ pma2 = isl_pw_multi_aff_pullback_pw_multi_aff(pma2, pma);
+ expr = isl_ast_build_access_from_pw_multi_aff(build, pma2);
+ stmt->u.c.local_index = expr;
+
+ stmt->u.c.array = group->array;
+ stmt->u.c.local_array = group->local_array;
+ stmt->type = ppcg_kernel_copy;
+
+ id = isl_id_alloc(kernel->ctx, "copy", stmt);
+ id = isl_id_set_free_user(id, &ppcg_kernel_stmt_free);
+ return isl_ast_node_set_annotation(node, id);
+}
+
+/* Create a synchronization ppcg_kernel_stmt and
+ * attach it to the node "node" representing the synchronization.
+ */
+static __isl_give isl_ast_node *create_sync_leaf(
+ struct ppcg_kernel *kernel, __isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build)
+{
+ struct ppcg_kernel_stmt *stmt;
+ isl_id *id;
+
+ stmt = isl_calloc_type(kernel->ctx, struct ppcg_kernel_stmt);
+ if (!stmt)
+ return isl_ast_node_free(node);
+
+ stmt->type = ppcg_kernel_sync;
+ id = isl_id_alloc(kernel->ctx, "sync", stmt);
+ id = isl_id_set_free_user(id, &ppcg_kernel_stmt_free);
+ return isl_ast_node_set_annotation(node, id);
+}
+
+/* Build AST expressions for the device array sizes of all arrays in "prog"
+ * that require allocation on the device using "build", as well as
+ * for the original array sizes of all arrays that need to be declared
+ * on the host.
+ * "node" is freed in case of error.
+ */
+static __isl_give isl_ast_node *build_array_bounds(
+ __isl_take isl_ast_node *node, struct gpu_prog *prog,
+ __isl_keep isl_ast_build *build)
+{
+ int i;
+
+ for (i = 0; i < prog->n_array; ++i) {
+ struct gpu_array_info *array = &prog->array[i];
+ isl_multi_pw_aff *size;
+ isl_ast_expr *expr;
+
+ if (!gpu_array_requires_device_allocation(array))
+ continue;
+
+ size = isl_multi_pw_aff_copy(array->bound);
+ expr = ppcg_build_size_expr(size, build);
+ array->bound_expr = expr;
+ if (!expr)
+ return isl_ast_node_free(node);
+ }
+
+ for (i = 0; i < prog->n_array; ++i) {
+ struct gpu_array_info *array = &prog->array[i];
+ isl_set *extent;
+ isl_multi_pw_aff *size;
+ isl_ast_expr *expr;
+
+ if (!array->declare_local)
+ continue;
+ extent = isl_set_copy(array->declared_extent);
+ size = ppcg_size_from_extent(extent);
+ expr = ppcg_build_size_expr(size, build);
+ array->declared_size = expr;
+ if (!expr)
+ return isl_ast_node_free(node);
+ }
+
+ return node;
+}
+
+/* Internal data structure for at_domain.
+ *
+ * "prog" represents the entire scop.
+ * "kernel" points to the kernel to which the current schedule node
+ * belongs. It is set by before_mark and reset by after_mark.
+ * It may be NULL if we are outside any kernel.
+ */
+struct ppcg_at_domain_data {
+ struct gpu_prog *prog;
+ struct gpu_gen *gen;
+ struct ppcg_kernel *kernel;
+};
+
+/* This function is called for each instance of a user statement
+ * in the kernel. This may be one of the original user statements
+ * or a statement introduced by PPCG.
+ *
+ * We first check if the statement id corresponds to a gpu statement,
+ * which indicates the statement is an original user statement. Any statement
+ * that is not an original user statement has been introduced by PPCG and
+ * requires special handling.
+ *
+ * If the user statement is one of the original user statements, then we call
+ * create_domain_leaf. If it is "init_device", then we call
+ * build_array_bounds. Otherwise, we check if it is a copy or synchronization
+ * statement and call the appropriate functions. Statements that copy an array
+ * to/from the device do not need any further treatment.
+ * Neither does "clear_device".
+ */
+static __isl_give isl_ast_node *at_domain(__isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build, void *user)
+{
+ struct ppcg_at_domain_data *data = user;
+ struct gpu_stmt *gpu_stmt;
+ isl_ast_expr *expr, *arg;
+ isl_id *id;
+ int is_sync;
+ const char *name;
+ void *p;
+
+ expr = isl_ast_node_user_get_expr(node);
+ arg = isl_ast_expr_get_op_arg(expr, 0);
+ id = isl_ast_expr_get_id(arg);
+ name = isl_id_get_name(id);
+ p = isl_id_get_user(id);
+ isl_ast_expr_free(expr);
+ isl_ast_expr_free(arg);
+
+ gpu_stmt = find_stmt(data->prog, id);
+ is_sync = gpu_tree_id_is_sync(id, data->kernel);
+ isl_id_free(id);
+
+ if (gpu_stmt)
+ return create_domain_leaf(data->kernel, node, build, gpu_stmt,
+ data->gen);
+
+ if (!prefixcmp(name, "to_device_") || !prefixcmp(name, "from_device_"))
+ return node;
+ if (!strcmp(name, "init_device"))
+ return build_array_bounds(node, data->prog, build);
+ if (!strcmp(name, "clear_device"))
+ return node;
+ if (is_sync < 0)
+ return isl_ast_node_free(node);
+ if (!strcmp(name, "read") || !strcmp(name, "write")) {
+ struct gpu_array_ref_group *group = p;
+ return create_access_leaf(data->kernel, group, node, build);
+ }
+ if (!is_sync)
+ isl_die(data->prog->ctx, isl_error_internal,
+ "unknown statement type",
+ return isl_ast_node_free(node));
+ return create_sync_leaf(data->kernel, node, build);
+}
+
+/* Given a set of wrapped references "ref", return the corresponding
+ * access relations based on the tagged access relations "tagged".
+ *
+ * The elements of "ref" are of the form
+ *
+ * [D -> R]
+ *
+ * with D an iteration domains and R a reference.
+ * The elements of "tagged" are of the form
+ *
+ * [D -> R] -> A
+ *
+ * with A an array.
+ *
+ * Extend "tagged" to include the iteration domain in the range, i.e.,
+ *
+ * [D -> R] -> [D -> A]
+ *
+ * apply the result to "ref" and then unwrap the resulting set
+ * to obtain relations of the form
+ *
+ * D -> A
+ */
+static __isl_give isl_union_map *wrapped_reference_to_access(
+ __isl_take isl_union_set *ref, __isl_take isl_union_map *tagged)
+{
+ isl_union_map *tag2access;
+
+ tag2access = isl_union_map_copy(tagged);
+ tag2access = isl_union_map_universe(tag2access);
+ tag2access = isl_union_set_unwrap(isl_union_map_domain(tag2access));
+ tag2access = isl_union_map_domain_map(tag2access);
+ tag2access = isl_union_map_range_product(tag2access, tagged);
+
+ ref = isl_union_set_coalesce(ref);
+ ref = isl_union_set_apply(ref, tag2access);
+
+ return isl_union_set_unwrap(ref);
+}
+
+/* Given an access relation "access" from one or more array reference groups,
+ * remove those reads if ("read" is 1) or writes (if "read" is 0)
+ * that are only needed to communicate data within
+ * the same iteration of "sched".
+ * The domain of "sched" corresponds to the original statement instances,
+ * i.e., those that appear in the domains of the access relations.
+ * "tagged" contains all tagged access relations to all
+ * the array reference groups accessed by "access" from statement
+ * instances scheduled by "sched".
+ *
+ * If the access is a read then it is either an element of
+ *
+ * live_in union (range flow)
+ *
+ * where live_in and flow may be overapproximations, or
+ * it reads an uninitialized value (that is not live-in because
+ * there is an intermediate kill) or it reads a value that was
+ * written within the same (compound) statement instance.
+ * If the access is a write then it is either an element of
+ *
+ * live_out union (domain flow)
+ *
+ * or it writes a value that is never read (and is not live-out
+ * because of an intermediate kill) or only
+ * within the same (compound) statement instance.
+ * In both cases, the access relation is also a subset of
+ * the group access relation.
+ *
+ * The cases where an uninitialized value is read or a value is written
+ * that is never read or where the dataflow occurs within a statement
+ * instance are also considered local and may also be removed.
+ *
+ * Essentially, we compute the intersection of "access" with either
+ *
+ * live_in union (range non-local-flow)
+ *
+ * or
+ *
+ * live_out union (domain non-local-flow)
+ *
+ * We first construct a relation "local"
+ *
+ * [[D -> R] -> [D' -> R']]
+ *
+ * of pairs of domain iterations accessing the reference group
+ * and references in the group that are coscheduled by "sched".
+ *
+ * If this relation does not intersect the dataflow dependences,
+ * then there is nothing we can possibly remove, unless the dataflow
+ * dependences themselves only relate a subset of the accesses.
+ * In particular, the accesses may not be involved in any dataflow
+ * dependences, either because they are uninitialized reads/dead writes
+ * or because the dataflow occurs inside a statement instance.
+ *
+ * Since the computation below may break up the access relation
+ * into smaller pieces, we only perform the intersection with
+ * the non-local dependent accesses if the local pairs
+ * intersect the dataflow dependences. Otherwise, we intersect
+ * with the universe of the non-local dependent accesses.
+ * This should at least remove accesses from statements that
+ * do not participate in any dependences.
+ *
+ * In particular, we remove the "local" dataflow dependences from
+ * the set of all dataflow dependences, or at least those
+ * that may contribute to a domain/range that intersects
+ * the domain of "access".
+ * Note that if the potential dataflow dependences are an overapproximation
+ * of the actual dataflow dependences, then the result remains an
+ * overapproximation of the non-local dataflow dependences.
+ * Copying to/from global memory is only needed for the references
+ * in the domain/range of the result or for accesses that are live out/in
+ * for the entire scop.
+ *
+ * We therefore map the domain/range of the "external" relation
+ * to the corresponding access relation and take the union with
+ * the live out/in relation.
+ */
+static __isl_give isl_union_map *remove_local_accesses(
+ struct gpu_prog *prog, __isl_take isl_union_map *tagged,
+ __isl_take isl_union_map *access, __isl_take isl_union_map *sched,
+ int read)
+{
+ int empty;
+ isl_union_pw_multi_aff *tagger;
+ isl_union_set *domain, *access_domain;
+ isl_union_map *local, *external, *universe;
+ isl_union_set *tag_set;
+
+ if (isl_union_map_is_empty(access)) {
+ isl_union_map_free(sched);
+ isl_union_map_free(tagged);
+ return access;
+ }
+
+ tagger = isl_union_pw_multi_aff_copy(prog->scop->tagger);
+ domain = isl_union_map_domain(isl_union_map_copy(tagged));
+ tagger = isl_union_pw_multi_aff_intersect_domain(tagger,
+ isl_union_set_copy(domain));
+ sched = isl_union_map_preimage_domain_union_pw_multi_aff(sched, tagger);
+
+ local = isl_union_map_apply_range(sched,
+ isl_union_map_reverse(isl_union_map_copy(sched)));
+ local = isl_union_map_intersect(local,
+ isl_union_map_copy(prog->scop->tagged_dep_flow));
+
+ empty = isl_union_map_is_empty(local);
+
+ external = isl_union_map_copy(prog->scop->tagged_dep_flow);
+ universe = isl_union_map_universe(isl_union_map_copy(access));
+ access_domain = isl_union_map_domain(universe);
+ domain = isl_union_set_universe(domain);
+ universe = isl_union_set_unwrap(domain);
+ universe = isl_union_map_intersect_domain(universe, access_domain);
+ domain = isl_union_map_wrap(universe);
+ if (read)
+ external = isl_union_map_intersect_range(external, domain);
+ else
+ external = isl_union_map_intersect_domain(external, domain);
+ external = isl_union_map_intersect_params(external,
+ isl_set_copy(prog->scop->context));
+ external = isl_union_map_subtract(external, local);
+
+ if (read) {
+ tag_set = isl_union_map_range(external);
+ external = wrapped_reference_to_access(tag_set, tagged);
+ external = isl_union_map_union(external,
+ isl_union_map_copy(prog->scop->live_in));
+ } else {
+ tag_set = isl_union_map_domain(external);
+ external = wrapped_reference_to_access(tag_set, tagged);
+ external = isl_union_map_union(external,
+ isl_union_map_copy(prog->scop->live_out));
+ }
+
+ if (empty < 0)
+ external = isl_union_map_free(external);
+ else if (empty)
+ external = isl_union_map_universe(external);
+
+ access = isl_union_map_intersect(access, external);
+
+ return access;
+}
+
+/* Given an access relation "access" from "group", remove those reads
+ * if ("read" is 1) or writes (if "read" is 0) that are only needed to
+ * communicate data within the same iteration of the schedule "prefix"
+ * at the position where the copying of the group is inserted.
+ * That is, the output dimension of "prefix"
+ * is equal to tile->depth.
+ * The domain of "prefix" corresponds to the original statement instances,
+ * i.e., those that appear in the domains of the access relations.
+ *
+ * Extract the tagged access relation of "group" and
+ * then call remove_local_accesses.
+ */
+static __isl_give isl_union_map *remove_local_accesses_group(
+ struct ppcg_kernel *kernel, struct gpu_array_ref_group *group,
+ __isl_take isl_union_map *access, __isl_keep isl_union_map *prefix,
+ int read)
+{
+ isl_union_map *sched, *tagged;
+
+ if (isl_union_map_is_empty(access))
+ return access;
+
+ tagged = group_tagged_access_relation(group);
+ sched = isl_union_map_copy(prefix);
+
+ return remove_local_accesses(kernel->prog, tagged, access, sched, read);
+}
+
+/* Build an access AST expression for the effective grid size using "build".
+ * Store the result in kernel->grid_size_expr.
+ */
+static isl_stat build_grid_size(struct ppcg_kernel *kernel,
+ __isl_keep isl_ast_build *build)
+{
+ isl_multi_pw_aff *size;
+
+ size = isl_multi_pw_aff_copy(kernel->grid_size);
+ size = isl_multi_pw_aff_set_tuple_name(size, isl_dim_out, "grid");
+ kernel->grid_size_expr = ppcg_build_size_expr(size, build);
+
+ if (!kernel->grid_size_expr)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Build access AST expressions for the localized array sizes using "build".
+ * Store the result in local->bound_expr.
+ * Only do this for arrays for which localized bounds have been computed.
+ */
+static isl_stat build_local_array_sizes(struct ppcg_kernel *kernel,
+ __isl_keep isl_ast_build *build)
+{
+ int i;
+
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *local = &kernel->array[i];
+ isl_multi_pw_aff *size;
+
+ if (local->n_group == 0)
+ continue;
+ size = isl_multi_pw_aff_copy(local->bound);
+ local->bound_expr = ppcg_build_size_expr(size, build);
+ if (!local->bound_expr)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Build access AST expressions for the effective grid size and
+ * the localized array sizes using "build".
+ */
+static isl_stat build_grid_and_local_array_sizes(struct ppcg_kernel *kernel,
+ __isl_keep isl_ast_build *build)
+{
+ if (build_grid_size(kernel, build) < 0)
+ return isl_stat_error;
+ if (build_local_array_sizes(kernel, build) < 0)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* This function is called before the AST generator starts traversing
+ * the schedule subtree of a node with mark "mark".
+ *
+ * If the mark is called "kernel", store the kernel pointer in data->kernel
+ * for use in at_domain and build AST expressions for the grid size and
+ * the localized array sizes.
+ */
+static isl_stat before_mark(__isl_keep isl_id *mark,
+ __isl_keep isl_ast_build *build, void *user)
+{
+ struct ppcg_at_domain_data *data = user;
+
+ if (!mark)
+ return isl_stat_error;
+ if (!strcmp(isl_id_get_name(mark), "kernel")) {
+ data->kernel = isl_id_get_user(mark);
+ if (build_grid_and_local_array_sizes(data->kernel, build) < 0)
+ return isl_stat_error;
+ }
+ return isl_stat_ok;
+}
+
+/* This function is called after the AST generator has finished traversing
+ * the schedule subtree of a mark node. "node" points to the corresponding
+ * mark AST node.
+ *
+ * If the mark is called "kernel", then replace "node" by a user node
+ * that "calls" the kernel, representing the launch of the kernel.
+ * The original "node" is stored inside the kernel object so that
+ * it can be used to print the device code.
+ * Note that this assumes that a kernel is only launched once.
+ * Also clear data->kernel.
+ */
+static __isl_give isl_ast_node *after_mark(__isl_take isl_ast_node *node,
+ __isl_keep isl_ast_build *build, void *user)
+{
+ isl_ctx *ctx;
+ isl_id *id;
+ isl_ast_expr *expr;
+ isl_ast_expr_list *list;
+ struct ppcg_kernel *kernel;
+ struct ppcg_at_domain_data *data = user;
+
+ ctx = isl_ast_node_get_ctx(node);
+ id = isl_ast_node_mark_get_id(node);
+ if (!id)
+ return isl_ast_node_free(node);
+ if (strcmp(isl_id_get_name(id), "kernel") || !data->kernel) {
+ isl_id_free(id);
+ return node;
+ }
+ kernel = data->kernel;
+ data->kernel = NULL;
+ kernel->space = isl_ast_build_get_schedule_space(build);
+ kernel->tree = isl_ast_node_mark_get_node(node);
+ isl_ast_node_free(node);
+
+ expr = isl_ast_expr_from_id(isl_id_copy(id));
+ list = isl_ast_expr_list_alloc(ctx, 0);
+ expr = isl_ast_expr_call(expr, list);
+ node = isl_ast_node_alloc_user(expr);
+ node = isl_ast_node_set_annotation(node, id);
+
+ return node;
+}
+
+static isl_bool update_depth(__isl_keep isl_schedule_node *node, void *user)
+{
+ int *depth = user;
+ int node_depth;
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_leaf)
+ return isl_bool_true;
+ node_depth = isl_schedule_node_get_schedule_depth(node);
+ if (node_depth > *depth)
+ *depth = node_depth;
+
+ return isl_bool_false;
+}
+
+/* Use isl to generate code for both the host and the device
+ * from "schedule".
+ * The device code is marked by "kernel" mark nodes in the schedule tree,
+ * containing a pointer to a ppcg_kernel object.
+ * The returned AST only contains the AST for the host code.
+ * The ASTs for the device code are embedded in ppcg_kernel objects
+ * attached to the leaf nodes that call "kernel".
+ */
+__isl_give isl_ast_node *generate_code(struct gpu_gen *gen,
+ __isl_take isl_schedule *schedule)
+{
+ struct ppcg_at_domain_data data;
+ isl_ast_build *build;
+ isl_ast_node *tree;
+ isl_id_list *iterators;
+ int depth;
+
+ data.prog = gen->prog;
+ data.gen = gen;
+ data.kernel = NULL;
+
+ depth = 0;
+ if (isl_schedule_foreach_schedule_node_top_down(schedule, &update_depth,
+ &depth) < 0)
+ return NULL;
+ build = isl_ast_build_alloc(gen->prog->ctx);
+ iterators = ppcg_scop_generate_names(gen->prog->scop, depth, "c");
+ build = isl_ast_build_set_iterators(build, iterators);
+ build = isl_ast_build_set_at_each_domain(build, &at_domain, &data);
+ build = isl_ast_build_set_before_each_mark(build, &before_mark, &data);
+ build = isl_ast_build_set_after_each_mark(build, &after_mark, &data);
+ if (gen->prog->scop->options->debug->dump_final_schedule)
+ isl_schedule_dump(schedule);
+ tree = isl_ast_build_node_from_schedule(build, schedule);
+ isl_ast_build_free(build);
+
+ return tree;
+}
+
+__isl_give isl_union_map *extract_sizes_from_str(isl_ctx *ctx, const char *str)
+{
+ if (!str)
+ return NULL;
+ return isl_union_map_read_from_str(ctx, str);
+}
+
+/* Can "node" be tiled and then mapped to block and thread identifiers?
+ * That is, is it permutable with at least one coincident dimension?
+ */
+static int is_permutable(__isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return -1;
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_band)
+ return 0;
+ if (!isl_schedule_node_band_get_permutable(node))
+ return 0;
+ if (isl_schedule_node_band_n_member(node) < 1)
+ return 0;
+ if (!isl_schedule_node_band_member_get_coincident(node, 0))
+ return 0;
+
+ return 1;
+}
+
+/* A isl_schedule_foreach_schedule_node_top_down callback
+ * for setting *any_permutable and aborting the search
+ * if "node" is a permutable band with coincident dimensions.
+ * Otherwise, continue searching.
+ */
+static isl_bool set_permutable(__isl_keep isl_schedule_node *node, void *user)
+{
+ int *any_permutable = user;
+ int permutable;
+
+ permutable = is_permutable(node);
+ if (permutable < 0)
+ return isl_bool_error;
+ if (!permutable)
+ return isl_bool_true;
+
+ *any_permutable = 1;
+
+ return isl_bool_error;
+}
+
+/* Does the subtree rooted at "node" have any suitably permutable band nodes?
+ * That is, does it have any nodes that are permutable and that
+ * have a least one coincident dimension?
+ */
+static int subtree_has_permutable_bands(__isl_keep isl_schedule_node *node)
+{
+ int any_parallelism = 0;
+
+ if (isl_schedule_node_foreach_descendant_top_down(node, &set_permutable,
+ &any_parallelism) < 0 &&
+ !any_parallelism)
+ return -1;
+
+ return any_parallelism;
+}
+
+/* Does "schedule" contain any permutable band with at least one coincident
+ * member?
+ */
+int has_any_permutable_node(__isl_keep isl_schedule *schedule)
+{
+ isl_schedule_node *root;
+ int any_permutable;
+
+ root = isl_schedule_get_root(schedule);
+ any_permutable = subtree_has_permutable_bands(root);
+ isl_schedule_node_free(root);
+
+ return any_permutable;
+}
+
+/* Is "node" a candidate for mapping to block and thread identifiers?
+ * In particular, is it permutable with at least one coincident dimension?
+ * Alternatively, does the subtree rooted at "node" not contain
+ * any such permutable node? Filter nodes are skipped in this case,
+ * because a band node will be inserted in front of the returned
+ * node and this is not possible for filter nodes that are children
+ * of set or sequence nodes.
+ */
+static int is_candidate(__isl_keep isl_schedule_node *node)
+{
+ int permutable;
+
+ if (isl_schedule_node_get_type(node) == isl_schedule_node_leaf)
+ return 1;
+ permutable = is_permutable(node);
+ if (permutable < 0 || permutable)
+ return permutable;
+ if (isl_schedule_node_get_type(node) == isl_schedule_node_filter)
+ return 0;
+ permutable = subtree_has_permutable_bands(node);
+ if (permutable < 0)
+ return -1;
+ return !permutable;
+}
+
+/* Is "node" the outermost node in its branch that can be tiled
+ * and then mapped to block and thread identifiers?
+ * If there are no such nodes in the subtree at "node" and
+ * if "node" is not a filter node, then it is accepted too.
+ */
+static int is_outer_tilable(__isl_keep isl_schedule_node *node)
+{
+ int tilable;
+ isl_schedule_node *ancestor;
+
+ tilable = is_candidate(node);
+ if (tilable < 0)
+ return -1;
+ if (!tilable)
+ return 0;
+
+ tilable = 0;
+ ancestor = isl_schedule_node_copy(node);
+ while (isl_schedule_node_has_parent(ancestor)) {
+ ancestor = isl_schedule_node_parent(ancestor);
+
+ tilable = is_candidate(ancestor);
+ if (tilable < 0 || tilable)
+ break;
+ }
+
+ isl_schedule_node_free(ancestor);
+ return tilable < 0 ? -1 : !tilable;
+}
+
+/* Collect the references to all writes in "group".
+ * Each reference is represented by a universe set in a space
+ *
+ * [S[i,j] -> R[]]
+ *
+ * with S[i,j] the statement instance space and R[] the array reference.
+ */
+static __isl_give isl_union_set *group_tagged_writes(
+ struct gpu_array_ref_group *group)
+{
+ int i;
+ isl_space *space;
+ isl_union_set *writes;
+
+ space = isl_map_get_space(group->access);
+ writes = isl_union_set_empty(space);
+ for (i = 0; i < group->n_ref; ++i) {
+ isl_space *space;
+ isl_set *writes_i;
+
+ if (!group->refs[i]->write)
+ continue;
+
+ space = isl_map_get_space(group->refs[i]->tagged_access);
+ space = isl_space_domain(space);
+ writes_i = isl_set_universe(space);
+ writes = isl_union_set_add_set(writes, writes_i);
+ }
+
+ return writes;
+}
+
+/* Is there any write access in "group" that requires synchronization
+ * on a write to global memory?
+ * We currently take into account all writes that would require
+ * synchronization at the thread level depth, but if the copying
+ * for this group is performed at an outer level, then we do not
+ * actually need to take into account dependences at intermediate levels.
+ */
+static int any_sync_writes_in_group(struct ppcg_kernel *kernel,
+ struct gpu_array_ref_group *group)
+{
+ isl_union_set *writes;
+ int empty, disjoint;
+
+ empty = isl_union_set_is_empty(kernel->sync_writes);
+ if (empty < 0)
+ return -1;
+ if (empty)
+ return 0;
+
+ writes = group_tagged_writes(group);
+ disjoint = isl_union_set_is_disjoint(kernel->sync_writes, writes);
+ isl_union_set_free(writes);
+
+ return disjoint < 0 ? -1 : !disjoint;
+}
+
+/* Collect the references to all writes in "kernel" that write directly
+ * to global or shared memory, i.e., that are not mapped to private memory.
+ * Each reference is represented by a universe set in a space
+ *
+ * [S[i,j] -> R[]]
+ *
+ * with S[i,j] the statement instance space and R[] the array reference.
+ */
+static __isl_give isl_union_set *collect_non_private_tagged_writes(
+ struct ppcg_kernel *kernel)
+{
+ isl_union_set *writes;
+ int i, j;
+
+ writes = isl_union_set_empty(isl_union_set_get_space(kernel->arrays));
+
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *array = &kernel->array[i];
+
+ for (j = 0; j < array->n_group; ++j) {
+ struct gpu_array_ref_group *group = array->groups[j];
+ enum ppcg_group_access_type type;
+ isl_union_set *writes_ij;
+
+ if (!group->write)
+ continue;
+ type = gpu_array_ref_group_type(group);
+ if (type == ppcg_access_private)
+ continue;
+ writes_ij = group_tagged_writes(group);
+ writes = isl_union_set_union(writes, writes_ij);
+ }
+ }
+
+ return writes;
+}
+
+/* Are there any direct writes to global memory that require
+ * synchronization?
+ */
+static int any_global_or_shared_sync_writes(struct ppcg_kernel *kernel)
+{
+ isl_union_set *writes;
+ int empty, disjoint;
+
+ empty = isl_union_set_is_empty(kernel->sync_writes);
+ if (empty < 0)
+ return -1;
+ if (empty)
+ return 0;
+
+ writes = collect_non_private_tagged_writes(kernel);
+ disjoint = isl_union_set_is_disjoint(kernel->sync_writes, writes);
+ isl_union_set_free(writes);
+
+ return disjoint < 0 ? -1 : !disjoint;
+}
+
+/* Construct an isl_multi_val for use as tile sizes for tiling "node"
+ * from the elements in "tile_size".
+ */
+static __isl_give isl_multi_val *construct_band_tiles_sizes(
+ __isl_keep isl_schedule_node *node, int *tile_size)
+{
+ isl_space *space;
+
+ if (!node)
+ return NULL;
+
+ space = isl_schedule_node_band_get_space(node);
+ return ppcg_multi_val_from_int_list(space, tile_size);
+}
+
+/* Replace the partial schedule S of the band node "node" by
+ *
+ * floor(S/f)
+ *
+ * or
+ *
+ * f * floor(S/f)
+ *
+ * if scale_tile_loops is set, with f the integers in "factor".
+ * The list that "factor" points to is assumed to contain at least
+ * as many elements as the number of members in the band.
+ */
+static __isl_give isl_schedule_node *snap_band_to_sizes(
+ __isl_take isl_schedule_node *node, int *factor,
+ struct ppcg_options *options)
+{
+ isl_multi_val *mv;
+
+ mv = construct_band_tiles_sizes(node, factor);
+ node = isl_schedule_node_band_scale_down(node, isl_multi_val_copy(mv));
+ if (options->scale_tile_loops)
+ node = isl_schedule_node_band_scale(node,
+ isl_multi_val_copy(mv));
+ isl_multi_val_free(mv);
+
+ return node;
+}
+
+/* Tile "band" with tile size specified by "sizes".
+ *
+ * Since the tile loops will be mapped to block ids, we forcibly
+ * turn off tile loop scaling. We may want to enable tile loop scaling
+ * at some later point, but then we would have to support the detection
+ * of strides during the mapping to block ids.
+ * Similarly, since the point loops will be mapped to thread ids,
+ * we forcibly shift the point loops so that they start at zero.
+ */
+static __isl_give isl_schedule_node *tile_band(
+ __isl_take isl_schedule_node *node, __isl_take isl_multi_val *sizes)
+{
+ isl_ctx *ctx = isl_schedule_node_get_ctx(node);
+ int scale_tile;
+ int shift_point;
+
+ scale_tile = isl_options_get_tile_scale_tile_loops(ctx);
+ isl_options_set_tile_scale_tile_loops(ctx, 0);
+ shift_point = isl_options_get_tile_shift_point_loops(ctx);
+ isl_options_set_tile_shift_point_loops(ctx, 1);
+
+ node = isl_schedule_node_band_tile(node, sizes);
+
+ isl_options_set_tile_scale_tile_loops(ctx, scale_tile);
+ isl_options_set_tile_shift_point_loops(ctx, shift_point);
+
+ return node;
+}
+
+/* Extract the set of parameter values and outer schedule dimensions
+ * for which any statement instance
+ * in the kernel inserted at "node" needs to be executed.
+ * Intersect the set of parameter values derived from the host schedule
+ * relation with the context of "prog".
+ */
+static __isl_give isl_set *extract_context(__isl_keep isl_schedule_node *node,
+ struct gpu_prog *prog)
+{
+ isl_union_map *schedule;
+ isl_union_set *schedule_domain;
+ isl_set *context;
+ int empty;
+
+ schedule = isl_schedule_node_get_prefix_schedule_relation(node);
+ schedule_domain = isl_union_map_range(schedule);
+ empty = isl_union_set_is_empty(schedule_domain);
+ if (empty < 0) {
+ isl_union_set_free(schedule_domain);
+ return NULL;
+ }
+ if (empty) {
+ int depth;
+ isl_space *space;
+
+ space = isl_union_set_get_space(schedule_domain);
+ isl_union_set_free(schedule_domain);
+ space = isl_space_set_from_params(space);
+ depth = isl_schedule_node_get_schedule_depth(node);
+ space = isl_space_add_dims(space, isl_dim_set, depth);
+ context = isl_set_empty(space);
+ } else {
+ context = isl_set_from_union_set(schedule_domain);
+ }
+ context = isl_set_intersect_params(context,
+ isl_set_copy(prog->context));
+
+ return context;
+}
+
+/* Return the set of outer array elements accessed by
+ * by the statement instances in "domain" in "prog".
+ * The instances in "domain" are those that appear
+ * in the domains of the access relations in "prog".
+ */
+static __isl_give isl_union_set *accessed_by_domain(
+ __isl_take isl_union_set *domain, struct gpu_prog *prog)
+{
+ isl_union_map *access;
+ isl_union_set *arrays;
+
+ access = isl_union_map_union(isl_union_map_copy(prog->read),
+ isl_union_map_copy(prog->may_write));
+ access = isl_union_map_intersect_domain(access, domain);
+ arrays = isl_union_map_range(access);
+ arrays = isl_union_set_apply(arrays,
+ isl_union_map_copy(prog->to_outer));
+
+ return arrays;
+}
+
+/* Return the number of outer band members of the band node "node"
+ * that are marked coincident.
+ */
+static int n_outer_coincidence(__isl_keep isl_schedule_node *node)
+{
+ int i, n;
+
+ n = isl_schedule_node_band_n_member(node);
+
+ for (i = 0; i < n; ++i)
+ if (!isl_schedule_node_band_member_get_coincident(node, i))
+ break;
+
+ return i;
+}
+
+/* If the band node "node" has more than "n" members, then split off
+ * the first "n" of them.
+ */
+static __isl_give isl_schedule_node *split_band(
+ __isl_take isl_schedule_node *node, int n)
+{
+ int dim;
+
+ dim = isl_schedule_node_band_n_member(node);
+ if (n < dim)
+ node = isl_schedule_node_band_split(node, n);
+
+ return node;
+}
+
+/* Scale a band node that may have been split by split_band.
+ * "sizes" are the scaling factors for the original node.
+ * "node" either points to the original band node, or the outer
+ * of the two pieces after splitting.
+ *
+ * If the number of elements in "node" is smaller than the number of
+ * elements in "sizes", then some splitting has occurred and we split
+ * "sizes" in the same way.
+ */
+static __isl_give isl_schedule_node *scale_band(
+ __isl_take isl_schedule_node *node, __isl_take isl_multi_val *sizes)
+{
+ int n, dim;
+
+ n = isl_multi_val_dim(sizes, isl_dim_set);
+ dim = isl_schedule_node_band_n_member(node);
+ if (n > dim) {
+ isl_multi_val *sizes2;
+
+ sizes2 = isl_multi_val_copy(sizes);
+ sizes = isl_multi_val_drop_dims(sizes,
+ isl_dim_set, dim, n - dim);
+ sizes2 = isl_multi_val_drop_dims(sizes2, isl_dim_set, 0, dim);
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_band_scale(node, sizes2);
+ node = isl_schedule_node_parent(node);
+ }
+
+ return isl_schedule_node_band_scale(node, sizes);
+}
+
+/* Return an isl_multi_aff, with as elements the parameters in "space"
+ * that have the names specified by the elements in "names".
+ * If (some of) these parameters do not already appear in "space",
+ * then they are added first.
+ */
+static __isl_give isl_multi_aff *parameter_vector(__isl_take isl_space *space,
+ __isl_keep isl_id_list *names)
+{
+ int i, n;
+ isl_local_space *ls;
+ isl_multi_aff *ma;
+
+ if (!names)
+ space = isl_space_free(space);
+
+ n = isl_id_list_n_id(names);
+ for (i = 0; i < n; ++i) {
+ int pos;
+ isl_id *id;
+
+ id = isl_id_list_get_id(names, i);
+ pos = isl_space_find_dim_by_id(space, isl_dim_param, id);
+ if (pos >= 0) {
+ isl_id_free(id);
+ continue;
+ }
+ pos = isl_space_dim(space, isl_dim_param);
+ space = isl_space_add_dims(space, isl_dim_param, 1);
+ space = isl_space_set_dim_id(space, isl_dim_param, pos, id);
+ }
+ ma = isl_multi_aff_zero(isl_space_copy(space));
+ ls = isl_local_space_from_space(isl_space_domain(space));
+ for (i = 0; i < n; ++i) {
+ int pos;
+ isl_id *id;
+ isl_aff *aff;
+
+ id = isl_id_list_get_id(names, i);
+ pos = isl_space_find_dim_by_id(space, isl_dim_param, id);
+ isl_id_free(id);
+ aff = isl_aff_var_on_domain(isl_local_space_copy(ls),
+ isl_dim_param, pos);
+ ma = isl_multi_aff_set_aff(ma, i, aff);
+ }
+ isl_local_space_free(ls);
+
+ return ma;
+}
+
+/* Return constraints on the domain elements that equate a sequence of
+ * parameters called "names", to the partial schedule
+ * of "node" modulo the integers in "size".
+ * The number of elements in the array "size" should be equal
+ * to the number of elements in "names".
+ * The number of members of the band node "node" should be smaller
+ * than or equal to this number. If it is smaller, then the first
+ * elements of "names" are equated to zero.
+ */
+static __isl_give isl_union_set *set_schedule_modulo(
+ __isl_keep isl_schedule_node *node, __isl_keep isl_id_list *names,
+ int *size)
+{
+ int n, n_zero;
+ isl_space *space;
+ isl_multi_aff *ma;
+ isl_multi_union_pw_aff *mupa, *mupa2;
+ isl_multi_val *mv;
+ isl_union_set *domain;
+
+ if (!node)
+ return NULL;
+ n = isl_id_list_n_id(names);
+ if (n == 0)
+ return isl_schedule_node_get_universe_domain(node);
+ n_zero = n - isl_schedule_node_band_n_member(node);
+
+ mupa = isl_schedule_node_band_get_partial_schedule(node);
+ mv = construct_band_tiles_sizes(node, size + n_zero);
+ mupa = isl_multi_union_pw_aff_mod_multi_val(mupa, mv);
+
+ space = isl_multi_union_pw_aff_get_space(mupa);
+ space = isl_space_params(space);
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, n_zero);
+ ma = isl_multi_aff_zero(space);
+
+ domain = isl_schedule_node_get_universe_domain(node);
+ mupa2 = isl_multi_union_pw_aff_multi_aff_on_domain(
+ isl_union_set_copy(domain), ma);
+ mupa = isl_multi_union_pw_aff_range_product(mupa2, mupa);
+
+ space = isl_multi_union_pw_aff_get_space(mupa);
+ ma = parameter_vector(space, names);
+
+ mupa2 = isl_multi_union_pw_aff_multi_aff_on_domain(domain, ma);
+ mupa = isl_multi_union_pw_aff_sub(mupa, mupa2);
+
+ return isl_multi_union_pw_aff_zero_union_set(mupa);
+}
+
+/* Insert a context node at "node" introducing the block and thread
+ * identifiers along with their bounds, which are stored in kernel->grid_size
+ * and kernel->block_dim.
+ * Note that the bounds on the block identifiers may implicitly impose
+ * constraints on the parameters. A guard needs to be inserted
+ * in the schedule tree to ensure that those bounds hold at "node".
+ * This guard is inserted in insert_guard.
+ */
+static __isl_give isl_schedule_node *insert_context(struct ppcg_kernel *kernel,
+ __isl_take isl_schedule_node *node)
+{
+ isl_set *context;
+
+ context = isl_set_universe(isl_set_get_space(kernel->context));
+
+ context = add_bounded_parameters_dynamic(context,
+ kernel->grid_size, kernel->block_ids);
+ context = add_bounded_parameters(context,
+ kernel->block_dim, kernel->thread_ids);
+
+ node = isl_schedule_node_insert_context(node, context);
+
+ return node;
+}
+
+/* Insert a guard that eliminates kernel launches where the kernel
+ * obviously does not have any work to do.
+ *
+ * In particular, eliminate kernel launches where there are obviously
+ * zero blocks.
+ * Use the same block size constraints that are used to create the context
+ * to ensure that all constraints implicit in the constructed context
+ * are imposed by the guard.
+ *
+ * Additionally, add other constraints that are valid
+ * for each executed instance ("context"), as long as this does not result
+ * in a disjunction.
+ */
+static __isl_give isl_schedule_node *insert_guard(
+ __isl_take isl_schedule_node *node, __isl_keep isl_set *context,
+ __isl_keep isl_multi_pw_aff *size, struct ppcg_scop *scop)
+{
+ unsigned nparam, n;
+ isl_set *guard;
+ isl_id_list *ids;
+
+ guard = isl_set_copy(context);
+ guard = isl_set_compute_divs(guard);
+ guard = isl_set_from_basic_set(isl_set_simple_hull(guard));
+
+ nparam = isl_set_dim(guard, isl_dim_param);
+ n = isl_multi_pw_aff_dim(size, isl_dim_out);
+ ids = ppcg_scop_generate_names(scop, n, "__ppcg_tmp");
+ guard = add_bounded_parameters_dynamic(guard, size, ids);
+ isl_id_list_free(ids);
+ guard = isl_set_project_out(guard, isl_dim_param, nparam, n);
+
+ node = isl_schedule_node_insert_guard(node, guard);
+
+ return node;
+}
+
+/* Does any array reference group mapping require the band that is mapped
+ * to threads to be unrolled?
+ */
+static int kernel_requires_unroll(struct ppcg_kernel *kernel)
+{
+ int i, j;
+
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *array = &kernel->array[i];
+
+ for (j = 0; j < array->n_group; ++j) {
+ struct gpu_array_ref_group *group = array->groups[j];
+ if (gpu_array_ref_group_requires_unroll(group))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Mark the given band node "node" for unrolling by the AST generator and
+ * then sink it to the leaves of the schedule tree.
+ * All dimensions of "node" are assumed to be coincident, such that this
+ * sinking is a valid operation.
+ */
+static __isl_give isl_schedule_node *unroll(__isl_take isl_schedule_node *node)
+{
+ node = ppcg_set_schedule_node_type(node, isl_ast_loop_unroll);
+
+ node = isl_schedule_node_band_sink(node);
+
+ return node;
+}
+
+/* Insert a synchronization node in the schedule tree of "node"
+ * after the core computation of "kernel" at the level of the band
+ * that is mapped to threads, except if that level is equal to
+ * that of the band that is mapped to blocks or if there are no writes
+ * to global or shared memory in the core computation that require
+ * synchronization.
+ * If there are any writes to shared memory and the shared memory
+ * copying is performed at the same level, then synchronization
+ * is needed between the core and the copying anyway, so we might
+ * as well add it here. If the copying is performed at a higher
+ * level, then different iterations of intermediate schedule dimensions
+ * may have a different mapping from between shared memory elements and
+ * threads, such that synchronization is required after the core.
+ * "node" is assumed to point to the kernel node.
+ *
+ * If the shared and the thread mark point to the same node, then make
+ * sure the synchronization is inserted outside of the shared mark.
+ */
+static __isl_give isl_schedule_node *add_sync(struct ppcg_kernel *kernel,
+ __isl_take isl_schedule_node *node)
+{
+ int depth;
+ int need_sync;
+
+ need_sync = any_global_or_shared_sync_writes(kernel);
+ if (need_sync < 0)
+ return isl_schedule_node_free(node);
+ if (!need_sync)
+ return node;
+
+ node = gpu_tree_move_down_to_thread(node, kernel->core);
+ depth = isl_schedule_node_get_schedule_depth(node);
+ node = gpu_tree_move_up_to_kernel(node);
+ if (depth == isl_schedule_node_get_schedule_depth(node))
+ return node;
+
+ node = gpu_tree_move_down_to_depth(node, depth, kernel->core);
+ node = gpu_tree_ensure_following_sync(node, kernel);
+
+ node = gpu_tree_move_up_to_kernel(node);
+
+ return node;
+}
+
+/* Return a read ("read" is 1) or write access relation for "group"
+ * with those accesses removed that are only needed to communicate data
+ * within the subtree of the schedule rooted at "node".
+ * Furthermore, include the prefix schedule at "node".
+ * That is, return a relation of the form
+ *
+ * S -> [D -> A]
+ *
+ * with D the outer schedule dimensions at "node".
+ */
+static __isl_give isl_union_map *anchored_non_local_accesses(
+ struct ppcg_kernel *kernel, struct gpu_array_ref_group *group,
+ __isl_take isl_schedule_node *node, int read)
+{
+ isl_union_map *access;
+ isl_union_map *prefix;
+
+ prefix = isl_schedule_node_get_prefix_schedule_relation(node);
+ prefix = isl_union_map_preimage_domain_union_pw_multi_aff(prefix,
+ isl_union_pw_multi_aff_copy(kernel->contraction));
+ access = gpu_array_ref_group_access_relation(group, read, !read);
+ access = remove_local_accesses_group(kernel, group, access, prefix,
+ read);
+ access = isl_union_map_range_product(prefix, access);
+
+ return access;
+}
+
+/* Given an array reference group "group", create a mapping
+ *
+ * read[D -> A] -> [D -> A]
+ *
+ * if "read" is set or
+ *
+ * write[D -> A] -> [D -> A]
+ *
+ * if "read" is not set.
+ * D corresponds to the outer tile->depth dimensions of
+ * the kernel schedule.
+ */
+static __isl_give isl_multi_aff *create_from_access(isl_ctx *ctx,
+ struct gpu_array_ref_group *group, int read)
+{
+ struct gpu_array_tile *tile;
+ isl_space *space;
+ isl_id *id;
+
+ tile = gpu_array_ref_group_tile(group);
+ space = isl_space_copy(group->array->space);
+ space = isl_space_from_range(space);
+ space = isl_space_add_dims(space, isl_dim_in, tile->depth);
+ space = isl_space_wrap(space);
+ space = isl_space_map_from_set(space);
+
+ id = isl_id_alloc(ctx, read ? "read" : "write", group);
+ space = isl_space_set_tuple_id(space, isl_dim_in, id);
+
+ return isl_multi_aff_identity(space);
+}
+
+/* If any writes in "group" require synchronization, then make sure
+ * that there is a synchronization node for "kernel" after the node
+ * following "node" in a sequence.
+ *
+ * If "shared" is set and no synchronization is needed for
+ * the writes to global memory, then add synchronization before
+ * the kernel to protect shared memory from being overwritten
+ * by the next iteration of the core computation.
+ * No additional synchronization is needed to protect against
+ * the next copy into shared memory because each element of
+ * the shared memory tile is always copied by the same thread.
+ */
+static __isl_give isl_schedule_node *add_group_write_sync(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel,
+ struct gpu_array_ref_group *group, int shared)
+{
+ int need_sync;
+
+ need_sync = any_sync_writes_in_group(kernel, group);
+ if (need_sync < 0)
+ return isl_schedule_node_free(node);
+ if (need_sync) {
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_next_sibling(node);
+ node = isl_schedule_node_child(node, 0);
+ node = gpu_tree_ensure_following_sync(node, kernel);
+ } else if (shared) {
+ struct gpu_array_tile *tile;
+
+ tile = gpu_array_ref_group_tile(group);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_parent(node);
+ node = gpu_tree_move_down_to_depth(node, tile->depth,
+ kernel->core);
+ node = gpu_tree_move_left_to_sync(node, kernel);
+ }
+
+ return node;
+}
+
+/* Add copy statements to the schedule tree of "node"
+ * for reading from global memory to private memory (if "read" is set) or
+ * for writing back from private memory to global memory
+ * (if "read" is not set) for the array reference group "group" that
+ * is mapped to private memory.
+ * On input, "node" points to the kernel node, and it is moved
+ * back there on output.
+ *
+ * The copies are performed in the order of the array elements.
+ * The copy statement instances include a reference to the outer
+ * tile->depth dimensions of the kernel schedule for ease of
+ * combining them with the group tiling.
+ *
+ * That is, the extra schedule is of the form
+ *
+ * type[D -> A] -> A
+ *
+ * where D corresponds to the outer tile->depth dimensions of
+ * the kernel schedule and A to the global array.
+ * This schedule is unrolled because registers are not addressable.
+ *
+ * The copying is inserted in the schedule tree through an extension
+ * of the form
+ *
+ * D -> type[D -> A]
+ *
+ * where the extra domain elements type[D -> A] are those accessed
+ * by the group.
+ * A filter is inserted on type[D -> A] to ensure that the element
+ * is read/written by the same thread that needs the element.
+ * This filter is obtained by applying
+ *
+ * S -> type[D -> A]
+ *
+ * to the thread filter for the core statements.
+ *
+ * The extension is inserted before the core computation in case of a read
+ * and after the core computation in case of a write.
+ * In the latter case, we also make sure that there is a synchronization
+ * node after the write to global memory, unless this write is performed
+ * at the outer level of the kernel.
+ * In principle, this synchronization could be inserted higher
+ * in the schedule tree depending on where the corresponding reads
+ * from global memory are performed.
+ */
+static __isl_give isl_schedule_node *add_copies_group_private(
+ struct ppcg_kernel *kernel, struct gpu_array_ref_group *group,
+ __isl_take isl_schedule_node *node, int read)
+{
+ struct gpu_array_tile *tile;
+ isl_union_map *access;
+ isl_union_set *domain;
+ isl_space *space;
+ isl_multi_aff *from_access;
+ isl_multi_pw_aff *mpa;
+ isl_multi_union_pw_aff *mupa;
+ isl_union_pw_multi_aff *contraction;
+ isl_schedule_node *graft;
+ isl_union_set *filter;
+ int kernel_depth;
+ int empty;
+
+ kernel_depth = isl_schedule_node_get_schedule_depth(node);
+ tile = gpu_array_ref_group_tile(group);
+ node = gpu_tree_move_down_to_depth(node, tile->depth, kernel->core);
+
+ access = anchored_non_local_accesses(kernel, group, node, read);
+ empty = isl_union_map_is_empty(access);
+ if (empty < 0 || empty) {
+ isl_union_map_free(access);
+ if (empty < 0)
+ return isl_schedule_node_free(node);
+ return gpu_tree_move_up_to_kernel(node);
+ }
+
+ group->array->global = 1;
+ group->local_array->global = 1;
+
+ from_access = create_from_access(kernel->ctx, group, read);
+ space = isl_space_domain(isl_multi_aff_get_space(from_access));
+ access = isl_union_map_preimage_range_multi_aff(access, from_access);
+
+ filter = isl_union_set_copy(kernel->thread_filter);
+ contraction = isl_union_pw_multi_aff_copy(kernel->contraction);
+ filter = isl_union_set_preimage_union_pw_multi_aff(filter, contraction);
+ filter = isl_union_set_apply(filter, isl_union_map_copy(access));
+ filter = isl_union_set_detect_equalities(filter);
+ filter = isl_union_set_coalesce(filter);
+
+ domain = isl_union_map_range(access);
+ access = isl_union_set_wrapped_domain_map(domain);
+ access = isl_union_map_reverse(access);
+ access = isl_union_map_coalesce(access);
+ graft = isl_schedule_node_from_extension(access);
+
+ space = isl_space_map_from_set(space);
+ mpa = isl_multi_pw_aff_identity(space);
+ mpa = isl_multi_pw_aff_range_factor_range(mpa);
+ mupa = isl_multi_union_pw_aff_from_multi_pw_aff(mpa);
+
+ graft = isl_schedule_node_child(graft, 0);
+ graft = isl_schedule_node_insert_partial_schedule(graft, mupa);
+ graft = unroll(graft);
+
+ graft = isl_schedule_node_insert_filter(graft, filter);
+
+ graft = isl_schedule_node_parent(graft);
+
+ if (read)
+ node = isl_schedule_node_graft_before(node, graft);
+ else {
+ node = isl_schedule_node_graft_after(node, graft);
+ if (kernel_depth < tile->depth)
+ node = add_group_write_sync(node, kernel, group, 0);
+ }
+
+ node = gpu_tree_move_up_to_kernel(node);
+
+ return node;
+}
+
+/* Add copy statements to the schedule tree of "node"
+ * for reading from global memory to shared memory (if "read" is set) or
+ * for writing back from shared memory to global memory
+ * (if "read" is not set) for the array reference group "group" that
+ * is mapped to shared memory.
+ * On input, "node" points to the kernel node, and it is moved
+ * back there on output.
+ *
+ * The copies are performed in the order of the corresponding shared
+ * memory tile.
+ * The copy statement instances include a reference to the outer
+ * tile->depth dimensions of the kernel schedule for ease of
+ * combining them with the group tiling.
+ *
+ * If we are performing a read from global memory to shared memory and
+ * if the array involved is not a scalar, then we copy
+ * the entire tile to shared memory. This may result in some extra
+ * elements getting copied, but it should lead to simpler code
+ * (which means that fewer registers may be needed) and less divergence.
+ *
+ * Otherwise, we only copy the elements that will be read or have been written
+ * in the kernel.
+ *
+ * That is, the extra schedule is of the form
+ *
+ * type[D -> A] -> T
+ *
+ * where D corresponds to the outer tile->depth dimensions of
+ * the kernel schedule, A to the global array and T is the corresponding
+ * shared memory tile.
+ *
+ * The copying is inserted in the schedule tree through an extension
+ * of the form
+ *
+ * D -> type[D -> A]
+ *
+ * where the extra domain elements type[D -> A] are those accessed
+ * by the group. In the case of read from a non-scalar, this set
+ * is replaced by the entire shared memory tile.
+ *
+ * If the "unroll_copy_shared" option is set, then the AST generator
+ * is instructed to unroll the copying code.
+ *
+ * A filter is inserted on type[D -> A] to map the copy instances
+ * to the threads. In particular, the thread identifiers are
+ * equated to the position inside the shared memory tile (T)
+ * modulo the block size.
+ * We try to align the innermost tile dimension with the innermost
+ * thread identifier (x) as a heuristic to improve coalescing.
+ * In particular, if the dimension of the tile is greater than
+ * the dimension of the block, then the schedule mapping to the tile
+ * is broken up into two pieces and the filter is applied to the inner part.
+ * If, on the other hand, the dimension of the tile is smaller than
+ * the dimension of the block, then the initial thread identifiers
+ * are equated to zero and the remaining thread identifiers are
+ * matched to the memory tile.
+ *
+ * The extension is inserted before the core computation in case of a read
+ * and after the core computation in case of a write.
+ * In the case of a read, we first need to make sure there is some
+ * synchronization before the core computation such that we can put the read
+ * from global memory to shared memory before that synchronization.
+ * This ensures that all threads have finished copying into shared memory
+ * before the shared memory is used.
+ * We also need to make sure that there is a synchronization node after
+ * the core computation to ensure that the next load into shared memory
+ * only happens after all data has been used. There is no need for
+ * this synchronization if we are at the outer level since then there
+ * won't be a next load.
+ * In the case of a write, we need to make sure there is some synchronization
+ * after the core computation such taht we can put the write from shared
+ * memory to global memory after that synchronization.
+ * Unless we are at the outer level, we also need a synchronization node
+ * after the write to ensure the data is saved to global memory
+ * before the next iteration write to the same shared memory.
+ * It also makes sure the data has arrived in global memory before
+ * it is read in a subsequent iteration.
+ */
+static __isl_give isl_schedule_node *add_copies_group_shared(
+ struct ppcg_kernel *kernel, struct gpu_array_ref_group *group,
+ __isl_take isl_schedule_node *node, int read)
+{
+ struct gpu_array_tile *tile;
+ isl_union_map *access;
+ isl_union_set *domain;
+ isl_multi_aff *ma;
+ isl_multi_aff *from_access;
+ isl_multi_pw_aff *mpa;
+ isl_multi_union_pw_aff *mupa;
+ isl_schedule_node *graft;
+ isl_union_set *filter;
+ int skip;
+ int kernel_depth;
+ int empty;
+
+ tile = gpu_array_ref_group_tile(group);
+ kernel_depth = isl_schedule_node_get_schedule_depth(node);
+ node = gpu_tree_move_down_to_depth(node, tile->depth, kernel->core);
+
+ access = anchored_non_local_accesses(kernel, group, node, read);
+ empty = isl_union_map_is_empty(access);
+ if (empty < 0 || empty) {
+ isl_union_map_free(access);
+ if (empty < 0)
+ return isl_schedule_node_free(node);
+ return gpu_tree_move_up_to_kernel(node);
+ }
+
+ group->array->global = 1;
+ group->local_array->global = 1;
+
+ from_access = create_from_access(kernel->ctx, group, read);
+
+ ma = isl_multi_aff_copy(tile->tiling);
+ ma = isl_multi_aff_pullback_multi_aff(ma,
+ isl_multi_aff_copy(from_access));
+ mpa = isl_multi_pw_aff_from_multi_aff(ma);
+ mupa = isl_multi_union_pw_aff_from_multi_pw_aff(mpa);
+
+ domain = isl_union_map_range(access);
+
+ if (read && !gpu_array_is_scalar(group->array)) {
+ isl_map *map;
+ isl_union_set_free(domain);
+ map = group_tile(group);
+ domain = isl_union_set_from_set(isl_map_wrap(map));
+ }
+
+ domain = isl_union_set_preimage_multi_aff(domain, from_access);
+ access = isl_union_set_wrapped_domain_map(domain);
+ access = isl_union_map_reverse(access);
+ access = isl_union_map_coalesce(access);
+ graft = isl_schedule_node_from_extension(access);
+
+ graft = isl_schedule_node_child(graft, 0);
+
+ graft = isl_schedule_node_insert_partial_schedule(graft, mupa);
+ if (kernel->options->unroll_copy_shared)
+ graft = ppcg_set_schedule_node_type(graft, isl_ast_loop_unroll);
+
+ if (tile->n > kernel->n_block && kernel->n_block > 0) {
+ graft = isl_schedule_node_band_split(graft,
+ tile->n - kernel->n_block);
+ graft = isl_schedule_node_child(graft, 0);
+ }
+ if (tile->n < kernel->n_block)
+ skip = kernel->n_block - tile->n;
+ else
+ skip = 0;
+ filter = set_schedule_modulo(graft, kernel->thread_ids,
+ kernel->block_dim);
+ if (!kernel->options->wrap)
+ graft = snap_band_to_sizes(graft, kernel->block_dim + skip,
+ kernel->options);
+ if (tile->n > kernel->n_block && kernel->n_block > 0)
+ graft = isl_schedule_node_parent(graft);
+ graft = isl_schedule_node_insert_filter(graft, filter);
+
+ while (graft && isl_schedule_node_has_parent(graft))
+ graft = isl_schedule_node_parent(graft);
+
+ if (read) {
+ if (kernel_depth < tile->depth)
+ node = gpu_tree_ensure_sync_after_core(node, kernel);
+ node = gpu_tree_move_left_to_sync(node, kernel);
+ node = isl_schedule_node_graft_before(node, graft);
+ } else {
+ node = gpu_tree_move_right_to_sync(node, kernel);
+ node = isl_schedule_node_graft_after(node, graft);
+ if (kernel_depth < tile->depth)
+ node = add_group_write_sync(node, kernel, group, 1);
+ }
+
+ node = gpu_tree_move_up_to_kernel(node);
+
+ return node;
+}
+
+/* Check whether the array reference group "group" is mapped to
+ * private or shared memory and, if so,
+ * add copy statements to the schedule tree of "node"
+ * for reading from global memory to private or shared memory
+ * (if "read" is set) or for writing back from private or shared memory
+ * to global memory (if "read" is not set) for this group.
+ * On input, "node" points to the kernel node, and it is moved
+ * back there on output.
+ */
+static __isl_give isl_schedule_node *add_copies_group(
+ struct ppcg_kernel *kernel, struct gpu_array_ref_group *group,
+ __isl_take isl_schedule_node *node, int read)
+{
+ enum ppcg_group_access_type type;
+
+ type = gpu_array_ref_group_type(group);
+ if (type == ppcg_access_private)
+ return add_copies_group_private(kernel, group, node, read);
+ if (type == ppcg_access_shared)
+ return add_copies_group_shared(kernel, group, node, read);
+ return node;
+}
+
+/* For each array reference group that is mapped to private or shared memory,
+ * add copy statements to the schedule tree of "node"
+ * for reading from global memory to private or shared memory
+ * and for writing back.
+ * On input, "node" points to the kernel node, and it is moved
+ * back there on output.
+ */
+static __isl_give isl_schedule_node *add_copies(struct ppcg_kernel *kernel,
+ __isl_take isl_schedule_node *node)
+{
+ int i, j;
+
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *array = &kernel->array[i];
+
+ for (j = 0; j < array->n_group; ++j) {
+ struct gpu_array_ref_group *group = array->groups[j];
+
+ node = add_copies_group(kernel, group, node, 1);
+ if (!node)
+ return NULL;
+ node = add_copies_group(kernel, group, node, 0);
+ if (!node)
+ return NULL;
+ }
+ }
+
+ return node;
+}
+
+/* Mark all dimensions in the current band node atomic.
+ */
+static __isl_give isl_schedule_node *atomic(__isl_take isl_schedule_node *node)
+{
+ return ppcg_set_schedule_node_type(node, isl_ast_loop_atomic);
+}
+
+/* Mark "node" atomic, if it is a band node.
+ * Do the same for all ancestors.
+ * Return a pointer to "node" (in the updated schedule tree).
+ */
+static __isl_give isl_schedule_node *atomic_ancestors(
+ __isl_take isl_schedule_node *node)
+{
+ int pos;
+
+ if (!node)
+ return NULL;
+ if (!isl_schedule_node_has_parent(node))
+ return node;
+
+ pos = isl_schedule_node_get_child_position(node);
+ node = isl_schedule_node_parent(node);
+ if (isl_schedule_node_get_type(node) == isl_schedule_node_band)
+ node = atomic(node);
+ node = atomic_ancestors(node);
+ node = isl_schedule_node_child(node, pos);
+
+ return node;
+}
+
+/* Collect all write references that require synchronization.
+ * "node" is assumed to point to the kernel node.
+ * Each reference is represented by a universe set in a space
+ *
+ * [S[i,j] -> R[]]
+ *
+ * with S[i,j] the statement instance space and R[] the array reference.
+ *
+ * This function should be called before block and thread filters are added.
+ *
+ * Synchronization is needed after a write if there is a subsequent read
+ * within the same block that may not be performed by the same thread.
+ * There should not be any dependences between different blocks,
+ * so we start with the flow dependences within the same kernel invocation
+ * and we subtract from these those dependences that are mapped
+ * to the same iteration of the bands where synchronization is inserted.
+ * We do not remove pairs of instances that are known to map to
+ * the same thread across different iterations of the intermediate
+ * bands because the read may be performed by a different thread
+ * than the one that needs the value if shared memory is involved.
+ *
+ * We also consider all pairs of possible writes that access the same
+ * memory location and that may be mapped to the same block but not
+ * to the same iteration of the intermediate bands.
+ * In theory, it would be possible for one thread to still be in
+ * a previous iteration of a loop in these bands.
+ * A write to global memory in this delayed thread could then overwrite
+ * a write from another thread that has already moved on to
+ * the next iteration.
+ *
+ * After computing the above writes paired off with reads or writes
+ * that depend on them, we project onto the domain writes.
+ * Sychronization is needed after writes to global memory
+ * through these references.
+ */
+static __isl_give isl_union_set *compute_sync_writes(
+ struct ppcg_kernel *kernel, __isl_keep isl_schedule_node *node)
+{
+ isl_union_map *local;
+ isl_union_map *may_writes, *shared_access;
+ isl_union_map *kernel_prefix, *thread_prefix;
+ isl_union_map *equal;
+ isl_union_set *wrap;
+ isl_union_set *domain;
+ isl_union_pw_multi_aff *contraction;
+
+ kernel_prefix = isl_schedule_node_get_prefix_schedule_union_map(node);
+ node = isl_schedule_node_copy(node);
+ node = gpu_tree_move_down_to_thread(node, kernel->core);
+ thread_prefix = isl_schedule_node_get_prefix_schedule_union_map(node);
+ isl_schedule_node_free(node);
+
+ contraction = kernel->contraction;
+ kernel_prefix = isl_union_map_preimage_domain_union_pw_multi_aff(
+ kernel_prefix, isl_union_pw_multi_aff_copy(contraction));
+ thread_prefix = isl_union_map_preimage_domain_union_pw_multi_aff(
+ thread_prefix, isl_union_pw_multi_aff_copy(contraction));
+ domain = isl_union_set_copy(kernel->expanded_domain);
+ domain = isl_union_set_universe(domain);
+
+ may_writes = isl_union_map_copy(kernel->prog->scop->tagged_may_writes);
+ may_writes = isl_union_map_curry(may_writes);
+ may_writes = isl_union_map_intersect_domain(may_writes, domain);
+ may_writes = isl_union_map_uncurry(may_writes);
+ shared_access = isl_union_map_copy(may_writes);
+ shared_access = isl_union_map_apply_range(shared_access,
+ isl_union_map_reverse(may_writes));
+
+ local = isl_union_map_copy(kernel->prog->scop->tagged_dep_flow);
+ local = isl_union_map_union(local, shared_access);
+ local = isl_union_map_zip(local);
+
+ equal = isl_union_map_apply_range(kernel_prefix,
+ isl_union_map_reverse(isl_union_map_copy(kernel_prefix)));
+ wrap = isl_union_map_wrap(equal);
+ local = isl_union_map_intersect_domain(local, wrap);
+ equal = isl_union_map_apply_range(thread_prefix,
+ isl_union_map_reverse(isl_union_map_copy(thread_prefix)));
+ wrap = isl_union_map_wrap(equal);
+ local = isl_union_map_subtract_domain(local, wrap);
+
+ local = isl_union_map_zip(local);
+ local = isl_union_map_universe(local);
+
+ return isl_union_map_domain(local);
+}
+
+/* Group the domain elements into a single space, named kernelX,
+ * with X the kernel sequence number "kernel_id".
+ */
+static __isl_give isl_schedule_node *group_statements(
+ __isl_take isl_schedule_node *node, int kernel_id)
+{
+ char buffer[20];
+ isl_id *id;
+
+ if (!node)
+ return NULL;
+
+ snprintf(buffer, sizeof(buffer), "kernel%d", kernel_id);
+ id = isl_id_alloc(isl_schedule_node_get_ctx(node), buffer, NULL);
+ return isl_schedule_node_group(node, id);
+}
+
+/* Create a ppcg_kernel representing the domain instances that reach "node"
+ * and insert a mark node pointing to the ppcg_kernel before "node".
+ * The band that "node" points to is the band that needs to be mapped
+ * to block identifiers. The band that needs to be mapped to thread
+ * identifiers should be marked by a "thread" mark by the caller.
+ * The linear branch between the current node and the "thread" mark
+ * may also have a "shared" mark. If present, the mapping to shared
+ * memory is computed at that point.
+ * Both marks are removed by this function.
+ * If "scale" is set, then the band that "node" points to is scaled
+ * by "sizes".
+ *
+ * Mark all outer band nodes as atomic to ensure each kernel is only
+ * scheduled once.
+ * If the domain elements that reach "node" live in more than one space,
+ * then group the domain elements into a single space, named kernelX,
+ * with X the kernel sequence number.
+ *
+ * Insert a guard node governing the kernel node to ensure that
+ * no kernels with zero blocks are launched.
+ *
+ * Insert a context node describing the block and thread
+ * identifiers inside the kernel mark.
+ * The context node needs to be inserted after the effective block size
+ * has been determined such that the bounds on the thread identifiers
+ * would reflect the effective block size.
+ * Insert a filter node inside the context node mapping the statement
+ * instances to block identifiers. In particular, the block identifiers
+ * are equated to the partial schedule of band that was marked for mapping
+ * to blocks modulo the grid size.
+ * Insert a filter node inside the "thread" mark mapping the statement
+ * instances to thread identifiers. In particular, the thread identifiers
+ * are equated to the partial schedule of band that was marked for mapping
+ * to threads modulo the block size.
+ *
+ * Compute array reference groups for all arrays, set the local
+ * array bounds based on the set of domain instances that reach
+ * the kernel node, check the total amount of shared memory used
+ * and compute all group tilings.
+ * The array reference groups are computed after the block filter
+ * has been inserted because it affects the mapping to shared or
+ * private memory. This computation also requires the thread filter
+ * (in the ppcg_kernel object), but this thread filter should not
+ * have been added to the schedule tree yet since the computation
+ * requires the schedule of the band that needs to be mapped to
+ * threads before the privatization is applied.
+ *
+ * If any array reference group requires the band mapped to threads
+ * to be unrolled, then we perform the required unrolling.
+ *
+ * We save a copy of the schedule that may influence the mappings
+ * to shared or private memory in kernel->copy_schedule.
+ *
+ * Finally, we add synchronization and copy statements to the schedule tree,
+ * remove the "thread" mark and create representations for the local
+ * variables in the kernel.
+ *
+ * We keep a copy of the isl_id that points to the kernel to ensure
+ * that the kernel does not get destroyed if the schedule node
+ * is freed due to some error condition.
+ */
+__isl_give isl_schedule_node *gpu_create_kernel(struct gpu_gen *gen,
+ __isl_take isl_schedule_node *node, int scale,
+ __isl_keep isl_multi_val *sizes)
+{
+ struct ppcg_kernel *kernel;
+ isl_id *id;
+ isl_schedule_node *node_thread;
+ isl_union_map *host_schedule;
+ isl_union_pw_multi_aff *contraction;
+ isl_set *host_domain;
+ isl_union_set *domain, *expanded;
+ int single_statement;
+
+ node = gpu_tree_insert_shared_before_thread(node);
+ if (!node)
+ return NULL;
+
+ kernel = isl_calloc_type(gen->ctx, struct ppcg_kernel);
+ kernel = ppcg_kernel_create_local_arrays(kernel, gen->prog);
+ if (!kernel)
+ return isl_schedule_node_free(node);
+
+ domain = isl_schedule_node_get_domain(node);
+ single_statement = isl_union_set_n_set(domain) == 1;
+
+ kernel->ctx = gen->ctx;
+ kernel->prog = gen->prog;
+ kernel->options = gen->options;
+ kernel->context = extract_context(node, gen->prog);
+ kernel->core = isl_union_set_universe(isl_union_set_copy(domain));
+ contraction = isl_schedule_node_get_subtree_contraction(node);
+ kernel->contraction = isl_union_pw_multi_aff_copy(contraction);
+ expanded = isl_union_set_copy(domain);
+ expanded = isl_union_set_preimage_union_pw_multi_aff(expanded,
+ contraction);
+ kernel->expanded_domain = isl_union_set_copy(expanded);
+ kernel->arrays = accessed_by_domain(expanded, gen->prog);
+ kernel->n_grid = n_outer_coincidence(node);
+ node_thread = isl_schedule_node_copy(node);
+ node_thread = gpu_tree_move_down_to_thread(node_thread, kernel->core);
+ node_thread = isl_schedule_node_child(node_thread, 0);
+ kernel->n_block = n_outer_coincidence(node_thread);
+ isl_schedule_node_free(node_thread);
+ kernel->id = gen->kernel_id++;
+ read_grid_and_block_sizes(kernel, gen);
+
+ kernel->sync_writes = compute_sync_writes(kernel, node);
+
+ host_schedule = isl_schedule_node_get_prefix_schedule_union_map(node);
+ host_domain = isl_set_from_union_set(isl_union_map_range(
+ host_schedule));
+
+ node = atomic_ancestors(node);
+
+ id = isl_id_alloc(gen->ctx, "kernel", kernel);
+ id = isl_id_set_free_user(id, &ppcg_kernel_free_wrap);
+ node = isl_schedule_node_insert_mark(node, isl_id_copy(id));
+
+ if (!single_statement)
+ node = group_statements(node, kernel->id);
+
+ node = isl_schedule_node_child(node, 0);
+ node = split_band(node, kernel->n_grid);
+ kernel->block_ids = ppcg_scop_generate_names(gen->prog->scop,
+ kernel->n_grid, "b");
+ kernel->block_filter = set_schedule_modulo(node, kernel->block_ids,
+ kernel->grid_dim);
+ kernel->grid_size = extract_grid_size(kernel,
+ isl_union_set_copy(domain));
+ if (!kernel->options->wrap)
+ node = snap_band_to_sizes(node, kernel->grid_dim,
+ kernel->options);
+ if (scale)
+ node = scale_band(node, isl_multi_val_copy(sizes));
+ node = isl_schedule_node_parent(node);
+ if (!single_statement)
+ node = isl_schedule_node_parent(node);
+ node = insert_guard(node, kernel->context, kernel->grid_size,
+ gen->prog->scop);
+ node = gpu_tree_move_down_to_thread(node, kernel->core);
+ node = isl_schedule_node_child(node, 0);
+ node = split_band(node, kernel->n_block);
+ kernel->thread_ids = ppcg_scop_generate_names(gen->prog->scop,
+ kernel->n_block, "t");
+ kernel->thread_filter = set_schedule_modulo(node, kernel->thread_ids,
+ kernel->block_dim);
+ if (extract_block_size(kernel, domain) < 0)
+ node = isl_schedule_node_free(node);
+
+ node = gpu_tree_move_up_to_kernel(node);
+ node = isl_schedule_node_child(node, 0);
+ node = insert_context(kernel, node);
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_insert_filter(node,
+ isl_union_set_copy(kernel->block_filter));
+
+ node = gpu_tree_move_up_to_kernel(node);
+
+ if (gpu_group_references(kernel, node) < 0)
+ node = isl_schedule_node_free(node);
+ localize_bounds(kernel, host_domain);
+ isl_set_free(host_domain);
+
+ check_shared_memory_bound(kernel);
+ mark_global_arrays(kernel);
+ compute_group_tilings(kernel);
+
+ node = gpu_tree_move_down_to_thread(node, kernel->core);
+ node = isl_schedule_node_child(node, 0);
+ if (!kernel->options->wrap)
+ node = snap_band_to_sizes(node, kernel->block_dim,
+ kernel->options);
+ node = isl_schedule_node_insert_filter(node,
+ isl_union_set_copy(kernel->thread_filter));
+ if (kernel_requires_unroll(kernel)) {
+ node = isl_schedule_node_child(node, 0);
+ node = unroll(node);
+ }
+
+ node = gpu_tree_move_up_to_thread(node);
+ kernel->copy_schedule_dim = isl_schedule_node_get_schedule_depth(node);
+ kernel->copy_schedule =
+ isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(node);
+ contraction = isl_union_pw_multi_aff_copy(kernel->contraction);
+ kernel->copy_schedule =
+ isl_union_pw_multi_aff_pullback_union_pw_multi_aff(
+ kernel->copy_schedule, contraction);
+
+ node = gpu_tree_move_up_to_kernel(node);
+
+ node = add_sync(kernel, node);
+ node = add_copies(kernel, node);
+
+ node = gpu_tree_move_down_to_shared(node, kernel->core);
+ node = isl_schedule_node_delete(node);
+
+ node = gpu_tree_move_down_to_thread(node, kernel->core);
+ node = isl_schedule_node_delete(node);
+
+ node = gpu_tree_move_up_to_kernel(node);
+
+ if (create_kernel_vars(kernel) < 0)
+ node = isl_schedule_node_free(node);
+
+ if (!single_statement)
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_parent(node);
+
+ isl_id_free(id);
+ return node;
+}
+
+/* Insert a zero-dimensional permutable band at "node".
+ */
+static __isl_give isl_schedule_node *insert_empty_permutable_band(
+ __isl_take isl_schedule_node *node)
+{
+ isl_space *space;
+ isl_schedule *schedule;
+ isl_union_set *domain;
+ isl_multi_union_pw_aff *mupa;
+
+ schedule = isl_schedule_node_get_schedule(node);
+ domain = isl_schedule_get_domain(schedule);
+ space = isl_union_set_get_space(domain);
+ isl_union_set_free(domain);
+ isl_schedule_free(schedule);
+
+ space = isl_space_set_from_params(space);
+ mupa = isl_multi_union_pw_aff_zero(space);
+ node = isl_schedule_node_insert_partial_schedule(node, mupa);
+ node = isl_schedule_node_band_set_permutable(node, 1);
+
+ return node;
+}
+
+/* See if hybrid tiling can be performed on "node" and its parent.
+ * If so, apply hybrid tiling and return the updated schedule tree.
+ * If not, return the original schedule tree.
+ * Return NULL on error.
+ *
+ * First check if "node", together with its parent, meets
+ * the basic requirements for hybrid tiling.
+ * If so, compute the relative dependence distances of "node"
+ * with respect to its parent and check if they are sufficiently bounded.
+ * If so, apply hybrid tiling using user specified tile sizes.
+ *
+ * The tile sizes are read before the dependence distance bounds are
+ * computed, because the user may have specified fewer dimensions
+ * than are available. In this case, the remaining schedule dimensions
+ * are split off and the dependence distances should be computed
+ * after these dimensions have been split off.
+ */
+static __isl_give isl_schedule_node *try_hybrid_tile(struct gpu_gen *gen,
+ __isl_take isl_schedule_node *node)
+{
+ int tile_len;
+ int *tile_size;
+ isl_bool ok;
+ isl_schedule_node *orig = node;
+ ppcg_ht_bounds *bounds;
+
+ ok = ppcg_ht_parent_has_input_pattern(node);
+ if (ok < 0)
+ return isl_schedule_node_free(node);
+ if (!ok)
+ return orig;
+
+ tile_len = 1 + isl_schedule_node_band_n_member(node);
+ tile_size = read_tile_sizes(gen, &tile_len);
+ if (!tile_size)
+ return isl_schedule_node_free(node);
+
+ node = isl_schedule_node_copy(node);
+ node = split_band(node, tile_len - 1);
+ node = isl_schedule_node_parent(node);
+ bounds = ppcg_ht_compute_bounds(gen->prog->scop, node);
+ node = isl_schedule_node_child(node, 0);
+
+ ok = ppcg_ht_bounds_is_valid(bounds);
+ if (ok >= 0 && ok)
+ node = gpu_hybrid_tile(gen, node, bounds, tile_size);
+ else
+ ppcg_ht_bounds_free(bounds);
+ free(tile_size);
+
+ if (ok >= 0 && !ok) {
+ isl_schedule_node_free(node);
+ return orig;
+ }
+ isl_schedule_node_free(orig);
+ if (ok < 0)
+ return isl_schedule_node_free(node);
+ return node;
+}
+
+/* If "node" is the outermost permutable band that can be mapped to block and
+ * thread identifiers in its branch (or the root of a subtree with
+ * no such outer bands),
+ * then mark the band as such, attaching a ppcg_kernel to the mark.
+ *
+ * If hybrid tiling is allowed, then first try and apply it
+ * to "node" and its parent.
+ *
+ * If "node" is the root of a subtree without permutable bands,
+ * then insert a zero-dimensional permutable band such that
+ * we can assume that "node" always points to a band node.
+ * This includes the case where "node" already points to a band node,
+ * but one without any coincident dimension. In this case,
+ * the extra node ensures that this original node does not get tiled.
+ *
+ * Tile "node" using user specified tile sizes, after splitting the band
+ * if the number of specified tile sizes is smaller than the dimension
+ * of the band. Mark the point band of this tiling as the band that
+ * needs to be mapped to threads and instruct the AST generator to unroll
+ * the band if the "unroll_gpu_tile" option is set.
+ * Create a kernel representing the domain instances that reach "node" and
+ * insert a mark node pointing to the ppcg_kernel before the band node.
+ */
+static __isl_give isl_schedule_node *mark_outer_permutable(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ struct gpu_gen *gen = user;
+ int outer;
+ int scale;
+ int tile_len;
+ int *tile_size;
+ isl_id *id;
+ isl_multi_val *sizes;
+
+ outer = is_outer_tilable(node);
+ if (outer < 0)
+ return isl_schedule_node_free(node);
+ if (!outer)
+ return node;
+
+ if (gen->options->hybrid) {
+ isl_schedule_node *saved = isl_schedule_node_copy(node);
+ node = try_hybrid_tile(gen, node);
+ isl_schedule_node_free(saved);
+ if (node != saved)
+ return node;
+ }
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_band ||
+ !isl_schedule_node_band_member_get_coincident(node, 0))
+ node = insert_empty_permutable_band(node);
+
+ tile_len = isl_schedule_node_band_n_member(node);
+ tile_size = read_tile_sizes(gen, &tile_len);
+ if (!tile_size)
+ return isl_schedule_node_free(node);
+ if (tile_len < isl_schedule_node_band_n_member(node))
+ node = isl_schedule_node_band_split(node, tile_len);
+ sizes = construct_band_tiles_sizes(node, tile_size);
+ node = tile_band(node, isl_multi_val_copy(sizes));
+ node = isl_schedule_node_child(node, 0);
+ if (gen->options->unroll_gpu_tile)
+ node = ppcg_set_schedule_node_type(node, isl_ast_loop_unroll);
+ id = isl_id_alloc(gen->ctx, "thread", NULL);
+ node = isl_schedule_node_insert_mark(node, id);
+ node = isl_schedule_node_parent(node);
+
+ scale = gen->options->scale_tile_loops;
+ node = gpu_create_kernel(gen, node, scale, sizes);
+ isl_multi_val_free(sizes);
+ free(tile_size);
+
+ return node;
+}
+
+/* Given a set or sequence node, return the union the filters of either all
+ * (if "only_initial" is not set) or the initial (if "only_initial" is set)
+ * direct subtrees that do not contain any suitably permutable bands
+ * (according to subtree_has_permutable_bands).
+ */
+static __isl_give isl_union_set *get_non_parallel_subtree_filters(
+ __isl_keep isl_schedule_node *node, int only_initial)
+{
+ isl_space *space;
+ isl_union_set *filter;
+ int i, n;
+
+ n = isl_schedule_node_n_children(node);
+ if (n < 0)
+ return NULL;
+
+ node = isl_schedule_node_copy(node);
+ node = isl_schedule_node_child(node, 0);
+ filter = isl_schedule_node_filter_get_filter(node);
+ node = isl_schedule_node_parent(node);
+ space = isl_union_set_get_space(filter);
+ isl_union_set_free(filter);
+ filter = isl_union_set_empty(space);
+
+ for (i = 0; i < n; ++i) {
+ int parallelism;
+
+ node = isl_schedule_node_child(node, i);
+ parallelism = subtree_has_permutable_bands(node);
+ if (parallelism < 0) {
+ filter = isl_union_set_free(filter);
+ } else if (!parallelism) {
+ isl_union_set *filter_i;
+ filter_i = isl_schedule_node_filter_get_filter(node);
+ filter = isl_union_set_union(filter, filter_i);
+ } else if (only_initial)
+ break;
+ node = isl_schedule_node_parent(node);
+ }
+
+ isl_schedule_node_free(node);
+
+ return filter;
+}
+
+/* Given a set or sequence node, return the union of the filters of
+ * the direct subtrees that do not contain any suitably permutable bands
+ * (according to subtree_has_permutable_bands).
+ */
+static __isl_give isl_union_set *get_all_non_parallel_subtree_filters(
+ __isl_keep isl_schedule_node *node)
+{
+ return get_non_parallel_subtree_filters(node, 0);
+}
+
+/* Given a set or sequence node, return the union of the filters of
+ * the initial direct subtrees that do not contain any suitably permutable
+ * bands (according to subtree_has_permutable_bands).
+ */
+static __isl_give isl_union_set *get_initial_non_parallel_subtree_filters(
+ __isl_keep isl_schedule_node *node)
+{
+ return get_non_parallel_subtree_filters(node, 1);
+}
+
+/* Mark all variables that are accessed by the statement instances in "domain"
+ * and that are local to "prog" as requiring a declaration in the host code.
+ * The statement instances in "domain" correspond to (a subset of)
+ * the active instances at "node".
+ * "node" is not modified by this function, except that NULL is returned
+ * in case of error.
+ */
+static __isl_give isl_schedule_node *declare_accessed_local_variables(
+ __isl_take isl_schedule_node *node, struct gpu_prog *prog,
+ __isl_keep isl_union_set *domain)
+{
+ isl_union_pw_multi_aff *contraction;
+ isl_union_set *arrays;
+ int i;
+
+ if (!ppcg_scop_any_hidden_declarations(prog->scop))
+ return node;
+ contraction = isl_schedule_node_get_subtree_contraction(node);
+ domain = isl_union_set_copy(domain);
+ domain = isl_union_set_preimage_union_pw_multi_aff(domain, contraction);
+ arrays = accessed_by_domain(domain, prog);
+
+ for (i = 0; i < prog->n_array; ++i) {
+ isl_space *space;
+ isl_set *set;
+ int empty;
+
+ if (!prog->array[i].local)
+ continue;
+ space = isl_set_get_space(prog->array[i].extent);
+ set = isl_union_set_extract_set(arrays, space);
+ empty = isl_set_plain_is_empty(set);
+ isl_set_free(set);
+ if (empty < 0)
+ goto error;
+ if (!empty)
+ prog->array[i].declare_local = 1;
+ }
+
+ isl_union_set_free(arrays);
+ return node;
+error:
+ isl_union_set_free(arrays);
+ return isl_schedule_node_free(node);
+}
+
+/* If "node" points to a set node, then separate its children
+ * into subtrees that have suitably permutable bands and
+ * those that do not.
+ * Adjust the schedule tree in order to execute the second group
+ * after the first group and return a pointer to the first group,
+ * assuming there are any such subtrees.
+ * If "node" points to a sequence node, then separate the initial
+ * children that do not have suitably permutable bands and
+ * return a pointer to the subsequence of children that do have such bands,
+ * assuming there are any such subtrees.
+ *
+ * In both cases, mark all local variables in "prog" that are accessed by
+ * the group without permutable bands as requiring a declaration on the host.
+ */
+static __isl_give isl_schedule_node *isolate_permutable_subtrees(
+ __isl_take isl_schedule_node *node, struct gpu_prog *prog)
+{
+ isl_union_set *filter;
+ enum isl_schedule_node_type type;
+
+ if (!node)
+ return NULL;
+ type = isl_schedule_node_get_type(node);
+ if (type == isl_schedule_node_set) {
+ filter = get_all_non_parallel_subtree_filters(node);
+ node = declare_accessed_local_variables(node, prog, filter);
+ node = isl_schedule_node_order_after(node, filter);
+ } else if (type == isl_schedule_node_sequence) {
+ filter = get_initial_non_parallel_subtree_filters(node);
+ node = declare_accessed_local_variables(node, prog, filter);
+ node = isl_schedule_node_order_before(node, filter);
+ }
+
+ return node;
+}
+
+/* Replace any reference to an array element in the range of "copy"
+ * by a reference to all array elements (defined by the extent of the array).
+ */
+static __isl_give isl_union_map *approximate_copy_out(
+ __isl_take isl_union_map *copy, struct gpu_prog *prog)
+{
+ int i;
+ isl_union_map *res;
+
+ res = isl_union_map_empty(isl_union_map_get_space(copy));
+
+ for (i = 0; i < prog->n_array; ++i) {
+ isl_space *space;
+ isl_set *set;
+ isl_union_map *copy_i;
+ isl_union_set *extent, *domain;
+
+ space = isl_space_copy(prog->array[i].space);
+ extent = isl_union_set_from_set(isl_set_universe(space));
+ copy_i = isl_union_map_copy(copy);
+ copy_i = isl_union_map_intersect_range(copy_i, extent);
+ set = isl_set_copy(prog->array[i].extent);
+ extent = isl_union_set_from_set(set);
+ domain = isl_union_map_domain(copy_i);
+ copy_i = isl_union_map_from_domain_and_range(domain, extent);
+ res = isl_union_map_union(res, copy_i);
+ }
+
+ isl_union_map_free(copy);
+
+ return res;
+}
+
+/* Insert "kernel" marks that point to a ppcg_kernel structure
+ * in front of all outermost tilable band that (by construction)
+ * have at least one parallel loop.
+ */
+static __isl_give isl_schedule_node *mark_kernels(struct gpu_gen *gen,
+ __isl_take isl_schedule_node *node)
+{
+ return isl_schedule_node_map_descendant_bottom_up(node,
+ &mark_outer_permutable, gen);
+}
+
+/* Construct schedule constraints from the dependences in prog->scop and
+ * the array order dependences in prog->array_order.
+ *
+ * If live range reordering is allowed, then we need to make sure
+ * that live ranges on arrays are not run in parallel since doing
+ * so would require array expansion. We therefore add the array
+ * order dependences to the coincidence dependences. Non-zero array
+ * order dependences will then prevent a schedule dimension from being
+ * considered parallel.
+ * Live ranges derived from scalars are allowed to be run in parallel
+ * since we force the scalars to be mapped to private memory in
+ * check_scalar_live_ranges.
+ * If live range reordering is allowed, then the false dependences
+ * are not added to the validity constraints as that would prevent
+ * reordering. Instead, the external false dependences that enforce that reads
+ * from potentially live-in data precede any later write and
+ * that writes of potentially live-out data follow any other earlier write
+ * are added to the validity and the coincidence constraints.
+ * The false dependences are still added to the proximity constraints
+ * for consistency with the case where live range reordering is not allowed.
+ * The coincidence constraints then consist of flow dependences,
+ * external false dependences and array order dependences.
+ * The independences can be filtered out from the first two sets.
+ * They have already been filtered out from the array order dependences
+ * on a per array basis in collect_order_dependences.
+ * There is no need for a per array handling of the other two sets
+ * as there should be no flow or external false dependence on local
+ * variables that can be filtered out.
+ */
+static __isl_give isl_schedule_constraints *construct_schedule_constraints(
+ struct gpu_prog *prog)
+{
+ isl_union_set *domain;
+ isl_union_map *dep_raw, *dep;
+ isl_union_map *validity, *proximity, *coincidence;
+ isl_schedule_constraints *sc;
+
+ domain = isl_union_set_copy(prog->scop->domain);
+ sc = isl_schedule_constraints_on_domain(domain);
+ sc = isl_schedule_constraints_set_context(sc,
+ isl_set_copy(prog->scop->context));
+ if (prog->scop->options->live_range_reordering) {
+ sc = isl_schedule_constraints_set_conditional_validity(sc,
+ isl_union_map_copy(prog->scop->tagged_dep_flow),
+ isl_union_map_copy(prog->scop->tagged_dep_order));
+ proximity = isl_union_map_copy(prog->scop->dep_flow);
+ validity = isl_union_map_copy(proximity);
+ validity = isl_union_map_union(validity,
+ isl_union_map_copy(prog->scop->dep_forced));
+ proximity = isl_union_map_union(proximity,
+ isl_union_map_copy(prog->scop->dep_false));
+ coincidence = isl_union_map_copy(validity);
+ coincidence = isl_union_map_subtract(coincidence,
+ isl_union_map_copy(prog->scop->independence));
+ coincidence = isl_union_map_union(coincidence,
+ isl_union_map_copy(prog->array_order));
+ } else {
+ dep_raw = isl_union_map_copy(prog->scop->dep_flow);
+ dep = isl_union_map_copy(prog->scop->dep_false);
+ dep = isl_union_map_union(dep, dep_raw);
+ dep = isl_union_map_coalesce(dep);
+ proximity = isl_union_map_copy(dep);
+ coincidence = isl_union_map_copy(dep);
+ validity = dep;
+ }
+ sc = isl_schedule_constraints_set_validity(sc, validity);
+ sc = isl_schedule_constraints_set_coincidence(sc, coincidence);
+ sc = isl_schedule_constraints_set_proximity(sc, proximity);
+
+ if (prog->scop->options->debug->dump_schedule_constraints)
+ isl_schedule_constraints_dump(sc);
+ return sc;
+}
+
+/* Compute an appropriate schedule based on the accesses in
+ * gen->read and gen->write.
+ *
+ * We derive schedule constraints from the dependences in gen->prog->scop
+ * and then use isl to compute a schedule that has a parallel loop
+ * in each tilable band.
+ * During the schedule construction, some statement instances
+ * may be grouped first based on the input schedule.
+ */
+static __isl_give isl_schedule *compute_schedule(struct gpu_gen *gen)
+{
+ isl_schedule_constraints *sc;
+ isl_schedule *schedule;
+
+ sc = construct_schedule_constraints(gen->prog);
+ schedule = gen->prog->scop->schedule;
+ schedule = ppcg_compute_schedule(sc, schedule, gen->options);
+
+ return schedule;
+}
+
+/* If the band node "node" has exactly one member then mark it permutable.
+ */
+static __isl_give isl_schedule_node *band_set_permutable(
+ __isl_take isl_schedule_node *node,
+ __isl_keep isl_schedule_constraints *sc)
+{
+ if (isl_schedule_node_band_n_member(node) == 1)
+ node = isl_schedule_node_band_set_permutable(node, 1);
+
+ return node;
+}
+
+/* Return the coincidence constraints between pairs of instances
+ * that are scheduled together by the ancestors of "node".
+ * That is, select those coincidence constraints that relate
+ * pairs of instances that have the same value for the prefix schedule.
+ * If the schedule depth is zero, then the prefix schedule does not
+ * contain any information, so we intersect domain and range
+ * of the schedule constraints with the reaching domain elements instead.
+ */
+static __isl_give isl_union_map *get_local_coincidence(
+ __isl_keep isl_schedule_node *node,
+ __isl_keep isl_schedule_constraints *sc)
+{
+ isl_union_map *coincidence;
+ isl_multi_union_pw_aff *prefix;
+ isl_union_pw_multi_aff *contraction;
+
+ coincidence = isl_schedule_constraints_get_coincidence(sc);
+ contraction = isl_schedule_node_get_subtree_contraction(node);
+ if (isl_schedule_node_get_schedule_depth(node) == 0) {
+ isl_union_set *domain;
+
+ domain = isl_schedule_node_get_domain(node);
+ domain = isl_union_set_preimage_union_pw_multi_aff(domain,
+ contraction);
+ coincidence = isl_union_map_intersect_domain(coincidence,
+ isl_union_set_copy(domain));
+ coincidence = isl_union_map_intersect_range(coincidence,
+ domain);
+ return coincidence;
+ }
+
+ prefix = isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
+ prefix = isl_multi_union_pw_aff_pullback_union_pw_multi_aff(prefix,
+ contraction);
+ return isl_union_map_eq_at_multi_union_pw_aff(coincidence, prefix);
+}
+
+/* For each member in the band node "node", determine whether
+ * it is coincident with respect to the outer nodes and mark
+ * it accordingly.
+ *
+ * That is, for each coincidence constraint between pairs
+ * of instances that are scheduled together by the outer nodes,
+ * check that domain and range are assigned the same value
+ * by the band member. This test is performed by checking
+ * that imposing the same value for the band member does not
+ * remove any elements from the set of coincidence constraints.
+ */
+static __isl_give isl_schedule_node *band_set_coincident(
+ __isl_take isl_schedule_node *node,
+ __isl_keep isl_schedule_constraints *sc)
+{
+ isl_union_map *coincidence;
+ isl_union_pw_multi_aff *contraction;
+ isl_multi_union_pw_aff *partial;
+ int i, n;
+
+ coincidence = get_local_coincidence(node, sc);
+
+ partial = isl_schedule_node_band_get_partial_schedule(node);
+ contraction = isl_schedule_node_get_subtree_contraction(node);
+ partial = isl_multi_union_pw_aff_pullback_union_pw_multi_aff(partial,
+ contraction);
+ n = isl_schedule_node_band_n_member(node);
+ for (i = 0; i < n; ++i) {
+ isl_union_map *coincidence_i;
+ isl_union_pw_aff *upa;
+ isl_multi_union_pw_aff *partial_i;
+ int subset;
+
+ upa = isl_multi_union_pw_aff_get_union_pw_aff(partial, i);
+ partial_i = isl_multi_union_pw_aff_from_union_pw_aff(upa);
+ coincidence_i = isl_union_map_copy(coincidence);
+ coincidence_i = isl_union_map_eq_at_multi_union_pw_aff(
+ coincidence_i, partial_i);
+ subset = isl_union_map_is_subset(coincidence, coincidence_i);
+ isl_union_map_free(coincidence_i);
+
+ if (subset < 0)
+ break;
+ node = isl_schedule_node_band_member_set_coincident(node, i,
+ subset);
+ }
+ if (i < n)
+ node = isl_schedule_node_free(node);
+ isl_multi_union_pw_aff_free(partial);
+ isl_union_map_free(coincidence);
+
+ return node;
+}
+
+/* If "node" is a band, then set its properties.
+ *
+ * In particular, if the band has exactly one member, then mark it permutable.
+ * Mark the band member coincident based on the coincidence constraints
+ * of "sc".
+ */
+static __isl_give isl_schedule_node *set_band_properties(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ isl_schedule_constraints *sc = user;
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_band)
+ return node;
+ if (isl_schedule_node_band_n_member(node) == 0)
+ return node;
+
+ node = band_set_permutable(node, sc);
+ node = band_set_coincident(node, sc);
+
+ return node;
+}
+
+/* Return the original schedule with all bands marked permutable and
+ * all band members marked coincident based on the coincidence constraints.
+ * The bands are explicitly marked permutable so that they will be considered
+ * by mark_outer_permutable.
+ */
+static __isl_give isl_schedule *determine_properties_original_schedule(
+ struct gpu_gen *gen)
+{
+ isl_schedule *schedule;
+ isl_schedule_constraints *sc;
+
+ schedule = isl_schedule_copy(gen->prog->scop->schedule);
+ sc = construct_schedule_constraints(gen->prog);
+ schedule = isl_schedule_map_schedule_node_bottom_up(schedule,
+ &set_band_properties, sc);
+ isl_schedule_constraints_free(sc);
+
+ return schedule;
+}
+
+/* Compute a schedule or determine the properties of the original schedule
+ * depending on the value of the "reschedule" option.
+ */
+static __isl_give isl_schedule *compute_or_set_properties(void *user)
+{
+ struct gpu_gen *gen = user;
+
+ if (gen->options->reschedule)
+ return compute_schedule(gen);
+ else
+ return determine_properties_original_schedule(gen);
+}
+
+/* Obtain a schedule for the scop, by reading it from
+ * a file, by computing one or by determining the properties
+ * of the original schedule.
+ */
+__isl_give isl_schedule *get_schedule(struct gpu_gen *gen)
+{
+ return ppcg_get_schedule(gen->ctx, gen->options,
+ &compute_or_set_properties, gen);
+}
+
+/* Construct the string "<a>_<b>".
+ */
+static char *concat(isl_ctx *ctx, const char *a, const char *b)
+{
+ isl_printer *p;
+ char *s;
+
+ p = isl_printer_to_str(ctx);
+ p = isl_printer_print_str(p, a);
+ p = isl_printer_print_str(p, "_");
+ p = isl_printer_print_str(p, b);
+ s = isl_printer_get_str(p);
+ isl_printer_free(p);
+
+ return s;
+}
+
+/* For each array in "prog" of which an element appears in "accessed" and
+ * that is not a read only scalar, create a zero-dimensional universe set
+ * of which the tuple id has name "<prefix>_<name of array>" and a user
+ * pointer pointing to the array (gpu_array_info).
+ *
+ * If the array is local to "prog", then make sure it will be declared
+ * in the host code.
+ *
+ * Return the list of these universe sets.
+ */
+static __isl_give isl_union_set_list *create_copy_filters(struct gpu_prog *prog,
+ const char *prefix, __isl_take isl_union_set *accessed)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_union_set_list *filters;
+
+ ctx = prog->ctx;
+ filters = isl_union_set_list_alloc(ctx, 0);
+ for (i = 0; i < prog->n_array; ++i) {
+ struct gpu_array_info *array = &prog->array[i];
+ isl_space *space;
+ isl_set *accessed_i;
+ int empty;
+ char *name;
+ isl_id *id;
+ isl_union_set *uset;
+
+ if (gpu_array_is_read_only_scalar(array))
+ continue;
+
+ space = isl_space_copy(array->space);
+ accessed_i = isl_union_set_extract_set(accessed, space);
+ empty = isl_set_plain_is_empty(accessed_i);
+ isl_set_free(accessed_i);
+ if (empty < 0) {
+ filters = isl_union_set_list_free(filters);
+ break;
+ }
+ if (empty)
+ continue;
+
+ array->global = 1;
+ if (array->local)
+ array->declare_local = 1;
+
+ name = concat(ctx, prefix, array->name);
+ id = name ? isl_id_alloc(ctx, name, array) : NULL;
+ free(name);
+ space = isl_space_set_alloc(ctx, 0, 0);
+ space = isl_space_set_tuple_id(space, isl_dim_set, id);
+ uset = isl_union_set_from_set(isl_set_universe(space));
+
+ filters = isl_union_set_list_add(filters, uset);
+ }
+ isl_union_set_free(accessed);
+
+ return filters;
+}
+
+/* Make sure that code for the statements in "filters" that
+ * copy arrays to or from the device is only generated when
+ * the size of the corresponding array is positive.
+ * That is, add a set node underneath "graft" with "filters" as children
+ * and for each child add a guard that the selects the parameter
+ * values for which the corresponding array has a positive size.
+ * The array is available in the user pointer of the statement identifier.
+ * "depth" is the schedule depth of the position where "graft"
+ * will be added.
+ */
+static __isl_give isl_schedule_node *insert_positive_size_guards(
+ __isl_take isl_schedule_node *graft,
+ __isl_take isl_union_set_list *filters, int depth)
+{
+ int i, n;
+
+ graft = isl_schedule_node_child(graft, 0);
+ graft = isl_schedule_node_insert_set(graft, filters);
+ n = isl_schedule_node_n_children(graft);
+ for (i = 0; i < n; ++i) {
+ isl_union_set *filter;
+ isl_set *domain, *guard;
+ isl_id *id;
+ struct gpu_array_info *array;
+
+ graft = isl_schedule_node_child(graft, i);
+ filter = isl_schedule_node_filter_get_filter(graft);
+ domain = isl_set_from_union_set(filter);
+ id = isl_set_get_tuple_id(domain);
+ array = isl_id_get_user(id);
+ isl_id_free(id);
+ isl_set_free(domain);
+ guard = gpu_array_positive_size_guard(array);
+ guard = isl_set_from_params(guard);
+ guard = isl_set_add_dims(guard, isl_dim_set, depth);
+ graft = isl_schedule_node_child(graft, 0);
+ graft = isl_schedule_node_insert_guard(graft, guard);
+ graft = isl_schedule_node_parent(graft);
+ graft = isl_schedule_node_parent(graft);
+ }
+ graft = isl_schedule_node_parent(graft);
+
+ return graft;
+}
+
+/* Create a graft for copying arrays to or from the device,
+ * whenever the size of the array is strictly positive.
+ * Each statement is called "<prefix>_<name of array>" and
+ * the identifier has a user pointer pointing to the array.
+ * The graft will be added at the position specified by "node".
+ * "copy" contains the array elements that need to be copied.
+ * Only arrays of which some elements need to be copied
+ * will have a corresponding statement in the graph.
+ * Note though that each such statement will copy the entire array.
+ */
+static __isl_give isl_schedule_node *create_copy_device(struct gpu_prog *prog,
+ __isl_keep isl_schedule_node *node, const char *prefix,
+ __isl_take isl_union_set *copy)
+{
+ int depth;
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_union_set *all, *domain;
+ isl_union_set_list *filters;
+ isl_union_map *extension;
+ isl_schedule_node *graft;
+
+ ctx = prog->ctx;
+ depth = isl_schedule_node_get_schedule_depth(node);
+ filters = create_copy_filters(prog, prefix, copy);
+ all = isl_union_set_list_union(isl_union_set_list_copy(filters));
+
+ space = depth < 0 ? NULL : isl_space_set_alloc(ctx, 0, depth);
+ domain = isl_union_set_from_set(isl_set_universe(space));
+ extension = isl_union_map_from_domain_and_range(domain, all);
+ graft = isl_schedule_node_from_extension(extension);
+
+ if (!filters)
+ return isl_schedule_node_free(graft);
+ if (isl_union_set_list_n_union_set(filters) == 0) {
+ isl_union_set_list_free(filters);
+ return graft;
+ }
+
+ return insert_positive_size_guards(graft, filters, depth);
+}
+
+/* Return (the universe spaces of) the arrays that are declared
+ * inside the scop corresponding to "prog" and for which all
+ * potential writes inside the scop form a subset of "domain".
+ */
+static __isl_give isl_union_set *extract_local_accesses(struct gpu_prog *prog,
+ __isl_keep isl_union_set *domain)
+{
+ int i;
+ isl_union_set *local;
+
+ local = isl_union_set_empty(isl_union_set_get_space(domain));
+
+ for (i = 0; i < prog->n_array; ++i) {
+ isl_set *set;
+ isl_union_map *to_outer;
+ isl_union_map *may_write;
+ isl_union_set *write_domain;
+ isl_union_set *fields;
+ int subset;
+
+ if (!prog->array[i].local)
+ continue;
+
+ set = isl_set_universe(isl_space_copy(prog->array[i].space));
+ to_outer = isl_union_map_copy(prog->to_outer);
+ to_outer = isl_union_map_intersect_range(to_outer,
+ isl_union_set_from_set(isl_set_copy(set)));
+ fields = isl_union_map_domain(to_outer);
+ may_write = isl_union_map_copy(prog->may_write);
+ may_write = isl_union_map_intersect_range(may_write, fields);
+ write_domain = isl_union_map_domain(may_write);
+ subset = isl_union_set_is_subset(write_domain, domain);
+ isl_union_set_free(write_domain);
+
+ if (subset < 0) {
+ isl_set_free(set);
+ return isl_union_set_free(local);
+ } else if (subset) {
+ local = isl_union_set_add_set(local, set);
+ } else {
+ isl_set_free(set);
+ }
+ }
+
+ return local;
+}
+
+/* Internal data structure for node_may_persist.
+ *
+ * "tagger" maps tagged iteration domains to the corresponding untagged
+ * iteration domain.
+ *
+ * "may_persist_flow" is the set of all tagged dataflow dependences
+ * with those dependences removed that either precede or follow
+ * the kernel launch in a sequence.
+ * "inner_band_flow" is the set of all tagged dataflow dependences
+ * that are local to a given iteration of the outer band nodes
+ * with respect to the current node.
+ * "local_flow" is equal to "inner_band_flow", except that the domain
+ * and the range have been intersected with intermediate filters
+ * on children of sets or sequences.
+ */
+struct ppcg_may_persist_data {
+ isl_union_pw_multi_aff *tagger;
+
+ isl_union_map *local_flow;
+ isl_union_map *inner_band_flow;
+ isl_union_map *may_persist_flow;
+};
+
+/* Update the information in "data" based on the band ancestor "node".
+ *
+ * In particular, we restrict the dependences in data->local_flow
+ * to those dependence where the source and the sink occur in
+ * the same iteration of the given band node.
+ * We also update data->inner_band_flow to the new value of
+ * data->local_flow.
+ */
+static int update_may_persist_at_band(__isl_keep isl_schedule_node *node,
+ struct ppcg_may_persist_data *data)
+{
+ isl_multi_union_pw_aff *partial;
+ isl_union_pw_multi_aff *contraction;
+ isl_union_map *flow;
+
+ if (isl_schedule_node_band_n_member(node) == 0)
+ return 0;
+
+ partial = isl_schedule_node_band_get_partial_schedule(node);
+ contraction = isl_schedule_node_get_subtree_contraction(node);
+ partial = isl_multi_union_pw_aff_pullback_union_pw_multi_aff(partial,
+ contraction);
+ partial = isl_multi_union_pw_aff_pullback_union_pw_multi_aff(partial,
+ isl_union_pw_multi_aff_copy(data->tagger));
+
+ flow = data->local_flow;
+ flow = isl_union_map_eq_at_multi_union_pw_aff(flow, partial);
+ data->local_flow = flow;
+
+ isl_union_map_free(data->inner_band_flow);
+ data->inner_band_flow = isl_union_map_copy(data->local_flow);
+
+ return 0;
+}
+
+/* Given a set of local reaching domain elements "domain",
+ * expand them to the corresponding leaf domain elements using "contraction"
+ * and insert the array references tags using data->tagger.
+ */
+static __isl_give isl_union_set *expand_and_tag(
+ __isl_take isl_union_set *domain,
+ __isl_take isl_union_pw_multi_aff *contraction,
+ struct ppcg_may_persist_data *data)
+{
+ domain = isl_union_set_preimage_union_pw_multi_aff(domain,
+ contraction);
+ domain = isl_union_set_preimage_union_pw_multi_aff(domain,
+ isl_union_pw_multi_aff_copy(data->tagger));
+ return domain;
+}
+
+/* Given a filter node that is the child of a set or sequence node,
+ * restrict data->local_flow to refer only to those elements
+ * in the filter of the node.
+ * "contraction" maps the leaf domain elements of the schedule tree
+ * to the corresponding domain elements at (the parent of) "node".
+ */
+static int filter_flow(__isl_keep isl_schedule_node *node,
+ struct ppcg_may_persist_data *data,
+ __isl_take isl_union_pw_multi_aff *contraction)
+{
+ isl_union_set *filter;
+ isl_union_map *flow;
+
+ flow = data->local_flow;
+ filter = isl_schedule_node_filter_get_filter(node);
+ filter = expand_and_tag(filter, contraction, data);
+ flow = isl_union_map_intersect_domain(flow, isl_union_set_copy(filter));
+ flow = isl_union_map_intersect_range(flow, filter);
+ data->local_flow = flow;
+
+ return 0;
+}
+
+/* Given a filter node "node", collect the filters on all preceding siblings
+ * (which are also filter nodes), add them to "filters" and return the result.
+ */
+static __isl_give isl_union_set *add_previous_filters(
+ __isl_take isl_union_set *filters, __isl_keep isl_schedule_node *node)
+{
+ isl_schedule_node *sibling;
+
+ sibling = isl_schedule_node_copy(node);
+ while (sibling && isl_schedule_node_has_previous_sibling(sibling)) {
+ isl_union_set *filter;
+
+ sibling = isl_schedule_node_previous_sibling(sibling);
+ filter = isl_schedule_node_filter_get_filter(sibling);
+ filters = isl_union_set_union(filters, filter);
+ }
+ isl_schedule_node_free(sibling);
+ if (!sibling)
+ return isl_union_set_free(filters);
+
+ return filters;
+}
+
+/* Given a filter node "node", collect the filters on all following siblings
+ * (which are also filter nodes), add them to "filters" and return the result.
+ */
+static __isl_give isl_union_set *add_next_filters(
+ __isl_take isl_union_set *filters, __isl_keep isl_schedule_node *node)
+{
+ isl_schedule_node *sibling;
+
+ sibling = isl_schedule_node_copy(node);
+ while (sibling && isl_schedule_node_has_next_sibling(sibling)) {
+ isl_union_set *filter;
+
+ sibling = isl_schedule_node_next_sibling(sibling);
+ filter = isl_schedule_node_filter_get_filter(sibling);
+ filters = isl_union_set_union(filters, filter);
+ }
+ isl_schedule_node_free(sibling);
+ if (!sibling)
+ return isl_union_set_free(filters);
+
+ return filters;
+}
+
+/* Remove those flow dependences from data->may_persist_flow
+ * that flow between elements of "domain" within the same iteration
+ * of all outer band nodes.
+ * "contraction" maps the leaf domain elements of the schedule tree
+ * to the corresponding elements "domain".
+ */
+static void remove_external_flow(struct ppcg_may_persist_data *data,
+ __isl_take isl_union_set *domain,
+ __isl_keep isl_union_pw_multi_aff *contraction)
+{
+ isl_union_map *flow;
+
+ contraction = isl_union_pw_multi_aff_copy(contraction);
+ domain = expand_and_tag(domain, contraction, data);
+ flow = isl_union_map_copy(data->local_flow);
+ flow = isl_union_map_intersect_domain(flow, isl_union_set_copy(domain));
+ flow = isl_union_map_intersect_range(flow, domain);
+
+ data->may_persist_flow = isl_union_map_subtract(data->may_persist_flow,
+ flow);
+}
+
+/* Update the information in "data" based on the filter ancestor "node".
+ * We only need to modify anything if the filter is the child
+ * of a set or sequence node.
+ *
+ * In the case of a sequence, we remove the dependences between
+ * statement instances that are both executed either before or
+ * after the subtree that will be mapped to a kernel, within
+ * the same iteration of outer bands.
+ *
+ * In both cases, we restrict data->local_flow to the current child.
+ */
+static int update_may_persist_at_filter(__isl_keep isl_schedule_node *node,
+ struct ppcg_may_persist_data *data)
+{
+ enum isl_schedule_node_type type;
+ isl_schedule_node *parent;
+ isl_space *space;
+ isl_union_pw_multi_aff *contraction;
+ isl_union_set *before, *after, *filter;
+
+ type = isl_schedule_node_get_parent_type(node);
+ if (type != isl_schedule_node_sequence && type != isl_schedule_node_set)
+ return 0;
+
+ parent = isl_schedule_node_copy(node);
+ parent = isl_schedule_node_parent(parent);
+ contraction = isl_schedule_node_get_subtree_contraction(parent);
+ isl_schedule_node_free(parent);
+
+ if (type == isl_schedule_node_set)
+ return filter_flow(node, data, contraction);
+
+ filter = isl_schedule_node_filter_get_filter(node);
+ space = isl_union_set_get_space(filter);
+ isl_union_set_free(filter);
+ before = isl_union_set_empty(space);
+ after = isl_union_set_copy(before);
+ before = add_previous_filters(before, node);
+ after = add_next_filters(after, node);
+
+ remove_external_flow(data, before, contraction);
+ remove_external_flow(data, after, contraction);
+
+ return filter_flow(node, data, contraction);
+}
+
+/* Update the information in "data" based on the ancestor "node".
+ */
+static isl_stat update_may_persist_at(__isl_keep isl_schedule_node *node,
+ void *user)
+{
+ struct ppcg_may_persist_data *data = user;
+
+ switch (isl_schedule_node_get_type(node)) {
+ case isl_schedule_node_error:
+ return isl_stat_error;
+ case isl_schedule_node_context:
+ case isl_schedule_node_domain:
+ case isl_schedule_node_expansion:
+ case isl_schedule_node_extension:
+ case isl_schedule_node_guard:
+ case isl_schedule_node_leaf:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ break;
+ case isl_schedule_node_band:
+ if (update_may_persist_at_band(node, data) < 0)
+ return isl_stat_error;
+ break;
+ case isl_schedule_node_filter:
+ if (update_may_persist_at_filter(node, data) < 0)
+ return isl_stat_error;
+ break;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Determine the set of array elements that may need to be perserved
+ * by a kernel constructed from the subtree at "node".
+ * This includes the set of array elements that may need to be preserved
+ * by the entire scop (prog->may_persist) and the elements for which
+ * there is a potential flow dependence that may cross a kernel launch.
+ *
+ * To determine the second set, we start from all flow dependences.
+ * From this set of dependences, we remove those that cannot possibly
+ * require data to be preserved by a kernel launch.
+ * In particular, we consider the following sets of dependences.
+ * - dependences of which the write occurs inside the kernel.
+ * If the data is needed outside the kernel, then it will
+ * be copied out immediately after the kernel launch, so there
+ * is no need for any special care.
+ * - dependences of which the read occurs inside the kernel and the
+ * corresponding write occurs inside the same iteration of the
+ * outer band nodes. This means that the data is needed in
+ * the first kernel launch after the write, which is already
+ * taken care of by the standard copy-in. That is, the data
+ * do not need to be preserved by any intermediate call to
+ * the same kernel.
+ * - dependences of which the write and the read either both occur
+ * before the kernel launch or both occur after the kernel launch,
+ * within the same iteration of the outer band nodes with respect
+ * to the sequence that determines the ordering of the dependence
+ * and the kernel launch. Such flow dependences cannot cross
+ * any kernel launch.
+ *
+ * For the remaining (tagged) dependences, we take the domain
+ * (i.e., the tagged writes) and apply the tagged access relation
+ * to obtain the accessed data elements.
+ * These are then combined with the elements that may need to be
+ * preserved by the entire scop.
+ */
+static __isl_give isl_union_set *node_may_persist(
+ __isl_keep isl_schedule_node *node, struct gpu_prog *prog)
+{
+ struct ppcg_may_persist_data data;
+ isl_union_pw_multi_aff *contraction;
+ isl_union_set *domain;
+ isl_union_set *persist;
+ isl_union_map *flow, *local_flow;
+
+ data.tagger = prog->scop->tagger;
+
+ flow = isl_union_map_copy(prog->scop->tagged_dep_flow);
+ data.local_flow = isl_union_map_copy(flow);
+ data.inner_band_flow = isl_union_map_copy(flow);
+ data.may_persist_flow = flow;
+ if (isl_schedule_node_foreach_ancestor_top_down(node,
+ &update_may_persist_at, &data) < 0)
+ data.may_persist_flow =
+ isl_union_map_free(data.may_persist_flow);
+ flow = data.may_persist_flow;
+ isl_union_map_free(data.local_flow);
+
+ domain = isl_schedule_node_get_domain(node);
+ contraction = isl_schedule_node_get_subtree_contraction(node);
+ domain = isl_union_set_preimage_union_pw_multi_aff(domain,
+ contraction);
+ domain = isl_union_set_preimage_union_pw_multi_aff(domain,
+ isl_union_pw_multi_aff_copy(data.tagger));
+ flow = isl_union_map_subtract_domain(flow, isl_union_set_copy(domain));
+ local_flow = data.inner_band_flow;
+ local_flow = isl_union_map_intersect_range(local_flow, domain);
+ flow = isl_union_map_subtract(flow, local_flow);
+
+ persist = isl_union_map_domain(flow);
+ persist = isl_union_set_apply(persist,
+ isl_union_map_copy(prog->scop->tagged_may_writes));
+ persist = isl_union_set_union(persist,
+ isl_union_set_copy(prog->may_persist));
+
+ return persist;
+}
+
+/* Add nodes for copying outer arrays in and out of the device
+ * before and after the subtree "node", which contains one or more kernels.
+ * "domain" contains the original statement instances, i.e.,
+ * those that correspond to the domains of the access relations in "prog".
+ * In particular, the domain has not been contracted in any way.
+ * "prefix" contains the prefix schedule at that point, in terms
+ * of the same original statement instances.
+ *
+ * We first compute the sets of outer array elements that need
+ * to be copied in and out and then graft in the nodes for
+ * performing this copying.
+ *
+ * In particular, for each array that is possibly written anywhere in
+ * the subtree "node" and that may be used after "node"
+ * or that may be visible outside the corresponding scop,
+ * we copy out its entire extent.
+ *
+ * Any array elements that is read without first being written inside
+ * the subtree "node" needs to be copied in.
+ * Furthermore, if there are any array elements that
+ * are copied out, but that may not be written inside "node, then
+ * they also need to be copied in to ensure that the value after execution
+ * is the same as the value before execution, at least for those array
+ * elements that may have their values preserved by the scop or that
+ * may be written before "node" and read after "node".
+ * In case the array elements are structures, we need to take into
+ * account that all members of the structures need to be written
+ * by "node" before we can avoid copying the data structure in.
+ *
+ * Note that the may_write relation is intersected with the domain,
+ * which has been intersected with the context.
+ * This helps in those cases where the arrays are declared with a fixed size,
+ * while the accesses are parametric and the context assigns a fixed value
+ * to the parameters.
+ *
+ * If an element from a local array is read without first being written,
+ * then there is no point in copying it in since it cannot have been
+ * written prior to the scop. Warn about the uninitialized read instead.
+ */
+static __isl_give isl_schedule_node *add_to_from_device(
+ __isl_take isl_schedule_node *node, __isl_take isl_union_set *domain,
+ __isl_take isl_union_map *prefix, struct gpu_prog *prog)
+{
+ isl_union_set *local;
+ isl_union_set *may_persist;
+ isl_union_map *may_write, *must_write, *copy_out, *not_written;
+ isl_union_map *read, *copy_in;
+ isl_union_map *tagged;
+ isl_union_map *local_uninitialized;
+ isl_schedule_node *graft;
+
+ tagged = isl_union_map_copy(prog->scop->tagged_reads);
+ tagged = isl_union_map_union(tagged,
+ isl_union_map_copy(prog->scop->tagged_may_writes));
+
+ may_write = isl_union_map_copy(prog->may_write);
+ may_write = isl_union_map_intersect_domain(may_write,
+ isl_union_set_copy(domain));
+ may_write = remove_local_accesses(prog,
+ isl_union_map_copy(tagged), may_write,
+ isl_union_map_copy(prefix), 0);
+ may_write = isl_union_map_apply_range(may_write,
+ isl_union_map_copy(prog->to_outer));
+ may_write = isl_union_map_apply_domain(may_write,
+ isl_union_map_copy(prefix));
+ may_write = approximate_copy_out(may_write, prog);
+ copy_out = isl_union_map_copy(may_write);
+ may_write = isl_union_map_apply_range(may_write,
+ isl_union_map_copy(prog->to_inner));
+ must_write = isl_union_map_copy(prog->must_write);
+ must_write = isl_union_map_apply_domain(must_write,
+ isl_union_map_copy(prefix));
+ may_persist = node_may_persist(node, prog);
+ may_write = isl_union_map_intersect_range(may_write, may_persist);
+ not_written = isl_union_map_subtract(may_write, must_write);
+
+ local = extract_local_accesses(prog, domain);
+ read = isl_union_map_copy(prog->read);
+ read = isl_union_map_intersect_domain(read, domain);
+ read = remove_local_accesses(prog, tagged, read,
+ isl_union_map_copy(prefix), 1);
+ local = isl_union_set_apply(local, isl_union_map_copy(prog->to_inner));
+ local_uninitialized = isl_union_map_copy(prog->scop->live_in);
+ local_uninitialized = isl_union_map_intersect_range(local_uninitialized,
+ local);
+ local_uninitialized = isl_union_map_intersect(local_uninitialized,
+ isl_union_map_copy(read));
+ if (!isl_union_map_is_empty(local_uninitialized)) {
+ fprintf(stderr,
+ "possibly uninitialized reads (not copied in):\n");
+ isl_union_map_dump(local_uninitialized);
+ }
+ read = isl_union_map_subtract(read, local_uninitialized);
+ read = isl_union_map_apply_domain(read, prefix);
+ copy_in = isl_union_map_union(read, not_written);
+ copy_in = isl_union_map_apply_range(copy_in,
+ isl_union_map_copy(prog->to_outer));
+
+ graft = create_copy_device(prog, node, "to_device",
+ isl_union_map_range(copy_in));
+ node = isl_schedule_node_graft_before(node, graft);
+ graft = create_copy_device(prog, node, "from_device",
+ isl_union_map_range(copy_out));
+ node = isl_schedule_node_graft_after(node, graft);
+
+ return node;
+}
+
+/* Add nodes for initializing ("init_device") and clearing ("clear_device")
+ * the device before and after "node".
+ */
+static __isl_give isl_schedule_node *add_init_clear_device(
+ __isl_take isl_schedule_node *node)
+{
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_union_set *domain;
+ isl_schedule_node *graft;
+
+ ctx = isl_schedule_node_get_ctx(node);
+
+ space = isl_space_set_alloc(ctx, 0, 0);
+ space = isl_space_set_tuple_name(space, isl_dim_set, "init_device");
+ domain = isl_union_set_from_set(isl_set_universe(space));
+ graft = isl_schedule_node_from_domain(domain);
+
+ node = isl_schedule_node_graft_before(node, graft);
+
+ space = isl_space_set_alloc(ctx, 0, 0);
+ space = isl_space_set_tuple_name(space, isl_dim_set, "clear_device");
+ domain = isl_union_set_from_set(isl_set_universe(space));
+ graft = isl_schedule_node_from_domain(domain);
+
+ node = isl_schedule_node_graft_after(node, graft);
+
+ return node;
+}
+
+/* Update "schedule" for mapping to a GPU device.
+ *
+ * In particular, insert a context node, create kernels for
+ * each outermost tilable band and introduce nodes for copying arrays
+ * in and out of the device and for initializing and clearing the device.
+ * If the child of the initial root points to a set node,
+ * then children of this node that do not contain any tilable bands
+ * are separated from the other children and are not mapped to
+ * the device.
+ *
+ * The GPU code is generated in a context where at least one
+ * statement instance is executed. The corresponding guard is inserted
+ * around the entire schedule.
+ */
+__isl_give isl_schedule *map_to_device(struct gpu_gen *gen,
+ __isl_take isl_schedule *schedule, int to_from_device)
+{
+ isl_schedule_node *node;
+ isl_set *context;
+ isl_set *guard;
+ isl_union_set *domain;
+ isl_union_map *prefix;
+ isl_union_pw_multi_aff *contraction;
+ struct gpu_prog *prog;
+
+ context = isl_set_copy(gen->prog->context);
+ context = isl_set_from_params(context);
+ schedule = isl_schedule_insert_context(schedule, context);
+
+ prog = gen->prog;
+ guard = isl_union_set_params(isl_union_set_copy(prog->scop->domain));
+ prog->context = isl_set_intersect(prog->context, isl_set_copy(guard));
+ guard = isl_set_from_params(guard);
+
+ node = isl_schedule_get_root(schedule);
+ isl_schedule_free(schedule);
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_child(node, 0);
+ node = isolate_permutable_subtrees(node, gen->prog);
+ domain = isl_schedule_node_get_domain(node);
+ contraction = isl_schedule_node_get_subtree_contraction(node);
+ domain = isl_union_set_preimage_union_pw_multi_aff(domain,
+ isl_union_pw_multi_aff_copy(contraction));
+ prefix = isl_schedule_node_get_prefix_schedule_union_map(node);
+ prefix = isl_union_map_preimage_domain_union_pw_multi_aff(prefix,
+ contraction);
+ node = mark_kernels(gen, node);
+ if (to_from_device) {
+ node = add_to_from_device(node, domain, prefix, gen->prog);
+ } else {
+ isl_union_set_free(domain);
+ isl_union_map_free(prefix);
+ }
+ node = isl_schedule_node_root(node);
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_insert_guard(node, guard);
+ node = isl_schedule_node_child(node, 0);
+ node = add_init_clear_device(node);
+ schedule = isl_schedule_node_get_schedule(node);
+ isl_schedule_node_free(node);
+
+ return schedule;
+}
+
+/* Internal data structure for extract_access.
+ * "next_access" points to the end of a linked list that is extended
+ * by extract_access.
+ * "single_expression" is set if the access expressions belong to
+ * an expression statement (i.e., a statement without internal control).
+ * "any_to_outer" maps all intermediate arrays to their outer arrays.
+ */
+struct ppcg_extract_access_data {
+ struct gpu_stmt_access **next_access;
+ int single_expression;
+ isl_union_map *any_to_outer;
+};
+
+/* Given a tagged access relation to a single array "tagged", extract it
+ * as a map, taking into account that the input may be empty.
+ * If the access relation is empty, then it does not contain
+ * any space information, so we try to recover it from the index
+ * expression.
+ * The space of the index expression is of the form I -> A,
+ * with I the statement instances and A the array, or [I -> F] -> A,
+ * with F the filters corresponding to arguments.
+ * We first drop F, if present, obtaining I -> A.
+ * Then we construct I -> R, with R the reference tag,
+ * combine the two into I -> [R -> A] and uncurry to obtain
+ * the final result [I -> R] -> A.
+ * Note that the index expression may have a lower dimension
+ * than that of the array, but this dimension is not used
+ * if the access relation is empty.
+ */
+static __isl_give isl_map *extract_single_tagged_access(
+ __isl_take isl_union_map *tagged, __isl_keep pet_expr *expr)
+{
+ int empty;
+ isl_id *id;
+ isl_space *space, *space2;
+ isl_multi_pw_aff *index;
+
+ empty = isl_union_map_is_empty(tagged);
+ if (empty < 0)
+ goto error;
+ if (!empty)
+ return isl_map_from_union_map(tagged);
+ isl_union_map_free(tagged);
+
+ index = pet_expr_access_get_index(expr);
+ space = isl_multi_pw_aff_get_space(index);
+ isl_multi_pw_aff_free(index);
+ if (isl_space_domain_is_wrapping(space))
+ space = isl_space_domain_factor_domain(space);
+ space2 = isl_space_copy(space);
+ space2 = isl_space_from_domain(isl_space_domain(space));
+ id = pet_expr_access_get_ref_id(expr);
+ space2 = isl_space_set_tuple_id(space2, isl_dim_out, id);
+ space = isl_space_range_product(space2, space);
+ space = isl_space_uncurry(space);
+
+ return isl_map_empty(space);
+error:
+ isl_union_map_free(tagged);
+ return NULL;
+}
+
+/* Does the index expression "index" of "expr" represent an access
+ * to a single element?
+ * That is, is "index" completely specified?
+ *
+ * If "expr" accesses elements from different spaces (i.e., fields
+ * of a structure), then it does not access a single element.
+ * Otherwise, if the single space of the access matches the space
+ * of "index", then the index expression is completely specified
+ * (no pointer to a lower-dimensional slice of the accessed array)
+ * and a single element is being accessed.
+ */
+static isl_bool complete_index(__isl_keep pet_expr *expr,
+ __isl_keep isl_multi_pw_aff *index)
+{
+ isl_union_map *read, *write, *all;
+ isl_map *map;
+ isl_space *space1, *space2;
+ isl_bool complete;
+
+ read = pet_expr_access_get_may_read(expr);
+ write = pet_expr_access_get_may_write(expr);
+ all = isl_union_map_union(read, write);
+ if (!all)
+ return isl_bool_error;
+ if (isl_union_map_n_map(all) != 1) {
+ isl_union_map_free(all);
+ return isl_bool_false;
+ }
+ map = isl_map_from_union_map(all);
+ space1 = isl_map_get_space(map);
+ isl_map_free(map);
+ space2 = isl_multi_pw_aff_get_space(index);
+ complete = isl_space_tuple_is_equal(space1, isl_dim_out,
+ space2, isl_dim_out);
+ isl_space_free(space1);
+ isl_space_free(space2);
+
+ return complete;
+}
+
+/* Does "expr" access a single, fixed element (independently of the statement
+ * instance)?
+ * That is, does it have a completely specified constant index expression?
+ *
+ * Note that it is not sufficient for the index expression to be
+ * piecewise constant. isl_multi_pw_aff_is_cst can therefore not be used.
+ */
+static isl_bool accesses_fixed_element(__isl_keep pet_expr *expr)
+{
+ int i, n;
+ isl_multi_pw_aff *index;
+ isl_bool fixed = isl_bool_true;
+
+ index = pet_expr_access_get_index(expr);
+ if (index < 0)
+ return isl_bool_error;
+ n = isl_multi_pw_aff_dim(index, isl_dim_out);
+ for (i = 0; i < n; ++i) {
+ isl_pw_aff *pa;
+
+ pa = isl_multi_pw_aff_get_pw_aff(index, 0);
+ fixed = isl_pw_aff_n_piece(pa) == 1;
+ if (fixed)
+ fixed = isl_pw_aff_is_cst(pa);
+ isl_pw_aff_free(pa);
+ if (fixed < 0 || !fixed)
+ break;
+ }
+ if (fixed >= 0 && fixed)
+ fixed = complete_index(expr, index);
+ isl_multi_pw_aff_free(index);
+
+ return fixed;
+}
+
+/* Extract a gpu_stmt_access from "expr", append it to the list
+ * that ends in *data->next_access and update the end of the list.
+ * If the access expression performs a write, then it is considered
+ * exact only if it appears in a single expression statement and
+ * if its may access relation is equal to its must access relation.
+ *
+ * The combined set of may accesses may be a union if member accesses
+ * are involved, but the entire set is derived from a single reference and
+ * therefore from a single index expression. These accesses therefore
+ * all map to the same outer array.
+ */
+static int extract_access(__isl_keep pet_expr *expr, void *user)
+{
+ struct ppcg_extract_access_data *data = user;
+ isl_union_map *tagged;
+ struct gpu_stmt_access *access;
+ isl_ctx *ctx = pet_expr_get_ctx(expr);
+ isl_multi_pw_aff *index;
+
+ access = isl_alloc_type(ctx, struct gpu_stmt_access);
+ assert(access);
+ access->next = NULL;
+ access->read = pet_expr_access_is_read(expr);
+ access->write = pet_expr_access_is_write(expr);
+ tagged = pet_expr_access_get_tagged_may_read(expr);
+ tagged = isl_union_map_union(tagged,
+ pet_expr_access_get_tagged_may_write(expr));
+ tagged = isl_union_map_apply_range(tagged,
+ isl_union_map_copy(data->any_to_outer));
+ if (!access->write) {
+ access->exact_write = 1;
+ } else if (!data->single_expression) {
+ access->exact_write = 0;
+ } else {
+ isl_union_map *must, *may;
+ may = isl_union_map_copy(tagged);
+ may = isl_union_map_domain_factor_domain(may);
+ must = pet_expr_access_get_must_write(expr);
+ access->exact_write = isl_union_map_is_equal(must, may);
+ isl_union_map_free(must);
+ isl_union_map_free(may);
+ }
+ index = pet_expr_access_get_index(expr);
+ access->n_index = isl_multi_pw_aff_dim(index, isl_dim_out);
+ isl_multi_pw_aff_free(index);
+ access->ref_id = pet_expr_access_get_ref_id(expr);
+ access->tagged_access = extract_single_tagged_access(tagged, expr);
+ access->access = isl_map_copy(access->tagged_access);
+ access->access = isl_map_domain_factor_domain(access->access);
+ access->fixed_element = accesses_fixed_element(expr);
+
+ *data->next_access = access;
+ data->next_access = &(*data->next_access)->next;
+
+ if (!access->access || access->fixed_element < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Construct a linked list of gpu_stmt_access objects,
+ * one for each access expression in the statement body.
+ * "any_to_outer" maps all intermediate arrays to their outer arrays.
+ */
+static int pet_stmt_extract_accesses(struct gpu_stmt *stmt,
+ __isl_keep isl_union_map *any_to_outer)
+{
+ struct ppcg_extract_access_data data;
+
+ stmt->accesses = NULL;
+ data.next_access = &stmt->accesses;
+ data.single_expression =
+ pet_tree_get_type(stmt->stmt->body) == pet_tree_expr;
+ data.any_to_outer = any_to_outer;
+ return pet_tree_foreach_access_expr(stmt->stmt->body,
+ &extract_access, &data);
+}
+
+/* Has statement "stmt" been killed from "scop"?
+ * That is, is the instance set of "scop" free from any
+ * instances of "stmt"?
+ */
+static isl_bool is_stmt_killed(struct ppcg_scop *scop, struct pet_stmt *stmt)
+{
+ isl_space *space;
+ isl_set *left;
+ isl_bool empty;
+
+ if (!scop || !stmt)
+ return isl_bool_error;
+ space = isl_set_get_space(stmt->domain);
+ left = isl_union_set_extract_set(scop->domain, space);
+ empty = isl_set_plain_is_empty(left);
+ isl_set_free(left);
+
+ return empty;
+}
+
+/* Return an array of gpu_stmt representing the statements in "scop".
+ * Do not collect array accesses for statements that have been killed.
+ */
+static struct gpu_stmt *extract_stmts(isl_ctx *ctx, struct ppcg_scop *scop,
+ __isl_keep isl_union_map *any_to_outer)
+{
+ int i;
+ struct gpu_stmt *stmts;
+
+ stmts = isl_calloc_array(ctx, struct gpu_stmt, scop->pet->n_stmt);
+ if (!stmts)
+ return NULL;
+
+ for (i = 0; i < scop->pet->n_stmt; ++i) {
+ struct gpu_stmt *s = &stmts[i];
+ isl_bool killed;
+
+ s->id = isl_set_get_tuple_id(scop->pet->stmts[i]->domain);
+ s->stmt = scop->pet->stmts[i];
+ killed = is_stmt_killed(scop, scop->pet->stmts[i]);
+ if (killed < 0)
+ return free_stmts(stmts, i + 1);
+ if (killed)
+ continue;
+ if (pet_stmt_extract_accesses(s, any_to_outer) < 0)
+ return free_stmts(stmts, i + 1);
+ }
+
+ return stmts;
+}
+
+/* Generate CUDA code for "scop" and print it to "p".
+ * After generating an AST for the transformed scop as explained below,
+ * we call "gen->print" to print the AST in the desired output format
+ * to "p".
+ *
+ * If it turns out that it does not make sense to generate GPU code,
+ * then we generate CPU code instead.
+ *
+ * The declarations of the arrays that are visible outside of the scop
+ * are printed outside of the code generated from the schedule,
+ * because the generated code may involve a guard around the entire code.
+ *
+ * We first compute a schedule that respects the dependences
+ * of the original program and select the outermost bands
+ * of tilable dimensions that have at least one parallel loop.
+ * If the --load-schedule is specified, then the loaded schedule
+ * is used instead of a computed schedule.
+ *
+ * Each of these bands B is then tiled according to "tile" sizes, resulting
+ * in two nested bands, with a kernel marker on top
+ *
+ * K
+ * |
+ * T
+ * |
+ * P
+ *
+ * We then split off at most 2 parallel dimensions from the T band and
+ * at most 3 parallel dimension from the P band
+ *
+ * K
+ * |
+ * T
+ * T1
+ * |
+ * T2
+ * |
+ * P1
+ * |
+ * P2
+ *
+ * A filter is introduced in front of T1 that maps the domain instances
+ * to block identifiers. Similarly, a filter is introduced in front of P1
+ * that maps the domain instances to thread identifiers.
+ *
+ * For each iteration of the T2 band and for each array, we compute
+ * the array elements accessed by that iteration, construct a rectangular
+ * box around it and shift it to the origin. The result is used
+ * as shared memory for the array.
+ *
+ * Copying and synchronization statements are added to this schedule tree.
+ * In principle, these are added in front of the P1 band, but some of
+ * them may get hoisted up to higher levels.
+ *
+ * The entire AST is then generated from the single resulting schedule tree.
+ * During the generation the subtrees at kernel nodes (K) are saved
+ * aside and replaced by kernel calls. The result is printed as host code
+ * while the saved subtrees are printed as device code.
+ */
+static __isl_give isl_printer *generate(__isl_take isl_printer *p,
+ struct gpu_gen *gen, struct ppcg_scop *scop,
+ struct ppcg_options *options)
+{
+ struct gpu_prog *prog;
+ isl_ctx *ctx;
+ isl_schedule *schedule;
+ int any_permutable;
+
+ if (!scop)
+ return isl_printer_free(p);
+
+ ctx = isl_printer_get_ctx(p);
+ prog = gpu_prog_alloc(ctx, scop);
+ if (!prog)
+ return isl_printer_free(p);
+
+ gen->prog = prog;
+ schedule = get_schedule(gen);
+
+ any_permutable = has_any_permutable_node(schedule);
+ if (any_permutable < 0 || !any_permutable) {
+ if (any_permutable < 0)
+ p = isl_printer_free(p);
+ else
+ p = print_cpu(p, scop, options);
+ isl_schedule_free(schedule);
+ } else {
+ const int create_to_from_device = 1;
+ schedule = map_to_device(gen, schedule, create_to_from_device);
+ gen->tree = generate_code(gen, schedule);
+ p = ppcg_set_macro_names(p);
+ p = ppcg_print_exposed_declarations(p, prog->scop);
+ p = gen->print(p, gen->prog, gen->tree, &gen->types,
+ gen->print_user);
+ isl_ast_node_free(gen->tree);
+ }
+
+ gpu_prog_free(prog);
+
+ return p;
+}
+
+/* Wrapper around generate for use as a ppcg_transform callback.
+ */
+static __isl_give isl_printer *generate_wrap(__isl_take isl_printer *p,
+ struct ppcg_scop *scop, void *user)
+{
+ struct gpu_gen *gen = user;
+
+ return generate(p, gen, scop, gen->options);
+}
+
+/* Transform the code in the file called "input" by replacing
+ * all scops by corresponding GPU code and write the results to "out".
+ */
+int generate_gpu(isl_ctx *ctx, const char *input, FILE *out,
+ struct ppcg_options *options,
+ __isl_give isl_printer *(*print)(__isl_take isl_printer *p,
+ struct gpu_prog *prog, __isl_keep isl_ast_node *tree,
+ struct gpu_types *types, void *user), void *user)
+{
+ struct gpu_gen gen;
+ int r;
+ int i;
+
+ gen.ctx = ctx;
+ gen.sizes = extract_sizes_from_str(ctx, options->sizes);
+ gen.options = options;
+ gen.kernel_id = 0;
+ gen.print = print;
+ gen.print_user = user;
+ gen.types.n = 0;
+ gen.types.name = NULL;
+
+ if (options->debug->dump_sizes) {
+ isl_space *space = isl_space_params_alloc(ctx, 0);
+ gen.used_sizes = isl_union_map_empty(space);
+ }
+
+ r = ppcg_transform(ctx, input, out, options, &generate_wrap, &gen);
+
+ if (options->debug->dump_sizes) {
+ isl_union_map_dump(gen.used_sizes);
+ isl_union_map_free(gen.used_sizes);
+ }
+
+ isl_union_map_free(gen.sizes);
+ for (i = 0; i < gen.types.n; ++i)
+ free(gen.types.name[i]);
+ free(gen.types.name);
+
+ return r;
+}
+
+/* Compute the set of inner array elements that may have their values
+ * preserved by "prog". In particular, collect the array elements of
+ * arrays that are not local to "prog" and remove those elements that
+ * are definitely killed or definitely written by "prog".
+ */
+__isl_give isl_union_set *compute_may_persist(struct gpu_prog *prog)
+{
+ int i;
+ isl_union_set *may_persist, *killed;
+ isl_union_map *must_kill;
+
+ may_persist = isl_union_set_empty(isl_set_get_space(prog->context));
+ for (i = 0; i < prog->n_array; ++i) {
+ isl_set *extent;
+
+ if (prog->array[i].local)
+ continue;
+
+ extent = isl_set_copy(prog->array[i].extent);
+ may_persist = isl_union_set_add_set(may_persist, extent);
+ }
+
+ may_persist = isl_union_set_intersect_params(may_persist,
+ isl_set_copy(prog->context));
+ may_persist = isl_union_set_apply(may_persist,
+ isl_union_map_copy(prog->to_inner));
+ must_kill = isl_union_map_copy(prog->tagged_must_kill);
+ killed = isl_union_map_range(must_kill);
+ must_kill = isl_union_map_copy(prog->must_write);
+ killed = isl_union_set_union(killed, isl_union_map_range(must_kill));
+
+ may_persist = isl_union_set_subtract(may_persist, killed);
+ return may_persist;
+}
+
+struct gpu_prog *gpu_prog_alloc(isl_ctx *ctx, struct ppcg_scop *scop)
+{
+ struct gpu_prog *prog;
+ isl_space *space;
+ isl_map *id;
+
+ if (!scop)
+ return NULL;
+
+ prog = isl_calloc_type(ctx, struct gpu_prog);
+ assert(prog);
+
+ prog->ctx = ctx;
+ prog->scop = scop;
+ prog->context = isl_set_copy(scop->context);
+ prog->n_stmts = scop->pet->n_stmt;
+ prog->any_to_outer = pet_scop_compute_outer_to_any(scop->pet);
+ prog->any_to_outer = isl_union_map_reverse(prog->any_to_outer);
+ space = isl_union_map_get_space(prog->any_to_outer);
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, 1);
+ space = isl_space_map_from_set(space);
+ id = isl_map_identity(space);
+ prog->any_to_outer = isl_union_map_add_map(prog->any_to_outer, id);
+ prog->stmts = extract_stmts(ctx, scop, prog->any_to_outer);
+ prog->read = isl_union_map_copy(scop->reads);
+ prog->may_write = isl_union_map_copy(scop->may_writes);
+ prog->must_write = isl_union_map_copy(scop->must_writes);
+ prog->tagged_must_kill = isl_union_map_copy(scop->tagged_must_kills);
+ prog->to_inner = pet_scop_compute_outer_to_inner(scop->pet);
+ prog->to_outer = isl_union_map_copy(prog->to_inner);
+ prog->to_outer = isl_union_map_reverse(prog->to_outer);
+
+ if (!prog->stmts)
+ return gpu_prog_free(prog);
+
+ if (collect_array_info(prog) < 0)
+ return gpu_prog_free(prog);
+ prog->may_persist = compute_may_persist(prog);
+
+ return prog;
+}
+
+void *gpu_prog_free(struct gpu_prog *prog)
+{
+ if (!prog)
+ return NULL;
+ free_array_info(prog);
+ free_stmts(prog->stmts, prog->n_stmts);
+ isl_union_map_free(prog->any_to_outer);
+ isl_union_map_free(prog->to_outer);
+ isl_union_map_free(prog->to_inner);
+ isl_union_map_free(prog->read);
+ isl_union_map_free(prog->may_write);
+ isl_union_map_free(prog->must_write);
+ isl_union_map_free(prog->tagged_must_kill);
+ isl_union_map_free(prog->array_order);
+ isl_union_set_free(prog->may_persist);
+ isl_set_free(prog->context);
+ free(prog);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu.h
new file mode 100644
index 00000000000..62fb0f741bb
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu.h
@@ -0,0 +1,459 @@
+#ifndef _GPU_H
+#define _GPU_H
+
+#include <isl/ast.h>
+#include <isl/id.h>
+#include <isl/id_to_ast_expr.h>
+
+#include <pet.h>
+
+#include "ppcg.h"
+#include "ppcg_options.h"
+
+/* An access to an outer array element or an iterator.
+ * Accesses to iterators have an access relation that maps to an unnamed space.
+ * An access may be both read and write.
+ * If the access relation is empty, then the output dimension may
+ * not be equal to the dimension of the corresponding array.
+ */
+struct gpu_stmt_access {
+ /* Access reads elements */
+ int read;
+ /* Access writes elements */
+ int write;
+ /* All writes are definite writes. */
+ int exact_write;
+ /* Is a single, fixed element being accessed? */
+ isl_bool fixed_element;
+ /* The number of index expressions specified in the access. */
+ int n_index;
+
+ /* May access relation */
+ isl_map *access;
+ /* May access relation with as domain a mapping from iteration domain
+ * to a reference identifier.
+ */
+ isl_map *tagged_access;
+ /* The reference id of the corresponding pet_expr. */
+ isl_id *ref_id;
+
+ struct gpu_stmt_access *next;
+};
+
+/* A representation of a user statement.
+ * "stmt" points to the corresponding pet statement.
+ * "id" is the identifier of the instance set of the statement.
+ * "accesses" is a linked list of accesses performed by the statement.
+ * If the statement has been killed, i.e., if it will not be scheduled,
+ * then this linked list may be empty even if the actual statement does
+ * perform accesses.
+ */
+struct gpu_stmt {
+ isl_id *id;
+ struct pet_stmt *stmt;
+
+ struct gpu_stmt_access *accesses;
+};
+
+/* Represents an outer array possibly accessed by a gpu_prog.
+ */
+struct gpu_array_info {
+ /* The array data space. */
+ isl_space *space;
+ /* Element type. */
+ char *type;
+ /* Element size. */
+ int size;
+ /* Name of the array. */
+ char *name;
+ /* Declared extent of original array. */
+ isl_set *declared_extent;
+ /* AST expression for declared size of original array. */
+ isl_ast_expr *declared_size;
+ /* Extent of the array that needs to be copied. */
+ isl_set *extent;
+ /* Number of indices. */
+ unsigned n_index;
+ /* For each index, a bound on "extent" in that direction. */
+ isl_multi_pw_aff *bound;
+ /* The corresponding access AST expression, if the array needs
+ * to be allocated on the device.
+ */
+ isl_ast_expr *bound_expr;
+
+ /* All references to this array; point to elements of a linked list. */
+ int n_ref;
+ struct gpu_stmt_access **refs;
+
+ /* Is this array accessed at all by the program? */
+ int accessed;
+
+ /* Is this a scalar that is read-only within the entire program? */
+ int read_only_scalar;
+
+ /* Are the elements of the array structures? */
+ int has_compound_element;
+
+ /* Are the elements only accessed through constant index expressions? */
+ int only_fixed_element;
+
+ /* Is the array local to the scop? */
+ int local;
+ /* Is the array local and should it be declared on the host? */
+ int declare_local;
+
+ /* Is the corresponding global device memory accessed in any way? */
+ int global;
+
+ /* Should the array be linearized? */
+ int linearize;
+
+ /* Order dependences on this array.
+ * Only used if live_range_reordering option is set.
+ * It is set to NULL otherwise.
+ */
+ isl_union_map *dep_order;
+
+ void *user;
+};
+
+/* Represents an outer array accessed by a ppcg_kernel, localized
+ * to the context of this kernel.
+ *
+ * "array" points to the corresponding array in the gpu_prog.
+ * The "n_group" "groups" are the reference groups associated to the array.
+ * If "force_private" is set, then the array (in practice a scalar)
+ * must be mapped to a register.
+ * "global" is set if the global device memory corresponding
+ * to this array is accessed by the kernel.
+ * "bound" is equal to array->bound specialized to the current kernel.
+ * "bound_expr" is the corresponding access AST expression.
+ */
+struct gpu_local_array_info {
+ struct gpu_array_info *array;
+
+ int n_group;
+ struct gpu_array_ref_group **groups;
+
+ int force_private;
+ int global;
+
+ unsigned n_index;
+ isl_multi_pw_aff *bound;
+ isl_ast_expr *bound_expr;
+};
+
+__isl_give isl_ast_expr *gpu_local_array_info_linearize_index(
+ struct gpu_local_array_info *array, __isl_take isl_ast_expr *expr);
+
+/* A sequence of "n" names of types.
+ */
+struct gpu_types {
+ int n;
+ char **name;
+};
+
+/* "read" and "write" contain the original access relations, possibly
+ * involving member accesses.
+ *
+ * The elements of "array", as well as the ranges of "copy_in" and "copy_out"
+ * only refer to the outer arrays of any possible member accesses.
+ */
+struct gpu_prog {
+ isl_ctx *ctx;
+
+ struct ppcg_scop *scop;
+
+ /* Set of parameter values */
+ isl_set *context;
+
+ /* All potential read accesses in the entire program */
+ isl_union_map *read;
+
+ /* All potential write accesses in the entire program */
+ isl_union_map *may_write;
+ /* All definite write accesses in the entire program */
+ isl_union_map *must_write;
+ /* All tagged definite kills in the entire program */
+ isl_union_map *tagged_must_kill;
+
+ /* The set of inner array elements that may be preserved. */
+ isl_union_set *may_persist;
+
+ /* A mapping from all innermost arrays to their outer arrays. */
+ isl_union_map *to_outer;
+ /* A mapping from the outer arrays to all corresponding inner arrays. */
+ isl_union_map *to_inner;
+ /* A mapping from all intermediate arrays to their outer arrays,
+ * including an identity mapping from the anonymous 1D space to itself.
+ */
+ isl_union_map *any_to_outer;
+
+ /* Order dependences on non-scalars. */
+ isl_union_map *array_order;
+
+ /* Array of statements */
+ int n_stmts;
+ struct gpu_stmt *stmts;
+
+ int n_array;
+ struct gpu_array_info *array;
+};
+
+struct gpu_gen {
+ isl_ctx *ctx;
+ struct ppcg_options *options;
+
+ /* Callback for printing of AST in appropriate format. */
+ __isl_give isl_printer *(*print)(__isl_take isl_printer *p,
+ struct gpu_prog *prog, __isl_keep isl_ast_node *tree,
+ struct gpu_types *types, void *user);
+ void *print_user;
+
+ isl_id_to_ast_expr *(*build_ast_expr)(void *stmt,
+ isl_ast_build *build,
+ isl_multi_pw_aff *(*fn_index)(
+ __isl_take isl_multi_pw_aff *mpa, isl_id *id,
+ void *user),
+ void *user_index,
+ isl_ast_expr *(*fn_expr)(isl_ast_expr *expr,
+ isl_id *id, void *user),
+ void *user_expr);
+
+ struct gpu_prog *prog;
+ /* The generated AST. */
+ isl_ast_node *tree;
+
+ /* The sequence of types for which a definition has been printed. */
+ struct gpu_types types;
+
+ /* User specified tile, grid and block sizes for each kernel */
+ isl_union_map *sizes;
+
+ /* Effectively used tile, grid and block sizes for each kernel */
+ isl_union_map *used_sizes;
+
+ /* Identifier of the next kernel. */
+ int kernel_id;
+};
+
+enum ppcg_group_access_type {
+ ppcg_access_global,
+ ppcg_access_shared,
+ ppcg_access_private
+};
+
+enum ppcg_kernel_stmt_type {
+ ppcg_kernel_copy,
+ ppcg_kernel_domain,
+ ppcg_kernel_sync
+};
+
+/* Representation of special statements, in particular copy statements
+ * and __syncthreads statements, inside a kernel.
+ *
+ * type represents the kind of statement
+ *
+ *
+ * for ppcg_kernel_copy statements we have
+ *
+ * read is set if the statement should copy data from global memory
+ * to shared memory or registers.
+ *
+ * index expresses an access to the array element that needs to be copied
+ * local_index expresses the corresponding element in the tile
+ *
+ * array refers to the original array being copied
+ * local_array is a pointer to the appropriate element in the "array"
+ * array of the ppcg_kernel to which this copy access belongs
+ *
+ *
+ * for ppcg_kernel_domain statements we have
+ *
+ * stmt is the corresponding input statement
+ *
+ * n_access is the number of accesses in stmt
+ * access is an array of local information about the accesses
+ */
+struct ppcg_kernel_stmt {
+ enum ppcg_kernel_stmt_type type;
+
+ union {
+ struct {
+ int read;
+ isl_ast_expr *index;
+ isl_ast_expr *local_index;
+ struct gpu_array_info *array;
+ struct gpu_local_array_info *local_array;
+ } c;
+ struct {
+ struct gpu_stmt *stmt;
+ isl_id_to_ast_expr *ref2expr;
+ } d;
+ } u;
+};
+
+/* Representation of a local variable in a kernel.
+ */
+struct ppcg_kernel_var {
+ struct gpu_array_info *array;
+ enum ppcg_group_access_type type;
+ char *name;
+ isl_vec *size;
+};
+
+/* Representation of a kernel.
+ *
+ * prog describes the original code from which the kernel is extracted.
+ *
+ * id is the sequence number of the kernel.
+ *
+ * block_ids contains the list of block identifiers for this kernel.
+ * thread_ids contains the list of thread identifiers for this kernel.
+ *
+ * the first n_grid elements of grid_dim represent the specified size
+ * of the grid.
+ * the first n_block elements of block_dim represent the specified or
+ * effective size of the block.
+ * Note that in the input file, the sizes of the grid and the blocks
+ * are specified in the order x, y, z, but internally, the sizes
+ * are stored in reverse order, so that the last element always
+ * refers to the x dimension.
+ *
+ * grid_size reflects the effective grid size.
+ * grid_size_expr contains a corresponding access AST expression, built within
+ * the context where the launch appears.
+ *
+ * context contains the values of the parameters and outer schedule dimensions
+ * for which any statement instance in this kernel needs to be executed.
+ *
+ * n_sync is the number of synchronization operations that have
+ * been introduced in the schedule tree corresponding to this kernel (so far).
+ *
+ * core contains the spaces of the statement domains that form
+ * the core computation of the kernel. It is used to navigate
+ * the tree during the construction of the device part of the schedule
+ * tree in gpu_create_kernel.
+ *
+ * expanded_domain contains the original statement instances,
+ * i.e., those that appear in the domains of access relations,
+ * that are involved in the kernel.
+ * contraction maps those original statement instances to
+ * the statement instances that are active at the point
+ * in the schedule tree where the kernel is created.
+ *
+ * arrays is the set of possibly accessed outer array elements.
+ *
+ * space is the schedule space of the AST context. That is, it represents
+ * the loops of the generated host code containing the kernel launch.
+ *
+ * n_array is the total number of arrays in the input program and also
+ * the number of element in the array array.
+ * array contains information about each array that is local
+ * to the current kernel. If an array is not used in a kernel,
+ * then the corresponding entry does not contain any information.
+ *
+ * any_force_private is set if any array in the kernel is marked force_private
+ *
+ * block_filter contains constraints on the domain elements in the kernel
+ * that encode the mapping to block identifiers, where the block identifiers
+ * are represented by "n_grid" parameters with as names the elements
+ * of "block_ids".
+ *
+ * thread_filter contains constraints on the domain elements in the kernel
+ * that encode the mapping to thread identifiers, where the thread identifiers
+ * are represented by "n_block" parameters with as names the elements
+ * of "thread_ids".
+ *
+ * copy_schedule corresponds to the schedule dimensions of
+ * the (tiled) schedule for this kernel that have been taken into account
+ * for computing private/shared memory tiles.
+ * The domain corresponds to the original statement instances, i.e.,
+ * those that appear in the leaves of the schedule tree.
+ * copy_schedule_dim is the dimension of this schedule.
+ *
+ * sync_writes contains write references that require synchronization.
+ * Each reference is represented by a universe set in a space [S[i,j] -> R[]]
+ * with S[i,j] the statement instance space and R[] the array reference.
+ */
+struct ppcg_kernel {
+ isl_ctx *ctx;
+ struct ppcg_options *options;
+
+ struct gpu_prog *prog;
+
+ int id;
+
+ isl_id_list *block_ids;
+ isl_id_list *thread_ids;
+
+ int n_grid;
+ int n_block;
+ int grid_dim[2];
+ int block_dim[3];
+
+ isl_multi_pw_aff *grid_size;
+ isl_ast_expr *grid_size_expr;
+ isl_set *context;
+
+ int n_sync;
+ isl_union_set *core;
+ isl_union_set *arrays;
+
+ isl_union_pw_multi_aff *contraction;
+ isl_union_set *expanded_domain;
+
+ isl_space *space;
+
+ int n_array;
+ struct gpu_local_array_info *array;
+
+ int n_var;
+ struct ppcg_kernel_var *var;
+
+ int any_force_private;
+
+ isl_union_set *block_filter;
+ isl_union_set *thread_filter;
+ isl_union_pw_multi_aff *copy_schedule;
+ int copy_schedule_dim;
+
+ isl_union_set *sync_writes;
+
+ isl_ast_node *tree;
+};
+
+int gpu_array_is_scalar(struct gpu_array_info *array);
+int gpu_array_is_read_only_scalar(struct gpu_array_info *array);
+int gpu_array_requires_device_allocation(struct gpu_array_info *array);
+__isl_give isl_set *gpu_array_positive_size_guard(struct gpu_array_info *array);
+isl_bool gpu_array_can_be_private(struct gpu_array_info *array);
+
+struct gpu_prog *gpu_prog_alloc(isl_ctx *ctx, struct ppcg_scop *scop);
+void *gpu_prog_free(struct gpu_prog *prog);
+
+int ppcg_kernel_requires_array_argument(struct ppcg_kernel *kernel, int i);
+
+int generate_gpu(isl_ctx *ctx, const char *input, FILE *out,
+ struct ppcg_options *options,
+ __isl_give isl_printer *(*print)(__isl_take isl_printer *p,
+ struct gpu_prog *prog, __isl_keep isl_ast_node *tree,
+ struct gpu_types *types, void *user), void *user);
+
+__isl_give isl_schedule_node *gpu_create_kernel(struct gpu_gen *gen,
+ __isl_take isl_schedule_node *node, int scale,
+ __isl_keep isl_multi_val *sizes);
+
+__isl_give isl_schedule *get_schedule(struct gpu_gen *gen);
+int has_any_permutable_node(__isl_keep isl_schedule *schedule);
+__isl_give isl_schedule *map_to_device(struct gpu_gen *gen,
+ __isl_take isl_schedule *schedule,
+ int to_from_device);
+__isl_give isl_ast_node *generate_code(struct gpu_gen *gen,
+ __isl_take isl_schedule *schedule);
+
+__isl_give isl_union_set *compute_may_persist(struct gpu_prog *prog);
+void collect_references(struct gpu_prog *prog, struct gpu_array_info *array);
+void collect_order_dependences(struct gpu_prog *prog);
+isl_bool only_fixed_element_accessed(struct gpu_array_info *array);
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_array_tile.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_array_tile.c
new file mode 100644
index 00000000000..b358f2b8e4a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_array_tile.c
@@ -0,0 +1,71 @@
+#include <isl/aff.h>
+#include <isl/map.h>
+
+#include "gpu_array_tile.h"
+
+struct gpu_array_tile *gpu_array_tile_free(struct gpu_array_tile *tile)
+{
+ int j;
+
+ if (!tile)
+ return NULL;
+
+ for (j = 0; j < tile->n; ++j) {
+ isl_val_free(tile->bound[j].size);
+ isl_val_free(tile->bound[j].stride);
+ isl_aff_free(tile->bound[j].lb);
+ isl_aff_free(tile->bound[j].shift);
+ }
+ free(tile->bound);
+ isl_multi_aff_free(tile->tiling);
+ free(tile);
+
+ return NULL;
+}
+
+/* Create a gpu_array_tile for an array of dimension "n_index".
+ */
+struct gpu_array_tile *gpu_array_tile_create(isl_ctx *ctx, int n_index)
+{
+ int i;
+ struct gpu_array_tile *tile;
+
+ tile = isl_calloc_type(ctx, struct gpu_array_tile);
+ if (!tile)
+ return NULL;
+
+ tile->ctx = ctx;
+ tile->bound = isl_alloc_array(ctx, struct gpu_array_bound, n_index);
+ if (!tile->bound)
+ return gpu_array_tile_free(tile);
+
+ tile->n = n_index;
+
+ for (i = 0; i < n_index; ++i) {
+ tile->bound[i].size = NULL;
+ tile->bound[i].lb = NULL;
+ tile->bound[i].stride = NULL;
+ tile->bound[i].shift = NULL;
+ }
+
+ return tile;
+}
+
+/* Compute the size of the tile specified by "tile"
+ * in number of elements and return the result.
+ */
+__isl_give isl_val *gpu_array_tile_size(struct gpu_array_tile *tile)
+{
+ int i;
+ isl_val *size;
+
+ if (!tile)
+ return NULL;
+
+ size = isl_val_one(tile->ctx);
+
+ for (i = 0; i < tile->n; ++i)
+ size = isl_val_mul(size, isl_val_copy(tile->bound[i].size));
+
+ return size;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_array_tile.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_array_tile.h
new file mode 100644
index 00000000000..53b8e3db74c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_array_tile.h
@@ -0,0 +1,59 @@
+#ifndef GPU_ARRAY_TILE_H
+#define GPU_ARRAY_TILE_H
+
+#include <isl/aff_type.h>
+#include <isl/map_type.h>
+#include <isl/val.h>
+
+/* The fields stride and shift only contain valid information
+ * if shift != NULL.
+ * If so, they express that current index is such that if you add shift,
+ * then the result is always a multiple of stride.
+ * Let D represent the initial tile->depth dimensions of the computed schedule.
+ * The spaces of "lb" and "shift" are of the form
+ *
+ * D -> [b]
+ */
+struct gpu_array_bound {
+ isl_val *size;
+ isl_aff *lb;
+
+ isl_val *stride;
+ isl_aff *shift;
+};
+
+/* A tile of an outer array.
+ *
+ * requires_unroll is set if the schedule dimensions that are mapped
+ * to threads need to be unrolled for this (private) tile to be used.
+ *
+ * "depth" reflects the number of schedule dimensions that affect the tile.
+ * The copying into and/or out of the tile is performed at that depth.
+ *
+ * n is the dimension of the array.
+ * bound is an array of size "n" representing the lower bound
+ * and size for each index.
+ *
+ * tiling maps a tile in the global array to the corresponding
+ * shared/private memory tile and is of the form
+ *
+ * { [D[i] -> A[a]] -> T[(a + shift(i))/stride - lb(i)] }
+ *
+ * where D represents the initial "depth" dimensions
+ * of the computed schedule.
+ */
+struct gpu_array_tile {
+ isl_ctx *ctx;
+ int requires_unroll;
+ int depth;
+ int n;
+ struct gpu_array_bound *bound;
+ isl_multi_aff *tiling;
+};
+
+struct gpu_array_tile *gpu_array_tile_create(isl_ctx *ctx, int n_index);
+struct gpu_array_tile *gpu_array_tile_free(struct gpu_array_tile *tile);
+
+__isl_give isl_val *gpu_array_tile_size(struct gpu_array_tile *tile);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_group.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_group.c
new file mode 100644
index 00000000000..7b869b978c4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_group.c
@@ -0,0 +1,1828 @@
+/*
+ * Copyright 2010-2011 INRIA Saclay
+ * Copyright 2012-2014 Ecole Normale Superieure
+ * Copyright 2015 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/constraint.h>
+#include <isl/ilp.h>
+
+#include "gpu_array_tile.h"
+#include "gpu_group.h"
+#include "gpu_tree.h"
+#include "schedule.h"
+
+/* Print the name of the local copy of a given group of array references.
+ */
+__isl_give isl_printer *gpu_array_ref_group_print_name(
+ struct gpu_array_ref_group *group, __isl_take isl_printer *p)
+{
+ int global = 0;
+ enum ppcg_group_access_type type;
+
+ type = gpu_array_ref_group_type(group);
+ if (type == ppcg_access_private)
+ p = isl_printer_print_str(p, "private_");
+ else if (type == ppcg_access_shared)
+ p = isl_printer_print_str(p, "shared_");
+ else
+ global = 1;
+ p = isl_printer_print_str(p, group->array->name);
+ if (!global && group->local_array->n_group > 1) {
+ p = isl_printer_print_str(p, "_");
+ p = isl_printer_print_int(p, group->nr);
+ }
+
+ return p;
+}
+
+/* Return the union of all read (read = 1) and/or write (write = 1)
+ * access relations in the group.
+ */
+__isl_give isl_union_map *gpu_array_ref_group_access_relation(
+ struct gpu_array_ref_group *group, int read, int write)
+{
+ int i;
+ isl_union_map *access;
+
+ access = isl_union_map_empty(isl_map_get_space(group->access));
+ for (i = 0; i < group->n_ref; ++i) {
+ isl_map *map_i;
+
+ if (!((read && group->refs[i]->read) ||
+ (write && group->refs[i]->write)))
+ continue;
+ map_i = isl_map_copy(group->refs[i]->access);
+ access = isl_union_map_union(access,
+ isl_union_map_from_map(map_i));
+ }
+
+ return access;
+}
+
+/* Should this array reference group be mapped to private, shared or global
+ * memory?
+ * If we have computed both a private and a shared tile, then
+ * the tile with the smallest depth is used. If both have the same depth,
+ * then the private tile is used.
+ */
+enum ppcg_group_access_type gpu_array_ref_group_type(
+ struct gpu_array_ref_group *group)
+{
+ if (group->private_tile && group->shared_tile &&
+ group->shared_tile->depth < group->private_tile->depth)
+ return ppcg_access_shared;
+ if (group->private_tile)
+ return ppcg_access_private;
+ if (group->shared_tile)
+ return ppcg_access_shared;
+ return ppcg_access_global;
+}
+
+
+/* Return the effective gpu_array_tile associated to "group" or
+ * NULL if there is no such gpu_array_tile.
+ */
+struct gpu_array_tile *gpu_array_ref_group_tile(
+ struct gpu_array_ref_group *group)
+{
+ switch (gpu_array_ref_group_type(group)) {
+ case ppcg_access_global:
+ return NULL;
+ case ppcg_access_shared:
+ return group->shared_tile;
+ case ppcg_access_private:
+ return group->private_tile;
+ }
+}
+
+/* Does the tile associated to "group" require unrolling of the schedule
+ * dimensions mapped to threads?
+ * Note that this can only happen for private tiles.
+ */
+int gpu_array_ref_group_requires_unroll(struct gpu_array_ref_group *group)
+{
+ struct gpu_array_tile *tile;
+
+ tile = gpu_array_ref_group_tile(group);
+ if (!tile)
+ return 0;
+ return tile->requires_unroll;
+}
+
+/* Given a constraint
+ *
+ * a(p,i) + j = g f(e)
+ *
+ * or -a(p,i) - j = g f(e) if sign < 0,
+ * store a(p,i) in bound->shift and g (stride) in bound->stride.
+ * a(p,i) is assumed to be an expression in only the parameters
+ * and the input dimensions.
+ */
+static void extract_stride(__isl_keep isl_constraint *c,
+ struct gpu_array_bound *bound, __isl_keep isl_val *stride, int sign)
+{
+ int i;
+ isl_val *v;
+ isl_space *space;
+ unsigned nparam;
+ unsigned nvar;
+ isl_aff *aff;
+
+ isl_val_free(bound->stride);
+ bound->stride = isl_val_copy(stride);
+
+ space = isl_constraint_get_space(c);
+ space = isl_space_domain(space);
+
+ nparam = isl_space_dim(space, isl_dim_param);
+ nvar = isl_space_dim(space, isl_dim_set);
+
+ v = isl_constraint_get_constant_val(c);
+ if (sign < 0)
+ v = isl_val_neg(v);
+ aff = isl_aff_zero_on_domain(isl_local_space_from_space(space));
+ aff = isl_aff_set_constant_val(aff, v);
+
+ for (i = 0; i < nparam; ++i) {
+ if (!isl_constraint_involves_dims(c, isl_dim_param, i, 1))
+ continue;
+ v = isl_constraint_get_coefficient_val(c, isl_dim_param, i);
+ if (sign < 0)
+ v = isl_val_neg(v);
+ aff = isl_aff_add_coefficient_val(aff, isl_dim_param, i, v);
+ }
+
+ for (i = 0; i < nvar; ++i) {
+ if (!isl_constraint_involves_dims(c, isl_dim_in, i, 1))
+ continue;
+ v = isl_constraint_get_coefficient_val(c, isl_dim_in, i);
+ if (sign < 0)
+ v = isl_val_neg(v);
+ aff = isl_aff_add_coefficient_val(aff, isl_dim_in, i, v);
+ }
+
+ bound->shift = aff;
+}
+
+/* Given an equality constraint of a map with a single output dimension j,
+ * check if the constraint is of the form
+ *
+ * a(p,i) + j = g f(e)
+ *
+ * with a(p,i) an expression in the parameters and input dimensions
+ * and f(e) an expression in the existentially quantified variables.
+ * If so, and if g is larger than any such g from a previously considered
+ * constraint, then call extract_stride to record the stride information
+ * in bound.
+ */
+static isl_stat check_stride_constraint(__isl_take isl_constraint *c,
+ void *user)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_val *v;
+ unsigned n_div;
+ struct gpu_array_bound *bound = user;
+
+ ctx = isl_constraint_get_ctx(c);
+ n_div = isl_constraint_dim(c, isl_dim_div);
+ v = isl_constraint_get_coefficient_val(c, isl_dim_out, 0);
+
+ if (n_div && (isl_val_is_one(v) || isl_val_is_negone(v))) {
+ int s = isl_val_sgn(v);
+ isl_val *stride = isl_val_zero(ctx);
+
+ isl_val_free(v);
+ for (i = 0; i < n_div; ++i) {
+ v = isl_constraint_get_coefficient_val(c,
+ isl_dim_div, i);
+ stride = isl_val_gcd(stride, v);
+ }
+ if (!isl_val_is_zero(stride) &&
+ isl_val_gt(stride, bound->stride))
+ extract_stride(c, bound, stride, s);
+
+ isl_val_free(stride);
+ } else
+ isl_val_free(v);
+
+ isl_constraint_free(c);
+ return isl_stat_ok;
+}
+
+/* Given contraints on an array index i, check if we can find
+ * a shift a(p) and a stride g such that
+ *
+ * a(p) + i = 0 mod g
+ *
+ * If so, record the information in bound and apply the mapping
+ * i -> (i + a(p))/g to the array index in bounds and return
+ * the new constraints.
+ * If not, simply return the original constraints.
+ *
+ * If bounds is a subset of the space
+ *
+ * D -> i
+ *
+ * then the bound recorded in bound->shift is of the form
+ *
+ * D -> s(D)
+ *
+ * with s(D) equal to a(p) above.
+ * Next, we construct a mapping of the form
+ *
+ * [D -> i] -> [D -> (i + S(D))/g]
+ *
+ * This mapping is computed as follows.
+ * We first introduce "i" in the domain through precomposition
+ * with [D -> i] -> D obtaining
+ *
+ * [D -> i] -> s(D)
+ *
+ * Adding [D -> i] -> i produces
+ *
+ * [D -> i] -> i + s(D)
+ *
+ * and the domain product with [D -> i] -> D yields
+ *
+ * [D -> i] -> [D -> i + s(D)]
+ *
+ * Composition with [D -> i] -> [D -> i/g] gives the desired result.
+ */
+static __isl_give isl_basic_map *check_stride(struct gpu_array_bound *bound,
+ __isl_take isl_basic_map *bounds)
+{
+ isl_space *space;
+ isl_basic_map *hull;
+ isl_basic_map *shift, *id, *bmap, *scale;
+ isl_basic_set *bset;
+ isl_aff *aff;
+
+ bound->stride = NULL;
+
+ hull = isl_basic_map_affine_hull(isl_basic_map_copy(bounds));
+
+ isl_basic_map_foreach_constraint(hull, &check_stride_constraint, bound);
+
+ isl_basic_map_free(hull);
+
+ if (!bound->stride)
+ return bounds;
+
+ shift = isl_basic_map_from_aff(isl_aff_copy(bound->shift));
+ space = isl_basic_map_get_space(bounds);
+ bmap = isl_basic_map_domain_map(isl_basic_map_universe(space));
+ shift = isl_basic_map_apply_range(bmap, shift);
+ space = isl_basic_map_get_space(bounds);
+ id = isl_basic_map_range_map(isl_basic_map_universe(space));
+ shift = isl_basic_map_sum(id, shift);
+ space = isl_basic_map_get_space(bounds);
+ id = isl_basic_map_domain_map(isl_basic_map_universe(space));
+ shift = isl_basic_map_range_product(id, shift);
+
+ space = isl_space_domain(isl_basic_map_get_space(bounds));
+ id = isl_basic_map_identity(isl_space_map_from_set(space));
+ space = isl_space_range(isl_basic_map_get_space(bounds));
+ aff = isl_aff_zero_on_domain(isl_local_space_from_space(space));
+ aff = isl_aff_add_coefficient_si(aff, isl_dim_in, 0, 1);
+ aff = isl_aff_scale_down_val(aff, isl_val_copy(bound->stride));
+ scale = isl_basic_map_from_aff(aff);
+ scale = isl_basic_map_product(id, scale);
+
+ bmap = isl_basic_map_apply_range(shift, scale);
+ bset = isl_basic_set_apply(isl_basic_map_wrap(bounds), bmap);
+ bounds = isl_basic_set_unwrap(bset);
+
+ return bounds;
+}
+
+/* Data used in compute_array_dim_size and compute_size_in_direction.
+ *
+ * pos is the position of the variable representing the array index,
+ * i.e., the variable for which want to compute the size. This variable
+ * is also the last variable in the set.
+ */
+struct gpu_size_info {
+ isl_basic_set *bset;
+ struct gpu_array_bound *bound;
+ int pos;
+};
+
+/* Given a constraint from the basic set describing the bounds on
+ * an array index, check if it is a lower bound, say m i >= b(x), and,
+ * if so, check whether the expression "i - ceil(b(x)/m) + 1" has a constant
+ * upper bound. If so, and if this bound is smaller than any bound
+ * derived from earlier constraints, set the size to this bound on
+ * the expression and the lower bound to ceil(b(x)/m).
+ */
+static isl_stat compute_size_in_direction(__isl_take isl_constraint *c,
+ void *user)
+{
+ struct gpu_size_info *size = user;
+ unsigned nparam;
+ unsigned n_div;
+ isl_val *v;
+ isl_aff *aff;
+ isl_aff *lb;
+
+ nparam = isl_basic_set_dim(size->bset, isl_dim_param);
+ n_div = isl_constraint_dim(c, isl_dim_div);
+
+ if (isl_constraint_involves_dims(c, isl_dim_div, 0, n_div) ||
+ !isl_constraint_is_lower_bound(c, isl_dim_set, size->pos)) {
+ isl_constraint_free(c);
+ return isl_stat_ok;
+ }
+
+ aff = isl_constraint_get_bound(c, isl_dim_set, size->pos);
+ aff = isl_aff_ceil(aff);
+
+ lb = isl_aff_copy(aff);
+
+ aff = isl_aff_neg(aff);
+ aff = isl_aff_add_coefficient_si(aff, isl_dim_in, size->pos, 1);
+
+ v = isl_basic_set_max_val(size->bset, aff);
+ isl_aff_free(aff);
+
+ if (isl_val_is_int(v)) {
+ v = isl_val_add_ui(v, 1);
+ if (!size->bound->size || isl_val_lt(v, size->bound->size)) {
+ isl_val_free(size->bound->size);
+ size->bound->size = isl_val_copy(v);
+ lb = isl_aff_drop_dims(lb, isl_dim_in, size->pos, 1);
+ isl_aff_free(size->bound->lb);
+ size->bound->lb = isl_aff_copy(lb);
+ }
+ }
+ isl_val_free(v);
+ isl_aff_free(lb);
+
+ isl_constraint_free(c);
+
+ return isl_stat_ok;
+}
+
+/* Given a basic map "bounds" that maps parameters and input dimensions
+ * to a single output dimension, look for an expression in the parameters
+ * and input dimensions such that the range of the output dimension shifted
+ * by this expression is a constant.
+ *
+ * In particular, we currently only consider lower bounds on the output
+ * dimension as candidate expressions.
+ */
+static int compute_array_dim_size(struct gpu_array_bound *bound,
+ __isl_take isl_basic_map *bounds)
+{
+ struct gpu_size_info size;
+
+ bounds = isl_basic_map_detect_equalities(bounds);
+ bounds = check_stride(bound, bounds);
+
+ bound->size = NULL;
+ bound->lb = NULL;
+
+ size.bound = bound;
+ size.pos = isl_basic_map_dim(bounds, isl_dim_in);
+ size.bset = isl_basic_map_wrap(bounds);
+ size.bset = isl_basic_set_flatten(size.bset);
+ size.bset = isl_set_simple_hull(isl_basic_set_compute_divs(size.bset));
+ isl_basic_set_foreach_constraint(size.bset, &compute_size_in_direction,
+ &size);
+ isl_basic_set_free(size.bset);
+
+ return bound->size ? 0 : -1;
+}
+
+/* Check if we can find a memory tile for the given array
+ * based on the given accesses, and if so, put the results in "tile".
+ *
+ * We project the accesses on each index in turn and look for a parametric
+ * offset such that the size is constant.
+ *
+ * tile->depth is initialized to the input dimension of the computed bounds.
+ */
+static int can_tile(__isl_keep isl_map *access, struct gpu_array_tile *tile)
+{
+ int i;
+
+ tile->depth = isl_map_dim(access, isl_dim_in);
+
+ for (i = 0; i < tile->n; ++i) {
+ isl_map *access_i;
+ isl_basic_map *hull;
+
+ access_i = isl_map_copy(access);
+ access_i = isl_map_project_out(access_i, isl_dim_out, 0, i);
+ access_i = isl_map_project_out(access_i, isl_dim_out,
+ 1, tile->n - (i + 1));
+ access_i = isl_map_compute_divs(access_i);
+ hull = isl_map_simple_hull(access_i);
+ if (compute_array_dim_size(&tile->bound[i], hull) < 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Internal data structure for gpu_group_references.
+ *
+ * scop represents the input scop.
+ * kernel_depth is the schedule depth where the kernel launch will
+ * be introduced, i.e., it is the depth of the band that is mapped
+ * to blocks.
+ * shared_depth is the schedule depth at which the copying to/from
+ * shared memory is computed. The copy operation may then
+ * later be hoisted to a higher level.
+ * thread_depth is the schedule depth where the thread mark is located,
+ * i.e., it is the depth of the band that is mapped to threads and also
+ * the schedule depth at which the copying to/from private memory
+ * is computed. The copy operation may then later be hoisted to
+ * a higher level.
+ * n_thread is the number of schedule dimensions in the band that
+ * is mapped to threads.
+ * privatization lives in the range of thread_sched (i.e., it is
+ * of dimension thread_depth + n_thread) and encodes the mapping
+ * to thread identifiers (as parameters).
+ * host_sched contains the kernel_depth dimensions of the host schedule.
+ * shared_sched contains the first shared_depth dimensions of the
+ * kernel schedule.
+ * copy_sched contains the first thread_depth dimensions of the
+ * kernel schedule.
+ * thread_sched contains the first (thread_depth + n_thread) dimensions
+ * of the kernel schedule.
+ * full_sched is a union_map representation of the entire kernel schedule.
+ * The schedules are all formulated in terms of the original statement
+ * instances, i.e., those that appear in the domains of the access
+ * relations.
+ */
+struct gpu_group_data {
+ struct ppcg_scop *scop;
+ int kernel_depth;
+ int shared_depth;
+ int thread_depth;
+ int n_thread;
+ isl_set *privatization;
+ isl_union_map *host_sched;
+ isl_union_map *shared_sched;
+ isl_union_map *copy_sched;
+ isl_union_map *thread_sched;
+ isl_union_map *full_sched;
+};
+
+/* Construct a map from domain_space to domain_space that increments
+ * the dimension at position "pos" and leaves all other dimensions
+ * constant.
+ */
+static __isl_give isl_map *next(__isl_take isl_space *domain_space, int pos)
+{
+ isl_space *space;
+ isl_aff *aff;
+ isl_multi_aff *next;
+
+ space = isl_space_map_from_set(domain_space);
+ next = isl_multi_aff_identity(space);
+ aff = isl_multi_aff_get_aff(next, pos);
+ aff = isl_aff_add_constant_si(aff, 1);
+ next = isl_multi_aff_set_aff(next, pos, aff);
+
+ return isl_map_from_multi_aff(next);
+}
+
+/* Check if the given access is coalesced (or if there is no point
+ * in trying to coalesce the access by mapping the array to shared memory).
+ * That is, check whether incrementing the dimension that will get
+ * wrapped over the last thread index results in incrementing
+ * the last array index.
+ *
+ * If no two consecutive array elements are ever accessed by "access",
+ * then mapping the corresponding array to shared memory will not
+ * improve coalescing. In fact, the copying will likely be performed
+ * by a single thread. Consider the access as coalesced such that
+ * the caller will not try and map the array to shared memory just
+ * to improve coalescing.
+ *
+ * This function is only called for access relations without reuse and
+ * kernels with at least one thread identifier.
+ */
+static int access_is_coalesced(struct gpu_group_data *data,
+ __isl_keep isl_union_map *access)
+{
+ int dim;
+ isl_space *space;
+ isl_set *accessed;
+ isl_map *access_map;
+ isl_map *next_thread_x;
+ isl_map *next_element;
+ isl_map *map;
+ int coalesced, empty;
+
+ access = isl_union_map_copy(access);
+ access = isl_union_map_apply_domain(access,
+ isl_union_map_copy(data->full_sched));
+ access_map = isl_map_from_union_map(access);
+
+ space = isl_map_get_space(access_map);
+ space = isl_space_range(space);
+ dim = isl_space_dim(space, isl_dim_set);
+ if (dim == 0)
+ next_element = isl_map_empty(isl_space_map_from_set(space));
+ else
+ next_element = next(space, dim - 1);
+
+ accessed = isl_map_range(isl_map_copy(access_map));
+ map = isl_map_copy(next_element);
+ map = isl_map_intersect_domain(map, isl_set_copy(accessed));
+ map = isl_map_intersect_range(map, accessed);
+ empty = isl_map_is_empty(map);
+ isl_map_free(map);
+
+ if (empty < 0 || empty) {
+ isl_map_free(next_element);
+ isl_map_free(access_map);
+ return empty;
+ }
+
+ space = isl_map_get_space(access_map);
+ space = isl_space_domain(space);
+ next_thread_x = next(space, data->thread_depth + data->n_thread - 1);
+
+ map = isl_map_apply_domain(next_thread_x, isl_map_copy(access_map));
+ map = isl_map_apply_range(map, access_map);
+
+ coalesced = isl_map_is_subset(map, next_element);
+
+ isl_map_free(next_element);
+ isl_map_free(map);
+
+ return coalesced;
+}
+
+/* Replace the host schedule dimensions in the access relation "access"
+ * by parameters, so that they are treated as fixed when checking for reuse
+ * (within a kernel) or whether two consecutive elements are accessed
+ * (within a kernel).
+ */
+static __isl_give isl_union_map *localize_access(struct gpu_group_data *data,
+ __isl_take isl_union_map *access)
+{
+ int n;
+ isl_space *space;
+ isl_set *param;
+ isl_union_map *umap;
+ isl_id_list *ids;
+
+ umap = isl_union_map_copy(data->host_sched);
+ space = isl_union_map_get_space(umap);
+ n = data->kernel_depth;
+ ids = ppcg_scop_generate_names(data->scop, n, "__ppcg_host_");
+ param = parametrization(space, n, 0, ids);
+ isl_id_list_free(ids);
+ umap = isl_union_map_intersect_range(umap,
+ isl_union_set_from_set(param));
+ access = isl_union_map_intersect_domain(access,
+ isl_union_map_domain(umap));
+
+ return access;
+}
+
+/* Given an access relation in terms of at least data->thread_depth initial
+ * dimensions of the computed schedule, check if it is bijective for
+ * fixed values of the first data->thread_depth dimensions.
+ * We perform this check by equating these dimensions to parameters.
+ */
+static int access_is_bijective(struct gpu_group_data *data,
+ __isl_keep isl_map *access)
+{
+ int res;
+ int dim;
+ isl_set *par;
+ isl_space *space;
+ isl_id_list *ids;
+
+ access = isl_map_copy(access);
+ space = isl_space_params(isl_map_get_space(access));
+ ids = ppcg_scop_generate_names(data->scop, data->thread_depth, "s");
+ dim = isl_map_dim(access, isl_dim_in);
+ par = parametrization(space, dim, 0, ids);
+ isl_id_list_free(ids);
+ access = isl_map_intersect_domain(access, par);
+ res = isl_map_is_bijective(access);
+ isl_map_free(access);
+
+ return res;
+}
+
+/* Compute the number of outer schedule tile dimensions that affect
+ * the offset of "tile".
+ * If there is no such dimension, then return the index
+ * of the first kernel dimension, i.e., data->kernel_depth.
+ */
+static int compute_tile_depth(struct gpu_group_data *data,
+ struct gpu_array_tile *tile)
+{
+ int i, j;
+
+ for (j = tile->depth - 1; j >= data->kernel_depth; --j) {
+ for (i = 0; i < tile->n; ++i) {
+ isl_aff *lb;
+ isl_aff *shift;
+
+ lb = tile->bound[i].lb;
+ if (isl_aff_involves_dims(lb, isl_dim_in, j, 1))
+ break;
+
+ shift = tile->bound[i].shift;
+ if (!shift)
+ continue;
+ if (isl_aff_involves_dims(shift, isl_dim_in, j, 1))
+ break;
+ }
+ if (i < tile->n)
+ break;
+ }
+
+ return ++j;
+}
+
+/* Return the lowest depth between data->kernel_depth and data->thread_depth
+ * at which every array element accessed through "acc" is accessed
+ * by a single thread. The input dimension of "acc" is
+ * data->thread_depth + data->n_thread, where the final data->n_thread
+ * dimensions are those that will be mapped to threads.
+ * If the values for these dimensions are uniquely determined
+ * by the array index and a given number of outer dimensions, then
+ * there is only one thread accessing that array element within those
+ * outer dimensions.
+ *
+ * The input space of "acc" is first split up, such that it has the form
+ *
+ * [O -> T] -> A
+ *
+ * with O the outer dimensions, T the dimensions that will be mapped to threads
+ * and A the array index.
+ *
+ * Then the positions of T and A are interchanged to simplify the test
+ * whether T uniquely depends on O and A.
+ * In particular, the above access relation is first combined with
+ *
+ * [O -> T] -> T
+ *
+ * to form
+ *
+ * [O -> T] -> [A -> T]
+ *
+ * from which
+ *
+ * O -> [A -> T]
+ *
+ * is extracted, which is then uncurried to
+ *
+ * [O -> A] -> T
+ *
+ * Finally, the final dimensions of O are projected out one by one
+ * until T is no longer uniquely determined by A and the remaining
+ * dimensions in O. The value returned is that of the last dimension
+ * that was successfully projected out.
+ * Note that there is no need to test whether [O -> A] -> T itself
+ * is single-valued as that was already tested in access_is_bijective.
+ */
+static int compute_accessed_by_single_thread_depth(struct gpu_group_data *data,
+ __isl_keep isl_map *acc)
+{
+ int i;
+ isl_space *space;
+ isl_map *map;
+ isl_bool sv;
+
+ if (data->thread_depth == data->kernel_depth)
+ return data->thread_depth;
+
+ acc = isl_map_copy(acc);
+
+ space = isl_map_get_space(acc);
+ space = isl_space_params(space);
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, data->thread_depth);
+ space = isl_space_from_domain(space);
+ space = isl_space_add_dims(space, isl_dim_out, data->n_thread);
+ space = isl_space_wrap(space);
+ map = isl_set_flatten_map(isl_set_universe(space));
+ acc = isl_map_apply_range(map, acc);
+
+ space = isl_space_domain(isl_map_get_space(acc));
+ map = isl_map_range_map(isl_map_universe(isl_space_unwrap(space)));
+ acc = isl_map_range_product(acc, map);
+ acc = isl_map_domain_factor_domain(acc);
+ acc = isl_map_uncurry(acc);
+
+ for (i = data->thread_depth - 1; i >= data->kernel_depth; --i) {
+ acc = isl_map_project_out(acc, isl_dim_in, i, 1);
+ sv = isl_map_is_single_valued(acc);
+ if (sv < 0)
+ return -1;
+ if (!sv)
+ break;
+ }
+
+ isl_map_free(acc);
+
+ return ++i;
+}
+
+/* Adjust the fields of "tile" to reflect the new input dimension "depth".
+ * The dimension beyond "depth" are assumed not to affect the tile,
+ * so they can simply be dropped.
+ */
+static int tile_adjust_depth(struct gpu_array_tile *tile, int depth)
+{
+ int i;
+
+ if (tile->depth == depth)
+ return 0;
+
+ for (i = 0; i < tile->n; ++i) {
+ tile->bound[i].lb = isl_aff_drop_dims(tile->bound[i].lb,
+ isl_dim_in, depth, tile->depth - depth);
+ if (!tile->bound[i].lb)
+ return -1;
+ if (!tile->bound[i].shift)
+ continue;
+ tile->bound[i].shift = isl_aff_drop_dims(tile->bound[i].shift,
+ isl_dim_in, depth, tile->depth - depth);
+ if (!tile->bound[i].shift)
+ return -1;
+ }
+
+ tile->depth = depth;
+
+ return 0;
+}
+
+/* Determine the number of schedule dimensions that affect the offset of the
+ * shared or private tile "tile" and store the result in tile->depth, with
+ * a lower bound of data->kernel_depth.
+ * Also adjust the fields of the tile to only refer to the tile->depth
+ * outer schedule dimensions.
+ */
+static isl_stat tile_set_depth(struct gpu_group_data *data,
+ struct gpu_array_tile *tile)
+{
+ if (tile_adjust_depth(tile, compute_tile_depth(data, tile)) < 0)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Determine the number of schedule dimensions that affect the offset of the
+ * shared tile and store the minimum of the private and shared tile depth
+ * in group->min_depth, with a lower bound of data->kernel_depth.
+ * If there is no tile defined on the array reference group,
+ * then set group->min_depth to data->thread_depth.
+ */
+static int set_depth(struct gpu_group_data *data,
+ struct gpu_array_ref_group *group)
+{
+ group->min_depth = data->thread_depth;
+
+ if (group->private_tile) {
+ if (group->private_tile->depth < group->min_depth)
+ group->min_depth = group->private_tile->depth;
+ }
+ if (group->shared_tile) {
+ if (tile_set_depth(data, group->shared_tile) < 0)
+ return -1;
+ if (group->shared_tile->depth < group->min_depth)
+ group->min_depth = group->shared_tile->depth;
+ }
+
+ return 0;
+}
+
+/* Fill up the groups array with singleton groups, i.e., one group
+ * per reference, initializing the array, access, write, n_ref and refs fields.
+ * In particular the access field is initialized to the scheduled
+ * access relation of the array reference.
+ *
+ * Return the number of elements initialized, i.e., the number of
+ * active references in the current kernel.
+ */
+static int populate_array_references(struct gpu_local_array_info *local,
+ struct gpu_array_ref_group **groups, struct gpu_group_data *data)
+{
+ int i;
+ int n;
+ isl_ctx *ctx = isl_union_map_get_ctx(data->copy_sched);
+
+ n = 0;
+ for (i = 0; i < local->array->n_ref; ++i) {
+ isl_union_map *umap;
+ isl_map *map;
+ struct gpu_array_ref_group *group;
+ struct gpu_stmt_access *access = local->array->refs[i];
+
+ map = isl_map_copy(access->access);
+ umap = isl_union_map_from_map(map);
+ umap = isl_union_map_apply_domain(umap,
+ isl_union_map_copy(data->copy_sched));
+
+ if (isl_union_map_is_empty(umap)) {
+ isl_union_map_free(umap);
+ continue;
+ }
+
+ map = isl_map_from_union_map(umap);
+ map = isl_map_detect_equalities(map);
+
+ group = isl_calloc_type(ctx, struct gpu_array_ref_group);
+ if (!group)
+ return -1;
+ group->local_array = local;
+ group->array = local->array;
+ group->access = map;
+ group->write = access->write;
+ group->exact_write = access->exact_write;
+ group->slice = access->n_index < local->array->n_index;
+ group->refs = &local->array->refs[i];
+ group->n_ref = 1;
+
+ groups[n++] = group;
+ }
+
+ return n;
+}
+
+/* If group->n_ref == 1, then group->refs was set by
+ * populate_array_references to point directly into
+ * group->array->refs and should not be freed.
+ * If group->n_ref > 1, then group->refs was set by join_groups
+ * to point to a newly allocated array.
+ */
+struct gpu_array_ref_group *gpu_array_ref_group_free(
+ struct gpu_array_ref_group *group)
+{
+ if (!group)
+ return NULL;
+ gpu_array_tile_free(group->shared_tile);
+ gpu_array_tile_free(group->private_tile);
+ isl_map_free(group->access);
+ if (group->n_ref > 1)
+ free(group->refs);
+ free(group);
+ return NULL;
+}
+
+/* Check if the access relations of group1 and group2 overlap within
+ * copy_sched.
+ */
+static int accesses_overlap(struct gpu_array_ref_group *group1,
+ struct gpu_array_ref_group *group2)
+{
+ int disjoint;
+
+ disjoint = isl_map_is_disjoint(group1->access, group2->access);
+ if (disjoint < 0)
+ return -1;
+
+ return !disjoint;
+}
+
+/* Combine the given two groups into a single group, containing
+ * the references of both groups.
+ */
+static struct gpu_array_ref_group *join_groups(
+ struct gpu_array_ref_group *group1,
+ struct gpu_array_ref_group *group2)
+{
+ int i;
+ isl_ctx *ctx;
+ struct gpu_array_ref_group *group;
+
+ if (!group1 || !group2)
+ return NULL;
+
+ ctx = isl_map_get_ctx(group1->access);
+ group = isl_calloc_type(ctx, struct gpu_array_ref_group);
+ if (!group)
+ return NULL;
+ group->local_array = group1->local_array;
+ group->array = group1->array;
+ group->access = isl_map_union(isl_map_copy(group1->access),
+ isl_map_copy(group2->access));
+ group->write = group1->write || group2->write;
+ group->exact_write = group1->exact_write && group2->exact_write;
+ group->slice = group1->slice || group2->slice;
+ group->n_ref = group1->n_ref + group2->n_ref;
+ group->refs = isl_alloc_array(ctx, struct gpu_stmt_access *,
+ group->n_ref);
+ if (!group->refs)
+ return gpu_array_ref_group_free(group);
+ for (i = 0; i < group1->n_ref; ++i)
+ group->refs[i] = group1->refs[i];
+ for (i = 0; i < group2->n_ref; ++i)
+ group->refs[group1->n_ref + i] = group2->refs[i];
+
+ return group;
+}
+
+/* Combine the given two groups into a single group and free
+ * the original two groups.
+ */
+static struct gpu_array_ref_group *join_groups_and_free(
+ struct gpu_array_ref_group *group1,
+ struct gpu_array_ref_group *group2)
+{
+ struct gpu_array_ref_group *group;
+
+ group = join_groups(group1, group2);
+ gpu_array_ref_group_free(group1);
+ gpu_array_ref_group_free(group2);
+ return group;
+}
+
+/* Report that the array reference group with the given access relation
+ * is not mapped to shared memory in the given kernel because
+ * it does not exhibit any reuse and is considered to be coalesced.
+ */
+static void report_no_reuse_and_coalesced(struct ppcg_kernel *kernel,
+ __isl_keep isl_union_map *access)
+{
+ isl_ctx *ctx;
+ isl_printer *p;
+
+ ctx = isl_union_map_get_ctx(access);
+ p = isl_printer_to_file(ctx, stdout);
+ p = isl_printer_print_str(p, "Array reference group ");
+ p = isl_printer_print_union_map(p, access);
+ p = isl_printer_print_str(p,
+ " not considered for mapping to shared memory in kernel");
+ p = isl_printer_print_int(p, kernel->id);
+ p = isl_printer_print_str(p,
+ " because it exhibits no reuse and is considered to be coalesced");
+ p = isl_printer_end_line(p);
+ isl_printer_free(p);
+}
+
+/* Given an access relation in terms of the data->thread_depth initial
+ * dimensions of the computed schedule and the thread identifiers
+ * (as parameters), check if the use of the corresponding private tile
+ * requires unrolling.
+ *
+ * If we are creating a private tile because we are forced to,
+ * then no unrolling is required.
+ * Otherwise we check if "access" is bijective and unrolling
+ * is required if it is not. Note that the access relation
+ * has already been determined to be bijective before the introduction
+ * of the thread identifiers and the removal of the schedule dimensions
+ * that are mapped to these threads. If the access relation is no longer
+ * bijective, then this means that more than one value of one of those
+ * schedule dimensions is mapped to the same thread and therefore
+ * unrolling is required.
+ */
+static int check_requires_unroll(struct gpu_group_data *data,
+ __isl_keep isl_map *access, int force_private)
+{
+ int bijective;
+
+ if (force_private)
+ return 0;
+ bijective = access_is_bijective(data, access);
+ if (bijective < 0)
+ return -1;
+ return !bijective;
+}
+
+/* Map the domain of "access" to the outer data->shared_depth
+ * schedule dimensions. When data->shared_depth is equal to
+ * data->thread_depth, this result is already available in group->access.
+ */
+static __isl_give isl_map *shared_access(struct gpu_array_ref_group *group,
+ __isl_keep isl_union_map *access, struct gpu_group_data *data)
+{
+ isl_union_map *shared;
+
+ if (data->shared_depth == data->thread_depth)
+ return isl_map_copy(group->access);
+
+ shared = isl_union_map_copy(access);
+ shared = isl_union_map_apply_domain(shared,
+ isl_union_map_copy(data->shared_sched));
+ return isl_map_from_union_map(shared);
+}
+
+/* Compute the private and/or shared memory tiles for the array
+ * reference group "group" of array "array".
+ * Return 0 on success and -1 on error.
+ *
+ * If the array is a read-only scalar or if the user requested
+ * not to use shared or private memory, then we do not need to do anything.
+ *
+ * If any reference in the reference group accesses more than one element,
+ * then we would have to make sure that the layout in shared memory
+ * is the same as that in global memory. Since we do not handle this yet
+ * (and it may not even be possible), we refuse to map to private or
+ * shared memory in such cases.
+ *
+ * If the array group involves any may writes (that are not must writes),
+ * then we would have to make sure that we load the data into shared/private
+ * memory first in case the data is not written by the kernel
+ * (but still written back out to global memory).
+ * Since we don't have any such mechanism at the moment, we don't
+ * compute shared/private tiles for groups involving may writes.
+ *
+ * We only try to compute a shared memory tile if there is any reuse
+ * or if the access is not coalesced.
+ * Reuse and coalescing are checked within the given kernel.
+ *
+ * For computing a private memory tile, we also require that there is
+ * some reuse. Moreover, we require that the access is private
+ * to the thread. That is, we check that any given array element
+ * is only accessed by a single thread.
+ * We compute an access relation that maps the outer
+ * data->thread_depth + data->n_thread schedule dimensions.
+ * The latter data->n_thread will be mapped to thread identifiers.
+ * We actually check that those iterators that will be wrapped
+ * partition the array space. This check is stricter than necessary
+ * since several iterations may be mapped onto the same thread
+ * and then they could be allowed to access the same memory elements,
+ * but our check does not allow this situation.
+ *
+ * For private memory tiles, the number of schedule dimensions that
+ * affect the offset is computed and stored in tile->depth, with
+ * a lower bound of data->kernel_depth. If this depth is smaller
+ * than the minimal depth that still ensures that every element
+ * is accessed by a single thread, then the depth is raised
+ * to this minimal depth.
+ * The fields of the tile are then adjusted to only refer to the tile->depth
+ * outer schedule dimensions.
+ *
+ * We also check that the index expression only depends on parallel
+ * loops. That way, we can move those loops innermost and unroll them.
+ * Again, we use a test that is stricter than necessary.
+ * We actually check whether the index expression only depends
+ * on the iterators that are wrapped over the threads.
+ * These are necessarily parallel, but there may be more parallel loops.
+ *
+ * Combining the injectivity of the first test with the single-valuedness
+ * of the second test, we simply test for bijectivity.
+ *
+ * If the use of the private tile requires unrolling, but some
+ * of the other arrays are forcibly mapped to private memory,
+ * then we do not allow the use of this private tile since
+ * we cannot move the schedule dimensions that need to be unrolled down
+ * without performing some kind of expansion on those arrays
+ * that are forcibly mapped to private memory.
+ *
+ * If the array is marked force_private, then we bypass all checks
+ * and assume we can (and should) use registers only.
+ *
+ * If it turns out we can (or have to) use registers, we compute
+ * the private memory tile size using can_tile, after introducing a dependence
+ * on the thread indices.
+ */
+static int compute_group_bounds_core(struct ppcg_kernel *kernel,
+ struct gpu_array_ref_group *group, struct gpu_group_data *data)
+{
+ isl_ctx *ctx = isl_space_get_ctx(group->array->space);
+ isl_union_map *access, *local;
+ int n_index = group->array->n_index;
+ int no_reuse, coalesced;
+ isl_map *acc;
+ int force_private = group->local_array->force_private;
+ int use_shared = !force_private && kernel->options->use_shared_memory &&
+ data->n_thread > 0;
+ int use_private = force_private || kernel->options->use_private_memory;
+ int r = 0;
+ int requires_unroll;
+ int unique_depth;
+
+ if (!use_shared && !use_private)
+ return 0;
+ if (gpu_array_is_read_only_scalar(group->array))
+ return 0;
+ if (!force_private && !group->exact_write)
+ return 0;
+ if (group->slice)
+ return 0;
+
+ access = gpu_array_ref_group_access_relation(group, 1, 1);
+ local = localize_access(data, isl_union_map_copy(access));
+ no_reuse = isl_union_map_is_injective(local);
+ if (no_reuse < 0)
+ r = -1;
+ if (use_shared && no_reuse)
+ coalesced = access_is_coalesced(data, local);
+ isl_union_map_free(local);
+
+ if (r >= 0 && kernel->options->debug->verbose &&
+ use_shared && no_reuse && coalesced)
+ report_no_reuse_and_coalesced(kernel, access);
+
+ if (use_shared && (!no_reuse || !coalesced)) {
+ group->shared_tile = gpu_array_tile_create(ctx,
+ group->array->n_index);
+ acc = shared_access(group, access, data);
+ if (!group->shared_tile)
+ r = -1;
+ else if (!can_tile(acc, group->shared_tile))
+ group->shared_tile =
+ gpu_array_tile_free(group->shared_tile);
+ isl_map_free(acc);
+ }
+
+ if (r < 0 || (!force_private && (!use_private || no_reuse))) {
+ isl_union_map_free(access);
+ return r;
+ }
+
+ access = isl_union_map_apply_domain(access,
+ isl_union_map_copy(data->thread_sched));
+
+ acc = isl_map_from_union_map(access);
+
+ if (!force_private && !access_is_bijective(data, acc)) {
+ isl_map_free(acc);
+ return 0;
+ }
+
+ unique_depth = compute_accessed_by_single_thread_depth(data, acc);
+
+ acc = isl_map_intersect_domain(acc, isl_set_copy(data->privatization));
+ acc = isl_map_project_out(acc, isl_dim_in, data->thread_depth,
+ data->n_thread);
+ requires_unroll = check_requires_unroll(data, acc, force_private);
+ if (unique_depth < 0 || requires_unroll < 0 ||
+ (requires_unroll && kernel->any_force_private)) {
+ isl_map_free(acc);
+ return requires_unroll < 0 ? -1 : 0;
+ }
+
+ group->private_tile = gpu_array_tile_create(ctx, n_index);
+ if (!group->private_tile) {
+ isl_map_free(acc);
+ return -1;
+ }
+ group->private_tile->requires_unroll = requires_unroll;
+ if (!can_tile(acc, group->private_tile))
+ group->private_tile = gpu_array_tile_free(group->private_tile);
+
+ isl_map_free(acc);
+
+ if (group->private_tile) {
+ struct gpu_array_tile *tile = group->private_tile;
+ int tile_depth = compute_tile_depth(data, tile);
+ if (tile_depth < unique_depth)
+ tile_depth = unique_depth;
+ if (tile_adjust_depth(tile, tile_depth) < 0)
+ return -1;
+ }
+
+ if (force_private && !group->private_tile)
+ isl_die(ctx, isl_error_internal,
+ "unable to map array reference group to registers",
+ return -1);
+
+ return 0;
+}
+
+/* Compute the private and/or shared memory tiles for the array
+ * reference group "group" of array "array" and set the tile depth.
+ * Return 0 on success and -1 on error.
+ */
+static int compute_group_bounds(struct ppcg_kernel *kernel,
+ struct gpu_array_ref_group *group, struct gpu_group_data *data)
+{
+ if (!group)
+ return -1;
+ if (compute_group_bounds_core(kernel, group, data) < 0)
+ return -1;
+ if (set_depth(data, group) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* If two groups have overlapping access relations (as determined by
+ * the "overlap" function) and if one of them involves a write,
+ * then merge the two groups into one.
+ * If "compute_bounds" is set, then call compute_group_bounds
+ * on the merged groups.
+ *
+ * Return the updated number of groups.
+ * Return -1 on error.
+ */
+static int group_writes(struct ppcg_kernel *kernel,
+ int n, struct gpu_array_ref_group **groups,
+ int (*overlap)(struct gpu_array_ref_group *group1,
+ struct gpu_array_ref_group *group2), int compute_bounds,
+ struct gpu_group_data *data)
+{
+ int i, j;
+
+ for (i = 0; i < n; ++i) {
+ for (j = n - 1; j > i; --j) {
+ if (!groups[i]->write && !groups[j]->write)
+ continue;
+
+ if (!overlap(groups[i], groups[j]))
+ continue;
+
+ groups[i] = join_groups_and_free(groups[i], groups[j]);
+ if (j != n - 1)
+ groups[j] = groups[n - 1];
+ groups[n - 1] = NULL;
+ n--;
+
+ if (!groups[i])
+ return -1;
+ if (compute_bounds &&
+ compute_group_bounds(kernel, groups[i], data) < 0)
+ return -1;
+ }
+ }
+
+ return n;
+}
+
+/* If two groups have overlapping access relations (within the innermost
+ * loop) and if one of them involves a write, then merge the two groups
+ * into one.
+ *
+ * Return the updated number of groups.
+ */
+static int group_overlapping_writes(struct ppcg_kernel *kernel,
+ int n, struct gpu_array_ref_group **groups,
+ struct gpu_group_data *data)
+{
+ return group_writes(kernel, n, groups, &accesses_overlap, 0, data);
+}
+
+/* Check if the access relations of group1 and group2 overlap within
+ * the outermost min(group1->min_depth, group2->min_depth) loops.
+ */
+static int depth_accesses_overlap(struct gpu_array_ref_group *group1,
+ struct gpu_array_ref_group *group2)
+{
+ int depth;
+ int dim;
+ int empty;
+ isl_map *map_i, *map_j, *map;
+
+ depth = group1->min_depth;
+ if (group2->min_depth < depth)
+ depth = group2->min_depth;
+ map_i = isl_map_copy(group1->access);
+ dim = isl_map_dim(map_i, isl_dim_in);
+ map_i = isl_map_eliminate(map_i, isl_dim_in, depth, dim - depth);
+ map_j = isl_map_copy(group2->access);
+ map_j = isl_map_eliminate(map_j, isl_dim_in, depth, dim - depth);
+ map = isl_map_intersect(map_i, map_j);
+ empty = isl_map_is_empty(map);
+ isl_map_free(map);
+
+ return !empty;
+}
+
+/* If two groups have overlapping access relations (within the outer
+ * depth loops) and if one of them involves a write,
+ * then merge the two groups into one.
+ *
+ * Return the updated number of groups.
+ */
+static int group_depth_overlapping_writes(struct ppcg_kernel *kernel,
+ int n, struct gpu_array_ref_group **groups, struct gpu_group_data *data)
+{
+ return group_writes(kernel, n, groups, &depth_accesses_overlap, 1,
+ data);
+}
+
+/* Is the size of the tile specified by "tile" smaller than the sum of
+ * the sizes of the tiles specified by "tile1" and "tile2"?
+ */
+static int smaller_tile(struct gpu_array_tile *tile,
+ struct gpu_array_tile *tile1, struct gpu_array_tile *tile2)
+{
+ int smaller;
+ isl_val *size, *size1, *size2;
+
+ size = gpu_array_tile_size(tile);
+ size1 = gpu_array_tile_size(tile1);
+ size2 = gpu_array_tile_size(tile2);
+
+ size = isl_val_sub(size, size1);
+ size = isl_val_sub(size, size2);
+ smaller = isl_val_is_neg(size);
+
+ isl_val_free(size);
+
+ return smaller;
+}
+
+/* Given an initial grouping of array references and shared memory tiles
+ * for each group that allows for a shared memory tile, merge two groups
+ * if both have a shared memory tile, the merged group also has
+ * a shared memory tile and the size of the tile for the merge group
+ * is smaller than the sum of the tile sizes of the individual groups.
+ *
+ * If merging two groups decreases the depth of the tile of
+ * one or both of the two groups, then we need to check for overlapping
+ * writes again.
+ *
+ * Return the number of groups after merging.
+ * Return -1 on error.
+ */
+static int group_common_shared_memory_tile(struct ppcg_kernel *kernel,
+ struct gpu_array_info *array, int n,
+ struct gpu_array_ref_group **groups, struct gpu_group_data *data)
+{
+ int i, j;
+ int recompute_overlap = 0;
+
+ for (i = 0; i < n; ++i) {
+ if (!groups[i]->shared_tile)
+ continue;
+ for (j = n - 1; j > i; --j) {
+ struct gpu_array_ref_group *group;
+
+ if (!groups[j]->shared_tile)
+ continue;
+
+ if (!depth_accesses_overlap(groups[i], groups[j]))
+ continue;
+
+ group = join_groups(groups[i], groups[j]);
+ if (compute_group_bounds(kernel, group, data) < 0) {
+ gpu_array_ref_group_free(group);
+ return -1;
+ }
+ if (!group->shared_tile ||
+ !smaller_tile(group->shared_tile,
+ groups[i]->shared_tile,
+ groups[j]->shared_tile)) {
+ gpu_array_ref_group_free(group);
+ continue;
+ }
+
+ if (group->min_depth < groups[i]->min_depth ||
+ group->min_depth < groups[j]->min_depth)
+ recompute_overlap = 1;
+ gpu_array_ref_group_free(groups[i]);
+ gpu_array_ref_group_free(groups[j]);
+ groups[i] = group;
+ if (j != n - 1)
+ groups[j] = groups[n - 1];
+ n--;
+ }
+ }
+
+ if (recompute_overlap)
+ n = group_depth_overlapping_writes(kernel, n, groups, data);
+ return n;
+}
+
+/* Set array->n_group and array->groups to n and groups.
+ *
+ * Additionally, set the "nr" field of each group.
+ */
+static void set_array_groups(struct gpu_local_array_info *array,
+ int n, struct gpu_array_ref_group **groups)
+{
+ int i;
+
+ array->n_group = n;
+ array->groups = groups;
+
+ for (i = 0; i < n; ++i)
+ groups[i]->nr = i;
+}
+
+/* Combine all groups in "groups" into a single group and return
+ * the new number of groups (1 or 0 if there were no groups to start with).
+ */
+static int join_all_groups(int n, struct gpu_array_ref_group **groups)
+{
+ int i;
+
+ for (i = n - 1; i > 0; --i) {
+ groups[0] = join_groups_and_free(groups[0], groups[i]);
+ groups[i] = NULL;
+ n--;
+ }
+
+ return n;
+}
+
+/* Group array references that should be considered together when
+ * deciding whether to access them from private, shared or global memory.
+ * Return -1 on error.
+ *
+ * In particular, if two array references overlap and if one of them
+ * is a write, then the two references are grouped together.
+ * We first perform an initial grouping based only on the access relation.
+ * After computing shared and private memory tiles, we check for
+ * overlapping writes again, but this time taking into account
+ * the depth of the effective tile.
+ *
+ * Furthermore, if two groups admit a shared memory tile and if the
+ * combination of the two also admits a shared memory tile, we merge
+ * the two groups.
+ *
+ * If the array contains structures, then we compute a single
+ * reference group without trying to find any tiles
+ * since we do not map such arrays to private or shared
+ * memory. The only exception is when those arrays of structures
+ * are required to be mapped to private memory.
+ */
+static int group_array_references(struct ppcg_kernel *kernel,
+ struct gpu_local_array_info *local, struct gpu_group_data *data)
+{
+ int i;
+ int n;
+ isl_ctx *ctx = isl_union_map_get_ctx(data->shared_sched);
+ struct gpu_array_ref_group **groups;
+
+ groups = isl_calloc_array(ctx, struct gpu_array_ref_group *,
+ local->array->n_ref);
+ if (!groups)
+ return -1;
+
+ n = populate_array_references(local, groups, data);
+
+ if (local->array->has_compound_element && !local->force_private) {
+ n = join_all_groups(n, groups);
+ set_array_groups(local, n, groups);
+ return 0;
+ }
+
+ n = group_overlapping_writes(kernel, n, groups, data);
+
+ for (i = 0; i < n; ++i)
+ if (compute_group_bounds(kernel, groups[i], data) < 0)
+ n = -1;
+
+ n = group_depth_overlapping_writes(kernel, n, groups, data);
+
+ n = group_common_shared_memory_tile(kernel, local->array,
+ n, groups, data);
+
+ set_array_groups(local, n, groups);
+
+ if (n >= 0)
+ return 0;
+
+ for (i = 0; i < local->array->n_ref; ++i)
+ gpu_array_ref_group_free(groups[i]);
+ return -1;
+}
+
+/* For each array in the input program that can be mapped to private memory,
+ * check if there are any order dependences active inside the current kernel,
+ * within the same iteration of the host schedule, i.e., the prefix
+ * schedule at "node".
+ * If so, mark the array as force_private so that its reference groups will be
+ * mapped to a registers.
+ *
+ * Note that the arrays that cannot be mapped to private memory have
+ * had their order dependences added to prog->array_order and
+ * subsequently to the coincidence constraints.
+ */
+static void check_can_be_private_live_ranges(struct ppcg_kernel *kernel,
+ __isl_keep isl_schedule_node *node)
+{
+ int i;
+ isl_union_set *domain;
+ isl_multi_union_pw_aff *prefix;
+ isl_union_pw_multi_aff *contraction;
+
+ if (!kernel->options->live_range_reordering)
+ return;
+
+ kernel->any_force_private = 0;
+
+ prefix = isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
+ contraction = isl_union_pw_multi_aff_copy(kernel->contraction);
+ prefix = isl_multi_union_pw_aff_pullback_union_pw_multi_aff(prefix,
+ contraction);
+ domain = isl_union_set_copy(kernel->expanded_domain);
+ domain = isl_union_set_universe(domain);
+
+ for (i = 0; i < kernel->n_array; ++i) {
+ struct gpu_local_array_info *local = &kernel->array[i];
+ isl_union_map *order;
+
+ local->force_private = 0;
+ if (!gpu_array_can_be_private(local->array))
+ continue;
+ order = isl_union_map_copy(local->array->dep_order);
+ order = isl_union_map_intersect_domain(order,
+ isl_union_set_copy(domain));
+ order = isl_union_map_intersect_range(order,
+ isl_union_set_copy(domain));
+ order = isl_union_map_eq_at_multi_union_pw_aff(order,
+ isl_multi_union_pw_aff_copy(prefix));
+ if (!isl_union_map_is_empty(order)) {
+ local->force_private = 1;
+ kernel->any_force_private = 1;
+ }
+ isl_union_map_free(order);
+ }
+
+ isl_multi_union_pw_aff_free(prefix);
+ isl_union_set_free(domain);
+}
+
+/* Expand the domain of the schedule "s" by plugging in
+ * the contraction "contraction" and return the result.
+ */
+static __isl_give isl_union_map *expand(__isl_take isl_union_map *s,
+ __isl_keep isl_union_pw_multi_aff *contraction)
+{
+ contraction = isl_union_pw_multi_aff_copy(contraction);
+ s = isl_union_map_preimage_domain_union_pw_multi_aff(s, contraction);
+ return s;
+}
+
+/* Create a set of dimension data->thread_depth + data->n_thread
+ * that equates the residue of the final data->n_thread dimensions
+ * modulo the kernel->block_dim sizes to the thread identifiers.
+ * Store the computed set in data->privatization.
+ *
+ * The construction starts with the space of kernel->thread_filter,
+ * which is known to reference all thread identifiers.
+ */
+static void compute_privatization(struct gpu_group_data *data,
+ struct ppcg_kernel *kernel)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_space *space;
+ isl_local_space *ls;
+ isl_set *set;
+
+ ctx = isl_union_map_get_ctx(data->shared_sched);
+ space = isl_union_set_get_space(kernel->thread_filter);
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set,
+ data->thread_depth + data->n_thread);
+ set = isl_set_universe(space);
+ space = isl_set_get_space(set);
+ ls = isl_local_space_from_space(space);
+
+ for (i = 0; i < data->n_thread; ++i) {
+ isl_aff *aff, *aff2;
+ isl_constraint *c;
+ isl_val *v;
+ isl_id *id;
+ int pos;
+
+ aff = isl_aff_var_on_domain(isl_local_space_copy(ls),
+ isl_dim_set, data->thread_depth + i);
+ v = isl_val_int_from_si(ctx, kernel->block_dim[i]);
+ aff = isl_aff_mod_val(aff, v);
+ id = isl_id_list_get_id(kernel->thread_ids, i);
+ pos = isl_set_find_dim_by_id(set, isl_dim_param, id);
+ isl_id_free(id);
+ aff2 = isl_aff_var_on_domain(isl_local_space_copy(ls),
+ isl_dim_param, pos);
+ aff = isl_aff_sub(aff, aff2);
+ c = isl_equality_from_aff(aff);
+ set = isl_set_add_constraint(set, c);
+ }
+
+ isl_local_space_free(ls);
+ data->privatization = set;
+}
+
+/* Return the prefix schedule at "node" as a relation
+ * between domain elements and schedule dimensions after detecting
+ * equalities in this relation.
+ */
+static __isl_give isl_union_map *prefix_with_equalities(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_union_map *schedule;
+
+ schedule = isl_schedule_node_get_prefix_schedule_relation(node);
+ schedule = isl_union_map_detect_equalities(schedule);
+
+ return schedule;
+}
+
+/* Group references of all arrays in "kernel".
+ * "node" points to the kernel mark.
+ * The mapping to shared memory in computed at the "shared" mark.
+ *
+ * We first extract all required schedule information into
+ * a gpu_group_data structure and then consider each array
+ * in turn.
+ */
+int gpu_group_references(struct ppcg_kernel *kernel,
+ __isl_keep isl_schedule_node *node)
+{
+ int i;
+ int r = 0;
+ isl_union_pw_multi_aff *contraction;
+ struct gpu_group_data data;
+
+ check_can_be_private_live_ranges(kernel, node);
+
+ data.scop = kernel->prog->scop;
+
+ data.kernel_depth = isl_schedule_node_get_schedule_depth(node);
+ data.host_sched = isl_schedule_node_get_prefix_schedule_relation(node);
+
+ node = isl_schedule_node_copy(node);
+ node = gpu_tree_move_down_to_shared(node, kernel->core);
+ data.shared_depth = isl_schedule_node_get_schedule_depth(node);
+ data.shared_sched = prefix_with_equalities(node);
+
+ node = gpu_tree_move_down_to_thread(node, kernel->core);
+ node = isl_schedule_node_child(node, 0);
+ data.thread_depth = isl_schedule_node_get_schedule_depth(node);
+ data.n_thread = isl_schedule_node_band_n_member(node);
+ if (data.thread_depth == data.shared_depth)
+ data.copy_sched = isl_union_map_copy(data.shared_sched);
+ else
+ data.copy_sched = prefix_with_equalities(node);
+ data.thread_sched = isl_union_map_copy(data.copy_sched);
+ data.thread_sched = isl_union_map_flat_range_product(data.thread_sched,
+ isl_schedule_node_band_get_partial_schedule_union_map(node));
+ data.thread_sched = isl_union_map_detect_equalities(data.thread_sched);
+
+ contraction = isl_union_pw_multi_aff_copy(kernel->contraction);
+ data.host_sched = expand(data.host_sched, contraction);
+ data.shared_sched = expand(data.shared_sched, contraction);
+ if (data.thread_depth == data.shared_depth) {
+ isl_union_map_free(data.copy_sched);
+ data.copy_sched = isl_union_map_copy(data.shared_sched);
+ } else {
+ data.copy_sched = expand(data.copy_sched, contraction);
+ }
+ data.thread_sched = expand(data.thread_sched, contraction);
+ isl_union_pw_multi_aff_free(contraction);
+
+ node = isl_schedule_node_child(node, 0);
+ data.full_sched = isl_union_map_copy(data.thread_sched);
+ data.full_sched = isl_union_map_flat_range_product(data.full_sched,
+ isl_schedule_node_get_subtree_schedule_union_map(node));
+ isl_schedule_node_free(node);
+
+ compute_privatization(&data, kernel);
+
+ for (i = 0; i < kernel->n_array; ++i) {
+ r = group_array_references(kernel, &kernel->array[i], &data);
+ if (r < 0)
+ break;
+ }
+
+ isl_union_map_free(data.host_sched);
+ isl_union_map_free(data.shared_sched);
+ isl_union_map_free(data.copy_sched);
+ isl_union_map_free(data.thread_sched);
+ isl_union_map_free(data.full_sched);
+ isl_set_free(data.privatization);
+
+ return r;
+}
+
+/* Given a description of an array tile "tile" and the "space"
+ *
+ * { D -> A }
+ *
+ * where D represents the first tile->depth schedule dimensions
+ * and A represents the array, construct an isl_multi_aff
+ *
+ * { [D[i] -> A[a]] -> A'[a'] }
+ *
+ * with A' a scaled down copy of A according to the shifts and strides
+ * in "tile". In particular,
+ *
+ * a' = (a + shift(i))/stride
+ *
+ * "insert_array" represents
+ *
+ * { [D -> A] -> D }
+ *
+ * and is used to insert A into the domain of functions that only
+ * reference D.
+ */
+static __isl_give isl_multi_aff *strided_tile(
+ struct gpu_array_tile *tile, __isl_keep isl_space *space,
+ __isl_keep isl_multi_aff *insert_array)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_multi_aff *shift;
+ isl_multi_val *stride;
+ isl_space *space2;
+ isl_local_space *ls;
+ isl_multi_aff *tiling;
+
+ ctx = isl_space_get_ctx(space);
+ space2 = isl_space_domain(isl_space_copy(space));
+ ls = isl_local_space_from_space(space2);
+ space2 = isl_space_range(isl_space_copy(space));
+ stride = isl_multi_val_zero(space2);
+ shift = isl_multi_aff_zero(isl_space_copy(space));
+
+ for (i = 0; i < tile->n; ++i) {
+ struct gpu_array_bound *bound = &tile->bound[i];
+ isl_val *stride_i;
+ isl_aff *shift_i;
+
+ if (tile->bound[i].shift) {
+ stride_i = isl_val_copy(bound->stride);
+ shift_i = isl_aff_copy(bound->shift);
+ } else {
+ stride_i = isl_val_one(ctx);
+ shift_i = isl_aff_zero_on_domain(
+ isl_local_space_copy(ls));
+ }
+
+ stride = isl_multi_val_set_val(stride, i, stride_i);
+ shift = isl_multi_aff_set_aff(shift, i, shift_i);
+ }
+ isl_local_space_free(ls);
+
+ shift = isl_multi_aff_pullback_multi_aff(shift,
+ isl_multi_aff_copy(insert_array));
+
+ tiling = isl_multi_aff_range_map(isl_space_copy(space));
+ tiling = isl_multi_aff_add(tiling, shift);
+ tiling = isl_multi_aff_scale_down_multi_val(tiling, stride);
+
+ return tiling;
+}
+
+/* Compute a tiling for the array reference group "group".
+ *
+ * The tiling is of the form
+ *
+ * { [D[i] -> A[a]] -> T[t] }
+ *
+ * where D represents the first tile->depth schedule dimensions,
+ * A represents the global array and T represents the shared or
+ * private memory tile. The name of T is the name of the local
+ * array.
+ *
+ * If there is any stride in the accesses, then the mapping is
+ *
+ * t = (a + shift(i))/stride - lb(i)
+ *
+ * otherwise, it is simply
+ *
+ * t = a - lb(i)
+ */
+void gpu_array_ref_group_compute_tiling(struct gpu_array_ref_group *group)
+{
+ int i;
+ struct gpu_array_tile *tile;
+ isl_space *space;
+ isl_multi_aff *tiling, *lb, *insert_array;
+ isl_printer *p;
+ char *local_name;
+
+ tile = gpu_array_ref_group_tile(group);
+ if (!tile)
+ return;
+
+ space = isl_map_get_space(group->access);
+ space = isl_space_from_range(isl_space_range(space));
+ space = isl_space_add_dims(space, isl_dim_in, tile->depth);
+ insert_array = isl_multi_aff_domain_map(isl_space_copy(space));
+
+ for (i = 0; i < tile->n; ++i)
+ if (tile->bound[i].shift)
+ break;
+
+ if (i < tile->n)
+ tiling = strided_tile(tile, space, insert_array);
+ else
+ tiling = isl_multi_aff_range_map(isl_space_copy(space));
+
+ lb = isl_multi_aff_zero(space);
+ for (i = 0; i < tile->n; ++i) {
+ isl_aff *lb_i = isl_aff_copy(tile->bound[i].lb);
+ lb = isl_multi_aff_set_aff(lb, i, lb_i);
+ }
+ lb = isl_multi_aff_pullback_multi_aff(lb, insert_array);
+
+ tiling = isl_multi_aff_sub(tiling, lb);
+
+ p = isl_printer_to_str(isl_multi_aff_get_ctx(tiling));
+ p = gpu_array_ref_group_print_name(group, p);
+ local_name = isl_printer_get_str(p);
+ isl_printer_free(p);
+ tiling = isl_multi_aff_set_tuple_name(tiling, isl_dim_out, local_name);
+ free(local_name);
+
+ tile->tiling = tiling;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_group.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_group.h
new file mode 100644
index 00000000000..c94812d39e2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_group.h
@@ -0,0 +1,65 @@
+#ifndef GPU_GROUP_H
+#define GPU_GROUP_H
+
+#include <isl/schedule_node.h>
+#include "gpu.h"
+
+/* A group of array references in a kernel that should be handled together.
+ * If private_tile is not NULL, then it is mapped to registers.
+ * Otherwise, if shared_tile is not NULL, it is mapped to shared memory.
+ * Otherwise, it is accessed from global memory.
+ * Note that if both private_tile and shared_tile are set, then shared_tile
+ * is only used inside group_common_shared_memory_tile.
+ */
+struct gpu_array_ref_group {
+ /* The references in this group access this local array. */
+ struct gpu_local_array_info *local_array;
+ /* This is the corresponding array. */
+ struct gpu_array_info *array;
+ /* Position of this group in the list of reference groups of array. */
+ int nr;
+
+ /* The following fields are use during the construction of the groups.
+ * access is the combined access relation relative to the private
+ * memory tiling. In particular, the domain of the map corresponds
+ * to the first thread_depth dimensions of the kernel schedule.
+ * write is set if any access in the group is a write.
+ * exact_write is set if all writes are definite writes.
+ * slice is set if there is at least one access in the group
+ * that refers to more than one element
+ * "min_depth" is the minimum of the tile depths and thread_depth.
+ */
+ isl_map *access;
+ int write;
+ int exact_write;
+ int slice;
+ int min_depth;
+
+ /* The shared memory tile, NULL if none. */
+ struct gpu_array_tile *shared_tile;
+
+ /* The private memory tile, NULL if none. */
+ struct gpu_array_tile *private_tile;
+
+ /* References in this group; point to elements of a linked list. */
+ int n_ref;
+ struct gpu_stmt_access **refs;
+};
+
+int gpu_group_references(struct ppcg_kernel *kernel,
+ __isl_keep isl_schedule_node *node);
+
+__isl_give isl_printer *gpu_array_ref_group_print_name(
+ struct gpu_array_ref_group *group, __isl_take isl_printer *p);
+void gpu_array_ref_group_compute_tiling(struct gpu_array_ref_group *group);
+__isl_give isl_union_map *gpu_array_ref_group_access_relation(
+ struct gpu_array_ref_group *group, int read, int write);
+int gpu_array_ref_group_requires_unroll(struct gpu_array_ref_group *group);
+enum ppcg_group_access_type gpu_array_ref_group_type(
+ struct gpu_array_ref_group *group);
+struct gpu_array_tile *gpu_array_ref_group_tile(
+ struct gpu_array_ref_group *group);
+struct gpu_array_ref_group *gpu_array_ref_group_free(
+ struct gpu_array_ref_group *group);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_hybrid.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_hybrid.c
new file mode 100644
index 00000000000..f1b8af3e50c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_hybrid.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2013 Ecole Normale Superieure
+ * Copyright 2015 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <string.h>
+
+#include <isl/val.h>
+#include <isl/space.h>
+#include <isl/union_set.h>
+#include <isl/schedule_node.h>
+
+#include "hybrid.h"
+#include "gpu_hybrid.h"
+#include "gpu_tree.h"
+#include "schedule.h"
+#include "util.h"
+
+/* Have all domain elements been filtered out before reaching
+ * the "node" position in the schedule tree?
+ */
+static isl_bool has_empty_domain(__isl_keep isl_schedule_node *node)
+{
+ isl_union_set *domain;
+ isl_bool empty;
+
+ domain = isl_schedule_node_get_domain(node);
+ empty = isl_union_set_is_empty(domain);
+ isl_union_set_free(domain);
+
+ return empty;
+}
+
+/* Given a pointer to a phase in the result of hybrid tiling,
+ * map the phase to the device, provided the phase is non-empty.
+ * Empty phases can occur if the input schedule domain can be
+ * covered by a small number of hexagons that all belong to the same phase.
+ *
+ * The input has the following form:
+ *
+ * M - CT - P - C - ...
+ *
+ * with M the phase marker, CT the space tiling, P the original
+ * parent band and C the original child band.
+ * The (outer dimensions of the) C band need to be mapped to threads.
+ * The (outer dimension of the) CT band needs to be mapped to blocks.
+ * The mapping to shared memory needs to be computed between the CT and
+ * the P band.
+ *
+ * The C band is first shifted to start at zero.
+ * Then the appropriate markers are introduced and a kernel is
+ * created for the tree rooted at CT.
+ * If the "unroll_gpu_tile" option is set, then the AST generator
+ * is instructed to unroll the P and C bands.
+ */
+static __isl_give isl_schedule_node *update_phase(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ struct gpu_gen *gen = user;
+ int depth0, depth;
+ isl_ctx *ctx;
+ isl_id *id;
+ isl_bool empty_domain;
+ ppcg_ht_phase *phase;
+
+ empty_domain = has_empty_domain(node);
+ if (empty_domain < 0)
+ return isl_schedule_node_free(node);
+ if (empty_domain)
+ return node;
+
+ if (!node)
+ return NULL;
+ ctx = isl_schedule_node_get_ctx(node);
+
+ phase = ppcg_ht_phase_extract_from_mark(node);
+
+ depth0 = isl_schedule_node_get_tree_depth(node);
+
+ node = isl_schedule_node_child(node, 0);
+
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_child(node, 0);
+ node = ppcg_ht_phase_shift_space_point(phase, node);
+ if (gen->options->unroll_gpu_tile)
+ node = ppcg_set_schedule_node_type(node, isl_ast_loop_unroll);
+ id = isl_id_alloc(ctx, "thread", NULL);
+ node = isl_schedule_node_insert_mark(node, id);
+ node = isl_schedule_node_parent(node);
+ if (gen->options->unroll_gpu_tile)
+ node = ppcg_set_schedule_node_type(node, isl_ast_loop_unroll);
+ id = isl_id_alloc(ctx, "shared", NULL);
+ node = isl_schedule_node_insert_mark(node, id);
+ node = isl_schedule_node_parent(node);
+
+ node = gpu_create_kernel(gen, node, 0, NULL);
+
+ depth = isl_schedule_node_get_tree_depth(node);
+ node = isl_schedule_node_ancestor(node, depth - depth0);
+
+ return node;
+}
+
+/* Apply hybrid tiling on "node" and its parent based on the (valid)
+ * bounds on the relative dependence distances "bounds" and
+ * the tile sizes in "tile_sizes".
+ * The number of elements in "tile_sizes" is at least as large
+ * as the sum of the dimensions of the parent and the child node.
+ *
+ * Convert the tile_sizes to an isl_multi_val in the right space,
+ * insert the hybrid tiling and then create a kernel inside each phase.
+ * Finally, remove the phase marks.
+ */
+__isl_give isl_schedule_node *gpu_hybrid_tile(struct gpu_gen *gen,
+ __isl_take isl_schedule_node *node, __isl_take ppcg_ht_bounds *bounds,
+ int *tile_sizes)
+{
+ isl_multi_val *mv;
+ isl_space *space, *space2;
+
+ if (!node || !bounds)
+ goto error;
+
+ space2 = isl_schedule_node_band_get_space(node);
+ node = isl_schedule_node_parent(node);
+ space = isl_schedule_node_band_get_space(node);
+ space = isl_space_product(space, space2);
+ mv = ppcg_multi_val_from_int_list(space, tile_sizes);
+
+ node = ppcg_ht_bounds_insert_tiling(bounds, mv, node, gen->options);
+
+ node = hybrid_tile_foreach_phase(node, &update_phase, gen);
+
+ node = hybrid_tile_drop_phase_marks(node);
+
+ return node;
+error:
+ isl_schedule_node_free(node);
+ ppcg_ht_bounds_free(bounds);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_hybrid.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_hybrid.h
new file mode 100644
index 00000000000..0348261586b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_hybrid.h
@@ -0,0 +1,13 @@
+#ifndef GPU_HYBRID_H
+#define GPU_HYBRID_H
+
+#include <isl/schedule_node.h>
+
+#include "gpu.h"
+#include "hybrid.h"
+
+__isl_give isl_schedule_node *gpu_hybrid_tile(struct gpu_gen *gen,
+ __isl_take isl_schedule_node *node, __isl_take ppcg_ht_bounds *bounds,
+ int *tile_sizes);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_print.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_print.c
new file mode 100644
index 00000000000..a1ae96830c6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_print.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2012 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <string.h>
+
+#include <isl/aff.h>
+
+#include "gpu_print.h"
+#include "print.h"
+#include "schedule.h"
+
+/* Print declarations to "p" for arrays that are local to "prog"
+ * but that are used on the host and therefore require a declaration.
+ */
+__isl_give isl_printer *gpu_print_local_declarations(__isl_take isl_printer *p,
+ struct gpu_prog *prog)
+{
+ int i;
+
+ if (!prog)
+ return isl_printer_free(p);
+
+ for (i = 0; i < prog->n_array; ++i) {
+ struct gpu_array_info *array = &prog->array[i];
+ isl_ast_expr *size;
+
+ if (!array->declare_local)
+ continue;
+ size = array->declared_size;
+ p = ppcg_print_declaration_with_size(p, array->type, size);
+ }
+
+ return p;
+}
+
+/* Print an expression for the size of "array" in bytes.
+ */
+__isl_give isl_printer *gpu_array_info_print_size(__isl_take isl_printer *prn,
+ struct gpu_array_info *array)
+{
+ int i;
+
+ for (i = 0; i < array->n_index; ++i) {
+ isl_ast_expr *bound;
+
+ prn = isl_printer_print_str(prn, "(");
+ bound = isl_ast_expr_get_op_arg(array->bound_expr, 1 + i);
+ prn = isl_printer_print_ast_expr(prn, bound);
+ isl_ast_expr_free(bound);
+ prn = isl_printer_print_str(prn, ") * ");
+ }
+ prn = isl_printer_print_str(prn, "sizeof(");
+ prn = isl_printer_print_str(prn, array->type);
+ prn = isl_printer_print_str(prn, ")");
+
+ return prn;
+}
+
+/* Print the declaration of a non-linearized array argument.
+ */
+static __isl_give isl_printer *print_non_linearized_declaration_argument(
+ __isl_take isl_printer *p, struct gpu_array_info *array)
+{
+ p = isl_printer_print_str(p, array->type);
+ p = isl_printer_print_str(p, " ");
+
+ p = isl_printer_print_ast_expr(p, array->bound_expr);
+
+ return p;
+}
+
+/* Print the declaration of an array argument.
+ * "memory_space" allows to specify a memory space prefix.
+ */
+__isl_give isl_printer *gpu_array_info_print_declaration_argument(
+ __isl_take isl_printer *p, struct gpu_array_info *array,
+ const char *memory_space)
+{
+ if (gpu_array_is_read_only_scalar(array)) {
+ p = isl_printer_print_str(p, array->type);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_str(p, array->name);
+ return p;
+ }
+
+ if (memory_space) {
+ p = isl_printer_print_str(p, memory_space);
+ p = isl_printer_print_str(p, " ");
+ }
+
+ if (array->n_index != 0 && !array->linearize)
+ return print_non_linearized_declaration_argument(p, array);
+
+ p = isl_printer_print_str(p, array->type);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_str(p, "*");
+ p = isl_printer_print_str(p, array->name);
+
+ return p;
+}
+
+/* Print the call of an array argument.
+ */
+__isl_give isl_printer *gpu_array_info_print_call_argument(
+ __isl_take isl_printer *p, struct gpu_array_info *array)
+{
+ if (gpu_array_is_read_only_scalar(array))
+ return isl_printer_print_str(p, array->name);
+
+ p = isl_printer_print_str(p, "dev_");
+ p = isl_printer_print_str(p, array->name);
+
+ return p;
+}
+
+/* Print an access to the element in the private/shared memory copy
+ * described by "stmt". The index of the copy is recorded in
+ * stmt->local_index as an access to the array.
+ */
+static __isl_give isl_printer *stmt_print_local_index(__isl_take isl_printer *p,
+ struct ppcg_kernel_stmt *stmt)
+{
+ return isl_printer_print_ast_expr(p, stmt->u.c.local_index);
+}
+
+/* Print an access to the element in the global memory copy
+ * described by "stmt". The index of the copy is recorded in
+ * stmt->index as an access to the array.
+ */
+static __isl_give isl_printer *stmt_print_global_index(
+ __isl_take isl_printer *p, struct ppcg_kernel_stmt *stmt)
+{
+ struct gpu_array_info *array = stmt->u.c.array;
+ isl_ast_expr *index;
+
+ if (gpu_array_is_scalar(array)) {
+ if (!gpu_array_is_read_only_scalar(array))
+ p = isl_printer_print_str(p, "*");
+ p = isl_printer_print_str(p, array->name);
+ return p;
+ }
+
+ index = isl_ast_expr_copy(stmt->u.c.index);
+
+ p = isl_printer_print_ast_expr(p, index);
+ isl_ast_expr_free(index);
+
+ return p;
+}
+
+/* Print a copy statement.
+ *
+ * A read copy statement is printed as
+ *
+ * local = global;
+ *
+ * while a write copy statement is printed as
+ *
+ * global = local;
+ */
+__isl_give isl_printer *ppcg_kernel_print_copy(__isl_take isl_printer *p,
+ struct ppcg_kernel_stmt *stmt)
+{
+ p = isl_printer_start_line(p);
+ if (stmt->u.c.read) {
+ p = stmt_print_local_index(p, stmt);
+ p = isl_printer_print_str(p, " = ");
+ p = stmt_print_global_index(p, stmt);
+ } else {
+ p = stmt_print_global_index(p, stmt);
+ p = isl_printer_print_str(p, " = ");
+ p = stmt_print_local_index(p, stmt);
+ }
+ p = isl_printer_print_str(p, ";");
+ p = isl_printer_end_line(p);
+
+ return p;
+}
+
+__isl_give isl_printer *ppcg_kernel_print_domain(__isl_take isl_printer *p,
+ struct ppcg_kernel_stmt *stmt)
+{
+ return pet_stmt_print_body(stmt->u.d.stmt->stmt, p, stmt->u.d.ref2expr);
+}
+
+/* This function is called for each node in a GPU AST.
+ * In case of a user node, print the macro definitions required
+ * for printing the AST expressions in the annotation, if any.
+ * For other nodes, return true such that descendants are also
+ * visited.
+ *
+ * In particular, for a kernel launch, print the macro definitions
+ * needed for the grid size.
+ * For a copy statement, print the macro definitions needed
+ * for the two index expressions.
+ * For an original user statement, print the macro definitions
+ * needed for the substitutions.
+ */
+static isl_bool at_node(__isl_keep isl_ast_node *node, void *user)
+{
+ const char *name;
+ isl_id *id;
+ int is_kernel;
+ struct ppcg_kernel *kernel;
+ struct ppcg_kernel_stmt *stmt;
+ isl_printer **p = user;
+
+ if (isl_ast_node_get_type(node) != isl_ast_node_user)
+ return isl_bool_true;
+
+ id = isl_ast_node_get_annotation(node);
+ if (!id)
+ return isl_bool_false;
+
+ name = isl_id_get_name(id);
+ if (!name)
+ return isl_bool_error;
+ is_kernel = !strcmp(name, "kernel");
+ kernel = is_kernel ? isl_id_get_user(id) : NULL;
+ stmt = is_kernel ? NULL : isl_id_get_user(id);
+ isl_id_free(id);
+
+ if ((is_kernel && !kernel) || (!is_kernel && !stmt))
+ return isl_bool_error;
+
+ if (is_kernel) {
+ *p = ppcg_ast_expr_print_macros(kernel->grid_size_expr, *p);
+ } else if (stmt->type == ppcg_kernel_copy) {
+ *p = ppcg_ast_expr_print_macros(stmt->u.c.index, *p);
+ *p = ppcg_ast_expr_print_macros(stmt->u.c.local_index, *p);
+ } else if (stmt->type == ppcg_kernel_domain) {
+ *p = ppcg_print_body_macros(*p, stmt->u.d.ref2expr);
+ }
+ if (!*p)
+ return isl_bool_error;
+
+ return isl_bool_false;
+}
+
+/* Print the required macros for the GPU AST "node" to "p",
+ * including those needed for the user statements inside the AST.
+ */
+__isl_give isl_printer *gpu_print_macros(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node)
+{
+ if (isl_ast_node_foreach_descendant_top_down(node, &at_node, &p) < 0)
+ return isl_printer_free(p);
+ p = ppcg_print_macros(p, node);
+ return p;
+}
+
+/* Was the definition of "type" printed before?
+ * That is, does its name appear in the list of printed types "types"?
+ */
+static int already_printed(struct gpu_types *types,
+ struct pet_type *type)
+{
+ int i;
+
+ for (i = 0; i < types->n; ++i)
+ if (!strcmp(types->name[i], type->name))
+ return 1;
+
+ return 0;
+}
+
+/* Print the definitions of all types prog->scop that have not been
+ * printed before (according to "types") on "p".
+ * Extend the list of printed types "types" with the newly printed types.
+ */
+__isl_give isl_printer *gpu_print_types(__isl_take isl_printer *p,
+ struct gpu_types *types, struct gpu_prog *prog)
+{
+ int i, n;
+ isl_ctx *ctx;
+ char **name;
+
+ n = prog->scop->pet->n_type;
+
+ if (n == 0)
+ return p;
+
+ ctx = isl_printer_get_ctx(p);
+ name = isl_realloc_array(ctx, types->name, char *, types->n + n);
+ if (!name)
+ return isl_printer_free(p);
+ types->name = name;
+
+ for (i = 0; i < n; ++i) {
+ struct pet_type *type = prog->scop->pet->types[i];
+
+ if (already_printed(types, type))
+ continue;
+
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, type->definition);
+ p = isl_printer_print_str(p, ";");
+ p = isl_printer_end_line(p);
+
+ types->name[types->n++] = strdup(type->name);
+ }
+
+ return p;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_print.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_print.h
new file mode 100644
index 00000000000..7f07d365869
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_print.h
@@ -0,0 +1,28 @@
+#ifndef GPU_PRINT_H
+#define GPU_PRINT_H
+
+#include "gpu.h"
+
+__isl_give isl_printer *gpu_print_local_declarations(__isl_take isl_printer *p,
+ struct gpu_prog *prog);
+
+__isl_give isl_printer *gpu_print_types(__isl_take isl_printer *p,
+ struct gpu_types *types, struct gpu_prog *prog);
+
+__isl_give isl_printer *gpu_print_macros(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node);
+
+__isl_give isl_printer *gpu_array_info_print_size(__isl_take isl_printer *prn,
+ struct gpu_array_info *array);
+__isl_give isl_printer *gpu_array_info_print_declaration_argument(
+ __isl_take isl_printer *p, struct gpu_array_info *array,
+ const char *memory_space);
+__isl_give isl_printer *gpu_array_info_print_call_argument(
+ __isl_take isl_printer *p, struct gpu_array_info *array);
+
+__isl_give isl_printer *ppcg_kernel_print_copy(__isl_take isl_printer *p,
+ struct ppcg_kernel_stmt *stmt);
+__isl_give isl_printer *ppcg_kernel_print_domain(__isl_take isl_printer *p,
+ struct ppcg_kernel_stmt *stmt);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_tree.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_tree.c
new file mode 100644
index 00000000000..0dcc7a144ee
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_tree.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright 2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <string.h>
+
+#include <isl/set.h>
+#include <isl/union_set.h>
+#include <isl/space.h>
+
+#include "gpu_tree.h"
+
+/* The functions in this file are used to navigate part of a schedule tree
+ * that is mapped to blocks. Initially, this part consists of a linear
+ * branch segment with a mark node with name "kernel" on the outer end
+ * and a mark node with name "thread" on the inner end.
+ * During the mapping to blocks, branching may be introduced, but only
+ * one of the elements in each sequence contains the "thread" mark.
+ * The filter of this element (and only this filter) contains
+ * domain elements identified by the "core" argument of the functions
+ * that move down this tree.
+ *
+ * Synchronization statements have a name that starts with "sync" and
+ * a user pointer pointing to the kernel that contains the synchronization.
+ * The functions inserting or detecting synchronizations take a ppcg_kernel
+ * argument to be able to create or identify such statements.
+ * They may also use two fields in this structure, the "core" field
+ * to move around in the tree and the "n_sync" field to make sure that
+ * each synchronization has a different name (within the kernel).
+ */
+
+/* Is "node" a mark node with an identifier called "name"?
+ */
+static int is_marked(__isl_keep isl_schedule_node *node, const char *name)
+{
+ isl_id *mark;
+ int has_name;
+
+ if (!node)
+ return -1;
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_mark)
+ return 0;
+
+ mark = isl_schedule_node_mark_get_id(node);
+ if (!mark)
+ return -1;
+
+ has_name = !strcmp(isl_id_get_name(mark), name);
+ isl_id_free(mark);
+
+ return has_name;
+}
+
+/* Is "node" a mark node with an identifier called "kernel"?
+ */
+int gpu_tree_node_is_kernel(__isl_keep isl_schedule_node *node)
+{
+ return is_marked(node, "kernel");
+}
+
+/* Is "node" a mark node with an identifier called "shared"?
+ */
+static int node_is_shared(__isl_keep isl_schedule_node *node)
+{
+ return is_marked(node, "shared");
+}
+
+/* Is "node" a mark node with an identifier called "thread"?
+ */
+static int node_is_thread(__isl_keep isl_schedule_node *node)
+{
+ return is_marked(node, "thread");
+}
+
+/* Insert a mark node with identifier "shared" in front of "node".
+ */
+static __isl_give isl_schedule_node *insert_shared(
+ __isl_take isl_schedule_node *node)
+{
+ isl_ctx *ctx;
+ isl_id *id;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ id = isl_id_alloc(ctx, "shared", NULL);
+ node = isl_schedule_node_insert_mark(node, id);
+
+ return node;
+}
+
+/* Insert a "shared" mark in front of the "thread" mark
+ * provided the linear branch between "node" and the "thread" mark
+ * does not contain such a "shared" mark already.
+ *
+ * As a side effect, this function checks that the subtree at "node"
+ * actually contains a "thread" mark and that there is no branching
+ * in between "node" and this "thread" mark.
+ */
+__isl_give isl_schedule_node *gpu_tree_insert_shared_before_thread(
+ __isl_take isl_schedule_node *node)
+{
+ int depth0, depth;
+ int any_shared = 0;
+
+ if (!node)
+ return NULL;
+
+ depth0 = isl_schedule_node_get_tree_depth(node);
+
+ for (;;) {
+ int is_thread;
+ int n;
+
+ if (!any_shared) {
+ any_shared = node_is_shared(node);
+ if (any_shared < 0)
+ return isl_schedule_node_free(node);
+ }
+ is_thread = node_is_thread(node);
+ if (is_thread < 0)
+ return isl_schedule_node_free(node);
+ if (is_thread)
+ break;
+ n = isl_schedule_node_n_children(node);
+ if (n == 0)
+ isl_die(isl_schedule_node_get_ctx(node),
+ isl_error_invalid,
+ "no thread marker found",
+ return isl_schedule_node_free(node));
+ if (n > 1)
+ isl_die(isl_schedule_node_get_ctx(node),
+ isl_error_invalid,
+ "expecting single thread marker",
+ return isl_schedule_node_free(node));
+
+ node = isl_schedule_node_child(node, 0);
+ }
+
+ if (!any_shared)
+ node = insert_shared(node);
+ depth = isl_schedule_node_get_tree_depth(node);
+ node = isl_schedule_node_ancestor(node, depth - depth0);
+
+ return node;
+}
+
+/* Assuming "node" is a filter node, does it correspond to the branch
+ * that contains the "thread" mark, i.e., does it contain any elements
+ * in "core"?
+ */
+static int node_is_core(__isl_keep isl_schedule_node *node,
+ __isl_keep isl_union_set *core)
+{
+ int disjoint;
+ isl_union_set *filter;
+
+ filter = isl_schedule_node_filter_get_filter(node);
+ disjoint = isl_union_set_is_disjoint(filter, core);
+ isl_union_set_free(filter);
+ if (disjoint < 0)
+ return -1;
+
+ return !disjoint;
+}
+
+/* Move to the only child of "node" that has the "thread" mark as descendant,
+ * where the branch containing this mark is identified by the domain elements
+ * in "core".
+ *
+ * If "node" is not a sequence, then it only has one child and we move
+ * to that single child.
+ * Otherwise, we check each of the filters in the children, pick
+ * the one that corresponds to "core" and return a pointer to the child
+ * of the filter node.
+ */
+static __isl_give isl_schedule_node *core_child(
+ __isl_take isl_schedule_node *node, __isl_keep isl_union_set *core)
+{
+ int i, n;
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
+ return isl_schedule_node_child(node, 0);
+
+ n = isl_schedule_node_n_children(node);
+ for (i = 0; i < n; ++i) {
+ int is_core;
+
+ node = isl_schedule_node_child(node, i);
+ is_core = node_is_core(node, core);
+
+ if (is_core < 0)
+ return isl_schedule_node_free(node);
+ if (is_core)
+ return isl_schedule_node_child(node, 0);
+
+ node = isl_schedule_node_parent(node);
+ }
+
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_internal,
+ "core child not found", return isl_schedule_node_free(node));
+}
+
+/* Move down the branch between "kernel" and "thread" until
+ * the "shared" mark is reached, where the branch containing the "shared"
+ * mark is identified by the domain elements in "core".
+ */
+__isl_give isl_schedule_node *gpu_tree_move_down_to_shared(
+ __isl_take isl_schedule_node *node, __isl_keep isl_union_set *core)
+{
+ int is_shared;
+
+ while ((is_shared = node_is_shared(node)) == 0)
+ node = core_child(node, core);
+ if (is_shared < 0)
+ node = isl_schedule_node_free(node);
+
+ return node;
+}
+
+/* Move down the branch between "kernel" and "thread" until
+ * the "thread" mark is reached, where the branch containing the "thread"
+ * mark is identified by the domain elements in "core".
+ */
+__isl_give isl_schedule_node *gpu_tree_move_down_to_thread(
+ __isl_take isl_schedule_node *node, __isl_keep isl_union_set *core)
+{
+ int is_thread;
+
+ while ((is_thread = node_is_thread(node)) == 0)
+ node = core_child(node, core);
+ if (is_thread < 0)
+ node = isl_schedule_node_free(node);
+
+ return node;
+}
+
+/* Move up the tree underneath the "thread" mark until
+ * the "thread" mark is reached.
+ */
+__isl_give isl_schedule_node *gpu_tree_move_up_to_thread(
+ __isl_take isl_schedule_node *node)
+{
+ int is_thread;
+
+ while ((is_thread = node_is_thread(node)) == 0)
+ node = isl_schedule_node_parent(node);
+ if (is_thread < 0)
+ node = isl_schedule_node_free(node);
+
+ return node;
+}
+
+/* Move up the tree underneath the "kernel" mark until
+ * the "kernel" mark is reached.
+ */
+__isl_give isl_schedule_node *gpu_tree_move_up_to_kernel(
+ __isl_take isl_schedule_node *node)
+{
+ int is_kernel;
+
+ while ((is_kernel = gpu_tree_node_is_kernel(node)) == 0)
+ node = isl_schedule_node_parent(node);
+ if (is_kernel < 0)
+ node = isl_schedule_node_free(node);
+
+ return node;
+}
+
+/* Move down from the "kernel" mark (or at least a node with schedule
+ * depth smaller than or equal to "depth") to a band node at schedule
+ * depth "depth". The "thread" mark is assumed to have a schedule
+ * depth greater than or equal to "depth". The branch containing the
+ * "thread" mark is identified by the domain elements in "core".
+ *
+ * If the desired schedule depth is in the middle of band node,
+ * then the band node is split into two pieces, the second piece
+ * at the desired schedule depth.
+ */
+__isl_give isl_schedule_node *gpu_tree_move_down_to_depth(
+ __isl_take isl_schedule_node *node, int depth,
+ __isl_keep isl_union_set *core)
+{
+ int is_shared;
+ int is_thread = 0;
+
+ while (node && isl_schedule_node_get_schedule_depth(node) < depth) {
+ if (isl_schedule_node_get_type(node) ==
+ isl_schedule_node_band) {
+ int node_depth, node_dim;
+ node_depth = isl_schedule_node_get_schedule_depth(node);
+ node_dim = isl_schedule_node_band_n_member(node);
+ if (node_depth + node_dim > depth)
+ node = isl_schedule_node_band_split(node,
+ depth - node_depth);
+ }
+ node = core_child(node, core);
+ }
+ while ((is_shared = node_is_shared(node)) == 0 &&
+ (is_thread = node_is_thread(node)) == 0 &&
+ isl_schedule_node_get_type(node) != isl_schedule_node_band)
+ node = core_child(node, core);
+ if (is_shared < 0 || is_thread < 0)
+ node = isl_schedule_node_free(node);
+
+ return node;
+}
+
+/* Create a union set containing a single set with a tuple identifier
+ * called "syncX" and user pointer equal to "kernel".
+ */
+static __isl_give isl_union_set *create_sync_domain(struct ppcg_kernel *kernel)
+{
+ isl_space *space;
+ isl_id *id;
+ char name[40];
+
+ space = isl_space_set_alloc(kernel->ctx, 0, 0);
+ snprintf(name, sizeof(name), "sync%d", kernel->n_sync++);
+ id = isl_id_alloc(kernel->ctx, name, kernel);
+ space = isl_space_set_tuple_id(space, isl_dim_set, id);
+ return isl_union_set_from_set(isl_set_universe(space));
+}
+
+/* Is "id" the identifier of a synchronization statement inside "kernel"?
+ * That is, does its name start with "sync" and does it point to "kernel"?
+ */
+int gpu_tree_id_is_sync(__isl_keep isl_id *id, struct ppcg_kernel *kernel)
+{
+ const char *name;
+
+ name = isl_id_get_name(id);
+ if (!name)
+ return 0;
+ else if (strncmp(name, "sync", 4))
+ return 0;
+ return isl_id_get_user(id) == kernel;
+}
+
+/* Does "domain" consist of a single set with a tuple identifier
+ * corresponding to a synchronization for "kernel"?
+ */
+static int domain_is_sync(__isl_keep isl_union_set *domain,
+ struct ppcg_kernel *kernel)
+{
+ int is_sync;
+ isl_id *id;
+ isl_set *set;
+
+ if (isl_union_set_n_set(domain) != 1)
+ return 0;
+ set = isl_set_from_union_set(isl_union_set_copy(domain));
+ id = isl_set_get_tuple_id(set);
+ is_sync = gpu_tree_id_is_sync(id, kernel);
+ isl_id_free(id);
+ isl_set_free(set);
+
+ return is_sync;
+}
+
+/* Does "node" point to a filter selecting a synchronization statement
+ * for "kernel"?
+ */
+static int node_is_sync_filter(__isl_keep isl_schedule_node *node,
+ struct ppcg_kernel *kernel)
+{
+ int is_sync;
+ enum isl_schedule_node_type type;
+ isl_union_set *domain;
+
+ if (!node)
+ return -1;
+ type = isl_schedule_node_get_type(node);
+ if (type != isl_schedule_node_filter)
+ return 0;
+ domain = isl_schedule_node_filter_get_filter(node);
+ is_sync = domain_is_sync(domain, kernel);
+ isl_union_set_free(domain);
+
+ return is_sync;
+}
+
+/* Is "node" part of a sequence with a previous synchronization statement
+ * for "kernel"?
+ * That is, is the parent of "node" a filter such that there is
+ * a previous filter that picks out exactly such a synchronization statement?
+ */
+static int has_preceding_sync(__isl_keep isl_schedule_node *node,
+ struct ppcg_kernel *kernel)
+{
+ int found = 0;
+
+ node = isl_schedule_node_copy(node);
+ node = isl_schedule_node_parent(node);
+ while (!found && isl_schedule_node_has_previous_sibling(node)) {
+ node = isl_schedule_node_previous_sibling(node);
+ if (!node)
+ break;
+ found = node_is_sync_filter(node, kernel);
+ }
+ if (!node)
+ found = -1;
+ isl_schedule_node_free(node);
+
+ return found;
+}
+
+/* Is "node" part of a sequence with a subsequent synchronization statement
+ * for "kernel"?
+ * That is, is the parent of "node" a filter such that there is
+ * a subsequent filter that picks out exactly such a synchronization statement?
+ */
+static int has_following_sync(__isl_keep isl_schedule_node *node,
+ struct ppcg_kernel *kernel)
+{
+ int found = 0;
+
+ node = isl_schedule_node_copy(node);
+ node = isl_schedule_node_parent(node);
+ while (!found && isl_schedule_node_has_next_sibling(node)) {
+ node = isl_schedule_node_next_sibling(node);
+ if (!node)
+ break;
+ found = node_is_sync_filter(node, kernel);
+ }
+ if (!node)
+ found = -1;
+ isl_schedule_node_free(node);
+
+ return found;
+}
+
+/* Does the subtree rooted at "node" (which is a band node) contain
+ * any synchronization statement for "kernel" that precedes
+ * the core computation of "kernel" (identified by the elements
+ * in kernel->core)?
+ */
+static int has_sync_before_core(__isl_keep isl_schedule_node *node,
+ struct ppcg_kernel *kernel)
+{
+ int has_sync = 0;
+ int is_thread;
+
+ node = isl_schedule_node_copy(node);
+ while ((is_thread = node_is_thread(node)) == 0) {
+ node = core_child(node, kernel->core);
+ has_sync = has_preceding_sync(node, kernel);
+ if (has_sync < 0 || has_sync)
+ break;
+ }
+ if (is_thread < 0 || !node)
+ has_sync = -1;
+ isl_schedule_node_free(node);
+
+ return has_sync;
+}
+
+/* Does the subtree rooted at "node" (which is a band node) contain
+ * any synchronization statement for "kernel" that follows
+ * the core computation of "kernel" (identified by the elements
+ * in kernel->core)?
+ */
+static int has_sync_after_core(__isl_keep isl_schedule_node *node,
+ struct ppcg_kernel *kernel)
+{
+ int has_sync = 0;
+ int is_thread;
+
+ node = isl_schedule_node_copy(node);
+ while ((is_thread = node_is_thread(node)) == 0) {
+ node = core_child(node, kernel->core);
+ has_sync = has_following_sync(node, kernel);
+ if (has_sync < 0 || has_sync)
+ break;
+ }
+ if (is_thread < 0 || !node)
+ has_sync = -1;
+ isl_schedule_node_free(node);
+
+ return has_sync;
+}
+
+/* Insert (or extend) an extension on top of "node" that puts
+ * a synchronization node for "kernel" before "node".
+ * Return a pointer to the original node in the updated schedule tree.
+ */
+static __isl_give isl_schedule_node *insert_sync_before(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel)
+{
+ isl_union_set *domain;
+ isl_schedule_node *graft;
+
+ if (!node)
+ return NULL;
+
+ domain = create_sync_domain(kernel);
+ graft = isl_schedule_node_from_domain(domain);
+ node = isl_schedule_node_graft_before(node, graft);
+
+ return node;
+}
+
+/* Insert (or extend) an extension on top of "node" that puts
+ * a synchronization node for "kernel" afater "node".
+ * Return a pointer to the original node in the updated schedule tree.
+ */
+static __isl_give isl_schedule_node *insert_sync_after(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel)
+{
+ isl_union_set *domain;
+ isl_schedule_node *graft;
+
+ if (!node)
+ return NULL;
+
+ domain = create_sync_domain(kernel);
+ graft = isl_schedule_node_from_domain(domain);
+ node = isl_schedule_node_graft_after(node, graft);
+
+ return node;
+}
+
+/* Insert an extension on top of "node" that puts a synchronization node
+ * for "kernel" before "node" unless there already is
+ * such a synchronization node.
+ */
+__isl_give isl_schedule_node *gpu_tree_ensure_preceding_sync(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel)
+{
+ int has_sync;
+
+ has_sync = has_preceding_sync(node, kernel);
+ if (has_sync < 0)
+ return isl_schedule_node_free(node);
+ if (has_sync)
+ return node;
+ return insert_sync_before(node, kernel);
+}
+
+/* Insert an extension on top of "node" that puts a synchronization node
+ * for "kernel" after "node" unless there already is
+ * such a synchronization node.
+ */
+__isl_give isl_schedule_node *gpu_tree_ensure_following_sync(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel)
+{
+ int has_sync;
+
+ has_sync = has_following_sync(node, kernel);
+ if (has_sync < 0)
+ return isl_schedule_node_free(node);
+ if (has_sync)
+ return node;
+ return insert_sync_after(node, kernel);
+}
+
+/* Insert an extension on top of "node" that puts a synchronization node
+ * for "kernel" after "node" unless there already is such a sync node or
+ * "node" itself already * contains a synchronization node following
+ * the core computation of "kernel".
+ */
+__isl_give isl_schedule_node *gpu_tree_ensure_sync_after_core(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel)
+{
+ int has_sync;
+
+ has_sync = has_sync_after_core(node, kernel);
+ if (has_sync < 0)
+ return isl_schedule_node_free(node);
+ if (has_sync)
+ return node;
+ has_sync = has_following_sync(node, kernel);
+ if (has_sync < 0)
+ return isl_schedule_node_free(node);
+ if (has_sync)
+ return node;
+ return insert_sync_after(node, kernel);
+}
+
+/* Move left in the sequence on top of "node" to a synchronization node
+ * for "kernel".
+ * If "node" itself contains a synchronization node preceding
+ * the core computation of "kernel", then return "node" itself.
+ * Otherwise, if "node" does not have a preceding synchronization node,
+ * then create one first.
+ */
+__isl_give isl_schedule_node *gpu_tree_move_left_to_sync(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel)
+{
+ int has_sync;
+ int is_sync;
+
+ has_sync = has_sync_before_core(node, kernel);
+ if (has_sync < 0)
+ return isl_schedule_node_free(node);
+ if (has_sync)
+ return node;
+ node = gpu_tree_ensure_preceding_sync(node, kernel);
+ node = isl_schedule_node_parent(node);
+ while ((is_sync = node_is_sync_filter(node, kernel)) == 0)
+ node = isl_schedule_node_previous_sibling(node);
+ if (is_sync < 0)
+ node = isl_schedule_node_free(node);
+ node = isl_schedule_node_child(node, 0);
+
+ return node;
+}
+
+/* Move right in the sequence on top of "node" to a synchronization node
+ * for "kernel".
+ * If "node" itself contains a synchronization node following
+ * the core computation of "kernel", then return "node" itself.
+ * Otherwise, if "node" does not have a following synchronization node,
+ * then create one first.
+ */
+__isl_give isl_schedule_node *gpu_tree_move_right_to_sync(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel)
+{
+ int has_sync;
+ int is_sync;
+
+ has_sync = has_sync_after_core(node, kernel);
+ if (has_sync < 0)
+ return isl_schedule_node_free(node);
+ if (has_sync)
+ return node;
+ node = gpu_tree_ensure_following_sync(node, kernel);
+ node = isl_schedule_node_parent(node);
+ while ((is_sync = node_is_sync_filter(node, kernel)) == 0)
+ node = isl_schedule_node_next_sibling(node);
+ if (is_sync < 0)
+ node = isl_schedule_node_free(node);
+ node = isl_schedule_node_child(node, 0);
+
+ return node;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_tree.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_tree.h
new file mode 100644
index 00000000000..a2a02584375
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/gpu_tree.h
@@ -0,0 +1,33 @@
+#ifndef GPU_TREE_H
+#define GPU_TREE_H
+
+#include <isl/schedule_node.h>
+
+#include "gpu.h"
+
+__isl_give isl_schedule_node *gpu_tree_insert_shared_before_thread(
+ __isl_take isl_schedule_node *node);
+int gpu_tree_node_is_kernel(__isl_keep isl_schedule_node *node);
+__isl_give isl_schedule_node *gpu_tree_move_down_to_shared(
+ __isl_take isl_schedule_node *node, __isl_keep isl_union_set *core);
+__isl_give isl_schedule_node *gpu_tree_move_up_to_thread(
+ __isl_take isl_schedule_node *node);
+__isl_give isl_schedule_node *gpu_tree_move_down_to_thread(
+ __isl_take isl_schedule_node *node, __isl_keep isl_union_set *core);
+__isl_give isl_schedule_node *gpu_tree_move_up_to_kernel(
+ __isl_take isl_schedule_node *node);
+__isl_give isl_schedule_node *gpu_tree_move_down_to_depth(
+ __isl_take isl_schedule_node *node, int depth,
+ __isl_keep isl_union_set *core);
+
+int gpu_tree_id_is_sync(__isl_keep isl_id *id, struct ppcg_kernel *kernel);
+__isl_give isl_schedule_node *gpu_tree_ensure_sync_after_core(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel);
+__isl_give isl_schedule_node *gpu_tree_ensure_following_sync(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel);
+__isl_give isl_schedule_node *gpu_tree_move_left_to_sync(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel);
+__isl_give isl_schedule_node *gpu_tree_move_right_to_sync(
+ __isl_take isl_schedule_node *node, struct ppcg_kernel *kernel);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/grouping.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/grouping.c
new file mode 100644
index 00000000000..1c98bdbc6d0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/grouping.c
@@ -0,0 +1,684 @@
+/*
+ * Copyright 2016 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+#include <isl/ctx.h>
+#include <isl/id.h>
+#include <isl/val.h>
+#include <isl/space.h>
+#include <isl/aff.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl/schedule.h>
+#include <isl/schedule_node.h>
+
+#include "ppcg.h"
+
+/* Internal data structure for use during the detection of statements
+ * that can be grouped.
+ *
+ * "sc" contains the original schedule constraints (not a copy).
+ * "dep" contains the intersection of the validity and the proximity
+ * constraints in "sc". It may be NULL if it has not been computed yet.
+ * "group_id" is the identifier for the next group that is extracted.
+ *
+ * "domain" is the set of statement instances that belong to any of the groups.
+ * "contraction" maps the elements of "domain" to the corresponding group
+ * instances.
+ * "schedule" schedules the statements in each group relatively to each other.
+ * These last three fields are NULL if no groups have been found so far.
+ */
+struct ppcg_grouping {
+ isl_schedule_constraints *sc;
+
+ isl_union_map *dep;
+ int group_id;
+
+ isl_union_set *domain;
+ isl_union_pw_multi_aff *contraction;
+ isl_schedule *schedule;
+};
+
+/* Clear all memory allocated by "grouping".
+ */
+static void ppcg_grouping_clear(struct ppcg_grouping *grouping)
+{
+ isl_union_map_free(grouping->dep);
+ isl_union_set_free(grouping->domain);
+ isl_union_pw_multi_aff_free(grouping->contraction);
+ isl_schedule_free(grouping->schedule);
+}
+
+/* Compute the intersection of the proximity and validity dependences
+ * in grouping->sc and store the result in grouping->dep, unless
+ * this intersection has been computed before.
+ */
+static isl_stat ppcg_grouping_compute_dep(struct ppcg_grouping *grouping)
+{
+ isl_union_map *validity, *proximity;
+
+ if (grouping->dep)
+ return isl_stat_ok;
+
+ validity = isl_schedule_constraints_get_validity(grouping->sc);
+ proximity = isl_schedule_constraints_get_proximity(grouping->sc);
+ grouping->dep = isl_union_map_intersect(validity, proximity);
+
+ if (!grouping->dep)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Information extracted from one or more consecutive leaves
+ * in the input schedule.
+ *
+ * "list" contains the sets of statement instances in the leaves,
+ * one element in the list for each original leaf.
+ * "domain" contains the union of the sets in "list".
+ * "prefix" contains the prefix schedule of these elements.
+ */
+struct ppcg_grouping_leaf {
+ isl_union_set *domain;
+ isl_union_set_list *list;
+ isl_multi_union_pw_aff *prefix;
+};
+
+/* Free all memory allocated for "leaves".
+ */
+static void ppcg_grouping_leaf_free(int n, struct ppcg_grouping_leaf leaves[])
+{
+ int i;
+
+ if (!leaves)
+ return;
+
+ for (i = 0; i < n; ++i) {
+ isl_union_set_free(leaves[i].domain);
+ isl_union_set_list_free(leaves[i].list);
+ isl_multi_union_pw_aff_free(leaves[i].prefix);
+ }
+
+ free(leaves);
+}
+
+/* Short-hand for retrieving the prefix schedule at "node"
+ * in the form of an isl_multi_union_pw_aff.
+ */
+static __isl_give isl_multi_union_pw_aff *get_prefix(
+ __isl_keep isl_schedule_node *node)
+{
+ return isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
+}
+
+/* Return an array of "n" elements with information extracted from
+ * the "n" children of "node" starting at "first", all of which
+ * are known to be filtered leaves.
+ */
+struct ppcg_grouping_leaf *extract_leaves(__isl_keep isl_schedule_node *node,
+ int first, int n)
+{
+ int i;
+ isl_ctx *ctx;
+ struct ppcg_grouping_leaf *leaves;
+
+ if (!node)
+ return NULL;
+
+ ctx = isl_schedule_node_get_ctx(node);
+ leaves = isl_calloc_array(ctx, struct ppcg_grouping_leaf, n);
+ if (!leaves)
+ return NULL;
+
+ for (i = 0; i < n; ++i) {
+ isl_schedule_node *child;
+ isl_union_set *domain;
+
+ child = isl_schedule_node_get_child(node, first + i);
+ child = isl_schedule_node_child(child, 0);
+ domain = isl_schedule_node_get_domain(child);
+ leaves[i].domain = isl_union_set_copy(domain);
+ leaves[i].list = isl_union_set_list_from_union_set(domain);
+ leaves[i].prefix = get_prefix(child);
+ isl_schedule_node_free(child);
+ }
+
+ return leaves;
+}
+
+/* Internal data structure used by merge_leaves.
+ *
+ * "src" and "dst" point to the two consecutive leaves that are
+ * under investigation for being merged.
+ * "merge" is initially set to 0 and is set to 1 as soon as
+ * it turns out that it is useful to merge the two leaves.
+ */
+struct ppcg_merge_leaves_data {
+ int merge;
+ struct ppcg_grouping_leaf *src;
+ struct ppcg_grouping_leaf *dst;
+};
+
+/* Given a relation "map" between instances of two statements A and B,
+ * does it relate every instance of A (according to the domain of "src")
+ * to every instance of B (according to the domain of "dst")?
+ */
+static isl_bool covers_src_and_dst(__isl_keep isl_map *map,
+ struct ppcg_grouping_leaf *src, struct ppcg_grouping_leaf *dst)
+{
+ isl_space *space;
+ isl_set *set1, *set2;
+ isl_bool is_subset;
+
+ space = isl_space_domain(isl_map_get_space(map));
+ set1 = isl_union_set_extract_set(src->domain, space);
+ set2 = isl_map_domain(isl_map_copy(map));
+ is_subset = isl_set_is_subset(set1, set2);
+ isl_set_free(set1);
+ isl_set_free(set2);
+ if (is_subset < 0 || !is_subset)
+ return is_subset;
+
+ space = isl_space_range(isl_map_get_space(map));
+ set1 = isl_union_set_extract_set(dst->domain, space);
+ set2 = isl_map_range(isl_map_copy(map));
+ is_subset = isl_set_is_subset(set1, set2);
+ isl_set_free(set1);
+ isl_set_free(set2);
+
+ return is_subset;
+}
+
+/* Given a relation "map" between instances of two statements A and B,
+ * are pairs of related instances executed together in the input schedule?
+ * That is, is each pair of instances assigned the same value
+ * by the corresponding prefix schedules?
+ *
+ * In particular, select the subset of "map" that has pairs of elements
+ * with the same value for the prefix schedules and then check
+ * if "map" is still a subset of the result.
+ */
+static isl_bool matches_prefix(__isl_keep isl_map *map,
+ struct ppcg_grouping_leaf *src, struct ppcg_grouping_leaf *dst)
+{
+ isl_union_map *umap, *equal;
+ isl_multi_union_pw_aff *src_prefix, *dst_prefix, *prefix;
+ isl_bool is_subset;
+
+ src_prefix = isl_multi_union_pw_aff_copy(src->prefix);
+ dst_prefix = isl_multi_union_pw_aff_copy(dst->prefix);
+ prefix = isl_multi_union_pw_aff_union_add(src_prefix, dst_prefix);
+
+ umap = isl_union_map_from_map(isl_map_copy(map));
+ equal = isl_union_map_copy(umap);
+ equal = isl_union_map_eq_at_multi_union_pw_aff(equal, prefix);
+
+ is_subset = isl_union_map_is_subset(umap, equal);
+
+ isl_union_map_free(umap);
+ isl_union_map_free(equal);
+
+ return is_subset;
+}
+
+/* Given a set of validity and proximity schedule constraints "map"
+ * between statements in consecutive leaves in a valid schedule,
+ * should the two leaves be merged into one?
+ *
+ * In particular, the two are merged if the constraints form
+ * a bijection between every instance of the first statement and
+ * every instance of the second statement. Moreover, each
+ * pair of such dependent instances needs to be executed consecutively
+ * in the input schedule. That is, they need to be assigned
+ * the same value by their prefix schedules.
+ *
+ * What this means is that for each instance of the first statement
+ * there is exactly one instance of the second statement that
+ * is executed immediately after the instance of the first statement and
+ * that, moreover, both depends on this statement instance and
+ * should be brought as close as possible to this statement instance.
+ * In other words, it is both possible to execute the two instances
+ * together (according to the input schedule) and desirable to do so
+ * (according to the validity and proximity schedule constraints).
+ */
+static isl_stat check_merge(__isl_take isl_map *map, void *user)
+{
+ struct ppcg_merge_leaves_data *data = user;
+ isl_bool ok;
+
+ ok = covers_src_and_dst(map, data->src, data->dst);
+ if (ok >= 0 && ok)
+ ok = isl_map_is_bijective(map);
+ if (ok >= 0 && ok)
+ ok = matches_prefix(map, data->src, data->dst);
+
+ isl_map_free(map);
+
+ if (ok < 0)
+ return isl_stat_error;
+ if (!ok)
+ return isl_stat_ok;
+
+ data->merge = 1;
+ return isl_stat_error;
+}
+
+/* Merge the leaves at position "pos" and "pos + 1" in "leaves".
+ */
+static isl_stat merge_pair(int n, struct ppcg_grouping_leaf leaves[], int pos)
+{
+ int i;
+
+ leaves[pos].domain = isl_union_set_union(leaves[pos].domain,
+ leaves[pos + 1].domain);
+ leaves[pos].list = isl_union_set_list_concat(leaves[pos].list,
+ leaves[pos + 1].list);
+ leaves[pos].prefix = isl_multi_union_pw_aff_union_add(
+ leaves[pos].prefix, leaves[pos + 1].prefix);
+ for (i = pos + 1; i + 1 < n; ++i)
+ leaves[i] = leaves[i + 1];
+ leaves[n - 1].domain = NULL;
+ leaves[n - 1].list = NULL;
+ leaves[n - 1].prefix = NULL;
+
+ if (!leaves[pos].domain || !leaves[pos].list || !leaves[pos].prefix)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Merge pairs of consecutive leaves in "leaves" taking into account
+ * the intersection of validity and proximity schedule constraints "dep".
+ *
+ * If a leaf has been merged with the next leaf, then the combination
+ * is checked again for merging with the next leaf.
+ * That is, if the leaves are A, B and C, then B may not have been
+ * merged with C, but after merging A and B, it could still be useful
+ * to merge the combination AB with C.
+ *
+ * Two leaves A and B are merged if there are instances of at least
+ * one pair of statements, one statement in A and one B, such that
+ * the validity and proximity schedule constraints between them
+ * make them suitable for merging according to check_merge.
+ *
+ * Return the final number of leaves in the sequence, or -1 on error.
+ */
+static int merge_leaves(int n, struct ppcg_grouping_leaf leaves[],
+ __isl_keep isl_union_map *dep)
+{
+ int i;
+ struct ppcg_merge_leaves_data data;
+
+ for (i = n - 1; i >= 0; --i) {
+ isl_union_map *dep_i;
+ isl_stat ok;
+
+ if (i + 1 >= n)
+ continue;
+
+ dep_i = isl_union_map_copy(dep);
+ dep_i = isl_union_map_intersect_domain(dep_i,
+ isl_union_set_copy(leaves[i].domain));
+ dep_i = isl_union_map_intersect_range(dep_i,
+ isl_union_set_copy(leaves[i + 1].domain));
+ data.merge = 0;
+ data.src = &leaves[i];
+ data.dst = &leaves[i + 1];
+ ok = isl_union_map_foreach_map(dep_i, &check_merge, &data);
+ isl_union_map_free(dep_i);
+ if (ok < 0 && !data.merge)
+ return -1;
+ if (!data.merge)
+ continue;
+ if (merge_pair(n, leaves, i) < 0)
+ return -1;
+ --n;
+ ++i;
+ }
+
+ return n;
+}
+
+/* Construct a schedule with "domain" as domain, that executes
+ * the elements of "list" in order (as a sequence).
+ */
+static __isl_give isl_schedule *schedule_from_domain_and_list(
+ __isl_keep isl_union_set *domain, __isl_keep isl_union_set_list *list)
+{
+ isl_schedule *schedule;
+ isl_schedule_node *node;
+
+ schedule = isl_schedule_from_domain(isl_union_set_copy(domain));
+ node = isl_schedule_get_root(schedule);
+ isl_schedule_free(schedule);
+ node = isl_schedule_node_child(node, 0);
+ list = isl_union_set_list_copy(list);
+ node = isl_schedule_node_insert_sequence(node, list);
+ schedule = isl_schedule_node_get_schedule(node);
+ isl_schedule_node_free(node);
+
+ return schedule;
+}
+
+/* Construct a unique identifier for a group in "grouping".
+ *
+ * The name is of the form G_n, with n the first value starting at
+ * grouping->group_id that does not result in an identifier
+ * that is already in use in the domain of the original schedule
+ * constraints.
+ */
+static isl_id *construct_group_id(struct ppcg_grouping *grouping,
+ __isl_take isl_space *space)
+{
+ isl_ctx *ctx;
+ isl_id *id;
+ isl_bool empty;
+ isl_union_set *domain;
+
+ if (!space)
+ return NULL;
+
+ ctx = isl_space_get_ctx(space);
+ domain = isl_schedule_constraints_get_domain(grouping->sc);
+
+ do {
+ char buffer[20];
+ isl_id *id;
+ isl_set *set;
+
+ snprintf(buffer, sizeof(buffer), "G_%d", grouping->group_id);
+ grouping->group_id++;
+ id = isl_id_alloc(ctx, buffer, NULL);
+ space = isl_space_set_tuple_id(space, isl_dim_set, id);
+ set = isl_union_set_extract_set(domain, isl_space_copy(space));
+ empty = isl_set_plain_is_empty(set);
+ isl_set_free(set);
+ } while (empty >= 0 && !empty);
+
+ if (empty < 0)
+ space = isl_space_free(space);
+
+ id = isl_space_get_tuple_id(space, isl_dim_set);
+
+ isl_space_free(space);
+ isl_union_set_free(domain);
+
+ return id;
+}
+
+/* Construct a contraction from "prefix" and "domain" for a new group
+ * in "grouping".
+ *
+ * The values of the prefix schedule "prefix" are used as instances
+ * of the new group. The identifier of the group is constructed
+ * in such a way that it does not conflict with those of earlier
+ * groups nor with statements in the domain of the original
+ * schedule constraints.
+ * The isl_multi_union_pw_aff "prefix" then simply needs to be
+ * converted to an isl_union_pw_multi_aff. However, this is not
+ * possible if "prefix" is zero-dimensional, so in this case,
+ * a contraction is constructed from "domain" instead.
+ */
+static isl_union_pw_multi_aff *group_contraction_from_prefix_and_domain(
+ struct ppcg_grouping *grouping,
+ __isl_keep isl_multi_union_pw_aff *prefix,
+ __isl_keep isl_union_set *domain)
+{
+ isl_id *id;
+ isl_space *space;
+ int dim;
+
+ space = isl_multi_union_pw_aff_get_space(prefix);
+ if (!space)
+ return NULL;
+ dim = isl_space_dim(space, isl_dim_set);
+ id = construct_group_id(grouping, space);
+ if (dim == 0) {
+ isl_multi_val *mv;
+
+ space = isl_multi_union_pw_aff_get_space(prefix);
+ space = isl_space_set_tuple_id(space, isl_dim_set, id);
+ mv = isl_multi_val_zero(space);
+ domain = isl_union_set_copy(domain);
+ return isl_union_pw_multi_aff_multi_val_on_domain(domain, mv);
+ }
+ prefix = isl_multi_union_pw_aff_copy(prefix);
+ prefix = isl_multi_union_pw_aff_set_tuple_id(prefix, isl_dim_out, id);
+ return isl_union_pw_multi_aff_from_multi_union_pw_aff(prefix);
+}
+
+/* Extend "grouping" with groups corresponding to merged
+ * leaves in the list of potentially merged leaves "leaves".
+ *
+ * The "list" field of each element in "leaves" contains a list
+ * of the instances sets of the original leaves that have been
+ * merged into this element. If at least two of the original leaves
+ * have been merged into a given element, then add the corresponding
+ * group to "grouping".
+ * In particular, the domain is extended with the statement instances
+ * of the merged leaves, the contraction is extended with a mapping
+ * of these statement instances to instances of a new group and
+ * the schedule is extended with a schedule that executes
+ * the statement instances according to the order of the leaves
+ * in which they appear.
+ * Since the instances of the groups should already be scheduled apart
+ * in the schedule into which this schedule will be plugged in,
+ * the schedules of the individual groups are combined independently
+ * of each other (as a set).
+ */
+static isl_stat add_groups(struct ppcg_grouping *grouping,
+ int n, struct ppcg_grouping_leaf leaves[])
+{
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ int n_leaf;
+ isl_schedule *schedule;
+ isl_union_set *domain;
+ isl_union_pw_multi_aff *upma;
+
+ n_leaf = isl_union_set_list_n_union_set(leaves[i].list);
+ if (n_leaf < 0)
+ return isl_stat_error;
+ if (n_leaf <= 1)
+ continue;
+ schedule = schedule_from_domain_and_list(leaves[i].domain,
+ leaves[i].list);
+ upma = group_contraction_from_prefix_and_domain(grouping,
+ leaves[i].prefix, leaves[i].domain);
+
+ domain = isl_union_set_copy(leaves[i].domain);
+ if (grouping->domain) {
+ domain = isl_union_set_union(domain, grouping->domain);
+ upma = isl_union_pw_multi_aff_union_add(upma,
+ grouping->contraction);
+ schedule = isl_schedule_set(schedule,
+ grouping->schedule);
+ }
+ grouping->domain = domain;
+ grouping->contraction = upma;
+ grouping->schedule = schedule;
+
+ if (!grouping->domain || !grouping->contraction ||
+ !grouping->schedule)
+ return isl_stat_error;
+ }
+
+ return isl_stat_ok;
+}
+
+/* Look for any pairs of consecutive leaves among the "n" children of "node"
+ * starting at "first" that should be merged together.
+ * Store the results in "grouping".
+ *
+ * First make sure the intersection of validity and proximity
+ * schedule constraints is available and extract the required
+ * information from the "n" leaves.
+ * Then try and merge consecutive leaves based on the validity
+ * and proximity constraints.
+ * If any pairs were successfully merged, then add groups
+ * corresponding to the merged leaves to "grouping".
+ */
+static isl_stat group_subsequence(__isl_keep isl_schedule_node *node,
+ int first, int n, struct ppcg_grouping *grouping)
+{
+ int n_merge;
+ struct ppcg_grouping_leaf *leaves;
+
+ if (ppcg_grouping_compute_dep(grouping) < 0)
+ return isl_stat_error;
+
+ leaves = extract_leaves(node, first, n);
+ if (!leaves)
+ return isl_stat_error;
+
+ n_merge = merge_leaves(n, leaves, grouping->dep);
+ if (n_merge >= 0 && n_merge < n &&
+ add_groups(grouping, n_merge, leaves) < 0)
+ return isl_stat_error;
+
+ ppcg_grouping_leaf_free(n, leaves);
+
+ return isl_stat_ok;
+}
+
+/* If "node" is a sequence, then check if it has any consecutive
+ * leaves that should be merged together and store the results
+ * in "grouping".
+ *
+ * In particular, call group_subsequence on each consecutive
+ * sequence of (filtered) leaves among the children of "node".
+ */
+static isl_bool detect_groups(__isl_keep isl_schedule_node *node, void *user)
+{
+ int i, n, first;
+ struct ppcg_grouping *grouping = user;
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
+ return isl_bool_true;
+
+ n = isl_schedule_node_n_children(node);
+ if (n < 0)
+ return isl_bool_error;
+
+ first = -1;
+ for (i = 0; i < n; ++i) {
+ isl_schedule_node *child;
+ enum isl_schedule_node_type type;
+
+ child = isl_schedule_node_get_child(node, i);
+ child = isl_schedule_node_child(child, 0);
+ type = isl_schedule_node_get_type(child);
+ isl_schedule_node_free(child);
+
+ if (first >= 0 && type != isl_schedule_node_leaf) {
+ if (group_subsequence(node, first, i - first,
+ grouping) < 0)
+ return isl_bool_error;
+ first = -1;
+ }
+ if (first < 0 && type == isl_schedule_node_leaf)
+ first = i;
+ }
+ if (first >= 0) {
+ if (group_subsequence(node, first, n - first, grouping) < 0)
+ return isl_bool_error;
+ }
+
+ return isl_bool_true;
+}
+
+/* Complete "grouping" to cover all statement instances in the domain
+ * of grouping->sc.
+ *
+ * In particular, grouping->domain is set to the full set of statement
+ * instances; group->contraction is extended with an identity
+ * contraction on the additional instances and group->schedule
+ * is extended with an independent schedule on those additional instances.
+ * In the extension of group->contraction, the additional instances
+ * are split into those belong to different statements and those
+ * that belong to some of the same statements. The first group
+ * is replaced by its universe in order to simplify the contraction extension.
+ */
+static void complete_grouping(struct ppcg_grouping *grouping)
+{
+ isl_union_set *domain, *left, *overlap;
+ isl_union_pw_multi_aff *upma;
+ isl_schedule *schedule;
+
+ domain = isl_schedule_constraints_get_domain(grouping->sc);
+ left = isl_union_set_subtract(isl_union_set_copy(domain),
+ isl_union_set_copy(grouping->domain));
+ schedule = isl_schedule_from_domain(isl_union_set_copy(left));
+ schedule = isl_schedule_set(schedule, grouping->schedule);
+ grouping->schedule = schedule;
+
+ overlap = isl_union_set_universe(grouping->domain);
+ grouping->domain = domain;
+ overlap = isl_union_set_intersect(isl_union_set_copy(left), overlap);
+ left = isl_union_set_subtract(left, isl_union_set_copy(overlap));
+ left = isl_union_set_universe(left);
+ left = isl_union_set_union(left, overlap);
+ upma = isl_union_set_identity_union_pw_multi_aff(left);
+ upma = isl_union_pw_multi_aff_union_add(upma, grouping->contraction);
+ grouping->contraction = upma;
+}
+
+/* Compute a schedule on the domain of "sc" that respects the schedule
+ * constraints in "sc".
+ *
+ * "schedule" is a known correct schedule that is used to combine
+ * groups of statements if options->group_chains is set.
+ * In particular, statements that are executed consecutively in a sequence
+ * in this schedule and where all instances of the second depend on
+ * the instance of the first that is executed in the same iteration
+ * of outer band nodes are grouped together into a single statement.
+ * The schedule constraints are then mapped to these groups of statements
+ * and the resulting schedule is expanded again to refer to the original
+ * statements.
+ */
+__isl_give isl_schedule *ppcg_compute_schedule(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_keep isl_schedule *schedule, struct ppcg_options *options)
+{
+ struct ppcg_grouping grouping = { sc };
+ isl_union_pw_multi_aff *contraction;
+ isl_union_map *umap;
+ isl_schedule *res, *expansion;
+
+ if (!options->group_chains)
+ return isl_schedule_constraints_compute_schedule(sc);
+
+ grouping.group_id = 0;
+ if (isl_schedule_foreach_schedule_node_top_down(schedule,
+ &detect_groups, &grouping) < 0)
+ goto error;
+ if (!grouping.contraction) {
+ ppcg_grouping_clear(&grouping);
+ return isl_schedule_constraints_compute_schedule(sc);
+ }
+ complete_grouping(&grouping);
+ contraction = isl_union_pw_multi_aff_copy(grouping.contraction);
+ umap = isl_union_map_from_union_pw_multi_aff(contraction);
+
+ sc = isl_schedule_constraints_apply(sc, umap);
+
+ res = isl_schedule_constraints_compute_schedule(sc);
+
+ contraction = isl_union_pw_multi_aff_copy(grouping.contraction);
+ expansion = isl_schedule_copy(grouping.schedule);
+ res = isl_schedule_expand(res, contraction, expansion);
+
+ ppcg_grouping_clear(&grouping);
+ return res;
+error:
+ ppcg_grouping_clear(&grouping);
+ isl_schedule_constraints_free(sc);
+ return NULL;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/hybrid.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/hybrid.c
new file mode 100644
index 00000000000..b0a047cb5c9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/hybrid.c
@@ -0,0 +1,2242 @@
+/*
+ * Copyright 2013 Ecole Normale Superieure
+ * Copyright 2015 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <string.h>
+
+#include <isl/space.h>
+#include <isl/constraint.h>
+#include <isl/val.h>
+#include <isl/aff.h>
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+
+#include "hybrid.h"
+#include "schedule.h"
+
+/* The hybrid tiling implemented in this file is based on
+ * Grosser et al., "Hybrid Hexagonal/Classical Tiling for GPUs".
+ */
+
+/* Bounds on relative dependence distances in input to hybrid tiling.
+ * upper is an upper bound on the relative dependence distances
+ * in the first space dimension
+ * -lower is a lower bound on the relative dependence distances
+ * in all space dimensions.
+ *
+ * In particular,
+ *
+ * d_i >= -lower_i d_0
+ * and
+ * d_1 <= upper d_0
+ *
+ * for each dependence distance vector d, where d_1 is the component
+ * corresponding to the first space dimension.
+ *
+ * upper and lower are always non-negative.
+ * Some of the values may be NaN if no bound could be found.
+ */
+struct ppcg_ht_bounds {
+ isl_val *upper;
+ isl_multi_val *lower;
+};
+
+/* Free "bounds" along with all its fields.
+ */
+__isl_null ppcg_ht_bounds *ppcg_ht_bounds_free(
+ __isl_take ppcg_ht_bounds *bounds)
+{
+ if (!bounds)
+ return NULL;
+ isl_val_free(bounds->upper);
+ isl_multi_val_free(bounds->lower);
+ free(bounds);
+
+ return NULL;
+}
+
+/* Create a ppcg_ht_bounds object for a band living in "space".
+ * The bounds are initialized to NaN.
+ */
+__isl_give ppcg_ht_bounds *ppcg_ht_bounds_alloc(__isl_take isl_space *space)
+{
+ int i, n;
+ isl_ctx *ctx;
+ ppcg_ht_bounds *bounds;
+
+ if (!space)
+ return NULL;
+
+ ctx = isl_space_get_ctx(space);
+ bounds = isl_alloc_type(ctx, struct ppcg_ht_bounds);
+ if (!bounds)
+ goto error;
+ bounds->upper = isl_val_nan(ctx);
+ bounds->lower = isl_multi_val_zero(space);
+ n = isl_multi_val_dim(bounds->lower, isl_dim_set);
+ for (i = 0; i < n; ++i) {
+ isl_val *v = isl_val_copy(bounds->upper);
+ bounds->lower = isl_multi_val_set_val(bounds->lower, i, v);
+ }
+
+ if (!bounds->lower || !bounds->upper)
+ return ppcg_ht_bounds_free(bounds);
+
+ return bounds;
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+void ppcg_ht_bounds_dump(__isl_keep ppcg_ht_bounds *bounds)
+{
+ if (!bounds)
+ return;
+
+ fprintf(stderr, "lower: ");
+ isl_multi_val_dump(bounds->lower);
+ fprintf(stderr, "upper: ");
+ isl_val_dump(bounds->upper);
+}
+
+/* Return the upper bound on the relative dependence distances
+ * in the first space dimension.
+ */
+__isl_give isl_val *ppcg_ht_bounds_get_upper(__isl_keep ppcg_ht_bounds *bounds)
+{
+ if (!bounds)
+ return NULL;
+ return isl_val_copy(bounds->upper);
+}
+
+/* Replace the upper bound on the relative dependence distances
+ * in the first space dimension by "upper".
+ */
+__isl_give ppcg_ht_bounds *ppcg_ht_bounds_set_upper(
+ __isl_take ppcg_ht_bounds *bounds, __isl_take isl_val *upper)
+{
+ if (!bounds || !upper)
+ goto error;
+ isl_val_free(bounds->upper);
+ bounds->upper = upper;
+ return bounds;
+error:
+ ppcg_ht_bounds_free(bounds);
+ isl_val_free(upper);
+ return NULL;
+}
+
+/* Return the lower bound on the relative dependence distances
+ * in space dimension "pos".
+ */
+__isl_give isl_val *ppcg_ht_bounds_get_lower(__isl_keep ppcg_ht_bounds *bounds,
+ int pos)
+{
+ if (!bounds)
+ return NULL;
+ return isl_multi_val_get_val(bounds->lower, pos);
+}
+
+/* Replace the lower bound on the relative dependence distances
+ * in space dimension "pos" by "lower".
+ */
+__isl_give ppcg_ht_bounds *ppcg_ht_bounds_set_lower(
+ __isl_take ppcg_ht_bounds *bounds, int pos, __isl_take isl_val *lower)
+{
+ if (!bounds || !lower)
+ goto error;
+ bounds->lower = isl_multi_val_set_val(bounds->lower, pos, lower);
+ if (!bounds->lower)
+ return ppcg_ht_bounds_free(bounds);
+ return bounds;
+error:
+ ppcg_ht_bounds_free(bounds);
+ isl_val_free(lower);
+ return NULL;
+}
+
+/* Can the bounds on relative dependence distances recorded in "bounds"
+ * be used to perform hybrid tiling?
+ * In particular, have appropriate lower and upper bounds been found?
+ * Any NaN indicates that no corresponding bound was found.
+ */
+isl_bool ppcg_ht_bounds_is_valid(__isl_keep ppcg_ht_bounds *bounds)
+{
+ isl_bool is_nan;
+ int i, n;
+
+ if (!bounds)
+ return isl_bool_error;
+ is_nan = isl_val_is_nan(bounds->upper);
+ if (is_nan < 0)
+ return isl_bool_error;
+ if (is_nan)
+ return isl_bool_false;
+
+ n = isl_multi_val_dim(bounds->lower, isl_dim_set);
+ for (i = 0; i < n; ++i) {
+ isl_val *v;
+
+ v = isl_multi_val_get_val(bounds->lower, i);
+ is_nan = isl_val_is_nan(v);
+ if (is_nan < 0)
+ return isl_bool_error;
+ if (is_nan)
+ return isl_bool_false;
+ isl_val_free(v);
+ }
+
+ return isl_bool_true;
+}
+
+/* Structure that represents the basic hexagonal tiling,
+ * along with information that is needed to perform the hybrid tiling.
+ *
+ * "bounds" are the bounds on the dependence distances that
+ * define the hexagonal shape and the required skewing in the remaining
+ * space dimensions.
+ *
+ * "input_node" points to the input pair of band nodes.
+ * "input_schedule" is the partial schedule of this input pair of band nodes.
+ * The space of this schedule is [P -> C], where P is the space
+ * of the parent node and C is the space of the child node.
+ *
+ * "space_sizes" represent the total size of a tile for the space
+ * dimensions, i.e., those corresponding to the child node.
+ * The space of "space_sizes" is C.
+ * If S_0 is the original tile size in the first space dimension,
+ * then the first entry of "space_sizes" is equal to
+ * W = 2*S_0 + floor(d_l h) + floor(d_u h).
+ * The remaining entries are the same as in the original tile sizes.
+ *
+ * The basic hexagonal tiling "hex" is defined
+ * in a "ts" (time-space) space and corresponds to the phase-1 tiles.
+ * "time_tile" maps the "ts" space to outer time tile.
+ * Is is equal to ts[t, s] -> floor(t/(2 * S_t)), with S_t the original tile
+ * size corresponding to the parent node.
+ * "local_time" maps the "ts" space to the time dimension inside each tile.
+ * It is equal to ts[t, s] -> t mod (2 S_t), with S_t the original tile
+ * size corresponding to the parent node.
+ * "shift_space" shifts the tiles at time tile T = floor(t/(2 S_t))
+ * in the space dimension such that they align to a multiple of W.
+ * It is equal to ts[t, s] -> s + (-(2 * shift_s)*T) % W,
+ * with shift_s = S_0 + floor(d_u h).
+ * "shift_phase" is the shift taken to go from phase 0 to phase 1
+ * It is equal to ts[t, s] -> ts[t + S_t, s + shift_s],
+ * with shift_s = S_0 + floor(d_u h).
+ *
+ * "project_ts" projects the space of the input schedule to the ts-space.
+ * It is equal to [P[t] -> C[s_0, ...]] -> ts[t, s_0].
+ */
+struct ppcg_ht_tiling {
+ int ref;
+
+ ppcg_ht_bounds *bounds;
+ isl_schedule_node *input_node;
+ isl_multi_union_pw_aff *input_schedule;
+
+ isl_multi_val *space_sizes;
+
+ isl_aff *time_tile;
+ isl_aff *local_time;
+ isl_aff *shift_space;
+ isl_multi_aff *shift_phase;
+ isl_set *hex;
+
+ isl_multi_aff *project_ts;
+};
+typedef struct ppcg_ht_tiling ppcg_ht_tiling;
+
+/* Return the space of the pair of band nodes that form the input
+ * to the hybrid tiling.
+ * In particular, return the space [P -> C], where P is the space
+ * of the parent node and C is the space of the child node.
+ */
+__isl_give isl_space *ppcg_ht_tiling_get_input_space(
+ __isl_keep ppcg_ht_tiling *tile)
+{
+ if (!tile)
+ return NULL;
+
+ return isl_multi_union_pw_aff_get_space(tile->input_schedule);
+}
+
+/* Remove a reference to "tile" and free "tile" along with all its fields
+ * as soon as the reference count drops to zero.
+ */
+static __isl_null ppcg_ht_tiling *ppcg_ht_tiling_free(
+ __isl_take ppcg_ht_tiling *tiling)
+{
+ if (!tiling)
+ return NULL;
+ if (--tiling->ref > 0)
+ return NULL;
+
+ ppcg_ht_bounds_free(tiling->bounds);
+ isl_schedule_node_free(tiling->input_node);
+ isl_multi_union_pw_aff_free(tiling->input_schedule);
+ isl_multi_val_free(tiling->space_sizes);
+ isl_aff_free(tiling->time_tile);
+ isl_aff_free(tiling->local_time);
+ isl_aff_free(tiling->shift_space);
+ isl_multi_aff_free(tiling->shift_phase);
+ isl_set_free(tiling->hex);
+ isl_multi_aff_free(tiling->project_ts);
+ free(tiling);
+
+ return NULL;
+}
+
+/* Return a new reference to "tiling".
+ */
+__isl_give ppcg_ht_tiling *ppcg_ht_tiling_copy(
+ __isl_keep ppcg_ht_tiling *tiling)
+{
+ if (!tiling)
+ return NULL;
+
+ tiling->ref++;
+ return tiling;
+}
+
+/* Return the isl_ctx to which "tiling" belongs.
+ */
+isl_ctx *ppcg_ht_tiling_get_ctx(__isl_keep ppcg_ht_tiling *tiling)
+{
+ if (!tiling)
+ return NULL;
+
+ return isl_multi_union_pw_aff_get_ctx(tiling->input_schedule);
+}
+
+/* Representation of one of the two phases of hybrid tiling.
+ *
+ * "tiling" points to the shared tiling data.
+ *
+ * "time_tile", "local_time" and "shift_space" are equal to the corresponding
+ * fields of "tiling", pulled back to the input space.
+ * In case of phase 0, these expressions have also been moved
+ * from phase 1 to phase 0.
+ *
+ * "domain" contains the hexagonal tiling of this phase.
+ *
+ * "space_shift" is the shift that should be added to the space band
+ * in order to be able to apply rectangular tiling to the space.
+ * For phase 1, it is equal to
+ *
+ * [P[t] -> C[s_0, s_i]] -> C[(-(2 * shift_s)*T) % W, dl_i * u]
+ *
+ * with shift_s = S_0 + floor(d_u h),
+ * T equal to "time_tile" and u equal to "local_time".
+ * For phase 0, it is equal to
+ *
+ * [P[t] -> C[s_0, s_i]] -> C[shift_s + (-(2 * shift_s)*T) % W, dl_i * u]
+ *
+ * "space_tile" is the space tiling. It is equal to
+ *
+ * [P[t] -> C[s]] -> C[floor((s + space_shift)/space_size]
+ */
+struct ppcg_ht_phase {
+ ppcg_ht_tiling *tiling;
+
+ isl_aff *time_tile;
+ isl_aff *local_time;
+ isl_aff *shift_space;
+ isl_set *domain;
+
+ isl_multi_aff *space_shift;
+ isl_multi_aff *space_tile;
+};
+
+/* Free "phase" along with all its fields.
+ */
+static __isl_null ppcg_ht_phase *ppcg_ht_phase_free(
+ __isl_take ppcg_ht_phase *phase)
+{
+ if (!phase)
+ return NULL;
+
+ ppcg_ht_tiling_free(phase->tiling);
+ isl_aff_free(phase->time_tile);
+ isl_aff_free(phase->local_time);
+ isl_aff_free(phase->shift_space);
+ isl_set_free(phase->domain);
+ isl_multi_aff_free(phase->space_shift);
+ isl_multi_aff_free(phase->space_tile);
+ free(phase);
+
+ return NULL;
+}
+
+/* Wrapper around ppcg_ht_phase_free for use as an argument
+ * to isl_id_set_free_user.
+ */
+static void ppcg_ht_phase_free_wrap(void *user)
+{
+ ppcg_ht_phase *phase = user;
+
+ ppcg_ht_phase_free(phase);
+}
+
+/* Return the domain of hybrid tiling phase "phase".
+ */
+static __isl_give isl_set *ppcg_ht_phase_get_domain(ppcg_ht_phase *phase)
+{
+ if (!phase)
+ return NULL;
+
+ return isl_set_copy(phase->domain);
+}
+
+/* Return the space of the pair of band nodes that form the input
+ * to the hybrid tiling of which "phase" is a phase.
+ * In particular, return the space [P -> C], where P is the space
+ * of the parent node and C is the space of the child node.
+ */
+static __isl_give isl_space *ppcg_ht_phase_get_input_space(
+ __isl_keep ppcg_ht_phase *phase)
+{
+ if (!phase)
+ return NULL;
+
+ return ppcg_ht_tiling_get_input_space(phase->tiling);
+}
+
+/* Construct the lower left constraint of the hexagonal tile, i.e.,
+ *
+ * du a - b <= (2h+1) du - duh
+ * -du a + b + (2h+1) du - duh >= 0
+ *
+ * where duh = floor(du * h).
+ *
+ * This constraint corresponds to (6) in
+ * "Hybrid Hexagonal/Classical Tiling for GPUs".
+ */
+static __isl_give isl_constraint *hex_lower_left(__isl_take isl_local_space *ls,
+ __isl_keep isl_val *h, __isl_keep isl_val *du, __isl_keep isl_val *duh)
+{
+ isl_val *v;
+ isl_aff *aff;
+
+ v = isl_val_add_ui(isl_val_mul_ui(isl_val_copy(h), 2), 1);
+ v = isl_val_mul(v, isl_val_copy(du));
+ v = isl_val_sub(v, isl_val_copy(duh));
+ aff = isl_aff_val_on_domain(ls, v);
+ v = isl_val_neg(isl_val_copy(du));
+ aff = isl_aff_set_coefficient_val(aff, isl_dim_in, 0, v);
+ aff = isl_aff_set_coefficient_si(aff, isl_dim_in, 1, 1);
+
+ return isl_inequality_from_aff(aff);
+}
+
+/* Construct the lower constraint of the hexagonal tile, i.e.,
+ *
+ * a <= 2h+1
+ * -a + 2h+1 >= 0
+ *
+ * This constraint corresponds to (7) in
+ * "Hybrid Hexagonal/Classical Tiling for GPUs".
+ */
+static __isl_give isl_constraint *hex_lower(__isl_take isl_local_space *ls,
+ __isl_keep isl_val *h)
+{
+ isl_val *v;
+ isl_aff *aff;
+
+ v = isl_val_add_ui(isl_val_mul_ui(isl_val_copy(h), 2), 1);
+ aff = isl_aff_val_on_domain(ls, v);
+ aff = isl_aff_set_coefficient_si(aff, isl_dim_in, 0, -1);
+
+ return isl_inequality_from_aff(aff);
+}
+
+/* Construct the lower right constraint of the hexagonal tile, i.e.,
+ *
+ * dl a + b <= (2h+1) dl + duh + (s0-1)
+ * -dl a - b + (2h+1) dl + duh + (s0-1) >= 0
+ *
+ * where duh = floor(du * h).
+ *
+ * This constraint corresponds to (8) in
+ * "Hybrid Hexagonal/Classical Tiling for GPUs".
+ */
+static __isl_give isl_constraint *hex_lower_right(
+ __isl_take isl_local_space *ls, __isl_keep isl_val *h,
+ __isl_keep isl_val *s0, __isl_keep isl_val *dl, __isl_keep isl_val *duh)
+{
+ isl_val *v;
+ isl_aff *aff;
+
+ v = isl_val_add_ui(isl_val_mul_ui(isl_val_copy(h), 2), 1);
+ v = isl_val_mul(v, isl_val_copy(dl));
+ v = isl_val_add(v, isl_val_copy(duh));
+ v = isl_val_add(v, isl_val_copy(s0));
+ v = isl_val_sub_ui(v, 1);
+ aff = isl_aff_val_on_domain(ls, v);
+ v = isl_val_neg(isl_val_copy(dl));
+ aff = isl_aff_set_coefficient_val(aff, isl_dim_in, 0, v);
+ aff = isl_aff_set_coefficient_si(aff, isl_dim_in, 1, -1);
+
+ return isl_inequality_from_aff(aff);
+}
+
+/* Construct the upper left constraint of the hexagonal tile, i.e.,
+ *
+ * dl a + b >= h dl - (d - 1)/d with d = den(dl)
+ * dl a + b - h dl + (d - 1)/d >= 0
+ *
+ * This constraint corresponds to (10) in
+ * "Hybrid Hexagonal/Classical Tiling for GPUs".
+ */
+static __isl_give isl_constraint *hex_upper_left(__isl_take isl_local_space *ls,
+ __isl_keep isl_val *h, __isl_keep isl_val *dl)
+{
+ isl_val *v, *d;
+ isl_aff *aff;
+
+ d = isl_val_get_den_val(dl);
+ v = isl_val_sub_ui(isl_val_copy(d), 1);
+ v = isl_val_div(v, d);
+ v = isl_val_sub(v, isl_val_mul(isl_val_copy(h), isl_val_copy(dl)));
+ aff = isl_aff_val_on_domain(ls, v);
+ aff = isl_aff_set_coefficient_val(aff, isl_dim_in, 0, isl_val_copy(dl));
+ aff = isl_aff_set_coefficient_si(aff, isl_dim_in, 1, 1);
+
+ return isl_inequality_from_aff(aff);
+}
+
+/* Construct the upper right constraint of the hexagonal tile, i.e.,
+ *
+ * du a - b >= du h - duh - (s0-1) - dlh - (d - 1)/d with d = den(du)
+ * du a - b - du h + duh + (s0-1) + dlh + (d - 1)/d >= 0
+ *
+ * where dlh = floor(dl * h) and duh = floor(du * h).
+ *
+ * This constraint corresponds to (12) in
+ * "Hybrid Hexagonal/Classical Tiling for GPUs".
+ */
+static __isl_give isl_constraint *hex_upper_right(
+ __isl_take isl_local_space *ls, __isl_keep isl_val *h,
+ __isl_keep isl_val *s0, __isl_keep isl_val *du,
+ __isl_keep isl_val *dlh, __isl_keep isl_val *duh)
+{
+ isl_val *v, *d;
+ isl_aff *aff;
+
+ d = isl_val_get_den_val(du);
+ v = isl_val_sub_ui(isl_val_copy(d), 1);
+ v = isl_val_div(v, d);
+ v = isl_val_sub(v, isl_val_mul(isl_val_copy(h), isl_val_copy(du)));
+ v = isl_val_add(v, isl_val_copy(duh));
+ v = isl_val_add(v, isl_val_copy(dlh));
+ v = isl_val_add(v, isl_val_copy(s0));
+ v = isl_val_sub_ui(v, 1);
+ aff = isl_aff_val_on_domain(ls, v);
+ aff = isl_aff_set_coefficient_val(aff, isl_dim_in, 0, isl_val_copy(du));
+ aff = isl_aff_set_coefficient_si(aff, isl_dim_in, 1, -1);
+
+ return isl_inequality_from_aff(aff);
+}
+
+/* Construct the uppper constraint of the hexagonal tile, i.e.,
+ *
+ * a >= 0
+ *
+ * This constraint corresponds to (13) in
+ * "Hybrid Hexagonal/Classical Tiling for GPUs".
+ */
+static __isl_give isl_constraint *hex_upper(__isl_take isl_local_space *ls)
+{
+ isl_aff *aff;
+
+ aff = isl_aff_var_on_domain(ls, isl_dim_set, 0);
+
+ return isl_inequality_from_aff(aff);
+}
+
+/* Construct the basic hexagonal tile shape.
+ * "space" is the 2D space in which the hexagon should be constructed.
+ * h is st-1, with st the tile size in the time dimension
+ * s0 is the tile size in the space dimension
+ * dl is a bound on the negative relative dependence distances, i.e.,
+ *
+ * d_s >= -dl d_t
+ *
+ * du is a bound on the positive relative dependence distances, i.e.,
+ *
+ * d_s <= du d_t
+ *
+ * with (d_t,d_s) any dependence distance vector.
+ * dlh = floor(dl * h)
+ * duh = floor(du * h)
+ *
+ * The shape of the hexagon is as follows:
+ *
+ * 0 dlh dlh+s0-1
+ * ______ __
+ * 0 / \_ /
+ * / \_ /
+ * h / \ ______ /
+ * h+1 \_ // \\_
+ * \_ // \\_
+ * 2h+1 \______// \\
+ * 0 duh duh+s0-1
+ * duh+s0-1+dlh
+ * duh+s0-1+dlh+1+s0+1
+ *
+ * The next hexagon is shifted by duh + dlh + 2 * s0.
+ *
+ * The slope of the "/" constraints is dl.
+ * The slope of the "\_" constraints is du.
+ */
+static __isl_give isl_set *compute_hexagon(__isl_take isl_space *space,
+ __isl_keep isl_val *h, __isl_keep isl_val *s0,
+ __isl_keep isl_val *dl, __isl_keep isl_val *du,
+ __isl_keep isl_val *dlh, __isl_keep isl_val *duh)
+{
+ isl_local_space *ls;
+ isl_constraint *c;
+ isl_basic_set *bset;
+
+ ls = isl_local_space_from_space(space);
+
+ c = hex_lower_left(isl_local_space_copy(ls), h, du, duh);
+ bset = isl_basic_set_from_constraint(c);
+
+ c = hex_lower(isl_local_space_copy(ls), h);
+ bset = isl_basic_set_add_constraint(bset, c);
+
+ c = hex_lower_right(isl_local_space_copy(ls), h, s0, dl, duh);
+ bset = isl_basic_set_add_constraint(bset, c);
+
+ c = hex_upper_left(isl_local_space_copy(ls), h, dl);
+ bset = isl_basic_set_add_constraint(bset, c);
+
+ c = hex_upper_right(isl_local_space_copy(ls), h, s0, du, dlh, duh);
+ bset = isl_basic_set_add_constraint(bset, c);
+
+ c = hex_upper(ls);
+ bset = isl_basic_set_add_constraint(bset, c);
+
+ return isl_set_from_basic_set(bset);
+}
+
+/* Name of the ts-space.
+ */
+static const char *ts_space_name = "ts";
+
+/* Construct and return the space ts[t, s].
+ */
+static __isl_give isl_space *construct_ts_space(isl_ctx *ctx)
+{
+ isl_space *s;
+
+ s = isl_space_set_alloc(ctx, 0, 2);
+ s = isl_space_set_tuple_name(s, isl_dim_set, ts_space_name);
+
+ return s;
+}
+
+/* Name of the local ts-space.
+ */
+static const char *local_ts_space_name = "local_ts";
+
+/* Construct and return the space local_ts[t, s].
+ */
+static __isl_give isl_space *construct_local_ts_space(isl_ctx *ctx)
+{
+ isl_space *s;
+
+ s = isl_space_set_alloc(ctx, 0, 2);
+ s = isl_space_set_tuple_name(s, isl_dim_set, local_ts_space_name);
+
+ return s;
+}
+
+/* Compute the total size of a tile for the space dimensions,
+ * i.e., those corresponding to the child node
+ * of the input pattern.
+ * If S_0 is the original tile size in the first space dimension,
+ * then the first entry of "space_sizes" is equal to
+ * W = 2*S_0 + floor(d_l h) + floor(d_u h).
+ * The remaining entries are the same as in the original tile sizes.
+ * "tile_sizes" contains the original tile sizes, including
+ * the tile size corresponding to the parent node.
+ * "dlh" is equal to floor(d_l h).
+ * "duh" is equal to floor(d_u h).
+ */
+static __isl_give isl_multi_val *compute_space_sizes(
+ __isl_keep isl_multi_val *tile_sizes,
+ __isl_keep isl_val *dlh, __isl_keep isl_val *duh)
+{
+ isl_val *size;
+ isl_multi_val *space_sizes;
+
+ space_sizes = isl_multi_val_copy(tile_sizes);
+ space_sizes = isl_multi_val_factor_range(space_sizes);
+ size = isl_multi_val_get_val(space_sizes, 0);
+ size = isl_val_mul_ui(size, 2);
+ size = isl_val_add(size, isl_val_copy(duh));
+ size = isl_val_add(size, isl_val_copy(dlh));
+ space_sizes = isl_multi_val_set_val(space_sizes, 0, size);
+
+ return space_sizes;
+}
+
+/* Compute the offset of phase 1 with respect to phase 0
+ * in the ts-space ("space").
+ * In particular, return
+ *
+ * ts[st, s0 + duh]
+ */
+static __isl_give isl_multi_val *compute_phase_shift(
+ __isl_keep isl_space *space, __isl_keep isl_val *st,
+ __isl_keep isl_val *s0, __isl_keep isl_val *duh)
+{
+ isl_val *v;
+ isl_multi_val *phase_shift;
+
+ phase_shift = isl_multi_val_zero(isl_space_copy(space));
+ phase_shift = isl_multi_val_set_val(phase_shift, 0, isl_val_copy(st));
+ v = isl_val_add(isl_val_copy(duh), isl_val_copy(s0));
+ phase_shift = isl_multi_val_set_val(phase_shift, 1, v);
+
+ return phase_shift;
+}
+
+/* Return the function
+ *
+ * ts[t, s] -> floor(t/(2 * st))
+ *
+ * representing the time tile.
+ * "space" is the space ts[t, s].
+ */
+static __isl_give isl_aff *compute_time_tile(__isl_keep isl_space *space,
+ __isl_keep isl_val *st)
+{
+ isl_val *v;
+ isl_aff *t;
+ isl_local_space *ls;
+
+ ls = isl_local_space_from_space(isl_space_copy(space));
+ t = isl_aff_var_on_domain(ls, isl_dim_set, 0);
+ v = isl_val_mul_ui(isl_val_copy(st), 2);
+ t = isl_aff_floor(isl_aff_scale_down_val(t, v));
+
+ return t;
+}
+
+/* Compute a shift in the space dimension for tiles
+ * at time tile T = floor(t/(2 * S_t))
+ * such that they align to a multiple of the total space tile dimension W.
+ * In particular, compute
+ *
+ * ts[t, s] -> s + (-(2 * shift_s)*T) % W
+ *
+ * where shift_s is the shift of phase 1 with respect to phase 0
+ * in the space dimension (the first element of "phase_shift").
+ * W is stored in the first element of "space_sizes".
+ * "time_tile" is the function
+ *
+ * ts[t, s] -> floor(t/(2 * S_T))
+ *
+ * Since phase 1 is shifted by shift_s with respect to phase 0,
+ * the next line of phase 0 (at T+1) is shifted by 2*shift_s
+ * with respect to the previous line (at T).
+ * A shift of -(2 * shift_s)*T therefore allows the basic pattern
+ * (which starts at 0) to be applied.
+ * However, this shift will be used to obtain the tile coordinate
+ * in the first space dimension and if the original values
+ * in the space dimension are non-negative, then the shift should
+ * not make them negative. Moreover, the shift should be as minimal
+ * as possible.
+ * Since the pattern repeats itself with a period of W in the space
+ * dimension, the shift can be replaced by (-(2 * shift_s)*T) % W.
+ */
+static __isl_give isl_aff *compute_shift_space(__isl_keep isl_aff *time_tile,
+ __isl_keep isl_multi_val *space_sizes,
+ __isl_keep isl_multi_val *phase_shift)
+{
+ isl_val *v;
+ isl_aff *s, *t;
+ isl_local_space *ls;
+
+ ls = isl_local_space_from_space(isl_aff_get_domain_space(time_tile));
+ t = isl_aff_copy(time_tile);
+ v = isl_val_mul_ui(isl_multi_val_get_val(phase_shift, 1), 2);
+ v = isl_val_neg(v);
+ t = isl_aff_scale_val(t, v);
+ v = isl_multi_val_get_val(space_sizes, 0);
+ t = isl_aff_mod_val(t, v);
+ s = isl_aff_var_on_domain(ls, isl_dim_set, 1);
+ s = isl_aff_add(s, t);
+
+ return s;
+}
+
+/* Give the phase_shift ts[S_t, S_0 + floor(d_u h)],
+ * compute a function that applies the shift, i.e.,
+ *
+ * ts[t, s] -> ts[t + S_t, s + S_0 + floor(d_u h)],
+ */
+static __isl_give isl_multi_aff *compute_shift_phase(
+ __isl_keep isl_multi_val *phase_shift)
+{
+ isl_space *space;
+ isl_multi_aff *shift;
+
+ space = isl_multi_val_get_space(phase_shift);
+ shift = isl_multi_aff_multi_val_on_space(space,
+ isl_multi_val_copy(phase_shift));
+ space = isl_multi_aff_get_space(shift);
+ shift = isl_multi_aff_add(shift, isl_multi_aff_identity(space));
+
+ return shift;
+}
+
+/* Compute a mapping from the ts-space to the local coordinates
+ * within each tile. In particular, compute
+ *
+ * ts[t, s] -> local_ts[t % (2 S_t), (s + (-(2 * shift_s)*T) % W) % W]
+ *
+ * "ts" is the space ts[t, s]
+ * "local_ts" is the space local_ts[t, s]
+ * "shift_space" is equal to ts[t, s] -> s + (-(2 * shift_s)*T) % W
+ * "st" is the tile size in the time dimension S_t.
+ * The first element of "space_sizes" is equal to W.
+ */
+static __isl_give isl_multi_aff *compute_localize(
+ __isl_keep isl_space *local_ts, __isl_keep isl_aff *shift_space,
+ __isl_keep isl_val *st, __isl_keep isl_multi_val *space_sizes)
+{
+ isl_val *v;
+ isl_space *space;
+ isl_aff *s, *t;
+ isl_multi_aff *localize;
+
+ space = isl_aff_get_domain_space(shift_space);
+ local_ts = isl_space_copy(local_ts);
+ space = isl_space_map_from_domain_and_range(space, local_ts);
+ localize = isl_multi_aff_identity(space);
+ t = isl_multi_aff_get_aff(localize, 0);
+ v = isl_val_mul_ui(isl_val_copy(st), 2);
+ t = isl_aff_mod_val(t, v);
+ localize = isl_multi_aff_set_aff(localize, 0, t);
+ s = isl_aff_copy(shift_space);
+ v = isl_multi_val_get_val(space_sizes, 0);
+ s = isl_aff_mod_val(s, v);
+ localize = isl_multi_aff_set_aff(localize, 1, s);
+
+ return localize;
+}
+
+/* Set the project_ts field of "tiling".
+ *
+ * This field projects the space of the input schedule to the ts-space.
+ * It is equal to [P[t] -> C[s_0, ...]] -> ts[t, s_0].
+ */
+static __isl_give ppcg_ht_tiling *ppcg_ht_tiling_set_project_ts(
+ __isl_take ppcg_ht_tiling *tiling)
+{
+ int n;
+ isl_space *space;
+ isl_multi_aff *project;
+
+ if (!tiling)
+ return NULL;
+
+ space = ppcg_ht_tiling_get_input_space(tiling);
+ n = isl_space_dim(space, isl_dim_set);
+ project = isl_multi_aff_project_out_map(space, isl_dim_set, 2, n - 2);
+ project = isl_multi_aff_set_tuple_name(project,
+ isl_dim_out, ts_space_name);
+ if (!project)
+ return ppcg_ht_tiling_free(tiling);
+
+ tiling->project_ts = project;
+
+ return tiling;
+}
+
+/* Construct a hybrid tiling description from bounds on the dependence
+ * distances "bounds".
+ * "input_node" points to the original parent node.
+ * "input_schedule" is the combined schedule of the parent and child
+ * node in the input.
+ * "tile_sizes" are the original, user specified tile sizes.
+ */
+static __isl_give ppcg_ht_tiling *ppcg_ht_bounds_construct_tiling(
+ __isl_take ppcg_ht_bounds *bounds,
+ __isl_keep isl_schedule_node *input_node,
+ __isl_keep isl_multi_union_pw_aff *input_schedule,
+ __isl_keep isl_multi_val *tile_sizes)
+{
+ isl_ctx *ctx;
+ ppcg_ht_tiling *tiling;
+ isl_multi_val *space_sizes, *phase_shift;
+ isl_aff *time_tile, *shift_space;
+ isl_multi_aff *localize;
+ isl_val *h, *duh, *dlh;
+ isl_val *st, *s0, *du, *dl;
+ isl_space *ts, *local_ts;
+
+ if (!bounds || !input_node || !input_schedule || !tile_sizes)
+ goto error;
+
+ ctx = isl_multi_union_pw_aff_get_ctx(input_schedule);
+ tiling = isl_calloc_type(ctx, struct ppcg_ht_tiling);
+ if (!tiling)
+ goto error;
+ tiling->ref = 1;
+
+ st = isl_multi_val_get_val(tile_sizes, 0);
+ h = isl_val_sub_ui(isl_val_copy(st), 1);
+ s0 = isl_multi_val_get_val(tile_sizes, 1);
+ du = ppcg_ht_bounds_get_upper(bounds);
+ dl = ppcg_ht_bounds_get_lower(bounds, 0);
+
+ duh = isl_val_floor(isl_val_mul(isl_val_copy(du), isl_val_copy(h)));
+ dlh = isl_val_floor(isl_val_mul(isl_val_copy(dl), isl_val_copy(h)));
+
+ ts = construct_ts_space(ctx);
+ local_ts = construct_local_ts_space(ctx);
+
+ space_sizes = compute_space_sizes(tile_sizes, dlh, duh);
+ phase_shift = compute_phase_shift(ts, st, s0, duh);
+ time_tile = compute_time_tile(ts, st);
+ shift_space = compute_shift_space(time_tile, space_sizes, phase_shift);
+ localize = compute_localize(local_ts, shift_space, st, space_sizes);
+ isl_space_free(ts);
+
+ tiling->input_node = isl_schedule_node_copy(input_node);
+ tiling->input_schedule = isl_multi_union_pw_aff_copy(input_schedule);
+ tiling->space_sizes = space_sizes;
+ tiling->bounds = bounds;
+ tiling->local_time = isl_multi_aff_get_aff(localize, 0);
+ tiling->hex = compute_hexagon(local_ts, h, s0, dl, du, dlh, duh);
+ tiling->hex = isl_set_preimage_multi_aff(tiling->hex, localize);
+ tiling->time_tile = time_tile;
+ tiling->shift_space = shift_space;
+ tiling->shift_phase = compute_shift_phase(phase_shift);
+ isl_multi_val_free(phase_shift);
+
+ isl_val_free(duh);
+ isl_val_free(dlh);
+ isl_val_free(du);
+ isl_val_free(dl);
+ isl_val_free(s0);
+ isl_val_free(st);
+ isl_val_free(h);
+
+ if (!tiling->input_schedule || !tiling->local_time || !tiling->hex ||
+ !tiling->shift_space || !tiling->shift_phase)
+ return ppcg_ht_tiling_free(tiling);
+
+ tiling = ppcg_ht_tiling_set_project_ts(tiling);
+
+ return tiling;
+error:
+ ppcg_ht_bounds_free(bounds);
+ return NULL;
+}
+
+/* Are all members of the band node "node" coincident?
+ */
+static isl_bool all_coincident(__isl_keep isl_schedule_node *node)
+{
+ int i, n;
+
+ n = isl_schedule_node_band_n_member(node);
+ for (i = 0; i < n; ++i) {
+ isl_bool c;
+
+ c = isl_schedule_node_band_member_get_coincident(node, i);
+ if (c < 0 || !c)
+ return c;
+ }
+
+ return isl_bool_true;
+}
+
+/* Does "node" satisfy the properties of the inner node in the input
+ * pattern for hybrid tiling?
+ * That is, is it a band node with only coincident members, of which
+ * there is at least one?
+ */
+static isl_bool has_child_properties(__isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return isl_bool_error;
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_band)
+ return isl_bool_false;
+ if (isl_schedule_node_band_n_member(node) < 1)
+ return isl_bool_false;
+ return all_coincident(node);
+}
+
+/* Does "node" satisfy the properties of the outer node in the input
+ * pattern for hybrid tiling?
+ * That is, is it a band node with a single member?
+ */
+static isl_bool has_parent_properties(__isl_keep isl_schedule_node *node)
+{
+ if (!node)
+ return isl_bool_error;
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_band)
+ return isl_bool_false;
+ if (isl_schedule_node_band_n_member(node) != 1)
+ return isl_bool_false;
+ return isl_bool_true;
+}
+
+/* Does the parent of "node" satisfy the input patttern for hybrid tiling?
+ * That is, does "node" satisfy the properties of the inner node and
+ * does the parent of "node" satisfy the properties of the outer node?
+ */
+isl_bool ppcg_ht_parent_has_input_pattern(__isl_keep isl_schedule_node *node)
+{
+ isl_bool has_pattern;
+
+ has_pattern = has_child_properties(node);
+ if (has_pattern < 0 || !has_pattern)
+ return has_pattern;
+
+ node = isl_schedule_node_copy(node);
+ node = isl_schedule_node_parent(node);
+ has_pattern = has_parent_properties(node);
+ isl_schedule_node_free(node);
+
+ return has_pattern;
+}
+
+/* Does "node" satisfy the input patttern for hybrid tiling?
+ * That is, does "node" satisfy the properties of the outer node and
+ * does the child of "node" satisfy the properties of the inner node?
+ */
+isl_bool ppcg_ht_has_input_pattern(__isl_keep isl_schedule_node *node)
+{
+ isl_bool has_pattern;
+
+ has_pattern = has_parent_properties(node);
+ if (has_pattern < 0 || !has_pattern)
+ return has_pattern;
+
+ node = isl_schedule_node_get_child(node, 0);
+ has_pattern = has_child_properties(node);
+ isl_schedule_node_free(node);
+
+ return has_pattern;
+}
+
+/* Check that "node" satisfies the input pattern for hybrid tiling.
+ * Error out if it does not.
+ */
+static isl_stat check_input_pattern(__isl_keep isl_schedule_node *node)
+{
+ isl_bool has_pattern;
+
+ has_pattern = ppcg_ht_has_input_pattern(node);
+ if (has_pattern < 0)
+ return isl_stat_error;
+ if (!has_pattern)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+ "invalid input pattern for hybrid tiling",
+ return isl_stat_error);
+
+ return isl_stat_ok;
+}
+
+/* Extract the input schedule from "node", i.e., the product
+ * of the partial schedules of the parent and child nodes
+ * in the input pattern.
+ */
+static __isl_give isl_multi_union_pw_aff *extract_input_schedule(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_multi_union_pw_aff *partial, *partial2;
+
+ partial = isl_schedule_node_band_get_partial_schedule(node);
+ node = isl_schedule_node_get_child(node, 0);
+ partial2 = isl_schedule_node_band_get_partial_schedule(node);
+ isl_schedule_node_free(node);
+
+ return isl_multi_union_pw_aff_range_product(partial, partial2);
+}
+
+/* Collect all dependences from "scop" that are relevant for performing
+ * hybrid tiling on "node" and its child and map them to the schedule
+ * space of this pair of nodes.
+ *
+ * In case live range reordering is not used,
+ * the flow and the false dependences are collected.
+ * In case live range reordering is used,
+ * the flow and the forced dependences are collected, as well
+ * as the order dependences that are adjacent to non-local
+ * flow dependences.
+ *
+ * In all cases, only dependences that map to the same instance
+ * of the outer part of the schedule are considered.
+ */
+static __isl_give isl_map *collect_deps(struct ppcg_scop *scop,
+ __isl_keep isl_schedule_node *node)
+{
+ isl_space *space;
+ isl_multi_union_pw_aff *prefix, *partial;
+ isl_union_map *flow, *other, *dep, *umap;
+ isl_map *map;
+
+ prefix = isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
+ partial = extract_input_schedule(node);
+ space = isl_multi_union_pw_aff_get_space(partial);
+
+ flow = isl_union_map_copy(scop->dep_flow);
+ flow = isl_union_map_eq_at_multi_union_pw_aff(flow,
+ isl_multi_union_pw_aff_copy(prefix));
+ if (!scop->options->live_range_reordering) {
+ other = isl_union_map_copy(scop->dep_false);
+ other = isl_union_map_eq_at_multi_union_pw_aff(other, prefix);
+ } else {
+ isl_union_map *local, *non_local, *order, *adj;
+ isl_union_set *domain, *range;
+
+ other = isl_union_map_copy(scop->dep_forced);
+ other = isl_union_map_eq_at_multi_union_pw_aff(other,
+ isl_multi_union_pw_aff_copy(prefix));
+ local = isl_union_map_copy(flow);
+ local = isl_union_map_eq_at_multi_union_pw_aff(local,
+ isl_multi_union_pw_aff_copy(partial));
+ non_local = isl_union_map_copy(flow);
+ non_local = isl_union_map_subtract(non_local, local);
+
+ order = isl_union_map_copy(scop->dep_order);
+ order = isl_union_map_eq_at_multi_union_pw_aff(order, prefix);
+ adj = isl_union_map_copy(order);
+ domain = isl_union_map_domain(isl_union_map_copy(non_local));
+ domain = isl_union_set_coalesce(domain);
+ adj = isl_union_map_intersect_range(adj, domain);
+ other = isl_union_map_union(other, adj);
+
+ adj = order;
+ range = isl_union_map_range(non_local);
+ range = isl_union_set_coalesce(range);
+ adj = isl_union_map_intersect_domain(adj, range);
+ other = isl_union_map_union(other, adj);
+ }
+ dep = isl_union_map_union(flow, other);
+
+ umap = isl_union_map_from_multi_union_pw_aff(partial);
+ dep = isl_union_map_apply_domain(dep, isl_union_map_copy(umap));
+ dep = isl_union_map_apply_range(dep, umap);
+
+ space = isl_space_map_from_set(space);
+ map = isl_union_map_extract_map(dep, space);
+ isl_union_map_free(dep);
+
+ map = isl_map_coalesce(map);
+
+ return map;
+}
+
+/* Given a constraint of the form
+ *
+ * a i_0 + b i_1 >= 0
+ * or
+ * a i_0 + b i_1 = 0
+ *
+ * use it to update one or both of the non-negative bounds
+ * in "list" = (min, max) such that
+ *
+ * i_1 >= -min i_0
+ * and
+ * i_1 <= max i_0
+ *
+ * If b = 0, then the constraint cannot be used.
+ * Otherwise, the constraint is equivalent to
+ *
+ * sgn(b) i_1 >= - a/abs(b) i_0
+ * i.e.,
+ * i_1 >= - a/abs(b) i_0
+ * or
+ * i_1 <= a/abs(b) i_0
+ *
+ * Set the first or second element of "list" to max(0, a/abs(b)),
+ * according to the sign of "b". Or set both in case the constraint
+ * is an equality, taking into account the sign change.
+ */
+static __isl_give isl_val_list *list_set_min_max(__isl_take isl_val_list *list,
+ __isl_keep isl_constraint *c)
+{
+ isl_val *a, *b;
+ int sign;
+ int pos;
+ isl_bool eq, is_zero, is_neg;
+
+ eq = isl_constraint_is_equality(c);
+ if (eq < 0)
+ return isl_val_list_free(list);
+
+ b = isl_constraint_get_coefficient_val(c, isl_dim_set, 1);
+ is_zero = isl_val_is_zero(b);
+ if (is_zero == isl_bool_true) {
+ isl_val_free(b);
+ return list;
+ }
+ a = isl_constraint_get_coefficient_val(c, isl_dim_set, 0);
+ sign = isl_val_sgn(b);
+ b = isl_val_abs(b);
+ a = isl_val_div(a, b);
+
+ if (eq)
+ b = isl_val_copy(a);
+
+ pos = sign > 0 ? 0 : 1;
+ is_neg = isl_val_is_neg(a);
+ if (is_neg == isl_bool_true)
+ a = isl_val_set_si(a, 0);
+ list = isl_val_list_set_val(list, pos, a);
+
+ if (!eq)
+ return is_neg < 0 ? isl_val_list_free(list) : list;
+
+ pos = 1 - pos;
+ a = isl_val_neg(b);
+ is_neg = isl_val_is_neg(a);
+ if (is_neg == isl_bool_true)
+ a = isl_val_set_si(a, 0);
+ list = isl_val_list_set_val(list, pos, a);
+
+ return is_neg < 0 ? isl_val_list_free(list) : list;
+}
+
+/* If constraint "c" passes through the origin, then try and use it
+ * to update the non-negative bounds in "list" = (min, max) such that
+ *
+ * i_1 >= -min i_0
+ * and
+ * i_1 <= max i_0
+ */
+static isl_stat set_min_max(__isl_take isl_constraint *c, void *user)
+{
+ isl_val *v;
+ isl_val_list **list = user;
+ isl_bool is_zero;
+
+ v = isl_constraint_get_constant_val(c);
+ is_zero = isl_val_is_zero(v);
+ isl_val_free(v);
+
+ if (is_zero == isl_bool_true)
+ *list = list_set_min_max(*list, c);
+
+ isl_constraint_free(c);
+ return is_zero < 0 ? isl_stat_error : isl_stat_ok;
+}
+
+/* Given a set of dependence distance vectors "dist", compute
+ * pair of non-negative bounds min and max such that
+ *
+ * d_pos >= -min d_0
+ * and
+ * d_pos <= max d_0
+ *
+ * and return the pair (min, max).
+ * If no bound can be found in either direction, then the bound
+ * is replaced by NaN.
+ *
+ * The dependence distances are first projected onto the (d_0, d_pos).
+ * Then the zero dependence distance is added and the convex hull is computed.
+ * Finally, the bounds are extracted from the constraints of the convex hull
+ * that pass through the origin.
+ */
+static __isl_give isl_val_list *min_max_dist(__isl_keep isl_set *dist, int pos)
+{
+ isl_space *space;
+ isl_basic_set *hull;
+ int dim;
+ isl_ctx *ctx;
+ isl_val *nan;
+ isl_val_list *list;
+
+ ctx = isl_set_get_ctx(dist);
+ nan = isl_val_nan(ctx);
+ list = isl_val_list_alloc(ctx, 2);
+ list = isl_val_list_add(list, isl_val_copy(nan));
+ list = isl_val_list_add(list, nan);
+
+ dist = isl_set_copy(dist);
+ dim = isl_set_dim(dist, isl_dim_set);
+ if (dist && pos >= dim)
+ isl_die(ctx, isl_error_internal, "position out of bounds",
+ dist = isl_set_free(dist));
+ dist = isl_set_project_out(dist, isl_dim_set, pos + 1, dim - (pos + 1));
+ dist = isl_set_project_out(dist, isl_dim_set, 1, pos - 1);
+
+ space = isl_set_get_space(dist);
+ dist = isl_set_union(dist, isl_set_from_point(isl_point_zero(space)));
+ dist = isl_set_remove_divs(dist);
+ hull = isl_set_convex_hull(dist);
+
+ if (isl_basic_set_foreach_constraint(hull, &set_min_max, &list) < 0)
+ list = isl_val_list_free(list);
+ isl_basic_set_free(hull);
+
+ return list;
+}
+
+/* Given a schedule node "node" that, together with its child,
+ * satisfies the input pattern for hybrid tiling, compute bounds
+ * on the relative dependence distances of the child node with
+ * respect to the parent node. These bounds are needed to
+ * construct a hybrid tiling.
+ *
+ * First all relevant dependences are collected and mapped
+ * to the schedule space of the pair of nodes. Then, the
+ * dependence distances are computed in this space.
+ *
+ * These dependence distances are then projected onto a two-dimensional
+ * space consisting of the single schedule dimension of the outer node
+ * and one of the schedule dimensions of the inner node.
+ * The maximal and minimal relative dependence distances are extracted
+ * from these projections.
+ * This process is repeated for each of the schedule dimensions
+ * of the inner node. For the first dimension, both minimal and
+ * maximal relative dependence distances are stored in the result.
+ * For the other dimensions, only the minimal relative dependence
+ * distance is stored.
+ */
+__isl_give ppcg_ht_bounds *ppcg_ht_compute_bounds(struct ppcg_scop *scop,
+ __isl_keep isl_schedule_node *node)
+{
+ ppcg_ht_bounds *bnd;
+ isl_space *space;
+ isl_map *map;
+ isl_set *dist;
+ isl_val_list *pair;
+ isl_schedule_node *child;
+ int n;
+ int i, dim;
+
+ if (!scop || !node || check_input_pattern(node) < 0)
+ return NULL;
+
+ child = isl_schedule_node_get_child(node, 0);
+ space = isl_schedule_node_band_get_space(child);
+ dim = isl_schedule_node_band_n_member(child);
+ isl_schedule_node_free(child);
+ bnd = ppcg_ht_bounds_alloc(space);
+ if (!bnd)
+ return NULL;
+
+ map = collect_deps(scop, node);
+
+ dist = isl_map_deltas(map);
+ n = isl_set_dim(dist, isl_dim_param);
+ dist = isl_set_project_out(dist, isl_dim_param, 0, n);
+
+ pair = min_max_dist(dist, 1);
+ bnd = ppcg_ht_bounds_set_lower(bnd, 0, isl_val_list_get_val(pair, 0));
+ bnd = ppcg_ht_bounds_set_upper(bnd, isl_val_list_get_val(pair, 1));
+ isl_val_list_free(pair);
+
+ for (i = 1; i < dim; ++i) {
+ pair = min_max_dist(dist, 1 + i);
+ bnd = ppcg_ht_bounds_set_lower(bnd, i,
+ isl_val_list_get_val(pair, 0));
+ isl_val_list_free(pair);
+ }
+
+ isl_set_free(dist);
+
+ return bnd;
+}
+
+/* Check if all the fields of "phase" are valid, freeing "phase"
+ * if they are not.
+ */
+static __isl_give ppcg_ht_phase *check_phase(__isl_take ppcg_ht_phase *phase)
+{
+ if (!phase)
+ return NULL;
+
+ if (!phase->tiling || !phase->local_time ||
+ !phase->shift_space || !phase->domain)
+ return ppcg_ht_phase_free(phase);
+
+ return phase;
+}
+
+/* Construct a ppcg_ht_phase object, that simply copies
+ * information from "tiling".
+ * That is, the result is defined over the "ts" space and
+ * corresponds to phase 1.
+ */
+static __isl_give ppcg_ht_phase *construct_phase(
+ __isl_keep ppcg_ht_tiling *tiling)
+{
+ isl_ctx *ctx;
+ ppcg_ht_phase *phase;
+
+ if (!tiling)
+ return NULL;
+
+ ctx = ppcg_ht_tiling_get_ctx(tiling);
+ phase = isl_calloc_type(ctx, struct ppcg_ht_phase);
+ if (!phase)
+ return NULL;
+ phase->tiling = ppcg_ht_tiling_copy(tiling);
+ phase->time_tile = isl_aff_copy(tiling->time_tile);
+ phase->local_time = isl_aff_copy(tiling->local_time);
+ phase->shift_space = isl_aff_copy(tiling->shift_space);
+ phase->domain = isl_set_copy(tiling->hex);
+
+ return check_phase(phase);
+}
+
+/* Align the parameters of the elements of "phase" to those of "space".
+ */
+static __isl_give ppcg_ht_phase *phase_align_params(
+ __isl_take ppcg_ht_phase *phase, __isl_take isl_space *space)
+{
+ if (!phase)
+ goto error;
+
+ phase->time_tile = isl_aff_align_params(phase->time_tile,
+ isl_space_copy(space));
+ phase->local_time = isl_aff_align_params(phase->local_time,
+ isl_space_copy(space));
+ phase->shift_space = isl_aff_align_params(phase->shift_space,
+ isl_space_copy(space));
+ phase->domain = isl_set_align_params(phase->domain, space);
+
+ return check_phase(phase);
+error:
+ isl_space_free(space);
+ return NULL;
+}
+
+/* Pull back "phase" over "ma".
+ * That is, take a phase defined over the range of "ma" and
+ * turn it into a phase defined over the domain of "ma".
+ */
+static __isl_give ppcg_ht_phase *pullback_phase(__isl_take ppcg_ht_phase *phase,
+ __isl_take isl_multi_aff *ma)
+{
+ phase = phase_align_params(phase, isl_multi_aff_get_space(ma));
+ if (!phase)
+ goto error;
+
+ phase->time_tile = isl_aff_pullback_multi_aff(phase->time_tile,
+ isl_multi_aff_copy(ma));
+ phase->local_time = isl_aff_pullback_multi_aff(phase->local_time,
+ isl_multi_aff_copy(ma));
+ phase->shift_space = isl_aff_pullback_multi_aff(phase->shift_space,
+ isl_multi_aff_copy(ma));
+ phase->domain = isl_set_preimage_multi_aff(phase->domain, ma);
+
+ return check_phase(phase);
+error:
+ isl_multi_aff_free(ma);
+ return NULL;
+}
+
+/* Pullback "phase" over phase->tiling->shift_phase, which shifts
+ * phase 0 to phase 1. The pullback therefore takes a phase 1
+ * description and turns it into a phase 0 description.
+ */
+static __isl_give ppcg_ht_phase *shift_phase(__isl_take ppcg_ht_phase *phase)
+{
+ ppcg_ht_tiling *tiling;
+
+ if (!phase)
+ return NULL;
+
+ tiling = phase->tiling;
+ return pullback_phase(phase, isl_multi_aff_copy(tiling->shift_phase));
+}
+
+/* Take a "phase" defined over the ts-space and plug in the projection
+ * from the input schedule space to the ts-space.
+ * The result is then defined over this input schedule space.
+ */
+static __isl_give ppcg_ht_phase *lift_phase(__isl_take ppcg_ht_phase *phase)
+{
+ ppcg_ht_tiling *tiling;
+
+ if (!phase)
+ return NULL;
+
+ tiling = phase->tiling;
+ return pullback_phase(phase, isl_multi_aff_copy(tiling->project_ts));
+}
+
+/* Compute the shift that should be added to the space band
+ * in order to be able to apply rectangular tiling to the space.
+ * Store the shift in phase->space_shift.
+ *
+ * In the first dimension, it is equal to shift_space - s.
+ * For phase 1, this results in
+ *
+ * (-(2 * shift_s)*T) % W
+ *
+ * In phase 0, the "s" in shift_space has been replaced by "s + shift_s",
+ * so the result is
+ *
+ * shift_s + (-(2 * shift_s)*T) % W
+ *
+ * In the other dimensions, the shift is equal to
+ *
+ * dl_i * local_time.
+ */
+static __isl_give ppcg_ht_phase *compute_space_shift(
+ __isl_take ppcg_ht_phase *phase)
+{
+ int i, n;
+ isl_space *space;
+ isl_local_space *ls;
+ isl_aff *aff, *s;
+ isl_multi_aff *space_shift;
+
+ if (!phase)
+ return NULL;
+
+ space = ppcg_ht_phase_get_input_space(phase);
+ space = isl_space_unwrap(space);
+ space = isl_space_range_map(space);
+
+ space_shift = isl_multi_aff_zero(space);
+ aff = isl_aff_copy(phase->shift_space);
+ ls = isl_local_space_from_space(isl_aff_get_domain_space(aff));
+ s = isl_aff_var_on_domain(ls, isl_dim_set, 1);
+ aff = isl_aff_sub(aff, s);
+ space_shift = isl_multi_aff_set_aff(space_shift, 0, aff);
+
+ n = isl_multi_aff_dim(space_shift, isl_dim_out);
+ for (i = 1; i < n; ++i) {
+ isl_val *v;
+ isl_aff *time;
+
+ v = ppcg_ht_bounds_get_lower(phase->tiling->bounds, i);
+ time = isl_aff_copy(phase->local_time);
+ time = isl_aff_scale_val(time, v);
+ space_shift = isl_multi_aff_set_aff(space_shift, i, time);
+ }
+
+ if (!space_shift)
+ return ppcg_ht_phase_free(phase);
+ phase->space_shift = space_shift;
+ return phase;
+}
+
+/* Compute the space tiling and store the result in phase->space_tile.
+ * The space tiling is of the form
+ *
+ * [P[t] -> C[s]] -> C[floor((s + space_shift)/space_size]
+ */
+static __isl_give ppcg_ht_phase *compute_space_tile(
+ __isl_take ppcg_ht_phase *phase)
+{
+ isl_space *space;
+ isl_multi_val *space_sizes;
+ isl_multi_aff *space_shift;
+ isl_multi_aff *tile;
+
+ if (!phase)
+ return NULL;
+
+ space = ppcg_ht_phase_get_input_space(phase);
+ space = isl_space_unwrap(space);
+ tile = isl_multi_aff_range_map(space);
+ space_shift = isl_multi_aff_copy(phase->space_shift);
+ tile = isl_multi_aff_add(space_shift, tile);
+ space_sizes = isl_multi_val_copy(phase->tiling->space_sizes);
+ tile = isl_multi_aff_scale_down_multi_val(tile, space_sizes);
+ tile = isl_multi_aff_floor(tile);
+
+ if (!tile)
+ return ppcg_ht_phase_free(phase);
+ phase->space_tile = tile;
+ return phase;
+}
+
+/* Construct a representation for one of the two phase for hybrid tiling
+ * "tiling". If "shift" is not set, then the phase is constructed
+ * directly from the hexagonal tile shape in "tiling", which represents
+ * the phase-1 tiles. If "shift" is set, then this tile shape is shifted
+ * back over tiling->shift_phase to obtain the phase-0 tiles.
+ *
+ * First copy data from "tiling", then optionally shift the phase and
+ * finally move the tiling from the "ts" space of "tiling" to
+ * the space of the input pattern.
+ *
+ * After the basic phase has been computed, also compute
+ * the corresponding space shift.
+ */
+static __isl_give ppcg_ht_phase *ppcg_ht_tiling_compute_phase(
+ __isl_keep ppcg_ht_tiling *tiling, int shift)
+{
+ ppcg_ht_phase *phase;
+
+ phase = construct_phase(tiling);
+ if (shift)
+ phase = shift_phase(phase);
+ phase = lift_phase(phase);
+
+ phase = compute_space_shift(phase);
+ phase = compute_space_tile(phase);
+
+ return phase;
+}
+
+/* Consruct a function that is equal to the time tile of "phase0"
+ * on the domain of "phase0" and equal to the time tile of "phase1"
+ * on the domain of "phase1".
+ * The two domains are assumed to form a partition of the input
+ * schedule space.
+ */
+static __isl_give isl_pw_multi_aff *combine_time_tile(
+ __isl_keep ppcg_ht_phase *phase0, __isl_keep ppcg_ht_phase *phase1)
+{
+ isl_aff *T;
+ isl_pw_aff *time, *time1;
+
+ if (!phase0 || !phase1)
+ return NULL;
+
+ T = isl_aff_copy(phase0->time_tile);
+ time = isl_pw_aff_alloc(ppcg_ht_phase_get_domain(phase0), T);
+
+ T = isl_aff_copy(phase1->time_tile);
+ time1 = isl_pw_aff_alloc(ppcg_ht_phase_get_domain(phase1), T);
+
+ time = isl_pw_aff_union_add(time, time1);
+
+ return isl_pw_multi_aff_from_pw_aff(time);
+}
+
+/* Name used in mark nodes that contain a pointer to a ppcg_ht_phase.
+ */
+static char *ppcg_phase_name = "phase";
+
+/* Does "id" contain a pointer to a ppcg_ht_phase?
+ * That is, is it called "phase"?
+ */
+static isl_bool is_phase_id(__isl_keep isl_id *id)
+{
+ const char *name;
+
+ name = isl_id_get_name(id);
+ if (!name)
+ return isl_bool_error;
+
+ return !strcmp(name, ppcg_phase_name);
+}
+
+/* Given a mark node with an identifier that points to a ppcg_ht_phase,
+ * extract this ppcg_ht_phase pointer.
+ */
+__isl_keep ppcg_ht_phase *ppcg_ht_phase_extract_from_mark(
+ __isl_keep isl_schedule_node *node)
+{
+ isl_bool is_phase;
+ isl_id *id;
+ void *p;
+
+ if (!node)
+ return NULL;
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_mark)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_internal,
+ "not a phase mark", return NULL);
+
+ id = isl_schedule_node_mark_get_id(node);
+ is_phase = is_phase_id(id);
+ p = isl_id_get_user(id);
+ isl_id_free(id);
+
+ if (is_phase < 0)
+ return NULL;
+ if (!is_phase)
+ isl_die(isl_schedule_node_get_ctx(node), isl_error_internal,
+ "not a phase mark", return NULL);
+
+ return p;
+}
+
+/* Insert a mark node at "node" holding a pointer to "phase".
+ */
+static __isl_give isl_schedule_node *insert_phase(
+ __isl_take isl_schedule_node *node, __isl_take ppcg_ht_phase *phase)
+{
+ isl_ctx *ctx;
+ isl_id *id;
+
+ if (!node)
+ goto error;
+ ctx = isl_schedule_node_get_ctx(node);
+ id = isl_id_alloc(ctx, ppcg_phase_name, phase);
+ if (!id)
+ goto error;
+ id = isl_id_set_free_user(id, &ppcg_ht_phase_free_wrap);
+ node = isl_schedule_node_insert_mark(node, id);
+
+ return node;
+error:
+ ppcg_ht_phase_free(phase);
+ isl_schedule_node_free(node);
+ return NULL;
+}
+
+/* Construct a mapping from the elements of the original pair of bands
+ * to which tiling was applied that belong to a tile of "phase"
+ * to that tile, preserving the values for the outer bands.
+ *
+ * The mapping is of the form
+ *
+ * [[outer] -> [P -> C]] -> [[outer] -> [tile]]
+ *
+ * where tile is defined by a concatenation of the time_tile and
+ * the space_tile.
+ */
+static __isl_give isl_map *construct_tile_map(__isl_keep ppcg_ht_phase *phase)
+{
+ int depth;
+ isl_space *space;
+ isl_multi_aff *ma;
+ isl_multi_aff *tiling;
+ isl_map *el2tile;
+
+ depth = isl_schedule_node_get_schedule_depth(
+ phase->tiling->input_node);
+ space = isl_aff_get_space(phase->time_tile);
+ space = isl_space_params(space);
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, depth);
+ space = isl_space_map_from_set(space);
+ ma = isl_multi_aff_identity(space);
+
+ tiling = isl_multi_aff_flat_range_product(
+ isl_multi_aff_from_aff(isl_aff_copy(phase->time_tile)),
+ isl_multi_aff_copy(phase->space_tile));
+ el2tile = isl_map_from_multi_aff(tiling);
+ el2tile = isl_map_intersect_domain(el2tile,
+ isl_set_copy(phase->domain));
+ el2tile = isl_map_product(isl_map_from_multi_aff(ma), el2tile);
+
+ return el2tile;
+}
+
+/* Return a description of the full tiles of "phase" at the point
+ * in the original schedule tree where the tiling was applied.
+ *
+ * First construct a mapping from the input schedule dimensions
+ * up to an including the original pair of bands to which hybrid tiling
+ * was applied to schedule dimensions in which this original pair
+ * has been replaced by the tiles.
+ * This mapping is of the form
+ *
+ * [[outer] -> [P -> C]] -> [[outer] -> [tile]]
+ *
+ * Apply this mapping to the set of all values for the input
+ * schedule dimensions and then apply its inverse.
+ * The result is the set of values for the input schedule dimensions
+ * that would map to any of the tiles. Subtracting from this set
+ * the set of values that are actually executed produces the set
+ * of values that belong to a tile but that are not executed.
+ * Mapping these back to the tiles produces a description of
+ * the partial tiles. Subtracting these from the set of all tiles
+ * produces a description of the full tiles in the form
+ *
+ * [[outer] -> [tile]]
+ */
+static __isl_give isl_set *compute_full_tile(__isl_keep ppcg_ht_phase *phase)
+{
+ isl_schedule_node *node;
+ isl_union_set *domain;
+ isl_union_map *prefix, *schedule;
+ isl_set *all, *partial, *all_el;
+ isl_map *tile2el, *el2tile;
+ isl_multi_union_pw_aff *mupa;
+
+ el2tile = construct_tile_map(phase);
+ tile2el = isl_map_reverse(isl_map_copy(el2tile));
+
+ node = phase->tiling->input_node;
+ prefix = isl_schedule_node_get_prefix_schedule_union_map(node);
+ domain = isl_schedule_node_get_domain(node);
+ mupa = isl_multi_union_pw_aff_copy(phase->tiling->input_schedule);
+ schedule = isl_union_map_from_multi_union_pw_aff(mupa);
+ schedule = isl_union_map_range_product(prefix, schedule);
+ all_el = isl_set_from_union_set(isl_union_set_apply(domain, schedule));
+ all_el = isl_set_coalesce(all_el);
+
+ all = isl_set_apply(isl_set_copy(all_el), isl_map_copy(el2tile));
+
+ partial = isl_set_copy(all);
+ partial = isl_set_apply(partial, tile2el);
+ partial = isl_set_subtract(partial, all_el);
+ partial = isl_set_apply(partial, el2tile);
+
+ return isl_set_subtract(all, partial);
+}
+
+/* Copy the AST loop types of the non-isolated part to those
+ * of the isolated part.
+ */
+static __isl_give isl_schedule_node *set_isolate_loop_type(
+ __isl_take isl_schedule_node *node)
+{
+ int i, n;
+
+ n = isl_schedule_node_band_n_member(node);
+ for (i = 0; i < n; ++i) {
+ enum isl_ast_loop_type type;
+
+ type = isl_schedule_node_band_member_get_ast_loop_type(node, i);
+ node = isl_schedule_node_band_member_set_isolate_ast_loop_type(
+ node, i, type);
+ }
+
+ return node;
+}
+
+/* If options->isolate_full_tiles is set, then mark the full tiles
+ * in "node" for isolation. The full tiles are derived from "phase".
+ * "node" may point to a part of the tiling, e.g., the space tiling.
+ *
+ * The full tiles are originally computed in the form
+ *
+ * [[outer] -> [tile]]
+ *
+ * However, the band that "node" points to may only contain
+ * subset of the tile dimensions.
+ * The description above is therefore treated as
+ *
+ * [[outer] -> [before; this; after]]
+ *
+ * before is of size "pos"; this is of size "dim"; and
+ * after is of size "out - pos - dim".
+ * The after part is first project out. Then the range is split
+ * into a before and this part and finally the before part is moved
+ * to the domain, resulting in
+ *
+ * [[outer; before] -> [this]]
+ *
+ * This description is then used as the isolate option.
+ *
+ * The AST loop type for the isolated part is set to be the same
+ * as that of the non-isolated part.
+ */
+static __isl_give isl_schedule_node *ppcg_ht_phase_isolate_full_tile_node(
+ __isl_keep ppcg_ht_phase *phase, __isl_take isl_schedule_node *node,
+ struct ppcg_options *options)
+{
+ int in, out, pos, depth, dim;
+ isl_space *space;
+ isl_multi_aff *ma1, *ma2;
+ isl_set *tile;
+ isl_map *map;
+ isl_set *set;
+ isl_union_set *opt;
+
+ if (!options->isolate_full_tiles)
+ return node;
+
+ depth = isl_schedule_node_get_schedule_depth(node);
+ dim = isl_schedule_node_band_n_member(node);
+
+ tile = compute_full_tile(phase);
+ map = isl_set_unwrap(tile);
+ in = isl_map_dim(map, isl_dim_in);
+ out = isl_map_dim(map, isl_dim_out);
+ pos = depth - in;
+ map = isl_map_project_out(map, isl_dim_out, pos + dim,
+ out - (pos + dim));
+ space = isl_space_range(isl_map_get_space(map));
+ ma1 = isl_multi_aff_project_out_map(isl_space_copy(space),
+ isl_dim_set, pos, dim);
+ ma2 = isl_multi_aff_project_out_map(space, isl_dim_set, 0, pos);
+ ma1 = isl_multi_aff_range_product(ma1, ma2);
+ map = isl_map_apply_range(map, isl_map_from_multi_aff(ma1));
+ map = isl_map_uncurry(map);
+ map = isl_map_flatten_domain(map);
+ set = isl_map_wrap(map);
+ set = isl_set_set_tuple_name(set, "isolate");
+
+ opt = isl_schedule_node_band_get_ast_build_options(node);
+ opt = isl_union_set_add_set(opt, set);
+ node = isl_schedule_node_band_set_ast_build_options(node, opt);
+ node = set_isolate_loop_type(node);
+
+ return node;
+}
+
+/* Insert a band node for performing the space tiling for "phase" at "node".
+ * In particular, insert a band node with partial schedule
+ *
+ * [P[t] -> C[s]] -> C[floor((s + space_shift)/space_size)]
+ *
+ * pulled back over the input schedule.
+ * "options" determines whether full tiles should be separated
+ * from partial tiles.
+ *
+ * The first tile dimension iterates over the hexagons in the same
+ * phase, which are independent by construction. The first dimension
+ * is therefore marked coincident.
+ * All dimensions are also marked for being generated as atomic loops
+ * because separation is usually not desirable on tile loops.
+ */
+static __isl_give isl_schedule_node *insert_space_tiling(
+ __isl_keep ppcg_ht_phase *phase, __isl_take isl_schedule_node *node,
+ struct ppcg_options *options)
+{
+ isl_multi_aff *space_tile;
+ isl_multi_union_pw_aff *mupa;
+
+ if (!phase)
+ return isl_schedule_node_free(node);
+
+ space_tile = isl_multi_aff_copy(phase->space_tile);
+ mupa = isl_multi_union_pw_aff_copy(phase->tiling->input_schedule);
+ mupa = isl_multi_union_pw_aff_apply_multi_aff(mupa, space_tile);
+ node = isl_schedule_node_insert_partial_schedule(node, mupa);
+ node = ppcg_set_schedule_node_type(node, isl_ast_loop_atomic);
+ node = ppcg_ht_phase_isolate_full_tile_node(phase, node, options);
+ node = isl_schedule_node_band_member_set_coincident(node, 0, 1);
+
+ return node;
+}
+
+/* Given a pointer "node" to (a copy of) the original child node
+ * in the input pattern, adjust its partial schedule such that
+ * it starts at zero within each tile.
+ *
+ * That is, replace "s" by (s + space_shift) % space_sizes.
+ */
+__isl_give isl_schedule_node *ppcg_ht_phase_shift_space_point(
+ __isl_keep ppcg_ht_phase *phase, __isl_take isl_schedule_node *node)
+{
+ isl_multi_val *space_sizes;
+ isl_multi_aff *space_shift;
+ isl_multi_union_pw_aff *mupa;
+
+ space_shift = isl_multi_aff_copy(phase->space_shift);
+ mupa = isl_multi_union_pw_aff_copy(phase->tiling->input_schedule);
+ mupa = isl_multi_union_pw_aff_apply_multi_aff(mupa, space_shift);
+ node = isl_schedule_node_band_shift(node, mupa);
+ space_sizes = isl_multi_val_copy(phase->tiling->space_sizes);
+ node = isl_schedule_node_band_mod(node, space_sizes);
+
+ return node;
+}
+
+/* Does
+ *
+ * s0 > delta + 2 * {delta * h} - 1
+ *
+ * hold?
+ */
+static isl_bool wide_enough(__isl_keep isl_val *s0, __isl_keep isl_val *delta,
+ __isl_keep isl_val *h)
+{
+ isl_val *v, *v2;
+ isl_bool ok;
+
+ v = isl_val_mul(isl_val_copy(delta), isl_val_copy(h));
+ v2 = isl_val_floor(isl_val_copy(v));
+ v = isl_val_sub(v, v2);
+ v = isl_val_mul_ui(v, 2);
+ v = isl_val_add(v, isl_val_copy(delta));
+ v = isl_val_sub_ui(v, 1);
+ ok = isl_val_gt(s0, v);
+ isl_val_free(v);
+
+ return ok;
+}
+
+/* Is the tile size specified by "sizes" wide enough in the first space
+ * dimension, i.e., the base of the hexagon? This ensures that,
+ * after hybrid tiling using "bounds" and these sizes,
+ * neighboring hexagons in the same phase are far enough apart
+ * that they do not depend on each other.
+ * The test is only meaningful if the bounds are valid.
+ *
+ * Let st be (half) the size in the time dimension and s0 the base
+ * size in the first space dimension. Let delta be the dependence
+ * distance in either positive or negative direction. In principle,
+ * it should be enough to have s0 + 1 > delta, i.e., s0 >= delta.
+ * However, in case of fractional delta, the tile is not extended
+ * with delta * (st - 1), but instead with floor(delta * (st - 1)).
+ * The condition therefore needs to be adjusted to
+ *
+ * s0 + 1 > delta + 2 {delta * (st - 1)}
+ *
+ * (with {} the fractional part) to account for the two slanted sides.
+ * The condition in the paper "Hybrid Hexagonal/Classical Tiling for GPUs"
+ * translates to
+ *
+ * s0 >= delta + {delta * (st - 1)}
+ *
+ * Since 1 > frac(delta * (st - 1)), this condition implies
+ * the condition above.
+ *
+ * The condition is checked for both directions.
+ */
+isl_bool ppcg_ht_bounds_supports_sizes(__isl_keep ppcg_ht_bounds *bounds,
+ __isl_keep isl_multi_val *sizes)
+{
+ isl_val *s0, *h;
+ isl_val *delta;
+ isl_bool ok;
+
+ ok = ppcg_ht_bounds_is_valid(bounds);
+ if (ok < 0 || !ok)
+ return ok;
+
+ h = isl_val_sub_ui(isl_multi_val_get_val(sizes, 0), 1);
+ s0 = isl_multi_val_get_val(sizes, 1);
+
+ delta = ppcg_ht_bounds_get_lower(bounds, 0);
+ ok = wide_enough(s0, delta, h);
+ isl_val_free(delta);
+
+ delta = ppcg_ht_bounds_get_upper(bounds);
+ if (ok == isl_bool_true)
+ ok = wide_enough(s0, delta, h);
+ isl_val_free(delta);
+
+ isl_val_free(s0);
+ isl_val_free(h);
+
+ return ok;
+}
+
+/* Check that the tile will be wide enough in the first space
+ * dimension, i.e., the base of the hexagon. This ensures that
+ * neighboring hexagons in the same phase are far enough apart
+ * that they do not depend on each other.
+ *
+ * Error out if the condition fails to hold.
+ */
+static isl_stat check_width(__isl_keep ppcg_ht_bounds *bounds,
+ __isl_keep isl_multi_val *sizes)
+{
+ isl_bool ok;
+
+ ok = ppcg_ht_bounds_supports_sizes(bounds, sizes);
+
+ if (ok < 0)
+ return isl_stat_error;
+ if (!ok)
+ isl_die(isl_multi_val_get_ctx(sizes), isl_error_invalid,
+ "base of hybrid tiling hexagon not sufficiently wide",
+ return isl_stat_error);
+
+ return isl_stat_ok;
+}
+
+/* Given valid bounds on the relative dependence distances for
+ * the pair of nested nodes that "node" point to, as well as sufficiently
+ * wide tile sizes "sizes", insert the corresponding time and space tiling
+ * at "node", along with a pair of phase nodes that can be used
+ * to make further changes.
+ * The space of "sizes" should be the product of the spaces
+ * of the schedules of the pair of parent and child nodes.
+ * "options" determines whether full tiles should be separated
+ * from partial tiles.
+ *
+ * In particular, given an input of the form
+ *
+ * P - C - ...
+ *
+ * the output has the form
+ *
+ * /- F0 - M0 - CT0 - P - C - ...
+ * PT - seq
+ * \- F1 - M1 - CT1 - P - C - ...
+ *
+ * PT is the global time tiling. Within each of these tiles,
+ * two phases are executed in order. Within each phase, the schedule
+ * space is further subdivided into tiles through CT0 and CT1.
+ * The first dimension of each of these iterates over the hexagons
+ * within a phase and these are independent by construction.
+ * The F0 and F1 filters filter the statement instances that belong
+ * to the corresponding phase. The M0 and M1 marks contain a pointer
+ * to a ppcg_ht_phase object that can be used to perform further changes.
+ *
+ * After checking that input satisfies the requirements,
+ * a data structure is constructed that represents the tiling and
+ * two additional data structures are constructed for the two phases
+ * of the tiling. These are then used to define the filters F0 and F1 and
+ * combined to construct the time tiling PT.
+ * Then the time tiling node PT is inserted, followed by
+ * the sequence with the two filters, the CT space tiling nodes and
+ * the phase markers M.
+ */
+__isl_give isl_schedule_node *ppcg_ht_bounds_insert_tiling(
+ __isl_take ppcg_ht_bounds *bounds, __isl_take isl_multi_val *sizes,
+ __isl_take isl_schedule_node *node, struct ppcg_options *options)
+{
+ isl_ctx *ctx;
+ isl_union_set *phase0;
+ isl_union_set *phase1;
+ isl_multi_union_pw_aff *input, *dom_time;
+ isl_union_pw_multi_aff *upma;
+ isl_pw_multi_aff *time;
+ isl_union_set_list *phases;
+ ppcg_ht_tiling *tiling;
+ ppcg_ht_phase *phase_0;
+ ppcg_ht_phase *phase_1;
+
+ if (!node || !sizes || !bounds)
+ goto error;
+ if (check_input_pattern(node) < 0 || check_width(bounds, sizes) < 0)
+ goto error;
+
+ ctx = isl_schedule_node_get_ctx(node);
+
+ input = extract_input_schedule(node);
+
+ tiling = ppcg_ht_bounds_construct_tiling(bounds, node, input, sizes);
+ phase_0 = ppcg_ht_tiling_compute_phase(tiling, 1);
+ phase_1 = ppcg_ht_tiling_compute_phase(tiling, 0);
+ time = combine_time_tile(phase_0, phase_1);
+ ppcg_ht_tiling_free(tiling);
+
+ upma = isl_union_pw_multi_aff_from_multi_union_pw_aff(
+ isl_multi_union_pw_aff_copy(input));
+ phase0 = isl_union_set_from_set(ppcg_ht_phase_get_domain(phase_0));
+ phase0 = isl_union_set_preimage_union_pw_multi_aff(phase0,
+ isl_union_pw_multi_aff_copy(upma));
+ phase1 = isl_union_set_from_set(ppcg_ht_phase_get_domain(phase_1));
+ phase1 = isl_union_set_preimage_union_pw_multi_aff(phase1, upma);
+
+ phases = isl_union_set_list_alloc(ctx, 2);
+ phases = isl_union_set_list_add(phases, phase0);
+ phases = isl_union_set_list_add(phases, phase1);
+
+ dom_time = isl_multi_union_pw_aff_apply_pw_multi_aff(input, time);
+ node = isl_schedule_node_insert_partial_schedule(node, dom_time);
+
+ node = isl_schedule_node_child(node, 0);
+
+ node = isl_schedule_node_insert_sequence(node, phases);
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_child(node, 0);
+ node = insert_space_tiling(phase_0, node, options);
+ node = insert_phase(node, phase_0);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_next_sibling(node);
+ node = isl_schedule_node_child(node, 0);
+ node = insert_space_tiling(phase_1, node, options);
+ node = insert_phase(node, phase_1);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_parent(node);
+
+ node = isl_schedule_node_parent(node);
+
+ isl_multi_val_free(sizes);
+ return node;
+error:
+ isl_multi_val_free(sizes);
+ isl_schedule_node_free(node);
+ ppcg_ht_bounds_free(bounds);
+ return NULL;
+}
+
+/* Given a branch "node" that contains a sequence node with two phases
+ * of hybrid tiling as input, call "fn" on each of the two phase marker
+ * nodes.
+ *
+ * That is, the input is as follows
+ *
+ * /- F0 - M0 - ...
+ * ... - seq
+ * \- F1 - M1 - ...
+ *
+ * and "fn" is called on M0 and on M1.
+ */
+__isl_give isl_schedule_node *hybrid_tile_foreach_phase(
+ __isl_take isl_schedule_node *node,
+ __isl_give isl_schedule_node *(*fn)(__isl_take isl_schedule_node *node,
+ void *user), void *user)
+{
+ int depth0, depth;
+
+ depth0 = isl_schedule_node_get_tree_depth(node);
+
+ while (node &&
+ isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
+ node = isl_schedule_node_child(node, 0);
+
+ node = isl_schedule_node_child(node, 0);
+ node = isl_schedule_node_child(node, 0);
+ if (!node)
+ return NULL;
+ node = fn(node, user);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_next_sibling(node);
+ node = isl_schedule_node_child(node, 0);
+ if (!node)
+ return NULL;
+ node = fn(node, user);
+ node = isl_schedule_node_parent(node);
+ node = isl_schedule_node_parent(node);
+
+ depth = isl_schedule_node_get_tree_depth(node);
+ node = isl_schedule_node_ancestor(node, depth - depth0);
+
+ return node;
+}
+
+/* This function is called on each of the two phase marks
+ * in a hybrid tiling tree.
+ * Drop the phase mark at "node".
+ */
+static __isl_give isl_schedule_node *drop_phase_mark(
+ __isl_take isl_schedule_node *node, void *user)
+{
+ isl_id *id;
+ isl_bool is_phase;
+
+ if (isl_schedule_node_get_type(node) != isl_schedule_node_mark)
+ return node;
+
+ id = isl_schedule_node_mark_get_id(node);
+ is_phase = is_phase_id(id);
+ isl_id_free(id);
+
+ if (is_phase < 0)
+ return isl_schedule_node_free(node);
+ if (is_phase)
+ node = isl_schedule_node_delete(node);
+
+ return node;
+}
+
+/* Given a branch "node" that contains a sequence node with two phases
+ * of hybrid tiling as input, remove the two phase marker nodes.
+ *
+ * That is, the input is as follows
+ *
+ * /- F0 - M0 - ...
+ * ... - seq
+ * \- F1 - M1 - ...
+ *
+ * and the output is
+ *
+ * /- F0 - ...
+ * ... - seq
+ * \- F1 - ...
+ */
+__isl_give isl_schedule_node *hybrid_tile_drop_phase_marks(
+ __isl_take isl_schedule_node *node)
+{
+ return hybrid_tile_foreach_phase(node, &drop_phase_mark, NULL);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/hybrid.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/hybrid.h
new file mode 100644
index 00000000000..71136c6779e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/hybrid.h
@@ -0,0 +1,41 @@
+#ifndef HYBRID_H
+#define HYBRID_H
+
+#include <isl/val.h>
+#include <isl/schedule_node.h>
+
+#include "ppcg.h"
+
+struct ppcg_ht_bounds;
+typedef struct ppcg_ht_bounds ppcg_ht_bounds;
+
+struct ppcg_ht_phase;
+typedef struct ppcg_ht_phase ppcg_ht_phase;
+
+isl_bool ppcg_ht_has_input_pattern(__isl_keep isl_schedule_node *node);
+isl_bool ppcg_ht_parent_has_input_pattern(__isl_keep isl_schedule_node *node);
+
+__isl_give ppcg_ht_bounds *ppcg_ht_compute_bounds(struct ppcg_scop *scop,
+ __isl_keep isl_schedule_node *node);
+void ppcg_ht_bounds_dump(__isl_keep ppcg_ht_bounds *bounds);
+isl_bool ppcg_ht_bounds_is_valid(__isl_keep ppcg_ht_bounds *bounds);
+isl_bool ppcg_ht_bounds_supports_sizes(__isl_keep ppcg_ht_bounds *bounds,
+ __isl_keep isl_multi_val *sizes);
+__isl_give isl_schedule_node *ppcg_ht_bounds_insert_tiling(
+ __isl_take ppcg_ht_bounds *bounds, __isl_take isl_multi_val *sizes,
+ __isl_take isl_schedule_node *node, struct ppcg_options *options);
+__isl_null ppcg_ht_bounds *ppcg_ht_bounds_free(
+ __isl_take ppcg_ht_bounds *bounds);
+
+__isl_keep ppcg_ht_phase *ppcg_ht_phase_extract_from_mark(
+ __isl_keep isl_schedule_node *node);
+__isl_give isl_schedule_node *ppcg_ht_phase_shift_space_point(
+ __isl_keep ppcg_ht_phase *phase, __isl_take isl_schedule_node *node);
+__isl_give isl_schedule_node *hybrid_tile_foreach_phase(
+ __isl_take isl_schedule_node *node,
+ __isl_give isl_schedule_node *(*fn)(__isl_take isl_schedule_node *node,
+ void *user), void *user);
+__isl_give isl_schedule_node *hybrid_tile_drop_phase_marks(
+ __isl_take isl_schedule_node *node);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/opencl.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/opencl.h
new file mode 100644
index 00000000000..bf10f95cbf6
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/opencl.h
@@ -0,0 +1,11 @@
+#ifndef _OPENCL_H
+#define _OPENCL_H
+
+#include <pet.h>
+#include "ppcg_options.h"
+#include "ppcg.h"
+
+int generate_opencl(isl_ctx *ctx, struct ppcg_options *options,
+ const char *input, const char *output);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg.c
new file mode 100644
index 00000000000..ed5aceca46a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg.c
@@ -0,0 +1,1067 @@
+/*
+ * Copyright 2011 INRIA Saclay
+ * Copyright 2013 Ecole Normale Superieure
+ * Copyright 2015 Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <isl/ctx.h>
+#include <isl/id.h>
+#include <isl/val.h>
+#include <isl/set.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl/aff.h>
+#include <isl/flow.h>
+#include <isl/options.h>
+#include <isl/schedule.h>
+#include <isl/ast.h>
+#include <isl/id_to_ast_expr.h>
+#include <isl/ast_build.h>
+#include <isl/schedule.h>
+#include <pet.h>
+#include "ppcg.h"
+#include "ppcg_options.h"
+#include "cuda.h"
+#include "opencl.h"
+#include "cpu.h"
+
+struct options {
+ struct pet_options *pet;
+ struct ppcg_options *ppcg;
+ char *input;
+ char *output;
+};
+
+const char *ppcg_version(void);
+static void print_version(void)
+{
+ printf("%s", ppcg_version());
+}
+
+ISL_ARGS_START(struct options, options_args)
+ISL_ARG_CHILD(struct options, pet, "pet", &pet_options_args, "pet options")
+ISL_ARG_CHILD(struct options, ppcg, NULL, &ppcg_options_args, "ppcg options")
+ISL_ARG_STR(struct options, output, 'o', NULL,
+ "filename", NULL, "output filename (c and opencl targets)")
+ISL_ARG_ARG(struct options, input, "input", NULL)
+ISL_ARG_VERSION(print_version)
+ISL_ARGS_END
+
+ISL_ARG_DEF(options, struct options, options_args)
+
+/* Return a pointer to the final path component of "filename" or
+ * to "filename" itself if it does not contain any components.
+ */
+const char *ppcg_base_name(const char *filename)
+{
+ const char *base;
+
+ base = strrchr(filename, '/');
+ if (base)
+ return ++base;
+ else
+ return filename;
+}
+
+/* Copy the base name of "input" to "name" and return its length.
+ * "name" is not NULL terminated.
+ *
+ * In particular, remove all leading directory components and
+ * the final extension, if any.
+ */
+int ppcg_extract_base_name(char *name, const char *input)
+{
+ const char *base;
+ const char *ext;
+ int len;
+
+ base = ppcg_base_name(input);
+ ext = strrchr(base, '.');
+ len = ext ? ext - base : strlen(base);
+
+ memcpy(name, base, len);
+
+ return len;
+}
+
+/* Does "scop" refer to any arrays that are declared, but not
+ * exposed to the code after the scop?
+ */
+int ppcg_scop_any_hidden_declarations(struct ppcg_scop *scop)
+{
+ int i;
+
+ if (!scop)
+ return 0;
+
+ // This is a pet feature not available in Polly.
+ return 0;
+
+ for (i = 0; i < scop->pet->n_array; ++i)
+ if (scop->pet->arrays[i]->declared &&
+ !scop->pet->arrays[i]->exposed)
+ return 1;
+
+ return 0;
+}
+
+/* Collect all variable names that are in use in "scop".
+ * In particular, collect all parameters in the context and
+ * all the array names.
+ * Store these names in an isl_id_to_ast_expr by mapping
+ * them to a dummy value (0).
+ */
+static __isl_give isl_id_to_ast_expr *collect_names(struct pet_scop *scop)
+{
+ int i, n;
+ isl_ctx *ctx;
+ isl_ast_expr *zero;
+ isl_id_to_ast_expr *names;
+
+ ctx = isl_set_get_ctx(scop->context);
+
+ n = isl_set_dim(scop->context, isl_dim_param);
+
+ names = isl_id_to_ast_expr_alloc(ctx, n + scop->n_array);
+ zero = isl_ast_expr_from_val(isl_val_zero(ctx));
+
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+
+ id = isl_set_get_dim_id(scop->context, isl_dim_param, i);
+ names = isl_id_to_ast_expr_set(names,
+ id, isl_ast_expr_copy(zero));
+ }
+
+ for (i = 0; i < scop->n_array; ++i) {
+ struct pet_array *array = scop->arrays[i];
+ isl_id *id;
+
+ id = isl_set_get_tuple_id(array->extent);
+ names = isl_id_to_ast_expr_set(names,
+ id, isl_ast_expr_copy(zero));
+ }
+
+ isl_ast_expr_free(zero);
+
+ return names;
+}
+
+/* Return an isl_id called "prefix%d", with "%d" set to "i".
+ * If an isl_id with such a name already appears among the variable names
+ * of "scop", then adjust the name to "prefix%d_%d".
+ */
+static __isl_give isl_id *generate_name(struct ppcg_scop *scop,
+ const char *prefix, int i)
+{
+ int j;
+ char name[16];
+ isl_ctx *ctx;
+ isl_id *id;
+ int has_name;
+
+ ctx = isl_set_get_ctx(scop->context);
+ snprintf(name, sizeof(name), "%s%d", prefix, i);
+ id = isl_id_alloc(ctx, name, NULL);
+
+ j = 0;
+ while ((has_name = isl_id_to_ast_expr_has(scop->names, id)) == 1) {
+ isl_id_free(id);
+ snprintf(name, sizeof(name), "%s%d_%d", prefix, i, j++);
+ id = isl_id_alloc(ctx, name, NULL);
+ }
+
+ return has_name < 0 ? isl_id_free(id) : id;
+}
+
+/* Return a list of "n" isl_ids of the form "prefix%d".
+ * If an isl_id with such a name already appears among the variable names
+ * of "scop", then adjust the name to "prefix%d_%d".
+ */
+__isl_give isl_id_list *ppcg_scop_generate_names(struct ppcg_scop *scop,
+ int n, const char *prefix)
+{
+ int i;
+ isl_ctx *ctx;
+ isl_id_list *names;
+
+ ctx = isl_set_get_ctx(scop->context);
+ names = isl_id_list_alloc(ctx, n);
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+
+ id = generate_name(scop, prefix, i);
+ names = isl_id_list_add(names, id);
+ }
+
+ return names;
+}
+
+/* Is "stmt" not a kill statement?
+ */
+static int is_not_kill(struct pet_stmt *stmt)
+{
+ return !pet_stmt_is_kill(stmt);
+}
+
+/* Collect the iteration domains of the statements in "scop" that
+ * satisfy "pred".
+ */
+static __isl_give isl_union_set *collect_domains(struct pet_scop *scop,
+ int (*pred)(struct pet_stmt *stmt))
+{
+ int i;
+ isl_set *domain_i;
+ isl_union_set *domain;
+
+ if (!scop)
+ return NULL;
+
+ domain = isl_union_set_empty(isl_set_get_space(scop->context));
+
+ for (i = 0; i < scop->n_stmt; ++i) {
+ struct pet_stmt *stmt = scop->stmts[i];
+
+ if (!pred(stmt))
+ continue;
+
+ if (stmt->n_arg > 0)
+ isl_die(isl_union_set_get_ctx(domain),
+ isl_error_unsupported,
+ "data dependent conditions not supported",
+ return isl_union_set_free(domain));
+
+ domain_i = isl_set_copy(scop->stmts[i]->domain);
+ domain = isl_union_set_add_set(domain, domain_i);
+ }
+
+ return domain;
+}
+
+/* Collect the iteration domains of the statements in "scop",
+ * skipping kill statements.
+ */
+static __isl_give isl_union_set *collect_non_kill_domains(struct pet_scop *scop)
+{
+ return collect_domains(scop, &is_not_kill);
+}
+
+/* This function is used as a callback to pet_expr_foreach_call_expr
+ * to detect if there is any call expression in the input expression.
+ * Assign the value 1 to the integer that "user" points to and
+ * abort the search since we have found what we were looking for.
+ */
+static int set_has_call(__isl_keep pet_expr *expr, void *user)
+{
+ int *has_call = user;
+
+ *has_call = 1;
+
+ return -1;
+}
+
+/* Does "expr" contain any call expressions?
+ */
+static int expr_has_call(__isl_keep pet_expr *expr)
+{
+ int has_call = 0;
+
+ if (pet_expr_foreach_call_expr(expr, &set_has_call, &has_call) < 0 &&
+ !has_call)
+ return -1;
+
+ return has_call;
+}
+
+/* This function is a callback for pet_tree_foreach_expr.
+ * If "expr" contains any call (sub)expressions, then set *has_call
+ * and abort the search.
+ */
+static int check_call(__isl_keep pet_expr *expr, void *user)
+{
+ int *has_call = user;
+
+ if (expr_has_call(expr))
+ *has_call = 1;
+
+ return *has_call ? -1 : 0;
+}
+
+/* Does "stmt" contain any call expressions?
+ */
+static int has_call(struct pet_stmt *stmt)
+{
+ int has_call = 0;
+
+ if (pet_tree_foreach_expr(stmt->body, &check_call, &has_call) < 0 &&
+ !has_call)
+ return -1;
+
+ return has_call;
+}
+
+/* Collect the iteration domains of the statements in "scop"
+ * that contain a call expression.
+ */
+static __isl_give isl_union_set *collect_call_domains(struct pet_scop *scop)
+{
+ return collect_domains(scop, &has_call);
+}
+
+/* Given a union of "tagged" access relations of the form
+ *
+ * [S_i[...] -> R_j[]] -> A_k[...]
+ *
+ * project out the "tags" (R_j[]).
+ * That is, return a union of relations of the form
+ *
+ * S_i[...] -> A_k[...]
+ */
+static __isl_give isl_union_map *project_out_tags(
+ __isl_take isl_union_map *umap)
+{
+ return isl_union_map_domain_factor_domain(umap);
+}
+
+/* Construct a function from tagged iteration domains to the corresponding
+ * untagged iteration domains with as range of the wrapped map in the domain
+ * the reference tags that appear in any of the reads, writes or kills.
+ * Store the result in ps->tagger.
+ *
+ * For example, if the statement with iteration space S[i,j]
+ * contains two array references R_1[] and R_2[], then ps->tagger will contain
+ *
+ * { [S[i,j] -> R_1[]] -> S[i,j]; [S[i,j] -> R_2[]] -> S[i,j] }
+ */
+void compute_tagger(struct ppcg_scop *ps)
+{
+ isl_union_map *tagged;
+ isl_union_pw_multi_aff *tagger;
+
+ tagged = isl_union_map_copy(ps->tagged_reads);
+ tagged = isl_union_map_union(tagged,
+ isl_union_map_copy(ps->tagged_may_writes));
+ tagged = isl_union_map_union(tagged,
+ isl_union_map_copy(ps->tagged_must_kills));
+ tagged = isl_union_map_universe(tagged);
+ tagged = isl_union_set_unwrap(isl_union_map_domain(tagged));
+
+ tagger = isl_union_map_domain_map_union_pw_multi_aff(tagged);
+
+ ps->tagger = tagger;
+}
+
+/* Compute the live out accesses, i.e., the writes that are
+ * potentially not killed by any kills or any other writes, and
+ * store them in ps->live_out.
+ *
+ * We compute the "dependence" of any "kill" (an explicit kill
+ * or a must write) on any may write.
+ * The elements accessed by the may writes with a "depending" kill
+ * also accessing the element are definitely killed.
+ * The remaining may writes can potentially be live out.
+ *
+ * The result of the dependence analysis is
+ *
+ * { IW -> [IK -> A] }
+ *
+ * with IW the instance of the write statement, IK the instance of kill
+ * statement and A the element that was killed.
+ * The range factor range is
+ *
+ * { IW -> A }
+ *
+ * containing all such pairs for which there is a kill statement instance,
+ * i.e., all pairs that have been killed.
+ */
+static void compute_live_out(struct ppcg_scop *ps)
+{
+ isl_schedule *schedule;
+ isl_union_map *kills;
+ isl_union_map *exposed;
+ isl_union_map *covering;
+ isl_union_access_info *access;
+ isl_union_flow *flow;
+
+ schedule = isl_schedule_copy(ps->schedule);
+ kills = isl_union_map_union(isl_union_map_copy(ps->must_writes),
+ isl_union_map_copy(ps->must_kills));
+ access = isl_union_access_info_from_sink(kills);
+ access = isl_union_access_info_set_may_source(access,
+ isl_union_map_copy(ps->may_writes));
+ access = isl_union_access_info_set_schedule(access, schedule);
+ flow = isl_union_access_info_compute_flow(access);
+ covering = isl_union_flow_get_full_may_dependence(flow);
+ isl_union_flow_free(flow);
+
+ covering = isl_union_map_range_factor_range(covering);
+ exposed = isl_union_map_copy(ps->may_writes);
+ exposed = isl_union_map_subtract(exposed, covering);
+ ps->live_out = exposed;
+}
+
+/* Compute the tagged flow dependences and the live_in accesses and store
+ * the results in ps->tagged_dep_flow and ps->live_in.
+ *
+ * We allow both the must writes and the must kills to serve as
+ * definite sources such that a subsequent read would not depend
+ * on any earlier write. The resulting flow dependences with
+ * a must kill as source reflect possibly uninitialized reads.
+ * No dependences need to be introduced to protect such reads
+ * (other than those imposed by potential flows from may writes
+ * that follow the kill). We therefore remove those flow dependences.
+ * This is also useful for the dead code elimination, which assumes
+ * the flow sources are non-kill instances.
+ */
+static void compute_tagged_flow_dep_only(struct ppcg_scop *ps)
+{
+ isl_union_pw_multi_aff *tagger;
+ isl_schedule *schedule;
+ isl_union_map *live_in;
+ isl_union_access_info *access;
+ isl_union_flow *flow;
+ isl_union_map *must_source;
+ isl_union_map *kills;
+ isl_union_map *tagged_flow;
+
+ tagger = isl_union_pw_multi_aff_copy(ps->tagger);
+ schedule = isl_schedule_copy(ps->schedule);
+ schedule = isl_schedule_pullback_union_pw_multi_aff(schedule, tagger);
+ kills = isl_union_map_copy(ps->tagged_must_kills);
+ must_source = isl_union_map_copy(ps->tagged_must_writes);
+ must_source = isl_union_map_union(must_source,
+ isl_union_map_copy(kills));
+ access = isl_union_access_info_from_sink(
+ isl_union_map_copy(ps->tagged_reads));
+ access = isl_union_access_info_set_must_source(access, must_source);
+ access = isl_union_access_info_set_may_source(access,
+ isl_union_map_copy(ps->tagged_may_writes));
+ access = isl_union_access_info_set_schedule(access, schedule);
+ flow = isl_union_access_info_compute_flow(access);
+ tagged_flow = isl_union_flow_get_may_dependence(flow);
+ tagged_flow = isl_union_map_subtract_domain(tagged_flow,
+ isl_union_map_domain(kills));
+ ps->tagged_dep_flow = tagged_flow;
+ live_in = isl_union_flow_get_may_no_source(flow);
+ ps->live_in = project_out_tags(live_in);
+ isl_union_flow_free(flow);
+}
+
+/* Compute ps->dep_flow from ps->tagged_dep_flow
+ * by projecting out the reference tags.
+ */
+static void derive_flow_dep_from_tagged_flow_dep(struct ppcg_scop *ps)
+{
+ ps->dep_flow = isl_union_map_copy(ps->tagged_dep_flow);
+ ps->dep_flow = isl_union_map_factor_domain(ps->dep_flow);
+}
+
+/* Compute the flow dependences and the live_in accesses and store
+ * the results in ps->dep_flow and ps->live_in.
+ * A copy of the flow dependences, tagged with the reference tags
+ * is stored in ps->tagged_dep_flow.
+ *
+ * We first compute ps->tagged_dep_flow, i.e., the tagged flow dependences
+ * and then project out the tags.
+ */
+static void compute_tagged_flow_dep(struct ppcg_scop *ps)
+{
+ compute_tagged_flow_dep_only(ps);
+ derive_flow_dep_from_tagged_flow_dep(ps);
+}
+
+/* Compute the order dependences that prevent the potential live ranges
+ * from overlapping.
+ *
+ * In particular, construct a union of relations
+ *
+ * [R[...] -> R_1[]] -> [W[...] -> R_2[]]
+ *
+ * where [R[...] -> R_1[]] is the range of one or more live ranges
+ * (i.e., a read) and [W[...] -> R_2[]] is the domain of one or more
+ * live ranges (i.e., a write). Moreover, the read and the write
+ * access the same memory element and the read occurs before the write
+ * in the original schedule.
+ * The scheduler allows some of these dependences to be violated, provided
+ * the adjacent live ranges are all local (i.e., their domain and range
+ * are mapped to the same point by the current schedule band).
+ *
+ * Note that if a live range is not local, then we need to make
+ * sure it does not overlap with _any_ other live range, and not
+ * just with the "previous" and/or the "next" live range.
+ * We therefore add order dependences between reads and
+ * _any_ later potential write.
+ *
+ * We also need to be careful about writes without a corresponding read.
+ * They are already prevented from moving past non-local preceding
+ * intervals, but we also need to prevent them from moving past non-local
+ * following intervals. We therefore also add order dependences from
+ * potential writes that do not appear in any intervals
+ * to all later potential writes.
+ * Note that dead code elimination should have removed most of these
+ * dead writes, but the dead code elimination may not remove all dead writes,
+ * so we need to consider them to be safe.
+ *
+ * The order dependences are computed by computing the "dataflow"
+ * from the above unmatched writes and the reads to the may writes.
+ * The unmatched writes and the reads are treated as may sources
+ * such that they would not kill order dependences from earlier
+ * such writes and reads.
+ */
+static void compute_order_dependences(struct ppcg_scop *ps)
+{
+ isl_union_map *reads;
+ isl_union_map *shared_access;
+ isl_union_set *matched;
+ isl_union_map *unmatched;
+ isl_union_pw_multi_aff *tagger;
+ isl_schedule *schedule;
+ isl_union_access_info *access;
+ isl_union_flow *flow;
+
+ tagger = isl_union_pw_multi_aff_copy(ps->tagger);
+ schedule = isl_schedule_copy(ps->schedule);
+ schedule = isl_schedule_pullback_union_pw_multi_aff(schedule, tagger);
+ reads = isl_union_map_copy(ps->tagged_reads);
+ matched = isl_union_map_domain(isl_union_map_copy(ps->tagged_dep_flow));
+ unmatched = isl_union_map_copy(ps->tagged_may_writes);
+ unmatched = isl_union_map_subtract_domain(unmatched, matched);
+ reads = isl_union_map_union(reads, unmatched);
+ access = isl_union_access_info_from_sink(
+ isl_union_map_copy(ps->tagged_may_writes));
+ access = isl_union_access_info_set_may_source(access, reads);
+ access = isl_union_access_info_set_schedule(access, schedule);
+ flow = isl_union_access_info_compute_flow(access);
+ shared_access = isl_union_flow_get_may_dependence(flow);
+ isl_union_flow_free(flow);
+
+ ps->tagged_dep_order = isl_union_map_copy(shared_access);
+ ps->dep_order = isl_union_map_factor_domain(shared_access);
+}
+
+/* Compute those validity dependences of the program represented by "scop"
+ * that should be unconditionally enforced even when live-range reordering
+ * is used.
+ *
+ * In particular, compute the external false dependences
+ * as well as order dependences between sources with the same sink.
+ * The anti-dependences are already taken care of by the order dependences.
+ * The external false dependences are only used to ensure that live-in and
+ * live-out data is not overwritten by any writes inside the scop.
+ * The independences are removed from the external false dependences,
+ * but not from the order dependences between sources with the same sink.
+ *
+ * In particular, the reads from live-in data need to precede any
+ * later write to the same memory element.
+ * As to live-out data, the last writes need to remain the last writes.
+ * That is, any earlier write in the original schedule needs to precede
+ * the last write to the same memory element in the computed schedule.
+ * The possible last writes have been computed by compute_live_out.
+ * They may include kills, but if the last access is a kill,
+ * then the corresponding dependences will effectively be ignored
+ * since we do not schedule any kill statements.
+ *
+ * Note that the set of live-in and live-out accesses may be
+ * an overapproximation. There may therefore be potential writes
+ * before a live-in access and after a live-out access.
+ *
+ * In the presence of may-writes, there may be multiple live-ranges
+ * with the same sink, accessing the same memory element.
+ * The sources of these live-ranges need to be executed
+ * in the same relative order as in the original program
+ * since we do not know which of the may-writes will actually
+ * perform a write. Consider all sources that share a sink and
+ * that may write to the same memory element and compute
+ * the order dependences among them.
+ */
+static void compute_forced_dependences(struct ppcg_scop *ps)
+{
+ isl_union_map *shared_access;
+ isl_union_map *exposed;
+ isl_union_map *live_in;
+ isl_union_map *sink_access;
+ isl_union_map *shared_sink;
+ isl_union_access_info *access;
+ isl_union_flow *flow;
+ isl_schedule *schedule;
+
+ exposed = isl_union_map_copy(ps->live_out);
+ schedule = isl_schedule_copy(ps->schedule);
+ access = isl_union_access_info_from_sink(exposed);
+ access = isl_union_access_info_set_may_source(access,
+ isl_union_map_copy(ps->may_writes));
+ access = isl_union_access_info_set_schedule(access, schedule);
+ flow = isl_union_access_info_compute_flow(access);
+ shared_access = isl_union_flow_get_may_dependence(flow);
+ isl_union_flow_free(flow);
+ ps->dep_forced = shared_access;
+
+ schedule = isl_schedule_copy(ps->schedule);
+ access = isl_union_access_info_from_sink(
+ isl_union_map_copy(ps->may_writes));
+ access = isl_union_access_info_set_may_source(access,
+ isl_union_map_copy(ps->live_in));
+ access = isl_union_access_info_set_schedule(access, schedule);
+ flow = isl_union_access_info_compute_flow(access);
+ live_in = isl_union_flow_get_may_dependence(flow);
+ isl_union_flow_free(flow);
+
+ ps->dep_forced = isl_union_map_union(ps->dep_forced, live_in);
+ ps->dep_forced = isl_union_map_subtract(ps->dep_forced,
+ isl_union_map_copy(ps->independence));
+
+ schedule = isl_schedule_copy(ps->schedule);
+ sink_access = isl_union_map_copy(ps->tagged_dep_flow);
+ sink_access = isl_union_map_range_product(sink_access,
+ isl_union_map_copy(ps->tagged_may_writes));
+ sink_access = isl_union_map_domain_factor_domain(sink_access);
+ access = isl_union_access_info_from_sink(
+ isl_union_map_copy(sink_access));
+ access = isl_union_access_info_set_may_source(access, sink_access);
+ access = isl_union_access_info_set_schedule(access, schedule);
+ flow = isl_union_access_info_compute_flow(access);
+ shared_sink = isl_union_flow_get_may_dependence(flow);
+ isl_union_flow_free(flow);
+ ps->dep_forced = isl_union_map_union(ps->dep_forced, shared_sink);
+}
+
+/* Remove independence from the tagged flow dependences.
+ * Since the user has guaranteed that source and sink of an independence
+ * can be executed in any order, there cannot be a flow dependence
+ * between them, so they can be removed from the set of flow dependences.
+ * However, if the source of such a flow dependence is a must write,
+ * then it may have killed other potential sources, which would have
+ * to be recovered if we were to remove those flow dependences.
+ * We therefore keep the flow dependences that originate in a must write,
+ * even if it corresponds to a known independence.
+ */
+static void remove_independences_from_tagged_flow(struct ppcg_scop *ps)
+{
+ isl_union_map *tf;
+ isl_union_set *indep;
+ isl_union_set *mw;
+
+ tf = isl_union_map_copy(ps->tagged_dep_flow);
+ tf = isl_union_map_zip(tf);
+ indep = isl_union_map_wrap(isl_union_map_copy(ps->independence));
+ tf = isl_union_map_intersect_domain(tf, indep);
+ tf = isl_union_map_zip(tf);
+ mw = isl_union_map_domain(isl_union_map_copy(ps->tagged_must_writes));
+ tf = isl_union_map_subtract_domain(tf, mw);
+ ps->tagged_dep_flow = isl_union_map_subtract(ps->tagged_dep_flow, tf);
+}
+
+/* Compute the dependences of the program represented by "scop"
+ * in case live range reordering is allowed.
+ *
+ * We compute the actual live ranges and the corresponding order
+ * false dependences.
+ *
+ * The independences are removed from the flow dependences
+ * (provided the source is not a must-write) as well as
+ * from the external false dependences (by compute_forced_dependences).
+ */
+static void compute_live_range_reordering_dependences(struct ppcg_scop *ps)
+{
+ compute_tagged_flow_dep_only(ps);
+ remove_independences_from_tagged_flow(ps);
+ derive_flow_dep_from_tagged_flow_dep(ps);
+ compute_order_dependences(ps);
+ compute_forced_dependences(ps);
+}
+
+/* Compute the potential flow dependences and the potential live in
+ * accesses.
+ */
+static void compute_flow_dep(struct ppcg_scop *ps)
+{
+ isl_union_access_info *access;
+ isl_union_flow *flow;
+
+ access = isl_union_access_info_from_sink(isl_union_map_copy(ps->reads));
+ access = isl_union_access_info_set_must_source(access,
+ isl_union_map_copy(ps->must_writes));
+ access = isl_union_access_info_set_may_source(access,
+ isl_union_map_copy(ps->may_writes));
+ access = isl_union_access_info_set_schedule(access,
+ isl_schedule_copy(ps->schedule));
+ flow = isl_union_access_info_compute_flow(access);
+
+ ps->dep_flow = isl_union_flow_get_may_dependence(flow);
+ ps->live_in = isl_union_flow_get_may_no_source(flow);
+ isl_union_flow_free(flow);
+}
+
+/* Compute the dependences of the program represented by "scop".
+ * Store the computed potential flow dependences
+ * in scop->dep_flow and the reads with potentially no corresponding writes in
+ * scop->live_in.
+ * Store the potential live out accesses in scop->live_out.
+ * Store the potential false (anti and output) dependences in scop->dep_false.
+ *
+ * If live range reordering is allowed, then we compute a separate
+ * set of order dependences and a set of external false dependences
+ * in compute_live_range_reordering_dependences.
+ */
+void compute_dependences(struct ppcg_scop *scop)
+{
+ isl_union_map *may_source;
+ isl_union_access_info *access;
+ isl_union_flow *flow;
+
+ if (!scop)
+ return;
+
+ compute_live_out(scop);
+
+ if (scop->options->live_range_reordering)
+ compute_live_range_reordering_dependences(scop);
+ else if (scop->options->target != PPCG_TARGET_C)
+ compute_tagged_flow_dep(scop);
+ else
+ compute_flow_dep(scop);
+
+ may_source = isl_union_map_union(isl_union_map_copy(scop->may_writes),
+ isl_union_map_copy(scop->reads));
+ access = isl_union_access_info_from_sink(
+ isl_union_map_copy(scop->may_writes));
+ access = isl_union_access_info_set_must_source(access,
+ isl_union_map_copy(scop->must_writes));
+ access = isl_union_access_info_set_may_source(access, may_source);
+ access = isl_union_access_info_set_schedule(access,
+ isl_schedule_copy(scop->schedule));
+ flow = isl_union_access_info_compute_flow(access);
+
+ scop->dep_false = isl_union_flow_get_may_dependence(flow);
+ scop->dep_false = isl_union_map_coalesce(scop->dep_false);
+ isl_union_flow_free(flow);
+}
+
+/* Eliminate dead code from ps->domain.
+ *
+ * In particular, intersect both ps->domain and the domain of
+ * ps->schedule with the (parts of) iteration
+ * domains that are needed to produce the output or for statement
+ * iterations that call functions.
+ * Also intersect the range of the dataflow dependences with
+ * this domain such that the removed instances will no longer
+ * be considered as targets of dataflow.
+ *
+ * We start with the iteration domains that call functions
+ * and the set of iterations that last write to an array
+ * (except those that are later killed).
+ *
+ * Then we add those statement iterations that produce
+ * something needed by the "live" statements iterations.
+ * We keep doing this until no more statement iterations can be added.
+ * To ensure that the procedure terminates, we compute the affine
+ * hull of the live iterations (bounded to the original iteration
+ * domains) each time we have added extra iterations.
+ */
+void eliminate_dead_code(struct ppcg_scop *ps)
+{
+ isl_union_set *live;
+ isl_union_map *dep;
+ isl_union_pw_multi_aff *tagger;
+
+ live = isl_union_map_domain(isl_union_map_copy(ps->live_out));
+ if (!isl_union_set_is_empty(ps->call)) {
+ live = isl_union_set_union(live, isl_union_set_copy(ps->call));
+ live = isl_union_set_coalesce(live);
+ }
+
+ dep = isl_union_map_copy(ps->dep_flow);
+ dep = isl_union_map_reverse(dep);
+
+ for (;;) {
+ isl_union_set *extra;
+
+ extra = isl_union_set_apply(isl_union_set_copy(live),
+ isl_union_map_copy(dep));
+ if (isl_union_set_is_subset(extra, live)) {
+ isl_union_set_free(extra);
+ break;
+ }
+
+ live = isl_union_set_union(live, extra);
+ live = isl_union_set_affine_hull(live);
+ live = isl_union_set_intersect(live,
+ isl_union_set_copy(ps->domain));
+ }
+
+ isl_union_map_free(dep);
+
+ ps->domain = isl_union_set_intersect(ps->domain,
+ isl_union_set_copy(live));
+ ps->schedule = isl_schedule_intersect_domain(ps->schedule,
+ isl_union_set_copy(live));
+ ps->dep_flow = isl_union_map_intersect_range(ps->dep_flow,
+ isl_union_set_copy(live));
+ tagger = isl_union_pw_multi_aff_copy(ps->tagger);
+ live = isl_union_set_preimage_union_pw_multi_aff(live, tagger);
+ ps->tagged_dep_flow = isl_union_map_intersect_range(ps->tagged_dep_flow,
+ live);
+}
+
+/* Intersect "set" with the set described by "str", taking the NULL
+ * string to represent the universal set.
+ */
+static __isl_give isl_set *set_intersect_str(__isl_take isl_set *set,
+ const char *str)
+{
+ isl_ctx *ctx;
+ isl_set *set2;
+
+ if (!str)
+ return set;
+
+ ctx = isl_set_get_ctx(set);
+ set2 = isl_set_read_from_str(ctx, str);
+ set = isl_set_intersect(set, set2);
+
+ return set;
+}
+
+void *ppcg_scop_free(struct ppcg_scop *ps)
+{
+ if (!ps)
+ return NULL;
+
+ isl_set_free(ps->context);
+ isl_union_set_free(ps->domain);
+ isl_union_set_free(ps->call);
+ isl_union_map_free(ps->tagged_reads);
+ isl_union_map_free(ps->reads);
+ isl_union_map_free(ps->live_in);
+ isl_union_map_free(ps->tagged_may_writes);
+ isl_union_map_free(ps->tagged_must_writes);
+ isl_union_map_free(ps->may_writes);
+ isl_union_map_free(ps->must_writes);
+ isl_union_map_free(ps->live_out);
+ isl_union_map_free(ps->tagged_must_kills);
+ isl_union_map_free(ps->must_kills);
+ isl_union_map_free(ps->tagged_dep_flow);
+ isl_union_map_free(ps->dep_flow);
+ isl_union_map_free(ps->dep_false);
+ isl_union_map_free(ps->dep_forced);
+ isl_union_map_free(ps->tagged_dep_order);
+ isl_union_map_free(ps->dep_order);
+ isl_schedule_free(ps->schedule);
+ isl_union_pw_multi_aff_free(ps->tagger);
+ isl_union_map_free(ps->independence);
+ isl_id_to_ast_expr_free(ps->names);
+
+ free(ps);
+
+ return NULL;
+}
+
+/* Extract a ppcg_scop from a pet_scop.
+ *
+ * The constructed ppcg_scop refers to elements from the pet_scop
+ * so the pet_scop should not be freed before the ppcg_scop.
+ */
+static struct ppcg_scop *ppcg_scop_from_pet_scop(struct pet_scop *scop,
+ struct ppcg_options *options)
+{
+ int i;
+ isl_ctx *ctx;
+ struct ppcg_scop *ps;
+
+ if (!scop)
+ return NULL;
+
+ ctx = isl_set_get_ctx(scop->context);
+
+ ps = isl_calloc_type(ctx, struct ppcg_scop);
+ if (!ps)
+ return NULL;
+
+ ps->names = collect_names(scop);
+ ps->options = options;
+ ps->start = pet_loc_get_start(scop->loc);
+ ps->end = pet_loc_get_end(scop->loc);
+ ps->context = isl_set_copy(scop->context);
+ ps->context = set_intersect_str(ps->context, options->ctx);
+ if (options->non_negative_parameters) {
+ isl_space *space = isl_set_get_space(ps->context);
+ isl_set *nn = isl_set_nat_universe(space);
+ ps->context = isl_set_intersect(ps->context, nn);
+ }
+ ps->domain = collect_non_kill_domains(scop);
+ ps->call = collect_call_domains(scop);
+ ps->tagged_reads = pet_scop_get_tagged_may_reads(scop);
+ ps->reads = pet_scop_get_may_reads(scop);
+ ps->tagged_may_writes = pet_scop_get_tagged_may_writes(scop);
+ ps->may_writes = pet_scop_get_may_writes(scop);
+ ps->tagged_must_writes = pet_scop_get_tagged_must_writes(scop);
+ ps->must_writes = pet_scop_get_must_writes(scop);
+ ps->tagged_must_kills = pet_scop_get_tagged_must_kills(scop);
+ ps->must_kills = pet_scop_get_must_kills(scop);
+ ps->schedule = isl_schedule_copy(scop->schedule);
+ ps->pet = scop;
+ ps->independence = isl_union_map_empty(isl_set_get_space(ps->context));
+ for (i = 0; i < scop->n_independence; ++i)
+ ps->independence = isl_union_map_union(ps->independence,
+ isl_union_map_copy(scop->independences[i]->filter));
+
+ compute_tagger(ps);
+ compute_dependences(ps);
+ eliminate_dead_code(ps);
+
+ if (!ps->context || !ps->domain || !ps->call || !ps->reads ||
+ !ps->may_writes || !ps->must_writes || !ps->tagged_must_kills ||
+ !ps->must_kills || !ps->schedule || !ps->independence || !ps->names)
+ return ppcg_scop_free(ps);
+
+ return ps;
+}
+
+/* Internal data structure for ppcg_transform.
+ */
+struct ppcg_transform_data {
+ struct ppcg_options *options;
+ __isl_give isl_printer *(*transform)(__isl_take isl_printer *p,
+ struct ppcg_scop *scop, void *user);
+ void *user;
+};
+
+/* Should we print the original code?
+ * That is, does "scop" involve any data dependent conditions or
+ * nested expressions that cannot be handled by pet_stmt_build_ast_exprs?
+ */
+static int print_original(struct pet_scop *scop, struct ppcg_options *options)
+{
+ if (!pet_scop_can_build_ast_exprs(scop)) {
+ if (options->debug->verbose)
+ fprintf(stdout, "Printing original code because "
+ "some index expressions cannot currently "
+ "be printed\n");
+ return 1;
+ }
+
+ if (pet_scop_has_data_dependent_conditions(scop)) {
+ if (options->debug->verbose)
+ fprintf(stdout, "Printing original code because "
+ "input involves data dependent conditions\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Callback for pet_transform_C_source that transforms
+ * the given pet_scop to a ppcg_scop before calling the
+ * ppcg_transform callback.
+ *
+ * If "scop" contains any data dependent conditions or if we may
+ * not be able to print the transformed program, then just print
+ * the original code.
+ */
+static __isl_give isl_printer *transform(__isl_take isl_printer *p,
+ struct pet_scop *scop, void *user)
+{
+ struct ppcg_transform_data *data = user;
+ struct ppcg_scop *ps;
+
+ if (print_original(scop, data->options)) {
+ p = pet_scop_print_original(scop, p);
+ pet_scop_free(scop);
+ return p;
+ }
+
+ scop = pet_scop_align_params(scop);
+ ps = ppcg_scop_from_pet_scop(scop, data->options);
+
+ p = data->transform(p, ps, data->user);
+
+ ppcg_scop_free(ps);
+ pet_scop_free(scop);
+
+ return p;
+}
+
+/* Transform the C source file "input" by rewriting each scop
+ * through a call to "transform".
+ * The transformed C code is written to "out".
+ *
+ * This is a wrapper around pet_transform_C_source that transforms
+ * the pet_scop to a ppcg_scop before calling "fn".
+ */
+int ppcg_transform(isl_ctx *ctx, const char *input, FILE *out,
+ struct ppcg_options *options,
+ __isl_give isl_printer *(*fn)(__isl_take isl_printer *p,
+ struct ppcg_scop *scop, void *user), void *user)
+{
+ struct ppcg_transform_data data = { options, fn, user };
+ return pet_transform_C_source(ctx, input, out, &transform, &data);
+}
+
+/* Check consistency of options.
+ *
+ * Return -1 on error.
+ */
+static int check_options(isl_ctx *ctx)
+{
+ struct options *options;
+
+ options = isl_ctx_peek_options(ctx, &options_args);
+ if (!options)
+ isl_die(ctx, isl_error_internal,
+ "unable to find options", return -1);
+
+ if (options->ppcg->openmp &&
+ !isl_options_get_ast_build_atomic_upper_bound(ctx))
+ isl_die(ctx, isl_error_invalid,
+ "OpenMP requires atomic bounds", return -1);
+
+ return 0;
+}
+
+#if 0
+int main(int argc, char **argv)
+{
+ int r;
+ isl_ctx *ctx;
+ struct options *options;
+
+ options = options_new_with_defaults();
+ assert(options);
+
+ ctx = isl_ctx_alloc_with_options(&options_args, options);
+ ppcg_options_set_target_defaults(options->ppcg);
+ isl_options_set_ast_build_detect_min_max(ctx, 1);
+ isl_options_set_ast_print_macro_once(ctx, 1);
+ isl_options_set_schedule_whole_component(ctx, 0);
+ isl_options_set_schedule_maximize_band_depth(ctx, 1);
+ isl_options_set_schedule_maximize_coincidence(ctx, 1);
+ pet_options_set_encapsulate_dynamic_control(ctx, 1);
+ argc = options_parse(options, argc, argv, ISL_ARG_ALL);
+
+ if (check_options(ctx) < 0)
+ r = EXIT_FAILURE;
+ else if (options->ppcg->target == PPCG_TARGET_CUDA)
+ r = generate_cuda(ctx, options->ppcg, options->input);
+ else if (options->ppcg->target == PPCG_TARGET_OPENCL)
+ r = generate_opencl(ctx, options->ppcg, options->input,
+ options->output);
+ else
+ r = generate_cpu(ctx, options->ppcg, options->input,
+ options->output);
+
+ isl_ctx_free(ctx);
+
+ return r;
+}
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg.h
new file mode 100644
index 00000000000..ef89683dc1f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg.h
@@ -0,0 +1,128 @@
+#ifndef PPCG_H
+#define PPCG_H
+
+#include <isl/schedule.h>
+#include <isl/set.h>
+#include <isl/union_set.h>
+#include <isl/union_map.h>
+#include <isl/id_to_ast_expr.h>
+#include <pet.h>
+
+#include "ppcg_options.h"
+
+const char *ppcg_base_name(const char *filename);
+int ppcg_extract_base_name(char *name, const char *input);
+
+/* Representation of the scop for use inside PPCG.
+ *
+ * "options" are the options specified by the user.
+ * Some fields in this structure may depend on some of the options.
+ *
+ * "start" and "end" are file offsets of the corresponding program text.
+ * "context" represents constraints on the parameters.
+ * "domain" is the union of all iteration domains.
+ * "call" contains the iteration domains of statements with a call expression.
+ * "reads" contains all potential read accesses.
+ * "tagged_reads" is the same as "reads", except that the domain is a wrapped
+ * relation mapping an iteration domain to a reference identifier
+ * "live_in" contains the potential read accesses that potentially
+ * have no corresponding writes in the scop.
+ * "may_writes" contains all potential write accesses.
+ * "tagged_may_writes" is the same as "may_writes", except that the domain
+ * is a wrapped relation mapping an iteration domain
+ * to a reference identifier
+ * "must_writes" contains all definite write accesses.
+ * "tagged_must_writes" is the same as "must_writes", except that the domain
+ * is a wrapped relation mapping an iteration domain
+ * to a reference identifier
+ * "live_out" contains the potential write accesses that are potentially
+ * not killed by any kills or any other writes.
+ * "must_kills" contains all definite kill accesses.
+ * "tagged_must_kills" is the same as "must_kills", except that the domain
+ * is a wrapped relation mapping an iteration domain
+ * to a reference identifier.
+ *
+ * "tagger" maps tagged iteration domains to the corresponding untagged
+ * iteration domain.
+ *
+ * "independence" is the union of all independence filters.
+ *
+ * "dep_flow" represents the potential flow dependences.
+ * "tagged_dep_flow" is the same as "dep_flow", except that both domain and
+ * range are wrapped relations mapping an iteration domain to
+ * a reference identifier. May be NULL if not computed.
+ * "dep_false" represents the potential false (anti and output) dependences.
+ * "dep_forced" represents the validity constraints that should be enforced
+ * even when live-range reordering is used.
+ * In particular, these constraints ensure that all live-in
+ * accesses remain live-in and that all live-out accesses remain live-out
+ * and that multiple potential sources for the same read are
+ * executed in the original order.
+ * "dep_order"/"tagged_dep_order" represents the order dependences between
+ * the live range intervals in "dep_flow"/"tagged_dep_flow".
+ * It is only used if the live_range_reordering
+ * option is set. Otherwise it is NULL.
+ * If "dep_order" is used, then "dep_false" only contains a limited
+ * set of anti and output dependences.
+ * "schedule" represents the (original) schedule.
+ *
+ * "names" contains all variable names that are in use by the scop.
+ * The names are mapped to a dummy value.
+ *
+ * "pet" is the original pet_scop.
+ */
+struct ppcg_scop {
+ struct ppcg_options *options;
+
+ unsigned start;
+ unsigned end;
+
+ isl_set *context;
+ isl_union_set *domain;
+ isl_union_set *call;
+ isl_union_map *tagged_reads;
+ isl_union_map *reads;
+ isl_union_map *live_in;
+ isl_union_map *tagged_may_writes;
+ isl_union_map *may_writes;
+ isl_union_map *tagged_must_writes;
+ isl_union_map *must_writes;
+ isl_union_map *live_out;
+ isl_union_map *tagged_must_kills;
+ isl_union_map *must_kills;
+
+ isl_union_pw_multi_aff *tagger;
+
+ isl_union_map *independence;
+
+ isl_union_map *dep_flow;
+ isl_union_map *tagged_dep_flow;
+ isl_union_map *dep_false;
+ isl_union_map *dep_forced;
+ isl_union_map *dep_order;
+ isl_union_map *tagged_dep_order;
+ isl_schedule *schedule;
+
+ isl_id_to_ast_expr *names;
+
+ struct pet_scop *pet;
+};
+
+int ppcg_scop_any_hidden_declarations(struct ppcg_scop *scop);
+__isl_give isl_id_list *ppcg_scop_generate_names(struct ppcg_scop *scop,
+ int n, const char *prefix);
+
+int ppcg_transform(isl_ctx *ctx, const char *input, FILE *out,
+ struct ppcg_options *options,
+ __isl_give isl_printer *(*fn)(__isl_take isl_printer *p,
+ struct ppcg_scop *scop, void *user), void *user);
+
+__isl_give isl_schedule *ppcg_compute_schedule(
+ __isl_take isl_schedule_constraints *sc,
+ __isl_keep isl_schedule *schedule, struct ppcg_options *options);
+
+void compute_tagger(struct ppcg_scop *ps);
+void compute_dependences(struct ppcg_scop *scop);
+void eliminate_dead_code(struct ppcg_scop *ps);
+void *ppcg_scop_free(struct ppcg_scop *ps);
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg_options.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg_options.c
new file mode 100644
index 00000000000..77b0f29de01
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg_options.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2010-2011 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include "ppcg_options.h"
+
+static struct isl_arg_choice target[] = {
+ {"c", PPCG_TARGET_C},
+ {"cuda", PPCG_TARGET_CUDA},
+ {"opencl", PPCG_TARGET_OPENCL},
+ {0}
+};
+
+/* Set defaults that depend on the target.
+ * In particular, set --schedule-outer-coincidence iff target is a GPU.
+ */
+void ppcg_options_set_target_defaults(struct ppcg_options *options)
+{
+ char *argv[2] = { NULL };
+
+ argv[0] = "ppcg_options_set_target_defaults";
+ if (options->target == PPCG_TARGET_C)
+ argv[1] = "--no-schedule-outer-coincidence";
+ else
+ argv[1] = "--schedule-outer-coincidence";
+
+ isl_options_parse(options->isl, 2, argv, ISL_ARG_ALL);
+}
+
+/* Callback that is called whenever the "target" option is set (to "val").
+ * The callback is called after target has been updated.
+ *
+ * Call ppcg_options_set_target_defaults to reset the target-dependent options.
+ */
+static int set_target(void *opt, unsigned val)
+{
+ struct ppcg_options *options = opt;
+
+ ppcg_options_set_target_defaults(options);
+
+ return 0;
+}
+
+ISL_ARGS_START(struct ppcg_debug_options, ppcg_debug_options_args)
+ISL_ARG_BOOL(struct ppcg_debug_options, dump_schedule_constraints, 0,
+ "dump-schedule-constraints", 0, "dump schedule constraints")
+ISL_ARG_BOOL(struct ppcg_debug_options, dump_schedule, 0,
+ "dump-schedule", 0, "dump isl computed schedule")
+ISL_ARG_BOOL(struct ppcg_debug_options, dump_final_schedule, 0,
+ "dump-final-schedule", 0, "dump PPCG computed schedule")
+ISL_ARG_BOOL(struct ppcg_debug_options, dump_sizes, 0,
+ "dump-sizes", 0,
+ "dump effectively used per kernel tile, grid and block sizes")
+ISL_ARG_BOOL(struct ppcg_debug_options, verbose, 'v', "verbose", 0, NULL)
+ISL_ARGS_END
+
+ISL_ARGS_START(struct ppcg_options, ppcg_opencl_options_args)
+ISL_ARG_STR(struct ppcg_options, opencl_compiler_options, 0, "compiler-options",
+ "options", NULL, "options to pass to the OpenCL compiler")
+ISL_ARG_BOOL(struct ppcg_options, opencl_use_gpu, 0, "use-gpu", 1,
+ "use GPU device (if available)")
+ISL_ARG_STR_LIST(struct ppcg_options, opencl_n_include_file,
+ opencl_include_files, 0, "include-file", "filename",
+ "file to #include in generated OpenCL code")
+ISL_ARG_BOOL(struct ppcg_options, opencl_print_kernel_types, 0,
+ "print-kernel-types", 1,
+ "print definitions of types in the kernel file")
+ISL_ARG_BOOL(struct ppcg_options, opencl_embed_kernel_code, 0,
+ "embed-kernel-code", 0, "embed kernel code into host code")
+ISL_ARGS_END
+
+ISL_ARGS_START(struct ppcg_options, ppcg_options_args)
+ISL_ARG_CHILD(struct ppcg_options, isl, "isl", &isl_options_args, "isl options")
+ISL_ARG_CHILD(struct ppcg_options, debug, NULL, &ppcg_debug_options_args,
+ "debugging options")
+ISL_ARG_BOOL(struct ppcg_options, group_chains, 0, "group-chains", 1,
+ "group chains of interdependent statements that are executed "
+ "consecutively in the original schedule before scheduling")
+ISL_ARG_BOOL(struct ppcg_options, reschedule, 0, "reschedule", 1,
+ "replace original schedule by isl computed schedule")
+ISL_ARG_BOOL(struct ppcg_options, scale_tile_loops, 0,
+ "scale-tile-loops", 1, NULL)
+ISL_ARG_BOOL(struct ppcg_options, wrap, 0, "wrap", 1, NULL)
+ISL_ARG_BOOL(struct ppcg_options, use_shared_memory, 0, "shared-memory", 1,
+ "use shared memory in kernel code")
+ISL_ARG_BOOL(struct ppcg_options, use_private_memory, 0, "private-memory", 1,
+ "use private memory in kernel code")
+ISL_ARG_STR(struct ppcg_options, ctx, 0, "ctx", "context", NULL,
+ "Constraints on parameters")
+ISL_ARG_BOOL(struct ppcg_options, non_negative_parameters, 0,
+ "assume-non-negative-parameters", 0,
+ "assume all parameters are non-negative)")
+ISL_ARG_BOOL(struct ppcg_options, tile, 0, "tile", 0,
+ "perform tiling (C target)")
+ISL_ARG_INT(struct ppcg_options, tile_size, 'S', "tile-size", "size", 32, NULL)
+ISL_ARG_BOOL(struct ppcg_options, isolate_full_tiles, 0, "isolate-full-tiles",
+ 0, "isolate full tiles from partial tiles (hybrid tiling)")
+ISL_ARG_STR(struct ppcg_options, sizes, 0, "sizes", "sizes", NULL,
+ "Per kernel tile, grid and block sizes")
+ISL_ARG_INT(struct ppcg_options, max_shared_memory, 0,
+ "max-shared-memory", "size", 8192, "maximal amount of shared memory")
+ISL_ARG_BOOL(struct ppcg_options, openmp, 0, "openmp", 0,
+ "Generate OpenMP macros (only for C target)")
+ISL_ARG_USER_OPT_CHOICE(struct ppcg_options, target, 0, "target", target,
+ &set_target, PPCG_TARGET_CUDA, PPCG_TARGET_CUDA,
+ "the target to generate code for")
+ISL_ARG_BOOL(struct ppcg_options, linearize_device_arrays, 0,
+ "linearize-device-arrays", 1,
+ "linearize all device arrays, even those of fixed size")
+ISL_ARG_BOOL(struct ppcg_options, allow_gnu_extensions, 0,
+ "allow-gnu-extensions", 1,
+ "allow the use of GNU extensions in generated code")
+ISL_ARG_BOOL(struct ppcg_options, live_range_reordering, 0,
+ "live-range-reordering", 1,
+ "allow successive live ranges on the same memory element "
+ "to be reordered")
+ISL_ARG_BOOL(struct ppcg_options, hybrid, 0, "hybrid", 0,
+ "apply hybrid tiling whenever a suitable input pattern is found "
+ "(GPU targets)")
+ISL_ARG_BOOL(struct ppcg_options, unroll_copy_shared, 0, "unroll-copy-shared",
+ 0, "unroll code for copying to/from shared memory")
+ISL_ARG_BOOL(struct ppcg_options, unroll_gpu_tile, 0, "unroll-gpu-tile", 0,
+ "unroll code inside tile on GPU targets")
+ISL_ARG_GROUP("opencl", &ppcg_opencl_options_args, "OpenCL options")
+ISL_ARG_STR(struct ppcg_options, save_schedule_file, 0, "save-schedule",
+ "file", NULL, "save isl computed schedule to <file>")
+ISL_ARG_STR(struct ppcg_options, load_schedule_file, 0, "load-schedule",
+ "file", NULL, "load schedule from <file>, "
+ "using it instead of an isl computed schedule")
+ISL_ARGS_END
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg_options.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg_options.h
new file mode 100644
index 00000000000..56aa7cb781a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ppcg_options.h
@@ -0,0 +1,100 @@
+#ifndef PPCG_OPTIONS_H
+#define PPCG_OPTIONS_H
+
+#include <isl/arg.h>
+#include <isl/options.h>
+
+struct ppcg_debug_options {
+ int dump_schedule_constraints;
+ int dump_schedule;
+ int dump_final_schedule;
+ int dump_sizes;
+ int verbose;
+};
+
+struct ppcg_options {
+ struct isl_options *isl;
+ struct ppcg_debug_options *debug;
+
+ /* Group chains of consecutive statements before scheduling. */
+ int group_chains;
+
+ /* Use isl to compute a schedule replacing the original schedule. */
+ int reschedule;
+ int scale_tile_loops;
+ int wrap;
+
+ /* Assume all parameters are non-negative. */
+ int non_negative_parameters;
+ char *ctx;
+ char *sizes;
+
+ /* Perform tiling (C target). */
+ int tile;
+ int tile_size;
+
+ /* Isolate full tiles from partial tiles. */
+ int isolate_full_tiles;
+
+ /* Take advantage of private memory. */
+ int use_private_memory;
+
+ /* Take advantage of shared memory. */
+ int use_shared_memory;
+
+ /* Maximal amount of shared memory. */
+ int max_shared_memory;
+
+ /* The target we generate code for. */
+ int target;
+
+ /* Generate OpenMP macros (C target only). */
+ int openmp;
+
+ /* Linearize all device arrays. */
+ int linearize_device_arrays;
+
+ /* Allow the use of GNU extensions in generated code. */
+ int allow_gnu_extensions;
+
+ /* Allow live range to be reordered. */
+ int live_range_reordering;
+
+ /* Allow hybrid tiling whenever a suitable input pattern is found. */
+ int hybrid;
+
+ /* Unroll the code for copying to/from shared memory. */
+ int unroll_copy_shared;
+ /* Unroll code inside tile on GPU targets. */
+ int unroll_gpu_tile;
+
+ /* Options to pass to the OpenCL compiler. */
+ char *opencl_compiler_options;
+ /* Prefer GPU device over CPU. */
+ int opencl_use_gpu;
+ /* Number of files to include. */
+ int opencl_n_include_file;
+ /* Files to include. */
+ const char **opencl_include_files;
+ /* Print definitions of types in kernels. */
+ int opencl_print_kernel_types;
+ /* Embed OpenCL kernel code in host code. */
+ int opencl_embed_kernel_code;
+
+ /* Name of file for saving isl computed schedule or NULL. */
+ char *save_schedule_file;
+ /* Name of file for loading schedule or NULL. */
+ char *load_schedule_file;
+};
+
+ISL_ARG_DECL(ppcg_debug_options, struct ppcg_debug_options,
+ ppcg_debug_options_args)
+ISL_ARG_DECL(ppcg_options, struct ppcg_options, ppcg_options_args)
+
+#define PPCG_TARGET_C 0
+#define PPCG_TARGET_CUDA 1
+#define PPCG_TARGET_OPENCL 2
+
+void ppcg_options_set_target_defaults(struct ppcg_options *options);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/print.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/print.c
new file mode 100644
index 00000000000..dd839e48e51
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/print.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright 2012-2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
+ */
+
+#include <isl/aff.h>
+#include <isl/ast_build.h>
+#include <isl/id.h>
+
+#include "print.h"
+#include "util.h"
+
+__isl_give isl_printer *ppcg_start_block(__isl_take isl_printer *p)
+{
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "{");
+ p = isl_printer_end_line(p);
+ p = isl_printer_indent(p, 2);
+ return p;
+}
+
+__isl_give isl_printer *ppcg_end_block(__isl_take isl_printer *p)
+{
+ p = isl_printer_indent(p, -2);
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "}");
+ p = isl_printer_end_line(p);
+ return p;
+}
+
+/* Names of notes that keep track of whether min/max
+ * macro definitions have already been printed.
+ */
+static const char *ppcg_max_printed = "ppcg_max_printed";
+static const char *ppcg_min_printed = "ppcg_min_printed";
+
+/* Has the macro definition corresponding to "note_name" been printed
+ * to "p" before?
+ * That is, does "p" have an associated "note_name" note?
+ */
+static isl_bool printed_before(__isl_keep isl_printer *p, const char *note_name)
+{
+ isl_ctx *ctx;
+ isl_id *id;
+ isl_bool printed;
+
+ if (!p)
+ return isl_bool_error;
+
+ ctx = isl_printer_get_ctx(p);
+ id = isl_id_alloc(ctx, note_name, NULL);
+ printed = isl_printer_has_note(p, id);
+ isl_id_free(id);
+
+ return printed;
+}
+
+/* Keep track of the fact that the macro definition corresponding
+ * to "note_name" has been printed to "p" by attaching a note with
+ * that name. The value of the note is of no importance, but it
+ * has to be a valid isl_id, so the note identifier is reused
+ * as the note.
+ */
+static __isl_give isl_printer *mark_printed(__isl_take isl_printer *p,
+ const char *note_name)
+{
+ isl_ctx *ctx;
+ isl_id *id;
+
+ if (!p)
+ return NULL;
+
+ ctx = isl_printer_get_ctx(p);
+ id = isl_id_alloc(ctx, note_name, NULL);
+ return isl_printer_set_note(p, id, isl_id_copy(id));
+}
+
+/* Print a macro definition "def" for the macro "name" to "p",
+ * unless such a macro definition has been printed to "p" before.
+ * "note_name" is used as the name of the note that keeps track
+ * of whether this printing has happened.
+ */
+static __isl_give isl_printer *print_ppcg_macro(__isl_take isl_printer *p,
+ const char *name, const char *def, const char *note_name)
+{
+ isl_bool printed;
+
+ printed = printed_before(p, note_name);
+ if (printed < 0)
+ return isl_printer_free(p);
+ if (printed)
+ return p;
+
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, "#define ");
+ p = isl_printer_print_str(p, name);
+ p = isl_printer_print_str(p, def);
+ p = isl_printer_end_line(p);
+
+ p = mark_printed(p, note_name);
+
+ return p;
+}
+
+/* Structure for keeping track of definitions of some macros.
+ */
+struct ppcg_macros {
+ const char *min;
+ const char *max;
+};
+
+/* Free the memory allocated by a struct ppcg_macros.
+ */
+static void ppcg_macros_free(void *user)
+{
+ free(user);
+}
+
+/* Default macro definitions (when GNU extensions are allowed).
+ */
+struct ppcg_macros ppcg_macros_default = {
+ .min = "(x,y) "
+ "({ __typeof__(x) _x = (x); __typeof__(y) _y = (y); "
+ "_x < _y ? _x : _y; })",
+ .max = "(x,y) "
+ "({ __typeof__(x) _x = (x); __typeof__(y) _y = (y); "
+ "_x > _y ? _x : _y; })",
+};
+
+/* Name used for the note that keeps track of macro definitions.
+ */
+static const char *ppcg_macros = "ppcg_macros";
+
+/* Set the macro definitions for isl_ast_op_min and isl_ast_op_max
+ * to "min" and "max" and store them in "p".
+ *
+ * In particular, create a ppcg_macros object and attach it
+ * as a note to the printer.
+ */
+__isl_give isl_printer *ppcg_set_macros(__isl_take isl_printer *p,
+ const char *min, const char *max)
+{
+ isl_ctx *ctx;
+ isl_id *id, *macros_id;
+ struct ppcg_macros *macros;
+
+ if (!p)
+ return NULL;
+
+ ctx = isl_printer_get_ctx(p);
+ macros = isl_alloc_type(ctx, struct ppcg_macros);
+ if (!macros)
+ return isl_printer_free(p);
+ macros->min = min;
+ macros->max = max;
+ id = isl_id_alloc(ctx, ppcg_macros, NULL);
+ macros_id = isl_id_alloc(ctx, NULL, macros);
+ if (!macros_id)
+ ppcg_macros_free(macros);
+ else
+ macros_id = isl_id_set_free_user(macros_id, &ppcg_macros_free);
+
+ p = isl_printer_set_note(p, id, macros_id);
+
+ return p;
+}
+
+/* Return the ppcg_macros object that holds the currently active
+ * macro definitions in "p".
+ * If "p" has a note with macro definitions, then return those.
+ * Otherwise, return the default macro definitions.
+ */
+static struct ppcg_macros *get_macros(__isl_keep isl_printer *p)
+{
+ isl_id *id;
+ isl_bool has_macros;
+ struct ppcg_macros *macros;
+
+ id = isl_id_alloc(isl_printer_get_ctx(p), ppcg_macros, NULL);
+ has_macros = isl_printer_has_note(p, id);
+ if (has_macros < 0 || !has_macros) {
+ isl_id_free(id);
+ if (has_macros < 0)
+ return NULL;
+ return &ppcg_macros_default;
+ }
+ id = isl_printer_get_note(p, id);
+ macros = isl_id_get_user(id);
+ isl_id_free(id);
+
+ return macros;
+}
+
+/* Print the currently active macro definition for ppcg_max.
+ */
+static __isl_give isl_printer *print_max(__isl_take isl_printer *p)
+{
+ struct ppcg_macros *macros;
+
+ macros = get_macros(p);
+ if (!macros)
+ return isl_printer_free(p);
+ return print_ppcg_macro(p, ppcg_max, macros->max, ppcg_max_printed);
+}
+
+/* Print the currently active macro definition for ppcg_min.
+ */
+static __isl_give isl_printer *print_min(__isl_take isl_printer *p)
+{
+ struct ppcg_macros *macros;
+
+ macros = get_macros(p);
+ if (!macros)
+ return isl_printer_free(p);
+ return print_ppcg_macro(p, ppcg_min, macros->min, ppcg_min_printed);
+}
+
+/* Print a macro definition for "type" to "p".
+ * If GNU extensions are allowed, then print a specialized definition
+ * for isl_ast_op_min and isl_ast_op_max.
+ * Otherwise, use the default isl definition.
+ */
+__isl_give isl_printer *ppcg_print_macro(enum isl_ast_op_type type,
+ __isl_take isl_printer *p)
+{
+ isl_ctx *ctx;
+ struct ppcg_options *options;
+
+ if (!p)
+ return NULL;
+
+ ctx = isl_printer_get_ctx(p);
+ options = isl_ctx_peek_options(ctx, &ppcg_options_args);
+ if (!options || !options->allow_gnu_extensions)
+ return isl_ast_op_type_print_macro(type, p);
+
+ switch (type) {
+ case isl_ast_op_max:
+ return print_max(p);
+ case isl_ast_op_min:
+ return print_min(p);
+ default:
+ return isl_ast_op_type_print_macro(type, p);
+ }
+}
+
+/* isl_ast_expr_foreach_ast_op_type or isl_ast_node_foreach_ast_op_type
+ * callback that prints a macro definition for "type".
+ */
+static isl_stat print_macro(enum isl_ast_op_type type, void *user)
+{
+ isl_printer **p = user;
+
+ *p = ppcg_print_macro(type, *p);
+ if (!*p)
+ return isl_stat_error;
+
+ return isl_stat_ok;
+}
+
+/* Print the required macros for "expr".
+ */
+__isl_give isl_printer *ppcg_ast_expr_print_macros(
+ __isl_keep isl_ast_expr *expr, __isl_take isl_printer *p)
+{
+ if (isl_ast_expr_foreach_ast_op_type(expr, &print_macro, &p) < 0)
+ return isl_printer_free(p);
+ return p;
+}
+
+/* isl_id_to_ast_expr_foreach callback that prints the required
+ * macro definitions for "val".
+ */
+static isl_stat print_expr_macros(__isl_take isl_id *key,
+ __isl_take isl_ast_expr *val, void *user)
+{
+ isl_printer **p = user;
+
+ *p = ppcg_ast_expr_print_macros(val, *p);
+ isl_id_free(key);
+ isl_ast_expr_free(val);
+
+ if (!*p)
+ return isl_stat_error;
+ return isl_stat_ok;
+}
+
+/* Print the required macro definitions for the body of a statement in which
+ * the access expressions are replaced by the isl_ast_expr objects
+ * in "ref2expr".
+ */
+__isl_give isl_printer *ppcg_print_body_macros(__isl_take isl_printer *p,
+ __isl_keep isl_id_to_ast_expr *ref2expr)
+{
+ if (isl_id_to_ast_expr_foreach(ref2expr, &print_expr_macros, &p) < 0)
+ return isl_printer_free(p);
+ return p;
+}
+
+/* Print the required macros for "node".
+ */
+__isl_give isl_printer *ppcg_print_macros(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node)
+{
+ if (isl_ast_node_foreach_ast_op_type(node, &print_macro, &p) < 0)
+ return isl_printer_free(p);
+ return p;
+}
+
+/* Names used for the macros that may appear in a printed isl AST.
+ */
+const char *ppcg_min = "ppcg_min";
+const char *ppcg_max = "ppcg_max";
+const char *ppcg_fdiv_q = "ppcg_fdiv_q";
+
+/* Set the names of the macros that may appear in a printed isl AST.
+ */
+__isl_give isl_printer *ppcg_set_macro_names(__isl_take isl_printer *p)
+{
+ p = isl_ast_op_type_set_print_name(p, isl_ast_op_min, ppcg_min);
+ p = isl_ast_op_type_set_print_name(p, isl_ast_op_max, ppcg_max);
+ p = isl_ast_op_type_set_print_name(p, isl_ast_op_fdiv_q, ppcg_fdiv_q);
+
+ return p;
+}
+
+/* Given a multi affine expression "mpa" without domain, modify it to have
+ * the schedule space of "build" as domain.
+ *
+ * If the schedule space of "build" is a parameter space, then nothing
+ * needs to be done.
+ * Otherwise, "mpa" is first given a 0D domain and then it is combined
+ * with a mapping from the schedule space of "build" to the same 0D domain.
+ */
+__isl_give isl_multi_pw_aff *ppcg_attach_multi_pw_aff(
+ __isl_take isl_multi_pw_aff *mpa, __isl_keep isl_ast_build *build)
+{
+ isl_bool params;
+ isl_space *space;
+ isl_multi_aff *ma;
+
+ space = isl_ast_build_get_schedule_space(build);
+ params = isl_space_is_params(space);
+ if (params < 0 || params) {
+ isl_space_free(space);
+ if (params < 0)
+ return isl_multi_pw_aff_free(mpa);
+ return mpa;
+ }
+ space = isl_space_from_domain(space);
+ ma = isl_multi_aff_zero(space);
+ mpa = isl_multi_pw_aff_from_range(mpa);
+ mpa = isl_multi_pw_aff_pullback_multi_aff(mpa, ma);
+
+ return mpa;
+}
+
+/* Build an access AST expression from "size" using "build".
+ * "size" does not have a domain, but "build" may have a proper schedule space.
+ * First modify "size" to have that schedule space as domain.
+ */
+__isl_give isl_ast_expr *ppcg_build_size_expr(__isl_take isl_multi_pw_aff *size,
+ __isl_keep isl_ast_build *build)
+{
+ size = ppcg_attach_multi_pw_aff(size, build);
+ return isl_ast_build_access_from_multi_pw_aff(build, size);
+}
+
+/* Print a declaration for an array with element type "base_type" and
+ * size "size" to "p".
+ */
+__isl_give isl_printer *ppcg_print_declaration_with_size(
+ __isl_take isl_printer *p, const char *base_type,
+ __isl_keep isl_ast_expr *size)
+{
+ if (!base_type || !size)
+ return isl_printer_free(p);
+
+ p = ppcg_ast_expr_print_macros(size, p);
+ p = isl_printer_start_line(p);
+ p = isl_printer_print_str(p, base_type);
+ p = isl_printer_print_str(p, " ");
+ p = isl_printer_print_ast_expr(p, size);
+ p = isl_printer_print_str(p, ";");
+ p = isl_printer_end_line(p);
+
+ return p;
+}
+
+/* Print a declaration for array "array" to "p", using "build"
+ * to simplify any size expressions.
+ *
+ * The size is computed from the extent of the array and is
+ * subsequently converted to an "access expression" by "build".
+ */
+__isl_give isl_printer *ppcg_print_declaration(__isl_take isl_printer *p,
+ struct pet_array *array, __isl_keep isl_ast_build *build)
+{
+ isl_multi_pw_aff *size;
+ isl_ast_expr *expr;
+
+ if (!array)
+ return isl_printer_free(p);
+
+ size = ppcg_size_from_extent(isl_set_copy(array->extent));
+ expr = isl_ast_build_access_from_multi_pw_aff(build, size);
+ p = ppcg_print_declaration_with_size(p, array->element_type, expr);
+ isl_ast_expr_free(expr);
+
+ return p;
+}
+
+/* Print declarations for the arrays in "scop" that are declared
+ * and that are exposed (if exposed == 1) or not exposed (if exposed == 0).
+ */
+static __isl_give isl_printer *print_declarations(__isl_take isl_printer *p,
+ struct ppcg_scop *scop, int exposed)
+{
+ int i;
+ isl_ast_build *build;
+
+ if (!scop)
+ return isl_printer_free(p);
+
+ build = isl_ast_build_from_context(isl_set_copy(scop->context));
+ for (i = 0; i < scop->pet->n_array; ++i) {
+ struct pet_array *array = scop->pet->arrays[i];
+
+ if (!array->declared)
+ continue;
+ if (array->exposed != exposed)
+ continue;
+
+ p = ppcg_print_declaration(p, array, build);
+ }
+ isl_ast_build_free(build);
+
+ return p;
+}
+
+/* Print declarations for the arrays in "scop" that are declared
+ * and exposed to the code after the scop.
+ */
+__isl_give isl_printer *ppcg_print_exposed_declarations(
+ __isl_take isl_printer *p, struct ppcg_scop *scop)
+{
+ return print_declarations(p, scop, 1);
+}
+
+/* Print declarations for the arrays in "scop" that are declared,
+ * but not exposed to the code after the scop.
+ */
+__isl_give isl_printer *ppcg_print_hidden_declarations(
+ __isl_take isl_printer *p, struct ppcg_scop *scop)
+{
+ return print_declarations(p, scop, 0);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/print.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/print.h
new file mode 100644
index 00000000000..3b33b0ce32a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/print.h
@@ -0,0 +1,40 @@
+#ifndef PRINT_H
+#define PRINT_H
+
+#include <isl/ast.h>
+
+#include "ppcg.h"
+
+extern const char *ppcg_min;
+extern const char *ppcg_max;
+extern const char *ppcg_fdiv_q;
+
+__isl_give isl_printer *ppcg_start_block(__isl_take isl_printer *p);
+__isl_give isl_printer *ppcg_end_block(__isl_take isl_printer *p);
+
+__isl_give isl_printer *ppcg_set_macro_names(__isl_take isl_printer *p);
+__isl_give isl_printer *ppcg_set_macros(__isl_take isl_printer *p,
+ const char *min, const char *max);
+__isl_give isl_printer *ppcg_print_macro(enum isl_ast_op_type type,
+ __isl_take isl_printer *p);
+__isl_give isl_printer *ppcg_ast_expr_print_macros(
+ __isl_keep isl_ast_expr *expr, __isl_take isl_printer *p);
+__isl_give isl_printer *ppcg_print_body_macros(__isl_take isl_printer *p,
+ __isl_keep isl_id_to_ast_expr *ref2expr);
+__isl_give isl_printer *ppcg_print_macros(__isl_take isl_printer *p,
+ __isl_keep isl_ast_node *node);
+
+__isl_give isl_ast_expr *ppcg_build_size_expr(__isl_take isl_multi_pw_aff *size,
+ __isl_keep isl_ast_build *build);
+
+__isl_give isl_printer *ppcg_print_declaration_with_size(
+ __isl_take isl_printer *p, const char *base_type,
+ __isl_keep isl_ast_expr *size);
+__isl_give isl_printer *ppcg_print_declaration(__isl_take isl_printer *p,
+ struct pet_array *array, __isl_keep isl_ast_build *build);
+__isl_give isl_printer *ppcg_print_exposed_declarations(
+ __isl_take isl_printer *p, struct ppcg_scop *scop);
+__isl_give isl_printer *ppcg_print_hidden_declarations(
+ __isl_take isl_printer *p, struct ppcg_scop *scop);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/schedule.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/schedule.c
new file mode 100644
index 00000000000..7e65c31391b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/schedule.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2010-2011 INRIA Saclay
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
+ * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
+ * 91893 Orsay, France
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <isl/set.h>
+#include <isl/map.h>
+#include <isl/constraint.h>
+
+#include "schedule.h"
+
+/* Add parameters with identifiers "ids" to "set".
+ */
+static __isl_give isl_set *add_params(__isl_take isl_set *set,
+ __isl_keep isl_id_list *ids)
+{
+ int i, n;
+ unsigned nparam;
+
+ n = isl_id_list_n_id(ids);
+
+ nparam = isl_set_dim(set, isl_dim_param);
+ set = isl_set_add_dims(set, isl_dim_param, n);
+
+ for (i = 0; i < n; ++i) {
+ isl_id *id;
+
+ id = isl_id_list_get_id(ids, i);
+ set = isl_set_set_dim_id(set, isl_dim_param, nparam + i, id);
+ }
+
+ return set;
+}
+
+/* Equate the dimensions of "set" starting at "first" to
+ * freshly created parameters with identifiers "ids".
+ * The number of equated dimensions is equal to the number of elements in "ids".
+ */
+static __isl_give isl_set *parametrize(__isl_take isl_set *set,
+ int first, __isl_keep isl_id_list *ids)
+{
+ int i, n;
+ unsigned nparam;
+
+ nparam = isl_set_dim(set, isl_dim_param);
+
+ set = add_params(set, ids);
+
+ n = isl_id_list_n_id(ids);
+ for (i = 0; i < n; ++i)
+ set = isl_set_equate(set, isl_dim_param, nparam + i,
+ isl_dim_set, first + i);
+
+ return set;
+}
+
+/* Given a parameter space "space", create a set of dimension "len"
+ * of which the dimensions starting at "first" are equated to
+ * freshly created parameters with identifiers "ids".
+ */
+__isl_give isl_set *parametrization(__isl_take isl_space *space,
+ int len, int first, __isl_keep isl_id_list *ids)
+{
+ isl_set *set;
+
+ space = isl_space_set_from_params(space);
+ space = isl_space_add_dims(space, isl_dim_set, len);
+ set = isl_set_universe(space);
+
+ return parametrize(set, first, ids);
+}
+
+/* Load and return a schedule from a file called "filename".
+ */
+static __isl_give isl_schedule *load_schedule(isl_ctx *ctx,
+ const char *filename)
+{
+ FILE *file;
+ isl_schedule *schedule;
+
+ file = fopen(filename, "r");
+ if (!file) {
+ fprintf(stderr, "Unable to open '%s' for reading\n", filename);
+ return NULL;
+ }
+ schedule = isl_schedule_read_from_file(ctx, file);
+ fclose(file);
+
+ return schedule;
+}
+
+/* Save the schedule "schedule" to a file called "filename".
+ * The schedule is printed in block style.
+ */
+static void save_schedule(__isl_keep isl_schedule *schedule,
+ const char *filename)
+{
+ FILE *file;
+ isl_ctx *ctx;
+ isl_printer *p;
+
+ if (!schedule)
+ return;
+
+ file = fopen(filename, "w");
+ if (!file) {
+ fprintf(stderr, "Unable to open '%s' for writing\n", filename);
+ return;
+ }
+ ctx = isl_schedule_get_ctx(schedule);
+ p = isl_printer_to_file(ctx, file);
+ p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_BLOCK);
+ p = isl_printer_print_schedule(p, schedule);
+ isl_printer_free(p);
+ fclose(file);
+}
+
+/* Obtain a schedule, either by reading it form a file
+ * or by computing it using "compute".
+ * Also take care of saving the computed schedule and/or
+ * dumping the obtained schedule if requested by the user.
+ */
+__isl_give isl_schedule *ppcg_get_schedule(isl_ctx *ctx,
+ struct ppcg_options *options,
+ __isl_give isl_schedule *(*compute)(void *user), void *user)
+{
+ isl_schedule *schedule;
+
+ if (options->load_schedule_file) {
+ schedule = load_schedule(ctx, options->load_schedule_file);
+ } else {
+ schedule = compute(user);
+ if (options->save_schedule_file)
+ save_schedule(schedule, options->save_schedule_file);
+ }
+ if (options->debug->dump_schedule)
+ isl_schedule_dump(schedule);
+
+ return schedule;
+}
+
+/* Mark all dimensions in the band node "node" to be of "type".
+ */
+__isl_give isl_schedule_node *ppcg_set_schedule_node_type(
+ __isl_take isl_schedule_node *node, enum isl_ast_loop_type type)
+{
+ int i, n;
+
+ n = isl_schedule_node_band_n_member(node);
+ for (i = 0; i < n; ++i)
+ node = isl_schedule_node_band_member_set_ast_loop_type(node, i,
+ type);
+
+ return node;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/schedule.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/schedule.h
new file mode 100644
index 00000000000..771466558b3
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/schedule.h
@@ -0,0 +1,21 @@
+#ifndef _SCHEDULE_H
+#define _SCHEDULE_H
+
+#include <isl/id.h>
+#include <isl/space.h>
+#include <isl/schedule.h>
+#include <isl/schedule_node.h>
+
+#include "ppcg_options.h"
+
+__isl_give isl_set *parametrization(__isl_take isl_space *space,
+ int len, int first, __isl_keep isl_id_list *names);
+
+__isl_give isl_schedule *ppcg_get_schedule(isl_ctx *ctx,
+ struct ppcg_options *options,
+ __isl_give isl_schedule *(*compute)(void *user), void *user);
+
+__isl_give isl_schedule_node *ppcg_set_schedule_node_type(
+ __isl_take isl_schedule_node *node, enum isl_ast_loop_type type);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/util.c b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/util.c
new file mode 100644
index 00000000000..0a67bc34d47
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/util.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012-2013 Ecole Normale Superieure
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ * Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France
+ */
+
+#include <isl/space.h>
+#include <isl/val.h>
+#include <isl/aff.h>
+#include <isl/set.h>
+
+#include "util.h"
+
+/* Construct an isl_multi_val living in "space" with all values equal to "val".
+ */
+__isl_give isl_multi_val *ppcg_multi_val_from_int(__isl_take isl_space *space,
+ int val)
+{
+ int i, n;
+ isl_ctx *ctx;
+ isl_val *v;
+ isl_multi_val *mv;
+
+ if (!space)
+ return NULL;
+
+ ctx = isl_space_get_ctx(space);
+ n = isl_space_dim(space, isl_dim_set);
+ mv = isl_multi_val_zero(space);
+ v = isl_val_int_from_si(ctx, val);
+ for (i = 0; i < n; ++i)
+ mv = isl_multi_val_set_val(mv, i, isl_val_copy(v));
+ isl_val_free(v);
+
+ return mv;
+}
+
+/* Construct an isl_multi_val living in "space" with values specified
+ * by "list". "list" is assumed to have at least as many entries
+ * as the set dimension of "space".
+ */
+__isl_give isl_multi_val *ppcg_multi_val_from_int_list(
+ __isl_take isl_space *space, int *list)
+{
+ int i, n;
+ isl_ctx *ctx;
+ isl_multi_val *mv;
+
+ if (!space)
+ return NULL;
+
+ ctx = isl_space_get_ctx(space);
+ n = isl_space_dim(space, isl_dim_set);
+ mv = isl_multi_val_zero(space);
+ for (i = 0; i < n; ++i) {
+ isl_val *v;
+
+ v = isl_val_int_from_si(ctx, list[i]);
+ mv = isl_multi_val_set_val(mv, i, v);
+ }
+
+ return mv;
+}
+
+/* Compute the size of a bounding box around the origin and "set",
+ * where "set" is assumed to contain only non-negative elements.
+ * In particular, compute the maximal value of "set" in each direction
+ * and add one.
+ */
+__isl_give isl_multi_pw_aff *ppcg_size_from_extent(__isl_take isl_set *set)
+{
+ int i, n;
+ isl_multi_pw_aff *mpa;
+
+ n = isl_set_dim(set, isl_dim_set);
+ mpa = isl_multi_pw_aff_zero(isl_set_get_space(set));
+ for (i = 0; i < n; ++i) {
+ isl_space *space;
+ isl_aff *one;
+ isl_pw_aff *bound;
+
+ if (!isl_set_dim_has_upper_bound(set, isl_dim_set, i)) {
+ const char *name;
+ name = isl_set_get_tuple_name(set);
+ if (!name)
+ name = "";
+ fprintf(stderr, "unable to determine extent of '%s' "
+ "in dimension %d\n", name, i);
+ set = isl_set_free(set);
+ }
+ bound = isl_set_dim_max(isl_set_copy(set), i);
+
+ space = isl_pw_aff_get_domain_space(bound);
+ one = isl_aff_zero_on_domain(isl_local_space_from_space(space));
+ one = isl_aff_add_constant_si(one, 1);
+ bound = isl_pw_aff_add(bound, isl_pw_aff_from_aff(one));
+ mpa = isl_multi_pw_aff_set_pw_aff(mpa, i, bound);
+ }
+ isl_set_free(set);
+
+ return mpa;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/util.h b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/util.h
new file mode 100644
index 00000000000..544d6edc82d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/util.h
@@ -0,0 +1,22 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <string.h>
+
+#include <isl/space.h>
+#include <isl/val.h>
+
+/* Compare the prefix of "s" to "prefix" up to the length of "prefix".
+ */
+static inline int prefixcmp(const char *s, const char *prefix)
+{
+ return strncmp(s, prefix, strlen(prefix));
+}
+
+__isl_give isl_multi_val *ppcg_multi_val_from_int(__isl_take isl_space *space,
+ int val);
+__isl_give isl_multi_val *ppcg_multi_val_from_int_list(
+ __isl_take isl_space *space, int *list);
+__isl_give isl_multi_pw_aff *ppcg_size_from_extent(__isl_take isl_set *set);
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ya.make b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ya.make
new file mode 100644
index 00000000000..7463a450c8d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/External/ppcg/ya.make
@@ -0,0 +1,49 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(MIT)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14/tools/polly/lib/External/isl
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/polly/lib/External
+ contrib/libs/llvm14/tools/polly/lib/External/isl
+ contrib/libs/llvm14/tools/polly/lib/External/isl/include
+ contrib/libs/llvm14/tools/polly/lib/External/pet/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+IF (OS_WINDOWS)
+ CFLAGS(
+ -DPATH_MAX=260
+ )
+ENDIF()
+
+SRCS(
+ cuda.c
+ cuda_common.c
+ external.c
+ gpu.c
+ gpu_array_tile.c
+ gpu_group.c
+ gpu_hybrid.c
+ gpu_print.c
+ gpu_tree.c
+ grouping.c
+ hybrid.c
+ ppcg.c
+ ppcg_options.c
+ print.c
+ schedule.c
+ util.c
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/polly/lib/Support/DumpFunctionPass.cpp b/contrib/libs/llvm14/tools/polly/lib/Support/DumpFunctionPass.cpp
new file mode 100644
index 00000000000..a14e0efb82c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Support/DumpFunctionPass.cpp
@@ -0,0 +1,130 @@
+//===------ DumpFunctionPass.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Write a function to a file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/Support/DumpFunctionPass.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Transforms/IPO/GlobalDCE.h"
+#include "llvm/Transforms/IPO/StripDeadPrototypes.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+
+#define DEBUG_TYPE "polly-dump-func"
+
+using namespace llvm;
+using namespace polly;
+
+namespace {
+
+static void runDumpFunction(llvm::Function &F, StringRef Suffix) {
+ StringRef FName = F.getName();
+ Module *M = F.getParent();
+
+ StringRef ModuleName = M->getName();
+ StringRef Stem = sys::path::stem(ModuleName);
+ std::string Dumpfile = (Twine(Stem) + "-" + FName + Suffix + ".ll").str();
+ LLVM_DEBUG(dbgs() << "Dumping function '" << FName << "' to '" << Dumpfile
+ << "'...\n");
+
+ ValueToValueMapTy VMap;
+ auto ShouldCloneDefinition = [&F](const GlobalValue *GV) -> bool {
+ return GV == &F;
+ };
+ std::unique_ptr<Module> CM = CloneModule(*M, VMap, ShouldCloneDefinition);
+ Function *NewF = cast<Function>(VMap.lookup(&F));
+ assert(NewF && "Expected selected function to be cloned");
+
+ LLVM_DEBUG(dbgs() << "Global DCE...\n");
+
+ // Stop F itself from being pruned
+ GlobalValue::LinkageTypes OrigLinkage = NewF->getLinkage();
+ NewF->setLinkage(GlobalValue::ExternalLinkage);
+
+ {
+ ModuleAnalysisManager MAM;
+ ModulePassManager MPM;
+
+ PassInstrumentationCallbacks PIC;
+ MAM.registerPass([&] { return PassInstrumentationAnalysis(&PIC); });
+
+ MPM.addPass(GlobalDCEPass());
+ MPM.addPass(StripDeadPrototypesPass());
+ MPM.run(*CM, MAM);
+ }
+
+ // Restore old linkage
+ NewF->setLinkage(OrigLinkage);
+
+ LLVM_DEBUG(dbgs() << "Write to file '" << Dumpfile << "'...\n");
+
+ std::unique_ptr<ToolOutputFile> Out;
+ std::error_code EC;
+ Out.reset(new ToolOutputFile(Dumpfile, EC, sys::fs::OF_None));
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return;
+ }
+
+ CM->print(Out->os(), nullptr);
+ Out->keep();
+ LLVM_DEBUG(dbgs() << "Dump file " << Dumpfile << " written successfully\n");
+}
+
+class DumpFunctionWrapperPass : public FunctionPass {
+private:
+ DumpFunctionWrapperPass(const DumpFunctionWrapperPass &) = delete;
+ const DumpFunctionWrapperPass &
+ operator=(const DumpFunctionWrapperPass &) = delete;
+
+ std::string Suffix;
+
+public:
+ static char ID;
+
+ explicit DumpFunctionWrapperPass() : FunctionPass(ID), Suffix("-dump") {}
+
+ explicit DumpFunctionWrapperPass(std::string Suffix)
+ : FunctionPass(ID), Suffix(std::move(Suffix)) {}
+
+ /// @name FunctionPass interface
+ //@{
+ virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ }
+
+ virtual bool runOnFunction(llvm::Function &F) override {
+ runDumpFunction(F, Suffix);
+ return false;
+ }
+ //@}
+};
+
+char DumpFunctionWrapperPass::ID;
+} // namespace
+
+FunctionPass *polly::createDumpFunctionWrapperPass(std::string Suffix) {
+ return new DumpFunctionWrapperPass(std::move(Suffix));
+}
+
+llvm::PreservedAnalyses DumpFunctionPass::run(Function &F,
+ FunctionAnalysisManager &AM) {
+ runDumpFunction(F, Suffix);
+ return PreservedAnalyses::all();
+}
+
+INITIALIZE_PASS_BEGIN(DumpFunctionWrapperPass, "polly-dump-function",
+ "Polly - Dump Function", false, false)
+INITIALIZE_PASS_END(DumpFunctionWrapperPass, "polly-dump-function",
+ "Polly - Dump Function", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Support/DumpModulePass.cpp b/contrib/libs/llvm14/tools/polly/lib/Support/DumpModulePass.cpp
new file mode 100644
index 00000000000..85f4dd39add
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Support/DumpModulePass.cpp
@@ -0,0 +1,102 @@
+//===------ DumpModulePass.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Write a module to a file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/Support/DumpModulePass.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+#define DEBUG_TYPE "polly-dump-module"
+
+using namespace llvm;
+using namespace polly;
+
+namespace {
+
+static void runDumpModule(llvm::Module &M, StringRef Filename, bool IsSuffix) {
+ std::string Dumpfile;
+ if (IsSuffix) {
+ StringRef ModuleName = M.getName();
+ StringRef Stem = sys::path::stem(ModuleName);
+ Dumpfile = (Twine(Stem) + Filename + ".ll").str();
+ } else {
+ Dumpfile = Filename.str();
+ }
+ LLVM_DEBUG(dbgs() << "Dumping module to " << Dumpfile << '\n');
+
+ std::unique_ptr<ToolOutputFile> Out;
+ std::error_code EC;
+ Out.reset(new ToolOutputFile(Dumpfile, EC, sys::fs::OF_None));
+ if (EC) {
+ errs() << EC.message() << '\n';
+ return;
+ }
+
+ M.print(Out->os(), nullptr);
+ Out->keep();
+}
+
+class DumpModuleWrapperPass : public ModulePass {
+private:
+ DumpModuleWrapperPass(const DumpModuleWrapperPass &) = delete;
+ const DumpModuleWrapperPass &
+ operator=(const DumpModuleWrapperPass &) = delete;
+
+ std::string Filename;
+ bool IsSuffix;
+
+public:
+ static char ID;
+
+ /// This constructor is used e.g. if using opt -polly-dump-module.
+ ///
+ /// Provide a default suffix to not overwrite the original file.
+ explicit DumpModuleWrapperPass()
+ : ModulePass(ID), Filename("-dump"), IsSuffix(true) {}
+
+ explicit DumpModuleWrapperPass(std::string Filename, bool IsSuffix)
+ : ModulePass(ID), Filename(std::move(Filename)), IsSuffix(IsSuffix) {}
+
+ /// @name ModulePass interface
+ //@{
+ virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ }
+
+ virtual bool runOnModule(llvm::Module &M) override {
+ runDumpModule(M, Filename, IsSuffix);
+ return false;
+ }
+ //@}
+};
+
+char DumpModuleWrapperPass::ID;
+} // namespace
+
+ModulePass *polly::createDumpModuleWrapperPass(std::string Filename,
+ bool IsSuffix) {
+ return new DumpModuleWrapperPass(std::move(Filename), IsSuffix);
+}
+
+llvm::PreservedAnalyses DumpModulePass::run(llvm::Module &M,
+ llvm::ModuleAnalysisManager &AM) {
+ runDumpModule(M, Filename, IsSuffix);
+ return PreservedAnalyses::all();
+}
+
+INITIALIZE_PASS_BEGIN(DumpModuleWrapperPass, "polly-dump-module",
+ "Polly - Dump Module", false, false)
+INITIALIZE_PASS_END(DumpModuleWrapperPass, "polly-dump-module",
+ "Polly - Dump Module", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Support/GICHelper.cpp b/contrib/libs/llvm14/tools/polly/lib/Support/GICHelper.cpp
new file mode 100644
index 00000000000..638289c86be
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Support/GICHelper.cpp
@@ -0,0 +1,259 @@
+//===- GmpConv.cpp - Recreate LLVM IR from the Scop. ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Functions for converting between gmp objects and llvm::APInt.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/Support/GICHelper.h"
+#include "llvm/ADT/APInt.h"
+#include "isl/val.h"
+
+using namespace llvm;
+
+__isl_give isl_val *polly::isl_valFromAPInt(isl_ctx *Ctx, const APInt Int,
+ bool IsSigned) {
+ APInt Abs;
+ isl_val *v;
+
+ // As isl is interpreting the input always as unsigned value, we need some
+ // additional pre and post processing to import signed values. The approach
+ // we take is to first obtain the absolute value of Int and then negate the
+ // value after it has been imported to isl.
+ //
+ // It should be noted that the smallest integer value represented in two's
+ // complement with a certain amount of bits does not have a corresponding
+ // positive representation in two's complement representation with the same
+ // number of bits. E.g. 110 (-2) does not have a corresponding value for (2).
+ // To ensure that there is always a corresponding value available we first
+ // sign-extend the input by one bit and only then take the absolute value.
+ if (IsSigned)
+ Abs = Int.sext(Int.getBitWidth() + 1).abs();
+ else
+ Abs = Int;
+
+ const uint64_t *Data = Abs.getRawData();
+ unsigned Words = Abs.getNumWords();
+
+ v = isl_val_int_from_chunks(Ctx, Words, sizeof(uint64_t), Data);
+
+ if (IsSigned && Int.isNegative())
+ v = isl_val_neg(v);
+
+ return v;
+}
+
+APInt polly::APIntFromVal(__isl_take isl_val *Val) {
+ uint64_t *Data;
+ int NumChunks;
+ const static int ChunkSize = sizeof(uint64_t);
+
+ assert(isl_val_is_int(Val) && "Only integers can be converted to APInt");
+
+ NumChunks = isl_val_n_abs_num_chunks(Val, ChunkSize);
+ Data = (uint64_t *)malloc(NumChunks * ChunkSize);
+ isl_val_get_abs_num_chunks(Val, ChunkSize, Data);
+ int NumBits = CHAR_BIT * ChunkSize * NumChunks;
+ APInt A(NumBits, NumChunks, Data);
+
+ // As isl provides only an interface to obtain data that describes the
+ // absolute value of an isl_val, A at this point always contains a positive
+ // number. In case Val was originally negative, we expand the size of A by
+ // one and negate the value (in two's complement representation). As a result,
+ // the new value in A corresponds now with Val.
+ if (isl_val_is_neg(Val)) {
+ A = A.zext(A.getBitWidth() + 1);
+ A = -A;
+ }
+
+ // isl may represent small numbers with more than the minimal number of bits.
+ // We truncate the APInt to the minimal number of bits needed to represent the
+ // signed value it contains, to ensure that the bitwidth is always minimal.
+ if (A.getMinSignedBits() < A.getBitWidth())
+ A = A.trunc(A.getMinSignedBits());
+
+ free(Data);
+ isl_val_free(Val);
+ return A;
+}
+
+template <typename ISLTy, typename ISL_CTX_GETTER, typename ISL_PRINTER>
+static inline std::string stringFromIslObjInternal(__isl_keep ISLTy *isl_obj,
+ ISL_CTX_GETTER ctx_getter_fn,
+ ISL_PRINTER printer_fn,
+ std::string DefaultValue) {
+ if (!isl_obj)
+ return DefaultValue;
+ isl_ctx *ctx = ctx_getter_fn(isl_obj);
+ isl_printer *p = isl_printer_to_str(ctx);
+ p = printer_fn(p, isl_obj);
+ char *char_str = isl_printer_get_str(p);
+ std::string string;
+ if (char_str)
+ string = char_str;
+ else
+ string = DefaultValue;
+ free(char_str);
+ isl_printer_free(p);
+ return string;
+}
+
+#define ISL_C_OBJECT_TO_STRING(name) \
+ std::string polly::stringFromIslObj(__isl_keep isl_##name *Obj, \
+ std::string DefaultValue) { \
+ return stringFromIslObjInternal(Obj, isl_##name##_get_ctx, \
+ isl_printer_print_##name, DefaultValue); \
+ }
+
+ISL_C_OBJECT_TO_STRING(aff)
+ISL_C_OBJECT_TO_STRING(ast_expr)
+ISL_C_OBJECT_TO_STRING(ast_node)
+ISL_C_OBJECT_TO_STRING(basic_map)
+ISL_C_OBJECT_TO_STRING(basic_set)
+ISL_C_OBJECT_TO_STRING(map)
+ISL_C_OBJECT_TO_STRING(set)
+ISL_C_OBJECT_TO_STRING(id)
+ISL_C_OBJECT_TO_STRING(multi_aff)
+ISL_C_OBJECT_TO_STRING(multi_pw_aff)
+ISL_C_OBJECT_TO_STRING(multi_union_pw_aff)
+ISL_C_OBJECT_TO_STRING(point)
+ISL_C_OBJECT_TO_STRING(pw_aff)
+ISL_C_OBJECT_TO_STRING(pw_multi_aff)
+ISL_C_OBJECT_TO_STRING(schedule)
+ISL_C_OBJECT_TO_STRING(schedule_node)
+ISL_C_OBJECT_TO_STRING(space)
+ISL_C_OBJECT_TO_STRING(union_access_info)
+ISL_C_OBJECT_TO_STRING(union_flow)
+ISL_C_OBJECT_TO_STRING(union_set)
+ISL_C_OBJECT_TO_STRING(union_map)
+ISL_C_OBJECT_TO_STRING(union_pw_aff)
+ISL_C_OBJECT_TO_STRING(union_pw_multi_aff)
+
+static void replace(std::string &str, const std::string &find,
+ const std::string &replace) {
+ size_t pos = 0;
+ while ((pos = str.find(find, pos)) != std::string::npos) {
+ str.replace(pos, find.length(), replace);
+ pos += replace.length();
+ }
+}
+
+static void makeIslCompatible(std::string &str) {
+ replace(str, ".", "_");
+ replace(str, "\"", "_");
+ replace(str, " ", "__");
+ replace(str, "=>", "TO");
+ replace(str, "+", "_");
+}
+
+std::string polly::getIslCompatibleName(const std::string &Prefix,
+ const std::string &Middle,
+ const std::string &Suffix) {
+ std::string S = Prefix + Middle + Suffix;
+ makeIslCompatible(S);
+ return S;
+}
+
+std::string polly::getIslCompatibleName(const std::string &Prefix,
+ const std::string &Name, long Number,
+ const std::string &Suffix,
+ bool UseInstructionNames) {
+ std::string S = Prefix;
+
+ if (UseInstructionNames)
+ S += std::string("_") + Name;
+ else
+ S += std::to_string(Number);
+
+ S += Suffix;
+
+ makeIslCompatible(S);
+ return S;
+}
+
+std::string polly::getIslCompatibleName(const std::string &Prefix,
+ const Value *Val, long Number,
+ const std::string &Suffix,
+ bool UseInstructionNames) {
+ std::string ValStr;
+
+ if (UseInstructionNames && Val->hasName())
+ ValStr = std::string("_") + std::string(Val->getName());
+ else
+ ValStr = std::to_string(Number);
+
+ return getIslCompatibleName(Prefix, ValStr, Suffix);
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+#define ISL_DUMP_OBJECT_IMPL(NAME) \
+ void polly::dumpIslObj(const isl::NAME &Obj) { \
+ isl_##NAME##_dump(Obj.get()); \
+ } \
+ void polly::dumpIslObj(isl_##NAME *Obj) { isl_##NAME##_dump(Obj); }
+
+ISL_DUMP_OBJECT_IMPL(aff)
+ISL_DUMP_OBJECT_IMPL(aff_list)
+ISL_DUMP_OBJECT_IMPL(ast_expr)
+ISL_DUMP_OBJECT_IMPL(ast_node)
+ISL_DUMP_OBJECT_IMPL(ast_node_list)
+ISL_DUMP_OBJECT_IMPL(basic_map)
+ISL_DUMP_OBJECT_IMPL(basic_map_list)
+ISL_DUMP_OBJECT_IMPL(basic_set)
+ISL_DUMP_OBJECT_IMPL(basic_set_list)
+ISL_DUMP_OBJECT_IMPL(constraint)
+ISL_DUMP_OBJECT_IMPL(id)
+ISL_DUMP_OBJECT_IMPL(id_list)
+ISL_DUMP_OBJECT_IMPL(id_to_ast_expr)
+ISL_DUMP_OBJECT_IMPL(local_space)
+ISL_DUMP_OBJECT_IMPL(map)
+ISL_DUMP_OBJECT_IMPL(map_list)
+ISL_DUMP_OBJECT_IMPL(multi_aff)
+ISL_DUMP_OBJECT_IMPL(multi_pw_aff)
+ISL_DUMP_OBJECT_IMPL(multi_union_pw_aff)
+ISL_DUMP_OBJECT_IMPL(multi_val)
+ISL_DUMP_OBJECT_IMPL(point)
+ISL_DUMP_OBJECT_IMPL(pw_aff)
+ISL_DUMP_OBJECT_IMPL(pw_aff_list)
+ISL_DUMP_OBJECT_IMPL(pw_multi_aff)
+ISL_DUMP_OBJECT_IMPL(schedule)
+ISL_DUMP_OBJECT_IMPL(schedule_constraints)
+ISL_DUMP_OBJECT_IMPL(schedule_node)
+ISL_DUMP_OBJECT_IMPL(set)
+ISL_DUMP_OBJECT_IMPL(set_list)
+ISL_DUMP_OBJECT_IMPL(space)
+ISL_DUMP_OBJECT_IMPL(union_map)
+ISL_DUMP_OBJECT_IMPL(union_pw_aff)
+ISL_DUMP_OBJECT_IMPL(union_pw_aff_list)
+ISL_DUMP_OBJECT_IMPL(union_pw_multi_aff)
+ISL_DUMP_OBJECT_IMPL(union_set)
+ISL_DUMP_OBJECT_IMPL(union_set_list)
+ISL_DUMP_OBJECT_IMPL(val)
+ISL_DUMP_OBJECT_IMPL(val_list)
+
+void polly::dumpIslObj(__isl_keep isl_schedule_node *node, raw_ostream &OS) {
+ if (!node)
+ return;
+
+ isl_ctx *ctx = isl_schedule_node_get_ctx(node);
+ isl_printer *p = isl_printer_to_str(ctx);
+ p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_BLOCK);
+ p = isl_printer_print_schedule_node(p, node);
+
+ char *char_str = isl_printer_get_str(p);
+ OS << char_str;
+
+ free(char_str);
+ isl_printer_free(p);
+}
+
+void polly::dumpIslObj(const isl::schedule_node &Node, raw_ostream &OS) {
+ dumpIslObj(Node.get(), OS);
+}
+
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/Support/ISLTools.cpp b/contrib/libs/llvm14/tools/polly/lib/Support/ISLTools.cpp
new file mode 100644
index 00000000000..8b8c51aa202
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Support/ISLTools.cpp
@@ -0,0 +1,908 @@
+//===------ ISLTools.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Tools, utilities, helpers and extensions useful in conjunction with the
+// Integer Set Library (isl).
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/Support/ISLTools.h"
+#include "polly/Support/GICHelper.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <vector>
+
+using namespace polly;
+
+namespace {
+/// Create a map that shifts one dimension by an offset.
+///
+/// Example:
+/// makeShiftDimAff({ [i0, i1] -> [o0, o1] }, 1, -2)
+/// = { [i0, i1] -> [i0, i1 - 1] }
+///
+/// @param Space The map space of the result. Must have equal number of in- and
+/// out-dimensions.
+/// @param Pos Position to shift.
+/// @param Amount Value added to the shifted dimension.
+///
+/// @return An isl_multi_aff for the map with this shifted dimension.
+isl::multi_aff makeShiftDimAff(isl::space Space, int Pos, int Amount) {
+ auto Identity = isl::multi_aff::identity(Space);
+ if (Amount == 0)
+ return Identity;
+ auto ShiftAff = Identity.at(Pos);
+ ShiftAff = ShiftAff.set_constant_si(Amount);
+ return Identity.set_aff(Pos, ShiftAff);
+}
+
+/// Construct a map that swaps two nested tuples.
+///
+/// @param FromSpace1 { Space1[] }
+/// @param FromSpace2 { Space2[] }
+///
+/// @return { [Space1[] -> Space2[]] -> [Space2[] -> Space1[]] }
+isl::basic_map makeTupleSwapBasicMap(isl::space FromSpace1,
+ isl::space FromSpace2) {
+ // Fast-path on out-of-quota.
+ if (FromSpace1.is_null() || FromSpace2.is_null())
+ return {};
+
+ assert(FromSpace1.is_set());
+ assert(FromSpace2.is_set());
+
+ unsigned Dims1 = unsignedFromIslSize(FromSpace1.dim(isl::dim::set));
+ unsigned Dims2 = unsignedFromIslSize(FromSpace2.dim(isl::dim::set));
+
+ isl::space FromSpace =
+ FromSpace1.map_from_domain_and_range(FromSpace2).wrap();
+ isl::space ToSpace = FromSpace2.map_from_domain_and_range(FromSpace1).wrap();
+ isl::space MapSpace = FromSpace.map_from_domain_and_range(ToSpace);
+
+ isl::basic_map Result = isl::basic_map::universe(MapSpace);
+ for (unsigned i = 0u; i < Dims1; i += 1)
+ Result = Result.equate(isl::dim::in, i, isl::dim::out, Dims2 + i);
+ for (unsigned i = 0u; i < Dims2; i += 1) {
+ Result = Result.equate(isl::dim::in, Dims1 + i, isl::dim::out, i);
+ }
+
+ return Result;
+}
+
+/// Like makeTupleSwapBasicMap(isl::space,isl::space), but returns
+/// an isl_map.
+isl::map makeTupleSwapMap(isl::space FromSpace1, isl::space FromSpace2) {
+ isl::basic_map BMapResult = makeTupleSwapBasicMap(FromSpace1, FromSpace2);
+ return isl::map(BMapResult);
+}
+} // anonymous namespace
+
+isl::map polly::beforeScatter(isl::map Map, bool Strict) {
+ isl::space RangeSpace = Map.get_space().range();
+ isl::map ScatterRel =
+ Strict ? isl::map::lex_gt(RangeSpace) : isl::map::lex_ge(RangeSpace);
+ return Map.apply_range(ScatterRel);
+}
+
+isl::union_map polly::beforeScatter(isl::union_map UMap, bool Strict) {
+ isl::union_map Result = isl::union_map::empty(UMap.ctx());
+
+ for (isl::map Map : UMap.get_map_list()) {
+ isl::map After = beforeScatter(Map, Strict);
+ Result = Result.unite(After);
+ }
+
+ return Result;
+}
+
+isl::map polly::afterScatter(isl::map Map, bool Strict) {
+ isl::space RangeSpace = Map.get_space().range();
+ isl::map ScatterRel =
+ Strict ? isl::map::lex_lt(RangeSpace) : isl::map::lex_le(RangeSpace);
+ return Map.apply_range(ScatterRel);
+}
+
+isl::union_map polly::afterScatter(const isl::union_map &UMap, bool Strict) {
+ isl::union_map Result = isl::union_map::empty(UMap.ctx());
+ for (isl::map Map : UMap.get_map_list()) {
+ isl::map After = afterScatter(Map, Strict);
+ Result = Result.unite(After);
+ }
+ return Result;
+}
+
+isl::map polly::betweenScatter(isl::map From, isl::map To, bool InclFrom,
+ bool InclTo) {
+ isl::map AfterFrom = afterScatter(From, !InclFrom);
+ isl::map BeforeTo = beforeScatter(To, !InclTo);
+
+ return AfterFrom.intersect(BeforeTo);
+}
+
+isl::union_map polly::betweenScatter(isl::union_map From, isl::union_map To,
+ bool InclFrom, bool InclTo) {
+ isl::union_map AfterFrom = afterScatter(From, !InclFrom);
+ isl::union_map BeforeTo = beforeScatter(To, !InclTo);
+
+ return AfterFrom.intersect(BeforeTo);
+}
+
+isl::map polly::singleton(isl::union_map UMap, isl::space ExpectedSpace) {
+ if (UMap.is_null())
+ return {};
+
+ if (isl_union_map_n_map(UMap.get()) == 0)
+ return isl::map::empty(ExpectedSpace);
+
+ isl::map Result = isl::map::from_union_map(UMap);
+ assert(Result.is_null() ||
+ Result.get_space().has_equal_tuples(ExpectedSpace));
+
+ return Result;
+}
+
+isl::set polly::singleton(isl::union_set USet, isl::space ExpectedSpace) {
+ if (USet.is_null())
+ return {};
+
+ if (isl_union_set_n_set(USet.get()) == 0)
+ return isl::set::empty(ExpectedSpace);
+
+ isl::set Result(USet);
+ assert(Result.is_null() ||
+ Result.get_space().has_equal_tuples(ExpectedSpace));
+
+ return Result;
+}
+
+unsigned polly::getNumScatterDims(const isl::union_map &Schedule) {
+ unsigned Dims = 0;
+ for (isl::map Map : Schedule.get_map_list()) {
+ if (Map.is_null())
+ continue;
+
+ Dims = std::max(Dims, unsignedFromIslSize(Map.range_tuple_dim()));
+ }
+ return Dims;
+}
+
+isl::space polly::getScatterSpace(const isl::union_map &Schedule) {
+ if (Schedule.is_null())
+ return {};
+ unsigned Dims = getNumScatterDims(Schedule);
+ isl::space ScatterSpace = Schedule.get_space().set_from_params();
+ return ScatterSpace.add_dims(isl::dim::set, Dims);
+}
+
+isl::map polly::makeIdentityMap(const isl::set &Set, bool RestrictDomain) {
+ isl::map Result = isl::map::identity(Set.get_space().map_from_set());
+ if (RestrictDomain)
+ Result = Result.intersect_domain(Set);
+ return Result;
+}
+
+isl::union_map polly::makeIdentityMap(const isl::union_set &USet,
+ bool RestrictDomain) {
+ isl::union_map Result = isl::union_map::empty(USet.ctx());
+ for (isl::set Set : USet.get_set_list()) {
+ isl::map IdentityMap = makeIdentityMap(Set, RestrictDomain);
+ Result = Result.unite(IdentityMap);
+ }
+ return Result;
+}
+
+isl::map polly::reverseDomain(isl::map Map) {
+ isl::space DomSpace = Map.get_space().domain().unwrap();
+ isl::space Space1 = DomSpace.domain();
+ isl::space Space2 = DomSpace.range();
+ isl::map Swap = makeTupleSwapMap(Space1, Space2);
+ return Map.apply_domain(Swap);
+}
+
+isl::union_map polly::reverseDomain(const isl::union_map &UMap) {
+ isl::union_map Result = isl::union_map::empty(UMap.ctx());
+ for (isl::map Map : UMap.get_map_list()) {
+ auto Reversed = reverseDomain(std::move(Map));
+ Result = Result.unite(Reversed);
+ }
+ return Result;
+}
+
+isl::set polly::shiftDim(isl::set Set, int Pos, int Amount) {
+ unsigned NumDims = unsignedFromIslSize(Set.tuple_dim());
+ if (Pos < 0)
+ Pos = NumDims + Pos;
+ assert(unsigned(Pos) < NumDims && "Dimension index must be in range");
+ isl::space Space = Set.get_space();
+ Space = Space.map_from_domain_and_range(Space);
+ isl::multi_aff Translator = makeShiftDimAff(Space, Pos, Amount);
+ isl::map TranslatorMap = isl::map::from_multi_aff(Translator);
+ return Set.apply(TranslatorMap);
+}
+
+isl::union_set polly::shiftDim(isl::union_set USet, int Pos, int Amount) {
+ isl::union_set Result = isl::union_set::empty(USet.ctx());
+ for (isl::set Set : USet.get_set_list()) {
+ isl::set Shifted = shiftDim(Set, Pos, Amount);
+ Result = Result.unite(Shifted);
+ }
+ return Result;
+}
+
+isl::map polly::shiftDim(isl::map Map, isl::dim Dim, int Pos, int Amount) {
+ unsigned NumDims = unsignedFromIslSize(Map.dim(Dim));
+ if (Pos < 0)
+ Pos = NumDims + Pos;
+ assert(unsigned(Pos) < NumDims && "Dimension index must be in range");
+ isl::space Space = Map.get_space();
+ switch (Dim) {
+ case isl::dim::in:
+ Space = Space.domain();
+ break;
+ case isl::dim::out:
+ Space = Space.range();
+ break;
+ default:
+ llvm_unreachable("Unsupported value for 'dim'");
+ }
+ Space = Space.map_from_domain_and_range(Space);
+ isl::multi_aff Translator = makeShiftDimAff(Space, Pos, Amount);
+ isl::map TranslatorMap = isl::map::from_multi_aff(Translator);
+ switch (Dim) {
+ case isl::dim::in:
+ return Map.apply_domain(TranslatorMap);
+ case isl::dim::out:
+ return Map.apply_range(TranslatorMap);
+ default:
+ llvm_unreachable("Unsupported value for 'dim'");
+ }
+}
+
+isl::union_map polly::shiftDim(isl::union_map UMap, isl::dim Dim, int Pos,
+ int Amount) {
+ isl::union_map Result = isl::union_map::empty(UMap.ctx());
+
+ for (isl::map Map : UMap.get_map_list()) {
+ isl::map Shifted = shiftDim(Map, Dim, Pos, Amount);
+ Result = Result.unite(Shifted);
+ }
+ return Result;
+}
+
+void polly::simplify(isl::set &Set) {
+ Set = isl::manage(isl_set_compute_divs(Set.copy()));
+ Set = Set.detect_equalities();
+ Set = Set.coalesce();
+}
+
+void polly::simplify(isl::union_set &USet) {
+ USet = isl::manage(isl_union_set_compute_divs(USet.copy()));
+ USet = USet.detect_equalities();
+ USet = USet.coalesce();
+}
+
+void polly::simplify(isl::map &Map) {
+ Map = isl::manage(isl_map_compute_divs(Map.copy()));
+ Map = Map.detect_equalities();
+ Map = Map.coalesce();
+}
+
+void polly::simplify(isl::union_map &UMap) {
+ UMap = isl::manage(isl_union_map_compute_divs(UMap.copy()));
+ UMap = UMap.detect_equalities();
+ UMap = UMap.coalesce();
+}
+
+isl::union_map polly::computeReachingWrite(isl::union_map Schedule,
+ isl::union_map Writes, bool Reverse,
+ bool InclPrevDef, bool InclNextDef) {
+
+ // { Scatter[] }
+ isl::space ScatterSpace = getScatterSpace(Schedule);
+
+ // { ScatterRead[] -> ScatterWrite[] }
+ isl::map Relation;
+ if (Reverse)
+ Relation = InclPrevDef ? isl::map::lex_lt(ScatterSpace)
+ : isl::map::lex_le(ScatterSpace);
+ else
+ Relation = InclNextDef ? isl::map::lex_gt(ScatterSpace)
+ : isl::map::lex_ge(ScatterSpace);
+
+ // { ScatterWrite[] -> [ScatterRead[] -> ScatterWrite[]] }
+ isl::map RelationMap = Relation.range_map().reverse();
+
+ // { Element[] -> ScatterWrite[] }
+ isl::union_map WriteAction = Schedule.apply_domain(Writes);
+
+ // { ScatterWrite[] -> Element[] }
+ isl::union_map WriteActionRev = WriteAction.reverse();
+
+ // { Element[] -> [ScatterUse[] -> ScatterWrite[]] }
+ isl::union_map DefSchedRelation =
+ isl::union_map(RelationMap).apply_domain(WriteActionRev);
+
+ // For each element, at every point in time, map to the times of previous
+ // definitions. { [Element[] -> ScatterRead[]] -> ScatterWrite[] }
+ isl::union_map ReachableWrites = DefSchedRelation.uncurry();
+ if (Reverse)
+ ReachableWrites = ReachableWrites.lexmin();
+ else
+ ReachableWrites = ReachableWrites.lexmax();
+
+ // { [Element[] -> ScatterWrite[]] -> ScatterWrite[] }
+ isl::union_map SelfUse = WriteAction.range_map();
+
+ if (InclPrevDef && InclNextDef) {
+ // Add the Def itself to the solution.
+ ReachableWrites = ReachableWrites.unite(SelfUse).coalesce();
+ } else if (!InclPrevDef && !InclNextDef) {
+ // Remove Def itself from the solution.
+ ReachableWrites = ReachableWrites.subtract(SelfUse);
+ }
+
+ // { [Element[] -> ScatterRead[]] -> Domain[] }
+ return ReachableWrites.apply_range(Schedule.reverse());
+}
+
+isl::union_map
+polly::computeArrayUnused(isl::union_map Schedule, isl::union_map Writes,
+ isl::union_map Reads, bool ReadEltInSameInst,
+ bool IncludeLastRead, bool IncludeWrite) {
+ // { Element[] -> Scatter[] }
+ isl::union_map ReadActions = Schedule.apply_domain(Reads);
+ isl::union_map WriteActions = Schedule.apply_domain(Writes);
+
+ // { [Element[] -> DomainWrite[]] -> Scatter[] }
+ isl::union_map EltDomWrites =
+ Writes.reverse().range_map().apply_range(Schedule);
+
+ // { [Element[] -> Scatter[]] -> DomainWrite[] }
+ isl::union_map ReachingOverwrite = computeReachingWrite(
+ Schedule, Writes, true, ReadEltInSameInst, !ReadEltInSameInst);
+
+ // { [Element[] -> Scatter[]] -> DomainWrite[] }
+ isl::union_map ReadsOverwritten =
+ ReachingOverwrite.intersect_domain(ReadActions.wrap());
+
+ // { [Element[] -> DomainWrite[]] -> Scatter[] }
+ isl::union_map ReadsOverwrittenRotated =
+ reverseDomain(ReadsOverwritten).curry().reverse();
+ isl::union_map LastOverwrittenRead = ReadsOverwrittenRotated.lexmax();
+
+ // { [Element[] -> DomainWrite[]] -> Scatter[] }
+ isl::union_map BetweenLastReadOverwrite = betweenScatter(
+ LastOverwrittenRead, EltDomWrites, IncludeLastRead, IncludeWrite);
+
+ // { [Element[] -> Scatter[]] -> DomainWrite[] }
+ isl::union_map ReachingOverwriteZone = computeReachingWrite(
+ Schedule, Writes, true, IncludeLastRead, IncludeWrite);
+
+ // { [Element[] -> DomainWrite[]] -> Scatter[] }
+ isl::union_map ReachingOverwriteRotated =
+ reverseDomain(ReachingOverwriteZone).curry().reverse();
+
+ // { [Element[] -> DomainWrite[]] -> Scatter[] }
+ isl::union_map WritesWithoutReads = ReachingOverwriteRotated.subtract_domain(
+ ReadsOverwrittenRotated.domain());
+
+ return BetweenLastReadOverwrite.unite(WritesWithoutReads)
+ .domain_factor_domain();
+}
+
+isl::union_set polly::convertZoneToTimepoints(isl::union_set Zone,
+ bool InclStart, bool InclEnd) {
+ if (!InclStart && InclEnd)
+ return Zone;
+
+ auto ShiftedZone = shiftDim(Zone, -1, -1);
+ if (InclStart && !InclEnd)
+ return ShiftedZone;
+ else if (!InclStart && !InclEnd)
+ return Zone.intersect(ShiftedZone);
+
+ assert(InclStart && InclEnd);
+ return Zone.unite(ShiftedZone);
+}
+
+isl::union_map polly::convertZoneToTimepoints(isl::union_map Zone, isl::dim Dim,
+ bool InclStart, bool InclEnd) {
+ if (!InclStart && InclEnd)
+ return Zone;
+
+ auto ShiftedZone = shiftDim(Zone, Dim, -1, -1);
+ if (InclStart && !InclEnd)
+ return ShiftedZone;
+ else if (!InclStart && !InclEnd)
+ return Zone.intersect(ShiftedZone);
+
+ assert(InclStart && InclEnd);
+ return Zone.unite(ShiftedZone);
+}
+
+isl::map polly::convertZoneToTimepoints(isl::map Zone, isl::dim Dim,
+ bool InclStart, bool InclEnd) {
+ if (!InclStart && InclEnd)
+ return Zone;
+
+ auto ShiftedZone = shiftDim(Zone, Dim, -1, -1);
+ if (InclStart && !InclEnd)
+ return ShiftedZone;
+ else if (!InclStart && !InclEnd)
+ return Zone.intersect(ShiftedZone);
+
+ assert(InclStart && InclEnd);
+ return Zone.unite(ShiftedZone);
+}
+
+isl::map polly::distributeDomain(isl::map Map) {
+ // Note that we cannot take Map apart into { Domain[] -> Range1[] } and {
+ // Domain[] -> Range2[] } and combine again. We would loose any relation
+ // between Range1[] and Range2[] that is not also a constraint to Domain[].
+
+ isl::space Space = Map.get_space();
+ isl::space DomainSpace = Space.domain();
+ if (DomainSpace.is_null())
+ return {};
+ unsigned DomainDims = unsignedFromIslSize(DomainSpace.dim(isl::dim::set));
+ isl::space RangeSpace = Space.range().unwrap();
+ isl::space Range1Space = RangeSpace.domain();
+ if (Range1Space.is_null())
+ return {};
+ unsigned Range1Dims = unsignedFromIslSize(Range1Space.dim(isl::dim::set));
+ isl::space Range2Space = RangeSpace.range();
+ if (Range2Space.is_null())
+ return {};
+ unsigned Range2Dims = unsignedFromIslSize(Range2Space.dim(isl::dim::set));
+
+ isl::space OutputSpace =
+ DomainSpace.map_from_domain_and_range(Range1Space)
+ .wrap()
+ .map_from_domain_and_range(
+ DomainSpace.map_from_domain_and_range(Range2Space).wrap());
+
+ isl::basic_map Translator = isl::basic_map::universe(
+ Space.wrap().map_from_domain_and_range(OutputSpace.wrap()));
+
+ for (unsigned i = 0; i < DomainDims; i += 1) {
+ Translator = Translator.equate(isl::dim::in, i, isl::dim::out, i);
+ Translator = Translator.equate(isl::dim::in, i, isl::dim::out,
+ DomainDims + Range1Dims + i);
+ }
+ for (unsigned i = 0; i < Range1Dims; i += 1)
+ Translator = Translator.equate(isl::dim::in, DomainDims + i, isl::dim::out,
+ DomainDims + i);
+ for (unsigned i = 0; i < Range2Dims; i += 1)
+ Translator = Translator.equate(isl::dim::in, DomainDims + Range1Dims + i,
+ isl::dim::out,
+ DomainDims + Range1Dims + DomainDims + i);
+
+ return Map.wrap().apply(Translator).unwrap();
+}
+
+isl::union_map polly::distributeDomain(isl::union_map UMap) {
+ isl::union_map Result = isl::union_map::empty(UMap.ctx());
+ for (isl::map Map : UMap.get_map_list()) {
+ auto Distributed = distributeDomain(Map);
+ Result = Result.unite(Distributed);
+ }
+ return Result;
+}
+
+isl::union_map polly::liftDomains(isl::union_map UMap, isl::union_set Factor) {
+
+ // { Factor[] -> Factor[] }
+ isl::union_map Factors = makeIdentityMap(Factor, true);
+
+ return Factors.product(UMap);
+}
+
+isl::union_map polly::applyDomainRange(isl::union_map UMap,
+ isl::union_map Func) {
+ // This implementation creates unnecessary cross products of the
+ // DomainDomain[] and Func. An alternative implementation could reverse
+ // domain+uncurry,apply Func to what now is the domain, then undo the
+ // preparing transformation. Another alternative implementation could create a
+ // translator map for each piece.
+
+ // { DomainDomain[] }
+ isl::union_set DomainDomain = UMap.domain().unwrap().domain();
+
+ // { [DomainDomain[] -> DomainRange[]] -> [DomainDomain[] -> NewDomainRange[]]
+ // }
+ isl::union_map LifetedFunc = liftDomains(std::move(Func), DomainDomain);
+
+ return UMap.apply_domain(LifetedFunc);
+}
+
+isl::map polly::intersectRange(isl::map Map, isl::union_set Range) {
+ isl::set RangeSet = Range.extract_set(Map.get_space().range());
+ return Map.intersect_range(RangeSet);
+}
+
+isl::map polly::subtractParams(isl::map Map, isl::set Params) {
+ auto MapSpace = Map.get_space();
+ auto ParamsMap = isl::map::universe(MapSpace).intersect_params(Params);
+ return Map.subtract(ParamsMap);
+}
+
+isl::set polly::subtractParams(isl::set Set, isl::set Params) {
+ isl::space SetSpace = Set.get_space();
+ isl::set ParamsSet = isl::set::universe(SetSpace).intersect_params(Params);
+ return Set.subtract(ParamsSet);
+}
+
+isl::val polly::getConstant(isl::pw_aff PwAff, bool Max, bool Min) {
+ assert(!Max || !Min); // Cannot return min and max at the same time.
+ isl::val Result;
+ isl::stat Stat = PwAff.foreach_piece(
+ [=, &Result](isl::set Set, isl::aff Aff) -> isl::stat {
+ if (!Result.is_null() && Result.is_nan())
+ return isl::stat::ok();
+
+ // TODO: If Min/Max, we can also determine a minimum/maximum value if
+ // Set is constant-bounded.
+ if (!Aff.is_cst()) {
+ Result = isl::val::nan(Aff.ctx());
+ return isl::stat::error();
+ }
+
+ isl::val ThisVal = Aff.get_constant_val();
+ if (Result.is_null()) {
+ Result = ThisVal;
+ return isl::stat::ok();
+ }
+
+ if (Result.eq(ThisVal))
+ return isl::stat::ok();
+
+ if (Max && ThisVal.gt(Result)) {
+ Result = ThisVal;
+ return isl::stat::ok();
+ }
+
+ if (Min && ThisVal.lt(Result)) {
+ Result = ThisVal;
+ return isl::stat::ok();
+ }
+
+ // Not compatible
+ Result = isl::val::nan(Aff.ctx());
+ return isl::stat::error();
+ });
+
+ if (Stat.is_error())
+ return {};
+
+ return Result;
+}
+
+llvm::iota_range<unsigned> polly::rangeIslSize(unsigned Begin, isl::size End) {
+ unsigned UEnd = unsignedFromIslSize(End);
+ return llvm::seq<unsigned>(std::min(Begin, UEnd), UEnd);
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+static void foreachPoint(const isl::set &Set,
+ const std::function<void(isl::point P)> &F) {
+ Set.foreach_point([&](isl::point P) -> isl::stat {
+ F(P);
+ return isl::stat::ok();
+ });
+}
+
+static void foreachPoint(isl::basic_set BSet,
+ const std::function<void(isl::point P)> &F) {
+ foreachPoint(isl::set(BSet), F);
+}
+
+/// Determine the sorting order of the sets @p A and @p B without considering
+/// the space structure.
+///
+/// Ordering is based on the lower bounds of the set's dimensions. First
+/// dimensions are considered first.
+static int flatCompare(const isl::basic_set &A, const isl::basic_set &B) {
+ // Quick bail-out on out-of-quota.
+ if (A.is_null() || B.is_null())
+ return 0;
+
+ unsigned ALen = unsignedFromIslSize(A.dim(isl::dim::set));
+ unsigned BLen = unsignedFromIslSize(B.dim(isl::dim::set));
+ unsigned Len = std::min(ALen, BLen);
+
+ for (unsigned i = 0; i < Len; i += 1) {
+ isl::basic_set ADim =
+ A.project_out(isl::dim::param, 0,
+ unsignedFromIslSize(A.dim(isl::dim::param)))
+ .project_out(isl::dim::set, i + 1, ALen - i - 1)
+ .project_out(isl::dim::set, 0, i);
+ isl::basic_set BDim =
+ B.project_out(isl::dim::param, 0,
+ unsignedFromIslSize(B.dim(isl::dim::param)))
+ .project_out(isl::dim::set, i + 1, BLen - i - 1)
+ .project_out(isl::dim::set, 0, i);
+
+ isl::basic_set AHull = isl::set(ADim).convex_hull();
+ isl::basic_set BHull = isl::set(BDim).convex_hull();
+
+ bool ALowerBounded =
+ bool(isl::set(AHull).dim_has_any_lower_bound(isl::dim::set, 0));
+ bool BLowerBounded =
+ bool(isl::set(BHull).dim_has_any_lower_bound(isl::dim::set, 0));
+
+ int BoundedCompare = BLowerBounded - ALowerBounded;
+ if (BoundedCompare != 0)
+ return BoundedCompare;
+
+ if (!ALowerBounded || !BLowerBounded)
+ continue;
+
+ isl::pw_aff AMin = isl::set(ADim).dim_min(0);
+ isl::pw_aff BMin = isl::set(BDim).dim_min(0);
+
+ isl::val AMinVal = polly::getConstant(AMin, false, true);
+ isl::val BMinVal = polly::getConstant(BMin, false, true);
+
+ int MinCompare = AMinVal.sub(BMinVal).sgn();
+ if (MinCompare != 0)
+ return MinCompare;
+ }
+
+ // If all the dimensions' lower bounds are equal or incomparable, sort based
+ // on the number of dimensions.
+ return ALen - BLen;
+}
+
+/// Compare the sets @p A and @p B according to their nested space structure.
+/// Returns 0 if the structure is considered equal.
+/// If @p ConsiderTupleLen is false, the number of dimensions in a tuple are
+/// ignored, i.e. a tuple with the same name but different number of dimensions
+/// are considered equal.
+static int structureCompare(const isl::space &ASpace, const isl::space &BSpace,
+ bool ConsiderTupleLen) {
+ int WrappingCompare = bool(ASpace.is_wrapping()) - bool(BSpace.is_wrapping());
+ if (WrappingCompare != 0)
+ return WrappingCompare;
+
+ if (ASpace.is_wrapping() && BSpace.is_wrapping()) {
+ isl::space AMap = ASpace.unwrap();
+ isl::space BMap = BSpace.unwrap();
+
+ int FirstResult =
+ structureCompare(AMap.domain(), BMap.domain(), ConsiderTupleLen);
+ if (FirstResult != 0)
+ return FirstResult;
+
+ return structureCompare(AMap.range(), BMap.range(), ConsiderTupleLen);
+ }
+
+ std::string AName;
+ if (!ASpace.is_params() && ASpace.has_tuple_name(isl::dim::set))
+ AName = ASpace.get_tuple_name(isl::dim::set);
+
+ std::string BName;
+ if (!BSpace.is_params() && BSpace.has_tuple_name(isl::dim::set))
+ BName = BSpace.get_tuple_name(isl::dim::set);
+
+ int NameCompare = AName.compare(BName);
+ if (NameCompare != 0)
+ return NameCompare;
+
+ if (ConsiderTupleLen) {
+ int LenCompare = (int)unsignedFromIslSize(BSpace.dim(isl::dim::set)) -
+ (int)unsignedFromIslSize(ASpace.dim(isl::dim::set));
+ if (LenCompare != 0)
+ return LenCompare;
+ }
+
+ return 0;
+}
+
+/// Compare the sets @p A and @p B according to their nested space structure. If
+/// the structure is the same, sort using the dimension lower bounds.
+/// Returns an std::sort compatible bool.
+static bool orderComparer(const isl::basic_set &A, const isl::basic_set &B) {
+ isl::space ASpace = A.get_space();
+ isl::space BSpace = B.get_space();
+
+ // Ignoring number of dimensions first ensures that structures with same tuple
+ // names, but different number of dimensions are still sorted close together.
+ int TupleNestingCompare = structureCompare(ASpace, BSpace, false);
+ if (TupleNestingCompare != 0)
+ return TupleNestingCompare < 0;
+
+ int TupleCompare = structureCompare(ASpace, BSpace, true);
+ if (TupleCompare != 0)
+ return TupleCompare < 0;
+
+ return flatCompare(A, B) < 0;
+}
+
+/// Print a string representation of @p USet to @p OS.
+///
+/// The pieces of @p USet are printed in a sorted order. Spaces with equal or
+/// similar nesting structure are printed together. Compared to isl's own
+/// printing function the uses the structure itself as base of the sorting, not
+/// a hash of it. It ensures that e.g. maps spaces with same domain structure
+/// are printed together. Set pieces with same structure are printed in order of
+/// their lower bounds.
+///
+/// @param USet Polyhedra to print.
+/// @param OS Target stream.
+/// @param Simplify Whether to simplify the polyhedron before printing.
+/// @param IsMap Whether @p USet is a wrapped map. If true, sets are
+/// unwrapped before printing to again appear as a map.
+static void printSortedPolyhedra(isl::union_set USet, llvm::raw_ostream &OS,
+ bool Simplify, bool IsMap) {
+ if (USet.is_null()) {
+ OS << "<null>\n";
+ return;
+ }
+
+ if (Simplify)
+ simplify(USet);
+
+ // Get all the polyhedra.
+ std::vector<isl::basic_set> BSets;
+
+ for (isl::set Set : USet.get_set_list()) {
+ for (isl::basic_set BSet : Set.get_basic_set_list()) {
+ BSets.push_back(BSet);
+ }
+ }
+
+ if (BSets.empty()) {
+ OS << "{\n}\n";
+ return;
+ }
+
+ // Sort the polyhedra.
+ llvm::sort(BSets, orderComparer);
+
+ // Print the polyhedra.
+ bool First = true;
+ for (const isl::basic_set &BSet : BSets) {
+ std::string Str;
+ if (IsMap)
+ Str = stringFromIslObj(isl::map(BSet.unwrap()));
+ else
+ Str = stringFromIslObj(isl::set(BSet));
+ size_t OpenPos = Str.find_first_of('{');
+ assert(OpenPos != std::string::npos);
+ size_t ClosePos = Str.find_last_of('}');
+ assert(ClosePos != std::string::npos);
+
+ if (First)
+ OS << llvm::StringRef(Str).substr(0, OpenPos + 1) << "\n ";
+ else
+ OS << ";\n ";
+
+ OS << llvm::StringRef(Str).substr(OpenPos + 1, ClosePos - OpenPos - 2);
+ First = false;
+ }
+ assert(!First);
+ OS << "\n}\n";
+}
+
+static void recursiveExpand(isl::basic_set BSet, unsigned Dim,
+ isl::set &Expanded) {
+ unsigned Dims = unsignedFromIslSize(BSet.dim(isl::dim::set));
+ if (Dim >= Dims) {
+ Expanded = Expanded.unite(BSet);
+ return;
+ }
+
+ isl::basic_set DimOnly =
+ BSet.project_out(isl::dim::param, 0,
+ unsignedFromIslSize(BSet.dim(isl::dim::param)))
+ .project_out(isl::dim::set, Dim + 1, Dims - Dim - 1)
+ .project_out(isl::dim::set, 0, Dim);
+ if (!DimOnly.is_bounded()) {
+ recursiveExpand(BSet, Dim + 1, Expanded);
+ return;
+ }
+
+ foreachPoint(DimOnly, [&, Dim](isl::point P) {
+ isl::val Val = P.get_coordinate_val(isl::dim::set, 0);
+ isl::basic_set FixBSet = BSet.fix_val(isl::dim::set, Dim, Val);
+ recursiveExpand(FixBSet, Dim + 1, Expanded);
+ });
+}
+
+/// Make each point of a set explicit.
+///
+/// "Expanding" makes each point a set contains explicit. That is, the result is
+/// a set of singleton polyhedra. Unbounded dimensions are not expanded.
+///
+/// Example:
+/// { [i] : 0 <= i < 2 }
+/// is expanded to:
+/// { [0]; [1] }
+static isl::set expand(const isl::set &Set) {
+ isl::set Expanded = isl::set::empty(Set.get_space());
+ for (isl::basic_set BSet : Set.get_basic_set_list())
+ recursiveExpand(BSet, 0, Expanded);
+ return Expanded;
+}
+
+/// Expand all points of a union set explicit.
+///
+/// @see expand(const isl::set)
+static isl::union_set expand(const isl::union_set &USet) {
+ isl::union_set Expanded = isl::union_set::empty(USet.ctx());
+ for (isl::set Set : USet.get_set_list()) {
+ isl::set SetExpanded = expand(Set);
+ Expanded = Expanded.unite(SetExpanded);
+ }
+ return Expanded;
+}
+
+LLVM_DUMP_METHOD void polly::dumpPw(const isl::set &Set) {
+ printSortedPolyhedra(Set, llvm::errs(), true, false);
+}
+
+LLVM_DUMP_METHOD void polly::dumpPw(const isl::map &Map) {
+ printSortedPolyhedra(Map.wrap(), llvm::errs(), true, true);
+}
+
+LLVM_DUMP_METHOD void polly::dumpPw(const isl::union_set &USet) {
+ printSortedPolyhedra(USet, llvm::errs(), true, false);
+}
+
+LLVM_DUMP_METHOD void polly::dumpPw(const isl::union_map &UMap) {
+ printSortedPolyhedra(UMap.wrap(), llvm::errs(), true, true);
+}
+
+LLVM_DUMP_METHOD void polly::dumpPw(__isl_keep isl_set *Set) {
+ dumpPw(isl::manage_copy(Set));
+}
+
+LLVM_DUMP_METHOD void polly::dumpPw(__isl_keep isl_map *Map) {
+ dumpPw(isl::manage_copy(Map));
+}
+
+LLVM_DUMP_METHOD void polly::dumpPw(__isl_keep isl_union_set *USet) {
+ dumpPw(isl::manage_copy(USet));
+}
+
+LLVM_DUMP_METHOD void polly::dumpPw(__isl_keep isl_union_map *UMap) {
+ dumpPw(isl::manage_copy(UMap));
+}
+
+LLVM_DUMP_METHOD void polly::dumpExpanded(const isl::set &Set) {
+ printSortedPolyhedra(expand(Set), llvm::errs(), false, false);
+}
+
+LLVM_DUMP_METHOD void polly::dumpExpanded(const isl::map &Map) {
+ printSortedPolyhedra(expand(Map.wrap()), llvm::errs(), false, true);
+}
+
+LLVM_DUMP_METHOD void polly::dumpExpanded(const isl::union_set &USet) {
+ printSortedPolyhedra(expand(USet), llvm::errs(), false, false);
+}
+
+LLVM_DUMP_METHOD void polly::dumpExpanded(const isl::union_map &UMap) {
+ printSortedPolyhedra(expand(UMap.wrap()), llvm::errs(), false, true);
+}
+
+LLVM_DUMP_METHOD void polly::dumpExpanded(__isl_keep isl_set *Set) {
+ dumpExpanded(isl::manage_copy(Set));
+}
+
+LLVM_DUMP_METHOD void polly::dumpExpanded(__isl_keep isl_map *Map) {
+ dumpExpanded(isl::manage_copy(Map));
+}
+
+LLVM_DUMP_METHOD void polly::dumpExpanded(__isl_keep isl_union_set *USet) {
+ dumpExpanded(isl::manage_copy(USet));
+}
+
+LLVM_DUMP_METHOD void polly::dumpExpanded(__isl_keep isl_union_map *UMap) {
+ dumpExpanded(isl::manage_copy(UMap));
+}
+#endif
diff --git a/contrib/libs/llvm14/tools/polly/lib/Support/PollyPasses.def b/contrib/libs/llvm14/tools/polly/lib/Support/PollyPasses.def
new file mode 100644
index 00000000000..03b3dc89569
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Support/PollyPasses.def
@@ -0,0 +1,42 @@
+#ifndef FUNCTION_ANALYSIS
+#define FUNCTION_ANALYSIS(NAME, CREATE_PASS)
+#endif
+FUNCTION_ANALYSIS("polly-detect", ScopAnalysis())
+FUNCTION_ANALYSIS("polly-function-scops", ScopInfoAnalysis())
+#undef FUNCTION_ANALYSIS
+
+#ifndef FUNCTION_PASS
+#define FUNCTION_PASS(NAME, CREATE_PASS)
+#endif
+FUNCTION_PASS("polly-prepare", CodePreparationPass())
+FUNCTION_PASS("print<polly-detect>", ScopAnalysisPrinterPass(errs()))
+FUNCTION_PASS("print<polly-function-scops>", ScopInfoPrinterPass(errs()))
+#undef FUNCTION_PASS
+
+#ifndef SCOP_ANALYSIS
+#define SCOP_ANALYSIS(NAME, CREATE_PASS)
+#endif
+SCOP_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis(PIC))
+SCOP_ANALYSIS("polly-ast", IslAstAnalysis())
+SCOP_ANALYSIS("polly-dependences", DependenceAnalysis())
+#undef SCOP_ANALYSIS
+
+#ifndef SCOP_PASS
+#define SCOP_PASS(NAME, CREATE_PASS)
+#endif
+SCOP_PASS("polly-export-jscop", JSONExportPass())
+SCOP_PASS("polly-import-jscop", JSONImportPass())
+SCOP_PASS("print<polly-ast>", IslAstPrinterPass(outs()))
+SCOP_PASS("print<polly-dependences>", DependenceInfoPrinterPass(outs()))
+SCOP_PASS("polly-codegen", CodeGenerationPass())
+SCOP_PASS("polly-simplify", SimplifyPass())
+SCOP_PASS("print<polly-simplify>", SimplifyPrinterPass(outs()))
+SCOP_PASS("polly-optree", ForwardOpTreePass())
+SCOP_PASS("print<polly-optree>", ForwardOpTreePrinterPass(outs()))
+SCOP_PASS("polly-delicm", DeLICMPass())
+SCOP_PASS("print<polly-delicm>", DeLICMPrinterPass(outs()))
+SCOP_PASS("polly-prune-unprofitable", PruneUnprofitablePass())
+SCOP_PASS("polly-opt-isl", IslScheduleOptimizerPass())
+SCOP_PASS("print<polly-opt-isl>", IslScheduleOptimizerPrinterPass(outs()))
+SCOP_PASS("polly-dce", DeadCodeElimPass())
+#undef SCOP_PASS
diff --git a/contrib/libs/llvm14/tools/polly/lib/Support/RegisterPasses.cpp b/contrib/libs/llvm14/tools/polly/lib/Support/RegisterPasses.cpp
new file mode 100644
index 00000000000..e546525e138
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Support/RegisterPasses.cpp
@@ -0,0 +1,840 @@
+//===------ RegisterPasses.cpp - Add the Polly Passes to default passes --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file composes the individual LLVM-IR passes provided by Polly to a
+// functional polyhedral optimizer. The polyhedral optimizer is automatically
+// made available to LLVM based compilers by loading the Polly shared library
+// into such a compiler.
+//
+// The Polly optimizer is made available by executing a static constructor that
+// registers the individual Polly passes in the LLVM pass manager builder. The
+// passes are registered such that the default behaviour of the compiler is not
+// changed, but that the flag '-polly' provided at optimization level '-O3'
+// enables additional polyhedral optimizations.
+//===----------------------------------------------------------------------===//
+
+#include "polly/RegisterPasses.h"
+#include "polly/Canonicalization.h"
+#include "polly/CodeGen/CodeGeneration.h"
+#include "polly/CodeGen/CodegenCleanup.h"
+#include "polly/CodeGen/IslAst.h"
+#include "polly/CodePreparation.h"
+#include "polly/DeLICM.h"
+#include "polly/DeadCodeElimination.h"
+#include "polly/DependenceInfo.h"
+#include "polly/ForwardOpTree.h"
+#include "polly/JSONExporter.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/PolyhedralInfo.h"
+#include "polly/PruneUnprofitable.h"
+#include "polly/ScheduleOptimizer.h"
+#include "polly/ScopDetection.h"
+#include "polly/ScopInfo.h"
+#include "polly/Simplify.h"
+#include "polly/Support/DumpFunctionPass.h"
+#include "polly/Support/DumpModulePass.h"
+#include "llvm/Analysis/CFGPrinter.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/PassPlugin.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+
+using namespace llvm;
+using namespace polly;
+
+cl::OptionCategory PollyCategory("Polly Options",
+ "Configure the polly loop optimizer");
+
+static cl::opt<bool>
+ PollyEnabled("polly",
+ cl::desc("Enable the polly optimizer (with -O1, -O2 or -O3)"),
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> PollyDetectOnly(
+ "polly-only-scop-detection",
+ cl::desc("Only run scop detection, but no other optimizations"),
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+enum PassPositionChoice {
+ POSITION_EARLY,
+ POSITION_AFTER_LOOPOPT,
+ POSITION_BEFORE_VECTORIZER
+};
+
+enum OptimizerChoice { OPTIMIZER_NONE, OPTIMIZER_ISL };
+
+static cl::opt<PassPositionChoice> PassPosition(
+ "polly-position", cl::desc("Where to run polly in the pass pipeline"),
+ cl::values(
+ clEnumValN(POSITION_EARLY, "early", "Before everything"),
+ clEnumValN(POSITION_AFTER_LOOPOPT, "after-loopopt",
+ "After the loop optimizer (but within the inline cycle)"),
+ clEnumValN(POSITION_BEFORE_VECTORIZER, "before-vectorizer",
+ "Right before the vectorizer")),
+ cl::Hidden, cl::init(POSITION_BEFORE_VECTORIZER), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<OptimizerChoice>
+ Optimizer("polly-optimizer", cl::desc("Select the scheduling optimizer"),
+ cl::values(clEnumValN(OPTIMIZER_NONE, "none", "No optimizer"),
+ clEnumValN(OPTIMIZER_ISL, "isl",
+ "The isl scheduling optimizer")),
+ cl::Hidden, cl::init(OPTIMIZER_ISL), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+enum CodeGenChoice { CODEGEN_FULL, CODEGEN_AST, CODEGEN_NONE };
+static cl::opt<CodeGenChoice> CodeGeneration(
+ "polly-code-generation", cl::desc("How much code-generation to perform"),
+ cl::values(clEnumValN(CODEGEN_FULL, "full", "AST and IR generation"),
+ clEnumValN(CODEGEN_AST, "ast", "Only AST generation"),
+ clEnumValN(CODEGEN_NONE, "none", "No code generation")),
+ cl::Hidden, cl::init(CODEGEN_FULL), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+enum TargetChoice { TARGET_CPU, TARGET_GPU, TARGET_HYBRID };
+static cl::opt<TargetChoice>
+ Target("polly-target", cl::desc("The hardware to target"),
+ cl::values(clEnumValN(TARGET_CPU, "cpu", "generate CPU code")
+#ifdef GPU_CODEGEN
+ ,
+ clEnumValN(TARGET_GPU, "gpu", "generate GPU code"),
+ clEnumValN(TARGET_HYBRID, "hybrid",
+ "generate GPU code (preferably) or CPU code")
+#endif
+ ),
+ cl::init(TARGET_CPU), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+VectorizerChoice polly::PollyVectorizerChoice;
+static cl::opt<polly::VectorizerChoice, true> Vectorizer(
+ "polly-vectorizer", cl::desc("Select the vectorization strategy"),
+ cl::values(
+ clEnumValN(polly::VECTORIZER_NONE, "none", "No Vectorization"),
+ clEnumValN(polly::VECTORIZER_POLLY, "polly",
+ "Polly internal vectorizer"),
+ clEnumValN(
+ polly::VECTORIZER_STRIPMINE, "stripmine",
+ "Strip-mine outer loops for the loop-vectorizer to trigger")),
+ cl::location(PollyVectorizerChoice), cl::init(polly::VECTORIZER_NONE),
+ cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> ImportJScop(
+ "polly-import",
+ cl::desc("Import the polyhedral description of the detected Scops"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> FullyIndexedStaticExpansion(
+ "polly-enable-mse",
+ cl::desc("Fully expand the memory accesses of the detected Scops"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> ExportJScop(
+ "polly-export",
+ cl::desc("Export the polyhedral description of the detected Scops"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> DeadCodeElim("polly-run-dce",
+ cl::desc("Run the dead code elimination"),
+ cl::Hidden, cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool> PollyViewer(
+ "polly-show",
+ cl::desc("Highlight the code regions that will be optimized in a "
+ "(CFG BBs and LLVM-IR instructions)"),
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> PollyOnlyViewer(
+ "polly-show-only",
+ cl::desc("Highlight the code regions that will be optimized in "
+ "a (CFG only BBs)"),
+ cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ PollyPrinter("polly-dot", cl::desc("Enable the Polly DOT printer in -O3"),
+ cl::Hidden, cl::value_desc("Run the Polly DOT printer at -O3"),
+ cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<bool> PollyOnlyPrinter(
+ "polly-dot-only",
+ cl::desc("Enable the Polly DOT printer in -O3 (no BB content)"), cl::Hidden,
+ cl::value_desc("Run the Polly DOT printer at -O3 (no BB content"),
+ cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ CFGPrinter("polly-view-cfg",
+ cl::desc("Show the Polly CFG right after code generation"),
+ cl::Hidden, cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ EnablePolyhedralInfo("polly-enable-polyhedralinfo",
+ cl::desc("Enable polyhedral interface of Polly"),
+ cl::Hidden, cl::init(false), cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ EnableForwardOpTree("polly-enable-optree",
+ cl::desc("Enable operand tree forwarding"), cl::Hidden,
+ cl::init(true), cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ DumpBefore("polly-dump-before",
+ cl::desc("Dump module before Polly transformations into a file "
+ "suffixed with \"-before\""),
+ cl::init(false), cl::cat(PollyCategory));
+
+static cl::list<std::string> DumpBeforeFile(
+ "polly-dump-before-file",
+ cl::desc("Dump module before Polly transformations to the given file"),
+ cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ DumpAfter("polly-dump-after",
+ cl::desc("Dump module after Polly transformations into a file "
+ "suffixed with \"-after\""),
+ cl::init(false), cl::cat(PollyCategory));
+
+static cl::list<std::string> DumpAfterFile(
+ "polly-dump-after-file",
+ cl::desc("Dump module after Polly transformations to the given file"),
+ cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ EnableDeLICM("polly-enable-delicm",
+ cl::desc("Eliminate scalar loop carried dependences"),
+ cl::Hidden, cl::init(true), cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ EnableSimplify("polly-enable-simplify",
+ cl::desc("Simplify SCoP after optimizations"),
+ cl::init(true), cl::cat(PollyCategory));
+
+static cl::opt<bool> EnablePruneUnprofitable(
+ "polly-enable-prune-unprofitable",
+ cl::desc("Bail out on unprofitable SCoPs before rescheduling"), cl::Hidden,
+ cl::init(true), cl::cat(PollyCategory));
+
+namespace {
+
+/// Initialize Polly passes when library is loaded.
+///
+/// We use the constructor of a statically declared object to initialize the
+/// different Polly passes right after the Polly library is loaded. This ensures
+/// that the Polly passes are available e.g. in the 'opt' tool.
+class StaticInitializer {
+public:
+ StaticInitializer() {
+ llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
+ polly::initializePollyPasses(Registry);
+ }
+};
+static StaticInitializer InitializeEverything;
+} // end of anonymous namespace.
+
+namespace polly {
+void initializePollyPasses(PassRegistry &Registry) {
+ initializeCodeGenerationPass(Registry);
+
+#ifdef GPU_CODEGEN
+ initializePPCGCodeGenerationPass(Registry);
+ initializeManagedMemoryRewritePassPass(Registry);
+ LLVMInitializeNVPTXTarget();
+ LLVMInitializeNVPTXTargetInfo();
+ LLVMInitializeNVPTXTargetMC();
+ LLVMInitializeNVPTXAsmPrinter();
+#endif
+ initializeCodePreparationPass(Registry);
+ initializeDeadCodeElimWrapperPassPass(Registry);
+ initializeDependenceInfoPass(Registry);
+ initializeDependenceInfoWrapperPassPass(Registry);
+ initializeJSONExporterPass(Registry);
+ initializeJSONImporterPass(Registry);
+ initializeMaximalStaticExpanderPass(Registry);
+ initializeIslAstInfoWrapperPassPass(Registry);
+ initializeIslScheduleOptimizerWrapperPassPass(Registry);
+ initializePollyCanonicalizePass(Registry);
+ initializePolyhedralInfoPass(Registry);
+ initializeScopDetectionWrapperPassPass(Registry);
+ initializeScopInlinerPass(Registry);
+ initializeScopInfoRegionPassPass(Registry);
+ initializeScopInfoWrapperPassPass(Registry);
+ initializeCodegenCleanupPass(Registry);
+ initializeFlattenSchedulePass(Registry);
+ initializeForwardOpTreeWrapperPassPass(Registry);
+ initializeDeLICMWrapperPassPass(Registry);
+ initializeSimplifyWrapperPassPass(Registry);
+ initializeDumpModuleWrapperPassPass(Registry);
+ initializePruneUnprofitableWrapperPassPass(Registry);
+}
+
+/// Register Polly passes such that they form a polyhedral optimizer.
+///
+/// The individual Polly passes are registered in the pass manager such that
+/// they form a full polyhedral optimizer. The flow of the optimizer starts with
+/// a set of preparing transformations that canonicalize the LLVM-IR such that
+/// the LLVM-IR is easier for us to understand and to optimizes. On the
+/// canonicalized LLVM-IR we first run the ScopDetection pass, which detects
+/// static control flow regions. Those regions are then translated by the
+/// ScopInfo pass into a polyhedral representation. As a next step, a scheduling
+/// optimizer is run on the polyhedral representation and finally the optimized
+/// polyhedral representation is code generated back to LLVM-IR.
+///
+/// Besides this core functionality, we optionally schedule passes that provide
+/// a graphical view of the scops (Polly[Only]Viewer, Polly[Only]Printer), that
+/// allow the export/import of the polyhedral representation
+/// (JSCON[Exporter|Importer]) or that show the cfg after code generation.
+///
+/// For certain parts of the Polly optimizer, several alternatives are provided:
+///
+/// As scheduling optimizer we support the isl scheduling optimizer
+/// (http://freecode.com/projects/isl).
+/// It is also possible to run Polly with no optimizer. This mode is mainly
+/// provided to analyze the run and compile time changes caused by the
+/// scheduling optimizer.
+///
+/// Polly supports the isl internal code generator.
+static void registerPollyPasses(llvm::legacy::PassManagerBase &PM,
+ bool EnableForOpt) {
+ if (DumpBefore)
+ PM.add(polly::createDumpModuleWrapperPass("-before", true));
+ for (auto &Filename : DumpBeforeFile)
+ PM.add(polly::createDumpModuleWrapperPass(Filename, false));
+
+ PM.add(polly::createCodePreparationPass());
+ PM.add(polly::createScopDetectionWrapperPassPass());
+
+ if (PollyDetectOnly)
+ return;
+
+ if (PollyViewer)
+ PM.add(polly::createDOTViewerPass());
+ if (PollyOnlyViewer)
+ PM.add(polly::createDOTOnlyViewerPass());
+ if (PollyPrinter)
+ PM.add(polly::createDOTPrinterPass());
+ if (PollyOnlyPrinter)
+ PM.add(polly::createDOTOnlyPrinterPass());
+
+ PM.add(polly::createScopInfoRegionPassPass());
+ if (EnablePolyhedralInfo)
+ PM.add(polly::createPolyhedralInfoPass());
+
+ if (EnableSimplify)
+ PM.add(polly::createSimplifyWrapperPass(0));
+ if (EnableForwardOpTree)
+ PM.add(polly::createForwardOpTreeWrapperPass());
+ if (EnableDeLICM)
+ PM.add(polly::createDeLICMWrapperPass());
+ if (EnableSimplify)
+ PM.add(polly::createSimplifyWrapperPass(1));
+
+ if (ImportJScop)
+ PM.add(polly::createJSONImporterPass());
+
+ if (DeadCodeElim)
+ PM.add(polly::createDeadCodeElimWrapperPass());
+
+ if (FullyIndexedStaticExpansion)
+ PM.add(polly::createMaximalStaticExpansionPass());
+
+ if (EnablePruneUnprofitable)
+ PM.add(polly::createPruneUnprofitableWrapperPass());
+
+#ifdef GPU_CODEGEN
+ if (Target == TARGET_HYBRID)
+ PM.add(
+ polly::createPPCGCodeGenerationPass(GPUArchChoice, GPURuntimeChoice));
+#endif
+ if (Target == TARGET_CPU || Target == TARGET_HYBRID)
+ switch (Optimizer) {
+ case OPTIMIZER_NONE:
+ break; /* Do nothing */
+
+ case OPTIMIZER_ISL:
+ PM.add(polly::createIslScheduleOptimizerWrapperPass());
+ break;
+ }
+
+ if (ExportJScop)
+ PM.add(polly::createJSONExporterPass());
+
+ if (!EnableForOpt)
+ return;
+
+ if (Target == TARGET_CPU || Target == TARGET_HYBRID)
+ switch (CodeGeneration) {
+ case CODEGEN_AST:
+ PM.add(polly::createIslAstInfoWrapperPassPass());
+ break;
+ case CODEGEN_FULL:
+ PM.add(polly::createCodeGenerationPass());
+ break;
+ case CODEGEN_NONE:
+ break;
+ }
+#ifdef GPU_CODEGEN
+ else {
+ PM.add(
+ polly::createPPCGCodeGenerationPass(GPUArchChoice, GPURuntimeChoice));
+ PM.add(polly::createManagedMemoryRewritePassPass());
+ }
+#endif
+
+#ifdef GPU_CODEGEN
+ if (Target == TARGET_HYBRID)
+ PM.add(polly::createManagedMemoryRewritePassPass(GPUArchChoice,
+ GPURuntimeChoice));
+#endif
+
+ // FIXME: This dummy ModulePass keeps some programs from miscompiling,
+ // probably some not correctly preserved analyses. It acts as a barrier to
+ // force all analysis results to be recomputed.
+ PM.add(createBarrierNoopPass());
+
+ if (DumpAfter)
+ PM.add(polly::createDumpModuleWrapperPass("-after", true));
+ for (auto &Filename : DumpAfterFile)
+ PM.add(polly::createDumpModuleWrapperPass(Filename, false));
+
+ if (CFGPrinter)
+ PM.add(llvm::createCFGPrinterLegacyPassPass());
+}
+
+static bool shouldEnablePollyForOptimization() { return PollyEnabled; }
+
+static bool shouldEnablePollyForDiagnostic() {
+ // FIXME: PollyTrackFailures is user-controlled, should not be set
+ // programmatically.
+ if (PollyOnlyPrinter || PollyPrinter || PollyOnlyViewer || PollyViewer)
+ PollyTrackFailures = true;
+
+ return PollyOnlyPrinter || PollyPrinter || PollyOnlyViewer || PollyViewer ||
+ ExportJScop;
+}
+
+static void
+registerPollyEarlyAsPossiblePasses(const llvm::PassManagerBuilder &Builder,
+ llvm::legacy::PassManagerBase &PM) {
+ if (PassPosition != POSITION_EARLY)
+ return;
+
+ bool EnableForOpt = shouldEnablePollyForOptimization() &&
+ Builder.OptLevel >= 1 && Builder.SizeLevel == 0;
+ if (!shouldEnablePollyForDiagnostic() && !EnableForOpt)
+ return;
+
+ registerCanonicalicationPasses(PM);
+ registerPollyPasses(PM, EnableForOpt);
+}
+
+static void
+registerPollyLoopOptimizerEndPasses(const llvm::PassManagerBuilder &Builder,
+ llvm::legacy::PassManagerBase &PM) {
+ if (PassPosition != POSITION_AFTER_LOOPOPT)
+ return;
+
+ bool EnableForOpt = shouldEnablePollyForOptimization() &&
+ Builder.OptLevel >= 1 && Builder.SizeLevel == 0;
+ if (!shouldEnablePollyForDiagnostic() && !EnableForOpt)
+ return;
+
+ registerPollyPasses(PM, EnableForOpt);
+ if (EnableForOpt)
+ PM.add(createCodegenCleanupPass());
+}
+
+static void
+registerPollyScalarOptimizerLatePasses(const llvm::PassManagerBuilder &Builder,
+ llvm::legacy::PassManagerBase &PM) {
+ if (PassPosition != POSITION_BEFORE_VECTORIZER)
+ return;
+
+ bool EnableForOpt = shouldEnablePollyForOptimization() &&
+ Builder.OptLevel >= 1 && Builder.SizeLevel == 0;
+ if (!shouldEnablePollyForDiagnostic() && !EnableForOpt)
+ return;
+
+ polly::registerPollyPasses(PM, EnableForOpt);
+ if (EnableForOpt)
+ PM.add(createCodegenCleanupPass());
+}
+
+/// Add the pass sequence required for Polly to the New Pass Manager.
+///
+/// @param PM The pass manager itself.
+/// @param Level The optimization level. Used for the cleanup of Polly's
+/// output.
+/// @param EnableForOpt Whether to add Polly IR transformations. If False, only
+/// the analysis passes are added, skipping Polly itself.
+/// The IR may still be modified.
+static void buildCommonPollyPipeline(FunctionPassManager &PM,
+ OptimizationLevel Level,
+ bool EnableForOpt) {
+ PassBuilder PB;
+ ScopPassManager SPM;
+
+ PM.addPass(CodePreparationPass());
+
+ // TODO add utility passes for the various command line options, once they're
+ // ported
+
+ if (PollyDetectOnly) {
+ // Don't add more passes other than the ScopPassManager's detection passes.
+ PM.addPass(createFunctionToScopPassAdaptor(std::move(SPM)));
+ return;
+ }
+
+ if (PollyViewer)
+ report_fatal_error("Option -polly-show not supported with NPM", false);
+ if (PollyOnlyViewer)
+ report_fatal_error("Option -polly-show-only not supported with NPM", false);
+ if (PollyPrinter)
+ report_fatal_error("Option -polly-dot not supported with NPM", false);
+ if (PollyOnlyPrinter)
+ report_fatal_error("Option -polly-dot-only not supported with NPM", false);
+ if (EnablePolyhedralInfo)
+ report_fatal_error(
+ "Option -polly-enable-polyhedralinfo not supported with NPM", false);
+
+ if (EnableSimplify)
+ SPM.addPass(SimplifyPass(0));
+ if (EnableForwardOpTree)
+ SPM.addPass(ForwardOpTreePass());
+ if (EnableDeLICM)
+ SPM.addPass(DeLICMPass());
+ if (EnableSimplify)
+ SPM.addPass(SimplifyPass(1));
+
+ if (ImportJScop)
+ SPM.addPass(JSONImportPass());
+
+ if (DeadCodeElim)
+ SPM.addPass(DeadCodeElimPass());
+
+ if (FullyIndexedStaticExpansion)
+ report_fatal_error("Option -polly-enable-mse not supported with NPM",
+ false);
+
+ if (EnablePruneUnprofitable)
+ SPM.addPass(PruneUnprofitablePass());
+
+ if (Target == TARGET_CPU || Target == TARGET_HYBRID) {
+ switch (Optimizer) {
+ case OPTIMIZER_NONE:
+ break; /* Do nothing */
+ case OPTIMIZER_ISL:
+ SPM.addPass(IslScheduleOptimizerPass());
+ break;
+ }
+ }
+
+ if (ExportJScop)
+ report_fatal_error("Option -polly-export not supported with NPM", false);
+
+ if (!EnableForOpt)
+ return;
+
+ if (Target == TARGET_CPU || Target == TARGET_HYBRID) {
+ switch (CodeGeneration) {
+ case CODEGEN_AST:
+ SPM.addPass(
+ RequireAnalysisPass<IslAstAnalysis, Scop, ScopAnalysisManager,
+ ScopStandardAnalysisResults &, SPMUpdater &>());
+ break;
+ case CODEGEN_FULL:
+ SPM.addPass(CodeGenerationPass());
+ break;
+ case CODEGEN_NONE:
+ break;
+ }
+ }
+#ifdef GPU_CODEGEN
+ else
+ report_fatal_error("Option -polly-target=gpu not supported for NPM", false);
+#endif
+
+#ifdef GPU_CODEGEN
+ if (Target == TARGET_HYBRID)
+ report_fatal_error("Option -polly-target=hybrid not supported for NPM",
+ false);
+#endif
+
+ PM.addPass(createFunctionToScopPassAdaptor(std::move(SPM)));
+ PM.addPass(PB.buildFunctionSimplificationPipeline(
+ Level, ThinOrFullLTOPhase::None)); // Cleanup
+
+ if (CFGPrinter)
+ PM.addPass(llvm::CFGPrinterPass());
+}
+
+static void buildEarlyPollyPipeline(ModulePassManager &MPM,
+ OptimizationLevel Level) {
+ bool EnableForOpt =
+ shouldEnablePollyForOptimization() && Level.isOptimizingForSpeed();
+ if (!shouldEnablePollyForDiagnostic() && !EnableForOpt)
+ return;
+
+ FunctionPassManager FPM = buildCanonicalicationPassesForNPM(MPM, Level);
+
+ if (DumpBefore || !DumpBeforeFile.empty()) {
+ MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+
+ if (DumpBefore)
+ MPM.addPass(DumpModulePass("-before", true));
+ for (auto &Filename : DumpBeforeFile)
+ MPM.addPass(DumpModulePass(Filename, false));
+
+ FPM = FunctionPassManager();
+ }
+
+ buildCommonPollyPipeline(FPM, Level, EnableForOpt);
+ MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+
+ if (DumpAfter)
+ MPM.addPass(DumpModulePass("-after", true));
+ for (auto &Filename : DumpAfterFile)
+ MPM.addPass(DumpModulePass(Filename, false));
+}
+
+static void buildLatePollyPipeline(FunctionPassManager &PM,
+ OptimizationLevel Level) {
+ bool EnableForOpt =
+ shouldEnablePollyForOptimization() && Level.isOptimizingForSpeed();
+ if (!shouldEnablePollyForDiagnostic() && !EnableForOpt)
+ return;
+
+ if (DumpBefore)
+ PM.addPass(DumpFunctionPass("-before"));
+ if (!DumpBeforeFile.empty())
+ report_fatal_error("Option -polly-dump-before-file at -polly-position=late "
+ "not supported with NPM",
+ false);
+
+ buildCommonPollyPipeline(PM, Level, EnableForOpt);
+
+ if (DumpAfter)
+ PM.addPass(DumpFunctionPass("-after"));
+ if (!DumpAfterFile.empty())
+ report_fatal_error("Option -polly-dump-after-file at -polly-position=late "
+ "not supported with NPM",
+ false);
+}
+
+/// Register Polly to be available as an optimizer
+///
+///
+/// We can currently run Polly at three different points int the pass manager.
+/// a) very early, b) after the canonicalizing loop transformations and c) right
+/// before the vectorizer.
+///
+/// The default is currently a), to register Polly such that it runs as early as
+/// possible. This has several implications:
+///
+/// 1) We need to schedule more canonicalization passes
+///
+/// As nothing is run before Polly, it is necessary to run a set of preparing
+/// transformations before Polly to canonicalize the LLVM-IR and to allow
+/// Polly to detect and understand the code.
+///
+/// 2) LICM and LoopIdiom pass have not yet been run
+///
+/// Loop invariant code motion as well as the loop idiom recognition pass make
+/// it more difficult for Polly to transform code. LICM may introduce
+/// additional data dependences that are hard to eliminate and the loop idiom
+/// recognition pass may introduce calls to memset that we currently do not
+/// understand. By running Polly early enough (meaning before these passes) we
+/// avoid difficulties that may be introduced by these passes.
+///
+/// 3) We get the full -O3 optimization sequence after Polly
+///
+/// The LLVM-IR that is generated by Polly has been optimized on a high level,
+/// but it may be rather inefficient on the lower/scalar level. By scheduling
+/// Polly before all other passes, we have the full sequence of -O3
+/// optimizations behind us, such that inefficiencies on the low level can
+/// be optimized away.
+///
+/// We are currently evaluating the benefit or running Polly at position b) or
+/// c). b) is likely too early as it interacts with the inliner. c) is nice
+/// as everything is fully inlined and canonicalized, but we need to be able
+/// to handle LICMed code to make it useful.
+static llvm::RegisterStandardPasses RegisterPollyOptimizerEarly(
+ llvm::PassManagerBuilder::EP_ModuleOptimizerEarly,
+ registerPollyEarlyAsPossiblePasses);
+
+static llvm::RegisterStandardPasses
+ RegisterPollyOptimizerLoopEnd(llvm::PassManagerBuilder::EP_LoopOptimizerEnd,
+ registerPollyLoopOptimizerEndPasses);
+
+static llvm::RegisterStandardPasses RegisterPollyOptimizerScalarLate(
+ llvm::PassManagerBuilder::EP_VectorizerStart,
+ registerPollyScalarOptimizerLatePasses);
+
+static OwningScopAnalysisManagerFunctionProxy
+createScopAnalyses(FunctionAnalysisManager &FAM,
+ PassInstrumentationCallbacks *PIC) {
+ OwningScopAnalysisManagerFunctionProxy Proxy;
+#define SCOP_ANALYSIS(NAME, CREATE_PASS) \
+ Proxy.getManager().registerPass([PIC] { \
+ (void)PIC; \
+ return CREATE_PASS; \
+ });
+#include "PollyPasses.def"
+
+ Proxy.getManager().registerPass(
+ [&FAM] { return FunctionAnalysisManagerScopProxy(FAM); });
+ return Proxy;
+}
+
+static void registerFunctionAnalyses(FunctionAnalysisManager &FAM,
+ PassInstrumentationCallbacks *PIC) {
+
+#define FUNCTION_ANALYSIS(NAME, CREATE_PASS) \
+ FAM.registerPass([] { return CREATE_PASS; });
+
+#include "PollyPasses.def"
+
+ FAM.registerPass([&FAM, PIC] { return createScopAnalyses(FAM, PIC); });
+}
+
+static bool
+parseFunctionPipeline(StringRef Name, FunctionPassManager &FPM,
+ ArrayRef<PassBuilder::PipelineElement> Pipeline) {
+ if (parseAnalysisUtilityPasses<OwningScopAnalysisManagerFunctionProxy>(
+ "polly-scop-analyses", Name, FPM))
+ return true;
+
+#define FUNCTION_ANALYSIS(NAME, CREATE_PASS) \
+ if (parseAnalysisUtilityPasses< \
+ std::remove_reference<decltype(CREATE_PASS)>::type>(NAME, Name, \
+ FPM)) \
+ return true;
+
+#define FUNCTION_PASS(NAME, CREATE_PASS) \
+ if (Name == NAME) { \
+ FPM.addPass(CREATE_PASS); \
+ return true; \
+ }
+
+#include "PollyPasses.def"
+ return false;
+}
+
+static bool parseScopPass(StringRef Name, ScopPassManager &SPM,
+ PassInstrumentationCallbacks *PIC) {
+#define SCOP_ANALYSIS(NAME, CREATE_PASS) \
+ if (parseAnalysisUtilityPasses< \
+ std::remove_reference<decltype(CREATE_PASS)>::type>(NAME, Name, \
+ SPM)) \
+ return true;
+
+#define SCOP_PASS(NAME, CREATE_PASS) \
+ if (Name == NAME) { \
+ SPM.addPass(CREATE_PASS); \
+ return true; \
+ }
+
+#include "PollyPasses.def"
+
+ return false;
+}
+
+static bool parseScopPipeline(StringRef Name, FunctionPassManager &FPM,
+ PassInstrumentationCallbacks *PIC,
+ ArrayRef<PassBuilder::PipelineElement> Pipeline) {
+ if (Name != "scop")
+ return false;
+ if (!Pipeline.empty()) {
+ ScopPassManager SPM;
+ for (const auto &E : Pipeline)
+ if (!parseScopPass(E.Name, SPM, PIC))
+ return false;
+ FPM.addPass(createFunctionToScopPassAdaptor(std::move(SPM)));
+ }
+ return true;
+}
+
+static bool isScopPassName(StringRef Name) {
+#define SCOP_ANALYSIS(NAME, CREATE_PASS) \
+ if (Name == "require<" NAME ">") \
+ return true; \
+ if (Name == "invalidate<" NAME ">") \
+ return true;
+
+#define SCOP_PASS(NAME, CREATE_PASS) \
+ if (Name == NAME) \
+ return true;
+
+#include "PollyPasses.def"
+
+ return false;
+}
+
+static bool
+parseTopLevelPipeline(ModulePassManager &MPM, PassInstrumentationCallbacks *PIC,
+ ArrayRef<PassBuilder::PipelineElement> Pipeline) {
+ std::vector<PassBuilder::PipelineElement> FullPipeline;
+ StringRef FirstName = Pipeline.front().Name;
+
+ if (!isScopPassName(FirstName))
+ return false;
+
+ FunctionPassManager FPM;
+ ScopPassManager SPM;
+
+ for (auto &Element : Pipeline) {
+ auto &Name = Element.Name;
+ auto &InnerPipeline = Element.InnerPipeline;
+ if (!InnerPipeline.empty()) // Scop passes don't have inner pipelines
+ return false;
+ if (!parseScopPass(Name, SPM, PIC))
+ return false;
+ }
+
+ FPM.addPass(createFunctionToScopPassAdaptor(std::move(SPM)));
+ MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+
+ return true;
+}
+
+void registerPollyPasses(PassBuilder &PB) {
+ PassInstrumentationCallbacks *PIC = PB.getPassInstrumentationCallbacks();
+ PB.registerAnalysisRegistrationCallback([PIC](FunctionAnalysisManager &FAM) {
+ registerFunctionAnalyses(FAM, PIC);
+ });
+ PB.registerPipelineParsingCallback(parseFunctionPipeline);
+ PB.registerPipelineParsingCallback(
+ [PIC](StringRef Name, FunctionPassManager &FPM,
+ ArrayRef<PassBuilder::PipelineElement> Pipeline) -> bool {
+ return parseScopPipeline(Name, FPM, PIC, Pipeline);
+ });
+ PB.registerParseTopLevelPipelineCallback(
+ [PIC](ModulePassManager &MPM,
+ ArrayRef<PassBuilder::PipelineElement> Pipeline) -> bool {
+ return parseTopLevelPipeline(MPM, PIC, Pipeline);
+ });
+
+ switch (PassPosition) {
+ case POSITION_EARLY:
+ PB.registerPipelineStartEPCallback(buildEarlyPollyPipeline);
+ break;
+ case POSITION_AFTER_LOOPOPT:
+ report_fatal_error(
+ "Option -polly-position=after-loopopt not supported with NPM", false);
+ break;
+ case POSITION_BEFORE_VECTORIZER:
+ PB.registerVectorizerStartEPCallback(buildLatePollyPipeline);
+ break;
+ }
+}
+} // namespace polly
+
+llvm::PassPluginLibraryInfo getPollyPluginInfo() {
+ return {LLVM_PLUGIN_API_VERSION, "Polly", LLVM_VERSION_STRING,
+ polly::registerPollyPasses};
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Support/SCEVAffinator.cpp b/contrib/libs/llvm14/tools/polly/lib/Support/SCEVAffinator.cpp
new file mode 100644
index 00000000000..b317702194e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Support/SCEVAffinator.cpp
@@ -0,0 +1,576 @@
+//===--------- SCEVAffinator.cpp - Create Scops from LLVM IR -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Create a polyhedral description for a SCEV value.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/Support/SCEVAffinator.h"
+#include "polly/Options.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/GICHelper.h"
+#include "polly/Support/SCEVValidator.h"
+#include "isl/aff.h"
+#include "isl/local_space.h"
+#include "isl/set.h"
+#include "isl/val.h"
+
+using namespace llvm;
+using namespace polly;
+
+static cl::opt<bool> IgnoreIntegerWrapping(
+ "polly-ignore-integer-wrapping",
+ cl::desc("Do not build run-time checks to proof absence of integer "
+ "wrapping"),
+ cl::Hidden, cl::ZeroOrMore, cl::init(false), cl::cat(PollyCategory));
+
+// The maximal number of basic sets we allow during the construction of a
+// piecewise affine function. More complex ones will result in very high
+// compile time.
+static int const MaxDisjunctionsInPwAff = 100;
+
+// The maximal number of bits for which a general expression is modeled
+// precisely.
+static unsigned const MaxSmallBitWidth = 7;
+
+/// Add the number of basic sets in @p Domain to @p User
+static isl_stat addNumBasicSets(__isl_take isl_set *Domain,
+ __isl_take isl_aff *Aff, void *User) {
+ auto *NumBasicSets = static_cast<unsigned *>(User);
+ *NumBasicSets += isl_set_n_basic_set(Domain);
+ isl_set_free(Domain);
+ isl_aff_free(Aff);
+ return isl_stat_ok;
+}
+
+/// Determine if @p PWAC is too complex to continue.
+static bool isTooComplex(PWACtx PWAC) {
+ unsigned NumBasicSets = 0;
+ isl_pw_aff_foreach_piece(PWAC.first.get(), addNumBasicSets, &NumBasicSets);
+ if (NumBasicSets <= MaxDisjunctionsInPwAff)
+ return false;
+ return true;
+}
+
+/// Return the flag describing the possible wrapping of @p Expr.
+static SCEV::NoWrapFlags getNoWrapFlags(const SCEV *Expr) {
+ if (auto *NAry = dyn_cast<SCEVNAryExpr>(Expr))
+ return NAry->getNoWrapFlags();
+ return SCEV::NoWrapMask;
+}
+
+static PWACtx combine(PWACtx PWAC0, PWACtx PWAC1,
+ __isl_give isl_pw_aff *(Fn)(__isl_take isl_pw_aff *,
+ __isl_take isl_pw_aff *)) {
+ PWAC0.first = isl::manage(Fn(PWAC0.first.release(), PWAC1.first.release()));
+ PWAC0.second = PWAC0.second.unite(PWAC1.second);
+ return PWAC0;
+}
+
+static __isl_give isl_pw_aff *getWidthExpValOnDomain(unsigned Width,
+ __isl_take isl_set *Dom) {
+ auto *Ctx = isl_set_get_ctx(Dom);
+ auto *WidthVal = isl_val_int_from_ui(Ctx, Width);
+ auto *ExpVal = isl_val_2exp(WidthVal);
+ return isl_pw_aff_val_on_domain(Dom, ExpVal);
+}
+
+SCEVAffinator::SCEVAffinator(Scop *S, LoopInfo &LI)
+ : S(S), Ctx(S->getIslCtx().get()), SE(*S->getSE()), LI(LI),
+ TD(S->getFunction().getParent()->getDataLayout()) {}
+
+Loop *SCEVAffinator::getScope() { return BB ? LI.getLoopFor(BB) : nullptr; }
+
+void SCEVAffinator::interpretAsUnsigned(PWACtx &PWAC, unsigned Width) {
+ auto *NonNegDom = isl_pw_aff_nonneg_set(PWAC.first.copy());
+ auto *NonNegPWA =
+ isl_pw_aff_intersect_domain(PWAC.first.copy(), isl_set_copy(NonNegDom));
+ auto *ExpPWA = getWidthExpValOnDomain(Width, isl_set_complement(NonNegDom));
+ PWAC.first = isl::manage(isl_pw_aff_union_add(
+ NonNegPWA, isl_pw_aff_add(PWAC.first.release(), ExpPWA)));
+}
+
+void SCEVAffinator::takeNonNegativeAssumption(
+ PWACtx &PWAC, RecordedAssumptionsTy *RecordedAssumptions) {
+ this->RecordedAssumptions = RecordedAssumptions;
+
+ auto *NegPWA = isl_pw_aff_neg(PWAC.first.copy());
+ auto *NegDom = isl_pw_aff_pos_set(NegPWA);
+ PWAC.second =
+ isl::manage(isl_set_union(PWAC.second.release(), isl_set_copy(NegDom)));
+ auto *Restriction = BB ? NegDom : isl_set_params(NegDom);
+ auto DL = BB ? BB->getTerminator()->getDebugLoc() : DebugLoc();
+ recordAssumption(RecordedAssumptions, UNSIGNED, isl::manage(Restriction), DL,
+ AS_RESTRICTION, BB);
+}
+
+PWACtx SCEVAffinator::getPWACtxFromPWA(isl::pw_aff PWA) {
+ return std::make_pair(PWA, isl::set::empty(isl::space(Ctx, 0, NumIterators)));
+}
+
+PWACtx SCEVAffinator::getPwAff(const SCEV *Expr, BasicBlock *BB,
+ RecordedAssumptionsTy *RecordedAssumptions) {
+ this->BB = BB;
+ this->RecordedAssumptions = RecordedAssumptions;
+
+ if (BB) {
+ auto *DC = S->getDomainConditions(BB).release();
+ NumIterators = isl_set_n_dim(DC);
+ isl_set_free(DC);
+ } else
+ NumIterators = 0;
+
+ return visit(Expr);
+}
+
+PWACtx SCEVAffinator::checkForWrapping(const SCEV *Expr, PWACtx PWAC) const {
+ // If the SCEV flags do contain NSW (no signed wrap) then PWA already
+ // represents Expr in modulo semantic (it is not allowed to overflow), thus we
+ // are done. Otherwise, we will compute:
+ // PWA = ((PWA + 2^(n-1)) mod (2 ^ n)) - 2^(n-1)
+ // whereas n is the number of bits of the Expr, hence:
+ // n = bitwidth(ExprType)
+
+ if (IgnoreIntegerWrapping || (getNoWrapFlags(Expr) & SCEV::FlagNSW))
+ return PWAC;
+
+ isl::pw_aff PWAMod = addModuloSemantic(PWAC.first, Expr->getType());
+
+ isl::set NotEqualSet = PWAC.first.ne_set(PWAMod);
+ PWAC.second = PWAC.second.unite(NotEqualSet).coalesce();
+
+ const DebugLoc &Loc = BB ? BB->getTerminator()->getDebugLoc() : DebugLoc();
+ if (!BB)
+ NotEqualSet = NotEqualSet.params();
+ NotEqualSet = NotEqualSet.coalesce();
+
+ if (!NotEqualSet.is_empty())
+ recordAssumption(RecordedAssumptions, WRAPPING, NotEqualSet, Loc,
+ AS_RESTRICTION, BB);
+
+ return PWAC;
+}
+
+isl::pw_aff SCEVAffinator::addModuloSemantic(isl::pw_aff PWA,
+ Type *ExprType) const {
+ unsigned Width = TD.getTypeSizeInBits(ExprType);
+
+ auto ModVal = isl::val::int_from_ui(Ctx, Width);
+ ModVal = ModVal.pow2();
+
+ isl::set Domain = PWA.domain();
+ isl::pw_aff AddPW =
+ isl::manage(getWidthExpValOnDomain(Width - 1, Domain.release()));
+
+ return PWA.add(AddPW).mod(ModVal).sub(AddPW);
+}
+
+bool SCEVAffinator::hasNSWAddRecForLoop(Loop *L) const {
+ for (const auto &CachedPair : CachedExpressions) {
+ auto *AddRec = dyn_cast<SCEVAddRecExpr>(CachedPair.first.first);
+ if (!AddRec)
+ continue;
+ if (AddRec->getLoop() != L)
+ continue;
+ if (AddRec->getNoWrapFlags() & SCEV::FlagNSW)
+ return true;
+ }
+
+ return false;
+}
+
+bool SCEVAffinator::computeModuloForExpr(const SCEV *Expr) {
+ unsigned Width = TD.getTypeSizeInBits(Expr->getType());
+ // We assume nsw expressions never overflow.
+ if (auto *NAry = dyn_cast<SCEVNAryExpr>(Expr))
+ if (NAry->getNoWrapFlags() & SCEV::FlagNSW)
+ return false;
+ return Width <= MaxSmallBitWidth;
+}
+
+PWACtx SCEVAffinator::visit(const SCEV *Expr) {
+
+ auto Key = std::make_pair(Expr, BB);
+ PWACtx PWAC = CachedExpressions[Key];
+ if (!PWAC.first.is_null())
+ return PWAC;
+
+ auto ConstantAndLeftOverPair = extractConstantFactor(Expr, SE);
+ auto *Factor = ConstantAndLeftOverPair.first;
+ Expr = ConstantAndLeftOverPair.second;
+
+ auto *Scope = getScope();
+ S->addParams(getParamsInAffineExpr(&S->getRegion(), Scope, Expr, SE));
+
+ // In case the scev is a valid parameter, we do not further analyze this
+ // expression, but create a new parameter in the isl_pw_aff. This allows us
+ // to treat subexpressions that we cannot translate into an piecewise affine
+ // expression, as constant parameters of the piecewise affine expression.
+ if (isl_id *Id = S->getIdForParam(Expr).release()) {
+ isl_space *Space = isl_space_set_alloc(Ctx.get(), 1, NumIterators);
+ Space = isl_space_set_dim_id(Space, isl_dim_param, 0, Id);
+
+ isl_set *Domain = isl_set_universe(isl_space_copy(Space));
+ isl_aff *Affine = isl_aff_zero_on_domain(isl_local_space_from_space(Space));
+ Affine = isl_aff_add_coefficient_si(Affine, isl_dim_param, 0, 1);
+
+ PWAC = getPWACtxFromPWA(isl::manage(isl_pw_aff_alloc(Domain, Affine)));
+ } else {
+ PWAC = SCEVVisitor<SCEVAffinator, PWACtx>::visit(Expr);
+ if (computeModuloForExpr(Expr))
+ PWAC.first = addModuloSemantic(PWAC.first, Expr->getType());
+ else
+ PWAC = checkForWrapping(Expr, PWAC);
+ }
+
+ if (!Factor->getType()->isIntegerTy(1)) {
+ PWAC = combine(PWAC, visitConstant(Factor), isl_pw_aff_mul);
+ if (computeModuloForExpr(Key.first))
+ PWAC.first = addModuloSemantic(PWAC.first, Expr->getType());
+ }
+
+ // For compile time reasons we need to simplify the PWAC before we cache and
+ // return it.
+ PWAC.first = PWAC.first.coalesce();
+ if (!computeModuloForExpr(Key.first))
+ PWAC = checkForWrapping(Key.first, PWAC);
+
+ CachedExpressions[Key] = PWAC;
+ return PWAC;
+}
+
+PWACtx SCEVAffinator::visitConstant(const SCEVConstant *Expr) {
+ ConstantInt *Value = Expr->getValue();
+ isl_val *v;
+
+ // LLVM does not define if an integer value is interpreted as a signed or
+ // unsigned value. Hence, without further information, it is unknown how
+ // this value needs to be converted to GMP. At the moment, we only support
+ // signed operations. So we just interpret it as signed. Later, there are
+ // two options:
+ //
+ // 1. We always interpret any value as signed and convert the values on
+ // demand.
+ // 2. We pass down the signedness of the calculation and use it to interpret
+ // this constant correctly.
+ v = isl_valFromAPInt(Ctx.get(), Value->getValue(), /* isSigned */ true);
+
+ isl_space *Space = isl_space_set_alloc(Ctx.get(), 0, NumIterators);
+ isl_local_space *ls = isl_local_space_from_space(Space);
+ return getPWACtxFromPWA(
+ isl::manage(isl_pw_aff_from_aff(isl_aff_val_on_domain(ls, v))));
+}
+
+PWACtx SCEVAffinator::visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) {
+ return visit(Expr->getOperand(0));
+}
+
+PWACtx SCEVAffinator::visitTruncateExpr(const SCEVTruncateExpr *Expr) {
+ // Truncate operations are basically modulo operations, thus we can
+ // model them that way. However, for large types we assume the operand
+ // to fit in the new type size instead of introducing a modulo with a very
+ // large constant.
+
+ auto *Op = Expr->getOperand();
+ auto OpPWAC = visit(Op);
+
+ unsigned Width = TD.getTypeSizeInBits(Expr->getType());
+
+ if (computeModuloForExpr(Expr))
+ return OpPWAC;
+
+ auto *Dom = OpPWAC.first.domain().release();
+ auto *ExpPWA = getWidthExpValOnDomain(Width - 1, Dom);
+ auto *GreaterDom =
+ isl_pw_aff_ge_set(OpPWAC.first.copy(), isl_pw_aff_copy(ExpPWA));
+ auto *SmallerDom =
+ isl_pw_aff_lt_set(OpPWAC.first.copy(), isl_pw_aff_neg(ExpPWA));
+ auto *OutOfBoundsDom = isl_set_union(SmallerDom, GreaterDom);
+ OpPWAC.second = OpPWAC.second.unite(isl::manage_copy(OutOfBoundsDom));
+
+ if (!BB) {
+ assert(isl_set_dim(OutOfBoundsDom, isl_dim_set) == 0 &&
+ "Expected a zero dimensional set for non-basic-block domains");
+ OutOfBoundsDom = isl_set_params(OutOfBoundsDom);
+ }
+
+ recordAssumption(RecordedAssumptions, UNSIGNED, isl::manage(OutOfBoundsDom),
+ DebugLoc(), AS_RESTRICTION, BB);
+
+ return OpPWAC;
+}
+
+PWACtx SCEVAffinator::visitZeroExtendExpr(const SCEVZeroExtendExpr *Expr) {
+ // A zero-extended value can be interpreted as a piecewise defined signed
+ // value. If the value was non-negative it stays the same, otherwise it
+ // is the sum of the original value and 2^n where n is the bit-width of
+ // the original (or operand) type. Examples:
+ // zext i8 127 to i32 -> { [127] }
+ // zext i8 -1 to i32 -> { [256 + (-1)] } = { [255] }
+ // zext i8 %v to i32 -> [v] -> { [v] | v >= 0; [256 + v] | v < 0 }
+ //
+ // However, LLVM/Scalar Evolution uses zero-extend (potentially lead by a
+ // truncate) to represent some forms of modulo computation. The left-hand side
+ // of the condition in the code below would result in the SCEV
+ // "zext i1 <false, +, true>for.body" which is just another description
+ // of the C expression "i & 1 != 0" or, equivalently, "i % 2 != 0".
+ //
+ // for (i = 0; i < N; i++)
+ // if (i & 1 != 0 /* == i % 2 */)
+ // /* do something */
+ //
+ // If we do not make the modulo explicit but only use the mechanism described
+ // above we will get the very restrictive assumption "N < 3", because for all
+ // values of N >= 3 the SCEVAddRecExpr operand of the zero-extend would wrap.
+ // Alternatively, we can make the modulo in the operand explicit in the
+ // resulting piecewise function and thereby avoid the assumption on N. For the
+ // example this would result in the following piecewise affine function:
+ // { [i0] -> [(1)] : 2*floor((-1 + i0)/2) = -1 + i0;
+ // [i0] -> [(0)] : 2*floor((i0)/2) = i0 }
+ // To this end we can first determine if the (immediate) operand of the
+ // zero-extend can wrap and, in case it might, we will use explicit modulo
+ // semantic to compute the result instead of emitting non-wrapping
+ // assumptions.
+ //
+ // Note that operands with large bit-widths are less likely to be negative
+ // because it would result in a very large access offset or loop bound after
+ // the zero-extend. To this end one can optimistically assume the operand to
+ // be positive and avoid the piecewise definition if the bit-width is bigger
+ // than some threshold (here MaxZextSmallBitWidth).
+ //
+ // We choose to go with a hybrid solution of all modeling techniques described
+ // above. For small bit-widths (up to MaxZextSmallBitWidth) we will model the
+ // wrapping explicitly and use a piecewise defined function. However, if the
+ // bit-width is bigger than MaxZextSmallBitWidth we will employ overflow
+ // assumptions and assume the "former negative" piece will not exist.
+
+ auto *Op = Expr->getOperand();
+ auto OpPWAC = visit(Op);
+
+ // If the width is to big we assume the negative part does not occur.
+ if (!computeModuloForExpr(Op)) {
+ takeNonNegativeAssumption(OpPWAC, RecordedAssumptions);
+ return OpPWAC;
+ }
+
+ // If the width is small build the piece for the non-negative part and
+ // the one for the negative part and unify them.
+ unsigned Width = TD.getTypeSizeInBits(Op->getType());
+ interpretAsUnsigned(OpPWAC, Width);
+ return OpPWAC;
+}
+
+PWACtx SCEVAffinator::visitSignExtendExpr(const SCEVSignExtendExpr *Expr) {
+ // As all values are represented as signed, a sign extension is a noop.
+ return visit(Expr->getOperand());
+}
+
+PWACtx SCEVAffinator::visitAddExpr(const SCEVAddExpr *Expr) {
+ PWACtx Sum = visit(Expr->getOperand(0));
+
+ for (int i = 1, e = Expr->getNumOperands(); i < e; ++i) {
+ Sum = combine(Sum, visit(Expr->getOperand(i)), isl_pw_aff_add);
+ if (isTooComplex(Sum))
+ return complexityBailout();
+ }
+
+ return Sum;
+}
+
+PWACtx SCEVAffinator::visitMulExpr(const SCEVMulExpr *Expr) {
+ PWACtx Prod = visit(Expr->getOperand(0));
+
+ for (int i = 1, e = Expr->getNumOperands(); i < e; ++i) {
+ Prod = combine(Prod, visit(Expr->getOperand(i)), isl_pw_aff_mul);
+ if (isTooComplex(Prod))
+ return complexityBailout();
+ }
+
+ return Prod;
+}
+
+PWACtx SCEVAffinator::visitAddRecExpr(const SCEVAddRecExpr *Expr) {
+ assert(Expr->isAffine() && "Only affine AddRecurrences allowed");
+
+ auto Flags = Expr->getNoWrapFlags();
+
+ // Directly generate isl_pw_aff for Expr if 'start' is zero.
+ if (Expr->getStart()->isZero()) {
+ assert(S->contains(Expr->getLoop()) &&
+ "Scop does not contain the loop referenced in this AddRec");
+
+ PWACtx Step = visit(Expr->getOperand(1));
+ isl_space *Space = isl_space_set_alloc(Ctx.get(), 0, NumIterators);
+ isl_local_space *LocalSpace = isl_local_space_from_space(Space);
+
+ unsigned loopDimension = S->getRelativeLoopDepth(Expr->getLoop());
+
+ isl_aff *LAff = isl_aff_set_coefficient_si(
+ isl_aff_zero_on_domain(LocalSpace), isl_dim_in, loopDimension, 1);
+ isl_pw_aff *LPwAff = isl_pw_aff_from_aff(LAff);
+
+ Step.first = Step.first.mul(isl::manage(LPwAff));
+ return Step;
+ }
+
+ // Translate AddRecExpr from '{start, +, inc}' into 'start + {0, +, inc}'
+ // if 'start' is not zero.
+ // TODO: Using the original SCEV no-wrap flags is not always safe, however
+ // as our code generation is reordering the expression anyway it doesn't
+ // really matter.
+ const SCEV *ZeroStartExpr =
+ SE.getAddRecExpr(SE.getConstant(Expr->getStart()->getType(), 0),
+ Expr->getStepRecurrence(SE), Expr->getLoop(), Flags);
+
+ PWACtx Result = visit(ZeroStartExpr);
+ PWACtx Start = visit(Expr->getStart());
+ Result = combine(Result, Start, isl_pw_aff_add);
+ return Result;
+}
+
+PWACtx SCEVAffinator::visitSMaxExpr(const SCEVSMaxExpr *Expr) {
+ PWACtx Max = visit(Expr->getOperand(0));
+
+ for (int i = 1, e = Expr->getNumOperands(); i < e; ++i) {
+ Max = combine(Max, visit(Expr->getOperand(i)), isl_pw_aff_max);
+ if (isTooComplex(Max))
+ return complexityBailout();
+ }
+
+ return Max;
+}
+
+PWACtx SCEVAffinator::visitSMinExpr(const SCEVSMinExpr *Expr) {
+ PWACtx Min = visit(Expr->getOperand(0));
+
+ for (int i = 1, e = Expr->getNumOperands(); i < e; ++i) {
+ Min = combine(Min, visit(Expr->getOperand(i)), isl_pw_aff_min);
+ if (isTooComplex(Min))
+ return complexityBailout();
+ }
+
+ return Min;
+}
+
+PWACtx SCEVAffinator::visitUMaxExpr(const SCEVUMaxExpr *Expr) {
+ llvm_unreachable("SCEVUMaxExpr not yet supported");
+}
+
+PWACtx SCEVAffinator::visitUMinExpr(const SCEVUMinExpr *Expr) {
+ llvm_unreachable("SCEVUMinExpr not yet supported");
+}
+
+PWACtx
+SCEVAffinator::visitSequentialUMinExpr(const SCEVSequentialUMinExpr *Expr) {
+ llvm_unreachable("SCEVSequentialUMinExpr not yet supported");
+}
+
+PWACtx SCEVAffinator::visitUDivExpr(const SCEVUDivExpr *Expr) {
+ // The handling of unsigned division is basically the same as for signed
+ // division, except the interpretation of the operands. As the divisor
+ // has to be constant in both cases we can simply interpret it as an
+ // unsigned value without additional complexity in the representation.
+ // For the dividend we could choose from the different representation
+ // schemes introduced for zero-extend operations but for now we will
+ // simply use an assumption.
+ auto *Dividend = Expr->getLHS();
+ auto *Divisor = Expr->getRHS();
+ assert(isa<SCEVConstant>(Divisor) &&
+ "UDiv is no parameter but has a non-constant RHS.");
+
+ auto DividendPWAC = visit(Dividend);
+ auto DivisorPWAC = visit(Divisor);
+
+ if (SE.isKnownNegative(Divisor)) {
+ // Interpret negative divisors unsigned. This is a special case of the
+ // piece-wise defined value described for zero-extends as we already know
+ // the actual value of the constant divisor.
+ unsigned Width = TD.getTypeSizeInBits(Expr->getType());
+ auto *DivisorDom = DivisorPWAC.first.domain().release();
+ auto *WidthExpPWA = getWidthExpValOnDomain(Width, DivisorDom);
+ DivisorPWAC.first = DivisorPWAC.first.add(isl::manage(WidthExpPWA));
+ }
+
+ // TODO: One can represent the dividend as piece-wise function to be more
+ // precise but therefor a heuristic is needed.
+
+ // Assume a non-negative dividend.
+ takeNonNegativeAssumption(DividendPWAC, RecordedAssumptions);
+
+ DividendPWAC = combine(DividendPWAC, DivisorPWAC, isl_pw_aff_div);
+ DividendPWAC.first = DividendPWAC.first.floor();
+
+ return DividendPWAC;
+}
+
+PWACtx SCEVAffinator::visitSDivInstruction(Instruction *SDiv) {
+ assert(SDiv->getOpcode() == Instruction::SDiv && "Assumed SDiv instruction!");
+
+ auto *Scope = getScope();
+ auto *Divisor = SDiv->getOperand(1);
+ auto *DivisorSCEV = SE.getSCEVAtScope(Divisor, Scope);
+ auto DivisorPWAC = visit(DivisorSCEV);
+ assert(isa<SCEVConstant>(DivisorSCEV) &&
+ "SDiv is no parameter but has a non-constant RHS.");
+
+ auto *Dividend = SDiv->getOperand(0);
+ auto *DividendSCEV = SE.getSCEVAtScope(Dividend, Scope);
+ auto DividendPWAC = visit(DividendSCEV);
+ DividendPWAC = combine(DividendPWAC, DivisorPWAC, isl_pw_aff_tdiv_q);
+ return DividendPWAC;
+}
+
+PWACtx SCEVAffinator::visitSRemInstruction(Instruction *SRem) {
+ assert(SRem->getOpcode() == Instruction::SRem && "Assumed SRem instruction!");
+
+ auto *Scope = getScope();
+ auto *Divisor = SRem->getOperand(1);
+ auto *DivisorSCEV = SE.getSCEVAtScope(Divisor, Scope);
+ auto DivisorPWAC = visit(DivisorSCEV);
+ assert(isa<ConstantInt>(Divisor) &&
+ "SRem is no parameter but has a non-constant RHS.");
+
+ auto *Dividend = SRem->getOperand(0);
+ auto *DividendSCEV = SE.getSCEVAtScope(Dividend, Scope);
+ auto DividendPWAC = visit(DividendSCEV);
+ DividendPWAC = combine(DividendPWAC, DivisorPWAC, isl_pw_aff_tdiv_r);
+ return DividendPWAC;
+}
+
+PWACtx SCEVAffinator::visitUnknown(const SCEVUnknown *Expr) {
+ if (Instruction *I = dyn_cast<Instruction>(Expr->getValue())) {
+ switch (I->getOpcode()) {
+ case Instruction::IntToPtr:
+ return visit(SE.getSCEVAtScope(I->getOperand(0), getScope()));
+ case Instruction::SDiv:
+ return visitSDivInstruction(I);
+ case Instruction::SRem:
+ return visitSRemInstruction(I);
+ default:
+ break; // Fall through.
+ }
+ }
+
+ if (isa<ConstantPointerNull>(Expr->getValue())) {
+ isl::val v{Ctx, 0};
+ isl::space Space{Ctx, 0, NumIterators};
+ isl::local_space ls{Space};
+ return getPWACtxFromPWA(isl::aff(ls, v));
+ }
+
+ llvm_unreachable("Unknowns SCEV was neither a parameter, a constant nor a "
+ "valid instruction.");
+}
+
+PWACtx SCEVAffinator::complexityBailout() {
+ // We hit the complexity limit for affine expressions; invalidate the scop
+ // and return a constant zero.
+ const DebugLoc &Loc = BB ? BB->getTerminator()->getDebugLoc() : DebugLoc();
+ S->invalidate(COMPLEXITY, Loc);
+ return visit(SE.getZero(Type::getInt32Ty(S->getFunction().getContext())));
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Support/SCEVValidator.cpp b/contrib/libs/llvm14/tools/polly/lib/Support/SCEVValidator.cpp
new file mode 100644
index 00000000000..e2236fe1d2a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Support/SCEVValidator.cpp
@@ -0,0 +1,770 @@
+
+#include "polly/Support/SCEVValidator.h"
+#include "polly/ScopDetection.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "llvm/Support/Debug.h"
+
+using namespace llvm;
+using namespace polly;
+
+#define DEBUG_TYPE "polly-scev-validator"
+
+namespace SCEVType {
+/// The type of a SCEV
+///
+/// To check for the validity of a SCEV we assign to each SCEV a type. The
+/// possible types are INT, PARAM, IV and INVALID. The order of the types is
+/// important. The subexpressions of SCEV with a type X can only have a type
+/// that is smaller or equal than X.
+enum TYPE {
+ // An integer value.
+ INT,
+
+ // An expression that is constant during the execution of the Scop,
+ // but that may depend on parameters unknown at compile time.
+ PARAM,
+
+ // An expression that may change during the execution of the SCoP.
+ IV,
+
+ // An invalid expression.
+ INVALID
+};
+} // namespace SCEVType
+
+/// The result the validator returns for a SCEV expression.
+class ValidatorResult {
+ /// The type of the expression
+ SCEVType::TYPE Type;
+
+ /// The set of Parameters in the expression.
+ ParameterSetTy Parameters;
+
+public:
+ /// The copy constructor
+ ValidatorResult(const ValidatorResult &Source) {
+ Type = Source.Type;
+ Parameters = Source.Parameters;
+ }
+
+ /// Construct a result with a certain type and no parameters.
+ ValidatorResult(SCEVType::TYPE Type) : Type(Type) {
+ assert(Type != SCEVType::PARAM && "Did you forget to pass the parameter");
+ }
+
+ /// Construct a result with a certain type and a single parameter.
+ ValidatorResult(SCEVType::TYPE Type, const SCEV *Expr) : Type(Type) {
+ Parameters.insert(Expr);
+ }
+
+ /// Get the type of the ValidatorResult.
+ SCEVType::TYPE getType() { return Type; }
+
+ /// Is the analyzed SCEV constant during the execution of the SCoP.
+ bool isConstant() { return Type == SCEVType::INT || Type == SCEVType::PARAM; }
+
+ /// Is the analyzed SCEV valid.
+ bool isValid() { return Type != SCEVType::INVALID; }
+
+ /// Is the analyzed SCEV of Type IV.
+ bool isIV() { return Type == SCEVType::IV; }
+
+ /// Is the analyzed SCEV of Type INT.
+ bool isINT() { return Type == SCEVType::INT; }
+
+ /// Is the analyzed SCEV of Type PARAM.
+ bool isPARAM() { return Type == SCEVType::PARAM; }
+
+ /// Get the parameters of this validator result.
+ const ParameterSetTy &getParameters() { return Parameters; }
+
+ /// Add the parameters of Source to this result.
+ void addParamsFrom(const ValidatorResult &Source) {
+ Parameters.insert(Source.Parameters.begin(), Source.Parameters.end());
+ }
+
+ /// Merge a result.
+ ///
+ /// This means to merge the parameters and to set the Type to the most
+ /// specific Type that matches both.
+ void merge(const ValidatorResult &ToMerge) {
+ Type = std::max(Type, ToMerge.Type);
+ addParamsFrom(ToMerge);
+ }
+
+ void print(raw_ostream &OS) {
+ switch (Type) {
+ case SCEVType::INT:
+ OS << "SCEVType::INT";
+ break;
+ case SCEVType::PARAM:
+ OS << "SCEVType::PARAM";
+ break;
+ case SCEVType::IV:
+ OS << "SCEVType::IV";
+ break;
+ case SCEVType::INVALID:
+ OS << "SCEVType::INVALID";
+ break;
+ }
+ }
+};
+
+raw_ostream &operator<<(raw_ostream &OS, class ValidatorResult &VR) {
+ VR.print(OS);
+ return OS;
+}
+
+/// Check if a SCEV is valid in a SCoP.
+struct SCEVValidator
+ : public SCEVVisitor<SCEVValidator, class ValidatorResult> {
+private:
+ const Region *R;
+ Loop *Scope;
+ ScalarEvolution &SE;
+ InvariantLoadsSetTy *ILS;
+
+public:
+ SCEVValidator(const Region *R, Loop *Scope, ScalarEvolution &SE,
+ InvariantLoadsSetTy *ILS)
+ : R(R), Scope(Scope), SE(SE), ILS(ILS) {}
+
+ class ValidatorResult visitConstant(const SCEVConstant *Constant) {
+ return ValidatorResult(SCEVType::INT);
+ }
+
+ class ValidatorResult visitZeroExtendOrTruncateExpr(const SCEV *Expr,
+ const SCEV *Operand) {
+ ValidatorResult Op = visit(Operand);
+ auto Type = Op.getType();
+
+ // If unsigned operations are allowed return the operand, otherwise
+ // check if we can model the expression without unsigned assumptions.
+ if (PollyAllowUnsignedOperations || Type == SCEVType::INVALID)
+ return Op;
+
+ if (Type == SCEVType::IV)
+ return ValidatorResult(SCEVType::INVALID);
+ return ValidatorResult(SCEVType::PARAM, Expr);
+ }
+
+ class ValidatorResult visitPtrToIntExpr(const SCEVPtrToIntExpr *Expr) {
+ return visit(Expr->getOperand());
+ }
+
+ class ValidatorResult visitTruncateExpr(const SCEVTruncateExpr *Expr) {
+ return visitZeroExtendOrTruncateExpr(Expr, Expr->getOperand());
+ }
+
+ class ValidatorResult visitZeroExtendExpr(const SCEVZeroExtendExpr *Expr) {
+ return visitZeroExtendOrTruncateExpr(Expr, Expr->getOperand());
+ }
+
+ class ValidatorResult visitSignExtendExpr(const SCEVSignExtendExpr *Expr) {
+ return visit(Expr->getOperand());
+ }
+
+ class ValidatorResult visitAddExpr(const SCEVAddExpr *Expr) {
+ ValidatorResult Return(SCEVType::INT);
+
+ for (int i = 0, e = Expr->getNumOperands(); i < e; ++i) {
+ ValidatorResult Op = visit(Expr->getOperand(i));
+ Return.merge(Op);
+
+ // Early exit.
+ if (!Return.isValid())
+ break;
+ }
+
+ return Return;
+ }
+
+ class ValidatorResult visitMulExpr(const SCEVMulExpr *Expr) {
+ ValidatorResult Return(SCEVType::INT);
+
+ bool HasMultipleParams = false;
+
+ for (int i = 0, e = Expr->getNumOperands(); i < e; ++i) {
+ ValidatorResult Op = visit(Expr->getOperand(i));
+
+ if (Op.isINT())
+ continue;
+
+ if (Op.isPARAM() && Return.isPARAM()) {
+ HasMultipleParams = true;
+ continue;
+ }
+
+ if ((Op.isIV() || Op.isPARAM()) && !Return.isINT()) {
+ LLVM_DEBUG(
+ dbgs() << "INVALID: More than one non-int operand in MulExpr\n"
+ << "\tExpr: " << *Expr << "\n"
+ << "\tPrevious expression type: " << Return << "\n"
+ << "\tNext operand (" << Op << "): " << *Expr->getOperand(i)
+ << "\n");
+
+ return ValidatorResult(SCEVType::INVALID);
+ }
+
+ Return.merge(Op);
+ }
+
+ if (HasMultipleParams && Return.isValid())
+ return ValidatorResult(SCEVType::PARAM, Expr);
+
+ return Return;
+ }
+
+ class ValidatorResult visitAddRecExpr(const SCEVAddRecExpr *Expr) {
+ if (!Expr->isAffine()) {
+ LLVM_DEBUG(dbgs() << "INVALID: AddRec is not affine");
+ return ValidatorResult(SCEVType::INVALID);
+ }
+
+ ValidatorResult Start = visit(Expr->getStart());
+ ValidatorResult Recurrence = visit(Expr->getStepRecurrence(SE));
+
+ if (!Start.isValid())
+ return Start;
+
+ if (!Recurrence.isValid())
+ return Recurrence;
+
+ auto *L = Expr->getLoop();
+ if (R->contains(L) && (!Scope || !L->contains(Scope))) {
+ LLVM_DEBUG(
+ dbgs() << "INVALID: Loop of AddRec expression boxed in an a "
+ "non-affine subregion or has a non-synthesizable exit "
+ "value.");
+ return ValidatorResult(SCEVType::INVALID);
+ }
+
+ if (R->contains(L)) {
+ if (Recurrence.isINT()) {
+ ValidatorResult Result(SCEVType::IV);
+ Result.addParamsFrom(Start);
+ return Result;
+ }
+
+ LLVM_DEBUG(dbgs() << "INVALID: AddRec within scop has non-int"
+ "recurrence part");
+ return ValidatorResult(SCEVType::INVALID);
+ }
+
+ assert(Recurrence.isConstant() && "Expected 'Recurrence' to be constant");
+
+ // Directly generate ValidatorResult for Expr if 'start' is zero.
+ if (Expr->getStart()->isZero())
+ return ValidatorResult(SCEVType::PARAM, Expr);
+
+ // Translate AddRecExpr from '{start, +, inc}' into 'start + {0, +, inc}'
+ // if 'start' is not zero.
+ const SCEV *ZeroStartExpr = SE.getAddRecExpr(
+ SE.getConstant(Expr->getStart()->getType(), 0),
+ Expr->getStepRecurrence(SE), Expr->getLoop(), Expr->getNoWrapFlags());
+
+ ValidatorResult ZeroStartResult =
+ ValidatorResult(SCEVType::PARAM, ZeroStartExpr);
+ ZeroStartResult.addParamsFrom(Start);
+
+ return ZeroStartResult;
+ }
+
+ class ValidatorResult visitSMaxExpr(const SCEVSMaxExpr *Expr) {
+ ValidatorResult Return(SCEVType::INT);
+
+ for (int i = 0, e = Expr->getNumOperands(); i < e; ++i) {
+ ValidatorResult Op = visit(Expr->getOperand(i));
+
+ if (!Op.isValid())
+ return Op;
+
+ Return.merge(Op);
+ }
+
+ return Return;
+ }
+
+ class ValidatorResult visitSMinExpr(const SCEVSMinExpr *Expr) {
+ ValidatorResult Return(SCEVType::INT);
+
+ for (int i = 0, e = Expr->getNumOperands(); i < e; ++i) {
+ ValidatorResult Op = visit(Expr->getOperand(i));
+
+ if (!Op.isValid())
+ return Op;
+
+ Return.merge(Op);
+ }
+
+ return Return;
+ }
+
+ class ValidatorResult visitUMaxExpr(const SCEVUMaxExpr *Expr) {
+ // We do not support unsigned max operations. If 'Expr' is constant during
+ // Scop execution we treat this as a parameter, otherwise we bail out.
+ for (int i = 0, e = Expr->getNumOperands(); i < e; ++i) {
+ ValidatorResult Op = visit(Expr->getOperand(i));
+
+ if (!Op.isConstant()) {
+ LLVM_DEBUG(dbgs() << "INVALID: UMaxExpr has a non-constant operand");
+ return ValidatorResult(SCEVType::INVALID);
+ }
+ }
+
+ return ValidatorResult(SCEVType::PARAM, Expr);
+ }
+
+ class ValidatorResult visitUMinExpr(const SCEVUMinExpr *Expr) {
+ // We do not support unsigned min operations. If 'Expr' is constant during
+ // Scop execution we treat this as a parameter, otherwise we bail out.
+ for (int i = 0, e = Expr->getNumOperands(); i < e; ++i) {
+ ValidatorResult Op = visit(Expr->getOperand(i));
+
+ if (!Op.isConstant()) {
+ LLVM_DEBUG(dbgs() << "INVALID: UMinExpr has a non-constant operand");
+ return ValidatorResult(SCEVType::INVALID);
+ }
+ }
+
+ return ValidatorResult(SCEVType::PARAM, Expr);
+ }
+
+ class ValidatorResult
+ visitSequentialUMinExpr(const SCEVSequentialUMinExpr *Expr) {
+ // We do not support unsigned min operations. If 'Expr' is constant during
+ // Scop execution we treat this as a parameter, otherwise we bail out.
+ for (int i = 0, e = Expr->getNumOperands(); i < e; ++i) {
+ ValidatorResult Op = visit(Expr->getOperand(i));
+
+ if (!Op.isConstant()) {
+ LLVM_DEBUG(
+ dbgs()
+ << "INVALID: SCEVSequentialUMinExpr has a non-constant operand");
+ return ValidatorResult(SCEVType::INVALID);
+ }
+ }
+
+ return ValidatorResult(SCEVType::PARAM, Expr);
+ }
+
+ ValidatorResult visitGenericInst(Instruction *I, const SCEV *S) {
+ if (R->contains(I)) {
+ LLVM_DEBUG(dbgs() << "INVALID: UnknownExpr references an instruction "
+ "within the region\n");
+ return ValidatorResult(SCEVType::INVALID);
+ }
+
+ return ValidatorResult(SCEVType::PARAM, S);
+ }
+
+ ValidatorResult visitLoadInstruction(Instruction *I, const SCEV *S) {
+ if (R->contains(I) && ILS) {
+ ILS->insert(cast<LoadInst>(I));
+ return ValidatorResult(SCEVType::PARAM, S);
+ }
+
+ return visitGenericInst(I, S);
+ }
+
+ ValidatorResult visitDivision(const SCEV *Dividend, const SCEV *Divisor,
+ const SCEV *DivExpr,
+ Instruction *SDiv = nullptr) {
+
+ // First check if we might be able to model the division, thus if the
+ // divisor is constant. If so, check the dividend, otherwise check if
+ // the whole division can be seen as a parameter.
+ if (isa<SCEVConstant>(Divisor) && !Divisor->isZero())
+ return visit(Dividend);
+
+ // For signed divisions use the SDiv instruction to check for a parameter
+ // division, for unsigned divisions check the operands.
+ if (SDiv)
+ return visitGenericInst(SDiv, DivExpr);
+
+ ValidatorResult LHS = visit(Dividend);
+ ValidatorResult RHS = visit(Divisor);
+ if (LHS.isConstant() && RHS.isConstant())
+ return ValidatorResult(SCEVType::PARAM, DivExpr);
+
+ LLVM_DEBUG(
+ dbgs() << "INVALID: unsigned division of non-constant expressions");
+ return ValidatorResult(SCEVType::INVALID);
+ }
+
+ ValidatorResult visitUDivExpr(const SCEVUDivExpr *Expr) {
+ if (!PollyAllowUnsignedOperations)
+ return ValidatorResult(SCEVType::INVALID);
+
+ auto *Dividend = Expr->getLHS();
+ auto *Divisor = Expr->getRHS();
+ return visitDivision(Dividend, Divisor, Expr);
+ }
+
+ ValidatorResult visitSDivInstruction(Instruction *SDiv, const SCEV *Expr) {
+ assert(SDiv->getOpcode() == Instruction::SDiv &&
+ "Assumed SDiv instruction!");
+
+ auto *Dividend = SE.getSCEV(SDiv->getOperand(0));
+ auto *Divisor = SE.getSCEV(SDiv->getOperand(1));
+ return visitDivision(Dividend, Divisor, Expr, SDiv);
+ }
+
+ ValidatorResult visitSRemInstruction(Instruction *SRem, const SCEV *S) {
+ assert(SRem->getOpcode() == Instruction::SRem &&
+ "Assumed SRem instruction!");
+
+ auto *Divisor = SRem->getOperand(1);
+ auto *CI = dyn_cast<ConstantInt>(Divisor);
+ if (!CI || CI->isZeroValue())
+ return visitGenericInst(SRem, S);
+
+ auto *Dividend = SRem->getOperand(0);
+ auto *DividendSCEV = SE.getSCEV(Dividend);
+ return visit(DividendSCEV);
+ }
+
+ ValidatorResult visitUnknown(const SCEVUnknown *Expr) {
+ Value *V = Expr->getValue();
+
+ if (!Expr->getType()->isIntegerTy() && !Expr->getType()->isPointerTy()) {
+ LLVM_DEBUG(dbgs() << "INVALID: UnknownExpr is not an integer or pointer");
+ return ValidatorResult(SCEVType::INVALID);
+ }
+
+ if (isa<UndefValue>(V)) {
+ LLVM_DEBUG(dbgs() << "INVALID: UnknownExpr references an undef value");
+ return ValidatorResult(SCEVType::INVALID);
+ }
+
+ if (Instruction *I = dyn_cast<Instruction>(Expr->getValue())) {
+ switch (I->getOpcode()) {
+ case Instruction::IntToPtr:
+ return visit(SE.getSCEVAtScope(I->getOperand(0), Scope));
+ case Instruction::Load:
+ return visitLoadInstruction(I, Expr);
+ case Instruction::SDiv:
+ return visitSDivInstruction(I, Expr);
+ case Instruction::SRem:
+ return visitSRemInstruction(I, Expr);
+ default:
+ return visitGenericInst(I, Expr);
+ }
+ }
+
+ if (Expr->getType()->isPointerTy()) {
+ if (isa<ConstantPointerNull>(V))
+ return ValidatorResult(SCEVType::INT); // "int"
+ }
+
+ return ValidatorResult(SCEVType::PARAM, Expr);
+ }
+};
+
+/// Check whether a SCEV refers to an SSA name defined inside a region.
+class SCEVInRegionDependences {
+ const Region *R;
+ Loop *Scope;
+ const InvariantLoadsSetTy &ILS;
+ bool AllowLoops;
+ bool HasInRegionDeps = false;
+
+public:
+ SCEVInRegionDependences(const Region *R, Loop *Scope, bool AllowLoops,
+ const InvariantLoadsSetTy &ILS)
+ : R(R), Scope(Scope), ILS(ILS), AllowLoops(AllowLoops) {}
+
+ bool follow(const SCEV *S) {
+ if (auto Unknown = dyn_cast<SCEVUnknown>(S)) {
+ Instruction *Inst = dyn_cast<Instruction>(Unknown->getValue());
+
+ if (Inst) {
+ // When we invariant load hoist a load, we first make sure that there
+ // can be no dependences created by it in the Scop region. So, we should
+ // not consider scalar dependences to `LoadInst`s that are invariant
+ // load hoisted.
+ //
+ // If this check is not present, then we create data dependences which
+ // are strictly not necessary by tracking the invariant load as a
+ // scalar.
+ LoadInst *LI = dyn_cast<LoadInst>(Inst);
+ if (LI && ILS.contains(LI))
+ return false;
+ }
+
+ // Return true when Inst is defined inside the region R.
+ if (!Inst || !R->contains(Inst))
+ return true;
+
+ HasInRegionDeps = true;
+ return false;
+ }
+
+ if (auto AddRec = dyn_cast<SCEVAddRecExpr>(S)) {
+ if (AllowLoops)
+ return true;
+
+ auto *L = AddRec->getLoop();
+ if (R->contains(L) && !L->contains(Scope)) {
+ HasInRegionDeps = true;
+ return false;
+ }
+ }
+
+ return true;
+ }
+ bool isDone() { return false; }
+ bool hasDependences() { return HasInRegionDeps; }
+};
+
+namespace polly {
+/// Find all loops referenced in SCEVAddRecExprs.
+class SCEVFindLoops {
+ SetVector<const Loop *> &Loops;
+
+public:
+ SCEVFindLoops(SetVector<const Loop *> &Loops) : Loops(Loops) {}
+
+ bool follow(const SCEV *S) {
+ if (const SCEVAddRecExpr *AddRec = dyn_cast<SCEVAddRecExpr>(S))
+ Loops.insert(AddRec->getLoop());
+ return true;
+ }
+ bool isDone() { return false; }
+};
+
+void findLoops(const SCEV *Expr, SetVector<const Loop *> &Loops) {
+ SCEVFindLoops FindLoops(Loops);
+ SCEVTraversal<SCEVFindLoops> ST(FindLoops);
+ ST.visitAll(Expr);
+}
+
+/// Find all values referenced in SCEVUnknowns.
+class SCEVFindValues {
+ ScalarEvolution &SE;
+ SetVector<Value *> &Values;
+
+public:
+ SCEVFindValues(ScalarEvolution &SE, SetVector<Value *> &Values)
+ : SE(SE), Values(Values) {}
+
+ bool follow(const SCEV *S) {
+ const SCEVUnknown *Unknown = dyn_cast<SCEVUnknown>(S);
+ if (!Unknown)
+ return true;
+
+ Values.insert(Unknown->getValue());
+ Instruction *Inst = dyn_cast<Instruction>(Unknown->getValue());
+ if (!Inst || (Inst->getOpcode() != Instruction::SRem &&
+ Inst->getOpcode() != Instruction::SDiv))
+ return false;
+
+ auto *Dividend = SE.getSCEV(Inst->getOperand(1));
+ if (!isa<SCEVConstant>(Dividend))
+ return false;
+
+ auto *Divisor = SE.getSCEV(Inst->getOperand(0));
+ SCEVFindValues FindValues(SE, Values);
+ SCEVTraversal<SCEVFindValues> ST(FindValues);
+ ST.visitAll(Dividend);
+ ST.visitAll(Divisor);
+
+ return false;
+ }
+ bool isDone() { return false; }
+};
+
+void findValues(const SCEV *Expr, ScalarEvolution &SE,
+ SetVector<Value *> &Values) {
+ SCEVFindValues FindValues(SE, Values);
+ SCEVTraversal<SCEVFindValues> ST(FindValues);
+ ST.visitAll(Expr);
+}
+
+bool hasScalarDepsInsideRegion(const SCEV *Expr, const Region *R,
+ llvm::Loop *Scope, bool AllowLoops,
+ const InvariantLoadsSetTy &ILS) {
+ SCEVInRegionDependences InRegionDeps(R, Scope, AllowLoops, ILS);
+ SCEVTraversal<SCEVInRegionDependences> ST(InRegionDeps);
+ ST.visitAll(Expr);
+ return InRegionDeps.hasDependences();
+}
+
+bool isAffineExpr(const Region *R, llvm::Loop *Scope, const SCEV *Expr,
+ ScalarEvolution &SE, InvariantLoadsSetTy *ILS) {
+ if (isa<SCEVCouldNotCompute>(Expr))
+ return false;
+
+ SCEVValidator Validator(R, Scope, SE, ILS);
+ LLVM_DEBUG({
+ dbgs() << "\n";
+ dbgs() << "Expr: " << *Expr << "\n";
+ dbgs() << "Region: " << R->getNameStr() << "\n";
+ dbgs() << " -> ";
+ });
+
+ ValidatorResult Result = Validator.visit(Expr);
+
+ LLVM_DEBUG({
+ if (Result.isValid())
+ dbgs() << "VALID\n";
+ dbgs() << "\n";
+ });
+
+ return Result.isValid();
+}
+
+static bool isAffineExpr(Value *V, const Region *R, Loop *Scope,
+ ScalarEvolution &SE, ParameterSetTy &Params) {
+ auto *E = SE.getSCEV(V);
+ if (isa<SCEVCouldNotCompute>(E))
+ return false;
+
+ SCEVValidator Validator(R, Scope, SE, nullptr);
+ ValidatorResult Result = Validator.visit(E);
+ if (!Result.isValid())
+ return false;
+
+ auto ResultParams = Result.getParameters();
+ Params.insert(ResultParams.begin(), ResultParams.end());
+
+ return true;
+}
+
+bool isAffineConstraint(Value *V, const Region *R, llvm::Loop *Scope,
+ ScalarEvolution &SE, ParameterSetTy &Params,
+ bool OrExpr) {
+ if (auto *ICmp = dyn_cast<ICmpInst>(V)) {
+ return isAffineConstraint(ICmp->getOperand(0), R, Scope, SE, Params,
+ true) &&
+ isAffineConstraint(ICmp->getOperand(1), R, Scope, SE, Params, true);
+ } else if (auto *BinOp = dyn_cast<BinaryOperator>(V)) {
+ auto Opcode = BinOp->getOpcode();
+ if (Opcode == Instruction::And || Opcode == Instruction::Or)
+ return isAffineConstraint(BinOp->getOperand(0), R, Scope, SE, Params,
+ false) &&
+ isAffineConstraint(BinOp->getOperand(1), R, Scope, SE, Params,
+ false);
+ /* Fall through */
+ }
+
+ if (!OrExpr)
+ return false;
+
+ return isAffineExpr(V, R, Scope, SE, Params);
+}
+
+ParameterSetTy getParamsInAffineExpr(const Region *R, Loop *Scope,
+ const SCEV *Expr, ScalarEvolution &SE) {
+ if (isa<SCEVCouldNotCompute>(Expr))
+ return ParameterSetTy();
+
+ InvariantLoadsSetTy ILS;
+ SCEVValidator Validator(R, Scope, SE, &ILS);
+ ValidatorResult Result = Validator.visit(Expr);
+ assert(Result.isValid() && "Requested parameters for an invalid SCEV!");
+
+ return Result.getParameters();
+}
+
+std::pair<const SCEVConstant *, const SCEV *>
+extractConstantFactor(const SCEV *S, ScalarEvolution &SE) {
+ auto *ConstPart = cast<SCEVConstant>(SE.getConstant(S->getType(), 1));
+
+ if (auto *Constant = dyn_cast<SCEVConstant>(S))
+ return std::make_pair(Constant, SE.getConstant(S->getType(), 1));
+
+ auto *AddRec = dyn_cast<SCEVAddRecExpr>(S);
+ if (AddRec) {
+ auto *StartExpr = AddRec->getStart();
+ if (StartExpr->isZero()) {
+ auto StepPair = extractConstantFactor(AddRec->getStepRecurrence(SE), SE);
+ auto *LeftOverAddRec =
+ SE.getAddRecExpr(StartExpr, StepPair.second, AddRec->getLoop(),
+ AddRec->getNoWrapFlags());
+ return std::make_pair(StepPair.first, LeftOverAddRec);
+ }
+ return std::make_pair(ConstPart, S);
+ }
+
+ if (auto *Add = dyn_cast<SCEVAddExpr>(S)) {
+ SmallVector<const SCEV *, 4> LeftOvers;
+ auto Op0Pair = extractConstantFactor(Add->getOperand(0), SE);
+ auto *Factor = Op0Pair.first;
+ if (SE.isKnownNegative(Factor)) {
+ Factor = cast<SCEVConstant>(SE.getNegativeSCEV(Factor));
+ LeftOvers.push_back(SE.getNegativeSCEV(Op0Pair.second));
+ } else {
+ LeftOvers.push_back(Op0Pair.second);
+ }
+
+ for (unsigned u = 1, e = Add->getNumOperands(); u < e; u++) {
+ auto OpUPair = extractConstantFactor(Add->getOperand(u), SE);
+ // TODO: Use something smarter than equality here, e.g., gcd.
+ if (Factor == OpUPair.first)
+ LeftOvers.push_back(OpUPair.second);
+ else if (Factor == SE.getNegativeSCEV(OpUPair.first))
+ LeftOvers.push_back(SE.getNegativeSCEV(OpUPair.second));
+ else
+ return std::make_pair(ConstPart, S);
+ }
+
+ auto *NewAdd = SE.getAddExpr(LeftOvers, Add->getNoWrapFlags());
+ return std::make_pair(Factor, NewAdd);
+ }
+
+ auto *Mul = dyn_cast<SCEVMulExpr>(S);
+ if (!Mul)
+ return std::make_pair(ConstPart, S);
+
+ SmallVector<const SCEV *, 4> LeftOvers;
+ for (auto *Op : Mul->operands())
+ if (isa<SCEVConstant>(Op))
+ ConstPart = cast<SCEVConstant>(SE.getMulExpr(ConstPart, Op));
+ else
+ LeftOvers.push_back(Op);
+
+ return std::make_pair(ConstPart, SE.getMulExpr(LeftOvers));
+}
+
+const SCEV *tryForwardThroughPHI(const SCEV *Expr, Region &R,
+ ScalarEvolution &SE, ScopDetection *SD) {
+ if (auto *Unknown = dyn_cast<SCEVUnknown>(Expr)) {
+ Value *V = Unknown->getValue();
+ auto *PHI = dyn_cast<PHINode>(V);
+ if (!PHI)
+ return Expr;
+
+ Value *Final = nullptr;
+
+ for (unsigned i = 0; i < PHI->getNumIncomingValues(); i++) {
+ BasicBlock *Incoming = PHI->getIncomingBlock(i);
+ if (SD->isErrorBlock(*Incoming, R) && R.contains(Incoming))
+ continue;
+ if (Final)
+ return Expr;
+ Final = PHI->getIncomingValue(i);
+ }
+
+ if (Final)
+ return SE.getSCEV(Final);
+ }
+ return Expr;
+}
+
+Value *getUniqueNonErrorValue(PHINode *PHI, Region *R, ScopDetection *SD) {
+ Value *V = nullptr;
+ for (unsigned i = 0; i < PHI->getNumIncomingValues(); i++) {
+ BasicBlock *BB = PHI->getIncomingBlock(i);
+ if (!SD->isErrorBlock(*BB, *R)) {
+ if (V)
+ return nullptr;
+ V = PHI->getIncomingValue(i);
+ }
+ }
+
+ return V;
+}
+} // namespace polly
diff --git a/contrib/libs/llvm14/tools/polly/lib/Support/ScopHelper.cpp b/contrib/libs/llvm14/tools/polly/lib/Support/ScopHelper.cpp
new file mode 100644
index 00000000000..2f4f5e74318
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Support/ScopHelper.cpp
@@ -0,0 +1,817 @@
+//===- ScopHelper.cpp - Some Helper Functions for Scop. ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Small functions that help with Scop and LLVM-IR.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/Support/ScopHelper.h"
+#include "polly/Options.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/SCEVValidator.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/LoopUtils.h"
+#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
+
+using namespace llvm;
+using namespace polly;
+
+#define DEBUG_TYPE "polly-scop-helper"
+
+static cl::list<std::string> DebugFunctions(
+ "polly-debug-func",
+ cl::desc("Allow calls to the specified functions in SCoPs even if their "
+ "side-effects are unknown. This can be used to do debug output in "
+ "Polly-transformed code."),
+ cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated, cl::cat(PollyCategory));
+
+// Ensures that there is just one predecessor to the entry node from outside the
+// region.
+// The identity of the region entry node is preserved.
+static void simplifyRegionEntry(Region *R, DominatorTree *DT, LoopInfo *LI,
+ RegionInfo *RI) {
+ BasicBlock *EnteringBB = R->getEnteringBlock();
+ BasicBlock *Entry = R->getEntry();
+
+ // Before (one of):
+ //
+ // \ / //
+ // EnteringBB //
+ // | \------> //
+ // \ / | //
+ // Entry <--\ Entry <--\ //
+ // / \ / / \ / //
+ // .... .... //
+
+ // Create single entry edge if the region has multiple entry edges.
+ if (!EnteringBB) {
+ SmallVector<BasicBlock *, 4> Preds;
+ for (BasicBlock *P : predecessors(Entry))
+ if (!R->contains(P))
+ Preds.push_back(P);
+
+ BasicBlock *NewEntering =
+ SplitBlockPredecessors(Entry, Preds, ".region_entering", DT, LI);
+
+ if (RI) {
+ // The exit block of predecessing regions must be changed to NewEntering
+ for (BasicBlock *ExitPred : predecessors(NewEntering)) {
+ Region *RegionOfPred = RI->getRegionFor(ExitPred);
+ if (RegionOfPred->getExit() != Entry)
+ continue;
+
+ while (!RegionOfPred->isTopLevelRegion() &&
+ RegionOfPred->getExit() == Entry) {
+ RegionOfPred->replaceExit(NewEntering);
+ RegionOfPred = RegionOfPred->getParent();
+ }
+ }
+
+ // Make all ancestors use EnteringBB as entry; there might be edges to it
+ Region *AncestorR = R->getParent();
+ RI->setRegionFor(NewEntering, AncestorR);
+ while (!AncestorR->isTopLevelRegion() && AncestorR->getEntry() == Entry) {
+ AncestorR->replaceEntry(NewEntering);
+ AncestorR = AncestorR->getParent();
+ }
+ }
+
+ EnteringBB = NewEntering;
+ }
+ assert(R->getEnteringBlock() == EnteringBB);
+
+ // After:
+ //
+ // \ / //
+ // EnteringBB //
+ // | //
+ // | //
+ // Entry <--\ //
+ // / \ / //
+ // .... //
+}
+
+// Ensure that the region has a single block that branches to the exit node.
+static void simplifyRegionExit(Region *R, DominatorTree *DT, LoopInfo *LI,
+ RegionInfo *RI) {
+ BasicBlock *ExitBB = R->getExit();
+ BasicBlock *ExitingBB = R->getExitingBlock();
+
+ // Before:
+ //
+ // (Region) ______/ //
+ // \ | / //
+ // ExitBB //
+ // / \ //
+
+ if (!ExitingBB) {
+ SmallVector<BasicBlock *, 4> Preds;
+ for (BasicBlock *P : predecessors(ExitBB))
+ if (R->contains(P))
+ Preds.push_back(P);
+
+ // Preds[0] Preds[1] otherBB //
+ // \ | ________/ //
+ // \ | / //
+ // BB //
+ ExitingBB =
+ SplitBlockPredecessors(ExitBB, Preds, ".region_exiting", DT, LI);
+ // Preds[0] Preds[1] otherBB //
+ // \ / / //
+ // BB.region_exiting / //
+ // \ / //
+ // BB //
+
+ if (RI)
+ RI->setRegionFor(ExitingBB, R);
+
+ // Change the exit of nested regions, but not the region itself,
+ R->replaceExitRecursive(ExitingBB);
+ R->replaceExit(ExitBB);
+ }
+ assert(ExitingBB == R->getExitingBlock());
+
+ // After:
+ //
+ // \ / //
+ // ExitingBB _____/ //
+ // \ / //
+ // ExitBB //
+ // / \ //
+}
+
+void polly::simplifyRegion(Region *R, DominatorTree *DT, LoopInfo *LI,
+ RegionInfo *RI) {
+ assert(R && !R->isTopLevelRegion());
+ assert(!RI || RI == R->getRegionInfo());
+ assert((!RI || DT) &&
+ "RegionInfo requires DominatorTree to be updated as well");
+
+ simplifyRegionEntry(R, DT, LI, RI);
+ simplifyRegionExit(R, DT, LI, RI);
+ assert(R->isSimple());
+}
+
+// Split the block into two successive blocks.
+//
+// Like llvm::SplitBlock, but also preserves RegionInfo
+static BasicBlock *splitBlock(BasicBlock *Old, Instruction *SplitPt,
+ DominatorTree *DT, llvm::LoopInfo *LI,
+ RegionInfo *RI) {
+ assert(Old && SplitPt);
+
+ // Before:
+ //
+ // \ / //
+ // Old //
+ // / \ //
+
+ BasicBlock *NewBlock = llvm::SplitBlock(Old, SplitPt, DT, LI);
+
+ if (RI) {
+ Region *R = RI->getRegionFor(Old);
+ RI->setRegionFor(NewBlock, R);
+ }
+
+ // After:
+ //
+ // \ / //
+ // Old //
+ // | //
+ // NewBlock //
+ // / \ //
+
+ return NewBlock;
+}
+
+void polly::splitEntryBlockForAlloca(BasicBlock *EntryBlock, DominatorTree *DT,
+ LoopInfo *LI, RegionInfo *RI) {
+ // Find first non-alloca instruction. Every basic block has a non-alloca
+ // instruction, as every well formed basic block has a terminator.
+ BasicBlock::iterator I = EntryBlock->begin();
+ while (isa<AllocaInst>(I))
+ ++I;
+
+ // splitBlock updates DT, LI and RI.
+ splitBlock(EntryBlock, &*I, DT, LI, RI);
+}
+
+void polly::splitEntryBlockForAlloca(BasicBlock *EntryBlock, Pass *P) {
+ auto *DTWP = P->getAnalysisIfAvailable<DominatorTreeWrapperPass>();
+ auto *DT = DTWP ? &DTWP->getDomTree() : nullptr;
+ auto *LIWP = P->getAnalysisIfAvailable<LoopInfoWrapperPass>();
+ auto *LI = LIWP ? &LIWP->getLoopInfo() : nullptr;
+ RegionInfoPass *RIP = P->getAnalysisIfAvailable<RegionInfoPass>();
+ RegionInfo *RI = RIP ? &RIP->getRegionInfo() : nullptr;
+
+ // splitBlock updates DT, LI and RI.
+ polly::splitEntryBlockForAlloca(EntryBlock, DT, LI, RI);
+}
+
+void polly::recordAssumption(polly::RecordedAssumptionsTy *RecordedAssumptions,
+ polly::AssumptionKind Kind, isl::set Set,
+ DebugLoc Loc, polly::AssumptionSign Sign,
+ BasicBlock *BB, bool RTC) {
+ assert((Set.is_params() || BB) &&
+ "Assumptions without a basic block must be parameter sets");
+ if (RecordedAssumptions)
+ RecordedAssumptions->push_back({Kind, Sign, Set, Loc, BB, RTC});
+}
+
+/// The SCEVExpander will __not__ generate any code for an existing SDiv/SRem
+/// instruction but just use it, if it is referenced as a SCEVUnknown. We want
+/// however to generate new code if the instruction is in the analyzed region
+/// and we generate code outside/in front of that region. Hence, we generate the
+/// code for the SDiv/SRem operands in front of the analyzed region and then
+/// create a new SDiv/SRem operation there too.
+struct ScopExpander : SCEVVisitor<ScopExpander, const SCEV *> {
+ friend struct SCEVVisitor<ScopExpander, const SCEV *>;
+
+ explicit ScopExpander(const Region &R, ScalarEvolution &SE,
+ const DataLayout &DL, const char *Name, ValueMapT *VMap,
+ BasicBlock *RTCBB)
+ : Expander(SE, DL, Name, /*PreserveLCSSA=*/false), SE(SE), Name(Name),
+ R(R), VMap(VMap), RTCBB(RTCBB) {}
+
+ Value *expandCodeFor(const SCEV *E, Type *Ty, Instruction *I) {
+ // If we generate code in the region we will immediately fall back to the
+ // SCEVExpander, otherwise we will stop at all unknowns in the SCEV and if
+ // needed replace them by copies computed in the entering block.
+ if (!R.contains(I))
+ E = visit(E);
+ return Expander.expandCodeFor(E, Ty, I);
+ }
+
+ const SCEV *visit(const SCEV *E) {
+ // Cache the expansion results for intermediate SCEV expressions. A SCEV
+ // expression can refer to an operand multiple times (e.g. "x*x), so
+ // a naive visitor takes exponential time.
+ if (SCEVCache.count(E))
+ return SCEVCache[E];
+ const SCEV *Result = SCEVVisitor::visit(E);
+ SCEVCache[E] = Result;
+ return Result;
+ }
+
+private:
+ SCEVExpander Expander;
+ ScalarEvolution &SE;
+ const char *Name;
+ const Region &R;
+ ValueMapT *VMap;
+ BasicBlock *RTCBB;
+ DenseMap<const SCEV *, const SCEV *> SCEVCache;
+
+ const SCEV *visitGenericInst(const SCEVUnknown *E, Instruction *Inst,
+ Instruction *IP) {
+ if (!Inst || !R.contains(Inst))
+ return E;
+
+ assert(!Inst->mayThrow() && !Inst->mayReadOrWriteMemory() &&
+ !isa<PHINode>(Inst));
+
+ auto *InstClone = Inst->clone();
+ for (auto &Op : Inst->operands()) {
+ assert(SE.isSCEVable(Op->getType()));
+ auto *OpSCEV = SE.getSCEV(Op);
+ auto *OpClone = expandCodeFor(OpSCEV, Op->getType(), IP);
+ InstClone->replaceUsesOfWith(Op, OpClone);
+ }
+
+ InstClone->setName(Name + Inst->getName());
+ InstClone->insertBefore(IP);
+ return SE.getSCEV(InstClone);
+ }
+
+ const SCEV *visitUnknown(const SCEVUnknown *E) {
+
+ // If a value mapping was given try if the underlying value is remapped.
+ Value *NewVal = VMap ? VMap->lookup(E->getValue()) : nullptr;
+ if (NewVal) {
+ auto *NewE = SE.getSCEV(NewVal);
+
+ // While the mapped value might be different the SCEV representation might
+ // not be. To this end we will check before we go into recursion here.
+ if (E != NewE)
+ return visit(NewE);
+ }
+
+ Instruction *Inst = dyn_cast<Instruction>(E->getValue());
+ Instruction *IP;
+ if (Inst && !R.contains(Inst))
+ IP = Inst;
+ else if (Inst && RTCBB->getParent() == Inst->getFunction())
+ IP = RTCBB->getTerminator();
+ else
+ IP = RTCBB->getParent()->getEntryBlock().getTerminator();
+
+ if (!Inst || (Inst->getOpcode() != Instruction::SRem &&
+ Inst->getOpcode() != Instruction::SDiv))
+ return visitGenericInst(E, Inst, IP);
+
+ const SCEV *LHSScev = SE.getSCEV(Inst->getOperand(0));
+ const SCEV *RHSScev = SE.getSCEV(Inst->getOperand(1));
+
+ if (!SE.isKnownNonZero(RHSScev))
+ RHSScev = SE.getUMaxExpr(RHSScev, SE.getConstant(E->getType(), 1));
+
+ Value *LHS = expandCodeFor(LHSScev, E->getType(), IP);
+ Value *RHS = expandCodeFor(RHSScev, E->getType(), IP);
+
+ Inst = BinaryOperator::Create((Instruction::BinaryOps)Inst->getOpcode(),
+ LHS, RHS, Inst->getName() + Name, IP);
+ return SE.getSCEV(Inst);
+ }
+
+ /// The following functions will just traverse the SCEV and rebuild it with
+ /// the new operands returned by the traversal.
+ ///
+ ///{
+ const SCEV *visitConstant(const SCEVConstant *E) { return E; }
+ const SCEV *visitPtrToIntExpr(const SCEVPtrToIntExpr *E) {
+ return SE.getPtrToIntExpr(visit(E->getOperand()), E->getType());
+ }
+ const SCEV *visitTruncateExpr(const SCEVTruncateExpr *E) {
+ return SE.getTruncateExpr(visit(E->getOperand()), E->getType());
+ }
+ const SCEV *visitZeroExtendExpr(const SCEVZeroExtendExpr *E) {
+ return SE.getZeroExtendExpr(visit(E->getOperand()), E->getType());
+ }
+ const SCEV *visitSignExtendExpr(const SCEVSignExtendExpr *E) {
+ return SE.getSignExtendExpr(visit(E->getOperand()), E->getType());
+ }
+ const SCEV *visitUDivExpr(const SCEVUDivExpr *E) {
+ auto *RHSScev = visit(E->getRHS());
+ if (!SE.isKnownNonZero(RHSScev))
+ RHSScev = SE.getUMaxExpr(RHSScev, SE.getConstant(E->getType(), 1));
+ return SE.getUDivExpr(visit(E->getLHS()), RHSScev);
+ }
+ const SCEV *visitAddExpr(const SCEVAddExpr *E) {
+ SmallVector<const SCEV *, 4> NewOps;
+ for (const SCEV *Op : E->operands())
+ NewOps.push_back(visit(Op));
+ return SE.getAddExpr(NewOps);
+ }
+ const SCEV *visitMulExpr(const SCEVMulExpr *E) {
+ SmallVector<const SCEV *, 4> NewOps;
+ for (const SCEV *Op : E->operands())
+ NewOps.push_back(visit(Op));
+ return SE.getMulExpr(NewOps);
+ }
+ const SCEV *visitUMaxExpr(const SCEVUMaxExpr *E) {
+ SmallVector<const SCEV *, 4> NewOps;
+ for (const SCEV *Op : E->operands())
+ NewOps.push_back(visit(Op));
+ return SE.getUMaxExpr(NewOps);
+ }
+ const SCEV *visitSMaxExpr(const SCEVSMaxExpr *E) {
+ SmallVector<const SCEV *, 4> NewOps;
+ for (const SCEV *Op : E->operands())
+ NewOps.push_back(visit(Op));
+ return SE.getSMaxExpr(NewOps);
+ }
+ const SCEV *visitUMinExpr(const SCEVUMinExpr *E) {
+ SmallVector<const SCEV *, 4> NewOps;
+ for (const SCEV *Op : E->operands())
+ NewOps.push_back(visit(Op));
+ return SE.getUMinExpr(NewOps);
+ }
+ const SCEV *visitSMinExpr(const SCEVSMinExpr *E) {
+ SmallVector<const SCEV *, 4> NewOps;
+ for (const SCEV *Op : E->operands())
+ NewOps.push_back(visit(Op));
+ return SE.getSMinExpr(NewOps);
+ }
+ const SCEV *visitSequentialUMinExpr(const SCEVSequentialUMinExpr *E) {
+ SmallVector<const SCEV *, 4> NewOps;
+ for (const SCEV *Op : E->operands())
+ NewOps.push_back(visit(Op));
+ return SE.getUMinExpr(NewOps, /*Sequential=*/true);
+ }
+ const SCEV *visitAddRecExpr(const SCEVAddRecExpr *E) {
+ SmallVector<const SCEV *, 4> NewOps;
+ for (const SCEV *Op : E->operands())
+ NewOps.push_back(visit(Op));
+ return SE.getAddRecExpr(NewOps, E->getLoop(), E->getNoWrapFlags());
+ }
+ ///}
+};
+
+Value *polly::expandCodeFor(Scop &S, ScalarEvolution &SE, const DataLayout &DL,
+ const char *Name, const SCEV *E, Type *Ty,
+ Instruction *IP, ValueMapT *VMap,
+ BasicBlock *RTCBB) {
+ ScopExpander Expander(S.getRegion(), SE, DL, Name, VMap, RTCBB);
+ return Expander.expandCodeFor(E, Ty, IP);
+}
+
+Value *polly::getConditionFromTerminator(Instruction *TI) {
+ if (BranchInst *BR = dyn_cast<BranchInst>(TI)) {
+ if (BR->isUnconditional())
+ return ConstantInt::getTrue(Type::getInt1Ty(TI->getContext()));
+
+ return BR->getCondition();
+ }
+
+ if (SwitchInst *SI = dyn_cast<SwitchInst>(TI))
+ return SI->getCondition();
+
+ return nullptr;
+}
+
+Loop *polly::getLoopSurroundingScop(Scop &S, LoopInfo &LI) {
+ // Start with the smallest loop containing the entry and expand that
+ // loop until it contains all blocks in the region. If there is a loop
+ // containing all blocks in the region check if it is itself contained
+ // and if so take the parent loop as it will be the smallest containing
+ // the region but not contained by it.
+ Loop *L = LI.getLoopFor(S.getEntry());
+ while (L) {
+ bool AllContained = true;
+ for (auto *BB : S.blocks())
+ AllContained &= L->contains(BB);
+ if (AllContained)
+ break;
+ L = L->getParentLoop();
+ }
+
+ return L ? (S.contains(L) ? L->getParentLoop() : L) : nullptr;
+}
+
+unsigned polly::getNumBlocksInLoop(Loop *L) {
+ unsigned NumBlocks = L->getNumBlocks();
+ SmallVector<BasicBlock *, 4> ExitBlocks;
+ L->getExitBlocks(ExitBlocks);
+
+ for (auto ExitBlock : ExitBlocks) {
+ if (isa<UnreachableInst>(ExitBlock->getTerminator()))
+ NumBlocks++;
+ }
+ return NumBlocks;
+}
+
+unsigned polly::getNumBlocksInRegionNode(RegionNode *RN) {
+ if (!RN->isSubRegion())
+ return 1;
+
+ Region *R = RN->getNodeAs<Region>();
+ return std::distance(R->block_begin(), R->block_end());
+}
+
+Loop *polly::getRegionNodeLoop(RegionNode *RN, LoopInfo &LI) {
+ if (!RN->isSubRegion()) {
+ BasicBlock *BB = RN->getNodeAs<BasicBlock>();
+ Loop *L = LI.getLoopFor(BB);
+
+ // Unreachable statements are not considered to belong to a LLVM loop, as
+ // they are not part of an actual loop in the control flow graph.
+ // Nevertheless, we handle certain unreachable statements that are common
+ // when modeling run-time bounds checks as being part of the loop to be
+ // able to model them and to later eliminate the run-time bounds checks.
+ //
+ // Specifically, for basic blocks that terminate in an unreachable and
+ // where the immediate predecessor is part of a loop, we assume these
+ // basic blocks belong to the loop the predecessor belongs to. This
+ // allows us to model the following code.
+ //
+ // for (i = 0; i < N; i++) {
+ // if (i > 1024)
+ // abort(); <- this abort might be translated to an
+ // unreachable
+ //
+ // A[i] = ...
+ // }
+ if (!L && isa<UnreachableInst>(BB->getTerminator()) && BB->getPrevNode())
+ L = LI.getLoopFor(BB->getPrevNode());
+ return L;
+ }
+
+ Region *NonAffineSubRegion = RN->getNodeAs<Region>();
+ Loop *L = LI.getLoopFor(NonAffineSubRegion->getEntry());
+ while (L && NonAffineSubRegion->contains(L))
+ L = L->getParentLoop();
+ return L;
+}
+
+static bool hasVariantIndex(GetElementPtrInst *Gep, Loop *L, Region &R,
+ ScalarEvolution &SE) {
+ for (const Use &Val : llvm::drop_begin(Gep->operands(), 1)) {
+ const SCEV *PtrSCEV = SE.getSCEVAtScope(Val, L);
+ Loop *OuterLoop = R.outermostLoopInRegion(L);
+ if (!SE.isLoopInvariant(PtrSCEV, OuterLoop))
+ return true;
+ }
+ return false;
+}
+
+bool polly::isHoistableLoad(LoadInst *LInst, Region &R, LoopInfo &LI,
+ ScalarEvolution &SE, const DominatorTree &DT,
+ const InvariantLoadsSetTy &KnownInvariantLoads) {
+ Loop *L = LI.getLoopFor(LInst->getParent());
+ auto *Ptr = LInst->getPointerOperand();
+
+ // A LoadInst is hoistable if the address it is loading from is also
+ // invariant; in this case: another invariant load (whether that address
+ // is also not written to has to be checked separately)
+ // TODO: This only checks for a LoadInst->GetElementPtrInst->LoadInst
+ // pattern generated by the Chapel frontend, but generally this applies
+ // for any chain of instruction that does not also depend on any
+ // induction variable
+ if (auto *GepInst = dyn_cast<GetElementPtrInst>(Ptr)) {
+ if (!hasVariantIndex(GepInst, L, R, SE)) {
+ if (auto *DecidingLoad =
+ dyn_cast<LoadInst>(GepInst->getPointerOperand())) {
+ if (KnownInvariantLoads.count(DecidingLoad))
+ return true;
+ }
+ }
+ }
+
+ const SCEV *PtrSCEV = SE.getSCEVAtScope(Ptr, L);
+ while (L && R.contains(L)) {
+ if (!SE.isLoopInvariant(PtrSCEV, L))
+ return false;
+ L = L->getParentLoop();
+ }
+
+ for (auto *User : Ptr->users()) {
+ auto *UserI = dyn_cast<Instruction>(User);
+ if (!UserI || !R.contains(UserI))
+ continue;
+ if (!UserI->mayWriteToMemory())
+ continue;
+
+ auto &BB = *UserI->getParent();
+ if (DT.dominates(&BB, LInst->getParent()))
+ return false;
+
+ bool DominatesAllPredecessors = true;
+ if (R.isTopLevelRegion()) {
+ for (BasicBlock &I : *R.getEntry()->getParent())
+ if (isa<ReturnInst>(I.getTerminator()) && !DT.dominates(&BB, &I))
+ DominatesAllPredecessors = false;
+ } else {
+ for (auto Pred : predecessors(R.getExit()))
+ if (R.contains(Pred) && !DT.dominates(&BB, Pred))
+ DominatesAllPredecessors = false;
+ }
+
+ if (!DominatesAllPredecessors)
+ continue;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool polly::isIgnoredIntrinsic(const Value *V) {
+ if (auto *IT = dyn_cast<IntrinsicInst>(V)) {
+ switch (IT->getIntrinsicID()) {
+ // Lifetime markers are supported/ignored.
+ case llvm::Intrinsic::lifetime_start:
+ case llvm::Intrinsic::lifetime_end:
+ // Invariant markers are supported/ignored.
+ case llvm::Intrinsic::invariant_start:
+ case llvm::Intrinsic::invariant_end:
+ // Some misc annotations are supported/ignored.
+ case llvm::Intrinsic::var_annotation:
+ case llvm::Intrinsic::ptr_annotation:
+ case llvm::Intrinsic::annotation:
+ case llvm::Intrinsic::donothing:
+ case llvm::Intrinsic::assume:
+ // Some debug info intrinsics are supported/ignored.
+ case llvm::Intrinsic::dbg_value:
+ case llvm::Intrinsic::dbg_declare:
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+bool polly::canSynthesize(const Value *V, const Scop &S, ScalarEvolution *SE,
+ Loop *Scope) {
+ if (!V || !SE->isSCEVable(V->getType()))
+ return false;
+
+ const InvariantLoadsSetTy &ILS = S.getRequiredInvariantLoads();
+ if (const SCEV *Scev = SE->getSCEVAtScope(const_cast<Value *>(V), Scope))
+ if (!isa<SCEVCouldNotCompute>(Scev))
+ if (!hasScalarDepsInsideRegion(Scev, &S.getRegion(), Scope, false, ILS))
+ return true;
+
+ return false;
+}
+
+llvm::BasicBlock *polly::getUseBlock(const llvm::Use &U) {
+ Instruction *UI = dyn_cast<Instruction>(U.getUser());
+ if (!UI)
+ return nullptr;
+
+ if (PHINode *PHI = dyn_cast<PHINode>(UI))
+ return PHI->getIncomingBlock(U);
+
+ return UI->getParent();
+}
+
+llvm::Loop *polly::getFirstNonBoxedLoopFor(llvm::Loop *L, llvm::LoopInfo &LI,
+ const BoxedLoopsSetTy &BoxedLoops) {
+ while (BoxedLoops.count(L))
+ L = L->getParentLoop();
+ return L;
+}
+
+llvm::Loop *polly::getFirstNonBoxedLoopFor(llvm::BasicBlock *BB,
+ llvm::LoopInfo &LI,
+ const BoxedLoopsSetTy &BoxedLoops) {
+ Loop *L = LI.getLoopFor(BB);
+ return getFirstNonBoxedLoopFor(L, LI, BoxedLoops);
+}
+
+bool polly::isDebugCall(Instruction *Inst) {
+ auto *CI = dyn_cast<CallInst>(Inst);
+ if (!CI)
+ return false;
+
+ Function *CF = CI->getCalledFunction();
+ if (!CF)
+ return false;
+
+ return std::find(DebugFunctions.begin(), DebugFunctions.end(),
+ CF->getName()) != DebugFunctions.end();
+}
+
+static bool hasDebugCall(BasicBlock *BB) {
+ for (Instruction &Inst : *BB) {
+ if (isDebugCall(&Inst))
+ return true;
+ }
+ return false;
+}
+
+bool polly::hasDebugCall(ScopStmt *Stmt) {
+ // Quick skip if no debug functions have been defined.
+ if (DebugFunctions.empty())
+ return false;
+
+ if (!Stmt)
+ return false;
+
+ for (Instruction *Inst : Stmt->getInstructions())
+ if (isDebugCall(Inst))
+ return true;
+
+ if (Stmt->isRegionStmt()) {
+ for (BasicBlock *RBB : Stmt->getRegion()->blocks())
+ if (RBB != Stmt->getEntryBlock() && ::hasDebugCall(RBB))
+ return true;
+ }
+
+ return false;
+}
+
+/// Find a property in a LoopID.
+static MDNode *findNamedMetadataNode(MDNode *LoopMD, StringRef Name) {
+ if (!LoopMD)
+ return nullptr;
+ for (const MDOperand &X : drop_begin(LoopMD->operands(), 1)) {
+ auto *OpNode = dyn_cast<MDNode>(X.get());
+ if (!OpNode)
+ continue;
+
+ auto *OpName = dyn_cast<MDString>(OpNode->getOperand(0));
+ if (!OpName)
+ continue;
+ if (OpName->getString() == Name)
+ return OpNode;
+ }
+ return nullptr;
+}
+
+static Optional<const MDOperand *> findNamedMetadataArg(MDNode *LoopID,
+ StringRef Name) {
+ MDNode *MD = findNamedMetadataNode(LoopID, Name);
+ if (!MD)
+ return None;
+ switch (MD->getNumOperands()) {
+ case 1:
+ return nullptr;
+ case 2:
+ return &MD->getOperand(1);
+ default:
+ llvm_unreachable("loop metadata has 0 or 1 operand");
+ }
+}
+
+Optional<Metadata *> polly::findMetadataOperand(MDNode *LoopMD,
+ StringRef Name) {
+ MDNode *MD = findNamedMetadataNode(LoopMD, Name);
+ if (!MD)
+ return None;
+ switch (MD->getNumOperands()) {
+ case 1:
+ return nullptr;
+ case 2:
+ return MD->getOperand(1).get();
+ default:
+ llvm_unreachable("loop metadata must have 0 or 1 operands");
+ }
+}
+
+static Optional<bool> getOptionalBoolLoopAttribute(MDNode *LoopID,
+ StringRef Name) {
+ MDNode *MD = findNamedMetadataNode(LoopID, Name);
+ if (!MD)
+ return None;
+ switch (MD->getNumOperands()) {
+ case 1:
+ return true;
+ case 2:
+ if (ConstantInt *IntMD =
+ mdconst::extract_or_null<ConstantInt>(MD->getOperand(1).get()))
+ return IntMD->getZExtValue();
+ return true;
+ }
+ llvm_unreachable("unexpected number of options");
+}
+
+bool polly::getBooleanLoopAttribute(MDNode *LoopID, StringRef Name) {
+ return getOptionalBoolLoopAttribute(LoopID, Name).getValueOr(false);
+}
+
+llvm::Optional<int> polly::getOptionalIntLoopAttribute(MDNode *LoopID,
+ StringRef Name) {
+ const MDOperand *AttrMD =
+ findNamedMetadataArg(LoopID, Name).getValueOr(nullptr);
+ if (!AttrMD)
+ return None;
+
+ ConstantInt *IntMD = mdconst::extract_or_null<ConstantInt>(AttrMD->get());
+ if (!IntMD)
+ return None;
+
+ return IntMD->getSExtValue();
+}
+
+bool polly::hasDisableAllTransformsHint(Loop *L) {
+ return llvm::hasDisableAllTransformsHint(L);
+}
+
+bool polly::hasDisableAllTransformsHint(llvm::MDNode *LoopID) {
+ return getBooleanLoopAttribute(LoopID, "llvm.loop.disable_nonforced");
+}
+
+isl::id polly::getIslLoopAttr(isl::ctx Ctx, BandAttr *Attr) {
+ assert(Attr && "Must be a valid BandAttr");
+
+ // The name "Loop" signals that this id contains a pointer to a BandAttr.
+ // The ScheduleOptimizer also uses the string "Inter iteration alias-free" in
+ // markers, but it's user pointer is an llvm::Value.
+ isl::id Result = isl::id::alloc(Ctx, "Loop with Metadata", Attr);
+ Result = isl::manage(isl_id_set_free_user(Result.release(), [](void *Ptr) {
+ BandAttr *Attr = reinterpret_cast<BandAttr *>(Ptr);
+ delete Attr;
+ }));
+ return Result;
+}
+
+isl::id polly::createIslLoopAttr(isl::ctx Ctx, Loop *L) {
+ if (!L)
+ return {};
+
+ // A loop without metadata does not need to be annotated.
+ MDNode *LoopID = L->getLoopID();
+ if (!LoopID)
+ return {};
+
+ BandAttr *Attr = new BandAttr();
+ Attr->OriginalLoop = L;
+ Attr->Metadata = L->getLoopID();
+
+ return getIslLoopAttr(Ctx, Attr);
+}
+
+bool polly::isLoopAttr(const isl::id &Id) {
+ if (Id.is_null())
+ return false;
+
+ return Id.get_name() == "Loop with Metadata";
+}
+
+BandAttr *polly::getLoopAttr(const isl::id &Id) {
+ if (!isLoopAttr(Id))
+ return nullptr;
+
+ return reinterpret_cast<BandAttr *>(Id.get_user());
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Support/ScopLocation.cpp b/contrib/libs/llvm14/tools/polly/lib/Support/ScopLocation.cpp
new file mode 100644
index 00000000000..01f3d68926d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Support/ScopLocation.cpp
@@ -0,0 +1,43 @@
+//=== ScopLocation.cpp - Debug location for ScopDetection ----- -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Helper function for extracting region debug information.
+//
+//===----------------------------------------------------------------------===//
+//
+#include "polly/Support/ScopLocation.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+
+using namespace llvm;
+
+namespace polly {
+
+void getDebugLocation(const Region *R, unsigned &LineBegin, unsigned &LineEnd,
+ std::string &FileName) {
+ LineBegin = -1;
+ LineEnd = 0;
+
+ for (const BasicBlock *BB : R->blocks())
+ for (const Instruction &Inst : *BB) {
+ DebugLoc DL = Inst.getDebugLoc();
+ if (!DL)
+ continue;
+
+ auto *Scope = cast<DIScope>(DL.getScope());
+
+ if (FileName.empty())
+ FileName = Scope->getFilename().str();
+
+ unsigned NewLine = DL.getLine();
+
+ LineBegin = std::min(LineBegin, NewLine);
+ LineEnd = std::max(LineEnd, NewLine);
+ }
+}
+} // namespace polly
diff --git a/contrib/libs/llvm14/tools/polly/lib/Support/VirtualInstruction.cpp b/contrib/libs/llvm14/tools/polly/lib/Support/VirtualInstruction.cpp
new file mode 100644
index 00000000000..07f8ff54324
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Support/VirtualInstruction.cpp
@@ -0,0 +1,420 @@
+//===------ VirtualInstruction.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Tools for determining which instructions are within a statement and the
+// nature of their operands.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/Support/VirtualInstruction.h"
+
+using namespace polly;
+using namespace llvm;
+
+VirtualUse VirtualUse::create(Scop *S, const Use &U, LoopInfo *LI,
+ bool Virtual) {
+ auto *UserBB = getUseBlock(U);
+ Loop *UserScope = LI->getLoopFor(UserBB);
+ Instruction *UI = dyn_cast<Instruction>(U.getUser());
+ ScopStmt *UserStmt = S->getStmtFor(UI);
+
+ // Uses by PHI nodes are always reading values written by other statements,
+ // except it is within a region statement.
+ if (PHINode *PHI = dyn_cast<PHINode>(UI)) {
+ // Handle PHI in exit block.
+ if (S->getRegion().getExit() == PHI->getParent())
+ return VirtualUse(UserStmt, U.get(), Inter, nullptr, nullptr);
+
+ if (UserStmt->getEntryBlock() != PHI->getParent())
+ return VirtualUse(UserStmt, U.get(), Intra, nullptr, nullptr);
+
+ // The MemoryAccess is expected to be set if @p Virtual is true.
+ MemoryAccess *IncomingMA = nullptr;
+ if (Virtual) {
+ if (const ScopArrayInfo *SAI =
+ S->getScopArrayInfoOrNull(PHI, MemoryKind::PHI)) {
+ IncomingMA = S->getPHIRead(SAI);
+ assert(IncomingMA->getStatement() == UserStmt);
+ }
+ }
+
+ return VirtualUse(UserStmt, U.get(), Inter, nullptr, IncomingMA);
+ }
+
+ return create(S, UserStmt, UserScope, U.get(), Virtual);
+}
+
+VirtualUse VirtualUse::create(Scop *S, ScopStmt *UserStmt, Loop *UserScope,
+ Value *Val, bool Virtual) {
+ assert(!isa<StoreInst>(Val) && "a StoreInst cannot be used");
+
+ if (isa<BasicBlock>(Val))
+ return VirtualUse(UserStmt, Val, Block, nullptr, nullptr);
+
+ if (isa<llvm::Constant>(Val) || isa<MetadataAsValue>(Val) ||
+ isa<InlineAsm>(Val))
+ return VirtualUse(UserStmt, Val, Constant, nullptr, nullptr);
+
+ // Is the value synthesizable? If the user has been pruned
+ // (UserStmt == nullptr), it is either not used anywhere or is synthesizable.
+ // We assume synthesizable which practically should have the same effect.
+ auto *SE = S->getSE();
+ if (SE->isSCEVable(Val->getType())) {
+ auto *ScevExpr = SE->getSCEVAtScope(Val, UserScope);
+ if (!UserStmt || canSynthesize(Val, *UserStmt->getParent(), SE, UserScope))
+ return VirtualUse(UserStmt, Val, Synthesizable, ScevExpr, nullptr);
+ }
+
+ // FIXME: Inconsistency between lookupInvariantEquivClass and
+ // getRequiredInvariantLoads. Querying one of them should be enough.
+ auto &RIL = S->getRequiredInvariantLoads();
+ if (S->lookupInvariantEquivClass(Val) || RIL.count(dyn_cast<LoadInst>(Val)))
+ return VirtualUse(UserStmt, Val, Hoisted, nullptr, nullptr);
+
+ // ReadOnly uses may have MemoryAccesses that we want to associate with the
+ // use. This is why we look for a MemoryAccess here already.
+ MemoryAccess *InputMA = nullptr;
+ if (UserStmt && Virtual)
+ InputMA = UserStmt->lookupValueReadOf(Val);
+
+ // Uses are read-only if they have been defined before the SCoP, i.e., they
+ // cannot be written to inside the SCoP. Arguments are defined before any
+ // instructions, hence also before the SCoP. If the user has been pruned
+ // (UserStmt == nullptr) and is not SCEVable, assume it is read-only as it is
+ // neither an intra- nor an inter-use.
+ if (!UserStmt || isa<Argument>(Val))
+ return VirtualUse(UserStmt, Val, ReadOnly, nullptr, InputMA);
+
+ auto Inst = cast<Instruction>(Val);
+ if (!S->contains(Inst))
+ return VirtualUse(UserStmt, Val, ReadOnly, nullptr, InputMA);
+
+ // A use is inter-statement if either it is defined in another statement, or
+ // there is a MemoryAccess that reads its value that has been written by
+ // another statement.
+ if (InputMA || (!Virtual && UserStmt != S->getStmtFor(Inst)))
+ return VirtualUse(UserStmt, Val, Inter, nullptr, InputMA);
+
+ return VirtualUse(UserStmt, Val, Intra, nullptr, nullptr);
+}
+
+void VirtualUse::print(raw_ostream &OS, bool Reproducible) const {
+ OS << "User: [" << User->getBaseName() << "] ";
+ switch (Kind) {
+ case VirtualUse::Constant:
+ OS << "Constant Op:";
+ break;
+ case VirtualUse::Block:
+ OS << "BasicBlock Op:";
+ break;
+ case VirtualUse::Synthesizable:
+ OS << "Synthesizable Op:";
+ break;
+ case VirtualUse::Hoisted:
+ OS << "Hoisted load Op:";
+ break;
+ case VirtualUse::ReadOnly:
+ OS << "Read-Only Op:";
+ break;
+ case VirtualUse::Intra:
+ OS << "Intra Op:";
+ break;
+ case VirtualUse::Inter:
+ OS << "Inter Op:";
+ break;
+ }
+
+ if (Val) {
+ OS << ' ';
+ if (Reproducible)
+ OS << '"' << Val->getName() << '"';
+ else
+ Val->print(OS, true);
+ }
+ if (ScevExpr) {
+ OS << ' ';
+ ScevExpr->print(OS);
+ }
+ if (InputMA && !Reproducible)
+ OS << ' ' << InputMA;
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void VirtualUse::dump() const {
+ print(errs(), false);
+ errs() << '\n';
+}
+#endif
+
+void VirtualInstruction::print(raw_ostream &OS, bool Reproducible) const {
+ if (!Stmt || !Inst) {
+ OS << "[null VirtualInstruction]";
+ return;
+ }
+
+ OS << "[" << Stmt->getBaseName() << "]";
+ Inst->print(OS, !Reproducible);
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void VirtualInstruction::dump() const {
+ print(errs(), false);
+ errs() << '\n';
+}
+#endif
+
+/// Return true if @p Inst cannot be removed, even if it is nowhere referenced.
+static bool isRoot(const Instruction *Inst) {
+ // The store is handled by its MemoryAccess. The load must be reached from the
+ // roots in order to be marked as used.
+ if (isa<LoadInst>(Inst) || isa<StoreInst>(Inst))
+ return false;
+
+ // Terminator instructions (in region statements) are required for control
+ // flow.
+ if (Inst->isTerminator())
+ return true;
+
+ // Writes to memory must be honored.
+ if (Inst->mayWriteToMemory())
+ return true;
+
+ return false;
+}
+
+/// Return true for MemoryAccesses that cannot be removed because it represents
+/// an llvm::Value that is used after the SCoP.
+static bool isEscaping(MemoryAccess *MA) {
+ assert(MA->isOriginalValueKind());
+ Scop *S = MA->getStatement()->getParent();
+ return S->isEscaping(cast<Instruction>(MA->getAccessValue()));
+}
+
+/// Add non-removable virtual instructions in @p Stmt to @p RootInsts.
+static void
+addInstructionRoots(ScopStmt *Stmt,
+ SmallVectorImpl<VirtualInstruction> &RootInsts) {
+ if (!Stmt->isBlockStmt()) {
+ // In region statements the terminator statement and all statements that
+ // are not in the entry block cannot be eliminated and consequently must
+ // be roots.
+ RootInsts.emplace_back(Stmt,
+ Stmt->getRegion()->getEntry()->getTerminator());
+ for (BasicBlock *BB : Stmt->getRegion()->blocks())
+ if (Stmt->getRegion()->getEntry() != BB)
+ for (Instruction &Inst : *BB)
+ RootInsts.emplace_back(Stmt, &Inst);
+ return;
+ }
+
+ for (Instruction *Inst : Stmt->getInstructions())
+ if (isRoot(Inst))
+ RootInsts.emplace_back(Stmt, Inst);
+}
+
+/// Add non-removable memory accesses in @p Stmt to @p RootInsts.
+///
+/// @param Local If true, all writes are assumed to escape. markAndSweep
+/// algorithms can use this to be applicable to a single ScopStmt only without
+/// the risk of removing definitions required by other statements.
+/// If false, only writes for SCoP-escaping values are roots. This
+/// is global mode, where such writes must be marked by theirs uses
+/// in order to be reachable.
+static void addAccessRoots(ScopStmt *Stmt,
+ SmallVectorImpl<MemoryAccess *> &RootAccs,
+ bool Local) {
+ for (auto *MA : *Stmt) {
+ if (!MA->isWrite())
+ continue;
+
+ // Writes to arrays are always used.
+ if (MA->isLatestArrayKind())
+ RootAccs.push_back(MA);
+
+ // Values are roots if they are escaping.
+ else if (MA->isLatestValueKind()) {
+ if (Local || isEscaping(MA))
+ RootAccs.push_back(MA);
+ }
+
+ // Exit phis are, by definition, escaping.
+ else if (MA->isLatestExitPHIKind())
+ RootAccs.push_back(MA);
+
+ // phi writes are only roots if we are not visiting the statement
+ // containing the PHINode.
+ else if (Local && MA->isLatestPHIKind())
+ RootAccs.push_back(MA);
+ }
+}
+
+/// Determine all instruction and access roots.
+static void addRoots(ScopStmt *Stmt,
+ SmallVectorImpl<VirtualInstruction> &RootInsts,
+ SmallVectorImpl<MemoryAccess *> &RootAccs, bool Local) {
+ addInstructionRoots(Stmt, RootInsts);
+ addAccessRoots(Stmt, RootAccs, Local);
+}
+
+/// Mark accesses and instructions as used if they are reachable from a root,
+/// walking the operand trees.
+///
+/// @param S The SCoP to walk.
+/// @param LI The LoopInfo Analysis.
+/// @param RootInsts List of root instructions.
+/// @param RootAccs List of root accesses.
+/// @param UsesInsts[out] Receives all reachable instructions, including the
+/// roots.
+/// @param UsedAccs[out] Receives all reachable accesses, including the roots.
+/// @param OnlyLocal If non-nullptr, restricts walking to a single
+/// statement.
+static void walkReachable(Scop *S, LoopInfo *LI,
+ ArrayRef<VirtualInstruction> RootInsts,
+ ArrayRef<MemoryAccess *> RootAccs,
+ DenseSet<VirtualInstruction> &UsedInsts,
+ DenseSet<MemoryAccess *> &UsedAccs,
+ ScopStmt *OnlyLocal = nullptr) {
+ UsedInsts.clear();
+ UsedAccs.clear();
+
+ SmallVector<VirtualInstruction, 32> WorklistInsts;
+ SmallVector<MemoryAccess *, 32> WorklistAccs;
+
+ WorklistInsts.append(RootInsts.begin(), RootInsts.end());
+ WorklistAccs.append(RootAccs.begin(), RootAccs.end());
+
+ auto AddToWorklist = [&](VirtualUse VUse) {
+ switch (VUse.getKind()) {
+ case VirtualUse::Block:
+ case VirtualUse::Constant:
+ case VirtualUse::Synthesizable:
+ case VirtualUse::Hoisted:
+ break;
+ case VirtualUse::ReadOnly:
+ // Read-only scalars only have MemoryAccesses if ModelReadOnlyScalars is
+ // enabled.
+ if (!VUse.getMemoryAccess())
+ break;
+ LLVM_FALLTHROUGH;
+ case VirtualUse::Inter:
+ assert(VUse.getMemoryAccess());
+ WorklistAccs.push_back(VUse.getMemoryAccess());
+ break;
+ case VirtualUse::Intra:
+ WorklistInsts.emplace_back(VUse.getUser(),
+ cast<Instruction>(VUse.getValue()));
+ break;
+ }
+ };
+
+ while (true) {
+ // We have two worklists to process: Only when the MemoryAccess worklist is
+ // empty, we process the instruction worklist.
+
+ while (!WorklistAccs.empty()) {
+ auto *Acc = WorklistAccs.pop_back_val();
+
+ ScopStmt *Stmt = Acc->getStatement();
+ if (OnlyLocal && Stmt != OnlyLocal)
+ continue;
+
+ auto Inserted = UsedAccs.insert(Acc);
+ if (!Inserted.second)
+ continue;
+
+ if (Acc->isRead()) {
+ const ScopArrayInfo *SAI = Acc->getScopArrayInfo();
+
+ if (Acc->isLatestValueKind()) {
+ MemoryAccess *DefAcc = S->getValueDef(SAI);
+
+ // Accesses to read-only values do not have a definition.
+ if (DefAcc)
+ WorklistAccs.push_back(S->getValueDef(SAI));
+ }
+
+ if (Acc->isLatestAnyPHIKind()) {
+ auto IncomingMAs = S->getPHIIncomings(SAI);
+ WorklistAccs.append(IncomingMAs.begin(), IncomingMAs.end());
+ }
+ }
+
+ if (Acc->isWrite()) {
+ if (Acc->isOriginalValueKind() ||
+ (Acc->isOriginalArrayKind() && Acc->getAccessValue())) {
+ Loop *Scope = Stmt->getSurroundingLoop();
+ VirtualUse VUse =
+ VirtualUse::create(S, Stmt, Scope, Acc->getAccessValue(), true);
+ AddToWorklist(VUse);
+ }
+
+ if (Acc->isOriginalAnyPHIKind()) {
+ for (auto Incoming : Acc->getIncoming()) {
+ VirtualUse VUse = VirtualUse::create(
+ S, Stmt, LI->getLoopFor(Incoming.first), Incoming.second, true);
+ AddToWorklist(VUse);
+ }
+ }
+
+ if (Acc->isOriginalArrayKind())
+ WorklistInsts.emplace_back(Stmt, Acc->getAccessInstruction());
+ }
+ }
+
+ // If both worklists are empty, stop walking.
+ if (WorklistInsts.empty())
+ break;
+
+ VirtualInstruction VInst = WorklistInsts.pop_back_val();
+ ScopStmt *Stmt = VInst.getStmt();
+ Instruction *Inst = VInst.getInstruction();
+
+ // Do not process statements other than the local.
+ if (OnlyLocal && Stmt != OnlyLocal)
+ continue;
+
+ auto InsertResult = UsedInsts.insert(VInst);
+ if (!InsertResult.second)
+ continue;
+
+ // Add all operands to the worklists.
+ PHINode *PHI = dyn_cast<PHINode>(Inst);
+ if (PHI && PHI->getParent() == Stmt->getEntryBlock()) {
+ if (MemoryAccess *PHIRead = Stmt->lookupPHIReadOf(PHI))
+ WorklistAccs.push_back(PHIRead);
+ } else {
+ for (VirtualUse VUse : VInst.operands())
+ AddToWorklist(VUse);
+ }
+
+ // If there is an array access, also add its MemoryAccesses to the worklist.
+ const MemoryAccessList *Accs = Stmt->lookupArrayAccessesFor(Inst);
+ if (!Accs)
+ continue;
+
+ for (MemoryAccess *Acc : *Accs)
+ WorklistAccs.push_back(Acc);
+ }
+}
+
+void polly::markReachable(Scop *S, LoopInfo *LI,
+ DenseSet<VirtualInstruction> &UsedInsts,
+ DenseSet<MemoryAccess *> &UsedAccs,
+ ScopStmt *OnlyLocal) {
+ SmallVector<VirtualInstruction, 32> RootInsts;
+ SmallVector<MemoryAccess *, 32> RootAccs;
+
+ if (OnlyLocal) {
+ addRoots(OnlyLocal, RootInsts, RootAccs, true);
+ } else {
+ for (auto &Stmt : *S)
+ addRoots(&Stmt, RootInsts, RootAccs, false);
+ }
+
+ walkReachable(S, LI, RootInsts, RootAccs, UsedInsts, UsedAccs, OnlyLocal);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/Canonicalization.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/Canonicalization.cpp
new file mode 100644
index 00000000000..d65d626df0c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/Canonicalization.cpp
@@ -0,0 +1,178 @@
+//===---- Canonicalization.cpp - Run canonicalization passes --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Run the set of default canonicalization passes.
+//
+// This pass is mainly used for debugging.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/Canonicalization.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "llvm/Analysis/GlobalsModRef.h"
+#include "llvm/Analysis/ProfileSummaryInfo.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/FunctionAttrs.h"
+#include "llvm/Transforms/InstCombine/InstCombine.h"
+#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/Scalar/EarlyCSE.h"
+#include "llvm/Transforms/Scalar/IndVarSimplify.h"
+#include "llvm/Transforms/Scalar/LoopRotation.h"
+#include "llvm/Transforms/Scalar/Reassociate.h"
+#include "llvm/Transforms/Scalar/SimplifyCFG.h"
+#include "llvm/Transforms/Scalar/TailRecursionElimination.h"
+#include "llvm/Transforms/Utils.h"
+#include "llvm/Transforms/Utils/Mem2Reg.h"
+
+using namespace llvm;
+using namespace polly;
+
+static cl::opt<bool>
+ PollyInliner("polly-run-inliner",
+ cl::desc("Run an early inliner pass before Polly"), cl::Hidden,
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+void polly::registerCanonicalicationPasses(llvm::legacy::PassManagerBase &PM) {
+ bool UseMemSSA = true;
+ PM.add(llvm::createPromoteMemoryToRegisterPass());
+ PM.add(llvm::createEarlyCSEPass(UseMemSSA));
+ PM.add(llvm::createInstructionCombiningPass());
+ PM.add(llvm::createCFGSimplificationPass());
+ PM.add(llvm::createTailCallEliminationPass());
+ PM.add(llvm::createCFGSimplificationPass());
+ PM.add(llvm::createReassociatePass());
+ PM.add(llvm::createLoopRotatePass());
+ if (PollyInliner) {
+ PM.add(llvm::createFunctionInliningPass(200));
+ PM.add(llvm::createPromoteMemoryToRegisterPass());
+ PM.add(llvm::createCFGSimplificationPass());
+ PM.add(llvm::createInstructionCombiningPass());
+ PM.add(createBarrierNoopPass());
+ }
+ PM.add(llvm::createInstructionCombiningPass());
+ PM.add(llvm::createIndVarSimplifyPass());
+}
+
+/// Adapted from llvm::PassBuilder::buildInlinerPipeline
+static ModuleInlinerWrapperPass
+buildInlinePasses(llvm::OptimizationLevel Level) {
+ InlineParams IP = getInlineParams(200);
+ ModuleInlinerWrapperPass MIWP(IP);
+
+ // Require the GlobalsAA analysis for the module so we can query it within
+ // the CGSCC pipeline.
+ MIWP.addModulePass(RequireAnalysisPass<GlobalsAA, Module>());
+ // Invalidate AAManager so it can be recreated and pick up the newly available
+ // GlobalsAA.
+ MIWP.addModulePass(
+ createModuleToFunctionPassAdaptor(InvalidateAnalysisPass<AAManager>()));
+
+ // Require the ProfileSummaryAnalysis for the module so we can query it within
+ // the inliner pass.
+ MIWP.addModulePass(RequireAnalysisPass<ProfileSummaryAnalysis, Module>());
+
+ // Now begin the main postorder CGSCC pipeline.
+ // FIXME: The current CGSCC pipeline has its origins in the legacy pass
+ // manager and trying to emulate its precise behavior. Much of this doesn't
+ // make a lot of sense and we should revisit the core CGSCC structure.
+ CGSCCPassManager &MainCGPipeline = MIWP.getPM();
+
+ // Now deduce any function attributes based in the current code.
+ MainCGPipeline.addPass(PostOrderFunctionAttrsPass());
+
+ return MIWP;
+}
+
+FunctionPassManager
+polly::buildCanonicalicationPassesForNPM(llvm::ModulePassManager &MPM,
+ llvm::OptimizationLevel Level) {
+ FunctionPassManager FPM;
+
+ bool UseMemSSA = true;
+ FPM.addPass(PromotePass());
+ FPM.addPass(EarlyCSEPass(UseMemSSA));
+ FPM.addPass(InstCombinePass());
+ FPM.addPass(SimplifyCFGPass());
+ FPM.addPass(TailCallElimPass());
+ FPM.addPass(SimplifyCFGPass());
+ FPM.addPass(ReassociatePass());
+ {
+ LoopPassManager LPM;
+ LPM.addPass(LoopRotatePass(Level != OptimizationLevel::Oz));
+ FPM.addPass(createFunctionToLoopPassAdaptor<LoopPassManager>(
+ std::move(LPM), /*UseMemorySSA=*/false,
+ /*UseBlockFrequencyInfo=*/false));
+ }
+ if (PollyInliner) {
+ MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+ MPM.addPass(buildInlinePasses(Level));
+ FPM = FunctionPassManager();
+
+ FPM.addPass(PromotePass());
+ FPM.addPass(SimplifyCFGPass());
+ FPM.addPass(InstCombinePass());
+ }
+ FPM.addPass(InstCombinePass());
+ {
+ LoopPassManager LPM;
+ LPM.addPass(IndVarSimplifyPass());
+ FPM.addPass(createFunctionToLoopPassAdaptor<LoopPassManager>(
+ std::move(LPM), /*UseMemorySSA=*/false,
+ /*UseBlockFrequencyInfo=*/true));
+ }
+
+ return FPM;
+}
+
+namespace {
+class PollyCanonicalize : public ModulePass {
+ PollyCanonicalize(const PollyCanonicalize &) = delete;
+ const PollyCanonicalize &operator=(const PollyCanonicalize &) = delete;
+
+public:
+ static char ID;
+
+ explicit PollyCanonicalize() : ModulePass(ID) {}
+ ~PollyCanonicalize();
+
+ /// @name FunctionPass interface.
+ //@{
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+ void releaseMemory() override;
+ bool runOnModule(Module &M) override;
+ void print(raw_ostream &OS, const Module *) const override;
+ //@}
+};
+} // namespace
+
+PollyCanonicalize::~PollyCanonicalize() {}
+
+void PollyCanonicalize::getAnalysisUsage(AnalysisUsage &AU) const {}
+
+void PollyCanonicalize::releaseMemory() {}
+
+bool PollyCanonicalize::runOnModule(Module &M) {
+ legacy::PassManager PM;
+ registerCanonicalicationPasses(PM);
+ PM.run(M);
+
+ return true;
+}
+
+void PollyCanonicalize::print(raw_ostream &OS, const Module *) const {}
+
+char PollyCanonicalize::ID = 0;
+
+Pass *polly::createPollyCanonicalizePass() { return new PollyCanonicalize(); }
+
+INITIALIZE_PASS_BEGIN(PollyCanonicalize, "polly-canonicalize",
+ "Polly - Run canonicalization passes", false, false)
+INITIALIZE_PASS_END(PollyCanonicalize, "polly-canonicalize",
+ "Polly - Run canonicalization passes", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/CodePreparation.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/CodePreparation.cpp
new file mode 100644
index 00000000000..dfbf616ff96
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/CodePreparation.cpp
@@ -0,0 +1,120 @@
+//===---- CodePreparation.cpp - Code preparation for Scop Detection -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The Polly code preparation pass is executed before SCoP detection. Its
+// currently only splits the entry block of the SCoP to make room for alloc
+// instructions as they are generated during code generation.
+//
+// XXX: In the future, we should remove the need for this pass entirely and
+// instead add this spitting to the code generation pass.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/CodePreparation.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/Analysis/DominanceFrontier.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/InitializePasses.h"
+
+using namespace llvm;
+using namespace polly;
+
+namespace {
+
+/// Prepare the IR for the scop detection.
+///
+class CodePreparation : public FunctionPass {
+ CodePreparation(const CodePreparation &) = delete;
+ const CodePreparation &operator=(const CodePreparation &) = delete;
+
+ LoopInfo *LI;
+ ScalarEvolution *SE;
+
+ void clear();
+
+public:
+ static char ID;
+
+ explicit CodePreparation() : FunctionPass(ID) {}
+ ~CodePreparation();
+
+ /// @name FunctionPass interface.
+ //@{
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+ void releaseMemory() override;
+ bool runOnFunction(Function &F) override;
+ void print(raw_ostream &OS, const Module *) const override;
+ //@}
+};
+} // namespace
+
+PreservedAnalyses CodePreparationPass::run(Function &F,
+ FunctionAnalysisManager &FAM) {
+
+ // Find first non-alloca instruction. Every basic block has a non-alloca
+ // instruction, as every well formed basic block has a terminator.
+ auto &EntryBlock = F.getEntryBlock();
+ BasicBlock::iterator I = EntryBlock.begin();
+ while (isa<AllocaInst>(I))
+ ++I;
+
+ auto &DT = FAM.getResult<DominatorTreeAnalysis>(F);
+ auto &LI = FAM.getResult<LoopAnalysis>(F);
+
+ // splitBlock updates DT, LI and RI.
+ splitEntryBlockForAlloca(&EntryBlock, &DT, &LI, nullptr);
+
+ PreservedAnalyses PA;
+ PA.preserve<DominatorTreeAnalysis>();
+ PA.preserve<LoopAnalysis>();
+ return PA;
+}
+
+void CodePreparation::clear() {}
+
+CodePreparation::~CodePreparation() { clear(); }
+
+void CodePreparation::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequired<LoopInfoWrapperPass>();
+ AU.addRequired<ScalarEvolutionWrapperPass>();
+
+ AU.addPreserved<LoopInfoWrapperPass>();
+ AU.addPreserved<RegionInfoPass>();
+ AU.addPreserved<DominatorTreeWrapperPass>();
+ AU.addPreserved<DominanceFrontierWrapperPass>();
+}
+
+bool CodePreparation::runOnFunction(Function &F) {
+ if (skipFunction(F))
+ return false;
+
+ LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+ SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
+
+ splitEntryBlockForAlloca(&F.getEntryBlock(), this);
+
+ return true;
+}
+
+void CodePreparation::releaseMemory() { clear(); }
+
+void CodePreparation::print(raw_ostream &OS, const Module *) const {}
+
+char CodePreparation::ID = 0;
+char &polly::CodePreparationID = CodePreparation::ID;
+
+Pass *polly::createCodePreparationPass() { return new CodePreparation(); }
+
+INITIALIZE_PASS_BEGIN(CodePreparation, "polly-prepare",
+ "Polly - Prepare code for polly", false, false)
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
+INITIALIZE_PASS_END(CodePreparation, "polly-prepare",
+ "Polly - Prepare code for polly", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/DeLICM.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/DeLICM.cpp
new file mode 100644
index 00000000000..d5a54cd7c70
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/DeLICM.cpp
@@ -0,0 +1,1496 @@
+//===------ DeLICM.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Undo the effect of Loop Invariant Code Motion (LICM) and
+// GVN Partial Redundancy Elimination (PRE) on SCoP-level.
+//
+// Namely, remove register/scalar dependencies by mapping them back to array
+// elements.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/DeLICM.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "polly/ScopInfo.h"
+#include "polly/ScopPass.h"
+#include "polly/Support/GICHelper.h"
+#include "polly/Support/ISLOStream.h"
+#include "polly/Support/ISLTools.h"
+#include "polly/ZoneAlgo.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/InitializePasses.h"
+
+#define DEBUG_TYPE "polly-delicm"
+
+using namespace polly;
+using namespace llvm;
+
+namespace {
+
+cl::opt<int>
+ DelicmMaxOps("polly-delicm-max-ops",
+ cl::desc("Maximum number of isl operations to invest for "
+ "lifetime analysis; 0=no limit"),
+ cl::init(1000000), cl::cat(PollyCategory));
+
+cl::opt<bool> DelicmOverapproximateWrites(
+ "polly-delicm-overapproximate-writes",
+ cl::desc(
+ "Do more PHI writes than necessary in order to avoid partial accesses"),
+ cl::init(false), cl::Hidden, cl::cat(PollyCategory));
+
+cl::opt<bool> DelicmPartialWrites("polly-delicm-partial-writes",
+ cl::desc("Allow partial writes"),
+ cl::init(true), cl::Hidden,
+ cl::cat(PollyCategory));
+
+cl::opt<bool>
+ DelicmComputeKnown("polly-delicm-compute-known",
+ cl::desc("Compute known content of array elements"),
+ cl::init(true), cl::Hidden, cl::cat(PollyCategory));
+
+STATISTIC(DeLICMAnalyzed, "Number of successfully analyzed SCoPs");
+STATISTIC(DeLICMOutOfQuota,
+ "Analyses aborted because max_operations was reached");
+STATISTIC(MappedValueScalars, "Number of mapped Value scalars");
+STATISTIC(MappedPHIScalars, "Number of mapped PHI scalars");
+STATISTIC(TargetsMapped, "Number of stores used for at least one mapping");
+STATISTIC(DeLICMScopsModified, "Number of SCoPs optimized");
+
+STATISTIC(NumValueWrites, "Number of scalar value writes after DeLICM");
+STATISTIC(NumValueWritesInLoops,
+ "Number of scalar value writes nested in affine loops after DeLICM");
+STATISTIC(NumPHIWrites, "Number of scalar phi writes after DeLICM");
+STATISTIC(NumPHIWritesInLoops,
+ "Number of scalar phi writes nested in affine loops after DeLICM");
+STATISTIC(NumSingletonWrites, "Number of singleton writes after DeLICM");
+STATISTIC(NumSingletonWritesInLoops,
+ "Number of singleton writes nested in affine loops after DeLICM");
+
+isl::union_map computeReachingOverwrite(isl::union_map Schedule,
+ isl::union_map Writes,
+ bool InclPrevWrite,
+ bool InclOverwrite) {
+ return computeReachingWrite(Schedule, Writes, true, InclPrevWrite,
+ InclOverwrite);
+}
+
+/// Compute the next overwrite for a scalar.
+///
+/// @param Schedule { DomainWrite[] -> Scatter[] }
+/// Schedule of (at least) all writes. Instances not in @p
+/// Writes are ignored.
+/// @param Writes { DomainWrite[] }
+/// The element instances that write to the scalar.
+/// @param InclPrevWrite Whether to extend the timepoints to include
+/// the timepoint where the previous write happens.
+/// @param InclOverwrite Whether the reaching overwrite includes the timepoint
+/// of the overwrite itself.
+///
+/// @return { Scatter[] -> DomainDef[] }
+isl::union_map computeScalarReachingOverwrite(isl::union_map Schedule,
+ isl::union_set Writes,
+ bool InclPrevWrite,
+ bool InclOverwrite) {
+
+ // { DomainWrite[] }
+ auto WritesMap = isl::union_map::from_domain(Writes);
+
+ // { [Element[] -> Scatter[]] -> DomainWrite[] }
+ auto Result = computeReachingOverwrite(
+ std::move(Schedule), std::move(WritesMap), InclPrevWrite, InclOverwrite);
+
+ return Result.domain_factor_range();
+}
+
+/// Overload of computeScalarReachingOverwrite, with only one writing statement.
+/// Consequently, the result consists of only one map space.
+///
+/// @param Schedule { DomainWrite[] -> Scatter[] }
+/// @param Writes { DomainWrite[] }
+/// @param InclPrevWrite Include the previous write to result.
+/// @param InclOverwrite Include the overwrite to the result.
+///
+/// @return { Scatter[] -> DomainWrite[] }
+isl::map computeScalarReachingOverwrite(isl::union_map Schedule,
+ isl::set Writes, bool InclPrevWrite,
+ bool InclOverwrite) {
+ isl::space ScatterSpace = getScatterSpace(Schedule);
+ isl::space DomSpace = Writes.get_space();
+
+ isl::union_map ReachOverwrite = computeScalarReachingOverwrite(
+ Schedule, isl::union_set(Writes), InclPrevWrite, InclOverwrite);
+
+ isl::space ResultSpace = ScatterSpace.map_from_domain_and_range(DomSpace);
+ return singleton(std::move(ReachOverwrite), ResultSpace);
+}
+
+/// Try to find a 'natural' extension of a mapped to elements outside its
+/// domain.
+///
+/// @param Relevant The map with mapping that may not be modified.
+/// @param Universe The domain to which @p Relevant needs to be extended.
+///
+/// @return A map with that associates the domain elements of @p Relevant to the
+/// same elements and in addition the elements of @p Universe to some
+/// undefined elements. The function prefers to return simple maps.
+isl::union_map expandMapping(isl::union_map Relevant, isl::union_set Universe) {
+ Relevant = Relevant.coalesce();
+ isl::union_set RelevantDomain = Relevant.domain();
+ isl::union_map Simplified = Relevant.gist_domain(RelevantDomain);
+ Simplified = Simplified.coalesce();
+ return Simplified.intersect_domain(Universe);
+}
+
+/// Represent the knowledge of the contents of any array elements in any zone or
+/// the knowledge we would add when mapping a scalar to an array element.
+///
+/// Every array element at every zone unit has one of two states:
+///
+/// - Unused: Not occupied by any value so a transformation can change it to
+/// other values.
+///
+/// - Occupied: The element contains a value that is still needed.
+///
+/// The union of Unused and Unknown zones forms the universe, the set of all
+/// elements at every timepoint. The universe can easily be derived from the
+/// array elements that are accessed someway. Arrays that are never accessed
+/// also never play a role in any computation and can hence be ignored. With a
+/// given universe, only one of the sets needs to stored implicitly. Computing
+/// the complement is also an expensive operation, hence this class has been
+/// designed that only one of sets is needed while the other is assumed to be
+/// implicit. It can still be given, but is mostly ignored.
+///
+/// There are two use cases for the Knowledge class:
+///
+/// 1) To represent the knowledge of the current state of ScopInfo. The unused
+/// state means that an element is currently unused: there is no read of it
+/// before the next overwrite. Also called 'Existing'.
+///
+/// 2) To represent the requirements for mapping a scalar to array elements. The
+/// unused state means that there is no change/requirement. Also called
+/// 'Proposed'.
+///
+/// In addition to these states at unit zones, Knowledge needs to know when
+/// values are written. This is because written values may have no lifetime (one
+/// reason is that the value is never read). Such writes would therefore never
+/// conflict, but overwrite values that might still be required. Another source
+/// of problems are multiple writes to the same element at the same timepoint,
+/// because their order is undefined.
+class Knowledge {
+private:
+ /// { [Element[] -> Zone[]] }
+ /// Set of array elements and when they are alive.
+ /// Can contain a nullptr; in this case the set is implicitly defined as the
+ /// complement of #Unused.
+ ///
+ /// The set of alive array elements is represented as zone, as the set of live
+ /// values can differ depending on how the elements are interpreted.
+ /// Assuming a value X is written at timestep [0] and read at timestep [1]
+ /// without being used at any later point, then the value is alive in the
+ /// interval ]0,1[. This interval cannot be represented by an integer set, as
+ /// it does not contain any integer point. Zones allow us to represent this
+ /// interval and can be converted to sets of timepoints when needed (e.g., in
+ /// isConflicting when comparing to the write sets).
+ /// @see convertZoneToTimepoints and this file's comment for more details.
+ isl::union_set Occupied;
+
+ /// { [Element[] -> Zone[]] }
+ /// Set of array elements when they are not alive, i.e. their memory can be
+ /// used for other purposed. Can contain a nullptr; in this case the set is
+ /// implicitly defined as the complement of #Occupied.
+ isl::union_set Unused;
+
+ /// { [Element[] -> Zone[]] -> ValInst[] }
+ /// Maps to the known content for each array element at any interval.
+ ///
+ /// Any element/interval can map to multiple known elements. This is due to
+ /// multiple llvm::Value referring to the same content. Examples are
+ ///
+ /// - A value stored and loaded again. The LoadInst represents the same value
+ /// as the StoreInst's value operand.
+ ///
+ /// - A PHINode is equal to any one of the incoming values. In case of
+ /// LCSSA-form, it is always equal to its single incoming value.
+ ///
+ /// Two Knowledges are considered not conflicting if at least one of the known
+ /// values match. Not known values are not stored as an unnamed tuple (as
+ /// #Written does), but maps to nothing.
+ ///
+ /// Known values are usually just defined for #Occupied elements. Knowing
+ /// #Unused contents has no advantage as it can be overwritten.
+ isl::union_map Known;
+
+ /// { [Element[] -> Scatter[]] -> ValInst[] }
+ /// The write actions currently in the scop or that would be added when
+ /// mapping a scalar. Maps to the value that is written.
+ ///
+ /// Written values that cannot be identified are represented by an unknown
+ /// ValInst[] (an unnamed tuple of 0 dimension). It conflicts with itself.
+ isl::union_map Written;
+
+ /// Check whether this Knowledge object is well-formed.
+ void checkConsistency() const {
+#ifndef NDEBUG
+ // Default-initialized object
+ if (Occupied.is_null() && Unused.is_null() && Known.is_null() &&
+ Written.is_null())
+ return;
+
+ assert(!Occupied.is_null() || !Unused.is_null());
+ assert(!Known.is_null());
+ assert(!Written.is_null());
+
+ // If not all fields are defined, we cannot derived the universe.
+ if (Occupied.is_null() || Unused.is_null())
+ return;
+
+ assert(Occupied.is_disjoint(Unused));
+ auto Universe = Occupied.unite(Unused);
+
+ assert(!Known.domain().is_subset(Universe).is_false());
+ assert(!Written.domain().is_subset(Universe).is_false());
+#endif
+ }
+
+public:
+ /// Initialize a nullptr-Knowledge. This is only provided for convenience; do
+ /// not use such an object.
+ Knowledge() {}
+
+ /// Create a new object with the given members.
+ Knowledge(isl::union_set Occupied, isl::union_set Unused,
+ isl::union_map Known, isl::union_map Written)
+ : Occupied(std::move(Occupied)), Unused(std::move(Unused)),
+ Known(std::move(Known)), Written(std::move(Written)) {
+ checkConsistency();
+ }
+
+ /// Return whether this object was not default-constructed.
+ bool isUsable() const {
+ return (Occupied.is_null() || Unused.is_null()) && !Known.is_null() &&
+ !Written.is_null();
+ }
+
+ /// Print the content of this object to @p OS.
+ void print(llvm::raw_ostream &OS, unsigned Indent = 0) const {
+ if (isUsable()) {
+ if (!Occupied.is_null())
+ OS.indent(Indent) << "Occupied: " << Occupied << "\n";
+ else
+ OS.indent(Indent) << "Occupied: <Everything else not in Unused>\n";
+ if (!Unused.is_null())
+ OS.indent(Indent) << "Unused: " << Unused << "\n";
+ else
+ OS.indent(Indent) << "Unused: <Everything else not in Occupied>\n";
+ OS.indent(Indent) << "Known: " << Known << "\n";
+ OS.indent(Indent) << "Written : " << Written << '\n';
+ } else {
+ OS.indent(Indent) << "Invalid knowledge\n";
+ }
+ }
+
+ /// Combine two knowledges, this and @p That.
+ void learnFrom(Knowledge That) {
+ assert(!isConflicting(*this, That));
+ assert(!Unused.is_null() && !That.Occupied.is_null());
+ assert(
+ That.Unused.is_null() &&
+ "This function is only prepared to learn occupied elements from That");
+ assert(Occupied.is_null() && "This function does not implement "
+ "`this->Occupied = "
+ "this->Occupied.unite(That.Occupied);`");
+
+ Unused = Unused.subtract(That.Occupied);
+ Known = Known.unite(That.Known);
+ Written = Written.unite(That.Written);
+
+ checkConsistency();
+ }
+
+ /// Determine whether two Knowledges conflict with each other.
+ ///
+ /// In theory @p Existing and @p Proposed are symmetric, but the
+ /// implementation is constrained by the implicit interpretation. That is, @p
+ /// Existing must have #Unused defined (use case 1) and @p Proposed must have
+ /// #Occupied defined (use case 1).
+ ///
+ /// A conflict is defined as non-preserved semantics when they are merged. For
+ /// instance, when for the same array and zone they assume different
+ /// llvm::Values.
+ ///
+ /// @param Existing One of the knowledges with #Unused defined.
+ /// @param Proposed One of the knowledges with #Occupied defined.
+ /// @param OS Dump the conflict reason to this output stream; use
+ /// nullptr to not output anything.
+ /// @param Indent Indention for the conflict reason.
+ ///
+ /// @return True, iff the two knowledges are conflicting.
+ static bool isConflicting(const Knowledge &Existing,
+ const Knowledge &Proposed,
+ llvm::raw_ostream *OS = nullptr,
+ unsigned Indent = 0) {
+ assert(!Existing.Unused.is_null());
+ assert(!Proposed.Occupied.is_null());
+
+#ifndef NDEBUG
+ if (!Existing.Occupied.is_null() && !Proposed.Unused.is_null()) {
+ auto ExistingUniverse = Existing.Occupied.unite(Existing.Unused);
+ auto ProposedUniverse = Proposed.Occupied.unite(Proposed.Unused);
+ assert(ExistingUniverse.is_equal(ProposedUniverse) &&
+ "Both inputs' Knowledges must be over the same universe");
+ }
+#endif
+
+ // Do the Existing and Proposed lifetimes conflict?
+ //
+ // Lifetimes are described as the cross-product of array elements and zone
+ // intervals in which they are alive (the space { [Element[] -> Zone[]] }).
+ // In the following we call this "element/lifetime interval".
+ //
+ // In order to not conflict, one of the following conditions must apply for
+ // each element/lifetime interval:
+ //
+ // 1. If occupied in one of the knowledges, it is unused in the other.
+ //
+ // - or -
+ //
+ // 2. Both contain the same value.
+ //
+ // Instead of partitioning the element/lifetime intervals into a part that
+ // both Knowledges occupy (which requires an expensive subtraction) and for
+ // these to check whether they are known to be the same value, we check only
+ // the second condition and ensure that it also applies when then first
+ // condition is true. This is done by adding a wildcard value to
+ // Proposed.Known and Existing.Unused such that they match as a common known
+ // value. We use the "unknown ValInst" for this purpose. Every
+ // Existing.Unused may match with an unknown Proposed.Occupied because these
+ // never are in conflict with each other.
+ auto ProposedOccupiedAnyVal = makeUnknownForDomain(Proposed.Occupied);
+ auto ProposedValues = Proposed.Known.unite(ProposedOccupiedAnyVal);
+
+ auto ExistingUnusedAnyVal = makeUnknownForDomain(Existing.Unused);
+ auto ExistingValues = Existing.Known.unite(ExistingUnusedAnyVal);
+
+ auto MatchingVals = ExistingValues.intersect(ProposedValues);
+ auto Matches = MatchingVals.domain();
+
+ // Any Proposed.Occupied must either have a match between the known values
+ // of Existing and Occupied, or be in Existing.Unused. In the latter case,
+ // the previously added "AnyVal" will match each other.
+ if (!Proposed.Occupied.is_subset(Matches)) {
+ if (OS) {
+ auto Conflicting = Proposed.Occupied.subtract(Matches);
+ auto ExistingConflictingKnown =
+ Existing.Known.intersect_domain(Conflicting);
+ auto ProposedConflictingKnown =
+ Proposed.Known.intersect_domain(Conflicting);
+
+ OS->indent(Indent) << "Proposed lifetime conflicting with Existing's\n";
+ OS->indent(Indent) << "Conflicting occupied: " << Conflicting << "\n";
+ if (!ExistingConflictingKnown.is_empty())
+ OS->indent(Indent)
+ << "Existing Known: " << ExistingConflictingKnown << "\n";
+ if (!ProposedConflictingKnown.is_empty())
+ OS->indent(Indent)
+ << "Proposed Known: " << ProposedConflictingKnown << "\n";
+ }
+ return true;
+ }
+
+ // Do the writes in Existing conflict with occupied values in Proposed?
+ //
+ // In order to not conflict, it must either write to unused lifetime or
+ // write the same value. To check, we remove the writes that write into
+ // Proposed.Unused (they never conflict) and then see whether the written
+ // value is already in Proposed.Known. If there are multiple known values
+ // and a written value is known under different names, it is enough when one
+ // of the written values (assuming that they are the same value under
+ // different names, e.g. a PHINode and one of the incoming values) matches
+ // one of the known names.
+ //
+ // We convert here the set of lifetimes to actual timepoints. A lifetime is
+ // in conflict with a set of write timepoints, if either a live timepoint is
+ // clearly within the lifetime or if a write happens at the beginning of the
+ // lifetime (where it would conflict with the value that actually writes the
+ // value alive). There is no conflict at the end of a lifetime, as the alive
+ // value will always be read, before it is overwritten again. The last
+ // property holds in Polly for all scalar values and we expect all users of
+ // Knowledge to check this property also for accesses to MemoryKind::Array.
+ auto ProposedFixedDefs =
+ convertZoneToTimepoints(Proposed.Occupied, true, false);
+ auto ProposedFixedKnown =
+ convertZoneToTimepoints(Proposed.Known, isl::dim::in, true, false);
+
+ auto ExistingConflictingWrites =
+ Existing.Written.intersect_domain(ProposedFixedDefs);
+ auto ExistingConflictingWritesDomain = ExistingConflictingWrites.domain();
+
+ auto CommonWrittenVal =
+ ProposedFixedKnown.intersect(ExistingConflictingWrites);
+ auto CommonWrittenValDomain = CommonWrittenVal.domain();
+
+ if (!ExistingConflictingWritesDomain.is_subset(CommonWrittenValDomain)) {
+ if (OS) {
+ auto ExistingConflictingWritten =
+ ExistingConflictingWrites.subtract_domain(CommonWrittenValDomain);
+ auto ProposedConflictingKnown = ProposedFixedKnown.subtract_domain(
+ ExistingConflictingWritten.domain());
+
+ OS->indent(Indent)
+ << "Proposed a lifetime where there is an Existing write into it\n";
+ OS->indent(Indent) << "Existing conflicting writes: "
+ << ExistingConflictingWritten << "\n";
+ if (!ProposedConflictingKnown.is_empty())
+ OS->indent(Indent)
+ << "Proposed conflicting known: " << ProposedConflictingKnown
+ << "\n";
+ }
+ return true;
+ }
+
+ // Do the writes in Proposed conflict with occupied values in Existing?
+ auto ExistingAvailableDefs =
+ convertZoneToTimepoints(Existing.Unused, true, false);
+ auto ExistingKnownDefs =
+ convertZoneToTimepoints(Existing.Known, isl::dim::in, true, false);
+
+ auto ProposedWrittenDomain = Proposed.Written.domain();
+ auto KnownIdentical = ExistingKnownDefs.intersect(Proposed.Written);
+ auto IdenticalOrUnused =
+ ExistingAvailableDefs.unite(KnownIdentical.domain());
+ if (!ProposedWrittenDomain.is_subset(IdenticalOrUnused)) {
+ if (OS) {
+ auto Conflicting = ProposedWrittenDomain.subtract(IdenticalOrUnused);
+ auto ExistingConflictingKnown =
+ ExistingKnownDefs.intersect_domain(Conflicting);
+ auto ProposedConflictingWritten =
+ Proposed.Written.intersect_domain(Conflicting);
+
+ OS->indent(Indent) << "Proposed writes into range used by Existing\n";
+ OS->indent(Indent) << "Proposed conflicting writes: "
+ << ProposedConflictingWritten << "\n";
+ if (!ExistingConflictingKnown.is_empty())
+ OS->indent(Indent)
+ << "Existing conflicting known: " << ExistingConflictingKnown
+ << "\n";
+ }
+ return true;
+ }
+
+ // Does Proposed write at the same time as Existing already does (order of
+ // writes is undefined)? Writing the same value is permitted.
+ auto ExistingWrittenDomain = Existing.Written.domain();
+ auto BothWritten =
+ Existing.Written.domain().intersect(Proposed.Written.domain());
+ auto ExistingKnownWritten = filterKnownValInst(Existing.Written);
+ auto ProposedKnownWritten = filterKnownValInst(Proposed.Written);
+ auto CommonWritten =
+ ExistingKnownWritten.intersect(ProposedKnownWritten).domain();
+
+ if (!BothWritten.is_subset(CommonWritten)) {
+ if (OS) {
+ auto Conflicting = BothWritten.subtract(CommonWritten);
+ auto ExistingConflictingWritten =
+ Existing.Written.intersect_domain(Conflicting);
+ auto ProposedConflictingWritten =
+ Proposed.Written.intersect_domain(Conflicting);
+
+ OS->indent(Indent) << "Proposed writes at the same time as an already "
+ "Existing write\n";
+ OS->indent(Indent) << "Conflicting writes: " << Conflicting << "\n";
+ if (!ExistingConflictingWritten.is_empty())
+ OS->indent(Indent)
+ << "Exiting write: " << ExistingConflictingWritten << "\n";
+ if (!ProposedConflictingWritten.is_empty())
+ OS->indent(Indent)
+ << "Proposed write: " << ProposedConflictingWritten << "\n";
+ }
+ return true;
+ }
+
+ return false;
+ }
+};
+
+/// Implementation of the DeLICM/DePRE transformation.
+class DeLICMImpl : public ZoneAlgorithm {
+private:
+ /// Knowledge before any transformation took place.
+ Knowledge OriginalZone;
+
+ /// Current knowledge of the SCoP including all already applied
+ /// transformations.
+ Knowledge Zone;
+
+ /// Number of StoreInsts something can be mapped to.
+ int NumberOfCompatibleTargets = 0;
+
+ /// The number of StoreInsts to which at least one value or PHI has been
+ /// mapped to.
+ int NumberOfTargetsMapped = 0;
+
+ /// The number of llvm::Value mapped to some array element.
+ int NumberOfMappedValueScalars = 0;
+
+ /// The number of PHIs mapped to some array element.
+ int NumberOfMappedPHIScalars = 0;
+
+ /// Determine whether two knowledges are conflicting with each other.
+ ///
+ /// @see Knowledge::isConflicting
+ bool isConflicting(const Knowledge &Proposed) {
+ raw_ostream *OS = nullptr;
+ LLVM_DEBUG(OS = &llvm::dbgs());
+ return Knowledge::isConflicting(Zone, Proposed, OS, 4);
+ }
+
+ /// Determine whether @p SAI is a scalar that can be mapped to an array
+ /// element.
+ bool isMappable(const ScopArrayInfo *SAI) {
+ assert(SAI);
+
+ if (SAI->isValueKind()) {
+ auto *MA = S->getValueDef(SAI);
+ if (!MA) {
+ LLVM_DEBUG(
+ dbgs()
+ << " Reject because value is read-only within the scop\n");
+ return false;
+ }
+
+ // Mapping if value is used after scop is not supported. The code
+ // generator would need to reload the scalar after the scop, but it
+ // does not have the information to where it is mapped to. Only the
+ // MemoryAccesses have that information, not the ScopArrayInfo.
+ auto Inst = MA->getAccessInstruction();
+ for (auto User : Inst->users()) {
+ if (!isa<Instruction>(User))
+ return false;
+ auto UserInst = cast<Instruction>(User);
+
+ if (!S->contains(UserInst)) {
+ LLVM_DEBUG(dbgs() << " Reject because value is escaping\n");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ if (SAI->isPHIKind()) {
+ auto *MA = S->getPHIRead(SAI);
+ assert(MA);
+
+ // Mapping of an incoming block from before the SCoP is not supported by
+ // the code generator.
+ auto PHI = cast<PHINode>(MA->getAccessInstruction());
+ for (auto Incoming : PHI->blocks()) {
+ if (!S->contains(Incoming)) {
+ LLVM_DEBUG(dbgs()
+ << " Reject because at least one incoming block is "
+ "not in the scop region\n");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ LLVM_DEBUG(dbgs() << " Reject ExitPHI or other non-value\n");
+ return false;
+ }
+
+ /// Compute the uses of a MemoryKind::Value and its lifetime (from its
+ /// definition to the last use).
+ ///
+ /// @param SAI The ScopArrayInfo representing the value's storage.
+ ///
+ /// @return { DomainDef[] -> DomainUse[] }, { DomainDef[] -> Zone[] }
+ /// First element is the set of uses for each definition.
+ /// The second is the lifetime of each definition.
+ std::tuple<isl::union_map, isl::map>
+ computeValueUses(const ScopArrayInfo *SAI) {
+ assert(SAI->isValueKind());
+
+ // { DomainRead[] }
+ auto Reads = makeEmptyUnionSet();
+
+ // Find all uses.
+ for (auto *MA : S->getValueUses(SAI))
+ Reads = Reads.unite(getDomainFor(MA));
+
+ // { DomainRead[] -> Scatter[] }
+ auto ReadSchedule = getScatterFor(Reads);
+
+ auto *DefMA = S->getValueDef(SAI);
+ assert(DefMA);
+
+ // { DomainDef[] }
+ auto Writes = getDomainFor(DefMA);
+
+ // { DomainDef[] -> Scatter[] }
+ auto WriteScatter = getScatterFor(Writes);
+
+ // { Scatter[] -> DomainDef[] }
+ auto ReachDef = getScalarReachingDefinition(DefMA->getStatement());
+
+ // { [DomainDef[] -> Scatter[]] -> DomainUse[] }
+ auto Uses = isl::union_map(ReachDef.reverse().range_map())
+ .apply_range(ReadSchedule.reverse());
+
+ // { DomainDef[] -> Scatter[] }
+ auto UseScatter =
+ singleton(Uses.domain().unwrap(),
+ Writes.get_space().map_from_domain_and_range(ScatterSpace));
+
+ // { DomainDef[] -> Zone[] }
+ auto Lifetime = betweenScatter(WriteScatter, UseScatter, false, true);
+
+ // { DomainDef[] -> DomainRead[] }
+ auto DefUses = Uses.domain_factor_domain();
+
+ return std::make_pair(DefUses, Lifetime);
+ }
+
+ /// Try to map a MemoryKind::Value to a given array element.
+ ///
+ /// @param SAI Representation of the scalar's memory to map.
+ /// @param TargetElt { Scatter[] -> Element[] }
+ /// Suggestion where to map a scalar to when at a timepoint.
+ ///
+ /// @return true if the scalar was successfully mapped.
+ bool tryMapValue(const ScopArrayInfo *SAI, isl::map TargetElt) {
+ assert(SAI->isValueKind());
+
+ auto *DefMA = S->getValueDef(SAI);
+ assert(DefMA->isValueKind());
+ assert(DefMA->isMustWrite());
+ auto *V = DefMA->getAccessValue();
+ auto *DefInst = DefMA->getAccessInstruction();
+
+ // Stop if the scalar has already been mapped.
+ if (!DefMA->getLatestScopArrayInfo()->isValueKind())
+ return false;
+
+ // { DomainDef[] -> Scatter[] }
+ auto DefSched = getScatterFor(DefMA);
+
+ // Where each write is mapped to, according to the suggestion.
+ // { DomainDef[] -> Element[] }
+ auto DefTarget = TargetElt.apply_domain(DefSched.reverse());
+ simplify(DefTarget);
+ LLVM_DEBUG(dbgs() << " Def Mapping: " << DefTarget << '\n');
+
+ auto OrigDomain = getDomainFor(DefMA);
+ auto MappedDomain = DefTarget.domain();
+ if (!OrigDomain.is_subset(MappedDomain)) {
+ LLVM_DEBUG(
+ dbgs()
+ << " Reject because mapping does not encompass all instances\n");
+ return false;
+ }
+
+ // { DomainDef[] -> Zone[] }
+ isl::map Lifetime;
+
+ // { DomainDef[] -> DomainUse[] }
+ isl::union_map DefUses;
+
+ std::tie(DefUses, Lifetime) = computeValueUses(SAI);
+ LLVM_DEBUG(dbgs() << " Lifetime: " << Lifetime << '\n');
+
+ /// { [Element[] -> Zone[]] }
+ auto EltZone = Lifetime.apply_domain(DefTarget).wrap();
+ simplify(EltZone);
+
+ // When known knowledge is disabled, just return the unknown value. It will
+ // either get filtered out or conflict with itself.
+ // { DomainDef[] -> ValInst[] }
+ isl::map ValInst;
+ if (DelicmComputeKnown)
+ ValInst = makeValInst(V, DefMA->getStatement(),
+ LI->getLoopFor(DefInst->getParent()));
+ else
+ ValInst = makeUnknownForDomain(DefMA->getStatement());
+
+ // { DomainDef[] -> [Element[] -> Zone[]] }
+ auto EltKnownTranslator = DefTarget.range_product(Lifetime);
+
+ // { [Element[] -> Zone[]] -> ValInst[] }
+ auto EltKnown = ValInst.apply_domain(EltKnownTranslator);
+ simplify(EltKnown);
+
+ // { DomainDef[] -> [Element[] -> Scatter[]] }
+ auto WrittenTranslator = DefTarget.range_product(DefSched);
+
+ // { [Element[] -> Scatter[]] -> ValInst[] }
+ auto DefEltSched = ValInst.apply_domain(WrittenTranslator);
+ simplify(DefEltSched);
+
+ Knowledge Proposed(EltZone, {}, filterKnownValInst(EltKnown), DefEltSched);
+ if (isConflicting(Proposed))
+ return false;
+
+ // { DomainUse[] -> Element[] }
+ auto UseTarget = DefUses.reverse().apply_range(DefTarget);
+
+ mapValue(SAI, std::move(DefTarget), std::move(UseTarget),
+ std::move(Lifetime), std::move(Proposed));
+ return true;
+ }
+
+ /// After a scalar has been mapped, update the global knowledge.
+ void applyLifetime(Knowledge Proposed) {
+ Zone.learnFrom(std::move(Proposed));
+ }
+
+ /// Map a MemoryKind::Value scalar to an array element.
+ ///
+ /// Callers must have ensured that the mapping is valid and not conflicting.
+ ///
+ /// @param SAI The ScopArrayInfo representing the scalar's memory to
+ /// map.
+ /// @param DefTarget { DomainDef[] -> Element[] }
+ /// The array element to map the scalar to.
+ /// @param UseTarget { DomainUse[] -> Element[] }
+ /// The array elements the uses are mapped to.
+ /// @param Lifetime { DomainDef[] -> Zone[] }
+ /// The lifetime of each llvm::Value definition for
+ /// reporting.
+ /// @param Proposed Mapping constraints for reporting.
+ void mapValue(const ScopArrayInfo *SAI, isl::map DefTarget,
+ isl::union_map UseTarget, isl::map Lifetime,
+ Knowledge Proposed) {
+ // Redirect the read accesses.
+ for (auto *MA : S->getValueUses(SAI)) {
+ // { DomainUse[] }
+ auto Domain = getDomainFor(MA);
+
+ // { DomainUse[] -> Element[] }
+ auto NewAccRel = UseTarget.intersect_domain(Domain);
+ simplify(NewAccRel);
+
+ assert(isl_union_map_n_map(NewAccRel.get()) == 1);
+ MA->setNewAccessRelation(isl::map::from_union_map(NewAccRel));
+ }
+
+ auto *WA = S->getValueDef(SAI);
+ WA->setNewAccessRelation(DefTarget);
+ applyLifetime(Proposed);
+
+ MappedValueScalars++;
+ NumberOfMappedValueScalars += 1;
+ }
+
+ isl::map makeValInst(Value *Val, ScopStmt *UserStmt, Loop *Scope,
+ bool IsCertain = true) {
+ // When known knowledge is disabled, just return the unknown value. It will
+ // either get filtered out or conflict with itself.
+ if (!DelicmComputeKnown)
+ return makeUnknownForDomain(UserStmt);
+ return ZoneAlgorithm::makeValInst(Val, UserStmt, Scope, IsCertain);
+ }
+
+ /// Express the incoming values of a PHI for each incoming statement in an
+ /// isl::union_map.
+ ///
+ /// @param SAI The PHI scalar represented by a ScopArrayInfo.
+ ///
+ /// @return { PHIWriteDomain[] -> ValInst[] }
+ isl::union_map determinePHIWrittenValues(const ScopArrayInfo *SAI) {
+ auto Result = makeEmptyUnionMap();
+
+ // Collect the incoming values.
+ for (auto *MA : S->getPHIIncomings(SAI)) {
+ // { DomainWrite[] -> ValInst[] }
+ isl::union_map ValInst;
+ auto *WriteStmt = MA->getStatement();
+
+ auto Incoming = MA->getIncoming();
+ assert(!Incoming.empty());
+ if (Incoming.size() == 1) {
+ ValInst = makeValInst(Incoming[0].second, WriteStmt,
+ LI->getLoopFor(Incoming[0].first));
+ } else {
+ // If the PHI is in a subregion's exit node it can have multiple
+ // incoming values (+ maybe another incoming edge from an unrelated
+ // block). We cannot directly represent it as a single llvm::Value.
+ // We currently model it as unknown value, but modeling as the PHIInst
+ // itself could be OK, too.
+ ValInst = makeUnknownForDomain(WriteStmt);
+ }
+
+ Result = Result.unite(ValInst);
+ }
+
+ assert(Result.is_single_valued() &&
+ "Cannot have multiple incoming values for same incoming statement");
+ return Result;
+ }
+
+ /// Try to map a MemoryKind::PHI scalar to a given array element.
+ ///
+ /// @param SAI Representation of the scalar's memory to map.
+ /// @param TargetElt { Scatter[] -> Element[] }
+ /// Suggestion where to map the scalar to when at a
+ /// timepoint.
+ ///
+ /// @return true if the PHI scalar has been mapped.
+ bool tryMapPHI(const ScopArrayInfo *SAI, isl::map TargetElt) {
+ auto *PHIRead = S->getPHIRead(SAI);
+ assert(PHIRead->isPHIKind());
+ assert(PHIRead->isRead());
+
+ // Skip if already been mapped.
+ if (!PHIRead->getLatestScopArrayInfo()->isPHIKind())
+ return false;
+
+ // { DomainRead[] -> Scatter[] }
+ auto PHISched = getScatterFor(PHIRead);
+
+ // { DomainRead[] -> Element[] }
+ auto PHITarget = PHISched.apply_range(TargetElt);
+ simplify(PHITarget);
+ LLVM_DEBUG(dbgs() << " Mapping: " << PHITarget << '\n');
+
+ auto OrigDomain = getDomainFor(PHIRead);
+ auto MappedDomain = PHITarget.domain();
+ if (!OrigDomain.is_subset(MappedDomain)) {
+ LLVM_DEBUG(
+ dbgs()
+ << " Reject because mapping does not encompass all instances\n");
+ return false;
+ }
+
+ // { DomainRead[] -> DomainWrite[] }
+ auto PerPHIWrites = computePerPHI(SAI);
+ if (PerPHIWrites.is_null()) {
+ LLVM_DEBUG(
+ dbgs() << " Reject because cannot determine incoming values\n");
+ return false;
+ }
+
+ // { DomainWrite[] -> Element[] }
+ auto WritesTarget = PerPHIWrites.apply_domain(PHITarget).reverse();
+ simplify(WritesTarget);
+
+ // { DomainWrite[] }
+ auto UniverseWritesDom = isl::union_set::empty(ParamSpace.ctx());
+
+ for (auto *MA : S->getPHIIncomings(SAI))
+ UniverseWritesDom = UniverseWritesDom.unite(getDomainFor(MA));
+
+ auto RelevantWritesTarget = WritesTarget;
+ if (DelicmOverapproximateWrites)
+ WritesTarget = expandMapping(WritesTarget, UniverseWritesDom);
+
+ auto ExpandedWritesDom = WritesTarget.domain();
+ if (!DelicmPartialWrites &&
+ !UniverseWritesDom.is_subset(ExpandedWritesDom)) {
+ LLVM_DEBUG(
+ dbgs() << " Reject because did not find PHI write mapping for "
+ "all instances\n");
+ if (DelicmOverapproximateWrites)
+ LLVM_DEBUG(dbgs() << " Relevant Mapping: "
+ << RelevantWritesTarget << '\n');
+ LLVM_DEBUG(dbgs() << " Deduced Mapping: " << WritesTarget
+ << '\n');
+ LLVM_DEBUG(dbgs() << " Missing instances: "
+ << UniverseWritesDom.subtract(ExpandedWritesDom)
+ << '\n');
+ return false;
+ }
+
+ // { DomainRead[] -> Scatter[] }
+ isl::union_map PerPHIWriteScatterUmap = PerPHIWrites.apply_range(Schedule);
+ isl::map PerPHIWriteScatter =
+ singleton(PerPHIWriteScatterUmap, PHISched.get_space());
+
+ // { DomainRead[] -> Zone[] }
+ auto Lifetime = betweenScatter(PerPHIWriteScatter, PHISched, false, true);
+ simplify(Lifetime);
+ LLVM_DEBUG(dbgs() << " Lifetime: " << Lifetime << "\n");
+
+ // { DomainWrite[] -> Zone[] }
+ auto WriteLifetime = isl::union_map(Lifetime).apply_domain(PerPHIWrites);
+
+ // { DomainWrite[] -> ValInst[] }
+ auto WrittenValue = determinePHIWrittenValues(SAI);
+
+ // { DomainWrite[] -> [Element[] -> Scatter[]] }
+ auto WrittenTranslator = WritesTarget.range_product(Schedule);
+
+ // { [Element[] -> Scatter[]] -> ValInst[] }
+ auto Written = WrittenValue.apply_domain(WrittenTranslator);
+ simplify(Written);
+
+ // { DomainWrite[] -> [Element[] -> Zone[]] }
+ auto LifetimeTranslator = WritesTarget.range_product(WriteLifetime);
+
+ // { DomainWrite[] -> ValInst[] }
+ auto WrittenKnownValue = filterKnownValInst(WrittenValue);
+
+ // { [Element[] -> Zone[]] -> ValInst[] }
+ auto EltLifetimeInst = WrittenKnownValue.apply_domain(LifetimeTranslator);
+ simplify(EltLifetimeInst);
+
+ // { [Element[] -> Zone[] }
+ auto Occupied = LifetimeTranslator.range();
+ simplify(Occupied);
+
+ Knowledge Proposed(Occupied, {}, EltLifetimeInst, Written);
+ if (isConflicting(Proposed))
+ return false;
+
+ mapPHI(SAI, std::move(PHITarget), std::move(WritesTarget),
+ std::move(Lifetime), std::move(Proposed));
+ return true;
+ }
+
+ /// Map a MemoryKind::PHI scalar to an array element.
+ ///
+ /// Callers must have ensured that the mapping is valid and not conflicting
+ /// with the common knowledge.
+ ///
+ /// @param SAI The ScopArrayInfo representing the scalar's memory to
+ /// map.
+ /// @param ReadTarget { DomainRead[] -> Element[] }
+ /// The array element to map the scalar to.
+ /// @param WriteTarget { DomainWrite[] -> Element[] }
+ /// New access target for each PHI incoming write.
+ /// @param Lifetime { DomainRead[] -> Zone[] }
+ /// The lifetime of each PHI for reporting.
+ /// @param Proposed Mapping constraints for reporting.
+ void mapPHI(const ScopArrayInfo *SAI, isl::map ReadTarget,
+ isl::union_map WriteTarget, isl::map Lifetime,
+ Knowledge Proposed) {
+ // { Element[] }
+ isl::space ElementSpace = ReadTarget.get_space().range();
+
+ // Redirect the PHI incoming writes.
+ for (auto *MA : S->getPHIIncomings(SAI)) {
+ // { DomainWrite[] }
+ auto Domain = getDomainFor(MA);
+
+ // { DomainWrite[] -> Element[] }
+ auto NewAccRel = WriteTarget.intersect_domain(Domain);
+ simplify(NewAccRel);
+
+ isl::space NewAccRelSpace =
+ Domain.get_space().map_from_domain_and_range(ElementSpace);
+ isl::map NewAccRelMap = singleton(NewAccRel, NewAccRelSpace);
+ MA->setNewAccessRelation(NewAccRelMap);
+ }
+
+ // Redirect the PHI read.
+ auto *PHIRead = S->getPHIRead(SAI);
+ PHIRead->setNewAccessRelation(ReadTarget);
+ applyLifetime(Proposed);
+
+ MappedPHIScalars++;
+ NumberOfMappedPHIScalars++;
+ }
+
+ /// Search and map scalars to memory overwritten by @p TargetStoreMA.
+ ///
+ /// Start trying to map scalars that are used in the same statement as the
+ /// store. For every successful mapping, try to also map scalars of the
+ /// statements where those are written. Repeat, until no more mapping
+ /// opportunity is found.
+ ///
+ /// There is currently no preference in which order scalars are tried.
+ /// Ideally, we would direct it towards a load instruction of the same array
+ /// element.
+ bool collapseScalarsToStore(MemoryAccess *TargetStoreMA) {
+ assert(TargetStoreMA->isLatestArrayKind());
+ assert(TargetStoreMA->isMustWrite());
+
+ auto TargetStmt = TargetStoreMA->getStatement();
+
+ // { DomTarget[] }
+ auto TargetDom = getDomainFor(TargetStmt);
+
+ // { DomTarget[] -> Element[] }
+ auto TargetAccRel = getAccessRelationFor(TargetStoreMA);
+
+ // { Zone[] -> DomTarget[] }
+ // For each point in time, find the next target store instance.
+ auto Target =
+ computeScalarReachingOverwrite(Schedule, TargetDom, false, true);
+
+ // { Zone[] -> Element[] }
+ // Use the target store's write location as a suggestion to map scalars to.
+ auto EltTarget = Target.apply_range(TargetAccRel);
+ simplify(EltTarget);
+ LLVM_DEBUG(dbgs() << " Target mapping is " << EltTarget << '\n');
+
+ // Stack of elements not yet processed.
+ SmallVector<MemoryAccess *, 16> Worklist;
+
+ // Set of scalars already tested.
+ SmallPtrSet<const ScopArrayInfo *, 16> Closed;
+
+ // Lambda to add all scalar reads to the work list.
+ auto ProcessAllIncoming = [&](ScopStmt *Stmt) {
+ for (auto *MA : *Stmt) {
+ if (!MA->isLatestScalarKind())
+ continue;
+ if (!MA->isRead())
+ continue;
+
+ Worklist.push_back(MA);
+ }
+ };
+
+ auto *WrittenVal = TargetStoreMA->getAccessInstruction()->getOperand(0);
+ if (auto *WrittenValInputMA = TargetStmt->lookupInputAccessOf(WrittenVal))
+ Worklist.push_back(WrittenValInputMA);
+ else
+ ProcessAllIncoming(TargetStmt);
+
+ auto AnyMapped = false;
+ auto &DL = S->getRegion().getEntry()->getModule()->getDataLayout();
+ auto StoreSize =
+ DL.getTypeAllocSize(TargetStoreMA->getAccessValue()->getType());
+
+ while (!Worklist.empty()) {
+ auto *MA = Worklist.pop_back_val();
+
+ auto *SAI = MA->getScopArrayInfo();
+ if (Closed.count(SAI))
+ continue;
+ Closed.insert(SAI);
+ LLVM_DEBUG(dbgs() << "\n Trying to map " << MA << " (SAI: " << SAI
+ << ")\n");
+
+ // Skip non-mappable scalars.
+ if (!isMappable(SAI))
+ continue;
+
+ auto MASize = DL.getTypeAllocSize(MA->getAccessValue()->getType());
+ if (MASize > StoreSize) {
+ LLVM_DEBUG(
+ dbgs() << " Reject because storage size is insufficient\n");
+ continue;
+ }
+
+ // Try to map MemoryKind::Value scalars.
+ if (SAI->isValueKind()) {
+ if (!tryMapValue(SAI, EltTarget))
+ continue;
+
+ auto *DefAcc = S->getValueDef(SAI);
+ ProcessAllIncoming(DefAcc->getStatement());
+
+ AnyMapped = true;
+ continue;
+ }
+
+ // Try to map MemoryKind::PHI scalars.
+ if (SAI->isPHIKind()) {
+ if (!tryMapPHI(SAI, EltTarget))
+ continue;
+ // Add inputs of all incoming statements to the worklist. Prefer the
+ // input accesses of the incoming blocks.
+ for (auto *PHIWrite : S->getPHIIncomings(SAI)) {
+ auto *PHIWriteStmt = PHIWrite->getStatement();
+ bool FoundAny = false;
+ for (auto Incoming : PHIWrite->getIncoming()) {
+ auto *IncomingInputMA =
+ PHIWriteStmt->lookupInputAccessOf(Incoming.second);
+ if (!IncomingInputMA)
+ continue;
+
+ Worklist.push_back(IncomingInputMA);
+ FoundAny = true;
+ }
+
+ if (!FoundAny)
+ ProcessAllIncoming(PHIWrite->getStatement());
+ }
+
+ AnyMapped = true;
+ continue;
+ }
+ }
+
+ if (AnyMapped) {
+ TargetsMapped++;
+ NumberOfTargetsMapped++;
+ }
+ return AnyMapped;
+ }
+
+ /// Compute when an array element is unused.
+ ///
+ /// @return { [Element[] -> Zone[]] }
+ isl::union_set computeLifetime() const {
+ // { Element[] -> Zone[] }
+ auto ArrayUnused = computeArrayUnused(Schedule, AllMustWrites, AllReads,
+ false, false, true);
+
+ auto Result = ArrayUnused.wrap();
+
+ simplify(Result);
+ return Result;
+ }
+
+ /// Determine when an array element is written to, and which value instance is
+ /// written.
+ ///
+ /// @return { [Element[] -> Scatter[]] -> ValInst[] }
+ isl::union_map computeWritten() const {
+ // { [Element[] -> Scatter[]] -> ValInst[] }
+ auto EltWritten = applyDomainRange(AllWriteValInst, Schedule);
+
+ simplify(EltWritten);
+ return EltWritten;
+ }
+
+ /// Determine whether an access touches at most one element.
+ ///
+ /// The accessed element could be a scalar or accessing an array with constant
+ /// subscript, such that all instances access only that element.
+ ///
+ /// @param MA The access to test.
+ ///
+ /// @return True, if zero or one elements are accessed; False if at least two
+ /// different elements are accessed.
+ bool isScalarAccess(MemoryAccess *MA) {
+ auto Map = getAccessRelationFor(MA);
+ auto Set = Map.range();
+ return Set.is_singleton();
+ }
+
+ /// Print mapping statistics to @p OS.
+ void printStatistics(llvm::raw_ostream &OS, int Indent = 0) const {
+ OS.indent(Indent) << "Statistics {\n";
+ OS.indent(Indent + 4) << "Compatible overwrites: "
+ << NumberOfCompatibleTargets << "\n";
+ OS.indent(Indent + 4) << "Overwrites mapped to: " << NumberOfTargetsMapped
+ << '\n';
+ OS.indent(Indent + 4) << "Value scalars mapped: "
+ << NumberOfMappedValueScalars << '\n';
+ OS.indent(Indent + 4) << "PHI scalars mapped: "
+ << NumberOfMappedPHIScalars << '\n';
+ OS.indent(Indent) << "}\n";
+ }
+
+public:
+ DeLICMImpl(Scop *S, LoopInfo *LI) : ZoneAlgorithm("polly-delicm", S, LI) {}
+
+ /// Calculate the lifetime (definition to last use) of every array element.
+ ///
+ /// @return True if the computed lifetimes (#Zone) is usable.
+ bool computeZone() {
+ // Check that nothing strange occurs.
+ collectCompatibleElts();
+
+ isl::union_set EltUnused;
+ isl::union_map EltKnown, EltWritten;
+
+ {
+ IslMaxOperationsGuard MaxOpGuard(IslCtx.get(), DelicmMaxOps);
+
+ computeCommon();
+
+ EltUnused = computeLifetime();
+ EltKnown = computeKnown(true, false);
+ EltWritten = computeWritten();
+ }
+ DeLICMAnalyzed++;
+
+ if (EltUnused.is_null() || EltKnown.is_null() || EltWritten.is_null()) {
+ assert(isl_ctx_last_error(IslCtx.get()) == isl_error_quota &&
+ "The only reason that these things have not been computed should "
+ "be if the max-operations limit hit");
+ DeLICMOutOfQuota++;
+ LLVM_DEBUG(dbgs() << "DeLICM analysis exceeded max_operations\n");
+ DebugLoc Begin, End;
+ getDebugLocations(getBBPairForRegion(&S->getRegion()), Begin, End);
+ OptimizationRemarkAnalysis R(DEBUG_TYPE, "OutOfQuota", Begin,
+ S->getEntry());
+ R << "maximal number of operations exceeded during zone analysis";
+ S->getFunction().getContext().diagnose(R);
+ return false;
+ }
+
+ Zone = OriginalZone = Knowledge({}, EltUnused, EltKnown, EltWritten);
+ LLVM_DEBUG(dbgs() << "Computed Zone:\n"; OriginalZone.print(dbgs(), 4));
+
+ assert(Zone.isUsable() && OriginalZone.isUsable());
+ return true;
+ }
+
+ /// Try to map as many scalars to unused array elements as possible.
+ ///
+ /// Multiple scalars might be mappable to intersecting unused array element
+ /// zones, but we can only chose one. This is a greedy algorithm, therefore
+ /// the first processed element claims it.
+ void greedyCollapse() {
+ bool Modified = false;
+
+ for (auto &Stmt : *S) {
+ for (auto *MA : Stmt) {
+ if (!MA->isLatestArrayKind())
+ continue;
+ if (!MA->isWrite())
+ continue;
+
+ if (MA->isMayWrite()) {
+ LLVM_DEBUG(dbgs() << "Access " << MA
+ << " pruned because it is a MAY_WRITE\n");
+ OptimizationRemarkMissed R(DEBUG_TYPE, "TargetMayWrite",
+ MA->getAccessInstruction());
+ R << "Skipped possible mapping target because it is not an "
+ "unconditional overwrite";
+ S->getFunction().getContext().diagnose(R);
+ continue;
+ }
+
+ if (Stmt.getNumIterators() == 0) {
+ LLVM_DEBUG(dbgs() << "Access " << MA
+ << " pruned because it is not in a loop\n");
+ OptimizationRemarkMissed R(DEBUG_TYPE, "WriteNotInLoop",
+ MA->getAccessInstruction());
+ R << "skipped possible mapping target because it is not in a loop";
+ S->getFunction().getContext().diagnose(R);
+ continue;
+ }
+
+ if (isScalarAccess(MA)) {
+ LLVM_DEBUG(dbgs()
+ << "Access " << MA
+ << " pruned because it writes only a single element\n");
+ OptimizationRemarkMissed R(DEBUG_TYPE, "ScalarWrite",
+ MA->getAccessInstruction());
+ R << "skipped possible mapping target because the memory location "
+ "written to does not depend on its outer loop";
+ S->getFunction().getContext().diagnose(R);
+ continue;
+ }
+
+ if (!isa<StoreInst>(MA->getAccessInstruction())) {
+ LLVM_DEBUG(dbgs() << "Access " << MA
+ << " pruned because it is not a StoreInst\n");
+ OptimizationRemarkMissed R(DEBUG_TYPE, "NotAStore",
+ MA->getAccessInstruction());
+ R << "skipped possible mapping target because non-store instructions "
+ "are not supported";
+ S->getFunction().getContext().diagnose(R);
+ continue;
+ }
+
+ // Check for more than one element acces per statement instance.
+ // Currently we expect write accesses to be functional, eg. disallow
+ //
+ // { Stmt[0] -> [i] : 0 <= i < 2 }
+ //
+ // This may occur when some accesses to the element write/read only
+ // parts of the element, eg. a single byte. Polly then divides each
+ // element into subelements of the smallest access length, normal access
+ // then touch multiple of such subelements. It is very common when the
+ // array is accesses with memset, memcpy or memmove which take i8*
+ // arguments.
+ isl::union_map AccRel = MA->getLatestAccessRelation();
+ if (!AccRel.is_single_valued().is_true()) {
+ LLVM_DEBUG(dbgs() << "Access " << MA
+ << " is incompatible because it writes multiple "
+ "elements per instance\n");
+ OptimizationRemarkMissed R(DEBUG_TYPE, "NonFunctionalAccRel",
+ MA->getAccessInstruction());
+ R << "skipped possible mapping target because it writes more than "
+ "one element";
+ S->getFunction().getContext().diagnose(R);
+ continue;
+ }
+
+ isl::union_set TouchedElts = AccRel.range();
+ if (!TouchedElts.is_subset(CompatibleElts)) {
+ LLVM_DEBUG(
+ dbgs()
+ << "Access " << MA
+ << " is incompatible because it touches incompatible elements\n");
+ OptimizationRemarkMissed R(DEBUG_TYPE, "IncompatibleElts",
+ MA->getAccessInstruction());
+ R << "skipped possible mapping target because a target location "
+ "cannot be reliably analyzed";
+ S->getFunction().getContext().diagnose(R);
+ continue;
+ }
+
+ assert(isCompatibleAccess(MA));
+ NumberOfCompatibleTargets++;
+ LLVM_DEBUG(dbgs() << "Analyzing target access " << MA << "\n");
+ if (collapseScalarsToStore(MA))
+ Modified = true;
+ }
+ }
+
+ if (Modified)
+ DeLICMScopsModified++;
+ }
+
+ /// Dump the internal information about a performed DeLICM to @p OS.
+ void print(llvm::raw_ostream &OS, int Indent = 0) {
+ if (!Zone.isUsable()) {
+ OS.indent(Indent) << "Zone not computed\n";
+ return;
+ }
+
+ printStatistics(OS, Indent);
+ if (!isModified()) {
+ OS.indent(Indent) << "No modification has been made\n";
+ return;
+ }
+ printAccesses(OS, Indent);
+ }
+
+ /// Return whether at least one transformation been applied.
+ bool isModified() const { return NumberOfTargetsMapped > 0; }
+};
+
+static std::unique_ptr<DeLICMImpl> collapseToUnused(Scop &S, LoopInfo &LI) {
+ std::unique_ptr<DeLICMImpl> Impl = std::make_unique<DeLICMImpl>(&S, &LI);
+
+ if (!Impl->computeZone()) {
+ LLVM_DEBUG(dbgs() << "Abort because cannot reliably compute lifetimes\n");
+ return Impl;
+ }
+
+ LLVM_DEBUG(dbgs() << "Collapsing scalars to unused array elements...\n");
+ Impl->greedyCollapse();
+
+ LLVM_DEBUG(dbgs() << "\nFinal Scop:\n");
+ LLVM_DEBUG(dbgs() << S);
+
+ return Impl;
+}
+
+static std::unique_ptr<DeLICMImpl> runDeLICM(Scop &S, LoopInfo &LI) {
+ std::unique_ptr<DeLICMImpl> Impl = collapseToUnused(S, LI);
+
+ Scop::ScopStatistics ScopStats = S.getStatistics();
+ NumValueWrites += ScopStats.NumValueWrites;
+ NumValueWritesInLoops += ScopStats.NumValueWritesInLoops;
+ NumPHIWrites += ScopStats.NumPHIWrites;
+ NumPHIWritesInLoops += ScopStats.NumPHIWritesInLoops;
+ NumSingletonWrites += ScopStats.NumSingletonWrites;
+ NumSingletonWritesInLoops += ScopStats.NumSingletonWritesInLoops;
+
+ return Impl;
+}
+
+static PreservedAnalyses runDeLICMUsingNPM(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &U, raw_ostream *OS) {
+ LoopInfo &LI = SAR.LI;
+ std::unique_ptr<DeLICMImpl> Impl = runDeLICM(S, LI);
+
+ if (OS) {
+ *OS << "Printing analysis 'Polly - DeLICM/DePRE' for region: '"
+ << S.getName() << "' in function '" << S.getFunction().getName()
+ << "':\n";
+ if (Impl) {
+ assert(Impl->getScop() == &S);
+
+ *OS << "DeLICM result:\n";
+ Impl->print(*OS);
+ }
+ }
+
+ if (!Impl->isModified())
+ return PreservedAnalyses::all();
+
+ PreservedAnalyses PA;
+ PA.preserveSet<AllAnalysesOn<Module>>();
+ PA.preserveSet<AllAnalysesOn<Function>>();
+ PA.preserveSet<AllAnalysesOn<Loop>>();
+ return PA;
+}
+
+class DeLICMWrapperPass : public ScopPass {
+private:
+ DeLICMWrapperPass(const DeLICMWrapperPass &) = delete;
+ const DeLICMWrapperPass &operator=(const DeLICMWrapperPass &) = delete;
+
+ /// The pass implementation, also holding per-scop data.
+ std::unique_ptr<DeLICMImpl> Impl;
+
+public:
+ static char ID;
+ explicit DeLICMWrapperPass() : ScopPass(ID) {}
+
+ virtual void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequiredTransitive<ScopInfoRegionPass>();
+ AU.addRequired<LoopInfoWrapperPass>();
+ AU.setPreservesAll();
+ }
+
+ virtual bool runOnScop(Scop &S) override {
+ // Free resources for previous scop's computation, if not yet done.
+ releaseMemory();
+
+ auto &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+ Impl = runDeLICM(S, LI);
+
+ return Impl->isModified();
+ }
+
+ virtual void printScop(raw_ostream &OS, Scop &S) const override {
+ if (!Impl)
+ return;
+ assert(Impl->getScop() == &S);
+
+ OS << "DeLICM result:\n";
+ Impl->print(OS);
+ }
+
+ virtual void releaseMemory() override { Impl.reset(); }
+};
+
+char DeLICMWrapperPass::ID;
+} // anonymous namespace
+
+Pass *polly::createDeLICMWrapperPass() { return new DeLICMWrapperPass(); }
+
+INITIALIZE_PASS_BEGIN(DeLICMWrapperPass, "polly-delicm", "Polly - DeLICM/DePRE",
+ false, false)
+INITIALIZE_PASS_DEPENDENCY(ScopInfoWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
+INITIALIZE_PASS_END(DeLICMWrapperPass, "polly-delicm", "Polly - DeLICM/DePRE",
+ false, false)
+
+llvm::PreservedAnalyses DeLICMPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &U) {
+ return runDeLICMUsingNPM(S, SAM, SAR, U, nullptr);
+}
+
+llvm::PreservedAnalyses DeLICMPrinterPass::run(Scop &S,
+ ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &U) {
+ return runDeLICMUsingNPM(S, SAM, SAR, U, &OS);
+}
+
+bool polly::isConflicting(
+ isl::union_set ExistingOccupied, isl::union_set ExistingUnused,
+ isl::union_map ExistingKnown, isl::union_map ExistingWrites,
+ isl::union_set ProposedOccupied, isl::union_set ProposedUnused,
+ isl::union_map ProposedKnown, isl::union_map ProposedWrites,
+ llvm::raw_ostream *OS, unsigned Indent) {
+ Knowledge Existing(std::move(ExistingOccupied), std::move(ExistingUnused),
+ std::move(ExistingKnown), std::move(ExistingWrites));
+ Knowledge Proposed(std::move(ProposedOccupied), std::move(ProposedUnused),
+ std::move(ProposedKnown), std::move(ProposedWrites));
+
+ return Knowledge::isConflicting(Existing, Proposed, OS, Indent);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/DeadCodeElimination.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/DeadCodeElimination.cpp
new file mode 100644
index 00000000000..546f38653b9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/DeadCodeElimination.cpp
@@ -0,0 +1,200 @@
+//===- DeadCodeElimination.cpp - Eliminate dead iteration ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The polyhedral dead code elimination pass analyses a SCoP to eliminate
+// statement instances that can be proven dead.
+// As a consequence, the code generated for this SCoP may execute a statement
+// less often. This means, a statement may be executed only in certain loop
+// iterations or it may not even be part of the generated code at all.
+//
+// This code:
+//
+// for (i = 0; i < N; i++)
+// arr[i] = 0;
+// for (i = 0; i < N; i++)
+// arr[i] = 10;
+// for (i = 0; i < N; i++)
+// arr[i] = i;
+//
+// is e.g. simplified to:
+//
+// for (i = 0; i < N; i++)
+// arr[i] = i;
+//
+// The idea and the algorithm used was first implemented by Sven Verdoolaege in
+// the 'ppcg' tool.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/DeadCodeElimination.h"
+#include "polly/DependenceInfo.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/Options.h"
+#include "polly/ScopInfo.h"
+#include "llvm/Support/CommandLine.h"
+#include "isl/isl-noexceptions.h"
+
+using namespace llvm;
+using namespace polly;
+
+namespace {
+
+cl::opt<int> DCEPreciseSteps(
+ "polly-dce-precise-steps",
+ cl::desc("The number of precise steps between two approximating "
+ "iterations. (A value of -1 schedules another approximation stage "
+ "before the actual dead code elimination."),
+ cl::ZeroOrMore, cl::init(-1), cl::cat(PollyCategory));
+
+class DeadCodeElimWrapperPass : public ScopPass {
+public:
+ static char ID;
+ explicit DeadCodeElimWrapperPass() : ScopPass(ID) {}
+
+ /// Remove dead iterations from the schedule of @p S.
+ bool runOnScop(Scop &S) override;
+
+ /// Register all analyses and transformation required.
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+};
+
+char DeadCodeElimWrapperPass::ID = 0;
+
+/// Return the set of live iterations.
+///
+/// The set of live iterations are all iterations that write to memory and for
+/// which we can not prove that there will be a later write that _must_
+/// overwrite the same memory location and is consequently the only one that
+/// is visible after the execution of the SCoP.
+///
+/// To compute the live outs, we compute for the data-locations that are
+/// must-written to the last statement that touches these locations. On top of
+/// this we add all statements that perform may-write accesses.
+///
+/// We could be more precise by removing may-write accesses for which we know
+/// that they are overwritten by a must-write after. However, at the moment the
+/// only may-writes we introduce access the full (unbounded) array, such that
+/// bounded write accesses can not overwrite all of the data-locations. As
+/// this means may-writes are in the current situation always live, there is
+/// no point in trying to remove them from the live-out set.
+static isl::union_set getLiveOut(Scop &S) {
+ isl::union_map Schedule = S.getSchedule();
+ isl::union_map MustWrites = S.getMustWrites();
+ isl::union_map WriteIterations = MustWrites.reverse();
+ isl::union_map WriteTimes = WriteIterations.apply_range(Schedule);
+
+ isl::union_map LastWriteTimes = WriteTimes.lexmax();
+ isl::union_map LastWriteIterations =
+ LastWriteTimes.apply_range(Schedule.reverse());
+
+ isl::union_set Live = LastWriteIterations.range();
+ isl::union_map MayWrites = S.getMayWrites();
+ Live = Live.unite(MayWrites.domain());
+ return Live.coalesce();
+}
+
+/// Performs polyhedral dead iteration elimination by:
+/// o Assuming that the last write to each location is live.
+/// o Following each RAW dependency from a live iteration backwards and adding
+/// that iteration to the live set.
+///
+/// To ensure the set of live iterations does not get too complex we always
+/// combine a certain number of precise steps with one approximating step that
+/// simplifies the life set with an affine hull.
+static bool runDeadCodeElimination(Scop &S, int PreciseSteps,
+ const Dependences &D) {
+ if (!D.hasValidDependences())
+ return false;
+
+ isl::union_set Live = getLiveOut(S);
+ isl::union_map Dep =
+ D.getDependences(Dependences::TYPE_RAW | Dependences::TYPE_RED);
+ Dep = Dep.reverse();
+
+ if (PreciseSteps == -1)
+ Live = Live.affine_hull();
+
+ isl::union_set OriginalDomain = S.getDomains();
+ int Steps = 0;
+ while (true) {
+ Steps++;
+
+ isl::union_set Extra = Live.apply(Dep);
+
+ if (Extra.is_subset(Live))
+ break;
+
+ Live = Live.unite(Extra);
+
+ if (Steps > PreciseSteps) {
+ Steps = 0;
+ Live = Live.affine_hull();
+ }
+
+ Live = Live.intersect(OriginalDomain);
+ }
+
+ Live = Live.coalesce();
+
+ return S.restrictDomains(Live);
+}
+
+bool DeadCodeElimWrapperPass::runOnScop(Scop &S) {
+ auto &DI = getAnalysis<DependenceInfo>();
+ const Dependences &Deps = DI.getDependences(Dependences::AL_Statement);
+
+ bool Changed = runDeadCodeElimination(S, DCEPreciseSteps, Deps);
+
+ // FIXME: We can probably avoid the recomputation of all dependences by
+ // updating them explicitly.
+ if (Changed)
+ DI.recomputeDependences(Dependences::AL_Statement);
+
+ return false;
+}
+
+void DeadCodeElimWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ ScopPass::getAnalysisUsage(AU);
+ AU.addRequired<DependenceInfo>();
+}
+
+} // namespace
+
+Pass *polly::createDeadCodeElimWrapperPass() {
+ return new DeadCodeElimWrapperPass();
+}
+
+llvm::PreservedAnalyses DeadCodeElimPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &U) {
+ DependenceAnalysis::Result &DA = SAM.getResult<DependenceAnalysis>(S, SAR);
+ const Dependences &Deps = DA.getDependences(Dependences::AL_Statement);
+
+ bool Changed = runDeadCodeElimination(S, DCEPreciseSteps, Deps);
+
+ // FIXME: We can probably avoid the recomputation of all dependences by
+ // updating them explicitly.
+ if (Changed)
+ DA.recomputeDependences(Dependences::AL_Statement);
+
+ if (!Changed)
+ return PreservedAnalyses::all();
+
+ PreservedAnalyses PA;
+ PA.preserveSet<AllAnalysesOn<Module>>();
+ PA.preserveSet<AllAnalysesOn<Function>>();
+ PA.preserveSet<AllAnalysesOn<Loop>>();
+ return PA;
+}
+
+INITIALIZE_PASS_BEGIN(DeadCodeElimWrapperPass, "polly-dce",
+ "Polly - Remove dead iterations", false, false)
+INITIALIZE_PASS_DEPENDENCY(DependenceInfo)
+INITIALIZE_PASS_DEPENDENCY(ScopInfoRegionPass)
+INITIALIZE_PASS_END(DeadCodeElimWrapperPass, "polly-dce",
+ "Polly - Remove dead iterations", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/FlattenAlgo.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/FlattenAlgo.cpp
new file mode 100644
index 00000000000..f8ed332348a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/FlattenAlgo.cpp
@@ -0,0 +1,342 @@
+//===------ FlattenAlgo.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Main algorithm of the FlattenSchedulePass. This is a separate file to avoid
+// the unittest for this requiring linking against LLVM.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/FlattenAlgo.h"
+#include "polly/Support/ISLOStream.h"
+#include "polly/Support/ISLTools.h"
+#include "llvm/Support/Debug.h"
+#define DEBUG_TYPE "polly-flatten-algo"
+
+using namespace polly;
+using namespace llvm;
+
+namespace {
+
+/// Whether a dimension of a set is bounded (lower and upper) by a constant,
+/// i.e. there are two constants Min and Max, such that every value x of the
+/// chosen dimensions is Min <= x <= Max.
+bool isDimBoundedByConstant(isl::set Set, unsigned dim) {
+ auto ParamDims = unsignedFromIslSize(Set.dim(isl::dim::param));
+ Set = Set.project_out(isl::dim::param, 0, ParamDims);
+ Set = Set.project_out(isl::dim::set, 0, dim);
+ auto SetDims = unsignedFromIslSize(Set.tuple_dim());
+ assert(SetDims >= 1);
+ Set = Set.project_out(isl::dim::set, 1, SetDims - 1);
+ return bool(Set.is_bounded());
+}
+
+/// Whether a dimension of a set is (lower and upper) bounded by a constant or
+/// parameters, i.e. there are two expressions Min_p and Max_p of the parameters
+/// p, such that every value x of the chosen dimensions is
+/// Min_p <= x <= Max_p.
+bool isDimBoundedByParameter(isl::set Set, unsigned dim) {
+ Set = Set.project_out(isl::dim::set, 0, dim);
+ auto SetDims = unsignedFromIslSize(Set.tuple_dim());
+ assert(SetDims >= 1);
+ Set = Set.project_out(isl::dim::set, 1, SetDims - 1);
+ return bool(Set.is_bounded());
+}
+
+/// Whether BMap's first out-dimension is not a constant.
+bool isVariableDim(const isl::basic_map &BMap) {
+ auto FixedVal = BMap.plain_get_val_if_fixed(isl::dim::out, 0);
+ return FixedVal.is_null() || FixedVal.is_nan();
+}
+
+/// Whether Map's first out dimension is no constant nor piecewise constant.
+bool isVariableDim(const isl::map &Map) {
+ for (isl::basic_map BMap : Map.get_basic_map_list())
+ if (isVariableDim(BMap))
+ return false;
+
+ return true;
+}
+
+/// Whether UMap's first out dimension is no (piecewise) constant.
+bool isVariableDim(const isl::union_map &UMap) {
+ for (isl::map Map : UMap.get_map_list())
+ if (isVariableDim(Map))
+ return false;
+ return true;
+}
+
+/// Compute @p UPwAff - @p Val.
+isl::union_pw_aff subtract(isl::union_pw_aff UPwAff, isl::val Val) {
+ if (Val.is_zero())
+ return UPwAff;
+
+ auto Result = isl::union_pw_aff::empty(UPwAff.get_space());
+ isl::stat Stat =
+ UPwAff.foreach_pw_aff([=, &Result](isl::pw_aff PwAff) -> isl::stat {
+ auto ValAff =
+ isl::pw_aff(isl::set::universe(PwAff.get_space().domain()), Val);
+ auto Subtracted = PwAff.sub(ValAff);
+ Result = Result.union_add(isl::union_pw_aff(Subtracted));
+ return isl::stat::ok();
+ });
+ if (Stat.is_error())
+ return {};
+ return Result;
+}
+
+/// Compute @UPwAff * @p Val.
+isl::union_pw_aff multiply(isl::union_pw_aff UPwAff, isl::val Val) {
+ if (Val.is_one())
+ return UPwAff;
+
+ auto Result = isl::union_pw_aff::empty(UPwAff.get_space());
+ isl::stat Stat =
+ UPwAff.foreach_pw_aff([=, &Result](isl::pw_aff PwAff) -> isl::stat {
+ auto ValAff =
+ isl::pw_aff(isl::set::universe(PwAff.get_space().domain()), Val);
+ auto Multiplied = PwAff.mul(ValAff);
+ Result = Result.union_add(Multiplied);
+ return isl::stat::ok();
+ });
+ if (Stat.is_error())
+ return {};
+ return Result;
+}
+
+/// Remove @p n dimensions from @p UMap's range, starting at @p first.
+///
+/// It is assumed that all maps in the maps have at least the necessary number
+/// of out dimensions.
+isl::union_map scheduleProjectOut(const isl::union_map &UMap, unsigned first,
+ unsigned n) {
+ if (n == 0)
+ return UMap; /* isl_map_project_out would also reset the tuple, which should
+ have no effect on schedule ranges */
+
+ auto Result = isl::union_map::empty(UMap.ctx());
+ for (isl::map Map : UMap.get_map_list()) {
+ auto Outprojected = Map.project_out(isl::dim::out, first, n);
+ Result = Result.unite(Outprojected);
+ }
+ return Result;
+}
+
+/// Return the @p pos' range dimension, converted to an isl_union_pw_aff.
+isl::union_pw_aff scheduleExtractDimAff(isl::union_map UMap, unsigned pos) {
+ auto SingleUMap = isl::union_map::empty(UMap.ctx());
+ for (isl::map Map : UMap.get_map_list()) {
+ unsigned MapDims = unsignedFromIslSize(Map.range_tuple_dim());
+ assert(MapDims > pos);
+ isl::map SingleMap = Map.project_out(isl::dim::out, 0, pos);
+ SingleMap = SingleMap.project_out(isl::dim::out, 1, MapDims - pos - 1);
+ SingleUMap = SingleUMap.unite(SingleMap);
+ };
+
+ auto UAff = isl::union_pw_multi_aff(SingleUMap);
+ auto FirstMAff = isl::multi_union_pw_aff(UAff);
+ return FirstMAff.at(0);
+}
+
+/// Flatten a sequence-like first dimension.
+///
+/// A sequence-like scatter dimension is constant, or at least only small
+/// variation, typically the result of ordering a sequence of different
+/// statements. An example would be:
+/// { Stmt_A[] -> [0, X, ...]; Stmt_B[] -> [1, Y, ...] }
+/// to schedule all instances of Stmt_A before any instance of Stmt_B.
+///
+/// To flatten, first begin with an offset of zero. Then determine the lowest
+/// possible value of the dimension, call it "i" [In the example we start at 0].
+/// Considering only schedules with that value, consider only instances with
+/// that value and determine the extent of the next dimension. Let l_X(i) and
+/// u_X(i) its minimum (lower bound) and maximum (upper bound) value. Add them
+/// as "Offset + X - l_X(i)" to the new schedule, then add "u_X(i) - l_X(i) + 1"
+/// to Offset and remove all i-instances from the old schedule. Repeat with the
+/// remaining lowest value i' until there are no instances in the old schedule
+/// left.
+/// The example schedule would be transformed to:
+/// { Stmt_X[] -> [X - l_X, ...]; Stmt_B -> [l_X - u_X + 1 + Y - l_Y, ...] }
+isl::union_map tryFlattenSequence(isl::union_map Schedule) {
+ auto IslCtx = Schedule.ctx();
+ auto ScatterSet = isl::set(Schedule.range());
+
+ auto ParamSpace = Schedule.get_space().params();
+ auto Dims = unsignedFromIslSize(ScatterSet.tuple_dim());
+ assert(Dims >= 2u);
+
+ // Would cause an infinite loop.
+ if (!isDimBoundedByConstant(ScatterSet, 0)) {
+ LLVM_DEBUG(dbgs() << "Abort; dimension is not of fixed size\n");
+ return {};
+ }
+
+ auto AllDomains = Schedule.domain();
+ auto AllDomainsToNull = isl::union_pw_multi_aff(AllDomains);
+
+ auto NewSchedule = isl::union_map::empty(ParamSpace.ctx());
+ auto Counter = isl::pw_aff(isl::local_space(ParamSpace.set_from_params()));
+
+ while (!ScatterSet.is_empty()) {
+ LLVM_DEBUG(dbgs() << "Next counter:\n " << Counter << "\n");
+ LLVM_DEBUG(dbgs() << "Remaining scatter set:\n " << ScatterSet << "\n");
+ auto ThisSet = ScatterSet.project_out(isl::dim::set, 1, Dims - 1);
+ auto ThisFirst = ThisSet.lexmin();
+ auto ScatterFirst = ThisFirst.add_dims(isl::dim::set, Dims - 1);
+
+ auto SubSchedule = Schedule.intersect_range(ScatterFirst);
+ SubSchedule = scheduleProjectOut(SubSchedule, 0, 1);
+ SubSchedule = flattenSchedule(SubSchedule);
+
+ unsigned SubDims = getNumScatterDims(SubSchedule);
+ assert(SubDims >= 1);
+ auto FirstSubSchedule = scheduleProjectOut(SubSchedule, 1, SubDims - 1);
+ auto FirstScheduleAff = scheduleExtractDimAff(FirstSubSchedule, 0);
+ auto RemainingSubSchedule = scheduleProjectOut(SubSchedule, 0, 1);
+
+ auto FirstSubScatter = isl::set(FirstSubSchedule.range());
+ LLVM_DEBUG(dbgs() << "Next step in sequence is:\n " << FirstSubScatter
+ << "\n");
+
+ if (!isDimBoundedByParameter(FirstSubScatter, 0)) {
+ LLVM_DEBUG(dbgs() << "Abort; sequence step is not bounded\n");
+ return {};
+ }
+
+ auto FirstSubScatterMap = isl::map::from_range(FirstSubScatter);
+
+ // isl_set_dim_max returns a strange isl_pw_aff with domain tuple_id of
+ // 'none'. It doesn't match with any space including a 0-dimensional
+ // anonymous tuple.
+ // Interesting, one can create such a set using
+ // isl_set_universe(ParamSpace). Bug?
+ auto PartMin = FirstSubScatterMap.dim_min(0);
+ auto PartMax = FirstSubScatterMap.dim_max(0);
+ auto One = isl::pw_aff(isl::set::universe(ParamSpace.set_from_params()),
+ isl::val::one(IslCtx));
+ auto PartLen = PartMax.add(PartMin.neg()).add(One);
+
+ auto AllPartMin = isl::union_pw_aff(PartMin).pullback(AllDomainsToNull);
+ auto FirstScheduleAffNormalized = FirstScheduleAff.sub(AllPartMin);
+ auto AllCounter = isl::union_pw_aff(Counter).pullback(AllDomainsToNull);
+ auto FirstScheduleAffWithOffset =
+ FirstScheduleAffNormalized.add(AllCounter);
+
+ auto ScheduleWithOffset =
+ isl::union_map::from(
+ isl::union_pw_multi_aff(FirstScheduleAffWithOffset))
+ .flat_range_product(RemainingSubSchedule);
+ NewSchedule = NewSchedule.unite(ScheduleWithOffset);
+
+ ScatterSet = ScatterSet.subtract(ScatterFirst);
+ Counter = Counter.add(PartLen);
+ }
+
+ LLVM_DEBUG(dbgs() << "Sequence-flatten result is:\n " << NewSchedule
+ << "\n");
+ return NewSchedule;
+}
+
+/// Flatten a loop-like first dimension.
+///
+/// A loop-like dimension is one that depends on a variable (usually a loop's
+/// induction variable). Let the input schedule look like this:
+/// { Stmt[i] -> [i, X, ...] }
+///
+/// To flatten, we determine the largest extent of X which may not depend on the
+/// actual value of i. Let l_X() the smallest possible value of X and u_X() its
+/// largest value. Then, construct a new schedule
+/// { Stmt[i] -> [i * (u_X() - l_X() + 1), ...] }
+isl::union_map tryFlattenLoop(isl::union_map Schedule) {
+ assert(getNumScatterDims(Schedule) >= 2);
+
+ auto Remaining = scheduleProjectOut(Schedule, 0, 1);
+ auto SubSchedule = flattenSchedule(Remaining);
+ unsigned SubDims = getNumScatterDims(SubSchedule);
+
+ assert(SubDims >= 1);
+
+ auto SubExtent = isl::set(SubSchedule.range());
+ auto SubExtentDims = unsignedFromIslSize(SubExtent.dim(isl::dim::param));
+ SubExtent = SubExtent.project_out(isl::dim::param, 0, SubExtentDims);
+ SubExtent = SubExtent.project_out(isl::dim::set, 1, SubDims - 1);
+
+ if (!isDimBoundedByConstant(SubExtent, 0)) {
+ LLVM_DEBUG(dbgs() << "Abort; dimension not bounded by constant\n");
+ return {};
+ }
+
+ auto Min = SubExtent.dim_min(0);
+ LLVM_DEBUG(dbgs() << "Min bound:\n " << Min << "\n");
+ auto MinVal = getConstant(Min, false, true);
+ auto Max = SubExtent.dim_max(0);
+ LLVM_DEBUG(dbgs() << "Max bound:\n " << Max << "\n");
+ auto MaxVal = getConstant(Max, true, false);
+
+ if (MinVal.is_null() || MaxVal.is_null() || MinVal.is_nan() ||
+ MaxVal.is_nan()) {
+ LLVM_DEBUG(dbgs() << "Abort; dimension bounds could not be determined\n");
+ return {};
+ }
+
+ auto FirstSubScheduleAff = scheduleExtractDimAff(SubSchedule, 0);
+ auto RemainingSubSchedule = scheduleProjectOut(std::move(SubSchedule), 0, 1);
+
+ auto LenVal = MaxVal.sub(MinVal).add(1);
+ auto FirstSubScheduleNormalized = subtract(FirstSubScheduleAff, MinVal);
+
+ // TODO: Normalize FirstAff to zero (convert to isl_map, determine minimum,
+ // subtract it)
+ auto FirstAff = scheduleExtractDimAff(Schedule, 0);
+ auto Offset = multiply(FirstAff, LenVal);
+ isl::union_pw_multi_aff Index = FirstSubScheduleNormalized.add(Offset);
+ auto IndexMap = isl::union_map::from(Index);
+
+ auto Result = IndexMap.flat_range_product(RemainingSubSchedule);
+ LLVM_DEBUG(dbgs() << "Loop-flatten result is:\n " << Result << "\n");
+ return Result;
+}
+} // anonymous namespace
+
+isl::union_map polly::flattenSchedule(isl::union_map Schedule) {
+ unsigned Dims = getNumScatterDims(Schedule);
+ LLVM_DEBUG(dbgs() << "Recursive schedule to process:\n " << Schedule
+ << "\n");
+
+ // Base case; no dimensions left
+ if (Dims == 0) {
+ // TODO: Add one dimension?
+ return Schedule;
+ }
+
+ // Base case; already one-dimensional
+ if (Dims == 1)
+ return Schedule;
+
+ // Fixed dimension; no need to preserve variabledness.
+ if (!isVariableDim(Schedule)) {
+ LLVM_DEBUG(dbgs() << "Fixed dimension; try sequence flattening\n");
+ auto NewScheduleSequence = tryFlattenSequence(Schedule);
+ if (!NewScheduleSequence.is_null())
+ return NewScheduleSequence;
+ }
+
+ // Constant stride
+ LLVM_DEBUG(dbgs() << "Try loop flattening\n");
+ auto NewScheduleLoop = tryFlattenLoop(Schedule);
+ if (!NewScheduleLoop.is_null())
+ return NewScheduleLoop;
+
+ // Try again without loop condition (may blow up the number of pieces!!)
+ LLVM_DEBUG(dbgs() << "Try sequence flattening again\n");
+ auto NewScheduleSequence = tryFlattenSequence(Schedule);
+ if (!NewScheduleSequence.is_null())
+ return NewScheduleSequence;
+
+ // Cannot flatten
+ return Schedule;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/FlattenSchedule.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/FlattenSchedule.cpp
new file mode 100644
index 00000000000..c02c71e175a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/FlattenSchedule.cpp
@@ -0,0 +1,106 @@
+//===------ FlattenSchedule.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Try to reduce the number of scatter dimension. Useful to make isl_union_map
+// schedules more understandable. This is only intended for debugging and
+// unittests, not for production use.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/FlattenSchedule.h"
+#include "polly/FlattenAlgo.h"
+#include "polly/ScopInfo.h"
+#include "polly/ScopPass.h"
+#include "polly/Support/ISLOStream.h"
+#include "polly/Support/ISLTools.h"
+#define DEBUG_TYPE "polly-flatten-schedule"
+
+using namespace polly;
+using namespace llvm;
+
+namespace {
+
+/// Print a schedule to @p OS.
+///
+/// Prints the schedule for each statements on a new line.
+void printSchedule(raw_ostream &OS, const isl::union_map &Schedule,
+ int indent) {
+ for (isl::map Map : Schedule.get_map_list())
+ OS.indent(indent) << Map << "\n";
+}
+
+/// Flatten the schedule stored in an polly::Scop.
+class FlattenSchedule : public ScopPass {
+private:
+ FlattenSchedule(const FlattenSchedule &) = delete;
+ const FlattenSchedule &operator=(const FlattenSchedule &) = delete;
+
+ std::shared_ptr<isl_ctx> IslCtx;
+ isl::union_map OldSchedule;
+
+public:
+ static char ID;
+ explicit FlattenSchedule() : ScopPass(ID) {}
+
+ virtual void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequiredTransitive<ScopInfoRegionPass>();
+ AU.setPreservesAll();
+ }
+
+ virtual bool runOnScop(Scop &S) override {
+ // Keep a reference to isl_ctx to ensure that it is not freed before we free
+ // OldSchedule.
+ IslCtx = S.getSharedIslCtx();
+
+ LLVM_DEBUG(dbgs() << "Going to flatten old schedule:\n");
+ OldSchedule = S.getSchedule();
+ LLVM_DEBUG(printSchedule(dbgs(), OldSchedule, 2));
+
+ auto Domains = S.getDomains();
+ auto RestrictedOldSchedule = OldSchedule.intersect_domain(Domains);
+ LLVM_DEBUG(dbgs() << "Old schedule with domains:\n");
+ LLVM_DEBUG(printSchedule(dbgs(), RestrictedOldSchedule, 2));
+
+ auto NewSchedule = flattenSchedule(RestrictedOldSchedule);
+
+ LLVM_DEBUG(dbgs() << "Flattened new schedule:\n");
+ LLVM_DEBUG(printSchedule(dbgs(), NewSchedule, 2));
+
+ NewSchedule = NewSchedule.gist_domain(Domains);
+ LLVM_DEBUG(dbgs() << "Gisted, flattened new schedule:\n");
+ LLVM_DEBUG(printSchedule(dbgs(), NewSchedule, 2));
+
+ S.setSchedule(NewSchedule);
+ return false;
+ }
+
+ virtual void printScop(raw_ostream &OS, Scop &S) const override {
+ OS << "Schedule before flattening {\n";
+ printSchedule(OS, OldSchedule, 4);
+ OS << "}\n\n";
+
+ OS << "Schedule after flattening {\n";
+ printSchedule(OS, S.getSchedule(), 4);
+ OS << "}\n";
+ }
+
+ virtual void releaseMemory() override {
+ OldSchedule = {};
+ IslCtx.reset();
+ }
+};
+
+char FlattenSchedule::ID;
+} // anonymous namespace
+
+Pass *polly::createFlattenSchedulePass() { return new FlattenSchedule(); }
+
+INITIALIZE_PASS_BEGIN(FlattenSchedule, "polly-flatten-schedule",
+ "Polly - Flatten schedule", false, false)
+INITIALIZE_PASS_END(FlattenSchedule, "polly-flatten-schedule",
+ "Polly - Flatten schedule", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/ForwardOpTree.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/ForwardOpTree.cpp
new file mode 100644
index 00000000000..b54683993e7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/ForwardOpTree.cpp
@@ -0,0 +1,1169 @@
+//===- ForwardOpTree.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Move instructions between statements.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/ForwardOpTree.h"
+#include "polly/Options.h"
+#include "polly/ScopBuilder.h"
+#include "polly/ScopInfo.h"
+#include "polly/ScopPass.h"
+#include "polly/Support/GICHelper.h"
+#include "polly/Support/ISLOStream.h"
+#include "polly/Support/ISLTools.h"
+#include "polly/Support/VirtualInstruction.h"
+#include "polly/ZoneAlgo.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/ValueTracking.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Value.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include "isl/ctx.h"
+#include "isl/isl-noexceptions.h"
+#include <cassert>
+#include <memory>
+
+#define DEBUG_TYPE "polly-optree"
+
+using namespace llvm;
+using namespace polly;
+
+static cl::opt<bool>
+ AnalyzeKnown("polly-optree-analyze-known",
+ cl::desc("Analyze array contents for load forwarding"),
+ cl::cat(PollyCategory), cl::init(true), cl::Hidden);
+
+static cl::opt<bool>
+ NormalizePHIs("polly-optree-normalize-phi",
+ cl::desc("Replace PHIs by their incoming values"),
+ cl::cat(PollyCategory), cl::init(false), cl::Hidden);
+
+static cl::opt<unsigned>
+ MaxOps("polly-optree-max-ops",
+ cl::desc("Maximum number of ISL operations to invest for known "
+ "analysis; 0=no limit"),
+ cl::init(1000000), cl::cat(PollyCategory), cl::Hidden);
+
+STATISTIC(KnownAnalyzed, "Number of successfully analyzed SCoPs");
+STATISTIC(KnownOutOfQuota,
+ "Analyses aborted because max_operations was reached");
+
+STATISTIC(TotalInstructionsCopied, "Number of copied instructions");
+STATISTIC(TotalKnownLoadsForwarded,
+ "Number of forwarded loads because their value was known");
+STATISTIC(TotalReloads, "Number of reloaded values");
+STATISTIC(TotalReadOnlyCopied, "Number of copied read-only accesses");
+STATISTIC(TotalForwardedTrees, "Number of forwarded operand trees");
+STATISTIC(TotalModifiedStmts,
+ "Number of statements with at least one forwarded tree");
+
+STATISTIC(ScopsModified, "Number of SCoPs with at least one forwarded tree");
+
+STATISTIC(NumValueWrites, "Number of scalar value writes after OpTree");
+STATISTIC(NumValueWritesInLoops,
+ "Number of scalar value writes nested in affine loops after OpTree");
+STATISTIC(NumPHIWrites, "Number of scalar phi writes after OpTree");
+STATISTIC(NumPHIWritesInLoops,
+ "Number of scalar phi writes nested in affine loops after OpTree");
+STATISTIC(NumSingletonWrites, "Number of singleton writes after OpTree");
+STATISTIC(NumSingletonWritesInLoops,
+ "Number of singleton writes nested in affine loops after OpTree");
+
+namespace {
+
+/// The state of whether an operand tree was/can be forwarded.
+///
+/// The items apply to an instructions and its operand tree with the instruction
+/// as the root element. If the value in question is not an instruction in the
+/// SCoP, it can be a leaf of an instruction's operand tree.
+enum ForwardingDecision {
+ /// An uninitialized value.
+ FD_Unknown,
+
+ /// The root instruction or value cannot be forwarded at all.
+ FD_CannotForward,
+
+ /// The root instruction or value can be forwarded as a leaf of a larger
+ /// operand tree.
+ /// It does not make sense to move the value itself, it would just replace it
+ /// by a use of itself. For instance, a constant "5" used in a statement can
+ /// be forwarded, but it would just replace it by the same constant "5".
+ /// However, it makes sense to move as an operand of
+ ///
+ /// %add = add 5, 5
+ ///
+ /// where "5" is moved as part of a larger operand tree. "5" would be placed
+ /// (disregarding for a moment that literal constants don't have a location
+ /// and can be used anywhere) into the same statement as %add would.
+ FD_CanForwardLeaf,
+
+ /// The root instruction can be forwarded and doing so avoids a scalar
+ /// dependency.
+ ///
+ /// This can be either because the operand tree can be moved to the target
+ /// statement, or a memory access is redirected to read from a different
+ /// location.
+ FD_CanForwardProfitably,
+
+ /// A forwarding method cannot be applied to the operand tree.
+ /// The difference to FD_CannotForward is that there might be other methods
+ /// that can handle it.
+ FD_NotApplicable
+};
+
+/// Represents the evaluation of and action to taken when forwarding a value
+/// from an operand tree.
+struct ForwardingAction {
+ using KeyTy = std::pair<Value *, ScopStmt *>;
+
+ /// Evaluation of forwarding a value.
+ ForwardingDecision Decision = FD_Unknown;
+
+ /// Callback to execute the forwarding.
+ /// Returning true allows deleting the polly::MemoryAccess if the value is the
+ /// root of the operand tree (and its elimination the reason why the
+ /// forwarding is done). Return false if the MemoryAccess is reused or there
+ /// might be other users of the read accesses. In the letter case the
+ /// polly::SimplifyPass can remove dead MemoryAccesses.
+ std::function<bool()> Execute = []() -> bool {
+ llvm_unreachable("unspecified how to forward");
+ };
+
+ /// Other values that need to be forwarded if this action is executed. Their
+ /// actions are executed after this one.
+ SmallVector<KeyTy, 4> Depends;
+
+ /// Named ctor: The method creating this object does not apply to the kind of
+ /// value, but other methods may.
+ static ForwardingAction notApplicable() {
+ ForwardingAction Result;
+ Result.Decision = FD_NotApplicable;
+ return Result;
+ }
+
+ /// Named ctor: The value cannot be forwarded.
+ static ForwardingAction cannotForward() {
+ ForwardingAction Result;
+ Result.Decision = FD_CannotForward;
+ return Result;
+ }
+
+ /// Named ctor: The value can just be used without any preparation.
+ static ForwardingAction triviallyForwardable(bool IsProfitable, Value *Val) {
+ ForwardingAction Result;
+ Result.Decision =
+ IsProfitable ? FD_CanForwardProfitably : FD_CanForwardLeaf;
+ Result.Execute = [=]() {
+ LLVM_DEBUG(dbgs() << " trivially forwarded: " << *Val << "\n");
+ return true;
+ };
+ return Result;
+ }
+
+ /// Name ctor: The value can be forwarded by executing an action.
+ static ForwardingAction canForward(std::function<bool()> Execute,
+ ArrayRef<KeyTy> Depends,
+ bool IsProfitable) {
+ ForwardingAction Result;
+ Result.Decision =
+ IsProfitable ? FD_CanForwardProfitably : FD_CanForwardLeaf;
+ Result.Execute = std::move(Execute);
+ Result.Depends.append(Depends.begin(), Depends.end());
+ return Result;
+ }
+};
+
+/// Implementation of operand tree forwarding for a specific SCoP.
+///
+/// For a statement that requires a scalar value (through a value read
+/// MemoryAccess), see if its operand can be moved into the statement. If so,
+/// the MemoryAccess is removed and the all the operand tree instructions are
+/// moved into the statement. All original instructions are left in the source
+/// statements. The simplification pass can clean these up.
+class ForwardOpTreeImpl : ZoneAlgorithm {
+private:
+ using MemoizationTy = DenseMap<ForwardingAction::KeyTy, ForwardingAction>;
+
+ /// Scope guard to limit the number of isl operations for this pass.
+ IslMaxOperationsGuard &MaxOpGuard;
+
+ /// How many instructions have been copied to other statements.
+ int NumInstructionsCopied = 0;
+
+ /// Number of loads forwarded because their value was known.
+ int NumKnownLoadsForwarded = 0;
+
+ /// Number of values reloaded from known array elements.
+ int NumReloads = 0;
+
+ /// How many read-only accesses have been copied.
+ int NumReadOnlyCopied = 0;
+
+ /// How many operand trees have been forwarded.
+ int NumForwardedTrees = 0;
+
+ /// Number of statements with at least one forwarded operand tree.
+ int NumModifiedStmts = 0;
+
+ /// Whether we carried out at least one change to the SCoP.
+ bool Modified = false;
+
+ /// Cache of how to forward values.
+ /// The key of this map is the llvm::Value to be forwarded and the
+ /// polly::ScopStmt it is forwarded from. This is because the same llvm::Value
+ /// can evaluate differently depending on where it is evaluate. For instance,
+ /// a synthesizable Scev represents a recurrence with an loop but the loop's
+ /// exit value if evaluated after the loop.
+ /// The cached results are only valid for the current TargetStmt.
+ /// CHECKME: ScalarEvolution::getScevAtScope should take care for getting the
+ /// exit value when instantiated outside of the loop. The primary concern is
+ /// ambiguity when crossing PHI nodes, which currently is not supported.
+ MemoizationTy ForwardingActions;
+
+ /// Contains the zones where array elements are known to contain a specific
+ /// value.
+ /// { [Element[] -> Zone[]] -> ValInst[] }
+ /// @see computeKnown()
+ isl::union_map Known;
+
+ /// Translator for newly introduced ValInsts to already existing ValInsts such
+ /// that new introduced load instructions can reuse the Known analysis of its
+ /// original load. { ValInst[] -> ValInst[] }
+ isl::union_map Translator;
+
+ /// Get list of array elements that do contain the same ValInst[] at Domain[].
+ ///
+ /// @param ValInst { Domain[] -> ValInst[] }
+ /// The values for which we search for alternative locations,
+ /// per statement instance.
+ ///
+ /// @return { Domain[] -> Element[] }
+ /// For each statement instance, the array elements that contain the
+ /// same ValInst.
+ isl::union_map findSameContentElements(isl::union_map ValInst) {
+ assert(!ValInst.is_single_valued().is_false());
+
+ // { Domain[] }
+ isl::union_set Domain = ValInst.domain();
+
+ // { Domain[] -> Scatter[] }
+ isl::union_map Schedule = getScatterFor(Domain);
+
+ // { Element[] -> [Scatter[] -> ValInst[]] }
+ isl::union_map MustKnownCurried =
+ convertZoneToTimepoints(Known, isl::dim::in, false, true).curry();
+
+ // { [Domain[] -> ValInst[]] -> Scatter[] }
+ isl::union_map DomValSched = ValInst.domain_map().apply_range(Schedule);
+
+ // { [Scatter[] -> ValInst[]] -> [Domain[] -> ValInst[]] }
+ isl::union_map SchedValDomVal =
+ DomValSched.range_product(ValInst.range_map()).reverse();
+
+ // { Element[] -> [Domain[] -> ValInst[]] }
+ isl::union_map MustKnownInst = MustKnownCurried.apply_range(SchedValDomVal);
+
+ // { Domain[] -> Element[] }
+ isl::union_map MustKnownMap =
+ MustKnownInst.uncurry().domain().unwrap().reverse();
+ simplify(MustKnownMap);
+
+ return MustKnownMap;
+ }
+
+ /// Find a single array element for each statement instance, within a single
+ /// array.
+ ///
+ /// @param MustKnown { Domain[] -> Element[] }
+ /// Set of candidate array elements.
+ /// @param Domain { Domain[] }
+ /// The statement instance for which we need elements for.
+ ///
+ /// @return { Domain[] -> Element[] }
+ /// For each statement instance, an array element out of @p MustKnown.
+ /// All array elements must be in the same array (Polly does not yet
+ /// support reading from different accesses using the same
+ /// MemoryAccess). If no mapping for all of @p Domain exists, returns
+ /// null.
+ isl::map singleLocation(isl::union_map MustKnown, isl::set Domain) {
+ // { Domain[] -> Element[] }
+ isl::map Result;
+
+ // Make irrelevant elements not interfere.
+ Domain = Domain.intersect_params(S->getContext());
+
+ // MemoryAccesses can read only elements from a single array
+ // (i.e. not: { Dom[0] -> A[0]; Dom[1] -> B[1] }).
+ // Look through all spaces until we find one that contains at least the
+ // wanted statement instance.s
+ for (isl::map Map : MustKnown.get_map_list()) {
+ // Get the array this is accessing.
+ isl::id ArrayId = Map.get_tuple_id(isl::dim::out);
+ ScopArrayInfo *SAI = static_cast<ScopArrayInfo *>(ArrayId.get_user());
+
+ // No support for generation of indirect array accesses.
+ if (SAI->getBasePtrOriginSAI())
+ continue;
+
+ // Determine whether this map contains all wanted values.
+ isl::set MapDom = Map.domain();
+ if (!Domain.is_subset(MapDom).is_true())
+ continue;
+
+ // There might be multiple array elements that contain the same value, but
+ // choose only one of them. lexmin is used because it returns a one-value
+ // mapping, we do not care about which one.
+ // TODO: Get the simplest access function.
+ Result = Map.lexmin();
+ break;
+ }
+
+ return Result;
+ }
+
+public:
+ ForwardOpTreeImpl(Scop *S, LoopInfo *LI, IslMaxOperationsGuard &MaxOpGuard)
+ : ZoneAlgorithm("polly-optree", S, LI), MaxOpGuard(MaxOpGuard) {}
+
+ /// Compute the zones of known array element contents.
+ ///
+ /// @return True if the computed #Known is usable.
+ bool computeKnownValues() {
+ isl::union_map MustKnown, KnownFromLoad, KnownFromInit;
+
+ // Check that nothing strange occurs.
+ collectCompatibleElts();
+
+ {
+ IslQuotaScope QuotaScope = MaxOpGuard.enter();
+
+ computeCommon();
+ if (NormalizePHIs)
+ computeNormalizedPHIs();
+ Known = computeKnown(true, true);
+
+ // Preexisting ValInsts use the known content analysis of themselves.
+ Translator = makeIdentityMap(Known.range(), false);
+ }
+
+ if (Known.is_null() || Translator.is_null() || NormalizeMap.is_null()) {
+ assert(isl_ctx_last_error(IslCtx.get()) == isl_error_quota);
+ Known = {};
+ Translator = {};
+ NormalizeMap = {};
+ LLVM_DEBUG(dbgs() << "Known analysis exceeded max_operations\n");
+ return false;
+ }
+
+ KnownAnalyzed++;
+ LLVM_DEBUG(dbgs() << "All known: " << Known << "\n");
+
+ return true;
+ }
+
+ void printStatistics(raw_ostream &OS, int Indent = 0) {
+ OS.indent(Indent) << "Statistics {\n";
+ OS.indent(Indent + 4) << "Instructions copied: " << NumInstructionsCopied
+ << '\n';
+ OS.indent(Indent + 4) << "Known loads forwarded: " << NumKnownLoadsForwarded
+ << '\n';
+ OS.indent(Indent + 4) << "Reloads: " << NumReloads << '\n';
+ OS.indent(Indent + 4) << "Read-only accesses copied: " << NumReadOnlyCopied
+ << '\n';
+ OS.indent(Indent + 4) << "Operand trees forwarded: " << NumForwardedTrees
+ << '\n';
+ OS.indent(Indent + 4) << "Statements with forwarded operand trees: "
+ << NumModifiedStmts << '\n';
+ OS.indent(Indent) << "}\n";
+ }
+
+ void printStatements(raw_ostream &OS, int Indent = 0) const {
+ OS.indent(Indent) << "After statements {\n";
+ for (auto &Stmt : *S) {
+ OS.indent(Indent + 4) << Stmt.getBaseName() << "\n";
+ for (auto *MA : Stmt)
+ MA->print(OS);
+
+ OS.indent(Indent + 12);
+ Stmt.printInstructions(OS);
+ }
+ OS.indent(Indent) << "}\n";
+ }
+
+ /// Create a new MemoryAccess of type read and MemoryKind::Array.
+ ///
+ /// @param Stmt The statement in which the access occurs.
+ /// @param LI The instruction that does the access.
+ /// @param AccessRelation The array element that each statement instance
+ /// accesses.
+ ///
+ /// @param The newly created access.
+ MemoryAccess *makeReadArrayAccess(ScopStmt *Stmt, LoadInst *LI,
+ isl::map AccessRelation) {
+ isl::id ArrayId = AccessRelation.get_tuple_id(isl::dim::out);
+ ScopArrayInfo *SAI = reinterpret_cast<ScopArrayInfo *>(ArrayId.get_user());
+
+ // Create a dummy SCEV access, to be replaced anyway.
+ SmallVector<const SCEV *, 4> Sizes;
+ Sizes.reserve(SAI->getNumberOfDimensions());
+ SmallVector<const SCEV *, 4> Subscripts;
+ Subscripts.reserve(SAI->getNumberOfDimensions());
+ for (unsigned i = 0; i < SAI->getNumberOfDimensions(); i += 1) {
+ Sizes.push_back(SAI->getDimensionSize(i));
+ Subscripts.push_back(nullptr);
+ }
+
+ MemoryAccess *Access =
+ new MemoryAccess(Stmt, LI, MemoryAccess::READ, SAI->getBasePtr(),
+ LI->getType(), true, {}, Sizes, LI, MemoryKind::Array);
+ S->addAccessFunction(Access);
+ Stmt->addAccess(Access, true);
+
+ Access->setNewAccessRelation(AccessRelation);
+
+ return Access;
+ }
+
+ /// Forward a load by reading from an array element that contains the same
+ /// value. Typically the location it was loaded from.
+ ///
+ /// @param TargetStmt The statement the operand tree will be copied to.
+ /// @param Inst The (possibly speculatable) instruction to forward.
+ /// @param UseStmt The statement that uses @p Inst.
+ /// @param UseLoop The loop @p Inst is used in.
+ /// @param DefStmt The statement @p Inst is defined in.
+ /// @param DefLoop The loop which contains @p Inst.
+ ///
+ /// @return A ForwardingAction object describing the feasibility and
+ /// profitability evaluation and the callback carrying-out the value
+ /// forwarding.
+ ForwardingAction forwardKnownLoad(ScopStmt *TargetStmt, Instruction *Inst,
+ ScopStmt *UseStmt, Loop *UseLoop,
+ ScopStmt *DefStmt, Loop *DefLoop) {
+ // Cannot do anything without successful known analysis.
+ if (Known.is_null() || Translator.is_null() ||
+ MaxOpGuard.hasQuotaExceeded())
+ return ForwardingAction::notApplicable();
+
+ LoadInst *LI = dyn_cast<LoadInst>(Inst);
+ if (!LI)
+ return ForwardingAction::notApplicable();
+
+ ForwardingDecision OpDecision =
+ forwardTree(TargetStmt, LI->getPointerOperand(), DefStmt, DefLoop);
+ switch (OpDecision) {
+ case FD_CanForwardProfitably:
+ case FD_CanForwardLeaf:
+ break;
+ case FD_CannotForward:
+ return ForwardingAction::cannotForward();
+ default:
+ llvm_unreachable("Shouldn't return this");
+ }
+
+ MemoryAccess *Access = TargetStmt->getArrayAccessOrNULLFor(LI);
+ if (Access) {
+ // If the load is already in the statement, no forwarding is necessary.
+ // However, it might happen that the LoadInst is already present in the
+ // statement's instruction list. In that case we do as follows:
+ // - For the evaluation, we can trivially forward it as it is
+ // benefit of forwarding an already present instruction.
+ // - For the execution, prepend the instruction (to make it
+ // available to all instructions following in the instruction list), but
+ // do not add another MemoryAccess.
+ auto ExecAction = [this, TargetStmt, LI, Access]() -> bool {
+ TargetStmt->prependInstruction(LI);
+ LLVM_DEBUG(
+ dbgs() << " forwarded known load with preexisting MemoryAccess"
+ << Access << "\n");
+ (void)Access;
+
+ NumKnownLoadsForwarded++;
+ TotalKnownLoadsForwarded++;
+ return true;
+ };
+ return ForwardingAction::canForward(
+ ExecAction, {{LI->getPointerOperand(), DefStmt}}, true);
+ }
+
+ // Allow the following Isl calculations (until we return the
+ // ForwardingAction, excluding the code inside the lambda that will be
+ // executed later) to fail.
+ IslQuotaScope QuotaScope = MaxOpGuard.enter();
+
+ // { DomainDef[] -> ValInst[] }
+ isl::map ExpectedVal = makeValInst(Inst, UseStmt, UseLoop);
+ assert(!isNormalized(ExpectedVal).is_false() &&
+ "LoadInsts are always normalized");
+
+ // { DomainUse[] -> DomainTarget[] }
+ isl::map UseToTarget = getDefToTarget(UseStmt, TargetStmt);
+
+ // { DomainTarget[] -> ValInst[] }
+ isl::map TargetExpectedVal = ExpectedVal.apply_domain(UseToTarget);
+ isl::union_map TranslatedExpectedVal =
+ isl::union_map(TargetExpectedVal).apply_range(Translator);
+
+ // { DomainTarget[] -> Element[] }
+ isl::union_map Candidates = findSameContentElements(TranslatedExpectedVal);
+
+ isl::map SameVal = singleLocation(Candidates, getDomainFor(TargetStmt));
+ if (SameVal.is_null())
+ return ForwardingAction::notApplicable();
+
+ LLVM_DEBUG(dbgs() << " expected values where " << TargetExpectedVal
+ << "\n");
+ LLVM_DEBUG(dbgs() << " candidate elements where " << Candidates
+ << "\n");
+
+ // { ValInst[] }
+ isl::space ValInstSpace = ExpectedVal.get_space().range();
+
+ // After adding a new load to the SCoP, also update the Known content
+ // about it. The new load will have a known ValInst of
+ // { [DomainTarget[] -> Value[]] }
+ // but which -- because it is a copy of it -- has same value as the
+ // { [DomainDef[] -> Value[]] }
+ // that it replicates. Instead of cloning the known content of
+ // [DomainDef[] -> Value[]]
+ // for DomainTarget[], we add a 'translator' that maps
+ // [DomainTarget[] -> Value[]] to [DomainDef[] -> Value[]]
+ // before comparing to the known content.
+ // TODO: 'Translator' could also be used to map PHINodes to their incoming
+ // ValInsts.
+ isl::map LocalTranslator;
+ if (!ValInstSpace.is_wrapping().is_false()) {
+ // { DefDomain[] -> Value[] }
+ isl::map ValInsts = ExpectedVal.range().unwrap();
+
+ // { DefDomain[] }
+ isl::set DefDomain = ValInsts.domain();
+
+ // { Value[] }
+ isl::space ValSpace = ValInstSpace.unwrap().range();
+
+ // { Value[] -> Value[] }
+ isl::map ValToVal =
+ isl::map::identity(ValSpace.map_from_domain_and_range(ValSpace));
+
+ // { DomainDef[] -> DomainTarget[] }
+ isl::map DefToTarget = getDefToTarget(DefStmt, TargetStmt);
+
+ // { [TargetDomain[] -> Value[]] -> [DefDomain[] -> Value] }
+ LocalTranslator = DefToTarget.reverse().product(ValToVal);
+ LLVM_DEBUG(dbgs() << " local translator is " << LocalTranslator
+ << "\n");
+
+ if (LocalTranslator.is_null())
+ return ForwardingAction::notApplicable();
+ }
+
+ auto ExecAction = [this, TargetStmt, LI, SameVal,
+ LocalTranslator]() -> bool {
+ TargetStmt->prependInstruction(LI);
+ MemoryAccess *Access = makeReadArrayAccess(TargetStmt, LI, SameVal);
+ LLVM_DEBUG(dbgs() << " forwarded known load with new MemoryAccess"
+ << Access << "\n");
+ (void)Access;
+
+ if (!LocalTranslator.is_null())
+ Translator = Translator.unite(LocalTranslator);
+
+ NumKnownLoadsForwarded++;
+ TotalKnownLoadsForwarded++;
+ return true;
+ };
+ return ForwardingAction::canForward(
+ ExecAction, {{LI->getPointerOperand(), DefStmt}}, true);
+ }
+
+ /// Forward a scalar by redirecting the access to an array element that stores
+ /// the same value.
+ ///
+ /// @param TargetStmt The statement the operand tree will be copied to.
+ /// @param Inst The scalar to forward.
+ /// @param UseStmt The statement that uses @p Inst.
+ /// @param UseLoop The loop @p Inst is used in.
+ /// @param DefStmt The statement @p Inst is defined in.
+ /// @param DefLoop The loop which contains @p Inst.
+ ///
+ /// @return A ForwardingAction object describing the feasibility and
+ /// profitability evaluation and the callback carrying-out the value
+ /// forwarding.
+ ForwardingAction reloadKnownContent(ScopStmt *TargetStmt, Instruction *Inst,
+ ScopStmt *UseStmt, Loop *UseLoop,
+ ScopStmt *DefStmt, Loop *DefLoop) {
+ // Cannot do anything without successful known analysis.
+ if (Known.is_null() || Translator.is_null() ||
+ MaxOpGuard.hasQuotaExceeded())
+ return ForwardingAction::notApplicable();
+
+ // Don't spend too much time analyzing whether it can be reloaded.
+ IslQuotaScope QuotaScope = MaxOpGuard.enter();
+
+ // { DomainDef[] -> ValInst[] }
+ isl::union_map ExpectedVal = makeNormalizedValInst(Inst, UseStmt, UseLoop);
+
+ // { DomainUse[] -> DomainTarget[] }
+ isl::map UseToTarget = getDefToTarget(UseStmt, TargetStmt);
+
+ // { DomainTarget[] -> ValInst[] }
+ isl::union_map TargetExpectedVal = ExpectedVal.apply_domain(UseToTarget);
+ isl::union_map TranslatedExpectedVal =
+ TargetExpectedVal.apply_range(Translator);
+
+ // { DomainTarget[] -> Element[] }
+ isl::union_map Candidates = findSameContentElements(TranslatedExpectedVal);
+
+ isl::map SameVal = singleLocation(Candidates, getDomainFor(TargetStmt));
+ simplify(SameVal);
+ if (SameVal.is_null())
+ return ForwardingAction::notApplicable();
+
+ auto ExecAction = [this, TargetStmt, Inst, SameVal]() {
+ MemoryAccess *Access = TargetStmt->lookupInputAccessOf(Inst);
+ if (!Access)
+ Access = TargetStmt->ensureValueRead(Inst);
+ Access->setNewAccessRelation(SameVal);
+
+ LLVM_DEBUG(dbgs() << " forwarded known content of " << *Inst
+ << " which is " << SameVal << "\n");
+ TotalReloads++;
+ NumReloads++;
+ return false;
+ };
+
+ return ForwardingAction::canForward(ExecAction, {}, true);
+ }
+
+ /// Forwards a speculatively executable instruction.
+ ///
+ /// @param TargetStmt The statement the operand tree will be copied to.
+ /// @param UseInst The (possibly speculatable) instruction to forward.
+ /// @param DefStmt The statement @p UseInst is defined in.
+ /// @param DefLoop The loop which contains @p UseInst.
+ ///
+ /// @return A ForwardingAction object describing the feasibility and
+ /// profitability evaluation and the callback carrying-out the value
+ /// forwarding.
+ ForwardingAction forwardSpeculatable(ScopStmt *TargetStmt,
+ Instruction *UseInst, ScopStmt *DefStmt,
+ Loop *DefLoop) {
+ // PHIs, unless synthesizable, are not yet supported.
+ if (isa<PHINode>(UseInst))
+ return ForwardingAction::notApplicable();
+
+ // Compatible instructions must satisfy the following conditions:
+ // 1. Idempotent (instruction will be copied, not moved; although its
+ // original instance might be removed by simplification)
+ // 2. Not access memory (There might be memory writes between)
+ // 3. Not cause undefined behaviour (we might copy to a location when the
+ // original instruction was no executed; this is currently not possible
+ // because we do not forward PHINodes)
+ // 4. Not leak memory if executed multiple times (i.e. malloc)
+ //
+ // Instruction::mayHaveSideEffects is not sufficient because it considers
+ // malloc to not have side-effects. llvm::isSafeToSpeculativelyExecute is
+ // not sufficient because it allows memory accesses.
+ if (mayBeMemoryDependent(*UseInst))
+ return ForwardingAction::notApplicable();
+
+ SmallVector<ForwardingAction::KeyTy, 4> Depends;
+ Depends.reserve(UseInst->getNumOperands());
+ for (Value *OpVal : UseInst->operand_values()) {
+ ForwardingDecision OpDecision =
+ forwardTree(TargetStmt, OpVal, DefStmt, DefLoop);
+ switch (OpDecision) {
+ case FD_CannotForward:
+ return ForwardingAction::cannotForward();
+
+ case FD_CanForwardLeaf:
+ case FD_CanForwardProfitably:
+ Depends.emplace_back(OpVal, DefStmt);
+ break;
+
+ case FD_NotApplicable:
+ case FD_Unknown:
+ llvm_unreachable(
+ "forwardTree should never return FD_NotApplicable/FD_Unknown");
+ }
+ }
+
+ auto ExecAction = [this, TargetStmt, UseInst]() {
+ // To ensure the right order, prepend this instruction before its
+ // operands. This ensures that its operands are inserted before the
+ // instruction using them.
+ TargetStmt->prependInstruction(UseInst);
+
+ LLVM_DEBUG(dbgs() << " forwarded speculable instruction: " << *UseInst
+ << "\n");
+ NumInstructionsCopied++;
+ TotalInstructionsCopied++;
+ return true;
+ };
+ return ForwardingAction::canForward(ExecAction, Depends, true);
+ }
+
+ /// Determines whether an operand tree can be forwarded and returns
+ /// instructions how to do so in the form of a ForwardingAction object.
+ ///
+ /// @param TargetStmt The statement the operand tree will be copied to.
+ /// @param UseVal The value (usually an instruction) which is root of an
+ /// operand tree.
+ /// @param UseStmt The statement that uses @p UseVal.
+ /// @param UseLoop The loop @p UseVal is used in.
+ ///
+ /// @return A ForwardingAction object describing the feasibility and
+ /// profitability evaluation and the callback carrying-out the value
+ /// forwarding.
+ ForwardingAction forwardTreeImpl(ScopStmt *TargetStmt, Value *UseVal,
+ ScopStmt *UseStmt, Loop *UseLoop) {
+ ScopStmt *DefStmt = nullptr;
+ Loop *DefLoop = nullptr;
+
+ // { DefDomain[] -> TargetDomain[] }
+ isl::map DefToTarget;
+
+ VirtualUse VUse = VirtualUse::create(UseStmt, UseLoop, UseVal, true);
+ switch (VUse.getKind()) {
+ case VirtualUse::Constant:
+ case VirtualUse::Block:
+ case VirtualUse::Hoisted:
+ // These can be used anywhere without special considerations.
+ return ForwardingAction::triviallyForwardable(false, UseVal);
+
+ case VirtualUse::Synthesizable: {
+ // Check if the value is synthesizable at the new location as well. This
+ // might be possible when leaving a loop for which ScalarEvolution is
+ // unable to derive the exit value for.
+ // TODO: If there is a LCSSA PHI at the loop exit, use that one.
+ // If the SCEV contains a SCEVAddRecExpr, we currently depend on that we
+ // do not forward past its loop header. This would require us to use a
+ // previous loop induction variable instead the current one. We currently
+ // do not allow forwarding PHI nodes, thus this should never occur (the
+ // only exception where no phi is necessary being an unreachable loop
+ // without edge from the outside).
+ VirtualUse TargetUse = VirtualUse::create(
+ S, TargetStmt, TargetStmt->getSurroundingLoop(), UseVal, true);
+ if (TargetUse.getKind() == VirtualUse::Synthesizable)
+ return ForwardingAction::triviallyForwardable(false, UseVal);
+
+ LLVM_DEBUG(
+ dbgs() << " Synthesizable would not be synthesizable anymore: "
+ << *UseVal << "\n");
+ return ForwardingAction::cannotForward();
+ }
+
+ case VirtualUse::ReadOnly: {
+ if (!ModelReadOnlyScalars)
+ return ForwardingAction::triviallyForwardable(false, UseVal);
+
+ // If we model read-only scalars, we need to create a MemoryAccess for it.
+ auto ExecAction = [this, TargetStmt, UseVal]() {
+ TargetStmt->ensureValueRead(UseVal);
+
+ LLVM_DEBUG(dbgs() << " forwarded read-only value " << *UseVal
+ << "\n");
+ NumReadOnlyCopied++;
+ TotalReadOnlyCopied++;
+
+ // Note that we cannot return true here. With a operand tree
+ // depth of 0, UseVal is the use in TargetStmt that we try to replace.
+ // With -polly-analyze-read-only-scalars=true we would ensure the
+ // existence of a MemoryAccess (which already exists for a leaf) and be
+ // removed again by tryForwardTree because it's goal is to remove this
+ // scalar MemoryAccess. It interprets FD_CanForwardTree as the
+ // permission to do so.
+ return false;
+ };
+ return ForwardingAction::canForward(ExecAction, {}, false);
+ }
+
+ case VirtualUse::Intra:
+ // Knowing that UseStmt and DefStmt are the same statement instance, just
+ // reuse the information about UseStmt for DefStmt
+ DefStmt = UseStmt;
+
+ LLVM_FALLTHROUGH;
+ case VirtualUse::Inter:
+ Instruction *Inst = cast<Instruction>(UseVal);
+
+ if (!DefStmt) {
+ DefStmt = S->getStmtFor(Inst);
+ if (!DefStmt)
+ return ForwardingAction::cannotForward();
+ }
+
+ DefLoop = LI->getLoopFor(Inst->getParent());
+
+ ForwardingAction SpeculativeResult =
+ forwardSpeculatable(TargetStmt, Inst, DefStmt, DefLoop);
+ if (SpeculativeResult.Decision != FD_NotApplicable)
+ return SpeculativeResult;
+
+ ForwardingAction KnownResult = forwardKnownLoad(
+ TargetStmt, Inst, UseStmt, UseLoop, DefStmt, DefLoop);
+ if (KnownResult.Decision != FD_NotApplicable)
+ return KnownResult;
+
+ ForwardingAction ReloadResult = reloadKnownContent(
+ TargetStmt, Inst, UseStmt, UseLoop, DefStmt, DefLoop);
+ if (ReloadResult.Decision != FD_NotApplicable)
+ return ReloadResult;
+
+ // When no method is found to forward the operand tree, we effectively
+ // cannot handle it.
+ LLVM_DEBUG(dbgs() << " Cannot forward instruction: " << *Inst << "\n");
+ return ForwardingAction::cannotForward();
+ }
+
+ llvm_unreachable("Case unhandled");
+ }
+
+ /// Determines whether an operand tree can be forwarded. Previous evaluations
+ /// are cached.
+ ///
+ /// @param TargetStmt The statement the operand tree will be copied to.
+ /// @param UseVal The value (usually an instruction) which is root of an
+ /// operand tree.
+ /// @param UseStmt The statement that uses @p UseVal.
+ /// @param UseLoop The loop @p UseVal is used in.
+ ///
+ /// @return FD_CannotForward if @p UseVal cannot be forwarded.
+ /// FD_CanForwardLeaf if @p UseVal is forwardable, but not
+ /// profitable.
+ /// FD_CanForwardProfitably if @p UseVal is forwardable and useful to
+ /// do.
+ ForwardingDecision forwardTree(ScopStmt *TargetStmt, Value *UseVal,
+ ScopStmt *UseStmt, Loop *UseLoop) {
+ // Lookup any cached evaluation.
+ auto It = ForwardingActions.find({UseVal, UseStmt});
+ if (It != ForwardingActions.end())
+ return It->second.Decision;
+
+ // Make a new evaluation.
+ ForwardingAction Action =
+ forwardTreeImpl(TargetStmt, UseVal, UseStmt, UseLoop);
+ ForwardingDecision Result = Action.Decision;
+
+ // Remember for the next time.
+ assert(!ForwardingActions.count({UseVal, UseStmt}) &&
+ "circular dependency?");
+ ForwardingActions.insert({{UseVal, UseStmt}, std::move(Action)});
+
+ return Result;
+ }
+
+ /// Forward an operand tree using cached actions.
+ ///
+ /// @param Stmt Statement the operand tree is moved into.
+ /// @param UseVal Root of the operand tree within @p Stmt.
+ /// @param RA The MemoryAccess for @p UseVal that the forwarding intends
+ /// to remove.
+ void applyForwardingActions(ScopStmt *Stmt, Value *UseVal, MemoryAccess *RA) {
+ using ChildItTy =
+ decltype(std::declval<ForwardingAction>().Depends.begin());
+ using EdgeTy = std::pair<ForwardingAction *, ChildItTy>;
+
+ DenseSet<ForwardingAction::KeyTy> Visited;
+ SmallVector<EdgeTy, 32> Stack;
+ SmallVector<ForwardingAction *, 32> Ordered;
+
+ // Seed the tree search using the root value.
+ assert(ForwardingActions.count({UseVal, Stmt}));
+ ForwardingAction *RootAction = &ForwardingActions[{UseVal, Stmt}];
+ Stack.emplace_back(RootAction, RootAction->Depends.begin());
+
+ // Compute the postorder of the operand tree: all operands of an instruction
+ // must be visited before the instruction itself. As an additional
+ // requirement, the topological ordering must be 'compact': Any subtree node
+ // must not be interleaved with nodes from a non-shared subtree. This is
+ // because the same llvm::Instruction can be materialized multiple times as
+ // used at different ScopStmts which might be different values. Intersecting
+ // these lifetimes may result in miscompilations.
+ // FIXME: Intersecting lifetimes might still be possible for the roots
+ // themselves, since instructions are just prepended to a ScopStmt's
+ // instruction list.
+ while (!Stack.empty()) {
+ EdgeTy &Top = Stack.back();
+ ForwardingAction *TopAction = Top.first;
+ ChildItTy &TopEdge = Top.second;
+
+ if (TopEdge == TopAction->Depends.end()) {
+ // Postorder sorting
+ Ordered.push_back(TopAction);
+ Stack.pop_back();
+ continue;
+ }
+ ForwardingAction::KeyTy Key = *TopEdge;
+
+ // Next edge for this level
+ ++TopEdge;
+
+ auto VisitIt = Visited.insert(Key);
+ if (!VisitIt.second)
+ continue;
+
+ assert(ForwardingActions.count(Key) &&
+ "Must not insert new actions during execution phase");
+ ForwardingAction *ChildAction = &ForwardingActions[Key];
+ Stack.emplace_back(ChildAction, ChildAction->Depends.begin());
+ }
+
+ // Actually, we need the reverse postorder because actions prepend new
+ // instructions. Therefore, the first one will always be the action for the
+ // operand tree's root.
+ assert(Ordered.back() == RootAction);
+ if (RootAction->Execute())
+ Stmt->removeSingleMemoryAccess(RA);
+ Ordered.pop_back();
+ for (auto DepAction : reverse(Ordered)) {
+ assert(DepAction->Decision != FD_Unknown &&
+ DepAction->Decision != FD_CannotForward);
+ assert(DepAction != RootAction);
+ DepAction->Execute();
+ }
+ }
+
+ /// Try to forward an operand tree rooted in @p RA.
+ bool tryForwardTree(MemoryAccess *RA) {
+ assert(RA->isLatestScalarKind());
+ LLVM_DEBUG(dbgs() << "Trying to forward operand tree " << RA << "...\n");
+
+ ScopStmt *Stmt = RA->getStatement();
+ Loop *InLoop = Stmt->getSurroundingLoop();
+
+ isl::map TargetToUse;
+ if (!Known.is_null()) {
+ isl::space DomSpace = Stmt->getDomainSpace();
+ TargetToUse =
+ isl::map::identity(DomSpace.map_from_domain_and_range(DomSpace));
+ }
+
+ ForwardingDecision Assessment =
+ forwardTree(Stmt, RA->getAccessValue(), Stmt, InLoop);
+
+ // If considered feasible and profitable, forward it.
+ bool Changed = false;
+ if (Assessment == FD_CanForwardProfitably) {
+ applyForwardingActions(Stmt, RA->getAccessValue(), RA);
+ Changed = true;
+ }
+
+ ForwardingActions.clear();
+ return Changed;
+ }
+
+ /// Return which SCoP this instance is processing.
+ Scop *getScop() const { return S; }
+
+ /// Run the algorithm: Use value read accesses as operand tree roots and try
+ /// to forward them into the statement.
+ bool forwardOperandTrees() {
+ for (ScopStmt &Stmt : *S) {
+ bool StmtModified = false;
+
+ // Because we are modifying the MemoryAccess list, collect them first to
+ // avoid iterator invalidation.
+ SmallVector<MemoryAccess *, 16> Accs(Stmt.begin(), Stmt.end());
+
+ for (MemoryAccess *RA : Accs) {
+ if (!RA->isRead())
+ continue;
+ if (!RA->isLatestScalarKind())
+ continue;
+
+ if (tryForwardTree(RA)) {
+ Modified = true;
+ StmtModified = true;
+ NumForwardedTrees++;
+ TotalForwardedTrees++;
+ }
+ }
+
+ if (StmtModified) {
+ NumModifiedStmts++;
+ TotalModifiedStmts++;
+ }
+ }
+
+ if (Modified) {
+ ScopsModified++;
+ S->realignParams();
+ }
+ return Modified;
+ }
+
+ /// Print the pass result, performed transformations and the SCoP after the
+ /// transformation.
+ void print(raw_ostream &OS, int Indent = 0) {
+ printStatistics(OS, Indent);
+
+ if (!Modified) {
+ // This line can easily be checked in regression tests.
+ OS << "ForwardOpTree executed, but did not modify anything\n";
+ return;
+ }
+
+ printStatements(OS, Indent);
+ }
+
+ bool isModified() const { return Modified; }
+};
+
+static std::unique_ptr<ForwardOpTreeImpl> runForwardOpTree(Scop &S,
+ LoopInfo &LI) {
+ std::unique_ptr<ForwardOpTreeImpl> Impl;
+ {
+ IslMaxOperationsGuard MaxOpGuard(S.getIslCtx().get(), MaxOps, false);
+ Impl = std::make_unique<ForwardOpTreeImpl>(&S, &LI, MaxOpGuard);
+
+ if (AnalyzeKnown) {
+ LLVM_DEBUG(dbgs() << "Prepare forwarders...\n");
+ Impl->computeKnownValues();
+ }
+
+ LLVM_DEBUG(dbgs() << "Forwarding operand trees...\n");
+ Impl->forwardOperandTrees();
+
+ if (MaxOpGuard.hasQuotaExceeded()) {
+ LLVM_DEBUG(dbgs() << "Not all operations completed because of "
+ "max_operations exceeded\n");
+ KnownOutOfQuota++;
+ }
+ }
+
+ LLVM_DEBUG(dbgs() << "\nFinal Scop:\n");
+ LLVM_DEBUG(dbgs() << S);
+
+ // Update statistics
+ Scop::ScopStatistics ScopStats = S.getStatistics();
+ NumValueWrites += ScopStats.NumValueWrites;
+ NumValueWritesInLoops += ScopStats.NumValueWritesInLoops;
+ NumPHIWrites += ScopStats.NumPHIWrites;
+ NumPHIWritesInLoops += ScopStats.NumPHIWritesInLoops;
+ NumSingletonWrites += ScopStats.NumSingletonWrites;
+ NumSingletonWritesInLoops += ScopStats.NumSingletonWritesInLoops;
+
+ return Impl;
+}
+
+static PreservedAnalyses
+runForwardOpTreeUsingNPM(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U,
+ raw_ostream *OS) {
+ LoopInfo &LI = SAR.LI;
+
+ std::unique_ptr<ForwardOpTreeImpl> Impl = runForwardOpTree(S, LI);
+ if (OS) {
+ *OS << "Printing analysis 'Polly - Forward operand tree' for region: '"
+ << S.getName() << "' in function '" << S.getFunction().getName()
+ << "':\n";
+ if (Impl) {
+ assert(Impl->getScop() == &S);
+
+ Impl->print(*OS);
+ }
+ }
+
+ if (!Impl->isModified())
+ return PreservedAnalyses::all();
+
+ PreservedAnalyses PA;
+ PA.preserveSet<AllAnalysesOn<Module>>();
+ PA.preserveSet<AllAnalysesOn<Function>>();
+ PA.preserveSet<AllAnalysesOn<Loop>>();
+ return PA;
+}
+
+/// Pass that redirects scalar reads to array elements that are known to contain
+/// the same value.
+///
+/// This reduces the number of scalar accesses and therefore potentially
+/// increases the freedom of the scheduler. In the ideal case, all reads of a
+/// scalar definition are redirected (We currently do not care about removing
+/// the write in this case). This is also useful for the main DeLICM pass as
+/// there are less scalars to be mapped.
+class ForwardOpTreeWrapperPass : public ScopPass {
+private:
+ /// The pass implementation, also holding per-scop data.
+ std::unique_ptr<ForwardOpTreeImpl> Impl;
+
+public:
+ static char ID;
+
+ explicit ForwardOpTreeWrapperPass() : ScopPass(ID) {}
+ ForwardOpTreeWrapperPass(const ForwardOpTreeWrapperPass &) = delete;
+ ForwardOpTreeWrapperPass &
+ operator=(const ForwardOpTreeWrapperPass &) = delete;
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequiredTransitive<ScopInfoRegionPass>();
+ AU.addRequired<LoopInfoWrapperPass>();
+ AU.setPreservesAll();
+ }
+
+ bool runOnScop(Scop &S) override {
+ // Free resources for previous SCoP's computation, if not yet done.
+ releaseMemory();
+
+ LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+
+ Impl = runForwardOpTree(S, LI);
+
+ return false;
+ }
+
+ void printScop(raw_ostream &OS, Scop &S) const override {
+ if (!Impl)
+ return;
+
+ assert(Impl->getScop() == &S);
+ Impl->print(OS);
+ }
+
+ void releaseMemory() override { Impl.reset(); }
+}; // class ForwardOpTree
+
+char ForwardOpTreeWrapperPass::ID;
+} // namespace
+
+Pass *polly::createForwardOpTreeWrapperPass() {
+ return new ForwardOpTreeWrapperPass();
+}
+
+INITIALIZE_PASS_BEGIN(ForwardOpTreeWrapperPass, "polly-optree",
+ "Polly - Forward operand tree", false, false)
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
+INITIALIZE_PASS_END(ForwardOpTreeWrapperPass, "polly-optree",
+ "Polly - Forward operand tree", false, false)
+
+llvm::PreservedAnalyses ForwardOpTreePass::run(Scop &S,
+ ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &U) {
+ return runForwardOpTreeUsingNPM(S, SAM, SAR, U, nullptr);
+}
+
+llvm::PreservedAnalyses
+ForwardOpTreePrinterPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U) {
+ return runForwardOpTreeUsingNPM(S, SAM, SAR, U, &OS);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/ManualOptimizer.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/ManualOptimizer.cpp
new file mode 100644
index 00000000000..ec4c584b27e
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/ManualOptimizer.cpp
@@ -0,0 +1,304 @@
+//===------ ManualOptimizer.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Handle pragma/metadata-directed transformations.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/ManualOptimizer.h"
+#include "polly/DependenceInfo.h"
+#include "polly/Options.h"
+#include "polly/ScheduleTreeTransform.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/Transforms/Utils/LoopUtils.h"
+
+#define DEBUG_TYPE "polly-opt-manual"
+
+using namespace polly;
+using namespace llvm;
+
+namespace {
+
+static cl::opt<bool> IgnoreDepcheck(
+ "polly-pragma-ignore-depcheck",
+ cl::desc("Skip the dependency check for pragma-based transformations"),
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+/// Same as llvm::hasUnrollTransformation(), but takes a LoopID as argument
+/// instead of a Loop.
+static TransformationMode hasUnrollTransformation(MDNode *LoopID) {
+ if (getBooleanLoopAttribute(LoopID, "llvm.loop.unroll.disable"))
+ return TM_SuppressedByUser;
+
+ Optional<int> Count =
+ getOptionalIntLoopAttribute(LoopID, "llvm.loop.unroll.count");
+ if (Count.hasValue())
+ return Count.getValue() == 1 ? TM_SuppressedByUser : TM_ForcedByUser;
+
+ if (getBooleanLoopAttribute(LoopID, "llvm.loop.unroll.enable"))
+ return TM_ForcedByUser;
+
+ if (getBooleanLoopAttribute(LoopID, "llvm.loop.unroll.full"))
+ return TM_ForcedByUser;
+
+ if (hasDisableAllTransformsHint(LoopID))
+ return TM_Disable;
+
+ return TM_Unspecified;
+}
+
+// Return the first DebugLoc in the list.
+static DebugLoc findFirstDebugLoc(MDNode *MD) {
+ if (MD) {
+ for (const MDOperand &X : drop_begin(MD->operands(), 1)) {
+ Metadata *A = X.get();
+ if (!isa<DILocation>(A))
+ continue;
+ return cast<DILocation>(A);
+ }
+ }
+
+ return {};
+}
+
+static DebugLoc findTransformationDebugLoc(MDNode *LoopMD, StringRef Name) {
+ // First find dedicated transformation location
+ // (such as the location of #pragma clang loop)
+ MDNode *MD = findOptionMDForLoopID(LoopMD, Name);
+ if (DebugLoc K = findFirstDebugLoc(MD))
+ return K;
+
+ // Otherwise, fall back to the location of the loop itself
+ return findFirstDebugLoc(LoopMD);
+}
+
+/// Apply full or partial unrolling.
+static isl::schedule applyLoopUnroll(MDNode *LoopMD,
+ isl::schedule_node BandToUnroll) {
+ TransformationMode UnrollMode = ::hasUnrollTransformation(LoopMD);
+ if (UnrollMode & TM_Disable)
+ return {};
+
+ assert(!BandToUnroll.is_null());
+ // TODO: Isl's codegen also supports unrolling by isl_ast_build via
+ // isl_schedule_node_band_set_ast_build_options({ unroll[x] }) which would be
+ // more efficient because the content duplication is delayed. However, the
+ // unrolled loop could be input of another loop transformation which expects
+ // the explicit schedule nodes. That is, we would need this explicit expansion
+ // anyway and using the ISL codegen option is a compile-time optimization.
+ int64_t Factor = getOptionalIntLoopAttribute(LoopMD, "llvm.loop.unroll.count")
+ .getValueOr(0);
+ bool Full = getBooleanLoopAttribute(LoopMD, "llvm.loop.unroll.full");
+ assert((!Full || !(Factor > 0)) &&
+ "Cannot unroll fully and partially at the same time");
+
+ if (Full)
+ return applyFullUnroll(BandToUnroll);
+
+ if (Factor > 0)
+ return applyPartialUnroll(BandToUnroll, Factor);
+
+ // For heuristic unrolling, fall back to LLVM's LoopUnroll pass.
+ return {};
+}
+
+static isl::schedule applyLoopFission(MDNode *LoopMD,
+ isl::schedule_node BandToFission) {
+ // TODO: Make it possible to selectively fission substatements.
+ // TODO: Apply followup loop properties.
+ // TODO: Instead of fission every statement, find the maximum set that does
+ // not cause a dependency violation.
+ return applyMaxFission(BandToFission);
+}
+
+// Return the properties from a LoopID. Scalar properties are ignored.
+static auto getLoopMDProps(MDNode *LoopMD) {
+ return map_range(
+ make_filter_range(
+ drop_begin(LoopMD->operands(), 1),
+ [](const MDOperand &MDOp) { return isa<MDNode>(MDOp.get()); }),
+ [](const MDOperand &MDOp) { return cast<MDNode>(MDOp.get()); });
+}
+
+/// Recursively visit all nodes in a schedule, loop for loop-transformations
+/// metadata and apply the first encountered.
+class SearchTransformVisitor
+ : public RecursiveScheduleTreeVisitor<SearchTransformVisitor> {
+private:
+ using BaseTy = RecursiveScheduleTreeVisitor<SearchTransformVisitor>;
+ BaseTy &getBase() { return *this; }
+ const BaseTy &getBase() const { return *this; }
+
+ polly::Scop *S;
+ const Dependences *D;
+ OptimizationRemarkEmitter *ORE;
+
+ // Set after a transformation is applied. Recursive search must be aborted
+ // once this happens to ensure that any new followup transformation is
+ // transformed in innermost-first order.
+ isl::schedule Result;
+
+ /// Check wether a schedule after a transformation is legal. Return the old
+ /// schedule without the transformation.
+ isl::schedule
+ checkDependencyViolation(llvm::MDNode *LoopMD, llvm::Value *CodeRegion,
+ const isl::schedule_node &OrigBand,
+ StringRef DebugLocAttr, StringRef TransPrefix,
+ StringRef RemarkName, StringRef TransformationName) {
+ if (D->isValidSchedule(*S, Result))
+ return Result;
+
+ LLVMContext &Ctx = LoopMD->getContext();
+ LLVM_DEBUG(dbgs() << "Dependency violation detected\n");
+
+ DebugLoc TransformLoc = findTransformationDebugLoc(LoopMD, DebugLocAttr);
+
+ if (IgnoreDepcheck) {
+ LLVM_DEBUG(dbgs() << "Still accepting transformation due to "
+ "-polly-pragma-ignore-depcheck\n");
+ if (ORE) {
+ ORE->emit(
+ OptimizationRemark(DEBUG_TYPE, RemarkName, TransformLoc, CodeRegion)
+ << (Twine("Could not verify dependencies for ") +
+ TransformationName +
+ "; still applying because of -polly-pragma-ignore-depcheck")
+ .str());
+ }
+ return Result;
+ }
+
+ LLVM_DEBUG(dbgs() << "Rolling back transformation\n");
+
+ if (ORE) {
+ ORE->emit(DiagnosticInfoOptimizationFailure(DEBUG_TYPE, RemarkName,
+ TransformLoc, CodeRegion)
+ << (Twine("not applying ") + TransformationName +
+ ": cannot ensure semantic equivalence due to possible "
+ "dependency violations")
+ .str());
+ }
+
+ // If illegal, revert and remove the transformation to not risk re-trying
+ // indefintely.
+ MDNode *NewLoopMD =
+ makePostTransformationMetadata(Ctx, LoopMD, {TransPrefix}, {});
+ BandAttr *Attr = getBandAttr(OrigBand);
+ Attr->Metadata = NewLoopMD;
+
+ // Roll back old schedule.
+ return OrigBand.get_schedule();
+ }
+
+public:
+ SearchTransformVisitor(polly::Scop *S, const Dependences *D,
+ OptimizationRemarkEmitter *ORE)
+ : S(S), D(D), ORE(ORE) {}
+
+ static isl::schedule applyOneTransformation(polly::Scop *S,
+ const Dependences *D,
+ OptimizationRemarkEmitter *ORE,
+ const isl::schedule &Sched) {
+ SearchTransformVisitor Transformer(S, D, ORE);
+ Transformer.visit(Sched);
+ return Transformer.Result;
+ }
+
+ void visitBand(isl::schedule_node_band Band) {
+ // Transform inner loops first (depth-first search).
+ getBase().visitBand(Band);
+ if (!Result.is_null())
+ return;
+
+ // Since it is (currently) not possible to have a BandAttr marker that is
+ // specific to each loop in a band, we only support single-loop bands.
+ if (isl_schedule_node_band_n_member(Band.get()) != 1)
+ return;
+
+ BandAttr *Attr = getBandAttr(Band);
+ if (!Attr) {
+ // Band has no attribute.
+ return;
+ }
+
+ // CodeRegion used but ORE to determine code hotness.
+ // TODO: Works only for original loop; for transformed loops, should track
+ // where the loop's body code comes from.
+ Loop *Loop = Attr->OriginalLoop;
+ Value *CodeRegion = nullptr;
+ if (Loop)
+ CodeRegion = Loop->getHeader();
+
+ MDNode *LoopMD = Attr->Metadata;
+ if (!LoopMD)
+ return;
+
+ // Iterate over loop properties to find the first transformation.
+ // FIXME: If there are more than one transformation in the LoopMD (making
+ // the order of transformations ambiguous), all others are silently ignored.
+ for (MDNode *MD : getLoopMDProps(LoopMD)) {
+ auto *NameMD = dyn_cast<MDString>(MD->getOperand(0).get());
+ if (!NameMD)
+ continue;
+ StringRef AttrName = NameMD->getString();
+
+ // Honor transformation order; transform the first transformation in the
+ // list first.
+ if (AttrName == "llvm.loop.unroll.enable" ||
+ AttrName == "llvm.loop.unroll.count" ||
+ AttrName == "llvm.loop.unroll.full") {
+ Result = applyLoopUnroll(LoopMD, Band);
+ if (!Result.is_null())
+ return;
+ } else if (AttrName == "llvm.loop.distribute.enable") {
+ Result = applyLoopFission(LoopMD, Band);
+ if (!Result.is_null())
+ Result = checkDependencyViolation(
+ LoopMD, CodeRegion, Band, "llvm.loop.distribute.loc",
+ "llvm.loop.distribute.", "FailedRequestedFission",
+ "loop fission/distribution");
+ if (!Result.is_null())
+ return;
+ }
+
+ // not a loop transformation; look for next property
+ }
+ }
+
+ void visitNode(isl::schedule_node Other) {
+ if (!Result.is_null())
+ return;
+ getBase().visitNode(Other);
+ }
+};
+
+} // namespace
+
+isl::schedule
+polly::applyManualTransformations(Scop *S, isl::schedule Sched,
+ const Dependences &D,
+ OptimizationRemarkEmitter *ORE) {
+ // Search the loop nest for transformations until fixpoint.
+ while (true) {
+ isl::schedule Result =
+ SearchTransformVisitor::applyOneTransformation(S, &D, ORE, Sched);
+ if (Result.is_null()) {
+ // No (more) transformation has been found.
+ break;
+ }
+
+ // Use transformed schedule and look for more transformations.
+ Sched = Result;
+ }
+
+ return Sched;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/MatmulOptimizer.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/MatmulOptimizer.cpp
new file mode 100644
index 00000000000..60dd9eda3c2
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/MatmulOptimizer.cpp
@@ -0,0 +1,1039 @@
+//===- MatmulOptimizer.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/MatmulOptimizer.h"
+#include "polly/DependenceInfo.h"
+#include "polly/Options.h"
+#include "polly/ScheduleTreeTransform.h"
+#include "polly/ScopInfo.h"
+#include "polly/ScopPass.h"
+#include "polly/Simplify.h"
+#include "polly/Support/ISLTools.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/TypeSize.h"
+#include "llvm/Support/raw_ostream.h"
+#include "isl/ctx.h"
+#include "isl/schedule_node.h"
+#include "isl/schedule_type.h"
+#include "isl/union_map.h"
+#include "isl/union_set.h"
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#define DEBUG_TYPE "polly-opt-isl"
+
+using namespace llvm;
+using namespace polly;
+
+namespace llvm {
+class Value;
+}
+
+static cl::opt<int> LatencyVectorFma(
+ "polly-target-latency-vector-fma",
+ cl::desc("The minimal number of cycles between issuing two "
+ "dependent consecutive vector fused multiply-add "
+ "instructions."),
+ cl::Hidden, cl::init(8), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> ThroughputVectorFma(
+ "polly-target-throughput-vector-fma",
+ cl::desc("A throughput of the processor floating-point arithmetic units "
+ "expressed in the number of vector fused multiply-add "
+ "instructions per clock cycle."),
+ cl::Hidden, cl::init(1), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> FirstCacheLevelSize(
+ "polly-target-1st-cache-level-size",
+ cl::desc("The size of the first cache level specified in bytes."),
+ cl::Hidden, cl::init(-1), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> FirstCacheLevelDefaultSize(
+ "polly-target-1st-cache-level-default-size",
+ cl::desc("The default size of the first cache level specified in bytes"
+ " (if not enough were provided by the TargetTransformInfo)."),
+ cl::Hidden, cl::init(32768), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> SecondCacheLevelSize(
+ "polly-target-2nd-cache-level-size",
+ cl::desc("The size of the second level specified in bytes."), cl::Hidden,
+ cl::init(-1), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> SecondCacheLevelDefaultSize(
+ "polly-target-2nd-cache-level-default-size",
+ cl::desc("The default size of the second cache level specified in bytes"
+ " (if not enough were provided by the TargetTransformInfo)."),
+ cl::Hidden, cl::init(262144), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+// This option, along with --polly-target-2nd-cache-level-associativity,
+// --polly-target-1st-cache-level-size, and --polly-target-2st-cache-level-size
+// represent the parameters of the target cache, which do not have typical
+// values that can be used by default. However, to apply the pattern matching
+// optimizations, we use the values of the parameters of Intel Core i7-3820
+// SandyBridge in case the parameters are not specified or not provided by the
+// TargetTransformInfo.
+static cl::opt<int> FirstCacheLevelAssociativity(
+ "polly-target-1st-cache-level-associativity",
+ cl::desc("The associativity of the first cache level."), cl::Hidden,
+ cl::init(-1), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> FirstCacheLevelDefaultAssociativity(
+ "polly-target-1st-cache-level-default-associativity",
+ cl::desc("The default associativity of the first cache level"
+ " (if not enough were provided by the TargetTransformInfo)."),
+ cl::Hidden, cl::init(8), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> SecondCacheLevelAssociativity(
+ "polly-target-2nd-cache-level-associativity",
+ cl::desc("The associativity of the second cache level."), cl::Hidden,
+ cl::init(-1), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> SecondCacheLevelDefaultAssociativity(
+ "polly-target-2nd-cache-level-default-associativity",
+ cl::desc("The default associativity of the second cache level"
+ " (if not enough were provided by the TargetTransformInfo)."),
+ cl::Hidden, cl::init(8), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> VectorRegisterBitwidth(
+ "polly-target-vector-register-bitwidth",
+ cl::desc("The size in bits of a vector register (if not set, this "
+ "information is taken from LLVM's target information."),
+ cl::Hidden, cl::init(-1), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> PollyPatternMatchingNcQuotient(
+ "polly-pattern-matching-nc-quotient",
+ cl::desc("Quotient that is obtained by dividing Nc, the parameter of the"
+ "macro-kernel, by Nr, the parameter of the micro-kernel"),
+ cl::Hidden, cl::init(256), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+namespace {
+/// Parameters of the micro kernel.
+///
+/// Parameters, which determine sizes of rank-1 (i.e., outer product) update
+/// used in the optimized matrix multiplication.
+struct MicroKernelParamsTy {
+ int Mr;
+ int Nr;
+};
+
+/// Parameters of the macro kernel.
+///
+/// Parameters, which determine sizes of blocks of partitioned matrices
+/// used in the optimized matrix multiplication.
+struct MacroKernelParamsTy {
+ int Mc;
+ int Nc;
+ int Kc;
+};
+
+/// Parameters of the matrix multiplication operands.
+///
+/// Parameters, which describe access relations that represent operands of the
+/// matrix multiplication.
+struct MatMulInfoTy {
+ MemoryAccess *A = nullptr;
+ MemoryAccess *B = nullptr;
+ MemoryAccess *ReadFromC = nullptr;
+ MemoryAccess *WriteToC = nullptr;
+ int i = -1;
+ int j = -1;
+ int k = -1;
+};
+
+/// Create an isl::union_set, which describes the option of the form
+/// [isolate[] -> unroll[x]].
+///
+/// @param Ctx An isl::ctx, which is used to create the isl::union_set.
+static isl::union_set getUnrollIsolatedSetOptions(isl::ctx Ctx) {
+ isl::space Space = isl::space(Ctx, 0, 0, 1);
+ isl::map UnrollIsolatedSetOption = isl::map::universe(Space);
+ isl::id DimInId = isl::id::alloc(Ctx, "isolate", nullptr);
+ isl::id DimOutId = isl::id::alloc(Ctx, "unroll", nullptr);
+ UnrollIsolatedSetOption =
+ UnrollIsolatedSetOption.set_tuple_id(isl::dim::in, DimInId);
+ UnrollIsolatedSetOption =
+ UnrollIsolatedSetOption.set_tuple_id(isl::dim::out, DimOutId);
+ return UnrollIsolatedSetOption.wrap();
+}
+
+/// Permute the two dimensions of the isl map.
+///
+/// Permute @p DstPos and @p SrcPos dimensions of the isl map @p Map that
+/// have type @p DimType.
+///
+/// @param Map The isl map to be modified.
+/// @param DimType The type of the dimensions.
+/// @param DstPos The first dimension.
+/// @param SrcPos The second dimension.
+/// @return The modified map.
+static isl::map permuteDimensions(isl::map Map, isl::dim DimType,
+ unsigned DstPos, unsigned SrcPos) {
+ assert(DstPos < unsignedFromIslSize(Map.dim(DimType)) &&
+ SrcPos < unsignedFromIslSize(Map.dim(DimType)));
+ if (DstPos == SrcPos)
+ return Map;
+ isl::id DimId;
+ if (Map.has_tuple_id(DimType))
+ DimId = Map.get_tuple_id(DimType);
+ auto FreeDim = DimType == isl::dim::in ? isl::dim::out : isl::dim::in;
+ isl::id FreeDimId;
+ if (Map.has_tuple_id(FreeDim))
+ FreeDimId = Map.get_tuple_id(FreeDim);
+ auto MaxDim = std::max(DstPos, SrcPos);
+ auto MinDim = std::min(DstPos, SrcPos);
+ Map = Map.move_dims(FreeDim, 0, DimType, MaxDim, 1);
+ Map = Map.move_dims(FreeDim, 0, DimType, MinDim, 1);
+ Map = Map.move_dims(DimType, MinDim, FreeDim, 1, 1);
+ Map = Map.move_dims(DimType, MaxDim, FreeDim, 0, 1);
+ if (!DimId.is_null())
+ Map = Map.set_tuple_id(DimType, DimId);
+ if (!FreeDimId.is_null())
+ Map = Map.set_tuple_id(FreeDim, FreeDimId);
+ return Map;
+}
+
+/// Check the form of the access relation.
+///
+/// Check that the access relation @p AccMap has the form M[i][j], where i
+/// is a @p FirstPos and j is a @p SecondPos.
+///
+/// @param AccMap The access relation to be checked.
+/// @param FirstPos The index of the input dimension that is mapped to
+/// the first output dimension.
+/// @param SecondPos The index of the input dimension that is mapped to the
+/// second output dimension.
+/// @return True in case @p AccMap has the expected form and false,
+/// otherwise.
+static bool isMatMulOperandAcc(isl::set Domain, isl::map AccMap, int &FirstPos,
+ int &SecondPos) {
+ isl::space Space = AccMap.get_space();
+ isl::map Universe = isl::map::universe(Space);
+
+ if (unsignedFromIslSize(Space.dim(isl::dim::out)) != 2)
+ return false;
+
+ // MatMul has the form:
+ // for (i = 0; i < N; i++)
+ // for (j = 0; j < M; j++)
+ // for (k = 0; k < P; k++)
+ // C[i, j] += A[i, k] * B[k, j]
+ //
+ // Permutation of three outer loops: 3! = 6 possibilities.
+ int FirstDims[] = {0, 0, 1, 1, 2, 2};
+ int SecondDims[] = {1, 2, 2, 0, 0, 1};
+ for (int i = 0; i < 6; i += 1) {
+ auto PossibleMatMul =
+ Universe.equate(isl::dim::in, FirstDims[i], isl::dim::out, 0)
+ .equate(isl::dim::in, SecondDims[i], isl::dim::out, 1);
+
+ AccMap = AccMap.intersect_domain(Domain);
+ PossibleMatMul = PossibleMatMul.intersect_domain(Domain);
+
+ // If AccMap spans entire domain (Non-partial write),
+ // compute FirstPos and SecondPos.
+ // If AccMap != PossibleMatMul here (the two maps have been gisted at
+ // this point), it means that the writes are not complete, or in other
+ // words, it is a Partial write and Partial writes must be rejected.
+ if (AccMap.is_equal(PossibleMatMul)) {
+ if (FirstPos != -1 && FirstPos != FirstDims[i])
+ continue;
+ FirstPos = FirstDims[i];
+ if (SecondPos != -1 && SecondPos != SecondDims[i])
+ continue;
+ SecondPos = SecondDims[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/// Does the memory access represent a non-scalar operand of the matrix
+/// multiplication.
+///
+/// Check that the memory access @p MemAccess is the read access to a non-scalar
+/// operand of the matrix multiplication or its result.
+///
+/// @param MemAccess The memory access to be checked.
+/// @param MMI Parameters of the matrix multiplication operands.
+/// @return True in case the memory access represents the read access
+/// to a non-scalar operand of the matrix multiplication and
+/// false, otherwise.
+static bool isMatMulNonScalarReadAccess(MemoryAccess *MemAccess,
+ MatMulInfoTy &MMI) {
+ if (!MemAccess->isLatestArrayKind() || !MemAccess->isRead())
+ return false;
+ auto AccMap = MemAccess->getLatestAccessRelation();
+ isl::set StmtDomain = MemAccess->getStatement()->getDomain();
+ if (isMatMulOperandAcc(StmtDomain, AccMap, MMI.i, MMI.j) && !MMI.ReadFromC) {
+ MMI.ReadFromC = MemAccess;
+ return true;
+ }
+ if (isMatMulOperandAcc(StmtDomain, AccMap, MMI.i, MMI.k) && !MMI.A) {
+ MMI.A = MemAccess;
+ return true;
+ }
+ if (isMatMulOperandAcc(StmtDomain, AccMap, MMI.k, MMI.j) && !MMI.B) {
+ MMI.B = MemAccess;
+ return true;
+ }
+ return false;
+}
+
+/// Check accesses to operands of the matrix multiplication.
+///
+/// Check that accesses of the SCoP statement, which corresponds to
+/// the partial schedule @p PartialSchedule, are scalar in terms of loops
+/// containing the matrix multiplication, in case they do not represent
+/// accesses to the non-scalar operands of the matrix multiplication or
+/// its result.
+///
+/// @param PartialSchedule The partial schedule of the SCoP statement.
+/// @param MMI Parameters of the matrix multiplication operands.
+/// @return True in case the corresponding SCoP statement
+/// represents matrix multiplication and false,
+/// otherwise.
+static bool containsOnlyMatrMultAcc(isl::map PartialSchedule,
+ MatMulInfoTy &MMI) {
+ auto InputDimId = PartialSchedule.get_tuple_id(isl::dim::in);
+ auto *Stmt = static_cast<ScopStmt *>(InputDimId.get_user());
+ unsigned OutDimNum = unsignedFromIslSize(PartialSchedule.range_tuple_dim());
+ assert(OutDimNum > 2 && "In case of the matrix multiplication the loop nest "
+ "and, consequently, the corresponding scheduling "
+ "functions have at least three dimensions.");
+ auto MapI =
+ permuteDimensions(PartialSchedule, isl::dim::out, MMI.i, OutDimNum - 1);
+ auto MapJ =
+ permuteDimensions(PartialSchedule, isl::dim::out, MMI.j, OutDimNum - 1);
+ auto MapK =
+ permuteDimensions(PartialSchedule, isl::dim::out, MMI.k, OutDimNum - 1);
+
+ auto Accesses = getAccessesInOrder(*Stmt);
+ for (auto *MemA = Accesses.begin(); MemA != Accesses.end() - 1; MemA++) {
+ auto *MemAccessPtr = *MemA;
+ if (MemAccessPtr->isLatestArrayKind() && MemAccessPtr != MMI.WriteToC &&
+ !isMatMulNonScalarReadAccess(MemAccessPtr, MMI) &&
+ !(MemAccessPtr->isStrideZero(MapI) &&
+ MemAccessPtr->isStrideZero(MapJ) && MemAccessPtr->isStrideZero(MapK)))
+ return false;
+ }
+ return true;
+}
+
+/// Check for dependencies corresponding to the matrix multiplication.
+///
+/// Check that there is only true dependence of the form
+/// S(..., k, ...) -> S(..., k + 1, …), where S is the SCoP statement
+/// represented by @p Schedule and k is @p Pos. Such a dependence corresponds
+/// to the dependency produced by the matrix multiplication.
+///
+/// @param Schedule The schedule of the SCoP statement.
+/// @param D The SCoP dependencies.
+/// @param Pos The parameter to describe an acceptable true dependence.
+/// In case it has a negative value, try to determine its
+/// acceptable value.
+/// @return True in case dependencies correspond to the matrix multiplication
+/// and false, otherwise.
+static bool containsOnlyMatMulDep(isl::map Schedule, const Dependences *D,
+ int &Pos) {
+ isl::union_map Dep = D->getDependences(Dependences::TYPE_RAW);
+ isl::union_map Red = D->getDependences(Dependences::TYPE_RED);
+ if (!Red.is_null())
+ Dep = Dep.unite(Red);
+ auto DomainSpace = Schedule.get_space().domain();
+ auto Space = DomainSpace.map_from_domain_and_range(DomainSpace);
+ auto Deltas = Dep.extract_map(Space).deltas();
+ int DeltasDimNum = unsignedFromIslSize(Deltas.dim(isl::dim::set));
+ for (int i = 0; i < DeltasDimNum; i++) {
+ auto Val = Deltas.plain_get_val_if_fixed(isl::dim::set, i);
+ Pos = Pos < 0 && Val.is_one() ? i : Pos;
+ if (Val.is_nan() || !(Val.is_zero() || (i == Pos && Val.is_one())))
+ return false;
+ }
+ if (DeltasDimNum == 0 || Pos < 0)
+ return false;
+ return true;
+}
+
+/// Check if the SCoP statement could probably be optimized with analytical
+/// modeling.
+///
+/// containsMatrMult tries to determine whether the following conditions
+/// are true:
+/// 1. The last memory access modeling an array, MA1, represents writing to
+/// memory and has the form S(..., i1, ..., i2, ...) -> M(i1, i2) or
+/// S(..., i2, ..., i1, ...) -> M(i1, i2), where S is the SCoP statement
+/// under consideration.
+/// 2. There is only one loop-carried true dependency, and it has the
+/// form S(..., i3, ...) -> S(..., i3 + 1, ...), and there are no
+/// loop-carried or anti dependencies.
+/// 3. SCoP contains three access relations, MA2, MA3, and MA4 that represent
+/// reading from memory and have the form S(..., i3, ...) -> M(i1, i3),
+/// S(..., i3, ...) -> M(i3, i2), S(...) -> M(i1, i2), respectively,
+/// and all memory accesses of the SCoP that are different from MA1, MA2,
+/// MA3, and MA4 have stride 0, if the innermost loop is exchanged with any
+/// of loops i1, i2 and i3.
+///
+/// @param PartialSchedule The PartialSchedule that contains a SCoP statement
+/// to check.
+/// @D The SCoP dependencies.
+/// @MMI Parameters of the matrix multiplication operands.
+static bool containsMatrMult(isl::map PartialSchedule, const Dependences *D,
+ MatMulInfoTy &MMI) {
+ auto InputDimsId = PartialSchedule.get_tuple_id(isl::dim::in);
+ auto *Stmt = static_cast<ScopStmt *>(InputDimsId.get_user());
+ if (Stmt->size() <= 1)
+ return false;
+
+ auto Accesses = getAccessesInOrder(*Stmt);
+ for (auto *MemA = Accesses.end() - 1; MemA != Accesses.begin(); MemA--) {
+ auto *MemAccessPtr = *MemA;
+ if (!MemAccessPtr->isLatestArrayKind())
+ continue;
+ if (!MemAccessPtr->isWrite())
+ return false;
+ auto AccMap = MemAccessPtr->getLatestAccessRelation();
+ if (!isMatMulOperandAcc(Stmt->getDomain(), AccMap, MMI.i, MMI.j))
+ return false;
+ MMI.WriteToC = MemAccessPtr;
+ break;
+ }
+
+ if (!containsOnlyMatMulDep(PartialSchedule, D, MMI.k))
+ return false;
+
+ if (!MMI.WriteToC || !containsOnlyMatrMultAcc(PartialSchedule, MMI))
+ return false;
+
+ if (!MMI.A || !MMI.B || !MMI.ReadFromC)
+ return false;
+ return true;
+}
+
+/// Permute two dimensions of the band node.
+///
+/// Permute FirstDim and SecondDim dimensions of the Node.
+///
+/// @param Node The band node to be modified.
+/// @param FirstDim The first dimension to be permuted.
+/// @param SecondDim The second dimension to be permuted.
+static isl::schedule_node permuteBandNodeDimensions(isl::schedule_node Node,
+ unsigned FirstDim,
+ unsigned SecondDim) {
+ assert(isl_schedule_node_get_type(Node.get()) == isl_schedule_node_band &&
+ (unsigned)isl_schedule_node_band_n_member(Node.get()) >
+ std::max(FirstDim, SecondDim));
+ auto PartialSchedule =
+ isl::manage(isl_schedule_node_band_get_partial_schedule(Node.get()));
+ auto PartialScheduleFirstDim = PartialSchedule.at(FirstDim);
+ auto PartialScheduleSecondDim = PartialSchedule.at(SecondDim);
+ PartialSchedule =
+ PartialSchedule.set_union_pw_aff(SecondDim, PartialScheduleFirstDim);
+ PartialSchedule =
+ PartialSchedule.set_union_pw_aff(FirstDim, PartialScheduleSecondDim);
+ Node = isl::manage(isl_schedule_node_delete(Node.release()));
+ return Node.insert_partial_schedule(PartialSchedule);
+}
+
+static isl::schedule_node
+createMicroKernel(isl::schedule_node Node,
+ MicroKernelParamsTy MicroKernelParams) {
+ Node = applyRegisterTiling(Node, {MicroKernelParams.Mr, MicroKernelParams.Nr},
+ 1);
+ Node = Node.parent().parent();
+ return permuteBandNodeDimensions(Node, 0, 1).child(0).child(0);
+}
+
+/// Create the BLIS macro-kernel.
+///
+/// We create the BLIS macro-kernel by applying a combination of tiling
+/// of dimensions of the band node and interchanging of two innermost
+/// modified dimensions. The values of of MacroKernelParams's fields are used
+/// as tile sizes.
+///
+/// @param Node The schedule node to be modified.
+/// @param MacroKernelParams Parameters of the macro kernel
+/// to be used as tile sizes.
+static isl::schedule_node
+createMacroKernel(isl::schedule_node Node,
+ MacroKernelParamsTy MacroKernelParams) {
+ assert(isl_schedule_node_get_type(Node.get()) == isl_schedule_node_band);
+ if (MacroKernelParams.Mc == 1 && MacroKernelParams.Nc == 1 &&
+ MacroKernelParams.Kc == 1)
+ return Node;
+ int DimOutNum = isl_schedule_node_band_n_member(Node.get());
+ std::vector<int> TileSizes(DimOutNum, 1);
+ TileSizes[DimOutNum - 3] = MacroKernelParams.Mc;
+ TileSizes[DimOutNum - 2] = MacroKernelParams.Nc;
+ TileSizes[DimOutNum - 1] = MacroKernelParams.Kc;
+ Node = tileNode(Node, "1st level tiling", TileSizes, 1);
+ Node = Node.parent().parent();
+ Node = permuteBandNodeDimensions(Node, DimOutNum - 2, DimOutNum - 1);
+ Node = permuteBandNodeDimensions(Node, DimOutNum - 3, DimOutNum - 1);
+
+ // Mark the outermost loop as parallelizable.
+ Node = Node.as<isl::schedule_node_band>().member_set_coincident(0, true);
+
+ return Node.child(0).child(0);
+}
+
+/// Get the size of the widest type of the matrix multiplication operands
+/// in bytes, including alignment padding.
+///
+/// @param MMI Parameters of the matrix multiplication operands.
+/// @return The size of the widest type of the matrix multiplication operands
+/// in bytes, including alignment padding.
+static uint64_t getMatMulAlignTypeSize(MatMulInfoTy MMI) {
+ auto *S = MMI.A->getStatement()->getParent();
+ auto &DL = S->getFunction().getParent()->getDataLayout();
+ auto ElementSizeA = DL.getTypeAllocSize(MMI.A->getElementType());
+ auto ElementSizeB = DL.getTypeAllocSize(MMI.B->getElementType());
+ auto ElementSizeC = DL.getTypeAllocSize(MMI.WriteToC->getElementType());
+ return std::max({ElementSizeA, ElementSizeB, ElementSizeC});
+}
+
+/// Get the size of the widest type of the matrix multiplication operands
+/// in bits.
+///
+/// @param MMI Parameters of the matrix multiplication operands.
+/// @return The size of the widest type of the matrix multiplication operands
+/// in bits.
+static uint64_t getMatMulTypeSize(MatMulInfoTy MMI) {
+ auto *S = MMI.A->getStatement()->getParent();
+ auto &DL = S->getFunction().getParent()->getDataLayout();
+ auto ElementSizeA = DL.getTypeSizeInBits(MMI.A->getElementType());
+ auto ElementSizeB = DL.getTypeSizeInBits(MMI.B->getElementType());
+ auto ElementSizeC = DL.getTypeSizeInBits(MMI.WriteToC->getElementType());
+ return std::max({ElementSizeA, ElementSizeB, ElementSizeC});
+}
+
+/// Get parameters of the BLIS micro kernel.
+///
+/// We choose the Mr and Nr parameters of the micro kernel to be large enough
+/// such that no stalls caused by the combination of latencies and dependencies
+/// are introduced during the updates of the resulting matrix of the matrix
+/// multiplication. However, they should also be as small as possible to
+/// release more registers for entries of multiplied matrices.
+///
+/// @param TTI Target Transform Info.
+/// @param MMI Parameters of the matrix multiplication operands.
+/// @return The structure of type MicroKernelParamsTy.
+/// @see MicroKernelParamsTy
+static struct MicroKernelParamsTy
+getMicroKernelParams(const TargetTransformInfo *TTI, MatMulInfoTy MMI) {
+ assert(TTI && "The target transform info should be provided.");
+
+ // Nvec - Number of double-precision floating-point numbers that can be hold
+ // by a vector register. Use 2 by default.
+ long RegisterBitwidth = VectorRegisterBitwidth;
+
+ if (RegisterBitwidth == -1)
+ RegisterBitwidth =
+ TTI->getRegisterBitWidth(TargetTransformInfo::RGK_FixedWidthVector);
+ auto ElementSize = getMatMulTypeSize(MMI);
+ assert(ElementSize > 0 && "The element size of the matrix multiplication "
+ "operands should be greater than zero.");
+ auto Nvec = RegisterBitwidth / ElementSize;
+ if (Nvec == 0)
+ Nvec = 2;
+ int Nr = ceil(sqrt((double)(Nvec * LatencyVectorFma * ThroughputVectorFma)) /
+ Nvec) *
+ Nvec;
+ int Mr = ceil((double)(Nvec * LatencyVectorFma * ThroughputVectorFma / Nr));
+ return {Mr, Nr};
+}
+
+/// Determine parameters of the target cache.
+///
+/// @param TTI Target Transform Info.
+static void getTargetCacheParameters(const llvm::TargetTransformInfo *TTI) {
+ auto L1DCache = llvm::TargetTransformInfo::CacheLevel::L1D;
+ auto L2DCache = llvm::TargetTransformInfo::CacheLevel::L2D;
+ if (FirstCacheLevelSize == -1) {
+ if (TTI->getCacheSize(L1DCache).hasValue())
+ FirstCacheLevelSize = TTI->getCacheSize(L1DCache).getValue();
+ else
+ FirstCacheLevelSize = static_cast<int>(FirstCacheLevelDefaultSize);
+ }
+ if (SecondCacheLevelSize == -1) {
+ if (TTI->getCacheSize(L2DCache).hasValue())
+ SecondCacheLevelSize = TTI->getCacheSize(L2DCache).getValue();
+ else
+ SecondCacheLevelSize = static_cast<int>(SecondCacheLevelDefaultSize);
+ }
+ if (FirstCacheLevelAssociativity == -1) {
+ if (TTI->getCacheAssociativity(L1DCache).hasValue())
+ FirstCacheLevelAssociativity =
+ TTI->getCacheAssociativity(L1DCache).getValue();
+ else
+ FirstCacheLevelAssociativity =
+ static_cast<int>(FirstCacheLevelDefaultAssociativity);
+ }
+ if (SecondCacheLevelAssociativity == -1) {
+ if (TTI->getCacheAssociativity(L2DCache).hasValue())
+ SecondCacheLevelAssociativity =
+ TTI->getCacheAssociativity(L2DCache).getValue();
+ else
+ SecondCacheLevelAssociativity =
+ static_cast<int>(SecondCacheLevelDefaultAssociativity);
+ }
+}
+
+/// Get parameters of the BLIS macro kernel.
+///
+/// During the computation of matrix multiplication, blocks of partitioned
+/// matrices are mapped to different layers of the memory hierarchy.
+/// To optimize data reuse, blocks should be ideally kept in cache between
+/// iterations. Since parameters of the macro kernel determine sizes of these
+/// blocks, there are upper and lower bounds on these parameters.
+///
+/// @param TTI Target Transform Info.
+/// @param MicroKernelParams Parameters of the micro-kernel
+/// to be taken into account.
+/// @param MMI Parameters of the matrix multiplication operands.
+/// @return The structure of type MacroKernelParamsTy.
+/// @see MacroKernelParamsTy
+/// @see MicroKernelParamsTy
+static struct MacroKernelParamsTy
+getMacroKernelParams(const llvm::TargetTransformInfo *TTI,
+ const MicroKernelParamsTy &MicroKernelParams,
+ MatMulInfoTy MMI) {
+ getTargetCacheParameters(TTI);
+ // According to www.cs.utexas.edu/users/flame/pubs/TOMS-BLIS-Analytical.pdf,
+ // it requires information about the first two levels of a cache to determine
+ // all the parameters of a macro-kernel. It also checks that an associativity
+ // degree of a cache level is greater than two. Otherwise, another algorithm
+ // for determination of the parameters should be used.
+ if (!(MicroKernelParams.Mr > 0 && MicroKernelParams.Nr > 0 &&
+ FirstCacheLevelSize > 0 && SecondCacheLevelSize > 0 &&
+ FirstCacheLevelAssociativity > 2 && SecondCacheLevelAssociativity > 2))
+ return {1, 1, 1};
+ // The quotient should be greater than zero.
+ if (PollyPatternMatchingNcQuotient <= 0)
+ return {1, 1, 1};
+ int Car = floor(
+ (FirstCacheLevelAssociativity - 1) /
+ (1 + static_cast<double>(MicroKernelParams.Nr) / MicroKernelParams.Mr));
+
+ // Car can be computed to be zero since it is floor to int.
+ // On Mac OS, division by 0 does not raise a signal. This causes negative
+ // tile sizes to be computed. Prevent division by Cac==0 by early returning
+ // if this happens.
+ if (Car == 0)
+ return {1, 1, 1};
+
+ auto ElementSize = getMatMulAlignTypeSize(MMI);
+ assert(ElementSize > 0 && "The element size of the matrix multiplication "
+ "operands should be greater than zero.");
+ int Kc = (Car * FirstCacheLevelSize) /
+ (MicroKernelParams.Mr * FirstCacheLevelAssociativity * ElementSize);
+ double Cac =
+ static_cast<double>(Kc * ElementSize * SecondCacheLevelAssociativity) /
+ SecondCacheLevelSize;
+ int Mc = floor((SecondCacheLevelAssociativity - 2) / Cac);
+ int Nc = PollyPatternMatchingNcQuotient * MicroKernelParams.Nr;
+
+ assert(Mc > 0 && Nc > 0 && Kc > 0 &&
+ "Matrix block sizes should be greater than zero");
+ return {Mc, Nc, Kc};
+}
+
+/// Create an access relation that is specific to
+/// the matrix multiplication pattern.
+///
+/// Create an access relation of the following form:
+/// [O0, O1, O2, O3, O4, O5, O6, O7, O8] -> [OI, O5, OJ]
+/// where I is @p FirstDim, J is @p SecondDim.
+///
+/// It can be used, for example, to create relations that helps to consequently
+/// access elements of operands of a matrix multiplication after creation of
+/// the BLIS micro and macro kernels.
+///
+/// @see ScheduleTreeOptimizer::createMicroKernel
+/// @see ScheduleTreeOptimizer::createMacroKernel
+///
+/// Subsequently, the described access relation is applied to the range of
+/// @p MapOldIndVar, that is used to map original induction variables to
+/// the ones, which are produced by schedule transformations. It helps to
+/// define relations using a new space and, at the same time, keep them
+/// in the original one.
+///
+/// @param MapOldIndVar The relation, which maps original induction variables
+/// to the ones, which are produced by schedule
+/// transformations.
+/// @param FirstDim, SecondDim The input dimensions that are used to define
+/// the specified access relation.
+/// @return The specified access relation.
+static isl::map getMatMulAccRel(isl::map MapOldIndVar, unsigned FirstDim,
+ unsigned SecondDim) {
+ auto AccessRelSpace = isl::space(MapOldIndVar.ctx(), 0, 9, 3);
+ auto AccessRel = isl::map::universe(AccessRelSpace);
+ AccessRel = AccessRel.equate(isl::dim::in, FirstDim, isl::dim::out, 0);
+ AccessRel = AccessRel.equate(isl::dim::in, 5, isl::dim::out, 1);
+ AccessRel = AccessRel.equate(isl::dim::in, SecondDim, isl::dim::out, 2);
+ return MapOldIndVar.apply_range(AccessRel);
+}
+
+static isl::schedule_node createExtensionNode(isl::schedule_node Node,
+ isl::map ExtensionMap) {
+ auto Extension = isl::union_map(ExtensionMap);
+ auto NewNode = isl::schedule_node::from_extension(Extension);
+ return Node.graft_before(NewNode);
+}
+
+static isl::schedule_node optimizePackedB(isl::schedule_node Node,
+ ScopStmt *Stmt, isl::map MapOldIndVar,
+ MicroKernelParamsTy MicroParams,
+ MacroKernelParamsTy MacroParams,
+ MatMulInfoTy &MMI) {
+ Scop *S = Stmt->getParent();
+ isl::set Domain = Stmt->getDomain();
+
+ // Create packed array.
+ unsigned FirstDimSize = MacroParams.Nc / MicroParams.Nr;
+ unsigned SecondDimSize = MacroParams.Kc;
+ unsigned ThirdDimSize = MicroParams.Nr;
+ ScopArrayInfo *PackedB =
+ S->createScopArrayInfo(MMI.B->getElementType(), "Packed_B",
+ {FirstDimSize, SecondDimSize, ThirdDimSize});
+
+ // Compute the access relation for copying from B to PackedB.
+ isl::map AccRelB = MMI.B->getLatestAccessRelation();
+ isl::map AccRelPackedB = getMatMulAccRel(MapOldIndVar, 3, 7);
+ AccRelPackedB =
+ AccRelPackedB.set_tuple_id(isl::dim::out, PackedB->getBasePtrId());
+
+ // Create the copy statement and redirect access.
+ ScopStmt *CopyStmt = S->addScopStmt(AccRelB, AccRelPackedB, Domain);
+ MMI.B->setNewAccessRelation(AccRelPackedB);
+
+ unsigned Dim = unsignedFromIslSize(MapOldIndVar.range_tuple_dim());
+ assert(Dim >= 2);
+ // Insert into the schedule tree.
+ isl::map ExtMap = MapOldIndVar.project_out(isl::dim::out, 2, Dim - 2);
+ ExtMap = ExtMap.reverse();
+ ExtMap = ExtMap.fix_si(isl::dim::out, MMI.i, 0);
+ ExtMap = ExtMap.intersect_range(Domain);
+ ExtMap = ExtMap.set_tuple_id(isl::dim::out, CopyStmt->getDomainId());
+ return createExtensionNode(Node, ExtMap);
+}
+
+static isl::schedule_node optimizePackedA(isl::schedule_node Node, ScopStmt *,
+ isl::map MapOldIndVar,
+ MicroKernelParamsTy MicroParams,
+ MacroKernelParamsTy MacroParams,
+ MatMulInfoTy &MMI) {
+ isl::id InputDimsId = MapOldIndVar.get_tuple_id(isl::dim::in);
+ ScopStmt *Stmt = static_cast<ScopStmt *>(InputDimsId.get_user());
+ isl::set Domain = Stmt->getDomain();
+ isl::id DomainId = Domain.get_tuple_id();
+
+ // Create the packed array.
+ unsigned FirstDimSize = MacroParams.Mc / MicroParams.Mr;
+ unsigned SecondDimSize = MacroParams.Kc;
+ unsigned ThirdDimSize = MicroParams.Mr;
+ ScopArrayInfo *PackedA = Stmt->getParent()->createScopArrayInfo(
+ MMI.A->getElementType(), "Packed_A",
+ {FirstDimSize, SecondDimSize, ThirdDimSize});
+
+ // Compute the access relation for copying from A to PackedA.
+ isl::map AccRelA = MMI.A->getLatestAccessRelation();
+ isl::map AccRelPackedA = getMatMulAccRel(MapOldIndVar, 4, 6);
+ AccRelPackedA =
+ AccRelPackedA.set_tuple_id(isl::dim::out, PackedA->getBasePtrId());
+ // { MemrefA[] -> PackedA[] }
+ isl::map PackedATranslator = AccRelPackedA.apply_domain(AccRelA);
+
+ // Compute the domain for the copy statement.
+ // Construct the copy statement domain out of the 3 outermost scatter
+ // dimensions (to match the 3 band nodes surrounding the extension node) and
+ // the array elements to copy (one statement instance per array element).
+ // { Scatter[] }
+ isl::set ScatterDomain = MapOldIndVar.intersect_domain(Domain).range();
+ // { Scatter[] -> OutermostScatter[] }
+ isl::map OuterDomainMap =
+ makeIdentityMap(ScatterDomain, true).project_out(isl::dim::out, 3, 6);
+ // { Scatter[] -> MemrefA[] }
+ isl::map CopyFrom = MapOldIndVar.reverse().apply_range(AccRelA);
+ // { Scatter[] -> CopyStmt[] }
+ isl::map DomainTranslator = OuterDomainMap.range_product(CopyFrom);
+ // { CopyStmt[] }
+ isl::set CopyDomain = DomainTranslator.range();
+
+ // Translate the access relations to the new domain.
+ // { CopyStmt[] -> MemrefA[] }
+ CopyFrom = CopyFrom.apply_domain(DomainTranslator);
+ // { CopyStmt[] -> PackedA[] }
+ isl::map CopyTo = CopyFrom.apply_range(PackedATranslator);
+
+ // Create the copy statement and redirect access.
+ ScopStmt *CopyStmt =
+ Stmt->getParent()->addScopStmt(CopyFrom, CopyTo, CopyDomain);
+ MMI.A->setNewAccessRelation(AccRelPackedA);
+
+ // Insert into the schedule tree.
+ // { Scatter[] -> CopyStmt[] }
+ isl::map ExtScatterCopy = makeIdentityMap(CopyStmt->getDomain(), true);
+ ExtScatterCopy = ExtScatterCopy.project_out(isl::dim::in, 3, 2);
+ return createExtensionNode(Node, ExtScatterCopy);
+}
+
+/// Apply the packing transformation.
+///
+/// The packing transformation can be described as a data-layout
+/// transformation that requires to introduce a new array, copy data
+/// to the array, and change memory access locations to reference the array.
+/// It can be used to ensure that elements of the new array are read in-stride
+/// access, aligned to cache lines boundaries, and preloaded into certain cache
+/// levels.
+///
+/// As an example let us consider the packing of the array A that would help
+/// to read its elements with in-stride access. An access to the array A
+/// is represented by an access relation that has the form
+/// S[i, j, k] -> A[i, k]. The scheduling function of the SCoP statement S has
+/// the form S[i,j, k] -> [floor((j mod Nc) / Nr), floor((i mod Mc) / Mr),
+/// k mod Kc, j mod Nr, i mod Mr].
+///
+/// To ensure that elements of the array A are read in-stride access, we add
+/// a new array Packed_A[Mc/Mr][Kc][Mr] to the SCoP, using
+/// Scop::createScopArrayInfo, change the access relation
+/// S[i, j, k] -> A[i, k] to
+/// S[i, j, k] -> Packed_A[floor((i mod Mc) / Mr), k mod Kc, i mod Mr], using
+/// MemoryAccess::setNewAccessRelation, and copy the data to the array, using
+/// the copy statement created by Scop::addScopStmt.
+///
+/// @param Node The schedule node to be optimized.
+/// @param MapOldIndVar The relation, which maps original induction variables
+/// to the ones, which are produced by schedule
+/// transformations.
+/// @param MicroParams, MacroParams Parameters of the BLIS kernel
+/// to be taken into account.
+/// @param MMI Parameters of the matrix multiplication operands.
+/// @return The optimized schedule node.
+static isl::schedule_node
+optimizeDataLayoutMatrMulPattern(isl::schedule_node Node, isl::map MapOldIndVar,
+ MicroKernelParamsTy MicroParams,
+ MacroKernelParamsTy MacroParams,
+ MatMulInfoTy &MMI) {
+ isl::id InputDimsId = MapOldIndVar.get_tuple_id(isl::dim::in);
+ ScopStmt *Stmt = static_cast<ScopStmt *>(InputDimsId.get_user());
+
+ Node = Node.parent().parent().parent().parent().parent().parent();
+ Node = isl::manage(isl_schedule_node_band_split(Node.release(), 2));
+
+ Node = Node.child(0);
+ Node =
+ optimizePackedB(Node, Stmt, MapOldIndVar, MicroParams, MacroParams, MMI);
+
+ Node = Node.child(0);
+ Node =
+ optimizePackedA(Node, Stmt, MapOldIndVar, MicroParams, MacroParams, MMI);
+
+ return Node.child(0).child(0).child(0).child(0).child(0);
+}
+
+/// Get a relation mapping induction variables produced by schedule
+/// transformations to the original ones.
+///
+/// @param Node The schedule node produced as the result of creation
+/// of the BLIS kernels.
+/// @param MicroKernelParams, MacroKernelParams Parameters of the BLIS kernel
+/// to be taken into account.
+/// @return The relation mapping original induction variables to the ones
+/// produced by schedule transformation.
+/// @see ScheduleTreeOptimizer::createMicroKernel
+/// @see ScheduleTreeOptimizer::createMacroKernel
+/// @see getMacroKernelParams
+static isl::map
+getInductionVariablesSubstitution(isl::schedule_node Node,
+ MicroKernelParamsTy MicroKernelParams,
+ MacroKernelParamsTy MacroKernelParams) {
+ auto Child = Node.child(0);
+ auto UnMapOldIndVar = Child.get_prefix_schedule_union_map();
+ auto MapOldIndVar = isl::map::from_union_map(UnMapOldIndVar);
+ unsigned Dim = unsignedFromIslSize(MapOldIndVar.range_tuple_dim());
+ if (Dim > 9u)
+ return MapOldIndVar.project_out(isl::dim::out, 0, Dim - 9);
+ return MapOldIndVar;
+}
+
+/// Isolate a set of partial tile prefixes and unroll the isolated part.
+///
+/// The set should ensure that it contains only partial tile prefixes that have
+/// exactly Mr x Nr iterations of the two innermost loops produced by
+/// the optimization of the matrix multiplication. Mr and Nr are parameters of
+/// the micro-kernel.
+///
+/// In case of parametric bounds, this helps to auto-vectorize the unrolled
+/// innermost loops, using the SLP vectorizer.
+///
+/// @param Node The schedule node to be modified.
+/// @param MicroKernelParams Parameters of the micro-kernel
+/// to be taken into account.
+/// @return The modified isl_schedule_node.
+static isl::schedule_node
+isolateAndUnrollMatMulInnerLoops(isl::schedule_node Node,
+ struct MicroKernelParamsTy MicroKernelParams) {
+ isl::schedule_node Child = Node.child(0);
+ isl::union_map UnMapOldIndVar = Child.get_prefix_schedule_relation();
+ isl::set Prefix = isl::map::from_union_map(UnMapOldIndVar).range();
+ unsigned Dims = unsignedFromIslSize(Prefix.tuple_dim());
+ assert(Dims >= 1);
+ Prefix = Prefix.project_out(isl::dim::set, Dims - 1, 1);
+ Prefix = getPartialTilePrefixes(Prefix, MicroKernelParams.Nr);
+ Prefix = getPartialTilePrefixes(Prefix, MicroKernelParams.Mr);
+
+ isl::union_set IsolateOption =
+ getIsolateOptions(Prefix.add_dims(isl::dim::set, 3), 3);
+ isl::ctx Ctx = Node.ctx();
+ auto Options = IsolateOption.unite(getDimOptions(Ctx, "unroll"));
+ Options = Options.unite(getUnrollIsolatedSetOptions(Ctx));
+ Node = Node.as<isl::schedule_node_band>().set_ast_build_options(Options);
+ Node = Node.parent().parent().parent();
+ IsolateOption = getIsolateOptions(Prefix, 3);
+ Options = IsolateOption.unite(getDimOptions(Ctx, "separate"));
+ Node = Node.as<isl::schedule_node_band>().set_ast_build_options(Options);
+ Node = Node.child(0).child(0).child(0);
+ return Node;
+}
+
+/// Insert "Loop Vectorizer Disabled" mark node.
+///
+/// @param Node The child of the mark node to be inserted.
+/// @return The modified isl_schedule_node.
+static isl::schedule_node markLoopVectorizerDisabled(isl::schedule_node Node) {
+ auto Id = isl::id::alloc(Node.ctx(), "Loop Vectorizer Disabled", nullptr);
+ return Node.insert_mark(Id).child(0);
+}
+
+/// Restore the initial ordering of dimensions of the band node
+///
+/// In case the band node represents all the dimensions of the iteration
+/// domain, recreate the band node to restore the initial ordering of the
+/// dimensions.
+///
+/// @param Node The band node to be modified.
+/// @return The modified schedule node.
+static isl::schedule_node
+getBandNodeWithOriginDimOrder(isl::schedule_node Node) {
+ assert(isl_schedule_node_get_type(Node.get()) == isl_schedule_node_band);
+ if (isl_schedule_node_get_type(Node.child(0).get()) != isl_schedule_node_leaf)
+ return Node;
+ auto Domain = Node.get_universe_domain();
+ assert(isl_union_set_n_set(Domain.get()) == 1);
+ if (Node.get_schedule_depth().release() != 0 ||
+ (unsignedFromIslSize(isl::set(Domain).tuple_dim()) !=
+ unsignedFromIslSize(Node.as<isl::schedule_node_band>().n_member())))
+ return Node;
+ Node = isl::manage(isl_schedule_node_delete(Node.copy()));
+ auto PartialSchedulePwAff = Domain.identity_union_pw_multi_aff();
+ auto PartialScheduleMultiPwAff =
+ isl::multi_union_pw_aff(PartialSchedulePwAff);
+ PartialScheduleMultiPwAff =
+ PartialScheduleMultiPwAff.reset_tuple_id(isl::dim::set);
+ return Node.insert_partial_schedule(PartialScheduleMultiPwAff);
+}
+
+static isl::schedule_node optimizeMatMulPattern(isl::schedule_node Node,
+ const TargetTransformInfo *TTI,
+ MatMulInfoTy &MMI) {
+ assert(TTI && "The target transform info should be provided.");
+ int DimOutNum = isl_schedule_node_band_n_member(Node.get());
+ assert(DimOutNum > 2 && "In case of the matrix multiplication the loop nest "
+ "and, consequently, the corresponding scheduling "
+ "functions have at least three dimensions.");
+ Node = getBandNodeWithOriginDimOrder(Node);
+ Node = permuteBandNodeDimensions(Node, MMI.i, DimOutNum - 3);
+ int NewJ = MMI.j == DimOutNum - 3 ? MMI.i : MMI.j;
+ int NewK = MMI.k == DimOutNum - 3 ? MMI.i : MMI.k;
+ Node = permuteBandNodeDimensions(Node, NewJ, DimOutNum - 2);
+ NewK = NewK == DimOutNum - 2 ? NewJ : NewK;
+ Node = permuteBandNodeDimensions(Node, NewK, DimOutNum - 1);
+ auto MicroKernelParams = getMicroKernelParams(TTI, MMI);
+ auto MacroKernelParams = getMacroKernelParams(TTI, MicroKernelParams, MMI);
+ Node = createMacroKernel(Node, MacroKernelParams);
+ Node = createMicroKernel(Node, MicroKernelParams);
+ if (MacroKernelParams.Mc == 1 || MacroKernelParams.Nc == 1 ||
+ MacroKernelParams.Kc == 1)
+ return Node;
+ auto MapOldIndVar = getInductionVariablesSubstitution(Node, MicroKernelParams,
+ MacroKernelParams);
+ if (MapOldIndVar.is_null())
+ return Node;
+ Node = markLoopVectorizerDisabled(Node.parent()).child(0);
+ Node = isolateAndUnrollMatMulInnerLoops(Node, MicroKernelParams);
+ return optimizeDataLayoutMatrMulPattern(Node, MapOldIndVar, MicroKernelParams,
+ MacroKernelParams, MMI);
+}
+
+/// Check if this node contains a partial schedule that could
+/// probably be optimized with analytical modeling.
+///
+/// isMatrMultPattern tries to determine whether the following conditions
+/// are true:
+/// 1. the partial schedule contains only one statement.
+/// 2. there are exactly three input dimensions.
+/// 3. all memory accesses of the statement will have stride 0 or 1, if we
+/// interchange loops (switch the variable used in the inner loop to
+/// the outer loop).
+/// 4. all memory accesses of the statement except from the last one, are
+/// read memory access and the last one is write memory access.
+/// 5. all subscripts of the last memory access of the statement don't
+/// contain the variable used in the inner loop.
+/// If this is the case, we could try to use an approach that is similar to
+/// the one used to get close-to-peak performance of matrix multiplications.
+///
+/// @param Node The node to check.
+/// @param D The SCoP dependencies.
+/// @param MMI Parameters of the matrix multiplication operands.
+static bool isMatrMultPattern(isl::schedule_node Node, const Dependences *D,
+ MatMulInfoTy &MMI) {
+ auto PartialSchedule = isl::manage(
+ isl_schedule_node_band_get_partial_schedule_union_map(Node.get()));
+ Node = Node.child(0);
+ auto LeafType = isl_schedule_node_get_type(Node.get());
+ Node = Node.parent();
+ if (LeafType != isl_schedule_node_leaf ||
+ isl_schedule_node_band_n_member(Node.get()) < 3 ||
+ Node.get_schedule_depth().release() != 0 ||
+ isl_union_map_n_map(PartialSchedule.get()) != 1)
+ return false;
+ auto NewPartialSchedule = isl::map::from_union_map(PartialSchedule);
+ if (containsMatrMult(NewPartialSchedule, D, MMI))
+ return true;
+ return false;
+}
+
+} // namespace
+
+isl::schedule_node
+polly::tryOptimizeMatMulPattern(isl::schedule_node Node,
+ const llvm::TargetTransformInfo *TTI,
+ const Dependences *D) {
+ MatMulInfoTy MMI;
+ if (isMatrMultPattern(Node, D, MMI)) {
+ LLVM_DEBUG(dbgs() << "The matrix multiplication pattern was detected\n");
+ return optimizeMatMulPattern(Node, TTI, MMI);
+ }
+ return {};
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/MaximalStaticExpansion.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/MaximalStaticExpansion.cpp
new file mode 100644
index 00000000000..f0893b72db7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/MaximalStaticExpansion.cpp
@@ -0,0 +1,487 @@
+//===- MaximalStaticExpansion.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass fully expand the memory accesses of a Scop to get rid of
+// dependencies.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/DependenceInfo.h"
+#include "polly/LinkAllPasses.h"
+#include "polly/ScopInfo.h"
+#include "polly/ScopPass.h"
+#include "polly/Support/ISLTools.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/InitializePasses.h"
+#include "isl/isl-noexceptions.h"
+#include "isl/union_map.h"
+#include <cassert>
+#include <limits>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+using namespace polly;
+
+#define DEBUG_TYPE "polly-mse"
+
+namespace {
+
+class MaximalStaticExpander : public ScopPass {
+public:
+ static char ID;
+
+ explicit MaximalStaticExpander() : ScopPass(ID) {}
+
+ ~MaximalStaticExpander() override = default;
+
+ /// Expand the accesses of the SCoP.
+ ///
+ /// @param S The SCoP that must be expanded.
+ bool runOnScop(Scop &S) override;
+
+ /// Print the SCoP.
+ ///
+ /// @param OS The stream where to print.
+ /// @param S The SCop that must be printed.
+ void printScop(raw_ostream &OS, Scop &S) const override;
+
+ /// Register all analyses and transformations required.
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+
+private:
+ /// OptimizationRemarkEmitter object for displaying diagnostic remarks.
+ OptimizationRemarkEmitter *ORE;
+
+ /// Emit remark
+ void emitRemark(StringRef Msg, Instruction *Inst);
+
+ /// Return true if the SAI in parameter is expandable.
+ ///
+ /// @param SAI the SAI that need to be checked.
+ /// @param Writes A set that will contains all the write accesses.
+ /// @param Reads A set that will contains all the read accesses.
+ /// @param S The SCop in which the SAI is in.
+ /// @param Dependences The RAW dependences of the SCop.
+ bool isExpandable(const ScopArrayInfo *SAI,
+ SmallPtrSetImpl<MemoryAccess *> &Writes,
+ SmallPtrSetImpl<MemoryAccess *> &Reads, Scop &S,
+ const isl::union_map &Dependences);
+
+ /// Expand the MemoryAccess according to its domain.
+ ///
+ /// @param S The SCop in which the memory access appears in.
+ /// @param MA The memory access that need to be expanded.
+ ScopArrayInfo *expandAccess(Scop &S, MemoryAccess *MA);
+
+ /// Filter the dependences to have only one related to current memory access.
+ ///
+ /// @param S The SCop in which the memory access appears in.
+ /// @param MapDependences The dependences to filter.
+ /// @param MA The memory access that need to be expanded.
+ isl::union_map filterDependences(Scop &S,
+ const isl::union_map &MapDependences,
+ MemoryAccess *MA);
+
+ /// Expand the MemoryAccess according to Dependences and already expanded
+ /// MemoryAccesses.
+ ///
+ /// @param The SCop in which the memory access appears in.
+ /// @param The memory access that need to be expanded.
+ /// @param Dependences The RAW dependences of the SCop.
+ /// @param ExpandedSAI The expanded SAI created during write expansion.
+ /// @param Reverse if true, the Dependences union_map is reversed before
+ /// intersection.
+ void mapAccess(Scop &S, SmallPtrSetImpl<MemoryAccess *> &Accesses,
+ const isl::union_map &Dependences, ScopArrayInfo *ExpandedSAI,
+ bool Reverse);
+
+ /// Expand PHI memory accesses.
+ ///
+ /// @param The SCop in which the memory access appears in.
+ /// @param The ScopArrayInfo representing the PHI accesses to expand.
+ /// @param Dependences The RAW dependences of the SCop.
+ void expandPhi(Scop &S, const ScopArrayInfo *SAI,
+ const isl::union_map &Dependences);
+};
+} // namespace
+
+#ifndef NDEBUG
+/// Whether a dimension of a set is bounded (lower and upper) by a constant,
+/// i.e. there are two constants Min and Max, such that every value x of the
+/// chosen dimensions is Min <= x <= Max.
+static bool isDimBoundedByConstant(isl::set Set, unsigned dim) {
+ auto ParamDims = unsignedFromIslSize(Set.dim(isl::dim::param));
+ Set = Set.project_out(isl::dim::param, 0, ParamDims);
+ Set = Set.project_out(isl::dim::set, 0, dim);
+ auto SetDims = unsignedFromIslSize(Set.tuple_dim());
+ assert(SetDims >= 1);
+ Set = Set.project_out(isl::dim::set, 1, SetDims - 1);
+ return bool(Set.is_bounded());
+}
+#endif
+
+char MaximalStaticExpander::ID = 0;
+
+isl::union_map MaximalStaticExpander::filterDependences(
+ Scop &S, const isl::union_map &Dependences, MemoryAccess *MA) {
+ auto SAI = MA->getLatestScopArrayInfo();
+
+ auto AccessDomainSet = MA->getAccessRelation().domain();
+ auto AccessDomainId = AccessDomainSet.get_tuple_id();
+
+ isl::union_map MapDependences = isl::union_map::empty(S.getIslCtx());
+
+ for (isl::map Map : Dependences.get_map_list()) {
+ // Filter out Statement to Statement dependences.
+ if (!Map.can_curry())
+ continue;
+
+ // Intersect with the relevant SAI.
+ auto TmpMapDomainId =
+ Map.get_space().domain().unwrap().range().get_tuple_id(isl::dim::set);
+
+ ScopArrayInfo *UserSAI =
+ static_cast<ScopArrayInfo *>(TmpMapDomainId.get_user());
+
+ if (SAI != UserSAI)
+ continue;
+
+ // Get the correct S1[] -> S2[] dependence.
+ auto NewMap = Map.factor_domain();
+ auto NewMapDomainId = NewMap.domain().get_tuple_id();
+
+ if (AccessDomainId.get() != NewMapDomainId.get())
+ continue;
+
+ // Add the corresponding map to MapDependences.
+ MapDependences = MapDependences.unite(NewMap);
+ }
+
+ return MapDependences;
+}
+
+bool MaximalStaticExpander::isExpandable(
+ const ScopArrayInfo *SAI, SmallPtrSetImpl<MemoryAccess *> &Writes,
+ SmallPtrSetImpl<MemoryAccess *> &Reads, Scop &S,
+ const isl::union_map &Dependences) {
+ if (SAI->isValueKind()) {
+ Writes.insert(S.getValueDef(SAI));
+ for (auto MA : S.getValueUses(SAI))
+ Reads.insert(MA);
+ return true;
+ } else if (SAI->isPHIKind()) {
+ auto Read = S.getPHIRead(SAI);
+
+ auto StmtDomain = isl::union_set(Read->getStatement()->getDomain());
+
+ auto Writes = S.getPHIIncomings(SAI);
+
+ // Get the domain where all the writes are writing to.
+ auto WriteDomain = isl::union_set::empty(S.getIslCtx());
+
+ for (auto Write : Writes) {
+ auto MapDeps = filterDependences(S, Dependences, Write);
+ for (isl::map Map : MapDeps.get_map_list())
+ WriteDomain = WriteDomain.unite(Map.range());
+ }
+
+ // For now, read from original scalar is not possible.
+ if (!StmtDomain.is_equal(WriteDomain)) {
+ emitRemark(SAI->getName() + " read from its original value.",
+ Read->getAccessInstruction());
+ return false;
+ }
+
+ return true;
+ } else if (SAI->isExitPHIKind()) {
+ // For now, we are not able to expand ExitPhi.
+ emitRemark(SAI->getName() + " is a ExitPhi node.",
+ S.getEnteringBlock()->getFirstNonPHI());
+ return false;
+ }
+
+ int NumberWrites = 0;
+ for (ScopStmt &Stmt : S) {
+ auto StmtReads = isl::union_map::empty(S.getIslCtx());
+ auto StmtWrites = isl::union_map::empty(S.getIslCtx());
+
+ for (MemoryAccess *MA : Stmt) {
+ // Check if the current MemoryAccess involved the current SAI.
+ if (SAI != MA->getLatestScopArrayInfo())
+ continue;
+
+ // For now, we are not able to expand array where read come after write
+ // (to the same location) in a same statement.
+ auto AccRel = isl::union_map(MA->getAccessRelation());
+ if (MA->isRead()) {
+ // Reject load after store to same location.
+ if (!StmtWrites.is_disjoint(AccRel)) {
+ emitRemark(SAI->getName() + " has read after write to the same "
+ "element in same statement. The "
+ "dependences found during analysis may "
+ "be wrong because Polly is not able to "
+ "handle such case for now.",
+ MA->getAccessInstruction());
+ return false;
+ }
+
+ StmtReads = StmtReads.unite(AccRel);
+ } else {
+ StmtWrites = StmtWrites.unite(AccRel);
+ }
+
+ // For now, we are not able to expand MayWrite.
+ if (MA->isMayWrite()) {
+ emitRemark(SAI->getName() + " has a maywrite access.",
+ MA->getAccessInstruction());
+ return false;
+ }
+
+ // For now, we are not able to expand SAI with more than one write.
+ if (MA->isMustWrite()) {
+ Writes.insert(MA);
+ NumberWrites++;
+ if (NumberWrites > 1) {
+ emitRemark(SAI->getName() + " has more than 1 write access.",
+ MA->getAccessInstruction());
+ return false;
+ }
+ }
+
+ // Check if it is possible to expand this read.
+ if (MA->isRead()) {
+ // Get the domain of the current ScopStmt.
+ auto StmtDomain = Stmt.getDomain();
+
+ // Get the domain of the future Read access.
+ auto ReadDomainSet = MA->getAccessRelation().domain();
+ auto ReadDomain = isl::union_set(ReadDomainSet);
+
+ // Get the dependences relevant for this MA
+ auto MapDependences = filterDependences(S, Dependences.reverse(), MA);
+ unsigned NumberElementMap = isl_union_map_n_map(MapDependences.get());
+
+ if (NumberElementMap == 0) {
+ emitRemark("The expansion of " + SAI->getName() +
+ " would lead to a read from the original array.",
+ MA->getAccessInstruction());
+ return false;
+ }
+
+ auto DepsDomain = MapDependences.domain();
+
+ // If there are multiple maps in the Deps, we cannot handle this case
+ // for now.
+ if (NumberElementMap != 1) {
+ emitRemark(SAI->getName() +
+ " has too many dependences to be handle for now.",
+ MA->getAccessInstruction());
+ return false;
+ }
+
+ auto DepsDomainSet = isl::set(DepsDomain);
+
+ // For now, read from the original array is not possible.
+ if (!StmtDomain.is_subset(DepsDomainSet)) {
+ emitRemark("The expansion of " + SAI->getName() +
+ " would lead to a read from the original array.",
+ MA->getAccessInstruction());
+ return false;
+ }
+
+ Reads.insert(MA);
+ }
+ }
+ }
+
+ // No need to expand SAI with no write.
+ if (NumberWrites == 0) {
+ emitRemark(SAI->getName() + " has 0 write access.",
+ S.getEnteringBlock()->getFirstNonPHI());
+ return false;
+ }
+
+ return true;
+}
+
+void MaximalStaticExpander::mapAccess(Scop &S,
+ SmallPtrSetImpl<MemoryAccess *> &Accesses,
+ const isl::union_map &Dependences,
+ ScopArrayInfo *ExpandedSAI,
+ bool Reverse) {
+ for (auto MA : Accesses) {
+ // Get the current AM.
+ auto CurrentAccessMap = MA->getAccessRelation();
+
+ // Get RAW dependences for the current WA.
+ auto DomainSet = MA->getAccessRelation().domain();
+ auto Domain = isl::union_set(DomainSet);
+
+ // Get the dependences relevant for this MA.
+ isl::union_map MapDependences =
+ filterDependences(S, Reverse ? Dependences.reverse() : Dependences, MA);
+
+ // If no dependences, no need to modify anything.
+ if (MapDependences.is_empty())
+ return;
+
+ assert(isl_union_map_n_map(MapDependences.get()) == 1 &&
+ "There are more than one RAW dependencies in the union map.");
+ auto NewAccessMap = isl::map::from_union_map(MapDependences);
+
+ auto Id = ExpandedSAI->getBasePtrId();
+
+ // Replace the out tuple id with the one of the access array.
+ NewAccessMap = NewAccessMap.set_tuple_id(isl::dim::out, Id);
+
+ // Set the new access relation.
+ MA->setNewAccessRelation(NewAccessMap);
+ }
+}
+
+ScopArrayInfo *MaximalStaticExpander::expandAccess(Scop &S, MemoryAccess *MA) {
+ // Get the current AM.
+ auto CurrentAccessMap = MA->getAccessRelation();
+
+ unsigned in_dimensions =
+ unsignedFromIslSize(CurrentAccessMap.domain_tuple_dim());
+
+ // Get domain from the current AM.
+ auto Domain = CurrentAccessMap.domain();
+
+ // Create a new AM from the domain.
+ auto NewAccessMap = isl::map::from_domain(Domain);
+
+ // Add dimensions to the new AM according to the current in_dim.
+ NewAccessMap = NewAccessMap.add_dims(isl::dim::out, in_dimensions);
+
+ // Create the string representing the name of the new SAI.
+ // One new SAI for each statement so that each write go to a different memory
+ // cell.
+ auto CurrentStmtDomain = MA->getStatement()->getDomain();
+ auto CurrentStmtName = CurrentStmtDomain.get_tuple_name();
+ auto CurrentOutId = CurrentAccessMap.get_tuple_id(isl::dim::out);
+ std::string CurrentOutIdString =
+ MA->getScopArrayInfo()->getName() + "_" + CurrentStmtName + "_expanded";
+
+ // Set the tuple id for the out dimension.
+ NewAccessMap = NewAccessMap.set_tuple_id(isl::dim::out, CurrentOutId);
+
+ // Create the size vector.
+ std::vector<unsigned> Sizes;
+ for (unsigned i = 0; i < in_dimensions; i++) {
+ assert(isDimBoundedByConstant(CurrentStmtDomain, i) &&
+ "Domain boundary are not constant.");
+ auto UpperBound = getConstant(CurrentStmtDomain.dim_max(i), true, false);
+ assert(!UpperBound.is_null() && UpperBound.is_pos() &&
+ !UpperBound.is_nan() &&
+ "The upper bound is not a positive integer.");
+ assert(UpperBound.le(isl::val(CurrentAccessMap.ctx(),
+ std::numeric_limits<int>::max() - 1)) &&
+ "The upper bound overflow a int.");
+ Sizes.push_back(UpperBound.get_num_si() + 1);
+ }
+
+ // Get the ElementType of the current SAI.
+ auto ElementType = MA->getLatestScopArrayInfo()->getElementType();
+
+ // Create (or get if already existing) the new expanded SAI.
+ auto ExpandedSAI =
+ S.createScopArrayInfo(ElementType, CurrentOutIdString, Sizes);
+ ExpandedSAI->setIsOnHeap(true);
+
+ // Get the out Id of the expanded Array.
+ auto NewOutId = ExpandedSAI->getBasePtrId();
+
+ // Set the out id of the new AM to the new SAI id.
+ NewAccessMap = NewAccessMap.set_tuple_id(isl::dim::out, NewOutId);
+
+ // Add constraints to linked output with input id.
+ auto SpaceMap = NewAccessMap.get_space();
+ auto ConstraintBasicMap = isl::basic_map::equal(
+ SpaceMap, unsignedFromIslSize(SpaceMap.dim(isl::dim::in)));
+ NewAccessMap = isl::map(ConstraintBasicMap);
+
+ // Set the new access relation map.
+ MA->setNewAccessRelation(NewAccessMap);
+
+ return ExpandedSAI;
+}
+
+void MaximalStaticExpander::expandPhi(Scop &S, const ScopArrayInfo *SAI,
+ const isl::union_map &Dependences) {
+ SmallPtrSet<MemoryAccess *, 4> Writes;
+ for (auto MA : S.getPHIIncomings(SAI))
+ Writes.insert(MA);
+ auto Read = S.getPHIRead(SAI);
+ auto ExpandedSAI = expandAccess(S, Read);
+
+ mapAccess(S, Writes, Dependences, ExpandedSAI, false);
+}
+
+void MaximalStaticExpander::emitRemark(StringRef Msg, Instruction *Inst) {
+ ORE->emit(OptimizationRemarkAnalysis(DEBUG_TYPE, "ExpansionRejection", Inst)
+ << Msg);
+}
+
+bool MaximalStaticExpander::runOnScop(Scop &S) {
+ // Get the ORE from OptimizationRemarkEmitterWrapperPass.
+ ORE = &(getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE());
+
+ // Get the RAW Dependences.
+ auto &DI = getAnalysis<DependenceInfo>();
+ auto &D = DI.getDependences(Dependences::AL_Reference);
+ isl::union_map Dependences = D.getDependences(Dependences::TYPE_RAW);
+
+ SmallVector<ScopArrayInfo *, 4> CurrentSAI(S.arrays().begin(),
+ S.arrays().end());
+
+ for (auto SAI : CurrentSAI) {
+ SmallPtrSet<MemoryAccess *, 4> AllWrites;
+ SmallPtrSet<MemoryAccess *, 4> AllReads;
+ if (!isExpandable(SAI, AllWrites, AllReads, S, Dependences))
+ continue;
+
+ if (SAI->isValueKind() || SAI->isArrayKind()) {
+ assert(AllWrites.size() == 1 || SAI->isValueKind());
+
+ auto TheWrite = *(AllWrites.begin());
+ ScopArrayInfo *ExpandedArray = expandAccess(S, TheWrite);
+
+ mapAccess(S, AllReads, Dependences, ExpandedArray, true);
+ } else if (SAI->isPHIKind()) {
+ expandPhi(S, SAI, Dependences);
+ }
+ }
+
+ return false;
+}
+
+void MaximalStaticExpander::printScop(raw_ostream &OS, Scop &S) const {
+ S.print(OS, false);
+}
+
+void MaximalStaticExpander::getAnalysisUsage(AnalysisUsage &AU) const {
+ ScopPass::getAnalysisUsage(AU);
+ AU.addRequired<DependenceInfo>();
+ AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
+}
+
+Pass *polly::createMaximalStaticExpansionPass() {
+ return new MaximalStaticExpander();
+}
+
+INITIALIZE_PASS_BEGIN(MaximalStaticExpander, "polly-mse",
+ "Polly - Maximal static expansion of SCoP", false, false);
+INITIALIZE_PASS_DEPENDENCY(DependenceInfo);
+INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass);
+INITIALIZE_PASS_END(MaximalStaticExpander, "polly-mse",
+ "Polly - Maximal static expansion of SCoP", false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/ScheduleOptimizer.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/ScheduleOptimizer.cpp
new file mode 100644
index 00000000000..0a646113954
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/ScheduleOptimizer.cpp
@@ -0,0 +1,1010 @@
+//===- ScheduleOptimizer.cpp - Calculate an optimized schedule ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass generates an entirely new schedule tree from the data dependences
+// and iteration domains. The new schedule tree is computed in two steps:
+//
+// 1) The isl scheduling optimizer is run
+//
+// The isl scheduling optimizer creates a new schedule tree that maximizes
+// parallelism and tileability and minimizes data-dependence distances. The
+// algorithm used is a modified version of the ``Pluto'' algorithm:
+//
+// U. Bondhugula, A. Hartono, J. Ramanujam, and P. Sadayappan.
+// A Practical Automatic Polyhedral Parallelizer and Locality Optimizer.
+// In Proceedings of the 2008 ACM SIGPLAN Conference On Programming Language
+// Design and Implementation, PLDI ’08, pages 101–113. ACM, 2008.
+//
+// 2) A set of post-scheduling transformations is applied on the schedule tree.
+//
+// These optimizations include:
+//
+// - Tiling of the innermost tilable bands
+// - Prevectorization - The choice of a possible outer loop that is strip-mined
+// to the innermost level to enable inner-loop
+// vectorization.
+// - Some optimizations for spatial locality are also planned.
+//
+// For a detailed description of the schedule tree itself please see section 6
+// of:
+//
+// Polyhedral AST generation is more than scanning polyhedra
+// Tobias Grosser, Sven Verdoolaege, Albert Cohen
+// ACM Transactions on Programming Languages and Systems (TOPLAS),
+// 37(4), July 2015
+// http://www.grosser.es/#pub-polyhedral-AST-generation
+//
+// This publication also contains a detailed discussion of the different options
+// for polyhedral loop unrolling, full/partial tile separation and other uses
+// of the schedule tree.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/ScheduleOptimizer.h"
+#include "polly/CodeGen/CodeGeneration.h"
+#include "polly/DependenceInfo.h"
+#include "polly/ManualOptimizer.h"
+#include "polly/MatmulOptimizer.h"
+#include "polly/Options.h"
+#include "polly/ScheduleTreeTransform.h"
+#include "polly/Support/ISLOStream.h"
+#include "polly/Support/ISLTools.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/CommandLine.h"
+#include "isl/options.h"
+
+using namespace llvm;
+using namespace polly;
+
+namespace llvm {
+class Loop;
+class Module;
+} // namespace llvm
+
+#define DEBUG_TYPE "polly-opt-isl"
+
+static cl::opt<std::string>
+ OptimizeDeps("polly-opt-optimize-only",
+ cl::desc("Only a certain kind of dependences (all/raw)"),
+ cl::Hidden, cl::init("all"), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<std::string>
+ SimplifyDeps("polly-opt-simplify-deps",
+ cl::desc("Dependences should be simplified (yes/no)"),
+ cl::Hidden, cl::init("yes"), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<int> MaxConstantTerm(
+ "polly-opt-max-constant-term",
+ cl::desc("The maximal constant term allowed (-1 is unlimited)"), cl::Hidden,
+ cl::init(20), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> MaxCoefficient(
+ "polly-opt-max-coefficient",
+ cl::desc("The maximal coefficient allowed (-1 is unlimited)"), cl::Hidden,
+ cl::init(20), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<std::string>
+ MaximizeBandDepth("polly-opt-maximize-bands",
+ cl::desc("Maximize the band depth (yes/no)"), cl::Hidden,
+ cl::init("yes"), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ GreedyFusion("polly-loopfusion-greedy",
+ cl::desc("Aggressively try to fuse everything"), cl::Hidden,
+ cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<std::string> OuterCoincidence(
+ "polly-opt-outer-coincidence",
+ cl::desc("Try to construct schedules where the outer member of each band "
+ "satisfies the coincidence constraints (yes/no)"),
+ cl::Hidden, cl::init("no"), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> PrevectorWidth(
+ "polly-prevect-width",
+ cl::desc(
+ "The number of loop iterations to strip-mine for pre-vectorization"),
+ cl::Hidden, cl::init(4), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> FirstLevelTiling("polly-tiling",
+ cl::desc("Enable loop tiling"),
+ cl::init(true), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<int> FirstLevelDefaultTileSize(
+ "polly-default-tile-size",
+ cl::desc("The default tile size (if not enough were provided by"
+ " --polly-tile-sizes)"),
+ cl::Hidden, cl::init(32), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::list<int>
+ FirstLevelTileSizes("polly-tile-sizes",
+ cl::desc("A tile size for each loop dimension, filled "
+ "with --polly-default-tile-size"),
+ cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ SecondLevelTiling("polly-2nd-level-tiling",
+ cl::desc("Enable a 2nd level loop of loop tiling"),
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<int> SecondLevelDefaultTileSize(
+ "polly-2nd-level-default-tile-size",
+ cl::desc("The default 2nd-level tile size (if not enough were provided by"
+ " --polly-2nd-level-tile-sizes)"),
+ cl::Hidden, cl::init(16), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::list<int>
+ SecondLevelTileSizes("polly-2nd-level-tile-sizes",
+ cl::desc("A tile size for each loop dimension, filled "
+ "with --polly-default-tile-size"),
+ cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool> RegisterTiling("polly-register-tiling",
+ cl::desc("Enable register tiling"),
+ cl::init(false), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<int> RegisterDefaultTileSize(
+ "polly-register-tiling-default-tile-size",
+ cl::desc("The default register tile size (if not enough were provided by"
+ " --polly-register-tile-sizes)"),
+ cl::Hidden, cl::init(2), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::list<int>
+ RegisterTileSizes("polly-register-tile-sizes",
+ cl::desc("A tile size for each loop dimension, filled "
+ "with --polly-register-tile-size"),
+ cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool> PragmaBasedOpts(
+ "polly-pragma-based-opts",
+ cl::desc("Apply user-directed transformation from metadata"),
+ cl::init(true), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> EnableReschedule("polly-reschedule",
+ cl::desc("Optimize SCoPs using ISL"),
+ cl::init(true), cl::ZeroOrMore,
+ cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ PMBasedOpts("polly-pattern-matching-based-opts",
+ cl::desc("Perform optimizations based on pattern matching"),
+ cl::init(true), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool>
+ EnablePostopts("polly-postopts",
+ cl::desc("Apply post-rescheduling optimizations such as "
+ "tiling (requires -polly-reschedule)"),
+ cl::init(true), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+static cl::opt<bool> OptimizedScops(
+ "polly-optimized-scops",
+ cl::desc("Polly - Dump polyhedral description of Scops optimized with "
+ "the isl scheduling optimizer and the set of post-scheduling "
+ "transformations is applied on the schedule tree"),
+ cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
+
+STATISTIC(ScopsProcessed, "Number of scops processed");
+STATISTIC(ScopsRescheduled, "Number of scops rescheduled");
+STATISTIC(ScopsOptimized, "Number of scops optimized");
+
+STATISTIC(NumAffineLoopsOptimized, "Number of affine loops optimized");
+STATISTIC(NumBoxedLoopsOptimized, "Number of boxed loops optimized");
+
+#define THREE_STATISTICS(VARNAME, DESC) \
+ static Statistic VARNAME[3] = { \
+ {DEBUG_TYPE, #VARNAME "0", DESC " (original)"}, \
+ {DEBUG_TYPE, #VARNAME "1", DESC " (after scheduler)"}, \
+ {DEBUG_TYPE, #VARNAME "2", DESC " (after optimizer)"}}
+
+THREE_STATISTICS(NumBands, "Number of bands");
+THREE_STATISTICS(NumBandMembers, "Number of band members");
+THREE_STATISTICS(NumCoincident, "Number of coincident band members");
+THREE_STATISTICS(NumPermutable, "Number of permutable bands");
+THREE_STATISTICS(NumFilters, "Number of filter nodes");
+THREE_STATISTICS(NumExtension, "Number of extension nodes");
+
+STATISTIC(FirstLevelTileOpts, "Number of first level tiling applied");
+STATISTIC(SecondLevelTileOpts, "Number of second level tiling applied");
+STATISTIC(RegisterTileOpts, "Number of register tiling applied");
+STATISTIC(PrevectOpts, "Number of strip-mining for prevectorization applied");
+STATISTIC(MatMulOpts,
+ "Number of matrix multiplication patterns detected and optimized");
+
+namespace {
+/// Additional parameters of the schedule optimizer.
+///
+/// Target Transform Info and the SCoP dependencies used by the schedule
+/// optimizer.
+struct OptimizerAdditionalInfoTy {
+ const llvm::TargetTransformInfo *TTI;
+ const Dependences *D;
+ bool PatternOpts;
+ bool Postopts;
+ bool Prevect;
+};
+
+class ScheduleTreeOptimizer {
+public:
+ /// Apply schedule tree transformations.
+ ///
+ /// This function takes an (possibly already optimized) schedule tree and
+ /// applies a set of additional optimizations on the schedule tree. The
+ /// transformations applied include:
+ ///
+ /// - Pattern-based optimizations
+ /// - Tiling
+ /// - Prevectorization
+ ///
+ /// @param Schedule The schedule object the transformations will be applied
+ /// to.
+ /// @param OAI Target Transform Info and the SCoP dependencies.
+ /// @returns The transformed schedule.
+ static isl::schedule
+ optimizeSchedule(isl::schedule Schedule,
+ const OptimizerAdditionalInfoTy *OAI = nullptr);
+
+ /// Apply schedule tree transformations.
+ ///
+ /// This function takes a node in an (possibly already optimized) schedule
+ /// tree and applies a set of additional optimizations on this schedule tree
+ /// node and its descendants. The transformations applied include:
+ ///
+ /// - Pattern-based optimizations
+ /// - Tiling
+ /// - Prevectorization
+ ///
+ /// @param Node The schedule object post-transformations will be applied to.
+ /// @param OAI Target Transform Info and the SCoP dependencies.
+ /// @returns The transformed schedule.
+ static isl::schedule_node
+ optimizeScheduleNode(isl::schedule_node Node,
+ const OptimizerAdditionalInfoTy *OAI = nullptr);
+
+ /// Decide if the @p NewSchedule is profitable for @p S.
+ ///
+ /// @param S The SCoP we optimize.
+ /// @param NewSchedule The new schedule we computed.
+ ///
+ /// @return True, if we believe @p NewSchedule is an improvement for @p S.
+ static bool isProfitableSchedule(polly::Scop &S, isl::schedule NewSchedule);
+
+ /// Isolate a set of partial tile prefixes.
+ ///
+ /// This set should ensure that it contains only partial tile prefixes that
+ /// have exactly VectorWidth iterations.
+ ///
+ /// @param Node A schedule node band, which is a parent of a band node,
+ /// that contains a vector loop.
+ /// @return Modified isl_schedule_node.
+ static isl::schedule_node isolateFullPartialTiles(isl::schedule_node Node,
+ int VectorWidth);
+
+private:
+ /// Check if this node is a band node we want to tile.
+ ///
+ /// We look for innermost band nodes where individual dimensions are marked as
+ /// permutable.
+ ///
+ /// @param Node The node to check.
+ static bool isTileableBandNode(isl::schedule_node Node);
+
+ /// Pre-vectorizes one scheduling dimension of a schedule band.
+ ///
+ /// prevectSchedBand splits out the dimension DimToVectorize, tiles it and
+ /// sinks the resulting point loop.
+ ///
+ /// Example (DimToVectorize=0, VectorWidth=4):
+ ///
+ /// | Before transformation:
+ /// |
+ /// | A[i,j] -> [i,j]
+ /// |
+ /// | for (i = 0; i < 128; i++)
+ /// | for (j = 0; j < 128; j++)
+ /// | A(i,j);
+ ///
+ /// | After transformation:
+ /// |
+ /// | for (it = 0; it < 32; it+=1)
+ /// | for (j = 0; j < 128; j++)
+ /// | for (ip = 0; ip <= 3; ip++)
+ /// | A(4 * it + ip,j);
+ ///
+ /// The goal of this transformation is to create a trivially vectorizable
+ /// loop. This means a parallel loop at the innermost level that has a
+ /// constant number of iterations corresponding to the target vector width.
+ ///
+ /// This transformation creates a loop at the innermost level. The loop has
+ /// a constant number of iterations, if the number of loop iterations at
+ /// DimToVectorize can be divided by VectorWidth. The default VectorWidth is
+ /// currently constant and not yet target specific. This function does not
+ /// reason about parallelism.
+ static isl::schedule_node prevectSchedBand(isl::schedule_node Node,
+ unsigned DimToVectorize,
+ int VectorWidth);
+
+ /// Apply additional optimizations on the bands in the schedule tree.
+ ///
+ /// We are looking for an innermost band node and apply the following
+ /// transformations:
+ ///
+ /// - Tile the band
+ /// - if the band is tileable
+ /// - if the band has more than one loop dimension
+ ///
+ /// - Prevectorize the schedule of the band (or the point loop in case of
+ /// tiling).
+ /// - if vectorization is enabled
+ ///
+ /// @param Node The schedule node to (possibly) optimize.
+ /// @param User A pointer to forward some use information
+ /// (currently unused).
+ static isl_schedule_node *optimizeBand(isl_schedule_node *Node, void *User);
+
+ /// Apply tiling optimizations on the bands in the schedule tree.
+ ///
+ /// @param Node The schedule node to (possibly) optimize.
+ static isl::schedule_node applyTileBandOpt(isl::schedule_node Node);
+
+ /// Apply prevectorization on the bands in the schedule tree.
+ ///
+ /// @param Node The schedule node to (possibly) prevectorize.
+ static isl::schedule_node applyPrevectBandOpt(isl::schedule_node Node);
+};
+
+isl::schedule_node
+ScheduleTreeOptimizer::isolateFullPartialTiles(isl::schedule_node Node,
+ int VectorWidth) {
+ assert(isl_schedule_node_get_type(Node.get()) == isl_schedule_node_band);
+ Node = Node.child(0).child(0);
+ isl::union_map SchedRelUMap = Node.get_prefix_schedule_relation();
+ isl::union_set ScheduleRangeUSet = SchedRelUMap.range();
+ isl::set ScheduleRange{ScheduleRangeUSet};
+ isl::set IsolateDomain = getPartialTilePrefixes(ScheduleRange, VectorWidth);
+ auto AtomicOption = getDimOptions(IsolateDomain.ctx(), "atomic");
+ isl::union_set IsolateOption = getIsolateOptions(IsolateDomain, 1);
+ Node = Node.parent().parent();
+ isl::union_set Options = IsolateOption.unite(AtomicOption);
+ isl::schedule_node_band Result =
+ Node.as<isl::schedule_node_band>().set_ast_build_options(Options);
+ return Result;
+}
+
+struct InsertSimdMarkers : public ScheduleNodeRewriter<InsertSimdMarkers> {
+ isl::schedule_node visitBand(isl::schedule_node_band Band) {
+ isl::schedule_node Node = visitChildren(Band);
+
+ // Only add SIMD markers to innermost bands.
+ if (!Node.first_child().isa<isl::schedule_node_leaf>())
+ return Node;
+
+ isl::id LoopMarker = isl::id::alloc(Band.ctx(), "SIMD", nullptr);
+ return Band.insert_mark(LoopMarker);
+ }
+};
+
+isl::schedule_node ScheduleTreeOptimizer::prevectSchedBand(
+ isl::schedule_node Node, unsigned DimToVectorize, int VectorWidth) {
+ assert(isl_schedule_node_get_type(Node.get()) == isl_schedule_node_band);
+
+ auto Space = isl::manage(isl_schedule_node_band_get_space(Node.get()));
+ unsigned ScheduleDimensions = unsignedFromIslSize(Space.dim(isl::dim::set));
+ assert(DimToVectorize < ScheduleDimensions);
+
+ if (DimToVectorize > 0) {
+ Node = isl::manage(
+ isl_schedule_node_band_split(Node.release(), DimToVectorize));
+ Node = Node.child(0);
+ }
+ if (DimToVectorize < ScheduleDimensions - 1)
+ Node = isl::manage(isl_schedule_node_band_split(Node.release(), 1));
+ Space = isl::manage(isl_schedule_node_band_get_space(Node.get()));
+ auto Sizes = isl::multi_val::zero(Space);
+ Sizes = Sizes.set_val(0, isl::val(Node.ctx(), VectorWidth));
+ Node =
+ isl::manage(isl_schedule_node_band_tile(Node.release(), Sizes.release()));
+ Node = isolateFullPartialTiles(Node, VectorWidth);
+ Node = Node.child(0);
+ // Make sure the "trivially vectorizable loop" is not unrolled. Otherwise,
+ // we will have troubles to match it in the backend.
+ Node = Node.as<isl::schedule_node_band>().set_ast_build_options(
+ isl::union_set(Node.ctx(), "{ unroll[x]: 1 = 0 }"));
+
+ // Sink the inner loop into the smallest possible statements to make them
+ // represent a single vector instruction if possible.
+ Node = isl::manage(isl_schedule_node_band_sink(Node.release()));
+
+ // Add SIMD markers to those vector statements.
+ InsertSimdMarkers SimdMarkerInserter;
+ Node = SimdMarkerInserter.visit(Node);
+
+ PrevectOpts++;
+ return Node.parent();
+}
+
+static bool isSimpleInnermostBand(const isl::schedule_node &Node) {
+ assert(isl_schedule_node_get_type(Node.get()) == isl_schedule_node_band);
+ assert(isl_schedule_node_n_children(Node.get()) == 1);
+
+ auto ChildType = isl_schedule_node_get_type(Node.child(0).get());
+
+ if (ChildType == isl_schedule_node_leaf)
+ return true;
+
+ if (ChildType != isl_schedule_node_sequence)
+ return false;
+
+ auto Sequence = Node.child(0);
+
+ for (int c = 0, nc = isl_schedule_node_n_children(Sequence.get()); c < nc;
+ ++c) {
+ auto Child = Sequence.child(c);
+ if (isl_schedule_node_get_type(Child.get()) != isl_schedule_node_filter)
+ return false;
+ if (isl_schedule_node_get_type(Child.child(0).get()) !=
+ isl_schedule_node_leaf)
+ return false;
+ }
+ return true;
+}
+
+bool ScheduleTreeOptimizer::isTileableBandNode(isl::schedule_node Node) {
+ if (isl_schedule_node_get_type(Node.get()) != isl_schedule_node_band)
+ return false;
+
+ if (isl_schedule_node_n_children(Node.get()) != 1)
+ return false;
+
+ if (!isl_schedule_node_band_get_permutable(Node.get()))
+ return false;
+
+ auto Space = isl::manage(isl_schedule_node_band_get_space(Node.get()));
+
+ if (unsignedFromIslSize(Space.dim(isl::dim::set)) <= 1u)
+ return false;
+
+ return isSimpleInnermostBand(Node);
+}
+
+__isl_give isl::schedule_node
+ScheduleTreeOptimizer::applyTileBandOpt(isl::schedule_node Node) {
+ if (FirstLevelTiling) {
+ Node = tileNode(Node, "1st level tiling", FirstLevelTileSizes,
+ FirstLevelDefaultTileSize);
+ FirstLevelTileOpts++;
+ }
+
+ if (SecondLevelTiling) {
+ Node = tileNode(Node, "2nd level tiling", SecondLevelTileSizes,
+ SecondLevelDefaultTileSize);
+ SecondLevelTileOpts++;
+ }
+
+ if (RegisterTiling) {
+ Node =
+ applyRegisterTiling(Node, RegisterTileSizes, RegisterDefaultTileSize);
+ RegisterTileOpts++;
+ }
+
+ return Node;
+}
+
+isl::schedule_node
+ScheduleTreeOptimizer::applyPrevectBandOpt(isl::schedule_node Node) {
+ auto Space = isl::manage(isl_schedule_node_band_get_space(Node.get()));
+ int Dims = unsignedFromIslSize(Space.dim(isl::dim::set));
+
+ for (int i = Dims - 1; i >= 0; i--)
+ if (Node.as<isl::schedule_node_band>().member_get_coincident(i)) {
+ Node = prevectSchedBand(Node, i, PrevectorWidth);
+ break;
+ }
+
+ return Node;
+}
+
+__isl_give isl_schedule_node *
+ScheduleTreeOptimizer::optimizeBand(__isl_take isl_schedule_node *NodeArg,
+ void *User) {
+ const OptimizerAdditionalInfoTy *OAI =
+ static_cast<const OptimizerAdditionalInfoTy *>(User);
+ assert(OAI && "Expecting optimization options");
+
+ isl::schedule_node Node = isl::manage(NodeArg);
+ if (!isTileableBandNode(Node))
+ return Node.release();
+
+ if (OAI->PatternOpts) {
+ isl::schedule_node PatternOptimizedSchedule =
+ tryOptimizeMatMulPattern(Node, OAI->TTI, OAI->D);
+ if (!PatternOptimizedSchedule.is_null()) {
+ MatMulOpts++;
+ return PatternOptimizedSchedule.release();
+ }
+ }
+
+ if (OAI->Postopts)
+ Node = applyTileBandOpt(Node);
+
+ if (OAI->Prevect) {
+ // FIXME: Prevectorization requirements are different from those checked by
+ // isTileableBandNode.
+ Node = applyPrevectBandOpt(Node);
+ }
+
+ return Node.release();
+}
+
+isl::schedule
+ScheduleTreeOptimizer::optimizeSchedule(isl::schedule Schedule,
+ const OptimizerAdditionalInfoTy *OAI) {
+ auto Root = Schedule.get_root();
+ Root = optimizeScheduleNode(Root, OAI);
+ return Root.get_schedule();
+}
+
+isl::schedule_node ScheduleTreeOptimizer::optimizeScheduleNode(
+ isl::schedule_node Node, const OptimizerAdditionalInfoTy *OAI) {
+ Node = isl::manage(isl_schedule_node_map_descendant_bottom_up(
+ Node.release(), optimizeBand,
+ const_cast<void *>(static_cast<const void *>(OAI))));
+ return Node;
+}
+
+bool ScheduleTreeOptimizer::isProfitableSchedule(Scop &S,
+ isl::schedule NewSchedule) {
+ // To understand if the schedule has been optimized we check if the schedule
+ // has changed at all.
+ // TODO: We can improve this by tracking if any necessarily beneficial
+ // transformations have been performed. This can e.g. be tiling, loop
+ // interchange, or ...) We can track this either at the place where the
+ // transformation has been performed or, in case of automatic ILP based
+ // optimizations, by comparing (yet to be defined) performance metrics
+ // before/after the scheduling optimizer
+ // (e.g., #stride-one accesses)
+ // FIXME: A schedule tree whose union_map-conversion is identical to the
+ // original schedule map may still allow for parallelization, i.e. can still
+ // be profitable.
+ auto NewScheduleMap = NewSchedule.get_map();
+ auto OldSchedule = S.getSchedule();
+ assert(!OldSchedule.is_null() &&
+ "Only IslScheduleOptimizer can insert extension nodes "
+ "that make Scop::getSchedule() return nullptr.");
+ bool changed = !OldSchedule.is_equal(NewScheduleMap);
+ return changed;
+}
+
+class IslScheduleOptimizerWrapperPass : public ScopPass {
+public:
+ static char ID;
+
+ explicit IslScheduleOptimizerWrapperPass() : ScopPass(ID) {}
+
+ /// Optimize the schedule of the SCoP @p S.
+ bool runOnScop(Scop &S) override;
+
+ /// Print the new schedule for the SCoP @p S.
+ void printScop(raw_ostream &OS, Scop &S) const override;
+
+ /// Register all analyses and transformation required.
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+
+ /// Release the internal memory.
+ void releaseMemory() override {
+ LastSchedule = {};
+ IslCtx.reset();
+ }
+
+private:
+ std::shared_ptr<isl_ctx> IslCtx;
+ isl::schedule LastSchedule;
+};
+
+char IslScheduleOptimizerWrapperPass::ID = 0;
+
+#ifndef NDEBUG
+static void printSchedule(llvm::raw_ostream &OS, const isl::schedule &Schedule,
+ StringRef Desc) {
+ isl::ctx Ctx = Schedule.ctx();
+ isl_printer *P = isl_printer_to_str(Ctx.get());
+ P = isl_printer_set_yaml_style(P, ISL_YAML_STYLE_BLOCK);
+ P = isl_printer_print_schedule(P, Schedule.get());
+ char *Str = isl_printer_get_str(P);
+ OS << Desc << ": \n" << Str << "\n";
+ free(Str);
+ isl_printer_free(P);
+}
+#endif
+
+/// Collect statistics for the schedule tree.
+///
+/// @param Schedule The schedule tree to analyze. If not a schedule tree it is
+/// ignored.
+/// @param Version The version of the schedule tree that is analyzed.
+/// 0 for the original schedule tree before any transformation.
+/// 1 for the schedule tree after isl's rescheduling.
+/// 2 for the schedule tree after optimizations are applied
+/// (tiling, pattern matching)
+static void walkScheduleTreeForStatistics(isl::schedule Schedule, int Version) {
+ auto Root = Schedule.get_root();
+ if (Root.is_null())
+ return;
+
+ isl_schedule_node_foreach_descendant_top_down(
+ Root.get(),
+ [](__isl_keep isl_schedule_node *nodeptr, void *user) -> isl_bool {
+ isl::schedule_node Node = isl::manage_copy(nodeptr);
+ int Version = *static_cast<int *>(user);
+
+ switch (isl_schedule_node_get_type(Node.get())) {
+ case isl_schedule_node_band: {
+ NumBands[Version]++;
+ if (isl_schedule_node_band_get_permutable(Node.get()) ==
+ isl_bool_true)
+ NumPermutable[Version]++;
+
+ int CountMembers = isl_schedule_node_band_n_member(Node.get());
+ NumBandMembers[Version] += CountMembers;
+ for (int i = 0; i < CountMembers; i += 1) {
+ if (Node.as<isl::schedule_node_band>().member_get_coincident(i))
+ NumCoincident[Version]++;
+ }
+ break;
+ }
+
+ case isl_schedule_node_filter:
+ NumFilters[Version]++;
+ break;
+
+ case isl_schedule_node_extension:
+ NumExtension[Version]++;
+ break;
+
+ default:
+ break;
+ }
+
+ return isl_bool_true;
+ },
+ &Version);
+}
+
+static bool runIslScheduleOptimizer(
+ Scop &S,
+ function_ref<const Dependences &(Dependences::AnalysisLevel)> GetDeps,
+ TargetTransformInfo *TTI, OptimizationRemarkEmitter *ORE,
+ isl::schedule &LastSchedule) {
+
+ // Skip SCoPs in case they're already optimised by PPCGCodeGeneration
+ if (S.isToBeSkipped())
+ return false;
+
+ // Skip empty SCoPs but still allow code generation as it will delete the
+ // loops present but not needed.
+ if (S.getSize() == 0) {
+ S.markAsOptimized();
+ return false;
+ }
+
+ ScopsProcessed++;
+
+ // Schedule without optimizations.
+ isl::schedule Schedule = S.getScheduleTree();
+ walkScheduleTreeForStatistics(S.getScheduleTree(), 0);
+ LLVM_DEBUG(printSchedule(dbgs(), Schedule, "Original schedule tree"));
+
+ bool HasUserTransformation = false;
+ if (PragmaBasedOpts) {
+ isl::schedule ManuallyTransformed = applyManualTransformations(
+ &S, Schedule, GetDeps(Dependences::AL_Statement), ORE);
+ if (ManuallyTransformed.is_null()) {
+ LLVM_DEBUG(dbgs() << "Error during manual optimization\n");
+ return false;
+ }
+
+ if (ManuallyTransformed.get() != Schedule.get()) {
+ // User transformations have precedence over other transformations.
+ HasUserTransformation = true;
+ Schedule = std::move(ManuallyTransformed);
+ LLVM_DEBUG(
+ printSchedule(dbgs(), Schedule, "After manual transformations"));
+ }
+ }
+
+ // Only continue if either manual transformations have been applied or we are
+ // allowed to apply heuristics.
+ // TODO: Detect disabled heuristics and no user-directed transformation
+ // metadata earlier in ScopDetection.
+ if (!HasUserTransformation && S.hasDisableHeuristicsHint()) {
+ LLVM_DEBUG(dbgs() << "Heuristic optimizations disabled by metadata\n");
+ return false;
+ }
+
+ // Get dependency analysis.
+ const Dependences &D = GetDeps(Dependences::AL_Statement);
+ if (D.getSharedIslCtx() != S.getSharedIslCtx()) {
+ LLVM_DEBUG(dbgs() << "DependenceInfo for another SCoP/isl_ctx\n");
+ return false;
+ }
+ if (!D.hasValidDependences()) {
+ LLVM_DEBUG(dbgs() << "Dependency information not available\n");
+ return false;
+ }
+
+ // Apply ISL's algorithm only if not overriden by the user. Note that
+ // post-rescheduling optimizations (tiling, pattern-based, prevectorization)
+ // rely on the coincidence/permutable annotations on schedule tree bands that
+ // are added by the rescheduling analyzer. Therefore, disabling the
+ // rescheduler implicitly also disables these optimizations.
+ if (!EnableReschedule) {
+ LLVM_DEBUG(dbgs() << "Skipping rescheduling due to command line option\n");
+ } else if (HasUserTransformation) {
+ LLVM_DEBUG(
+ dbgs() << "Skipping rescheduling due to manual transformation\n");
+ } else {
+ // Build input data.
+ int ValidityKinds =
+ Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW;
+ int ProximityKinds;
+
+ if (OptimizeDeps == "all")
+ ProximityKinds =
+ Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW;
+ else if (OptimizeDeps == "raw")
+ ProximityKinds = Dependences::TYPE_RAW;
+ else {
+ errs() << "Do not know how to optimize for '" << OptimizeDeps << "'"
+ << " Falling back to optimizing all dependences.\n";
+ ProximityKinds =
+ Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW;
+ }
+
+ isl::union_set Domain = S.getDomains();
+
+ if (Domain.is_null())
+ return false;
+
+ isl::union_map Validity = D.getDependences(ValidityKinds);
+ isl::union_map Proximity = D.getDependences(ProximityKinds);
+
+ // Simplify the dependences by removing the constraints introduced by the
+ // domains. This can speed up the scheduling time significantly, as large
+ // constant coefficients will be removed from the dependences. The
+ // introduction of some additional dependences reduces the possible
+ // transformations, but in most cases, such transformation do not seem to be
+ // interesting anyway. In some cases this option may stop the scheduler to
+ // find any schedule.
+ if (SimplifyDeps == "yes") {
+ Validity = Validity.gist_domain(Domain);
+ Validity = Validity.gist_range(Domain);
+ Proximity = Proximity.gist_domain(Domain);
+ Proximity = Proximity.gist_range(Domain);
+ } else if (SimplifyDeps != "no") {
+ errs()
+ << "warning: Option -polly-opt-simplify-deps should either be 'yes' "
+ "or 'no'. Falling back to default: 'yes'\n";
+ }
+
+ LLVM_DEBUG(dbgs() << "\n\nCompute schedule from: ");
+ LLVM_DEBUG(dbgs() << "Domain := " << Domain << ";\n");
+ LLVM_DEBUG(dbgs() << "Proximity := " << Proximity << ";\n");
+ LLVM_DEBUG(dbgs() << "Validity := " << Validity << ";\n");
+
+ int IslMaximizeBands;
+ if (MaximizeBandDepth == "yes") {
+ IslMaximizeBands = 1;
+ } else if (MaximizeBandDepth == "no") {
+ IslMaximizeBands = 0;
+ } else {
+ errs()
+ << "warning: Option -polly-opt-maximize-bands should either be 'yes'"
+ " or 'no'. Falling back to default: 'yes'\n";
+ IslMaximizeBands = 1;
+ }
+
+ int IslOuterCoincidence;
+ if (OuterCoincidence == "yes") {
+ IslOuterCoincidence = 1;
+ } else if (OuterCoincidence == "no") {
+ IslOuterCoincidence = 0;
+ } else {
+ errs() << "warning: Option -polly-opt-outer-coincidence should either be "
+ "'yes' or 'no'. Falling back to default: 'no'\n";
+ IslOuterCoincidence = 0;
+ }
+
+ isl_ctx *Ctx = S.getIslCtx().get();
+
+ isl_options_set_schedule_outer_coincidence(Ctx, IslOuterCoincidence);
+ isl_options_set_schedule_maximize_band_depth(Ctx, IslMaximizeBands);
+ isl_options_set_schedule_max_constant_term(Ctx, MaxConstantTerm);
+ isl_options_set_schedule_max_coefficient(Ctx, MaxCoefficient);
+ isl_options_set_tile_scale_tile_loops(Ctx, 0);
+
+ auto OnErrorStatus = isl_options_get_on_error(Ctx);
+ isl_options_set_on_error(Ctx, ISL_ON_ERROR_CONTINUE);
+
+ auto SC = isl::schedule_constraints::on_domain(Domain);
+ SC = SC.set_proximity(Proximity);
+ SC = SC.set_validity(Validity);
+ SC = SC.set_coincidence(Validity);
+ Schedule = SC.compute_schedule();
+ isl_options_set_on_error(Ctx, OnErrorStatus);
+
+ ScopsRescheduled++;
+ LLVM_DEBUG(printSchedule(dbgs(), Schedule, "After rescheduling"));
+ }
+
+ walkScheduleTreeForStatistics(Schedule, 1);
+
+ // In cases the scheduler is not able to optimize the code, we just do not
+ // touch the schedule.
+ if (Schedule.is_null())
+ return false;
+
+ if (GreedyFusion) {
+ isl::union_map Validity = D.getDependences(
+ Dependences::TYPE_RAW | Dependences::TYPE_WAR | Dependences::TYPE_WAW);
+ Schedule = applyGreedyFusion(Schedule, Validity);
+ assert(!Schedule.is_null());
+ }
+
+ // Apply post-rescheduling optimizations (if enabled) and/or prevectorization.
+ const OptimizerAdditionalInfoTy OAI = {
+ TTI, const_cast<Dependences *>(&D),
+ /*PatternOpts=*/!HasUserTransformation && PMBasedOpts,
+ /*Postopts=*/!HasUserTransformation && EnablePostopts,
+ /*Prevect=*/PollyVectorizerChoice != VECTORIZER_NONE};
+ if (OAI.PatternOpts || OAI.Postopts || OAI.Prevect) {
+ Schedule = ScheduleTreeOptimizer::optimizeSchedule(Schedule, &OAI);
+ Schedule = hoistExtensionNodes(Schedule);
+ LLVM_DEBUG(printSchedule(dbgs(), Schedule, "After post-optimizations"));
+ walkScheduleTreeForStatistics(Schedule, 2);
+ }
+
+ // Skip profitability check if user transformation(s) have been applied.
+ if (!HasUserTransformation &&
+ !ScheduleTreeOptimizer::isProfitableSchedule(S, Schedule))
+ return false;
+
+ auto ScopStats = S.getStatistics();
+ ScopsOptimized++;
+ NumAffineLoopsOptimized += ScopStats.NumAffineLoops;
+ NumBoxedLoopsOptimized += ScopStats.NumBoxedLoops;
+ LastSchedule = Schedule;
+
+ S.setScheduleTree(Schedule);
+ S.markAsOptimized();
+
+ if (OptimizedScops)
+ errs() << S;
+
+ return false;
+}
+
+bool IslScheduleOptimizerWrapperPass::runOnScop(Scop &S) {
+ releaseMemory();
+
+ Function &F = S.getFunction();
+ IslCtx = S.getSharedIslCtx();
+
+ auto getDependences =
+ [this](Dependences::AnalysisLevel) -> const Dependences & {
+ return getAnalysis<DependenceInfo>().getDependences(
+ Dependences::AL_Statement);
+ };
+ OptimizationRemarkEmitter &ORE =
+ getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE();
+ TargetTransformInfo *TTI =
+ &getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
+ return runIslScheduleOptimizer(S, getDependences, TTI, &ORE, LastSchedule);
+}
+
+static void runScheduleOptimizerPrinter(raw_ostream &OS,
+ isl::schedule LastSchedule) {
+ isl_printer *p;
+ char *ScheduleStr;
+
+ OS << "Calculated schedule:\n";
+
+ if (LastSchedule.is_null()) {
+ OS << "n/a\n";
+ return;
+ }
+
+ p = isl_printer_to_str(LastSchedule.ctx().get());
+ p = isl_printer_set_yaml_style(p, ISL_YAML_STYLE_BLOCK);
+ p = isl_printer_print_schedule(p, LastSchedule.get());
+ ScheduleStr = isl_printer_get_str(p);
+ isl_printer_free(p);
+
+ OS << ScheduleStr << "\n";
+
+ free(ScheduleStr);
+}
+
+void IslScheduleOptimizerWrapperPass::printScop(raw_ostream &OS, Scop &) const {
+ runScheduleOptimizerPrinter(OS, LastSchedule);
+}
+
+void IslScheduleOptimizerWrapperPass::getAnalysisUsage(
+ AnalysisUsage &AU) const {
+ ScopPass::getAnalysisUsage(AU);
+ AU.addRequired<DependenceInfo>();
+ AU.addRequired<TargetTransformInfoWrapperPass>();
+ AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
+
+ AU.addPreserved<DependenceInfo>();
+ AU.addPreserved<OptimizationRemarkEmitterWrapperPass>();
+}
+
+} // namespace
+
+Pass *polly::createIslScheduleOptimizerWrapperPass() {
+ return new IslScheduleOptimizerWrapperPass();
+}
+
+INITIALIZE_PASS_BEGIN(IslScheduleOptimizerWrapperPass, "polly-opt-isl",
+ "Polly - Optimize schedule of SCoP", false, false);
+INITIALIZE_PASS_DEPENDENCY(DependenceInfo);
+INITIALIZE_PASS_DEPENDENCY(ScopInfoRegionPass);
+INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass);
+INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass);
+INITIALIZE_PASS_END(IslScheduleOptimizerWrapperPass, "polly-opt-isl",
+ "Polly - Optimize schedule of SCoP", false, false)
+
+static llvm::PreservedAnalyses
+runIslScheduleOptimizerUsingNPM(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U,
+ raw_ostream *OS) {
+ DependenceAnalysis::Result &Deps = SAM.getResult<DependenceAnalysis>(S, SAR);
+ auto GetDeps = [&Deps](Dependences::AnalysisLevel) -> const Dependences & {
+ return Deps.getDependences(Dependences::AL_Statement);
+ };
+ OptimizationRemarkEmitter ORE(&S.getFunction());
+ TargetTransformInfo *TTI = &SAR.TTI;
+ isl::schedule LastSchedule;
+ bool Modified = runIslScheduleOptimizer(S, GetDeps, TTI, &ORE, LastSchedule);
+ if (OS) {
+ *OS << "Printing analysis 'Polly - Optimize schedule of SCoP' for region: '"
+ << S.getName() << "' in function '" << S.getFunction().getName()
+ << "':\n";
+ runScheduleOptimizerPrinter(*OS, LastSchedule);
+ }
+
+ if (!Modified)
+ return PreservedAnalyses::all();
+
+ PreservedAnalyses PA;
+ PA.preserveSet<AllAnalysesOn<Module>>();
+ PA.preserveSet<AllAnalysesOn<Function>>();
+ PA.preserveSet<AllAnalysesOn<Loop>>();
+ return PA;
+}
+
+llvm::PreservedAnalyses
+IslScheduleOptimizerPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U) {
+ return runIslScheduleOptimizerUsingNPM(S, SAM, SAR, U, nullptr);
+}
+
+llvm::PreservedAnalyses
+IslScheduleOptimizerPrinterPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &U) {
+ return runIslScheduleOptimizerUsingNPM(S, SAM, SAR, U, &OS);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/ScheduleTreeTransform.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/ScheduleTreeTransform.cpp
new file mode 100644
index 00000000000..01f18eadb4d
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/ScheduleTreeTransform.cpp
@@ -0,0 +1,1243 @@
+//===- polly/ScheduleTreeTransform.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Make changes to isl's schedule tree data structure.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/ScheduleTreeTransform.h"
+#include "polly/Support/GICHelper.h"
+#include "polly/Support/ISLTools.h"
+#include "polly/Support/ScopHelper.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/Transforms/Utils/UnrollLoop.h"
+
+#define DEBUG_TYPE "polly-opt-isl"
+
+using namespace polly;
+using namespace llvm;
+
+namespace {
+
+/// Copy the band member attributes (coincidence, loop type, isolate ast loop
+/// type) from one band to another.
+static isl::schedule_node_band
+applyBandMemberAttributes(isl::schedule_node_band Target, int TargetIdx,
+ const isl::schedule_node_band &Source,
+ int SourceIdx) {
+ bool Coincident = Source.member_get_coincident(SourceIdx).release();
+ Target = Target.member_set_coincident(TargetIdx, Coincident);
+
+ isl_ast_loop_type LoopType =
+ isl_schedule_node_band_member_get_ast_loop_type(Source.get(), SourceIdx);
+ Target = isl::manage(isl_schedule_node_band_member_set_ast_loop_type(
+ Target.release(), TargetIdx, LoopType))
+ .as<isl::schedule_node_band>();
+
+ isl_ast_loop_type IsolateType =
+ isl_schedule_node_band_member_get_isolate_ast_loop_type(Source.get(),
+ SourceIdx);
+ Target = isl::manage(isl_schedule_node_band_member_set_isolate_ast_loop_type(
+ Target.release(), TargetIdx, IsolateType))
+ .as<isl::schedule_node_band>();
+
+ return Target;
+}
+
+/// Create a new band by copying members from another @p Band. @p IncludeCb
+/// decides which band indices are copied to the result.
+template <typename CbTy>
+static isl::schedule rebuildBand(isl::schedule_node_band OldBand,
+ isl::schedule Body, CbTy IncludeCb) {
+ int NumBandDims = unsignedFromIslSize(OldBand.n_member());
+
+ bool ExcludeAny = false;
+ bool IncludeAny = false;
+ for (auto OldIdx : seq<int>(0, NumBandDims)) {
+ if (IncludeCb(OldIdx))
+ IncludeAny = true;
+ else
+ ExcludeAny = true;
+ }
+
+ // Instead of creating a zero-member band, don't create a band at all.
+ if (!IncludeAny)
+ return Body;
+
+ isl::multi_union_pw_aff PartialSched = OldBand.get_partial_schedule();
+ isl::multi_union_pw_aff NewPartialSched;
+ if (ExcludeAny) {
+ // Select the included partial scatter functions.
+ isl::union_pw_aff_list List = PartialSched.list();
+ int NewIdx = 0;
+ for (auto OldIdx : seq<int>(0, NumBandDims)) {
+ if (IncludeCb(OldIdx))
+ NewIdx += 1;
+ else
+ List = List.drop(NewIdx, 1);
+ }
+ isl::space ParamSpace = PartialSched.get_space().params();
+ isl::space NewScatterSpace = ParamSpace.add_unnamed_tuple(NewIdx);
+ NewPartialSched = isl::multi_union_pw_aff(NewScatterSpace, List);
+ } else {
+ // Just reuse original scatter function of copying all of them.
+ NewPartialSched = PartialSched;
+ }
+
+ // Create the new band node.
+ isl::schedule_node_band NewBand =
+ Body.insert_partial_schedule(NewPartialSched)
+ .get_root()
+ .child(0)
+ .as<isl::schedule_node_band>();
+
+ // If OldBand was permutable, so is the new one, even if some dimensions are
+ // missing.
+ bool IsPermutable = OldBand.permutable().release();
+ NewBand = NewBand.set_permutable(IsPermutable);
+
+ // Reapply member attributes.
+ int NewIdx = 0;
+ for (auto OldIdx : seq<int>(0, NumBandDims)) {
+ if (!IncludeCb(OldIdx))
+ continue;
+ NewBand =
+ applyBandMemberAttributes(std::move(NewBand), NewIdx, OldBand, OldIdx);
+ NewIdx += 1;
+ }
+
+ return NewBand.get_schedule();
+}
+
+/// Rewrite a schedule tree by reconstructing it bottom-up.
+///
+/// By default, the original schedule tree is reconstructed. To build a
+/// different tree, redefine visitor methods in a derived class (CRTP).
+///
+/// Note that AST build options are not applied; Setting the isolate[] option
+/// makes the schedule tree 'anchored' and cannot be modified afterwards. Hence,
+/// AST build options must be set after the tree has been constructed.
+template <typename Derived, typename... Args>
+struct ScheduleTreeRewriter
+ : public RecursiveScheduleTreeVisitor<Derived, isl::schedule, Args...> {
+ Derived &getDerived() { return *static_cast<Derived *>(this); }
+ const Derived &getDerived() const {
+ return *static_cast<const Derived *>(this);
+ }
+
+ isl::schedule visitDomain(isl::schedule_node_domain Node, Args... args) {
+ // Every schedule_tree already has a domain node, no need to add one.
+ return getDerived().visit(Node.first_child(), std::forward<Args>(args)...);
+ }
+
+ isl::schedule visitBand(isl::schedule_node_band Band, Args... args) {
+ isl::schedule NewChild =
+ getDerived().visit(Band.child(0), std::forward<Args>(args)...);
+ return rebuildBand(Band, NewChild, [](int) { return true; });
+ }
+
+ isl::schedule visitSequence(isl::schedule_node_sequence Sequence,
+ Args... args) {
+ int NumChildren = isl_schedule_node_n_children(Sequence.get());
+ isl::schedule Result =
+ getDerived().visit(Sequence.child(0), std::forward<Args>(args)...);
+ for (int i = 1; i < NumChildren; i += 1)
+ Result = Result.sequence(
+ getDerived().visit(Sequence.child(i), std::forward<Args>(args)...));
+ return Result;
+ }
+
+ isl::schedule visitSet(isl::schedule_node_set Set, Args... args) {
+ int NumChildren = isl_schedule_node_n_children(Set.get());
+ isl::schedule Result =
+ getDerived().visit(Set.child(0), std::forward<Args>(args)...);
+ for (int i = 1; i < NumChildren; i += 1)
+ Result = isl::manage(
+ isl_schedule_set(Result.release(),
+ getDerived()
+ .visit(Set.child(i), std::forward<Args>(args)...)
+ .release()));
+ return Result;
+ }
+
+ isl::schedule visitLeaf(isl::schedule_node_leaf Leaf, Args... args) {
+ return isl::schedule::from_domain(Leaf.get_domain());
+ }
+
+ isl::schedule visitMark(const isl::schedule_node &Mark, Args... args) {
+
+ isl::id TheMark = Mark.as<isl::schedule_node_mark>().get_id();
+ isl::schedule_node NewChild =
+ getDerived()
+ .visit(Mark.first_child(), std::forward<Args>(args)...)
+ .get_root()
+ .first_child();
+ return NewChild.insert_mark(TheMark).get_schedule();
+ }
+
+ isl::schedule visitExtension(isl::schedule_node_extension Extension,
+ Args... args) {
+ isl::union_map TheExtension =
+ Extension.as<isl::schedule_node_extension>().get_extension();
+ isl::schedule_node NewChild = getDerived()
+ .visit(Extension.child(0), args...)
+ .get_root()
+ .first_child();
+ isl::schedule_node NewExtension =
+ isl::schedule_node::from_extension(TheExtension);
+ return NewChild.graft_before(NewExtension).get_schedule();
+ }
+
+ isl::schedule visitFilter(isl::schedule_node_filter Filter, Args... args) {
+ isl::union_set FilterDomain =
+ Filter.as<isl::schedule_node_filter>().get_filter();
+ isl::schedule NewSchedule =
+ getDerived().visit(Filter.child(0), std::forward<Args>(args)...);
+ return NewSchedule.intersect_domain(FilterDomain);
+ }
+
+ isl::schedule visitNode(isl::schedule_node Node, Args... args) {
+ llvm_unreachable("Not implemented");
+ }
+};
+
+/// Rewrite the schedule tree without any changes. Useful to copy a subtree into
+/// a new schedule, discarding everything but.
+struct IdentityRewriter : public ScheduleTreeRewriter<IdentityRewriter> {};
+
+/// Rewrite a schedule tree to an equivalent one without extension nodes.
+///
+/// Each visit method takes two additional arguments:
+///
+/// * The new domain the node, which is the inherited domain plus any domains
+/// added by extension nodes.
+///
+/// * A map of extension domains of all children is returned; it is required by
+/// band nodes to schedule the additional domains at the same position as the
+/// extension node would.
+///
+struct ExtensionNodeRewriter
+ : public ScheduleTreeRewriter<ExtensionNodeRewriter, const isl::union_set &,
+ isl::union_map &> {
+ using BaseTy = ScheduleTreeRewriter<ExtensionNodeRewriter,
+ const isl::union_set &, isl::union_map &>;
+ BaseTy &getBase() { return *this; }
+ const BaseTy &getBase() const { return *this; }
+
+ isl::schedule visitSchedule(isl::schedule Schedule) {
+ isl::union_map Extensions;
+ isl::schedule Result =
+ visit(Schedule.get_root(), Schedule.get_domain(), Extensions);
+ assert(!Extensions.is_null() && Extensions.is_empty());
+ return Result;
+ }
+
+ isl::schedule visitSequence(isl::schedule_node_sequence Sequence,
+ const isl::union_set &Domain,
+ isl::union_map &Extensions) {
+ int NumChildren = isl_schedule_node_n_children(Sequence.get());
+ isl::schedule NewNode = visit(Sequence.first_child(), Domain, Extensions);
+ for (int i = 1; i < NumChildren; i += 1) {
+ isl::schedule_node OldChild = Sequence.child(i);
+ isl::union_map NewChildExtensions;
+ isl::schedule NewChildNode = visit(OldChild, Domain, NewChildExtensions);
+ NewNode = NewNode.sequence(NewChildNode);
+ Extensions = Extensions.unite(NewChildExtensions);
+ }
+ return NewNode;
+ }
+
+ isl::schedule visitSet(isl::schedule_node_set Set,
+ const isl::union_set &Domain,
+ isl::union_map &Extensions) {
+ int NumChildren = isl_schedule_node_n_children(Set.get());
+ isl::schedule NewNode = visit(Set.first_child(), Domain, Extensions);
+ for (int i = 1; i < NumChildren; i += 1) {
+ isl::schedule_node OldChild = Set.child(i);
+ isl::union_map NewChildExtensions;
+ isl::schedule NewChildNode = visit(OldChild, Domain, NewChildExtensions);
+ NewNode = isl::manage(
+ isl_schedule_set(NewNode.release(), NewChildNode.release()));
+ Extensions = Extensions.unite(NewChildExtensions);
+ }
+ return NewNode;
+ }
+
+ isl::schedule visitLeaf(isl::schedule_node_leaf Leaf,
+ const isl::union_set &Domain,
+ isl::union_map &Extensions) {
+ Extensions = isl::union_map::empty(Leaf.ctx());
+ return isl::schedule::from_domain(Domain);
+ }
+
+ isl::schedule visitBand(isl::schedule_node_band OldNode,
+ const isl::union_set &Domain,
+ isl::union_map &OuterExtensions) {
+ isl::schedule_node OldChild = OldNode.first_child();
+ isl::multi_union_pw_aff PartialSched =
+ isl::manage(isl_schedule_node_band_get_partial_schedule(OldNode.get()));
+
+ isl::union_map NewChildExtensions;
+ isl::schedule NewChild = visit(OldChild, Domain, NewChildExtensions);
+
+ // Add the extensions to the partial schedule.
+ OuterExtensions = isl::union_map::empty(NewChildExtensions.ctx());
+ isl::union_map NewPartialSchedMap = isl::union_map::from(PartialSched);
+ unsigned BandDims = isl_schedule_node_band_n_member(OldNode.get());
+ for (isl::map Ext : NewChildExtensions.get_map_list()) {
+ unsigned ExtDims = unsignedFromIslSize(Ext.domain_tuple_dim());
+ assert(ExtDims >= BandDims);
+ unsigned OuterDims = ExtDims - BandDims;
+
+ isl::map BandSched =
+ Ext.project_out(isl::dim::in, 0, OuterDims).reverse();
+ NewPartialSchedMap = NewPartialSchedMap.unite(BandSched);
+
+ // There might be more outer bands that have to schedule the extensions.
+ if (OuterDims > 0) {
+ isl::map OuterSched =
+ Ext.project_out(isl::dim::in, OuterDims, BandDims);
+ OuterExtensions = OuterExtensions.unite(OuterSched);
+ }
+ }
+ isl::multi_union_pw_aff NewPartialSchedAsAsMultiUnionPwAff =
+ isl::multi_union_pw_aff::from_union_map(NewPartialSchedMap);
+ isl::schedule_node NewNode =
+ NewChild.insert_partial_schedule(NewPartialSchedAsAsMultiUnionPwAff)
+ .get_root()
+ .child(0);
+
+ // Reapply permutability and coincidence attributes.
+ NewNode = isl::manage(isl_schedule_node_band_set_permutable(
+ NewNode.release(),
+ isl_schedule_node_band_get_permutable(OldNode.get())));
+ for (unsigned i = 0; i < BandDims; i += 1)
+ NewNode = applyBandMemberAttributes(NewNode.as<isl::schedule_node_band>(),
+ i, OldNode, i);
+
+ return NewNode.get_schedule();
+ }
+
+ isl::schedule visitFilter(isl::schedule_node_filter Filter,
+ const isl::union_set &Domain,
+ isl::union_map &Extensions) {
+ isl::union_set FilterDomain =
+ Filter.as<isl::schedule_node_filter>().get_filter();
+ isl::union_set NewDomain = Domain.intersect(FilterDomain);
+
+ // A filter is added implicitly if necessary when joining schedule trees.
+ return visit(Filter.first_child(), NewDomain, Extensions);
+ }
+
+ isl::schedule visitExtension(isl::schedule_node_extension Extension,
+ const isl::union_set &Domain,
+ isl::union_map &Extensions) {
+ isl::union_map ExtDomain =
+ Extension.as<isl::schedule_node_extension>().get_extension();
+ isl::union_set NewDomain = Domain.unite(ExtDomain.range());
+ isl::union_map ChildExtensions;
+ isl::schedule NewChild =
+ visit(Extension.first_child(), NewDomain, ChildExtensions);
+ Extensions = ChildExtensions.unite(ExtDomain);
+ return NewChild;
+ }
+};
+
+/// Collect all AST build options in any schedule tree band.
+///
+/// ScheduleTreeRewriter cannot apply the schedule tree options. This class
+/// collects these options to apply them later.
+struct CollectASTBuildOptions
+ : public RecursiveScheduleTreeVisitor<CollectASTBuildOptions> {
+ using BaseTy = RecursiveScheduleTreeVisitor<CollectASTBuildOptions>;
+ BaseTy &getBase() { return *this; }
+ const BaseTy &getBase() const { return *this; }
+
+ llvm::SmallVector<isl::union_set, 8> ASTBuildOptions;
+
+ void visitBand(isl::schedule_node_band Band) {
+ ASTBuildOptions.push_back(
+ isl::manage(isl_schedule_node_band_get_ast_build_options(Band.get())));
+ return getBase().visitBand(Band);
+ }
+};
+
+/// Apply AST build options to the bands in a schedule tree.
+///
+/// This rewrites a schedule tree with the AST build options applied. We assume
+/// that the band nodes are visited in the same order as they were when the
+/// build options were collected, typically by CollectASTBuildOptions.
+struct ApplyASTBuildOptions
+ : public ScheduleNodeRewriter<ApplyASTBuildOptions> {
+ using BaseTy = ScheduleNodeRewriter<ApplyASTBuildOptions>;
+ BaseTy &getBase() { return *this; }
+ const BaseTy &getBase() const { return *this; }
+
+ size_t Pos;
+ llvm::ArrayRef<isl::union_set> ASTBuildOptions;
+
+ ApplyASTBuildOptions(llvm::ArrayRef<isl::union_set> ASTBuildOptions)
+ : ASTBuildOptions(ASTBuildOptions) {}
+
+ isl::schedule visitSchedule(isl::schedule Schedule) {
+ Pos = 0;
+ isl::schedule Result = visit(Schedule).get_schedule();
+ assert(Pos == ASTBuildOptions.size() &&
+ "AST build options must match to band nodes");
+ return Result;
+ }
+
+ isl::schedule_node visitBand(isl::schedule_node_band Band) {
+ isl::schedule_node_band Result =
+ Band.set_ast_build_options(ASTBuildOptions[Pos]);
+ Pos += 1;
+ return getBase().visitBand(Result);
+ }
+};
+
+/// Return whether the schedule contains an extension node.
+static bool containsExtensionNode(isl::schedule Schedule) {
+ assert(!Schedule.is_null());
+
+ auto Callback = [](__isl_keep isl_schedule_node *Node,
+ void *User) -> isl_bool {
+ if (isl_schedule_node_get_type(Node) == isl_schedule_node_extension) {
+ // Stop walking the schedule tree.
+ return isl_bool_error;
+ }
+
+ // Continue searching the subtree.
+ return isl_bool_true;
+ };
+ isl_stat RetVal = isl_schedule_foreach_schedule_node_top_down(
+ Schedule.get(), Callback, nullptr);
+
+ // We assume that the traversal itself does not fail, i.e. the only reason to
+ // return isl_stat_error is that an extension node was found.
+ return RetVal == isl_stat_error;
+}
+
+/// Find a named MDNode property in a LoopID.
+static MDNode *findOptionalNodeOperand(MDNode *LoopMD, StringRef Name) {
+ return dyn_cast_or_null<MDNode>(
+ findMetadataOperand(LoopMD, Name).getValueOr(nullptr));
+}
+
+/// Is this node of type mark?
+static bool isMark(const isl::schedule_node &Node) {
+ return isl_schedule_node_get_type(Node.get()) == isl_schedule_node_mark;
+}
+
+/// Is this node of type band?
+static bool isBand(const isl::schedule_node &Node) {
+ return isl_schedule_node_get_type(Node.get()) == isl_schedule_node_band;
+}
+
+#ifndef NDEBUG
+/// Is this node a band of a single dimension (i.e. could represent a loop)?
+static bool isBandWithSingleLoop(const isl::schedule_node &Node) {
+ return isBand(Node) && isl_schedule_node_band_n_member(Node.get()) == 1;
+}
+#endif
+
+static bool isLeaf(const isl::schedule_node &Node) {
+ return isl_schedule_node_get_type(Node.get()) == isl_schedule_node_leaf;
+}
+
+/// Create an isl::id representing the output loop after a transformation.
+static isl::id createGeneratedLoopAttr(isl::ctx Ctx, MDNode *FollowupLoopMD) {
+ // Don't need to id the followup.
+ // TODO: Append llvm.loop.disable_heustistics metadata unless overridden by
+ // user followup-MD
+ if (!FollowupLoopMD)
+ return {};
+
+ BandAttr *Attr = new BandAttr();
+ Attr->Metadata = FollowupLoopMD;
+ return getIslLoopAttr(Ctx, Attr);
+}
+
+/// A loop consists of a band and an optional marker that wraps it. Return the
+/// outermost of the two.
+
+/// That is, either the mark or, if there is not mark, the loop itself. Can
+/// start with either the mark or the band.
+static isl::schedule_node moveToBandMark(isl::schedule_node BandOrMark) {
+ if (isBandMark(BandOrMark)) {
+ assert(isBandWithSingleLoop(BandOrMark.child(0)));
+ return BandOrMark;
+ }
+ assert(isBandWithSingleLoop(BandOrMark));
+
+ isl::schedule_node Mark = BandOrMark.parent();
+ if (isBandMark(Mark))
+ return Mark;
+
+ // Band has no loop marker.
+ return BandOrMark;
+}
+
+static isl::schedule_node removeMark(isl::schedule_node MarkOrBand,
+ BandAttr *&Attr) {
+ MarkOrBand = moveToBandMark(MarkOrBand);
+
+ isl::schedule_node Band;
+ if (isMark(MarkOrBand)) {
+ Attr = getLoopAttr(MarkOrBand.as<isl::schedule_node_mark>().get_id());
+ Band = isl::manage(isl_schedule_node_delete(MarkOrBand.release()));
+ } else {
+ Attr = nullptr;
+ Band = MarkOrBand;
+ }
+
+ assert(isBandWithSingleLoop(Band));
+ return Band;
+}
+
+/// Remove the mark that wraps a loop. Return the band representing the loop.
+static isl::schedule_node removeMark(isl::schedule_node MarkOrBand) {
+ BandAttr *Attr;
+ return removeMark(MarkOrBand, Attr);
+}
+
+static isl::schedule_node insertMark(isl::schedule_node Band, isl::id Mark) {
+ assert(isBand(Band));
+ assert(moveToBandMark(Band).is_equal(Band) &&
+ "Don't add a two marks for a band");
+
+ return Band.insert_mark(Mark).child(0);
+}
+
+/// Return the (one-dimensional) set of numbers that are divisible by @p Factor
+/// with remainder @p Offset.
+///
+/// isDivisibleBySet(Ctx, 4, 0) = { [i] : floord(i,4) = 0 }
+/// isDivisibleBySet(Ctx, 4, 1) = { [i] : floord(i,4) = 1 }
+///
+static isl::basic_set isDivisibleBySet(isl::ctx &Ctx, long Factor,
+ long Offset) {
+ isl::val ValFactor{Ctx, Factor};
+ isl::val ValOffset{Ctx, Offset};
+
+ isl::space Unispace{Ctx, 0, 1};
+ isl::local_space LUnispace{Unispace};
+ isl::aff AffFactor{LUnispace, ValFactor};
+ isl::aff AffOffset{LUnispace, ValOffset};
+
+ isl::aff Id = isl::aff::var_on_domain(LUnispace, isl::dim::out, 0);
+ isl::aff DivMul = Id.mod(ValFactor);
+ isl::basic_map Divisible = isl::basic_map::from_aff(DivMul);
+ isl::basic_map Modulo = Divisible.fix_val(isl::dim::out, 0, ValOffset);
+ return Modulo.domain();
+}
+
+/// Make the last dimension of Set to take values from 0 to VectorWidth - 1.
+///
+/// @param Set A set, which should be modified.
+/// @param VectorWidth A parameter, which determines the constraint.
+static isl::set addExtentConstraints(isl::set Set, int VectorWidth) {
+ unsigned Dims = unsignedFromIslSize(Set.tuple_dim());
+ assert(Dims >= 1);
+ isl::space Space = Set.get_space();
+ isl::local_space LocalSpace = isl::local_space(Space);
+ isl::constraint ExtConstr = isl::constraint::alloc_inequality(LocalSpace);
+ ExtConstr = ExtConstr.set_constant_si(0);
+ ExtConstr = ExtConstr.set_coefficient_si(isl::dim::set, Dims - 1, 1);
+ Set = Set.add_constraint(ExtConstr);
+ ExtConstr = isl::constraint::alloc_inequality(LocalSpace);
+ ExtConstr = ExtConstr.set_constant_si(VectorWidth - 1);
+ ExtConstr = ExtConstr.set_coefficient_si(isl::dim::set, Dims - 1, -1);
+ return Set.add_constraint(ExtConstr);
+}
+
+/// Collapse perfectly nested bands into a single band.
+class BandCollapseRewriter : public ScheduleTreeRewriter<BandCollapseRewriter> {
+private:
+ using BaseTy = ScheduleTreeRewriter<BandCollapseRewriter>;
+ BaseTy &getBase() { return *this; }
+ const BaseTy &getBase() const { return *this; }
+
+public:
+ isl::schedule visitBand(isl::schedule_node_band RootBand) {
+ isl::schedule_node_band Band = RootBand;
+ isl::ctx Ctx = Band.ctx();
+
+ // Do not merge permutable band to avoid loosing the permutability property.
+ // Cannot collapse even two permutable loops, they might be permutable
+ // individually, but not necassarily accross.
+ if (unsignedFromIslSize(Band.n_member()) > 1u && Band.permutable())
+ return getBase().visitBand(Band);
+
+ // Find collapsable bands.
+ SmallVector<isl::schedule_node_band> Nest;
+ int NumTotalLoops = 0;
+ isl::schedule_node Body;
+ while (true) {
+ Nest.push_back(Band);
+ NumTotalLoops += unsignedFromIslSize(Band.n_member());
+ Body = Band.first_child();
+ if (!Body.isa<isl::schedule_node_band>())
+ break;
+ Band = Body.as<isl::schedule_node_band>();
+
+ // Do not include next band if it is permutable to not lose its
+ // permutability property.
+ if (unsignedFromIslSize(Band.n_member()) > 1u && Band.permutable())
+ break;
+ }
+
+ // Nothing to collapse, preserve permutability.
+ if (Nest.size() <= 1)
+ return getBase().visitBand(Band);
+
+ LLVM_DEBUG({
+ dbgs() << "Found loops to collapse between\n";
+ dumpIslObj(RootBand, dbgs());
+ dbgs() << "and\n";
+ dumpIslObj(Body, dbgs());
+ dbgs() << "\n";
+ });
+
+ isl::schedule NewBody = visit(Body);
+
+ // Collect partial schedules from all members.
+ isl::union_pw_aff_list PartScheds{Ctx, NumTotalLoops};
+ for (isl::schedule_node_band Band : Nest) {
+ int NumLoops = unsignedFromIslSize(Band.n_member());
+ isl::multi_union_pw_aff BandScheds = Band.get_partial_schedule();
+ for (auto j : seq<int>(0, NumLoops))
+ PartScheds = PartScheds.add(BandScheds.at(j));
+ }
+ isl::space ScatterSpace = isl::space(Ctx, 0, NumTotalLoops);
+ isl::multi_union_pw_aff PartSchedsMulti{ScatterSpace, PartScheds};
+
+ isl::schedule_node_band CollapsedBand =
+ NewBody.insert_partial_schedule(PartSchedsMulti)
+ .get_root()
+ .first_child()
+ .as<isl::schedule_node_band>();
+
+ // Copy over loop attributes form original bands.
+ int LoopIdx = 0;
+ for (isl::schedule_node_band Band : Nest) {
+ int NumLoops = unsignedFromIslSize(Band.n_member());
+ for (int i : seq<int>(0, NumLoops)) {
+ CollapsedBand = applyBandMemberAttributes(std::move(CollapsedBand),
+ LoopIdx, Band, i);
+ LoopIdx += 1;
+ }
+ }
+ assert(LoopIdx == NumTotalLoops &&
+ "Expect the same number of loops to add up again");
+
+ return CollapsedBand.get_schedule();
+ }
+};
+
+static isl::schedule collapseBands(isl::schedule Sched) {
+ LLVM_DEBUG(dbgs() << "Collapse bands in schedule\n");
+ BandCollapseRewriter Rewriter;
+ return Rewriter.visit(Sched);
+}
+
+/// Collect sequentially executed bands (or anything else), even if nested in a
+/// mark or other nodes whose child is executed just once. If we can
+/// successfully fuse the bands, we allow them to be removed.
+static void collectPotentiallyFusableBands(
+ isl::schedule_node Node,
+ SmallVectorImpl<std::pair<isl::schedule_node, isl::schedule_node>>
+ &ScheduleBands,
+ const isl::schedule_node &DirectChild) {
+ switch (isl_schedule_node_get_type(Node.get())) {
+ case isl_schedule_node_sequence:
+ case isl_schedule_node_set:
+ case isl_schedule_node_mark:
+ case isl_schedule_node_domain:
+ case isl_schedule_node_filter:
+ if (Node.has_children()) {
+ isl::schedule_node C = Node.first_child();
+ while (true) {
+ collectPotentiallyFusableBands(C, ScheduleBands, DirectChild);
+ if (!C.has_next_sibling())
+ break;
+ C = C.next_sibling();
+ }
+ }
+ break;
+
+ default:
+ // Something that does not execute suquentially (e.g. a band)
+ ScheduleBands.push_back({Node, DirectChild});
+ break;
+ }
+}
+
+/// Remove dependencies that are resolved by @p PartSched. That is, remove
+/// everything that we already know is executed in-order.
+static isl::union_map remainingDepsFromPartialSchedule(isl::union_map PartSched,
+ isl::union_map Deps) {
+ unsigned NumDims = getNumScatterDims(PartSched);
+ auto ParamSpace = PartSched.get_space().params();
+
+ // { Scatter[] }
+ isl::space ScatterSpace =
+ ParamSpace.set_from_params().add_dims(isl::dim::set, NumDims);
+
+ // { Scatter[] -> Domain[] }
+ isl::union_map PartSchedRev = PartSched.reverse();
+
+ // { Scatter[] -> Scatter[] }
+ isl::map MaybeBefore = isl::map::lex_le(ScatterSpace);
+
+ // { Domain[] -> Domain[] }
+ isl::union_map DomMaybeBefore =
+ MaybeBefore.apply_domain(PartSchedRev).apply_range(PartSchedRev);
+
+ // { Domain[] -> Domain[] }
+ isl::union_map ChildRemainingDeps = Deps.intersect(DomMaybeBefore);
+
+ return ChildRemainingDeps;
+}
+
+/// Remove dependencies that are resolved by executing them in the order
+/// specified by @p Domains;
+static isl::union_map remainigDepsFromSequence(ArrayRef<isl::union_set> Domains,
+ isl::union_map Deps) {
+ isl::ctx Ctx = Deps.ctx();
+ isl::space ParamSpace = Deps.get_space().params();
+
+ // Create a partial schedule mapping to constants that reflect the execution
+ // order.
+ isl::union_map PartialSchedules = isl::union_map::empty(Ctx);
+ for (auto P : enumerate(Domains)) {
+ isl::val ExecTime = isl::val(Ctx, P.index());
+ isl::union_pw_aff DomSched{P.value(), ExecTime};
+ PartialSchedules = PartialSchedules.unite(DomSched.as_union_map());
+ }
+
+ return remainingDepsFromPartialSchedule(PartialSchedules, Deps);
+}
+
+/// Determine whether the outermost loop of to bands can be fused while
+/// respecting validity dependencies.
+static bool canFuseOutermost(const isl::schedule_node_band &LHS,
+ const isl::schedule_node_band &RHS,
+ const isl::union_map &Deps) {
+ // { LHSDomain[] -> Scatter[] }
+ isl::union_map LHSPartSched =
+ LHS.get_partial_schedule().get_at(0).as_union_map();
+
+ // { Domain[] -> Scatter[] }
+ isl::union_map RHSPartSched =
+ RHS.get_partial_schedule().get_at(0).as_union_map();
+
+ // Dependencies that are already resolved because LHS executes before RHS, but
+ // will not be anymore after fusion. { DefDomain[] -> UseDomain[] }
+ isl::union_map OrderedBySequence =
+ Deps.intersect_domain(LHSPartSched.domain())
+ .intersect_range(RHSPartSched.domain());
+
+ isl::space ParamSpace = OrderedBySequence.get_space().params();
+ isl::space NewScatterSpace = ParamSpace.add_unnamed_tuple(1);
+
+ // { Scatter[] -> Scatter[] }
+ isl::map After = isl::map::lex_gt(NewScatterSpace);
+
+ // After fusion, instances with smaller (or equal, which means they will be
+ // executed in the same iteration, but the LHS instance is still sequenced
+ // before RHS) scatter value will still be executed before. This are the
+ // orderings where this is not necessarily the case.
+ // { LHSDomain[] -> RHSDomain[] }
+ isl::union_map MightBeAfterDoms = After.apply_domain(LHSPartSched.reverse())
+ .apply_range(RHSPartSched.reverse());
+
+ // Dependencies that are not resolved by the new execution order.
+ isl::union_map WithBefore = OrderedBySequence.intersect(MightBeAfterDoms);
+
+ return WithBefore.is_empty();
+}
+
+/// Fuse @p LHS and @p RHS if possible while preserving validity dependenvies.
+static isl::schedule tryGreedyFuse(isl::schedule_node_band LHS,
+ isl::schedule_node_band RHS,
+ const isl::union_map &Deps) {
+ if (!canFuseOutermost(LHS, RHS, Deps))
+ return {};
+
+ LLVM_DEBUG({
+ dbgs() << "Found loops for greedy fusion:\n";
+ dumpIslObj(LHS, dbgs());
+ dbgs() << "and\n";
+ dumpIslObj(RHS, dbgs());
+ dbgs() << "\n";
+ });
+
+ // The partial schedule of the bands outermost loop that we need to combine
+ // for the fusion.
+ isl::union_pw_aff LHSPartOuterSched = LHS.get_partial_schedule().get_at(0);
+ isl::union_pw_aff RHSPartOuterSched = RHS.get_partial_schedule().get_at(0);
+
+ // Isolate band bodies as roots of their own schedule trees.
+ IdentityRewriter Rewriter;
+ isl::schedule LHSBody = Rewriter.visit(LHS.first_child());
+ isl::schedule RHSBody = Rewriter.visit(RHS.first_child());
+
+ // Reconstruct the non-outermost (not going to be fused) loops from both
+ // bands.
+ // TODO: Maybe it is possibly to transfer the 'permutability' property from
+ // LHS+RHS. At minimum we need merge multiple band members at once, otherwise
+ // permutability has no meaning.
+ isl::schedule LHSNewBody =
+ rebuildBand(LHS, LHSBody, [](int i) { return i > 0; });
+ isl::schedule RHSNewBody =
+ rebuildBand(RHS, RHSBody, [](int i) { return i > 0; });
+
+ // The loop body of the fused loop.
+ isl::schedule NewCommonBody = LHSNewBody.sequence(RHSNewBody);
+
+ // Combine the partial schedules of both loops to a new one. Instances with
+ // the same scatter value are put together.
+ isl::union_map NewCommonPartialSched =
+ LHSPartOuterSched.as_union_map().unite(RHSPartOuterSched.as_union_map());
+ isl::schedule NewCommonSchedule = NewCommonBody.insert_partial_schedule(
+ NewCommonPartialSched.as_multi_union_pw_aff());
+
+ return NewCommonSchedule;
+}
+
+static isl::schedule tryGreedyFuse(isl::schedule_node LHS,
+ isl::schedule_node RHS,
+ const isl::union_map &Deps) {
+ // TODO: Non-bands could be interpreted as a band with just as single
+ // iteration. However, this is only useful if both ends of a fused loop were
+ // originally loops themselves.
+ if (!LHS.isa<isl::schedule_node_band>())
+ return {};
+ if (!RHS.isa<isl::schedule_node_band>())
+ return {};
+
+ return tryGreedyFuse(LHS.as<isl::schedule_node_band>(),
+ RHS.as<isl::schedule_node_band>(), Deps);
+}
+
+/// Fuse all fusable loop top-down in a schedule tree.
+///
+/// The isl::union_map parameters is the set of validity dependencies that have
+/// not been resolved/carried by a parent schedule node.
+class GreedyFusionRewriter
+ : public ScheduleTreeRewriter<GreedyFusionRewriter, isl::union_map> {
+private:
+ using BaseTy = ScheduleTreeRewriter<GreedyFusionRewriter, isl::union_map>;
+ BaseTy &getBase() { return *this; }
+ const BaseTy &getBase() const { return *this; }
+
+public:
+ /// Is set to true if anything has been fused.
+ bool AnyChange = false;
+
+ isl::schedule visitBand(isl::schedule_node_band Band, isl::union_map Deps) {
+ // { Domain[] -> Scatter[] }
+ isl::union_map PartSched =
+ isl::union_map::from(Band.get_partial_schedule());
+ assert(getNumScatterDims(PartSched) ==
+ unsignedFromIslSize(Band.n_member()));
+ isl::space ParamSpace = PartSched.get_space().params();
+
+ // { Scatter[] -> Domain[] }
+ isl::union_map PartSchedRev = PartSched.reverse();
+
+ // Possible within the same iteration. Dependencies with smaller scatter
+ // value are carried by this loop and therefore have been resolved by the
+ // in-order execution if the loop iteration. A dependency with small scatter
+ // value would be a dependency violation that we assume did not happen. {
+ // Domain[] -> Domain[] }
+ isl::union_map Unsequenced = PartSchedRev.apply_domain(PartSchedRev);
+
+ // Actual dependencies within the same iteration.
+ // { DefDomain[] -> UseDomain[] }
+ isl::union_map RemDeps = Deps.intersect(Unsequenced);
+
+ return getBase().visitBand(Band, RemDeps);
+ }
+
+ isl::schedule visitSequence(isl::schedule_node_sequence Sequence,
+ isl::union_map Deps) {
+ int NumChildren = isl_schedule_node_n_children(Sequence.get());
+
+ // List of fusion candidates. The first element is the fusion candidate, the
+ // second is candidate's ancestor that is the sequence's direct child. It is
+ // preferable to use the direct child if not if its non-direct children is
+ // fused to preserve its structure such as mark nodes.
+ SmallVector<std::pair<isl::schedule_node, isl::schedule_node>> Bands;
+ for (auto i : seq<int>(0, NumChildren)) {
+ isl::schedule_node Child = Sequence.child(i);
+ collectPotentiallyFusableBands(Child, Bands, Child);
+ }
+
+ // Direct children that had at least one of its decendants fused.
+ SmallDenseSet<isl_schedule_node *, 4> ChangedDirectChildren;
+
+ // Fuse neigboring bands until reaching the end of candidates.
+ int i = 0;
+ while (i + 1 < (int)Bands.size()) {
+ isl::schedule Fused =
+ tryGreedyFuse(Bands[i].first, Bands[i + 1].first, Deps);
+ if (Fused.is_null()) {
+ // Cannot merge this node with the next; look at next pair.
+ i += 1;
+ continue;
+ }
+
+ // Mark the direct children as (partially) fused.
+ if (!Bands[i].second.is_null())
+ ChangedDirectChildren.insert(Bands[i].second.get());
+ if (!Bands[i + 1].second.is_null())
+ ChangedDirectChildren.insert(Bands[i + 1].second.get());
+
+ // Collapse the neigbros to a single new candidate that could be fused
+ // with the next candidate.
+ Bands[i] = {Fused.get_root(), {}};
+ Bands.erase(Bands.begin() + i + 1);
+
+ AnyChange = true;
+ }
+
+ // By construction equal if done with collectPotentiallyFusableBands's
+ // output.
+ SmallVector<isl::union_set> SubDomains;
+ SubDomains.reserve(NumChildren);
+ for (int i = 0; i < NumChildren; i += 1)
+ SubDomains.push_back(Sequence.child(i).domain());
+ auto SubRemainingDeps = remainigDepsFromSequence(SubDomains, Deps);
+
+ // We may iterate over direct children multiple times, be sure to add each
+ // at most once.
+ SmallDenseSet<isl_schedule_node *, 4> AlreadyAdded;
+
+ isl::schedule Result;
+ for (auto &P : Bands) {
+ isl::schedule_node MaybeFused = P.first;
+ isl::schedule_node DirectChild = P.second;
+
+ // If not modified, use the direct child.
+ if (!DirectChild.is_null() &&
+ !ChangedDirectChildren.count(DirectChild.get())) {
+ if (AlreadyAdded.count(DirectChild.get()))
+ continue;
+ AlreadyAdded.insert(DirectChild.get());
+ MaybeFused = DirectChild;
+ } else {
+ assert(AnyChange &&
+ "Need changed flag for be consistent with actual change");
+ }
+
+ // Top-down recursion: If the outermost loop has been fused, their nested
+ // bands might be fusable now as well.
+ isl::schedule InnerFused = visit(MaybeFused, SubRemainingDeps);
+
+ // Reconstruct the sequence, with some of the children fused.
+ if (Result.is_null())
+ Result = InnerFused;
+ else
+ Result = Result.sequence(InnerFused);
+ }
+
+ return Result;
+ }
+};
+
+} // namespace
+
+bool polly::isBandMark(const isl::schedule_node &Node) {
+ return isMark(Node) &&
+ isLoopAttr(Node.as<isl::schedule_node_mark>().get_id());
+}
+
+BandAttr *polly::getBandAttr(isl::schedule_node MarkOrBand) {
+ MarkOrBand = moveToBandMark(MarkOrBand);
+ if (!isMark(MarkOrBand))
+ return nullptr;
+
+ return getLoopAttr(MarkOrBand.as<isl::schedule_node_mark>().get_id());
+}
+
+isl::schedule polly::hoistExtensionNodes(isl::schedule Sched) {
+ // If there is no extension node in the first place, return the original
+ // schedule tree.
+ if (!containsExtensionNode(Sched))
+ return Sched;
+
+ // Build options can anchor schedule nodes, such that the schedule tree cannot
+ // be modified anymore. Therefore, apply build options after the tree has been
+ // created.
+ CollectASTBuildOptions Collector;
+ Collector.visit(Sched);
+
+ // Rewrite the schedule tree without extension nodes.
+ ExtensionNodeRewriter Rewriter;
+ isl::schedule NewSched = Rewriter.visitSchedule(Sched);
+
+ // Reapply the AST build options. The rewriter must not change the iteration
+ // order of bands. Any other node type is ignored.
+ ApplyASTBuildOptions Applicator(Collector.ASTBuildOptions);
+ NewSched = Applicator.visitSchedule(NewSched);
+
+ return NewSched;
+}
+
+isl::schedule polly::applyFullUnroll(isl::schedule_node BandToUnroll) {
+ isl::ctx Ctx = BandToUnroll.ctx();
+
+ // Remove the loop's mark, the loop will disappear anyway.
+ BandToUnroll = removeMark(BandToUnroll);
+ assert(isBandWithSingleLoop(BandToUnroll));
+
+ isl::multi_union_pw_aff PartialSched = isl::manage(
+ isl_schedule_node_band_get_partial_schedule(BandToUnroll.get()));
+ assert(unsignedFromIslSize(PartialSched.dim(isl::dim::out)) == 1u &&
+ "Can only unroll a single dimension");
+ isl::union_pw_aff PartialSchedUAff = PartialSched.at(0);
+
+ isl::union_set Domain = BandToUnroll.get_domain();
+ PartialSchedUAff = PartialSchedUAff.intersect_domain(Domain);
+ isl::union_map PartialSchedUMap =
+ isl::union_map::from(isl::union_pw_multi_aff(PartialSchedUAff));
+
+ // Enumerator only the scatter elements.
+ isl::union_set ScatterList = PartialSchedUMap.range();
+
+ // Enumerate all loop iterations.
+ // TODO: Diagnose if not enumerable or depends on a parameter.
+ SmallVector<isl::point, 16> Elts;
+ ScatterList.foreach_point([&Elts](isl::point P) -> isl::stat {
+ Elts.push_back(P);
+ return isl::stat::ok();
+ });
+
+ // Don't assume that foreach_point returns in execution order.
+ llvm::sort(Elts, [](isl::point P1, isl::point P2) -> bool {
+ isl::val C1 = P1.get_coordinate_val(isl::dim::set, 0);
+ isl::val C2 = P2.get_coordinate_val(isl::dim::set, 0);
+ return C1.lt(C2);
+ });
+
+ // Convert the points to a sequence of filters.
+ isl::union_set_list List = isl::union_set_list(Ctx, Elts.size());
+ for (isl::point P : Elts) {
+ // Determine the domains that map this scatter element.
+ isl::union_set DomainFilter = PartialSchedUMap.intersect_range(P).domain();
+
+ List = List.add(DomainFilter);
+ }
+
+ // Replace original band with unrolled sequence.
+ isl::schedule_node Body =
+ isl::manage(isl_schedule_node_delete(BandToUnroll.release()));
+ Body = Body.insert_sequence(List);
+ return Body.get_schedule();
+}
+
+isl::schedule polly::applyPartialUnroll(isl::schedule_node BandToUnroll,
+ int Factor) {
+ assert(Factor > 0 && "Positive unroll factor required");
+ isl::ctx Ctx = BandToUnroll.ctx();
+
+ // Remove the mark, save the attribute for later use.
+ BandAttr *Attr;
+ BandToUnroll = removeMark(BandToUnroll, Attr);
+ assert(isBandWithSingleLoop(BandToUnroll));
+
+ isl::multi_union_pw_aff PartialSched = isl::manage(
+ isl_schedule_node_band_get_partial_schedule(BandToUnroll.get()));
+
+ // { Stmt[] -> [x] }
+ isl::union_pw_aff PartialSchedUAff = PartialSched.at(0);
+
+ // Here we assume the schedule stride is one and starts with 0, which is not
+ // necessarily the case.
+ isl::union_pw_aff StridedPartialSchedUAff =
+ isl::union_pw_aff::empty(PartialSchedUAff.get_space());
+ isl::val ValFactor{Ctx, Factor};
+ PartialSchedUAff.foreach_pw_aff([&StridedPartialSchedUAff,
+ &ValFactor](isl::pw_aff PwAff) -> isl::stat {
+ isl::space Space = PwAff.get_space();
+ isl::set Universe = isl::set::universe(Space.domain());
+ isl::pw_aff AffFactor{Universe, ValFactor};
+ isl::pw_aff DivSchedAff = PwAff.div(AffFactor).floor().mul(AffFactor);
+ StridedPartialSchedUAff = StridedPartialSchedUAff.union_add(DivSchedAff);
+ return isl::stat::ok();
+ });
+
+ isl::union_set_list List = isl::union_set_list(Ctx, Factor);
+ for (auto i : seq<int>(0, Factor)) {
+ // { Stmt[] -> [x] }
+ isl::union_map UMap =
+ isl::union_map::from(isl::union_pw_multi_aff(PartialSchedUAff));
+
+ // { [x] }
+ isl::basic_set Divisible = isDivisibleBySet(Ctx, Factor, i);
+
+ // { Stmt[] }
+ isl::union_set UnrolledDomain = UMap.intersect_range(Divisible).domain();
+
+ List = List.add(UnrolledDomain);
+ }
+
+ isl::schedule_node Body =
+ isl::manage(isl_schedule_node_delete(BandToUnroll.copy()));
+ Body = Body.insert_sequence(List);
+ isl::schedule_node NewLoop =
+ Body.insert_partial_schedule(StridedPartialSchedUAff);
+
+ MDNode *FollowupMD = nullptr;
+ if (Attr && Attr->Metadata)
+ FollowupMD =
+ findOptionalNodeOperand(Attr->Metadata, LLVMLoopUnrollFollowupUnrolled);
+
+ isl::id NewBandId = createGeneratedLoopAttr(Ctx, FollowupMD);
+ if (!NewBandId.is_null())
+ NewLoop = insertMark(NewLoop, NewBandId);
+
+ return NewLoop.get_schedule();
+}
+
+isl::set polly::getPartialTilePrefixes(isl::set ScheduleRange,
+ int VectorWidth) {
+ unsigned Dims = unsignedFromIslSize(ScheduleRange.tuple_dim());
+ assert(Dims >= 1);
+ isl::set LoopPrefixes =
+ ScheduleRange.drop_constraints_involving_dims(isl::dim::set, Dims - 1, 1);
+ auto ExtentPrefixes = addExtentConstraints(LoopPrefixes, VectorWidth);
+ isl::set BadPrefixes = ExtentPrefixes.subtract(ScheduleRange);
+ BadPrefixes = BadPrefixes.project_out(isl::dim::set, Dims - 1, 1);
+ LoopPrefixes = LoopPrefixes.project_out(isl::dim::set, Dims - 1, 1);
+ return LoopPrefixes.subtract(BadPrefixes);
+}
+
+isl::union_set polly::getIsolateOptions(isl::set IsolateDomain,
+ unsigned OutDimsNum) {
+ unsigned Dims = unsignedFromIslSize(IsolateDomain.tuple_dim());
+ assert(OutDimsNum <= Dims &&
+ "The isl::set IsolateDomain is used to describe the range of schedule "
+ "dimensions values, which should be isolated. Consequently, the "
+ "number of its dimensions should be greater than or equal to the "
+ "number of the schedule dimensions.");
+ isl::map IsolateRelation = isl::map::from_domain(IsolateDomain);
+ IsolateRelation = IsolateRelation.move_dims(isl::dim::out, 0, isl::dim::in,
+ Dims - OutDimsNum, OutDimsNum);
+ isl::set IsolateOption = IsolateRelation.wrap();
+ isl::id Id = isl::id::alloc(IsolateOption.ctx(), "isolate", nullptr);
+ IsolateOption = IsolateOption.set_tuple_id(Id);
+ return isl::union_set(IsolateOption);
+}
+
+isl::union_set polly::getDimOptions(isl::ctx Ctx, const char *Option) {
+ isl::space Space(Ctx, 0, 1);
+ auto DimOption = isl::set::universe(Space);
+ auto Id = isl::id::alloc(Ctx, Option, nullptr);
+ DimOption = DimOption.set_tuple_id(Id);
+ return isl::union_set(DimOption);
+}
+
+isl::schedule_node polly::tileNode(isl::schedule_node Node,
+ const char *Identifier,
+ ArrayRef<int> TileSizes,
+ int DefaultTileSize) {
+ auto Space = isl::manage(isl_schedule_node_band_get_space(Node.get()));
+ auto Dims = Space.dim(isl::dim::set);
+ auto Sizes = isl::multi_val::zero(Space);
+ std::string IdentifierString(Identifier);
+ for (unsigned i : rangeIslSize(0, Dims)) {
+ unsigned tileSize = i < TileSizes.size() ? TileSizes[i] : DefaultTileSize;
+ Sizes = Sizes.set_val(i, isl::val(Node.ctx(), tileSize));
+ }
+ auto TileLoopMarkerStr = IdentifierString + " - Tiles";
+ auto TileLoopMarker = isl::id::alloc(Node.ctx(), TileLoopMarkerStr, nullptr);
+ Node = Node.insert_mark(TileLoopMarker);
+ Node = Node.child(0);
+ Node =
+ isl::manage(isl_schedule_node_band_tile(Node.release(), Sizes.release()));
+ Node = Node.child(0);
+ auto PointLoopMarkerStr = IdentifierString + " - Points";
+ auto PointLoopMarker =
+ isl::id::alloc(Node.ctx(), PointLoopMarkerStr, nullptr);
+ Node = Node.insert_mark(PointLoopMarker);
+ return Node.child(0);
+}
+
+isl::schedule_node polly::applyRegisterTiling(isl::schedule_node Node,
+ ArrayRef<int> TileSizes,
+ int DefaultTileSize) {
+ Node = tileNode(Node, "Register tiling", TileSizes, DefaultTileSize);
+ auto Ctx = Node.ctx();
+ return Node.as<isl::schedule_node_band>().set_ast_build_options(
+ isl::union_set(Ctx, "{unroll[x]}"));
+}
+
+/// Find statements and sub-loops in (possibly nested) sequences.
+static void
+collectFussionableStmts(isl::schedule_node Node,
+ SmallVectorImpl<isl::schedule_node> &ScheduleStmts) {
+ if (isBand(Node) || isLeaf(Node)) {
+ ScheduleStmts.push_back(Node);
+ return;
+ }
+
+ if (Node.has_children()) {
+ isl::schedule_node C = Node.first_child();
+ while (true) {
+ collectFussionableStmts(C, ScheduleStmts);
+ if (!C.has_next_sibling())
+ break;
+ C = C.next_sibling();
+ }
+ }
+}
+
+isl::schedule polly::applyMaxFission(isl::schedule_node BandToFission) {
+ isl::ctx Ctx = BandToFission.ctx();
+ BandToFission = removeMark(BandToFission);
+ isl::schedule_node BandBody = BandToFission.child(0);
+
+ SmallVector<isl::schedule_node> FissionableStmts;
+ collectFussionableStmts(BandBody, FissionableStmts);
+ size_t N = FissionableStmts.size();
+
+ // Collect the domain for each of the statements that will get their own loop.
+ isl::union_set_list DomList = isl::union_set_list(Ctx, N);
+ for (size_t i = 0; i < N; ++i) {
+ isl::schedule_node BodyPart = FissionableStmts[i];
+ DomList = DomList.add(BodyPart.get_domain());
+ }
+
+ // Apply the fission by copying the entire loop, but inserting a filter for
+ // the statement domains for each fissioned loop.
+ isl::schedule_node Fissioned = BandToFission.insert_sequence(DomList);
+
+ return Fissioned.get_schedule();
+}
+
+isl::schedule polly::applyGreedyFusion(isl::schedule Sched,
+ const isl::union_map &Deps) {
+ LLVM_DEBUG(dbgs() << "Greedy loop fusion\n");
+
+ GreedyFusionRewriter Rewriter;
+ isl::schedule Result = Rewriter.visit(Sched, Deps);
+ if (!Rewriter.AnyChange) {
+ LLVM_DEBUG(dbgs() << "Found nothing to fuse\n");
+ return Sched;
+ }
+
+ // GreedyFusionRewriter due to working loop-by-loop, bands with multiple loops
+ // may have been split into multiple bands.
+ return collapseBands(Result);
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/ScopInliner.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/ScopInliner.cpp
new file mode 100644
index 00000000000..c93c00f5870
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/ScopInliner.cpp
@@ -0,0 +1,127 @@
+//===---- ScopInliner.cpp - Polyhedral based inliner ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Take a SCC and:
+// 1. If it has more than one component, bail out (contains cycles)
+// 2. If it has just one component, and if the function is entirely a scop,
+// inline it.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/LinkAllPasses.h"
+#include "polly/ScopDetection.h"
+#include "llvm/Analysis/CallGraph.h"
+#include "llvm/Analysis/CallGraphSCCPass.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Transforms/IPO/AlwaysInliner.h"
+
+#define DEBUG_TYPE "polly-scop-inliner"
+
+using namespace llvm;
+using namespace polly;
+
+namespace {
+class ScopInliner : public CallGraphSCCPass {
+ using llvm::Pass::doInitialization;
+
+public:
+ static char ID;
+
+ ScopInliner() : CallGraphSCCPass(ID) {}
+
+ bool doInitialization(CallGraph &CG) override {
+ if (!polly::PollyAllowFullFunction) {
+ report_fatal_error(
+ "Aborting from ScopInliner because it only makes sense to run with "
+ "-polly-allow-full-function. "
+ "The heurtistic for ScopInliner checks that the full function is a "
+ "Scop, which happens if and only if polly-allow-full-function is "
+ " enabled. "
+ " If not, the entry block is not included in the Scop");
+ }
+ return true;
+ }
+
+ bool runOnSCC(CallGraphSCC &SCC) override {
+ // We do not try to inline non-trivial SCCs because this would lead to
+ // "infinite" inlining if we are not careful.
+ if (SCC.size() > 1)
+ return false;
+ assert(SCC.size() == 1 && "found empty SCC");
+ Function *F = (*SCC.begin())->getFunction();
+
+ // If the function is a nullptr, or the function is a declaration.
+ if (!F)
+ return false;
+ if (F->isDeclaration()) {
+ LLVM_DEBUG(dbgs() << "Skipping " << F->getName()
+ << "because it is a declaration.\n");
+ return false;
+ }
+
+ PassBuilder PB;
+ // Populate analysis managers and register Polly-specific analyses.
+ LoopAnalysisManager LAM;
+ FunctionAnalysisManager FAM;
+ CGSCCAnalysisManager CGAM;
+ ModuleAnalysisManager MAM;
+ FAM.registerPass([] { return ScopAnalysis(); });
+ PB.registerModuleAnalyses(MAM);
+ PB.registerCGSCCAnalyses(CGAM);
+ PB.registerFunctionAnalyses(FAM);
+ PB.registerLoopAnalyses(LAM);
+ PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+
+ RegionInfo &RI = FAM.getResult<RegionInfoAnalysis>(*F);
+ ScopDetection &SD = FAM.getResult<ScopAnalysis>(*F);
+
+ const bool HasScopAsTopLevelRegion =
+ SD.ValidRegions.contains(RI.getTopLevelRegion());
+
+ bool Changed = false;
+ if (HasScopAsTopLevelRegion) {
+ LLVM_DEBUG(dbgs() << "Skipping " << F->getName()
+ << " has scop as top level region");
+ F->addFnAttr(llvm::Attribute::AlwaysInline);
+
+ ModulePassManager MPM;
+ MPM.addPass(AlwaysInlinerPass());
+ Module *M = F->getParent();
+ assert(M && "Function has illegal module");
+ PreservedAnalyses PA = MPM.run(*M, MAM);
+ if (!PA.areAllPreserved())
+ Changed = true;
+ } else {
+ LLVM_DEBUG(dbgs() << F->getName()
+ << " does NOT have scop as top level region\n");
+ }
+
+ return Changed;
+ };
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ CallGraphSCCPass::getAnalysisUsage(AU);
+ }
+};
+} // namespace
+char ScopInliner::ID;
+
+Pass *polly::createScopInlinerPass() {
+ ScopInliner *pass = new ScopInliner();
+ return pass;
+}
+
+INITIALIZE_PASS_BEGIN(
+ ScopInliner, "polly-scop-inliner",
+ "inline functions based on how much of the function is a scop.", false,
+ false)
+INITIALIZE_PASS_END(
+ ScopInliner, "polly-scop-inliner",
+ "inline functions based on how much of the function is a scop.", false,
+ false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/Simplify.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/Simplify.cpp
new file mode 100644
index 00000000000..2f5788becfc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/Simplify.cpp
@@ -0,0 +1,852 @@
+//===------ Simplify.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Simplify a SCoP by removing unnecessary statements and accesses.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/Simplify.h"
+#include "polly/ScopInfo.h"
+#include "polly/ScopPass.h"
+#include "polly/Support/GICHelper.h"
+#include "polly/Support/ISLOStream.h"
+#include "polly/Support/ISLTools.h"
+#include "polly/Support/VirtualInstruction.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "polly-simplify"
+
+using namespace llvm;
+using namespace polly;
+
+namespace {
+
+#define TWO_STATISTICS(VARNAME, DESC) \
+ static llvm::Statistic VARNAME[2] = { \
+ {DEBUG_TYPE, #VARNAME "0", DESC " (first)"}, \
+ {DEBUG_TYPE, #VARNAME "1", DESC " (second)"}}
+
+/// Number of max disjuncts we allow in removeOverwrites(). This is to avoid
+/// that the analysis of accesses in a statement is becoming too complex. Chosen
+/// to be relatively small because all the common cases should access only few
+/// array elements per statement.
+static unsigned const SimplifyMaxDisjuncts = 4;
+
+TWO_STATISTICS(ScopsProcessed, "Number of SCoPs processed");
+TWO_STATISTICS(ScopsModified, "Number of SCoPs simplified");
+
+TWO_STATISTICS(TotalEmptyDomainsRemoved,
+ "Number of statement with empty domains removed in any SCoP");
+TWO_STATISTICS(TotalOverwritesRemoved, "Number of removed overwritten writes");
+TWO_STATISTICS(TotalWritesCoalesced, "Number of writes coalesced with another");
+TWO_STATISTICS(TotalRedundantWritesRemoved,
+ "Number of writes of same value removed in any SCoP");
+TWO_STATISTICS(TotalEmptyPartialAccessesRemoved,
+ "Number of empty partial accesses removed");
+TWO_STATISTICS(TotalDeadAccessesRemoved, "Number of dead accesses removed");
+TWO_STATISTICS(TotalDeadInstructionsRemoved,
+ "Number of unused instructions removed");
+TWO_STATISTICS(TotalStmtsRemoved, "Number of statements removed in any SCoP");
+
+TWO_STATISTICS(NumValueWrites, "Number of scalar value writes after Simplify");
+TWO_STATISTICS(
+ NumValueWritesInLoops,
+ "Number of scalar value writes nested in affine loops after Simplify");
+TWO_STATISTICS(NumPHIWrites,
+ "Number of scalar phi writes after the first simplification");
+TWO_STATISTICS(
+ NumPHIWritesInLoops,
+ "Number of scalar phi writes nested in affine loops after Simplify");
+TWO_STATISTICS(NumSingletonWrites, "Number of singleton writes after Simplify");
+TWO_STATISTICS(
+ NumSingletonWritesInLoops,
+ "Number of singleton writes nested in affine loops after Simplify");
+
+static bool isImplicitRead(MemoryAccess *MA) {
+ return MA->isRead() && MA->isOriginalScalarKind();
+}
+
+static bool isExplicitAccess(MemoryAccess *MA) {
+ return MA->isOriginalArrayKind();
+}
+
+static bool isImplicitWrite(MemoryAccess *MA) {
+ return MA->isWrite() && MA->isOriginalScalarKind();
+}
+
+/// Like isl::union_map::unite, but may also return an underapproximated
+/// result if getting too complex.
+///
+/// This is implemented by adding disjuncts to the results until the limit is
+/// reached.
+static isl::union_map underapproximatedAddMap(isl::union_map UMap,
+ isl::map Map) {
+ if (UMap.is_null() || Map.is_null())
+ return {};
+
+ isl::map PrevMap = UMap.extract_map(Map.get_space());
+
+ // Fast path: If known that we cannot exceed the disjunct limit, just add
+ // them.
+ if (unsignedFromIslSize(PrevMap.n_basic_map()) +
+ unsignedFromIslSize(Map.n_basic_map()) <=
+ SimplifyMaxDisjuncts)
+ return UMap.unite(Map);
+
+ isl::map Result = isl::map::empty(PrevMap.get_space());
+ for (isl::basic_map BMap : PrevMap.get_basic_map_list()) {
+ if (unsignedFromIslSize(Result.n_basic_map()) > SimplifyMaxDisjuncts)
+ break;
+ Result = Result.unite(BMap);
+ }
+ for (isl::basic_map BMap : Map.get_basic_map_list()) {
+ if (unsignedFromIslSize(Result.n_basic_map()) > SimplifyMaxDisjuncts)
+ break;
+ Result = Result.unite(BMap);
+ }
+
+ isl::union_map UResult =
+ UMap.subtract(isl::map::universe(PrevMap.get_space()));
+ UResult.unite(Result);
+
+ return UResult;
+}
+
+class SimplifyImpl {
+private:
+ /// The invocation id (if there are multiple instances in the pass manager's
+ /// pipeline) to determine which statistics to update.
+ int CallNo;
+
+ /// The last/current SCoP that is/has been processed.
+ Scop *S = nullptr;
+
+ /// Number of statements with empty domains removed from the SCoP.
+ int EmptyDomainsRemoved = 0;
+
+ /// Number of writes that are overwritten anyway.
+ int OverwritesRemoved = 0;
+
+ /// Number of combined writes.
+ int WritesCoalesced = 0;
+
+ /// Number of redundant writes removed from this SCoP.
+ int RedundantWritesRemoved = 0;
+
+ /// Number of writes with empty access domain removed.
+ int EmptyPartialAccessesRemoved = 0;
+
+ /// Number of unused accesses removed from this SCoP.
+ int DeadAccessesRemoved = 0;
+
+ /// Number of unused instructions removed from this SCoP.
+ int DeadInstructionsRemoved = 0;
+
+ /// Number of unnecessary statements removed from the SCoP.
+ int StmtsRemoved = 0;
+
+ /// Remove statements that are never executed due to their domains being
+ /// empty.
+ ///
+ /// In contrast to Scop::simplifySCoP, this removes based on the SCoP's
+ /// effective domain, i.e. including the SCoP's context as used by some other
+ /// simplification methods in this pass. This is necessary because the
+ /// analysis on empty domains is unreliable, e.g. remove a scalar value
+ /// definition MemoryAccesses, but not its use.
+ void removeEmptyDomainStmts();
+
+ /// Remove writes that are overwritten unconditionally later in the same
+ /// statement.
+ ///
+ /// There must be no read of the same value between the write (that is to be
+ /// removed) and the overwrite.
+ void removeOverwrites();
+
+ /// Combine writes that write the same value if possible.
+ ///
+ /// This function is able to combine:
+ /// - Partial writes with disjoint domain.
+ /// - Writes that write to the same array element.
+ ///
+ /// In all cases, both writes must write the same values.
+ void coalesceWrites();
+
+ /// Remove writes that just write the same value already stored in the
+ /// element.
+ void removeRedundantWrites();
+
+ /// Remove statements without side effects.
+ void removeUnnecessaryStmts();
+
+ /// Remove accesses that have an empty domain.
+ void removeEmptyPartialAccesses();
+
+ /// Mark all reachable instructions and access, and sweep those that are not
+ /// reachable.
+ void markAndSweep(LoopInfo *LI);
+
+ /// Print simplification statistics to @p OS.
+ void printStatistics(llvm::raw_ostream &OS, int Indent = 0) const;
+
+ /// Print the current state of all MemoryAccesses to @p OS.
+ void printAccesses(llvm::raw_ostream &OS, int Indent = 0) const;
+
+public:
+ explicit SimplifyImpl(int CallNo = 0) : CallNo(CallNo) {}
+
+ void run(Scop &S, LoopInfo *LI);
+
+ void printScop(raw_ostream &OS, Scop &S) const;
+
+ /// Return whether at least one simplification has been applied.
+ bool isModified() const;
+};
+
+/// Return whether at least one simplification has been applied.
+bool SimplifyImpl::isModified() const {
+ return EmptyDomainsRemoved > 0 || OverwritesRemoved > 0 ||
+ WritesCoalesced > 0 || RedundantWritesRemoved > 0 ||
+ EmptyPartialAccessesRemoved > 0 || DeadAccessesRemoved > 0 ||
+ DeadInstructionsRemoved > 0 || StmtsRemoved > 0;
+}
+
+/// Remove statements that are never executed due to their domains being
+/// empty.
+///
+/// In contrast to Scop::simplifySCoP, this removes based on the SCoP's
+/// effective domain, i.e. including the SCoP's context as used by some other
+/// simplification methods in this pass. This is necessary because the
+/// analysis on empty domains is unreliable, e.g. remove a scalar value
+/// definition MemoryAccesses, but not its use.
+void SimplifyImpl::removeEmptyDomainStmts() {
+ size_t NumStmtsBefore = S->getSize();
+
+ S->removeStmts([](ScopStmt &Stmt) -> bool {
+ auto EffectiveDomain =
+ Stmt.getDomain().intersect_params(Stmt.getParent()->getContext());
+ return EffectiveDomain.is_empty();
+ });
+
+ assert(NumStmtsBefore >= S->getSize());
+ EmptyDomainsRemoved = NumStmtsBefore - S->getSize();
+ LLVM_DEBUG(dbgs() << "Removed " << EmptyDomainsRemoved << " (of "
+ << NumStmtsBefore << ") statements with empty domains \n");
+ TotalEmptyDomainsRemoved[CallNo] += EmptyDomainsRemoved;
+}
+
+/// Remove writes that are overwritten unconditionally later in the same
+/// statement.
+///
+/// There must be no read of the same value between the write (that is to be
+/// removed) and the overwrite.
+void SimplifyImpl::removeOverwrites() {
+ for (auto &Stmt : *S) {
+ isl::set Domain = Stmt.getDomain();
+ isl::union_map WillBeOverwritten = isl::union_map::empty(S->getIslCtx());
+
+ SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
+
+ // Iterate in reverse order, so the overwrite comes before the write that
+ // is to be removed.
+ for (auto *MA : reverse(Accesses)) {
+
+ // In region statements, the explicit accesses can be in blocks that are
+ // can be executed in any order. We therefore process only the implicit
+ // writes and stop after that.
+ if (Stmt.isRegionStmt() && isExplicitAccess(MA))
+ break;
+
+ auto AccRel = MA->getAccessRelation();
+ AccRel = AccRel.intersect_domain(Domain);
+ AccRel = AccRel.intersect_params(S->getContext());
+
+ // If a value is read in-between, do not consider it as overwritten.
+ if (MA->isRead()) {
+ // Invalidate all overwrites for the array it accesses to avoid too
+ // complex isl sets.
+ isl::map AccRelUniv = isl::map::universe(AccRel.get_space());
+ WillBeOverwritten = WillBeOverwritten.subtract(AccRelUniv);
+ continue;
+ }
+
+ // If all of a write's elements are overwritten, remove it.
+ isl::union_map AccRelUnion = AccRel;
+ if (AccRelUnion.is_subset(WillBeOverwritten)) {
+ LLVM_DEBUG(dbgs() << "Removing " << MA
+ << " which will be overwritten anyway\n");
+
+ Stmt.removeSingleMemoryAccess(MA);
+ OverwritesRemoved++;
+ TotalOverwritesRemoved[CallNo]++;
+ }
+
+ // Unconditional writes overwrite other values.
+ if (MA->isMustWrite()) {
+ // Avoid too complex isl sets. If necessary, throw away some of the
+ // knowledge.
+ WillBeOverwritten = underapproximatedAddMap(WillBeOverwritten, AccRel);
+ }
+ }
+ }
+}
+
+/// Combine writes that write the same value if possible.
+///
+/// This function is able to combine:
+/// - Partial writes with disjoint domain.
+/// - Writes that write to the same array element.
+///
+/// In all cases, both writes must write the same values.
+void SimplifyImpl::coalesceWrites() {
+ for (auto &Stmt : *S) {
+ isl::set Domain = Stmt.getDomain().intersect_params(S->getContext());
+
+ // We let isl do the lookup for the same-value condition. For this, we
+ // wrap llvm::Value into an isl::set such that isl can do the lookup in
+ // its hashtable implementation. llvm::Values are only compared within a
+ // ScopStmt, so the map can be local to this scope. TODO: Refactor with
+ // ZoneAlgorithm::makeValueSet()
+ SmallDenseMap<Value *, isl::set> ValueSets;
+ auto makeValueSet = [&ValueSets, this](Value *V) -> isl::set {
+ assert(V);
+ isl::set &Result = ValueSets[V];
+ if (Result.is_null()) {
+ isl::ctx Ctx = S->getIslCtx();
+ std::string Name = getIslCompatibleName(
+ "Val", V, ValueSets.size() - 1, std::string(), UseInstructionNames);
+ isl::id Id = isl::id::alloc(Ctx, Name, V);
+ Result = isl::set::universe(
+ isl::space(Ctx, 0, 0).set_tuple_id(isl::dim::set, Id));
+ }
+ return Result;
+ };
+
+ // List of all eligible (for coalescing) writes of the future.
+ // { [Domain[] -> Element[]] -> [Value[] -> MemoryAccess[]] }
+ isl::union_map FutureWrites = isl::union_map::empty(S->getIslCtx());
+
+ // Iterate over accesses from the last to the first.
+ SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
+ for (MemoryAccess *MA : reverse(Accesses)) {
+ // In region statements, the explicit accesses can be in blocks that can
+ // be executed in any order. We therefore process only the implicit
+ // writes and stop after that.
+ if (Stmt.isRegionStmt() && isExplicitAccess(MA))
+ break;
+
+ // { Domain[] -> Element[] }
+ isl::map AccRel = MA->getLatestAccessRelation().intersect_domain(Domain);
+
+ // { [Domain[] -> Element[]] }
+ isl::set AccRelWrapped = AccRel.wrap();
+
+ // { Value[] }
+ isl::set ValSet;
+
+ if (MA->isMustWrite() && (MA->isOriginalScalarKind() ||
+ isa<StoreInst>(MA->getAccessInstruction()))) {
+ // Normally, tryGetValueStored() should be used to determine which
+ // element is written, but it can return nullptr; For PHI accesses,
+ // getAccessValue() returns the PHI instead of the PHI's incoming
+ // value. In this case, where we only compare values of a single
+ // statement, this is fine, because within a statement, a PHI in a
+ // successor block has always the same value as the incoming write. We
+ // still preferably use the incoming value directly so we also catch
+ // direct uses of that.
+ Value *StoredVal = MA->tryGetValueStored();
+ if (!StoredVal)
+ StoredVal = MA->getAccessValue();
+ ValSet = makeValueSet(StoredVal);
+
+ // { Domain[] }
+ isl::set AccDomain = AccRel.domain();
+
+ // Parts of the statement's domain that is not written by this access.
+ isl::set UndefDomain = Domain.subtract(AccDomain);
+
+ // { Element[] }
+ isl::set ElementUniverse =
+ isl::set::universe(AccRel.get_space().range());
+
+ // { Domain[] -> Element[] }
+ isl::map UndefAnything =
+ isl::map::from_domain_and_range(UndefDomain, ElementUniverse);
+
+ // We are looking a compatible write access. The other write can
+ // access these elements...
+ isl::map AllowedAccesses = AccRel.unite(UndefAnything);
+
+ // ... and must write the same value.
+ // { [Domain[] -> Element[]] -> Value[] }
+ isl::map Filter =
+ isl::map::from_domain_and_range(AllowedAccesses.wrap(), ValSet);
+
+ // Lookup future write that fulfills these conditions.
+ // { [[Domain[] -> Element[]] -> Value[]] -> MemoryAccess[] }
+ isl::union_map Filtered =
+ FutureWrites.uncurry().intersect_domain(Filter.wrap());
+
+ // Iterate through the candidates.
+ for (isl::map Map : Filtered.get_map_list()) {
+ MemoryAccess *OtherMA = (MemoryAccess *)Map.get_space()
+ .get_tuple_id(isl::dim::out)
+ .get_user();
+
+ isl::map OtherAccRel =
+ OtherMA->getLatestAccessRelation().intersect_domain(Domain);
+
+ // The filter only guaranteed that some of OtherMA's accessed
+ // elements are allowed. Verify that it only accesses allowed
+ // elements. Otherwise, continue with the next candidate.
+ if (!OtherAccRel.is_subset(AllowedAccesses).is_true())
+ continue;
+
+ // The combined access relation.
+ // { Domain[] -> Element[] }
+ isl::map NewAccRel = AccRel.unite(OtherAccRel);
+ simplify(NewAccRel);
+
+ // Carry out the coalescing.
+ Stmt.removeSingleMemoryAccess(MA);
+ OtherMA->setNewAccessRelation(NewAccRel);
+
+ // We removed MA, OtherMA takes its role.
+ MA = OtherMA;
+
+ TotalWritesCoalesced[CallNo]++;
+ WritesCoalesced++;
+
+ // Don't look for more candidates.
+ break;
+ }
+ }
+
+ // Two writes cannot be coalesced if there is another access (to some of
+ // the written elements) between them. Remove all visited write accesses
+ // from the list of eligible writes. Don't just remove the accessed
+ // elements, but any MemoryAccess that touches any of the invalidated
+ // elements.
+ SmallPtrSet<MemoryAccess *, 2> TouchedAccesses;
+ for (isl::map Map :
+ FutureWrites.intersect_domain(AccRelWrapped).get_map_list()) {
+ MemoryAccess *MA = (MemoryAccess *)Map.get_space()
+ .range()
+ .unwrap()
+ .get_tuple_id(isl::dim::out)
+ .get_user();
+ TouchedAccesses.insert(MA);
+ }
+ isl::union_map NewFutureWrites =
+ isl::union_map::empty(FutureWrites.ctx());
+ for (isl::map FutureWrite : FutureWrites.get_map_list()) {
+ MemoryAccess *MA = (MemoryAccess *)FutureWrite.get_space()
+ .range()
+ .unwrap()
+ .get_tuple_id(isl::dim::out)
+ .get_user();
+ if (!TouchedAccesses.count(MA))
+ NewFutureWrites = NewFutureWrites.unite(FutureWrite);
+ }
+ FutureWrites = NewFutureWrites;
+
+ if (MA->isMustWrite() && !ValSet.is_null()) {
+ // { MemoryAccess[] }
+ auto AccSet =
+ isl::set::universe(isl::space(S->getIslCtx(), 0, 0)
+ .set_tuple_id(isl::dim::set, MA->getId()));
+
+ // { Val[] -> MemoryAccess[] }
+ isl::map ValAccSet = isl::map::from_domain_and_range(ValSet, AccSet);
+
+ // { [Domain[] -> Element[]] -> [Value[] -> MemoryAccess[]] }
+ isl::map AccRelValAcc =
+ isl::map::from_domain_and_range(AccRelWrapped, ValAccSet.wrap());
+ FutureWrites = FutureWrites.unite(AccRelValAcc);
+ }
+ }
+ }
+}
+
+/// Remove writes that just write the same value already stored in the
+/// element.
+void SimplifyImpl::removeRedundantWrites() {
+ for (auto &Stmt : *S) {
+ SmallDenseMap<Value *, isl::set> ValueSets;
+ auto makeValueSet = [&ValueSets, this](Value *V) -> isl::set {
+ assert(V);
+ isl::set &Result = ValueSets[V];
+ if (Result.is_null()) {
+ isl_ctx *Ctx = S->getIslCtx().get();
+ std::string Name = getIslCompatibleName(
+ "Val", V, ValueSets.size() - 1, std::string(), UseInstructionNames);
+ isl::id Id = isl::manage(isl_id_alloc(Ctx, Name.c_str(), V));
+ Result = isl::set::universe(
+ isl::space(Ctx, 0, 0).set_tuple_id(isl::dim::set, Id));
+ }
+ return Result;
+ };
+
+ isl::set Domain = Stmt.getDomain();
+ Domain = Domain.intersect_params(S->getContext());
+
+ // List of element reads that still have the same value while iterating
+ // through the MemoryAccesses.
+ // { [Domain[] -> Element[]] -> Val[] }
+ isl::union_map Known = isl::union_map::empty(S->getIslCtx());
+
+ SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
+ for (MemoryAccess *MA : Accesses) {
+ // Is the memory access in a defined order relative to the other
+ // accesses? In region statements, only the first and the last accesses
+ // have defined order. Execution of those in the middle may depend on
+ // runtime conditions an therefore cannot be modified.
+ bool IsOrdered =
+ Stmt.isBlockStmt() || MA->isOriginalScalarKind() ||
+ (!S->getBoxedLoops().size() && MA->getAccessInstruction() &&
+ Stmt.getEntryBlock() == MA->getAccessInstruction()->getParent());
+
+ isl::map AccRel = MA->getAccessRelation();
+ AccRel = AccRel.intersect_domain(Domain);
+ isl::set AccRelWrapped = AccRel.wrap();
+
+ // Determine whether a write is redundant (stores only values that are
+ // already present in the written array elements) and remove it if this
+ // is the case.
+ if (IsOrdered && MA->isMustWrite() &&
+ (isa<StoreInst>(MA->getAccessInstruction()) ||
+ MA->isOriginalScalarKind())) {
+ Value *StoredVal = MA->tryGetValueStored();
+ if (!StoredVal)
+ StoredVal = MA->getAccessValue();
+
+ if (StoredVal) {
+ // Lookup in the set of known values.
+ isl::map AccRelStoredVal = isl::map::from_domain_and_range(
+ AccRelWrapped, makeValueSet(StoredVal));
+ if (isl::union_map(AccRelStoredVal).is_subset(Known)) {
+ LLVM_DEBUG(dbgs() << "Cleanup of " << MA << ":\n");
+ LLVM_DEBUG(dbgs() << " Scalar: " << *StoredVal << "\n");
+ LLVM_DEBUG(dbgs() << " AccRel: " << AccRel << "\n");
+
+ Stmt.removeSingleMemoryAccess(MA);
+
+ RedundantWritesRemoved++;
+ TotalRedundantWritesRemoved[CallNo]++;
+ }
+ }
+ }
+
+ // Update the know values set.
+ if (MA->isRead()) {
+ // Loaded values are the currently known values of the array element
+ // it was loaded from.
+ Value *LoadedVal = MA->getAccessValue();
+ if (LoadedVal && IsOrdered) {
+ isl::map AccRelVal = isl::map::from_domain_and_range(
+ AccRelWrapped, makeValueSet(LoadedVal));
+
+ Known = Known.unite(AccRelVal);
+ }
+ } else if (MA->isWrite()) {
+ // Remove (possibly) overwritten values from the known elements set.
+ // We remove all elements of the accessed array to avoid too complex
+ // isl sets.
+ isl::set AccRelUniv = isl::set::universe(AccRelWrapped.get_space());
+ Known = Known.subtract_domain(AccRelUniv);
+
+ // At this point, we could add the written value of must-writes.
+ // However, writing same values is already handled by
+ // coalesceWrites().
+ }
+ }
+ }
+}
+
+/// Remove statements without side effects.
+void SimplifyImpl::removeUnnecessaryStmts() {
+ auto NumStmtsBefore = S->getSize();
+ S->simplifySCoP(true);
+ assert(NumStmtsBefore >= S->getSize());
+ StmtsRemoved = NumStmtsBefore - S->getSize();
+ LLVM_DEBUG(dbgs() << "Removed " << StmtsRemoved << " (of " << NumStmtsBefore
+ << ") statements\n");
+ TotalStmtsRemoved[CallNo] += StmtsRemoved;
+}
+
+/// Remove accesses that have an empty domain.
+void SimplifyImpl::removeEmptyPartialAccesses() {
+ for (ScopStmt &Stmt : *S) {
+ // Defer the actual removal to not invalidate iterators.
+ SmallVector<MemoryAccess *, 8> DeferredRemove;
+
+ for (MemoryAccess *MA : Stmt) {
+ if (!MA->isWrite())
+ continue;
+
+ isl::map AccRel = MA->getAccessRelation();
+ if (!AccRel.is_empty().is_true())
+ continue;
+
+ LLVM_DEBUG(
+ dbgs() << "Removing " << MA
+ << " because it's a partial access that never occurs\n");
+ DeferredRemove.push_back(MA);
+ }
+
+ for (MemoryAccess *MA : DeferredRemove) {
+ Stmt.removeSingleMemoryAccess(MA);
+ EmptyPartialAccessesRemoved++;
+ TotalEmptyPartialAccessesRemoved[CallNo]++;
+ }
+ }
+}
+
+/// Mark all reachable instructions and access, and sweep those that are not
+/// reachable.
+void SimplifyImpl::markAndSweep(LoopInfo *LI) {
+ DenseSet<MemoryAccess *> UsedMA;
+ DenseSet<VirtualInstruction> UsedInsts;
+
+ // Get all reachable instructions and accesses.
+ markReachable(S, LI, UsedInsts, UsedMA);
+
+ // Remove all non-reachable accesses.
+ // We need get all MemoryAccesses first, in order to not invalidate the
+ // iterators when removing them.
+ SmallVector<MemoryAccess *, 64> AllMAs;
+ for (ScopStmt &Stmt : *S)
+ AllMAs.append(Stmt.begin(), Stmt.end());
+
+ for (MemoryAccess *MA : AllMAs) {
+ if (UsedMA.count(MA))
+ continue;
+ LLVM_DEBUG(dbgs() << "Removing " << MA
+ << " because its value is not used\n");
+ ScopStmt *Stmt = MA->getStatement();
+ Stmt->removeSingleMemoryAccess(MA);
+
+ DeadAccessesRemoved++;
+ TotalDeadAccessesRemoved[CallNo]++;
+ }
+
+ // Remove all non-reachable instructions.
+ for (ScopStmt &Stmt : *S) {
+ // Note that for region statements, we can only remove the non-terminator
+ // instructions of the entry block. All other instructions are not in the
+ // instructions list, but implicitly always part of the statement.
+
+ SmallVector<Instruction *, 32> AllInsts(Stmt.insts_begin(),
+ Stmt.insts_end());
+ SmallVector<Instruction *, 32> RemainInsts;
+
+ for (Instruction *Inst : AllInsts) {
+ auto It = UsedInsts.find({&Stmt, Inst});
+ if (It == UsedInsts.end()) {
+ LLVM_DEBUG(dbgs() << "Removing "; Inst->print(dbgs());
+ dbgs() << " because it is not used\n");
+ DeadInstructionsRemoved++;
+ TotalDeadInstructionsRemoved[CallNo]++;
+ continue;
+ }
+
+ RemainInsts.push_back(Inst);
+
+ // If instructions appear multiple times, keep only the first.
+ UsedInsts.erase(It);
+ }
+
+ // Set the new instruction list to be only those we did not remove.
+ Stmt.setInstructions(RemainInsts);
+ }
+}
+
+/// Print simplification statistics to @p OS.
+void SimplifyImpl::printStatistics(llvm::raw_ostream &OS, int Indent) const {
+ OS.indent(Indent) << "Statistics {\n";
+ OS.indent(Indent + 4) << "Empty domains removed: " << EmptyDomainsRemoved
+ << '\n';
+ OS.indent(Indent + 4) << "Overwrites removed: " << OverwritesRemoved << '\n';
+ OS.indent(Indent + 4) << "Partial writes coalesced: " << WritesCoalesced
+ << "\n";
+ OS.indent(Indent + 4) << "Redundant writes removed: "
+ << RedundantWritesRemoved << "\n";
+ OS.indent(Indent + 4) << "Accesses with empty domains removed: "
+ << EmptyPartialAccessesRemoved << "\n";
+ OS.indent(Indent + 4) << "Dead accesses removed: " << DeadAccessesRemoved
+ << '\n';
+ OS.indent(Indent + 4) << "Dead instructions removed: "
+ << DeadInstructionsRemoved << '\n';
+ OS.indent(Indent + 4) << "Stmts removed: " << StmtsRemoved << "\n";
+ OS.indent(Indent) << "}\n";
+}
+
+/// Print the current state of all MemoryAccesses to @p OS.
+void SimplifyImpl::printAccesses(llvm::raw_ostream &OS, int Indent) const {
+ OS.indent(Indent) << "After accesses {\n";
+ for (auto &Stmt : *S) {
+ OS.indent(Indent + 4) << Stmt.getBaseName() << "\n";
+ for (auto *MA : Stmt)
+ MA->print(OS);
+ }
+ OS.indent(Indent) << "}\n";
+}
+
+void SimplifyImpl::run(Scop &S, LoopInfo *LI) {
+ // Must not have run before.
+ assert(!this->S);
+ assert(!isModified());
+
+ // Prepare processing of this SCoP.
+ this->S = &S;
+ ScopsProcessed[CallNo]++;
+
+ LLVM_DEBUG(dbgs() << "Removing statements that are never executed...\n");
+ removeEmptyDomainStmts();
+
+ LLVM_DEBUG(dbgs() << "Removing partial writes that never happen...\n");
+ removeEmptyPartialAccesses();
+
+ LLVM_DEBUG(dbgs() << "Removing overwrites...\n");
+ removeOverwrites();
+
+ LLVM_DEBUG(dbgs() << "Coalesce partial writes...\n");
+ coalesceWrites();
+
+ LLVM_DEBUG(dbgs() << "Removing redundant writes...\n");
+ removeRedundantWrites();
+
+ LLVM_DEBUG(dbgs() << "Cleanup unused accesses...\n");
+ markAndSweep(LI);
+
+ LLVM_DEBUG(dbgs() << "Removing statements without side effects...\n");
+ removeUnnecessaryStmts();
+
+ if (isModified())
+ ScopsModified[CallNo]++;
+ LLVM_DEBUG(dbgs() << "\nFinal Scop:\n");
+ LLVM_DEBUG(dbgs() << S);
+
+ auto ScopStats = S.getStatistics();
+ NumValueWrites[CallNo] += ScopStats.NumValueWrites;
+ NumValueWritesInLoops[CallNo] += ScopStats.NumValueWritesInLoops;
+ NumPHIWrites[CallNo] += ScopStats.NumPHIWrites;
+ NumPHIWritesInLoops[CallNo] += ScopStats.NumPHIWritesInLoops;
+ NumSingletonWrites[CallNo] += ScopStats.NumSingletonWrites;
+ NumSingletonWritesInLoops[CallNo] += ScopStats.NumSingletonWritesInLoops;
+}
+
+void SimplifyImpl::printScop(raw_ostream &OS, Scop &S) const {
+ assert(&S == this->S &&
+ "Can only print analysis for the last processed SCoP");
+ printStatistics(OS);
+
+ if (!isModified()) {
+ OS << "SCoP could not be simplified\n";
+ return;
+ }
+ printAccesses(OS);
+}
+
+class SimplifyWrapperPass : public ScopPass {
+public:
+ static char ID;
+ int CallNo;
+ Optional<SimplifyImpl> Impl;
+
+ explicit SimplifyWrapperPass(int CallNo = 0) : ScopPass(ID), CallNo(CallNo) {}
+
+ virtual void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.addRequiredTransitive<ScopInfoRegionPass>();
+ AU.addRequired<LoopInfoWrapperPass>();
+ AU.setPreservesAll();
+ }
+
+ virtual bool runOnScop(Scop &S) override {
+ LoopInfo *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+
+ Impl.emplace(CallNo);
+ Impl->run(S, LI);
+
+ return false;
+ }
+
+ virtual void printScop(raw_ostream &OS, Scop &S) const override {
+ if (Impl)
+ Impl->printScop(OS, S);
+ }
+
+ virtual void releaseMemory() override { Impl.reset(); }
+};
+
+char SimplifyWrapperPass::ID;
+
+static llvm::PreservedAnalyses
+runSimplifyUsingNPM(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U, int CallNo,
+ raw_ostream *OS) {
+ SimplifyImpl Impl(CallNo);
+ Impl.run(S, &SAR.LI);
+ if (OS) {
+ *OS << "Printing analysis 'Polly - Simplify' for region: '" << S.getName()
+ << "' in function '" << S.getFunction().getName() << "':\n";
+ Impl.printScop(*OS, S);
+ }
+
+ if (!Impl.isModified())
+ return llvm::PreservedAnalyses::all();
+
+ PreservedAnalyses PA;
+ PA.preserveSet<AllAnalysesOn<Module>>();
+ PA.preserveSet<AllAnalysesOn<Function>>();
+ PA.preserveSet<AllAnalysesOn<Loop>>();
+ return PA;
+}
+
+} // anonymous namespace
+
+llvm::PreservedAnalyses SimplifyPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &U) {
+ return runSimplifyUsingNPM(S, SAM, SAR, U, CallNo, nullptr);
+}
+
+llvm::PreservedAnalyses
+SimplifyPrinterPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U) {
+ return runSimplifyUsingNPM(S, SAM, SAR, U, CallNo, &OS);
+}
+
+SmallVector<MemoryAccess *, 32> polly::getAccessesInOrder(ScopStmt &Stmt) {
+ SmallVector<MemoryAccess *, 32> Accesses;
+
+ for (MemoryAccess *MemAcc : Stmt)
+ if (isImplicitRead(MemAcc))
+ Accesses.push_back(MemAcc);
+
+ for (MemoryAccess *MemAcc : Stmt)
+ if (isExplicitAccess(MemAcc))
+ Accesses.push_back(MemAcc);
+
+ for (MemoryAccess *MemAcc : Stmt)
+ if (isImplicitWrite(MemAcc))
+ Accesses.push_back(MemAcc);
+
+ return Accesses;
+}
+
+Pass *polly::createSimplifyWrapperPass(int CallNo) {
+ return new SimplifyWrapperPass(CallNo);
+}
+
+INITIALIZE_PASS_BEGIN(SimplifyWrapperPass, "polly-simplify", "Polly - Simplify",
+ false, false)
+INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
+INITIALIZE_PASS_END(SimplifyWrapperPass, "polly-simplify", "Polly - Simplify",
+ false, false)
diff --git a/contrib/libs/llvm14/tools/polly/lib/Transform/ZoneAlgo.cpp b/contrib/libs/llvm14/tools/polly/lib/Transform/ZoneAlgo.cpp
new file mode 100644
index 00000000000..4c86891d2cf
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/Transform/ZoneAlgo.cpp
@@ -0,0 +1,1172 @@
+//===------ ZoneAlgo.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Derive information about array elements between statements ("Zones").
+//
+// The algorithms here work on the scatter space - the image space of the
+// schedule returned by Scop::getSchedule(). We call an element in that space a
+// "timepoint". Timepoints are lexicographically ordered such that we can
+// defined ranges in the scatter space. We use two flavors of such ranges:
+// Timepoint sets and zones. A timepoint set is simply a subset of the scatter
+// space and is directly stored as isl_set.
+//
+// Zones are used to describe the space between timepoints as open sets, i.e.
+// they do not contain the extrema. Using isl rational sets to express these
+// would be overkill. We also cannot store them as the integer timepoints they
+// contain; the (nonempty) zone between 1 and 2 would be empty and
+// indistinguishable from e.g. the zone between 3 and 4. Also, we cannot store
+// the integer set including the extrema; the set ]1,2[ + ]3,4[ could be
+// coalesced to ]1,3[, although we defined the range [2,3] to be not in the set.
+// Instead, we store the "half-open" integer extrema, including the lower bound,
+// but excluding the upper bound. Examples:
+//
+// * The set { [i] : 1 <= i <= 3 } represents the zone ]0,3[ (which contains the
+// integer points 1 and 2, but not 0 or 3)
+//
+// * { [1] } represents the zone ]0,1[
+//
+// * { [i] : i = 1 or i = 3 } represents the zone ]0,1[ + ]2,3[
+//
+// Therefore, an integer i in the set represents the zone ]i-1,i[, i.e. strictly
+// speaking the integer points never belong to the zone. However, depending an
+// the interpretation, one might want to include them. Part of the
+// interpretation may not be known when the zone is constructed.
+//
+// Reads are assumed to always take place before writes, hence we can think of
+// reads taking place at the beginning of a timepoint and writes at the end.
+//
+// Let's assume that the zone represents the lifetime of a variable. That is,
+// the zone begins with a write that defines the value during its lifetime and
+// ends with the last read of that value. In the following we consider whether a
+// read/write at the beginning/ending of the lifetime zone should be within the
+// zone or outside of it.
+//
+// * A read at the timepoint that starts the live-range loads the previous
+// value. Hence, exclude the timepoint starting the zone.
+//
+// * A write at the timepoint that starts the live-range is not defined whether
+// it occurs before or after the write that starts the lifetime. We do not
+// allow this situation to occur. Hence, we include the timepoint starting the
+// zone to determine whether they are conflicting.
+//
+// * A read at the timepoint that ends the live-range reads the same variable.
+// We include the timepoint at the end of the zone to include that read into
+// the live-range. Doing otherwise would mean that the two reads access
+// different values, which would mean that the value they read are both alive
+// at the same time but occupy the same variable.
+//
+// * A write at the timepoint that ends the live-range starts a new live-range.
+// It must not be included in the live-range of the previous definition.
+//
+// All combinations of reads and writes at the endpoints are possible, but most
+// of the time only the write->read (for instance, a live-range from definition
+// to last use) and read->write (for instance, an unused range from last use to
+// overwrite) and combinations are interesting (half-open ranges). write->write
+// zones might be useful as well in some context to represent
+// output-dependencies.
+//
+// @see convertZoneToTimepoints
+//
+//
+// The code makes use of maps and sets in many different spaces. To not loose
+// track in which space a set or map is expected to be in, variables holding an
+// isl reference are usually annotated in the comments. They roughly follow isl
+// syntax for spaces, but only the tuples, not the dimensions. The tuples have a
+// meaning as follows:
+//
+// * Space[] - An unspecified tuple. Used for function parameters such that the
+// function caller can use it for anything they like.
+//
+// * Domain[] - A statement instance as returned by ScopStmt::getDomain()
+// isl_id_get_name: Stmt_<NameOfBasicBlock>
+// isl_id_get_user: Pointer to ScopStmt
+//
+// * Element[] - An array element as in the range part of
+// MemoryAccess::getAccessRelation()
+// isl_id_get_name: MemRef_<NameOfArrayVariable>
+// isl_id_get_user: Pointer to ScopArrayInfo
+//
+// * Scatter[] - Scatter space or space of timepoints
+// Has no tuple id
+//
+// * Zone[] - Range between timepoints as described above
+// Has no tuple id
+//
+// * ValInst[] - An llvm::Value as defined at a specific timepoint.
+//
+// A ValInst[] itself can be structured as one of:
+//
+// * [] - An unknown value.
+// Always zero dimensions
+// Has no tuple id
+//
+// * Value[] - An llvm::Value that is read-only in the SCoP, i.e. its
+// runtime content does not depend on the timepoint.
+// Always zero dimensions
+// isl_id_get_name: Val_<NameOfValue>
+// isl_id_get_user: A pointer to an llvm::Value
+//
+// * SCEV[...] - A synthesizable llvm::SCEV Expression.
+// In contrast to a Value[] is has at least one dimension per
+// SCEVAddRecExpr in the SCEV.
+//
+// * [Domain[] -> Value[]] - An llvm::Value that may change during the
+// Scop's execution.
+// The tuple itself has no id, but it wraps a map space holding a
+// statement instance which defines the llvm::Value as the map's domain
+// and llvm::Value itself as range.
+//
+// @see makeValInst()
+//
+// An annotation "{ Domain[] -> Scatter[] }" therefore means: A map from a
+// statement instance to a timepoint, aka a schedule. There is only one scatter
+// space, but most of the time multiple statements are processed in one set.
+// This is why most of the time isl_union_map has to be used.
+//
+// The basic algorithm works as follows:
+// At first we verify that the SCoP is compatible with this technique. For
+// instance, two writes cannot write to the same location at the same statement
+// instance because we cannot determine within the polyhedral model which one
+// comes first. Once this was verified, we compute zones at which an array
+// element is unused. This computation can fail if it takes too long. Then the
+// main algorithm is executed. Because every store potentially trails an unused
+// zone, we start at stores. We search for a scalar (MemoryKind::Value or
+// MemoryKind::PHI) that we can map to the array element overwritten by the
+// store, preferably one that is used by the store or at least the ScopStmt.
+// When it does not conflict with the lifetime of the values in the array
+// element, the map is applied and the unused zone updated as it is now used. We
+// continue to try to map scalars to the array element until there are no more
+// candidates to map. The algorithm is greedy in the sense that the first scalar
+// not conflicting will be mapped. Other scalars processed later that could have
+// fit the same unused zone will be rejected. As such the result depends on the
+// processing order.
+//
+//===----------------------------------------------------------------------===//
+
+#include "polly/ZoneAlgo.h"
+#include "polly/ScopInfo.h"
+#include "polly/Support/GICHelper.h"
+#include "polly/Support/ISLTools.h"
+#include "polly/Support/VirtualInstruction.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "polly-zone"
+
+STATISTIC(NumIncompatibleArrays, "Number of not zone-analyzable arrays");
+STATISTIC(NumCompatibleArrays, "Number of zone-analyzable arrays");
+STATISTIC(NumRecursivePHIs, "Number of recursive PHIs");
+STATISTIC(NumNormalizablePHIs, "Number of normalizable PHIs");
+STATISTIC(NumPHINormialization, "Number of PHI executed normalizations");
+
+using namespace polly;
+using namespace llvm;
+
+static isl::union_map computeReachingDefinition(isl::union_map Schedule,
+ isl::union_map Writes,
+ bool InclDef, bool InclRedef) {
+ return computeReachingWrite(Schedule, Writes, false, InclDef, InclRedef);
+}
+
+/// Compute the reaching definition of a scalar.
+///
+/// Compared to computeReachingDefinition, there is just one element which is
+/// accessed and therefore only a set if instances that accesses that element is
+/// required.
+///
+/// @param Schedule { DomainWrite[] -> Scatter[] }
+/// @param Writes { DomainWrite[] }
+/// @param InclDef Include the timepoint of the definition to the result.
+/// @param InclRedef Include the timepoint of the overwrite into the result.
+///
+/// @return { Scatter[] -> DomainWrite[] }
+static isl::union_map computeScalarReachingDefinition(isl::union_map Schedule,
+ isl::union_set Writes,
+ bool InclDef,
+ bool InclRedef) {
+ // { DomainWrite[] -> Element[] }
+ isl::union_map Defs = isl::union_map::from_domain(Writes);
+
+ // { [Element[] -> Scatter[]] -> DomainWrite[] }
+ auto ReachDefs =
+ computeReachingDefinition(Schedule, Defs, InclDef, InclRedef);
+
+ // { Scatter[] -> DomainWrite[] }
+ return ReachDefs.curry().range().unwrap();
+}
+
+/// Compute the reaching definition of a scalar.
+///
+/// This overload accepts only a single writing statement as an isl_map,
+/// consequently the result also is only a single isl_map.
+///
+/// @param Schedule { DomainWrite[] -> Scatter[] }
+/// @param Writes { DomainWrite[] }
+/// @param InclDef Include the timepoint of the definition to the result.
+/// @param InclRedef Include the timepoint of the overwrite into the result.
+///
+/// @return { Scatter[] -> DomainWrite[] }
+static isl::map computeScalarReachingDefinition(isl::union_map Schedule,
+ isl::set Writes, bool InclDef,
+ bool InclRedef) {
+ isl::space DomainSpace = Writes.get_space();
+ isl::space ScatterSpace = getScatterSpace(Schedule);
+
+ // { Scatter[] -> DomainWrite[] }
+ isl::union_map UMap = computeScalarReachingDefinition(
+ Schedule, isl::union_set(Writes), InclDef, InclRedef);
+
+ isl::space ResultSpace = ScatterSpace.map_from_domain_and_range(DomainSpace);
+ return singleton(UMap, ResultSpace);
+}
+
+isl::union_map polly::makeUnknownForDomain(isl::union_set Domain) {
+ return isl::union_map::from_domain(Domain);
+}
+
+/// Create a domain-to-unknown value mapping.
+///
+/// @see makeUnknownForDomain(isl::union_set)
+///
+/// @param Domain { Domain[] }
+///
+/// @return { Domain[] -> ValInst[] }
+static isl::map makeUnknownForDomain(isl::set Domain) {
+ return isl::map::from_domain(Domain);
+}
+
+/// Return whether @p Map maps to an unknown value.
+///
+/// @param { [] -> ValInst[] }
+static bool isMapToUnknown(const isl::map &Map) {
+ isl::space Space = Map.get_space().range();
+ return Space.has_tuple_id(isl::dim::set).is_false() &&
+ Space.is_wrapping().is_false() &&
+ Space.dim(isl::dim::set).release() == 0;
+}
+
+isl::union_map polly::filterKnownValInst(const isl::union_map &UMap) {
+ isl::union_map Result = isl::union_map::empty(UMap.ctx());
+ for (isl::map Map : UMap.get_map_list()) {
+ if (!isMapToUnknown(Map))
+ Result = Result.unite(Map);
+ }
+ return Result;
+}
+
+ZoneAlgorithm::ZoneAlgorithm(const char *PassName, Scop *S, LoopInfo *LI)
+ : PassName(PassName), IslCtx(S->getSharedIslCtx()), S(S), LI(LI),
+ Schedule(S->getSchedule()) {
+ auto Domains = S->getDomains();
+
+ Schedule = Schedule.intersect_domain(Domains);
+ ParamSpace = Schedule.get_space();
+ ScatterSpace = getScatterSpace(Schedule);
+}
+
+/// Check if all stores in @p Stmt store the very same value.
+///
+/// This covers a special situation occurring in Polybench's
+/// covariance/correlation (which is typical for algorithms that cover symmetric
+/// matrices):
+///
+/// for (int i = 0; i < n; i += 1)
+/// for (int j = 0; j <= i; j += 1) {
+/// double x = ...;
+/// C[i][j] = x;
+/// C[j][i] = x;
+/// }
+///
+/// For i == j, the same value is written twice to the same element.Double
+/// writes to the same element are not allowed in DeLICM because its algorithm
+/// does not see which of the writes is effective.But if its the same value
+/// anyway, it doesn't matter.
+///
+/// LLVM passes, however, cannot simplify this because the write is necessary
+/// for i != j (unless it would add a condition for one of the writes to occur
+/// only if i != j).
+///
+/// TODO: In the future we may want to extent this to make the checks
+/// specific to different memory locations.
+static bool onlySameValueWrites(ScopStmt *Stmt) {
+ Value *V = nullptr;
+
+ for (auto *MA : *Stmt) {
+ if (!MA->isLatestArrayKind() || !MA->isMustWrite() ||
+ !MA->isOriginalArrayKind())
+ continue;
+
+ if (!V) {
+ V = MA->getAccessValue();
+ continue;
+ }
+
+ if (V != MA->getAccessValue())
+ return false;
+ }
+ return true;
+}
+
+/// Is @p InnerLoop nested inside @p OuterLoop?
+static bool isInsideLoop(Loop *OuterLoop, Loop *InnerLoop) {
+ // If OuterLoop is nullptr, we cannot call its contains() method. In this case
+ // OuterLoop represents the 'top level' and therefore contains all loop.
+ return !OuterLoop || OuterLoop->contains(InnerLoop);
+}
+
+void ZoneAlgorithm::collectIncompatibleElts(ScopStmt *Stmt,
+ isl::union_set &IncompatibleElts,
+ isl::union_set &AllElts) {
+ auto Stores = makeEmptyUnionMap();
+ auto Loads = makeEmptyUnionMap();
+
+ // This assumes that the MemoryKind::Array MemoryAccesses are iterated in
+ // order.
+ for (auto *MA : *Stmt) {
+ if (!MA->isOriginalArrayKind())
+ continue;
+
+ isl::map AccRelMap = getAccessRelationFor(MA);
+ isl::union_map AccRel = AccRelMap;
+
+ // To avoid solving any ILP problems, always add entire arrays instead of
+ // just the elements that are accessed.
+ auto ArrayElts = isl::set::universe(AccRelMap.get_space().range());
+ AllElts = AllElts.unite(ArrayElts);
+
+ if (MA->isRead()) {
+ // Reject load after store to same location.
+ if (!Stores.is_disjoint(AccRel)) {
+ LLVM_DEBUG(
+ dbgs() << "Load after store of same element in same statement\n");
+ OptimizationRemarkMissed R(PassName, "LoadAfterStore",
+ MA->getAccessInstruction());
+ R << "load after store of same element in same statement";
+ R << " (previous stores: " << Stores;
+ R << ", loading: " << AccRel << ")";
+ S->getFunction().getContext().diagnose(R);
+
+ IncompatibleElts = IncompatibleElts.unite(ArrayElts);
+ }
+
+ Loads = Loads.unite(AccRel);
+
+ continue;
+ }
+
+ // In region statements the order is less clear, eg. the load and store
+ // might be in a boxed loop.
+ if (Stmt->isRegionStmt() && !Loads.is_disjoint(AccRel)) {
+ LLVM_DEBUG(dbgs() << "WRITE in non-affine subregion not supported\n");
+ OptimizationRemarkMissed R(PassName, "StoreInSubregion",
+ MA->getAccessInstruction());
+ R << "store is in a non-affine subregion";
+ S->getFunction().getContext().diagnose(R);
+
+ IncompatibleElts = IncompatibleElts.unite(ArrayElts);
+ }
+
+ // Do not allow more than one store to the same location.
+ if (!Stores.is_disjoint(AccRel) && !onlySameValueWrites(Stmt)) {
+ LLVM_DEBUG(dbgs() << "WRITE after WRITE to same element\n");
+ OptimizationRemarkMissed R(PassName, "StoreAfterStore",
+ MA->getAccessInstruction());
+ R << "store after store of same element in same statement";
+ R << " (previous stores: " << Stores;
+ R << ", storing: " << AccRel << ")";
+ S->getFunction().getContext().diagnose(R);
+
+ IncompatibleElts = IncompatibleElts.unite(ArrayElts);
+ }
+
+ Stores = Stores.unite(AccRel);
+ }
+}
+
+void ZoneAlgorithm::addArrayReadAccess(MemoryAccess *MA) {
+ assert(MA->isLatestArrayKind());
+ assert(MA->isRead());
+ ScopStmt *Stmt = MA->getStatement();
+
+ // { DomainRead[] -> Element[] }
+ auto AccRel = intersectRange(getAccessRelationFor(MA), CompatibleElts);
+ AllReads = AllReads.unite(AccRel);
+
+ if (LoadInst *Load = dyn_cast_or_null<LoadInst>(MA->getAccessInstruction())) {
+ // { DomainRead[] -> ValInst[] }
+ isl::map LoadValInst = makeValInst(
+ Load, Stmt, LI->getLoopFor(Load->getParent()), Stmt->isBlockStmt());
+
+ // { DomainRead[] -> [Element[] -> DomainRead[]] }
+ isl::map IncludeElement = AccRel.domain_map().curry();
+
+ // { [Element[] -> DomainRead[]] -> ValInst[] }
+ isl::map EltLoadValInst = LoadValInst.apply_domain(IncludeElement);
+
+ AllReadValInst = AllReadValInst.unite(EltLoadValInst);
+ }
+}
+
+isl::union_map ZoneAlgorithm::getWrittenValue(MemoryAccess *MA,
+ isl::map AccRel) {
+ if (!MA->isMustWrite())
+ return {};
+
+ Value *AccVal = MA->getAccessValue();
+ ScopStmt *Stmt = MA->getStatement();
+ Instruction *AccInst = MA->getAccessInstruction();
+
+ // Write a value to a single element.
+ auto L = MA->isOriginalArrayKind() ? LI->getLoopFor(AccInst->getParent())
+ : Stmt->getSurroundingLoop();
+ if (AccVal &&
+ AccVal->getType() == MA->getLatestScopArrayInfo()->getElementType() &&
+ AccRel.is_single_valued().is_true())
+ return makeNormalizedValInst(AccVal, Stmt, L);
+
+ // memset(_, '0', ) is equivalent to writing the null value to all touched
+ // elements. isMustWrite() ensures that all of an element's bytes are
+ // overwritten.
+ if (auto *Memset = dyn_cast<MemSetInst>(AccInst)) {
+ auto *WrittenConstant = dyn_cast<Constant>(Memset->getValue());
+ Type *Ty = MA->getLatestScopArrayInfo()->getElementType();
+ if (WrittenConstant && WrittenConstant->isZeroValue()) {
+ Constant *Zero = Constant::getNullValue(Ty);
+ return makeNormalizedValInst(Zero, Stmt, L);
+ }
+ }
+
+ return {};
+}
+
+void ZoneAlgorithm::addArrayWriteAccess(MemoryAccess *MA) {
+ assert(MA->isLatestArrayKind());
+ assert(MA->isWrite());
+ auto *Stmt = MA->getStatement();
+
+ // { Domain[] -> Element[] }
+ isl::map AccRel = intersectRange(getAccessRelationFor(MA), CompatibleElts);
+
+ if (MA->isMustWrite())
+ AllMustWrites = AllMustWrites.unite(AccRel);
+
+ if (MA->isMayWrite())
+ AllMayWrites = AllMayWrites.unite(AccRel);
+
+ // { Domain[] -> ValInst[] }
+ isl::union_map WriteValInstance = getWrittenValue(MA, AccRel);
+ if (WriteValInstance.is_null())
+ WriteValInstance = makeUnknownForDomain(Stmt);
+
+ // { Domain[] -> [Element[] -> Domain[]] }
+ isl::map IncludeElement = AccRel.domain_map().curry();
+
+ // { [Element[] -> DomainWrite[]] -> ValInst[] }
+ isl::union_map EltWriteValInst =
+ WriteValInstance.apply_domain(IncludeElement);
+
+ AllWriteValInst = AllWriteValInst.unite(EltWriteValInst);
+}
+
+/// For an llvm::Value defined in @p DefStmt, compute the RAW dependency for a
+/// use in every instance of @p UseStmt.
+///
+/// @param UseStmt Statement a scalar is used in.
+/// @param DefStmt Statement a scalar is defined in.
+///
+/// @return { DomainUse[] -> DomainDef[] }
+isl::map ZoneAlgorithm::computeUseToDefFlowDependency(ScopStmt *UseStmt,
+ ScopStmt *DefStmt) {
+ // { DomainUse[] -> Scatter[] }
+ isl::map UseScatter = getScatterFor(UseStmt);
+
+ // { Zone[] -> DomainDef[] }
+ isl::map ReachDefZone = getScalarReachingDefinition(DefStmt);
+
+ // { Scatter[] -> DomainDef[] }
+ isl::map ReachDefTimepoints =
+ convertZoneToTimepoints(ReachDefZone, isl::dim::in, false, true);
+
+ // { DomainUse[] -> DomainDef[] }
+ return UseScatter.apply_range(ReachDefTimepoints);
+}
+
+/// Return whether @p PHI refers (also transitively through other PHIs) to
+/// itself.
+///
+/// loop:
+/// %phi1 = phi [0, %preheader], [%phi1, %loop]
+/// br i1 %c, label %loop, label %exit
+///
+/// exit:
+/// %phi2 = phi [%phi1, %bb]
+///
+/// In this example, %phi1 is recursive, but %phi2 is not.
+static bool isRecursivePHI(const PHINode *PHI) {
+ SmallVector<const PHINode *, 8> Worklist;
+ SmallPtrSet<const PHINode *, 8> Visited;
+ Worklist.push_back(PHI);
+
+ while (!Worklist.empty()) {
+ const PHINode *Cur = Worklist.pop_back_val();
+
+ if (Visited.count(Cur))
+ continue;
+ Visited.insert(Cur);
+
+ for (const Use &Incoming : Cur->incoming_values()) {
+ Value *IncomingVal = Incoming.get();
+ auto *IncomingPHI = dyn_cast<PHINode>(IncomingVal);
+ if (!IncomingPHI)
+ continue;
+
+ if (IncomingPHI == PHI)
+ return true;
+ Worklist.push_back(IncomingPHI);
+ }
+ }
+ return false;
+}
+
+isl::union_map ZoneAlgorithm::computePerPHI(const ScopArrayInfo *SAI) {
+ // TODO: If the PHI has an incoming block from before the SCoP, it is not
+ // represented in any ScopStmt.
+
+ auto *PHI = cast<PHINode>(SAI->getBasePtr());
+ auto It = PerPHIMaps.find(PHI);
+ if (It != PerPHIMaps.end())
+ return It->second;
+
+ // Cannot reliably compute immediate predecessor for undefined executions, so
+ // bail out if we do not know. This in particular applies to undefined control
+ // flow.
+ isl::set DefinedContext = S->getDefinedBehaviorContext();
+ if (DefinedContext.is_null())
+ return {};
+
+ assert(SAI->isPHIKind());
+
+ // { DomainPHIWrite[] -> Scatter[] }
+ isl::union_map PHIWriteScatter = makeEmptyUnionMap();
+
+ // Collect all incoming block timepoints.
+ for (MemoryAccess *MA : S->getPHIIncomings(SAI)) {
+ isl::map Scatter = getScatterFor(MA);
+ PHIWriteScatter = PHIWriteScatter.unite(Scatter);
+ }
+
+ // { DomainPHIRead[] -> Scatter[] }
+ isl::map PHIReadScatter = getScatterFor(S->getPHIRead(SAI));
+
+ // { DomainPHIRead[] -> Scatter[] }
+ isl::map BeforeRead = beforeScatter(PHIReadScatter, true);
+
+ // { Scatter[] }
+ isl::set WriteTimes = singleton(PHIWriteScatter.range(), ScatterSpace);
+
+ // { DomainPHIRead[] -> Scatter[] }
+ isl::map PHIWriteTimes = BeforeRead.intersect_range(WriteTimes);
+
+ // Remove instances outside the context.
+ PHIWriteTimes = PHIWriteTimes.intersect_params(DefinedContext);
+
+ isl::map LastPerPHIWrites = PHIWriteTimes.lexmax();
+
+ // { DomainPHIRead[] -> DomainPHIWrite[] }
+ isl::union_map Result =
+ isl::union_map(LastPerPHIWrites).apply_range(PHIWriteScatter.reverse());
+ assert(!Result.is_single_valued().is_false());
+ assert(!Result.is_injective().is_false());
+
+ PerPHIMaps.insert({PHI, Result});
+ return Result;
+}
+
+isl::union_set ZoneAlgorithm::makeEmptyUnionSet() const {
+ return isl::union_set::empty(ParamSpace.ctx());
+}
+
+isl::union_map ZoneAlgorithm::makeEmptyUnionMap() const {
+ return isl::union_map::empty(ParamSpace.ctx());
+}
+
+void ZoneAlgorithm::collectCompatibleElts() {
+ // First find all the incompatible elements, then take the complement.
+ // We compile the list of compatible (rather than incompatible) elements so
+ // users can intersect with the list, not requiring a subtract operation. It
+ // also allows us to define a 'universe' of all elements and makes it more
+ // explicit in which array elements can be used.
+ isl::union_set AllElts = makeEmptyUnionSet();
+ isl::union_set IncompatibleElts = makeEmptyUnionSet();
+
+ for (auto &Stmt : *S)
+ collectIncompatibleElts(&Stmt, IncompatibleElts, AllElts);
+
+ NumIncompatibleArrays += isl_union_set_n_set(IncompatibleElts.get());
+ CompatibleElts = AllElts.subtract(IncompatibleElts);
+ NumCompatibleArrays += isl_union_set_n_set(CompatibleElts.get());
+}
+
+isl::map ZoneAlgorithm::getScatterFor(ScopStmt *Stmt) const {
+ isl::space ResultSpace =
+ Stmt->getDomainSpace().map_from_domain_and_range(ScatterSpace);
+ return Schedule.extract_map(ResultSpace);
+}
+
+isl::map ZoneAlgorithm::getScatterFor(MemoryAccess *MA) const {
+ return getScatterFor(MA->getStatement());
+}
+
+isl::union_map ZoneAlgorithm::getScatterFor(isl::union_set Domain) const {
+ return Schedule.intersect_domain(Domain);
+}
+
+isl::map ZoneAlgorithm::getScatterFor(isl::set Domain) const {
+ auto ResultSpace = Domain.get_space().map_from_domain_and_range(ScatterSpace);
+ auto UDomain = isl::union_set(Domain);
+ auto UResult = getScatterFor(std::move(UDomain));
+ auto Result = singleton(std::move(UResult), std::move(ResultSpace));
+ assert(Result.is_null() || Result.domain().is_equal(Domain) == isl_bool_true);
+ return Result;
+}
+
+isl::set ZoneAlgorithm::getDomainFor(ScopStmt *Stmt) const {
+ return Stmt->getDomain().remove_redundancies();
+}
+
+isl::set ZoneAlgorithm::getDomainFor(MemoryAccess *MA) const {
+ return getDomainFor(MA->getStatement());
+}
+
+isl::map ZoneAlgorithm::getAccessRelationFor(MemoryAccess *MA) const {
+ auto Domain = getDomainFor(MA);
+ auto AccRel = MA->getLatestAccessRelation();
+ return AccRel.intersect_domain(Domain);
+}
+
+isl::map ZoneAlgorithm::getDefToTarget(ScopStmt *DefStmt,
+ ScopStmt *TargetStmt) {
+ // No translation required if the definition is already at the target.
+ if (TargetStmt == DefStmt)
+ return isl::map::identity(
+ getDomainFor(TargetStmt).get_space().map_from_set());
+
+ isl::map &Result = DefToTargetCache[std::make_pair(TargetStmt, DefStmt)];
+
+ // This is a shortcut in case the schedule is still the original and
+ // TargetStmt is in the same or nested inside DefStmt's loop. With the
+ // additional assumption that operand trees do not cross DefStmt's loop
+ // header, then TargetStmt's instance shared coordinates are the same as
+ // DefStmt's coordinates. All TargetStmt instances with this prefix share
+ // the same DefStmt instance.
+ // Model:
+ //
+ // for (int i < 0; i < N; i+=1) {
+ // DefStmt:
+ // D = ...;
+ // for (int j < 0; j < N; j+=1) {
+ // TargetStmt:
+ // use(D);
+ // }
+ // }
+ //
+ // Here, the value used in TargetStmt is defined in the corresponding
+ // DefStmt, i.e.
+ //
+ // { DefStmt[i] -> TargetStmt[i,j] }
+ //
+ // In practice, this should cover the majority of cases.
+ if (Result.is_null() && S->isOriginalSchedule() &&
+ isInsideLoop(DefStmt->getSurroundingLoop(),
+ TargetStmt->getSurroundingLoop())) {
+ isl::set DefDomain = getDomainFor(DefStmt);
+ isl::set TargetDomain = getDomainFor(TargetStmt);
+ assert(unsignedFromIslSize(DefDomain.tuple_dim()) <=
+ unsignedFromIslSize(TargetDomain.tuple_dim()));
+
+ Result = isl::map::from_domain_and_range(DefDomain, TargetDomain);
+ for (unsigned i : rangeIslSize(0, DefDomain.tuple_dim()))
+ Result = Result.equate(isl::dim::in, i, isl::dim::out, i);
+ }
+
+ if (Result.is_null()) {
+ // { DomainDef[] -> DomainTarget[] }
+ Result = computeUseToDefFlowDependency(TargetStmt, DefStmt).reverse();
+ simplify(Result);
+ }
+
+ return Result;
+}
+
+isl::map ZoneAlgorithm::getScalarReachingDefinition(ScopStmt *Stmt) {
+ auto &Result = ScalarReachDefZone[Stmt];
+ if (!Result.is_null())
+ return Result;
+
+ auto Domain = getDomainFor(Stmt);
+ Result = computeScalarReachingDefinition(Schedule, Domain, false, true);
+ simplify(Result);
+
+ return Result;
+}
+
+isl::map ZoneAlgorithm::getScalarReachingDefinition(isl::set DomainDef) {
+ auto DomId = DomainDef.get_tuple_id();
+ auto *Stmt = static_cast<ScopStmt *>(isl_id_get_user(DomId.get()));
+
+ auto StmtResult = getScalarReachingDefinition(Stmt);
+
+ return StmtResult.intersect_range(DomainDef);
+}
+
+isl::map ZoneAlgorithm::makeUnknownForDomain(ScopStmt *Stmt) const {
+ return ::makeUnknownForDomain(getDomainFor(Stmt));
+}
+
+isl::id ZoneAlgorithm::makeValueId(Value *V) {
+ if (!V)
+ return {};
+
+ auto &Id = ValueIds[V];
+ if (Id.is_null()) {
+ auto Name = getIslCompatibleName("Val_", V, ValueIds.size() - 1,
+ std::string(), UseInstructionNames);
+ Id = isl::id::alloc(IslCtx.get(), Name.c_str(), V);
+ }
+ return Id;
+}
+
+isl::space ZoneAlgorithm::makeValueSpace(Value *V) {
+ auto Result = ParamSpace.set_from_params();
+ return Result.set_tuple_id(isl::dim::set, makeValueId(V));
+}
+
+isl::set ZoneAlgorithm::makeValueSet(Value *V) {
+ auto Space = makeValueSpace(V);
+ return isl::set::universe(Space);
+}
+
+isl::map ZoneAlgorithm::makeValInst(Value *Val, ScopStmt *UserStmt, Loop *Scope,
+ bool IsCertain) {
+ // If the definition/write is conditional, the value at the location could
+ // be either the written value or the old value. Since we cannot know which
+ // one, consider the value to be unknown.
+ if (!IsCertain)
+ return makeUnknownForDomain(UserStmt);
+
+ auto DomainUse = getDomainFor(UserStmt);
+ auto VUse = VirtualUse::create(S, UserStmt, Scope, Val, true);
+ switch (VUse.getKind()) {
+ case VirtualUse::Constant:
+ case VirtualUse::Block:
+ case VirtualUse::Hoisted:
+ case VirtualUse::ReadOnly: {
+ // The definition does not depend on the statement which uses it.
+ auto ValSet = makeValueSet(Val);
+ return isl::map::from_domain_and_range(DomainUse, ValSet);
+ }
+
+ case VirtualUse::Synthesizable: {
+ auto *ScevExpr = VUse.getScevExpr();
+ auto UseDomainSpace = DomainUse.get_space();
+
+ // Construct the SCEV space.
+ // TODO: Add only the induction variables referenced in SCEVAddRecExpr
+ // expressions, not just all of them.
+ auto ScevId = isl::manage(isl_id_alloc(UseDomainSpace.ctx().get(), nullptr,
+ const_cast<SCEV *>(ScevExpr)));
+
+ auto ScevSpace = UseDomainSpace.drop_dims(isl::dim::set, 0, 0);
+ ScevSpace = ScevSpace.set_tuple_id(isl::dim::set, ScevId);
+
+ // { DomainUse[] -> ScevExpr[] }
+ auto ValInst =
+ isl::map::identity(UseDomainSpace.map_from_domain_and_range(ScevSpace));
+ return ValInst;
+ }
+
+ case VirtualUse::Intra: {
+ // Definition and use is in the same statement. We do not need to compute
+ // a reaching definition.
+
+ // { llvm::Value }
+ auto ValSet = makeValueSet(Val);
+
+ // { UserDomain[] -> llvm::Value }
+ auto ValInstSet = isl::map::from_domain_and_range(DomainUse, ValSet);
+
+ // { UserDomain[] -> [UserDomain[] - >llvm::Value] }
+ auto Result = ValInstSet.domain_map().reverse();
+ simplify(Result);
+ return Result;
+ }
+
+ case VirtualUse::Inter: {
+ // The value is defined in a different statement.
+
+ auto *Inst = cast<Instruction>(Val);
+ auto *ValStmt = S->getStmtFor(Inst);
+
+ // If the llvm::Value is defined in a removed Stmt, we cannot derive its
+ // domain. We could use an arbitrary statement, but this could result in
+ // different ValInst[] for the same llvm::Value.
+ if (!ValStmt)
+ return ::makeUnknownForDomain(DomainUse);
+
+ // { DomainUse[] -> DomainDef[] }
+ auto UsedInstance = getDefToTarget(ValStmt, UserStmt).reverse();
+
+ // { llvm::Value }
+ auto ValSet = makeValueSet(Val);
+
+ // { DomainUse[] -> llvm::Value[] }
+ auto ValInstSet = isl::map::from_domain_and_range(DomainUse, ValSet);
+
+ // { DomainUse[] -> [DomainDef[] -> llvm::Value] }
+ auto Result = UsedInstance.range_product(ValInstSet);
+
+ simplify(Result);
+ return Result;
+ }
+ }
+ llvm_unreachable("Unhandled use type");
+}
+
+/// Remove all computed PHIs out of @p Input and replace by their incoming
+/// value.
+///
+/// @param Input { [] -> ValInst[] }
+/// @param ComputedPHIs Set of PHIs that are replaced. Its ValInst must appear
+/// on the LHS of @p NormalizeMap.
+/// @param NormalizeMap { ValInst[] -> ValInst[] }
+static isl::union_map normalizeValInst(isl::union_map Input,
+ const DenseSet<PHINode *> &ComputedPHIs,
+ isl::union_map NormalizeMap) {
+ isl::union_map Result = isl::union_map::empty(Input.ctx());
+ for (isl::map Map : Input.get_map_list()) {
+ isl::space Space = Map.get_space();
+ isl::space RangeSpace = Space.range();
+
+ // Instructions within the SCoP are always wrapped. Non-wrapped tuples
+ // are therefore invariant in the SCoP and don't need normalization.
+ if (!RangeSpace.is_wrapping()) {
+ Result = Result.unite(Map);
+ continue;
+ }
+
+ auto *PHI = dyn_cast<PHINode>(static_cast<Value *>(
+ RangeSpace.unwrap().get_tuple_id(isl::dim::out).get_user()));
+
+ // If no normalization is necessary, then the ValInst stands for itself.
+ if (!ComputedPHIs.count(PHI)) {
+ Result = Result.unite(Map);
+ continue;
+ }
+
+ // Otherwise, apply the normalization.
+ isl::union_map Mapped = isl::union_map(Map).apply_range(NormalizeMap);
+ Result = Result.unite(Mapped);
+ NumPHINormialization++;
+ }
+ return Result;
+}
+
+isl::union_map ZoneAlgorithm::makeNormalizedValInst(llvm::Value *Val,
+ ScopStmt *UserStmt,
+ llvm::Loop *Scope,
+ bool IsCertain) {
+ isl::map ValInst = makeValInst(Val, UserStmt, Scope, IsCertain);
+ isl::union_map Normalized =
+ normalizeValInst(ValInst, ComputedPHIs, NormalizeMap);
+ return Normalized;
+}
+
+bool ZoneAlgorithm::isCompatibleAccess(MemoryAccess *MA) {
+ if (!MA)
+ return false;
+ if (!MA->isLatestArrayKind())
+ return false;
+ Instruction *AccInst = MA->getAccessInstruction();
+ return isa<StoreInst>(AccInst) || isa<LoadInst>(AccInst);
+}
+
+bool ZoneAlgorithm::isNormalizable(MemoryAccess *MA) {
+ assert(MA->isRead());
+
+ // Exclude ExitPHIs, we are assuming that a normalizable PHI has a READ
+ // MemoryAccess.
+ if (!MA->isOriginalPHIKind())
+ return false;
+
+ // Exclude recursive PHIs, normalizing them would require a transitive
+ // closure.
+ auto *PHI = cast<PHINode>(MA->getAccessInstruction());
+ if (RecursivePHIs.count(PHI))
+ return false;
+
+ // Ensure that each incoming value can be represented by a ValInst[].
+ // We do represent values from statements associated to multiple incoming
+ // value by the PHI itself, but we do not handle this case yet (especially
+ // isNormalized()) when normalizing.
+ const ScopArrayInfo *SAI = MA->getOriginalScopArrayInfo();
+ auto Incomings = S->getPHIIncomings(SAI);
+ for (MemoryAccess *Incoming : Incomings) {
+ if (Incoming->getIncoming().size() != 1)
+ return false;
+ }
+
+ return true;
+}
+
+isl::boolean ZoneAlgorithm::isNormalized(isl::map Map) {
+ isl::space Space = Map.get_space();
+ isl::space RangeSpace = Space.range();
+
+ isl::boolean IsWrapping = RangeSpace.is_wrapping();
+ if (!IsWrapping.is_true())
+ return !IsWrapping;
+ isl::space Unwrapped = RangeSpace.unwrap();
+
+ isl::id OutTupleId = Unwrapped.get_tuple_id(isl::dim::out);
+ if (OutTupleId.is_null())
+ return isl::boolean();
+ auto *PHI = dyn_cast<PHINode>(static_cast<Value *>(OutTupleId.get_user()));
+ if (!PHI)
+ return true;
+
+ isl::id InTupleId = Unwrapped.get_tuple_id(isl::dim::in);
+ if (OutTupleId.is_null())
+ return isl::boolean();
+ auto *IncomingStmt = static_cast<ScopStmt *>(InTupleId.get_user());
+ MemoryAccess *PHIRead = IncomingStmt->lookupPHIReadOf(PHI);
+ if (!isNormalizable(PHIRead))
+ return true;
+
+ return false;
+}
+
+isl::boolean ZoneAlgorithm::isNormalized(isl::union_map UMap) {
+ isl::boolean Result = true;
+ for (isl::map Map : UMap.get_map_list()) {
+ Result = isNormalized(Map);
+ if (Result.is_true())
+ continue;
+ break;
+ }
+ return Result;
+}
+
+void ZoneAlgorithm::computeCommon() {
+ AllReads = makeEmptyUnionMap();
+ AllMayWrites = makeEmptyUnionMap();
+ AllMustWrites = makeEmptyUnionMap();
+ AllWriteValInst = makeEmptyUnionMap();
+ AllReadValInst = makeEmptyUnionMap();
+
+ // Default to empty, i.e. no normalization/replacement is taking place. Call
+ // computeNormalizedPHIs() to initialize.
+ NormalizeMap = makeEmptyUnionMap();
+ ComputedPHIs.clear();
+
+ for (auto &Stmt : *S) {
+ for (auto *MA : Stmt) {
+ if (!MA->isLatestArrayKind())
+ continue;
+
+ if (MA->isRead())
+ addArrayReadAccess(MA);
+
+ if (MA->isWrite())
+ addArrayWriteAccess(MA);
+ }
+ }
+
+ // { DomainWrite[] -> Element[] }
+ AllWrites = AllMustWrites.unite(AllMayWrites);
+
+ // { [Element[] -> Zone[]] -> DomainWrite[] }
+ WriteReachDefZone =
+ computeReachingDefinition(Schedule, AllWrites, false, true);
+ simplify(WriteReachDefZone);
+}
+
+void ZoneAlgorithm::computeNormalizedPHIs() {
+ // Determine which PHIs can reference themselves. They are excluded from
+ // normalization to avoid problems with transitive closures.
+ for (ScopStmt &Stmt : *S) {
+ for (MemoryAccess *MA : Stmt) {
+ if (!MA->isPHIKind())
+ continue;
+ if (!MA->isRead())
+ continue;
+
+ // TODO: Can be more efficient since isRecursivePHI can theoretically
+ // determine recursiveness for multiple values and/or cache results.
+ auto *PHI = cast<PHINode>(MA->getAccessInstruction());
+ if (isRecursivePHI(PHI)) {
+ NumRecursivePHIs++;
+ RecursivePHIs.insert(PHI);
+ }
+ }
+ }
+
+ // { PHIValInst[] -> IncomingValInst[] }
+ isl::union_map AllPHIMaps = makeEmptyUnionMap();
+
+ // Discover new PHIs and try to normalize them.
+ DenseSet<PHINode *> AllPHIs;
+ for (ScopStmt &Stmt : *S) {
+ for (MemoryAccess *MA : Stmt) {
+ if (!MA->isOriginalPHIKind())
+ continue;
+ if (!MA->isRead())
+ continue;
+ if (!isNormalizable(MA))
+ continue;
+
+ auto *PHI = cast<PHINode>(MA->getAccessInstruction());
+ const ScopArrayInfo *SAI = MA->getOriginalScopArrayInfo();
+
+ // Determine which instance of the PHI statement corresponds to which
+ // incoming value. Skip if we cannot determine PHI predecessors.
+ // { PHIDomain[] -> IncomingDomain[] }
+ isl::union_map PerPHI = computePerPHI(SAI);
+ if (PerPHI.is_null())
+ continue;
+
+ // { PHIDomain[] -> PHIValInst[] }
+ isl::map PHIValInst = makeValInst(PHI, &Stmt, Stmt.getSurroundingLoop());
+
+ // { IncomingDomain[] -> IncomingValInst[] }
+ isl::union_map IncomingValInsts = makeEmptyUnionMap();
+
+ // Get all incoming values.
+ for (MemoryAccess *MA : S->getPHIIncomings(SAI)) {
+ ScopStmt *IncomingStmt = MA->getStatement();
+
+ auto Incoming = MA->getIncoming();
+ assert(Incoming.size() == 1 && "The incoming value must be "
+ "representable by something else than "
+ "the PHI itself");
+ Value *IncomingVal = Incoming[0].second;
+
+ // { IncomingDomain[] -> IncomingValInst[] }
+ isl::map IncomingValInst = makeValInst(
+ IncomingVal, IncomingStmt, IncomingStmt->getSurroundingLoop());
+
+ IncomingValInsts = IncomingValInsts.unite(IncomingValInst);
+ }
+
+ // { PHIValInst[] -> IncomingValInst[] }
+ isl::union_map PHIMap =
+ PerPHI.apply_domain(PHIValInst).apply_range(IncomingValInsts);
+ assert(!PHIMap.is_single_valued().is_false());
+
+ // Resolve transitiveness: The incoming value of the newly discovered PHI
+ // may reference a previously normalized PHI. At the same time, already
+ // normalized PHIs might be normalized to the new PHI. At the end, none of
+ // the PHIs may appear on the right-hand-side of the normalization map.
+ PHIMap = normalizeValInst(PHIMap, AllPHIs, AllPHIMaps);
+ AllPHIs.insert(PHI);
+ AllPHIMaps = normalizeValInst(AllPHIMaps, AllPHIs, PHIMap);
+
+ AllPHIMaps = AllPHIMaps.unite(PHIMap);
+ NumNormalizablePHIs++;
+ }
+ }
+ simplify(AllPHIMaps);
+
+ // Apply the normalization.
+ ComputedPHIs = AllPHIs;
+ NormalizeMap = AllPHIMaps;
+
+ assert(NormalizeMap.is_null() || isNormalized(NormalizeMap));
+}
+
+void ZoneAlgorithm::printAccesses(llvm::raw_ostream &OS, int Indent) const {
+ OS.indent(Indent) << "After accesses {\n";
+ for (auto &Stmt : *S) {
+ OS.indent(Indent + 4) << Stmt.getBaseName() << "\n";
+ for (auto *MA : Stmt)
+ MA->print(OS);
+ }
+ OS.indent(Indent) << "}\n";
+}
+
+isl::union_map ZoneAlgorithm::computeKnownFromMustWrites() const {
+ // { [Element[] -> Zone[]] -> [Element[] -> DomainWrite[]] }
+ isl::union_map EltReachdDef = distributeDomain(WriteReachDefZone.curry());
+
+ // { [Element[] -> DomainWrite[]] -> ValInst[] }
+ isl::union_map AllKnownWriteValInst = filterKnownValInst(AllWriteValInst);
+
+ // { [Element[] -> Zone[]] -> ValInst[] }
+ return EltReachdDef.apply_range(AllKnownWriteValInst);
+}
+
+isl::union_map ZoneAlgorithm::computeKnownFromLoad() const {
+ // { Element[] }
+ isl::union_set AllAccessedElts = AllReads.range().unite(AllWrites.range());
+
+ // { Element[] -> Scatter[] }
+ isl::union_map EltZoneUniverse = isl::union_map::from_domain_and_range(
+ AllAccessedElts, isl::set::universe(ScatterSpace));
+
+ // This assumes there are no "holes" in
+ // isl_union_map_domain(WriteReachDefZone); alternatively, compute the zone
+ // before the first write or that are not written at all.
+ // { Element[] -> Scatter[] }
+ isl::union_set NonReachDef =
+ EltZoneUniverse.wrap().subtract(WriteReachDefZone.domain());
+
+ // { [Element[] -> Zone[]] -> ReachDefId[] }
+ isl::union_map DefZone =
+ WriteReachDefZone.unite(isl::union_map::from_domain(NonReachDef));
+
+ // { [Element[] -> Scatter[]] -> Element[] }
+ isl::union_map EltZoneElt = EltZoneUniverse.domain_map();
+
+ // { [Element[] -> Zone[]] -> [Element[] -> ReachDefId[]] }
+ isl::union_map DefZoneEltDefId = EltZoneElt.range_product(DefZone);
+
+ // { Element[] -> [Zone[] -> ReachDefId[]] }
+ isl::union_map EltDefZone = DefZone.curry();
+
+ // { [Element[] -> Zone[] -> [Element[] -> ReachDefId[]] }
+ isl::union_map EltZoneEltDefid = distributeDomain(EltDefZone);
+
+ // { [Element[] -> Scatter[]] -> DomainRead[] }
+ isl::union_map Reads = AllReads.range_product(Schedule).reverse();
+
+ // { [Element[] -> Scatter[]] -> [Element[] -> DomainRead[]] }
+ isl::union_map ReadsElt = EltZoneElt.range_product(Reads);
+
+ // { [Element[] -> Scatter[]] -> ValInst[] }
+ isl::union_map ScatterKnown = ReadsElt.apply_range(AllReadValInst);
+
+ // { [Element[] -> ReachDefId[]] -> ValInst[] }
+ isl::union_map DefidKnown =
+ DefZoneEltDefId.apply_domain(ScatterKnown).reverse();
+
+ // { [Element[] -> Zone[]] -> ValInst[] }
+ return DefZoneEltDefId.apply_range(DefidKnown);
+}
+
+isl::union_map ZoneAlgorithm::computeKnown(bool FromWrite,
+ bool FromRead) const {
+ isl::union_map Result = makeEmptyUnionMap();
+
+ if (FromWrite)
+ Result = Result.unite(computeKnownFromMustWrites());
+
+ if (FromRead)
+ Result = Result.unite(computeKnownFromLoad());
+
+ simplify(Result);
+ return Result;
+}
diff --git a/contrib/libs/llvm14/tools/polly/lib/ya.make b/contrib/libs/llvm14/tools/polly/lib/ya.make
new file mode 100644
index 00000000000..2cb500ccce9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/polly/lib/ya.make
@@ -0,0 +1,104 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(
+ Apache-2.0 WITH LLVM-exception AND
+ MIT
+)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/Linker
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Passes
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target
+ contrib/libs/llvm14/lib/Target/NVPTX
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Transforms/IPO
+ contrib/libs/llvm14/lib/Transforms/InstCombine
+ contrib/libs/llvm14/lib/Transforms/Scalar
+ contrib/libs/llvm14/lib/Transforms/Utils
+ contrib/libs/llvm14/lib/Transforms/Vectorize
+ contrib/libs/llvm14/tools/polly/lib/External/isl
+ contrib/libs/llvm14/tools/polly/lib/External/ppcg
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/polly/include
+ contrib/libs/llvm14/tools/polly/lib
+ contrib/libs/llvm14/tools/polly/lib/External
+ contrib/libs/llvm14/tools/polly/lib/External/isl/include
+ contrib/libs/llvm14/tools/polly/lib/External/pet/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ Analysis/DependenceInfo.cpp
+ Analysis/PolyhedralInfo.cpp
+ Analysis/PruneUnprofitable.cpp
+ Analysis/ScopBuilder.cpp
+ Analysis/ScopDetection.cpp
+ Analysis/ScopDetectionDiagnostic.cpp
+ Analysis/ScopGraphPrinter.cpp
+ Analysis/ScopInfo.cpp
+ Analysis/ScopPass.cpp
+ CodeGen/BlockGenerators.cpp
+ CodeGen/CodeGeneration.cpp
+ CodeGen/CodegenCleanup.cpp
+ CodeGen/IRBuilder.cpp
+ CodeGen/IslAst.cpp
+ CodeGen/IslExprBuilder.cpp
+ CodeGen/IslNodeBuilder.cpp
+ CodeGen/LoopGenerators.cpp
+ CodeGen/LoopGeneratorsGOMP.cpp
+ CodeGen/LoopGeneratorsKMP.cpp
+ CodeGen/ManagedMemoryRewrite.cpp
+ CodeGen/PPCGCodeGeneration.cpp
+ CodeGen/PerfMonitor.cpp
+ CodeGen/RuntimeDebugBuilder.cpp
+ CodeGen/Utils.cpp
+ Exchange/JSONExporter.cpp
+ Support/DumpFunctionPass.cpp
+ Support/DumpModulePass.cpp
+ Support/GICHelper.cpp
+ Support/ISLTools.cpp
+ Support/RegisterPasses.cpp
+ Support/SCEVAffinator.cpp
+ Support/SCEVValidator.cpp
+ Support/ScopHelper.cpp
+ Support/ScopLocation.cpp
+ Support/VirtualInstruction.cpp
+ Transform/Canonicalization.cpp
+ Transform/CodePreparation.cpp
+ Transform/DeLICM.cpp
+ Transform/DeadCodeElimination.cpp
+ Transform/FlattenAlgo.cpp
+ Transform/FlattenSchedule.cpp
+ Transform/ForwardOpTree.cpp
+ Transform/ManualOptimizer.cpp
+ Transform/MatmulOptimizer.cpp
+ Transform/MaximalStaticExpansion.cpp
+ Transform/ScheduleOptimizer.cpp
+ Transform/ScheduleTreeTransform.cpp
+ Transform/ScopInliner.cpp
+ Transform/Simplify.cpp
+ Transform/ZoneAlgo.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/remarks-shlib/libremarks.cpp b/contrib/libs/llvm14/tools/remarks-shlib/libremarks.cpp
new file mode 100644
index 00000000000..3f65217cbcc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/remarks-shlib/libremarks.cpp
@@ -0,0 +1,17 @@
+//===-libremarks.cpp - LLVM Remarks Shared Library ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Provide a library to work with remark diagnostics.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-c/Remarks.h"
+
+extern uint32_t LLVMRemarkVersion(void) {
+ return REMARKS_API_VERSION;
+}
diff --git a/contrib/libs/llvm14/tools/remarks-shlib/ya.make b/contrib/libs/llvm14/tools/remarks-shlib/ya.make
new file mode 100644
index 00000000000..dd091f2f57b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/remarks-shlib/ya.make
@@ -0,0 +1,26 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/Remarks
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/remarks-shlib
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ libremarks.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/sancov/sancov.cpp b/contrib/libs/llvm14/tools/sancov/sancov.cpp
new file mode 100644
index 00000000000..c997154bac4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/sancov/sancov.cpp
@@ -0,0 +1,1194 @@
+//===-- sancov.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// This file is a command-line tool for reading and analyzing sanitizer
+// coverage.
+//===----------------------------------------------------------------------===//
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstrAnalysis.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/MD5.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/SHA1.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <set>
+#include <vector>
+
+using namespace llvm;
+
+namespace {
+
+// --------- COMMAND LINE FLAGS ---------
+
+enum ActionType {
+ CoveredFunctionsAction,
+ HtmlReportAction,
+ MergeAction,
+ NotCoveredFunctionsAction,
+ PrintAction,
+ PrintCovPointsAction,
+ StatsAction,
+ SymbolizeAction
+};
+
+cl::opt<ActionType> Action(
+ cl::desc("Action (required)"), cl::Required,
+ cl::values(
+ clEnumValN(PrintAction, "print", "Print coverage addresses"),
+ clEnumValN(PrintCovPointsAction, "print-coverage-pcs",
+ "Print coverage instrumentation points addresses."),
+ clEnumValN(CoveredFunctionsAction, "covered-functions",
+ "Print all covered funcions."),
+ clEnumValN(NotCoveredFunctionsAction, "not-covered-functions",
+ "Print all not covered funcions."),
+ clEnumValN(StatsAction, "print-coverage-stats",
+ "Print coverage statistics."),
+ clEnumValN(HtmlReportAction, "html-report",
+ "REMOVED. Use -symbolize & coverage-report-server.py."),
+ clEnumValN(SymbolizeAction, "symbolize",
+ "Produces a symbolized JSON report from binary report."),
+ clEnumValN(MergeAction, "merge", "Merges reports.")));
+
+static cl::list<std::string>
+ ClInputFiles(cl::Positional, cl::OneOrMore,
+ cl::desc("<action> <binary files...> <.sancov files...> "
+ "<.symcov files...>"));
+
+static cl::opt<bool> ClDemangle("demangle", cl::init(true),
+ cl::desc("Print demangled function name."));
+
+static cl::opt<bool>
+ ClSkipDeadFiles("skip-dead-files", cl::init(true),
+ cl::desc("Do not list dead source files in reports."));
+
+static cl::opt<std::string> ClStripPathPrefix(
+ "strip_path_prefix", cl::init(""),
+ cl::desc("Strip this prefix from file paths in reports."));
+
+static cl::opt<std::string>
+ ClBlacklist("blacklist", cl::init(""),
+ cl::desc("Blacklist file (sanitizer blacklist format)."));
+
+static cl::opt<bool> ClUseDefaultBlacklist(
+ "use_default_blacklist", cl::init(true), cl::Hidden,
+ cl::desc("Controls if default blacklist should be used."));
+
+static const char *const DefaultBlacklistStr = "fun:__sanitizer_.*\n"
+ "src:/usr/include/.*\n"
+ "src:.*/libc\\+\\+/.*\n";
+
+// --------- FORMAT SPECIFICATION ---------
+
+struct FileHeader {
+ uint32_t Bitness;
+ uint32_t Magic;
+};
+
+static const uint32_t BinCoverageMagic = 0xC0BFFFFF;
+static const uint32_t Bitness32 = 0xFFFFFF32;
+static const uint32_t Bitness64 = 0xFFFFFF64;
+
+static const Regex SancovFileRegex("(.*)\\.[0-9]+\\.sancov");
+static const Regex SymcovFileRegex(".*\\.symcov");
+
+// --------- MAIN DATASTRUCTURES ----------
+
+// Contents of .sancov file: list of coverage point addresses that were
+// executed.
+struct RawCoverage {
+ explicit RawCoverage(std::unique_ptr<std::set<uint64_t>> Addrs)
+ : Addrs(std::move(Addrs)) {}
+
+ // Read binary .sancov file.
+ static ErrorOr<std::unique_ptr<RawCoverage>>
+ read(const std::string &FileName);
+
+ std::unique_ptr<std::set<uint64_t>> Addrs;
+};
+
+// Coverage point has an opaque Id and corresponds to multiple source locations.
+struct CoveragePoint {
+ explicit CoveragePoint(const std::string &Id) : Id(Id) {}
+
+ std::string Id;
+ SmallVector<DILineInfo, 1> Locs;
+};
+
+// Symcov file content: set of covered Ids plus information about all available
+// coverage points.
+struct SymbolizedCoverage {
+ // Read json .symcov file.
+ static std::unique_ptr<SymbolizedCoverage> read(const std::string &InputFile);
+
+ std::set<std::string> CoveredIds;
+ std::string BinaryHash;
+ std::vector<CoveragePoint> Points;
+};
+
+struct CoverageStats {
+ size_t AllPoints;
+ size_t CovPoints;
+ size_t AllFns;
+ size_t CovFns;
+};
+
+// --------- ERROR HANDLING ---------
+
+static void fail(const llvm::Twine &E) {
+ errs() << "ERROR: " << E << "\n";
+ exit(1);
+}
+
+static void failIf(bool B, const llvm::Twine &E) {
+ if (B)
+ fail(E);
+}
+
+static void failIfError(std::error_code Error) {
+ if (!Error)
+ return;
+ errs() << "ERROR: " << Error.message() << "(" << Error.value() << ")\n";
+ exit(1);
+}
+
+template <typename T> static void failIfError(const ErrorOr<T> &E) {
+ failIfError(E.getError());
+}
+
+static void failIfError(Error Err) {
+ if (Err) {
+ logAllUnhandledErrors(std::move(Err), errs(), "ERROR: ");
+ exit(1);
+ }
+}
+
+template <typename T> static void failIfError(Expected<T> &E) {
+ failIfError(E.takeError());
+}
+
+static void failIfNotEmpty(const llvm::Twine &E) {
+ if (E.str().empty())
+ return;
+ fail(E);
+}
+
+template <typename T>
+static void failIfEmpty(const std::unique_ptr<T> &Ptr,
+ const std::string &Message) {
+ if (Ptr.get())
+ return;
+ fail(Message);
+}
+
+// ----------- Coverage I/O ----------
+template <typename T>
+static void readInts(const char *Start, const char *End,
+ std::set<uint64_t> *Ints) {
+ const T *S = reinterpret_cast<const T *>(Start);
+ const T *E = reinterpret_cast<const T *>(End);
+ std::copy(S, E, std::inserter(*Ints, Ints->end()));
+}
+
+ErrorOr<std::unique_ptr<RawCoverage>>
+RawCoverage::read(const std::string &FileName) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(FileName);
+ if (!BufOrErr)
+ return BufOrErr.getError();
+ std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
+ if (Buf->getBufferSize() < 8) {
+ errs() << "File too small (<8): " << Buf->getBufferSize() << '\n';
+ return make_error_code(errc::illegal_byte_sequence);
+ }
+ const FileHeader *Header =
+ reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
+
+ if (Header->Magic != BinCoverageMagic) {
+ errs() << "Wrong magic: " << Header->Magic << '\n';
+ return make_error_code(errc::illegal_byte_sequence);
+ }
+
+ auto Addrs = std::make_unique<std::set<uint64_t>>();
+
+ switch (Header->Bitness) {
+ case Bitness64:
+ readInts<uint64_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
+ Addrs.get());
+ break;
+ case Bitness32:
+ readInts<uint32_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
+ Addrs.get());
+ break;
+ default:
+ errs() << "Unsupported bitness: " << Header->Bitness << '\n';
+ return make_error_code(errc::illegal_byte_sequence);
+ }
+
+ // Ignore slots that are zero, so a runtime implementation is not required
+ // to compactify the data.
+ Addrs->erase(0);
+
+ return std::unique_ptr<RawCoverage>(new RawCoverage(std::move(Addrs)));
+}
+
+// Print coverage addresses.
+raw_ostream &operator<<(raw_ostream &OS, const RawCoverage &CoverageData) {
+ for (auto Addr : *CoverageData.Addrs) {
+ OS << "0x";
+ OS.write_hex(Addr);
+ OS << "\n";
+ }
+ return OS;
+}
+
+static raw_ostream &operator<<(raw_ostream &OS, const CoverageStats &Stats) {
+ OS << "all-edges: " << Stats.AllPoints << "\n";
+ OS << "cov-edges: " << Stats.CovPoints << "\n";
+ OS << "all-functions: " << Stats.AllFns << "\n";
+ OS << "cov-functions: " << Stats.CovFns << "\n";
+ return OS;
+}
+
+// Output symbolized information for coverage points in JSON.
+// Format:
+// {
+// '<file_name>' : {
+// '<function_name>' : {
+// '<point_id'> : '<line_number>:'<column_number'.
+// ....
+// }
+// }
+// }
+static void operator<<(json::OStream &W,
+ const std::vector<CoveragePoint> &Points) {
+ // Group points by file.
+ std::map<std::string, std::vector<const CoveragePoint *>> PointsByFile;
+ for (const auto &Point : Points) {
+ for (const DILineInfo &Loc : Point.Locs) {
+ PointsByFile[Loc.FileName].push_back(&Point);
+ }
+ }
+
+ for (const auto &P : PointsByFile) {
+ std::string FileName = P.first;
+ std::map<std::string, std::vector<const CoveragePoint *>> PointsByFn;
+ for (auto PointPtr : P.second) {
+ for (const DILineInfo &Loc : PointPtr->Locs) {
+ PointsByFn[Loc.FunctionName].push_back(PointPtr);
+ }
+ }
+
+ W.attributeObject(P.first, [&] {
+ // Group points by function.
+ for (const auto &P : PointsByFn) {
+ std::string FunctionName = P.first;
+ std::set<std::string> WrittenIds;
+
+ W.attributeObject(FunctionName, [&] {
+ for (const CoveragePoint *Point : P.second) {
+ for (const auto &Loc : Point->Locs) {
+ if (Loc.FileName != FileName || Loc.FunctionName != FunctionName)
+ continue;
+ if (WrittenIds.find(Point->Id) != WrittenIds.end())
+ continue;
+
+ // Output <point_id> : "<line>:<col>".
+ WrittenIds.insert(Point->Id);
+ W.attribute(Point->Id,
+ (utostr(Loc.Line) + ":" + utostr(Loc.Column)));
+ }
+ }
+ });
+ }
+ });
+ }
+}
+
+static void operator<<(json::OStream &W, const SymbolizedCoverage &C) {
+ W.object([&] {
+ W.attributeArray("covered-points", [&] {
+ for (const std::string &P : C.CoveredIds) {
+ W.value(P);
+ }
+ });
+ W.attribute("binary-hash", C.BinaryHash);
+ W.attributeObject("point-symbol-info", [&] { W << C.Points; });
+ });
+}
+
+static std::string parseScalarString(yaml::Node *N) {
+ SmallString<64> StringStorage;
+ yaml::ScalarNode *S = dyn_cast<yaml::ScalarNode>(N);
+ failIf(!S, "expected string");
+ return std::string(S->getValue(StringStorage));
+}
+
+std::unique_ptr<SymbolizedCoverage>
+SymbolizedCoverage::read(const std::string &InputFile) {
+ auto Coverage(std::make_unique<SymbolizedCoverage>());
+
+ std::map<std::string, CoveragePoint> Points;
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(InputFile);
+ failIfError(BufOrErr);
+
+ SourceMgr SM;
+ yaml::Stream S(**BufOrErr, SM);
+
+ yaml::document_iterator DI = S.begin();
+ failIf(DI == S.end(), "empty document: " + InputFile);
+ yaml::Node *Root = DI->getRoot();
+ failIf(!Root, "expecting root node: " + InputFile);
+ yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);
+ failIf(!Top, "expecting mapping node: " + InputFile);
+
+ for (auto &KVNode : *Top) {
+ auto Key = parseScalarString(KVNode.getKey());
+
+ if (Key == "covered-points") {
+ yaml::SequenceNode *Points =
+ dyn_cast<yaml::SequenceNode>(KVNode.getValue());
+ failIf(!Points, "expected array: " + InputFile);
+
+ for (auto I = Points->begin(), E = Points->end(); I != E; ++I) {
+ Coverage->CoveredIds.insert(parseScalarString(&*I));
+ }
+ } else if (Key == "binary-hash") {
+ Coverage->BinaryHash = parseScalarString(KVNode.getValue());
+ } else if (Key == "point-symbol-info") {
+ yaml::MappingNode *PointSymbolInfo =
+ dyn_cast<yaml::MappingNode>(KVNode.getValue());
+ failIf(!PointSymbolInfo, "expected mapping node: " + InputFile);
+
+ for (auto &FileKVNode : *PointSymbolInfo) {
+ auto Filename = parseScalarString(FileKVNode.getKey());
+
+ yaml::MappingNode *FileInfo =
+ dyn_cast<yaml::MappingNode>(FileKVNode.getValue());
+ failIf(!FileInfo, "expected mapping node: " + InputFile);
+
+ for (auto &FunctionKVNode : *FileInfo) {
+ auto FunctionName = parseScalarString(FunctionKVNode.getKey());
+
+ yaml::MappingNode *FunctionInfo =
+ dyn_cast<yaml::MappingNode>(FunctionKVNode.getValue());
+ failIf(!FunctionInfo, "expected mapping node: " + InputFile);
+
+ for (auto &PointKVNode : *FunctionInfo) {
+ auto PointId = parseScalarString(PointKVNode.getKey());
+ auto Loc = parseScalarString(PointKVNode.getValue());
+
+ size_t ColonPos = Loc.find(':');
+ failIf(ColonPos == std::string::npos, "expected ':': " + InputFile);
+
+ auto LineStr = Loc.substr(0, ColonPos);
+ auto ColStr = Loc.substr(ColonPos + 1, Loc.size());
+
+ if (Points.find(PointId) == Points.end())
+ Points.insert(std::make_pair(PointId, CoveragePoint(PointId)));
+
+ DILineInfo LineInfo;
+ LineInfo.FileName = Filename;
+ LineInfo.FunctionName = FunctionName;
+ char *End;
+ LineInfo.Line = std::strtoul(LineStr.c_str(), &End, 10);
+ LineInfo.Column = std::strtoul(ColStr.c_str(), &End, 10);
+
+ CoveragePoint *CoveragePoint = &Points.find(PointId)->second;
+ CoveragePoint->Locs.push_back(LineInfo);
+ }
+ }
+ }
+ } else {
+ errs() << "Ignoring unknown key: " << Key << "\n";
+ }
+ }
+
+ for (auto &KV : Points) {
+ Coverage->Points.push_back(KV.second);
+ }
+
+ return Coverage;
+}
+
+// ---------- MAIN FUNCTIONALITY ----------
+
+std::string stripPathPrefix(std::string Path) {
+ if (ClStripPathPrefix.empty())
+ return Path;
+ size_t Pos = Path.find(ClStripPathPrefix);
+ if (Pos == std::string::npos)
+ return Path;
+ return Path.substr(Pos + ClStripPathPrefix.size());
+}
+
+static std::unique_ptr<symbolize::LLVMSymbolizer> createSymbolizer() {
+ symbolize::LLVMSymbolizer::Options SymbolizerOptions;
+ SymbolizerOptions.Demangle = ClDemangle;
+ SymbolizerOptions.UseSymbolTable = true;
+ return std::unique_ptr<symbolize::LLVMSymbolizer>(
+ new symbolize::LLVMSymbolizer(SymbolizerOptions));
+}
+
+static std::string normalizeFilename(const std::string &FileName) {
+ SmallString<256> S(FileName);
+ sys::path::remove_dots(S, /* remove_dot_dot */ true);
+ return stripPathPrefix(sys::path::convert_to_slash(std::string(S)));
+}
+
+class Blacklists {
+public:
+ Blacklists()
+ : DefaultBlacklist(createDefaultBlacklist()),
+ UserBlacklist(createUserBlacklist()) {}
+
+ bool isBlacklisted(const DILineInfo &I) {
+ if (DefaultBlacklist &&
+ DefaultBlacklist->inSection("sancov", "fun", I.FunctionName))
+ return true;
+ if (DefaultBlacklist &&
+ DefaultBlacklist->inSection("sancov", "src", I.FileName))
+ return true;
+ if (UserBlacklist &&
+ UserBlacklist->inSection("sancov", "fun", I.FunctionName))
+ return true;
+ if (UserBlacklist && UserBlacklist->inSection("sancov", "src", I.FileName))
+ return true;
+ return false;
+ }
+
+private:
+ static std::unique_ptr<SpecialCaseList> createDefaultBlacklist() {
+ if (!ClUseDefaultBlacklist)
+ return std::unique_ptr<SpecialCaseList>();
+ std::unique_ptr<MemoryBuffer> MB =
+ MemoryBuffer::getMemBuffer(DefaultBlacklistStr);
+ std::string Error;
+ auto Blacklist = SpecialCaseList::create(MB.get(), Error);
+ failIfNotEmpty(Error);
+ return Blacklist;
+ }
+
+ static std::unique_ptr<SpecialCaseList> createUserBlacklist() {
+ if (ClBlacklist.empty())
+ return std::unique_ptr<SpecialCaseList>();
+
+ return SpecialCaseList::createOrDie({{ClBlacklist}},
+ *vfs::getRealFileSystem());
+ }
+ std::unique_ptr<SpecialCaseList> DefaultBlacklist;
+ std::unique_ptr<SpecialCaseList> UserBlacklist;
+};
+
+static std::vector<CoveragePoint>
+getCoveragePoints(const std::string &ObjectFile,
+ const std::set<uint64_t> &Addrs,
+ const std::set<uint64_t> &CoveredAddrs) {
+ std::vector<CoveragePoint> Result;
+ auto Symbolizer(createSymbolizer());
+ Blacklists B;
+
+ std::set<std::string> CoveredFiles;
+ if (ClSkipDeadFiles) {
+ for (auto Addr : CoveredAddrs) {
+ // TODO: it would be neccessary to set proper section index here.
+ // object::SectionedAddress::UndefSection works for only absolute
+ // addresses.
+ object::SectionedAddress ModuleAddress = {
+ Addr, object::SectionedAddress::UndefSection};
+
+ auto LineInfo = Symbolizer->symbolizeCode(ObjectFile, ModuleAddress);
+ failIfError(LineInfo);
+ CoveredFiles.insert(LineInfo->FileName);
+ auto InliningInfo =
+ Symbolizer->symbolizeInlinedCode(ObjectFile, ModuleAddress);
+ failIfError(InliningInfo);
+ for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
+ auto FrameInfo = InliningInfo->getFrame(I);
+ CoveredFiles.insert(FrameInfo.FileName);
+ }
+ }
+ }
+
+ for (auto Addr : Addrs) {
+ std::set<DILineInfo> Infos; // deduplicate debug info.
+
+ // TODO: it would be neccessary to set proper section index here.
+ // object::SectionedAddress::UndefSection works for only absolute addresses.
+ object::SectionedAddress ModuleAddress = {
+ Addr, object::SectionedAddress::UndefSection};
+
+ auto LineInfo = Symbolizer->symbolizeCode(ObjectFile, ModuleAddress);
+ failIfError(LineInfo);
+ if (ClSkipDeadFiles &&
+ CoveredFiles.find(LineInfo->FileName) == CoveredFiles.end())
+ continue;
+ LineInfo->FileName = normalizeFilename(LineInfo->FileName);
+ if (B.isBlacklisted(*LineInfo))
+ continue;
+
+ auto Id = utohexstr(Addr, true);
+ auto Point = CoveragePoint(Id);
+ Infos.insert(*LineInfo);
+ Point.Locs.push_back(*LineInfo);
+
+ auto InliningInfo =
+ Symbolizer->symbolizeInlinedCode(ObjectFile, ModuleAddress);
+ failIfError(InliningInfo);
+ for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
+ auto FrameInfo = InliningInfo->getFrame(I);
+ if (ClSkipDeadFiles &&
+ CoveredFiles.find(FrameInfo.FileName) == CoveredFiles.end())
+ continue;
+ FrameInfo.FileName = normalizeFilename(FrameInfo.FileName);
+ if (B.isBlacklisted(FrameInfo))
+ continue;
+ if (Infos.find(FrameInfo) == Infos.end()) {
+ Infos.insert(FrameInfo);
+ Point.Locs.push_back(FrameInfo);
+ }
+ }
+
+ Result.push_back(Point);
+ }
+
+ return Result;
+}
+
+static bool isCoveragePointSymbol(StringRef Name) {
+ return Name == "__sanitizer_cov" || Name == "__sanitizer_cov_with_check" ||
+ Name == "__sanitizer_cov_trace_func_enter" ||
+ Name == "__sanitizer_cov_trace_pc_guard" ||
+ // Mac has '___' prefix
+ Name == "___sanitizer_cov" || Name == "___sanitizer_cov_with_check" ||
+ Name == "___sanitizer_cov_trace_func_enter" ||
+ Name == "___sanitizer_cov_trace_pc_guard";
+}
+
+// Locate __sanitizer_cov* function addresses inside the stubs table on MachO.
+static void findMachOIndirectCovFunctions(const object::MachOObjectFile &O,
+ std::set<uint64_t> *Result) {
+ MachO::dysymtab_command Dysymtab = O.getDysymtabLoadCommand();
+ MachO::symtab_command Symtab = O.getSymtabLoadCommand();
+
+ for (const auto &Load : O.load_commands()) {
+ if (Load.C.cmd == MachO::LC_SEGMENT_64) {
+ MachO::segment_command_64 Seg = O.getSegment64LoadCommand(Load);
+ for (unsigned J = 0; J < Seg.nsects; ++J) {
+ MachO::section_64 Sec = O.getSection64(Load, J);
+
+ uint32_t SectionType = Sec.flags & MachO::SECTION_TYPE;
+ if (SectionType == MachO::S_SYMBOL_STUBS) {
+ uint32_t Stride = Sec.reserved2;
+ uint32_t Cnt = Sec.size / Stride;
+ uint32_t N = Sec.reserved1;
+ for (uint32_t J = 0; J < Cnt && N + J < Dysymtab.nindirectsyms; J++) {
+ uint32_t IndirectSymbol =
+ O.getIndirectSymbolTableEntry(Dysymtab, N + J);
+ uint64_t Addr = Sec.addr + J * Stride;
+ if (IndirectSymbol < Symtab.nsyms) {
+ object::SymbolRef Symbol = *(O.getSymbolByIndex(IndirectSymbol));
+ Expected<StringRef> Name = Symbol.getName();
+ failIfError(Name);
+ if (isCoveragePointSymbol(Name.get())) {
+ Result->insert(Addr);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (Load.C.cmd == MachO::LC_SEGMENT) {
+ errs() << "ERROR: 32 bit MachO binaries not supported\n";
+ }
+ }
+}
+
+// Locate __sanitizer_cov* function addresses that are used for coverage
+// reporting.
+static std::set<uint64_t>
+findSanitizerCovFunctions(const object::ObjectFile &O) {
+ std::set<uint64_t> Result;
+
+ for (const object::SymbolRef &Symbol : O.symbols()) {
+ Expected<uint64_t> AddressOrErr = Symbol.getAddress();
+ failIfError(AddressOrErr);
+ uint64_t Address = AddressOrErr.get();
+
+ Expected<StringRef> NameOrErr = Symbol.getName();
+ failIfError(NameOrErr);
+ StringRef Name = NameOrErr.get();
+
+ Expected<uint32_t> FlagsOrErr = Symbol.getFlags();
+ // TODO: Test this error.
+ failIfError(FlagsOrErr);
+ uint32_t Flags = FlagsOrErr.get();
+
+ if (!(Flags & object::BasicSymbolRef::SF_Undefined) &&
+ isCoveragePointSymbol(Name)) {
+ Result.insert(Address);
+ }
+ }
+
+ if (const auto *CO = dyn_cast<object::COFFObjectFile>(&O)) {
+ for (const object::ExportDirectoryEntryRef &Export :
+ CO->export_directories()) {
+ uint32_t RVA;
+ failIfError(Export.getExportRVA(RVA));
+
+ StringRef Name;
+ failIfError(Export.getSymbolName(Name));
+
+ if (isCoveragePointSymbol(Name))
+ Result.insert(CO->getImageBase() + RVA);
+ }
+ }
+
+ if (const auto *MO = dyn_cast<object::MachOObjectFile>(&O)) {
+ findMachOIndirectCovFunctions(*MO, &Result);
+ }
+
+ 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'.
+static void getObjectCoveragePoints(const object::ObjectFile &O,
+ std::set<uint64_t> *Addrs) {
+ Triple TheTriple("unknown-unknown-unknown");
+ TheTriple.setArch(Triple::ArchType(O.getArch()));
+ auto TripleName = TheTriple.getTriple();
+
+ std::string Error;
+ const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error);
+ failIfNotEmpty(Error);
+
+ std::unique_ptr<const MCSubtargetInfo> STI(
+ TheTarget->createMCSubtargetInfo(TripleName, "", ""));
+ failIfEmpty(STI, "no subtarget info for target " + TripleName);
+
+ std::unique_ptr<const MCRegisterInfo> MRI(
+ TheTarget->createMCRegInfo(TripleName));
+ failIfEmpty(MRI, "no register info for target " + TripleName);
+
+ MCTargetOptions MCOptions;
+ std::unique_ptr<const MCAsmInfo> AsmInfo(
+ TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ failIfEmpty(AsmInfo, "no asm info for target " + TripleName);
+
+ MCContext Ctx(TheTriple, AsmInfo.get(), MRI.get(), STI.get());
+ std::unique_ptr<MCDisassembler> DisAsm(
+ TheTarget->createMCDisassembler(*STI, Ctx));
+ failIfEmpty(DisAsm, "no disassembler info for target " + TripleName);
+
+ std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo());
+ failIfEmpty(MII, "no instruction info for target " + TripleName);
+
+ std::unique_ptr<const MCInstrAnalysis> MIA(
+ TheTarget->createMCInstrAnalysis(MII.get()));
+ failIfEmpty(MIA, "no instruction analysis info for target " + TripleName);
+
+ auto SanCovAddrs = findSanitizerCovFunctions(O);
+ if (SanCovAddrs.empty())
+ fail("__sanitizer_cov* functions not found");
+
+ for (object::SectionRef Section : O.sections()) {
+ if (Section.isVirtual() || !Section.isText()) // llvm-objdump does the same.
+ continue;
+ uint64_t SectionAddr = Section.getAddress();
+ uint64_t SectSize = Section.getSize();
+ if (!SectSize)
+ continue;
+
+ Expected<StringRef> BytesStr = Section.getContents();
+ failIfError(BytesStr);
+ ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(*BytesStr);
+
+ for (uint64_t Index = 0, Size = 0; Index < Section.getSize();
+ Index += Size) {
+ MCInst Inst;
+ if (!DisAsm->getInstruction(Inst, Size, Bytes.slice(Index),
+ SectionAddr + Index, nulls())) {
+ if (Size == 0)
+ Size = 1;
+ continue;
+ }
+ uint64_t Addr = Index + SectionAddr;
+ // Sanitizer coverage uses the address of the next instruction - 1.
+ uint64_t CovPoint = getPreviousInstructionPc(Addr + Size, TheTriple);
+ uint64_t Target;
+ if (MIA->isCall(Inst) &&
+ MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target) &&
+ SanCovAddrs.find(Target) != SanCovAddrs.end())
+ Addrs->insert(CovPoint);
+ }
+ }
+}
+
+static void
+visitObjectFiles(const object::Archive &A,
+ function_ref<void(const object::ObjectFile &)> Fn) {
+ Error Err = Error::success();
+ for (auto &C : A.children(Err)) {
+ Expected<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary();
+ failIfError(ChildOrErr);
+ if (auto *O = dyn_cast<object::ObjectFile>(&*ChildOrErr.get()))
+ Fn(*O);
+ else
+ failIfError(object::object_error::invalid_file_type);
+ }
+ failIfError(std::move(Err));
+}
+
+static void
+visitObjectFiles(const std::string &FileName,
+ function_ref<void(const object::ObjectFile &)> Fn) {
+ Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
+ object::createBinary(FileName);
+ if (!BinaryOrErr)
+ failIfError(BinaryOrErr);
+
+ object::Binary &Binary = *BinaryOrErr.get().getBinary();
+ if (object::Archive *A = dyn_cast<object::Archive>(&Binary))
+ visitObjectFiles(*A, Fn);
+ else if (object::ObjectFile *O = dyn_cast<object::ObjectFile>(&Binary))
+ Fn(*O);
+ else
+ failIfError(object::object_error::invalid_file_type);
+}
+
+static std::set<uint64_t>
+findSanitizerCovFunctions(const std::string &FileName) {
+ std::set<uint64_t> Result;
+ visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
+ auto Addrs = findSanitizerCovFunctions(O);
+ Result.insert(Addrs.begin(), Addrs.end());
+ });
+ return Result;
+}
+
+// Locate addresses of all coverage points in a file. Coverage point
+// is defined as the 'address of instruction following __sanitizer_cov
+// call - 1'.
+static std::set<uint64_t> findCoveragePointAddrs(const std::string &FileName) {
+ std::set<uint64_t> Result;
+ visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
+ getObjectCoveragePoints(O, &Result);
+ });
+ return Result;
+}
+
+static void printCovPoints(const std::string &ObjFile, raw_ostream &OS) {
+ for (uint64_t Addr : findCoveragePointAddrs(ObjFile)) {
+ OS << "0x";
+ OS.write_hex(Addr);
+ OS << "\n";
+ }
+}
+
+static ErrorOr<bool> isCoverageFile(const std::string &FileName) {
+ auto ShortFileName = llvm::sys::path::filename(FileName);
+ if (!SancovFileRegex.match(ShortFileName))
+ return false;
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(FileName);
+ if (!BufOrErr) {
+ errs() << "Warning: " << BufOrErr.getError().message() << "("
+ << BufOrErr.getError().value()
+ << "), filename: " << llvm::sys::path::filename(FileName) << "\n";
+ return BufOrErr.getError();
+ }
+ std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
+ if (Buf->getBufferSize() < 8) {
+ return false;
+ }
+ const FileHeader *Header =
+ reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
+ return Header->Magic == BinCoverageMagic;
+}
+
+static bool isSymbolizedCoverageFile(const std::string &FileName) {
+ auto ShortFileName = llvm::sys::path::filename(FileName);
+ return SymcovFileRegex.match(ShortFileName);
+}
+
+static std::unique_ptr<SymbolizedCoverage>
+symbolize(const RawCoverage &Data, const std::string ObjectFile) {
+ auto Coverage = std::make_unique<SymbolizedCoverage>();
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(ObjectFile);
+ failIfError(BufOrErr);
+ SHA1 Hasher;
+ Hasher.update((*BufOrErr)->getBuffer());
+ Coverage->BinaryHash = toHex(Hasher.final());
+
+ Blacklists B;
+ auto Symbolizer(createSymbolizer());
+
+ for (uint64_t Addr : *Data.Addrs) {
+ // TODO: it would be neccessary to set proper section index here.
+ // object::SectionedAddress::UndefSection works for only absolute addresses.
+ auto LineInfo = Symbolizer->symbolizeCode(
+ ObjectFile, {Addr, object::SectionedAddress::UndefSection});
+ failIfError(LineInfo);
+ if (B.isBlacklisted(*LineInfo))
+ continue;
+
+ Coverage->CoveredIds.insert(utohexstr(Addr, true));
+ }
+
+ std::set<uint64_t> AllAddrs = findCoveragePointAddrs(ObjectFile);
+ if (!std::includes(AllAddrs.begin(), AllAddrs.end(), Data.Addrs->begin(),
+ Data.Addrs->end())) {
+ fail("Coverage points in binary and .sancov file do not match.");
+ }
+ Coverage->Points = getCoveragePoints(ObjectFile, AllAddrs, *Data.Addrs);
+ return Coverage;
+}
+
+struct FileFn {
+ bool operator<(const FileFn &RHS) const {
+ return std::tie(FileName, FunctionName) <
+ std::tie(RHS.FileName, RHS.FunctionName);
+ }
+
+ std::string FileName;
+ std::string FunctionName;
+};
+
+static std::set<FileFn>
+computeFunctions(const std::vector<CoveragePoint> &Points) {
+ std::set<FileFn> Fns;
+ for (const auto &Point : Points) {
+ for (const auto &Loc : Point.Locs) {
+ Fns.insert(FileFn{Loc.FileName, Loc.FunctionName});
+ }
+ }
+ return Fns;
+}
+
+static std::set<FileFn>
+computeNotCoveredFunctions(const SymbolizedCoverage &Coverage) {
+ auto Fns = computeFunctions(Coverage.Points);
+
+ for (const auto &Point : Coverage.Points) {
+ if (Coverage.CoveredIds.find(Point.Id) == Coverage.CoveredIds.end())
+ continue;
+
+ for (const auto &Loc : Point.Locs) {
+ Fns.erase(FileFn{Loc.FileName, Loc.FunctionName});
+ }
+ }
+
+ return Fns;
+}
+
+static std::set<FileFn>
+computeCoveredFunctions(const SymbolizedCoverage &Coverage) {
+ auto AllFns = computeFunctions(Coverage.Points);
+ std::set<FileFn> Result;
+
+ for (const auto &Point : Coverage.Points) {
+ if (Coverage.CoveredIds.find(Point.Id) == Coverage.CoveredIds.end())
+ continue;
+
+ for (const auto &Loc : Point.Locs) {
+ Result.insert(FileFn{Loc.FileName, Loc.FunctionName});
+ }
+ }
+
+ return Result;
+}
+
+typedef std::map<FileFn, std::pair<uint32_t, uint32_t>> FunctionLocs;
+// finds first location in a file for each function.
+static FunctionLocs resolveFunctions(const SymbolizedCoverage &Coverage,
+ const std::set<FileFn> &Fns) {
+ FunctionLocs Result;
+ for (const auto &Point : Coverage.Points) {
+ for (const auto &Loc : Point.Locs) {
+ FileFn Fn = FileFn{Loc.FileName, Loc.FunctionName};
+ if (Fns.find(Fn) == Fns.end())
+ continue;
+
+ auto P = std::make_pair(Loc.Line, Loc.Column);
+ auto I = Result.find(Fn);
+ if (I == Result.end() || I->second > P) {
+ Result[Fn] = P;
+ }
+ }
+ }
+ return Result;
+}
+
+static void printFunctionLocs(const FunctionLocs &FnLocs, raw_ostream &OS) {
+ for (const auto &P : FnLocs) {
+ OS << stripPathPrefix(P.first.FileName) << ":" << P.second.first << " "
+ << P.first.FunctionName << "\n";
+ }
+}
+CoverageStats computeStats(const SymbolizedCoverage &Coverage) {
+ CoverageStats Stats = {Coverage.Points.size(), Coverage.CoveredIds.size(),
+ computeFunctions(Coverage.Points).size(),
+ computeCoveredFunctions(Coverage).size()};
+ return Stats;
+}
+
+// Print list of covered functions.
+// Line format: <file_name>:<line> <function_name>
+static void printCoveredFunctions(const SymbolizedCoverage &CovData,
+ raw_ostream &OS) {
+ auto CoveredFns = computeCoveredFunctions(CovData);
+ printFunctionLocs(resolveFunctions(CovData, CoveredFns), OS);
+}
+
+// Print list of not covered functions.
+// Line format: <file_name>:<line> <function_name>
+static void printNotCoveredFunctions(const SymbolizedCoverage &CovData,
+ raw_ostream &OS) {
+ auto NotCoveredFns = computeNotCoveredFunctions(CovData);
+ printFunctionLocs(resolveFunctions(CovData, NotCoveredFns), OS);
+}
+
+// Read list of files and merges their coverage info.
+static void readAndPrintRawCoverage(const std::vector<std::string> &FileNames,
+ raw_ostream &OS) {
+ std::vector<std::unique_ptr<RawCoverage>> Covs;
+ for (const auto &FileName : FileNames) {
+ auto Cov = RawCoverage::read(FileName);
+ if (!Cov)
+ continue;
+ OS << *Cov.get();
+ }
+}
+
+static std::unique_ptr<SymbolizedCoverage>
+merge(const std::vector<std::unique_ptr<SymbolizedCoverage>> &Coverages) {
+ if (Coverages.empty())
+ return nullptr;
+
+ auto Result = std::make_unique<SymbolizedCoverage>();
+
+ for (size_t I = 0; I < Coverages.size(); ++I) {
+ const SymbolizedCoverage &Coverage = *Coverages[I];
+ std::string Prefix;
+ if (Coverages.size() > 1) {
+ // prefix is not needed when there's only one file.
+ Prefix = utostr(I);
+ }
+
+ for (const auto &Id : Coverage.CoveredIds) {
+ Result->CoveredIds.insert(Prefix + Id);
+ }
+
+ for (const auto &CovPoint : Coverage.Points) {
+ CoveragePoint NewPoint(CovPoint);
+ NewPoint.Id = Prefix + CovPoint.Id;
+ Result->Points.push_back(NewPoint);
+ }
+ }
+
+ if (Coverages.size() == 1) {
+ Result->BinaryHash = Coverages[0]->BinaryHash;
+ }
+
+ return Result;
+}
+
+static std::unique_ptr<SymbolizedCoverage>
+readSymbolizeAndMergeCmdArguments(std::vector<std::string> FileNames) {
+ std::vector<std::unique_ptr<SymbolizedCoverage>> Coverages;
+
+ {
+ // Short name => file name.
+ std::map<std::string, std::string> ObjFiles;
+ std::string FirstObjFile;
+ std::set<std::string> CovFiles;
+
+ // Partition input values into coverage/object files.
+ for (const auto &FileName : FileNames) {
+ if (isSymbolizedCoverageFile(FileName)) {
+ Coverages.push_back(SymbolizedCoverage::read(FileName));
+ }
+
+ auto ErrorOrIsCoverage = isCoverageFile(FileName);
+ if (!ErrorOrIsCoverage)
+ continue;
+ if (ErrorOrIsCoverage.get()) {
+ CovFiles.insert(FileName);
+ } else {
+ auto ShortFileName = llvm::sys::path::filename(FileName);
+ if (ObjFiles.find(std::string(ShortFileName)) != ObjFiles.end()) {
+ fail("Duplicate binary file with a short name: " + ShortFileName);
+ }
+
+ ObjFiles[std::string(ShortFileName)] = FileName;
+ if (FirstObjFile.empty())
+ FirstObjFile = FileName;
+ }
+ }
+
+ SmallVector<StringRef, 2> Components;
+
+ // Object file => list of corresponding coverage file names.
+ std::map<std::string, std::vector<std::string>> CoverageByObjFile;
+ for (const auto &FileName : CovFiles) {
+ auto ShortFileName = llvm::sys::path::filename(FileName);
+ auto Ok = SancovFileRegex.match(ShortFileName, &Components);
+ if (!Ok) {
+ fail("Can't match coverage file name against "
+ "<module_name>.<pid>.sancov pattern: " +
+ FileName);
+ }
+
+ auto Iter = ObjFiles.find(std::string(Components[1]));
+ if (Iter == ObjFiles.end()) {
+ fail("Object file for coverage not found: " + FileName);
+ }
+
+ CoverageByObjFile[Iter->second].push_back(FileName);
+ };
+
+ for (const auto &Pair : ObjFiles) {
+ auto FileName = Pair.second;
+ if (CoverageByObjFile.find(FileName) == CoverageByObjFile.end())
+ errs() << "WARNING: No coverage file for " << FileName << "\n";
+ }
+
+ // Read raw coverage and symbolize it.
+ for (const auto &Pair : CoverageByObjFile) {
+ if (findSanitizerCovFunctions(Pair.first).empty()) {
+ errs()
+ << "WARNING: Ignoring " << Pair.first
+ << " and its coverage because __sanitizer_cov* functions were not "
+ "found.\n";
+ continue;
+ }
+
+ for (const std::string &CoverageFile : Pair.second) {
+ auto DataOrError = RawCoverage::read(CoverageFile);
+ failIfError(DataOrError);
+ Coverages.push_back(symbolize(*DataOrError.get(), Pair.first));
+ }
+ }
+ }
+
+ return merge(Coverages);
+}
+
+} // namespace
+
+int main(int Argc, char **Argv) {
+ llvm::InitLLVM X(Argc, Argv);
+
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllDisassemblers();
+
+ cl::ParseCommandLineOptions(Argc, Argv,
+ "Sanitizer Coverage Processing Tool (sancov)\n\n"
+ " This tool can extract various coverage-related information from: \n"
+ " coverage-instrumented binary files, raw .sancov files and their "
+ "symbolized .symcov version.\n"
+ " Depending on chosen action the tool expects different input files:\n"
+ " -print-coverage-pcs - coverage-instrumented binary files\n"
+ " -print-coverage - .sancov files\n"
+ " <other actions> - .sancov files & corresponding binary "
+ "files, .symcov files\n"
+ );
+
+ // -print doesn't need object files.
+ if (Action == PrintAction) {
+ readAndPrintRawCoverage(ClInputFiles, outs());
+ return 0;
+ } else if (Action == PrintCovPointsAction) {
+ // -print-coverage-points doesn't need coverage files.
+ for (const std::string &ObjFile : ClInputFiles) {
+ printCovPoints(ObjFile, outs());
+ }
+ return 0;
+ }
+
+ auto Coverage = readSymbolizeAndMergeCmdArguments(ClInputFiles);
+ failIf(!Coverage, "No valid coverage files given.");
+
+ switch (Action) {
+ case CoveredFunctionsAction: {
+ printCoveredFunctions(*Coverage, outs());
+ return 0;
+ }
+ case NotCoveredFunctionsAction: {
+ printNotCoveredFunctions(*Coverage, outs());
+ return 0;
+ }
+ case StatsAction: {
+ outs() << computeStats(*Coverage);
+ return 0;
+ }
+ case MergeAction:
+ case SymbolizeAction: { // merge & symbolize are synonims.
+ json::OStream W(outs(), 2);
+ W << *Coverage;
+ return 0;
+ }
+ case HtmlReportAction:
+ errs() << "-html-report option is removed: "
+ "use -symbolize & coverage-report-server.py instead\n";
+ return 1;
+ case PrintAction:
+ case PrintCovPointsAction:
+ llvm_unreachable("unsupported action");
+ }
+}
diff --git a/contrib/libs/llvm14/tools/sancov/ya.make b/contrib/libs/llvm14/tools/sancov/ya.make
new file mode 100644
index 00000000000..352f673ffd4
--- /dev/null
+++ b/contrib/libs/llvm14/tools/sancov/ya.make
@@ -0,0 +1,61 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/DebugInfo/MSF
+ contrib/libs/llvm14/lib/DebugInfo/PDB
+ contrib/libs/llvm14/lib/DebugInfo/Symbolize
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCDisassembler
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/Target/AArch64/Disassembler
+ contrib/libs/llvm14/lib/Target/AArch64/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/AArch64/TargetInfo
+ contrib/libs/llvm14/lib/Target/AArch64/Utils
+ contrib/libs/llvm14/lib/Target/ARM/Disassembler
+ contrib/libs/llvm14/lib/Target/ARM/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/ARM/TargetInfo
+ contrib/libs/llvm14/lib/Target/ARM/Utils
+ contrib/libs/llvm14/lib/Target/BPF/Disassembler
+ contrib/libs/llvm14/lib/Target/BPF/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/BPF/TargetInfo
+ contrib/libs/llvm14/lib/Target/NVPTX/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/NVPTX/TargetInfo
+ contrib/libs/llvm14/lib/Target/PowerPC/Disassembler
+ contrib/libs/llvm14/lib/Target/PowerPC/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/PowerPC/TargetInfo
+ contrib/libs/llvm14/lib/Target/X86/Disassembler
+ contrib/libs/llvm14/lib/Target/X86/MCTargetDesc
+ contrib/libs/llvm14/lib/Target/X86/TargetInfo
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/sancov
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ sancov.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/sanstats/sanstats.cpp b/contrib/libs/llvm14/tools/sanstats/sanstats.cpp
new file mode 100644
index 00000000000..51bf95e730f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/sanstats/sanstats.cpp
@@ -0,0 +1,150 @@
+//===- sanstats.cpp - Sanitizer statistics dumper -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This tool dumps statistics information from files in the format produced
+// by clang's -fsanitize-stats feature.
+//
+//===----------------------------------------------------------------------===//
+
+#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>
+
+using namespace llvm;
+
+static cl::opt<std::string> ClInputFile(cl::Positional, cl::Required,
+ cl::desc("<filename>"));
+
+static cl::opt<bool> ClDemangle("demangle", cl::init(false),
+ cl::desc("Print demangled function name."));
+
+inline uint64_t KindFromData(uint64_t Data, char SizeofPtr) {
+ return Data >> (SizeofPtr * 8 - kSanitizerStatKindBits);
+}
+
+inline uint64_t CountFromData(uint64_t Data, char SizeofPtr) {
+ return Data & ((1ull << (SizeofPtr * 8 - kSanitizerStatKindBits)) - 1);
+}
+
+static uint64_t ReadLE(char Size, const char *Begin, const char *End) {
+ uint64_t Result = 0;
+ char Pos = 0;
+ while (Begin < End && Pos != Size) {
+ Result |= uint64_t(uint8_t(*Begin)) << (Pos * 8);
+ ++Begin;
+ ++Pos;
+ }
+ return Result;
+}
+
+static const char *ReadModule(char SizeofPtr, const char *Begin,
+ const char *End) {
+ const char *FilenameBegin = Begin;
+ while (Begin != End && *Begin)
+ ++Begin;
+ if (Begin == End)
+ return nullptr;
+ 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)
+ return nullptr;
+
+ symbolize::LLVMSymbolizer::Options SymbolizerOptions;
+ SymbolizerOptions.Demangle = ClDemangle;
+ SymbolizerOptions.UseSymbolTable = true;
+ symbolize::LLVMSymbolizer Symbolizer(SymbolizerOptions);
+
+ while (true) {
+ uint64_t Addr = ReadLE(SizeofPtr, Begin, End);
+ Begin += SizeofPtr;
+ uint64_t Data = ReadLE(SizeofPtr, Begin, End);
+ Begin += SizeofPtr;
+
+ if (Begin > End)
+ return nullptr;
+ if (Addr == 0 && Data == 0)
+ return Begin;
+ if (Begin == End)
+ return nullptr;
+
+ // As the instrumentation tracks the return address and not
+ // the address of the call to `__sanitizer_stat_report` we
+ // remove one from the address to get the correct DI.
+ // TODO: it would be neccessary to set proper section index here.
+ // object::SectionedAddress::UndefSection works for only absolute addresses.
+ if (Expected<DILineInfo> LineInfo = Symbolizer.symbolizeCode(
+ Filename, {Addr - 1, object::SectionedAddress::UndefSection})) {
+ llvm::outs() << format_hex(Addr - 1, 18) << ' ' << LineInfo->FileName
+ << ':' << LineInfo->Line << ' ' << LineInfo->FunctionName
+ << ' ';
+ } else {
+ logAllUnhandledErrors(LineInfo.takeError(), llvm::outs(), "<error> ");
+ }
+
+ switch (KindFromData(Data, SizeofPtr)) {
+ case SanStat_CFI_VCall:
+ llvm::outs() << "cfi-vcall";
+ break;
+ case SanStat_CFI_NVCall:
+ llvm::outs() << "cfi-nvcall";
+ break;
+ case SanStat_CFI_DerivedCast:
+ llvm::outs() << "cfi-derived-cast";
+ break;
+ case SanStat_CFI_UnrelatedCast:
+ llvm::outs() << "cfi-unrelated-cast";
+ break;
+ case SanStat_CFI_ICall:
+ llvm::outs() << "cfi-icall";
+ break;
+ default:
+ llvm::outs() << "<unknown>";
+ break;
+ }
+
+ llvm::outs() << " " << CountFromData(Data, SizeofPtr) << '\n';
+ }
+}
+
+int main(int argc, char **argv) {
+ cl::ParseCommandLineOptions(argc, argv,
+ "Sanitizer Statistics Processing Tool");
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile(
+ ClInputFile, /*IsText=*/false, /*RequiresNullTerminator=*/false);
+ if (!MBOrErr) {
+ errs() << argv[0] << ": " << ClInputFile << ": "
+ << MBOrErr.getError().message() << '\n';
+ return 1;
+ }
+ std::unique_ptr<MemoryBuffer> MB = std::move(MBOrErr.get());
+ const char *Begin = MB->getBufferStart(), *End = MB->getBufferEnd();
+ if (Begin == End) {
+ errs() << argv[0] << ": " << ClInputFile << ": short read\n";
+ return 1;
+ }
+ char SizeofPtr = *Begin++;
+ while (Begin != End) {
+ Begin = ReadModule(SizeofPtr, Begin, End);
+ if (Begin == nullptr) {
+ errs() << argv[0] << ": " << ClInputFile << ": short read\n";
+ return 1;
+ }
+ assert(Begin <= End);
+ }
+}
diff --git a/contrib/libs/llvm14/tools/sanstats/ya.make b/contrib/libs/llvm14/tools/sanstats/ya.make
new file mode 100644
index 00000000000..15ae5087b2a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/sanstats/ya.make
@@ -0,0 +1,42 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/DebugInfo/MSF
+ contrib/libs/llvm14/lib/DebugInfo/PDB
+ contrib/libs/llvm14/lib/DebugInfo/Symbolize
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/sanstats
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ sanstats.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/split-file/split-file.cpp b/contrib/libs/llvm14/tools/split-file/split-file.cpp
new file mode 100644
index 00000000000..4a92c1be78a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/split-file/split-file.cpp
@@ -0,0 +1,178 @@
+//===- split-file.cpp - Input splitting utility ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Split input into multipe parts separated by regex '^(.|//)--- ' and extract
+// the specified part.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+
+static cl::OptionCategory cat("split-file Options");
+
+static cl::opt<std::string> input(cl::Positional, cl::desc("filename"),
+ cl::cat(cat));
+
+static cl::opt<std::string> output(cl::Positional, cl::desc("directory"),
+ cl::value_desc("directory"), cl::cat(cat));
+
+static cl::opt<bool> leadingLines("leading-lines",
+ cl::desc("Preserve line numbers"),
+ cl::cat(cat));
+
+static cl::opt<bool> noLeadingLines("no-leading-lines",
+ cl::desc("Don't preserve line numbers (default)"),
+ cl::cat(cat));
+
+static StringRef toolName;
+static int errorCount;
+
+[[noreturn]] static void fatal(StringRef filename, const Twine &message) {
+ if (filename.empty())
+ WithColor::error(errs(), toolName) << message << '\n';
+ else
+ WithColor::error(errs(), toolName) << filename << ": " << message << '\n';
+ exit(1);
+}
+
+static void error(StringRef filename, int64_t line, const Twine &message) {
+ ++errorCount;
+ errs() << filename << ':' << line << ": ";
+ WithColor::error(errs()) << message << '\n';
+}
+
+namespace {
+struct Part {
+ const char *begin = nullptr;
+ const char *end = nullptr;
+ int64_t leadingLines = 0;
+};
+} // namespace
+
+static int handle(MemoryBuffer &inputBuf, StringRef input) {
+ DenseMap<StringRef, Part> partToBegin;
+ StringRef lastPart, separator;
+ StringRef EOL = inputBuf.getBuffer().detectEOL();
+ for (line_iterator i(inputBuf, /*SkipBlanks=*/false, '\0'); !i.is_at_eof();) {
+ const int64_t lineNo = i.line_number();
+ const StringRef line = *i++;
+ const size_t markerLen = line.startswith("//") ? 6 : 5;
+ if (!(line.size() >= markerLen &&
+ line.substr(markerLen - 4).startswith("--- ")))
+ continue;
+ separator = line.substr(0, markerLen);
+ const StringRef partName = line.substr(markerLen);
+ if (partName.empty()) {
+ error(input, lineNo, "empty part name");
+ continue;
+ }
+ if (isSpace(partName.front()) || isSpace(partName.back())) {
+ error(input, lineNo, "part name cannot have leading or trailing space");
+ continue;
+ }
+
+ auto res = partToBegin.try_emplace(partName);
+ if (!res.second) {
+ error(input, lineNo,
+ "'" + separator + partName + "' occurs more than once");
+ continue;
+ }
+ if (!lastPart.empty())
+ partToBegin[lastPart].end = line.data();
+ Part &cur = res.first->second;
+ if (!i.is_at_eof())
+ cur.begin = i->data();
+ // If --leading-lines is specified, numEmptyLines is 0. Append newlines so
+ // that the extracted part preserves line numbers.
+ cur.leadingLines = leadingLines ? i.line_number() - 1 : 0;
+
+ lastPart = partName;
+ }
+ if (lastPart.empty())
+ fatal(input, "no part separator was found");
+ if (errorCount)
+ return 1;
+ partToBegin[lastPart].end = inputBuf.getBufferEnd();
+
+ std::vector<std::unique_ptr<ToolOutputFile>> outputFiles;
+ SmallString<256> partPath;
+ for (auto &keyValue : partToBegin) {
+ partPath.clear();
+ sys::path::append(partPath, output, keyValue.first);
+ std::error_code ec =
+ sys::fs::create_directories(sys::path::parent_path(partPath));
+ if (ec)
+ fatal(input, ec.message());
+ auto f = std::make_unique<ToolOutputFile>(partPath.str(), ec,
+ llvm::sys::fs::OF_None);
+ if (!f)
+ fatal(input, ec.message());
+
+ Part &part = keyValue.second;
+ for (int64_t i = 0; i != part.leadingLines; ++i)
+ (*f).os() << EOL;
+ if (part.begin)
+ (*f).os().write(part.begin, part.end - part.begin);
+ outputFiles.push_back(std::move(f));
+ }
+
+ for (std::unique_ptr<ToolOutputFile> &outputFile : outputFiles)
+ outputFile->keep();
+ return 0;
+}
+
+int main(int argc, const char **argv) {
+ toolName = sys::path::stem(argv[0]);
+ cl::HideUnrelatedOptions({&cat});
+ cl::ParseCommandLineOptions(
+ argc, argv,
+ "Split input into multiple parts separated by regex '^(.|//)--- ' and "
+ "extract the part specified by '^(.|//)--- <part>'\n",
+ nullptr,
+ /*EnvVar=*/nullptr,
+ /*LongOptionsUseDoubleDash=*/true);
+
+ if (input.empty())
+ fatal("", "input filename is not specified");
+ if (output.empty())
+ fatal("", "output directory is not specified");
+ ErrorOr<std::unique_ptr<MemoryBuffer>> bufferOrErr =
+ MemoryBuffer::getFileOrSTDIN(input);
+ if (std::error_code ec = bufferOrErr.getError())
+ fatal(input, ec.message());
+
+ // Delete output if it is a file or an empty directory, so that we can create
+ // a directory.
+ sys::fs::file_status status;
+ if (std::error_code ec = sys::fs::status(output, status))
+ if (ec.value() != static_cast<int>(std::errc::no_such_file_or_directory))
+ fatal(output, ec.message());
+ if (status.type() != sys::fs::file_type::file_not_found &&
+ status.type() != sys::fs::file_type::directory_file &&
+ status.type() != sys::fs::file_type::regular_file)
+ fatal(output, "output cannot be a special file");
+ if (std::error_code ec = sys::fs::remove(output, /*IgnoreNonExisting=*/true))
+ if (ec.value() != static_cast<int>(std::errc::directory_not_empty) &&
+ ec.value() != static_cast<int>(std::errc::file_exists))
+ fatal(output, ec.message());
+ return handle(**bufferOrErr, input);
+}
diff --git a/contrib/libs/llvm14/tools/split-file/ya.make b/contrib/libs/llvm14/tools/split-file/ya.make
new file mode 100644
index 00000000000..63fd89e8296
--- /dev/null
+++ b/contrib/libs/llvm14/tools/split-file/ya.make
@@ -0,0 +1,27 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/split-file
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ split-file.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/verify-uselistorder/verify-uselistorder.cpp b/contrib/libs/llvm14/tools/verify-uselistorder/verify-uselistorder.cpp
new file mode 100644
index 00000000000..dd45209a134
--- /dev/null
+++ b/contrib/libs/llvm14/tools/verify-uselistorder/verify-uselistorder.cpp
@@ -0,0 +1,574 @@
+//===- verify-uselistorder.cpp - The LLVM Modular Optimizer ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Verify that use-list order can be serialized correctly. After reading the
+// provided IR, this tool shuffles the use-lists and then writes and reads to a
+// separate Module whose use-list orders are compared to the original.
+//
+// The shuffles are deterministic, but guarantee that use-lists will change.
+// The algorithm per iteration is as follows:
+//
+// 1. Seed the random number generator. The seed is different for each
+// shuffle. Shuffle 0 uses default+0, shuffle 1 uses default+1, and so on.
+//
+// 2. Visit every Value in a deterministic order.
+//
+// 3. Assign a random number to each Use in the Value's use-list in order.
+//
+// 4. If the numbers are already in order, reassign numbers until they aren't.
+//
+// 5. Sort the use-list using Value::sortUseList(), which is a stable sort.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/UseListOrder.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/SystemUtils.h"
+#include "llvm/Support/raw_ostream.h"
+#include <random>
+#include <vector>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "uselistorder"
+
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input bitcode file>"),
+ cl::init("-"),
+ cl::value_desc("filename"));
+
+static cl::opt<bool> SaveTemps("save-temps", cl::desc("Save temp files"),
+ cl::init(false));
+
+static cl::opt<unsigned>
+ NumShuffles("num-shuffles",
+ cl::desc("Number of times to shuffle and verify use-lists"),
+ cl::init(1));
+
+namespace {
+
+struct TempFile {
+ std::string Filename;
+ FileRemover Remover;
+ bool init(const std::string &Ext);
+ bool writeBitcode(const Module &M) const;
+ bool writeAssembly(const Module &M) const;
+ std::unique_ptr<Module> readBitcode(LLVMContext &Context) const;
+ std::unique_ptr<Module> readAssembly(LLVMContext &Context) const;
+};
+
+struct ValueMapping {
+ DenseMap<const Value *, unsigned> IDs;
+ std::vector<const Value *> Values;
+
+ /// Construct a value mapping for module.
+ ///
+ /// Creates mapping from every value in \c M to an ID. This mapping includes
+ /// un-referencable values.
+ ///
+ /// Every \a Value that gets serialized in some way should be represented
+ /// here. The order needs to be deterministic, but it's unnecessary to match
+ /// the value-ids in the bitcode writer.
+ ///
+ /// All constants that are referenced by other values are included in the
+ /// mapping, but others -- which wouldn't be serialized -- are not.
+ ValueMapping(const Module &M);
+
+ /// Map a value.
+ ///
+ /// Maps a value. If it's a constant, maps all of its operands first.
+ void map(const Value *V);
+ unsigned lookup(const Value *V) const { return IDs.lookup(V); }
+};
+
+} // end namespace
+
+bool TempFile::init(const std::string &Ext) {
+ SmallVector<char, 64> Vector;
+ LLVM_DEBUG(dbgs() << " - create-temp-file\n");
+ if (auto EC = sys::fs::createTemporaryFile("uselistorder", Ext, Vector)) {
+ errs() << "verify-uselistorder: error: " << EC.message() << "\n";
+ return true;
+ }
+ assert(!Vector.empty());
+
+ Filename.assign(Vector.data(), Vector.data() + Vector.size());
+ Remover.setFile(Filename, !SaveTemps);
+ if (SaveTemps)
+ outs() << " - filename = " << Filename << "\n";
+ return false;
+}
+
+bool TempFile::writeBitcode(const Module &M) const {
+ LLVM_DEBUG(dbgs() << " - write bitcode\n");
+ std::error_code EC;
+ raw_fd_ostream OS(Filename, EC, sys::fs::OF_None);
+ if (EC) {
+ errs() << "verify-uselistorder: error: " << EC.message() << "\n";
+ return true;
+ }
+
+ WriteBitcodeToFile(M, OS, /* ShouldPreserveUseListOrder */ true);
+ return false;
+}
+
+bool TempFile::writeAssembly(const Module &M) const {
+ LLVM_DEBUG(dbgs() << " - write assembly\n");
+ std::error_code EC;
+ raw_fd_ostream OS(Filename, EC, sys::fs::OF_TextWithCRLF);
+ if (EC) {
+ errs() << "verify-uselistorder: error: " << EC.message() << "\n";
+ return true;
+ }
+
+ M.print(OS, nullptr, /* ShouldPreserveUseListOrder */ true);
+ return false;
+}
+
+std::unique_ptr<Module> TempFile::readBitcode(LLVMContext &Context) const {
+ LLVM_DEBUG(dbgs() << " - read bitcode\n");
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOr =
+ MemoryBuffer::getFile(Filename);
+ if (!BufferOr) {
+ errs() << "verify-uselistorder: error: " << BufferOr.getError().message()
+ << "\n";
+ return nullptr;
+ }
+
+ MemoryBuffer *Buffer = BufferOr.get().get();
+ Expected<std::unique_ptr<Module>> ModuleOr =
+ parseBitcodeFile(Buffer->getMemBufferRef(), Context);
+ if (!ModuleOr) {
+ logAllUnhandledErrors(ModuleOr.takeError(), errs(),
+ "verify-uselistorder: error: ");
+ return nullptr;
+ }
+ return std::move(ModuleOr.get());
+}
+
+std::unique_ptr<Module> TempFile::readAssembly(LLVMContext &Context) const {
+ LLVM_DEBUG(dbgs() << " - read assembly\n");
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M = parseAssemblyFile(Filename, Err, Context);
+ if (!M.get())
+ Err.print("verify-uselistorder", errs());
+ return M;
+}
+
+ValueMapping::ValueMapping(const Module &M) {
+ // Every value should be mapped, including things like void instructions and
+ // basic blocks that are kept out of the ValueEnumerator.
+ //
+ // The current mapping order makes it easier to debug the tables. It happens
+ // to be similar to the ID mapping when writing ValueEnumerator, but they
+ // aren't (and needn't be) in sync.
+
+ // Globals.
+ for (const GlobalVariable &G : M.globals())
+ map(&G);
+ for (const GlobalAlias &A : M.aliases())
+ map(&A);
+ for (const GlobalIFunc &IF : M.ifuncs())
+ map(&IF);
+ for (const Function &F : M)
+ map(&F);
+
+ // Constants used by globals.
+ for (const GlobalVariable &G : M.globals())
+ if (G.hasInitializer())
+ map(G.getInitializer());
+ for (const GlobalAlias &A : M.aliases())
+ map(A.getAliasee());
+ for (const GlobalIFunc &IF : M.ifuncs())
+ map(IF.getResolver());
+ for (const Function &F : M)
+ for (Value *Op : F.operands())
+ map(Op);
+
+ // Function bodies.
+ for (const Function &F : M) {
+ for (const Argument &A : F.args())
+ map(&A);
+ for (const BasicBlock &BB : F)
+ map(&BB);
+ for (const BasicBlock &BB : F)
+ for (const Instruction &I : BB)
+ map(&I);
+
+ // Constants used by instructions.
+ for (const BasicBlock &BB : F)
+ for (const Instruction &I : BB)
+ for (const Value *Op : I.operands()) {
+ // Look through a metadata wrapper.
+ if (const auto *MAV = dyn_cast<MetadataAsValue>(Op))
+ if (const auto *VAM = dyn_cast<ValueAsMetadata>(MAV->getMetadata()))
+ Op = VAM->getValue();
+
+ if ((isa<Constant>(Op) && !isa<GlobalValue>(*Op)) ||
+ isa<InlineAsm>(Op))
+ map(Op);
+ }
+ }
+}
+
+void ValueMapping::map(const Value *V) {
+ if (IDs.lookup(V))
+ return;
+
+ if (auto *C = dyn_cast<Constant>(V))
+ if (!isa<GlobalValue>(C))
+ for (const Value *Op : C->operands())
+ map(Op);
+
+ Values.push_back(V);
+ IDs[V] = Values.size();
+}
+
+#ifndef NDEBUG
+static void dumpMapping(const ValueMapping &VM) {
+ dbgs() << "value-mapping (size = " << VM.Values.size() << "):\n";
+ for (unsigned I = 0, E = VM.Values.size(); I != E; ++I) {
+ dbgs() << " - id = " << I << ", value = ";
+ VM.Values[I]->dump();
+ }
+}
+
+static void debugValue(const ValueMapping &M, unsigned I, StringRef Desc) {
+ const Value *V = M.Values[I];
+ dbgs() << " - " << Desc << " value = ";
+ V->dump();
+ for (const Use &U : V->uses()) {
+ dbgs() << " => use: op = " << U.getOperandNo()
+ << ", user-id = " << M.IDs.lookup(U.getUser()) << ", user = ";
+ U.getUser()->dump();
+ }
+}
+
+static void debugUserMismatch(const ValueMapping &L, const ValueMapping &R,
+ unsigned I) {
+ dbgs() << " - fail: user mismatch: ID = " << I << "\n";
+ debugValue(L, I, "LHS");
+ debugValue(R, I, "RHS");
+
+ dbgs() << "\nlhs-";
+ dumpMapping(L);
+ dbgs() << "\nrhs-";
+ dumpMapping(R);
+}
+
+static void debugSizeMismatch(const ValueMapping &L, const ValueMapping &R) {
+ dbgs() << " - fail: map size: " << L.Values.size()
+ << " != " << R.Values.size() << "\n";
+ dbgs() << "\nlhs-";
+ dumpMapping(L);
+ dbgs() << "\nrhs-";
+ dumpMapping(R);
+}
+#endif
+
+static bool matches(const ValueMapping &LM, const ValueMapping &RM) {
+ LLVM_DEBUG(dbgs() << "compare value maps\n");
+ if (LM.Values.size() != RM.Values.size()) {
+ LLVM_DEBUG(debugSizeMismatch(LM, RM));
+ return false;
+ }
+
+ // This mapping doesn't include dangling constant users, since those don't
+ // get serialized. However, checking if users are constant and calling
+ // isConstantUsed() on every one is very expensive. Instead, just check if
+ // the user is mapped.
+ auto skipUnmappedUsers =
+ [&](Value::const_use_iterator &U, Value::const_use_iterator E,
+ const ValueMapping &M) {
+ while (U != E && !M.lookup(U->getUser()))
+ ++U;
+ };
+
+ // Iterate through all values, and check that both mappings have the same
+ // users.
+ for (unsigned I = 0, E = LM.Values.size(); I != E; ++I) {
+ const Value *L = LM.Values[I];
+ const Value *R = RM.Values[I];
+ auto LU = L->use_begin(), LE = L->use_end();
+ auto RU = R->use_begin(), RE = R->use_end();
+ skipUnmappedUsers(LU, LE, LM);
+ skipUnmappedUsers(RU, RE, RM);
+
+ while (LU != LE) {
+ if (RU == RE) {
+ LLVM_DEBUG(debugUserMismatch(LM, RM, I));
+ return false;
+ }
+ if (LM.lookup(LU->getUser()) != RM.lookup(RU->getUser())) {
+ LLVM_DEBUG(debugUserMismatch(LM, RM, I));
+ return false;
+ }
+ if (LU->getOperandNo() != RU->getOperandNo()) {
+ LLVM_DEBUG(debugUserMismatch(LM, RM, I));
+ return false;
+ }
+ skipUnmappedUsers(++LU, LE, LM);
+ skipUnmappedUsers(++RU, RE, RM);
+ }
+ if (RU != RE) {
+ LLVM_DEBUG(debugUserMismatch(LM, RM, I));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void verifyAfterRoundTrip(const Module &M,
+ std::unique_ptr<Module> OtherM) {
+ if (!OtherM)
+ report_fatal_error("parsing failed");
+ if (verifyModule(*OtherM, &errs()))
+ report_fatal_error("verification failed");
+ if (!matches(ValueMapping(M), ValueMapping(*OtherM)))
+ report_fatal_error("use-list order changed");
+}
+
+static void verifyBitcodeUseListOrder(const Module &M) {
+ TempFile F;
+ if (F.init("bc"))
+ report_fatal_error("failed to initialize bitcode file");
+
+ if (F.writeBitcode(M))
+ report_fatal_error("failed to write bitcode");
+
+ LLVMContext Context;
+ verifyAfterRoundTrip(M, F.readBitcode(Context));
+}
+
+static void verifyAssemblyUseListOrder(const Module &M) {
+ TempFile F;
+ if (F.init("ll"))
+ report_fatal_error("failed to initialize assembly file");
+
+ if (F.writeAssembly(M))
+ report_fatal_error("failed to write assembly");
+
+ LLVMContext Context;
+ verifyAfterRoundTrip(M, F.readAssembly(Context));
+}
+
+static void verifyUseListOrder(const Module &M) {
+ outs() << "verify bitcode\n";
+ verifyBitcodeUseListOrder(M);
+ outs() << "verify assembly\n";
+ verifyAssemblyUseListOrder(M);
+}
+
+static void shuffleValueUseLists(Value *V, std::minstd_rand0 &Gen,
+ DenseSet<Value *> &Seen) {
+ if (!Seen.insert(V).second)
+ return;
+
+ if (auto *C = dyn_cast<Constant>(V))
+ if (!isa<GlobalValue>(C))
+ for (Value *Op : C->operands())
+ shuffleValueUseLists(Op, Gen, Seen);
+
+ if (V->use_empty() || std::next(V->use_begin()) == V->use_end())
+ // Nothing to shuffle for 0 or 1 users.
+ return;
+
+ // Generate random numbers between 10 and 99, which will line up nicely in
+ // debug output. We're not worried about collisons here.
+ LLVM_DEBUG(dbgs() << "V = "; V->dump());
+ std::uniform_int_distribution<short> Dist(10, 99);
+ SmallDenseMap<const Use *, short, 16> Order;
+ auto compareUses =
+ [&Order](const Use &L, const Use &R) { return Order[&L] < Order[&R]; };
+ do {
+ for (const Use &U : V->uses()) {
+ auto I = Dist(Gen);
+ Order[&U] = I;
+ LLVM_DEBUG(dbgs() << " - order: " << I << ", op = " << U.getOperandNo()
+ << ", U = ";
+ U.getUser()->dump());
+ }
+ } while (std::is_sorted(V->use_begin(), V->use_end(), compareUses));
+
+ LLVM_DEBUG(dbgs() << " => shuffle\n");
+ V->sortUseList(compareUses);
+
+ LLVM_DEBUG({
+ for (const Use &U : V->uses()) {
+ dbgs() << " - order: " << Order.lookup(&U)
+ << ", op = " << U.getOperandNo() << ", U = ";
+ U.getUser()->dump();
+ }
+ });
+}
+
+static void reverseValueUseLists(Value *V, DenseSet<Value *> &Seen) {
+ if (!Seen.insert(V).second)
+ return;
+
+ if (auto *C = dyn_cast<Constant>(V))
+ if (!isa<GlobalValue>(C))
+ for (Value *Op : C->operands())
+ reverseValueUseLists(Op, Seen);
+
+ if (V->use_empty() || std::next(V->use_begin()) == V->use_end())
+ // Nothing to shuffle for 0 or 1 users.
+ return;
+
+ LLVM_DEBUG({
+ dbgs() << "V = ";
+ V->dump();
+ for (const Use &U : V->uses()) {
+ dbgs() << " - order: op = " << U.getOperandNo() << ", U = ";
+ U.getUser()->dump();
+ }
+ dbgs() << " => reverse\n";
+ });
+
+ V->reverseUseList();
+
+ LLVM_DEBUG({
+ for (const Use &U : V->uses()) {
+ dbgs() << " - order: op = " << U.getOperandNo() << ", U = ";
+ U.getUser()->dump();
+ }
+ });
+}
+
+template <class Changer>
+static void changeUseLists(Module &M, Changer changeValueUseList) {
+ // Visit every value that would be serialized to an IR file.
+ //
+ // Globals.
+ for (GlobalVariable &G : M.globals())
+ changeValueUseList(&G);
+ for (GlobalAlias &A : M.aliases())
+ changeValueUseList(&A);
+ for (GlobalIFunc &IF : M.ifuncs())
+ changeValueUseList(&IF);
+ for (Function &F : M)
+ changeValueUseList(&F);
+
+ // Constants used by globals.
+ for (GlobalVariable &G : M.globals())
+ if (G.hasInitializer())
+ changeValueUseList(G.getInitializer());
+ for (GlobalAlias &A : M.aliases())
+ changeValueUseList(A.getAliasee());
+ for (GlobalIFunc &IF : M.ifuncs())
+ changeValueUseList(IF.getResolver());
+ for (Function &F : M)
+ for (Value *Op : F.operands())
+ changeValueUseList(Op);
+
+ // Function bodies.
+ for (Function &F : M) {
+ for (Argument &A : F.args())
+ changeValueUseList(&A);
+ for (BasicBlock &BB : F)
+ changeValueUseList(&BB);
+ for (BasicBlock &BB : F)
+ for (Instruction &I : BB)
+ changeValueUseList(&I);
+
+ // Constants used by instructions.
+ for (BasicBlock &BB : F)
+ for (Instruction &I : BB)
+ for (Value *Op : I.operands()) {
+ // Look through a metadata wrapper.
+ if (auto *MAV = dyn_cast<MetadataAsValue>(Op))
+ if (auto *VAM = dyn_cast<ValueAsMetadata>(MAV->getMetadata()))
+ Op = VAM->getValue();
+ if ((isa<Constant>(Op) && !isa<GlobalValue>(*Op)) ||
+ isa<InlineAsm>(Op))
+ changeValueUseList(Op);
+ }
+ }
+
+ if (verifyModule(M, &errs()))
+ report_fatal_error("verification failed");
+}
+
+static void shuffleUseLists(Module &M, unsigned SeedOffset) {
+ std::minstd_rand0 Gen(std::minstd_rand0::default_seed + SeedOffset);
+ DenseSet<Value *> Seen;
+ changeUseLists(M, [&](Value *V) { shuffleValueUseLists(V, Gen, Seen); });
+ LLVM_DEBUG(dbgs() << "\n");
+}
+
+static void reverseUseLists(Module &M) {
+ DenseSet<Value *> Seen;
+ changeUseLists(M, [&](Value *V) { reverseValueUseLists(V, Seen); });
+ LLVM_DEBUG(dbgs() << "\n");
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ // Enable debug stream buffering.
+ EnableDebugBuffering = true;
+
+ cl::ParseCommandLineOptions(argc, argv,
+ "llvm tool to verify use-list order\n");
+
+ LLVMContext Context;
+ SMDiagnostic Err;
+
+ // Load the input module...
+ std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context);
+
+ if (!M.get()) {
+ Err.print(argv[0], errs());
+ return 1;
+ }
+ if (verifyModule(*M, &errs())) {
+ errs() << argv[0] << ": " << InputFilename
+ << ": error: input module is broken!\n";
+ return 1;
+ }
+
+ // Verify the use lists now and after reversing them.
+ outs() << "*** verify-uselistorder ***\n";
+ verifyUseListOrder(*M);
+ outs() << "reverse\n";
+ reverseUseLists(*M);
+ verifyUseListOrder(*M);
+
+ for (unsigned I = 0, E = NumShuffles; I != E; ++I) {
+ outs() << "\n";
+
+ // Shuffle with a different (deterministic) seed each time.
+ outs() << "shuffle (" << I + 1 << " of " << E << ")\n";
+ shuffleUseLists(*M, I);
+
+ // Verify again before and after reversing.
+ verifyUseListOrder(*M);
+ outs() << "reverse\n";
+ reverseUseLists(*M);
+ verifyUseListOrder(*M);
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/verify-uselistorder/ya.make b/contrib/libs/llvm14/tools/verify-uselistorder/ya.make
new file mode 100644
index 00000000000..ac4184eb63f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/verify-uselistorder/ya.make
@@ -0,0 +1,43 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/Analysis
+ contrib/libs/llvm14/lib/AsmParser
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitcode/Writer
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/DWARF
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/IRReader
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ProfileData
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/verify-uselistorder
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ verify-uselistorder.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/yaml2obj/ya.make b/contrib/libs/llvm14/tools/yaml2obj/ya.make
new file mode 100644
index 00000000000..893b943ada0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/yaml2obj/ya.make
@@ -0,0 +1,38 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/DebugInfo/CodeView
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/ObjectYAML
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ contrib/libs/llvm14/tools/yaml2obj
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ yaml2obj.cpp
+)
+
+END()
diff --git a/contrib/libs/llvm14/tools/yaml2obj/yaml2obj.cpp b/contrib/libs/llvm14/tools/yaml2obj/yaml2obj.cpp
new file mode 100644
index 00000000000..8a44c44eedc
--- /dev/null
+++ b/contrib/libs/llvm14/tools/yaml2obj/yaml2obj.cpp
@@ -0,0 +1,145 @@
+//===- yaml2obj - Convert YAML to a binary object file --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This program takes a YAML description of an object file and outputs the
+// binary equivalent.
+//
+// This is used for writing tests that require binary files.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ObjectYAML/yaml2obj.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ObjectYAML/ObjectYAML.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <system_error>
+
+using namespace llvm;
+
+namespace {
+cl::OptionCategory Cat("yaml2obj Options");
+
+cl::opt<std::string> Input(cl::Positional, cl::desc("<input file>"),
+ cl::init("-"), cl::cat(Cat));
+
+cl::list<std::string>
+ D("D", cl::Prefix,
+ cl::desc("Defined the specified macros to their specified "
+ "definition. The syntax is <macro>=<definition>"),
+ cl::cat(Cat));
+
+cl::opt<unsigned>
+ DocNum("docnum", cl::init(1),
+ cl::desc("Read specified document from input (default = 1)"),
+ cl::cat(Cat));
+
+static cl::opt<uint64_t> MaxSize(
+ "max-size", cl::init(10 * 1024 * 1024),
+ cl::desc(
+ "Sets the maximum allowed output size (0 means no limit) [ELF only]"),
+ cl::cat(Cat));
+
+cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
+ cl::value_desc("filename"), cl::init("-"),
+ cl::Prefix, cl::cat(Cat));
+} // namespace
+
+static Optional<std::string> preprocess(StringRef Buf,
+ yaml::ErrorHandler ErrHandler) {
+ DenseMap<StringRef, StringRef> Defines;
+ for (StringRef Define : D) {
+ StringRef Macro, Definition;
+ std::tie(Macro, Definition) = Define.split('=');
+ if (!Define.count('=') || Macro.empty()) {
+ ErrHandler("invalid syntax for -D: " + Define);
+ return {};
+ }
+ if (!Defines.try_emplace(Macro, Definition).second) {
+ ErrHandler("'" + Macro + "'" + " redefined");
+ return {};
+ }
+ }
+
+ std::string Preprocessed;
+ while (!Buf.empty()) {
+ if (Buf.startswith("[[")) {
+ size_t I = Buf.find_first_of("[]", 2);
+ if (Buf.substr(I).startswith("]]")) {
+ StringRef MacroExpr = Buf.substr(2, I - 2);
+ StringRef Macro;
+ StringRef Default;
+ std::tie(Macro, Default) = MacroExpr.split('=');
+
+ // When the -D option is requested, we use the provided value.
+ // Otherwise we use a default macro value if present.
+ auto It = Defines.find(Macro);
+ Optional<StringRef> Value;
+ if (It != Defines.end())
+ Value = It->second;
+ else if (!Default.empty() || MacroExpr.endswith("="))
+ Value = Default;
+
+ if (Value) {
+ Preprocessed += *Value;
+ Buf = Buf.substr(I + 2);
+ continue;
+ }
+ }
+ }
+
+ Preprocessed += Buf[0];
+ Buf = Buf.substr(1);
+ }
+
+ return Preprocessed;
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ cl::HideUnrelatedOptions(Cat);
+ cl::ParseCommandLineOptions(
+ argc, argv, "Create an object file from a YAML description", nullptr,
+ nullptr, /*LongOptionsUseDoubleDash=*/true);
+
+ auto ErrHandler = [](const Twine &Msg) {
+ WithColor::error(errs(), "yaml2obj") << Msg << "\n";
+ };
+
+ std::error_code EC;
+ std::unique_ptr<ToolOutputFile> Out(
+ new ToolOutputFile(OutputFilename, EC, sys::fs::OF_None));
+ if (EC) {
+ ErrHandler("failed to open '" + OutputFilename + "': " + EC.message());
+ return 1;
+ }
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
+ MemoryBuffer::getFileOrSTDIN(Input);
+ if (!Buf)
+ return 1;
+
+ Optional<std::string> Buffer = preprocess(Buf.get()->getBuffer(), ErrHandler);
+ if (!Buffer)
+ return 1;
+ yaml::Input YIn(*Buffer);
+
+ if (!convertYAML(YIn, Out->os(), ErrHandler, DocNum,
+ MaxSize == 0 ? UINT64_MAX : MaxSize))
+ return 1;
+
+ Out->keep();
+ Out->os().flush();
+ return 0;
+}